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