]> www.wagner.pp.ru Git - oss/ck.git/blob - ckPack.c
Ck console graphics toolkit
[oss/ck.git] / ckPack.c
1 /* 
2  * ckPack.c --
3  *
4  *      This file contains code to implement the "packer"
5  *      geometry manager.
6  *
7  * Copyright (c) 1990-1994 The Regents of the University of California.
8  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
9  * Copyright (c) 1995 Christian Werner
10  *
11  * See the file "license.terms" for information on usage and redistribution
12  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
13  */
14
15 #include "ckPort.h"
16 #include "ck.h"
17
18 typedef enum {TOP, BOTTOM, LEFT, RIGHT} Side;
19
20 /* For each window that the packer cares about (either because
21  * the window is managed by the packer or because the window
22  * has slaves that are managed by the packer), there is a
23  * structure of the following type:
24  */
25
26 typedef struct Packer {
27     CkWindow *winPtr;           /* Pointer to window.  NULL means that
28                                  * the window has been deleted, but the
29                                  * packer hasn't had a chance to clean up
30                                  * yet because the structure is still in
31                                  * use. */
32     struct Packer *masterPtr;   /* Master window within which this window
33                                  * is packed (NULL means this window
34                                  * isn't managed by the packer). */
35     struct Packer *nextPtr;     /* Next window packed within same
36                                  * parent.  List is priority-ordered:
37                                  * first on list gets packed first. */
38     struct Packer *slavePtr;    /* First in list of slaves packed
39                                  * inside this window (NULL means
40                                  * no packed slaves). */
41     Side side;                  /* Side of parent against which
42                                  * this window is packed. */
43     Ck_Anchor anchor;           /* If frame allocated for window is larger
44                                  * than window needs, this indicates how
45                                  * where to position window in frame. */
46     int padX, padY;             /* Total additional pixels to leave around the
47                                  * window (half of this space is left on each
48                                  * side).  This is space *outside* the window:
49                                  * we'll allocate extra space in frame but
50                                  * won't enlarge window). */
51     int iPadX, iPadY;           /* Total extra pixels to allocate inside the
52                                  * window (half this amount will appear on
53                                  * each side). */
54     int *abortPtr;              /* If non-NULL, it means that there is a nested
55                                  * call to ArrangePacking already working on
56                                  * this window.  *abortPtr may be set to 1 to
57                                  * abort that nested call.  This happens, for
58                                  * example, if tkwin or any of its slaves
59                                  * is deleted. */
60     int flags;                  /* Miscellaneous flags;  see below
61                                  * for definitions. */
62 } Packer;
63
64 /*
65  * Flag values for Packer structures:
66  *
67  * REQUESTED_REPACK:            1 means a Ck_DoWhenIdle request
68  *                              has already been made to repack
69  *                              all the slaves of this window.
70  * FILLX:                       1 means if frame allocated for window
71  *                              is wider than window needs, expand window
72  *                              to fill frame.  0 means don't make window
73  *                              any larger than needed.
74  * FILLY:                       Same as FILLX, except for height.
75  * EXPAND:                      1 means this window's frame will absorb any
76  *                              extra space in the parent window.
77  * DONT_PROPAGATE:              1 means don't set this window's requested
78  *                              size.  0 means if this window is a master
79  *                              then Tk will set its requested size to fit
80  *                              the needs of its slaves.
81  */
82
83 #define REQUESTED_REPACK        1
84 #define FILLX                   2
85 #define FILLY                   4
86 #define EXPAND                  8
87 #define DONT_PROPAGATE          16
88
89 /*
90  * Hash table used to map from CkWindow pointers to corresponding
91  * Packer structures:
92  */
93
94 static Tcl_HashTable packerHashTable;
95
96 /*
97  * Have statics in this module been initialized?
98  */
99
100 static int initialized = 0;
101
102 /*
103  * The following structure is the official type record for the
104  * packer:
105  */
106
107 static void             PackReqProc _ANSI_ARGS_((ClientData clientData,
108                             CkWindow *winPtr));
109 static void             PackLostSlaveProc _ANSI_ARGS_((ClientData clientData,
110                             CkWindow *winPtr));
111
112 static Ck_GeomMgr packerType = {
113     "pack",                     /* name */
114     PackReqProc,                /* requestProc */
115     PackLostSlaveProc,          /* lostSlaveProc */
116 };
117
118 /*
119  * Forward declarations for procedures defined later in this file:
120  */
121
122 static void             ArrangePacking _ANSI_ARGS_((ClientData clientData));
123 static int              ConfigureSlaves _ANSI_ARGS_((Tcl_Interp *interp,
124                             CkWindow *winPtr, int argc, char *argv[]));
125 static Packer *         GetPacker _ANSI_ARGS_((CkWindow *winPtr));
126 static void             PackStructureProc _ANSI_ARGS_((ClientData clientData,
127                             CkEvent *eventPtr));
128 static void             Unlink _ANSI_ARGS_((Packer *packPtr));
129 static int              XExpansion _ANSI_ARGS_((Packer *slavePtr,
130                             int cavityWidth));
131 static int              YExpansion _ANSI_ARGS_((Packer *slavePtr,
132                             int cavityHeight));
133 \f
134 /*
135  *--------------------------------------------------------------
136  *
137  * Ck_PackCmd --
138  *
139  *      This procedure is invoked to process the "pack" Tcl command.
140  *      See the user documentation for details on what it does.
141  *
142  * Results:
143  *      A standard Tcl result.
144  *
145  * Side effects:
146  *      See the user documentation.
147  *
148  *--------------------------------------------------------------
149  */
150
151 int
152 Ck_PackCmd(clientData, interp, argc, argv)
153     ClientData clientData;      /* Main window associated with
154                                  * interpreter. */
155     Tcl_Interp *interp;         /* Current interpreter. */
156     int argc;                   /* Number of arguments. */
157     char **argv;                /* Argument strings. */
158 {
159     CkWindow *mainPtr = (CkWindow *) clientData;
160     size_t length;
161     int c;
162
163     if ((argc >= 2) && (argv[1][0] == '.')) {
164         return ConfigureSlaves(interp, mainPtr, argc-1, argv+1);
165     }
166     if (argc < 3) {
167         Tcl_AppendResult(interp, "wrong # args: should be \"",
168                 argv[0], " option arg ?arg ...?\"", (char *) NULL);
169         return TCL_ERROR;
170     }
171     c = argv[1][0];
172     length = strlen(argv[1]);
173     if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) {
174         if (argv[2][0] != '.') {
175             Tcl_AppendResult(interp, "bad argument \"", argv[2],
176                     "\": must be name of window", (char *) NULL);
177             return TCL_ERROR;
178         }
179         return ConfigureSlaves(interp, mainPtr, argc-2, argv+2);
180     } else if ((c == 'f') && (strncmp(argv[1], "forget", length) == 0)) {
181         CkWindow *slave;
182         Packer *slavePtr;
183         int i;
184
185         for (i = 2; i < argc; i++) {
186             slave = Ck_NameToWindow(interp, argv[i], mainPtr);
187             if (slave == NULL) {
188                 continue;
189             }
190             slavePtr = GetPacker(slave);
191             if ((slavePtr != NULL) && (slavePtr->masterPtr != NULL)) {
192                 Ck_ManageGeometry(slave, (Ck_GeomMgr *) NULL,
193                         (ClientData) NULL);
194                 if (slavePtr->masterPtr->winPtr != slavePtr->winPtr->parentPtr) {
195                     Ck_UnmaintainGeometry(slavePtr->winPtr,
196                             slavePtr->masterPtr->winPtr);
197                 }
198                 Unlink(slavePtr);
199                 Ck_UnmapWindow(slavePtr->winPtr);
200             }
201         }
202     } else if ((c == 'i') && (strncmp(argv[1], "info", length) == 0)) {
203         register Packer *slavePtr;
204         CkWindow *slave;
205         char buffer[300];
206         static char *sideNames[] = {"top", "bottom", "left", "right"};
207
208         if (argc != 3) {
209             Tcl_AppendResult(interp, "wrong # args: should be \"",
210                     argv[0], " info window\"", (char *) NULL);
211             return TCL_ERROR;
212         }
213         slave = Ck_NameToWindow(interp, argv[2], mainPtr);
214         if (slave == NULL) {
215             return TCL_ERROR;
216         }
217         slavePtr = GetPacker(slave);
218         if (slavePtr->masterPtr == NULL) {
219             Tcl_AppendResult(interp, "window \"", argv[2],
220                     "\" isn't packed", (char *) NULL);
221             return TCL_ERROR;
222         }
223         Tcl_AppendElement(interp, "-in");
224         Tcl_AppendElement(interp, slavePtr->masterPtr->winPtr->pathName);
225         Tcl_AppendElement(interp, "-anchor");
226         Tcl_AppendElement(interp, Ck_NameOfAnchor(slavePtr->anchor));
227         Tcl_AppendResult(interp, " -expand ",
228                 (slavePtr->flags & EXPAND) ? "1" : "0", " -fill ",
229                 (char *) NULL);
230         switch (slavePtr->flags & (FILLX|FILLY)) {
231             case 0:
232                 Tcl_AppendResult(interp, "none", (char *) NULL);
233                 break;
234             case FILLX:
235                 Tcl_AppendResult(interp, "x", (char *) NULL);
236                 break;
237             case FILLY:
238                 Tcl_AppendResult(interp, "y", (char *) NULL);
239                 break;
240             case FILLX|FILLY:
241                 Tcl_AppendResult(interp, "both", (char *) NULL);
242                 break;
243         }
244         sprintf(buffer, " -ipadx %d -ipady %d -padx %d -pady %d",
245                 slavePtr->iPadX/2, slavePtr->iPadY/2, slavePtr->padX/2,
246                 slavePtr->padY/2);
247         Tcl_AppendResult(interp, buffer, " -side ", sideNames[slavePtr->side],
248                 (char *) NULL);
249     } else if ((c == 'p') && (strncmp(argv[1], "propagate", length) == 0)) {
250         CkWindow *master;
251         Packer *masterPtr;
252         int propagate;
253
254         if (argc > 4) {
255             Tcl_AppendResult(interp, "wrong # args: should be \"",
256                     argv[0], " propagate window ?boolean?\"", (char *) NULL);
257             return TCL_ERROR;
258         }
259         master = Ck_NameToWindow(interp, argv[2], mainPtr);
260         if (master == NULL) {
261             return TCL_ERROR;
262         }
263         masterPtr = GetPacker(master);
264         if (argc == 3) {
265             if (masterPtr->flags & DONT_PROPAGATE) {
266                 interp->result = "0";
267             } else {
268                 interp->result = "1";
269             }
270             return TCL_OK;
271         }
272         if (Tcl_GetBoolean(interp, argv[3], &propagate) != TCL_OK) {
273             return TCL_ERROR;
274         }
275         if (propagate) {
276             masterPtr->flags &= ~DONT_PROPAGATE;
277
278             /*
279              * Repack the master to allow new geometry information to
280              * propagate upwards to the master's master.
281              */
282
283             if (masterPtr->abortPtr != NULL) {
284                 *masterPtr->abortPtr = 1;
285             }
286             if (!(masterPtr->flags & REQUESTED_REPACK)) {
287                 masterPtr->flags |= REQUESTED_REPACK;
288                 Tcl_DoWhenIdle(ArrangePacking, (ClientData) masterPtr);
289             }
290         } else {
291             masterPtr->flags |= DONT_PROPAGATE;
292         }
293     } else if ((c == 's') && (strncmp(argv[1], "slaves", length) == 0)) {
294         CkWindow *master;
295         Packer *masterPtr, *slavePtr;
296
297         if (argc != 3) {
298             Tcl_AppendResult(interp, "wrong # args: should be \"",
299                     argv[0], " slaves window\"", (char *) NULL);
300             return TCL_ERROR;
301         }
302         master = Ck_NameToWindow(interp, argv[2], mainPtr);
303         if (master == NULL) {
304             return TCL_ERROR;
305         }
306         masterPtr = GetPacker(master);
307         for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
308                 slavePtr = slavePtr->nextPtr) {
309             Tcl_AppendElement(interp, slavePtr->winPtr->pathName);
310         }
311     } else {
312         Tcl_AppendResult(interp, "bad option \"", argv[1],
313                 "\":  must be configure, forget, info, ",
314                 "propagate, or slaves", (char *) NULL);
315         return TCL_ERROR;
316     }
317     return TCL_OK;
318 }
319 \f
320 /*
321  *--------------------------------------------------------------
322  *
323  * PackReqProc --
324  *
325  *      This procedure is invoked by Ck_GeometryRequest for
326  *      windows managed by the packer.
327  *
328  * Results:
329  *      None.
330  *
331  * Side effects:
332  *      Arranges for winPtr, and all its managed siblings, to
333  *      be re-packed at the next idle point.
334  *
335  *--------------------------------------------------------------
336  */
337
338 static void
339 PackReqProc(clientData, winPtr)
340     ClientData clientData;      /* Packer's information about
341                                  * window that got new preferred
342                                  * geometry.  */
343     CkWindow *winPtr;           /* Other information about the window. */
344 {
345     register Packer *packPtr = (Packer *) clientData;
346
347     packPtr = packPtr->masterPtr;
348     if (!(packPtr->flags & REQUESTED_REPACK)) {
349         packPtr->flags |= REQUESTED_REPACK;
350         Tcl_DoWhenIdle(ArrangePacking, (ClientData) packPtr);
351     }
352 }
353 \f
354 /*
355  *--------------------------------------------------------------
356  *
357  * PackLostSlaveProc --
358  *
359  *      This procedure is invoked whenever some other geometry
360  *      claims control over a slave that used to be managed by us.
361  *
362  * Results:
363  *      None.
364  *
365  * Side effects:
366  *      Forgets all packer-related information about the slave.
367  *
368  *--------------------------------------------------------------
369  */
370
371 static void
372 PackLostSlaveProc(clientData, winPtr)
373     ClientData clientData;      /* Packer structure for slave window that
374                                  * was stolen away. */
375     CkWindow *winPtr;           /* Pointer to window. */
376 {
377     register Packer *slavePtr = (Packer *) clientData;
378
379     if (slavePtr->masterPtr->winPtr != slavePtr->winPtr->parentPtr) {
380         Ck_UnmaintainGeometry(slavePtr->winPtr, slavePtr->masterPtr->winPtr);
381     }
382     Unlink(slavePtr);
383     Ck_UnmapWindow(slavePtr->winPtr);
384 }
385 \f
386 /*
387  *--------------------------------------------------------------
388  *
389  * ArrangePacking --
390  *
391  *      This procedure is invoked (using the Tcl_DoWhenIdle
392  *      mechanism) to re-layout a set of windows managed by
393  *      the packer.  It is invoked at idle time so that a
394  *      series of packer requests can be merged into a single
395  *      layout operation.
396  *
397  * Results:
398  *      None.
399  *
400  * Side effects:
401  *      The packed slaves of masterPtr may get resized or
402  *      moved.
403  *
404  *--------------------------------------------------------------
405  */
406
407 static void
408 ArrangePacking(clientData)
409     ClientData clientData;      /* Structure describing parent whose slaves
410                                  * are to be re-layed out. */
411 {
412     register Packer *masterPtr = (Packer *) clientData;
413     register Packer *slavePtr;  
414     int cavityX, cavityY, cavityWidth, cavityHeight;
415                                 /* These variables keep track of the
416                                  * as-yet-unallocated space remaining in
417                                  * the middle of the parent window. */
418     int frameX, frameY, frameWidth, frameHeight;
419                                 /* These variables keep track of the frame
420                                  * allocated to the current window. */
421     int x, y, width, height;    /* These variables are used to hold the
422                                  * actual geometry of the current window. */
423     int intBWidth;              /* Width of internal border in parent window,
424                                  * if any. */
425     int abort;                  /* May get set to non-zero to abort this
426                                  * repacking operation. */
427     int borderX, borderY;
428     int maxWidth, maxHeight, tmp;
429
430     masterPtr->flags &= ~REQUESTED_REPACK;
431
432     /*
433      * If the parent has no slaves anymore, then don't do anything
434      * at all:  just leave the parent's size as-is.
435      */
436
437     if (masterPtr->slavePtr == NULL) {
438         return;
439     }
440
441     /*
442      * Abort any nested call to ArrangePacking for this window, since
443      * we'll do everything necessary here, and set up so this call
444      * can be aborted if necessary.  
445      */
446
447     if (masterPtr->abortPtr != NULL) {
448         *masterPtr->abortPtr = 1;
449     }
450     masterPtr->abortPtr = &abort;
451     abort = 0;
452     Ck_Preserve((ClientData) masterPtr);
453
454     /*
455      * Pass #1: scan all the slaves to figure out the total amount
456      * of space needed.  Two separate width and height values are
457      * computed:
458      *
459      * width -          Holds the sum of the widths (plus padding) of
460      *                  all the slaves seen so far that were packed LEFT
461      *                  or RIGHT.
462      * height -         Holds the sum of the heights (plus padding) of
463      *                  all the slaves seen so far that were packed TOP
464      *                  or BOTTOM.
465      *
466      * maxWidth -       Gradually builds up the width needed by the master
467      *                  to just barely satisfy all the slave's needs.  For
468      *                  each slave, the code computes the width needed for
469      *                  all the slaves so far and updates maxWidth if the
470      *                  new value is greater.
471      * maxHeight -      Same as maxWidth, except keeps height info.
472      */
473
474     intBWidth = (masterPtr->winPtr->flags & CK_BORDER) ? 1 : 0;
475     width = height = maxWidth = maxHeight = 2*intBWidth;
476     for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
477             slavePtr = slavePtr->nextPtr) {
478         if ((slavePtr->side == TOP) || (slavePtr->side == BOTTOM)) {
479             tmp = slavePtr->winPtr->reqWidth
480                     + slavePtr->padX + slavePtr->iPadX + width;
481             if (tmp > maxWidth) {
482                 maxWidth = tmp;
483             }
484             height += slavePtr->winPtr->reqHeight
485                     + slavePtr->padY + slavePtr->iPadY;
486         } else {
487             tmp = slavePtr->winPtr->reqHeight
488                     + slavePtr->padY + slavePtr->iPadY + height;
489             if (tmp > maxHeight) {
490                 maxHeight = tmp;
491             }
492             width += slavePtr->winPtr->reqWidth
493                     + slavePtr->padX + slavePtr->iPadX;
494         }
495     }
496     if (width > maxWidth) {
497         maxWidth = width;
498     }
499     if (height > maxHeight) {
500         maxHeight = height;
501     }
502
503     /*
504      * If the total amount of space needed in the parent window has
505      * changed, and if we're propagating geometry information, then
506      * notify the next geometry manager up and requeue ourselves to
507      * start again after the parent has had a chance to
508      * resize us.
509      */
510
511     if (((maxWidth != masterPtr->winPtr->reqWidth)
512             || (maxHeight != masterPtr->winPtr->reqHeight))
513             && !(masterPtr->flags & DONT_PROPAGATE)) {
514         Ck_GeometryRequest(masterPtr->winPtr, maxWidth, maxHeight);
515         masterPtr->flags |= REQUESTED_REPACK;
516         Tcl_DoWhenIdle(ArrangePacking, (ClientData) masterPtr);
517         goto done;
518     }
519
520     /*
521      * Pass #2: scan the slaves a second time assigning
522      * new sizes.  The "cavity" variables keep track of the
523      * unclaimed space in the cavity of the window;  this
524      * shrinks inward as we allocate windows around the
525      * edges.  The "frame" variables keep track of the space
526      * allocated to the current window and its frame.  The
527      * current window is then placed somewhere inside the
528      * frame, depending on anchor.
529      */
530
531     cavityX = cavityY = x = y = intBWidth;
532     cavityWidth = masterPtr->winPtr->width - 2*intBWidth;
533     cavityHeight = masterPtr->winPtr->height - 2*intBWidth;
534     for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
535             slavePtr = slavePtr->nextPtr) {
536         if ((slavePtr->side == TOP) || (slavePtr->side == BOTTOM)) {
537             frameWidth = cavityWidth;
538             frameHeight = slavePtr->winPtr->reqHeight
539                     + slavePtr->padY + slavePtr->iPadY;
540             if (slavePtr->flags & EXPAND) {
541                 frameHeight += YExpansion(slavePtr, cavityHeight);
542             }
543             cavityHeight -= frameHeight;
544             if (cavityHeight < 0) {
545                 frameHeight += cavityHeight;
546                 cavityHeight = 0;
547             }
548             frameX = cavityX;
549             if (slavePtr->side == TOP) {
550                 frameY = cavityY;
551                 cavityY += frameHeight;
552             } else {
553                 frameY = cavityY + cavityHeight;
554             }
555         } else {
556             frameHeight = cavityHeight;
557             frameWidth = slavePtr->winPtr->reqWidth
558                     + slavePtr->padX + slavePtr->iPadX;
559             if (slavePtr->flags & EXPAND) {
560                 frameWidth += XExpansion(slavePtr, cavityWidth);
561             }
562             cavityWidth -= frameWidth;
563             if (cavityWidth < 0) {
564                 frameWidth += cavityWidth;
565                 cavityWidth = 0;
566             }
567             frameY = cavityY;
568             if (slavePtr->side == LEFT) {
569                 frameX = cavityX;
570                 cavityX += frameWidth;
571             } else {
572                 frameX = cavityX + cavityWidth;
573             }
574         }
575
576         /*
577          * Now that we've got the size of the frame for the window,
578          * compute the window's actual size and location using the
579          * fill, padding, and frame factors.  The variables "borderX"
580          * and "borderY" are used to handle the differences between
581          * old-style packing and the new style (in old-style, iPadX
582          * and iPadY are always zero and padding is completely ignored
583          * except when computing frame size).
584          */
585
586         borderX = slavePtr->padX;
587         borderY = slavePtr->padY;
588         width = slavePtr->winPtr->reqWidth + slavePtr->iPadX;
589         if ((slavePtr->flags & FILLX)
590                 || (width > (frameWidth - borderX))) {
591             width = frameWidth - borderX;
592         }
593         height = slavePtr->winPtr->reqHeight + slavePtr->iPadY;
594         if ((slavePtr->flags & FILLY)
595                 || (height > (frameHeight - borderY))) {
596             height = frameHeight - borderY;
597         }
598         borderX /= 2;
599         borderY /= 2;
600         switch (slavePtr->anchor) {
601             case CK_ANCHOR_N:
602                 x = frameX + (frameWidth - width)/2;
603                 y = frameY + borderY;
604                 break;
605             case CK_ANCHOR_NE:
606                 x = frameX + frameWidth - width - borderX;
607                 y = frameY + borderY;
608                 break;
609             case CK_ANCHOR_E:
610                 x = frameX + frameWidth - width - borderX;
611                 y = frameY + (frameHeight - height)/2;
612                 break;
613             case CK_ANCHOR_SE:
614                 x = frameX + frameWidth - width - borderX;
615                 y = frameY + frameHeight - height - borderY;
616                 break;
617             case CK_ANCHOR_S:
618                 x = frameX + (frameWidth - width)/2;
619                 y = frameY + frameHeight - height - borderY;
620                 break;
621             case CK_ANCHOR_SW:
622                 x = frameX + borderX;
623                 y = frameY + frameHeight - height - borderY;
624                 break;
625             case CK_ANCHOR_W:
626                 x = frameX + borderX;
627                 y = frameY + (frameHeight - height)/2;
628                 break;
629             case CK_ANCHOR_NW:
630                 x = frameX + borderX;
631                 y = frameY + borderY;
632                 break;
633             case CK_ANCHOR_CENTER:
634                 x = frameX + (frameWidth - width)/2;
635                 y = frameY + (frameHeight - height)/2;
636                 break;
637             default:
638                 panic("bad frame factor in ArrangePacking");
639         }
640
641         /*
642          * The final step is to set the position, size, and mapped/unmapped
643          * state of the slave.
644          */
645
646         if (masterPtr->winPtr == slavePtr->winPtr->parentPtr) {
647             if (width <= 0 || height <= 0) {
648                 Ck_UnmapWindow(slavePtr->winPtr);
649             } else {
650                 if (width != slavePtr->winPtr->width ||
651                     height != slavePtr->winPtr->height)
652                         Ck_ResizeWindow(slavePtr->winPtr, width, height);
653                 if (x != slavePtr->winPtr->x || 
654                     y != slavePtr->winPtr->y)
655                     Ck_MoveWindow(slavePtr->winPtr, x, y);
656                 /*
657                 * Temporary kludge til Ck_MoveResizeWindow available !!!
658                 */
659                 if (width != slavePtr->winPtr->width ||
660                     height != slavePtr->winPtr->height)
661                         Ck_ResizeWindow(slavePtr->winPtr, width, height);
662                 if (abort)
663                     goto done;
664
665                 /*
666                 * Don't map the slave if the master isn't mapped: wait
667                 * until the master gets mapped later.
668                 */ 
669
670                 if (masterPtr->winPtr->flags & CK_MAPPED) {
671                     Ck_MapWindow(slavePtr->winPtr);
672                 }
673             }
674         } else {
675             if ((width <= 0) || (height <= 0)) {
676                 Ck_UnmaintainGeometry(slavePtr->winPtr, masterPtr->winPtr);
677                 Ck_UnmapWindow(slavePtr->winPtr);
678             } else {
679                 Ck_MaintainGeometry(slavePtr->winPtr, masterPtr->winPtr,
680                     x, y, width, height);
681             }
682         }
683
684         /*
685          * Changes to the window's structure could cause almost anything
686          * to happen, including deleting the parent or child.  If this
687          * happens, we'll be told to abort.
688          */
689
690         if (abort) {
691             goto done;
692         }
693     }
694
695 done:
696     masterPtr->abortPtr = NULL;
697     Ck_Release((ClientData) masterPtr);
698 }
699 \f
700 /*
701  *----------------------------------------------------------------------
702  *
703  * XExpansion --
704  *
705  *      Given a list of packed slaves, the first of which is packed
706  *      on the left or right and is expandable, compute how much to
707  *      expand the child.
708  *
709  * Results:
710  *      The return value is the number of additional pixels to give to
711  *      the child.
712  *
713  * Side effects:
714  *      None.
715  *
716  *----------------------------------------------------------------------
717  */
718
719 static int
720 XExpansion(slavePtr, cavityWidth)
721     register Packer *slavePtr;          /* First in list of remaining
722                                          * slaves. */
723     int cavityWidth;                    /* Horizontal space left for all
724                                          * remaining slaves. */
725 {
726     int numExpand, minExpand, curExpand;
727     int childWidth;
728
729     /*
730      * This procedure is tricky because windows packed top or bottom can
731      * be interspersed among expandable windows packed left or right.
732      * Scan through the list, keeping a running sum of the widths of
733      * all left and right windows (actually, count the cavity space not
734      * allocated) and a running count of all expandable left and right
735      * windows.  At each top or bottom window, and at the end of the
736      * list, compute the expansion factor that seems reasonable at that
737      * point.  Return the smallest factor seen at any of these points.
738      */
739
740     minExpand = cavityWidth;
741     numExpand = 0;
742     for ( ; slavePtr != NULL; slavePtr = slavePtr->nextPtr) {
743         childWidth = slavePtr->winPtr->reqWidth
744                 + slavePtr->padX + slavePtr->iPadX;
745         if ((slavePtr->side == TOP) || (slavePtr->side == BOTTOM)) {
746             curExpand = (cavityWidth - childWidth)/numExpand;
747             if (curExpand < minExpand) {
748                 minExpand = curExpand;
749             }
750         } else {
751             cavityWidth -= childWidth;
752             if (slavePtr->flags & EXPAND) {
753                 numExpand++;
754             }
755         }
756     }
757     curExpand = cavityWidth/numExpand;
758     if (curExpand < minExpand) {
759         minExpand = curExpand;
760     }
761     return (minExpand < 0) ? 0 : minExpand;
762 }
763 \f
764 /*
765  *----------------------------------------------------------------------
766  *
767  * YExpansion --
768  *
769  *      Given a list of packed slaves, the first of which is packed
770  *      on the top or bottom and is expandable, compute how much to
771  *      expand the child.
772  *
773  * Results:
774  *      The return value is the number of additional pixels to give to
775  *      the child.
776  *
777  * Side effects:
778  *      None.
779  *
780  *----------------------------------------------------------------------
781  */
782
783 static int
784 YExpansion(slavePtr, cavityHeight)
785     register Packer *slavePtr;          /* First in list of remaining
786                                          * slaves. */
787     int cavityHeight;                   /* Vertical space left for all
788                                          * remaining slaves. */
789 {
790     int numExpand, minExpand, curExpand;
791     int childHeight;
792
793     /*
794      * See comments for XExpansion.
795      */
796
797     minExpand = cavityHeight;
798     numExpand = 0;
799     for ( ; slavePtr != NULL; slavePtr = slavePtr->nextPtr) {
800         childHeight = slavePtr->winPtr->reqHeight
801                 + slavePtr->padY + slavePtr->iPadY;
802         if ((slavePtr->side == LEFT) || (slavePtr->side == RIGHT)) {
803             curExpand = (cavityHeight - childHeight)/numExpand;
804             if (curExpand < minExpand) {
805                 minExpand = curExpand;
806             }
807         } else {
808             cavityHeight -= childHeight;
809             if (slavePtr->flags & EXPAND) {
810                 numExpand++;
811             }
812         }
813     }
814     curExpand = cavityHeight/numExpand;
815     if (curExpand < minExpand) {
816         minExpand = curExpand;
817     }
818     return (minExpand < 0) ? 0 : minExpand;
819 }
820 \f
821 /*
822  *--------------------------------------------------------------
823  *
824  * GetPacker --
825  *
826  *      This internal procedure is used to locate a Packer
827  *      structure for a given window, creating one if one
828  *      doesn't exist already.
829  *
830  * Results:
831  *      The return value is a pointer to the Packer structure
832  *      corresponding to tkwin.
833  *
834  * Side effects:
835  *      A new packer structure may be created.  If so, then
836  *      a callback is set up to clean things up when the
837  *      window is deleted.
838  *
839  *--------------------------------------------------------------
840  */
841
842 static Packer *
843 GetPacker(winPtr)
844     CkWindow *winPtr;           /* Pointer to window for which
845                                  * packer structure is desired. */
846 {
847     register Packer *packPtr;
848     Tcl_HashEntry *hPtr;
849     int new;
850
851     if (!initialized) {
852         initialized = 1;
853         Tcl_InitHashTable(&packerHashTable, TCL_ONE_WORD_KEYS);
854     }
855
856     /*
857      * See if there's already packer for this window.  If not,
858      * then create a new one.
859      */
860
861     hPtr = Tcl_CreateHashEntry(&packerHashTable, (char *) winPtr, &new);
862     if (!new) {
863         return (Packer *) Tcl_GetHashValue(hPtr);
864     }
865     packPtr = (Packer *) ckalloc(sizeof (Packer));
866     packPtr->winPtr = winPtr;
867     packPtr->masterPtr = NULL;
868     packPtr->nextPtr = NULL;
869     packPtr->slavePtr = NULL;
870     packPtr->side = TOP;
871     packPtr->anchor = CK_ANCHOR_CENTER;
872     packPtr->padX = packPtr->padY = 0;
873     packPtr->iPadX = packPtr->iPadY = 0;
874     packPtr->abortPtr = NULL;
875     packPtr->flags = 0;
876     Tcl_SetHashValue(hPtr, packPtr);
877     Ck_CreateEventHandler(winPtr,
878         CK_EV_DESTROY | CK_EV_MAP | CK_EV_EXPOSE,
879         PackStructureProc, (ClientData) packPtr);
880     return packPtr;
881 }
882 \f
883 /*
884  *----------------------------------------------------------------------
885  *
886  * Unlink --
887  *
888  *      Remove a packer from its parent's list of slaves.
889  *
890  * Results:
891  *      None.
892  *
893  * Side effects:
894  *      The parent will be scheduled for repacking.
895  *
896  *----------------------------------------------------------------------
897  */
898
899 static void
900 Unlink(packPtr)
901     register Packer *packPtr;           /* Window to unlink. */
902 {
903     register Packer *masterPtr, *packPtr2;
904
905     masterPtr = packPtr->masterPtr;
906     if (masterPtr == NULL) {
907         return;
908     }
909     if (masterPtr->slavePtr == packPtr) {
910         masterPtr->slavePtr = packPtr->nextPtr;
911     } else {
912         for (packPtr2 = masterPtr->slavePtr; ; packPtr2 = packPtr2->nextPtr) {
913             if (packPtr2 == NULL) {
914                 panic("Unlink couldn't find previous window");
915             }
916             if (packPtr2->nextPtr == packPtr) {
917                 packPtr2->nextPtr = packPtr->nextPtr;
918                 break;
919             }
920         }
921     }
922     if (!(masterPtr->flags & REQUESTED_REPACK)) {
923         masterPtr->flags |= REQUESTED_REPACK;
924         Tcl_DoWhenIdle(ArrangePacking, (ClientData) masterPtr);
925     }
926     if (masterPtr->abortPtr != NULL) {
927         *masterPtr->abortPtr = 1;
928     }
929
930     packPtr->masterPtr = NULL;
931 }
932 \f
933 /*
934  *----------------------------------------------------------------------
935  *
936  * DestroyPacker --
937  *
938  *      This procedure is invoked by Ck_EventuallyFree or Ck_Release
939  *      to clean up the internal structure of a packer at a safe time
940  *      (when no-one is using it anymore).
941  *
942  * Results:
943  *      None.
944  *
945  * Side effects:
946  *      Everything associated with the packer is freed up.
947  *
948  *----------------------------------------------------------------------
949  */
950
951 static void
952 DestroyPacker(clientData)
953     ClientData clientData;              /* Info about packed window that
954                                          * is now dead. */
955 {
956     register Packer *packPtr = (Packer *) clientData;
957     ckfree((char *) packPtr);
958 }
959 \f
960 /*
961  *----------------------------------------------------------------------
962  *
963  * PackStructureProc --
964  *
965  *      This procedure is invoked by the event dispatcher in response
966  *      to CK_EV_MAP/CK_EV_EXPOSE/CK_EV_DESTROY events.
967  *
968  * Results:
969  *      None.
970  *
971  * Side effects:
972  *      If a window was just deleted, clean up all its packer-related
973  *      information.  If it was just resized, repack its slaves, if
974  *      any.
975  *
976  *----------------------------------------------------------------------
977  */
978
979 static void
980 PackStructureProc(clientData, eventPtr)
981     ClientData clientData;              /* Our information about window
982                                          * referred to by eventPtr. */
983     CkEvent *eventPtr;                  /* Describes what just happened. */
984 {
985     register Packer *packPtr = (Packer *) clientData;
986
987     if (eventPtr->type == CK_EV_MAP || eventPtr->type == CK_EV_EXPOSE) {
988         if ((packPtr->slavePtr != NULL)
989                 && !(packPtr->flags & REQUESTED_REPACK)) {
990             packPtr->flags |= REQUESTED_REPACK;
991             Tcl_DoWhenIdle(ArrangePacking, (ClientData) packPtr);
992         }
993     } else if (eventPtr->type == CK_EV_DESTROY) {
994         register Packer *slavePtr, *nextPtr;
995
996         if (packPtr->masterPtr != NULL) {
997             Unlink(packPtr);
998         }
999         for (slavePtr = packPtr->slavePtr; slavePtr != NULL;
1000                 slavePtr = nextPtr) {
1001             Ck_ManageGeometry(slavePtr->winPtr, (Ck_GeomMgr *) NULL,
1002                     (ClientData) NULL);
1003             Ck_UnmapWindow(slavePtr->winPtr);
1004             slavePtr->masterPtr = NULL;
1005             nextPtr = slavePtr->nextPtr;
1006             slavePtr->nextPtr = NULL;
1007         }
1008         Tcl_DeleteHashEntry(Tcl_FindHashEntry(&packerHashTable,
1009             (char *) packPtr->winPtr));
1010         if (packPtr->flags & REQUESTED_REPACK) {
1011             Tcl_CancelIdleCall(ArrangePacking, (ClientData) packPtr);
1012         }
1013         packPtr->winPtr = NULL;
1014         Ck_EventuallyFree((ClientData) packPtr, (Ck_FreeProc *) DestroyPacker);
1015     }
1016 }
1017 \f
1018 /*
1019  *----------------------------------------------------------------------
1020  *
1021  * ConfigureSlaves --
1022  *
1023  *      This implements the guts of the "pack configure" command.  Given
1024  *      a list of slaves and configuration options, it arranges for the
1025  *      packer to manage the slaves and sets the specified options.
1026  *
1027  * Results:
1028  *      TCL_OK is returned if all went well.  Otherwise, TCL_ERROR is
1029  *      returned and interp->result is set to contain an error message.
1030  *
1031  * Side effects:
1032  *      Slave windows get taken over by the packer.
1033  *
1034  *----------------------------------------------------------------------
1035  */
1036
1037 static int
1038 ConfigureSlaves(interp, winPtr, argc, argv)
1039     Tcl_Interp *interp;         /* Interpreter for error reporting. */
1040     CkWindow *winPtr;           /* Any window in application containing
1041                                  * slaves.  Used to look up slave names. */
1042     int argc;                   /* Number of elements in argv. */
1043     char *argv[];               /* Argument strings:  contains one or more
1044                                  * window names followed by any number
1045                                  * of "option value" pairs.  Caller must
1046                                  * make sure that there is at least one
1047                                  * window name. */
1048 {
1049     Packer *masterPtr, *slavePtr, *prevPtr, *otherPtr;
1050     CkWindow *other, *slave, *parent, *ancestor;
1051     int i, j, numWindows, c, tmp, positionGiven;
1052     size_t length;
1053
1054     /*
1055      * Find out how many windows are specified.
1056      */
1057
1058     for (numWindows = 0; numWindows < argc; numWindows++) {
1059         if (argv[numWindows][0] != '.') {
1060             break;
1061         }
1062     }
1063
1064     /*
1065      * Iterate over all of the slave windows, parsing the configuration
1066      * options for each slave.  It's a bit wasteful to re-parse the
1067      * options for each slave, but things get too messy if we try to
1068      * parse the arguments just once at the beginning.  For example,
1069      * if a slave already is packed we want to just change a few
1070      * existing values without resetting everything.  If there are
1071      * multiple windows, the -after, -before, and -in options only
1072      * get processed for the first window.
1073      */
1074
1075     masterPtr = NULL;
1076     prevPtr = NULL;
1077     positionGiven = 0;
1078     for (j = 0; j < numWindows; j++) {
1079         slave = Ck_NameToWindow(interp, argv[j], winPtr);
1080         if (slave == NULL) {
1081             return TCL_ERROR;
1082         }
1083         if (slave->flags & CK_TOPLEVEL) {
1084             Tcl_AppendResult(interp, "can't pack \"", argv[j],
1085                     "\": it's a top-level window", (char *) NULL);
1086             return TCL_ERROR;
1087         }
1088         slavePtr = GetPacker(slave);
1089
1090         /*
1091          * If the slave isn't currently packed, reset all of its
1092          * configuration information to default values (there could
1093          * be old values left from a previous packing).
1094          */
1095
1096         if (slavePtr->masterPtr == NULL) {
1097             slavePtr->side = TOP;
1098             slavePtr->anchor = CK_ANCHOR_CENTER;
1099             slavePtr->padX = slavePtr->padY = 0;
1100             slavePtr->iPadX = slavePtr->iPadY = 0;
1101             slavePtr->flags &= ~(FILLX|FILLY|EXPAND);
1102         }
1103
1104         for (i = numWindows; i < argc; i+=2) {
1105             if ((i+2) > argc) {
1106                 Tcl_AppendResult(interp, "extra option \"", argv[i],
1107                         "\" (option with no value?)", (char *) NULL);
1108                 return TCL_ERROR;
1109             }
1110             length = strlen(argv[i]);
1111             if (length < 2) {
1112                 goto badOption;
1113             }
1114             c = argv[i][1];
1115             if ((c == 'a') && (strncmp(argv[i], "-after", length) == 0)
1116                     && (length >= 2)) {
1117                 if (j == 0) {
1118                     other = Ck_NameToWindow(interp, argv[i+1], winPtr);
1119                     if (other == NULL) {
1120                         return TCL_ERROR;
1121                     }
1122                     prevPtr = GetPacker(other);
1123                     if (prevPtr->masterPtr == NULL) {
1124                         notPacked:
1125                         Tcl_AppendResult(interp, "window \"", argv[i+1],
1126                                 "\" isn't packed", (char *) NULL);
1127                         return TCL_ERROR;
1128                     }
1129                     masterPtr = prevPtr->masterPtr;
1130                     positionGiven = 1;
1131                 }
1132             } else if ((c == 'a') && (strncmp(argv[i], "-anchor", length) == 0)
1133                     && (length >= 2)) {
1134                 if (Ck_GetAnchor(interp, argv[i+1], &slavePtr->anchor)
1135                         != TCL_OK) {
1136                     return TCL_ERROR;
1137                 }
1138             } else if ((c == 'b')
1139                     && (strncmp(argv[i], "-before", length) == 0)) {
1140                 if (j == 0) {
1141                     other = Ck_NameToWindow(interp, argv[i+1], winPtr);
1142                     if (other == NULL) {
1143                         return TCL_ERROR;
1144                     }
1145                     otherPtr = GetPacker(other);
1146                     if (otherPtr->masterPtr == NULL) {
1147                         goto notPacked;
1148                     }
1149                     masterPtr = otherPtr->masterPtr;
1150                     prevPtr = masterPtr->slavePtr;
1151                     if (prevPtr == otherPtr) {
1152                         prevPtr = NULL;
1153                     } else {
1154                         while (prevPtr->nextPtr != otherPtr) {
1155                             prevPtr = prevPtr->nextPtr;
1156                         }
1157                     }
1158                     positionGiven = 1;
1159                 }
1160             } else if ((c == 'e')
1161                     && (strncmp(argv[i], "-expand", length) == 0)) {
1162                 if (Tcl_GetBoolean(interp, argv[i+1], &tmp) != TCL_OK) {
1163                     return TCL_ERROR;
1164                 }
1165                 slavePtr->flags &= ~EXPAND;
1166                 if (tmp) {
1167                     slavePtr->flags |= EXPAND;
1168                 }
1169             } else if ((c == 'f') && (strncmp(argv[i], "-fill", length) == 0)) {
1170                 if (strcmp(argv[i+1], "none") == 0) {
1171                     slavePtr->flags &= ~(FILLX|FILLY);
1172                 } else if (strcmp(argv[i+1], "x") == 0) {
1173                     slavePtr->flags = (slavePtr->flags & ~FILLY) | FILLX;
1174                 } else if (strcmp(argv[i+1], "y") == 0) {
1175                     slavePtr->flags = (slavePtr->flags & ~FILLX) | FILLY;
1176                 } else if (strcmp(argv[i+1], "both") == 0) {
1177                     slavePtr->flags |= FILLX|FILLY;
1178                 } else {
1179                     Tcl_AppendResult(interp, "bad fill style \"", argv[i+1],
1180                             "\": must be none, x, y, or both", (char *) NULL);
1181                     return TCL_ERROR;
1182                 }
1183             } else if ((c == 'i') && (strcmp(argv[i], "-in") == 0)) {
1184                 if (j == 0) {
1185                     other = Ck_NameToWindow(interp, argv[i+1], winPtr);
1186                     if (other == NULL) {
1187                         return TCL_ERROR;
1188                     }
1189                     masterPtr = GetPacker(other);
1190                     prevPtr = masterPtr->slavePtr;
1191                     if (prevPtr != NULL) {
1192                         while (prevPtr->nextPtr != NULL) {
1193                             prevPtr = prevPtr->nextPtr;
1194                         }
1195                     }
1196                     positionGiven = 1;
1197                 }
1198             } else if ((c == 'i') && (strcmp(argv[i], "-ipadx") == 0)) {
1199                 if ((Ck_GetCoord(interp, slave, argv[i+1], &tmp) != TCL_OK)
1200                         || (tmp < 0)) {
1201                     badPad:
1202                     Tcl_ResetResult(interp);
1203                     Tcl_AppendResult(interp, "bad pad value \"", argv[i+1],
1204                             "\": must be positive screen distance",
1205                             (char *) NULL);
1206                     return TCL_ERROR;
1207                 }
1208                 slavePtr->iPadX = tmp*2;
1209             } else if ((c == 'i') && (strcmp(argv[i], "-ipady") == 0)) {
1210                 if ((Ck_GetCoord(interp, slave, argv[i+1], &tmp) != TCL_OK)
1211                         || (tmp< 0)) {
1212                     goto badPad;
1213                 }
1214                 slavePtr->iPadY = tmp*2;
1215             } else if ((c == 'p') && (strcmp(argv[i], "-padx") == 0)) {
1216                 if ((Ck_GetCoord(interp, slave, argv[i+1], &tmp) != TCL_OK)
1217                         || (tmp< 0)) {
1218                     goto badPad;
1219                 }
1220                 slavePtr->padX = tmp*2;
1221             } else if ((c == 'p') && (strcmp(argv[i], "-pady") == 0)) {
1222                 if ((Ck_GetCoord(interp, slave, argv[i+1], &tmp) != TCL_OK)
1223                         || (tmp< 0)) {
1224                     goto badPad;
1225                 }
1226                 slavePtr->padY = tmp*2;
1227             } else if ((c == 's') && (strncmp(argv[i], "-side", length) == 0)) {
1228                 c = argv[i+1][0];
1229                 if ((c == 't') && (strcmp(argv[i+1], "top") == 0)) {
1230                     slavePtr->side = TOP;
1231                 } else if ((c == 'b') && (strcmp(argv[i+1], "bottom") == 0)) {
1232                     slavePtr->side = BOTTOM;
1233                 } else if ((c == 'l') && (strcmp(argv[i+1], "left") == 0)) {
1234                     slavePtr->side = LEFT;
1235                 } else if ((c == 'r') && (strcmp(argv[i+1], "right") == 0)) {
1236                     slavePtr->side = RIGHT;
1237                 } else {
1238                     Tcl_AppendResult(interp, "bad side \"", argv[i+1],
1239                             "\": must be top, bottom, left, or right",
1240                             (char *) NULL);
1241                     return TCL_ERROR;
1242                 }
1243             } else {
1244                 badOption:
1245                 Tcl_AppendResult(interp, "unknown or ambiguous option \"",
1246                         argv[i], "\": must be -after, -anchor, -before, ",
1247                         "-expand, -fill, -in, -ipadx, -ipady, -padx, ",
1248                         "-pady, or -side", (char *) NULL);
1249                 return TCL_ERROR;
1250             }
1251         }
1252
1253         /*
1254          * If no position in a packing list was specified and the slave
1255          * is already packed, then leave it in its current location in
1256          * its current packing list.
1257          */
1258
1259         if (!positionGiven && (slavePtr->masterPtr != NULL)) {
1260             masterPtr = slavePtr->masterPtr;
1261             goto scheduleLayout;
1262         }
1263
1264         /*
1265          * If the slave is going to be put back after itself then
1266          * skip the whole operation, since it won't work anyway.
1267          */
1268
1269         if (prevPtr == slavePtr) {
1270             masterPtr = slavePtr->masterPtr;
1271             goto scheduleLayout;
1272         }
1273     
1274         /*
1275          * If none of the "-before", or "-after" options has
1276          * been specified, arrange for the slave to go at the end of
1277          * the order for its parent.
1278          */
1279     
1280         if (!positionGiven) {
1281             masterPtr = GetPacker(slave->parentPtr);
1282             prevPtr = masterPtr->slavePtr;
1283             if (prevPtr != NULL) {
1284                 while (prevPtr->nextPtr != NULL) {
1285                     prevPtr = prevPtr->nextPtr;
1286                 }
1287             }
1288         }
1289
1290         /*
1291          * Make sure that the slave's parent is either the master or
1292          * an ancestor of the master.
1293          */
1294
1295         parent = slave->parentPtr;
1296         for (ancestor = masterPtr->winPtr; ;
1297                 ancestor = ancestor->parentPtr) {
1298             if (ancestor == parent) {
1299                 break;
1300             }
1301             if (ancestor->flags & CK_TOPLEVEL) {
1302                 Tcl_AppendResult(interp, "can't pack ", argv[j],
1303                     " inside ", masterPtr->winPtr->pathName,
1304                     (char *) NULL);
1305                 return TCL_ERROR;
1306             }
1307         }
1308         if (slave == masterPtr->winPtr) {
1309                 Tcl_AppendResult(interp, "can't pack ", argv[j],
1310                     " inside itself", (char *) NULL);
1311             return TCL_ERROR;
1312         }
1313         
1314         /*
1315          * Unpack the slave if it's currently packed, then position it
1316          * after prevPtr.
1317          */
1318
1319         if (slavePtr->masterPtr != NULL) {
1320             if ((slavePtr->masterPtr != masterPtr) &&
1321                     (slavePtr->masterPtr->winPtr
1322                     != slavePtr->winPtr->parentPtr)) {
1323                 Ck_UnmaintainGeometry(slavePtr->winPtr,
1324                         slavePtr->masterPtr->winPtr);
1325             }
1326             Unlink(slavePtr);
1327         }
1328         slavePtr->masterPtr = masterPtr;
1329         if (prevPtr == NULL) {
1330             slavePtr->nextPtr = masterPtr->slavePtr;
1331             masterPtr->slavePtr = slavePtr;
1332         } else {
1333             slavePtr->nextPtr = prevPtr->nextPtr;
1334             prevPtr->nextPtr = slavePtr;
1335         }
1336         Ck_ManageGeometry(slave, &packerType, (ClientData) slavePtr);
1337         prevPtr = slavePtr;
1338
1339         /*
1340          * Arrange for the parent to be re-packed at the first
1341          * idle moment.
1342          */
1343
1344         scheduleLayout:
1345         if (masterPtr->abortPtr != NULL) {
1346             *masterPtr->abortPtr = 1;
1347         }
1348         if (!(masterPtr->flags & REQUESTED_REPACK)) {
1349             masterPtr->flags |= REQUESTED_REPACK;
1350             Tcl_DoWhenIdle(ArrangePacking, (ClientData) masterPtr);
1351         }
1352     }
1353     return TCL_OK;
1354 }