xref: /plan9/sys/src/libcontrol/group.c (revision 6f314b92956e16637888967d7478acfbd2ac8dab)
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