summaryrefslogtreecommitdiffstats
path: root/tk8.6/generic/tkPack.c
diff options
context:
space:
mode:
authorWilliam Joye <wjoye@cfa.harvard.edu>2016-10-18 17:31:55 (GMT)
committerWilliam Joye <wjoye@cfa.harvard.edu>2016-10-18 17:31:55 (GMT)
commit39e34335fb6eb6eaf2b7ee51ccf172006dd46fbb (patch)
tree8e5374666c7f0b3017176ec9d6e6b6eae0dcabac /tk8.6/generic/tkPack.c
parent066971b1e6e77991d9161bb0216a63ba94ea04f9 (diff)
parent6b095f3c8521ca7215e6ff5dcbada52b197ef7d0 (diff)
downloadblt-39e34335fb6eb6eaf2b7ee51ccf172006dd46fbb.zip
blt-39e34335fb6eb6eaf2b7ee51ccf172006dd46fbb.tar.gz
blt-39e34335fb6eb6eaf2b7ee51ccf172006dd46fbb.tar.bz2
Merge commit '6b095f3c8521ca7215e6ff5dcbada52b197ef7d0' as 'tk8.6'
Diffstat (limited to 'tk8.6/generic/tkPack.c')
-rw-r--r--tk8.6/generic/tkPack.c1859
1 files changed, 1859 insertions, 0 deletions
diff --git a/tk8.6/generic/tkPack.c b/tk8.6/generic/tkPack.c
new file mode 100644
index 0000000..88a4b2d
--- /dev/null
+++ b/tk8.6/generic/tkPack.c
@@ -0,0 +1,1859 @@
+/*
+ * 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-1997 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution of
+ * this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+#include "tkInt.h"
+
+typedef enum {TOP, BOTTOM, LEFT, RIGHT} Side;
+static const char *const sideNames[] = {
+ "top", "bottom", "left", "right", NULL
+};
+
+/*
+ * 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 master. 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 master 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. Some is of this space is on each
+ * side. This is space *outside* the window:
+ * we'll allocate extra space in frame but
+ * won't enlarge window). */
+ int padLeft, padTop; /* The part of padX or padY to use on the left
+ * or top of the widget, respectively. By
+ * default, this is half of padX or padY. */
+ int iPadX, iPadY; /* Total extra pixels to allocate inside the
+ * window (half of 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 master. */
+ 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 master 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.
+ * ALLOCED_MASTER 1 means that Pack has allocated itself as
+ * geometry master for this window.
+ */
+
+#define REQUESTED_REPACK 1
+#define FILLX 2
+#define FILLY 4
+#define EXPAND 8
+#define OLD_STYLE 16
+#define DONT_PROPAGATE 32
+#define ALLOCED_MASTER 64
+
+/*
+ * The following structure is the official type record for the packer:
+ */
+
+static void PackReqProc(ClientData clientData, Tk_Window tkwin);
+static void PackLostSlaveProc(ClientData clientData,
+ Tk_Window tkwin);
+
+static const Tk_GeomMgr packerType = {
+ "pack", /* name */
+ PackReqProc, /* requestProc */
+ PackLostSlaveProc, /* lostSlaveProc */
+};
+
+/*
+ * Forward declarations for functions defined later in this file:
+ */
+
+static void ArrangePacking(ClientData clientData);
+static int ConfigureSlaves(Tcl_Interp *interp, Tk_Window tkwin,
+ int objc, Tcl_Obj *const objv[]);
+static void DestroyPacker(void *memPtr);
+static Packer * GetPacker(Tk_Window tkwin);
+static int PackAfter(Tcl_Interp *interp, Packer *prevPtr,
+ Packer *masterPtr, int objc,Tcl_Obj *const objv[]);
+static void PackStructureProc(ClientData clientData,
+ XEvent *eventPtr);
+static void Unlink(Packer *packPtr);
+static int XExpansion(Packer *slavePtr, int cavityWidth);
+static int YExpansion(Packer *slavePtr, int cavityHeight);
+
+/*
+ *------------------------------------------------------------------------
+ *
+ * TkAppendPadAmount --
+ *
+ * This function generates a text value that describes one of the -padx,
+ * -pady, -ipadx, or -ipady configuration options. The text value
+ * generated is appended to the given Tcl_Obj.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *------------------------------------------------------------------------
+ */
+
+void
+TkAppendPadAmount(
+ Tcl_Obj *bufferObj, /* The interpreter into which the result is
+ * written. */
+ const char *switchName, /* One of "padx", "pady", "ipadx" or
+ * "ipady" */
+ int halfSpace, /* The left or top padding amount */
+ int allSpace) /* The total amount of padding */
+{
+ Tcl_Obj *padding[2];
+
+ if (halfSpace*2 == allSpace) {
+ Tcl_DictObjPut(NULL, bufferObj, Tcl_NewStringObj(switchName, -1),
+ Tcl_NewIntObj(halfSpace));
+ } else {
+ padding[0] = Tcl_NewIntObj(halfSpace);
+ padding[1] = Tcl_NewIntObj(allSpace - halfSpace);
+ Tcl_DictObjPut(NULL, bufferObj, Tcl_NewStringObj(switchName, -1),
+ Tcl_NewListObj(2, padding));
+ }
+}
+
+/*
+ *------------------------------------------------------------------------
+ *
+ * Tk_PackCmd --
+ *
+ * This function 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_PackObjCmd(
+ ClientData clientData, /* Main window associated with interpreter. */
+ Tcl_Interp *interp, /* Current interpreter. */
+ int objc, /* Number of arguments. */
+ Tcl_Obj *const objv[]) /* Argument objects. */
+{
+ Tk_Window tkwin = clientData;
+ const char *argv2;
+ static const char *const optionStrings[] = {
+ /* after, append, before and unpack are deprecated */
+ "after", "append", "before", "unpack",
+ "configure", "forget", "info", "propagate", "slaves", NULL };
+ enum options {
+ PACK_AFTER, PACK_APPEND, PACK_BEFORE, PACK_UNPACK,
+ PACK_CONFIGURE, PACK_FORGET, PACK_INFO, PACK_PROPAGATE, PACK_SLAVES };
+ int index;
+
+ if (objc >= 2) {
+ const char *string = Tcl_GetString(objv[1]);
+
+ if (string[0] == '.') {
+ return ConfigureSlaves(interp, tkwin, objc-1, objv+1);
+ }
+ }
+ if (objc < 3) {
+ Tcl_WrongNumArgs(interp, 1, objv, "option arg ?arg ...?");
+ return TCL_ERROR;
+ }
+
+ if (Tcl_GetIndexFromObjStruct(interp, objv[1], optionStrings,
+ sizeof(char *), "option", 0, &index) != TCL_OK) {
+ /*
+ * Call it again without the deprecated ones to get a proper error
+ * message. This works well since there can't be any ambiguity between
+ * deprecated and new options.
+ */
+
+ Tcl_ResetResult(interp);
+ Tcl_GetIndexFromObjStruct(interp, objv[1], &optionStrings[4],
+ sizeof(char *), "option", 0, &index);
+ return TCL_ERROR;
+ }
+
+ argv2 = Tcl_GetString(objv[2]);
+ switch ((enum options) index) {
+ case PACK_AFTER: {
+ Packer *prevPtr;
+ Tk_Window tkwin2;
+
+ if (TkGetWindowFromObj(interp, tkwin, objv[2], &tkwin2) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ prevPtr = GetPacker(tkwin2);
+ if (prevPtr->masterPtr == NULL) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "window \"%s\" isn't packed", argv2));
+ Tcl_SetErrorCode(interp, "TK", "PACK", "NOT_PACKED", NULL);
+ return TCL_ERROR;
+ }
+ return PackAfter(interp, prevPtr, prevPtr->masterPtr, objc-3, objv+3);
+ }
+ case PACK_APPEND: {
+ Packer *masterPtr;
+ register Packer *prevPtr;
+ Tk_Window tkwin2;
+
+ if (TkGetWindowFromObj(interp, tkwin, objv[2], &tkwin2) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ masterPtr = GetPacker(tkwin2);
+ prevPtr = masterPtr->slavePtr;
+ if (prevPtr != NULL) {
+ while (prevPtr->nextPtr != NULL) {
+ prevPtr = prevPtr->nextPtr;
+ }
+ }
+ return PackAfter(interp, prevPtr, masterPtr, objc-3, objv+3);
+ }
+ case PACK_BEFORE: {
+ Packer *packPtr, *masterPtr;
+ register Packer *prevPtr;
+ Tk_Window tkwin2;
+
+ if (TkGetWindowFromObj(interp, tkwin, objv[2], &tkwin2) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ packPtr = GetPacker(tkwin2);
+ if (packPtr->masterPtr == NULL) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "window \"%s\" isn't packed", argv2));
+ Tcl_SetErrorCode(interp, "TK", "PACK", "NOT_PACKED", NULL);
+ return TCL_ERROR;
+ }
+ masterPtr = packPtr->masterPtr;
+ prevPtr = masterPtr->slavePtr;
+ if (prevPtr == packPtr) {
+ prevPtr = NULL;
+ } else {
+ for ( ; ; prevPtr = prevPtr->nextPtr) {
+ if (prevPtr == NULL) {
+ Tcl_Panic("\"pack before\" couldn't find predecessor");
+ }
+ if (prevPtr->nextPtr == packPtr) {
+ break;
+ }
+ }
+ }
+ return PackAfter(interp, prevPtr, masterPtr, objc-3, objv+3);
+ }
+ case PACK_CONFIGURE:
+ if (argv2[0] != '.') {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "bad argument \"%s\": must be name of window", argv2));
+ Tcl_SetErrorCode(interp, "TK", "VALUE", "WINDOW_PATH", NULL);
+ return TCL_ERROR;
+ }
+ return ConfigureSlaves(interp, tkwin, objc-2, objv+2);
+ case PACK_FORGET: {
+ Tk_Window slave;
+ Packer *slavePtr;
+ int i;
+
+ for (i = 2; i < objc; i++) {
+ if (TkGetWindowFromObj(interp, tkwin, objv[i], &slave) != TCL_OK) {
+ continue;
+ }
+ slavePtr = GetPacker(slave);
+ if ((slavePtr != NULL) && (slavePtr->masterPtr != NULL)) {
+ Tk_ManageGeometry(slave, NULL, NULL);
+ if (slavePtr->masterPtr->tkwin != Tk_Parent(slavePtr->tkwin)) {
+ Tk_UnmaintainGeometry(slavePtr->tkwin,
+ slavePtr->masterPtr->tkwin);
+ }
+ Unlink(slavePtr);
+ Tk_UnmapWindow(slavePtr->tkwin);
+ }
+ }
+ break;
+ }
+ case PACK_INFO: {
+ register Packer *slavePtr;
+ Tk_Window slave;
+ Tcl_Obj *infoObj;
+
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "window");
+ return TCL_ERROR;
+ }
+ if (TkGetWindowFromObj(interp, tkwin, objv[2], &slave) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ slavePtr = GetPacker(slave);
+ if (slavePtr->masterPtr == NULL) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "window \"%s\" isn't packed", argv2));
+ Tcl_SetErrorCode(interp, "TK", "PACK", "NOT_PACKED", NULL);
+ return TCL_ERROR;
+ }
+
+ infoObj = Tcl_NewObj();
+ Tcl_DictObjPut(NULL, infoObj, Tcl_NewStringObj("-in", -1),
+ TkNewWindowObj(slavePtr->masterPtr->tkwin));
+ Tcl_DictObjPut(NULL, infoObj, Tcl_NewStringObj("-anchor", -1),
+ Tcl_NewStringObj(Tk_NameOfAnchor(slavePtr->anchor), -1));
+ Tcl_DictObjPut(NULL, infoObj, Tcl_NewStringObj("-expand", -1),
+ Tcl_NewBooleanObj(slavePtr->flags & EXPAND));
+ switch (slavePtr->flags & (FILLX|FILLY)) {
+ case 0:
+ Tcl_DictObjPut(NULL, infoObj, Tcl_NewStringObj("-fill", -1),
+ Tcl_NewStringObj("none", -1));
+ break;
+ case FILLX:
+ Tcl_DictObjPut(NULL, infoObj, Tcl_NewStringObj("-fill", -1),
+ Tcl_NewStringObj("x", -1));
+ break;
+ case FILLY:
+ Tcl_DictObjPut(NULL, infoObj, Tcl_NewStringObj("-fill", -1),
+ Tcl_NewStringObj("y", -1));
+ break;
+ case FILLX|FILLY:
+ Tcl_DictObjPut(NULL, infoObj, Tcl_NewStringObj("-fill", -1),
+ Tcl_NewStringObj("both", -1));
+ break;
+ }
+ TkAppendPadAmount(infoObj, "-ipadx", slavePtr->iPadX/2, slavePtr->iPadX);
+ TkAppendPadAmount(infoObj, "-ipady", slavePtr->iPadY/2, slavePtr->iPadY);
+ TkAppendPadAmount(infoObj, "-padx", slavePtr->padLeft,slavePtr->padX);
+ TkAppendPadAmount(infoObj, "-pady", slavePtr->padTop, slavePtr->padY);
+ Tcl_DictObjPut(NULL, infoObj, Tcl_NewStringObj("-side", -1),
+ Tcl_NewStringObj(sideNames[slavePtr->side], -1));
+ Tcl_SetObjResult(interp, infoObj);
+ break;
+ }
+ case PACK_PROPAGATE: {
+ Tk_Window master;
+ Packer *masterPtr;
+ int propagate;
+
+ if (objc > 4) {
+ Tcl_WrongNumArgs(interp, 2, objv, "window ?boolean?");
+ return TCL_ERROR;
+ }
+ if (TkGetWindowFromObj(interp, tkwin, objv[2], &master) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ masterPtr = GetPacker(master);
+ if (objc == 3) {
+ Tcl_SetObjResult(interp,
+ Tcl_NewBooleanObj(!(masterPtr->flags & DONT_PROPAGATE)));
+ return TCL_OK;
+ }
+ if (Tcl_GetBooleanFromObj(interp, objv[3], &propagate) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (propagate) {
+ /*
+ * If we have slaves, we need to register as geometry master.
+ */
+
+ if (masterPtr->slavePtr != NULL) {
+ if (TkSetGeometryMaster(interp, master, "pack") != TCL_OK) {
+ return TCL_ERROR;
+ }
+ masterPtr->flags |= ALLOCED_MASTER;
+ }
+ 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, masterPtr);
+ }
+ } else {
+ if (masterPtr->flags & ALLOCED_MASTER) {
+ TkFreeGeometryMaster(master, "pack");
+ masterPtr->flags &= ~ALLOCED_MASTER;
+ }
+ masterPtr->flags |= DONT_PROPAGATE;
+ }
+ break;
+ }
+ case PACK_SLAVES: {
+ Tk_Window master;
+ Packer *masterPtr, *slavePtr;
+ Tcl_Obj *resultObj;
+
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "window");
+ return TCL_ERROR;
+ }
+ if (TkGetWindowFromObj(interp, tkwin, objv[2], &master) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ resultObj = Tcl_NewObj();
+ masterPtr = GetPacker(master);
+ for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
+ slavePtr = slavePtr->nextPtr) {
+ Tcl_ListObjAppendElement(NULL, resultObj,
+ TkNewWindowObj(slavePtr->tkwin));
+ }
+ Tcl_SetObjResult(interp, resultObj);
+ break;
+ }
+ case PACK_UNPACK: {
+ Tk_Window tkwin2;
+ Packer *packPtr;
+
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "window");
+ return TCL_ERROR;
+ }
+ if (TkGetWindowFromObj(interp, tkwin, objv[2], &tkwin2) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ packPtr = GetPacker(tkwin2);
+ if ((packPtr != NULL) && (packPtr->masterPtr != NULL)) {
+ Tk_ManageGeometry(tkwin2, NULL, NULL);
+ if (packPtr->masterPtr->tkwin != Tk_Parent(packPtr->tkwin)) {
+ Tk_UnmaintainGeometry(packPtr->tkwin,
+ packPtr->masterPtr->tkwin);
+ }
+ Unlink(packPtr);
+ Tk_UnmapWindow(packPtr->tkwin);
+ }
+ break;
+ }
+ }
+
+ return TCL_OK;
+}
+
+/*
+ *------------------------------------------------------------------------
+ *
+ * PackReqProc --
+ *
+ * This function 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 clientData, /* Packer's information about window that got
+ * new preferred geometry. */
+ Tk_Window tkwin) /* Other Tk-related information about the
+ * window. */
+{
+ register Packer *packPtr = clientData;
+
+ packPtr = packPtr->masterPtr;
+ if (!(packPtr->flags & REQUESTED_REPACK)) {
+ packPtr->flags |= REQUESTED_REPACK;
+ Tcl_DoWhenIdle(ArrangePacking, packPtr);
+ }
+}
+
+/*
+ *------------------------------------------------------------------------
+ *
+ * PackLostSlaveProc --
+ *
+ * This function 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 clientData, /* Packer structure for slave window that was
+ * stolen away. */
+ Tk_Window tkwin) /* Tk's handle for the slave window. */
+{
+ register Packer *slavePtr = clientData;
+
+ if (slavePtr->masterPtr->tkwin != Tk_Parent(slavePtr->tkwin)) {
+ Tk_UnmaintainGeometry(slavePtr->tkwin, slavePtr->masterPtr->tkwin);
+ }
+ Unlink(slavePtr);
+ Tk_UnmapWindow(slavePtr->tkwin);
+}
+
+/*
+ *------------------------------------------------------------------------
+ *
+ * ArrangePacking --
+ *
+ * This function 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) /* Structure describing master whose slaves
+ * are to be re-layed out. */
+{
+ register Packer *masterPtr = 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 master 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 abort; /* May get set to non-zero to abort this
+ * repacking operation. */
+ int borderX, borderY;
+ int borderTop, borderBtm;
+ int borderLeft, borderRight;
+ int maxWidth, maxHeight, tmp;
+
+ masterPtr->flags &= ~REQUESTED_REPACK;
+
+ /*
+ * If the master has no slaves anymore, then don't do anything at all:
+ * just leave the master'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(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.
+ */
+
+ width = maxWidth = Tk_InternalBorderLeft(masterPtr->tkwin) +
+ Tk_InternalBorderRight(masterPtr->tkwin);
+ height = maxHeight = Tk_InternalBorderTop(masterPtr->tkwin) +
+ Tk_InternalBorderBottom(masterPtr->tkwin);
+ 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 (maxWidth < Tk_MinReqWidth(masterPtr->tkwin)) {
+ maxWidth = Tk_MinReqWidth(masterPtr->tkwin);
+ }
+ if (maxHeight < Tk_MinReqHeight(masterPtr->tkwin)) {
+ maxHeight = Tk_MinReqHeight(masterPtr->tkwin);
+ }
+
+ /*
+ * If the total amount of space needed in the master 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
+ * master 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, 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 = x = Tk_InternalBorderLeft(masterPtr->tkwin);
+ cavityY = y = Tk_InternalBorderTop(masterPtr->tkwin);
+ cavityWidth = Tk_Width(masterPtr->tkwin) -
+ Tk_InternalBorderLeft(masterPtr->tkwin) -
+ Tk_InternalBorderRight(masterPtr->tkwin);
+ cavityHeight = Tk_Height(masterPtr->tkwin) -
+ Tk_InternalBorderTop(masterPtr->tkwin) -
+ Tk_InternalBorderBottom(masterPtr->tkwin);
+ 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;
+ borderTop = borderBtm = 0;
+ borderLeft = borderRight = 0;
+ } else {
+ borderX = slavePtr->padX;
+ borderY = slavePtr->padY;
+ borderLeft = slavePtr->padLeft;
+ borderRight = borderX - borderLeft;
+ borderTop = slavePtr->padTop;
+ borderBtm = borderY - borderTop;
+ }
+ 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;
+ }
+ switch (slavePtr->anchor) {
+ case TK_ANCHOR_N:
+ x = frameX + (borderLeft + frameWidth - width - borderRight)/2;
+ y = frameY + borderTop;
+ break;
+ case TK_ANCHOR_NE:
+ x = frameX + frameWidth - width - borderRight;
+ y = frameY + borderTop;
+ break;
+ case TK_ANCHOR_E:
+ x = frameX + frameWidth - width - borderRight;
+ y = frameY + (borderTop + frameHeight - height - borderBtm)/2;
+ break;
+ case TK_ANCHOR_SE:
+ x = frameX + frameWidth - width - borderRight;
+ y = frameY + frameHeight - height - borderBtm;
+ break;
+ case TK_ANCHOR_S:
+ x = frameX + (borderLeft + frameWidth - width - borderRight)/2;
+ y = frameY + frameHeight - height - borderBtm;
+ break;
+ case TK_ANCHOR_SW:
+ x = frameX + borderLeft;
+ y = frameY + frameHeight - height - borderBtm;
+ break;
+ case TK_ANCHOR_W:
+ x = frameX + borderLeft;
+ y = frameY + (borderTop + frameHeight - height - borderBtm)/2;
+ break;
+ case TK_ANCHOR_NW:
+ x = frameX + borderLeft;
+ y = frameY + borderTop;
+ break;
+ case TK_ANCHOR_CENTER:
+ x = frameX + (borderLeft + frameWidth - width - borderRight)/2;
+ y = frameY + (borderTop + frameHeight - height - borderBtm)/2;
+ break;
+ default:
+ Tcl_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(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(
+ 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 function 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)) {
+ if (numExpand) {
+ curExpand = (cavityWidth - childWidth)/numExpand;
+ if (curExpand < minExpand) {
+ minExpand = curExpand;
+ }
+ }
+ } else {
+ cavityWidth -= childWidth;
+ if (slavePtr->flags & EXPAND) {
+ numExpand++;
+ }
+ }
+ }
+ if (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(
+ 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)) {
+ if (numExpand) {
+ curExpand = (cavityHeight - childHeight)/numExpand;
+ if (curExpand < minExpand) {
+ minExpand = curExpand;
+ }
+ }
+ } else {
+ cavityHeight -= childHeight;
+ if (slavePtr->flags & EXPAND) {
+ numExpand++;
+ }
+ }
+ }
+ if (numExpand) {
+ curExpand = cavityHeight/numExpand;
+ if (curExpand < minExpand) {
+ minExpand = curExpand;
+ }
+ }
+ return (minExpand < 0) ? 0 : minExpand;
+}
+
+/*
+ *------------------------------------------------------------------------
+ *
+ * GetPacker --
+ *
+ * This internal function 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(
+ Tk_Window tkwin) /* Token for window for which packer structure
+ * is desired. */
+{
+ register Packer *packPtr;
+ Tcl_HashEntry *hPtr;
+ int isNew;
+ TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr;
+
+ if (!dispPtr->packInit) {
+ dispPtr->packInit = 1;
+ Tcl_InitHashTable(&dispPtr->packerHashTable, TCL_ONE_WORD_KEYS);
+ }
+
+ /*
+ * See if there's already packer for this window. If not, then create a
+ * new one.
+ */
+
+ hPtr = Tcl_CreateHashEntry(&dispPtr->packerHashTable, (char *) tkwin,
+ &isNew);
+ if (!isNew) {
+ return Tcl_GetHashValue(hPtr);
+ }
+ packPtr = 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->padLeft = packPtr->padTop = 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, packPtr);
+ return packPtr;
+}
+
+/*
+ *------------------------------------------------------------------------
+ *
+ * PackAfter --
+ *
+ * This function does most of the real work of adding one or more windows
+ * into the packing order for its master.
+ *
+ * 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(
+ 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 objc, /* Number of elements in objv. */
+ Tcl_Obj *const objv[]) /* Array of lists, each containing 2 elements:
+ * window name and side against which to
+ * pack. */
+{
+ register Packer *packPtr;
+ Tk_Window tkwin, ancestor, parent;
+ Tcl_Obj **options;
+ int index, 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 ( ; objc > 0; objc -= 2, objv += 2, prevPtr = packPtr) {
+ if (objc < 2) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "wrong # args: window \"%s\" should be followed by options",
+ Tcl_GetString(objv[0])));
+ Tcl_SetErrorCode(interp, "TCL", "WRONGARGS", 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.
+ */
+
+ if (TkGetWindowFromObj(interp, masterPtr->tkwin, objv[0], &tkwin)
+ != TCL_OK) {
+ 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_HIERARCHY) {
+ badWindow:
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "can't pack %s inside %s", Tcl_GetString(objv[0]),
+ Tk_PathName(masterPtr->tkwin)));
+ Tcl_SetErrorCode(interp, "TK", "GEOMETRY", "HIERARCHY", NULL);
+ return TCL_ERROR;
+ }
+ }
+ if (((Tk_FakeWin *) (tkwin))->flags & TK_TOP_HIERARCHY) {
+ goto badWindow;
+ }
+ if (tkwin == masterPtr->tkwin) {
+ goto badWindow;
+ }
+ packPtr = GetPacker(tkwin);
+
+ /*
+ * Process options for this window.
+ */
+
+ if (Tcl_ListObjGetElements(interp, objv[1], &optionCount, &options)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ packPtr->side = TOP;
+ packPtr->anchor = TK_ANCHOR_CENTER;
+ packPtr->padX = packPtr->padY = 0;
+ packPtr->padLeft = packPtr->padTop = 0;
+ packPtr->iPadX = packPtr->iPadY = 0;
+ packPtr->flags &= ~(FILLX|FILLY|EXPAND);
+ packPtr->flags |= OLD_STYLE;
+ for (index = 0 ; index < optionCount; index++) {
+ Tcl_Obj *curOptPtr = options[index];
+ const char *curOpt = Tcl_GetString(curOptPtr);
+ size_t length = curOptPtr->length;
+
+ c = curOpt[0];
+
+ 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_SetObjResult(interp, Tcl_ObjPrintf(
+ "wrong # args: \"%s\" option must be"
+ " followed by screen distance", curOpt));
+ Tcl_SetErrorCode(interp, "TK", "OLDPACK", "BAD_PARAMETER",
+ NULL);
+ return TCL_ERROR;
+ }
+ if (TkParsePadAmount(interp, tkwin, options[index+1],
+ &packPtr->padLeft, &packPtr->padX) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ packPtr->padX /= 2;
+ packPtr->padLeft /= 2;
+ packPtr->iPadX = 0;
+ index++;
+ } else if ((c == 'p') && (strcmp(curOpt, "pady")) == 0) {
+ if (optionCount < (index+2)) {
+ goto missingPad;
+ }
+ if (TkParsePadAmount(interp, tkwin, options[index+1],
+ &packPtr->padTop, &packPtr->padY) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ packPtr->padY /= 2;
+ packPtr->padTop /= 2;
+ packPtr->iPadY = 0;
+ index++;
+ } else if ((c == 'f') && (length > 1)
+ && (strncmp(curOpt, "frame", (size_t) length) == 0)) {
+ if (optionCount < (index+2)) {
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(
+ "wrong # args: \"frame\""
+ " option must be followed by anchor point", -1));
+ Tcl_SetErrorCode(interp, "TK", "OLDPACK", "BAD_PARAMETER",
+ NULL);
+ return TCL_ERROR;
+ }
+ if (Tk_GetAnchorFromObj(interp, options[index+1],
+ &packPtr->anchor) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ index++;
+ } else {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "bad option \"%s\": should be top, bottom, left,"
+ " right, expand, fill, fillx, filly, padx, pady, or"
+ " frame", curOpt));
+ Tcl_SetErrorCode(interp, "TK", "OLDPACK", "BAD_PARAMETER",
+ NULL);
+ return TCL_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 master'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, packPtr);
+
+ if (!(masterPtr->flags & DONT_PROPAGATE)) {
+ if (TkSetGeometryMaster(interp, masterPtr->tkwin, "pack")
+ != TCL_OK) {
+ Tk_ManageGeometry(tkwin, NULL, NULL);
+ Unlink(packPtr);
+ return TCL_ERROR;
+ }
+ masterPtr->flags |= ALLOCED_MASTER;
+ }
+ }
+ }
+
+ /*
+ * Arrange for the master 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, masterPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Unlink --
+ *
+ * Remove a packer from its master's list of slaves.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The master will be scheduled for repacking.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+Unlink(
+ 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) {
+ Tcl_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, masterPtr);
+ }
+ if (masterPtr->abortPtr != NULL) {
+ *masterPtr->abortPtr = 1;
+ }
+
+ packPtr->masterPtr = NULL;
+
+ /*
+ * If we have emptied this master from slaves it means we are no longer
+ * handling it and should mark it as free.
+ */
+
+ if (masterPtr->slavePtr == NULL && masterPtr->flags & ALLOCED_MASTER) {
+ TkFreeGeometryMaster(masterPtr->tkwin, "pack");
+ masterPtr->flags &= ~ALLOCED_MASTER;
+ }
+
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DestroyPacker --
+ *
+ * This function 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(
+ void *memPtr) /* Info about packed window that is now
+ * dead. */
+{
+ register Packer *packPtr = memPtr;
+
+ ckfree(packPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PackStructureProc --
+ *
+ * This function 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 clientData, /* Our information about window referred to by
+ * eventPtr. */
+ XEvent *eventPtr) /* Describes what just happened. */
+{
+ register Packer *packPtr = clientData;
+
+ if (eventPtr->type == ConfigureNotify) {
+ if ((packPtr->slavePtr != NULL)
+ && !(packPtr->flags & REQUESTED_REPACK)) {
+ packPtr->flags |= REQUESTED_REPACK;
+ Tcl_DoWhenIdle(ArrangePacking, packPtr);
+ }
+ if ((packPtr->masterPtr != NULL)
+ && (packPtr->doubleBw != 2*Tk_Changes(packPtr->tkwin)->border_width)) {
+ if (!(packPtr->masterPtr->flags & REQUESTED_REPACK)) {
+ packPtr->doubleBw = 2*Tk_Changes(packPtr->tkwin)->border_width;
+ packPtr->masterPtr->flags |= REQUESTED_REPACK;
+ Tcl_DoWhenIdle(ArrangePacking, 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, NULL, NULL);
+ Tk_UnmapWindow(slavePtr->tkwin);
+ slavePtr->masterPtr = NULL;
+ nextPtr = slavePtr->nextPtr;
+ slavePtr->nextPtr = NULL;
+ }
+
+ if (packPtr->tkwin != NULL) {
+ TkDisplay *dispPtr = ((TkWindow *) packPtr->tkwin)->dispPtr;
+ Tcl_DeleteHashEntry(Tcl_FindHashEntry(&dispPtr->packerHashTable,
+ (char *) packPtr->tkwin));
+ }
+
+ if (packPtr->flags & REQUESTED_REPACK) {
+ Tcl_CancelIdleCall(ArrangePacking, packPtr);
+ }
+ packPtr->tkwin = NULL;
+ Tcl_EventuallyFree(packPtr, (Tcl_FreeProc *) 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, packPtr);
+ }
+ } else if (eventPtr->type == UnmapNotify) {
+ register 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 the interp's result is set to contain an error message.
+ *
+ * Side effects:
+ * Slave windows get taken over by the packer.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+ConfigureSlaves(
+ Tcl_Interp *interp, /* Interpreter for error reporting. */
+ Tk_Window tkwin, /* Any window in application containing
+ * slaves. Used to look up slave names. */
+ int objc, /* Number of elements in argv. */
+ Tcl_Obj *const objv[]) /* Argument objects: 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, tmp, positionGiven;
+ const char *string;
+ static const char *const optionStrings[] = {
+ "-after", "-anchor", "-before", "-expand", "-fill",
+ "-in", "-ipadx", "-ipady", "-padx", "-pady", "-side", NULL };
+ enum options {
+ CONF_AFTER, CONF_ANCHOR, CONF_BEFORE, CONF_EXPAND, CONF_FILL,
+ CONF_IN, CONF_IPADX, CONF_IPADY, CONF_PADX, CONF_PADY, CONF_SIDE };
+ int index, side;
+
+ /*
+ * Find out how many windows are specified.
+ */
+
+ for (numWindows = 0; numWindows < objc; numWindows++) {
+ string = Tcl_GetString(objv[numWindows]);
+ if (string[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++) {
+ if (TkGetWindowFromObj(interp, tkwin, objv[j], &slave) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (Tk_TopWinHierarchy(slave)) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "can't pack \"%s\": it's a top-level window",
+ Tcl_GetString(objv[j])));
+ Tcl_SetErrorCode(interp, "TK", "GEOMETRY", "TOPLEVEL", 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->padLeft = slavePtr->padTop = 0;
+ slavePtr->iPadX = slavePtr->iPadY = 0;
+ slavePtr->flags &= ~(FILLX|FILLY|EXPAND);
+ }
+
+ for (i = numWindows; i < objc; i+=2) {
+ if ((i+2) > objc) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "extra option \"%s\" (option with no value?)",
+ Tcl_GetString(objv[i])));
+ Tcl_SetErrorCode(interp, "TK", "PACK", "BAD_PARAMETER", NULL);
+ return TCL_ERROR;
+ }
+ if (Tcl_GetIndexFromObjStruct(interp, objv[i], optionStrings,
+ sizeof(char *), "option", 0, &index) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ switch ((enum options) index) {
+ case CONF_AFTER:
+ if (j == 0) {
+ if (TkGetWindowFromObj(interp, tkwin, objv[i+1], &other)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ prevPtr = GetPacker(other);
+ if (prevPtr->masterPtr == NULL) {
+ notPacked:
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "window \"%s\" isn't packed",
+ Tcl_GetString(objv[i+1])));
+ Tcl_SetErrorCode(interp, "TK", "PACK", "NOT_PACKED",
+ NULL);
+ return TCL_ERROR;
+ }
+ masterPtr = prevPtr->masterPtr;
+ positionGiven = 1;
+ }
+ break;
+ case CONF_ANCHOR:
+ if (Tk_GetAnchorFromObj(interp, objv[i+1], &slavePtr->anchor)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ break;
+ case CONF_BEFORE:
+ if (j == 0) {
+ if (TkGetWindowFromObj(interp, tkwin, objv[i+1], &other)
+ != TCL_OK) {
+ 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;
+ }
+ break;
+ case CONF_EXPAND:
+ if (Tcl_GetBooleanFromObj(interp, objv[i+1], &tmp) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ slavePtr->flags &= ~EXPAND;
+ if (tmp) {
+ slavePtr->flags |= EXPAND;
+ }
+ break;
+ case CONF_FILL:
+ string = Tcl_GetString(objv[i+1]);
+ if (strcmp(string, "none") == 0) {
+ slavePtr->flags &= ~(FILLX|FILLY);
+ } else if (strcmp(string, "x") == 0) {
+ slavePtr->flags = (slavePtr->flags & ~FILLY) | FILLX;
+ } else if (strcmp(string, "y") == 0) {
+ slavePtr->flags = (slavePtr->flags & ~FILLX) | FILLY;
+ } else if (strcmp(string, "both") == 0) {
+ slavePtr->flags |= FILLX|FILLY;
+ } else {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "bad fill style \"%s\": must be "
+ "none, x, y, or both", string));
+ Tcl_SetErrorCode(interp, "TK", "VALUE", "FILL", NULL);
+ return TCL_ERROR;
+ }
+ break;
+ case CONF_IN:
+ if (j == 0) {
+ if (TkGetWindowFromObj(interp, tkwin, objv[i+1], &other)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ masterPtr = GetPacker(other);
+ prevPtr = masterPtr->slavePtr;
+ if (prevPtr != NULL) {
+ while (prevPtr->nextPtr != NULL) {
+ prevPtr = prevPtr->nextPtr;
+ }
+ }
+ positionGiven = 1;
+ }
+ break;
+ case CONF_IPADX:
+ if ((Tk_GetPixelsFromObj(interp, slave, objv[i+1], &tmp)
+ != TCL_OK) || (tmp < 0)) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "bad ipadx value \"%s\": must be positive screen"
+ " distance", Tcl_GetString(objv[i+1])));
+ Tcl_SetErrorCode(interp, "TK", "VALUE", "INT_PAD", NULL);
+ return TCL_ERROR;
+ }
+ slavePtr->iPadX = tmp * 2;
+ break;
+ case CONF_IPADY:
+ if ((Tk_GetPixelsFromObj(interp, slave, objv[i+1], &tmp)
+ != TCL_OK) || (tmp < 0)) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "bad ipady value \"%s\": must be positive screen"
+ " distance", Tcl_GetString(objv[i+1])));
+ Tcl_SetErrorCode(interp, "TK", "VALUE", "INT_PAD", NULL);
+ return TCL_ERROR;
+ }
+ slavePtr->iPadY = tmp * 2;
+ break;
+ case CONF_PADX:
+ if (TkParsePadAmount(interp, slave, objv[i+1],
+ &slavePtr->padLeft, &slavePtr->padX) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ break;
+ case CONF_PADY:
+ if (TkParsePadAmount(interp, slave, objv[i+1],
+ &slavePtr->padTop, &slavePtr->padY) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ break;
+ case CONF_SIDE:
+ if (Tcl_GetIndexFromObjStruct(interp, objv[i+1], sideNames,
+ sizeof(char *), "side", TCL_EXACT, &side) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ slavePtr->side = (Side) side;
+ break;
+ }
+ }
+
+ /*
+ * 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 or the same -in
+ * window is passed in again, then just 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_TopWinHierarchy(ancestor)) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "can't pack %s inside %s", Tcl_GetString(objv[j]),
+ Tk_PathName(masterPtr->tkwin)));
+ Tcl_SetErrorCode(interp, "TK", "GEOMETRY", "HIERARCHY", NULL);
+ return TCL_ERROR;
+ }
+ }
+ if (slave == masterPtr->tkwin) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "can't pack %s inside itself", Tcl_GetString(objv[j])));
+ Tcl_SetErrorCode(interp, "TK", "GEOMETRY", "SELF", 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, slavePtr);
+ prevPtr = slavePtr;
+
+ if (!(masterPtr->flags & DONT_PROPAGATE)) {
+ if (TkSetGeometryMaster(interp, masterPtr->tkwin, "pack")
+ != TCL_OK) {
+ Tk_ManageGeometry(slave, NULL, NULL);
+ Unlink(slavePtr);
+ return TCL_ERROR;
+ }
+ masterPtr->flags |= ALLOCED_MASTER;
+ }
+
+ /*
+ * Arrange for the master 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, masterPtr);
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ * Local Variables:
+ * mode: c
+ * c-basic-offset: 4
+ * fill-column: 78
+ * End:
+ */