summaryrefslogtreecommitdiffstats
path: root/generic/tkGrid.c
diff options
context:
space:
mode:
Diffstat (limited to 'generic/tkGrid.c')
-rw-r--r--generic/tkGrid.c2615
1 files changed, 2615 insertions, 0 deletions
diff --git a/generic/tkGrid.c b/generic/tkGrid.c
new file mode 100644
index 0000000..ea11a01
--- /dev/null
+++ b/generic/tkGrid.c
@@ -0,0 +1,2615 @@
+/*
+ * 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.
+ *
+ * SCCS: @(#) tkGrid.c 1.39 97/10/10 10:12:03
+ */
+
+#include "tkInt.h"
+
+/*
+ * Convenience Macros
+ */
+
+#ifdef MAX
+# undef MAX
+#endif
+#define MAX(x,y) ((x) > (y) ? (x) : (y))
+#ifdef MIN
+# undef MIN
+#endif
+#define MIN(x,y) ((x) > (y) ? (y) : (x))
+
+#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 */
+
+/*
+ * 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. */
+
+/*
+ * 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. */
+ 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. */
+ int minOffset; /* The minimum offset, in pixels, from
+ * the beginning of the layout to the
+ * right/bottom 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
+ * right-or-bottom edge of the slot calculated
+ * from bottom-or-right to top-or-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
+ * parent. */
+ int startY; /* Pixel offset of this layout within its
+ * parent. */
+} 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
+ * parent. 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. */
+ 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 (half of this space is left on each
+ * side). This is space *outside* the window:
+ * we'll allocate extra space in frame but
+ * won't enlarge window). */
+ int iPadX, iPadY; /* Total extra pixels to allocate inside the
+ * window (half this amount will appear on
+ * each side). */
+ int 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 parent. */
+ 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
+
+/*
+ * 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.
+ */
+
+#define REQUESTED_RELAYOUT 1
+#define DONT_PROPAGATE 2
+
+/*
+ * Hash table used to map from Tk_Window tokens to corresponding
+ * Grid structures:
+ */
+
+static Tcl_HashTable gridHashTable;
+static int initialized = 0;
+
+/*
+ * Prototypes for procedures used only in this file:
+ */
+
+static void AdjustForSticky _ANSI_ARGS_((Gridder *slavePtr, int *xPtr,
+ int *yPtr, int *widthPtr, int *heightPtr));
+static int AdjustOffsets _ANSI_ARGS_((int width,
+ int elements, SlotInfo *slotPtr));
+static void ArrangeGrid _ANSI_ARGS_((ClientData clientData));
+static int CheckSlotData _ANSI_ARGS_((Gridder *masterPtr, int slot,
+ int slotType, int checkOnly));
+static int ConfigureSlaves _ANSI_ARGS_((Tcl_Interp *interp,
+ Tk_Window tkwin, int argc, char *argv[]));
+static void DestroyGrid _ANSI_ARGS_((char *memPtr));
+static Gridder *GetGrid _ANSI_ARGS_((Tk_Window tkwin));
+static void GridStructureProc _ANSI_ARGS_((
+ ClientData clientData, XEvent *eventPtr));
+static void GridLostSlaveProc _ANSI_ARGS_((ClientData clientData,
+ Tk_Window tkwin));
+static void GridReqProc _ANSI_ARGS_((ClientData clientData,
+ Tk_Window tkwin));
+static void InitMasterData _ANSI_ARGS_((Gridder *masterPtr));
+static int ResolveConstraints _ANSI_ARGS_((Gridder *gridPtr,
+ int rowOrColumn, int maxOffset));
+static void SetGridSize _ANSI_ARGS_((Gridder *gridPtr));
+static void StickyToString _ANSI_ARGS_((int flags, char *result));
+static int StringToSticky _ANSI_ARGS_((char *string));
+static void Unlink _ANSI_ARGS_((Gridder *gridPtr));
+
+static 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_GridCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Main window associated with
+ * interpreter. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ Tk_Window tkwin = (Tk_Window) clientData;
+ Gridder *masterPtr; /* master grid record */
+ GridMaster *gridPtr; /* pointer to grid data */
+ size_t length; /* streing length of argument */
+ char c; /* 1st character of argument */
+
+ if ((argc >= 2) && ((argv[1][0] == '.') || (argv[1][0] == REL_SKIP) ||
+ (argv[1][0] == REL_VERT))) {
+ return ConfigureSlaves(interp, tkwin, argc-1, argv+1);
+ }
+ if (argc < 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " option arg ?arg ...?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ c = argv[1][0];
+ length = strlen(argv[1]);
+
+ if ((c == 'b') && (strncmp(argv[1], "bbox", length) == 0)) {
+ Tk_Window master;
+ 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 (argc!=3 && argc != 5 && argc != 7) {
+ Tcl_AppendResult(interp, "wrong number of arguments: ",
+ "must be \"",argv[0],
+ " bbox master ?column row ?column row??\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ master = Tk_NameToWindow(interp, argv[2], tkwin);
+ if (master == NULL) {
+ return TCL_ERROR;
+ }
+ masterPtr = GetGrid(master);
+
+ if (argc >= 5) {
+ if (Tcl_GetInt(interp, argv[3], &column) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (Tcl_GetInt(interp, argv[4], &row) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ column2 = column;
+ row2 = row;
+ }
+
+ if (argc == 7) {
+ if (Tcl_GetInt(interp, argv[5], &column2) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (Tcl_GetInt(interp, argv[6], &row2) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+
+ gridPtr = masterPtr->masterDataPtr;
+ if (gridPtr == NULL) {
+ sprintf(interp->result, "%d %d %d %d",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)) {
+ sprintf(interp->result, "%d %d %d %d",0,0,0,0);
+ return(TCL_OK);
+ }
+ if (argc == 3) {
+ row = 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;
+ }
+
+ sprintf(interp->result, "%d %d %d %d",
+ x + gridPtr->startX, y + gridPtr->startY, width, height);
+ } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) {
+ if (argv[2][0] != '.') {
+ Tcl_AppendResult(interp, "bad argument \"", argv[2],
+ "\": must be name of window", (char *) NULL);
+ return TCL_ERROR;
+ }
+ return ConfigureSlaves(interp, tkwin, argc-2, argv+2);
+ } else if (((c == 'f') && (strncmp(argv[1], "forget", length) == 0)) ||
+ ((c == 'r') && (strncmp(argv[1], "remove", length) == 0))) {
+ Tk_Window slave;
+ Gridder *slavePtr;
+ int i;
+
+ for (i = 2; i < argc; i++) {
+ slave = Tk_NameToWindow(interp, argv[i], tkwin);
+ if (slave == NULL) {
+ return TCL_ERROR;
+ }
+ slavePtr = GetGrid(slave);
+ if (slavePtr->masterPtr != NULL) {
+
+ /*
+ * For "forget", reset all the settings to their defaults
+ */
+
+ if (c == 'f') {
+ slavePtr->column = slavePtr->row = -1;
+ slavePtr->numCols = 1;
+ slavePtr->numRows = 1;
+ slavePtr->padX = slavePtr->padY = 0;
+ slavePtr->iPadX = slavePtr->iPadY = 0;
+ slavePtr->doubleBw = 2*Tk_Changes(tkwin)->border_width;
+ slavePtr->flags = 0;
+ slavePtr->sticky = 0;
+ }
+ Tk_ManageGeometry(slave, (Tk_GeomMgr *) NULL,
+ (ClientData) NULL);
+ if (slavePtr->masterPtr->tkwin != Tk_Parent(slavePtr->tkwin)) {
+ Tk_UnmaintainGeometry(slavePtr->tkwin,
+ slavePtr->masterPtr->tkwin);
+ }
+ Unlink(slavePtr);
+ Tk_UnmapWindow(slavePtr->tkwin);
+ }
+ }
+ } else if ((c == 'i') && (strncmp(argv[1], "info", length) == 0)) {
+ register Gridder *slavePtr;
+ Tk_Window slave;
+ char buffer[70];
+
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " info window\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ slave = Tk_NameToWindow(interp, argv[2], tkwin);
+ if (slave == NULL) {
+ return TCL_ERROR;
+ }
+ slavePtr = GetGrid(slave);
+ if (slavePtr->masterPtr == NULL) {
+ interp->result[0] = '\0';
+ return TCL_OK;
+ }
+
+ Tcl_AppendElement(interp, "-in");
+ Tcl_AppendElement(interp, Tk_PathName(slavePtr->masterPtr->tkwin));
+ sprintf(buffer, " -column %d -row %d -columnspan %d -rowspan %d",
+ slavePtr->column, slavePtr->row,
+ slavePtr->numCols, slavePtr->numRows);
+ Tcl_AppendResult(interp, buffer, (char *) NULL);
+ sprintf(buffer, " -ipadx %d -ipady %d -padx %d -pady %d",
+ slavePtr->iPadX/2, slavePtr->iPadY/2, slavePtr->padX/2,
+ slavePtr->padY/2);
+ Tcl_AppendResult(interp, buffer, (char *) NULL);
+ StickyToString(slavePtr->sticky,buffer);
+ Tcl_AppendResult(interp, " -sticky ", buffer, (char *) NULL);
+ } else if((c == 'l') && (strncmp(argv[1], "location", length) == 0)) {
+ Tk_Window master;
+ register SlotInfo *slotPtr;
+ int x, y; /* Offset in pixels, from edge of parent. */
+ int i, j; /* Corresponding column and row indeces. */
+ int endX, endY; /* end of grid */
+
+ if (argc != 5) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " location master x y\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+
+ master = Tk_NameToWindow(interp, argv[2], tkwin);
+ if (master == NULL) {
+ return TCL_ERROR;
+ }
+
+ if (Tk_GetPixels(interp, master, argv[3], &x) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (Tk_GetPixels(interp, master, argv[4], &y) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ masterPtr = GetGrid(master);
+ if (masterPtr->masterDataPtr == NULL) {
+ sprintf(interp->result, "%d %d", -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) {
+ Tk_CancelIdleCall(ArrangeGrid, (ClientData) masterPtr);
+ ArrangeGrid ((ClientData) 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 */
+ }
+ }
+
+ sprintf(interp->result, "%d %d", i, j);
+ } else if ((c == 'p') && (strncmp(argv[1], "propagate", length) == 0)) {
+ Tk_Window master;
+ int propagate;
+
+ if (argc > 4) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " propagate window ?boolean?\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ master = Tk_NameToWindow(interp, argv[2], tkwin);
+ if (master == NULL) {
+ return TCL_ERROR;
+ }
+ masterPtr = GetGrid(master);
+ if (argc == 3) {
+ interp->result = (masterPtr->flags & DONT_PROPAGATE) ? "0" : "1";
+ return TCL_OK;
+ }
+ if (Tcl_GetBoolean(interp, argv[3], &propagate) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((!propagate) ^ (masterPtr->flags&DONT_PROPAGATE)) {
+ 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, (ClientData) masterPtr);
+ }
+ }
+ } else if ((c == 's') && (strncmp(argv[1], "size", length) == 0)
+ && (length > 1)) {
+ Tk_Window master;
+
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " size window\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ master = Tk_NameToWindow(interp, argv[2], tkwin);
+ if (master == NULL) {
+ return TCL_ERROR;
+ }
+ masterPtr = GetGrid(master);
+
+ if (masterPtr->masterDataPtr != NULL) {
+ SetGridSize(masterPtr);
+ gridPtr = masterPtr->masterDataPtr;
+ sprintf(interp->result, "%d %d",
+ MAX(gridPtr->columnEnd, gridPtr->columnMax),
+ MAX(gridPtr->rowEnd, gridPtr->rowMax));
+ } else {
+ sprintf(interp->result, "%d %d",0, 0);
+ }
+ } else if ((c == 's') && (strncmp(argv[1], "slaves", length) == 0)
+ && (length > 1)) {
+ Tk_Window master;
+ Gridder *slavePtr;
+ int i, value;
+ int row = -1, column = -1;
+
+ if ((argc < 3) || ((argc%2) == 0)) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " slaves window ?-option value...?\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ for (i=3; i<argc; i+=2) {
+ length = strlen(argv[i]);
+ if ((*argv[i] != '-') || (length < 2)) {
+ Tcl_AppendResult(interp, "invalid args: should be \"",
+ argv[0], " slaves window ?-option value...?\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (Tcl_GetInt(interp, argv[i+1], &value) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (value < 0) {
+ Tcl_AppendResult(interp, argv[i],
+ " is an invalid value: should NOT be < 0",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (strncmp(argv[i], "-column", length) == 0) {
+ column = value;
+ } else if (strncmp(argv[i], "-row", length) == 0) {
+ row = value;
+ } else {
+ Tcl_AppendResult(interp, argv[i],
+ " is an invalid option: should be \"",
+ "-row, -column\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ }
+ master = Tk_NameToWindow(interp, argv[2], tkwin);
+ if (master == NULL) {
+ return TCL_ERROR;
+ }
+ masterPtr = GetGrid(master);
+
+ 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_AppendElement(interp, Tk_PathName(slavePtr->tkwin));
+ }
+
+ /*
+ * 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.
+ */
+
+ } else if(((c == 'c') && (strncmp(argv[1], "columnconfigure", length) == 0)
+ && (length >= 3)) ||
+ ((c == 'r') && (strncmp(argv[1], "rowconfigure", length) == 0)
+ && (length >=2))) {
+ Tk_Window master;
+ SlotInfo *slotPtr = NULL;
+ int slot; /* the column or row number */
+ size_t length; /* the # of chars in the "-option" string */
+ int slotType; /* COLUMN or ROW */
+ int size; /* the configuration value */
+ int checkOnly; /* check the size only */
+ int argcPtr; /* Number of items in index list */
+ char **argvPtr; /* array of indeces */
+ char **indexP; /* String value of current index list item. */
+ int ok; /* temporary TCL result code */
+ int i;
+
+ if (((argc%2 != 0) && (argc>6)) || (argc < 4)) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ", argv[1], " master index ?-option value...?\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+
+ master = Tk_NameToWindow(interp, argv[2], tkwin);
+ if (master == NULL) {
+ return TCL_ERROR;
+ }
+
+ if (Tcl_SplitList(interp, argv[3], &argcPtr, &argvPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ checkOnly = ((argc == 4) || (argc == 5));
+ masterPtr = GetGrid(master);
+ slotType = (c == 'c') ? COLUMN : ROW;
+ if (checkOnly && argcPtr > 1) {
+ Tcl_AppendResult(interp, argv[3],
+ " must be a single element.", (char *) NULL);
+ Tcl_Free((char *)argvPtr);
+ return TCL_ERROR;
+ }
+ for (indexP=argvPtr; *indexP != NULL; indexP++) {
+ if (Tcl_GetInt(interp, *indexP, &slot) != TCL_OK) {
+ Tcl_Free((char *)argvPtr);
+ return TCL_ERROR;
+ }
+ ok = CheckSlotData(masterPtr, slot, slotType, checkOnly);
+ if ((ok!=TCL_OK) && ((argc<4) || (argc>5))) {
+ Tcl_AppendResult(interp, argv[0],
+ " ", argv[1], ": \"", *argvPtr,"\" is out of range",
+ (char *) NULL);
+ Tcl_Free((char *)argvPtr);
+ return TCL_ERROR;
+ } else 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 (argc == 4) {
+ Tcl_Free((char *)argvPtr);
+ }
+ if ((argc == 4) && (ok == TCL_OK)) {
+ sprintf(interp->result,"-minsize %d -pad %d -weight %d",
+ slotPtr[slot].minSize,slotPtr[slot].pad,
+ slotPtr[slot].weight);
+ return (TCL_OK);
+ } else if (argc == 4) {
+ sprintf(interp->result,"-minsize %d -pad %d -weight %d", 0,0,0);
+ return (TCL_OK);
+ }
+
+ /*
+ * Loop through each option value pair, setting the values as required.
+ * If only one option is given, with no value, the current value is
+ * returned.
+ */
+
+ for (i=4; i<argc; i+=2) {
+ length = strlen(argv[i]);
+ if ((*argv[i] != '-') || length < 2) {
+ Tcl_AppendResult(interp, "invalid arg \"",
+ argv[i], "\" :expecting -minsize, -pad, or -weight.",
+ (char *) NULL);
+ Tcl_Free((char *)argvPtr);
+ return TCL_ERROR;
+ }
+ if (strncmp(argv[i], "-minsize", length) == 0) {
+ if (argc == 5) {
+ int value = ok == TCL_OK ? slotPtr[slot].minSize : 0;
+ sprintf(interp->result,"%d",value);
+ } else if (Tk_GetPixels(interp, master, argv[i+1], &size)
+ != TCL_OK) {
+ Tcl_Free((char *)argvPtr);
+ return TCL_ERROR;
+ } else {
+ slotPtr[slot].minSize = size;
+ }
+ }
+ else if (strncmp(argv[i], "-weight", length) == 0) {
+ int wt;
+ if (argc == 5) {
+ int value = ok == TCL_OK ? slotPtr[slot].weight : 0;
+ sprintf(interp->result,"%d",value);
+ } else if (Tcl_GetInt(interp, argv[i+1], &wt) != TCL_OK) {
+ Tcl_Free((char *)argvPtr);
+ return TCL_ERROR;
+ } else if (wt < 0) {
+ Tcl_AppendResult(interp, "invalid arg \"", argv[i],
+ "\": should be non-negative", (char *) NULL);
+ Tcl_Free((char *)argvPtr);
+ return TCL_ERROR;
+ } else {
+ slotPtr[slot].weight = wt;
+ }
+ }
+ else if (strncmp(argv[i], "-pad", length) == 0) {
+ if (argc == 5) {
+ int value = ok == TCL_OK ? slotPtr[slot].pad : 0;
+ sprintf(interp->result,"%d",value);
+ } else if (Tk_GetPixels(interp, master, argv[i+1], &size)
+ != TCL_OK) {
+ Tcl_Free((char *)argvPtr);
+ return TCL_ERROR;
+ } else if (size < 0) {
+ Tcl_AppendResult(interp, "invalid arg \"", argv[i],
+ "\": should be non-negative", (char *) NULL);
+ Tcl_Free((char *)argvPtr);
+ return TCL_ERROR;
+ } else {
+ slotPtr[slot].pad = size;
+ }
+ } else {
+ Tcl_AppendResult(interp, "invalid arg \"",
+ argv[i], "\": expecting -minsize, -pad, or -weight.",
+ (char *) NULL);
+ Tcl_Free((char *)argvPtr);
+ return TCL_ERROR;
+ }
+ }
+ }
+ Tcl_Free((char *)argvPtr);
+
+ /*
+ * If we changed a property, re-arrange the table,
+ * and check for constraint shrinkage.
+ */
+
+ if (argc != 5) {
+ if (slotType == ROW) {
+ int last = masterPtr->masterDataPtr->rowMax - 1;
+ while ((last >= 0) && (slotPtr[last].weight == 0)
+ && (slotPtr[last].pad == 0)
+ && (slotPtr[last].minSize == 0)) {
+ 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)) {
+ 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, (ClientData) masterPtr);
+ }
+ }
+ } else {
+ Tcl_AppendResult(interp, "bad option \"", argv[1],
+ "\": must be bbox, columnconfigure, configure, forget, info, ",
+ "location, propagate, remove, rowconfigure, size, or slaves.",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ 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, tkwin)
+ 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 = (Gridder *) clientData;
+
+ gridPtr = gridPtr->masterPtr;
+ if (!(gridPtr->flags & REQUESTED_RELAYOUT)) {
+ gridPtr->flags |= REQUESTED_RELAYOUT;
+ Tcl_DoWhenIdle(ArrangeGrid, (ClientData) 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, tkwin)
+ ClientData clientData; /* Grid structure for slave window that
+ * was stolen away. */
+ Tk_Window tkwin; /* Tk's handle for the slave window. */
+{
+ register Gridder *slavePtr = (Gridder *) 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 initial offset of the layout,
+ * if all the weights are zero, else 0.
+ *
+ * Side effects:
+ * The slot offsets are modified to shrink the layout.
+ *
+ *--------------------------------------------------------------
+ */
+
+static int
+AdjustOffsets(size, slots, slotPtr)
+ 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 = 0; /* Sum of the weights for all the slots. */
+ int weight = 0; /* Sum of the weights so far. */
+ int minSize = 0; /* 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(0);
+ }
+
+ /*
+ * If all the weights are zero, center the layout in its parent if
+ * there is extra space, else clip on the bottom/right.
+ */
+
+ for (slot=0; slot < slots; slot++) {
+ totalWeight += slotPtr[slot].weight;
+ }
+
+ if (totalWeight == 0 ) {
+ return(diff > 0 ? diff/2 : 0);
+ }
+
+ /*
+ * Add extra space according to the slot weights. This is done
+ * cumulatively to prevent round-off error accumulation.
+ */
+
+ if (diff > 0) {
+ for (weight=slot=0; slot < slots; slot++) {
+ weight += slotPtr[slot].weight;
+ slotPtr[slot].offset += diff * weight / totalWeight;
+ }
+ return(0);
+ }
+
+ /*
+ * The layout must shrink below its requested size. Compute the
+ * minimum possible size by looking at the slot minSizes.
+ */
+
+ for (slot=0; slot < slots; slot++) {
+ if (slotPtr[slot].weight > 0) {
+ minSize += slotPtr[slot].minSize;
+ } else if (slot > 0) {
+ minSize += slotPtr[slot].offset - slotPtr[slot-1].offset;
+ } else {
+ minSize += slotPtr[slot].offset;
+ }
+ }
+
+ /*
+ * If the requested size is less than the minimum required size,
+ * set the slot sizes to their minimum values, then clip on the
+ * bottom/right.
+ */
+
+ if (size <= minSize) {
+ int offset = 0;
+ for (slot=0; slot < slots; slot++) {
+ if (slotPtr[slot].weight > 0) {
+ offset += slotPtr[slot].minSize;
+ } else if (slot > 0) {
+ offset += slotPtr[slot].offset - slotPtr[slot-1].offset;
+ } else {
+ offset += slotPtr[slot].offset;
+ }
+ slotPtr[slot].offset = offset;
+ }
+ return(0);
+ }
+
+ /*
+ * 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.
+ */
+
+ for (totalWeight=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; /* max 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.
+ */
+
+ for (weight=slot=0; slot < slots; slot++) {
+ weight += slotPtr[slot].temp;
+ slotPtr[slot].offset += newDiff * weight / totalWeight;
+ }
+ diff -= newDiff;
+ }
+ return(0);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * 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(slavePtr, xPtr, yPtr, widthPtr, heightPtr)
+ 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->padX/2;
+ *widthPtr -= slavePtr->padX;
+ *yPtr += slavePtr->padY/2;
+ *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 clientData; /* Structure describing parent whose slaves
+ * are to be re-layed out. */
+{
+ register Gridder *masterPtr = (Gridder *) 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 */
+
+ masterPtr->flags &= ~REQUESTED_RELAYOUT;
+
+ /*
+ * If the parent has no slaves anymore, then don't do anything
+ * at all: just leave the parent's size as-is. Otherwise there is
+ * no way to "relinquish" control over the parent 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((ClientData) 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 += 2*Tk_InternalBorderWidth(masterPtr->tkwin);
+ height += 2*Tk_InternalBorderWidth(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, (ClientData) masterPtr);
+ }
+ masterPtr->abortPtr = NULL;
+ Tcl_Release((ClientData) masterPtr);
+ return;
+ }
+
+ /*
+ * If the currently requested layout size doesn't match the parent's
+ * window size, then adjust the slot offsets according to the
+ * weights. If all of the weights are zero, center the layout in
+ * its parent. I haven't decided what to do if the parent is smaller
+ * than the requested size.
+ */
+
+ realWidth = Tk_Width(masterPtr->tkwin) -
+ 2*Tk_InternalBorderWidth(masterPtr->tkwin);
+ realHeight = Tk_Height(masterPtr->tkwin) -
+ 2*Tk_InternalBorderWidth(masterPtr->tkwin);
+ slotPtr->startX = AdjustOffsets(realWidth,
+ MAX(slotPtr->columnEnd,slotPtr->columnMax), slotPtr->columnPtr);
+ slotPtr->startY = AdjustOffsets(realHeight,
+ MAX(slotPtr->rowEnd,slotPtr->rowMax), slotPtr->rowPtr);
+ slotPtr->startX += Tk_InternalBorderWidth(masterPtr->tkwin);
+ slotPtr->startY += Tk_InternalBorderWidth(masterPtr->tkwin);
+
+ /*
+ * 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((ClientData) 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(masterPtr, slotType, maxOffset)
+ 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. */
+
+ /*
+ * 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 = (GridLayout *) Tcl_Alloc(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].pad = slotPtr[slot].pad;
+ layoutPtr[slot].binNextPtr = NULL;
+ }
+ for(;slot<gridCount;slot++) {
+ layoutPtr[slot].minSize = 0;
+ layoutPtr[slot].weight = 0;
+ 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 {
+ 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 {
+ int size = slavePtr->size + layoutPtr[rightEdge].pad;
+ if (size > layoutPtr[rightEdge].minSize) {
+ layoutPtr[rightEdge].minSize = size;
+ }
+ }
+ }
+ break;
+ }
+
+ /*
+ * 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=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.
+ * 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.
+ */
+
+ for (weight=0,slot=start; slot<end; slot++) {
+ int diff = layoutPtr[slot].maxOffset - layoutPtr[slot].minOffset;
+ weight += noWeights ? 1 : layoutPtr[slot].weight;
+ if ((noWeights || layoutPtr[slot].weight>0) &&
+ (diff*totalWeight/weight) < (have-need)) {
+ have = diff * totalWeight / weight + need;
+ }
+ }
+
+ /*
+ * Now distribute the extra space among the slots by
+ * adjusting the minSizes and minOffsets.
+ */
+
+ for (weight=0,slot=start; slot<end; slot++) {
+ weight += noWeights ? 1 : layoutPtr[slot].weight;
+ layoutPtr[slot].minOffset +=
+ (int)((double) (have-need) * weight/totalWeight + 0.5);
+ layoutPtr[slot].minSize = layoutPtr[slot].minOffset
+ - layoutPtr[slot-1].minOffset;
+ }
+ layoutPtr[slot].minSize = layoutPtr[slot].minOffset
+ - layoutPtr[slot-1].minOffset;
+
+ /*
+ * 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--) {
+ 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) {
+ Tcl_Free((char *)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(tkwin)
+ Tk_Window tkwin; /* Token for window for which
+ * grid structure is desired. */
+{
+ register Gridder *gridPtr;
+ Tcl_HashEntry *hPtr;
+ int new;
+
+ if (!initialized) {
+ initialized = 1;
+ Tcl_InitHashTable(&gridHashTable, TCL_ONE_WORD_KEYS);
+ }
+
+ /*
+ * See if there's already grid for this window. If not,
+ * then create a new one.
+ */
+
+ hPtr = Tcl_CreateHashEntry(&gridHashTable, (char *) tkwin, &new);
+ if (!new) {
+ return (Gridder *) Tcl_GetHashValue(hPtr);
+ }
+ gridPtr = (Gridder *) Tcl_Alloc(sizeof(Gridder));
+ gridPtr->tkwin = tkwin;
+ gridPtr->masterPtr = NULL;
+ gridPtr->masterDataPtr = NULL;
+ gridPtr->nextPtr = NULL;
+ gridPtr->slavePtr = NULL;
+ gridPtr->binNextPtr = NULL;
+
+ gridPtr->column = gridPtr->row = -1;
+ gridPtr->numCols = 1;
+ gridPtr->numRows = 1;
+
+ gridPtr->padX = gridPtr->padY = 0;
+ gridPtr->iPadX = gridPtr->iPadY = 0;
+ gridPtr->doubleBw = 2*Tk_Changes(tkwin)->border_width;
+ gridPtr->abortPtr = NULL;
+ gridPtr->flags = 0;
+ gridPtr->sticky = 0;
+ gridPtr->size = 0;
+ gridPtr->masterDataPtr = NULL;
+ Tcl_SetHashValue(hPtr, gridPtr);
+ Tk_CreateEventHandler(tkwin, StructureNotifyMask,
+ GridStructureProc, (ClientData) 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(masterPtr)
+ 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);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * 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(masterPtr, slot, slotType, checkOnly)
+ 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 *new = (SlotInfo *) Tcl_Alloc(newSize);
+ SlotInfo *old = (slotType == ROW) ?
+ masterPtr->masterDataPtr->rowPtr :
+ masterPtr->masterDataPtr->columnPtr;
+ memcpy((VOID *) new, (VOID *) old, oldSize );
+ memset((VOID *) (new+numSlot), 0, newSize - oldSize );
+ Tcl_Free((char *) old);
+ if (slotType == ROW) {
+ masterPtr->masterDataPtr->rowPtr = new ;
+ masterPtr->masterDataPtr->rowSpace = newNumSlot ;
+ } else {
+ masterPtr->masterDataPtr->columnPtr = new;
+ 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(masterPtr)
+ Gridder *masterPtr;
+{
+ size_t size;
+ if (masterPtr->masterDataPtr == NULL) {
+ GridMaster *gridPtr = masterPtr->masterDataPtr =
+ (GridMaster *) Tcl_Alloc(sizeof(GridMaster));
+ size = sizeof(SlotInfo) * TYPICAL_SIZE;
+
+ gridPtr->columnEnd = 0;
+ gridPtr->columnMax = 0;
+ gridPtr->columnPtr = (SlotInfo *) Tcl_Alloc(size);
+ gridPtr->columnSpace = 0;
+ gridPtr->columnSpace = TYPICAL_SIZE;
+ gridPtr->rowEnd = 0;
+ gridPtr->rowMax = 0;
+ gridPtr->rowPtr = (SlotInfo *) Tcl_Alloc(size);
+ gridPtr->rowSpace = 0;
+ gridPtr->rowSpace = TYPICAL_SIZE;
+
+ memset((VOID *) gridPtr->columnPtr, 0, size);
+ memset((VOID *) gridPtr->rowPtr, 0, size);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Unlink --
+ *
+ * Remove a grid from its parent's list of slaves.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The parent will be scheduled for re-arranging, and the size of the
+ * grid will be adjusted accordingly
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+Unlink(slavePtr)
+ register Gridder *slavePtr; /* Window to unlink. */
+{
+ register Gridder *masterPtr, *slavePtr2;
+ GridMaster *gridPtr; /* pointer to grid data */
+
+ masterPtr = slavePtr->masterPtr;
+ if (masterPtr == NULL) {
+ return;
+ }
+
+ gridPtr = masterPtr->masterDataPtr;
+ if (masterPtr->slavePtr == slavePtr) {
+ masterPtr->slavePtr = slavePtr->nextPtr;
+ }
+ else {
+ for (slavePtr2 = masterPtr->slavePtr; ; slavePtr2 = slavePtr2->nextPtr) {
+ if (slavePtr2 == NULL) {
+ 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, (ClientData) masterPtr);
+ }
+ if (masterPtr->abortPtr != NULL) {
+ *masterPtr->abortPtr = 1;
+ }
+
+ if ((slavePtr->numCols+slavePtr->column == gridPtr->columnMax)
+ || (slavePtr->numRows+slavePtr->row == gridPtr->rowMax)) {
+ }
+ slavePtr->masterPtr = NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DestroyGrid --
+ *
+ * This procedure is invoked by Tk_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(memPtr)
+ char *memPtr; /* Info about window that is now dead. */
+{
+ register Gridder *gridPtr = (Gridder *) memPtr;
+
+ if (gridPtr->masterDataPtr != NULL) {
+ if (gridPtr->masterDataPtr->rowPtr != NULL) {
+ Tcl_Free((char *) gridPtr->masterDataPtr -> rowPtr);
+ }
+ if (gridPtr->masterDataPtr->columnPtr != NULL) {
+ Tcl_Free((char *) gridPtr->masterDataPtr -> columnPtr);
+ }
+ Tcl_Free((char *) gridPtr->masterDataPtr);
+ }
+ Tcl_Free((char *) 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, eventPtr)
+ ClientData clientData; /* Our information about window
+ * referred to by eventPtr. */
+ XEvent *eventPtr; /* Describes what just happened. */
+{
+ register Gridder *gridPtr = (Gridder *) clientData;
+
+ if (eventPtr->type == ConfigureNotify) {
+ if (!(gridPtr->flags & REQUESTED_RELAYOUT)) {
+ gridPtr->flags |= REQUESTED_RELAYOUT;
+ Tcl_DoWhenIdle(ArrangeGrid, (ClientData) gridPtr);
+ }
+ if (gridPtr->doubleBw != 2*Tk_Changes(gridPtr->tkwin)->border_width) {
+ if ((gridPtr->masterPtr != NULL) &&
+ !(gridPtr->masterPtr->flags & REQUESTED_RELAYOUT)) {
+ gridPtr->doubleBw = 2*Tk_Changes(gridPtr->tkwin)->border_width;
+ gridPtr->masterPtr->flags |= REQUESTED_RELAYOUT;
+ Tcl_DoWhenIdle(ArrangeGrid, (ClientData) 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(&gridHashTable,
+ (char *) gridPtr->tkwin));
+ if (gridPtr->flags & REQUESTED_RELAYOUT) {
+ Tk_CancelIdleCall(ArrangeGrid, (ClientData) gridPtr);
+ }
+ gridPtr->tkwin = NULL;
+ Tk_EventuallyFree((ClientData) gridPtr, DestroyGrid);
+ } else if (eventPtr->type == MapNotify) {
+ if (!(gridPtr->flags & REQUESTED_RELAYOUT)) {
+ gridPtr->flags |= REQUESTED_RELAYOUT;
+ Tcl_DoWhenIdle(ArrangeGrid, (ClientData) 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 interp->result is set to contain an error message.
+ *
+ * Side effects:
+ * Slave windows get taken over by the grid.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+ConfigureSlaves(interp, tkwin, argc, argv)
+ Tcl_Interp *interp; /* Interpreter for error reporting. */
+ Tk_Window tkwin; /* Any window in application containing
+ * slaves. Used to look up slave names. */
+ int argc; /* Number of elements in argv. */
+ char *argv[]; /* Argument strings: contains one or more
+ * window names followed by any number
+ * of "option value" pairs. Caller must
+ * make sure that there is at least one
+ * window name. */
+{
+ Gridder *masterPtr;
+ Gridder *slavePtr;
+ Tk_Window other, slave, parent, ancestor;
+ int i, j, c, tmp;
+ size_t length;
+ int numWindows;
+ int width;
+ int defaultColumn = 0; /* default column number */
+ int defaultColumnSpan = 1; /* default number of columns */
+ char *lastWindow; /* use this window to base current
+ * Row/col on */
+
+ /*
+ * Count the number of windows, or window short-cuts.
+ */
+
+ for(numWindows=i=0;i<argc;i++) {
+ char firstChar = *argv[i];
+ if (firstChar == '.') {
+ numWindows++;
+ continue;
+ }
+ length = strlen(argv[i]);
+ if (length > 1 && firstChar == '-') {
+ break;
+ }
+ if (length > 1) {
+ Tcl_AppendResult(interp, "unexpected parameter, \"",
+ argv[i], "\", in configure list. ",
+ "Should be window name or option", (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ if ((firstChar == REL_HORIZ) && ((numWindows == 0) ||
+ (*argv[i-1] == REL_SKIP) || (*argv[i-1] == REL_VERT))) {
+ Tcl_AppendResult(interp,
+ "Must specify window before shortcut '-'.",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ if ((firstChar == REL_VERT) || (firstChar == REL_SKIP)
+ || (firstChar == REL_HORIZ)) {
+ continue;
+ }
+
+ Tcl_AppendResult(interp, "invalid window shortcut, \"",
+ argv[i], "\" should be '-', 'x', or '^'", (char *) NULL);
+ return TCL_ERROR;
+ }
+ numWindows = i;
+
+ if ((argc-numWindows)&1) {
+ Tcl_AppendResult(interp, "extra option or",
+ " option with no value", (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ /*
+ * 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.
+ */
+
+ masterPtr = NULL;
+ for (j = 0; j < numWindows; j++) {
+ char firstChar = *argv[j];
+
+ /*
+ * '^' 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 &&
+ (*argv[j+defaultColumnSpan] == REL_HORIZ);
+ defaultColumnSpan++) {
+ /* null body */
+ }
+
+ slave = Tk_NameToWindow(interp, argv[j], tkwin);
+ if (slave == NULL) {
+ return TCL_ERROR;
+ }
+ if (Tk_IsTopLevel(slave)) {
+ Tcl_AppendResult(interp, "can't manage \"", argv[j],
+ "\": it's a top-level window", (char *) 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 < argc; i+=2) {
+ length = strlen(argv[i]);
+ c = argv[i][1];
+
+ if (length < 2) {
+ Tcl_AppendResult(interp, "unknown or ambiguous option \"",
+ argv[i], "\": must be ",
+ "-column, -columnspan, -in, -ipadx, -ipady, ",
+ "-padx, -pady, -row, -rowspan, or -sticky",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ if ((c == 'c') && (strncmp(argv[i], "-column", length) == 0)) {
+ if (Tcl_GetInt(interp, argv[i+1], &tmp) != TCL_OK || tmp<0) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "bad column value \"", argv[i+1],
+ "\": must be a non-negative integer", (char *)NULL);
+ return TCL_ERROR;
+ }
+ slavePtr->column = tmp;
+ } else if ((c == 'c')
+ && (strncmp(argv[i], "-columnspan", length) == 0)) {
+ if (Tcl_GetInt(interp, argv[i+1], &tmp) != TCL_OK || tmp <= 0) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "bad columnspan value \"", argv[i+1],
+ "\": must be a positive integer", (char *)NULL);
+ return TCL_ERROR;
+ }
+ slavePtr->numCols = tmp;
+ } else if ((c == 'i') && (strncmp(argv[i], "-in", length) == 0)) {
+ other = Tk_NameToWindow(interp, argv[i+1], tkwin);
+ if (other == NULL) {
+ return TCL_ERROR;
+ }
+ if (other == slave) {
+ sprintf(interp->result,"Window can't be managed in itself");
+ return TCL_ERROR;
+ }
+ masterPtr = GetGrid(other);
+ InitMasterData(masterPtr);
+ } else if ((c == 'i')
+ && (strncmp(argv[i], "-ipadx", length) == 0)) {
+ if ((Tk_GetPixels(interp, slave, argv[i+1], &tmp) != TCL_OK)
+ || (tmp < 0)) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "bad ipadx value \"", argv[i+1],
+ "\": must be positive screen distance",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ slavePtr->iPadX = tmp*2;
+ } else if ((c == 'i')
+ && (strncmp(argv[i], "-ipady", length) == 0)) {
+ if ((Tk_GetPixels(interp, slave, argv[i+1], &tmp) != TCL_OK)
+ || (tmp< 0)) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "bad ipady value \"", argv[i+1],
+ "\": must be positive screen distance",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ slavePtr->iPadY = tmp*2;
+ } else if ((c == 'p')
+ && (strncmp(argv[i], "-padx", length) == 0)) {
+ if ((Tk_GetPixels(interp, slave, argv[i+1], &tmp) != TCL_OK)
+ || (tmp< 0)) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "bad padx value \"", argv[i+1],
+ "\": must be positive screen distance",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ slavePtr->padX = tmp*2;
+ } else if ((c == 'p')
+ && (strncmp(argv[i], "-pady", length) == 0)) {
+ if ((Tk_GetPixels(interp, slave, argv[i+1], &tmp) != TCL_OK)
+ || (tmp< 0)) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "bad pady value \"", argv[i+1],
+ "\": must be positive screen distance",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ slavePtr->padY = tmp*2;
+ } else if ((c == 'r') && (strncmp(argv[i], "-row", length) == 0)) {
+ if (Tcl_GetInt(interp, argv[i+1], &tmp) != TCL_OK || tmp<0) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "bad grid value \"", argv[i+1],
+ "\": must be a non-negative integer", (char *)NULL);
+ return TCL_ERROR;
+ }
+ slavePtr->row = tmp;
+ } else if ((c == 'r')
+ && (strncmp(argv[i], "-rowspan", length) == 0)) {
+ if ((Tcl_GetInt(interp, argv[i+1], &tmp) != TCL_OK) || tmp<=0) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "bad rowspan value \"", argv[i+1],
+ "\": must be a positive integer", (char *)NULL);
+ return TCL_ERROR;
+ }
+ slavePtr->numRows = tmp;
+ } else if ((c == 's')
+ && strncmp(argv[i], "-sticky", length) == 0) {
+ int sticky = StringToSticky(argv[i+1]);
+ if (sticky == -1) {
+ Tcl_AppendResult(interp, "bad stickyness value \"", argv[i+1],
+ "\": must be a string containing n, e, s, and/or w",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ slavePtr->sticky = sticky;
+ } else {
+ Tcl_AppendResult(interp, "unknown or ambiguous option \"",
+ argv[i], "\": must be ",
+ "-column, -columnspan, -in, -ipadx, -ipady, ",
+ "-padx, -pady, -row, -rowspan, or -sticky",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ }
+
+ /*
+ * Make sure we have a geometry master. We look at:
+ * 1) the -in flag
+ * 2) the geometry master of the first slave (if specified)
+ * 3) the parent of the first slave.
+ */
+
+ if (masterPtr == NULL) {
+ masterPtr = slavePtr->masterPtr;
+ }
+ 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_IsTopLevel(ancestor)) {
+ Tcl_AppendResult(interp, "can't put ", argv[j],
+ " inside ", Tk_PathName(masterPtr->tkwin),
+ (char *) NULL);
+ Unlink(slavePtr);
+ return TCL_ERROR;
+ }
+ }
+
+ /*
+ * Try to make sure our master isn't managed by us.
+ */
+
+ if (masterPtr->masterPtr == slavePtr) {
+ Tcl_AppendResult(interp, "can't put ", argv[j],
+ " inside ", Tk_PathName(masterPtr->tkwin),
+ ", would cause management loop.",
+ (char *) NULL);
+ Unlink(slavePtr);
+ return TCL_ERROR;
+ }
+
+ Tk_ManageGeometry(slave, &gridMgrType, (ClientData) slavePtr);
+
+ /*
+ * Assign default position information.
+ */
+
+ if (slavePtr->column == -1) {
+ slavePtr->column = defaultColumn;
+ }
+ slavePtr->numCols += defaultColumnSpan - 1;
+ if (slavePtr->row == -1) {
+ if (masterPtr->masterDataPtr == NULL) {
+ slavePtr->row = 0;
+ } else {
+ slavePtr->row = masterPtr->masterDataPtr->rowEnd;
+ }
+ }
+ defaultColumn += slavePtr->numCols;
+ defaultColumnSpan = 1;
+
+ /*
+ * Arrange for the parent to be re-arranged at the first
+ * idle moment.
+ */
+
+ if (masterPtr->abortPtr != NULL) {
+ *masterPtr->abortPtr = 1;
+ }
+ if (!(masterPtr->flags & REQUESTED_RELAYOUT)) {
+ masterPtr->flags |= REQUESTED_RELAYOUT;
+ Tcl_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
+ }
+ }
+
+ /* Now look for all the "^"'s. */
+
+ lastWindow = NULL;
+ for (j = 0; j < numWindows; j++) {
+ struct Gridder *otherPtr;
+ int match; /* found a match for the ^ */
+ int lastRow, lastColumn; /* implied end of table */
+
+ if (*argv[j] == '.') {
+ lastWindow = argv[j];
+ }
+ if (*argv[j] != REL_VERT) {
+ continue;
+ }
+
+ if (masterPtr == NULL) {
+ Tcl_AppendResult(interp, "can't use '^', cant find master",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ for (width=1; width+j < numWindows && *argv[j+width] == REL_VERT;
+ width++) {
+ /* Null Body */
+ }
+
+ /*
+ * Find the implied grid location of the ^
+ */
+
+ if (lastWindow == NULL) {
+ if (masterPtr->masterDataPtr != NULL) {
+ SetGridSize(masterPtr);
+ lastRow = masterPtr->masterDataPtr->rowEnd - 1;
+ } else {
+ lastRow = 0;
+ }
+ lastColumn = 0;
+ } else {
+ other = Tk_NameToWindow(interp, lastWindow, tkwin);
+ otherPtr = GetGrid(other);
+ lastRow = otherPtr->row;
+ lastColumn = otherPtr->column + otherPtr->numCols;
+ }
+
+ for (match=0, slavePtr = masterPtr->slavePtr; slavePtr != NULL;
+ slavePtr = slavePtr->nextPtr) {
+
+ if (slavePtr->numCols == width
+ && slavePtr->column == lastColumn
+ && slavePtr->row + slavePtr->numRows == lastRow) {
+ slavePtr->numRows++;
+ match++;
+ }
+ lastWindow = Tk_PathName(slavePtr->tkwin);
+ }
+ if (!match) {
+ Tcl_AppendResult(interp, "can't find slave to extend with \"^\".",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ j += width - 1;
+ }
+
+ if (masterPtr == NULL) {
+ Tcl_AppendResult(interp, "can't determine master window",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ SetGridSize(masterPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * StickyToString
+ *
+ * Converts the internal boolean combination of "sticky" bits onto
+ * a TCL list element containing zero or mor of n, s, e, or w.
+ *
+ * Results:
+ * A string is placed into the "result" pointer.
+ *
+ * Side effects:
+ * none.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+StickyToString(flags, result)
+ int flags; /* the sticky flags */
+ char *result; /* where to put the result */
+{
+ int count = 0;
+ if (flags&STICK_NORTH) {
+ result[count++] = 'n';
+ }
+ if (flags&STICK_EAST) {
+ result[count++] = 'e';
+ }
+ if (flags&STICK_SOUTH) {
+ result[count++] = 's';
+ }
+ if (flags&STICK_WEST) {
+ result[count++] = 'w';
+ }
+ if (count) {
+ result[count] = '\0';
+ } else {
+ sprintf(result,"{}");
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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(string)
+ 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;
+}