ref: a5cb451b299b03f44154fac5780b6a57ca130ce0
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; }