xref: /inferno-os/libtk/canvu.c (revision c9c0d12ef55c878b0e361f9f6936bbb4c67b40fb)
1 #include <lib9.h>
2 #include <kernel.h>
3 #include "draw.h"
4 #include "tk.h"
5 #include "canvs.h"
6 
7 char*
tkparsepts(TkTop * t,TkCpoints * i,char ** arg,int close)8 tkparsepts(TkTop *t, TkCpoints *i, char **arg, int close)
9 {
10 	char *s, *e;
11 	Point *p, *d;
12 	int n, npoint;
13 
14 	i->parampt = nil;
15 	i->drawpt = nil;
16 	i->bb = bbnil;
17 	s = *arg;
18 	npoint = 0;
19 	while(*s) {
20 		s = tkskip(s, " \t");
21 		if(*s == '-' && (s[1] < '0' || s[1] > '9'))
22 			break;
23 		while(*s && *s != ' ' && *s != '\t')
24 			s++;
25 		npoint++;
26 	}
27 
28 	i->parampt = mallocz(npoint*sizeof(Point), 0);
29 	if(i->parampt == nil)
30 		return TkNomem;
31 
32 	s = *arg;
33 	p = i->parampt;
34 	npoint = 0;
35 	while(*s) {
36 		e = tkfracword(t, &s, &p->x, nil);
37 		if(e != nil)
38 			goto Error;
39 		e = tkfracword(t, &s, &p->y, nil);
40 		if(e != nil)
41 			goto Error;
42 		npoint++;
43 		s = tkskip(s, " \t");
44 		if(*s == '-' && (s[1] < '0' || s[1] > '9'))
45 			break;
46 		p++;
47 	}
48 	*arg = s;
49 	close = (close != 0);
50 	i->drawpt = mallocz((npoint+close)*sizeof(Point), 0);
51 	if(i->drawpt == nil){
52 		e = TkNomem;
53 		goto Error;
54 	}
55 
56 	d = i->drawpt;
57 	p = i->parampt;
58 	for(n = 0; n < npoint; n++) {
59 		d->x = TKF2I(p->x);
60 		d->y = TKF2I(p->y);
61 		if(d->x < i->bb.min.x)
62 			i->bb.min.x = d->x;
63 		if(d->x > i->bb.max.x)
64 			i->bb.max.x = d->x;
65 		if(d->y < i->bb.min.y)
66 			i->bb.min.y = d->y;
67 		if(d->y > i->bb.max.y)
68 			i->bb.max.y = d->y;
69 		d++;
70 		p++;
71 	}
72 	if (close)
73 		*d = i->drawpt[0];
74 
75 	i->npoint = npoint;
76 	return nil;
77 
78 Error:
79 	tkfreepoint(i);
80 	i->parampt = nil;
81 	i->drawpt = nil;
82 	return e;
83 }
84 
85 TkCitem*
tkcnewitem(Tk * tk,int t,int n)86 tkcnewitem(Tk *tk, int t, int n)
87 {
88 	TkCitem *i;
89 
90 	i = malloc(n);
91 	if(i == nil)
92 		return nil;
93 	memset(i, 0, n);
94 
95 	i->type = t;
96 	i->env = tk->env;
97 	i->env->ref++;
98 
99 	return i;
100 }
101 
102 /*
103  * expand the canvas's dirty rectangle, clipping
104  * appropriately to its boundaries.
105  */
106 void
tkcvssetdirty(Tk * tk)107 tkcvssetdirty(Tk *tk)
108 {
109 	TkCanvas *c;
110 	Rectangle r;
111 	c = TKobj(TkCanvas, tk);
112 
113 	r = tkrect(tk, 0);
114 	if (rectclip(&r, rectsubpt(c->update, c->view)))
115 		combinerect(&tk->dirty, r);
116 }
117 
118 void
tkxlatepts(Point * p,int npoints,int x,int y)119 tkxlatepts(Point *p, int npoints, int x, int y)
120 {
121 	while(npoints--) {
122 		p->x += x;
123 		p->y += y;
124 		p++;
125 	}
126 }
127 
128 void
tkbbmax(Rectangle * bb,Rectangle * r)129 tkbbmax(Rectangle *bb, Rectangle *r)
130 {
131 	if(r->min.x < bb->min.x)
132 		bb->min.x = r->min.x;
133 	if(r->min.y < bb->min.y)
134 		bb->min.y = r->min.y;
135 	if(r->max.x > bb->max.x)
136 		bb->max.x = r->max.x;
137 	if(r->max.y > bb->max.y)
138 		bb->max.y = r->max.y;
139 }
140 
141 void
tkpolybound(Point * p,int n,Rectangle * r)142 tkpolybound(Point *p, int n, Rectangle *r)
143 {
144 	while(n--) {
145 		if(p->x < r->min.x)
146 			r->min.x = p->x;
147 		if(p->y < r->min.y)
148 			r->min.y = p->y;
149 		if(p->x > r->max.x)
150 			r->max.x = p->x;
151 		if(p->y > r->max.y)
152 			r->max.y = p->y;
153 		p++;
154 	}
155 }
156 
157 /*
158  * look up a tag for a canvas item.
159  * if n is non-nil, and the tag isn't found,
160  * then add it to the canvas's taglist.
161  * NB if there are no binds done on the
162  * canvas, these tags never get cleared out,
163  * even if nothing refers to them.
164  */
165 TkName*
tkctaglook(Tk * tk,TkName * n,char * name)166 tkctaglook(Tk* tk, TkName *n, char *name)
167 {
168 	ulong h;
169 	TkCanvas *c;
170 	char *p, *s;
171 	TkName *f, **l;
172 
173 	c = TKobj(TkCanvas, tk);
174 
175 	s = name;
176 	if(s == nil)
177 		s = n->name;
178 
179 	if(strcmp(s, "current") == 0)
180 		return c->current;
181 
182 	h = 0;
183 	for(p = s; *p; p++)
184 		h += 3*h + *p;
185 
186 	l = &c->thash[h%TkChash];
187 	for(f = *l; f; f = f->link)
188 		if(strcmp(f->name, s) == 0)
189 			return f;
190 
191 	if(n == nil)
192 		return nil;
193 	n->link = *l;
194 	*l = n;
195 	return n;
196 }
197 
198 char*
tkcaddtag(Tk * tk,TkCitem * i,int new)199 tkcaddtag(Tk *tk, TkCitem *i, int new)
200 {
201 	TkCtag *t;
202 	TkCanvas *c;
203 	char buf[16];
204 	TkName *n, *f, *link;
205 
206 	c = TKobj(TkCanvas, tk);
207 	if(new != 0) {
208 		i->id = ++c->id;
209 		snprint(buf, sizeof(buf), "%d", i->id);
210 		n = tkmkname(buf);
211 		if(n == nil)
212 			return TkNomem;
213 		n->link = i->tags;
214 		i->tags = n;
215 	}
216 
217 	for(n = i->tags; n; n = link) {
218 		link = n->link;
219 		f = tkctaglook(tk, n, nil);
220 		if(n != f)
221 			free(n);
222 
223 		for(t = i->stag; t; t = t->itemlist)
224 			if(t->name == f)
225 				break;
226 		if(t == nil) {
227 			t = malloc(sizeof(TkCtag));
228 			if(t == nil) {
229 				tkfreename(link);
230 				return TkNomem;
231 			}
232 			t->name = f;
233 			t->taglist = f->obj;		/* add to head of items with this tag */
234 			f->obj = t;
235 			t->item = i;
236 			t->itemlist = i->stag;	/* add to head of tags for this item */
237 			i->stag = t;
238 		}
239 	}
240 	i->tags = nil;
241 
242 	if(new != 0) {
243 		i->tags = tkmkname("all");
244 		if(i->tags == nil)
245 			return TkNomem;		/* XXX - Tad: memory leak? */
246 		return tkcaddtag(tk, i, 0);
247 	}
248 
249 	return nil;
250 }
251 
252 void
tkfreepoint(TkCpoints * p)253 tkfreepoint(TkCpoints *p)
254 {
255 	free(p->drawpt);
256 	free(p->parampt);
257 }
258 
259 /*
260  * of all the items in ilist tagged with tag,
261  * return that tag for the first (topmost) item.
262  */
263 TkCtag*
tkclasttag(TkCitem * ilist,TkCtag * tag)264 tkclasttag(TkCitem *ilist, TkCtag* tag)
265 {
266 	TkCtag *last, *t;
267 
268 	if (tag == nil || tag->taglist == nil)
269 		return tag;
270 	last = nil;
271 	while(ilist) {
272 		for(t = tag; t; t = t->taglist) {
273 			if(t->item == ilist) {
274 				last = t;
275 				break;
276 			}
277 		}
278 		ilist = ilist->next;
279 	}
280 	return last;
281 }
282 
283 /*
284  * of all the items in ilist tagged with tag,
285  * return that tag for the first (bottommost) item.
286  */
287 TkCtag*
tkcfirsttag(TkCitem * ilist,TkCtag * tag)288 tkcfirsttag(TkCitem *ilist, TkCtag* tag)
289 {
290 	TkCtag *t;
291 
292 	if (tag == nil || tag->taglist == nil)
293 		return tag;
294 	for (; ilist != nil; ilist = ilist->next)
295 		for(t = tag; t; t = t->taglist)
296 			if(t->item == ilist)
297 				return t;
298 	return nil;
299 }
300 
301 void
tkmkpen(Image ** pen,TkEnv * e,Image * stipple)302 tkmkpen(Image **pen, TkEnv *e, Image *stipple)
303 {
304 	int locked;
305 	Display *d;
306 	Image *new, *fill;
307 
308 	fill = tkgc(e, TkCfill);
309 
310 	d = e->top->display;
311 	locked = lockdisplay(d);
312 	if(*pen != nil) {
313 		freeimage(*pen);
314 		*pen = nil;
315 	}
316 	if(stipple == nil) {
317 		if(locked)
318 			unlockdisplay(d);
319 		return;
320 	}
321 
322 	if(fill == nil)
323 		fill = d->black;
324 	new = allocimage(d, stipple->r, RGBA32, 1, DTransparent);	/* XXX RGBA32 is excessive sometimes... */
325 	if (new != nil)
326 		draw(new, stipple->r, fill, stipple, ZP);
327 	else
328 		new = fill;
329 	if(locked)
330 		unlockdisplay(d);
331 	*pen = new;
332 }
333 
334 Point
tkcvsanchor(Point dp,int w,int h,int anchor)335 tkcvsanchor(Point dp, int w, int h, int anchor)
336 {
337 	Point o;
338 
339 	if(anchor & Tknorth)
340 		o.y = dp.y;
341 	else if(anchor & Tksouth)
342 		o.y = dp.y - h;
343 	else
344 		o.y = dp.y - h/2;
345 
346 	if(anchor & Tkwest)
347 		o.x = dp.x;
348 	else if(anchor & Tkeast)
349 		o.x = dp.x - w;
350 	else
351 		o.x = dp.x - w/2;
352 
353 	return o;
354 }
355 
356 static TkCitem*
tkcvsmousefocus(TkCanvas * c,Point p)357 tkcvsmousefocus(TkCanvas *c, Point p)
358 {
359 	TkCitem *i, *s;
360 	int (*hit)(TkCitem*, Point);
361 
362 	if (c->grab != nil)
363 		return c->grab;
364 	s = nil;
365 	for(i = c->head; i; i = i->next)
366 		if(ptinrect(p, i->p.bb)) {
367 			if ((hit = tkcimethod[i->type].hit) != nil && !(*hit)(i, p))
368 				continue;
369 			s = i;
370 		}
371 
372 	return s;
373 }
374 
375 Tk*
tkcvsinwindow(Tk * tk,Point * p)376 tkcvsinwindow(Tk *tk, Point *p)
377 {
378 	TkCanvas *c;
379 	TkCitem *i;
380 	Point q;
381 	TkCwind *w;
382 
383 	c = TKobj(TkCanvas, tk);
384 
385 	q = addpt(*p, c->view);
386 	i = tkcvsmousefocus(c, addpt(*p, c->view));
387 	if (i == nil || i->type != TkCVwindow)
388 		return tk;
389 	w = TKobj(TkCwind, i);
390 	if (w->sub == nil)
391 		return tk;
392 	p->x = q.x - (i->p.bb.min.x + w->sub->borderwidth);
393 	p->y = q.y - (i->p.bb.min.y + w->sub->borderwidth);
394 	return w->sub;
395 }
396 
397 static Tk*
tkcvsmouseinsub(TkCwind * w,TkMouse m)398 tkcvsmouseinsub(TkCwind *w, TkMouse m)
399 {
400 	Point g, mp;
401 	int bd;
402 
403 	g = tkposn(w->sub);
404 	bd = w->sub->borderwidth;
405 	mp.x = m.x - (g.x + bd);
406 	mp.y = m.y - (g.y + bd);
407 	return tkinwindow(w->sub, mp, 0);
408 }
409 
410 static Tk*
tkcvsdeliver(Tk * tk,TkCitem * i,int event,void * data)411 tkcvsdeliver(Tk *tk, TkCitem *i, int event, void *data)
412 {
413 	Tk *ftk, *dest;
414 	TkCtag *t;
415 	TkCwind *w;
416 	TkAction *a;
417 
418 	if(i->type == TkCVwindow) {
419 		dest = nil;
420 		w = TKobj(TkCwind, i);
421 		if(w->sub == nil)
422 			return nil;
423 
424 		if(!(event & TkKey) && (event & TkEmouse)) {
425 			ftk = tkcvsmouseinsub(w, *(TkMouse*)data);
426 			if(ftk != w->focus) {
427 				tkdeliver(w->focus, TkLeave, data);
428 if(0)print("focus %p %q %p %q\n", w->sub, tkname(w->sub), ftk, tkname(ftk));
429 				tkdeliver(ftk, TkEnter, data);
430 				w->focus = ftk;
431 			}
432 			if(ftk != nil)
433 				dest = tkdeliver(ftk, event, data);
434 		} else {
435 			if(event & TkLeave) {
436 				tkdeliver(w->focus, TkLeave, data);
437 				w->focus = nil;
438 			} else if(event & TkEnter) {
439 				ftk = tkcvsmouseinsub(w, *(TkMouse*)data);
440 				tkdeliver(ftk, TkEnter, data);
441 				w->focus = ftk;
442 			} else
443 				dest = tkdeliver(w->sub, event, data);
444 		}
445 		return dest;
446 	}
447 
448 	for(t = i->stag; t != nil; t = t->itemlist) {
449 		a = t->name->prop.binds;
450 		if(a != nil)
451 			tksubdeliver(tk, a, event, data, 0);
452 	}
453 	return nil;
454 }
455 
456 Tk*
tkcvsevent(Tk * tk,int event,void * data)457 tkcvsevent(Tk *tk, int event, void *data)
458 {
459 	TkMouse m;
460 	TkCitem *f;
461 	Point mp, g;
462 	TkCanvas *c;
463 	Tk *dest;
464 
465 	c = TKobj(TkCanvas, tk);
466 
467 	if(event == TkLeave && c->mouse != nil) {
468 		tkcvsdeliver(tk, c->mouse, TkLeave, data);
469 		c->mouse = nil;
470 	}
471 
472 	dest = nil;
473 	if(!(event & TkKey) && (event & TkEmouse) || (event & TkEnter)) {
474 		m = *(TkMouse*)data;
475 		g = tkposn(tk);
476 		mp.x = (m.x - g.x - tk->borderwidth) + c->view.x;
477 		mp.y = (m.y - g.y - tk->borderwidth) + c->view.y;
478 		f = tkcvsmousefocus(c, mp);
479 		if(c->mouse != f) {
480 			if(c->mouse != nil) {
481 				tkcvsdeliver(tk, c->mouse, TkLeave, data);
482 				c->current->obj = nil;
483 			}
484 			if(f != nil) {
485 				c->current->obj = &c->curtag;
486 				c->curtag.item = f;
487 				tkcvsdeliver(tk, f, TkEnter, data);
488 			}
489 			c->mouse = f;
490 		}
491 		f = c->mouse;
492 		if(f != nil && (event & TkEnter) == 0)
493 			dest = tkcvsdeliver(tk, f, event, &m);
494 	}
495 
496 	if(event & TkKey) {
497 		f = c->focus;
498 		if(f != nil)
499 			tkcvsdeliver(tk, f, event, data);
500 	}
501 	if(dest == nil)
502 		tksubdeliver(tk, tk->binds, event, data, 0);
503 	return dest;
504 }
505 
506 /*
507  * debugging
508  */
509 void
tkcvsdump(Tk * tk)510 tkcvsdump(Tk *tk)
511 {
512 	TkCanvas *c;
513 	TkCitem *it;
514 	TkCwind *w;
515 	char v1[Tkminitem], v2[Tkminitem];
516 	int i;
517 
518 	if(tk == nil)
519 		return;
520 	c = TKobj(TkCanvas, tk);
521 	tkfprint(v1, c->width);
522 	tkfprint(v2, c->height);
523 	print("%q configure -width %s -height %s", tkname(tk), v1, v2);
524 	print(" # focus %#p mouse %#p grab %#p\n", c->focus, c->mouse, c->grab);
525 	for(it = c->head; it != nil; it = it->next){
526 		print("%q create %q", tkname(tk), tkcimethod[it->type].name);
527 		for(i = 0; i < it->p.npoint; i++){
528 			tkfprint(v1, it->p.parampt[i].x);
529 			tkfprint(v2, it->p.parampt[i].y);
530 			print(" %s %s", v1, v2);
531 		}
532 		if(it->type == TkCVwindow){
533 			w = TKobj(TkCwind, it);
534 			if(w->sub != nil)
535 				print(" -window %q", tkname(w->sub));
536 			print(" # item %#p id %d sub %#p focus [%#p %q]\n", it, it->id, w->sub, w->focus, tkname(w->focus));
537 		}else
538 			print("# item %#p id %d\n", it, it->id);
539 	}
540 }
541