xref: /inferno-os/libtk/grids.c (revision 90ccc69f5be0b7e26c3607dac07340fe3e271509)
1  #include "lib9.h"
2  #include "draw.h"
3  #include "tk.h"
4  
5  /*
6   * XXX TODO
7   * - grid rowcget|columncget
8   * - grid columnconfigure/rowconfigure accepts a list of indexes?
9   */
10  
11  #define	O(t, e)		((long)(&((t*)0)->e))
12  
13  typedef struct TkGridparam TkGridparam;
14  typedef struct TkBeamparam TkBeamparam;
15  
16  struct TkGridparam{
17  	Point	span;
18  	Tk*	in;
19  	Point	pad;
20  	Point ipad;
21  	char	*row;
22  	char *col;
23  	int	sticky;
24  };
25  
26  struct TkBeamparam{
27  	int	minsize;
28  	int	maxsize;
29  	int	weight;
30  	int	pad;
31  	char	*name;
32  	int	equalise;
33  };
34  
35  static
36  TkOption opts[] =
37  {
38  	"padx",		OPTnndist,	O(TkGridparam, pad.x),	nil,
39  	"pady",		OPTnndist,	O(TkGridparam, pad.y),	nil,
40  	"ipadx",	OPTnndist,	O(TkGridparam, ipad.x),	nil,
41  	"ipady",	OPTnndist,	O(TkGridparam, ipad.y),	nil,
42  	"in",		OPTwinp,		O(TkGridparam, in),		nil,
43  	"row",	OPTtext,		O(TkGridparam, row), nil,
44  	"column",	OPTtext,		O(TkGridparam, col), nil,
45  	"rowspan",	OPTnndist,	O(TkGridparam, span.y), nil,
46  	"columnspan",	OPTnndist,	O(TkGridparam, span.x), nil,
47  	"sticky",	OPTsticky,		O(TkGridparam, sticky), nil,
48  	nil
49  };
50  
51  static
52  TkOption beamopts[] =
53  {
54  	"minsize",		OPTnndist,	O(TkBeamparam, minsize),	nil,
55  	"maxsize",	OPTnndist,	O(TkBeamparam, maxsize),	nil,
56  	"weight",		OPTnndist,	O(TkBeamparam, weight),	nil,
57  	"pad",		OPTnndist,	O(TkBeamparam, pad),		nil,
58  	"name",		OPTtext,		O(TkBeamparam, name),		nil,
59  	"equalise",	OPTstab,		O(TkBeamparam, equalise),	tkbool,
60  	nil
61  };
62  
63  void
64  printgrid(TkGrid *grid)
65  {
66  	int x, y;
67  	Point dim;
68  
69  	dim = grid->dim;
70  	print("grid %P\n", grid->dim);
71  	print("  row heights: ");
72  	for(y = 0; y < dim.y; y++)
73  		print("%d[%d,%d,w%d,p%d]%s ",
74  			grid->rows[y].act,
75  			grid->rows[y].minsize,
76  			grid->rows[y].maxsize < 0x7fffffff ? grid->rows[y].maxsize : -1,
77  			grid->rows[y].weight,
78  			grid->rows[y].pad,
79  			grid->rows[y].name ? grid->rows[y].name : "");
80  	print("\n");
81  	print("  col widths: ");
82  	for(x = 0; x < dim.x; x++)
83  		print("%d[%d,%d,w%d,p%d]%s ",
84  			grid->cols[x].act,
85  			grid->cols[x].minsize,
86  			grid->cols[x].maxsize < 0x7fffffff ? grid->cols[x].maxsize : -1,
87  			grid->cols[x].weight,
88  			grid->cols[x].pad,
89  			grid->cols[x].name ? grid->cols[x].name : "");
90  	print("\n");
91  	for(y = 0; y < dim.y; y++){
92  		print("  row %d: ", y);
93  		for(x = 0; x < dim.x; x++){
94  			print("%p;", grid->cells[y][x].tk);
95  			print("%s%P\t", grid->cells[y][x].tk?grid->cells[y][x].tk->name->name:"(nil)",
96  				grid->cells[y][x].span);
97  		}
98  		print("\n");
99  	}
100  }
101  
102  static void
103  tkgridsetopt(TkGridparam *p, Tk *tk)
104  {
105  	if(p->pad.x != -1)
106  		tk->pad.x = p->pad.x*2;
107  	if(p->pad.y != -1)
108  		tk->pad.y = p->pad.y*2;
109  	if(p->ipad.x != -1)
110  		tk->ipad.x = p->ipad.x*2;
111  	if(p->ipad.y != -1)
112  		tk->ipad.y = p->ipad.y*2;
113  	if(p->sticky != -1)
114  		tk->flag = (tk->flag & ~(Tkanchor|Tkfill)) | (p->sticky & (Tkanchor|Tkfill));
115  }
116  
117  static void
118  initbeam(TkGridbeam *beam, int n)
119  {
120  	int i;
121  	memset(beam, 0, n * sizeof(TkGridbeam));
122  	for(i = 0; i < n; i++)
123  		beam[i].maxsize = 0x7fffffff;
124  }
125  
126  static char*
127  ensuregridsize(TkGrid *grid, Point dim)
128  {
129  	TkGridcell **cells, *cellrow;
130  	TkGridbeam *cols, *rows;
131  	Point olddim;
132  	int i;
133  	olddim = grid->dim;
134  	if(dim.x < olddim.x)
135  		dim.x = olddim.x;
136  	if(dim.y < olddim.y)
137  		dim.y = olddim.y;
138  	if(dim.y > olddim.y){
139  		cells = realloc(grid->cells, sizeof(TkGridcell*)*dim.y);
140  		if(cells == nil)
141  			return TkNomem;
142  		grid->cells = cells;
143  		for(i = olddim.y; i < dim.y; i++){
144  			cells[i] = malloc(sizeof(TkGridcell)*dim.x);
145  			if(cells[i] == nil){
146  				while(--i >= olddim.y)
147  					free(cells[i]);
148  				return TkNomem;
149  			}
150  		}
151  		rows = realloc(grid->rows, sizeof(TkGridbeam)*dim.y);
152  		if(rows == nil)
153  			return TkNomem;
154  		grid->rows = rows;
155  		initbeam(rows + olddim.y, dim.y - olddim.y);
156  		grid->dim.y = dim.y;
157  	}
158  
159  	if(dim.x > olddim.x){
160  		/*
161  		 * any newly allocated rows will have the correct number of
162  		 * columns, so we don't need to reallocate them
163  		 */
164  		cells = grid->cells;
165  		for(i = 0; i < olddim.y; i++){
166  			cellrow = realloc(cells[i], sizeof(TkGridcell) * dim.x);
167  			if(cellrow == nil)
168  				return TkNomem;	/* leak some earlier rows, but not permanently */
169  			memset(cellrow + olddim.x, 0, (dim.x-olddim.x)*sizeof(TkGridcell));
170  			cells[i] = cellrow;
171  		}
172  		cols = realloc(grid->cols, sizeof(TkGridbeam)*dim.x);
173  		if(cols == nil)
174  			return TkNomem;
175  		initbeam(cols + olddim.x, dim.x - olddim.x);
176  		grid->cols = cols;
177  		grid->dim.x = dim.x;
178  	}
179  	return nil;
180  }
181  
182  static TkGridbeam*
183  delbeams(TkGridbeam *beam, int nb, int x0, int x1)
184  {
185  	int i;
186  	TkGridbeam *b;
187  	for(i = x0; i < x1; i++)
188  		free(beam[i].name);
189  	memmove(&beam[x0], &beam[x1], sizeof(TkGridbeam) * (nb-x1));
190  	b = realloc(beam, sizeof(TkGridbeam) * (nb-(x1-x0)));
191  	return b ? b : beam;
192  }
193  
194  static void
195  delrows(TkGrid *grid, int y0, int y1)
196  {
197  	TkGridcell **cells;
198  	memmove(grid->cells+y0, grid->cells+y1, sizeof(TkGridcell*) * (grid->dim.y-y1));
199  	grid->dim.y -= (y1 - y0);
200  	cells = realloc(grid->cells, sizeof(TkGridcell*) * grid->dim.y);
201  	if(cells != nil || grid->dim.y == 0)
202  		grid->cells = cells;		/* can realloc to a smaller size ever fail? */
203  }
204  
205  static void
206  delcols(TkGrid *grid, int x0, int x1)
207  {
208  	TkGridcell **cells, *row;
209  	int y, ndx;
210  	Point dim;
211  	dim = grid->dim;
212  	ndx = dim.x - (x1 - x0);
213  	cells = grid->cells;
214  	for(y = 0; y < dim.y; y++){
215  		row = cells[y];
216  		memmove(row+x0, row+x1, sizeof(TkGridcell) * (dim.x - x1));
217  		row = realloc(row, sizeof(TkGridcell) * ndx);
218  		if(row != nil || ndx == 0)
219  			cells[y] = row;
220  	}
221  	grid->dim.x = ndx;
222  }
223  
224  /*
225   * insert items into rows/cols; the beam has already been expanded appropriately.
226   */
227  void
228  insbeams(TkGridbeam *beam, int nb, int x, int n)
229  {
230  	memmove(&beam[x+n], &beam[x], sizeof(TkGridbeam)*(nb-x-n));
231  	initbeam(beam+x, n);
232  }
233  
234  static char*
235  insrows(TkGrid *grid, int y0, int n)
236  {
237  	Point olddim;
238  	char *e;
239  	TkGridcell **cells, *tmp;
240  	int y;
241  
242  	olddim = grid->dim;
243  	if(y0 > olddim.y){
244  		n = y0 + n - olddim.y;
245  		y0 = olddim.y;
246  	}
247  
248  	e = ensuregridsize(grid, Pt(olddim.x, olddim.y + n));
249  	if(e != nil)
250  		return e;
251  	/*
252  	 * we know the extra rows will have been filled
253  	 * with blank, properly allocated rows, so just swap 'em with the
254  	 * ones that need moving.
255  	 */
256  	cells = grid->cells;
257  	for(y = olddim.y - 1; y >= y0; y--){
258  		tmp = cells[y + n];
259  		cells[y + n] = cells[y];
260  		cells[y] = tmp;
261  	}
262  	insbeams(grid->rows, grid->dim.y, y0, n);
263  	return nil;
264  }
265  
266  static char*
267  inscols(TkGrid *grid, int x0, int n)
268  {
269  	TkGridcell **cells;
270  	Point olddim;
271  	int y;
272  	char *e;
273  
274  	olddim = grid->dim;
275  	if(x0 > olddim.x){
276  		n = x0 + n - olddim.x;
277  		x0 = olddim.x;
278  	}
279  
280  	e = ensuregridsize(grid, Pt(olddim.x + n, olddim.y));
281  	if(e != nil)
282  		return e;
283  
284  	cells = grid->cells;
285  	for(y = 0; y < olddim.y; y++){
286  		memmove(cells[y] + x0 + n, cells[y] + x0, sizeof(TkGridcell) * (olddim.x - x0));
287  		memset(cells[y] + x0, 0, sizeof(TkGridcell) * n);
288  	}
289  	insbeams(grid->cols, grid->dim.x, x0, n);
290  	return nil;
291  }
292  
293  static int
294  maximum(int a, int b)
295  {
296  	if(a > b)
297  		return a;
298  	return b;
299  }
300  
301  /*
302   * return the width of cols/rows between x0 and x1 in the beam,
303   * excluding the padding at either end, but including padding in the middle.
304   */
305  static int
306  beamsize(TkGridbeam *cols, int x0, int x1)
307  {
308  	int tot, fpad, x;
309  
310  	if(x0 >= x1)
311  		return 0;
312  
313  	tot = cols[x0].act;
314  	fpad = cols[x0].pad;
315  	for(x = x0 + 1; x < x1; x++){
316  		tot += cols[x].act + maximum(cols[x].pad, fpad);
317  		fpad = cols[x].pad;
318  	}
319  	return tot;
320  }
321  
322  /*
323   * return starting position of cell index on beam, relative
324   * to top-left of grid
325   */
326  static int
327  beamcellpos(TkGridbeam *beam, int blen, int index)
328  {
329  	int x;
330  	if(blen == 0 || index >= blen || index < 0)
331  		return 0;
332  	x = beam[0].pad + beamsize(beam, 0, index);
333  	if(index > 0)
334  		x += maximum(beam[index-1].pad, beam[index].pad);
335  	return x;
336  }
337  
338  static Rectangle
339  cellbbox(TkGrid *grid, Point pos)
340  {
341  	Point dim;
342  	Rectangle r;
343  
344  	dim = grid->dim;
345  	if(pos.x > dim.x)
346  		pos.x = dim.x;
347  	if(pos.y > dim.y)
348  		pos.y = dim.y;
349  
350  	r.min.x = beamcellpos(grid->cols, dim.x, pos.x);
351  	r.min.y = beamcellpos(grid->rows, dim.y, pos.y);
352  	if(pos.x == dim.x)
353  		r.max.x = r.min.x;
354  	else
355  		r.max.x = r.min.x + grid->cols[pos.x].act;
356  	if(pos.y == dim.y)
357  		r.max.y = r.min.y;
358  	else
359  		r.max.y = r.min.y + grid->rows[pos.y].act;
360  	return rectaddpt(r, grid->origin);
361  }
362  
363  /*
364   * return true ifthere are any spanning cells covering row _index_
365   */
366  static int
367  gridrowhasspan(TkGrid *grid, int index)
368  {
369  	int i, d;
370  	Point dim;
371  	TkGridcell *cell;
372  
373  	dim = grid->dim;
374  	if(index > 0 && index < dim.y){
375  		for(i = 0; i < dim.x; i++){
376  			cell = &grid->cells[index][i];
377  			if(cell->tk != nil){
378  				d = cell->span.x;
379  				if(d == 0)
380  					return 1;
381  				i += d - 1;
382  			}
383  		}
384  	}
385  	return 0;
386  }
387  
388  /*
389   * return true ifthere are any spanning cells covering column _index_
390   */
391  static int
392  gridcolhasspan(TkGrid *grid, int index)
393  {
394  	int i, d;
395  	Point dim;
396  	TkGridcell *cell;
397  
398  	dim = grid->dim;
399  	if(index > 0 && index < dim.x){
400  		for(i = 0; i < dim.y; i++){
401  			cell = &grid->cells[i][index];
402  			if(cell->tk != nil){
403  				d = cell->span.y;
404  				if(d == 0)
405  					return 1;
406  				i += d - 1;
407  			}
408  		}
409  	}
410  	return 0;
411  }
412  
413  /*
414   * find cell that's spanning the grid position p
415   */
416  static int
417  findspan(TkGrid *grid, Point p, Point *cp)
418  {
419  	Point dim;
420  	TkGridcell **cells;
421  	Tk *tk;
422  
423  	dim = grid->dim;
424  	cells = grid->cells;
425  
426  	if(p.x < 0 || p.y < 0 || p.x >= dim.x || p.y >= dim.y)
427  		return 0;
428  
429  	if(cells[p.y][p.x].tk == nil)
430  		return 0;
431  
432  	if(cells[p.y][p.x].span.x == 0){
433  		tk = cells[p.y][p.x].tk;
434  		for(; p.y >= 0; p.y--)
435  			if(cells[p.y][p.x].tk != tk)
436  				break;
437  		p.y++;
438  		for(; p.x >= 0; p.x--)
439  			if(cells[p.y][p.x].tk != tk)
440  				break;
441  		p.x++;
442  	}
443  	*cp = p;
444  	return 1;
445  }
446  
447  static int
448  parsegridindex(TkGridbeam *beam, int blen, char *s)
449  {
450  	int n, i;
451  	char *e;
452  
453  	if(s[0] == '\0')
454  		return -1;
455  
456  	n = strtol(s, &e, 10);
457  	if(*e == '\0')
458  		return n;
459  
460  	if(strcmp(s, "end") == 0)
461  		return blen;
462  
463  	for(i = 0; i < blen; i++)
464  		if(beam[i].name != nil && strcmp(beam[i].name, s) == 0)
465  			return i;
466  	return -1;
467  }
468  
469  static char*
470  tkgridconfigure(TkTop *t, TkGridparam *p, TkName *names)
471  {
472  	TkGrid *grid;
473  	TkGridcell **cells;
474  	TkName *n;
475  	Tk *tkf, *tkp;
476  	Point dim, pos, q, span, startpos;
477  	int maxcol, c, i, j, x;
478  	char *e;
479  
480  	if(names == nil)
481  		return nil;
482  
483  	if(p->span.x < 1 || p->span.y < 1)
484  		return TkBadvl;
485  
486  	tkf = nil;
487  
488  	maxcol = 0;
489  	for(n = names; n; n = n->link){
490  		c = n->name[0];
491  		if((c=='-' || c=='^' || c=='x') && n->name[1] == '\0'){
492  			maxcol++;
493  			continue;
494  		}
495  		tkp = tklook(t, n->name, 0);
496  		if(tkp == nil){
497  			tkerr(t, n->name);
498  			return TkBadwp;
499  		}
500  		if(tkp->flag & Tkwindow)
501  			return TkIstop;
502  		if(tkp->parent != nil)
503  			return TkWpack;
504  
505  		/*
506  		 * unpacking now does give an non-reversible side effect
507  		 * ifthere's an error encountered later, but also means
508  		 * that a widget repacked in the same grid will
509  		 * have its original cell still available
510  		 */
511  		if(tkp->master != nil){
512  			tkpackqit(tkp->master);
513  			tkdelpack(tkp);
514  		}
515  		if(tkf == nil)
516  			tkf = tkp;
517  		n->obj = tkp;
518  		tkp->flag &= ~Tkgridpack;
519  		maxcol += p->span.x;
520  	}
521  
522  	if(p->in == nil && tkf != nil)
523  		p->in = tklook(t, tkf->name->name, 1);
524  
525  	if(p->in == nil)
526  		return TkNomaster;
527  
528  	grid = p->in->grid;
529  	if(grid == nil && p->in->slave != nil)
530  		return TkNotgrid;
531  
532  	if(grid == nil){
533  		grid = malloc(sizeof(TkGrid));
534  		if(grid == nil)
535  			return TkNomem;
536  		p->in->grid = grid;
537  	}
538  
539  	dim = grid->dim;
540  	pos = ZP;
541  	if(p->row != nil){
542  		pos.y = parsegridindex(grid->rows, dim.y, p->row);
543  		if(pos.y < 0)
544  			return TkBadix;
545  	}
546  	if(p->col != nil){
547  		pos.x = parsegridindex(grid->cols, dim.x, p->col);
548  		if(pos.x < 0)
549  			return TkBadix;
550  	}
551  	/*
552  	 * ifrow is not specified, find first unoccupied row
553  	 */
554  	if(p->row == nil){
555  		for(pos.y = 0; pos.y < dim.y; pos.y++){
556  			for(x = 0; x < dim.x; x++)
557  				if(grid->cells[pos.y][x].tk != nil)
558  					break;
559  			if(x == dim.x)
560  				break;
561  		}
562  	}
563  	e = ensuregridsize(grid, Pt(pos.x + maxcol, pos.y + p->span.y));
564  	if(e != nil)
565  		return e;
566  	cells = grid->cells;
567  
568  	startpos = pos;
569  	/*
570  	 * check that all our grid cells are empty, and that row/col spans
571  	 * are well formed
572  	 */
573  	n = names;
574  	while(n != nil){
575  		c = n->name[0];
576  		switch (c){
577  		case 'x':
578  			n = n->link;
579  			pos.x++;
580  			break;
581  		case '^':
582  			if(findspan(grid, Pt(pos.x, pos.y - 1), &q) == 0)
583  				return TkBadspan;
584  			span = cells[q.y][q.x].span;
585  			for(i = 0; i < span.x; i++){
586  				if(n == nil || strcmp(n->name, "^"))
587  					return TkBadspan;
588  				if(cells[pos.y][pos.x + i].tk != nil)
589  					return TkBadgridcell;
590  				n = n->link;
591  			}
592  			pos.x += span.x;
593  			break;
594  		case '-':
595  			return TkBadspan;
596  		case '.':
597  			tkp = n->obj;
598  			if(tkisslave(p->in, tkp))
599  				return TkRecur;
600  			n = n->link;
601  			if(tkp->flag & Tkgridpack)
602  				return TkWpack;
603  			tkp->flag |= Tkgridpack;
604  			span = p->span;
605  			for(; n != nil && strcmp(n->name, "-") == 0; n = n->link)
606  				span.x++;
607  			for(i = pos.x; i < pos.x + span.x; i++)
608  				for(j = pos.y; j < pos.y + span.y; j++)
609  					if(cells[j][i].tk != nil)
610  						return TkBadgridcell;
611  			pos.x = i;
612  			break;
613  		}
614  	}
615  
616  	/*
617  	 * actually insert the items into the grid
618  	 */
619  	n = names;
620  	pos = startpos;
621  	while(n != nil){
622  		c = n->name[0];
623  		switch (c){
624  		case 'x':
625  			n = n->link;
626  			pos.x++;
627  			break;
628  		case '^':
629  			findspan(grid, Pt(pos.x, pos.y - 1), &q);
630  			span = cells[q.y][q.x].span;
631  			tkf = cells[q.y][q.x].tk;
632  			if(q.y + span.y == pos.y)
633  				cells[q.y][q.x].span.y++;
634  
635  			for(i = 0; i < span.x; i++){
636  				cells[pos.y][pos.x++].tk = tkf;
637  				n = n->link;
638  			}
639  			break;
640  		case '.':
641  			tkf = n->obj;
642  			n = n->link;
643  			span = p->span;
644  			for(; n != nil && strcmp(n->name, "-") == 0; n = n->link)
645  				span.x++;
646  			for(i = pos.x; i < pos.x + span.x; i++)
647  				for(j = pos.y; j < pos.y + span.y; j++)
648  					cells[j][i].tk = tkf;
649  			cells[pos.y][pos.x].span = span;
650  			tkf->master = p->in;
651  			tkf->next = p->in->slave;
652  			p->in->slave = tkf;
653  			if(p->in->flag & Tksubsub)
654  				tksetbits(tkf, Tksubsub);
655  			tkgridsetopt(p, tkf);
656  			pos.x = i;
657  			break;
658  		}
659  	}
660  	tkpackqit(p->in);
661  	tkrunpack(t);
662  	return nil;
663  }
664  
665  void
666  tkgriddelslave(Tk *tk)
667  {
668  	int y, x, yy;
669  	TkGrid *grid;
670  	TkGridcell **cells, *cell;
671  	Point dim, span;
672  
673  	if(tk == nil || tk->master == nil || tk->master->grid == nil)
674  		return;
675  	grid = tk->master->grid;
676  	cells = grid->cells;
677  	dim = grid->dim;
678  	for(y = 0; y < dim.y; y++){
679  		for(x = 0; x < dim.x; x++){
680  			cell = &cells[y][x];
681  			if(cell->tk == tk){
682  				span = cell->span;
683  				for(yy = y; yy < y + span.y; yy++)
684  					memset(cells[yy] + x, 0, span.x * sizeof(TkGridcell));
685  				return;
686  			}
687  		}
688  	}
689  }
690  
691  char*
692  tkgetgridmaster(TkTop *t, char **arg, char *buf, char *ebuf, Tk **master)
693  {
694  	TkGrid *grid;
695  
696  	*arg = tkword(t, *arg, buf, ebuf, nil);
697  	*master = tklook(t, buf, 0);
698  	if(*master == nil)
699  		return TkBadwp;
700  	grid = (*master)->grid;
701  	if(grid == nil && (*master)->slave != nil)
702  		return TkNotgrid;
703  	return nil;
704  }
705  
706  static int
707  gridfindloc(TkGridbeam *beam, int blen, int f)
708  {
709  	int x, i, fpad;
710  	if(blen == 0 || f < 0)
711  		return -1;
712  
713  	fpad = 0;
714  	x =  0;
715  	for(i = 0; i < blen; i++){
716  		x += maximum(fpad, beam[i].pad);
717  		if(x <= f && f < x + beam[i].act)
718  			return i;
719  		x += beam[i].act;
720  	}
721  	return -1;
722  }
723  
724  static char*
725  tkgridcellinfo(TkTop *t, char *arg, char **val, char *buf, char *ebuf)
726  {
727  	/* grid cellinfo master x y */
728  	Tk *master;
729  	char *e;
730  	Point p;
731  	TkGrid *grid;
732  	TkGridcell **cells;
733  
734  	e = tkgetgridmaster(t, &arg, buf, ebuf, &master);
735  	if(e != nil || master->grid == nil)
736  		return e;
737  	grid = master->grid;
738  
739  	e = tkfracword(t, &arg, &p.x, nil);
740  	if(e != nil)
741  		return e;
742  	e = tkfracword(t, &arg, &p.y, nil);
743  	if(e != nil)
744  		return e;
745  
746  	p.x = TKF2I(p.x);
747  	p.y = TKF2I(p.y);
748  	if(p.x < 0 || p.x >= grid->dim.x || p.y < 0 || p.y >= grid->dim.y)
749  		return nil;
750  
751  	if(!findspan(grid, p, &p))
752  		return nil;
753  
754  	cells = grid->cells;
755  	return tkvalue(val, "%s -in %s -column %d -row %d -columnspan %d -rowspan %d",
756  		cells[p.y][p.x].tk->name->name,
757  		cells[p.y][p.x].tk->master->name->name, p.x, p.y,
758  		cells[p.y][p.x].span.x, cells[p.y][p.x].span.y);
759  }
760  
761  static char*
762  tkgridlocation(TkTop *t, char *arg, char **val, char *buf, char *ebuf)
763  {
764  	/* grid location master x y */
765  	Tk *master;
766  	char *e;
767  	Point p;
768  	int col, row;
769  	TkGrid *grid;
770  
771  	e = tkgetgridmaster(t, &arg, buf, ebuf, &master);
772  	if(e != nil || master->grid == nil)
773  		return e;
774  	grid = master->grid;
775  
776  	e = tkfracword(t, &arg, &p.x, nil);
777  	if(e != nil)
778  		return e;
779  	e = tkfracword(t, &arg, &p.y, nil);
780  	if(e != nil)
781  		return e;
782  
783  	p.x = TKF2I(p.x);
784  	p.y = TKF2I(p.y);
785  
786  	p = subpt(p, grid->origin);
787  	col = gridfindloc(grid->cols, grid->dim.x, p.x);
788  	row = gridfindloc(grid->rows, grid->dim.y, p.y);
789  	if(col < 0 || row < 0)
790  		return nil;
791  	return tkvalue(val, "%d %d", col, row);
792  }
793  
794  static char*
795  tkgridinfo(TkTop *t, char *arg, char **val, char *buf, char *ebuf)
796  {
797  	Tk *tk;
798  	TkGrid *grid;
799  	int x, y;
800  	Point dim;
801  	TkGridcell *row;
802  
803  	tkword(t, arg, buf, ebuf, nil);
804  	tk = tklook(t, buf, 0);
805  	if(tk == nil)
806  		return TkBadwp;
807  	if(tk->master == nil || tk->master->grid == nil)
808  		return TkNotgrid;
809  	grid = tk->master->grid;
810  	dim = grid->dim;
811  	for(y = 0; y < dim.y; y++){
812  		row = grid->cells[y];
813  		for(x = 0; x < dim.x; x++)
814  			if(row[x].tk == tk)
815  				goto Found;
816  	}
817  	return TkNotgrid;		/* should not happen */
818  Found:
819  	return tkvalue(val, "-in %s -column %d -row %d -columnspan %d -rowspan %d",
820  		tk->master->name->name, x, y, grid->cells[y][x].span.x, grid->cells[y][x].span.y);
821  }
822  
823  static char*
824  tkgridforget(TkTop *t, char *arg, char *buf, char *ebuf)
825  {
826  	Tk *tk;
827  	for(;;){
828  		arg = tkword(t, arg, buf, ebuf, nil);
829  		if(arg == nil || buf[0] == '\0')
830  			break;
831  		tk = tklook(t, buf, 0);
832  		if(tk == nil){
833  			tkrunpack(t);
834  			tkerr(t, buf);
835  			return TkBadwp;
836  		}
837  		tkpackqit(tk->master);
838  		tkdelpack(tk);
839  	}
840  	tkrunpack(t);
841  	return nil;
842  }
843  
844  static char*
845  tkgridslaves(TkTop *t, char *arg, char **val, char *buf, char *ebuf)
846  {
847  	Tk *master, *tk;
848  	char *fmt;
849  	int i, isrow, index;
850  	TkGrid *grid;
851  	TkGridcell *cell;
852  	char *e;
853  	e = tkgetgridmaster(t, &arg, buf, ebuf, &master);
854  	if(e != nil || master->grid == nil)
855  		return e;
856  	grid = master->grid;
857  	arg = tkword(t, arg, buf, ebuf, nil);
858  	fmt = "%s";
859  	if(buf[0] == '\0'){
860  		for(tk = master->slave; tk != nil; tk = tk->next){
861  			if(tk->name != nil){
862  				e = tkvalue(val, fmt, tk->name->name);
863  				if(e != nil)
864  					return e;
865  				fmt = " %s";
866  			}
867  		}
868  		return nil;
869  	}
870  	if(strcmp(buf, "-row") == 0)
871  		isrow = 1;
872  	else if(strcmp(buf, "-column") == 0)
873  		isrow = 0;
874  	else
875  		return TkBadop;
876  	tkword(t, arg, buf, ebuf, nil);
877  	if(isrow)
878  		index = parsegridindex(grid->rows, grid->dim.y, buf);
879  	else
880  		index = parsegridindex(grid->cols, grid->dim.x, buf);
881  	if(index < 0)
882  		return TkBadix;
883  	if(isrow){
884  		if(index >= grid->dim.y)
885  			return nil;
886  		for(i = 0; i < grid->dim.x; i++){
887  			cell = &grid->cells[index][i];
888  			if(cell->tk != nil && cell->span.x > 0 && cell->tk->name != nil){
889  				e = tkvalue(val, fmt, cell->tk->name->name);
890  				if(e != nil)
891  					return e;
892  				fmt = " %s";
893  			}
894  		}
895  	} else{
896  		if(index >= grid->dim.x)
897  			return nil;
898  		for(i = 0; i < grid->dim.y; i++){
899  			cell = &grid->cells[i][index];
900  			if(cell->tk != nil && cell->span.x > 0 && cell->tk->name != nil){
901  				e = tkvalue(val, fmt, cell->tk->name->name);
902  				if(e != nil)
903  					return e;
904  				fmt = " %s";
905  			}
906  		}
907  	}
908  
909  	return nil;
910  }
911  
912  static char*
913  tkgriddelete(TkTop *t, char *arg, char *buf, char *ebuf, int delrow)
914  {
915  	Tk *master, **l, *f;
916  	TkGrid *grid;
917  	TkGridbeam *beam;
918  	int blen, i0, i1, x, y;
919  	Point dim;
920  	TkGridcell **cells;
921  	char *e;
922  
923  	/*
924  	 * grid (columndelete|rowdelete) master index0 ?index1?
925  	 */
926  
927  	e = tkgetgridmaster(t, &arg, buf, ebuf, &master);
928  	if(e != nil || master->grid == nil)
929  		return e;
930  	grid = master->grid;
931  
932  	if(delrow){
933  		beam = grid->rows;
934  		blen = grid->dim.y;
935  	} else{
936  		beam = grid->cols;
937  		blen = grid->dim.x;
938  	}
939  
940  	arg = tkword(t, arg, buf, ebuf, nil);
941  	i0 = parsegridindex(beam, blen, buf);
942  	if(i0 < 0)
943  		return TkBadix;
944  
945  	tkword(t, arg, buf, ebuf, nil);
946  	if(buf[0] == '\0')
947  		i1 = i0 + 1;
948  	else
949  		i1 = parsegridindex(beam, blen, buf);
950  	if(i1 < 0 || i0 > i1)
951  		return TkBadix;
952  	if(i0 > blen || i0 == i1)
953  		return nil;
954  	if(i1 > blen)
955  		i1 = blen;
956  	cells = grid->cells;
957  	dim = grid->dim;
958  	if(delrow){
959  		if(gridrowhasspan(grid, i0) || gridrowhasspan(grid, i1))
960  			return TkBadgridcell;
961  		for(y = i0; y < i1; y++)
962  			for(x = 0; x < dim.x; x++)
963  				if(cells[y][x].tk != nil)
964  					cells[y][x].tk->flag |= Tkgridremove;
965  		delrows(grid, i0, i1);
966  		grid->rows = delbeams(beam, blen, i0, i1);
967  	} else{
968  		if(gridcolhasspan(grid, i0) || gridcolhasspan(grid, i1))
969  			return TkBadgridcell;
970  		for(y = 0; y < dim.y; y++)
971  			for(x = i0; x < i1; x++)
972  				if(cells[y][x].tk != nil)
973  					cells[y][x].tk->flag |= Tkgridremove;
974  		delcols(grid, i0, i1);
975  		grid->cols = delbeams(beam, blen, i0, i1);
976  	}
977  	l = &master->slave;
978  	for(f = *l; f; f = f->next){
979  		if(f->flag & Tkgridremove){
980  			*l = f->next;
981  			f->master = nil;
982  			f->flag &= ~Tkgridremove;
983  		} else
984  			l = &f->next;
985  	}
986  	tkpackqit(master);
987  	tkrunpack(t);
988  	return nil;
989  }
990  
991  
992  static char*
993  tkgridinsert(TkTop *t, char *arg, char *buf, char *ebuf, int insertrow)
994  {
995  	int index, count;
996  	Point dim;
997  	Tk *master;
998  	TkGrid *grid;
999  	int gotarg;
1000  	char *e;
1001  
1002  	/*
1003  	 * grid (rowinsert|columninsert) master index ?count?
1004  	 * it's an error ifthe insert splits any spanning cells.
1005  	 */
1006  	e = tkgetgridmaster(t, &arg, buf, ebuf, &master);
1007  	if(e != nil || master->grid == nil)
1008  		return e;
1009  	grid = master->grid;
1010  	dim = grid->dim;
1011  
1012  	arg = tkword(t, arg, buf, ebuf, nil);
1013  	if(insertrow)
1014  		index = parsegridindex(grid->rows, dim.y, buf);
1015  	else
1016  		index = parsegridindex(grid->cols, dim.x, buf);
1017  	if(index < 0 || index > (insertrow ? dim.y : dim.x))
1018  		return TkBadix;
1019  
1020  	tkword(t, arg, buf, ebuf, &gotarg);
1021  	if(gotarg){
1022  		count = strtol(buf, &buf, 10);
1023  		if(buf[0] != '\0' || count < 0)
1024  			return TkBadvl;
1025  	} else
1026  		count = 1;
1027  
1028  	/*
1029  	 * check that we're not splitting any spanning cells
1030  	 */
1031  	if(insertrow){
1032  		if(gridrowhasspan(grid, index))
1033  			return TkBadgridcell;
1034  		e = insrows(grid, index, count);
1035  	} else{
1036  		if(gridcolhasspan(grid, index))
1037  			return TkBadgridcell;
1038  		e = inscols(grid, index, count);
1039  	}
1040  	tkpackqit(master);
1041  	tkrunpack(t);
1042  	return e;
1043  }
1044  
1045  /*
1046   * (rowconfigure|columnconfigure) master index ?-option value ...?
1047   */
1048  static char*
1049  tkbeamconfigure(TkTop *t, char *arg, int isrow)
1050  {
1051  	TkBeamparam p;
1052  	TkOptab tko[2];
1053  	TkName *names;
1054  	Tk *master;
1055  	int index;
1056  	TkGrid *grid;
1057  	TkGridbeam *beam;
1058  	Point dim;
1059  	char *e;
1060  
1061  	p.equalise = BoolX;
1062  	p.name = nil;
1063  	p.weight = -1;
1064  	p.minsize = -1;
1065  	p.maxsize = -1;
1066  	p.pad = -1;
1067  
1068  	tko[0].ptr = &p;
1069  	tko[0].optab = beamopts;
1070  	tko[1].ptr = nil;
1071  
1072  	names = nil;
1073  	e = tkparse(t, arg, tko, &names);
1074  	if(e != nil)
1075  		return e;
1076  
1077  	if(names == nil || names->link == nil)
1078  		return TkBadvl;
1079  
1080  	master = tklook(t, names->name, 0);
1081  	if(master == nil)
1082  		return TkBadwp;
1083  
1084  	grid = master->grid;
1085  	if(grid == nil){
1086  		if(master->slave != nil)
1087  			return TkNotgrid;
1088  		grid = master->grid = malloc(sizeof(TkGrid));
1089  		if(grid == nil){
1090  			tkfreename(names);
1091  			return TkNomem;
1092  		}
1093  	}
1094  
1095  	if(isrow){
1096  		index = parsegridindex(grid->rows, grid->dim.y, names->link->name);
1097  	} else
1098  		index = parsegridindex(grid->cols, grid->dim.x, names->link->name);
1099  	if(index < 0){
1100  		e = TkBadix;
1101  		goto Error;
1102  	}
1103  	if(isrow)
1104  		dim = Pt(grid->dim.x, index + 1);
1105  	else
1106  		dim = Pt(index + 1, grid->dim.y);
1107  	e = ensuregridsize(grid, dim);
1108  	if(e != nil)
1109  		goto Error;
1110  
1111  	if(isrow)
1112  		beam = &grid->rows[index];
1113  	else
1114  		beam = &grid->cols[index];
1115  
1116  	if(p.minsize >= 0)
1117  		beam->minsize = p.minsize;
1118  	if(p.maxsize >= 0)
1119  		beam->maxsize = p.maxsize;
1120  	if(p.weight >= 0)
1121  		beam->weight = p.weight;
1122  	if(p.pad >= 0)
1123  		beam->pad = p.pad;
1124  	if(p.name != nil){
1125  		free(beam->name);
1126  		beam->name = p.name;
1127  	}
1128  	if(p.equalise != BoolX)
1129  		beam->equalise = p.equalise == BoolT;
1130  
1131  	tkpackqit(master);
1132  	tkrunpack(t);
1133  
1134  Error:
1135  	tkfreename(names);
1136  	return e;
1137  }
1138  
1139  char*
1140  tkgridsize(TkTop *t, char *arg, char **val, char *buf, char *ebuf)
1141  {
1142  	Tk *master;
1143  	TkGrid *grid;
1144  	char *e;
1145  
1146  	e = tkgetgridmaster(t, &arg, buf, ebuf, &master);
1147  	if(e != nil)
1148  		return e;
1149  	grid = master->grid;
1150  	if(grid == nil)
1151  		return tkvalue(val, "0 0");
1152  	else
1153  		return tkvalue(val, "%d %d", grid->dim.x, grid->dim.y);
1154  }
1155  
1156  char*
1157  tkgridbbox(TkTop *t, char *arg, char **val, char *buf, char *ebuf)
1158  {
1159  	Point p0, p1;
1160  	Tk *master;
1161  	TkGrid *grid;
1162  	char *e;
1163  	int gotarg;
1164  	Point dim;
1165  	Rectangle r;
1166  
1167  	e = tkgetgridmaster(t, &arg, buf, ebuf, &master);
1168  	if(e != nil || master->grid == nil)
1169  		return e;
1170  
1171  	grid = master->grid;
1172  	dim = grid->dim;
1173  	arg = tkword(t, arg, buf, ebuf, &gotarg);
1174  	if(!gotarg){
1175  		p0 = ZP;
1176  		p1 = dim;
1177  	} else{
1178  		p0.x = parsegridindex(grid->cols, dim.x, buf);
1179  		arg = tkword(t, arg, buf, ebuf, &gotarg);
1180  		if(!gotarg)
1181  			return TkFewpt;
1182  		p0.y = parsegridindex(grid->rows, dim.y, buf);
1183  		arg = tkword(t, arg, buf, ebuf, &gotarg);
1184  		if(!gotarg){
1185  			p1 = p0;
1186  		} else{
1187  			p1.x = parsegridindex(grid->cols, dim.x, buf);
1188  			arg = tkword(t, arg, buf, ebuf, &gotarg);
1189  			if(!gotarg)
1190  				return TkFewpt;
1191  			p1.y = parsegridindex(grid->rows, dim.y, buf);
1192  		}
1193  	}
1194  	if(p0.x < 0 || p0.y < 0 || p1.x < 0 || p1.y < 0)
1195  		return TkBadix;
1196  
1197  	r = cellbbox(grid, p0);
1198  	if(!eqpt(p0, p1))
1199  		combinerect(&r, cellbbox(grid, p1));
1200  	return tkvalue(val, "%d %d %d %d", r.min.x, r.min.y, r.max.x, r.max.y);
1201  }
1202  
1203  char*
1204  tkgridindex(TkTop *t, char *arg, char **val, char *buf, char *ebuf, int isrow)
1205  {
1206  	Tk *master;
1207  	TkGrid *grid;
1208  	TkGridbeam *beam;
1209  	int blen, i;
1210  
1211  	arg = tkword(t, arg, buf, ebuf, nil);
1212  	master = tklook(t, buf, 0);
1213  	if(master == nil)
1214  		return TkBadwp;
1215  	tkword(t, arg, buf, ebuf, nil);
1216  	grid = master->grid;
1217  	if(grid == nil){
1218  		beam = nil;
1219  		blen = 0;
1220  	} else if(isrow){
1221  		beam = grid->rows;
1222  		blen = grid->dim.y;
1223  	} else{
1224  		beam = grid->cols;
1225  		blen = grid->dim.x;
1226  	}
1227  	i = parsegridindex(beam, blen, buf);
1228  	if(i < 0)
1229  		return TkBadix;
1230  	return tkvalue(val, "%d", i);
1231  }
1232  
1233  void
1234  tkfreegrid(TkGrid *grid)
1235  {
1236  	Point dim;
1237  	int i;
1238  	dim = grid->dim;
1239  	for(i = 0; i < dim.x; i++)
1240  		free(grid->cols[i].name);
1241  	for(i = 0; i < dim.y; i++)
1242  		free(grid->rows[i].name);
1243  	for(i = 0; i < dim.y; i++)
1244  		free(grid->cells[i]);
1245  	free(grid->cells);
1246  	free(grid->rows);
1247  	free(grid->cols);
1248  	free(grid);
1249  }
1250  
1251  char*
1252  tkgrid(TkTop *t, char *arg, char **val)
1253  {
1254  	TkGridparam *p;
1255  	TkOptab tko[2];
1256  	TkName *names;
1257  	char *e, *w, *buf;
1258  
1259  	buf = mallocz(Tkmaxitem, 0);
1260  	if(buf == nil)
1261  		return TkNomem;
1262  
1263  	w = tkword(t, arg, buf, buf+Tkmaxitem, nil);
1264  	if('a' <= buf[0] && buf[0] <= 'z'){
1265  		if(strcmp(buf, "debug") == 0){
1266  			Tk *tk;
1267  			e = tkgetgridmaster(t, &w, buf, buf+Tkmaxitem, &tk);
1268  			if(e == nil)
1269  				printgrid(tk->grid);
1270  		} else
1271  		if(strcmp(buf, "forget") == 0)
1272  			e = tkgridforget(t, w, buf, buf+Tkmaxitem);
1273  		else if(strcmp(buf, "propagate") == 0)
1274  			e = tkpropagate(t, w);
1275  		else if(strcmp(buf, "slaves") == 0)
1276  			e = tkgridslaves(t, w, val, buf, buf+Tkmaxitem);
1277  		else if(strcmp(buf, "rowconfigure") == 0)
1278  			e = tkbeamconfigure(t, w, 1);
1279  		else if(strcmp(buf, "columnconfigure") == 0)
1280  			e = tkbeamconfigure(t, w, 0);
1281  		else if(strcmp(buf, "rowinsert") == 0)
1282  			e = tkgridinsert(t, w, buf, buf+Tkmaxitem, 1);
1283  		else if(strcmp(buf, "columninsert") == 0)
1284  			e = tkgridinsert(t, w, buf, buf+Tkmaxitem, 0);
1285  		else if(strcmp(buf, "size") == 0)
1286  			e = tkgridsize(t, w, val, buf, buf+Tkmaxitem);
1287  		else if(strcmp(buf, "rowdelete") == 0)
1288  			e = tkgriddelete(t, w, buf, buf+Tkmaxitem, 1);
1289  		else if(strcmp(buf, "columndelete") == 0)
1290  			e = tkgriddelete(t, w, buf, buf+Tkmaxitem, 0);
1291  		else if(strcmp(buf, "rowindex") == 0)
1292  			e = tkgridindex(t, w, val, buf, buf+Tkmaxitem, 1);
1293  		else if(strcmp(buf, "columnindex") == 0)
1294  			e = tkgridindex(t, w, val, buf, buf+Tkmaxitem, 0);
1295  		else if(strcmp(buf, "bbox") == 0)
1296  			e = tkgridbbox(t, w, val, buf, buf+Tkmaxitem);
1297  		else if(strcmp(buf, "location") == 0)
1298  			e = tkgridlocation(t, w, val, buf, buf+Tkmaxitem);
1299  		else if(strcmp(buf, "cellinfo") == 0)
1300  			e = tkgridcellinfo(t, w, val, buf, buf+Tkmaxitem);
1301  		else if(strcmp(buf, "info") == 0)
1302  			e = tkgridinfo(t, w, val, buf, buf+Tkmaxitem);
1303  		else{
1304  			tkerr(t, buf);
1305  			e = TkBadcm;
1306  		}
1307  	} else{
1308  		p = malloc(sizeof(TkGridparam));
1309  		if(p == nil)
1310  			return TkNomem;
1311  		tko[0].ptr = p;
1312  		tko[0].optab = opts;
1313  		tko[1].ptr = nil;
1314  
1315  		p->span.x = 1;
1316  		p->span.y = 1;
1317  		p->pad.x = p->pad.y = p->ipad.x = p->ipad.y = -1;
1318  		p->sticky = -1;
1319  
1320  		names = nil;
1321  		e = tkparse(t, arg, tko, &names);
1322  		if(e != nil){
1323  			free(p);
1324  			return e;
1325  		}
1326  
1327  		e = tkgridconfigure(t, p, names);
1328  		free(p->row);
1329  		free(p->col);
1330  		free(p);
1331  		tkfreename(names);
1332  	}
1333  	free(buf);
1334  	return e;
1335  }
1336  
1337  /*
1338   * expand widths of rows/columns according to weight.
1339   * return amount of space still left over.
1340   */
1341  static int
1342  expandwidths(int x0, int x1, int totwidth, TkGridbeam *cols, int expandzero)
1343  {
1344  	int share, x, slack, m, w, equal;
1345  
1346  	if(x0 >= x1)
1347  		return 0;
1348  
1349  	share = 0;
1350  	for(x = x0; x < x1; x++)
1351  		share += cols[x].weight;
1352  
1353  	slack = totwidth - beamsize(cols, x0, x1);
1354  	if(slack <= 0)
1355  		return 0;
1356  
1357  	if(share == 0 && expandzero){
1358  		share = x1 - x0;
1359  		equal = 1;
1360  	} else
1361  		equal = 0;
1362  
1363  	for(x = x0; x < x1 && share > 0 ; x++){
1364  		w = equal ? 1 : cols[x].weight;
1365  		m = slack * w / share;
1366  		cols[x].act += m;
1367  		slack -= m;
1368  		share -= w;
1369  	}
1370  	return slack;
1371  }
1372  
1373  static void
1374  gridequalise(TkGridbeam *beam, int blen)
1375  {
1376  	int i, max;
1377  
1378  	max = 0;
1379  	for(i = 0; i < blen; i++)
1380  		if(beam[i].equalise == BoolT && beam[i].act > max)
1381  			max = beam[i].act;
1382  
1383  	if(max > 0)
1384  		for(i = 0; i < blen; i++)
1385  			if(beam[i].equalise == BoolT)
1386  				beam[i].act = max;
1387  }
1388  
1389  /*
1390   * take into account min/max beam sizes.
1391   * max takes precedence
1392   */
1393  static void
1394  beamminmax(TkGridbeam *beam, int n)
1395  {
1396  	TkGridbeam *e;
1397  	e = &beam[n];
1398  	for(; beam < e; beam++){
1399  		if(beam->act < beam->minsize)
1400  			beam->act = beam->minsize;
1401  		if(beam->act > beam->maxsize)
1402  			beam->act = beam->maxsize;
1403  	}
1404  }
1405  
1406  int
1407  tkgridder(Tk *master)
1408  {
1409  	TkGrid *grid;
1410  	TkGridcell **cells, *cell;
1411  	TkGridbeam *rows, *cols;
1412  	TkGeom pos;
1413  	Point org;
1414  	Tk *slave;
1415  	int dx, dy, x, y, w, bw2, fpadx, fpady;
1416  	Point req;
1417  
1418  	grid = master->grid;
1419  	dx = grid->dim.x;
1420  	dy = grid->dim.y;
1421  	cells = grid->cells;
1422  	rows = grid->rows;
1423  	cols = grid->cols;
1424  
1425  	for(x = 0; x < dx; x++)
1426  		cols[x].act = 0;
1427  
1428  	/* calculate column widths and row heights (ignoring multi-column cells) */
1429  	for(y = 0; y < dy; y++){
1430  		rows[y].act = 0;
1431  		for(x = 0; x < dx; x++){
1432  			cell = &cells[y][x];
1433  			if((slave = cell->tk) != nil){
1434  				bw2 = slave->borderwidth * 2;
1435  				w = slave->req.width + bw2 + slave->pad.x + slave->ipad.x;
1436  				if(cell->span.x == 1 && w > cols[x].act)
1437  					cols[x].act = w;
1438  				w = slave->req.height + bw2 + slave->pad.y + slave->ipad.y;
1439  				if(cell->span.y == 1 && w > rows[y].act)
1440  					rows[y].act = w;
1441  			}
1442  		}
1443  	}
1444  
1445  	beamminmax(rows, dy);
1446  	beamminmax(cols, dx);
1447  
1448  	/* now check that spanning cells fit in their rows/columns */
1449  	for(y = 0; y < dy; y++)
1450  		for(x = 0; x < dx; x++){
1451  			cell = &cells[y][x];
1452  			if((slave = cell->tk) != nil){
1453  				bw2 = slave->borderwidth * 2;
1454  				if(cell->span.x > 1){
1455  					w = slave->req.width + bw2 + slave->pad.x + slave->ipad.x;
1456  					expandwidths(x, x+cell->span.x, w, cols, 1);
1457  				}
1458  				if(cell->span.y > 1){
1459  					w = slave->req.height + bw2 + slave->pad.y + slave->ipad.y;
1460  					expandwidths(y, y+cell->span.y, w, rows, 1);
1461  				}
1462  			}
1463  		}
1464  
1465  	gridequalise(rows, dy);
1466  	gridequalise(cols, dx);
1467  
1468  	if(dx == 0)
1469  		req.x = 0;
1470  	else
1471  		req.x = beamsize(cols, 0, dx) + cols[0].pad + cols[dx-1].pad;
1472  
1473  	if(dy == 0)
1474  		req.y = 0;
1475  	else
1476  		req.y = beamsize(rows, 0, dy) + rows[0].pad + rows[dy-1].pad;
1477  
1478  	if(req.x != master->req.width || req.y != master->req.height)
1479  	if((master->flag & Tknoprop) == 0){
1480  		if(master->geom != nil){
1481  			master->geom(master, master->act.x, master->act.y,
1482  					req.x, req.y);
1483  		} else{
1484  			master->req.width = req.x;
1485  			master->req.height = req.y;
1486  			tkpackqit(master->master);
1487  		}
1488  		return 0;
1489      	}
1490  	org = ZP;
1491  	if(dx > 0 && master->act.width > req.x)
1492  		org.x = expandwidths(0, dx,
1493  				master->act.width - (cols[0].pad + cols[dx-1].pad),
1494  				cols, 0) / 2;
1495  	if(dy > 0 && master->act.height > req.y)
1496  		org.y = expandwidths(0, dy,
1497  				master->act.height - (rows[0].pad + rows[dy-1].pad),
1498  				rows, 0) / 2;
1499  
1500  	grid->origin = org;
1501  	pos.y = org.y;
1502  	fpady = 0;
1503  	for(y = 0; y < dy; y++){
1504  		pos.y += maximum(fpady, rows[y].pad);
1505  		fpady = rows[y].pad;
1506  
1507  		pos.x = org.x;
1508  		fpadx = 0;
1509  		for(x = 0; x < dx; x++){
1510  			cell = &cells[y][x];
1511  			pos.x += maximum(fpadx, cols[x].pad);
1512  			fpadx = cols[x].pad;
1513  			if((slave = cell->tk) != nil && cell->span.x > 0){
1514  				pos.width = beamsize(cols, x, x + cell->span.x);
1515  				pos.height = beamsize(rows, y, y + cell->span.y);
1516  				tksetslavereq(slave, pos);
1517  			}
1518  			pos.x += cols[x].act;
1519  		}
1520  		pos.y += rows[y].act;
1521  	}
1522  
1523  	master->dirty = tkrect(master, 1);
1524  	tkdirty(master);
1525  	return 1;
1526  }
1527