xref: /inferno-os/libtk/packr.c (revision 5849851a19380dbb62a47d9c4d868a81e42fa79b)
1 #include "lib9.h"
2 #include "draw.h"
3 #include "tk.h"
4 
5 #define	O(t, e)		((long)(&((t*)0)->e))
6 
7 typedef struct Pack Pack;
8 struct Pack
9 {
10 	Tk*	t;
11 	Pack*	next;
12 };
13 static Pack *packorder;
14 
15 static int tkpacker(Tk *);
16 
17 typedef struct TkParam TkParam;
18 struct TkParam
19 {
20 	Point	pad;
21 	Point	ipad;
22 	int	side;
23 	int	anchor;
24 	int	fill;
25 	Tk*	in;
26 	Tk*	before;
27 	Tk*	after;
28 	int	expand;
29 };
30 
31 TkParam defparam = {
32 	{-1, -1},	/* p.pad */
33 	{-1, -1},	/* p.ipad */
34 	-1,		/* side */
35 	-1,		/* anchor */
36 	-1,		/* fill */
37 	nil,		/* in */
38 	nil, 		/* before */
39 	nil,		/* after */
40 	BoolX	/* expand */
41 };
42 
43 static
44 TkStab tkside[] =
45 {
46 	"top",		Tktop,
47 	"bottom",	Tkbottom,
48 	"left",		Tkleft,
49 	"right",	Tkright,
50 	nil
51 };
52 
53 static
54 TkStab tkfill[] =
55 {
56 	"none",		0,
57 	"x",		Tkfillx,
58 	"y",		Tkfilly,
59 	"both",		Tkfillx|Tkfilly,
60 	nil
61 };
62 
63 static
64 TkOption opts[] =
65 {
66 	"padx",		OPTnndist,	O(TkParam, pad.x),	nil,
67 	"pady",		OPTnndist,	O(TkParam, pad.y),	nil,
68 	"ipadx",	OPTnndist,	O(TkParam, ipad.x),	nil,
69 	"ipady",	OPTnndist,	O(TkParam, ipad.y),	nil,
70 	"side",		OPTstab,	O(TkParam, side),	tkside,
71 	"anchor",	OPTstab,	O(TkParam, anchor),	tkanchor,
72 	"fill",		OPTstab,	O(TkParam, fill),	tkfill,
73 	"in",		OPTwinp,	O(TkParam, in),		nil,
74 	"before",	OPTwinp,	O(TkParam, before),	nil,
75 	"after",	OPTwinp,	O(TkParam, after),	nil,
76 	"expand",	OPTstab,	O(TkParam, expand),	tkbool,
77 	nil
78 };
79 
80 void
tkdelpack(Tk * t)81 tkdelpack(Tk *t)
82 {
83 	Tk *f, **l, *sub, *p;
84 
85 	sub = tkfindsub(t);
86 	if(sub != nil) {
87 		p = sub->parent;
88 		if(tkmethod[p->type]->forgetsub != nil)
89 			tkmethod[p->type]->forgetsub(sub, t);
90 	}
91 
92 	if(t->master == nil)
93 		return;
94 
95 	if(t->master->grid != nil)
96 		tkgriddelslave(t);
97 
98 	l = &t->master->slave;
99 	for(f = *l; f; f = f->next) {
100 		if(f == t) {
101 			*l = t->next;
102 			break;
103 		}
104 		l = &f->next;
105 	}
106 	t->master = nil;
107 }
108 
109 void
tkappendpack(Tk * parent,Tk * tk,int where)110 tkappendpack(Tk *parent, Tk *tk, int where)
111 {
112 	Tk *f, **l;
113 
114 	tk->master = parent;
115 	l = &parent->slave;
116 	for(f = *l; f; f = f->next) {
117 		if(where-- == 0)
118 			break;
119 		l = &f->next;
120 	}
121 	*l = tk;
122 	tk->next = f;
123 
124 	for( ; parent != nil; parent = parent->master)
125 		if(parent->parent != nil){
126 			tk->flag |= Tksubsub;
127 			break;
128 		}
129 }
130 
131 static void
tkpackqrm(Tk * t)132 tkpackqrm(Tk *t)
133 {
134 	Pack *f, **l;
135 
136 	l = &packorder;
137 	for(f = *l; f; f = f->next) {
138 		if(f->t == t) {
139 			*l = f->next;
140 			free(f);
141 			break;
142 		}
143 		l = &f->next;
144 	}
145 }
146 
147 /* XXX - Tad: leaky... should propagate  */
148 void
tkpackqit(Tk * t)149 tkpackqit(Tk *t)
150 {
151 	Pack *f;
152 
153 	if(t == nil || (t->flag & Tkdestroy))
154 		return;
155 
156 	tkpackqrm(t);
157 	f = malloc(sizeof(Pack));
158 	if(f == nil) {
159 		print("tkpackqit: malloc failed\n");
160 		return;
161 	}
162 
163 	f->t = t;
164 	f->next = packorder;
165 	packorder = f;
166 }
167 
168 void
tkrunpack(TkTop * t)169 tkrunpack(TkTop *t)
170 {
171 	Tk *tk;
172 	int done;
173 
174 	while(packorder != nil) {
175 		tk = packorder->t;
176 		if (tk->grid != nil)
177 			done = tkgridder(tk);
178 		else
179 			done = tkpacker(tk);
180 		if (done)
181 			tkpackqrm(tk);
182 	}
183 	tkenterleave(t);
184 	tkdirtyfocusorder(t);
185 }
186 
187 static void
tksetopt(TkParam * p,Tk * tk)188 tksetopt(TkParam *p, Tk *tk)
189 {
190 	if(p->pad.x != -1)
191 		tk->pad.x = p->pad.x*2;
192 	if(p->pad.y != -1)
193 		tk->pad.y = p->pad.y*2;
194 	if(p->ipad.x != -1)
195 		tk->ipad.x = p->ipad.x*2;
196 	if(p->ipad.y != -1)
197 		tk->ipad.y = p->ipad.y*2;
198 	if(p->side != -1) {
199 		tk->flag &= ~Tkside;
200 		tk->flag |= p->side;
201 	}
202 	if(p->anchor != -1) {
203 		tk->flag &= ~Tkanchor;
204 		tk->flag |= p->anchor;
205 	}
206 	if(p->fill != -1) {
207 		tk->flag &= ~Tkfill;
208 		tk->flag |= p->fill;
209 	}
210 	if(p->expand != BoolX) {
211 		if(p->expand == BoolT) {
212 			tk->flag |= Tkexpand;
213 		}
214 		else
215 			tk->flag &= ~Tkexpand;
216 	}
217 }
218 
219 static char*
tkforget(TkTop * t,char * arg)220 tkforget(TkTop *t, char *arg)
221 {
222 	Tk *tk;
223 	char *buf;
224 
225 	buf = mallocz(Tkmaxitem, 0);
226 	if(buf == nil)
227 		return TkNomem;
228 	for(;;) {
229 		arg = tkword(t, arg, buf, buf+Tkmaxitem, nil);
230 		if(buf[0] == '\0')
231 			break;
232 		tk = tklook(t, buf, 0);
233 		if(tk == nil) {
234 			tkrunpack(t);
235 			tkerr(t, buf);
236 			free(buf);
237 			return TkBadwp;
238 		}
239 		tkpackqit(tk->master);
240 		tkdelpack(tk);
241 	}
242 	free(buf);
243 	tkrunpack(t);
244 	return nil;
245 }
246 
247 char*
tkpropagate(TkTop * t,char * arg)248 tkpropagate(TkTop *t, char *arg)
249 {
250 	Tk *tk;
251 	TkStab *s;
252 	char *buf;
253 
254 	buf = mallocz(Tkmaxitem, 0);
255 	if(buf == nil)
256 		return TkNomem;
257 	arg = tkword(t, arg, buf, buf+Tkmaxitem, nil);
258 	tk = tklook(t, buf, 0);
259 	if(tk == nil) {
260 		tkerr(t, buf);
261 		free(buf);
262 		return TkBadwp;
263 	}
264 
265 	tkword(t, arg, buf, buf+Tkmaxitem, nil);
266 	for(s = tkbool; s->val; s++) {
267 		if(strcmp(s->val, buf) == 0) {
268 			if(s->con == BoolT) {
269 				tk->flag &= ~Tknoprop;
270 				tkpackqit(tk);
271 				tkrunpack(t);
272 			} else
273 				tk->flag |= Tknoprop;
274 			free(buf);
275 			return nil;
276 		}
277 	}
278 	free(buf);
279 	return TkBadvl;
280 }
281 
282 static char*
tkslaves(TkTop * t,char * arg,char ** val)283 tkslaves(TkTop *t, char *arg, char **val)
284 {
285 	Tk *tk;
286 	char *fmt, *e, *buf;
287 
288 	buf = mallocz(Tkmaxitem, 0);
289 	if(buf == nil)
290 		return TkNomem;
291 	tkword(t, arg, buf, buf+Tkmaxitem, nil);
292 	tk = tklook(t, buf, 0);
293 	if(tk == nil){
294 		tkerr(t, buf);
295 		free(buf);
296 		return TkBadwp;
297 	}
298 	free(buf);
299 
300 	fmt = "%s";
301 	for(tk = tk->slave; tk; tk = tk->next) {
302 		if (tk->name != nil) {
303 			e = tkvalue(val, fmt, tk->name->name);
304 			if(e != nil)
305 				return e;
306 			fmt = " %s";
307 		}
308 	}
309 
310 	return nil;
311 }
312 
313 int
tkisslave(Tk * in,Tk * tk)314 tkisslave(Tk *in, Tk *tk)
315 {
316 	if(in == nil)
317 		return 0;
318 	if(in == tk)
319 		return 1;
320 	for(tk = tk->slave; tk; tk = tk->next)
321 		if(tkisslave(in, tk))
322 			return 1;
323 	return 0;
324 }
325 
326 static char*
tkcanpack(Tk * tk,Tk * parent)327 tkcanpack(Tk *tk, Tk *parent)
328 {
329 	if(tkisslave(parent, tk))
330 		return TkRecur;
331 	if (parent->grid != nil) {
332 		if (parent->slave != nil)
333 			return TkIsgrid;
334 		tkfreegrid(parent->grid);
335 		parent->grid = nil;
336 	}
337 	return nil;
338 }
339 
340 char*
tkpack(TkTop * t,char * arg,char ** val)341 tkpack(TkTop *t, char *arg, char **val)
342 {
343 	TkParam param = defparam;
344 	TkParam *p = &param;
345 	TkOptab tko[2];
346 	Tk *tk, **l, *tkp;
347 	TkName *names, *n;
348 	char *e, *w, *buf;
349 
350 	buf = mallocz(Tkminitem, 0);
351 	if(buf == nil)
352 		return TkNomem;
353 
354 	w = tkword(t, arg, buf, buf+Tkminitem, nil);
355 	if(strcmp(buf, "forget") == 0) {
356 		e = tkforget(t, w);
357 		free(buf);
358 		return e;
359 	}
360 	if(strcmp(buf, "propagate") == 0) {
361 		e = tkpropagate(t, w);
362 		free(buf);
363 		return e;
364 	}
365 	if(strcmp(buf, "slaves") == 0) {
366 		e = tkslaves(t, w, val);
367 		free(buf);
368 		return e;
369 	}
370 	free(buf);
371 
372 	tko[0].ptr = p;
373 	tko[0].optab = opts;
374 	tko[1].ptr = nil;
375 
376 	names = nil;
377 	e = tkparse(t, arg, tko, &names);
378 	if(e != nil)
379 		return e;
380 
381 	if((p->before && p->before->master == nil) ||
382 	   (p->after && p->after->master == nil)) {
383 		tkfreename(names);
384 		return TkNotpk;
385 	}
386 
387 	for(n = names; n; n = n->link) {
388 		tkp = tklook(t, n->name, 0);
389 		if(tkp == nil) {
390 			tkerr(t, n->name);
391 			tkfreename(names);
392 			return TkBadwp;
393 		}
394 		if(tkp->flag & Tkwindow) {
395 			tkfreename(names);
396 			return TkIstop;
397 		}
398 		if(tkp->parent != nil) {
399 			tkfreename(names);
400 			return TkWpack;
401 		}
402 		n->obj = tkp;
403 	}
404 
405 	e = nil;
406 	for(n = names; n; n = n->link) {
407 		tk = n->obj;
408 		if(tk->master == nil) {
409 			tk->pad = ZP;
410 			tk->ipad = ZP;
411 			tk->flag &= ~(Tkanchor|Tkside|Tkfill|Tkexpand);
412 			tk->flag |= Tktop;
413 		}
414 		if(tk->master != nil) {
415 			tkpackqit(tk->master);
416 			tkdelpack(tk);
417 		}
418 		if(p->before == nil && p->after == nil && p->in == nil) {
419 			tkp = tklook(t, n->name, 1);
420 			if(tkp == nil) {
421 				e = TkBadwp;
422 				tkerr(t, n->name);
423 				goto Error;
424 			}
425 			e = tkcanpack(tk, tkp);
426 			if (e != nil)
427 				goto Error;
428 			tkappendpack(tkp, tk, -1);
429 		}
430 		else {
431 			if(p->in != nil) {
432 				e = tkcanpack(tk, p->in);
433 				if(e != nil)
434 					goto Error;
435 				tkappendpack(p->in, tk, -1);
436 			}
437 			else
438 			if(p->before != nil) {
439 				e = tkcanpack(tk, p->before->master);
440 				if (e != nil)
441 					goto Error;
442 				tk->master = p->before->master;
443 				l = &tk->master->slave;
444 				for(;;) {
445 					if(*l == p->before) {
446 						tk->next = *l;
447 						*l = tk;
448 						break;
449 					}
450 					l = &(*l)->next;
451 				}
452 				p->before = tk;
453 			}
454 			else {
455 				e = tkcanpack(tk, p->after->master);
456 				if (e != nil)
457 					goto Error;
458 				tk->master = p->after->master;
459 				tk->next = p->after->next;
460 				p->after->next = tk;
461 				p->after = tk;
462 			}
463 		}
464 		tksetopt(p, tk);
465 		if (tk->master->flag&Tksubsub)
466 			tksetbits(tk, Tksubsub);
467 		tkpackqit(tk->master);
468 	}
469 
470 Error:
471 	tkfreename(names);
472 	tkrunpack(t);
473 
474 	return e;
475 }
476 
477 void
tksetslavereq(Tk * slave,TkGeom frame)478 tksetslavereq(Tk *slave, TkGeom frame)
479 {
480 	Point border;
481 	TkGeom pos, old;
482 	int slave2BW;
483 	void (*geomfn)(Tk*);
484 
485 	border.x = slave->pad.x;
486 	border.y = slave->pad.y;
487 
488 	slave2BW = slave->borderwidth * 2;
489 
490 	pos.width = slave->req.width + slave2BW + slave->ipad.x;
491 	if((slave->flag&Tkfillx) || (pos.width > (frame.width - border.x)))
492 		pos.width = frame.width - border.x;
493 
494 	pos.height = slave->req.height + slave2BW + slave->ipad.y;
495 	if((slave->flag&Tkfilly) || (pos.height > (frame.height - border.y)))
496     		pos.height = frame.height - border.y;
497 
498 	border.x /= 2;
499 	border.y /= 2;
500 
501 	if(slave->flag & Tknorth)
502 		pos.y = frame.y + border.y;
503 	else
504 	if(slave->flag & Tksouth)
505 		pos.y = frame.y + frame.height - pos.height - border.y;
506 	else
507 		pos.y = frame.y + (frame.height - pos.height)/2;
508 
509 	if(slave->flag & Tkwest)
510 		pos.x = frame.x + border.x;
511 	else
512 	if(slave->flag & Tkeast)
513 		pos.x = frame.x + frame.width - pos.width - border.x;
514 	else
515 		pos.x = frame.x + (frame.width - pos.width)/2;
516 
517 	pos.width -= slave2BW;
518 	pos.height -= slave2BW;
519 
520 	if(memcmp(&slave->act, &pos, sizeof(TkGeom)) != 0) {
521 		old = slave->act;
522 		slave->act = pos;
523 		geomfn = tkmethod[slave->type]->geom;
524 		if(geomfn != nil)
525 			geomfn(slave);
526 		if(slave->slave)
527 			tkpackqit(slave);
528 		tkdeliver(slave, TkConfigure, &old);
529 
530 		slave->dirty = tkrect(slave, 1);
531 		slave->flag |= Tkrefresh;
532 	}
533 }
534 static int
tkexpandx(Tk * slave,int cavityWidth)535 tkexpandx(Tk* slave, int cavityWidth)
536 {
537 	int numExpand, minExpand, curExpand, childWidth;
538 
539 	minExpand = cavityWidth;
540 	numExpand = 0;
541 	for( ;slave != nil; slave = slave->next) {
542 		childWidth = slave->req.width + slave->borderwidth*2 +
543 				slave->pad.x + slave->ipad.x;
544 		if(slave->flag & (Tktop|Tkbottom)) {
545 			curExpand = (cavityWidth - childWidth)/numExpand;
546 			if (curExpand < minExpand)
547 				minExpand = curExpand;
548 		}
549 		else {
550 	    		cavityWidth -= childWidth;
551 	    		if(slave->flag & Tkexpand)
552 				numExpand++;
553 		}
554 	}
555 	curExpand = cavityWidth/numExpand;
556 	if(curExpand < minExpand)
557 		minExpand = curExpand;
558 
559 	return (minExpand < 0) ? 0 : minExpand;
560 }
561 
562 static int
tkexpandy(Tk * slave,int cavityHeight)563 tkexpandy(Tk *slave, int cavityHeight)
564 {
565 	int numExpand, minExpand, curExpand, childHeight;
566 
567 	minExpand = cavityHeight;
568 	numExpand = 0;
569 	for ( ;slave != nil; slave = slave->next) {
570 		childHeight = slave->req.height + slave->borderwidth*2 +
571 			+ slave->pad.y + slave->ipad.y;
572 		if(slave->flag & (Tkleft|Tkright)) {
573 			curExpand = (cavityHeight - childHeight)/numExpand;
574 			if(curExpand < minExpand)
575 				minExpand = curExpand;
576 		}
577 		else {
578 			cavityHeight -= childHeight;
579 			if(slave->flag & Tkexpand)
580 				numExpand++;
581 		}
582 	}
583 	curExpand = cavityHeight/numExpand;
584 	if(curExpand < minExpand)
585 		minExpand = curExpand;
586 
587 	return (minExpand < 0) ? 0 : minExpand;
588 }
589 
590 static int
tkpacker(Tk * master)591 tkpacker(Tk *master)
592 {
593 	Tk *slave;
594 	TkGeom frame, cavity, pos;
595 	int maxwidth, maxheight, tmp, slave2BW;
596 
597 	pos.width = 0;
598 	pos.height = 0;
599 	maxwidth = 0;
600 	maxheight = 0;
601 
602 	master->flag |= Tkrefresh;
603 
604 	for (slave = master->slave; slave != nil; slave = slave->next) {
605 		slave2BW = slave->borderwidth*2;
606 		if(slave->flag & (Tktop|Tkbottom)) {
607 	    		tmp = slave->req.width + slave2BW +
608 		    		slave->pad.x + slave->ipad.x + pos.width;
609 			if(tmp > maxwidth)
610 				maxwidth = tmp;
611 	    		pos.height += slave->req.height + slave2BW +
612 		    		slave->pad.y + slave->ipad.y;
613 		}
614 		else {
615 	    		tmp = slave->req.height + slave2BW +
616 		    		slave->pad.y + slave->ipad.y + pos.height;
617 	    		if(tmp > maxheight)
618 				maxheight = tmp;
619 	    		pos.width += slave->req.width + slave2BW +
620 		    		+ slave->pad.x + slave->ipad.x;
621 		}
622 	}
623 	if(pos.width > maxwidth)
624 		maxwidth = pos.width;
625 	if(pos.height > maxheight)
626 		maxheight = pos.height;
627 
628 	if(maxwidth != master->req.width || maxheight != master->req.height)
629 	if((master->flag & Tknoprop) == 0) {
630 		if(master->geom != nil) {
631 			master->geom(master, master->act.x, master->act.y,
632 					maxwidth, maxheight);
633 		} else {
634 			master->req.width = maxwidth;
635 			master->req.height = maxheight;
636 			tkpackqit(master->master);
637 		}
638 		return 0;
639     	}
640 
641     	cavity.x = 0;
642 	cavity.y = 0;
643 	pos.x = 0;
644 	pos.y = 0;
645 	cavity.width = master->act.width;
646 	cavity.height = master->act.height;
647 
648 	for(slave = master->slave; slave != nil; slave = slave->next) {
649 		slave2BW = slave->borderwidth*2;
650 		if(slave->flag & (Tktop|Tkbottom)) {
651 	    		frame.width = cavity.width;
652 	    		frame.height = slave->req.height + slave2BW +
653 		    			slave->pad.y + slave->ipad.y;
654 	    		if(slave->flag & Tkexpand)
655 				frame.height += tkexpandy(slave, cavity.height);
656 	    		cavity.height -= frame.height;
657 	    		if(cavity.height < 0) {
658 				frame.height += cavity.height;
659 				cavity.height = 0;
660 	    		}
661 	    		frame.x = cavity.x;
662 	    		if(slave->flag & Tktop) {
663 				frame.y = cavity.y;
664 				cavity.y += frame.height;
665 	    		}
666 			else
667 				frame.y = cavity.y + cavity.height;
668 		}
669 		else {
670 	    		frame.height = cavity.height;
671 	    		frame.width = slave->req.width + slave2BW +
672 					slave->pad.x + slave->ipad.x;
673 	    		if(slave->flag & Tkexpand)
674 				frame.width += tkexpandx(slave, cavity.width);
675 	    		cavity.width -= frame.width;
676 	    		if(cavity.width < 0) {
677 				frame.width += cavity.width;
678 				cavity.width = 0;
679 	    		}
680 	    		frame.y = cavity.y;
681 	    		if(slave->flag & Tkleft) {
682 				frame.x = cavity.x;
683 				cavity.x += frame.width;
684 	    		}
685 			else
686 				frame.x = cavity.x + cavity.width;
687 		}
688 
689 		tksetslavereq(slave, frame);
690 	}
691 
692 	master->dirty = tkrect(master, 1);
693 	tkdirty(master);
694 	return 1;
695 }
696 
697