1 #include <u.h>
2 #include <libc.h>
3 #include <thread.h>
4 #include <draw.h>
5 #include <mouse.h>
6 #include <keyboard.h>
7 #include <control.h>
8 #include "group.h"
9
10 static int debug = 0;
11 static int debugm = 0;
12 static int debugr = 0;
13
14 enum{
15 EAdd,
16 EBorder,
17 EBordercolor,
18 EFocus,
19 EHide,
20 EImage,
21 ERect,
22 ERemove,
23 EReveal,
24 ESeparation,
25 EShow,
26 ESize,
27 };
28
29 static char *cmds[] = {
30 [EAdd] = "add",
31 [EBorder] = "border",
32 [EBordercolor] = "bordercolor",
33 [EFocus] = "focus",
34 [EHide] = "hide",
35 [EImage] = "image",
36 [ERect] = "rect",
37 [ERemove] = "remove",
38 [EReveal] = "reveal",
39 [ESeparation] = "separation",
40 [EShow] = "show",
41 [ESize] = "size",
42 };
43
44 static void boxboxresize(Group*, Rectangle);
45 static void columnresize(Group*, Rectangle);
46 static void groupctl(Control *c, CParse *cp);
47 static void groupfree(Control*);
48 static void groupmouse(Control *, Mouse *);
49 static void groupsize(Control *c);
50 static void removegroup(Group*, int);
51 static void rowresize(Group*, Rectangle);
52 static void stackresize(Group*, Rectangle);
53
54 static void
groupinit(Group * g)55 groupinit(Group *g)
56 {
57 g->bordercolor = _getctlimage("black");
58 g->image = _getctlimage("white");
59 g->border = 0;
60 g->mansize = 0;
61 g->separation = 0;
62 g->selected = -1;
63 g->lastkid = -1;
64 g->kids = nil;
65 g->separators = nil;
66 g->nkids = 0;
67 g->nseparators = 0;
68 g->ctl = groupctl;
69 g->mouse = groupmouse;
70 g->exit = groupfree;
71 }
72
73 static void
groupctl(Control * c,CParse * cp)74 groupctl(Control *c, CParse *cp)
75 {
76 int cmd, i, n;
77
78 Rectangle r;
79 Group *g;
80
81 g = (Group*)c;
82 cmd = _ctllookup(cp->args[0], cmds, nelem(cmds));
83 switch(cmd){
84 case EAdd:
85 for (i = 1; i < cp->nargs; i++){
86 c = controlcalled(cp->args[i]);
87 if (c == nil)
88 ctlerror("%q: no such control: %s", g->name, cp->args[i]);
89 _ctladdgroup(g, c);
90 }
91 if (g->setsize)
92 g->setsize((Control*)g);
93 break;
94 case EBorder:
95 _ctlargcount(g, cp, 2);
96 if(cp->iargs[1] < 0)
97 ctlerror("%q: bad border: %c", g->name, cp->str);
98 g->border = cp->iargs[1];
99 break;
100 case EBordercolor:
101 _ctlargcount(g, cp, 2);
102 _setctlimage(g, &g->bordercolor, cp->args[1]);
103 break;
104 case EFocus:
105 /* ignore focus change */
106 break;
107 case EHide:
108 _ctlargcount(g, cp, 1);
109 for (i = 0; i < g->nkids; i++)
110 if (g->kids[i]->ctl)
111 _ctlprint(g->kids[i], "hide");
112 g->hidden = 1;
113 break;
114 case EImage:
115 _ctlargcount(g, cp, 2);
116 _setctlimage(g, &g->image, cp->args[1]);
117 break;
118 case ERect:
119 _ctlargcount(g, cp, 5);
120 r.min.x = cp->iargs[1];
121 r.min.y = cp->iargs[2];
122 r.max.x = cp->iargs[3];
123 r.max.y = cp->iargs[4];
124 if(Dx(r)<=0 || Dy(r)<=0)
125 ctlerror("%q: bad rectangle: %s", g->name, cp->str);
126 g->rect = r;
127 r = insetrect(r, g->border);
128 if (g->nkids == 0)
129 return;
130 switch(g->type){
131 case Ctlboxbox:
132 boxboxresize(g, r);
133 break;
134 case Ctlcolumn:
135 columnresize(g, r);
136 break;
137 case Ctlrow:
138 rowresize(g, r);
139 break;
140 case Ctlstack:
141 stackresize(g, r);
142 break;
143 }
144 break;
145 case ERemove:
146 _ctlargcount(g, cp, 2);
147 for (n = 0; n < g->nkids; n++)
148 if (strcmp(cp->args[1], g->kids[n]->name) == 0)
149 break;
150 if (n == g->nkids)
151 ctlerror("%s: remove nonexistent control: %q", g->name, cp->args[1]);
152 removegroup(g, n);
153 if (g->setsize)
154 g->setsize((Control*)g);
155 break;
156 case EReveal:
157 g->hidden = 0;
158 if (debugr) fprint(2, "reveal %s\n", g->name);
159 if (g->type == Ctlstack){
160 if (cp->nargs == 2){
161 if (cp->iargs[1] < 0 || cp->iargs[1] >= g->nkids)
162 ctlerror("%s: control out of range: %q", g->name, cp->str);
163 g->selected = cp->iargs[1];
164 }else
165 _ctlargcount(g, cp, 1);
166 for (i = 0; i < g->nkids; i++)
167 if (g->kids[i]->ctl){
168 if (g->selected == i){
169 if (debugr) fprint(2, "reveal %s: reveal kid %s\n", g->name, g->kids[i]->name);
170 _ctlprint(g->kids[i], "reveal");
171 }else{
172 if (debugr) fprint(2, "reveal %s: hide kid %s\n", g->name, g->kids[i]->name);
173 _ctlprint(g->kids[i], "hide");
174 }
175 }
176 break;
177 }
178 _ctlargcount(g, cp, 1);
179 if (debug) fprint(2, "reveal %s: border %R/%d\n", g->name, g->rect, g->border);
180 border(g->screen, g->rect, g->border, g->bordercolor->image, g->bordercolor->image->r.min);
181 r = insetrect(g->rect, g->border);
182 if (debug) fprint(2, "reveal %s: draw %R\n", g->name, r);
183 draw(g->screen, r, g->image->image, nil, g->image->image->r.min);
184 for (i = 0; i < g->nkids; i++)
185 if (g->kids[i]->ctl)
186 _ctlprint(g->kids[i], "reveal");
187 break;
188 case EShow:
189 _ctlargcount(g, cp, 1);
190 if (g->hidden)
191 break;
192 // pass it on to the kiddies
193 if (debug) fprint(2, "show %s: border %R/%d\n", g->name, g->rect, g->border);
194 border(g->screen, g->rect, g->border, g->bordercolor->image, g->bordercolor->image->r.min);
195 r = insetrect(g->rect, g->border);
196 if (debug) fprint(2, "show %s: draw %R\n", g->name, r);
197 draw(g->screen, r, g->image->image, nil, g->image->image->r.min);
198 for (i = 0; i < g->nkids; i++)
199 if (g->kids[i]->ctl){
200 if (debug) fprint(2, "show %s: kid %s: %q\n", g->name, g->kids[i]->name, cp->str);
201 _ctlprint(g->kids[i], "show");
202 }
203 flushimage(display, 1);
204 break;
205 case ESize:
206 r.max = Pt(_Ctlmaxsize, _Ctlmaxsize);
207 if (g->type == Ctlboxbox)
208 _ctlargcount(g, cp, 5);
209 switch(cp->nargs){
210 default:
211 ctlerror("%s: args of %q", g->name, cp->str);
212 case 1:
213 /* recursively set size */
214 g->mansize = 0;
215 if (g->setsize)
216 g->setsize((Control*)g);
217 break;
218 case 5:
219 _ctlargcount(g, cp, 5);
220 r.max.x = cp->iargs[3];
221 r.max.y = cp->iargs[4];
222 /* fall through */
223 case 3:
224 r.min.x = cp->iargs[1];
225 r.min.y = cp->iargs[2];
226 if(r.min.x<=0 || r.min.y<=0 || r.max.x<=0 || r.max.y<=0 || r.max.x < r.min.x || r.max.y < r.min.y)
227 ctlerror("%q: bad sizes: %s", g->name, cp->str);
228 g->size = r;
229 g->mansize = 1;
230 break;
231 }
232 break;
233 case ESeparation:
234 if (g->type != Ctlstack){
235 _ctlargcount(g, cp, 2);
236 if(cp->iargs[1] < 0)
237 ctlerror("%q: illegal value: %c", g->name, cp->str);
238 g->separation = cp->iargs[1];
239 break;
240 }
241 // fall through for Ctlstack
242 default:
243 ctlerror("%q: unrecognized message '%s'", g->name, cp->str);
244 break;
245 }
246 }
247
248 static void
groupfree(Control * c)249 groupfree(Control *c)
250 {
251 Group *g;
252
253 g = (Group*)c;
254 _putctlimage(g->bordercolor);
255 free(g->kids);
256 }
257
258 static void
groupmouse(Control * c,Mouse * m)259 groupmouse(Control *c, Mouse *m)
260 {
261 Group *g;
262 int i, lastkid;
263
264 g = (Group*)c;
265 if (g->type == Ctlstack){
266 i = g->selected;
267 if (i >= 0 && g->kids[i]->mouse &&
268 ( ( ((m->buttons == 0) || (g->lastbut == 0)) &&
269 ptinrect(m->xy, g->kids[i]->rect) ) ||
270 ( ((m->buttons != 0) || (g->lastbut != 0)) &&
271 (g->lastkid == i) ) ) ) {
272 if (debugm) fprint(2, "groupmouse %s mouse kid %s i=%d lastkid=%d buttons=%d lastbut=%d inrect=%d\n",
273 g->name, g->kids[i]->name, i, g->lastkid, m->buttons, g->lastbut,
274 ptinrect(m->xy, g->kids[i]->rect) ? 1 : 0);
275 (g->kids[i]->mouse)(g->kids[i], m);
276 g->lastkid = i;
277 g->lastbut = m->buttons;
278 } else {
279 if (debugm) fprint(2, "groupmouse %s skip kid %s i=%d lastkid=%d buttons=%d lastbut=%d inrect=%d\n",
280 g->name, g->kids[i]->name, i, g->lastkid, m->buttons, g->lastbut,
281 ptinrect(m->xy, g->kids[i]->rect) ? 1 : 0);
282 }
283 return;
284 }
285
286 lastkid = -1;
287 for(i=0; i<g->nkids; i++) {
288 if(g->kids[i]->mouse &&
289 ( ( ((m->buttons == 0) || (g->lastbut == 0)) &&
290 ptinrect(m->xy, g->kids[i]->rect) ) ||
291 ( ((m->buttons != 0) || (g->lastbut != 0)) &&
292 (g->lastkid == i) ) ) ) {
293 if (debugm) fprint(2, "groupmouse %s mouse kid %s i=%d lastkid=%d buttons=%d lastbut=%d inrect=%d\n",
294 g->name, g->kids[i]->name, i, g->lastkid, m->buttons, g->lastbut,
295 ptinrect(m->xy, g->kids[i]->rect) ? 1 : 0);
296 (g->kids[i]->mouse)(g->kids[i], m);
297 lastkid = i;
298 } else {
299 if (debugm) fprint(2, "groupmouse %s skip kid %s i=%d lastkid=%d buttons=%d lastbut=%d inrect=%d\n",
300 g->name, g->kids[i]->name, i, g->lastkid, m->buttons, g->lastbut,
301 ptinrect(m->xy, g->kids[i]->rect) ? 1 : 0);
302 }
303 }
304 g->lastkid = lastkid;
305 g->lastbut = m->buttons;
306
307 #ifdef notdef
308 if(m->buttons == 0){
309 /* buttons now up */
310 g->lastbut = 0;
311 return;
312 }
313 if(g->lastbut == 0 && m->buttons != 0){
314 /* button went down, start tracking border */
315 switch(g->stacking){
316 default:
317 return;
318 case Vertical:
319 p = Pt(m->xy.x, middle_of_border.y);
320 p0 = Pt(g->r.min.x, m->xy.y);
321 p1 = Pt(g->r.max.x, m->xy.y);
322 break;
323 case Horizontal:
324 p = Pt(middle_of_border.x, m->xy.y);
325 p0 = Pt(m->xy.x, g->r.min.y);
326 p1 = Pt(m->xy.x, g->r.max.y);
327 break;
328 }
329 // setcursor();
330 oi = nil;
331 } else if (g->lastbut != 0 && s->m.buttons != 0){
332 /* button is down, keep tracking border */
333 if(!eqpt(s->m.xy, p)){
334 p = onscreen(s->m.xy);
335 r = canonrect(Rpt(p0, p));
336 if(Dx(r)>5 && Dy(r)>5){
337 i = allocwindow(wscreen, r, Refnone, 0xEEEEEEFF); /* grey */
338 freeimage(oi);
339 if(i == nil)
340 goto Rescue;
341 oi = i;
342 border(i, r, Selborder, red, ZP);
343 flushimage(display, 1);
344 }
345 }
346 } else if (g->lastbut != 0 && s->m.buttons == 0){
347 /* button went up, resize kiddies */
348 }
349 g->lastbut = s->m.buttons;
350 #endif
351 }
352
353 static void
activategroup(Control * c,int act)354 activategroup(Control *c, int act)
355 {
356 int i;
357 Group *g;
358
359 g = (Group*)c;
360 for (i = 0; i < g->nkids; i++)
361 if (act)
362 activate(g->kids[i]);
363 else
364 deactivate(g->kids[i]);
365 }
366
367 Control *
createrow(Controlset * cs,char * name)368 createrow(Controlset *cs, char *name)
369 {
370 Control *c;
371 c = _createctl(cs, "row", sizeof(Group), name);
372 groupinit((Group*)c);
373 c->setsize = groupsize;
374 c->activate = activategroup;
375 return c;
376 }
377
378 Control *
createcolumn(Controlset * cs,char * name)379 createcolumn(Controlset *cs, char *name)
380 {
381 Control *c;
382 c = _createctl(cs, "column", sizeof(Group), name);
383 groupinit((Group*)c);
384 c->setsize = groupsize;
385 c->activate = activategroup;
386 return c;
387 }
388
389 Control *
createboxbox(Controlset * cs,char * name)390 createboxbox(Controlset *cs, char *name)
391 {
392 Control *c;
393 c = _createctl(cs, "boxbox", sizeof(Group), name);
394 groupinit((Group*)c);
395 c->activate = activategroup;
396 return c;
397 }
398
399 Control *
createstack(Controlset * cs,char * name)400 createstack(Controlset *cs, char *name)
401 {
402 Control *c;
403 c = _createctl(cs, "stack", sizeof(Group), name);
404 groupinit((Group*)c);
405 c->setsize = groupsize;
406 return c;
407 }
408
409 void
_ctladdgroup(Control * c,Control * q)410 _ctladdgroup(Control *c, Control *q)
411 {
412 Group *g = (Group*)c;
413
414 g->kids = ctlrealloc(g->kids, sizeof(Group*)*(g->nkids+1));
415 g->kids[g->nkids++] = q;
416 }
417
418 static void
removegroup(Group * g,int n)419 removegroup(Group *g, int n)
420 {
421 int i;
422
423 if (g->selected == n)
424 g->selected = -1;
425 else if (g->selected > n)
426 g->selected--;
427
428 for (i = n+1; i < g->nkids; i++)
429 g->kids[i-1] = g->kids[i];
430 g->nkids--;
431 }
432
433 static void
groupsize(Control * c)434 groupsize(Control *c)
435 {
436 Rectangle r;
437 int i;
438 Control *q;
439 Group *g;
440
441 g = (Group*)c;
442 assert(g->type == Ctlcolumn || g->type == Ctlrow || g->type == Ctlstack);
443 if (g->mansize) return;
444 r = Rect(1, 1, 1, 1);
445 if (debug) fprint(2, "groupsize %q\n", g->name);
446 for (i = 0; i < g->nkids; i++){
447 q = g->kids[i];
448 if (q->setsize)
449 q->setsize(q);
450 if (q->size.min.x == 0 || q->size.min.y == 0 || q->size.max.x == 0 || q->size.max.y == 0)
451 ctlerror("%q: bad size %R", q->name, q->size);
452 if (debug) fprint(2, "groupsize %q: [%d %q]: %R\n", g->name, i, q->name, q->size);
453 switch(g->type){
454 case Ctlrow:
455 if (i)
456 r.min.x += q->size.min.x + g->border;
457 else
458 r.min.x = q->size.min.x;
459 if (i)
460 r.max.x += q->size.max.x + g->border;
461 else
462 r.max.x = q->size.max.x;
463 if (r.min.y < q->size.min.y) r.min.y = q->size.min.y;
464 if (r.max.y < q->size.max.y) r.max.y = q->size.max.y;
465 break;
466 case Ctlcolumn:
467 if (r.min.x < q->size.min.x) r.min.x = q->size.min.x;
468 if (r.max.x < q->size.max.x) r.max.x = q->size.max.x;
469 if (i)
470 r.min.y += q->size.min.y + g->border;
471 else
472 r.min.y = q->size.min.y;
473 if (i)
474 r.max.y += q->size.max.y + g->border;
475 else
476 r.max.y = q->size.max.y;
477 break;
478 case Ctlstack:
479 if (r.min.x < q->size.min.x) r.min.x = q->size.min.x;
480 if (r.max.x < q->size.max.x) r.max.x = q->size.max.x;
481 if (r.min.y < q->size.min.y) r.min.y = q->size.min.y;
482 if (r.max.y < q->size.max.y) r.max.y = q->size.max.y;
483 break;
484 }
485 }
486 g->size = rectaddpt(r, Pt(g->border, g->border));
487 if (debug) fprint(2, "groupsize %q: %R\n", g->name, g->size);
488 }
489
490 static void
boxboxresize(Group * g,Rectangle r)491 boxboxresize(Group *g, Rectangle r)
492 {
493 int rows, cols, ht, wid, i, hpad, wpad;
494 Rectangle rr;
495
496 if(debug) fprint(2, "boxboxresize %q %R (%d×%d) min/max %R separation %d\n", g->name, r, Dx(r), Dy(r), g->size, g->separation);
497 ht = 0;
498 for(i=0; i<g->nkids; i++){
499 if (g->kids[i]->size.min.y > ht)
500 ht = g->kids[i]->size.min.y;
501 }
502 if (ht == 0)
503 ctlerror("boxboxresize: height");
504 rows = Dy(r) / (ht+g->separation);
505 hpad = (Dy(r) % (ht+g->separation)) / g->nkids;
506 cols = (g->nkids+rows-1)/rows;
507 wid = Dx(r) / cols - g->separation;
508 for(i=0; i<g->nkids; i++){
509 if (g->kids[i]->size.max.x < wid)
510 wid = g->kids[i]->size.max.x;
511 }
512 for(i=0; i<g->nkids; i++){
513 if (g->kids[i]->size.min.x > wid)
514 wid = g->kids[i]->size.min.x;
515 }
516 if (wid > Dx(r) / cols)
517 ctlerror("can't fit controls in boxbox");
518 wpad = (Dx(r) % (wid+g->separation)) / g->nkids;
519 rr = rectaddpt(Rect(0,0,wid, ht), addpt(r.min, Pt(g->separation/2, g->separation/2)));
520 if(debug) fprint(2, "boxboxresize rows %d, cols %d, wid %d, ht %d, wpad %d, hpad %d\n", rows, cols, wid, ht, wpad, hpad);
521 for(i=0; i<g->nkids; i++){
522 if(debug) fprint(2, " %d %q: %R (%d×%d)\n", i, g->kids[i]->name, rr, Dx(rr), Dy(rr));
523 _ctlprint(g->kids[i], "rect %R",
524 rectaddpt(rr, Pt((wpad+wid+g->separation)*(i/rows), (hpad+ht+g->separation)*(i%rows))));
525 }
526 g->nseparators = rows + cols - 2;
527 g->separators = realloc(g->separators, g->nseparators*sizeof(Rectangle));
528 rr = r;
529 rr.max.y = rr.min.y + g->separation+hpad;
530 for (i = 1; i < rows; i++){
531 g->separators[i-1] = rectaddpt(rr, Pt(0, (hpad+ht+g->separation)*i-g->separation-hpad));
532 if(debug) fprint(2, "row separation %d [%d]: %R\n", i, i-1, rectaddpt(rr, Pt(0, (hpad+ht+g->separation)*i-g->separation)));
533 }
534 rr = r;
535 rr.max.x = rr.min.x + g->separation+wpad;
536 for (i = 1; i < cols; i++){
537 g->separators[i+rows-2] = rectaddpt(rr, Pt((wpad+wid+g->separation)*i-g->separation-wpad, 0));
538 if(debug) fprint(2, "col separation %d [%d]: %R\n", i, i+rows-2, rectaddpt(rr, Pt((wpad+wid+g->separation)*i-g->separation, 0)));
539 }
540 }
541
542 static void
columnresize(Group * g,Rectangle r)543 columnresize(Group *g, Rectangle r)
544 {
545 int x, y, *d, *p, i, j, t;
546 Rectangle rr;
547 Control *q;
548
549 x = Dx(r);
550 y = Dy(r);
551 if(debug) fprint(2, "columnresize %q %R (%d×%d) min/max %R separation %d\n", g->name, r, Dx(r), Dy(r), g->size, g->separation);
552 if (x < g->size.min.x) {
553 werrstr("resize %s: too narrow: need %d, have %d", g->name, g->size.min.x, x);
554 r.max.x = r.min.x + g->size.min.x;
555 }
556 if (y < g->size.min.y) {
557 werrstr("resize %s: too short: need %d, have %d", g->name, g->size.min.y, y);
558 r.max.y = r.min.y + g->size.min.y;
559 y = Dy(r);
560 }
561 d = ctlmalloc(g->nkids*sizeof(int));
562 p = ctlmalloc(g->nkids*sizeof(int));
563 if(debug) fprint(2, "kiddies: ");
564 for (i = 0; i < g->nkids; i++) {
565 q = g->kids[i];
566 if(debug) fprint(2, "[%q]: %d⋯%d\t", q->name, q->size.min.y, q->size.max.y);
567 d[i] = q->size.min.y;
568 y -= d[i];
569 p[i] = q->size.max.y - q->size.min.y;
570 }
571 if(debug) fprint(2, "\n");
572 y -= (g->nkids-1) * g->separation;
573 if(y < 0){
574 if (debug) fprint(2, "columnresize: y == %d\n", y);
575 y = 0;
576 }
577 if (y >= g->size.max.y - g->size.min.y) {
578 // all rects can be maximum width
579 for (i = 0; i < g->nkids; i++)
580 d[i] += p[i];
581 y -= g->size.max.y - g->size.min.y;
582 } else {
583 // rects can't be max width, divide up the rest
584 j = y;
585 for (i = 0; i < g->nkids; i++) {
586 t = p[i] * y/(g->size.max.y - g->size.min.y);
587 d[i] += t;
588 j -= t;
589 }
590 d[0] += j;
591 y = 0;
592 }
593 g->nseparators = g->nkids-1;
594 g->separators = realloc(g->separators, g->nseparators*sizeof(Rectangle));
595 j = 0;
596 rr = r;
597 for (i = 0; i < g->nkids; i++) {
598 q = g->kids[i];
599 if (i < g->nkids - 1){
600 g->separators[i].min.x = r.min.x;
601 g->separators[i].max.x = r.max.x;
602 }
603 t = y / (g->nkids - i);
604 y -= t;
605 j += t/2;
606 rr.min.y = r.min.y + j;
607 if (i)
608 g->separators[i-1].max.y = rr.min.y;
609 j += d[i];
610 rr.max.y = r.min.y + j;
611 if (i < g->nkids - 1)
612 g->separators[i].min.y = rr.max.y;
613 j += g->separation + t - t/2;
614 _ctlprint(q, "rect %R", rr);
615 if(debug) fprint(2, " %d %q: %R (%d×%d)\n", i, q->name, rr, Dx(rr), Dy(rr));
616 }
617 free(d);
618 free(p);
619 }
620
621 static void
rowresize(Group * g,Rectangle r)622 rowresize(Group *g, Rectangle r)
623 {
624 int x, y, *d, *p, i, j, t;
625 Rectangle rr;
626 Control *q;
627
628 x = Dx(r);
629 y = Dy(r);
630 if(debug) fprint(2, "rowresize %q %R (%d×%d), separation %d\n", g->name, r, Dx(r), Dy(r), g->separation);
631 if (x < g->size.min.x) {
632 werrstr("resize %s: too narrow: need %d, have %d", g->name, g->size.min.x, x);
633 r.max.x = r.min.x + g->size.min.x;
634 x = Dx(r);
635 }
636 if (y < g->size.min.y) {
637 werrstr("resize %s: too short: need %d, have %d", g->name, g->size.min.y, y);
638 r.max.y = r.min.y + g->size.min.y;
639 }
640 d = ctlmalloc(g->nkids*sizeof(int));
641 p = ctlmalloc(g->nkids*sizeof(int));
642 if(debug) fprint(2, "kiddies: ");
643 for (i = 0; i < g->nkids; i++) {
644 q = g->kids[i];
645 if(debug) fprint(2, "[%q]: %d⋯%d\t", q->name, q->size.min.x, q->size.max.x);
646 d[i] = q->size.min.x;
647 x -= d[i];
648 p[i] = q->size.max.x - q->size.min.x;
649 }
650 if(debug) fprint(2, "\n");
651 x -= (g->nkids-1) * g->separation;
652 if(x < 0){
653 if (debug) fprint(2, "rowresize: x == %d\n", x);
654 x = 0;
655 }
656 if (x >= g->size.max.x - g->size.min.x) {
657 if (debug) fprint(2, "max: %d > %d - %d", x, g->size.max.x, g->size.min.x);
658 // all rects can be maximum width
659 for (i = 0; i < g->nkids; i++)
660 d[i] += p[i];
661 x -= g->size.max.x - g->size.min.x;
662 } else {
663 if (debug) fprint(2, "divvie up: %d < %d - %d", x, g->size.max.x, g->size.min.x);
664 // rects can't be max width, divide up the rest
665 j = x;
666 for (i = 0; i < g->nkids; i++) {
667 t = p[i] * x/(g->size.max.x - g->size.min.x);
668 d[i] += t;
669 j -= t;
670 }
671 d[0] += j;
672 x = 0;
673 }
674 j = 0;
675 g->nseparators = g->nkids-1;
676 g->separators = realloc(g->separators, g->nseparators*sizeof(Rectangle));
677 rr = r;
678 for (i = 0; i < g->nkids; i++) {
679 q = g->kids[i];
680 if (i < g->nkids - 1){
681 g->separators[i].min.y = r.min.y;
682 g->separators[i].max.y = r.max.y;
683 }
684 t = x / (g->nkids - i);
685 x -= t;
686 j += t/2;
687 rr.min.x = r.min.x + j;
688 if (i)
689 g->separators[i-1].max.x = rr.min.x;
690 j += d[i];
691 rr.max.x = r.min.x + j;
692 if (i < g->nkids - 1)
693 g->separators[i].min.x = rr.max.x;
694 j += g->separation + t - t/2;
695 _ctlprint(q, "rect %R", rr);
696 if(debug) fprint(2, " %d %q: %R (%d×%d)\n", i, q->name, rr, Dx(rr), Dy(rr));
697 }
698 free(d);
699 free(p);
700 }
701
702 static void
stackresize(Group * g,Rectangle r)703 stackresize(Group *g, Rectangle r)
704 {
705 int x, y, i;
706 Control *q;
707
708 x = Dx(r);
709 y = Dy(r);
710 if(debug) fprint(2, "stackresize %q %R (%d×%d)\n", g->name, r, Dx(r), Dy(r));
711 if (x < g->size.min.x){
712 werrstr("resize %s: too narrow: need %d, have %d", g->name, g->size.min.x, x);
713 return;
714 }
715 if (y < g->size.min.y){
716 werrstr("resize %s: too short: need %d, have %d", g->name, g->size.min.y, y);
717 return;
718 }
719 if (x > g->size.max.x) {
720 x = (x - g->size.max.x)/2;
721 r.min.x += x;
722 r.max.x -= x;
723 }
724 if (y > g->size.max.y) {
725 y = (y - g->size.max.y)/2;
726 r.min.y += y;
727 r.max.y -= y;
728 }
729 for (i = 0; i < g->nkids; i++){
730 q = g->kids[i];
731 if(debug) fprint(2, " %d %q: %R (%d×%d)\n", i, q->name, r, Dx(r), Dy(r));
732 }
733 for (i = 0; i < g->nkids; i++){
734 q = g->kids[i];
735 _ctlprint(q, "rect %R", r);
736 }
737 }
738