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