4 * This file contains code to implement the "packer"
5 * geometry manager for Tk.
7 * Copyright 1990 Regents of the University of California.
8 * Permission to use, copy, modify, and distribute this
9 * software and its documentation for any purpose and without
10 * fee is hereby granted, provided that the above copyright
11 * notice appear in all copies. The University of California
12 * makes no representations about the suitability of this
13 * software for any purpose. It is provided "as is" without
14 * express or implied warranty.
18 static char rcsid
[] = "$Header: /user6/ouster/wish/RCS/tkPack.c,v 1.27 92/01/04 15:16:41 ouster Exp $ SPRITE (Berkeley)";
24 typedef enum {TOP
, BOTTOM
, LEFT
, RIGHT
} Side
;
26 /* For each window that the packer cares about (either because
27 * the window is managed by the packer or because the window
28 * has children that are managed by the packer), there is a
29 * structure of the following type:
32 typedef struct Packer
{
33 Tk_Window tkwin
; /* Tk token for window. NULL means that
34 * the window has been deleted, but the
35 * packet hasn't had a chance to clean up
36 * yet because the structure is still in
38 struct Packer
*parentPtr
; /* Parent within which this window
39 * is packed (NULL means this window
40 * isn't managed by the packer). */
41 struct Packer
*nextPtr
; /* Next window packed within same
42 * parent. List is priority-ordered:
43 * first on list gets packed first. */
44 struct Packer
*childPtr
; /* First in list of children packed
45 * inside this window (NULL means
46 * no packed children). */
47 Side side
; /* Side of parent against which
48 * this window is packed. */
49 Tk_Anchor anchorPoint
; /* If frame allocated for window is larger
50 * than window needs, this indicates how
51 * where to position window in frame. */
52 int padX
, padY
; /* Additional amounts of space to give window
53 * besides what it asked for. */
54 int doubleBw
; /* Twice the window's last known border
55 * width. If this changes, the window
56 * must be repacked within its parent. */
57 int *abortPtr
; /* If non-NULL, it means that there is a nested
58 * call to ArrangePacking already working on
59 * this window. *abortPtr may be set to 1 to
60 * abort that nested call. This happens, for
61 * example, if tkwin or any of its children
63 int flags
; /* Miscellaneous flags; see below
68 * Flag values for Packer structures:
70 * REQUESTED_REPACK: 1 means a Tk_DoWhenIdle request
71 * has already been made to repack
72 * all the children of this window.
73 * FILLX: 1 means if frame allocated for window
74 * is wider than window needs, expand window
75 * to fill frame. 0 means don't make window
76 * any larger than needed.
77 * FILLY: Same as FILLX, except for height.
78 * EXPAND: 1 means this window's frame will absorb any
79 * extra space in the parent window.
82 #define REQUESTED_REPACK 1
88 * Hash table used to map from Tk_Window tokens to corresponding
92 static Tcl_HashTable packerHashTable
;
95 * Have statics in this module been initialized?
98 static int initialized
= 0;
101 * Forward declarations for procedures defined later in this file:
104 static void ArrangePacking
_ANSI_ARGS_((ClientData clientData
));
105 static Packer
* GetPacker
_ANSI_ARGS_((Tk_Window tkwin
));
106 static int PackAfter
_ANSI_ARGS_((Tcl_Interp
*interp
,
107 Packer
*prevPtr
, Packer
*parentPtr
, int argc
,
109 static void PackReqProc
_ANSI_ARGS_((ClientData clientData
,
111 static void PackStructureProc
_ANSI_ARGS_((ClientData clientData
,
113 static void Unlink
_ANSI_ARGS_((Packer
*packPtr
));
116 *--------------------------------------------------------------
120 * This procedure is invoked to process the "pack" Tcl command.
121 * See the user documentation for details on what it does.
124 * A standard Tcl result.
127 * See the user documentation.
129 *--------------------------------------------------------------
134 ClientData clientData
, /* Main window associated with
136 Tcl_Interp
*interp
, /* Current interpreter. */
137 int argc
, /* Number of arguments. */
138 char **argv
/* Argument strings. */
141 Tk_Window tkwin
= (Tk_Window
) clientData
;
146 Tcl_AppendResult(interp
, "wrong # args: should be \"",
147 argv
[0], " option arg ?arg ...?\"", (char *) NULL
);
151 length
= strlen(argv
[1]);
152 if ((c
== 'a') && (length
>= 2)
153 && (strncmp(argv
[1], "after", length
) == 0)) {
157 tkwin2
= Tk_NameToWindow(interp
, argv
[2], tkwin
);
158 if (tkwin2
== NULL
) {
161 prevPtr
= GetPacker(tkwin2
);
162 if (prevPtr
->parentPtr
== NULL
) {
163 Tcl_AppendResult(interp
, "window \"", argv
[2],
164 "\" isn't packed", (char *) NULL
);
167 return PackAfter(interp
, prevPtr
, prevPtr
->parentPtr
, argc
-3, argv
+3);
168 } else if ((c
== 'a') && (length
>= 2)
169 && (strncmp(argv
[1], "append", length
) == 0)) {
171 register Packer
*prevPtr
;
174 tkwin2
= Tk_NameToWindow(interp
, argv
[2], tkwin
);
175 if (tkwin2
== NULL
) {
178 parentPtr
= GetPacker(tkwin2
);
179 prevPtr
= parentPtr
->childPtr
;
180 if (prevPtr
!= NULL
) {
181 while (prevPtr
->nextPtr
!= NULL
) {
182 prevPtr
= prevPtr
->nextPtr
;
185 return PackAfter(interp
, prevPtr
, parentPtr
, argc
-3, argv
+3);
186 } else if ((c
== 'b') && (strncmp(argv
[1], "before", length
) == 0)) {
187 Packer
*packPtr
, *parentPtr
;
188 register Packer
*prevPtr
;
191 tkwin2
= Tk_NameToWindow(interp
, argv
[2], tkwin
);
192 if (tkwin2
== NULL
) {
195 packPtr
= GetPacker(tkwin2
);
196 if (packPtr
->parentPtr
== NULL
) {
197 Tcl_AppendResult(interp
, "window \"", argv
[2],
198 "\" isn't packed", (char *) NULL
);
201 parentPtr
= packPtr
->parentPtr
;
202 prevPtr
= parentPtr
->childPtr
;
203 if (prevPtr
== packPtr
) {
206 for ( ; ; prevPtr
= prevPtr
->nextPtr
) {
207 if (prevPtr
== NULL
) {
208 panic("\"pack before\" couldn't find predecessor");
210 if (prevPtr
->nextPtr
== packPtr
) {
215 return PackAfter(interp
, prevPtr
, parentPtr
, argc
-3, argv
+3);
216 } else if ((c
== 'i') && (strncmp(argv
[1], "info", length
) == 0)) {
218 register Packer
*packPtr
;
221 static char *sideNames
[] = {"top", "bottom", "left", "right"};
224 Tcl_AppendResult(interp
, "wrong # args: should be \"",
225 argv
[0], " info window\"", (char *) NULL
);
228 tkwin2
= Tk_NameToWindow(interp
, argv
[2], tkwin
);
229 if (tkwin2
== NULL
) {
232 packPtr
= GetPacker(tkwin2
);
234 for (packPtr
= packPtr
->childPtr
; packPtr
!= NULL
;
235 packPtr
= packPtr
->nextPtr
) {
236 Tcl_AppendResult(interp
, prefix
, Tk_PathName(packPtr
->tkwin
),
237 " {", sideNames
[(int) packPtr
->side
],
238 " frame ", Tk_NameOfAnchor(packPtr
->anchorPoint
),
240 if (packPtr
->padX
!= 0) {
241 sprintf(tmp
, "%d", packPtr
->padX
);
242 Tcl_AppendResult(interp
, " padx ", tmp
, (char *) NULL
);
244 if (packPtr
->padY
!= 0) {
245 sprintf(tmp
, "%d", packPtr
->padY
);
246 Tcl_AppendResult(interp
, " pady ", tmp
, (char *) NULL
);
248 if (packPtr
->flags
& EXPAND
) {
249 Tcl_AppendResult(interp
, " expand", (char *) NULL
);
251 if ((packPtr
->flags
& (FILLX
|FILLY
)) == (FILLX
|FILLY
)) {
252 Tcl_AppendResult(interp
, " fill", (char *) NULL
);
253 } else if (packPtr
->flags
& FILLX
) {
254 Tcl_AppendResult(interp
, " fillx", (char *) NULL
);
255 } else if (packPtr
->flags
& FILLY
) {
256 Tcl_AppendResult(interp
, " filly", (char *) NULL
);
258 Tcl_AppendResult(interp
, "}", (char *) NULL
);
262 } else if ((c
== 'u') && (strncmp(argv
[1], "unpack", length
) == 0)) {
267 Tcl_AppendResult(interp
, "wrong # args: should be \"",
268 argv
[0], " unpack window\"", (char *) NULL
);
271 tkwin2
= Tk_NameToWindow(interp
, argv
[2], tkwin
);
272 if (tkwin2
== NULL
) {
275 packPtr
= GetPacker(tkwin2
);
276 if ((packPtr
!= NULL
) && (packPtr
->parentPtr
!= NULL
)) {
277 Tk_ManageGeometry(tkwin2
, (Tk_GeometryProc
*) NULL
,
280 Tk_UnmapWindow(packPtr
->tkwin
);
283 Tcl_AppendResult(interp
, "bad option \"", argv
[1],
284 "\": must be after, append, before, or info", (char *) NULL
);
291 *--------------------------------------------------------------
295 * This procedure is invoked by Tk_GeometryRequest for
296 * windows managed by the packer.
302 * Arranges for tkwin, and all its managed siblings, to
303 * be re-packed at the next idle point.
305 *--------------------------------------------------------------
311 ClientData clientData
, /* Packer's information about
312 * window that got new preferred
314 Tk_Window tkwin
/* Other Tk-related information
315 * about the window. */
318 register Packer
*packPtr
= (Packer
*) clientData
;
320 packPtr
= packPtr
->parentPtr
;
321 if (!(packPtr
->flags
& REQUESTED_REPACK
)) {
322 packPtr
->flags
|= REQUESTED_REPACK
;
323 Tk_DoWhenIdle(ArrangePacking
, (ClientData
) packPtr
);
328 *--------------------------------------------------------------
332 * This procedure is invoked (using the Tk_DoWhenIdle
333 * mechanism) to re-layout a set of windows managed by
334 * the packer. It is invoked at idle time so that a
335 * series of packer requests can be merged into a single
342 * The packed children of parentPtr may get resized or
345 *--------------------------------------------------------------
350 ClientData clientData
/* Structure describing parent
351 * whose children are to be
355 register Packer
*parentPtr
= (Packer
*) clientData
;
356 register Packer
*childPtr
;
357 int numExpX
, numExpY
; /* # of windows that are expandable in
359 int spareX
, spareY
; /* Amount of extra space to give to each
360 * expandable window. */
361 int leftOverX
, leftOverY
; /* Extra chunk of space to give to last
362 * expandable window. */
363 int cavityX
, cavityY
, cavityWidth
, cavityHeight
;
364 /* These variables keep track of the
365 * as-yet-unallocated space remaining in
366 * the middle of the parent window. */
367 int frameX
, frameY
, frameWidth
, frameHeight
;
368 /* These variables keep track of the frame
369 * allocated to the current window. */
370 int x
, y
, width
, height
; /* These variables are used to hold the
371 * actual geometry of the current window. */
372 int intBWidth
; /* Width of internal border in parent window,
374 int abort
; /* May get set to non-zero to abort this
375 * repacking operation. */
376 int maxWidth
, maxHeight
, tmp
;
378 parentPtr
->flags
&= ~REQUESTED_REPACK
;
381 * If the parent has no children anymore, then don't do anything
382 * at all: just leave the parent's size as-is.
385 if (parentPtr
->childPtr
== NULL
) {
390 * Abort any nested call to ArrangePacking for this window, since
391 * we'll do everything necessary here, and set up so this call
392 * can be aborted if necessary.
395 if (parentPtr
->abortPtr
!= NULL
) {
396 *parentPtr
->abortPtr
= 1;
398 parentPtr
->abortPtr
= &abort
;
400 Tk_Preserve((ClientData
) parentPtr
);
403 * Pass #1: scan all the children to figure out the total amount
404 * of space needed. Two separate widths and heights are computed.
406 * "Width" and "height" compute the minimum parent size to meet
407 * the needs of each window in the direction "where there is
408 * flexibility". For example, if a child is packed TOP, then
409 * y is the flexible direction: the child's requested height
410 * will determine its size. For this window x is the inflexible
411 * direction: the window's width will be determined by the amount
412 * of space left in the parent's cavity, not by the window's
413 * requested width. "Width" and "height" are needed in order to
414 * compute how much extra space there is, so that it can be divided
415 * among the windows that have the EXPAND flag.
417 * "MaxWidth" and "maxHeight" compute the minimum parent size to
418 * meet all the needs of every window in both directions, flexible
419 * or inflexible. These values are needed to make geometry requests
420 * of the parent's parent.
423 intBWidth
= Tk_InternalBorderWidth(parentPtr
->tkwin
);
424 width
= height
= maxWidth
= maxHeight
= 2*intBWidth
;
425 numExpX
= numExpY
= 0;
426 for (childPtr
= parentPtr
->childPtr
; childPtr
!= NULL
;
427 childPtr
= childPtr
->nextPtr
) {
428 if ((childPtr
->side
== TOP
) || (childPtr
->side
== BOTTOM
)) {
429 tmp
= Tk_ReqWidth(childPtr
->tkwin
) + childPtr
->doubleBw
430 + childPtr
->padX
+ width
;
431 if (tmp
> maxWidth
) {
434 height
+= Tk_ReqHeight(childPtr
->tkwin
) + childPtr
->doubleBw
436 if (childPtr
->flags
& EXPAND
) {
440 tmp
= Tk_ReqHeight(childPtr
->tkwin
) + childPtr
->doubleBw
441 + childPtr
->padY
+ height
;
442 if (tmp
> maxHeight
) {
445 width
+= Tk_ReqWidth(childPtr
->tkwin
) + childPtr
->doubleBw
447 if (childPtr
->flags
& EXPAND
) {
452 if (width
> maxWidth
) {
455 if (height
> maxHeight
) {
460 * If the total amount of space needed in the parent window has
461 * changed, then notify the next geometry manager up and requeue
462 * ourselves to start again after the parent has had a chance to
466 if ((maxWidth
!= Tk_ReqWidth(parentPtr
->tkwin
))
467 || (maxHeight
!= Tk_ReqHeight(parentPtr
->tkwin
))) {
468 Tk_GeometryRequest(parentPtr
->tkwin
, maxWidth
, maxHeight
);
469 parentPtr
->flags
|= REQUESTED_REPACK
;
470 Tk_DoWhenIdle(ArrangePacking
, (ClientData
) parentPtr
);
475 * If there is spare space, figure out how much of it goes to
476 * each of the windows that is expandable.
479 spareX
= Tk_Width(parentPtr
->tkwin
) - width
;
480 spareY
= Tk_Height(parentPtr
->tkwin
) - height
;
481 if ((spareX
<= 0) || (numExpX
== 0)) {
485 leftOverX
= spareX
% numExpX
;
488 if ((spareY
<= 0) || (numExpY
== 0)) {
492 leftOverY
= spareY
% numExpY
;
497 * Pass #2: scan the children a second time assigning
498 * new sizes. The "cavity" variables keep track of the
499 * unclaimed space in the cavity of the window; this
500 * shrinks inward as we allocate windows around the
501 * edges. The "frame" variables keep track of the space
502 * allocated to the current window and its frame. The
503 * current window is then placed somewhere inside the
504 * frame, depending on anchorPoint.
507 cavityX
= cavityY
= x
= y
= intBWidth
;
508 cavityWidth
= Tk_Width(parentPtr
->tkwin
) - 2*intBWidth
;
509 cavityHeight
= Tk_Height(parentPtr
->tkwin
) - 2*intBWidth
;
510 for (childPtr
= parentPtr
->childPtr
; childPtr
!= NULL
;
511 childPtr
= childPtr
->nextPtr
) {
512 if ((childPtr
->side
== TOP
) || (childPtr
->side
== BOTTOM
)) {
513 frameWidth
= cavityWidth
;
514 frameHeight
= Tk_ReqHeight(childPtr
->tkwin
) + childPtr
->padY
515 + childPtr
->doubleBw
;
516 if (childPtr
->flags
& EXPAND
) {
517 frameHeight
+= spareY
;
520 frameHeight
+= leftOverY
;
523 cavityHeight
-= frameHeight
;
524 if (cavityHeight
< 0) {
525 frameHeight
+= cavityHeight
;
529 if (childPtr
->side
== TOP
) {
531 cavityY
+= frameHeight
;
533 frameY
= cavityY
+ cavityHeight
;
536 frameHeight
= cavityHeight
;
537 frameWidth
= Tk_ReqWidth(childPtr
->tkwin
) + childPtr
->padX
538 + childPtr
->doubleBw
;
539 if (childPtr
->flags
& EXPAND
) {
540 frameWidth
+= spareX
;
543 frameWidth
+= leftOverX
;
546 cavityWidth
-= frameWidth
;
547 if (cavityWidth
< 0) {
548 frameWidth
+= cavityWidth
;
552 if (childPtr
->side
== LEFT
) {
554 cavityX
+= frameWidth
;
556 frameX
= cavityX
+ cavityWidth
;
561 * Now that we've got the size of the frame for the window,
562 * compute the window's actual size and location using the
563 * fill and frame factors.
566 width
= Tk_ReqWidth(childPtr
->tkwin
) + childPtr
->doubleBw
;
567 if ((childPtr
->flags
& FILLX
) || (width
> frameWidth
)) {
570 height
= Tk_ReqHeight(childPtr
->tkwin
) + childPtr
->doubleBw
;
571 if ((childPtr
->flags
& FILLY
) || (height
> frameHeight
)) {
572 height
= frameHeight
;
574 switch (childPtr
->anchorPoint
) {
576 x
= frameX
+ (frameWidth
- width
)/2;
580 x
= frameX
+ frameWidth
- width
;
584 x
= frameX
+ frameWidth
- width
;
585 y
= frameY
+ (frameHeight
- height
)/2;
588 x
= frameX
+ frameWidth
- width
;
589 y
= frameY
+ frameHeight
- height
;
592 x
= frameX
+ (frameWidth
- width
)/2;
593 y
= frameY
+ frameHeight
- height
;
597 y
= frameY
+ frameHeight
- height
;
601 y
= frameY
+ (frameHeight
- height
)/2;
607 case TK_ANCHOR_CENTER
:
608 x
= frameX
+ (frameWidth
- width
)/2;
609 y
= frameY
+ (frameHeight
- height
)/2;
612 panic("bad frame factor in ArrangePacking");
614 width
-= childPtr
->doubleBw
;
615 height
-= childPtr
->doubleBw
;
618 * If the window is too small to be interesting then
619 * unmap it. Otherwise configure it and then make sure
623 if ((width
<= 0) || (height
<= 0)) {
624 Tk_UnmapWindow(childPtr
->tkwin
);
626 if ((x
!= Tk_X(childPtr
->tkwin
))
627 || (y
!= Tk_Y(childPtr
->tkwin
))
628 || (width
!= Tk_Width(childPtr
->tkwin
))
629 || (height
!= Tk_Height(childPtr
->tkwin
))) {
630 Tk_MoveResizeWindow(childPtr
->tkwin
, x
, y
,
631 (unsigned int) width
, (unsigned int) height
);
636 Tk_MapWindow(childPtr
->tkwin
);
640 * Changes to the window's structure could cause almost anything
641 * to happen, including deleting the parent or child. If this
642 * happens, we'll be told to abort.
651 parentPtr
->abortPtr
= NULL
;
652 Tk_Release((ClientData
) parentPtr
);
656 *--------------------------------------------------------------
660 * This internal procedure is used to locate a Packer
661 * structure for a given window, creating one if one
662 * doesn't exist already.
665 * The return value is a pointer to the Packer structure
666 * corresponding to tkwin.
669 * A new packer structure may be created. If so, then
670 * a callback is set up to clean things up when the
673 *--------------------------------------------------------------
678 Tk_Window tkwin
/* Token for window for which
679 * packer structure is desired. */
682 register Packer
*packPtr
;
688 Tcl_InitHashTable(&packerHashTable
, TCL_ONE_WORD_KEYS
);
692 * See if there's already packer for this window. If not,
693 * then create a new one.
696 hPtr
= Tcl_CreateHashEntry(&packerHashTable
, (char *) tkwin
, &new);
698 return (Packer
*) Tcl_GetHashValue(hPtr
);
700 packPtr
= (Packer
*) ckalloc(sizeof(Packer
));
701 packPtr
->tkwin
= tkwin
;
702 packPtr
->parentPtr
= NULL
;
703 packPtr
->nextPtr
= NULL
;
704 packPtr
->childPtr
= NULL
;
706 packPtr
->anchorPoint
= TK_ANCHOR_CENTER
;
707 packPtr
->padX
= packPtr
->padY
= 0;
708 packPtr
->doubleBw
= 2*Tk_Changes(tkwin
)->border_width
;
709 packPtr
->abortPtr
= NULL
;
711 Tcl_SetHashValue(hPtr
, packPtr
);
712 Tk_CreateEventHandler(tkwin
, StructureNotifyMask
,
713 PackStructureProc
, (ClientData
) packPtr
);
718 *--------------------------------------------------------------
722 * This procedure does most of the real work of adding
723 * one or more windows into the packing order for its parent.
726 * A standard Tcl return value.
729 * The geometry of the specified windows may change, both now and
730 * again in the future.
732 *--------------------------------------------------------------
737 Tcl_Interp
*interp
, /* Interpreter for error reporting. */
738 Packer
*prevPtr
, /* Pack windows in argv just after this
739 * window; NULL means pack as first
740 * child of parentPtr. */
741 Packer
*parentPtr
, /* Parent in which to pack windows. */
742 int argc
, /* Number of elements in argv. */
743 char **argv
/* Array of lists, each containing 2
744 * elements: window name and side
745 * against which to pack. */
748 register Packer
*packPtr
;
750 int length
, optionCount
;
756 * Iterate over all of the window specifiers, each consisting of
757 * two arguments. The first argument contains the window name and
758 * the additional arguments contain options such as "top" or
762 for ( ; argc
> 0; argc
-= 2, argv
+= 2, prevPtr
= packPtr
) {
764 Tcl_AppendResult(interp
, "wrong # args: window \"",
765 argv
[0], "\" should be followed by options",
771 * Find the packer for the window to be packed, and make sure
772 * that the window in which it will be packed is its parent.
775 tkwin
= Tk_NameToWindow(interp
, argv
[0], parentPtr
->tkwin
);
779 if (Tk_Parent(tkwin
) != parentPtr
->tkwin
) {
780 Tcl_AppendResult(interp
, "tried to pack \"",
781 argv
[0], "\" in window that isn't its parent",
785 packPtr
= GetPacker(tkwin
);
788 * Process options for this window.
791 if (Tcl_SplitList(interp
, argv
[1], &optionCount
, &options
) != TCL_OK
) {
795 packPtr
->anchorPoint
= TK_ANCHOR_CENTER
;
796 packPtr
->padX
= packPtr
->padY
= 0;
797 packPtr
->flags
&= ~(FILLX
|FILLY
|EXPAND
);
798 for (index
= 0 ; index
< optionCount
; index
++) {
799 char *curOpt
= options
[index
];
802 length
= strlen(curOpt
);
805 && (strncmp(curOpt
, "top", length
)) == 0) {
807 } else if ((c
== 'b')
808 && (strncmp(curOpt
, "bottom", length
)) == 0) {
809 packPtr
->side
= BOTTOM
;
810 } else if ((c
== 'l')
811 && (strncmp(curOpt
, "left", length
)) == 0) {
812 packPtr
->side
= LEFT
;
813 } else if ((c
== 'r')
814 && (strncmp(curOpt
, "right", length
)) == 0) {
815 packPtr
->side
= RIGHT
;
816 } else if ((c
== 'e')
817 && (strncmp(curOpt
, "expand", length
)) == 0) {
818 packPtr
->flags
|= EXPAND
;
819 } else if ((c
== 'f')
820 && (strcmp(curOpt
, "fill")) == 0) {
821 packPtr
->flags
|= FILLX
|FILLY
;
822 } else if ((length
== 5) && (strcmp(curOpt
, "fillx")) == 0) {
823 packPtr
->flags
|= FILLX
;
824 } else if ((length
== 5) && (strcmp(curOpt
, "filly")) == 0) {
825 packPtr
->flags
|= FILLY
;
826 } else if ((c
== 'p') && (strcmp(curOpt
, "padx")) == 0) {
827 if (optionCount
< (index
+2)) {
829 Tcl_AppendResult(interp
, "wrong # args: \"", curOpt
,
830 "\" option must be followed by count",
834 if ((Tcl_GetInt(interp
, options
[index
+1], &packPtr
->padX
)
835 != TCL_OK
) || (packPtr
->padX
< 0)) {
837 Tcl_AppendResult(interp
, "bad pad value \"",
838 options
[index
+1], "\": must be positive integer",
843 } else if ((c
== 'p') && (strcmp(curOpt
, "pady")) == 0) {
844 if (optionCount
< (index
+2)) {
847 if ((Tcl_GetInt(interp
, options
[index
+1], &packPtr
->padY
)
848 != TCL_OK
) || (packPtr
->padY
< 0)) {
852 } else if ((c
== 'f') && (length
> 1)
853 && (strncmp(curOpt
, "frame", length
) == 0)) {
854 if (optionCount
< (index
+2)) {
855 Tcl_AppendResult(interp
, "wrong # args: \"frame\" ",
856 "option must be followed by anchor point",
860 if (Tk_GetAnchor(interp
, options
[index
+1],
861 &packPtr
->anchorPoint
) != TCL_OK
) {
866 Tcl_AppendResult(interp
, "bad option \"", curOpt
,
867 "\": should be top, bottom, left, right, ",
868 "expand, fill, fillx, filly, padx, pady, or frame",
874 if (packPtr
!= prevPtr
) {
877 * Unpack this window if it's currently packed.
880 if (packPtr
->parentPtr
!= NULL
) {
885 * Add the window in the correct place in its parent's
886 * packing order, then make sure that the window is
890 packPtr
->parentPtr
= parentPtr
;
891 if (prevPtr
== NULL
) {
892 packPtr
->nextPtr
= parentPtr
->childPtr
;
893 parentPtr
->childPtr
= packPtr
;
895 packPtr
->nextPtr
= prevPtr
->nextPtr
;
896 prevPtr
->nextPtr
= packPtr
;
898 Tk_ManageGeometry(tkwin
, PackReqProc
, (ClientData
) packPtr
);
900 ckfree((char *) options
);
904 * Arrange for the parent to be re-packed at the first
908 if (parentPtr
->abortPtr
!= NULL
) {
909 *parentPtr
->abortPtr
= 1;
911 if (!(parentPtr
->flags
& REQUESTED_REPACK
)) {
912 parentPtr
->flags
|= REQUESTED_REPACK
;
913 Tk_DoWhenIdle(ArrangePacking
, (ClientData
) parentPtr
);
918 ckfree((char *) options
);
923 *----------------------------------------------------------------------
927 * Remove a packer from its parent's list of children.
933 * The parent will be scheduled for repacking.
935 *----------------------------------------------------------------------
940 register Packer
*packPtr
/* Window to unlink. */
943 register Packer
*parentPtr
, *packPtr2
;
945 parentPtr
= packPtr
->parentPtr
;
946 if (parentPtr
== NULL
) {
949 if (parentPtr
->childPtr
== packPtr
) {
950 parentPtr
->childPtr
= packPtr
->nextPtr
;
952 for (packPtr2
= parentPtr
->childPtr
; ; packPtr2
= packPtr2
->nextPtr
) {
953 if (packPtr2
== NULL
) {
954 panic("Unlink couldn't find previous window");
956 if (packPtr2
->nextPtr
== packPtr
) {
957 packPtr2
->nextPtr
= packPtr
->nextPtr
;
962 if (!(parentPtr
->flags
& REQUESTED_REPACK
)) {
963 parentPtr
->flags
|= REQUESTED_REPACK
;
964 Tk_DoWhenIdle(ArrangePacking
, (ClientData
) parentPtr
);
966 if (parentPtr
->abortPtr
!= NULL
) {
967 *parentPtr
->abortPtr
= 1;
970 packPtr
->parentPtr
= NULL
;
974 *----------------------------------------------------------------------
978 * This procedure is invoked by Tk_EventuallyFree or Tk_Release
979 * to clean up the internal structure of a packer at a safe time
980 * (when no-one is using it anymore).
986 * Everything associated with the packer is freed up.
988 *----------------------------------------------------------------------
993 ClientData clientData
/* Info about packed window that
997 register Packer
*packPtr
= (Packer
*) clientData
;
998 ckfree((char *) packPtr
);
1002 *----------------------------------------------------------------------
1004 * PackStructureProc --
1006 * This procedure is invoked by the Tk event dispatcher in response
1007 * to StructureNotify events.
1013 * If a window was just deleted, clean up all its packer-related
1014 * information. If it was just resized, repack its children, if
1017 *----------------------------------------------------------------------
1022 ClientData clientData
, /* Our information about window
1023 * referred to by eventPtr. */
1024 XEvent
*eventPtr
/* Describes what just happened. */
1027 register Packer
*packPtr
= (Packer
*) clientData
;
1028 if (eventPtr
->type
== ConfigureNotify
) {
1029 if ((packPtr
->childPtr
!= NULL
)
1030 && !(packPtr
->flags
& REQUESTED_REPACK
)) {
1031 packPtr
->flags
|= REQUESTED_REPACK
;
1032 Tk_DoWhenIdle(ArrangePacking
, (ClientData
) packPtr
);
1034 if (packPtr
->doubleBw
!= 2*Tk_Changes(packPtr
->tkwin
)->border_width
) {
1035 if ((packPtr
->parentPtr
!= NULL
)
1036 && !(packPtr
->parentPtr
->flags
& REQUESTED_REPACK
)) {
1037 packPtr
->doubleBw
= 2*Tk_Changes(packPtr
->tkwin
)->border_width
;
1038 packPtr
->parentPtr
->flags
|= REQUESTED_REPACK
;
1039 Tk_DoWhenIdle(ArrangePacking
, (ClientData
) packPtr
->parentPtr
);
1042 } else if (eventPtr
->type
== DestroyNotify
) {
1043 register Packer
*packPtr2
;
1045 if (packPtr
->parentPtr
!= NULL
) {
1048 for (packPtr2
= packPtr
->childPtr
; packPtr2
!= NULL
;
1049 packPtr2
= packPtr2
->nextPtr
) {
1050 packPtr2
->parentPtr
= NULL
;
1051 packPtr2
->nextPtr
= NULL
;
1053 Tcl_DeleteHashEntry(Tcl_FindHashEntry(&packerHashTable
,
1054 (char *) packPtr
->tkwin
));
1055 if (packPtr
->flags
& REQUESTED_REPACK
) {
1056 Tk_CancelIdleCall(ArrangePacking
, (ClientData
) packPtr
);
1058 packPtr
->tkwin
= NULL
;
1059 Tk_EventuallyFree((ClientData
) packPtr
, DestroyPacker
);