/[dtapublic]/projs/trunk/shared_source/c_tk_base_7_5_w_mods/tkgrid.c
ViewVC logotype

Annotation of /projs/trunk/shared_source/c_tk_base_7_5_w_mods/tkgrid.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 42 - (hide annotations) (download)
Fri Oct 14 01:50:00 2016 UTC (8 years ago) by dashley
Original Path: projs/trunk/shared_source/tk_base/tkgrid.c
File MIME type: text/plain
File size: 83092 byte(s)
Move shared source code to commonize.
1 dashley 25 /* $Header: /cvsroot/esrg/sfesrg/esrgpcpj/shared/tk_base/tkgrid.c,v 1.1.1.1 2001/06/13 05:02:05 dtashley Exp $ */
2    
3     /*
4     * tkGrid.c --
5     *
6     * Grid based geometry manager.
7     *
8     * Copyright (c) 1996-1997 by Sun Microsystems, Inc.
9     *
10     * See the file "license.terms" for information on usage and redistribution
11     * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
12     *
13     * RCS: @(#) $Id: tkgrid.c,v 1.1.1.1 2001/06/13 05:02:05 dtashley Exp $
14     */
15    
16     #include "tkInt.h"
17    
18     /*
19     * Convenience Macros
20     */
21    
22     #ifdef MAX
23     # undef MAX
24     #endif
25     #define MAX(x,y) ((x) > (y) ? (x) : (y))
26     #ifdef MIN
27     # undef MIN
28     #endif
29     #define MIN(x,y) ((x) > (y) ? (y) : (x))
30    
31     #define COLUMN (1) /* working on column offsets */
32     #define ROW (2) /* working on row offsets */
33    
34     #define CHECK_ONLY (1) /* check max slot constraint */
35     #define CHECK_SPACE (2) /* alloc more space, don't change max */
36    
37     /*
38     * Pre-allocate enough row and column slots for "typical" sized tables
39     * this value should be chosen so by the time the extra malloc's are
40     * required, the layout calculations overwehlm them. [A "slot" contains
41     * information for either a row or column, depending upon the context.]
42     */
43    
44     #define TYPICAL_SIZE 25 /* (arbitrary guess) */
45     #define PREALLOC 10 /* extra slots to allocate */
46    
47     /*
48     * Data structures are allocated dynamically to support arbitrary sized tables.
49     * However, the space is proportional to the highest numbered slot with
50     * some non-default property. This limit is used to head off mistakes and
51     * denial of service attacks by limiting the amount of storage required.
52     */
53    
54     #define MAX_ELEMENT 10000
55    
56     /*
57     * Special characters to support relative layouts.
58     */
59    
60     #define REL_SKIP 'x' /* Skip this column. */
61     #define REL_HORIZ '-' /* Extend previous widget horizontally. */
62     #define REL_VERT '^' /* Extend widget from row above. */
63    
64     /*
65     * Structure to hold information for grid masters. A slot is either
66     * a row or column.
67     */
68    
69     typedef struct SlotInfo {
70     int minSize; /* The minimum size of this slot (in pixels).
71     * It is set via the rowconfigure or
72     * columnconfigure commands. */
73     int weight; /* The resize weight of this slot. (0) means
74     * this slot doesn't resize. Extra space in
75     * the layout is given distributed among slots
76     * inproportion to their weights. */
77     int pad; /* Extra padding, in pixels, required for
78     * this slot. This amount is "added" to the
79     * largest slave in the slot. */
80     int offset; /* This is a cached value used for
81     * introspection. It is the pixel
82     * offset of the right or bottom edge
83     * of this slot from the beginning of the
84     * layout. */
85     int temp; /* This is a temporary value used for
86     * calculating adjusted weights when
87     * shrinking the layout below its
88     * nominal size. */
89     } SlotInfo;
90    
91     /*
92     * Structure to hold information during layout calculations. There
93     * is one of these for each slot, an array for each of the rows or columns.
94     */
95    
96     typedef struct GridLayout {
97     struct Gridder *binNextPtr; /* The next slave window in this bin.
98     * Each bin contains a list of all
99     * slaves whose spans are >1 and whose
100     * right edges fall in this slot. */
101     int minSize; /* Minimum size needed for this slot,
102     * in pixels. This is the space required
103     * to hold any slaves contained entirely
104     * in this slot, adjusted for any slot
105     * constrants, such as size or padding. */
106     int pad; /* Padding needed for this slot */
107     int weight; /* Slot weight, controls resizing. */
108     int minOffset; /* The minimum offset, in pixels, from
109     * the beginning of the layout to the
110     * right/bottom edge of the slot calculated
111     * from top/left to bottom/right. */
112     int maxOffset; /* The maximum offset, in pixels, from
113     * the beginning of the layout to the
114     * right-or-bottom edge of the slot calculated
115     * from bottom-or-right to top-or-left. */
116     } GridLayout;
117    
118     /*
119     * Keep one of these for each geometry master.
120     */
121    
122     typedef struct {
123     SlotInfo *columnPtr; /* Pointer to array of column constraints. */
124     SlotInfo *rowPtr; /* Pointer to array of row constraints. */
125     int columnEnd; /* The last column occupied by any slave. */
126     int columnMax; /* The number of columns with constraints. */
127     int columnSpace; /* The number of slots currently allocated for
128     * column constraints. */
129     int rowEnd; /* The last row occupied by any slave. */
130     int rowMax; /* The number of rows with constraints. */
131     int rowSpace; /* The number of slots currently allocated
132     * for row constraints. */
133     int startX; /* Pixel offset of this layout within its
134     * parent. */
135     int startY; /* Pixel offset of this layout within its
136     * parent. */
137     } GridMaster;
138    
139     /*
140     * For each window that the grid cares about (either because
141     * the window is managed by the grid or because the window
142     * has slaves that are managed by the grid), there is a
143     * structure of the following type:
144     */
145    
146     typedef struct Gridder {
147     Tk_Window tkwin; /* Tk token for window. NULL means that
148     * the window has been deleted, but the
149     * gridder hasn't had a chance to clean up
150     * yet because the structure is still in
151     * use. */
152     struct Gridder *masterPtr; /* Master window within which this window
153     * is managed (NULL means this window
154     * isn't managed by the gridder). */
155     struct Gridder *nextPtr; /* Next window managed within same
156     * parent. List order doesn't matter. */
157     struct Gridder *slavePtr; /* First in list of slaves managed
158     * inside this window (NULL means
159     * no grid slaves). */
160     GridMaster *masterDataPtr; /* Additional data for geometry master. */
161     int column, row; /* Location in the grid (starting
162     * from zero). */
163     int numCols, numRows; /* Number of columns or rows this slave spans.
164     * Should be at least 1. */
165     int padX, padY; /* Total additional pixels to leave around the
166     * window (half of this space is left on each
167     * side). This is space *outside* the window:
168     * we'll allocate extra space in frame but
169     * won't enlarge window). */
170     int iPadX, iPadY; /* Total extra pixels to allocate inside the
171     * window (half this amount will appear on
172     * each side). */
173     int sticky; /* which sides of its cavity this window
174     * sticks to. See below for definitions */
175     int doubleBw; /* Twice the window's last known border
176     * width. If this changes, the window
177     * must be re-arranged within its parent. */
178     int *abortPtr; /* If non-NULL, it means that there is a nested
179     * call to ArrangeGrid already working on
180     * this window. *abortPtr may be set to 1 to
181     * abort that nested call. This happens, for
182     * example, if tkwin or any of its slaves
183     * is deleted. */
184     int flags; /* Miscellaneous flags; see below
185     * for definitions. */
186    
187     /*
188     * These fields are used temporarily for layout calculations only.
189     */
190    
191     struct Gridder *binNextPtr; /* Link to next span>1 slave in this bin. */
192     int size; /* Nominal size (width or height) in pixels
193     * of the slave. This includes the padding. */
194     } Gridder;
195    
196     /* Flag values for "sticky"ness The 16 combinations subsume the packer's
197     * notion of anchor and fill.
198     *
199     * STICK_NORTH This window sticks to the top of its cavity.
200     * STICK_EAST This window sticks to the right edge of its cavity.
201     * STICK_SOUTH This window sticks to the bottom of its cavity.
202     * STICK_WEST This window sticks to the left edge of its cavity.
203     */
204    
205     #define STICK_NORTH 1
206     #define STICK_EAST 2
207     #define STICK_SOUTH 4
208     #define STICK_WEST 8
209    
210     /*
211     * Flag values for Grid structures:
212     *
213     * REQUESTED_RELAYOUT: 1 means a Tcl_DoWhenIdle request
214     * has already been made to re-arrange
215     * all the slaves of this window.
216     *
217     * DONT_PROPAGATE: 1 means don't set this window's requested
218     * size. 0 means if this window is a master
219     * then Tk will set its requested size to fit
220     * the needs of its slaves.
221     */
222    
223     #define REQUESTED_RELAYOUT 1
224     #define DONT_PROPAGATE 2
225    
226     /*
227     * Prototypes for procedures used only in this file:
228     */
229    
230     static void AdjustForSticky _ANSI_ARGS_((Gridder *slavePtr, int *xPtr,
231     int *yPtr, int *widthPtr, int *heightPtr));
232     static int AdjustOffsets _ANSI_ARGS_((int width,
233     int elements, SlotInfo *slotPtr));
234     static void ArrangeGrid _ANSI_ARGS_((ClientData clientData));
235     static int CheckSlotData _ANSI_ARGS_((Gridder *masterPtr, int slot,
236     int slotType, int checkOnly));
237     static int ConfigureSlaves _ANSI_ARGS_((Tcl_Interp *interp,
238     Tk_Window tkwin, int argc, char *argv[]));
239     static void DestroyGrid _ANSI_ARGS_((char *memPtr));
240     static Gridder *GetGrid _ANSI_ARGS_((Tk_Window tkwin));
241     static void GridStructureProc _ANSI_ARGS_((
242     ClientData clientData, XEvent *eventPtr));
243     static void GridLostSlaveProc _ANSI_ARGS_((ClientData clientData,
244     Tk_Window tkwin));
245     static void GridReqProc _ANSI_ARGS_((ClientData clientData,
246     Tk_Window tkwin));
247     static void InitMasterData _ANSI_ARGS_((Gridder *masterPtr));
248     static int ResolveConstraints _ANSI_ARGS_((Gridder *gridPtr,
249     int rowOrColumn, int maxOffset));
250     static void SetGridSize _ANSI_ARGS_((Gridder *gridPtr));
251     static void StickyToString _ANSI_ARGS_((int flags, char *result));
252     static int StringToSticky _ANSI_ARGS_((char *string));
253     static void Unlink _ANSI_ARGS_((Gridder *gridPtr));
254    
255     static Tk_GeomMgr gridMgrType = {
256     "grid", /* name */
257     GridReqProc, /* requestProc */
258     GridLostSlaveProc, /* lostSlaveProc */
259     };
260    
261     /*
262     *--------------------------------------------------------------
263     *
264     * Tk_GridCmd --
265     *
266     * This procedure is invoked to process the "grid" Tcl command.
267     * See the user documentation for details on what it does.
268     *
269     * Results:
270     * A standard Tcl result.
271     *
272     * Side effects:
273     * See the user documentation.
274     *
275     *--------------------------------------------------------------
276     */
277    
278     int
279     Tk_GridCmd(clientData, interp, argc, argv)
280     ClientData clientData; /* Main window associated with
281     * interpreter. */
282     Tcl_Interp *interp; /* Current interpreter. */
283     int argc; /* Number of arguments. */
284     char **argv; /* Argument strings. */
285     {
286     Tk_Window tkwin = (Tk_Window) clientData;
287     Gridder *masterPtr; /* master grid record */
288     GridMaster *gridPtr; /* pointer to grid data */
289     size_t length; /* streing length of argument */
290     char c; /* 1st character of argument */
291    
292     if ((argc >= 2) && ((argv[1][0] == '.') || (argv[1][0] == REL_SKIP) ||
293     (argv[1][0] == REL_VERT))) {
294     return ConfigureSlaves(interp, tkwin, argc-1, argv+1);
295     }
296     if (argc < 3) {
297     Tcl_AppendResult(interp, "wrong # args: should be \"",
298     argv[0], " option arg ?arg ...?\"", (char *) NULL);
299     return TCL_ERROR;
300     }
301     c = argv[1][0];
302     length = strlen(argv[1]);
303    
304     if ((c == 'b') && (strncmp(argv[1], "bbox", length) == 0)) {
305     Tk_Window master;
306     int row, column; /* origin for bounding box */
307     int row2, column2; /* end of bounding box */
308     int endX, endY; /* last column/row in the layout */
309     int x=0, y=0; /* starting pixels for this bounding box */
310     int width, height; /* size of the bounding box */
311     char buf[TCL_INTEGER_SPACE * 4];
312    
313     if (argc!=3 && argc != 5 && argc != 7) {
314     Tcl_AppendResult(interp, "wrong number of arguments: ",
315     "must be \"",argv[0],
316     " bbox master ?column row ?column row??\"",
317     (char *) NULL);
318     return TCL_ERROR;
319     }
320    
321     master = Tk_NameToWindow(interp, argv[2], tkwin);
322     if (master == NULL) {
323     return TCL_ERROR;
324     }
325     masterPtr = GetGrid(master);
326    
327     if (argc >= 5) {
328     if (Tcl_GetInt(interp, argv[3], &column) != TCL_OK) {
329     return TCL_ERROR;
330     }
331     if (Tcl_GetInt(interp, argv[4], &row) != TCL_OK) {
332     return TCL_ERROR;
333     }
334     column2 = column;
335     row2 = row;
336     }
337    
338     if (argc == 7) {
339     if (Tcl_GetInt(interp, argv[5], &column2) != TCL_OK) {
340     return TCL_ERROR;
341     }
342     if (Tcl_GetInt(interp, argv[6], &row2) != TCL_OK) {
343     return TCL_ERROR;
344     }
345     }
346    
347     gridPtr = masterPtr->masterDataPtr;
348     if (gridPtr == NULL) {
349     Tcl_SetResult(interp, "0 0 0 0", TCL_STATIC);
350     return(TCL_OK);
351     }
352    
353     SetGridSize(masterPtr);
354     endX = MAX(gridPtr->columnEnd, gridPtr->columnMax);
355     endY = MAX(gridPtr->rowEnd, gridPtr->rowMax);
356    
357     if ((endX == 0) || (endY == 0)) {
358     Tcl_SetResult(interp, "0 0 0 0", TCL_STATIC);
359     return(TCL_OK);
360     }
361     if (argc == 3) {
362     row = column = 0;
363     row2 = endY;
364     column2 = endX;
365     }
366    
367     if (column > column2) {
368     int temp = column;
369     column = column2, column2 = temp;
370     }
371     if (row > row2) {
372     int temp = row;
373     row = row2, row2 = temp;
374     }
375    
376     if (column > 0 && column < endX) {
377     x = gridPtr->columnPtr[column-1].offset;
378     } else if (column > 0) {
379     x = gridPtr->columnPtr[endX-1].offset;
380     }
381    
382     if (row > 0 && row < endY) {
383     y = gridPtr->rowPtr[row-1].offset;
384     } else if (row > 0) {
385     y = gridPtr->rowPtr[endY-1].offset;
386     }
387    
388     if (column2 < 0) {
389     width = 0;
390     } else if (column2 >= endX) {
391     width = gridPtr->columnPtr[endX-1].offset - x;
392     } else {
393     width = gridPtr->columnPtr[column2].offset - x;
394     }
395    
396     if (row2 < 0) {
397     height = 0;
398     } else if (row2 >= endY) {
399     height = gridPtr->rowPtr[endY-1].offset - y;
400     } else {
401     height = gridPtr->rowPtr[row2].offset - y;
402     }
403    
404     sprintf(buf, "%d %d %d %d", x + gridPtr->startX, y + gridPtr->startY,
405     width, height);
406     Tcl_SetResult(interp, buf, TCL_VOLATILE);
407     } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) {
408     if (argv[2][0] != '.') {
409     Tcl_AppendResult(interp, "bad argument \"", argv[2],
410     "\": must be name of window", (char *) NULL);
411     return TCL_ERROR;
412     }
413     return ConfigureSlaves(interp, tkwin, argc-2, argv+2);
414     } else if (((c == 'f') && (strncmp(argv[1], "forget", length) == 0)) ||
415     ((c == 'r') && (strncmp(argv[1], "remove", length) == 0))) {
416     Tk_Window slave;
417     Gridder *slavePtr;
418     int i;
419    
420     for (i = 2; i < argc; i++) {
421     slave = Tk_NameToWindow(interp, argv[i], tkwin);
422     if (slave == NULL) {
423     return TCL_ERROR;
424     }
425     slavePtr = GetGrid(slave);
426     if (slavePtr->masterPtr != NULL) {
427    
428     /*
429     * For "forget", reset all the settings to their defaults
430     */
431    
432     if (c == 'f') {
433     slavePtr->column = slavePtr->row = -1;
434     slavePtr->numCols = 1;
435     slavePtr->numRows = 1;
436     slavePtr->padX = slavePtr->padY = 0;
437     slavePtr->iPadX = slavePtr->iPadY = 0;
438     slavePtr->doubleBw = 2*Tk_Changes(tkwin)->border_width;
439     if (slavePtr->flags & REQUESTED_RELAYOUT) {
440     Tcl_CancelIdleCall(ArrangeGrid, (ClientData) slavePtr);
441     }
442     slavePtr->flags = 0;
443     slavePtr->sticky = 0;
444     }
445     Tk_ManageGeometry(slave, (Tk_GeomMgr *) NULL,
446     (ClientData) NULL);
447     if (slavePtr->masterPtr->tkwin != Tk_Parent(slavePtr->tkwin)) {
448     Tk_UnmaintainGeometry(slavePtr->tkwin,
449     slavePtr->masterPtr->tkwin);
450     }
451     Unlink(slavePtr);
452     Tk_UnmapWindow(slavePtr->tkwin);
453     }
454     }
455     } else if ((c == 'i') && (strncmp(argv[1], "info", length) == 0)) {
456     register Gridder *slavePtr;
457     Tk_Window slave;
458     char buffer[64 + TCL_INTEGER_SPACE * 4];
459    
460     if (argc != 3) {
461     Tcl_AppendResult(interp, "wrong # args: should be \"",
462     argv[0], " info window\"", (char *) NULL);
463     return TCL_ERROR;
464     }
465     slave = Tk_NameToWindow(interp, argv[2], tkwin);
466     if (slave == NULL) {
467     return TCL_ERROR;
468     }
469     slavePtr = GetGrid(slave);
470     if (slavePtr->masterPtr == NULL) {
471     Tcl_ResetResult(interp);
472     return TCL_OK;
473     }
474    
475     Tcl_AppendElement(interp, "-in");
476     Tcl_AppendElement(interp, Tk_PathName(slavePtr->masterPtr->tkwin));
477     sprintf(buffer, " -column %d -row %d -columnspan %d -rowspan %d",
478     slavePtr->column, slavePtr->row,
479     slavePtr->numCols, slavePtr->numRows);
480     Tcl_AppendResult(interp, buffer, (char *) NULL);
481     sprintf(buffer, " -ipadx %d -ipady %d -padx %d -pady %d",
482     slavePtr->iPadX/2, slavePtr->iPadY/2, slavePtr->padX/2,
483     slavePtr->padY/2);
484     Tcl_AppendResult(interp, buffer, (char *) NULL);
485     StickyToString(slavePtr->sticky,buffer);
486     Tcl_AppendResult(interp, " -sticky ", buffer, (char *) NULL);
487     } else if((c == 'l') && (strncmp(argv[1], "location", length) == 0)) {
488     Tk_Window master;
489     register SlotInfo *slotPtr;
490     int x, y; /* Offset in pixels, from edge of parent. */
491     int i, j; /* Corresponding column and row indeces. */
492     int endX, endY; /* end of grid */
493     char buf[TCL_INTEGER_SPACE * 2];
494    
495     if (argc != 5) {
496     Tcl_AppendResult(interp, "wrong # args: should be \"",
497     argv[0], " location master x y\"", (char *)NULL);
498     return TCL_ERROR;
499     }
500    
501     master = Tk_NameToWindow(interp, argv[2], tkwin);
502     if (master == NULL) {
503     return TCL_ERROR;
504     }
505    
506     if (Tk_GetPixels(interp, master, argv[3], &x) != TCL_OK) {
507     return TCL_ERROR;
508     }
509     if (Tk_GetPixels(interp, master, argv[4], &y) != TCL_OK) {
510     return TCL_ERROR;
511     }
512    
513     masterPtr = GetGrid(master);
514     if (masterPtr->masterDataPtr == NULL) {
515     Tcl_SetResult(interp, "-1 -1", TCL_STATIC);
516     return TCL_OK;
517     }
518     gridPtr = masterPtr->masterDataPtr;
519    
520     /*
521     * Update any pending requests. This is not always the
522     * steady state value, as more configure events could be in
523     * the pipeline, but its as close as its easy to get.
524     */
525    
526     while (masterPtr->flags & REQUESTED_RELAYOUT) {
527     Tcl_CancelIdleCall(ArrangeGrid, (ClientData) masterPtr);
528     ArrangeGrid ((ClientData) masterPtr);
529     }
530     SetGridSize(masterPtr);
531     endX = MAX(gridPtr->columnEnd, gridPtr->columnMax);
532     endY = MAX(gridPtr->rowEnd, gridPtr->rowMax);
533    
534     slotPtr = masterPtr->masterDataPtr->columnPtr;
535     if (x < masterPtr->masterDataPtr->startX) {
536     i = -1;
537     } else {
538     x -= masterPtr->masterDataPtr->startX;
539     for (i=0;slotPtr[i].offset < x && i < endX; i++) {
540     /* null body */
541     }
542     }
543    
544     slotPtr = masterPtr->masterDataPtr->rowPtr;
545     if (y < masterPtr->masterDataPtr->startY) {
546     j = -1;
547     } else {
548     y -= masterPtr->masterDataPtr->startY;
549     for (j=0;slotPtr[j].offset < y && j < endY; j++) {
550     /* null body */
551     }
552     }
553    
554     sprintf(buf, "%d %d", i, j);
555     Tcl_SetResult(interp, buf, TCL_VOLATILE);
556     } else if ((c == 'p') && (strncmp(argv[1], "propagate", length) == 0)) {
557     Tk_Window master;
558     int propagate;
559    
560     if (argc > 4) {
561     Tcl_AppendResult(interp, "wrong # args: should be \"",
562     argv[0], " propagate window ?boolean?\"",
563     (char *) NULL);
564     return TCL_ERROR;
565     }
566     master = Tk_NameToWindow(interp, argv[2], tkwin);
567     if (master == NULL) {
568     return TCL_ERROR;
569     }
570     masterPtr = GetGrid(master);
571     if (argc == 3) {
572     Tcl_SetResult(interp,
573     ((masterPtr->flags & DONT_PROPAGATE) ? "0" : "1"),
574     TCL_STATIC);
575     return TCL_OK;
576     }
577     if (Tcl_GetBoolean(interp, argv[3], &propagate) != TCL_OK) {
578     return TCL_ERROR;
579     }
580    
581     /* Only request a relayout if the propagation bit changes */
582    
583     if ((!propagate) ^ (masterPtr->flags&DONT_PROPAGATE)) {
584     if (propagate) {
585     masterPtr->flags &= ~DONT_PROPAGATE;
586     } else {
587     masterPtr->flags |= DONT_PROPAGATE;
588     }
589    
590     /*
591     * Re-arrange the master to allow new geometry information to
592     * propagate upwards to the master's master.
593     */
594    
595     if (masterPtr->abortPtr != NULL) {
596     *masterPtr->abortPtr = 1;
597     }
598     if (!(masterPtr->flags & REQUESTED_RELAYOUT)) {
599     masterPtr->flags |= REQUESTED_RELAYOUT;
600     Tcl_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
601     }
602     }
603     } else if ((c == 's') && (strncmp(argv[1], "size", length) == 0)
604     && (length > 1)) {
605     Tk_Window master;
606    
607     if (argc != 3) {
608     Tcl_AppendResult(interp, "wrong # args: should be \"",
609     argv[0], " size window\"", (char *) NULL);
610     return TCL_ERROR;
611     }
612     master = Tk_NameToWindow(interp, argv[2], tkwin);
613     if (master == NULL) {
614     return TCL_ERROR;
615     }
616     masterPtr = GetGrid(master);
617    
618     if (masterPtr->masterDataPtr != NULL) {
619     char buf[TCL_INTEGER_SPACE * 2];
620    
621     SetGridSize(masterPtr);
622     gridPtr = masterPtr->masterDataPtr;
623     sprintf(buf, "%d %d",
624     MAX(gridPtr->columnEnd, gridPtr->columnMax),
625     MAX(gridPtr->rowEnd, gridPtr->rowMax));
626     Tcl_SetResult(interp, buf, TCL_VOLATILE);
627     } else {
628     Tcl_SetResult(interp, "0 0", TCL_STATIC);
629     }
630     } else if ((c == 's') && (strncmp(argv[1], "slaves", length) == 0)
631     && (length > 1)) {
632     Tk_Window master;
633     Gridder *slavePtr;
634     int i, value;
635     int row = -1, column = -1;
636    
637     if ((argc < 3) || ((argc%2) == 0)) {
638     Tcl_AppendResult(interp, "wrong # args: should be \"",
639     argv[0], " slaves window ?-option value...?\"",
640     (char *) NULL);
641     return TCL_ERROR;
642     }
643    
644     for (i=3; i<argc; i+=2) {
645     length = strlen(argv[i]);
646     if ((*argv[i] != '-') || (length < 2)) {
647     Tcl_AppendResult(interp, "invalid args: should be \"",
648     argv[0], " slaves window ?-option value...?\"",
649     (char *) NULL);
650     return TCL_ERROR;
651     }
652     if (Tcl_GetInt(interp, argv[i+1], &value) != TCL_OK) {
653     return TCL_ERROR;
654     }
655     if (value < 0) {
656     Tcl_AppendResult(interp, argv[i],
657     " is an invalid value: should NOT be < 0",
658     (char *) NULL);
659     return TCL_ERROR;
660     }
661     if (strncmp(argv[i], "-column", length) == 0) {
662     column = value;
663     } else if (strncmp(argv[i], "-row", length) == 0) {
664     row = value;
665     } else {
666     Tcl_AppendResult(interp, argv[i],
667     " is an invalid option: should be \"",
668     "-row, -column\"",
669     (char *) NULL);
670     return TCL_ERROR;
671     }
672     }
673     master = Tk_NameToWindow(interp, argv[2], tkwin);
674     if (master == NULL) {
675     return TCL_ERROR;
676     }
677     masterPtr = GetGrid(master);
678    
679     for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
680     slavePtr = slavePtr->nextPtr) {
681     if (column>=0 && (slavePtr->column > column
682     || slavePtr->column+slavePtr->numCols-1 < column)) {
683     continue;
684     }
685     if (row>=0 && (slavePtr->row > row ||
686     slavePtr->row+slavePtr->numRows-1 < row)) {
687     continue;
688     }
689     Tcl_AppendElement(interp, Tk_PathName(slavePtr->tkwin));
690     }
691    
692     /*
693     * Sample argument combinations:
694     * grid columnconfigure <master> <index> -option
695     * grid columnconfigure <master> <index> -option value -option value
696     * grid rowconfigure <master> <index>
697     * grid rowconfigure <master> <index> -option
698     * grid rowconfigure <master> <index> -option value -option value.
699     */
700    
701     } else if(((c == 'c') && (strncmp(argv[1], "columnconfigure", length) == 0)
702     && (length >= 3)) ||
703     ((c == 'r') && (strncmp(argv[1], "rowconfigure", length) == 0)
704     && (length >=2))) {
705     Tk_Window master;
706     SlotInfo *slotPtr = NULL;
707     int slot; /* the column or row number */
708     size_t length; /* the # of chars in the "-option" string */
709     int slotType; /* COLUMN or ROW */
710     int size; /* the configuration value */
711     int checkOnly; /* check the size only */
712     int argcPtr; /* Number of items in index list */
713     char **argvPtr; /* array of indeces */
714     char **indexP; /* String value of current index list item. */
715     int ok; /* temporary TCL result code */
716     int i;
717    
718     if (((argc%2 != 0) && (argc>6)) || (argc < 4)) {
719     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
720     " ", argv[1], " master index ?-option value...?\"",
721     (char *)NULL);
722     return TCL_ERROR;
723     }
724    
725     master = Tk_NameToWindow(interp, argv[2], tkwin);
726     if (master == NULL) {
727     return TCL_ERROR;
728     }
729    
730     if (Tcl_SplitList(interp, argv[3], &argcPtr, &argvPtr) != TCL_OK) {
731     return TCL_ERROR;
732     }
733    
734     checkOnly = ((argc == 4) || (argc == 5));
735     masterPtr = GetGrid(master);
736     slotType = (c == 'c') ? COLUMN : ROW;
737     if (checkOnly && argcPtr > 1) {
738     Tcl_AppendResult(interp, argv[3],
739     " must be a single element.", (char *) NULL);
740     Tcl_Free((char *)argvPtr);
741     return TCL_ERROR;
742     }
743     for (indexP=argvPtr; *indexP != NULL; indexP++) {
744     if (Tcl_GetInt(interp, *indexP, &slot) != TCL_OK) {
745     Tcl_Free((char *)argvPtr);
746     return TCL_ERROR;
747     }
748     ok = CheckSlotData(masterPtr, slot, slotType, checkOnly);
749     if ((ok!=TCL_OK) && ((argc<4) || (argc>5))) {
750     Tcl_AppendResult(interp, argv[0],
751     " ", argv[1], ": \"", *argvPtr,"\" is out of range",
752     (char *) NULL);
753     Tcl_Free((char *)argvPtr);
754     return TCL_ERROR;
755     } else if (ok == TCL_OK) {
756     slotPtr = (slotType == COLUMN) ?
757     masterPtr->masterDataPtr->columnPtr :
758     masterPtr->masterDataPtr->rowPtr;
759     }
760    
761     /*
762     * Return all of the options for this row or column. If the
763     * request is out of range, return all 0's.
764     */
765    
766     if (argc == 4) {
767     Tcl_Free((char *)argvPtr);
768     }
769     if ((argc == 4) && (ok == TCL_OK)) {
770     char buf[64 + TCL_INTEGER_SPACE * 3];
771    
772     sprintf(buf, "-minsize %d -pad %d -weight %d",
773     slotPtr[slot].minSize,slotPtr[slot].pad,
774     slotPtr[slot].weight);
775     Tcl_SetResult(interp, buf, TCL_VOLATILE);
776     return (TCL_OK);
777     } else if (argc == 4) {
778     Tcl_SetResult(interp, "-minsize 0 -pad 0 -weight 0",
779     TCL_STATIC);
780     return (TCL_OK);
781     }
782    
783     /*
784     * Loop through each option value pair, setting the values as required.
785     * If only one option is given, with no value, the current value is
786     * returned.
787     */
788    
789     for (i=4; i<argc; i+=2) {
790     length = strlen(argv[i]);
791     if ((*argv[i] != '-') || length < 2) {
792     Tcl_AppendResult(interp, "invalid arg \"",
793     argv[i], "\" :expecting -minsize, -pad, or -weight.",
794     (char *) NULL);
795     Tcl_Free((char *)argvPtr);
796     return TCL_ERROR;
797     }
798     if (strncmp(argv[i], "-minsize", length) == 0) {
799     if (argc == 5) {
800     char buf[TCL_INTEGER_SPACE];
801     int value;
802    
803     value = (ok == TCL_OK) ? slotPtr[slot].minSize : 0;
804     sprintf(buf, "%d", value);
805     Tcl_SetResult(interp, buf, TCL_VOLATILE);
806     } else if (Tk_GetPixels(interp, master, argv[i+1], &size)
807     != TCL_OK) {
808     Tcl_Free((char *)argvPtr);
809     return TCL_ERROR;
810     } else {
811     slotPtr[slot].minSize = size;
812     }
813     }
814     else if (strncmp(argv[i], "-weight", length) == 0) {
815     int wt;
816     if (argc == 5) {
817     char buf[TCL_INTEGER_SPACE];
818     int value;
819    
820     value = (ok == TCL_OK) ? slotPtr[slot].weight : 0;
821     sprintf(buf, "%d", value);
822     Tcl_SetResult(interp, buf, TCL_VOLATILE);
823     } else if (Tcl_GetInt(interp, argv[i+1], &wt) != TCL_OK) {
824     Tcl_Free((char *)argvPtr);
825     return TCL_ERROR;
826     } else if (wt < 0) {
827     Tcl_AppendResult(interp, "invalid arg \"", argv[i],
828     "\": should be non-negative", (char *) NULL);
829     Tcl_Free((char *)argvPtr);
830     return TCL_ERROR;
831     } else {
832     slotPtr[slot].weight = wt;
833     }
834     }
835     else if (strncmp(argv[i], "-pad", length) == 0) {
836     if (argc == 5) {
837     char buf[TCL_INTEGER_SPACE];
838     int value;
839    
840     value = (ok == TCL_OK) ? slotPtr[slot].pad : 0;
841     sprintf(buf, "%d", value);
842     Tcl_SetResult(interp, buf, TCL_VOLATILE);
843     } else if (Tk_GetPixels(interp, master, argv[i+1], &size)
844     != TCL_OK) {
845     Tcl_Free((char *)argvPtr);
846     return TCL_ERROR;
847     } else if (size < 0) {
848     Tcl_AppendResult(interp, "invalid arg \"", argv[i],
849     "\": should be non-negative", (char *) NULL);
850     Tcl_Free((char *)argvPtr);
851     return TCL_ERROR;
852     } else {
853     slotPtr[slot].pad = size;
854     }
855     } else {
856     Tcl_AppendResult(interp, "invalid arg \"",
857     argv[i], "\": expecting -minsize, -pad, or -weight.",
858     (char *) NULL);
859     Tcl_Free((char *)argvPtr);
860     return TCL_ERROR;
861     }
862     }
863     }
864     Tcl_Free((char *)argvPtr);
865    
866     /*
867     * If we changed a property, re-arrange the table,
868     * and check for constraint shrinkage.
869     */
870    
871     if (argc != 5) {
872     if (slotType == ROW) {
873     int last = masterPtr->masterDataPtr->rowMax - 1;
874     while ((last >= 0) && (slotPtr[last].weight == 0)
875     && (slotPtr[last].pad == 0)
876     && (slotPtr[last].minSize == 0)) {
877     last--;
878     }
879     masterPtr->masterDataPtr->rowMax = last+1;
880     } else {
881     int last = masterPtr->masterDataPtr->columnMax - 1;
882     while ((last >= 0) && (slotPtr[last].weight == 0)
883     && (slotPtr[last].pad == 0)
884     && (slotPtr[last].minSize == 0)) {
885     last--;
886     }
887     masterPtr->masterDataPtr->columnMax = last + 1;
888     }
889    
890     if (masterPtr->abortPtr != NULL) {
891     *masterPtr->abortPtr = 1;
892     }
893     if (!(masterPtr->flags & REQUESTED_RELAYOUT)) {
894     masterPtr->flags |= REQUESTED_RELAYOUT;
895     Tcl_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
896     }
897     }
898     } else {
899     Tcl_AppendResult(interp, "bad option \"", argv[1],
900     "\": must be bbox, columnconfigure, configure, forget, info, ",
901     "location, propagate, remove, rowconfigure, size, or slaves.",
902     (char *) NULL);
903     return TCL_ERROR;
904     }
905     return TCL_OK;
906     }
907    
908     /*
909     *--------------------------------------------------------------
910     *
911     * GridReqProc --
912     *
913     * This procedure is invoked by Tk_GeometryRequest for
914     * windows managed by the grid.
915     *
916     * Results:
917     * None.
918     *
919     * Side effects:
920     * Arranges for tkwin, and all its managed siblings, to
921     * be re-arranged at the next idle point.
922     *
923     *--------------------------------------------------------------
924     */
925    
926     static void
927     GridReqProc(clientData, tkwin)
928     ClientData clientData; /* Grid's information about
929     * window that got new preferred
930     * geometry. */
931     Tk_Window tkwin; /* Other Tk-related information
932     * about the window. */
933     {
934     register Gridder *gridPtr = (Gridder *) clientData;
935    
936     gridPtr = gridPtr->masterPtr;
937     if (!(gridPtr->flags & REQUESTED_RELAYOUT)) {
938     gridPtr->flags |= REQUESTED_RELAYOUT;
939     Tcl_DoWhenIdle(ArrangeGrid, (ClientData) gridPtr);
940     }
941     }
942    
943     /*
944     *--------------------------------------------------------------
945     *
946     * GridLostSlaveProc --
947     *
948     * This procedure is invoked by Tk whenever some other geometry
949     * claims control over a slave that used to be managed by us.
950     *
951     * Results:
952     * None.
953     *
954     * Side effects:
955     * Forgets all grid-related information about the slave.
956     *
957     *--------------------------------------------------------------
958     */
959    
960     static void
961     GridLostSlaveProc(clientData, tkwin)
962     ClientData clientData; /* Grid structure for slave window that
963     * was stolen away. */
964     Tk_Window tkwin; /* Tk's handle for the slave window. */
965     {
966     register Gridder *slavePtr = (Gridder *) clientData;
967    
968     if (slavePtr->masterPtr->tkwin != Tk_Parent(slavePtr->tkwin)) {
969     Tk_UnmaintainGeometry(slavePtr->tkwin, slavePtr->masterPtr->tkwin);
970     }
971     Unlink(slavePtr);
972     Tk_UnmapWindow(slavePtr->tkwin);
973     }
974    
975     /*
976     *--------------------------------------------------------------
977     *
978     * AdjustOffsets --
979     *
980     * This procedure adjusts the size of the layout to fit in the
981     * space provided. If it needs more space, the extra is added
982     * according to the weights. If it needs less, the space is removed
983     * according to the weights, but at no time does the size drop below
984     * the minsize specified for that slot.
985     *
986     * Results:
987     * The initial offset of the layout,
988     * if all the weights are zero, else 0.
989     *
990     * Side effects:
991     * The slot offsets are modified to shrink the layout.
992     *
993     *--------------------------------------------------------------
994     */
995    
996     static int
997     AdjustOffsets(size, slots, slotPtr)
998     int size; /* The total layout size (in pixels). */
999     int slots; /* Number of slots. */
1000     register SlotInfo *slotPtr; /* Pointer to slot array. */
1001     {
1002     register int slot; /* Current slot. */
1003     int diff; /* Extra pixels needed to add to the layout. */
1004     int totalWeight = 0; /* Sum of the weights for all the slots. */
1005     int weight = 0; /* Sum of the weights so far. */
1006     int minSize = 0; /* Minimum possible layout size. */
1007     int newDiff; /* The most pixels that can be added on
1008     * the current pass. */
1009    
1010     diff = size - slotPtr[slots-1].offset;
1011    
1012     /*
1013     * The layout is already the correct size; all done.
1014     */
1015    
1016     if (diff == 0) {
1017     return(0);
1018     }
1019    
1020     /*
1021     * If all the weights are zero, center the layout in its parent if
1022     * there is extra space, else clip on the bottom/right.
1023     */
1024    
1025     for (slot=0; slot < slots; slot++) {
1026     totalWeight += slotPtr[slot].weight;
1027     }
1028    
1029     if (totalWeight == 0 ) {
1030     return(diff > 0 ? diff/2 : 0);
1031     }
1032    
1033     /*
1034     * Add extra space according to the slot weights. This is done
1035     * cumulatively to prevent round-off error accumulation.
1036     */
1037    
1038     if (diff > 0) {
1039     for (weight=slot=0; slot < slots; slot++) {
1040     weight += slotPtr[slot].weight;
1041     slotPtr[slot].offset += diff * weight / totalWeight;
1042     }
1043     return(0);
1044     }
1045    
1046     /*
1047     * The layout must shrink below its requested size. Compute the
1048     * minimum possible size by looking at the slot minSizes.
1049     */
1050    
1051     for (slot=0; slot < slots; slot++) {
1052     if (slotPtr[slot].weight > 0) {
1053     minSize += slotPtr[slot].minSize;
1054     } else if (slot > 0) {
1055     minSize += slotPtr[slot].offset - slotPtr[slot-1].offset;
1056     } else {
1057     minSize += slotPtr[slot].offset;
1058     }
1059     }
1060    
1061     /*
1062     * If the requested size is less than the minimum required size,
1063     * set the slot sizes to their minimum values, then clip on the
1064     * bottom/right.
1065     */
1066    
1067     if (size <= minSize) {
1068     int offset = 0;
1069     for (slot=0; slot < slots; slot++) {
1070     if (slotPtr[slot].weight > 0) {
1071     offset += slotPtr[slot].minSize;
1072     } else if (slot > 0) {
1073     offset += slotPtr[slot].offset - slotPtr[slot-1].offset;
1074     } else {
1075     offset += slotPtr[slot].offset;
1076     }
1077     slotPtr[slot].offset = offset;
1078     }
1079     return(0);
1080     }
1081    
1082     /*
1083     * Remove space from slots according to their weights. The weights
1084     * get renormalized anytime a slot shrinks to its minimum size.
1085     */
1086    
1087     while (diff < 0) {
1088    
1089     /*
1090     * Find the total weight for the shrinkable slots.
1091     */
1092    
1093     for (totalWeight=slot=0; slot < slots; slot++) {
1094     int current = (slot == 0) ? slotPtr[slot].offset :
1095     slotPtr[slot].offset - slotPtr[slot-1].offset;
1096     if (current > slotPtr[slot].minSize) {
1097     totalWeight += slotPtr[slot].weight;
1098     slotPtr[slot].temp = slotPtr[slot].weight;
1099     } else {
1100     slotPtr[slot].temp = 0;
1101     }
1102     }
1103     if (totalWeight == 0) {
1104     break;
1105     }
1106    
1107     /*
1108     * Find the maximum amount of space we can distribute this pass.
1109     */
1110    
1111     newDiff = diff;
1112     for (slot = 0; slot < slots; slot++) {
1113     int current; /* current size of this slot */
1114     int maxDiff; /* max diff that would cause
1115     * this slot to equal its minsize */
1116     if (slotPtr[slot].temp == 0) {
1117     continue;
1118     }
1119     current = (slot == 0) ? slotPtr[slot].offset :
1120     slotPtr[slot].offset - slotPtr[slot-1].offset;
1121     maxDiff = totalWeight * (slotPtr[slot].minSize - current)
1122     / slotPtr[slot].temp;
1123     if (maxDiff > newDiff) {
1124     newDiff = maxDiff;
1125     }
1126     }
1127    
1128     /*
1129     * Now distribute the space.
1130     */
1131    
1132     for (weight=slot=0; slot < slots; slot++) {
1133     weight += slotPtr[slot].temp;
1134     slotPtr[slot].offset += newDiff * weight / totalWeight;
1135     }
1136     diff -= newDiff;
1137     }
1138     return(0);
1139     }
1140    
1141     /*
1142     *--------------------------------------------------------------
1143     *
1144     * AdjustForSticky --
1145     *
1146     * This procedure adjusts the size of a slave in its cavity based
1147     * on its "sticky" flags.
1148     *
1149     * Results:
1150     * The input x, y, width, and height are changed to represent the
1151     * desired coordinates of the slave.
1152     *
1153     * Side effects:
1154     * None.
1155     *
1156     *--------------------------------------------------------------
1157     */
1158    
1159     static void
1160     AdjustForSticky(slavePtr, xPtr, yPtr, widthPtr, heightPtr)
1161     Gridder *slavePtr; /* Slave window to arrange in its cavity. */
1162     int *xPtr; /* Pixel location of the left edge of the cavity. */
1163     int *yPtr; /* Pixel location of the top edge of the cavity. */
1164     int *widthPtr; /* Width of the cavity (in pixels). */
1165     int *heightPtr; /* Height of the cavity (in pixels). */
1166     {
1167     int diffx=0; /* Cavity width - slave width. */
1168     int diffy=0; /* Cavity hight - slave height. */
1169     int sticky = slavePtr->sticky;
1170    
1171     *xPtr += slavePtr->padX/2;
1172     *widthPtr -= slavePtr->padX;
1173     *yPtr += slavePtr->padY/2;
1174     *heightPtr -= slavePtr->padY;
1175    
1176     if (*widthPtr > (Tk_ReqWidth(slavePtr->tkwin) + slavePtr->iPadX)) {
1177     diffx = *widthPtr - (Tk_ReqWidth(slavePtr->tkwin) + slavePtr->iPadX);
1178     *widthPtr = Tk_ReqWidth(slavePtr->tkwin) + slavePtr->iPadX;
1179     }
1180    
1181     if (*heightPtr > (Tk_ReqHeight(slavePtr->tkwin) + slavePtr->iPadY)) {
1182     diffy = *heightPtr - (Tk_ReqHeight(slavePtr->tkwin) + slavePtr->iPadY);
1183     *heightPtr = Tk_ReqHeight(slavePtr->tkwin) + slavePtr->iPadY;
1184     }
1185    
1186     if (sticky&STICK_EAST && sticky&STICK_WEST) {
1187     *widthPtr += diffx;
1188     }
1189     if (sticky&STICK_NORTH && sticky&STICK_SOUTH) {
1190     *heightPtr += diffy;
1191     }
1192     if (!(sticky&STICK_WEST)) {
1193     *xPtr += (sticky&STICK_EAST) ? diffx : diffx/2;
1194     }
1195     if (!(sticky&STICK_NORTH)) {
1196     *yPtr += (sticky&STICK_SOUTH) ? diffy : diffy/2;
1197     }
1198     }
1199    
1200     /*
1201     *--------------------------------------------------------------
1202     *
1203     * ArrangeGrid --
1204     *
1205     * This procedure is invoked (using the Tcl_DoWhenIdle
1206     * mechanism) to re-layout a set of windows managed by
1207     * the grid. It is invoked at idle time so that a
1208     * series of grid requests can be merged into a single
1209     * layout operation.
1210     *
1211     * Results:
1212     * None.
1213     *
1214     * Side effects:
1215     * The slaves of masterPtr may get resized or moved.
1216     *
1217     *--------------------------------------------------------------
1218     */
1219    
1220     static void
1221     ArrangeGrid(clientData)
1222     ClientData clientData; /* Structure describing parent whose slaves
1223     * are to be re-layed out. */
1224     {
1225     register Gridder *masterPtr = (Gridder *) clientData;
1226     register Gridder *slavePtr;
1227     GridMaster *slotPtr = masterPtr->masterDataPtr;
1228     int abort;
1229     int width, height; /* requested size of layout, in pixels */
1230     int realWidth, realHeight; /* actual size layout should take-up */
1231    
1232     masterPtr->flags &= ~REQUESTED_RELAYOUT;
1233    
1234     /*
1235     * If the parent has no slaves anymore, then don't do anything
1236     * at all: just leave the parent's size as-is. Otherwise there is
1237     * no way to "relinquish" control over the parent so another geometry
1238     * manager can take over.
1239     */
1240    
1241     if (masterPtr->slavePtr == NULL) {
1242     return;
1243     }
1244    
1245     if (masterPtr->masterDataPtr == NULL) {
1246     return;
1247     }
1248    
1249     /*
1250     * Abort any nested call to ArrangeGrid for this window, since
1251     * we'll do everything necessary here, and set up so this call
1252     * can be aborted if necessary.
1253     */
1254    
1255     if (masterPtr->abortPtr != NULL) {
1256     *masterPtr->abortPtr = 1;
1257     }
1258     masterPtr->abortPtr = &abort;
1259     abort = 0;
1260     Tcl_Preserve((ClientData) masterPtr);
1261    
1262     /*
1263     * Call the constraint engine to fill in the row and column offsets.
1264     */
1265    
1266     SetGridSize(masterPtr);
1267     width = ResolveConstraints(masterPtr, COLUMN, 0);
1268     height = ResolveConstraints(masterPtr, ROW, 0);
1269     width += 2*Tk_InternalBorderWidth(masterPtr->tkwin);
1270     height += 2*Tk_InternalBorderWidth(masterPtr->tkwin);
1271    
1272     if (((width != Tk_ReqWidth(masterPtr->tkwin))
1273     || (height != Tk_ReqHeight(masterPtr->tkwin)))
1274     && !(masterPtr->flags & DONT_PROPAGATE)) {
1275     Tk_GeometryRequest(masterPtr->tkwin, width, height);
1276     if (width>1 && height>1) {
1277     masterPtr->flags |= REQUESTED_RELAYOUT;
1278     Tcl_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
1279     }
1280     masterPtr->abortPtr = NULL;
1281     Tcl_Release((ClientData) masterPtr);
1282     return;
1283     }
1284    
1285     /*
1286     * If the currently requested layout size doesn't match the parent's
1287     * window size, then adjust the slot offsets according to the
1288     * weights. If all of the weights are zero, center the layout in
1289     * its parent. I haven't decided what to do if the parent is smaller
1290     * than the requested size.
1291     */
1292    
1293     realWidth = Tk_Width(masterPtr->tkwin) -
1294     2*Tk_InternalBorderWidth(masterPtr->tkwin);
1295     realHeight = Tk_Height(masterPtr->tkwin) -
1296     2*Tk_InternalBorderWidth(masterPtr->tkwin);
1297     slotPtr->startX = AdjustOffsets(realWidth,
1298     MAX(slotPtr->columnEnd,slotPtr->columnMax), slotPtr->columnPtr);
1299     slotPtr->startY = AdjustOffsets(realHeight,
1300     MAX(slotPtr->rowEnd,slotPtr->rowMax), slotPtr->rowPtr);
1301     slotPtr->startX += Tk_InternalBorderWidth(masterPtr->tkwin);
1302     slotPtr->startY += Tk_InternalBorderWidth(masterPtr->tkwin);
1303    
1304     /*
1305     * Now adjust the actual size of the slave to its cavity by
1306     * computing the cavity size, and adjusting the widget according
1307     * to its stickyness.
1308     */
1309    
1310     for (slavePtr = masterPtr->slavePtr; slavePtr != NULL && !abort;
1311     slavePtr = slavePtr->nextPtr) {
1312     int x, y; /* top left coordinate */
1313     int width, height; /* slot or slave size */
1314     int col = slavePtr->column;
1315     int row = slavePtr->row;
1316    
1317     x = (col>0) ? slotPtr->columnPtr[col-1].offset : 0;
1318     y = (row>0) ? slotPtr->rowPtr[row-1].offset : 0;
1319    
1320     width = slotPtr->columnPtr[slavePtr->numCols+col-1].offset - x;
1321     height = slotPtr->rowPtr[slavePtr->numRows+row-1].offset - y;
1322    
1323     x += slotPtr->startX;
1324     y += slotPtr->startY;
1325    
1326     AdjustForSticky(slavePtr, &x, &y, &width, &height);
1327    
1328     /*
1329     * Now put the window in the proper spot. (This was taken directly
1330     * from tkPack.c.) If the slave is a child of the master, then
1331     * do this here. Otherwise let Tk_MaintainGeometry do the work.
1332     */
1333    
1334     if (masterPtr->tkwin == Tk_Parent(slavePtr->tkwin)) {
1335     if ((width <= 0) || (height <= 0)) {
1336     Tk_UnmapWindow(slavePtr->tkwin);
1337     } else {
1338     if ((x != Tk_X(slavePtr->tkwin))
1339     || (y != Tk_Y(slavePtr->tkwin))
1340     || (width != Tk_Width(slavePtr->tkwin))
1341     || (height != Tk_Height(slavePtr->tkwin))) {
1342     Tk_MoveResizeWindow(slavePtr->tkwin, x, y, width, height);
1343     }
1344     if (abort) {
1345     break;
1346     }
1347    
1348     /*
1349     * Don't map the slave if the master isn't mapped: wait
1350     * until the master gets mapped later.
1351     */
1352    
1353     if (Tk_IsMapped(masterPtr->tkwin)) {
1354     Tk_MapWindow(slavePtr->tkwin);
1355     }
1356     }
1357     } else {
1358     if ((width <= 0) || (height <= 0)) {
1359     Tk_UnmaintainGeometry(slavePtr->tkwin, masterPtr->tkwin);
1360     Tk_UnmapWindow(slavePtr->tkwin);
1361     } else {
1362     Tk_MaintainGeometry(slavePtr->tkwin, masterPtr->tkwin,
1363     x, y, width, height);
1364     }
1365     }
1366     }
1367    
1368     masterPtr->abortPtr = NULL;
1369     Tcl_Release((ClientData) masterPtr);
1370     }
1371    
1372     /*
1373     *--------------------------------------------------------------
1374     *
1375     * ResolveConstraints --
1376     *
1377     * Resolve all of the column and row boundaries. Most of
1378     * the calculations are identical for rows and columns, so this procedure
1379     * is called twice, once for rows, and again for columns.
1380     *
1381     * Results:
1382     * The offset (in pixels) from the left/top edge of this layout is
1383     * returned.
1384     *
1385     * Side effects:
1386     * The slot offsets are copied into the SlotInfo structure for the
1387     * geometry master.
1388     *
1389     *--------------------------------------------------------------
1390     */
1391    
1392     static int
1393     ResolveConstraints(masterPtr, slotType, maxOffset)
1394     Gridder *masterPtr; /* The geometry master for this grid. */
1395     int slotType; /* Either ROW or COLUMN. */
1396     int maxOffset; /* The actual maximum size of this layout
1397     * in pixels, or 0 (not currently used). */
1398     {
1399     register SlotInfo *slotPtr; /* Pointer to row/col constraints. */
1400     register Gridder *slavePtr; /* List of slave windows in this grid. */
1401     int constraintCount; /* Count of rows or columns that have
1402     * constraints. */
1403     int slotCount; /* Last occupied row or column. */
1404     int gridCount; /* The larger of slotCount and constraintCount.
1405     */
1406     GridLayout *layoutPtr; /* Temporary layout structure. */
1407     int requiredSize; /* The natural size of the grid (pixels).
1408     * This is the minimum size needed to
1409     * accomodate all of the slaves at their
1410     * requested sizes. */
1411     int offset; /* The pixel offset of the right edge of the
1412     * current slot from the beginning of the
1413     * layout. */
1414     int slot; /* The current slot. */
1415     int start; /* The first slot of a contiguous set whose
1416     * constraints are not yet fully resolved. */
1417     int end; /* The Last slot of a contiguous set whose
1418     * constraints are not yet fully resolved. */
1419    
1420     /*
1421     * For typical sized tables, we'll use stack space for the layout data
1422     * to avoid the overhead of a malloc and free for every layout.
1423     */
1424    
1425     GridLayout layoutData[TYPICAL_SIZE + 1];
1426    
1427     if (slotType == COLUMN) {
1428     constraintCount = masterPtr->masterDataPtr->columnMax;
1429     slotCount = masterPtr->masterDataPtr->columnEnd;
1430     slotPtr = masterPtr->masterDataPtr->columnPtr;
1431     } else {
1432     constraintCount = masterPtr->masterDataPtr->rowMax;
1433     slotCount = masterPtr->masterDataPtr->rowEnd;
1434     slotPtr = masterPtr->masterDataPtr->rowPtr;
1435     }
1436    
1437     /*
1438     * Make sure there is enough memory for the layout.
1439     */
1440    
1441     gridCount = MAX(constraintCount,slotCount);
1442     if (gridCount >= TYPICAL_SIZE) {
1443     layoutPtr = (GridLayout *) ckalloc(sizeof(GridLayout) * (1+gridCount));
1444     } else {
1445     layoutPtr = layoutData;
1446     }
1447    
1448     /*
1449     * Allocate an extra layout slot to represent the left/top edge of
1450     * the 0th slot to make it easier to calculate slot widths from
1451     * offsets without special case code.
1452     * Initialize the "dummy" slot to the left/top of the table.
1453     * This slot avoids special casing the first slot.
1454     */
1455    
1456     layoutPtr->minOffset = 0;
1457     layoutPtr->maxOffset = 0;
1458     layoutPtr++;
1459    
1460     /*
1461     * Step 1.
1462     * Copy the slot constraints into the layout structure,
1463     * and initialize the rest of the fields.
1464     */
1465    
1466     for (slot=0; slot < constraintCount; slot++) {
1467     layoutPtr[slot].minSize = slotPtr[slot].minSize;
1468     layoutPtr[slot].weight = slotPtr[slot].weight;
1469     layoutPtr[slot].pad = slotPtr[slot].pad;
1470     layoutPtr[slot].binNextPtr = NULL;
1471     }
1472     for(;slot<gridCount;slot++) {
1473     layoutPtr[slot].minSize = 0;
1474     layoutPtr[slot].weight = 0;
1475     layoutPtr[slot].pad = 0;
1476     layoutPtr[slot].binNextPtr = NULL;
1477     }
1478    
1479     /*
1480     * Step 2.
1481     * Slaves with a span of 1 are used to determine the minimum size of
1482     * each slot. Slaves whose span is two or more slots don't
1483     * contribute to the minimum size of each slot directly, but can cause
1484     * slots to grow if their size exceeds the the sizes of the slots they
1485     * span.
1486     *
1487     * Bin all slaves whose spans are > 1 by their right edges. This
1488     * allows the computation on minimum and maximum possible layout
1489     * sizes at each slot boundary, without the need to re-sort the slaves.
1490     */
1491    
1492     switch (slotType) {
1493     case COLUMN:
1494     for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
1495     slavePtr = slavePtr->nextPtr) {
1496     int rightEdge = slavePtr->column + slavePtr->numCols - 1;
1497     slavePtr->size = Tk_ReqWidth(slavePtr->tkwin) +
1498     slavePtr->padX + slavePtr->iPadX + slavePtr->doubleBw;
1499     if (slavePtr->numCols > 1) {
1500     slavePtr->binNextPtr = layoutPtr[rightEdge].binNextPtr;
1501     layoutPtr[rightEdge].binNextPtr = slavePtr;
1502     } else {
1503     int size = slavePtr->size + layoutPtr[rightEdge].pad;
1504     if (size > layoutPtr[rightEdge].minSize) {
1505     layoutPtr[rightEdge].minSize = size;
1506     }
1507     }
1508     }
1509     break;
1510     case ROW:
1511     for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
1512     slavePtr = slavePtr->nextPtr) {
1513     int rightEdge = slavePtr->row + slavePtr->numRows - 1;
1514     slavePtr->size = Tk_ReqHeight(slavePtr->tkwin) +
1515     slavePtr->padY + slavePtr->iPadY + slavePtr->doubleBw;
1516     if (slavePtr->numRows > 1) {
1517     slavePtr->binNextPtr = layoutPtr[rightEdge].binNextPtr;
1518     layoutPtr[rightEdge].binNextPtr = slavePtr;
1519     } else {
1520     int size = slavePtr->size + layoutPtr[rightEdge].pad;
1521     if (size > layoutPtr[rightEdge].minSize) {
1522     layoutPtr[rightEdge].minSize = size;
1523     }
1524     }
1525     }
1526     break;
1527     }
1528    
1529     /*
1530     * Step 3.
1531     * Determine the minimum slot offsets going from left to right
1532     * that would fit all of the slaves. This determines the minimum
1533     */
1534    
1535     for (offset=slot=0; slot < gridCount; slot++) {
1536     layoutPtr[slot].minOffset = layoutPtr[slot].minSize + offset;
1537     for (slavePtr = layoutPtr[slot].binNextPtr; slavePtr != NULL;
1538     slavePtr = slavePtr->binNextPtr) {
1539     int span = (slotType == COLUMN) ? slavePtr->numCols : slavePtr->numRows;
1540     int required = slavePtr->size + layoutPtr[slot - span].minOffset;
1541     if (required > layoutPtr[slot].minOffset) {
1542     layoutPtr[slot].minOffset = required;
1543     }
1544     }
1545     offset = layoutPtr[slot].minOffset;
1546     }
1547    
1548     /*
1549     * At this point, we know the minimum required size of the entire layout.
1550     * It might be prudent to stop here if our "master" will resize itself
1551     * to this size.
1552     */
1553    
1554     requiredSize = offset;
1555     if (maxOffset > offset) {
1556     offset=maxOffset;
1557     }
1558    
1559     /*
1560     * Step 4.
1561     * Determine the minimum slot offsets going from right to left,
1562     * bounding the pixel range of each slot boundary.
1563     * Pre-fill all of the right offsets with the actual size of the table;
1564     * they will be reduced as required.
1565     */
1566    
1567     for (slot=0; slot < gridCount; slot++) {
1568     layoutPtr[slot].maxOffset = offset;
1569     }
1570     for (slot=gridCount-1; slot > 0;) {
1571     for (slavePtr = layoutPtr[slot].binNextPtr; slavePtr != NULL;
1572     slavePtr = slavePtr->binNextPtr) {
1573     int span = (slotType == COLUMN) ? slavePtr->numCols : slavePtr->numRows;
1574     int require = offset - slavePtr->size;
1575     int startSlot = slot - span;
1576     if (startSlot >=0 && require < layoutPtr[startSlot].maxOffset) {
1577     layoutPtr[startSlot].maxOffset = require;
1578     }
1579     }
1580     offset -= layoutPtr[slot].minSize;
1581     slot--;
1582     if (layoutPtr[slot].maxOffset < offset) {
1583     offset = layoutPtr[slot].maxOffset;
1584     } else {
1585     layoutPtr[slot].maxOffset = offset;
1586     }
1587     }
1588    
1589     /*
1590     * Step 5.
1591     * At this point, each slot boundary has a range of values that
1592     * will satisfy the overall layout size.
1593     * Make repeated passes over the layout structure looking for
1594     * spans of slot boundaries where the minOffsets are less than
1595     * the maxOffsets, and adjust the offsets according to the slot
1596     * weights. At each pass, at least one slot boundary will have
1597     * its range of possible values fixed at a single value.
1598     */
1599    
1600     for (start=0; start < gridCount;) {
1601     int totalWeight = 0; /* Sum of the weights for all of the
1602     * slots in this span. */
1603     int need = 0; /* The minimum space needed to layout
1604     * this span. */
1605     int have; /* The actual amount of space that will
1606     * be taken up by this span. */
1607     int weight; /* Cumulative weights of the columns in
1608     * this span. */
1609     int noWeights = 0; /* True if the span has no weights. */
1610    
1611     /*
1612     * Find a span by identifying ranges of slots whose edges are
1613     * already constrained at fixed offsets, but whose internal
1614     * slot boundaries have a range of possible positions.
1615     */
1616    
1617     if (layoutPtr[start].minOffset == layoutPtr[start].maxOffset) {
1618     start++;
1619     continue;
1620     }
1621    
1622     for (end=start+1; end<gridCount; end++) {
1623     if (layoutPtr[end].minOffset == layoutPtr[end].maxOffset) {
1624     break;
1625     }
1626     }
1627    
1628     /*
1629     * We found a span. Compute the total weight, minumum space required,
1630     * for this span, and the actual amount of space the span should
1631     * use.
1632     */
1633    
1634     for (slot=start; slot<=end; slot++) {
1635     totalWeight += layoutPtr[slot].weight;
1636     need += layoutPtr[slot].minSize;
1637     }
1638     have = layoutPtr[end].maxOffset - layoutPtr[start-1].minOffset;
1639    
1640     /*
1641     * If all the weights in the span are zero, then distribute the
1642     * extra space evenly.
1643     */
1644    
1645     if (totalWeight == 0) {
1646     noWeights++;
1647     totalWeight = end - start + 1;
1648     }
1649    
1650     /*
1651     * It might not be possible to give the span all of the space
1652     * available on this pass without violating the size constraints
1653     * of one or more of the internal slot boundaries.
1654     * Determine the maximum amount of space that when added to the
1655     * entire span, would cause a slot boundary to have its possible
1656     * range reduced to one value, and reduce the amount of extra
1657     * space allocated on this pass accordingly.
1658     *
1659     * The calculation is done cumulatively to avoid accumulating
1660     * roundoff errors.
1661     */
1662    
1663     for (weight=0,slot=start; slot<end; slot++) {
1664     int diff = layoutPtr[slot].maxOffset - layoutPtr[slot].minOffset;
1665     weight += noWeights ? 1 : layoutPtr[slot].weight;
1666     if ((noWeights || layoutPtr[slot].weight>0) &&
1667     (diff*totalWeight/weight) < (have-need)) {
1668     have = diff * totalWeight / weight + need;
1669     }
1670     }
1671    
1672     /*
1673     * Now distribute the extra space among the slots by
1674     * adjusting the minSizes and minOffsets.
1675     */
1676    
1677     for (weight=0,slot=start; slot<end; slot++) {
1678     weight += noWeights ? 1 : layoutPtr[slot].weight;
1679     layoutPtr[slot].minOffset +=
1680     (int)((double) (have-need) * weight/totalWeight + 0.5);
1681     layoutPtr[slot].minSize = layoutPtr[slot].minOffset
1682     - layoutPtr[slot-1].minOffset;
1683     }
1684     layoutPtr[slot].minSize = layoutPtr[slot].minOffset
1685     - layoutPtr[slot-1].minOffset;
1686    
1687     /*
1688     * Having pushed the top/left boundaries of the slots to
1689     * take up extra space, the bottom/right space is recalculated
1690     * to propagate the new space allocation.
1691     */
1692    
1693     for (slot=end; slot > start; slot--) {
1694     layoutPtr[slot-1].maxOffset =
1695     layoutPtr[slot].maxOffset-layoutPtr[slot].minSize;
1696     }
1697     }
1698    
1699    
1700     /*
1701     * Step 6.
1702     * All of the space has been apportioned; copy the
1703     * layout information back into the master.
1704     */
1705    
1706     for (slot=0; slot < gridCount; slot++) {
1707     slotPtr[slot].offset = layoutPtr[slot].minOffset;
1708     }
1709    
1710     --layoutPtr;
1711     if (layoutPtr != layoutData) {
1712     Tcl_Free((char *)layoutPtr);
1713     }
1714     return requiredSize;
1715     }
1716    
1717     /*
1718     *--------------------------------------------------------------
1719     *
1720     * GetGrid --
1721     *
1722     * This internal procedure is used to locate a Grid
1723     * structure for a given window, creating one if one
1724     * doesn't exist already.
1725     *
1726     * Results:
1727     * The return value is a pointer to the Grid structure
1728     * corresponding to tkwin.
1729     *
1730     * Side effects:
1731     * A new grid structure may be created. If so, then
1732     * a callback is set up to clean things up when the
1733     * window is deleted.
1734     *
1735     *--------------------------------------------------------------
1736     */
1737    
1738     static Gridder *
1739     GetGrid(tkwin)
1740     Tk_Window tkwin; /* Token for window for which
1741     * grid structure is desired. */
1742     {
1743     register Gridder *gridPtr;
1744     Tcl_HashEntry *hPtr;
1745     int new;
1746     TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr;
1747    
1748     if (!dispPtr->gridInit) {
1749     Tcl_InitHashTable(&dispPtr->gridHashTable, TCL_ONE_WORD_KEYS);
1750     dispPtr->gridInit = 1;
1751     }
1752    
1753     /*
1754     * See if there's already grid for this window. If not,
1755     * then create a new one.
1756     */
1757    
1758     hPtr = Tcl_CreateHashEntry(&dispPtr->gridHashTable, (char *) tkwin, &new);
1759     if (!new) {
1760     return (Gridder *) Tcl_GetHashValue(hPtr);
1761     }
1762     gridPtr = (Gridder *) ckalloc(sizeof(Gridder));
1763     gridPtr->tkwin = tkwin;
1764     gridPtr->masterPtr = NULL;
1765     gridPtr->masterDataPtr = NULL;
1766     gridPtr->nextPtr = NULL;
1767     gridPtr->slavePtr = NULL;
1768     gridPtr->binNextPtr = NULL;
1769    
1770     gridPtr->column = gridPtr->row = -1;
1771     gridPtr->numCols = 1;
1772     gridPtr->numRows = 1;
1773    
1774     gridPtr->padX = gridPtr->padY = 0;
1775     gridPtr->iPadX = gridPtr->iPadY = 0;
1776     gridPtr->doubleBw = 2*Tk_Changes(tkwin)->border_width;
1777     gridPtr->abortPtr = NULL;
1778     gridPtr->flags = 0;
1779     gridPtr->sticky = 0;
1780     gridPtr->size = 0;
1781     gridPtr->masterDataPtr = NULL;
1782     Tcl_SetHashValue(hPtr, gridPtr);
1783     Tk_CreateEventHandler(tkwin, StructureNotifyMask,
1784     GridStructureProc, (ClientData) gridPtr);
1785     return gridPtr;
1786     }
1787    
1788     /*
1789     *--------------------------------------------------------------
1790     *
1791     * SetGridSize --
1792     *
1793     * This internal procedure sets the size of the grid occupied
1794     * by slaves.
1795     *
1796     * Results:
1797     * none
1798     *
1799     * Side effects:
1800     * The width and height arguments are filled in the master data structure.
1801     * Additional space is allocated for the constraints to accomodate
1802     * the offsets.
1803     *
1804     *--------------------------------------------------------------
1805     */
1806    
1807     static void
1808     SetGridSize(masterPtr)
1809     Gridder *masterPtr; /* The geometry master for this grid. */
1810     {
1811     register Gridder *slavePtr; /* Current slave window. */
1812     int maxX = 0, maxY = 0;
1813    
1814     for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
1815     slavePtr = slavePtr->nextPtr) {
1816     maxX = MAX(maxX,slavePtr->numCols + slavePtr->column);
1817     maxY = MAX(maxY,slavePtr->numRows + slavePtr->row);
1818     }
1819     masterPtr->masterDataPtr->columnEnd = maxX;
1820     masterPtr->masterDataPtr->rowEnd = maxY;
1821     CheckSlotData(masterPtr, maxX, COLUMN, CHECK_SPACE);
1822     CheckSlotData(masterPtr, maxY, ROW, CHECK_SPACE);
1823     }
1824    
1825     /*
1826     *--------------------------------------------------------------
1827     *
1828     * CheckSlotData --
1829     *
1830     * This internal procedure is used to manage the storage for
1831     * row and column (slot) constraints.
1832     *
1833     * Results:
1834     * TRUE if the index is OK, False otherwise.
1835     *
1836     * Side effects:
1837     * A new master grid structure may be created. If so, then
1838     * it is initialized. In addition, additional storage for
1839     * a row or column constraints may be allocated, and the constraint
1840     * maximums are adjusted.
1841     *
1842     *--------------------------------------------------------------
1843     */
1844    
1845     static int
1846     CheckSlotData(masterPtr, slot, slotType, checkOnly)
1847     Gridder *masterPtr; /* the geometry master for this grid */
1848     int slot; /* which slot to look at */
1849     int slotType; /* ROW or COLUMN */
1850     int checkOnly; /* don't allocate new space if true */
1851     {
1852     int numSlot; /* number of slots already allocated (Space) */
1853     int end; /* last used constraint */
1854    
1855     /*
1856     * If slot is out of bounds, return immediately.
1857     */
1858    
1859     if (slot < 0 || slot >= MAX_ELEMENT) {
1860     return TCL_ERROR;
1861     }
1862    
1863     if ((checkOnly == CHECK_ONLY) && (masterPtr->masterDataPtr == NULL)) {
1864     return TCL_ERROR;
1865     }
1866    
1867     /*
1868     * If we need to allocate more space, allocate a little extra to avoid
1869     * repeated re-alloc's for large tables. We need enough space to
1870     * hold all of the offsets as well.
1871     */
1872    
1873     InitMasterData(masterPtr);
1874     end = (slotType == ROW) ? masterPtr->masterDataPtr->rowMax :
1875     masterPtr->masterDataPtr->columnMax;
1876     if (checkOnly == CHECK_ONLY) {
1877     return (end < slot) ? TCL_ERROR : TCL_OK;
1878     } else {
1879     numSlot = (slotType == ROW) ? masterPtr->masterDataPtr->rowSpace
1880     : masterPtr->masterDataPtr->columnSpace;
1881     if (slot >= numSlot) {
1882     int newNumSlot = slot + PREALLOC ;
1883     size_t oldSize = numSlot * sizeof(SlotInfo) ;
1884     size_t newSize = newNumSlot * sizeof(SlotInfo) ;
1885     SlotInfo *new = (SlotInfo *) ckalloc(newSize);
1886     SlotInfo *old = (slotType == ROW) ?
1887     masterPtr->masterDataPtr->rowPtr :
1888     masterPtr->masterDataPtr->columnPtr;
1889     memcpy((VOID *) new, (VOID *) old, oldSize );
1890     memset((VOID *) (new+numSlot), 0, newSize - oldSize );
1891     Tcl_Free((char *) old);
1892     if (slotType == ROW) {
1893     masterPtr->masterDataPtr->rowPtr = new ;
1894     masterPtr->masterDataPtr->rowSpace = newNumSlot ;
1895     } else {
1896     masterPtr->masterDataPtr->columnPtr = new;
1897     masterPtr->masterDataPtr->columnSpace = newNumSlot ;
1898     }
1899     }
1900     if (slot >= end && checkOnly != CHECK_SPACE) {
1901     if (slotType == ROW) {
1902     masterPtr->masterDataPtr->rowMax = slot+1;
1903     } else {
1904     masterPtr->masterDataPtr->columnMax = slot+1;
1905     }
1906     }
1907     return TCL_OK;
1908     }
1909     }
1910    
1911     /*
1912     *--------------------------------------------------------------
1913     *
1914     * InitMasterData --
1915     *
1916     * This internal procedure is used to allocate and initialize
1917     * the data for a geometry master, if the data
1918     * doesn't exist already.
1919     *
1920     * Results:
1921     * none
1922     *
1923     * Side effects:
1924     * A new master grid structure may be created. If so, then
1925     * it is initialized.
1926     *
1927     *--------------------------------------------------------------
1928     */
1929    
1930     static void
1931     InitMasterData(masterPtr)
1932     Gridder *masterPtr;
1933     {
1934     size_t size;
1935     if (masterPtr->masterDataPtr == NULL) {
1936     GridMaster *gridPtr = masterPtr->masterDataPtr =
1937     (GridMaster *) ckalloc(sizeof(GridMaster));
1938     size = sizeof(SlotInfo) * TYPICAL_SIZE;
1939    
1940     gridPtr->columnEnd = 0;
1941     gridPtr->columnMax = 0;
1942     gridPtr->columnPtr = (SlotInfo *) ckalloc(size);
1943     gridPtr->columnSpace = TYPICAL_SIZE;
1944     gridPtr->rowEnd = 0;
1945     gridPtr->rowMax = 0;
1946     gridPtr->rowPtr = (SlotInfo *) ckalloc(size);
1947     gridPtr->rowSpace = TYPICAL_SIZE;
1948     gridPtr->startX = 0;
1949     gridPtr->startY = 0;
1950    
1951     memset((VOID *) gridPtr->columnPtr, 0, size);
1952     memset((VOID *) gridPtr->rowPtr, 0, size);
1953     }
1954     }
1955    
1956     /*
1957     *----------------------------------------------------------------------
1958     *
1959     * Unlink --
1960     *
1961     * Remove a grid from its parent's list of slaves.
1962     *
1963     * Results:
1964     * None.
1965     *
1966     * Side effects:
1967     * The parent will be scheduled for re-arranging, and the size of the
1968     * grid will be adjusted accordingly
1969     *
1970     *----------------------------------------------------------------------
1971     */
1972    
1973     static void
1974     Unlink(slavePtr)
1975     register Gridder *slavePtr; /* Window to unlink. */
1976     {
1977     register Gridder *masterPtr, *slavePtr2;
1978     GridMaster *gridPtr; /* pointer to grid data */
1979    
1980     masterPtr = slavePtr->masterPtr;
1981     if (masterPtr == NULL) {
1982     return;
1983     }
1984    
1985     gridPtr = masterPtr->masterDataPtr;
1986     if (masterPtr->slavePtr == slavePtr) {
1987     masterPtr->slavePtr = slavePtr->nextPtr;
1988     }
1989     else {
1990     for (slavePtr2 = masterPtr->slavePtr; ; slavePtr2 = slavePtr2->nextPtr) {
1991     if (slavePtr2 == NULL) {
1992     panic("Unlink couldn't find previous window");
1993     }
1994     if (slavePtr2->nextPtr == slavePtr) {
1995     slavePtr2->nextPtr = slavePtr->nextPtr;
1996     break;
1997     }
1998     }
1999     }
2000     if (!(masterPtr->flags & REQUESTED_RELAYOUT)) {
2001     masterPtr->flags |= REQUESTED_RELAYOUT;
2002     Tcl_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
2003     }
2004     if (masterPtr->abortPtr != NULL) {
2005     *masterPtr->abortPtr = 1;
2006     }
2007    
2008     if ((slavePtr->numCols+slavePtr->column == gridPtr->columnMax)
2009     || (slavePtr->numRows+slavePtr->row == gridPtr->rowMax)) {
2010     }
2011     slavePtr->masterPtr = NULL;
2012     }
2013    
2014     /*
2015     *----------------------------------------------------------------------
2016     *
2017     * DestroyGrid --
2018     *
2019     * This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
2020     * to clean up the internal structure of a grid at a safe time
2021     * (when no-one is using it anymore). Cleaning up the grid involves
2022     * freeing the main structure for all windows. and the master structure
2023     * for geometry managers.
2024     *
2025     * Results:
2026     * None.
2027     *
2028     * Side effects:
2029     * Everything associated with the grid is freed up.
2030     *
2031     *----------------------------------------------------------------------
2032     */
2033    
2034     static void
2035     DestroyGrid(memPtr)
2036     char *memPtr; /* Info about window that is now dead. */
2037     {
2038     register Gridder *gridPtr = (Gridder *) memPtr;
2039    
2040     if (gridPtr->masterDataPtr != NULL) {
2041     if (gridPtr->masterDataPtr->rowPtr != NULL) {
2042     Tcl_Free((char *) gridPtr->masterDataPtr -> rowPtr);
2043     }
2044     if (gridPtr->masterDataPtr->columnPtr != NULL) {
2045     Tcl_Free((char *) gridPtr->masterDataPtr -> columnPtr);
2046     }
2047     Tcl_Free((char *) gridPtr->masterDataPtr);
2048     }
2049     Tcl_Free((char *) gridPtr);
2050     }
2051    
2052     /*
2053     *----------------------------------------------------------------------
2054     *
2055     * GridStructureProc --
2056     *
2057     * This procedure is invoked by the Tk event dispatcher in response
2058     * to StructureNotify events.
2059     *
2060     * Results:
2061     * None.
2062     *
2063     * Side effects:
2064     * If a window was just deleted, clean up all its grid-related
2065     * information. If it was just resized, re-configure its slaves, if
2066     * any.
2067     *
2068     *----------------------------------------------------------------------
2069     */
2070    
2071     static void
2072     GridStructureProc(clientData, eventPtr)
2073     ClientData clientData; /* Our information about window
2074     * referred to by eventPtr. */
2075     XEvent *eventPtr; /* Describes what just happened. */
2076     {
2077     register Gridder *gridPtr = (Gridder *) clientData;
2078     TkDisplay *dispPtr = ((TkWindow *) gridPtr->tkwin)->dispPtr;
2079    
2080     if (eventPtr->type == ConfigureNotify) {
2081     if (!(gridPtr->flags & REQUESTED_RELAYOUT)) {
2082     gridPtr->flags |= REQUESTED_RELAYOUT;
2083     Tcl_DoWhenIdle(ArrangeGrid, (ClientData) gridPtr);
2084     }
2085     if (gridPtr->doubleBw != 2*Tk_Changes(gridPtr->tkwin)->border_width) {
2086     if ((gridPtr->masterPtr != NULL) &&
2087     !(gridPtr->masterPtr->flags & REQUESTED_RELAYOUT)) {
2088     gridPtr->doubleBw = 2*Tk_Changes(gridPtr->tkwin)->border_width;
2089     gridPtr->masterPtr->flags |= REQUESTED_RELAYOUT;
2090     Tcl_DoWhenIdle(ArrangeGrid, (ClientData) gridPtr->masterPtr);
2091     }
2092     }
2093     } else if (eventPtr->type == DestroyNotify) {
2094     register Gridder *gridPtr2, *nextPtr;
2095    
2096     if (gridPtr->masterPtr != NULL) {
2097     Unlink(gridPtr);
2098     }
2099     for (gridPtr2 = gridPtr->slavePtr; gridPtr2 != NULL;
2100     gridPtr2 = nextPtr) {
2101     Tk_UnmapWindow(gridPtr2->tkwin);
2102     gridPtr2->masterPtr = NULL;
2103     nextPtr = gridPtr2->nextPtr;
2104     gridPtr2->nextPtr = NULL;
2105     }
2106     Tcl_DeleteHashEntry(Tcl_FindHashEntry(&dispPtr->gridHashTable,
2107     (char *) gridPtr->tkwin));
2108     if (gridPtr->flags & REQUESTED_RELAYOUT) {
2109     Tcl_CancelIdleCall(ArrangeGrid, (ClientData) gridPtr);
2110     }
2111     gridPtr->tkwin = NULL;
2112     Tcl_EventuallyFree((ClientData) gridPtr, DestroyGrid);
2113     } else if (eventPtr->type == MapNotify) {
2114     if (!(gridPtr->flags & REQUESTED_RELAYOUT)) {
2115     gridPtr->flags |= REQUESTED_RELAYOUT;
2116     Tcl_DoWhenIdle(ArrangeGrid, (ClientData) gridPtr);
2117     }
2118     } else if (eventPtr->type == UnmapNotify) {
2119     register Gridder *gridPtr2;
2120    
2121     for (gridPtr2 = gridPtr->slavePtr; gridPtr2 != NULL;
2122     gridPtr2 = gridPtr2->nextPtr) {
2123     Tk_UnmapWindow(gridPtr2->tkwin);
2124     }
2125     }
2126     }
2127    
2128     /*
2129     *----------------------------------------------------------------------
2130     *
2131     * ConfigureSlaves --
2132     *
2133     * This implements the guts of the "grid configure" command. Given
2134     * a list of slaves and configuration options, it arranges for the
2135     * grid to manage the slaves and sets the specified options.
2136     * arguments consist of windows or window shortcuts followed by
2137     * "-option value" pairs.
2138     *
2139     * Results:
2140     * TCL_OK is returned if all went well. Otherwise, TCL_ERROR is
2141     * returned and the interp's result is set to contain an error message.
2142     *
2143     * Side effects:
2144     * Slave windows get taken over by the grid.
2145     *
2146     *----------------------------------------------------------------------
2147     */
2148    
2149     static int
2150     ConfigureSlaves(interp, tkwin, argc, argv)
2151     Tcl_Interp *interp; /* Interpreter for error reporting. */
2152     Tk_Window tkwin; /* Any window in application containing
2153     * slaves. Used to look up slave names. */
2154     int argc; /* Number of elements in argv. */
2155     char *argv[]; /* Argument strings: contains one or more
2156     * window names followed by any number
2157     * of "option value" pairs. Caller must
2158     * make sure that there is at least one
2159     * window name. */
2160     {
2161     Gridder *masterPtr;
2162     Gridder *slavePtr;
2163     Tk_Window other, slave, parent, ancestor;
2164     int i, j, c, tmp;
2165     size_t length;
2166     int numWindows;
2167     int width;
2168     int defaultColumn = 0; /* default column number */
2169     int defaultColumnSpan = 1; /* default number of columns */
2170     char *lastWindow; /* use this window to base current
2171     * Row/col on */
2172    
2173     /*
2174     * Count the number of windows, or window short-cuts.
2175     */
2176    
2177     for(numWindows=i=0;i<argc;i++) {
2178     char firstChar = *argv[i];
2179     if (firstChar == '.') {
2180     numWindows++;
2181     continue;
2182     }
2183     length = strlen(argv[i]);
2184     if (length > 1 && firstChar == '-') {
2185     break;
2186     }
2187     if (length > 1) {
2188     Tcl_AppendResult(interp, "unexpected parameter, \"",
2189     argv[i], "\", in configure list. ",
2190     "Should be window name or option", (char *) NULL);
2191     return TCL_ERROR;
2192     }
2193    
2194     if ((firstChar == REL_HORIZ) && ((numWindows == 0) ||
2195     (*argv[i-1] == REL_SKIP) || (*argv[i-1] == REL_VERT))) {
2196     Tcl_AppendResult(interp,
2197     "Must specify window before shortcut '-'.",
2198     (char *) NULL);
2199     return TCL_ERROR;
2200     }
2201    
2202     if ((firstChar == REL_VERT) || (firstChar == REL_SKIP)
2203     || (firstChar == REL_HORIZ)) {
2204     continue;
2205     }
2206    
2207     Tcl_AppendResult(interp, "invalid window shortcut, \"",
2208     argv[i], "\" should be '-', 'x', or '^'", (char *) NULL);
2209     return TCL_ERROR;
2210     }
2211     numWindows = i;
2212    
2213     if ((argc-numWindows)&1) {
2214     Tcl_AppendResult(interp, "extra option or",
2215     " option with no value", (char *) NULL);
2216     return TCL_ERROR;
2217     }
2218    
2219     /*
2220     * Iterate over all of the slave windows and short-cuts, parsing
2221     * options for each slave. It's a bit wasteful to re-parse the
2222     * options for each slave, but things get too messy if we try to
2223     * parse the arguments just once at the beginning. For example,
2224     * if a slave already is managed we want to just change a few
2225     * existing values without resetting everything. If there are
2226     * multiple windows, the -in option only gets processed for the
2227     * first window.
2228     */
2229    
2230     masterPtr = NULL;
2231     for (j = 0; j < numWindows; j++) {
2232     char firstChar = *argv[j];
2233    
2234     /*
2235     * '^' and 'x' cause us to skip a column. '-' is processed
2236     * as part of its preceeding slave.
2237     */
2238    
2239     if ((firstChar == REL_VERT) || (firstChar == REL_SKIP)) {
2240     defaultColumn++;
2241     continue;
2242     }
2243     if (firstChar == REL_HORIZ) {
2244     continue;
2245     }
2246    
2247     for (defaultColumnSpan=1;
2248     j + defaultColumnSpan < numWindows &&
2249     (*argv[j+defaultColumnSpan] == REL_HORIZ);
2250     defaultColumnSpan++) {
2251     /* null body */
2252     }
2253    
2254     slave = Tk_NameToWindow(interp, argv[j], tkwin);
2255     if (slave == NULL) {
2256     return TCL_ERROR;
2257     }
2258     if (Tk_IsTopLevel(slave)) {
2259     Tcl_AppendResult(interp, "can't manage \"", argv[j],
2260     "\": it's a top-level window", (char *) NULL);
2261     return TCL_ERROR;
2262     }
2263     slavePtr = GetGrid(slave);
2264    
2265     /*
2266     * The following statement is taken from tkPack.c:
2267     *
2268     * "If the slave isn't currently managed, reset all of its
2269     * configuration information to default values (there could
2270     * be old values left from a previous packer)."
2271     *
2272     * I [D.S.] disagree with this statement. If a slave is disabled (using
2273     * "forget") and then re-enabled, I submit that 90% of the time the
2274     * programmer will want it to retain its old configuration information.
2275     * If the programmer doesn't want this behavior, then the
2276     * defaults can be reestablished by hand, without having to worry
2277     * about keeping track of the old state.
2278     */
2279    
2280     for (i = numWindows; i < argc; i+=2) {
2281     length = strlen(argv[i]);
2282     c = argv[i][1];
2283    
2284     if (length < 2) {
2285     Tcl_AppendResult(interp, "unknown or ambiguous option \"",
2286     argv[i], "\": must be ",
2287     "-column, -columnspan, -in, -ipadx, -ipady, ",
2288     "-padx, -pady, -row, -rowspan, or -sticky",
2289     (char *) NULL);
2290     return TCL_ERROR;
2291     }
2292     if ((c == 'c') && (strncmp(argv[i], "-column", length) == 0)) {
2293     if (Tcl_GetInt(interp, argv[i+1], &tmp) != TCL_OK || tmp<0) {
2294     Tcl_ResetResult(interp);
2295     Tcl_AppendResult(interp, "bad column value \"", argv[i+1],
2296     "\": must be a non-negative integer", (char *)NULL);
2297     return TCL_ERROR;
2298     }
2299     slavePtr->column = tmp;
2300     } else if ((c == 'c')
2301     && (strncmp(argv[i], "-columnspan", length) == 0)) {
2302     if (Tcl_GetInt(interp, argv[i+1], &tmp) != TCL_OK || tmp <= 0) {
2303     Tcl_ResetResult(interp);
2304     Tcl_AppendResult(interp, "bad columnspan value \"", argv[i+1],
2305     "\": must be a positive integer", (char *)NULL);
2306     return TCL_ERROR;
2307     }
2308     slavePtr->numCols = tmp;
2309     } else if ((c == 'i') && (strncmp(argv[i], "-in", length) == 0)) {
2310     other = Tk_NameToWindow(interp, argv[i+1], tkwin);
2311     if (other == NULL) {
2312     return TCL_ERROR;
2313     }
2314     if (other == slave) {
2315     Tcl_SetResult(interp, "Window can't be managed in itself",
2316     TCL_STATIC);
2317     return TCL_ERROR;
2318     }
2319     masterPtr = GetGrid(other);
2320     InitMasterData(masterPtr);
2321     } else if ((c == 'i')
2322     && (strncmp(argv[i], "-ipadx", length) == 0)) {
2323     if ((Tk_GetPixels(interp, slave, argv[i+1], &tmp) != TCL_OK)
2324     || (tmp < 0)) {
2325     Tcl_ResetResult(interp);
2326     Tcl_AppendResult(interp, "bad ipadx value \"", argv[i+1],
2327     "\": must be positive screen distance",
2328     (char *) NULL);
2329     return TCL_ERROR;
2330     }
2331     slavePtr->iPadX = tmp*2;
2332     } else if ((c == 'i')
2333     && (strncmp(argv[i], "-ipady", length) == 0)) {
2334     if ((Tk_GetPixels(interp, slave, argv[i+1], &tmp) != TCL_OK)
2335     || (tmp< 0)) {
2336     Tcl_ResetResult(interp);
2337     Tcl_AppendResult(interp, "bad ipady value \"", argv[i+1],
2338     "\": must be positive screen distance",
2339     (char *) NULL);
2340     return TCL_ERROR;
2341     }
2342     slavePtr->iPadY = tmp*2;
2343     } else if ((c == 'p')
2344     && (strncmp(argv[i], "-padx", length) == 0)) {
2345     if ((Tk_GetPixels(interp, slave, argv[i+1], &tmp) != TCL_OK)
2346     || (tmp< 0)) {
2347     Tcl_ResetResult(interp);
2348     Tcl_AppendResult(interp, "bad padx value \"", argv[i+1],
2349     "\": must be positive screen distance",
2350     (char *) NULL);
2351     return TCL_ERROR;
2352     }
2353     slavePtr->padX = tmp*2;
2354     } else if ((c == 'p')
2355     && (strncmp(argv[i], "-pady", length) == 0)) {
2356     if ((Tk_GetPixels(interp, slave, argv[i+1], &tmp) != TCL_OK)
2357     || (tmp< 0)) {
2358     Tcl_ResetResult(interp);
2359     Tcl_AppendResult(interp, "bad pady value \"", argv[i+1],
2360     "\": must be positive screen distance",
2361     (char *) NULL);
2362     return TCL_ERROR;
2363     }
2364     slavePtr->padY = tmp*2;
2365     } else if ((c == 'r') && (strncmp(argv[i], "-row", length) == 0)) {
2366     if (Tcl_GetInt(interp, argv[i+1], &tmp) != TCL_OK || tmp<0) {
2367     Tcl_ResetResult(interp);
2368     Tcl_AppendResult(interp, "bad grid value \"", argv[i+1],
2369     "\": must be a non-negative integer", (char *)NULL);
2370     return TCL_ERROR;
2371     }
2372     slavePtr->row = tmp;
2373     } else if ((c == 'r')
2374     && (strncmp(argv[i], "-rowspan", length) == 0)) {
2375     if ((Tcl_GetInt(interp, argv[i+1], &tmp) != TCL_OK) || tmp<=0) {
2376     Tcl_ResetResult(interp);
2377     Tcl_AppendResult(interp, "bad rowspan value \"", argv[i+1],
2378     "\": must be a positive integer", (char *)NULL);
2379     return TCL_ERROR;
2380     }
2381     slavePtr->numRows = tmp;
2382     } else if ((c == 's')
2383     && strncmp(argv[i], "-sticky", length) == 0) {
2384     int sticky = StringToSticky(argv[i+1]);
2385     if (sticky == -1) {
2386     Tcl_AppendResult(interp, "bad stickyness value \"", argv[i+1],
2387     "\": must be a string containing n, e, s, and/or w",
2388     (char *)NULL);
2389     return TCL_ERROR;
2390     }
2391     slavePtr->sticky = sticky;
2392     } else {
2393     Tcl_AppendResult(interp, "unknown or ambiguous option \"",
2394     argv[i], "\": must be ",
2395     "-column, -columnspan, -in, -ipadx, -ipady, ",
2396     "-padx, -pady, -row, -rowspan, or -sticky",
2397     (char *) NULL);
2398     return TCL_ERROR;
2399     }
2400     }
2401    
2402     /*
2403     * Make sure we have a geometry master. We look at:
2404     * 1) the -in flag
2405     * 2) the geometry master of the first slave (if specified)
2406     * 3) the parent of the first slave.
2407     */
2408    
2409     if (masterPtr == NULL) {
2410     masterPtr = slavePtr->masterPtr;
2411     }
2412     parent = Tk_Parent(slave);
2413     if (masterPtr == NULL) {
2414     masterPtr = GetGrid(parent);
2415     InitMasterData(masterPtr);
2416     }
2417    
2418     if (slavePtr->masterPtr != NULL && slavePtr->masterPtr != masterPtr) {
2419     Unlink(slavePtr);
2420     slavePtr->masterPtr = NULL;
2421     }
2422    
2423     if (slavePtr->masterPtr == NULL) {
2424     Gridder *tempPtr = masterPtr->slavePtr;
2425     slavePtr->masterPtr = masterPtr;
2426     masterPtr->slavePtr = slavePtr;
2427     slavePtr->nextPtr = tempPtr;
2428     }
2429    
2430     /*
2431     * Make sure that the slave's parent is either the master or
2432     * an ancestor of the master, and that the master and slave
2433     * aren't the same.
2434     */
2435    
2436     for (ancestor = masterPtr->tkwin; ; ancestor = Tk_Parent(ancestor)) {
2437     if (ancestor == parent) {
2438     break;
2439     }
2440     if (Tk_IsTopLevel(ancestor)) {
2441     Tcl_AppendResult(interp, "can't put ", argv[j],
2442     " inside ", Tk_PathName(masterPtr->tkwin),
2443     (char *) NULL);
2444     Unlink(slavePtr);
2445     return TCL_ERROR;
2446     }
2447     }
2448    
2449     /*
2450     * Try to make sure our master isn't managed by us.
2451     */
2452    
2453     if (masterPtr->masterPtr == slavePtr) {
2454     Tcl_AppendResult(interp, "can't put ", argv[j],
2455     " inside ", Tk_PathName(masterPtr->tkwin),
2456     ", would cause management loop.",
2457     (char *) NULL);
2458     Unlink(slavePtr);
2459     return TCL_ERROR;
2460     }
2461    
2462     Tk_ManageGeometry(slave, &gridMgrType, (ClientData) slavePtr);
2463    
2464     /*
2465     * Assign default position information.
2466     */
2467    
2468     if (slavePtr->column == -1) {
2469     slavePtr->column = defaultColumn;
2470     }
2471     slavePtr->numCols += defaultColumnSpan - 1;
2472     if (slavePtr->row == -1) {
2473     if (masterPtr->masterDataPtr == NULL) {
2474     slavePtr->row = 0;
2475     } else {
2476     slavePtr->row = masterPtr->masterDataPtr->rowEnd;
2477     }
2478     }
2479     defaultColumn += slavePtr->numCols;
2480     defaultColumnSpan = 1;
2481    
2482     /*
2483     * Arrange for the parent to be re-arranged at the first
2484     * idle moment.
2485     */
2486    
2487     if (masterPtr->abortPtr != NULL) {
2488     *masterPtr->abortPtr = 1;
2489     }
2490     if (!(masterPtr->flags & REQUESTED_RELAYOUT)) {
2491     masterPtr->flags |= REQUESTED_RELAYOUT;
2492     Tcl_DoWhenIdle(ArrangeGrid, (ClientData) masterPtr);
2493     }
2494     }
2495    
2496     /* Now look for all the "^"'s. */
2497    
2498     lastWindow = NULL;
2499     for (j = 0; j < numWindows; j++) {
2500     struct Gridder *otherPtr;
2501     int match; /* found a match for the ^ */
2502     int lastRow, lastColumn; /* implied end of table */
2503    
2504     if (*argv[j] == '.') {
2505     lastWindow = argv[j];
2506     }
2507     if (*argv[j] != REL_VERT) {
2508     continue;
2509     }
2510    
2511     if (masterPtr == NULL) {
2512     Tcl_AppendResult(interp, "can't use '^', cant find master",
2513     (char *) NULL);
2514     return TCL_ERROR;
2515     }
2516    
2517     /* Count the number of consecutive ^'s starting from this position */
2518     for (width=1; width+j < numWindows && *argv[j+width] == REL_VERT;
2519     width++) {
2520     /* Null Body */
2521     }
2522    
2523     /*
2524     * Find the implied grid location of the ^
2525     */
2526    
2527     if (lastWindow == NULL) {
2528     if (masterPtr->masterDataPtr != NULL) {
2529     SetGridSize(masterPtr);
2530     lastRow = masterPtr->masterDataPtr->rowEnd - 2;
2531     } else {
2532     lastRow = 0;
2533     }
2534     lastColumn = 0;
2535     } else {
2536     other = Tk_NameToWindow(interp, lastWindow, tkwin);
2537     otherPtr = GetGrid(other);
2538     lastRow = otherPtr->row + otherPtr->numRows - 2;
2539     lastColumn = otherPtr->column + otherPtr->numCols;
2540     }
2541    
2542     for (match=0, slavePtr = masterPtr->slavePtr; slavePtr != NULL;
2543     slavePtr = slavePtr->nextPtr) {
2544    
2545     if (slavePtr->column == lastColumn
2546     && slavePtr->row + slavePtr->numRows - 1 == lastRow) {
2547     if (slavePtr->numCols <= width) {
2548     slavePtr->numRows++;
2549     match++;
2550     j += slavePtr->numCols - 1;
2551     lastWindow = Tk_PathName(slavePtr->tkwin);
2552     break;
2553     }
2554     }
2555     }
2556     if (!match) {
2557     Tcl_AppendResult(interp, "can't find slave to extend with \"^\".",
2558     (char *) NULL);
2559     return TCL_ERROR;
2560     }
2561     /* j += width - 1; */
2562     }
2563    
2564     if (masterPtr == NULL) {
2565     Tcl_AppendResult(interp, "can't determine master window",
2566     (char *) NULL);
2567     return TCL_ERROR;
2568     }
2569     SetGridSize(masterPtr);
2570     return TCL_OK;
2571     }
2572    
2573     /*
2574     *----------------------------------------------------------------------
2575     *
2576     * StickyToString
2577     *
2578     * Converts the internal boolean combination of "sticky" bits onto
2579     * a TCL list element containing zero or mor of n, s, e, or w.
2580     *
2581     * Results:
2582     * A string is placed into the "result" pointer.
2583     *
2584     * Side effects:
2585     * none.
2586     *
2587     *----------------------------------------------------------------------
2588     */
2589    
2590     static void
2591     StickyToString(flags, result)
2592     int flags; /* the sticky flags */
2593     char *result; /* where to put the result */
2594     {
2595     int count = 0;
2596     if (flags&STICK_NORTH) {
2597     result[count++] = 'n';
2598     }
2599     if (flags&STICK_EAST) {
2600     result[count++] = 'e';
2601     }
2602     if (flags&STICK_SOUTH) {
2603     result[count++] = 's';
2604     }
2605     if (flags&STICK_WEST) {
2606     result[count++] = 'w';
2607     }
2608     if (count) {
2609     result[count] = '\0';
2610     } else {
2611     sprintf(result,"{}");
2612     }
2613     }
2614    
2615     /*
2616     *----------------------------------------------------------------------
2617     *
2618     * StringToSticky --
2619     *
2620     * Converts an ascii string representing a widgets stickyness
2621     * into the boolean result.
2622     *
2623     * Results:
2624     * The boolean combination of the "sticky" bits is retuned. If an
2625     * error occurs, such as an invalid character, -1 is returned instead.
2626     *
2627     * Side effects:
2628     * none
2629     *
2630     *----------------------------------------------------------------------
2631     */
2632    
2633     static int
2634     StringToSticky(string)
2635     char *string;
2636     {
2637     int sticky = 0;
2638     char c;
2639    
2640     while ((c = *string++) != '\0') {
2641     switch (c) {
2642     case 'n': case 'N': sticky |= STICK_NORTH; break;
2643     case 'e': case 'E': sticky |= STICK_EAST; break;
2644     case 's': case 'S': sticky |= STICK_SOUTH; break;
2645     case 'w': case 'W': sticky |= STICK_WEST; break;
2646     case ' ': case ',': case '\t': case '\r': case '\n': break;
2647     default: return -1;
2648     }
2649     }
2650     return sticky;
2651     }
2652    
2653    
2654     /* $History: tkGrid.c $
2655     *
2656     * ***************** Version 1 *****************
2657     * User: Dtashley Date: 1/02/01 Time: 2:55a
2658     * Created in $/IjuScripter, IjuConsole/Source/Tk Base
2659     * Initial check-in.
2660     */
2661    
2662     /* End of TKGRID.C */

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25