|
|
Micropolis Activity
/*
* 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: /var/lib/cvsd/repos/micropolis/micropolis/src/tk/tkpack.c,v 1.1.1.1 2020/03/11 09:08:51 root 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);
}
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.