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 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 *--------------------------------------------------------------
133 Tk_PackCmd(clientData
, interp
, argc
, argv
)
134 ClientData clientData
; /* Main window associated with
136 Tcl_Interp
*interp
; /* Current interpreter. */
137 int argc
; /* Number of arguments. */
138 char **argv
; /* Argument strings. */
140 Tk_Window tkwin
= (Tk_Window
) clientData
;
145 Tcl_AppendResult(interp
, "wrong # args: should be \"",
146 argv
[0], " option arg ?arg ...?\"", (char *) NULL
);
150 length
= strlen(argv
[1]);
151 if ((c
== 'a') && (length
>= 2)
152 && (strncmp(argv
[1], "after", length
) == 0)) {
156 tkwin2
= Tk_NameToWindow(interp
, argv
[2], tkwin
);
157 if (tkwin2
== NULL
) {
160 prevPtr
= GetPacker(tkwin2
);
161 if (prevPtr
->parentPtr
== NULL
) {
162 Tcl_AppendResult(interp
, "window \"", argv
[2],
163 "\" isn't packed", (char *) NULL
);
166 return PackAfter(interp
, prevPtr
, prevPtr
->parentPtr
, argc
-3, argv
+3);
167 } else if ((c
== 'a') && (length
>= 2)
168 && (strncmp(argv
[1], "append", length
) == 0)) {
170 register Packer
*prevPtr
;
173 tkwin2
= Tk_NameToWindow(interp
, argv
[2], tkwin
);
174 if (tkwin2
== NULL
) {
177 parentPtr
= GetPacker(tkwin2
);
178 prevPtr
= parentPtr
->childPtr
;
179 if (prevPtr
!= NULL
) {
180 while (prevPtr
->nextPtr
!= NULL
) {
181 prevPtr
= prevPtr
->nextPtr
;
184 return PackAfter(interp
, prevPtr
, parentPtr
, argc
-3, argv
+3);
185 } else if ((c
== 'b') && (strncmp(argv
[1], "before", length
) == 0)) {
186 Packer
*packPtr
, *parentPtr
;
187 register Packer
*prevPtr
;
190 tkwin2
= Tk_NameToWindow(interp
, argv
[2], tkwin
);
191 if (tkwin2
== NULL
) {
194 packPtr
= GetPacker(tkwin2
);
195 if (packPtr
->parentPtr
== NULL
) {
196 Tcl_AppendResult(interp
, "window \"", argv
[2],
197 "\" isn't packed", (char *) NULL
);
200 parentPtr
= packPtr
->parentPtr
;
201 prevPtr
= parentPtr
->childPtr
;
202 if (prevPtr
== packPtr
) {
205 for ( ; ; prevPtr
= prevPtr
->nextPtr
) {
206 if (prevPtr
== NULL
) {
207 panic("\"pack before\" couldn't find predecessor");
209 if (prevPtr
->nextPtr
== packPtr
) {
214 return PackAfter(interp
, prevPtr
, parentPtr
, argc
-3, argv
+3);
215 } else if ((c
== 'i') && (strncmp(argv
[1], "info", length
) == 0)) {
217 register Packer
*packPtr
;
220 static char *sideNames
[] = {"top", "bottom", "left", "right"};
223 Tcl_AppendResult(interp
, "wrong # args: should be \"",
224 argv
[0], " info window\"", (char *) NULL
);
227 tkwin2
= Tk_NameToWindow(interp
, argv
[2], tkwin
);
228 if (tkwin2
== NULL
) {
231 packPtr
= GetPacker(tkwin2
);
233 for (packPtr
= packPtr
->childPtr
; packPtr
!= NULL
;
234 packPtr
= packPtr
->nextPtr
) {
235 Tcl_AppendResult(interp
, prefix
, Tk_PathName(packPtr
->tkwin
),
236 " {", sideNames
[(int) packPtr
->side
],
237 " frame ", Tk_NameOfAnchor(packPtr
->anchorPoint
),
239 if (packPtr
->padX
!= 0) {
240 sprintf(tmp
, "%d", packPtr
->padX
);
241 Tcl_AppendResult(interp
, " padx ", tmp
, (char *) NULL
);
243 if (packPtr
->padY
!= 0) {
244 sprintf(tmp
, "%d", packPtr
->padY
);
245 Tcl_AppendResult(interp
, " pady ", tmp
, (char *) NULL
);
247 if (packPtr
->flags
& EXPAND
) {
248 Tcl_AppendResult(interp
, " expand", (char *) NULL
);
250 if ((packPtr
->flags
& (FILLX
|FILLY
)) == (FILLX
|FILLY
)) {
251 Tcl_AppendResult(interp
, " fill", (char *) NULL
);
252 } else if (packPtr
->flags
& FILLX
) {
253 Tcl_AppendResult(interp
, " fillx", (char *) NULL
);
254 } else if (packPtr
->flags
& FILLY
) {
255 Tcl_AppendResult(interp
, " filly", (char *) NULL
);
257 Tcl_AppendResult(interp
, "}", (char *) NULL
);
261 } else if ((c
== 'u') && (strncmp(argv
[1], "unpack", length
) == 0)) {
266 Tcl_AppendResult(interp
, "wrong # args: should be \"",
267 argv
[0], " unpack window\"", (char *) NULL
);
270 tkwin2
= Tk_NameToWindow(interp
, argv
[2], tkwin
);
271 if (tkwin2
== NULL
) {
274 packPtr
= GetPacker(tkwin2
);
275 if ((packPtr
!= NULL
) && (packPtr
->parentPtr
!= NULL
)) {
276 Tk_ManageGeometry(tkwin2
, (Tk_GeometryProc
*) NULL
,
279 Tk_UnmapWindow(packPtr
->tkwin
);
282 Tcl_AppendResult(interp
, "bad option \"", argv
[1],
283 "\": must be after, append, before, or info", (char *) NULL
);
290 *--------------------------------------------------------------
294 * This procedure is invoked by Tk_GeometryRequest for
295 * windows managed by the packer.
301 * Arranges for tkwin, and all its managed siblings, to
302 * be re-packed at the next idle point.
304 *--------------------------------------------------------------
309 PackReqProc(clientData
, tkwin
)
310 ClientData clientData
; /* Packer's information about
311 * window that got new preferred
313 Tk_Window tkwin
; /* Other Tk-related information
314 * about the window. */
316 register Packer
*packPtr
= (Packer
*) clientData
;
318 packPtr
= packPtr
->parentPtr
;
319 if (!(packPtr
->flags
& REQUESTED_REPACK
)) {
320 packPtr
->flags
|= REQUESTED_REPACK
;
321 Tk_DoWhenIdle(ArrangePacking
, (ClientData
) packPtr
);
326 *--------------------------------------------------------------
330 * This procedure is invoked (using the Tk_DoWhenIdle
331 * mechanism) to re-layout a set of windows managed by
332 * the packer. It is invoked at idle time so that a
333 * series of packer requests can be merged into a single
340 * The packed children of parentPtr may get resized or
343 *--------------------------------------------------------------
347 ArrangePacking(clientData
)
348 ClientData clientData
; /* Structure describing parent
349 * whose children are to be
352 register Packer
*parentPtr
= (Packer
*) clientData
;
353 register Packer
*childPtr
;
354 int numExpX
, numExpY
; /* # of windows that are expandable in
356 int spareX
, spareY
; /* Amount of extra space to give to each
357 * expandable window. */
358 int leftOverX
, leftOverY
; /* Extra chunk of space to give to last
359 * expandable window. */
360 int cavityX
, cavityY
, cavityWidth
, cavityHeight
;
361 /* These variables keep track of the
362 * as-yet-unallocated space remaining in
363 * the middle of the parent window. */
364 int frameX
, frameY
, frameWidth
, frameHeight
;
365 /* These variables keep track of the frame
366 * allocated to the current window. */
367 int x
, y
, width
, height
; /* These variables are used to hold the
368 * actual geometry of the current window. */
369 int intBWidth
; /* Width of internal border in parent window,
371 int abort
; /* May get set to non-zero to abort this
372 * repacking operation. */
373 int maxWidth
, maxHeight
, tmp
;
375 parentPtr
->flags
&= ~REQUESTED_REPACK
;
378 * If the parent has no children anymore, then don't do anything
379 * at all: just leave the parent's size as-is.
382 if (parentPtr
->childPtr
== NULL
) {
387 * Abort any nested call to ArrangePacking for this window, since
388 * we'll do everything necessary here, and set up so this call
389 * can be aborted if necessary.
392 if (parentPtr
->abortPtr
!= NULL
) {
393 *parentPtr
->abortPtr
= 1;
395 parentPtr
->abortPtr
= &abort
;
397 Tk_Preserve((ClientData
) parentPtr
);
400 * Pass #1: scan all the children to figure out the total amount
401 * of space needed. Two separate widths and heights are computed.
403 * "Width" and "height" compute the minimum parent size to meet
404 * the needs of each window in the direction "where there is
405 * flexibility". For example, if a child is packed TOP, then
406 * y is the flexible direction: the child's requested height
407 * will determine its size. For this window x is the inflexible
408 * direction: the window's width will be determined by the amount
409 * of space left in the parent's cavity, not by the window's
410 * requested width. "Width" and "height" are needed in order to
411 * compute how much extra space there is, so that it can be divided
412 * among the windows that have the EXPAND flag.
414 * "MaxWidth" and "maxHeight" compute the minimum parent size to
415 * meet all the needs of every window in both directions, flexible
416 * or inflexible. These values are needed to make geometry requests
417 * of the parent's parent.
420 intBWidth
= Tk_InternalBorderWidth(parentPtr
->tkwin
);
421 width
= height
= maxWidth
= maxHeight
= 2*intBWidth
;
422 numExpX
= numExpY
= 0;
423 for (childPtr
= parentPtr
->childPtr
; childPtr
!= NULL
;
424 childPtr
= childPtr
->nextPtr
) {
425 if ((childPtr
->side
== TOP
) || (childPtr
->side
== BOTTOM
)) {
426 tmp
= Tk_ReqWidth(childPtr
->tkwin
) + childPtr
->doubleBw
427 + childPtr
->padX
+ width
;
428 if (tmp
> maxWidth
) {
431 height
+= Tk_ReqHeight(childPtr
->tkwin
) + childPtr
->doubleBw
433 if (childPtr
->flags
& EXPAND
) {
437 tmp
= Tk_ReqHeight(childPtr
->tkwin
) + childPtr
->doubleBw
438 + childPtr
->padY
+ height
;
439 if (tmp
> maxHeight
) {
442 width
+= Tk_ReqWidth(childPtr
->tkwin
) + childPtr
->doubleBw
444 if (childPtr
->flags
& EXPAND
) {
449 if (width
> maxWidth
) {
452 if (height
> maxHeight
) {
457 * If the total amount of space needed in the parent window has
458 * changed, then notify the next geometry manager up and requeue
459 * ourselves to start again after the parent has had a chance to
463 if ((maxWidth
!= Tk_ReqWidth(parentPtr
->tkwin
))
464 || (maxHeight
!= Tk_ReqHeight(parentPtr
->tkwin
))) {
465 Tk_GeometryRequest(parentPtr
->tkwin
, maxWidth
, maxHeight
);
466 parentPtr
->flags
|= REQUESTED_REPACK
;
467 Tk_DoWhenIdle(ArrangePacking
, (ClientData
) parentPtr
);
472 * If there is spare space, figure out how much of it goes to
473 * each of the windows that is expandable.
476 spareX
= Tk_Width(parentPtr
->tkwin
) - width
;
477 spareY
= Tk_Height(parentPtr
->tkwin
) - height
;
478 if ((spareX
<= 0) || (numExpX
== 0)) {
482 leftOverX
= spareX
% numExpX
;
485 if ((spareY
<= 0) || (numExpY
== 0)) {
489 leftOverY
= spareY
% numExpY
;
494 * Pass #2: scan the children a second time assigning
495 * new sizes. The "cavity" variables keep track of the
496 * unclaimed space in the cavity of the window; this
497 * shrinks inward as we allocate windows around the
498 * edges. The "frame" variables keep track of the space
499 * allocated to the current window and its frame. The
500 * current window is then placed somewhere inside the
501 * frame, depending on anchorPoint.
504 cavityX
= cavityY
= x
= y
= intBWidth
;
505 cavityWidth
= Tk_Width(parentPtr
->tkwin
) - 2*intBWidth
;
506 cavityHeight
= Tk_Height(parentPtr
->tkwin
) - 2*intBWidth
;
507 for (childPtr
= parentPtr
->childPtr
; childPtr
!= NULL
;
508 childPtr
= childPtr
->nextPtr
) {
509 if ((childPtr
->side
== TOP
) || (childPtr
->side
== BOTTOM
)) {
510 frameWidth
= cavityWidth
;
511 frameHeight
= Tk_ReqHeight(childPtr
->tkwin
) + childPtr
->padY
512 + childPtr
->doubleBw
;
513 if (childPtr
->flags
& EXPAND
) {
514 frameHeight
+= spareY
;
517 frameHeight
+= leftOverY
;
520 cavityHeight
-= frameHeight
;
521 if (cavityHeight
< 0) {
522 frameHeight
+= cavityHeight
;
526 if (childPtr
->side
== TOP
) {
528 cavityY
+= frameHeight
;
530 frameY
= cavityY
+ cavityHeight
;
533 frameHeight
= cavityHeight
;
534 frameWidth
= Tk_ReqWidth(childPtr
->tkwin
) + childPtr
->padX
535 + childPtr
->doubleBw
;
536 if (childPtr
->flags
& EXPAND
) {
537 frameWidth
+= spareX
;
540 frameWidth
+= leftOverX
;
543 cavityWidth
-= frameWidth
;
544 if (cavityWidth
< 0) {
545 frameWidth
+= cavityWidth
;
549 if (childPtr
->side
== LEFT
) {
551 cavityX
+= frameWidth
;
553 frameX
= cavityX
+ cavityWidth
;
558 * Now that we've got the size of the frame for the window,
559 * compute the window's actual size and location using the
560 * fill and frame factors.
563 width
= Tk_ReqWidth(childPtr
->tkwin
) + childPtr
->doubleBw
;
564 if ((childPtr
->flags
& FILLX
) || (width
> frameWidth
)) {
567 height
= Tk_ReqHeight(childPtr
->tkwin
) + childPtr
->doubleBw
;
568 if ((childPtr
->flags
& FILLY
) || (height
> frameHeight
)) {
569 height
= frameHeight
;
571 switch (childPtr
->anchorPoint
) {
573 x
= frameX
+ (frameWidth
- width
)/2;
577 x
= frameX
+ frameWidth
- width
;
581 x
= frameX
+ frameWidth
- width
;
582 y
= frameY
+ (frameHeight
- height
)/2;
585 x
= frameX
+ frameWidth
- width
;
586 y
= frameY
+ frameHeight
- height
;
589 x
= frameX
+ (frameWidth
- width
)/2;
590 y
= frameY
+ frameHeight
- height
;
594 y
= frameY
+ frameHeight
- height
;
598 y
= frameY
+ (frameHeight
- height
)/2;
604 case TK_ANCHOR_CENTER
:
605 x
= frameX
+ (frameWidth
- width
)/2;
606 y
= frameY
+ (frameHeight
- height
)/2;
609 panic("bad frame factor in ArrangePacking");
611 width
-= childPtr
->doubleBw
;
612 height
-= childPtr
->doubleBw
;
615 * If the window is too small to be interesting then
616 * unmap it. Otherwise configure it and then make sure
620 if ((width
<= 0) || (height
<= 0)) {
621 Tk_UnmapWindow(childPtr
->tkwin
);
623 if ((x
!= Tk_X(childPtr
->tkwin
))
624 || (y
!= Tk_Y(childPtr
->tkwin
))
625 || (width
!= Tk_Width(childPtr
->tkwin
))
626 || (height
!= Tk_Height(childPtr
->tkwin
))) {
627 Tk_MoveResizeWindow(childPtr
->tkwin
, x
, y
,
628 (unsigned int) width
, (unsigned int) height
);
633 Tk_MapWindow(childPtr
->tkwin
);
637 * Changes to the window's structure could cause almost anything
638 * to happen, including deleting the parent or child. If this
639 * happens, we'll be told to abort.
648 parentPtr
->abortPtr
= NULL
;
649 Tk_Release((ClientData
) parentPtr
);
653 *--------------------------------------------------------------
657 * This internal procedure is used to locate a Packer
658 * structure for a given window, creating one if one
659 * doesn't exist already.
662 * The return value is a pointer to the Packer structure
663 * corresponding to tkwin.
666 * A new packer structure may be created. If so, then
667 * a callback is set up to clean things up when the
670 *--------------------------------------------------------------
675 Tk_Window tkwin
; /* Token for window for which
676 * packer structure is desired. */
678 register Packer
*packPtr
;
684 Tcl_InitHashTable(&packerHashTable
, TCL_ONE_WORD_KEYS
);
688 * See if there's already packer for this window. If not,
689 * then create a new one.
692 hPtr
= Tcl_CreateHashEntry(&packerHashTable
, (char *) tkwin
, &new);
694 return (Packer
*) Tcl_GetHashValue(hPtr
);
696 packPtr
= (Packer
*) ckalloc(sizeof(Packer
));
697 packPtr
->tkwin
= tkwin
;
698 packPtr
->parentPtr
= NULL
;
699 packPtr
->nextPtr
= NULL
;
700 packPtr
->childPtr
= NULL
;
702 packPtr
->anchorPoint
= TK_ANCHOR_CENTER
;
703 packPtr
->padX
= packPtr
->padY
= 0;
704 packPtr
->doubleBw
= 2*Tk_Changes(tkwin
)->border_width
;
705 packPtr
->abortPtr
= NULL
;
707 Tcl_SetHashValue(hPtr
, packPtr
);
708 Tk_CreateEventHandler(tkwin
, StructureNotifyMask
,
709 PackStructureProc
, (ClientData
) packPtr
);
714 *--------------------------------------------------------------
718 * This procedure does most of the real work of adding
719 * one or more windows into the packing order for its parent.
722 * A standard Tcl return value.
725 * The geometry of the specified windows may change, both now and
726 * again in the future.
728 *--------------------------------------------------------------
732 PackAfter(interp
, prevPtr
, parentPtr
, argc
, argv
)
733 Tcl_Interp
*interp
; /* Interpreter for error reporting. */
734 Packer
*prevPtr
; /* Pack windows in argv just after this
735 * window; NULL means pack as first
736 * child of parentPtr. */
737 Packer
*parentPtr
; /* Parent in which to pack windows. */
738 int argc
; /* Number of elements in argv. */
739 char **argv
; /* Array of lists, each containing 2
740 * elements: window name and side
741 * against which to pack. */
743 register Packer
*packPtr
;
745 int length
, optionCount
;
751 * Iterate over all of the window specifiers, each consisting of
752 * two arguments. The first argument contains the window name and
753 * the additional arguments contain options such as "top" or
757 for ( ; argc
> 0; argc
-= 2, argv
+= 2, prevPtr
= packPtr
) {
759 Tcl_AppendResult(interp
, "wrong # args: window \"",
760 argv
[0], "\" should be followed by options",
766 * Find the packer for the window to be packed, and make sure
767 * that the window in which it will be packed is its parent.
770 tkwin
= Tk_NameToWindow(interp
, argv
[0], parentPtr
->tkwin
);
774 if (Tk_Parent(tkwin
) != parentPtr
->tkwin
) {
775 Tcl_AppendResult(interp
, "tried to pack \"",
776 argv
[0], "\" in window that isn't its parent",
780 packPtr
= GetPacker(tkwin
);
783 * Process options for this window.
786 if (Tcl_SplitList(interp
, argv
[1], &optionCount
, &options
) != TCL_OK
) {
790 packPtr
->anchorPoint
= TK_ANCHOR_CENTER
;
791 packPtr
->padX
= packPtr
->padY
= 0;
792 packPtr
->flags
&= ~(FILLX
|FILLY
|EXPAND
);
793 for (index
= 0 ; index
< optionCount
; index
++) {
794 char *curOpt
= options
[index
];
797 length
= strlen(curOpt
);
800 && (strncmp(curOpt
, "top", length
)) == 0) {
802 } else if ((c
== 'b')
803 && (strncmp(curOpt
, "bottom", length
)) == 0) {
804 packPtr
->side
= BOTTOM
;
805 } else if ((c
== 'l')
806 && (strncmp(curOpt
, "left", length
)) == 0) {
807 packPtr
->side
= LEFT
;
808 } else if ((c
== 'r')
809 && (strncmp(curOpt
, "right", length
)) == 0) {
810 packPtr
->side
= RIGHT
;
811 } else if ((c
== 'e')
812 && (strncmp(curOpt
, "expand", length
)) == 0) {
813 packPtr
->flags
|= EXPAND
;
814 } else if ((c
== 'f')
815 && (strcmp(curOpt
, "fill")) == 0) {
816 packPtr
->flags
|= FILLX
|FILLY
;
817 } else if ((length
== 5) && (strcmp(curOpt
, "fillx")) == 0) {
818 packPtr
->flags
|= FILLX
;
819 } else if ((length
== 5) && (strcmp(curOpt
, "filly")) == 0) {
820 packPtr
->flags
|= FILLY
;
821 } else if ((c
== 'p') && (strcmp(curOpt
, "padx")) == 0) {
822 if (optionCount
< (index
+2)) {
824 Tcl_AppendResult(interp
, "wrong # args: \"", curOpt
,
825 "\" option must be followed by count",
829 if ((Tcl_GetInt(interp
, options
[index
+1], &packPtr
->padX
)
830 != TCL_OK
) || (packPtr
->padX
< 0)) {
832 Tcl_AppendResult(interp
, "bad pad value \"",
833 options
[index
+1], "\": must be positive integer",
838 } else if ((c
== 'p') && (strcmp(curOpt
, "pady")) == 0) {
839 if (optionCount
< (index
+2)) {
842 if ((Tcl_GetInt(interp
, options
[index
+1], &packPtr
->padY
)
843 != TCL_OK
) || (packPtr
->padY
< 0)) {
847 } else if ((c
== 'f') && (length
> 1)
848 && (strncmp(curOpt
, "frame", length
) == 0)) {
849 if (optionCount
< (index
+2)) {
850 Tcl_AppendResult(interp
, "wrong # args: \"frame\" ",
851 "option must be followed by anchor point",
855 if (Tk_GetAnchor(interp
, options
[index
+1],
856 &packPtr
->anchorPoint
) != TCL_OK
) {
861 Tcl_AppendResult(interp
, "bad option \"", curOpt
,
862 "\": should be top, bottom, left, right, ",
863 "expand, fill, fillx, filly, padx, pady, or frame",
869 if (packPtr
!= prevPtr
) {
872 * Unpack this window if it's currently packed.
875 if (packPtr
->parentPtr
!= NULL
) {
880 * Add the window in the correct place in its parent's
881 * packing order, then make sure that the window is
885 packPtr
->parentPtr
= parentPtr
;
886 if (prevPtr
== NULL
) {
887 packPtr
->nextPtr
= parentPtr
->childPtr
;
888 parentPtr
->childPtr
= packPtr
;
890 packPtr
->nextPtr
= prevPtr
->nextPtr
;
891 prevPtr
->nextPtr
= packPtr
;
893 Tk_ManageGeometry(tkwin
, PackReqProc
, (ClientData
) packPtr
);
895 ckfree((char *) options
);
899 * Arrange for the parent to be re-packed at the first
903 if (parentPtr
->abortPtr
!= NULL
) {
904 *parentPtr
->abortPtr
= 1;
906 if (!(parentPtr
->flags
& REQUESTED_REPACK
)) {
907 parentPtr
->flags
|= REQUESTED_REPACK
;
908 Tk_DoWhenIdle(ArrangePacking
, (ClientData
) parentPtr
);
913 ckfree((char *) options
);
918 *----------------------------------------------------------------------
922 * Remove a packer from its parent's list of children.
928 * The parent will be scheduled for repacking.
930 *----------------------------------------------------------------------
935 register Packer
*packPtr
; /* Window to unlink. */
937 register Packer
*parentPtr
, *packPtr2
;
939 parentPtr
= packPtr
->parentPtr
;
940 if (parentPtr
== NULL
) {
943 if (parentPtr
->childPtr
== packPtr
) {
944 parentPtr
->childPtr
= packPtr
->nextPtr
;
946 for (packPtr2
= parentPtr
->childPtr
; ; packPtr2
= packPtr2
->nextPtr
) {
947 if (packPtr2
== NULL
) {
948 panic("Unlink couldn't find previous window");
950 if (packPtr2
->nextPtr
== packPtr
) {
951 packPtr2
->nextPtr
= packPtr
->nextPtr
;
956 if (!(parentPtr
->flags
& REQUESTED_REPACK
)) {
957 parentPtr
->flags
|= REQUESTED_REPACK
;
958 Tk_DoWhenIdle(ArrangePacking
, (ClientData
) parentPtr
);
960 if (parentPtr
->abortPtr
!= NULL
) {
961 *parentPtr
->abortPtr
= 1;
964 packPtr
->parentPtr
= NULL
;
968 *----------------------------------------------------------------------
972 * This procedure is invoked by Tk_EventuallyFree or Tk_Release
973 * to clean up the internal structure of a packer at a safe time
974 * (when no-one is using it anymore).
980 * Everything associated with the packer is freed up.
982 *----------------------------------------------------------------------
986 DestroyPacker(clientData
)
987 ClientData clientData
; /* Info about packed window that
990 register Packer
*packPtr
= (Packer
*) clientData
;
991 ckfree((char *) packPtr
);
995 *----------------------------------------------------------------------
997 * PackStructureProc --
999 * This procedure is invoked by the Tk event dispatcher in response
1000 * to StructureNotify events.
1006 * If a window was just deleted, clean up all its packer-related
1007 * information. If it was just resized, repack its children, if
1010 *----------------------------------------------------------------------
1014 PackStructureProc(clientData
, eventPtr
)
1015 ClientData clientData
; /* Our information about window
1016 * referred to by eventPtr. */
1017 XEvent
*eventPtr
; /* Describes what just happened. */
1019 register Packer
*packPtr
= (Packer
*) clientData
;
1020 if (eventPtr
->type
== ConfigureNotify
) {
1021 if ((packPtr
->childPtr
!= NULL
)
1022 && !(packPtr
->flags
& REQUESTED_REPACK
)) {
1023 packPtr
->flags
|= REQUESTED_REPACK
;
1024 Tk_DoWhenIdle(ArrangePacking
, (ClientData
) packPtr
);
1026 if (packPtr
->doubleBw
!= 2*Tk_Changes(packPtr
->tkwin
)->border_width
) {
1027 if ((packPtr
->parentPtr
!= NULL
)
1028 && !(packPtr
->parentPtr
->flags
& REQUESTED_REPACK
)) {
1029 packPtr
->doubleBw
= 2*Tk_Changes(packPtr
->tkwin
)->border_width
;
1030 packPtr
->parentPtr
->flags
|= REQUESTED_REPACK
;
1031 Tk_DoWhenIdle(ArrangePacking
, (ClientData
) packPtr
->parentPtr
);
1034 } else if (eventPtr
->type
== DestroyNotify
) {
1035 register Packer
*packPtr2
;
1037 if (packPtr
->parentPtr
!= NULL
) {
1040 for (packPtr2
= packPtr
->childPtr
; packPtr2
!= NULL
;
1041 packPtr2
= packPtr2
->nextPtr
) {
1042 packPtr2
->parentPtr
= NULL
;
1043 packPtr2
->nextPtr
= NULL
;
1045 Tcl_DeleteHashEntry(Tcl_FindHashEntry(&packerHashTable
,
1046 (char *) packPtr
->tkwin
));
1047 if (packPtr
->flags
& REQUESTED_REPACK
) {
1048 Tk_CancelIdleCall(ArrangePacking
, (ClientData
) packPtr
);
1050 packPtr
->tkwin
= NULL
;
1051 Tk_EventuallyFree((ClientData
) packPtr
, DestroyPacker
);