ref: 9f76a7f6819ac04552b4fb6588156f3e4089d1d7
dir: /libtk/grids.c/
#include "lib9.h"
#include "draw.h"
#include "tk.h"
/*
* XXX TODO
* - grid rowcget|columncget
* - grid columnconfigure/rowconfigure accepts a list of indexes?
*/
#define O(t, e) ((long)(&((t*)0)->e))
typedef struct TkGridparam TkGridparam;
typedef struct TkBeamparam TkBeamparam;
struct TkGridparam{
Point span;
Tk* in;
Point pad;
Point ipad;
char *row;
char *col;
int sticky;
};
struct TkBeamparam{
int minsize;
int maxsize;
int weight;
int pad;
char *name;
int equalise;
};
static
TkOption opts[] =
{
"padx", OPTnndist, O(TkGridparam, pad.x), nil,
"pady", OPTnndist, O(TkGridparam, pad.y), nil,
"ipadx", OPTnndist, O(TkGridparam, ipad.x), nil,
"ipady", OPTnndist, O(TkGridparam, ipad.y), nil,
"in", OPTwinp, O(TkGridparam, in), nil,
"row", OPTtext, O(TkGridparam, row), nil,
"column", OPTtext, O(TkGridparam, col), nil,
"rowspan", OPTnndist, O(TkGridparam, span.y), nil,
"columnspan", OPTnndist, O(TkGridparam, span.x), nil,
"sticky", OPTsticky, O(TkGridparam, sticky), nil,
nil
};
static
TkOption beamopts[] =
{
"minsize", OPTnndist, O(TkBeamparam, minsize), nil,
"maxsize", OPTnndist, O(TkBeamparam, maxsize), nil,
"weight", OPTnndist, O(TkBeamparam, weight), nil,
"pad", OPTnndist, O(TkBeamparam, pad), nil,
"name", OPTtext, O(TkBeamparam, name), nil,
"equalise", OPTstab, O(TkBeamparam, equalise), tkbool,
nil
};
void
printgrid(TkGrid *grid)
{
int x, y;
Point dim;
dim = grid->dim;
print("grid %P\n", grid->dim);
print(" row heights: ");
for(y = 0; y < dim.y; y++)
print("%d[%d,%d,w%d,p%d]%s ",
grid->rows[y].act,
grid->rows[y].minsize,
grid->rows[y].maxsize < 0x7fffffff ? grid->rows[y].maxsize : -1,
grid->rows[y].weight,
grid->rows[y].pad,
grid->rows[y].name ? grid->rows[y].name : "");
print("\n");
print(" col widths: ");
for(x = 0; x < dim.x; x++)
print("%d[%d,%d,w%d,p%d]%s ",
grid->cols[x].act,
grid->cols[x].minsize,
grid->cols[x].maxsize < 0x7fffffff ? grid->cols[x].maxsize : -1,
grid->cols[x].weight,
grid->cols[x].pad,
grid->cols[x].name ? grid->cols[x].name : "");
print("\n");
for(y = 0; y < dim.y; y++){
print(" row %d: ", y);
for(x = 0; x < dim.x; x++){
print("%p;", grid->cells[y][x].tk);
print("%s%P\t", grid->cells[y][x].tk?grid->cells[y][x].tk->name->name:"(nil)",
grid->cells[y][x].span);
}
print("\n");
}
}
static void
tkgridsetopt(TkGridparam *p, Tk *tk)
{
if(p->pad.x != -1)
tk->pad.x = p->pad.x*2;
if(p->pad.y != -1)
tk->pad.y = p->pad.y*2;
if(p->ipad.x != -1)
tk->ipad.x = p->ipad.x*2;
if(p->ipad.y != -1)
tk->ipad.y = p->ipad.y*2;
if(p->sticky != -1)
tk->flag = (tk->flag & ~(Tkanchor|Tkfill)) | (p->sticky & (Tkanchor|Tkfill));
}
static void
initbeam(TkGridbeam *beam, int n)
{
int i;
memset(beam, 0, n * sizeof(TkGridbeam));
for(i = 0; i < n; i++)
beam[i].maxsize = 0x7fffffff;
}
static char*
ensuregridsize(TkGrid *grid, Point dim)
{
TkGridcell **cells, *cellrow;
TkGridbeam *cols, *rows;
Point olddim;
int i;
olddim = grid->dim;
if(dim.x < olddim.x)
dim.x = olddim.x;
if(dim.y < olddim.y)
dim.y = olddim.y;
if(dim.y > olddim.y){
cells = realloc(grid->cells, sizeof(TkGridcell*)*dim.y);
if(cells == nil)
return TkNomem;
grid->cells = cells;
for(i = olddim.y; i < dim.y; i++){
cells[i] = malloc(sizeof(TkGridcell)*dim.x);
if(cells[i] == nil){
while(--i >= olddim.y)
free(cells[i]);
return TkNomem;
}
}
rows = realloc(grid->rows, sizeof(TkGridbeam)*dim.y);
if(rows == nil)
return TkNomem;
grid->rows = rows;
initbeam(rows + olddim.y, dim.y - olddim.y);
grid->dim.y = dim.y;
}
if(dim.x > olddim.x){
/*
* any newly allocated rows will have the correct number of
* columns, so we don't need to reallocate them
*/
cells = grid->cells;
for(i = 0; i < olddim.y; i++){
cellrow = realloc(cells[i], sizeof(TkGridcell) * dim.x);
if(cellrow == nil)
return TkNomem; /* leak some earlier rows, but not permanently */
memset(cellrow + olddim.x, 0, (dim.x-olddim.x)*sizeof(TkGridcell));
cells[i] = cellrow;
}
cols = realloc(grid->cols, sizeof(TkGridbeam)*dim.x);
if(cols == nil)
return TkNomem;
initbeam(cols + olddim.x, dim.x - olddim.x);
grid->cols = cols;
grid->dim.x = dim.x;
}
return nil;
}
static TkGridbeam*
delbeams(TkGridbeam *beam, int nb, int x0, int x1)
{
int i;
TkGridbeam *b;
for(i = x0; i < x1; i++)
free(beam[i].name);
memmove(&beam[x0], &beam[x1], sizeof(TkGridbeam) * (nb-x1));
b = realloc(beam, sizeof(TkGridbeam) * (nb-(x1-x0)));
return b ? b : beam;
}
static void
delrows(TkGrid *grid, int y0, int y1)
{
TkGridcell **cells;
memmove(grid->cells+y0, grid->cells+y1, sizeof(TkGridcell*) * (grid->dim.y-y1));
grid->dim.y -= (y1 - y0);
cells = realloc(grid->cells, sizeof(TkGridcell*) * grid->dim.y);
if(cells != nil || grid->dim.y == 0)
grid->cells = cells; /* can realloc to a smaller size ever fail? */
}
static void
delcols(TkGrid *grid, int x0, int x1)
{
TkGridcell **cells, *row;
int y, ndx;
Point dim;
dim = grid->dim;
ndx = dim.x - (x1 - x0);
cells = grid->cells;
for(y = 0; y < dim.y; y++){
row = cells[y];
memmove(row+x0, row+x1, sizeof(TkGridcell) * (dim.x - x1));
row = realloc(row, sizeof(TkGridcell) * ndx);
if(row != nil || ndx == 0)
cells[y] = row;
}
grid->dim.x = ndx;
}
/*
* insert items into rows/cols; the beam has already been expanded appropriately.
*/
void
insbeams(TkGridbeam *beam, int nb, int x, int n)
{
memmove(&beam[x+n], &beam[x], sizeof(TkGridbeam)*(nb-x-n));
initbeam(beam+x, n);
}
static char*
insrows(TkGrid *grid, int y0, int n)
{
Point olddim;
char *e;
TkGridcell **cells, *tmp;
int y;
olddim = grid->dim;
if(y0 > olddim.y){
n = y0 + n - olddim.y;
y0 = olddim.y;
}
e = ensuregridsize(grid, Pt(olddim.x, olddim.y + n));
if(e != nil)
return e;
/*
* we know the extra rows will have been filled
* with blank, properly allocated rows, so just swap 'em with the
* ones that need moving.
*/
cells = grid->cells;
for(y = olddim.y - 1; y >= y0; y--){
tmp = cells[y + n];
cells[y + n] = cells[y];
cells[y] = tmp;
}
insbeams(grid->rows, grid->dim.y, y0, n);
return nil;
}
static char*
inscols(TkGrid *grid, int x0, int n)
{
TkGridcell **cells;
Point olddim;
int y;
char *e;
olddim = grid->dim;
if(x0 > olddim.x){
n = x0 + n - olddim.x;
x0 = olddim.x;
}
e = ensuregridsize(grid, Pt(olddim.x + n, olddim.y));
if(e != nil)
return e;
cells = grid->cells;
for(y = 0; y < olddim.y; y++){
memmove(cells[y] + x0 + n, cells[y] + x0, sizeof(TkGridcell) * (olddim.x - x0));
memset(cells[y] + x0, 0, sizeof(TkGridcell) * n);
}
insbeams(grid->cols, grid->dim.x, x0, n);
return nil;
}
static int
maximum(int a, int b)
{
if(a > b)
return a;
return b;
}
/*
* return the width of cols/rows between x0 and x1 in the beam,
* excluding the padding at either end, but including padding in the middle.
*/
static int
beamsize(TkGridbeam *cols, int x0, int x1)
{
int tot, fpad, x;
if(x0 >= x1)
return 0;
tot = cols[x0].act;
fpad = cols[x0].pad;
for(x = x0 + 1; x < x1; x++){
tot += cols[x].act + maximum(cols[x].pad, fpad);
fpad = cols[x].pad;
}
return tot;
}
/*
* return starting position of cell index on beam, relative
* to top-left of grid
*/
static int
beamcellpos(TkGridbeam *beam, int blen, int index)
{
int x;
if(blen == 0 || index >= blen || index < 0)
return 0;
x = beam[0].pad + beamsize(beam, 0, index);
if(index > 0)
x += maximum(beam[index-1].pad, beam[index].pad);
return x;
}
static Rectangle
cellbbox(TkGrid *grid, Point pos)
{
Point dim;
Rectangle r;
dim = grid->dim;
if(pos.x > dim.x)
pos.x = dim.x;
if(pos.y > dim.y)
pos.y = dim.y;
r.min.x = beamcellpos(grid->cols, dim.x, pos.x);
r.min.y = beamcellpos(grid->rows, dim.y, pos.y);
if(pos.x == dim.x)
r.max.x = r.min.x;
else
r.max.x = r.min.x + grid->cols[pos.x].act;
if(pos.y == dim.y)
r.max.y = r.min.y;
else
r.max.y = r.min.y + grid->rows[pos.y].act;
return rectaddpt(r, grid->origin);
}
/*
* return true ifthere are any spanning cells covering row _index_
*/
static int
gridrowhasspan(TkGrid *grid, int index)
{
int i, d;
Point dim;
TkGridcell *cell;
dim = grid->dim;
if(index > 0 && index < dim.y){
for(i = 0; i < dim.x; i++){
cell = &grid->cells[index][i];
if(cell->tk != nil){
d = cell->span.x;
if(d == 0)
return 1;
i += d - 1;
}
}
}
return 0;
}
/*
* return true ifthere are any spanning cells covering column _index_
*/
static int
gridcolhasspan(TkGrid *grid, int index)
{
int i, d;
Point dim;
TkGridcell *cell;
dim = grid->dim;
if(index > 0 && index < dim.x){
for(i = 0; i < dim.y; i++){
cell = &grid->cells[i][index];
if(cell->tk != nil){
d = cell->span.y;
if(d == 0)
return 1;
i += d - 1;
}
}
}
return 0;
}
/*
* find cell that's spanning the grid position p
*/
static int
findspan(TkGrid *grid, Point p, Point *cp)
{
Point dim;
TkGridcell **cells;
Tk *tk;
dim = grid->dim;
cells = grid->cells;
if(p.x < 0 || p.y < 0 || p.x >= dim.x || p.y >= dim.y)
return 0;
if(cells[p.y][p.x].tk == nil)
return 0;
if(cells[p.y][p.x].span.x == 0){
tk = cells[p.y][p.x].tk;
for(; p.y >= 0; p.y--)
if(cells[p.y][p.x].tk != tk)
break;
p.y++;
for(; p.x >= 0; p.x--)
if(cells[p.y][p.x].tk != tk)
break;
p.x++;
}
*cp = p;
return 1;
}
static int
parsegridindex(TkGridbeam *beam, int blen, char *s)
{
int n, i;
char *e;
if(s[0] == '\0')
return -1;
n = strtol(s, &e, 10);
if(*e == '\0')
return n;
if(strcmp(s, "end") == 0)
return blen;
for(i = 0; i < blen; i++)
if(beam[i].name != nil && strcmp(beam[i].name, s) == 0)
return i;
return -1;
}
static char*
tkgridconfigure(TkTop *t, TkGridparam *p, TkName *names)
{
TkGrid *grid;
TkGridcell **cells;
TkName *n;
Tk *tkf, *tkp;
Point dim, pos, q, span, startpos;
int maxcol, c, i, j, x;
char *e;
if(names == nil)
return nil;
if(p->span.x < 1 || p->span.y < 1)
return TkBadvl;
tkf = nil;
maxcol = 0;
for(n = names; n; n = n->link){
c = n->name[0];
if((c=='-' || c=='^' || c=='x') && n->name[1] == '\0'){
maxcol++;
continue;
}
tkp = tklook(t, n->name, 0);
if(tkp == nil){
tkerr(t, n->name);
return TkBadwp;
}
if(tkp->flag & Tkwindow)
return TkIstop;
if(tkp->parent != nil)
return TkWpack;
/*
* unpacking now does give an non-reversible side effect
* ifthere's an error encountered later, but also means
* that a widget repacked in the same grid will
* have its original cell still available
*/
if(tkp->master != nil){
tkpackqit(tkp->master);
tkdelpack(tkp);
}
if(tkf == nil)
tkf = tkp;
n->obj = tkp;
tkp->flag &= ~Tkgridpack;
maxcol += p->span.x;
}
if(p->in == nil && tkf != nil)
p->in = tklook(t, tkf->name->name, 1);
if(p->in == nil)
return TkNomaster;
grid = p->in->grid;
if(grid == nil && p->in->slave != nil)
return TkNotgrid;
if(grid == nil){
grid = malloc(sizeof(TkGrid));
if(grid == nil)
return TkNomem;
p->in->grid = grid;
}
dim = grid->dim;
pos = ZP;
if(p->row != nil){
pos.y = parsegridindex(grid->rows, dim.y, p->row);
if(pos.y < 0)
return TkBadix;
}
if(p->col != nil){
pos.x = parsegridindex(grid->cols, dim.x, p->col);
if(pos.x < 0)
return TkBadix;
}
/*
* ifrow is not specified, find first unoccupied row
*/
if(p->row == nil){
for(pos.y = 0; pos.y < dim.y; pos.y++){
for(x = 0; x < dim.x; x++)
if(grid->cells[pos.y][x].tk != nil)
break;
if(x == dim.x)
break;
}
}
e = ensuregridsize(grid, Pt(pos.x + maxcol, pos.y + p->span.y));
if(e != nil)
return e;
cells = grid->cells;
startpos = pos;
/*
* check that all our grid cells are empty, and that row/col spans
* are well formed
*/
n = names;
while(n != nil){
c = n->name[0];
switch (c){
case 'x':
n = n->link;
pos.x++;
break;
case '^':
if(findspan(grid, Pt(pos.x, pos.y - 1), &q) == 0)
return TkBadspan;
span = cells[q.y][q.x].span;
for(i = 0; i < span.x; i++){
if(n == nil || strcmp(n->name, "^"))
return TkBadspan;
if(cells[pos.y][pos.x + i].tk != nil)
return TkBadgridcell;
n = n->link;
}
pos.x += span.x;
break;
case '-':
return TkBadspan;
case '.':
tkp = n->obj;
if(tkisslave(p->in, tkp))
return TkRecur;
n = n->link;
if(tkp->flag & Tkgridpack)
return TkWpack;
tkp->flag |= Tkgridpack;
span = p->span;
for(; n != nil && strcmp(n->name, "-") == 0; n = n->link)
span.x++;
for(i = pos.x; i < pos.x + span.x; i++)
for(j = pos.y; j < pos.y + span.y; j++)
if(cells[j][i].tk != nil)
return TkBadgridcell;
pos.x = i;
break;
}
}
/*
* actually insert the items into the grid
*/
n = names;
pos = startpos;
while(n != nil){
c = n->name[0];
switch (c){
case 'x':
n = n->link;
pos.x++;
break;
case '^':
findspan(grid, Pt(pos.x, pos.y - 1), &q);
span = cells[q.y][q.x].span;
tkf = cells[q.y][q.x].tk;
if(q.y + span.y == pos.y)
cells[q.y][q.x].span.y++;
for(i = 0; i < span.x; i++){
cells[pos.y][pos.x++].tk = tkf;
n = n->link;
}
break;
case '.':
tkf = n->obj;
n = n->link;
span = p->span;
for(; n != nil && strcmp(n->name, "-") == 0; n = n->link)
span.x++;
for(i = pos.x; i < pos.x + span.x; i++)
for(j = pos.y; j < pos.y + span.y; j++)
cells[j][i].tk = tkf;
cells[pos.y][pos.x].span = span;
tkf->master = p->in;
tkf->next = p->in->slave;
p->in->slave = tkf;
if(p->in->flag & Tksubsub)
tksetbits(tkf, Tksubsub);
tkgridsetopt(p, tkf);
pos.x = i;
break;
}
}
tkpackqit(p->in);
tkrunpack(t);
return nil;
}
void
tkgriddelslave(Tk *tk)
{
int y, x, yy;
TkGrid *grid;
TkGridcell **cells, *cell;
Point dim, span;
if(tk == nil || tk->master == nil || tk->master->grid == nil)
return;
grid = tk->master->grid;
cells = grid->cells;
dim = grid->dim;
for(y = 0; y < dim.y; y++){
for(x = 0; x < dim.x; x++){
cell = &cells[y][x];
if(cell->tk == tk){
span = cell->span;
for(yy = y; yy < y + span.y; yy++)
memset(cells[yy] + x, 0, span.x * sizeof(TkGridcell));
return;
}
}
}
}
char*
tkgetgridmaster(TkTop *t, char **arg, char *buf, char *ebuf, Tk **master)
{
TkGrid *grid;
*arg = tkword(t, *arg, buf, ebuf, nil);
*master = tklook(t, buf, 0);
if(*master == nil)
return TkBadwp;
grid = (*master)->grid;
if(grid == nil && (*master)->slave != nil)
return TkNotgrid;
return nil;
}
static int
gridfindloc(TkGridbeam *beam, int blen, int f)
{
int x, i, fpad;
if(blen == 0 || f < 0)
return -1;
fpad = 0;
x = 0;
for(i = 0; i < blen; i++){
x += maximum(fpad, beam[i].pad);
if(x <= f && f < x + beam[i].act)
return i;
x += beam[i].act;
}
return -1;
}
static char*
tkgridcellinfo(TkTop *t, char *arg, char **val, char *buf, char *ebuf)
{
/* grid cellinfo master x y */
Tk *master;
char *e;
Point p;
TkGrid *grid;
TkGridcell **cells;
e = tkgetgridmaster(t, &arg, buf, ebuf, &master);
if(e != nil || master->grid == nil)
return e;
grid = master->grid;
e = tkfracword(t, &arg, &p.x, nil);
if(e != nil)
return e;
e = tkfracword(t, &arg, &p.y, nil);
if(e != nil)
return e;
p.x = TKF2I(p.x);
p.y = TKF2I(p.y);
if(p.x < 0 || p.x >= grid->dim.x || p.y < 0 || p.y >= grid->dim.y)
return nil;
if(!findspan(grid, p, &p))
return nil;
cells = grid->cells;
return tkvalue(val, "%s -in %s -column %d -row %d -columnspan %d -rowspan %d",
cells[p.y][p.x].tk->name->name,
cells[p.y][p.x].tk->master->name->name, p.x, p.y,
cells[p.y][p.x].span.x, cells[p.y][p.x].span.y);
}
static char*
tkgridlocation(TkTop *t, char *arg, char **val, char *buf, char *ebuf)
{
/* grid location master x y */
Tk *master;
char *e;
Point p;
int col, row;
TkGrid *grid;
e = tkgetgridmaster(t, &arg, buf, ebuf, &master);
if(e != nil || master->grid == nil)
return e;
grid = master->grid;
e = tkfracword(t, &arg, &p.x, nil);
if(e != nil)
return e;
e = tkfracword(t, &arg, &p.y, nil);
if(e != nil)
return e;
p.x = TKF2I(p.x);
p.y = TKF2I(p.y);
p = subpt(p, grid->origin);
col = gridfindloc(grid->cols, grid->dim.x, p.x);
row = gridfindloc(grid->rows, grid->dim.y, p.y);
if(col < 0 || row < 0)
return nil;
return tkvalue(val, "%d %d", col, row);
}
static char*
tkgridinfo(TkTop *t, char *arg, char **val, char *buf, char *ebuf)
{
Tk *tk;
TkGrid *grid;
int x, y;
Point dim;
TkGridcell *row;
tkword(t, arg, buf, ebuf, nil);
tk = tklook(t, buf, 0);
if(tk == nil)
return TkBadwp;
if(tk->master == nil || tk->master->grid == nil)
return TkNotgrid;
grid = tk->master->grid;
dim = grid->dim;
for(y = 0; y < dim.y; y++){
row = grid->cells[y];
for(x = 0; x < dim.x; x++)
if(row[x].tk == tk)
goto Found;
}
return TkNotgrid; /* should not happen */
Found:
return tkvalue(val, "-in %s -column %d -row %d -columnspan %d -rowspan %d",
tk->master->name->name, x, y, grid->cells[y][x].span.x, grid->cells[y][x].span.y);
}
static char*
tkgridforget(TkTop *t, char *arg, char *buf, char *ebuf)
{
Tk *tk;
for(;;){
arg = tkword(t, arg, buf, ebuf, nil);
if(arg == nil || buf[0] == '\0')
break;
tk = tklook(t, buf, 0);
if(tk == nil){
tkrunpack(t);
tkerr(t, buf);
return TkBadwp;
}
tkpackqit(tk->master);
tkdelpack(tk);
}
tkrunpack(t);
return nil;
}
static char*
tkgridslaves(TkTop *t, char *arg, char **val, char *buf, char *ebuf)
{
Tk *master, *tk;
char *fmt;
int i, isrow, index;
TkGrid *grid;
TkGridcell *cell;
char *e;
e = tkgetgridmaster(t, &arg, buf, ebuf, &master);
if(e != nil || master->grid == nil)
return e;
grid = master->grid;
arg = tkword(t, arg, buf, ebuf, nil);
fmt = "%s";
if(buf[0] == '\0'){
for(tk = master->slave; tk != nil; tk = tk->next){
if(tk->name != nil){
e = tkvalue(val, fmt, tk->name->name);
if(e != nil)
return e;
fmt = " %s";
}
}
return nil;
}
if(strcmp(buf, "-row") == 0)
isrow = 1;
else if(strcmp(buf, "-column") == 0)
isrow = 0;
else
return TkBadop;
tkword(t, arg, buf, ebuf, nil);
if(isrow)
index = parsegridindex(grid->rows, grid->dim.y, buf);
else
index = parsegridindex(grid->cols, grid->dim.x, buf);
if(index < 0)
return TkBadix;
if(isrow){
if(index >= grid->dim.y)
return nil;
for(i = 0; i < grid->dim.x; i++){
cell = &grid->cells[index][i];
if(cell->tk != nil && cell->span.x > 0 && cell->tk->name != nil){
e = tkvalue(val, fmt, cell->tk->name->name);
if(e != nil)
return e;
fmt = " %s";
}
}
} else{
if(index >= grid->dim.x)
return nil;
for(i = 0; i < grid->dim.y; i++){
cell = &grid->cells[i][index];
if(cell->tk != nil && cell->span.x > 0 && cell->tk->name != nil){
e = tkvalue(val, fmt, cell->tk->name->name);
if(e != nil)
return e;
fmt = " %s";
}
}
}
return nil;
}
static char*
tkgriddelete(TkTop *t, char *arg, char *buf, char *ebuf, int delrow)
{
Tk *master, **l, *f;
TkGrid *grid;
TkGridbeam *beam;
int blen, i0, i1, x, y;
Point dim;
TkGridcell **cells;
char *e;
/*
* grid (columndelete|rowdelete) master index0 ?index1?
*/
e = tkgetgridmaster(t, &arg, buf, ebuf, &master);
if(e != nil || master->grid == nil)
return e;
grid = master->grid;
if(delrow){
beam = grid->rows;
blen = grid->dim.y;
} else{
beam = grid->cols;
blen = grid->dim.x;
}
arg = tkword(t, arg, buf, ebuf, nil);
i0 = parsegridindex(beam, blen, buf);
if(i0 < 0)
return TkBadix;
tkword(t, arg, buf, ebuf, nil);
if(buf[0] == '\0')
i1 = i0 + 1;
else
i1 = parsegridindex(beam, blen, buf);
if(i1 < 0 || i0 > i1)
return TkBadix;
if(i0 > blen || i0 == i1)
return nil;
if(i1 > blen)
i1 = blen;
cells = grid->cells;
dim = grid->dim;
if(delrow){
if(gridrowhasspan(grid, i0) || gridrowhasspan(grid, i1))
return TkBadgridcell;
for(y = i0; y < i1; y++)
for(x = 0; x < dim.x; x++)
if(cells[y][x].tk != nil)
cells[y][x].tk->flag |= Tkgridremove;
delrows(grid, i0, i1);
grid->rows = delbeams(beam, blen, i0, i1);
} else{
if(gridcolhasspan(grid, i0) || gridcolhasspan(grid, i1))
return TkBadgridcell;
for(y = 0; y < dim.y; y++)
for(x = i0; x < i1; x++)
if(cells[y][x].tk != nil)
cells[y][x].tk->flag |= Tkgridremove;
delcols(grid, i0, i1);
grid->cols = delbeams(beam, blen, i0, i1);
}
l = &master->slave;
for(f = *l; f; f = f->next){
if(f->flag & Tkgridremove){
*l = f->next;
f->master = nil;
f->flag &= ~Tkgridremove;
} else
l = &f->next;
}
tkpackqit(master);
tkrunpack(t);
return nil;
}
static char*
tkgridinsert(TkTop *t, char *arg, char *buf, char *ebuf, int insertrow)
{
int index, count;
Point dim;
Tk *master;
TkGrid *grid;
int gotarg;
char *e;
/*
* grid (rowinsert|columninsert) master index ?count?
* it's an error ifthe insert splits any spanning cells.
*/
e = tkgetgridmaster(t, &arg, buf, ebuf, &master);
if(e != nil || master->grid == nil)
return e;
grid = master->grid;
dim = grid->dim;
arg = tkword(t, arg, buf, ebuf, nil);
if(insertrow)
index = parsegridindex(grid->rows, dim.y, buf);
else
index = parsegridindex(grid->cols, dim.x, buf);
if(index < 0 || index > (insertrow ? dim.y : dim.x))
return TkBadix;
tkword(t, arg, buf, ebuf, &gotarg);
if(gotarg){
count = strtol(buf, &buf, 10);
if(buf[0] != '\0' || count < 0)
return TkBadvl;
} else
count = 1;
/*
* check that we're not splitting any spanning cells
*/
if(insertrow){
if(gridrowhasspan(grid, index))
return TkBadgridcell;
e = insrows(grid, index, count);
} else{
if(gridcolhasspan(grid, index))
return TkBadgridcell;
e = inscols(grid, index, count);
}
tkpackqit(master);
tkrunpack(t);
return e;
}
/*
* (rowconfigure|columnconfigure) master index ?-option value ...?
*/
static char*
tkbeamconfigure(TkTop *t, char *arg, int isrow)
{
TkBeamparam p;
TkOptab tko[2];
TkName *names;
Tk *master;
int index;
TkGrid *grid;
TkGridbeam *beam;
Point dim;
char *e;
p.equalise = BoolX;
p.name = nil;
p.weight = -1;
p.minsize = -1;
p.maxsize = -1;
p.pad = -1;
tko[0].ptr = &p;
tko[0].optab = beamopts;
tko[1].ptr = nil;
names = nil;
e = tkparse(t, arg, tko, &names);
if(e != nil)
return e;
if(names == nil || names->link == nil)
return TkBadvl;
master = tklook(t, names->name, 0);
if(master == nil)
return TkBadwp;
grid = master->grid;
if(grid == nil){
if(master->slave != nil)
return TkNotgrid;
grid = master->grid = malloc(sizeof(TkGrid));
if(grid == nil){
tkfreename(names);
return TkNomem;
}
}
if(isrow){
index = parsegridindex(grid->rows, grid->dim.y, names->link->name);
} else
index = parsegridindex(grid->cols, grid->dim.x, names->link->name);
if(index < 0){
e = TkBadix;
goto Error;
}
if(isrow)
dim = Pt(grid->dim.x, index + 1);
else
dim = Pt(index + 1, grid->dim.y);
e = ensuregridsize(grid, dim);
if(e != nil)
goto Error;
if(isrow)
beam = &grid->rows[index];
else
beam = &grid->cols[index];
if(p.minsize >= 0)
beam->minsize = p.minsize;
if(p.maxsize >= 0)
beam->maxsize = p.maxsize;
if(p.weight >= 0)
beam->weight = p.weight;
if(p.pad >= 0)
beam->pad = p.pad;
if(p.name != nil){
free(beam->name);
beam->name = p.name;
}
if(p.equalise != BoolX)
beam->equalise = p.equalise == BoolT;
tkpackqit(master);
tkrunpack(t);
Error:
tkfreename(names);
return e;
}
char*
tkgridsize(TkTop *t, char *arg, char **val, char *buf, char *ebuf)
{
Tk *master;
TkGrid *grid;
char *e;
e = tkgetgridmaster(t, &arg, buf, ebuf, &master);
if(e != nil)
return e;
grid = master->grid;
if(grid == nil)
return tkvalue(val, "0 0");
else
return tkvalue(val, "%d %d", grid->dim.x, grid->dim.y);
}
char*
tkgridbbox(TkTop *t, char *arg, char **val, char *buf, char *ebuf)
{
Point p0, p1;
Tk *master;
TkGrid *grid;
char *e;
int gotarg;
Point dim;
Rectangle r;
e = tkgetgridmaster(t, &arg, buf, ebuf, &master);
if(e != nil || master->grid == nil)
return e;
grid = master->grid;
dim = grid->dim;
arg = tkword(t, arg, buf, ebuf, &gotarg);
if(!gotarg){
p0 = ZP;
p1 = dim;
} else{
p0.x = parsegridindex(grid->cols, dim.x, buf);
arg = tkword(t, arg, buf, ebuf, &gotarg);
if(!gotarg)
return TkFewpt;
p0.y = parsegridindex(grid->rows, dim.y, buf);
arg = tkword(t, arg, buf, ebuf, &gotarg);
if(!gotarg){
p1 = p0;
} else{
p1.x = parsegridindex(grid->cols, dim.x, buf);
arg = tkword(t, arg, buf, ebuf, &gotarg);
if(!gotarg)
return TkFewpt;
p1.y = parsegridindex(grid->rows, dim.y, buf);
}
}
if(p0.x < 0 || p0.y < 0 || p1.x < 0 || p1.y < 0)
return TkBadix;
r = cellbbox(grid, p0);
if(!eqpt(p0, p1))
combinerect(&r, cellbbox(grid, p1));
return tkvalue(val, "%d %d %d %d", r.min.x, r.min.y, r.max.x, r.max.y);
}
char*
tkgridindex(TkTop *t, char *arg, char **val, char *buf, char *ebuf, int isrow)
{
Tk *master;
TkGrid *grid;
TkGridbeam *beam;
int blen, i;
arg = tkword(t, arg, buf, ebuf, nil);
master = tklook(t, buf, 0);
if(master == nil)
return TkBadwp;
tkword(t, arg, buf, ebuf, nil);
grid = master->grid;
if(grid == nil){
beam = nil;
blen = 0;
} else if(isrow){
beam = grid->rows;
blen = grid->dim.y;
} else{
beam = grid->cols;
blen = grid->dim.x;
}
i = parsegridindex(beam, blen, buf);
if(i < 0)
return TkBadix;
return tkvalue(val, "%d", i);
}
void
tkfreegrid(TkGrid *grid)
{
Point dim;
int i;
dim = grid->dim;
for(i = 0; i < dim.x; i++)
free(grid->cols[i].name);
for(i = 0; i < dim.y; i++)
free(grid->rows[i].name);
for(i = 0; i < dim.y; i++)
free(grid->cells[i]);
free(grid->cells);
free(grid->rows);
free(grid->cols);
free(grid);
}
char*
tkgrid(TkTop *t, char *arg, char **val)
{
TkGridparam *p;
TkOptab tko[2];
TkName *names;
char *e, *w, *buf;
buf = mallocz(Tkmaxitem, 0);
if(buf == nil)
return TkNomem;
w = tkword(t, arg, buf, buf+Tkmaxitem, nil);
if('a' <= buf[0] && buf[0] <= 'z'){
if(strcmp(buf, "debug") == 0){
Tk *tk;
e = tkgetgridmaster(t, &w, buf, buf+Tkmaxitem, &tk);
if(e == nil)
printgrid(tk->grid);
} else
if(strcmp(buf, "forget") == 0)
e = tkgridforget(t, w, buf, buf+Tkmaxitem);
else if(strcmp(buf, "propagate") == 0)
e = tkpropagate(t, w);
else if(strcmp(buf, "slaves") == 0)
e = tkgridslaves(t, w, val, buf, buf+Tkmaxitem);
else if(strcmp(buf, "rowconfigure") == 0)
e = tkbeamconfigure(t, w, 1);
else if(strcmp(buf, "columnconfigure") == 0)
e = tkbeamconfigure(t, w, 0);
else if(strcmp(buf, "rowinsert") == 0)
e = tkgridinsert(t, w, buf, buf+Tkmaxitem, 1);
else if(strcmp(buf, "columninsert") == 0)
e = tkgridinsert(t, w, buf, buf+Tkmaxitem, 0);
else if(strcmp(buf, "size") == 0)
e = tkgridsize(t, w, val, buf, buf+Tkmaxitem);
else if(strcmp(buf, "rowdelete") == 0)
e = tkgriddelete(t, w, buf, buf+Tkmaxitem, 1);
else if(strcmp(buf, "columndelete") == 0)
e = tkgriddelete(t, w, buf, buf+Tkmaxitem, 0);
else if(strcmp(buf, "rowindex") == 0)
e = tkgridindex(t, w, val, buf, buf+Tkmaxitem, 1);
else if(strcmp(buf, "columnindex") == 0)
e = tkgridindex(t, w, val, buf, buf+Tkmaxitem, 0);
else if(strcmp(buf, "bbox") == 0)
e = tkgridbbox(t, w, val, buf, buf+Tkmaxitem);
else if(strcmp(buf, "location") == 0)
e = tkgridlocation(t, w, val, buf, buf+Tkmaxitem);
else if(strcmp(buf, "cellinfo") == 0)
e = tkgridcellinfo(t, w, val, buf, buf+Tkmaxitem);
else if(strcmp(buf, "info") == 0)
e = tkgridinfo(t, w, val, buf, buf+Tkmaxitem);
else{
tkerr(t, buf);
e = TkBadcm;
}
} else{
p = malloc(sizeof(TkGridparam));
if(p == nil)
return TkNomem;
tko[0].ptr = p;
tko[0].optab = opts;
tko[1].ptr = nil;
p->span.x = 1;
p->span.y = 1;
p->pad.x = p->pad.y = p->ipad.x = p->ipad.y = -1;
p->sticky = -1;
names = nil;
e = tkparse(t, arg, tko, &names);
if(e != nil){
free(p);
return e;
}
e = tkgridconfigure(t, p, names);
free(p->row);
free(p->col);
free(p);
tkfreename(names);
}
free(buf);
return e;
}
/*
* expand widths of rows/columns according to weight.
* return amount of space still left over.
*/
static int
expandwidths(int x0, int x1, int totwidth, TkGridbeam *cols, int expandzero)
{
int share, x, slack, m, w, equal;
if(x0 >= x1)
return 0;
share = 0;
for(x = x0; x < x1; x++)
share += cols[x].weight;
slack = totwidth - beamsize(cols, x0, x1);
if(slack <= 0)
return 0;
if(share == 0 && expandzero){
share = x1 - x0;
equal = 1;
} else
equal = 0;
for(x = x0; x < x1 && share > 0 ; x++){
w = equal ? 1 : cols[x].weight;
m = slack * w / share;
cols[x].act += m;
slack -= m;
share -= w;
}
return slack;
}
static void
gridequalise(TkGridbeam *beam, int blen)
{
int i, max;
max = 0;
for(i = 0; i < blen; i++)
if(beam[i].equalise == BoolT && beam[i].act > max)
max = beam[i].act;
if(max > 0)
for(i = 0; i < blen; i++)
if(beam[i].equalise == BoolT)
beam[i].act = max;
}
/*
* take into account min/max beam sizes.
* max takes precedence
*/
static void
beamminmax(TkGridbeam *beam, int n)
{
TkGridbeam *e;
e = &beam[n];
for(; beam < e; beam++){
if(beam->act < beam->minsize)
beam->act = beam->minsize;
if(beam->act > beam->maxsize)
beam->act = beam->maxsize;
}
}
int
tkgridder(Tk *master)
{
TkGrid *grid;
TkGridcell **cells, *cell;
TkGridbeam *rows, *cols;
TkGeom pos;
Point org;
Tk *slave;
int dx, dy, x, y, w, bw2, fpadx, fpady;
Point req;
grid = master->grid;
dx = grid->dim.x;
dy = grid->dim.y;
cells = grid->cells;
rows = grid->rows;
cols = grid->cols;
for(x = 0; x < dx; x++)
cols[x].act = 0;
/* calculate column widths and row heights (ignoring multi-column cells) */
for(y = 0; y < dy; y++){
rows[y].act = 0;
for(x = 0; x < dx; x++){
cell = &cells[y][x];
if((slave = cell->tk) != nil){
bw2 = slave->borderwidth * 2;
w = slave->req.width + bw2 + slave->pad.x + slave->ipad.x;
if(cell->span.x == 1 && w > cols[x].act)
cols[x].act = w;
w = slave->req.height + bw2 + slave->pad.y + slave->ipad.y;
if(cell->span.y == 1 && w > rows[y].act)
rows[y].act = w;
}
}
}
beamminmax(rows, dy);
beamminmax(cols, dx);
/* now check that spanning cells fit in their rows/columns */
for(y = 0; y < dy; y++)
for(x = 0; x < dx; x++){
cell = &cells[y][x];
if((slave = cell->tk) != nil){
bw2 = slave->borderwidth * 2;
if(cell->span.x > 1){
w = slave->req.width + bw2 + slave->pad.x + slave->ipad.x;
expandwidths(x, x+cell->span.x, w, cols, 1);
}
if(cell->span.y > 1){
w = slave->req.height + bw2 + slave->pad.y + slave->ipad.y;
expandwidths(y, y+cell->span.y, w, rows, 1);
}
}
}
gridequalise(rows, dy);
gridequalise(cols, dx);
if(dx == 0)
req.x = 0;
else
req.x = beamsize(cols, 0, dx) + cols[0].pad + cols[dx-1].pad;
if(dy == 0)
req.y = 0;
else
req.y = beamsize(rows, 0, dy) + rows[0].pad + rows[dy-1].pad;
if(req.x != master->req.width || req.y != master->req.height)
if((master->flag & Tknoprop) == 0){
if(master->geom != nil){
master->geom(master, master->act.x, master->act.y,
req.x, req.y);
} else{
master->req.width = req.x;
master->req.height = req.y;
tkpackqit(master->master);
}
return 0;
}
org = ZP;
if(dx > 0 && master->act.width > req.x)
org.x = expandwidths(0, dx,
master->act.width - (cols[0].pad + cols[dx-1].pad),
cols, 0) / 2;
if(dy > 0 && master->act.height > req.y)
org.y = expandwidths(0, dy,
master->act.height - (rows[0].pad + rows[dy-1].pad),
rows, 0) / 2;
grid->origin = org;
pos.y = org.y;
fpady = 0;
for(y = 0; y < dy; y++){
pos.y += maximum(fpady, rows[y].pad);
fpady = rows[y].pad;
pos.x = org.x;
fpadx = 0;
for(x = 0; x < dx; x++){
cell = &cells[y][x];
pos.x += maximum(fpadx, cols[x].pad);
fpadx = cols[x].pad;
if((slave = cell->tk) != nil && cell->span.x > 0){
pos.width = beamsize(cols, x, x + cell->span.x);
pos.height = beamsize(rows, y, y + cell->span.y);
tksetslavereq(slave, pos);
}
pos.x += cols[x].act;
}
pos.y += rows[y].act;
}
master->dirty = tkrect(master, 1);
tkdirty(master);
return 1;
}