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