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