1 /*
2 * tkTreeColumn.c --
3 *
4 * This module implements treectrl widget's columns.
5 *
6 * Copyright (c) 2002-2011 Tim Baker
7 * Copyright (c) 2002-2003 Christian Krone
8 * Copyright (c) 2003 ActiveState Corporation
9 */
10
11 #include "tkTreeCtrl.h"
12
13 typedef struct TreeColumn_ TreeColumn_;
14
15 typedef struct ColumnSpan ColumnSpan;
16 typedef struct SpanArray SpanArray;
17 struct SpanArray
18 {
19 ColumnSpan **spans;
20 int count; /* Number of useful elements in spans[]. */
21 int alloc; /* Number of elements allocated in spans[]. */
22 };
23
24 /* A structure of the following type is kept for each span in an item or
25 * header that covers a unique range of columns. */
26 struct ColumnSpan
27 {
28 TreeColumn start; /* First column in the span. */
29 TreeColumn end; /* Last column in the span. */
30 int maxNeededWidth; /* Width of the widest style in any item
31 * or header. */
32 int widthOfColumns; /* Sum of the calculated display widths of
33 * the columns. */
34 SpanArray spansToRight; /* List of spans following this one. */
35 ColumnSpan *next; /* Head is TreeColumnPriv_.spans. */
36 ColumnSpan *nextCur; /* Head is TreeColumnPriv_.spansCur. */
37 int sumOfSpans;
38 };
39
40 /* A structure of the following type is kept for each TreeColumn.
41 * This is used when calculating the requested width of styles. */
42 typedef struct ColumnReqData ColumnReqData;
43 struct ColumnReqData
44 {
45 int vis; /* TRUE if the column is visible, otherwise FALSE. */
46 int min; /* -minwidth or -1, no greater than -maxwidth. */
47 int fixed; /* -width, or -1. */
48 int max; /* -maxwidth or -1. */
49 int req; /* The width requested by a all or part of a style
50 * in a single item in this column. */
51 int maxSingleSpanWidth; /* The widest span of 1. */
52 int maxSingleItemWidth; /* The widest span of 1 in items. */
53 int maxSingleHeaderWidth; /* The widest span of 1 in headers. */
54 SpanArray spans; /* Array of span pointers touching this column.*/
55 TreeColumn spanMin; /* Any span that includes this column */
56 TreeColumn spanMax; /* begins on or after spanMin and ends on */
57 /* or before spanMax. This includes spans */
58 /* in headers and items. */
59 int fat; /* TRUE when every spans[].widthOfColumns is greater
60 * than spans[].maxNeededWidth, indicating the column
61 * is wider than needed. */
62 };
63
64 /* A structure of the following type is kept for each TreeCtrl to hold
65 * private data used by this file. */
66 struct TreeColumnPriv_
67 {
68 int spansInvalid; /* TRUE if the TreeColumn.spanMin and
69 * TreeColumn.spanMax fields are
70 * out-of-date, otherwise FALSE. */
71 int reqInvalid; /* TRUE if TreeColumn.reqData is out-of-date,
72 * otherwise FALSE. */
73 ColumnSpan *spans; /* All the spans for the widget. */
74 ColumnSpan *freeSpans; /* Unused spans. */
75 ColumnSpan *spansCur; /* The subset of spans[] for the current
76 * update. */
77 int allSpansAreOne; /* TRUE if all spans cover exactly one column,
78 * otherwise FALSE. */
79 };
80
81 #ifdef UNIFORM_GROUP
82 typedef struct UniformGroup {
83 Tcl_HashEntry *hPtr; /* Entry in TreeCtrl.uniformGroupHash */
84 int refCount; /* Number of columns in this group. */
85 int minSize; /* Used during layout. */
86 } UniformGroup;
87 #endif
88
89 /*
90 * The following structure holds information about a single
91 * column in a TreeCtrl.
92 */
93 struct TreeColumn_
94 {
95 int width; /* -width */
96 Tcl_Obj *widthObj; /* -width */
97 int minWidth; /* -minwidth */
98 Tcl_Obj *minWidthObj; /* -minwidth */
99 int maxWidth; /* -maxwidth */
100 Tcl_Obj *maxWidthObj; /* -maxwidth */
101 #ifdef DEPRECATED
102 int stepWidth; /* -stepwidth */
103 Tcl_Obj *stepWidthObj; /* -stepwidth */
104 int widthHack; /* -widthhack */
105 #endif /* DEPRECATED */
106 Tk_Justify justify; /* -justify */
107 int itemJustify; /* -itemjustify */
108 int expand; /* -expand */
109 int squeeze; /* -squeeze */
110 int visible; /* -visible */
111 int resize; /* -resize */
112 TagInfo *tagInfo; /* -tags */
113 Tcl_Obj *itemBgObj; /* -itembackground */
114 TreeStyle itemStyle; /* -itemstyle */
115 int lock; /* -lock */
116
117 TreeCtrl *tree;
118 Tk_OptionTable optionTable;
119 int id; /* unique column identifier */
120 int index; /* order in list of columns */
121 int offset; /* Total width of preceding columns */
122 int useWidth; /* -width, -minwidth, or required+expansion */
123 int widthOfItems; /* width of all TreeItemColumns */
124 int itemBgCount; /* -itembackground colors */
125 TreeColor **itemBgColor; /* -itembackground colors */
126 TreeColumn prev;
127 TreeColumn next;
128 #ifdef UNIFORM_GROUP
129 UniformGroup *uniform; /* -uniform */
130 int weight; /* -weight */
131 #endif
132 TreeColumnDInfo dInfo; /* Display info. */
133 #if COLUMNGRID == 1
134 Tcl_Obj *gridLeftColorObj; /* -gridleftcolor */
135 Tcl_Obj *gridRightColorObj; /* -gridrightcolor */
136 TreeColor *gridLeftColor; /* -gridleftcolor */
137 TreeColor *gridRightColor; /* -gridrightcolor */
138 #endif
139 ColumnReqData reqData;
140 };
141
142 #ifdef UNIFORM_GROUP
143 /*
144 *----------------------------------------------------------------------
145 *
146 * UniformGroupCO_Set --
147 * UniformGroupCO_Get --
148 * UniformGroupCO_Restore --
149 * UniformGroupCO_Free --
150 *
151 * These procedures implement a TK_OPTION_CUSTOM where the custom
152 * option is a UniformGroup.
153 *
154 * Results:
155 * None.
156 *
157 * Side effects:
158 * None.
159 *
160 *----------------------------------------------------------------------
161 */
162
163 static int
UniformGroupCO_Set(ClientData clientData,Tcl_Interp * interp,Tk_Window tkwin,Tcl_Obj ** valuePtr,char * recordPtr,int internalOffset,char * saveInternalPtr,int flags)164 UniformGroupCO_Set(
165 ClientData clientData,
166 Tcl_Interp *interp,
167 Tk_Window tkwin,
168 Tcl_Obj **valuePtr,
169 char *recordPtr,
170 int internalOffset,
171 char *saveInternalPtr,
172 int flags
173 )
174 {
175 TreeCtrl *tree = (TreeCtrl *) ((TkWindow *) tkwin)->instanceData;
176 int objEmpty;
177 UniformGroup **internalPtr, *new;
178
179 if (internalOffset >= 0)
180 internalPtr = (UniformGroup **) (recordPtr + internalOffset);
181 else
182 internalPtr = NULL;
183
184 objEmpty = ObjectIsEmpty((*valuePtr));
185
186 if ((flags & TK_OPTION_NULL_OK) && objEmpty)
187 (*valuePtr) = NULL;
188
189 if (internalPtr != NULL) {
190 if (*valuePtr != NULL) {
191 int isNew;
192 Tcl_HashEntry *hPtr = Tcl_CreateHashEntry(&tree->uniformGroupHash,
193 Tcl_GetString(*valuePtr), &isNew);
194 if (isNew) {
195 new = (UniformGroup *) ckalloc(sizeof(UniformGroup));
196 new->refCount = 0;
197 new->hPtr = hPtr;
198 Tcl_SetHashValue(hPtr, (ClientData) new);
199 } else {
200 new = (UniformGroup *) Tcl_GetHashValue(hPtr);
201 }
202 new->refCount++;
203 #ifdef TREECTRL_DEBUG
204 if (tree->debug.enable)
205 dbwin("UniformGroupCO_Set: %s refCount=%d\n", Tcl_GetString(*valuePtr), new->refCount);
206 #endif
207 } else {
208 new = NULL;
209 }
210 *((UniformGroup **) saveInternalPtr) = *internalPtr;
211 *internalPtr = new;
212 }
213
214 return TCL_OK;
215 }
216
217 static Tcl_Obj *
UniformGroupCO_Get(ClientData clientData,Tk_Window tkwin,char * recordPtr,int internalOffset)218 UniformGroupCO_Get(
219 ClientData clientData,
220 Tk_Window tkwin,
221 char *recordPtr,
222 int internalOffset
223 )
224 {
225 TreeCtrl *tree = (TreeCtrl *) ((TkWindow *) tkwin)->instanceData;
226 UniformGroup *uniform = *(UniformGroup **) (recordPtr + internalOffset);
227
228 if (uniform == NULL)
229 return NULL;
230 return Tcl_NewStringObj(Tcl_GetHashKey(&tree->uniformGroupHash,
231 uniform->hPtr), -1);
232 }
233
234 static void
UniformGroupCO_Restore(ClientData clientData,Tk_Window tkwin,char * internalPtr,char * saveInternalPtr)235 UniformGroupCO_Restore(
236 ClientData clientData,
237 Tk_Window tkwin,
238 char *internalPtr,
239 char *saveInternalPtr
240 )
241 {
242 *(UniformGroup **) internalPtr = *(UniformGroup **) saveInternalPtr;
243 }
244
245 static void
UniformGroupCO_Free(ClientData clientData,Tk_Window tkwin,char * internalPtr)246 UniformGroupCO_Free(
247 ClientData clientData,
248 Tk_Window tkwin,
249 char *internalPtr
250 )
251 {
252 UniformGroup *uniform = *(UniformGroup **) internalPtr;
253
254 #ifdef TREECTRL_DEBUG
255 TreeCtrl *tree = (TreeCtrl *) ((TkWindow *) tkwin)->instanceData;
256 if (tree->debug.enable && uniform != NULL) {
257 dbwin("UniformGroupCO_Free: %s refCount=%d\n", Tcl_GetHashKey(&tree->uniformGroupHash, uniform->hPtr), uniform->refCount - 1);
258 }
259 #endif
260 if ((uniform != NULL) && (--uniform->refCount <= 0)) {
261 Tcl_DeleteHashEntry(uniform->hPtr);
262 ckfree((char *) uniform);
263 *((UniformGroup **) internalPtr) = NULL;
264 }
265 }
266
267 static Tk_ObjCustomOption uniformGroupCO =
268 {
269 "uniform group",
270 UniformGroupCO_Set,
271 UniformGroupCO_Get,
272 UniformGroupCO_Restore,
273 UniformGroupCO_Free,
274 (ClientData) NULL
275 };
276 #endif /* UNIFORM_GROUP */
277
278 static CONST char *arrowSideST[] = { "left", "right", (char *) NULL };
279 static CONST char *lockST[] = { "left", "none", "right", (char *) NULL };
280 static CONST char *justifyStrings[] = {
281 "left", "right", "center", (char *) NULL
282 };
283
284 #define COLU_CONF_TWIDTH 0x0008 /* totalWidth */
285 #define COLU_CONF_ITEMBG 0x0010
286 #define COLU_CONF_DISPLAY 0x0040
287 #define COLU_CONF_JUSTIFY 0x0080
288 #define COLU_CONF_TAGS 0x0100
289 #ifdef DEPRECATED
290 #define COLU_CONF_RANGES 0x0800
291 #endif
292 #if COLUMNGRID == 1
293 #define COLU_CONF_GRIDLINES 0x1000
294 #endif
295
296 static Tk_OptionSpec columnSpecs[] = {
297 {TK_OPTION_BOOLEAN, "-expand", (char *) NULL, (char *) NULL,
298 "0", -1, Tk_Offset(TreeColumn_, expand),
299 0, (ClientData) NULL, COLU_CONF_TWIDTH},
300 #if COLUMNGRID==1
301 {TK_OPTION_CUSTOM, "-gridleftcolor", (char *) NULL, (char *) NULL, (char *) NULL,
302 Tk_Offset(TreeColumn_, gridLeftColorObj),
303 Tk_Offset(TreeColumn_, gridLeftColor),
304 TK_OPTION_NULL_OK, (ClientData) &TreeCtrlCO_treecolor,
305 COLU_CONF_GRIDLINES},
306 {TK_OPTION_CUSTOM, "-gridrightcolor", (char *) NULL, (char *) NULL, (char *) NULL,
307 Tk_Offset(TreeColumn_, gridRightColorObj),
308 Tk_Offset(TreeColumn_, gridRightColor),
309 TK_OPTION_NULL_OK, (ClientData) &TreeCtrlCO_treecolor,
310 COLU_CONF_GRIDLINES},
311 #endif
312 {TK_OPTION_STRING, "-itembackground", (char *) NULL, (char *) NULL,
313 (char *) NULL, Tk_Offset(TreeColumn_, itemBgObj), -1,
314 TK_OPTION_NULL_OK, (ClientData) NULL, COLU_CONF_ITEMBG},
315 {TK_OPTION_CUSTOM, "-itemjustify", (char *) NULL, (char *) NULL,
316 (char *) NULL, -1, Tk_Offset(TreeColumn_, itemJustify),
317 TK_OPTION_NULL_OK, (ClientData) NULL, COLU_CONF_JUSTIFY},
318 {TK_OPTION_CUSTOM, "-itemstyle", (char *) NULL, (char *) NULL,
319 (char *) NULL, -1, Tk_Offset(TreeColumn_, itemStyle),
320 TK_OPTION_NULL_OK, (ClientData) &TreeCtrlCO_style, 0},
321 {TK_OPTION_JUSTIFY, "-justify", (char *) NULL, (char *) NULL,
322 "left", -1, Tk_Offset(TreeColumn_, justify),
323 0, (ClientData) NULL, COLU_CONF_DISPLAY | COLU_CONF_JUSTIFY},
324 {TK_OPTION_STRING_TABLE, "-lock", (char *) NULL, (char *) NULL,
325 "none", -1, Tk_Offset(TreeColumn_, lock), 0, (ClientData) lockST, 0},
326 {TK_OPTION_PIXELS, "-maxwidth", (char *) NULL, (char *) NULL,
327 (char *) NULL, Tk_Offset(TreeColumn_, maxWidthObj),
328 Tk_Offset(TreeColumn_, maxWidth),
329 TK_OPTION_NULL_OK, (ClientData) NULL, COLU_CONF_TWIDTH},
330 {TK_OPTION_PIXELS, "-minwidth", (char *) NULL, (char *) NULL,
331 (char *) NULL, Tk_Offset(TreeColumn_, minWidthObj),
332 Tk_Offset(TreeColumn_, minWidth),
333 TK_OPTION_NULL_OK, (ClientData) NULL, COLU_CONF_TWIDTH},
334 {TK_OPTION_BOOLEAN, "-resize", (char *) NULL, (char *) NULL,
335 "1", -1, Tk_Offset(TreeColumn_, resize), 0, (ClientData) NULL, 0},
336 {TK_OPTION_BOOLEAN, "-squeeze", (char *) NULL, (char *) NULL,
337 "0", -1, Tk_Offset(TreeColumn_, squeeze),
338 0, (ClientData) NULL, COLU_CONF_TWIDTH},
339 #ifdef DEPRECATED
340 {TK_OPTION_PIXELS, "-stepwidth", (char *) NULL, (char *) NULL,
341 (char *) NULL, Tk_Offset(TreeColumn_, stepWidthObj),
342 Tk_Offset(TreeColumn_, stepWidth),
343 TK_OPTION_NULL_OK, (ClientData) NULL, COLU_CONF_RANGES},
344 #endif /* DEPRECATED */
345 {TK_OPTION_CUSTOM, "-tags", (char *) NULL, (char *) NULL,
346 (char *) NULL, -1, Tk_Offset(TreeColumn_, tagInfo),
347 TK_OPTION_NULL_OK, (ClientData) &TreeCtrlCO_tagInfo, COLU_CONF_TAGS},
348 #ifdef UNIFORM_GROUP
349 {TK_OPTION_CUSTOM, "-uniform", (char *) NULL, (char *) NULL,
350 (char *) NULL, -1, Tk_Offset(TreeColumn_, uniform), TK_OPTION_NULL_OK,
351 (ClientData) &uniformGroupCO, COLU_CONF_TWIDTH},
352 {TK_OPTION_INT, "-weight", (char *) NULL, (char *) NULL,
353 "1", -1, Tk_Offset(TreeColumn_, weight),
354 TK_OPTION_NULL_OK, (ClientData) NULL, COLU_CONF_TWIDTH},
355 #endif
356 {TK_OPTION_PIXELS, "-width", (char *) NULL, (char *) NULL,
357 (char *) NULL, Tk_Offset(TreeColumn_, widthObj), Tk_Offset(TreeColumn_, width),
358 TK_OPTION_NULL_OK, (ClientData) NULL, COLU_CONF_TWIDTH},
359 {TK_OPTION_BOOLEAN, "-visible", (char *) NULL, (char *) NULL,
360 "1", -1, Tk_Offset(TreeColumn_, visible),
361 0, (ClientData) NULL, COLU_CONF_TWIDTH | COLU_CONF_DISPLAY},
362 #ifdef DEPRECATED
363 {TK_OPTION_BOOLEAN, "-widthhack", (char *) NULL, (char *) NULL,
364 "0", -1, Tk_Offset(TreeColumn_, widthHack),
365 0, (ClientData) NULL, COLU_CONF_RANGES},
366 #endif /* DEPRECATED */
367 {TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL,
368 (char *) NULL, 0, -1, 0, 0, 0}
369 };
370
371 #define IS_TAIL(C) ((C) == tree->columnTail)
372 #define IS_ALL(C) (((C) == COLUMN_ALL) || ((C) == COLUMN_NTAIL))
373
374 /*
375 *----------------------------------------------------------------------
376 *
377 * ColumnCO_Set --
378 *
379 * Tk_ObjCustomOption.setProc(). Converts a Tcl_Obj holding a
380 * column description into a pointer to a Column.
381 *
382 * Results:
383 * A standard Tcl result.
384 *
385 * Side effects:
386 * May store a TreeColumn pointer into the internal representation
387 * pointer. May change the pointer to the Tcl_Obj to NULL to indicate
388 * that the specified string was empty and that is acceptable.
389 *
390 *----------------------------------------------------------------------
391 */
392
393 static int
ColumnCO_Set(ClientData clientData,Tcl_Interp * interp,Tk_Window tkwin,Tcl_Obj ** value,char * recordPtr,int internalOffset,char * saveInternalPtr,int flags)394 ColumnCO_Set(
395 ClientData clientData, /* CFO_xxx flags to control the conversion. */
396 Tcl_Interp *interp, /* Current interpreter. */
397 Tk_Window tkwin, /* Window for which option is being set. */
398 Tcl_Obj **value, /* Pointer to the pointer to the value object.
399 * We use a pointer to the pointer because
400 * we may need to return a value (NULL). */
401 char *recordPtr, /* Pointer to storage for the widget record. */
402 int internalOffset, /* Offset within *recordPtr at which the
403 * internal value is to be stored. */
404 char *saveInternalPtr, /* Pointer to storage for the old value. */
405 int flags /* Flags for the option, set Tk_SetOptions. */
406 )
407 {
408 int cfoFlags = PTR2INT(clientData);
409 TreeCtrl *tree = (TreeCtrl *) ((TkWindow *) tkwin)->instanceData;
410 int objEmpty;
411 TreeColumn new, *internalPtr;
412
413 if (internalOffset >= 0)
414 internalPtr = (TreeColumn *) (recordPtr + internalOffset);
415 else
416 internalPtr = NULL;
417
418 objEmpty = ObjectIsEmpty((*value));
419
420 if ((flags & TK_OPTION_NULL_OK) && objEmpty)
421 (*value) = NULL;
422 else {
423 if (TreeColumn_FromObj(tree, (*value), &new, cfoFlags) != TCL_OK)
424 return TCL_ERROR;
425 }
426 if (internalPtr != NULL) {
427 if ((*value) == NULL)
428 new = NULL;
429 *((TreeColumn *) saveInternalPtr) = *internalPtr;
430 *internalPtr = new;
431 }
432
433 return TCL_OK;
434 }
435
436 /*
437 *----------------------------------------------------------------------
438 *
439 * ColumnCO_Get --
440 *
441 * Tk_ObjCustomOption.getProc(). Converts a TreeColumn into a
442 * Tcl_Obj string representation.
443 *
444 * Results:
445 * Tcl_Obj containing the string representation of the column.
446 * Returns NULL if the TreeColumn is NULL.
447 *
448 * Side effects:
449 * May create a new Tcl_Obj.
450 *
451 *----------------------------------------------------------------------
452 */
453
454 static Tcl_Obj *
ColumnCO_Get(ClientData clientData,Tk_Window tkwin,char * recordPtr,int internalOffset)455 ColumnCO_Get(
456 ClientData clientData, /* Not used. */
457 Tk_Window tkwin, /* Window for which option is being set. */
458 char *recordPtr, /* Pointer to widget record. */
459 int internalOffset /* Offset within *recordPtr containing the
460 * sticky value. */
461 )
462 {
463 TreeColumn value = *(TreeColumn *) (recordPtr + internalOffset);
464 TreeCtrl *tree = (TreeCtrl *) ((TkWindow *) tkwin)->instanceData;
465 if (value == NULL)
466 return NULL;
467 #if 0
468 if (value == COLUMN_ALL)
469 return Tcl_NewStringObj("all", -1);
470 #endif
471 return TreeColumn_ToObj(tree, value);
472 }
473
474 /*
475 *----------------------------------------------------------------------
476 *
477 * ColumnCO_Restore --
478 *
479 * Tk_ObjCustomOption.restoreProc(). Restores a TreeColumn value
480 * from a saved value.
481 *
482 * Results:
483 * None.
484 *
485 * Side effects:
486 * Restores the old value.
487 *
488 *----------------------------------------------------------------------
489 */
490
491 static void
ColumnCO_Restore(ClientData clientData,Tk_Window tkwin,char * internalPtr,char * saveInternalPtr)492 ColumnCO_Restore(
493 ClientData clientData, /* Not used. */
494 Tk_Window tkwin, /* Not used. */
495 char *internalPtr, /* Where to store old value. */
496 char *saveInternalPtr) /* Pointer to old value. */
497 {
498 *(TreeColumn *) internalPtr = *(TreeColumn *) saveInternalPtr;
499 }
500
501 /*
502 * The following structure contains pointers to functions used for processing
503 * a custom config option that handles Tcl_Obj<->TreeColumn conversion.
504 * A column description must refer to a single column.
505 */
506 Tk_ObjCustomOption TreeCtrlCO_column =
507 {
508 "column",
509 ColumnCO_Set,
510 ColumnCO_Get,
511 ColumnCO_Restore,
512 NULL,
513 (ClientData) INT2PTR(CFO_NOT_NULL)
514 };
515
516 /*
517 * The following structure contains pointers to functions used for processing
518 * a custom config option that handles Tcl_Obj<->TreeColumn conversion.
519 * A column description must refer to a single column.
520 * "tail" is not allowed.
521 */
522 Tk_ObjCustomOption TreeCtrlCO_column_NOT_TAIL =
523 {
524 "column",
525 ColumnCO_Set,
526 ColumnCO_Get,
527 ColumnCO_Restore,
528 NULL,
529 (ClientData) INT2PTR(CFO_NOT_NULL | CFO_NOT_TAIL)
530 };
531
532 static Tk_OptionSpec dragSpecs[] = {
533 {TK_OPTION_BOOLEAN, "-enable", (char *) NULL, (char *) NULL,
534 "0", -1, Tk_Offset(TreeCtrl, columnDrag.enable),
535 0, (ClientData) NULL, 0},
536 {TK_OPTION_INT, "-imagealpha", (char *) NULL, (char *) NULL,
537 "200", -1, Tk_Offset(TreeCtrl, columnDrag.alpha),
538 0, (ClientData) NULL, 0},
539 {TK_OPTION_COLOR, "-imagecolor", (char *) NULL, (char *) NULL,
540 "gray75", -1, Tk_Offset(TreeCtrl, columnDrag.color),
541 0, (ClientData) NULL, 0},
542 {TK_OPTION_CUSTOM, "-imagecolumn", (char *) NULL, (char *) NULL,
543 (char *) NULL, -1, Tk_Offset(TreeCtrl, columnDrag.column),
544 TK_OPTION_NULL_OK, (ClientData) &TreeCtrlCO_column_NOT_TAIL, 0},
545 {TK_OPTION_PIXELS, "-imageoffset", (char *) NULL, (char *) NULL,
546 (char *) NULL, Tk_Offset(TreeCtrl, columnDrag.offsetObj),
547 Tk_Offset(TreeCtrl, columnDrag.offset), 0, (ClientData) NULL, 0},
548 {TK_OPTION_INT, "-imagespan", (char *) NULL, (char *) NULL,
549 "1", -1, Tk_Offset(TreeCtrl, columnDrag.span),
550 0, (ClientData) NULL, 0},
551 {TK_OPTION_COLOR, "-indicatorcolor", (char *) NULL, (char *) NULL,
552 "Black", -1, Tk_Offset(TreeCtrl, columnDrag.indColor),
553 0, (ClientData) NULL, 0},
554 {TK_OPTION_CUSTOM, "-indicatorcolumn", (char *) NULL, (char *) NULL,
555 (char *) NULL, -1, Tk_Offset(TreeCtrl, columnDrag.indColumn),
556 TK_OPTION_NULL_OK, (ClientData) &TreeCtrlCO_column, 0},
557 {TK_OPTION_STRING_TABLE, "-indicatorside", (char *) NULL, (char *) NULL,
558 "left", -1, Tk_Offset(TreeCtrl, columnDrag.indSide),
559 0, (ClientData) arrowSideST, 0},
560 {TK_OPTION_INT, "-indicatorspan", (char *) NULL, (char *) NULL,
561 "1", -1, Tk_Offset(TreeCtrl, columnDrag.indSpan),
562 0, (ClientData) NULL, 0},
563 {TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL,
564 (char *) NULL, 0, -1, 0, 0, 0}
565 };
566
567 /*
568 *----------------------------------------------------------------------
569 *
570 * TreeColumn_FirstAndLast --
571 *
572 * Determine the order of two columns and swap them if needed.
573 *
574 * Results:
575 * The return value is the number of columns in the range between
576 * first and last.
577 *
578 * Side effects:
579 * None.
580 *
581 *----------------------------------------------------------------------
582 */
583
584 int
TreeColumn_FirstAndLast(TreeColumn * first,TreeColumn * last)585 TreeColumn_FirstAndLast(
586 TreeColumn *first, /* Column token. */
587 TreeColumn *last /* Column token. */
588 )
589 {
590 int indexFirst, indexLast, index;
591
592 indexFirst = TreeColumn_Index(*first);
593 indexLast = TreeColumn_Index(*last);
594 if (indexFirst > indexLast) {
595 TreeColumn column = *first;
596 *first = *last;
597 *last = column;
598
599 index = indexFirst;
600 indexFirst = indexLast;
601 indexLast = index;
602 }
603 return indexLast - indexFirst + 1;
604 }
605
606 /*
607 *----------------------------------------------------------------------
608 *
609 * ColumnHasTag --
610 *
611 * Checks whether a column has a certain tag.
612 *
613 * Results:
614 * Returns TRUE if the column has the given tag.
615 *
616 * Side effects:
617 * None.
618 *
619 *----------------------------------------------------------------------
620 */
621
622 static int
ColumnHasTag(TreeColumn column,Tk_Uid tag)623 ColumnHasTag(
624 TreeColumn column, /* The column to test. */
625 Tk_Uid tag /* Tag to look for. */
626 )
627 {
628 TagInfo *tagInfo = column->tagInfo;
629 Tk_Uid *tagPtr;
630 int count;
631
632 if (tagInfo == NULL)
633 return 0;
634
635 for (tagPtr = tagInfo->tagPtr, count = tagInfo->numTags;
636 count > 0; tagPtr++, count--) {
637 if (*tagPtr == tag) {
638 return 1;
639 }
640 }
641 return 0;
642 }
643
644 typedef struct Qualifiers {
645 TreeCtrl *tree;
646 int visible; /* 1 for -visible TRUE,
647 0 for -visible FALSE,
648 -1 for unspecified. */
649 TagExpr expr; /* Tag expression. */
650 int exprOK; /* TRUE if expr is valid. */
651 int lock; /* COLUMN_LOCK_xxx or -1 */
652 int ntail; /* 1 for !tail,
653 * 0 for unspecified. */
654 Tk_Uid tag; /* Tag (without operators) or NULL. */
655 } Qualifiers;
656
657 /*
658 *----------------------------------------------------------------------
659 *
660 * Qualifiers_Init --
661 *
662 * Helper routine for TreeColumnList_FromObj.
663 *
664 * Results:
665 * None.
666 *
667 * Side effects:
668 * None.
669 *
670 *----------------------------------------------------------------------
671 */
672
673 static void
Qualifiers_Init(TreeCtrl * tree,Qualifiers * q)674 Qualifiers_Init(
675 TreeCtrl *tree, /* Widget info. */
676 Qualifiers *q /* Out: Initialized qualifiers. */
677 )
678 {
679 q->tree = tree;
680 q->visible = -1;
681 q->exprOK = FALSE;
682 q->lock = -1;
683 q->ntail = 0;
684 q->tag = NULL;
685 }
686
687 /*
688 *----------------------------------------------------------------------
689 *
690 * Qualifiers_Scan --
691 *
692 * Helper routine for TreeColumnList_FromObj.
693 *
694 * Results:
695 * TCL_OK or TCL_ERROR.
696 *
697 * Side effects:
698 * None.
699 *
700 *----------------------------------------------------------------------
701 */
702
703 static int
Qualifiers_Scan(Qualifiers * q,int objc,Tcl_Obj ** objv,int startIndex,int * argsUsed)704 Qualifiers_Scan(
705 Qualifiers *q, /* Must call Qualifiers_Init first,
706 * and Qualifiers_Free if result is TCL_OK. */
707 int objc, /* Number of arguments. */
708 Tcl_Obj **objv, /* Argument values. */
709 int startIndex, /* First objv[] index to look at. */
710 int *argsUsed /* Out: number of objv[] used. */
711 )
712 {
713 TreeCtrl *tree = q->tree;
714 Tcl_Interp *interp = tree->interp;
715 int qual, j = startIndex;
716
717 static CONST char *qualifiers[] = {
718 "lock", "tag", "visible", "!tail", "!visible", NULL
719 };
720 enum qualEnum {
721 QUAL_LOCK, QUAL_TAG, QUAL_VISIBLE, QUAL_NOT_TAIL, QUAL_NOT_VISIBLE
722 };
723 /* Number of arguments used by qualifiers[]. */
724 static int qualArgs[] = {
725 2, 2, 1, 1, 1
726 };
727
728 *argsUsed = 0;
729
730 for (; j < objc; ) {
731 if (Tcl_GetIndexFromObj(NULL, objv[j], qualifiers, NULL, 0,
732 &qual) != TCL_OK)
733 break;
734 if (objc - j < qualArgs[qual]) {
735 Tcl_AppendResult(interp, "missing arguments to \"",
736 Tcl_GetString(objv[j]), "\" qualifier", NULL);
737 goto errorExit;
738 }
739 switch ((enum qualEnum) qual) {
740 case QUAL_LOCK: {
741 if (Tcl_GetIndexFromObj(interp, objv[j + 1], lockST,
742 "lock", 0, &q->lock) != TCL_OK)
743 goto errorExit;
744 break;
745 }
746 case QUAL_TAG: {
747 if (tree->columnTagExpr) {
748 if (q->exprOK)
749 TagExpr_Free(&q->expr);
750 if (TagExpr_Init(tree, objv[j + 1], &q->expr) != TCL_OK)
751 return TCL_ERROR;
752 q->exprOK = TRUE;
753 } else {
754 q->tag = Tk_GetUid(Tcl_GetString(objv[j + 1]));
755 }
756 break;
757 }
758 case QUAL_VISIBLE: {
759 q->visible = 1;
760 break;
761 }
762 case QUAL_NOT_TAIL: {
763 q->ntail = 1;
764 break;
765 }
766 case QUAL_NOT_VISIBLE: {
767 q->visible = 0;
768 break;
769 }
770 }
771 *argsUsed += qualArgs[qual];
772 j += qualArgs[qual];
773 }
774 return TCL_OK;
775 errorExit:
776 if (q->exprOK)
777 TagExpr_Free(&q->expr);
778 return TCL_ERROR;
779 }
780
781 /*
782 *----------------------------------------------------------------------
783 *
784 * Qualifies --
785 *
786 * Helper routine for TreeColumnList_FromObj.
787 *
788 * Results:
789 * Returns TRUE if the column meets the given criteria.
790 *
791 * Side effects:
792 * None.
793 *
794 *----------------------------------------------------------------------
795 */
796
797 static int
Qualifies(Qualifiers * q,TreeColumn column)798 Qualifies(
799 Qualifiers *q, /* Qualifiers to check. */
800 TreeColumn column /* The column to test. May be NULL. */
801 )
802 {
803 /* Note: if the column is NULL it is a "match" because we have run
804 * out of columns to check. */
805 if (column == NULL)
806 return 1;
807 if ((q->ntail == 1) && (column == column->tree->columnTail))
808 return 0;
809 if ((q->visible == 1) && !column->visible)
810 return 0;
811 else if ((q->visible == 0) && column->visible)
812 return 0;
813 if (q->exprOK && !TagExpr_Eval(&q->expr, column->tagInfo))
814 return 0;
815 if ((q->lock != -1) && (column->lock != q->lock))
816 return 0;
817 if ((q->tag != NULL) && !ColumnHasTag(column, q->tag))
818 return 0;
819 return 1;
820 }
821
822 /*
823 *----------------------------------------------------------------------
824 *
825 * Qualifiers_Free --
826 *
827 * Helper routine for TreeColumnList_FromObj.
828 *
829 * Results:
830 * None.
831 *
832 * Side effects:
833 * None.
834 *
835 *----------------------------------------------------------------------
836 */
837
838 static void
Qualifiers_Free(Qualifiers * q)839 Qualifiers_Free(
840 Qualifiers *q /* Out: Initialized qualifiers. */
841 )
842 {
843 if (q->exprOK)
844 TagExpr_Free(&q->expr);
845 }
846
847 /*
848 *----------------------------------------------------------------------
849 *
850 * TreeColumnList_FromObj --
851 *
852 * Parse a Tcl_Obj column description to get a list of columns.
853 *
854 * -- returning a single column --
855 * ID MODIFIERS
856 * first QUALIFIERS MODIFIERS
857 * end|last QUALIFIERS MODIFIERS
858 * order N QUALIFIERS MODIFIERS
859 * tail
860 * tree
861 * -- returning multiple columns --
862 * all QUALIFIERS
863 * QUALIFIERS (like "all QUALIFIERS")
864 * list listOfDescs
865 * range first last QUALIFIERS
866 * tag tagExpr QUALIFIERS
867 * TAG-EXPR QUALIFIERS MODIFIERS
868 *
869 * MODIFIERS:
870 * -- returning a single column --
871 * next QUALIFIERS
872 * prev QUALIFIERS
873 * span N QUALIFIERS
874 *
875 * QUALIFIERS:
876 * state stateList
877 * tag tagExpr
878 * visible
879 * !visible
880 * !tail
881 *
882 * Results:
883 * A standard Tcl result.
884 *
885 * Side effects:
886 * None.
887 *
888 *----------------------------------------------------------------------
889 */
890
891 int
TreeColumnList_FromObj(TreeCtrl * tree,Tcl_Obj * objPtr,TreeColumnList * columns,int flags)892 TreeColumnList_FromObj(
893 TreeCtrl *tree, /* Widget info. */
894 Tcl_Obj *objPtr, /* Column description. */
895 TreeColumnList *columns, /* Uninitialized list. Caller must free
896 * it with TreeColumnList_Free unless the
897 * result of this function is TCL_ERROR. */
898 int flags /* CFO_xxx flags. */
899 )
900 {
901 Tcl_Interp *interp = tree->interp;
902 int i, objc, index, listIndex;
903 Tcl_Obj **objv, *elemPtr;
904 TreeColumn column = NULL;
905 Qualifiers q;
906 int qualArgsTotal;
907
908 static CONST char *indexName[] = {
909 "all", "end", "first", "last", "list", "order", "range", "tail",
910 "tree", (char *) NULL
911 };
912 enum indexEnum {
913 INDEX_ALL, INDEX_END, INDEX_FIRST, INDEX_LAST, INDEX_LIST, INDEX_ORDER,
914 INDEX_RANGE, INDEX_TAIL, INDEX_TREE
915 } ;
916 /* Number of arguments used by indexName[]. */
917 static int indexArgs[] = {
918 1, 1, 1, 1, 2, 2, 3, 1, 1
919 };
920 /* Boolean: can indexName[] be followed by 1 or more qualifiers. */
921 static int indexQual[] = {
922 1, 0, 1, 1, 0, 1, 1, 0, 0
923 };
924
925 static CONST char *modifiers[] = {
926 "next", "prev", "span", (char *) NULL
927 };
928 enum modEnum {
929 TMOD_NEXT, TMOD_PREV, TMOD_SPAN
930 };
931 /* Number of arguments used by modifiers[]. */
932 static int modArgs[] = {
933 1, 1, 2
934 };
935 /* Boolean: can modifiers[] be followed by 1 or more qualifiers. */
936 static int modQual[] = {
937 1, 1, 1
938 };
939
940 TreeColumnList_Init(tree, columns, 0);
941 Qualifiers_Init(tree, &q);
942
943 if (Tcl_ListObjGetElements(NULL, objPtr, &objc, &objv) != TCL_OK)
944 goto badDesc;
945 if (objc == 0)
946 goto badDesc;
947
948 listIndex = 0;
949 elemPtr = objv[listIndex];
950 if (Tcl_GetIndexFromObj(NULL, elemPtr, indexName, NULL, 0, &index)
951 == TCL_OK) {
952
953 if (objc - listIndex < indexArgs[index]) {
954 Tcl_AppendResult(interp, "missing arguments to \"",
955 Tcl_GetString(elemPtr), "\" keyword", NULL);
956 goto errorExit;
957 }
958
959 qualArgsTotal = 0;
960 if (indexQual[index]) {
961 if (Qualifiers_Scan(&q, objc, objv, listIndex + indexArgs[index],
962 &qualArgsTotal) != TCL_OK) {
963 goto errorExit;
964 }
965 }
966
967 switch ((enum indexEnum) index) {
968 case INDEX_ALL: {
969 if (qualArgsTotal) {
970 column = tree->columns;
971 while (column != NULL) {
972 if (Qualifies(&q, column)) {
973 TreeColumnList_Append(columns, column);
974 }
975 column = column->next;
976 }
977 if (!(flags & CFO_NOT_TAIL) &&
978 Qualifies(&q, tree->columnTail)) {
979 TreeColumnList_Append(columns, tree->columnTail);
980 }
981 column = NULL;
982 } else if (flags & CFO_LIST_ALL) {
983 column = tree->columns;
984 while (column != NULL) {
985 TreeColumnList_Append(columns, column);
986 column = column->next;
987 }
988 if (!(flags & CFO_NOT_TAIL))
989 TreeColumnList_Append(columns, tree->columnTail);
990 column = NULL;
991 } else if (flags & CFO_NOT_TAIL) {
992 column = COLUMN_NTAIL;
993 } else {
994 column = COLUMN_ALL;
995 }
996 break;
997 }
998 case INDEX_FIRST: {
999 column = tree->columns;
1000 while (!Qualifies(&q, column))
1001 column = column->next;
1002 break;
1003 }
1004 case INDEX_END:
1005 case INDEX_LAST: {
1006 column = tree->columnLast;
1007 while (!Qualifies(&q, column)) {
1008 column = column->prev;
1009 }
1010 break;
1011 }
1012 case INDEX_LIST: {
1013 int listObjc;
1014 Tcl_Obj **listObjv;
1015 int count;
1016
1017 if (Tcl_ListObjGetElements(interp, objv[listIndex + 1],
1018 &listObjc, &listObjv) != TCL_OK)
1019 goto errorExit;
1020 for (i = 0; i < listObjc; i++) {
1021 TreeColumnList column2s;
1022 if (TreeColumnList_FromObj(tree, listObjv[i], &column2s,
1023 flags) != TCL_OK)
1024 goto errorExit;
1025 TreeColumnList_Concat(columns, &column2s);
1026 TreeColumnList_Free(&column2s);
1027 }
1028 /* If any of the column descriptions in the list is "all", then
1029 * clear the list of columns and use "all". */
1030 count = TreeColumnList_Count(columns);
1031 for (i = 0; i < count; i++) {
1032 TreeColumn column = TreeColumnList_Nth(columns, i);
1033 if (IS_ALL(column))
1034 break;
1035 }
1036 if (i < count) {
1037 TreeColumnList_Free(columns);
1038 if (flags & CFO_NOT_TAIL)
1039 column = COLUMN_NTAIL;
1040 else
1041 column = COLUMN_ALL;
1042 } else
1043 column = NULL;
1044 break;
1045 }
1046 case INDEX_ORDER: {
1047 int order;
1048
1049 if (Tcl_GetIntFromObj(NULL, objv[listIndex + 1], &order) != TCL_OK)
1050 goto errorExit;
1051 column = tree->columns;
1052 while (column != NULL) {
1053 if (Qualifies(&q, column))
1054 if (order-- <= 0)
1055 break;
1056 column = column->next;
1057 }
1058 break;
1059 }
1060 case INDEX_RANGE: {
1061 TreeColumn _first, _last;
1062
1063 if (TreeColumn_FromObj(tree, objv[listIndex + 1],
1064 &_first, CFO_NOT_NULL) != TCL_OK)
1065 goto errorExit;
1066 if (TreeColumn_FromObj(tree, objv[listIndex + 2],
1067 &_last, CFO_NOT_NULL) != TCL_OK)
1068 goto errorExit;
1069 (void) TreeColumn_FirstAndLast(&_first, &_last);
1070 column = _first;
1071 while (1) {
1072 if (Qualifies(&q, column)) {
1073 TreeColumnList_Append(columns, column);
1074 }
1075 if (column == _last)
1076 break;
1077 column = column->next;
1078 if (column == NULL)
1079 column = tree->columnTail;
1080 }
1081 column = NULL;
1082 break;
1083 }
1084 case INDEX_TAIL: {
1085 column = tree->columnTail;
1086 break;
1087 }
1088 case INDEX_TREE: {
1089 column = tree->columnTree;
1090 break;
1091 }
1092 }
1093 listIndex += indexArgs[index] + qualArgsTotal;
1094
1095 /* No indexName[] was found. */
1096 } else {
1097 int gotId = FALSE, id;
1098 TagExpr expr;
1099
1100 if (tree->columnPrefixLen) {
1101 char *end, *t = Tcl_GetString(elemPtr);
1102 if (strncmp(t, tree->columnPrefix, tree->columnPrefixLen) == 0) {
1103 t += tree->columnPrefixLen;
1104 id = strtoul(t, &end, 10);
1105 if ((end != t) && (*end == '\0'))
1106 gotId = TRUE;
1107 }
1108
1109 } else if (Tcl_GetIntFromObj(NULL, elemPtr, &id) == TCL_OK) {
1110 gotId = TRUE;
1111 }
1112 if (gotId) {
1113 column = tree->columns;
1114 while (column) {
1115 if (column->id == id)
1116 break;
1117 column = column->next;
1118 }
1119 listIndex++;
1120 goto gotFirstPart;
1121 }
1122
1123 /* Try a list of qualifiers. This has the same effect as
1124 * "all QUALIFIERS". */
1125 if (Qualifiers_Scan(&q, objc, objv, listIndex, &qualArgsTotal)
1126 != TCL_OK) {
1127 goto errorExit;
1128 }
1129 if (qualArgsTotal) {
1130 column = tree->columns;
1131 while (column != NULL) {
1132 if (Qualifies(&q, column)) {
1133 TreeColumnList_Append(columns, column);
1134 }
1135 column = column->next;
1136 }
1137 if (!(flags & CFO_NOT_TAIL) &&
1138 Qualifies(&q, tree->columnTail)) {
1139 TreeColumnList_Append(columns, tree->columnTail);
1140 }
1141 column = NULL;
1142 listIndex += qualArgsTotal;
1143 goto gotFirstPart;
1144 }
1145
1146 /* Try a tag or tag expression followed by qualifiers. */
1147 if (objc > 1) {
1148 if (Qualifiers_Scan(&q, objc, objv, listIndex + 1,
1149 &qualArgsTotal) != TCL_OK) {
1150 goto errorExit;
1151 }
1152 }
1153 if (tree->columnTagExpr) {
1154 if (TagExpr_Init(tree, elemPtr, &expr) != TCL_OK)
1155 goto errorExit;
1156 column = tree->columns;
1157 while (column != NULL) {
1158 if (TagExpr_Eval(&expr, column->tagInfo) && Qualifies(&q, column)) {
1159 TreeColumnList_Append(columns, column);
1160 }
1161 column = column->next;
1162 }
1163 if (!(flags & CFO_NOT_TAIL) &&
1164 TagExpr_Eval(&expr, tree->columnTail->tagInfo) &&
1165 Qualifies(&q, tree->columnTail)) {
1166 TreeColumnList_Append(columns, tree->columnTail);
1167 }
1168 TagExpr_Free(&expr);
1169 } else {
1170 Tk_Uid tag = Tk_GetUid(Tcl_GetString(elemPtr));
1171 column = tree->columns;
1172 while (column != NULL) {
1173 if (ColumnHasTag(column, tag) && Qualifies(&q, column)) {
1174 TreeColumnList_Append(columns, column);
1175 }
1176 column = column->next;
1177 }
1178 if (!(flags & CFO_NOT_TAIL) &&
1179 ColumnHasTag(tree->columnTail, tag) &&
1180 Qualifies(&q, tree->columnTail)) {
1181 TreeColumnList_Append(columns, tree->columnTail);
1182 }
1183 }
1184 column = NULL;
1185 listIndex += 1 + qualArgsTotal;
1186 }
1187
1188 gotFirstPart:
1189
1190 /* If 1 column, use it and clear the list. */
1191 if (TreeColumnList_Count(columns) == 1) {
1192 column = TreeColumnList_Nth(columns, 0);
1193 columns->count = 0;
1194 }
1195
1196 /* If "all" but only tail column exists, use it. */
1197 if (IS_ALL(column) && (tree->columns == NULL) && !(flags & CFO_NOT_TAIL))
1198 column = tree->columnTail;
1199
1200 /* If > 1 column, no modifiers may follow. */
1201 if ((TreeColumnList_Count(columns) > 1) || IS_ALL(column)) {
1202 if (listIndex < objc) {
1203 Tcl_AppendResult(interp, "unexpected arguments after \"",
1204 (char *) NULL);
1205 for (i = 0; i < listIndex; i++) {
1206 Tcl_AppendResult(interp, Tcl_GetString(objv[i]), (char *) NULL);
1207 if (i != listIndex - 1)
1208 Tcl_AppendResult(interp, " ", (char *) NULL);
1209 }
1210 Tcl_AppendResult(interp, "\"", (char *) NULL);
1211 goto errorExit;
1212 }
1213 }
1214
1215 /* This means a valid specification was given, but there is no such column */
1216 if ((TreeColumnList_Count(columns) == 0) && (column == NULL)) {
1217 if (flags & CFO_NOT_NULL)
1218 goto notNull;
1219 /* Empty list returned */
1220 goto goodExit;
1221 }
1222
1223 /* Process any modifiers following the column we matched above. */
1224 for (; listIndex < objc; /* nothing */) {
1225 int qualArgsTotal = 0;
1226
1227 elemPtr = objv[listIndex];
1228 if (Tcl_GetIndexFromObj(interp, elemPtr, modifiers, "modifier", 0,
1229 &index) != TCL_OK) {
1230 goto errorExit;
1231 }
1232 if (objc - listIndex < modArgs[index]) {
1233 Tcl_AppendResult(interp, "missing arguments to \"",
1234 Tcl_GetString(elemPtr), "\" modifier", NULL);
1235 goto errorExit;
1236 }
1237 if (modQual[index]) {
1238 Qualifiers_Free(&q);
1239 Qualifiers_Init(tree, &q);
1240 if (Qualifiers_Scan(&q, objc, objv, listIndex + modArgs[index],
1241 &qualArgsTotal) != TCL_OK) {
1242 goto errorExit;
1243 }
1244 }
1245 switch ((enum modEnum) index) {
1246 case TMOD_NEXT: {
1247 int isTail = IS_TAIL(column);
1248 if (isTail) {
1249 column = NULL;
1250 break;
1251 }
1252 column = column->next;
1253 while (!Qualifies(&q, column))
1254 column = column->next;
1255 if (column == NULL) {
1256 column = tree->columnTail;
1257 if (!Qualifies(&q, column))
1258 column = NULL;
1259 }
1260 break;
1261 }
1262 case TMOD_PREV: {
1263 int isTail = IS_TAIL(column);
1264 if (isTail)
1265 column = tree->columnLast;
1266 else
1267 column = column->prev;
1268 while (!Qualifies(&q, column))
1269 column = column->prev;
1270 break;
1271 }
1272 case TMOD_SPAN: {
1273 int span, lock;
1274 TreeColumn match = NULL;
1275
1276 if (Tcl_GetIntFromObj(NULL, objv[listIndex + 1], &span) != TCL_OK)
1277 goto errorExit;
1278 lock = column->lock;
1279 while (span-- > 0 && column != NULL && column->lock == lock) {
1280 if (!Qualifies(&q, column))
1281 break;
1282 match = column;
1283 column = column->next;
1284 }
1285 column = match;
1286 break;
1287 }
1288 }
1289 if ((TreeColumnList_Count(columns) == 0) && (column == NULL)) {
1290 if (flags & CFO_NOT_NULL)
1291 goto notNull;
1292 /* Empty list returned. */
1293 goto goodExit;
1294 }
1295 listIndex += modArgs[index] + qualArgsTotal;
1296 }
1297 if ((flags & CFO_NOT_MANY) && (IS_ALL(column) ||
1298 (TreeColumnList_Count(columns) > 1))) {
1299 FormatResult(interp, "can't specify > 1 column for this command");
1300 goto errorExit;
1301 }
1302 if ((flags & CFO_NOT_NULL) && (TreeColumnList_Count(columns) == 0) &&
1303 (column == NULL)) {
1304 notNull:
1305 FormatResult(interp, "column \"%s\" doesn't exist", Tcl_GetString(objPtr));
1306 goto errorExit;
1307 }
1308 if (TreeColumnList_Count(columns)) {
1309 if (flags & (CFO_NOT_TAIL)) {
1310 int i;
1311 for (i = 0; i < TreeColumnList_Count(columns); i++) {
1312 column = TreeColumnList_Nth(columns, i);
1313 if ((flags & CFO_NOT_TAIL) && IS_TAIL(column))
1314 goto notTail;
1315 }
1316 }
1317 } else if (IS_ALL(column)) {
1318 TreeColumnList_Append(columns, column);
1319 } else {
1320 if ((flags & CFO_NOT_TAIL) && IS_TAIL(column)) {
1321 notTail:
1322 FormatResult(interp, "can't specify \"tail\" for this command");
1323 goto errorExit;
1324 }
1325 TreeColumnList_Append(columns, column);
1326 }
1327 goodExit:
1328 Qualifiers_Free(&q);
1329 return TCL_OK;
1330
1331 badDesc:
1332 FormatResult(interp, "bad column description \"%s\"", Tcl_GetString(objPtr));
1333 goto errorExit;
1334
1335 errorExit:
1336 Qualifiers_Free(&q);
1337 TreeColumnList_Free(columns);
1338 return TCL_ERROR;
1339 }
1340
1341 /*
1342 *----------------------------------------------------------------------
1343 *
1344 * TreeColumn_FromObj --
1345 *
1346 * Parse a Tcl_Obj column description to get a single column.
1347 *
1348 * Results:
1349 * TCL_OK or TCL_ERROR.
1350 *
1351 * Side effects:
1352 * None.
1353 *
1354 *----------------------------------------------------------------------
1355 */
1356
1357 int
TreeColumn_FromObj(TreeCtrl * tree,Tcl_Obj * objPtr,TreeColumn * columnPtr,int flags)1358 TreeColumn_FromObj(
1359 TreeCtrl *tree, /* Widget info. */
1360 Tcl_Obj *objPtr, /* Object to parse to a column. */
1361 TreeColumn *columnPtr, /* Returned column. */
1362 int flags /* CFO_xxx flags */
1363 )
1364 {
1365 TreeColumnList columns;
1366
1367 if (TreeColumnList_FromObj(tree, objPtr, &columns, flags | CFO_NOT_MANY) != TCL_OK)
1368 return TCL_ERROR;
1369 /* May be NULL. */
1370 (*columnPtr) = TreeColumnList_Nth(&columns, 0);
1371 TreeColumnList_Free(&columns);
1372 return TCL_OK;
1373 }
1374
1375 /*
1376 *----------------------------------------------------------------------
1377 *
1378 * TreeColumnForEach_Start --
1379 *
1380 * Begin iterating over items. A command might accept two column
1381 * descriptions for a range of column, or a single column description
1382 * which may itself refer to multiple column. Either column
1383 * description could be "all".
1384 *
1385 * Results:
1386 * Returns the first column to iterate over. If an error occurs
1387 * then ColumnForEach.error is set to 1.
1388 *
1389 * Side effects:
1390 * None.
1391 *
1392 *----------------------------------------------------------------------
1393 */
1394
1395 TreeColumn
TreeColumnForEach_Start(TreeColumnList * columns,TreeColumnList * column2s,ColumnForEach * iter)1396 TreeColumnForEach_Start(
1397 TreeColumnList *columns, /* List of columns. */
1398 TreeColumnList *column2s, /* List of columns or NULL. */
1399 ColumnForEach *iter /* Returned info, pass to
1400 TreeColumnForEach_Next. */
1401 )
1402 {
1403 TreeCtrl *tree = columns->tree;
1404 TreeColumn column, column2 = NULL;
1405
1406 column = TreeColumnList_Nth(columns, 0);
1407 if (column2s)
1408 column2 = TreeColumnList_Nth(column2s, 0);
1409
1410 iter->tree = tree;
1411 iter->all = FALSE;
1412 iter->ntail = FALSE;
1413 iter->error = 0;
1414 iter->list = NULL;
1415
1416 if (IS_ALL(column) || IS_ALL(column2)) {
1417 iter->all = TRUE;
1418 iter->ntail = (column == COLUMN_NTAIL) || (column2 == COLUMN_NTAIL);
1419 if (tree->columns == NULL)
1420 return iter->current = iter->ntail ? NULL : tree->columnTail;
1421 iter->next = TreeColumn_Next(tree->columns);
1422 return iter->current = tree->columns;
1423 }
1424
1425 if (column2 != NULL) {
1426 if (TreeColumn_FirstAndLast(&column, &column2) == 0) {
1427 iter->error = 1;
1428 return NULL;
1429 }
1430 iter->next = TreeColumn_Next(column);
1431 iter->last = column2;
1432 return iter->current = column;
1433 }
1434
1435 iter->list = columns;
1436 iter->index = 0;
1437 return iter->current = column;
1438 }
1439
1440 /*
1441 *----------------------------------------------------------------------
1442 *
1443 * TreeColumnForEach_Next --
1444 *
1445 * Returns the next column to iterate over. Keep calling this until
1446 * the result is NULL.
1447 *
1448 * Results:
1449 * Returns the next column to iterate over or NULL.
1450 *
1451 * Side effects:
1452 * None.
1453 *
1454 *----------------------------------------------------------------------
1455 */
1456
1457 TreeColumn
TreeColumnForEach_Next(ColumnForEach * iter)1458 TreeColumnForEach_Next(
1459 ColumnForEach *iter /* Initialized by TreeColumnForEach_Start. */
1460 )
1461 {
1462 TreeCtrl *tree = iter->tree;
1463 TreeColumn column;
1464
1465 if (iter->all) {
1466 if (iter->current == tree->columnTail)
1467 return iter->current = NULL;
1468 column = iter->next;
1469 if (column == NULL)
1470 return iter->current = iter->ntail ? NULL : tree->columnTail;
1471 iter->next = TreeColumn_Next(column);
1472 return iter->current = column;
1473 }
1474
1475 if (iter->list != NULL) {
1476 if (iter->index >= TreeColumnList_Count(iter->list))
1477 return iter->current = NULL;
1478 return iter->current = TreeColumnList_Nth(iter->list, ++iter->index);
1479 }
1480
1481 if (iter->current == iter->last)
1482 return iter->current = NULL;
1483 column = iter->next;
1484 iter->next = TreeColumn_Next(column);
1485 return iter->current = column;
1486 }
1487
1488 /*
1489 *----------------------------------------------------------------------
1490 *
1491 * TreeColumn_ToObj --
1492 *
1493 * Return a Tcl_Obj representing a column.
1494 *
1495 * Results:
1496 * A Tcl_Obj.
1497 *
1498 * Side effects:
1499 * Allocates a Tcl_Obj.
1500 *
1501 *----------------------------------------------------------------------
1502 */
1503
1504 Tcl_Obj *
TreeColumn_ToObj(TreeCtrl * tree,TreeColumn column)1505 TreeColumn_ToObj(
1506 TreeCtrl *tree, /* Widget info. */
1507 TreeColumn column /* Column token to get Tcl_Obj for. */
1508 )
1509 {
1510 if (column == tree->columnTail)
1511 return Tcl_NewStringObj("tail", -1);
1512 if (tree->columnPrefixLen) {
1513 char buf[100 + TCL_INTEGER_SPACE];
1514 (void) sprintf(buf, "%s%d", tree->columnPrefix, column->id);
1515 return Tcl_NewStringObj(buf, -1);
1516 }
1517 return Tcl_NewIntObj(column->id);
1518 }
1519
1520 /*
1521 *----------------------------------------------------------------------
1522 *
1523 * Tree_FindColumn --
1524 *
1525 * Get the N'th column in a TreeCtrl.
1526 *
1527 * Results:
1528 * Token for the N'th column.
1529 *
1530 * Side effects:
1531 * None.
1532 *
1533 *----------------------------------------------------------------------
1534 */
1535
1536 TreeColumn
Tree_FindColumn(TreeCtrl * tree,int columnIndex)1537 Tree_FindColumn(
1538 TreeCtrl *tree, /* Widget info. */
1539 int columnIndex /* 0-based index of the column to return. */
1540 )
1541 {
1542 TreeColumn column = tree->columns;
1543
1544 if (columnIndex == tree->columnTail->index) return tree->columnTail;
1545
1546 while (column != NULL) {
1547 if (column->index == columnIndex)
1548 break;
1549 column = column->next;
1550 }
1551 return column;
1552 }
1553
1554 TreeColumn
Tree_FirstColumn(TreeCtrl * tree,int lock,int tailOK)1555 Tree_FirstColumn(
1556 TreeCtrl *tree,
1557 int lock,
1558 int tailOK
1559 )
1560 {
1561 TreeColumn column = NULL;
1562
1563 switch (lock) {
1564 case COLUMN_LOCK_LEFT:
1565 column = tree->columnLockLeft;
1566 break;
1567 case COLUMN_LOCK_NONE:
1568 column = tree->columnLockNone;
1569 if (column == NULL && tailOK)
1570 column = tree->columnTail;
1571 break;
1572 case COLUMN_LOCK_RIGHT:
1573 column = tree->columnLockRight;
1574 break;
1575 default:
1576 column = tree->columns;
1577 if (column == NULL && tailOK)
1578 column = tree->columnTail;
1579 break;
1580 }
1581 return column;
1582 }
1583
1584 TreeColumn
Tree_ColumnToTheRight(TreeColumn column,int displayOrder,int tailOK)1585 Tree_ColumnToTheRight(
1586 TreeColumn column, /* Column token. */
1587 int displayOrder,
1588 int tailOK
1589 )
1590 {
1591 TreeCtrl *tree = column->tree;
1592 TreeColumn next = column->next;
1593
1594 if (column == tree->columnTail)
1595 tailOK = FALSE;
1596 if (displayOrder && next == tree->columnLockRight) {
1597 return tailOK ? tree->columnTail : NULL;
1598 }
1599 if (next == NULL && tailOK)
1600 return tree->columnTail;
1601 return next;
1602 }
1603
1604 /*
1605 *----------------------------------------------------------------------
1606 *
1607 * TreeColumn_Next --
1608 *
1609 * Return the column to the right of the given one.
1610 *
1611 * Results:
1612 * Token for the next column.
1613 *
1614 * Side effects:
1615 * None.
1616 *
1617 *----------------------------------------------------------------------
1618 */
1619
1620 TreeColumn
TreeColumn_Next(TreeColumn column)1621 TreeColumn_Next(
1622 TreeColumn column /* Column token. */
1623 )
1624 {
1625 return column->next;
1626 }
1627
1628 /*
1629 *----------------------------------------------------------------------
1630 *
1631 * TreeColumn_Prev --
1632 *
1633 * Return the column to the left of the given one.
1634 *
1635 * Results:
1636 * Token for the previous column.
1637 *
1638 * Side effects:
1639 * None.
1640 *
1641 *----------------------------------------------------------------------
1642 */
1643
1644 TreeColumn
TreeColumn_Prev(TreeColumn column)1645 TreeColumn_Prev(
1646 TreeColumn column /* Column token. */
1647 )
1648 {
1649 return column->prev;
1650 }
1651
1652 /*
1653 *----------------------------------------------------------------------
1654 *
1655 * Column_FreeColors --
1656 *
1657 * Frees an array of XColors. This is used to free the -itembackground
1658 * array of colors.
1659 *
1660 * Results:
1661 * None.
1662 *
1663 * Side effects:
1664 * Memory is deallocated, colors are freed.
1665 *
1666 *----------------------------------------------------------------------
1667 */
1668
1669 static void
Column_FreeColors(TreeColumn column,TreeColor ** colors,int count)1670 Column_FreeColors(
1671 TreeColumn column, /* Column token. */
1672 TreeColor **colors, /* Array of colors. May be NULL. */
1673 int count /* Number of colors. */
1674 )
1675 {
1676 int i;
1677
1678 if (colors == NULL) {
1679 return;
1680 }
1681 for (i = 0; i < count; i++) {
1682 if (colors[i] != NULL) {
1683 Tree_FreeColor(column->tree, colors[i]);
1684 }
1685 }
1686 WCFREE(colors, XColor *, count);
1687 }
1688
1689 /*
1690 *----------------------------------------------------------------------
1691 *
1692 * Column_Move --
1693 *
1694 * Move a column before another.
1695 *
1696 * Results:
1697 * If the column is moved, then the list of item-columns for every item
1698 * is rearranged and the treectrl option -defaultstyles is rearranged.
1699 * Whether the column is moved or not, the .index field of every
1700 * column is recalculated.
1701 *
1702 * Side effects:
1703 * A redisplay is scheduled if the moved column is visible.
1704 *
1705 *----------------------------------------------------------------------
1706 */
1707
1708 static void
Column_Move(TreeColumn move,TreeColumn before)1709 Column_Move(
1710 TreeColumn move, /* Column to move. */
1711 TreeColumn before /* Column to place 'move' in front of.
1712 * May be the same as 'move'. */
1713 )
1714 {
1715 TreeCtrl *tree = move->tree;
1716 TreeColumn column, prev, next, last;
1717 Tcl_HashEntry *hPtr;
1718 Tcl_HashSearch search;
1719 TreeItem item;
1720 int index;
1721 #ifdef DEPRECATED
1722 int numStyles;
1723 #endif
1724
1725 if (move == before)
1726 goto renumber;
1727 if (move->index == before->index - 1)
1728 goto renumber;
1729
1730 /* Move the column in every header */
1731 item = tree->headerItems;
1732 while (item != NULL) {
1733 TreeItem_MoveColumn(tree, item, move->index, before->index);
1734 item = TreeItem_GetNextSibling(tree, item);
1735 }
1736
1737 /* Move the column in every item */
1738 hPtr = Tcl_FirstHashEntry(&tree->itemHash, &search);
1739 while (hPtr != NULL) {
1740 item = (TreeItem) Tcl_GetHashValue(hPtr);
1741 TreeItem_MoveColumn(tree, item, move->index, before->index);
1742 hPtr = Tcl_NextHashEntry(&search);
1743 }
1744
1745 /* Indicate that all items must recalculate their list of spans. */
1746 TreeItem_SpansInvalidate(tree, NULL);
1747
1748 #ifdef DEPRECATED
1749 /* Re-order -defaultstyle */
1750 numStyles = tree->defaultStyle.numStyles;
1751 if ((numStyles > 0) && ((before->index < numStyles) ||
1752 (move->index < numStyles))) {
1753 TreeStyle style, *styles;
1754 int i, j;
1755 Tcl_Obj *staticObjv[STATIC_SIZE], **objv = staticObjv;
1756
1757 /* Case 1: move existing */
1758 if ((before->index <= numStyles) && (move->index < numStyles)) {
1759 styles = tree->defaultStyle.styles;
1760 style = styles[move->index];
1761 for (i = move->index; i < numStyles - 1; i++)
1762 styles[i] = styles[i + 1];
1763 j = before->index;
1764 if (move->index < before->index)
1765 j--;
1766 for (i = numStyles - 1; i > j; i--)
1767 styles[i] = styles[i - 1];
1768 styles[j] = style;
1769
1770 /* Case 2: insert empty between existing */
1771 } else if (before->index < numStyles) {
1772 numStyles++;
1773 styles = (TreeStyle *) ckalloc(numStyles * sizeof(TreeStyle));
1774 for (i = 0; i < before->index; i++)
1775 styles[i] = tree->defaultStyle.styles[i];
1776 styles[i++] = NULL;
1777 for (; i < numStyles; i++)
1778 styles[i] = tree->defaultStyle.styles[i - 1];
1779
1780 /* Case 3: move existing past end */
1781 } else {
1782 numStyles += before->index - numStyles;
1783 styles = (TreeStyle *) ckalloc(numStyles * sizeof(TreeStyle));
1784 style = tree->defaultStyle.styles[move->index];
1785 for (i = 0; i < move->index; i++)
1786 styles[i] = tree->defaultStyle.styles[i];
1787 for (; i < tree->defaultStyle.numStyles - 1; i++)
1788 styles[i] = tree->defaultStyle.styles[i + 1];
1789 for (; i < numStyles - 1; i++)
1790 styles[i] = NULL;
1791 styles[i] = style;
1792 }
1793 Tcl_DecrRefCount(tree->defaultStyle.stylesObj);
1794 STATIC_ALLOC(objv, Tcl_Obj *, numStyles);
1795 for (i = 0; i < numStyles; i++) {
1796 if (styles[i] != NULL)
1797 objv[i] = TreeStyle_ToObj(styles[i]);
1798 else
1799 objv[i] = Tcl_NewObj();
1800 }
1801 tree->defaultStyle.stylesObj = Tcl_NewListObj(numStyles, objv);
1802 Tcl_IncrRefCount(tree->defaultStyle.stylesObj);
1803 STATIC_FREE(objv, Tcl_Obj *, numStyles);
1804 if (styles != tree->defaultStyle.styles) {
1805 ckfree((char *) tree->defaultStyle.styles);
1806 tree->defaultStyle.styles = styles;
1807 tree->defaultStyle.numStyles = numStyles;
1808 }
1809 }
1810 #endif /* DEPRECATED */
1811
1812 /* Unlink. */
1813 prev = move->prev;
1814 next = move->next;
1815 if (prev == NULL)
1816 tree->columns = next;
1817 else
1818 prev->next = next;
1819 if (next == NULL)
1820 tree->columnLast = prev;
1821 else
1822 next->prev = prev;
1823
1824 /* Link. */
1825 if (before == tree->columnTail) {
1826 last = tree->columnLast;
1827 last->next = move;
1828 move->prev = last;
1829 move->next = NULL;
1830 tree->columnLast = move;
1831 } else {
1832 prev = before->prev;
1833 if (prev == NULL)
1834 tree->columns = move;
1835 else
1836 prev->next = move;
1837 before->prev = move;
1838 move->prev = prev;
1839 move->next = before;
1840 }
1841
1842 /* Renumber columns */
1843 renumber:
1844 tree->columnLockLeft = NULL;
1845 tree->columnLockNone = NULL;
1846 tree->columnLockRight = NULL;
1847
1848 index = 0;
1849 column = tree->columns;
1850 while (column != NULL) {
1851 column->index = index++;
1852 if (column->lock == COLUMN_LOCK_LEFT && tree->columnLockLeft == NULL)
1853 tree->columnLockLeft = column;
1854 if (column->lock == COLUMN_LOCK_NONE && tree->columnLockNone == NULL)
1855 tree->columnLockNone = column;
1856 if (column->lock == COLUMN_LOCK_RIGHT && tree->columnLockRight == NULL)
1857 tree->columnLockRight = column;
1858 column = column->next;
1859 }
1860
1861 if (move->visible) {
1862 /* Must update column widths because of expansion. */
1863 /* Also update columnTreeLeft. */
1864 TreeColumns_InvalidateWidth(tree);
1865 TreeColumns_InvalidateCounts(tree);
1866 }
1867 }
1868
1869 /*
1870 *----------------------------------------------------------------------
1871 *
1872 * Column_Config --
1873 *
1874 * This procedure is called to process an objc/objv list to set
1875 * configuration options for a Column.
1876 *
1877 * Results:
1878 * The return value is a standard Tcl result. If TCL_ERROR is
1879 * returned, then an error message is left in interp's result.
1880 *
1881 * Side effects:
1882 * Configuration information, such as text string, colors, font,
1883 * etc. get set for column; old resources get freed, if there
1884 * were any. Display changes may occur.
1885 *
1886 *----------------------------------------------------------------------
1887 */
1888
1889 static int
Column_Config(TreeColumn column,int objc,Tcl_Obj * CONST objv[],int createFlag)1890 Column_Config(
1891 TreeColumn column, /* Column record. */
1892 int objc, /* Number of arguments. */
1893 Tcl_Obj *CONST objv[], /* Argument values. */
1894 int createFlag /* TRUE if the Column is being created. */
1895 )
1896 {
1897 TreeCtrl *tree = column->tree;
1898 TreeColumn_ saved;
1899 TreeColumn walk;
1900 Tk_SavedOptions savedOptions;
1901 int error;
1902 Tcl_Obj *errorResult = NULL;
1903 int mask, maskFree = 0;
1904 int visible = column->visible;
1905 int lock = column->lock;
1906 #if COLUMNGRID == 1
1907 int gridLines, prevGridLines = visible &&
1908 (column->gridLeftColor != NULL ||
1909 column->gridRightColor != NULL);
1910 #endif
1911
1912 int objC = 0, hObjC = 0;
1913 Tcl_Obj *staticObjV[STATIC_SIZE], **objV = staticObjV;
1914 Tcl_Obj *staticHObjV[STATIC_SIZE], **hObjV = staticHObjV;
1915 int i;
1916
1917 /* Hack -- Pass some options to the underlying header-column */
1918 STATIC_ALLOC(objV, Tcl_Obj *, objc);
1919 STATIC_ALLOC(hObjV, Tcl_Obj *, objc);
1920 for (i = 0; i < objc; i += 2) {
1921 Tk_OptionSpec *specPtr = columnSpecs;
1922 int length;
1923 CONST char *optionName = Tcl_GetStringFromObj(objv[i], &length);
1924 while (specPtr->type != TK_OPTION_END) {
1925 if (strncmp(specPtr->optionName, optionName, length) == 0) {
1926 objV[objC++] = objv[i];
1927 if (i + 1 < objc)
1928 objV[objC++] = objv[i + 1];
1929 /* Pass -justify to the default header as well */
1930 if (strcmp(specPtr->optionName, "-justify") == 0) {
1931 hObjV[hObjC++] = objv[i];
1932 if (i + 1 < objc)
1933 hObjV[hObjC++] = objv[i + 1];
1934 }
1935 break;
1936 }
1937 specPtr++;
1938 }
1939 if (specPtr->type == TK_OPTION_END) {
1940 hObjV[hObjC++] = objv[i];
1941 if (i + 1 < objc)
1942 hObjV[hObjC++] = objv[i + 1];
1943 }
1944 }
1945 if (TreeHeader_ConsumeColumnConfig(tree, column, hObjC, hObjV, createFlag) != TCL_OK) {
1946 STATIC_FREE(objV, Tcl_Obj *, objc);
1947 STATIC_FREE(hObjV, Tcl_Obj *, objc);
1948 return TCL_ERROR;
1949 }
1950
1951 /* Init these to prevent compiler warnings */
1952 saved.itemBgCount = 0;
1953 saved.itemBgColor = NULL;
1954
1955 for (error = 0; error <= 1; error++) {
1956 if (error == 0) {
1957 if (Tk_SetOptions(tree->interp, (char *) column,
1958 column->optionTable, objC, objV, tree->tkwin,
1959 &savedOptions, &mask) != TCL_OK) {
1960 mask = 0;
1961 continue;
1962 }
1963
1964 /* Wouldn't have to do this if Tk_InitOptions() would return
1965 * a mask of configured options like Tk_SetOptions() does. */
1966 if (createFlag) {
1967 if (column->itemBgObj != NULL)
1968 mask |= COLU_CONF_ITEMBG;
1969 }
1970
1971 /*
1972 * Step 1: Save old values
1973 */
1974
1975 if (mask & COLU_CONF_ITEMBG) {
1976 saved.itemBgColor = column->itemBgColor;
1977 saved.itemBgCount = column->itemBgCount;
1978 }
1979
1980 if (column == tree->columnTail) {
1981 if (column->itemStyle != NULL) {
1982 FormatResult(tree->interp,
1983 "can't change the -itemstyle option of the tail column");
1984 continue;
1985 }
1986 if (column->lock != COLUMN_LOCK_NONE) {
1987 FormatResult(tree->interp,
1988 "can't change the -lock option of the tail column");
1989 continue;
1990 }
1991 }
1992
1993 /*
1994 * Step 2: Process new values
1995 */
1996
1997 if (mask & COLU_CONF_ITEMBG) {
1998 if (column->itemBgObj == NULL) {
1999 column->itemBgColor = NULL;
2000 column->itemBgCount = 0;
2001 } else {
2002 int i, length, listObjc;
2003 Tcl_Obj **listObjv;
2004 TreeColor **colors;
2005
2006 if (Tcl_ListObjGetElements(tree->interp, column->itemBgObj,
2007 &listObjc, &listObjv) != TCL_OK)
2008 continue;
2009 colors = (TreeColor **) ckalloc(sizeof(TreeColor *) * listObjc);
2010 for (i = 0; i < listObjc; i++)
2011 colors[i] = NULL;
2012 for (i = 0; i < listObjc; i++) {
2013 /* Can specify "" for tree background */
2014 (void) Tcl_GetStringFromObj(listObjv[i], &length);
2015 if (length != 0) {
2016 colors[i] = Tree_AllocColorFromObj(tree, listObjv[i]);
2017 if (colors[i] == NULL)
2018 break;
2019 }
2020 }
2021 if (i < listObjc) {
2022 Column_FreeColors(column, colors, listObjc);
2023 continue;
2024 }
2025 column->itemBgColor = colors;
2026 column->itemBgCount = listObjc;
2027 maskFree |= COLU_CONF_ITEMBG;
2028 }
2029 }
2030
2031 /*
2032 * Step 3: Free saved values
2033 */
2034
2035 if (mask & COLU_CONF_ITEMBG)
2036 Column_FreeColors(column, saved.itemBgColor, saved.itemBgCount);
2037 Tk_FreeSavedOptions(&savedOptions);
2038 STATIC_FREE(objV, Tcl_Obj *, objc);
2039 STATIC_FREE(hObjV, Tcl_Obj *, objc);
2040 break;
2041 } else {
2042 errorResult = Tcl_GetObjResult(tree->interp);
2043 Tcl_IncrRefCount(errorResult);
2044 Tk_RestoreSavedOptions(&savedOptions);
2045
2046 /*
2047 * Free new values.
2048 */
2049 if (maskFree & COLU_CONF_ITEMBG)
2050 Column_FreeColors(column, column->itemBgColor, column->itemBgCount);
2051
2052 /*
2053 * Restore old values.
2054 */
2055 if (mask & COLU_CONF_ITEMBG) {
2056 column->itemBgColor = saved.itemBgColor;
2057 column->itemBgCount = saved.itemBgCount;
2058 }
2059
2060 Tcl_SetObjResult(tree->interp, errorResult);
2061 Tcl_DecrRefCount(errorResult);
2062 STATIC_FREE(objV, Tcl_Obj *, objc);
2063 STATIC_FREE(hObjV, Tcl_Obj *, objc);
2064 return TCL_ERROR;
2065 }
2066 }
2067
2068 /* Indicate that all items must recalculate their list of spans. */
2069 if (visible != column->visible || lock != column->lock)
2070 TreeItem_SpansInvalidate(tree, NULL);
2071
2072 if (visible != column->visible || lock != column->lock)
2073 TreeColumns_InvalidateCounts(tree);
2074
2075 if (mask & COLU_CONF_ITEMBG) {
2076 if (!createFlag) {
2077 /* Set max -itembackground */
2078 tree->columnBgCnt = 0;
2079 walk = tree->columns;
2080 while (walk != NULL) {
2081 if (walk->visible) {
2082 if (walk->itemBgCount > tree->columnBgCnt)
2083 tree->columnBgCnt = walk->itemBgCount;
2084 }
2085 walk = walk->next;
2086 }
2087 }
2088 Tree_DInfoChanged(tree, DINFO_INVALIDATE); /* implicit DINFO_DRAW_WHITESPACE */
2089 }
2090
2091 if (!createFlag && (column->lock != lock)) {
2092 TreeColumn before = NULL;
2093 switch (column->lock) {
2094 case COLUMN_LOCK_LEFT:
2095 before = tree->columnLockNone;
2096 if (before == NULL)
2097 before = tree->columnLockRight;
2098 break;
2099 case COLUMN_LOCK_NONE:
2100 if (lock == COLUMN_LOCK_LEFT) {
2101 before = tree->columnLockNone;
2102 if (before == NULL)
2103 before = tree->columnLockRight;
2104 } else
2105 before = tree->columnLockRight;
2106 break;
2107 case COLUMN_LOCK_RIGHT:
2108 before = NULL;
2109 break;
2110 }
2111 if (before == NULL)
2112 before = tree->columnTail;
2113 Column_Move(column, before);
2114 Tree_DInfoChanged(tree, DINFO_REDO_COLUMN_WIDTH);
2115 }
2116
2117 /* If there are no more visible columns, the header isn't shown. */
2118 /* Header height may change with the width of columns (due to text wrapping). */
2119 if (mask & COLU_CONF_TWIDTH) {
2120 tree->headerHeight = -1;
2121 }
2122
2123 /* FIXME: only this column needs to be redisplayed. */
2124 if (mask & COLU_CONF_JUSTIFY)
2125 Tree_DInfoChanged(tree, DINFO_INVALIDATE);
2126
2127 #ifdef DEPRECATED
2128 /* -stepwidth and -widthhack */
2129 if (mask & COLU_CONF_RANGES)
2130 Tree_DInfoChanged(tree, DINFO_REDO_RANGES);
2131 #endif
2132
2133 /* Redraw everything */
2134 if (mask & COLU_CONF_TWIDTH) {
2135 if ((column->reqData.spanMin != NULL) && /* if it's NULL, then it's hidden or tree->spansInvalid=TRUE */
2136 (column->reqData.spanMin != column->reqData.spanMax)) { /* spans of 1 don't require item-width recalc */
2137 TreeColumns_InvalidateWidthOfItems(tree, column);
2138 }
2139 TreeColumns_InvalidateWidth(tree);
2140 Tree_DInfoChanged(tree, DINFO_DRAW_HEADER);
2141 }
2142
2143 /* Redraw header only */
2144 else if (mask & COLU_CONF_DISPLAY) {
2145 Tree_DInfoChanged(tree, DINFO_DRAW_HEADER);
2146 }
2147
2148 #if COLUMNGRID == 1
2149 if (mask & COLU_CONF_GRIDLINES) {
2150 Tree_DInfoChanged(tree, DINFO_INVALIDATE | DINFO_DRAW_WHITESPACE);
2151 }
2152
2153 gridLines = column->visible &&
2154 (column->gridLeftColor != NULL ||
2155 column->gridRightColor != NULL);
2156 if (gridLines != prevGridLines) {
2157 tree->columnsWithGridLines += gridLines ? 1 : -1;
2158 /*dbwin("tree->columnsWithGridLines is now %d", tree->columnsWithGridLines);*/
2159 }
2160 #endif
2161
2162 return TCL_OK;
2163 }
2164
2165 /*
2166 *----------------------------------------------------------------------
2167 *
2168 * Column_Alloc --
2169 *
2170 * Allocate and initialize a new Column record.
2171 *
2172 * Results:
2173 * Pointer to the new Column, or NULL if errors occurred.
2174 *
2175 * Side effects:
2176 * Memory is allocated.
2177 *
2178 *----------------------------------------------------------------------
2179 */
2180
2181 static TreeColumn
Column_Alloc(TreeCtrl * tree)2182 Column_Alloc(
2183 TreeCtrl *tree /* Widget info. */
2184 )
2185 {
2186 TreeColumn column;
2187
2188 column = (TreeColumn) ckalloc(sizeof(TreeColumn_));
2189 memset(column, '\0', sizeof(TreeColumn_));
2190 column->tree = tree;
2191 column->optionTable = Tk_CreateOptionTable(tree->interp, columnSpecs);
2192 column->itemJustify = -1;
2193 if (Tk_InitOptions(tree->interp, (char *) column, column->optionTable,
2194 tree->tkwin) != TCL_OK) {
2195 WFREE(column, TreeColumn_);
2196 return NULL;
2197 }
2198 #if 0
2199 if (Tk_SetOptions(header->tree->interp, (char *) column,
2200 column->optionTable, 0,
2201 NULL, header->tree->tkwin, &savedOptions,
2202 (int *) NULL) != TCL_OK) {
2203 WFREE(column, TreeColumn_);
2204 return NULL;
2205 }
2206 #endif
2207 tree->headerHeight = -1;
2208 /* Don't call TreeColumns_InvalidateWidth(), it will fail during
2209 * Tree_InitColumns(). */
2210 tree->widthOfColumns = -1;
2211 tree->widthOfColumnsLeft = tree->widthOfColumnsRight = -1;
2212 column->id = tree->nextColumnId++;
2213 tree->columnCount++;
2214 return column;
2215 }
2216
2217 /*
2218 *----------------------------------------------------------------------
2219 *
2220 * Column_Free --
2221 *
2222 * Free a Column.
2223 *
2224 * Results:
2225 * Pointer to the next column.
2226 *
2227 * Side effects:
2228 * Memory is deallocated. If this is the last column being
2229 * deleted, the TreeCtrl.nextColumnId field is reset to zero.
2230 *
2231 *----------------------------------------------------------------------
2232 */
2233
2234 static TreeColumn
Column_Free(TreeColumn column)2235 Column_Free(
2236 TreeColumn column /* Column record. */
2237 )
2238 {
2239 TreeCtrl *tree = column->tree;
2240 TreeColumn next = column->next;
2241
2242 Column_FreeColors(column, column->itemBgColor, column->itemBgCount);
2243 TreeDisplay_FreeColumnDInfo(tree, column);
2244 Tk_FreeConfigOptions((char *) column, column->optionTable, tree->tkwin);
2245 if (column->reqData.spans.spans != NULL)
2246 ckfree((char *) column->reqData.spans.spans);
2247 WFREE(column, TreeColumn_);
2248 tree->columnCount--;
2249 if (tree->columnCount == 0)
2250 tree->nextColumnId = 0;
2251 return next;
2252 }
2253
2254 /*
2255 *----------------------------------------------------------------------
2256 *
2257 * TreeColumn_SetDInfo --
2258 *
2259 * Store a display-info token in a column. Called by the display
2260 * code.
2261 *
2262 * Results:
2263 * None.
2264 *
2265 * Side effects:
2266 * None.
2267 *
2268 *----------------------------------------------------------------------
2269 */
2270
2271 void
TreeColumn_SetDInfo(TreeColumn column,TreeColumnDInfo dInfo)2272 TreeColumn_SetDInfo(
2273 TreeColumn column, /* Column record. */
2274 TreeColumnDInfo dInfo /* Display info token. */
2275 )
2276 {
2277 column->dInfo = dInfo;
2278 }
2279
2280 /*
2281 *----------------------------------------------------------------------
2282 *
2283 * TreeColumn_GetDInfo --
2284 *
2285 * Return the display-info token of a column. Called by the display
2286 * code.
2287 *
2288 * Results:
2289 * The display-info token or NULL.
2290 *
2291 * Side effects:
2292 * None.
2293 *
2294 *----------------------------------------------------------------------
2295 */
2296
2297 TreeColumnDInfo
TreeColumn_GetDInfo(TreeColumn column)2298 TreeColumn_GetDInfo(
2299 TreeColumn column /* Column record. */
2300 )
2301 {
2302 return column->dInfo;
2303 }
2304
2305 /*
2306 *----------------------------------------------------------------------
2307 *
2308 * TreeColumn_FixedWidth --
2309 *
2310 * Return the value of the -width option.
2311 *
2312 * Results:
2313 * The pixel width or -1 if the -width option is unspecified.
2314 *
2315 * Side effects:
2316 * None.
2317 *
2318 *----------------------------------------------------------------------
2319 */
2320
2321 int
TreeColumn_FixedWidth(TreeColumn column)2322 TreeColumn_FixedWidth(
2323 TreeColumn column /* Column token. */
2324 )
2325 {
2326 return column->widthObj ? column->width : -1;
2327 }
2328
2329 /*
2330 *----------------------------------------------------------------------
2331 *
2332 * TreeColumn_MinWidth --
2333 *
2334 * Return the value of the -minwidth option.
2335 *
2336 * Results:
2337 * The pixel width or -1 if the -minwidth option is unspecified.
2338 *
2339 * Side effects:
2340 * None.
2341 *
2342 *----------------------------------------------------------------------
2343 */
2344
2345 int
TreeColumn_MinWidth(TreeColumn column)2346 TreeColumn_MinWidth(
2347 TreeColumn column /* Column token. */
2348 )
2349 {
2350 return column->minWidthObj ? column->minWidth : -1;
2351 }
2352
2353 /*
2354 *----------------------------------------------------------------------
2355 *
2356 * TreeColumn_MaxWidth --
2357 *
2358 * Return the value of the -maxwidth option.
2359 *
2360 * Results:
2361 * The pixel width or -1 if the -maxwidth option is unspecified.
2362 *
2363 * Side effects:
2364 * None.
2365 *
2366 *----------------------------------------------------------------------
2367 */
2368
2369 int
TreeColumn_MaxWidth(TreeColumn column)2370 TreeColumn_MaxWidth(
2371 TreeColumn column /* Column token. */
2372 )
2373 {
2374 return column->maxWidthObj ? column->maxWidth : -1;
2375 }
2376
2377 #ifdef DEPRECATED
2378 /*
2379 *----------------------------------------------------------------------
2380 *
2381 * TreeColumn_StepWidth --
2382 *
2383 * Return the value of the -stepwidth option.
2384 * NOTE: -stepwidth is deprecated.
2385 *
2386 * Results:
2387 * The pixel width or -1 if the -stepwidth option is unspecified.
2388 *
2389 * Side effects:
2390 * None.
2391 *
2392 *----------------------------------------------------------------------
2393 */
2394
2395 int
TreeColumn_StepWidth(TreeColumn column)2396 TreeColumn_StepWidth(
2397 TreeColumn column /* Column token. */
2398 )
2399 {
2400 return column->stepWidthObj ? column->stepWidth : -1;
2401 }
2402 #endif /* DEPRECATED */
2403
2404 /*
2405 *----------------------------------------------------------------------
2406 *
2407 * TreeColumn_UseWidth --
2408 *
2409 * Return the actual display width of a column.
2410 *
2411 * Results:
2412 * Pixel width.
2413 *
2414 * Side effects:
2415 * The size of any column that is marked out-of-date is
2416 * recalculated. This could involve recalculating the size of
2417 * every element and style in the column in all items.
2418 *
2419 *----------------------------------------------------------------------
2420 */
2421
2422 int
TreeColumn_UseWidth(TreeColumn column)2423 TreeColumn_UseWidth(
2424 TreeColumn column /* Column token. */
2425 )
2426 {
2427 /* Update layout if needed */
2428 (void) Tree_WidthOfColumns(column->tree);
2429
2430 return column->useWidth;
2431 }
2432
2433 /*
2434 *----------------------------------------------------------------------
2435 *
2436 * TreeColumn_Offset --
2437 *
2438 * Return the x-offset of a column.
2439 *
2440 * Results:
2441 * Pixel offset.
2442 *
2443 * Side effects:
2444 * Column layout is updated if needed.
2445 *
2446 *----------------------------------------------------------------------
2447 */
2448
2449 int
TreeColumn_Offset(TreeColumn column)2450 TreeColumn_Offset(
2451 TreeColumn column /* Column token. */
2452 )
2453 {
2454 /* Update layout if needed */
2455 (void) Tree_WidthOfColumns(column->tree);
2456
2457 return column->offset;
2458 }
2459
2460 /*
2461 *----------------------------------------------------------------------
2462 *
2463 * TreeColumn_ItemJustify --
2464 *
2465 * Return the value of the -itemjustify config option for a column.
2466 * If -itemjustify is unspecified, then return the value of the
2467 * -justify option.
2468 *
2469 * Results:
2470 * TK_JUSTIFY_xxx constant.
2471 *
2472 * Side effects:
2473 * None.
2474 *
2475 *----------------------------------------------------------------------
2476 */
2477
2478 Tk_Justify
TreeColumn_ItemJustify(TreeColumn column)2479 TreeColumn_ItemJustify(
2480 TreeColumn column /* Column token. */
2481 )
2482 {
2483 return (column->itemJustify != -1) ? column->itemJustify : column->justify;
2484 }
2485
2486 #ifdef DEPRECATED
2487 /*
2488 *----------------------------------------------------------------------
2489 *
2490 * TreeColumn_WidthHack --
2491 *
2492 * Return the value of the -widthhack config option for a column.
2493 * NOTE: -widthhack is deprecated.
2494 *
2495 * Results:
2496 * Boolean value.
2497 *
2498 * Side effects:
2499 * None.
2500 *
2501 *----------------------------------------------------------------------
2502 */
2503
2504 int
TreeColumn_WidthHack(TreeColumn column)2505 TreeColumn_WidthHack(
2506 TreeColumn column /* Column token. */
2507 )
2508 {
2509 return column->widthHack;
2510 }
2511 #endif /* DEPRECATED */
2512
2513 /*
2514 *----------------------------------------------------------------------
2515 *
2516 * TreeColumn_Squeeze --
2517 *
2518 * Return the value of the -squeeze config option for a column.
2519 *
2520 * Results:
2521 * Boolean value.
2522 *
2523 * Side effects:
2524 * None.
2525 *
2526 *----------------------------------------------------------------------
2527 */
2528
2529 int
TreeColumn_Squeeze(TreeColumn column)2530 TreeColumn_Squeeze(
2531 TreeColumn column /* Column token. */
2532 )
2533 {
2534 return column->squeeze;
2535 }
2536
2537 /*
2538 *----------------------------------------------------------------------
2539 *
2540 * TreeColumn_BackgroundCount --
2541 *
2542 * Return the number of -itembackground colors for a column.
2543 *
2544 * Results:
2545 * column->itemBgCount.
2546 *
2547 * Side effects:
2548 * None.
2549 *
2550 *----------------------------------------------------------------------
2551 */
2552
2553 int
TreeColumn_BackgroundCount(TreeColumn column)2554 TreeColumn_BackgroundCount(
2555 TreeColumn column /* Column token. */
2556 )
2557 {
2558 return column->itemBgCount;
2559 }
2560
2561 /*
2562 *----------------------------------------------------------------------
2563 *
2564 * TreeColumn_BackgroundColor --
2565 *
2566 * Return a TreeColor for one color of the -itembackground
2567 * config option for a column.
2568 *
2569 * Results:
2570 * A TreeColor, or NULL.
2571 *
2572 * Side effects:
2573 * None.
2574 *
2575 *----------------------------------------------------------------------
2576 */
2577
2578 TreeColor *
TreeColumn_BackgroundColor(TreeColumn column,int index)2579 TreeColumn_BackgroundColor(
2580 TreeColumn column, /* Column token. */
2581 int index /* This number is determined by the display
2582 * code. */
2583 )
2584 {
2585 if ((index < 0) || (column->itemBgCount == 0))
2586 return NULL;
2587 return column->itemBgColor[index % column->itemBgCount];
2588 }
2589
2590 #if COLUMNGRID == 1
2591 /*
2592 *----------------------------------------------------------------------
2593 *
2594 * TreeColumn_GridColors --
2595 *
2596 * Returns the color and width for this column's gridlines.
2597 *
2598 * Results:
2599 * Returns the left and right colors and their widths. Either
2600 * color may be NULL.
2601 *
2602 * Side effects:
2603 * None.
2604 *
2605 *----------------------------------------------------------------------
2606 */
2607
2608 int
TreeColumn_GridColors(TreeColumn column,TreeColor ** leftColorPtr,TreeColor ** rightColorPtr,int * leftWidthPtr,int * rightWidthPtr)2609 TreeColumn_GridColors(
2610 TreeColumn column, /* Column token. */
2611 TreeColor **leftColorPtr, /* Returned left color. */
2612 TreeColor **rightColorPtr, /* Returned right color. */
2613 int *leftWidthPtr,
2614 int *rightWidthPtr
2615 )
2616 {
2617 (*leftColorPtr) = column->gridLeftColor;
2618 (*rightColorPtr) = column->gridRightColor;
2619 (*leftWidthPtr) = 1;
2620 (*rightWidthPtr) = 1;
2621 return (*leftColorPtr != NULL && *leftWidthPtr > 0) ||
2622 (*rightColorPtr != NULL && *rightWidthPtr > 0);
2623 }
2624 #endif
2625
2626 /*
2627 *----------------------------------------------------------------------
2628 *
2629 * TreeColumn_ItemStyle --
2630 *
2631 * Return the value of the -itemstyle config option for a column.
2632 *
2633 * Results:
2634 * TreeStyle or NULL.
2635 *
2636 * Side effects:
2637 * None.
2638 *
2639 *----------------------------------------------------------------------
2640 */
2641
2642 TreeStyle
TreeColumn_ItemStyle(TreeColumn column)2643 TreeColumn_ItemStyle(
2644 TreeColumn column /* Column token. */
2645 )
2646 {
2647 return column->itemStyle;
2648 }
2649
2650 /*
2651 *----------------------------------------------------------------------
2652 *
2653 * TreeColumn_StyleDeleted --
2654 *
2655 * Called when a master style is deleted.
2656 *
2657 * Results:
2658 * Clear the column's -itemstyle option if it is the style being
2659 * deleted.
2660 *
2661 * Side effects:
2662 * None.
2663 *
2664 *----------------------------------------------------------------------
2665 */
2666
2667 void
TreeColumn_StyleDeleted(TreeColumn column,TreeStyle style)2668 TreeColumn_StyleDeleted(
2669 TreeColumn column, /* Column token. */
2670 TreeStyle style /* Style that was deleted. */
2671 )
2672 {
2673 if (column->itemStyle == style)
2674 column->itemStyle = NULL;
2675 }
2676
2677 /*
2678 *----------------------------------------------------------------------
2679 *
2680 * TreeColumn_Visible --
2681 *
2682 * Return the value of the -visible config option for a column.
2683 *
2684 * Results:
2685 * Boolean value.
2686 *
2687 * Side effects:
2688 * None.
2689 *
2690 *----------------------------------------------------------------------
2691 */
2692
2693 int
TreeColumn_Visible(TreeColumn column)2694 TreeColumn_Visible(
2695 TreeColumn column /* Column token. */
2696 )
2697 {
2698 return column->visible;
2699 }
2700
2701 /*
2702 *----------------------------------------------------------------------
2703 *
2704 * TreeColumn_GetID --
2705 *
2706 * Return the unique identifier for a column.
2707 *
2708 * Results:
2709 * Unique integer id.
2710 *
2711 * Side effects:
2712 * None.
2713 *
2714 *----------------------------------------------------------------------
2715 */
2716
TreeColumn_GetID(TreeColumn column)2717 int TreeColumn_GetID(
2718 TreeColumn column /* Column token. */
2719 )
2720 {
2721 return column->id;
2722 }
2723
2724 /*
2725 *----------------------------------------------------------------------
2726 *
2727 * TreeColumn_Lock --
2728 *
2729 * Return the value of the -lock option for a column.
2730 *
2731 * Results:
2732 * One of the COLUMN_LOCK_xxx constants.
2733 *
2734 * Side effects:
2735 * None.
2736 *
2737 *----------------------------------------------------------------------
2738 */
2739
TreeColumn_Lock(TreeColumn column)2740 int TreeColumn_Lock(
2741 TreeColumn column /* Column token. */
2742 )
2743 {
2744 return column->lock;
2745 }
2746
2747 /*
2748 *----------------------------------------------------------------------
2749 *
2750 * TreeColumn_Index --
2751 *
2752 * Return the 0-based index for a column.
2753 *
2754 * Results:
2755 * Position of the column in the list of columns.
2756 *
2757 * Side effects:
2758 * None.
2759 *
2760 *----------------------------------------------------------------------
2761 */
2762
2763 int
TreeColumn_Index(TreeColumn column)2764 TreeColumn_Index(
2765 TreeColumn column /* Column token. */
2766 )
2767 {
2768 return column->index;
2769 }
2770
2771 /*
2772 *----------------------------------------------------------------------
2773 *
2774 * TreeColumn_VisIndex --
2775 *
2776 * Return the 0-based index for a column.
2777 *
2778 * Results:
2779 * Position of the column in the list of visible columns.
2780 *
2781 * Side effects:
2782 * None.
2783 *
2784 *----------------------------------------------------------------------
2785 */
2786
2787 int
TreeColumn_VisIndex(TreeColumn column)2788 TreeColumn_VisIndex(
2789 TreeColumn column /* Column token. */
2790 )
2791 {
2792 TreeCtrl *tree = column->tree;
2793 TreeColumn walk = Tree_FirstColumn(tree, column->lock, TRUE);
2794 int index = 0;
2795
2796 /* FIXME: slow, this is only used by headers in TreeItem_Indent. */
2797 /* I can't calculate this in LayoutColumns because TreeItem_Indent is
2798 * used during that procedure. */
2799
2800 if (!column->visible)
2801 return -1;
2802
2803 while (walk != column) {
2804 if (walk->visible) {
2805 /* I only care about the first non-locked visible column (index==0).*/
2806 return 1;
2807 index++;
2808 }
2809 walk = Tree_ColumnToTheRight(walk, TRUE, TRUE);
2810 }
2811 return index;
2812 }
2813
2814 /*
2815 *----------------------------------------------------------------------
2816 *
2817 * ColumnTagCmd --
2818 *
2819 * This procedure is invoked to process the [column tag] widget
2820 * command. See the user documentation for details on what
2821 * it does.
2822 *
2823 * Results:
2824 * A standard Tcl result.
2825 *
2826 * Side effects:
2827 * See the user documentation.
2828 *
2829 *----------------------------------------------------------------------
2830 */
2831
2832 static int
ColumnTagCmd(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])2833 ColumnTagCmd(
2834 ClientData clientData, /* Widget info. */
2835 Tcl_Interp *interp, /* Current interpreter. */
2836 int objc, /* Number of arguments. */
2837 Tcl_Obj *CONST objv[] /* Argument values. */
2838 )
2839 {
2840 TreeCtrl *tree = clientData;
2841 static CONST char *commandNames[] = {
2842 "add", "expr", "names", "remove", (char *) NULL
2843 };
2844 enum {
2845 COMMAND_ADD, COMMAND_EXPR, COMMAND_NAMES, COMMAND_REMOVE
2846 };
2847 int index;
2848 ColumnForEach iter;
2849 TreeColumnList columns;
2850 TreeColumn column;
2851 int result = TCL_OK;
2852
2853 if (objc < 4) {
2854 Tcl_WrongNumArgs(interp, 3, objv, "command ?arg arg ...?");
2855 return TCL_ERROR;
2856 }
2857
2858 if (Tcl_GetIndexFromObj(interp, objv[3], commandNames, "command", 0,
2859 &index) != TCL_OK) {
2860 return TCL_ERROR;
2861 }
2862
2863 switch (index) {
2864 /* T column tag add C tagList */
2865 case COMMAND_ADD: {
2866 int i, numTags;
2867 Tcl_Obj **listObjv;
2868 Tk_Uid staticTags[STATIC_SIZE], *tags = staticTags;
2869
2870 if (objc != 6) {
2871 Tcl_WrongNumArgs(interp, 4, objv, "column tagList");
2872 return TCL_ERROR;
2873 }
2874 if (TreeColumnList_FromObj(tree, objv[4], &columns, 0) != TCL_OK) {
2875 return TCL_ERROR;
2876 }
2877 if (Tcl_ListObjGetElements(interp, objv[5], &numTags, &listObjv) != TCL_OK) {
2878 result = TCL_ERROR;
2879 break;
2880 }
2881 STATIC_ALLOC(tags, Tk_Uid, numTags);
2882 for (i = 0; i < numTags; i++) {
2883 tags[i] = Tk_GetUid(Tcl_GetString(listObjv[i]));
2884 }
2885 COLUMN_FOR_EACH(column, &columns, NULL, &iter) {
2886 column->tagInfo = TagInfo_Add(tree, column->tagInfo, tags, numTags);
2887 }
2888 STATIC_FREE(tags, Tk_Uid, numTags);
2889 break;
2890 }
2891
2892 /* T column tag expr C tagExpr */
2893 case COMMAND_EXPR: {
2894 TagExpr expr;
2895 int ok = TRUE;
2896
2897 if (objc != 6) {
2898 Tcl_WrongNumArgs(interp, 4, objv, "column tagExpr");
2899 return TCL_ERROR;
2900 }
2901 if (TreeColumnList_FromObj(tree, objv[4], &columns, 0) != TCL_OK) {
2902 return TCL_ERROR;
2903 }
2904 if (TagExpr_Init(tree, objv[5], &expr) != TCL_OK) {
2905 result = TCL_ERROR;
2906 break;
2907 }
2908 COLUMN_FOR_EACH(column, &columns, NULL, &iter) {
2909 if (!TagExpr_Eval(&expr, column->tagInfo)) {
2910 ok = FALSE;
2911 break;
2912 }
2913 }
2914 TagExpr_Free(&expr);
2915 Tcl_SetObjResult(interp, Tcl_NewBooleanObj(ok));
2916 break;
2917 }
2918
2919 /* T column tag names C */
2920 case COMMAND_NAMES: {
2921 Tcl_Obj *listObj;
2922 Tk_Uid *tags = NULL;
2923 int i, tagSpace = 0, numTags = 0;
2924
2925 if (objc != 5) {
2926 Tcl_WrongNumArgs(interp, 4, objv, "column");
2927 return TCL_ERROR;
2928 }
2929 if (TreeColumnList_FromObj(tree, objv[4], &columns, 0) != TCL_OK) {
2930 return TCL_ERROR;
2931 }
2932 COLUMN_FOR_EACH(column, &columns, NULL, &iter) {
2933 tags = TagInfo_Names(tree, column->tagInfo, tags, &numTags, &tagSpace);
2934 }
2935 if (numTags) {
2936 listObj = Tcl_NewListObj(0, NULL);
2937 for (i = 0; i < numTags; i++) {
2938 Tcl_ListObjAppendElement(NULL, listObj,
2939 Tcl_NewStringObj((char *) tags[i], -1));
2940 }
2941 Tcl_SetObjResult(interp, listObj);
2942 ckfree((char *) tags);
2943 }
2944 break;
2945 }
2946
2947 /* T column tag remove C tagList */
2948 case COMMAND_REMOVE: {
2949 int i, numTags;
2950 Tcl_Obj **listObjv;
2951 Tk_Uid staticTags[STATIC_SIZE], *tags = staticTags;
2952
2953 if (objc != 6) {
2954 Tcl_WrongNumArgs(interp, 4, objv, "column tagList");
2955 return TCL_ERROR;
2956 }
2957 if (TreeColumnList_FromObj(tree, objv[4], &columns, 0) != TCL_OK) {
2958 return TCL_ERROR;
2959 }
2960 if (Tcl_ListObjGetElements(interp, objv[5], &numTags, &listObjv) != TCL_OK) {
2961 result = TCL_ERROR;
2962 break;
2963 }
2964 STATIC_ALLOC(tags, Tk_Uid, numTags);
2965 for (i = 0; i < numTags; i++) {
2966 tags[i] = Tk_GetUid(Tcl_GetString(listObjv[i]));
2967 }
2968 COLUMN_FOR_EACH(column, &columns, NULL, &iter) {
2969 column->tagInfo = TagInfo_Remove(tree, column->tagInfo, tags, numTags);
2970 }
2971 STATIC_FREE(tags, Tk_Uid, numTags);
2972 break;
2973 }
2974 }
2975
2976 TreeColumnList_Free(&columns);
2977 return result;
2978 }
2979
2980 /*
2981 *----------------------------------------------------------------------
2982 *
2983 * TreeColumnCmd --
2984 *
2985 * This procedure is invoked to process the [column] widget
2986 * command. See the user documentation for details on what it
2987 * does.
2988 *
2989 * Results:
2990 * A standard Tcl result.
2991 *
2992 * Side effects:
2993 * See the user documentation.
2994 *
2995 *----------------------------------------------------------------------
2996 */
2997
2998 int
TreeColumnCmd(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])2999 TreeColumnCmd(
3000 ClientData clientData, /* Widget info. */
3001 Tcl_Interp *interp, /* Current interpreter. */
3002 int objc, /* Number of arguments. */
3003 Tcl_Obj *CONST objv[] /* Argument values. */
3004 )
3005 {
3006 TreeCtrl *tree = clientData;
3007 static CONST char *commandNames[] = {
3008 "bbox", "cget", "compare", "configure", "count", "create", "delete",
3009 "dragcget", "dragconfigure", "id",
3010 #ifdef DEPRECATED
3011 "index",
3012 #endif
3013 "list", "move", "neededwidth", "order", "tag", "width", (char *) NULL
3014 };
3015 enum {
3016 COMMAND_BBOX, COMMAND_CGET, COMMAND_COMPARE, COMMAND_CONFIGURE,
3017 COMMAND_COUNT, COMMAND_CREATE, COMMAND_DELETE, COMMAND_DRAGCGET,
3018 COMMAND_DRAGCONF, COMMAND_ID,
3019 #ifdef DEPRECATED
3020 COMMAND_INDEX,
3021 #endif
3022 COMMAND_LIST, COMMAND_MOVE, COMMAND_NEEDEDWIDTH, COMMAND_ORDER,
3023 COMMAND_TAG, COMMAND_WIDTH
3024 };
3025 int index;
3026 TreeColumnList columns;
3027 TreeColumn column;
3028 ColumnForEach citer;
3029
3030 if (objc < 3) {
3031 Tcl_WrongNumArgs(interp, 2, objv, "command ?arg arg ...?");
3032 return TCL_ERROR;
3033 }
3034
3035 if (Tcl_GetIndexFromObj(interp, objv[2], commandNames, "command", 0,
3036 &index) != TCL_OK) {
3037 return TCL_ERROR;
3038 }
3039
3040 TreeColumnList_Init(tree, &columns, 0);
3041
3042 switch (index) {
3043 case COMMAND_BBOX: {
3044 int left, top, width, height;
3045
3046 if (objc != 4) {
3047 Tcl_WrongNumArgs(interp, 3, objv, "column");
3048 return TCL_ERROR;
3049 }
3050 if (TreeColumn_FromObj(tree, objv[3], &column,
3051 CFO_NOT_NULL | CFO_NOT_TAIL) != TCL_OK)
3052 return TCL_ERROR;
3053 if (TreeColumn_Bbox(column, &left, &top, &width, &height) < 0)
3054 break;
3055 FormatResult(interp, "%d %d %d %d",
3056 left, top, left + width, top + height);
3057 break;
3058 }
3059
3060 case COMMAND_CGET: {
3061 TreeColumn column;
3062 Tcl_Obj *resultObjPtr;
3063
3064 if (objc != 5) {
3065 Tcl_WrongNumArgs(interp, 3, objv, "column option");
3066 return TCL_ERROR;
3067 }
3068 if (TreeColumn_FromObj(tree, objv[3], &column,
3069 CFO_NOT_NULL) != TCL_OK)
3070 return TCL_ERROR;
3071 {
3072 Tk_OptionSpec *specPtr = columnSpecs;
3073 int length;
3074 CONST char *optionName = Tcl_GetStringFromObj(objv[4], &length);
3075 while (specPtr->type != TK_OPTION_END) {
3076 if (strncmp(specPtr->optionName, optionName, length) == 0) {
3077 break;
3078 }
3079 specPtr++;
3080 }
3081 if (specPtr->type == TK_OPTION_END) {
3082 return TreeHeader_ConsumeColumnCget(tree, column, objv[4]);
3083 }
3084 }
3085 resultObjPtr = Tk_GetOptionValue(interp, (char *) column,
3086 column->optionTable, objv[4], tree->tkwin);
3087 if (resultObjPtr == NULL)
3088 return TCL_ERROR;
3089 Tcl_SetObjResult(interp, resultObjPtr);
3090 break;
3091 }
3092
3093 /* T column compare C op C */
3094 case COMMAND_COMPARE: {
3095 TreeColumn column1, column2;
3096 static CONST char *opName[] = { "<", "<=", "==", ">=", ">", "!=", NULL };
3097 int op, compare = 0, index1, index2;
3098
3099 if (objc != 6) {
3100 Tcl_WrongNumArgs(interp, 3, objv, "column1 op column2");
3101 return TCL_ERROR;
3102 }
3103 if (TreeColumn_FromObj(tree, objv[3], &column1,
3104 CFO_NOT_NULL) != TCL_OK)
3105 return TCL_ERROR;
3106 if (Tcl_GetIndexFromObj(interp, objv[4], opName,
3107 "comparison operator", 0, &op) != TCL_OK)
3108 return TCL_ERROR;
3109 if (TreeColumn_FromObj(tree, objv[5], &column2,
3110 CFO_NOT_NULL) != TCL_OK)
3111 return TCL_ERROR;
3112 index1 = TreeColumn_Index(column1);
3113 index2 = TreeColumn_Index(column2);
3114 switch (op) {
3115 case 0: compare = index1 < index2; break;
3116 case 1: compare = index1 <= index2; break;
3117 case 2: compare = index1 == index2; break;
3118 case 3: compare = index1 >= index2; break;
3119 case 4: compare = index1 > index2; break;
3120 case 5: compare = index1 != index2; break;
3121 }
3122 Tcl_SetObjResult(interp, Tcl_NewBooleanObj(compare));
3123 break;
3124 }
3125
3126 case COMMAND_CONFIGURE: {
3127 if (objc < 4) {
3128 Tcl_WrongNumArgs(interp, 3, objv, "column ?option? ?value? ?option value ...?");
3129 return TCL_ERROR;
3130 }
3131 if (objc <= 5) {
3132 Tcl_Obj *resultObjPtr;
3133 if (TreeColumn_FromObj(tree, objv[3], &column,
3134 CFO_NOT_NULL) != TCL_OK)
3135 return TCL_ERROR;
3136 if (objc == 5) {
3137 Tk_OptionSpec *specPtr = columnSpecs;
3138 int length;
3139 CONST char *optionName = Tcl_GetStringFromObj(objv[4], &length);
3140 while (specPtr->type != TK_OPTION_END) {
3141 if (strncmp(specPtr->optionName, optionName, length) == 0) {
3142 break;
3143 }
3144 specPtr++;
3145 }
3146 if (specPtr->type == TK_OPTION_END) {
3147 resultObjPtr = TreeHeader_ConsumeColumnOptionInfo(tree, column, objv[4]);
3148 if (resultObjPtr == NULL)
3149 goto errorExit;
3150 Tcl_SetObjResult(interp, resultObjPtr);
3151 break;
3152 }
3153 resultObjPtr = Tk_GetOptionInfo(interp, (char *) column,
3154 column->optionTable,
3155 (objc == 4) ? (Tcl_Obj *) NULL : objv[4],
3156 tree->tkwin);
3157 if (resultObjPtr == NULL)
3158 goto errorExit;
3159 Tcl_SetObjResult(interp, resultObjPtr);
3160 } else {
3161 /* Return the combined [configure] output of the column and header-column */
3162 Tcl_Obj *objPtr = Tk_GetOptionInfo(interp, (char *) column,
3163 column->optionTable, (Tcl_Obj *) NULL, tree->tkwin);
3164 resultObjPtr = TreeHeader_ConsumeColumnOptionInfo(tree, column, NULL);
3165 Tcl_ListObjAppendList(interp, resultObjPtr, objPtr);
3166 Tcl_SetObjResult(interp, resultObjPtr);
3167 break;
3168 }
3169 break;
3170 }
3171 /* If "all" is specified, get a list of columns instead of
3172 * COLUMN_ALL, since changing the -lock option of a column
3173 * may reorder columns. */
3174 if (TreeColumnList_FromObj(tree, objv[3], &columns,
3175 CFO_LIST_ALL | CFO_NOT_NULL) != TCL_OK)
3176 return TCL_ERROR;
3177 COLUMN_FOR_EACH(column, &columns, NULL, &citer) {
3178 if (Column_Config(column, objc - 4, objv + 4, FALSE) != TCL_OK)
3179 goto errorExit;
3180 }
3181 break;
3182 }
3183
3184 case COMMAND_CREATE: {
3185 TreeColumn column, last = tree->columnLast;
3186 TreeItem item;
3187
3188 /* FIXME: -count N -tags $tags */
3189 column = Column_Alloc(tree);
3190
3191 column->index = TreeColumn_Index(tree->columnTail) + 1; /* after the tail column */
3192
3193 /* Create the item-column and header-column in every header */
3194 item = tree->headerItems;
3195 while (item != NULL) {
3196 (void) TreeItem_MakeColumnExist(tree, item, column->index);
3197 item = TreeItem_GetNextSibling(tree, item);
3198 }
3199 column->index = TreeColumn_Index(tree->columnTail);
3200
3201 if (Column_Config(column, objc - 3, objv + 3, TRUE) != TCL_OK) {
3202
3203 /* Delete the item-column and header-column in every header */
3204 item = tree->headerItems;
3205 while (item != NULL) {
3206 TreeItem_RemoveColumns(tree, item, column->index, column->index);
3207 item = TreeItem_GetNextSibling(tree, item);
3208 }
3209
3210 Column_Free(column);
3211 return TCL_ERROR;
3212 }
3213
3214 if (tree->columns == NULL) {
3215 column->index = 0;
3216 tree->columns = column;
3217 } else {
3218 last->next = column;
3219 column->prev = last;
3220 column->index = last->index + 1;
3221 }
3222 tree->columnLast = column;
3223 tree->columnTail->index++;
3224
3225 {
3226 TreeColumn before = NULL;
3227 switch (column->lock) {
3228 case COLUMN_LOCK_LEFT:
3229 before = tree->columnLockNone;
3230 if (before == NULL)
3231 before = tree->columnLockRight;
3232 break;
3233 case COLUMN_LOCK_NONE:
3234 before = tree->columnLockRight;
3235 break;
3236 case COLUMN_LOCK_RIGHT:
3237 before = NULL;
3238 break;
3239 }
3240 if (before == NULL)
3241 before = tree->columnTail;
3242 Column_Move(column, before);
3243 }
3244
3245 item = tree->headerItems;
3246 while (item != NULL) {
3247 TreeItemColumn itemColumn = TreeItem_FindColumn(tree, item,
3248 column->index);
3249 TreeHeaderColumn_EnsureStyleExists(TreeItem_GetHeader(tree,
3250 item), TreeItemColumn_GetHeaderColumn(tree, itemColumn),
3251 column);
3252 item = TreeItem_GetNextSibling(tree, item);
3253 }
3254
3255 /* Indicate that all items must recalculate their list of spans. */
3256 TreeItem_SpansInvalidate(tree, NULL);
3257
3258 TreeColumns_InvalidateCounts(tree);
3259 Tree_DInfoChanged(tree, DINFO_REDO_COLUMN_WIDTH);
3260 Tcl_SetObjResult(interp, TreeColumn_ToObj(tree, column));
3261 break;
3262 }
3263
3264 /* T column delete first ?last? */
3265 case COMMAND_DELETE: {
3266 TreeColumnList column2s;
3267 TreeColumn prev, next;
3268 int flags = CFO_NOT_NULL | CFO_NOT_TAIL;
3269 TreeItem item;
3270 Tcl_HashEntry *hPtr;
3271 Tcl_HashSearch search;
3272 int index;
3273
3274 if (objc < 4 || objc > 5) {
3275 Tcl_WrongNumArgs(interp, 3, objv, "first ?last?");
3276 return TCL_ERROR;
3277 }
3278 if (objc == 5)
3279 flags |= CFO_NOT_MANY;
3280 if (TreeColumnList_FromObj(tree, objv[3], &columns,
3281 flags) != TCL_OK)
3282 goto errorExit;
3283 if (objc == 5) {
3284 if (TreeColumnList_FromObj(tree, objv[4], &column2s,
3285 CFO_NOT_NULL | CFO_NOT_TAIL) != TCL_OK)
3286 goto errorExit;
3287 }
3288 COLUMN_FOR_EACH(column, &columns, (objc == 5) ? &column2s : NULL,
3289 &citer) {
3290 /* T column delete "all" */
3291 if (citer.all) {
3292 column = tree->columns;
3293 while (column != NULL) {
3294 TreeDisplay_ColumnDeleted(tree, column);
3295 TreeHeader_ColumnDeleted(tree, column);
3296 TreeGradient_ColumnDeleted(tree, column);
3297 #if COLUMNGRID == 1
3298 if (column->visible &&
3299 (column->gridLeftColor != NULL ||
3300 column->gridRightColor != NULL)) {
3301 tree->columnsWithGridLines -= 1;
3302 }
3303 #endif
3304 column = Column_Free(column);
3305 }
3306 tree->columnTail->index = 0;
3307 tree->columns = NULL;
3308 tree->columnLast = NULL;
3309 tree->columnLockLeft = NULL;
3310 tree->columnLockNone = NULL;
3311 tree->columnLockRight = NULL;
3312
3313 /* Delete all TreeItemColumns */
3314 item = tree->headerItems;
3315 while (item != NULL) {
3316 TreeItem_RemoveAllColumns(tree, item);
3317 item = TreeItem_GetNextSibling(tree, item);
3318 }
3319
3320 /* Delete all TreeItemColumns */
3321 hPtr = Tcl_FirstHashEntry(&tree->itemHash, &search);
3322 while (hPtr != NULL) {
3323 item = (TreeItem) Tcl_GetHashValue(hPtr);
3324 TreeItem_RemoveAllColumns(tree, item);
3325 hPtr = Tcl_NextHashEntry(&search);
3326 }
3327
3328 tree->columnTree = NULL;
3329 goto doneDELETE;
3330 }
3331
3332 /* Delete all TreeItemColumns */
3333 item = tree->headerItems;
3334 while (item != NULL) {
3335 TreeItem_RemoveColumns(tree, item, column->index,
3336 column->index);
3337 item = TreeItem_GetNextSibling(tree, item);
3338 }
3339
3340 /* Delete all TreeItemColumns */
3341 hPtr = Tcl_FirstHashEntry(&tree->itemHash, &search);
3342 while (hPtr != NULL) {
3343 item = (TreeItem) Tcl_GetHashValue(hPtr);
3344 TreeItem_RemoveColumns(tree, item, column->index,
3345 column->index);
3346 hPtr = Tcl_NextHashEntry(&search);
3347 }
3348
3349 TreeDisplay_ColumnDeleted(tree, column);
3350 TreeHeader_ColumnDeleted(tree, column);
3351 TreeGradient_ColumnDeleted(tree, column);
3352 #if COLUMNGRID == 1
3353 if (column->visible &&
3354 (column->gridLeftColor != NULL ||
3355 column->gridRightColor != NULL)) {
3356 tree->columnsWithGridLines -= 1;
3357 /*dbwin("tree->columnsWithGridLines is now %d", tree->columnsWithGridLines);*/
3358 }
3359 #endif
3360
3361 /* Unlink. */
3362 prev = column->prev;
3363 next = column->next;
3364 if (prev == NULL)
3365 tree->columns = next;
3366 else
3367 prev->next = next;
3368 if (next == NULL)
3369 tree->columnLast = prev;
3370 else
3371 next->prev = prev;
3372
3373 if (column == tree->columnTree)
3374 tree->columnTree = NULL;
3375
3376 (void) Column_Free(column);
3377
3378 /* Renumber trailing columns */
3379 column = next;
3380 while (column != NULL) {
3381 column->index--;
3382 column = column->next;
3383 }
3384 }
3385
3386 tree->columnLockLeft = NULL;
3387 tree->columnLockNone = NULL;
3388 tree->columnLockRight = NULL;
3389
3390 index = 0;
3391 column = tree->columns;
3392 while (column != NULL) {
3393 column->index = index++;
3394 if (column->lock == COLUMN_LOCK_LEFT && tree->columnLockLeft == NULL)
3395 tree->columnLockLeft = column;
3396 if (column->lock == COLUMN_LOCK_NONE && tree->columnLockNone == NULL)
3397 tree->columnLockNone = column;
3398 if (column->lock == COLUMN_LOCK_RIGHT && tree->columnLockRight == NULL)
3399 tree->columnLockRight = column;
3400 column = column->next;
3401 }
3402 tree->columnTail->index = index;
3403
3404 doneDELETE:
3405 TreeColumns_InvalidateCounts(tree);
3406 tree->widthOfColumns = tree->headerHeight = -1;
3407 tree->widthOfColumnsLeft = tree->widthOfColumnsRight = -1;
3408 tree->columnPriv->reqInvalid = TRUE;
3409 Tree_DInfoChanged(tree, DINFO_REDO_COLUMN_WIDTH);
3410
3411 /* Indicate that all items must recalculate their list of spans. */
3412 TreeItem_SpansInvalidate(tree, NULL);
3413
3414 if (objc == 5)
3415 TreeColumnList_Free(&column2s);
3416 break;
3417 }
3418
3419 /* T column dragcget option */
3420 case COMMAND_DRAGCGET: {
3421 return TreeHeaderCmd(clientData, interp, objc, objv);
3422 }
3423
3424 /* T column dragconfigure ?option? ?value? ?option value ...? */
3425 case COMMAND_DRAGCONF: {
3426 return TreeHeaderCmd(clientData, interp, objc, objv);
3427 }
3428
3429 case COMMAND_COUNT: {
3430 int count = tree->columnCount;
3431
3432 if (objc < 3 || objc > 4) {
3433 Tcl_WrongNumArgs(interp, 3, objv, "?columnDesc?");
3434 return TCL_ERROR;
3435 }
3436 if (objc == 4) {
3437 if (TreeColumnList_FromObj(tree, objv[3], &columns, 0)
3438 != TCL_OK)
3439 return TCL_ERROR;
3440 count = 0;
3441 COLUMN_FOR_EACH(column, &columns, NULL, &citer) {
3442 count++;
3443 }
3444 }
3445 Tcl_SetObjResult(interp, Tcl_NewIntObj(count));
3446 break;
3447 }
3448
3449 case COMMAND_WIDTH: {
3450 if (objc != 4) {
3451 Tcl_WrongNumArgs(interp, 3, objv, "column");
3452 return TCL_ERROR;
3453 }
3454 if (TreeColumn_FromObj(tree, objv[3], &column,
3455 CFO_NOT_NULL) != TCL_OK)
3456 return TCL_ERROR;
3457
3458 Tcl_SetObjResult(interp, Tcl_NewIntObj(TreeColumn_UseWidth(column)));
3459 break;
3460 }
3461
3462 case COMMAND_ID:
3463 #ifdef DEPRECATED
3464 case COMMAND_INDEX:
3465 #endif
3466 {
3467 Tcl_Obj *listObj;
3468
3469 if (objc != 4) {
3470 Tcl_WrongNumArgs(interp, 3, objv, "column");
3471 return TCL_ERROR;
3472 }
3473 if (TreeColumnList_FromObj(tree, objv[3], &columns, 0) != TCL_OK)
3474 return TCL_ERROR;
3475 listObj = Tcl_NewListObj(0, NULL);
3476 COLUMN_FOR_EACH(column, &columns, NULL, &citer) {
3477 Tcl_ListObjAppendElement(interp, listObj,
3478 TreeColumn_ToObj(tree, column));
3479 }
3480 Tcl_SetObjResult(interp, listObj);
3481 break;
3482 }
3483
3484 /* T column list ?-visible? */
3485 case COMMAND_LIST: {
3486 TreeColumn column = tree->columns;
3487 Tcl_Obj *listObj;
3488 int visible = FALSE;
3489
3490 if (objc > 4) {
3491 Tcl_WrongNumArgs(interp, 3, objv, "?-visible?");
3492 return TCL_ERROR;
3493 }
3494 if (objc == 4) {
3495 int len;
3496 char *s = Tcl_GetStringFromObj(objv[3], &len);
3497 if ((s[0] == '-') && (strncmp(s, "-visible", len) == 0))
3498 visible = TRUE;
3499 else {
3500 FormatResult(interp, "bad switch \"%s\": must be -visible",
3501 s);
3502 return TCL_ERROR;
3503 }
3504 }
3505 listObj = Tcl_NewListObj(0, NULL);
3506 while (column != NULL) {
3507 if (!visible || column->visible)
3508 Tcl_ListObjAppendElement(interp, listObj,
3509 TreeColumn_ToObj(tree, column));
3510 column = column->next;
3511 }
3512 Tcl_SetObjResult(interp, listObj);
3513 break;
3514 }
3515
3516 /* T column move C before */
3517 case COMMAND_MOVE: {
3518 TreeColumn move, before;
3519 TreeColumn first = NULL, last = tree->columnTail;
3520
3521 if (objc != 5) {
3522 Tcl_WrongNumArgs(interp, 3, objv, "column before");
3523 return TCL_ERROR;
3524 }
3525 if (TreeColumn_FromObj(tree, objv[3], &move,
3526 CFO_NOT_NULL | CFO_NOT_TAIL) != TCL_OK)
3527 return TCL_ERROR;
3528 if (TreeColumn_FromObj(tree, objv[4], &before,
3529 CFO_NOT_NULL) != TCL_OK)
3530 return TCL_ERROR;
3531
3532 if ((move == before) || (move->index == before->index - 1))
3533 break;
3534 switch (move->lock) {
3535 case COLUMN_LOCK_LEFT:
3536 first = tree->columnLockLeft;
3537 if (tree->columnLockNone != NULL)
3538 last = tree->columnLockNone;
3539 else if (tree->columnLockRight != NULL)
3540 last = tree->columnLockRight;
3541 break;
3542 case COLUMN_LOCK_NONE:
3543 first = tree->columnLockNone;
3544 if (tree->columnLockRight != NULL)
3545 last = tree->columnLockRight;
3546 break;
3547 case COLUMN_LOCK_RIGHT:
3548 first = tree->columnLockRight;
3549 break;
3550 }
3551 if (before->index < first->index || before->index > last->index) {
3552 if (before == tree->columnTail) {
3553 FormatResult(tree->interp,
3554 "can't move column %s%d before tail: -lock options conflict",
3555 tree->columnPrefix, move->id);
3556 } else {
3557 FormatResult(tree->interp,
3558 "can't move column %s%d before column %s%d: -lock options conflict",
3559 tree->columnPrefix, move->id,
3560 tree->columnPrefix, before->id);
3561 }
3562 return TCL_ERROR;
3563 }
3564 Column_Move(move, before);
3565
3566 /* Indicate that all items must recalculate their list of spans. */
3567 TreeItem_SpansInvalidate(tree, NULL);
3568 break;
3569 }
3570
3571 case COMMAND_NEEDEDWIDTH: {
3572 TreeColumn column;
3573 int width;
3574
3575 if (objc != 4) {
3576 Tcl_WrongNumArgs(interp, 3, objv, "column");
3577 return TCL_ERROR;
3578 }
3579 if (TreeColumn_FromObj(tree, objv[3], &column,
3580 CFO_NOT_NULL) != TCL_OK)
3581 return TCL_ERROR;
3582
3583 /* Update layout if needed */
3584 (void) Tree_CanvasWidth(tree);
3585 width = TreeColumn_WidthOfItems(column);
3586 width = MAX(width, TreeColumn_WidthOfHeaders(column));
3587 Tcl_SetObjResult(interp, Tcl_NewIntObj(width));
3588 break;
3589 }
3590
3591 /* T column order C ?-visible? */
3592 case COMMAND_ORDER: {
3593 TreeColumn column;
3594 int visible = FALSE;
3595 int index = 0;
3596
3597 if (objc < 4 || objc > 5) {
3598 Tcl_WrongNumArgs(interp, 3, objv, "column ?-visible?");
3599 return TCL_ERROR;
3600 }
3601 if (objc == 5) {
3602 int len;
3603 char *s = Tcl_GetStringFromObj(objv[4], &len);
3604 if ((s[0] == '-') && (strncmp(s, "-visible", len) == 0))
3605 visible = TRUE;
3606 else {
3607 FormatResult(interp, "bad switch \"%s\": must be -visible",
3608 s);
3609 return TCL_ERROR;
3610 }
3611 }
3612 if (TreeColumn_FromObj(tree, objv[3], &column,
3613 CFO_NOT_NULL) != TCL_OK)
3614 return TCL_ERROR;
3615 if (visible) {
3616 TreeColumn walk = tree->columns;
3617 while (walk != NULL) {
3618 if (walk == column)
3619 break;
3620 if (walk->visible)
3621 index++;
3622 walk = walk->next;
3623 }
3624 if (!column->visible)
3625 index = -1;
3626 } else {
3627 index = column->index;
3628 }
3629 Tcl_SetObjResult(interp, Tcl_NewIntObj(index));
3630 break;
3631 }
3632
3633 case COMMAND_TAG: {
3634 return ColumnTagCmd(clientData, interp, objc, objv);
3635 }
3636 }
3637
3638 TreeColumnList_Free(&columns);
3639 return TCL_OK;
3640
3641 errorExit:
3642 TreeColumnList_Free(&columns);
3643 return TCL_ERROR;
3644 }
3645
3646 /*
3647 *----------------------------------------------------------------------
3648 *
3649 * InitColumnReqData --
3650 *
3651 * Initialize the ColumnReqData structure in every column.
3652 *
3653 * Results:
3654 * None.
3655 *
3656 * Side effects:
3657 * None.
3658 *
3659 *----------------------------------------------------------------------
3660 */
3661
3662 static void
InitColumnReqData(TreeCtrl * tree)3663 InitColumnReqData(
3664 TreeCtrl *tree
3665 )
3666 {
3667 TreeColumnPriv priv = tree->columnPriv;
3668 ColumnReqData *cd;
3669 TreeColumn column;
3670
3671 if (!priv->reqInvalid || tree->columnCount == 0)
3672 return;
3673
3674 /*dbwin("InitColumnReqData %s\n", Tk_PathName(tree->tkwin));*/
3675
3676 for (column = tree->columns;
3677 column != NULL;
3678 column = column->next) {
3679 cd = &column->reqData;
3680 cd->vis = TreeColumn_Visible(column);
3681 cd->min = TreeColumn_MinWidth(column);
3682 cd->fixed = TreeColumn_FixedWidth(column);
3683 cd->max = TreeColumn_MaxWidth(column);
3684 /* cd->req = 0;*/
3685 if ((cd->max >= 0) && (cd->min > cd->max))
3686 cd->min = cd->max;
3687 }
3688
3689 priv->reqInvalid = FALSE;
3690 }
3691
3692 /*
3693 *----------------------------------------------------------------------
3694 *
3695 * SpanArray_Add --
3696 *
3697 * Adds a ColumnSpan pointer to an array if it isn't already in
3698 * the array.
3699 *
3700 * Results:
3701 * None.
3702 *
3703 * Side effects:
3704 * Memory may be allocated.
3705 *
3706 *----------------------------------------------------------------------
3707 */
3708
3709 static void
SpanArray_Add(SpanArray * sa,ColumnSpan * cs)3710 SpanArray_Add(
3711 SpanArray *sa,
3712 ColumnSpan *cs
3713 )
3714 {
3715 int i;
3716
3717 for (i = 0; i < sa->count; i++) {
3718 if (sa->spans[i] == cs)
3719 return;
3720 }
3721 if (sa->alloc < sa->count + 1) {
3722 sa->spans = (ColumnSpan **) ckrealloc((char *) sa->spans,
3723 sizeof(ColumnSpan *) * (sa->count + 10));
3724 sa->alloc = sa->count + 10;
3725 }
3726 sa->spans[sa->count++] = cs;
3727 }
3728
3729 /*
3730 *----------------------------------------------------------------------
3731 *
3732 * AddColumnSpan --
3733 *
3734 * Adds or updates a span record for a range of columns
3735 * covered by a span. For every unique range of columns covered
3736 * by a span, there exists exactly one ColumnSpan record.
3737 *
3738 * Results:
3739 * Pointer to a new or updated ColumnSpan.
3740 *
3741 * Side effects:
3742 * Memory may be allocated.
3743 *
3744 *----------------------------------------------------------------------
3745 */
3746
3747 static ColumnSpan *
AddColumnSpan(ColumnSpan * spanPrev,TreeColumn spanMin,TreeColumn spanMax,int neededWidth,int doHeaders)3748 AddColumnSpan(
3749 ColumnSpan *spanPrev, /* The span to the left. The span returned
3750 * by this function will be added to the
3751 * spanToTheRight array of spanPrev if it
3752 * wasn't already. */
3753 TreeColumn spanMin, /* First column in the span. */
3754 TreeColumn spanMax, /* Last column in the span. */
3755 int neededWidth, /* Width needed by the span. */
3756 int doHeaders /* TRUE if this span is in a header, FALSE
3757 * if the span is in an item. */
3758 )
3759 {
3760 TreeCtrl *tree = spanMin->tree;
3761 TreeColumnPriv priv = tree->columnPriv;
3762 ColumnSpan *cs = priv->spans;
3763 ColumnReqData *cd = &spanMin->reqData;
3764 TreeColumn column;
3765 int i;
3766
3767 /* See if a span record exists by checking the list of spans touching
3768 * the first column. */
3769 for (i = 0; i < cd->spans.count; i++) {
3770 cs = cd->spans.spans[i];
3771 if ((cs->start == spanMin) && (cs->end == spanMax))
3772 break;
3773 }
3774 if (i < cd->spans.count) {
3775 /* Add this span to the list of spans following spanPrev. */
3776 if (spanPrev != NULL && priv->spansInvalid == TRUE)
3777 SpanArray_Add(&spanPrev->spansToRight, cs);
3778
3779 cs->maxNeededWidth = MAX(cs->maxNeededWidth, neededWidth);
3780
3781 /* Remember the widest span of 1 in this column. */
3782 if (spanMin == spanMax) {
3783 cd->maxSingleSpanWidth = MAX(cd->maxSingleSpanWidth, neededWidth);
3784 if (doHeaders)
3785 cd->maxSingleHeaderWidth = MAX(cd->maxSingleHeaderWidth, neededWidth);
3786 else
3787 cd->maxSingleItemWidth = MAX(cd->maxSingleItemWidth, neededWidth);
3788 }
3789 return cs;
3790 }
3791
3792 #ifdef TREECTRL_DEBUG
3793 if (priv->spansInvalid == FALSE) BreakIntoDebugger();
3794 #endif
3795
3796 if (priv->freeSpans == NULL) {
3797 cs = (ColumnSpan *) ckalloc(sizeof(ColumnSpan));
3798 cs->spansToRight.alloc = 0;
3799 cs->spansToRight.spans = NULL;
3800 } else {
3801 cs = priv->freeSpans;
3802 priv->freeSpans = cs->next;
3803 }
3804 cs->start = spanMin;
3805 cs->end = spanMax;
3806 cs->maxNeededWidth = neededWidth;
3807 cs->spansToRight.count = 0;
3808
3809 cs->next = priv->spans;
3810 priv->spans = cs;
3811
3812 cs->nextCur = priv->spansCur;
3813 priv->spansCur = cs;
3814
3815 /* Add this span to the list of spans following spanPrev. */
3816 if (spanPrev != NULL)
3817 SpanArray_Add(&spanPrev->spansToRight, cs);
3818
3819 for (column = spanMin; column != spanMax->next; column = column->next) {
3820 cd = &column->reqData;
3821 /* Add this new span record to the list of span records touching
3822 * this column. */
3823 SpanArray_Add(&cd->spans, cs);
3824
3825 /* Track the minimum and maximum columns of any span touching this
3826 * column.*/
3827 if (priv->spansInvalid == FALSE) {
3828 #ifdef TREECTRL_DEBUG
3829 if (spanMin->index < cd->spanMin->index) BreakIntoDebugger();
3830 if (spanMax->index > cd->spanMax->index) BreakIntoDebugger();
3831 #endif
3832 } else {
3833 if (spanMin->index < cd->spanMin->index)
3834 cd->spanMin = spanMin;
3835 if (spanMax->index > cd->spanMax->index)
3836 cd->spanMax = spanMax;
3837 }
3838
3839 /* Remember the widest span of 1 in this column. */
3840 if (spanMin == spanMax) {
3841 cd->maxSingleSpanWidth = MAX(cd->maxSingleSpanWidth, neededWidth);
3842 if (doHeaders)
3843 cd->maxSingleHeaderWidth = MAX(cd->maxSingleHeaderWidth, neededWidth);
3844 else
3845 cd->maxSingleItemWidth = MAX(cd->maxSingleItemWidth, neededWidth);
3846 } else
3847 priv->allSpansAreOne = FALSE;
3848 }
3849 return cs;
3850 }
3851 /*
3852 s1 -> s2 -> s3
3853 |_ s4 -> s5
3854 s6 -> s7 -> s8
3855 */
3856 static int
SumSpanWidths(int * sum,SpanArray * sa,TreeColumn end)3857 SumSpanWidths(
3858 int *sum, /* The current total span width. */
3859 SpanArray *sa, /* For each span in this array, the
3860 * maximum width of each chain of spans
3861 * starting with that span is found and
3862 * added to *sum. */
3863 TreeColumn end /* The column up to and including which the
3864 * sum of spans should be found. */
3865 )
3866 {
3867 int i, max = 0;
3868 int visited = 0;
3869
3870 for (i = 0; i < sa->count; i++) {
3871 ColumnSpan *cs = sa->spans[i];
3872 if (cs->end->index <= end->index) {
3873 visited++;
3874 if (cs->sumOfSpans == -1) {
3875 cs->sumOfSpans = cs->maxNeededWidth;
3876 visited += SumSpanWidths(&cs->sumOfSpans, &cs->spansToRight, end);
3877 }
3878 max = MAX(max, cs->sumOfSpans);
3879 }
3880 }
3881 *sum += max;
3882 return visited;
3883 }
3884
3885 /*
3886 *----------------------------------------------------------------------
3887 *
3888 * TrimTheFat --
3889 *
3890 * Removes excess width from columns that may have been added by
3891 * DistributeSpanWidthToColumns(). In the scenario below, where
3892 * two items and the width needed by each of 3 spans is shown,
3893 * Column0 will have a requested width of 75, and Column1 will
3894 * have a requested width of 100/2=50, for a total width of
3895 * 75+50=125, which is wider than any item needs. At the end of
3896 * this procedure, the requested width of Column1 will be 25.
3897 *
3898 * Column0 Column1
3899 * item1: 75----- 25----
3900 * item2: 100-----------
3901 *
3902 * Results:
3903 * None.
3904 *
3905 * Side effects:
3906 * None.
3907 *
3908 *----------------------------------------------------------------------
3909 */
3910
3911 static void
TrimTheFatAux(TreeColumn start,TreeColumn end)3912 TrimTheFatAux(
3913 TreeColumn start,
3914 TreeColumn end
3915 )
3916 {
3917 #ifdef TREECTRL_DEBUG
3918 TreeCtrl *tree = start->tree;
3919 #endif
3920 TreeColumn column;
3921 int sumOfSpanWidths = 0;
3922 int sumOfColumnWidths = 0, fat, fatPerCol;
3923 int numColsThatCanShrink = 0, csn;
3924 ColumnReqData *cd;
3925 ColumnSpan *cs;
3926 int visited;
3927
3928 #ifdef TREECTRL_DEBUG
3929 if (start->prev != NULL &&
3930 start->prev->reqData.spanMax->index >= start->index) BreakIntoDebugger();
3931 if (end->next != NULL &&
3932 end->next->reqData.spanMin->index <= end->index) BreakIntoDebugger();
3933 #endif
3934
3935 /* Sum the widths of spans across the range of columns. */
3936 sumOfSpanWidths = 0;
3937 visited = SumSpanWidths(&sumOfSpanWidths, &start->reqData.spans, end);
3938 if (sumOfSpanWidths <= 0)
3939 return;
3940
3941 for (column = start; column != end->next; column = column->next) {
3942 cd = &column->reqData;
3943 if (!cd->vis) continue;
3944 sumOfColumnWidths += (cd->fixed >= 0) ? cd->fixed : column->widthOfItems;
3945 if (cd->fat &&
3946 (cd->fixed < 0) &&
3947 (column->widthOfItems > MAX(cd->maxSingleSpanWidth, cd->min)))
3948 numColsThatCanShrink++;
3949 }
3950
3951 #ifdef TREECTRL_DEBUG
3952 if (tree->debug.enable && tree->debug.span)
3953 dbwin("%d-%d sumOfColumnWidths %d sumOfSpanWidths %d (visited %d)\n", start->index, end->index, sumOfColumnWidths, sumOfSpanWidths, visited);
3954 #endif
3955
3956 if (!numColsThatCanShrink)
3957 return;
3958 fat = sumOfColumnWidths - sumOfSpanWidths;
3959 if (fat <= 0)
3960 return;
3961
3962 while (fat > 0) {
3963 int origFat = fat;
3964 /* Subtract 1 to smooth out the distribution. May result in more looping. */
3965 fatPerCol = MAX(1, fat / numColsThatCanShrink - 1);
3966 for (column = start; column != end->next; column = column->next) {
3967 int minSpanFat = -1;
3968 cd = &column->reqData;
3969 if (!cd->vis || !cd->fat) continue;
3970 if ((cd->fixed >= 0) || (column->widthOfItems <= cd->min) ||
3971 (column->widthOfItems <= cd->maxSingleSpanWidth))
3972 continue;
3973 for (csn = 0; csn < cd->spans.count; csn++) {
3974 cs = cd->spans.spans[csn];
3975 if (cs->widthOfColumns > cs->maxNeededWidth) {
3976 if (minSpanFat == -1)
3977 minSpanFat = cs->widthOfColumns - cs->maxNeededWidth;
3978 else
3979 minSpanFat = MIN(minSpanFat, cs->widthOfColumns - cs->maxNeededWidth);
3980 } else {
3981 minSpanFat = 0; /* no span touching this column can get smaller */
3982 cd->fat = FALSE; /* FIXME: mark all in this span */
3983 --numColsThatCanShrink;
3984 break;
3985 }
3986 }
3987 if (minSpanFat > 0) {
3988 int trim = MIN(minSpanFat, fatPerCol);
3989 if (column->widthOfItems - trim < cd->min)
3990 trim = column->widthOfItems - cd->min;
3991 if (column->widthOfItems - trim < cd->maxSingleSpanWidth)
3992 trim = column->widthOfItems - cd->maxSingleSpanWidth;
3993 #ifdef TREECTRL_DEBUG
3994 if (tree->debug.enable && tree->debug.span)
3995 dbwin("trimmed %d from %d (numColsThatCanShrink=%d fat=%d minSpanFat=%d)\n", trim, column->index, numColsThatCanShrink, fat, minSpanFat);
3996 #endif
3997 column->widthOfItems -= trim;
3998 fat -= trim;
3999 if (fat <= 0) break;
4000 for (csn = 0; csn < cd->spans.count; csn++) {
4001 cs = cd->spans.spans[csn];
4002 cs->widthOfColumns -= trim;
4003 }
4004 if (column->widthOfItems <= MAX(cd->maxSingleSpanWidth, cd->min))
4005 --numColsThatCanShrink;
4006 }
4007 if (numColsThatCanShrink == 0)
4008 break;
4009 }
4010 if (fat == origFat || numColsThatCanShrink == 0)
4011 break;
4012 }
4013 }
4014
4015 static void
TrimTheFat(TreeColumn start,TreeColumn end)4016 TrimTheFat(
4017 TreeColumn start,
4018 TreeColumn end
4019 )
4020 {
4021 TreeColumnPriv priv = start->tree->columnPriv;
4022 TreeColumn column;
4023 ColumnReqData *cd;
4024 ColumnSpan *cs;
4025
4026 if (priv->allSpansAreOne)
4027 return;
4028
4029 #ifdef TREECTRL_DEBUG
4030 if (start->prev != NULL &&
4031 start->prev->reqData.spanMax->index >= start->index) BreakIntoDebugger();
4032 if (end->next != NULL &&
4033 end->next->reqData.spanMin->index <= end->index) BreakIntoDebugger();
4034 #endif
4035
4036 for (cs = priv->spansCur; cs != NULL; cs = cs->nextCur) {
4037 /* Sum the current widths of columns in each span record. */
4038 cs->widthOfColumns = 0;
4039 for (column = cs->start; column != cs->end->next; column = column->next) {
4040 cd = &column->reqData;
4041 if (!cd->vis) continue;
4042 cs->widthOfColumns += (cd->fixed >= 0) ? cd->fixed : column->widthOfItems;
4043 }
4044 /* Mark columns that cannot get smaller. */
4045 if (cs->widthOfColumns <= cs->maxNeededWidth) {
4046 for (column = cs->start; column != cs->end->next; column = column->next) {
4047 cd = &column->reqData;
4048 cd->fat = FALSE;
4049 }
4050 #if 0
4051 } else {
4052 priv->minPositiveFatOfAllSpans = MIN(priv->minPositiveFatOfAllSpans, cs->widthOfColumns - cs->maxNeededWidth);
4053 #endif
4054 }
4055 cs->sumOfSpans = -1;
4056 }
4057
4058 column = start;
4059 while (column != end->next) {
4060 TreeColumn columnMin, columnMax;
4061 /* Operate on the narrowest range of columns whose spans do not
4062 * overlap another such range of columns. */
4063 columnMin = column->reqData.spanMin;
4064 columnMax = column->reqData.spanMax;
4065 while ((columnMax->next != NULL) &&
4066 (columnMax->next->reqData.spanMin->index <= columnMax->index)) {
4067 columnMax = columnMax->next->reqData.spanMax;
4068 }
4069 TrimTheFatAux(columnMin, columnMax);
4070 column = columnMax->next;
4071 }
4072 }
4073
4074 /*
4075 *----------------------------------------------------------------------
4076 *
4077 * TreeItem_RequestWidthInColumns --
4078 *
4079 * Calculates the width needed by styles in a range of columns
4080 * for a single header or item.
4081 *
4082 * Results:
4083 * None.
4084 *
4085 * Side effects:
4086 * None.
4087 *
4088 *----------------------------------------------------------------------
4089 */
4090
4091 void
TreeItem_RequestWidthInColumns(TreeCtrl * tree,TreeItem item,TreeColumn columnMin,TreeColumn columnMax)4092 TreeItem_RequestWidthInColumns(
4093 TreeCtrl *tree, /* Widget info. */
4094 TreeItem item,
4095 TreeColumn columnMin,
4096 TreeColumn columnMax
4097 )
4098 {
4099 #ifdef TREECTRL_DEBUG
4100 TreeColumnPriv priv = tree->columnPriv;
4101 #endif
4102 int doHeaders = TreeItem_GetHeader(tree, item) != NULL;
4103 int columnIndexMin = TreeColumn_Index(columnMin);
4104 int columnIndexMax = TreeColumn_Index(columnMax);
4105 int *spans = TreeItem_GetSpans(tree, item);
4106 TreeItemColumn itemColumn;
4107 TreeColumn treeColumn;
4108 int columnIndex, width, indent;
4109 ColumnSpan *csPrev = NULL;
4110
4111 #ifdef TREECTRL_DEBUG
4112 if (columnMax == tree->columnTail) BreakIntoDebugger();
4113 if (priv->reqInvalid) BreakIntoDebugger();
4114 if (columnMin->prev != NULL &&
4115 columnMin->prev->reqData.spanMax->index >= columnMin->index) BreakIntoDebugger();
4116 if (columnMax->next != NULL &&
4117 columnMax->next->reqData.spanMin->index <= columnMax->index) BreakIntoDebugger();
4118 #endif
4119
4120 treeColumn = columnMin;
4121 itemColumn = TreeItem_FindColumn(tree, item, columnIndexMin);
4122 if (spans == NULL) {
4123 for (columnIndex = columnIndexMin;
4124 columnIndex <= columnIndexMax;
4125 ++columnIndex) {
4126 ColumnReqData *cd = &treeColumn->reqData;
4127 if (cd->vis) {
4128 width = (itemColumn != NULL) ?
4129 TreeItemColumn_NeededWidth(tree, item, itemColumn) : 0;
4130 /* FOR COMPATIBILITY ONLY. Don't request width from -indent
4131 * if there is no item column. */
4132 if (itemColumn != NULL) {
4133 indent = doHeaders ? 0 : TreeItem_Indent(tree, treeColumn, item);
4134 width += indent;
4135 }
4136 csPrev = AddColumnSpan(csPrev, treeColumn, treeColumn, width, doHeaders);
4137 }
4138 treeColumn = TreeColumn_Next(treeColumn);
4139 if (itemColumn != NULL)
4140 itemColumn = TreeItemColumn_GetNext(tree, itemColumn);
4141 }
4142 return;
4143 }
4144
4145 #if defined(TREECTRL_DEBUG)
4146 /* It must be true that a span starts at columnMin. */
4147 if (spans[columnIndexMin] != columnIndexMin) BreakIntoDebugger();
4148 #endif
4149
4150 for (columnIndex = columnIndexMin;
4151 columnIndex <= columnIndexMax;
4152 /*++columnIndex*/) {
4153 ColumnReqData *cd = &treeColumn->reqData;
4154 int columnIndex2 = columnIndex;
4155 TreeColumn treeColumn2 = treeColumn;
4156 TreeColumn lastColumnInSpan = treeColumn;
4157 #if defined(TREECTRL_DEBUG)
4158 if (TreeColumn_Index(treeColumn) != columnIndex) BreakIntoDebugger();
4159 if (itemColumn != NULL && TreeItemColumn_Index(tree, item, itemColumn) != columnIndex) BreakIntoDebugger();
4160 if (spans[columnIndex] != columnIndex) BreakIntoDebugger();
4161 #endif
4162 while ((columnIndex2 <= columnIndexMax) &&
4163 (spans[columnIndex2] == columnIndex)) {
4164 lastColumnInSpan = treeColumn2;
4165 treeColumn2 = TreeColumn_Next(treeColumn2);
4166 columnIndex2++;
4167 }
4168
4169 if (cd->vis) {
4170 width = (itemColumn != NULL) ?
4171 TreeItemColumn_NeededWidth(tree, item, itemColumn) : 0;
4172
4173 /* Indentation is spread amongst the visible columns as well, and
4174 * is only used if the -treecolumn is the first column in the span. */
4175 /* FOR COMPATIBILITY ONLY. Don't request width from -indent
4176 * if there is no item column. */
4177 if (itemColumn != NULL) {
4178 indent = doHeaders ? 0 : TreeItem_Indent(tree, treeColumn, item);
4179 width += indent;
4180 }
4181 csPrev = AddColumnSpan(csPrev, treeColumn, lastColumnInSpan, width, doHeaders);
4182 }
4183
4184 treeColumn = TreeColumn_Next(lastColumnInSpan);
4185 if (treeColumn == NULL)
4186 break;
4187 while (/*(itemColumn != NULL) && */(columnIndex < TreeColumn_Index(treeColumn))) {
4188 if (itemColumn != NULL)
4189 itemColumn = TreeItemColumn_GetNext(tree, itemColumn);
4190 ++columnIndex;
4191 }
4192 }
4193 }
4194
4195 /*
4196 *----------------------------------------------------------------------
4197 *
4198 * DistributeSpanWidthToColumns --
4199 *
4200 * Ensures that each range of columns covered by a span is wide
4201 * enough to contain that span, without violating the -width and
4202 * -maxwidth column options.
4203 *
4204 * Results:
4205 * None.
4206 *
4207 * Side effects:
4208 * None.
4209 *
4210 *----------------------------------------------------------------------
4211 */
4212
4213 static void
DistributeSpanWidthToColumns(TreeColumn start,TreeColumn end)4214 DistributeSpanWidthToColumns(
4215 TreeColumn start,
4216 TreeColumn end
4217 )
4218 {
4219 TreeCtrl *tree = start->tree;
4220 TreeColumnPriv priv = tree->columnPriv;
4221 ColumnSpan *cs;
4222 ColumnReqData *cd;
4223 TreeColumn column;
4224
4225 if (priv->allSpansAreOne) {
4226 for (column = start; column != end->next; column = column->next)
4227 column->widthOfItems = column->reqData.maxSingleSpanWidth;
4228 return;
4229 }
4230
4231 for (cs = priv->spansCur; cs != NULL; cs = cs->nextCur) {
4232 int numVisibleColumns = 0, spaceRemaining = cs->maxNeededWidth;
4233 int numMinWidth = 0, minMinWidth = -1, varWidth = 0;
4234
4235 if (cs->maxNeededWidth <= 0)
4236 continue;
4237
4238 for (column = cs->start;
4239 column != cs->end->next;
4240 column = column->next) {
4241 cd = &column->reqData;
4242 #if defined(TREECTRL_DEBUG)
4243 if (cd->vis != TreeColumn_Visible(column)) BreakIntoDebugger();
4244 if (TreeColumn_MaxWidth(column) >= 0 && cd->min > TreeColumn_MaxWidth(column)) BreakIntoDebugger();
4245 if (TreeColumn_MaxWidth(column) < 0 && cd->min != TreeColumn_MinWidth(column)) BreakIntoDebugger();
4246 if (cd->max != TreeColumn_MaxWidth(column)) BreakIntoDebugger();
4247 if (cd->fixed != TreeColumn_FixedWidth(column)) BreakIntoDebugger();
4248 #endif
4249 cd->req = 0;
4250 if (cd->vis)
4251 numVisibleColumns++;
4252 }
4253
4254 /* Dump space into fixed-width and minwidth */
4255 for (column = cs->start;
4256 (spaceRemaining > 0) &&
4257 (column != cs->end->next);
4258 column = column->next) {
4259 int spaceUsed;
4260 cd = &column->reqData;
4261 if (!cd->vis) continue;
4262 if (cd->fixed >= 0) {
4263 spaceUsed = cd->fixed;
4264 numVisibleColumns--;
4265 } else if (cd->min >= 0) {
4266 spaceUsed = cd->min;
4267 ++numMinWidth;
4268 if (minMinWidth == -1)
4269 minMinWidth = cd->min;
4270 else
4271 minMinWidth = MIN(minMinWidth, cd->min);
4272 } else
4273 continue;
4274 spaceUsed = MIN(spaceUsed, spaceRemaining);
4275 cd->req += spaceUsed;
4276 spaceRemaining -= spaceUsed;
4277 }
4278
4279 /* Distribute width to visible columns in the span. */
4280 while ((spaceRemaining > 0) && (numVisibleColumns > 0)) {
4281 int each;
4282 int origSpaceRemaining = spaceRemaining;
4283
4284 /* This is the amount to give to each column. Some columns
4285 * may get one extra pixel (starting from the leftmost column). */
4286 each = MAX(1, spaceRemaining / numVisibleColumns);
4287 varWidth += each;
4288
4289 /* If all the columns have -minwidth, there will be a lot of
4290 * useless looping until varWidth exceeds the smallest -minwidth. */
4291 if (numMinWidth == numVisibleColumns)
4292 while (varWidth <= minMinWidth)
4293 varWidth += each;
4294
4295 for (column = cs->start;
4296 (spaceRemaining > 0) &&
4297 (column != cs->end->next);
4298 column = column->next) {
4299 int spaceUsed = each;
4300 cd = &column->reqData;
4301 if (!cd->vis) continue;
4302 if (cd->fixed >= 0)
4303 continue;
4304 if ((cd->max >= 0) && (cd->req >= cd->max))
4305 continue;
4306 /* Don't grow minwidth columns until the space of variable-width
4307 * columns exceeds that of minwidth. */
4308 if ((cd->min >= 0) && (cd->req >= varWidth))
4309 continue;
4310 if (cd->min >= 0) {
4311 spaceUsed = MIN(varWidth - cd->req, spaceUsed);
4312 }
4313 if (cd->max >= 0) {
4314 spaceUsed = MIN(cd->max - cd->req, spaceUsed);
4315 if (cd->req + MIN(spaceUsed, spaceRemaining) >= cd->max) {
4316 spaceUsed = cd->max - cd->req;
4317 numVisibleColumns--;
4318 }
4319 }
4320 spaceUsed = MIN(spaceUsed, spaceRemaining);
4321 cd->req += spaceUsed;
4322 #if defined(TREECTRL_DEBUG)
4323 if (cd->fixed >= 0 && cd->req > cd->fixed) BreakIntoDebugger();
4324 if (cd->fixed < 0 && cd->max >= 0 && cd->req > cd->max) BreakIntoDebugger();
4325 #endif
4326 spaceRemaining -= spaceUsed;
4327 }
4328
4329 if (spaceRemaining == origSpaceRemaining)
4330 break;
4331 }
4332 for (column = cs->start;
4333 (column != cs->end->next);
4334 column = column->next) {
4335 cd = &column->reqData;
4336 if (!cd->vis) continue;
4337 column->widthOfItems = MAX(column->widthOfItems, cd->req);
4338 }
4339 }
4340 }
4341
4342 /*
4343 *----------------------------------------------------------------------
4344 *
4345 * TreeColumn_WidthOfItems --
4346 *
4347 * Calculate the maximum needed width of the styles in every
4348 * ReallyVisible() item for a particular column. The width will
4349 * only be recalculated if it is marked out-of-date.
4350 *
4351 * Results:
4352 * Pixel width.
4353 *
4354 * Side effects:
4355 * The size of elements and styles will be updated if they are
4356 * marked out-of-date.
4357 *
4358 *----------------------------------------------------------------------
4359 */
4360
4361 int
TreeColumn_WidthOfItems(TreeColumn column)4362 TreeColumn_WidthOfItems(
4363 TreeColumn column /* Column token. */
4364 )
4365 {
4366 TreeCtrl *tree = column->tree;
4367 TreeColumnPriv priv = tree->columnPriv;
4368 TreeColumn columnMin = NULL, columnMax = NULL;
4369
4370 if (IS_TAIL(column))
4371 return 0;
4372
4373 if (priv->spansInvalid) {
4374 columnMin = tree->columns;
4375 columnMax = tree->columnLast;
4376 if (priv->freeSpans != NULL)
4377 priv->freeSpans->next = priv->spans;
4378 else
4379 priv->freeSpans = priv->spans;
4380 priv->spans = NULL;
4381 priv->spansCur = NULL;
4382 priv->allSpansAreOne = TRUE;
4383 } else if (column->reqData.spanMin->widthOfItems < 0) {
4384 ColumnSpan *cs;
4385
4386 columnMin = column->reqData.spanMin;
4387 columnMax = column->reqData.spanMax;
4388 /* Gather all adjacent out-of-date columns into one group. */
4389 /* Must also get any spans that overlap any spans that overlap this
4390 * column. */
4391 while ((columnMin->prev != NULL) &&
4392 ((columnMin->prev->reqData.spanMax->index >= columnMin->index) ||
4393 (columnMin->prev->reqData.spanMin->widthOfItems < 0))) {
4394 columnMin = columnMin->prev->reqData.spanMin;
4395 }
4396 while ((columnMax->next != NULL) &&
4397 ((columnMax->next->reqData.spanMin->index <= columnMax->index) ||
4398 (columnMax->next->widthOfItems < 0))) {
4399 columnMax = columnMax->next->reqData.spanMax;
4400 }
4401 /* Build the list of span records touching the range of columns. */
4402 priv->spansCur = NULL;
4403 for (cs = priv->spans; cs != NULL; cs = cs->next) {
4404 if (cs->start->index < columnMin->index ||
4405 cs->end->index > columnMax->index) {
4406 continue;
4407 }
4408 cs->maxNeededWidth = 0;
4409 cs->nextCur = priv->spansCur;
4410 priv->spansCur = cs;
4411 }
4412 }
4413 if (columnMin != NULL) {
4414 TreeColumn column2;
4415 for (column2 = columnMin;
4416 column2 != columnMax->next;
4417 column2 = column2->next) {
4418 column2->widthOfItems = 0;
4419 column2->reqData.maxSingleSpanWidth = 0;
4420 column2->reqData.maxSingleHeaderWidth = 0;
4421 column2->reqData.maxSingleItemWidth = 0;
4422 column2->reqData.fat = TRUE;
4423 if (priv->spansInvalid) {
4424 column2->reqData.spanMin = column2->reqData.spanMax = column2;
4425 column2->reqData.spans.count = 0;
4426 }
4427 }
4428 #ifdef TREECTRL_DEBUG
4429 if (tree->debug.enable && tree->debug.span)
4430 dbwin("RequestWidthInColumns %s %d-%d allSpansAreOne=%d\n",
4431 Tk_PathName(tree->tkwin), columnMin->index, columnMax->index,
4432 priv->allSpansAreOne);
4433 #endif
4434 InitColumnReqData(tree);
4435 TreeHeaders_RequestWidthInColumns(tree, columnMin, columnMax);
4436 TreeItems_RequestWidthInColumns(tree, columnMin, columnMax);
4437 priv->spansInvalid = FALSE; /* Clear this *after* the above call. */
4438 DistributeSpanWidthToColumns(columnMin, columnMax);
4439 TrimTheFat(columnMin, columnMax);
4440 }
4441
4442 /* FOR COMPATIBILITY ONLY */
4443 /* See the use of TreeColumn_WidthOfItems() by tkTreeDisplay.c. */
4444 TreeColumns_UpdateCounts(tree);
4445 if (tree->columnCountVis == 1 && tree->columnVis == column)
4446 return column->reqData.maxSingleItemWidth;
4447
4448 return column->widthOfItems;
4449 }
4450
4451 /*
4452 *----------------------------------------------------------------------
4453 *
4454 * TreeColumn_WidthOfHeaders --
4455 *
4456 * Return the requested width of styles displayed in every visible
4457 * header for a single column.
4458 *
4459 * Results:
4460 * Pixel width.
4461 *
4462 * Side effects:
4463 * None.
4464 *
4465 *----------------------------------------------------------------------
4466 */
4467
4468 int
TreeColumn_WidthOfHeaders(TreeColumn column)4469 TreeColumn_WidthOfHeaders(
4470 TreeColumn column /* Column token. */
4471 )
4472 {
4473 TreeCtrl *tree = column->tree;
4474 int width = TreeColumn_WidthOfItems(column);
4475
4476 /* FOR COMPATIBILITY ONLY */
4477 /* See the use of TreeColumn_WidthOfItems() by tkTreeDisplay.c. */
4478 if (tree->columnCountVis == 1 && tree->columnVis == column)
4479 return column->reqData.maxSingleHeaderWidth;
4480
4481 return width;
4482 }
4483
4484 /*
4485 *----------------------------------------------------------------------
4486 *
4487 * TreeColumns_InvalidateSpans --
4488 *
4489 * Marks the spanMin & spanMax fields of every column as
4490 * out-of-date.
4491 * Called when anything affects the spans of items or headers,
4492 * such as:
4493 * a) header and item deletion
4494 * b) header and item visibility changes, including expanding,
4495 * collapsing, or reparenting
4496 * d) [item span] or [header span] changed spans
4497 * e) column creation, deletion, reordering, or visibility
4498 * changes (handled by TreeItem_SpansInvalidate).
4499 *
4500 * Results:
4501 * None.
4502 *
4503 * Side effects:
4504 * None.
4505 *
4506 *----------------------------------------------------------------------
4507 */
4508
4509 void
TreeColumns_InvalidateSpans(TreeCtrl * tree)4510 TreeColumns_InvalidateSpans(
4511 TreeCtrl *tree /* Widget info. */
4512 )
4513 {
4514 tree->columnPriv->spansInvalid = TRUE;
4515 }
4516
4517 /*
4518 *----------------------------------------------------------------------
4519 *
4520 * TreeColumns_InvalidateWidthOfItems --
4521 *
4522 * Marks the width requested by items in zero or more columns
4523 * as out-of-date.
4524 *
4525 * Results:
4526 * None.
4527 *
4528 * Side effects:
4529 * Idle task may be scheduled.
4530 *
4531 *----------------------------------------------------------------------
4532 */
4533
4534 void
TreeColumns_InvalidateWidthOfItems(TreeCtrl * tree,TreeColumn column)4535 TreeColumns_InvalidateWidthOfItems(
4536 TreeCtrl *tree, /* Widget info. */
4537 TreeColumn column /* Column to modify. NULL means
4538 * modify every column. */
4539 )
4540 {
4541 TreeColumnPriv priv = tree->columnPriv;
4542
4543 /* FIXME: This gets called for both items and headers. If invalidating
4544 * header width, there is no need to invalidate widthOfItems unless the
4545 * column is covered by a span > 1 in one or more items. */
4546
4547 if (column == NULL) {
4548 column = tree->columns;
4549 while (column != NULL) {
4550 column->widthOfItems = -1;
4551 column = column->next;
4552 }
4553 } else if (!priv->spansInvalid &&
4554 column->reqData.spanMin != NULL) { /* spanMin/Max can be NULL during creation when spansInvalid hasn't been set TRUE yet */
4555 TreeColumn columnMin = column->reqData.spanMin;
4556 TreeColumn columnMax = column->reqData.spanMax;
4557 columnMin->widthOfItems = -1;
4558
4559 /* Must recalculate the width of items in every span that overlaps
4560 * any of the spans that include this column. */
4561 while ((columnMin->prev != NULL) &&
4562 (columnMin->prev->reqData.spanMax->index >= columnMin->index)) {
4563 columnMin = columnMin->prev->reqData.spanMin;
4564 columnMin->widthOfItems = -1;
4565 }
4566 while ((columnMax->next != NULL) &&
4567 (columnMax->next->reqData.spanMin->index <= columnMax->index)) {
4568 columnMax = columnMax->next->reqData.spanMax;
4569 columnMax->reqData.spanMin->widthOfItems = -1;
4570 }
4571
4572 }
4573 TreeColumns_InvalidateWidth(tree);
4574 }
4575
4576 /*
4577 *----------------------------------------------------------------------
4578 *
4579 * TreeColumns_InvalidateWidth --
4580 *
4581 * Marks the width of columns as out-of-date.
4582 * Schedules a redisplay to check the widths of columns which
4583 * will perform any relayout necessary.
4584 *
4585 * Results:
4586 * None.
4587 *
4588 * Side effects:
4589 * Idle task may be scheduled.
4590 *
4591 *----------------------------------------------------------------------
4592 */
4593
4594 void
TreeColumns_InvalidateWidth(TreeCtrl * tree)4595 TreeColumns_InvalidateWidth(
4596 TreeCtrl *tree /* Widget info. */
4597 )
4598 {
4599 tree->widthOfColumns = -1;
4600 tree->widthOfColumnsLeft = -1;
4601 tree->widthOfColumnsRight = -1;
4602 tree->columnPriv->reqInvalid = TRUE;
4603 Tree_DInfoChanged(tree, DINFO_CHECK_COLUMN_WIDTH);
4604 }
4605
4606 /*
4607 *--------------------------------------------------------------
4608 *
4609 * TreeColumn_Bbox --
4610 *
4611 * Return the bounding box for a column.
4612 * This used to be the function to call to get the bounding
4613 * box of a column header.
4614 *
4615 * Results:
4616 * Return value is -1 if the column is not visible.
4617 *
4618 * Side effects:
4619 * Column layout will be updated if needed.
4620 *
4621 *--------------------------------------------------------------
4622 */
4623
4624 int
TreeColumn_Bbox(TreeColumn column,int * x,int * y,int * w,int * h)4625 TreeColumn_Bbox(
4626 TreeColumn column, /* Column token. */
4627 int *x, int *y, /* Out: window coordinates. */
4628 int *w, int *h /* Out: width and height. */
4629 )
4630 {
4631 TreeCtrl *tree = column->tree;
4632 int left = 0;
4633
4634 if (!tree->showHeader || !TreeColumn_Visible(column))
4635 return -1;
4636
4637 *y = Tree_HeaderTop(tree);
4638 *h = Tree_HeaderHeight(tree);
4639
4640 if (column == tree->columnTail) {
4641 *x = Tree_WidthOfColumns(tree) - tree->xOrigin; /* Canvas -> Window */
4642 *w = 1; /* xxx */
4643 return 0;
4644 }
4645
4646 /* Get width (and update column layout) */
4647 *w = TreeColumn_UseWidth(column);
4648
4649 switch (TreeColumn_Lock(column)) {
4650 case COLUMN_LOCK_LEFT:
4651 left = Tree_BorderLeft(tree);
4652 break;
4653 case COLUMN_LOCK_NONE:
4654 left = 0 - Tree_GetOriginX(tree); /* Canvas -> Window */
4655 break;
4656 case COLUMN_LOCK_RIGHT:
4657 left = Tree_ContentRight(tree);
4658 break;
4659 }
4660
4661 *x = left + TreeColumn_Offset(column);
4662 return 0;
4663 }
4664
4665 /*
4666 *----------------------------------------------------------------------
4667 *
4668 * LayoutColumns --
4669 *
4670 * Calculates the display width and horizontal offset of a range
4671 * of columns.
4672 *
4673 * Results:
4674 * The .useWidth and .offset fields of every column in the range
4675 * are updated.
4676 * The result is the sum of the widths of all visible columns in the
4677 * range.
4678 *
4679 * Side effects:
4680 * The size of elements and styles may be updated if they are
4681 * marked out-of-date.
4682 *
4683 *----------------------------------------------------------------------
4684 */
4685
4686 static int
LayoutColumns(TreeCtrl * tree,TreeColumn first)4687 LayoutColumns(
4688 TreeCtrl *tree, /* Widget info. */
4689 TreeColumn first /* First column to update. All columns
4690 * with the same -lock value are updated. */
4691 )
4692 {
4693 TreeColumn column;
4694 int width, visWidth, totalWidth = 0;
4695 int numExpand = 0, numSqueeze = 0;
4696 #ifdef UNIFORM_GROUP
4697 Tcl_HashEntry *hPtr;
4698 Tcl_HashSearch search;
4699 UniformGroup *uniform;
4700 int uniformCount = 0;
4701 #endif
4702
4703 #ifdef TREECTRL_DEBUG
4704 if (tree->debugCheck.inLayoutColumns)
4705 panic("recursive call to LayoutColumns");
4706 #endif
4707
4708 if (first == NULL)
4709 return 0;
4710
4711 tree = first->tree;
4712
4713 #ifdef TREECTRL_DEBUG
4714 tree->debugCheck.inLayoutColumns = TRUE;
4715 #endif
4716
4717 #ifdef UNIFORM_GROUP
4718 /* Initialize the .minSize field of every uniform group. */
4719 hPtr = Tcl_FirstHashEntry(&tree->uniformGroupHash, &search);
4720 while (hPtr != NULL) {
4721 uniform = (UniformGroup *) Tcl_GetHashValue(hPtr);
4722 uniform->minSize = 0;
4723 hPtr = Tcl_NextHashEntry(&search);
4724 }
4725 #endif
4726
4727 /*
4728 * Determine the initial display width of each column. This will be:
4729 * a) the column's -width option (a fixed width), or
4730 * b) the maximum of:
4731 * 1) the width requested by the column's header
4732 * 2) the width requested by each item style in that column
4733 * For b) the width is clipped to -minwidth and -maxwidth.
4734 */
4735 column = first;
4736 while (column != NULL && column->lock == first->lock) {
4737 if (column->visible) {
4738 if (column->widthObj != NULL)
4739 width = column->width;
4740 else {
4741 width = TreeColumn_WidthOfItems(column);
4742 width = MAX(width, TreeColumn_WidthOfHeaders(column));
4743 width = MAX(width, TreeColumn_MinWidth(column));
4744 if (TreeColumn_MaxWidth(column) != -1)
4745 width = MIN(width, TreeColumn_MaxWidth(column));
4746 #ifdef UNIFORM_GROUP
4747 /* Track the maximum requested width of every column in this
4748 * column's uniform group considering -weight. */
4749 if (column->uniform != NULL) {
4750 int weight = MAX(column->weight, 1);
4751 int minSize = (width + weight - 1) / weight;
4752 if (minSize > column->uniform->minSize)
4753 column->uniform->minSize = minSize;
4754 uniformCount++;
4755 }
4756 if (column->expand)
4757 numExpand += MAX(column->weight, 0);
4758 if (column->squeeze)
4759 numSqueeze += MAX(column->weight, 0);
4760 #else
4761 if (column->expand)
4762 numExpand++;
4763 if (column->squeeze)
4764 numSqueeze++;
4765 #endif
4766 }
4767 } else
4768 width = 0;
4769 column->useWidth = width;
4770 totalWidth += width;
4771 column = column->next;
4772 }
4773
4774 #ifdef UNIFORM_GROUP
4775 /* Apply the -uniform and -weight options. */
4776 if (uniformCount > 0) {
4777 column = first;
4778 while (column != NULL && column->lock == first->lock) {
4779 if (column->visible &&
4780 column->widthObj == NULL &&
4781 column->uniform != NULL) {
4782 int weight = MAX(column->weight, 1);
4783 width = column->uniform->minSize * weight;
4784 if (column->maxWidthObj != NULL)
4785 width = MIN(width, column->maxWidth);
4786 totalWidth -= column->useWidth;
4787 column->useWidth = width;
4788 totalWidth += width;
4789 }
4790 column = column->next;
4791 }
4792 }
4793 #endif /* UNIFORM_GROUP */
4794
4795 /* Locked columns don't squeeze or expand. */
4796 if (first->lock != COLUMN_LOCK_NONE)
4797 goto doOffsets;
4798
4799 visWidth = Tree_ContentWidth(tree);
4800
4801 /* Hack */
4802 visWidth -= tree->canvasPadX[PAD_TOP_LEFT] + tree->canvasPadX[PAD_BOTTOM_RIGHT];
4803
4804 if (visWidth <= 0)
4805 goto doOffsets;
4806
4807 /* Squeeze columns */
4808 if ((visWidth < totalWidth) && (numSqueeze > 0)) {
4809 int spaceRemaining = totalWidth - visWidth;
4810 while ((spaceRemaining > 0) && (numSqueeze > 0)) {
4811 int each = (spaceRemaining >= numSqueeze) ?
4812 spaceRemaining / numSqueeze : 1;
4813 numSqueeze = 0;
4814 column = first;
4815 while (column != NULL && column->lock == first->lock) {
4816 if (column->visible &&
4817 column->squeeze &&
4818 (column->widthObj == NULL)) {
4819 int min = MAX(0, TreeColumn_MinWidth(column));
4820 if (column->useWidth > min) {
4821 int sub = MIN(each, column->useWidth - min);
4822 column->useWidth -= sub;
4823 spaceRemaining -= sub;
4824 if (!spaceRemaining) break;
4825 if (column->useWidth > min)
4826 numSqueeze++;
4827 }
4828 }
4829 column = column->next;
4830 }
4831 }
4832 }
4833
4834 /* Expand columns */
4835 if ((visWidth > totalWidth) && (numExpand > 0)) {
4836 int spaceRemaining = visWidth - totalWidth;
4837 while ((spaceRemaining > 0) && (numExpand > 0)) {
4838 int each = (spaceRemaining >= numExpand) ?
4839 spaceRemaining / numExpand : 1;
4840 numExpand = 0;
4841 column = first;
4842 while (column != NULL && column->lock == first->lock) {
4843 #ifdef UNIFORM_GROUP
4844 int weight = MAX(column->weight, 0);
4845 if (column->visible &&
4846 column->expand && weight &&
4847 (column->widthObj == NULL)) {
4848 int max = TreeColumn_MaxWidth(column);
4849 if ((max == -1) || (column->useWidth < max)) {
4850 int eachW = MIN(each * weight, spaceRemaining);
4851 int add = (max == -1) ? eachW : MIN(eachW, max - column->useWidth);
4852 column->useWidth += add;
4853 spaceRemaining -= add;
4854 if (!spaceRemaining) break;
4855 if ((max == -1) || (column->useWidth < max))
4856 numExpand += weight;
4857 #else
4858 if (column->visible &&
4859 column->expand &&
4860 (column->widthObj == NULL)) {
4861 int max = TreeColumn_MaxWidth(column);
4862 if ((max == -1) || (column->useWidth < max)) {
4863 int add = (max == -1) ? each : MIN(each, max - column->useWidth);
4864 column->useWidth += add;
4865 spaceRemaining -= add;
4866 if (!spaceRemaining) break;
4867 if ((max == -1) || (column->useWidth < max))
4868 numExpand++;
4869 #endif
4870 }
4871 }
4872 column = column->next;
4873 }
4874 }
4875 }
4876
4877 doOffsets:
4878
4879 /* Calculate the horizontal offset of each column in the range.
4880 * The total width is recalculated as well (needed anyway if any
4881 * columns were expanded or squeezed). */
4882 totalWidth = 0;
4883 column = first;
4884 while (column != NULL && column->lock == first->lock) {
4885 column->offset = totalWidth;
4886
4887 /* Hack */
4888 if (column->lock == COLUMN_LOCK_NONE)
4889 column->offset += tree->canvasPadX[PAD_TOP_LEFT];
4890
4891 totalWidth += column->useWidth;
4892 column = column->next;
4893 }
4894
4895 #ifdef TREECTRL_DEBUG
4896 tree->debugCheck.inLayoutColumns = FALSE;
4897 #endif
4898
4899 return totalWidth;
4900 }
4901
4902 /*
4903 *----------------------------------------------------------------------
4904 *
4905 * Tree_WidthOfColumns --
4906 *
4907 * Return the total display width of all non-locked columns (except
4908 * the tail).
4909 * The width is only recalculated if it is marked out-of-date.
4910 * Other fields of the TreeCtrl are updated to reflect the current
4911 * arrangement of columns.
4912 *
4913 * Results:
4914 * Pixel width.
4915 *
4916 * Side effects:
4917 * The size of elements and styles may be updated if they are
4918 * marked out-of-date.
4919 *
4920 *----------------------------------------------------------------------
4921 */
4922
4923 int
4924 Tree_WidthOfColumns(
4925 TreeCtrl *tree /* Widget info. */
4926 )
4927 {
4928 /* Tree_WidthOfColumns used to update columnCountVis, but that is now
4929 * done in a separate function TreeColumns_UpdateCounts, because
4930 * TreeItem_ReallyVisible needs up-to-date counts (for header items)
4931 * and LayoutColumns may call TreeItem_ReallyVisible. */
4932 TreeColumns_UpdateCounts(tree);
4933
4934 /* This gets called when the layout of all columns needs to be current.
4935 * So update the layout of the left- and right-locked columns too. */
4936 (void) Tree_WidthOfLeftColumns(tree);
4937 (void) Tree_WidthOfRightColumns(tree);
4938
4939 if (tree->widthOfColumns >= 0)
4940 return tree->widthOfColumns;
4941
4942 tree->widthOfColumns = LayoutColumns(tree, tree->columnLockNone);
4943
4944 if (tree->columnTree != NULL && TreeColumn_Visible(tree->columnTree)) {
4945 tree->columnTreeLeft = tree->columnTree->offset;
4946 tree->columnTreeVis = TRUE;
4947 } else {
4948 tree->columnTreeLeft = 0;
4949 tree->columnTreeVis = FALSE;
4950 }
4951
4952 /* I can't calculate the width of the tail column here because it
4953 * depends on Tree_FakeCanvasWidth which calls this function. */
4954 tree->columnTail->offset = tree->canvasPadX[PAD_TOP_LEFT] +
4955 tree->widthOfColumns;
4956 tree->columnTail->useWidth = 0; /* hack */
4957
4958 return tree->widthOfColumns;
4959 }
4960
4961 /*
4962 *----------------------------------------------------------------------
4963 *
4964 * Tree_WidthOfLeftColumns --
4965 *
4966 * Return the total display width of all left-locked columns.
4967 * The width is only recalculated if it is marked out-of-date.
4968 * Other fields of the TreeCtrl are updated to reflect the current
4969 * arrangement of columns.
4970 *
4971 * Results:
4972 * Pixel width.
4973 *
4974 * Side effects:
4975 * The size of elements and styles may be updated if they are
4976 * marked out-of-date.
4977 *
4978 *----------------------------------------------------------------------
4979 */
4980
4981 int
4982 Tree_WidthOfLeftColumns(
4983 TreeCtrl *tree /* Widget info. */
4984 )
4985 {
4986 if (tree->widthOfColumnsLeft >= 0)
4987 return tree->widthOfColumnsLeft;
4988
4989 if (!Tree_ShouldDisplayLockedColumns(tree)) {
4990 TreeColumn column = tree->columnLockLeft;
4991 while (column != NULL && column->lock == COLUMN_LOCK_LEFT) {
4992 column->useWidth = 0;
4993 column = column->next;
4994 }
4995 tree->columnCountVisLeft = 0;
4996 tree->widthOfColumnsLeft = 0;
4997 return 0;
4998 }
4999
5000 tree->widthOfColumnsLeft = LayoutColumns(tree, tree->columnLockLeft);
5001
5002 return tree->widthOfColumnsLeft;
5003 }
5004
5005 /*
5006 *----------------------------------------------------------------------
5007 *
5008 * Tree_WidthOfRightColumns --
5009 *
5010 * Return the total display width of all right-locked columns.
5011 * The width is only recalculated if it is marked out-of-date.
5012 * Other fields of the TreeCtrl are updated to reflect the current
5013 * arrangement of columns.
5014 *
5015 * Results:
5016 * Pixel width.
5017 *
5018 * Side effects:
5019 * The size of elements and styles may be updated if they are
5020 * marked out-of-date.
5021 *
5022 *----------------------------------------------------------------------
5023 */
5024
5025 int
5026 Tree_WidthOfRightColumns(
5027 TreeCtrl *tree /* Widget info. */
5028 )
5029 {
5030 if (tree->widthOfColumnsRight >= 0)
5031 return tree->widthOfColumnsRight;
5032
5033 if (!Tree_ShouldDisplayLockedColumns(tree)) {
5034 TreeColumn column = tree->columnLockRight;
5035 while (column != NULL && column->lock == COLUMN_LOCK_RIGHT) {
5036 column->useWidth = 0;
5037 column = column->next;
5038 }
5039 tree->columnCountVisRight = 0;
5040 tree->widthOfColumnsRight = 0;
5041 return 0;
5042 }
5043
5044 tree->widthOfColumnsRight = LayoutColumns(tree, tree->columnLockRight);
5045
5046 return tree->widthOfColumnsRight;
5047 }
5048
5049 /*
5050 *----------------------------------------------------------------------
5051 *
5052 * UpdateColumnCounts --
5053 *
5054 * Calculates the number of visible columns with the same -lock
5055 * value, and optionally returns the first visible column in the
5056 * group.
5057 *
5058 * Results:
5059 * None.
5060 *
5061 * Side effects:
5062 * None.
5063 *
5064 *----------------------------------------------------------------------
5065 */
5066
5067 static void
5068 UpdateColumnCounts(
5069 TreeCtrl *tree, /* Widget info. */
5070 TreeColumn first, /* First column to check. All columns
5071 * with the same -lock value are checked. */
5072 TreeColumn *visPtr, /* Out: first visible column. */
5073 int *countVisPtr /* Out: number of visible columns. */
5074 )
5075 {
5076 TreeColumn column;
5077
5078 if (visPtr != NULL)
5079 (*visPtr) = NULL;
5080 (*countVisPtr) = 0;
5081
5082 for (column = first;
5083 column != NULL && column->lock == first->lock;
5084 column = column->next) {
5085 if (column->visible) {
5086 if (visPtr != NULL && (*visPtr) == NULL)
5087 (*visPtr) = column;
5088 (*countVisPtr)++;
5089 }
5090 }
5091 }
5092
5093 /*
5094 *----------------------------------------------------------------------
5095 *
5096 * TreeColumns_UpdateCounts --
5097 *
5098 * Recalculates the number of visible columns and the first
5099 * visible non-locked column if needed.
5100 *
5101 * Results:
5102 * None.
5103 *
5104 * Side effects:
5105 * None.
5106 *
5107 *----------------------------------------------------------------------
5108 */
5109
5110 void
5111 TreeColumns_UpdateCounts(
5112 TreeCtrl *tree /* Widget info. */
5113 )
5114 {
5115 int displayLockedColumns = Tree_ShouldDisplayLockedColumns(tree);
5116
5117 if (tree->displayLockedColumns != displayLockedColumns) {
5118 tree->columnCountVis = -1;
5119 tree->displayLockedColumns = displayLockedColumns;
5120 }
5121
5122 if (tree->columnCountVis >= 0)
5123 return;
5124
5125 UpdateColumnCounts(tree, tree->columnLockNone,
5126 &tree->columnVis, &tree->columnCountVis);
5127
5128 if (displayLockedColumns) {
5129 UpdateColumnCounts(tree, tree->columnLockLeft,
5130 NULL, &tree->columnCountVisLeft);
5131 UpdateColumnCounts(tree, tree->columnLockRight,
5132 NULL, &tree->columnCountVisRight);
5133 } else {
5134 tree->columnCountVisLeft = 0;
5135 tree->columnCountVisRight = 0;
5136 }
5137 }
5138
5139 /*
5140 *----------------------------------------------------------------------
5141 *
5142 * TreeColumns_InvalidateCounts --
5143 *
5144 * Marks the number of visible columns as out-of-date.
5145 * The number of visible columns changes when:
5146 * 1) creating a column
5147 * 2) deleting a column
5148 * 3) moving a column (affects tree->columnVis anyway)
5149 * 4) column option -visible changes
5150 * 5) column option -lock changes
5151 * 6) Tree_ShouldDisplayLockedColumns() changes
5152 *
5153 * Results:
5154 * None.
5155 *
5156 * Side effects:
5157 * None.
5158 *
5159 *----------------------------------------------------------------------
5160 */
5161
5162 void
5163 TreeColumns_InvalidateCounts(
5164 TreeCtrl *tree /* Widget info. */
5165 )
5166 {
5167 tree->columnCountVis = -1;
5168 tree->columnCountVisLeft = -1;
5169 tree->columnCountVisRight = -1;
5170 }
5171
5172 /*
5173 *----------------------------------------------------------------------
5174 *
5175 * TreeColumn_InitWidget --
5176 *
5177 * Perform column-related initialization when a new TreeCtrl is
5178 * created.
5179 *
5180 * Results:
5181 * A standard Tcl result.
5182 *
5183 * Side effects:
5184 * Memory is allocated.
5185 *
5186 *----------------------------------------------------------------------
5187 */
5188
5189 void
5190 TreeColumn_InitWidget(
5191 TreeCtrl *tree /* Widget info. */
5192 )
5193 {
5194 TreeColumn column;
5195
5196 column = Column_Alloc(tree);
5197 column->id = -1;
5198 column->reqData.spanMin = column->reqData.spanMax = column;
5199 tree->columnTail = column;
5200 tree->nextColumnId = 0;
5201 tree->columnCount = 0;
5202 Column_Config(column, 0, NULL, TRUE);
5203
5204 tree->columnDrag.optionTable = Tk_CreateOptionTable(tree->interp,
5205 dragSpecs);
5206 (void) Tk_InitOptions(tree->interp, (char *) tree,
5207 tree->columnDrag.optionTable, tree->tkwin);
5208
5209 #ifdef UNIFORM_GROUP
5210 Tcl_InitHashTable(&tree->uniformGroupHash, TCL_STRING_KEYS);
5211 #endif
5212
5213 tree->columnPriv = (TreeColumnPriv) ckalloc(sizeof(struct TreeColumnPriv_));
5214 memset((char *) tree->columnPriv, 0, sizeof(struct TreeColumnPriv_));
5215 }
5216
5217 /*
5218 *----------------------------------------------------------------------
5219 *
5220 * TreeColumn_FreeWidget --
5221 *
5222 * Free column-related resources for a deleted TreeCtrl.
5223 *
5224 * Results:
5225 * None.
5226 *
5227 * Side effects:
5228 * Memory is deallocated.
5229 *
5230 *----------------------------------------------------------------------
5231 */
5232
5233 void
5234 TreeColumn_FreeWidget(
5235 TreeCtrl *tree /* Widget info. */
5236 )
5237 {
5238 TreeColumn column = tree->columns;
5239 struct TreeColumnPriv_ *priv = tree->columnPriv;
5240
5241 while (column != NULL) {
5242 column = Column_Free(column);
5243 }
5244
5245 Column_Free(tree->columnTail);
5246 tree->columnCount = 0;
5247
5248 #ifdef UNIFORM_GROUP
5249 Tcl_DeleteHashTable(&tree->uniformGroupHash);
5250 #endif
5251
5252 while (priv->spans != NULL) {
5253 ColumnSpan *cs = priv->spans;
5254 priv->spans = cs->next;
5255 if (cs->spansToRight.spans != NULL)
5256 ckfree((char *) cs->spansToRight.spans);
5257 ckfree((char *) cs);
5258 }
5259 while (priv->freeSpans != NULL) {
5260 ColumnSpan *cs = priv->freeSpans;
5261 priv->freeSpans = cs->next;
5262 if (cs->spansToRight.spans != NULL)
5263 ckfree((char *) cs->spansToRight.spans);
5264 ckfree((char *) cs);
5265 }
5266
5267 ckfree((char *) priv);
5268 }
5269
5270 /*
5271 *----------------------------------------------------------------------
5272 *
5273 * TreeColumn_InitInterp --
5274 *
5275 * Performs column-related initialization when the TkTreeCtrl
5276 * package is loaded into an interpreter.
5277 *
5278 * Results:
5279 * TCL_OK.
5280 *
5281 * Side effects:
5282 * Messes with the columnSpecs[] option array.
5283 *
5284 *----------------------------------------------------------------------
5285 */
5286
5287 int
5288 TreeColumn_InitInterp(
5289 Tcl_Interp *interp /* Current interpreter. */
5290 )
5291 {
5292 StringTableCO_Init(columnSpecs, "-itemjustify", justifyStrings);
5293 TreeStyleCO_Init(columnSpecs, "-itemstyle", STATE_DOMAIN_ITEM);
5294
5295 return TCL_OK;
5296 }
5297