xref: /inferno-os/libtk/canvu.c (revision 46439007cf417cbd9ac8049bb4122c890097a0fa)
1 #include <lib9.h>
2 #include <kernel.h>
3 #include "draw.h"
4 #include "tk.h"
5 #include "canvs.h"
6 
7 char*
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*
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
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
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
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
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*
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*
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
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*
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*
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
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
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
342 	if(anchor & Tksouth)
343 		o.y = dp.y - h;
344 	else
345 		o.y = dp.y - h/2;
346 
347 	if(anchor & Tkwest)
348 		o.x = dp.x;
349 	else
350 	if(anchor & Tkeast)
351 		o.x = dp.x - w;
352 	else
353 		o.x = dp.x - w/2;
354 
355 	return o;
356 }
357 
358 static TkCitem*
359 tkcvsmousefocus(TkCanvas *c, Point p)
360 {
361 	TkCitem *i, *s;
362 	int (*hit)(TkCitem*, Point);
363 
364 	if (c->grab != nil)
365 		return c->grab;
366 	s = nil;
367 	for(i = c->head; i; i = i->next)
368 		if(ptinrect(p, i->p.bb)) {
369 			if ((hit = tkcimethod[i->type].hit) != nil && !(*hit)(i, p))
370 				continue;
371 			s = i;
372 		}
373 
374 	return s;
375 }
376 
377 Tk*
378 tkcvsinwindow(Tk *tk, Point *p)
379 {
380 	TkCanvas *c;
381 	TkCitem *i;
382 	Point q;
383 	TkCwind *w;
384 
385 	c = TKobj(TkCanvas, tk);
386 
387 	q = addpt(*p, c->view);
388 	i = tkcvsmousefocus(c, addpt(*p, c->view));
389 	if (i == nil || i->type != TkCVwindow)
390 		return tk;
391 	w = TKobj(TkCwind, i);
392 	if (w->sub == nil)
393 		return tk;
394 	p->x = q.x - (i->p.bb.min.x + w->sub->borderwidth);
395 	p->y = q.y - (i->p.bb.min.y + w->sub->borderwidth);
396 	return w->sub;
397 }
398 
399 static Tk*
400 tkcvsmouseinsub(TkCwind *w, TkMouse m)
401 {
402 	Point g, mp;
403 	int bd;
404 
405 	g = tkposn(w->sub);
406 	bd = w->sub->borderwidth;
407 	mp.x = m.x - (g.x + bd);
408 	mp.y = m.y - (g.y + bd);
409 	return tkinwindow(w->sub, mp, 0);
410 }
411 
412 static Tk*
413 tkcvsdeliver(Tk *tk, TkCitem *i, int event, void *data)
414 {
415 	Tk *ftk, *dest;
416 	TkCtag *t;
417 	TkCwind *w;
418 	TkAction *a;
419 
420 	if(i->type == TkCVwindow) {
421 		dest = nil;
422 		w = TKobj(TkCwind, i);
423 		if(w->sub == nil)
424 			return nil;
425 
426 		if(!(event & TkKey) && (event & TkEmouse)) {
427 			ftk = tkcvsmouseinsub(w, *(TkMouse*)data);
428 			if(ftk != w->focus) {
429 				tkdeliver(w->focus, TkLeave, data);
430 				tkdeliver(ftk, TkEnter, data);
431 				w->focus = ftk;
432 			}
433 			if(ftk != nil)
434 				dest = tkdeliver(ftk, event, data);
435 		}
436 		else {
437 			if (event & TkLeave) {
438 				tkdeliver(w->focus, TkLeave, data);
439 				w->focus = nil;
440 			} else if (event & TkEnter) {
441 				ftk = tkcvsmouseinsub(w, *(TkMouse*)data);
442 				tkdeliver(ftk, TkEnter, data);
443 				w->focus = ftk;
444 			} else
445 				dest = tkdeliver(w->sub, event, data);
446 		}
447 		return dest;
448 	}
449 
450 	for(t = i->stag; t != nil; t = t->itemlist) {
451 		a = t->name->prop.binds;
452 		if(a != nil)
453 			tksubdeliver(tk, a, event, data, 0);
454 	}
455 	return nil;
456 }
457 
458 Tk*
459 tkcvsevent(Tk *tk, int event, void *data)
460 {
461 	TkMouse m;
462 	TkCitem *f;
463 	Point mp, g;
464 	TkCanvas *c;
465 	Tk *dest;
466 
467 	c = TKobj(TkCanvas, tk);
468 
469 	if(event == TkLeave && c->mouse != nil) {
470 		tkcvsdeliver(tk, c->mouse, TkLeave, data);
471 		c->mouse = nil;
472 	}
473 
474 	dest = nil;
475 	if(!(event & TkKey) && (event & TkEmouse) || (event & TkEnter)) {
476 		m = *(TkMouse*)data;
477 		g = tkposn(tk);
478 		mp.x = (m.x - g.x - tk->borderwidth) + c->view.x;
479 		mp.y = (m.y - g.y - tk->borderwidth) + c->view.y;
480 		f = tkcvsmousefocus(c, mp);
481 		if(c->mouse != f) {
482 			if(c->mouse != nil) {
483 				tkcvsdeliver(tk, c->mouse, TkLeave, data);
484 				c->current->obj = nil;
485 			}
486 			if(f != nil) {
487 				c->current->obj = &c->curtag;
488 				c->curtag.item = f;
489 				tkcvsdeliver(tk, f, TkEnter, data);
490 			}
491 			c->mouse = f;
492 		}
493 		f = c->mouse;
494 		if(f != nil && (event & TkEnter) == 0)
495 			dest = tkcvsdeliver(tk, f, event, &m);
496 	}
497 
498 	if(event & TkKey) {
499 		f = c->focus;
500 		if(f != nil)
501 			tkcvsdeliver(tk, f, event, data);
502 	}
503 	if(dest == nil)
504 		tksubdeliver(tk, tk->binds, event, data, 0);
505 	return dest;
506 }
507