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