source: trunk/kitgen/8.x/blt/generic/bltTable.c@ 176

Last change on this file since 176 was 175, checked in by demin, 12 years ago

initial commit

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