summaryrefslogtreecommitdiffstats
path: root/generic/tkPack.c
diff options
context:
space:
mode:
authorrjohnson <rjohnson>1998-04-01 09:51:44 (GMT)
committerrjohnson <rjohnson>1998-04-01 09:51:44 (GMT)
commit066ea7fd88d49cb456f74da71dbe875e4fc0aabb (patch)
tree8fb30cb152c4dc191be47fa043d2e6f5ea38c7ba /generic/tkPack.c
parent13242623d2ff3ea02ab6a62bfb48a7dbb5c27e22 (diff)
downloadtk-066ea7fd88d49cb456f74da71dbe875e4fc0aabb.zip
tk-066ea7fd88d49cb456f74da71dbe875e4fc0aabb.tar.gz
tk-066ea7fd88d49cb456f74da71dbe875e4fc0aabb.tar.bz2
Initial revision
Diffstat (limited to 'generic/tkPack.c')
-rw-r--r--generic/tkPack.c1727
1 files changed, 1727 insertions, 0 deletions
diff --git a/generic/tkPack.c b/generic/tkPack.c
new file mode 100644
index 0000000..4ff1049
--- /dev/null
+++ b/generic/tkPack.c
@@ -0,0 +1,1727 @@
+/*
+ * tkPack.c --
+ *
+ * This file contains code to implement the "packer"
+ * geometry manager for Tk.
+ *
+ * Copyright (c) 1990-1994 The Regents of the University of California.
+ * Copyright (c) 1994-1995 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * SCCS: @(#) tkPack.c 1.64 96/05/03 10:51:52
+ */
+
+#include "tkPort.h"
+#include "tkInt.h"
+
+typedef enum {TOP, BOTTOM, LEFT, RIGHT} Side;
+
+/* For each window that the packer cares about (either because
+ * the window is managed by the packer or because the window
+ * has slaves that are managed by the packer), there is a
+ * structure of the following type:
+ */
+
+typedef struct Packer {
+ Tk_Window tkwin; /* Tk token for window. NULL means that
+ * the window has been deleted, but the
+ * packet hasn't had a chance to clean up
+ * yet because the structure is still in
+ * use. */
+ struct Packer *masterPtr; /* Master window within which this window
+ * is packed (NULL means this window
+ * isn't managed by the packer). */
+ struct Packer *nextPtr; /* Next window packed within same
+ * parent. List is priority-ordered:
+ * first on list gets packed first. */
+ struct Packer *slavePtr; /* First in list of slaves packed
+ * inside this window (NULL means
+ * no packed slaves). */
+ Side side; /* Side of parent against which
+ * this window is packed. */
+ Tk_Anchor anchor; /* If frame allocated for window is larger
+ * than window needs, this indicates how
+ * where to position window in frame. */
+ int padX, padY; /* Total additional pixels to leave around the
+ * window (half of this space is left on each
+ * side). This is space *outside* the window:
+ * we'll allocate extra space in frame but
+ * won't enlarge window). */
+ int iPadX, iPadY; /* Total extra pixels to allocate inside the
+ * window (half this amount will appear on
+ * each side). */
+ int doubleBw; /* Twice the window's last known border
+ * width. If this changes, the window
+ * must be repacked within its parent. */
+ int *abortPtr; /* If non-NULL, it means that there is a nested
+ * call to ArrangePacking already working on
+ * this window. *abortPtr may be set to 1 to
+ * abort that nested call. This happens, for
+ * example, if tkwin or any of its slaves
+ * is deleted. */
+ int flags; /* Miscellaneous flags; see below
+ * for definitions. */
+} Packer;
+
+/*
+ * Flag values for Packer structures:
+ *
+ * REQUESTED_REPACK: 1 means a Tcl_DoWhenIdle request
+ * has already been made to repack
+ * all the slaves of this window.
+ * FILLX: 1 means if frame allocated for window
+ * is wider than window needs, expand window
+ * to fill frame. 0 means don't make window
+ * any larger than needed.
+ * FILLY: Same as FILLX, except for height.
+ * EXPAND: 1 means this window's frame will absorb any
+ * extra space in the parent window.
+ * OLD_STYLE: 1 means this window is being managed with
+ * the old-style packer algorithms (before
+ * Tk version 3.3). The main difference is
+ * that padding and filling are done differently.
+ * DONT_PROPAGATE: 1 means don't set this window's requested
+ * size. 0 means if this window is a master
+ * then Tk will set its requested size to fit
+ * the needs of its slaves.
+ */
+
+#define REQUESTED_REPACK 1
+#define FILLX 2
+#define FILLY 4
+#define EXPAND 8
+#define OLD_STYLE 16
+#define DONT_PROPAGATE 32
+
+/*
+ * Hash table used to map from Tk_Window tokens to corresponding
+ * Packer structures:
+ */
+
+static Tcl_HashTable packerHashTable;
+
+/*
+ * Have statics in this module been initialized?
+ */
+
+static int initialized = 0;
+
+/*
+ * The following structure is the official type record for the
+ * packer:
+ */
+
+static void PackReqProc _ANSI_ARGS_((ClientData clientData,
+ Tk_Window tkwin));
+static void PackLostSlaveProc _ANSI_ARGS_((ClientData clientData,
+ Tk_Window tkwin));
+
+static Tk_GeomMgr packerType = {
+ "pack", /* name */
+ PackReqProc, /* requestProc */
+ PackLostSlaveProc, /* lostSlaveProc */
+};
+
+/*
+ * Forward declarations for procedures defined later in this file:
+ */
+
+static void ArrangePacking _ANSI_ARGS_((ClientData clientData));
+static int ConfigureSlaves _ANSI_ARGS_((Tcl_Interp *interp,
+ Tk_Window tkwin, int argc, char *argv[]));
+static void DestroyPacker _ANSI_ARGS_((char *memPtr));
+static Packer * GetPacker _ANSI_ARGS_((Tk_Window tkwin));
+static int PackAfter _ANSI_ARGS_((Tcl_Interp *interp,
+ Packer *prevPtr, Packer *masterPtr, int argc,
+ char **argv));
+static void PackReqProc _ANSI_ARGS_((ClientData clientData,
+ Tk_Window tkwin));
+static void PackStructureProc _ANSI_ARGS_((ClientData clientData,
+ XEvent *eventPtr));
+static void Unlink _ANSI_ARGS_((Packer *packPtr));
+static int XExpansion _ANSI_ARGS_((Packer *slavePtr,
+ int cavityWidth));
+static int YExpansion _ANSI_ARGS_((Packer *slavePtr,
+ int cavityHeight));
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Tk_PackCmd --
+ *
+ * This procedure is invoked to process the "pack" Tcl command.
+ * See the user documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+
+int
+Tk_PackCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Main window associated with
+ * interpreter. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ Tk_Window tkwin = (Tk_Window) clientData;
+ size_t length;
+ int c;
+
+ if ((argc >= 2) && (argv[1][0] == '.')) {
+ return ConfigureSlaves(interp, tkwin, argc-1, argv+1);
+ }
+ if (argc < 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " option arg ?arg ...?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ c = argv[1][0];
+ length = strlen(argv[1]);
+ if ((c == 'a') && (length >= 2)
+ && (strncmp(argv[1], "after", length) == 0)) {
+ Packer *prevPtr;
+ Tk_Window tkwin2;
+
+ tkwin2 = Tk_NameToWindow(interp, argv[2], tkwin);
+ if (tkwin2 == NULL) {
+ return TCL_ERROR;
+ }
+ prevPtr = GetPacker(tkwin2);
+ if (prevPtr->masterPtr == NULL) {
+ Tcl_AppendResult(interp, "window \"", argv[2],
+ "\" isn't packed", (char *) NULL);
+ return TCL_ERROR;
+ }
+ return PackAfter(interp, prevPtr, prevPtr->masterPtr, argc-3, argv+3);
+ } else if ((c == 'a') && (length >= 2)
+ && (strncmp(argv[1], "append", length) == 0)) {
+ Packer *masterPtr;
+ register Packer *prevPtr;
+ Tk_Window tkwin2;
+
+ tkwin2 = Tk_NameToWindow(interp, argv[2], tkwin);
+ if (tkwin2 == NULL) {
+ return TCL_ERROR;
+ }
+ masterPtr = GetPacker(tkwin2);
+ prevPtr = masterPtr->slavePtr;
+ if (prevPtr != NULL) {
+ while (prevPtr->nextPtr != NULL) {
+ prevPtr = prevPtr->nextPtr;
+ }
+ }
+ return PackAfter(interp, prevPtr, masterPtr, argc-3, argv+3);
+ } else if ((c == 'b') && (strncmp(argv[1], "before", length) == 0)) {
+ Packer *packPtr, *masterPtr;
+ register Packer *prevPtr;
+ Tk_Window tkwin2;
+
+ tkwin2 = Tk_NameToWindow(interp, argv[2], tkwin);
+ if (tkwin2 == NULL) {
+ return TCL_ERROR;
+ }
+ packPtr = GetPacker(tkwin2);
+ if (packPtr->masterPtr == NULL) {
+ Tcl_AppendResult(interp, "window \"", argv[2],
+ "\" isn't packed", (char *) NULL);
+ return TCL_ERROR;
+ }
+ masterPtr = packPtr->masterPtr;
+ prevPtr = masterPtr->slavePtr;
+ if (prevPtr == packPtr) {
+ prevPtr = NULL;
+ } else {
+ for ( ; ; prevPtr = prevPtr->nextPtr) {
+ if (prevPtr == NULL) {
+ panic("\"pack before\" couldn't find predecessor");
+ }
+ if (prevPtr->nextPtr == packPtr) {
+ break;
+ }
+ }
+ }
+ return PackAfter(interp, prevPtr, masterPtr, argc-3, argv+3);
+ } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) {
+ if (argv[2][0] != '.') {
+ Tcl_AppendResult(interp, "bad argument \"", argv[2],
+ "\": must be name of window", (char *) NULL);
+ return TCL_ERROR;
+ }
+ return ConfigureSlaves(interp, tkwin, argc-2, argv+2);
+ } else if ((c == 'f') && (strncmp(argv[1], "forget", length) == 0)) {
+ Tk_Window slave;
+ Packer *slavePtr;
+ int i;
+
+ for (i = 2; i < argc; i++) {
+ slave = Tk_NameToWindow(interp, argv[i], tkwin);
+ if (slave == NULL) {
+ continue;
+ }
+ slavePtr = GetPacker(slave);
+ if ((slavePtr != NULL) && (slavePtr->masterPtr != NULL)) {
+ Tk_ManageGeometry(slave, (Tk_GeomMgr *) NULL,
+ (ClientData) NULL);
+ if (slavePtr->masterPtr->tkwin != Tk_Parent(slavePtr->tkwin)) {
+ Tk_UnmaintainGeometry(slavePtr->tkwin,
+ slavePtr->masterPtr->tkwin);
+ }
+ Unlink(slavePtr);
+ Tk_UnmapWindow(slavePtr->tkwin);
+ }
+ }
+ } else if ((c == 'i') && (strncmp(argv[1], "info", length) == 0)) {
+ register Packer *slavePtr;
+ Tk_Window slave;
+ char buffer[300];
+ static char *sideNames[] = {"top", "bottom", "left", "right"};
+
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " info window\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ slave = Tk_NameToWindow(interp, argv[2], tkwin);
+ if (slave == NULL) {
+ return TCL_ERROR;
+ }
+ slavePtr = GetPacker(slave);
+ if (slavePtr->masterPtr == NULL) {
+ Tcl_AppendResult(interp, "window \"", argv[2],
+ "\" isn't packed", (char *) NULL);
+ return TCL_ERROR;
+ }
+ Tcl_AppendElement(interp, "-in");
+ Tcl_AppendElement(interp, Tk_PathName(slavePtr->masterPtr->tkwin));
+ Tcl_AppendElement(interp, "-anchor");
+ Tcl_AppendElement(interp, Tk_NameOfAnchor(slavePtr->anchor));
+ Tcl_AppendResult(interp, " -expand ",
+ (slavePtr->flags & EXPAND) ? "1" : "0", " -fill ",
+ (char *) NULL);
+ switch (slavePtr->flags & (FILLX|FILLY)) {
+ case 0:
+ Tcl_AppendResult(interp, "none", (char *) NULL);
+ break;
+ case FILLX:
+ Tcl_AppendResult(interp, "x", (char *) NULL);
+ break;
+ case FILLY:
+ Tcl_AppendResult(interp, "y", (char *) NULL);
+ break;
+ case FILLX|FILLY:
+ Tcl_AppendResult(interp, "both", (char *) NULL);
+ break;
+ }
+ sprintf(buffer, " -ipadx %d -ipady %d -padx %d -pady %d",
+ slavePtr->iPadX/2, slavePtr->iPadY/2, slavePtr->padX/2,
+ slavePtr->padY/2);
+ Tcl_AppendResult(interp, buffer, " -side ", sideNames[slavePtr->side],
+ (char *) NULL);
+ } else if ((c == 'p') && (strncmp(argv[1], "propagate", length) == 0)) {
+ Tk_Window master;
+ Packer *masterPtr;
+ int propagate;
+
+ if (argc > 4) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " propagate window ?boolean?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ master = Tk_NameToWindow(interp, argv[2], tkwin);
+ if (master == NULL) {
+ return TCL_ERROR;
+ }
+ masterPtr = GetPacker(master);
+ if (argc == 3) {
+ if (masterPtr->flags & DONT_PROPAGATE) {
+ interp->result = "0";
+ } else {
+ interp->result = "1";
+ }
+ return TCL_OK;
+ }
+ if (Tcl_GetBoolean(interp, argv[3], &propagate) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (propagate) {
+ masterPtr->flags &= ~DONT_PROPAGATE;
+
+ /*
+ * Repack the master to allow new geometry information to
+ * propagate upwards to the master's master.
+ */
+
+ if (masterPtr->abortPtr != NULL) {
+ *masterPtr->abortPtr = 1;
+ }
+ if (!(masterPtr->flags & REQUESTED_REPACK)) {
+ masterPtr->flags |= REQUESTED_REPACK;
+ Tcl_DoWhenIdle(ArrangePacking, (ClientData) masterPtr);
+ }
+ } else {
+ masterPtr->flags |= DONT_PROPAGATE;
+ }
+ } else if ((c == 's') && (strncmp(argv[1], "slaves", length) == 0)) {
+ Tk_Window master;
+ Packer *masterPtr, *slavePtr;
+
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " slaves window\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ master = Tk_NameToWindow(interp, argv[2], tkwin);
+ if (master == NULL) {
+ return TCL_ERROR;
+ }
+ masterPtr = GetPacker(master);
+ for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
+ slavePtr = slavePtr->nextPtr) {
+ Tcl_AppendElement(interp, Tk_PathName(slavePtr->tkwin));
+ }
+ } else if ((c == 'u') && (strncmp(argv[1], "unpack", length) == 0)) {
+ Tk_Window tkwin2;
+ Packer *packPtr;
+
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " unpack window\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ tkwin2 = Tk_NameToWindow(interp, argv[2], tkwin);
+ if (tkwin2 == NULL) {
+ return TCL_ERROR;
+ }
+ packPtr = GetPacker(tkwin2);
+ if ((packPtr != NULL) && (packPtr->masterPtr != NULL)) {
+ Tk_ManageGeometry(tkwin2, (Tk_GeomMgr *) NULL,
+ (ClientData) NULL);
+ if (packPtr->masterPtr->tkwin != Tk_Parent(packPtr->tkwin)) {
+ Tk_UnmaintainGeometry(packPtr->tkwin,
+ packPtr->masterPtr->tkwin);
+ }
+ Unlink(packPtr);
+ Tk_UnmapWindow(packPtr->tkwin);
+ }
+ } else {
+ Tcl_AppendResult(interp, "bad option \"", argv[1],
+ "\": must be configure, forget, info, ",
+ "propagate, or slaves", (char *) NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * PackReqProc --
+ *
+ * This procedure is invoked by Tk_GeometryRequest for
+ * windows managed by the packer.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Arranges for tkwin, and all its managed siblings, to
+ * be re-packed at the next idle point.
+ *
+ *--------------------------------------------------------------
+ */
+
+ /* ARGSUSED */
+static void
+PackReqProc(clientData, tkwin)
+ ClientData clientData; /* Packer's information about
+ * window that got new preferred
+ * geometry. */
+ Tk_Window tkwin; /* Other Tk-related information
+ * about the window. */
+{
+ register Packer *packPtr = (Packer *) clientData;
+
+ packPtr = packPtr->masterPtr;
+ if (!(packPtr->flags & REQUESTED_REPACK)) {
+ packPtr->flags |= REQUESTED_REPACK;
+ Tcl_DoWhenIdle(ArrangePacking, (ClientData) packPtr);
+ }
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * PackLostSlaveProc --
+ *
+ * This procedure is invoked by Tk whenever some other geometry
+ * claims control over a slave that used to be managed by us.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Forgets all packer-related information about the slave.
+ *
+ *--------------------------------------------------------------
+ */
+
+ /* ARGSUSED */
+static void
+PackLostSlaveProc(clientData, tkwin)
+ ClientData clientData; /* Packer structure for slave window that
+ * was stolen away. */
+ Tk_Window tkwin; /* Tk's handle for the slave window. */
+{
+ register Packer *slavePtr = (Packer *) clientData;
+
+ if (slavePtr->masterPtr->tkwin != Tk_Parent(slavePtr->tkwin)) {
+ Tk_UnmaintainGeometry(slavePtr->tkwin, slavePtr->masterPtr->tkwin);
+ }
+ Unlink(slavePtr);
+ Tk_UnmapWindow(slavePtr->tkwin);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * ArrangePacking --
+ *
+ * This procedure is invoked (using the Tcl_DoWhenIdle
+ * mechanism) to re-layout a set of windows managed by
+ * the packer. It is invoked at idle time so that a
+ * series of packer requests can be merged into a single
+ * layout operation.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The packed slaves of masterPtr may get resized or
+ * moved.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+ArrangePacking(clientData)
+ ClientData clientData; /* Structure describing parent whose slaves
+ * are to be re-layed out. */
+{
+ register Packer *masterPtr = (Packer *) clientData;
+ register Packer *slavePtr;
+ int cavityX, cavityY, cavityWidth, cavityHeight;
+ /* These variables keep track of the
+ * as-yet-unallocated space remaining in
+ * the middle of the parent window. */
+ int frameX, frameY, frameWidth, frameHeight;
+ /* These variables keep track of the frame
+ * allocated to the current window. */
+ int x, y, width, height; /* These variables are used to hold the
+ * actual geometry of the current window. */
+ int intBWidth; /* Width of internal border in parent window,
+ * if any. */
+ int abort; /* May get set to non-zero to abort this
+ * repacking operation. */
+ int borderX, borderY;
+ int maxWidth, maxHeight, tmp;
+
+ masterPtr->flags &= ~REQUESTED_REPACK;
+
+ /*
+ * If the parent has no slaves anymore, then don't do anything
+ * at all: just leave the parent's size as-is.
+ */
+
+ if (masterPtr->slavePtr == NULL) {
+ return;
+ }
+
+ /*
+ * Abort any nested call to ArrangePacking for this window, since
+ * we'll do everything necessary here, and set up so this call
+ * can be aborted if necessary.
+ */
+
+ if (masterPtr->abortPtr != NULL) {
+ *masterPtr->abortPtr = 1;
+ }
+ masterPtr->abortPtr = &abort;
+ abort = 0;
+ Tcl_Preserve((ClientData) masterPtr);
+
+ /*
+ * Pass #1: scan all the slaves to figure out the total amount
+ * of space needed. Two separate width and height values are
+ * computed:
+ *
+ * width - Holds the sum of the widths (plus padding) of
+ * all the slaves seen so far that were packed LEFT
+ * or RIGHT.
+ * height - Holds the sum of the heights (plus padding) of
+ * all the slaves seen so far that were packed TOP
+ * or BOTTOM.
+ *
+ * maxWidth - Gradually builds up the width needed by the master
+ * to just barely satisfy all the slave's needs. For
+ * each slave, the code computes the width needed for
+ * all the slaves so far and updates maxWidth if the
+ * new value is greater.
+ * maxHeight - Same as maxWidth, except keeps height info.
+ */
+
+ intBWidth = Tk_InternalBorderWidth(masterPtr->tkwin);
+ width = height = maxWidth = maxHeight = 2*intBWidth;
+ for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
+ slavePtr = slavePtr->nextPtr) {
+ if ((slavePtr->side == TOP) || (slavePtr->side == BOTTOM)) {
+ tmp = Tk_ReqWidth(slavePtr->tkwin) + slavePtr->doubleBw
+ + slavePtr->padX + slavePtr->iPadX + width;
+ if (tmp > maxWidth) {
+ maxWidth = tmp;
+ }
+ height += Tk_ReqHeight(slavePtr->tkwin) + slavePtr->doubleBw
+ + slavePtr->padY + slavePtr->iPadY;
+ } else {
+ tmp = Tk_ReqHeight(slavePtr->tkwin) + slavePtr->doubleBw
+ + slavePtr->padY + slavePtr->iPadY + height;
+ if (tmp > maxHeight) {
+ maxHeight = tmp;
+ }
+ width += Tk_ReqWidth(slavePtr->tkwin) + slavePtr->doubleBw
+ + slavePtr->padX + slavePtr->iPadX;
+ }
+ }
+ if (width > maxWidth) {
+ maxWidth = width;
+ }
+ if (height > maxHeight) {
+ maxHeight = height;
+ }
+
+ /*
+ * If the total amount of space needed in the parent window has
+ * changed, and if we're propagating geometry information, then
+ * notify the next geometry manager up and requeue ourselves to
+ * start again after the parent has had a chance to
+ * resize us.
+ */
+
+ if (((maxWidth != Tk_ReqWidth(masterPtr->tkwin))
+ || (maxHeight != Tk_ReqHeight(masterPtr->tkwin)))
+ && !(masterPtr->flags & DONT_PROPAGATE)) {
+ Tk_GeometryRequest(masterPtr->tkwin, maxWidth, maxHeight);
+ masterPtr->flags |= REQUESTED_REPACK;
+ Tcl_DoWhenIdle(ArrangePacking, (ClientData) masterPtr);
+ goto done;
+ }
+
+ /*
+ * Pass #2: scan the slaves a second time assigning
+ * new sizes. The "cavity" variables keep track of the
+ * unclaimed space in the cavity of the window; this
+ * shrinks inward as we allocate windows around the
+ * edges. The "frame" variables keep track of the space
+ * allocated to the current window and its frame. The
+ * current window is then placed somewhere inside the
+ * frame, depending on anchor.
+ */
+
+ cavityX = cavityY = x = y = intBWidth;
+ cavityWidth = Tk_Width(masterPtr->tkwin) - 2*intBWidth;
+ cavityHeight = Tk_Height(masterPtr->tkwin) - 2*intBWidth;
+ for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
+ slavePtr = slavePtr->nextPtr) {
+ if ((slavePtr->side == TOP) || (slavePtr->side == BOTTOM)) {
+ frameWidth = cavityWidth;
+ frameHeight = Tk_ReqHeight(slavePtr->tkwin) + slavePtr->doubleBw
+ + slavePtr->padY + slavePtr->iPadY;
+ if (slavePtr->flags & EXPAND) {
+ frameHeight += YExpansion(slavePtr, cavityHeight);
+ }
+ cavityHeight -= frameHeight;
+ if (cavityHeight < 0) {
+ frameHeight += cavityHeight;
+ cavityHeight = 0;
+ }
+ frameX = cavityX;
+ if (slavePtr->side == TOP) {
+ frameY = cavityY;
+ cavityY += frameHeight;
+ } else {
+ frameY = cavityY + cavityHeight;
+ }
+ } else {
+ frameHeight = cavityHeight;
+ frameWidth = Tk_ReqWidth(slavePtr->tkwin) + slavePtr->doubleBw
+ + slavePtr->padX + slavePtr->iPadX;
+ if (slavePtr->flags & EXPAND) {
+ frameWidth += XExpansion(slavePtr, cavityWidth);
+ }
+ cavityWidth -= frameWidth;
+ if (cavityWidth < 0) {
+ frameWidth += cavityWidth;
+ cavityWidth = 0;
+ }
+ frameY = cavityY;
+ if (slavePtr->side == LEFT) {
+ frameX = cavityX;
+ cavityX += frameWidth;
+ } else {
+ frameX = cavityX + cavityWidth;
+ }
+ }
+
+ /*
+ * Now that we've got the size of the frame for the window,
+ * compute the window's actual size and location using the
+ * fill, padding, and frame factors. The variables "borderX"
+ * and "borderY" are used to handle the differences between
+ * old-style packing and the new style (in old-style, iPadX
+ * and iPadY are always zero and padding is completely ignored
+ * except when computing frame size).
+ */
+
+ if (slavePtr->flags & OLD_STYLE) {
+ borderX = borderY = 0;
+ } else {
+ borderX = slavePtr->padX;
+ borderY = slavePtr->padY;
+ }
+ width = Tk_ReqWidth(slavePtr->tkwin) + slavePtr->doubleBw
+ + slavePtr->iPadX;
+ if ((slavePtr->flags & FILLX)
+ || (width > (frameWidth - borderX))) {
+ width = frameWidth - borderX;
+ }
+ height = Tk_ReqHeight(slavePtr->tkwin) + slavePtr->doubleBw
+ + slavePtr->iPadY;
+ if ((slavePtr->flags & FILLY)
+ || (height > (frameHeight - borderY))) {
+ height = frameHeight - borderY;
+ }
+ borderX /= 2;
+ borderY /= 2;
+ switch (slavePtr->anchor) {
+ case TK_ANCHOR_N:
+ x = frameX + (frameWidth - width)/2;
+ y = frameY + borderY;
+ break;
+ case TK_ANCHOR_NE:
+ x = frameX + frameWidth - width - borderX;
+ y = frameY + borderY;
+ break;
+ case TK_ANCHOR_E:
+ x = frameX + frameWidth - width - borderX;
+ y = frameY + (frameHeight - height)/2;
+ break;
+ case TK_ANCHOR_SE:
+ x = frameX + frameWidth - width - borderX;
+ y = frameY + frameHeight - height - borderY;
+ break;
+ case TK_ANCHOR_S:
+ x = frameX + (frameWidth - width)/2;
+ y = frameY + frameHeight - height - borderY;
+ break;
+ case TK_ANCHOR_SW:
+ x = frameX + borderX;
+ y = frameY + frameHeight - height - borderY;
+ break;
+ case TK_ANCHOR_W:
+ x = frameX + borderX;
+ y = frameY + (frameHeight - height)/2;
+ break;
+ case TK_ANCHOR_NW:
+ x = frameX + borderX;
+ y = frameY + borderY;
+ break;
+ case TK_ANCHOR_CENTER:
+ x = frameX + (frameWidth - width)/2;
+ y = frameY + (frameHeight - height)/2;
+ break;
+ default:
+ panic("bad frame factor in ArrangePacking");
+ }
+ width -= slavePtr->doubleBw;
+ height -= slavePtr->doubleBw;
+
+ /*
+ * The final step is to set the position, size, and mapped/unmapped
+ * state of the slave. If the slave is a child of the master, then
+ * do this here. Otherwise let Tk_MaintainGeometry do the work.
+ */
+
+ if (masterPtr->tkwin == Tk_Parent(slavePtr->tkwin)) {
+ if ((width <= 0) || (height <= 0)) {
+ Tk_UnmapWindow(slavePtr->tkwin);
+ } else {
+ if ((x != Tk_X(slavePtr->tkwin))
+ || (y != Tk_Y(slavePtr->tkwin))
+ || (width != Tk_Width(slavePtr->tkwin))
+ || (height != Tk_Height(slavePtr->tkwin))) {
+ Tk_MoveResizeWindow(slavePtr->tkwin, x, y, width, height);
+ }
+ if (abort) {
+ goto done;
+ }
+
+ /*
+ * Don't map the slave if the master isn't mapped: wait
+ * until the master gets mapped later.
+ */
+
+ if (Tk_IsMapped(masterPtr->tkwin)) {
+ Tk_MapWindow(slavePtr->tkwin);
+ }
+ }
+ } else {
+ if ((width <= 0) || (height <= 0)) {
+ Tk_UnmaintainGeometry(slavePtr->tkwin, masterPtr->tkwin);
+ Tk_UnmapWindow(slavePtr->tkwin);
+ } else {
+ Tk_MaintainGeometry(slavePtr->tkwin, masterPtr->tkwin,
+ x, y, width, height);
+ }
+ }
+
+ /*
+ * Changes to the window's structure could cause almost anything
+ * to happen, including deleting the parent or child. If this
+ * happens, we'll be told to abort.
+ */
+
+ if (abort) {
+ goto done;
+ }
+ }
+
+ done:
+ masterPtr->abortPtr = NULL;
+ Tcl_Release((ClientData) masterPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XExpansion --
+ *
+ * Given a list of packed slaves, the first of which is packed
+ * on the left or right and is expandable, compute how much to
+ * expand the child.
+ *
+ * Results:
+ * The return value is the number of additional pixels to give to
+ * the child.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+XExpansion(slavePtr, cavityWidth)
+ register Packer *slavePtr; /* First in list of remaining
+ * slaves. */
+ int cavityWidth; /* Horizontal space left for all
+ * remaining slaves. */
+{
+ int numExpand, minExpand, curExpand;
+ int childWidth;
+
+ /*
+ * This procedure is tricky because windows packed top or bottom can
+ * be interspersed among expandable windows packed left or right.
+ * Scan through the list, keeping a running sum of the widths of
+ * all left and right windows (actually, count the cavity space not
+ * allocated) and a running count of all expandable left and right
+ * windows. At each top or bottom window, and at the end of the
+ * list, compute the expansion factor that seems reasonable at that
+ * point. Return the smallest factor seen at any of these points.
+ */
+
+ minExpand = cavityWidth;
+ numExpand = 0;
+ for ( ; slavePtr != NULL; slavePtr = slavePtr->nextPtr) {
+ childWidth = Tk_ReqWidth(slavePtr->tkwin) + slavePtr->doubleBw
+ + slavePtr->padX + slavePtr->iPadX;
+ if ((slavePtr->side == TOP) || (slavePtr->side == BOTTOM)) {
+ curExpand = (cavityWidth - childWidth)/numExpand;
+ if (curExpand < minExpand) {
+ minExpand = curExpand;
+ }
+ } else {
+ cavityWidth -= childWidth;
+ if (slavePtr->flags & EXPAND) {
+ numExpand++;
+ }
+ }
+ }
+ curExpand = cavityWidth/numExpand;
+ if (curExpand < minExpand) {
+ minExpand = curExpand;
+ }
+ return (minExpand < 0) ? 0 : minExpand;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * YExpansion --
+ *
+ * Given a list of packed slaves, the first of which is packed
+ * on the top or bottom and is expandable, compute how much to
+ * expand the child.
+ *
+ * Results:
+ * The return value is the number of additional pixels to give to
+ * the child.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+YExpansion(slavePtr, cavityHeight)
+ register Packer *slavePtr; /* First in list of remaining
+ * slaves. */
+ int cavityHeight; /* Vertical space left for all
+ * remaining slaves. */
+{
+ int numExpand, minExpand, curExpand;
+ int childHeight;
+
+ /*
+ * See comments for XExpansion.
+ */
+
+ minExpand = cavityHeight;
+ numExpand = 0;
+ for ( ; slavePtr != NULL; slavePtr = slavePtr->nextPtr) {
+ childHeight = Tk_ReqHeight(slavePtr->tkwin) + slavePtr->doubleBw
+ + slavePtr->padY + slavePtr->iPadY;
+ if ((slavePtr->side == LEFT) || (slavePtr->side == RIGHT)) {
+ curExpand = (cavityHeight - childHeight)/numExpand;
+ if (curExpand < minExpand) {
+ minExpand = curExpand;
+ }
+ } else {
+ cavityHeight -= childHeight;
+ if (slavePtr->flags & EXPAND) {
+ numExpand++;
+ }
+ }
+ }
+ curExpand = cavityHeight/numExpand;
+ if (curExpand < minExpand) {
+ minExpand = curExpand;
+ }
+ return (minExpand < 0) ? 0 : minExpand;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * GetPacker --
+ *
+ * This internal procedure is used to locate a Packer
+ * structure for a given window, creating one if one
+ * doesn't exist already.
+ *
+ * Results:
+ * The return value is a pointer to the Packer structure
+ * corresponding to tkwin.
+ *
+ * Side effects:
+ * A new packer structure may be created. If so, then
+ * a callback is set up to clean things up when the
+ * window is deleted.
+ *
+ *--------------------------------------------------------------
+ */
+
+static Packer *
+GetPacker(tkwin)
+ Tk_Window tkwin; /* Token for window for which
+ * packer structure is desired. */
+{
+ register Packer *packPtr;
+ Tcl_HashEntry *hPtr;
+ int new;
+
+ if (!initialized) {
+ initialized = 1;
+ Tcl_InitHashTable(&packerHashTable, TCL_ONE_WORD_KEYS);
+ }
+
+ /*
+ * See if there's already packer for this window. If not,
+ * then create a new one.
+ */
+
+ hPtr = Tcl_CreateHashEntry(&packerHashTable, (char *) tkwin, &new);
+ if (!new) {
+ return (Packer *) Tcl_GetHashValue(hPtr);
+ }
+ packPtr = (Packer *) ckalloc(sizeof(Packer));
+ packPtr->tkwin = tkwin;
+ packPtr->masterPtr = NULL;
+ packPtr->nextPtr = NULL;
+ packPtr->slavePtr = NULL;
+ packPtr->side = TOP;
+ packPtr->anchor = TK_ANCHOR_CENTER;
+ packPtr->padX = packPtr->padY = 0;
+ packPtr->iPadX = packPtr->iPadY = 0;
+ packPtr->doubleBw = 2*Tk_Changes(tkwin)->border_width;
+ packPtr->abortPtr = NULL;
+ packPtr->flags = 0;
+ Tcl_SetHashValue(hPtr, packPtr);
+ Tk_CreateEventHandler(tkwin, StructureNotifyMask,
+ PackStructureProc, (ClientData) packPtr);
+ return packPtr;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * PackAfter --
+ *
+ * This procedure does most of the real work of adding
+ * one or more windows into the packing order for its parent.
+ *
+ * Results:
+ * A standard Tcl return value.
+ *
+ * Side effects:
+ * The geometry of the specified windows may change, both now and
+ * again in the future.
+ *
+ *--------------------------------------------------------------
+ */
+
+static int
+PackAfter(interp, prevPtr, masterPtr, argc, argv)
+ Tcl_Interp *interp; /* Interpreter for error reporting. */
+ Packer *prevPtr; /* Pack windows in argv just after this
+ * window; NULL means pack as first
+ * child of masterPtr. */
+ Packer *masterPtr; /* Master in which to pack windows. */
+ int argc; /* Number of elements in argv. */
+ char **argv; /* Array of lists, each containing 2
+ * elements: window name and side
+ * against which to pack. */
+{
+ register Packer *packPtr;
+ Tk_Window tkwin, ancestor, parent;
+ size_t length;
+ char **options;
+ int index, tmp, optionCount, c;
+
+ /*
+ * Iterate over all of the window specifiers, each consisting of
+ * two arguments. The first argument contains the window name and
+ * the additional arguments contain options such as "top" or
+ * "padx 20".
+ */
+
+ for ( ; argc > 0; argc -= 2, argv += 2, prevPtr = packPtr) {
+ if (argc < 2) {
+ Tcl_AppendResult(interp, "wrong # args: window \"",
+ argv[0], "\" should be followed by options",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ /*
+ * Find the packer for the window to be packed, and make sure
+ * that the window in which it will be packed is either its
+ * or a descendant of its parent.
+ */
+
+ tkwin = Tk_NameToWindow(interp, argv[0], masterPtr->tkwin);
+ if (tkwin == NULL) {
+ return TCL_ERROR;
+ }
+
+ parent = Tk_Parent(tkwin);
+ for (ancestor = masterPtr->tkwin; ; ancestor = Tk_Parent(ancestor)) {
+ if (ancestor == parent) {
+ break;
+ }
+ if (((Tk_FakeWin *) (ancestor))->flags & TK_TOP_LEVEL) {
+ badWindow:
+ Tcl_AppendResult(interp, "can't pack ", argv[0],
+ " inside ", Tk_PathName(masterPtr->tkwin),
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ }
+ if (((Tk_FakeWin *) (tkwin))->flags & TK_TOP_LEVEL) {
+ goto badWindow;
+ }
+ if (tkwin == masterPtr->tkwin) {
+ goto badWindow;
+ }
+ packPtr = GetPacker(tkwin);
+
+ /*
+ * Process options for this window.
+ */
+
+ if (Tcl_SplitList(interp, argv[1], &optionCount, &options) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ packPtr->side = TOP;
+ packPtr->anchor = TK_ANCHOR_CENTER;
+ packPtr->padX = packPtr->padY = 0;
+ packPtr->iPadX = packPtr->iPadY = 0;
+ packPtr->flags &= ~(FILLX|FILLY|EXPAND);
+ packPtr->flags |= OLD_STYLE;
+ for (index = 0 ; index < optionCount; index++) {
+ char *curOpt = options[index];
+
+ c = curOpt[0];
+ length = strlen(curOpt);
+
+ if ((c == 't')
+ && (strncmp(curOpt, "top", length)) == 0) {
+ packPtr->side = TOP;
+ } else if ((c == 'b')
+ && (strncmp(curOpt, "bottom", length)) == 0) {
+ packPtr->side = BOTTOM;
+ } else if ((c == 'l')
+ && (strncmp(curOpt, "left", length)) == 0) {
+ packPtr->side = LEFT;
+ } else if ((c == 'r')
+ && (strncmp(curOpt, "right", length)) == 0) {
+ packPtr->side = RIGHT;
+ } else if ((c == 'e')
+ && (strncmp(curOpt, "expand", length)) == 0) {
+ packPtr->flags |= EXPAND;
+ } else if ((c == 'f')
+ && (strcmp(curOpt, "fill")) == 0) {
+ packPtr->flags |= FILLX|FILLY;
+ } else if ((length == 5) && (strcmp(curOpt, "fillx")) == 0) {
+ packPtr->flags |= FILLX;
+ } else if ((length == 5) && (strcmp(curOpt, "filly")) == 0) {
+ packPtr->flags |= FILLY;
+ } else if ((c == 'p') && (strcmp(curOpt, "padx")) == 0) {
+ if (optionCount < (index+2)) {
+ missingPad:
+ Tcl_AppendResult(interp, "wrong # args: \"", curOpt,
+ "\" option must be followed by screen distance",
+ (char *) NULL);
+ goto error;
+ }
+ if ((Tk_GetPixels(interp, tkwin, options[index+1], &tmp)
+ != TCL_OK) || (tmp < 0)) {
+ badPad:
+ Tcl_AppendResult(interp, "bad pad value \"",
+ options[index+1],
+ "\": must be positive screen distance",
+ (char *) NULL);
+ goto error;
+ }
+ packPtr->padX = tmp;
+ packPtr->iPadX = 0;
+ index++;
+ } else if ((c == 'p') && (strcmp(curOpt, "pady")) == 0) {
+ if (optionCount < (index+2)) {
+ goto missingPad;
+ }
+ if ((Tk_GetPixels(interp, tkwin, options[index+1], &tmp)
+ != TCL_OK) || (tmp < 0)) {
+ goto badPad;
+ }
+ packPtr->padY = tmp;
+ packPtr->iPadY = 0;
+ index++;
+ } else if ((c == 'f') && (length > 1)
+ && (strncmp(curOpt, "frame", length) == 0)) {
+ if (optionCount < (index+2)) {
+ Tcl_AppendResult(interp, "wrong # args: \"frame\" ",
+ "option must be followed by anchor point",
+ (char *) NULL);
+ goto error;
+ }
+ if (Tk_GetAnchor(interp, options[index+1],
+ &packPtr->anchor) != TCL_OK) {
+ goto error;
+ }
+ index++;
+ } else {
+ Tcl_AppendResult(interp, "bad option \"", curOpt,
+ "\": should be top, bottom, left, right, ",
+ "expand, fill, fillx, filly, padx, pady, or frame",
+ (char *) NULL);
+ goto error;
+ }
+ }
+
+ if (packPtr != prevPtr) {
+
+ /*
+ * Unpack this window if it's currently packed.
+ */
+
+ if (packPtr->masterPtr != NULL) {
+ if ((packPtr->masterPtr != masterPtr) &&
+ (packPtr->masterPtr->tkwin
+ != Tk_Parent(packPtr->tkwin))) {
+ Tk_UnmaintainGeometry(packPtr->tkwin,
+ packPtr->masterPtr->tkwin);
+ }
+ Unlink(packPtr);
+ }
+
+ /*
+ * Add the window in the correct place in its parent's
+ * packing order, then make sure that the window is
+ * managed by us.
+ */
+
+ packPtr->masterPtr = masterPtr;
+ if (prevPtr == NULL) {
+ packPtr->nextPtr = masterPtr->slavePtr;
+ masterPtr->slavePtr = packPtr;
+ } else {
+ packPtr->nextPtr = prevPtr->nextPtr;
+ prevPtr->nextPtr = packPtr;
+ }
+ Tk_ManageGeometry(tkwin, &packerType, (ClientData) packPtr);
+ }
+ ckfree((char *) options);
+ }
+
+ /*
+ * Arrange for the parent to be re-packed at the first
+ * idle moment.
+ */
+
+ if (masterPtr->abortPtr != NULL) {
+ *masterPtr->abortPtr = 1;
+ }
+ if (!(masterPtr->flags & REQUESTED_REPACK)) {
+ masterPtr->flags |= REQUESTED_REPACK;
+ Tcl_DoWhenIdle(ArrangePacking, (ClientData) masterPtr);
+ }
+ return TCL_OK;
+
+ error:
+ ckfree((char *) options);
+ return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Unlink --
+ *
+ * Remove a packer from its parent's list of slaves.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The parent will be scheduled for repacking.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+Unlink(packPtr)
+ register Packer *packPtr; /* Window to unlink. */
+{
+ register Packer *masterPtr, *packPtr2;
+
+ masterPtr = packPtr->masterPtr;
+ if (masterPtr == NULL) {
+ return;
+ }
+ if (masterPtr->slavePtr == packPtr) {
+ masterPtr->slavePtr = packPtr->nextPtr;
+ } else {
+ for (packPtr2 = masterPtr->slavePtr; ; packPtr2 = packPtr2->nextPtr) {
+ if (packPtr2 == NULL) {
+ panic("Unlink couldn't find previous window");
+ }
+ if (packPtr2->nextPtr == packPtr) {
+ packPtr2->nextPtr = packPtr->nextPtr;
+ break;
+ }
+ }
+ }
+ if (!(masterPtr->flags & REQUESTED_REPACK)) {
+ masterPtr->flags |= REQUESTED_REPACK;
+ Tcl_DoWhenIdle(ArrangePacking, (ClientData) masterPtr);
+ }
+ if (masterPtr->abortPtr != NULL) {
+ *masterPtr->abortPtr = 1;
+ }
+
+ packPtr->masterPtr = NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DestroyPacker --
+ *
+ * This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
+ * to clean up the internal structure of a packer at a safe time
+ * (when no-one is using it anymore).
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Everything associated with the packer is freed up.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DestroyPacker(memPtr)
+ char *memPtr; /* Info about packed window that
+ * is now dead. */
+{
+ register Packer *packPtr = (Packer *) memPtr;
+ ckfree((char *) packPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PackStructureProc --
+ *
+ * This procedure is invoked by the Tk event dispatcher in response
+ * to StructureNotify events.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * If a window was just deleted, clean up all its packer-related
+ * information. If it was just resized, repack its slaves, if
+ * any.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+PackStructureProc(clientData, eventPtr)
+ ClientData clientData; /* Our information about window
+ * referred to by eventPtr. */
+ XEvent *eventPtr; /* Describes what just happened. */
+{
+ register Packer *packPtr = (Packer *) clientData;
+ if (eventPtr->type == ConfigureNotify) {
+ if ((packPtr->slavePtr != NULL)
+ && !(packPtr->flags & REQUESTED_REPACK)) {
+ packPtr->flags |= REQUESTED_REPACK;
+ Tcl_DoWhenIdle(ArrangePacking, (ClientData) packPtr);
+ }
+ if (packPtr->doubleBw != 2*Tk_Changes(packPtr->tkwin)->border_width) {
+ if ((packPtr->masterPtr != NULL)
+ && !(packPtr->masterPtr->flags & REQUESTED_REPACK)) {
+ packPtr->doubleBw = 2*Tk_Changes(packPtr->tkwin)->border_width;
+ packPtr->masterPtr->flags |= REQUESTED_REPACK;
+ Tcl_DoWhenIdle(ArrangePacking, (ClientData) packPtr->masterPtr);
+ }
+ }
+ } else if (eventPtr->type == DestroyNotify) {
+ register Packer *slavePtr, *nextPtr;
+
+ if (packPtr->masterPtr != NULL) {
+ Unlink(packPtr);
+ }
+ for (slavePtr = packPtr->slavePtr; slavePtr != NULL;
+ slavePtr = nextPtr) {
+ Tk_ManageGeometry(slavePtr->tkwin, (Tk_GeomMgr *) NULL,
+ (ClientData) NULL);
+ Tk_UnmapWindow(slavePtr->tkwin);
+ slavePtr->masterPtr = NULL;
+ nextPtr = slavePtr->nextPtr;
+ slavePtr->nextPtr = NULL;
+ }
+ Tcl_DeleteHashEntry(Tcl_FindHashEntry(&packerHashTable,
+ (char *) packPtr->tkwin));
+ if (packPtr->flags & REQUESTED_REPACK) {
+ Tcl_CancelIdleCall(ArrangePacking, (ClientData) packPtr);
+ }
+ packPtr->tkwin = NULL;
+ Tcl_EventuallyFree((ClientData) packPtr, DestroyPacker);
+ } else if (eventPtr->type == MapNotify) {
+ /*
+ * When a master gets mapped, must redo the geometry computation
+ * so that all of its slaves get remapped.
+ */
+
+ if ((packPtr->slavePtr != NULL)
+ && !(packPtr->flags & REQUESTED_REPACK)) {
+ packPtr->flags |= REQUESTED_REPACK;
+ Tcl_DoWhenIdle(ArrangePacking, (ClientData) packPtr);
+ }
+ } else if (eventPtr->type == UnmapNotify) {
+ Packer *packPtr2;
+
+ /*
+ * Unmap all of the slaves when the master gets unmapped,
+ * so that they don't bother to keep redisplaying
+ * themselves.
+ */
+
+ for (packPtr2 = packPtr->slavePtr; packPtr2 != NULL;
+ packPtr2 = packPtr2->nextPtr) {
+ Tk_UnmapWindow(packPtr2->tkwin);
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureSlaves --
+ *
+ * This implements the guts of the "pack configure" command. Given
+ * a list of slaves and configuration options, it arranges for the
+ * packer to manage the slaves and sets the specified options.
+ *
+ * Results:
+ * TCL_OK is returned if all went well. Otherwise, TCL_ERROR is
+ * returned and interp->result is set to contain an error message.
+ *
+ * Side effects:
+ * Slave windows get taken over by the packer.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+ConfigureSlaves(interp, tkwin, argc, argv)
+ Tcl_Interp *interp; /* Interpreter for error reporting. */
+ Tk_Window tkwin; /* Any window in application containing
+ * slaves. Used to look up slave names. */
+ int argc; /* Number of elements in argv. */
+ char *argv[]; /* Argument strings: contains one or more
+ * window names followed by any number
+ * of "option value" pairs. Caller must
+ * make sure that there is at least one
+ * window name. */
+{
+ Packer *masterPtr, *slavePtr, *prevPtr, *otherPtr;
+ Tk_Window other, slave, parent, ancestor;
+ int i, j, numWindows, c, tmp, positionGiven;
+ size_t length;
+
+ /*
+ * Find out how many windows are specified.
+ */
+
+ for (numWindows = 0; numWindows < argc; numWindows++) {
+ if (argv[numWindows][0] != '.') {
+ break;
+ }
+ }
+
+ /*
+ * Iterate over all of the slave windows, parsing the configuration
+ * options for each slave. It's a bit wasteful to re-parse the
+ * options for each slave, but things get too messy if we try to
+ * parse the arguments just once at the beginning. For example,
+ * if a slave already is packed we want to just change a few
+ * existing values without resetting everything. If there are
+ * multiple windows, the -after, -before, and -in options only
+ * get processed for the first window.
+ */
+
+ masterPtr = NULL;
+ prevPtr = NULL;
+ positionGiven = 0;
+ for (j = 0; j < numWindows; j++) {
+ slave = Tk_NameToWindow(interp, argv[j], tkwin);
+ if (slave == NULL) {
+ return TCL_ERROR;
+ }
+ if (Tk_IsTopLevel(slave)) {
+ Tcl_AppendResult(interp, "can't pack \"", argv[j],
+ "\": it's a top-level window", (char *) NULL);
+ return TCL_ERROR;
+ }
+ slavePtr = GetPacker(slave);
+ slavePtr->flags &= ~OLD_STYLE;
+
+ /*
+ * If the slave isn't currently packed, reset all of its
+ * configuration information to default values (there could
+ * be old values left from a previous packing).
+ */
+
+ if (slavePtr->masterPtr == NULL) {
+ slavePtr->side = TOP;
+ slavePtr->anchor = TK_ANCHOR_CENTER;
+ slavePtr->padX = slavePtr->padY = 0;
+ slavePtr->iPadX = slavePtr->iPadY = 0;
+ slavePtr->flags &= ~(FILLX|FILLY|EXPAND);
+ }
+
+ for (i = numWindows; i < argc; i+=2) {
+ if ((i+2) > argc) {
+ Tcl_AppendResult(interp, "extra option \"", argv[i],
+ "\" (option with no value?)", (char *) NULL);
+ return TCL_ERROR;
+ }
+ length = strlen(argv[i]);
+ if (length < 2) {
+ goto badOption;
+ }
+ c = argv[i][1];
+ if ((c == 'a') && (strncmp(argv[i], "-after", length) == 0)
+ && (length >= 2)) {
+ if (j == 0) {
+ other = Tk_NameToWindow(interp, argv[i+1], tkwin);
+ if (other == NULL) {
+ return TCL_ERROR;
+ }
+ prevPtr = GetPacker(other);
+ if (prevPtr->masterPtr == NULL) {
+ notPacked:
+ Tcl_AppendResult(interp, "window \"", argv[i+1],
+ "\" isn't packed", (char *) NULL);
+ return TCL_ERROR;
+ }
+ masterPtr = prevPtr->masterPtr;
+ positionGiven = 1;
+ }
+ } else if ((c == 'a') && (strncmp(argv[i], "-anchor", length) == 0)
+ && (length >= 2)) {
+ if (Tk_GetAnchor(interp, argv[i+1], &slavePtr->anchor)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ } else if ((c == 'b')
+ && (strncmp(argv[i], "-before", length) == 0)) {
+ if (j == 0) {
+ other = Tk_NameToWindow(interp, argv[i+1], tkwin);
+ if (other == NULL) {
+ return TCL_ERROR;
+ }
+ otherPtr = GetPacker(other);
+ if (otherPtr->masterPtr == NULL) {
+ goto notPacked;
+ }
+ masterPtr = otherPtr->masterPtr;
+ prevPtr = masterPtr->slavePtr;
+ if (prevPtr == otherPtr) {
+ prevPtr = NULL;
+ } else {
+ while (prevPtr->nextPtr != otherPtr) {
+ prevPtr = prevPtr->nextPtr;
+ }
+ }
+ positionGiven = 1;
+ }
+ } else if ((c == 'e')
+ && (strncmp(argv[i], "-expand", length) == 0)) {
+ if (Tcl_GetBoolean(interp, argv[i+1], &tmp) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ slavePtr->flags &= ~EXPAND;
+ if (tmp) {
+ slavePtr->flags |= EXPAND;
+ }
+ } else if ((c == 'f') && (strncmp(argv[i], "-fill", length) == 0)) {
+ if (strcmp(argv[i+1], "none") == 0) {
+ slavePtr->flags &= ~(FILLX|FILLY);
+ } else if (strcmp(argv[i+1], "x") == 0) {
+ slavePtr->flags = (slavePtr->flags & ~FILLY) | FILLX;
+ } else if (strcmp(argv[i+1], "y") == 0) {
+ slavePtr->flags = (slavePtr->flags & ~FILLX) | FILLY;
+ } else if (strcmp(argv[i+1], "both") == 0) {
+ slavePtr->flags |= FILLX|FILLY;
+ } else {
+ Tcl_AppendResult(interp, "bad fill style \"", argv[i+1],
+ "\": must be none, x, y, or both", (char *) NULL);
+ return TCL_ERROR;
+ }
+ } else if ((c == 'i') && (strcmp(argv[i], "-in") == 0)) {
+ if (j == 0) {
+ other = Tk_NameToWindow(interp, argv[i+1], tkwin);
+ if (other == NULL) {
+ return TCL_ERROR;
+ }
+ masterPtr = GetPacker(other);
+ prevPtr = masterPtr->slavePtr;
+ if (prevPtr != NULL) {
+ while (prevPtr->nextPtr != NULL) {
+ prevPtr = prevPtr->nextPtr;
+ }
+ }
+ positionGiven = 1;
+ }
+ } else if ((c == 'i') && (strcmp(argv[i], "-ipadx") == 0)) {
+ if ((Tk_GetPixels(interp, slave, argv[i+1], &tmp) != TCL_OK)
+ || (tmp < 0)) {
+ badPad:
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "bad pad value \"", argv[i+1],
+ "\": must be positive screen distance",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ slavePtr->iPadX = tmp*2;
+ } else if ((c == 'i') && (strcmp(argv[i], "-ipady") == 0)) {
+ if ((Tk_GetPixels(interp, slave, argv[i+1], &tmp) != TCL_OK)
+ || (tmp< 0)) {
+ goto badPad;
+ }
+ slavePtr->iPadY = tmp*2;
+ } else if ((c == 'p') && (strcmp(argv[i], "-padx") == 0)) {
+ if ((Tk_GetPixels(interp, slave, argv[i+1], &tmp) != TCL_OK)
+ || (tmp< 0)) {
+ goto badPad;
+ }
+ slavePtr->padX = tmp*2;
+ } else if ((c == 'p') && (strcmp(argv[i], "-pady") == 0)) {
+ if ((Tk_GetPixels(interp, slave, argv[i+1], &tmp) != TCL_OK)
+ || (tmp< 0)) {
+ goto badPad;
+ }
+ slavePtr->padY = tmp*2;
+ } else if ((c == 's') && (strncmp(argv[i], "-side", length) == 0)) {
+ c = argv[i+1][0];
+ if ((c == 't') && (strcmp(argv[i+1], "top") == 0)) {
+ slavePtr->side = TOP;
+ } else if ((c == 'b') && (strcmp(argv[i+1], "bottom") == 0)) {
+ slavePtr->side = BOTTOM;
+ } else if ((c == 'l') && (strcmp(argv[i+1], "left") == 0)) {
+ slavePtr->side = LEFT;
+ } else if ((c == 'r') && (strcmp(argv[i+1], "right") == 0)) {
+ slavePtr->side = RIGHT;
+ } else {
+ Tcl_AppendResult(interp, "bad side \"", argv[i+1],
+ "\": must be top, bottom, left, or right",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ } else {
+ badOption:
+ Tcl_AppendResult(interp, "unknown or ambiguous option \"",
+ argv[i], "\": must be -after, -anchor, -before, ",
+ "-expand, -fill, -in, -ipadx, -ipady, -padx, ",
+ "-pady, or -side", (char *) NULL);
+ return TCL_ERROR;
+ }
+ }
+
+ /*
+ * If no position in a packing list was specified and the slave
+ * is already packed, then leave it in its current location in
+ * its current packing list.
+ */
+
+ if (!positionGiven && (slavePtr->masterPtr != NULL)) {
+ masterPtr = slavePtr->masterPtr;
+ goto scheduleLayout;
+ }
+
+ /*
+ * If the slave is going to be put back after itself then
+ * skip the whole operation, since it won't work anyway.
+ */
+
+ if (prevPtr == slavePtr) {
+ masterPtr = slavePtr->masterPtr;
+ goto scheduleLayout;
+ }
+
+ /*
+ * If none of the "-in", "-before", or "-after" options has
+ * been specified, arrange for the slave to go at the end of
+ * the order for its parent.
+ */
+
+ if (!positionGiven) {
+ masterPtr = GetPacker(Tk_Parent(slave));
+ prevPtr = masterPtr->slavePtr;
+ if (prevPtr != NULL) {
+ while (prevPtr->nextPtr != NULL) {
+ prevPtr = prevPtr->nextPtr;
+ }
+ }
+ }
+
+ /*
+ * Make sure that the slave's parent is either the master or
+ * an ancestor of the master, and that the master and slave
+ * aren't the same.
+ */
+
+ parent = Tk_Parent(slave);
+ for (ancestor = masterPtr->tkwin; ; ancestor = Tk_Parent(ancestor)) {
+ if (ancestor == parent) {
+ break;
+ }
+ if (Tk_IsTopLevel(ancestor)) {
+ Tcl_AppendResult(interp, "can't pack ", argv[j],
+ " inside ", Tk_PathName(masterPtr->tkwin),
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ }
+ if (slave == masterPtr->tkwin) {
+ Tcl_AppendResult(interp, "can't pack ", argv[j],
+ " inside itself", (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ /*
+ * Unpack the slave if it's currently packed, then position it
+ * after prevPtr.
+ */
+
+ if (slavePtr->masterPtr != NULL) {
+ if ((slavePtr->masterPtr != masterPtr) &&
+ (slavePtr->masterPtr->tkwin
+ != Tk_Parent(slavePtr->tkwin))) {
+ Tk_UnmaintainGeometry(slavePtr->tkwin,
+ slavePtr->masterPtr->tkwin);
+ }
+ Unlink(slavePtr);
+ }
+ slavePtr->masterPtr = masterPtr;
+ if (prevPtr == NULL) {
+ slavePtr->nextPtr = masterPtr->slavePtr;
+ masterPtr->slavePtr = slavePtr;
+ } else {
+ slavePtr->nextPtr = prevPtr->nextPtr;
+ prevPtr->nextPtr = slavePtr;
+ }
+ Tk_ManageGeometry(slave, &packerType, (ClientData) slavePtr);
+ prevPtr = slavePtr;
+
+ /*
+ * Arrange for the parent to be re-packed at the first
+ * idle moment.
+ */
+
+ scheduleLayout:
+ if (masterPtr->abortPtr != NULL) {
+ *masterPtr->abortPtr = 1;
+ }
+ if (!(masterPtr->flags & REQUESTED_REPACK)) {
+ masterPtr->flags |= REQUESTED_REPACK;
+ Tcl_DoWhenIdle(ArrangePacking, (ClientData) masterPtr);
+ }
+ }
+ return TCL_OK;
+}