OSDN Git Service

Initial revision
[pf3gnuchains/pf3gnuchains3x.git] / tk / generic / tkPack.c
1 /* 
2  * tkPack.c --
3  *
4  *      This file contains code to implement the "packer"
5  *      geometry manager for Tk.
6  *
7  * Copyright (c) 1990-1994 The Regents of the University of California.
8  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
9  *
10  * See the file "license.terms" for information on usage and redistribution
11  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
12  *
13  * RCS: @(#) $Id$
14  */
15
16 #include "tkPort.h"
17 #include "tkInt.h"
18
19 typedef enum {TOP, BOTTOM, LEFT, RIGHT} Side;
20
21 /* For each window that the packer cares about (either because
22  * the window is managed by the packer or because the window
23  * has slaves that are managed by the packer), there is a
24  * structure of the following type:
25  */
26
27 typedef struct Packer {
28     Tk_Window tkwin;            /* Tk token for window.  NULL means that
29                                  * the window has been deleted, but the
30                                  * packet hasn't had a chance to clean up
31                                  * yet because the structure is still in
32                                  * use. */
33     struct Packer *masterPtr;   /* Master window within which this window
34                                  * is packed (NULL means this window
35                                  * isn't managed by the packer). */
36     struct Packer *nextPtr;     /* Next window packed within same
37                                  * parent.  List is priority-ordered:
38                                  * first on list gets packed first. */
39     struct Packer *slavePtr;    /* First in list of slaves packed
40                                  * inside this window (NULL means
41                                  * no packed slaves). */
42     Side side;                  /* Side of parent against which
43                                  * this window is packed. */
44     Tk_Anchor anchor;           /* If frame allocated for window is larger
45                                  * than window needs, this indicates how
46                                  * where to position window in frame. */
47     int padX, padY;             /* Total additional pixels to leave around the
48                                  * window (half of this space is left on each
49                                  * side).  This is space *outside* the window:
50                                  * we'll allocate extra space in frame but
51                                  * won't enlarge window). */
52     int iPadX, iPadY;           /* Total extra pixels to allocate inside the
53                                  * window (half this amount will appear on
54                                  * each side). */
55     int doubleBw;               /* Twice the window's last known border
56                                  * width.  If this changes, the window
57                                  * must be repacked within its parent. */
58     int *abortPtr;              /* If non-NULL, it means that there is a nested
59                                  * call to ArrangePacking already working on
60                                  * this window.  *abortPtr may be set to 1 to
61                                  * abort that nested call.  This happens, for
62                                  * example, if tkwin or any of its slaves
63                                  * is deleted. */
64     int flags;                  /* Miscellaneous flags;  see below
65                                  * for definitions. */
66 } Packer;
67
68 /*
69  * Flag values for Packer structures:
70  *
71  * REQUESTED_REPACK:            1 means a Tcl_DoWhenIdle request
72  *                              has already been made to repack
73  *                              all the slaves of this window.
74  * FILLX:                       1 means if frame allocated for window
75  *                              is wider than window needs, expand window
76  *                              to fill frame.  0 means don't make window
77  *                              any larger than needed.
78  * FILLY:                       Same as FILLX, except for height.
79  * EXPAND:                      1 means this window's frame will absorb any
80  *                              extra space in the parent window.
81  * OLD_STYLE:                   1 means this window is being managed with
82  *                              the old-style packer algorithms (before
83  *                              Tk version 3.3).  The main difference is
84  *                              that padding and filling are done differently.
85  * DONT_PROPAGATE:              1 means don't set this window's requested
86  *                              size.  0 means if this window is a master
87  *                              then Tk will set its requested size to fit
88  *                              the needs of its slaves.
89  */
90
91 #define REQUESTED_REPACK        1
92 #define FILLX                   2
93 #define FILLY                   4
94 #define EXPAND                  8
95 #define OLD_STYLE               16
96 #define DONT_PROPAGATE          32
97
98 /*
99  * Hash table used to map from Tk_Window tokens to corresponding
100  * Packer structures:
101  */
102
103 static Tcl_HashTable packerHashTable;
104
105 /*
106  * Have statics in this module been initialized?
107  */
108
109 static int initialized = 0;
110
111 /*
112  * The following structure is the official type record for the
113  * packer:
114  */
115
116 static void             PackReqProc _ANSI_ARGS_((ClientData clientData,
117                             Tk_Window tkwin));
118 static void             PackLostSlaveProc _ANSI_ARGS_((ClientData clientData,
119                             Tk_Window tkwin));
120
121 static Tk_GeomMgr packerType = {
122     "pack",                     /* name */
123     PackReqProc,                /* requestProc */
124     PackLostSlaveProc,          /* lostSlaveProc */
125 };
126
127 /*
128  * Forward declarations for procedures defined later in this file:
129  */
130
131 static void             ArrangePacking _ANSI_ARGS_((ClientData clientData));
132 static int              ConfigureSlaves _ANSI_ARGS_((Tcl_Interp *interp,
133                             Tk_Window tkwin, int argc, char *argv[]));
134 static void             DestroyPacker _ANSI_ARGS_((char *memPtr));
135 static Packer *         GetPacker _ANSI_ARGS_((Tk_Window tkwin));
136 static int              PackAfter _ANSI_ARGS_((Tcl_Interp *interp,
137                             Packer *prevPtr, Packer *masterPtr, int argc,
138                             char **argv));
139 static void             PackReqProc _ANSI_ARGS_((ClientData clientData,
140                             Tk_Window tkwin));
141 static void             PackStructureProc _ANSI_ARGS_((ClientData clientData,
142                             XEvent *eventPtr));
143 static void             Unlink _ANSI_ARGS_((Packer *packPtr));
144 static int              XExpansion _ANSI_ARGS_((Packer *slavePtr,
145                             int cavityWidth));
146 static int              YExpansion _ANSI_ARGS_((Packer *slavePtr,
147                             int cavityHeight));
148 \f
149 /*
150  *--------------------------------------------------------------
151  *
152  * Tk_PackCmd --
153  *
154  *      This procedure is invoked to process the "pack" Tcl command.
155  *      See the user documentation for details on what it does.
156  *
157  * Results:
158  *      A standard Tcl result.
159  *
160  * Side effects:
161  *      See the user documentation.
162  *
163  *--------------------------------------------------------------
164  */
165
166 int
167 Tk_PackCmd(clientData, interp, argc, argv)
168     ClientData clientData;      /* Main window associated with
169                                  * interpreter. */
170     Tcl_Interp *interp;         /* Current interpreter. */
171     int argc;                   /* Number of arguments. */
172     char **argv;                /* Argument strings. */
173 {
174     Tk_Window tkwin = (Tk_Window) clientData;
175     size_t length;
176     int c;
177
178     if ((argc >= 2) && (argv[1][0] == '.')) {
179         return ConfigureSlaves(interp, tkwin, argc-1, argv+1);
180     }
181     if (argc < 3) {
182         Tcl_AppendResult(interp, "wrong # args: should be \"",
183                 argv[0], " option arg ?arg ...?\"", (char *) NULL);
184         return TCL_ERROR;
185     }
186     c = argv[1][0];
187     length = strlen(argv[1]);
188     if ((c == 'a') && (length >= 2)
189             && (strncmp(argv[1], "after", length) == 0)) {
190         Packer *prevPtr;
191         Tk_Window tkwin2;
192
193         tkwin2 = Tk_NameToWindow(interp, argv[2], tkwin);
194         if (tkwin2 == NULL) {
195             return TCL_ERROR;
196         }
197         prevPtr = GetPacker(tkwin2);
198         if (prevPtr->masterPtr == NULL) {
199             Tcl_AppendResult(interp, "window \"", argv[2],
200                     "\" isn't packed", (char *) NULL);
201             return TCL_ERROR;
202         }
203         return PackAfter(interp, prevPtr, prevPtr->masterPtr, argc-3, argv+3);
204     } else if ((c == 'a') && (length >= 2)
205             && (strncmp(argv[1], "append", length) == 0)) {
206         Packer *masterPtr;
207         register Packer *prevPtr;
208         Tk_Window tkwin2;
209
210         tkwin2 = Tk_NameToWindow(interp, argv[2], tkwin);
211         if (tkwin2 == NULL) {
212             return TCL_ERROR;
213         }
214         masterPtr = GetPacker(tkwin2);
215         prevPtr = masterPtr->slavePtr;
216         if (prevPtr != NULL) {
217             while (prevPtr->nextPtr != NULL) {
218                 prevPtr = prevPtr->nextPtr;
219             }
220         }
221         return PackAfter(interp, prevPtr, masterPtr, argc-3, argv+3);
222     } else if ((c == 'b') && (strncmp(argv[1], "before", length) == 0)) {
223         Packer *packPtr, *masterPtr;
224         register Packer *prevPtr;
225         Tk_Window tkwin2;
226
227         tkwin2 = Tk_NameToWindow(interp, argv[2], tkwin);
228         if (tkwin2 == NULL) {
229             return TCL_ERROR;
230         }
231         packPtr = GetPacker(tkwin2);
232         if (packPtr->masterPtr == NULL) {
233             Tcl_AppendResult(interp, "window \"", argv[2],
234                     "\" isn't packed", (char *) NULL);
235             return TCL_ERROR;
236         }
237         masterPtr = packPtr->masterPtr;
238         prevPtr = masterPtr->slavePtr;
239         if (prevPtr == packPtr) {
240             prevPtr = NULL;
241         } else {
242             for ( ; ; prevPtr = prevPtr->nextPtr) {
243                 if (prevPtr == NULL) {
244                     panic("\"pack before\" couldn't find predecessor");
245                 }
246                 if (prevPtr->nextPtr == packPtr) {
247                     break;
248                 }
249             }
250         }
251         return PackAfter(interp, prevPtr, masterPtr, argc-3, argv+3);
252     } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) {
253         if (argv[2][0] != '.') {
254             Tcl_AppendResult(interp, "bad argument \"", argv[2],
255                     "\": must be name of window", (char *) NULL);
256             return TCL_ERROR;
257         }
258         return ConfigureSlaves(interp, tkwin, argc-2, argv+2);
259     } else if ((c == 'f') && (strncmp(argv[1], "forget", length) == 0)) {
260         Tk_Window slave;
261         Packer *slavePtr;
262         int i;
263
264         for (i = 2; i < argc; i++) {
265             slave = Tk_NameToWindow(interp, argv[i], tkwin);
266             if (slave == NULL) {
267                 continue;
268             }
269             slavePtr = GetPacker(slave);
270             if ((slavePtr != NULL) && (slavePtr->masterPtr != NULL)) {
271                 Tk_ManageGeometry(slave, (Tk_GeomMgr *) NULL,
272                         (ClientData) NULL);
273                 if (slavePtr->masterPtr->tkwin != Tk_Parent(slavePtr->tkwin)) {
274                     Tk_UnmaintainGeometry(slavePtr->tkwin,
275                             slavePtr->masterPtr->tkwin);
276                 }
277                 Unlink(slavePtr);
278                 Tk_UnmapWindow(slavePtr->tkwin);
279             }
280         }
281     } else if ((c == 'i') && (strncmp(argv[1], "info", length) == 0)) {
282         register Packer *slavePtr;
283         Tk_Window slave;
284         char buffer[300];
285         static char *sideNames[] = {"top", "bottom", "left", "right"};
286
287         if (argc != 3) {
288             Tcl_AppendResult(interp, "wrong # args: should be \"",
289                     argv[0], " info window\"", (char *) NULL);
290             return TCL_ERROR;
291         }
292         slave = Tk_NameToWindow(interp, argv[2], tkwin);
293         if (slave == NULL) {
294             return TCL_ERROR;
295         }
296         slavePtr = GetPacker(slave);
297         if (slavePtr->masterPtr == NULL) {
298             Tcl_AppendResult(interp, "window \"", argv[2],
299                     "\" isn't packed", (char *) NULL);
300             return TCL_ERROR;
301         }
302         Tcl_AppendElement(interp, "-in");
303         Tcl_AppendElement(interp, Tk_PathName(slavePtr->masterPtr->tkwin));
304         Tcl_AppendElement(interp, "-anchor");
305         Tcl_AppendElement(interp, Tk_NameOfAnchor(slavePtr->anchor));
306         Tcl_AppendResult(interp, " -expand ",
307                 (slavePtr->flags & EXPAND) ? "1" : "0", " -fill ",
308                 (char *) NULL);
309         switch (slavePtr->flags & (FILLX|FILLY)) {
310             case 0:
311                 Tcl_AppendResult(interp, "none", (char *) NULL);
312                 break;
313             case FILLX:
314                 Tcl_AppendResult(interp, "x", (char *) NULL);
315                 break;
316             case FILLY:
317                 Tcl_AppendResult(interp, "y", (char *) NULL);
318                 break;
319             case FILLX|FILLY:
320                 Tcl_AppendResult(interp, "both", (char *) NULL);
321                 break;
322         }
323         sprintf(buffer, " -ipadx %d -ipady %d -padx %d -pady %d",
324                 slavePtr->iPadX/2, slavePtr->iPadY/2, slavePtr->padX/2,
325                 slavePtr->padY/2);
326         Tcl_AppendResult(interp, buffer, " -side ", sideNames[slavePtr->side],
327                 (char *) NULL);
328     } else if ((c == 'p') && (strncmp(argv[1], "propagate", length) == 0)) {
329         Tk_Window master;
330         Packer *masterPtr;
331         int propagate;
332
333         if (argc > 4) {
334             Tcl_AppendResult(interp, "wrong # args: should be \"",
335                     argv[0], " propagate window ?boolean?\"", (char *) NULL);
336             return TCL_ERROR;
337         }
338         master = Tk_NameToWindow(interp, argv[2], tkwin);
339         if (master == NULL) {
340             return TCL_ERROR;
341         }
342         masterPtr = GetPacker(master);
343         if (argc == 3) {
344             if (masterPtr->flags & DONT_PROPAGATE) {
345                 interp->result = "0";
346             } else {
347                 interp->result = "1";
348             }
349             return TCL_OK;
350         }
351         if (Tcl_GetBoolean(interp, argv[3], &propagate) != TCL_OK) {
352             return TCL_ERROR;
353         }
354         if (propagate) {
355             masterPtr->flags &= ~DONT_PROPAGATE;
356
357             /*
358              * Repack the master to allow new geometry information to
359              * propagate upwards to the master's master.
360              */
361
362             if (masterPtr->abortPtr != NULL) {
363                 *masterPtr->abortPtr = 1;
364             }
365             if (!(masterPtr->flags & REQUESTED_REPACK)) {
366                 masterPtr->flags |= REQUESTED_REPACK;
367                 Tcl_DoWhenIdle(ArrangePacking, (ClientData) masterPtr);
368             }
369         } else {
370             masterPtr->flags |= DONT_PROPAGATE;
371         }
372     } else if ((c == 's') && (strncmp(argv[1], "slaves", length) == 0)) {
373         Tk_Window master;
374         Packer *masterPtr, *slavePtr;
375
376         if (argc != 3) {
377             Tcl_AppendResult(interp, "wrong # args: should be \"",
378                     argv[0], " slaves window\"", (char *) NULL);
379             return TCL_ERROR;
380         }
381         master = Tk_NameToWindow(interp, argv[2], tkwin);
382         if (master == NULL) {
383             return TCL_ERROR;
384         }
385         masterPtr = GetPacker(master);
386         for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
387                 slavePtr = slavePtr->nextPtr) {
388             Tcl_AppendElement(interp, Tk_PathName(slavePtr->tkwin));
389         }
390     } else if ((c == 'u') && (strncmp(argv[1], "unpack", length) == 0)) {
391         Tk_Window tkwin2;
392         Packer *packPtr;
393
394         if (argc != 3) {
395             Tcl_AppendResult(interp, "wrong # args: should be \"",
396                     argv[0], " unpack window\"", (char *) NULL);
397             return TCL_ERROR;
398         }
399         tkwin2 = Tk_NameToWindow(interp, argv[2], tkwin);
400         if (tkwin2 == NULL) {
401             return TCL_ERROR;
402         }
403         packPtr = GetPacker(tkwin2);
404         if ((packPtr != NULL) && (packPtr->masterPtr != NULL)) {
405             Tk_ManageGeometry(tkwin2, (Tk_GeomMgr *) NULL,
406                     (ClientData) NULL);
407             if (packPtr->masterPtr->tkwin != Tk_Parent(packPtr->tkwin)) {
408                 Tk_UnmaintainGeometry(packPtr->tkwin,
409                         packPtr->masterPtr->tkwin);
410             }
411             Unlink(packPtr);
412             Tk_UnmapWindow(packPtr->tkwin);
413         }
414     } else {
415         Tcl_AppendResult(interp, "bad option \"", argv[1],
416                 "\": must be configure, forget, info, ",
417                 "propagate, or slaves", (char *) NULL);
418         return TCL_ERROR;
419     }
420     return TCL_OK;
421 }
422 \f
423 /*
424  *--------------------------------------------------------------
425  *
426  * PackReqProc --
427  *
428  *      This procedure is invoked by Tk_GeometryRequest for
429  *      windows managed by the packer.
430  *
431  * Results:
432  *      None.
433  *
434  * Side effects:
435  *      Arranges for tkwin, and all its managed siblings, to
436  *      be re-packed at the next idle point.
437  *
438  *--------------------------------------------------------------
439  */
440
441         /* ARGSUSED */
442 static void
443 PackReqProc(clientData, tkwin)
444     ClientData clientData;      /* Packer's information about
445                                  * window that got new preferred
446                                  * geometry.  */
447     Tk_Window tkwin;            /* Other Tk-related information
448                                  * about the window. */
449 {
450     register Packer *packPtr = (Packer *) clientData;
451
452     packPtr = packPtr->masterPtr;
453     if (!(packPtr->flags & REQUESTED_REPACK)) {
454         packPtr->flags |= REQUESTED_REPACK;
455         Tcl_DoWhenIdle(ArrangePacking, (ClientData) packPtr);
456     }
457 }
458 \f
459 /*
460  *--------------------------------------------------------------
461  *
462  * PackLostSlaveProc --
463  *
464  *      This procedure is invoked by Tk whenever some other geometry
465  *      claims control over a slave that used to be managed by us.
466  *
467  * Results:
468  *      None.
469  *
470  * Side effects:
471  *      Forgets all packer-related information about the slave.
472  *
473  *--------------------------------------------------------------
474  */
475
476         /* ARGSUSED */
477 static void
478 PackLostSlaveProc(clientData, tkwin)
479     ClientData clientData;      /* Packer structure for slave window that
480                                  * was stolen away. */
481     Tk_Window tkwin;            /* Tk's handle for the slave window. */
482 {
483     register Packer *slavePtr = (Packer *) clientData;
484
485     if (slavePtr->masterPtr->tkwin != Tk_Parent(slavePtr->tkwin)) {
486         Tk_UnmaintainGeometry(slavePtr->tkwin, slavePtr->masterPtr->tkwin);
487     }
488     Unlink(slavePtr);
489     Tk_UnmapWindow(slavePtr->tkwin);
490 }
491 \f
492 /*
493  *--------------------------------------------------------------
494  *
495  * ArrangePacking --
496  *
497  *      This procedure is invoked (using the Tcl_DoWhenIdle
498  *      mechanism) to re-layout a set of windows managed by
499  *      the packer.  It is invoked at idle time so that a
500  *      series of packer requests can be merged into a single
501  *      layout operation.
502  *
503  * Results:
504  *      None.
505  *
506  * Side effects:
507  *      The packed slaves of masterPtr may get resized or
508  *      moved.
509  *
510  *--------------------------------------------------------------
511  */
512
513 static void
514 ArrangePacking(clientData)
515     ClientData clientData;      /* Structure describing parent whose slaves
516                                  * are to be re-layed out. */
517 {
518     register Packer *masterPtr = (Packer *) clientData;
519     register Packer *slavePtr;  
520     int cavityX, cavityY, cavityWidth, cavityHeight;
521                                 /* These variables keep track of the
522                                  * as-yet-unallocated space remaining in
523                                  * the middle of the parent window. */
524     int frameX, frameY, frameWidth, frameHeight;
525                                 /* These variables keep track of the frame
526                                  * allocated to the current window. */
527     int x, y, width, height;    /* These variables are used to hold the
528                                  * actual geometry of the current window. */
529     int intBWidth;              /* Width of internal border in parent window,
530                                  * if any. */
531     int abort;                  /* May get set to non-zero to abort this
532                                  * repacking operation. */
533     int borderX, borderY;
534     int maxWidth, maxHeight, tmp;
535
536     masterPtr->flags &= ~REQUESTED_REPACK;
537
538     /*
539      * If the parent has no slaves anymore, then don't do anything
540      * at all:  just leave the parent's size as-is.
541      */
542
543     if (masterPtr->slavePtr == NULL) {
544         return;
545     }
546
547     /*
548      * Abort any nested call to ArrangePacking for this window, since
549      * we'll do everything necessary here, and set up so this call
550      * can be aborted if necessary.  
551      */
552
553     if (masterPtr->abortPtr != NULL) {
554         *masterPtr->abortPtr = 1;
555     }
556     masterPtr->abortPtr = &abort;
557     abort = 0;
558     Tcl_Preserve((ClientData) masterPtr);
559
560     /*
561      * Pass #1: scan all the slaves to figure out the total amount
562      * of space needed.  Two separate width and height values are
563      * computed:
564      *
565      * width -          Holds the sum of the widths (plus padding) of
566      *                  all the slaves seen so far that were packed LEFT
567      *                  or RIGHT.
568      * height -         Holds the sum of the heights (plus padding) of
569      *                  all the slaves seen so far that were packed TOP
570      *                  or BOTTOM.
571      *
572      * maxWidth -       Gradually builds up the width needed by the master
573      *                  to just barely satisfy all the slave's needs.  For
574      *                  each slave, the code computes the width needed for
575      *                  all the slaves so far and updates maxWidth if the
576      *                  new value is greater.
577      * maxHeight -      Same as maxWidth, except keeps height info.
578      */
579
580     intBWidth = Tk_InternalBorderWidth(masterPtr->tkwin);
581     width = height = maxWidth = maxHeight = 2*intBWidth;
582     for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
583             slavePtr = slavePtr->nextPtr) {
584         if ((slavePtr->side == TOP) || (slavePtr->side == BOTTOM)) {
585             tmp = Tk_ReqWidth(slavePtr->tkwin) + slavePtr->doubleBw
586                     + slavePtr->padX + slavePtr->iPadX + width;
587             if (tmp > maxWidth) {
588                 maxWidth = tmp;
589             }
590             height += Tk_ReqHeight(slavePtr->tkwin) + slavePtr->doubleBw
591                     + slavePtr->padY + slavePtr->iPadY;
592         } else {
593             tmp = Tk_ReqHeight(slavePtr->tkwin) + slavePtr->doubleBw
594                     + slavePtr->padY + slavePtr->iPadY + height;
595             if (tmp > maxHeight) {
596                 maxHeight = tmp;
597             }
598             width += Tk_ReqWidth(slavePtr->tkwin) + slavePtr->doubleBw
599                     + slavePtr->padX + slavePtr->iPadX;
600         }
601     }
602     if (width > maxWidth) {
603         maxWidth = width;
604     }
605     if (height > maxHeight) {
606         maxHeight = height;
607     }
608
609     /*
610      * If the total amount of space needed in the parent window has
611      * changed, and if we're propagating geometry information, then
612      * notify the next geometry manager up and requeue ourselves to
613      * start again after the parent has had a chance to
614      * resize us.
615      */
616
617     if (((maxWidth != Tk_ReqWidth(masterPtr->tkwin))
618             || (maxHeight != Tk_ReqHeight(masterPtr->tkwin)))
619             && !(masterPtr->flags & DONT_PROPAGATE)) {
620         Tk_GeometryRequest(masterPtr->tkwin, maxWidth, maxHeight);
621         masterPtr->flags |= REQUESTED_REPACK;
622         Tcl_DoWhenIdle(ArrangePacking, (ClientData) masterPtr);
623         goto done;
624     }
625
626     /*
627      * Pass #2: scan the slaves a second time assigning
628      * new sizes.  The "cavity" variables keep track of the
629      * unclaimed space in the cavity of the window;  this
630      * shrinks inward as we allocate windows around the
631      * edges.  The "frame" variables keep track of the space
632      * allocated to the current window and its frame.  The
633      * current window is then placed somewhere inside the
634      * frame, depending on anchor.
635      */
636
637     cavityX = cavityY = x = y = intBWidth;
638     cavityWidth = Tk_Width(masterPtr->tkwin) - 2*intBWidth;
639     cavityHeight = Tk_Height(masterPtr->tkwin) - 2*intBWidth;
640     for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
641             slavePtr = slavePtr->nextPtr) {
642         if ((slavePtr->side == TOP) || (slavePtr->side == BOTTOM)) {
643             frameWidth = cavityWidth;
644             frameHeight = Tk_ReqHeight(slavePtr->tkwin) + slavePtr->doubleBw
645                     + slavePtr->padY + slavePtr->iPadY;
646             if (slavePtr->flags & EXPAND) {
647                 frameHeight += YExpansion(slavePtr, cavityHeight);
648             }
649             cavityHeight -= frameHeight;
650             if (cavityHeight < 0) {
651                 frameHeight += cavityHeight;
652                 cavityHeight = 0;
653             }
654             frameX = cavityX;
655             if (slavePtr->side == TOP) {
656                 frameY = cavityY;
657                 cavityY += frameHeight;
658             } else {
659                 frameY = cavityY + cavityHeight;
660             }
661         } else {
662             frameHeight = cavityHeight;
663             frameWidth = Tk_ReqWidth(slavePtr->tkwin) + slavePtr->doubleBw
664                     + slavePtr->padX + slavePtr->iPadX;
665             if (slavePtr->flags & EXPAND) {
666                 frameWidth += XExpansion(slavePtr, cavityWidth);
667             }
668             cavityWidth -= frameWidth;
669             if (cavityWidth < 0) {
670                 frameWidth += cavityWidth;
671                 cavityWidth = 0;
672             }
673             frameY = cavityY;
674             if (slavePtr->side == LEFT) {
675                 frameX = cavityX;
676                 cavityX += frameWidth;
677             } else {
678                 frameX = cavityX + cavityWidth;
679             }
680         }
681
682         /*
683          * Now that we've got the size of the frame for the window,
684          * compute the window's actual size and location using the
685          * fill, padding, and frame factors.  The variables "borderX"
686          * and "borderY" are used to handle the differences between
687          * old-style packing and the new style (in old-style, iPadX
688          * and iPadY are always zero and padding is completely ignored
689          * except when computing frame size).
690          */
691
692         if (slavePtr->flags & OLD_STYLE) {
693             borderX = borderY = 0;
694         } else {
695             borderX = slavePtr->padX;
696             borderY = slavePtr->padY;
697         }
698         width = Tk_ReqWidth(slavePtr->tkwin) + slavePtr->doubleBw
699                 + slavePtr->iPadX;
700         if ((slavePtr->flags & FILLX)
701                 || (width > (frameWidth - borderX))) {
702             width = frameWidth - borderX;
703         }
704         height = Tk_ReqHeight(slavePtr->tkwin) + slavePtr->doubleBw
705                 + slavePtr->iPadY;
706         if ((slavePtr->flags & FILLY)
707                 || (height > (frameHeight - borderY))) {
708             height = frameHeight - borderY;
709         }
710         borderX /= 2;
711         borderY /= 2;
712         switch (slavePtr->anchor) {
713             case TK_ANCHOR_N:
714                 x = frameX + (frameWidth - width)/2;
715                 y = frameY + borderY;
716                 break;
717             case TK_ANCHOR_NE:
718                 x = frameX + frameWidth - width - borderX;
719                 y = frameY + borderY;
720                 break;
721             case TK_ANCHOR_E:
722                 x = frameX + frameWidth - width - borderX;
723                 y = frameY + (frameHeight - height)/2;
724                 break;
725             case TK_ANCHOR_SE:
726                 x = frameX + frameWidth - width - borderX;
727                 y = frameY + frameHeight - height - borderY;
728                 break;
729             case TK_ANCHOR_S:
730                 x = frameX + (frameWidth - width)/2;
731                 y = frameY + frameHeight - height - borderY;
732                 break;
733             case TK_ANCHOR_SW:
734                 x = frameX + borderX;
735                 y = frameY + frameHeight - height - borderY;
736                 break;
737             case TK_ANCHOR_W:
738                 x = frameX + borderX;
739                 y = frameY + (frameHeight - height)/2;
740                 break;
741             case TK_ANCHOR_NW:
742                 x = frameX + borderX;
743                 y = frameY + borderY;
744                 break;
745             case TK_ANCHOR_CENTER:
746                 x = frameX + (frameWidth - width)/2;
747                 y = frameY + (frameHeight - height)/2;
748                 break;
749             default:
750                 panic("bad frame factor in ArrangePacking");
751         }
752         width -= slavePtr->doubleBw;
753         height -= slavePtr->doubleBw;
754
755         /*
756          * The final step is to set the position, size, and mapped/unmapped
757          * state of the slave.  If the slave is a child of the master, then
758          * do this here.  Otherwise let Tk_MaintainGeometry do the work.
759          */
760
761         if (masterPtr->tkwin == Tk_Parent(slavePtr->tkwin)) {
762             if ((width <= 0) || (height <= 0)) {
763                 Tk_UnmapWindow(slavePtr->tkwin);
764             } else {
765                 if ((x != Tk_X(slavePtr->tkwin))
766                         || (y != Tk_Y(slavePtr->tkwin))
767                         || (width != Tk_Width(slavePtr->tkwin))
768                         || (height != Tk_Height(slavePtr->tkwin))) {
769                     Tk_MoveResizeWindow(slavePtr->tkwin, x, y, width, height);
770                 }
771                 if (abort) {
772                     goto done;
773                 }
774
775                 /*
776                  * Don't map the slave if the master isn't mapped: wait
777                  * until the master gets mapped later.
778                  */
779
780                 if (Tk_IsMapped(masterPtr->tkwin)) {
781                     Tk_MapWindow(slavePtr->tkwin);
782                 }
783             }
784         } else {
785             if ((width <= 0) || (height <= 0)) {
786                 Tk_UnmaintainGeometry(slavePtr->tkwin, masterPtr->tkwin);
787                 Tk_UnmapWindow(slavePtr->tkwin);
788             } else {
789                 Tk_MaintainGeometry(slavePtr->tkwin, masterPtr->tkwin,
790                         x, y, width, height);
791             }
792         }
793
794         /*
795          * Changes to the window's structure could cause almost anything
796          * to happen, including deleting the parent or child.  If this
797          * happens, we'll be told to abort.
798          */
799
800         if (abort) {
801             goto done;
802         }
803     }
804
805     done:
806     masterPtr->abortPtr = NULL;
807     Tcl_Release((ClientData) masterPtr);
808 }
809 \f
810 /*
811  *----------------------------------------------------------------------
812  *
813  * XExpansion --
814  *
815  *      Given a list of packed slaves, the first of which is packed
816  *      on the left or right and is expandable, compute how much to
817  *      expand the child.
818  *
819  * Results:
820  *      The return value is the number of additional pixels to give to
821  *      the child.
822  *
823  * Side effects:
824  *      None.
825  *
826  *----------------------------------------------------------------------
827  */
828
829 static int
830 XExpansion(slavePtr, cavityWidth)
831     register Packer *slavePtr;          /* First in list of remaining
832                                          * slaves. */
833     int cavityWidth;                    /* Horizontal space left for all
834                                          * remaining slaves. */
835 {
836     int numExpand, minExpand, curExpand;
837     int childWidth;
838
839     /*
840      * This procedure is tricky because windows packed top or bottom can
841      * be interspersed among expandable windows packed left or right.
842      * Scan through the list, keeping a running sum of the widths of
843      * all left and right windows (actually, count the cavity space not
844      * allocated) and a running count of all expandable left and right
845      * windows.  At each top or bottom window, and at the end of the
846      * list, compute the expansion factor that seems reasonable at that
847      * point.  Return the smallest factor seen at any of these points.
848      */
849
850     minExpand = cavityWidth;
851     numExpand = 0;
852     for ( ; slavePtr != NULL; slavePtr = slavePtr->nextPtr) {
853         childWidth = Tk_ReqWidth(slavePtr->tkwin) + slavePtr->doubleBw
854                 + slavePtr->padX + slavePtr->iPadX;
855         if ((slavePtr->side == TOP) || (slavePtr->side == BOTTOM)) {
856             curExpand = (cavityWidth - childWidth)/numExpand;
857             if (curExpand < minExpand) {
858                 minExpand = curExpand;
859             }
860         } else {
861             cavityWidth -= childWidth;
862             if (slavePtr->flags & EXPAND) {
863                 numExpand++;
864             }
865         }
866     }
867     curExpand = cavityWidth/numExpand;
868     if (curExpand < minExpand) {
869         minExpand = curExpand;
870     }
871     return (minExpand < 0) ? 0 : minExpand;
872 }
873 \f
874 /*
875  *----------------------------------------------------------------------
876  *
877  * YExpansion --
878  *
879  *      Given a list of packed slaves, the first of which is packed
880  *      on the top or bottom and is expandable, compute how much to
881  *      expand the child.
882  *
883  * Results:
884  *      The return value is the number of additional pixels to give to
885  *      the child.
886  *
887  * Side effects:
888  *      None.
889  *
890  *----------------------------------------------------------------------
891  */
892
893 static int
894 YExpansion(slavePtr, cavityHeight)
895     register Packer *slavePtr;          /* First in list of remaining
896                                          * slaves. */
897     int cavityHeight;                   /* Vertical space left for all
898                                          * remaining slaves. */
899 {
900     int numExpand, minExpand, curExpand;
901     int childHeight;
902
903     /*
904      * See comments for XExpansion.
905      */
906
907     minExpand = cavityHeight;
908     numExpand = 0;
909     for ( ; slavePtr != NULL; slavePtr = slavePtr->nextPtr) {
910         childHeight = Tk_ReqHeight(slavePtr->tkwin) + slavePtr->doubleBw
911                 + slavePtr->padY + slavePtr->iPadY;
912         if ((slavePtr->side == LEFT) || (slavePtr->side == RIGHT)) {
913             curExpand = (cavityHeight - childHeight)/numExpand;
914             if (curExpand < minExpand) {
915                 minExpand = curExpand;
916             }
917         } else {
918             cavityHeight -= childHeight;
919             if (slavePtr->flags & EXPAND) {
920                 numExpand++;
921             }
922         }
923     }
924     curExpand = cavityHeight/numExpand;
925     if (curExpand < minExpand) {
926         minExpand = curExpand;
927     }
928     return (minExpand < 0) ? 0 : minExpand;
929 }
930 \f
931 /*
932  *--------------------------------------------------------------
933  *
934  * GetPacker --
935  *
936  *      This internal procedure is used to locate a Packer
937  *      structure for a given window, creating one if one
938  *      doesn't exist already.
939  *
940  * Results:
941  *      The return value is a pointer to the Packer structure
942  *      corresponding to tkwin.
943  *
944  * Side effects:
945  *      A new packer structure may be created.  If so, then
946  *      a callback is set up to clean things up when the
947  *      window is deleted.
948  *
949  *--------------------------------------------------------------
950  */
951
952 static Packer *
953 GetPacker(tkwin)
954     Tk_Window tkwin;            /* Token for window for which
955                                  * packer structure is desired. */
956 {
957     register Packer *packPtr;
958     Tcl_HashEntry *hPtr;
959     int new;
960
961     if (!initialized) {
962         initialized = 1;
963         Tcl_InitHashTable(&packerHashTable, TCL_ONE_WORD_KEYS);
964     }
965
966     /*
967      * See if there's already packer for this window.  If not,
968      * then create a new one.
969      */
970
971     hPtr = Tcl_CreateHashEntry(&packerHashTable, (char *) tkwin, &new);
972     if (!new) {
973         return (Packer *) Tcl_GetHashValue(hPtr);
974     }
975     packPtr = (Packer *) ckalloc(sizeof(Packer));
976     packPtr->tkwin = tkwin;
977     packPtr->masterPtr = NULL;
978     packPtr->nextPtr = NULL;
979     packPtr->slavePtr = NULL;
980     packPtr->side = TOP;
981     packPtr->anchor = TK_ANCHOR_CENTER;
982     packPtr->padX = packPtr->padY = 0;
983     packPtr->iPadX = packPtr->iPadY = 0;
984     packPtr->doubleBw = 2*Tk_Changes(tkwin)->border_width;
985     packPtr->abortPtr = NULL;
986     packPtr->flags = 0;
987     Tcl_SetHashValue(hPtr, packPtr);
988     Tk_CreateEventHandler(tkwin, StructureNotifyMask,
989             PackStructureProc, (ClientData) packPtr);
990     return packPtr;
991 }
992 \f
993 /*
994  *--------------------------------------------------------------
995  *
996  * PackAfter --
997  *
998  *      This procedure does most of the real work of adding
999  *      one or more windows into the packing order for its parent.
1000  *
1001  * Results:
1002  *      A standard Tcl return value.
1003  *
1004  * Side effects:
1005  *      The geometry of the specified windows may change, both now and
1006  *      again in the future.
1007  *
1008  *--------------------------------------------------------------
1009  */
1010
1011 static int
1012 PackAfter(interp, prevPtr, masterPtr, argc, argv)
1013     Tcl_Interp *interp;         /* Interpreter for error reporting. */
1014     Packer *prevPtr;            /* Pack windows in argv just after this
1015                                  * window;  NULL means pack as first
1016                                  * child of masterPtr. */
1017     Packer *masterPtr;          /* Master in which to pack windows. */
1018     int argc;                   /* Number of elements in argv. */
1019     char **argv;                /* Array of lists, each containing 2
1020                                  * elements:  window name and side
1021                                  * against which to pack. */
1022 {
1023     register Packer *packPtr;
1024     Tk_Window tkwin, ancestor, parent;
1025     size_t length;
1026     char **options;
1027     int index, tmp, optionCount, c;
1028
1029     /*
1030      * Iterate over all of the window specifiers, each consisting of
1031      * two arguments.  The first argument contains the window name and
1032      * the additional arguments contain options such as "top" or
1033      * "padx 20".
1034      */
1035
1036     for ( ; argc > 0; argc -= 2, argv += 2, prevPtr = packPtr) {
1037         if (argc < 2) {
1038             Tcl_AppendResult(interp, "wrong # args: window \"",
1039                     argv[0], "\" should be followed by options",
1040                     (char *) NULL);
1041             return TCL_ERROR;
1042         }
1043
1044         /*
1045          * Find the packer for the window to be packed, and make sure
1046          * that the window in which it will be packed is either its
1047          * or a descendant of its parent.
1048          */
1049
1050         tkwin = Tk_NameToWindow(interp, argv[0], masterPtr->tkwin);
1051         if (tkwin == NULL) {
1052             return TCL_ERROR;
1053         }
1054
1055         parent = Tk_Parent(tkwin);
1056         for (ancestor = masterPtr->tkwin; ; ancestor = Tk_Parent(ancestor)) {
1057             if (ancestor == parent) {
1058                 break;
1059             }
1060             if (((Tk_FakeWin *) (ancestor))->flags & TK_TOP_LEVEL) {
1061                 badWindow:
1062                 Tcl_AppendResult(interp, "can't pack ", argv[0],
1063                         " inside ", Tk_PathName(masterPtr->tkwin),
1064                         (char *) NULL);
1065                 return TCL_ERROR;
1066             }
1067         }
1068         if (((Tk_FakeWin *) (tkwin))->flags & TK_TOP_LEVEL) {
1069             goto badWindow;
1070         }
1071         if (tkwin == masterPtr->tkwin) {
1072             goto badWindow;
1073         }
1074         packPtr = GetPacker(tkwin);
1075
1076         /*
1077          * Process options for this window.
1078          */
1079
1080         if (Tcl_SplitList(interp, argv[1], &optionCount, &options) != TCL_OK) {
1081             return TCL_ERROR;
1082         }
1083         packPtr->side = TOP;
1084         packPtr->anchor = TK_ANCHOR_CENTER;
1085         packPtr->padX = packPtr->padY = 0;
1086         packPtr->iPadX = packPtr->iPadY = 0;
1087         packPtr->flags &= ~(FILLX|FILLY|EXPAND);
1088         packPtr->flags |= OLD_STYLE;
1089         for (index = 0 ; index < optionCount; index++) {
1090             char *curOpt = options[index];
1091
1092             c = curOpt[0];
1093             length = strlen(curOpt);
1094
1095             if ((c == 't')
1096                     && (strncmp(curOpt, "top", length)) == 0) {
1097                 packPtr->side = TOP;
1098             } else if ((c == 'b')
1099                     && (strncmp(curOpt, "bottom", length)) == 0) {
1100                 packPtr->side = BOTTOM;
1101             } else if ((c == 'l')
1102                     && (strncmp(curOpt, "left", length)) == 0) {
1103                 packPtr->side = LEFT;
1104             } else if ((c == 'r')
1105                     && (strncmp(curOpt, "right", length)) == 0) {
1106                 packPtr->side = RIGHT;
1107             } else if ((c == 'e')
1108                     && (strncmp(curOpt, "expand", length)) == 0) {
1109                 packPtr->flags |= EXPAND;
1110             } else if ((c == 'f')
1111                     && (strcmp(curOpt, "fill")) == 0) {
1112                 packPtr->flags |= FILLX|FILLY;
1113             } else if ((length == 5) && (strcmp(curOpt, "fillx")) == 0) {
1114                 packPtr->flags |= FILLX;
1115             } else if ((length == 5) && (strcmp(curOpt, "filly")) == 0) {
1116                 packPtr->flags |= FILLY;
1117             } else if ((c == 'p') && (strcmp(curOpt, "padx")) == 0) {
1118                 if (optionCount < (index+2)) {
1119                     missingPad:
1120                     Tcl_AppendResult(interp, "wrong # args: \"", curOpt,
1121                             "\" option must be followed by screen distance",
1122                             (char *) NULL);
1123                     goto error;
1124                 }
1125                 if ((Tk_GetPixels(interp, tkwin, options[index+1], &tmp)
1126                         != TCL_OK) || (tmp < 0)) {
1127                     badPad:
1128                     Tcl_AppendResult(interp, "bad pad value \"",
1129                             options[index+1],
1130                             "\": must be positive screen distance",
1131                             (char *) NULL);
1132                     goto error;
1133                 }
1134                 packPtr->padX = tmp;
1135                 packPtr->iPadX = 0;
1136                 index++;
1137             } else if ((c == 'p') && (strcmp(curOpt, "pady")) == 0) {
1138                 if (optionCount < (index+2)) {
1139                     goto missingPad;
1140                 }
1141                 if ((Tk_GetPixels(interp, tkwin, options[index+1], &tmp)
1142                         != TCL_OK) || (tmp < 0)) {
1143                     goto badPad;
1144                 }
1145                 packPtr->padY = tmp;
1146                 packPtr->iPadY = 0;
1147                 index++;
1148             } else if ((c == 'f') && (length > 1)
1149                     && (strncmp(curOpt, "frame", length) == 0)) {
1150                 if (optionCount < (index+2)) {
1151                     Tcl_AppendResult(interp, "wrong # args: \"frame\" ",
1152                             "option must be followed by anchor point",
1153                             (char *) NULL);
1154                     goto error;
1155                 }
1156                 if (Tk_GetAnchor(interp, options[index+1],
1157                         &packPtr->anchor) != TCL_OK) {
1158                     goto error;
1159                 }
1160                 index++;
1161             } else {
1162                 Tcl_AppendResult(interp, "bad option \"", curOpt,
1163                         "\": should be top, bottom, left, right, ",
1164                         "expand, fill, fillx, filly, padx, pady, or frame",
1165                         (char *) NULL);
1166                 goto error;
1167             }
1168         }
1169
1170         if (packPtr != prevPtr) {
1171
1172             /*
1173              * Unpack this window if it's currently packed.
1174              */
1175
1176             if (packPtr->masterPtr != NULL) {
1177                 if ((packPtr->masterPtr != masterPtr) &&
1178                         (packPtr->masterPtr->tkwin
1179                         != Tk_Parent(packPtr->tkwin))) {
1180                     Tk_UnmaintainGeometry(packPtr->tkwin,
1181                             packPtr->masterPtr->tkwin);
1182                 }
1183                 Unlink(packPtr);
1184             }
1185         
1186             /*
1187              * Add the window in the correct place in its parent's
1188              * packing order, then make sure that the window is
1189              * managed by us.
1190              */
1191
1192             packPtr->masterPtr = masterPtr;
1193             if (prevPtr == NULL) {
1194                 packPtr->nextPtr = masterPtr->slavePtr;
1195                 masterPtr->slavePtr = packPtr;
1196             } else {
1197                 packPtr->nextPtr = prevPtr->nextPtr;
1198                 prevPtr->nextPtr = packPtr;
1199             }
1200             Tk_ManageGeometry(tkwin, &packerType, (ClientData) packPtr);
1201         }
1202         ckfree((char *) options);
1203     }
1204
1205     /*
1206      * Arrange for the parent to be re-packed at the first
1207      * idle moment.
1208      */
1209
1210     if (masterPtr->abortPtr != NULL) {
1211         *masterPtr->abortPtr = 1;
1212     }
1213     if (!(masterPtr->flags & REQUESTED_REPACK)) {
1214         masterPtr->flags |= REQUESTED_REPACK;
1215         Tcl_DoWhenIdle(ArrangePacking, (ClientData) masterPtr);
1216     }
1217     return TCL_OK;
1218
1219     error:
1220     ckfree((char *) options);
1221     return TCL_ERROR;
1222 }
1223 \f
1224 /*
1225  *----------------------------------------------------------------------
1226  *
1227  * Unlink --
1228  *
1229  *      Remove a packer from its parent's list of slaves.
1230  *
1231  * Results:
1232  *      None.
1233  *
1234  * Side effects:
1235  *      The parent will be scheduled for repacking.
1236  *
1237  *----------------------------------------------------------------------
1238  */
1239
1240 static void
1241 Unlink(packPtr)
1242     register Packer *packPtr;           /* Window to unlink. */
1243 {
1244     register Packer *masterPtr, *packPtr2;
1245
1246     masterPtr = packPtr->masterPtr;
1247     if (masterPtr == NULL) {
1248         return;
1249     }
1250     if (masterPtr->slavePtr == packPtr) {
1251         masterPtr->slavePtr = packPtr->nextPtr;
1252     } else {
1253         for (packPtr2 = masterPtr->slavePtr; ; packPtr2 = packPtr2->nextPtr) {
1254             if (packPtr2 == NULL) {
1255                 panic("Unlink couldn't find previous window");
1256             }
1257             if (packPtr2->nextPtr == packPtr) {
1258                 packPtr2->nextPtr = packPtr->nextPtr;
1259                 break;
1260             }
1261         }
1262     }
1263     if (!(masterPtr->flags & REQUESTED_REPACK)) {
1264         masterPtr->flags |= REQUESTED_REPACK;
1265         Tcl_DoWhenIdle(ArrangePacking, (ClientData) masterPtr);
1266     }
1267     if (masterPtr->abortPtr != NULL) {
1268         *masterPtr->abortPtr = 1;
1269     }
1270
1271     packPtr->masterPtr = NULL;
1272 }
1273 \f
1274 /*
1275  *----------------------------------------------------------------------
1276  *
1277  * DestroyPacker --
1278  *
1279  *      This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
1280  *      to clean up the internal structure of a packer at a safe time
1281  *      (when no-one is using it anymore).
1282  *
1283  * Results:
1284  *      None.
1285  *
1286  * Side effects:
1287  *      Everything associated with the packer is freed up.
1288  *
1289  *----------------------------------------------------------------------
1290  */
1291
1292 static void
1293 DestroyPacker(memPtr)
1294     char *memPtr;               /* Info about packed window that
1295                                  * is now dead. */
1296 {
1297     register Packer *packPtr = (Packer *) memPtr;
1298     ckfree((char *) packPtr);
1299 }
1300 \f
1301 /*
1302  *----------------------------------------------------------------------
1303  *
1304  * PackStructureProc --
1305  *
1306  *      This procedure is invoked by the Tk event dispatcher in response
1307  *      to StructureNotify events.
1308  *
1309  * Results:
1310  *      None.
1311  *
1312  * Side effects:
1313  *      If a window was just deleted, clean up all its packer-related
1314  *      information.  If it was just resized, repack its slaves, if
1315  *      any.
1316  *
1317  *----------------------------------------------------------------------
1318  */
1319
1320 static void
1321 PackStructureProc(clientData, eventPtr)
1322     ClientData clientData;              /* Our information about window
1323                                          * referred to by eventPtr. */
1324     XEvent *eventPtr;                   /* Describes what just happened. */
1325 {
1326     register Packer *packPtr = (Packer *) clientData;
1327     if (eventPtr->type == ConfigureNotify) {
1328         if ((packPtr->slavePtr != NULL)
1329                 && !(packPtr->flags & REQUESTED_REPACK)) {
1330             packPtr->flags |= REQUESTED_REPACK;
1331             Tcl_DoWhenIdle(ArrangePacking, (ClientData) packPtr);
1332         }
1333         if (packPtr->doubleBw != 2*Tk_Changes(packPtr->tkwin)->border_width) {
1334             if ((packPtr->masterPtr != NULL)
1335                     && !(packPtr->masterPtr->flags & REQUESTED_REPACK)) {
1336                 packPtr->doubleBw = 2*Tk_Changes(packPtr->tkwin)->border_width;
1337                 packPtr->masterPtr->flags |= REQUESTED_REPACK;
1338                 Tcl_DoWhenIdle(ArrangePacking, (ClientData) packPtr->masterPtr);
1339             }
1340         }
1341     } else if (eventPtr->type == DestroyNotify) {
1342         register Packer *slavePtr, *nextPtr;
1343
1344         if (packPtr->masterPtr != NULL) {
1345             Unlink(packPtr);
1346         }
1347         for (slavePtr = packPtr->slavePtr; slavePtr != NULL;
1348                 slavePtr = nextPtr) {
1349             Tk_ManageGeometry(slavePtr->tkwin, (Tk_GeomMgr *) NULL,
1350                     (ClientData) NULL);
1351             Tk_UnmapWindow(slavePtr->tkwin);
1352             slavePtr->masterPtr = NULL;
1353             nextPtr = slavePtr->nextPtr;
1354             slavePtr->nextPtr = NULL;
1355         }
1356         Tcl_DeleteHashEntry(Tcl_FindHashEntry(&packerHashTable,
1357                 (char *) packPtr->tkwin));
1358         if (packPtr->flags & REQUESTED_REPACK) {
1359             Tcl_CancelIdleCall(ArrangePacking, (ClientData) packPtr);
1360         }
1361         packPtr->tkwin = NULL;
1362         Tcl_EventuallyFree((ClientData) packPtr, DestroyPacker);
1363     } else if (eventPtr->type == MapNotify) {
1364         /*
1365          * When a master gets mapped, must redo the geometry computation
1366          * so that all of its slaves get remapped.
1367          */
1368
1369         if ((packPtr->slavePtr != NULL)
1370                 && !(packPtr->flags & REQUESTED_REPACK)) {
1371             packPtr->flags |= REQUESTED_REPACK;
1372             Tcl_DoWhenIdle(ArrangePacking, (ClientData) packPtr);
1373         }
1374     } else if (eventPtr->type == UnmapNotify) {
1375         Packer *packPtr2;
1376
1377         /*
1378          * Unmap all of the slaves when the master gets unmapped,
1379          * so that they don't bother to keep redisplaying
1380          * themselves.
1381          */
1382
1383         for (packPtr2 = packPtr->slavePtr; packPtr2 != NULL;
1384                 packPtr2 = packPtr2->nextPtr) {
1385             Tk_UnmapWindow(packPtr2->tkwin);
1386         }
1387     }
1388 }
1389 \f
1390 /*
1391  *----------------------------------------------------------------------
1392  *
1393  * ConfigureSlaves --
1394  *
1395  *      This implements the guts of the "pack configure" command.  Given
1396  *      a list of slaves and configuration options, it arranges for the
1397  *      packer to manage the slaves and sets the specified options.
1398  *
1399  * Results:
1400  *      TCL_OK is returned if all went well.  Otherwise, TCL_ERROR is
1401  *      returned and interp->result is set to contain an error message.
1402  *
1403  * Side effects:
1404  *      Slave windows get taken over by the packer.
1405  *
1406  *----------------------------------------------------------------------
1407  */
1408
1409 static int
1410 ConfigureSlaves(interp, tkwin, argc, argv)
1411     Tcl_Interp *interp;         /* Interpreter for error reporting. */
1412     Tk_Window tkwin;            /* Any window in application containing
1413                                  * slaves.  Used to look up slave names. */
1414     int argc;                   /* Number of elements in argv. */
1415     char *argv[];               /* Argument strings:  contains one or more
1416                                  * window names followed by any number
1417                                  * of "option value" pairs.  Caller must
1418                                  * make sure that there is at least one
1419                                  * window name. */
1420 {
1421     Packer *masterPtr, *slavePtr, *prevPtr, *otherPtr;
1422     Tk_Window other, slave, parent, ancestor;
1423     int i, j, numWindows, c, tmp, positionGiven;
1424     size_t length;
1425
1426     /*
1427      * Find out how many windows are specified.
1428      */
1429
1430     for (numWindows = 0; numWindows < argc; numWindows++) {
1431         if (argv[numWindows][0] != '.') {
1432             break;
1433         }
1434     }
1435
1436     /*
1437      * Iterate over all of the slave windows, parsing the configuration
1438      * options for each slave.  It's a bit wasteful to re-parse the
1439      * options for each slave, but things get too messy if we try to
1440      * parse the arguments just once at the beginning.  For example,
1441      * if a slave already is packed we want to just change a few
1442      * existing values without resetting everything.  If there are
1443      * multiple windows, the -after, -before, and -in options only
1444      * get processed for the first window.
1445      */
1446
1447     masterPtr = NULL;
1448     prevPtr = NULL;
1449     positionGiven = 0;
1450     for (j = 0; j < numWindows; j++) {
1451         slave = Tk_NameToWindow(interp, argv[j], tkwin);
1452         if (slave == NULL) {
1453             return TCL_ERROR;
1454         }
1455         if (Tk_IsTopLevel(slave)) {
1456             Tcl_AppendResult(interp, "can't pack \"", argv[j],
1457                     "\": it's a top-level window", (char *) NULL);
1458             return TCL_ERROR;
1459         }
1460         slavePtr = GetPacker(slave);
1461         slavePtr->flags &= ~OLD_STYLE;
1462
1463         /*
1464          * If the slave isn't currently packed, reset all of its
1465          * configuration information to default values (there could
1466          * be old values left from a previous packing).
1467          */
1468
1469         if (slavePtr->masterPtr == NULL) {
1470             slavePtr->side = TOP;
1471             slavePtr->anchor = TK_ANCHOR_CENTER;
1472             slavePtr->padX = slavePtr->padY = 0;
1473             slavePtr->iPadX = slavePtr->iPadY = 0;
1474             slavePtr->flags &= ~(FILLX|FILLY|EXPAND);
1475         }
1476
1477         for (i = numWindows; i < argc; i+=2) {
1478             if ((i+2) > argc) {
1479                 Tcl_AppendResult(interp, "extra option \"", argv[i],
1480                         "\" (option with no value?)", (char *) NULL);
1481                 return TCL_ERROR;
1482             }
1483             length = strlen(argv[i]);
1484             if (length < 2) {
1485                 goto badOption;
1486             }
1487             c = argv[i][1];
1488             if ((c == 'a') && (strncmp(argv[i], "-after", length) == 0)
1489                     && (length >= 2)) {
1490                 if (j == 0) {
1491                     other = Tk_NameToWindow(interp, argv[i+1], tkwin);
1492                     if (other == NULL) {
1493                         return TCL_ERROR;
1494                     }
1495                     prevPtr = GetPacker(other);
1496                     if (prevPtr->masterPtr == NULL) {
1497                         notPacked:
1498                         Tcl_AppendResult(interp, "window \"", argv[i+1],
1499                                 "\" isn't packed", (char *) NULL);
1500                         return TCL_ERROR;
1501                     }
1502                     masterPtr = prevPtr->masterPtr;
1503                     positionGiven = 1;
1504                 }
1505             } else if ((c == 'a') && (strncmp(argv[i], "-anchor", length) == 0)
1506                     && (length >= 2)) {
1507                 if (Tk_GetAnchor(interp, argv[i+1], &slavePtr->anchor)
1508                         != TCL_OK) {
1509                     return TCL_ERROR;
1510                 }
1511             } else if ((c == 'b')
1512                     && (strncmp(argv[i], "-before", length) == 0)) {
1513                 if (j == 0) {
1514                     other = Tk_NameToWindow(interp, argv[i+1], tkwin);
1515                     if (other == NULL) {
1516                         return TCL_ERROR;
1517                     }
1518                     otherPtr = GetPacker(other);
1519                     if (otherPtr->masterPtr == NULL) {
1520                         goto notPacked;
1521                     }
1522                     masterPtr = otherPtr->masterPtr;
1523                     prevPtr = masterPtr->slavePtr;
1524                     if (prevPtr == otherPtr) {
1525                         prevPtr = NULL;
1526                     } else {
1527                         while (prevPtr->nextPtr != otherPtr) {
1528                             prevPtr = prevPtr->nextPtr;
1529                         }
1530                     }
1531                     positionGiven = 1;
1532                 }
1533             } else if ((c == 'e')
1534                     && (strncmp(argv[i], "-expand", length) == 0)) {
1535                 if (Tcl_GetBoolean(interp, argv[i+1], &tmp) != TCL_OK) {
1536                     return TCL_ERROR;
1537                 }
1538                 slavePtr->flags &= ~EXPAND;
1539                 if (tmp) {
1540                     slavePtr->flags |= EXPAND;
1541                 }
1542             } else if ((c == 'f') && (strncmp(argv[i], "-fill", length) == 0)) {
1543                 if (strcmp(argv[i+1], "none") == 0) {
1544                     slavePtr->flags &= ~(FILLX|FILLY);
1545                 } else if (strcmp(argv[i+1], "x") == 0) {
1546                     slavePtr->flags = (slavePtr->flags & ~FILLY) | FILLX;
1547                 } else if (strcmp(argv[i+1], "y") == 0) {
1548                     slavePtr->flags = (slavePtr->flags & ~FILLX) | FILLY;
1549                 } else if (strcmp(argv[i+1], "both") == 0) {
1550                     slavePtr->flags |= FILLX|FILLY;
1551                 } else {
1552                     Tcl_AppendResult(interp, "bad fill style \"", argv[i+1],
1553                             "\": must be none, x, y, or both", (char *) NULL);
1554                     return TCL_ERROR;
1555                 }
1556             } else if ((c == 'i') && (strcmp(argv[i], "-in") == 0)) {
1557                 if (j == 0) {
1558                     other = Tk_NameToWindow(interp, argv[i+1], tkwin);
1559                     if (other == NULL) {
1560                         return TCL_ERROR;
1561                     }
1562                     masterPtr = GetPacker(other);
1563                     prevPtr = masterPtr->slavePtr;
1564                     if (prevPtr != NULL) {
1565                         while (prevPtr->nextPtr != NULL) {
1566                             prevPtr = prevPtr->nextPtr;
1567                         }
1568                     }
1569                     positionGiven = 1;
1570                 }
1571             } else if ((c == 'i') && (strcmp(argv[i], "-ipadx") == 0)) {
1572                 if ((Tk_GetPixels(interp, slave, argv[i+1], &tmp) != TCL_OK)
1573                         || (tmp < 0)) {
1574                     badPad:
1575                     Tcl_ResetResult(interp);
1576                     Tcl_AppendResult(interp, "bad pad value \"", argv[i+1],
1577                             "\": must be positive screen distance",
1578                             (char *) NULL);
1579                     return TCL_ERROR;
1580                 }
1581                 slavePtr->iPadX = tmp*2;
1582             } else if ((c == 'i') && (strcmp(argv[i], "-ipady") == 0)) {
1583                 if ((Tk_GetPixels(interp, slave, argv[i+1], &tmp) != TCL_OK)
1584                         || (tmp< 0)) {
1585                     goto badPad;
1586                 }
1587                 slavePtr->iPadY = tmp*2;
1588             } else if ((c == 'p') && (strcmp(argv[i], "-padx") == 0)) {
1589                 if ((Tk_GetPixels(interp, slave, argv[i+1], &tmp) != TCL_OK)
1590                         || (tmp< 0)) {
1591                     goto badPad;
1592                 }
1593                 slavePtr->padX = tmp*2;
1594             } else if ((c == 'p') && (strcmp(argv[i], "-pady") == 0)) {
1595                 if ((Tk_GetPixels(interp, slave, argv[i+1], &tmp) != TCL_OK)
1596                         || (tmp< 0)) {
1597                     goto badPad;
1598                 }
1599                 slavePtr->padY = tmp*2;
1600             } else if ((c == 's') && (strncmp(argv[i], "-side", length) == 0)) {
1601                 c = argv[i+1][0];
1602                 if ((c == 't') && (strcmp(argv[i+1], "top") == 0)) {
1603                     slavePtr->side = TOP;
1604                 } else if ((c == 'b') && (strcmp(argv[i+1], "bottom") == 0)) {
1605                     slavePtr->side = BOTTOM;
1606                 } else if ((c == 'l') && (strcmp(argv[i+1], "left") == 0)) {
1607                     slavePtr->side = LEFT;
1608                 } else if ((c == 'r') && (strcmp(argv[i+1], "right") == 0)) {
1609                     slavePtr->side = RIGHT;
1610                 } else {
1611                     Tcl_AppendResult(interp, "bad side \"", argv[i+1],
1612                             "\": must be top, bottom, left, or right",
1613                             (char *) NULL);
1614                     return TCL_ERROR;
1615                 }
1616             } else {
1617                 badOption:
1618                 Tcl_AppendResult(interp, "unknown or ambiguous option \"",
1619                         argv[i], "\": must be -after, -anchor, -before, ",
1620                         "-expand, -fill, -in, -ipadx, -ipady, -padx, ",
1621                         "-pady, or -side", (char *) NULL);
1622                 return TCL_ERROR;
1623             }
1624         }
1625
1626         /*
1627          * If no position in a packing list was specified and the slave
1628          * is already packed, then leave it in its current location in
1629          * its current packing list.
1630          */
1631
1632         if (!positionGiven && (slavePtr->masterPtr != NULL)) {
1633             masterPtr = slavePtr->masterPtr;
1634             goto scheduleLayout;
1635         }
1636
1637         /*
1638          * If the slave is going to be put back after itself then
1639          * skip the whole operation, since it won't work anyway.
1640          */
1641
1642         if (prevPtr == slavePtr) {
1643             masterPtr = slavePtr->masterPtr;
1644             goto scheduleLayout;
1645         }
1646     
1647         /*
1648          * If none of the "-in", "-before", or "-after" options has
1649          * been specified, arrange for the slave to go at the end of
1650          * the order for its parent.
1651          */
1652     
1653         if (!positionGiven) {
1654             masterPtr = GetPacker(Tk_Parent(slave));
1655             prevPtr = masterPtr->slavePtr;
1656             if (prevPtr != NULL) {
1657                 while (prevPtr->nextPtr != NULL) {
1658                     prevPtr = prevPtr->nextPtr;
1659                 }
1660             }
1661         }
1662
1663         /*
1664          * Make sure that the slave's parent is either the master or
1665          * an ancestor of the master, and that the master and slave
1666          * aren't the same.
1667          */
1668     
1669         parent = Tk_Parent(slave);
1670         for (ancestor = masterPtr->tkwin; ; ancestor = Tk_Parent(ancestor)) {
1671             if (ancestor == parent) {
1672                 break;
1673             }
1674             if (Tk_IsTopLevel(ancestor)) {
1675                 Tcl_AppendResult(interp, "can't pack ", argv[j],
1676                         " inside ", Tk_PathName(masterPtr->tkwin),
1677                         (char *) NULL);
1678                 return TCL_ERROR;
1679             }
1680         }
1681         if (slave == masterPtr->tkwin) {
1682             Tcl_AppendResult(interp, "can't pack ", argv[j],
1683                     " inside itself", (char *) NULL);
1684             return TCL_ERROR;
1685         }
1686
1687         /*
1688          * Unpack the slave if it's currently packed, then position it
1689          * after prevPtr.
1690          */
1691
1692         if (slavePtr->masterPtr != NULL) {
1693             if ((slavePtr->masterPtr != masterPtr) &&
1694                     (slavePtr->masterPtr->tkwin
1695                     != Tk_Parent(slavePtr->tkwin))) {
1696                 Tk_UnmaintainGeometry(slavePtr->tkwin,
1697                         slavePtr->masterPtr->tkwin);
1698             }
1699             Unlink(slavePtr);
1700         }
1701         slavePtr->masterPtr = masterPtr;
1702         if (prevPtr == NULL) {
1703             slavePtr->nextPtr = masterPtr->slavePtr;
1704             masterPtr->slavePtr = slavePtr;
1705         } else {
1706             slavePtr->nextPtr = prevPtr->nextPtr;
1707             prevPtr->nextPtr = slavePtr;
1708         }
1709         Tk_ManageGeometry(slave, &packerType, (ClientData) slavePtr);
1710         prevPtr = slavePtr;
1711
1712         /*
1713          * Arrange for the parent to be re-packed at the first
1714          * idle moment.
1715          */
1716
1717         scheduleLayout:
1718         if (masterPtr->abortPtr != NULL) {
1719             *masterPtr->abortPtr = 1;
1720         }
1721         if (!(masterPtr->flags & REQUESTED_REPACK)) {
1722             masterPtr->flags |= REQUESTED_REPACK;
1723             Tcl_DoWhenIdle(ArrangePacking, (ClientData) masterPtr);
1724         }
1725     }
1726     return TCL_OK;
1727 }