1/*
2 * tkGrid.c --
3 *
4 *	Grid based geometry manager.
5 *
6 * Copyright (c) 1996-1997 by Sun Microsystems, Inc.
7 *
8 * See the file "license.terms" for information on usage and redistribution of
9 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
10 *
11 * RCS: @(#) $Id$
12 */
13
14#include "tkInt.h"
15
16/*
17 * Convenience Macros
18 */
19
20#ifdef MAX
21#   undef MAX
22#endif
23#define MAX(x,y)	((x) > (y) ? (x) : (y))
24
25#define COLUMN		(1)	/* Working on column offsets. */
26#define ROW		(2)	/* Working on row offsets. */
27
28#define CHECK_ONLY	(1)	/* Check max slot constraint. */
29#define CHECK_SPACE	(2)	/* Alloc more space, don't change max. */
30
31/*
32 * Pre-allocate enough row and column slots for "typical" sized tables this
33 * value should be chosen so by the time the extra malloc's are required, the
34 * layout calculations overwehlm them. [A "slot" contains information for
35 * either a row or column, depending upon the context.]
36 */
37
38#define TYPICAL_SIZE	25	/* (Arbitrary guess) */
39#define PREALLOC	10	/* Extra slots to allocate. */
40
41/*
42 * Pre-allocate room for uniform groups during layout.
43 */
44
45#define UNIFORM_PREALLOC 10
46
47/*
48 * Data structures are allocated dynamically to support arbitrary sized
49 * tables. However, the space is proportional to the highest numbered slot
50 * with some non-default property. This limit is used to head off mistakes and
51 * denial of service attacks by limiting the amount of storage required.
52 */
53
54#define MAX_ELEMENT	10000
55
56/*
57 * Special characters to support relative layouts.
58 */
59
60#define REL_SKIP	'x'	/* Skip this column. */
61#define REL_HORIZ	'-'	/* Extend previous widget horizontally. */
62#define REL_VERT	'^'	/* Extend widget from row above. */
63
64/*
65 * Default value for 'grid anchor'.
66 */
67
68#define GRID_DEFAULT_ANCHOR TK_ANCHOR_NW
69
70/*
71 * Structure to hold information for grid masters. A slot is either a row or
72 * column.
73 */
74
75typedef struct SlotInfo {
76    int minSize;		/* The minimum size of this slot (in pixels).
77				 * It is set via the rowconfigure or
78				 * columnconfigure commands. */
79    int weight;			/* The resize weight of this slot. (0) means
80				 * this slot doesn't resize. Extra space in
81				 * the layout is given distributed among slots
82				 * inproportion to their weights. */
83    int pad;			/* Extra padding, in pixels, required for this
84				 * slot. This amount is "added" to the largest
85				 * slave in the slot. */
86    Tk_Uid uniform;		/* Value of -uniform option. It is used to
87				 * group slots that should have the same
88				 * size. */
89    int offset;			/* This is a cached value used for
90				 * introspection. It is the pixel offset of
91				 * the right or bottom edge of this slot from
92				 * the beginning of the layout. */
93    int temp;			/* This is a temporary value used for
94				 * calculating adjusted weights when shrinking
95				 * the layout below its nominal size. */
96} SlotInfo;
97
98/*
99 * Structure to hold information during layout calculations. There is one of
100 * these for each slot, an array for each of the rows or columns.
101 */
102
103typedef struct GridLayout {
104    struct Gridder *binNextPtr;	/* The next slave window in this bin. Each bin
105    				 * contains a list of all slaves whose spans
106    				 * are >1 and whose right edges fall in this
107    				 * slot. */
108    int minSize;		/* Minimum size needed for this slot, in
109    				 * pixels. This is the space required to hold
110    				 * any slaves contained entirely in this slot,
111    				 * adjusted for any slot constrants, such as
112    				 * size or padding. */
113    int pad;			/* Padding needed for this slot */
114    int weight;			/* Slot weight, controls resizing. */
115    Tk_Uid uniform;		/* Value of -uniform option. It is used to
116				 * group slots that should have the same
117				 * size. */
118    int minOffset;		/* The minimum offset, in pixels, from the
119    				 * beginning of the layout to the bottom/right
120    				 * edge of the slot calculated from top/left
121    				 * to bottom/right. */
122    int maxOffset;		/* The maximum offset, in pixels, from the
123    				 * beginning of the layout to the bottom/right
124    				 * edge of the slot calculated from
125    				 * bottom/right to top/left. */
126} GridLayout;
127
128/*
129 * Keep one of these for each geometry master.
130 */
131
132typedef struct {
133    SlotInfo *columnPtr;	/* Pointer to array of column constraints. */
134    SlotInfo *rowPtr;		/* Pointer to array of row constraints. */
135    int columnEnd;		/* The last column occupied by any slave. */
136    int columnMax;		/* The number of columns with constraints. */
137    int columnSpace;		/* The number of slots currently allocated for
138    				 * column constraints. */
139    int rowEnd;			/* The last row occupied by any slave. */
140    int rowMax;			/* The number of rows with constraints. */
141    int rowSpace;		/* The number of slots currently allocated for
142    				 * row constraints. */
143    int startX;			/* Pixel offset of this layout within its
144    				 * master. */
145    int startY;			/* Pixel offset of this layout within its
146    				 * master. */
147    Tk_Anchor anchor;		/* Value of anchor option: specifies where a
148				 * grid without weight should be placed. */
149} GridMaster;
150
151/*
152 * For each window that the grid cares about (either because the window is
153 * managed by the grid or because the window has slaves that are managed by
154 * the grid), there is a structure of the following type:
155 */
156
157typedef struct Gridder {
158    Tk_Window tkwin;		/* Tk token for window. NULL means that the
159				 * window has been deleted, but the gridder
160				 * hasn't had a chance to clean up yet because
161				 * the structure is still in use. */
162    struct Gridder *masterPtr;	/* Master window within which this window is
163				 * managed (NULL means this window isn't
164				 * managed by the gridder). */
165    struct Gridder *nextPtr;	/* Next window managed within same master.
166				 * List order doesn't matter. */
167    struct Gridder *slavePtr;	/* First in list of slaves managed inside this
168				 * window (NULL means no grid slaves). */
169    GridMaster *masterDataPtr;	/* Additional data for geometry master. */
170    int column, row;		/* Location in the grid (starting from
171				 * zero). */
172    int numCols, numRows;	/* Number of columns or rows this slave spans.
173				 * Should be at least 1. */
174    int padX, padY;		/* Total additional pixels to leave around the
175				 * window. Some is of this space is on each
176				 * side. This is space *outside* the window:
177				 * we'll allocate extra space in frame but
178				 * won't enlarge window). */
179    int padLeft, padTop;	/* The part of padX or padY to use on the left
180				 * or top of the widget, respectively. By
181				 * default, this is half of padX or padY. */
182    int iPadX, iPadY;		/* Total extra pixels to allocate inside the
183				 * window (half this amount will appear on
184				 * each side). */
185    int sticky;			/* which sides of its cavity this window
186				 * sticks to. See below for definitions */
187    int doubleBw;		/* Twice the window's last known border width.
188				 * If this changes, the window must be
189				 * re-arranged within its master. */
190    int *abortPtr;		/* If non-NULL, it means that there is a
191				 * nested call to ArrangeGrid already working
192				 * on this window. *abortPtr may be set to 1
193				 * to abort that nested call. This happens,
194				 * for example, if tkwin or any of its slaves
195				 * is deleted. */
196    int flags;			/* Miscellaneous flags; see below for
197				 * definitions. */
198
199    /*
200     * These fields are used temporarily for layout calculations only.
201     */
202
203    struct Gridder *binNextPtr;	/* Link to next span>1 slave in this bin. */
204    int size;			/* Nominal size (width or height) in pixels of
205    				 * the slave. This includes the padding. */
206} Gridder;
207
208/*
209 * Flag values for "sticky"ness. The 16 combinations subsume the packer's
210 * notion of anchor and fill.
211 *
212 * STICK_NORTH			This window sticks to the top of its cavity.
213 * STICK_EAST			This window sticks to the right edge of its
214 *				cavity.
215 * STICK_SOUTH			This window sticks to the bottom of its cavity.
216 * STICK_WEST			This window sticks to the left edge of its
217 *				cavity.
218 */
219
220#define STICK_NORTH		1
221#define STICK_EAST		2
222#define STICK_SOUTH		4
223#define STICK_WEST		8
224
225
226/*
227 * Structure to gather information about uniform groups during layout.
228 */
229
230typedef struct UniformGroup {
231    Tk_Uid group;
232    int minSize;
233} UniformGroup;
234
235/*
236 * Flag values for Grid structures:
237 *
238 * REQUESTED_RELAYOUT		1 means a Tcl_DoWhenIdle request has already
239 *				been made to re-arrange all the slaves of this
240 *				window.
241 * DONT_PROPAGATE		1 means don't set this window's requested
242 *				size. 0 means if this window is a master then
243 *				Tk will set its requested size to fit the
244 *				needs of its slaves.
245 */
246
247#define REQUESTED_RELAYOUT	1
248#define DONT_PROPAGATE		2
249
250/*
251 * Prototypes for procedures used only in this file:
252 */
253
254static void		AdjustForSticky(Gridder *slavePtr, int *xPtr,
255			    int *yPtr, int *widthPtr, int *heightPtr);
256static int		AdjustOffsets(int width, int elements,
257			    SlotInfo *slotPtr);
258static void		ArrangeGrid(ClientData clientData);
259static int		CheckSlotData(Gridder *masterPtr, int slot,
260			    int slotType, int checkOnly);
261static int		ConfigureSlaves(Tcl_Interp *interp, Tk_Window tkwin,
262			    int objc, Tcl_Obj *CONST objv[]);
263static void		DestroyGrid(char *memPtr);
264static Gridder *	GetGrid(Tk_Window tkwin);
265static int		GridAnchorCommand(Tk_Window tkwin, Tcl_Interp *interp,
266			    int objc, Tcl_Obj *CONST objv[]);
267static int		GridBboxCommand(Tk_Window tkwin, Tcl_Interp *interp,
268			    int objc, Tcl_Obj *CONST objv[]);
269static int		GridForgetRemoveCommand(Tk_Window tkwin,
270			    Tcl_Interp *interp, int objc,
271			    Tcl_Obj *CONST objv[]);
272static int		GridInfoCommand(Tk_Window tkwin, Tcl_Interp *interp,
273			    int objc, Tcl_Obj *CONST objv[]);
274static int		GridLocationCommand(Tk_Window tkwin,
275			    Tcl_Interp *interp, int objc,
276			    Tcl_Obj *CONST objv[]);
277static int		GridPropagateCommand(Tk_Window tkwin,
278			    Tcl_Interp *interp, int objc,
279			    Tcl_Obj *CONST objv[]);
280static int		GridRowColumnConfigureCommand(Tk_Window tkwin,
281			    Tcl_Interp *interp, int objc,
282			    Tcl_Obj *CONST objv[]);
283static int		GridSizeCommand(Tk_Window tkwin, Tcl_Interp *interp,
284			    int objc, Tcl_Obj *CONST objv[]);
285static int		GridSlavesCommand(Tk_Window tkwin, Tcl_Interp *interp,
286			    int objc, Tcl_Obj *CONST objv[]);
287static void		GridStructureProc(ClientData clientData,
288			    XEvent *eventPtr);
289static void		GridLostSlaveProc(ClientData clientData,
290			    Tk_Window tkwin);
291static void		GridReqProc(ClientData clientData, Tk_Window tkwin);
292static void		InitMasterData(Gridder *masterPtr);
293static Tcl_Obj *	NewPairObj(int, int);
294static Tcl_Obj *	NewQuadObj(int, int, int, int);
295static int		ResolveConstraints(Gridder *gridPtr, int rowOrColumn,
296			    int maxOffset);
297static void		SetGridSize(Gridder *gridPtr);
298static int		SetSlaveColumn(Tcl_Interp *interp, Gridder *slavePtr,
299			    int column, int numCols);
300static int		SetSlaveRow(Tcl_Interp *interp, Gridder *slavePtr,
301			    int row, int numRows);
302static void		StickyToString(int flags, char *result);
303static int		StringToSticky(char *string);
304static void		Unlink(Gridder *gridPtr);
305
306static const Tk_GeomMgr gridMgrType = {
307    "grid",			/* name */
308    GridReqProc,		/* requestProc */
309    GridLostSlaveProc,		/* lostSlaveProc */
310};
311
312/*
313 *----------------------------------------------------------------------
314 *
315 * Tk_GridCmd --
316 *
317 *	This procedure is invoked to process the "grid" Tcl command. See the
318 *	user documentation for details on what it does.
319 *
320 * Results:
321 *	A standard Tcl result.
322 *
323 * Side effects:
324 *	See the user documentation.
325 *
326 *----------------------------------------------------------------------
327 */
328
329int
330Tk_GridObjCmd(
331    ClientData clientData,	/* Main window associated with interpreter. */
332    Tcl_Interp *interp,		/* Current interpreter. */
333    int objc,			/* Number of arguments. */
334    Tcl_Obj *CONST objv[])	/* Argument objects. */
335{
336    Tk_Window tkwin = (Tk_Window) clientData;
337    static CONST char *optionStrings[] = {
338	"anchor", "bbox", "columnconfigure", "configure",
339	"forget", "info", "location", "propagate", "remove",
340	"rowconfigure", "size",	"slaves", NULL
341    };
342    enum options {
343	GRID_ANCHOR, GRID_BBOX, GRID_COLUMNCONFIGURE, GRID_CONFIGURE,
344	GRID_FORGET, GRID_INFO, GRID_LOCATION, GRID_PROPAGATE, GRID_REMOVE,
345	GRID_ROWCONFIGURE, GRID_SIZE, GRID_SLAVES
346    };
347    int index;
348
349    if (objc >= 2) {
350	char *argv1 = Tcl_GetString(objv[1]);
351
352	if ((argv1[0] == '.') || (argv1[0] == REL_SKIP) ||
353    		(argv1[0] == REL_VERT)) {
354	    return ConfigureSlaves(interp, tkwin, objc-1, objv+1);
355	}
356    }
357    if (objc < 3) {
358	Tcl_WrongNumArgs(interp, 1, objv, "option arg ?arg ...?");
359	return TCL_ERROR;
360    }
361
362    if (Tcl_GetIndexFromObj(interp, objv[1], optionStrings, "option", 0,
363	    &index) != TCL_OK) {
364	return TCL_ERROR;
365    }
366
367    switch ((enum options) index) {
368    case GRID_ANCHOR:
369	return GridAnchorCommand(tkwin, interp, objc, objv);
370    case GRID_BBOX:
371	return GridBboxCommand(tkwin, interp, objc, objv);
372    case GRID_CONFIGURE:
373	return ConfigureSlaves(interp, tkwin, objc-2, objv+2);
374    case GRID_FORGET:
375    case GRID_REMOVE:
376	return GridForgetRemoveCommand(tkwin, interp, objc, objv);
377    case GRID_INFO:
378	return GridInfoCommand(tkwin, interp, objc, objv);
379    case GRID_LOCATION:
380	return GridLocationCommand(tkwin, interp, objc, objv);
381    case GRID_PROPAGATE:
382	return GridPropagateCommand(tkwin, interp, objc, objv);
383    case GRID_SIZE:
384	return GridSizeCommand(tkwin, interp, objc, objv);
385    case GRID_SLAVES:
386	return GridSlavesCommand(tkwin, interp, objc, objv);
387
388    /*
389     * Sample argument combinations:
390     *  grid columnconfigure <master> <index> -option
391     *  grid columnconfigure <master> <index> -option value -option value
392     *  grid rowconfigure <master> <index>
393     *  grid rowconfigure <master> <index> -option
394     *  grid rowconfigure <master> <index> -option value -option value.
395     */
396
397    case GRID_COLUMNCONFIGURE:
398    case GRID_ROWCONFIGURE:
399	return GridRowColumnConfigureCommand(tkwin, interp, objc, objv);
400    }
401
402    /* This should not happen */
403    Tcl_SetResult(interp, "Internal error in grid.", TCL_STATIC);
404    return TCL_ERROR;
405}
406
407/*
408 *----------------------------------------------------------------------
409 *
410 * GridAnchorCommand --
411 *
412 *	Implementation of the [grid anchor] subcommand. See the user
413 *	documentation for details on what it does.
414 *
415 * Results:
416 *	Standard Tcl result.
417 *
418 * Side effects:
419 *	May recompute grid geometry.
420 *
421 *----------------------------------------------------------------------
422 */
423
424static int
425GridAnchorCommand(
426    Tk_Window tkwin,		/* Main window of the application. */
427    Tcl_Interp *interp,		/* Current interpreter. */
428    int objc,			/* Number of arguments. */
429    Tcl_Obj *CONST objv[])	/* Argument objects. */
430{
431    Tk_Window master;
432    Gridder *masterPtr;
433    GridMaster *gridPtr;
434    Tk_Anchor old;
435
436    if (objc > 4) {
437	Tcl_WrongNumArgs(interp, 2, objv, "window ?anchor?");
438	return TCL_ERROR;
439    }
440
441    if (TkGetWindowFromObj(interp, tkwin, objv[2], &master) != TCL_OK) {
442	return TCL_ERROR;
443    }
444    masterPtr = GetGrid(master);
445
446    if (objc == 3) {
447	gridPtr = masterPtr->masterDataPtr;
448	Tcl_SetResult(interp, (char *) Tk_NameOfAnchor(gridPtr == NULL ?
449		GRID_DEFAULT_ANCHOR : gridPtr->anchor), TCL_VOLATILE);
450	return TCL_OK;
451    }
452
453    InitMasterData(masterPtr);
454    gridPtr = masterPtr->masterDataPtr;
455    old = gridPtr->anchor;
456    if (Tk_GetAnchorFromObj(interp, objv[3], &gridPtr->anchor) != TCL_OK) {
457	return TCL_ERROR;
458    }
459
460    /*
461     * Only request a relayout if the anchor changes.
462     */
463
464    if (old != gridPtr->anchor) {
465	if (masterPtr->abortPtr != NULL) {
466	    *masterPtr->abortPtr = 1;
467	}
468	if (!(masterPtr->flags & REQUESTED_RELAYOUT)) {
469	    masterPtr->flags |= REQUESTED_RELAYOUT;
470	    Tcl_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
471	}
472    }
473    return TCL_OK;
474}
475
476/*
477 *----------------------------------------------------------------------
478 *
479 * GridBboxCommand --
480 *
481 *	Implementation of the [grid bbox] subcommand.
482 *
483 * Results:
484 *	Standard Tcl result.
485 *
486 * Side effects:
487 *	Places bounding box information in the interp's result field.
488 *
489 *----------------------------------------------------------------------
490 */
491
492static int
493GridBboxCommand(
494    Tk_Window tkwin,		/* Main window of the application. */
495    Tcl_Interp *interp,		/* Current interpreter. */
496    int objc,			/* Number of arguments. */
497    Tcl_Obj *CONST objv[])	/* Argument objects. */
498{
499    Tk_Window master;
500    Gridder *masterPtr;		/* master grid record */
501    GridMaster *gridPtr;	/* pointer to grid data */
502    int row, column;		/* origin for bounding box */
503    int row2, column2;		/* end of bounding box */
504    int endX, endY;		/* last column/row in the layout */
505    int x=0, y=0;		/* starting pixels for this bounding box */
506    int width, height;		/* size of the bounding box */
507
508    if (objc!=3 && objc != 5 && objc != 7) {
509	Tcl_WrongNumArgs(interp, 2, objv, "master ?column row ?column row??");
510	return TCL_ERROR;
511    }
512
513    if (TkGetWindowFromObj(interp, tkwin, objv[2], &master) != TCL_OK) {
514	return TCL_ERROR;
515    }
516    masterPtr = GetGrid(master);
517
518    if (objc >= 5) {
519	if (Tcl_GetIntFromObj(interp, objv[3], &column) != TCL_OK) {
520	    return TCL_ERROR;
521	}
522	if (Tcl_GetIntFromObj(interp, objv[4], &row) != TCL_OK) {
523	    return TCL_ERROR;
524	}
525	column2 = column;
526	row2 = row;
527    }
528
529    if (objc == 7) {
530	if (Tcl_GetIntFromObj(interp, objv[5], &column2) != TCL_OK) {
531	    return TCL_ERROR;
532	}
533	if (Tcl_GetIntFromObj(interp, objv[6], &row2) != TCL_OK) {
534	    return TCL_ERROR;
535	}
536    }
537
538    gridPtr = masterPtr->masterDataPtr;
539    if (gridPtr == NULL) {
540	Tcl_SetObjResult(interp, NewQuadObj(0, 0, 0, 0));
541	return TCL_OK;
542    }
543
544    SetGridSize(masterPtr);
545    endX = MAX(gridPtr->columnEnd, gridPtr->columnMax);
546    endY = MAX(gridPtr->rowEnd, gridPtr->rowMax);
547
548    if ((endX == 0) || (endY == 0)) {
549	Tcl_SetObjResult(interp, NewQuadObj(0, 0, 0, 0));
550	return TCL_OK;
551    }
552    if (objc == 3) {
553	row = 0;
554	column = 0;
555	row2 = endY;
556	column2 = endX;
557    }
558
559    if (column > column2) {
560	int temp = column;
561
562	column = column2;
563	column2 = temp;
564    }
565    if (row > row2) {
566	int temp = row;
567
568	row = row2;
569	row2 = temp;
570    }
571
572    if (column > 0 && column < endX) {
573	x = gridPtr->columnPtr[column-1].offset;
574    } else if (column > 0) {
575	x = gridPtr->columnPtr[endX-1].offset;
576    }
577
578    if (row > 0 && row < endY) {
579	y = gridPtr->rowPtr[row-1].offset;
580    } else if (row > 0) {
581	y = gridPtr->rowPtr[endY-1].offset;
582    }
583
584    if (column2 < 0) {
585	width = 0;
586    } else if (column2 >= endX) {
587	width = gridPtr->columnPtr[endX-1].offset - x;
588    } else {
589	width = gridPtr->columnPtr[column2].offset - x;
590    }
591
592    if (row2 < 0) {
593	height = 0;
594    } else if (row2 >= endY) {
595	height = gridPtr->rowPtr[endY-1].offset - y;
596    } else {
597	height = gridPtr->rowPtr[row2].offset - y;
598    }
599
600    Tcl_SetObjResult(interp, NewQuadObj(
601	    x + gridPtr->startX, y + gridPtr->startY, width, height));
602    return TCL_OK;
603}
604
605/*
606 *----------------------------------------------------------------------
607 *
608 * GridForgetRemoveCommand --
609 *
610 *	Implementation of the [grid forget]/[grid remove] subcommands. See the
611 *	user documentation for details on what these do.
612 *
613 * Results:
614 *	Standard Tcl result.
615 *
616 * Side effects:
617 *	Removes a window from a grid layout.
618 *
619 *----------------------------------------------------------------------
620 */
621
622static int
623GridForgetRemoveCommand(
624    Tk_Window tkwin,		/* Main window of the application. */
625    Tcl_Interp *interp,		/* Current interpreter. */
626    int objc,			/* Number of arguments. */
627    Tcl_Obj *CONST objv[])	/* Argument objects. */
628{
629    Tk_Window slave;
630    Gridder *slavePtr;
631    int i;
632    char *string = Tcl_GetString(objv[1]);
633    char c = string[0];
634
635    for (i = 2; i < objc; i++) {
636	if (TkGetWindowFromObj(interp, tkwin, objv[i], &slave) != TCL_OK) {
637	    return TCL_ERROR;
638	}
639
640	slavePtr = GetGrid(slave);
641	if (slavePtr->masterPtr != NULL) {
642	    /*
643	     * For "forget", reset all the settings to their defaults
644	     */
645
646	    if (c == 'f') {
647		slavePtr->column = -1;
648		slavePtr->row = -1;
649		slavePtr->numCols = 1;
650		slavePtr->numRows = 1;
651		slavePtr->padX = 0;
652		slavePtr->padY = 0;
653		slavePtr->padLeft = 0;
654		slavePtr->padTop = 0;
655		slavePtr->iPadX = 0;
656		slavePtr->iPadY = 0;
657		slavePtr->doubleBw = 2*Tk_Changes(tkwin)->border_width;
658		if (slavePtr->flags & REQUESTED_RELAYOUT) {
659		    Tcl_CancelIdleCall(ArrangeGrid, (ClientData) slavePtr);
660		}
661		slavePtr->flags = 0;
662		slavePtr->sticky = 0;
663	    }
664	    Tk_ManageGeometry(slave, NULL, (ClientData) NULL);
665	    if (slavePtr->masterPtr->tkwin != Tk_Parent(slavePtr->tkwin)) {
666		Tk_UnmaintainGeometry(slavePtr->tkwin,
667			slavePtr->masterPtr->tkwin);
668	    }
669	    Unlink(slavePtr);
670	    Tk_UnmapWindow(slavePtr->tkwin);
671	}
672    }
673    return TCL_OK;
674}
675
676/*
677 *----------------------------------------------------------------------
678 *
679 * GridInfoCommand --
680 *
681 *	Implementation of the [grid info] subcommand. See the user
682 *	documentation for details on what it does.
683 *
684 * Results:
685 *	Standard Tcl result.
686 *
687 * Side effects:
688 *	Puts gridding information in the interpreter's result.
689 *
690 *----------------------------------------------------------------------
691 */
692
693static int
694GridInfoCommand(
695    Tk_Window tkwin,		/* Main window of the application. */
696    Tcl_Interp *interp,		/* Current interpreter. */
697    int objc,			/* Number of arguments. */
698    Tcl_Obj *CONST objv[])	/* Argument objects. */
699{
700    register Gridder *slavePtr;
701    Tk_Window slave;
702    char buffer[64 + TCL_INTEGER_SPACE * 4];
703
704    if (objc != 3) {
705	Tcl_WrongNumArgs(interp, 2, objv, "window");
706	return TCL_ERROR;
707    }
708    if (TkGetWindowFromObj(interp, tkwin, objv[2], &slave) != TCL_OK) {
709	return TCL_ERROR;
710    }
711    slavePtr = GetGrid(slave);
712    if (slavePtr->masterPtr == NULL) {
713	Tcl_ResetResult(interp);
714	return TCL_OK;
715    }
716
717    Tcl_AppendElement(interp, "-in");
718    Tcl_AppendElement(interp, Tk_PathName(slavePtr->masterPtr->tkwin));
719    sprintf(buffer, " -column %d -row %d -columnspan %d -rowspan %d",
720	    slavePtr->column, slavePtr->row,
721	    slavePtr->numCols, slavePtr->numRows);
722    Tcl_AppendResult(interp, buffer, NULL);
723    TkPrintPadAmount(interp, "ipadx", slavePtr->iPadX/2, slavePtr->iPadX);
724    TkPrintPadAmount(interp, "ipady", slavePtr->iPadY/2, slavePtr->iPadY);
725    TkPrintPadAmount(interp, "padx", slavePtr->padLeft, slavePtr->padX);
726    TkPrintPadAmount(interp, "pady", slavePtr->padTop, slavePtr->padY);
727    StickyToString(slavePtr->sticky, buffer);
728    Tcl_AppendResult(interp, " -sticky ", buffer, NULL);
729    return TCL_OK;
730}
731
732/*
733 *----------------------------------------------------------------------
734 *
735 * GridLocationCommand --
736 *
737 *	Implementation of the [grid location] subcommand. See the user
738 *	documentation for details on what it does.
739 *
740 * Results:
741 *	Standard Tcl result.
742 *
743 * Side effects:
744 *	Puts location information in the interpreter's result field.
745 *
746 *----------------------------------------------------------------------
747 */
748
749static int
750GridLocationCommand(
751    Tk_Window tkwin,		/* Main window of the application. */
752    Tcl_Interp *interp,		/* Current interpreter. */
753    int objc,			/* Number of arguments. */
754    Tcl_Obj *CONST objv[])	/* Argument objects. */
755{
756    Tk_Window master;
757    Gridder *masterPtr;		/* Master grid record. */
758    GridMaster *gridPtr;	/* Pointer to grid data. */
759    register SlotInfo *slotPtr;
760    int x, y;			/* Offset in pixels, from edge of master. */
761    int i, j;			/* Corresponding column and row indeces. */
762    int endX, endY;		/* End of grid. */
763
764    if (objc != 5) {
765	Tcl_WrongNumArgs(interp, 2, objv, "master x y");
766	return TCL_ERROR;
767    }
768
769    if (TkGetWindowFromObj(interp, tkwin, objv[2], &master) != TCL_OK) {
770	return TCL_ERROR;
771    }
772
773    if (Tk_GetPixelsFromObj(interp, master, objv[3], &x) != TCL_OK) {
774	return TCL_ERROR;
775    }
776    if (Tk_GetPixelsFromObj(interp, master, objv[4], &y) != TCL_OK) {
777	return TCL_ERROR;
778    }
779
780    masterPtr = GetGrid(master);
781    if (masterPtr->masterDataPtr == NULL) {
782	Tcl_SetObjResult(interp, NewPairObj(-1, -1));
783	return TCL_OK;
784    }
785    gridPtr = masterPtr->masterDataPtr;
786
787    /*
788     * Update any pending requests. This is not always the steady state value,
789     * as more configure events could be in the pipeline, but its as close as
790     * its easy to get.
791     */
792
793    while (masterPtr->flags & REQUESTED_RELAYOUT) {
794	Tcl_CancelIdleCall(ArrangeGrid, (ClientData) masterPtr);
795	ArrangeGrid((ClientData) masterPtr);
796    }
797    SetGridSize(masterPtr);
798    endX = MAX(gridPtr->columnEnd, gridPtr->columnMax);
799    endY = MAX(gridPtr->rowEnd, gridPtr->rowMax);
800
801    slotPtr = masterPtr->masterDataPtr->columnPtr;
802    if (x < masterPtr->masterDataPtr->startX) {
803	i = -1;
804    } else {
805	x -= masterPtr->masterDataPtr->startX;
806	for (i = 0; slotPtr[i].offset < x && i < endX; i++) {
807	    /* null body */
808	}
809    }
810
811    slotPtr = masterPtr->masterDataPtr->rowPtr;
812    if (y < masterPtr->masterDataPtr->startY) {
813	j = -1;
814    } else {
815	y -= masterPtr->masterDataPtr->startY;
816	for (j = 0; slotPtr[j].offset < y && j < endY; j++) {
817	    /* null body */
818	}
819    }
820
821    Tcl_SetObjResult(interp, NewPairObj(i, j));
822    return TCL_OK;
823}
824
825/*
826 *----------------------------------------------------------------------
827 *
828 * GridPropagateCommand --
829 *
830 *	Implementation of the [grid propagate] subcommand. See the user
831 *	documentation for details on what it does.
832 *
833 * Results:
834 *	Standard Tcl result.
835 *
836 * Side effects:
837 *	May alter geometry propagation for a widget.
838 *
839 *----------------------------------------------------------------------
840 */
841
842static int
843GridPropagateCommand(
844    Tk_Window tkwin,		/* Main window of the application. */
845    Tcl_Interp *interp,		/* Current interpreter. */
846    int objc,			/* Number of arguments. */
847    Tcl_Obj *CONST objv[])	/* Argument objects. */
848{
849    Tk_Window master;
850    Gridder *masterPtr;
851    int propagate, old;
852
853    if (objc > 4) {
854	Tcl_WrongNumArgs(interp, 2, objv, "window ?boolean?");
855	return TCL_ERROR;
856    }
857
858    if (TkGetWindowFromObj(interp, tkwin, objv[2], &master) != TCL_OK) {
859	return TCL_ERROR;
860    }
861    masterPtr = GetGrid(master);
862    if (objc == 3) {
863	Tcl_SetObjResult(interp,
864		Tcl_NewBooleanObj(!(masterPtr->flags & DONT_PROPAGATE)));
865	return TCL_OK;
866    }
867    if (Tcl_GetBooleanFromObj(interp, objv[3], &propagate) != TCL_OK) {
868	return TCL_ERROR;
869    }
870
871    /*
872     * Only request a relayout if the propagation bit changes.
873     */
874
875    old = !(masterPtr->flags & DONT_PROPAGATE);
876    if (propagate != old) {
877	if (propagate) {
878	    masterPtr->flags &= ~DONT_PROPAGATE;
879	} else {
880	    masterPtr->flags |= DONT_PROPAGATE;
881	}
882
883	/*
884	 * Re-arrange the master to allow new geometry information to
885	 * propagate upwards to the master's master.
886	 */
887
888	if (masterPtr->abortPtr != NULL) {
889	    *masterPtr->abortPtr = 1;
890	}
891	if (!(masterPtr->flags & REQUESTED_RELAYOUT)) {
892	    masterPtr->flags |= REQUESTED_RELAYOUT;
893	    Tcl_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
894	}
895    }
896    return TCL_OK;
897}
898
899/*
900 *----------------------------------------------------------------------
901 *
902 * GridRowColumnConfigureCommand --
903 *
904 *	Implementation of the [grid rowconfigure] and [grid columnconfigure]
905 *	subcommands. See the user documentation for details on what these do.
906 *
907 * Results:
908 *	Standard Tcl result.
909 *
910 * Side effects:
911 *	Depends on arguments; see user documentation.
912 *
913 *----------------------------------------------------------------------
914 */
915
916static int
917GridRowColumnConfigureCommand(
918    Tk_Window tkwin,		/* Main window of the application. */
919    Tcl_Interp *interp,		/* Current interpreter. */
920    int objc,			/* Number of arguments. */
921    Tcl_Obj *CONST objv[])	/* Argument objects. */
922{
923    Tk_Window master, slave;
924    Gridder *masterPtr, *slavePtr;
925    SlotInfo *slotPtr = NULL;
926    int slot;			/* the column or row number */
927    int slotType;		/* COLUMN or ROW */
928    int size;			/* the configuration value */
929    int lObjc;			/* Number of items in index list */
930    Tcl_Obj **lObjv;		/* array of indices */
931    int ok;			/* temporary TCL result code */
932    int i, j, first, last;
933    char *string;
934    static CONST char *optionStrings[] = {
935	"-minsize", "-pad", "-uniform", "-weight", NULL
936    };
937    enum options {
938	ROWCOL_MINSIZE, ROWCOL_PAD, ROWCOL_UNIFORM, ROWCOL_WEIGHT
939    };
940    int index;
941    Tcl_Obj *listCopy;
942
943    if (((objc % 2 != 0) && (objc > 6)) || (objc < 4)) {
944	Tcl_WrongNumArgs(interp, 2, objv, "master index ?-option value...?");
945	return TCL_ERROR;
946    }
947
948    if (TkGetWindowFromObj(interp, tkwin, objv[2], &master) != TCL_OK) {
949	return TCL_ERROR;
950    }
951
952    listCopy = Tcl_DuplicateObj(objv[3]);
953    Tcl_IncrRefCount(listCopy);
954    if (Tcl_ListObjGetElements(interp, listCopy, &lObjc, &lObjv) != TCL_OK) {
955	Tcl_DecrRefCount(listCopy);
956	return TCL_ERROR;
957    }
958
959    string = Tcl_GetString(objv[1]);
960    slotType = (*string == 'c') ? COLUMN : ROW;
961    if (lObjc == 0) {
962	Tcl_AppendResult(interp, "no ",
963		(slotType == COLUMN) ? "column" : "row",
964		" indices specified", NULL);
965	Tcl_DecrRefCount(listCopy);
966	return TCL_ERROR;
967    }
968
969    masterPtr = GetGrid(master);
970    first = 0; /* lint */
971    last = 0; /* lint */
972
973    if ((objc == 4) || (objc == 5)) {
974	if (lObjc != 1) {
975	    Tcl_AppendResult(interp, Tcl_GetString(objv[0]), " ",
976		    Tcl_GetString(objv[1]),
977		    ": must specify a single element on retrieval", NULL);
978	    Tcl_DecrRefCount(listCopy);
979	    return TCL_ERROR;
980	}
981	if (Tcl_GetIntFromObj(interp, lObjv[0], &slot) != TCL_OK) {
982	    Tcl_AppendResult(interp,
983		    " (when retreiving options only integer indices are "
984		    "allowed)", NULL);
985	    Tcl_DecrRefCount(listCopy);
986	    return TCL_ERROR;
987	}
988	ok = CheckSlotData(masterPtr, slot, slotType, /* checkOnly */ 1);
989	if (ok == TCL_OK) {
990	    slotPtr = (slotType == COLUMN) ?
991		    masterPtr->masterDataPtr->columnPtr :
992		    masterPtr->masterDataPtr->rowPtr;
993	}
994
995	/*
996	 * Return all of the options for this row or column. If the request is
997	 * out of range, return all 0's.
998	 */
999
1000	if (objc == 4) {
1001	    int minsize = 0, pad = 0, weight = 0;
1002	    Tk_Uid uniform = NULL;
1003	    Tcl_Obj *res = Tcl_NewListObj(0, NULL);
1004
1005	    if (ok == TCL_OK) {
1006		minsize = slotPtr[slot].minSize;
1007		pad     = slotPtr[slot].pad;
1008		weight  = slotPtr[slot].weight;
1009		uniform = slotPtr[slot].uniform;
1010	    }
1011
1012	    Tcl_ListObjAppendElement(interp, res,
1013		    Tcl_NewStringObj("-minsize", -1));
1014	    Tcl_ListObjAppendElement(interp, res, Tcl_NewIntObj(minsize));
1015	    Tcl_ListObjAppendElement(interp, res,
1016		    Tcl_NewStringObj("-pad", -1));
1017	    Tcl_ListObjAppendElement(interp, res, Tcl_NewIntObj(pad));
1018	    Tcl_ListObjAppendElement(interp, res,
1019		    Tcl_NewStringObj("-uniform", -1));
1020	    Tcl_ListObjAppendElement(interp, res,
1021		    Tcl_NewStringObj(uniform == NULL ? "" : uniform, -1));
1022	    Tcl_ListObjAppendElement(interp, res,
1023		    Tcl_NewStringObj("-weight", -1));
1024	    Tcl_ListObjAppendElement(interp, res, Tcl_NewIntObj(weight));
1025	    Tcl_SetObjResult(interp, res);
1026	    Tcl_DecrRefCount(listCopy);
1027	    return TCL_OK;
1028	}
1029
1030	/*
1031	 * If only one option is given, with no value, the current value is
1032	 * returned.
1033	 */
1034
1035	if (Tcl_GetIndexFromObj(interp, objv[4], optionStrings, "option", 0,
1036		&index) != TCL_OK) {
1037	    Tcl_DecrRefCount(listCopy);
1038	    return TCL_ERROR;
1039	}
1040	if (index == ROWCOL_MINSIZE) {
1041	    Tcl_SetObjResult(interp,
1042		    Tcl_NewIntObj((ok == TCL_OK) ? slotPtr[slot].minSize : 0));
1043	} else if (index == ROWCOL_WEIGHT) {
1044	    Tcl_SetObjResult(interp,
1045		    Tcl_NewIntObj((ok == TCL_OK) ? slotPtr[slot].weight : 0));
1046	} else if (index == ROWCOL_UNIFORM) {
1047	    Tk_Uid value = (ok == TCL_OK) ? slotPtr[slot].uniform : "";
1048
1049	    Tcl_SetObjResult(interp,
1050		    Tcl_NewStringObj(value == NULL ? "" : value, -1));
1051	} else if (index == ROWCOL_PAD) {
1052	    Tcl_SetObjResult(interp,
1053		    Tcl_NewIntObj((ok == TCL_OK) ? slotPtr[slot].pad : 0));
1054	}
1055	Tcl_DecrRefCount(listCopy);
1056	return TCL_OK;
1057    }
1058
1059    for (j = 0; j < lObjc; j++) {
1060	int allSlaves = 0;
1061
1062	if (Tcl_GetIntFromObj(NULL, lObjv[j], &slot) == TCL_OK) {
1063	    first = slot;
1064	    last = slot;
1065	    slavePtr = NULL;
1066	} else if (strcmp(Tcl_GetString(lObjv[j]), "all") == 0) {
1067	    /*
1068	     * Make sure master is initialised.
1069	     */
1070
1071	    InitMasterData(masterPtr);
1072
1073	    slavePtr = masterPtr->slavePtr;
1074	    if (slavePtr == NULL) {
1075		continue;
1076	    }
1077	    allSlaves = 1;
1078	} else if (TkGetWindowFromObj(NULL, tkwin, lObjv[j], &slave)
1079		== TCL_OK) {
1080	    /*
1081	     * Is it gridded in this master?
1082	     */
1083
1084	    slavePtr = GetGrid(slave);
1085	    if (slavePtr->masterPtr != masterPtr) {
1086		Tcl_AppendResult(interp, Tcl_GetString(objv[0]), " ",
1087			Tcl_GetString(objv[1]), ": the window \"",
1088			Tcl_GetString(lObjv[j]), "\" is not managed by \"",
1089		 	Tcl_GetString(objv[2]), "\"", NULL);
1090		Tcl_DecrRefCount(listCopy);
1091		return TCL_ERROR;
1092	    }
1093	} else {
1094	    Tcl_AppendResult(interp, Tcl_GetString(objv[0]), " ",
1095		    Tcl_GetString(objv[1]), ": illegal index \"",
1096		    Tcl_GetString(lObjv[j]), "\"", NULL);
1097	    Tcl_DecrRefCount(listCopy);
1098	    return TCL_ERROR;
1099	}
1100
1101	/*
1102	 * The outer loop is only to handle "all".
1103	 */
1104
1105	do {
1106	    if (slavePtr != NULL) {
1107		first = (slotType == COLUMN) ?
1108			slavePtr->column : slavePtr->row;
1109		last = first - 1 + ((slotType == COLUMN) ?
1110			slavePtr->numCols : slavePtr->numRows);
1111	    }
1112
1113	    for (slot = first; slot <= last; slot++) {
1114		ok = CheckSlotData(masterPtr, slot, slotType, /*checkOnly*/ 0);
1115		if (ok != TCL_OK) {
1116		    Tcl_AppendResult(interp, Tcl_GetString(objv[0]), " ",
1117			    Tcl_GetString(objv[1]), ": \"",
1118			    Tcl_GetString(lObjv[j]),
1119			    "\" is out of range", NULL);
1120   		    Tcl_DecrRefCount(listCopy);
1121		    return TCL_ERROR;
1122		}
1123		slotPtr = (slotType == COLUMN) ?
1124			masterPtr->masterDataPtr->columnPtr :
1125			masterPtr->masterDataPtr->rowPtr;
1126
1127		/*
1128		 * Loop through each option value pair, setting the values as
1129		 * required.
1130		 */
1131
1132		for (i = 4; i < objc; i += 2) {
1133		    if (Tcl_GetIndexFromObj(interp, objv[i], optionStrings,
1134			    "option", 0, &index) != TCL_OK) {
1135   			Tcl_DecrRefCount(listCopy);
1136			return TCL_ERROR;
1137		    }
1138		    if (index == ROWCOL_MINSIZE) {
1139			if (Tk_GetPixelsFromObj(interp, master, objv[i+1],
1140				&size) != TCL_OK) {
1141   			    Tcl_DecrRefCount(listCopy);
1142			    return TCL_ERROR;
1143			} else {
1144			    slotPtr[slot].minSize = size;
1145			}
1146		    } else if (index == ROWCOL_WEIGHT) {
1147			int wt;
1148
1149			if (Tcl_GetIntFromObj(interp,objv[i+1],&wt)!=TCL_OK) {
1150   			    Tcl_DecrRefCount(listCopy);
1151			    return TCL_ERROR;
1152			} else if (wt < 0) {
1153			    Tcl_AppendResult(interp, "invalid arg \"",
1154				    Tcl_GetString(objv[i]),
1155				    "\": should be non-negative", NULL);
1156   			    Tcl_DecrRefCount(listCopy);
1157			    return TCL_ERROR;
1158			} else {
1159			    slotPtr[slot].weight = wt;
1160			}
1161		    } else if (index == ROWCOL_UNIFORM) {
1162			slotPtr[slot].uniform =
1163				Tk_GetUid(Tcl_GetString(objv[i+1]));
1164			if (slotPtr[slot].uniform != NULL &&
1165				slotPtr[slot].uniform[0] == 0) {
1166			    slotPtr[slot].uniform = NULL;
1167			}
1168		    } else if (index == ROWCOL_PAD) {
1169			if (Tk_GetPixelsFromObj(interp, master, objv[i+1],
1170				&size) != TCL_OK) {
1171   			    Tcl_DecrRefCount(listCopy);
1172			    return TCL_ERROR;
1173			} else if (size < 0) {
1174			    Tcl_AppendResult(interp, "invalid arg \"",
1175				    Tcl_GetString(objv[i]),
1176				    "\": should be non-negative", NULL);
1177   			    Tcl_DecrRefCount(listCopy);
1178			    return TCL_ERROR;
1179			} else {
1180			    slotPtr[slot].pad = size;
1181			}
1182		    }
1183		}
1184	    }
1185	    if (slavePtr != NULL) {
1186		slavePtr = slavePtr->nextPtr;
1187	    }
1188	} while ((allSlaves == 1) && (slavePtr != NULL));
1189    }
1190    Tcl_DecrRefCount(listCopy);
1191
1192    /*
1193     * We changed a property, re-arrange the table, and check for constraint
1194     * shrinkage. A null slotPtr will occur for 'all' checks.
1195     */
1196
1197    if (slotPtr != NULL) {
1198	if (slotType == ROW) {
1199	    int last = masterPtr->masterDataPtr->rowMax - 1;
1200
1201	    while ((last >= 0) && (slotPtr[last].weight == 0)
1202		    && (slotPtr[last].pad == 0) && (slotPtr[last].minSize == 0)
1203		    && (slotPtr[last].uniform == NULL)) {
1204		last--;
1205	    }
1206	    masterPtr->masterDataPtr->rowMax = last+1;
1207	} else {
1208	    int last = masterPtr->masterDataPtr->columnMax - 1;
1209
1210	    while ((last >= 0) && (slotPtr[last].weight == 0)
1211		    && (slotPtr[last].pad == 0) && (slotPtr[last].minSize == 0)
1212		    && (slotPtr[last].uniform == NULL)) {
1213		last--;
1214	    }
1215	    masterPtr->masterDataPtr->columnMax = last + 1;
1216	}
1217    }
1218
1219    if (masterPtr->abortPtr != NULL) {
1220	*masterPtr->abortPtr = 1;
1221    }
1222    if (!(masterPtr->flags & REQUESTED_RELAYOUT)) {
1223	masterPtr->flags |= REQUESTED_RELAYOUT;
1224	Tcl_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
1225    }
1226    return TCL_OK;
1227}
1228
1229/*
1230 *----------------------------------------------------------------------
1231 *
1232 * GridSizeCommand --
1233 *
1234 *	Implementation of the [grid size] subcommand. See the user
1235 *	documentation for details on what it does.
1236 *
1237 * Results:
1238 *	Standard Tcl result.
1239 *
1240 * Side effects:
1241 *	Puts grid size information in the interpreter's result.
1242 *
1243 *----------------------------------------------------------------------
1244 */
1245
1246static int
1247GridSizeCommand(
1248    Tk_Window tkwin,		/* Main window of the application. */
1249    Tcl_Interp *interp,		/* Current interpreter. */
1250    int objc,			/* Number of arguments. */
1251    Tcl_Obj *CONST objv[])	/* Argument objects. */
1252{
1253    Tk_Window master;
1254    Gridder *masterPtr;
1255    GridMaster *gridPtr;	/* pointer to grid data */
1256
1257    if (objc != 3) {
1258	Tcl_WrongNumArgs(interp, 2, objv, "window");
1259	return TCL_ERROR;
1260    }
1261
1262    if (TkGetWindowFromObj(interp, tkwin, objv[2], &master) != TCL_OK) {
1263	return TCL_ERROR;
1264    }
1265    masterPtr = GetGrid(master);
1266
1267    if (masterPtr->masterDataPtr != NULL) {
1268	SetGridSize(masterPtr);
1269	gridPtr = masterPtr->masterDataPtr;
1270	Tcl_SetObjResult(interp, NewPairObj(
1271		MAX(gridPtr->columnEnd, gridPtr->columnMax),
1272		MAX(gridPtr->rowEnd, gridPtr->rowMax)));
1273    } else {
1274	Tcl_SetObjResult(interp, NewPairObj(0, 0));
1275    }
1276    return TCL_OK;
1277}
1278
1279/*
1280 *----------------------------------------------------------------------
1281 *
1282 * GridSlavesCommand --
1283 *
1284 *	Implementation of the [grid slaves] subcommand. See the user
1285 *	documentation for details on what it does.
1286 *
1287 * Results:
1288 *	Standard Tcl result.
1289 *
1290 * Side effects:
1291 *	Places a list of slaves of the specified window in the interpreter's
1292 *	result field.
1293 *
1294 *----------------------------------------------------------------------
1295 */
1296
1297static int
1298GridSlavesCommand(
1299    Tk_Window tkwin,		/* Main window of the application. */
1300    Tcl_Interp *interp,		/* Current interpreter. */
1301    int objc,			/* Number of arguments. */
1302    Tcl_Obj *CONST objv[])	/* Argument objects. */
1303{
1304    Tk_Window master;
1305    Gridder *masterPtr;		/* master grid record */
1306    Gridder *slavePtr;
1307    int i, value, index;
1308    int row = -1, column = -1;
1309    static CONST char *optionStrings[] = {
1310	"-column", "-row", NULL
1311    };
1312    enum options { SLAVES_COLUMN, SLAVES_ROW };
1313    Tcl_Obj *res;
1314
1315    if ((objc < 3) || ((objc % 2) == 0)) {
1316	Tcl_WrongNumArgs(interp, 2, objv, "window ?-option value...?");
1317	return TCL_ERROR;
1318    }
1319
1320    for (i = 3; i < objc; i += 2) {
1321	if (Tcl_GetIndexFromObj(interp, objv[i], optionStrings, "option", 0,
1322		&index) != TCL_OK) {
1323	    return TCL_ERROR;
1324	}
1325	if (Tcl_GetIntFromObj(interp, objv[i+1], &value) != TCL_OK) {
1326	    return TCL_ERROR;
1327	}
1328	if (value < 0) {
1329	    Tcl_AppendResult(interp, Tcl_GetString(objv[i]),
1330		    " is an invalid value: should NOT be < 0", NULL);
1331	    return TCL_ERROR;
1332	}
1333	if (index == SLAVES_COLUMN) {
1334	    column = value;
1335	} else {
1336	    row = value;
1337	}
1338    }
1339
1340    if (TkGetWindowFromObj(interp, tkwin, objv[2], &master) != TCL_OK) {
1341	return TCL_ERROR;
1342    }
1343    masterPtr = GetGrid(master);
1344
1345    res = Tcl_NewListObj(0, NULL);
1346    for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
1347	    slavePtr = slavePtr->nextPtr) {
1348	if (column>=0 && (slavePtr->column > column
1349		|| slavePtr->column+slavePtr->numCols-1 < column)) {
1350	    continue;
1351	}
1352	if (row>=0 && (slavePtr->row > row ||
1353		slavePtr->row+slavePtr->numRows-1 < row)) {
1354	    continue;
1355	}
1356	Tcl_ListObjAppendElement(interp, res,
1357		Tcl_NewStringObj(Tk_PathName(slavePtr->tkwin), -1));
1358    }
1359    Tcl_SetObjResult(interp, res);
1360    return TCL_OK;
1361}
1362
1363/*
1364 *----------------------------------------------------------------------
1365 *
1366 * GridReqProc --
1367 *
1368 *	This procedure is invoked by Tk_GeometryRequest for windows managed by
1369 *	the grid.
1370 *
1371 * Results:
1372 *	None.
1373 *
1374 * Side effects:
1375 *	Arranges for tkwin, and all its managed siblings, to be re-arranged at
1376 *	the next idle point.
1377 *
1378 *----------------------------------------------------------------------
1379 */
1380
1381static void
1382GridReqProc(
1383    ClientData clientData,	/* Grid's information about window that got
1384				 * new preferred geometry. */
1385    Tk_Window tkwin)		/* Other Tk-related information about the
1386				 * window. */
1387{
1388    register Gridder *gridPtr = (Gridder *) clientData;
1389
1390    gridPtr = gridPtr->masterPtr;
1391    if (gridPtr && !(gridPtr->flags & REQUESTED_RELAYOUT)) {
1392	gridPtr->flags |= REQUESTED_RELAYOUT;
1393	Tcl_DoWhenIdle(ArrangeGrid, (ClientData) gridPtr);
1394    }
1395}
1396
1397/*
1398 *----------------------------------------------------------------------
1399 *
1400 * GridLostSlaveProc --
1401 *
1402 *	This procedure is invoked by Tk whenever some other geometry claims
1403 *	control over a slave that used to be managed by us.
1404 *
1405 * Results:
1406 *	None.
1407 *
1408 * Side effects:
1409 *	Forgets all grid-related information about the slave.
1410 *
1411 *----------------------------------------------------------------------
1412 */
1413
1414static void
1415GridLostSlaveProc(
1416    ClientData clientData,	/* Grid structure for slave window that was
1417				 * stolen away. */
1418    Tk_Window tkwin)		/* Tk's handle for the slave window. */
1419{
1420    register Gridder *slavePtr = (Gridder *) clientData;
1421
1422    if (slavePtr->masterPtr->tkwin != Tk_Parent(slavePtr->tkwin)) {
1423	Tk_UnmaintainGeometry(slavePtr->tkwin, slavePtr->masterPtr->tkwin);
1424    }
1425    Unlink(slavePtr);
1426    Tk_UnmapWindow(slavePtr->tkwin);
1427}
1428
1429/*
1430 *----------------------------------------------------------------------
1431 *
1432 * AdjustOffsets --
1433 *
1434 *	This procedure adjusts the size of the layout to fit in the space
1435 *	provided. If it needs more space, the extra is added according to the
1436 *	weights. If it needs less, the space is removed according to the
1437 *	weights, but at no time does the size drop below the minsize specified
1438 *	for that slot.
1439 *
1440 * Results:
1441 *	The size used by the layout.
1442 *
1443 * Side effects:
1444 *	The slot offsets are modified to shrink the layout.
1445 *
1446 *----------------------------------------------------------------------
1447 */
1448
1449static int
1450AdjustOffsets(
1451    int size,			/* The total layout size (in pixels). */
1452    int slots,			/* Number of slots. */
1453    register SlotInfo *slotPtr)	/* Pointer to slot array. */
1454{
1455    register int slot;		/* Current slot. */
1456    int diff;			/* Extra pixels needed to add to the layout. */
1457    int totalWeight;		/* Sum of the weights for all the slots. */
1458    int weight;			/* Sum of the weights so far. */
1459    int minSize;		/* Minimum possible layout size. */
1460    int newDiff;		/* The most pixels that can be added on the
1461    				 * current pass. */
1462
1463    diff = size - slotPtr[slots-1].offset;
1464
1465    /*
1466     * The layout is already the correct size; all done.
1467     */
1468
1469    if (diff == 0) {
1470	return size;
1471    }
1472
1473    /*
1474     * If all the weights are zero, there is nothing more to do.
1475     */
1476
1477    totalWeight = 0;
1478    for (slot = 0; slot < slots; slot++) {
1479	totalWeight += slotPtr[slot].weight;
1480    }
1481
1482    if (totalWeight == 0) {
1483	return slotPtr[slots-1].offset;
1484    }
1485
1486    /*
1487     * Add extra space according to the slot weights. This is done
1488     * cumulatively to prevent round-off error accumulation.
1489     */
1490
1491    if (diff > 0) {
1492	weight = 0;
1493	for (slot = 0; slot < slots; slot++) {
1494	    weight += slotPtr[slot].weight;
1495	    slotPtr[slot].offset += diff * weight / totalWeight;
1496	}
1497	return size;
1498    }
1499
1500    /*
1501     * The layout must shrink below its requested size. Compute the minimum
1502     * possible size by looking at the slot minSizes. Store each slot's
1503     * minimum size in temp.
1504     */
1505
1506    minSize = 0;
1507    for (slot = 0; slot < slots; slot++) {
1508    	if (slotPtr[slot].weight > 0) {
1509	    slotPtr[slot].temp = slotPtr[slot].minSize;
1510	} else if (slot > 0) {
1511	    slotPtr[slot].temp = slotPtr[slot].offset - slotPtr[slot-1].offset;
1512	} else {
1513	    slotPtr[slot].temp = slotPtr[slot].offset;
1514	}
1515	minSize += slotPtr[slot].temp;
1516    }
1517
1518    /*
1519     * If the requested size is less than the minimum required size, set the
1520     * slot sizes to their minimum values.
1521     */
1522
1523    if (size <= minSize) {
1524    	int offset = 0;
1525
1526	for (slot = 0; slot < slots; slot++) {
1527	    offset += slotPtr[slot].temp;
1528	    slotPtr[slot].offset = offset;
1529	}
1530	return minSize;
1531    }
1532
1533    /*
1534     * Remove space from slots according to their weights. The weights get
1535     * renormalized anytime a slot shrinks to its minimum size.
1536     */
1537
1538    while (diff < 0) {
1539	/*
1540	 * Find the total weight for the shrinkable slots.
1541	 */
1542
1543	totalWeight = 0;
1544	for (slot = 0; slot < slots; slot++) {
1545	    int current = (slot == 0) ? slotPtr[slot].offset :
1546		    slotPtr[slot].offset - slotPtr[slot-1].offset;
1547
1548	    if (current > slotPtr[slot].minSize) {
1549		totalWeight += slotPtr[slot].weight;
1550		slotPtr[slot].temp = slotPtr[slot].weight;
1551	    } else {
1552		slotPtr[slot].temp = 0;
1553	    }
1554	}
1555	if (totalWeight == 0) {
1556	    break;
1557	}
1558
1559	/*
1560	 * Find the maximum amount of space we can distribute this pass.
1561	 */
1562
1563	newDiff = diff;
1564	for (slot = 0; slot < slots; slot++) {
1565	    int current;	/* Current size of this slot. */
1566	    int maxDiff;	/* Maximum diff that would cause this slot to
1567	    			 * equal its minsize. */
1568
1569	    if (slotPtr[slot].temp == 0) {
1570	    	continue;
1571	    }
1572	    current = (slot == 0) ? slotPtr[slot].offset :
1573		    slotPtr[slot].offset - slotPtr[slot-1].offset;
1574	    maxDiff = totalWeight * (slotPtr[slot].minSize - current)
1575		    / slotPtr[slot].temp;
1576	    if (maxDiff > newDiff) {
1577	    	newDiff = maxDiff;
1578	    }
1579	}
1580
1581	/*
1582	 * Now distribute the space.
1583	 */
1584
1585	weight = 0;
1586	for (slot = 0; slot < slots; slot++) {
1587	    weight += slotPtr[slot].temp;
1588	    slotPtr[slot].offset += newDiff * weight / totalWeight;
1589	}
1590    	diff -= newDiff;
1591    }
1592    return size;
1593}
1594
1595/*
1596 *----------------------------------------------------------------------
1597 *
1598 * AdjustForSticky --
1599 *
1600 *	This procedure adjusts the size of a slave in its cavity based on its
1601 *	"sticky" flags.
1602 *
1603 * Results:
1604 *	The input x, y, width, and height are changed to represent the desired
1605 *	coordinates of the slave.
1606 *
1607 * Side effects:
1608 *	None.
1609 *
1610 *----------------------------------------------------------------------
1611 */
1612
1613static void
1614AdjustForSticky(
1615    Gridder *slavePtr,	/* Slave window to arrange in its cavity. */
1616    int *xPtr,		/* Pixel location of the left edge of the cavity. */
1617    int *yPtr,		/* Pixel location of the top edge of the cavity. */
1618    int *widthPtr,	/* Width of the cavity (in pixels). */
1619    int *heightPtr)	/* Height of the cavity (in pixels). */
1620{
1621    int diffx = 0;	/* Cavity width - slave width. */
1622    int diffy = 0;	/* Cavity hight - slave height. */
1623    int sticky = slavePtr->sticky;
1624
1625    *xPtr += slavePtr->padLeft;
1626    *widthPtr -= slavePtr->padX;
1627    *yPtr += slavePtr->padTop;
1628    *heightPtr -= slavePtr->padY;
1629
1630    if (*widthPtr > (Tk_ReqWidth(slavePtr->tkwin) + slavePtr->iPadX)) {
1631	diffx = *widthPtr - (Tk_ReqWidth(slavePtr->tkwin) + slavePtr->iPadX);
1632	*widthPtr = Tk_ReqWidth(slavePtr->tkwin) + slavePtr->iPadX;
1633    }
1634
1635    if (*heightPtr > (Tk_ReqHeight(slavePtr->tkwin) + slavePtr->iPadY)) {
1636	diffy = *heightPtr - (Tk_ReqHeight(slavePtr->tkwin) + slavePtr->iPadY);
1637	*heightPtr = Tk_ReqHeight(slavePtr->tkwin) + slavePtr->iPadY;
1638    }
1639
1640    if (sticky&STICK_EAST && sticky&STICK_WEST) {
1641	*widthPtr += diffx;
1642    }
1643    if (sticky&STICK_NORTH && sticky&STICK_SOUTH) {
1644	*heightPtr += diffy;
1645    }
1646    if (!(sticky&STICK_WEST)) {
1647    	*xPtr += (sticky&STICK_EAST) ? diffx : diffx/2;
1648    }
1649    if (!(sticky&STICK_NORTH)) {
1650    	*yPtr += (sticky&STICK_SOUTH) ? diffy : diffy/2;
1651    }
1652}
1653
1654/*
1655 *----------------------------------------------------------------------
1656 *
1657 * ArrangeGrid --
1658 *
1659 *	This procedure is invoked (using the Tcl_DoWhenIdle mechanism) to
1660 *	re-layout a set of windows managed by the grid. It is invoked at idle
1661 *	time so that a series of grid requests can be merged into a single
1662 *	layout operation.
1663 *
1664 * Results:
1665 *	None.
1666 *
1667 * Side effects:
1668 *	The slaves of masterPtr may get resized or moved.
1669 *
1670 *----------------------------------------------------------------------
1671 */
1672
1673static void
1674ArrangeGrid(
1675    ClientData clientData)	/* Structure describing master whose slaves
1676				 * are to be re-layed out. */
1677{
1678    register Gridder *masterPtr = (Gridder *) clientData;
1679    register Gridder *slavePtr;
1680    GridMaster *slotPtr = masterPtr->masterDataPtr;
1681    int abort;
1682    int width, height;		/* Requested size of layout, in pixels. */
1683    int realWidth, realHeight;	/* Actual size layout should take-up. */
1684    int usedX, usedY;
1685
1686    masterPtr->flags &= ~REQUESTED_RELAYOUT;
1687
1688    /*
1689     * If the master has no slaves anymore, then don't do anything at all:
1690     * just leave the master's size as-is. Otherwise there is no way to
1691     * "relinquish" control over the master so another geometry manager can
1692     * take over.
1693     */
1694
1695    if (masterPtr->slavePtr == NULL) {
1696	return;
1697    }
1698
1699    if (masterPtr->masterDataPtr == NULL) {
1700	return;
1701    }
1702
1703    /*
1704     * Abort any nested call to ArrangeGrid for this window, since we'll do
1705     * everything necessary here, and set up so this call can be aborted if
1706     * necessary.
1707     */
1708
1709    if (masterPtr->abortPtr != NULL) {
1710	*masterPtr->abortPtr = 1;
1711    }
1712    masterPtr->abortPtr = &abort;
1713    abort = 0;
1714    Tcl_Preserve((ClientData) masterPtr);
1715
1716    /*
1717     * Call the constraint engine to fill in the row and column offsets.
1718     */
1719
1720    SetGridSize(masterPtr);
1721    width = ResolveConstraints(masterPtr, COLUMN, 0);
1722    height = ResolveConstraints(masterPtr, ROW, 0);
1723    width += Tk_InternalBorderLeft(masterPtr->tkwin) +
1724	    Tk_InternalBorderRight(masterPtr->tkwin);
1725    height += Tk_InternalBorderTop(masterPtr->tkwin) +
1726	    Tk_InternalBorderBottom(masterPtr->tkwin);
1727
1728    if (width < Tk_MinReqWidth(masterPtr->tkwin)) {
1729	width = Tk_MinReqWidth(masterPtr->tkwin);
1730    }
1731    if (height < Tk_MinReqHeight(masterPtr->tkwin)) {
1732	height = Tk_MinReqHeight(masterPtr->tkwin);
1733    }
1734
1735    if (((width != Tk_ReqWidth(masterPtr->tkwin))
1736	    || (height != Tk_ReqHeight(masterPtr->tkwin)))
1737	    && !(masterPtr->flags & DONT_PROPAGATE)) {
1738	Tk_GeometryRequest(masterPtr->tkwin, width, height);
1739	if (width>1 && height>1) {
1740	    masterPtr->flags |= REQUESTED_RELAYOUT;
1741	    Tcl_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
1742	}
1743	masterPtr->abortPtr = NULL;
1744	Tcl_Release((ClientData) masterPtr);
1745	return;
1746    }
1747
1748    /*
1749     * If the currently requested layout size doesn't match the master's
1750     * window size, then adjust the slot offsets according to the weights. If
1751     * all of the weights are zero, place the layout according to the anchor
1752     * value.
1753     */
1754
1755    realWidth = Tk_Width(masterPtr->tkwin) -
1756	    Tk_InternalBorderLeft(masterPtr->tkwin) -
1757	    Tk_InternalBorderRight(masterPtr->tkwin);
1758    realHeight = Tk_Height(masterPtr->tkwin) -
1759	    Tk_InternalBorderTop(masterPtr->tkwin) -
1760	    Tk_InternalBorderBottom(masterPtr->tkwin);
1761    usedX = AdjustOffsets(realWidth,
1762	    MAX(slotPtr->columnEnd, slotPtr->columnMax), slotPtr->columnPtr);
1763    usedY = AdjustOffsets(realHeight, MAX(slotPtr->rowEnd, slotPtr->rowMax),
1764	    slotPtr->rowPtr);
1765    TkComputeAnchor(masterPtr->masterDataPtr->anchor, masterPtr->tkwin,
1766	    0, 0, usedX, usedY, &slotPtr->startX, &slotPtr->startY);
1767
1768    /*
1769     * Now adjust the actual size of the slave to its cavity by computing the
1770     * cavity size, and adjusting the widget according to its stickyness.
1771     */
1772
1773    for (slavePtr = masterPtr->slavePtr; slavePtr != NULL && !abort;
1774	    slavePtr = slavePtr->nextPtr) {
1775	int x, y;			/* Top left coordinate */
1776	int width, height;		/* Slot or slave size */
1777	int col = slavePtr->column;
1778	int row = slavePtr->row;
1779
1780	x = (col>0) ? slotPtr->columnPtr[col-1].offset : 0;
1781	y = (row>0) ? slotPtr->rowPtr[row-1].offset : 0;
1782
1783	width = slotPtr->columnPtr[slavePtr->numCols+col-1].offset - x;
1784	height = slotPtr->rowPtr[slavePtr->numRows+row-1].offset - y;
1785
1786	x += slotPtr->startX;
1787	y += slotPtr->startY;
1788
1789	AdjustForSticky(slavePtr, &x, &y, &width, &height);
1790
1791	/*
1792	 * Now put the window in the proper spot. (This was taken directly
1793	 * from tkPack.c.) If the slave is a child of the master, then do this
1794	 * here. Otherwise let Tk_MaintainGeometry do the work.
1795	 */
1796
1797	if (masterPtr->tkwin == Tk_Parent(slavePtr->tkwin)) {
1798	    if ((width <= 0) || (height <= 0)) {
1799		Tk_UnmapWindow(slavePtr->tkwin);
1800	    } else {
1801		if ((x != Tk_X(slavePtr->tkwin))
1802			|| (y != Tk_Y(slavePtr->tkwin))
1803			|| (width != Tk_Width(slavePtr->tkwin))
1804			|| (height != Tk_Height(slavePtr->tkwin))) {
1805		    Tk_MoveResizeWindow(slavePtr->tkwin, x, y, width, height);
1806		}
1807		if (abort) {
1808		    break;
1809		}
1810
1811		/*
1812		 * Don't map the slave if the master isn't mapped: wait until
1813		 * the master gets mapped later.
1814		 */
1815
1816		if (Tk_IsMapped(masterPtr->tkwin)) {
1817		    Tk_MapWindow(slavePtr->tkwin);
1818		}
1819	    }
1820	} else if ((width <= 0) || (height <= 0)) {
1821	    Tk_UnmaintainGeometry(slavePtr->tkwin, masterPtr->tkwin);
1822	    Tk_UnmapWindow(slavePtr->tkwin);
1823	} else {
1824	    Tk_MaintainGeometry(slavePtr->tkwin, masterPtr->tkwin, x, y,
1825		    width, height);
1826	}
1827    }
1828
1829    masterPtr->abortPtr = NULL;
1830    Tcl_Release((ClientData) masterPtr);
1831}
1832
1833/*
1834 *----------------------------------------------------------------------
1835 *
1836 * ResolveConstraints --
1837 *
1838 *	Resolve all of the column and row boundaries. Most of the calculations
1839 *	are identical for rows and columns, so this procedure is called twice,
1840 *	once for rows, and again for columns.
1841 *
1842 * Results:
1843 *	The offset (in pixels) from the left/top edge of this layout is
1844 *	returned.
1845 *
1846 * Side effects:
1847 *	The slot offsets are copied into the SlotInfo structure for the
1848 *	geometry master.
1849 *
1850 *----------------------------------------------------------------------
1851 */
1852
1853static int
1854ResolveConstraints(
1855    Gridder *masterPtr,		/* The geometry master for this grid. */
1856    int slotType,		/* Either ROW or COLUMN. */
1857    int maxOffset)		/* The actual maximum size of this layout in
1858				 * pixels, or 0 (not currently used). */
1859{
1860    register SlotInfo *slotPtr;	/* Pointer to row/col constraints. */
1861    register Gridder *slavePtr;	/* List of slave windows in this grid. */
1862    int constraintCount;	/* Count of rows or columns that have
1863				 * constraints. */
1864    int slotCount;		/* Last occupied row or column. */
1865    int gridCount;		/* The larger of slotCount and
1866				 * constraintCount. */
1867    GridLayout *layoutPtr;	/* Temporary layout structure. */
1868    int requiredSize;		/* The natural size of the grid (pixels).
1869				 * This is the minimum size needed to
1870				 * accomodate all of the slaves at their
1871				 * requested sizes. */
1872    int offset;			/* The pixel offset of the right edge of the
1873				 * current slot from the beginning of the
1874				 * layout. */
1875    int slot;			/* The current slot. */
1876    int start;			/* The first slot of a contiguous set whose
1877				 * constraints are not yet fully resolved. */
1878    int end;			/* The Last slot of a contiguous set whose
1879				 * constraints are not yet fully resolved. */
1880    UniformGroup uniformPre[UNIFORM_PREALLOC];
1881				/* Pre-allocated space for uniform groups. */
1882    UniformGroup *uniformGroupPtr;
1883				/* Uniform groups data. */
1884    int uniformGroups;		/* Number of currently used uniform groups. */
1885    int uniformGroupsAlloced;	/* Size of allocated space for uniform
1886				 * groups. */
1887    int weight, minSize;
1888    int prevGrow, accWeight, grow;
1889
1890    /*
1891     * For typical sized tables, we'll use stack space for the layout data to
1892     * avoid the overhead of a malloc and free for every layout.
1893     */
1894
1895    GridLayout layoutData[TYPICAL_SIZE + 1];
1896
1897    if (slotType == COLUMN) {
1898	constraintCount = masterPtr->masterDataPtr->columnMax;
1899	slotCount = masterPtr->masterDataPtr->columnEnd;
1900	slotPtr = masterPtr->masterDataPtr->columnPtr;
1901    } else {
1902	constraintCount = masterPtr->masterDataPtr->rowMax;
1903	slotCount = masterPtr->masterDataPtr->rowEnd;
1904	slotPtr = masterPtr->masterDataPtr->rowPtr;
1905    }
1906
1907    /*
1908     * Make sure there is enough memory for the layout.
1909     */
1910
1911    gridCount = MAX(constraintCount, slotCount);
1912    if (gridCount >= TYPICAL_SIZE) {
1913	layoutPtr = (GridLayout *)
1914		ckalloc(sizeof(GridLayout) * (1+gridCount));
1915    } else {
1916	layoutPtr = layoutData;
1917    }
1918
1919    /*
1920     * Allocate an extra layout slot to represent the left/top edge of the 0th
1921     * slot to make it easier to calculate slot widths from offsets without
1922     * special case code.
1923     *
1924     * Initialize the "dummy" slot to the left/top of the table. This slot
1925     * avoids special casing the first slot.
1926     */
1927
1928    layoutPtr->minOffset = 0;
1929    layoutPtr->maxOffset = 0;
1930    layoutPtr++;
1931
1932    /*
1933     * Step 1.
1934     * Copy the slot constraints into the layout structure, and initialize the
1935     * rest of the fields.
1936     */
1937
1938    for (slot=0; slot < constraintCount; slot++) {
1939	layoutPtr[slot].minSize = slotPtr[slot].minSize;
1940	layoutPtr[slot].weight = slotPtr[slot].weight;
1941	layoutPtr[slot].uniform = slotPtr[slot].uniform;
1942	layoutPtr[slot].pad = slotPtr[slot].pad;
1943	layoutPtr[slot].binNextPtr = NULL;
1944    }
1945    for (; slot<gridCount; slot++) {
1946	layoutPtr[slot].minSize = 0;
1947	layoutPtr[slot].weight = 0;
1948	layoutPtr[slot].uniform = NULL;
1949	layoutPtr[slot].pad = 0;
1950	layoutPtr[slot].binNextPtr = NULL;
1951    }
1952
1953    /*
1954     * Step 2.
1955     * Slaves with a span of 1 are used to determine the minimum size of each
1956     * slot. Slaves whose span is two or more slots don't contribute to the
1957     * minimum size of each slot directly, but can cause slots to grow if
1958     * their size exceeds the the sizes of the slots they span.
1959     *
1960     * Bin all slaves whose spans are > 1 by their right edges. This allows
1961     * the computation on minimum and maximum possible layout sizes at each
1962     * slot boundary, without the need to re-sort the slaves.
1963     */
1964
1965    switch (slotType) {
1966    case COLUMN:
1967	for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
1968		slavePtr = slavePtr->nextPtr) {
1969	    int rightEdge = slavePtr->column + slavePtr->numCols - 1;
1970
1971	    slavePtr->size = Tk_ReqWidth(slavePtr->tkwin) + slavePtr->padX
1972		    + slavePtr->iPadX + slavePtr->doubleBw;
1973	    if (slavePtr->numCols > 1) {
1974		slavePtr->binNextPtr = layoutPtr[rightEdge].binNextPtr;
1975		layoutPtr[rightEdge].binNextPtr = slavePtr;
1976	    } else {
1977		int size = slavePtr->size + layoutPtr[rightEdge].pad;
1978
1979		if (size > layoutPtr[rightEdge].minSize) {
1980		    layoutPtr[rightEdge].minSize = size;
1981		}
1982	    }
1983	}
1984	break;
1985    case ROW:
1986	for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
1987		slavePtr = slavePtr->nextPtr) {
1988	    int rightEdge = slavePtr->row + slavePtr->numRows - 1;
1989
1990	    slavePtr->size = Tk_ReqHeight(slavePtr->tkwin) + slavePtr->padY
1991		    + slavePtr->iPadY + slavePtr->doubleBw;
1992	    if (slavePtr->numRows > 1) {
1993		slavePtr->binNextPtr = layoutPtr[rightEdge].binNextPtr;
1994		layoutPtr[rightEdge].binNextPtr = slavePtr;
1995	    } else {
1996		int size = slavePtr->size + layoutPtr[rightEdge].pad;
1997
1998		if (size > layoutPtr[rightEdge].minSize) {
1999		    layoutPtr[rightEdge].minSize = size;
2000		}
2001	    }
2002	}
2003	break;
2004    }
2005
2006    /*
2007     * Step 2b.
2008     * Consider demands on uniform sizes.
2009     */
2010
2011    uniformGroupPtr = uniformPre;
2012    uniformGroupsAlloced = UNIFORM_PREALLOC;
2013    uniformGroups = 0;
2014
2015    for (slot = 0; slot < gridCount; slot++) {
2016	if (layoutPtr[slot].uniform != NULL) {
2017	    for (start = 0; start < uniformGroups; start++) {
2018		if (uniformGroupPtr[start].group == layoutPtr[slot].uniform) {
2019		    break;
2020		}
2021	    }
2022	    if (start >= uniformGroups) {
2023		/*
2024		 * Have not seen that group before, set up data for it.
2025		 */
2026
2027		if (uniformGroups >= uniformGroupsAlloced) {
2028		    /*
2029		     * We need to allocate more space.
2030		     */
2031
2032		    size_t oldSize = uniformGroupsAlloced
2033			    * sizeof(UniformGroup);
2034		    size_t newSize = (uniformGroupsAlloced + UNIFORM_PREALLOC)
2035			    * sizeof(UniformGroup);
2036		    UniformGroup *newUG = (UniformGroup *) ckalloc(newSize);
2037		    UniformGroup *oldUG = uniformGroupPtr;
2038
2039		    memcpy(newUG, oldUG, oldSize);
2040		    if (oldUG != uniformPre) {
2041			ckfree((char *) oldUG);
2042		    }
2043		    uniformGroupPtr = newUG;
2044		    uniformGroupsAlloced += UNIFORM_PREALLOC;
2045		}
2046		uniformGroups++;
2047		uniformGroupPtr[start].group = layoutPtr[slot].uniform;
2048		uniformGroupPtr[start].minSize = 0;
2049	    }
2050	    weight = layoutPtr[slot].weight;
2051	    weight = weight > 0 ? weight : 1;
2052	    minSize = (layoutPtr[slot].minSize + weight - 1) / weight;
2053	    if (minSize > uniformGroupPtr[start].minSize) {
2054		uniformGroupPtr[start].minSize = minSize;
2055	    }
2056	}
2057    }
2058
2059    /*
2060     * Data has been gathered about uniform groups. Now relayout accordingly.
2061     */
2062
2063    if (uniformGroups > 0) {
2064	for (slot = 0; slot < gridCount; slot++) {
2065	    if (layoutPtr[slot].uniform != NULL) {
2066		for (start = 0; start < uniformGroups; start++) {
2067		    if (uniformGroupPtr[start].group ==
2068			    layoutPtr[slot].uniform) {
2069			weight = layoutPtr[slot].weight;
2070			weight = weight > 0 ? weight : 1;
2071			layoutPtr[slot].minSize =
2072				uniformGroupPtr[start].minSize * weight;
2073			break;
2074		    }
2075		}
2076	    }
2077	}
2078    }
2079
2080    if (uniformGroupPtr != uniformPre) {
2081	ckfree((char *) uniformGroupPtr);
2082    }
2083
2084    /*
2085     * Step 3.
2086     * Determine the minimum slot offsets going from left to right that would
2087     * fit all of the slaves. This determines the minimum
2088     */
2089
2090    for (offset=0,slot=0; slot < gridCount; slot++) {
2091	layoutPtr[slot].minOffset = layoutPtr[slot].minSize + offset;
2092	for (slavePtr = layoutPtr[slot].binNextPtr; slavePtr != NULL;
2093		slavePtr = slavePtr->binNextPtr) {
2094	    int span = (slotType == COLUMN) ?
2095		    slavePtr->numCols : slavePtr->numRows;
2096	    int required = slavePtr->size + layoutPtr[slot - span].minOffset;
2097
2098	    if (required > layoutPtr[slot].minOffset) {
2099		layoutPtr[slot].minOffset = required;
2100	    }
2101	}
2102	offset = layoutPtr[slot].minOffset;
2103    }
2104
2105    /*
2106     * At this point, we know the minimum required size of the entire layout.
2107     * It might be prudent to stop here if our "master" will resize itself to
2108     * this size.
2109     */
2110
2111    requiredSize = offset;
2112    if (maxOffset > offset) {
2113	offset=maxOffset;
2114    }
2115
2116    /*
2117     * Step 4.
2118     * Determine the minimum slot offsets going from right to left, bounding
2119     * the pixel range of each slot boundary. Pre-fill all of the right
2120     * offsets with the actual size of the table; they will be reduced as
2121     * required.
2122     */
2123
2124    for (slot=0; slot < gridCount; slot++) {
2125	layoutPtr[slot].maxOffset = offset;
2126    }
2127    for (slot=gridCount-1; slot > 0;) {
2128	for (slavePtr = layoutPtr[slot].binNextPtr; slavePtr != NULL;
2129		slavePtr = slavePtr->binNextPtr) {
2130	    int span = (slotType == COLUMN) ?
2131		    slavePtr->numCols : slavePtr->numRows;
2132	    int require = offset - slavePtr->size;
2133	    int startSlot = slot - span;
2134
2135	    if (startSlot >=0 && require < layoutPtr[startSlot].maxOffset) {
2136		layoutPtr[startSlot].maxOffset = require;
2137	    }
2138	}
2139	offset -= layoutPtr[slot].minSize;
2140	slot--;
2141	if (layoutPtr[slot].maxOffset < offset) {
2142	    offset = layoutPtr[slot].maxOffset;
2143	} else {
2144	    layoutPtr[slot].maxOffset = offset;
2145	}
2146    }
2147
2148    /*
2149     * Step 5.
2150     * At this point, each slot boundary has a range of values that will
2151     * satisfy the overall layout size. Make repeated passes over the layout
2152     * structure looking for spans of slot boundaries where the minOffsets are
2153     * less than the maxOffsets, and adjust the offsets according to the slot
2154     * weights. At each pass, at least one slot boundary will have its range
2155     * of possible values fixed at a single value.
2156     */
2157
2158    for (start = 0; start < gridCount;) {
2159	int totalWeight = 0;	/* Sum of the weights for all of the slots in
2160				 * this span. */
2161	int need = 0;		/* The minimum space needed to layout this
2162				 * span. */
2163	int have;		/* The actual amount of space that will be
2164				 * taken up by this span. */
2165	int weight;		/* Cumulative weights of the columns in this
2166				 * span. */
2167	int noWeights = 0;	/* True if the span has no weights. */
2168
2169	/*
2170	 * Find a span by identifying ranges of slots whose edges are already
2171	 * constrained at fixed offsets, but whose internal slot boundaries
2172	 * have a range of possible positions.
2173	 */
2174
2175	if (layoutPtr[start].minOffset == layoutPtr[start].maxOffset) {
2176	    start++;
2177	    continue;
2178	}
2179
2180	for (end = start + 1; end < gridCount; end++) {
2181	    if (layoutPtr[end].minOffset == layoutPtr[end].maxOffset) {
2182		break;
2183	    }
2184	}
2185
2186	/*
2187	 * We found a span. Compute the total weight, minumum space required,
2188	 * for this span, and the actual amount of space the span should use.
2189	 */
2190
2191	for (slot = start; slot <= end; slot++) {
2192	    totalWeight += layoutPtr[slot].weight;
2193	    need += layoutPtr[slot].minSize;
2194	}
2195	have = layoutPtr[end].maxOffset - layoutPtr[start-1].minOffset;
2196
2197	/*
2198	 * If all the weights in the span are zero, then distribute the extra
2199	 * space evenly.
2200	 */
2201
2202	if (totalWeight == 0) {
2203	    noWeights++;
2204	    totalWeight = end - start + 1;
2205	}
2206
2207	/*
2208	 * It might not be possible to give the span all of the space
2209	 * available on this pass without violating the size constraints of
2210	 * one or more of the internal slot boundaries. Try to determine the
2211	 * maximum amount of space that when added to the entire span, would
2212	 * cause a slot boundary to have its possible range reduced to one
2213	 * value, and reduce the amount of extra space allocated on this pass
2214	 * accordingly.
2215	 *
2216	 * The calculation is done cumulatively to avoid accumulating roundoff
2217	 * errors.
2218	 */
2219
2220	do {
2221	    int prevMinOffset = layoutPtr[start - 1].minOffset;
2222
2223	    prevGrow = 0;
2224	    accWeight = 0;
2225	    for (slot = start; slot <= end; slot++) {
2226		weight = noWeights ? 1 : layoutPtr[slot].weight;
2227		accWeight += weight;
2228		grow = (have - need) * accWeight / totalWeight - prevGrow;
2229		prevGrow += grow;
2230
2231		if ((weight > 0) &&
2232			((prevMinOffset + layoutPtr[slot].minSize + grow)
2233			> layoutPtr[slot].maxOffset)) {
2234		    int newHave;
2235
2236		    /*
2237		     * There is not enough room to grow that much. Calculate
2238		     * how much this slot can grow and how much "have" that
2239		     * corresponds to.
2240		     */
2241
2242		    grow = layoutPtr[slot].maxOffset -
2243			    layoutPtr[slot].minSize - prevMinOffset;
2244		    newHave = grow * totalWeight / weight;
2245		    if (newHave > totalWeight) {
2246			/*
2247			 * By distributing multiples of totalWeight we
2248			 * minimize rounding errors since they will only
2249			 * happen in the last loop(s).
2250			 */
2251
2252			newHave = newHave / totalWeight * totalWeight;
2253		    }
2254		    if (newHave <= 0) {
2255			/*
2256			 * We can end up with a "have" of 0 here if the
2257			 * previous slots have taken all the space. In that
2258			 * case we cannot guess an appropriate "have" so we
2259			 * just try some lower "have" that is >= 1, to make
2260			 * sure this terminates.
2261			 */
2262
2263			newHave = (have - need) - 1;
2264			if (newHave > (3 * totalWeight)) {
2265			    /*
2266			     * Go down 25% for large values.
2267			     */
2268			    newHave = newHave * 3 / 4;
2269			}
2270
2271			if (newHave > totalWeight) {
2272			    /*
2273			     * Round down to a multiple of totalWeight.
2274			     */
2275			    newHave = newHave / totalWeight * totalWeight;
2276			}
2277
2278			if (newHave <= 0) {
2279			    newHave = 1;
2280			}
2281		    }
2282		    have = newHave + need;
2283
2284		    /*
2285		     * Restart loop to check if the new "have" will fit.
2286		     */
2287
2288		    break;
2289		}
2290		prevMinOffset += layoutPtr[slot].minSize + grow;
2291		if (prevMinOffset < layoutPtr[slot].minOffset) {
2292		    prevMinOffset = layoutPtr[slot].minOffset;
2293		}
2294	    }
2295
2296	    /*
2297	     * Quit the outer loop if the inner loop ran all the way.
2298	     */
2299	} while (slot <= end);
2300
2301	/*
2302	 * Now distribute the extra space among the slots by adjusting the
2303	 * minSizes and minOffsets.
2304	 */
2305
2306	prevGrow = 0;
2307	accWeight = 0;
2308	for (slot = start; slot <= end; slot++) {
2309	    accWeight += noWeights ? 1 : layoutPtr[slot].weight;
2310	    grow = (have - need) * accWeight / totalWeight - prevGrow;
2311	    prevGrow += grow;
2312	    layoutPtr[slot].minSize += grow;
2313	    if ((layoutPtr[slot-1].minOffset + layoutPtr[slot].minSize)
2314		    > layoutPtr[slot].minOffset) {
2315		layoutPtr[slot].minOffset = layoutPtr[slot-1].minOffset +
2316			layoutPtr[slot].minSize;
2317	    }
2318	}
2319
2320	/*
2321	 * Having pushed the top/left boundaries of the slots to take up extra
2322	 * space, the bottom/right space is recalculated to propagate the new
2323	 * space allocation.
2324	 */
2325
2326	for (slot = end; slot > start; slot--) {
2327	    /*
2328	     * maxOffset may not go up.
2329	     */
2330
2331	    if ((layoutPtr[slot].maxOffset-layoutPtr[slot].minSize)
2332		    < layoutPtr[slot-1].maxOffset) {
2333		layoutPtr[slot-1].maxOffset =
2334			layoutPtr[slot].maxOffset-layoutPtr[slot].minSize;
2335	    }
2336	}
2337    }
2338
2339    /*
2340     * Step 6.
2341     * All of the space has been apportioned; copy the layout information back
2342     * into the master.
2343     */
2344
2345    for (slot=0; slot < gridCount; slot++) {
2346	slotPtr[slot].offset = layoutPtr[slot].minOffset;
2347    }
2348
2349    --layoutPtr;
2350    if (layoutPtr != layoutData) {
2351	ckfree((char *) layoutPtr);
2352    }
2353    return requiredSize;
2354}
2355
2356/*
2357 *----------------------------------------------------------------------
2358 *
2359 * GetGrid --
2360 *
2361 *	This internal procedure is used to locate a Grid structure for a given
2362 *	window, creating one if one doesn't exist already.
2363 *
2364 * Results:
2365 *	The return value is a pointer to the Grid structure corresponding to
2366 *	tkwin.
2367 *
2368 * Side effects:
2369 *	A new grid structure may be created. If so, then a callback is set up
2370 *	to clean things up when the window is deleted.
2371 *
2372 *----------------------------------------------------------------------
2373 */
2374
2375static Gridder *
2376GetGrid(
2377    Tk_Window tkwin)		/* Token for window for which grid structure
2378				 * is desired. */
2379{
2380    register Gridder *gridPtr;
2381    Tcl_HashEntry *hPtr;
2382    int isNew;
2383    TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr;
2384
2385    if (!dispPtr->gridInit) {
2386	Tcl_InitHashTable(&dispPtr->gridHashTable, TCL_ONE_WORD_KEYS);
2387	dispPtr->gridInit = 1;
2388    }
2389
2390    /*
2391     * See if there's already grid for this window. If not, then create a new
2392     * one.
2393     */
2394
2395    hPtr = Tcl_CreateHashEntry(&dispPtr->gridHashTable, (char*) tkwin, &isNew);
2396    if (!isNew) {
2397	return (Gridder *) Tcl_GetHashValue(hPtr);
2398    }
2399    gridPtr = (Gridder *) ckalloc(sizeof(Gridder));
2400    gridPtr->tkwin = tkwin;
2401    gridPtr->masterPtr = NULL;
2402    gridPtr->masterDataPtr = NULL;
2403    gridPtr->nextPtr = NULL;
2404    gridPtr->slavePtr = NULL;
2405    gridPtr->binNextPtr = NULL;
2406
2407    gridPtr->column = -1;
2408    gridPtr->row = -1;
2409    gridPtr->numCols = 1;
2410    gridPtr->numRows = 1;
2411
2412    gridPtr->padX = 0;
2413    gridPtr->padY = 0;
2414    gridPtr->padLeft = 0;
2415    gridPtr->padTop = 0;
2416    gridPtr->iPadX = 0;
2417    gridPtr->iPadY = 0;
2418    gridPtr->doubleBw = 2*Tk_Changes(tkwin)->border_width;
2419    gridPtr->abortPtr = NULL;
2420    gridPtr->flags = 0;
2421    gridPtr->sticky = 0;
2422    gridPtr->size = 0;
2423    gridPtr->masterDataPtr = NULL;
2424    Tcl_SetHashValue(hPtr, gridPtr);
2425    Tk_CreateEventHandler(tkwin, StructureNotifyMask,
2426	    GridStructureProc, (ClientData) gridPtr);
2427    return gridPtr;
2428}
2429
2430/*
2431 *----------------------------------------------------------------------
2432 *
2433 * SetGridSize --
2434 *
2435 *	This internal procedure sets the size of the grid occupied by slaves.
2436 *
2437 * Results:
2438 *	None
2439 *
2440 * Side effects:
2441 *	The width and height arguments are filled in the master data
2442 *	structure. Additional space is allocated for the constraints to
2443 *	accomodate the offsets.
2444 *
2445 *----------------------------------------------------------------------
2446 */
2447
2448static void
2449SetGridSize(
2450    Gridder *masterPtr)		/* The geometry master for this grid. */
2451{
2452    register Gridder *slavePtr;	/* Current slave window. */
2453    int maxX = 0, maxY = 0;
2454
2455    for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
2456	    slavePtr = slavePtr->nextPtr) {
2457	maxX = MAX(maxX, slavePtr->numCols + slavePtr->column);
2458	maxY = MAX(maxY, slavePtr->numRows + slavePtr->row);
2459    }
2460    masterPtr->masterDataPtr->columnEnd = maxX;
2461    masterPtr->masterDataPtr->rowEnd = maxY;
2462    CheckSlotData(masterPtr, maxX, COLUMN, CHECK_SPACE);
2463    CheckSlotData(masterPtr, maxY, ROW, CHECK_SPACE);
2464}
2465
2466/*
2467 *----------------------------------------------------------------------
2468 *
2469 * SetSlaveColumn --
2470 *
2471 *	Update column data for a slave, checking that MAX_ELEMENT bound
2472 *      is not passed.
2473 *
2474 * Results:
2475 *	TCL_ERROR if out of bounds, TCL_OK otherwise
2476 *
2477 * Side effects:
2478 *	Slave fields are updated.
2479 *
2480 *----------------------------------------------------------------------
2481 */
2482
2483static int
2484SetSlaveColumn(
2485    Tcl_Interp *interp,		/* Interp for error message. */
2486    Gridder *slavePtr,		/* Slave to be updated. */
2487    int column,			/* New column or -1 to be unchanged. */
2488    int numCols)		/* New columnspan or -1 to be unchanged. */
2489{
2490    int newColumn, newNumCols, lastCol;
2491
2492    newColumn = (column >= 0) ? column : slavePtr->column;
2493    newNumCols = (numCols >= 1) ? numCols : slavePtr->numCols;
2494
2495    lastCol = ((newColumn >= 0) ? newColumn : 0) + newNumCols;
2496    if (lastCol >= MAX_ELEMENT) {
2497	Tcl_SetResult(interp, "Column out of bounds", TCL_STATIC);
2498	return TCL_ERROR;
2499    }
2500
2501    slavePtr->column = newColumn;
2502    slavePtr->numCols = newNumCols;
2503    return TCL_OK;
2504}
2505
2506/*
2507 *----------------------------------------------------------------------
2508 *
2509 * SetSlaveRow --
2510 *
2511 *	Update row data for a slave, checking that MAX_ELEMENT bound
2512 *      is not passed.
2513 *
2514 * Results:
2515 *	TCL_ERROR if out of bounds, TCL_OK otherwise
2516 *
2517 * Side effects:
2518 *	Slave fields are updated.
2519 *
2520 *----------------------------------------------------------------------
2521 */
2522
2523static int
2524SetSlaveRow(
2525    Tcl_Interp *interp,		/* Interp for error message. */
2526    Gridder *slavePtr,		/* Slave to be updated. */
2527    int row,			/* New row or -1 to be unchanged. */
2528    int numRows)		/* New rowspan or -1 to be unchanged. */
2529{
2530    int newRow, newNumRows, lastRow;
2531
2532    newRow = (row >= 0) ? row : slavePtr->row;
2533    newNumRows = (numRows >= 1) ? numRows : slavePtr->numRows;
2534
2535    lastRow = ((newRow >= 0) ? newRow : 0) + newNumRows;
2536    if (lastRow >= MAX_ELEMENT) {
2537	Tcl_SetResult(interp, "Row out of bounds", TCL_STATIC);
2538	return TCL_ERROR;
2539    }
2540
2541    slavePtr->row = newRow;
2542    slavePtr->numRows = newNumRows;
2543    return TCL_OK;
2544}
2545
2546/*
2547 *----------------------------------------------------------------------
2548 *
2549 * CheckSlotData --
2550 *
2551 *	This internal procedure is used to manage the storage for row and
2552 *	column (slot) constraints.
2553 *
2554 * Results:
2555 *	TRUE if the index is OK, False otherwise.
2556 *
2557 * Side effects:
2558 *	A new master grid structure may be created. If so, then it is
2559 *	initialized. In addition, additional storage for a row or column
2560 *	constraints may be allocated, and the constraint maximums are
2561 *	adjusted.
2562 *
2563 *----------------------------------------------------------------------
2564 */
2565
2566static int
2567CheckSlotData(
2568    Gridder *masterPtr,		/* The geometry master for this grid. */
2569    int slot,			/* Which slot to look at. */
2570    int slotType,		/* ROW or COLUMN. */
2571    int checkOnly)		/* Don't allocate new space if true. */
2572{
2573    int numSlot;		/* Number of slots already allocated (Space) */
2574    int end;			/* Last used constraint. */
2575
2576    /*
2577     * If slot is out of bounds, return immediately.
2578     */
2579
2580    if (slot < 0 || slot >= MAX_ELEMENT) {
2581	return TCL_ERROR;
2582    }
2583
2584    if ((checkOnly == CHECK_ONLY) && (masterPtr->masterDataPtr == NULL)) {
2585	return TCL_ERROR;
2586    }
2587
2588    /*
2589     * If we need to allocate more space, allocate a little extra to avoid
2590     * repeated re-alloc's for large tables. We need enough space to hold all
2591     * of the offsets as well.
2592     */
2593
2594    InitMasterData(masterPtr);
2595    end = (slotType == ROW) ? masterPtr->masterDataPtr->rowMax :
2596	    masterPtr->masterDataPtr->columnMax;
2597    if (checkOnly == CHECK_ONLY) {
2598    	return ((end < slot) ? TCL_ERROR : TCL_OK);
2599    } else {
2600    	numSlot = (slotType == ROW) ? masterPtr->masterDataPtr->rowSpace
2601		: masterPtr->masterDataPtr->columnSpace;
2602    	if (slot >= numSlot) {
2603	    int newNumSlot = slot + PREALLOC;
2604	    size_t oldSize = numSlot * sizeof(SlotInfo);
2605	    size_t newSize = newNumSlot * sizeof(SlotInfo);
2606	    SlotInfo *newSI = (SlotInfo *) ckalloc(newSize);
2607	    SlotInfo *oldSI = (slotType == ROW)
2608		    ? masterPtr->masterDataPtr->rowPtr
2609		    : masterPtr->masterDataPtr->columnPtr;
2610
2611	    memcpy(newSI, oldSI, oldSize);
2612	    memset(newSI+numSlot, 0, newSize - oldSize);
2613	    ckfree((char *) oldSI);
2614	    if (slotType == ROW) {
2615	 	masterPtr->masterDataPtr->rowPtr = newSI;
2616	    	masterPtr->masterDataPtr->rowSpace = newNumSlot;
2617	    } else {
2618	    	masterPtr->masterDataPtr->columnPtr = newSI;
2619	    	masterPtr->masterDataPtr->columnSpace = newNumSlot;
2620	    }
2621	}
2622	if (slot >= end && checkOnly != CHECK_SPACE) {
2623	    if (slotType == ROW) {
2624		masterPtr->masterDataPtr->rowMax = slot+1;
2625	    } else {
2626		masterPtr->masterDataPtr->columnMax = slot+1;
2627	    }
2628	}
2629    	return TCL_OK;
2630    }
2631}
2632
2633/*
2634 *----------------------------------------------------------------------
2635 *
2636 * InitMasterData --
2637 *
2638 *	This internal procedure is used to allocate and initialize the data
2639 *	for a geometry master, if the data doesn't exist already.
2640 *
2641 * Results:
2642 *	none
2643 *
2644 * Side effects:
2645 *	A new master grid structure may be created. If so, then it is
2646 *	initialized.
2647 *
2648 *----------------------------------------------------------------------
2649 */
2650
2651static void
2652InitMasterData(
2653    Gridder *masterPtr)
2654{
2655    if (masterPtr->masterDataPtr == NULL) {
2656	GridMaster *gridPtr = masterPtr->masterDataPtr = (GridMaster *)
2657		ckalloc(sizeof(GridMaster));
2658	size_t size = sizeof(SlotInfo) * TYPICAL_SIZE;
2659
2660	gridPtr->columnEnd = 0;
2661	gridPtr->columnMax = 0;
2662	gridPtr->columnPtr = (SlotInfo *) ckalloc(size);
2663	gridPtr->columnSpace = TYPICAL_SIZE;
2664	gridPtr->rowEnd = 0;
2665	gridPtr->rowMax = 0;
2666	gridPtr->rowPtr = (SlotInfo *) ckalloc(size);
2667	gridPtr->rowSpace = TYPICAL_SIZE;
2668	gridPtr->startX = 0;
2669	gridPtr->startY = 0;
2670	gridPtr->anchor = GRID_DEFAULT_ANCHOR;
2671
2672	memset(gridPtr->columnPtr, 0, size);
2673	memset(gridPtr->rowPtr, 0, size);
2674    }
2675}
2676
2677/*
2678 *----------------------------------------------------------------------
2679 *
2680 * Unlink --
2681 *
2682 *	Remove a grid from its master's list of slaves.
2683 *
2684 * Results:
2685 *	None.
2686 *
2687 * Side effects:
2688 *	The master will be scheduled for re-arranging, and the size of the
2689 *	grid will be adjusted accordingly
2690 *
2691 *----------------------------------------------------------------------
2692 */
2693
2694static void
2695Unlink(
2696    register Gridder *slavePtr)	/* Window to unlink. */
2697{
2698    register Gridder *masterPtr, *slavePtr2;
2699
2700    masterPtr = slavePtr->masterPtr;
2701    if (masterPtr == NULL) {
2702	return;
2703    }
2704
2705    if (masterPtr->slavePtr == slavePtr) {
2706	masterPtr->slavePtr = slavePtr->nextPtr;
2707    } else {
2708	for (slavePtr2=masterPtr->slavePtr ; ; slavePtr2=slavePtr2->nextPtr) {
2709	    if (slavePtr2 == NULL) {
2710		Tcl_Panic("Unlink couldn't find previous window");
2711	    }
2712	    if (slavePtr2->nextPtr == slavePtr) {
2713		slavePtr2->nextPtr = slavePtr->nextPtr;
2714		break;
2715	    }
2716	}
2717    }
2718    if (!(masterPtr->flags & REQUESTED_RELAYOUT)) {
2719	masterPtr->flags |= REQUESTED_RELAYOUT;
2720	Tcl_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
2721    }
2722    if (masterPtr->abortPtr != NULL) {
2723	*masterPtr->abortPtr = 1;
2724    }
2725
2726    SetGridSize(slavePtr->masterPtr);
2727    slavePtr->masterPtr = NULL;
2728}
2729
2730/*
2731 *----------------------------------------------------------------------
2732 *
2733 * DestroyGrid --
2734 *
2735 *	This procedure is invoked by Tcl_EventuallyFree or Tcl_Release to
2736 *	clean up the internal structure of a grid at a safe time (when no-one
2737 *	is using it anymore). Cleaning up the grid involves freeing the main
2738 *	structure for all windows and the master structure for geometry
2739 *	managers.
2740 *
2741 * Results:
2742 *	None.
2743 *
2744 * Side effects:
2745 *	Everything associated with the grid is freed up.
2746 *
2747 *----------------------------------------------------------------------
2748 */
2749
2750static void
2751DestroyGrid(
2752    char *memPtr)		/* Info about window that is now dead. */
2753{
2754    register Gridder *gridPtr = (Gridder *) memPtr;
2755
2756    if (gridPtr->masterDataPtr != NULL) {
2757	if (gridPtr->masterDataPtr->rowPtr != NULL) {
2758	    ckfree((char *) gridPtr->masterDataPtr -> rowPtr);
2759	}
2760	if (gridPtr->masterDataPtr->columnPtr != NULL) {
2761	    ckfree((char *) gridPtr->masterDataPtr -> columnPtr);
2762	}
2763	ckfree((char *) gridPtr->masterDataPtr);
2764    }
2765    ckfree((char *) gridPtr);
2766}
2767
2768/*
2769 *----------------------------------------------------------------------
2770 *
2771 * GridStructureProc --
2772 *
2773 *	This procedure is invoked by the Tk event dispatcher in response to
2774 *	StructureNotify events.
2775 *
2776 * Results:
2777 *	None.
2778 *
2779 * Side effects:
2780 *	If a window was just deleted, clean up all its grid-related
2781 *	information. If it was just resized, re-configure its slaves, if any.
2782 *
2783 *----------------------------------------------------------------------
2784 */
2785
2786static void
2787GridStructureProc(
2788    ClientData clientData,	/* Our information about window referred to by
2789				 * eventPtr. */
2790    XEvent *eventPtr)		/* Describes what just happened. */
2791{
2792    register Gridder *gridPtr = (Gridder *) clientData;
2793    TkDisplay *dispPtr = ((TkWindow *) gridPtr->tkwin)->dispPtr;
2794
2795    if (eventPtr->type == ConfigureNotify) {
2796	if ((gridPtr->slavePtr != NULL)
2797		&& !(gridPtr->flags & REQUESTED_RELAYOUT)) {
2798	    gridPtr->flags |= REQUESTED_RELAYOUT;
2799	    Tcl_DoWhenIdle(ArrangeGrid, (ClientData) gridPtr);
2800	}
2801	if ((gridPtr->masterPtr != NULL) &&
2802		(gridPtr->doubleBw != 2*Tk_Changes(gridPtr->tkwin)->border_width)) {
2803	    if (!(gridPtr->masterPtr->flags & REQUESTED_RELAYOUT)) {
2804		gridPtr->doubleBw = 2*Tk_Changes(gridPtr->tkwin)->border_width;
2805		gridPtr->masterPtr->flags |= REQUESTED_RELAYOUT;
2806		Tcl_DoWhenIdle(ArrangeGrid, (ClientData) gridPtr->masterPtr);
2807	    }
2808	}
2809    } else if (eventPtr->type == DestroyNotify) {
2810	register Gridder *gridPtr2, *nextPtr;
2811
2812	if (gridPtr->masterPtr != NULL) {
2813	    Unlink(gridPtr);
2814	}
2815	for (gridPtr2 = gridPtr->slavePtr; gridPtr2 != NULL;
2816		gridPtr2 = nextPtr) {
2817	    Tk_UnmapWindow(gridPtr2->tkwin);
2818	    gridPtr2->masterPtr = NULL;
2819	    nextPtr = gridPtr2->nextPtr;
2820	    gridPtr2->nextPtr = NULL;
2821	}
2822	Tcl_DeleteHashEntry(Tcl_FindHashEntry(&dispPtr->gridHashTable,
2823		(char *) gridPtr->tkwin));
2824	if (gridPtr->flags & REQUESTED_RELAYOUT) {
2825	    Tcl_CancelIdleCall(ArrangeGrid, (ClientData) gridPtr);
2826	}
2827	gridPtr->tkwin = NULL;
2828	Tcl_EventuallyFree((ClientData) gridPtr, DestroyGrid);
2829    } else if (eventPtr->type == MapNotify) {
2830	if ((gridPtr->slavePtr != NULL)
2831		&& !(gridPtr->flags & REQUESTED_RELAYOUT)) {
2832	    gridPtr->flags |= REQUESTED_RELAYOUT;
2833	    Tcl_DoWhenIdle(ArrangeGrid, (ClientData) gridPtr);
2834	}
2835    } else if (eventPtr->type == UnmapNotify) {
2836	register Gridder *gridPtr2;
2837
2838	for (gridPtr2 = gridPtr->slavePtr; gridPtr2 != NULL;
2839		gridPtr2 = gridPtr2->nextPtr) {
2840	    Tk_UnmapWindow(gridPtr2->tkwin);
2841	}
2842    }
2843}
2844
2845/*
2846 *----------------------------------------------------------------------
2847 *
2848 * ConfigureSlaves --
2849 *
2850 *	This implements the guts of the "grid configure" command. Given a list
2851 *	of slaves and configuration options, it arranges for the grid to
2852 *	manage the slaves and sets the specified options. Arguments consist
2853 *	of windows or window shortcuts followed by "-option value" pairs.
2854 *
2855 * Results:
2856 *	TCL_OK is returned if all went well. Otherwise, TCL_ERROR is returned
2857 *	and the interp's result is set to contain an error message.
2858 *
2859 * Side effects:
2860 *	Slave windows get taken over by the grid.
2861 *
2862 *----------------------------------------------------------------------
2863 */
2864
2865static int
2866ConfigureSlaves(
2867    Tcl_Interp *interp,		/* Interpreter for error reporting. */
2868    Tk_Window tkwin,		/* Any window in application containing
2869				 * slaves. Used to look up slave names. */
2870    int objc,			/* Number of elements in argv. */
2871    Tcl_Obj *CONST objv[])	/* Argument objects: contains one or more
2872				 * window names followed by any number of
2873				 * "option value" pairs. Caller must make sure
2874				 * that there is at least one window name. */
2875{
2876    Gridder *masterPtr = NULL;
2877    Gridder *slavePtr;
2878    Tk_Window other, slave, parent, ancestor;
2879    int i, j, tmp;
2880    int numWindows;
2881    int width;
2882    int defaultRow = -1;
2883    int defaultColumn = 0;	/* Default column number */
2884    int defaultColumnSpan = 1;	/* Default number of columns */
2885    char *lastWindow;		/* Use this window to base current row/col
2886				 * on */
2887    int numSkip;		/* Number of 'x' found */
2888    static CONST char *optionStrings[] = {
2889	"-column", "-columnspan", "-in", "-ipadx", "-ipady",
2890	"-padx", "-pady", "-row", "-rowspan", "-sticky", NULL
2891    };
2892    enum options {
2893	CONF_COLUMN, CONF_COLUMNSPAN, CONF_IN, CONF_IPADX, CONF_IPADY,
2894	CONF_PADX, CONF_PADY, CONF_ROW, CONF_ROWSPAN, CONF_STICKY };
2895    int index;
2896    char *string;
2897    char firstChar;
2898    int positionGiven;
2899
2900    /*
2901     * Count the number of windows, or window short-cuts.
2902     */
2903
2904    firstChar = 0;
2905    for (numWindows=0, i=0; i < objc; i++) {
2906	int length;
2907	char prevChar = firstChar;
2908
2909	string = Tcl_GetStringFromObj(objv[i], &length);
2910    	firstChar = string[0];
2911
2912	if (firstChar == '.') {
2913	    /*
2914	     * Check that windows are valid, and locate the first slave's
2915	     * parent window (default for -in).
2916	     */
2917
2918	    if (TkGetWindowFromObj(interp, tkwin, objv[i], &slave) != TCL_OK) {
2919		return TCL_ERROR;
2920	    }
2921	    if (masterPtr == NULL) {
2922		parent = Tk_Parent(slave);
2923		if (parent != NULL) {
2924		    masterPtr = GetGrid(parent);
2925		    InitMasterData(masterPtr);
2926		}
2927	    }
2928	    numWindows++;
2929	    continue;
2930    	}
2931	if (length > 1 && i == 0) {
2932	    Tcl_AppendResult(interp, "bad argument \"", string,
2933		    "\": must be name of window", NULL);
2934	    return TCL_ERROR;
2935	}
2936    	if (length > 1 && firstChar == '-') {
2937	    break;
2938	}
2939	if (length > 1) {
2940	    Tcl_AppendResult(interp, "unexpected parameter, \"",
2941		    string, "\", in configure list. ",
2942		    "Should be window name or option", NULL);
2943	    return TCL_ERROR;
2944	}
2945
2946	if ((firstChar == REL_HORIZ) && ((numWindows == 0) ||
2947		(prevChar == REL_SKIP) || (prevChar == REL_VERT))) {
2948	    Tcl_AppendResult(interp,
2949		    "Must specify window before shortcut '-'.", NULL);
2950	    return TCL_ERROR;
2951	}
2952
2953	if ((firstChar == REL_VERT) || (firstChar == REL_SKIP)
2954		|| (firstChar == REL_HORIZ)) {
2955	    continue;
2956	}
2957
2958	Tcl_AppendResult(interp, "invalid window shortcut, \"",
2959		string, "\" should be '-', 'x', or '^'", NULL);
2960	return TCL_ERROR;
2961    }
2962    numWindows = i;
2963
2964    if ((objc - numWindows) & 1) {
2965	Tcl_AppendResult(interp, "extra option or option with no value", NULL);
2966	return TCL_ERROR;
2967    }
2968
2969    /*
2970     * Go through all options looking for -in and -row, which are needed to be
2971     * found first to handle the special case where ^ is used on a row without
2972     * windows names, but with an -in option. Since all options are checked
2973     * here, we do not need to handle the error case again later.
2974     */
2975
2976    for (i = numWindows; i < objc; i += 2) {
2977	if (Tcl_GetIndexFromObj(interp, objv[i], optionStrings, "option", 0,
2978		&index) != TCL_OK) {
2979	    return TCL_ERROR;
2980	}
2981	if (index == CONF_IN) {
2982	    if (TkGetWindowFromObj(interp, tkwin, objv[i+1], &other) !=
2983		    TCL_OK) {
2984		return TCL_ERROR;
2985	    }
2986	    masterPtr = GetGrid(other);
2987	    InitMasterData(masterPtr);
2988	} else if (index == CONF_ROW) {
2989	    if (Tcl_GetIntFromObj(interp, objv[i+1], &tmp) != TCL_OK
2990		    || tmp < 0) {
2991		Tcl_ResetResult(interp);
2992		Tcl_AppendResult(interp, "bad row value \"",
2993			Tcl_GetString(objv[i+1]), "\": must be ",
2994			"a non-negative integer", NULL);
2995		return TCL_ERROR;
2996	    }
2997	    defaultRow = tmp;
2998	}
2999    }
3000
3001    /*
3002     * If no -row is given, use the first unoccupied row of the master.
3003     */
3004
3005    if (defaultRow < 0) {
3006	if (masterPtr != NULL && masterPtr->masterDataPtr != NULL) {
3007	    SetGridSize(masterPtr);
3008	    defaultRow = masterPtr->masterDataPtr->rowEnd;
3009	} else {
3010	    defaultRow = 0;
3011	}
3012    }
3013
3014    /*
3015     * Iterate over all of the slave windows and short-cuts, parsing options
3016     * for each slave. It's a bit wasteful to re-parse the options for each
3017     * slave, but things get too messy if we try to parse the arguments just
3018     * once at the beginning. For example, if a slave already is managed we
3019     * want to just change a few existing values without resetting everything.
3020     * If there are multiple windows, the -in option only gets processed for
3021     * the first window.
3022     */
3023
3024    positionGiven = 0;
3025    for (j = 0; j < numWindows; j++) {
3026	string = Tcl_GetString(objv[j]);
3027    	firstChar = string[0];
3028
3029	/*
3030	 * '^' and 'x' cause us to skip a column. '-' is processed as part of
3031	 * its preceeding slave.
3032	 */
3033
3034	if ((firstChar == REL_VERT) || (firstChar == REL_SKIP)) {
3035	    defaultColumn++;
3036	    continue;
3037	}
3038	if (firstChar == REL_HORIZ) {
3039	    continue;
3040	}
3041
3042	for (defaultColumnSpan = 1; j + defaultColumnSpan < numWindows;
3043		defaultColumnSpan++) {
3044	    char *string = Tcl_GetString(objv[j + defaultColumnSpan]);
3045
3046	    if (*string != REL_HORIZ) {
3047		break;
3048	    }
3049	}
3050
3051	if (TkGetWindowFromObj(interp, tkwin, objv[j], &slave) != TCL_OK) {
3052	    return TCL_ERROR;
3053	}
3054
3055	if (Tk_TopWinHierarchy(slave)) {
3056	    Tcl_AppendResult(interp, "can't manage \"", Tcl_GetString(objv[j]),
3057		    "\": it's a top-level window", NULL);
3058	    return TCL_ERROR;
3059	}
3060	slavePtr = GetGrid(slave);
3061
3062	/*
3063	 * The following statement is taken from tkPack.c:
3064	 *
3065	 * "If the slave isn't currently managed, reset all of its
3066	 * configuration information to default values (there could be old
3067	 * values left from a previous packer)."
3068	 *
3069	 * I [D.S.] disagree with this statement. If a slave is disabled
3070	 * (using "forget") and then re-enabled, I submit that 90% of the time
3071	 * the programmer will want it to retain its old configuration
3072	 * information. If the programmer doesn't want this behavior, then the
3073	 * defaults can be reestablished by hand, without having to worry
3074	 * about keeping track of the old state.
3075	 */
3076
3077	for (i = numWindows; i < objc; i += 2) {
3078	    Tcl_GetIndexFromObj(interp, objv[i], optionStrings, "option", 0,
3079		    &index);
3080	    switch ((enum options) index) {
3081	    case CONF_COLUMN:
3082		if (Tcl_GetIntFromObj(NULL, objv[i+1], &tmp) != TCL_OK
3083			|| tmp < 0) {
3084		    Tcl_AppendResult(interp, "bad column value \"",
3085			    Tcl_GetString(objv[i+1]), "\": must be ",
3086			    "a non-negative integer", NULL);
3087		    return TCL_ERROR;
3088		}
3089		if (SetSlaveColumn(interp, slavePtr, tmp, -1) != TCL_OK) {
3090		    return TCL_ERROR;
3091		}
3092		break;
3093	    case CONF_COLUMNSPAN:
3094		if (Tcl_GetIntFromObj(NULL, objv[i+1], &tmp) != TCL_OK
3095			|| tmp <= 0) {
3096		    Tcl_AppendResult(interp, "bad columnspan value \"",
3097			    Tcl_GetString(objv[i+1]), "\": must be ",
3098			    "a positive integer", NULL);
3099		    return TCL_ERROR;
3100		}
3101		if (SetSlaveColumn(interp, slavePtr, -1, tmp) != TCL_OK) {
3102		    return TCL_ERROR;
3103		}
3104		break;
3105	    case CONF_IN:
3106		if (TkGetWindowFromObj(interp, tkwin, objv[i+1],
3107			&other) != TCL_OK) {
3108		    return TCL_ERROR;
3109		}
3110		if (other == slave) {
3111		    Tcl_SetResult(interp, "Window can't be managed in itself",
3112			    TCL_STATIC);
3113		    return TCL_ERROR;
3114		}
3115		positionGiven = 1;
3116		masterPtr = GetGrid(other);
3117		InitMasterData(masterPtr);
3118		break;
3119	    case CONF_STICKY: {
3120		int sticky = StringToSticky(Tcl_GetString(objv[i+1]));
3121
3122		if (sticky == -1) {
3123		    Tcl_AppendResult(interp, "bad stickyness value \"",
3124			    Tcl_GetString(objv[i+1]), "\": must be ",
3125			    "a string containing n, e, s, and/or w", NULL);
3126		    return TCL_ERROR;
3127		}
3128		slavePtr->sticky = sticky;
3129		break;
3130	    }
3131	    case CONF_IPADX:
3132		if ((Tk_GetPixelsFromObj(NULL, slave, objv[i+1],
3133			&tmp) != TCL_OK) || (tmp < 0)) {
3134		    Tcl_AppendResult(interp, "bad ipadx value \"",
3135			    Tcl_GetString(objv[i+1]), "\": must be ",
3136			    "positive screen distance", NULL);
3137		    return TCL_ERROR;
3138		}
3139		slavePtr->iPadX = tmp*2;
3140		break;
3141	    case CONF_IPADY:
3142		if ((Tk_GetPixelsFromObj(NULL, slave, objv[i+1],
3143			&tmp) != TCL_OK) || (tmp < 0)) {
3144		    Tcl_AppendResult(interp, "bad ipady value \"",
3145			    Tcl_GetString(objv[i+1]), "\": must be ",
3146			    "positive screen distance", NULL);
3147		    return TCL_ERROR;
3148		}
3149		slavePtr->iPadY = tmp*2;
3150		break;
3151	    case CONF_PADX:
3152		if (TkParsePadAmount(interp, tkwin, objv[i+1],
3153			&slavePtr->padLeft, &slavePtr->padX) != TCL_OK) {
3154		    return TCL_ERROR;
3155		}
3156		break;
3157	    case CONF_PADY:
3158		if (TkParsePadAmount(interp, tkwin, objv[i+1],
3159			&slavePtr->padTop, &slavePtr->padY) != TCL_OK) {
3160		    return TCL_ERROR;
3161		}
3162		break;
3163	    case CONF_ROW:
3164		if (Tcl_GetIntFromObj(NULL, objv[i+1], &tmp) != TCL_OK
3165			|| tmp < 0) {
3166		    Tcl_AppendResult(interp, "bad row value \"",
3167			    Tcl_GetString(objv[i+1]),
3168			    "\": must be a non-negative integer", NULL);
3169		    return TCL_ERROR;
3170		}
3171		if (SetSlaveRow(interp, slavePtr, tmp, -1) != TCL_OK) {
3172		    return TCL_ERROR;
3173		}
3174		break;
3175	    case CONF_ROWSPAN:
3176		if ((Tcl_GetIntFromObj(NULL, objv[i+1], &tmp) != TCL_OK)
3177			|| tmp <= 0) {
3178		    Tcl_AppendResult(interp, "bad rowspan value \"",
3179			    Tcl_GetString(objv[i+1]),
3180			    "\": must be a positive integer", NULL);
3181		    return TCL_ERROR;
3182		}
3183		if (SetSlaveRow(interp, slavePtr, -1, tmp) != TCL_OK) {
3184		    return TCL_ERROR;
3185		}
3186		break;
3187	    }
3188	}
3189
3190	/*
3191	 * If no position was specified via -in and the slave is already
3192	 * packed, then leave it in its current location.
3193	 */
3194
3195    	if (!positionGiven && (slavePtr->masterPtr != NULL)) {
3196	    masterPtr = slavePtr->masterPtr;
3197	    goto scheduleLayout;
3198    	}
3199
3200	/*
3201	 * If the same -in window is passed in again, then just leave it in
3202	 * its current location.
3203	 */
3204
3205	if (positionGiven && (masterPtr == slavePtr->masterPtr)) {
3206	    goto scheduleLayout;
3207	}
3208
3209	/*
3210	 * Make sure we have a geometry master. We look at:
3211	 *  1)   the -in flag
3212	 *  2)   the parent of the first slave.
3213	 */
3214
3215	parent = Tk_Parent(slave);
3216    	if (masterPtr == NULL) {
3217	    masterPtr = GetGrid(parent);
3218	    InitMasterData(masterPtr);
3219    	}
3220
3221	if (slavePtr->masterPtr != NULL && slavePtr->masterPtr != masterPtr) {
3222	    Unlink(slavePtr);
3223	    slavePtr->masterPtr = NULL;
3224	}
3225
3226	if (slavePtr->masterPtr == NULL) {
3227	    Gridder *tempPtr = masterPtr->slavePtr;
3228
3229	    slavePtr->masterPtr = masterPtr;
3230	    masterPtr->slavePtr = slavePtr;
3231	    slavePtr->nextPtr = tempPtr;
3232	}
3233
3234	/*
3235	 * Make sure that the slave's parent is either the master or an
3236	 * ancestor of the master, and that the master and slave aren't the
3237	 * same.
3238	 */
3239
3240	for (ancestor = masterPtr->tkwin; ; ancestor = Tk_Parent(ancestor)) {
3241	    if (ancestor == parent) {
3242		break;
3243	    }
3244	    if (Tk_TopWinHierarchy(ancestor)) {
3245		Tcl_AppendResult(interp, "can't put ", Tcl_GetString(objv[j]),
3246			" inside ", Tk_PathName(masterPtr->tkwin), NULL);
3247		Unlink(slavePtr);
3248		return TCL_ERROR;
3249	    }
3250	}
3251
3252	/*
3253	 * Try to make sure our master isn't managed by us.
3254	 */
3255
3256     	if (masterPtr->masterPtr == slavePtr) {
3257	    Tcl_AppendResult(interp, "can't put ", Tcl_GetString(objv[j]),
3258		    " inside ", Tk_PathName(masterPtr->tkwin),
3259		    ", would cause management loop.", NULL);
3260	    Unlink(slavePtr);
3261	    return TCL_ERROR;
3262     	}
3263
3264	Tk_ManageGeometry(slave, &gridMgrType, (ClientData) slavePtr);
3265
3266	/*
3267	 * Assign default position information.
3268	 */
3269
3270	if (slavePtr->column == -1) {
3271	    if (SetSlaveColumn(interp, slavePtr, defaultColumn,-1) != TCL_OK){
3272		return TCL_ERROR;
3273	    }
3274	}
3275	if (SetSlaveColumn(interp, slavePtr, -1,
3276		slavePtr->numCols + defaultColumnSpan - 1) != TCL_OK) {
3277	    return TCL_ERROR;
3278	}
3279	if (slavePtr->row == -1) {
3280	    if (SetSlaveRow(interp, slavePtr, defaultRow, -1) != TCL_OK) {
3281		return TCL_ERROR;
3282	    }
3283	}
3284	defaultColumn += slavePtr->numCols;
3285	defaultColumnSpan = 1;
3286
3287	/*
3288	 * Arrange for the master to be re-arranged at the first idle moment.
3289	 */
3290
3291    scheduleLayout:
3292	if (masterPtr->abortPtr != NULL) {
3293	    *masterPtr->abortPtr = 1;
3294	}
3295	if (!(masterPtr->flags & REQUESTED_RELAYOUT)) {
3296	    masterPtr->flags |= REQUESTED_RELAYOUT;
3297	    Tcl_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
3298	}
3299    }
3300
3301    /*
3302     * Now look for all the "^"'s.
3303     */
3304
3305    lastWindow = NULL;
3306    numSkip = 0;
3307    for (j = 0; j < numWindows; j++) {
3308	struct Gridder *otherPtr;
3309	int match;		 /* Found a match for the ^ */
3310	int lastRow, lastColumn; /* Implied end of table. */
3311
3312	string = Tcl_GetString(objv[j]);
3313    	firstChar = string[0];
3314
3315    	if (firstChar == '.') {
3316	    lastWindow = string;
3317	    numSkip = 0;
3318	}
3319	if (firstChar == REL_SKIP) {
3320	    numSkip++;
3321	}
3322	if (firstChar != REL_VERT) {
3323	    continue;
3324	}
3325
3326	if (masterPtr == NULL) {
3327	    Tcl_AppendResult(interp, "can't use '^', cant find master", NULL);
3328	    return TCL_ERROR;
3329	}
3330
3331	/*
3332	 * Count the number of consecutive ^'s starting from this position.
3333	 */
3334
3335	for (width = 1; width + j < numWindows; width++) {
3336	    char *string = Tcl_GetString(objv[j+width]);
3337
3338	    if (*string != REL_VERT) {
3339		break;
3340	    }
3341	}
3342
3343	/*
3344	 * Find the implied grid location of the ^
3345	 */
3346
3347	if (lastWindow == NULL) {
3348	    lastRow = defaultRow - 1;
3349	    lastColumn = 0;
3350	} else {
3351	    other = Tk_NameToWindow(interp, lastWindow, tkwin);
3352	    otherPtr = GetGrid(other);
3353	    lastRow = otherPtr->row + otherPtr->numRows - 2;
3354	    lastColumn = otherPtr->column + otherPtr->numCols;
3355	}
3356
3357	lastColumn += numSkip;
3358
3359	match = 0;
3360	for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
3361		slavePtr = slavePtr->nextPtr) {
3362
3363	    if (slavePtr->column == lastColumn
3364		    && slavePtr->row + slavePtr->numRows - 1 == lastRow) {
3365		if (slavePtr->numCols <= width) {
3366		    if (SetSlaveRow(interp, slavePtr, -1,
3367			    slavePtr->numRows + 1) != TCL_OK) {
3368			return TCL_ERROR;
3369		    }
3370		    match++;
3371		    j += slavePtr->numCols - 1;
3372		    lastWindow = Tk_PathName(slavePtr->tkwin);
3373		    numSkip = 0;
3374		    break;
3375		}
3376	    }
3377	}
3378	if (!match) {
3379	    Tcl_AppendResult(interp, "can't find slave to extend with \"^\".",
3380		    NULL);
3381	    return TCL_ERROR;
3382	}
3383    }
3384
3385    if (masterPtr == NULL) {
3386	Tcl_AppendResult(interp, "can't determine master window", NULL);
3387	return TCL_ERROR;
3388    }
3389    SetGridSize(masterPtr);
3390    return TCL_OK;
3391}
3392
3393/*
3394 *----------------------------------------------------------------------
3395 *
3396 * StickyToString
3397 *
3398 *	Converts the internal boolean combination of "sticky" bits onto a Tcl
3399 *	list element containing zero or more of n, s, e, or w.
3400 *
3401 * Results:
3402 *	A string is placed into the "result" pointer.
3403 *
3404 * Side effects:
3405 *	none.
3406 *
3407 *----------------------------------------------------------------------
3408 */
3409
3410static void
3411StickyToString(
3412    int flags,			/* The sticky flags. */
3413    char *result)		/* Where to put the result. */
3414{
3415    int count = 0;
3416    if (flags&STICK_NORTH) {
3417    	result[count++] = 'n';
3418    }
3419    if (flags&STICK_EAST) {
3420    	result[count++] = 'e';
3421    }
3422    if (flags&STICK_SOUTH) {
3423    	result[count++] = 's';
3424    }
3425    if (flags&STICK_WEST) {
3426    	result[count++] = 'w';
3427    }
3428    if (count) {
3429	result[count] = '\0';
3430    } else {
3431	sprintf(result, "{}");
3432    }
3433}
3434
3435/*
3436 *----------------------------------------------------------------------
3437 *
3438 * StringToSticky --
3439 *
3440 *	Converts an ascii string representing a widgets stickyness into the
3441 *	boolean result.
3442 *
3443 * Results:
3444 *	The boolean combination of the "sticky" bits is retuned. If an error
3445 *	occurs, such as an invalid character, -1 is returned instead.
3446 *
3447 * Side effects:
3448 *	none
3449 *
3450 *----------------------------------------------------------------------
3451 */
3452
3453static int
3454StringToSticky(
3455    char *string)
3456{
3457    int sticky = 0;
3458    char c;
3459
3460    while ((c = *string++) != '\0') {
3461	switch (c) {
3462	case 'n': case 'N':
3463	    sticky |= STICK_NORTH;
3464	    break;
3465	case 'e': case 'E':
3466	    sticky |= STICK_EAST;
3467	    break;
3468	case 's': case 'S':
3469	    sticky |= STICK_SOUTH;
3470	    break;
3471	case 'w': case 'W':
3472	    sticky |= STICK_WEST;
3473	    break;
3474	case ' ': case ',': case '\t': case '\r': case '\n':
3475	    break;
3476	default:
3477	    return -1;
3478	}
3479    }
3480    return sticky;
3481}
3482
3483/*
3484 *----------------------------------------------------------------------
3485 *
3486 * NewPairObj --
3487 *
3488 *	Creates a new list object and fills it with two integer objects.
3489 *
3490 * Results:
3491 *	The newly created list object is returned.
3492 *
3493 * Side effects:
3494 *	None.
3495 *
3496 *----------------------------------------------------------------------
3497 */
3498
3499static Tcl_Obj *
3500NewPairObj(
3501    int val1, int val2)
3502{
3503    Tcl_Obj *ary[2];
3504
3505    ary[0] = Tcl_NewIntObj(val1);
3506    ary[1] = Tcl_NewIntObj(val2);
3507    return Tcl_NewListObj(2, ary);
3508}
3509
3510/*
3511 *----------------------------------------------------------------------
3512 *
3513 * NewQuadObj --
3514 *
3515 *	Creates a new list object and fills it with four integer objects.
3516 *
3517 * Results:
3518 *	The newly created list object is returned.
3519 *
3520 * Side effects:
3521 *	None.
3522 *
3523 *----------------------------------------------------------------------
3524 */
3525
3526static Tcl_Obj *
3527NewQuadObj(
3528    int val1, int val2, int val3, int val4)
3529{
3530    Tcl_Obj *ary[4];
3531
3532    ary[0] = Tcl_NewIntObj(val1);
3533    ary[1] = Tcl_NewIntObj(val2);
3534    ary[2] = Tcl_NewIntObj(val3);
3535    ary[3] = Tcl_NewIntObj(val4);
3536    return Tcl_NewListObj(4, ary);
3537}
3538
3539/*
3540 * Local Variables:
3541 * mode: c
3542 * c-basic-offset: 4
3543 * fill-column: 78
3544 * End:
3545 */
3546