summaryrefslogtreecommitdiff
path: root/libtk/grids.c
diff options
context:
space:
mode:
authorCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
committerCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
commit37da2899f40661e3e9631e497da8dc59b971cbd0 (patch)
treecbc6d4680e347d906f5fa7fca73214418741df72 /libtk/grids.c
parent54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff)
20060303a
Diffstat (limited to 'libtk/grids.c')
-rw-r--r--libtk/grids.c1552
1 files changed, 1552 insertions, 0 deletions
diff --git a/libtk/grids.c b/libtk/grids.c
new file mode 100644
index 00000000..4403fba2
--- /dev/null
+++ b/libtk/grids.c
@@ -0,0 +1,1552 @@
+#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){
+ for(i--; i >= olddim.y; i--)
+ 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;
+}
+
+/*
+ * optimised way to find a given slave, but somewhat more fragile
+ * as it assumes the slave has already been placed on the grid.
+ * not tested.
+ */
+static int
+findslave(TkGrid *grid, Tk *tk, Point *pt)
+{
+ Point loc, dim, p;
+ TkGridcell **cells;
+ dim = grid->dim;
+ cells = grid->cells;
+ loc.x = gridfindloc(grid->cols, grid->dim.x, tk->act.x);
+ if(loc.x == -1)
+ loc.x = 0;
+ loc.y = gridfindloc(grid->rows, grid->dim.y, tk->act.y);
+ if(loc.y == -1)
+ loc.y = 0;
+ for(p.y = loc.y; p.y < dim.y; p.y++)
+ for(p.x = loc.x; p.x < dim.x; p.x++)
+ if(cells[p.y][p.x].tk == tk){
+ *pt = p;
+ return 1;
+ }
+ return 0;
+}
+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;
+}