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 = ¶m;
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