747 lines
19 KiB
C
747 lines
19 KiB
C
/*
|
||
* This file is part of the UCB release of Plan 9. It is subject to the license
|
||
* terms in the LICENSE file found in the top-level directory of this
|
||
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
|
||
* part of the UCB release of Plan 9, including this file, may be copied,
|
||
* modified, propagated, or distributed except according to the terms contained
|
||
* in the LICENSE file.
|
||
*/
|
||
|
||
#include <u.h>
|
||
#include <libc.h>
|
||
#include <thread.h>
|
||
#include <draw.h>
|
||
#include <mouse.h>
|
||
#include <keyboard.h>
|
||
#include <control.h>
|
||
#include "group.h"
|
||
|
||
static int debug = 0;
|
||
static int debugm = 0;
|
||
static int debugr = 0;
|
||
|
||
enum{
|
||
EAdd,
|
||
EBorder,
|
||
EBordercolor,
|
||
EFocus,
|
||
EHide,
|
||
EImage,
|
||
ERect,
|
||
ERemove,
|
||
EReveal,
|
||
ESeparation,
|
||
EShow,
|
||
ESize,
|
||
};
|
||
|
||
static char *cmds[] = {
|
||
[EAdd] = "add",
|
||
[EBorder] = "border",
|
||
[EBordercolor] = "bordercolor",
|
||
[EFocus] = "focus",
|
||
[EHide] = "hide",
|
||
[EImage] = "image",
|
||
[ERect] = "rect",
|
||
[ERemove] = "remove",
|
||
[EReveal] = "reveal",
|
||
[ESeparation] = "separation",
|
||
[EShow] = "show",
|
||
[ESize] = "size",
|
||
};
|
||
|
||
static void boxboxresize(Group*, Rectangle);
|
||
static void columnresize(Group*, Rectangle);
|
||
static void groupctl(Control *c, CParse *cp);
|
||
static void groupfree(Control*);
|
||
static void groupmouse(Control *, Mouse *);
|
||
static void groupsize(Control *c);
|
||
static void removegroup(Group*, int);
|
||
static void rowresize(Group*, Rectangle);
|
||
static void stackresize(Group*, Rectangle);
|
||
|
||
static void
|
||
groupinit(Group *g)
|
||
{
|
||
g->bordercolor = _getctlimage("black");
|
||
g->image = _getctlimage("white");
|
||
g->border = 0;
|
||
g->mansize = 0;
|
||
g->separation = 0;
|
||
g->selected = -1;
|
||
g->lastkid = -1;
|
||
g->kids = nil;
|
||
g->separators = nil;
|
||
g->nkids = 0;
|
||
g->nseparators = 0;
|
||
g->ctl = groupctl;
|
||
g->mouse = groupmouse;
|
||
g->exit = groupfree;
|
||
}
|
||
|
||
static void
|
||
groupctl(Control *c, CParse *cp)
|
||
{
|
||
int cmd, i, n;
|
||
|
||
Rectangle r;
|
||
Group *g;
|
||
|
||
g = (Group*)c;
|
||
cmd = _ctllookup(cp->args[0], cmds, nelem(cmds));
|
||
switch(cmd){
|
||
case EAdd:
|
||
for (i = 1; i < cp->nargs; i++){
|
||
c = controlcalled(cp->args[i]);
|
||
if (c == nil)
|
||
ctlerror("%q: no such control: %s", g->name, cp->args[i]);
|
||
_ctladdgroup(g, c);
|
||
}
|
||
if (g->setsize)
|
||
g->setsize((Control*)g);
|
||
break;
|
||
case EBorder:
|
||
_ctlargcount(g, cp, 2);
|
||
if(cp->iargs[1] < 0)
|
||
ctlerror("%q: bad border: %c", g->name, cp->str);
|
||
g->border = cp->iargs[1];
|
||
break;
|
||
case EBordercolor:
|
||
_ctlargcount(g, cp, 2);
|
||
_setctlimage(g, &g->bordercolor, cp->args[1]);
|
||
break;
|
||
case EFocus:
|
||
/* ignore focus change */
|
||
break;
|
||
case EHide:
|
||
_ctlargcount(g, cp, 1);
|
||
for (i = 0; i < g->nkids; i++)
|
||
if (g->kids[i]->ctl)
|
||
_ctlprint(g->kids[i], "hide");
|
||
g->hidden = 1;
|
||
break;
|
||
case EImage:
|
||
_ctlargcount(g, cp, 2);
|
||
_setctlimage(g, &g->image, cp->args[1]);
|
||
break;
|
||
case ERect:
|
||
_ctlargcount(g, cp, 5);
|
||
r.min.x = cp->iargs[1];
|
||
r.min.y = cp->iargs[2];
|
||
r.max.x = cp->iargs[3];
|
||
r.max.y = cp->iargs[4];
|
||
if(Dx(r)<=0 || Dy(r)<=0)
|
||
ctlerror("%q: bad rectangle: %s", g->name, cp->str);
|
||
g->rect = r;
|
||
r = insetrect(r, g->border);
|
||
if (g->nkids == 0)
|
||
return;
|
||
switch(g->type){
|
||
case Ctlboxbox:
|
||
boxboxresize(g, r);
|
||
break;
|
||
case Ctlcolumn:
|
||
columnresize(g, r);
|
||
break;
|
||
case Ctlrow:
|
||
rowresize(g, r);
|
||
break;
|
||
case Ctlstack:
|
||
stackresize(g, r);
|
||
break;
|
||
}
|
||
break;
|
||
case ERemove:
|
||
_ctlargcount(g, cp, 2);
|
||
for (n = 0; n < g->nkids; n++)
|
||
if (strcmp(cp->args[1], g->kids[n]->name) == 0)
|
||
break;
|
||
if (n == g->nkids)
|
||
ctlerror("%s: remove nonexistent control: %q", g->name, cp->args[1]);
|
||
removegroup(g, n);
|
||
if (g->setsize)
|
||
g->setsize((Control*)g);
|
||
break;
|
||
case EReveal:
|
||
g->hidden = 0;
|
||
if (debugr) fprint(2, "reveal %s\n", g->name);
|
||
if (g->type == Ctlstack){
|
||
if (cp->nargs == 2){
|
||
if (cp->iargs[1] < 0 || cp->iargs[1] >= g->nkids)
|
||
ctlerror("%s: control out of range: %q", g->name, cp->str);
|
||
g->selected = cp->iargs[1];
|
||
}else
|
||
_ctlargcount(g, cp, 1);
|
||
for (i = 0; i < g->nkids; i++)
|
||
if (g->kids[i]->ctl){
|
||
if (g->selected == i){
|
||
if (debugr) fprint(2, "reveal %s: reveal kid %s\n", g->name, g->kids[i]->name);
|
||
_ctlprint(g->kids[i], "reveal");
|
||
}else{
|
||
if (debugr) fprint(2, "reveal %s: hide kid %s\n", g->name, g->kids[i]->name);
|
||
_ctlprint(g->kids[i], "hide");
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
_ctlargcount(g, cp, 1);
|
||
if (debug) fprint(2, "reveal %s: border %R/%d\n", g->name, g->rect, g->border);
|
||
border(g->screen, g->rect, g->border, g->bordercolor->image, g->bordercolor->image->r.min);
|
||
r = insetrect(g->rect, g->border);
|
||
if (debug) fprint(2, "reveal %s: draw %R\n", g->name, r);
|
||
draw(g->screen, r, g->image->image, nil, g->image->image->r.min);
|
||
for (i = 0; i < g->nkids; i++)
|
||
if (g->kids[i]->ctl)
|
||
_ctlprint(g->kids[i], "reveal");
|
||
break;
|
||
case EShow:
|
||
_ctlargcount(g, cp, 1);
|
||
if (g->hidden)
|
||
break;
|
||
// pass it on to the kiddies
|
||
if (debug) fprint(2, "show %s: border %R/%d\n", g->name, g->rect, g->border);
|
||
border(g->screen, g->rect, g->border, g->bordercolor->image, g->bordercolor->image->r.min);
|
||
r = insetrect(g->rect, g->border);
|
||
if (debug) fprint(2, "show %s: draw %R\n", g->name, r);
|
||
draw(g->screen, r, g->image->image, nil, g->image->image->r.min);
|
||
for (i = 0; i < g->nkids; i++)
|
||
if (g->kids[i]->ctl){
|
||
if (debug) fprint(2, "show %s: kid %s: %q\n", g->name, g->kids[i]->name, cp->str);
|
||
_ctlprint(g->kids[i], "show");
|
||
}
|
||
flushimage(display, 1);
|
||
break;
|
||
case ESize:
|
||
r.max = Pt(_Ctlmaxsize, _Ctlmaxsize);
|
||
if (g->type == Ctlboxbox)
|
||
_ctlargcount(g, cp, 5);
|
||
switch(cp->nargs){
|
||
default:
|
||
ctlerror("%s: args of %q", g->name, cp->str);
|
||
case 1:
|
||
/* recursively set size */
|
||
g->mansize = 0;
|
||
if (g->setsize)
|
||
g->setsize((Control*)g);
|
||
break;
|
||
case 5:
|
||
_ctlargcount(g, cp, 5);
|
||
r.max.x = cp->iargs[3];
|
||
r.max.y = cp->iargs[4];
|
||
/* fall through */
|
||
case 3:
|
||
r.min.x = cp->iargs[1];
|
||
r.min.y = cp->iargs[2];
|
||
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)
|
||
ctlerror("%q: bad sizes: %s", g->name, cp->str);
|
||
g->size = r;
|
||
g->mansize = 1;
|
||
break;
|
||
}
|
||
break;
|
||
case ESeparation:
|
||
if (g->type != Ctlstack){
|
||
_ctlargcount(g, cp, 2);
|
||
if(cp->iargs[1] < 0)
|
||
ctlerror("%q: illegal value: %c", g->name, cp->str);
|
||
g->separation = cp->iargs[1];
|
||
break;
|
||
}
|
||
// fall through for Ctlstack
|
||
default:
|
||
ctlerror("%q: unrecognized message '%s'", g->name, cp->str);
|
||
break;
|
||
}
|
||
}
|
||
|
||
static void
|
||
groupfree(Control *c)
|
||
{
|
||
Group *g;
|
||
|
||
g = (Group*)c;
|
||
_putctlimage(g->bordercolor);
|
||
free(g->kids);
|
||
}
|
||
|
||
static void
|
||
groupmouse(Control *c, Mouse *m)
|
||
{
|
||
Group *g;
|
||
int i, lastkid;
|
||
|
||
g = (Group*)c;
|
||
if (g->type == Ctlstack){
|
||
i = g->selected;
|
||
if (i >= 0 && g->kids[i]->mouse &&
|
||
( ( ((m->buttons == 0) || (g->lastbut == 0)) &&
|
||
ptinrect(m->xy, g->kids[i]->rect) ) ||
|
||
( ((m->buttons != 0) || (g->lastbut != 0)) &&
|
||
(g->lastkid == i) ) ) ) {
|
||
if (debugm) fprint(2, "groupmouse %s mouse kid %s i=%d lastkid=%d buttons=%d lastbut=%d inrect=%d\n",
|
||
g->name, g->kids[i]->name, i, g->lastkid, m->buttons, g->lastbut,
|
||
ptinrect(m->xy, g->kids[i]->rect) ? 1 : 0);
|
||
(g->kids[i]->mouse)(g->kids[i], m);
|
||
g->lastkid = i;
|
||
g->lastbut = m->buttons;
|
||
} else {
|
||
if (debugm) fprint(2, "groupmouse %s skip kid %s i=%d lastkid=%d buttons=%d lastbut=%d inrect=%d\n",
|
||
g->name, g->kids[i]->name, i, g->lastkid, m->buttons, g->lastbut,
|
||
ptinrect(m->xy, g->kids[i]->rect) ? 1 : 0);
|
||
}
|
||
return;
|
||
}
|
||
|
||
lastkid = -1;
|
||
for(i=0; i<g->nkids; i++) {
|
||
if(g->kids[i]->mouse &&
|
||
( ( ((m->buttons == 0) || (g->lastbut == 0)) &&
|
||
ptinrect(m->xy, g->kids[i]->rect) ) ||
|
||
( ((m->buttons != 0) || (g->lastbut != 0)) &&
|
||
(g->lastkid == i) ) ) ) {
|
||
if (debugm) fprint(2, "groupmouse %s mouse kid %s i=%d lastkid=%d buttons=%d lastbut=%d inrect=%d\n",
|
||
g->name, g->kids[i]->name, i, g->lastkid, m->buttons, g->lastbut,
|
||
ptinrect(m->xy, g->kids[i]->rect) ? 1 : 0);
|
||
(g->kids[i]->mouse)(g->kids[i], m);
|
||
lastkid = i;
|
||
} else {
|
||
if (debugm) fprint(2, "groupmouse %s skip kid %s i=%d lastkid=%d buttons=%d lastbut=%d inrect=%d\n",
|
||
g->name, g->kids[i]->name, i, g->lastkid, m->buttons, g->lastbut,
|
||
ptinrect(m->xy, g->kids[i]->rect) ? 1 : 0);
|
||
}
|
||
}
|
||
g->lastkid = lastkid;
|
||
g->lastbut = m->buttons;
|
||
|
||
#ifdef notdef
|
||
if(m->buttons == 0){
|
||
/* buttons now up */
|
||
g->lastbut = 0;
|
||
return;
|
||
}
|
||
if(g->lastbut == 0 && m->buttons != 0){
|
||
/* button went down, start tracking border */
|
||
switch(g->stacking){
|
||
default:
|
||
return;
|
||
case Vertical:
|
||
p = Pt(m->xy.x, middle_of_border.y);
|
||
p0 = Pt(g->r.min.x, m->xy.y);
|
||
p1 = Pt(g->r.max.x, m->xy.y);
|
||
break;
|
||
case Horizontal:
|
||
p = Pt(middle_of_border.x, m->xy.y);
|
||
p0 = Pt(m->xy.x, g->r.min.y);
|
||
p1 = Pt(m->xy.x, g->r.max.y);
|
||
break;
|
||
}
|
||
// setcursor();
|
||
oi = nil;
|
||
} else if (g->lastbut != 0 && s->m.buttons != 0){
|
||
/* button is down, keep tracking border */
|
||
if(!eqpt(s->m.xy, p)){
|
||
p = onscreen(s->m.xy);
|
||
r = canonrect(Rpt(p0, p));
|
||
if(Dx(r)>5 && Dy(r)>5){
|
||
i = allocwindow(wscreen, r, Refnone, 0xEEEEEEFF); /* grey */
|
||
freeimage(oi);
|
||
if(i == nil)
|
||
goto Rescue;
|
||
oi = i;
|
||
border(i, r, Selborder, red, ZP);
|
||
flushimage(display, 1);
|
||
}
|
||
}
|
||
} else if (g->lastbut != 0 && s->m.buttons == 0){
|
||
/* button went up, resize kiddies */
|
||
}
|
||
g->lastbut = s->m.buttons;
|
||
#endif
|
||
}
|
||
|
||
static void
|
||
activategroup(Control *c, int act)
|
||
{
|
||
int i;
|
||
Group *g;
|
||
|
||
g = (Group*)c;
|
||
for (i = 0; i < g->nkids; i++)
|
||
if (act)
|
||
activate(g->kids[i]);
|
||
else
|
||
deactivate(g->kids[i]);
|
||
}
|
||
|
||
Control *
|
||
createrow(Controlset *cs, char *name)
|
||
{
|
||
Control *c;
|
||
c = _createctl(cs, "row", sizeof(Group), name);
|
||
groupinit((Group*)c);
|
||
c->setsize = groupsize;
|
||
c->activate = activategroup;
|
||
return c;
|
||
}
|
||
|
||
Control *
|
||
createcolumn(Controlset *cs, char *name)
|
||
{
|
||
Control *c;
|
||
c = _createctl(cs, "column", sizeof(Group), name);
|
||
groupinit((Group*)c);
|
||
c->setsize = groupsize;
|
||
c->activate = activategroup;
|
||
return c;
|
||
}
|
||
|
||
Control *
|
||
createboxbox(Controlset *cs, char *name)
|
||
{
|
||
Control *c;
|
||
c = _createctl(cs, "boxbox", sizeof(Group), name);
|
||
groupinit((Group*)c);
|
||
c->activate = activategroup;
|
||
return c;
|
||
}
|
||
|
||
Control *
|
||
createstack(Controlset *cs, char *name)
|
||
{
|
||
Control *c;
|
||
c = _createctl(cs, "stack", sizeof(Group), name);
|
||
groupinit((Group*)c);
|
||
c->setsize = groupsize;
|
||
return c;
|
||
}
|
||
|
||
void
|
||
_ctladdgroup(Control *c, Control *q)
|
||
{
|
||
Group *g = (Group*)c;
|
||
|
||
g->kids = ctlrealloc(g->kids, sizeof(Group*)*(g->nkids+1));
|
||
g->kids[g->nkids++] = q;
|
||
}
|
||
|
||
static void
|
||
removegroup(Group *g, int n)
|
||
{
|
||
int i;
|
||
|
||
if (g->selected == n)
|
||
g->selected = -1;
|
||
else if (g->selected > n)
|
||
g->selected--;
|
||
|
||
for (i = n+1; i < g->nkids; i++)
|
||
g->kids[i-1] = g->kids[i];
|
||
g->nkids--;
|
||
}
|
||
|
||
static void
|
||
groupsize(Control *c)
|
||
{
|
||
Rectangle r;
|
||
int i;
|
||
Control *q;
|
||
Group *g;
|
||
|
||
g = (Group*)c;
|
||
assert(g->type == Ctlcolumn || g->type == Ctlrow || g->type == Ctlstack);
|
||
if (g->mansize) return;
|
||
r = Rect(1, 1, 1, 1);
|
||
if (debug) fprint(2, "groupsize %q\n", g->name);
|
||
for (i = 0; i < g->nkids; i++){
|
||
q = g->kids[i];
|
||
if (q->setsize)
|
||
q->setsize(q);
|
||
if (q->size.min.x == 0 || q->size.min.y == 0 || q->size.max.x == 0 || q->size.max.y == 0)
|
||
ctlerror("%q: bad size %R", q->name, q->size);
|
||
if (debug) fprint(2, "groupsize %q: [%d %q]: %R\n", g->name, i, q->name, q->size);
|
||
switch(g->type){
|
||
case Ctlrow:
|
||
if (i)
|
||
r.min.x += q->size.min.x + g->border;
|
||
else
|
||
r.min.x = q->size.min.x;
|
||
if (i)
|
||
r.max.x += q->size.max.x + g->border;
|
||
else
|
||
r.max.x = q->size.max.x;
|
||
if (r.min.y < q->size.min.y) r.min.y = q->size.min.y;
|
||
if (r.max.y < q->size.max.y) r.max.y = q->size.max.y;
|
||
break;
|
||
case Ctlcolumn:
|
||
if (r.min.x < q->size.min.x) r.min.x = q->size.min.x;
|
||
if (r.max.x < q->size.max.x) r.max.x = q->size.max.x;
|
||
if (i)
|
||
r.min.y += q->size.min.y + g->border;
|
||
else
|
||
r.min.y = q->size.min.y;
|
||
if (i)
|
||
r.max.y += q->size.max.y + g->border;
|
||
else
|
||
r.max.y = q->size.max.y;
|
||
break;
|
||
case Ctlstack:
|
||
if (r.min.x < q->size.min.x) r.min.x = q->size.min.x;
|
||
if (r.max.x < q->size.max.x) r.max.x = q->size.max.x;
|
||
if (r.min.y < q->size.min.y) r.min.y = q->size.min.y;
|
||
if (r.max.y < q->size.max.y) r.max.y = q->size.max.y;
|
||
break;
|
||
}
|
||
}
|
||
g->size = rectaddpt(r, Pt(g->border, g->border));
|
||
if (debug) fprint(2, "groupsize %q: %R\n", g->name, g->size);
|
||
}
|
||
|
||
static void
|
||
boxboxresize(Group *g, Rectangle r)
|
||
{
|
||
int rows, cols, ht, wid, i, hpad, wpad;
|
||
Rectangle rr;
|
||
|
||
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);
|
||
ht = 0;
|
||
for(i=0; i<g->nkids; i++){
|
||
if (g->kids[i]->size.min.y > ht)
|
||
ht = g->kids[i]->size.min.y;
|
||
}
|
||
if (ht == 0)
|
||
ctlerror("boxboxresize: height");
|
||
rows = Dy(r) / (ht+g->separation);
|
||
hpad = (Dy(r) % (ht+g->separation)) / g->nkids;
|
||
cols = (g->nkids+rows-1)/rows;
|
||
wid = Dx(r) / cols - g->separation;
|
||
for(i=0; i<g->nkids; i++){
|
||
if (g->kids[i]->size.max.x < wid)
|
||
wid = g->kids[i]->size.max.x;
|
||
}
|
||
for(i=0; i<g->nkids; i++){
|
||
if (g->kids[i]->size.min.x > wid)
|
||
wid = g->kids[i]->size.min.x;
|
||
}
|
||
if (wid > Dx(r) / cols)
|
||
ctlerror("can't fit controls in boxbox");
|
||
wpad = (Dx(r) % (wid+g->separation)) / g->nkids;
|
||
rr = rectaddpt(Rect(0,0,wid, ht), addpt(r.min, Pt(g->separation/2, g->separation/2)));
|
||
if(debug) fprint(2, "boxboxresize rows %d, cols %d, wid %d, ht %d, wpad %d, hpad %d\n", rows, cols, wid, ht, wpad, hpad);
|
||
for(i=0; i<g->nkids; i++){
|
||
if(debug) fprint(2, " %d %q: %R (%d×%d)\n", i, g->kids[i]->name, rr, Dx(rr), Dy(rr));
|
||
_ctlprint(g->kids[i], "rect %R",
|
||
rectaddpt(rr, Pt((wpad+wid+g->separation)*(i/rows), (hpad+ht+g->separation)*(i%rows))));
|
||
}
|
||
g->nseparators = rows + cols - 2;
|
||
g->separators = realloc(g->separators, g->nseparators*sizeof(Rectangle));
|
||
rr = r;
|
||
rr.max.y = rr.min.y + g->separation+hpad;
|
||
for (i = 1; i < rows; i++){
|
||
g->separators[i-1] = rectaddpt(rr, Pt(0, (hpad+ht+g->separation)*i-g->separation-hpad));
|
||
if(debug) fprint(2, "row separation %d [%d]: %R\n", i, i-1, rectaddpt(rr, Pt(0, (hpad+ht+g->separation)*i-g->separation)));
|
||
}
|
||
rr = r;
|
||
rr.max.x = rr.min.x + g->separation+wpad;
|
||
for (i = 1; i < cols; i++){
|
||
g->separators[i+rows-2] = rectaddpt(rr, Pt((wpad+wid+g->separation)*i-g->separation-wpad, 0));
|
||
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)));
|
||
}
|
||
}
|
||
|
||
static void
|
||
columnresize(Group *g, Rectangle r)
|
||
{
|
||
int x, y, *d, *p, i, j, t;
|
||
Rectangle rr;
|
||
Control *q;
|
||
|
||
x = Dx(r);
|
||
y = Dy(r);
|
||
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);
|
||
if (x < g->size.min.x) {
|
||
werrstr("resize %s: too narrow: need %d, have %d", g->name, g->size.min.x, x);
|
||
r.max.x = r.min.x + g->size.min.x;
|
||
}
|
||
if (y < g->size.min.y) {
|
||
werrstr("resize %s: too short: need %d, have %d", g->name, g->size.min.y, y);
|
||
r.max.y = r.min.y + g->size.min.y;
|
||
y = Dy(r);
|
||
}
|
||
d = ctlmalloc(g->nkids*sizeof(int));
|
||
p = ctlmalloc(g->nkids*sizeof(int));
|
||
if(debug) fprint(2, "kiddies: ");
|
||
for (i = 0; i < g->nkids; i++) {
|
||
q = g->kids[i];
|
||
if(debug) fprint(2, "[%q]: %d⋯%d\t", q->name, q->size.min.y, q->size.max.y);
|
||
d[i] = q->size.min.y;
|
||
y -= d[i];
|
||
p[i] = q->size.max.y - q->size.min.y;
|
||
}
|
||
if(debug) fprint(2, "\n");
|
||
y -= (g->nkids-1) * g->separation;
|
||
if(y < 0){
|
||
if (debug) fprint(2, "columnresize: y == %d\n", y);
|
||
y = 0;
|
||
}
|
||
if (y >= g->size.max.y - g->size.min.y) {
|
||
// all rects can be maximum width
|
||
for (i = 0; i < g->nkids; i++)
|
||
d[i] += p[i];
|
||
y -= g->size.max.y - g->size.min.y;
|
||
} else {
|
||
// rects can't be max width, divide up the rest
|
||
j = y;
|
||
for (i = 0; i < g->nkids; i++) {
|
||
t = p[i] * y/(g->size.max.y - g->size.min.y);
|
||
d[i] += t;
|
||
j -= t;
|
||
}
|
||
d[0] += j;
|
||
y = 0;
|
||
}
|
||
g->nseparators = g->nkids-1;
|
||
g->separators = realloc(g->separators, g->nseparators*sizeof(Rectangle));
|
||
j = 0;
|
||
rr = r;
|
||
for (i = 0; i < g->nkids; i++) {
|
||
q = g->kids[i];
|
||
if (i < g->nkids - 1){
|
||
g->separators[i].min.x = r.min.x;
|
||
g->separators[i].max.x = r.max.x;
|
||
}
|
||
t = y / (g->nkids - i);
|
||
y -= t;
|
||
j += t/2;
|
||
rr.min.y = r.min.y + j;
|
||
if (i)
|
||
g->separators[i-1].max.y = rr.min.y;
|
||
j += d[i];
|
||
rr.max.y = r.min.y + j;
|
||
if (i < g->nkids - 1)
|
||
g->separators[i].min.y = rr.max.y;
|
||
j += g->separation + t - t/2;
|
||
_ctlprint(q, "rect %R", rr);
|
||
if(debug) fprint(2, " %d %q: %R (%d×%d)\n", i, q->name, rr, Dx(rr), Dy(rr));
|
||
}
|
||
free(d);
|
||
free(p);
|
||
}
|
||
|
||
static void
|
||
rowresize(Group *g, Rectangle r)
|
||
{
|
||
int x, y, *d, *p, i, j, t;
|
||
Rectangle rr;
|
||
Control *q;
|
||
|
||
x = Dx(r);
|
||
y = Dy(r);
|
||
if(debug) fprint(2, "rowresize %q %R (%d×%d), separation %d\n", g->name, r, Dx(r), Dy(r), g->separation);
|
||
if (x < g->size.min.x) {
|
||
werrstr("resize %s: too narrow: need %d, have %d", g->name, g->size.min.x, x);
|
||
r.max.x = r.min.x + g->size.min.x;
|
||
x = Dx(r);
|
||
}
|
||
if (y < g->size.min.y) {
|
||
werrstr("resize %s: too short: need %d, have %d", g->name, g->size.min.y, y);
|
||
r.max.y = r.min.y + g->size.min.y;
|
||
}
|
||
d = ctlmalloc(g->nkids*sizeof(int));
|
||
p = ctlmalloc(g->nkids*sizeof(int));
|
||
if(debug) fprint(2, "kiddies: ");
|
||
for (i = 0; i < g->nkids; i++) {
|
||
q = g->kids[i];
|
||
if(debug) fprint(2, "[%q]: %d⋯%d\t", q->name, q->size.min.x, q->size.max.x);
|
||
d[i] = q->size.min.x;
|
||
x -= d[i];
|
||
p[i] = q->size.max.x - q->size.min.x;
|
||
}
|
||
if(debug) fprint(2, "\n");
|
||
x -= (g->nkids-1) * g->separation;
|
||
if(x < 0){
|
||
if (debug) fprint(2, "rowresize: x == %d\n", x);
|
||
x = 0;
|
||
}
|
||
if (x >= g->size.max.x - g->size.min.x) {
|
||
if (debug) fprint(2, "max: %d > %d - %d", x, g->size.max.x, g->size.min.x);
|
||
// all rects can be maximum width
|
||
for (i = 0; i < g->nkids; i++)
|
||
d[i] += p[i];
|
||
x -= g->size.max.x - g->size.min.x;
|
||
} else {
|
||
if (debug) fprint(2, "divvie up: %d < %d - %d", x, g->size.max.x, g->size.min.x);
|
||
// rects can't be max width, divide up the rest
|
||
j = x;
|
||
for (i = 0; i < g->nkids; i++) {
|
||
t = p[i] * x/(g->size.max.x - g->size.min.x);
|
||
d[i] += t;
|
||
j -= t;
|
||
}
|
||
d[0] += j;
|
||
x = 0;
|
||
}
|
||
j = 0;
|
||
g->nseparators = g->nkids-1;
|
||
g->separators = realloc(g->separators, g->nseparators*sizeof(Rectangle));
|
||
rr = r;
|
||
for (i = 0; i < g->nkids; i++) {
|
||
q = g->kids[i];
|
||
if (i < g->nkids - 1){
|
||
g->separators[i].min.y = r.min.y;
|
||
g->separators[i].max.y = r.max.y;
|
||
}
|
||
t = x / (g->nkids - i);
|
||
x -= t;
|
||
j += t/2;
|
||
rr.min.x = r.min.x + j;
|
||
if (i)
|
||
g->separators[i-1].max.x = rr.min.x;
|
||
j += d[i];
|
||
rr.max.x = r.min.x + j;
|
||
if (i < g->nkids - 1)
|
||
g->separators[i].min.x = rr.max.x;
|
||
j += g->separation + t - t/2;
|
||
_ctlprint(q, "rect %R", rr);
|
||
if(debug) fprint(2, " %d %q: %R (%d×%d)\n", i, q->name, rr, Dx(rr), Dy(rr));
|
||
}
|
||
free(d);
|
||
free(p);
|
||
}
|
||
|
||
static void
|
||
stackresize(Group *g, Rectangle r)
|
||
{
|
||
int x, y, i;
|
||
Control *q;
|
||
|
||
x = Dx(r);
|
||
y = Dy(r);
|
||
if(debug) fprint(2, "stackresize %q %R (%d×%d)\n", g->name, r, Dx(r), Dy(r));
|
||
if (x < g->size.min.x){
|
||
werrstr("resize %s: too narrow: need %d, have %d", g->name, g->size.min.x, x);
|
||
return;
|
||
}
|
||
if (y < g->size.min.y){
|
||
werrstr("resize %s: too short: need %d, have %d", g->name, g->size.min.y, y);
|
||
return;
|
||
}
|
||
if (x > g->size.max.x) {
|
||
x = (x - g->size.max.x)/2;
|
||
r.min.x += x;
|
||
r.max.x -= x;
|
||
}
|
||
if (y > g->size.max.y) {
|
||
y = (y - g->size.max.y)/2;
|
||
r.min.y += y;
|
||
r.max.y -= y;
|
||
}
|
||
for (i = 0; i < g->nkids; i++){
|
||
q = g->kids[i];
|
||
if(debug) fprint(2, " %d %q: %R (%d×%d)\n", i, q->name, r, Dx(r), Dy(r));
|
||
}
|
||
for (i = 0; i < g->nkids; i++){
|
||
q = g->kids[i];
|
||
_ctlprint(q, "rect %R", r);
|
||
}
|
||
}
|