]> www.wagner.pp.ru Git - oss/ck.git/blob - ckGrid.c
Ck console graphics toolkit
[oss/ck.git] / ckGrid.c
1 /* 
2  * ckGrid.c --
3  *
4  *      Grid based geometry manager.
5  *
6  * Copyright (c) 1996 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  */
12
13 #include "ckPort.h"
14 #include "ck.h"
15
16 /*
17  * LayoutInfo structure.  We shouldn't be using hard-wired limits!
18  */
19
20 #define MAXGRIDSIZE 128
21 #ifndef MAXINT
22 #  define MAXINT 0x7fff
23 #endif
24 #define MINWEIGHT 0.0001 /* weight totals < this are considered to be zero */
25
26 /*
27  * Special characters to support relative layouts
28  */
29
30 #define REL_SKIP        'x'     /* skip this column */
31 #define REL_HORIZ       '-'     /* extend previous widget horizontally */
32 #define REL_VERT        '^'     /* extend previous widget verticallly */
33
34 /*
35  *  structure to hold collected constraints temporarily:
36  *  needs to use a "Constrain" thingy
37  */
38
39 typedef struct {
40     int width, height;          /* number of cells horizontally, vertically */
41     int lastRow;                /* last cell with a window in it */
42     int minWidth[MAXGRIDSIZE];  /* largest minWidth in each column */
43     int minHeight[MAXGRIDSIZE]; /* largest minHeight in each row */
44     double weightX[MAXGRIDSIZE];/* largest weight in each column */
45     double weightY[MAXGRIDSIZE];/* largest weight in each row */
46 } LayoutInfo;
47
48 /* structure for holding row and column constraints */
49
50 typedef struct {
51     int used;           /* maximum element used */
52     int max;            /* maximum element allocated */
53     int *minsize;       /* array of minimum column/row sizes */
54     double *weight;     /* array of column/row weights */
55 } Constrain;
56
57 /* For each window that the gridbag cares about (either because
58  * the window is managed by the gridbag or because the window
59  * has slaves that are managed by the gridbag), there is a
60  * structure of the following type:
61  */
62
63 typedef struct GridBag {
64     CkWindow *winPtr;           /* Pointer to window.  NULL means that
65                                  * the window has been deleted, but the
66                                  * packet hasn't had a chance to clean up
67                                  * yet because the structure is still in
68                                  * use. */
69     struct GridBag *masterPtr;  /* Master window within which this window
70                                  * is managed (NULL means this window
71                                  * isn't managed by the gridbag). */
72     struct GridBag *nextPtr;    /* Next window managed within same
73                                  * parent.  List is priority-ordered:
74                                  * first on list gets layed out first. */
75     struct GridBag *slavePtr;   /* First in list of slaves managed
76                                  * inside this window (NULL means
77                                  * no gridbag slaves). */
78
79     int gridColumn, gridRow;
80     int gridWidth, gridHeight;
81
82     int tempX, tempY;
83     int tempWidth, tempHeight;
84
85     double weightX, weightY;
86     int minWidth, minHeight;
87
88     int padX, padY;             /* Total additional pixels to leave around the
89                                  * window (half of this space is left on each
90                                  * side).  This is space *outside* the window:
91                                  * we'll allocate extra space in frame but
92                                  * won't enlarge window). */
93     int iPadX, iPadY;           /* Total extra pixels to allocate inside the
94                                  * window (half this amount will appear on
95                                  * each side). */
96     int startx, starty;         /* starting location of layout */
97     int *abortPtr;              /* If non-NULL, it means that there is a nested
98                                  * call to ArrangeGrid already working on
99                                  * this window.  *abortPtr may be set to 1 to
100                                  * abort that nested call.  This happens, for
101                                  * example, if winPtr or any of its slaves
102                                  * is deleted. */
103     int flags;                  /* Miscellaneous flags;  see below
104                                  * for definitions. */
105
106     Constrain row, column;      /* column and row constraints */
107
108     int valid;
109     LayoutInfo *layoutCache;
110 } GridBag;
111
112 /*
113  * Flag values for GridBag structures:
114  *
115  * REQUESTED_RELAYOUT:          1 means a Tk_DoWhenIdle request
116  *                              has already been made to re-arrange
117  *                              all the slaves of this window.
118  * STICK_NORTH                  1 means this window sticks to the edgth of its
119  * STICK_EAST                   cavity
120  * STICK_SOUTH
121  * STICK_WEST
122  *
123  * DONT_PROPAGATE:              1 means don't set this window's requested
124  *                              size.  0 means if this window is a master
125  *                              then Ck will set its requested size to fit
126  *                              the needs of its slaves.
127  */
128
129 #define STICK_NORTH             1
130 #define STICK_EAST              2
131 #define STICK_SOUTH             4
132 #define STICK_WEST              8
133 #define STICK_ALL               (STICK_NORTH|STICK_EAST|STICK_SOUTH|STICK_WEST)
134
135 #define REQUESTED_RELAYOUT      16
136 #define DONT_PROPAGATE          32
137
138 /*
139  * Hash table used to map from CkWindow pointers to corresponding
140  * GridBag structures:
141  */
142
143 static Tcl_HashTable gridBagHashTable;
144
145 /*
146  * Have statics in this module been initialized?
147  */
148
149 static initialized = 0;
150
151 /*
152  * Prototypes for procedures used only in this file:
153  */
154
155 static void             ArrangeGrid _ANSI_ARGS_((ClientData clientData));
156 static int              ConfigureSlaves _ANSI_ARGS_((Tcl_Interp *interp,
157                             CkWindow *winPtr, int argc, char *argv[]));
158 static void             DestroyGridBag _ANSI_ARGS_((char *memPtr));
159 static void             GetCachedLayoutInfo _ANSI_ARGS_((GridBag *masterPtr));
160 static GridBag *        GetGridBag _ANSI_ARGS_((CkWindow *winPtr));
161 static void             GetLayoutInfo _ANSI_ARGS_((GridBag *masterPtr,
162                             LayoutInfo *r));
163 static void             GetMinSize _ANSI_ARGS_((GridBag *masterPtr,
164                             LayoutInfo *info, int *minw, int *minh));
165 static void             GridBagStructureProc _ANSI_ARGS_((
166                             ClientData clientData, CkEvent *eventPtr));
167 static void             GridLostSlaveProc _ANSI_ARGS_((ClientData clientData,
168                             CkWindow *winPtr));
169 static void             GridReqProc _ANSI_ARGS_((ClientData clientData,
170                             CkWindow *winPtr));
171 static void             GridBagStructureProc _ANSI_ARGS_((
172                             ClientData clientData, CkEvent *eventPtr));
173 static void             StickyToString _ANSI_ARGS_((int flags, char *result));
174 static int              StringToSticky _ANSI_ARGS_((char *string));
175 static void             Unlink _ANSI_ARGS_((GridBag *gridPtr));
176
177 static Ck_GeomMgr gridMgrType = {
178     "grid",                     /* name */
179     GridReqProc,                /* requestProc */
180     GridLostSlaveProc,          /* lostSlaveProc */
181 };
182 \f
183 /*
184  *--------------------------------------------------------------
185  *
186  * Ck_GridCmd --
187  *
188  *      This procedure is invoked to process the "grid" Tcl command.
189  *      See the user documentation for details on what it does.
190  *
191  * Results:
192  *      A standard Tcl result.
193  *
194  * Side effects:
195  *      See the user documentation.
196  *
197  *--------------------------------------------------------------
198  */
199
200 int
201 Ck_GridCmd(clientData, interp, argc, argv)
202     ClientData clientData;      /* Main window associated with
203                                  * interpreter. */
204     Tcl_Interp *interp;         /* Current interpreter. */
205     int argc;                   /* Number of arguments. */
206     char **argv;                /* Argument strings. */
207 {
208     CkWindow *winPtr = (CkWindow *) clientData;
209     size_t length;
210     char c;
211   
212     if ((argc >= 2) && (argv[1][0] == '.')) {
213         return ConfigureSlaves(interp, winPtr, argc-1, argv+1);
214     }
215     if (argc < 3) {
216         Tcl_AppendResult(interp, "wrong # args: should be \"",
217                 argv[0], " option arg ?arg ...?\"", (char *) NULL);
218         return TCL_ERROR;
219     }
220     c = argv[1][0];
221     length = strlen(argv[1]);
222   
223     if ((c == 'b') && (strncmp(argv[1], "bbox", length) == 0)) {
224         CkWindow *master;
225         GridBag *masterPtr;
226         int row, column;
227         int i, x, y;
228         int prevX, prevY;
229         int width, height;
230         double weight;
231         int diff;
232
233         if (argc != 5) {
234             Tcl_AppendResult(interp, "Wrong number of arguments: ",
235                     "must be \"",argv[0],
236                     " bbox <master> <column> <row>\"", (char *) NULL);
237             return TCL_ERROR;
238         }
239         
240         master = Ck_NameToWindow(interp, argv[2], winPtr);
241         if (master == NULL) {
242             return TCL_ERROR;
243         }
244         if (Tcl_GetInt(interp, argv[3], &column) != TCL_OK) {
245             return TCL_ERROR;
246         }
247         if (Tcl_GetInt(interp, argv[4], &row) != TCL_OK) {
248             return TCL_ERROR;
249         }
250         masterPtr = GetGridBag(master);
251
252         /* make sure the grid is up to snuff */
253
254         while ((masterPtr->flags & REQUESTED_RELAYOUT)) {
255             Tk_CancelIdleCall(ArrangeGrid, (ClientData) masterPtr);
256             ArrangeGrid((ClientData) masterPtr);
257         }
258         GetCachedLayoutInfo(masterPtr);
259
260         if (row < 0 || column < 0) {
261             *interp->result = '\0';
262             return TCL_OK;
263         }
264         if (column >= masterPtr->layoutCache->width ||
265                 row >= masterPtr->layoutCache->height) {
266             *interp->result = '\0';
267             return TCL_OK;
268         }
269         x = masterPtr->startx;
270         y = masterPtr->starty;
271         GetMinSize(masterPtr, masterPtr->layoutCache, &width, &height);
272
273         diff = masterPtr->winPtr->width - (width + masterPtr->iPadX);
274         for (weight=0.0, i=0; i<masterPtr->layoutCache->width; i++)
275             weight += masterPtr->layoutCache->weightX[i];
276
277         prevX = 0;                      /* Needed to prevent gcc warning. */
278         for (i=0; i<=column; i++) {
279             int dx = 0;
280             if (weight > MINWEIGHT) {
281                 dx = (int)((((double)diff) *
282                     masterPtr->layoutCache->weightX[i]) / weight);
283             }
284             prevX = x;
285             x += masterPtr->layoutCache->minWidth[i] + dx;
286         }
287         diff = masterPtr->winPtr->height - (height + masterPtr->iPadY);
288         for (weight=0.0, i=0; i<masterPtr->layoutCache->width; i++) {
289             weight += masterPtr->layoutCache->weightY[i];
290         }
291         prevY = 0;                      /* Needed to prevent gcc warning. */
292         for (i=0; i<=row; i++) {
293             int dy = 0;
294             if (weight > MINWEIGHT) {
295                 dy = (int)((((double)diff) * 
296                      masterPtr->layoutCache->weightY[i]) / weight);
297             }
298             prevY = y;
299             y += masterPtr->layoutCache->minHeight[i] + dy;
300         }
301         sprintf(interp->result,"%d %d %d %d",prevX,prevY,x - prevX,y - prevY);
302     } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) {
303         if (argv[2][0] != '.') {
304             Tcl_AppendResult(interp, "bad argument \"", argv[2],
305                     "\": must be name of window", (char *) NULL);
306             return TCL_ERROR;
307         }
308         return ConfigureSlaves(interp, winPtr, argc-2, argv+2);
309     } else if ((c == 'f') && (strncmp(argv[1], "forget", length) == 0)) {
310         CkWindow *slave;
311         GridBag *slavePtr;
312         int i;
313     
314         for (i = 2; i < argc; i++) {
315             slave = Ck_NameToWindow(interp, argv[i], winPtr);
316             if (slave == NULL) {
317                 return TCL_ERROR;
318             }
319             slavePtr = GetGridBag(slave);
320             if (slavePtr->masterPtr != NULL) {
321                 Ck_ManageGeometry(slave, (Ck_GeomMgr *) NULL,
322                         (ClientData) NULL);
323                 Unlink(slavePtr);
324                 Ck_UnmapWindow(slavePtr->winPtr);
325             }
326         }
327     } else if ((c == 'i') && (strncmp(argv[1], "info", length) == 0)) {
328         GridBag *slavePtr;
329         CkWindow *slave;
330         char buffer[64];
331     
332         if (argc != 3) {
333             Tcl_AppendResult(interp, "wrong # args: should be \"",
334                     argv[0], " info window\"", (char *) NULL);
335             return TCL_ERROR;
336         }
337         slave = Ck_NameToWindow(interp, argv[2], winPtr);
338         if (slave == NULL) {
339             return TCL_ERROR;
340         }
341         slavePtr = GetGridBag(slave);
342         if (slavePtr->masterPtr == NULL) {
343             interp->result[0] = '\0';
344             return TCL_OK;
345         }
346     
347 #if 0
348         Tcl_AppendElement(interp, "-in");
349         Tcl_AppendElement(interp, slavePtr->masterPtr->winPtr->pathName);
350 #endif
351         sprintf(buffer, " -column %d -row %d -columnspan %d -rowspan %d",
352                 slavePtr->gridColumn, slavePtr->gridRow,
353                 slavePtr->gridWidth, slavePtr->gridHeight);
354         Tcl_AppendResult(interp, buffer, (char *) NULL);
355         sprintf(buffer, " -ipadx %d -ipady %d -padx %d -pady %d",
356                 slavePtr->iPadX/2, slavePtr->iPadY/2, slavePtr->padX/2,
357                 slavePtr->padY/2);
358         Tcl_AppendResult(interp, buffer, (char *) NULL);
359         StickyToString(slavePtr->flags,buffer);
360         Tcl_AppendResult(interp, " -sticky ", buffer, (char *) NULL);
361 #if 0
362         sprintf(buffer, " -weightx %.2f -weighty %.2f",
363                 slavePtr->weightX, slavePtr->weightY);
364         Tcl_AppendResult(interp, buffer, (char *) NULL);
365 #endif
366     } else if ((c == 'p') && (strncmp(argv[1], "propagate", length) == 0)) {
367         CkWindow *master;
368         GridBag *masterPtr;
369         int propagate;
370     
371         if (argc > 4) {
372             Tcl_AppendResult(interp, "wrong # args: should be \"",
373                     argv[0], " propagate window ?boolean?\"",
374                     (char *) NULL);
375             return TCL_ERROR;
376         }
377         master = Ck_NameToWindow(interp, argv[2], winPtr);
378         if (master == NULL) {
379             return TCL_ERROR;
380         }
381         masterPtr = GetGridBag(master);
382         if (argc == 3) {
383             interp->result = (masterPtr->flags & DONT_PROPAGATE) ? "0" : "1";
384             return TCL_OK;
385         }
386         if (Tcl_GetBoolean(interp, argv[3], &propagate) != TCL_OK) {
387             return TCL_ERROR;
388         }
389         if (propagate) {
390             masterPtr->flags &= ~DONT_PROPAGATE;
391       
392             /*
393              * Re-arrange the master to allow new geometry information to
394              * propagate upwards to the master\'s master.
395              */
396       
397             if (masterPtr->abortPtr != NULL) {
398                 *masterPtr->abortPtr = 1;
399             }
400             masterPtr->valid = 0;
401             if (!(masterPtr->flags & REQUESTED_RELAYOUT)) {
402                 masterPtr->flags |= REQUESTED_RELAYOUT;
403                 Tk_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
404             }
405         } else {
406             masterPtr->flags |= DONT_PROPAGATE;
407         }
408     } else if ((c == 's') && (strncmp(argv[1], "size", length) == 0)) {
409         CkWindow *master;
410         GridBag *masterPtr;
411
412         if (argc != 3) {
413             Tcl_AppendResult(interp, "wrong # args: should be \"",
414                     argv[0], " size window\"", (char *) NULL);
415             return TCL_ERROR;
416         }
417         master = Ck_NameToWindow(interp, argv[2], winPtr);
418         if (master == NULL)
419             return TCL_ERROR;
420         masterPtr = GetGridBag(master);
421         GetCachedLayoutInfo(masterPtr);
422
423         sprintf(interp->result, "%d %d", masterPtr->layoutCache->width,
424                 masterPtr->layoutCache->height);
425     } else if ((c == 's') && (strncmp(argv[1], "slaves", length) == 0)) {
426         CkWindow *master;
427         GridBag *masterPtr, *slavePtr;
428         int i, value;
429         int row = -1, column = -1;
430  
431         if (argc < 3 || argc%2 ==0) {
432             Tcl_AppendResult(interp, "wrong # args: should be \"",
433                     argv[0], " slaves window ?-option value...?\"",
434                     (char *) NULL);
435             return TCL_ERROR;
436         }
437
438         for (i=3; i<argc; i+=2) {
439             if (*argv[i] != '-' || (length = strlen(argv[i])) < 2) {
440                 Tcl_AppendResult(interp, "Invalid args: should be \"",
441                         argv[0], " slaves window ?-option value...?\"",
442                         (char *) NULL);
443                 return TCL_ERROR;
444             }
445             if (Tcl_GetInt(interp, argv[i+1], &value) != TCL_OK) {
446                 return TCL_ERROR;
447             }
448             if (value < 0) {
449                 Tcl_AppendResult(interp, argv[i],
450                         " is an invalid value: should NOT be < 0",
451                         (char *) NULL);
452                 return TCL_ERROR;
453             }
454             if (strncmp(argv[i], "-column", length) == 0) {
455                 column = value;
456             } else if (strncmp(argv[i], "-row", length) == 0) {
457                 row = value;
458             } else {
459                 Tcl_AppendResult(interp, argv[i],
460                         " is an invalid option: should be \"",
461                         "-row, -column\"",
462                         (char *) NULL);
463                 return TCL_ERROR;
464             }
465         }
466         master = Ck_NameToWindow(interp, argv[2], winPtr);
467         if (master == NULL) {
468             return TCL_ERROR;
469         }
470         masterPtr = GetGridBag(master);
471
472         for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
473                                              slavePtr = slavePtr->nextPtr) {
474             if (column>=0 && (slavePtr->gridColumn > column
475                     || slavePtr->gridColumn+slavePtr->gridWidth-1 < column)) {
476                 continue;
477             }
478             if (row>=0 && (slavePtr->gridRow > row ||
479                     slavePtr->gridRow+slavePtr->gridHeight-1 < row)) {
480                 continue;
481             }
482             Tcl_AppendElement(interp, slavePtr->winPtr->pathName);
483         }
484
485     /*
486      * grid columnconfigure <master> <index> -option
487      * grid columnconfigure <master> <index> -option value -option value
488      * grid rowconfigure <master> <index> -option
489      * grid rowconfigure <master> <index> -option value -option value
490      */
491    
492     } else if (((c == 'c') &&
493          (strncmp(argv[1], "columnconfigure", length) == 0)) ||
494         ((c == 'r') && (strncmp(argv[1], "rowconfigure", length) == 0))) {
495         CkWindow *master;
496         GridBag *masterPtr;
497         Constrain *con;
498         int index, i, size;
499         double weight;
500
501         if (argc != 5 && (argc < 5 || argc%2 == 1)) {
502             Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
503                     " ", argv[1], " master index ?-option value...?\"",
504                     (char *)NULL);
505             return TCL_ERROR;
506         }
507
508         master = Ck_NameToWindow(interp, argv[2], winPtr);
509         if (master == NULL) {
510             return TCL_ERROR;
511         }
512         masterPtr = GetGridBag(master);
513         con = (c=='c') ? &(masterPtr->column) : &(masterPtr->row);
514
515         if (Tcl_GetInt(interp, argv[3], &index) != TCL_OK) {
516             return TCL_ERROR;
517         }
518         if (index < 0 || index >= MAXGRIDSIZE) {
519             Tcl_AppendResult(interp, argv[3], " is out of range",
520                     (char *)NULL);
521             return TCL_ERROR;
522         }
523
524         /*
525          *  make sure the row/column constraint array is allocated.  This
526          *  Should be changed to avoid hard-wired limits.  We'll wimp out
527          *  for now.
528          */
529
530         if (con->max == 0) {
531             unsigned int size;
532             con->max = MAXGRIDSIZE;
533             con->used = 0;
534
535             size = MAXGRIDSIZE * sizeof(con->minsize[0]);
536             con->minsize = (int *) ckalloc(size);
537             memset(con->minsize, 0, size);
538
539             size = MAXGRIDSIZE * sizeof(con->weight[0]);
540             con->weight = (double *) ckalloc(size);
541             memset(con->weight, 0, size);
542         }
543
544         for (i=4; i<argc; i+=2) {
545             if (*argv[i] != '-' || (length = strlen(argv[i])) < 2) {
546                 Tcl_AppendResult(interp, "Invalid arg: \"",
547                         argv[0], "\" expecting -minsize or -weight",
548                         (char *) NULL);
549                 return TCL_ERROR;
550             }
551             if (strncmp(argv[i], "-minsize", length) == 0) {
552                 if (argc == 5) {
553                     size = con->used <= index ?  0 : con->minsize[index];
554                     sprintf(interp->result, "%d", size);
555                 } else if (Ck_GetCoord(interp, master, argv[i + 1], &size)
556                         != TCL_OK) {
557                     return TCL_ERROR;
558                 } else {
559                     con->minsize[index] = size;
560                     if (size > 0 && index >= con->used) {
561                         con->used = index+1;
562                     } else if (size == 0 && index+1 == con->used) {
563                         while (index >= 0  && (con->minsize[index]==0) &&
564                                 (con->weight[index] == 0.0)) {
565                             index--;
566                         }
567                         con->used = index + 1;
568                     }
569                 }
570             } else if (strncmp(argv[i], "-weight", length) == 0) {
571                 if (argc == 5) {
572                     weight = con->used <= index ?  0 : con->weight[index];
573                     sprintf(interp->result, "%.2f", weight);
574                 } else if (Tcl_GetDouble(interp, argv[i+1], &weight)
575                     != TCL_OK) {
576                     return TCL_ERROR;
577                 } else {
578                     con->weight[index] = weight;
579                     if (weight > MINWEIGHT && index >= con->used) {
580                         con->used = index+1;
581                     } else if (weight == 0.0 && index+1 == con->used) {
582                         while (index >= 0 && (con->minsize[index]==0) &&
583                                 (con->weight[index] == 0.0)) {
584                             index--;
585                         }
586                         con->used = index + 1;
587                     }
588                 }
589             } else {
590                 Tcl_AppendResult(interp, argv[i],
591                         " is an invalid option: should be \"",
592                         "-minsize, -weight\"",
593                         (char *) NULL);
594                 return TCL_ERROR;
595             }
596         }
597
598         /* if we changed a property, re-arrange the table */
599
600         if (argc != 5) {
601             if (masterPtr->abortPtr != NULL) {
602                 *masterPtr->abortPtr = 1;
603             }
604             masterPtr->valid = 0;
605             if (!(masterPtr->flags & REQUESTED_RELAYOUT)) {
606                 masterPtr->flags |= REQUESTED_RELAYOUT;
607                 Tk_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
608             }
609         }
610     } else if ((c == 'l') && (strncmp(argv[1], "location", length) == 0)) {
611         CkWindow *master;
612         GridBag *masterPtr;
613         int x, y, i, j, w, h;
614         int width, height;
615         double weight;
616         int diff;
617
618         if (argc != 5) {
619             Tcl_AppendResult(interp, "wrong # args: should be \"",
620                     argv[0], " location master x y\"", (char *)NULL);
621             return TCL_ERROR;
622         }
623
624         master = Ck_NameToWindow(interp, argv[2], winPtr);
625         if (master == NULL) {
626             return TCL_ERROR;
627         }
628         masterPtr = GetGridBag(master);
629
630         if (Ck_GetCoord(interp, master, argv[3], &x) != TCL_OK) {
631             return TCL_ERROR;
632         }
633         if (Ck_GetCoord(interp, master, argv[4], &y) != TCL_OK) {
634             return TCL_ERROR;
635         }
636
637         /* make sure the grid is up to snuff */
638
639         while ((masterPtr->flags & REQUESTED_RELAYOUT)) {
640             Tk_CancelIdleCall(ArrangeGrid, (ClientData) masterPtr);
641             ArrangeGrid((ClientData) masterPtr);
642         }
643         GetCachedLayoutInfo(masterPtr);
644         GetMinSize(masterPtr, masterPtr->layoutCache, &width, &height);
645
646         diff = masterPtr->winPtr->width - (width + masterPtr->iPadX);
647         for (weight=0.0, i=0; i<masterPtr->layoutCache->width; i++) {
648             weight += masterPtr->layoutCache->weightX[i];
649         }
650         w = masterPtr->startx;
651         if (w > x) {
652             i = -1;
653         } else {
654             for (i = 0; i < masterPtr->layoutCache->width; i++) {
655                 int dx = 0;
656                 if (weight > MINWEIGHT) {
657                     dx = (int)((((double)diff) *
658                         masterPtr->layoutCache->weightX[i]) / weight);
659                     }
660                 w += masterPtr->layoutCache->minWidth[i] + dx;
661                 if (w > x) {
662                     break;
663                 }
664             }
665         }
666
667         diff = masterPtr->winPtr->height - (height + masterPtr->iPadY);
668         for (weight = 0.0, j = 0; j < masterPtr->layoutCache->height; j++)
669             weight += masterPtr->layoutCache->weightY[j];
670         h = masterPtr->starty;
671         if (h > y) {
672             j = -1;
673         } else {
674             for (j = 0; j < masterPtr->layoutCache->height; j++) {
675                 int dy = 0;
676                 if (weight > MINWEIGHT) {
677                     dy = (int)((((double)diff) *
678                         masterPtr->layoutCache->weightY[j]) / weight);
679                 }
680                 h += masterPtr->layoutCache->minHeight[j] + dy;
681                 if (h > y) {
682                     break;
683                 }
684             }
685         }
686         sprintf(interp->result, "%d %d", i, j);
687     } else {
688         Tcl_AppendResult(interp, "bad option \"", argv[1],
689                 "\":  must be bbox, columnconfigure, configure, forget, ",
690                 "info, location, propagate, rowconfigure, size, or slaves",
691                 (char *) NULL);
692         return TCL_ERROR;
693     }
694     return TCL_OK;
695 }
696 \f
697 /*
698  *--------------------------------------------------------------
699  *
700  * GridReqProc --
701  *
702  *      This procedure is invoked by Ck_GeometryRequest for
703  *      windows managed by the gridbag.
704  *
705  * Results:
706  *      None.
707  *
708  * Side effects:
709  *      Arranges for winPtr, and all its managed siblings, to
710  *      be re-arranged at the next idle point.
711  *
712  *--------------------------------------------------------------
713  */
714
715 static void
716 GridReqProc(clientData, winPtr)
717     ClientData clientData;      /* GridBag's information about
718                                  * window that got new preferred
719                                  * geometry.  */
720     CkWindow *winPtr;           /* Other Ck-related information
721                                  * about the window. */
722 {
723     GridBag *gridPtr = (GridBag *) clientData;
724
725     gridPtr = gridPtr->masterPtr;
726     gridPtr->valid = 0;
727     if (!(gridPtr->flags & REQUESTED_RELAYOUT)) {
728         gridPtr->flags |= REQUESTED_RELAYOUT;
729         Tk_DoWhenIdle(ArrangeGrid, (ClientData) gridPtr);
730     }
731 }
732 \f
733 /*
734  *--------------------------------------------------------------
735  *
736  * GridLostSlaveProc --
737  *
738  *      This procedure is invoked by Ck whenever some other geometry
739  *      claims control over a slave that used to be managed by us.
740  *
741  * Results:
742  *      None.
743  *
744  * Side effects:
745  *      Forgets all grid-related information about the slave.
746  *
747  *--------------------------------------------------------------
748  */
749
750 static void
751 GridLostSlaveProc(clientData, winPtr)
752     ClientData clientData;      /* GridBag structure for slave window that
753                                  * was stolen away. */
754     CkWindow *winPtr;           /* Pointer to the slave window. */
755 {
756     GridBag *slavePtr = (GridBag *) clientData;
757
758     if (slavePtr->masterPtr->winPtr != slavePtr->winPtr->parentPtr) {
759         Ck_UnmaintainGeometry(slavePtr->winPtr, slavePtr->masterPtr->winPtr);
760     }
761     Unlink(slavePtr);
762     Ck_UnmapWindow(slavePtr->winPtr);
763 }
764 \f
765 /*
766  *--------------------------------------------------------------
767  *
768  * Fill in an instance of the above structure for the current set
769  * of managed children.  This requires two passes through the
770  * set of children, first to figure out what cells they occupy
771  * and how many rows and columns there are, and then to distribute
772  * the weights and min sizes amoung the rows/columns.
773  *
774  * This also caches the minsizes for all the children when they are
775  * first encountered.
776  *
777  *--------------------------------------------------------------
778  */
779
780 static void
781 GetLayoutInfo(masterPtr, r)
782     GridBag *masterPtr;
783     LayoutInfo *r;
784 {
785     GridBag *slavePtr;
786     int i, k, px, py, pixels_diff, nextSize;
787     double weight_diff, weight;
788     int curX, curY, curWidth, curHeight, curRow, curCol;
789     int xMax[MAXGRIDSIZE];
790     int yMax[MAXGRIDSIZE];
791
792     /*
793      * Pass #1
794      *
795      * Figure out the dimensions of the layout grid.
796      */
797
798     r->width = r->height = 0;
799     curRow = curCol = -1;
800     memset(xMax, 0, sizeof(int) * MAXGRIDSIZE);
801     memset(yMax, 0, sizeof(int) * MAXGRIDSIZE);
802
803     for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
804                                          slavePtr = slavePtr->nextPtr) {
805
806         curX = slavePtr->gridColumn;
807         curY = slavePtr->gridRow;
808         curWidth = slavePtr->gridWidth;
809         curHeight = slavePtr->gridHeight;
810
811         /* Adjust the grid width and height */
812         for (px = curX + curWidth; r->width < px; r->width++) {
813             /* Null body. */
814         }
815         for (py = curY + curHeight; r->height < py; r->height++) {
816             /* Null body. */
817         }
818
819         /* Adjust the xMax and yMax arrays */
820         for (i = curX; i < (curX + curWidth); i++) {
821             yMax[i] = py;
822         }
823         for (i = curY; i < (curY + curHeight); i++) {
824             xMax[i] = px;
825         }
826
827         /* Cache the current slave's size. */
828         slavePtr->minWidth = slavePtr->winPtr->reqWidth;
829         slavePtr->minHeight = slavePtr->winPtr->reqHeight;
830     }
831
832     /*
833      * Apply minimum row/column dimensions
834      */ 
835     if (r->width < masterPtr->column.used) {
836         r->width = masterPtr->column.used;
837     }
838     r->lastRow = r->height;
839     if (r->height < masterPtr->row.used) {
840         r->height = masterPtr->row.used;
841     }
842
843     /*
844      * Pass #2
845      */
846
847     curRow = curCol = -1;
848     memset(xMax, 0, sizeof(int) * MAXGRIDSIZE);
849     memset(yMax, 0, sizeof(int) * MAXGRIDSIZE);
850
851     for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
852                                          slavePtr = slavePtr->nextPtr) {
853         curX = slavePtr->gridColumn;
854         curY = slavePtr->gridRow;
855         curWidth = slavePtr->gridWidth;
856         curHeight = slavePtr->gridHeight;
857
858         px = curX + curWidth;
859         py = curY + curHeight;
860
861         for (i = curX; i < (curX + curWidth); i++) {
862             yMax[i] = py;
863         }
864         for (i = curY; i < (curY + curHeight); i++) {
865             xMax[i] = px;
866         }
867
868         /* Assign the new values to the gridbag slave */
869         slavePtr->tempX = curX;
870         slavePtr->tempY = curY;
871         slavePtr->tempWidth = curWidth;
872         slavePtr->tempHeight = curHeight;
873     }
874
875     /*
876      * Pass #3
877      *
878      * Distribute the minimun widths and weights:
879      */
880
881     /* Initialize arrays to zero */
882     memset(r->minWidth, 0, r->width * sizeof(int));
883     memset(r->minHeight, 0, r->height * sizeof(int));
884     memset(r->weightX, 0, r->width * sizeof(double));
885     memset(r->weightY, 0, r->height * sizeof(double));
886     nextSize = MAXINT;
887
888     for (i = 1; i != MAXINT; i = nextSize, nextSize = MAXINT) {
889         for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
890                                              slavePtr = slavePtr->nextPtr) {
891
892             if (slavePtr->tempWidth == i) {
893                 px = slavePtr->tempX + slavePtr->tempWidth; /* right column */
894
895                 /* 
896                  * Figure out if we should use this slave's weight.
897                  * If the weight is less than the total weight spanned by
898                  * the width of the cell, then discard the weight.
899                  * Otherwise split it the difference
900                  * according to the existing weights.
901                  */
902
903                 weight_diff = slavePtr->weightX;
904                 for (k = slavePtr->tempX; k < px; k++)
905                     weight_diff -= r->weightX[k];
906                 if (weight_diff > 0.0) {
907                     weight = 0.0;
908                     for (k = slavePtr->tempX; k < px; k++)
909                         weight += r->weightX[k];
910                     for (k = slavePtr->tempX; weight > MINWEIGHT; k++) {
911                         double wt = r->weightX[k];
912                         double dx = (wt * weight_diff) / weight;
913                         r->weightX[k] += dx;
914                         weight_diff -= dx;
915                         weight -= wt;
916                     }
917                     /* Assign the remainder to the rightmost cell */
918                     r->weightX[px-1] += weight_diff;
919                 }
920
921                 /*
922                  * Calculate the minWidth array values.
923                  * First, figure out how wide the current slave needs to be.
924                  * Then, see if it will fit within the current minWidth values.
925                  * If it won't fit, add the difference according to the
926                  * weightX array.
927                  */
928
929                 pixels_diff = slavePtr->minWidth + slavePtr->padX +
930                     slavePtr->iPadX;
931                 for (k = slavePtr->tempX; k < px; k++)
932                     pixels_diff -= r->minWidth[k];
933                 if (pixels_diff > 0) {
934                     weight = 0.0;
935                     for (k = slavePtr->tempX; k < px; k++)
936                         weight += r->weightX[k];
937                     for (k = slavePtr->tempX; weight > MINWEIGHT; k++) {
938                         double wt = r->weightX[k];
939                         int dx = (int)((wt * ((double)pixels_diff)) / weight);
940                         r->minWidth[k] += dx;
941                         pixels_diff -= dx;
942                         weight -= wt;
943                     }
944                     /* Any leftovers go into the rightmost cell */
945                     r->minWidth[px-1] += pixels_diff;
946                 }
947             } else if (slavePtr->tempWidth > i &&
948                 slavePtr->tempWidth < nextSize)
949                 nextSize = slavePtr->tempWidth;
950
951             if (slavePtr->tempHeight == i) {
952                 py = slavePtr->tempY + slavePtr->tempHeight; /* bottom row */
953
954                 /* 
955                  * Figure out if we should use this slave's weight.
956                  * If the weight is less than the total weight spanned by
957                  * the height of the cell, then discard the weight.
958                  * Otherwise split it the difference according to the
959                  * existing weights.
960                  */
961
962                 weight_diff = slavePtr->weightY;
963                 for (k = slavePtr->tempY; k < py; k++)
964                     weight_diff -= r->weightY[k];
965                 if (weight_diff > 0.0) {
966                     weight = 0.0;
967                     for (k = slavePtr->tempY; k < py; k++)
968                         weight += r->weightY[k];
969                     for (k = slavePtr->tempY; weight > MINWEIGHT; k++) {
970                         double wt = r->weightY[k];
971                         double dy = (wt * weight_diff) / weight;
972                         r->weightY[k] += dy;
973                         weight_diff -= dy;
974                         weight -= wt;
975                     }
976                     /* Assign the remainder to the bottom cell */
977                     r->weightY[py-1] += weight_diff;
978                 }
979
980                 /*
981                  * Calculate the minHeight array values.
982                  * First, figure out how tall the current slave needs to be.
983                  * Then, see if it will fit within the current minHeight
984                  * values. If it won't fit, add the difference according to
985                  * the weightY array.
986                  */
987
988                 pixels_diff = slavePtr->minHeight + slavePtr->padY +
989                     slavePtr->iPadY;
990                 for (k = slavePtr->tempY; k < py; k++)
991                     pixels_diff -= r->minHeight[k];
992                 if (pixels_diff > 0) {
993                     weight = 0.0;
994                     for (k = slavePtr->tempY; k < py; k++)
995                         weight += r->weightY[k];
996                     for (k = slavePtr->tempY; weight > MINWEIGHT; k++) {
997                         double wt = r->weightY[k];
998                         int dy = (int)((wt * ((double)pixels_diff)) / weight);
999                         r->minHeight[k] += dy;
1000                         pixels_diff -= dy;
1001                         weight -= wt;
1002                     }
1003                     /* Any leftovers go into the bottom cell */
1004                     r->minHeight[py-1] += pixels_diff;
1005                 }
1006             } else if (slavePtr->tempHeight > i &&
1007                 slavePtr->tempHeight < nextSize)
1008                 nextSize = slavePtr->tempHeight;
1009         }
1010     }
1011
1012     /*
1013      * Apply minimum row/column dimensions
1014      */
1015     for (i=0; i<masterPtr->column.used; i++) {
1016         if (r->minWidth[i] < masterPtr->column.minsize[i])
1017             r->minWidth[i] = masterPtr->column.minsize[i];
1018         if (r->weightX[i] < masterPtr->column.weight[i])
1019             r->weightX[i] = masterPtr->column.weight[i];
1020     }
1021     for (i=0; i<masterPtr->row.used; i++) {
1022         if (r->minHeight[i] < masterPtr->row.minsize[i])
1023             r->minHeight[i] = masterPtr->row.minsize[i];
1024         if (r->weightY[i] < masterPtr->row.weight[i])
1025             r->weightY[i] = masterPtr->row.weight[i];
1026     }
1027 }
1028 \f
1029 /*
1030  *--------------------------------------------------------------
1031  *
1032  * Cache the layout info after it is calculated.
1033  *
1034  *--------------------------------------------------------------
1035  */
1036
1037 static void
1038 GetCachedLayoutInfo(masterPtr)
1039     GridBag *masterPtr;
1040 {
1041     if (masterPtr->valid == 0) {
1042         if (!masterPtr->layoutCache)
1043             masterPtr->layoutCache = (LayoutInfo *)ckalloc(sizeof(LayoutInfo));
1044
1045         GetLayoutInfo(masterPtr, masterPtr->layoutCache);
1046         masterPtr->valid = 1;
1047     }
1048 }
1049 \f
1050 /*
1051  *--------------------------------------------------------------
1052  *
1053  * Adjusts the x, y, width, and height fields to the correct
1054  * values depending on the constraint geometry and pads.
1055  *
1056  *--------------------------------------------------------------
1057  */
1058
1059 static void
1060 AdjustForGravity(gridPtr, x, y, width, height)
1061     GridBag *gridPtr;
1062     int *x;
1063     int *y;
1064     int *width;
1065     int *height;
1066 {
1067     int diffx=0, diffy=0;
1068     int sticky = gridPtr->flags&STICK_ALL;
1069
1070     *x += gridPtr->padX/2;
1071     *width -= gridPtr->padX;
1072     *y += gridPtr->padY/2;
1073     *height -= gridPtr->padY;
1074
1075     if (*width > (gridPtr->minWidth + gridPtr->iPadX)) {
1076         diffx = *width - (gridPtr->minWidth + gridPtr->iPadX);
1077         *width = gridPtr->minWidth + gridPtr->iPadX;
1078     }
1079
1080     if (*height > (gridPtr->minHeight + gridPtr->iPadY)) {
1081         diffy = *height - (gridPtr->minHeight + gridPtr->iPadY);
1082         *height = gridPtr->minHeight + gridPtr->iPadY;
1083     }
1084
1085     if (sticky&STICK_EAST && sticky&STICK_WEST)
1086         *width += diffx;
1087     if (sticky&STICK_NORTH && sticky&STICK_SOUTH)
1088         *height += diffy;
1089     if (!(sticky&STICK_WEST)) {
1090         if (sticky&STICK_EAST)
1091             *x += diffx;
1092         else
1093             *x += diffx/2;
1094     }
1095     if (!(sticky&STICK_NORTH)) {
1096         if (sticky&STICK_SOUTH)
1097             *y += diffy;
1098         else
1099             *y += diffy/2;
1100     }
1101 }
1102 \f
1103 /*
1104  *--------------------------------------------------------------
1105  *
1106  * Figure out the minimum size (not counting the X border) of the
1107  * master based on the information from GetLayoutInfo()
1108  *
1109  *--------------------------------------------------------------
1110  */
1111
1112 static void
1113 GetMinSize(masterPtr, info, minw, minh)
1114     GridBag *masterPtr;
1115     LayoutInfo *info;
1116     int *minw;
1117     int *minh;
1118 {
1119     int i, t;
1120     int intBWidth;      /* Width of internal border in parent window,
1121                          * if any. */
1122
1123     intBWidth = (masterPtr->winPtr->flags & CK_BORDER) ? 1 : 0; 
1124
1125     t = 0;
1126     for(i = 0; i < info->width; i++)
1127         t += info->minWidth[i];
1128     *minw = t + 2*intBWidth;
1129
1130     t = 0;
1131     for(i = 0; i < info->height; i++)
1132         t += info->minHeight[i];
1133     *minh = t + 2*intBWidth;
1134 }
1135 \f
1136 /*
1137  *--------------------------------------------------------------
1138  *
1139  * ArrangeGrid --
1140  *
1141  *      This procedure is invoked (using the Tk_DoWhenIdle
1142  *      mechanism) to re-layout a set of windows managed by
1143  *      the gridbag.  It is invoked at idle time so that a
1144  *      series of gridbag requests can be merged into a single
1145  *      layout operation.
1146  *
1147  * Results:
1148  *      None.
1149  *
1150  * Side effects:
1151  *      The slaves of masterPtr may get resized or moved.
1152  *
1153  *--------------------------------------------------------------
1154  */
1155
1156 static void
1157 ArrangeGrid(clientData)
1158     ClientData clientData;      /* Structure describing parent whose slaves
1159                                  * are to be re-layed out. */
1160 {
1161     GridBag *masterPtr = (GridBag *) clientData;
1162     GridBag *slavePtr;  
1163     int abort;
1164     int i, x, y, width, height;
1165     int diffw, diffh;
1166     double weight;
1167     CkWindow *parent, *ancestor;
1168     LayoutInfo info;
1169     int intBWidth;      /* Width of internal border in parent window,
1170                          * if any. */
1171     int iPadX, iPadY;
1172
1173     masterPtr->flags &= ~REQUESTED_RELAYOUT;
1174
1175     /*
1176      * If the parent has no slaves anymore, then don't do anything
1177      * at all:  just leave the parent's size as-is.
1178      * Even if row and column constraints have been set!
1179      */
1180
1181     if (masterPtr->slavePtr == NULL) {
1182         return;
1183     }
1184
1185     /*
1186      * Abort any nested call to ArrangeGrid for this window, since
1187      * we'll do everything necessary here, and set up so this call
1188      * can be aborted if necessary.  
1189      */
1190
1191     if (masterPtr->abortPtr != NULL) {
1192         *masterPtr->abortPtr = 1;
1193     }
1194     masterPtr->abortPtr = &abort;
1195     abort = 0;
1196     Ck_Preserve((ClientData) masterPtr);
1197
1198     /*
1199      * Pass #1: scan all the slaves to figure out the total amount
1200      * of space needed.
1201      */
1202
1203     GetLayoutInfo(masterPtr, &info);
1204     GetMinSize(masterPtr, &info, &width, &height);
1205
1206     if (((width != masterPtr->winPtr->reqWidth)
1207             || (height != masterPtr->winPtr->reqHeight))
1208             && !(masterPtr->flags & DONT_PROPAGATE)) {
1209         Ck_GeometryRequest(masterPtr->winPtr, width, height);
1210         masterPtr->flags |= REQUESTED_RELAYOUT;
1211         masterPtr->valid = 0;
1212         Tk_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
1213         goto done;
1214     }
1215
1216     /*
1217      * If the parent isn't mapped then don't do anything more:  wait
1218      * until it gets mapped again.  Need to get at least to here to
1219      * reflect size needs up the window hierarchy, but there's no
1220      * point in actually mapping the slaves.
1221      */
1222
1223     if (!(masterPtr->winPtr->flags & CK_MAPPED)) {
1224         goto done;
1225     }
1226
1227     /*
1228      * If the current dimensions of the window don't match the desired
1229      * dimensions, then adjust the minWidth and minHeight arrays
1230      * according to the weights.
1231      */
1232
1233     diffw = masterPtr->winPtr->width - (width + masterPtr->iPadX);
1234     if (diffw != 0) {
1235         weight = 0.0;
1236         for (i = 0; i < info.width; i++)
1237             weight += info.weightX[i];
1238         if (weight > MINWEIGHT) {
1239             for (i = 0; i < info.width; i++) {
1240                 int dx = (int)(( ((double)diffw) * info.weightX[i]) / weight);
1241                 info.minWidth[i] += dx;
1242                 width += dx;
1243                 if (info.minWidth[i] < 0) {
1244                     width -= info.minWidth[i];
1245                     info.minWidth[i] = 0;
1246                 }
1247             }
1248         }
1249         diffw = masterPtr->winPtr->width - (width + masterPtr->iPadX);
1250     }
1251     else {
1252         diffw = 0;
1253     }
1254
1255     diffh = masterPtr->winPtr->height - (height + masterPtr->iPadY);
1256     if (diffh != 0) {
1257         weight = 0.0;
1258         for (i = 0; i < info.height; i++)
1259             weight += info.weightY[i];
1260         if (weight > MINWEIGHT) {
1261             for (i = 0; i < info.height; i++) {
1262                 int dy = (int)(( ((double)diffh) * info.weightY[i]) / weight);
1263                 info.minHeight[i] += dy;
1264                 height += dy;
1265                 if (info.minHeight[i] < 0) {
1266                     height -= info.minHeight[i];
1267                     info.minHeight[i] = 0;
1268                 }
1269             }
1270         }
1271         diffh = masterPtr->winPtr->height - (height + masterPtr->iPadY);
1272     }
1273     else {
1274         diffh = 0;
1275     }
1276
1277     /*
1278      * Now do the actual layout of the slaves using the layout information
1279      * that has been collected.
1280      */
1281
1282     iPadX = masterPtr->iPadX/2;
1283     iPadY = masterPtr->iPadY/2;
1284     intBWidth = (masterPtr->winPtr->flags & CK_BORDER) ? 1 : 0;
1285
1286     for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
1287                                          slavePtr = slavePtr->nextPtr) {
1288
1289         masterPtr->startx = x = diffw/2 + intBWidth + iPadX;
1290         for(i = 0; i < slavePtr->tempX; i++)
1291             x += info.minWidth[i];
1292
1293         masterPtr->starty = y = diffh/2 + intBWidth + iPadY;
1294         for(i = 0; i < slavePtr->tempY; i++)
1295             y += info.minHeight[i];
1296
1297         width = 0;
1298         for(i = slavePtr->tempX; i < (slavePtr->tempX + slavePtr->tempWidth);
1299             i++)
1300             width += info.minWidth[i];
1301
1302         height = 0;
1303         for(i = slavePtr->tempY; i < (slavePtr->tempY + slavePtr->tempHeight);
1304             i++)
1305             height += info.minHeight[i];
1306
1307         AdjustForGravity(slavePtr, &x, &y, &width, &height);
1308
1309         /*
1310          * If the window in which slavePtr is managed is not its
1311          * parent in the window hierarchy, translate the coordinates
1312          * to the coordinate system of the real X parent.
1313          */
1314
1315         parent = slavePtr->winPtr->parentPtr;
1316         for (ancestor = masterPtr->winPtr; ancestor != parent;
1317              ancestor = ancestor->parentPtr) {
1318             x += ancestor->x;
1319             y += ancestor->y;
1320         }
1321
1322         /*
1323          * If the window is too small to be interesting then
1324          * unmap it.  Otherwise configure it and then make sure
1325          * it's mapped.
1326          */
1327
1328         if ((width <= 0) || (height <= 0)) {
1329             Ck_UnmapWindow(slavePtr->winPtr);
1330         }
1331         else {
1332             if (width != slavePtr->winPtr->width ||
1333                 height != slavePtr->winPtr->height)
1334                 Ck_ResizeWindow(slavePtr->winPtr, width, height);
1335             if (x != slavePtr->winPtr->x ||
1336                 y != slavePtr->winPtr->y)
1337                 Ck_MoveWindow(slavePtr->winPtr, x, y);
1338             /*
1339              * Temporary kludge til Ck_MoveResizeWindow available !!!
1340              */
1341             if (width != slavePtr->winPtr->width ||
1342                 height != slavePtr->winPtr->height)
1343                 Ck_ResizeWindow(slavePtr->winPtr, width, height);
1344             if (abort) {
1345                 goto done;
1346             }
1347             Ck_MapWindow(slavePtr->winPtr);
1348         }
1349
1350         /*
1351          * Changes to the window's structure could cause almost anything
1352          * to happen, including deleting the parent or child.  If this
1353          * happens, we'll be told to abort.
1354          */
1355
1356         if (abort) {
1357             goto done;
1358         }
1359     }
1360
1361     done:
1362     masterPtr->abortPtr = NULL;
1363     Ck_Release((ClientData) masterPtr);
1364 }
1365 \f
1366 /*
1367  *--------------------------------------------------------------
1368  *
1369  * GetGridBag --
1370  *
1371  *      This internal procedure is used to locate a GridBag
1372  *      structure for a given window, creating one if one
1373  *      doesn't exist already.
1374  *
1375  * Results:
1376  *      The return value is a pointer to the GridBag structure
1377  *      corresponding to winPtr.
1378  *
1379  * Side effects:
1380  *      A new gridbag structure may be created.  If so, then
1381  *      a callback is set up to clean things up when the
1382  *      window is deleted.
1383  *
1384  *--------------------------------------------------------------
1385  */
1386
1387 static GridBag *
1388 GetGridBag(winPtr)
1389     CkWindow *winPtr;           /* Pointer to window for which
1390                                  * gridbag structure is desired. */
1391 {
1392     GridBag *gridPtr;
1393     Tcl_HashEntry *hPtr;
1394     int new;
1395
1396     if (!initialized) {
1397         initialized = 1;
1398         Tcl_InitHashTable(&gridBagHashTable, TCL_ONE_WORD_KEYS);
1399     }
1400
1401     /*
1402      * See if there's already gridbag for this window.  If not,
1403      * then create a new one.
1404      */
1405
1406     hPtr = Tcl_CreateHashEntry(&gridBagHashTable, (char *) winPtr, &new);
1407     if (!new) {
1408         return (GridBag *) Tcl_GetHashValue(hPtr);
1409     }
1410     gridPtr = (GridBag *) ckalloc(sizeof(GridBag));
1411     gridPtr->winPtr = winPtr;
1412     gridPtr->masterPtr = NULL;
1413     gridPtr->nextPtr = NULL;
1414     gridPtr->slavePtr = NULL;
1415
1416     gridPtr->gridColumn = gridPtr->gridRow = -1;
1417     gridPtr->gridWidth = gridPtr->gridHeight = 1;
1418     gridPtr->weightX = gridPtr->weightY = 0.0;
1419     gridPtr->minWidth = gridPtr->minHeight = 0;
1420
1421     gridPtr->padX = gridPtr->padY = 0;
1422     gridPtr->iPadX = gridPtr->iPadY = 0;
1423     gridPtr->startx = gridPtr->starty = 0;
1424     gridPtr->abortPtr = NULL;
1425     gridPtr->flags = 0;
1426
1427     gridPtr->column.max = 0;
1428     gridPtr->row.max = 0;
1429     gridPtr->column.used = 0;
1430     gridPtr->row.used = 0;
1431
1432     gridPtr->valid = 0;
1433     gridPtr->layoutCache = NULL;
1434
1435     Tcl_SetHashValue(hPtr, gridPtr);
1436     Ck_CreateEventHandler(winPtr,
1437         CK_EV_MAP | CK_EV_UNMAP | CK_EV_EXPOSE | CK_EV_DESTROY,
1438         GridBagStructureProc, (ClientData) gridPtr);
1439     return gridPtr;
1440 }
1441 \f
1442 /*
1443  *----------------------------------------------------------------------
1444  *
1445  * Unlink --
1446  *
1447  *      Remove a gridbag from its parent's list of slaves.
1448  *
1449  * Results:
1450  *      None.
1451  *
1452  * Side effects:
1453  *      The parent will be scheduled for re-arranging.
1454  *
1455  *----------------------------------------------------------------------
1456  */
1457
1458 static void
1459 Unlink(gridPtr)
1460     GridBag *gridPtr;           /* Window to unlink. */
1461 {
1462     GridBag *masterPtr, *gridPtr2;
1463
1464     masterPtr = gridPtr->masterPtr;
1465     if (masterPtr == NULL) {
1466         return;
1467     }
1468     if (masterPtr->slavePtr == gridPtr) {
1469         masterPtr->slavePtr = gridPtr->nextPtr;
1470     }
1471     else {
1472         for (gridPtr2 = masterPtr->slavePtr; ; gridPtr2 = gridPtr2->nextPtr) {
1473             if (gridPtr2 == NULL) {
1474                 panic("Unlink couldn't find previous window");
1475             }
1476             if (gridPtr2->nextPtr == gridPtr) {
1477                 gridPtr2->nextPtr = gridPtr->nextPtr;
1478                 break;
1479             }
1480         }
1481     }
1482     masterPtr->valid = 0;
1483     if (!(masterPtr->flags & REQUESTED_RELAYOUT)) {
1484         masterPtr->flags |= REQUESTED_RELAYOUT;
1485         Tk_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
1486     }
1487     if (masterPtr->abortPtr != NULL) {
1488         *masterPtr->abortPtr = 1;
1489     }
1490
1491     gridPtr->masterPtr = NULL;
1492 }
1493 \f
1494 /*
1495  *----------------------------------------------------------------------
1496  *
1497  * DestroyGridBag --
1498  *
1499  *      This procedure is invoked by Ck_EventuallyFree or Ck_Release
1500  *      to clean up the internal structure of a gridbag at a safe time
1501  *      (when no-one is using it anymore).
1502  *
1503  * Results:
1504  *      None.
1505  *
1506  * Side effects:
1507  *      Everything associated with the gridbag is freed up.
1508  *
1509  *----------------------------------------------------------------------
1510  */
1511
1512 static void
1513 DestroyGridBag(memPtr)
1514     char *memPtr;               /* Info about window that is now dead. */
1515 {
1516     GridBag *gridPtr = (GridBag *) memPtr;
1517
1518     if (gridPtr->column.max) {
1519         ckfree((char *) gridPtr->column.minsize);
1520         ckfree((char *) gridPtr->column.weight);
1521     }
1522     if (gridPtr->row.max) {
1523         ckfree((char *) gridPtr->row.minsize);
1524         ckfree((char *) gridPtr->row.weight);
1525     }
1526     if (gridPtr->layoutCache)
1527         ckfree((char *) gridPtr->layoutCache);
1528
1529     ckfree((char *) gridPtr);
1530 }
1531 \f
1532 /*
1533  *----------------------------------------------------------------------
1534  *
1535  * GridBagStructureProc --
1536  *
1537  *      This procedure is invoked by the Ck event dispatcher in response
1538  *      to window change events.
1539  *
1540  * Results:
1541  *      None.
1542  *
1543  * Side effects:
1544  *      If a window was just deleted, clean up all its gridbag-related
1545  *      information.  If it was just resized, re-configure its slaves, if
1546  *      any.
1547  *
1548  *----------------------------------------------------------------------
1549  */
1550
1551 static void
1552 GridBagStructureProc(clientData, eventPtr)
1553     ClientData clientData;              /* Our information about window
1554                                          * referred to by eventPtr. */
1555     CkEvent *eventPtr;                  /* Describes what just happened. */
1556 {
1557     GridBag *gridPtr = (GridBag *) clientData;
1558
1559     if (eventPtr->type == CK_EV_MAP || eventPtr->type == CK_EV_EXPOSE) {
1560         gridPtr->valid = 0;
1561         if (!(gridPtr->flags & REQUESTED_RELAYOUT)) {
1562             gridPtr->flags |= REQUESTED_RELAYOUT;
1563             Tk_DoWhenIdle(ArrangeGrid, (ClientData) gridPtr);
1564         }
1565     } else if (eventPtr->type == CK_EV_DESTROY) {
1566         GridBag *gridPtr2, *nextPtr;
1567
1568         if (gridPtr->masterPtr != NULL) {
1569             Unlink(gridPtr);
1570         }
1571         for (gridPtr2 = gridPtr->slavePtr; gridPtr2 != NULL;
1572                                            gridPtr2 = nextPtr) {
1573             Ck_UnmapWindow(gridPtr2->winPtr);
1574             gridPtr2->masterPtr = NULL;
1575             nextPtr = gridPtr2->nextPtr;
1576             gridPtr2->nextPtr = NULL;
1577         }
1578         Tcl_DeleteHashEntry(Tcl_FindHashEntry(&gridBagHashTable,
1579                 (char *) gridPtr->winPtr));
1580         if (gridPtr->flags & REQUESTED_RELAYOUT) {
1581             Tk_CancelIdleCall(ArrangeGrid, (ClientData) gridPtr);
1582         }
1583         gridPtr->winPtr = NULL;
1584         Ck_EventuallyFree((ClientData) gridPtr,
1585             (Ck_FreeProc *) DestroyGridBag);
1586     } else if (eventPtr->type == CK_EV_UNMAP) {
1587         GridBag *gridPtr2;
1588
1589         for (gridPtr2 = gridPtr->slavePtr; gridPtr2 != NULL;
1590                                            gridPtr2 = gridPtr2->nextPtr) {
1591             Ck_UnmapWindow(gridPtr2->winPtr);
1592         }
1593     }
1594 }
1595 \f
1596 /*
1597  *----------------------------------------------------------------------
1598  *
1599  * ConfigureSlaves --
1600  *
1601  *      This implements the guts of the "grid configure" command.  Given
1602  *      a list of slaves and configuration options, it arranges for the
1603  *      gridbag to manage the slaves and sets the specified options.
1604  *
1605  * Results:
1606  *      TCL_OK is returned if all went well.  Otherwise, TCL_ERROR is
1607  *      returned and interp->result is set to contain an error message.
1608  *
1609  * Side effects:
1610  *      Slave windows get taken over by the gridbag.
1611  *
1612  *----------------------------------------------------------------------
1613  */
1614
1615 static int
1616 ConfigureSlaves(interp, winPtr, argc, argv)
1617     Tcl_Interp *interp;         /* Interpreter for error reporting. */
1618     CkWindow *winPtr;           /* Any window in application containing
1619                                  * slaves.  Used to look up slave names. */
1620     int argc;                   /* Number of elements in argv. */
1621     char *argv[];               /* Argument strings:  contains one or more
1622                                  * window names followed by any number
1623                                  * of "option value" pairs.  Caller must
1624                                  * make sure that there is at least one
1625                                  * window name. */
1626 {
1627     GridBag *masterPtr, *slavePtr, *prevPtr;
1628     CkWindow *other, *slave;
1629 #if 0
1630     CkWindow *parent, *ancestor;
1631 #endif
1632     int i, j, numWindows, c, length, tmp, positionGiven;
1633     int currentColumn=0, numColumns=1;
1634     int gotLayout = 0;
1635     int gotWidth = 0;
1636     int width;
1637
1638     /*
1639      * Find out how many windows are specified. (shouldn't use
1640      * hardwired symbols)
1641      */
1642
1643     for (numWindows = 0; numWindows < argc; numWindows++) {
1644         if (argv[numWindows][0] != '.'
1645                  && strcmp(argv[numWindows],"-")!=0
1646                  && strcmp(argv[numWindows],"^")!=0
1647                  && strcmp(argv[numWindows],"x")!=0) {
1648             break;
1649         }
1650     }
1651     slave = NULL;
1652
1653     /*
1654      * Iterate over all of the slave windows, parsing the configuration
1655      * options for each slave.  It's a bit wasteful to re-parse the
1656      * options for each slave, but things get too messy if we try to
1657      * parse the arguments just once at the beginning.  For example,
1658      * if a slave already is managed we want to just change a few
1659      * existing values without resetting everything.  If there are
1660      * multiple windows, the -in option only gets processed for the
1661      * first window.
1662      */
1663
1664     masterPtr = NULL;
1665     prevPtr = NULL;
1666     positionGiven = 0;
1667     for (j = 0; j < numWindows; j++) {
1668
1669         /* adjust default widget location for non-widgets */
1670         if (*argv[j] != '.') {
1671             switch (*argv[j]) {
1672                 case '^':       /* extend the widget in the previous row 
1673                                  * Since we don't know who the master is yet,
1674                                  * handle these in a separate pass at the end
1675                                  */
1676                     /* no break */
1677                 case REL_SKIP:  /* skip over the next column */
1678                     currentColumn++;
1679                     break;
1680                 case REL_HORIZ: /* increase the span, already dealt with */
1681                     /* not quite right */
1682                     if (j>0 && (*argv[j-1] == REL_SKIP || *argv[j-1] == '^')) {
1683                         Tcl_AppendResult(interp, "Invalid grid combination:",
1684                             " \"-\" can't follow \"", argv[j - 1], "\"", NULL);
1685                         return TCL_ERROR;
1686                     }
1687                     break;
1688                 default:
1689                     panic("Invalid grid position indicator");
1690             }
1691             continue;
1692         }
1693
1694         for (numColumns = 1;
1695              j + numColumns < numWindows && *argv[j + numColumns] == REL_HORIZ;
1696              numColumns++) {
1697             /* null body */
1698         }
1699         slave = Ck_NameToWindow(interp, argv[j], winPtr);
1700         if (slave == NULL) {
1701             return TCL_ERROR;
1702         }
1703         if (slave->flags & CK_TOPLEVEL) {
1704             Tcl_AppendResult(interp, "can't manage \"", argv[j],
1705                     "\": it's a top-level window", (char *) NULL);
1706             return TCL_ERROR;
1707         }
1708         slavePtr = GetGridBag(slave);
1709
1710         /*
1711          * The following statement is taken from tkPack.c:
1712          *
1713          * "If the slave isn't currently managed, reset all of its
1714          * configuration information to default values (there could
1715          * be old values left from a previous packer)."
1716          *
1717          * I disagree with this statement.  If a slave is disabled (using
1718          * "forget") and then re-enabled, I submit that 90% of the time the
1719          * programmer will want it to retain its old configuration information.
1720          * If the programmer doesn't want this behavior, then she can reset the
1721          * defaults for herself, but she will never have to worry about keeping
1722          * track of the old state. 
1723          */
1724
1725         for (i = numWindows; i < argc; i+=2) {
1726             if ((i+2) > argc) {
1727                 Tcl_AppendResult(interp, "extra option \"", argv[i],
1728                         "\" (option with no value?)", (char *) NULL);
1729                 return TCL_ERROR;
1730             }
1731             length = strlen(argv[i]);
1732             if (length < 2) {
1733                 goto badOption;
1734             }
1735             c = argv[i][1];
1736 #if 0
1737             if ((c == 'i') && (strcmp(argv[i], "-in") == 0)) {
1738                 if (j == 0) {
1739                     other = Ck_NameToWindow(interp, argv[i+1], winPtr);
1740                     if (other == NULL) {
1741                         return TCL_ERROR;
1742                     }
1743                     if (other == slave) {
1744                         sprintf(interp->result,
1745                             "Window can't be managed in itself");
1746                         return TCL_ERROR;
1747                     }
1748                     masterPtr = GetGridBag(other);
1749                     prevPtr = masterPtr->slavePtr;
1750                     if (prevPtr != NULL) {
1751                         while (prevPtr->nextPtr != NULL) {
1752                             prevPtr = prevPtr->nextPtr;
1753                         }
1754                     }
1755                     positionGiven = 1;
1756                 }
1757             } else
1758 #endif
1759             if ((c == 'i') && (strcmp(argv[i], "-ipadx") == 0)) {
1760                 if ((Ck_GetCoord(interp, slave, argv[i + 1], &tmp) != TCL_OK)
1761                         || (tmp < 0)) {
1762                     Tcl_ResetResult(interp);
1763                     Tcl_AppendResult(interp, "bad ipadx value \"", argv[i+1],
1764                             "\": must be positive screen distance",
1765                             (char *) NULL);
1766                     return TCL_ERROR;
1767                 }
1768                 slavePtr->iPadX = tmp*2;
1769             } else if ((c == 'i') && (strcmp(argv[i], "-ipady") == 0)) {
1770                 if ((Ck_GetCoord(interp, slave, argv[i + 1], &tmp) != TCL_OK)
1771                         || (tmp< 0)) {
1772                     Tcl_ResetResult(interp);
1773                     Tcl_AppendResult(interp, "bad ipady value \"", argv[i+1],
1774                             "\": must be positive screen distance",
1775                             (char *) NULL);
1776                     return TCL_ERROR;
1777                 }
1778                 slavePtr->iPadY = tmp*2;
1779             } else if ((c == 'p') && (strcmp(argv[i], "-padx") == 0)) {
1780                 if ((Ck_GetCoord(interp, slave, argv[i + 1], &tmp) != TCL_OK)
1781                         || (tmp< 0)) {
1782                     Tcl_ResetResult(interp);
1783                     Tcl_AppendResult(interp, "bad padx value \"", argv[i+1],
1784                             "\": must be positive screen distance",
1785                             (char *) NULL);
1786                     return TCL_ERROR;
1787                 }
1788                 slavePtr->padX = tmp*2;
1789             } else if ((c == 'p') && (strcmp(argv[i], "-pady") == 0)) {
1790                 if ((Ck_GetCoord(interp, slave, argv[i + 1], &tmp) != TCL_OK)
1791                         || (tmp< 0)) {
1792                     Tcl_ResetResult(interp);
1793                     Tcl_AppendResult(interp, "bad pady value \"", argv[i+1],
1794                             "\": must be positive screen distance",
1795                             (char *) NULL);
1796                     return TCL_ERROR;
1797                 }
1798                 slavePtr->padY = tmp*2;
1799             } else if ((c == 'c') && (strcmp(argv[i], "-column") == 0)) {
1800                 if (Tcl_GetInt(interp, argv[i+1], &tmp) != TCL_OK || tmp<0) {
1801                     Tcl_ResetResult(interp);
1802                     Tcl_AppendResult(interp, "bad column value \"", argv[i+1],
1803                         "\": must be a non-negative integer", (char *) NULL);
1804                     return TCL_ERROR;
1805                 }
1806                 slavePtr->gridColumn = tmp;
1807             } else if ((c == 'r') && (strcmp(argv[i], "-row") == 0)) {
1808                 if (Tcl_GetInt(interp, argv[i+1], &tmp) != TCL_OK || tmp<0) {
1809                     Tcl_ResetResult(interp);
1810                     Tcl_AppendResult(interp, "bad grid value \"", argv[i+1],
1811                         "\": must be a non-negative integer", (char *) NULL);
1812                     return TCL_ERROR;
1813                 }
1814                 slavePtr->gridRow = tmp;
1815             } else if ((c == 'c') && (strcmp(argv[i], "-columnspan") == 0)) {
1816                 if (Tcl_GetInt(interp, argv[i+1], &tmp) != TCL_OK ||
1817                     tmp <= 0) {
1818                     Tcl_ResetResult(interp);
1819                     Tcl_AppendResult(interp, "bad columnspan value \"",
1820                             argv[i+1],
1821                             "\": must be a positive integer", (char *) NULL);
1822                     return TCL_ERROR;
1823                 }
1824                 slavePtr->gridWidth = tmp;
1825                 gotWidth++;
1826             } else if ((c == 'r') && (strcmp(argv[i], "-rowspan") == 0)) {
1827                 if (Tcl_GetInt(interp, argv[i+1], &tmp) != TCL_OK) {
1828                     Tcl_ResetResult(interp);
1829                     Tcl_AppendResult(interp, "bad rowspan value \"",
1830                             argv[i+1],
1831                             "\": must be a positive integer", (char *) NULL);
1832                     return TCL_ERROR;
1833                 }
1834                 slavePtr->gridHeight = tmp;
1835 #if 0
1836             } else if ((c == 'w') &&
1837                 (!strcmp(argv[i], "-weightx") ||
1838                  !strcmp(argv[i], "-wx"))) {
1839                 if (Tcl_GetDouble(interp, argv[i+1], &tmp_dbl) != TCL_OK) {
1840                     Tcl_ResetResult(interp);
1841                     Tcl_AppendResult(interp, "bad weight value \"", argv[i+1],
1842                             "\": must be a double", (char *) NULL);
1843                     return TCL_ERROR;
1844                 }
1845                 slavePtr->weightX = tmp_dbl;
1846             } else if ((c == 'w') &&
1847                 (!strcmp(argv[i], "-weighty") ||
1848                  !strcmp(argv[i], "-wy"))) {
1849                 if (Tcl_GetDouble(interp, argv[i+1], &tmp_dbl) != TCL_OK) {
1850                     Tcl_ResetResult(interp);
1851                     Tcl_AppendResult(interp, "bad weight value \"", argv[i+1],
1852                             "\": must be a double", (char *) NULL);
1853                     return TCL_ERROR;
1854                 }
1855                 slavePtr->weightY = tmp_dbl;
1856 #endif
1857             } else if ((c == 's') && strcmp(argv[i], "-sticky") == 0) {
1858                 int sticky = StringToSticky(argv[i+1]);
1859                 if (sticky == -1) {
1860                     Tcl_AppendResult(interp, "bad stickyness value \"",
1861                         argv[i+1],
1862                         "\": must be a string containing n, e, s, and/or w",
1863                         (char *) NULL);
1864                     return TCL_ERROR;
1865                 }
1866                 slavePtr->flags = sticky | (slavePtr->flags & ~STICK_ALL);
1867             } else {
1868                 badOption:
1869                 Tcl_AppendResult(interp, "unknown or ambiguous option \"",
1870 #if 0
1871                         argv[i], "\": must be -in, -sticky, ",
1872 #else
1873                         argv[i], "\": must be -sticky, ",
1874 #endif
1875                         "-row, -column, -rowspan, -columnspan, ",
1876                         "-ipadx, -ipady, -padx or -pady.",
1877                         (char *) NULL);
1878                 return TCL_ERROR;
1879             }
1880         }
1881
1882         /*
1883          * If no position in a gridbag list was specified and the slave
1884          * is already managed, then leave it in its current location in
1885          * its current gridbag list.
1886          */
1887
1888         if (!positionGiven && (slavePtr->masterPtr != NULL)) {
1889             masterPtr = slavePtr->masterPtr;
1890             goto scheduleLayout;
1891         }
1892
1893         /*
1894          * If the slave is going to be put back after itself then
1895          * skip the whole operation, since it won't work anyway.
1896          */
1897
1898         if (prevPtr == slavePtr) {
1899             masterPtr = slavePtr->masterPtr;
1900             goto scheduleLayout;
1901         }
1902     
1903         /*
1904          * If the "-in" option has not been specified, arrange for the
1905          * slave to go at the end of the order for its parent.
1906          */
1907     
1908         if (!positionGiven) {
1909             masterPtr = GetGridBag(slave->parentPtr);
1910             prevPtr = masterPtr->slavePtr;
1911             if (prevPtr != NULL) {
1912                 while (prevPtr->nextPtr != NULL) {
1913                     prevPtr = prevPtr->nextPtr;
1914                 }
1915             }
1916         }
1917 #if 0
1918         /*
1919          * Make sure that the slave's parent is either the master or
1920          * an ancestor of the master.
1921          */
1922     
1923         parent = slave->parentPtr;
1924         for (ancestor = masterPtr->winPtr; ; ancestor = ancestor->parentPtr) {
1925             if (ancestor == parent) {
1926                 break;
1927             }
1928             if (ancestor->flags & CK_TOPLEVEL) {
1929                 Tcl_AppendResult(interp, "can't put ", argv[j],
1930                         " inside ", masterPtr->winPtr->parentPtr,
1931                         (char *) NULL);
1932                 return TCL_ERROR;
1933             }
1934         }
1935 #else
1936         if (masterPtr->winPtr != slave->parentPtr) {
1937             Tcl_AppendResult(interp, "can't put ", argv[j],
1938                 " inside ", masterPtr->winPtr->parentPtr,
1939                 (char *) NULL);
1940             return TCL_ERROR;
1941         }
1942 #endif
1943
1944         /*
1945          * Unlink the slave if it's currently managed, then position it
1946          * after prevPtr.
1947          */
1948
1949         if (slavePtr->masterPtr != NULL) {
1950             Unlink(slavePtr);
1951         }
1952         slavePtr->masterPtr = masterPtr;
1953         if (prevPtr == NULL) {
1954             slavePtr->nextPtr = masterPtr->slavePtr;
1955             masterPtr->slavePtr = slavePtr;
1956         } else {
1957             slavePtr->nextPtr = prevPtr->nextPtr;
1958             prevPtr->nextPtr = slavePtr;
1959         }
1960         Ck_ManageGeometry(slave, &gridMgrType, (ClientData) slavePtr);
1961         prevPtr = slavePtr;
1962
1963         /* assign default row and column */
1964
1965         if (slavePtr->gridColumn == -1) {
1966             slavePtr->gridColumn = currentColumn;
1967         }
1968         slavePtr->gridWidth += numColumns - 1;
1969         if (slavePtr->gridRow == -1) {
1970             if (!gotLayout++) GetCachedLayoutInfo(masterPtr);
1971             slavePtr->gridRow = masterPtr->layoutCache->lastRow;
1972         }
1973
1974         /*
1975          * Arrange for the parent to be re-arranged at the first
1976          * idle moment.
1977          */
1978
1979         scheduleLayout:
1980         if (masterPtr->abortPtr != NULL) {
1981             *masterPtr->abortPtr = 1;
1982         }
1983         masterPtr->valid = 0;
1984         if (!(masterPtr->flags & REQUESTED_RELAYOUT)) {
1985             masterPtr->flags |= REQUESTED_RELAYOUT;
1986             Tk_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
1987         }
1988         currentColumn += slavePtr->gridWidth;
1989         numColumns = 1;
1990     }
1991
1992     /* now look for all the "^"'s */
1993
1994     for (j = 0; j < numWindows; j++) {
1995         struct GridBag *otherPtr;
1996         char *lastWindow; /* use this window to base current row/col on */
1997         int match;        /* found a match for the ^ */
1998
1999         if (*argv[j] == '.') {
2000             lastWindow = argv[j];
2001         }
2002         if (*argv[j] != '^') {
2003             continue;
2004         }
2005         for (width=1; width+j < numWindows && *argv[j+width] == '^'; width++) {
2006             /* Null Body */
2007         }
2008         other = Ck_NameToWindow(interp, lastWindow, winPtr);
2009         otherPtr = GetGridBag(other);
2010         if (!gotLayout++) GetCachedLayoutInfo(masterPtr);
2011
2012         for (match=0, slavePtr = masterPtr->slavePtr; slavePtr != NULL;
2013                                          slavePtr = slavePtr->nextPtr) {
2014
2015             if (slavePtr->gridWidth == width
2016                 && slavePtr->gridColumn == otherPtr->gridColumn +
2017                    otherPtr->gridWidth
2018                 && slavePtr->gridRow + slavePtr->gridHeight == 
2019                    otherPtr->gridRow) {
2020                 slavePtr->gridHeight++;
2021                 match++;
2022             }
2023             lastWindow = slavePtr->winPtr->pathName;
2024         }
2025         if (!match) {
2026             Tcl_AppendResult(interp, "can't find slave to extend with \"^\"",
2027                     " after ", lastWindow, (char *) NULL);
2028             return TCL_ERROR;
2029         }
2030         j += width - 1;
2031     }
2032     return TCL_OK;
2033 }
2034 \f
2035 /*
2036  *----------------------------------------------------------------------
2037  *
2038  * Convert "Sticky" bits into a string
2039  *
2040  *----------------------------------------------------------------------
2041  */
2042
2043 static void
2044 StickyToString(flags, result)
2045     int flags;          /* the sticky flags */
2046     char *result;       /* where to put the result */
2047 {
2048     int count = 0;
2049     if (flags & STICK_NORTH)
2050         result[count++] = 'n';
2051     if (flags & STICK_EAST)
2052         result[count++] = 'e';
2053     if (flags & STICK_SOUTH)
2054         result[count++] = 's';
2055     if (flags & STICK_WEST)
2056         result[count++] = 'w';
2057     if (count) {
2058         result[count] = '\0';
2059     } else {
2060         sprintf(result,"{}");
2061     }
2062 }
2063 \f
2064 /*
2065  *----------------------------------------------------------------------
2066  *
2067  * Convert sticky string to flags
2068  *
2069  *----------------------------------------------------------------------
2070  */
2071
2072 static int
2073 StringToSticky(string)
2074     char *string;
2075 {
2076     int sticky = 0;
2077     char c;
2078
2079     while ((c = *string++) != '\0') {
2080         switch (c) {
2081             case 'n': case 'N': sticky |= STICK_NORTH; break;
2082             case 'e': case 'E': sticky |= STICK_EAST;  break;
2083             case 's': case 'S': sticky |= STICK_SOUTH; break;
2084             case 'w': case 'W': sticky |= STICK_WEST;  break;
2085             case ' ': case ',': case '\t': case '\r': case '\n': break;
2086             default: return -1;
2087         }
2088     }
2089     return sticky;
2090 }