1 /*
2  * bltTable.c --
3  *
4  *	This module implements a table-based geometry manager
5  *	for the BLT toolkit.
6  *
7  * Copyright 1993-1998 Lucent Technologies, Inc.
8  *
9  * Permission to use, copy, modify, and distribute this software and
10  * its documentation for any purpose and without fee is hereby
11  * granted, provided that the above copyright notice appear in all
12  * copies and that both that the copyright notice and warranty
13  * disclaimer appear in supporting documentation, and that the names
14  * of Lucent Technologies any of their entities not be used in
15  * advertising or publicity pertaining to distribution of the software
16  * without specific, written prior permission.
17  *
18  * Lucent Technologies disclaims all warranties with regard to this
19  * software, including all implied warranties of merchantability and
20  * fitness.  In no event shall Lucent Technologies be liable for any
21  * special, indirect or consequential damages or any damages
22  * whatsoever resulting from loss of use, data or profits, whether in
23  * an action of contract, negligence or other tortuous action, arising
24  * out of or in connection with the use or performance of this
25  * software.
26  *
27  *	The "blttable" geometry manager was created by George Howlett.
28  */
29 
30 /*
31  * To do:
32  *
33  * 3) No way to detect if widget is already a container of another
34  *    geometry manager.  This one is especially bad with toplevel
35  *    widgets, causing the window manager to lock-up trying to handle the
36  *    myriads of resize requests.
37  *
38  *    Note: This problem continues in Tk 8.x.  It's possible for a widget
39  *	    to be a container for two different geometry managers.  Each manager
40  *	    will set its own requested geometry for the container widget. The
41  *	    winner sets the geometry last (sometimes ad infinitum).
42  *
43  * 7) Relative sizing of partitions?
44  *
45  */
46 
47 #include "bltInt.h"
48 
49 #include "bltTable.h"
50 
51 #define TABLE_THREAD_KEY	"BLT Table Data"
52 #define TABLE_DEF_PAD		0
53 
54 /*
55  * Default values for widget attributes.
56  */
57 #define DEF_TABLE_ANCHOR	"center"
58 #define DEF_TABLE_COLUMNS 	"0"
59 #define DEF_TABLE_FILL		"none"
60 #define DEF_TABLE_PAD		"0"
61 #define DEF_TABLE_PROPAGATE 	"1"
62 #define DEF_TABLE_RESIZE	"both"
63 #define DEF_TABLE_ROWS		"0"
64 #define DEF_TABLE_SPAN		"1"
65 #define DEF_TABLE_CONTROL	"normal"
66 #define DEF_TABLE_WEIGHT	"1.0"
67 
68 #define ENTRY_DEF_PAD		0
69 #define ENTRY_DEF_ANCHOR	TK_ANCHOR_CENTER
70 #define ENTRY_DEF_FILL		FILL_NONE
71 #define ENTRY_DEF_SPAN		1
72 #define ENTRY_DEF_CONTROL	CONTROL_NORMAL
73 #define ENTRY_DEF_IPAD		0
74 
75 #define ROWCOL_DEF_RESIZE	(RESIZE_BOTH | RESIZE_VIRGIN)
76 #define ROWCOL_DEF_PAD		0
77 #define ROWCOL_DEF_WEIGHT	1.0
78 
79 static Blt_Uid rowUid, columnUid;
80 
81 static void WidgetGeometryProc _ANSI_ARGS_((ClientData clientData,
82 	Tk_Window tkwin));
83 static void WidgetCustodyProc _ANSI_ARGS_((ClientData clientData,
84 	Tk_Window tkwin));
85 
86 static Tk_GeomMgr tableMgrInfo =
87 {
88     "blttable",			/* Name of geometry manager used by winfo */
89     WidgetGeometryProc,		/* Procedure to for new geometry requests */
90     WidgetCustodyProc,		/* Procedure when widget is taken away */
91 };
92 
93 static int StringToLimits _ANSI_ARGS_((ClientData clientData,
94 	Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec,
95 	int offset));
96 
97 static char *LimitsToString _ANSI_ARGS_((ClientData clientData,
98 	Tk_Window tkwin, char *widgRec, int offset,
99 	Tcl_FreeProc **freeProcPtr));
100 
101 static Tk_CustomOption limitsOption =
102 {
103     StringToLimits, LimitsToString, (ClientData)0
104 };
105 
106 static int StringToResize _ANSI_ARGS_((ClientData clientData,
107 	Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec,
108 	int offset));
109 static char *ResizeToString _ANSI_ARGS_((ClientData clientData,
110 	Tk_Window tkwin, char *widgRec, int offset,
111 	Tcl_FreeProc **freeProcPtr));
112 
113 static Tk_CustomOption resizeOption =
114 {
115     StringToResize, ResizeToString, (ClientData)0
116 };
117 
118 static int StringToControl _ANSI_ARGS_((ClientData clientData,
119 	Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec,
120 	int offset));
121 static char *ControlToString _ANSI_ARGS_((ClientData clientData,
122 	Tk_Window tkwin, char *widgRec, int offset,
123 	Tcl_FreeProc **freeProcPtr));
124 
125 static Tk_CustomOption controlOption =
126 {
127     StringToControl, ControlToString, (ClientData)0
128 };
129 
130 extern Tk_CustomOption bltPadOption;
131 extern Tk_CustomOption bltFillOption;
132 extern Tk_CustomOption bltDistanceOption;
133 
134 static Tk_ConfigSpec rowConfigSpecs[] =
135 {
136     {TK_CONFIG_CUSTOM, "-height", (char *)NULL, (char *)NULL,
137 	(char *)NULL, Tk_Offset(RowColumn, reqSize), TK_CONFIG_NULL_OK,
138 	&limitsOption},
139     {TK_CONFIG_CUSTOM, "-pady", (char *)NULL, (char *)NULL,
140 	DEF_TABLE_PAD, Tk_Offset(RowColumn, pad),
141 	TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption},
142     {TK_CONFIG_CUSTOM, "-resize", (char *)NULL, (char *)NULL,
143 	DEF_TABLE_RESIZE, Tk_Offset(RowColumn, resize),
144 	TK_CONFIG_DONT_SET_DEFAULT, &resizeOption},
145     {TK_CONFIG_DOUBLE, "-weight", (char *)NULL, (char *)NULL,
146 	DEF_TABLE_WEIGHT, Tk_Offset(RowColumn, weight),
147 	TK_CONFIG_NULL_OK | TK_CONFIG_DONT_SET_DEFAULT, &limitsOption},
148     {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
149 };
150 
151 static Tk_ConfigSpec columnConfigSpecs[] =
152 {
153     {TK_CONFIG_CUSTOM, "-padx", (char *)NULL, (char *)NULL,
154 	DEF_TABLE_PAD, Tk_Offset(RowColumn, pad),
155 	TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption},
156     {TK_CONFIG_CUSTOM, "-resize", (char *)NULL, (char *)NULL,
157 	DEF_TABLE_RESIZE, Tk_Offset(RowColumn, resize),
158 	TK_CONFIG_DONT_SET_DEFAULT, &resizeOption},
159     {TK_CONFIG_DOUBLE, "-weight", (char *)NULL, (char *)NULL,
160 	DEF_TABLE_WEIGHT, Tk_Offset(RowColumn, weight),
161 	TK_CONFIG_NULL_OK | TK_CONFIG_DONT_SET_DEFAULT, &limitsOption},
162     {TK_CONFIG_CUSTOM, "-width", (char *)NULL, (char *)NULL,
163 	(char *)NULL, Tk_Offset(RowColumn, reqSize), TK_CONFIG_NULL_OK,
164 	&limitsOption},
165     {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
166 };
167 
168 
169 static Tk_ConfigSpec entryConfigSpecs[] =
170 {
171     {TK_CONFIG_ANCHOR, "-anchor", (char *)NULL, (char *)NULL,
172 	DEF_TABLE_ANCHOR, Tk_Offset(Entry, anchor),
173 	TK_CONFIG_DONT_SET_DEFAULT},
174     {TK_CONFIG_INT, "-columnspan", "columnSpan", (char *)NULL,
175 	DEF_TABLE_SPAN, Tk_Offset(Entry, column.span),
176 	TK_CONFIG_DONT_SET_DEFAULT},
177     {TK_CONFIG_CUSTOM, "-columncontrol", "columnControl", (char *)NULL,
178 	DEF_TABLE_CONTROL, Tk_Offset(Entry, column.control),
179 	TK_CONFIG_DONT_SET_DEFAULT, &controlOption},
180     {TK_CONFIG_SYNONYM, "-cspan", "columnSpan", (char *)NULL,
181 	(char *)NULL, Tk_Offset(Entry, column.span), 0},
182     {TK_CONFIG_SYNONYM, "-ccontrol", "columnControl", (char *)NULL,
183 	(char *)NULL, Tk_Offset(Entry, column.control), 0},
184     {TK_CONFIG_CUSTOM, "-fill", (char *)NULL, (char *)NULL,
185 	DEF_TABLE_FILL, Tk_Offset(Entry, fill),
186 	TK_CONFIG_DONT_SET_DEFAULT, &bltFillOption},
187     {TK_CONFIG_SYNONYM, "-height", "reqHeight", (char *)NULL,
188 	(char *)NULL, Tk_Offset(Entry, reqHeight), 0},
189     {TK_CONFIG_CUSTOM, "-padx", (char *)NULL, (char *)NULL,
190 	(char *)NULL, Tk_Offset(Entry, padX), 0, &bltPadOption},
191     {TK_CONFIG_CUSTOM, "-pady", (char *)NULL, (char *)NULL,
192 	(char *)NULL, Tk_Offset(Entry, padY), 0, &bltPadOption},
193     {TK_CONFIG_CUSTOM, "-ipadx", (char *)NULL, (char *)NULL,
194 	(char *)NULL, Tk_Offset(Entry, ipadX), 0, &bltDistanceOption},
195     {TK_CONFIG_CUSTOM, "-ipady", (char *)NULL, (char *)NULL,
196 	(char *)NULL, Tk_Offset(Entry, ipadY), 0, &bltDistanceOption},
197     {TK_CONFIG_CUSTOM, "-reqheight", "reqHeight", (char *)NULL,
198 	(char *)NULL, Tk_Offset(Entry, reqHeight), TK_CONFIG_NULL_OK,
199 	&limitsOption},
200     {TK_CONFIG_CUSTOM, "-reqwidth", "reqWidth", (char *)NULL,
201 	(char *)NULL, Tk_Offset(Entry, reqWidth), TK_CONFIG_NULL_OK,
202 	&limitsOption},
203     {TK_CONFIG_INT, "-rowspan", "rowSpan", (char *)NULL,
204 	DEF_TABLE_SPAN, Tk_Offset(Entry, row.span),
205 	TK_CONFIG_DONT_SET_DEFAULT},
206     {TK_CONFIG_CUSTOM, "-rowcontrol", "rowControl", (char *)NULL,
207 	DEF_TABLE_CONTROL, Tk_Offset(Entry, row.control),
208 	TK_CONFIG_DONT_SET_DEFAULT, &controlOption},
209     {TK_CONFIG_SYNONYM, "-rspan", "rowSpan", (char *)NULL,
210 	(char *)NULL, Tk_Offset(Entry, row.span), 0},
211     {TK_CONFIG_SYNONYM, "-rcontrol", "rowControl", (char *)NULL,
212 	(char *)NULL, Tk_Offset(Entry, row.control), 0},
213     {TK_CONFIG_SYNONYM, "-width", "reqWidth", (char *)NULL,
214 	(char *)NULL, Tk_Offset(Entry, reqWidth), 0},
215     {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
216 };
217 
218 
219 static Tk_ConfigSpec tableConfigSpecs[] =
220 {
221     {TK_CONFIG_CUSTOM, "-padx", (char *)NULL, (char *)NULL,
222 	DEF_TABLE_PAD, Tk_Offset(Table, padX),
223 	TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption},
224     {TK_CONFIG_CUSTOM, "-pady", (char *)NULL, (char *)NULL,
225 	DEF_TABLE_PAD, Tk_Offset(Table, padY),
226 	TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption},
227     {TK_CONFIG_BOOLEAN, "-propagate", (char *)NULL, (char *)NULL,
228 	DEF_TABLE_PROPAGATE, Tk_Offset(Table, propagate),
229 	TK_CONFIG_DONT_SET_DEFAULT},
230     {TK_CONFIG_CUSTOM, "-reqheight", (char *)NULL, (char *)NULL,
231 	(char *)NULL, Tk_Offset(Table, reqHeight), TK_CONFIG_NULL_OK,
232 	&limitsOption},
233     {TK_CONFIG_CUSTOM, "-reqwidth", (char *)NULL, (char *)NULL,
234 	(char *)NULL, Tk_Offset(Table, reqWidth), TK_CONFIG_NULL_OK,
235 	&limitsOption},
236     {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
237 };
238 
239 /*
240  * Forward declarations
241  */
242 static void ArrangeTable _ANSI_ARGS_((ClientData clientData));
243 static void DestroyTable _ANSI_ARGS_((DestroyData dataPtr));
244 static void DestroyEntry _ANSI_ARGS_((Entry * entryPtr));
245 static void TableEventProc _ANSI_ARGS_((ClientData clientData,
246 	XEvent *eventPtr));
247 static void BinEntry _ANSI_ARGS_((Table *tablePtr, Entry * entryPtr));
248 static RowColumn *InitSpan _ANSI_ARGS_((PartitionInfo * infoPtr, int start,
249 	int span));
250 
251 static EntrySearchProc FindEntry;
252 static Tcl_CmdProc TableCmd;
253 static Tcl_InterpDeleteProc TableInterpDeleteProc;
254 static Tk_EventProc WidgetEventProc;
255 
256 /*
257  * ----------------------------------------------------------------------------
258  *
259  * StringToLimits --
260  *
261  *	Converts the list of elements into zero or more pixel values which
262  *	determine the range of pixel values possible.  An element can be in
263  *	any form accepted by Tk_GetPixels. The list has a different meaning
264  *	based upon the number of elements.
265  *
266  *	    # of elements:
267  *
268  *	    0 - the limits are reset to the defaults.
269  *	    1 - the minimum and maximum values are set to this
270  *		value, freezing the range at a single value.
271  *	    2 - first element is the minimum, the second is the
272  *		maximum.
273  *	    3 - first element is the minimum, the second is the
274  *		maximum, and the third is the nominal value.
275  *
276  *	Any element may be the empty string which indicates the default.
277  *
278  * Results:
279  *	The return value is a standard Tcl result.  The min and max fields
280  *	of the range are set.
281  *
282  * ----------------------------------------------------------------------------
283  */
284 /*ARGSUSED*/
285 static int
StringToLimits(clientData,interp,tkwin,string,widgRec,offset)286 StringToLimits(clientData, interp, tkwin, string, widgRec, offset)
287     ClientData clientData;	/* Not used. */
288     Tcl_Interp *interp;		/* Interpreter to send results back to */
289     Tk_Window tkwin;		/* Widget of table */
290     char *string;		/* New width list */
291     char *widgRec;		/* Widget record */
292     int offset;			/* Offset of limits */
293 {
294     Limits *limitsPtr = (Limits *)(widgRec + offset);
295     char **elemArr;
296     int nElem;
297     int limArr[3];
298     Tk_Window winArr[3];
299     int flags;
300 
301     elemArr = NULL;
302     nElem = 0;
303 
304     /* Initialize limits to default values */
305     limArr[2] = LIMITS_NOM;
306     limArr[1] = LIMITS_MAX;
307     limArr[0] = LIMITS_MIN;
308     winArr[0] = winArr[1] = winArr[2] = NULL;
309     flags = 0;
310 
311     if (string != NULL) {
312 	int size;
313 	int i;
314 
315 	if (Tcl_SplitList(interp, string, &nElem, &elemArr) != TCL_OK) {
316 	    return TCL_ERROR;
317 	}
318 	if (nElem > 3) {
319 	    Tcl_AppendResult(interp, "wrong # limits \"", string, "\"",
320 		(char *)NULL);
321 	    goto error;
322 	}
323 	for (i = 0; i < nElem; i++) {
324 	    if (elemArr[i][0] == '\0') {
325 		continue;	/* Empty string: use default value */
326 	    }
327 	    flags |= (LIMITS_SET_BIT << i);
328 	    if ((elemArr[i][0] == '.') &&
329 		((elemArr[i][1] == '\0') || isalpha(UCHAR(elemArr[i][1])))) {
330 		Tk_Window tkwin2;
331 
332 		/* Widget specified: save pointer to widget */
333 		tkwin2 = Tk_NameToWindow(interp, elemArr[i], tkwin);
334 		if (tkwin2 == NULL) {
335 		    goto error;
336 		}
337 		winArr[i] = tkwin2;
338 	    } else {
339 		if (Tk_GetPixels(interp, tkwin, elemArr[i], &size) != TCL_OK) {
340 		    goto error;
341 		}
342 		if ((size < LIMITS_MIN) || (size > LIMITS_MAX)) {
343 		    Tcl_AppendResult(interp, "bad limits \"", string, "\"",
344 			(char *)NULL);
345 		    goto error;
346 		}
347 		limArr[i] = size;
348 	    }
349 	}
350 	Blt_Free(elemArr);
351     }
352     /*
353     * Check the limits specified.  We can't check the requested
354     * size of widgets.
355     */
356     switch (nElem) {
357     case 1:
358 	flags |= (LIMITS_SET_MIN | LIMITS_SET_MAX);
359 	if (winArr[0] == NULL) {
360 	    limArr[1] = limArr[0];	/* Set minimum and maximum to value */
361 	} else {
362 	    winArr[1] = winArr[0];
363 	}
364 	break;
365 
366     case 2:
367 	if ((winArr[0] == NULL) && (winArr[1] == NULL) &&
368 	    (limArr[1] < limArr[0])) {
369 	    Tcl_AppendResult(interp, "bad range \"", string,
370 		"\": min > max", (char *)NULL);
371 	    return TCL_ERROR;	/* Minimum is greater than maximum */
372 	}
373 	break;
374 
375     case 3:
376 	if ((winArr[0] == NULL) && (winArr[1] == NULL)) {
377 	    if (limArr[1] < limArr[0]) {
378 		Tcl_AppendResult(interp, "bad range \"", string,
379 		    "\": min > max", (char *)NULL);
380 		return TCL_ERROR;	/* Minimum is greater than maximum */
381 	    }
382 	    if ((winArr[2] == NULL) &&
383 		((limArr[2] < limArr[0]) || (limArr[2] > limArr[1]))) {
384 		Tcl_AppendResult(interp, "nominal value \"", string,
385 		    "\" out of range", (char *)NULL);
386 		return TCL_ERROR;	/* Nominal is outside of range defined
387 					 * by minimum and maximum */
388 	    }
389 	}
390 	break;
391     }
392     limitsPtr->min = limArr[0];
393     limitsPtr->max = limArr[1];
394     limitsPtr->nom = limArr[2];
395     limitsPtr->wMin = winArr[0];
396     limitsPtr->wMax = winArr[1];
397     limitsPtr->wNom = winArr[2];
398     limitsPtr->flags = flags;
399     return TCL_OK;
400   error:
401     Blt_Free(elemArr);
402     return TCL_ERROR;
403 }
404 
405 /*
406  * ----------------------------------------------------------------------------
407  *
408  * ResetLimits --
409  *
410  *	Resets the limits to their default values.
411  *
412  * Results:
413  *	None.
414  *
415  * ----------------------------------------------------------------------------
416  */
417 INLINE static void
ResetLimits(limitsPtr)418 ResetLimits(limitsPtr)
419     Limits *limitsPtr;		/* Limits to be imposed on the value */
420 {
421     limitsPtr->flags = 0;
422     limitsPtr->min = LIMITS_MIN;
423     limitsPtr->max = LIMITS_MAX;
424     limitsPtr->nom = LIMITS_NOM;
425     limitsPtr->wNom = limitsPtr->wMax = limitsPtr->wMin = NULL;
426 }
427 
428 /*
429  * ----------------------------------------------------------------------------
430  *
431  * GetBoundedWidth --
432  *
433  *	Bounds a given width value to the limits described in the limit
434  *	structure.  The initial starting value may be overridden by the
435  *	nominal value in the limits.
436  *
437  * Results:
438  *	Returns the constrained value.
439  *
440  * ----------------------------------------------------------------------------
441  */
442 static int
GetBoundedWidth(width,limitsPtr)443 GetBoundedWidth(width, limitsPtr)
444     int width;			/* Initial value to be constrained */
445     Limits *limitsPtr;		/* Limits to be imposed on the value */
446 {
447     /*
448      * Check widgets for requested width values;
449      */
450     if (limitsPtr->wMin != NULL) {
451 	limitsPtr->min = Tk_ReqWidth(limitsPtr->wMin);
452     }
453     if (limitsPtr->wMax != NULL) {
454 	limitsPtr->max = Tk_ReqWidth(limitsPtr->wMax);
455     }
456     if (limitsPtr->wNom != NULL) {
457 	limitsPtr->nom = Tk_ReqWidth(limitsPtr->wNom);
458     }
459     if (limitsPtr->flags & LIMITS_SET_NOM) {
460 	width = limitsPtr->nom;	/* Override initial value */
461     }
462     if (width < limitsPtr->min) {
463 	width = limitsPtr->min;	/* Bounded by minimum value */
464     } else if (width > limitsPtr->max) {
465 	width = limitsPtr->max;	/* Bounded by maximum value */
466     }
467     return width;
468 }
469 
470 /*
471  * ----------------------------------------------------------------------------
472  *
473  * GetBoundedHeight --
474  *
475  *	Bounds a given value to the limits described in the limit
476  *	structure.  The initial starting value may be overridden by the
477  *	nominal value in the limits.
478  *
479  * Results:
480  *	Returns the constrained value.
481  *
482  * ----------------------------------------------------------------------------
483  */
484 static int
GetBoundedHeight(height,limitsPtr)485 GetBoundedHeight(height, limitsPtr)
486     int height;			/* Initial value to be constrained */
487     Limits *limitsPtr;		/* Limits to be imposed on the value */
488 {
489     /*
490      * Check widgets for requested height values;
491      */
492     if (limitsPtr->wMin != NULL) {
493 	limitsPtr->min = Tk_ReqHeight(limitsPtr->wMin);
494     }
495     if (limitsPtr->wMax != NULL) {
496 	limitsPtr->max = Tk_ReqHeight(limitsPtr->wMax);
497     }
498     if (limitsPtr->wNom != NULL) {
499 	limitsPtr->nom = Tk_ReqHeight(limitsPtr->wNom);
500     }
501     if (limitsPtr->flags & LIMITS_SET_NOM) {
502 	height = limitsPtr->nom;/* Override initial value */
503     }
504     if (height < limitsPtr->min) {
505 	height = limitsPtr->min;/* Bounded by minimum value */
506     } else if (height > limitsPtr->max) {
507 	height = limitsPtr->max;/* Bounded by maximum value */
508     }
509     return height;
510 }
511 
512 /*
513  * ----------------------------------------------------------------------------
514  *
515  * NameOfLimits --
516  *
517  *	Convert the values into a list representing the limits.
518  *
519  * Results:
520  *	The static string representation of the limits is returned.
521  *
522  * ----------------------------------------------------------------------------
523  */
524 static char *
NameOfLimits(limitsPtr)525 NameOfLimits(limitsPtr)
526     Limits *limitsPtr;
527 {
528     Tcl_DString buffer;
529 #define STRING_SPACE 200
530     static char string[STRING_SPACE + 1];
531 
532     Tcl_DStringInit(&buffer);
533 
534     if (limitsPtr->wMin != NULL) {
535 	Tcl_DStringAppendElement(&buffer, Tk_PathName(limitsPtr->wMin));
536     } else if (limitsPtr->flags & LIMITS_SET_MIN) {
537 	Tcl_DStringAppendElement(&buffer, Blt_Itoa(limitsPtr->min));
538     } else {
539 	Tcl_DStringAppendElement(&buffer, "");
540     }
541 
542     if (limitsPtr->wMax != NULL) {
543 	Tcl_DStringAppendElement(&buffer, Tk_PathName(limitsPtr->wMax));
544     } else if (limitsPtr->flags & LIMITS_SET_MAX) {
545 	Tcl_DStringAppendElement(&buffer, Blt_Itoa(limitsPtr->max));
546     } else {
547 	Tcl_DStringAppendElement(&buffer, "");
548     }
549 
550     if (limitsPtr->wNom != NULL) {
551 	Tcl_DStringAppendElement(&buffer, Tk_PathName(limitsPtr->wNom));
552     } else if (limitsPtr->flags & LIMITS_SET_NOM) {
553 	Tcl_DStringAppendElement(&buffer, Blt_Itoa(limitsPtr->nom));
554     } else {
555 	Tcl_DStringAppendElement(&buffer, "");
556     }
557     strncpy(string, Tcl_DStringValue(&buffer), STRING_SPACE);
558     string[STRING_SPACE] = '\0';
559     return string;
560 }
561 
562 /*
563  * ----------------------------------------------------------------------------
564  *
565  * LimitsToString --
566  *
567  *	Convert the limits of the pixel values allowed into a list.
568  *
569  * Results:
570  *	The string representation of the limits is returned.
571  *
572  * ----------------------------------------------------------------------------
573  */
574 /*ARGSUSED*/
575 static char *
LimitsToString(clientData,tkwin,widgRec,offset,freeProcPtr)576 LimitsToString(clientData, tkwin, widgRec, offset, freeProcPtr)
577     ClientData clientData;	/* Not used. */
578     Tk_Window tkwin;		/* Not used. */
579     char *widgRec;		/* Row/column structure record */
580     int offset;			/* Offset of widget RowColumn record */
581     Tcl_FreeProc **freeProcPtr;	/* Memory deallocation routine */
582 {
583     Limits *limitsPtr = (Limits *)(widgRec + offset);
584 
585     return NameOfLimits(limitsPtr);
586 }
587 
588 /*
589  * ----------------------------------------------------------------------------
590  *
591  * StringToResize --
592  *
593  *	Converts the resize mode into its numeric representation.  Valid
594  *	mode strings are "none", "expand", "shrink", or "both".
595  *
596  * ----------------------------------------------------------------------------
597  */
598 /*ARGSUSED*/
599 static int
StringToResize(clientData,interp,tkwin,string,widgRec,offset)600 StringToResize(clientData, interp, tkwin, string, widgRec, offset)
601     ClientData clientData;	/* Not used. */
602     Tcl_Interp *interp;		/* Interpreter to send results back to */
603     Tk_Window tkwin;		/* Not used. */
604     char *string;		/* Resize style string */
605     char *widgRec;		/* Entry structure record */
606     int offset;			/* Offset of style in record */
607 {
608     int *resizePtr = (int *)(widgRec + offset);
609     unsigned int length;
610     char c;
611 
612     c = string[0];
613     length = strlen(string);
614     if ((c == 'n') && (strncmp(string, "none", length) == 0)) {
615 	*resizePtr = RESIZE_NONE;
616     } else if ((c == 'b') && (strncmp(string, "both", length) == 0)) {
617 	*resizePtr = RESIZE_BOTH;
618     } else if ((c == 'e') && (strncmp(string, "expand", length) == 0)) {
619 	*resizePtr = RESIZE_EXPAND;
620     } else if ((c == 's') && (strncmp(string, "shrink", length) == 0)) {
621 	*resizePtr = RESIZE_SHRINK;
622     } else {
623 	Tcl_AppendResult(interp, "bad resize argument \"", string,
624 	    "\": should be \"none\", \"expand\", \"shrink\", or \"both\"",
625 	    (char *)NULL);
626 	return TCL_ERROR;
627     }
628     return TCL_OK;
629 }
630 
631 /*
632  * ----------------------------------------------------------------------------
633  *
634  * NameOfResize --
635  *
636  *	Converts the resize value into its string representation.
637  *
638  * Results:
639  *	Returns a pointer to the static name string.
640  *
641  * ----------------------------------------------------------------------------
642  */
643 static char *
NameOfResize(resize)644 NameOfResize(resize)
645     int resize;
646 {
647     switch (resize & RESIZE_BOTH) {
648     case RESIZE_NONE:
649 	return "none";
650     case RESIZE_EXPAND:
651 	return "expand";
652     case RESIZE_SHRINK:
653 	return "shrink";
654     case RESIZE_BOTH:
655 	return "both";
656     default:
657 	return "unknown resize value";
658     }
659 }
660 
661 /*
662  * ----------------------------------------------------------------------------
663  *
664  * ResizeToString --
665  *
666  *	Returns resize mode string based upon the resize flags.
667  *
668  * Results:
669  *	The resize mode string is returned.
670  *
671  * ----------------------------------------------------------------------------
672  */
673 /*ARGSUSED*/
674 static char *
ResizeToString(clientData,tkwin,widgRec,offset,freeProcPtr)675 ResizeToString(clientData, tkwin, widgRec, offset, freeProcPtr)
676     ClientData clientData;	/* Not used. */
677     Tk_Window tkwin;		/* Not used. */
678     char *widgRec;		/* Row/column structure record */
679     int offset;			/* Offset of resize in RowColumn record */
680     Tcl_FreeProc **freeProcPtr;	/* Not used. */
681 {
682     int resize = *(int *)(widgRec + offset);
683 
684     return NameOfResize(resize);
685 }
686 
687 /*
688  * ----------------------------------------------------------------------------
689  *
690  * StringToControl --
691  *
692  *	Converts the control string into its numeric representation.
693  *	Valid control strings are "none", "normal", and "full".
694  *
695  * ----------------------------------------------------------------------------
696  */
697 /*ARGSUSED*/
698 static int
StringToControl(clientData,interp,tkwin,string,widgRec,offset)699 StringToControl(clientData, interp, tkwin, string, widgRec, offset)
700     ClientData clientData;	/* Not used. */
701     Tcl_Interp *interp;		/* Interpreter to send results back to */
702     Tk_Window tkwin;		/* Not used. */
703     char *string;		/* Control style string */
704     char *widgRec;		/* Entry structure record */
705     int offset;			/* Offset of style in record */
706 {
707     double *controlPtr = (double *)(widgRec + offset);
708     unsigned int length;
709     int bool;
710     char c;
711 
712     c = string[0];
713     length = strlen(string);
714     if (Tcl_GetBoolean(NULL, string, &bool) == TCL_OK) {
715 	*controlPtr = bool;
716 	return TCL_OK;
717     }
718     if ((c == 'n') && (length > 1) &&
719 	(strncmp(string, "normal", length) == 0)) {
720 	*controlPtr = CONTROL_NORMAL;
721     } else if ((c == 'n') && (length > 1) &&
722 	(strncmp(string, "none", length) == 0)) {
723 	*controlPtr = CONTROL_NONE;
724     } else if ((c == 'f') && (strncmp(string, "full", length) == 0)) {
725 	*controlPtr = CONTROL_FULL;
726     } else {
727 	double control;
728 
729 	if ((Tcl_GetDouble(interp, string, &control) != TCL_OK) ||
730 	    (control < 0.0)) {
731 	    Tcl_AppendResult(interp, "bad control argument \"", string,
732 		"\": should be \"normal\", \"none\", or \"full\"",
733 		(char *)NULL);
734 	    return TCL_ERROR;
735 	}
736 	*controlPtr = control;
737     }
738     return TCL_OK;
739 }
740 
741 /*
742  * ----------------------------------------------------------------------------
743  *
744  * NameOfControl --
745  *
746  *	Converts the control value into its string representation.
747  *
748  * Results:
749  *	Returns a pointer to the static name string.
750  *
751  * ----------------------------------------------------------------------------
752  */
753 static char *
NameOfControl(control)754 NameOfControl(control)
755     double control;
756 {
757     if (control == CONTROL_NORMAL) {
758 	return "normal";
759     } else if (control == CONTROL_NONE) {
760 	return "none";
761     } else if (control == CONTROL_FULL) {
762 	return "full";
763     } else {
764 	static char string[TCL_DOUBLE_SPACE + 1];
765 
766 	sprintf(string, "%g", control);
767 	return string;
768     }
769 }
770 
771 /*
772  * ----------------------------------------------------------------------------
773  *
774  * ControlToString --
775  *
776  *	Returns control mode string based upon the control flags.
777  *
778  * Results:
779  *	The control mode string is returned.
780  *
781  * ----------------------------------------------------------------------------
782  */
783 /*ARGSUSED*/
784 static char *
ControlToString(clientData,tkwin,widgRec,offset,freeProcPtr)785 ControlToString(clientData, tkwin, widgRec, offset, freeProcPtr)
786     ClientData clientData;	/* Not used. */
787     Tk_Window tkwin;		/* Not used. */
788     char *widgRec;		/* Row/column structure record */
789     int offset;			/* Offset of control in RowColumn record */
790     Tcl_FreeProc **freeProcPtr;	/* Not used. */
791 {
792     double control = *(double *)(widgRec + offset);
793 
794     return NameOfControl(control);
795 }
796 
797 
798 static void
EventuallyArrangeTable(tablePtr)799 EventuallyArrangeTable(tablePtr)
800     Table *tablePtr;
801 {
802     if (!(tablePtr->flags & ARRANGE_PENDING)) {
803 	tablePtr->flags |= ARRANGE_PENDING;
804 	Tcl_DoWhenIdle(ArrangeTable, tablePtr);
805     }
806 }
807 
808 /*
809  * ----------------------------------------------------------------------------
810  *
811  * TableEventProc --
812  *
813  *	This procedure is invoked by the Tk event handler when the
814  *	container widget is reconfigured or destroyed.
815  *
816  *	The table will be rearranged at the next idle point if the
817  *	container widget has been resized or moved. There's a
818  *	distinction made between parent and non-parent container
819  *	arrangements.  If the container is moved and it's the parent
820  *	of the widgets, they're are moved automatically.  If it's
821  *	not the parent, those widgets need to be moved manually.
822  *	This can be a performance hit in rare cases where we're
823  *	scrolling the container (by moving the window) and there
824  *	are lots of non-child widgets arranged insided.
825  *
826  * Results:
827  *	None.
828  *
829  * Side effects:
830  *	Arranges for the table associated with tkwin to have its
831  *	layout re-computed and drawn at the next idle point.
832  *
833  * ----------------------------------------------------------------------------
834  */
835 static void
TableEventProc(clientData,eventPtr)836 TableEventProc(clientData, eventPtr)
837     ClientData clientData;	/* Information about widget */
838     XEvent *eventPtr;		/* Information about event */
839 {
840     register Table *tablePtr = clientData;
841 
842     if (eventPtr->type == ConfigureNotify) {
843 	if ((tablePtr->container.width != Tk_Width(tablePtr->tkwin)) ||
844 	    (tablePtr->container.height != Tk_Height(tablePtr->tkwin))
845 	    || (tablePtr->flags & NON_PARENT)) {
846 	    EventuallyArrangeTable(tablePtr);
847 	}
848     } else if (eventPtr->type == DestroyNotify) {
849 	if (tablePtr->flags & ARRANGE_PENDING) {
850 	    Tcl_CancelIdleCall(ArrangeTable, tablePtr);
851 	}
852 	tablePtr->tkwin = NULL;
853 	Tcl_EventuallyFree(tablePtr, DestroyTable);
854     }
855 }
856 
857 /*
858  * ----------------------------------------------------------------------------
859  *
860  * WidgetEventProc --
861  *
862  *	This procedure is invoked by the Tk event handler when
863  *	StructureNotify events occur in a widget managed by the table.
864  *	For example, when a managed widget is destroyed, it frees the
865  *	corresponding entry structure and arranges for the table
866  *	layout to be re-computed at the next idle point.
867  *
868  * Results:
869  *	None.
870  *
871  * Side effects:
872  *	If the managed widget was deleted, the Entry structure gets
873  *	cleaned up and the table is rearranged.
874  *
875  * ----------------------------------------------------------------------------
876  */
877 static void
WidgetEventProc(clientData,eventPtr)878 WidgetEventProc(clientData, eventPtr)
879     ClientData clientData;	/* Pointer to Entry structure for widget
880 				 * referred to by eventPtr. */
881     XEvent *eventPtr;		/* Describes what just happened. */
882 {
883     Entry *entryPtr = (Entry *) clientData;
884     Table *tablePtr = entryPtr->tablePtr;
885 
886     if (eventPtr->type == ConfigureNotify) {
887 	int borderWidth;
888 
889 	tablePtr->flags |= REQUEST_LAYOUT;
890 	borderWidth = Tk_Changes(entryPtr->tkwin)->border_width;
891 	if (entryPtr->borderWidth != borderWidth) {
892 	    entryPtr->borderWidth = borderWidth;
893 	    EventuallyArrangeTable(tablePtr);
894 	}
895     } else if (eventPtr->type == DestroyNotify) {
896 	entryPtr->tkwin = NULL;
897 	DestroyEntry(entryPtr);
898 	tablePtr->flags |= REQUEST_LAYOUT;
899 	EventuallyArrangeTable(tablePtr);
900     }
901 }
902 
903 /*
904  * ----------------------------------------------------------------------------
905  *
906  * WidgetCustodyProc --
907  *
908  * 	This procedure is invoked when a widget has been stolen by
909  * 	another geometry manager.  The information and memory
910  * 	associated with the widget is released.
911  *
912  * Results:
913  *	None.
914  *
915  * Side effects:
916   *	Arranges for the table to have its layout re-arranged at the
917  *	next idle point.
918  *
919  * ----------------------------------------------------------------------------
920  */
921 /* ARGSUSED */
922 static void
WidgetCustodyProc(clientData,tkwin)923 WidgetCustodyProc(clientData, tkwin)
924     ClientData clientData;	/* Information about the widget */
925     Tk_Window tkwin;		/* Not used. */
926 {
927     Entry *entryPtr = (Entry *) clientData;
928     Table *tablePtr = entryPtr->tablePtr;
929 
930     if (Tk_IsMapped(entryPtr->tkwin)) {
931 	Tk_UnmapWindow(entryPtr->tkwin);
932     }
933     Tk_UnmaintainGeometry(entryPtr->tkwin, tablePtr->tkwin);
934     entryPtr->tkwin = NULL;
935     DestroyEntry(entryPtr);
936     tablePtr->flags |= REQUEST_LAYOUT;
937     EventuallyArrangeTable(tablePtr);
938 }
939 
940 /*
941  * ----------------------------------------------------------------------------
942  *
943  * WidgetGeometryProc --
944  *
945  *	This procedure is invoked by Tk_GeometryRequest for widgets
946  *	managed by the table geometry manager.
947  *
948  * Results:
949  *	None.
950  *
951  * Side effects:
952  *	Arranges for the table to have its layout re-computed and
953  *	re-arranged at the next idle point.
954  *
955  * ---------------------------------------------------------------------------- */
956 /* ARGSUSED */
957 static void
WidgetGeometryProc(clientData,tkwin)958 WidgetGeometryProc(clientData, tkwin)
959     ClientData clientData;	/* Information about widget that got new
960 				 * preferred geometry.  */
961     Tk_Window tkwin;		/* Other Tk-related information about the
962 			         * widget. */
963 {
964     Entry *entryPtr = (Entry *) clientData;
965 
966     entryPtr->tablePtr->flags |= REQUEST_LAYOUT;
967     EventuallyArrangeTable(entryPtr->tablePtr);
968 }
969 
970 /*
971  * ----------------------------------------------------------------------------
972  *
973  * FindEntry --
974  *
975  *	Searches for the table entry corresponding to the given
976  *	widget.
977  *
978  * Results:
979  *	If a structure associated with the widget exists, a pointer to
980  *	that structure is returned. Otherwise NULL.
981  *
982  * ----------------------------------------------------------------------------
983  */
984 static Entry *
FindEntry(tablePtr,tkwin)985 FindEntry(tablePtr, tkwin)
986     Table *tablePtr;
987     Tk_Window tkwin;		/* Widget associated with table entry */
988 {
989     Blt_HashEntry *hPtr;
990 
991     hPtr = Blt_FindHashEntry(&(tablePtr->entryTable), (char *)tkwin);
992     if (hPtr == NULL) {
993 	return NULL;
994     }
995     return (Entry *) Blt_GetHashValue(hPtr);
996 }
997 
998 
999 static int
GetEntry(interp,tablePtr,string,entryPtrPtr)1000 GetEntry(interp, tablePtr, string, entryPtrPtr)
1001     Tcl_Interp *interp;
1002     Table *tablePtr;
1003     char *string;
1004     Entry **entryPtrPtr;
1005 {
1006     Tk_Window tkwin;
1007     Entry *entryPtr;
1008 
1009     tkwin = Tk_NameToWindow(interp, string, tablePtr->tkwin);
1010     if (tkwin == NULL) {
1011 	return TCL_ERROR;
1012     }
1013     entryPtr = FindEntry(tablePtr, tkwin);
1014     if (entryPtr == NULL) {
1015 	Tcl_AppendResult(interp, "\"", Tk_PathName(tkwin),
1016 	    "\" is not managed by any table", (char *)NULL);
1017 	return TCL_ERROR;
1018     }
1019     *entryPtrPtr = entryPtr;
1020     return TCL_OK;
1021 }
1022 
1023 /*
1024  * ----------------------------------------------------------------------------
1025  *
1026  * CreateEntry --
1027  *
1028  *	This procedure creates and initializes a new Entry structure
1029  *	to hold a widget.  A valid widget has a parent widget that is
1030  *	either a) the container widget itself or b) a mutual ancestor
1031  *	of the container widget.
1032  *
1033  * Results:
1034  *	Returns a pointer to the new structure describing the new
1035  *	widget entry.  If an error occurred, then the return
1036  *	value is NULL and an error message is left in interp->result.
1037  *
1038  * Side effects:
1039  *	Memory is allocated and initialized for the Entry structure.
1040  *
1041  * ---------------------------------------------------------------------------- */
1042 static Entry *
CreateEntry(tablePtr,tkwin)1043 CreateEntry(tablePtr, tkwin)
1044     Table *tablePtr;
1045     Tk_Window tkwin;
1046 {
1047     register Entry *entryPtr;
1048     int dummy;
1049     Tk_Window parent, ancestor;
1050 
1051     /*
1052      * Check that this widget can be managed by this table.  A valid
1053      * widget has a parent widget that either
1054      *
1055      *    1) is the container widget, or
1056      *    2) is a mutual ancestor of the container widget.
1057      */
1058     ancestor = Tk_Parent(tkwin);
1059     for (parent = tablePtr->tkwin; (parent != ancestor) &&
1060 	(!Tk_IsTopLevel(parent)); parent = Tk_Parent(parent)) {
1061 	/* empty */
1062     }
1063     if (ancestor != parent) {
1064 	Tcl_AppendResult(tablePtr->interp, "can't manage \"",
1065 	    Tk_PathName(tkwin), "\" in table \"", Tk_PathName(tablePtr->tkwin),
1066 	    "\"", (char *)NULL);
1067 	return NULL;
1068     }
1069     entryPtr = Blt_Calloc(1, sizeof(Entry));
1070     assert(entryPtr);
1071 
1072     /* Initialize the entry structure */
1073 
1074     entryPtr->tkwin = tkwin;
1075     entryPtr->tablePtr = tablePtr;
1076     entryPtr->borderWidth = Tk_Changes(tkwin)->border_width;
1077     entryPtr->fill = ENTRY_DEF_FILL;
1078     entryPtr->row.control = entryPtr->column.control = ENTRY_DEF_CONTROL;
1079     entryPtr->anchor = ENTRY_DEF_ANCHOR;
1080     entryPtr->row.span = entryPtr->column.span = ENTRY_DEF_SPAN;
1081     ResetLimits(&(entryPtr->reqWidth));
1082     ResetLimits(&(entryPtr->reqHeight));
1083 
1084     /*
1085      * Add the entry to the following data structures.
1086      *
1087      * 	1) A chain of widgets managed by the table.
1088      *   2) A hash table of widgets managed by the table.
1089      */
1090     entryPtr->linkPtr = Blt_ChainAppend(tablePtr->chainPtr, entryPtr);
1091     entryPtr->hashPtr = Blt_CreateHashEntry(&(tablePtr->entryTable),
1092 	(char *)tkwin, &dummy);
1093     Blt_SetHashValue(entryPtr->hashPtr, entryPtr);
1094 
1095     Tk_CreateEventHandler(tkwin, StructureNotifyMask, WidgetEventProc,
1096 	entryPtr);
1097     Tk_ManageGeometry(tkwin, &tableMgrInfo, (ClientData)entryPtr);
1098 
1099     return entryPtr;
1100 }
1101 
1102 /*
1103  * ----------------------------------------------------------------------------
1104  *
1105  * DestroyEntry --
1106  *
1107  *	Removes the Entry structure from the hash table and frees
1108  *	the memory allocated by it.  If the table is still in use
1109  *	(i.e. was not called from DestoryTable), remove its entries
1110  *	from the lists of row and column sorted partitions.
1111  *
1112  * Results:
1113  *	None.
1114  *
1115  * Side effects:
1116  *	Everything associated with the entry is freed up.
1117  *
1118  * ----------------------------------------------------------------------------
1119  */
1120 static void
DestroyEntry(entryPtr)1121 DestroyEntry(entryPtr)
1122     Entry *entryPtr;
1123 {
1124     Table *tablePtr = entryPtr->tablePtr;
1125 
1126     if (entryPtr->row.linkPtr != NULL) {
1127 	Blt_ChainDeleteLink(entryPtr->row.chainPtr, entryPtr->row.linkPtr);
1128     }
1129     if (entryPtr->column.linkPtr != NULL) {
1130 	Blt_ChainDeleteLink(entryPtr->column.chainPtr,
1131 	    entryPtr->column.linkPtr);
1132     }
1133     if (entryPtr->linkPtr != NULL) {
1134 	Blt_ChainDeleteLink(tablePtr->chainPtr, entryPtr->linkPtr);
1135     }
1136     if (entryPtr->tkwin != NULL) {
1137 	Tk_DeleteEventHandler(entryPtr->tkwin, StructureNotifyMask,
1138 	      WidgetEventProc, (ClientData)entryPtr);
1139 	Tk_ManageGeometry(entryPtr->tkwin, (Tk_GeomMgr *)NULL,
1140 			  (ClientData)entryPtr);
1141 	if ((tablePtr->tkwin != NULL) &&
1142 	    (Tk_Parent(entryPtr->tkwin) != tablePtr->tkwin)) {
1143 	    Tk_UnmaintainGeometry(entryPtr->tkwin, tablePtr->tkwin);
1144 	}
1145 	if (Tk_IsMapped(entryPtr->tkwin)) {
1146 	    Tk_UnmapWindow(entryPtr->tkwin);
1147 	}
1148     }
1149     if (entryPtr->hashPtr != NULL) {
1150 	Blt_DeleteHashEntry(&(tablePtr->entryTable), entryPtr->hashPtr);
1151     }
1152     Blt_Free(entryPtr);
1153 }
1154 
1155 /*
1156  * ----------------------------------------------------------------------------
1157  *
1158  * ConfigureEntry --
1159  *
1160  *	This procedure is called to process an argv/argc list, plus
1161  *	the Tk option database, in order to configure (or reconfigure)
1162  *	one or more entries.  Entries hold information about widgets
1163  *	managed by the table geometry manager.
1164  *
1165  * 	Note: You can query only one widget at a time.  But several
1166  * 	      can be reconfigured at once.
1167  *
1168  * Results:
1169  *	The return value is a standard Tcl result.  If TCL_ERROR is
1170  *	returned, then interp->result contains an error message.
1171  *
1172  * Side effects:
1173  *	The table layout is recomputed and rearranged at the next idle
1174  *	point.
1175  *
1176  * ----------------------------------------------------------------------------
1177  */
1178 static int
ConfigureEntry(tablePtr,interp,entryPtr,argc,argv)1179 ConfigureEntry(tablePtr, interp, entryPtr, argc, argv)
1180     Table *tablePtr;
1181     Tcl_Interp *interp;
1182     Entry *entryPtr;
1183     int argc;			/* Option-value arguments */
1184     char **argv;
1185 {
1186     int oldRowSpan, oldColSpan;
1187 
1188     if (entryPtr->tablePtr != tablePtr) {
1189 	Tcl_AppendResult(interp, "widget  \"", Tk_PathName(entryPtr->tkwin),
1190 	    "\" does not belong to table \"", Tk_PathName(tablePtr->tkwin),
1191 	    "\"", (char *)NULL);
1192 	return TCL_ERROR;
1193     }
1194     if (argc == 0) {
1195 	return Tk_ConfigureInfo(interp, entryPtr->tkwin, entryConfigSpecs,
1196 	    (char *)entryPtr, (char *)NULL, 0);
1197     } else if (argc == 1) {
1198 	return Tk_ConfigureInfo(interp, entryPtr->tkwin, entryConfigSpecs,
1199 	    (char *)entryPtr, argv[0], 0);
1200     }
1201     oldRowSpan = entryPtr->row.span;
1202     oldColSpan = entryPtr->column.span;
1203 
1204     if (Tk_ConfigureWidget(interp, entryPtr->tkwin, entryConfigSpecs,
1205 	    argc, argv, (char *)entryPtr, TK_CONFIG_ARGV_ONLY) != TCL_OK) {
1206 	return TCL_ERROR;
1207     }
1208     if ((entryPtr->column.span < 1) || (entryPtr->column.span > USHRT_MAX)) {
1209 	Tcl_AppendResult(interp, "bad column span specified for \"",
1210 	    Tk_PathName(entryPtr->tkwin), "\"", (char *)NULL);
1211 	return TCL_ERROR;
1212     }
1213     if ((entryPtr->row.span < 1) || (entryPtr->row.span > USHRT_MAX)) {
1214 	Tcl_AppendResult(interp, "bad row span specified for \"",
1215 	    Tk_PathName(entryPtr->tkwin), "\"", (char *)NULL);
1216 	return TCL_ERROR;
1217     }
1218     if ((oldColSpan != entryPtr->column.span) ||
1219 	(oldRowSpan != entryPtr->row.span)) {
1220 	BinEntry(tablePtr, entryPtr);
1221     }
1222     return TCL_OK;
1223 }
1224 
1225 /*
1226  * ----------------------------------------------------------------------------
1227  *
1228  * PrintEntry --
1229  *
1230  *	Returns the name, position and options of a widget in the table.
1231  *
1232  * Results:
1233  *	Returns a standard Tcl result.  A list of the widget
1234  *	attributes is left in interp->result.
1235  *
1236  * ----------------------------------------------------------------------------
1237  */
1238 /*ARGSUSED*/
1239 static void
PrintEntry(entryPtr,resultPtr)1240 PrintEntry(entryPtr, resultPtr)
1241     Entry *entryPtr;
1242     Tcl_DString *resultPtr;
1243 {
1244     char string[200];
1245 
1246     sprintf(string, "    %d,%d  ", entryPtr->row.rcPtr->index,
1247 	entryPtr->column.rcPtr->index);
1248     Tcl_DStringAppend(resultPtr, string, -1);
1249     Tcl_DStringAppend(resultPtr, Tk_PathName(entryPtr->tkwin), -1);
1250     if (entryPtr->ipadX != ENTRY_DEF_PAD) {
1251 	Tcl_DStringAppend(resultPtr, " -ipadx ", -1);
1252 	Tcl_DStringAppend(resultPtr, Blt_Itoa(entryPtr->ipadX), -1);
1253     }
1254     if (entryPtr->ipadY != ENTRY_DEF_PAD) {
1255 	Tcl_DStringAppend(resultPtr, " -ipady ", -1);
1256 	Tcl_DStringAppend(resultPtr, Blt_Itoa(entryPtr->ipadY), -1);
1257     }
1258     if (entryPtr->row.span != ENTRY_DEF_SPAN) {
1259 	Tcl_DStringAppend(resultPtr, " -rowspan ", -1);
1260 	Tcl_DStringAppend(resultPtr, Blt_Itoa(entryPtr->row.span), -1);
1261     }
1262     if (entryPtr->column.span != ENTRY_DEF_SPAN) {
1263 	Tcl_DStringAppend(resultPtr, " -columnspan ", -1);
1264 	Tcl_DStringAppend(resultPtr, Blt_Itoa(entryPtr->column.span), -1);
1265     }
1266     if (entryPtr->anchor != ENTRY_DEF_ANCHOR) {
1267 	Tcl_DStringAppend(resultPtr, " -anchor ", -1);
1268 	Tcl_DStringAppend(resultPtr, Tk_NameOfAnchor(entryPtr->anchor), -1);
1269     }
1270     if ((entryPtr->padLeft != ENTRY_DEF_PAD) ||
1271 	(entryPtr->padRight != ENTRY_DEF_PAD)) {
1272 	Tcl_DStringAppend(resultPtr, " -padx ", -1);
1273 	sprintf(string, "{%d %d}", entryPtr->padLeft, entryPtr->padRight);
1274 	Tcl_DStringAppend(resultPtr, string, -1);
1275     }
1276     if ((entryPtr->padTop != ENTRY_DEF_PAD) ||
1277 	(entryPtr->padBottom != ENTRY_DEF_PAD)) {
1278 	Tcl_DStringAppend(resultPtr, " -pady ", -1);
1279 	sprintf(string, "{%d %d}", entryPtr->padTop, entryPtr->padBottom);
1280 	Tcl_DStringAppend(resultPtr, string, -1);
1281     }
1282     if (entryPtr->fill != ENTRY_DEF_FILL) {
1283 	Tcl_DStringAppend(resultPtr, " -fill ", -1);
1284 	Tcl_DStringAppend(resultPtr, Blt_NameOfFill(entryPtr->fill), -1);
1285     }
1286     if (entryPtr->column.control != ENTRY_DEF_CONTROL) {
1287 	Tcl_DStringAppend(resultPtr, " -columncontrol ", -1);
1288 	Tcl_DStringAppend(resultPtr, NameOfControl(entryPtr->column.control), -1);
1289     }
1290     if (entryPtr->row.control != ENTRY_DEF_CONTROL) {
1291 	Tcl_DStringAppend(resultPtr, " -rowcontrol ", -1);
1292 	Tcl_DStringAppend(resultPtr, NameOfControl(entryPtr->row.control), -1);
1293     }
1294     if ((entryPtr->reqWidth.nom != LIMITS_NOM) ||
1295 	(entryPtr->reqWidth.min != LIMITS_MIN) ||
1296 	(entryPtr->reqWidth.max != LIMITS_MAX)) {
1297 	Tcl_DStringAppend(resultPtr, " -reqwidth {", -1);
1298 	Tcl_DStringAppend(resultPtr, NameOfLimits(&(entryPtr->reqWidth)), -1);
1299 	Tcl_DStringAppend(resultPtr, "}", -1);
1300     }
1301     if ((entryPtr->reqHeight.nom != LIMITS_NOM) ||
1302 	(entryPtr->reqHeight.min != LIMITS_MIN) ||
1303 	(entryPtr->reqHeight.max != LIMITS_MAX)) {
1304 	Tcl_DStringAppend(resultPtr, " -reqheight {", -1);
1305 	Tcl_DStringAppend(resultPtr, NameOfLimits(&(entryPtr->reqHeight)), -1);
1306 	Tcl_DStringAppend(resultPtr, "}", -1);
1307     }
1308 }
1309 
1310 /*
1311  * ----------------------------------------------------------------------------
1312  *
1313  * InfoEntry --
1314  *
1315  *	Returns the name, position and options of a widget in the table.
1316  *
1317  * Results:
1318  *	Returns a standard Tcl result.  A list of the widget
1319  *	attributes is left in interp->result.
1320  *
1321  * ----------------------------------------------------------------------------
1322  */
1323 /*ARGSUSED*/
1324 static int
InfoEntry(interp,tablePtr,entryPtr)1325 InfoEntry(interp, tablePtr, entryPtr)
1326     Tcl_Interp *interp;
1327     Table *tablePtr;
1328     Entry *entryPtr;
1329 {
1330     Tcl_DString dString;
1331 
1332     if (entryPtr->tablePtr != tablePtr) {
1333 	Tcl_AppendResult(interp, "widget  \"", Tk_PathName(entryPtr->tkwin),
1334 	    "\" does not belong to table \"", Tk_PathName(tablePtr->tkwin),
1335 	    "\"", (char *)NULL);
1336 	return TCL_ERROR;
1337     }
1338     Tcl_DStringInit(&dString);
1339     PrintEntry(entryPtr, &dString);
1340     Tcl_DStringResult(interp, &dString);
1341     return TCL_OK;
1342 }
1343 
1344 /*
1345  * ----------------------------------------------------------------------------
1346  *
1347  * CreateRowColumn --
1348  *
1349  *	Creates and initializes a structure that manages the size of a
1350  *	row or column in the table. There will be one of these
1351  *	structures allocated for each row and column in the table,
1352  *	regardless if a widget is contained in it or not.
1353  *
1354  * Results:
1355  *	Returns a pointer to the newly allocated row or column
1356  *	structure.
1357  *
1358  * ----------------------------------------------------------------------------
1359  */
1360 static RowColumn *
CreateRowColumn()1361 CreateRowColumn()
1362 {
1363     RowColumn *rcPtr;
1364 
1365     rcPtr = Blt_Malloc(sizeof(RowColumn));
1366     rcPtr->resize = ROWCOL_DEF_RESIZE;
1367     ResetLimits(&(rcPtr->reqSize));
1368     rcPtr->nomSize = LIMITS_NOM;
1369     rcPtr->pad.side1 = rcPtr->pad.side2 = ROWCOL_DEF_PAD;
1370     rcPtr->size = rcPtr->index = rcPtr->minSpan = 0;
1371     rcPtr->weight = ROWCOL_DEF_WEIGHT;
1372     return rcPtr;
1373 }
1374 
1375 static PartitionInfo *
ParseRowColumn2(tablePtr,string,numberPtr)1376 ParseRowColumn2(tablePtr, string, numberPtr)
1377     Table *tablePtr;
1378     char *string;
1379     int *numberPtr;
1380 {
1381     char c;
1382     int n;
1383     PartitionInfo *infoPtr;
1384 
1385     c = tolower(string[0]);
1386     if (c == 'c') {
1387 	infoPtr = &(tablePtr->columnInfo);
1388     } else if (c == 'r') {
1389 	infoPtr = &(tablePtr->rowInfo);
1390     } else {
1391 	Tcl_AppendResult(tablePtr->interp, "bad index \"", string,
1392 	    "\": must start with \"r\" or \"c\"", (char *)NULL);
1393 	return NULL;
1394     }
1395     /* Handle row or column configuration queries */
1396     if (Tcl_GetInt(tablePtr->interp, string + 1, &n) != TCL_OK) {
1397 	return NULL;
1398     }
1399     *numberPtr = (int)n;
1400     return infoPtr;
1401 }
1402 
1403 static PartitionInfo *
ParseRowColumn(tablePtr,string,numberPtr)1404 ParseRowColumn(tablePtr, string, numberPtr)
1405     Table *tablePtr;
1406     char *string;
1407     int *numberPtr;
1408 {
1409     int n;
1410     PartitionInfo *infoPtr;
1411 
1412     infoPtr = ParseRowColumn2(tablePtr, string, &n);
1413     if (infoPtr == NULL) {
1414 	return NULL;
1415     }
1416     if ((n < 0) || (n >= Blt_ChainGetLength(infoPtr->chainPtr))) {
1417 	Tcl_AppendResult(tablePtr->interp, "bad ", infoPtr->type, " index \"",
1418 	    string, "\"", (char *)NULL);
1419 	return NULL;
1420     }
1421     *numberPtr = (int)n;
1422     return infoPtr;
1423 }
1424 
1425 /*
1426  * ----------------------------------------------------------------------------
1427  *
1428  * GetRowColumn --
1429  *
1430  *	Gets the designated row or column from the table.  If the row
1431  *	or column index is greater than the size of the table, new
1432  *	rows/columns will be automatically allocated.
1433  *
1434  * Results:
1435  *	Returns a pointer to the row or column structure.
1436  *
1437  * ----------------------------------------------------------------------------
1438  */
1439 static RowColumn *
GetRowColumn(infoPtr,n)1440 GetRowColumn(infoPtr, n)
1441     PartitionInfo *infoPtr;
1442     int n;
1443 {
1444     Blt_ChainLink *linkPtr;
1445     RowColumn *rcPtr;
1446     register int i;
1447 
1448     for (i = Blt_ChainGetLength(infoPtr->chainPtr); i <= n; i++) {
1449 	rcPtr = CreateRowColumn();
1450 	rcPtr->index = i;
1451 	rcPtr->linkPtr = Blt_ChainAppend(infoPtr->chainPtr, (ClientData)rcPtr);
1452     }
1453     linkPtr = Blt_ChainGetNthLink(infoPtr->chainPtr, n);
1454     if (linkPtr == NULL) {
1455 	return NULL;
1456     }
1457     return Blt_ChainGetValue(linkPtr);
1458 }
1459 
1460 /*
1461  * ----------------------------------------------------------------------------
1462  *
1463  * DeleteRowColumn --
1464  *
1465  *	Deletes a span of rows/columns from the table. The number of
1466  *	rows/columns to be deleted is given by span.
1467  *
1468  * Results:
1469  *	None.
1470  *
1471  * Side effects:
1472  *	The size of the column partition array may be extended and
1473  *	initialized.
1474  *
1475  * ----------------------------------------------------------------------------
1476  */
1477 static void
DeleteRowColumn(tablePtr,infoPtr,rcPtr)1478 DeleteRowColumn(tablePtr, infoPtr, rcPtr)
1479     Table *tablePtr;
1480     PartitionInfo *infoPtr;
1481     RowColumn *rcPtr;
1482 {
1483     Blt_ChainLink *linkPtr, *nextPtr;
1484     Entry *entryPtr;
1485 
1486     /*
1487      * Remove any entries that start in the row/column to be deleted.
1488      * They point to memory that will be freed.
1489      */
1490     if (infoPtr->type == rowUid) {
1491 	for (linkPtr = Blt_ChainFirstLink(tablePtr->chainPtr); linkPtr != NULL;
1492 	    linkPtr = nextPtr) {
1493 	    nextPtr = Blt_ChainNextLink(linkPtr);
1494 	    entryPtr = Blt_ChainGetValue(linkPtr);
1495 	    if (entryPtr->row.rcPtr->index == rcPtr->index) {
1496 		DestroyEntry(entryPtr);
1497 	    }
1498 	}
1499     } else {
1500 	for (linkPtr = Blt_ChainFirstLink(tablePtr->chainPtr); linkPtr != NULL;
1501 	    linkPtr = nextPtr) {
1502 	    nextPtr = Blt_ChainNextLink(linkPtr);
1503 	    entryPtr = Blt_ChainGetValue(linkPtr);
1504 	    if (entryPtr->column.rcPtr->index == rcPtr->index) {
1505 		DestroyEntry(entryPtr);
1506 	    }
1507 	}
1508     }
1509 }
1510 
1511 /*
1512  * ----------------------------------------------------------------------------
1513  *
1514  * ConfigureRowColumn --
1515  *
1516  *	This procedure is called to process an argv/argc list in order
1517  *	to configure a row or column in the table geometry manager.
1518  *
1519  * Results:
1520  *	The return value is a standard Tcl result.  If TCL_ERROR is
1521  *	returned, then interp->result holds an error message.
1522  *
1523  * Side effects:
1524  *	Partition configuration options (bounds, resize flags, etc)
1525  *	get set.  New partitions may be created as necessary. The
1526  *	table is recalculated and arranged at the next idle point.
1527  *
1528  * ----------------------------------------------------------------------------
1529  */
1530 static int
ConfigureRowColumn(tablePtr,infoPtr,pattern,argc,argv)1531 ConfigureRowColumn(tablePtr, infoPtr, pattern, argc, argv)
1532     Table *tablePtr;		/* Table to be configured */
1533     PartitionInfo *infoPtr;
1534     char *pattern;
1535     int argc;
1536     char **argv;
1537 {
1538     RowColumn *rcPtr;
1539     register Blt_ChainLink *linkPtr;
1540     char string[200];
1541     int nMatches;
1542 
1543     nMatches = 0;
1544     for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr); linkPtr != NULL;
1545 	linkPtr = Blt_ChainNextLink(linkPtr)) {
1546 	rcPtr = Blt_ChainGetValue(linkPtr);
1547 	sprintf(string, "%c%d", pattern[0], rcPtr->index);
1548 	if (Tcl_StringMatch(string, pattern)) {
1549 	    if (argc == 0) {
1550 		return Tk_ConfigureInfo(tablePtr->interp, tablePtr->tkwin,
1551 		    infoPtr->configSpecs, (char *)rcPtr, NULL, 0);
1552 	    } else if (argc == 1) {
1553 		return Tk_ConfigureInfo(tablePtr->interp, tablePtr->tkwin,
1554 		    infoPtr->configSpecs, (char *)rcPtr, argv[0], 0);
1555 	    } else {
1556 		if (Tk_ConfigureWidget(tablePtr->interp, tablePtr->tkwin,
1557 			infoPtr->configSpecs, argc, argv, (char *)rcPtr,
1558 			TK_CONFIG_ARGV_ONLY) != TCL_OK) {
1559 		    return TCL_ERROR;
1560 		}
1561 	    }
1562 	    nMatches++;
1563 	}
1564     }
1565     if (nMatches == 0) {
1566 	int n;
1567 
1568 	/*
1569 	 * We found no existing partitions matching this pattern, so
1570 	 * see if this designates an new partition (one beyond the
1571 	 * current range).
1572 	 */
1573 	if ((Tcl_GetInt(NULL, pattern + 1, &n) != TCL_OK) || (n < 0)) {
1574 	    Tcl_AppendResult(tablePtr->interp, "pattern \"", pattern,
1575 		     "\" matches no ", infoPtr->type, " in table \"",
1576 		     Tk_PathName(tablePtr->tkwin), "\"", (char *)NULL);
1577 	    return TCL_ERROR;
1578 	}
1579 	rcPtr = GetRowColumn(infoPtr, n);
1580 	assert(rcPtr);
1581 	if (Tk_ConfigureWidget(tablePtr->interp, tablePtr->tkwin,
1582 	       infoPtr->configSpecs, argc, argv, (char *)rcPtr,
1583 	       TK_CONFIG_ARGV_ONLY) != TCL_OK) {
1584 	    return TCL_ERROR;
1585 	}
1586     }
1587     EventuallyArrangeTable(tablePtr);
1588     return TCL_OK;
1589 }
1590 
1591 static void
PrintRowColumn(interp,infoPtr,rcPtr,resultPtr)1592 PrintRowColumn(interp, infoPtr, rcPtr, resultPtr)
1593     Tcl_Interp *interp;
1594     PartitionInfo *infoPtr;
1595     RowColumn *rcPtr;
1596     Tcl_DString *resultPtr;
1597 {
1598     char string[200];
1599     char *padFmt, *sizeFmt;
1600 
1601     if (infoPtr->type == rowUid) {
1602 	padFmt = " -pady {%d %d}";
1603 	sizeFmt = " -height {%s}";
1604     } else {
1605 	padFmt = " -padx {%d %d}";
1606 	sizeFmt = " -width {%s}";
1607     }
1608     if (rcPtr->resize != ROWCOL_DEF_RESIZE) {
1609 	Tcl_DStringAppend(resultPtr, " -resize ", -1);
1610 	Tcl_DStringAppend(resultPtr, NameOfResize(rcPtr->resize), -1);
1611     }
1612     if ((rcPtr->pad.side1 != ROWCOL_DEF_PAD) ||
1613 	(rcPtr->pad.side2 != ROWCOL_DEF_PAD)) {
1614 	sprintf(string, padFmt, rcPtr->pad.side1, rcPtr->pad.side2);
1615 	Tcl_DStringAppend(resultPtr, string, -1);
1616     }
1617     if (rcPtr->weight != ROWCOL_DEF_WEIGHT) {
1618 	Tcl_DStringAppend(resultPtr, " -weight ", -1);
1619 	Tcl_DStringAppend(resultPtr, Blt_Dtoa(interp, rcPtr->weight), -1);
1620     }
1621     if ((rcPtr->reqSize.min != LIMITS_MIN) ||
1622 	(rcPtr->reqSize.nom != LIMITS_NOM) ||
1623 	(rcPtr->reqSize.max != LIMITS_MAX)) {
1624 	sprintf(string, sizeFmt, NameOfLimits(&(rcPtr->reqSize)));
1625 	Tcl_DStringAppend(resultPtr, string, -1);
1626     }
1627 }
1628 
1629 /*
1630  * ----------------------------------------------------------------------------
1631  *
1632  * InfoRowColumn --
1633  *
1634  *	Returns the options of a partition in the table.
1635  *
1636  * Results:
1637  *	Returns a standard Tcl result.  A list of the partition
1638  *	attributes is left in interp->result.
1639  *
1640  * ----------------------------------------------------------------------------
1641  */
1642 /*ARGSUSED*/
1643 static int
InfoRowColumn(tablePtr,interp,pattern)1644 InfoRowColumn(tablePtr, interp, pattern)
1645     Table *tablePtr;
1646     Tcl_Interp *interp;
1647     char *pattern;
1648 {
1649     RowColumn *rcPtr;
1650     char string[200];
1651     PartitionInfo *infoPtr;
1652     char c;
1653     Blt_ChainLink *linkPtr, *lastPtr;
1654     Tcl_DString dString;
1655 
1656     c = pattern[0];
1657     if ((c == 'r') || (c == 'R')) {
1658 	infoPtr = &(tablePtr->rowInfo);
1659     } else {
1660 	infoPtr = &(tablePtr->columnInfo);
1661     }
1662     Tcl_DStringInit(&dString);
1663     lastPtr = Blt_ChainLastLink(infoPtr->chainPtr);
1664     for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr); linkPtr != NULL;
1665 	linkPtr = Blt_ChainNextLink(linkPtr)) {
1666 	rcPtr = Blt_ChainGetValue(linkPtr);
1667 	sprintf(string, "%c%d", infoPtr->type[0], rcPtr->index);
1668 	if (Tcl_StringMatch(string, pattern)) {
1669 	    Tcl_DStringAppend(&dString, string, -1);
1670 	    PrintRowColumn(interp, infoPtr, rcPtr, &dString);
1671 	    if (linkPtr != lastPtr) {
1672 		Tcl_DStringAppend(&dString, " \\\n", -1);
1673 	    } else {
1674 		Tcl_DStringAppend(&dString, "\n", -1);
1675 	    }
1676 	}
1677     }
1678     Tcl_DStringResult(interp, &dString);
1679     return TCL_OK;
1680 }
1681 
1682 /*
1683  * ----------------------------------------------------------------------------
1684  *
1685  * InitSpan --
1686  *
1687  *	Checks the size of the column partitions and extends the size
1688  *	if a larger array is needed.
1689  *
1690  * Results:
1691  *	Returns 1 if the column exists.  Otherwise 0 is returned and
1692  *	interp->result contains an error message.
1693  *
1694  * Side effects:
1695  *	The size of the column partition array may be extended and
1696  *	initialized.
1697  *
1698  * ----------------------------------------------------------------------------
1699  */
1700 static RowColumn *
InitSpan(infoPtr,start,span)1701 InitSpan(infoPtr, start, span)
1702     PartitionInfo *infoPtr;
1703     int start, span;
1704 {
1705     int length;
1706     RowColumn *rcPtr;
1707     register int i;
1708     Blt_ChainLink *linkPtr;
1709 
1710     length = Blt_ChainGetLength(infoPtr->chainPtr);
1711     for (i = length; i < (start + span); i++) {
1712 	rcPtr = CreateRowColumn();
1713 	rcPtr->index = i;
1714 	rcPtr->linkPtr = Blt_ChainAppend(infoPtr->chainPtr, (ClientData)rcPtr);
1715     }
1716     linkPtr = Blt_ChainGetNthLink(infoPtr->chainPtr, start);
1717     return Blt_ChainGetValue(linkPtr);
1718 }
1719 
1720 /*
1721  * ----------------------------------------------------------------------------
1722  *
1723  * Blt_GetTable --
1724  *
1725  *	Searches for a table associated by the path name of the widget
1726  *	container.
1727  *
1728  *	Errors may occur because
1729  *	  1) pathName isn't a valid for any Tk widget, or
1730  *	  2) there's no table associated with that widget as a container.
1731  *
1732  * Results:
1733  *	If a table entry exists, a pointer to the Table structure is
1734  *	returned. Otherwise NULL is returned.
1735  *
1736  * ----------------------------------------------------------------------------
1737  */
1738 /*LINTLIBRARY*/
1739 int
Blt_GetTable(dataPtr,interp,pathName,tablePtrPtr)1740 Blt_GetTable(dataPtr, interp, pathName, tablePtrPtr)
1741     TableInterpData *dataPtr;	/* Interpreter-specific data. */
1742     Tcl_Interp *interp;		/* Interpreter to report errors back to. */
1743     char *pathName;		/* Path name of the container widget. */
1744     Table **tablePtrPtr;
1745 {
1746     Blt_HashEntry *hPtr;
1747     Tk_Window tkwin;
1748 
1749     tkwin = Tk_NameToWindow(interp, pathName, Tk_MainWindow(interp));
1750     if (tkwin == NULL) {
1751 	return TCL_ERROR;
1752     }
1753     hPtr = Blt_FindHashEntry(&(dataPtr->tableTable), (char *)tkwin);
1754     if (hPtr == NULL) {
1755 	Tcl_AppendResult(interp, "no table associated with widget \"",
1756 	    pathName, "\"", (char *)NULL);
1757 	return TCL_ERROR;
1758     }
1759     *tablePtrPtr = (Table *)Blt_GetHashValue(hPtr);
1760     return TCL_OK;
1761 }
1762 
1763 /*
1764  * ----------------------------------------------------------------------------
1765  *
1766  * CreateTable --
1767  *
1768  *	This procedure creates and initializes a new Table structure
1769  *	with tkwin as its container widget. The internal structures
1770  *	associated with the table are initialized.
1771  *
1772  * Results:
1773  *	Returns the pointer to the new Table structure describing the
1774  *	new table geometry manager.  If an error occurred, the return
1775  *	value will be NULL and an error message is left in
1776  *	interp->result.
1777  *
1778  * Side effects:
1779  *	Memory is allocated and initialized, an event handler is set
1780  *	up to watch tkwin, etc.
1781  *
1782  * ----------------------------------------------------------------------------
1783  */
1784 static Table *
CreateTable(dataPtr,interp,pathName)1785 CreateTable(dataPtr, interp, pathName)
1786     TableInterpData *dataPtr;
1787     Tcl_Interp *interp;		/* Interpreter associated with table. */
1788     char *pathName;		/* Path name of the container widget to be
1789 				 * associated with the new table. */
1790 {
1791     register Table *tablePtr;
1792     Tk_Window tkwin;
1793     int dummy;
1794     Blt_HashEntry *hPtr;
1795 
1796     tkwin = Tk_NameToWindow(interp, pathName, Tk_MainWindow(interp));
1797     if (tkwin == NULL) {
1798 	return NULL;
1799     }
1800     tablePtr = Blt_Calloc(1, sizeof(Table));
1801     assert(tablePtr);
1802     tablePtr->tkwin = tkwin;
1803     tablePtr->interp = interp;
1804     tablePtr->rowInfo.type = rowUid;
1805     tablePtr->rowInfo.configSpecs = rowConfigSpecs;
1806     tablePtr->rowInfo.chainPtr = Blt_ChainCreate();
1807     tablePtr->columnInfo.type = columnUid;
1808     tablePtr->columnInfo.configSpecs = columnConfigSpecs;
1809     tablePtr->columnInfo.chainPtr = Blt_ChainCreate();
1810     tablePtr->propagate = TRUE;
1811 
1812     tablePtr->arrangeProc = ArrangeTable;
1813     Blt_InitHashTable(&(tablePtr->entryTable), BLT_ONE_WORD_KEYS);
1814     tablePtr->findEntryProc = FindEntry;
1815 
1816     ResetLimits(&(tablePtr->reqWidth));
1817     ResetLimits(&(tablePtr->reqHeight));
1818 
1819     tablePtr->chainPtr = Blt_ChainCreate();
1820     tablePtr->rowInfo.list = Blt_ListCreate(BLT_ONE_WORD_KEYS);
1821     tablePtr->columnInfo.list = Blt_ListCreate(BLT_ONE_WORD_KEYS);
1822 
1823     Tk_CreateEventHandler(tablePtr->tkwin, StructureNotifyMask,
1824 	TableEventProc, (ClientData)tablePtr);
1825     hPtr = Blt_CreateHashEntry(&(dataPtr->tableTable), (char *)tkwin, &dummy);
1826     tablePtr->hashPtr = hPtr;
1827     tablePtr->tablePtr = &(dataPtr->tableTable);
1828     Blt_SetHashValue(hPtr, (ClientData)tablePtr);
1829     return tablePtr;
1830 }
1831 
1832 /*
1833  * ----------------------------------------------------------------------------
1834  *
1835  * ConfigureTable --
1836  *
1837  *	This procedure is called to process an argv/argc list in order
1838  *	to configure the table geometry manager.
1839  *
1840  * Results:
1841  *	The return value is a standard Tcl result.  If TCL_ERROR is
1842  *	returned, then interp->result contains an error message.
1843  *
1844  * Side effects:
1845  *	Table configuration options (-padx, -pady, etc.) get set.  The
1846  *	table is recalculated and arranged at the next idle point.
1847  *
1848  * ----------------------------------------------------------------------------
1849  */
1850 static int
ConfigureTable(tablePtr,interp,argc,argv)1851 ConfigureTable(tablePtr, interp, argc, argv)
1852     Table *tablePtr;		/* Table to be configured */
1853     Tcl_Interp *interp;		/* Interpreter to report results back to */
1854     int argc;
1855     char **argv;		/* Option-value pairs */
1856 {
1857     if (argc == 0) {
1858 	return Tk_ConfigureInfo(interp, tablePtr->tkwin, tableConfigSpecs,
1859 	    (char *)tablePtr, (char *)NULL, 0);
1860     } else if (argc == 1) {
1861 	return Tk_ConfigureInfo(interp, tablePtr->tkwin, tableConfigSpecs,
1862 	    (char *)tablePtr, argv[0], 0);
1863     }
1864     if (Tk_ConfigureWidget(interp, tablePtr->tkwin, tableConfigSpecs,
1865 	    argc, argv, (char *)tablePtr, TK_CONFIG_ARGV_ONLY) != TCL_OK) {
1866 	return TCL_ERROR;
1867     }
1868     /* Arrange for the table layout to be computed at the next idle point. */
1869     tablePtr->flags |= REQUEST_LAYOUT;
1870     EventuallyArrangeTable(tablePtr);
1871     return TCL_OK;
1872 }
1873 
1874 static void
PrintTable(tablePtr,resultPtr)1875 PrintTable(tablePtr, resultPtr)
1876     Table *tablePtr;
1877     Tcl_DString *resultPtr;
1878 {
1879     char string[200];
1880 
1881     if ((tablePtr->padLeft != TABLE_DEF_PAD) ||
1882 	(tablePtr->padRight != TABLE_DEF_PAD)) {
1883 	sprintf(string, " -padx {%d %d}", tablePtr->padLeft, tablePtr->padRight);
1884 	Tcl_DStringAppend(resultPtr, string, -1);
1885     }
1886     if ((tablePtr->padTop != TABLE_DEF_PAD) ||
1887 	(tablePtr->padBottom != TABLE_DEF_PAD)) {
1888 	sprintf(string, " -pady {%d %d}", tablePtr->padTop, tablePtr->padBottom);
1889 	Tcl_DStringAppend(resultPtr, string, -1);
1890     }
1891     if (!tablePtr->propagate) {
1892 	Tcl_DStringAppend(resultPtr, " -propagate no", -1);
1893     }
1894     if ((tablePtr->reqWidth.min != LIMITS_MIN) ||
1895 	(tablePtr->reqWidth.nom != LIMITS_NOM) ||
1896 	(tablePtr->reqWidth.max != LIMITS_MAX)) {
1897 	Tcl_DStringAppend(resultPtr, " -reqwidth {%s}", -1);
1898 	Tcl_DStringAppend(resultPtr, NameOfLimits(&(tablePtr->reqWidth)), -1);
1899     }
1900     if ((tablePtr->reqHeight.min != LIMITS_MIN) ||
1901 	(tablePtr->reqHeight.nom != LIMITS_NOM) ||
1902 	(tablePtr->reqHeight.max != LIMITS_MAX)) {
1903 	Tcl_DStringAppend(resultPtr, " -reqheight {%s}", -1);
1904 	Tcl_DStringAppend(resultPtr, NameOfLimits(&(tablePtr->reqHeight)), -1);
1905     }
1906 }
1907 
1908 /*
1909  * ----------------------------------------------------------------------------
1910  *
1911  * DestroyPartitions --
1912  *
1913  *	Clear each of the lists managing the entries.  The entries in
1914  *	the lists of row and column spans are themselves lists which
1915  *	need to be cleared.
1916  *
1917  * ----------------------------------------------------------------------------
1918  */
1919 static void
DestroyPartitions(infoPtr)1920 DestroyPartitions(infoPtr)
1921     PartitionInfo *infoPtr;
1922 {
1923     if (infoPtr->list != NULL) {
1924 	Blt_Chain *chainPtr;
1925 	Blt_ListNode node;
1926 
1927 	for (node = Blt_ListFirstNode(infoPtr->list); node != NULL;
1928 	    node = Blt_ListNextNode(node)) {
1929 	    chainPtr = (Blt_Chain *)Blt_ListGetValue(node);
1930 	    if (chainPtr != NULL) {
1931 		Blt_ChainDestroy(chainPtr);
1932 	    }
1933 	}
1934 	Blt_ListDestroy(infoPtr->list);
1935     }
1936     if (infoPtr->chainPtr != NULL) {
1937 	Blt_ChainLink *linkPtr;
1938 	RowColumn *rcPtr;
1939 
1940 	for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr);
1941 	    linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
1942 	    rcPtr = Blt_ChainGetValue(linkPtr);
1943 	    Blt_Free(rcPtr);
1944 	}
1945 	Blt_ChainDestroy(infoPtr->chainPtr);
1946     }
1947 }
1948 
1949 /*
1950  * ----------------------------------------------------------------------------
1951  *
1952  * DestroyTable --
1953  *
1954  *	This procedure is invoked by Tcl_EventuallyFree or Tcl_Release to
1955  *	clean up the Table structure at a safe time (when no-one is using
1956  *	it anymore).
1957  *
1958  * Results:
1959  *	None.
1960  *
1961  * Side effects:
1962  *	Everything associated with the table geometry manager is freed up.
1963  *
1964  * ----------------------------------------------------------------------------
1965  */
1966 static void
DestroyTable(dataPtr)1967 DestroyTable(dataPtr)
1968     DestroyData dataPtr;	/* Table structure */
1969 {
1970     Blt_ChainLink *linkPtr;
1971     Entry *entryPtr;
1972     Table *tablePtr = (Table *)dataPtr;
1973 
1974     /* Release the chain of entries. */
1975     for (linkPtr = Blt_ChainFirstLink(tablePtr->chainPtr); linkPtr != NULL;
1976 	linkPtr = Blt_ChainNextLink(linkPtr)) {
1977 	entryPtr = Blt_ChainGetValue(linkPtr);
1978 	entryPtr->linkPtr = NULL; /* Don't disrupt this chain of entries */
1979 	DestroyEntry(entryPtr);
1980     }
1981     Blt_ChainDestroy(tablePtr->chainPtr);
1982 
1983     DestroyPartitions(&(tablePtr->rowInfo));
1984     DestroyPartitions(&(tablePtr->columnInfo));
1985     Blt_DeleteHashTable(&(tablePtr->entryTable));
1986     if (tablePtr->hashPtr != NULL) {
1987 	Blt_DeleteHashEntry(tablePtr->tablePtr, tablePtr->hashPtr);
1988     }
1989     Blt_Free(tablePtr);
1990 }
1991 
1992 /*
1993  * ----------------------------------------------------------------------------
1994  *
1995  * BinEntry --
1996  *
1997  *	Adds the entry to the lists of both row and column spans.  The
1998  *	layout of the table is done in order of partition spans, from
1999  *	shorted to longest.  The widgets spanning a particular number of
2000  *	partitions are stored in a linked list.  Each list is in turn,
2001  *	contained within a master list.
2002  *
2003  * Results:
2004  *	None.
2005  *
2006  * Side effects:
2007  *	The entry is added to both the lists of row and columns spans.
2008  *	This will effect the layout of the widgets.
2009  *
2010  * ----------------------------------------------------------------------------
2011  */
2012 static void
BinEntry(tablePtr,entryPtr)2013 BinEntry(tablePtr, entryPtr)
2014     Table *tablePtr;
2015     Entry *entryPtr;
2016 {
2017     Blt_ListNode node;
2018     Blt_List list;
2019     Blt_Chain *chainPtr;
2020     int key;
2021 
2022     /*
2023      * Remove the entry from both row and column lists.  It will be
2024      * re-inserted into the table at the new position.
2025      */
2026     if (entryPtr->column.linkPtr != NULL) {
2027 	Blt_ChainUnlinkLink(entryPtr->column.chainPtr,
2028 	    entryPtr->column.linkPtr);
2029     }
2030     if (entryPtr->row.linkPtr != NULL) {
2031 	Blt_ChainUnlinkLink(entryPtr->row.chainPtr, entryPtr->row.linkPtr);
2032     }
2033     list = tablePtr->rowInfo.list;
2034     key = 0;			/* Initialize key to bogus span */
2035     for (node = Blt_ListFirstNode(list); node != NULL;
2036 	node = Blt_ListNextNode(node)) {
2037 	key = (int)Blt_ListGetKey(node);
2038 	if (entryPtr->row.span <= key) {
2039 	    break;
2040 	}
2041     }
2042     if (key != entryPtr->row.span) {
2043 	Blt_ListNode newNode;
2044 
2045 	/*
2046 	 * Create a new list (bucket) to hold entries of that size
2047 	 * span and and link it into the list of buckets.
2048 	 */
2049 	newNode = Blt_ListCreateNode(list, (char *)entryPtr->row.span);
2050 	Blt_ListSetValue(newNode, (char *)Blt_ChainCreate());
2051 	Blt_ListLinkBefore(list, newNode, node);
2052 	node = newNode;
2053     }
2054     chainPtr = (Blt_Chain *) Blt_ListGetValue(node);
2055     if (entryPtr->row.linkPtr == NULL) {
2056 	entryPtr->row.linkPtr = Blt_ChainAppend(chainPtr, entryPtr);
2057     } else {
2058 	Blt_ChainLinkBefore(chainPtr, entryPtr->row.linkPtr, NULL);
2059     }
2060     entryPtr->row.chainPtr = chainPtr;
2061 
2062     list = tablePtr->columnInfo.list;
2063     key = 0;
2064     for (node = Blt_ListFirstNode(list); node != NULL;
2065 	node = Blt_ListNextNode(node)) {
2066 	key = (int)Blt_ListGetKey(node);
2067 	if (entryPtr->column.span <= key) {
2068 	    break;
2069 	}
2070     }
2071     if (key != entryPtr->column.span) {
2072 	Blt_ListNode newNode;
2073 
2074 	/*
2075 	 * Create a new list (bucket) to hold entries of that size
2076 	 * span and and link it into the list of buckets.
2077 	 */
2078 	newNode = Blt_ListCreateNode(list, (char *)entryPtr->column.span);
2079 	Blt_ListSetValue(newNode, (char *)Blt_ChainCreate());
2080 	Blt_ListLinkBefore(list, newNode, node);
2081 	node = newNode;
2082     }
2083     chainPtr = (Blt_Chain *) Blt_ListGetValue(node);
2084 
2085     /* Add the new entry to the span bucket */
2086     if (entryPtr->column.linkPtr == NULL) {
2087 	entryPtr->column.linkPtr =
2088 	    Blt_ChainAppend(chainPtr, entryPtr);
2089     } else {
2090 	Blt_ChainLinkBefore(chainPtr, entryPtr->column.linkPtr, NULL);
2091     }
2092     entryPtr->column.chainPtr = chainPtr;
2093 }
2094 
2095 /*
2096  * ----------------------------------------------------------------------------
2097  *
2098  * ParseIndex --
2099  *
2100  *	Parse the entry index string and return the row and column
2101  *	numbers in their respective parameters.  The format of a table
2102  *	entry index is row,column where row is the row number and
2103  *	column is the column number.  Rows and columns are numbered
2104  *	starting from zero.
2105  *
2106  * Results:
2107  *	Returns a standard Tcl result.  If TCL_OK is returned, the row
2108  *	and column numbers are returned via rowPtr and columnPtr
2109  *	respectively.
2110  *
2111  * ----------------------------------------------------------------------------
2112  */
2113 static int
ParseIndex(interp,string,rowPtr,columnPtr)2114 ParseIndex(interp, string, rowPtr, columnPtr)
2115     Tcl_Interp *interp;
2116     char *string;
2117     int *rowPtr, *columnPtr;
2118 {
2119     char *comma;
2120     long row, column;
2121     int result;
2122 
2123     comma = strchr(string, ',');
2124     if (comma == NULL) {
2125 	Tcl_AppendResult(interp, "bad index \"", string,
2126 	    "\": should be \"row,column\"", (char *)NULL);
2127 	return TCL_ERROR;
2128 
2129     }
2130     *comma = '\0';
2131     result = ((Tcl_ExprLong(interp, string, &row) != TCL_OK) ||
2132 	(Tcl_ExprLong(interp, comma + 1, &column) != TCL_OK));
2133     *comma = ',';		/* Repair the argument */
2134     if (result) {
2135 	return TCL_ERROR;
2136     }
2137     if ((row < 0) || (row > (long)USHRT_MAX)) {
2138 	Tcl_AppendResult(interp, "bad index \"", string,
2139 	    "\": row is out of range", (char *)NULL);
2140 	return TCL_ERROR;
2141 
2142     }
2143     if ((column < 0) || (column > (long)USHRT_MAX)) {
2144 	Tcl_AppendResult(interp, "bad index \"", string,
2145 	    "\": column is out of range", (char *)NULL);
2146 	return TCL_ERROR;
2147     }
2148     *rowPtr = (int)row;
2149     *columnPtr = (int)column;
2150     return TCL_OK;
2151 }
2152 
2153 /*
2154  * ----------------------------------------------------------------------------
2155  *
2156  * ManageEntry --
2157  *
2158  *	Inserts the given widget into the table at a given row and
2159  *	column position.  The widget can already be managed by this or
2160  *	another table.  The widget will be simply moved to the new
2161  *	location in this table.
2162  *
2163  *	The new widget is inserted into both a hash table (this is
2164  *	used to locate the information associated with the widget) and
2165  *	a list (used to indicate relative ordering of widgets).
2166  *
2167  * Results:
2168  *	Returns a standard Tcl result.  If an error occurred, TCL_ERROR is
2169  *	returned and an error message is left in interp->result.
2170  *
2171  * Side Effects:
2172  *	The table is re-computed and arranged at the next idle point.
2173  *
2174  * ---------------------------------------------------------------------------- */
2175 static int
ManageEntry(interp,tablePtr,tkwin,row,column,argc,argv)2176 ManageEntry(interp, tablePtr, tkwin, row, column, argc, argv)
2177     Tcl_Interp *interp;
2178     Table *tablePtr;
2179     Tk_Window tkwin;
2180     int row, column;
2181     int argc;
2182     char **argv;
2183 {
2184     Entry *entryPtr;
2185     int result = TCL_OK;
2186 
2187     entryPtr = FindEntry(tablePtr, tkwin);
2188     if ((entryPtr != NULL) && (entryPtr->tablePtr != tablePtr)) {
2189 	/* The entry for the widget already exists. If it's
2190 	 * managed by another table, delete it.  */
2191 	DestroyEntry(entryPtr);
2192 	entryPtr = NULL;
2193     }
2194     if (entryPtr == NULL) {
2195 	entryPtr = CreateEntry(tablePtr, tkwin);
2196 	if (entryPtr == NULL) {
2197 	    return TCL_ERROR;
2198 	}
2199     }
2200     if (argc > 0) {
2201 	result = Tk_ConfigureWidget(tablePtr->interp, entryPtr->tkwin,
2202 	    entryConfigSpecs, argc, argv, (char *)entryPtr,
2203 	    TK_CONFIG_ARGV_ONLY);
2204     }
2205     if ((entryPtr->column.span < 1) || (entryPtr->row.span < 1)) {
2206 	Tcl_AppendResult(tablePtr->interp, "bad span specified for \"",
2207 	    Tk_PathName(tkwin), "\"", (char *)NULL);
2208 	DestroyEntry(entryPtr);
2209 	return TCL_ERROR;
2210     }
2211     entryPtr->column.rcPtr = InitSpan(&(tablePtr->columnInfo), column,
2212 	entryPtr->column.span);
2213     entryPtr->row.rcPtr = InitSpan(&(tablePtr->rowInfo), row,
2214 	entryPtr->row.span);
2215     /*
2216      * Insert the entry into both the row and column layout lists
2217      */
2218     BinEntry(tablePtr, entryPtr);
2219 
2220     return result;
2221 }
2222 
2223 /*
2224  * ----------------------------------------------------------------------------
2225  *
2226  * BuildTable --
2227  *
2228  *	Processes an argv/argc list of table entries to add and
2229  *	configure new widgets into the table.  A table entry consists
2230  *	of the widget path name, table index, and optional
2231  *	configuration options.  The first argument in the argv list is
2232  *	the name of the table.  If no table exists for the given
2233  *	widget, a new one is created.
2234  *
2235  * Results:
2236  *	Returns a standard Tcl result.  If an error occurred,
2237  *	TCL_ERROR is returned and an error message is left in
2238  *	interp->result.
2239  *
2240  * Side Effects:
2241  *	Memory is allocated, a new table is possibly created, etc.
2242  *	The table is re-computed and arranged at the next idle point.
2243  *
2244  * ----------------------------------------------------------------------------
2245  */
2246 static int
BuildTable(tablePtr,interp,argc,argv)2247 BuildTable(tablePtr, interp, argc, argv)
2248     Table *tablePtr;		/* Table to manage new widgets */
2249     Tcl_Interp *interp;		/* Interpreter to report errors back to */
2250     int argc;			/*  */
2251     char **argv;		/* List of widgets, indices, and options */
2252 {
2253     Tk_Window tkwin;
2254     int row, column;
2255     int nextRow, nextColumn;
2256     register int i;
2257 
2258     /* Process any options specific to the table */
2259     for (i = 2; i < argc; i += 2) {
2260 	if (argv[i][0] != '-') {
2261 	    break;
2262 	}
2263     }
2264     if (i > argc) {
2265 	i = argc;
2266     }
2267     if (i > 2) {
2268 	if (ConfigureTable(tablePtr, interp, i - 2, argv + 2) != TCL_OK) {
2269 	    return TCL_ERROR;
2270 	}
2271     }
2272     nextRow = tablePtr->nRows;
2273     nextColumn = 0;
2274     argc -= i, argv += i;
2275     while (argc > 0) {
2276 	/*
2277 	 * Allow the name of the widget and row/column index to be
2278 	 * specified in any order.
2279 	 */
2280 	if (argv[0][0] == '.') {
2281 	    tkwin = Tk_NameToWindow(interp, argv[0], tablePtr->tkwin);
2282 	    if (tkwin == NULL) {
2283 		return TCL_ERROR;
2284 	    }
2285 	    if ((argc == 1) || (argv[1][0] == '-')) {
2286 		/* No row,column index, use defaults instead */
2287 		row = nextRow, column = nextColumn;
2288 		argc--, argv++;
2289 	    } else {
2290 		if (ParseIndex(interp, argv[1], &row, &column) != TCL_OK) {
2291 		    return TCL_ERROR;	/* Invalid row,column index */
2292 		}
2293 		/* Skip over the widget pathname and table index. */
2294 		argc -= 2, argv += 2;
2295 	    }
2296 	} else {
2297 	    if (ParseIndex(interp, argv[0], &row, &column) != TCL_OK) {
2298 		return TCL_ERROR;
2299 	    }
2300 	    if (argc == 1) {
2301 		Tcl_AppendResult(interp, "missing widget pathname after \"",
2302 			 argv[0], "\"", (char *)NULL);
2303 		return TCL_ERROR;
2304 	    }
2305 	    tkwin = Tk_NameToWindow(interp, argv[1], tablePtr->tkwin);
2306 	    if (tkwin == NULL) {
2307 		return TCL_ERROR;
2308 	    }
2309 	    /* Skip over the widget pathname and table index. */
2310 	    argc -= 2, argv += 2;
2311 	}
2312 
2313 	/* Find the end of the widget's option-value pairs */
2314 	for (i = 0; i < argc; i += 2) {
2315 	    if (argv[i][0] != '-') {
2316 		break;
2317 	    }
2318 	}
2319 	if (i > argc) {
2320 	    i = argc;
2321 	}
2322 	if (ManageEntry(interp, tablePtr, tkwin, row,
2323 		column, i, argv) != TCL_OK) {
2324 	    return TCL_ERROR;
2325 	}
2326 	nextColumn = column + 1;
2327 	argc -= i, argv += i;
2328     }
2329     /* Arrange for the new table layout to be calculated. */
2330     tablePtr->flags |= REQUEST_LAYOUT;
2331     EventuallyArrangeTable(tablePtr);
2332 
2333     Tcl_SetResult(interp, Tk_PathName(tablePtr->tkwin), TCL_VOLATILE);
2334     return TCL_OK;
2335 }
2336 
2337 /*
2338  * ----------------------------------------------------------------------------
2339  *
2340  * ParseItem --
2341  *
2342  *	Parses a string representing an item in the table.  An item
2343  *	may be one of the following:
2344  *		Rn	- Row index, where n is the index of row
2345  *		Cn	- Column index, where n is the index of column
2346  *		r,c	- Cell index, where r is the row index and c
2347  *			  is the column index.
2348  *
2349  * Results:
2350  *	Returns a standard Tcl result.  If no error occurred, TCL_OK
2351  *	is returned.  *RowPtr* will return the row index.  *ColumnPtr*
2352  *	will return the column index.  If the row or column index is
2353  *	not applicable, -1 is returned via *rowPtr* or *columnPtr*.
2354  *
2355  * ----------------------------------------------------------------------------
2356  */
2357 static int
ParseItem(tablePtr,string,rowPtr,columnPtr)2358 ParseItem(tablePtr, string, rowPtr, columnPtr)
2359     Table *tablePtr;
2360     char *string;
2361     int *rowPtr, *columnPtr;
2362 {
2363     char c;
2364     long partNum;
2365 
2366     c = tolower(string[0]);
2367     *rowPtr = *columnPtr = -1;
2368     if (c == 'r') {
2369 	if (Tcl_ExprLong(tablePtr->interp, string + 1, &partNum) != TCL_OK) {
2370 	    return TCL_ERROR;
2371 	}
2372 	if ((partNum < 0) || (partNum >= tablePtr->nRows)) {
2373 	    Tcl_AppendResult(tablePtr->interp, "row index \"", string,
2374 		"\" is out of range", (char *)NULL);
2375 	    return TCL_ERROR;
2376 	}
2377 	*rowPtr = (int)partNum;
2378     } else if (c == 'c') {
2379 	if (Tcl_ExprLong(tablePtr->interp, string + 1, &partNum) != TCL_OK) {
2380 	    return TCL_ERROR;
2381 	}
2382 	if ((partNum < 0) || (partNum >= tablePtr->nColumns)) {
2383 	    Tcl_AppendResult(tablePtr->interp, "column index \"", string,
2384 		"\" is out of range", (char *)NULL);
2385 	    return TCL_ERROR;
2386 	}
2387 	*columnPtr = (int)partNum;
2388     } else {
2389 	if (ParseIndex(tablePtr->interp, string,
2390 		rowPtr, columnPtr) != TCL_OK) {
2391 	    return TCL_ERROR;	/* Invalid row,column index */
2392 	}
2393 	if ((*rowPtr < 0) || (*rowPtr >= tablePtr->nRows) ||
2394 	    (*columnPtr < 0) || (*columnPtr >= tablePtr->nColumns)) {
2395 	    Tcl_AppendResult(tablePtr->interp, "index \"", string,
2396 		"\" is out of range", (char *)NULL);
2397 	    return TCL_ERROR;
2398 	}
2399     }
2400     return TCL_OK;
2401 }
2402 
2403 /*
2404  * ----------------------------------------------------------------------------
2405  *
2406  * TranslateAnchor --
2407  *
2408  * 	Translate the coordinates of a given bounding box based upon
2409  * 	the anchor specified.  The anchor indicates where the given xy
2410  * 	position is in relation to the bounding box.
2411  *
2412  *  		nw --- n --- ne
2413  *  		|            |     x,y ---+
2414  *  		w   center   e      |     |
2415  *  		|            |      +-----+
2416  *  		sw --- s --- se
2417  *
2418  * Results:
2419  *	The translated coordinates of the bounding box are returned.
2420  *
2421  * ----------------------------------------------------------------------------
2422  */
2423 static void
TranslateAnchor(dx,dy,anchor,xPtr,yPtr)2424 TranslateAnchor(dx, dy, anchor, xPtr, yPtr)
2425     int dx, dy;			/* Difference between outer and inner regions
2426 				 */
2427     Tk_Anchor anchor;		/* Direction of the anchor */
2428     int *xPtr, *yPtr;
2429 {
2430     int x, y;
2431 
2432     x = y = 0;
2433     switch (anchor) {
2434     case TK_ANCHOR_NW:		/* Upper left corner */
2435 	break;
2436     case TK_ANCHOR_W:		/* Left center */
2437 	y = (dy / 2);
2438 	break;
2439     case TK_ANCHOR_SW:		/* Lower left corner */
2440 	y = dy;
2441 	break;
2442     case TK_ANCHOR_N:		/* Top center */
2443 	x = (dx / 2);
2444 	break;
2445     case TK_ANCHOR_CENTER:	/* Centered */
2446 	x = (dx / 2);
2447 	y = (dy / 2);
2448 	break;
2449     case TK_ANCHOR_S:		/* Bottom center */
2450 	x = (dx / 2);
2451 	y = dy;
2452 	break;
2453     case TK_ANCHOR_NE:		/* Upper right corner */
2454 	x = dx;
2455 	break;
2456     case TK_ANCHOR_E:		/* Right center */
2457 	x = dx;
2458 	y = (dy / 2);
2459 	break;
2460     case TK_ANCHOR_SE:		/* Lower right corner */
2461 	x = dx;
2462 	y = dy;
2463 	break;
2464     }
2465     *xPtr = (*xPtr) + x;
2466     *yPtr = (*yPtr) + y;
2467 }
2468 
2469 /*
2470  * ----------------------------------------------------------------------------
2471  *
2472  * GetReqWidth --
2473  *
2474  *	Returns the width requested by the widget starting in the
2475  *	given entry.  The requested space also includes any internal
2476  *	padding which has been designated for this widget.
2477  *
2478  *	The requested width of the widget is always bounded by the limits
2479  *	set in entryPtr->reqWidth.
2480  *
2481  * Results:
2482  *	Returns the requested width of the widget.
2483  *
2484  * ----------------------------------------------------------------------------
2485  */
2486 static int
GetReqWidth(entryPtr)2487 GetReqWidth(entryPtr)
2488     Entry *entryPtr;
2489 {
2490     int width;
2491 
2492     width = Tk_ReqWidth(entryPtr->tkwin) + (2 * entryPtr->ipadX);
2493     width = GetBoundedWidth(width, &(entryPtr->reqWidth));
2494     return width;
2495 }
2496 
2497 /*
2498  * ----------------------------------------------------------------------------
2499  *
2500  * GetReqHeight --
2501  *
2502  *	Returns the height requested by the widget starting in the
2503  *	given entry.  The requested space also includes any internal
2504  *	padding which has been designated for this widget.
2505  *
2506  *	The requested height of the widget is always bounded by the
2507  *	limits set in entryPtr->reqHeight.
2508  *
2509  * Results:
2510  *	Returns the requested height of the widget.
2511  *
2512  * ----------------------------------------------------------------------------
2513  */
2514 static int
GetReqHeight(entryPtr)2515 GetReqHeight(entryPtr)
2516     Entry *entryPtr;
2517 {
2518     int height;
2519 
2520     height = Tk_ReqHeight(entryPtr->tkwin) + (2 * entryPtr->ipadY);
2521     height = GetBoundedHeight(height, &(entryPtr->reqHeight));
2522     return height;
2523 }
2524 
2525 /*
2526  * ----------------------------------------------------------------------------
2527  *
2528  * GetTotalSpan --
2529  *
2530  *	Sums the row/column space requirements for the entire table.
2531  *
2532  * Results:
2533  *	Returns the space currently used in the span of partitions.
2534  *
2535  * ----------------------------------------------------------------------------
2536  */
2537 static int
GetTotalSpan(infoPtr)2538 GetTotalSpan(infoPtr)
2539     PartitionInfo *infoPtr;
2540 {
2541     register int spaceUsed;
2542     Blt_ChainLink *linkPtr;
2543     RowColumn *rcPtr;		/* Start of partitions */
2544 
2545     spaceUsed = 0;
2546     for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr); linkPtr != NULL;
2547 	linkPtr = Blt_ChainNextLink(linkPtr)) {
2548 	rcPtr = Blt_ChainGetValue(linkPtr);
2549 	spaceUsed += rcPtr->size;
2550     }
2551     return spaceUsed;
2552 }
2553 
2554 /*
2555  * ----------------------------------------------------------------------------
2556  *
2557  * GetSpan --
2558  *
2559  *	Determines the space used by rows/columns for an entry.
2560  *
2561  * Results:
2562  *	Returns the space currently used in the span of partitions.
2563  *
2564  * ----------------------------------------------------------------------------
2565  */
2566 static int
GetSpan(infoPtr,entryPtr)2567 GetSpan(infoPtr, entryPtr)
2568     PartitionInfo *infoPtr;
2569     Entry *entryPtr;
2570 {
2571     RowColumn *startPtr;
2572     register int spaceUsed;
2573     int count;
2574     Blt_ChainLink *linkPtr;
2575     RowColumn *rcPtr;		/* Start of partitions */
2576     int span;			/* Number of partitions spanned */
2577 
2578     if (infoPtr->type == rowUid) {
2579 	rcPtr = entryPtr->row.rcPtr;
2580 	span = entryPtr->row.span;
2581     } else {
2582 	rcPtr = entryPtr->column.rcPtr;
2583 	span = entryPtr->column.span;
2584     }
2585 
2586     count = spaceUsed = 0;
2587     linkPtr = rcPtr->linkPtr;
2588     startPtr = Blt_ChainGetValue(linkPtr);
2589     for ( /*empty*/ ; (linkPtr != NULL) && (count < span);
2590 	linkPtr = Blt_ChainNextLink(linkPtr)) {
2591 	rcPtr = Blt_ChainGetValue(linkPtr);
2592 	spaceUsed += rcPtr->size;
2593 	count++;
2594     }
2595     /*
2596      * Subtract off the padding on either side of the span, since the
2597      * widget can't grow into it.
2598      */
2599     spaceUsed -= (startPtr->pad.side1 + rcPtr->pad.side2 + infoPtr->ePad);
2600     return spaceUsed;
2601 }
2602 
2603 /*
2604  * ----------------------------------------------------------------------------
2605  *
2606  * GrowSpan --
2607  *
2608  *	Expand the span by the amount of the extra space needed.  This
2609  *	procedure is used in LayoutPartitions to grow the partitions
2610  *	to their minimum nominal size, starting from a zero width and
2611  *	height space.
2612  *
2613  *	This looks more complicated than it really is.  The idea is to
2614  *	make the size of the partitions correspond to the smallest
2615  *	entry spans.  For example, if widget A is in column 1 and
2616  *	widget B spans both columns 0 and 1, any extra space needed to
2617  *	fit widget B should come from column 0.
2618  *
2619  *	On the first pass we try to add space to partitions which have
2620  *	not been touched yet (i.e. have no nominal size).  Since the
2621  *	row and column lists are sorted in ascending order of the
2622  *	number of rows or columns spanned, the space is distributed
2623  *	amongst the smallest spans first.
2624  *
2625  *	The second pass handles the case of widgets which have the
2626  *	same span.  For example, if A and B, which span the same
2627  *	number of partitions are the only widgets to span column 1,
2628  *	column 1 would grow to contain the bigger of the two slices of
2629  *	space.
2630  *
2631  *	If there is still extra space after the first two passes, this
2632  *	means that there were no partitions of with no widget spans or
2633  *	the same order span that could be expanded. The third pass
2634  *	will try to remedy this by parcelling out the left over space
2635  *	evenly among the rest of the partitions.
2636  *
2637  *	On each pass, we have to keep iterating over the span, evenly
2638  *	doling out slices of extra space, because we may hit partition
2639  *	limits as space is donated.  In addition, if there are left
2640  *	over pixels because of round-off, this will distribute them as
2641  *	evenly as possible.  For the worst case, it will take *span*
2642  *	passes to expand the span.
2643  *
2644  * Results:
2645  *	None.
2646  *
2647  * Side Effects:
2648  * 	The partitions in the span may be expanded.
2649  *
2650  * ----------------------------------------------------------------------------
2651  */
2652 static void
GrowSpan(infoPtr,entryPtr,growth)2653 GrowSpan(infoPtr, entryPtr, growth)
2654     PartitionInfo *infoPtr;
2655     Entry *entryPtr;
2656     int growth;			/* The amount of extra space needed to
2657 				 * grow the span. */
2658 {
2659     register RowColumn *rcPtr;
2660     Blt_ChainLink *linkPtr;
2661     int spaceLeft, ration;
2662     int nOpen;			/* # of partitions with space available */
2663     register int n;
2664     RowColumn *startPtr;	/* Starting (column/row) partition  */
2665     int span;			/* Number of partitions in the span */
2666 
2667     if (infoPtr->type == rowUid) {
2668 	startPtr = entryPtr->row.rcPtr;
2669 	span = entryPtr->row.span;
2670     } else {
2671 	startPtr = entryPtr->column.rcPtr;
2672 	span = entryPtr->column.span;
2673     }
2674 
2675     /*
2676      * ------------------------------------------------------------------------
2677      *
2678      * Pass 1: First add space to rows/columns that haven't determined
2679      *	       their nominal sizes yet.
2680      *
2681      * ------------------------------------------------------------------------
2682      */
2683 
2684     nOpen = 0;
2685     /* Find out how many partitions have no size yet */
2686     linkPtr = startPtr->linkPtr;
2687     for (n = 0; n < span; n++) {
2688 	rcPtr = Blt_ChainGetValue(linkPtr);
2689 	if ((rcPtr->nomSize == LIMITS_NOM) && (rcPtr->maxSize > rcPtr->size)) {
2690 	    nOpen++;
2691 	}
2692 	linkPtr = Blt_ChainNextLink(linkPtr);
2693     }
2694 
2695     while ((nOpen > 0) && (growth > 0)) {
2696 	ration = growth / nOpen;
2697 	if (ration == 0) {
2698 	    ration = 1;
2699 	}
2700 	linkPtr = startPtr->linkPtr;
2701 	for (n = 0; (n < span) && (growth > 0); n++) {
2702 	    rcPtr = Blt_ChainGetValue(linkPtr);
2703 	    spaceLeft = rcPtr->maxSize - rcPtr->size;
2704 	    if ((rcPtr->nomSize == LIMITS_NOM) && (spaceLeft > 0)) {
2705 		if (ration < spaceLeft) {
2706 		    growth -= ration;
2707 		    rcPtr->size += ration;
2708 		} else {
2709 		    growth -= spaceLeft;
2710 		    rcPtr->size += spaceLeft;
2711 		    nOpen--;
2712 		}
2713 		rcPtr->minSpan = span;
2714 		rcPtr->control = entryPtr;
2715 	    }
2716 	    linkPtr = Blt_ChainNextLink(linkPtr);
2717 	}
2718     }
2719 
2720     /*
2721      * ------------------------------------------------------------------------
2722      *
2723      * Pass 2: Add space to partitions which have the same minimum span
2724      *
2725      * ------------------------------------------------------------------------
2726      */
2727 
2728     nOpen = 0;
2729     linkPtr = startPtr->linkPtr;
2730     for (n = 0; n < span; n++) {
2731 	rcPtr = Blt_ChainGetValue(linkPtr);
2732 	if ((rcPtr->minSpan == span) && (rcPtr->maxSize > rcPtr->size)) {
2733 	    nOpen++;
2734 	}
2735 	linkPtr = Blt_ChainNextLink(linkPtr);
2736     }
2737     while ((nOpen > 0) && (growth > 0)) {
2738 	ration = growth / nOpen;
2739 	if (ration == 0) {
2740 	    ration = 1;
2741 	}
2742 	linkPtr = startPtr->linkPtr;
2743 	for (n = 0; (n < span) && (growth > 0); n++) {
2744 	    rcPtr = Blt_ChainGetValue(linkPtr);
2745 	    spaceLeft = rcPtr->maxSize - rcPtr->size;
2746 	    if ((rcPtr->minSpan == span) && (spaceLeft > 0)) {
2747 		if (ration < spaceLeft) {
2748 		    growth -= ration;
2749 		    rcPtr->size += ration;
2750 		} else {
2751 		    growth -= spaceLeft;
2752 		    rcPtr->size += spaceLeft;
2753 		    nOpen--;
2754 		}
2755 		rcPtr->control = entryPtr;
2756 	    }
2757 	    linkPtr = Blt_ChainNextLink(linkPtr);
2758 	}
2759     }
2760 
2761     /*
2762      * ------------------------------------------------------------------------
2763      *
2764      * Pass 3: Try to expand all the partitions with space still available
2765      *
2766      * ------------------------------------------------------------------------
2767      */
2768 
2769     /* Find out how many partitions still have space available */
2770     nOpen = 0;
2771     linkPtr = startPtr->linkPtr;
2772     for (n = 0; n < span; n++) {
2773 	rcPtr = Blt_ChainGetValue(linkPtr);
2774 	if ((rcPtr->resize & RESIZE_EXPAND) && (rcPtr->maxSize > rcPtr->size)) {
2775 	    nOpen++;
2776 	}
2777 	/* Set the nominal size of the row/column. */
2778 	rcPtr->nomSize = rcPtr->size;
2779 	linkPtr = Blt_ChainNextLink(linkPtr);
2780     }
2781     while ((nOpen > 0) && (growth > 0)) {
2782 	ration = growth / nOpen;
2783 	if (ration == 0) {
2784 	    ration = 1;
2785 	}
2786 	linkPtr = startPtr->linkPtr;
2787 	for (n = 0; (n < span) && (growth > 0); n++) {
2788 	    rcPtr = Blt_ChainGetValue(linkPtr);
2789 	    linkPtr = Blt_ChainNextLink(linkPtr);
2790 	    if (!(rcPtr->resize & RESIZE_EXPAND)) {
2791 		continue;
2792 	    }
2793 	    spaceLeft = rcPtr->maxSize - rcPtr->size;
2794 	    if (spaceLeft > 0) {
2795 		if (ration < spaceLeft) {
2796 		    growth -= ration;
2797 		    rcPtr->size += ration;
2798 		} else {
2799 		    growth -= spaceLeft;
2800 		    rcPtr->size += spaceLeft;
2801 		    nOpen--;
2802 		}
2803 		rcPtr->nomSize = rcPtr->size;
2804 		rcPtr->control = entryPtr;
2805 	    }
2806 	}
2807     }
2808 }
2809 
2810 /*
2811  * ----------------------------------------------------------------------------
2812  *
2813  * AdjustPartitions --
2814  *
2815  *	Adjust the span by the amount of the extra space needed.  If
2816  *	the amount (adjustSpace) is negative, shrink the span,
2817  *	otherwise expand it.  Size constraints on the partitions may
2818  *	prevent any or all of the spacing adjustments.
2819  *
2820  *	This is very much like the GrowSpan procedure, but in this
2821  *	case we are shrinking or expanding all the (row or column)
2822  *	partitions. It uses a two pass approach, first giving space to
2823  *	partitions which not are smaller/larger than their nominal
2824  *	sizes. This is because constraints on the partitions may cause
2825  *	resizing to be non-linear.
2826  *
2827  *	If there is still extra space, this means that all partitions
2828  *	are at least to their nominal sizes.  The second pass will try
2829  *	to add/remove the left over space evenly among all the
2830  *	partitions which still have space available.
2831  *
2832  * Results:
2833  *	None.
2834  *
2835  * Side Effects:
2836  *	The size of the partitions in the span may be increased or
2837  *	decreased.
2838  *
2839  * ----------------------------------------------------------------------------
2840  */
2841 static void
AdjustPartitions(infoPtr,adjustment)2842 AdjustPartitions(infoPtr, adjustment)
2843     PartitionInfo *infoPtr;	/* Array of (column/row) partitions  */
2844     int adjustment;		/* The amount of extra space to grow or shrink
2845 				 * the span. If negative, it represents the
2846 				 * amount of space to remove */
2847 {
2848     register RowColumn *rcPtr;
2849     int ration;			/* Amount of space to ration to each
2850 				 * row/column. */
2851     int delta;			/* Amount of space needed */
2852     int spaceLeft;		/* Amount of space still available */
2853     int size;			/* Amount of space requested for a particular
2854 				 * row/column. */
2855     int nOpen;			/* Number of rows/columns that still can
2856 				 * be adjusted. */
2857     Blt_Chain *chainPtr;
2858     Blt_ChainLink *linkPtr;
2859     double totalWeight;
2860 
2861     chainPtr = infoPtr->chainPtr;
2862 
2863     /*
2864      * ------------------------------------------------------------------------
2865      *
2866      * Pass 1: First adjust the size of rows/columns that still haven't
2867      *	      reached their nominal size.
2868      *
2869      * ------------------------------------------------------------------------
2870      */
2871     delta = adjustment;
2872 
2873     nOpen = 0;
2874     totalWeight = 0.0;
2875     for (linkPtr = Blt_ChainFirstLink(chainPtr); linkPtr != NULL;
2876 	linkPtr = Blt_ChainNextLink(linkPtr)) {
2877 	rcPtr = Blt_ChainGetValue(linkPtr);
2878 	if (rcPtr->weight > 0.0) {
2879 	    if (delta < 0) {
2880 		spaceLeft = rcPtr->size - rcPtr->nomSize;
2881 	    } else {
2882 		spaceLeft = rcPtr->nomSize - rcPtr->size;
2883 	    }
2884 	    if (spaceLeft > 0) {
2885 		nOpen++;
2886 		totalWeight += rcPtr->weight;
2887 	    }
2888 	}
2889     }
2890 
2891     while ((nOpen > 0) && (totalWeight > 0.0) && (delta != 0)) {
2892 	ration = (int)(delta / totalWeight);
2893 	if (ration == 0) {
2894 	    ration = (delta > 0) ? 1 : -1;
2895 	}
2896 	for (linkPtr = Blt_ChainFirstLink(chainPtr);
2897 	    (linkPtr != NULL) && (delta != 0);
2898 	    linkPtr = Blt_ChainNextLink(linkPtr)) {
2899 	    rcPtr = Blt_ChainGetValue(linkPtr);
2900 	    if (rcPtr->weight > 0.0) {
2901 		spaceLeft = rcPtr->nomSize - rcPtr->size;
2902 		if (((delta > 0) && (spaceLeft > 0)) ||
2903 		    ((delta < 0) && (spaceLeft < 0))) {
2904 		    size = (int)(ration * rcPtr->weight);
2905 		    if (size > delta) {
2906 			size = delta;
2907 		    }
2908 		    if (ABS(size) < ABS(spaceLeft)) {
2909 			delta -= size;
2910 			rcPtr->size += size;
2911 		    } else {
2912 			delta -= spaceLeft;
2913 			rcPtr->size += spaceLeft;
2914 			nOpen--;
2915 			totalWeight -= rcPtr->weight;
2916 		    }
2917 		}
2918 	    }
2919 	}
2920     }
2921     /*
2922      * ------------------------------------------------------------------------
2923      *
2924      * Pass 2: Adjust the partitions with space still available
2925      *
2926      * ------------------------------------------------------------------------
2927      */
2928 
2929     nOpen = 0;
2930     totalWeight = 0.0;
2931     for (linkPtr = Blt_ChainFirstLink(chainPtr); linkPtr != NULL;
2932 	linkPtr = Blt_ChainNextLink(linkPtr)) {
2933 	rcPtr = Blt_ChainGetValue(linkPtr);
2934 	if (rcPtr->weight > 0.0) {
2935 	    if (delta > 0) {
2936 		spaceLeft = rcPtr->maxSize - rcPtr->size;
2937 	    } else {
2938 		spaceLeft = rcPtr->size - rcPtr->minSize;
2939 	    }
2940 	    if (spaceLeft > 0) {
2941 		nOpen++;
2942 		totalWeight += rcPtr->weight;
2943 	    }
2944 	}
2945     }
2946     while ((nOpen > 0) && (totalWeight > 0.0) && (delta != 0)) {
2947 	ration = (int)(delta / totalWeight);
2948 	if (ration == 0) {
2949 	    ration = (delta > 0) ? 1 : -1;
2950 	}
2951 	linkPtr = Blt_ChainFirstLink(chainPtr);
2952 	for ( /*empty*/ ; (linkPtr != NULL) && (delta != 0);
2953 	    linkPtr = Blt_ChainNextLink(linkPtr)) {
2954 	    rcPtr = Blt_ChainGetValue(linkPtr);
2955 	    if (rcPtr->weight > 0.0) {
2956 		if (delta > 0) {
2957 		    spaceLeft = rcPtr->maxSize - rcPtr->size;
2958 		} else {
2959 		    spaceLeft = rcPtr->minSize - rcPtr->size;
2960 		}
2961 		if (((delta > 0) && (spaceLeft > 0)) ||
2962 		    ((delta < 0) && (spaceLeft < 0))) {
2963 		    size = (int)(ration * rcPtr->weight);
2964 		    if (size > delta) {
2965 			size = delta;
2966 		    }
2967 		    if (ABS(size) < ABS(spaceLeft)) {
2968 			delta -= size;
2969 			rcPtr->size += size;
2970 		    } else {
2971 			delta -= spaceLeft;
2972 			rcPtr->size += spaceLeft;
2973 			nOpen--;
2974 			totalWeight -= rcPtr->weight;
2975 		    }
2976 		}
2977 	    }
2978 	}
2979     }
2980 }
2981 
2982 /*
2983  * ----------------------------------------------------------------------------
2984  *
2985  * ResetPartitions --
2986  *
2987  *	Sets/resets the size of each row and column partition to the
2988  *	minimum limit of the partition (this is usually zero). This
2989  *	routine gets called when new widgets are added, deleted, or
2990  *	resized.
2991  *
2992  * Results:
2993  *	None.
2994  *
2995  * Side Effects:
2996  * 	The size of each partition is re-initialized to its minimum
2997  * 	size.
2998  *
2999  * ----------------------------------------------------------------------------
3000  */
3001 static void
ResetPartitions(tablePtr,infoPtr,limitsProc)3002 ResetPartitions(tablePtr, infoPtr, limitsProc)
3003     Table *tablePtr;
3004     PartitionInfo *infoPtr;
3005     LimitsProc *limitsProc;
3006 {
3007     register RowColumn *rcPtr;
3008     register Blt_ChainLink *linkPtr;
3009     int pad, size;
3010 
3011     for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr); linkPtr != NULL;
3012 	linkPtr = Blt_ChainNextLink(linkPtr)) {
3013 	rcPtr = Blt_ChainGetValue(linkPtr);
3014 
3015 	/*
3016 	 * The constraint procedure below also has the desired
3017 	 * side-effect of setting the minimum, maximum, and nominal
3018 	 * values to the requested size of its associated widget (if
3019 	 * one exists).
3020 	 */
3021 	size = (*limitsProc) (0, &(rcPtr->reqSize));
3022 
3023 	pad = PADDING(rcPtr->pad) + infoPtr->ePad;
3024 	if (rcPtr->reqSize.flags & LIMITS_SET_NOM) {
3025 
3026 	    /*
3027 	     * This could be done more cleanly.  We want to ensure
3028 	     * that the requested nominal size is not overridden when
3029 	     * determining the normal sizes.  So temporarily fix min
3030 	     * and max to the nominal size and reset them back later.
3031 	     */
3032 	    rcPtr->minSize = rcPtr->maxSize = rcPtr->size =
3033 		rcPtr->nomSize = size + pad;
3034 
3035 	} else {
3036 	    /* The range defaults to 0..MAXINT */
3037 	    rcPtr->minSize = rcPtr->reqSize.min + pad;
3038 	    rcPtr->maxSize = rcPtr->reqSize.max + pad;
3039 	    rcPtr->nomSize = LIMITS_NOM;
3040 	    rcPtr->size = pad;
3041 	}
3042 	rcPtr->minSpan = 0;
3043 	rcPtr->control = NULL;
3044 	rcPtr->count = 0;
3045     }
3046 }
3047 
3048 /*
3049  * ----------------------------------------------------------------------------
3050  *
3051  * SetNominalSizes
3052  *
3053  *	Sets the normal sizes for each partition.  The partition size
3054  *	is the requested widget size plus an amount of padding.  In
3055  *	addition, adjust the min/max bounds of the partition depending
3056  *	upon the resize flags (whether the partition can be expanded
3057  *	or shrunk from its normal size).
3058  *
3059  * Results:
3060  *	Returns the total space needed for the all the partitions.
3061  *
3062  * Side Effects:
3063  *	The nominal size of each partition is set.  This is later used
3064  *	to determine how to shrink or grow the table if the container
3065  *	can't be resized to accommodate the exact size requirements
3066  *	of all the partitions.
3067  *
3068  * ----------------------------------------------------------------------------
3069  */
3070 static int
SetNominalSizes(tablePtr,infoPtr)3071 SetNominalSizes(tablePtr, infoPtr)
3072     Table *tablePtr;
3073     PartitionInfo *infoPtr;
3074 {
3075     register RowColumn *rcPtr;
3076     Blt_ChainLink *linkPtr;
3077     int pad, size, total;
3078 
3079     total = 0;
3080     for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr); linkPtr != NULL;
3081 	linkPtr = Blt_ChainNextLink(linkPtr)) {
3082 	rcPtr = Blt_ChainGetValue(linkPtr);
3083 	pad = PADDING(rcPtr->pad) + infoPtr->ePad;
3084 
3085 	/*
3086 	 * Restore the real bounds after temporarily setting nominal
3087 	 * size.  These values may have been set in ResetPartitions to
3088 	 * restrict the size of the paritition to the requested range.
3089 	 */
3090 
3091 	rcPtr->minSize = rcPtr->reqSize.min + pad;
3092 	rcPtr->maxSize = rcPtr->reqSize.max + pad;
3093 
3094 	size = rcPtr->size;
3095 	if (size > rcPtr->maxSize) {
3096 	    size = rcPtr->maxSize;
3097 	} else if (size < rcPtr->minSize) {
3098 	    size = rcPtr->minSize;
3099 	}
3100 	if ((infoPtr->ePad > 0) && (size < tablePtr->editPtr->minSize)) {
3101 	    size = tablePtr->editPtr->minSize;
3102 	}
3103 	rcPtr->nomSize = rcPtr->size = size;
3104 
3105 	/*
3106 	 * If a partition can't be resized (to either expand or
3107 	 * shrink), hold its respective limit at its normal size.
3108 	 */
3109 	if (!(rcPtr->resize & RESIZE_EXPAND)) {
3110 	    rcPtr->maxSize = rcPtr->nomSize;
3111 	}
3112 	if (!(rcPtr->resize & RESIZE_SHRINK)) {
3113 	    rcPtr->minSize = rcPtr->nomSize;
3114 	}
3115 	if (rcPtr->control == NULL) {
3116 	    /* If a row/column contains no entries, then its size
3117 	     * should be locked. */
3118 	    if (rcPtr->resize & RESIZE_VIRGIN) {
3119 		rcPtr->maxSize = rcPtr->minSize = size;
3120 	    } else {
3121 		if (!(rcPtr->resize & RESIZE_EXPAND)) {
3122 		    rcPtr->maxSize = size;
3123 		}
3124 		if (!(rcPtr->resize & RESIZE_SHRINK)) {
3125 		    rcPtr->minSize = size;
3126 		}
3127 	    }
3128 	    rcPtr->nomSize = size;
3129 	}
3130 	total += rcPtr->nomSize;
3131     }
3132     return total;
3133 }
3134 
3135 /*
3136  * ----------------------------------------------------------------------------
3137  *
3138  * LockPartitions
3139  *
3140  *	Sets the maximum size of a row or column, if the partition
3141  *	has a widget that controls it.
3142  *
3143  * Results:
3144  *	None.
3145  *
3146  * ----------------------------------------------------------------------------
3147  */
3148 static void
LockPartitions(infoPtr)3149 LockPartitions(infoPtr)
3150     PartitionInfo *infoPtr;
3151 {
3152     register RowColumn *rcPtr;
3153     Blt_ChainLink *linkPtr;
3154 
3155     for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr); linkPtr != NULL;
3156 	linkPtr = Blt_ChainNextLink(linkPtr)) {
3157 	rcPtr = Blt_ChainGetValue(linkPtr);
3158 	if (rcPtr->control != NULL) {
3159 	    /* Partition is controlled by this widget */
3160 	    rcPtr->maxSize = rcPtr->size;
3161 	}
3162     }
3163 }
3164 
3165 /*
3166  * ----------------------------------------------------------------------------
3167  *
3168  * LayoutPartitions --
3169  *
3170  *	Calculates the normal space requirements for both the row and
3171  *	column partitions.  Each widget is added in order of the
3172  *	number of rows or columns spanned, which defines the space
3173  *	needed among in the partitions spanned.
3174  *
3175  * Results:
3176  *	None.
3177  *
3178  * Side Effects:
3179  *
3180  * 	The sum of normal sizes set here will be used as the normal size
3181  * 	for the container widget.
3182  *
3183  * ----------------------------------------------------------------------------
3184  */
3185 static void
LayoutPartitions(tablePtr)3186 LayoutPartitions(tablePtr)
3187     Table *tablePtr;
3188 {
3189     register Blt_ListNode node;
3190     Blt_Chain *chainPtr;
3191     Blt_ChainLink *linkPtr;
3192     register Entry *entryPtr;
3193     int needed, used, total;
3194     PartitionInfo *infoPtr;
3195 
3196     infoPtr = &(tablePtr->columnInfo);
3197 
3198     ResetPartitions(tablePtr, infoPtr, GetBoundedWidth);
3199 
3200     for (node = Blt_ListFirstNode(infoPtr->list); node != NULL;
3201 	node = Blt_ListNextNode(node)) {
3202 	chainPtr = (Blt_Chain *) Blt_ListGetValue(node);
3203 
3204 	for (linkPtr = Blt_ChainFirstLink(chainPtr); linkPtr != NULL;
3205 	    linkPtr = Blt_ChainNextLink(linkPtr)) {
3206 	    entryPtr = Blt_ChainGetValue(linkPtr);
3207 	    if (entryPtr->column.control != CONTROL_FULL) {
3208 		continue;
3209 	    }
3210 	    needed = GetReqWidth(entryPtr) + PADDING(entryPtr->padX) +
3211 		2 * (entryPtr->borderWidth + tablePtr->eEntryPad);
3212 	    if (needed <= 0) {
3213 		continue;
3214 	    }
3215 	    used = GetSpan(infoPtr, entryPtr);
3216 	    if (needed > used) {
3217 		GrowSpan(infoPtr, entryPtr, needed - used);
3218 	    }
3219 	}
3220     }
3221 
3222     LockPartitions(infoPtr);
3223 
3224     for (node = Blt_ListFirstNode(infoPtr->list); node != NULL;
3225 	node = Blt_ListNextNode(node)) {
3226 	chainPtr = (Blt_Chain *) Blt_ListGetValue(node);
3227 
3228 	for (linkPtr = Blt_ChainFirstLink(chainPtr); linkPtr != NULL;
3229 	    linkPtr = Blt_ChainNextLink(linkPtr)) {
3230 	    entryPtr = Blt_ChainGetValue(linkPtr);
3231 
3232 	    needed = GetReqWidth(entryPtr) + PADDING(entryPtr->padX) +
3233 		2 * (entryPtr->borderWidth + tablePtr->eEntryPad);
3234 
3235 	    if (entryPtr->column.control >= 0.0) {
3236 		needed = (int)(needed * entryPtr->column.control);
3237 	    }
3238 	    if (needed <= 0) {
3239 		continue;
3240 	    }
3241 	    used = GetSpan(infoPtr, entryPtr);
3242 	    if (needed > used) {
3243 		GrowSpan(infoPtr, entryPtr, needed - used);
3244 	    }
3245 	}
3246     }
3247     total = SetNominalSizes(tablePtr, infoPtr);
3248     tablePtr->normal.width = GetBoundedWidth(total, &(tablePtr->reqWidth)) +
3249 	PADDING(tablePtr->padX) +
3250 	2 * (tablePtr->eTablePad + Tk_InternalBorderWidth(tablePtr->tkwin));
3251 
3252     infoPtr = &(tablePtr->rowInfo);
3253 
3254     ResetPartitions(tablePtr, infoPtr, GetBoundedHeight);
3255 
3256     for (node = Blt_ListFirstNode(infoPtr->list); node != NULL;
3257 	node = Blt_ListNextNode(node)) {
3258 	chainPtr = (Blt_Chain *) Blt_ListGetValue(node);
3259 
3260 	for (linkPtr = Blt_ChainFirstLink(chainPtr); linkPtr != NULL;
3261 	    linkPtr = Blt_ChainNextLink(linkPtr)) {
3262 	    entryPtr = Blt_ChainGetValue(linkPtr);
3263 	    if (entryPtr->row.control != CONTROL_FULL) {
3264 		continue;
3265 	    }
3266 	    needed = GetReqHeight(entryPtr) + PADDING(entryPtr->padY) +
3267 		2 * (entryPtr->borderWidth + tablePtr->eEntryPad);
3268 	    if (needed <= 0) {
3269 		continue;
3270 	    }
3271 	    used = GetSpan(infoPtr, entryPtr);
3272 	    if (needed > used) {
3273 		GrowSpan(infoPtr, entryPtr, needed - used);
3274 	    }
3275 	}
3276     }
3277 
3278     LockPartitions(&(tablePtr->rowInfo));
3279 
3280     for (node = Blt_ListFirstNode(infoPtr->list); node != NULL;
3281 	node = Blt_ListNextNode(node)) {
3282 	chainPtr = Blt_ChainGetValue(node);
3283 
3284 	for (linkPtr = Blt_ChainFirstLink(chainPtr); linkPtr != NULL;
3285 	    linkPtr = Blt_ChainNextLink(linkPtr)) {
3286 	    entryPtr = Blt_ChainGetValue(linkPtr);
3287 	    needed = GetReqHeight(entryPtr) + PADDING(entryPtr->padY) +
3288 		2 * (entryPtr->borderWidth + tablePtr->eEntryPad);
3289 	    if (entryPtr->row.control >= 0.0) {
3290 		needed = (int)(needed * entryPtr->row.control);
3291 	    }
3292 	    if (needed <= 0) {
3293 		continue;
3294 	    }
3295 	    used = GetSpan(infoPtr, entryPtr);
3296 	    if (needed > used) {
3297 		GrowSpan(infoPtr, entryPtr, needed - used);
3298 	    }
3299 	}
3300     }
3301     total = SetNominalSizes(tablePtr, infoPtr);
3302     tablePtr->normal.height = GetBoundedHeight(total, &(tablePtr->reqHeight)) +
3303 	PADDING(tablePtr->padY) +
3304 	2 * (tablePtr->eTablePad + Tk_InternalBorderWidth(tablePtr->tkwin));
3305 }
3306 
3307 /*
3308  * ----------------------------------------------------------------------------
3309  *
3310  * ArrangeEntries
3311  *
3312  *	Places each widget at its proper location.  First determines
3313  *	the size and position of the each widget.  It then considers the
3314  *	following:
3315  *
3316  *	  1. translation of widget position its parent widget.
3317  *	  2. fill style
3318  *	  3. anchor
3319  *	  4. external and internal padding
3320  *	  5. widget size must be greater than zero
3321  *
3322  * Results:
3323  *	None.
3324  *
3325  * Side Effects:
3326  * 	The size of each partition is re-initialized its minimum size.
3327  *
3328  * ----------------------------------------------------------------------------
3329  */
3330 static void
ArrangeEntries(tablePtr)3331 ArrangeEntries(tablePtr)
3332     Table *tablePtr;		/* Table widget structure */
3333 {
3334     register Blt_ChainLink *linkPtr;
3335     register Entry *entryPtr;
3336     register int spanWidth, spanHeight;
3337     int x, y;
3338     int winWidth, winHeight;
3339     int dx, dy;
3340     int maxX, maxY;
3341     int extra;
3342 
3343     maxX = tablePtr->container.width -
3344 	(Tk_InternalBorderWidth(tablePtr->tkwin) + tablePtr->padRight +
3345 	tablePtr->eTablePad);
3346     maxY = tablePtr->container.height -
3347 	(Tk_InternalBorderWidth(tablePtr->tkwin) + tablePtr->padBottom +
3348 	tablePtr->eTablePad);
3349 
3350     for (linkPtr = Blt_ChainFirstLink(tablePtr->chainPtr); linkPtr != NULL;
3351 	linkPtr = Blt_ChainNextLink(linkPtr)) {
3352 	entryPtr = Blt_ChainGetValue(linkPtr);
3353 
3354 	x = entryPtr->column.rcPtr->offset +
3355 	    entryPtr->column.rcPtr->pad.side1 +
3356 	    entryPtr->padLeft +
3357 	    Tk_Changes(entryPtr->tkwin)->border_width +
3358 	    tablePtr->eEntryPad;
3359 	y = entryPtr->row.rcPtr->offset +
3360 	    entryPtr->row.rcPtr->pad.side1 +
3361 	    entryPtr->padTop +
3362 	    Tk_Changes(entryPtr->tkwin)->border_width +
3363 	    tablePtr->eEntryPad;
3364 
3365 	/*
3366 	 * Unmap any widgets that start beyond of the right edge of
3367 	 * the container.
3368 	 */
3369 	if ((x >= maxX) || (y >= maxY)) {
3370 	    if (Tk_IsMapped(entryPtr->tkwin)) {
3371 		if (Tk_Parent(entryPtr->tkwin) != tablePtr->tkwin) {
3372 		    Tk_UnmaintainGeometry(entryPtr->tkwin, tablePtr->tkwin);
3373 		}
3374 		Tk_UnmapWindow(entryPtr->tkwin);
3375 	    }
3376 	    continue;
3377 	}
3378 	extra = 2 * (entryPtr->borderWidth + tablePtr->eEntryPad);
3379 	spanWidth = GetSpan(&(tablePtr->columnInfo), entryPtr) -
3380 	    (extra + PADDING(entryPtr->padX));
3381 	spanHeight = GetSpan(&(tablePtr->rowInfo), entryPtr) -
3382 	    (extra + PADDING(entryPtr->padY));
3383 
3384 	winWidth = GetReqWidth(entryPtr);
3385 	winHeight = GetReqHeight(entryPtr);
3386 
3387 	/*
3388 	 *
3389 	 * Compare the widget's requested size to the size of the span.
3390 	 *
3391 	 * 1) If the widget is larger than the span or if the fill flag
3392 	 *    is set, make the widget the size of the span. Check that the
3393 	 *    new size is within the bounds set for the widget.
3394 	 *
3395 	 * 2) Otherwise, position the widget in the space according to its
3396 	 *    anchor.
3397 	 *
3398 	 */
3399 	if ((spanWidth <= winWidth) || (entryPtr->fill & FILL_X)) {
3400 	    winWidth = spanWidth;
3401 	    if (winWidth > entryPtr->reqWidth.max) {
3402 		winWidth = entryPtr->reqWidth.max;
3403 	    }
3404 	}
3405 	if ((spanHeight <= winHeight) || (entryPtr->fill & FILL_Y)) {
3406 	    winHeight = spanHeight;
3407 	    if (winHeight > entryPtr->reqHeight.max) {
3408 		winHeight = entryPtr->reqHeight.max;
3409 	    }
3410 	}
3411 	dx = dy = 0;
3412 	if (spanWidth > winWidth) {
3413 	    dx = (spanWidth - winWidth);
3414 	}
3415 	if (spanHeight > winHeight) {
3416 	    dy = (spanHeight - winHeight);
3417 	}
3418 	if ((dx > 0) || (dy > 0)) {
3419 	    TranslateAnchor(dx, dy, entryPtr->anchor, &x, &y);
3420 	}
3421 	/*
3422 	 * Clip the widget at the bottom and/or right edge of the
3423 	 * container.
3424 	 */
3425 	if (winWidth > (maxX - x)) {
3426 	    winWidth = (maxX - x);
3427 	}
3428 	if (winHeight > (maxY - y)) {
3429 	    winHeight = (maxY - y);
3430 	}
3431 
3432 	/*
3433 	 * If the widget is too small (i.e. it has only an external
3434 	 * border) then unmap it.
3435 	 */
3436 	if ((winWidth < 1) || (winHeight < 1)) {
3437 	    if (Tk_IsMapped(entryPtr->tkwin)) {
3438 		if (tablePtr->tkwin != Tk_Parent(entryPtr->tkwin)) {
3439 		    Tk_UnmaintainGeometry(entryPtr->tkwin, tablePtr->tkwin);
3440 		}
3441 		Tk_UnmapWindow(entryPtr->tkwin);
3442 	    }
3443 	    continue;
3444 	}
3445 
3446 	/*
3447 	 * Resize and/or move the widget as necessary.
3448 	 */
3449 	entryPtr->x = x;
3450 	entryPtr->y = y;
3451 
3452 	if (tablePtr->tkwin != Tk_Parent(entryPtr->tkwin)) {
3453 	    Tk_MaintainGeometry(entryPtr->tkwin, tablePtr->tkwin, x, y,
3454 		winWidth, winHeight);
3455 	} else {
3456 	    if ((x != Tk_X(entryPtr->tkwin)) ||
3457 		(y != Tk_Y(entryPtr->tkwin)) ||
3458 		(winWidth != Tk_Width(entryPtr->tkwin)) ||
3459 		(winHeight != Tk_Height(entryPtr->tkwin))) {
3460 		Tk_MoveResizeWindow(entryPtr->tkwin, x, y, winWidth, winHeight);
3461 	    }
3462 	    if (!Tk_IsMapped(entryPtr->tkwin)) {
3463 		Tk_MapWindow(entryPtr->tkwin);
3464 	    }
3465 	}
3466     }
3467 }
3468 
3469 /*
3470  * ----------------------------------------------------------------------------
3471  *
3472  * ArrangeTable --
3473  *
3474  *
3475  * Results:
3476  *	None.
3477  *
3478  * Side Effects:
3479  * 	The widgets in the table are possibly resized and redrawn.
3480  *
3481  * ----------------------------------------------------------------------------
3482  */
3483 static void
ArrangeTable(clientData)3484 ArrangeTable(clientData)
3485     ClientData clientData;
3486 {
3487     Table *tablePtr = clientData;
3488     int width, height;
3489     int offset;
3490     int padX, padY;
3491     int outerPad;
3492     RowColumn *columnPtr, *rowPtr;
3493     Blt_ChainLink *linkPtr;
3494 
3495 #ifdef notdef
3496     fprintf(stderr, "ArrangeTable(%s)\n", Tk_PathName(tablePtr->tkwin));
3497 #endif
3498     Tcl_Preserve(tablePtr);
3499     tablePtr->flags &= ~ARRANGE_PENDING;
3500 
3501     tablePtr->rowInfo.ePad = tablePtr->columnInfo.ePad = tablePtr->eTablePad =
3502 	tablePtr->eEntryPad = 0;
3503     if (tablePtr->editPtr != NULL) {
3504 	tablePtr->rowInfo.ePad = tablePtr->columnInfo.ePad =
3505 	    tablePtr->editPtr->gridLineWidth;
3506 	tablePtr->eTablePad = tablePtr->editPtr->gridLineWidth;
3507 	tablePtr->eEntryPad = tablePtr->editPtr->entryPad;
3508     }
3509     /*
3510      * If the table has no children anymore, then don't do anything at
3511      * all: just leave the container widget's size as-is.
3512      */
3513     if ((Blt_ChainGetLength(tablePtr->chainPtr) == 0) ||
3514 	(tablePtr->tkwin == NULL)) {
3515 	Tcl_Release(tablePtr);
3516 	return;
3517     }
3518     if (tablePtr->flags & REQUEST_LAYOUT) {
3519 	tablePtr->flags &= ~REQUEST_LAYOUT;
3520 	LayoutPartitions(tablePtr);
3521     }
3522     /*
3523      * Initially, try to fit the partitions exactly into the container
3524      * by resizing the container.  If the widget's requested size is
3525      * different, send a request to the container widget's geometry
3526      * manager to resize.
3527      */
3528     if ((tablePtr->propagate) &&
3529 	((tablePtr->normal.width != Tk_ReqWidth(tablePtr->tkwin)) ||
3530 	    (tablePtr->normal.height != Tk_ReqHeight(tablePtr->tkwin)))) {
3531 	Tk_GeometryRequest(tablePtr->tkwin, tablePtr->normal.width,
3532 	    tablePtr->normal.height);
3533 	EventuallyArrangeTable(tablePtr);
3534 	Tcl_Release(tablePtr);
3535 	return;
3536     }
3537     /*
3538      * Save the width and height of the container so we know when its
3539      * size has changed during ConfigureNotify events.
3540      */
3541     tablePtr->container.width = Tk_Width(tablePtr->tkwin);
3542     tablePtr->container.height = Tk_Height(tablePtr->tkwin);
3543     outerPad = 2 * (Tk_InternalBorderWidth(tablePtr->tkwin) +
3544 	tablePtr->eTablePad);
3545     padX = outerPad + tablePtr->columnInfo.ePad + PADDING(tablePtr->padX);
3546     padY = outerPad + tablePtr->rowInfo.ePad + PADDING(tablePtr->padY);
3547 
3548     width = GetTotalSpan(&(tablePtr->columnInfo)) + padX;
3549     height = GetTotalSpan(&(tablePtr->rowInfo)) + padY;
3550 
3551     /*
3552      * If the previous geometry request was not fulfilled (i.e. the
3553      * size of the container is different from partitions' space
3554      * requirements), try to adjust size of the partitions to fit the
3555      * widget.
3556      */
3557     if (tablePtr->container.width != width) {
3558 	AdjustPartitions(&(tablePtr->columnInfo),
3559 	    tablePtr->container.width - width);
3560 	width = GetTotalSpan(&(tablePtr->columnInfo)) + padX;
3561     }
3562     if (tablePtr->container.height != height) {
3563 	AdjustPartitions(&(tablePtr->rowInfo),
3564 	    tablePtr->container.height - height);
3565 	height = GetTotalSpan(&(tablePtr->rowInfo)) + padY;
3566     }
3567     /*
3568      * If after adjusting the size of the partitions the space
3569      * required does not equal the size of the widget, do one of the
3570      * following:
3571      *
3572      * 1) If it's smaller, center the table in the widget.
3573      * 2) If it's bigger, clip the partitions that extend beyond
3574      *    the edge of the container.
3575      *
3576      * Set the row and column offsets (including the container's
3577      * internal border width). To be used later when positioning the
3578      * widgets.
3579      */
3580     offset = Tk_InternalBorderWidth(tablePtr->tkwin) + tablePtr->padLeft +
3581 	tablePtr->eTablePad;
3582     if (width < tablePtr->container.width) {
3583 	offset += (tablePtr->container.width - width) / 2;
3584     }
3585     for (linkPtr = Blt_ChainFirstLink(tablePtr->columnInfo.chainPtr);
3586 	linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
3587 	columnPtr = Blt_ChainGetValue(linkPtr);
3588 	columnPtr->offset = offset + tablePtr->columnInfo.ePad;
3589 	offset += columnPtr->size;
3590     }
3591     offset = Tk_InternalBorderWidth(tablePtr->tkwin) + tablePtr->padTop +
3592 	tablePtr->eTablePad;
3593     if (height < tablePtr->container.height) {
3594 	offset += (tablePtr->container.height - height) / 2;
3595     }
3596     for (linkPtr = Blt_ChainFirstLink(tablePtr->rowInfo.chainPtr);
3597 	linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
3598 	rowPtr = Blt_ChainGetValue(linkPtr);
3599 	rowPtr->offset = offset + tablePtr->rowInfo.ePad;
3600 	offset += rowPtr->size;
3601     }
3602     ArrangeEntries(tablePtr);
3603     if (tablePtr->editPtr != NULL) {
3604 	/* Redraw the editor */
3605 	(*tablePtr->editPtr->drawProc) (tablePtr->editPtr);
3606     }
3607     Tcl_Release(tablePtr);
3608 }
3609 
3610 /*
3611  * ----------------------------------------------------------------------------
3612  *
3613  * ArrangeOp --
3614  *
3615  *	Forces layout of the table geometry manager.  This is useful
3616  *	mostly for debugging the geometry manager.  You can get the
3617  *	geometry manager to calculate the normal (requested) width and
3618  *	height of each row and column.  Otherwise, you need to first
3619  *	withdraw the container widget, invoke "update", and then query
3620  *	the geometry manager.
3621  *
3622  * Results:
3623  *	Returns a standard Tcl result.  If the table is successfully
3624  *	rearranged, TCL_OK is returned. Otherwise, TCL_ERROR is returned
3625  *	and an error message is left in interp->result.
3626  *
3627  * ----------------------------------------------------------------------------
3628  */
3629 /*ARGSUSED*/
3630 static int
ArrangeOp(dataPtr,interp,argc,argv)3631 ArrangeOp(dataPtr, interp, argc, argv)
3632     TableInterpData *dataPtr;	/* Interpreter-specific data. */
3633     Tcl_Interp *interp;		/* Interpreter to report errors to */
3634     int argc;
3635     char **argv;		/* Path name of container associated with
3636 				 * the table */
3637 {
3638     Table *tablePtr;
3639 
3640     if (Blt_GetTable(dataPtr, interp, argv[2], &tablePtr) != TCL_OK) {
3641 	return TCL_ERROR;
3642     }
3643     tablePtr->flags |= REQUEST_LAYOUT;
3644     ArrangeTable(tablePtr);
3645     return TCL_OK;
3646 }
3647 
3648 /*
3649  * ----------------------------------------------------------------------------
3650  *
3651  * CgetOp --
3652  *
3653  *	Returns the name, position and options of a widget in the table.
3654  *
3655  * Results:
3656  *	Returns a standard Tcl result.  A list of the widget attributes
3657  *	is left in interp->result.
3658  *
3659  * --------------------------------------------------------------------------
3660  */
3661 /*ARGSUSED*/
3662 static int
CgetOp(dataPtr,interp,argc,argv)3663 CgetOp(dataPtr, interp, argc, argv)
3664     TableInterpData *dataPtr;	/* Interpreter-specific data. */
3665     Tcl_Interp *interp;
3666     int argc;
3667     char **argv;
3668 {
3669     Table *tablePtr;
3670     int length;
3671     char c;
3672     int n;
3673     PartitionInfo *infoPtr;
3674 
3675     if (Blt_GetTable(dataPtr, interp, argv[2], &tablePtr) != TCL_OK) {
3676 	return TCL_ERROR;
3677     }
3678     if (argc == 4) {
3679 	return Tk_ConfigureValue(interp, tablePtr->tkwin, tableConfigSpecs,
3680 	    (char *)tablePtr, argv[3], 0);
3681     }
3682     c = argv[3][0];
3683     length = strlen(argv[3]);
3684     if (c == '.') {		/* Configure widget */
3685 	Entry *entryPtr;
3686 
3687 	if (GetEntry(interp, tablePtr, argv[3], &entryPtr) != TCL_OK) {
3688 	    return TCL_ERROR;
3689 	}
3690 	return Tk_ConfigureValue(interp, entryPtr->tkwin, entryConfigSpecs,
3691 	    (char *)entryPtr, argv[4], 0);
3692     } else if ((c == 'c') && (strncmp(argv[3], "container", length) == 0)) {
3693 	return Tk_ConfigureValue(interp, tablePtr->tkwin, tableConfigSpecs,
3694 	    (char *)tablePtr, argv[4], 0);
3695     }
3696     infoPtr = ParseRowColumn(tablePtr, argv[3], &n);
3697     if (infoPtr == NULL) {
3698 	return TCL_ERROR;
3699     }
3700     return Tk_ConfigureValue(interp, tablePtr->tkwin, infoPtr->configSpecs,
3701 	(char *)GetRowColumn(infoPtr, n), argv[4], 0);
3702 }
3703 
3704 /*
3705  * ----------------------------------------------------------------------------
3706  *
3707  * ConfigureOp --
3708  *
3709  *	Returns the name, position and options of a widget in the table.
3710  *
3711  * Results:
3712  *	Returns a standard Tcl result.  A list of the table configuration
3713  *	option information is left in interp->result.
3714  *
3715  * --------------------------------------------------------------------------
3716  */
3717 /*ARGSUSED*/
3718 static int
ConfigureOp(dataPtr,interp,argc,argv)3719 ConfigureOp(dataPtr, interp, argc, argv)
3720     TableInterpData *dataPtr;	/* Interpreter-specific data. */
3721     Tcl_Interp *interp;
3722     int argc;
3723     char **argv;
3724 {
3725     Table *tablePtr;
3726     int length;
3727     char c1, c2;
3728     int count;
3729     int result;
3730     char **items;
3731     register int i;
3732 
3733     if (Blt_GetTable(dataPtr, interp, argv[2], &tablePtr) != TCL_OK) {
3734 	return TCL_ERROR;
3735     }
3736     /*
3737      * Find the end of the items. Search until we see an option (-).
3738      */
3739     argc -= 3, argv += 3;
3740     for (count = 0; count < argc; count++) {
3741 	if (argv[count][0] == '-') {
3742 	    break;
3743 	}
3744     }
3745     items = argv;		/* Save the start of the item list */
3746     argc -= count;		/* Move beyond the items to the options */
3747     argv += count;
3748 
3749     result = TCL_ERROR;		/* Suppress compiler warning */
3750 
3751     if (count == 0) {
3752 	result = ConfigureTable(tablePtr, interp, argc, argv);
3753     }
3754     for (i = 0; i < count; i++) {
3755 	c1 = items[i][0];
3756 	c2 = items[i][1];
3757 	length = strlen(items[i]);
3758 	if (c1 == '.') {		/* Configure widget */
3759 	    Entry *entryPtr;
3760 
3761 	    if (GetEntry(interp, tablePtr, items[i], &entryPtr) != TCL_OK) {
3762 		return TCL_ERROR;
3763 	    }
3764 	    result = ConfigureEntry(tablePtr, interp, entryPtr, argc, argv);
3765 	} else if ((c1 == 'r') || (c1 == 'R')) {
3766 	    result = ConfigureRowColumn(tablePtr, &(tablePtr->rowInfo),
3767 		items[i], argc, argv);
3768 	} else if ((c1 == 'c') && (c2 == 'o') &&
3769 	    (strncmp(argv[3], "container", length) == 0)) {
3770 	    result = ConfigureTable(tablePtr, interp, argc, argv);
3771 	} else if ((c1 == 'c') || (c1 == 'C')) {
3772 	    result = ConfigureRowColumn(tablePtr, &(tablePtr->columnInfo),
3773 		items[i], argc, argv);
3774 	} else {
3775 	    Tcl_AppendResult(interp, "unknown item \"", items[i],
3776 		"\": should be widget, row or column index, or \"container\"",
3777 		(char *)NULL);
3778 	    return TCL_ERROR;
3779 	}
3780 	if (result == TCL_ERROR) {
3781 	    break;
3782 	}
3783 	if ((i + 1) < count) {
3784 	    Tcl_AppendResult(interp, "\n", (char *)NULL);
3785 	}
3786     }
3787     tablePtr->flags |= REQUEST_LAYOUT;
3788     EventuallyArrangeTable(tablePtr);
3789     return result;
3790 }
3791 
3792 /*
3793  * ----------------------------------------------------------------------------
3794  *
3795  * DeleteOp --
3796  *
3797  *	Deletes the specified rows and/or columns from the table.
3798  *	Note that the row/column indices can be fixed only after
3799  *	all the deletions have occurred.
3800  *
3801  *		table delete .f r0 r1 r4 c0
3802  *
3803  * Results:
3804  *	Returns a standard Tcl result.
3805  *
3806  *
3807  * ----------------------------------------------------------------------------
3808  */
3809 /*ARGSUSED*/
3810 static int
DeleteOp(dataPtr,interp,argc,argv)3811 DeleteOp(dataPtr, interp, argc, argv)
3812     TableInterpData *dataPtr;	/* Interpreter-specific data. */
3813     Tcl_Interp *interp;
3814     int argc;
3815     char **argv;
3816 {
3817     Table *tablePtr;
3818     char c;
3819     Blt_ChainLink *linkPtr, *nextPtr;
3820     PartitionInfo *infoPtr;
3821     char string[200];
3822     int matches;
3823     register int i;
3824     RowColumn *rcPtr;
3825 
3826     if (Blt_GetTable(dataPtr, interp, argv[2], &tablePtr) != TCL_OK) {
3827 	return TCL_ERROR;
3828     }
3829     for (i = 3; i < argc; i++) {
3830 	c = tolower(argv[i][0]);
3831 	if ((c != 'r') && (c != 'c')) {
3832 	    Tcl_AppendResult(interp, "bad index \"", argv[i],
3833 		"\": must start with \"r\" or \"c\"", (char *)NULL);
3834 	    return TCL_ERROR;
3835 	}
3836     }
3837     matches = 0;
3838     for (i = 3; i < argc; i++) {
3839 	c = tolower(argv[i][0]);
3840 	infoPtr = (c == 'r') ? &(tablePtr->rowInfo) : &(tablePtr->columnInfo);
3841 	for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr); linkPtr != NULL;
3842 	    linkPtr = nextPtr) {
3843 	    nextPtr = Blt_ChainNextLink(linkPtr);
3844 	    rcPtr = Blt_ChainGetValue(linkPtr);
3845 	    sprintf(string, "%c%d", argv[i][0], rcPtr->index);
3846 	    if (Tcl_StringMatch(string, argv[i])) {
3847 		matches++;
3848 		DeleteRowColumn(tablePtr, infoPtr, rcPtr);
3849 		Blt_ChainDeleteLink(infoPtr->chainPtr, linkPtr);
3850 	    }
3851 	}
3852     }
3853     if (matches > 0) {		/* Fix indices */
3854 	i = 0;
3855 	for (linkPtr = Blt_ChainFirstLink(tablePtr->columnInfo.chainPtr);
3856 	    linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
3857 	    rcPtr = Blt_ChainGetValue(linkPtr);
3858 	    rcPtr->index = i++;
3859 	}
3860 	i = 0;
3861 	for (linkPtr = Blt_ChainFirstLink(tablePtr->rowInfo.chainPtr);
3862 	    linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
3863 	    rcPtr = Blt_ChainGetValue(linkPtr);
3864 	    rcPtr->index = i++;
3865 	}
3866 	tablePtr->flags |= REQUEST_LAYOUT;
3867 	EventuallyArrangeTable(tablePtr);
3868     }
3869     return TCL_OK;
3870 }
3871 
3872 /*
3873  * ----------------------------------------------------------------------------
3874  *
3875  * JoinOp --
3876  *
3877  *	Joins the specified span of rows/columns together into a
3878  *	partition.  The row/column indices can be fixed only after
3879  *	all the deletions have occurred.
3880  *
3881  *		table join .f r0 r3
3882  *		table join .f c2 c4
3883  * Results:
3884  *	Returns a standard Tcl result.
3885  *
3886  * ----------------------------------------------------------------------------
3887  */
3888 /*ARGSUSED*/
3889 static int
JoinOp(dataPtr,interp,argc,argv)3890 JoinOp(dataPtr, interp, argc, argv)
3891     TableInterpData *dataPtr;	/* Interpreter-specific data. */
3892     Tcl_Interp *interp;
3893     int argc;
3894     char **argv;
3895 {
3896     Table *tablePtr;
3897     Blt_ChainLink *linkPtr, *nextPtr, *fromPtr;
3898     PartitionInfo *infoPtr, *info2Ptr;
3899     Entry *entryPtr;
3900     int from, to;		/* Indices marking the span of
3901 				 * partitions to be joined together.  */
3902     int start, end;		/* Entry indices. */
3903     register int i;
3904     RowColumn *rcPtr;
3905 
3906     if (Blt_GetTable(dataPtr, interp, argv[2], &tablePtr) != TCL_OK) {
3907 	return TCL_ERROR;
3908     }
3909     infoPtr = ParseRowColumn(tablePtr, argv[3], &from);
3910     if (infoPtr == NULL) {
3911 	return TCL_ERROR;
3912     }
3913     info2Ptr = ParseRowColumn(tablePtr, argv[4], &to);
3914     if (info2Ptr == NULL) {
3915 	return TCL_ERROR;
3916     }
3917     if (infoPtr != info2Ptr) {
3918 	Tcl_AppendResult(interp,
3919 	    "\"from\" and \"to\" must both be rows or columns",
3920 	    (char *)NULL);
3921 	return TCL_ERROR;
3922     }
3923     if (from >= to) {
3924 	return TCL_OK;		/* No-op. */
3925     }
3926     fromPtr = Blt_ChainGetNthLink(infoPtr->chainPtr, from);
3927     rcPtr = Blt_ChainGetValue(fromPtr);
3928 
3929     /*
3930      * ---------------------------------------------------------------
3931      *
3932      *	Reduce the span of all entries that currently cross any of the
3933      *	trailing rows/columns.  Also, if the entry starts in one of
3934      *	these rows/columns, moved it to the designated "joined"
3935      *	row/column.
3936      *
3937      * ---------------------------------------------------------------
3938      */
3939     if (infoPtr->type == rowUid) {
3940 	for (linkPtr = Blt_ChainFirstLink(tablePtr->chainPtr); linkPtr != NULL;
3941 	    linkPtr = Blt_ChainNextLink(linkPtr)) {
3942 	    entryPtr = Blt_ChainGetValue(linkPtr);
3943 	    start = entryPtr->row.rcPtr->index + 1;
3944 	    end = entryPtr->row.rcPtr->index + entryPtr->row.span - 1;
3945 	    if ((end < from) || ((start > to))) {
3946 		continue;
3947 	    }
3948 	    entryPtr->row.span -= to - start + 1;
3949 	    if (start >= from) {/* Entry starts in a trailing partition. */
3950 		entryPtr->row.rcPtr = rcPtr;
3951 	    }
3952 	}
3953     } else {
3954 	for (linkPtr = Blt_ChainFirstLink(tablePtr->chainPtr); linkPtr != NULL;
3955 	    linkPtr = Blt_ChainNextLink(linkPtr)) {
3956 	    entryPtr = Blt_ChainGetValue(linkPtr);
3957 	    start = entryPtr->column.rcPtr->index + 1;
3958 	    end = entryPtr->column.rcPtr->index + entryPtr->column.span - 1;
3959 	    if ((end < from) || ((start > to))) {
3960 		continue;
3961 	    }
3962 	    entryPtr->column.span -= to - start + 1;
3963 	    if (start >= from) {/* Entry starts in a trailing partition. */
3964 		entryPtr->column.rcPtr = rcPtr;
3965 	    }
3966 	}
3967     }
3968     linkPtr = Blt_ChainNextLink(fromPtr);
3969     for (i = from + 1; i <= to; i++) {
3970 	nextPtr = Blt_ChainNextLink(linkPtr);
3971 	rcPtr = Blt_ChainGetValue(linkPtr);
3972 	DeleteRowColumn(tablePtr, infoPtr, rcPtr);
3973 	Blt_ChainDeleteLink(infoPtr->chainPtr, linkPtr);
3974 	linkPtr = nextPtr;
3975     }
3976     i = 0;
3977     for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr);
3978 	linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
3979 	rcPtr = Blt_ChainGetValue(linkPtr);
3980 	rcPtr->index = i++;
3981     }
3982     tablePtr->flags |= REQUEST_LAYOUT;
3983     EventuallyArrangeTable(tablePtr);
3984     return TCL_OK;
3985 }
3986 
3987 /*
3988  * ----------------------------------------------------------------------------
3989  *
3990  * ExtentsOp --
3991  *
3992  *	Returns a list of all the pathnames of the widgets managed by
3993  *	a table.  The table is determined from the name of the
3994  *	container widget associated with the table.
3995  *
3996  *		table extents .frame r0 c0 container
3997  *
3998  * Results:
3999  *	Returns a standard Tcl result.  If no error occurred, TCL_OK is
4000  *	returned and a list of widgets managed by the table is left in
4001  *	interp->result.
4002  *
4003  * ----------------------------------------------------------------------------
4004  */
4005 /*ARGSUSED*/
4006 static int
ExtentsOp(dataPtr,interp,argc,argv)4007 ExtentsOp(dataPtr, interp, argc, argv)
4008     TableInterpData *dataPtr;	/* Interpreter-specific data. */
4009     Tcl_Interp *interp;		/* Interpreter to return results to. */
4010     int argc;			/* # of arguments */
4011     char **argv;		/* Command line arguments. */
4012 {
4013     Table *tablePtr;
4014     Blt_ChainLink *linkPtr;
4015     RowColumn *rcPtr;
4016     RowColumn *c1Ptr, *r1Ptr, *c2Ptr, *r2Ptr;
4017     PartitionInfo *infoPtr;
4018     int x, y, width, height;
4019     char string[200];
4020     char c;
4021 
4022     if (Blt_GetTable(dataPtr, interp, argv[2], &tablePtr) != TCL_OK) {
4023 	return TCL_ERROR;
4024     }
4025     c = tolower(argv[3][0]);
4026     if (c == 'r') {
4027 	infoPtr = &(tablePtr->rowInfo);
4028     } else if (c == 'c') {
4029 	infoPtr = &(tablePtr->columnInfo);
4030     } else {
4031 	Tcl_AppendResult(interp, "unknown item \"", argv[3],
4032 	    "\": should be widget, row, or column", (char *)NULL);
4033 	return TCL_ERROR;
4034     }
4035     for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr);
4036 	linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
4037 	rcPtr = Blt_ChainGetValue(linkPtr);
4038 	sprintf(string, "%c%d", argv[3][0], rcPtr->index);
4039 	if (Tcl_StringMatch(string, argv[3])) {
4040 	    if (c == 'r') {
4041 		r1Ptr = r2Ptr = rcPtr;
4042 		c1Ptr = GetRowColumn(&(tablePtr->columnInfo), 0);
4043 		c2Ptr = GetRowColumn(&(tablePtr->columnInfo),
4044 				     tablePtr->nColumns - 1);
4045 	    } else {
4046 		c1Ptr = c2Ptr = rcPtr;
4047 		r1Ptr = GetRowColumn(&(tablePtr->rowInfo), 0);
4048 		r2Ptr = GetRowColumn(&(tablePtr->rowInfo),
4049 				     tablePtr->nRows - 1);
4050 	    }
4051 	    x = c1Ptr->offset;
4052 	    y = r1Ptr->offset;
4053 	    width = c2Ptr->offset + c2Ptr->size - x;
4054 	    height = r2Ptr->offset + r2Ptr->size - y;
4055 	    sprintf(string, "%c%d %d %d %d %d\n", argv[3][0], rcPtr->index,
4056 		x, y, width, height);
4057 	    Tcl_AppendResult(interp, string, (char *)NULL);
4058 	}
4059     }
4060     return TCL_OK;
4061 }
4062 
4063 /*
4064  * ----------------------------------------------------------------------------
4065  *
4066  * ForgetOp --
4067  *
4068  *	Processes an argv/argc list of widget names and purges their
4069  *	entries from their respective tables.  The widgets are unmapped and
4070  *	the tables are rearranged at the next idle point.  Note that all
4071  *	the named widgets do not need to exist in the same table.
4072  *
4073  * Results:
4074  *	Returns a standard Tcl result.  If an error occurred, TCL_ERROR is
4075  *	returned and an error message is left in interp->result.
4076  *
4077  * Side Effects:
4078  *	Memory is deallocated (the entry is destroyed), etc.  The
4079  *	affected tables are is re-computed and arranged at the next idle
4080  *	point.
4081  *
4082  * ----------------------------------------------------------------------------
4083  */
4084 static int
ForgetOp(dataPtr,interp,argc,argv)4085 ForgetOp(dataPtr, interp, argc, argv)
4086     TableInterpData *dataPtr;	/* Interpreter-specific data. */
4087     Tcl_Interp *interp;
4088     int argc;
4089     char **argv;
4090 {
4091     Entry *entryPtr;
4092     register int i;
4093     Blt_HashEntry *hPtr;
4094     Blt_HashSearch cursor;
4095     Table *tablePtr;
4096     Tk_Window tkwin, mainWindow;
4097 
4098     tablePtr = NULL;
4099     mainWindow = Tk_MainWindow(interp);
4100     for (i = 2; i < argc; i++) {
4101 	entryPtr = NULL;
4102 	tkwin = Tk_NameToWindow(interp, argv[i], mainWindow);
4103 	if (tkwin == NULL) {
4104 	    return TCL_ERROR;
4105 	}
4106 	for (hPtr = Blt_FirstHashEntry(&(dataPtr->tableTable), &cursor);
4107 	    hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
4108 	    tablePtr = (Table *)Blt_GetHashValue(hPtr);
4109 	    if (tablePtr->interp != interp) {
4110 		continue;
4111 	    }
4112 	    entryPtr = FindEntry(tablePtr, tkwin);
4113 	    if (entryPtr != NULL) {
4114 		break;
4115 	    }
4116 	}
4117 	if (entryPtr == NULL) {
4118 	    Tcl_AppendResult(interp, "\"", argv[i],
4119 		"\" is not managed by any table", (char *)NULL);
4120 	    return TCL_ERROR;
4121 	}
4122 	if (Tk_IsMapped(entryPtr->tkwin)) {
4123 	    Tk_UnmapWindow(entryPtr->tkwin);
4124 	}
4125 	/* Arrange for the call back here in the loop, because the
4126 	 * widgets may not belong to the same table.  */
4127 	tablePtr->flags |= REQUEST_LAYOUT;
4128 	EventuallyArrangeTable(tablePtr);
4129 	DestroyEntry(entryPtr);
4130     }
4131     return TCL_OK;
4132 }
4133 
4134 /*
4135  * ----------------------------------------------------------------------------
4136  *
4137  * InfoOp --
4138  *
4139  *	Returns the options of a widget or partition in the table.
4140  *
4141  * Results:
4142  *	Returns a standard Tcl result.  A list of the widget attributes
4143  *	is left in interp->result.
4144  *
4145  * ----------------------------------------------------------------------------
4146  */
4147 /*ARGSUSED*/
4148 static int
InfoOp(dataPtr,interp,argc,argv)4149 InfoOp(dataPtr, interp, argc, argv)
4150     TableInterpData *dataPtr;	/* Interpreter-specific data. */
4151     Tcl_Interp *interp;
4152     int argc;
4153     char **argv;
4154 {
4155     Table *tablePtr;
4156     int result;
4157     char c;
4158     register int i;
4159 
4160     if (Blt_GetTable(dataPtr, interp, argv[2], &tablePtr) != TCL_OK) {
4161 	return TCL_ERROR;
4162     }
4163     for (i = 3; i < argc; i++) {
4164 	c = argv[i][0];
4165 	if (c == '.') {		/* Entry information */
4166 	    Entry *entryPtr;
4167 
4168 	    if (GetEntry(interp, tablePtr, argv[i], &entryPtr) != TCL_OK) {
4169 		return TCL_ERROR;
4170 	    }
4171 	    result = InfoEntry(interp, tablePtr, entryPtr);
4172 	} else if ((c == 'r') || (c == 'R') || (c == 'c') || (c == 'C')) {
4173 	    result = InfoRowColumn(tablePtr, interp, argv[i]);
4174 	} else {
4175 	    Tcl_AppendResult(interp, "unknown item \"", argv[i],
4176 		"\": should be widget, row, or column", (char *)NULL);
4177 	    return TCL_ERROR;
4178 	}
4179 	if (result != TCL_OK) {
4180 	    return TCL_ERROR;
4181 	}
4182 	if ((i + 1) < argc) {
4183 	    Tcl_AppendResult(interp, "\n", (char *)NULL);
4184 	}
4185     }
4186     return TCL_OK;
4187 }
4188 
4189 /*
4190  * ----------------------------------------------------------------------------
4191  *
4192  * InsertOp --
4193  *
4194  *	Inserts a span of rows/columns into the table.
4195  *
4196  *		table insert .f r0 2
4197  *		table insert .f c0 5
4198  *
4199  * Results:
4200  *	Returns a standard Tcl result.  A list of the widget
4201  *	attributes is left in interp->result.
4202  *
4203  * ----------------------------------------------------------------------------
4204  */
4205 /*ARGSUSED*/
4206 static int
InsertOp(dataPtr,interp,argc,argv)4207 InsertOp(dataPtr, interp, argc, argv)
4208     TableInterpData *dataPtr;	/* Interpreter-specific data. */
4209     Tcl_Interp *interp;
4210     int argc;
4211     char **argv;
4212 {
4213     Table *tablePtr;
4214     long int span;
4215     int before;
4216     PartitionInfo *infoPtr;
4217     RowColumn *rcPtr;
4218     register int i;
4219     Blt_ChainLink *beforePtr, *linkPtr;
4220     int linkBefore;
4221 
4222     if (Blt_GetTable(dataPtr, interp, argv[2], &tablePtr) != TCL_OK) {
4223 	return TCL_ERROR;
4224     }
4225     linkBefore = TRUE;
4226     if (argv[3][0] == '-') {
4227 	if (strcmp(argv[3], "-before") == 0) {
4228 	    linkBefore = TRUE;
4229 	    argv++; argc--;
4230 	} else if (strcmp(argv[3], "-after") == 0) {
4231 	    linkBefore = FALSE;
4232 	    argv++; argc--;
4233 	}
4234     }
4235     if (argc == 3) {
4236 	Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
4237 			 "insert ", argv[2], "row|column ?span?", (char *)NULL);
4238 	return TCL_ERROR;
4239     }
4240     infoPtr = ParseRowColumn(tablePtr, argv[3], &before);
4241     if (infoPtr == NULL) {
4242 	return TCL_ERROR;
4243     }
4244     span = 1;
4245     if ((argc > 4) && (Tcl_ExprLong(interp, argv[4], &span) != TCL_OK)) {
4246 	return TCL_ERROR;
4247     }
4248     if (span < 1) {
4249 	Tcl_AppendResult(interp, "span value \"", argv[4],
4250 	    "\" can't be negative", (char *)NULL);
4251 	return TCL_ERROR;
4252     }
4253     beforePtr = Blt_ChainGetNthLink(infoPtr->chainPtr, before);
4254     /*
4255      * Insert the new rows/columns from the designated point in the
4256      * chain.
4257      */
4258     for (i = 0; i < span; i++) {
4259 	rcPtr = CreateRowColumn();
4260 	linkPtr = Blt_ChainNewLink();
4261 	Blt_ChainSetValue(linkPtr, rcPtr);
4262 	if (linkBefore) {
4263 	    Blt_ChainLinkBefore(infoPtr->chainPtr, linkPtr, beforePtr);
4264 	} else {
4265 	    Blt_ChainLinkAfter(infoPtr->chainPtr, linkPtr, beforePtr);
4266 	}
4267 	rcPtr->linkPtr = linkPtr;
4268     }
4269     i = 0;
4270     for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr); linkPtr != NULL;
4271 	linkPtr = Blt_ChainNextLink(linkPtr)) {
4272 	rcPtr = Blt_ChainGetValue(linkPtr);
4273 	/* Reset the indices of the trailing rows/columns.  */
4274 	rcPtr->index = i++;
4275     }
4276     tablePtr->flags |= REQUEST_LAYOUT;
4277     EventuallyArrangeTable(tablePtr);
4278     return TCL_OK;
4279 }
4280 
4281 /*
4282  * ----------------------------------------------------------------------------
4283  *
4284  * SplitOp --
4285  *
4286  *	Splits a single row/column into multiple partitions. Any
4287  *	widgets that span this row/column will be automatically
4288  *	corrected to include the new rows/columns.
4289  *
4290  *		table split .f r0 3
4291  *		table split .f c2 2
4292  * Results:
4293  *	Returns a standard Tcl result.  A list of the widget
4294  *	attributes is left in interp->result.
4295  *
4296  * ----------------------------------------------------------------------------
4297  */
4298 /*ARGSUSED*/
4299 static int
SplitOp(dataPtr,interp,argc,argv)4300 SplitOp(dataPtr, interp, argc, argv)
4301     TableInterpData *dataPtr;	/* Interpreter-specific data. */
4302     Tcl_Interp *interp;
4303     int argc;
4304     char **argv;
4305 {
4306     Table *tablePtr;
4307     int number, split;
4308     int start, end;
4309     PartitionInfo *infoPtr;
4310     RowColumn *rcPtr;
4311     register int i;
4312     Blt_ChainLink *afterPtr, *linkPtr;
4313     Entry *entryPtr;
4314 
4315     if (Blt_GetTable(dataPtr, interp, argv[2], &tablePtr) != TCL_OK) {
4316 	return TCL_ERROR;
4317     }
4318     infoPtr = ParseRowColumn(tablePtr, argv[3], &number);
4319     if (infoPtr == NULL) {
4320 	return TCL_ERROR;
4321     }
4322     split = 2;
4323     if (argc > 4) {
4324 	if (Tcl_GetInt(interp, argv[4], &split) != TCL_OK) {
4325 	    return TCL_ERROR;
4326 	}
4327     }
4328     if (split < 2) {
4329 	Tcl_AppendResult(interp, "bad split value \"", argv[4],
4330 	    "\": should be 2 or greater", (char *)NULL);
4331 	return TCL_ERROR;
4332     }
4333     afterPtr = Blt_ChainGetNthLink(infoPtr->chainPtr, number);
4334 
4335     /*
4336      * Append (split - 1) additional rows/columns starting
4337      * from the current point in the chain.
4338      */
4339 
4340     for (i = 1; i < split; i++) {
4341 	rcPtr = CreateRowColumn();
4342 	linkPtr = Blt_ChainNewLink();
4343 	Blt_ChainSetValue(linkPtr, rcPtr);
4344 	Blt_ChainLinkAfter(infoPtr->chainPtr, linkPtr, afterPtr);
4345 	rcPtr->linkPtr = linkPtr;
4346     }
4347 
4348     /*
4349      * Also increase the span of all entries that span this
4350      * row/column by split - 1.
4351      */
4352     if (infoPtr->type == rowUid) {
4353 	for (linkPtr = Blt_ChainFirstLink(tablePtr->chainPtr); linkPtr != NULL;
4354 	    linkPtr = Blt_ChainNextLink(linkPtr)) {
4355 	    entryPtr = Blt_ChainGetValue(linkPtr);
4356 	    start = entryPtr->row.rcPtr->index;
4357 	    end = entryPtr->row.rcPtr->index + entryPtr->row.span;
4358 	    if ((start <= number) && (number < end)) {
4359 		entryPtr->row.span += (split - 1);
4360 	    }
4361 	}
4362     } else {
4363 	for (linkPtr = Blt_ChainFirstLink(tablePtr->chainPtr); linkPtr != NULL;
4364 	    linkPtr = Blt_ChainNextLink(linkPtr)) {
4365 	    entryPtr = Blt_ChainGetValue(linkPtr);
4366 	    start = entryPtr->column.rcPtr->index;
4367 	    end = entryPtr->column.rcPtr->index + entryPtr->column.span;
4368 	    if ((start <= number) && (number < end)) {
4369 		entryPtr->column.span += (split - 1);
4370 	    }
4371 	}
4372     }
4373     /*
4374      * Be careful to renumber the rows or columns only after
4375      * processing each entry.  Otherwise row/column numbering
4376      * will be out of sync with the index.
4377      */
4378     i = number;
4379     for (linkPtr = afterPtr; linkPtr != NULL;
4380 	linkPtr = Blt_ChainNextLink(linkPtr)) {
4381 	rcPtr = Blt_ChainGetValue(linkPtr);
4382 	rcPtr->index = i++;	/* Renumber the trailing indices.  */
4383     }
4384 
4385     tablePtr->flags |= REQUEST_LAYOUT;
4386     EventuallyArrangeTable(tablePtr);
4387     return TCL_OK;
4388 }
4389 
4390 /*
4391  * ----------------------------------------------------------------------
4392  *
4393  * RowColumnSearch --
4394  *
4395  * 	Searches for the row or column designated by an x or y
4396  *	coordinate.
4397  *
4398  * Results:
4399  *	Returns a pointer to the row/column containing the given point.
4400  *	If no row/column contains the coordinate, NULL is returned.
4401  *
4402  * ----------------------------------------------------------------------
4403  */
4404 static RowColumn *
RowColumnSearch(infoPtr,x)4405 RowColumnSearch(infoPtr, x)
4406     PartitionInfo *infoPtr;
4407     int x;			/* Search coordinate  */
4408 {
4409     Blt_ChainLink *linkPtr;
4410     RowColumn *rcPtr;
4411 
4412     for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr);
4413 	linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
4414 	rcPtr = Blt_ChainGetValue(linkPtr);
4415 	if (x > (rcPtr->offset + rcPtr->size)) {
4416 	    break;		/* Too far, can't find row/column. */
4417 	}
4418 	if (x > rcPtr->offset) {
4419 	    return rcPtr;
4420 	}
4421     }
4422     return NULL;
4423 }
4424 
4425 /*
4426  *----------------------------------------------------------------------
4427  *
4428  * LocateOp --
4429  *
4430  *
4431  *	Returns the row,column index given a screen coordinate.
4432  *
4433  * Results:
4434  *	Returns a standard Tcl result.
4435  *
4436  *----------------------------------------------------------------------
4437  */
4438 /* ARGSUSED */
4439 static int
LocateOp(dataPtr,interp,argc,argv)4440 LocateOp(dataPtr, interp, argc, argv)
4441     TableInterpData *dataPtr;	/* Interpreter-specific data. */
4442     Tcl_Interp *interp;
4443     int argc;
4444     char **argv;
4445 {
4446     int x, y;
4447     RowColumn *rowPtr, *columnPtr;
4448     Table *tablePtr;
4449 
4450     if (Blt_GetTable(dataPtr, interp, argv[2], &tablePtr) != TCL_OK) {
4451 	return TCL_ERROR;
4452     }
4453     if (Blt_GetPixels(interp, tablePtr->tkwin, argv[3], PIXELS_ANY, &x)
4454 	!= TCL_OK) {
4455 	return TCL_ERROR;
4456     }
4457     if (Blt_GetPixels(interp, tablePtr->tkwin, argv[4], PIXELS_ANY, &y)
4458 	!= TCL_OK) {
4459 	return TCL_ERROR;
4460     }
4461     rowPtr = RowColumnSearch(&(tablePtr->rowInfo), y);
4462     if (rowPtr == NULL) {
4463 	return TCL_OK;
4464     }
4465     columnPtr = RowColumnSearch(&(tablePtr->columnInfo), x);
4466     if (columnPtr == NULL) {
4467 	return TCL_OK;
4468     }
4469     Tcl_AppendElement(interp, Blt_Itoa(rowPtr->index));
4470     Tcl_AppendElement(interp, Blt_Itoa(columnPtr->index));
4471     return TCL_OK;
4472 }
4473 
4474 /*
4475  * ----------------------------------------------------------------------------
4476  *
4477  * ContainersOp --
4478  *
4479  *	Returns a list of tables currently in use. A table is
4480  *	associated by the name of its container widget.  All tables
4481  *	matching a given pattern are included in this list.  If no
4482  *	pattern is present (argc == 0), all tables are included.
4483  *
4484  * Results:
4485  *	Returns a standard Tcl result.  If no error occurred, TCL_OK is
4486  *	returned and a list of tables is left in interp->result.
4487  *
4488  * ----------------------------------------------------------------------------
4489  */
4490 /*ARGSUSED*/
4491 static int
ContainersOp(dataPtr,interp,argc,argv)4492 ContainersOp(dataPtr, interp, argc, argv)
4493     TableInterpData *dataPtr;	/* Interpreter-specific data. */
4494     Tcl_Interp *interp;		/* Interpreter to return list of names to */
4495     int argc;
4496     char **argv;		/* Contains 0-1 arguments: search pattern */
4497 {
4498     Blt_HashEntry *hPtr;
4499     Blt_HashSearch cursor;
4500     register Table *tablePtr;
4501     char *pattern;
4502 
4503     pattern = NULL;
4504     if (argc > 2) {
4505 	if (argv[2][0] == '-') {
4506 	    unsigned int length;
4507 
4508 	    length = strlen(argv[2]);
4509 	    if ((length > 1) && (argv[2][1] == 'p') &&
4510 		(strncmp(argv[2], "-pattern", length) == 0)) {
4511 		pattern = argv[3];
4512 		goto search;
4513 	    } else if ((length > 1) && (argv[2][1] == 's') &&
4514 		(strncmp(argv[2], "-slave", length) == 0)) {
4515 		Tk_Window tkwin;
4516 
4517 		if (argc != 4) {
4518 		    Tcl_AppendResult(interp, "needs widget argument for \"",
4519 			argv[2], "\"", (char *)NULL);
4520 		    return TCL_ERROR;
4521 		}
4522 		tkwin = Tk_NameToWindow(interp, argv[3],
4523 		    Tk_MainWindow(interp));
4524 		if (tkwin == NULL) {
4525 		    return TCL_ERROR;
4526 		}
4527 		for (hPtr = Blt_FirstHashEntry(&(dataPtr->tableTable), &cursor);
4528 		    hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
4529 		    tablePtr = (Table *)Blt_GetHashValue(hPtr);
4530 		    if (FindEntry(tablePtr, tkwin) != NULL) {
4531 			Tcl_AppendElement(interp, Tk_PathName(tablePtr->tkwin));
4532 		    }
4533 		}
4534 		return TCL_OK;
4535 	    } else {
4536 		Tcl_AppendResult(interp, "bad switch \"", argv[2], "\" : \
4537 should be \"-pattern\", or \"-slave\"", (char *)NULL);
4538 		return TCL_ERROR;
4539 	    }
4540 	} else {
4541 	    pattern = argv[2];
4542 	}
4543     }
4544   search:
4545     for (hPtr = Blt_FirstHashEntry(&(dataPtr->tableTable), &cursor);
4546 	hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
4547 	tablePtr = (Table *)Blt_GetHashValue(hPtr);
4548 	if (tablePtr->interp == interp) {
4549 	    if ((pattern == NULL) ||
4550 		(Tcl_StringMatch(Tk_PathName(tablePtr->tkwin), pattern))) {
4551 		Tcl_AppendElement(interp, Tk_PathName(tablePtr->tkwin));
4552 	    }
4553 	}
4554     }
4555     return TCL_OK;
4556 }
4557 
4558 /*
4559  * ----------------------------------------------------------------------------
4560  *
4561  * SaveOp --
4562  *
4563  *	Returns a list of all the commands necessary to rebuild the
4564  *	the table.  This includes the layout of the widgets and any
4565  *	row, column, or table options set.
4566  *
4567  * Results:
4568  *	Returns a standard Tcl result.  If no error occurred, TCL_OK is
4569  *	returned and a list of widget path names is left in interp->result.
4570  *
4571  * ----------------------------------------------------------------------------
4572  */
4573 /*ARGSUSED*/
4574 static int
SaveOp(dataPtr,interp,argc,argv)4575 SaveOp(dataPtr, interp, argc, argv)
4576     TableInterpData *dataPtr;	/* Interpreter-specific data. */
4577     Tcl_Interp *interp;
4578     int argc;
4579     char **argv;
4580 {
4581     Table *tablePtr;
4582     Blt_ChainLink *linkPtr, *lastPtr;
4583     Entry *entryPtr;
4584     PartitionInfo *infoPtr;
4585     RowColumn *rcPtr;
4586     Tcl_DString dString;
4587     int start, last;
4588 
4589     if (Blt_GetTable(dataPtr, interp, argv[2], &tablePtr) != TCL_OK) {
4590 	return TCL_ERROR;
4591     }
4592     Tcl_DStringInit(&dString);
4593     Tcl_DStringAppend(&dString, "\n# Table widget layout\n\n", -1);
4594     Tcl_DStringAppend(&dString, argv[0], -1);
4595     Tcl_DStringAppend(&dString, " ", -1);
4596     Tcl_DStringAppend(&dString, Tk_PathName(tablePtr->tkwin), -1);
4597     Tcl_DStringAppend(&dString, " \\\n", -1);
4598     lastPtr = Blt_ChainLastLink(tablePtr->chainPtr);
4599     for (linkPtr = Blt_ChainFirstLink(tablePtr->chainPtr); linkPtr != NULL;
4600 	linkPtr = Blt_ChainNextLink(linkPtr)) {
4601 	entryPtr = Blt_ChainGetValue(linkPtr);
4602 	PrintEntry(entryPtr, &dString);
4603 	if (linkPtr != lastPtr) {
4604 	    Tcl_DStringAppend(&dString, " \\\n", -1);
4605 	}
4606     }
4607     Tcl_DStringAppend(&dString, "\n\n# Row configuration options\n\n", -1);
4608     infoPtr = &(tablePtr->rowInfo);
4609     for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr); linkPtr != NULL;
4610 	linkPtr = Blt_ChainNextLink(linkPtr)) {
4611 	rcPtr = Blt_ChainGetValue(linkPtr);
4612 	start = Tcl_DStringLength(&dString);
4613 	Tcl_DStringAppend(&dString, argv[0], -1);
4614 	Tcl_DStringAppend(&dString, " configure ", -1);
4615 	Tcl_DStringAppend(&dString, Tk_PathName(tablePtr->tkwin), -1);
4616 	Tcl_DStringAppend(&dString, " r", -1);
4617 	Tcl_DStringAppend(&dString, Blt_Itoa(rcPtr->index), -1);
4618 	last = Tcl_DStringLength(&dString);
4619 	PrintRowColumn(interp, infoPtr, rcPtr, &dString);
4620 	if (Tcl_DStringLength(&dString) == last) {
4621 	    Tcl_DStringSetLength(&dString, start);
4622 	} else {
4623 	    Tcl_DStringAppend(&dString, "\n", -1);
4624 	}
4625     }
4626     Tcl_DStringAppend(&dString, "\n\n# Column configuration options\n\n", -1);
4627     infoPtr = &(tablePtr->columnInfo);
4628     for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr); linkPtr != NULL;
4629 	linkPtr = Blt_ChainNextLink(linkPtr)) {
4630 	rcPtr = Blt_ChainGetValue(linkPtr);
4631 	start = Tcl_DStringLength(&dString);
4632 	Tcl_DStringAppend(&dString, argv[0], -1);
4633 	Tcl_DStringAppend(&dString, " configure ", -1);
4634 	Tcl_DStringAppend(&dString, Tk_PathName(tablePtr->tkwin), -1);
4635 	Tcl_DStringAppend(&dString, " c", -1);
4636 	Tcl_DStringAppend(&dString, Blt_Itoa(rcPtr->index), -1);
4637 	last = Tcl_DStringLength(&dString);
4638 	PrintRowColumn(interp, infoPtr, rcPtr, &dString);
4639 	if (Tcl_DStringLength(&dString) == last) {
4640 	    Tcl_DStringSetLength(&dString, start);
4641 	} else {
4642 	    Tcl_DStringAppend(&dString, "\n", -1);
4643 	}
4644     }
4645     start = Tcl_DStringLength(&dString);
4646     Tcl_DStringAppend(&dString, "\n\n# Table configuration options\n\n", -1);
4647     Tcl_DStringAppend(&dString, argv[0], -1);
4648     Tcl_DStringAppend(&dString, " configure ", -1);
4649     Tcl_DStringAppend(&dString, Tk_PathName(tablePtr->tkwin), -1);
4650     last = Tcl_DStringLength(&dString);
4651     PrintTable(tablePtr, &dString);
4652     if (Tcl_DStringLength(&dString) == last) {
4653 	Tcl_DStringSetLength(&dString, start);
4654     } else {
4655 	Tcl_DStringAppend(&dString, "\n", -1);
4656     }
4657     Tcl_DStringResult(interp, &dString);
4658     return TCL_OK;
4659 }
4660 
4661 /*
4662  * ----------------------------------------------------------------------------
4663  *
4664  * SearchOp --
4665  *
4666  *	Returns a list of all the pathnames of the widgets managed by
4667  *	a table geometry manager.  The table is given by the path name of a
4668  *	container widget associated with the table.
4669  *
4670  * Results:
4671  *	Returns a standard Tcl result.  If no error occurred, TCL_OK is
4672  *	returned and a list of widget path names is left in interp->result.
4673  *
4674  * ----------------------------------------------------------------------------
4675  */
4676 /*ARGSUSED*/
4677 static int
SearchOp(dataPtr,interp,argc,argv)4678 SearchOp(dataPtr, interp, argc, argv)
4679     TableInterpData *dataPtr;	/* Interpreter-specific data. */
4680     Tcl_Interp *interp;		/* Interpreter to return list of names to */
4681     int argc;			/* Number of arguments */
4682     char **argv;		/* Contains 1-2 arguments: pathname of container
4683 				 * widget associated with the table and search
4684 				 * pattern */
4685 {
4686     Table *tablePtr;
4687     Blt_ChainLink *linkPtr;
4688     Entry *entryPtr;
4689     int rspan, cspan, rstart, cstart;
4690     char *pattern;
4691     char c;
4692     int flags;
4693     register int i;
4694 
4695 #define	MATCH_PATTERN		(1<<0)	/* Find widgets whose path names
4696 					 * match a given pattern */
4697 #define	MATCH_INDEX_SPAN	(1<<1)	/* Find widgets that span index  */
4698 #define	MATCH_INDEX_START	(1<<2)	/* Find widgets that start at index */
4699 
4700 
4701     if (Blt_GetTable(dataPtr, interp, argv[2], &tablePtr) != TCL_OK) {
4702 	return TCL_ERROR;
4703     }
4704     flags = 0;
4705     pattern = NULL;
4706     rspan = cspan = rstart = cstart = 0;
4707 
4708     /* Parse switches and arguments first */
4709     for (i = 3; i < argc; i += 2) {
4710 	if (argv[i][0] == '-') {
4711 	    unsigned int length;
4712 
4713 	    if ((i + 1) == argc) {
4714 		Tcl_AppendResult(interp, "switch \"", argv[i], "\" needs value",
4715 		    (char *)NULL);
4716 		return TCL_ERROR;
4717 	    }
4718 	    length = strlen(argv[i]);
4719 	    c = argv[i][1];
4720 	    if ((c == 'p') && (length > 1) &&
4721 		(strncmp(argv[3], "-pattern", length) == 0)) {
4722 		flags |= MATCH_PATTERN;
4723 		pattern = argv[4];
4724 	    } else if ((c == 's') && (length > 2) &&
4725 		(strncmp(argv[i], "-start", length) == 0)) {
4726 		flags |= MATCH_INDEX_START;
4727 		if (ParseItem(tablePtr, argv[i + 1],
4728 			&rstart, &cstart) != TCL_OK) {
4729 		    return TCL_ERROR;
4730 		}
4731 	    } else if ((c == 's') && (length > 2) &&
4732 		(strncmp(argv[i], "-span", length) == 0)) {
4733 		flags |= MATCH_INDEX_SPAN;
4734 		if (ParseItem(tablePtr, argv[4],
4735 			&rspan, &cspan) != TCL_OK) {
4736 		    return TCL_ERROR;
4737 		}
4738 	    } else {
4739 		Tcl_AppendResult(interp, "bad switch \"", argv[3], "\" : \
4740 should be \"-pattern\", \"-span\", or \"-start\"", (char *)NULL);
4741 		return TCL_ERROR;
4742 	    }
4743 	} else {
4744 	    if ((i + 1) == argc) {
4745 		pattern = argv[i];
4746 		flags |= MATCH_PATTERN;
4747 	    }
4748 	}
4749     }
4750 
4751     /* Then try to match entries with the search criteria */
4752 
4753     for (linkPtr = Blt_ChainFirstLink(tablePtr->chainPtr); linkPtr != NULL;
4754 	linkPtr = Blt_ChainNextLink(linkPtr)) {
4755 	entryPtr = Blt_ChainGetValue(linkPtr);
4756 	if ((flags & MATCH_PATTERN) && (pattern != NULL)) {
4757 	    if (Tcl_StringMatch(Tk_PathName(entryPtr->tkwin), pattern)) {
4758 		goto match;
4759 	    }
4760 	}
4761 	if (flags & MATCH_INDEX_SPAN) {
4762 	    if ((rspan >= 0) && ((entryPtr->row.rcPtr->index <= rspan) ||
4763 		    ((entryPtr->row.rcPtr->index + entryPtr->row.span) > rspan))) {
4764 		goto match;
4765 	    }
4766 	    if ((cspan >= 0) && ((entryPtr->column.rcPtr->index <= cspan) ||
4767 		    ((entryPtr->column.rcPtr->index + entryPtr->column.span)
4768 			> cspan))) {
4769 		goto match;
4770 	    }
4771 	}
4772 	if (flags & MATCH_INDEX_START) {
4773 	    if ((rstart >= 0) && (entryPtr->row.rcPtr->index == rstart)) {
4774 		goto match;
4775 	    }
4776 	    if ((cstart >= 0) && (entryPtr->column.rcPtr->index == cstart)) {
4777 		goto match;
4778 	    }
4779 	}
4780 	continue;
4781       match:
4782 	Tcl_AppendElement(interp, Tk_PathName(entryPtr->tkwin));
4783     }
4784     return TCL_OK;
4785 }
4786 
4787 /*
4788  * ----------------------------------------------------------------------------
4789  *
4790  * Table operations.
4791  *
4792  * The fields for Blt_OpSpec are as follows:
4793  *
4794  *   - operation name
4795  *   - minimum number of characters required to disambiguate the operation name.
4796  *   - function associated with operation.
4797  *   - minimum number of arguments required.
4798  *   - maximum number of arguments allowed (0 indicates no limit).
4799  *   - usage string
4800  *
4801  * ----------------------------------------------------------------------------
4802  */
4803 static Blt_OpSpec operSpecs[] =
4804 {
4805     {"arrange", 1, (Blt_Op)ArrangeOp, 3, 3, "container",},
4806     {"cget", 2, (Blt_Op)CgetOp, 4, 5,
4807 	"container ?row|column|widget? option",},
4808     {"configure", 3, (Blt_Op)ConfigureOp, 3, 0,
4809 	"container ?row|column|widget?... ?option value?...",},
4810     {"containers", 3, (Blt_Op)ContainersOp, 2, 4, "?switch? ?arg?",},
4811     {"delete", 1, (Blt_Op)DeleteOp, 3, 0,
4812 	"container row|column ?row|column?",},
4813     {"extents", 1, (Blt_Op)ExtentsOp, 4, 4,
4814 	"container row|column|widget",},
4815     {"forget", 1, (Blt_Op)ForgetOp, 3, 0, "widget ?widget?...",},
4816     {"info", 3, (Blt_Op)InfoOp, 3, 0,
4817 	"container ?row|column|widget?...",},
4818     {"insert", 3, (Blt_Op)InsertOp, 4, 6,
4819 	"container ?-before|-after? row|column ?count?",},
4820     {"join", 1, (Blt_Op)JoinOp, 5, 5, "container first last",},
4821     {"locate", 2, (Blt_Op)LocateOp, 5, 5, "container x y",},
4822     {"save", 2, (Blt_Op)SaveOp, 3, 3, "container",},
4823     {"search", 2, (Blt_Op)SearchOp, 3, 0, "container ?switch arg?...",},
4824     {"split", 2, (Blt_Op)SplitOp, 4, 5, "container row|column div",},
4825 };
4826 
4827 static int nSpecs = sizeof(operSpecs) / sizeof(Blt_OpSpec);
4828 
4829 /*
4830  * ----------------------------------------------------------------------------
4831  *
4832  * TableCmd --
4833  *
4834  *	This procedure is invoked to process the Tcl command that
4835  *	corresponds to the table geometry manager.  See the user
4836  *	documentation for details on what it does.
4837  *
4838  * Results:
4839  *	A standard Tcl result.
4840  *
4841  * Side effects:
4842  *	See the user documentation.
4843  *
4844  * ----------------------------------------------------------------------------
4845  */
4846 static int
TableCmd(clientData,interp,argc,argv)4847 TableCmd(clientData, interp, argc, argv)
4848     ClientData clientData;	/* Interpreter-specific data. */
4849     Tcl_Interp *interp;
4850     int argc;
4851     char **argv;
4852 {
4853     TableInterpData *dataPtr = clientData;
4854     Blt_Op proc;
4855     int result;
4856 
4857     if ((argc > 1) && (argv[1][0] == '.')) {
4858 	Table *tablePtr;
4859 
4860 	if (Blt_GetTable(clientData, interp, argv[1], &tablePtr) != TCL_OK) {
4861 	    Tcl_ResetResult(interp);
4862 	    tablePtr = CreateTable(dataPtr, interp, argv[1]);
4863 	    if (tablePtr == NULL) {
4864 		return TCL_ERROR;
4865 	    }
4866 	}
4867 	return BuildTable(tablePtr, interp, argc, argv);
4868     }
4869     proc = Blt_GetOp(interp, nSpecs, operSpecs, BLT_OP_ARG1, argc, argv, 0);
4870     if (proc == NULL) {
4871 	return TCL_ERROR;
4872     }
4873     result = (*proc) (dataPtr, interp, argc, argv);
4874     return result;
4875 }
4876 
4877 
4878 /*
4879  * -----------------------------------------------------------------------
4880  *
4881  * TableInterpDeleteProc --
4882  *
4883  *	This is called when the interpreter hosting the table command
4884  *	is destroyed.
4885  *
4886  * Results:
4887  *	None.
4888  *
4889  * Side effects:
4890  *	Destroys all the hash table maintaining the names of the table
4891  *	geomtry managers.
4892  *
4893  * ------------------------------------------------------------------------
4894  */
4895 /* ARGSUSED */
4896 static void
TableInterpDeleteProc(clientData,interp)4897 TableInterpDeleteProc(clientData, interp)
4898     ClientData clientData;	/* Thread-specific data. */
4899     Tcl_Interp *interp;
4900 {
4901     TableInterpData *dataPtr = clientData;
4902     Blt_HashEntry *hPtr;
4903     Blt_HashSearch cursor;
4904     Table *tablePtr;
4905 
4906     for (hPtr = Blt_FirstHashEntry(&(dataPtr->tableTable), &cursor);
4907 	 hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
4908 	tablePtr = (Table *)Blt_GetHashValue(hPtr);
4909 	tablePtr->hashPtr = NULL;
4910 	DestroyTable((DestroyData)tablePtr);
4911     }
4912     Blt_DeleteHashTable(&(dataPtr->tableTable));
4913     Tcl_DeleteAssocData(interp, TABLE_THREAD_KEY);
4914     Blt_Free(dataPtr);
4915 }
4916 
4917 static TableInterpData *
GetTableInterpData(interp)4918 GetTableInterpData(interp)
4919      Tcl_Interp *interp;
4920 {
4921     TableInterpData *dataPtr;
4922     Tcl_InterpDeleteProc *proc;
4923 
4924     dataPtr = (TableInterpData *)
4925 	Tcl_GetAssocData(interp, TABLE_THREAD_KEY, &proc);
4926     if (dataPtr == NULL) {
4927 	dataPtr = Blt_Malloc(sizeof(TableInterpData));
4928 	assert(dataPtr);
4929 	Tcl_SetAssocData(interp, TABLE_THREAD_KEY, TableInterpDeleteProc,
4930 		dataPtr);
4931 	Blt_InitHashTable(&(dataPtr->tableTable), BLT_ONE_WORD_KEYS);
4932     }
4933     return dataPtr;
4934 }
4935 
4936 
4937 /*
4938  * ----------------------------------------------------------------------------
4939  *
4940  * Blt_TableInit --
4941  *
4942  *	This procedure is invoked to initialize the Tcl command that
4943  *	corresponds to the table geometry manager.
4944  *
4945  * Results:
4946  *	None.
4947  *
4948  * Side effects:
4949  *	Creates the new command and adds an entry into a global Tcl
4950  *	associative array.
4951  *
4952  * ---------------------------------------------------------------------------
4953  */
4954 int
Blt_TableInit(interp)4955 Blt_TableInit(interp)
4956     Tcl_Interp *interp;
4957 {
4958     static Blt_CmdSpec cmdSpec = {"blttable", TableCmd, };
4959     TableInterpData *dataPtr;
4960 
4961     dataPtr = GetTableInterpData(interp);
4962     cmdSpec.clientData = dataPtr;
4963     if (Blt_InitCmd(interp, "blt", &cmdSpec) == NULL) {
4964 	return TCL_ERROR;
4965     }
4966     rowUid = (Blt_Uid)Tk_GetUid("row");
4967     columnUid = (Blt_Uid)Tk_GetUid("column");
4968     return TCL_OK;
4969 }
4970