summaryrefslogtreecommitdiffstats
path: root/tk8.6/generic/tkGrid.c
diff options
context:
space:
mode:
Diffstat (limited to 'tk8.6/generic/tkGrid.c')
-rw-r--r--tk8.6/generic/tkGrid.c3666
1 files changed, 3666 insertions, 0 deletions
diff --git a/tk8.6/generic/tkGrid.c b/tk8.6/generic/tkGrid.c
new file mode 100644
index 0000000..2a88b76
--- /dev/null
+++ b/tk8.6/generic/tkGrid.c
@@ -0,0 +1,3666 @@
+/*
+ * tkGrid.c --
+ *
+ * Grid based geometry manager.
+ *
+ * Copyright (c) 1996-1997 by 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"
+
+/*
+ * Convenience Macros
+ */
+
+#ifdef MAX
+# undef MAX
+#endif
+#define MAX(x,y) ((x) > (y) ? (x) : (y))
+
+#define COLUMN (1) /* Working on column offsets. */
+#define ROW (2) /* Working on row offsets. */
+
+#define CHECK_ONLY (1) /* Check max slot constraint. */
+#define CHECK_SPACE (2) /* Alloc more space, don't change max. */
+
+/*
+ * Pre-allocate enough row and column slots for "typical" sized tables this
+ * value should be chosen so by the time the extra malloc's are required, the
+ * layout calculations overwehlm them. [A "slot" contains information for
+ * either a row or column, depending upon the context.]
+ */
+
+#define TYPICAL_SIZE 25 /* (Arbitrary guess) */
+#define PREALLOC 10 /* Extra slots to allocate. */
+
+/*
+ * Pre-allocate room for uniform groups during layout.
+ */
+
+#define UNIFORM_PREALLOC 10
+
+/*
+ * Data structures are allocated dynamically to support arbitrary sized
+ * tables. However, the space is proportional to the highest numbered slot
+ * with some non-default property. This limit is used to head off mistakes and
+ * denial of service attacks by limiting the amount of storage required.
+ */
+
+#define MAX_ELEMENT 10000
+
+/*
+ * Special characters to support relative layouts.
+ */
+
+#define REL_SKIP 'x' /* Skip this column. */
+#define REL_HORIZ '-' /* Extend previous widget horizontally. */
+#define REL_VERT '^' /* Extend widget from row above. */
+
+/*
+ * Default value for 'grid anchor'.
+ */
+
+#define GRID_DEFAULT_ANCHOR TK_ANCHOR_NW
+
+/*
+ * Structure to hold information for grid masters. A slot is either a row or
+ * column.
+ */
+
+typedef struct SlotInfo {
+ int minSize; /* The minimum size of this slot (in pixels).
+ * It is set via the rowconfigure or
+ * columnconfigure commands. */
+ int weight; /* The resize weight of this slot. (0) means
+ * this slot doesn't resize. Extra space in
+ * the layout is given distributed among slots
+ * inproportion to their weights. */
+ int pad; /* Extra padding, in pixels, required for this
+ * slot. This amount is "added" to the largest
+ * slave in the slot. */
+ Tk_Uid uniform; /* Value of -uniform option. It is used to
+ * group slots that should have the same
+ * size. */
+ int offset; /* This is a cached value used for
+ * introspection. It is the pixel offset of
+ * the right or bottom edge of this slot from
+ * the beginning of the layout. */
+ int temp; /* This is a temporary value used for
+ * calculating adjusted weights when shrinking
+ * the layout below its nominal size. */
+} SlotInfo;
+
+/*
+ * Structure to hold information during layout calculations. There is one of
+ * these for each slot, an array for each of the rows or columns.
+ */
+
+typedef struct GridLayout {
+ struct Gridder *binNextPtr; /* The next slave window in this bin. Each bin
+ * contains a list of all slaves whose spans
+ * are >1 and whose right edges fall in this
+ * slot. */
+ int minSize; /* Minimum size needed for this slot, in
+ * pixels. This is the space required to hold
+ * any slaves contained entirely in this slot,
+ * adjusted for any slot constrants, such as
+ * size or padding. */
+ int pad; /* Padding needed for this slot */
+ int weight; /* Slot weight, controls resizing. */
+ Tk_Uid uniform; /* Value of -uniform option. It is used to
+ * group slots that should have the same
+ * size. */
+ int minOffset; /* The minimum offset, in pixels, from the
+ * beginning of the layout to the bottom/right
+ * edge of the slot calculated from top/left
+ * to bottom/right. */
+ int maxOffset; /* The maximum offset, in pixels, from the
+ * beginning of the layout to the bottom/right
+ * edge of the slot calculated from
+ * bottom/right to top/left. */
+} GridLayout;
+
+/*
+ * Keep one of these for each geometry master.
+ */
+
+typedef struct {
+ SlotInfo *columnPtr; /* Pointer to array of column constraints. */
+ SlotInfo *rowPtr; /* Pointer to array of row constraints. */
+ int columnEnd; /* The last column occupied by any slave. */
+ int columnMax; /* The number of columns with constraints. */
+ int columnSpace; /* The number of slots currently allocated for
+ * column constraints. */
+ int rowEnd; /* The last row occupied by any slave. */
+ int rowMax; /* The number of rows with constraints. */
+ int rowSpace; /* The number of slots currently allocated for
+ * row constraints. */
+ int startX; /* Pixel offset of this layout within its
+ * master. */
+ int startY; /* Pixel offset of this layout within its
+ * master. */
+ Tk_Anchor anchor; /* Value of anchor option: specifies where a
+ * grid without weight should be placed. */
+} GridMaster;
+
+/*
+ * For each window that the grid cares about (either because the window is
+ * managed by the grid or because the window has slaves that are managed by
+ * the grid), there is a structure of the following type:
+ */
+
+typedef struct Gridder {
+ Tk_Window tkwin; /* Tk token for window. NULL means that the
+ * window has been deleted, but the gridder
+ * hasn't had a chance to clean up yet because
+ * the structure is still in use. */
+ struct Gridder *masterPtr; /* Master window within which this window is
+ * managed (NULL means this window isn't
+ * managed by the gridder). */
+ struct Gridder *nextPtr; /* Next window managed within same master.
+ * List order doesn't matter. */
+ struct Gridder *slavePtr; /* First in list of slaves managed inside this
+ * window (NULL means no grid slaves). */
+ GridMaster *masterDataPtr; /* Additional data for geometry master. */
+ Tcl_Obj *in; /* Store master name when removed. */
+ int column, row; /* Location in the grid (starting from
+ * zero). */
+ int numCols, numRows; /* Number of columns or rows this slave spans.
+ * Should be at least 1. */
+ 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 this amount will appear on
+ * each side). */
+ int sticky; /* which sides of its cavity this window
+ * sticks to. See below for definitions */
+ int doubleBw; /* Twice the window's last known border width.
+ * If this changes, the window must be
+ * re-arranged within its master. */
+ int *abortPtr; /* If non-NULL, it means that there is a
+ * nested call to ArrangeGrid 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. */
+
+ /*
+ * These fields are used temporarily for layout calculations only.
+ */
+
+ struct Gridder *binNextPtr; /* Link to next span>1 slave in this bin. */
+ int size; /* Nominal size (width or height) in pixels of
+ * the slave. This includes the padding. */
+} Gridder;
+
+/*
+ * Flag values for "sticky"ness. The 16 combinations subsume the packer's
+ * notion of anchor and fill.
+ *
+ * STICK_NORTH This window sticks to the top of its cavity.
+ * STICK_EAST This window sticks to the right edge of its
+ * cavity.
+ * STICK_SOUTH This window sticks to the bottom of its cavity.
+ * STICK_WEST This window sticks to the left edge of its
+ * cavity.
+ */
+
+#define STICK_NORTH 1
+#define STICK_EAST 2
+#define STICK_SOUTH 4
+#define STICK_WEST 8
+
+
+/*
+ * Structure to gather information about uniform groups during layout.
+ */
+
+typedef struct UniformGroup {
+ Tk_Uid group;
+ int minSize;
+} UniformGroup;
+
+/*
+ * Flag values for Grid structures:
+ *
+ * REQUESTED_RELAYOUT 1 means a Tcl_DoWhenIdle request has already
+ * been made to re-arrange all the slaves of this
+ * window.
+ * 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 Grid has allocated itself as
+ * geometry master for this window.
+ */
+
+#define REQUESTED_RELAYOUT 1
+#define DONT_PROPAGATE 2
+#define ALLOCED_MASTER 4
+
+/*
+ * Prototypes for procedures used only in this file:
+ */
+
+static void AdjustForSticky(Gridder *slavePtr, int *xPtr,
+ int *yPtr, int *widthPtr, int *heightPtr);
+static int AdjustOffsets(int width, int elements,
+ SlotInfo *slotPtr);
+static void ArrangeGrid(ClientData clientData);
+static int CheckSlotData(Gridder *masterPtr, int slot,
+ int slotType, int checkOnly);
+static int ConfigureSlaves(Tcl_Interp *interp, Tk_Window tkwin,
+ int objc, Tcl_Obj *const objv[]);
+static void DestroyGrid(void *memPtr);
+static Gridder * GetGrid(Tk_Window tkwin);
+static int GridAnchorCommand(Tk_Window tkwin, Tcl_Interp *interp,
+ int objc, Tcl_Obj *const objv[]);
+static int GridBboxCommand(Tk_Window tkwin, Tcl_Interp *interp,
+ int objc, Tcl_Obj *const objv[]);
+static int GridForgetRemoveCommand(Tk_Window tkwin,
+ Tcl_Interp *interp, int objc,
+ Tcl_Obj *const objv[]);
+static int GridInfoCommand(Tk_Window tkwin, Tcl_Interp *interp,
+ int objc, Tcl_Obj *const objv[]);
+static int GridLocationCommand(Tk_Window tkwin,
+ Tcl_Interp *interp, int objc,
+ Tcl_Obj *const objv[]);
+static int GridPropagateCommand(Tk_Window tkwin,
+ Tcl_Interp *interp, int objc,
+ Tcl_Obj *const objv[]);
+static int GridRowColumnConfigureCommand(Tk_Window tkwin,
+ Tcl_Interp *interp, int objc,
+ Tcl_Obj *const objv[]);
+static int GridSizeCommand(Tk_Window tkwin, Tcl_Interp *interp,
+ int objc, Tcl_Obj *const objv[]);
+static int GridSlavesCommand(Tk_Window tkwin, Tcl_Interp *interp,
+ int objc, Tcl_Obj *const objv[]);
+static void GridStructureProc(ClientData clientData,
+ XEvent *eventPtr);
+static void GridLostSlaveProc(ClientData clientData,
+ Tk_Window tkwin);
+static void GridReqProc(ClientData clientData, Tk_Window tkwin);
+static void InitMasterData(Gridder *masterPtr);
+static Tcl_Obj * NewPairObj(int, int);
+static Tcl_Obj * NewQuadObj(int, int, int, int);
+static int ResolveConstraints(Gridder *gridPtr, int rowOrColumn,
+ int maxOffset);
+static void SetGridSize(Gridder *gridPtr);
+static int SetSlaveColumn(Tcl_Interp *interp, Gridder *slavePtr,
+ int column, int numCols);
+static int SetSlaveRow(Tcl_Interp *interp, Gridder *slavePtr,
+ int row, int numRows);
+static Tcl_Obj * StickyToObj(int flags);
+static int StringToSticky(const char *string);
+static void Unlink(Gridder *gridPtr);
+
+static const Tk_GeomMgr gridMgrType = {
+ "grid", /* name */
+ GridReqProc, /* requestProc */
+ GridLostSlaveProc, /* lostSlaveProc */
+};
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_GridCmd --
+ *
+ * This procedure is invoked to process the "grid" 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_GridObjCmd(
+ 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;
+ static const char *const optionStrings[] = {
+ "anchor", "bbox", "columnconfigure", "configure",
+ "forget", "info", "location", "propagate", "remove",
+ "rowconfigure", "size", "slaves", NULL
+ };
+ enum options {
+ GRID_ANCHOR, GRID_BBOX, GRID_COLUMNCONFIGURE, GRID_CONFIGURE,
+ GRID_FORGET, GRID_INFO, GRID_LOCATION, GRID_PROPAGATE, GRID_REMOVE,
+ GRID_ROWCONFIGURE, GRID_SIZE, GRID_SLAVES
+ };
+ int index;
+
+ if (objc >= 2) {
+ const char *argv1 = Tcl_GetString(objv[1]);
+
+ if ((argv1[0] == '.') || (argv1[0] == REL_SKIP) ||
+ (argv1[0] == REL_VERT)) {
+ 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) {
+ return TCL_ERROR;
+ }
+
+ switch ((enum options) index) {
+ case GRID_ANCHOR:
+ return GridAnchorCommand(tkwin, interp, objc, objv);
+ case GRID_BBOX:
+ return GridBboxCommand(tkwin, interp, objc, objv);
+ case GRID_CONFIGURE:
+ return ConfigureSlaves(interp, tkwin, objc-2, objv+2);
+ case GRID_FORGET:
+ case GRID_REMOVE:
+ return GridForgetRemoveCommand(tkwin, interp, objc, objv);
+ case GRID_INFO:
+ return GridInfoCommand(tkwin, interp, objc, objv);
+ case GRID_LOCATION:
+ return GridLocationCommand(tkwin, interp, objc, objv);
+ case GRID_PROPAGATE:
+ return GridPropagateCommand(tkwin, interp, objc, objv);
+ case GRID_SIZE:
+ return GridSizeCommand(tkwin, interp, objc, objv);
+ case GRID_SLAVES:
+ return GridSlavesCommand(tkwin, interp, objc, objv);
+
+ /*
+ * Sample argument combinations:
+ * grid columnconfigure <master> <index> -option
+ * grid columnconfigure <master> <index> -option value -option value
+ * grid rowconfigure <master> <index>
+ * grid rowconfigure <master> <index> -option
+ * grid rowconfigure <master> <index> -option value -option value.
+ */
+
+ case GRID_COLUMNCONFIGURE:
+ case GRID_ROWCONFIGURE:
+ return GridRowColumnConfigureCommand(tkwin, interp, objc, objv);
+ }
+
+ /* This should not happen */
+ Tcl_SetObjResult(interp, Tcl_NewStringObj("internal error in grid", -1));
+ Tcl_SetErrorCode(interp, "TK", "API_ABUSE", NULL);
+ return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GridAnchorCommand --
+ *
+ * Implementation of the [grid anchor] subcommand. See the user
+ * documentation for details on what it does.
+ *
+ * Results:
+ * Standard Tcl result.
+ *
+ * Side effects:
+ * May recompute grid geometry.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+GridAnchorCommand(
+ Tk_Window tkwin, /* Main window of the application. */
+ Tcl_Interp *interp, /* Current interpreter. */
+ int objc, /* Number of arguments. */
+ Tcl_Obj *const objv[]) /* Argument objects. */
+{
+ Tk_Window master;
+ Gridder *masterPtr;
+ GridMaster *gridPtr;
+ Tk_Anchor old;
+
+ if (objc > 4) {
+ Tcl_WrongNumArgs(interp, 2, objv, "window ?anchor?");
+ return TCL_ERROR;
+ }
+
+ if (TkGetWindowFromObj(interp, tkwin, objv[2], &master) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ masterPtr = GetGrid(master);
+
+ if (objc == 3) {
+ gridPtr = masterPtr->masterDataPtr;
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(
+ Tk_NameOfAnchor(gridPtr?gridPtr->anchor:GRID_DEFAULT_ANCHOR),
+ -1));
+ return TCL_OK;
+ }
+
+ InitMasterData(masterPtr);
+ gridPtr = masterPtr->masterDataPtr;
+ old = gridPtr->anchor;
+ if (Tk_GetAnchorFromObj(interp, objv[3], &gridPtr->anchor) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ /*
+ * Only request a relayout if the anchor changes.
+ */
+
+ if (old != gridPtr->anchor) {
+ if (masterPtr->abortPtr != NULL) {
+ *masterPtr->abortPtr = 1;
+ }
+ if (!(masterPtr->flags & REQUESTED_RELAYOUT)) {
+ masterPtr->flags |= REQUESTED_RELAYOUT;
+ Tcl_DoWhenIdle(ArrangeGrid, masterPtr);
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GridBboxCommand --
+ *
+ * Implementation of the [grid bbox] subcommand.
+ *
+ * Results:
+ * Standard Tcl result.
+ *
+ * Side effects:
+ * Places bounding box information in the interp's result field.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+GridBboxCommand(
+ Tk_Window tkwin, /* Main window of the application. */
+ Tcl_Interp *interp, /* Current interpreter. */
+ int objc, /* Number of arguments. */
+ Tcl_Obj *const objv[]) /* Argument objects. */
+{
+ Tk_Window master;
+ Gridder *masterPtr; /* master grid record */
+ GridMaster *gridPtr; /* pointer to grid data */
+ int row, column; /* origin for bounding box */
+ int row2, column2; /* end of bounding box */
+ int endX, endY; /* last column/row in the layout */
+ int x=0, y=0; /* starting pixels for this bounding box */
+ int width, height; /* size of the bounding box */
+
+ if (objc!=3 && objc != 5 && objc != 7) {
+ Tcl_WrongNumArgs(interp, 2, objv, "master ?column row ?column row??");
+ return TCL_ERROR;
+ }
+
+ if (TkGetWindowFromObj(interp, tkwin, objv[2], &master) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ masterPtr = GetGrid(master);
+
+ if (objc >= 5) {
+ if (Tcl_GetIntFromObj(interp, objv[3], &column) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (Tcl_GetIntFromObj(interp, objv[4], &row) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ column2 = column;
+ row2 = row;
+ }
+
+ if (objc == 7) {
+ if (Tcl_GetIntFromObj(interp, objv[5], &column2) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (Tcl_GetIntFromObj(interp, objv[6], &row2) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+
+ gridPtr = masterPtr->masterDataPtr;
+ if (gridPtr == NULL) {
+ Tcl_SetObjResult(interp, NewQuadObj(0, 0, 0, 0));
+ return TCL_OK;
+ }
+
+ SetGridSize(masterPtr);
+ endX = MAX(gridPtr->columnEnd, gridPtr->columnMax);
+ endY = MAX(gridPtr->rowEnd, gridPtr->rowMax);
+
+ if ((endX == 0) || (endY == 0)) {
+ Tcl_SetObjResult(interp, NewQuadObj(0, 0, 0, 0));
+ return TCL_OK;
+ }
+ if (objc == 3) {
+ row = 0;
+ column = 0;
+ row2 = endY;
+ column2 = endX;
+ }
+
+ if (column > column2) {
+ int temp = column;
+
+ column = column2;
+ column2 = temp;
+ }
+ if (row > row2) {
+ int temp = row;
+
+ row = row2;
+ row2 = temp;
+ }
+
+ if (column > 0 && column < endX) {
+ x = gridPtr->columnPtr[column-1].offset;
+ } else if (column > 0) {
+ x = gridPtr->columnPtr[endX-1].offset;
+ }
+
+ if (row > 0 && row < endY) {
+ y = gridPtr->rowPtr[row-1].offset;
+ } else if (row > 0) {
+ y = gridPtr->rowPtr[endY-1].offset;
+ }
+
+ if (column2 < 0) {
+ width = 0;
+ } else if (column2 >= endX) {
+ width = gridPtr->columnPtr[endX-1].offset - x;
+ } else {
+ width = gridPtr->columnPtr[column2].offset - x;
+ }
+
+ if (row2 < 0) {
+ height = 0;
+ } else if (row2 >= endY) {
+ height = gridPtr->rowPtr[endY-1].offset - y;
+ } else {
+ height = gridPtr->rowPtr[row2].offset - y;
+ }
+
+ Tcl_SetObjResult(interp, NewQuadObj(
+ x + gridPtr->startX, y + gridPtr->startY, width, height));
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GridForgetRemoveCommand --
+ *
+ * Implementation of the [grid forget]/[grid remove] subcommands. See the
+ * user documentation for details on what these do.
+ *
+ * Results:
+ * Standard Tcl result.
+ *
+ * Side effects:
+ * Removes a window from a grid layout.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+GridForgetRemoveCommand(
+ Tk_Window tkwin, /* Main window of the application. */
+ Tcl_Interp *interp, /* Current interpreter. */
+ int objc, /* Number of arguments. */
+ Tcl_Obj *const objv[]) /* Argument objects. */
+{
+ Tk_Window slave;
+ Gridder *slavePtr;
+ int i;
+ const char *string = Tcl_GetString(objv[1]);
+ char c = string[0];
+
+ for (i = 2; i < objc; i++) {
+ if (TkGetWindowFromObj(interp, tkwin, objv[i], &slave) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ slavePtr = GetGrid(slave);
+ if (slavePtr->masterPtr != NULL) {
+ /*
+ * For "forget", reset all the settings to their defaults
+ */
+
+ if (c == 'f') {
+ slavePtr->column = -1;
+ slavePtr->row = -1;
+ slavePtr->numCols = 1;
+ slavePtr->numRows = 1;
+ slavePtr->padX = 0;
+ slavePtr->padY = 0;
+ slavePtr->padLeft = 0;
+ slavePtr->padTop = 0;
+ slavePtr->iPadX = 0;
+ slavePtr->iPadY = 0;
+ if (slavePtr->in != NULL) {
+ Tcl_DecrRefCount(slavePtr->in);
+ slavePtr->in = NULL;
+ }
+ slavePtr->doubleBw = 2*Tk_Changes(tkwin)->border_width;
+ if (slavePtr->flags & REQUESTED_RELAYOUT) {
+ Tcl_CancelIdleCall(ArrangeGrid, slavePtr);
+ }
+ slavePtr->flags = 0;
+ slavePtr->sticky = 0;
+ } else {
+ /*
+ * When removing, store name of master to be able to
+ * restore it later, even if the master is recreated.
+ */
+
+ if (slavePtr->in != NULL) {
+ Tcl_DecrRefCount(slavePtr->in);
+ slavePtr->in = NULL;
+ }
+ if (slavePtr->masterPtr != NULL) {
+ slavePtr->in = Tcl_NewStringObj(
+ Tk_PathName(slavePtr->masterPtr->tkwin), -1);
+ Tcl_IncrRefCount(slavePtr->in);
+ }
+ }
+ 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);
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GridInfoCommand --
+ *
+ * Implementation of the [grid info] subcommand. See the user
+ * documentation for details on what it does.
+ *
+ * Results:
+ * Standard Tcl result.
+ *
+ * Side effects:
+ * Puts gridding information in the interpreter's result.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+GridInfoCommand(
+ Tk_Window tkwin, /* Main window of the application. */
+ Tcl_Interp *interp, /* Current interpreter. */
+ int objc, /* Number of arguments. */
+ Tcl_Obj *const objv[]) /* Argument objects. */
+{
+ register Gridder *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 = GetGrid(slave);
+ if (slavePtr->masterPtr == NULL) {
+ Tcl_ResetResult(interp);
+ return TCL_OK;
+ }
+
+ infoObj = Tcl_NewObj();
+ Tcl_DictObjPut(NULL, infoObj, Tcl_NewStringObj("-in", -1),
+ TkNewWindowObj(slavePtr->masterPtr->tkwin));
+ Tcl_DictObjPut(NULL, infoObj, Tcl_NewStringObj("-column", -1),
+ Tcl_NewIntObj(slavePtr->column));
+ Tcl_DictObjPut(NULL, infoObj, Tcl_NewStringObj("-row", -1),
+ Tcl_NewIntObj(slavePtr->row));
+ Tcl_DictObjPut(NULL, infoObj, Tcl_NewStringObj("-columnspan", -1),
+ Tcl_NewIntObj(slavePtr->numCols));
+ Tcl_DictObjPut(NULL, infoObj, Tcl_NewStringObj("-rowspan", -1),
+ Tcl_NewIntObj(slavePtr->numRows));
+ 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("-sticky", -1),
+ StickyToObj(slavePtr->sticky));
+ Tcl_SetObjResult(interp, infoObj);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GridLocationCommand --
+ *
+ * Implementation of the [grid location] subcommand. See the user
+ * documentation for details on what it does.
+ *
+ * Results:
+ * Standard Tcl result.
+ *
+ * Side effects:
+ * Puts location information in the interpreter's result field.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+GridLocationCommand(
+ Tk_Window tkwin, /* Main window of the application. */
+ Tcl_Interp *interp, /* Current interpreter. */
+ int objc, /* Number of arguments. */
+ Tcl_Obj *const objv[]) /* Argument objects. */
+{
+ Tk_Window master;
+ Gridder *masterPtr; /* Master grid record. */
+ GridMaster *gridPtr; /* Pointer to grid data. */
+ register SlotInfo *slotPtr;
+ int x, y; /* Offset in pixels, from edge of master. */
+ int i, j; /* Corresponding column and row indeces. */
+ int endX, endY; /* End of grid. */
+
+ if (objc != 5) {
+ Tcl_WrongNumArgs(interp, 2, objv, "master x y");
+ return TCL_ERROR;
+ }
+
+ if (TkGetWindowFromObj(interp, tkwin, objv[2], &master) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ if (Tk_GetPixelsFromObj(interp, master, objv[3], &x) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (Tk_GetPixelsFromObj(interp, master, objv[4], &y) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ masterPtr = GetGrid(master);
+ if (masterPtr->masterDataPtr == NULL) {
+ Tcl_SetObjResult(interp, NewPairObj(-1, -1));
+ return TCL_OK;
+ }
+ gridPtr = masterPtr->masterDataPtr;
+
+ /*
+ * Update any pending requests. This is not always the steady state value,
+ * as more configure events could be in the pipeline, but its as close as
+ * its easy to get.
+ */
+
+ while (masterPtr->flags & REQUESTED_RELAYOUT) {
+ Tcl_CancelIdleCall(ArrangeGrid, masterPtr);
+ ArrangeGrid(masterPtr);
+ }
+ SetGridSize(masterPtr);
+ endX = MAX(gridPtr->columnEnd, gridPtr->columnMax);
+ endY = MAX(gridPtr->rowEnd, gridPtr->rowMax);
+
+ slotPtr = masterPtr->masterDataPtr->columnPtr;
+ if (x < masterPtr->masterDataPtr->startX) {
+ i = -1;
+ } else {
+ x -= masterPtr->masterDataPtr->startX;
+ for (i = 0; slotPtr[i].offset < x && i < endX; i++) {
+ /* null body */
+ }
+ }
+
+ slotPtr = masterPtr->masterDataPtr->rowPtr;
+ if (y < masterPtr->masterDataPtr->startY) {
+ j = -1;
+ } else {
+ y -= masterPtr->masterDataPtr->startY;
+ for (j = 0; slotPtr[j].offset < y && j < endY; j++) {
+ /* null body */
+ }
+ }
+
+ Tcl_SetObjResult(interp, NewPairObj(i, j));
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GridPropagateCommand --
+ *
+ * Implementation of the [grid propagate] subcommand. See the user
+ * documentation for details on what it does.
+ *
+ * Results:
+ * Standard Tcl result.
+ *
+ * Side effects:
+ * May alter geometry propagation for a widget.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+GridPropagateCommand(
+ Tk_Window tkwin, /* Main window of the application. */
+ Tcl_Interp *interp, /* Current interpreter. */
+ int objc, /* Number of arguments. */
+ Tcl_Obj *const objv[]) /* Argument objects. */
+{
+ Tk_Window master;
+ Gridder *masterPtr;
+ int propagate, old;
+
+ 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 = GetGrid(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;
+ }
+
+ /*
+ * Only request a relayout if the propagation bit changes.
+ */
+
+ old = !(masterPtr->flags & DONT_PROPAGATE);
+ if (propagate != old) {
+ if (propagate) {
+ /*
+ * If we have slaves, we need to register as geometry master.
+ */
+
+ if (masterPtr->slavePtr != NULL) {
+ if (TkSetGeometryMaster(interp, master, "grid") != TCL_OK) {
+ return TCL_ERROR;
+ }
+ masterPtr->flags |= ALLOCED_MASTER;
+ }
+ masterPtr->flags &= ~DONT_PROPAGATE;
+ } else {
+ if (masterPtr->flags & ALLOCED_MASTER) {
+ TkFreeGeometryMaster(master, "grid");
+ masterPtr->flags &= ~ALLOCED_MASTER;
+ }
+ masterPtr->flags |= DONT_PROPAGATE;
+ }
+
+ /*
+ * Re-arrange 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_RELAYOUT)) {
+ masterPtr->flags |= REQUESTED_RELAYOUT;
+ Tcl_DoWhenIdle(ArrangeGrid, masterPtr);
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GridRowColumnConfigureCommand --
+ *
+ * Implementation of the [grid rowconfigure] and [grid columnconfigure]
+ * subcommands. See the user documentation for details on what these do.
+ *
+ * Results:
+ * Standard Tcl result.
+ *
+ * Side effects:
+ * Depends on arguments; see user documentation.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+GridRowColumnConfigureCommand(
+ Tk_Window tkwin, /* Main window of the application. */
+ Tcl_Interp *interp, /* Current interpreter. */
+ int objc, /* Number of arguments. */
+ Tcl_Obj *const objv[]) /* Argument objects. */
+{
+ Tk_Window master, slave;
+ Gridder *masterPtr, *slavePtr;
+ SlotInfo *slotPtr = NULL;
+ int slot; /* the column or row number */
+ int slotType; /* COLUMN or ROW */
+ int size; /* the configuration value */
+ int lObjc; /* Number of items in index list */
+ Tcl_Obj **lObjv; /* array of indices */
+ int ok; /* temporary TCL result code */
+ int i, j, first, last;
+ const char *string;
+ static const char *const optionStrings[] = {
+ "-minsize", "-pad", "-uniform", "-weight", NULL
+ };
+ enum options {
+ ROWCOL_MINSIZE, ROWCOL_PAD, ROWCOL_UNIFORM, ROWCOL_WEIGHT
+ };
+ int index;
+ Tcl_Obj *listCopy;
+
+ if (((objc % 2 != 0) && (objc > 6)) || (objc < 4)) {
+ Tcl_WrongNumArgs(interp, 2, objv, "master index ?-option value ...?");
+ return TCL_ERROR;
+ }
+
+ if (TkGetWindowFromObj(interp, tkwin, objv[2], &master) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ listCopy = Tcl_DuplicateObj(objv[3]);
+ Tcl_IncrRefCount(listCopy);
+ if (Tcl_ListObjGetElements(interp, listCopy, &lObjc, &lObjv) != TCL_OK) {
+ Tcl_DecrRefCount(listCopy);
+ return TCL_ERROR;
+ }
+
+ string = Tcl_GetString(objv[1]);
+ slotType = (*string == 'c') ? COLUMN : ROW;
+ if (lObjc == 0) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf("no %s indices specified",
+ (slotType == COLUMN) ? "column" : "row"));
+ Tcl_SetErrorCode(interp, "TK", "GRID", "NO_INDEX", NULL);
+ Tcl_DecrRefCount(listCopy);
+ return TCL_ERROR;
+ }
+
+ masterPtr = GetGrid(master);
+ first = 0; /* lint */
+ last = 0; /* lint */
+
+ if ((objc == 4) || (objc == 5)) {
+ if (lObjc != 1) {
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(
+ "must specify a single element on retrieval", -1));
+ Tcl_SetErrorCode(interp, "TK", "GRID", "USAGE", NULL);
+ Tcl_DecrRefCount(listCopy);
+ return TCL_ERROR;
+ }
+ if (Tcl_GetIntFromObj(interp, lObjv[0], &slot) != TCL_OK) {
+ Tcl_AppendResult(interp,
+ " (when retrieving options only integer indices are "
+ "allowed)", NULL);
+ Tcl_SetErrorCode(interp, "TK", "GRID", "INDEX_FORMAT", NULL);
+ Tcl_DecrRefCount(listCopy);
+ return TCL_ERROR;
+ }
+ ok = CheckSlotData(masterPtr, slot, slotType, /* checkOnly */ 1);
+ if (ok == TCL_OK) {
+ slotPtr = (slotType == COLUMN) ?
+ masterPtr->masterDataPtr->columnPtr :
+ masterPtr->masterDataPtr->rowPtr;
+ }
+
+ /*
+ * Return all of the options for this row or column. If the request is
+ * out of range, return all 0's.
+ */
+
+ if (objc == 4) {
+ int minsize = 0, pad = 0, weight = 0;
+ Tk_Uid uniform = NULL;
+ Tcl_Obj *res = Tcl_NewListObj(0, NULL);
+
+ if (ok == TCL_OK) {
+ minsize = slotPtr[slot].minSize;
+ pad = slotPtr[slot].pad;
+ weight = slotPtr[slot].weight;
+ uniform = slotPtr[slot].uniform;
+ }
+
+ Tcl_ListObjAppendElement(interp, res,
+ Tcl_NewStringObj("-minsize", -1));
+ Tcl_ListObjAppendElement(interp, res, Tcl_NewIntObj(minsize));
+ Tcl_ListObjAppendElement(interp, res,
+ Tcl_NewStringObj("-pad", -1));
+ Tcl_ListObjAppendElement(interp, res, Tcl_NewIntObj(pad));
+ Tcl_ListObjAppendElement(interp, res,
+ Tcl_NewStringObj("-uniform", -1));
+ Tcl_ListObjAppendElement(interp, res,
+ Tcl_NewStringObj(uniform == NULL ? "" : uniform, -1));
+ Tcl_ListObjAppendElement(interp, res,
+ Tcl_NewStringObj("-weight", -1));
+ Tcl_ListObjAppendElement(interp, res, Tcl_NewIntObj(weight));
+ Tcl_SetObjResult(interp, res);
+ Tcl_DecrRefCount(listCopy);
+ return TCL_OK;
+ }
+
+ /*
+ * If only one option is given, with no value, the current value is
+ * returned.
+ */
+
+ if (Tcl_GetIndexFromObjStruct(interp, objv[4], optionStrings,
+ sizeof(char *), "option", 0, &index) != TCL_OK) {
+ Tcl_DecrRefCount(listCopy);
+ return TCL_ERROR;
+ }
+ if (index == ROWCOL_MINSIZE) {
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(
+ (ok == TCL_OK) ? slotPtr[slot].minSize : 0));
+ } else if (index == ROWCOL_WEIGHT) {
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(
+ (ok == TCL_OK) ? slotPtr[slot].weight : 0));
+ } else if (index == ROWCOL_UNIFORM) {
+ Tk_Uid value = (ok == TCL_OK) ? slotPtr[slot].uniform : "";
+
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(
+ (value == NULL) ? "" : value, -1));
+ } else if (index == ROWCOL_PAD) {
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(
+ (ok == TCL_OK) ? slotPtr[slot].pad : 0));
+ }
+ Tcl_DecrRefCount(listCopy);
+ return TCL_OK;
+ }
+
+ for (j = 0; j < lObjc; j++) {
+ int allSlaves = 0;
+
+ if (Tcl_GetIntFromObj(NULL, lObjv[j], &slot) == TCL_OK) {
+ first = slot;
+ last = slot;
+ slavePtr = NULL;
+ } else if (strcmp(Tcl_GetString(lObjv[j]), "all") == 0) {
+ /*
+ * Make sure master is initialised.
+ */
+
+ InitMasterData(masterPtr);
+
+ slavePtr = masterPtr->slavePtr;
+ if (slavePtr == NULL) {
+ continue;
+ }
+ allSlaves = 1;
+ } else if (TkGetWindowFromObj(NULL, tkwin, lObjv[j], &slave)
+ == TCL_OK) {
+ /*
+ * Is it gridded in this master?
+ */
+
+ slavePtr = GetGrid(slave);
+ if (slavePtr->masterPtr != masterPtr) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "the window \"%s\" is not managed by \"%s\"",
+ Tcl_GetString(lObjv[j]), Tcl_GetString(objv[2])));
+ Tcl_SetErrorCode(interp, "TK", "GRID", "NOT_MASTER", NULL);
+ Tcl_DecrRefCount(listCopy);
+ return TCL_ERROR;
+ }
+ } else {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "illegal index \"%s\"", Tcl_GetString(lObjv[j])));
+ Tcl_SetErrorCode(interp, "TK", "VALUE", "GRID_INDEX", NULL);
+ Tcl_DecrRefCount(listCopy);
+ return TCL_ERROR;
+ }
+
+ /*
+ * The outer loop is only to handle "all".
+ */
+
+ do {
+ if (slavePtr != NULL) {
+ first = (slotType == COLUMN) ?
+ slavePtr->column : slavePtr->row;
+ last = first - 1 + ((slotType == COLUMN) ?
+ slavePtr->numCols : slavePtr->numRows);
+ }
+
+ for (slot = first; slot <= last; slot++) {
+ ok = CheckSlotData(masterPtr, slot, slotType, /*checkOnly*/ 0);
+ if (ok != TCL_OK) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "\"%s\" is out of range",
+ Tcl_GetString(lObjv[j])));
+ Tcl_SetErrorCode(interp, "TK", "GRID", "INDEX_RANGE",
+ NULL);
+ Tcl_DecrRefCount(listCopy);
+ return TCL_ERROR;
+ }
+ slotPtr = (slotType == COLUMN) ?
+ masterPtr->masterDataPtr->columnPtr :
+ masterPtr->masterDataPtr->rowPtr;
+
+ /*
+ * Loop through each option value pair, setting the values as
+ * required.
+ */
+
+ for (i = 4; i < objc; i += 2) {
+ if (Tcl_GetIndexFromObjStruct(interp, objv[i], optionStrings,
+ sizeof(char *), "option", 0, &index) != TCL_OK) {
+ Tcl_DecrRefCount(listCopy);
+ return TCL_ERROR;
+ }
+ if (index == ROWCOL_MINSIZE) {
+ if (Tk_GetPixelsFromObj(interp, master, objv[i+1],
+ &size) != TCL_OK) {
+ Tcl_DecrRefCount(listCopy);
+ return TCL_ERROR;
+ } else {
+ slotPtr[slot].minSize = size;
+ }
+ } else if (index == ROWCOL_WEIGHT) {
+ int wt;
+
+ if (Tcl_GetIntFromObj(interp,objv[i+1],&wt)!=TCL_OK) {
+ Tcl_DecrRefCount(listCopy);
+ return TCL_ERROR;
+ } else if (wt < 0) {
+ Tcl_DecrRefCount(listCopy);
+ goto negativeIndex;
+ } else {
+ slotPtr[slot].weight = wt;
+ }
+ } else if (index == ROWCOL_UNIFORM) {
+ slotPtr[slot].uniform =
+ Tk_GetUid(Tcl_GetString(objv[i+1]));
+ if (slotPtr[slot].uniform != NULL &&
+ slotPtr[slot].uniform[0] == 0) {
+ slotPtr[slot].uniform = NULL;
+ }
+ } else if (index == ROWCOL_PAD) {
+ if (Tk_GetPixelsFromObj(interp, master, objv[i+1],
+ &size) != TCL_OK) {
+ Tcl_DecrRefCount(listCopy);
+ return TCL_ERROR;
+ } else if (size < 0) {
+ Tcl_DecrRefCount(listCopy);
+ goto negativeIndex;
+ } else {
+ slotPtr[slot].pad = size;
+ }
+ }
+ }
+ }
+ if (slavePtr != NULL) {
+ slavePtr = slavePtr->nextPtr;
+ }
+ } while ((allSlaves == 1) && (slavePtr != NULL));
+ }
+ Tcl_DecrRefCount(listCopy);
+
+ /*
+ * We changed a property, re-arrange the table, and check for constraint
+ * shrinkage. A null slotPtr will occur for 'all' checks.
+ */
+
+ if (slotPtr != NULL) {
+ if (slotType == ROW) {
+ int last = masterPtr->masterDataPtr->rowMax - 1;
+
+ while ((last >= 0) && (slotPtr[last].weight == 0)
+ && (slotPtr[last].pad == 0) && (slotPtr[last].minSize == 0)
+ && (slotPtr[last].uniform == NULL)) {
+ last--;
+ }
+ masterPtr->masterDataPtr->rowMax = last+1;
+ } else {
+ int last = masterPtr->masterDataPtr->columnMax - 1;
+
+ while ((last >= 0) && (slotPtr[last].weight == 0)
+ && (slotPtr[last].pad == 0) && (slotPtr[last].minSize == 0)
+ && (slotPtr[last].uniform == NULL)) {
+ last--;
+ }
+ masterPtr->masterDataPtr->columnMax = last + 1;
+ }
+ }
+
+ if (masterPtr->abortPtr != NULL) {
+ *masterPtr->abortPtr = 1;
+ }
+ if (!(masterPtr->flags & REQUESTED_RELAYOUT)) {
+ masterPtr->flags |= REQUESTED_RELAYOUT;
+ Tcl_DoWhenIdle(ArrangeGrid, masterPtr);
+ }
+ return TCL_OK;
+
+ negativeIndex:
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "invalid arg \"%s\": should be non-negative",
+ Tcl_GetString(objv[i])));
+ Tcl_SetErrorCode(interp, "TK", "GRID", "NEG_INDEX", NULL);
+ return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GridSizeCommand --
+ *
+ * Implementation of the [grid size] subcommand. See the user
+ * documentation for details on what it does.
+ *
+ * Results:
+ * Standard Tcl result.
+ *
+ * Side effects:
+ * Puts grid size information in the interpreter's result.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+GridSizeCommand(
+ Tk_Window tkwin, /* Main window of the application. */
+ Tcl_Interp *interp, /* Current interpreter. */
+ int objc, /* Number of arguments. */
+ Tcl_Obj *const objv[]) /* Argument objects. */
+{
+ Tk_Window master;
+ Gridder *masterPtr;
+ GridMaster *gridPtr; /* pointer to grid data */
+
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "window");
+ return TCL_ERROR;
+ }
+
+ if (TkGetWindowFromObj(interp, tkwin, objv[2], &master) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ masterPtr = GetGrid(master);
+
+ if (masterPtr->masterDataPtr != NULL) {
+ SetGridSize(masterPtr);
+ gridPtr = masterPtr->masterDataPtr;
+ Tcl_SetObjResult(interp, NewPairObj(
+ MAX(gridPtr->columnEnd, gridPtr->columnMax),
+ MAX(gridPtr->rowEnd, gridPtr->rowMax)));
+ } else {
+ Tcl_SetObjResult(interp, NewPairObj(0, 0));
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GridSlavesCommand --
+ *
+ * Implementation of the [grid slaves] subcommand. See the user
+ * documentation for details on what it does.
+ *
+ * Results:
+ * Standard Tcl result.
+ *
+ * Side effects:
+ * Places a list of slaves of the specified window in the interpreter's
+ * result field.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+GridSlavesCommand(
+ Tk_Window tkwin, /* Main window of the application. */
+ Tcl_Interp *interp, /* Current interpreter. */
+ int objc, /* Number of arguments. */
+ Tcl_Obj *const objv[]) /* Argument objects. */
+{
+ Tk_Window master;
+ Gridder *masterPtr; /* master grid record */
+ Gridder *slavePtr;
+ int i, value, index;
+ int row = -1, column = -1;
+ static const char *const optionStrings[] = {
+ "-column", "-row", NULL
+ };
+ enum options { SLAVES_COLUMN, SLAVES_ROW };
+ Tcl_Obj *res;
+
+ if ((objc < 3) || ((objc % 2) == 0)) {
+ Tcl_WrongNumArgs(interp, 2, objv, "window ?-option value ...?");
+ return TCL_ERROR;
+ }
+
+ for (i = 3; i < objc; i += 2) {
+ if (Tcl_GetIndexFromObjStruct(interp, objv[i], optionStrings,
+ sizeof(char *), "option", 0, &index) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (Tcl_GetIntFromObj(interp, objv[i+1], &value) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (value < 0) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "%d is an invalid value: should NOT be < 0", value));
+ Tcl_SetErrorCode(interp, "TK", "GRID", "NEG_INDEX", NULL);
+ return TCL_ERROR;
+ }
+ if (index == SLAVES_COLUMN) {
+ column = value;
+ } else {
+ row = value;
+ }
+ }
+
+ if (TkGetWindowFromObj(interp, tkwin, objv[2], &master) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ masterPtr = GetGrid(master);
+
+ res = Tcl_NewListObj(0, NULL);
+ for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
+ slavePtr = slavePtr->nextPtr) {
+ if ((column >= 0) && (slavePtr->column > column
+ || slavePtr->column+slavePtr->numCols-1 < column)) {
+ continue;
+ }
+ if ((row >= 0) && (slavePtr->row > row ||
+ slavePtr->row+slavePtr->numRows-1 < row)) {
+ continue;
+ }
+ Tcl_ListObjAppendElement(interp,res, TkNewWindowObj(slavePtr->tkwin));
+ }
+ Tcl_SetObjResult(interp, res);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GridReqProc --
+ *
+ * This procedure is invoked by Tk_GeometryRequest for windows managed by
+ * the grid.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Arranges for tkwin, and all its managed siblings, to be re-arranged at
+ * the next idle point.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+GridReqProc(
+ ClientData clientData, /* Grid's information about window that got
+ * new preferred geometry. */
+ Tk_Window tkwin) /* Other Tk-related information about the
+ * window. */
+{
+ register Gridder *gridPtr = clientData;
+
+ gridPtr = gridPtr->masterPtr;
+ if (gridPtr && !(gridPtr->flags & REQUESTED_RELAYOUT)) {
+ gridPtr->flags |= REQUESTED_RELAYOUT;
+ Tcl_DoWhenIdle(ArrangeGrid, gridPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GridLostSlaveProc --
+ *
+ * This procedure is invoked by Tk whenever some other geometry claims
+ * control over a slave that used to be managed by us.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Forgets all grid-related information about the slave.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+GridLostSlaveProc(
+ ClientData clientData, /* Grid structure for slave window that was
+ * stolen away. */
+ Tk_Window tkwin) /* Tk's handle for the slave window. */
+{
+ register Gridder *slavePtr = clientData;
+
+ if (slavePtr->masterPtr->tkwin != Tk_Parent(slavePtr->tkwin)) {
+ Tk_UnmaintainGeometry(slavePtr->tkwin, slavePtr->masterPtr->tkwin);
+ }
+ Unlink(slavePtr);
+ Tk_UnmapWindow(slavePtr->tkwin);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * AdjustOffsets --
+ *
+ * This procedure adjusts the size of the layout to fit in the space
+ * provided. If it needs more space, the extra is added according to the
+ * weights. If it needs less, the space is removed according to the
+ * weights, but at no time does the size drop below the minsize specified
+ * for that slot.
+ *
+ * Results:
+ * The size used by the layout.
+ *
+ * Side effects:
+ * The slot offsets are modified to shrink the layout.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+AdjustOffsets(
+ int size, /* The total layout size (in pixels). */
+ int slots, /* Number of slots. */
+ register SlotInfo *slotPtr) /* Pointer to slot array. */
+{
+ register int slot; /* Current slot. */
+ int diff; /* Extra pixels needed to add to the layout. */
+ int totalWeight; /* Sum of the weights for all the slots. */
+ int weight; /* Sum of the weights so far. */
+ int minSize; /* Minimum possible layout size. */
+ int newDiff; /* The most pixels that can be added on the
+ * current pass. */
+
+ diff = size - slotPtr[slots-1].offset;
+
+ /*
+ * The layout is already the correct size; all done.
+ */
+
+ if (diff == 0) {
+ return size;
+ }
+
+ /*
+ * If all the weights are zero, there is nothing more to do.
+ */
+
+ totalWeight = 0;
+ for (slot = 0; slot < slots; slot++) {
+ totalWeight += slotPtr[slot].weight;
+ }
+
+ if (totalWeight == 0) {
+ return slotPtr[slots-1].offset;
+ }
+
+ /*
+ * Add extra space according to the slot weights. This is done
+ * cumulatively to prevent round-off error accumulation.
+ */
+
+ if (diff > 0) {
+ weight = 0;
+ for (slot = 0; slot < slots; slot++) {
+ weight += slotPtr[slot].weight;
+ slotPtr[slot].offset += diff * weight / totalWeight;
+ }
+ return size;
+ }
+
+ /*
+ * The layout must shrink below its requested size. Compute the minimum
+ * possible size by looking at the slot minSizes. Store each slot's
+ * minimum size in temp.
+ */
+
+ minSize = 0;
+ for (slot = 0; slot < slots; slot++) {
+ if (slotPtr[slot].weight > 0) {
+ slotPtr[slot].temp = slotPtr[slot].minSize;
+ } else if (slot > 0) {
+ slotPtr[slot].temp = slotPtr[slot].offset - slotPtr[slot-1].offset;
+ } else {
+ slotPtr[slot].temp = slotPtr[slot].offset;
+ }
+ minSize += slotPtr[slot].temp;
+ }
+
+ /*
+ * If the requested size is less than the minimum required size, set the
+ * slot sizes to their minimum values.
+ */
+
+ if (size <= minSize) {
+ int offset = 0;
+
+ for (slot = 0; slot < slots; slot++) {
+ offset += slotPtr[slot].temp;
+ slotPtr[slot].offset = offset;
+ }
+ return minSize;
+ }
+
+ /*
+ * Remove space from slots according to their weights. The weights get
+ * renormalized anytime a slot shrinks to its minimum size.
+ */
+
+ while (diff < 0) {
+ /*
+ * Find the total weight for the shrinkable slots.
+ */
+
+ totalWeight = 0;
+ for (slot = 0; slot < slots; slot++) {
+ int current = (slot == 0) ? slotPtr[slot].offset :
+ slotPtr[slot].offset - slotPtr[slot-1].offset;
+
+ if (current > slotPtr[slot].minSize) {
+ totalWeight += slotPtr[slot].weight;
+ slotPtr[slot].temp = slotPtr[slot].weight;
+ } else {
+ slotPtr[slot].temp = 0;
+ }
+ }
+ if (totalWeight == 0) {
+ break;
+ }
+
+ /*
+ * Find the maximum amount of space we can distribute this pass.
+ */
+
+ newDiff = diff;
+ for (slot = 0; slot < slots; slot++) {
+ int current; /* Current size of this slot. */
+ int maxDiff; /* Maximum diff that would cause this slot to
+ * equal its minsize. */
+
+ if (slotPtr[slot].temp == 0) {
+ continue;
+ }
+ current = (slot == 0) ? slotPtr[slot].offset :
+ slotPtr[slot].offset - slotPtr[slot-1].offset;
+ maxDiff = totalWeight * (slotPtr[slot].minSize - current)
+ / slotPtr[slot].temp;
+ if (maxDiff > newDiff) {
+ newDiff = maxDiff;
+ }
+ }
+
+ /*
+ * Now distribute the space.
+ */
+
+ weight = 0;
+ for (slot = 0; slot < slots; slot++) {
+ weight += slotPtr[slot].temp;
+ slotPtr[slot].offset += newDiff * weight / totalWeight;
+ }
+ diff -= newDiff;
+ }
+ return size;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * AdjustForSticky --
+ *
+ * This procedure adjusts the size of a slave in its cavity based on its
+ * "sticky" flags.
+ *
+ * Results:
+ * The input x, y, width, and height are changed to represent the desired
+ * coordinates of the slave.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+AdjustForSticky(
+ Gridder *slavePtr, /* Slave window to arrange in its cavity. */
+ int *xPtr, /* Pixel location of the left edge of the cavity. */
+ int *yPtr, /* Pixel location of the top edge of the cavity. */
+ int *widthPtr, /* Width of the cavity (in pixels). */
+ int *heightPtr) /* Height of the cavity (in pixels). */
+{
+ int diffx = 0; /* Cavity width - slave width. */
+ int diffy = 0; /* Cavity hight - slave height. */
+ int sticky = slavePtr->sticky;
+
+ *xPtr += slavePtr->padLeft;
+ *widthPtr -= slavePtr->padX;
+ *yPtr += slavePtr->padTop;
+ *heightPtr -= slavePtr->padY;
+
+ if (*widthPtr > (Tk_ReqWidth(slavePtr->tkwin) + slavePtr->iPadX)) {
+ diffx = *widthPtr - (Tk_ReqWidth(slavePtr->tkwin) + slavePtr->iPadX);
+ *widthPtr = Tk_ReqWidth(slavePtr->tkwin) + slavePtr->iPadX;
+ }
+
+ if (*heightPtr > (Tk_ReqHeight(slavePtr->tkwin) + slavePtr->iPadY)) {
+ diffy = *heightPtr - (Tk_ReqHeight(slavePtr->tkwin) + slavePtr->iPadY);
+ *heightPtr = Tk_ReqHeight(slavePtr->tkwin) + slavePtr->iPadY;
+ }
+
+ if (sticky&STICK_EAST && sticky&STICK_WEST) {
+ *widthPtr += diffx;
+ }
+ if (sticky&STICK_NORTH && sticky&STICK_SOUTH) {
+ *heightPtr += diffy;
+ }
+ if (!(sticky&STICK_WEST)) {
+ *xPtr += (sticky&STICK_EAST) ? diffx : diffx/2;
+ }
+ if (!(sticky&STICK_NORTH)) {
+ *yPtr += (sticky&STICK_SOUTH) ? diffy : diffy/2;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ArrangeGrid --
+ *
+ * This procedure is invoked (using the Tcl_DoWhenIdle mechanism) to
+ * re-layout a set of windows managed by the grid. It is invoked at idle
+ * time so that a series of grid requests can be merged into a single
+ * layout operation.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The slaves of masterPtr may get resized or moved.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+ArrangeGrid(
+ ClientData clientData) /* Structure describing master whose slaves
+ * are to be re-layed out. */
+{
+ register Gridder *masterPtr = clientData;
+ register Gridder *slavePtr;
+ GridMaster *slotPtr = masterPtr->masterDataPtr;
+ int abort;
+ int width, height; /* Requested size of layout, in pixels. */
+ int realWidth, realHeight; /* Actual size layout should take-up. */
+ int usedX, usedY;
+
+ masterPtr->flags &= ~REQUESTED_RELAYOUT;
+
+ /*
+ * If the master has no slaves anymore, then don't do anything at all:
+ * just leave the master's size as-is. Otherwise there is no way to
+ * "relinquish" control over the master so another geometry manager can
+ * take over.
+ */
+
+ if (masterPtr->slavePtr == NULL) {
+ return;
+ }
+
+ if (masterPtr->masterDataPtr == NULL) {
+ return;
+ }
+
+ /*
+ * Abort any nested call to ArrangeGrid 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);
+
+ /*
+ * Call the constraint engine to fill in the row and column offsets.
+ */
+
+ SetGridSize(masterPtr);
+ width = ResolveConstraints(masterPtr, COLUMN, 0);
+ height = ResolveConstraints(masterPtr, ROW, 0);
+ width += Tk_InternalBorderLeft(masterPtr->tkwin) +
+ Tk_InternalBorderRight(masterPtr->tkwin);
+ height += Tk_InternalBorderTop(masterPtr->tkwin) +
+ Tk_InternalBorderBottom(masterPtr->tkwin);
+
+ if (width < Tk_MinReqWidth(masterPtr->tkwin)) {
+ width = Tk_MinReqWidth(masterPtr->tkwin);
+ }
+ if (height < Tk_MinReqHeight(masterPtr->tkwin)) {
+ height = Tk_MinReqHeight(masterPtr->tkwin);
+ }
+
+ if (((width != Tk_ReqWidth(masterPtr->tkwin))
+ || (height != Tk_ReqHeight(masterPtr->tkwin)))
+ && !(masterPtr->flags & DONT_PROPAGATE)) {
+ Tk_GeometryRequest(masterPtr->tkwin, width, height);
+ if (width>1 && height>1) {
+ masterPtr->flags |= REQUESTED_RELAYOUT;
+ Tcl_DoWhenIdle(ArrangeGrid, masterPtr);
+ }
+ masterPtr->abortPtr = NULL;
+ Tcl_Release(masterPtr);
+ return;
+ }
+
+ /*
+ * If the currently requested layout size doesn't match the master's
+ * window size, then adjust the slot offsets according to the weights. If
+ * all of the weights are zero, place the layout according to the anchor
+ * value.
+ */
+
+ realWidth = Tk_Width(masterPtr->tkwin) -
+ Tk_InternalBorderLeft(masterPtr->tkwin) -
+ Tk_InternalBorderRight(masterPtr->tkwin);
+ realHeight = Tk_Height(masterPtr->tkwin) -
+ Tk_InternalBorderTop(masterPtr->tkwin) -
+ Tk_InternalBorderBottom(masterPtr->tkwin);
+ usedX = AdjustOffsets(realWidth,
+ MAX(slotPtr->columnEnd, slotPtr->columnMax), slotPtr->columnPtr);
+ usedY = AdjustOffsets(realHeight, MAX(slotPtr->rowEnd, slotPtr->rowMax),
+ slotPtr->rowPtr);
+ TkComputeAnchor(masterPtr->masterDataPtr->anchor, masterPtr->tkwin,
+ 0, 0, usedX, usedY, &slotPtr->startX, &slotPtr->startY);
+
+ /*
+ * Now adjust the actual size of the slave to its cavity by computing the
+ * cavity size, and adjusting the widget according to its stickyness.
+ */
+
+ for (slavePtr = masterPtr->slavePtr; slavePtr != NULL && !abort;
+ slavePtr = slavePtr->nextPtr) {
+ int x, y; /* Top left coordinate */
+ int width, height; /* Slot or slave size */
+ int col = slavePtr->column;
+ int row = slavePtr->row;
+
+ x = (col>0) ? slotPtr->columnPtr[col-1].offset : 0;
+ y = (row>0) ? slotPtr->rowPtr[row-1].offset : 0;
+
+ width = slotPtr->columnPtr[slavePtr->numCols+col-1].offset - x;
+ height = slotPtr->rowPtr[slavePtr->numRows+row-1].offset - y;
+
+ x += slotPtr->startX;
+ y += slotPtr->startY;
+
+ AdjustForSticky(slavePtr, &x, &y, &width, &height);
+
+ /*
+ * Now put the window in the proper spot. (This was taken directly
+ * from tkPack.c.) 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) {
+ break;
+ }
+
+ /*
+ * 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);
+ }
+ }
+
+ masterPtr->abortPtr = NULL;
+ Tcl_Release(masterPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ResolveConstraints --
+ *
+ * Resolve all of the column and row boundaries. Most of the calculations
+ * are identical for rows and columns, so this procedure is called twice,
+ * once for rows, and again for columns.
+ *
+ * Results:
+ * The offset (in pixels) from the left/top edge of this layout is
+ * returned.
+ *
+ * Side effects:
+ * The slot offsets are copied into the SlotInfo structure for the
+ * geometry master.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+ResolveConstraints(
+ Gridder *masterPtr, /* The geometry master for this grid. */
+ int slotType, /* Either ROW or COLUMN. */
+ int maxOffset) /* The actual maximum size of this layout in
+ * pixels, or 0 (not currently used). */
+{
+ register SlotInfo *slotPtr; /* Pointer to row/col constraints. */
+ register Gridder *slavePtr; /* List of slave windows in this grid. */
+ int constraintCount; /* Count of rows or columns that have
+ * constraints. */
+ int slotCount; /* Last occupied row or column. */
+ int gridCount; /* The larger of slotCount and
+ * constraintCount. */
+ GridLayout *layoutPtr; /* Temporary layout structure. */
+ int requiredSize; /* The natural size of the grid (pixels).
+ * This is the minimum size needed to
+ * accomodate all of the slaves at their
+ * requested sizes. */
+ int offset; /* The pixel offset of the right edge of the
+ * current slot from the beginning of the
+ * layout. */
+ int slot; /* The current slot. */
+ int start; /* The first slot of a contiguous set whose
+ * constraints are not yet fully resolved. */
+ int end; /* The Last slot of a contiguous set whose
+ * constraints are not yet fully resolved. */
+ UniformGroup uniformPre[UNIFORM_PREALLOC];
+ /* Pre-allocated space for uniform groups. */
+ UniformGroup *uniformGroupPtr;
+ /* Uniform groups data. */
+ int uniformGroups; /* Number of currently used uniform groups. */
+ int uniformGroupsAlloced; /* Size of allocated space for uniform
+ * groups. */
+ int weight, minSize;
+ int prevGrow, accWeight, grow;
+
+ /*
+ * For typical sized tables, we'll use stack space for the layout data to
+ * avoid the overhead of a malloc and free for every layout.
+ */
+
+ GridLayout layoutData[TYPICAL_SIZE + 1];
+
+ if (slotType == COLUMN) {
+ constraintCount = masterPtr->masterDataPtr->columnMax;
+ slotCount = masterPtr->masterDataPtr->columnEnd;
+ slotPtr = masterPtr->masterDataPtr->columnPtr;
+ } else {
+ constraintCount = masterPtr->masterDataPtr->rowMax;
+ slotCount = masterPtr->masterDataPtr->rowEnd;
+ slotPtr = masterPtr->masterDataPtr->rowPtr;
+ }
+
+ /*
+ * Make sure there is enough memory for the layout.
+ */
+
+ gridCount = MAX(constraintCount, slotCount);
+ if (gridCount >= TYPICAL_SIZE) {
+ layoutPtr = ckalloc(sizeof(GridLayout) * (1+gridCount));
+ } else {
+ layoutPtr = layoutData;
+ }
+
+ /*
+ * Allocate an extra layout slot to represent the left/top edge of the 0th
+ * slot to make it easier to calculate slot widths from offsets without
+ * special case code.
+ *
+ * Initialize the "dummy" slot to the left/top of the table. This slot
+ * avoids special casing the first slot.
+ */
+
+ layoutPtr->minOffset = 0;
+ layoutPtr->maxOffset = 0;
+ layoutPtr++;
+
+ /*
+ * Step 1.
+ * Copy the slot constraints into the layout structure, and initialize the
+ * rest of the fields.
+ */
+
+ for (slot=0; slot < constraintCount; slot++) {
+ layoutPtr[slot].minSize = slotPtr[slot].minSize;
+ layoutPtr[slot].weight = slotPtr[slot].weight;
+ layoutPtr[slot].uniform = slotPtr[slot].uniform;
+ layoutPtr[slot].pad = slotPtr[slot].pad;
+ layoutPtr[slot].binNextPtr = NULL;
+ }
+ for (; slot<gridCount; slot++) {
+ layoutPtr[slot].minSize = 0;
+ layoutPtr[slot].weight = 0;
+ layoutPtr[slot].uniform = NULL;
+ layoutPtr[slot].pad = 0;
+ layoutPtr[slot].binNextPtr = NULL;
+ }
+
+ /*
+ * Step 2.
+ * Slaves with a span of 1 are used to determine the minimum size of each
+ * slot. Slaves whose span is two or more slots don't contribute to the
+ * minimum size of each slot directly, but can cause slots to grow if
+ * their size exceeds the the sizes of the slots they span.
+ *
+ * Bin all slaves whose spans are > 1 by their right edges. This allows
+ * the computation on minimum and maximum possible layout sizes at each
+ * slot boundary, without the need to re-sort the slaves.
+ */
+
+ switch (slotType) {
+ case COLUMN:
+ for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
+ slavePtr = slavePtr->nextPtr) {
+ int rightEdge = slavePtr->column + slavePtr->numCols - 1;
+
+ slavePtr->size = Tk_ReqWidth(slavePtr->tkwin) + slavePtr->padX
+ + slavePtr->iPadX + slavePtr->doubleBw;
+ if (slavePtr->numCols > 1) {
+ slavePtr->binNextPtr = layoutPtr[rightEdge].binNextPtr;
+ layoutPtr[rightEdge].binNextPtr = slavePtr;
+ } else if (rightEdge >= 0) {
+ int size = slavePtr->size + layoutPtr[rightEdge].pad;
+
+ if (size > layoutPtr[rightEdge].minSize) {
+ layoutPtr[rightEdge].minSize = size;
+ }
+ }
+ }
+ break;
+ case ROW:
+ for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
+ slavePtr = slavePtr->nextPtr) {
+ int rightEdge = slavePtr->row + slavePtr->numRows - 1;
+
+ slavePtr->size = Tk_ReqHeight(slavePtr->tkwin) + slavePtr->padY
+ + slavePtr->iPadY + slavePtr->doubleBw;
+ if (slavePtr->numRows > 1) {
+ slavePtr->binNextPtr = layoutPtr[rightEdge].binNextPtr;
+ layoutPtr[rightEdge].binNextPtr = slavePtr;
+ } else if (rightEdge >= 0) {
+ int size = slavePtr->size + layoutPtr[rightEdge].pad;
+
+ if (size > layoutPtr[rightEdge].minSize) {
+ layoutPtr[rightEdge].minSize = size;
+ }
+ }
+ }
+ break;
+ }
+
+ /*
+ * Step 2b.
+ * Consider demands on uniform sizes.
+ */
+
+ uniformGroupPtr = uniformPre;
+ uniformGroupsAlloced = UNIFORM_PREALLOC;
+ uniformGroups = 0;
+
+ for (slot = 0; slot < gridCount; slot++) {
+ if (layoutPtr[slot].uniform != NULL) {
+ for (start = 0; start < uniformGroups; start++) {
+ if (uniformGroupPtr[start].group == layoutPtr[slot].uniform) {
+ break;
+ }
+ }
+ if (start >= uniformGroups) {
+ /*
+ * Have not seen that group before, set up data for it.
+ */
+
+ if (uniformGroups >= uniformGroupsAlloced) {
+ /*
+ * We need to allocate more space.
+ */
+
+ size_t oldSize = uniformGroupsAlloced
+ * sizeof(UniformGroup);
+ size_t newSize = (uniformGroupsAlloced + UNIFORM_PREALLOC)
+ * sizeof(UniformGroup);
+ UniformGroup *newUG = ckalloc(newSize);
+ UniformGroup *oldUG = uniformGroupPtr;
+
+ memcpy(newUG, oldUG, oldSize);
+ if (oldUG != uniformPre) {
+ ckfree(oldUG);
+ }
+ uniformGroupPtr = newUG;
+ uniformGroupsAlloced += UNIFORM_PREALLOC;
+ }
+ uniformGroups++;
+ uniformGroupPtr[start].group = layoutPtr[slot].uniform;
+ uniformGroupPtr[start].minSize = 0;
+ }
+ weight = layoutPtr[slot].weight;
+ weight = weight > 0 ? weight : 1;
+ minSize = (layoutPtr[slot].minSize + weight - 1) / weight;
+ if (minSize > uniformGroupPtr[start].minSize) {
+ uniformGroupPtr[start].minSize = minSize;
+ }
+ }
+ }
+
+ /*
+ * Data has been gathered about uniform groups. Now relayout accordingly.
+ */
+
+ if (uniformGroups > 0) {
+ for (slot = 0; slot < gridCount; slot++) {
+ if (layoutPtr[slot].uniform != NULL) {
+ for (start = 0; start < uniformGroups; start++) {
+ if (uniformGroupPtr[start].group ==
+ layoutPtr[slot].uniform) {
+ weight = layoutPtr[slot].weight;
+ weight = weight > 0 ? weight : 1;
+ layoutPtr[slot].minSize =
+ uniformGroupPtr[start].minSize * weight;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (uniformGroupPtr != uniformPre) {
+ ckfree(uniformGroupPtr);
+ }
+
+ /*
+ * Step 3.
+ * Determine the minimum slot offsets going from left to right that would
+ * fit all of the slaves. This determines the minimum
+ */
+
+ for (offset=0,slot=0; slot < gridCount; slot++) {
+ layoutPtr[slot].minOffset = layoutPtr[slot].minSize + offset;
+ for (slavePtr = layoutPtr[slot].binNextPtr; slavePtr != NULL;
+ slavePtr = slavePtr->binNextPtr) {
+ int span = (slotType == COLUMN) ?
+ slavePtr->numCols : slavePtr->numRows;
+ int required = slavePtr->size + layoutPtr[slot - span].minOffset;
+
+ if (required > layoutPtr[slot].minOffset) {
+ layoutPtr[slot].minOffset = required;
+ }
+ }
+ offset = layoutPtr[slot].minOffset;
+ }
+
+ /*
+ * At this point, we know the minimum required size of the entire layout.
+ * It might be prudent to stop here if our "master" will resize itself to
+ * this size.
+ */
+
+ requiredSize = offset;
+ if (maxOffset > offset) {
+ offset=maxOffset;
+ }
+
+ /*
+ * Step 4.
+ * Determine the minimum slot offsets going from right to left, bounding
+ * the pixel range of each slot boundary. Pre-fill all of the right
+ * offsets with the actual size of the table; they will be reduced as
+ * required.
+ */
+
+ for (slot=0; slot < gridCount; slot++) {
+ layoutPtr[slot].maxOffset = offset;
+ }
+ for (slot=gridCount-1; slot > 0;) {
+ for (slavePtr = layoutPtr[slot].binNextPtr; slavePtr != NULL;
+ slavePtr = slavePtr->binNextPtr) {
+ int span = (slotType == COLUMN) ?
+ slavePtr->numCols : slavePtr->numRows;
+ int require = offset - slavePtr->size;
+ int startSlot = slot - span;
+
+ if (startSlot >=0 && require < layoutPtr[startSlot].maxOffset) {
+ layoutPtr[startSlot].maxOffset = require;
+ }
+ }
+ offset -= layoutPtr[slot].minSize;
+ slot--;
+ if (layoutPtr[slot].maxOffset < offset) {
+ offset = layoutPtr[slot].maxOffset;
+ } else {
+ layoutPtr[slot].maxOffset = offset;
+ }
+ }
+
+ /*
+ * Step 5.
+ * At this point, each slot boundary has a range of values that will
+ * satisfy the overall layout size. Make repeated passes over the layout
+ * structure looking for spans of slot boundaries where the minOffsets are
+ * less than the maxOffsets, and adjust the offsets according to the slot
+ * weights. At each pass, at least one slot boundary will have its range
+ * of possible values fixed at a single value.
+ */
+
+ for (start = 0; start < gridCount;) {
+ int totalWeight = 0; /* Sum of the weights for all of the slots in
+ * this span. */
+ int need = 0; /* The minimum space needed to layout this
+ * span. */
+ int have; /* The actual amount of space that will be
+ * taken up by this span. */
+ int weight; /* Cumulative weights of the columns in this
+ * span. */
+ int noWeights = 0; /* True if the span has no weights. */
+
+ /*
+ * Find a span by identifying ranges of slots whose edges are already
+ * constrained at fixed offsets, but whose internal slot boundaries
+ * have a range of possible positions.
+ */
+
+ if (layoutPtr[start].minOffset == layoutPtr[start].maxOffset) {
+ start++;
+ continue;
+ }
+
+ for (end = start + 1; end < gridCount; end++) {
+ if (layoutPtr[end].minOffset == layoutPtr[end].maxOffset) {
+ break;
+ }
+ }
+
+ /*
+ * We found a span. Compute the total weight, minumum space required,
+ * for this span, and the actual amount of space the span should use.
+ */
+
+ for (slot = start; slot <= end; slot++) {
+ totalWeight += layoutPtr[slot].weight;
+ need += layoutPtr[slot].minSize;
+ }
+ have = layoutPtr[end].maxOffset - layoutPtr[start-1].minOffset;
+
+ /*
+ * If all the weights in the span are zero, then distribute the extra
+ * space evenly.
+ */
+
+ if (totalWeight == 0) {
+ noWeights++;
+ totalWeight = end - start + 1;
+ }
+
+ /*
+ * It might not be possible to give the span all of the space
+ * available on this pass without violating the size constraints of
+ * one or more of the internal slot boundaries. Try to determine the
+ * maximum amount of space that when added to the entire span, would
+ * cause a slot boundary to have its possible range reduced to one
+ * value, and reduce the amount of extra space allocated on this pass
+ * accordingly.
+ *
+ * The calculation is done cumulatively to avoid accumulating roundoff
+ * errors.
+ */
+
+ do {
+ int prevMinOffset = layoutPtr[start - 1].minOffset;
+
+ prevGrow = 0;
+ accWeight = 0;
+ for (slot = start; slot <= end; slot++) {
+ weight = noWeights ? 1 : layoutPtr[slot].weight;
+ accWeight += weight;
+ grow = (have - need) * accWeight / totalWeight - prevGrow;
+ prevGrow += grow;
+
+ if ((weight > 0) &&
+ ((prevMinOffset + layoutPtr[slot].minSize + grow)
+ > layoutPtr[slot].maxOffset)) {
+ int newHave;
+
+ /*
+ * There is not enough room to grow that much. Calculate
+ * how much this slot can grow and how much "have" that
+ * corresponds to.
+ */
+
+ grow = layoutPtr[slot].maxOffset -
+ layoutPtr[slot].minSize - prevMinOffset;
+ newHave = grow * totalWeight / weight;
+ if (newHave > totalWeight) {
+ /*
+ * By distributing multiples of totalWeight we
+ * minimize rounding errors since they will only
+ * happen in the last loop(s).
+ */
+
+ newHave = newHave / totalWeight * totalWeight;
+ }
+ if (newHave <= 0) {
+ /*
+ * We can end up with a "have" of 0 here if the
+ * previous slots have taken all the space. In that
+ * case we cannot guess an appropriate "have" so we
+ * just try some lower "have" that is >= 1, to make
+ * sure this terminates.
+ */
+
+ newHave = (have - need) - 1;
+ if (newHave > (3 * totalWeight)) {
+ /*
+ * Go down 25% for large values.
+ */
+ newHave = newHave * 3 / 4;
+ }
+
+ if (newHave > totalWeight) {
+ /*
+ * Round down to a multiple of totalWeight.
+ */
+ newHave = newHave / totalWeight * totalWeight;
+ }
+
+ if (newHave <= 0) {
+ newHave = 1;
+ }
+ }
+ have = newHave + need;
+
+ /*
+ * Restart loop to check if the new "have" will fit.
+ */
+
+ break;
+ }
+ prevMinOffset += layoutPtr[slot].minSize + grow;
+ if (prevMinOffset < layoutPtr[slot].minOffset) {
+ prevMinOffset = layoutPtr[slot].minOffset;
+ }
+ }
+
+ /*
+ * Quit the outer loop if the inner loop ran all the way.
+ */
+ } while (slot <= end);
+
+ /*
+ * Now distribute the extra space among the slots by adjusting the
+ * minSizes and minOffsets.
+ */
+
+ prevGrow = 0;
+ accWeight = 0;
+ for (slot = start; slot <= end; slot++) {
+ accWeight += noWeights ? 1 : layoutPtr[slot].weight;
+ grow = (have - need) * accWeight / totalWeight - prevGrow;
+ prevGrow += grow;
+ layoutPtr[slot].minSize += grow;
+ if ((layoutPtr[slot-1].minOffset + layoutPtr[slot].minSize)
+ > layoutPtr[slot].minOffset) {
+ layoutPtr[slot].minOffset = layoutPtr[slot-1].minOffset +
+ layoutPtr[slot].minSize;
+ }
+ }
+
+ /*
+ * Having pushed the top/left boundaries of the slots to take up extra
+ * space, the bottom/right space is recalculated to propagate the new
+ * space allocation.
+ */
+
+ for (slot = end; slot > start; slot--) {
+ /*
+ * maxOffset may not go up.
+ */
+
+ if ((layoutPtr[slot].maxOffset-layoutPtr[slot].minSize)
+ < layoutPtr[slot-1].maxOffset) {
+ layoutPtr[slot-1].maxOffset =
+ layoutPtr[slot].maxOffset-layoutPtr[slot].minSize;
+ }
+ }
+ }
+
+ /*
+ * Step 6.
+ * All of the space has been apportioned; copy the layout information back
+ * into the master.
+ */
+
+ for (slot=0; slot < gridCount; slot++) {
+ slotPtr[slot].offset = layoutPtr[slot].minOffset;
+ }
+
+ --layoutPtr;
+ if (layoutPtr != layoutData) {
+ ckfree(layoutPtr);
+ }
+ return requiredSize;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetGrid --
+ *
+ * This internal procedure is used to locate a Grid structure for a given
+ * window, creating one if one doesn't exist already.
+ *
+ * Results:
+ * The return value is a pointer to the Grid structure corresponding to
+ * tkwin.
+ *
+ * Side effects:
+ * A new grid structure may be created. If so, then a callback is set up
+ * to clean things up when the window is deleted.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Gridder *
+GetGrid(
+ Tk_Window tkwin) /* Token for window for which grid structure
+ * is desired. */
+{
+ register Gridder *gridPtr;
+ Tcl_HashEntry *hPtr;
+ int isNew;
+ TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr;
+
+ if (!dispPtr->gridInit) {
+ Tcl_InitHashTable(&dispPtr->gridHashTable, TCL_ONE_WORD_KEYS);
+ dispPtr->gridInit = 1;
+ }
+
+ /*
+ * See if there's already grid for this window. If not, then create a new
+ * one.
+ */
+
+ hPtr = Tcl_CreateHashEntry(&dispPtr->gridHashTable, (char*) tkwin, &isNew);
+ if (!isNew) {
+ return Tcl_GetHashValue(hPtr);
+ }
+ gridPtr = ckalloc(sizeof(Gridder));
+ gridPtr->tkwin = tkwin;
+ gridPtr->masterPtr = NULL;
+ gridPtr->masterDataPtr = NULL;
+ gridPtr->nextPtr = NULL;
+ gridPtr->slavePtr = NULL;
+ gridPtr->binNextPtr = NULL;
+
+ gridPtr->column = -1;
+ gridPtr->row = -1;
+ gridPtr->numCols = 1;
+ gridPtr->numRows = 1;
+
+ gridPtr->padX = 0;
+ gridPtr->padY = 0;
+ gridPtr->padLeft = 0;
+ gridPtr->padTop = 0;
+ gridPtr->iPadX = 0;
+ gridPtr->iPadY = 0;
+ gridPtr->doubleBw = 2 * Tk_Changes(tkwin)->border_width;
+ gridPtr->abortPtr = NULL;
+ gridPtr->flags = 0;
+ gridPtr->sticky = 0;
+ gridPtr->size = 0;
+ gridPtr->in = NULL;
+ gridPtr->masterDataPtr = NULL;
+ Tcl_SetHashValue(hPtr, gridPtr);
+ Tk_CreateEventHandler(tkwin, StructureNotifyMask,
+ GridStructureProc, gridPtr);
+ return gridPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SetGridSize --
+ *
+ * This internal procedure sets the size of the grid occupied by slaves.
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * The width and height arguments are filled in the master data
+ * structure. Additional space is allocated for the constraints to
+ * accomodate the offsets.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+SetGridSize(
+ Gridder *masterPtr) /* The geometry master for this grid. */
+{
+ register Gridder *slavePtr; /* Current slave window. */
+ int maxX = 0, maxY = 0;
+
+ for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
+ slavePtr = slavePtr->nextPtr) {
+ maxX = MAX(maxX, slavePtr->numCols + slavePtr->column);
+ maxY = MAX(maxY, slavePtr->numRows + slavePtr->row);
+ }
+ masterPtr->masterDataPtr->columnEnd = maxX;
+ masterPtr->masterDataPtr->rowEnd = maxY;
+ CheckSlotData(masterPtr, maxX, COLUMN, CHECK_SPACE);
+ CheckSlotData(masterPtr, maxY, ROW, CHECK_SPACE);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SetSlaveColumn --
+ *
+ * Update column data for a slave, checking that MAX_ELEMENT bound
+ * is not passed.
+ *
+ * Results:
+ * TCL_ERROR if out of bounds, TCL_OK otherwise
+ *
+ * Side effects:
+ * Slave fields are updated.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+SetSlaveColumn(
+ Tcl_Interp *interp, /* Interp for error message. */
+ Gridder *slavePtr, /* Slave to be updated. */
+ int column, /* New column or -1 to be unchanged. */
+ int numCols) /* New columnspan or -1 to be unchanged. */
+{
+ int newColumn, newNumCols, lastCol;
+
+ newColumn = (column >= 0) ? column : slavePtr->column;
+ newNumCols = (numCols >= 1) ? numCols : slavePtr->numCols;
+
+ lastCol = ((newColumn >= 0) ? newColumn : 0) + newNumCols;
+ if (lastCol >= MAX_ELEMENT) {
+ Tcl_SetObjResult(interp, Tcl_NewStringObj("column out of bounds",-1));
+ Tcl_SetErrorCode(interp, "TK", "GRID", "BAD_COLUMN", NULL);
+ return TCL_ERROR;
+ }
+
+ slavePtr->column = newColumn;
+ slavePtr->numCols = newNumCols;
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SetSlaveRow --
+ *
+ * Update row data for a slave, checking that MAX_ELEMENT bound
+ * is not passed.
+ *
+ * Results:
+ * TCL_ERROR if out of bounds, TCL_OK otherwise
+ *
+ * Side effects:
+ * Slave fields are updated.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+SetSlaveRow(
+ Tcl_Interp *interp, /* Interp for error message. */
+ Gridder *slavePtr, /* Slave to be updated. */
+ int row, /* New row or -1 to be unchanged. */
+ int numRows) /* New rowspan or -1 to be unchanged. */
+{
+ int newRow, newNumRows, lastRow;
+
+ newRow = (row >= 0) ? row : slavePtr->row;
+ newNumRows = (numRows >= 1) ? numRows : slavePtr->numRows;
+
+ lastRow = ((newRow >= 0) ? newRow : 0) + newNumRows;
+ if (lastRow >= MAX_ELEMENT) {
+ Tcl_SetObjResult(interp, Tcl_NewStringObj("row out of bounds", -1));
+ Tcl_SetErrorCode(interp, "TK", "GRID", "BAD_ROW", NULL);
+ return TCL_ERROR;
+ }
+
+ slavePtr->row = newRow;
+ slavePtr->numRows = newNumRows;
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CheckSlotData --
+ *
+ * This internal procedure is used to manage the storage for row and
+ * column (slot) constraints.
+ *
+ * Results:
+ * TRUE if the index is OK, False otherwise.
+ *
+ * Side effects:
+ * A new master grid structure may be created. If so, then it is
+ * initialized. In addition, additional storage for a row or column
+ * constraints may be allocated, and the constraint maximums are
+ * adjusted.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+CheckSlotData(
+ Gridder *masterPtr, /* The geometry master for this grid. */
+ int slot, /* Which slot to look at. */
+ int slotType, /* ROW or COLUMN. */
+ int checkOnly) /* Don't allocate new space if true. */
+{
+ int numSlot; /* Number of slots already allocated (Space) */
+ int end; /* Last used constraint. */
+
+ /*
+ * If slot is out of bounds, return immediately.
+ */
+
+ if (slot < 0 || slot >= MAX_ELEMENT) {
+ return TCL_ERROR;
+ }
+
+ if ((checkOnly == CHECK_ONLY) && (masterPtr->masterDataPtr == NULL)) {
+ return TCL_ERROR;
+ }
+
+ /*
+ * If we need to allocate more space, allocate a little extra to avoid
+ * repeated re-alloc's for large tables. We need enough space to hold all
+ * of the offsets as well.
+ */
+
+ InitMasterData(masterPtr);
+ end = (slotType == ROW) ? masterPtr->masterDataPtr->rowMax :
+ masterPtr->masterDataPtr->columnMax;
+ if (checkOnly == CHECK_ONLY) {
+ return ((end < slot) ? TCL_ERROR : TCL_OK);
+ } else {
+ numSlot = (slotType == ROW) ? masterPtr->masterDataPtr->rowSpace
+ : masterPtr->masterDataPtr->columnSpace;
+ if (slot >= numSlot) {
+ int newNumSlot = slot + PREALLOC;
+ size_t oldSize = numSlot * sizeof(SlotInfo);
+ size_t newSize = newNumSlot * sizeof(SlotInfo);
+ SlotInfo *newSI = ckalloc(newSize);
+ SlotInfo *oldSI = (slotType == ROW)
+ ? masterPtr->masterDataPtr->rowPtr
+ : masterPtr->masterDataPtr->columnPtr;
+
+ memcpy(newSI, oldSI, oldSize);
+ memset(newSI+numSlot, 0, newSize - oldSize);
+ ckfree(oldSI);
+ if (slotType == ROW) {
+ masterPtr->masterDataPtr->rowPtr = newSI;
+ masterPtr->masterDataPtr->rowSpace = newNumSlot;
+ } else {
+ masterPtr->masterDataPtr->columnPtr = newSI;
+ masterPtr->masterDataPtr->columnSpace = newNumSlot;
+ }
+ }
+ if (slot >= end && checkOnly != CHECK_SPACE) {
+ if (slotType == ROW) {
+ masterPtr->masterDataPtr->rowMax = slot+1;
+ } else {
+ masterPtr->masterDataPtr->columnMax = slot+1;
+ }
+ }
+ return TCL_OK;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * InitMasterData --
+ *
+ * This internal procedure is used to allocate and initialize the data
+ * for a geometry master, if the data doesn't exist already.
+ *
+ * Results:
+ * none
+ *
+ * Side effects:
+ * A new master grid structure may be created. If so, then it is
+ * initialized.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+InitMasterData(
+ Gridder *masterPtr)
+{
+ if (masterPtr->masterDataPtr == NULL) {
+ GridMaster *gridPtr = masterPtr->masterDataPtr =
+ ckalloc(sizeof(GridMaster));
+ size_t size = sizeof(SlotInfo) * TYPICAL_SIZE;
+
+ gridPtr->columnEnd = 0;
+ gridPtr->columnMax = 0;
+ gridPtr->columnPtr = ckalloc(size);
+ gridPtr->columnSpace = TYPICAL_SIZE;
+ gridPtr->rowEnd = 0;
+ gridPtr->rowMax = 0;
+ gridPtr->rowPtr = ckalloc(size);
+ gridPtr->rowSpace = TYPICAL_SIZE;
+ gridPtr->startX = 0;
+ gridPtr->startY = 0;
+ gridPtr->anchor = GRID_DEFAULT_ANCHOR;
+
+ memset(gridPtr->columnPtr, 0, size);
+ memset(gridPtr->rowPtr, 0, size);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Unlink --
+ *
+ * Remove a grid from its master's list of slaves.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The master will be scheduled for re-arranging, and the size of the
+ * grid will be adjusted accordingly
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+Unlink(
+ register Gridder *slavePtr) /* Window to unlink. */
+{
+ register Gridder *masterPtr, *slavePtr2;
+
+ masterPtr = slavePtr->masterPtr;
+ if (masterPtr == NULL) {
+ return;
+ }
+
+ if (masterPtr->slavePtr == slavePtr) {
+ masterPtr->slavePtr = slavePtr->nextPtr;
+ } else {
+ for (slavePtr2=masterPtr->slavePtr ; ; slavePtr2=slavePtr2->nextPtr) {
+ if (slavePtr2 == NULL) {
+ Tcl_Panic("Unlink couldn't find previous window");
+ }
+ if (slavePtr2->nextPtr == slavePtr) {
+ slavePtr2->nextPtr = slavePtr->nextPtr;
+ break;
+ }
+ }
+ }
+ if (!(masterPtr->flags & REQUESTED_RELAYOUT)) {
+ masterPtr->flags |= REQUESTED_RELAYOUT;
+ Tcl_DoWhenIdle(ArrangeGrid, masterPtr);
+ }
+ if (masterPtr->abortPtr != NULL) {
+ *masterPtr->abortPtr = 1;
+ }
+
+ SetGridSize(slavePtr->masterPtr);
+ slavePtr->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, "grid");
+ masterPtr->flags &= ~ALLOCED_MASTER;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DestroyGrid --
+ *
+ * This procedure is invoked by Tcl_EventuallyFree or Tcl_Release to
+ * clean up the internal structure of a grid at a safe time (when no-one
+ * is using it anymore). Cleaning up the grid involves freeing the main
+ * structure for all windows and the master structure for geometry
+ * managers.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Everything associated with the grid is freed up.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DestroyGrid(
+ void *memPtr) /* Info about window that is now dead. */
+{
+ register Gridder *gridPtr = memPtr;
+
+ if (gridPtr->masterDataPtr != NULL) {
+ if (gridPtr->masterDataPtr->rowPtr != NULL) {
+ ckfree(gridPtr->masterDataPtr -> rowPtr);
+ }
+ if (gridPtr->masterDataPtr->columnPtr != NULL) {
+ ckfree(gridPtr->masterDataPtr -> columnPtr);
+ }
+ ckfree(gridPtr->masterDataPtr);
+ }
+ if (gridPtr->in != NULL) {
+ Tcl_DecrRefCount(gridPtr->in);
+ }
+ ckfree(gridPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GridStructureProc --
+ *
+ * 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 grid-related
+ * information. If it was just resized, re-configure its slaves, if any.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+GridStructureProc(
+ ClientData clientData, /* Our information about window referred to by
+ * eventPtr. */
+ XEvent *eventPtr) /* Describes what just happened. */
+{
+ register Gridder *gridPtr = clientData;
+ TkDisplay *dispPtr = ((TkWindow *) gridPtr->tkwin)->dispPtr;
+
+ if (eventPtr->type == ConfigureNotify) {
+ if ((gridPtr->slavePtr != NULL)
+ && !(gridPtr->flags & REQUESTED_RELAYOUT)) {
+ gridPtr->flags |= REQUESTED_RELAYOUT;
+ Tcl_DoWhenIdle(ArrangeGrid, gridPtr);
+ }
+ if ((gridPtr->masterPtr != NULL) &&
+ (gridPtr->doubleBw != 2*Tk_Changes(gridPtr->tkwin)->border_width)) {
+ if (!(gridPtr->masterPtr->flags & REQUESTED_RELAYOUT)) {
+ gridPtr->doubleBw = 2*Tk_Changes(gridPtr->tkwin)->border_width;
+ gridPtr->masterPtr->flags |= REQUESTED_RELAYOUT;
+ Tcl_DoWhenIdle(ArrangeGrid, gridPtr->masterPtr);
+ }
+ }
+ } else if (eventPtr->type == DestroyNotify) {
+ register Gridder *gridPtr2, *nextPtr;
+
+ if (gridPtr->masterPtr != NULL) {
+ Unlink(gridPtr);
+ }
+ for (gridPtr2 = gridPtr->slavePtr; gridPtr2 != NULL;
+ gridPtr2 = nextPtr) {
+ Tk_UnmapWindow(gridPtr2->tkwin);
+ gridPtr2->masterPtr = NULL;
+ nextPtr = gridPtr2->nextPtr;
+ gridPtr2->nextPtr = NULL;
+ }
+ Tcl_DeleteHashEntry(Tcl_FindHashEntry(&dispPtr->gridHashTable,
+ (char *) gridPtr->tkwin));
+ if (gridPtr->flags & REQUESTED_RELAYOUT) {
+ Tcl_CancelIdleCall(ArrangeGrid, gridPtr);
+ }
+ gridPtr->tkwin = NULL;
+ Tcl_EventuallyFree(gridPtr, (Tcl_FreeProc *)DestroyGrid);
+ } else if (eventPtr->type == MapNotify) {
+ if ((gridPtr->slavePtr != NULL)
+ && !(gridPtr->flags & REQUESTED_RELAYOUT)) {
+ gridPtr->flags |= REQUESTED_RELAYOUT;
+ Tcl_DoWhenIdle(ArrangeGrid, gridPtr);
+ }
+ } else if (eventPtr->type == UnmapNotify) {
+ register Gridder *gridPtr2;
+
+ for (gridPtr2 = gridPtr->slavePtr; gridPtr2 != NULL;
+ gridPtr2 = gridPtr2->nextPtr) {
+ Tk_UnmapWindow(gridPtr2->tkwin);
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureSlaves --
+ *
+ * This implements the guts of the "grid configure" command. Given a list
+ * of slaves and configuration options, it arranges for the grid to
+ * manage the slaves and sets the specified options. Arguments consist
+ * of windows or window shortcuts followed by "-option value" pairs.
+ *
+ * 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 grid.
+ *
+ *----------------------------------------------------------------------
+ */
+
+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. */
+{
+ Gridder *masterPtr = NULL;
+ Gridder *slavePtr;
+ Tk_Window other, slave, parent, ancestor;
+ int i, j, tmp;
+ int numWindows;
+ int width;
+ int defaultRow = -1;
+ int defaultColumn = 0; /* Default column number */
+ int defaultColumnSpan = 1; /* Default number of columns */
+ const char *lastWindow; /* Use this window to base current row/col
+ * on */
+ int numSkip; /* Number of 'x' found */
+ static const char *const optionStrings[] = {
+ "-column", "-columnspan", "-in", "-ipadx", "-ipady",
+ "-padx", "-pady", "-row", "-rowspan", "-sticky", NULL
+ };
+ enum options {
+ CONF_COLUMN, CONF_COLUMNSPAN, CONF_IN, CONF_IPADX, CONF_IPADY,
+ CONF_PADX, CONF_PADY, CONF_ROW, CONF_ROWSPAN, CONF_STICKY };
+ int index;
+ const char *string;
+ char firstChar;
+ int positionGiven;
+
+ /*
+ * Count the number of windows, or window short-cuts.
+ */
+
+ firstChar = 0;
+ for (numWindows=0, i=0; i < objc; i++) {
+ int length;
+ char prevChar = firstChar;
+
+ string = Tcl_GetStringFromObj(objv[i], &length);
+ firstChar = string[0];
+
+ if (firstChar == '.') {
+ /*
+ * Check that windows are valid, and locate the first slave's
+ * parent window (default for -in).
+ */
+
+ if (TkGetWindowFromObj(interp, tkwin, objv[i], &slave) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (masterPtr == NULL) {
+ /*
+ * Is there any saved -in from a removed slave?
+ * If there is, it becomes default for -in.
+ * If the stored master does not exist, just ignore it.
+ */
+
+ struct Gridder *slavePtr = GetGrid(slave);
+ if (slavePtr->in != NULL) {
+ if (TkGetWindowFromObj(interp, slave, slavePtr->in, &parent)
+ == TCL_OK) {
+ masterPtr = GetGrid(parent);
+ InitMasterData(masterPtr);
+ }
+ }
+ }
+ if (masterPtr == NULL) {
+ parent = Tk_Parent(slave);
+ if (parent != NULL) {
+ masterPtr = GetGrid(parent);
+ InitMasterData(masterPtr);
+ }
+ }
+ numWindows++;
+ continue;
+ }
+ if (length > 1 && i == 0) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "bad argument \"%s\": must be name of window", string));
+ Tcl_SetErrorCode(interp, "TK", "GRID", "BAD_PARAMETER", NULL);
+ return TCL_ERROR;
+ }
+ if (length > 1 && firstChar == '-') {
+ break;
+ }
+ if (length > 1) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "unexpected parameter \"%s\" in configure list:"
+ " should be window name or option", string));
+ Tcl_SetErrorCode(interp, "TK", "GRID", "BAD_PARAMETER", NULL);
+ return TCL_ERROR;
+ }
+
+ if ((firstChar == REL_HORIZ) && ((numWindows == 0) ||
+ (prevChar == REL_SKIP) || (prevChar == REL_VERT))) {
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(
+ "must specify window before shortcut '-'", -1));
+ Tcl_SetErrorCode(interp, "TK", "GRID", "SHORTCUT_USAGE", NULL);
+ return TCL_ERROR;
+ }
+
+ if ((firstChar == REL_VERT) || (firstChar == REL_SKIP)
+ || (firstChar == REL_HORIZ)) {
+ continue;
+ }
+
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "invalid window shortcut, \"%s\" should be '-', 'x', or '^'",
+ string));
+ Tcl_SetErrorCode(interp, "TK", "GRID", "SHORTCUT_USAGE", NULL);
+ return TCL_ERROR;
+ }
+ numWindows = i;
+
+ if ((objc - numWindows) & 1) {
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(
+ "extra option or option with no value", -1));
+ Tcl_SetErrorCode(interp, "TK", "GRID", "BAD_PARAMETER", NULL);
+ return TCL_ERROR;
+ }
+
+ /*
+ * Go through all options looking for -in and -row, which are needed to be
+ * found first to handle the special case where ^ is used on a row without
+ * windows names, but with an -in option. Since all options are checked
+ * here, we do not need to handle the error case again later.
+ */
+
+ for (i = numWindows; i < objc; i += 2) {
+ if (Tcl_GetIndexFromObjStruct(interp, objv[i], optionStrings,
+ sizeof(char *), "option", 0, &index) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (index == CONF_IN) {
+ if (TkGetWindowFromObj(interp, tkwin, objv[i+1], &other) !=
+ TCL_OK) {
+ return TCL_ERROR;
+ }
+ masterPtr = GetGrid(other);
+ InitMasterData(masterPtr);
+ } else if (index == CONF_ROW) {
+ if (Tcl_GetIntFromObj(interp, objv[i+1], &tmp) != TCL_OK
+ || tmp < 0) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "bad row value \"%s\": must be a non-negative integer",
+ Tcl_GetString(objv[i+1])));
+ Tcl_SetErrorCode(interp, "TK", "VALUE", "POSITIVE_INT", NULL);
+ return TCL_ERROR;
+ }
+ defaultRow = tmp;
+ }
+ }
+
+ /*
+ * If no -row is given, use the first unoccupied row of the master.
+ */
+
+ if (defaultRow < 0) {
+ if (masterPtr != NULL && masterPtr->masterDataPtr != NULL) {
+ SetGridSize(masterPtr);
+ defaultRow = masterPtr->masterDataPtr->rowEnd;
+ } else {
+ defaultRow = 0;
+ }
+ }
+
+ /*
+ * Iterate over all of the slave windows and short-cuts, parsing 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 managed we
+ * want to just change a few existing values without resetting everything.
+ * If there are multiple windows, the -in option only gets processed for
+ * the first window.
+ */
+
+ positionGiven = 0;
+ for (j = 0; j < numWindows; j++) {
+ string = Tcl_GetString(objv[j]);
+ firstChar = string[0];
+
+ /*
+ * '^' and 'x' cause us to skip a column. '-' is processed as part of
+ * its preceeding slave.
+ */
+
+ if ((firstChar == REL_VERT) || (firstChar == REL_SKIP)) {
+ defaultColumn++;
+ continue;
+ }
+ if (firstChar == REL_HORIZ) {
+ continue;
+ }
+
+ for (defaultColumnSpan = 1; j + defaultColumnSpan < numWindows;
+ defaultColumnSpan++) {
+ const char *string = Tcl_GetString(objv[j + defaultColumnSpan]);
+
+ if (*string != REL_HORIZ) {
+ break;
+ }
+ }
+
+ if (TkGetWindowFromObj(interp, tkwin, objv[j], &slave) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ if (Tk_TopWinHierarchy(slave)) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "can't manage \"%s\": it's a top-level window",
+ Tcl_GetString(objv[j])));
+ Tcl_SetErrorCode(interp, "TK", "GEOMETRY", "TOPLEVEL", NULL);
+ return TCL_ERROR;
+ }
+ slavePtr = GetGrid(slave);
+
+ /*
+ * The following statement is taken from tkPack.c:
+ *
+ * "If the slave isn't currently managed, reset all of its
+ * configuration information to default values (there could be old
+ * values left from a previous packer)."
+ *
+ * I [D.S.] disagree with this statement. If a slave is disabled
+ * (using "forget") and then re-enabled, I submit that 90% of the time
+ * the programmer will want it to retain its old configuration
+ * information. If the programmer doesn't want this behavior, then the
+ * defaults can be reestablished by hand, without having to worry
+ * about keeping track of the old state.
+ */
+
+ for (i = numWindows; i < objc; i += 2) {
+ Tcl_GetIndexFromObjStruct(interp, objv[i], optionStrings,
+ sizeof(char *), "option", 0, &index);
+ switch ((enum options) index) {
+ case CONF_COLUMN:
+ if (Tcl_GetIntFromObj(NULL, objv[i+1], &tmp) != TCL_OK
+ || tmp < 0) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "bad column value \"%s\": must be a non-negative integer",
+ Tcl_GetString(objv[i+1])));
+ Tcl_SetErrorCode(interp, "TK", "VALUE", "COLUMN", NULL);
+ return TCL_ERROR;
+ }
+ if (SetSlaveColumn(interp, slavePtr, tmp, -1) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ break;
+ case CONF_COLUMNSPAN:
+ if (Tcl_GetIntFromObj(NULL, objv[i+1], &tmp) != TCL_OK
+ || tmp <= 0) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "bad columnspan value \"%s\": must be a positive integer",
+ Tcl_GetString(objv[i+1])));
+ Tcl_SetErrorCode(interp, "TK", "VALUE", "SPAN", NULL);
+ return TCL_ERROR;
+ }
+ if (SetSlaveColumn(interp, slavePtr, -1, tmp) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ break;
+ case CONF_IN:
+ if (TkGetWindowFromObj(interp, tkwin, objv[i+1],
+ &other) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (other == slave) {
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(
+ "window can't be managed in itself", -1));
+ Tcl_SetErrorCode(interp, "TK", "GEOMETRY", "SELF", NULL);
+ return TCL_ERROR;
+ }
+ positionGiven = 1;
+ masterPtr = GetGrid(other);
+ InitMasterData(masterPtr);
+ break;
+ case CONF_STICKY: {
+ int sticky = StringToSticky(Tcl_GetString(objv[i+1]));
+
+ if (sticky == -1) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "bad stickyness value \"%s\": must be"
+ " a string containing n, e, s, and/or w",
+ Tcl_GetString(objv[i+1])));
+ Tcl_SetErrorCode(interp, "TK", "VALUE", "STICKY", NULL);
+ return TCL_ERROR;
+ }
+ slavePtr->sticky = sticky;
+ break;
+ }
+ case CONF_IPADX:
+ if ((Tk_GetPixelsFromObj(NULL, 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(NULL, 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, tkwin, objv[i+1],
+ &slavePtr->padLeft, &slavePtr->padX) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ break;
+ case CONF_PADY:
+ if (TkParsePadAmount(interp, tkwin, objv[i+1],
+ &slavePtr->padTop, &slavePtr->padY) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ break;
+ case CONF_ROW:
+ if (Tcl_GetIntFromObj(NULL, objv[i+1], &tmp) != TCL_OK
+ || tmp < 0) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "bad row value \"%s\": must be a non-negative integer",
+ Tcl_GetString(objv[i+1])));
+ Tcl_SetErrorCode(interp, "TK", "VALUE", "COLUMN", NULL);
+ return TCL_ERROR;
+ }
+ if (SetSlaveRow(interp, slavePtr, tmp, -1) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ break;
+ case CONF_ROWSPAN:
+ if ((Tcl_GetIntFromObj(NULL, objv[i+1], &tmp) != TCL_OK)
+ || tmp <= 0) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "bad rowspan value \"%s\": must be a positive integer",
+ Tcl_GetString(objv[i+1])));
+ Tcl_SetErrorCode(interp, "TK", "VALUE", "SPAN", NULL);
+ return TCL_ERROR;
+ }
+ if (SetSlaveRow(interp, slavePtr, -1, tmp) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ break;
+ }
+ }
+
+ /*
+ * If no position was specified via -in and the slave is already
+ * packed, then leave it in its current location.
+ */
+
+ if (!positionGiven && (slavePtr->masterPtr != NULL)) {
+ masterPtr = slavePtr->masterPtr;
+ goto scheduleLayout;
+ }
+
+ /*
+ * If the same -in window is passed in again, then just leave it in
+ * its current location.
+ */
+
+ if (positionGiven && (masterPtr == slavePtr->masterPtr)) {
+ goto scheduleLayout;
+ }
+
+ /*
+ * Make sure we have a geometry master. We look at:
+ * 1) the -in flag
+ * 2) the parent of the first slave.
+ */
+
+ parent = Tk_Parent(slave);
+ if (masterPtr == NULL) {
+ masterPtr = GetGrid(parent);
+ InitMasterData(masterPtr);
+ }
+
+ if (slavePtr->masterPtr != NULL && slavePtr->masterPtr != masterPtr) {
+ Unlink(slavePtr);
+ slavePtr->masterPtr = NULL;
+ }
+
+ if (slavePtr->masterPtr == NULL) {
+ Gridder *tempPtr = masterPtr->slavePtr;
+
+ slavePtr->masterPtr = masterPtr;
+ masterPtr->slavePtr = slavePtr;
+ slavePtr->nextPtr = tempPtr;
+ }
+
+ /*
+ * 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.
+ */
+
+ for (ancestor = masterPtr->tkwin; ; ancestor = Tk_Parent(ancestor)) {
+ if (ancestor == parent) {
+ break;
+ }
+ if (Tk_TopWinHierarchy(ancestor)) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "can't put %s inside %s", Tcl_GetString(objv[j]),
+ Tk_PathName(masterPtr->tkwin)));
+ Tcl_SetErrorCode(interp, "TK", "GEOMETRY", "HIERARCHY", NULL);
+ Unlink(slavePtr);
+ return TCL_ERROR;
+ }
+ }
+
+ /*
+ * Try to make sure our master isn't managed by us.
+ */
+
+ if (masterPtr->masterPtr == slavePtr) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "can't put %s inside %s, would cause management loop",
+ Tcl_GetString(objv[j]), Tk_PathName(masterPtr->tkwin)));
+ Tcl_SetErrorCode(interp, "TK", "GEOMETRY", "LOOP", NULL);
+ Unlink(slavePtr);
+ return TCL_ERROR;
+ }
+
+ Tk_ManageGeometry(slave, &gridMgrType, slavePtr);
+
+ if (!(masterPtr->flags & DONT_PROPAGATE)) {
+ if (TkSetGeometryMaster(interp, masterPtr->tkwin, "grid")
+ != TCL_OK) {
+ Tk_ManageGeometry(slave, NULL, NULL);
+ Unlink(slavePtr);
+ return TCL_ERROR;
+ }
+ masterPtr->flags |= ALLOCED_MASTER;
+ }
+
+ /*
+ * Assign default position information.
+ */
+
+ if (slavePtr->column == -1) {
+ if (SetSlaveColumn(interp, slavePtr, defaultColumn,-1) != TCL_OK){
+ return TCL_ERROR;
+ }
+ }
+ if (SetSlaveColumn(interp, slavePtr, -1,
+ slavePtr->numCols + defaultColumnSpan - 1) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (slavePtr->row == -1) {
+ if (SetSlaveRow(interp, slavePtr, defaultRow, -1) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+ defaultColumn += slavePtr->numCols;
+ defaultColumnSpan = 1;
+
+ /*
+ * Arrange for the master to be re-arranged at the first idle moment.
+ */
+
+ scheduleLayout:
+ if (masterPtr->abortPtr != NULL) {
+ *masterPtr->abortPtr = 1;
+ }
+ if (!(masterPtr->flags & REQUESTED_RELAYOUT)) {
+ masterPtr->flags |= REQUESTED_RELAYOUT;
+ Tcl_DoWhenIdle(ArrangeGrid, masterPtr);
+ }
+ }
+
+ /*
+ * Now look for all the "^"'s.
+ */
+
+ lastWindow = NULL;
+ numSkip = 0;
+ for (j = 0; j < numWindows; j++) {
+ struct Gridder *otherPtr;
+ int match; /* Found a match for the ^ */
+ int lastRow, lastColumn; /* Implied end of table. */
+
+ string = Tcl_GetString(objv[j]);
+ firstChar = string[0];
+
+ if (firstChar == '.') {
+ lastWindow = string;
+ numSkip = 0;
+ }
+ if (firstChar == REL_SKIP) {
+ numSkip++;
+ }
+ if (firstChar != REL_VERT) {
+ continue;
+ }
+
+ if (masterPtr == NULL) {
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(
+ "can't use '^', cant find master", -1));
+ Tcl_SetErrorCode(interp, "TK", "GRID", "SHORTCUT_USAGE", NULL);
+ return TCL_ERROR;
+ }
+
+ /*
+ * Count the number of consecutive ^'s starting from this position.
+ */
+
+ for (width = 1; width + j < numWindows; width++) {
+ const char *string = Tcl_GetString(objv[j+width]);
+
+ if (*string != REL_VERT) {
+ break;
+ }
+ }
+
+ /*
+ * Find the implied grid location of the ^
+ */
+
+ if (lastWindow == NULL) {
+ lastRow = defaultRow - 1;
+ lastColumn = 0;
+ } else {
+ other = Tk_NameToWindow(interp, lastWindow, tkwin);
+ otherPtr = GetGrid(other);
+ lastRow = otherPtr->row + otherPtr->numRows - 2;
+ lastColumn = otherPtr->column + otherPtr->numCols;
+ }
+
+ lastColumn += numSkip;
+
+ match = 0;
+ for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
+ slavePtr = slavePtr->nextPtr) {
+
+ if (slavePtr->column == lastColumn
+ && slavePtr->row + slavePtr->numRows - 1 == lastRow) {
+ if (slavePtr->numCols <= width) {
+ if (SetSlaveRow(interp, slavePtr, -1,
+ slavePtr->numRows + 1) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ match++;
+ j += slavePtr->numCols - 1;
+ lastWindow = Tk_PathName(slavePtr->tkwin);
+ numSkip = 0;
+ break;
+ }
+ }
+ }
+ if (!match) {
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(
+ "can't find slave to extend with \"^\"", -1));
+ Tcl_SetErrorCode(interp, "TK", "GRID", "SHORTCUT_USAGE", NULL);
+ return TCL_ERROR;
+ }
+ }
+
+ if (masterPtr == NULL) {
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(
+ "can't determine master window", -1));
+ Tcl_SetErrorCode(interp, "TK", "GRID", "SHORTCUT_USAGE", NULL);
+ return TCL_ERROR;
+ }
+ SetGridSize(masterPtr);
+
+ /*
+ * 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, "grid");
+ masterPtr->flags &= ~ALLOCED_MASTER;
+ }
+
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StickyToObj
+ *
+ * Converts the internal boolean combination of "sticky" bits onto a Tcl
+ * list element containing zero or more of n, s, e, or w.
+ *
+ * Results:
+ * A new object is returned that holds the sticky representation.
+ *
+ * Side effects:
+ * none.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Tcl_Obj *
+StickyToObj(
+ int flags) /* The sticky flags. */
+{
+ int count = 0;
+ char buffer[4];
+
+ if (flags & STICK_NORTH) {
+ buffer[count++] = 'n';
+ }
+ if (flags & STICK_EAST) {
+ buffer[count++] = 'e';
+ }
+ if (flags & STICK_SOUTH) {
+ buffer[count++] = 's';
+ }
+ if (flags & STICK_WEST) {
+ buffer[count++] = 'w';
+ }
+ return Tcl_NewStringObj(buffer, count);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringToSticky --
+ *
+ * Converts an ascii string representing a widgets stickyness into the
+ * boolean result.
+ *
+ * Results:
+ * The boolean combination of the "sticky" bits is retuned. If an error
+ * occurs, such as an invalid character, -1 is returned instead.
+ *
+ * Side effects:
+ * none
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+StringToSticky(
+ const char *string)
+{
+ int sticky = 0;
+ char c;
+
+ while ((c = *string++) != '\0') {
+ switch (c) {
+ case 'n': case 'N':
+ sticky |= STICK_NORTH;
+ break;
+ case 'e': case 'E':
+ sticky |= STICK_EAST;
+ break;
+ case 's': case 'S':
+ sticky |= STICK_SOUTH;
+ break;
+ case 'w': case 'W':
+ sticky |= STICK_WEST;
+ break;
+ case ' ': case ',': case '\t': case '\r': case '\n':
+ break;
+ default:
+ return -1;
+ }
+ }
+ return sticky;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NewPairObj --
+ *
+ * Creates a new list object and fills it with two integer objects.
+ *
+ * Results:
+ * The newly created list object is returned.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Tcl_Obj *
+NewPairObj(
+ int val1, int val2)
+{
+ Tcl_Obj *ary[2];
+
+ ary[0] = Tcl_NewIntObj(val1);
+ ary[1] = Tcl_NewIntObj(val2);
+ return Tcl_NewListObj(2, ary);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NewQuadObj --
+ *
+ * Creates a new list object and fills it with four integer objects.
+ *
+ * Results:
+ * The newly created list object is returned.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Tcl_Obj *
+NewQuadObj(
+ int val1, int val2, int val3, int val4)
+{
+ Tcl_Obj *ary[4];
+
+ ary[0] = Tcl_NewIntObj(val1);
+ ary[1] = Tcl_NewIntObj(val2);
+ ary[2] = Tcl_NewIntObj(val3);
+ ary[3] = Tcl_NewIntObj(val4);
+ return Tcl_NewListObj(4, ary);
+}
+
+/*
+ * Local Variables:
+ * mode: c
+ * c-basic-offset: 4
+ * fill-column: 78
+ * End:
+ */