4 * Grid based geometry manager.
6 * Copyright (c) 1996-1997 by Sun Microsystems, Inc.
8 * See the file "license.terms" for information on usage and redistribution
9 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
23 #define MAX(x,y) ((x) > (y) ? (x) : (y))
27 #define MIN(x,y) ((x) > (y) ? (y) : (x))
29 #define COLUMN (1) /* working on column offsets */
30 #define ROW (2) /* working on row offsets */
32 #define CHECK_ONLY (1) /* check max slot constraint */
33 #define CHECK_SPACE (2) /* alloc more space, don't change max */
36 * Pre-allocate enough row and column slots for "typical" sized tables
37 * this value should be chosen so by the time the extra malloc's are
38 * required, the layout calculations overwehlm them. [A "slot" contains
39 * information for either a row or column, depending upon the context.]
42 #define TYPICAL_SIZE 25 /* (arbitrary guess) */
43 #define PREALLOC 10 /* extra slots to allocate */
46 * Pre-allocate room for uniform groups during layout.
49 #define UNIFORM_PREALLOC 10
52 * Data structures are allocated dynamically to support arbitrary sized tables.
53 * However, the space is proportional to the highest numbered slot with
54 * some non-default property. This limit is used to head off mistakes and
55 * denial of service attacks by limiting the amount of storage required.
58 #define MAX_ELEMENT 10000
61 * Special characters to support relative layouts.
64 #define REL_SKIP 'x' /* Skip this column. */
65 #define REL_HORIZ '-' /* Extend previous widget horizontally. */
66 #define REL_VERT '^' /* Extend widget from row above. */
69 * Structure to hold information for grid masters. A slot is either
73 typedef struct SlotInfo {
74 int minSize; /* The minimum size of this slot (in pixels).
75 * It is set via the rowconfigure or
76 * columnconfigure commands. */
77 int weight; /* The resize weight of this slot. (0) means
78 * this slot doesn't resize. Extra space in
79 * the layout is given distributed among slots
80 * inproportion to their weights. */
81 int pad; /* Extra padding, in pixels, required for
82 * this slot. This amount is "added" to the
83 * largest slave in the slot. */
84 Tk_Uid uniform; /* Value of -uniform option. It is used to
85 * group slots that should have the same
87 int offset; /* This is a cached value used for
88 * introspection. It is the pixel
89 * offset of the right or bottom edge
90 * of this slot from the beginning of the
92 int temp; /* This is a temporary value used for
93 * calculating adjusted weights when
94 * shrinking the layout below its
99 * Structure to hold information during layout calculations. There
100 * is one of these for each slot, an array for each of the rows or columns.
103 typedef struct GridLayout {
104 struct Gridder *binNextPtr; /* The next slave window in this bin.
105 * Each bin contains a list of all
106 * slaves whose spans are >1 and whose
107 * right edges fall in this slot. */
108 int minSize; /* Minimum size needed for this slot,
109 * in pixels. This is the space required
110 * to hold any slaves contained entirely
111 * in this slot, adjusted for any slot
112 * constrants, such as size or padding. */
113 int pad; /* Padding needed for this slot */
114 int weight; /* Slot weight, controls resizing. */
115 Tk_Uid uniform; /* Value of -uniform option. It is used to
116 * group slots that should have the same
118 int minOffset; /* The minimum offset, in pixels, from
119 * the beginning of the layout to the
120 * right/bottom edge of the slot calculated
121 * from top/left to bottom/right. */
122 int maxOffset; /* The maximum offset, in pixels, from
123 * the beginning of the layout to the
124 * right-or-bottom edge of the slot calculated
125 * from bottom-or-right to top-or-left. */
129 * Keep one of these for each geometry master.
133 SlotInfo *columnPtr; /* Pointer to array of column constraints. */
134 SlotInfo *rowPtr; /* Pointer to array of row constraints. */
135 int columnEnd; /* The last column occupied by any slave. */
136 int columnMax; /* The number of columns with constraints. */
137 int columnSpace; /* The number of slots currently allocated for
138 * column constraints. */
139 int rowEnd; /* The last row occupied by any slave. */
140 int rowMax; /* The number of rows with constraints. */
141 int rowSpace; /* The number of slots currently allocated
142 * for row constraints. */
143 int startX; /* Pixel offset of this layout within its
145 int startY; /* Pixel offset of this layout within its
150 * For each window that the grid cares about (either because
151 * the window is managed by the grid or because the window
152 * has slaves that are managed by the grid), there is a
153 * structure of the following type:
156 typedef struct Gridder {
157 Tk_Window tkwin; /* Tk token for window. NULL means that
158 * the window has been deleted, but the
159 * gridder hasn't had a chance to clean up
160 * yet because the structure is still in
162 struct Gridder *masterPtr; /* Master window within which this window
163 * is managed (NULL means this window
164 * isn't managed by the gridder). */
165 struct Gridder *nextPtr; /* Next window managed within same
166 * parent. List order doesn't matter. */
167 struct Gridder *slavePtr; /* First in list of slaves managed
168 * inside this window (NULL means
169 * no grid slaves). */
170 GridMaster *masterDataPtr; /* Additional data for geometry master. */
171 int column, row; /* Location in the grid (starting
173 int numCols, numRows; /* Number of columns or rows this slave spans.
174 * Should be at least 1. */
175 int padX, padY; /* Total additional pixels to leave around the
176 * window. Some is of this space is on each
177 * side. This is space *outside* the window:
178 * we'll allocate extra space in frame but
179 * won't enlarge window). */
180 int padLeft, padTop; /* The part of padX or padY to use on the
181 * left or top of the widget, respectively.
182 * By default, this is half of padX or padY. */
183 int iPadX, iPadY; /* Total extra pixels to allocate inside the
184 * window (half this amount will appear on
186 int sticky; /* which sides of its cavity this window
187 * sticks to. See below for definitions */
188 int doubleBw; /* Twice the window's last known border
189 * width. If this changes, the window
190 * must be re-arranged within its parent. */
191 int *abortPtr; /* If non-NULL, it means that there is a nested
192 * call to ArrangeGrid already working on
193 * this window. *abortPtr may be set to 1 to
194 * abort that nested call. This happens, for
195 * example, if tkwin or any of its slaves
197 int flags; /* Miscellaneous flags; see below
198 * for definitions. */
201 * These fields are used temporarily for layout calculations only.
204 struct Gridder *binNextPtr; /* Link to next span>1 slave in this bin. */
205 int size; /* Nominal size (width or height) in pixels
206 * of the slave. This includes the padding. */
209 /* Flag values for "sticky"ness The 16 combinations subsume the packer's
210 * notion of anchor and fill.
212 * STICK_NORTH This window sticks to the top of its cavity.
213 * STICK_EAST This window sticks to the right edge of its cavity.
214 * STICK_SOUTH This window sticks to the bottom of its cavity.
215 * STICK_WEST This window sticks to the left edge of its cavity.
218 #define STICK_NORTH 1
220 #define STICK_SOUTH 4
225 * Structure to gather information about uniform groups during layout.
228 typedef struct UniformGroup {
234 * Flag values for Grid structures:
236 * REQUESTED_RELAYOUT: 1 means a Tcl_DoWhenIdle request
237 * has already been made to re-arrange
238 * all the slaves of this window.
240 * DONT_PROPAGATE: 1 means don't set this window's requested
241 * size. 0 means if this window is a master
242 * then Tk will set its requested size to fit
243 * the needs of its slaves.
246 #define REQUESTED_RELAYOUT 1
247 #define DONT_PROPAGATE 2
250 * Prototypes for procedures used only in this file:
253 static void AdjustForSticky _ANSI_ARGS_((Gridder *slavePtr, int *xPtr,
254 int *yPtr, int *widthPtr, int *heightPtr));
255 static int AdjustOffsets _ANSI_ARGS_((int width,
256 int elements, SlotInfo *slotPtr));
257 static void ArrangeGrid _ANSI_ARGS_((ClientData clientData));
258 static int CheckSlotData _ANSI_ARGS_((Gridder *masterPtr, int slot,
259 int slotType, int checkOnly));
260 static int ConfigureSlaves _ANSI_ARGS_((Tcl_Interp *interp,
261 Tk_Window tkwin, int objc, Tcl_Obj *CONST objv[]));
262 static void DestroyGrid _ANSI_ARGS_((char *memPtr));
263 static Gridder *GetGrid _ANSI_ARGS_((Tk_Window tkwin));
264 static int GridBboxCommand _ANSI_ARGS_((Tk_Window tkwin,
265 Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
266 static int GridForgetRemoveCommand _ANSI_ARGS_((Tk_Window tkwin,
267 Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
268 static int GridInfoCommand _ANSI_ARGS_((Tk_Window tkwin,
269 Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
270 static int GridLocationCommand _ANSI_ARGS_((Tk_Window tkwin,
271 Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
272 static int GridPropagateCommand _ANSI_ARGS_((Tk_Window tkwin,
273 Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
274 static int GridRowColumnConfigureCommand _ANSI_ARGS_((Tk_Window tkwin,
275 Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
276 static int GridSizeCommand _ANSI_ARGS_((Tk_Window tkwin,
277 Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
278 static int GridSlavesCommand _ANSI_ARGS_((Tk_Window tkwin,
279 Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
280 static void GridStructureProc _ANSI_ARGS_((
281 ClientData clientData, XEvent *eventPtr));
282 static void GridLostSlaveProc _ANSI_ARGS_((ClientData clientData,
284 static void GridReqProc _ANSI_ARGS_((ClientData clientData,
286 static void InitMasterData _ANSI_ARGS_((Gridder *masterPtr));
287 static Tcl_Obj *NewPairObj _ANSI_ARGS_((Tcl_Interp*, int, int));
288 static Tcl_Obj *NewQuadObj _ANSI_ARGS_((Tcl_Interp*, int, int, int, int));
289 static int ResolveConstraints _ANSI_ARGS_((Gridder *gridPtr,
290 int rowOrColumn, int maxOffset));
291 static void SetGridSize _ANSI_ARGS_((Gridder *gridPtr));
292 static void StickyToString _ANSI_ARGS_((int flags, char *result));
293 static int StringToSticky _ANSI_ARGS_((char *string));
294 static void Unlink _ANSI_ARGS_((Gridder *gridPtr));
297 * Prototypes for procedures contained in other files but not exported
301 void TkPrintPadAmount _ANSI_ARGS_((Tcl_Interp*, char*, int, int));
302 int TkParsePadAmount _ANSI_ARGS_((Tcl_Interp*, Tk_Window, Tcl_Obj*, int*, int*));
304 static Tk_GeomMgr gridMgrType = {
306 GridReqProc, /* requestProc */
307 GridLostSlaveProc, /* lostSlaveProc */
311 *--------------------------------------------------------------
315 * This procedure is invoked to process the "grid" Tcl command.
316 * See the user documentation for details on what it does.
319 * A standard Tcl result.
322 * See the user documentation.
324 *--------------------------------------------------------------
328 Tk_GridObjCmd(clientData, interp, objc, objv)
329 ClientData clientData; /* Main window associated with
331 Tcl_Interp *interp; /* Current interpreter. */
332 int objc; /* Number of arguments. */
333 Tcl_Obj *CONST objv[]; /* Argument objects. */
335 Tk_Window tkwin = (Tk_Window) clientData;
336 static CONST char *optionStrings[] = {
337 "bbox", "columnconfigure", "configure", "forget",
338 "info", "location", "propagate", "remove",
339 "rowconfigure", "size", "slaves", (char *) NULL };
341 GRID_BBOX, GRID_COLUMNCONFIGURE, GRID_CONFIGURE, GRID_FORGET,
342 GRID_INFO, GRID_LOCATION, GRID_PROPAGATE, GRID_REMOVE,
343 GRID_ROWCONFIGURE, GRID_SIZE, GRID_SLAVES };
348 char *argv1 = Tcl_GetString(objv[1]);
349 if ((argv1[0] == '.') || (argv1[0] == REL_SKIP) ||
350 (argv1[0] == REL_VERT)) {
351 return ConfigureSlaves(interp, tkwin, objc-1, objv+1);
355 Tcl_WrongNumArgs(interp, 1, objv, "option arg ?arg ...?");
359 if (Tcl_GetIndexFromObj(interp, objv[1], optionStrings, "option", 0,
364 switch ((enum options) index) {
366 return GridBboxCommand(tkwin, interp, objc, objv);
368 return ConfigureSlaves(interp, tkwin, objc-2, objv+2);
371 return GridForgetRemoveCommand(tkwin, interp, objc, objv);
373 return GridInfoCommand(tkwin, interp, objc, objv);
375 return GridLocationCommand(tkwin, interp, objc, objv);
377 return GridPropagateCommand(tkwin, interp, objc, objv);
379 return GridSizeCommand(tkwin, interp, objc, objv);
381 return GridSlavesCommand(tkwin, interp, objc, objv);
384 * Sample argument combinations:
385 * grid columnconfigure <master> <index> -option
386 * grid columnconfigure <master> <index> -option value -option value
387 * grid rowconfigure <master> <index>
388 * grid rowconfigure <master> <index> -option
389 * grid rowconfigure <master> <index> -option value -option value.
392 case GRID_COLUMNCONFIGURE:
393 case GRID_ROWCONFIGURE:
394 return GridRowColumnConfigureCommand(tkwin, interp, objc, objv);
397 /* This should not happen */
398 Tcl_SetResult(interp, "Internal error in grid.", TCL_STATIC);
403 *----------------------------------------------------------------------
407 * Implementation of the [grid bbox] subcommand.
410 * Standard Tcl result.
413 * Places bounding box information in the interp's result field.
415 *----------------------------------------------------------------------
419 GridBboxCommand(tkwin, interp, objc, objv)
420 Tk_Window tkwin; /* Main window of the application. */
421 Tcl_Interp *interp; /* Current interpreter. */
422 int objc; /* Number of arguments. */
423 Tcl_Obj *CONST objv[]; /* Argument objects. */
426 Gridder *masterPtr; /* master grid record */
427 GridMaster *gridPtr; /* pointer to grid data */
428 int row, column; /* origin for bounding box */
429 int row2, column2; /* end of bounding box */
430 int endX, endY; /* last column/row in the layout */
431 int x=0, y=0; /* starting pixels for this bounding box */
432 int width, height; /* size of the bounding box */
434 if (objc!=3 && objc != 5 && objc != 7) {
435 Tcl_WrongNumArgs(interp, 2, objv, "master ?column row ?column row??");
439 if (TkGetWindowFromObj(interp, tkwin, objv[2], &master) != TCL_OK) {
442 masterPtr = GetGrid(master);
445 if (Tcl_GetIntFromObj(interp, objv[3], &column) != TCL_OK) {
448 if (Tcl_GetIntFromObj(interp, objv[4], &row) != TCL_OK) {
456 if (Tcl_GetIntFromObj(interp, objv[5], &column2) != TCL_OK) {
459 if (Tcl_GetIntFromObj(interp, objv[6], &row2) != TCL_OK) {
464 gridPtr = masterPtr->masterDataPtr;
465 if (gridPtr == NULL) {
466 Tcl_SetObjResult(interp, NewQuadObj(interp, 0, 0, 0, 0));
470 SetGridSize(masterPtr);
471 endX = MAX(gridPtr->columnEnd, gridPtr->columnMax);
472 endY = MAX(gridPtr->rowEnd, gridPtr->rowMax);
474 if ((endX == 0) || (endY == 0)) {
475 Tcl_SetObjResult(interp, NewQuadObj(interp, 0, 0, 0, 0));
484 if (column > column2) {
486 column = column2, column2 = temp;
490 row = row2, row2 = temp;
493 if (column > 0 && column < endX) {
494 x = gridPtr->columnPtr[column-1].offset;
495 } else if (column > 0) {
496 x = gridPtr->columnPtr[endX-1].offset;
499 if (row > 0 && row < endY) {
500 y = gridPtr->rowPtr[row-1].offset;
501 } else if (row > 0) {
502 y = gridPtr->rowPtr[endY-1].offset;
507 } else if (column2 >= endX) {
508 width = gridPtr->columnPtr[endX-1].offset - x;
510 width = gridPtr->columnPtr[column2].offset - x;
515 } else if (row2 >= endY) {
516 height = gridPtr->rowPtr[endY-1].offset - y;
518 height = gridPtr->rowPtr[row2].offset - y;
521 Tcl_SetObjResult(interp, NewQuadObj(interp,
522 x + gridPtr->startX, y + gridPtr->startY, width, height));
527 *----------------------------------------------------------------------
529 * GridForgetRemoveCommand --
531 * Implementation of the [grid forget]/[grid remove] subcommands.
532 * See the user documentation for details on what these do.
535 * Standard Tcl result.
538 * Removes a window from a grid layout.
540 *----------------------------------------------------------------------
544 GridForgetRemoveCommand(tkwin, interp, objc, objv)
545 Tk_Window tkwin; /* Main window of the application. */
546 Tcl_Interp *interp; /* Current interpreter. */
547 int objc; /* Number of arguments. */
548 Tcl_Obj *CONST objv[]; /* Argument objects. */
553 char *string = Tcl_GetString(objv[1]);
556 for (i = 2; i < objc; i++) {
557 if (TkGetWindowFromObj(interp, tkwin, objv[i], &slave) != TCL_OK) {
561 slavePtr = GetGrid(slave);
562 if (slavePtr->masterPtr != NULL) {
565 * For "forget", reset all the settings to their defaults
569 slavePtr->column = slavePtr->row = -1;
570 slavePtr->numCols = 1;
571 slavePtr->numRows = 1;
572 slavePtr->padX = slavePtr->padY = 0;
573 slavePtr->padLeft = slavePtr->padTop = 0;
574 slavePtr->iPadX = slavePtr->iPadY = 0;
575 slavePtr->doubleBw = 2*Tk_Changes(tkwin)->border_width;
576 if (slavePtr->flags & REQUESTED_RELAYOUT) {
577 Tcl_CancelIdleCall(ArrangeGrid, (ClientData) slavePtr);
580 slavePtr->sticky = 0;
582 Tk_ManageGeometry(slave, (Tk_GeomMgr *) NULL,
584 if (slavePtr->masterPtr->tkwin != Tk_Parent(slavePtr->tkwin)) {
585 Tk_UnmaintainGeometry(slavePtr->tkwin,
586 slavePtr->masterPtr->tkwin);
589 Tk_UnmapWindow(slavePtr->tkwin);
596 *----------------------------------------------------------------------
600 * Implementation of the [grid info] subcommand. See the user
601 * documentation for details on what it does.
604 * Standard Tcl result.
607 * Puts gridding information in the interpreter's result.
609 *----------------------------------------------------------------------
613 GridInfoCommand(tkwin, interp, objc, objv)
614 Tk_Window tkwin; /* Main window of the application. */
615 Tcl_Interp *interp; /* Current interpreter. */
616 int objc; /* Number of arguments. */
617 Tcl_Obj *CONST objv[]; /* Argument objects. */
619 register Gridder *slavePtr;
621 char buffer[64 + TCL_INTEGER_SPACE * 4];
624 Tcl_WrongNumArgs(interp, 2, objv, "window");
627 if (TkGetWindowFromObj(interp, tkwin, objv[2], &slave) != TCL_OK) {
630 slavePtr = GetGrid(slave);
631 if (slavePtr->masterPtr == NULL) {
632 Tcl_ResetResult(interp);
636 Tcl_AppendElement(interp, "-in");
637 Tcl_AppendElement(interp, Tk_PathName(slavePtr->masterPtr->tkwin));
638 sprintf(buffer, " -column %d -row %d -columnspan %d -rowspan %d",
639 slavePtr->column, slavePtr->row,
640 slavePtr->numCols, slavePtr->numRows);
641 Tcl_AppendResult(interp, buffer, (char *) NULL);
642 TkPrintPadAmount(interp, "ipadx", slavePtr->iPadX/2, slavePtr->iPadX);
643 TkPrintPadAmount(interp, "ipady", slavePtr->iPadY/2, slavePtr->iPadY);
644 TkPrintPadAmount(interp, "padx", slavePtr->padLeft, slavePtr->padX);
645 TkPrintPadAmount(interp, "pady", slavePtr->padTop, slavePtr->padY);
646 StickyToString(slavePtr->sticky, buffer);
647 Tcl_AppendResult(interp, " -sticky ", buffer, (char *) NULL);
652 *----------------------------------------------------------------------
654 * GridLocationCommand --
656 * Implementation of the [grid location] subcommand. See the user
657 * documentation for details on what it does.
660 * Standard Tcl result.
663 * Puts location information in the interpreter's result field.
665 *----------------------------------------------------------------------
669 GridLocationCommand(tkwin, interp, objc, objv)
670 Tk_Window tkwin; /* Main window of the application. */
671 Tcl_Interp *interp; /* Current interpreter. */
672 int objc; /* Number of arguments. */
673 Tcl_Obj *CONST objv[]; /* Argument objects. */
676 Gridder *masterPtr; /* master grid record */
677 GridMaster *gridPtr; /* pointer to grid data */
678 register SlotInfo *slotPtr;
679 int x, y; /* Offset in pixels, from edge of parent. */
680 int i, j; /* Corresponding column and row indeces. */
681 int endX, endY; /* end of grid */
684 Tcl_WrongNumArgs(interp, 2, objv, "master x y");
688 if (TkGetWindowFromObj(interp, tkwin, objv[2], &master) != TCL_OK) {
692 if (Tk_GetPixelsFromObj(interp, master, objv[3], &x) != TCL_OK) {
695 if (Tk_GetPixelsFromObj(interp, master, objv[4], &y) != TCL_OK) {
699 masterPtr = GetGrid(master);
700 if (masterPtr->masterDataPtr == NULL) {
701 Tcl_SetObjResult(interp, NewPairObj(interp, -1, -1));
704 gridPtr = masterPtr->masterDataPtr;
707 * Update any pending requests. This is not always the
708 * steady state value, as more configure events could be in
709 * the pipeline, but its as close as its easy to get.
712 while (masterPtr->flags & REQUESTED_RELAYOUT) {
713 Tcl_CancelIdleCall(ArrangeGrid, (ClientData) masterPtr);
714 ArrangeGrid ((ClientData) masterPtr);
716 SetGridSize(masterPtr);
717 endX = MAX(gridPtr->columnEnd, gridPtr->columnMax);
718 endY = MAX(gridPtr->rowEnd, gridPtr->rowMax);
720 slotPtr = masterPtr->masterDataPtr->columnPtr;
721 if (x < masterPtr->masterDataPtr->startX) {
724 x -= masterPtr->masterDataPtr->startX;
725 for (i = 0; slotPtr[i].offset < x && i < endX; i++) {
730 slotPtr = masterPtr->masterDataPtr->rowPtr;
731 if (y < masterPtr->masterDataPtr->startY) {
734 y -= masterPtr->masterDataPtr->startY;
735 for (j = 0; slotPtr[j].offset < y && j < endY; j++) {
740 Tcl_SetObjResult(interp, NewPairObj(interp, i, j));
745 *----------------------------------------------------------------------
747 * GridPropagateCommand --
749 * Implementation of the [grid propagate] subcommand. See the user
750 * documentation for details on what it does.
753 * Standard Tcl result.
756 * May alter geometry propagation for a widget.
758 *----------------------------------------------------------------------
762 GridPropagateCommand(tkwin, interp, objc, objv)
763 Tk_Window tkwin; /* Main window of the application. */
764 Tcl_Interp *interp; /* Current interpreter. */
765 int objc; /* Number of arguments. */
766 Tcl_Obj *CONST objv[]; /* Argument objects. */
773 Tcl_WrongNumArgs(interp, 2, objv, "window ?boolean?");
777 if (TkGetWindowFromObj(interp, tkwin, objv[2], &master) != TCL_OK) {
780 masterPtr = GetGrid(master);
782 Tcl_SetObjResult(interp,
783 Tcl_NewBooleanObj(!(masterPtr->flags & DONT_PROPAGATE)));
786 if (Tcl_GetBooleanFromObj(interp, objv[3], &propagate) != TCL_OK) {
790 /* Only request a relayout if the propagation bit changes */
792 old = !(masterPtr->flags & DONT_PROPAGATE);
793 if (propagate != old) {
795 masterPtr->flags &= ~DONT_PROPAGATE;
797 masterPtr->flags |= DONT_PROPAGATE;
801 * Re-arrange the master to allow new geometry information to
802 * propagate upwards to the master's master.
805 if (masterPtr->abortPtr != NULL) {
806 *masterPtr->abortPtr = 1;
808 if (!(masterPtr->flags & REQUESTED_RELAYOUT)) {
809 masterPtr->flags |= REQUESTED_RELAYOUT;
810 Tcl_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
817 *----------------------------------------------------------------------
819 * GridRowColumnConfigureCommand --
821 * Implementation of the [grid rowconfigure] and [grid columnconfigure]
822 * subcommands. See the user documentation for details on what these
826 * Standard Tcl result.
829 * Depends on arguments; see user documentation.
831 *----------------------------------------------------------------------
835 GridRowColumnConfigureCommand(tkwin, interp, objc, objv)
836 Tk_Window tkwin; /* Main window of the application. */
837 Tcl_Interp *interp; /* Current interpreter. */
838 int objc; /* Number of arguments. */
839 Tcl_Obj *CONST objv[]; /* Argument objects. */
843 SlotInfo *slotPtr = NULL;
844 int slot; /* the column or row number */
845 int slotType; /* COLUMN or ROW */
846 int size; /* the configuration value */
847 int checkOnly; /* check the size only */
848 int lObjc; /* Number of items in index list */
849 Tcl_Obj **lObjv; /* array of indices */
850 int ok; /* temporary TCL result code */
853 static CONST char *optionStrings[] = {
854 "-minsize", "-pad", "-uniform", "-weight", (char *) NULL };
855 enum options { ROWCOL_MINSIZE, ROWCOL_PAD, ROWCOL_UNIFORM, ROWCOL_WEIGHT };
858 if (((objc % 2 != 0) && (objc > 6)) || (objc < 4)) {
859 Tcl_WrongNumArgs(interp, 2, objv, "master index ?-option value...?");
863 if (TkGetWindowFromObj(interp, tkwin, objv[2], &master) != TCL_OK) {
867 if (Tcl_ListObjGetElements(interp, objv[3], &lObjc, &lObjv) != TCL_OK) {
871 string = Tcl_GetString(objv[1]);
872 checkOnly = ((objc == 4) || (objc == 5));
873 masterPtr = GetGrid(master);
874 slotType = (*string == 'c') ? COLUMN : ROW;
875 if (checkOnly && lObjc > 1) {
876 Tcl_AppendResult(interp, Tcl_GetString(objv[3]),
877 " must be a single element.", (char *) NULL);
880 for (j = 0; j < lObjc; j++) {
881 if (Tcl_GetIntFromObj(interp, lObjv[j], &slot) != TCL_OK) {
884 ok = CheckSlotData(masterPtr, slot, slotType, checkOnly);
885 if ((ok != TCL_OK) && ((objc < 4) || (objc > 5))) {
886 Tcl_AppendResult(interp, Tcl_GetString(objv[0]), " ",
887 Tcl_GetString(objv[1]), ": \"", Tcl_GetString(lObjv[j]),
888 "\" is out of range", (char *) NULL);
890 } else if (ok == TCL_OK) {
891 slotPtr = (slotType == COLUMN) ?
892 masterPtr->masterDataPtr->columnPtr :
893 masterPtr->masterDataPtr->rowPtr;
897 * Return all of the options for this row or column. If the
898 * request is out of range, return all 0's.
902 int minsize = 0, pad = 0, weight = 0;
903 Tk_Uid uniform = NULL;
904 Tcl_Obj *res = Tcl_NewListObj(0, NULL);
907 minsize = slotPtr[slot].minSize;
908 pad = slotPtr[slot].pad;
909 weight = slotPtr[slot].weight;
910 uniform = slotPtr[slot].uniform;
913 Tcl_ListObjAppendElement(interp, res,
914 Tcl_NewStringObj("-minsize", -1));
915 Tcl_ListObjAppendElement(interp, res, Tcl_NewIntObj(minsize));
916 Tcl_ListObjAppendElement(interp, res,
917 Tcl_NewStringObj("-pad", -1));
918 Tcl_ListObjAppendElement(interp, res, Tcl_NewIntObj(pad));
919 Tcl_ListObjAppendElement(interp, res,
920 Tcl_NewStringObj("-uniform", -1));
921 Tcl_ListObjAppendElement(interp, res,
922 Tcl_NewStringObj(uniform == NULL ? "" : uniform, -1));
923 Tcl_ListObjAppendElement(interp, res,
924 Tcl_NewStringObj("-weight", -1));
925 Tcl_ListObjAppendElement(interp, res, Tcl_NewIntObj(weight));
926 Tcl_SetObjResult(interp, res);
931 * Loop through each option value pair, setting the values as
932 * required. If only one option is given, with no value, the
933 * current value is returned.
936 for (i = 4; i < objc; i += 2) {
937 if (Tcl_GetIndexFromObj(interp, objv[i], optionStrings, "option", 0,
941 if (index == ROWCOL_MINSIZE) {
943 Tcl_SetObjResult(interp, Tcl_NewIntObj(
944 (ok == TCL_OK) ? slotPtr[slot].minSize : 0));
945 } else if (Tk_GetPixelsFromObj(interp, master, objv[i+1], &size)
949 slotPtr[slot].minSize = size;
952 else if (index == ROWCOL_WEIGHT) {
955 Tcl_SetObjResult(interp, Tcl_NewIntObj(
956 (ok == TCL_OK) ? slotPtr[slot].weight : 0));
957 } else if (Tcl_GetIntFromObj(interp, objv[i+1], &wt)
961 Tcl_AppendResult(interp, "invalid arg \"",
962 Tcl_GetString(objv[i]),
963 "\": should be non-negative", (char *) NULL);
966 slotPtr[slot].weight = wt;
969 else if (index == ROWCOL_UNIFORM) {
972 value = (ok == TCL_OK) ? slotPtr[slot].uniform : "";
976 Tcl_SetObjResult(interp, Tcl_NewStringObj(value, -1));
978 slotPtr[slot].uniform = Tk_GetUid(Tcl_GetString(objv[i+1]));
979 if (slotPtr[slot].uniform != NULL &&
980 slotPtr[slot].uniform[0] == 0) {
981 slotPtr[slot].uniform = NULL;
985 else if (index == ROWCOL_PAD) {
987 Tcl_SetObjResult(interp, Tcl_NewIntObj(
988 (ok == TCL_OK) ? slotPtr[slot].pad : 0));
989 } else if (Tk_GetPixelsFromObj(interp, master, objv[i+1], &size)
992 } else if (size < 0) {
993 Tcl_AppendResult(interp, "invalid arg \"",
994 Tcl_GetString(objv[i]),
995 "\": should be non-negative", (char *) NULL);
998 slotPtr[slot].pad = size;
1005 * If we changed a property, re-arrange the table,
1006 * and check for constraint shrinkage.
1010 if (slotType == ROW) {
1011 int last = masterPtr->masterDataPtr->rowMax - 1;
1012 while ((last >= 0) && (slotPtr[last].weight == 0)
1013 && (slotPtr[last].pad == 0)
1014 && (slotPtr[last].minSize == 0)
1015 && (slotPtr[last].uniform == NULL)) {
1018 masterPtr->masterDataPtr->rowMax = last+1;
1020 int last = masterPtr->masterDataPtr->columnMax - 1;
1021 while ((last >= 0) && (slotPtr[last].weight == 0)
1022 && (slotPtr[last].pad == 0)
1023 && (slotPtr[last].minSize == 0)
1024 && (slotPtr[last].uniform == NULL)) {
1027 masterPtr->masterDataPtr->columnMax = last + 1;
1030 if (masterPtr->abortPtr != NULL) {
1031 *masterPtr->abortPtr = 1;
1033 if (!(masterPtr->flags & REQUESTED_RELAYOUT)) {
1034 masterPtr->flags |= REQUESTED_RELAYOUT;
1035 Tcl_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
1042 *----------------------------------------------------------------------
1044 * GridSizeCommand --
1046 * Implementation of the [grid size] subcommand. See the user
1047 * documentation for details on what it does.
1050 * Standard Tcl result.
1053 * Puts grid size information in the interpreter's result.
1055 *----------------------------------------------------------------------
1059 GridSizeCommand(tkwin, interp, objc, objv)
1060 Tk_Window tkwin; /* Main window of the application. */
1061 Tcl_Interp *interp; /* Current interpreter. */
1062 int objc; /* Number of arguments. */
1063 Tcl_Obj *CONST objv[]; /* Argument objects. */
1067 GridMaster *gridPtr; /* pointer to grid data */
1070 Tcl_WrongNumArgs(interp, 2, objv, "window");
1074 if (TkGetWindowFromObj(interp, tkwin, objv[2], &master) != TCL_OK) {
1077 masterPtr = GetGrid(master);
1079 if (masterPtr->masterDataPtr != NULL) {
1080 SetGridSize(masterPtr);
1081 gridPtr = masterPtr->masterDataPtr;
1082 Tcl_SetObjResult(interp, NewPairObj(interp,
1083 MAX(gridPtr->columnEnd, gridPtr->columnMax),
1084 MAX(gridPtr->rowEnd, gridPtr->rowMax)));
1086 Tcl_SetObjResult(interp, NewPairObj(interp, 0, 0));
1092 *----------------------------------------------------------------------
1094 * GridSlavesCommand --
1096 * Implementation of the [grid slaves] subcommand. See the user
1097 * documentation for details on what it does.
1100 * Standard Tcl result.
1103 * Places a list of slaves of the specified window in the
1104 * interpreter's result field.
1106 *----------------------------------------------------------------------
1110 GridSlavesCommand(tkwin, interp, objc, objv)
1111 Tk_Window tkwin; /* Main window of the application. */
1112 Tcl_Interp *interp; /* Current interpreter. */
1113 int objc; /* Number of arguments. */
1114 Tcl_Obj *CONST objv[]; /* Argument objects. */
1117 Gridder *masterPtr; /* master grid record */
1120 int row = -1, column = -1;
1121 static CONST char *optionStrings[] = {
1122 "-column", "-row", (char *) NULL };
1123 enum options { SLAVES_COLUMN, SLAVES_ROW };
1127 if ((objc < 3) || ((objc % 2) == 0)) {
1128 Tcl_WrongNumArgs(interp, 2, objv, "window ?-option value...?");
1132 for (i = 3; i < objc; i += 2) {
1133 if (Tcl_GetIndexFromObj(interp, objv[i], optionStrings, "option", 0,
1134 &index) != TCL_OK) {
1137 if (Tcl_GetIntFromObj(interp, objv[i+1], &value) != TCL_OK) {
1141 Tcl_AppendResult(interp, Tcl_GetString(objv[i]),
1142 " is an invalid value: should NOT be < 0",
1146 if (index == SLAVES_COLUMN) {
1153 if (TkGetWindowFromObj(interp, tkwin, objv[2], &master) != TCL_OK) {
1156 masterPtr = GetGrid(master);
1158 res = Tcl_NewListObj(0, NULL);
1159 for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
1160 slavePtr = slavePtr->nextPtr) {
1161 if (column>=0 && (slavePtr->column > column
1162 || slavePtr->column+slavePtr->numCols-1 < column)) {
1165 if (row>=0 && (slavePtr->row > row ||
1166 slavePtr->row+slavePtr->numRows-1 < row)) {
1169 Tcl_ListObjAppendElement(interp, res,
1170 Tcl_NewStringObj(Tk_PathName(slavePtr->tkwin), -1));
1172 Tcl_SetObjResult(interp, res);
1177 *--------------------------------------------------------------
1181 * This procedure is invoked by Tk_GeometryRequest for
1182 * windows managed by the grid.
1188 * Arranges for tkwin, and all its managed siblings, to
1189 * be re-arranged at the next idle point.
1191 *--------------------------------------------------------------
1195 GridReqProc(clientData, tkwin)
1196 ClientData clientData; /* Grid's information about
1197 * window that got new preferred
1199 Tk_Window tkwin; /* Other Tk-related information
1200 * about the window. */
1202 register Gridder *gridPtr = (Gridder *) clientData;
1204 gridPtr = gridPtr->masterPtr;
1205 if (gridPtr && !(gridPtr->flags & REQUESTED_RELAYOUT)) {
1206 gridPtr->flags |= REQUESTED_RELAYOUT;
1207 Tcl_DoWhenIdle(ArrangeGrid, (ClientData) gridPtr);
1212 *--------------------------------------------------------------
1214 * GridLostSlaveProc --
1216 * This procedure is invoked by Tk whenever some other geometry
1217 * claims control over a slave that used to be managed by us.
1223 * Forgets all grid-related information about the slave.
1225 *--------------------------------------------------------------
1229 GridLostSlaveProc(clientData, tkwin)
1230 ClientData clientData; /* Grid structure for slave window that
1231 * was stolen away. */
1232 Tk_Window tkwin; /* Tk's handle for the slave window. */
1234 register Gridder *slavePtr = (Gridder *) clientData;
1236 if (slavePtr->masterPtr->tkwin != Tk_Parent(slavePtr->tkwin)) {
1237 Tk_UnmaintainGeometry(slavePtr->tkwin, slavePtr->masterPtr->tkwin);
1240 Tk_UnmapWindow(slavePtr->tkwin);
1244 *--------------------------------------------------------------
1248 * This procedure adjusts the size of the layout to fit in the
1249 * space provided. If it needs more space, the extra is added
1250 * according to the weights. If it needs less, the space is removed
1251 * according to the weights, but at no time does the size drop below
1252 * the minsize specified for that slot.
1255 * The initial offset of the layout,
1256 * if all the weights are zero, else 0.
1259 * The slot offsets are modified to shrink the layout.
1261 *--------------------------------------------------------------
1265 AdjustOffsets(size, slots, slotPtr)
1266 int size; /* The total layout size (in pixels). */
1267 int slots; /* Number of slots. */
1268 register SlotInfo *slotPtr; /* Pointer to slot array. */
1270 register int slot; /* Current slot. */
1271 int diff; /* Extra pixels needed to add to the layout. */
1272 int totalWeight = 0; /* Sum of the weights for all the slots. */
1273 int weight = 0; /* Sum of the weights so far. */
1274 int minSize = 0; /* Minimum possible layout size. */
1275 int newDiff; /* The most pixels that can be added on
1276 * the current pass. */
1278 diff = size - slotPtr[slots-1].offset;
1281 * The layout is already the correct size; all done.
1289 * If all the weights are zero, center the layout in its parent if
1290 * there is extra space, else clip on the bottom/right.
1293 for (slot=0; slot < slots; slot++) {
1294 totalWeight += slotPtr[slot].weight;
1297 if (totalWeight == 0 ) {
1298 return(diff > 0 ? diff/2 : 0);
1302 * Add extra space according to the slot weights. This is done
1303 * cumulatively to prevent round-off error accumulation.
1307 for (weight=slot=0; slot < slots; slot++) {
1308 weight += slotPtr[slot].weight;
1309 slotPtr[slot].offset += diff * weight / totalWeight;
1315 * The layout must shrink below its requested size. Compute the
1316 * minimum possible size by looking at the slot minSizes.
1319 for (slot=0; slot < slots; slot++) {
1320 if (slotPtr[slot].weight > 0) {
1321 minSize += slotPtr[slot].minSize;
1322 } else if (slot > 0) {
1323 minSize += slotPtr[slot].offset - slotPtr[slot-1].offset;
1325 minSize += slotPtr[slot].offset;
1330 * If the requested size is less than the minimum required size,
1331 * set the slot sizes to their minimum values, then clip on the
1335 if (size <= minSize) {
1337 for (slot=0; slot < slots; slot++) {
1338 if (slotPtr[slot].weight > 0) {
1339 offset += slotPtr[slot].minSize;
1340 } else if (slot > 0) {
1341 offset += slotPtr[slot].offset - slotPtr[slot-1].offset;
1343 offset += slotPtr[slot].offset;
1345 slotPtr[slot].offset = offset;
1351 * Remove space from slots according to their weights. The weights
1352 * get renormalized anytime a slot shrinks to its minimum size.
1358 * Find the total weight for the shrinkable slots.
1361 for (totalWeight=slot=0; slot < slots; slot++) {
1362 int current = (slot == 0) ? slotPtr[slot].offset :
1363 slotPtr[slot].offset - slotPtr[slot-1].offset;
1364 if (current > slotPtr[slot].minSize) {
1365 totalWeight += slotPtr[slot].weight;
1366 slotPtr[slot].temp = slotPtr[slot].weight;
1368 slotPtr[slot].temp = 0;
1371 if (totalWeight == 0) {
1376 * Find the maximum amount of space we can distribute this pass.
1380 for (slot = 0; slot < slots; slot++) {
1381 int current; /* current size of this slot */
1382 int maxDiff; /* max diff that would cause
1383 * this slot to equal its minsize */
1384 if (slotPtr[slot].temp == 0) {
1387 current = (slot == 0) ? slotPtr[slot].offset :
1388 slotPtr[slot].offset - slotPtr[slot-1].offset;
1389 maxDiff = totalWeight * (slotPtr[slot].minSize - current)
1390 / slotPtr[slot].temp;
1391 if (maxDiff > newDiff) {
1397 * Now distribute the space.
1400 for (weight=slot=0; slot < slots; slot++) {
1401 weight += slotPtr[slot].temp;
1402 slotPtr[slot].offset += newDiff * weight / totalWeight;
1410 *--------------------------------------------------------------
1412 * AdjustForSticky --
1414 * This procedure adjusts the size of a slave in its cavity based
1415 * on its "sticky" flags.
1418 * The input x, y, width, and height are changed to represent the
1419 * desired coordinates of the slave.
1424 *--------------------------------------------------------------
1428 AdjustForSticky(slavePtr, xPtr, yPtr, widthPtr, heightPtr)
1429 Gridder *slavePtr; /* Slave window to arrange in its cavity. */
1430 int *xPtr; /* Pixel location of the left edge of the cavity. */
1431 int *yPtr; /* Pixel location of the top edge of the cavity. */
1432 int *widthPtr; /* Width of the cavity (in pixels). */
1433 int *heightPtr; /* Height of the cavity (in pixels). */
1435 int diffx=0; /* Cavity width - slave width. */
1436 int diffy=0; /* Cavity hight - slave height. */
1437 int sticky = slavePtr->sticky;
1439 *xPtr += slavePtr->padLeft;
1440 *widthPtr -= slavePtr->padX;
1441 *yPtr += slavePtr->padTop;
1442 *heightPtr -= slavePtr->padY;
1444 if (*widthPtr > (Tk_ReqWidth(slavePtr->tkwin) + slavePtr->iPadX)) {
1445 diffx = *widthPtr - (Tk_ReqWidth(slavePtr->tkwin) + slavePtr->iPadX);
1446 *widthPtr = Tk_ReqWidth(slavePtr->tkwin) + slavePtr->iPadX;
1449 if (*heightPtr > (Tk_ReqHeight(slavePtr->tkwin) + slavePtr->iPadY)) {
1450 diffy = *heightPtr - (Tk_ReqHeight(slavePtr->tkwin) + slavePtr->iPadY);
1451 *heightPtr = Tk_ReqHeight(slavePtr->tkwin) + slavePtr->iPadY;
1454 if (sticky&STICK_EAST && sticky&STICK_WEST) {
1457 if (sticky&STICK_NORTH && sticky&STICK_SOUTH) {
1458 *heightPtr += diffy;
1460 if (!(sticky&STICK_WEST)) {
1461 *xPtr += (sticky&STICK_EAST) ? diffx : diffx/2;
1463 if (!(sticky&STICK_NORTH)) {
1464 *yPtr += (sticky&STICK_SOUTH) ? diffy : diffy/2;
1469 *--------------------------------------------------------------
1473 * This procedure is invoked (using the Tcl_DoWhenIdle
1474 * mechanism) to re-layout a set of windows managed by
1475 * the grid. It is invoked at idle time so that a
1476 * series of grid requests can be merged into a single
1483 * The slaves of masterPtr may get resized or moved.
1485 *--------------------------------------------------------------
1489 ArrangeGrid(clientData)
1490 ClientData clientData; /* Structure describing parent whose slaves
1491 * are to be re-layed out. */
1493 register Gridder *masterPtr = (Gridder *) clientData;
1494 register Gridder *slavePtr;
1495 GridMaster *slotPtr = masterPtr->masterDataPtr;
1497 int width, height; /* requested size of layout, in pixels */
1498 int realWidth, realHeight; /* actual size layout should take-up */
1500 masterPtr->flags &= ~REQUESTED_RELAYOUT;
1503 * If the parent has no slaves anymore, then don't do anything
1504 * at all: just leave the parent's size as-is. Otherwise there is
1505 * no way to "relinquish" control over the parent so another geometry
1506 * manager can take over.
1509 if (masterPtr->slavePtr == NULL) {
1513 if (masterPtr->masterDataPtr == NULL) {
1518 * Abort any nested call to ArrangeGrid for this window, since
1519 * we'll do everything necessary here, and set up so this call
1520 * can be aborted if necessary.
1523 if (masterPtr->abortPtr != NULL) {
1524 *masterPtr->abortPtr = 1;
1526 masterPtr->abortPtr = &abort;
1528 Tcl_Preserve((ClientData) masterPtr);
1531 * Call the constraint engine to fill in the row and column offsets.
1534 SetGridSize(masterPtr);
1535 width = ResolveConstraints(masterPtr, COLUMN, 0);
1536 height = ResolveConstraints(masterPtr, ROW, 0);
1537 width += Tk_InternalBorderLeft(masterPtr->tkwin) +
1538 Tk_InternalBorderRight(masterPtr->tkwin);
1539 height += Tk_InternalBorderTop(masterPtr->tkwin) +
1540 Tk_InternalBorderBottom(masterPtr->tkwin);
1542 if (width < Tk_MinReqWidth(masterPtr->tkwin)) {
1543 width = Tk_MinReqWidth(masterPtr->tkwin);
1545 if (height < Tk_MinReqHeight(masterPtr->tkwin)) {
1546 height = Tk_MinReqHeight(masterPtr->tkwin);
1549 if (((width != Tk_ReqWidth(masterPtr->tkwin))
1550 || (height != Tk_ReqHeight(masterPtr->tkwin)))
1551 && !(masterPtr->flags & DONT_PROPAGATE)) {
1552 Tk_GeometryRequest(masterPtr->tkwin, width, height);
1553 if (width>1 && height>1) {
1554 masterPtr->flags |= REQUESTED_RELAYOUT;
1555 Tcl_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
1557 masterPtr->abortPtr = NULL;
1558 Tcl_Release((ClientData) masterPtr);
1563 * If the currently requested layout size doesn't match the parent's
1564 * window size, then adjust the slot offsets according to the
1565 * weights. If all of the weights are zero, center the layout in
1566 * its parent. I haven't decided what to do if the parent is smaller
1567 * than the requested size.
1570 realWidth = Tk_Width(masterPtr->tkwin) -
1571 Tk_InternalBorderLeft(masterPtr->tkwin) -
1572 Tk_InternalBorderRight(masterPtr->tkwin);
1573 realHeight = Tk_Height(masterPtr->tkwin) -
1574 Tk_InternalBorderTop(masterPtr->tkwin) -
1575 Tk_InternalBorderBottom(masterPtr->tkwin);
1576 slotPtr->startX = AdjustOffsets(realWidth,
1577 MAX(slotPtr->columnEnd,slotPtr->columnMax), slotPtr->columnPtr);
1578 slotPtr->startY = AdjustOffsets(realHeight,
1579 MAX(slotPtr->rowEnd,slotPtr->rowMax), slotPtr->rowPtr);
1580 slotPtr->startX += Tk_InternalBorderLeft(masterPtr->tkwin);
1581 slotPtr->startY += Tk_InternalBorderTop(masterPtr->tkwin);
1584 * Now adjust the actual size of the slave to its cavity by
1585 * computing the cavity size, and adjusting the widget according
1586 * to its stickyness.
1589 for (slavePtr = masterPtr->slavePtr; slavePtr != NULL && !abort;
1590 slavePtr = slavePtr->nextPtr) {
1591 int x, y; /* top left coordinate */
1592 int width, height; /* slot or slave size */
1593 int col = slavePtr->column;
1594 int row = slavePtr->row;
1596 x = (col>0) ? slotPtr->columnPtr[col-1].offset : 0;
1597 y = (row>0) ? slotPtr->rowPtr[row-1].offset : 0;
1599 width = slotPtr->columnPtr[slavePtr->numCols+col-1].offset - x;
1600 height = slotPtr->rowPtr[slavePtr->numRows+row-1].offset - y;
1602 x += slotPtr->startX;
1603 y += slotPtr->startY;
1605 AdjustForSticky(slavePtr, &x, &y, &width, &height);
1608 * Now put the window in the proper spot. (This was taken directly
1609 * from tkPack.c.) If the slave is a child of the master, then
1610 * do this here. Otherwise let Tk_MaintainGeometry do the work.
1613 if (masterPtr->tkwin == Tk_Parent(slavePtr->tkwin)) {
1614 if ((width <= 0) || (height <= 0)) {
1615 Tk_UnmapWindow(slavePtr->tkwin);
1617 if ((x != Tk_X(slavePtr->tkwin))
1618 || (y != Tk_Y(slavePtr->tkwin))
1619 || (width != Tk_Width(slavePtr->tkwin))
1620 || (height != Tk_Height(slavePtr->tkwin))) {
1621 Tk_MoveResizeWindow(slavePtr->tkwin, x, y, width, height);
1628 * Don't map the slave if the master isn't mapped: wait
1629 * until the master gets mapped later.
1632 if (Tk_IsMapped(masterPtr->tkwin)) {
1633 Tk_MapWindow(slavePtr->tkwin);
1637 if ((width <= 0) || (height <= 0)) {
1638 Tk_UnmaintainGeometry(slavePtr->tkwin, masterPtr->tkwin);
1639 Tk_UnmapWindow(slavePtr->tkwin);
1641 Tk_MaintainGeometry(slavePtr->tkwin, masterPtr->tkwin,
1642 x, y, width, height);
1647 masterPtr->abortPtr = NULL;
1648 Tcl_Release((ClientData) masterPtr);
1652 *--------------------------------------------------------------
1654 * ResolveConstraints --
1656 * Resolve all of the column and row boundaries. Most of
1657 * the calculations are identical for rows and columns, so this procedure
1658 * is called twice, once for rows, and again for columns.
1661 * The offset (in pixels) from the left/top edge of this layout is
1665 * The slot offsets are copied into the SlotInfo structure for the
1668 *--------------------------------------------------------------
1672 ResolveConstraints(masterPtr, slotType, maxOffset)
1673 Gridder *masterPtr; /* The geometry master for this grid. */
1674 int slotType; /* Either ROW or COLUMN. */
1675 int maxOffset; /* The actual maximum size of this layout
1676 * in pixels, or 0 (not currently used). */
1678 register SlotInfo *slotPtr; /* Pointer to row/col constraints. */
1679 register Gridder *slavePtr; /* List of slave windows in this grid. */
1680 int constraintCount; /* Count of rows or columns that have
1682 int slotCount; /* Last occupied row or column. */
1683 int gridCount; /* The larger of slotCount and constraintCount.
1685 GridLayout *layoutPtr; /* Temporary layout structure. */
1686 int requiredSize; /* The natural size of the grid (pixels).
1687 * This is the minimum size needed to
1688 * accomodate all of the slaves at their
1689 * requested sizes. */
1690 int offset; /* The pixel offset of the right edge of the
1691 * current slot from the beginning of the
1693 int slot; /* The current slot. */
1694 int start; /* The first slot of a contiguous set whose
1695 * constraints are not yet fully resolved. */
1696 int end; /* The Last slot of a contiguous set whose
1697 * constraints are not yet fully resolved. */
1698 UniformGroup uniformPre[UNIFORM_PREALLOC];
1699 /* Pre-allocated space for uniform groups. */
1700 UniformGroup *uniformGroupPtr;
1701 /* Uniform groups data. */
1702 int uniformGroups; /* Number of currently used uniform groups. */
1703 int uniformGroupsAlloced; /* Size of allocated space for uniform groups.
1705 int weight, minSize;
1708 * For typical sized tables, we'll use stack space for the layout data
1709 * to avoid the overhead of a malloc and free for every layout.
1712 GridLayout layoutData[TYPICAL_SIZE + 1];
1714 if (slotType == COLUMN) {
1715 constraintCount = masterPtr->masterDataPtr->columnMax;
1716 slotCount = masterPtr->masterDataPtr->columnEnd;
1717 slotPtr = masterPtr->masterDataPtr->columnPtr;
1719 constraintCount = masterPtr->masterDataPtr->rowMax;
1720 slotCount = masterPtr->masterDataPtr->rowEnd;
1721 slotPtr = masterPtr->masterDataPtr->rowPtr;
1725 * Make sure there is enough memory for the layout.
1728 gridCount = MAX(constraintCount,slotCount);
1729 if (gridCount >= TYPICAL_SIZE) {
1730 layoutPtr = (GridLayout *) ckalloc(sizeof(GridLayout) * (1+gridCount));
1732 layoutPtr = layoutData;
1736 * Allocate an extra layout slot to represent the left/top edge of
1737 * the 0th slot to make it easier to calculate slot widths from
1738 * offsets without special case code.
1739 * Initialize the "dummy" slot to the left/top of the table.
1740 * This slot avoids special casing the first slot.
1743 layoutPtr->minOffset = 0;
1744 layoutPtr->maxOffset = 0;
1749 * Copy the slot constraints into the layout structure,
1750 * and initialize the rest of the fields.
1753 for (slot=0; slot < constraintCount; slot++) {
1754 layoutPtr[slot].minSize = slotPtr[slot].minSize;
1755 layoutPtr[slot].weight = slotPtr[slot].weight;
1756 layoutPtr[slot].uniform = slotPtr[slot].uniform;
1757 layoutPtr[slot].pad = slotPtr[slot].pad;
1758 layoutPtr[slot].binNextPtr = NULL;
1760 for(;slot<gridCount;slot++) {
1761 layoutPtr[slot].minSize = 0;
1762 layoutPtr[slot].weight = 0;
1763 layoutPtr[slot].uniform = NULL;
1764 layoutPtr[slot].pad = 0;
1765 layoutPtr[slot].binNextPtr = NULL;
1770 * Slaves with a span of 1 are used to determine the minimum size of
1771 * each slot. Slaves whose span is two or more slots don't
1772 * contribute to the minimum size of each slot directly, but can cause
1773 * slots to grow if their size exceeds the the sizes of the slots they
1776 * Bin all slaves whose spans are > 1 by their right edges. This
1777 * allows the computation on minimum and maximum possible layout
1778 * sizes at each slot boundary, without the need to re-sort the slaves.
1783 for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
1784 slavePtr = slavePtr->nextPtr) {
1785 int rightEdge = slavePtr->column + slavePtr->numCols - 1;
1786 slavePtr->size = Tk_ReqWidth(slavePtr->tkwin) +
1787 slavePtr->padX + slavePtr->iPadX + slavePtr->doubleBw;
1788 if (slavePtr->numCols > 1) {
1789 slavePtr->binNextPtr = layoutPtr[rightEdge].binNextPtr;
1790 layoutPtr[rightEdge].binNextPtr = slavePtr;
1792 int size = slavePtr->size + layoutPtr[rightEdge].pad;
1793 if (size > layoutPtr[rightEdge].minSize) {
1794 layoutPtr[rightEdge].minSize = size;
1800 for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
1801 slavePtr = slavePtr->nextPtr) {
1802 int rightEdge = slavePtr->row + slavePtr->numRows - 1;
1803 slavePtr->size = Tk_ReqHeight(slavePtr->tkwin) +
1804 slavePtr->padY + slavePtr->iPadY + slavePtr->doubleBw;
1805 if (slavePtr->numRows > 1) {
1806 slavePtr->binNextPtr = layoutPtr[rightEdge].binNextPtr;
1807 layoutPtr[rightEdge].binNextPtr = slavePtr;
1809 int size = slavePtr->size + layoutPtr[rightEdge].pad;
1810 if (size > layoutPtr[rightEdge].minSize) {
1811 layoutPtr[rightEdge].minSize = size;
1820 * Consider demands on uniform sizes.
1823 uniformGroupPtr = uniformPre;
1824 uniformGroupsAlloced = UNIFORM_PREALLOC;
1827 for (slot = 0; slot < gridCount; slot++) {
1828 if (layoutPtr[slot].uniform != NULL) {
1829 for (start = 0; start < uniformGroups; start++) {
1830 if (uniformGroupPtr[start].group == layoutPtr[slot].uniform) {
1834 if (start >= uniformGroups) {
1836 * Have not seen that group before, set up data for it.
1839 if (uniformGroups >= uniformGroupsAlloced) {
1841 * We need to allocate more space.
1844 size_t oldSize = uniformGroupsAlloced
1845 * sizeof(UniformGroup);
1846 size_t newSize = (uniformGroupsAlloced + UNIFORM_PREALLOC)
1847 * sizeof(UniformGroup);
1848 UniformGroup *new = (UniformGroup *) ckalloc(newSize);
1849 UniformGroup *old = uniformGroupPtr;
1850 memcpy((VOID *) new, (VOID *) old, oldSize);
1851 if (old != uniformPre) {
1852 ckfree((char *) old);
1854 uniformGroupPtr = new;
1855 uniformGroupsAlloced += UNIFORM_PREALLOC;
1858 uniformGroupPtr[start].group = layoutPtr[slot].uniform;
1859 uniformGroupPtr[start].minSize = 0;
1861 weight = layoutPtr[slot].weight;
1862 weight = weight > 0 ? weight : 1;
1863 minSize = (layoutPtr[slot].minSize + weight - 1) / weight;
1864 if (minSize > uniformGroupPtr[start].minSize) {
1865 uniformGroupPtr[start].minSize = minSize;
1871 * Data has been gathered about uniform groups. Now relayout accordingly.
1874 if (uniformGroups > 0) {
1875 for (slot = 0; slot < gridCount; slot++) {
1876 if (layoutPtr[slot].uniform != NULL) {
1877 for (start = 0; start < uniformGroups; start++) {
1878 if (uniformGroupPtr[start].group ==
1879 layoutPtr[slot].uniform) {
1880 weight = layoutPtr[slot].weight;
1881 weight = weight > 0 ? weight : 1;
1882 layoutPtr[slot].minSize =
1883 uniformGroupPtr[start].minSize * weight;
1891 if (uniformGroupPtr != uniformPre) {
1892 ckfree((char *) uniformGroupPtr);
1897 * Determine the minimum slot offsets going from left to right
1898 * that would fit all of the slaves. This determines the minimum
1901 for (offset=slot=0; slot < gridCount; slot++) {
1902 layoutPtr[slot].minOffset = layoutPtr[slot].minSize + offset;
1903 for (slavePtr = layoutPtr[slot].binNextPtr; slavePtr != NULL;
1904 slavePtr = slavePtr->binNextPtr) {
1905 int span = (slotType == COLUMN) ? slavePtr->numCols : slavePtr->numRows;
1906 int required = slavePtr->size + layoutPtr[slot - span].minOffset;
1907 if (required > layoutPtr[slot].minOffset) {
1908 layoutPtr[slot].minOffset = required;
1911 offset = layoutPtr[slot].minOffset;
1915 * At this point, we know the minimum required size of the entire layout.
1916 * It might be prudent to stop here if our "master" will resize itself
1920 requiredSize = offset;
1921 if (maxOffset > offset) {
1927 * Determine the minimum slot offsets going from right to left,
1928 * bounding the pixel range of each slot boundary.
1929 * Pre-fill all of the right offsets with the actual size of the table;
1930 * they will be reduced as required.
1933 for (slot=0; slot < gridCount; slot++) {
1934 layoutPtr[slot].maxOffset = offset;
1936 for (slot=gridCount-1; slot > 0;) {
1937 for (slavePtr = layoutPtr[slot].binNextPtr; slavePtr != NULL;
1938 slavePtr = slavePtr->binNextPtr) {
1939 int span = (slotType == COLUMN) ? slavePtr->numCols : slavePtr->numRows;
1940 int require = offset - slavePtr->size;
1941 int startSlot = slot - span;
1942 if (startSlot >=0 && require < layoutPtr[startSlot].maxOffset) {
1943 layoutPtr[startSlot].maxOffset = require;
1946 offset -= layoutPtr[slot].minSize;
1948 if (layoutPtr[slot].maxOffset < offset) {
1949 offset = layoutPtr[slot].maxOffset;
1951 layoutPtr[slot].maxOffset = offset;
1957 * At this point, each slot boundary has a range of values that
1958 * will satisfy the overall layout size.
1959 * Make repeated passes over the layout structure looking for
1960 * spans of slot boundaries where the minOffsets are less than
1961 * the maxOffsets, and adjust the offsets according to the slot
1962 * weights. At each pass, at least one slot boundary will have
1963 * its range of possible values fixed at a single value.
1966 for (start=0; start < gridCount;) {
1967 int totalWeight = 0; /* Sum of the weights for all of the
1968 * slots in this span. */
1969 int need = 0; /* The minimum space needed to layout
1971 int have; /* The actual amount of space that will
1972 * be taken up by this span. */
1973 int weight; /* Cumulative weights of the columns in
1975 int noWeights = 0; /* True if the span has no weights. */
1978 * Find a span by identifying ranges of slots whose edges are
1979 * already constrained at fixed offsets, but whose internal
1980 * slot boundaries have a range of possible positions.
1983 if (layoutPtr[start].minOffset == layoutPtr[start].maxOffset) {
1988 for (end=start+1; end<gridCount; end++) {
1989 if (layoutPtr[end].minOffset == layoutPtr[end].maxOffset) {
1995 * We found a span. Compute the total weight, minumum space required,
1996 * for this span, and the actual amount of space the span should
2000 for (slot=start; slot<=end; slot++) {
2001 totalWeight += layoutPtr[slot].weight;
2002 need += layoutPtr[slot].minSize;
2004 have = layoutPtr[end].maxOffset - layoutPtr[start-1].minOffset;
2007 * If all the weights in the span are zero, then distribute the
2008 * extra space evenly.
2011 if (totalWeight == 0) {
2013 totalWeight = end - start + 1;
2017 * It might not be possible to give the span all of the space
2018 * available on this pass without violating the size constraints
2019 * of one or more of the internal slot boundaries.
2020 * Determine the maximum amount of space that when added to the
2021 * entire span, would cause a slot boundary to have its possible
2022 * range reduced to one value, and reduce the amount of extra
2023 * space allocated on this pass accordingly.
2025 * The calculation is done cumulatively to avoid accumulating
2029 for (weight=0,slot=start; slot<end; slot++) {
2030 int diff = layoutPtr[slot].maxOffset - layoutPtr[slot].minOffset;
2031 weight += noWeights ? 1 : layoutPtr[slot].weight;
2032 if ((noWeights || layoutPtr[slot].weight>0) &&
2033 (diff*totalWeight/weight) < (have-need)) {
2034 have = diff * totalWeight / weight + need;
2039 * Now distribute the extra space among the slots by
2040 * adjusting the minSizes and minOffsets.
2043 for (weight=0,slot=start; slot<end; slot++) {
2044 weight += noWeights ? 1 : layoutPtr[slot].weight;
2045 layoutPtr[slot].minOffset +=
2046 (int)((double) (have-need) * weight/totalWeight + 0.5);
2047 layoutPtr[slot].minSize = layoutPtr[slot].minOffset
2048 - layoutPtr[slot-1].minOffset;
2050 layoutPtr[slot].minSize = layoutPtr[slot].minOffset
2051 - layoutPtr[slot-1].minOffset;
2054 * Having pushed the top/left boundaries of the slots to
2055 * take up extra space, the bottom/right space is recalculated
2056 * to propagate the new space allocation.
2059 for (slot=end; slot > start; slot--) {
2060 layoutPtr[slot-1].maxOffset =
2061 layoutPtr[slot].maxOffset-layoutPtr[slot].minSize;
2068 * All of the space has been apportioned; copy the
2069 * layout information back into the master.
2072 for (slot=0; slot < gridCount; slot++) {
2073 slotPtr[slot].offset = layoutPtr[slot].minOffset;
2077 if (layoutPtr != layoutData) {
2078 ckfree((char *)layoutPtr);
2080 return requiredSize;
2084 *--------------------------------------------------------------
2088 * This internal procedure is used to locate a Grid
2089 * structure for a given window, creating one if one
2090 * doesn't exist already.
2093 * The return value is a pointer to the Grid structure
2094 * corresponding to tkwin.
2097 * A new grid structure may be created. If so, then
2098 * a callback is set up to clean things up when the
2099 * window is deleted.
2101 *--------------------------------------------------------------
2106 Tk_Window tkwin; /* Token for window for which
2107 * grid structure is desired. */
2109 register Gridder *gridPtr;
2110 Tcl_HashEntry *hPtr;
2112 TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr;
2114 if (!dispPtr->gridInit) {
2115 Tcl_InitHashTable(&dispPtr->gridHashTable, TCL_ONE_WORD_KEYS);
2116 dispPtr->gridInit = 1;
2120 * See if there's already grid for this window. If not,
2121 * then create a new one.
2124 hPtr = Tcl_CreateHashEntry(&dispPtr->gridHashTable, (char *) tkwin, &new);
2126 return (Gridder *) Tcl_GetHashValue(hPtr);
2128 gridPtr = (Gridder *) ckalloc(sizeof(Gridder));
2129 gridPtr->tkwin = tkwin;
2130 gridPtr->masterPtr = NULL;
2131 gridPtr->masterDataPtr = NULL;
2132 gridPtr->nextPtr = NULL;
2133 gridPtr->slavePtr = NULL;
2134 gridPtr->binNextPtr = NULL;
2136 gridPtr->column = gridPtr->row = -1;
2137 gridPtr->numCols = 1;
2138 gridPtr->numRows = 1;
2140 gridPtr->padX = gridPtr->padY = 0;
2141 gridPtr->padLeft = gridPtr->padTop = 0;
2142 gridPtr->iPadX = gridPtr->iPadY = 0;
2143 gridPtr->doubleBw = 2*Tk_Changes(tkwin)->border_width;
2144 gridPtr->abortPtr = NULL;
2146 gridPtr->sticky = 0;
2148 gridPtr->masterDataPtr = NULL;
2149 Tcl_SetHashValue(hPtr, gridPtr);
2150 Tk_CreateEventHandler(tkwin, StructureNotifyMask,
2151 GridStructureProc, (ClientData) gridPtr);
2156 *--------------------------------------------------------------
2160 * This internal procedure sets the size of the grid occupied
2167 * The width and height arguments are filled in the master data structure.
2168 * Additional space is allocated for the constraints to accomodate
2171 *--------------------------------------------------------------
2175 SetGridSize(masterPtr)
2176 Gridder *masterPtr; /* The geometry master for this grid. */
2178 register Gridder *slavePtr; /* Current slave window. */
2179 int maxX = 0, maxY = 0;
2181 for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
2182 slavePtr = slavePtr->nextPtr) {
2183 maxX = MAX(maxX,slavePtr->numCols + slavePtr->column);
2184 maxY = MAX(maxY,slavePtr->numRows + slavePtr->row);
2186 masterPtr->masterDataPtr->columnEnd = maxX;
2187 masterPtr->masterDataPtr->rowEnd = maxY;
2188 CheckSlotData(masterPtr, maxX, COLUMN, CHECK_SPACE);
2189 CheckSlotData(masterPtr, maxY, ROW, CHECK_SPACE);
2193 *--------------------------------------------------------------
2197 * This internal procedure is used to manage the storage for
2198 * row and column (slot) constraints.
2201 * TRUE if the index is OK, False otherwise.
2204 * A new master grid structure may be created. If so, then
2205 * it is initialized. In addition, additional storage for
2206 * a row or column constraints may be allocated, and the constraint
2207 * maximums are adjusted.
2209 *--------------------------------------------------------------
2213 CheckSlotData(masterPtr, slot, slotType, checkOnly)
2214 Gridder *masterPtr; /* the geometry master for this grid */
2215 int slot; /* which slot to look at */
2216 int slotType; /* ROW or COLUMN */
2217 int checkOnly; /* don't allocate new space if true */
2219 int numSlot; /* number of slots already allocated (Space) */
2220 int end; /* last used constraint */
2223 * If slot is out of bounds, return immediately.
2226 if (slot < 0 || slot >= MAX_ELEMENT) {
2230 if ((checkOnly == CHECK_ONLY) && (masterPtr->masterDataPtr == NULL)) {
2235 * If we need to allocate more space, allocate a little extra to avoid
2236 * repeated re-alloc's for large tables. We need enough space to
2237 * hold all of the offsets as well.
2240 InitMasterData(masterPtr);
2241 end = (slotType == ROW) ? masterPtr->masterDataPtr->rowMax :
2242 masterPtr->masterDataPtr->columnMax;
2243 if (checkOnly == CHECK_ONLY) {
2244 return (end < slot) ? TCL_ERROR : TCL_OK;
2246 numSlot = (slotType == ROW) ? masterPtr->masterDataPtr->rowSpace
2247 : masterPtr->masterDataPtr->columnSpace;
2248 if (slot >= numSlot) {
2249 int newNumSlot = slot + PREALLOC ;
2250 size_t oldSize = numSlot * sizeof(SlotInfo) ;
2251 size_t newSize = newNumSlot * sizeof(SlotInfo) ;
2252 SlotInfo *new = (SlotInfo *) ckalloc(newSize);
2253 SlotInfo *old = (slotType == ROW) ?
2254 masterPtr->masterDataPtr->rowPtr :
2255 masterPtr->masterDataPtr->columnPtr;
2256 memcpy((VOID *) new, (VOID *) old, oldSize );
2257 memset((VOID *) (new+numSlot), 0, newSize - oldSize );
2258 ckfree((char *) old);
2259 if (slotType == ROW) {
2260 masterPtr->masterDataPtr->rowPtr = new ;
2261 masterPtr->masterDataPtr->rowSpace = newNumSlot ;
2263 masterPtr->masterDataPtr->columnPtr = new;
2264 masterPtr->masterDataPtr->columnSpace = newNumSlot ;
2267 if (slot >= end && checkOnly != CHECK_SPACE) {
2268 if (slotType == ROW) {
2269 masterPtr->masterDataPtr->rowMax = slot+1;
2271 masterPtr->masterDataPtr->columnMax = slot+1;
2279 *--------------------------------------------------------------
2283 * This internal procedure is used to allocate and initialize
2284 * the data for a geometry master, if the data
2285 * doesn't exist already.
2291 * A new master grid structure may be created. If so, then
2292 * it is initialized.
2294 *--------------------------------------------------------------
2298 InitMasterData(masterPtr)
2302 if (masterPtr->masterDataPtr == NULL) {
2303 GridMaster *gridPtr = masterPtr->masterDataPtr =
2304 (GridMaster *) ckalloc(sizeof(GridMaster));
2305 size = sizeof(SlotInfo) * TYPICAL_SIZE;
2307 gridPtr->columnEnd = 0;
2308 gridPtr->columnMax = 0;
2309 gridPtr->columnPtr = (SlotInfo *) ckalloc(size);
2310 gridPtr->columnSpace = TYPICAL_SIZE;
2311 gridPtr->rowEnd = 0;
2312 gridPtr->rowMax = 0;
2313 gridPtr->rowPtr = (SlotInfo *) ckalloc(size);
2314 gridPtr->rowSpace = TYPICAL_SIZE;
2315 gridPtr->startX = 0;
2316 gridPtr->startY = 0;
2318 memset((VOID *) gridPtr->columnPtr, 0, size);
2319 memset((VOID *) gridPtr->rowPtr, 0, size);
2324 *----------------------------------------------------------------------
2328 * Remove a grid from its parent's list of slaves.
2334 * The parent will be scheduled for re-arranging, and the size of the
2335 * grid will be adjusted accordingly
2337 *----------------------------------------------------------------------
2342 register Gridder *slavePtr; /* Window to unlink. */
2344 register Gridder *masterPtr, *slavePtr2;
2346 masterPtr = slavePtr->masterPtr;
2347 if (masterPtr == NULL) {
2351 if (masterPtr->slavePtr == slavePtr) {
2352 masterPtr->slavePtr = slavePtr->nextPtr;
2354 for (slavePtr2 = masterPtr->slavePtr; ; slavePtr2 = slavePtr2->nextPtr) {
2355 if (slavePtr2 == NULL) {
2356 panic("Unlink couldn't find previous window");
2358 if (slavePtr2->nextPtr == slavePtr) {
2359 slavePtr2->nextPtr = slavePtr->nextPtr;
2364 if (!(masterPtr->flags & REQUESTED_RELAYOUT)) {
2365 masterPtr->flags |= REQUESTED_RELAYOUT;
2366 Tcl_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
2368 if (masterPtr->abortPtr != NULL) {
2369 *masterPtr->abortPtr = 1;
2372 SetGridSize(slavePtr->masterPtr);
2373 slavePtr->masterPtr = NULL;
2377 *----------------------------------------------------------------------
2381 * This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
2382 * to clean up the internal structure of a grid at a safe time
2383 * (when no-one is using it anymore). Cleaning up the grid involves
2384 * freeing the main structure for all windows. and the master structure
2385 * for geometry managers.
2391 * Everything associated with the grid is freed up.
2393 *----------------------------------------------------------------------
2398 char *memPtr; /* Info about window that is now dead. */
2400 register Gridder *gridPtr = (Gridder *) memPtr;
2402 if (gridPtr->masterDataPtr != NULL) {
2403 if (gridPtr->masterDataPtr->rowPtr != NULL) {
2404 ckfree((char *) gridPtr->masterDataPtr -> rowPtr);
2406 if (gridPtr->masterDataPtr->columnPtr != NULL) {
2407 ckfree((char *) gridPtr->masterDataPtr -> columnPtr);
2409 ckfree((char *) gridPtr->masterDataPtr);
2411 ckfree((char *) gridPtr);
2415 *----------------------------------------------------------------------
2417 * GridStructureProc --
2419 * This procedure is invoked by the Tk event dispatcher in response
2420 * to StructureNotify events.
2426 * If a window was just deleted, clean up all its grid-related
2427 * information. If it was just resized, re-configure its slaves, if
2430 *----------------------------------------------------------------------
2434 GridStructureProc(clientData, eventPtr)
2435 ClientData clientData; /* Our information about window
2436 * referred to by eventPtr. */
2437 XEvent *eventPtr; /* Describes what just happened. */
2439 register Gridder *gridPtr = (Gridder *) clientData;
2440 TkDisplay *dispPtr = ((TkWindow *) gridPtr->tkwin)->dispPtr;
2442 if (eventPtr->type == ConfigureNotify) {
2443 if (!(gridPtr->flags & REQUESTED_RELAYOUT)) {
2444 gridPtr->flags |= REQUESTED_RELAYOUT;
2445 Tcl_DoWhenIdle(ArrangeGrid, (ClientData) gridPtr);
2447 if (gridPtr->doubleBw != 2*Tk_Changes(gridPtr->tkwin)->border_width) {
2448 if ((gridPtr->masterPtr != NULL) &&
2449 !(gridPtr->masterPtr->flags & REQUESTED_RELAYOUT)) {
2450 gridPtr->doubleBw = 2*Tk_Changes(gridPtr->tkwin)->border_width;
2451 gridPtr->masterPtr->flags |= REQUESTED_RELAYOUT;
2452 Tcl_DoWhenIdle(ArrangeGrid, (ClientData) gridPtr->masterPtr);
2455 } else if (eventPtr->type == DestroyNotify) {
2456 register Gridder *gridPtr2, *nextPtr;
2458 if (gridPtr->masterPtr != NULL) {
2461 for (gridPtr2 = gridPtr->slavePtr; gridPtr2 != NULL;
2462 gridPtr2 = nextPtr) {
2463 Tk_UnmapWindow(gridPtr2->tkwin);
2464 gridPtr2->masterPtr = NULL;
2465 nextPtr = gridPtr2->nextPtr;
2466 gridPtr2->nextPtr = NULL;
2468 Tcl_DeleteHashEntry(Tcl_FindHashEntry(&dispPtr->gridHashTable,
2469 (char *) gridPtr->tkwin));
2470 if (gridPtr->flags & REQUESTED_RELAYOUT) {
2471 Tcl_CancelIdleCall(ArrangeGrid, (ClientData) gridPtr);
2473 gridPtr->tkwin = NULL;
2474 Tcl_EventuallyFree((ClientData) gridPtr, DestroyGrid);
2475 } else if (eventPtr->type == MapNotify) {
2476 if (!(gridPtr->flags & REQUESTED_RELAYOUT)) {
2477 gridPtr->flags |= REQUESTED_RELAYOUT;
2478 Tcl_DoWhenIdle(ArrangeGrid, (ClientData) gridPtr);
2480 } else if (eventPtr->type == UnmapNotify) {
2481 register Gridder *gridPtr2;
2483 for (gridPtr2 = gridPtr->slavePtr; gridPtr2 != NULL;
2484 gridPtr2 = gridPtr2->nextPtr) {
2485 Tk_UnmapWindow(gridPtr2->tkwin);
2491 *----------------------------------------------------------------------
2493 * ConfigureSlaves --
2495 * This implements the guts of the "grid configure" command. Given
2496 * a list of slaves and configuration options, it arranges for the
2497 * grid to manage the slaves and sets the specified options.
2498 * arguments consist of windows or window shortcuts followed by
2499 * "-option value" pairs.
2502 * TCL_OK is returned if all went well. Otherwise, TCL_ERROR is
2503 * returned and the interp's result is set to contain an error message.
2506 * Slave windows get taken over by the grid.
2508 *----------------------------------------------------------------------
2512 ConfigureSlaves(interp, tkwin, objc, objv)
2513 Tcl_Interp *interp; /* Interpreter for error reporting. */
2514 Tk_Window tkwin; /* Any window in application containing
2515 * slaves. Used to look up slave names. */
2516 int objc; /* Number of elements in argv. */
2517 Tcl_Obj *CONST objv[]; /* Argument objects: contains one or more
2518 * window names followed by any number
2519 * of "option value" pairs. Caller must
2520 * make sure that there is at least one
2525 Tk_Window other, slave, parent, ancestor;
2530 int defaultColumn = 0; /* default column number */
2531 int defaultColumnSpan = 1; /* default number of columns */
2532 char *lastWindow; /* use this window to base current
2534 int numSkip; /* number of 'x' found */
2535 static CONST char *optionStrings[] = {
2536 "-column", "-columnspan", "-in", "-ipadx", "-ipady",
2537 "-padx", "-pady", "-row", "-rowspan", "-sticky",
2540 CONF_COLUMN, CONF_COLUMNSPAN, CONF_IN, CONF_IPADX, CONF_IPADY,
2541 CONF_PADX, CONF_PADY, CONF_ROW, CONF_ROWSPAN, CONF_STICKY };
2544 char firstChar, prevChar;
2547 * Count the number of windows, or window short-cuts.
2551 for (numWindows = i = 0; i < objc; i++) {
2552 prevChar = firstChar;
2553 string = Tcl_GetStringFromObj(objv[i], (int *) &length);
2554 firstChar = string[0];
2556 if (firstChar == '.') {
2560 if (length > 1 && i == 0) {
2561 Tcl_AppendResult(interp, "bad argument \"", string,
2562 "\": must be name of window", (char *) NULL);
2565 if (length > 1 && firstChar == '-') {
2569 Tcl_AppendResult(interp, "unexpected parameter, \"",
2570 string, "\", in configure list. ",
2571 "Should be window name or option", (char *) NULL);
2575 if ((firstChar == REL_HORIZ) && ((numWindows == 0) ||
2576 (prevChar == REL_SKIP) || (prevChar == REL_VERT))) {
2577 Tcl_AppendResult(interp,
2578 "Must specify window before shortcut '-'.",
2583 if ((firstChar == REL_VERT) || (firstChar == REL_SKIP)
2584 || (firstChar == REL_HORIZ)) {
2588 Tcl_AppendResult(interp, "invalid window shortcut, \"",
2589 string, "\" should be '-', 'x', or '^'", (char *) NULL);
2594 if ((objc - numWindows) & 1) {
2595 Tcl_AppendResult(interp, "extra option or",
2596 " option with no value", (char *) NULL);
2601 * Iterate over all of the slave windows and short-cuts, parsing
2602 * options for each slave. It's a bit wasteful to re-parse the
2603 * options for each slave, but things get too messy if we try to
2604 * parse the arguments just once at the beginning. For example,
2605 * if a slave already is managed we want to just change a few
2606 * existing values without resetting everything. If there are
2607 * multiple windows, the -in option only gets processed for the
2612 for (j = 0; j < numWindows; j++) {
2613 string = Tcl_GetString(objv[j]);
2614 firstChar = string[0];
2617 * '^' and 'x' cause us to skip a column. '-' is processed
2618 * as part of its preceeding slave.
2621 if ((firstChar == REL_VERT) || (firstChar == REL_SKIP)) {
2625 if (firstChar == REL_HORIZ) {
2629 for (defaultColumnSpan = 1; j + defaultColumnSpan < numWindows;
2630 defaultColumnSpan++) {
2631 char *string = Tcl_GetString(objv[j + defaultColumnSpan]);
2632 if (*string != REL_HORIZ) {
2637 if (TkGetWindowFromObj(interp, tkwin, objv[j], &slave) != TCL_OK) {
2641 if (Tk_TopWinHierarchy(slave)) {
2642 Tcl_AppendResult(interp, "can't manage \"", Tcl_GetString(objv[j]),
2643 "\": it's a top-level window", (char *) NULL);
2646 slavePtr = GetGrid(slave);
2649 * The following statement is taken from tkPack.c:
2651 * "If the slave isn't currently managed, reset all of its
2652 * configuration information to default values (there could
2653 * be old values left from a previous packer)."
2655 * I [D.S.] disagree with this statement. If a slave is disabled (using
2656 * "forget") and then re-enabled, I submit that 90% of the time the
2657 * programmer will want it to retain its old configuration information.
2658 * If the programmer doesn't want this behavior, then the
2659 * defaults can be reestablished by hand, without having to worry
2660 * about keeping track of the old state.
2663 for (i = numWindows; i < objc; i += 2) {
2664 if (Tcl_GetIndexFromObj(interp, objv[i], optionStrings, "option", 0,
2665 &index) != TCL_OK) {
2668 if (index == CONF_COLUMN) {
2669 if (Tcl_GetIntFromObj(interp, objv[i+1], &tmp) != TCL_OK ||
2671 Tcl_ResetResult(interp);
2672 Tcl_AppendResult(interp, "bad column value \"",
2673 Tcl_GetString(objv[i+1]),
2674 "\": must be a non-negative integer", (char *)NULL);
2677 slavePtr->column = tmp;
2678 } else if (index == CONF_COLUMNSPAN) {
2679 if (Tcl_GetIntFromObj(interp, objv[i+1], &tmp) != TCL_OK ||
2681 Tcl_ResetResult(interp);
2682 Tcl_AppendResult(interp, "bad columnspan value \"",
2683 Tcl_GetString(objv[i+1]),
2684 "\": must be a positive integer", (char *)NULL);
2687 slavePtr->numCols = tmp;
2688 } else if (index == CONF_IN) {
2689 if (TkGetWindowFromObj(interp, tkwin, objv[i+1], &other) !=
2693 if (other == slave) {
2694 Tcl_SetResult(interp, "Window can't be managed in itself",
2698 masterPtr = GetGrid(other);
2699 InitMasterData(masterPtr);
2700 } else if (index == CONF_IPADX) {
2701 if ((Tk_GetPixelsFromObj(interp, slave, objv[i+1], &tmp)
2704 Tcl_ResetResult(interp);
2705 Tcl_AppendResult(interp, "bad ipadx value \"",
2706 Tcl_GetString(objv[i+1]),
2707 "\": must be positive screen distance",
2711 slavePtr->iPadX = tmp*2;
2712 } else if (index == CONF_IPADY) {
2713 if ((Tk_GetPixelsFromObj(interp, slave, objv[i+1], &tmp)
2716 Tcl_ResetResult(interp);
2717 Tcl_AppendResult(interp, "bad ipady value \"",
2718 Tcl_GetString(objv[i+1]),
2719 "\": must be positive screen distance",
2723 slavePtr->iPadY = tmp*2;
2724 } else if (index == CONF_PADX) {
2725 if (TkParsePadAmount(interp, tkwin, objv[i+1],
2726 &slavePtr->padLeft, &slavePtr->padX) != TCL_OK) {
2729 } else if (index == CONF_PADY) {
2730 if (TkParsePadAmount(interp, tkwin, objv[i+1],
2731 &slavePtr->padTop, &slavePtr->padY) != TCL_OK) {
2734 } else if (index == CONF_ROW) {
2735 if (Tcl_GetIntFromObj(interp, objv[i+1], &tmp) != TCL_OK
2737 Tcl_ResetResult(interp);
2738 Tcl_AppendResult(interp, "bad grid value \"",
2739 Tcl_GetString(objv[i+1]),
2740 "\": must be a non-negative integer", (char *)NULL);
2743 slavePtr->row = tmp;
2744 } else if (index == CONF_ROWSPAN) {
2745 if ((Tcl_GetIntFromObj(interp, objv[i+1], &tmp) != TCL_OK)
2747 Tcl_ResetResult(interp);
2748 Tcl_AppendResult(interp, "bad rowspan value \"",
2749 Tcl_GetString(objv[i+1]),
2750 "\": must be a positive integer", (char *)NULL);
2753 slavePtr->numRows = tmp;
2754 } else if (index == CONF_STICKY) {
2755 int sticky = StringToSticky(Tcl_GetString(objv[i+1]));
2757 Tcl_AppendResult(interp, "bad stickyness value \"",
2758 Tcl_GetString(objv[i+1]),
2759 "\": must be a string containing n, e, s, and/or w",
2763 slavePtr->sticky = sticky;
2768 * Make sure we have a geometry master. We look at:
2770 * 2) the geometry master of the first slave (if specified)
2771 * 3) the parent of the first slave.
2774 if (masterPtr == NULL) {
2775 masterPtr = slavePtr->masterPtr;
2777 parent = Tk_Parent(slave);
2778 if (masterPtr == NULL) {
2779 masterPtr = GetGrid(parent);
2780 InitMasterData(masterPtr);
2783 if (slavePtr->masterPtr != NULL && slavePtr->masterPtr != masterPtr) {
2785 slavePtr->masterPtr = NULL;
2788 if (slavePtr->masterPtr == NULL) {
2789 Gridder *tempPtr = masterPtr->slavePtr;
2790 slavePtr->masterPtr = masterPtr;
2791 masterPtr->slavePtr = slavePtr;
2792 slavePtr->nextPtr = tempPtr;
2796 * Make sure that the slave's parent is either the master or
2797 * an ancestor of the master, and that the master and slave
2801 for (ancestor = masterPtr->tkwin; ; ancestor = Tk_Parent(ancestor)) {
2802 if (ancestor == parent) {
2805 if (Tk_TopWinHierarchy(ancestor)) {
2806 Tcl_AppendResult(interp, "can't put ", Tcl_GetString(objv[j]),
2807 " inside ", Tk_PathName(masterPtr->tkwin),
2815 * Try to make sure our master isn't managed by us.
2818 if (masterPtr->masterPtr == slavePtr) {
2819 Tcl_AppendResult(interp, "can't put ", Tcl_GetString(objv[j]),
2820 " inside ", Tk_PathName(masterPtr->tkwin),
2821 ", would cause management loop.",
2827 Tk_ManageGeometry(slave, &gridMgrType, (ClientData) slavePtr);
2830 * Assign default position information.
2833 if (slavePtr->column == -1) {
2834 slavePtr->column = defaultColumn;
2836 slavePtr->numCols += defaultColumnSpan - 1;
2837 if (slavePtr->row == -1) {
2838 if (masterPtr->masterDataPtr == NULL) {
2841 slavePtr->row = masterPtr->masterDataPtr->rowEnd;
2844 defaultColumn += slavePtr->numCols;
2845 defaultColumnSpan = 1;
2848 * Arrange for the parent to be re-arranged at the first
2852 if (masterPtr->abortPtr != NULL) {
2853 *masterPtr->abortPtr = 1;
2855 if (!(masterPtr->flags & REQUESTED_RELAYOUT)) {
2856 masterPtr->flags |= REQUESTED_RELAYOUT;
2857 Tcl_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
2861 /* Now look for all the "^"'s. */
2865 for (j = 0; j < numWindows; j++) {
2866 struct Gridder *otherPtr;
2867 int match; /* found a match for the ^ */
2868 int lastRow, lastColumn; /* implied end of table */
2870 string = Tcl_GetString(objv[j]);
2871 firstChar = string[0];
2873 if (firstChar == '.') {
2874 lastWindow = string;
2877 if (firstChar == REL_SKIP) {
2880 if (firstChar != REL_VERT) {
2884 if (masterPtr == NULL) {
2885 Tcl_AppendResult(interp, "can't use '^', cant find master",
2890 /* Count the number of consecutive ^'s starting from this position */
2891 for (width = 1; width + j < numWindows; width++) {
2892 char *string = Tcl_GetString(objv[j+width]);
2893 if (*string != REL_VERT) break;
2897 * Find the implied grid location of the ^
2900 if (lastWindow == NULL) {
2901 if (masterPtr->masterDataPtr != NULL) {
2902 SetGridSize(masterPtr);
2903 lastRow = masterPtr->masterDataPtr->rowEnd - 2;
2909 other = Tk_NameToWindow(interp, lastWindow, tkwin);
2910 otherPtr = GetGrid(other);
2911 lastRow = otherPtr->row + otherPtr->numRows - 2;
2912 lastColumn = otherPtr->column + otherPtr->numCols;
2915 lastColumn += numSkip;
2917 for (match=0, slavePtr = masterPtr->slavePtr; slavePtr != NULL;
2918 slavePtr = slavePtr->nextPtr) {
2920 if (slavePtr->column == lastColumn
2921 && slavePtr->row + slavePtr->numRows - 1 == lastRow) {
2922 if (slavePtr->numCols <= width) {
2923 slavePtr->numRows++;
2925 j += slavePtr->numCols - 1;
2926 lastWindow = Tk_PathName(slavePtr->tkwin);
2933 Tcl_AppendResult(interp, "can't find slave to extend with \"^\".",
2939 if (masterPtr == NULL) {
2940 Tcl_AppendResult(interp, "can't determine master window",
2944 SetGridSize(masterPtr);
2949 *----------------------------------------------------------------------
2953 * Converts the internal boolean combination of "sticky" bits onto
2954 * a TCL list element containing zero or mor of n, s, e, or w.
2957 * A string is placed into the "result" pointer.
2962 *----------------------------------------------------------------------
2966 StickyToString(flags, result)
2967 int flags; /* the sticky flags */
2968 char *result; /* where to put the result */
2971 if (flags&STICK_NORTH) {
2972 result[count++] = 'n';
2974 if (flags&STICK_EAST) {
2975 result[count++] = 'e';
2977 if (flags&STICK_SOUTH) {
2978 result[count++] = 's';
2980 if (flags&STICK_WEST) {
2981 result[count++] = 'w';
2984 result[count] = '\0';
2986 sprintf(result,"{}");
2991 *----------------------------------------------------------------------
2995 * Converts an ascii string representing a widgets stickyness
2996 * into the boolean result.
2999 * The boolean combination of the "sticky" bits is retuned. If an
3000 * error occurs, such as an invalid character, -1 is returned instead.
3005 *----------------------------------------------------------------------
3009 StringToSticky(string)
3015 while ((c = *string++) != '\0') {
3017 case 'n': case 'N': sticky |= STICK_NORTH; break;
3018 case 'e': case 'E': sticky |= STICK_EAST; break;
3019 case 's': case 'S': sticky |= STICK_SOUTH; break;
3020 case 'w': case 'W': sticky |= STICK_WEST; break;
3021 case ' ': case ',': case '\t': case '\r': case '\n': break;
3029 *----------------------------------------------------------------------
3033 * Creates a new list object and fills it with two integer objects.
3036 * The newly created list object is returned.
3041 *----------------------------------------------------------------------
3045 NewPairObj(interp, val1, val2)
3046 Tcl_Interp *interp; /* Current interpreter. */
3049 Tcl_Obj *res = Tcl_NewListObj(0, NULL);
3050 Tcl_ListObjAppendElement(interp, res, Tcl_NewIntObj(val1));
3051 Tcl_ListObjAppendElement(interp, res, Tcl_NewIntObj(val2));
3056 *----------------------------------------------------------------------
3060 * Creates a new list object and fills it with four integer objects.
3063 * The newly created list object is returned.
3068 *----------------------------------------------------------------------
3072 NewQuadObj(interp, val1, val2, val3, val4)
3073 Tcl_Interp *interp; /* Current interpreter. */
3074 int val1, val2, val3, val4;
3076 Tcl_Obj *res = Tcl_NewListObj(0, NULL);
3077 Tcl_ListObjAppendElement(interp, res, Tcl_NewIntObj(val1));
3078 Tcl_ListObjAppendElement(interp, res, Tcl_NewIntObj(val2));
3079 Tcl_ListObjAppendElement(interp, res, Tcl_NewIntObj(val3));
3080 Tcl_ListObjAppendElement(interp, res, Tcl_NewIntObj(val4));