4 * Grid based geometry manager.
6 * Copyright (c) 1996 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.
17 * LayoutInfo structure. We shouldn't be using hard-wired limits!
20 #define MAXGRIDSIZE 128
22 # define MAXINT 0x7fff
24 #define MINWEIGHT 0.0001 /* weight totals < this are considered to be zero */
27 * Special characters to support relative layouts
30 #define REL_SKIP 'x' /* skip this column */
31 #define REL_HORIZ '-' /* extend previous widget horizontally */
32 #define REL_VERT '^' /* extend previous widget verticallly */
35 * structure to hold collected constraints temporarily:
36 * needs to use a "Constrain" thingy
40 int width, height; /* number of cells horizontally, vertically */
41 int lastRow; /* last cell with a window in it */
42 int minWidth[MAXGRIDSIZE]; /* largest minWidth in each column */
43 int minHeight[MAXGRIDSIZE]; /* largest minHeight in each row */
44 double weightX[MAXGRIDSIZE];/* largest weight in each column */
45 double weightY[MAXGRIDSIZE];/* largest weight in each row */
48 /* structure for holding row and column constraints */
51 int used; /* maximum element used */
52 int max; /* maximum element allocated */
53 int *minsize; /* array of minimum column/row sizes */
54 double *weight; /* array of column/row weights */
57 /* For each window that the gridbag cares about (either because
58 * the window is managed by the gridbag or because the window
59 * has slaves that are managed by the gridbag), there is a
60 * structure of the following type:
63 typedef struct GridBag {
64 CkWindow *winPtr; /* Pointer to window. NULL means that
65 * the window has been deleted, but the
66 * packet hasn't had a chance to clean up
67 * yet because the structure is still in
69 struct GridBag *masterPtr; /* Master window within which this window
70 * is managed (NULL means this window
71 * isn't managed by the gridbag). */
72 struct GridBag *nextPtr; /* Next window managed within same
73 * parent. List is priority-ordered:
74 * first on list gets layed out first. */
75 struct GridBag *slavePtr; /* First in list of slaves managed
76 * inside this window (NULL means
77 * no gridbag slaves). */
79 int gridColumn, gridRow;
80 int gridWidth, gridHeight;
83 int tempWidth, tempHeight;
85 double weightX, weightY;
86 int minWidth, minHeight;
88 int padX, padY; /* Total additional pixels to leave around the
89 * window (half of this space is left on each
90 * side). This is space *outside* the window:
91 * we'll allocate extra space in frame but
92 * won't enlarge window). */
93 int iPadX, iPadY; /* Total extra pixels to allocate inside the
94 * window (half this amount will appear on
96 int startx, starty; /* starting location of layout */
97 int *abortPtr; /* If non-NULL, it means that there is a nested
98 * call to ArrangeGrid already working on
99 * this window. *abortPtr may be set to 1 to
100 * abort that nested call. This happens, for
101 * example, if winPtr or any of its slaves
103 int flags; /* Miscellaneous flags; see below
104 * for definitions. */
106 Constrain row, column; /* column and row constraints */
109 LayoutInfo *layoutCache;
113 * Flag values for GridBag structures:
115 * REQUESTED_RELAYOUT: 1 means a Tk_DoWhenIdle request
116 * has already been made to re-arrange
117 * all the slaves of this window.
118 * STICK_NORTH 1 means this window sticks to the edgth of its
123 * DONT_PROPAGATE: 1 means don't set this window's requested
124 * size. 0 means if this window is a master
125 * then Ck will set its requested size to fit
126 * the needs of its slaves.
129 #define STICK_NORTH 1
131 #define STICK_SOUTH 4
133 #define STICK_ALL (STICK_NORTH|STICK_EAST|STICK_SOUTH|STICK_WEST)
135 #define REQUESTED_RELAYOUT 16
136 #define DONT_PROPAGATE 32
139 * Hash table used to map from CkWindow pointers to corresponding
140 * GridBag structures:
143 static Tcl_HashTable gridBagHashTable;
146 * Have statics in this module been initialized?
149 static initialized = 0;
152 * Prototypes for procedures used only in this file:
155 static void ArrangeGrid _ANSI_ARGS_((ClientData clientData));
156 static int ConfigureSlaves _ANSI_ARGS_((Tcl_Interp *interp,
157 CkWindow *winPtr, int argc, char *argv[]));
158 static void DestroyGridBag _ANSI_ARGS_((char *memPtr));
159 static void GetCachedLayoutInfo _ANSI_ARGS_((GridBag *masterPtr));
160 static GridBag * GetGridBag _ANSI_ARGS_((CkWindow *winPtr));
161 static void GetLayoutInfo _ANSI_ARGS_((GridBag *masterPtr,
163 static void GetMinSize _ANSI_ARGS_((GridBag *masterPtr,
164 LayoutInfo *info, int *minw, int *minh));
165 static void GridBagStructureProc _ANSI_ARGS_((
166 ClientData clientData, CkEvent *eventPtr));
167 static void GridLostSlaveProc _ANSI_ARGS_((ClientData clientData,
169 static void GridReqProc _ANSI_ARGS_((ClientData clientData,
171 static void GridBagStructureProc _ANSI_ARGS_((
172 ClientData clientData, CkEvent *eventPtr));
173 static void StickyToString _ANSI_ARGS_((int flags, char *result));
174 static int StringToSticky _ANSI_ARGS_((char *string));
175 static void Unlink _ANSI_ARGS_((GridBag *gridPtr));
177 static Ck_GeomMgr gridMgrType = {
179 GridReqProc, /* requestProc */
180 GridLostSlaveProc, /* lostSlaveProc */
184 *--------------------------------------------------------------
188 * This procedure is invoked to process the "grid" Tcl command.
189 * See the user documentation for details on what it does.
192 * A standard Tcl result.
195 * See the user documentation.
197 *--------------------------------------------------------------
201 Ck_GridCmd(clientData, interp, argc, argv)
202 ClientData clientData; /* Main window associated with
204 Tcl_Interp *interp; /* Current interpreter. */
205 int argc; /* Number of arguments. */
206 char **argv; /* Argument strings. */
208 CkWindow *winPtr = (CkWindow *) clientData;
212 if ((argc >= 2) && (argv[1][0] == '.')) {
213 return ConfigureSlaves(interp, winPtr, argc-1, argv+1);
216 Tcl_AppendResult(interp, "wrong # args: should be \"",
217 argv[0], " option arg ?arg ...?\"", (char *) NULL);
221 length = strlen(argv[1]);
223 if ((c == 'b') && (strncmp(argv[1], "bbox", length) == 0)) {
234 Tcl_AppendResult(interp, "Wrong number of arguments: ",
235 "must be \"",argv[0],
236 " bbox <master> <column> <row>\"", (char *) NULL);
240 master = Ck_NameToWindow(interp, argv[2], winPtr);
241 if (master == NULL) {
244 if (Tcl_GetInt(interp, argv[3], &column) != TCL_OK) {
247 if (Tcl_GetInt(interp, argv[4], &row) != TCL_OK) {
250 masterPtr = GetGridBag(master);
252 /* make sure the grid is up to snuff */
254 while ((masterPtr->flags & REQUESTED_RELAYOUT)) {
255 Tk_CancelIdleCall(ArrangeGrid, (ClientData) masterPtr);
256 ArrangeGrid((ClientData) masterPtr);
258 GetCachedLayoutInfo(masterPtr);
260 if (row < 0 || column < 0) {
261 *interp->result = '\0';
264 if (column >= masterPtr->layoutCache->width ||
265 row >= masterPtr->layoutCache->height) {
266 *interp->result = '\0';
269 x = masterPtr->startx;
270 y = masterPtr->starty;
271 GetMinSize(masterPtr, masterPtr->layoutCache, &width, &height);
273 diff = masterPtr->winPtr->width - (width + masterPtr->iPadX);
274 for (weight=0.0, i=0; i<masterPtr->layoutCache->width; i++)
275 weight += masterPtr->layoutCache->weightX[i];
277 prevX = 0; /* Needed to prevent gcc warning. */
278 for (i=0; i<=column; i++) {
280 if (weight > MINWEIGHT) {
281 dx = (int)((((double)diff) *
282 masterPtr->layoutCache->weightX[i]) / weight);
285 x += masterPtr->layoutCache->minWidth[i] + dx;
287 diff = masterPtr->winPtr->height - (height + masterPtr->iPadY);
288 for (weight=0.0, i=0; i<masterPtr->layoutCache->width; i++) {
289 weight += masterPtr->layoutCache->weightY[i];
291 prevY = 0; /* Needed to prevent gcc warning. */
292 for (i=0; i<=row; i++) {
294 if (weight > MINWEIGHT) {
295 dy = (int)((((double)diff) *
296 masterPtr->layoutCache->weightY[i]) / weight);
299 y += masterPtr->layoutCache->minHeight[i] + dy;
301 sprintf(interp->result,"%d %d %d %d",prevX,prevY,x - prevX,y - prevY);
302 } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) {
303 if (argv[2][0] != '.') {
304 Tcl_AppendResult(interp, "bad argument \"", argv[2],
305 "\": must be name of window", (char *) NULL);
308 return ConfigureSlaves(interp, winPtr, argc-2, argv+2);
309 } else if ((c == 'f') && (strncmp(argv[1], "forget", length) == 0)) {
314 for (i = 2; i < argc; i++) {
315 slave = Ck_NameToWindow(interp, argv[i], winPtr);
319 slavePtr = GetGridBag(slave);
320 if (slavePtr->masterPtr != NULL) {
321 Ck_ManageGeometry(slave, (Ck_GeomMgr *) NULL,
324 Ck_UnmapWindow(slavePtr->winPtr);
327 } else if ((c == 'i') && (strncmp(argv[1], "info", length) == 0)) {
333 Tcl_AppendResult(interp, "wrong # args: should be \"",
334 argv[0], " info window\"", (char *) NULL);
337 slave = Ck_NameToWindow(interp, argv[2], winPtr);
341 slavePtr = GetGridBag(slave);
342 if (slavePtr->masterPtr == NULL) {
343 interp->result[0] = '\0';
348 Tcl_AppendElement(interp, "-in");
349 Tcl_AppendElement(interp, slavePtr->masterPtr->winPtr->pathName);
351 sprintf(buffer, " -column %d -row %d -columnspan %d -rowspan %d",
352 slavePtr->gridColumn, slavePtr->gridRow,
353 slavePtr->gridWidth, slavePtr->gridHeight);
354 Tcl_AppendResult(interp, buffer, (char *) NULL);
355 sprintf(buffer, " -ipadx %d -ipady %d -padx %d -pady %d",
356 slavePtr->iPadX/2, slavePtr->iPadY/2, slavePtr->padX/2,
358 Tcl_AppendResult(interp, buffer, (char *) NULL);
359 StickyToString(slavePtr->flags,buffer);
360 Tcl_AppendResult(interp, " -sticky ", buffer, (char *) NULL);
362 sprintf(buffer, " -weightx %.2f -weighty %.2f",
363 slavePtr->weightX, slavePtr->weightY);
364 Tcl_AppendResult(interp, buffer, (char *) NULL);
366 } else if ((c == 'p') && (strncmp(argv[1], "propagate", length) == 0)) {
372 Tcl_AppendResult(interp, "wrong # args: should be \"",
373 argv[0], " propagate window ?boolean?\"",
377 master = Ck_NameToWindow(interp, argv[2], winPtr);
378 if (master == NULL) {
381 masterPtr = GetGridBag(master);
383 interp->result = (masterPtr->flags & DONT_PROPAGATE) ? "0" : "1";
386 if (Tcl_GetBoolean(interp, argv[3], &propagate) != TCL_OK) {
390 masterPtr->flags &= ~DONT_PROPAGATE;
393 * Re-arrange the master to allow new geometry information to
394 * propagate upwards to the master\'s master.
397 if (masterPtr->abortPtr != NULL) {
398 *masterPtr->abortPtr = 1;
400 masterPtr->valid = 0;
401 if (!(masterPtr->flags & REQUESTED_RELAYOUT)) {
402 masterPtr->flags |= REQUESTED_RELAYOUT;
403 Tk_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
406 masterPtr->flags |= DONT_PROPAGATE;
408 } else if ((c == 's') && (strncmp(argv[1], "size", length) == 0)) {
413 Tcl_AppendResult(interp, "wrong # args: should be \"",
414 argv[0], " size window\"", (char *) NULL);
417 master = Ck_NameToWindow(interp, argv[2], winPtr);
420 masterPtr = GetGridBag(master);
421 GetCachedLayoutInfo(masterPtr);
423 sprintf(interp->result, "%d %d", masterPtr->layoutCache->width,
424 masterPtr->layoutCache->height);
425 } else if ((c == 's') && (strncmp(argv[1], "slaves", length) == 0)) {
427 GridBag *masterPtr, *slavePtr;
429 int row = -1, column = -1;
431 if (argc < 3 || argc%2 ==0) {
432 Tcl_AppendResult(interp, "wrong # args: should be \"",
433 argv[0], " slaves window ?-option value...?\"",
438 for (i=3; i<argc; i+=2) {
439 if (*argv[i] != '-' || (length = strlen(argv[i])) < 2) {
440 Tcl_AppendResult(interp, "Invalid args: should be \"",
441 argv[0], " slaves window ?-option value...?\"",
445 if (Tcl_GetInt(interp, argv[i+1], &value) != TCL_OK) {
449 Tcl_AppendResult(interp, argv[i],
450 " is an invalid value: should NOT be < 0",
454 if (strncmp(argv[i], "-column", length) == 0) {
456 } else if (strncmp(argv[i], "-row", length) == 0) {
459 Tcl_AppendResult(interp, argv[i],
460 " is an invalid option: should be \"",
466 master = Ck_NameToWindow(interp, argv[2], winPtr);
467 if (master == NULL) {
470 masterPtr = GetGridBag(master);
472 for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
473 slavePtr = slavePtr->nextPtr) {
474 if (column>=0 && (slavePtr->gridColumn > column
475 || slavePtr->gridColumn+slavePtr->gridWidth-1 < column)) {
478 if (row>=0 && (slavePtr->gridRow > row ||
479 slavePtr->gridRow+slavePtr->gridHeight-1 < row)) {
482 Tcl_AppendElement(interp, slavePtr->winPtr->pathName);
486 * grid columnconfigure <master> <index> -option
487 * grid columnconfigure <master> <index> -option value -option value
488 * grid rowconfigure <master> <index> -option
489 * grid rowconfigure <master> <index> -option value -option value
492 } else if (((c == 'c') &&
493 (strncmp(argv[1], "columnconfigure", length) == 0)) ||
494 ((c == 'r') && (strncmp(argv[1], "rowconfigure", length) == 0))) {
501 if (argc != 5 && (argc < 5 || argc%2 == 1)) {
502 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
503 " ", argv[1], " master index ?-option value...?\"",
508 master = Ck_NameToWindow(interp, argv[2], winPtr);
509 if (master == NULL) {
512 masterPtr = GetGridBag(master);
513 con = (c=='c') ? &(masterPtr->column) : &(masterPtr->row);
515 if (Tcl_GetInt(interp, argv[3], &index) != TCL_OK) {
518 if (index < 0 || index >= MAXGRIDSIZE) {
519 Tcl_AppendResult(interp, argv[3], " is out of range",
525 * make sure the row/column constraint array is allocated. This
526 * Should be changed to avoid hard-wired limits. We'll wimp out
532 con->max = MAXGRIDSIZE;
535 size = MAXGRIDSIZE * sizeof(con->minsize[0]);
536 con->minsize = (int *) ckalloc(size);
537 memset(con->minsize, 0, size);
539 size = MAXGRIDSIZE * sizeof(con->weight[0]);
540 con->weight = (double *) ckalloc(size);
541 memset(con->weight, 0, size);
544 for (i=4; i<argc; i+=2) {
545 if (*argv[i] != '-' || (length = strlen(argv[i])) < 2) {
546 Tcl_AppendResult(interp, "Invalid arg: \"",
547 argv[0], "\" expecting -minsize or -weight",
551 if (strncmp(argv[i], "-minsize", length) == 0) {
553 size = con->used <= index ? 0 : con->minsize[index];
554 sprintf(interp->result, "%d", size);
555 } else if (Ck_GetCoord(interp, master, argv[i + 1], &size)
559 con->minsize[index] = size;
560 if (size > 0 && index >= con->used) {
562 } else if (size == 0 && index+1 == con->used) {
563 while (index >= 0 && (con->minsize[index]==0) &&
564 (con->weight[index] == 0.0)) {
567 con->used = index + 1;
570 } else if (strncmp(argv[i], "-weight", length) == 0) {
572 weight = con->used <= index ? 0 : con->weight[index];
573 sprintf(interp->result, "%.2f", weight);
574 } else if (Tcl_GetDouble(interp, argv[i+1], &weight)
578 con->weight[index] = weight;
579 if (weight > MINWEIGHT && index >= con->used) {
581 } else if (weight == 0.0 && index+1 == con->used) {
582 while (index >= 0 && (con->minsize[index]==0) &&
583 (con->weight[index] == 0.0)) {
586 con->used = index + 1;
590 Tcl_AppendResult(interp, argv[i],
591 " is an invalid option: should be \"",
592 "-minsize, -weight\"",
598 /* if we changed a property, re-arrange the table */
601 if (masterPtr->abortPtr != NULL) {
602 *masterPtr->abortPtr = 1;
604 masterPtr->valid = 0;
605 if (!(masterPtr->flags & REQUESTED_RELAYOUT)) {
606 masterPtr->flags |= REQUESTED_RELAYOUT;
607 Tk_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
610 } else if ((c == 'l') && (strncmp(argv[1], "location", length) == 0)) {
613 int x, y, i, j, w, h;
619 Tcl_AppendResult(interp, "wrong # args: should be \"",
620 argv[0], " location master x y\"", (char *)NULL);
624 master = Ck_NameToWindow(interp, argv[2], winPtr);
625 if (master == NULL) {
628 masterPtr = GetGridBag(master);
630 if (Ck_GetCoord(interp, master, argv[3], &x) != TCL_OK) {
633 if (Ck_GetCoord(interp, master, argv[4], &y) != TCL_OK) {
637 /* make sure the grid is up to snuff */
639 while ((masterPtr->flags & REQUESTED_RELAYOUT)) {
640 Tk_CancelIdleCall(ArrangeGrid, (ClientData) masterPtr);
641 ArrangeGrid((ClientData) masterPtr);
643 GetCachedLayoutInfo(masterPtr);
644 GetMinSize(masterPtr, masterPtr->layoutCache, &width, &height);
646 diff = masterPtr->winPtr->width - (width + masterPtr->iPadX);
647 for (weight=0.0, i=0; i<masterPtr->layoutCache->width; i++) {
648 weight += masterPtr->layoutCache->weightX[i];
650 w = masterPtr->startx;
654 for (i = 0; i < masterPtr->layoutCache->width; i++) {
656 if (weight > MINWEIGHT) {
657 dx = (int)((((double)diff) *
658 masterPtr->layoutCache->weightX[i]) / weight);
660 w += masterPtr->layoutCache->minWidth[i] + dx;
667 diff = masterPtr->winPtr->height - (height + masterPtr->iPadY);
668 for (weight = 0.0, j = 0; j < masterPtr->layoutCache->height; j++)
669 weight += masterPtr->layoutCache->weightY[j];
670 h = masterPtr->starty;
674 for (j = 0; j < masterPtr->layoutCache->height; j++) {
676 if (weight > MINWEIGHT) {
677 dy = (int)((((double)diff) *
678 masterPtr->layoutCache->weightY[j]) / weight);
680 h += masterPtr->layoutCache->minHeight[j] + dy;
686 sprintf(interp->result, "%d %d", i, j);
688 Tcl_AppendResult(interp, "bad option \"", argv[1],
689 "\": must be bbox, columnconfigure, configure, forget, ",
690 "info, location, propagate, rowconfigure, size, or slaves",
698 *--------------------------------------------------------------
702 * This procedure is invoked by Ck_GeometryRequest for
703 * windows managed by the gridbag.
709 * Arranges for winPtr, and all its managed siblings, to
710 * be re-arranged at the next idle point.
712 *--------------------------------------------------------------
716 GridReqProc(clientData, winPtr)
717 ClientData clientData; /* GridBag's information about
718 * window that got new preferred
720 CkWindow *winPtr; /* Other Ck-related information
721 * about the window. */
723 GridBag *gridPtr = (GridBag *) clientData;
725 gridPtr = gridPtr->masterPtr;
727 if (!(gridPtr->flags & REQUESTED_RELAYOUT)) {
728 gridPtr->flags |= REQUESTED_RELAYOUT;
729 Tk_DoWhenIdle(ArrangeGrid, (ClientData) gridPtr);
734 *--------------------------------------------------------------
736 * GridLostSlaveProc --
738 * This procedure is invoked by Ck whenever some other geometry
739 * claims control over a slave that used to be managed by us.
745 * Forgets all grid-related information about the slave.
747 *--------------------------------------------------------------
751 GridLostSlaveProc(clientData, winPtr)
752 ClientData clientData; /* GridBag structure for slave window that
753 * was stolen away. */
754 CkWindow *winPtr; /* Pointer to the slave window. */
756 GridBag *slavePtr = (GridBag *) clientData;
758 if (slavePtr->masterPtr->winPtr != slavePtr->winPtr->parentPtr) {
759 Ck_UnmaintainGeometry(slavePtr->winPtr, slavePtr->masterPtr->winPtr);
762 Ck_UnmapWindow(slavePtr->winPtr);
766 *--------------------------------------------------------------
768 * Fill in an instance of the above structure for the current set
769 * of managed children. This requires two passes through the
770 * set of children, first to figure out what cells they occupy
771 * and how many rows and columns there are, and then to distribute
772 * the weights and min sizes amoung the rows/columns.
774 * This also caches the minsizes for all the children when they are
777 *--------------------------------------------------------------
781 GetLayoutInfo(masterPtr, r)
786 int i, k, px, py, pixels_diff, nextSize;
787 double weight_diff, weight;
788 int curX, curY, curWidth, curHeight, curRow, curCol;
789 int xMax[MAXGRIDSIZE];
790 int yMax[MAXGRIDSIZE];
795 * Figure out the dimensions of the layout grid.
798 r->width = r->height = 0;
799 curRow = curCol = -1;
800 memset(xMax, 0, sizeof(int) * MAXGRIDSIZE);
801 memset(yMax, 0, sizeof(int) * MAXGRIDSIZE);
803 for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
804 slavePtr = slavePtr->nextPtr) {
806 curX = slavePtr->gridColumn;
807 curY = slavePtr->gridRow;
808 curWidth = slavePtr->gridWidth;
809 curHeight = slavePtr->gridHeight;
811 /* Adjust the grid width and height */
812 for (px = curX + curWidth; r->width < px; r->width++) {
815 for (py = curY + curHeight; r->height < py; r->height++) {
819 /* Adjust the xMax and yMax arrays */
820 for (i = curX; i < (curX + curWidth); i++) {
823 for (i = curY; i < (curY + curHeight); i++) {
827 /* Cache the current slave's size. */
828 slavePtr->minWidth = slavePtr->winPtr->reqWidth;
829 slavePtr->minHeight = slavePtr->winPtr->reqHeight;
833 * Apply minimum row/column dimensions
835 if (r->width < masterPtr->column.used) {
836 r->width = masterPtr->column.used;
838 r->lastRow = r->height;
839 if (r->height < masterPtr->row.used) {
840 r->height = masterPtr->row.used;
847 curRow = curCol = -1;
848 memset(xMax, 0, sizeof(int) * MAXGRIDSIZE);
849 memset(yMax, 0, sizeof(int) * MAXGRIDSIZE);
851 for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
852 slavePtr = slavePtr->nextPtr) {
853 curX = slavePtr->gridColumn;
854 curY = slavePtr->gridRow;
855 curWidth = slavePtr->gridWidth;
856 curHeight = slavePtr->gridHeight;
858 px = curX + curWidth;
859 py = curY + curHeight;
861 for (i = curX; i < (curX + curWidth); i++) {
864 for (i = curY; i < (curY + curHeight); i++) {
868 /* Assign the new values to the gridbag slave */
869 slavePtr->tempX = curX;
870 slavePtr->tempY = curY;
871 slavePtr->tempWidth = curWidth;
872 slavePtr->tempHeight = curHeight;
878 * Distribute the minimun widths and weights:
881 /* Initialize arrays to zero */
882 memset(r->minWidth, 0, r->width * sizeof(int));
883 memset(r->minHeight, 0, r->height * sizeof(int));
884 memset(r->weightX, 0, r->width * sizeof(double));
885 memset(r->weightY, 0, r->height * sizeof(double));
888 for (i = 1; i != MAXINT; i = nextSize, nextSize = MAXINT) {
889 for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
890 slavePtr = slavePtr->nextPtr) {
892 if (slavePtr->tempWidth == i) {
893 px = slavePtr->tempX + slavePtr->tempWidth; /* right column */
896 * Figure out if we should use this slave's weight.
897 * If the weight is less than the total weight spanned by
898 * the width of the cell, then discard the weight.
899 * Otherwise split it the difference
900 * according to the existing weights.
903 weight_diff = slavePtr->weightX;
904 for (k = slavePtr->tempX; k < px; k++)
905 weight_diff -= r->weightX[k];
906 if (weight_diff > 0.0) {
908 for (k = slavePtr->tempX; k < px; k++)
909 weight += r->weightX[k];
910 for (k = slavePtr->tempX; weight > MINWEIGHT; k++) {
911 double wt = r->weightX[k];
912 double dx = (wt * weight_diff) / weight;
917 /* Assign the remainder to the rightmost cell */
918 r->weightX[px-1] += weight_diff;
922 * Calculate the minWidth array values.
923 * First, figure out how wide the current slave needs to be.
924 * Then, see if it will fit within the current minWidth values.
925 * If it won't fit, add the difference according to the
929 pixels_diff = slavePtr->minWidth + slavePtr->padX +
931 for (k = slavePtr->tempX; k < px; k++)
932 pixels_diff -= r->minWidth[k];
933 if (pixels_diff > 0) {
935 for (k = slavePtr->tempX; k < px; k++)
936 weight += r->weightX[k];
937 for (k = slavePtr->tempX; weight > MINWEIGHT; k++) {
938 double wt = r->weightX[k];
939 int dx = (int)((wt * ((double)pixels_diff)) / weight);
940 r->minWidth[k] += dx;
944 /* Any leftovers go into the rightmost cell */
945 r->minWidth[px-1] += pixels_diff;
947 } else if (slavePtr->tempWidth > i &&
948 slavePtr->tempWidth < nextSize)
949 nextSize = slavePtr->tempWidth;
951 if (slavePtr->tempHeight == i) {
952 py = slavePtr->tempY + slavePtr->tempHeight; /* bottom row */
955 * Figure out if we should use this slave's weight.
956 * If the weight is less than the total weight spanned by
957 * the height of the cell, then discard the weight.
958 * Otherwise split it the difference according to the
962 weight_diff = slavePtr->weightY;
963 for (k = slavePtr->tempY; k < py; k++)
964 weight_diff -= r->weightY[k];
965 if (weight_diff > 0.0) {
967 for (k = slavePtr->tempY; k < py; k++)
968 weight += r->weightY[k];
969 for (k = slavePtr->tempY; weight > MINWEIGHT; k++) {
970 double wt = r->weightY[k];
971 double dy = (wt * weight_diff) / weight;
976 /* Assign the remainder to the bottom cell */
977 r->weightY[py-1] += weight_diff;
981 * Calculate the minHeight array values.
982 * First, figure out how tall the current slave needs to be.
983 * Then, see if it will fit within the current minHeight
984 * values. If it won't fit, add the difference according to
988 pixels_diff = slavePtr->minHeight + slavePtr->padY +
990 for (k = slavePtr->tempY; k < py; k++)
991 pixels_diff -= r->minHeight[k];
992 if (pixels_diff > 0) {
994 for (k = slavePtr->tempY; k < py; k++)
995 weight += r->weightY[k];
996 for (k = slavePtr->tempY; weight > MINWEIGHT; k++) {
997 double wt = r->weightY[k];
998 int dy = (int)((wt * ((double)pixels_diff)) / weight);
999 r->minHeight[k] += dy;
1003 /* Any leftovers go into the bottom cell */
1004 r->minHeight[py-1] += pixels_diff;
1006 } else if (slavePtr->tempHeight > i &&
1007 slavePtr->tempHeight < nextSize)
1008 nextSize = slavePtr->tempHeight;
1013 * Apply minimum row/column dimensions
1015 for (i=0; i<masterPtr->column.used; i++) {
1016 if (r->minWidth[i] < masterPtr->column.minsize[i])
1017 r->minWidth[i] = masterPtr->column.minsize[i];
1018 if (r->weightX[i] < masterPtr->column.weight[i])
1019 r->weightX[i] = masterPtr->column.weight[i];
1021 for (i=0; i<masterPtr->row.used; i++) {
1022 if (r->minHeight[i] < masterPtr->row.minsize[i])
1023 r->minHeight[i] = masterPtr->row.minsize[i];
1024 if (r->weightY[i] < masterPtr->row.weight[i])
1025 r->weightY[i] = masterPtr->row.weight[i];
1030 *--------------------------------------------------------------
1032 * Cache the layout info after it is calculated.
1034 *--------------------------------------------------------------
1038 GetCachedLayoutInfo(masterPtr)
1041 if (masterPtr->valid == 0) {
1042 if (!masterPtr->layoutCache)
1043 masterPtr->layoutCache = (LayoutInfo *)ckalloc(sizeof(LayoutInfo));
1045 GetLayoutInfo(masterPtr, masterPtr->layoutCache);
1046 masterPtr->valid = 1;
1051 *--------------------------------------------------------------
1053 * Adjusts the x, y, width, and height fields to the correct
1054 * values depending on the constraint geometry and pads.
1056 *--------------------------------------------------------------
1060 AdjustForGravity(gridPtr, x, y, width, height)
1067 int diffx=0, diffy=0;
1068 int sticky = gridPtr->flags&STICK_ALL;
1070 *x += gridPtr->padX/2;
1071 *width -= gridPtr->padX;
1072 *y += gridPtr->padY/2;
1073 *height -= gridPtr->padY;
1075 if (*width > (gridPtr->minWidth + gridPtr->iPadX)) {
1076 diffx = *width - (gridPtr->minWidth + gridPtr->iPadX);
1077 *width = gridPtr->minWidth + gridPtr->iPadX;
1080 if (*height > (gridPtr->minHeight + gridPtr->iPadY)) {
1081 diffy = *height - (gridPtr->minHeight + gridPtr->iPadY);
1082 *height = gridPtr->minHeight + gridPtr->iPadY;
1085 if (sticky&STICK_EAST && sticky&STICK_WEST)
1087 if (sticky&STICK_NORTH && sticky&STICK_SOUTH)
1089 if (!(sticky&STICK_WEST)) {
1090 if (sticky&STICK_EAST)
1095 if (!(sticky&STICK_NORTH)) {
1096 if (sticky&STICK_SOUTH)
1104 *--------------------------------------------------------------
1106 * Figure out the minimum size (not counting the X border) of the
1107 * master based on the information from GetLayoutInfo()
1109 *--------------------------------------------------------------
1113 GetMinSize(masterPtr, info, minw, minh)
1120 int intBWidth; /* Width of internal border in parent window,
1123 intBWidth = (masterPtr->winPtr->flags & CK_BORDER) ? 1 : 0;
1126 for(i = 0; i < info->width; i++)
1127 t += info->minWidth[i];
1128 *minw = t + 2*intBWidth;
1131 for(i = 0; i < info->height; i++)
1132 t += info->minHeight[i];
1133 *minh = t + 2*intBWidth;
1137 *--------------------------------------------------------------
1141 * This procedure is invoked (using the Tk_DoWhenIdle
1142 * mechanism) to re-layout a set of windows managed by
1143 * the gridbag. It is invoked at idle time so that a
1144 * series of gridbag requests can be merged into a single
1151 * The slaves of masterPtr may get resized or moved.
1153 *--------------------------------------------------------------
1157 ArrangeGrid(clientData)
1158 ClientData clientData; /* Structure describing parent whose slaves
1159 * are to be re-layed out. */
1161 GridBag *masterPtr = (GridBag *) clientData;
1164 int i, x, y, width, height;
1167 CkWindow *parent, *ancestor;
1169 int intBWidth; /* Width of internal border in parent window,
1173 masterPtr->flags &= ~REQUESTED_RELAYOUT;
1176 * If the parent has no slaves anymore, then don't do anything
1177 * at all: just leave the parent's size as-is.
1178 * Even if row and column constraints have been set!
1181 if (masterPtr->slavePtr == NULL) {
1186 * Abort any nested call to ArrangeGrid for this window, since
1187 * we'll do everything necessary here, and set up so this call
1188 * can be aborted if necessary.
1191 if (masterPtr->abortPtr != NULL) {
1192 *masterPtr->abortPtr = 1;
1194 masterPtr->abortPtr = &abort;
1196 Ck_Preserve((ClientData) masterPtr);
1199 * Pass #1: scan all the slaves to figure out the total amount
1203 GetLayoutInfo(masterPtr, &info);
1204 GetMinSize(masterPtr, &info, &width, &height);
1206 if (((width != masterPtr->winPtr->reqWidth)
1207 || (height != masterPtr->winPtr->reqHeight))
1208 && !(masterPtr->flags & DONT_PROPAGATE)) {
1209 Ck_GeometryRequest(masterPtr->winPtr, width, height);
1210 masterPtr->flags |= REQUESTED_RELAYOUT;
1211 masterPtr->valid = 0;
1212 Tk_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
1217 * If the parent isn't mapped then don't do anything more: wait
1218 * until it gets mapped again. Need to get at least to here to
1219 * reflect size needs up the window hierarchy, but there's no
1220 * point in actually mapping the slaves.
1223 if (!(masterPtr->winPtr->flags & CK_MAPPED)) {
1228 * If the current dimensions of the window don't match the desired
1229 * dimensions, then adjust the minWidth and minHeight arrays
1230 * according to the weights.
1233 diffw = masterPtr->winPtr->width - (width + masterPtr->iPadX);
1236 for (i = 0; i < info.width; i++)
1237 weight += info.weightX[i];
1238 if (weight > MINWEIGHT) {
1239 for (i = 0; i < info.width; i++) {
1240 int dx = (int)(( ((double)diffw) * info.weightX[i]) / weight);
1241 info.minWidth[i] += dx;
1243 if (info.minWidth[i] < 0) {
1244 width -= info.minWidth[i];
1245 info.minWidth[i] = 0;
1249 diffw = masterPtr->winPtr->width - (width + masterPtr->iPadX);
1255 diffh = masterPtr->winPtr->height - (height + masterPtr->iPadY);
1258 for (i = 0; i < info.height; i++)
1259 weight += info.weightY[i];
1260 if (weight > MINWEIGHT) {
1261 for (i = 0; i < info.height; i++) {
1262 int dy = (int)(( ((double)diffh) * info.weightY[i]) / weight);
1263 info.minHeight[i] += dy;
1265 if (info.minHeight[i] < 0) {
1266 height -= info.minHeight[i];
1267 info.minHeight[i] = 0;
1271 diffh = masterPtr->winPtr->height - (height + masterPtr->iPadY);
1278 * Now do the actual layout of the slaves using the layout information
1279 * that has been collected.
1282 iPadX = masterPtr->iPadX/2;
1283 iPadY = masterPtr->iPadY/2;
1284 intBWidth = (masterPtr->winPtr->flags & CK_BORDER) ? 1 : 0;
1286 for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
1287 slavePtr = slavePtr->nextPtr) {
1289 masterPtr->startx = x = diffw/2 + intBWidth + iPadX;
1290 for(i = 0; i < slavePtr->tempX; i++)
1291 x += info.minWidth[i];
1293 masterPtr->starty = y = diffh/2 + intBWidth + iPadY;
1294 for(i = 0; i < slavePtr->tempY; i++)
1295 y += info.minHeight[i];
1298 for(i = slavePtr->tempX; i < (slavePtr->tempX + slavePtr->tempWidth);
1300 width += info.minWidth[i];
1303 for(i = slavePtr->tempY; i < (slavePtr->tempY + slavePtr->tempHeight);
1305 height += info.minHeight[i];
1307 AdjustForGravity(slavePtr, &x, &y, &width, &height);
1310 * If the window in which slavePtr is managed is not its
1311 * parent in the window hierarchy, translate the coordinates
1312 * to the coordinate system of the real X parent.
1315 parent = slavePtr->winPtr->parentPtr;
1316 for (ancestor = masterPtr->winPtr; ancestor != parent;
1317 ancestor = ancestor->parentPtr) {
1323 * If the window is too small to be interesting then
1324 * unmap it. Otherwise configure it and then make sure
1328 if ((width <= 0) || (height <= 0)) {
1329 Ck_UnmapWindow(slavePtr->winPtr);
1332 if (width != slavePtr->winPtr->width ||
1333 height != slavePtr->winPtr->height)
1334 Ck_ResizeWindow(slavePtr->winPtr, width, height);
1335 if (x != slavePtr->winPtr->x ||
1336 y != slavePtr->winPtr->y)
1337 Ck_MoveWindow(slavePtr->winPtr, x, y);
1339 * Temporary kludge til Ck_MoveResizeWindow available !!!
1341 if (width != slavePtr->winPtr->width ||
1342 height != slavePtr->winPtr->height)
1343 Ck_ResizeWindow(slavePtr->winPtr, width, height);
1347 Ck_MapWindow(slavePtr->winPtr);
1351 * Changes to the window's structure could cause almost anything
1352 * to happen, including deleting the parent or child. If this
1353 * happens, we'll be told to abort.
1362 masterPtr->abortPtr = NULL;
1363 Ck_Release((ClientData) masterPtr);
1367 *--------------------------------------------------------------
1371 * This internal procedure is used to locate a GridBag
1372 * structure for a given window, creating one if one
1373 * doesn't exist already.
1376 * The return value is a pointer to the GridBag structure
1377 * corresponding to winPtr.
1380 * A new gridbag structure may be created. If so, then
1381 * a callback is set up to clean things up when the
1382 * window is deleted.
1384 *--------------------------------------------------------------
1389 CkWindow *winPtr; /* Pointer to window for which
1390 * gridbag structure is desired. */
1393 Tcl_HashEntry *hPtr;
1398 Tcl_InitHashTable(&gridBagHashTable, TCL_ONE_WORD_KEYS);
1402 * See if there's already gridbag for this window. If not,
1403 * then create a new one.
1406 hPtr = Tcl_CreateHashEntry(&gridBagHashTable, (char *) winPtr, &new);
1408 return (GridBag *) Tcl_GetHashValue(hPtr);
1410 gridPtr = (GridBag *) ckalloc(sizeof(GridBag));
1411 gridPtr->winPtr = winPtr;
1412 gridPtr->masterPtr = NULL;
1413 gridPtr->nextPtr = NULL;
1414 gridPtr->slavePtr = NULL;
1416 gridPtr->gridColumn = gridPtr->gridRow = -1;
1417 gridPtr->gridWidth = gridPtr->gridHeight = 1;
1418 gridPtr->weightX = gridPtr->weightY = 0.0;
1419 gridPtr->minWidth = gridPtr->minHeight = 0;
1421 gridPtr->padX = gridPtr->padY = 0;
1422 gridPtr->iPadX = gridPtr->iPadY = 0;
1423 gridPtr->startx = gridPtr->starty = 0;
1424 gridPtr->abortPtr = NULL;
1427 gridPtr->column.max = 0;
1428 gridPtr->row.max = 0;
1429 gridPtr->column.used = 0;
1430 gridPtr->row.used = 0;
1433 gridPtr->layoutCache = NULL;
1435 Tcl_SetHashValue(hPtr, gridPtr);
1436 Ck_CreateEventHandler(winPtr,
1437 CK_EV_MAP | CK_EV_UNMAP | CK_EV_EXPOSE | CK_EV_DESTROY,
1438 GridBagStructureProc, (ClientData) gridPtr);
1443 *----------------------------------------------------------------------
1447 * Remove a gridbag from its parent's list of slaves.
1453 * The parent will be scheduled for re-arranging.
1455 *----------------------------------------------------------------------
1460 GridBag *gridPtr; /* Window to unlink. */
1462 GridBag *masterPtr, *gridPtr2;
1464 masterPtr = gridPtr->masterPtr;
1465 if (masterPtr == NULL) {
1468 if (masterPtr->slavePtr == gridPtr) {
1469 masterPtr->slavePtr = gridPtr->nextPtr;
1472 for (gridPtr2 = masterPtr->slavePtr; ; gridPtr2 = gridPtr2->nextPtr) {
1473 if (gridPtr2 == NULL) {
1474 panic("Unlink couldn't find previous window");
1476 if (gridPtr2->nextPtr == gridPtr) {
1477 gridPtr2->nextPtr = gridPtr->nextPtr;
1482 masterPtr->valid = 0;
1483 if (!(masterPtr->flags & REQUESTED_RELAYOUT)) {
1484 masterPtr->flags |= REQUESTED_RELAYOUT;
1485 Tk_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
1487 if (masterPtr->abortPtr != NULL) {
1488 *masterPtr->abortPtr = 1;
1491 gridPtr->masterPtr = NULL;
1495 *----------------------------------------------------------------------
1499 * This procedure is invoked by Ck_EventuallyFree or Ck_Release
1500 * to clean up the internal structure of a gridbag at a safe time
1501 * (when no-one is using it anymore).
1507 * Everything associated with the gridbag is freed up.
1509 *----------------------------------------------------------------------
1513 DestroyGridBag(memPtr)
1514 char *memPtr; /* Info about window that is now dead. */
1516 GridBag *gridPtr = (GridBag *) memPtr;
1518 if (gridPtr->column.max) {
1519 ckfree((char *) gridPtr->column.minsize);
1520 ckfree((char *) gridPtr->column.weight);
1522 if (gridPtr->row.max) {
1523 ckfree((char *) gridPtr->row.minsize);
1524 ckfree((char *) gridPtr->row.weight);
1526 if (gridPtr->layoutCache)
1527 ckfree((char *) gridPtr->layoutCache);
1529 ckfree((char *) gridPtr);
1533 *----------------------------------------------------------------------
1535 * GridBagStructureProc --
1537 * This procedure is invoked by the Ck event dispatcher in response
1538 * to window change events.
1544 * If a window was just deleted, clean up all its gridbag-related
1545 * information. If it was just resized, re-configure its slaves, if
1548 *----------------------------------------------------------------------
1552 GridBagStructureProc(clientData, eventPtr)
1553 ClientData clientData; /* Our information about window
1554 * referred to by eventPtr. */
1555 CkEvent *eventPtr; /* Describes what just happened. */
1557 GridBag *gridPtr = (GridBag *) clientData;
1559 if (eventPtr->type == CK_EV_MAP || eventPtr->type == CK_EV_EXPOSE) {
1561 if (!(gridPtr->flags & REQUESTED_RELAYOUT)) {
1562 gridPtr->flags |= REQUESTED_RELAYOUT;
1563 Tk_DoWhenIdle(ArrangeGrid, (ClientData) gridPtr);
1565 } else if (eventPtr->type == CK_EV_DESTROY) {
1566 GridBag *gridPtr2, *nextPtr;
1568 if (gridPtr->masterPtr != NULL) {
1571 for (gridPtr2 = gridPtr->slavePtr; gridPtr2 != NULL;
1572 gridPtr2 = nextPtr) {
1573 Ck_UnmapWindow(gridPtr2->winPtr);
1574 gridPtr2->masterPtr = NULL;
1575 nextPtr = gridPtr2->nextPtr;
1576 gridPtr2->nextPtr = NULL;
1578 Tcl_DeleteHashEntry(Tcl_FindHashEntry(&gridBagHashTable,
1579 (char *) gridPtr->winPtr));
1580 if (gridPtr->flags & REQUESTED_RELAYOUT) {
1581 Tk_CancelIdleCall(ArrangeGrid, (ClientData) gridPtr);
1583 gridPtr->winPtr = NULL;
1584 Ck_EventuallyFree((ClientData) gridPtr,
1585 (Ck_FreeProc *) DestroyGridBag);
1586 } else if (eventPtr->type == CK_EV_UNMAP) {
1589 for (gridPtr2 = gridPtr->slavePtr; gridPtr2 != NULL;
1590 gridPtr2 = gridPtr2->nextPtr) {
1591 Ck_UnmapWindow(gridPtr2->winPtr);
1597 *----------------------------------------------------------------------
1599 * ConfigureSlaves --
1601 * This implements the guts of the "grid configure" command. Given
1602 * a list of slaves and configuration options, it arranges for the
1603 * gridbag to manage the slaves and sets the specified options.
1606 * TCL_OK is returned if all went well. Otherwise, TCL_ERROR is
1607 * returned and interp->result is set to contain an error message.
1610 * Slave windows get taken over by the gridbag.
1612 *----------------------------------------------------------------------
1616 ConfigureSlaves(interp, winPtr, argc, argv)
1617 Tcl_Interp *interp; /* Interpreter for error reporting. */
1618 CkWindow *winPtr; /* Any window in application containing
1619 * slaves. Used to look up slave names. */
1620 int argc; /* Number of elements in argv. */
1621 char *argv[]; /* Argument strings: contains one or more
1622 * window names followed by any number
1623 * of "option value" pairs. Caller must
1624 * make sure that there is at least one
1627 GridBag *masterPtr, *slavePtr, *prevPtr;
1628 CkWindow *other, *slave;
1630 CkWindow *parent, *ancestor;
1632 int i, j, numWindows, c, length, tmp, positionGiven;
1633 int currentColumn=0, numColumns=1;
1639 * Find out how many windows are specified. (shouldn't use
1640 * hardwired symbols)
1643 for (numWindows = 0; numWindows < argc; numWindows++) {
1644 if (argv[numWindows][0] != '.'
1645 && strcmp(argv[numWindows],"-")!=0
1646 && strcmp(argv[numWindows],"^")!=0
1647 && strcmp(argv[numWindows],"x")!=0) {
1654 * Iterate over all of the slave windows, parsing the configuration
1655 * options for each slave. It's a bit wasteful to re-parse the
1656 * options for each slave, but things get too messy if we try to
1657 * parse the arguments just once at the beginning. For example,
1658 * if a slave already is managed we want to just change a few
1659 * existing values without resetting everything. If there are
1660 * multiple windows, the -in option only gets processed for the
1667 for (j = 0; j < numWindows; j++) {
1669 /* adjust default widget location for non-widgets */
1670 if (*argv[j] != '.') {
1672 case '^': /* extend the widget in the previous row
1673 * Since we don't know who the master is yet,
1674 * handle these in a separate pass at the end
1677 case REL_SKIP: /* skip over the next column */
1680 case REL_HORIZ: /* increase the span, already dealt with */
1681 /* not quite right */
1682 if (j>0 && (*argv[j-1] == REL_SKIP || *argv[j-1] == '^')) {
1683 Tcl_AppendResult(interp, "Invalid grid combination:",
1684 " \"-\" can't follow \"", argv[j - 1], "\"", NULL);
1689 panic("Invalid grid position indicator");
1694 for (numColumns = 1;
1695 j + numColumns < numWindows && *argv[j + numColumns] == REL_HORIZ;
1699 slave = Ck_NameToWindow(interp, argv[j], winPtr);
1700 if (slave == NULL) {
1703 if (slave->flags & CK_TOPLEVEL) {
1704 Tcl_AppendResult(interp, "can't manage \"", argv[j],
1705 "\": it's a top-level window", (char *) NULL);
1708 slavePtr = GetGridBag(slave);
1711 * The following statement is taken from tkPack.c:
1713 * "If the slave isn't currently managed, reset all of its
1714 * configuration information to default values (there could
1715 * be old values left from a previous packer)."
1717 * I disagree with this statement. If a slave is disabled (using
1718 * "forget") and then re-enabled, I submit that 90% of the time the
1719 * programmer will want it to retain its old configuration information.
1720 * If the programmer doesn't want this behavior, then she can reset the
1721 * defaults for herself, but she will never have to worry about keeping
1722 * track of the old state.
1725 for (i = numWindows; i < argc; i+=2) {
1727 Tcl_AppendResult(interp, "extra option \"", argv[i],
1728 "\" (option with no value?)", (char *) NULL);
1731 length = strlen(argv[i]);
1737 if ((c == 'i') && (strcmp(argv[i], "-in") == 0)) {
1739 other = Ck_NameToWindow(interp, argv[i+1], winPtr);
1740 if (other == NULL) {
1743 if (other == slave) {
1744 sprintf(interp->result,
1745 "Window can't be managed in itself");
1748 masterPtr = GetGridBag(other);
1749 prevPtr = masterPtr->slavePtr;
1750 if (prevPtr != NULL) {
1751 while (prevPtr->nextPtr != NULL) {
1752 prevPtr = prevPtr->nextPtr;
1759 if ((c == 'i') && (strcmp(argv[i], "-ipadx") == 0)) {
1760 if ((Ck_GetCoord(interp, slave, argv[i + 1], &tmp) != TCL_OK)
1762 Tcl_ResetResult(interp);
1763 Tcl_AppendResult(interp, "bad ipadx value \"", argv[i+1],
1764 "\": must be positive screen distance",
1768 slavePtr->iPadX = tmp*2;
1769 } else if ((c == 'i') && (strcmp(argv[i], "-ipady") == 0)) {
1770 if ((Ck_GetCoord(interp, slave, argv[i + 1], &tmp) != TCL_OK)
1772 Tcl_ResetResult(interp);
1773 Tcl_AppendResult(interp, "bad ipady value \"", argv[i+1],
1774 "\": must be positive screen distance",
1778 slavePtr->iPadY = tmp*2;
1779 } else if ((c == 'p') && (strcmp(argv[i], "-padx") == 0)) {
1780 if ((Ck_GetCoord(interp, slave, argv[i + 1], &tmp) != TCL_OK)
1782 Tcl_ResetResult(interp);
1783 Tcl_AppendResult(interp, "bad padx value \"", argv[i+1],
1784 "\": must be positive screen distance",
1788 slavePtr->padX = tmp*2;
1789 } else if ((c == 'p') && (strcmp(argv[i], "-pady") == 0)) {
1790 if ((Ck_GetCoord(interp, slave, argv[i + 1], &tmp) != TCL_OK)
1792 Tcl_ResetResult(interp);
1793 Tcl_AppendResult(interp, "bad pady value \"", argv[i+1],
1794 "\": must be positive screen distance",
1798 slavePtr->padY = tmp*2;
1799 } else if ((c == 'c') && (strcmp(argv[i], "-column") == 0)) {
1800 if (Tcl_GetInt(interp, argv[i+1], &tmp) != TCL_OK || tmp<0) {
1801 Tcl_ResetResult(interp);
1802 Tcl_AppendResult(interp, "bad column value \"", argv[i+1],
1803 "\": must be a non-negative integer", (char *) NULL);
1806 slavePtr->gridColumn = tmp;
1807 } else if ((c == 'r') && (strcmp(argv[i], "-row") == 0)) {
1808 if (Tcl_GetInt(interp, argv[i+1], &tmp) != TCL_OK || tmp<0) {
1809 Tcl_ResetResult(interp);
1810 Tcl_AppendResult(interp, "bad grid value \"", argv[i+1],
1811 "\": must be a non-negative integer", (char *) NULL);
1814 slavePtr->gridRow = tmp;
1815 } else if ((c == 'c') && (strcmp(argv[i], "-columnspan") == 0)) {
1816 if (Tcl_GetInt(interp, argv[i+1], &tmp) != TCL_OK ||
1818 Tcl_ResetResult(interp);
1819 Tcl_AppendResult(interp, "bad columnspan value \"",
1821 "\": must be a positive integer", (char *) NULL);
1824 slavePtr->gridWidth = tmp;
1826 } else if ((c == 'r') && (strcmp(argv[i], "-rowspan") == 0)) {
1827 if (Tcl_GetInt(interp, argv[i+1], &tmp) != TCL_OK) {
1828 Tcl_ResetResult(interp);
1829 Tcl_AppendResult(interp, "bad rowspan value \"",
1831 "\": must be a positive integer", (char *) NULL);
1834 slavePtr->gridHeight = tmp;
1836 } else if ((c == 'w') &&
1837 (!strcmp(argv[i], "-weightx") ||
1838 !strcmp(argv[i], "-wx"))) {
1839 if (Tcl_GetDouble(interp, argv[i+1], &tmp_dbl) != TCL_OK) {
1840 Tcl_ResetResult(interp);
1841 Tcl_AppendResult(interp, "bad weight value \"", argv[i+1],
1842 "\": must be a double", (char *) NULL);
1845 slavePtr->weightX = tmp_dbl;
1846 } else if ((c == 'w') &&
1847 (!strcmp(argv[i], "-weighty") ||
1848 !strcmp(argv[i], "-wy"))) {
1849 if (Tcl_GetDouble(interp, argv[i+1], &tmp_dbl) != TCL_OK) {
1850 Tcl_ResetResult(interp);
1851 Tcl_AppendResult(interp, "bad weight value \"", argv[i+1],
1852 "\": must be a double", (char *) NULL);
1855 slavePtr->weightY = tmp_dbl;
1857 } else if ((c == 's') && strcmp(argv[i], "-sticky") == 0) {
1858 int sticky = StringToSticky(argv[i+1]);
1860 Tcl_AppendResult(interp, "bad stickyness value \"",
1862 "\": must be a string containing n, e, s, and/or w",
1866 slavePtr->flags = sticky | (slavePtr->flags & ~STICK_ALL);
1869 Tcl_AppendResult(interp, "unknown or ambiguous option \"",
1871 argv[i], "\": must be -in, -sticky, ",
1873 argv[i], "\": must be -sticky, ",
1875 "-row, -column, -rowspan, -columnspan, ",
1876 "-ipadx, -ipady, -padx or -pady.",
1883 * If no position in a gridbag list was specified and the slave
1884 * is already managed, then leave it in its current location in
1885 * its current gridbag list.
1888 if (!positionGiven && (slavePtr->masterPtr != NULL)) {
1889 masterPtr = slavePtr->masterPtr;
1890 goto scheduleLayout;
1894 * If the slave is going to be put back after itself then
1895 * skip the whole operation, since it won't work anyway.
1898 if (prevPtr == slavePtr) {
1899 masterPtr = slavePtr->masterPtr;
1900 goto scheduleLayout;
1904 * If the "-in" option has not been specified, arrange for the
1905 * slave to go at the end of the order for its parent.
1908 if (!positionGiven) {
1909 masterPtr = GetGridBag(slave->parentPtr);
1910 prevPtr = masterPtr->slavePtr;
1911 if (prevPtr != NULL) {
1912 while (prevPtr->nextPtr != NULL) {
1913 prevPtr = prevPtr->nextPtr;
1919 * Make sure that the slave's parent is either the master or
1920 * an ancestor of the master.
1923 parent = slave->parentPtr;
1924 for (ancestor = masterPtr->winPtr; ; ancestor = ancestor->parentPtr) {
1925 if (ancestor == parent) {
1928 if (ancestor->flags & CK_TOPLEVEL) {
1929 Tcl_AppendResult(interp, "can't put ", argv[j],
1930 " inside ", masterPtr->winPtr->parentPtr,
1936 if (masterPtr->winPtr != slave->parentPtr) {
1937 Tcl_AppendResult(interp, "can't put ", argv[j],
1938 " inside ", masterPtr->winPtr->parentPtr,
1945 * Unlink the slave if it's currently managed, then position it
1949 if (slavePtr->masterPtr != NULL) {
1952 slavePtr->masterPtr = masterPtr;
1953 if (prevPtr == NULL) {
1954 slavePtr->nextPtr = masterPtr->slavePtr;
1955 masterPtr->slavePtr = slavePtr;
1957 slavePtr->nextPtr = prevPtr->nextPtr;
1958 prevPtr->nextPtr = slavePtr;
1960 Ck_ManageGeometry(slave, &gridMgrType, (ClientData) slavePtr);
1963 /* assign default row and column */
1965 if (slavePtr->gridColumn == -1) {
1966 slavePtr->gridColumn = currentColumn;
1968 slavePtr->gridWidth += numColumns - 1;
1969 if (slavePtr->gridRow == -1) {
1970 if (!gotLayout++) GetCachedLayoutInfo(masterPtr);
1971 slavePtr->gridRow = masterPtr->layoutCache->lastRow;
1975 * Arrange for the parent to be re-arranged at the first
1980 if (masterPtr->abortPtr != NULL) {
1981 *masterPtr->abortPtr = 1;
1983 masterPtr->valid = 0;
1984 if (!(masterPtr->flags & REQUESTED_RELAYOUT)) {
1985 masterPtr->flags |= REQUESTED_RELAYOUT;
1986 Tk_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
1988 currentColumn += slavePtr->gridWidth;
1992 /* now look for all the "^"'s */
1994 for (j = 0; j < numWindows; j++) {
1995 struct GridBag *otherPtr;
1996 char *lastWindow; /* use this window to base current row/col on */
1997 int match; /* found a match for the ^ */
1999 if (*argv[j] == '.') {
2000 lastWindow = argv[j];
2002 if (*argv[j] != '^') {
2005 for (width=1; width+j < numWindows && *argv[j+width] == '^'; width++) {
2008 other = Ck_NameToWindow(interp, lastWindow, winPtr);
2009 otherPtr = GetGridBag(other);
2010 if (!gotLayout++) GetCachedLayoutInfo(masterPtr);
2012 for (match=0, slavePtr = masterPtr->slavePtr; slavePtr != NULL;
2013 slavePtr = slavePtr->nextPtr) {
2015 if (slavePtr->gridWidth == width
2016 && slavePtr->gridColumn == otherPtr->gridColumn +
2018 && slavePtr->gridRow + slavePtr->gridHeight ==
2019 otherPtr->gridRow) {
2020 slavePtr->gridHeight++;
2023 lastWindow = slavePtr->winPtr->pathName;
2026 Tcl_AppendResult(interp, "can't find slave to extend with \"^\"",
2027 " after ", lastWindow, (char *) NULL);
2036 *----------------------------------------------------------------------
2038 * Convert "Sticky" bits into a string
2040 *----------------------------------------------------------------------
2044 StickyToString(flags, result)
2045 int flags; /* the sticky flags */
2046 char *result; /* where to put the result */
2049 if (flags & STICK_NORTH)
2050 result[count++] = 'n';
2051 if (flags & STICK_EAST)
2052 result[count++] = 'e';
2053 if (flags & STICK_SOUTH)
2054 result[count++] = 's';
2055 if (flags & STICK_WEST)
2056 result[count++] = 'w';
2058 result[count] = '\0';
2060 sprintf(result,"{}");
2065 *----------------------------------------------------------------------
2067 * Convert sticky string to flags
2069 *----------------------------------------------------------------------
2073 StringToSticky(string)
2079 while ((c = *string++) != '\0') {
2081 case 'n': case 'N': sticky |= STICK_NORTH; break;
2082 case 'e': case 'E': sticky |= STICK_EAST; break;
2083 case 's': case 'S': sticky |= STICK_SOUTH; break;
2084 case 'w': case 'W': sticky |= STICK_WEST; break;
2085 case ' ': case ',': case '\t': case '\r': case '\n': break;