OSDN Git Service

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