4 * This file contains code to implement the "packer"
7 * Copyright (c) 1990-1994 The Regents of the University of California.
8 * Copyright (c) 1994-1995 Sun Microsystems, Inc.
9 * Copyright (c) 1995 Christian Werner
11 * See the file "license.terms" for information on usage and redistribution
12 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
18 typedef enum {TOP, BOTTOM, LEFT, RIGHT} Side;
20 /* For each window that the packer cares about (either because
21 * the window is managed by the packer or because the window
22 * has slaves that are managed by the packer), there is a
23 * structure of the following type:
26 typedef struct Packer {
27 CkWindow *winPtr; /* Pointer to window. NULL means that
28 * the window has been deleted, but the
29 * packer hasn't had a chance to clean up
30 * yet because the structure is still in
32 struct Packer *masterPtr; /* Master window within which this window
33 * is packed (NULL means this window
34 * isn't managed by the packer). */
35 struct Packer *nextPtr; /* Next window packed within same
36 * parent. List is priority-ordered:
37 * first on list gets packed first. */
38 struct Packer *slavePtr; /* First in list of slaves packed
39 * inside this window (NULL means
40 * no packed slaves). */
41 Side side; /* Side of parent against which
42 * this window is packed. */
43 Ck_Anchor anchor; /* If frame allocated for window is larger
44 * than window needs, this indicates how
45 * where to position window in frame. */
46 int padX, padY; /* Total additional pixels to leave around the
47 * window (half of this space is left on each
48 * side). This is space *outside* the window:
49 * we'll allocate extra space in frame but
50 * won't enlarge window). */
51 int iPadX, iPadY; /* Total extra pixels to allocate inside the
52 * window (half this amount will appear on
54 int *abortPtr; /* If non-NULL, it means that there is a nested
55 * call to ArrangePacking already working on
56 * this window. *abortPtr may be set to 1 to
57 * abort that nested call. This happens, for
58 * example, if tkwin or any of its slaves
60 int flags; /* Miscellaneous flags; see below
65 * Flag values for Packer structures:
67 * REQUESTED_REPACK: 1 means a Ck_DoWhenIdle request
68 * has already been made to repack
69 * all the slaves of this window.
70 * FILLX: 1 means if frame allocated for window
71 * is wider than window needs, expand window
72 * to fill frame. 0 means don't make window
73 * any larger than needed.
74 * FILLY: Same as FILLX, except for height.
75 * EXPAND: 1 means this window's frame will absorb any
76 * extra space in the parent window.
77 * DONT_PROPAGATE: 1 means don't set this window's requested
78 * size. 0 means if this window is a master
79 * then Tk will set its requested size to fit
80 * the needs of its slaves.
83 #define REQUESTED_REPACK 1
87 #define DONT_PROPAGATE 16
90 * Hash table used to map from CkWindow pointers to corresponding
94 static Tcl_HashTable packerHashTable;
97 * Have statics in this module been initialized?
100 static int initialized = 0;
103 * The following structure is the official type record for the
107 static void PackReqProc _ANSI_ARGS_((ClientData clientData,
109 static void PackLostSlaveProc _ANSI_ARGS_((ClientData clientData,
112 static Ck_GeomMgr packerType = {
114 PackReqProc, /* requestProc */
115 PackLostSlaveProc, /* lostSlaveProc */
119 * Forward declarations for procedures defined later in this file:
122 static void ArrangePacking _ANSI_ARGS_((ClientData clientData));
123 static int ConfigureSlaves _ANSI_ARGS_((Tcl_Interp *interp,
124 CkWindow *winPtr, int argc, char *argv[]));
125 static Packer * GetPacker _ANSI_ARGS_((CkWindow *winPtr));
126 static void PackStructureProc _ANSI_ARGS_((ClientData clientData,
128 static void Unlink _ANSI_ARGS_((Packer *packPtr));
129 static int XExpansion _ANSI_ARGS_((Packer *slavePtr,
131 static int YExpansion _ANSI_ARGS_((Packer *slavePtr,
135 *--------------------------------------------------------------
139 * This procedure is invoked to process the "pack" Tcl command.
140 * See the user documentation for details on what it does.
143 * A standard Tcl result.
146 * See the user documentation.
148 *--------------------------------------------------------------
152 Ck_PackCmd(clientData, interp, argc, argv)
153 ClientData clientData; /* Main window associated with
155 Tcl_Interp *interp; /* Current interpreter. */
156 int argc; /* Number of arguments. */
157 char **argv; /* Argument strings. */
159 CkWindow *mainPtr = (CkWindow *) clientData;
163 if ((argc >= 2) && (argv[1][0] == '.')) {
164 return ConfigureSlaves(interp, mainPtr, argc-1, argv+1);
167 Tcl_AppendResult(interp, "wrong # args: should be \"",
168 argv[0], " option arg ?arg ...?\"", (char *) NULL);
172 length = strlen(argv[1]);
173 if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) {
174 if (argv[2][0] != '.') {
175 Tcl_AppendResult(interp, "bad argument \"", argv[2],
176 "\": must be name of window", (char *) NULL);
179 return ConfigureSlaves(interp, mainPtr, argc-2, argv+2);
180 } else if ((c == 'f') && (strncmp(argv[1], "forget", length) == 0)) {
185 for (i = 2; i < argc; i++) {
186 slave = Ck_NameToWindow(interp, argv[i], mainPtr);
190 slavePtr = GetPacker(slave);
191 if ((slavePtr != NULL) && (slavePtr->masterPtr != NULL)) {
192 Ck_ManageGeometry(slave, (Ck_GeomMgr *) NULL,
194 if (slavePtr->masterPtr->winPtr != slavePtr->winPtr->parentPtr) {
195 Ck_UnmaintainGeometry(slavePtr->winPtr,
196 slavePtr->masterPtr->winPtr);
199 Ck_UnmapWindow(slavePtr->winPtr);
202 } else if ((c == 'i') && (strncmp(argv[1], "info", length) == 0)) {
203 register Packer *slavePtr;
206 static char *sideNames[] = {"top", "bottom", "left", "right"};
209 Tcl_AppendResult(interp, "wrong # args: should be \"",
210 argv[0], " info window\"", (char *) NULL);
213 slave = Ck_NameToWindow(interp, argv[2], mainPtr);
217 slavePtr = GetPacker(slave);
218 if (slavePtr->masterPtr == NULL) {
219 Tcl_AppendResult(interp, "window \"", argv[2],
220 "\" isn't packed", (char *) NULL);
223 Tcl_AppendElement(interp, "-in");
224 Tcl_AppendElement(interp, slavePtr->masterPtr->winPtr->pathName);
225 Tcl_AppendElement(interp, "-anchor");
226 Tcl_AppendElement(interp, Ck_NameOfAnchor(slavePtr->anchor));
227 Tcl_AppendResult(interp, " -expand ",
228 (slavePtr->flags & EXPAND) ? "1" : "0", " -fill ",
230 switch (slavePtr->flags & (FILLX|FILLY)) {
232 Tcl_AppendResult(interp, "none", (char *) NULL);
235 Tcl_AppendResult(interp, "x", (char *) NULL);
238 Tcl_AppendResult(interp, "y", (char *) NULL);
241 Tcl_AppendResult(interp, "both", (char *) NULL);
244 sprintf(buffer, " -ipadx %d -ipady %d -padx %d -pady %d",
245 slavePtr->iPadX/2, slavePtr->iPadY/2, slavePtr->padX/2,
247 Tcl_AppendResult(interp, buffer, " -side ", sideNames[slavePtr->side],
249 } else if ((c == 'p') && (strncmp(argv[1], "propagate", length) == 0)) {
255 Tcl_AppendResult(interp, "wrong # args: should be \"",
256 argv[0], " propagate window ?boolean?\"", (char *) NULL);
259 master = Ck_NameToWindow(interp, argv[2], mainPtr);
260 if (master == NULL) {
263 masterPtr = GetPacker(master);
265 if (masterPtr->flags & DONT_PROPAGATE) {
266 interp->result = "0";
268 interp->result = "1";
272 if (Tcl_GetBoolean(interp, argv[3], &propagate) != TCL_OK) {
276 masterPtr->flags &= ~DONT_PROPAGATE;
279 * Repack the master to allow new geometry information to
280 * propagate upwards to the master's master.
283 if (masterPtr->abortPtr != NULL) {
284 *masterPtr->abortPtr = 1;
286 if (!(masterPtr->flags & REQUESTED_REPACK)) {
287 masterPtr->flags |= REQUESTED_REPACK;
288 Tcl_DoWhenIdle(ArrangePacking, (ClientData) masterPtr);
291 masterPtr->flags |= DONT_PROPAGATE;
293 } else if ((c == 's') && (strncmp(argv[1], "slaves", length) == 0)) {
295 Packer *masterPtr, *slavePtr;
298 Tcl_AppendResult(interp, "wrong # args: should be \"",
299 argv[0], " slaves window\"", (char *) NULL);
302 master = Ck_NameToWindow(interp, argv[2], mainPtr);
303 if (master == NULL) {
306 masterPtr = GetPacker(master);
307 for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
308 slavePtr = slavePtr->nextPtr) {
309 Tcl_AppendElement(interp, slavePtr->winPtr->pathName);
312 Tcl_AppendResult(interp, "bad option \"", argv[1],
313 "\": must be configure, forget, info, ",
314 "propagate, or slaves", (char *) NULL);
321 *--------------------------------------------------------------
325 * This procedure is invoked by Ck_GeometryRequest for
326 * windows managed by the packer.
332 * Arranges for winPtr, and all its managed siblings, to
333 * be re-packed at the next idle point.
335 *--------------------------------------------------------------
339 PackReqProc(clientData, winPtr)
340 ClientData clientData; /* Packer's information about
341 * window that got new preferred
343 CkWindow *winPtr; /* Other information about the window. */
345 register Packer *packPtr = (Packer *) clientData;
347 packPtr = packPtr->masterPtr;
348 if (!(packPtr->flags & REQUESTED_REPACK)) {
349 packPtr->flags |= REQUESTED_REPACK;
350 Tcl_DoWhenIdle(ArrangePacking, (ClientData) packPtr);
355 *--------------------------------------------------------------
357 * PackLostSlaveProc --
359 * This procedure is invoked whenever some other geometry
360 * claims control over a slave that used to be managed by us.
366 * Forgets all packer-related information about the slave.
368 *--------------------------------------------------------------
372 PackLostSlaveProc(clientData, winPtr)
373 ClientData clientData; /* Packer structure for slave window that
374 * was stolen away. */
375 CkWindow *winPtr; /* Pointer to window. */
377 register Packer *slavePtr = (Packer *) clientData;
379 if (slavePtr->masterPtr->winPtr != slavePtr->winPtr->parentPtr) {
380 Ck_UnmaintainGeometry(slavePtr->winPtr, slavePtr->masterPtr->winPtr);
383 Ck_UnmapWindow(slavePtr->winPtr);
387 *--------------------------------------------------------------
391 * This procedure is invoked (using the Tcl_DoWhenIdle
392 * mechanism) to re-layout a set of windows managed by
393 * the packer. It is invoked at idle time so that a
394 * series of packer requests can be merged into a single
401 * The packed slaves of masterPtr may get resized or
404 *--------------------------------------------------------------
408 ArrangePacking(clientData)
409 ClientData clientData; /* Structure describing parent whose slaves
410 * are to be re-layed out. */
412 register Packer *masterPtr = (Packer *) clientData;
413 register Packer *slavePtr;
414 int cavityX, cavityY, cavityWidth, cavityHeight;
415 /* These variables keep track of the
416 * as-yet-unallocated space remaining in
417 * the middle of the parent window. */
418 int frameX, frameY, frameWidth, frameHeight;
419 /* These variables keep track of the frame
420 * allocated to the current window. */
421 int x, y, width, height; /* These variables are used to hold the
422 * actual geometry of the current window. */
423 int intBWidth; /* Width of internal border in parent window,
425 int abort; /* May get set to non-zero to abort this
426 * repacking operation. */
427 int borderX, borderY;
428 int maxWidth, maxHeight, tmp;
430 masterPtr->flags &= ~REQUESTED_REPACK;
433 * If the parent has no slaves anymore, then don't do anything
434 * at all: just leave the parent's size as-is.
437 if (masterPtr->slavePtr == NULL) {
442 * Abort any nested call to ArrangePacking for this window, since
443 * we'll do everything necessary here, and set up so this call
444 * can be aborted if necessary.
447 if (masterPtr->abortPtr != NULL) {
448 *masterPtr->abortPtr = 1;
450 masterPtr->abortPtr = &abort;
452 Ck_Preserve((ClientData) masterPtr);
455 * Pass #1: scan all the slaves to figure out the total amount
456 * of space needed. Two separate width and height values are
459 * width - Holds the sum of the widths (plus padding) of
460 * all the slaves seen so far that were packed LEFT
462 * height - Holds the sum of the heights (plus padding) of
463 * all the slaves seen so far that were packed TOP
466 * maxWidth - Gradually builds up the width needed by the master
467 * to just barely satisfy all the slave's needs. For
468 * each slave, the code computes the width needed for
469 * all the slaves so far and updates maxWidth if the
470 * new value is greater.
471 * maxHeight - Same as maxWidth, except keeps height info.
474 intBWidth = (masterPtr->winPtr->flags & CK_BORDER) ? 1 : 0;
475 width = height = maxWidth = maxHeight = 2*intBWidth;
476 for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
477 slavePtr = slavePtr->nextPtr) {
478 if ((slavePtr->side == TOP) || (slavePtr->side == BOTTOM)) {
479 tmp = slavePtr->winPtr->reqWidth
480 + slavePtr->padX + slavePtr->iPadX + width;
481 if (tmp > maxWidth) {
484 height += slavePtr->winPtr->reqHeight
485 + slavePtr->padY + slavePtr->iPadY;
487 tmp = slavePtr->winPtr->reqHeight
488 + slavePtr->padY + slavePtr->iPadY + height;
489 if (tmp > maxHeight) {
492 width += slavePtr->winPtr->reqWidth
493 + slavePtr->padX + slavePtr->iPadX;
496 if (width > maxWidth) {
499 if (height > maxHeight) {
504 * If the total amount of space needed in the parent window has
505 * changed, and if we're propagating geometry information, then
506 * notify the next geometry manager up and requeue ourselves to
507 * start again after the parent has had a chance to
511 if (((maxWidth != masterPtr->winPtr->reqWidth)
512 || (maxHeight != masterPtr->winPtr->reqHeight))
513 && !(masterPtr->flags & DONT_PROPAGATE)) {
514 Ck_GeometryRequest(masterPtr->winPtr, maxWidth, maxHeight);
515 masterPtr->flags |= REQUESTED_REPACK;
516 Tcl_DoWhenIdle(ArrangePacking, (ClientData) masterPtr);
521 * Pass #2: scan the slaves a second time assigning
522 * new sizes. The "cavity" variables keep track of the
523 * unclaimed space in the cavity of the window; this
524 * shrinks inward as we allocate windows around the
525 * edges. The "frame" variables keep track of the space
526 * allocated to the current window and its frame. The
527 * current window is then placed somewhere inside the
528 * frame, depending on anchor.
531 cavityX = cavityY = x = y = intBWidth;
532 cavityWidth = masterPtr->winPtr->width - 2*intBWidth;
533 cavityHeight = masterPtr->winPtr->height - 2*intBWidth;
534 for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
535 slavePtr = slavePtr->nextPtr) {
536 if ((slavePtr->side == TOP) || (slavePtr->side == BOTTOM)) {
537 frameWidth = cavityWidth;
538 frameHeight = slavePtr->winPtr->reqHeight
539 + slavePtr->padY + slavePtr->iPadY;
540 if (slavePtr->flags & EXPAND) {
541 frameHeight += YExpansion(slavePtr, cavityHeight);
543 cavityHeight -= frameHeight;
544 if (cavityHeight < 0) {
545 frameHeight += cavityHeight;
549 if (slavePtr->side == TOP) {
551 cavityY += frameHeight;
553 frameY = cavityY + cavityHeight;
556 frameHeight = cavityHeight;
557 frameWidth = slavePtr->winPtr->reqWidth
558 + slavePtr->padX + slavePtr->iPadX;
559 if (slavePtr->flags & EXPAND) {
560 frameWidth += XExpansion(slavePtr, cavityWidth);
562 cavityWidth -= frameWidth;
563 if (cavityWidth < 0) {
564 frameWidth += cavityWidth;
568 if (slavePtr->side == LEFT) {
570 cavityX += frameWidth;
572 frameX = cavityX + cavityWidth;
577 * Now that we've got the size of the frame for the window,
578 * compute the window's actual size and location using the
579 * fill, padding, and frame factors. The variables "borderX"
580 * and "borderY" are used to handle the differences between
581 * old-style packing and the new style (in old-style, iPadX
582 * and iPadY are always zero and padding is completely ignored
583 * except when computing frame size).
586 borderX = slavePtr->padX;
587 borderY = slavePtr->padY;
588 width = slavePtr->winPtr->reqWidth + slavePtr->iPadX;
589 if ((slavePtr->flags & FILLX)
590 || (width > (frameWidth - borderX))) {
591 width = frameWidth - borderX;
593 height = slavePtr->winPtr->reqHeight + slavePtr->iPadY;
594 if ((slavePtr->flags & FILLY)
595 || (height > (frameHeight - borderY))) {
596 height = frameHeight - borderY;
600 switch (slavePtr->anchor) {
602 x = frameX + (frameWidth - width)/2;
603 y = frameY + borderY;
606 x = frameX + frameWidth - width - borderX;
607 y = frameY + borderY;
610 x = frameX + frameWidth - width - borderX;
611 y = frameY + (frameHeight - height)/2;
614 x = frameX + frameWidth - width - borderX;
615 y = frameY + frameHeight - height - borderY;
618 x = frameX + (frameWidth - width)/2;
619 y = frameY + frameHeight - height - borderY;
622 x = frameX + borderX;
623 y = frameY + frameHeight - height - borderY;
626 x = frameX + borderX;
627 y = frameY + (frameHeight - height)/2;
630 x = frameX + borderX;
631 y = frameY + borderY;
633 case CK_ANCHOR_CENTER:
634 x = frameX + (frameWidth - width)/2;
635 y = frameY + (frameHeight - height)/2;
638 panic("bad frame factor in ArrangePacking");
642 * The final step is to set the position, size, and mapped/unmapped
643 * state of the slave.
646 if (masterPtr->winPtr == slavePtr->winPtr->parentPtr) {
647 if (width <= 0 || height <= 0) {
648 Ck_UnmapWindow(slavePtr->winPtr);
650 if (width != slavePtr->winPtr->width ||
651 height != slavePtr->winPtr->height)
652 Ck_ResizeWindow(slavePtr->winPtr, width, height);
653 if (x != slavePtr->winPtr->x ||
654 y != slavePtr->winPtr->y)
655 Ck_MoveWindow(slavePtr->winPtr, x, y);
657 * Temporary kludge til Ck_MoveResizeWindow available !!!
659 if (width != slavePtr->winPtr->width ||
660 height != slavePtr->winPtr->height)
661 Ck_ResizeWindow(slavePtr->winPtr, width, height);
666 * Don't map the slave if the master isn't mapped: wait
667 * until the master gets mapped later.
670 if (masterPtr->winPtr->flags & CK_MAPPED) {
671 Ck_MapWindow(slavePtr->winPtr);
675 if ((width <= 0) || (height <= 0)) {
676 Ck_UnmaintainGeometry(slavePtr->winPtr, masterPtr->winPtr);
677 Ck_UnmapWindow(slavePtr->winPtr);
679 Ck_MaintainGeometry(slavePtr->winPtr, masterPtr->winPtr,
680 x, y, width, height);
685 * Changes to the window's structure could cause almost anything
686 * to happen, including deleting the parent or child. If this
687 * happens, we'll be told to abort.
696 masterPtr->abortPtr = NULL;
697 Ck_Release((ClientData) masterPtr);
701 *----------------------------------------------------------------------
705 * Given a list of packed slaves, the first of which is packed
706 * on the left or right and is expandable, compute how much to
710 * The return value is the number of additional pixels to give to
716 *----------------------------------------------------------------------
720 XExpansion(slavePtr, cavityWidth)
721 register Packer *slavePtr; /* First in list of remaining
723 int cavityWidth; /* Horizontal space left for all
724 * remaining slaves. */
726 int numExpand, minExpand, curExpand;
730 * This procedure is tricky because windows packed top or bottom can
731 * be interspersed among expandable windows packed left or right.
732 * Scan through the list, keeping a running sum of the widths of
733 * all left and right windows (actually, count the cavity space not
734 * allocated) and a running count of all expandable left and right
735 * windows. At each top or bottom window, and at the end of the
736 * list, compute the expansion factor that seems reasonable at that
737 * point. Return the smallest factor seen at any of these points.
740 minExpand = cavityWidth;
742 for ( ; slavePtr != NULL; slavePtr = slavePtr->nextPtr) {
743 childWidth = slavePtr->winPtr->reqWidth
744 + slavePtr->padX + slavePtr->iPadX;
745 if ((slavePtr->side == TOP) || (slavePtr->side == BOTTOM)) {
746 curExpand = (cavityWidth - childWidth)/numExpand;
747 if (curExpand < minExpand) {
748 minExpand = curExpand;
751 cavityWidth -= childWidth;
752 if (slavePtr->flags & EXPAND) {
757 curExpand = cavityWidth/numExpand;
758 if (curExpand < minExpand) {
759 minExpand = curExpand;
761 return (minExpand < 0) ? 0 : minExpand;
765 *----------------------------------------------------------------------
769 * Given a list of packed slaves, the first of which is packed
770 * on the top or bottom and is expandable, compute how much to
774 * The return value is the number of additional pixels to give to
780 *----------------------------------------------------------------------
784 YExpansion(slavePtr, cavityHeight)
785 register Packer *slavePtr; /* First in list of remaining
787 int cavityHeight; /* Vertical space left for all
788 * remaining slaves. */
790 int numExpand, minExpand, curExpand;
794 * See comments for XExpansion.
797 minExpand = cavityHeight;
799 for ( ; slavePtr != NULL; slavePtr = slavePtr->nextPtr) {
800 childHeight = slavePtr->winPtr->reqHeight
801 + slavePtr->padY + slavePtr->iPadY;
802 if ((slavePtr->side == LEFT) || (slavePtr->side == RIGHT)) {
803 curExpand = (cavityHeight - childHeight)/numExpand;
804 if (curExpand < minExpand) {
805 minExpand = curExpand;
808 cavityHeight -= childHeight;
809 if (slavePtr->flags & EXPAND) {
814 curExpand = cavityHeight/numExpand;
815 if (curExpand < minExpand) {
816 minExpand = curExpand;
818 return (minExpand < 0) ? 0 : minExpand;
822 *--------------------------------------------------------------
826 * This internal procedure is used to locate a Packer
827 * structure for a given window, creating one if one
828 * doesn't exist already.
831 * The return value is a pointer to the Packer structure
832 * corresponding to tkwin.
835 * A new packer structure may be created. If so, then
836 * a callback is set up to clean things up when the
839 *--------------------------------------------------------------
844 CkWindow *winPtr; /* Pointer to window for which
845 * packer structure is desired. */
847 register Packer *packPtr;
853 Tcl_InitHashTable(&packerHashTable, TCL_ONE_WORD_KEYS);
857 * See if there's already packer for this window. If not,
858 * then create a new one.
861 hPtr = Tcl_CreateHashEntry(&packerHashTable, (char *) winPtr, &new);
863 return (Packer *) Tcl_GetHashValue(hPtr);
865 packPtr = (Packer *) ckalloc(sizeof (Packer));
866 packPtr->winPtr = winPtr;
867 packPtr->masterPtr = NULL;
868 packPtr->nextPtr = NULL;
869 packPtr->slavePtr = NULL;
871 packPtr->anchor = CK_ANCHOR_CENTER;
872 packPtr->padX = packPtr->padY = 0;
873 packPtr->iPadX = packPtr->iPadY = 0;
874 packPtr->abortPtr = NULL;
876 Tcl_SetHashValue(hPtr, packPtr);
877 Ck_CreateEventHandler(winPtr,
878 CK_EV_DESTROY | CK_EV_MAP | CK_EV_EXPOSE,
879 PackStructureProc, (ClientData) packPtr);
884 *----------------------------------------------------------------------
888 * Remove a packer from its parent's list of slaves.
894 * The parent will be scheduled for repacking.
896 *----------------------------------------------------------------------
901 register Packer *packPtr; /* Window to unlink. */
903 register Packer *masterPtr, *packPtr2;
905 masterPtr = packPtr->masterPtr;
906 if (masterPtr == NULL) {
909 if (masterPtr->slavePtr == packPtr) {
910 masterPtr->slavePtr = packPtr->nextPtr;
912 for (packPtr2 = masterPtr->slavePtr; ; packPtr2 = packPtr2->nextPtr) {
913 if (packPtr2 == NULL) {
914 panic("Unlink couldn't find previous window");
916 if (packPtr2->nextPtr == packPtr) {
917 packPtr2->nextPtr = packPtr->nextPtr;
922 if (!(masterPtr->flags & REQUESTED_REPACK)) {
923 masterPtr->flags |= REQUESTED_REPACK;
924 Tcl_DoWhenIdle(ArrangePacking, (ClientData) masterPtr);
926 if (masterPtr->abortPtr != NULL) {
927 *masterPtr->abortPtr = 1;
930 packPtr->masterPtr = NULL;
934 *----------------------------------------------------------------------
938 * This procedure is invoked by Ck_EventuallyFree or Ck_Release
939 * to clean up the internal structure of a packer at a safe time
940 * (when no-one is using it anymore).
946 * Everything associated with the packer is freed up.
948 *----------------------------------------------------------------------
952 DestroyPacker(clientData)
953 ClientData clientData; /* Info about packed window that
956 register Packer *packPtr = (Packer *) clientData;
957 ckfree((char *) packPtr);
961 *----------------------------------------------------------------------
963 * PackStructureProc --
965 * This procedure is invoked by the event dispatcher in response
966 * to CK_EV_MAP/CK_EV_EXPOSE/CK_EV_DESTROY events.
972 * If a window was just deleted, clean up all its packer-related
973 * information. If it was just resized, repack its slaves, if
976 *----------------------------------------------------------------------
980 PackStructureProc(clientData, eventPtr)
981 ClientData clientData; /* Our information about window
982 * referred to by eventPtr. */
983 CkEvent *eventPtr; /* Describes what just happened. */
985 register Packer *packPtr = (Packer *) clientData;
987 if (eventPtr->type == CK_EV_MAP || eventPtr->type == CK_EV_EXPOSE) {
988 if ((packPtr->slavePtr != NULL)
989 && !(packPtr->flags & REQUESTED_REPACK)) {
990 packPtr->flags |= REQUESTED_REPACK;
991 Tcl_DoWhenIdle(ArrangePacking, (ClientData) packPtr);
993 } else if (eventPtr->type == CK_EV_DESTROY) {
994 register Packer *slavePtr, *nextPtr;
996 if (packPtr->masterPtr != NULL) {
999 for (slavePtr = packPtr->slavePtr; slavePtr != NULL;
1000 slavePtr = nextPtr) {
1001 Ck_ManageGeometry(slavePtr->winPtr, (Ck_GeomMgr *) NULL,
1003 Ck_UnmapWindow(slavePtr->winPtr);
1004 slavePtr->masterPtr = NULL;
1005 nextPtr = slavePtr->nextPtr;
1006 slavePtr->nextPtr = NULL;
1008 Tcl_DeleteHashEntry(Tcl_FindHashEntry(&packerHashTable,
1009 (char *) packPtr->winPtr));
1010 if (packPtr->flags & REQUESTED_REPACK) {
1011 Tcl_CancelIdleCall(ArrangePacking, (ClientData) packPtr);
1013 packPtr->winPtr = NULL;
1014 Ck_EventuallyFree((ClientData) packPtr, (Ck_FreeProc *) DestroyPacker);
1019 *----------------------------------------------------------------------
1021 * ConfigureSlaves --
1023 * This implements the guts of the "pack configure" command. Given
1024 * a list of slaves and configuration options, it arranges for the
1025 * packer to manage the slaves and sets the specified options.
1028 * TCL_OK is returned if all went well. Otherwise, TCL_ERROR is
1029 * returned and interp->result is set to contain an error message.
1032 * Slave windows get taken over by the packer.
1034 *----------------------------------------------------------------------
1038 ConfigureSlaves(interp, winPtr, argc, argv)
1039 Tcl_Interp *interp; /* Interpreter for error reporting. */
1040 CkWindow *winPtr; /* Any window in application containing
1041 * slaves. Used to look up slave names. */
1042 int argc; /* Number of elements in argv. */
1043 char *argv[]; /* Argument strings: contains one or more
1044 * window names followed by any number
1045 * of "option value" pairs. Caller must
1046 * make sure that there is at least one
1049 Packer *masterPtr, *slavePtr, *prevPtr, *otherPtr;
1050 CkWindow *other, *slave, *parent, *ancestor;
1051 int i, j, numWindows, c, tmp, positionGiven;
1055 * Find out how many windows are specified.
1058 for (numWindows = 0; numWindows < argc; numWindows++) {
1059 if (argv[numWindows][0] != '.') {
1065 * Iterate over all of the slave windows, parsing the configuration
1066 * options for each slave. It's a bit wasteful to re-parse the
1067 * options for each slave, but things get too messy if we try to
1068 * parse the arguments just once at the beginning. For example,
1069 * if a slave already is packed we want to just change a few
1070 * existing values without resetting everything. If there are
1071 * multiple windows, the -after, -before, and -in options only
1072 * get processed for the first window.
1078 for (j = 0; j < numWindows; j++) {
1079 slave = Ck_NameToWindow(interp, argv[j], winPtr);
1080 if (slave == NULL) {
1083 if (slave->flags & CK_TOPLEVEL) {
1084 Tcl_AppendResult(interp, "can't pack \"", argv[j],
1085 "\": it's a top-level window", (char *) NULL);
1088 slavePtr = GetPacker(slave);
1091 * If the slave isn't currently packed, reset all of its
1092 * configuration information to default values (there could
1093 * be old values left from a previous packing).
1096 if (slavePtr->masterPtr == NULL) {
1097 slavePtr->side = TOP;
1098 slavePtr->anchor = CK_ANCHOR_CENTER;
1099 slavePtr->padX = slavePtr->padY = 0;
1100 slavePtr->iPadX = slavePtr->iPadY = 0;
1101 slavePtr->flags &= ~(FILLX|FILLY|EXPAND);
1104 for (i = numWindows; i < argc; i+=2) {
1106 Tcl_AppendResult(interp, "extra option \"", argv[i],
1107 "\" (option with no value?)", (char *) NULL);
1110 length = strlen(argv[i]);
1115 if ((c == 'a') && (strncmp(argv[i], "-after", length) == 0)
1118 other = Ck_NameToWindow(interp, argv[i+1], winPtr);
1119 if (other == NULL) {
1122 prevPtr = GetPacker(other);
1123 if (prevPtr->masterPtr == NULL) {
1125 Tcl_AppendResult(interp, "window \"", argv[i+1],
1126 "\" isn't packed", (char *) NULL);
1129 masterPtr = prevPtr->masterPtr;
1132 } else if ((c == 'a') && (strncmp(argv[i], "-anchor", length) == 0)
1134 if (Ck_GetAnchor(interp, argv[i+1], &slavePtr->anchor)
1138 } else if ((c == 'b')
1139 && (strncmp(argv[i], "-before", length) == 0)) {
1141 other = Ck_NameToWindow(interp, argv[i+1], winPtr);
1142 if (other == NULL) {
1145 otherPtr = GetPacker(other);
1146 if (otherPtr->masterPtr == NULL) {
1149 masterPtr = otherPtr->masterPtr;
1150 prevPtr = masterPtr->slavePtr;
1151 if (prevPtr == otherPtr) {
1154 while (prevPtr->nextPtr != otherPtr) {
1155 prevPtr = prevPtr->nextPtr;
1160 } else if ((c == 'e')
1161 && (strncmp(argv[i], "-expand", length) == 0)) {
1162 if (Tcl_GetBoolean(interp, argv[i+1], &tmp) != TCL_OK) {
1165 slavePtr->flags &= ~EXPAND;
1167 slavePtr->flags |= EXPAND;
1169 } else if ((c == 'f') && (strncmp(argv[i], "-fill", length) == 0)) {
1170 if (strcmp(argv[i+1], "none") == 0) {
1171 slavePtr->flags &= ~(FILLX|FILLY);
1172 } else if (strcmp(argv[i+1], "x") == 0) {
1173 slavePtr->flags = (slavePtr->flags & ~FILLY) | FILLX;
1174 } else if (strcmp(argv[i+1], "y") == 0) {
1175 slavePtr->flags = (slavePtr->flags & ~FILLX) | FILLY;
1176 } else if (strcmp(argv[i+1], "both") == 0) {
1177 slavePtr->flags |= FILLX|FILLY;
1179 Tcl_AppendResult(interp, "bad fill style \"", argv[i+1],
1180 "\": must be none, x, y, or both", (char *) NULL);
1183 } else if ((c == 'i') && (strcmp(argv[i], "-in") == 0)) {
1185 other = Ck_NameToWindow(interp, argv[i+1], winPtr);
1186 if (other == NULL) {
1189 masterPtr = GetPacker(other);
1190 prevPtr = masterPtr->slavePtr;
1191 if (prevPtr != NULL) {
1192 while (prevPtr->nextPtr != NULL) {
1193 prevPtr = prevPtr->nextPtr;
1198 } else if ((c == 'i') && (strcmp(argv[i], "-ipadx") == 0)) {
1199 if ((Ck_GetCoord(interp, slave, argv[i+1], &tmp) != TCL_OK)
1202 Tcl_ResetResult(interp);
1203 Tcl_AppendResult(interp, "bad pad value \"", argv[i+1],
1204 "\": must be positive screen distance",
1208 slavePtr->iPadX = tmp*2;
1209 } else if ((c == 'i') && (strcmp(argv[i], "-ipady") == 0)) {
1210 if ((Ck_GetCoord(interp, slave, argv[i+1], &tmp) != TCL_OK)
1214 slavePtr->iPadY = tmp*2;
1215 } else if ((c == 'p') && (strcmp(argv[i], "-padx") == 0)) {
1216 if ((Ck_GetCoord(interp, slave, argv[i+1], &tmp) != TCL_OK)
1220 slavePtr->padX = tmp*2;
1221 } else if ((c == 'p') && (strcmp(argv[i], "-pady") == 0)) {
1222 if ((Ck_GetCoord(interp, slave, argv[i+1], &tmp) != TCL_OK)
1226 slavePtr->padY = tmp*2;
1227 } else if ((c == 's') && (strncmp(argv[i], "-side", length) == 0)) {
1229 if ((c == 't') && (strcmp(argv[i+1], "top") == 0)) {
1230 slavePtr->side = TOP;
1231 } else if ((c == 'b') && (strcmp(argv[i+1], "bottom") == 0)) {
1232 slavePtr->side = BOTTOM;
1233 } else if ((c == 'l') && (strcmp(argv[i+1], "left") == 0)) {
1234 slavePtr->side = LEFT;
1235 } else if ((c == 'r') && (strcmp(argv[i+1], "right") == 0)) {
1236 slavePtr->side = RIGHT;
1238 Tcl_AppendResult(interp, "bad side \"", argv[i+1],
1239 "\": must be top, bottom, left, or right",
1245 Tcl_AppendResult(interp, "unknown or ambiguous option \"",
1246 argv[i], "\": must be -after, -anchor, -before, ",
1247 "-expand, -fill, -in, -ipadx, -ipady, -padx, ",
1248 "-pady, or -side", (char *) NULL);
1254 * If no position in a packing list was specified and the slave
1255 * is already packed, then leave it in its current location in
1256 * its current packing list.
1259 if (!positionGiven && (slavePtr->masterPtr != NULL)) {
1260 masterPtr = slavePtr->masterPtr;
1261 goto scheduleLayout;
1265 * If the slave is going to be put back after itself then
1266 * skip the whole operation, since it won't work anyway.
1269 if (prevPtr == slavePtr) {
1270 masterPtr = slavePtr->masterPtr;
1271 goto scheduleLayout;
1275 * If none of the "-before", or "-after" options has
1276 * been specified, arrange for the slave to go at the end of
1277 * the order for its parent.
1280 if (!positionGiven) {
1281 masterPtr = GetPacker(slave->parentPtr);
1282 prevPtr = masterPtr->slavePtr;
1283 if (prevPtr != NULL) {
1284 while (prevPtr->nextPtr != NULL) {
1285 prevPtr = prevPtr->nextPtr;
1291 * Make sure that the slave's parent is either the master or
1292 * an ancestor of the master.
1295 parent = slave->parentPtr;
1296 for (ancestor = masterPtr->winPtr; ;
1297 ancestor = ancestor->parentPtr) {
1298 if (ancestor == parent) {
1301 if (ancestor->flags & CK_TOPLEVEL) {
1302 Tcl_AppendResult(interp, "can't pack ", argv[j],
1303 " inside ", masterPtr->winPtr->pathName,
1308 if (slave == masterPtr->winPtr) {
1309 Tcl_AppendResult(interp, "can't pack ", argv[j],
1310 " inside itself", (char *) NULL);
1315 * Unpack the slave if it's currently packed, then position it
1319 if (slavePtr->masterPtr != NULL) {
1320 if ((slavePtr->masterPtr != masterPtr) &&
1321 (slavePtr->masterPtr->winPtr
1322 != slavePtr->winPtr->parentPtr)) {
1323 Ck_UnmaintainGeometry(slavePtr->winPtr,
1324 slavePtr->masterPtr->winPtr);
1328 slavePtr->masterPtr = masterPtr;
1329 if (prevPtr == NULL) {
1330 slavePtr->nextPtr = masterPtr->slavePtr;
1331 masterPtr->slavePtr = slavePtr;
1333 slavePtr->nextPtr = prevPtr->nextPtr;
1334 prevPtr->nextPtr = slavePtr;
1336 Ck_ManageGeometry(slave, &packerType, (ClientData) slavePtr);
1340 * Arrange for the parent to be re-packed at the first
1345 if (masterPtr->abortPtr != NULL) {
1346 *masterPtr->abortPtr = 1;
1348 if (!(masterPtr->flags & REQUESTED_REPACK)) {
1349 masterPtr->flags |= REQUESTED_REPACK;
1350 Tcl_DoWhenIdle(ArrangePacking, (ClientData) masterPtr);