]> git.lizzy.rs Git - plan9front.git/blob - sys/src/libcontrol/group.c
ip/cifsd: fix missing int return type for vpack() (thanks pr)
[plan9front.git] / sys / src / libcontrol / group.c
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
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
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
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
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
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 *
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 *
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 *
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 *
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
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
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
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
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
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
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
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 }