OSDN Git Service

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