11 static int debugm = 0;
12 static int debugr = 0;
29 static char *cmds[] = {
32 [EBordercolor] = "bordercolor",
39 [ESeparation] = "separation",
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);
57 g->bordercolor = _getctlimage("black");
58 g->image = _getctlimage("white");
69 g->mouse = groupmouse;
74 groupctl(Control *c, CParse *cp)
82 cmd = _ctllookup(cp->args[0], cmds, nelem(cmds));
85 for (i = 1; i < cp->nargs; i++){
86 c = controlcalled(cp->args[i]);
88 ctlerror("%q: no such control: %s", g->name, cp->args[i]);
92 g->setsize((Control*)g);
95 _ctlargcount(g, cp, 2);
97 ctlerror("%q: bad border: %c", g->name, cp->str);
98 g->border = cp->iargs[1];
101 _ctlargcount(g, cp, 2);
102 _setctlimage(g, &g->bordercolor, cp->args[1]);
105 /* ignore focus change */
108 _ctlargcount(g, cp, 1);
109 for (i = 0; i < g->nkids; i++)
111 _ctlprint(g->kids[i], "hide");
115 _ctlargcount(g, cp, 2);
116 _setctlimage(g, &g->image, cp->args[1]);
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);
127 r = insetrect(r, g->border);
146 _ctlargcount(g, cp, 2);
147 for (n = 0; n < g->nkids; n++)
148 if (strcmp(cp->args[1], g->kids[n]->name) == 0)
151 ctlerror("%s: remove nonexistent control: %q", g->name, cp->args[1]);
154 g->setsize((Control*)g);
158 if (debugr) fprint(2, "reveal %s\n", g->name);
159 if (g->type == Ctlstack){
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];
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");
172 if (debugr) fprint(2, "reveal %s: hide kid %s\n", g->name, g->kids[i]->name);
173 _ctlprint(g->kids[i], "hide");
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++)
186 _ctlprint(g->kids[i], "reveal");
189 _ctlargcount(g, cp, 1);
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");
203 flushimage(display, 1);
206 r.max = Pt(_Ctlmaxsize, _Ctlmaxsize);
207 if (g->type == Ctlboxbox)
208 _ctlargcount(g, cp, 5);
211 ctlerror("%s: args of %q", g->name, cp->str);
213 /* recursively set size */
216 g->setsize((Control*)g);
219 _ctlargcount(g, cp, 5);
220 r.max.x = cp->iargs[3];
221 r.max.y = cp->iargs[4];
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);
234 if (g->type != Ctlstack){
235 _ctlargcount(g, cp, 2);
237 ctlerror("%q: illegal value: %c", g->name, cp->str);
238 g->separation = cp->iargs[1];
241 // fall through for Ctlstack
243 ctlerror("%q: unrecognized message '%s'", g->name, cp->str);
249 groupfree(Control *c)
254 _putctlimage(g->bordercolor);
259 groupmouse(Control *c, Mouse *m)
265 if (g->type == Ctlstack){
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);
277 g->lastbut = m->buttons;
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);
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);
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);
304 g->lastkid = lastkid;
305 g->lastbut = m->buttons;
313 if(g->lastbut == 0 && m->buttons != 0){
314 /* button went down, start tracking border */
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);
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);
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 */
342 border(i, r, Selborder, red, ZP);
343 flushimage(display, 1);
346 } else if (g->lastbut != 0 && s->m.buttons == 0){
347 /* button went up, resize kiddies */
349 g->lastbut = s->m.buttons;
354 activategroup(Control *c, int act)
360 for (i = 0; i < g->nkids; i++)
362 activate(g->kids[i]);
364 deactivate(g->kids[i]);
368 createrow(Controlset *cs, char *name)
371 c = _createctl(cs, "row", sizeof(Group), name);
372 groupinit((Group*)c);
373 c->setsize = groupsize;
374 c->activate = activategroup;
379 createcolumn(Controlset *cs, char *name)
382 c = _createctl(cs, "column", sizeof(Group), name);
383 groupinit((Group*)c);
384 c->setsize = groupsize;
385 c->activate = activategroup;
390 createboxbox(Controlset *cs, char *name)
393 c = _createctl(cs, "boxbox", sizeof(Group), name);
394 groupinit((Group*)c);
395 c->activate = activategroup;
400 createstack(Controlset *cs, char *name)
403 c = _createctl(cs, "stack", sizeof(Group), name);
404 groupinit((Group*)c);
405 c->setsize = groupsize;
410 _ctladdgroup(Control *c, Control *q)
412 Group *g = (Group*)c;
414 g->kids = ctlrealloc(g->kids, sizeof(Group*)*(g->nkids+1));
415 g->kids[g->nkids++] = q;
419 removegroup(Group *g, int n)
423 if (g->selected == n)
425 else if (g->selected > n)
428 for (i = n+1; i < g->nkids; i++)
429 g->kids[i-1] = g->kids[i];
434 groupsize(Control *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++){
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);
456 r.min.x += q->size.min.x + g->border;
458 r.min.x = q->size.min.x;
460 r.max.x += q->size.max.x + g->border;
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;
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;
470 r.min.y += q->size.min.y + g->border;
472 r.min.y = q->size.min.y;
474 r.max.y += q->size.max.y + g->border;
476 r.max.y = q->size.max.y;
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;
486 g->size = rectaddpt(r, Pt(g->border, g->border));
487 if (debug) fprint(2, "groupsize %q: %R\n", g->name, g->size);
491 boxboxresize(Group *g, Rectangle r)
493 int rows, cols, ht, wid, i, hpad, wpad;
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);
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;
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;
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;
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))));
526 g->nseparators = rows + cols - 2;
527 g->separators = realloc(g->separators, g->nseparators*sizeof(Rectangle));
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)));
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)));
543 columnresize(Group *g, Rectangle r)
545 int x, y, *d, *p, i, j, t;
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;
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;
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++) {
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;
569 p[i] = q->size.max.y - q->size.min.y;
571 if(debug) fprint(2, "\n");
572 y -= (g->nkids-1) * g->separation;
574 if (debug) fprint(2, "columnresize: y == %d\n", y);
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++)
581 y -= g->size.max.y - g->size.min.y;
583 // rects can't be max width, divide up the rest
585 for (i = 0; i < g->nkids; i++) {
586 t = p[i] * y/(g->size.max.y - g->size.min.y);
593 g->nseparators = g->nkids-1;
594 g->separators = realloc(g->separators, g->nseparators*sizeof(Rectangle));
597 for (i = 0; i < g->nkids; 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;
603 t = y / (g->nkids - i);
606 rr.min.y = r.min.y + j;
608 g->separators[i-1].max.y = rr.min.y;
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));
622 rowresize(Group *g, Rectangle r)
624 int x, y, *d, *p, i, j, t;
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;
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;
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++) {
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;
648 p[i] = q->size.max.x - q->size.min.x;
650 if(debug) fprint(2, "\n");
651 x -= (g->nkids-1) * g->separation;
653 if (debug) fprint(2, "rowresize: x == %d\n", x);
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++)
661 x -= g->size.max.x - g->size.min.x;
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
666 for (i = 0; i < g->nkids; i++) {
667 t = p[i] * x/(g->size.max.x - g->size.min.x);
675 g->nseparators = g->nkids-1;
676 g->separators = realloc(g->separators, g->nseparators*sizeof(Rectangle));
678 for (i = 0; i < g->nkids; 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;
684 t = x / (g->nkids - i);
687 rr.min.x = r.min.x + j;
689 g->separators[i-1].max.x = rr.min.x;
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));
703 stackresize(Group *g, Rectangle 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);
715 if (y < g->size.min.y){
716 werrstr("resize %s: too short: need %d, have %d", g->name, g->size.min.y, y);
719 if (x > g->size.max.x) {
720 x = (x - g->size.max.x)/2;
724 if (y > g->size.max.y) {
725 y = (y - g->size.max.y)/2;
729 for (i = 0; i < g->nkids; i++){
731 if(debug) fprint(2, " %d %q: %R (%d×%d)\n", i, q->name, r, Dx(r), Dy(r));
733 for (i = 0; i < g->nkids; i++){
735 _ctlprint(q, "rect %R", r);