xref: /inferno-os/libtk/canvs.c (revision 5849851a19380dbb62a47d9c4d868a81e42fa79b)
1 #include <lib9.h>
2 #include <kernel.h>
3 #include "draw.h"
4 #include "tk.h"
5 #include "canvs.h"
6 
7 /* Widget Commands (+ means implemented)
8 	+addtag
9 		except halo and start options of closest spec
10 	+bbox
11 	+bind
12 	+canvasx
13 	+canvasy
14 	+cget
15 	+configure
16 	+coords
17 	+create
18 	+dchars
19 	+delete
20 	+dtag
21 	+find
22 	+focus
23 	+gettags
24 	+icursor
25 	+index
26 	+insert
27 	+itemcget
28 	+itemconfigure
29 	+lower
30 	+move
31 	 postscript
32 	+raise
33 	+scale
34 	 scan
35 	+select
36 	+type
37 	+xview
38 	+yview
39 */
40 
41 static
42 TkStab tkbuffer[] = {
43 	"visible",	TkCbufvisible,
44 	"all",		TkCbufall,
45 	"none",	TkCbufnone,
46 	"auto",	TkCbufauto,
47 
48 	/* backwards compatibility */
49 	"1",		TkCbufall,
50 	"yes",	TkCbufall,
51 	"off",		TkCbufall,
52 	"0",		TkCbufauto,
53 	"no",		TkCbufauto,
54 	"off",		TkCbufauto,
55 	nil
56 };
57 
58 #define	O(t, e)		((long)(&((t*)0)->e))
59 #define OA(t, e)	((long)(((t*)0)->e))
60 
61 static
62 TkOption opts[] =
63 {
64 	"closeenough",		OPTfrac,	O(TkCanvas, close),	nil,
65 	"confine",		OPTfrac,	O(TkCanvas, confine),	nil,
66 	"scrollregion",		OPTfrac,	OA(TkCanvas, scrollr),	IAUX(4),
67 	"xscrollincrement",	OPTfrac,	O(TkCanvas, xscrolli),	nil,
68 	"yscrollincrement",	OPTfrac,	O(TkCanvas, yscrolli),	nil,
69 	"xscrollcommand",	OPTtext,	O(TkCanvas, xscroll),	nil,
70 	"yscrollcommand",	OPTtext,	O(TkCanvas, yscroll),	nil,
71 	"width",		OPTnnfrac,	O(TkCanvas, width),	nil,
72 	"height",		OPTnnfrac,	O(TkCanvas, height),	nil,
73 	"buffer",		OPTstab,	O(TkCanvas, buffer),		tkbuffer,
74 	"buffered",		OPTstab,	O(TkCanvas, buffer),	tkbool,	/* backwards compatibility */
75 	"selectborderwidth",	OPTnndist, O(TkCanvas, sborderwidth), nil,
76 	nil
77 };
78 
79 int cvslshape[] = { TKI2F(8), TKI2F(10), TKI2F(3) };
80 Rectangle	bbnil = { 1000000, 1000000, -1000000, -1000000 };
81 Rectangle	huger = { -1000000, -1000000, 1000000, 1000000 };
82 
83 static void tkcvsgeom(Tk *tk);
84 
85 
86 static void
tkcvsf2i(Tk * tk,TkCanvas * tkc)87 tkcvsf2i(Tk *tk, TkCanvas *tkc)
88 {
89 	Rectangle r;
90 	tk->req.width = TKF2I(tkc->width);
91 	tk->req.height = TKF2I(tkc->height);
92 
93 	r.min.x = TKF2I(tkc->scrollr[0]);
94 	r.min.y = TKF2I(tkc->scrollr[1]);
95 	r.max.x = TKF2I(tkc->scrollr[2]);
96 	r.max.y = TKF2I(tkc->scrollr[3]);
97 
98 	/*
99 	 * make sure that the region is big enough to hold
100 	 * the actually displayed area
101 	 */
102 	if (Dx(r) < tk->act.width)
103 		r.max.x = r.min.x + tk->act.width;
104 	if (Dy(r) < tk->act.height)
105 		r.max.y = r.min.y + tk->act.height;
106 	tkc->region = r;
107 
108 	/*
109 	 * make sure that the view origin is at a valid
110 	 * position with respect to the scroll region.
111 	 */
112 	if (tkc->view.x + tk->act.width > r.max.x)
113 		tkc->view.x = r.max.x - tk->act.width;
114 	if (tkc->view.x < r.min.x)
115 		tkc->view.x = r.min.x;
116 
117 	if (tkc->view.y + tk->act.height > r.max.y)
118 		tkc->view.y = r.max.y - tk->act.height;
119 	if (tkc->view.y < r.min.y)
120 		tkc->view.y = r.min.y;
121 
122 }
123 
124 char*
tkcanvas(TkTop * t,char * arg,char ** ret)125 tkcanvas(TkTop *t, char *arg, char **ret)
126 {
127 	Tk *tk;
128 	char *e;
129 	TkCanvas *tkc;
130 	TkName *names;
131 	TkOptab tko[3];
132 
133 	tk = tknewobj(t, TKcanvas, sizeof(Tk)+sizeof(TkCanvas));
134 	if(tk == nil)
135 		return TkNomem;
136 
137 	tkc = TKobj(TkCanvas, tk);
138 	tkc->close = TKI2F(1);
139 	tkc->xscrolli = TKI2F(1);
140 	tkc->yscrolli = TKI2F(1);
141 	tkc->width = TKI2F(360);
142 	tkc->height = TKI2F(270);
143 	tkc->actions = 0;
144 	tkc->actlim = Tksweep;
145 	tkc->mask = nil;
146 	tkc->sborderwidth = 1;
147 
148 	tko[0].ptr = tkc;
149 	tko[0].optab = opts;
150 	tko[1].ptr = tk;
151 	tko[1].optab = tkgeneric;
152 	tko[2].ptr = nil;
153 
154 	names = nil;
155 	e = tkparse(t, arg, tko, &names);
156 	if(e != nil)
157 		goto err;
158 	if(names == nil) {
159 		/* tkerr(t, arg); XXX */
160 		e = TkBadwp;
161 		goto err;
162 	}
163 
164 	tkc->current = tkmkname("current");
165 	if(tkc->current == nil) {
166 		e = TkNomem;
167 		goto err;
168 	}
169 
170 	tksettransparent(tk, tkhasalpha(tk->env, TkCbackgnd));
171 	tkcvsf2i(tk, tkc);
172 
173 	e = tkaddchild(t, tk, &names);
174 	tkfreename(names);
175 	if(e != nil) {
176 		tkfreename(tkc->current);
177 		tkc->current = nil;
178 		goto err;
179 	}
180 	tk->name->link = nil;
181 
182 	e = tkvalue(ret, "%s", tk->name->name);
183 	if(e == nil)
184 	        return nil;
185 
186 	tkfreename(tkc->current);
187 	return e;
188 err:
189 	tkfreeobj(tk);
190 	return e;
191 }
192 
193 void
tkcvsdirty(Tk * sub)194 tkcvsdirty(Tk *sub)
195 {
196 	TkCanvas *c;
197 	Tk *tk, *parent;
198 	Rectangle r;
199 	Point rel;
200 
201 	rel = ZP;
202 	for(tk = sub; tk;  tk = tk->master) {
203 		rel.x += tk->borderwidth + tk->act.x;
204 		rel.y += tk->borderwidth + tk->act.y;
205 		if (tk->parent != nil)
206 			break;
207 	}
208 	if (tk == nil)
209 		return;
210 	parent = tk->parent;
211 	c = TKobj(TkCanvas, parent);
212 	r = rectaddpt(sub->dirty, rel);
213 	tkbbmax(&c->update, &r);
214 	tkcvssetdirty(parent);
215 }
216 
217 static void
tkcvsfocusorder(Tk * tk)218 tkcvsfocusorder(Tk *tk)
219 {
220 	TkCanvas *tkc = TKobj(TkCanvas, tk);
221 	TkCwind *win;
222 	TkCitem *it;
223 	TkWinfo *inf;
224 	int i, n;
225 
226 	n = 0;
227 	for (it = tkc->head; it != nil; it = it->next) {
228 		if (it->type == TkCVwindow) {
229 			win = TKobj(TkCwind, it);
230 			if (win->sub != nil)
231 				n++;
232 		}
233 	}
234 	if (n == 0)
235 		return;
236 
237 	inf = malloc(sizeof(*inf) * n);
238 	if (inf == nil)
239 		return;
240 
241 	i = 0;
242 	for (it = tkc->head; it != nil; it = it->next) {
243 		if (it->type == TkCVwindow) {
244 			win = TKobj(TkCwind, it);
245 			if (win->sub != nil) {
246 				inf[i].w = win->sub;
247 				inf[i].r = it->p.bb;
248 				i++;
249 			}
250 		}
251 	}
252 
253 	tksortfocusorder(inf, n);
254 	for (i = 0; i < n; i++)
255 		tkappendfocusorder(inf[i].w);
256 }
257 
258 static char*
tkcvscget(Tk * tk,char * arg,char ** val)259 tkcvscget(Tk *tk, char *arg, char **val)
260 {
261 	TkOptab tko[3];
262 	TkCanvas *tkc = TKobj(TkCanvas, tk);
263 
264 	tko[0].ptr = tkc;
265 	tko[0].optab = opts;
266 	tko[1].ptr = tk;
267 	tko[1].optab = tkgeneric;
268 	tko[2].ptr = nil;
269 
270 	return tkgencget(tko, arg, val, tk->env->top);
271 }
272 
273 static char*
tkcvsconf(Tk * tk,char * arg,char ** val)274 tkcvsconf(Tk *tk, char *arg, char **val)
275 {
276 	char *e;
277 	int bd;
278 	TkGeom g;
279 	Rectangle r;
280 	TkOptab tko[3];
281 	TkCanvas *c = TKobj(TkCanvas, tk);
282 
283 	tko[0].ptr = c;
284 	tko[0].optab = opts;
285 	tko[1].ptr = tk;
286 	tko[1].optab = tkgeneric;
287 	tko[2].ptr = nil;
288 
289 	if(*arg == '\0')
290 		return tkconflist(tko, val);
291 
292 	r.min = c->view;
293 	r.max.x = r.min.x+tk->act.width;
294 	r.max.y = r.min.y+tk->act.height;
295 	tkbbmax(&c->update, &r);
296 	tkbbmax(&c->update, &c->region);
297 
298 	bd = tk->borderwidth;
299 	g = tk->req;
300 	e = tkparse(tk->env->top, arg, tko, nil);
301 	if(e != nil)
302 		return e;
303 	tksettransparent(tk, tkhasalpha(tk->env, TkCbackgnd));
304 
305 	tkcvsf2i(tk, c);
306 
307 	tkcvsgeom(tk);
308 	tkgeomchg(tk, &g, bd);
309 	tkbbmax(&c->update, &c->region);
310 	tk->dirty = tkrect(tk, 1);
311 	return nil;
312 }
313 
314 void
tkcvsfreeitem(TkCitem * i)315 tkcvsfreeitem(TkCitem *i)
316 {
317 	int locked;
318 	Display *d;
319 
320 	d = i->env->top->display;
321 
322 	locked = lockdisplay(d);
323 	tkcimethod[i->type].free(i);
324 	if(locked)
325 		unlockdisplay(d);
326 
327 	tkfreepoint(&i->p);
328 	tkputenv(i->env);
329 	free(i);
330 }
331 
332 void
tkfreecanv(Tk * tk)333 tkfreecanv(Tk *tk)
334 {
335 	Display *d;
336 	int j, locked;
337 	TkCanvas *c;
338 	TkName *n, *nn;
339 	TkCtag *t, *tt;
340 	TkCitem *i, *next;
341 
342 	c = TKobj(TkCanvas, tk);
343 	for(i = c->head; i; i = next) {
344 		next = i->next;
345 		tkcvsfreeitem(i);
346 	}
347 
348 	if(c->xscroll != nil)
349 		free(c->xscroll);
350 	if(c->yscroll != nil)
351 		free(c->yscroll);
352 
353 	for(j = 0; j < TkChash; j++) {
354 		for(n = c->thash[j]; n; n = nn) {
355 			nn = n->link;
356 			for(t = n->obj; t; t = tt) {
357 				tt = t->taglist;
358 				free(t);
359 			}
360 			tkfreebind(n->prop.binds);
361 			free(n);
362 		}
363 	}
364 
365 	free(c->current);
366 
367 	if((c->ialloc && c->image != nil) || c->mask != nil) {
368 		if (c->ialloc && c->image != nil)
369 			d = c->image->display;
370 		else
371 			d = c->mask->display;
372 		locked = lockdisplay(d);
373 		if (c->image != nil && c->ialloc)
374 			freeimage(c->image);
375 		if (c->mask != nil)
376 			freeimage(c->mask);
377 		if(locked)
378 			unlockdisplay(d);
379 	}
380 }
381 
382 char*
tkdrawcanv(Tk * tk,Point orig)383 tkdrawcanv(Tk *tk, Point orig)
384 {
385 	Image *dst;
386 	TkCitem *i;
387 	Display *d;
388 	TkCanvas *c;
389 	Rectangle r, bufr, oclipr;
390 	int vis, alpha, buffer;
391 	Point rel, p;
392 	TkCimeth *imeth;
393 
394 	c = TKobj(TkCanvas, tk);
395 	d = tk->env->top->display;
396 	dst = tkimageof(tk);
397 	/*
398 	 * translation from local to screen coords
399 	 */
400 	rel.x = orig.x + tk->act.x + tk->borderwidth;
401 	rel.y = orig.y + tk->act.y + tk->borderwidth;
402 
403 	buffer = c->buffer;
404 	if (buffer == TkCbufauto)
405 		buffer = TkCbufvisible;
406 /*		buffer = (dst == TKobj(TkWin, tk->env->top->root)->image) ? TkCbufvisible : TkCbufnone; */
407 
408 	if (buffer == TkCbufnone) {
409 		if(c->image != nil && c->ialloc)
410 			freeimage(c->image);
411 		c->image = dst;
412 		c->ialloc = 0;
413 
414 		r = tkrect(tk, 0);
415 		bufr = r;
416 		rectclip(&bufr, tk->dirty);
417 		oclipr = dst->clipr;
418 
419 		replclipr(dst, 0, rectaddpt(bufr, rel));
420 		draw(dst, rectaddpt(bufr, rel), tkgc(tk->env, TkCbackgnd), nil, ZP);
421 
422 		p = subpt(rel, c->view);
423 		p.x = TKI2F(p.x);
424 		p.y = TKI2F(p.y);
425 		bufr = rectaddpt(bufr, c->view);
426 		for(i = c->head; i; i = i->next) {
427 			if(rectXrect(i->p.bb, bufr)) {
428 				imeth = &tkcimethod[i->type];
429 				imeth->coord(i, nil, p.x, p.y);
430 				imeth->draw(dst, i, tk->env);
431 				imeth->coord(i, nil, -p.x, -p.y);
432 			}
433 		}
434 		replclipr(dst, 0, oclipr);
435 	} else {
436 		if (c->buffer == TkCbufall)
437 			bufr = c->region;
438 		else {
439 			bufr.min = c->view;
440 			bufr.max.x = c->view.x + tk->act.width;
441 			bufr.max.y = c->view.y + tk->act.height;
442 		}
443 		alpha = (tk->env->colors[TkCbackgnd] & 0xff) != 0xff;
444 		if(c->image == nil || eqrect(bufr, c->image->r) == 0) {
445 			if(c->image != nil && c->ialloc)
446 				freeimage(c->image);
447 			c->image = allocimage(d, bufr, alpha?RGBA32:d->image->chan, 0, tk->env->colors[TkCbackgnd]);
448 			c->ialloc = 1;
449 			c->update = bufr;
450 			tkcvssetdirty(tk);		/* unnecessary? */
451 		}
452 
453 		if(c->image == nil)
454 			return nil;
455 
456 		r = c->update;
457 		if (rectclip(&r, c->image->r)) {
458 			if (alpha)
459 				drawop(c->image, c->update, nil, nil, ZP, Clear);
460 			draw(c->image, c->update, tkgc(tk->env, TkCbackgnd), nil, c->view);
461 			replclipr(c->image, 0, r);
462 			for(i = c->head; i; i = i->next) {
463 				if(rectXrect(i->p.bb, r))
464 					tkcimethod[i->type].draw(c->image, i, tk->env);
465 			}
466 			replclipr(c->image, 0, c->image->r);
467 		}
468 		/*
469 		 * if the visible area of the canvas image doesn't
470 		 * fit completely within the dirty rectangle,
471 		 * then we'll need to draw the background behind it
472 		 */
473 		r = tkrect(tk, 0);
474 		bufr = rectsubpt(bufr, c->view);
475 		vis = rectclip(&bufr, tkrect(tk, 0));
476 
477 		if (!vis || !rectinrect(tk->dirty, bufr))
478 			draw(dst, rectaddpt(tk->dirty, rel), tkgc(tk->env, TkCbackgnd), nil, c->view);
479 
480 		if (vis && rectclip(&bufr, tk->dirty))
481 			draw(dst, rectaddpt(bufr, rel), c->image, nil, addpt(bufr.min, c->view));
482 	}
483 
484 
485 	/*
486 	 * if the border is dirty too, then draw that
487 	 */
488 	if (!rectinrect(tk->dirty, bufr)) {
489 		r.min = addpt(r.min, rel);
490 		r.min.x -= tk->borderwidth;
491 		r.min.y -= tk->borderwidth;
492 		tkdrawrelief(dst, tk, r.min, TkCbackgnd, tk->relief);
493 	}
494 	c->update = bbnil;
495 	return nil;
496 }
497 
498 void
tkcvsappend(TkCanvas * c,TkCitem * i)499 tkcvsappend(TkCanvas *c, TkCitem *i)
500 {
501 	if(c->head == nil)
502 		c->head = i;
503 	else
504 		c->tail->next = i;
505 	c->tail = i;
506 }
507 
508 void
tkcvssv(Tk * tk)509 tkcvssv(Tk *tk)
510 {
511 	TkCanvas *c;
512 	int top, bot, height;
513 	char val[Tkminitem], cmd[Tkmaxitem], *v, *e;
514 
515 	c = TKobj(TkCanvas, tk);
516 	if(c->yscroll == nil)
517 		return;
518 
519 	top = 0;
520 	bot = TKI2F(1);
521 
522 	height = Dy(c->region);
523 	if(height != 0) {
524 		top = TKI2F(c->view.y)/height;
525 		bot = TKI2F(c->view.y+tk->act.height)/height;
526 	}
527 
528 	v = tkfprint(val, top);
529 	*v++ = ' ';
530 	tkfprint(v, bot);
531 	snprint(cmd, sizeof(cmd), "%s %s", c->yscroll, val);
532 	e = tkexec(tk->env->top, cmd, nil);
533 	if ((e != nil) && (tk->name != nil))
534 		print("tk: yscrollcommand \"%s\": %s\n", tk->name->name, e);
535 }
536 
537 void
tkcvssh(Tk * tk)538 tkcvssh(Tk *tk)
539 {
540 	int top, bot, width;
541 	TkCanvas *c = TKobj(TkCanvas, tk);
542 	char val[Tkminitem], cmd[Tkmaxitem], *v, *e;
543 
544 	if(c->xscroll == nil)
545 		return;
546 
547 	top = 0;
548 	bot = TKI2F(1);
549 
550 	width = Dx(c->region);
551 	if(width != 0) {
552 		top = TKI2F(c->view.x)/width;
553 		bot = TKI2F(c->view.x+tk->act.width)/width;
554 	}
555 
556 	v = tkfprint(val, top);
557 	*v++ = ' ';
558 	tkfprint(v, bot);
559 	snprint(cmd, sizeof(cmd), "%s %s", c->xscroll, val);
560 	e = tkexec(tk->env->top, cmd, nil);
561 	if ((e != nil) && (tk->name != nil))
562 		print("tk: xscrollcommand \"%s\": %s\n", tk->name->name, e);
563 }
564 
565 static void
tkcvsgeom(Tk * tk)566 tkcvsgeom(Tk *tk)
567 {
568 	TkCanvas *c;
569 	c = TKobj(TkCanvas, tk);
570 
571 	tkcvsf2i(tk, c);
572 	tk->dirty = tkrect(tk, 1);
573 	c->update = c->region;
574 
575 	tkcvssv(tk);
576 	tkcvssh(tk);
577 }
578 
579 char*
tkcvstags(Tk * tk,char * arg,char ** val,int af)580 tkcvstags(Tk *tk, char *arg, char **val, int af)
581 {
582 	TkTop *o;
583 	int x, y;
584 	TkName *f;
585 	TkCtag *t, *tt;
586 	char *fmt;
587 	TkCpoints p;
588 	TkCanvas *c;
589 	TkCitem *i, *b;
590 	int d, dist, dx, dy;
591 	char tag[Tkmaxitem], buf[Tkmaxitem];
592 	char *e;
593 
594 	USED(val);
595 
596 	c = TKobj(TkCanvas, tk);
597 
598 	o = tk->env->top;
599 	if(af == TkCadd) {
600 		arg = tkword(o, arg, tag, tag+sizeof(tag), nil);
601 		if(tag[0] == '\0' || (tag[0] >= '0' && tag[0] <= '9'))
602 			return TkBadtg;
603 	}
604 
605 	fmt = "%d";
606 	arg = tkword(o, arg, buf, buf+sizeof(buf), nil);
607 	if(strcmp(buf, "above") == 0) {
608 		tkword(o, arg, buf, buf+sizeof(buf), nil);
609 		f = tkctaglook(tk, nil, buf);
610 		if(f == nil)
611 			return TkBadtg;
612 
613 		t = tkclasttag(c->head, f->obj);
614 		if(t == nil)
615 			return TkBadtg;
616 
617 		for(i = t->item->next; i; i = i->next) {
618 			if(af == TkCadd) {
619 				i->tags = tkmkname(tag);
620 				if(i->tags == nil)
621 					return TkNomem;
622 				tkcaddtag(tk, i, 0);
623 			}
624 			else {
625 				e = tkvalue(val, fmt, i->id);
626 				if(e != nil)
627 					return e;
628 				fmt = " %d";
629 			}
630 		}
631 		return nil;
632 	}
633 
634 	if(strcmp(buf, "all") == 0) {
635 		for(i = c->head; i; i = i->next) {
636 			if(af == TkCadd) {
637 				i->tags = tkmkname(tag);
638 				if(i->tags == nil)
639 					return TkNomem;
640 				tkcaddtag(tk, i, 0);
641 			}
642 			else {
643 				e = tkvalue(val, fmt, i->id);
644 				if(e != nil)
645 					return e;
646 				fmt = " %d";
647 			}
648 		}
649 		return nil;
650 	}
651 
652 	if(strcmp(buf, "below") == 0) {
653 		tkword(o, arg, buf, buf+sizeof(buf), nil);
654 		f = tkctaglook(tk, nil, buf);
655 		if(f == nil)
656 			return TkBadtg;
657 		tt = f->obj;
658 		for(b = c->head; b; b = b->next) {
659 			for(t = tt; t; t = t->itemlist)
660 				if(t->item == b)
661 					goto found;
662 		}
663 	found:
664 		for(i = c->head; i != b; i = i->next) {
665 			if(af == TkCadd) {
666 				i->tags = tkmkname(tag);
667 				if(i->tags == nil)
668 					return TkNomem;
669 				tkcaddtag(tk, i, 0);
670 			}
671 			else {
672 				e = tkvalue(val, fmt, i->id);
673 				if(e != nil)
674 					return e;
675 				fmt = " %d";
676 			}
677 		}
678 		return nil;
679 	}
680 
681 	if(strcmp(buf, "closest") == 0) {
682 		e = tkfracword(o, &arg, &x, nil);
683 		if (e == nil)
684 			e = tkfracword(o, &arg, &y, nil);
685 		if (e != nil)
686 			return e;
687 		if(*arg != '\0')
688 			return "!not implemented";
689 
690 		x = TKF2I(x);
691 		y = TKF2I(y);
692 		i = nil;
693 		dist = 0;
694 		for(b = c->head; b != nil; b = b->next) {
695 			dx = x - (b->p.bb.min.x + Dx(b->p.bb)/2);
696 			dy = y - (b->p.bb.min.y + Dy(b->p.bb)/2);
697 			d = dx*dx + dy*dy;
698 			if(d < dist || dist == 0) {
699 				i = b;
700 				dist = d;
701 			}
702 		}
703 		if(i == nil)
704 			return nil;
705 
706 		if(af == TkCadd) {
707 			i->tags = tkmkname(tag);
708 			if(i->tags == nil)
709 				e = TkNomem;
710 			else
711 				tkcaddtag(tk, i, 0);
712 		}
713 		else
714 			e = tkvalue(val, fmt, i->id);
715 		return e;
716 	}
717 
718 	if(strcmp(buf, "withtag") == 0) {
719 		tkword(o, arg, buf, buf+sizeof(buf), nil);
720 		f = tkctaglook(tk, nil, buf);
721 		if(f == nil)
722 			return TkBadtg;
723 		for(t = f->obj; t; t = t->taglist) {
724 			i = t->item;
725 			if(af == TkCadd) {
726 				i->tags = tkmkname(tag);
727 				if(i->tags == nil)
728 					return TkNomem;
729 				tkcaddtag(tk, i, 0);
730 			}
731 			else {
732 				e = tkvalue(val, fmt, i->id);
733 				if(e != nil)
734 					return e;
735 				fmt = " %d";
736 			}
737 		}
738 		return nil;
739 	}
740 
741 	if(strcmp(buf, "enclosed") == 0) {
742 		e = tkparsepts(o, &p, &arg, 0);
743 		if(e != nil)
744 			goto done;
745 		if(p.npoint != 2) {
746 			e = TkFewpt;
747 			goto done;
748 		}
749 		for(i = c->head; i; i = i->next) {
750 			if(rectinrect(i->p.bb, p.bb)) {
751 				if(af == TkCadd) {
752 					i->tags = tkmkname(tag);
753 					if(i->tags == nil) {
754 						e = TkNomem;
755 						goto done;
756 					}
757 					tkcaddtag(tk, i, 0);
758 				}
759 				else {
760 					e = tkvalue(val, fmt, i->id);
761 					if(e != nil)
762 						goto done;
763 					fmt = " %d";
764 				}
765 			}
766 		}
767 		goto done;
768 	}
769 
770 	if(strcmp(buf, "overlapping") == 0) {
771 		e = tkparsepts(o, &p, &arg, 0);
772 		if(e != nil)
773 			goto done;
774 		if(p.npoint != 2) {
775 			e = TkFewpt;
776 			goto done;
777 		}
778 		for(i = c->head; i; i = i->next) {
779 			if(rectXrect(i->p.bb, p.bb)) {
780 				if(af == TkCadd) {
781 					i->tags = tkmkname(tag);
782 					if(i->tags == nil) {
783 						e = TkNomem;
784 						goto done;
785 					}
786 					tkcaddtag(tk, i, 0);
787 				}
788 				else {
789 					e = tkvalue(val, "%d ", i->id);
790 					if(e != nil)
791 						goto done;
792 				}
793 			}
794 		}
795 		goto done;
796 	}
797 
798 	return TkBadcm;
799 
800 done: 		 /* both no error and error do the same thing */
801 	tkfreepoint(&p);
802 	return e;
803 }
804 
805 static char*
tkcvsaddtag(Tk * tk,char * arg,char ** val)806 tkcvsaddtag(Tk *tk, char *arg, char **val)
807 {
808 	return tkcvstags(tk, arg, val, TkCadd);
809 }
810 
811 static char*
tkcvsfind(Tk * tk,char * arg,char ** val)812 tkcvsfind(Tk *tk, char *arg, char **val)
813 {
814 	return tkcvstags(tk, arg, val, TkCfind);
815 }
816 
817 static void
tksweepcanv(Tk * tk)818 tksweepcanv(Tk *tk)
819 {
820 	int j, k;
821 	TkCtag *t, *tt;
822 	TkName **np, *n, *nn;
823 	TkCitem *i;
824 	TkCanvas *c;
825 	TkAction *a;
826 
827 	c = TKobj(TkCanvas, tk);
828 
829 	for(j = 0; j < TkChash; j++)
830 		for(n = c->thash[j]; n != nil; n = n->link)
831 			n->ref = 0;
832 
833 	for(i = c->head; i != nil; i = i->next)
834 		for(t = i->stag; t != nil; t = t->itemlist)
835 			t->name->ref = 1;
836 
837 	k = 0;
838 	for(j = 0; j < TkChash; j++) {
839 		np = &c->thash[j];
840 		for(n = *np; n != nil; n = nn) {
841 			nn = n->link;
842 			if(n->ref == 0) {
843 				for(t = n->obj; t != nil; t = tt) {
844 					tt = t->taglist;
845 					free(t);
846 				}
847 				tkfreebind(n->prop.binds);
848 				free(n);
849 				*np = nn;
850 			} else {
851 				np = &n->link;
852 				for(a = n->prop.binds; a != nil; a = a->link)
853 					k++;
854 			}
855 		}
856 	}
857 
858 	c->actions = k;
859 	k = 3 * k / 2;
860 	if (k < Tksweep)
861 		c->actlim = Tksweep;
862 	else
863 		c->actlim = k;
864 }
865 
866 /*
867  * extension to tcl/tk:
868  * grab set tag
869  * grab release tag
870  * grab ifunset tag
871  */
872 static char*
tkcvsgrab(Tk * tk,char * arg,char ** val)873 tkcvsgrab(Tk *tk, char *arg, char **val)
874 {
875 	TkCtag *t;
876 	TkName *f;
877 	TkCanvas *c;
878 	char buf[Tkmaxitem];
879 
880 	c = TKobj(TkCanvas, tk);
881 	arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
882 	if (strcmp(buf, "status") == 0) {
883 		if (c->grab != nil)
884 			return tkvalue(val, "%d", c->grab->id);
885 	}
886 	else if (strcmp(buf, "release") == 0) {
887 		c->grab = nil;
888 	}
889 	else if (strcmp(buf, "set") == 0 || strcmp(buf, "ifunset") == 0) {
890 		if (buf[0] == 'i' && c->grab != nil)
891 			return nil;
892 		tkword(tk->env->top, arg, buf, buf + sizeof(buf), nil);
893 
894 		f = tkctaglook(tk, nil, buf);
895 		if(f == nil || f->obj == nil)
896 			return TkBadtg;
897 
898 		c = TKobj(TkCanvas, tk);
899 		t = tkcfirsttag(c->head, f->obj);
900 		if(t == nil)
901 			return TkBadtg;
902 		c->grab = t->item;
903 	}
904 	else
905 		return TkBadvl;
906 	return nil;
907 }
908 
909 static char*
tkcvsbind(Tk * tk,char * arg,char ** val)910 tkcvsbind(Tk *tk, char *arg, char **val)
911 {
912 	Rune r;
913 	TkCtag *t;
914 	TkName *f;
915 	TkAction *a;
916 	TkCanvas *c;
917 	int event, mode;
918 	char *cmd, buf[Tkmaxitem];
919 	char *e;
920 
921 	c = TKobj(TkCanvas, tk);
922 	if (c->actions >= c->actlim)
923 		tksweepcanv(tk);
924 	arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
925 
926 	f = tkctaglook(tk, nil, buf);
927 	if(f == nil) {
928 		f = tkctaglook(tk, tkmkname(buf), buf);
929 		if(f == nil)
930 			return TkNomem;
931 	}
932 
933 	arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
934 	if(buf[0] == '<') {
935 		event = tkseqparse(buf+1);
936 		if(event == -1)
937 			return TkBadsq;
938 	}
939 	else {
940 		chartorune(&r, buf);
941 		event = TkKey | r;
942 	}
943 	if(event == 0)
944 		return TkBadsq;
945 
946 	arg = tkskip(arg, " \t");
947 	if(*arg == '\0') {
948 		for(t = f->obj; t; t = t->taglist) {
949 			for(a = t->name->prop.binds; a; a = a->link)
950 				if(event == a->event)
951 					return tkvalue(val, "%s", a->arg);
952 			for(a = t->name->prop.binds; a; a = a->link)
953 				if(event & a->event)
954 					return tkvalue(val, "%s", a->arg);
955 		}
956 		return nil;
957 	}
958 
959 	mode = TkArepl;
960 	if(*arg == '+') {
961 		mode = TkAadd;
962 		arg++;
963 	}
964 	else if(*arg == '-'){
965 		mode = TkAsub;
966 		arg++;
967 	}
968 
969 	tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
970 	cmd = strdup(buf);
971 	if(cmd == nil)
972 		return TkNomem;
973 	e = tkaction(&f->prop.binds, event, TkDynamic, cmd, mode);
974 	if(e == nil)
975 		c->actions++;
976 	return e;
977 }
978 
979 static char*
tkcvscreate(Tk * tk,char * arg,char ** val)980 tkcvscreate(Tk *tk, char *arg, char **val)
981 {
982 	TkCimeth *m;
983 	char buf[Tkmaxitem];
984 
985 	arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
986 	for(m = tkcimethod; m->name; m++)
987 		if(strcmp(buf, m->name) == 0)
988 			return m->create(tk, arg, val);
989 
990 	return TkBadit;
991 }
992 
993 static char*
tkcvsbbox(Tk * tk,char * arg,char ** val)994 tkcvsbbox(Tk *tk, char *arg, char **val)
995 {
996 	TkName *f;
997 	TkCtag *t;
998 	Rectangle bb;
999 	char buf[Tkmaxitem];
1000 
1001 	bb = bbnil;
1002 	for(;;) {
1003 		arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
1004 		if(buf[0] == '\0')
1005 			break;
1006 		f = tkctaglook(tk, nil, buf);
1007 		if(f == nil)
1008 			return TkBadtg;
1009 		for(t = f->obj; t; t = t->taglist)
1010 			tkbbmax(&bb, &t->item->p.bb);
1011 	}
1012 	return tkvalue(val, "%d %d %d %d", bb.min.x, bb.min.y, bb.max.x, bb.max.y);
1013 }
1014 
1015 static char*
tkcvscanvx(Tk * tk,char * arg,char ** val)1016 tkcvscanvx(Tk *tk, char *arg, char **val)
1017 {
1018 	int x, s;
1019 	TkCanvas *c;
1020 	Point p;
1021 	char buf[Tkmaxitem];
1022 
1023 	c = TKobj(TkCanvas, tk);
1024 	arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
1025 	if(buf[0] == '\0')
1026 		return TkBadvl;
1027 
1028 	p = tkposn(tk);
1029 	x = atoi(buf) + c->view.x - (p.x + tk->borderwidth);
1030 
1031 	if(*arg) {
1032 		tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
1033 		s = atoi(buf);
1034 		if (s) {
1035 			if (x>=0)
1036 				x = ((x+s/2)/s)*s;
1037 			else
1038 				x = ((x-s/2)/s)*s;
1039 		}
1040 	}
1041 	return tkvalue(val, "%d", x);
1042 }
1043 
1044 static char*
tkcvscanvy(Tk * tk,char * arg,char ** val)1045 tkcvscanvy(Tk *tk, char *arg, char **val)
1046 {
1047 	int y, s;
1048 	TkCanvas *c;
1049 	Point p;
1050 	char buf[Tkmaxitem];
1051 
1052 	c = TKobj(TkCanvas, tk);
1053 	arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
1054 	if(buf[0] == '\0')
1055 		return TkBadvl;
1056 
1057 	p = tkposn(tk);
1058 	y = atoi(buf) + c->view.y - (p.y + tk->borderwidth);
1059 
1060 	if(*arg) {
1061 		tkitem(buf, arg);
1062 		s = atoi(buf);
1063 		if (s) {
1064 			if (y>=0)
1065 				y = ((y+s/2)/s)*s;
1066 			else
1067 				y = ((y-s/2)/s)*s;
1068 		}
1069 	}
1070 	return tkvalue(val, "%d", y);
1071 }
1072 
1073 static char *
tkcvsscreenx(Tk * tk,char * arg,char ** val)1074 tkcvsscreenx(Tk *tk, char *arg, char **val)
1075 {
1076 	int x;
1077 	TkCanvas *c;
1078 	Point p;
1079 	char buf[Tkmaxitem];
1080 
1081 	c = TKobj(TkCanvas, tk);
1082 	tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
1083 	if(buf[0] == '\0')
1084 		return TkBadvl;
1085 	p = tkposn(tk);
1086 	x = atoi(buf) - c->view.x + (p.x + tk->borderwidth);
1087 	return tkvalue(val, "%d", x);
1088 }
1089 
1090 static char *
tkcvsscreeny(Tk * tk,char * arg,char ** val)1091 tkcvsscreeny(Tk *tk, char *arg, char **val)
1092 {
1093 	int y;
1094 	TkCanvas *c;
1095 	Point p;
1096 	char buf[Tkmaxitem];
1097 
1098 	c = TKobj(TkCanvas, tk);
1099 	tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
1100 	if(buf[0] == '\0')
1101 		return TkBadvl;
1102 	p = tkposn(tk);
1103 	y = atoi(buf) - c->view.y + (p.y + tk->borderwidth);
1104 	return tkvalue(val, "%d", y);
1105 }
1106 
1107 static char*
tkcvscoords(Tk * tk,char * arg,char ** val)1108 tkcvscoords(Tk *tk, char *arg, char **val)
1109 {
1110 	int i;
1111 	Point *p;
1112 	TkCtag *t;
1113 	TkName *f;
1114 	TkCanvas *c;
1115 	TkCitem *item;
1116 	char *fmt, *e, *v, buf[Tkmaxitem];
1117 
1118 	arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
1119 	if(buf[0] == '\0')
1120 		return TkBadvl;
1121 
1122 	f = tkctaglook(tk, nil, buf);
1123 	if(f == nil || f->obj == nil)
1124 		return TkBadtg;
1125 
1126 	c = TKobj(TkCanvas, tk);
1127 
1128 	t = tkcfirsttag(c->head, f->obj);
1129 	if(t == nil)
1130 		return TkBadtg;
1131 
1132 	item = t->item;
1133 
1134 	if(*arg == '\0') {
1135 		fmt = "%s";
1136 		p = item->p.parampt;
1137 		for(i = 0; i < item->p.npoint; i++) {
1138 			v = tkfprint(buf, p->x);
1139 			*v++ = ' ';
1140 			tkfprint(v, p->y);
1141 			e = tkvalue(val, fmt, buf);
1142 			if(e != nil)
1143 				return e;
1144 			fmt = " %s";
1145 			p++;
1146 		}
1147 		return nil;
1148 	}
1149 
1150 	tkbbmax(&c->update, &item->p.bb);
1151 	e = tkcimethod[item->type].coord(item, arg, 0, 0);
1152 	tkbbmax(&c->update, &item->p.bb);
1153 	tkcvssetdirty(tk);
1154 	return e;
1155 }
1156 
1157 static char*
tkcvsscale(Tk * tk,char * arg,char ** val)1158 tkcvsscale(Tk *tk, char *arg, char **val)
1159 {
1160 	TkName *f;
1161 	TkCtag *t;
1162 	TkCanvas *c;
1163 	TkCpoints pts;
1164 	TkCitem *item;
1165 	int j;
1166 	char *e, buf[Tkmaxitem];
1167 	Point *p, *d, origin, scalef;
1168 
1169 	USED(val);
1170 
1171 	c = TKobj(TkCanvas, tk);
1172 
1173 	arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
1174 	f = tkctaglook(tk, nil, buf);
1175 	if(f == nil || f->obj == nil)
1176 		return TkBadtg;
1177 
1178 	e = tkparsepts(tk->env->top, &pts, &arg, 0);
1179 	if(e != nil)
1180 		return e;
1181 	if(pts.npoint != 2) {
1182 		tkfreepoint(&pts);
1183 		return TkFewpt;
1184 	}
1185 	origin = pts.parampt[0];
1186 	scalef = pts.parampt[1];
1187 	tkfreepoint(&pts);
1188 	for(t = f->obj; t; t = t->taglist) {
1189 		item = t->item;
1190 		p = item->p.parampt;
1191 		d = item->p.drawpt;
1192 		for(j = 0; j < item->p.npoint; j++) {
1193 			p->x -= origin.x;
1194 			p->y -= origin.y;
1195 			p->x = TKF2I(p->x*scalef.x);
1196 			p->y = TKF2I(p->y*scalef.y);
1197 			p->x += origin.x;
1198 			p->y += origin.y;
1199 			d->x = TKF2I(p->x);
1200 			d->y = TKF2I(p->y);
1201 			d++;
1202 			p++;
1203 		}
1204 		tkbbmax(&c->update, &item->p.bb);
1205 		e = tkcimethod[item->type].coord(item, nil, 0, 0);
1206 		tkbbmax(&c->update, &item->p.bb);
1207 		if(e != nil)
1208 			return e;
1209 
1210 		tkcvssetdirty(tk);
1211 	}
1212 	return nil;
1213 }
1214 
1215 static char*
tkcvsdtag(Tk * tk,char * arg,char ** val)1216 tkcvsdtag(Tk *tk, char *arg, char **val)
1217 {
1218 	TkName *f, *dt;
1219 	char buf[Tkmaxitem];
1220 	TkCtag **l, *t, *it, *tf;
1221 
1222 	USED(val);
1223 
1224 	arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
1225 	f = tkctaglook(tk, nil, buf);
1226 	if(f == nil || f->obj == nil)
1227 		return TkBadtg;
1228 
1229 /*
1230 	XXX this code doesn't appear to work properly.
1231 	fix it later. for the moment, it's just a somewhat more
1232 	efficient substitute for the later code, so just use that
1233 	instead.
1234 
1235 	if(*arg == '\0') {
1236 		for(t = f->obj; t; t = tf) {
1237 			l = &t->item->stag;
1238 			for(it = *l; it; it = it->itemlist) {
1239 				if(it->item == t->item) {
1240 					*l = it->itemlist;
1241 					break;
1242 				}
1243 				l = &it->itemlist;
1244 			}
1245 
1246 			tf = t->taglist;
1247 			free(t);
1248 		}
1249 		f->obj = nil;
1250 		return nil;
1251 	}
1252 */
1253 	if (*arg == '\0')
1254 		dt = f;
1255 	else {
1256 		tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
1257 		dt = tkctaglook(tk, nil, buf);
1258 		if(dt == nil || dt->obj == nil)
1259 			return TkBadtg;
1260 	}
1261 
1262 	for(t = f->obj; t; t = t->taglist) {
1263 		l = (TkCtag **)&dt->obj;
1264 		for(it = dt->obj; it; it = it->taglist) {
1265 			if(t->item == it->item) {
1266 				*l = it->taglist;
1267 				l = &t->item->stag;
1268 				for(tf = *l; tf; tf = tf->itemlist) {
1269 					if(tf == it) {
1270 						*l = tf->itemlist;
1271 						break;
1272 					}
1273 					l = &tf->itemlist;
1274 				}
1275 				free(it);
1276 				break;
1277 			}
1278 			l = &it->taglist;
1279 		}
1280 	}
1281 	return nil;
1282 }
1283 
1284 static char*
tkcvsdchars(Tk * tk,char * arg,char ** val)1285 tkcvsdchars(Tk *tk, char *arg, char **val)
1286 {
1287 	TkCtag *t;
1288 	TkName *f;
1289 	char *e, buf[Tkmaxitem];
1290 
1291 	USED(val);
1292 
1293 	arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
1294 	f = tkctaglook(tk, nil, buf);
1295 	if(f == nil || f->obj == nil)
1296 		return TkBadtg;
1297 
1298 	for(t = f->obj; t; t = t->taglist) {
1299 		if(t->item->type == TkCVtext) {
1300 			e = tkcvstextdchar(tk, t->item, arg);
1301 			if(e != nil)
1302 				return e;
1303 		}
1304 	}
1305 
1306 	return nil;
1307 }
1308 
1309 static char*
tkcvsindex(Tk * tk,char * arg,char ** val)1310 tkcvsindex(Tk *tk, char *arg, char **val)
1311 {
1312 	TkCtag *t;
1313 	TkName *f;
1314 	char *e, buf[Tkmaxitem];
1315 
1316 	USED(val);
1317 
1318 	arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
1319 	f = tkctaglook(tk, nil, buf);
1320 	if(f == nil || f->obj == nil)
1321 		return TkBadtg;
1322 
1323 	for(t = f->obj; t; t = t->taglist) {
1324 		if(t->item->type == TkCVtext) {
1325 			e = tkcvstextindex(tk, t->item, arg, val);
1326 			if(e != nil)
1327 				return e;
1328 			return nil;
1329 		}
1330 	}
1331 	return nil;
1332 }
1333 
1334 static char*
tkcvsicursor(Tk * tk,char * arg,char ** val)1335 tkcvsicursor(Tk *tk, char *arg, char **val)
1336 {
1337 	TkCtag *t;
1338 	TkName *f;
1339 	char *e, buf[Tkmaxitem];
1340 
1341 	USED(val);
1342 
1343 	arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
1344 	f = tkctaglook(tk, nil, buf);
1345 	if(f == nil || f->obj == nil)
1346 		return TkBadtg;
1347 
1348 	for(t = f->obj; t; t = t->taglist) {
1349 		if(t->item->type == TkCVtext) {
1350 			e = tkcvstexticursor(tk, t->item, arg);
1351 			if(e != nil)
1352 				return e;
1353 			return nil;
1354 		}
1355 	}
1356 	return nil;
1357 }
1358 
1359 static char*
tkcvsinsert(Tk * tk,char * arg,char ** val)1360 tkcvsinsert(Tk *tk, char *arg, char **val)
1361 {
1362 	TkCtag *t;
1363 	TkName *f;
1364 	char *e, buf[Tkmaxitem];
1365 
1366 	USED(val);
1367 
1368 	arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
1369 	f = tkctaglook(tk, nil, buf);
1370 	if(f == nil || f->obj == nil)
1371 		return TkBadtg;
1372 
1373 	for(t = f->obj; t; t = t->taglist) {
1374 		if(t->item->type == TkCVtext) {
1375 			e = tkcvstextinsert(tk, t->item, arg);
1376 			if(e != nil)
1377 				return e;
1378 		}
1379 	}
1380 
1381 	return nil;
1382 }
1383 
1384 static char*
tkcvsselect(Tk * tk,char * arg,char ** val)1385 tkcvsselect(Tk *tk, char *arg, char **val)
1386 {
1387 	int op;
1388 	TkCtag *t;
1389 	TkName *f;
1390 	TkCanvas *c;
1391 	char buf[Tkmaxitem];
1392 
1393 	c = TKobj(TkCanvas, tk);
1394 
1395 	arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
1396 	if(strcmp(buf, "clear") == 0) {
1397 		tkcvstextclr(tk);
1398 		return nil;
1399 	}
1400 	if(strcmp(buf, "item") == 0) {
1401 		if(c->selection)
1402 			return tkvalue(val, "%d", c->selection->id);
1403 		return nil;
1404 	}
1405 
1406 	if(strcmp(buf, "to") == 0)
1407 		op = TkCselto;
1408 	else
1409 	if(strcmp(buf, "from") == 0)
1410 		op = TkCselfrom;
1411 	else
1412 	if(strcmp(buf, "adjust") == 0)
1413 		op = TkCseladjust;
1414 	else
1415 		return TkBadcm;
1416 
1417 	arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
1418 	f = tkctaglook(tk, nil, buf);
1419 	if(f == nil)
1420 		return TkBadtg;
1421 
1422 	t = tkcfirsttag(c->head, f->obj);
1423 	if(t == nil)
1424 		return TkBadtg;
1425 
1426 	return tkcvstextselect(tk, t->item, arg, op);
1427 }
1428 
1429 static char*
tkcvsitemcget(Tk * tk,char * arg,char ** val)1430 tkcvsitemcget(Tk *tk, char *arg, char **val)
1431 {
1432 	TkName *f;
1433 	TkCtag *t;
1434 	TkCitem *i;
1435 	char buf[Tkmaxitem];
1436 
1437 	arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
1438 	f = tkctaglook(tk, nil, buf);
1439 	if(f == nil || f->obj == nil)
1440 		return TkBadtg;
1441 
1442 	for(i = TKobj(TkCanvas, tk)->head; i; i = i->next) {
1443 		for(t = f->obj; t; t = t->taglist)
1444 			if(i == t->item)
1445 				return tkcimethod[i->type].cget(i, arg, val);
1446 	}
1447 	return nil;
1448 }
1449 
1450 static char*
tkcvsitemconf(Tk * tk,char * arg,char ** val)1451 tkcvsitemconf(Tk *tk, char *arg, char **val)
1452 {
1453 	char *e;
1454 	TkName *f;
1455 	TkCtag *t;
1456 	TkCitem *i;
1457 	TkCanvas *c;
1458 	char buf[Tkmaxitem];
1459 
1460 	USED(val);
1461 	arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
1462 	f = tkctaglook(tk, nil, buf);
1463 	if(f == nil || f->obj == nil)
1464 		return TkBadtg;
1465 
1466 	c = TKobj(TkCanvas, tk);
1467 	for(t = f->obj; t; t = t->taglist) {
1468 		for(i = c->head; i; i = i->next) {
1469 			if(i == t->item) {
1470 				tkbbmax(&c->update, &i->p.bb);
1471 				e = tkcimethod[i->type].conf(tk, i, arg);
1472 				tkbbmax(&c->update, &i->p.bb);
1473 				tkcvssetdirty(tk);
1474 				if(e != nil)
1475 					return e;
1476 			}
1477 		}
1478 	}
1479 	return nil;
1480 }
1481 
1482 static void
tkcvsfreename(TkCanvas * c,TkName * n)1483 tkcvsfreename(TkCanvas *c, TkName *n)
1484 {
1485 	ulong h;
1486 	char *p, *s;
1487 	TkName *f, **l;
1488 
1489 	/* just free implicit ones for now */
1490 	if(n == nil)
1491 		return;
1492 	s = n->name;
1493 	if(s == nil || (s[0] < '0' || s[0] > '9'))
1494 		return;
1495 	h = 0;
1496 	for(p = s; *p; p++)
1497 		h += 3*h + *p;
1498 	l = &c->thash[h%TkChash];
1499 	for(f = *l; f; l = &f->link, f = *l)
1500 		if(f == n){
1501 			*l = f->link;
1502 			tkfreebind(f->prop.binds);
1503 			free(f);
1504 			return;
1505 		}
1506 }
1507 
1508 static char*
tkcvsdelete(Tk * tk,char * arg,char ** val)1509 tkcvsdelete(Tk *tk, char *arg, char **val)
1510 {
1511 	TkName *f;
1512 	TkCanvas *c;
1513 	char buf[Tkmaxitem];
1514 	TkCitem *item, *prev, *i;
1515 	TkCtag *t, *inext, **l, *dit, *it;
1516 
1517 	USED(val);
1518 
1519 	c = TKobj(TkCanvas, tk);
1520 	for(;;) {
1521 		arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
1522 		if(buf[0] == '\0')
1523 			break;
1524 		f = tkctaglook(tk, nil, buf);
1525 		if(f == nil || f->obj == nil)
1526 			return nil;
1527 		while(f->obj) {
1528 			t = f->obj;
1529 			item = t->item;
1530 			for(it = item->stag; it; it = inext) {
1531 				inext = it->itemlist;
1532 				l = (TkCtag **)&it->name->obj;
1533 				for(dit = *l; dit; dit = dit->taglist) {
1534 					if(dit->item == item) {
1535 						*l = dit->taglist;
1536 						if(dit != t){
1537 							tkcvsfreename(c, dit->name);
1538 							free(dit);
1539 						}
1540 						break;
1541 					}
1542 					l = &dit->taglist;
1543 				}
1544 			}
1545 			tkbbmax(&c->update, &item->p.bb);
1546 			tkcvssetdirty(tk);
1547 			prev = nil;
1548 			for(i = c->head; i; i = i->next) {
1549 				if(i == item)
1550 					break;
1551 				prev = i;
1552 			}
1553 			if(prev == nil)
1554 				c->head = i->next;
1555 			else
1556 				prev->next = i->next;
1557 			if(c->tail == item)
1558 				c->tail = prev;
1559 			if(c->focus == item)
1560 				c->focus = nil;
1561 			if(c->mouse == item)
1562 				c->mouse = nil;
1563 			if(c->selection == item)
1564 				c->selection = nil;
1565 			if(c->curtag.item == item)
1566 				c->current->obj = nil;
1567 			if (c->grab == item)
1568 				c->grab = nil;
1569 
1570 			tkcvsfreeitem(item);
1571 			free(t);
1572 		}
1573 	}
1574 	return nil;
1575 }
1576 
1577 static char*
tkcvsfocus(Tk * tk,char * arg,char ** val)1578 tkcvsfocus(Tk *tk, char *arg, char **val)
1579 {
1580 	TkName *f;
1581 	TkCtag *t;
1582 	TkCanvas *c;
1583 	TkCitem *i, *focus;
1584 	char buf[Tkmaxitem];
1585 
1586 	c = TKobj(TkCanvas, tk);
1587 
1588 	if(*arg == '\0') {
1589 		if(c->focus == nil)
1590 			return nil;
1591 		return tkvalue(val, "%d", c->focus->id);
1592 	}
1593 
1594 	tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
1595 	if(buf[0] == '\0')
1596 		return TkBadvl;
1597 	f = tkctaglook(tk, nil, buf);
1598 	if(f == nil || f->obj == nil)
1599 		return nil;
1600 
1601 	focus = c->focus;
1602 	if(focus != nil && focus->type == TkCVtext)
1603 		tkcvstextfocus(tk, focus, 0);
1604 
1605 	for(i = c->head; i; i = i->next) {
1606 		if(i->type == TkCVtext || i->type == TkCVwindow) {
1607 			for(t = f->obj; t; t = t->taglist)
1608 				if(t->item == i)
1609 					focus = i;
1610 		}
1611 	}
1612 
1613 	if(focus != nil && focus->type == TkCVtext)
1614 		tkcvstextfocus(tk, focus, 1);
1615 
1616 	c->focus = focus;
1617 	return nil;
1618 }
1619 
1620 static char*
tkcvsgettags(Tk * tk,char * arg,char ** val)1621 tkcvsgettags(Tk *tk, char *arg, char **val)
1622 {
1623 	TkCtag *t;
1624 	TkName *f;
1625 	TkCanvas *c;
1626 	char *fmt, *e, buf[Tkmaxitem];
1627 
1628 	tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
1629 	if(buf[0] == '\0')
1630 		return TkBadvl;
1631 
1632 	f = tkctaglook(tk, nil, buf);
1633 	if(f == nil)
1634 		return TkBadtg;
1635 
1636 	c = TKobj(TkCanvas, tk);
1637 	t = tkclasttag(c->head, f->obj);
1638 	if(t == nil)
1639 		return TkBadtg;
1640 	fmt = "%s";
1641 	t = t->item->stag;
1642 	while(t) {
1643 		/* XXX when might t->name be legally nil? */
1644 		if (t->name != nil) {
1645 			if (strcmp(t->name->name, "all")) {
1646 				e = tkvalue(val, fmt, t->name->name);
1647 				if(e != nil)
1648 					return e;
1649 				fmt = " %s";
1650 			}
1651 		}
1652 		t = t->itemlist;
1653 	}
1654 	return nil;
1655 }
1656 
1657 static char*
tkcvslower(Tk * tk,char * arg,char ** val)1658 tkcvslower(Tk *tk, char *arg, char **val)
1659 {
1660 	TkCtag *t;
1661 	TkCanvas *c;
1662 	TkName *f, *b;
1663 	char buf[Tkmaxitem];
1664 	TkCitem *it, **l, **below, *items, **itemtail, *prev, *iprev;
1665 
1666 	USED(val);
1667 	c = TKobj(TkCanvas, tk);
1668 
1669 	arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
1670 	f = tkctaglook(tk, nil, buf);
1671 	if(f == nil || f->obj == nil)
1672 		return nil;
1673 
1674 	below = &c->head;
1675 	tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
1676 	if(buf[0] != '\0') {
1677 		b = tkctaglook(tk, nil, buf);
1678 		if(b == nil || f->obj == nil)
1679 			return TkBadtg;
1680 		for(it = c->head; it; it = it->next) {
1681 			for(t = b->obj; t; t = t->taglist)
1682 				if(t->item == it)
1683 					goto found;
1684 			below = &it->next;
1685 		}
1686 	found:;
1687 	}
1688 	l = &c->head;
1689 	prev = iprev = nil;
1690 	itemtail = &items;;
1691 	for (it = *l; it != nil; it = *l) {
1692 		for (t = f->obj; t; t = t->taglist) {
1693 			if(t->item == it) {
1694 				if (it == *below || below == &it->next)
1695 					below = l;
1696 				if (it == c->tail)
1697 					c->tail = prev;
1698 				*l = it->next;
1699 				*itemtail = it;
1700 				iprev = it;
1701 				itemtail = &it->next;
1702 				tkbbmax(&c->update, &it->p.bb);
1703 				goto next;
1704 			}
1705 		}
1706 		prev = it;
1707 		l = &it->next;
1708 next:;
1709 	}
1710 	if (prev == nil)
1711 		c->tail = iprev;
1712 	*itemtail = *below;
1713 	*below = items;
1714 	tkcvssetdirty(tk);
1715 	return nil;
1716 }
1717 
1718 static char*
tkcvsmove(Tk * tk,char * arg,char ** val)1719 tkcvsmove(Tk *tk, char *arg, char **val)
1720 {
1721 	TkCtag *t;
1722 	int fx, fy;
1723 	TkTop *top;
1724 	TkCpoints *p;
1725 	TkName *tag;
1726 	Rectangle *u;
1727 	TkCitem *item;
1728 	char *e;
1729 	char buf[Tkmaxitem];
1730 
1731 	USED(val);
1732 	top = tk->env->top;
1733 	arg = tkword(top, arg, buf, buf+sizeof(buf), nil);
1734 	tag = tkctaglook(tk, nil, buf);
1735 	if(tag == nil)
1736 		return nil;
1737 
1738 	e = tkfracword(top, &arg, &fx, nil);
1739 	if (e != nil)
1740 		return e;
1741 	e = tkfracword(top, &arg, &fy, nil);
1742 	if(e != nil)
1743 		return e;
1744 
1745 	u = &TKobj(TkCanvas, tk)->update;
1746 	for(t = tag->obj; t; t = t->taglist) {
1747 		item = t->item;
1748 		p = &item->p;
1749 		tkbbmax(u, &p->bb);
1750 		tkcimethod[item->type].coord(item, nil, fx, fy);
1751 		tkbbmax(u, &p->bb);
1752 	}
1753 	tkcvssetdirty(tk);
1754 	return nil;
1755 }
1756 
1757 static char*
tkcvsraise(Tk * tk,char * arg,char ** val)1758 tkcvsraise(Tk *tk, char *arg, char **val)
1759 {
1760 	TkCtag *t;
1761 	TkCanvas *c;
1762 	TkName *f, *a;
1763 	char buf[Tkmaxitem];
1764 	TkCitem *prev, *it, *above, *items, *itemtail, *next;
1765 
1766 	USED(val);
1767 	c = TKobj(TkCanvas, tk);
1768 
1769 	arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
1770 	f = tkctaglook(tk, nil, buf);
1771 	if(f == nil)
1772 		return nil;
1773 
1774 	above = c->tail;
1775 	tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
1776 	if(buf[0] != '\0') {
1777 		a = tkctaglook(tk, nil, buf);
1778 		if(a == nil)
1779 			return TkBadtg;
1780 		/*
1781 		 * find topmost item in the display list matching the "above" tag
1782 		 */
1783 		for(it = c->head; it != nil; it = it->next) {
1784 			for(t = a->obj; t; t = t->taglist)
1785 				if(t->item == it)
1786 					above = it;
1787 		}
1788 	}
1789 	prev = nil;
1790 	items = itemtail = nil;
1791 	for (it = c->head; it != nil; it = next) {
1792 		next = it->next;
1793 		for (t = f->obj; t; t = t->taglist) {
1794 			if(t->item == it) {
1795 				if (it == above)
1796 					above = next;
1797 				if (prev)
1798 					prev->next = next;
1799 				else
1800 					c->head = next;
1801 				if (itemtail)
1802 					itemtail->next = it;
1803 				else
1804 					items = it;
1805 				itemtail = it;
1806 				tkbbmax(&c->update, &it->p.bb);
1807 				goto next;
1808 			}
1809 		}
1810 		prev = it;
1811 next:;
1812 	}
1813 	if (items != nil) {
1814 		if (above) {
1815 			itemtail->next = above->next;
1816 			if (above->next == nil)
1817 				c->tail = itemtail;
1818 			above->next = items;
1819 		} else {
1820 			if (prev)
1821 				prev->next = items;
1822 			else
1823 				c->head = items;
1824 			c->tail = itemtail;
1825 			itemtail->next = nil;
1826 		}
1827 	}
1828 
1829 	tkcvssetdirty(tk);
1830 	return nil;
1831 }
1832 
1833 static char*
tkcvstype(Tk * tk,char * arg,char ** val)1834 tkcvstype(Tk *tk, char *arg, char **val)
1835 {
1836 	TkCtag *t;
1837 	TkName *f;
1838 	TkCanvas *c;
1839 	char buf[Tkmaxitem];
1840 
1841 	tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
1842 	if(buf[0] == '\0')
1843 		return TkBadvl;
1844 
1845 	f = tkctaglook(tk, nil, buf);
1846 	if(f == nil || f->obj == nil)
1847 		return nil;
1848 
1849 	c = TKobj(TkCanvas, tk);
1850 
1851 	t = tkcfirsttag(c->head, f->obj);
1852 	if(t == nil)
1853 		return nil;
1854 
1855 	return tkvalue(val, "%s", tkcimethod[t->item->type].name);
1856 }
1857 
1858 static char*
tkcvsview(Tk * tk,char * arg,char ** val,int nl,int * posn,int min,int max,int inc)1859 tkcvsview(Tk *tk, char *arg, char **val, int nl, int *posn, int min, int max, int inc)
1860 {
1861 	TkTop *t;
1862 	int top, bot, diff, amount;
1863 	char *e;
1864 	char buf[Tkmaxitem], *v;
1865 
1866 	diff = max-min;
1867 	if(*arg == '\0') {
1868 		if ( diff == 0 )
1869 			top = bot = 0;
1870 		else {
1871 			top = TKI2F(*posn-min)/diff;
1872 			bot = TKI2F(*posn+nl-min)/diff;
1873 		}
1874 		v = tkfprint(buf, top);
1875 		*v++ = ' ';
1876 		tkfprint(v, bot);
1877 		return tkvalue(val, "%s", buf);
1878 	}
1879 
1880 	t = tk->env->top;
1881 	arg = tkword(t, arg, buf, buf+sizeof(buf), nil);
1882 	if(strcmp(buf, "moveto") == 0) {
1883 		e = tkfrac(&arg, &top, nil);
1884 		if (e != nil)
1885 			return e;
1886 		*posn = min + TKF2I((top+1)*diff);
1887 	}
1888 	else
1889 	if(strcmp(buf, "scroll") == 0) {
1890 		arg = tkword(t, arg, buf, buf+sizeof(buf), nil);
1891 		amount = atoi(buf);
1892 		tkword(t, arg, buf, buf+sizeof(buf), nil);
1893 		if(buf[0] == 'p')		/* Pages */
1894 			amount = amount * nl * 9 /10;
1895 		else if (inc > 0)
1896 			amount *= inc;
1897 		else
1898 			amount = amount * nl / 10;
1899 		*posn += amount;
1900 	}
1901 	else
1902 		return TkBadcm;
1903 
1904 	bot = max - nl;
1905 	if(*posn > bot)
1906 		*posn = bot;
1907 	if(*posn < min)
1908 		*posn = min;
1909 
1910 	tk->dirty = tkrect(tk, 0);
1911 	return nil;
1912 }
1913 
1914 static char*
tkcvsyview(Tk * tk,char * arg,char ** val)1915 tkcvsyview(Tk *tk, char *arg, char **val)
1916 {
1917 	int si;
1918 	char *e;
1919 	TkCanvas *c = TKobj(TkCanvas, tk);
1920 
1921 	si = TKF2I(c->yscrolli);
1922 	e = tkcvsview(tk, arg, val, tk->act.height, &c->view.y, c->region.min.y, c->region.max.y, si);
1923 	tkcvssv(tk);
1924 	return e;
1925 }
1926 
1927 static char*
tkcvsxview(Tk * tk,char * arg,char ** val)1928 tkcvsxview(Tk *tk, char *arg, char **val)
1929 {
1930 	int si;
1931 	char *e;
1932 	TkCanvas *c = TKobj(TkCanvas, tk);
1933 
1934 	si = TKF2I(c->xscrolli);
1935 	e = tkcvsview(tk, arg, val, tk->act.width, &c->view.x, c->region.min.x, c->region.max.x, si);
1936 	tkcvssh(tk);
1937 	return e;
1938 }
1939 
1940 /*
1941  * return in posn the new view origin such that (preferably) smin and smax
1942  * lie between cmin and cmax (cmin is the current view origin, and cmax the
1943  * other end of the visible area).
1944  * adjust posn (the view origin) so that (preferably) both smin and smax lie
1945  * inside cmin to cmax. if both smin and smax cannot fit, then
1946  * at least make sure that spref (smin<=spref<=smax) is visible.
1947  * return 0 if no adjustment is required (the interval is already visible).
1948  *
1949  * attempt to make an adjustment as small as possible that
1950  * fits these criteria.
1951  */
1952 static int
tkadjustvis(int * posn,int c0,int c1,int s0,int s1,int spref)1953 tkadjustvis(int *posn, int c0, int c1, int s0, int s1, int spref)
1954 {
1955 	int d, v;
1956 
1957 	d = c1 - c0;		/* visible width */
1958 
1959 	/*
1960 	 * if requested range fits inside visible range,
1961 	 * no adjustment is necessary
1962 	 */
1963 	if (c0 <= s0 && s1 <= c1)
1964 		return 0;
1965 
1966 	/*
1967 	 * if requested range fits, make it fully visible
1968 	 */
1969 	if (s1 - s0 < d) {
1970 		if (s0 < c0)
1971 			v = s0;
1972 		else
1973 			v = s1 - d;
1974 	} else {
1975 		/*
1976 		 * choose upper or lower end of requested range,
1977 		 * depending on which end of requested area is already
1978 		 * visible (if any).
1979 		 */
1980 		if (c0 <= s1 && s1 < c1) {		/* overlapping left of visible */
1981 			v = s1 - d;
1982 			if (v > spref)
1983 				v = spref;
1984 		}
1985 		else
1986 		if (c0 <= s0 && s0 < c1) {		/* overlapping right of visible */
1987 			v = s0;
1988 			if (v + d <= spref)
1989 				v = spref - d;
1990 		}
1991 		else
1992 		if (s1 < c0) {				/* left of visible */
1993 			v = spref;
1994 			if (v + d > s1)
1995 				v = s1 - d;
1996 		}
1997 		else {					/* right of visible */
1998 			v = spref - d;
1999 			if (v < s0)
2000 				v = s0;
2001 		}
2002 	}
2003 	*posn = v;
2004 	return 1;
2005 }
2006 
2007 static void
tkcvsseerect(Tk * tk,Rectangle r,Point p)2008 tkcvsseerect(Tk *tk, Rectangle r, Point p)
2009 {
2010 	TkCanvas *c;
2011 	int scrollh, scrollv;
2012 
2013 	c = TKobj(TkCanvas, tk);
2014 
2015 	scrollh = tkadjustvis(&c->view.x, c->view.x, c->view.x + tk->act.width,
2016 			r.min.x, r.max.x, p.x);
2017 	scrollv = tkadjustvis(&c->view.y, c->view.y, c->view.y + tk->act.height,
2018 			r.min.y, r.max.y, p.y);
2019 	if (scrollh)
2020 		tkcvssh(tk);
2021 	if (scrollv)
2022 		tkcvssv(tk);
2023 	if (scrollh || scrollv)
2024 		tk->dirty = tkrect(tk, 0);
2025 }
2026 
2027 static char*
tkcvssee(Tk * tk,char * arg,char ** val)2028 tkcvssee(Tk *tk, char *arg, char **val)
2029 {
2030 	Rectangle r;
2031 	int n, coords[4];
2032 	char *e;
2033 
2034 	USED(val);
2035 	n = 0;
2036 	while (n < 4) {
2037 		if (*arg == '\0')
2038 			break;
2039 		e = tkfracword(tk->env->top, &arg, &coords[n++], nil);
2040 		if (e != nil)
2041 			return e;
2042 	}
2043 
2044 	if (n != 2 && n != 4)
2045 		return TkFewpt;
2046 
2047 	r.min.x = TKF2I(coords[0]);
2048 	r.min.y = TKF2I(coords[1]);
2049 	if (n == 4) {
2050 		r.max.x = TKF2I(coords[2]);
2051 		r.max.y = TKF2I(coords[3]);
2052 	} else
2053 		r.max = r.min;
2054 	r = canonrect(r);
2055 	/*
2056 	 * XXX should intersect r with scrollregion here, as you shouldn't
2057 	 * be able to display things outside the scroll region. (??)
2058 	 */
2059 
2060 	tkcvsseerect(tk, r, r.min);
2061 	return nil;
2062 }
2063 
2064 static void
tkcvsseesub(Tk * tk,Rectangle * rr,Point * pp)2065 tkcvsseesub(Tk *tk, Rectangle *rr, Point *pp)
2066 {
2067 	Rectangle r;
2068 	Point p;
2069 	TkCanvas *c;
2070 	c = TKobj(TkCanvas, tk);
2071 
2072 	r = rectaddpt(*rr, c->view);
2073 	p = addpt(*pp, c->view);
2074 
2075 	tkcvsseerect(tk, r, p);
2076 
2077 	*rr = rectsubpt(r, c->view);
2078 	*pp = subpt(p, c->view);
2079 }
2080 
2081 static void
tkcvsgetimgs(Tk * tk,Image ** image,Image ** mask)2082 tkcvsgetimgs(Tk* tk, Image **image, Image **mask)
2083 {
2084 	TkCanvas *c;
2085 	c = TKobj(TkCanvas, tk);
2086 
2087 	*image = c->image;
2088 	*mask = c->mask;		/* XXX this is wrong - the mask image has nothing to do with the main image */
2089 }
2090 
2091 TkCimeth tkcimethod[] =
2092 {
2093 	"line",		tkcvslinecreat,
2094 			tkcvslinedraw,
2095 			tkcvslinefree,
2096 			tkcvslinecoord,
2097 			tkcvslinecget,
2098 			tkcvslineconf,
2099 			tkcvslinehit,
2100 
2101 	"text",		tkcvstextcreat,
2102 			tkcvstextdraw,
2103 			tkcvstextfree,
2104 			tkcvstextcoord,
2105 			tkcvstextcget,
2106 			tkcvstextconf,
2107 			nil,
2108 
2109 	"rectangle",	tkcvsrectcreat,
2110 			tkcvsrectdraw,
2111 			tkcvsrectfree,
2112 			tkcvsrectcoord,
2113 			tkcvsrectcget,
2114 			tkcvsrectconf,
2115 			nil,
2116 
2117 	"oval",		tkcvsovalcreat,
2118 			tkcvsovaldraw,
2119 			tkcvsovalfree,
2120 			tkcvsovalcoord,
2121 			tkcvsovalcget,
2122 			tkcvsovalconf,
2123 			tkcvsovalhit,
2124 
2125 	"bitmap",	tkcvsbitcreat,
2126 			tkcvsbitdraw,
2127 			tkcvsbitfree,
2128 			tkcvsbitcoord,
2129 			tkcvsbitcget,
2130 			tkcvsbitconf,
2131 			nil,
2132 
2133 	"polygon",	tkcvspolycreat,
2134 			tkcvspolydraw,
2135 			tkcvspolyfree,
2136 			tkcvspolycoord,
2137 			tkcvspolycget,
2138 			tkcvspolyconf,
2139 			tkcvspolyhit,
2140 
2141 	"window",	tkcvswindcreat,
2142 			tkcvswinddraw,
2143 			tkcvswindfree,
2144 			tkcvswindcoord,
2145 			tkcvswindcget,
2146 			tkcvswindconf,
2147 			nil,
2148 
2149 	"image",	tkcvsimgcreat,
2150 			tkcvsimgdraw,
2151 			tkcvsimgfree,
2152 			tkcvsimgcoord,
2153 			tkcvsimgcget,
2154 			tkcvsimgconf,
2155 			nil,
2156 
2157 	"arc",		tkcvsarccreat,
2158 			tkcvsarcdraw,
2159 			tkcvsarcfree,
2160 			tkcvsarccoord,
2161 			tkcvsarccget,
2162 			tkcvsarcconf,
2163 			nil,
2164 	nil
2165 };
2166 
2167 static
2168 TkCmdtab tkcanvcmd[] =
2169 {
2170 	"addtag",		tkcvsaddtag,
2171 	"bbox",			tkcvsbbox,
2172 	"bind",			tkcvsbind,
2173 	"cget",			tkcvscget,
2174 	"configure",		tkcvsconf,
2175 	"create",		tkcvscreate,
2176 	"canvasx",		tkcvscanvx,
2177 	"canvasy",		tkcvscanvy,
2178 	"coords",		tkcvscoords,
2179 	"dchars",		tkcvsdchars,
2180 	"delete",		tkcvsdelete,
2181 	"dtag",			tkcvsdtag,
2182 	"find",			tkcvsfind,
2183 	"focus",		tkcvsfocus,
2184 	"gettags",		tkcvsgettags,
2185 	"grab",		tkcvsgrab,
2186 	"icursor",		tkcvsicursor,
2187 	"insert",		tkcvsinsert,
2188 	"index",		tkcvsindex,
2189 	"itemcget",		tkcvsitemcget,
2190 	"itemconfigure",	tkcvsitemconf,
2191 	"lower",		tkcvslower,
2192 	"move",			tkcvsmove,
2193 	"raise",		tkcvsraise,
2194 	"screenx",		tkcvsscreenx,
2195 	"screeny",		tkcvsscreeny,
2196 	"see",		tkcvssee,
2197 	"select",		tkcvsselect,
2198 	"scale",		tkcvsscale,
2199 	"type",			tkcvstype,
2200 	"yview",		tkcvsyview,
2201 	"xview",		tkcvsxview,
2202 	nil
2203 };
2204 
2205 TkMethod canvasmethod = {
2206 	"canvas",
2207 	tkcanvcmd,
2208 	tkfreecanv,
2209 	tkdrawcanv,
2210 	tkcvsgeom,
2211 	tkcvsgetimgs,
2212 	tkcvsfocusorder,
2213 	tkcvsdirty,
2214 	tkcvsrelpos,
2215 	tkcvsevent,
2216 	tkcvsseesub,
2217 	tkcvsinwindow,
2218 	nil,
2219 	tkcvsforgetsub,
2220 };
2221