xref: /plan9/sys/src/libcontrol/group.c (revision 6f314b92956e16637888967d7478acfbd2ac8dab)
19a747e4fSDavid du Colombier #include <u.h>
29a747e4fSDavid du Colombier #include <libc.h>
39a747e4fSDavid du Colombier #include <thread.h>
49a747e4fSDavid du Colombier #include <draw.h>
59a747e4fSDavid du Colombier #include <mouse.h>
69a747e4fSDavid du Colombier #include <keyboard.h>
79a747e4fSDavid du Colombier #include <control.h>
89a747e4fSDavid du Colombier #include "group.h"
99a747e4fSDavid du Colombier 
109a747e4fSDavid du Colombier static int debug = 0;
11*6f314b92SDavid du Colombier static int debugm = 0;
12*6f314b92SDavid du Colombier static int debugr = 0;
139a747e4fSDavid du Colombier 
149a747e4fSDavid du Colombier enum{
159a747e4fSDavid du Colombier 	EAdd,
169a747e4fSDavid du Colombier 	EBorder,
179a747e4fSDavid du Colombier 	EBordercolor,
189a747e4fSDavid du Colombier 	EFocus,
199a747e4fSDavid du Colombier 	EHide,
209a747e4fSDavid du Colombier 	EImage,
219a747e4fSDavid du Colombier 	ERect,
229a747e4fSDavid du Colombier 	ERemove,
239a747e4fSDavid du Colombier 	EReveal,
249a747e4fSDavid du Colombier 	ESeparation,
259a747e4fSDavid du Colombier 	EShow,
269a747e4fSDavid du Colombier 	ESize,
279a747e4fSDavid du Colombier };
289a747e4fSDavid du Colombier 
299a747e4fSDavid du Colombier static char *cmds[] = {
309a747e4fSDavid du Colombier 	[EAdd] =			"add",
319a747e4fSDavid du Colombier 	[EBorder] =		"border",
329a747e4fSDavid du Colombier 	[EBordercolor] =	"bordercolor",
339a747e4fSDavid du Colombier 	[EFocus] = 		"focus",
349a747e4fSDavid du Colombier 	[EHide] =			"hide",
359a747e4fSDavid du Colombier 	[EImage] =		"image",
369a747e4fSDavid du Colombier 	[ERect] =			"rect",
379a747e4fSDavid du Colombier 	[ERemove] =		"remove",
389a747e4fSDavid du Colombier 	[EReveal] =		"reveal",
399a747e4fSDavid du Colombier 	[ESeparation] =		"separation",
409a747e4fSDavid du Colombier 	[EShow] =			"show",
419a747e4fSDavid du Colombier 	[ESize] =			"size",
429a747e4fSDavid du Colombier };
439a747e4fSDavid du Colombier 
449a747e4fSDavid du Colombier static void		boxboxresize(Group*, Rectangle);
459a747e4fSDavid du Colombier static void		columnresize(Group*, Rectangle);
469a747e4fSDavid du Colombier static void		groupctl(Control *c, CParse *cp);
479a747e4fSDavid du Colombier static void		groupfree(Control*);
489a747e4fSDavid du Colombier static void		groupmouse(Control *, Mouse *);
499a747e4fSDavid du Colombier static void		groupsize(Control *c);
509a747e4fSDavid du Colombier static void		removegroup(Group*, int);
519a747e4fSDavid du Colombier static void		rowresize(Group*, Rectangle);
529a747e4fSDavid du Colombier static void		stackresize(Group*, Rectangle);
539a747e4fSDavid du Colombier 
549a747e4fSDavid du Colombier static void
groupinit(Group * g)559a747e4fSDavid du Colombier groupinit(Group *g)
569a747e4fSDavid du Colombier {
579a747e4fSDavid du Colombier 	g->bordercolor = _getctlimage("black");
589a747e4fSDavid du Colombier 	g->image = _getctlimage("white");
599a747e4fSDavid du Colombier 	g->border = 0;
609a747e4fSDavid du Colombier 	g->mansize = 0;
619a747e4fSDavid du Colombier 	g->separation = 0;
629a747e4fSDavid du Colombier 	g->selected = -1;
63*6f314b92SDavid du Colombier 	g->lastkid = -1;
649a747e4fSDavid du Colombier 	g->kids = nil;
659a747e4fSDavid du Colombier 	g->separators = nil;
669a747e4fSDavid du Colombier 	g->nkids = 0;
679a747e4fSDavid du Colombier 	g->nseparators = 0;
689a747e4fSDavid du Colombier 	g->ctl = groupctl;
699a747e4fSDavid du Colombier 	g->mouse = groupmouse;
709a747e4fSDavid du Colombier 	g->exit = groupfree;
719a747e4fSDavid du Colombier }
729a747e4fSDavid du Colombier 
739a747e4fSDavid du Colombier static void
groupctl(Control * c,CParse * cp)749a747e4fSDavid du Colombier groupctl(Control *c, CParse *cp)
759a747e4fSDavid du Colombier {
769a747e4fSDavid du Colombier 	int cmd, i, n;
779a747e4fSDavid du Colombier 
789a747e4fSDavid du Colombier 	Rectangle r;
799a747e4fSDavid du Colombier 	Group *g;
809a747e4fSDavid du Colombier 
819a747e4fSDavid du Colombier 	g = (Group*)c;
829a747e4fSDavid du Colombier 	cmd = _ctllookup(cp->args[0], cmds, nelem(cmds));
839a747e4fSDavid du Colombier 	switch(cmd){
849a747e4fSDavid du Colombier 	case EAdd:
859a747e4fSDavid du Colombier 		for (i = 1; i < cp->nargs; i++){
869a747e4fSDavid du Colombier 			c = controlcalled(cp->args[i]);
879a747e4fSDavid du Colombier 			if (c == nil)
889a747e4fSDavid du Colombier 				ctlerror("%q: no such control: %s", g->name, cp->args[i]);
899a747e4fSDavid du Colombier 			_ctladdgroup(g, c);
909a747e4fSDavid du Colombier 		}
919a747e4fSDavid du Colombier 		if (g->setsize)
929a747e4fSDavid du Colombier 			g->setsize((Control*)g);
939a747e4fSDavid du Colombier 		break;
949a747e4fSDavid du Colombier 	case EBorder:
959a747e4fSDavid du Colombier 		_ctlargcount(g, cp, 2);
969a747e4fSDavid du Colombier 		if(cp->iargs[1] < 0)
979a747e4fSDavid du Colombier 			ctlerror("%q: bad border: %c", g->name, cp->str);
989a747e4fSDavid du Colombier 		g->border = cp->iargs[1];
999a747e4fSDavid du Colombier 		break;
1009a747e4fSDavid du Colombier 	case EBordercolor:
1019a747e4fSDavid du Colombier 		_ctlargcount(g, cp, 2);
1029a747e4fSDavid du Colombier 		_setctlimage(g, &g->bordercolor, cp->args[1]);
1039a747e4fSDavid du Colombier 		break;
1049a747e4fSDavid du Colombier 	case EFocus:
1059a747e4fSDavid du Colombier 		/* ignore focus change */
1069a747e4fSDavid du Colombier 		break;
1079a747e4fSDavid du Colombier 	case EHide:
1089a747e4fSDavid du Colombier 		_ctlargcount(g, cp, 1);
1099a747e4fSDavid du Colombier 		for (i = 0; i < g->nkids; i++)
1109a747e4fSDavid du Colombier 			if (g->kids[i]->ctl)
1119a747e4fSDavid du Colombier 				_ctlprint(g->kids[i], "hide");
1129a747e4fSDavid du Colombier 		g->hidden = 1;
1139a747e4fSDavid du Colombier 		break;
1149a747e4fSDavid du Colombier 	case EImage:
1159a747e4fSDavid du Colombier 		_ctlargcount(g, cp, 2);
1169a747e4fSDavid du Colombier 		_setctlimage(g, &g->image, cp->args[1]);
1179a747e4fSDavid du Colombier 		break;
1189a747e4fSDavid du Colombier 	case ERect:
1199a747e4fSDavid du Colombier 		_ctlargcount(g, cp, 5);
1209a747e4fSDavid du Colombier 		r.min.x = cp->iargs[1];
1219a747e4fSDavid du Colombier 		r.min.y = cp->iargs[2];
1229a747e4fSDavid du Colombier 		r.max.x = cp->iargs[3];
1239a747e4fSDavid du Colombier 		r.max.y = cp->iargs[4];
1249a747e4fSDavid du Colombier 		if(Dx(r)<=0 || Dy(r)<=0)
1259a747e4fSDavid du Colombier 			ctlerror("%q: bad rectangle: %s", g->name, cp->str);
1269a747e4fSDavid du Colombier 		g->rect = r;
1279a747e4fSDavid du Colombier 		r = insetrect(r, g->border);
1289a747e4fSDavid du Colombier 		if (g->nkids == 0)
1299a747e4fSDavid du Colombier 			return;
1309a747e4fSDavid du Colombier 		switch(g->type){
1319a747e4fSDavid du Colombier 		case Ctlboxbox:
1329a747e4fSDavid du Colombier 			boxboxresize(g, r);
1339a747e4fSDavid du Colombier 			break;
1349a747e4fSDavid du Colombier 		case Ctlcolumn:
1359a747e4fSDavid du Colombier 			columnresize(g, r);
1369a747e4fSDavid du Colombier 			break;
1379a747e4fSDavid du Colombier 		case Ctlrow:
1389a747e4fSDavid du Colombier 			rowresize(g, r);
1399a747e4fSDavid du Colombier 			break;
1409a747e4fSDavid du Colombier 		case Ctlstack:
1419a747e4fSDavid du Colombier 			stackresize(g, r);
1429a747e4fSDavid du Colombier 			break;
1439a747e4fSDavid du Colombier 		}
1449a747e4fSDavid du Colombier 		break;
1459a747e4fSDavid du Colombier 	case ERemove:
1469a747e4fSDavid du Colombier 		_ctlargcount(g, cp, 2);
1479a747e4fSDavid du Colombier 		for (n = 0; n < g->nkids; n++)
1489a747e4fSDavid du Colombier 			if (strcmp(cp->args[1], g->kids[n]->name) == 0)
1499a747e4fSDavid du Colombier 				break;
1509a747e4fSDavid du Colombier 		if (n == g->nkids)
1519a747e4fSDavid du Colombier 			ctlerror("%s: remove nonexistent control: %q", g->name, cp->args[1]);
1529a747e4fSDavid du Colombier 		removegroup(g, n);
1539a747e4fSDavid du Colombier 		if (g->setsize)
1549a747e4fSDavid du Colombier 			g->setsize((Control*)g);
1559a747e4fSDavid du Colombier 		break;
1569a747e4fSDavid du Colombier 	case EReveal:
1579a747e4fSDavid du Colombier 		g->hidden = 0;
158*6f314b92SDavid du Colombier 		if (debugr) fprint(2, "reveal %s\n", g->name);
1599a747e4fSDavid du Colombier 		if (g->type == Ctlstack){
1609a747e4fSDavid du Colombier 			if (cp->nargs == 2){
1619a747e4fSDavid du Colombier 				if (cp->iargs[1] < 0 || cp->iargs[1] >= g->nkids)
1629a747e4fSDavid du Colombier 					ctlerror("%s: control out of range: %q", g->name, cp->str);
1639a747e4fSDavid du Colombier 				g->selected = cp->iargs[1];
1649a747e4fSDavid du Colombier 			}else
1659a747e4fSDavid du Colombier 				_ctlargcount(g, cp, 1);
1669a747e4fSDavid du Colombier 			for (i = 0; i < g->nkids; i++)
1679a747e4fSDavid du Colombier 				if (g->kids[i]->ctl){
1689a747e4fSDavid du Colombier 					if (g->selected == i){
169*6f314b92SDavid du Colombier 						if (debugr) fprint(2, "reveal %s: reveal kid %s\n", g->name, g->kids[i]->name);
1709a747e4fSDavid du Colombier 						_ctlprint(g->kids[i], "reveal");
1719a747e4fSDavid du Colombier 					}else{
172*6f314b92SDavid du Colombier 						if (debugr) fprint(2, "reveal %s: hide kid %s\n", g->name, g->kids[i]->name);
1739a747e4fSDavid du Colombier 						_ctlprint(g->kids[i], "hide");
1749a747e4fSDavid du Colombier 					}
1759a747e4fSDavid du Colombier 				}
1769a747e4fSDavid du Colombier 			break;
1779a747e4fSDavid du Colombier 		}
1789a747e4fSDavid du Colombier 		_ctlargcount(g, cp, 1);
1799a747e4fSDavid du Colombier 		if (debug) fprint(2, "reveal %s: border %R/%d\n", g->name, g->rect, g->border);
1809a747e4fSDavid du Colombier 		border(g->screen, g->rect, g->border, g->bordercolor->image, g->bordercolor->image->r.min);
1819a747e4fSDavid du Colombier 		r = insetrect(g->rect, g->border);
1829a747e4fSDavid du Colombier 		if (debug) fprint(2, "reveal %s: draw %R\n", g->name, r);
1839a747e4fSDavid du Colombier 		draw(g->screen, r, g->image->image, nil, g->image->image->r.min);
1849a747e4fSDavid du Colombier 		for (i = 0; i < g->nkids; i++)
1859a747e4fSDavid du Colombier 			if (g->kids[i]->ctl)
1869a747e4fSDavid du Colombier 				_ctlprint(g->kids[i], "reveal");
1879a747e4fSDavid du Colombier 		break;
1889a747e4fSDavid du Colombier 	case EShow:
1899a747e4fSDavid du Colombier 		_ctlargcount(g, cp, 1);
1909a747e4fSDavid du Colombier 		if (g->hidden)
1919a747e4fSDavid du Colombier 			break;
1929a747e4fSDavid du Colombier 		// pass it on to the kiddies
1939a747e4fSDavid du Colombier 		if (debug) fprint(2, "show %s: border %R/%d\n", g->name, g->rect, g->border);
1949a747e4fSDavid du Colombier 		border(g->screen, g->rect, g->border, g->bordercolor->image, g->bordercolor->image->r.min);
1959a747e4fSDavid du Colombier 		r = insetrect(g->rect, g->border);
1969a747e4fSDavid du Colombier 		if (debug) fprint(2, "show %s: draw %R\n", g->name, r);
1979a747e4fSDavid du Colombier 		draw(g->screen, r, g->image->image, nil, g->image->image->r.min);
1989a747e4fSDavid du Colombier 		for (i = 0; i < g->nkids; i++)
1999a747e4fSDavid du Colombier 			if (g->kids[i]->ctl){
2009a747e4fSDavid du Colombier 				if (debug) fprint(2, "show %s: kid %s: %q\n", g->name, g->kids[i]->name, cp->str);
2019a747e4fSDavid du Colombier 				_ctlprint(g->kids[i], "show");
2029a747e4fSDavid du Colombier 			}
2039a747e4fSDavid du Colombier 		flushimage(display, 1);
2049a747e4fSDavid du Colombier 		break;
2059a747e4fSDavid du Colombier 	case ESize:
2069a747e4fSDavid du Colombier 		r.max = Pt(_Ctlmaxsize, _Ctlmaxsize);
2079a747e4fSDavid du Colombier 		if (g->type == Ctlboxbox)
2089a747e4fSDavid du Colombier 			_ctlargcount(g, cp, 5);
2099a747e4fSDavid du Colombier 		switch(cp->nargs){
2109a747e4fSDavid du Colombier 		default:
2119a747e4fSDavid du Colombier 			ctlerror("%s: args of %q", g->name, cp->str);
2129a747e4fSDavid du Colombier 		case 1:
2139a747e4fSDavid du Colombier 			/* recursively set size */
2149a747e4fSDavid du Colombier 			g->mansize = 0;
2159a747e4fSDavid du Colombier 			if (g->setsize)
2169a747e4fSDavid du Colombier 				g->setsize((Control*)g);
2179a747e4fSDavid du Colombier 			break;
2189a747e4fSDavid du Colombier 		case 5:
2199a747e4fSDavid du Colombier 			_ctlargcount(g, cp, 5);
2209a747e4fSDavid du Colombier 			r.max.x = cp->iargs[3];
2219a747e4fSDavid du Colombier 			r.max.y = cp->iargs[4];
2229a747e4fSDavid du Colombier 			/* fall through */
2239a747e4fSDavid du Colombier 		case 3:
2249a747e4fSDavid du Colombier 			r.min.x = cp->iargs[1];
2259a747e4fSDavid du Colombier 			r.min.y = cp->iargs[2];
2269a747e4fSDavid du Colombier 			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)
2279a747e4fSDavid du Colombier 			ctlerror("%q: bad sizes: %s", g->name, cp->str);
2289a747e4fSDavid du Colombier 			g->size = r;
2299a747e4fSDavid du Colombier 			g->mansize = 1;
2309a747e4fSDavid du Colombier 			break;
2319a747e4fSDavid du Colombier 		}
2329a747e4fSDavid du Colombier 		break;
2339a747e4fSDavid du Colombier 	case ESeparation:
2349a747e4fSDavid du Colombier 		if (g->type != Ctlstack){
2359a747e4fSDavid du Colombier 			_ctlargcount(g, cp, 2);
2369a747e4fSDavid du Colombier 			if(cp->iargs[1] < 0)
2379a747e4fSDavid du Colombier 				ctlerror("%q: illegal value: %c", g->name, cp->str);
2389a747e4fSDavid du Colombier 			g->separation = cp->iargs[1];
2399a747e4fSDavid du Colombier 			break;
2409a747e4fSDavid du Colombier 		}
2419a747e4fSDavid du Colombier 		// fall through for Ctlstack
2429a747e4fSDavid du Colombier 	default:
2439a747e4fSDavid du Colombier 		ctlerror("%q: unrecognized message '%s'", g->name, cp->str);
2449a747e4fSDavid du Colombier 		break;
2459a747e4fSDavid du Colombier 	}
2469a747e4fSDavid du Colombier }
2479a747e4fSDavid du Colombier 
2489a747e4fSDavid du Colombier static void
groupfree(Control * c)2499a747e4fSDavid du Colombier groupfree(Control *c)
2509a747e4fSDavid du Colombier {
2519a747e4fSDavid du Colombier 	Group *g;
2529a747e4fSDavid du Colombier 
2539a747e4fSDavid du Colombier 	g = (Group*)c;
2549a747e4fSDavid du Colombier 	_putctlimage(g->bordercolor);
2559a747e4fSDavid du Colombier 	free(g->kids);
2569a747e4fSDavid du Colombier }
2579a747e4fSDavid du Colombier 
2589a747e4fSDavid du Colombier static void
groupmouse(Control * c,Mouse * m)2599a747e4fSDavid du Colombier groupmouse(Control *c, Mouse *m)
2609a747e4fSDavid du Colombier {
2619a747e4fSDavid du Colombier 	Group *g;
262*6f314b92SDavid du Colombier 	int i, lastkid;
2639a747e4fSDavid du Colombier 
2649a747e4fSDavid du Colombier 	g = (Group*)c;
2659a747e4fSDavid du Colombier 	if (g->type == Ctlstack){
266*6f314b92SDavid du Colombier 		i = g->selected;
267*6f314b92SDavid du Colombier 		if (i >= 0 && g->kids[i]->mouse &&
268*6f314b92SDavid du Colombier                         ( ( ((m->buttons == 0) || (g->lastbut == 0)) &&
269*6f314b92SDavid du Colombier                            ptinrect(m->xy, g->kids[i]->rect) ) ||
270*6f314b92SDavid du Colombier                          ( ((m->buttons != 0) || (g->lastbut != 0)) &&
271*6f314b92SDavid du Colombier 		         (g->lastkid == i) ) ) ) {
272*6f314b92SDavid du Colombier 			if (debugm) fprint(2, "groupmouse %s mouse kid %s i=%d lastkid=%d buttons=%d lastbut=%d inrect=%d\n",
273*6f314b92SDavid du Colombier 						g->name, g->kids[i]->name, i, g->lastkid, m->buttons, g->lastbut,
274*6f314b92SDavid du Colombier 						ptinrect(m->xy, g->kids[i]->rect) ? 1 : 0);
275*6f314b92SDavid du Colombier 			(g->kids[i]->mouse)(g->kids[i], m);
276*6f314b92SDavid du Colombier 			g->lastkid = i;
277*6f314b92SDavid du Colombier 			g->lastbut = m->buttons;
278*6f314b92SDavid du Colombier 		} else {
279*6f314b92SDavid du Colombier 			if (debugm) fprint(2, "groupmouse %s skip kid %s i=%d lastkid=%d buttons=%d lastbut=%d inrect=%d\n",
280*6f314b92SDavid du Colombier 						g->name, g->kids[i]->name, i, g->lastkid, m->buttons, g->lastbut,
281*6f314b92SDavid du Colombier 						ptinrect(m->xy, g->kids[i]->rect) ? 1 : 0);
2829a747e4fSDavid du Colombier 		}
2839a747e4fSDavid du Colombier 		return;
2849a747e4fSDavid du Colombier 	}
2859a747e4fSDavid du Colombier 
286*6f314b92SDavid du Colombier 	lastkid = -1;
287*6f314b92SDavid du Colombier 	for(i=0; i<g->nkids; i++) {
288*6f314b92SDavid du Colombier 		if(g->kids[i]->mouse &&
289*6f314b92SDavid du Colombier                       ( ( ((m->buttons == 0) || (g->lastbut == 0)) &&
290*6f314b92SDavid du Colombier                            ptinrect(m->xy, g->kids[i]->rect) ) ||
291*6f314b92SDavid du Colombier                         ( ((m->buttons != 0) || (g->lastbut != 0)) &&
292*6f314b92SDavid du Colombier 		         (g->lastkid == i) ) ) ) {
293*6f314b92SDavid du Colombier 			if (debugm) fprint(2, "groupmouse %s mouse kid %s i=%d lastkid=%d buttons=%d lastbut=%d inrect=%d\n",
294*6f314b92SDavid du Colombier 						g->name, g->kids[i]->name, i, g->lastkid, m->buttons, g->lastbut,
295*6f314b92SDavid du Colombier 						ptinrect(m->xy, g->kids[i]->rect) ? 1 : 0);
2969a747e4fSDavid du Colombier 			(g->kids[i]->mouse)(g->kids[i], m);
297*6f314b92SDavid du Colombier 			lastkid = i;
298*6f314b92SDavid du Colombier 		} else {
299*6f314b92SDavid du Colombier 			if (debugm) fprint(2, "groupmouse %s skip kid %s i=%d lastkid=%d buttons=%d lastbut=%d inrect=%d\n",
300*6f314b92SDavid du Colombier 						g->name, g->kids[i]->name, i, g->lastkid, m->buttons, g->lastbut,
301*6f314b92SDavid du Colombier 						ptinrect(m->xy, g->kids[i]->rect) ? 1 : 0);
3029a747e4fSDavid du Colombier 		}
303*6f314b92SDavid du Colombier 	}
304*6f314b92SDavid du Colombier 	g->lastkid = lastkid;
305*6f314b92SDavid du Colombier 	g->lastbut = m->buttons;
3069a747e4fSDavid du Colombier 
3079a747e4fSDavid du Colombier #ifdef notdef
3089a747e4fSDavid du Colombier 	if(m->buttons == 0){
3099a747e4fSDavid du Colombier 		/* buttons now up */
3109a747e4fSDavid du Colombier 		g->lastbut = 0;
3119a747e4fSDavid du Colombier 		return;
3129a747e4fSDavid du Colombier 	}
3139a747e4fSDavid du Colombier 	if(g->lastbut == 0 && m->buttons != 0){
3149a747e4fSDavid du Colombier 		/* button went down, start tracking border */
3159a747e4fSDavid du Colombier 		switch(g->stacking){
3169a747e4fSDavid du Colombier 		default:
3179a747e4fSDavid du Colombier 			return;
3189a747e4fSDavid du Colombier 		case Vertical:
3199a747e4fSDavid du Colombier 			p = Pt(m->xy.x, middle_of_border.y);
3209a747e4fSDavid du Colombier 			p0 = Pt(g->r.min.x, m->xy.y);
3219a747e4fSDavid du Colombier 			p1 = Pt(g->r.max.x, m->xy.y);
3229a747e4fSDavid du Colombier 			break;
3239a747e4fSDavid du Colombier 		case Horizontal:
3249a747e4fSDavid du Colombier 			p = Pt(middle_of_border.x, m->xy.y);
3259a747e4fSDavid du Colombier 			p0 = Pt(m->xy.x, g->r.min.y);
3269a747e4fSDavid du Colombier 			p1 = Pt(m->xy.x, g->r.max.y);
3279a747e4fSDavid du Colombier 			break;
3289a747e4fSDavid du Colombier 		}
3299a747e4fSDavid du Colombier 	//	setcursor();
3309a747e4fSDavid du Colombier 		oi = nil;
3319a747e4fSDavid du Colombier 	} else if (g->lastbut != 0 && s->m.buttons != 0){
3329a747e4fSDavid du Colombier 		/* button is down, keep tracking border */
3339a747e4fSDavid du Colombier 		if(!eqpt(s->m.xy, p)){
3349a747e4fSDavid du Colombier 			p = onscreen(s->m.xy);
3359a747e4fSDavid du Colombier 			r = canonrect(Rpt(p0, p));
3369a747e4fSDavid du Colombier 			if(Dx(r)>5 && Dy(r)>5){
3379a747e4fSDavid du Colombier 				i = allocwindow(wscreen, r, Refnone, 0xEEEEEEFF); /* grey */
3389a747e4fSDavid du Colombier 				freeimage(oi);
3399a747e4fSDavid du Colombier 				if(i == nil)
3409a747e4fSDavid du Colombier 					goto Rescue;
3419a747e4fSDavid du Colombier 				oi = i;
3429a747e4fSDavid du Colombier 				border(i, r, Selborder, red, ZP);
3439a747e4fSDavid du Colombier 				flushimage(display, 1);
3449a747e4fSDavid du Colombier 			}
3459a747e4fSDavid du Colombier 		}
3469a747e4fSDavid du Colombier 	} else if (g->lastbut != 0 && s->m.buttons == 0){
3479a747e4fSDavid du Colombier 		/* button went up, resize kiddies */
3489a747e4fSDavid du Colombier 	}
3499a747e4fSDavid du Colombier 	g->lastbut = s->m.buttons;
3509a747e4fSDavid du Colombier #endif
3519a747e4fSDavid du Colombier }
3529a747e4fSDavid du Colombier 
3539a747e4fSDavid du Colombier static void
activategroup(Control * c,int act)3549a747e4fSDavid du Colombier activategroup(Control *c, int act)
3559a747e4fSDavid du Colombier {
3569a747e4fSDavid du Colombier 	int i;
3579a747e4fSDavid du Colombier 	Group *g;
3589a747e4fSDavid du Colombier 
3599a747e4fSDavid du Colombier 	g = (Group*)c;
3609a747e4fSDavid du Colombier 	for (i = 0; i < g->nkids; i++)
3619a747e4fSDavid du Colombier 		if (act)
3629a747e4fSDavid du Colombier 			activate(g->kids[i]);
3639a747e4fSDavid du Colombier 		else
3649a747e4fSDavid du Colombier 			deactivate(g->kids[i]);
3659a747e4fSDavid du Colombier }
3669a747e4fSDavid du Colombier 
3679a747e4fSDavid du Colombier Control *
createrow(Controlset * cs,char * name)3689a747e4fSDavid du Colombier createrow(Controlset *cs, char *name)
3699a747e4fSDavid du Colombier {
3709a747e4fSDavid du Colombier 	Control *c;
3719a747e4fSDavid du Colombier 	c = _createctl(cs, "row", sizeof(Group), name);
3729a747e4fSDavid du Colombier 	groupinit((Group*)c);
3739a747e4fSDavid du Colombier 	c->setsize = groupsize;
3749a747e4fSDavid du Colombier 	c->activate = activategroup;
3759a747e4fSDavid du Colombier 	return c;
3769a747e4fSDavid du Colombier }
3779a747e4fSDavid du Colombier 
3789a747e4fSDavid du Colombier Control *
createcolumn(Controlset * cs,char * name)3799a747e4fSDavid du Colombier createcolumn(Controlset *cs, char *name)
3809a747e4fSDavid du Colombier {
3819a747e4fSDavid du Colombier 	Control *c;
3829a747e4fSDavid du Colombier 	c = _createctl(cs, "column", sizeof(Group), name);
3839a747e4fSDavid du Colombier 	groupinit((Group*)c);
3849a747e4fSDavid du Colombier 	c->setsize = groupsize;
3859a747e4fSDavid du Colombier 	c->activate = activategroup;
3869a747e4fSDavid du Colombier 	return c;
3879a747e4fSDavid du Colombier }
3889a747e4fSDavid du Colombier 
3899a747e4fSDavid du Colombier Control *
createboxbox(Controlset * cs,char * name)3909a747e4fSDavid du Colombier createboxbox(Controlset *cs, char *name)
3919a747e4fSDavid du Colombier {
3929a747e4fSDavid du Colombier 	Control *c;
3939a747e4fSDavid du Colombier 	c = _createctl(cs, "boxbox", sizeof(Group), name);
3949a747e4fSDavid du Colombier 	groupinit((Group*)c);
3959a747e4fSDavid du Colombier 	c->activate = activategroup;
3969a747e4fSDavid du Colombier 	return c;
3979a747e4fSDavid du Colombier }
3989a747e4fSDavid du Colombier 
3999a747e4fSDavid du Colombier Control *
createstack(Controlset * cs,char * name)4009a747e4fSDavid du Colombier createstack(Controlset *cs, char *name)
4019a747e4fSDavid du Colombier {
4029a747e4fSDavid du Colombier 	Control *c;
4039a747e4fSDavid du Colombier 	c = _createctl(cs, "stack", sizeof(Group), name);
4049a747e4fSDavid du Colombier 	groupinit((Group*)c);
4059a747e4fSDavid du Colombier 	c->setsize = groupsize;
4069a747e4fSDavid du Colombier 	return c;
4079a747e4fSDavid du Colombier }
4089a747e4fSDavid du Colombier 
4099a747e4fSDavid du Colombier void
_ctladdgroup(Control * c,Control * q)4109a747e4fSDavid du Colombier _ctladdgroup(Control *c, Control *q)
4119a747e4fSDavid du Colombier {
4129a747e4fSDavid du Colombier 	Group *g = (Group*)c;
4139a747e4fSDavid du Colombier 
4149a747e4fSDavid du Colombier 	g->kids = ctlrealloc(g->kids, sizeof(Group*)*(g->nkids+1));
4159a747e4fSDavid du Colombier 	g->kids[g->nkids++] = q;
4169a747e4fSDavid du Colombier }
4179a747e4fSDavid du Colombier 
4189a747e4fSDavid du Colombier static void
removegroup(Group * g,int n)4199a747e4fSDavid du Colombier removegroup(Group *g, int n)
4209a747e4fSDavid du Colombier {
4219a747e4fSDavid du Colombier 	int i;
4229a747e4fSDavid du Colombier 
4239a747e4fSDavid du Colombier 	if (g->selected == n)
4249a747e4fSDavid du Colombier 		g->selected = -1;
4259a747e4fSDavid du Colombier 	else if (g->selected > n)
4269a747e4fSDavid du Colombier 		g->selected--;
4279a747e4fSDavid du Colombier 
4289a747e4fSDavid du Colombier 	for (i = n+1; i < g->nkids; i++)
4299a747e4fSDavid du Colombier 		g->kids[i-1] = g->kids[i];
4309a747e4fSDavid du Colombier 	g->nkids--;
4319a747e4fSDavid du Colombier }
4329a747e4fSDavid du Colombier 
4339a747e4fSDavid du Colombier static void
groupsize(Control * c)4349a747e4fSDavid du Colombier groupsize(Control *c)
4359a747e4fSDavid du Colombier {
4369a747e4fSDavid du Colombier 	Rectangle r;
4379a747e4fSDavid du Colombier 	int i;
4389a747e4fSDavid du Colombier 	Control *q;
4399a747e4fSDavid du Colombier 	Group *g;
4409a747e4fSDavid du Colombier 
4419a747e4fSDavid du Colombier 	g = (Group*)c;
4429a747e4fSDavid du Colombier 	assert(g->type == Ctlcolumn || g->type == Ctlrow || g->type == Ctlstack);
4439a747e4fSDavid du Colombier 	if (g->mansize) return;
4449a747e4fSDavid du Colombier 	r = Rect(1, 1, 1, 1);
4459a747e4fSDavid du Colombier 	if (debug) fprint(2, "groupsize %q\n", g->name);
4469a747e4fSDavid du Colombier 	for (i = 0; i < g->nkids; i++){
4479a747e4fSDavid du Colombier 		q = g->kids[i];
4489a747e4fSDavid du Colombier 		if (q->setsize)
4499a747e4fSDavid du Colombier 			q->setsize(q);
4509a747e4fSDavid du Colombier 		if (q->size.min.x == 0 || q->size.min.y == 0 || q->size.max.x == 0 || q->size.max.y == 0)
4519a747e4fSDavid du Colombier 			ctlerror("%q: bad size %R", q->name, q->size);
4529a747e4fSDavid du Colombier 		if (debug) fprint(2, "groupsize %q: [%d %q]: %R\n", g->name, i, q->name, q->size);
4539a747e4fSDavid du Colombier 		switch(g->type){
4549a747e4fSDavid du Colombier 		case Ctlrow:
4559a747e4fSDavid du Colombier 			if (i)
4569a747e4fSDavid du Colombier 				r.min.x += q->size.min.x + g->border;
4579a747e4fSDavid du Colombier 			else
4589a747e4fSDavid du Colombier 				r.min.x = q->size.min.x;
4599a747e4fSDavid du Colombier 			if (i)
4609a747e4fSDavid du Colombier 				r.max.x += q->size.max.x + g->border;
4619a747e4fSDavid du Colombier 			else
4629a747e4fSDavid du Colombier 				r.max.x = q->size.max.x;
4639a747e4fSDavid du Colombier 			if (r.min.y < q->size.min.y) r.min.y = q->size.min.y;
4649a747e4fSDavid du Colombier 			if (r.max.y < q->size.max.y) r.max.y = q->size.max.y;
4659a747e4fSDavid du Colombier 			break;
4669a747e4fSDavid du Colombier 		case Ctlcolumn:
4679a747e4fSDavid du Colombier 			if (r.min.x < q->size.min.x) r.min.x = q->size.min.x;
4689a747e4fSDavid du Colombier 			if (r.max.x < q->size.max.x) r.max.x = q->size.max.x;
4699a747e4fSDavid du Colombier 			if (i)
4709a747e4fSDavid du Colombier 				r.min.y += q->size.min.y + g->border;
4719a747e4fSDavid du Colombier 			else
4729a747e4fSDavid du Colombier 				r.min.y = q->size.min.y;
4739a747e4fSDavid du Colombier 			if (i)
4749a747e4fSDavid du Colombier 				r.max.y += q->size.max.y + g->border;
4759a747e4fSDavid du Colombier 			else
4769a747e4fSDavid du Colombier 				r.max.y = q->size.max.y;
4779a747e4fSDavid du Colombier 			break;
4789a747e4fSDavid du Colombier 		case Ctlstack:
4799a747e4fSDavid du Colombier 			if (r.min.x < q->size.min.x) r.min.x = q->size.min.x;
4809a747e4fSDavid du Colombier 			if (r.max.x < q->size.max.x) r.max.x = q->size.max.x;
4819a747e4fSDavid du Colombier 			if (r.min.y < q->size.min.y) r.min.y = q->size.min.y;
4829a747e4fSDavid du Colombier 			if (r.max.y < q->size.max.y) r.max.y = q->size.max.y;
4839a747e4fSDavid du Colombier 			break;
4849a747e4fSDavid du Colombier 		}
4859a747e4fSDavid du Colombier 	}
4869a747e4fSDavid du Colombier 	g->size = rectaddpt(r, Pt(g->border, g->border));
4879a747e4fSDavid du Colombier 	if (debug) fprint(2, "groupsize %q: %R\n", g->name, g->size);
4889a747e4fSDavid du Colombier }
4899a747e4fSDavid du Colombier 
4909a747e4fSDavid du Colombier static void
boxboxresize(Group * g,Rectangle r)4919a747e4fSDavid du Colombier boxboxresize(Group *g, Rectangle r)
4929a747e4fSDavid du Colombier {
4939a747e4fSDavid du Colombier 	int rows, cols, ht, wid, i, hpad, wpad;
4949a747e4fSDavid du Colombier 	Rectangle rr;
4959a747e4fSDavid du Colombier 
4969a747e4fSDavid du Colombier 	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);
4979a747e4fSDavid du Colombier 	ht = 0;
4989a747e4fSDavid du Colombier 	for(i=0; i<g->nkids; i++){
4999a747e4fSDavid du Colombier 		if (g->kids[i]->size.min.y > ht)
5009a747e4fSDavid du Colombier 			ht = g->kids[i]->size.min.y;
5019a747e4fSDavid du Colombier 	}
5029a747e4fSDavid du Colombier 	if (ht == 0)
5039a747e4fSDavid du Colombier 		ctlerror("boxboxresize: height");
5049a747e4fSDavid du Colombier 	rows = Dy(r) / (ht+g->separation);
5059a747e4fSDavid du Colombier 	hpad = (Dy(r) % (ht+g->separation)) / g->nkids;
5069a747e4fSDavid du Colombier 	cols = (g->nkids+rows-1)/rows;
5079a747e4fSDavid du Colombier 	wid = Dx(r) / cols - g->separation;
5089a747e4fSDavid du Colombier 	for(i=0; i<g->nkids; i++){
5099a747e4fSDavid du Colombier 		if (g->kids[i]->size.max.x < wid)
5109a747e4fSDavid du Colombier 			wid = g->kids[i]->size.max.x;
5119a747e4fSDavid du Colombier 	}
5129a747e4fSDavid du Colombier 	for(i=0; i<g->nkids; i++){
5139a747e4fSDavid du Colombier 		if (g->kids[i]->size.min.x > wid)
5149a747e4fSDavid du Colombier 			wid = g->kids[i]->size.min.x;
5159a747e4fSDavid du Colombier 	}
5169a747e4fSDavid du Colombier 	if (wid > Dx(r) / cols)
5179a747e4fSDavid du Colombier 		ctlerror("can't fit controls in boxbox");
5189a747e4fSDavid du Colombier 	wpad = (Dx(r) % (wid+g->separation)) / g->nkids;
5199a747e4fSDavid du Colombier 	rr = rectaddpt(Rect(0,0,wid, ht), addpt(r.min, Pt(g->separation/2, g->separation/2)));
5209a747e4fSDavid du Colombier 	if(debug) fprint(2, "boxboxresize rows %d, cols %d, wid %d, ht %d, wpad %d, hpad %d\n", rows, cols, wid, ht, wpad, hpad);
5219a747e4fSDavid du Colombier 	for(i=0; i<g->nkids; i++){
5229a747e4fSDavid du Colombier 		if(debug) fprint(2, "	%d %q: %R (%d×%d)\n", i, g->kids[i]->name, rr, Dx(rr), Dy(rr));
5239a747e4fSDavid du Colombier 		_ctlprint(g->kids[i], "rect %R",
5249a747e4fSDavid du Colombier 			rectaddpt(rr, Pt((wpad+wid+g->separation)*(i/rows), (hpad+ht+g->separation)*(i%rows))));
5259a747e4fSDavid du Colombier 	}
5269a747e4fSDavid du Colombier 	g->nseparators = rows + cols - 2;
5279a747e4fSDavid du Colombier 	g->separators = realloc(g->separators, g->nseparators*sizeof(Rectangle));
5289a747e4fSDavid du Colombier 	rr = r;
5299a747e4fSDavid du Colombier 	rr.max.y = rr.min.y + g->separation+hpad;
5309a747e4fSDavid du Colombier 	for (i = 1; i < rows; i++){
5319a747e4fSDavid du Colombier 		g->separators[i-1] = rectaddpt(rr, Pt(0, (hpad+ht+g->separation)*i-g->separation-hpad));
5329a747e4fSDavid du Colombier 		if(debug) fprint(2, "row separation %d [%d]: %R\n", i, i-1, rectaddpt(rr, Pt(0, (hpad+ht+g->separation)*i-g->separation)));
5339a747e4fSDavid du Colombier 	}
5349a747e4fSDavid du Colombier 	rr = r;
5359a747e4fSDavid du Colombier 	rr.max.x = rr.min.x + g->separation+wpad;
5369a747e4fSDavid du Colombier 	for (i = 1; i < cols; i++){
5379a747e4fSDavid du Colombier 		g->separators[i+rows-2] = rectaddpt(rr, Pt((wpad+wid+g->separation)*i-g->separation-wpad, 0));
5389a747e4fSDavid du Colombier 		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)));
5399a747e4fSDavid du Colombier 	}
5409a747e4fSDavid du Colombier }
5419a747e4fSDavid du Colombier 
5429a747e4fSDavid du Colombier static void
columnresize(Group * g,Rectangle r)5439a747e4fSDavid du Colombier columnresize(Group *g, Rectangle r)
5449a747e4fSDavid du Colombier {
5459a747e4fSDavid du Colombier 	int x, y, *d, *p, i, j, t;
5469a747e4fSDavid du Colombier 	Rectangle rr;
5479a747e4fSDavid du Colombier 	Control *q;
5489a747e4fSDavid du Colombier 
5499a747e4fSDavid du Colombier 	x = Dx(r);
5509a747e4fSDavid du Colombier 	y = Dy(r);
5519a747e4fSDavid du Colombier 	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);
5529a747e4fSDavid du Colombier 	if (x < g->size.min.x) {
5539a747e4fSDavid du Colombier 		werrstr("resize %s: too narrow: need %d, have %d", g->name, g->size.min.x, x);
5549a747e4fSDavid du Colombier 		r.max.x = r.min.x + g->size.min.x;
5559a747e4fSDavid du Colombier 	}
5569a747e4fSDavid du Colombier 	if (y < g->size.min.y) {
5579a747e4fSDavid du Colombier 		werrstr("resize %s: too short: need %d, have %d", g->name, g->size.min.y, y);
5589a747e4fSDavid du Colombier 		r.max.y = r.min.y + g->size.min.y;
5599a747e4fSDavid du Colombier 		y = Dy(r);
5609a747e4fSDavid du Colombier 	}
5619a747e4fSDavid du Colombier 	d = ctlmalloc(g->nkids*sizeof(int));
5629a747e4fSDavid du Colombier 	p = ctlmalloc(g->nkids*sizeof(int));
5639a747e4fSDavid du Colombier 	if(debug) fprint(2, "kiddies: ");
5649a747e4fSDavid du Colombier 	for (i = 0; i < g->nkids; i++) {
5659a747e4fSDavid du Colombier 		q = g->kids[i];
5669a747e4fSDavid du Colombier 		if(debug) fprint(2, "[%q]: %d⋯%d\t", q->name, q->size.min.y, q->size.max.y);
5679a747e4fSDavid du Colombier 		d[i] = q->size.min.y;
5689a747e4fSDavid du Colombier 		y -= d[i];
5699a747e4fSDavid du Colombier 		p[i] = q->size.max.y - q->size.min.y;
5709a747e4fSDavid du Colombier 	}
5719a747e4fSDavid du Colombier 	if(debug) fprint(2, "\n");
5729a747e4fSDavid du Colombier 	y -= (g->nkids-1) * g->separation;
5739a747e4fSDavid du Colombier 	if(y < 0){
5749a747e4fSDavid du Colombier 		if (debug) fprint(2, "columnresize: y == %d\n", y);
5759a747e4fSDavid du Colombier 		y = 0;
5769a747e4fSDavid du Colombier 	}
5779a747e4fSDavid du Colombier 	if (y >= g->size.max.y - g->size.min.y) {
5789a747e4fSDavid du Colombier 		// all rects can be maximum width
5799a747e4fSDavid du Colombier 		for (i = 0; i < g->nkids; i++)
5809a747e4fSDavid du Colombier 			d[i] += p[i];
5819a747e4fSDavid du Colombier 		y -= g->size.max.y - g->size.min.y;
5829a747e4fSDavid du Colombier 	} else {
5839a747e4fSDavid du Colombier 		// rects can't be max width, divide up the rest
5849a747e4fSDavid du Colombier 		j = y;
5859a747e4fSDavid du Colombier 		for (i = 0; i < g->nkids; i++) {
5869a747e4fSDavid du Colombier 			t = p[i] * y/(g->size.max.y - g->size.min.y);
5879a747e4fSDavid du Colombier 			d[i] += t;
5889a747e4fSDavid du Colombier 			j -= t;
5899a747e4fSDavid du Colombier 		}
5909a747e4fSDavid du Colombier 		d[0] += j;
5919a747e4fSDavid du Colombier 		y = 0;
5929a747e4fSDavid du Colombier 	}
5939a747e4fSDavid du Colombier 	g->nseparators = g->nkids-1;
5949a747e4fSDavid du Colombier 	g->separators = realloc(g->separators, g->nseparators*sizeof(Rectangle));
5959a747e4fSDavid du Colombier 	j = 0;
5969a747e4fSDavid du Colombier 	rr = r;
5979a747e4fSDavid du Colombier 	for (i = 0; i < g->nkids; i++) {
5989a747e4fSDavid du Colombier 		q = g->kids[i];
5999a747e4fSDavid du Colombier 		if (i < g->nkids - 1){
6009a747e4fSDavid du Colombier 			g->separators[i].min.x = r.min.x;
6019a747e4fSDavid du Colombier 			g->separators[i].max.x = r.max.x;
6029a747e4fSDavid du Colombier 		}
6039a747e4fSDavid du Colombier 		t = y / (g->nkids - i);
6049a747e4fSDavid du Colombier 		y -= t;
6059a747e4fSDavid du Colombier 		j += t/2;
6069a747e4fSDavid du Colombier 		rr.min.y = r.min.y + j;
6079a747e4fSDavid du Colombier 		if (i)
6089a747e4fSDavid du Colombier 			g->separators[i-1].max.y = rr.min.y;
6099a747e4fSDavid du Colombier 		j += d[i];
6109a747e4fSDavid du Colombier 		rr.max.y = r.min.y + j;
6119a747e4fSDavid du Colombier 		if (i < g->nkids - 1)
6129a747e4fSDavid du Colombier 			g->separators[i].min.y = rr.max.y;
6139a747e4fSDavid du Colombier 		j += g->separation + t - t/2;
6149a747e4fSDavid du Colombier 		_ctlprint(q, "rect %R", rr);
6159a747e4fSDavid du Colombier 		if(debug) fprint(2, "	%d %q: %R (%d×%d)\n", i, q->name, rr, Dx(rr), Dy(rr));
6169a747e4fSDavid du Colombier 	}
6179a747e4fSDavid du Colombier 	free(d);
6189a747e4fSDavid du Colombier 	free(p);
6199a747e4fSDavid du Colombier }
6209a747e4fSDavid du Colombier 
6219a747e4fSDavid du Colombier static void
rowresize(Group * g,Rectangle r)6229a747e4fSDavid du Colombier rowresize(Group *g, Rectangle r)
6239a747e4fSDavid du Colombier {
6249a747e4fSDavid du Colombier 	int x, y, *d, *p, i, j, t;
6259a747e4fSDavid du Colombier 	Rectangle rr;
6269a747e4fSDavid du Colombier 	Control *q;
6279a747e4fSDavid du Colombier 
6289a747e4fSDavid du Colombier 	x = Dx(r);
6299a747e4fSDavid du Colombier 	y = Dy(r);
6309a747e4fSDavid du Colombier 	if(debug) fprint(2, "rowresize %q %R (%d×%d), separation %d\n", g->name, r, Dx(r), Dy(r), g->separation);
6319a747e4fSDavid du Colombier 	if (x < g->size.min.x) {
6329a747e4fSDavid du Colombier 		werrstr("resize %s: too narrow: need %d, have %d", g->name, g->size.min.x, x);
6339a747e4fSDavid du Colombier 		r.max.x = r.min.x + g->size.min.x;
6349a747e4fSDavid du Colombier 		x = Dx(r);
6359a747e4fSDavid du Colombier 	}
6369a747e4fSDavid du Colombier 	if (y < g->size.min.y) {
6379a747e4fSDavid du Colombier 		werrstr("resize %s: too short: need %d, have %d", g->name, g->size.min.y, y);
6389a747e4fSDavid du Colombier 		r.max.y = r.min.y + g->size.min.y;
6399a747e4fSDavid du Colombier 	}
6409a747e4fSDavid du Colombier 	d = ctlmalloc(g->nkids*sizeof(int));
6419a747e4fSDavid du Colombier 	p = ctlmalloc(g->nkids*sizeof(int));
6429a747e4fSDavid du Colombier 	if(debug) fprint(2, "kiddies: ");
6439a747e4fSDavid du Colombier 	for (i = 0; i < g->nkids; i++) {
6449a747e4fSDavid du Colombier 		q = g->kids[i];
6459a747e4fSDavid du Colombier 		if(debug) fprint(2, "[%q]: %d⋯%d\t", q->name, q->size.min.x, q->size.max.x);
6469a747e4fSDavid du Colombier 		d[i] = q->size.min.x;
6479a747e4fSDavid du Colombier 		x -= d[i];
6489a747e4fSDavid du Colombier 		p[i] = q->size.max.x - q->size.min.x;
6499a747e4fSDavid du Colombier 	}
6509a747e4fSDavid du Colombier 	if(debug) fprint(2, "\n");
6519a747e4fSDavid du Colombier 	x -= (g->nkids-1) * g->separation;
6529a747e4fSDavid du Colombier 	if(x < 0){
6539a747e4fSDavid du Colombier 		if (debug) fprint(2, "rowresize: x == %d\n", x);
6549a747e4fSDavid du Colombier 		x = 0;
6559a747e4fSDavid du Colombier 	}
6569a747e4fSDavid du Colombier 	if (x >= g->size.max.x - g->size.min.x) {
6579a747e4fSDavid du Colombier 		if (debug) fprint(2, "max: %d > %d - %d", x, g->size.max.x, g->size.min.x);
6589a747e4fSDavid du Colombier 		// all rects can be maximum width
6599a747e4fSDavid du Colombier 		for (i = 0; i < g->nkids; i++)
6609a747e4fSDavid du Colombier 			d[i] += p[i];
6619a747e4fSDavid du Colombier 		x -= g->size.max.x - g->size.min.x;
6629a747e4fSDavid du Colombier 	} else {
6639a747e4fSDavid du Colombier 		if (debug) fprint(2, "divvie up: %d < %d - %d", x, g->size.max.x, g->size.min.x);
6649a747e4fSDavid du Colombier 		// rects can't be max width, divide up the rest
6659a747e4fSDavid du Colombier 		j = x;
6669a747e4fSDavid du Colombier 		for (i = 0; i < g->nkids; i++) {
6679a747e4fSDavid du Colombier 			t = p[i] * x/(g->size.max.x - g->size.min.x);
6689a747e4fSDavid du Colombier 			d[i] += t;
6699a747e4fSDavid du Colombier 			j -= t;
6709a747e4fSDavid du Colombier 		}
6719a747e4fSDavid du Colombier 		d[0] += j;
6729a747e4fSDavid du Colombier 		x = 0;
6739a747e4fSDavid du Colombier 	}
6749a747e4fSDavid du Colombier 	j = 0;
6759a747e4fSDavid du Colombier 	g->nseparators = g->nkids-1;
6769a747e4fSDavid du Colombier 	g->separators = realloc(g->separators, g->nseparators*sizeof(Rectangle));
6779a747e4fSDavid du Colombier 	rr = r;
6789a747e4fSDavid du Colombier 	for (i = 0; i < g->nkids; i++) {
6799a747e4fSDavid du Colombier 		q = g->kids[i];
6809a747e4fSDavid du Colombier 		if (i < g->nkids - 1){
6819a747e4fSDavid du Colombier 			g->separators[i].min.y = r.min.y;
6829a747e4fSDavid du Colombier 			g->separators[i].max.y = r.max.y;
6839a747e4fSDavid du Colombier 		}
6849a747e4fSDavid du Colombier 		t = x / (g->nkids - i);
6859a747e4fSDavid du Colombier 		x -= t;
6869a747e4fSDavid du Colombier 		j += t/2;
6879a747e4fSDavid du Colombier 		rr.min.x = r.min.x + j;
6889a747e4fSDavid du Colombier 		if (i)
6899a747e4fSDavid du Colombier 			g->separators[i-1].max.x = rr.min.x;
6909a747e4fSDavid du Colombier 		j += d[i];
6919a747e4fSDavid du Colombier 		rr.max.x = r.min.x + j;
6929a747e4fSDavid du Colombier 		if (i < g->nkids - 1)
6939a747e4fSDavid du Colombier 			g->separators[i].min.x = rr.max.x;
6949a747e4fSDavid du Colombier 		j += g->separation + t - t/2;
6959a747e4fSDavid du Colombier 		_ctlprint(q, "rect %R", rr);
6969a747e4fSDavid du Colombier 		if(debug) fprint(2, "	%d %q: %R (%d×%d)\n", i, q->name, rr, Dx(rr), Dy(rr));
6979a747e4fSDavid du Colombier 	}
6989a747e4fSDavid du Colombier 	free(d);
6999a747e4fSDavid du Colombier 	free(p);
7009a747e4fSDavid du Colombier }
7019a747e4fSDavid du Colombier 
7029a747e4fSDavid du Colombier static void
stackresize(Group * g,Rectangle r)7039a747e4fSDavid du Colombier stackresize(Group *g, Rectangle r)
7049a747e4fSDavid du Colombier {
7059a747e4fSDavid du Colombier 	int x, y, i;
7069a747e4fSDavid du Colombier 	Control *q;
7079a747e4fSDavid du Colombier 
7089a747e4fSDavid du Colombier 	x = Dx(r);
7099a747e4fSDavid du Colombier 	y = Dy(r);
7109a747e4fSDavid du Colombier 	if(debug) fprint(2, "stackresize %q %R (%d×%d)\n", g->name, r, Dx(r), Dy(r));
7119a747e4fSDavid du Colombier 	if (x < g->size.min.x){
7129a747e4fSDavid du Colombier 		werrstr("resize %s: too narrow: need %d, have %d", g->name, g->size.min.x, x);
7139a747e4fSDavid du Colombier 		return;
7149a747e4fSDavid du Colombier 	}
7159a747e4fSDavid du Colombier 	if (y < g->size.min.y){
7169a747e4fSDavid du Colombier 		werrstr("resize %s: too short: need %d, have %d", g->name, g->size.min.y, y);
7179a747e4fSDavid du Colombier 		return;
7189a747e4fSDavid du Colombier 	}
7199a747e4fSDavid du Colombier 	if (x > g->size.max.x) {
7209a747e4fSDavid du Colombier 		x = (x - g->size.max.x)/2;
7219a747e4fSDavid du Colombier 		r.min.x += x;
7229a747e4fSDavid du Colombier 		r.max.x -= x;
7239a747e4fSDavid du Colombier 	}
7249a747e4fSDavid du Colombier 	if (y > g->size.max.y) {
7259a747e4fSDavid du Colombier 		y = (y - g->size.max.y)/2;
7269a747e4fSDavid du Colombier 		r.min.y += y;
7279a747e4fSDavid du Colombier 		r.max.y -= y;
7289a747e4fSDavid du Colombier 	}
7299a747e4fSDavid du Colombier 	for (i = 0; i < g->nkids; i++){
7309a747e4fSDavid du Colombier 		q = g->kids[i];
7319a747e4fSDavid du Colombier 		if(debug) fprint(2, "	%d %q: %R (%d×%d)\n", i, q->name, r, Dx(r), Dy(r));
7329a747e4fSDavid du Colombier 	}
7339a747e4fSDavid du Colombier 	for (i = 0; i < g->nkids; i++){
7349a747e4fSDavid du Colombier 		q = g->kids[i];
7359a747e4fSDavid du Colombier 		_ctlprint(q, "rect %R", r);
7369a747e4fSDavid du Colombier 	}
7379a747e4fSDavid du Colombier }
738