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