/* * tkPack.c -- * * This file contains code to implement the "packer" * geometry manager for Tk. * * Copyright 1990 Regents of the University of California. * Permission to use, copy, modify, and distribute this * software and its documentation for any purpose and without * fee is hereby granted, provided that the above copyright * notice appear in all copies. The University of California * makes no representations about the suitability of this * software for any purpose. It is provided "as is" without * express or implied warranty. */ #ifndef lint static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkPack.c,v 1.27 92/01/04 15:16:41 ouster Exp $ SPRITE (Berkeley)"; #endif #include "tkConfig.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 children 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 *parentPtr; /* Parent 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 *childPtr; /* First in list of children packed * inside this window (NULL means * no packed children). */ Side side; /* Side of parent against which * this window is packed. */ Tk_Anchor anchorPoint; /* If frame allocated for window is larger * than window needs, this indicates how * where to position window in frame. */ int padX, padY; /* Additional amounts of space to give window * besides what it asked for. */ 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 children * is deleted. */ int flags; /* Miscellaneous flags; see below * for definitions. */ } Packer; /* * Flag values for Packer structures: * * REQUESTED_REPACK: 1 means a Tk_DoWhenIdle request * has already been made to repack * all the children 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. */ #define REQUESTED_REPACK 1 #define FILLX 2 #define FILLY 4 #define EXPAND 8 /* * 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 initialized = 0; /* * Forward declarations for procedures defined later in this file: */ static void ArrangePacking _ANSI_ARGS_((ClientData clientData)); static Packer * GetPacker _ANSI_ARGS_((Tk_Window tkwin)); static int PackAfter _ANSI_ARGS_((Tcl_Interp *interp, Packer *prevPtr, Packer *parentPtr, 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)); /* *-------------------------------------------------------------- * * 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; int length; char c; 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->parentPtr == NULL) { Tcl_AppendResult(interp, "window \"", argv[2], "\" isn't packed", (char *) NULL); return TCL_ERROR; } return PackAfter(interp, prevPtr, prevPtr->parentPtr, argc-3, argv+3); } else if ((c == 'a') && (length >= 2) && (strncmp(argv[1], "append", length) == 0)) { Packer *parentPtr; register Packer *prevPtr; Tk_Window tkwin2; tkwin2 = Tk_NameToWindow(interp, argv[2], tkwin); if (tkwin2 == NULL) { return TCL_ERROR; } parentPtr = GetPacker(tkwin2); prevPtr = parentPtr->childPtr; if (prevPtr != NULL) { while (prevPtr->nextPtr != NULL) { prevPtr = prevPtr->nextPtr; } } return PackAfter(interp, prevPtr, parentPtr, argc-3, argv+3); } else if ((c == 'b') && (strncmp(argv[1], "before", length) == 0)) { Packer *packPtr, *parentPtr; register Packer *prevPtr; Tk_Window tkwin2; tkwin2 = Tk_NameToWindow(interp, argv[2], tkwin); if (tkwin2 == NULL) { return TCL_ERROR; } packPtr = GetPacker(tkwin2); if (packPtr->parentPtr == NULL) { Tcl_AppendResult(interp, "window \"", argv[2], "\" isn't packed", (char *) NULL); return TCL_ERROR; } parentPtr = packPtr->parentPtr; prevPtr = parentPtr->childPtr; 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, parentPtr, argc-3, argv+3); } else if ((c == 'i') && (strncmp(argv[1], "info", length) == 0)) { char *prefix; register Packer *packPtr; Tk_Window tkwin2; char tmp[20]; 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; } tkwin2 = Tk_NameToWindow(interp, argv[2], tkwin); if (tkwin2 == NULL) { return TCL_ERROR; } packPtr = GetPacker(tkwin2); prefix = ""; for (packPtr = packPtr->childPtr; packPtr != NULL; packPtr = packPtr->nextPtr) { Tcl_AppendResult(interp, prefix, Tk_PathName(packPtr->tkwin), " {", sideNames[(int) packPtr->side], " frame ", Tk_NameOfAnchor(packPtr->anchorPoint), (char *) NULL); if (packPtr->padX != 0) { sprintf(tmp, "%d", packPtr->padX); Tcl_AppendResult(interp, " padx ", tmp, (char *) NULL); } if (packPtr->padY != 0) { sprintf(tmp, "%d", packPtr->padY); Tcl_AppendResult(interp, " pady ", tmp, (char *) NULL); } if (packPtr->flags & EXPAND) { Tcl_AppendResult(interp, " expand", (char *) NULL); } if ((packPtr->flags & (FILLX|FILLY)) == (FILLX|FILLY)) { Tcl_AppendResult(interp, " fill", (char *) NULL); } else if (packPtr->flags & FILLX) { Tcl_AppendResult(interp, " fillx", (char *) NULL); } else if (packPtr->flags & FILLY) { Tcl_AppendResult(interp, " filly", (char *) NULL); } Tcl_AppendResult(interp, "}", (char *) NULL); prefix = " "; } return TCL_OK; } 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->parentPtr != NULL)) { Tk_ManageGeometry(tkwin2, (Tk_GeometryProc *) NULL, (ClientData) NULL); Unlink(packPtr); Tk_UnmapWindow(packPtr->tkwin); } } else { Tcl_AppendResult(interp, "bad option \"", argv[1], "\": must be after, append, before, or info", (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->parentPtr; if (!(packPtr->flags & REQUESTED_REPACK)) { packPtr->flags |= REQUESTED_REPACK; Tk_DoWhenIdle(ArrangePacking, (ClientData) packPtr); } } /* *-------------------------------------------------------------- * * ArrangePacking -- * * This procedure is invoked (using the Tk_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 children of parentPtr may get resized or * moved. * *-------------------------------------------------------------- */ static void ArrangePacking(clientData) ClientData clientData; /* Structure describing parent * whose children are to be * re-layed out. */ { register Packer *parentPtr = (Packer *) clientData; register Packer *childPtr; int numExpX, numExpY; /* # of windows that are expandable in * each direction. */ int spareX, spareY; /* Amount of extra space to give to each * expandable window. */ int leftOverX, leftOverY; /* Extra chunk of space to give to last * expandable window. */ 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 maxWidth, maxHeight, tmp; parentPtr->flags &= ~REQUESTED_REPACK; /* * If the parent has no children anymore, then don't do anything * at all: just leave the parent's size as-is. */ if (parentPtr->childPtr == 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 (parentPtr->abortPtr != NULL) { *parentPtr->abortPtr = 1; } parentPtr->abortPtr = &abort; abort = 0; Tk_Preserve((ClientData) parentPtr); /* * Pass #1: scan all the children to figure out the total amount * of space needed. Two separate widths and heights are computed. * * "Width" and "height" compute the minimum parent size to meet * the needs of each window in the direction "where there is * flexibility". For example, if a child is packed TOP, then * y is the flexible direction: the child's requested height * will determine its size. For this window x is the inflexible * direction: the window's width will be determined by the amount * of space left in the parent's cavity, not by the window's * requested width. "Width" and "height" are needed in order to * compute how much extra space there is, so that it can be divided * among the windows that have the EXPAND flag. * * "MaxWidth" and "maxHeight" compute the minimum parent size to * meet all the needs of every window in both directions, flexible * or inflexible. These values are needed to make geometry requests * of the parent's parent. */ intBWidth = Tk_InternalBorderWidth(parentPtr->tkwin); width = height = maxWidth = maxHeight = 2*intBWidth; numExpX = numExpY = 0; for (childPtr = parentPtr->childPtr; childPtr != NULL; childPtr = childPtr->nextPtr) { if ((childPtr->side == TOP) || (childPtr->side == BOTTOM)) { tmp = Tk_ReqWidth(childPtr->tkwin) + childPtr->doubleBw + childPtr->padX + width; if (tmp > maxWidth) { maxWidth = tmp; } height += Tk_ReqHeight(childPtr->tkwin) + childPtr->doubleBw + childPtr->padY; if (childPtr->flags & EXPAND) { numExpY++; } } else { tmp = Tk_ReqHeight(childPtr->tkwin) + childPtr->doubleBw + childPtr->padY + height; if (tmp > maxHeight) { maxHeight = tmp; } width += Tk_ReqWidth(childPtr->tkwin) + childPtr->doubleBw + childPtr->padX; if (childPtr->flags & EXPAND) { numExpX++; } } } if (width > maxWidth) { maxWidth = width; } if (height > maxHeight) { maxHeight = height; } /* * If the total amount of space needed in the parent window has * changed, 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(parentPtr->tkwin)) || (maxHeight != Tk_ReqHeight(parentPtr->tkwin))) { Tk_GeometryRequest(parentPtr->tkwin, maxWidth, maxHeight); parentPtr->flags |= REQUESTED_REPACK; Tk_DoWhenIdle(ArrangePacking, (ClientData) parentPtr); goto done; } /* * If there is spare space, figure out how much of it goes to * each of the windows that is expandable. */ spareX = Tk_Width(parentPtr->tkwin) - width; spareY = Tk_Height(parentPtr->tkwin) - height; if ((spareX <= 0) || (numExpX == 0)) { leftOverX = 0; spareX = 0; } else { leftOverX = spareX % numExpX; spareX /= numExpX; } if ((spareY <= 0) || (numExpY == 0)) { leftOverY = spareY; spareY = 0; } else { leftOverY = spareY % numExpY; spareY /= numExpY; } /* * Pass #2: scan the children 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 anchorPoint. */ cavityX = cavityY = x = y = intBWidth; cavityWidth = Tk_Width(parentPtr->tkwin) - 2*intBWidth; cavityHeight = Tk_Height(parentPtr->tkwin) - 2*intBWidth; for (childPtr = parentPtr->childPtr; childPtr != NULL; childPtr = childPtr->nextPtr) { if ((childPtr->side == TOP) || (childPtr->side == BOTTOM)) { frameWidth = cavityWidth; frameHeight = Tk_ReqHeight(childPtr->tkwin) + childPtr->padY + childPtr->doubleBw; if (childPtr->flags & EXPAND) { frameHeight += spareY; numExpY--; if (numExpY == 0) { frameHeight += leftOverY; } } cavityHeight -= frameHeight; if (cavityHeight < 0) { frameHeight += cavityHeight; cavityHeight = 0; } frameX = cavityX; if (childPtr->side == TOP) { frameY = cavityY; cavityY += frameHeight; } else { frameY = cavityY + cavityHeight; } } else { frameHeight = cavityHeight; frameWidth = Tk_ReqWidth(childPtr->tkwin) + childPtr->padX + childPtr->doubleBw; if (childPtr->flags & EXPAND) { frameWidth += spareX; numExpX--; if (numExpX == 0) { frameWidth += leftOverX; } } cavityWidth -= frameWidth; if (cavityWidth < 0) { frameWidth += cavityWidth; cavityWidth = 0; } frameY = cavityY; if (childPtr->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 and frame factors. */ width = Tk_ReqWidth(childPtr->tkwin) + childPtr->doubleBw; if ((childPtr->flags & FILLX) || (width > frameWidth)) { width = frameWidth; } height = Tk_ReqHeight(childPtr->tkwin) + childPtr->doubleBw; if ((childPtr->flags & FILLY) || (height > frameHeight)) { height = frameHeight; } switch (childPtr->anchorPoint) { case TK_ANCHOR_N: x = frameX + (frameWidth - width)/2; y = frameY; break; case TK_ANCHOR_NE: x = frameX + frameWidth - width; y = frameY; break; case TK_ANCHOR_E: x = frameX + frameWidth - width; y = frameY + (frameHeight - height)/2; break; case TK_ANCHOR_SE: x = frameX + frameWidth - width; y = frameY + frameHeight - height; break; case TK_ANCHOR_S: x = frameX + (frameWidth - width)/2; y = frameY + frameHeight - height; break; case TK_ANCHOR_SW: x = frameX; y = frameY + frameHeight - height; break; case TK_ANCHOR_W: x = frameX; y = frameY + (frameHeight - height)/2; break; case TK_ANCHOR_NW: x = frameX; y = frameY; break; case TK_ANCHOR_CENTER: x = frameX + (frameWidth - width)/2; y = frameY + (frameHeight - height)/2; break; default: panic("bad frame factor in ArrangePacking"); } width -= childPtr->doubleBw; height -= childPtr->doubleBw; /* * If the window is too small to be interesting then * unmap it. Otherwise configure it and then make sure * it's mapped. */ if ((width <= 0) || (height <= 0)) { Tk_UnmapWindow(childPtr->tkwin); } else { if ((x != Tk_X(childPtr->tkwin)) || (y != Tk_Y(childPtr->tkwin)) || (width != Tk_Width(childPtr->tkwin)) || (height != Tk_Height(childPtr->tkwin))) { Tk_MoveResizeWindow(childPtr->tkwin, x, y, (unsigned int) width, (unsigned int) height); } if (abort) { goto done; } Tk_MapWindow(childPtr->tkwin); } /* * 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: parentPtr->abortPtr = NULL; Tk_Release((ClientData) parentPtr); } /* *-------------------------------------------------------------- * * 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->parentPtr = NULL; packPtr->nextPtr = NULL; packPtr->childPtr = NULL; packPtr->side = TOP; packPtr->anchorPoint = TK_ANCHOR_CENTER; packPtr->padX = packPtr->padY = 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, parentPtr, 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 parentPtr. */ Packer *parentPtr; /* Parent 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; int length, optionCount; char **options; int index; char 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 its parent. */ tkwin = Tk_NameToWindow(interp, argv[0], parentPtr->tkwin); if (tkwin == NULL) { return TCL_ERROR; } if (Tk_Parent(tkwin) != parentPtr->tkwin) { Tcl_AppendResult(interp, "tried to pack \"", argv[0], "\" in window that isn't its parent", (char *) NULL); return TCL_ERROR; } 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->anchorPoint = TK_ANCHOR_CENTER; packPtr->padX = packPtr->padY = 0; packPtr->flags &= ~(FILLX|FILLY|EXPAND); 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 count", (char *) NULL); goto error; } if ((Tcl_GetInt(interp, options[index+1], &packPtr->padX) != TCL_OK) || (packPtr->padX < 0)) { badPad: Tcl_AppendResult(interp, "bad pad value \"", options[index+1], "\": must be positive integer", (char *) NULL); goto error; } index++; } else if ((c == 'p') && (strcmp(curOpt, "pady")) == 0) { if (optionCount < (index+2)) { goto missingPad; } if ((Tcl_GetInt(interp, options[index+1], &packPtr->padY) != TCL_OK) || (packPtr->padY < 0)) { goto badPad; } 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->anchorPoint) != 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->parentPtr != NULL) { 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->parentPtr = parentPtr; if (prevPtr == NULL) { packPtr->nextPtr = parentPtr->childPtr; parentPtr->childPtr = packPtr; } else { packPtr->nextPtr = prevPtr->nextPtr; prevPtr->nextPtr = packPtr; } Tk_ManageGeometry(tkwin, PackReqProc, (ClientData) packPtr); } ckfree((char *) options); } /* * Arrange for the parent to be re-packed at the first * idle moment. */ if (parentPtr->abortPtr != NULL) { *parentPtr->abortPtr = 1; } if (!(parentPtr->flags & REQUESTED_REPACK)) { parentPtr->flags |= REQUESTED_REPACK; Tk_DoWhenIdle(ArrangePacking, (ClientData) parentPtr); } return TCL_OK; error: ckfree((char *) options); return TCL_ERROR; } /* *---------------------------------------------------------------------- * * Unlink -- * * Remove a packer from its parent's list of children. * * Results: * None. * * Side effects: * The parent will be scheduled for repacking. * *---------------------------------------------------------------------- */ static void Unlink(packPtr) register Packer *packPtr; /* Window to unlink. */ { register Packer *parentPtr, *packPtr2; parentPtr = packPtr->parentPtr; if (parentPtr == NULL) { return; } if (parentPtr->childPtr == packPtr) { parentPtr->childPtr = packPtr->nextPtr; } else { for (packPtr2 = parentPtr->childPtr; ; packPtr2 = packPtr2->nextPtr) { if (packPtr2 == NULL) { panic("Unlink couldn't find previous window"); } if (packPtr2->nextPtr == packPtr) { packPtr2->nextPtr = packPtr->nextPtr; break; } } } if (!(parentPtr->flags & REQUESTED_REPACK)) { parentPtr->flags |= REQUESTED_REPACK; Tk_DoWhenIdle(ArrangePacking, (ClientData) parentPtr); } if (parentPtr->abortPtr != NULL) { *parentPtr->abortPtr = 1; } packPtr->parentPtr = NULL; } /* *---------------------------------------------------------------------- * * DestroyPacker -- * * This procedure is invoked by Tk_EventuallyFree or Tk_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(clientData) ClientData clientData; /* Info about packed window that * is now dead. */ { register Packer *packPtr = (Packer *) clientData; 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 children, 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->childPtr != NULL) && !(packPtr->flags & REQUESTED_REPACK)) { packPtr->flags |= REQUESTED_REPACK; Tk_DoWhenIdle(ArrangePacking, (ClientData) packPtr); } if (packPtr->doubleBw != 2*Tk_Changes(packPtr->tkwin)->border_width) { if ((packPtr->parentPtr != NULL) && !(packPtr->parentPtr->flags & REQUESTED_REPACK)) { packPtr->doubleBw = 2*Tk_Changes(packPtr->tkwin)->border_width; packPtr->parentPtr->flags |= REQUESTED_REPACK; Tk_DoWhenIdle(ArrangePacking, (ClientData) packPtr->parentPtr); } } } else if (eventPtr->type == DestroyNotify) { register Packer *packPtr2; if (packPtr->parentPtr != NULL) { Unlink(packPtr); } for (packPtr2 = packPtr->childPtr; packPtr2 != NULL; packPtr2 = packPtr2->nextPtr) { packPtr2->parentPtr = NULL; packPtr2->nextPtr = NULL; } Tcl_DeleteHashEntry(Tcl_FindHashEntry(&packerHashTable, (char *) packPtr->tkwin)); if (packPtr->flags & REQUESTED_REPACK) { Tk_CancelIdleCall(ArrangePacking, (ClientData) packPtr); } packPtr->tkwin = NULL; Tk_EventuallyFree((ClientData) packPtr, DestroyPacker); } }