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