1 /*
2  * tkTreeItem.c --
3  *
4  *	This module implements items for treectrl widgets.
5  *
6  * Copyright (c) 2002-2011 Tim Baker
7  */
8 
9 #include "tkTreeCtrl.h"
10 
11 typedef struct TreeItem_ TreeItem_;
12 typedef struct TreeItemColumn_ TreeItemColumn_;
13 
14 /*
15  * A data structure of the following type is kept for a single column in a
16  * single item.
17  */
18 struct TreeItemColumn_ {
19     int cstate;		/* STATE_xxx flags manipulated with the
20 			 * [item state forcolumn] command */
21     int span;		/* Number of tree-columns this column covers */
22     TreeStyle style;	/* Instance style. */
23     TreeHeaderColumn headerColumn; /* The header-column if the parent item
24 			 * is actually a header, otherwise NULL. */
25     TreeItemColumn next;/* Column to the right of this one */
26 };
27 
28 /*
29  * A data structure of the following type is kept for each item.
30  */
31 struct TreeItem_ {
32     int id;		/* unique id */
33     int depth;		/* tree depth (-1 for the unique root item) */
34     int fixedHeight;	/* -height: desired height of this item (0 for
35 			 * no-such-value) */
36     int numChildren;
37     int index;		/* "row" in flattened tree */
38     int indexVis;	/* visible "row" in flattened tree, -1 if hidden */
39     int state;		/* STATE_xxx flags */
40     TreeItem parent;
41     TreeItem firstChild;
42     TreeItem lastChild;
43     TreeItem prevSibling;
44     TreeItem nextSibling;
45     TreeItemDInfo dInfo; /* display info, or NULL */
46     TreeItemRInfo rInfo; /* range info, or NULL */
47     TreeItemColumn columns;
48     int *spans;		/* 1 per tree-column. spans[N] is the column index of
49 			 * the item-column displayed in column N. If this
50 			 * item's columns all have a span of 1, this field
51 			 * is NULL (unless it was previously allocated
52 			 * because some spans were > 1). */
53     int spanAlloc;	/* Size of spans[]. */
54 #define ITEM_FLAG_DELETED	0x0001 /* Item is being deleted */
55 #define ITEM_FLAG_SPANS_SIMPLE	0x0002 /* All spans are 1 */
56 #define ITEM_FLAG_SPANS_VALID	0x0004 /* Some spans are > 1, but we don't
57 					* need to redo them. Also indicates
58 					* we have an entry in
59 					* TreeCtrl.itemSpansHash. */
60 #define ITEM_FLAG_BUTTON	0x0008 /* -button true */
61 #define ITEM_FLAG_BUTTON_AUTO	0x0010 /* -button auto */
62 #define ITEM_FLAG_VISIBLE	0x0020 /* -visible */
63 #define ITEM_FLAG_WRAP		0x0040 /* -wrap */
64 
65 #define ITEM_FLAG_BUTTONSTATE_ACTIVE	0x0080 /* buttonstate "active" */
66 #define ITEM_FLAG_BUTTONSTATE_PRESSED	0x0100 /* buttonstate "pressed" */
67     int flags;
68     TagInfo *tagInfo;	/* Tags. May be NULL. */
69 
70     TreeHeader header;	/* The header or NULL */
71 };
72 
73 #define ITEM_FLAGS_BUTTONSTATE (ITEM_FLAG_BUTTONSTATE_ACTIVE | \
74     ITEM_FLAG_BUTTONSTATE_PRESSED)
75 
76 static CONST char *ItemUid = "Item", *ItemColumnUid = "ItemColumn";
77 
78 /*
79  * Macro to test whether an item is the unique root item
80  */
81 #define IS_ROOT(i) ((i)->depth == -1)
82 
83 #define IS_ALL(i) ((i) == ITEM_ALL)
84 
85 #define IS_DELETED(i) (((i)->flags & ITEM_FLAG_DELETED) != 0)
86 #define IS_VISIBLE(i) (((i)->flags & ITEM_FLAG_VISIBLE) != 0)
87 #define IS_WRAP(i) (((i)->flags & ITEM_FLAG_WRAP) != 0)
88 
89 /*
90  * Flags returned by Tk_SetOptions() (see itemOptionSpecs below).
91  */
92 #define ITEM_CONF_BUTTON		0x0001
93 #define ITEM_CONF_SIZE			0x0002
94 #define ITEM_CONF_VISIBLE		0x0004
95 #define ITEM_CONF_WRAP			0x0008
96 
97 /*
98  * Information used for Item objv parsing.
99  */
100 static Tk_OptionSpec itemOptionSpecs[] = {
101     {TK_OPTION_CUSTOM, "-button", (char *) NULL, (char *) NULL,
102      "0", -1, Tk_Offset(TreeItem_, flags),
103      0, (ClientData) NULL, ITEM_CONF_BUTTON},
104     {TK_OPTION_PIXELS, "-height", (char *) NULL, (char *) NULL,
105      (char *) NULL, -1, Tk_Offset(TreeItem_, fixedHeight),
106      TK_OPTION_NULL_OK, (ClientData) NULL, ITEM_CONF_SIZE},
107     {TK_OPTION_CUSTOM, "-tags", (char *) NULL, (char *) NULL,
108      (char *) NULL, -1, Tk_Offset(TreeItem_, tagInfo),
109      TK_OPTION_NULL_OK, (ClientData) &TreeCtrlCO_tagInfo, 0},
110     {TK_OPTION_CUSTOM, "-visible", (char *) NULL, (char *) NULL,
111      "1", -1, Tk_Offset(TreeItem_, flags),
112      0, (ClientData) NULL, ITEM_CONF_VISIBLE},
113     {TK_OPTION_CUSTOM, "-wrap", (char *) NULL, (char *) NULL,
114      "0", -1, Tk_Offset(TreeItem_, flags),
115      0, (ClientData) NULL, ITEM_CONF_WRAP},
116     {TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL,
117      (char *) NULL, 0, -1, 0, 0, 0}
118 };
119 
120 /*
121  *----------------------------------------------------------------------
122  *
123  * Column_Alloc --
124  *
125  *	Allocate and initialize a new Column record.
126  *
127  * Results:
128  *	Pointer to allocated Column.
129  *
130  * Side effects:
131  *	Memory is allocated.
132  *
133  *----------------------------------------------------------------------
134  */
135 
136 static TreeItemColumn
Column_Alloc(TreeCtrl * tree,TreeItem item)137 Column_Alloc(
138     TreeCtrl *tree,		/* Widget info. */
139     TreeItem item
140     )
141 {
142 #ifdef ALLOC_HAX
143     TreeItemColumn column = (TreeItemColumn) TreeAlloc_Alloc(tree->allocData, ItemColumnUid,
144 	    sizeof(TreeItemColumn_));
145 #else
146     TreeItemColumn column = (TreeItemColumn) ckalloc(sizeof(TreeItemColumn_));
147 #endif
148     memset(column, '\0', sizeof(TreeItemColumn_));
149     column->span = 1;
150 
151     if (item->header != NULL) {
152 	column->headerColumn = TreeHeaderColumn_CreateWithItemColumn(
153 	    item->header, column);
154 #if TREECTRL_DEBUG
155 	if (column->headerColumn == NULL)
156 	    panic("TreeHeaderColumn_CreateWithItemColumn failed");
157 #endif
158 	column->cstate = STATE_HEADER_NORMAL;
159     }
160 
161     return column;
162 }
163 
164 /*
165  *----------------------------------------------------------------------
166  *
167  * TreeItemColumn_InvalidateSize --
168  *
169  *	Marks the needed height and width of the column as out-of-date.
170  *
171  * Results:
172  *	None.
173  *
174  * Side effects:
175  *	None.
176  *
177  *----------------------------------------------------------------------
178  */
179 
180 void
TreeItemColumn_InvalidateSize(TreeCtrl * tree,TreeItemColumn column_)181 TreeItemColumn_InvalidateSize(
182     TreeCtrl *tree,		/* Widget info. */
183     TreeItemColumn column_	/* Column token. */
184     )
185 {
186 }
187 
188 /*
189  *----------------------------------------------------------------------
190  *
191  * TreeItemColumn_NeededWidth --
192  *
193  *	Returns the requested width of a Column.
194  *
195  * Results:
196  *	If the Column has a style, the requested width of the style
197  *	is returned (a positive pixel value). Otherwise 0 is returned.
198  *
199  * Side effects:
200  *	None.
201  *
202  *----------------------------------------------------------------------
203  */
204 
205 int
TreeItemColumn_NeededWidth(TreeCtrl * tree,TreeItem item,TreeItemColumn column)206 TreeItemColumn_NeededWidth(
207     TreeCtrl *tree,		/* Widget info. */
208     TreeItem item,		/* Item token. */
209     TreeItemColumn column	/* Column token. */
210     )
211 {
212     if (column->style != NULL)
213 	return TreeStyle_NeededWidth(tree, column->style,
214 		item->state | column->cstate);
215     return 0;
216 }
217 
218 /*
219  *----------------------------------------------------------------------
220  *
221  * TreeItems_RequestWidthInColumns --
222  *
223  *	Calculates the width needed by styles in a range of columns
224  *	for every visible item.
225  *
226  * Results:
227  *	None.
228  *
229  * Side effects:
230  *	None.
231  *
232  *----------------------------------------------------------------------
233  */
234 
235 void
TreeItems_RequestWidthInColumns(TreeCtrl * tree,TreeColumn columnMin,TreeColumn columnMax)236 TreeItems_RequestWidthInColumns(
237     TreeCtrl *tree,		/* Widget info. */
238     TreeColumn columnMin,
239     TreeColumn columnMax
240     )
241 {
242     TreeItem item = tree->root;
243 
244     if (!TreeItem_ReallyVisible(tree, item))
245 	item = TreeItem_NextVisible(tree, item);
246     while (item != NULL) {
247 	TreeItem_RequestWidthInColumns(tree, item, columnMin, columnMax);
248 	item = TreeItem_NextVisible(tree, item);
249     }
250 }
251 
252 /*
253  *----------------------------------------------------------------------
254  *
255  * TreeItemColumn_GetStyle --
256  *
257  *	Returns the style assigned to a Column.
258  *
259  * Results:
260  *	Returns the style, or NULL.
261  *
262  * Side effects:
263  *	None.
264  *
265  *----------------------------------------------------------------------
266  */
267 
268 TreeStyle
TreeItemColumn_GetStyle(TreeCtrl * tree,TreeItemColumn column)269 TreeItemColumn_GetStyle(
270     TreeCtrl *tree,		/* Widget info. */
271     TreeItemColumn column	/* Column token. */
272     )
273 {
274     return column->style;
275 }
276 
277 /*
278  *----------------------------------------------------------------------
279  *
280  * TreeItemColumn_Index --
281  *
282  *	Return the 0-based index of a Column in an Item's linked list of
283  *	Columns.
284  *
285  * Results:
286  *	Integer index of the Column.
287  *
288  * Side effects:
289  *	Tcl_Panic() if the Column isn't found.
290  *
291  *----------------------------------------------------------------------
292  */
293 
294 int
TreeItemColumn_Index(TreeCtrl * tree,TreeItem item,TreeItemColumn column)295 TreeItemColumn_Index(
296     TreeCtrl *tree,		/* Widget info. */
297     TreeItem item,		/* Item token. */
298     TreeItemColumn column	/* Column token. */
299     )
300 {
301     TreeItemColumn walk;
302     int i = 0;
303 
304     walk = item->columns;
305     while ((walk != NULL) && (walk != column)) {
306 	i++;
307 	walk = walk->next;
308     }
309     if (walk == NULL)
310 	panic("TreeItemColumn_Index: couldn't find the column\n");
311     return i;
312 }
313 
314 /*
315  *----------------------------------------------------------------------
316  *
317  * TreeItemColumn_ForgetStyle --
318  *
319  *	Free the style assigned to a Column.
320  *
321  * Results:
322  *	Column has no style assigned anymore.
323  *
324  * Side effects:
325  *	Memory is freed.
326  *
327  *----------------------------------------------------------------------
328  */
329 
330 void
TreeItemColumn_ForgetStyle(TreeCtrl * tree,TreeItemColumn column)331 TreeItemColumn_ForgetStyle(
332     TreeCtrl *tree,		/* Widget info. */
333     TreeItemColumn column	/* Column token. */
334     )
335 {
336     if (column->style != NULL) {
337 	TreeStyle_FreeResources(tree, column->style);
338 	column->style = NULL;
339     }
340 }
341 
342 /*
343  *----------------------------------------------------------------------
344  *
345  * TreeItemColumn_SetStyle --
346  *
347  *	Assign a style to a Column, freeing the old one if it exists.
348  *
349  * Results:
350  *	None.
351  *
352  * Side effects:
353  *	None.
354  *
355  *----------------------------------------------------------------------
356  */
357 
358 void
TreeItemColumn_SetStyle(TreeCtrl * tree,TreeItemColumn column,TreeStyle style)359 TreeItemColumn_SetStyle(
360     TreeCtrl *tree,		/* Widget info. */
361     TreeItemColumn column,	/* Column token. */
362     TreeStyle style		/* New instance style. */
363     )
364 {
365     if (column->style != NULL) {
366 	TreeStyle_FreeResources(tree, column->style);
367     }
368     column->style = style;
369 }
370 
371 /*
372  *----------------------------------------------------------------------
373  *
374  * TreeItemColumn_GetNext --
375  *
376  *	Return the Column to the right of this one.
377  *
378  * Results:
379  *	The next Column in the linked list, or NULL.
380  *
381  * Side effects:
382  *	None.
383  *
384  *----------------------------------------------------------------------
385  */
386 
387 TreeItemColumn
TreeItemColumn_GetNext(TreeCtrl * tree,TreeItemColumn column)388 TreeItemColumn_GetNext(
389     TreeCtrl *tree,		/* Widget info. */
390     TreeItemColumn column	/* Column token. */
391     )
392 {
393     return column->next;
394 }
395 
396 /*
397  *----------------------------------------------------------------------
398  *
399  * Column_FreeResources --
400  *
401  *	Free the style and memory associated with the given Column.
402  *
403  * Results:
404  *	The next Column in the linked list, or NULL.
405  *
406  * Side effects:
407  *	Memory is freed.
408  *
409  *----------------------------------------------------------------------
410  */
411 
412 static TreeItemColumn
Column_FreeResources(TreeCtrl * tree,TreeItemColumn self)413 Column_FreeResources(
414     TreeCtrl *tree,		/* Widget info. */
415     TreeItemColumn self		/* Column to free. */
416     )
417 {
418     TreeItemColumn next = self->next;
419 
420     if (self->style != NULL)
421 	TreeStyle_FreeResources(tree, self->style);
422     if (self->headerColumn != NULL)
423 	TreeHeaderColumn_FreeResources(tree, self->headerColumn);
424 #ifdef ALLOC_HAX
425     TreeAlloc_Free(tree->allocData, ItemColumnUid, (char *) self, sizeof(TreeItemColumn_));
426 #else
427     WFREE(self, TreeItemColumn_);
428 #endif
429     return next;
430 }
431 
432 /*
433  *----------------------------------------------------------------------
434  *
435  * Item_UpdateIndex --
436  *
437  *	Set the Item.depth, Item.index and Item.indexVis fields of the
438  *	given Item and all its descendants.
439  *
440  * Results:
441  *	None.
442  *
443  * Side effects:
444  *	The TreeCtrl.depth field may be updated to track the maximum
445  *	depth of all items.
446  *
447  *----------------------------------------------------------------------
448  */
449 
450 static void
Item_UpdateIndex(TreeCtrl * tree,TreeItem item,int * index,int * indexVis)451 Item_UpdateIndex(TreeCtrl *tree,
452     TreeItem item,		/* Item to update. */
453     int *index,			/* New Item.index value for the item.
454 				 * Value is incremented. */
455     int *indexVis		/* New Item.indexVis value for the item if
456 				 * the item is ReallyVisible().
457 				 * Value is incremented if the item is
458 				 * ReallyVisible(). */
459     )
460 {
461     TreeItem child, parent = item->parent;
462     int parentVis, parentOpen;
463 
464     /* Also track max depth */
465     if (parent != NULL)
466 	item->depth = parent->depth + 1;
467     else
468 	item->depth = 0;
469     if (item->depth > tree->depth)
470 	tree->depth = item->depth;
471 
472     item->index = (*index)++;
473     item->indexVis = -1;
474     if (parent != NULL) {
475 	parentOpen = (parent->state & STATE_ITEM_OPEN) != 0;
476 	parentVis = parent->indexVis != -1;
477 	if (IS_ROOT(parent) && !tree->showRoot) {
478 	    parentOpen = TRUE;
479 	    parentVis = IS_VISIBLE(parent);
480 	}
481 	if (parentVis && parentOpen && IS_VISIBLE(item)) {
482 	    item->indexVis = (*indexVis)++;
483 	    if (IS_WRAP(item))
484 		tree->itemWrapCount++;
485 	}
486     }
487     child = item->firstChild;
488     while (child != NULL) {
489 	Item_UpdateIndex(tree, child, index, indexVis);
490 	child = child->nextSibling;
491     }
492 }
493 
494 /*
495  *----------------------------------------------------------------------
496  *
497  * Tree_UpdateItemIndex --
498  *
499  *	Set the Item.depth, Item.index and Item.indexVis fields of the
500  *	every Item. Set TreeCtrl.depth to the maximum depth of all
501  *	Items. Set TreeCtrl.itemVisCount to the count of all visible
502  *	items.
503  *
504  *	Because this is slow we try not to do it until necessary.
505  *	The tree->updateIndex flags indicates when this is needed.
506  *
507  * Results:
508  *	None.
509  *
510  * Side effects:
511  *	None.
512  *
513  *----------------------------------------------------------------------
514  */
515 
516 void
Tree_UpdateItemIndex(TreeCtrl * tree)517 Tree_UpdateItemIndex(
518     TreeCtrl *tree		/* Widget info. */
519     )
520 {
521     TreeItem item = tree->root;
522     int index = 1, indexVis = 0;
523 
524     if (!tree->updateIndex)
525 	return;
526 
527     if (tree->debug.enable && tree->debug.data)
528 	dbwin("Tree_UpdateItemIndex %s\n", Tk_PathName(tree->tkwin));
529 
530     /* Also track max depth */
531     tree->depth = -1;
532 
533     /* Count visible items with -wrap=true */
534     tree->itemWrapCount = 0;
535 
536     item->index = 0;
537     item->indexVis = -1;
538     if (tree->showRoot && IS_VISIBLE(item)) {
539 	item->indexVis = indexVis++;
540 	if (IS_WRAP(item))
541 	    tree->itemWrapCount++;
542     }
543     item = item->firstChild;
544     while (item != NULL) {
545 	Item_UpdateIndex(tree, item, &index, &indexVis);
546 	item = item->nextSibling;
547     }
548     tree->itemVisCount = indexVis;
549     tree->updateIndex = 0;
550 }
551 
552 /*
553  *----------------------------------------------------------------------
554  *
555  * Item_Alloc --
556  *
557  *	Allocate an initialize a new Item record.
558  *
559  * Results:
560  *	Pointer to the allocated Item record.
561  *
562  * Side effects:
563  *	Memory is allocated.
564  *
565  *----------------------------------------------------------------------
566  */
567 
568 static TreeItem
Item_Alloc(TreeCtrl * tree,int isHeader)569 Item_Alloc(
570     TreeCtrl *tree,		/* Widget info. */
571     int isHeader
572     )
573 {
574 #ifdef ALLOC_HAX
575     TreeItem item = (TreeItem) TreeAlloc_Alloc(tree->allocData, ItemUid, sizeof(TreeItem_));
576 #else
577     TreeItem item = (TreeItem) ckalloc(sizeof(TreeItem_));
578 #endif
579     memset(item, '\0', sizeof(TreeItem_));
580     if (Tk_InitOptions(tree->interp, (char *) item,
581 		tree->itemOptionTable, tree->tkwin) != TCL_OK)
582 	panic("Tk_InitOptions() failed in Item_Alloc()");
583     if (isHeader) {
584 	if (tree->gotFocus)
585 	    item->state |= STATE_HEADER_FOCUS;
586     } else {
587 	item->state =
588 	    STATE_ITEM_OPEN |
589 	    STATE_ITEM_ENABLED;
590 	if (tree->gotFocus)
591 	    item->state |= STATE_ITEM_FOCUS;
592     }
593     item->indexVis = -1;
594     /* In the typical case all spans are 1. */
595     item->flags |= ITEM_FLAG_SPANS_SIMPLE;
596     if (isHeader)
597 	Tree_AddHeader(tree, item);
598     else
599 	Tree_AddItem(tree, item);
600     return item;
601 }
602 
603 /*
604  *----------------------------------------------------------------------
605  *
606  * Item_AllocRoot --
607  *
608  *	Allocate and initialize a new Item record for the root item.
609  *
610  * Results:
611  *	Pointer to the allocated Item record.
612  *
613  * Side effects:
614  *	Memory is allocated.
615  *
616  *----------------------------------------------------------------------
617  */
618 
619 static TreeItem
Item_AllocRoot(TreeCtrl * tree)620 Item_AllocRoot(
621     TreeCtrl *tree		/* Widget info. */
622     )
623 {
624     TreeItem item;
625 
626     item = Item_Alloc(tree, FALSE);
627     item->depth = -1;
628     item->state |= STATE_ITEM_ACTIVE;
629     return item;
630 }
631 
632 /*
633  *----------------------------------------------------------------------
634  *
635  * TreeItem_GetFirstColumn --
636  *
637  *	Return the first Column record for an Item.
638  *
639  * Results:
640  *	Token for the column, or NULL.
641  *
642  * Side effects:
643  *	None.
644  *
645  *----------------------------------------------------------------------
646  */
647 
648 TreeItemColumn
TreeItem_GetFirstColumn(TreeCtrl * tree,TreeItem item)649 TreeItem_GetFirstColumn(
650     TreeCtrl *tree,		/* Widget info. */
651     TreeItem item		/* Item token. */
652     )
653 {
654     return item->columns;
655 }
656 
657 /*
658  *----------------------------------------------------------------------
659  *
660  * TreeItem_GetState --
661  *
662  *	Return the state flags for an Item.
663  *
664  * Results:
665  *	Bit mask of STATE_xxx flags.
666  *
667  * Side effects:
668  *	None.
669  *
670  *----------------------------------------------------------------------
671  */
672 
673 int
TreeItem_GetState(TreeCtrl * tree,TreeItem item)674 TreeItem_GetState(
675     TreeCtrl *tree,		/* Widget info. */
676     TreeItem item		/* Item token. */
677     )
678 {
679     return item->state;
680 }
681 
682 /*
683  *----------------------------------------------------------------------
684  *
685  * TreeItemColumn_GetState --
686  *
687  *	Return the state flags for an item-column.
688  *
689  * Results:
690  *	Bit mask of STATE_xxx flags.
691  *
692  * Side effects:
693  *	None.
694  *
695  *----------------------------------------------------------------------
696  */
697 
698 int
TreeItemColumn_GetState(TreeCtrl * tree,TreeItemColumn column)699 TreeItemColumn_GetState(
700     TreeCtrl *tree,		/* Widget info. */
701     TreeItemColumn column	/* Column token. */
702     )
703 {
704     return column->cstate;
705 }
706 
707 /*
708  *----------------------------------------------------------------------
709  *
710  * TreeItemColumn_ChangeState --
711  *
712  *	Toggles zero or more STATE_xxx flags for a Column. If the
713  *	Column has a style assigned, its state may be changed.
714  *
715  * Results:
716  *	Bit mask of CS_LAYOUT and CS_DISPLAY flags, or zero if no
717  *	changes occurred.
718  *
719  * Side effects:
720  *	Display changes.
721  *
722  *----------------------------------------------------------------------
723  */
724 
725 int
TreeItemColumn_ChangeState(TreeCtrl * tree,TreeItem item,TreeItemColumn column,TreeColumn treeColumn,int stateOff,int stateOn)726 TreeItemColumn_ChangeState(
727     TreeCtrl *tree,		/* Widget info. */
728     TreeItem item,		/* Item containing the column. */
729     TreeItemColumn column,	/* Column to modify the state of. */
730     TreeColumn treeColumn,	/* Tree column. */
731     int stateOff,		/* STATE_xxx flags to turn off. */
732     int stateOn			/* STATE_xxx flags to turn on. */
733     )
734 {
735     int cstate, state;
736     int sMask, iMask = 0;
737 
738     cstate = column->cstate;
739     cstate &= ~stateOff;
740     cstate |= stateOn;
741 
742     if (cstate == column->cstate)
743 	return 0;
744 
745     state = item->state | column->cstate;
746     state &= ~stateOff;
747     state |= stateOn;
748 
749     if (column->style != NULL) {
750 	sMask = TreeStyle_ChangeState(tree, column->style,
751 		item->state | column->cstate, state);
752 	if (sMask) {
753 	    if ((sMask & CS_LAYOUT) /*&& (item->header == NULL)*/)
754 		TreeColumns_InvalidateWidthOfItems(tree, treeColumn);
755 	    iMask |= sMask;
756 	}
757 
758 	if (iMask & CS_LAYOUT) {
759 	    TreeItem_InvalidateHeight(tree, item);
760 	    TreeItemColumn_InvalidateSize(tree, column);
761 	    Tree_FreeItemDInfo(tree, item, NULL);
762 	    if (item->header == NULL)
763 		Tree_DInfoChanged(tree, DINFO_REDO_RANGES);
764 	} else if (iMask & CS_DISPLAY) {
765 	    Tree_InvalidateItemDInfo(tree, treeColumn, item, NULL);
766 	}
767     }
768 
769     if ((iMask & CS_LAYOUT) /*&& (item->header != NULL)*/)
770 	TreeColumns_InvalidateWidth(tree);
771 
772     column->cstate = cstate;
773 
774     return iMask;
775 }
776 
777 /*
778  *----------------------------------------------------------------------
779  *
780  * TreeItem_ChangeState --
781  *
782  *	Toggles zero or more STATE_xxx flags for an Item. If the
783  *	Column has a style assigned, its state may be changed.
784  *
785  * Results:
786  *	Bit mask of CS_LAYOUT and CS_DISPLAY flags, or zero if no
787  *	changes occurred.
788  *
789  * Side effects:
790  *	Display changes.
791  *
792  *----------------------------------------------------------------------
793  */
794 
795 int
TreeItem_ChangeState(TreeCtrl * tree,TreeItem item,int stateOff,int stateOn)796 TreeItem_ChangeState(
797     TreeCtrl *tree,		/* Widget info. */
798     TreeItem item,		/* Item token. */
799     int stateOff,		/* STATE_xxx flags to turn off. */
800     int stateOn			/* STATE_xxx flags to turn on. */
801     )
802 {
803     TreeItemColumn column;
804     TreeColumn treeColumn;
805     int columnIndex = 0, state, cstate;
806     int sMask, iMask = 0;
807     int tailOK = item->header != NULL;
808 
809     state = item->state;
810     state &= ~stateOff;
811     state |= stateOn;
812 
813     if (state == item->state)
814 	return 0;
815 
816     treeColumn = Tree_FirstColumn(tree, -1, tailOK);
817     column = item->columns;
818     while (column != NULL) {
819 	if (column->style != NULL) {
820 	    cstate = item->state | column->cstate;
821 	    cstate &= ~stateOff;
822 	    cstate |= stateOn;
823 	    sMask = TreeStyle_ChangeState(tree, column->style,
824 		    item->state | column->cstate, cstate);
825 	    if (sMask) {
826 		if (sMask & CS_LAYOUT) {
827 		    TreeColumns_InvalidateWidthOfItems(tree, treeColumn);
828 		    TreeItemColumn_InvalidateSize(tree, column);
829 		} else if (sMask & CS_DISPLAY) {
830 		    Tree_InvalidateItemDInfo(tree, treeColumn, item, NULL);
831 		}
832 		iMask |= sMask;
833 	    }
834 	}
835 	columnIndex++;
836 	column = column->next;
837 	treeColumn = Tree_ColumnToTheRight(treeColumn, FALSE, tailOK);
838     }
839 
840     /* This item has a button */
841     if (TreeItem_HasButton(tree, item)) {
842 
843 	Tk_Image image1, image2;
844 	Pixmap bitmap1, bitmap2;
845 	/* NOTE: These next 2 lines must have 'static' to work around a
846 	 * Microsoft compiler optimization bug. */
847 	static int butOpen, butClosed;
848 	static int themeOpen, themeClosed;
849 	int w1, h1, w2, h2;
850 	void *ptr1 = NULL, *ptr2 = NULL;
851 
852 	/*
853 	 * Compare the image/bitmap/theme/xlib button for the old state
854 	 * to the image/bitmap/theme/xlib button for the new state. Figure
855 	 * out if the size or appearance has changed.
856 	 */
857 
858 	/* image > bitmap > theme > draw */
859 	image1 = PerStateImage_ForState(tree, &tree->buttonImage, item->state, NULL);
860 	if (image1 != NULL) {
861 	    Tk_SizeOfImage(image1, &w1, &h1);
862 	    ptr1 = image1;
863 	}
864 	if (ptr1 == NULL) {
865 	    bitmap1 = PerStateBitmap_ForState(tree, &tree->buttonBitmap, item->state, NULL);
866 	    if (bitmap1 != None) {
867 		Tk_SizeOfBitmap(tree->display, bitmap1, &w1, &h1);
868 		ptr1 = (void *) bitmap1;
869 	    }
870 	}
871 	if (ptr1 == NULL) {
872 	    if (tree->useTheme &&
873 		TreeTheme_GetButtonSize(tree, Tk_WindowId(tree->tkwin),
874 		(item->state & STATE_ITEM_OPEN) != 0, &w1, &h1) == TCL_OK) {
875 		ptr1 = (item->state & STATE_ITEM_OPEN) ? &themeOpen : &themeClosed;
876 	    }
877 	}
878 	if (ptr1 == NULL) {
879 	    w1 = h1 = tree->buttonSize;
880 	    ptr1 = (item->state & STATE_ITEM_OPEN) ? &butOpen : &butClosed;
881 	}
882 
883 	/* image > bitmap > theme > draw */
884 	image2 = PerStateImage_ForState(tree, &tree->buttonImage, state, NULL);
885 	if (image2 != NULL) {
886 	    Tk_SizeOfImage(image2, &w2, &h2);
887 	    ptr2 = image2;
888 	}
889 	if (ptr2 == NULL) {
890 	    bitmap2 = PerStateBitmap_ForState(tree, &tree->buttonBitmap, state, NULL);
891 	    if (bitmap2 != None) {
892 		Tk_SizeOfBitmap(tree->display, bitmap2, &w2, &h2);
893 		ptr2 = (void *) bitmap2;
894 	    }
895 	}
896 	if (ptr2 == NULL) {
897 	    if (tree->useTheme &&
898 		TreeTheme_GetButtonSize(tree, Tk_WindowId(tree->tkwin),
899 		(state & STATE_ITEM_OPEN) != 0, &w2, &h2) == TCL_OK) {
900 		ptr2 = (state & STATE_ITEM_OPEN) ? &themeOpen : &themeClosed;
901 	    }
902 	}
903 	if (ptr2 == NULL) {
904 	    w2 = h2 = tree->buttonSize;
905 	    ptr2 = (state & STATE_ITEM_OPEN) ? &butOpen : &butClosed;
906 	}
907 
908 	if ((w1 != w2) || (h1 != h2)) {
909 	    iMask |= CS_LAYOUT | CS_DISPLAY;
910 	} else if (ptr1 != ptr2) {
911 	    iMask |= CS_DISPLAY;
912 	    if (tree->columnTree != NULL)
913 		Tree_InvalidateItemDInfo(tree, tree->columnTree, item, NULL);
914 	}
915     }
916 
917     if (iMask & CS_LAYOUT) {
918 	TreeItem_InvalidateHeight(tree, item);
919 	Tree_FreeItemDInfo(tree, item, NULL);
920 	if (item->header == NULL)
921 	    Tree_DInfoChanged(tree, DINFO_REDO_RANGES);
922 	else
923 	    TreeColumns_InvalidateWidth(tree);
924     }
925 
926     item->state = state;
927 
928     return iMask;
929 }
930 
931 /*
932  *----------------------------------------------------------------------
933  *
934  * TreeItem_UndefineState --
935  *
936  *	Clear a STATE_xxx flag in an Item and its Columns. This is
937  *	called when a user-defined state is undefined via the
938  *	[state undefine] widget command.
939  *
940  * Results:
941  *	None.
942  *
943  * Side effects:
944  *	None.
945  *
946  *----------------------------------------------------------------------
947  */
948 
949 void
TreeItem_UndefineState(TreeCtrl * tree,TreeItem item,int state)950 TreeItem_UndefineState(
951     TreeCtrl *tree,		/* Widget info. */
952     TreeItem item,		/* Item token. */
953     int state			/* STATE_xxx flag that is undefined. */
954     )
955 {
956     TreeItemColumn column = item->columns;
957 
958     while (column != NULL) {
959 	column->cstate &= ~state;
960 	column = column->next;
961     }
962 
963     item->state &= ~state;
964 }
965 
966 /*
967  *----------------------------------------------------------------------
968  *
969  * TreeItem_HasButton --
970  *
971  *	Determine whether an item should have a button displayed next to
972  *	it. This considers the value of the item option -button as well
973  *	as the treectrl options -showbuttons, -showrootchildbuttons and
974  *	-showrootbutton.
975  *
976  * Results:
977  *	None.
978  *
979  * Side effects:
980  *	None.
981  *
982  *----------------------------------------------------------------------
983  */
984 
985 int
TreeItem_HasButton(TreeCtrl * tree,TreeItem item)986 TreeItem_HasButton(
987     TreeCtrl *tree,		/* Widget info. */
988     TreeItem item		/* Item token. */
989     )
990 {
991     if (!tree->showButtons || (IS_ROOT(item) && !tree->showRootButton))
992 	return 0;
993     if (item->parent == tree->root && !tree->showRootChildButtons)
994 	return 0;
995     if (item->flags & ITEM_FLAG_BUTTON)
996 	return 1;
997     if (item->flags & ITEM_FLAG_BUTTON_AUTO) {
998 	TreeItem child = item->firstChild;
999 	while (child != NULL) {
1000 	    if (IS_VISIBLE(child))
1001 		return 1;
1002 	    child = child->nextSibling;
1003 	}
1004     }
1005     return 0;
1006 }
1007 
1008 /*
1009  *----------------------------------------------------------------------
1010  *
1011  * TreeItem_GetButtonBbox --
1012  *
1013  *	Determine the bounding box of the expand/collapse button
1014  *	next to an item.
1015  *
1016  * Results:
1017  *	If the -treecolumn is not visible, or the item is not visible,
1018  *	or the item has no button, or the bounding box for the item
1019  *	in the -treecolumn has zero size, then 0 is returned.
1020  *
1021  *	Otherwise, the bounding box of the biggest possible button
1022  *	(considering -buttonbitmap, -buttonimage, -buttonsize and
1023  *	any themed button) is returned.
1024  *
1025  * Side effects:
1026  *	None.
1027  *
1028  *----------------------------------------------------------------------
1029  */
1030 
1031 int
TreeItem_GetButtonBbox(TreeCtrl * tree,TreeItem item,TreeRectangle * tr)1032 TreeItem_GetButtonBbox(
1033     TreeCtrl *tree,		/* Widget info. */
1034     TreeItem item,		/* Item token. */
1035     TreeRectangle *tr		/* Returned bounds of the button in
1036 				 * item coordinates. */
1037     )
1038 {
1039     TreeItemColumn itemColumn;
1040     TreeStyle style = NULL;
1041     int indent, buttonY = -1;
1042 
1043     if (!tree->columnTreeVis)
1044 	return 0;
1045 
1046     if (!TreeItem_HasButton(tree, item))
1047 	return 0;
1048 
1049     /* Get the bounding box in canvas coords of the tree-column for this
1050      * item. */
1051     if (TreeItem_GetRects(tree, item, tree->columnTree, 0, NULL, tr) == 0)
1052 	return 0;
1053 
1054     itemColumn = TreeItem_FindColumn(tree, item,
1055 	TreeColumn_Index(tree->columnTree));
1056     if (itemColumn != NULL)
1057 	style = TreeItemColumn_GetStyle(tree, itemColumn);
1058 
1059     indent = TreeItem_Indent(tree, tree->columnTree, item);
1060 
1061     if (style != NULL)
1062 	buttonY = TreeStyle_GetButtonY(tree, style);
1063 
1064     /* FIXME? The button is as wide as the -indent option. */
1065     tr->x = indent - tree->useIndent;
1066     tr->width = tree->useIndent;
1067 
1068     if (buttonY < 0)
1069 	tr->y = (tr->height - tree->buttonHeightMax) / 2;
1070     else
1071 	tr->y = buttonY;
1072     tr->height = tree->buttonHeightMax;
1073 
1074     return 1;
1075 }
1076 
1077 /*
1078  *----------------------------------------------------------------------
1079  *
1080  * TreeItem_IsPointInButton --
1081  *
1082  *	Determine if the given point is over the expand/collapse
1083  *	button next to an item.
1084  *
1085  * Results:
1086  *	1 if the point is over the button, 0 otherwise.
1087  *
1088  *	For the purposes of hit-testing, the button is considered to
1089  *	be larger than what is displayed to make it easier to click.
1090  *
1091  * Side effects:
1092  *	None.
1093  *
1094  *----------------------------------------------------------------------
1095  */
1096 
1097 int
TreeItem_IsPointInButton(TreeCtrl * tree,TreeItem item,int x,int y)1098 TreeItem_IsPointInButton(
1099     TreeCtrl *tree,		/* Widget info. */
1100     TreeItem item,		/* Item token. */
1101     int x,			/* Item coords. */
1102     int y			/* Item coords. */
1103     )
1104 {
1105     TreeRectangle tr;
1106 #define BUTTON_HITTEST_SLOP 11
1107     int centerY, slop = MAX(tree->buttonHeightMax / 2, BUTTON_HITTEST_SLOP);
1108 
1109     if (!TreeItem_GetButtonBbox(tree, item, &tr))
1110 	return 0;
1111 
1112     centerY = TreeRect_Top(tr) + TreeRect_Height(tr) / 2;
1113     if ((y < centerY - slop) ||
1114 	    (y >= centerY + slop + (tree->buttonHeightMax % 2)))
1115 	return 0;
1116 
1117     return 1;
1118 }
1119 
1120 /*
1121  *----------------------------------------------------------------------
1122  *
1123  * TreeItem_GetDepth --
1124  *
1125  *	Return the depth of an Item.
1126  *
1127  * Results:
1128  *	Integer >= -1. (-1 for the root)
1129  *
1130  * Side effects:
1131  *	None.
1132  *
1133  *----------------------------------------------------------------------
1134  */
1135 
1136 int
TreeItem_GetDepth(TreeCtrl * tree,TreeItem item)1137 TreeItem_GetDepth(
1138     TreeCtrl *tree,		/* Widget info. */
1139     TreeItem item		/* Item token. */
1140     )
1141 {
1142 #if 0
1143     Tree_UpdateItemIndex(tree);
1144 #endif
1145     return item->depth;
1146 }
1147 
1148 /*
1149  *----------------------------------------------------------------------
1150  *
1151  * TreeItem_GetID --
1152  *
1153  *	Return the unique ID of an Item.
1154  *
1155  * Results:
1156  *	Integer >= 0. (0 for the root)
1157  *
1158  * Side effects:
1159  *	None.
1160  *
1161  *----------------------------------------------------------------------
1162  */
1163 
1164 int
TreeItem_GetID(TreeCtrl * tree,TreeItem item)1165 TreeItem_GetID(
1166     TreeCtrl *tree,		/* Widget info. */
1167     TreeItem item		/* Item token. */
1168     )
1169 {
1170     return item->id;
1171 }
1172 
1173 /*
1174  *----------------------------------------------------------------------
1175  *
1176  * TreeItem_SetID --
1177  *
1178  *	Set the unique ID of an Item. This is called when the item
1179  *	is created.
1180  *
1181  * Results:
1182  *	The given ID.
1183  *
1184  * Side effects:
1185  *	None.
1186  *
1187  *----------------------------------------------------------------------
1188  */
1189 
1190 int
TreeItem_SetID(TreeCtrl * tree,TreeItem item,int id)1191 TreeItem_SetID(
1192     TreeCtrl *tree,		/* Widget info. */
1193     TreeItem item,		/* Item token. */
1194     int id			/* Unique ID for the item. */
1195     )
1196 {
1197     return item->id = id;
1198 }
1199 
1200 /*
1201  *----------------------------------------------------------------------
1202  *
1203  * TreeItem_GetEnabled --
1204  *
1205  *	Return whether an Item is enabled or not.
1206  *
1207  * Results:
1208  *	TRUE if the item is enabled, FALSE otherwise.
1209  *
1210  * Side effects:
1211  *	None.
1212  *
1213  *----------------------------------------------------------------------
1214  */
1215 
1216 int
TreeItem_GetEnabled(TreeCtrl * tree,TreeItem item)1217 TreeItem_GetEnabled(
1218     TreeCtrl *tree,		/* Widget info. */
1219     TreeItem item		/* Item token. */
1220     )
1221 {
1222     return (item->state & STATE_ITEM_ENABLED) != 0;
1223 }
1224 
1225 /*
1226  *----------------------------------------------------------------------
1227  *
1228  * TreeItem_GetSelected --
1229  *
1230  *	Return whether an Item is selected or not.
1231  *
1232  * Results:
1233  *	TRUE if the item is part of the selection, FALSE otherwise.
1234  *
1235  * Side effects:
1236  *	None.
1237  *
1238  *----------------------------------------------------------------------
1239  */
1240 
1241 int
TreeItem_GetSelected(TreeCtrl * tree,TreeItem item)1242 TreeItem_GetSelected(
1243     TreeCtrl *tree,		/* Widget info. */
1244     TreeItem item		/* Item token. */
1245     )
1246 {
1247     return (item->state & STATE_ITEM_SELECTED) != 0;
1248 }
1249 
1250 /*
1251  *----------------------------------------------------------------------
1252  *
1253  * TreeItem_CanAddToSelection --
1254  *
1255  *	Return whether an Item is selectable or not.
1256  *
1257  * Results:
1258  *	TRUE if the item can be added to the selection, FALSE otherwise.
1259  *
1260  * Side effects:
1261  *	None.
1262  *
1263  *----------------------------------------------------------------------
1264  */
1265 
1266 int
TreeItem_CanAddToSelection(TreeCtrl * tree,TreeItem item)1267 TreeItem_CanAddToSelection(
1268     TreeCtrl *tree,		/* Widget info. */
1269     TreeItem item		/* Item token. */
1270     )
1271 {
1272     if (item->header != NULL)
1273 	return FALSE;
1274 
1275     if (TreeItem_GetSelected(tree, item))
1276 	return FALSE;
1277 
1278     if (!TreeItem_GetEnabled(tree, item))
1279 	return FALSE;
1280 
1281 #ifdef SELECTION_VISIBLE
1282     if (!TreeItem_ReallyVisible(tree, item))
1283 	return FALSE;
1284 #endif
1285 
1286     return TRUE;
1287 }
1288 
1289 /*
1290  *----------------------------------------------------------------------
1291  *
1292  * TreeItem_GetParent --
1293  *
1294  *	Return the parent of an Item.
1295  *
1296  * Results:
1297  *	Token for parent Item, or NULL.
1298  *
1299  * Side effects:
1300  *	None.
1301  *
1302  *----------------------------------------------------------------------
1303  */
1304 
1305 TreeItem
TreeItem_GetParent(TreeCtrl * tree,TreeItem item)1306 TreeItem_GetParent(
1307     TreeCtrl *tree,		/* Widget info. */
1308     TreeItem item		/* Item token. */
1309     )
1310 {
1311     return item->parent;
1312 }
1313 
1314 /*
1315  *----------------------------------------------------------------------
1316  *
1317  * TreeItem_GetWrap --
1318  *
1319  *	Return whether an Item -wrap is TRUE or FALSE.
1320  *
1321  * Results:
1322  *	TRUE if the item should wrap, FALSE otherwise.
1323  *
1324  * Side effects:
1325  *	None.
1326  *
1327  *----------------------------------------------------------------------
1328  */
1329 
1330 int
TreeItem_GetWrap(TreeCtrl * tree,TreeItem item)1331 TreeItem_GetWrap(
1332     TreeCtrl *tree,		/* Widget info. */
1333     TreeItem item		/* Item token. */
1334     )
1335 {
1336     return (item->flags & ITEM_FLAG_WRAP) != 0;
1337 }
1338 
1339 /*
1340  *----------------------------------------------------------------------
1341  *
1342  * TreeItem_GetNextSibling --
1343  *
1344  *	Return the next sibling of an Item.
1345  *
1346  * Results:
1347  *	Token for next sibling Item, or NULL.
1348  *
1349  * Side effects:
1350  *	None.
1351  *
1352  *----------------------------------------------------------------------
1353  */
1354 
1355 TreeItem
TreeItem_GetNextSibling(TreeCtrl * tree,TreeItem item)1356 TreeItem_GetNextSibling(
1357     TreeCtrl *tree,		/* Widget info. */
1358     TreeItem item		/* Item token. */
1359     )
1360 {
1361     return item->nextSibling;
1362 }
1363 
1364 /*
1365  *----------------------------------------------------------------------
1366  *
1367  * TreeItem_NextSiblingVisible --
1368  *
1369  *	Find a following sibling that is ReallyVisible().
1370  *
1371  * Results:
1372  *	Token for a sibling Item, or NULL.
1373  *
1374  * Side effects:
1375  *	None.
1376  *
1377  *----------------------------------------------------------------------
1378  */
1379 
1380 TreeItem
TreeItem_NextSiblingVisible(TreeCtrl * tree,TreeItem item)1381 TreeItem_NextSiblingVisible(
1382     TreeCtrl *tree,		/* Widget info. */
1383     TreeItem item		/* Item token. */
1384     )
1385 {
1386     item = TreeItem_GetNextSibling(tree, item);
1387     while (item != NULL) {
1388 	if (TreeItem_ReallyVisible(tree, item))
1389 	    return item;
1390 	item = TreeItem_GetNextSibling(tree, item);
1391     }
1392     return NULL;
1393 }
1394 
1395 /*
1396  *----------------------------------------------------------------------
1397  *
1398  * TreeItem_SetDInfo --
1399  *
1400  *	Store a display-info token in an Item. Called by the display
1401  *	code.
1402  *
1403  * Results:
1404  *	None.
1405  *
1406  * Side effects:
1407  *	None.
1408  *
1409  *----------------------------------------------------------------------
1410  */
1411 
1412 void
TreeItem_SetDInfo(TreeCtrl * tree,TreeItem item,TreeItemDInfo dInfo)1413 TreeItem_SetDInfo(
1414     TreeCtrl *tree,		/* Widget info. */
1415     TreeItem item,		/* Item token. */
1416     TreeItemDInfo dInfo		/* Display-info token. */
1417     )
1418 {
1419     item->dInfo = dInfo;
1420 }
1421 
1422 /*
1423  *----------------------------------------------------------------------
1424  *
1425  * TreeItem_GetDInfo --
1426  *
1427  *	Return the display-info token of an Item. Called by the display
1428  *	code.
1429  *
1430  * Results:
1431  *	The display-info token or NULL.
1432  *
1433  * Side effects:
1434  *	None.
1435  *
1436  *----------------------------------------------------------------------
1437  */
1438 
1439 TreeItemDInfo
TreeItem_GetDInfo(TreeCtrl * tree,TreeItem item)1440 TreeItem_GetDInfo(
1441     TreeCtrl *tree,		/* Widget info. */
1442     TreeItem item		/* Item token. */
1443     )
1444 {
1445     return item->dInfo;
1446 }
1447 
1448 /*
1449  *----------------------------------------------------------------------
1450  *
1451  * TreeItem_SetRInfo --
1452  *
1453  *	Store a range-info token in an Item. Called by the display
1454  *	code.
1455  *
1456  * Results:
1457  *	None.
1458  *
1459  * Side effects:
1460  *	None.
1461  *
1462  *----------------------------------------------------------------------
1463  */
1464 
1465 void
TreeItem_SetRInfo(TreeCtrl * tree,TreeItem item,TreeItemRInfo rInfo)1466 TreeItem_SetRInfo(
1467     TreeCtrl *tree,		/* Widget info. */
1468     TreeItem item,		/* Item token. */
1469     TreeItemRInfo rInfo		/* Range-info token */
1470     )
1471 {
1472     item->rInfo = rInfo;
1473 }
1474 
1475 /*
1476  *----------------------------------------------------------------------
1477  *
1478  * TreeItem_GetRInfo --
1479  *
1480  *	Return the range-info token of an Item. Called by the display
1481  *	code.
1482  *
1483  * Results:
1484  *	The range-info token or NULL.
1485  *
1486  * Side effects:
1487  *	None.
1488  *
1489  *----------------------------------------------------------------------
1490  */
1491 
1492 TreeItemRInfo
TreeItem_GetRInfo(TreeCtrl * tree,TreeItem item)1493 TreeItem_GetRInfo(
1494     TreeCtrl *tree,		/* Widget info. */
1495     TreeItem item		/* Item token. */
1496     )
1497 {
1498     return item->rInfo;
1499 }
1500 
1501 /*
1502  *----------------------------------------------------------------------
1503  *
1504  * TreeItem_Next --
1505  *
1506  *	Return the Item after the given one.
1507  *
1508  * Results:
1509  *	Result will be:
1510  *	  a) the first child, or
1511  *	  b) the next sibling, or
1512  *	  c) the next sibling of an ancestor, or
1513  *	  d) NULL if no item follows this one
1514  *
1515  * Side effects:
1516  *	None.
1517  *
1518  *----------------------------------------------------------------------
1519  */
1520 
1521 TreeItem
TreeItem_Next(TreeCtrl * tree,TreeItem item)1522 TreeItem_Next(
1523     TreeCtrl *tree,		/* Widget info. */
1524     TreeItem item		/* Item token. */
1525     )
1526 {
1527     if (item->firstChild != NULL)
1528 	return item->firstChild;
1529     if (item->nextSibling != NULL)
1530 	return item->nextSibling;
1531     while (1) {
1532 	item = item->parent;
1533 	if (item == NULL)
1534 	    break;
1535 	if (item->nextSibling != NULL)
1536 	    return item->nextSibling;
1537     }
1538     return NULL;
1539 }
1540 
1541 /*
1542  *----------------------------------------------------------------------
1543  *
1544  * TreeItem_NextVisible --
1545  *
1546  *	Return the ReallyVisible() Item after the given one.
1547  *
1548  * Results:
1549  *	Result will be:
1550  *	  a) the first ReallyVisible() child, or
1551  *	  b) the next ReallyVisible() sibling, or
1552  *	  c) the next ReallyVisible() sibling of an ancestor, or
1553  *	  d) NULL if no ReallyVisible() item follows this one
1554  *
1555  * Side effects:
1556  *	None.
1557  *
1558  *----------------------------------------------------------------------
1559  */
1560 
1561 TreeItem
TreeItem_NextVisible(TreeCtrl * tree,TreeItem item)1562 TreeItem_NextVisible(
1563     TreeCtrl *tree,		/* Widget info. */
1564     TreeItem item		/* Item token. */
1565     )
1566 {
1567     item = TreeItem_Next(tree, item);
1568     while (item != NULL) {
1569 	if (TreeItem_ReallyVisible(tree, item))
1570 	    return item;
1571 	item = TreeItem_Next(tree, item);
1572     }
1573     return NULL;
1574 }
1575 
1576 /*
1577  *----------------------------------------------------------------------
1578  *
1579  * TreeItem_Prev --
1580  *
1581  *	Return the Item before the given one.
1582  *
1583  * Results:
1584  *	Result will be:
1585  *	  a) the last descendant of the previous sibling, or
1586  *	  b) the parent, or
1587  *	  c) NULL if the given Item is the root
1588  *
1589  * Side effects:
1590  *	None.
1591  *
1592  *----------------------------------------------------------------------
1593  */
1594 
1595 TreeItem
TreeItem_Prev(TreeCtrl * tree,TreeItem item)1596 TreeItem_Prev(
1597     TreeCtrl *tree,		/* Widget info. */
1598     TreeItem item		/* Item token. */
1599     )
1600 {
1601     TreeItem walk;
1602 
1603     if (item->parent == NULL) /* root */
1604 	return NULL;
1605     walk = item->parent;
1606     if (item->prevSibling) {
1607 	walk = item->prevSibling;
1608 	while (walk->lastChild != NULL)
1609 	    walk = walk->lastChild;
1610     }
1611     return walk;
1612 }
1613 
1614 /*
1615  *----------------------------------------------------------------------
1616  *
1617  * TreeItem_PrevVisible --
1618  *
1619  *	Return the ReallyVisible() Item before the given one.
1620  *
1621  * Results:
1622  *	Result will be:
1623  *	  a) the last descendant of the previous sibling, or
1624  *	  b) the parent, or
1625  *	  c) NULL if the given Item is the root
1626  *
1627  * Side effects:
1628  *	None.
1629  *
1630  *----------------------------------------------------------------------
1631  */
1632 
1633 TreeItem
TreeItem_PrevVisible(TreeCtrl * tree,TreeItem item)1634 TreeItem_PrevVisible(
1635     TreeCtrl *tree,		/* Widget info. */
1636     TreeItem item		/* Item token. */
1637     )
1638 {
1639     item = TreeItem_Prev(tree, item);
1640     while (item != NULL) {
1641 	if (TreeItem_ReallyVisible(tree, item))
1642 	    return item;
1643 	item = TreeItem_Prev(tree, item);
1644     }
1645     return NULL;
1646 }
1647 
1648 /*
1649  *----------------------------------------------------------------------
1650  *
1651  * TreeItem_ToIndex --
1652  *
1653  *	Return Item.index and Item.indexVis.
1654  *
1655  * Results:
1656  *	The zero-based indexes of the Item.
1657  *
1658  * Side effects:
1659  *	Will recalculate the indexes of every item if needed.
1660  *
1661  *----------------------------------------------------------------------
1662  */
1663 
1664 void
TreeItem_ToIndex(TreeCtrl * tree,TreeItem item,int * index,int * indexVis)1665 TreeItem_ToIndex(
1666     TreeCtrl *tree,		/* Widget info. */
1667     TreeItem item,		/* Item token. */
1668     int *index,			/* Returned Item.index, may be NULL */
1669     int *indexVis		/* Returned Item.indexVis, may be NULL */
1670     )
1671 {
1672     Tree_UpdateItemIndex(tree);
1673     if (index != NULL) (*index) = item->index;
1674     if (indexVis != NULL) (*indexVis) = item->indexVis;
1675 }
1676 
1677 /*
1678  *----------------------------------------------------------------------
1679  *
1680  * TreeItem_HasTag --
1681  *
1682  *	Checks whether an item has a certain tag.
1683  *
1684  * Results:
1685  *	Returns TRUE if the item has the given tag.
1686  *
1687  * Side effects:
1688  *	None.
1689  *
1690  *----------------------------------------------------------------------
1691  */
1692 
1693 int
TreeItem_HasTag(TreeItem item,Tk_Uid tag)1694 TreeItem_HasTag(
1695     TreeItem item,		/* The item to test. */
1696     Tk_Uid tag			/* Tag to look for. */
1697     )
1698 {
1699     TagInfo *tagInfo = item->tagInfo;
1700     Tk_Uid *tagPtr;
1701     int count;
1702 
1703     if (tagInfo == NULL)
1704 	return 0;
1705 
1706     for (tagPtr = tagInfo->tagPtr, count = tagInfo->numTags;
1707 	count > 0; tagPtr++, count--) {
1708 	if (*tagPtr == tag) {
1709 	    return 1;
1710 	}
1711     }
1712     return 0;
1713 }
1714 
1715 typedef struct Qualifiers {
1716     TreeCtrl *tree;
1717     int visible;		/* 1 if the item must be ReallyVisible(),
1718 				   0 if the item must not be ReallyVisible(),
1719 				   -1 for unspecified. */
1720     int states[3];		/* Item states that must be on or off. */
1721     TagExpr expr;		/* Tag expression. */
1722     int exprOK;			/* TRUE if expr is valid. */
1723     int depth;			/* >= 0 for depth, -1 for unspecified */
1724     Tk_Uid tag;			/* Tag (without operators) or NULL. */
1725 } Qualifiers;
1726 
1727 /*
1728  *----------------------------------------------------------------------
1729  *
1730  * Qualifiers_Init --
1731  *
1732  *	Helper routine for TreeItem_FromObj.
1733  *
1734  * Results:
1735  *	None.
1736  *
1737  * Side effects:
1738  *	None.
1739  *
1740  *----------------------------------------------------------------------
1741  */
1742 
1743 static void
Qualifiers_Init(TreeCtrl * tree,Qualifiers * q)1744 Qualifiers_Init(
1745     TreeCtrl *tree,		/* Widget info. */
1746     Qualifiers *q		/* Out: Initialized qualifiers. */
1747     )
1748 {
1749     q->tree = tree;
1750     q->visible = -1;
1751     q->states[0] = q->states[1] = q->states[2] = 0;
1752     q->exprOK = FALSE;
1753     q->depth = -1;
1754     q->tag = NULL;
1755 }
1756 
1757 /*
1758  *----------------------------------------------------------------------
1759  *
1760  * Qualifiers_Scan --
1761  *
1762  *	Helper routine for TreeItemList_FromObj.
1763  *
1764  * Results:
1765  *	TCL_OK or TCL_ERROR.
1766  *
1767  * Side effects:
1768  *	None.
1769  *
1770  *----------------------------------------------------------------------
1771  */
1772 
1773 static int
Qualifiers_Scan(Qualifiers * q,int objc,Tcl_Obj ** objv,int startIndex,int * argsUsed)1774 Qualifiers_Scan(
1775     Qualifiers *q,		/* Must call Qualifiers_Init first,
1776 				 * and Qualifiers_Free if result is TCL_OK. */
1777     int objc,			/* Number of arguments. */
1778     Tcl_Obj **objv,		/* Argument values. */
1779     int startIndex,		/* First objv[] index to look at. */
1780     int *argsUsed		/* Out: number of objv[] used. */
1781     )
1782 {
1783     TreeCtrl *tree = q->tree;
1784     Tcl_Interp *interp = tree->interp;
1785     int qual, j = startIndex;
1786 
1787     static CONST char *qualifiers[] = {
1788 	"depth", "state", "tag", "visible", "!visible", NULL
1789     };
1790     enum qualEnum {
1791 	QUAL_DEPTH, QUAL_STATE, QUAL_TAG, QUAL_VISIBLE, QUAL_NOT_VISIBLE
1792     };
1793     /* Number of arguments used by qualifiers[]. */
1794     static int qualArgs[] = {
1795 	2, 2, 2, 1, 1
1796     };
1797 
1798     *argsUsed = 0;
1799 
1800     for (; j < objc; ) {
1801 	if (Tcl_GetIndexFromObj(NULL, objv[j], qualifiers, NULL, 0,
1802 		&qual) != TCL_OK)
1803 	    break;
1804 	if (objc - j < qualArgs[qual]) {
1805 	    Tcl_AppendResult(interp, "missing arguments to \"",
1806 		    Tcl_GetString(objv[j]), "\" qualifier", NULL);
1807 	    goto errorExit;
1808 	}
1809 	switch ((enum qualEnum) qual) {
1810 	    case QUAL_DEPTH: {
1811 		if (Tcl_GetIntFromObj(interp, objv[j + 1], &q->depth) != TCL_OK)
1812 		    goto errorExit;
1813 		break;
1814 	    }
1815 	    case QUAL_STATE: {
1816 		if (Tree_StateFromListObj(tree, STATE_DOMAIN_ITEM, objv[j + 1], q->states,
1817 			SFO_NOT_TOGGLE) != TCL_OK)
1818 		    goto errorExit;
1819 		break;
1820 	    }
1821 	    case QUAL_TAG: {
1822 		if (tree->itemTagExpr) {
1823 		    if (q->exprOK)
1824 			TagExpr_Free(&q->expr);
1825 		    if (TagExpr_Init(tree, objv[j + 1], &q->expr) != TCL_OK)
1826 			return TCL_ERROR;
1827 		    q->exprOK = TRUE;
1828 		} else {
1829 		    q->tag = Tk_GetUid(Tcl_GetString(objv[j + 1]));
1830 		}
1831 		break;
1832 	    }
1833 	    case QUAL_VISIBLE: {
1834 		q->visible = 1;
1835 		break;
1836 	    }
1837 	    case QUAL_NOT_VISIBLE: {
1838 		q->visible = 0;
1839 		break;
1840 	    }
1841 	}
1842 	*argsUsed += qualArgs[qual];
1843 	j += qualArgs[qual];
1844     }
1845     return TCL_OK;
1846 errorExit:
1847     if (q->exprOK)
1848 	TagExpr_Free(&q->expr);
1849     return TCL_ERROR;
1850 }
1851 
1852 /*
1853  *----------------------------------------------------------------------
1854  *
1855  * Qualifies --
1856  *
1857  *	Helper routine for TreeItem_FromObj.
1858  *
1859  * Results:
1860  *	Returns TRUE if the item meets the given criteria.
1861  *
1862  * Side effects:
1863  *	None.
1864  *
1865  *----------------------------------------------------------------------
1866  */
1867 
1868 static int
Qualifies(Qualifiers * q,TreeItem item)1869 Qualifies(
1870     Qualifiers *q,		/* Qualifiers to check. */
1871     TreeItem item		/* The item to test. May be NULL. */
1872     )
1873 {
1874     TreeCtrl *tree = q->tree;
1875 
1876     /* Note: if the item is NULL it is a "match" because we have run
1877      * out of items to check. */
1878     if (item == NULL)
1879 	return 1;
1880     if ((q->visible == 1) && !TreeItem_ReallyVisible(tree, item))
1881 	return 0;
1882     else if ((q->visible == 0) && TreeItem_ReallyVisible(tree, (TreeItem) item))
1883 	return 0;
1884     if (q->states[STATE_OP_OFF] & item->state)
1885 	return 0;
1886     if ((q->states[STATE_OP_ON] & item->state) != q->states[STATE_OP_ON])
1887 	return 0;
1888     if (q->exprOK && !TagExpr_Eval(&q->expr, item->tagInfo))
1889 	return 0;
1890     if ((q->depth >= 0) && (item->depth + 1 != q->depth))
1891 	return 0;
1892     if ((q->tag != NULL) && !TreeItem_HasTag(item, q->tag))
1893 	return 0;
1894     return 1;
1895 }
1896 
1897 /*
1898  *----------------------------------------------------------------------
1899  *
1900  * Qualifiers_Free --
1901  *
1902  *	Helper routine for TreeItem_FromObj.
1903  *
1904  * Results:
1905  *	None.
1906  *
1907  * Side effects:
1908  *	None.
1909  *
1910  *----------------------------------------------------------------------
1911  */
1912 
1913 static void
Qualifiers_Free(Qualifiers * q)1914 Qualifiers_Free(
1915     Qualifiers *q		/* Out: Initialized qualifiers. */
1916     )
1917 {
1918     if (q->exprOK)
1919 	TagExpr_Free(&q->expr);
1920 }
1921 
1922 /*
1923  *----------------------------------------------------------------------
1924  *
1925  * NotManyMsg --
1926  *
1927  *	Set the interpreter result with an error message.
1928  *
1929  * Results:
1930  *	None.
1931  *
1932  * Side effects:
1933  *	Changes the interpreter result.
1934  *
1935  *----------------------------------------------------------------------
1936  */
1937 
1938 static void
NotManyMsg(TreeCtrl * tree,int doHeaders)1939 NotManyMsg(
1940     TreeCtrl *tree,
1941     int doHeaders
1942     )
1943 {
1944     FormatResult(tree->interp, "can't specify > 1 %s for this command",
1945 	doHeaders ? "header" : "item");
1946 }
1947 
1948 /*
1949  *----------------------------------------------------------------------
1950  *
1951  * TreeItemList_FromObj --
1952  *
1953  *	Parse a Tcl_Obj item description to get a list of items.
1954  *
1955  *   -- returning a single item --
1956  *   "active MODIFIERS"
1957  *   "anchor MODIFIERS"
1958  *   "nearest x y MODIFIERS"
1959  *   "root MODIFIERS"
1960  *   "first QUALIFIERS MODIFIERS"
1961  *   "last QUALIFIERS MODIFIERS"
1962  *   "end QUALIFIERS MODIFIERS"
1963  *   "rnc row col MODIFIERS"
1964  *   "ID MODIFIERS"
1965  *   -- returning multiple items --
1966  *   all QUALIFIERS
1967  *   QUALIFIERS (like "all QUALIFIERS")
1968  *   list listOfItemDescs
1969  *   range QUALIFIERS
1970  *   tag tagExpr QUALIFIERS
1971  *   "TAG-EXPR QUALFIERS"
1972  *
1973  *   MODIFIERS:
1974  *   -- returning a single item --
1975  *   above
1976  *   below
1977  *   left
1978  *   right
1979  *   top
1980  *   bottom
1981  *   leftmost
1982  *   rightmost
1983  *   next QUALIFIERS
1984  *   prev QUALIFIERS
1985  *   parent
1986  *   firstchild QUALIFIERS
1987  *   lastchild QUALIFIERS
1988  *   child N|end?-offset? QUALIFIERS
1989  *   nextsibling QUALIFIERS
1990  *   prevsibling QUALIFIERS
1991  *   sibling N|end?-offset? QUALIFIERS
1992  *   -- returning multiple items --
1993  *   ancestors QUALIFIERS
1994  *   children QUALIFIERS
1995  *   descendants QUALIFIERS
1996  *
1997  *   QUALIFIERS:
1998  *   depth integer
1999  *   state stateList
2000  *   tag tagExpr
2001  *   visible
2002  *   !visible
2003  *
2004  *   Examples:
2005  *   $T item id "first visible firstchild"
2006  *   $T item id "first visible firstchild visible"
2007  *   $T item id "nearest x y nextsibling visible"
2008  *   $T item id "last visible state enabled"
2009  *
2010  * Results:
2011  *	TCL_OK or TCL_ERROR.
2012  *
2013  * Side effects:
2014  *	None.
2015  *
2016  *----------------------------------------------------------------------
2017  */
2018 
2019 int
TreeItemList_FromObj(TreeCtrl * tree,Tcl_Obj * objPtr,TreeItemList * items,int flags)2020 TreeItemList_FromObj(
2021     TreeCtrl *tree,		/* Widget info. */
2022     Tcl_Obj *objPtr,		/* Object to parse to a list of items. */
2023     TreeItemList *items,	/* Uninitialized item list. Caller must free
2024 				 * it with TreeItemList_Free unless the
2025 				 * result of this function is TCL_ERROR. */
2026     int flags			/* IFO_xxx flags */
2027     )
2028 {
2029     Tcl_Interp *interp = tree->interp;
2030     int i, objc, index, listIndex, id;
2031     Tcl_HashEntry *hPtr;
2032     Tcl_HashSearch search;
2033     Tcl_Obj **objv, *elemPtr;
2034     TreeItem item = NULL;
2035     Qualifiers q;
2036     int qualArgsTotal;
2037 
2038     static CONST char *indexName[] = {
2039 	"active", "all", "anchor", "end", "first", "last", "list",
2040 	"nearest", "range", "rnc", "root", (char *) NULL
2041     };
2042     enum indexEnum {
2043 	INDEX_ACTIVE, INDEX_ALL, INDEX_ANCHOR, INDEX_END, INDEX_FIRST,
2044 	INDEX_LAST, INDEX_LIST, INDEX_NEAREST, INDEX_RANGE, INDEX_RNC,
2045 	INDEX_ROOT
2046     };
2047     /* Number of arguments used by indexName[]. */
2048     static int indexArgs[] = {
2049 	1, 1, 1, 1, 1, 1, 2, 3, 3, 3, 1
2050     };
2051     /* Boolean: can indexName[] be followed by 1 or more qualifiers. */
2052     static int indexQual[] = {
2053 	0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1
2054     };
2055 
2056     static CONST char *modifiers[] = {
2057 	"above", "ancestors", "below", "bottom", "child", "children",
2058 	"descendants", "firstchild", "lastchild", "left", "leftmost", "next",
2059 	"nextsibling", "parent", "prev", "prevsibling", "right", "rightmost",
2060 	"sibling", "top", (char *) NULL
2061     };
2062     enum modEnum {
2063 	TMOD_ABOVE, TMOD_ANCESTORS, TMOD_BELOW, TMOD_BOTTOM, TMOD_CHILD,
2064 	TMOD_CHILDREN, TMOD_DESCENDANTS, TMOD_FIRSTCHILD, TMOD_LASTCHILD,
2065 	TMOD_LEFT, TMOD_LEFTMOST, TMOD_NEXT, TMOD_NEXTSIBLING, TMOD_PARENT,
2066 	TMOD_PREV, TMOD_PREVSIBLING, TMOD_RIGHT, TMOD_RIGHTMOST, TMOD_SIBLING,
2067 	TMOD_TOP
2068     };
2069     /* Number of arguments used by modifiers[]. */
2070     static int modArgs[] = {
2071 	1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1
2072     };
2073     /* Boolean: can modifiers[] be followed by 1 or more qualifiers. */
2074     static int modQual[] = {
2075 	0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0
2076     };
2077 
2078     TreeItemList_Init(tree, items, 0);
2079     Qualifiers_Init(tree, &q);
2080 
2081     if (Tcl_ListObjGetElements(NULL, objPtr, &objc, &objv) != TCL_OK)
2082 	goto baditem;
2083     if (objc == 0)
2084 	goto baditem;
2085 
2086     listIndex = 0;
2087     elemPtr = objv[listIndex];
2088     if (Tcl_GetIndexFromObj(NULL, elemPtr, indexName, NULL, 0, &index)
2089 	    == TCL_OK) {
2090 
2091 	if (objc - listIndex < indexArgs[index]) {
2092 	    Tcl_AppendResult(interp, "missing arguments to \"",
2093 		    Tcl_GetString(elemPtr), "\" keyword", NULL);
2094 	    goto errorExit;
2095 	}
2096 
2097 	qualArgsTotal = 0;
2098 	if (indexQual[index]) {
2099 	    if (Qualifiers_Scan(&q, objc, objv, listIndex + indexArgs[index],
2100 		    &qualArgsTotal) != TCL_OK) {
2101 		goto errorExit;
2102 	    }
2103 	}
2104 
2105 	switch ((enum indexEnum) index) {
2106 	    case INDEX_ACTIVE: {
2107 		item = tree->activeItem;
2108 		break;
2109 	    }
2110 	    case INDEX_ALL: {
2111 		if (qualArgsTotal) {
2112 		    hPtr = Tcl_FirstHashEntry(&tree->itemHash, &search);
2113 		    while (hPtr != NULL) {
2114 			item = (TreeItem) Tcl_GetHashValue(hPtr);
2115 			if (Qualifies(&q, item)) {
2116 			    TreeItemList_Append(items, (TreeItem) item);
2117 			}
2118 			hPtr = Tcl_NextHashEntry(&search);
2119 		    }
2120 		    item = NULL;
2121 		} else if (flags & IFO_LIST_ALL) {
2122 		    hPtr = Tcl_FirstHashEntry(&tree->itemHash, &search);
2123 		    while (hPtr != NULL) {
2124 			item = (TreeItem) Tcl_GetHashValue(hPtr);
2125 			TreeItemList_Append(items, item);
2126 			hPtr = Tcl_NextHashEntry(&search);
2127 		    }
2128 		    item = NULL;
2129 		} else {
2130 		    item = ITEM_ALL;
2131 		}
2132 		break;
2133 	    }
2134 	    case INDEX_ANCHOR: {
2135 		item = tree->anchorItem;
2136 		break;
2137 	    }
2138 	    case INDEX_FIRST: {
2139 		item = tree->root;
2140 		while (!Qualifies(&q, item))
2141 		    item = TreeItem_Next(tree, item);
2142 		break;
2143 	    }
2144 	    case INDEX_END:
2145 	    case INDEX_LAST: {
2146 		item = tree->root;
2147 		while (item->lastChild) {
2148 		    item = item->lastChild;
2149 		}
2150 		while (!Qualifies(&q, item))
2151 		    item = TreeItem_Prev(tree, item);
2152 		break;
2153 	    }
2154 	    case INDEX_LIST: {
2155 		int listObjc;
2156 		Tcl_Obj **listObjv;
2157 		int count;
2158 
2159 		if (Tcl_ListObjGetElements(interp, objv[listIndex + 1],
2160 			&listObjc, &listObjv) != TCL_OK) {
2161 		    goto errorExit;
2162 		}
2163 		for (i = 0; i < listObjc; i++) {
2164 		    TreeItemList item2s;
2165 		    if (TreeItemList_FromObj(tree, listObjv[i], &item2s, flags)
2166 			    != TCL_OK)
2167 			goto errorExit;
2168 		    TreeItemList_Concat(items, &item2s);
2169 		    TreeItemList_Free(&item2s);
2170 		}
2171 		/* If any of the item descriptions in the list is "all", then
2172 		 * clear the list of items and use "all". */
2173 		count = TreeItemList_Count(items);
2174 		for (i = 0; i < count; i++) {
2175 		    TreeItem item2 = TreeItemList_Nth(items, i);
2176 		    if (IS_ALL(item2))
2177 			break;
2178 		}
2179 		if (i < count) {
2180 		    TreeItemList_Free(items);
2181 		    item = ITEM_ALL;
2182 		} else
2183 		    item = NULL;
2184 		break;
2185 	    }
2186 	    case INDEX_NEAREST: {
2187 		int x, y;
2188 
2189 		if (Tk_GetPixelsFromObj(interp, tree->tkwin,
2190 			objv[listIndex + 1], &x) != TCL_OK) {
2191 		    goto errorExit;
2192 		}
2193 		if (Tk_GetPixelsFromObj(interp, tree->tkwin,
2194 			objv[listIndex + 2], &y) != TCL_OK) {
2195 		    goto errorExit;
2196 		}
2197 		item = Tree_ItemUnderPoint(tree, &x, &y, NULL, TRUE);
2198 		break;
2199 	    }
2200 	    case INDEX_RANGE: {
2201 		TreeItem itemFirst, itemLast;
2202 
2203 		if (TreeItem_FromObj(tree, objv[listIndex + 1], &itemFirst,
2204 			IFO_NOT_NULL) != TCL_OK)
2205 		    goto errorExit;
2206 		if (TreeItem_FromObj(tree, objv[listIndex + 2], &itemLast,
2207 			IFO_NOT_NULL) != TCL_OK)
2208 		    goto errorExit;
2209 		if (TreeItem_FirstAndLast(tree, &itemFirst, &itemLast) == 0)
2210 		    goto errorExit;
2211 		while (1) {
2212 		    if (Qualifies(&q, itemFirst)) {
2213 			TreeItemList_Append(items, itemFirst);
2214 		    }
2215 		    if (itemFirst == itemLast)
2216 			break;
2217 		    itemFirst = TreeItem_Next(tree, itemFirst);
2218 		}
2219 		item = NULL;
2220 		break;
2221 	    }
2222 	    case INDEX_RNC: {
2223 		int row, col;
2224 
2225 		if (Tcl_GetIntFromObj(interp, objv[listIndex + 1], &row) != TCL_OK)
2226 		    goto errorExit;
2227 		if (Tcl_GetIntFromObj(interp, objv[listIndex + 2], &col) != TCL_OK)
2228 		    goto errorExit;
2229 		item = Tree_RNCToItem(tree, row, col);
2230 		break;
2231 	    }
2232 	    case INDEX_ROOT: {
2233 		item = tree->root;
2234 		break;
2235 	    }
2236 	}
2237 
2238 	listIndex += indexArgs[index] + qualArgsTotal;
2239 
2240     /* No indexName[] was found. */
2241     } else {
2242 	int gotId = FALSE;
2243 	TagExpr expr;
2244 
2245 	/* Try an itemPrefix + item ID. */
2246 	if (tree->itemPrefixLen) {
2247 	    char *end, *t = Tcl_GetString(elemPtr);
2248 	    if (strncmp(t, tree->itemPrefix, tree->itemPrefixLen) == 0) {
2249 		t += tree->itemPrefixLen;
2250 		id = strtoul(t, &end, 10);
2251 		if ((end != t) && (*end == '\0'))
2252 		    gotId = TRUE;
2253 	    }
2254 
2255 	/* Try an item ID. */
2256 	} else if (Tcl_GetIntFromObj(NULL, elemPtr, &id) == TCL_OK) {
2257 	    gotId = TRUE;
2258 	}
2259 	if (gotId) {
2260 	    hPtr = Tcl_FindHashEntry(&tree->itemHash, (char *) INT2PTR(id));
2261 	    if (hPtr != NULL) {
2262 		item = (TreeItem) Tcl_GetHashValue(hPtr);
2263 	    } else {
2264 		item = NULL;
2265 	    }
2266 	    listIndex++;
2267 	    goto gotFirstPart;
2268 	}
2269 
2270 	/* Try a list of qualifiers. This has the same effect as
2271 	 * "all QUALIFIERS". */
2272 	if (Qualifiers_Scan(&q, objc, objv, listIndex, &qualArgsTotal)
2273 		!= TCL_OK) {
2274 	    goto errorExit;
2275 	}
2276 	if (qualArgsTotal) {
2277 	    hPtr = Tcl_FirstHashEntry(&tree->itemHash, &search);
2278 	    while (hPtr != NULL) {
2279 		item = (TreeItem) Tcl_GetHashValue(hPtr);
2280 		if (Qualifies(&q, item)) {
2281 		    TreeItemList_Append(items, item);
2282 		}
2283 		hPtr = Tcl_NextHashEntry(&search);
2284 	    }
2285 	    item = NULL;
2286 	    listIndex += qualArgsTotal;
2287 	    goto gotFirstPart;
2288 	}
2289 
2290 	/* Try a tag or tag expression followed by qualifiers. */
2291 	if (objc > 1) {
2292 	    if (Qualifiers_Scan(&q, objc, objv, listIndex + 1,
2293 		    &qualArgsTotal) != TCL_OK) {
2294 		goto errorExit;
2295 	    }
2296 	}
2297 	if (tree->itemTagExpr) {
2298 	    if (TagExpr_Init(tree, elemPtr, &expr) != TCL_OK)
2299 		goto errorExit;
2300 	    hPtr = Tcl_FirstHashEntry(&tree->itemHash, &search);
2301 	    while (hPtr != NULL) {
2302 		item = (TreeItem) Tcl_GetHashValue(hPtr);
2303 		if (TagExpr_Eval(&expr, item->tagInfo) && Qualifies(&q, item)) {
2304 		    TreeItemList_Append(items, item);
2305 		}
2306 		hPtr = Tcl_NextHashEntry(&search);
2307 	    }
2308 	    TagExpr_Free(&expr);
2309 	} else {
2310 	    Tk_Uid tag = Tk_GetUid(Tcl_GetString(elemPtr));
2311 	    hPtr = Tcl_FirstHashEntry(&tree->itemHash, &search);
2312 	    while (hPtr != NULL) {
2313 		item = (TreeItem) Tcl_GetHashValue(hPtr);
2314 		if (TreeItem_HasTag(item, tag) && Qualifies(&q, item)) {
2315 		    TreeItemList_Append(items, item);
2316 		}
2317 		hPtr = Tcl_NextHashEntry(&search);
2318 	    }
2319 	}
2320 	item = NULL;
2321 	listIndex += 1 + qualArgsTotal;
2322     }
2323 
2324 gotFirstPart:
2325     /* If 1 item, use it and clear the list. */
2326     if (TreeItemList_Count(items) == 1) {
2327 	item = TreeItemList_Nth(items, 0);
2328 	items->count = 0;
2329     }
2330 
2331     /* If "all" but only root exists, use it. */
2332     if (IS_ALL(item) && (tree->itemCount == 1) && !(flags & IFO_NOT_ROOT)) {
2333 	item = tree->root;
2334     }
2335 
2336     /* If > 1 item, no modifiers may follow. */
2337     if ((TreeItemList_Count(items) > 1) || IS_ALL(item)) {
2338 	if (listIndex < objc) {
2339 	    Tcl_AppendResult(interp, "unexpected arguments after \"",
2340 		(char *) NULL);
2341 	    for (i = 0; i < listIndex; i++) {
2342 		Tcl_AppendResult(interp, Tcl_GetString(objv[i]), (char *) NULL);
2343 		if (i != listIndex - 1)
2344 		    Tcl_AppendResult(interp, " ", (char *) NULL);
2345 	    }
2346 	    Tcl_AppendResult(interp, "\"", (char *) NULL);
2347 	    goto errorExit;
2348 	}
2349     }
2350 
2351     /* This means a valid specification was given, but there is no such item */
2352     if ((TreeItemList_Count(items) == 0) && (item == NULL)) {
2353 	if (flags & IFO_NOT_NULL)
2354 	    goto noitem;
2355 	/* Empty list returned */
2356 	goto goodExit;
2357     }
2358 
2359     /* Process any modifiers following the item we matched above. */
2360     for (; listIndex < objc; /* nothing */) {
2361 
2362 	elemPtr = objv[listIndex];
2363 	if (Tcl_GetIndexFromObj(interp, elemPtr, modifiers, "modifier", 0,
2364 		    &index) != TCL_OK) {
2365 	    goto errorExit;
2366 	}
2367 	if (objc - listIndex < modArgs[index]) {
2368 	    Tcl_AppendResult(interp, "missing arguments to \"",
2369 		    Tcl_GetString(elemPtr), "\" modifier", NULL);
2370 	    goto errorExit;
2371 	}
2372 
2373 	qualArgsTotal = 0;
2374 	if (modQual[index]) {
2375 	    Qualifiers_Free(&q);
2376 	    Qualifiers_Init(tree, &q);
2377 	    if (Qualifiers_Scan(&q, objc, objv, listIndex + modArgs[index],
2378 		    &qualArgsTotal) != TCL_OK) {
2379 		goto errorExit;
2380 	    }
2381 	}
2382 
2383 	switch ((enum modEnum) index) {
2384 	    case TMOD_ABOVE: {
2385 		item = Tree_ItemAbove(tree, item);
2386 		break;
2387 	    }
2388 	    case TMOD_ANCESTORS: {
2389 		item = item->parent;
2390 		while (item != NULL) {
2391 		    if (Qualifies(&q, item)) {
2392 			TreeItemList_Append(items, item);
2393 		    }
2394 		    item = item->parent;
2395 		}
2396 		item = NULL;
2397 		break;
2398 	    }
2399 	    case TMOD_BELOW: {
2400 		item = Tree_ItemBelow(tree, item);
2401 		break;
2402 	    }
2403 	    case TMOD_BOTTOM: {
2404 		item = Tree_ItemBottom(tree, item);
2405 		break;
2406 	    }
2407 	    case TMOD_CHILD: {
2408 		int n, endRelative;
2409 
2410 		if (Tree_GetIntForIndex(tree, objv[listIndex + 1], &n,
2411 			&endRelative) != TCL_OK) {
2412 		    goto errorExit;
2413 		}
2414 		if (endRelative) {
2415 		    item = item->lastChild;
2416 		    while (item != NULL) {
2417 			if (Qualifies(&q, item))
2418 			    if (n-- <= 0)
2419 				break;
2420 			item = item->prevSibling;
2421 		    }
2422 		} else {
2423 		    item = item->firstChild;
2424 		    while (item != NULL) {
2425 			if (Qualifies(&q, item))
2426 			    if (n-- <= 0)
2427 				break;
2428 			item = item->nextSibling;
2429 		    }
2430 		}
2431 		break;
2432 	    }
2433 	    case TMOD_CHILDREN: {
2434 		item = item->firstChild;
2435 		while (item != NULL) {
2436 		    if (Qualifies(&q, item)) {
2437 			TreeItemList_Append(items, item);
2438 		    }
2439 		    item = item->nextSibling;
2440 		}
2441 		item = NULL;
2442 		break;
2443 	    }
2444 	    case TMOD_DESCENDANTS: {
2445 		TreeItem last = item;
2446 
2447 		while (last->lastChild != NULL)
2448 		    last = last->lastChild;
2449 		item = item->firstChild;
2450 		while (item != NULL) {
2451 		    if (Qualifies(&q, item)) {
2452 			TreeItemList_Append(items, item);
2453 		    }
2454 		    if (item == last)
2455 			break;
2456 		    item = TreeItem_Next(tree, item);
2457 		}
2458 		item = NULL;
2459 		break;
2460 	    }
2461 	    case TMOD_FIRSTCHILD: {
2462 		item = item->firstChild;
2463 		while (!Qualifies(&q, item))
2464 		    item = item->nextSibling;
2465 		break;
2466 	    }
2467 	    case TMOD_LASTCHILD: {
2468 		item = item->lastChild;
2469 		while (!Qualifies(&q, item))
2470 		    item = item->prevSibling;
2471 		break;
2472 	    }
2473 	    case TMOD_LEFT: {
2474 		item = Tree_ItemLeft(tree, item);
2475 		break;
2476 	    }
2477 	    case TMOD_LEFTMOST: {
2478 		item = Tree_ItemLeftMost(tree, item);
2479 		break;
2480 	    }
2481 	    case TMOD_NEXT: {
2482 		item = TreeItem_Next(tree, item);
2483 		while (!Qualifies(&q, item))
2484 		    item = TreeItem_Next(tree, item);
2485 		break;
2486 	    }
2487 	    case TMOD_NEXTSIBLING: {
2488 		item = item->nextSibling;
2489 		while (!Qualifies(&q, item))
2490 		    item = item->nextSibling;
2491 		break;
2492 	    }
2493 	    case TMOD_PARENT: {
2494 		item = item->parent;
2495 		break;
2496 	    }
2497 	    case TMOD_PREV: {
2498 		item = TreeItem_Prev(tree, item);
2499 		while (!Qualifies(&q, item))
2500 		    item = TreeItem_Prev(tree, item);
2501 		break;
2502 	    }
2503 	    case TMOD_PREVSIBLING: {
2504 		item = item->prevSibling;
2505 		while (!Qualifies(&q, item))
2506 		    item = item->prevSibling;
2507 		break;
2508 	    }
2509 	    case TMOD_RIGHT: {
2510 		item = Tree_ItemRight(tree, item);
2511 		break;
2512 	    }
2513 	    case TMOD_RIGHTMOST: {
2514 		item = Tree_ItemRightMost(tree, item);
2515 		break;
2516 	    }
2517 	    case TMOD_SIBLING: {
2518 		int n, endRelative;
2519 
2520 		if (Tree_GetIntForIndex(tree, objv[listIndex + 1], &n,
2521 			&endRelative) != TCL_OK) {
2522 		    goto errorExit;
2523 		}
2524 		item = item->parent;
2525 		if (item == NULL)
2526 		    break;
2527 		if (endRelative) {
2528 		    item = item->lastChild;
2529 		    while (item != NULL) {
2530 			if (Qualifies(&q, item))
2531 			    if (n-- <= 0)
2532 				break;
2533 			item = item->prevSibling;
2534 		    }
2535 		} else {
2536 		    item = item->firstChild;
2537 		    while (item != NULL) {
2538 			if (Qualifies(&q, item))
2539 			    if (n-- <= 0)
2540 				break;
2541 			item = item->nextSibling;
2542 		    }
2543 		}
2544 		break;
2545 	    }
2546 	    case TMOD_TOP: {
2547 		item = Tree_ItemTop(tree, item);
2548 		break;
2549 	    }
2550 	}
2551 	if ((TreeItemList_Count(items) > 1) || IS_ALL(item)) {
2552 	    int end = listIndex + modArgs[index] + qualArgsTotal;
2553 	    if (end < objc) {
2554 		Tcl_AppendResult(interp, "unexpected arguments after \"",
2555 		    (char *) NULL);
2556 		for (i = 0; i < end; i++) {
2557 		    Tcl_AppendResult(interp, Tcl_GetString(objv[i]), (char *) NULL);
2558 		    if (i != end - 1)
2559 			Tcl_AppendResult(interp, " ", (char *) NULL);
2560 		}
2561 		Tcl_AppendResult(interp, "\"", (char *) NULL);
2562 		goto errorExit;
2563 	    }
2564 	}
2565 	if ((TreeItemList_Count(items) == 0) && (item == NULL)) {
2566 	    if (flags & IFO_NOT_NULL)
2567 		goto noitem;
2568 	    /* Empty list returned. */
2569 	    goto goodExit;
2570 	}
2571 	listIndex += modArgs[index] + qualArgsTotal;
2572     }
2573     if ((flags & IFO_NOT_MANY) && (IS_ALL(item) ||
2574 	    (TreeItemList_Count(items) > 1))) {
2575 	NotManyMsg(tree, FALSE);
2576 	goto errorExit;
2577     }
2578     if (TreeItemList_Count(items)) {
2579 	if (flags & (IFO_NOT_ROOT | IFO_NOT_ORPHAN)) {
2580 	    int i;
2581 	    for (i = 0; i < TreeItemList_Count(items); i++) {
2582 		item = TreeItemList_Nth(items, i);
2583 		if (IS_ROOT(item) && (flags & IFO_NOT_ROOT))
2584 		    goto notRoot;
2585 		if ((item->parent == NULL) && (flags & IFO_NOT_ORPHAN))
2586 		    goto notOrphan;
2587 	    }
2588 	}
2589     } else if (IS_ALL(item)) {
2590 	TreeItemList_Append(items, ITEM_ALL);
2591     } else {
2592 	if (IS_ROOT(item) && (flags & IFO_NOT_ROOT)) {
2593 notRoot:
2594 	    FormatResult(interp, "can't specify \"root\" for this command");
2595 	    goto errorExit;
2596 	}
2597 	if ((item->parent == NULL) && (flags & IFO_NOT_ORPHAN)) {
2598 notOrphan:
2599 	    FormatResult(interp, "item \"%s\" has no parent",
2600 		    Tcl_GetString(objPtr));
2601 	    goto errorExit;
2602 	}
2603 	TreeItemList_Append(items, item);
2604     }
2605 goodExit:
2606     Qualifiers_Free(&q);
2607     return TCL_OK;
2608 
2609 baditem:
2610     Tcl_AppendResult(interp, "bad item description \"", Tcl_GetString(objPtr),
2611 	    "\"", NULL);
2612     goto errorExit;
2613 
2614 noitem:
2615     Tcl_AppendResult(interp, "item \"", Tcl_GetString(objPtr),
2616 	    "\" doesn't exist", NULL);
2617 
2618 errorExit:
2619     Qualifiers_Free(&q);
2620     TreeItemList_Free(items);
2621     return TCL_ERROR;
2622 }
2623 
2624 /*
2625  *----------------------------------------------------------------------
2626  *
2627  * TreeItem_FromObj --
2628  *
2629  *	Parse a Tcl_Obj item description to get a single item.
2630  *
2631  * Results:
2632  *	TCL_OK or TCL_ERROR.
2633  *
2634  * Side effects:
2635  *	None.
2636  *
2637  *----------------------------------------------------------------------
2638  */
2639 
2640 int
TreeItem_FromObj(TreeCtrl * tree,Tcl_Obj * objPtr,TreeItem * itemPtr,int flags)2641 TreeItem_FromObj(
2642     TreeCtrl *tree,		/* Widget info. */
2643     Tcl_Obj *objPtr,		/* Object to parse to an item. */
2644     TreeItem *itemPtr,		/* Returned item. */
2645     int flags			/* IFO_xxx flags */
2646     )
2647 {
2648     TreeItemList items;
2649 
2650     if (TreeItemList_FromObj(tree, objPtr, &items, flags | IFO_NOT_MANY) != TCL_OK)
2651 	return TCL_ERROR;
2652     /* May be NULL. */
2653     (*itemPtr) = TreeItemList_Nth(&items, 0);
2654     TreeItemList_Free(&items);
2655     return TCL_OK;
2656 }
2657 
2658 /*
2659  *----------------------------------------------------------------------
2660  *
2661  * TreeItemForEach_Start --
2662  *
2663  *	Begin iterating over items. A command might accept two item
2664  *	descriptions for a range of items, or a single item description
2665  *	which may itself refer to multiple items. Either item
2666  *	description could be ITEM_ALL.
2667  *
2668  * Results:
2669  *	Returns the first item to iterate over. If an error occurs
2670  *	then ItemForEach.error is set to 1.
2671  *
2672  * Side effects:
2673  *	None.
2674  *
2675  *----------------------------------------------------------------------
2676  */
2677 
2678 TreeItem
TreeItemForEach_Start(TreeItemList * items,TreeItemList * item2s,ItemForEach * iter)2679 TreeItemForEach_Start(
2680     TreeItemList *items,	/* List of items. */
2681     TreeItemList *item2s,	/* List of items or NULL. */
2682     ItemForEach *iter		/* Returned info, pass to
2683 				   TreeItemForEach_Next. */
2684     )
2685 {
2686     TreeCtrl *tree = items->tree;
2687     TreeItem item, item2 = NULL;
2688 
2689     item = TreeItemList_Nth(items, 0);
2690     if (item2s)
2691 	item2 = TreeItemList_Nth(item2s, 0);
2692 
2693     iter->tree = tree;
2694     iter->all = FALSE;
2695     iter->error = 0;
2696     iter->items = NULL;
2697 
2698     if (IS_ALL(item) || IS_ALL(item2)) {
2699 	Tcl_HashEntry *hPtr = Tcl_FirstHashEntry(&tree->itemHash, &iter->search);
2700 	iter->all = TRUE;
2701 	return iter->item = (TreeItem) Tcl_GetHashValue(hPtr);
2702     }
2703 
2704     if (item2 != NULL) {
2705 	if (TreeItem_FirstAndLast(tree, &item, &item2) == 0) {
2706 	    iter->error = 1;
2707 	    return NULL;
2708 	}
2709 	iter->last = item2;
2710 	return iter->item = item;
2711     }
2712 
2713     iter->items = items;
2714     iter->index = 0;
2715     return iter->item = item;
2716 }
2717 
2718 /*
2719  *----------------------------------------------------------------------
2720  *
2721  * TreeItemForEach_Next --
2722  *
2723  *	Returns the next item to iterate over. Keep calling this until
2724  *	the result is NULL.
2725  *
2726  * Results:
2727  *	Returns the next item to iterate over or NULL.
2728  *
2729  * Side effects:
2730  *	None.
2731  *
2732  *----------------------------------------------------------------------
2733  */
2734 
2735 TreeItem
TreeItemForEach_Next(ItemForEach * iter)2736 TreeItemForEach_Next(
2737     ItemForEach *iter		/* Initialized by TreeItemForEach_Start. */
2738     )
2739 {
2740     TreeCtrl *tree = iter->tree;
2741 
2742     if (iter->all) {
2743 	Tcl_HashEntry *hPtr = Tcl_NextHashEntry(&iter->search);
2744 	if (hPtr == NULL)
2745 	    return iter->item = NULL;
2746 	return iter->item = (TreeItem) Tcl_GetHashValue(hPtr);
2747     }
2748 
2749     if (iter->items != NULL) {
2750 	if (iter->index >= TreeItemList_Count(iter->items))
2751 	    return iter->item = NULL;
2752 	return iter->item = TreeItemList_Nth(iter->items, ++iter->index);
2753     }
2754 
2755     if (iter->item == iter->last)
2756 	return iter->item = NULL;
2757     return iter->item = TreeItem_Next(tree, iter->item);
2758 }
2759 
2760 /*
2761  *----------------------------------------------------------------------
2762  *
2763  * Item_ToggleOpen --
2764  *
2765  *	Inverts the STATE_ITEM_OPEN flag of an Item.
2766  *
2767  * Results:
2768  *	Items may be displayed/undisplayed.
2769  *
2770  * Side effects:
2771  *	Display changes.
2772  *
2773  *----------------------------------------------------------------------
2774  */
2775 
2776 static void
Item_ToggleOpen(TreeCtrl * tree,TreeItem item,int stateOff,int stateOn)2777 Item_ToggleOpen(
2778     TreeCtrl *tree,		/* Widget info. */
2779     TreeItem item,		/* Item record. */
2780     int stateOff,		/* STATE_ITEM_OPEN or 0 */
2781     int stateOn			/* STATE_ITEM_OPEN or 0 */
2782     )
2783 {
2784     int mask;
2785 
2786     mask = TreeItem_ChangeState(tree, item, stateOff, stateOn);
2787 
2788     if (IS_ROOT(item) && !tree->showRoot)
2789 	return;
2790 
2791 #if 0
2792     /* Don't affect display if we weren't visible */
2793     if (!TreeItem_ReallyVisible(tree, item))
2794 	return;
2795 
2796     /* Invalidate display info for this item, so it is redrawn later. */
2797     Tree_InvalidateItemDInfo(tree, item, NULL);
2798 #endif
2799 
2800     if (item->numChildren > 0) {
2801 	/* indexVis needs updating for all items after this one, if we
2802 	 * have any visible children */
2803 	tree->updateIndex = 1;
2804 	Tree_DInfoChanged(tree, DINFO_REDO_RANGES);
2805 
2806 	/* Hiding/showing children may change the width of any column */
2807 	TreeColumns_InvalidateWidthOfItems(tree, NULL);
2808 	TreeColumns_InvalidateSpans(tree);
2809     }
2810 
2811     /* If this item was previously onscreen, this call is repetitive. */
2812     Tree_EventuallyRedraw(tree);
2813 }
2814 
2815 /*
2816  *----------------------------------------------------------------------
2817  *
2818  * TreeItem_OpenClose --
2819  *
2820  *	Inverts the STATE_ITEM_OPEN flag of an Item.
2821  *
2822  * Results:
2823  *	Items may be displayed/undisplayed.
2824  *
2825  * Side effects:
2826  *	Display changes. <Expand> and <Collapse> events may be
2827  *	generated.
2828  *
2829  *----------------------------------------------------------------------
2830  */
2831 
2832 void
TreeItem_OpenClose(TreeCtrl * tree,TreeItem item,int mode)2833 TreeItem_OpenClose(
2834     TreeCtrl *tree,		/* Widget info. */
2835     TreeItem item,		/* Item token. */
2836     int mode			/* -1: toggle
2837 				 * 0: close
2838 				 * 1: open */
2839     )
2840 {
2841     int stateOff = 0, stateOn = 0;
2842 
2843     /* When processing a list of items, any <Expand> or <Collapse> event
2844      * may result in items being deleted. */
2845     if (IS_DELETED(item)) return;
2846 
2847     if (mode == -1) {
2848 	if (item->state & STATE_ITEM_OPEN)
2849 	    stateOff = STATE_ITEM_OPEN;
2850 	else
2851 	    stateOn = STATE_ITEM_OPEN;
2852     } else if (!mode && (item->state & STATE_ITEM_OPEN))
2853 	stateOff = STATE_ITEM_OPEN;
2854     else if (mode && !(item->state & STATE_ITEM_OPEN))
2855 	stateOn = STATE_ITEM_OPEN;
2856 
2857     if (stateOff != stateOn) {
2858 	TreeNotify_OpenClose(tree, item, stateOn, TRUE);
2859 	if (IS_DELETED(item)) return;
2860 	Item_ToggleOpen(tree, item, stateOff, stateOn);
2861 	TreeNotify_OpenClose(tree, item, stateOn, FALSE);
2862     }
2863 }
2864 
2865 /*
2866  *----------------------------------------------------------------------
2867  *
2868  * TreeItem_Delete --
2869  *
2870  *	Recursively frees resources associated with an Item and its
2871  *	descendants.
2872  *
2873  * Results:
2874  *	Items are removed from their parent and freed.
2875  *
2876  * Side effects:
2877  *	Memory is freed. If the active item or selection-anchor item
2878  *	is deleted, the root becomes the active/anchor item.
2879  *	Display changes may occur.
2880  *
2881  *----------------------------------------------------------------------
2882  */
2883 
2884 void
TreeItem_Delete(TreeCtrl * tree,TreeItem item)2885 TreeItem_Delete(
2886     TreeCtrl *tree,		/* Widget info. */
2887     TreeItem item		/* Item token. */
2888     )
2889 {
2890     while (item->numChildren > 0)
2891 	TreeItem_Delete(tree, item->firstChild);
2892 
2893     /* Remove from tree->headerItems. */
2894     if (item->header != NULL) {
2895 	if (item != tree->headerItems) {
2896 	    item->prevSibling->nextSibling = item->nextSibling;
2897 	    if (item->nextSibling != NULL)
2898 		item->nextSibling->prevSibling = item->prevSibling;
2899 	} else {
2900 	    tree->headerItems = item->nextSibling;
2901 	    if (item->nextSibling != NULL)
2902 		item->nextSibling->prevSibling = NULL;
2903 	}
2904 	item->prevSibling = item->nextSibling = NULL;
2905     }
2906 
2907     TreeItem_RemoveFromParent(tree, item);
2908     TreeDisplay_ItemDeleted(tree, item);
2909     TreeGradient_ItemDeleted(tree, item);
2910     TreeTheme_ItemDeleted(tree, item);
2911     if (item->header != NULL)
2912 	Tree_RemoveHeader(tree, item);
2913     else
2914 	Tree_RemoveItem(tree, item);
2915     TreeItem_FreeResources(tree, item);
2916     if (tree->activeItem == item) {
2917 	tree->activeItem = tree->root;
2918 	TreeItem_ChangeState(tree, tree->activeItem, 0, STATE_ITEM_ACTIVE);
2919     }
2920     if (tree->anchorItem == item)
2921 	tree->anchorItem = tree->root;
2922     if (tree->debug.enable && tree->debug.data)
2923 	Tree_Debug(tree);
2924 }
2925 
2926 /*
2927  *----------------------------------------------------------------------
2928  *
2929  * TreeItem_Deleted --
2930  *
2931  *	Return 1 if the given item is deleted.
2932  *
2933  * Results:
2934  *	None.
2935  *
2936  * Side effects:
2937  *	None.
2938  *
2939  *----------------------------------------------------------------------
2940  */
2941 
2942 int
TreeItem_Deleted(TreeCtrl * tree,TreeItem item)2943 TreeItem_Deleted(
2944     TreeCtrl *tree,		/* Widget info. */
2945     TreeItem item		/* Item token. */
2946     )
2947 {
2948     return IS_DELETED(item);
2949 }
2950 
2951 /*
2952  *----------------------------------------------------------------------
2953  *
2954  * TreeItem_FirstAndLast --
2955  *
2956  *	Determine the order of two items and swap them if needed.
2957  *
2958  * Results:
2959  *	If the items do not share a common ancestor, 0 is returned and
2960  *	an error message is left in the interpreter result.
2961  *	Otherwise the return value is the number of items in the
2962  *	range between first and last.
2963  *
2964  * Side effects:
2965  *	None.
2966  *
2967  *----------------------------------------------------------------------
2968  */
2969 
2970 int
TreeItem_FirstAndLast(TreeCtrl * tree,TreeItem * first,TreeItem * last)2971 TreeItem_FirstAndLast(
2972     TreeCtrl *tree,		/* Widget info. */
2973     TreeItem *first,		/* Item token. */
2974     TreeItem *last		/* Item token. */
2975     )
2976 {
2977     int indexFirst, indexLast, index;
2978 
2979     if (TreeItem_RootAncestor(tree, *first) !=
2980 	    TreeItem_RootAncestor(tree, *last)) {
2981 	FormatResult(tree->interp,
2982 		"item %s%d and item %s%d don't share a common ancestor",
2983 		tree->itemPrefix, TreeItem_GetID(tree, *first),
2984 		tree->itemPrefix, TreeItem_GetID(tree, *last));
2985 	return 0;
2986     }
2987     TreeItem_ToIndex(tree, *first, &indexFirst, NULL);
2988     TreeItem_ToIndex(tree, *last, &indexLast, NULL);
2989     if (indexFirst > indexLast) {
2990 	TreeItem item = *first;
2991 	*first = *last;
2992 	*last = item;
2993 
2994 	index = indexFirst;
2995 	indexFirst = indexLast;
2996 	indexLast = index;
2997     }
2998     return indexLast - indexFirst + 1;
2999 }
3000 
3001 /*
3002  *----------------------------------------------------------------------
3003  *
3004  * TreeItem_ListDescendants --
3005  *
3006  *	Appends descendants of an item to a list of items.
3007  *
3008  * Results:
3009  *	List of items may grow.
3010  *
3011  * Side effects:
3012  *	None.
3013  *
3014  *----------------------------------------------------------------------
3015  */
3016 
3017 void
TreeItem_ListDescendants(TreeCtrl * tree,TreeItem item,TreeItemList * items)3018 TreeItem_ListDescendants(
3019     TreeCtrl *tree,		/* Widget info. */
3020     TreeItem item,		/* Item token. */
3021     TreeItemList *items		/* List of items to append descendants to. */
3022     )
3023 {
3024     TreeItem last;
3025 
3026     if (item->firstChild == NULL)
3027 	return;
3028     last = item;
3029     while (last->lastChild != NULL)
3030 	last = last->lastChild;
3031     item = item->firstChild;
3032     while (1) {
3033 	TreeItemList_Append(items, item);
3034 	if (item == last)
3035 	    break;
3036 	item = TreeItem_Next(tree, item);
3037     }
3038 }
3039 
3040 /*
3041  *----------------------------------------------------------------------
3042  *
3043  * TreeItem_UpdateDepth --
3044  *
3045  *	Recursively updates Item.depth of an Item and its
3046  *	descendants.
3047  *
3048  * Results:
3049  *	None.
3050  *
3051  * Side effects:
3052  *	None.
3053  *
3054  *----------------------------------------------------------------------
3055  */
3056 
3057 void
TreeItem_UpdateDepth(TreeCtrl * tree,TreeItem item)3058 TreeItem_UpdateDepth(
3059     TreeCtrl *tree,		/* Widget info. */
3060     TreeItem item		/* Item token. */
3061     )
3062 {
3063     TreeItem child;
3064 
3065     if (IS_ROOT(item))
3066 	return;
3067     if (item->parent != NULL)
3068 	item->depth = item->parent->depth + 1;
3069     else
3070 	item->depth = 0;
3071     child = item->firstChild;
3072     while (child != NULL) {
3073 	TreeItem_UpdateDepth(tree, child);
3074 	child = child->nextSibling;
3075     }
3076 }
3077 
3078 /*
3079  *----------------------------------------------------------------------
3080  *
3081  * TreeItem_AddToParent --
3082  *
3083  *	Called *after* an Item is added as a child to another Item.
3084  *
3085  * Results:
3086  *	None.
3087  *
3088  * Side effects:
3089  *	Display changes.
3090  *
3091  *----------------------------------------------------------------------
3092  */
3093 
3094 void
TreeItem_AddToParent(TreeCtrl * tree,TreeItem item)3095 TreeItem_AddToParent(
3096     TreeCtrl *tree,		/* Widget info. */
3097     TreeItem item		/* Item token. */
3098     )
3099 {
3100     TreeItem last, parent = item->parent;
3101 
3102     /* If this is the new last child, redraw the lines of the previous
3103      * sibling and all of its descendants so the line from the previous
3104      * sibling reaches this item */
3105     if ((item->prevSibling != NULL) &&
3106 	    (item->nextSibling == NULL) &&
3107 	    tree->showLines && (tree->columnTree != NULL)) {
3108 	last = item->prevSibling;
3109 	while (last->lastChild != NULL)
3110 	    last = last->lastChild;
3111 	Tree_InvalidateItemDInfo(tree, tree->columnTree,
3112 		item->prevSibling, last);
3113     }
3114 
3115     /* Redraw the parent if the parent has "-button auto". */
3116     if (IS_VISIBLE(item) && (parent->flags & ITEM_FLAG_BUTTON_AUTO) &&
3117 	    tree->showButtons && (tree->columnTree != NULL)) {
3118 	Tree_InvalidateItemDInfo(tree, tree->columnTree, parent,
3119 		NULL);
3120     }
3121 
3122     tree->updateIndex = 1;
3123     Tree_DInfoChanged(tree, DINFO_REDO_RANGES);
3124 
3125     /* Tree_UpdateItemIndex() also recalcs depth, but in one of my demos
3126      * I retrieve item depth during list creation. Since Tree_UpdateItemIndex()
3127      * is slow I will keep depth up-to-date here. */
3128     TreeItem_UpdateDepth(tree, item);
3129 
3130     TreeColumns_InvalidateWidthOfItems(tree, NULL);
3131     TreeColumns_InvalidateSpans(tree);
3132 
3133     if (tree->debug.enable && tree->debug.data)
3134 	Tree_Debug(tree);
3135 }
3136 
3137 /*
3138  *----------------------------------------------------------------------
3139  *
3140  * RemoveFromParentAux --
3141  *
3142  *	Recursively update Item.depth, Item.index and Item.indexVis.
3143  *
3144  * Results:
3145  *	None.
3146  *
3147  * Side effects:
3148  *	Display changes.
3149  *
3150  *----------------------------------------------------------------------
3151  */
3152 
3153 static void
RemoveFromParentAux(TreeCtrl * tree,TreeItem item,int * index)3154 RemoveFromParentAux(
3155     TreeCtrl *tree,		/* Widget info. */
3156     TreeItem item,		/* Item being removed. */
3157     int *index			/* New value of Item.index. Is incremented. */
3158     )
3159 {
3160     TreeItem child;
3161 
3162     /* Invalidate display info. Don't free it because we may just be
3163      * moving the item to a new parent. FIXME: if it is being moved,
3164      * it might not actually need to be redrawn (just copied) */
3165     if (item->dInfo != NULL)
3166 	Tree_InvalidateItemDInfo(tree, NULL, item, NULL);
3167 
3168     if (item->parent != NULL)
3169 	item->depth = item->parent->depth + 1;
3170     else
3171 	item->depth = 0;
3172     item->index = (*index)++;
3173     item->indexVis = -1;
3174     child = item->firstChild;
3175     while (child != NULL) {
3176 	RemoveFromParentAux(tree, child, index);
3177 	child = child->nextSibling;
3178     }
3179 }
3180 
3181 /*
3182  *----------------------------------------------------------------------
3183  *
3184  * TreeItem_RemoveFromParent --
3185  *
3186  *	Remove an Item from its parent (if any).
3187  *
3188  * Results:
3189  *	None.
3190  *
3191  * Side effects:
3192  *	Display changes.
3193  *
3194  *----------------------------------------------------------------------
3195  */
3196 
3197 void
TreeItem_RemoveFromParent(TreeCtrl * tree,TreeItem item)3198 TreeItem_RemoveFromParent(
3199     TreeCtrl *tree,		/* Widget info. */
3200     TreeItem item		/* Item token. */
3201     )
3202 {
3203     TreeItem parent = item->parent;
3204     TreeItem last;
3205     int index = 0;
3206 
3207     if (parent == NULL)
3208 	return;
3209 
3210     /* If this is the last child, redraw the lines of the previous
3211      * sibling and all of its descendants because the line from
3212      * the previous sibling to us is now gone */
3213     if ((item->prevSibling != NULL) &&
3214 	    (item->nextSibling == NULL) &&
3215 	    tree->showLines && (tree->columnTree != NULL)) {
3216 	last = item->prevSibling;
3217 	while (last->lastChild != NULL)
3218 	    last = last->lastChild;
3219 	Tree_InvalidateItemDInfo(tree, tree->columnTree,
3220 		item->prevSibling, last);
3221     }
3222 
3223     /* Redraw the parent if the parent has "-button auto". */
3224     if (IS_VISIBLE(item) && (parent->flags & ITEM_FLAG_BUTTON_AUTO) &&
3225 	    tree->showButtons && (tree->columnTree != NULL)) {
3226 	Tree_InvalidateItemDInfo(tree, tree->columnTree, parent,
3227 		NULL);
3228     }
3229 
3230     /*
3231      * Set a flag indicating that item indexes are out-of-date. This doesn't
3232      * cover the current item being removed.
3233      */
3234     tree->updateIndex = 1;
3235     Tree_DInfoChanged(tree, DINFO_REDO_RANGES);
3236 
3237     if (item->prevSibling)
3238 	item->prevSibling->nextSibling = item->nextSibling;
3239     if (item->nextSibling)
3240 	item->nextSibling->prevSibling = item->prevSibling;
3241     if (parent->firstChild == item) {
3242 	parent->firstChild = item->nextSibling;
3243 	if (!parent->firstChild)
3244 	    parent->lastChild = NULL;
3245     }
3246     if (parent->lastChild == item)
3247 	parent->lastChild = item->prevSibling;
3248     item->prevSibling = item->nextSibling = NULL;
3249     item->parent = NULL;
3250     parent->numChildren--;
3251 
3252     /*
3253      * Update Item.depth, Item.index and Item.indexVis for the item and its
3254      * descendants. An up-to-date Item.index is needed for some operations that
3255      * use a range of items, such as [item delete].
3256      */
3257     RemoveFromParentAux(tree, item, &index);
3258 }
3259 
3260 /*
3261  *----------------------------------------------------------------------
3262  *
3263  * TreeItem_RemoveColumns --
3264  *
3265  *	Free a range of Columns in an Item.
3266  *
3267  * Results:
3268  *	None.
3269  *
3270  * Side effects:
3271  *	Memory is deallocated.
3272  *
3273  *----------------------------------------------------------------------
3274  */
3275 
3276 void
TreeItem_RemoveColumns(TreeCtrl * tree,TreeItem item,int first,int last)3277 TreeItem_RemoveColumns(
3278     TreeCtrl *tree,		/* Widget info. */
3279     TreeItem item,		/* Item token. */
3280     int first,			/* 0-based column index at start of
3281 				 * the range. Must be <= last */
3282     int last			/* 0-based column index at end of
3283 				 * the range. Must be >= first */
3284     )
3285 {
3286     TreeItemColumn column = item->columns;
3287     TreeItemColumn prev = NULL, next = NULL;
3288     int i = 0;
3289 
3290     while (column != NULL) {
3291 	next = column->next;
3292 	if (i == first - 1)
3293 	    prev = column;
3294 	else if (i >= first)
3295 	    Column_FreeResources(tree, column);
3296 	if (i == last)
3297 	    break;
3298 	++i;
3299 	column = next;
3300     }
3301     if (prev != NULL)
3302 	prev->next = next;
3303     else if (first == 0)
3304 	item->columns = next;
3305 }
3306 
3307 /*
3308  *----------------------------------------------------------------------
3309  *
3310  * TreeItem_RemoveAllColumns --
3311  *
3312  *	Free all the Columns in an Item.
3313  *
3314  * Results:
3315  *	None.
3316  *
3317  * Side effects:
3318  *	Memory is deallocated.
3319  *
3320  *----------------------------------------------------------------------
3321  */
3322 
3323 void
TreeItem_RemoveAllColumns(TreeCtrl * tree,TreeItem item)3324 TreeItem_RemoveAllColumns(
3325     TreeCtrl *tree,		/* Widget info. */
3326     TreeItem item		/* Item token. */
3327     )
3328 {
3329     TreeItemColumn column = item->columns;
3330 
3331     while (column != NULL) {
3332 	TreeItemColumn next = column->next;
3333 	/* Don't delete the tail item-column in header items. */
3334 	if (item->header != NULL && next == NULL) {
3335 	    item->columns = column;
3336 	    return;
3337 	}
3338 	Column_FreeResources(tree, column);
3339 	column = next;
3340     }
3341     item->columns = NULL;
3342 }
3343 
3344 /*
3345  *----------------------------------------------------------------------
3346  *
3347  * Item_CreateColumn --
3348  *
3349  *	Allocate a Column record for an Item if it doesn't already
3350  *	exist.
3351  *
3352  * Results:
3353  *	Pointer to new or existing Column record.
3354  *
3355  * Side effects:
3356  *	Any column records preceding the desired one are allocated
3357  *	if they weren't already. Memory is allocated.
3358  *
3359  *----------------------------------------------------------------------
3360  */
3361 
3362 static TreeItemColumn
Item_CreateColumn(TreeCtrl * tree,TreeItem item,int columnIndex,int * isNew)3363 Item_CreateColumn(
3364     TreeCtrl *tree,		/* Widget info. */
3365     TreeItem item,		/* Item to contain the column. */
3366     int columnIndex,		/* 0-based index of new column. */
3367     int *isNew			/* May be NULL. Set to TRUE if the
3368 				 * column record was created. */
3369     )
3370 {
3371     TreeItemColumn column;
3372     int i;
3373 
3374 #ifdef TREECTRL_DEBUG
3375     if (columnIndex < 0 || columnIndex >= tree->columnCount + (item->header ? 1 : 0)) {
3376 	panic("Item_CreateColumn with index %d, must be from 0-%d", columnIndex, tree->columnCount + (item->header ? 1 : 0) - 1);
3377     }
3378 #endif
3379 
3380     if (isNew != NULL) (*isNew) = FALSE;
3381     column = item->columns;
3382     if (column == NULL) {
3383 	column = Column_Alloc(tree, item);
3384 	item->columns = column;
3385 	if (isNew != NULL) (*isNew) = TRUE;
3386     }
3387     for (i = 0; i < columnIndex; i++) {
3388 	if (column->next == NULL) {
3389 	    column->next = Column_Alloc(tree, item);
3390 	    if (isNew != NULL) (*isNew) = TRUE;
3391 	}
3392 	column = column->next;
3393     }
3394 
3395 /* If creating a new -lock=none column then Column_Move does nothing */
3396 if (item->header != NULL && columnIndex == TreeColumn_Index(tree->columnTail) + 1) {
3397     TreeItem_MoveColumn(tree, item, columnIndex, columnIndex - 1);
3398 }
3399 
3400     return column;
3401 }
3402 
3403 /*
3404  *----------------------------------------------------------------------
3405  *
3406  * TreeItem_MoveColumn --
3407  *
3408  *	Rearranges an Item's list of Column records by moving one
3409  *	in front of another.
3410  *
3411  * Results:
3412  *	If the Column to be moved does not exist and the Column to place it
3413  *	in front of does not exist, then nothing happens. If the Column is
3414  *	to be moved past all currently allocated Columns, then new
3415  *	Column records are allocated.
3416  *
3417  * Side effects:
3418  *	Memory may be allocated.
3419  *
3420  *----------------------------------------------------------------------
3421  */
3422 
3423 void
TreeItem_MoveColumn(TreeCtrl * tree,TreeItem item,int columnIndex,int beforeIndex)3424 TreeItem_MoveColumn(
3425     TreeCtrl *tree,		/* Widget info. */
3426     TreeItem item,		/* Item token. */
3427     int columnIndex,		/* 0-based index of the column to move. */
3428     int beforeIndex		/* 0-based index of the second column to move
3429 				 * the first column to the left of. */
3430     )
3431 {
3432     TreeItemColumn before = NULL, move = NULL;
3433     TreeItemColumn prevM = NULL, prevB = NULL;
3434     TreeItemColumn last = NULL, prev, walk;
3435     int index = 0;
3436 
3437     prev = NULL;
3438     walk = item->columns;
3439     while (walk != NULL) {
3440 	if (index == columnIndex) {
3441 	    prevM = prev;
3442 	    move = walk;
3443 	}
3444 	if (index == beforeIndex) {
3445 	    prevB = prev;
3446 	    before = walk;
3447 	}
3448 	prev = walk;
3449 	if (walk->next == NULL)
3450 	    last = walk;
3451 	index++;
3452 	walk = walk->next;
3453     }
3454 
3455     if (move == NULL && before == NULL)
3456 	return;
3457     if (move == NULL)
3458 	move = Column_Alloc(tree, item);
3459     else {
3460 	if (before == NULL) {
3461 	    prevB = Item_CreateColumn(tree, item, beforeIndex - 1, NULL);
3462 	    last = prevB;
3463 	}
3464 	if (prevM == NULL)
3465 	    item->columns = move->next;
3466 	else
3467 	    prevM->next = move->next;
3468     }
3469     if (before == NULL) {
3470 	last->next = move;
3471 	move->next = NULL;
3472     } else {
3473 	if (prevB == NULL)
3474 	    item->columns = move;
3475 	else
3476 	    prevB->next = move;
3477 	move->next = before;
3478     }
3479 }
3480 
3481 /*
3482  *----------------------------------------------------------------------
3483  *
3484  * TreeItem_FreeResources --
3485  *
3486  *	Free memory etc assocated with an Item.
3487  *
3488  * Results:
3489  *	None.
3490  *
3491  * Side effects:
3492  *	Memory is deallocated. Display changes.
3493  *
3494  *----------------------------------------------------------------------
3495  */
3496 
3497 void
TreeItem_FreeResources(TreeCtrl * tree,TreeItem item)3498 TreeItem_FreeResources(
3499     TreeCtrl *tree,		/* Widget info. */
3500     TreeItem item		/* Item token. */
3501     )
3502 {
3503     TreeItemColumn column;
3504 
3505     column = item->columns;
3506     while (column != NULL)
3507 	column = Column_FreeResources(tree, column);
3508     if (item->dInfo != NULL)
3509 	Tree_FreeItemDInfo(tree, item, NULL);
3510     if (item->rInfo != NULL)
3511 	Tree_FreeItemRInfo(tree, item);
3512     if (item->spans != NULL)
3513 	ckfree((char *) item->spans);
3514     if (item->header != NULL)
3515 	TreeHeader_FreeResources(item->header);
3516     Tk_FreeConfigOptions((char *) item, tree->itemOptionTable, tree->tkwin);
3517 
3518     /* Add the item record to the "preserved" list. It will be freed later. */
3519     TreeItemList_Append(&tree->preserveItemList, item);
3520 }
3521 
3522 /*
3523  *----------------------------------------------------------------------
3524  *
3525  * TreeItem_Release --
3526  *
3527  *	Finally free an item record when it is no longer needed.
3528  *
3529  * Results:
3530  *	None.
3531  *
3532  * Side effects:
3533  *	Memory is deallocated.
3534  *
3535  *----------------------------------------------------------------------
3536  */
3537 
3538 void
TreeItem_Release(TreeCtrl * tree,TreeItem item)3539 TreeItem_Release(
3540     TreeCtrl *tree,		/* Widget info. */
3541     TreeItem item		/* Item token. */
3542     )
3543 {
3544 #ifdef ALLOC_HAX
3545     TreeAlloc_Free(tree->allocData, ItemUid, (char *) item, sizeof(TreeItem_));
3546 #else
3547     WFREE(item, TreeItem_);
3548 #endif
3549 }
3550 
3551 /*
3552  *----------------------------------------------------------------------
3553  *
3554  * Item_HeightOfStyles --
3555  *
3556  *	Return the height used by styles in an Item.
3557  *
3558  * Results:
3559  *	Maximum height in pixels of the style in each visible Column.
3560  *
3561  * Side effects:
3562  *	None.
3563  *
3564  *----------------------------------------------------------------------
3565  */
3566 
3567 static int
Item_HeightOfStyles(TreeCtrl * tree,TreeItem item)3568 Item_HeightOfStyles(
3569     TreeCtrl *tree,		/* Widget info. */
3570     TreeItem item		/* Item record. */
3571     )
3572 {
3573     TreeItemColumn column = item->columns;
3574     int *spans = TreeItem_GetSpans(tree, item);
3575     int tailOK = item->header != NULL;
3576     TreeColumn treeColumn = Tree_FirstColumn(tree, -1, tailOK);
3577     StyleDrawArgs drawArgs;
3578     int height = 0, hasHeaderElem = FALSE;
3579 
3580     drawArgs.tree = tree;
3581 
3582     if (spans == NULL) {
3583 	while (column != NULL) {
3584 	    if (TreeColumn_Visible(treeColumn) && (column->style != NULL)) {
3585 		drawArgs.state = item->state | column->cstate;
3586 		drawArgs.style = column->style;
3587 		drawArgs.indent = TreeItem_Indent(tree, treeColumn, item);
3588 		if (treeColumn == tree->columnTail) {
3589 		    drawArgs.width = -1; /* as much width as the style needs */
3590 		} else {
3591 		    drawArgs.width = TreeColumn_UseWidth(treeColumn);
3592 		    if (item->header != NULL)
3593 			drawArgs.width += drawArgs.indent;
3594 		}
3595 		height = MAX(height, TreeStyle_UseHeight(&drawArgs));
3596 		if (!hasHeaderElem && (item->header != NULL) &&
3597 			TreeStyle_HasHeaderElement(tree, column->style))
3598 		    hasHeaderElem = TRUE;
3599 	    }
3600 	    treeColumn = Tree_ColumnToTheRight(treeColumn, FALSE, tailOK);
3601 	    column = column->next;
3602 	}
3603     } else {
3604 	while (column != NULL) {
3605 	    if (TreeColumn_Visible(treeColumn)) {
3606 		int columnIndex = TreeColumn_Index(treeColumn);
3607 		int columnIndex2 = columnIndex;
3608 		TreeColumn treeColumn2 = treeColumn;
3609 		drawArgs.width = 0;
3610 #if defined(TREECTRL_DEBUG)
3611 		if (TreeColumn_Index(treeColumn) != columnIndex) BreakIntoDebugger();
3612 		if (TreeItemColumn_Index(tree, item, column) != columnIndex) BreakIntoDebugger();
3613 		if (spans[columnIndex] != columnIndex) BreakIntoDebugger();
3614 #endif
3615 		while (spans[columnIndex2] == columnIndex) {
3616 		    if (!TreeColumn_Visible(treeColumn2)) {
3617 			/* nothing */
3618 		    } else if (treeColumn2 == tree->columnTail) {
3619 			drawArgs.width = -1; /* as much width as the style needs */
3620 		    } else {
3621 			drawArgs.width += TreeColumn_UseWidth(treeColumn2);
3622 		    }
3623 		    treeColumn2 = Tree_ColumnToTheRight(treeColumn2, FALSE, tailOK);
3624 		    if (treeColumn2 == NULL)
3625 			break;
3626 		    columnIndex2++;
3627 		}
3628 		if (column->style != NULL) {
3629 		    drawArgs.indent = TreeItem_Indent(tree, treeColumn, item);
3630 		    if (item->header != NULL)
3631 			drawArgs.width += drawArgs.indent;
3632 		    drawArgs.state = item->state | column->cstate;
3633 		    drawArgs.style = column->style;
3634 		    height = MAX(height, TreeStyle_UseHeight(&drawArgs));
3635 		    if (!hasHeaderElem && (item->header != NULL) &&
3636 			    TreeStyle_HasHeaderElement(tree, column->style))
3637 			hasHeaderElem = TRUE;
3638 		}
3639 		treeColumn = treeColumn2;
3640 		if (treeColumn == NULL)
3641 		    break;
3642 		while ((column != NULL) && (columnIndex < columnIndex2)) {
3643 		    column = column->next;
3644 		    columnIndex++;
3645 		}
3646 		continue;
3647 	    }
3648 	    treeColumn = Tree_ColumnToTheRight(treeColumn, FALSE, tailOK);
3649 	    column = column->next;
3650 	}
3651     }
3652 
3653     /* List headers are a fixed height on Aqua. */
3654     /* FIXME: all that work above just to ignore the result here! */
3655     if (hasHeaderElem && tree->useTheme) {
3656 	if (tree->themeHeaderHeight > 0)
3657 	    return tree->themeHeaderHeight;
3658     }
3659 
3660     return height;
3661 }
3662 
3663 /*
3664  *----------------------------------------------------------------------
3665  *
3666  * TreeItem_Height --
3667  *
3668  *	Return the height of an Item.
3669  *
3670  * Results:
3671  *	If the Item -height option is > 0, the result is the maximum
3672  *	of the button height (if a button is displayed) and the -height
3673  *	option.
3674  *	If the TreeCtrl -itemheight option is > 0, the result is the maximum
3675  *	of the button height (if a button is displayed) and the -itemheight
3676  *	option.
3677  *	Otherwise the result is the maximum of the button height (if a button
3678  *	is displayed) AND the TreeCtrl -minitemheight AND the height of
3679  *	the style in each visible Column.
3680  *
3681  * Side effects:
3682  *	None.
3683  *
3684  *----------------------------------------------------------------------
3685  */
3686 
3687 int
TreeItem_Height(TreeCtrl * tree,TreeItem item)3688 TreeItem_Height(
3689     TreeCtrl *tree,		/* Widget info. */
3690     TreeItem item		/* Item token. */
3691     )
3692 {
3693     int buttonHeight = 0;
3694     int useHeight;
3695 
3696     if (!TreeItem_ReallyVisible(tree, item))
3697 	return 0;
3698 
3699     if (item->header != NULL) {
3700 	if (item->fixedHeight > 0)
3701 	    return item->fixedHeight;
3702 	return Item_HeightOfStyles(tree, item);
3703     }
3704 
3705     /* Get requested height of the style in each column */
3706     useHeight = Item_HeightOfStyles(tree, item);
3707 
3708     /* Can't have less height than our button */
3709     if (TreeItem_HasButton(tree, item)) {
3710 	buttonHeight = Tree_ButtonHeight(tree, item->state);
3711     }
3712 
3713     /* User specified a fixed height for this item */
3714     if (item->fixedHeight > 0)
3715 	return MAX(item->fixedHeight, buttonHeight);
3716 
3717     /* Fixed height of all items */
3718     if (tree->itemHeight > 0)
3719 	return MAX(tree->itemHeight, buttonHeight);
3720 
3721     /* Minimum height of all items */
3722     if (tree->minItemHeight > 0)
3723 	useHeight = MAX(useHeight, tree->minItemHeight);
3724 
3725     /* No fixed height specified */
3726     return MAX(useHeight, buttonHeight);
3727 }
3728 
3729 /*
3730  *----------------------------------------------------------------------
3731  *
3732  * TreeItem_InvalidateHeight --
3733  *
3734  *	Marks Item.neededHeight out-of-date.
3735  *	NOTE: Item.neededHeight is unused.
3736  *
3737  * Results:
3738  *	None.
3739  *
3740  * Side effects:
3741  *	None.
3742  *
3743  *----------------------------------------------------------------------
3744  */
3745 
3746 void
TreeItem_InvalidateHeight(TreeCtrl * tree,TreeItem item)3747 TreeItem_InvalidateHeight(
3748     TreeCtrl *tree,		/* Widget info. */
3749     TreeItem item		/* Item token. */
3750     )
3751 {
3752 }
3753 
3754 /*
3755  *----------------------------------------------------------------------
3756  *
3757  * TreeItem_FindColumn --
3758  *
3759  *	Return an item-column token given a zero-based index.
3760  *
3761  * Results:
3762  *	The item-column token or NULL.
3763  *
3764  * Side effects:
3765  *	None.
3766  *
3767  *----------------------------------------------------------------------
3768  */
3769 
3770 TreeItemColumn
TreeItem_FindColumn(TreeCtrl * tree,TreeItem item,int columnIndex)3771 TreeItem_FindColumn(
3772     TreeCtrl *tree,		/* Widget info. */
3773     TreeItem item,		/* Item token. */
3774     int columnIndex		/* 0-based index of column to find. */
3775     )
3776 {
3777     TreeItemColumn column;
3778     int i = 0;
3779 
3780     column = item->columns;
3781     if (!column)
3782 	return NULL;
3783     while (column != NULL && i < columnIndex) {
3784 	column = column->next;
3785 	i++;
3786     }
3787     return column;
3788 }
3789 
3790 /*
3791  *----------------------------------------------------------------------
3792  *
3793  * TreeItem_ColumnFromObj --
3794  *
3795  *	Return an item-column token given a Tcl_Obj column description.
3796  *
3797  * Results:
3798  *	TCL_OK or TCL_ERROR.
3799  *
3800  * Side effects:
3801  *	None.
3802  *
3803  *----------------------------------------------------------------------
3804  */
3805 
3806 int
TreeItem_ColumnFromObj(TreeCtrl * tree,TreeItem item,Tcl_Obj * obj,TreeItemColumn * columnPtr,TreeColumn * treeColumnPtr,int * indexPtr,int flags)3807 TreeItem_ColumnFromObj(
3808     TreeCtrl *tree,		/* Widget info. */
3809     TreeItem item,		/* Item token. */
3810     Tcl_Obj *obj,		/* Column description. */
3811     TreeItemColumn *columnPtr,	/* Returned column, or NULL. */
3812     TreeColumn *treeColumnPtr,	/* May be NULL. Returned tree column,
3813 				 * or NULL. */
3814     int *indexPtr,		/* May be NULL. Returned 0-based index of
3815 				 * the column. */
3816     int flags			/* CFO_XXX flags. */
3817     )
3818 {
3819     TreeColumn treeColumn;
3820     int columnIndex;
3821 
3822     if (TreeColumn_FromObj(tree, obj, &treeColumn, flags) != TCL_OK)
3823 	return TCL_ERROR;
3824     columnIndex = TreeColumn_Index(treeColumn);
3825     (*columnPtr) = TreeItem_FindColumn(tree, item, columnIndex);
3826     if (treeColumnPtr != NULL)
3827 	(*treeColumnPtr) = treeColumn;
3828     if (indexPtr != NULL)
3829 	(*indexPtr) = columnIndex;
3830     return TCL_OK;
3831 }
3832 
3833 /*
3834  *----------------------------------------------------------------------
3835  *
3836  * TreeItem_Indent --
3837  *
3838  *	Return the amount of indentation for the given item. This is
3839  *	the width of the buttons/lines.
3840  *
3841  * Results:
3842  *	Pixel value >= 0.
3843  *
3844  * Side effects:
3845  *	None.
3846  *
3847  *----------------------------------------------------------------------
3848  */
3849 
3850 int
TreeItem_Indent(TreeCtrl * tree,TreeColumn treeColumn,TreeItem item)3851 TreeItem_Indent(
3852     TreeCtrl *tree,		/* Widget info. */
3853     TreeColumn treeColumn,	/* Which column. */
3854     TreeItem item		/* Item token. */
3855     )
3856 {
3857     int depth;
3858 
3859     if (item->header != NULL) {
3860 	if ((TreeColumn_Lock(treeColumn) == COLUMN_LOCK_NONE) &&
3861 		(TreeColumn_VisIndex(treeColumn) == 0)) {
3862 	    return tree->canvasPadX[PAD_TOP_LEFT];
3863 	}
3864 	return 0;
3865     }
3866 
3867     if (treeColumn != tree->columnTree)
3868 	return 0;
3869 
3870     if (IS_ROOT(item))
3871 	return (tree->showRoot && tree->showButtons && tree->showRootButton)
3872 	    ? tree->useIndent : 0;
3873 
3874     Tree_UpdateItemIndex(tree);
3875 
3876     depth = item->depth;
3877     if (tree->showRoot)
3878     {
3879 	depth += 1;
3880 	if (tree->showButtons && tree->showRootButton)
3881 	    depth += 1;
3882     }
3883     else if (tree->showButtons && tree->showRootChildButtons)
3884 	depth += 1;
3885     else if (tree->showLines && tree->showRootLines)
3886 	depth += 1;
3887 
3888     return tree->useIndent * depth;
3889 }
3890 
3891 /*
3892  *----------------------------------------------------------------------
3893  *
3894  * ItemDrawBackground --
3895  *
3896  *	Draws part of the background area of an Item. The area is
3897  *	erased to the -itembackground color of the tree column or the
3898  *	TreeCtrl -background color. If the TreeCtrl -backgroundimage
3899  *	option is specified then that image is tiled over the given area.
3900  *
3901  * Results:
3902  *	None.
3903  *
3904  * Side effects:
3905  *	Stuff is drawn in a drawable.
3906  *
3907  *----------------------------------------------------------------------
3908  */
3909 
3910 static void
ItemDrawBackground(TreeCtrl * tree,TreeColumn treeColumn,TreeItem item,TreeItemColumn column,TreeDrawable td,int x,int y,int width,int height,int index)3911 ItemDrawBackground(
3912     TreeCtrl *tree,		/* Widget info. */
3913     TreeColumn treeColumn,	/* Tree-column token. */
3914     TreeItem item,		/* Item record. */
3915     TreeItemColumn column,	/* First column. */
3916     TreeDrawable td,		/* Where to draw. */
3917     int x, int y,		/* Area of the item to draw. */
3918     int width, int height,	/* ^ */
3919     int index			/* Used to select a color from the
3920 				 * tree-column's -itembackground option. */
3921     )
3922 {
3923     TreeColor *tc;
3924     TreeClip clip, *clipPtr = &clip;
3925     TreeRectangle tr;
3926 
3927 #if USE_ITEM_PIXMAP == 0
3928     clip.type = TREE_CLIP_AREA;
3929     switch (TreeColumn_Lock(treeColumn)) {
3930 	case COLUMN_LOCK_LEFT:
3931 	    clip.area = TREE_AREA_LEFT;
3932 	    break;
3933 	case COLUMN_LOCK_NONE:
3934 	    clip.area = TREE_AREA_CONTENT;
3935 	    break;
3936 	case COLUMN_LOCK_RIGHT:
3937 	    clip.area = TREE_AREA_RIGHT;
3938 	    break;
3939     }
3940 #else
3941     clipPtr = NULL;
3942 #endif
3943 
3944     TreeRect_SetXYWH(tr, x, y, width, height);
3945 
3946     /*
3947      * If the -backgroundimage is being drawn and is opaque,
3948      * there is no need to erase first (unless it doesn't tile!).
3949      */
3950     if (!Tree_IsBgImageOpaque(tree)) {
3951 	tc = TreeColumn_BackgroundColor(treeColumn, index);
3952 	if (tc != NULL) {
3953 	    TreeRectangle trBrush;
3954 	    TreeColor_GetBrushBounds(tree, tc, tr,
3955 		    tree->drawableXOrigin, tree->drawableYOrigin,
3956 		    treeColumn, item, &trBrush);
3957 	    if (!TreeColor_IsOpaque(tree, tc)
3958 		    || (trBrush.width <= 0) || (trBrush.height <= 0)) {
3959 		GC gc = Tk_3DBorderGC(tree->tkwin, tree->border, TK_3D_FLAT_GC);
3960 		Tree_FillRectangle(tree, td, clipPtr, gc, tr);
3961 	    }
3962 	    TreeColor_FillRect(tree, td, clipPtr, tc, trBrush, tr);
3963 	} else {
3964 	    GC gc = Tk_3DBorderGC(tree->tkwin, tree->border, TK_3D_FLAT_GC);
3965 	    Tree_FillRectangle(tree, td, clipPtr, gc, tr);
3966 	}
3967     }
3968     if (tree->backgroundImage != NULL) {
3969 	Tree_DrawBgImage(tree, td, tr, tree->drawableXOrigin,
3970 		tree->drawableYOrigin);
3971     }
3972 }
3973 
3974 /*
3975  *----------------------------------------------------------------------
3976  *
3977  * TreeItem_SpansInvalidate --
3978  *
3979  *	Invalidates the Item.spans field of one or all items.
3980  *
3981  * Results:
3982  *	The item(s) are removed from the TreeCtrl.itemSpansHash to
3983  *	indicate that the list of spans must be recalculated.
3984  *
3985  * Side effects:
3986  *	None.
3987  *
3988  *----------------------------------------------------------------------
3989  */
3990 
3991 void
TreeItem_SpansInvalidate(TreeCtrl * tree,TreeItem item)3992 TreeItem_SpansInvalidate(
3993     TreeCtrl *tree,		/* Widget info. */
3994     TreeItem item		/* Item token. NULL for all items. */
3995     )
3996 {
3997     Tcl_HashEntry *hPtr;
3998     Tcl_HashSearch search;
3999     int count = 0;
4000 
4001     if (item == NULL) {
4002 	hPtr = Tcl_FirstHashEntry(&tree->itemSpansHash, &search);
4003 	while (hPtr != NULL) {
4004 	    item = (TreeItem) Tcl_GetHashKey(&tree->itemSpansHash, hPtr);
4005 	    item->flags &= ~ITEM_FLAG_SPANS_VALID;
4006 	    count++;
4007 	    hPtr = Tcl_NextHashEntry(&search);
4008 	}
4009 	if (count) {
4010 	    Tcl_DeleteHashTable(&tree->itemSpansHash);
4011 	    Tcl_InitHashTable(&tree->itemSpansHash, TCL_ONE_WORD_KEYS);
4012 	}
4013     } else if (item->flags & ITEM_FLAG_SPANS_VALID) {
4014 	hPtr = Tcl_FindHashEntry(&tree->itemSpansHash, (char *) item);
4015 	Tcl_DeleteHashEntry(hPtr);
4016 	item->flags &= ~ITEM_FLAG_SPANS_VALID;
4017 	count++;
4018     }
4019 
4020     if (count && tree->debug.enable && tree->debug.span)
4021 	dbwin("TreeItem_SpansInvalidate forgot %d items\n", count);
4022 
4023     TreeColumns_InvalidateSpans(tree); /* FIXME: only if item visible? */
4024 }
4025 
4026 /*
4027  *----------------------------------------------------------------------
4028  *
4029  * TreeItem_SpansRedo --
4030  *
4031  *	Updates the Item.spans field of an item.
4032  *
4033  * Results:
4034  *	Item.spans is resized if needed to (at least) the current number
4035  *	of tree columns. For tree column N, the index of the item
4036  *	column displayed there is written to spans[N].
4037  *
4038  *	The return value is 1 if every span is 1, otherwise 0.
4039  *
4040  * Side effects:
4041  *	Memory may be allocated.
4042  *
4043  *----------------------------------------------------------------------
4044  */
4045 
4046 int
TreeItem_SpansRedo(TreeCtrl * tree,TreeItem item)4047 TreeItem_SpansRedo(
4048     TreeCtrl *tree,		/* Widget info. */
4049     TreeItem item		/* Item token. */
4050     )
4051 {
4052     TreeColumn treeColumn = tree->columns;
4053     TreeItemColumn itemColumn = item->columns;
4054     int columnCount = tree->columnCount + (item->header ? 1 : 0);
4055     int columnIndex = 0, spanner = 0, span = 1, simple = TRUE;
4056     int lock = TreeColumn_Lock(treeColumn);
4057 
4058     if (tree->debug.enable && tree->debug.span)
4059 	dbwin("TreeItem_SpansRedo %s %d\n", item->header ? "header" : "item",
4060 	    item->id);
4061 
4062     if (item->spans == NULL) {
4063 	item->spans = (int *) ckalloc(sizeof(int) * columnCount);
4064 	item->spanAlloc = columnCount;
4065     } else if (item->spanAlloc < columnCount) {
4066 	item->spans = (int *) ckrealloc((char *) item->spans,
4067 		sizeof(int) * columnCount);
4068 	item->spanAlloc = columnCount;
4069     }
4070 
4071     while (treeColumn != NULL) {
4072 	/* End current span if column lock changes. */
4073 	if (TreeColumn_Lock(treeColumn) != lock) {
4074 	    lock = TreeColumn_Lock(treeColumn);
4075 	    span = 1;
4076 	}
4077 	if (--span == 0) {
4078 	    if (TreeColumn_Visible(treeColumn))
4079 		span = itemColumn ? itemColumn->span : 1;
4080 	    else
4081 		span = 1;
4082 	    spanner = columnIndex;
4083 	}
4084 	if ((itemColumn != NULL) && (itemColumn->span > 1))
4085 	    simple = FALSE;
4086 	item->spans[columnIndex] = spanner;
4087 	columnIndex++;
4088 	treeColumn = TreeColumn_Next(treeColumn);
4089 	if (itemColumn != NULL)
4090 	    itemColumn = itemColumn->next;
4091     }
4092 
4093     /* Add a span of 1 for the tail column if this is a header. */
4094     if (item->header != NULL) {
4095 	item->spans[columnCount - 1] = columnCount - 1; /* tail column */
4096     }
4097 
4098     return simple;
4099 }
4100 
4101 /*
4102  *----------------------------------------------------------------------
4103  *
4104  * TreeItem_SpansRedoIfNeeded --
4105  *
4106  *	Updates the Item.spans field of an item if needed.
4107  *
4108  * Results:
4109  *	If all spans are known to be 1, nothing is done. If the list of
4110  *	spans is marked valid, nothing is done. Otherwise the list of
4111  *	spans is recalculated; if any span is > 1 the item is added
4112  *	to TreeCtrl.itemSpansHash.
4113  *
4114  * Side effects:
4115  *	Memory may be allocated.
4116  *
4117  *----------------------------------------------------------------------
4118  */
4119 
4120 void
TreeItem_SpansRedoIfNeeded(TreeCtrl * tree,TreeItem item)4121 TreeItem_SpansRedoIfNeeded(
4122     TreeCtrl *tree,
4123     TreeItem item
4124     )
4125 {
4126     /* All the spans are 1. */
4127     if (item->flags & ITEM_FLAG_SPANS_SIMPLE)
4128 	return;
4129 
4130     /* Some spans > 1, but we calculated them already. */
4131     if (item->flags & ITEM_FLAG_SPANS_VALID)
4132 	return;
4133 
4134     if (TreeItem_SpansRedo(tree, item)) {
4135 	/* Reverted to all spans=1. */
4136 	item->flags |= ITEM_FLAG_SPANS_SIMPLE;
4137     } else {
4138 	int isNew;
4139 	Tcl_HashEntry *hPtr;
4140 
4141 	hPtr = Tcl_CreateHashEntry(&tree->itemSpansHash, (char *) item, &isNew);
4142 	Tcl_SetHashValue(hPtr, (ClientData) item);
4143 	item->flags |= ITEM_FLAG_SPANS_VALID;
4144     }
4145 }
4146 
4147 /*
4148  *----------------------------------------------------------------------
4149  *
4150  * TreeItem_GetSpans --
4151  *
4152  *	Returns the spans[] array for an item.
4153  *
4154  * Results:
4155  *	If all spans are known to be 1, the result is NULL. Otherwise the
4156  *	list of spans is returned.
4157  *
4158  * Side effects:
4159  *	Memory may be allocated.
4160  *
4161  *----------------------------------------------------------------------
4162  */
4163 
4164 int *
TreeItem_GetSpans(TreeCtrl * tree,TreeItem item)4165 TreeItem_GetSpans(
4166     TreeCtrl *tree,		/* Widget info. */
4167     TreeItem item		/* Item token. */
4168     )
4169 {
4170     TreeItem_SpansRedoIfNeeded(tree, item);
4171     if (item->flags & ITEM_FLAG_SPANS_SIMPLE)
4172 	return NULL;
4173     return item->spans;
4174 }
4175 
4176 /*
4177  * The following structure holds information about which item column
4178  * is displayed at a given tree column.
4179  */
4180 typedef struct SpanInfo {
4181     TreeColumn treeColumn;	/* Always non-null. */
4182     TreeItemColumn itemColumn;	/* May be null. */
4183     int span;			/* Number of tree-columns spanned. */
4184     int width;			/* Width of the span. */
4185     int visIndex;		/* 0-based index into list of
4186 				 * spans.  Used by header items. */
4187     int isDragColumn;		/* TRUE if this span is for a column header
4188 				 * that is being dragged. */
4189 } SpanInfo;
4190 
4191 typedef struct ColumnColumn {
4192     TreeColumn treeColumn;
4193     TreeItemColumn itemColumn;
4194     int isDragColumn;
4195 } ColumnColumn;
4196 
4197 typedef struct SpanInfoStack SpanInfoStack;
4198 struct SpanInfoStack
4199 {
4200     int spanCount;
4201     SpanInfo *spans;
4202     int columnCount;
4203     ColumnColumn *columns;
4204     int inUse;
4205     SpanInfoStack *next;
4206 };
4207 
4208 /*
4209  *----------------------------------------------------------------------
4210  *
4211  * Item_GetSpans --
4212  *
4213  *	Fills an array of SpanInfo records, one per visible span.
4214  *
4215  * Results:
4216  *	The return value is the number of SpanInfo records written.
4217  *
4218  * Side effects:
4219  *	None.
4220  *
4221  *----------------------------------------------------------------------
4222  */
4223 
4224 static int
Item_GetSpans(TreeCtrl * tree,TreeItem item,TreeColumn firstColumn,TreeColumn lastColumn,int columnCount,SpanInfo spans[],int dragPosition)4225 Item_GetSpans(
4226     TreeCtrl *tree,		/* Widget info. */
4227     TreeItem item,		/* Item token. */
4228     TreeColumn firstColumn,	/* Which columns. */
4229     TreeColumn lastColumn,	/* Which columns. */
4230     int columnCount,
4231     SpanInfo spans[],		/* Returned span records. */
4232 #define WALKSPAN_IGNORE_DND 0	     /* Ignore header drag-and-drop positions. */
4233 #define WALKSPAN_ONLY_DRAGGED 0x01   /* Calculate the bounds of dragged headers
4234 				      * only considering -imageoffset. */
4235 #define WALKSPAN_DRAG_ORDER 0x02     /* Calculate the bounds of all headers in
4236 				      * their current drag positions. */
4237 #define WALKSPAN_IGNORE_DRAGGED 0x04 /* Don't call the callback routine for
4238                                       * dragged headers. */
4239     int dragPosition
4240     )
4241 {
4242     /* Note: getting head of the stack, that's ok since this routine isn't
4243      * in danger of being called recursively. */
4244     SpanInfoStack *siStack = tree->itemSpanPriv;
4245     ColumnColumn *columns = siStack->columns;
4246     TreeColumn treeColumn = firstColumn;
4247     int columnIndex = TreeColumn_Index(firstColumn);
4248     TreeItemColumn column = TreeItem_FindColumn(tree, item, columnIndex);
4249     int spanCount = 0, span = 1;
4250     SpanInfo *spanPtr = NULL;
4251     int i, isDragColumn;
4252 
4253     if ((item->header == NULL) && (dragPosition & WALKSPAN_ONLY_DRAGGED))
4254 	return 0;
4255 
4256     if ((columns == NULL) || (siStack->columnCount < tree->columnCount + 1)) {
4257 	columns = (ColumnColumn *) ckrealloc((char *) columns,
4258 	    sizeof(ColumnColumn) * (tree->columnCount + 1));
4259 	siStack->columnCount = tree->columnCount + 1;
4260 	siStack->columns = columns;
4261     }
4262 
4263 #ifdef TREECTRL_DEBUG
4264     for (i = 0; i < tree->columnCount + 1; i++) {
4265 	columns[i].treeColumn = NULL;
4266 	columns[i].itemColumn = NULL;
4267     }
4268 #endif
4269 
4270     columnCount = 0;
4271     while (treeColumn != NULL) {
4272 	if (TreeColumn_Lock(treeColumn) != TreeColumn_Lock(firstColumn))
4273 	    break;
4274 	columnIndex = columnCount;
4275 	isDragColumn = 0;
4276 	if ((item->header != NULL) && (dragPosition != WALKSPAN_IGNORE_DND)) {
4277 	    if (dragPosition & WALKSPAN_DRAG_ORDER) {
4278 		isDragColumn = TreeHeader_IsDraggedColumn(item->header,
4279 		    treeColumn);
4280 		columnIndex = TreeHeader_ColumnDragOrder(item->header,
4281 		    treeColumn, columnIndex);
4282 	    }
4283 	    if (dragPosition & WALKSPAN_ONLY_DRAGGED)
4284 		isDragColumn = 1;
4285 	}
4286 #ifdef TREECTRL_DEBUG
4287 	if (columnIndex < 0 || columnIndex >= siStack->columnCount) panic("Item_GetSpans columnIndex %d columnCount %d", columnIndex, siStack->columnCount);
4288 #endif
4289 	columns[columnIndex].treeColumn = treeColumn;
4290 	columns[columnIndex].itemColumn = column;
4291 	columns[columnIndex].isDragColumn = isDragColumn;
4292 	columnCount++;
4293 	if (treeColumn == lastColumn) /* FIXME: lastColumn is usually NULL */
4294 	    break;
4295 	treeColumn = Tree_ColumnToTheRight(treeColumn, TRUE,
4296 	    item->header != NULL);
4297 	if (column != NULL)
4298 	    column = column->next;
4299 	if (treeColumn == tree->columnTail) {
4300 	    while (column != NULL && column->next != NULL)
4301 		column = column->next;
4302 	}
4303     }
4304 
4305     isDragColumn = columns[0].isDragColumn;
4306     for (i = 0; i < columnCount; i++) {
4307 	treeColumn = columns[i].treeColumn;
4308 	column = columns[i].itemColumn;
4309 	if (isDragColumn != columns[i].isDragColumn)
4310 	    span = 1;
4311 	isDragColumn = columns[i].isDragColumn;
4312 	if (treeColumn == tree->columnTail) {
4313 	    span = 1; /* End the current span if it hits the tail. */
4314 	}
4315 	if (--span == 0) {
4316 	    /* Always create a span for the tail column in headers */
4317 	    if ((treeColumn == tree->columnTail) || TreeColumn_Visible(treeColumn)) {
4318 		span = column ? column->span : 1;
4319 		if (spanPtr == NULL)
4320 		    spanPtr = spans;
4321 		else
4322 		    spanPtr++;
4323 		spanPtr->treeColumn = treeColumn;
4324 		spanPtr->itemColumn = column;
4325 		spanPtr->span = 0;
4326 		spanPtr->width = 0;
4327 		if (!(dragPosition & WALKSPAN_ONLY_DRAGGED) &&
4328 			(item->header != NULL) &&
4329 			(spanCount == 0) &&
4330 			(TreeColumn_Lock(treeColumn) == COLUMN_LOCK_NONE)) {
4331 		    spanPtr->width += tree->canvasPadX[PAD_TOP_LEFT];
4332 		}
4333 		spanPtr->visIndex = spanCount;
4334 		spanPtr->isDragColumn = isDragColumn;
4335 		spanCount++;
4336 	    } else {
4337 		span = 1;
4338 		continue;
4339 	    }
4340 	}
4341 	spanPtr->span++;
4342 	if (treeColumn == tree->columnTail) {
4343 	    spanPtr->width = 100; /* TreeItem_WalkSpans will calculate the correct value */
4344 	} else {
4345 	    spanPtr->width += TreeColumn_UseWidth(treeColumn);
4346 	}
4347     }
4348 
4349     return spanCount;
4350 }
4351 
4352 typedef int (*TreeItemWalkSpansProc)(
4353     TreeCtrl *tree,
4354     TreeItem item,
4355     SpanInfo *spanPtr,
4356     StyleDrawArgs *drawArgs,
4357     ClientData clientData
4358     );
4359 
4360 /*
4361  *----------------------------------------------------------------------
4362  *
4363  * TreeItem_WalkSpans --
4364  *
4365  *	Iterates over the spans of an item and calls a callback routine
4366  *	for each span of non-zero width. This is used for drawing,
4367  *	hit-testing and other purposes.
4368  *
4369  * Results:
4370  *	None.
4371  *
4372  * Side effects:
4373  *	None.
4374  *
4375  *----------------------------------------------------------------------
4376  */
4377 
4378 static void
TreeItem_WalkSpans(TreeCtrl * tree,TreeItem item,int lock,int x,int y,int width,int height,int dragPosition,TreeItemWalkSpansProc proc,ClientData clientData)4379 TreeItem_WalkSpans(
4380     TreeCtrl *tree,		/* Widget info. */
4381     TreeItem item,		/* Item token. */
4382     int lock,			/* Which columns. */
4383     int x, int y,		/* Drawable coordinates of the item. */
4384     int width, int height,	/* Total size of the item. */
4385     int dragPosition,
4386     TreeItemWalkSpansProc proc,	/* Callback routine. */
4387     ClientData clientData	/* Data passed to callback routine. */
4388     )
4389 {
4390     SpanInfoStack *siStack = tree->itemSpanPriv;
4391     int columnWidth, totalWidth;
4392     TreeItemColumn itemColumn;
4393     StyleDrawArgs drawArgs;
4394     TreeColumn treeColumn = tree->columnLockNone, treeColumnLast = NULL;
4395     int spanCount, spanIndex, columnCount = tree->columnCountVis;
4396     SpanInfo *spans;
4397     int area = TREE_AREA_CONTENT;
4398 
4399     switch (lock) {
4400 	case COLUMN_LOCK_LEFT:
4401 	    treeColumn = tree->columnLockLeft;
4402 	    columnCount = tree->columnCountVisLeft;
4403 	    area = TREE_AREA_LEFT;
4404 	    break;
4405 	case COLUMN_LOCK_NONE:
4406 	    break;
4407 	case COLUMN_LOCK_RIGHT:
4408 	    treeColumn = tree->columnLockRight;
4409 	    columnCount = tree->columnCountVisRight;
4410 	    area = TREE_AREA_RIGHT;
4411 	    break;
4412     }
4413 
4414     if (item->header != NULL) {
4415 	switch (lock) {
4416 	    case COLUMN_LOCK_LEFT:
4417 		area = TREE_AREA_HEADER_LEFT;
4418 		break;
4419 	    case COLUMN_LOCK_NONE:
4420 		area = TREE_AREA_HEADER_NONE;
4421 		if (treeColumn == NULL)
4422 		    treeColumn = tree->columnTail;
4423 		columnCount += 1; /* +1 for columnTail */
4424 		break;
4425 	    case COLUMN_LOCK_RIGHT:
4426 		area = TREE_AREA_HEADER_RIGHT;
4427 		break;
4428 	}
4429 	if (dragPosition & WALKSPAN_ONLY_DRAGGED) {
4430 	    columnCount = TreeHeader_GetDraggedColumns(item->header, lock,
4431 		&treeColumn, &treeColumnLast);
4432 	    if (columnCount == 0)
4433 		return;
4434 	}
4435     }
4436 
4437     if (columnCount <= 0)
4438 	return;
4439 
4440     if (!Tree_AreaBbox(tree, area, &drawArgs.bounds)) {
4441 	TreeRect_SetXYWH(drawArgs.bounds, 0, 0, 0, 0);
4442     }
4443 
4444     /* Originally, the array of SpanInfo records used by this function was
4445      * allocated using STATIC_ALLOC.  Not wanting to allocate memory every
4446      * time this function is called (which is often) I decided to keep a
4447      * pointer to the array.  One problem is that TreeItem_UpdateWindowPositions
4448      * may result in recursive calls to this function via code in <Configure>
4449      * event scripts. So I have to keep a stack on the heap. That's also why
4450      * the array can't be shared by different treectrl widgets. */
4451     if (siStack == NULL) {
4452 	siStack = (SpanInfoStack *) ckalloc(sizeof(SpanInfoStack));
4453 	memset(siStack, '\0', sizeof(SpanInfoStack));
4454 	tree->itemSpanPriv = siStack;
4455     }
4456     while (siStack->inUse) {
4457 	if (siStack->next == NULL) {
4458 	    siStack->next = (SpanInfoStack *) ckalloc(sizeof(SpanInfoStack));
4459 	    memset(siStack->next, '\0', sizeof(SpanInfoStack));
4460 	    siStack = siStack->next;
4461 	    break;
4462 	}
4463 	siStack = siStack->next;
4464     }
4465     if (siStack->spanCount < columnCount) {
4466 	siStack->spans = (SpanInfo *) ckrealloc((char *) siStack->spans,
4467 	    sizeof(SpanInfo) * columnCount);
4468 	siStack->spanCount = columnCount;
4469     }
4470     spans = siStack->spans;
4471 
4472     spanCount = Item_GetSpans(tree, item, treeColumn, treeColumnLast,
4473 	columnCount, spans, dragPosition);
4474     if (spanCount <= 0)
4475 	return;
4476 
4477 #ifdef TREECTRL_DEBUG
4478     if (siStack->inUse) panic("TreeItem_WalkSpans stack is in use");
4479 #endif
4480     siStack->inUse = 1;
4481 
4482     drawArgs.tree = tree;
4483     drawArgs.item = item; /* needed for gradients */
4484     drawArgs.td.drawable = None;
4485 
4486     totalWidth = 0;
4487     if (dragPosition & WALKSPAN_ONLY_DRAGGED) {
4488 #ifdef TREECTRL_DEBUG
4489 	if (item->header == NULL) panic("TreeItem_WalkSpans header == NULL");
4490 #endif
4491 	treeColumn = spans[0].treeColumn; /* tree->columnDrag.column */
4492 	totalWidth = TreeColumn_Offset(treeColumn);
4493     }
4494     for (spanIndex = 0; spanIndex < spanCount; spanIndex++) {
4495 	treeColumn = spans[spanIndex].treeColumn;
4496 	itemColumn = spans[spanIndex].itemColumn;
4497 
4498 	/* This is where the actual width of the tail column is determined. */
4499 	if (treeColumn == tree->columnTail) {
4500 	    spans[spanIndex].width = MAX(0, MAX(Tree_ContentWidth(tree),
4501 		Tree_FakeCanvasWidth(tree)) - totalWidth) + tree->tailExtend;
4502 	}
4503 	if (item->header != NULL) {
4504 	    columnWidth = spans[spanIndex].width;
4505 
4506 	/* If this is the single visible column, use the provided width which
4507 	 * may be different than the column's width. */
4508 	} else if ((tree->columnCountVis == 1) && (treeColumn == tree->columnVis)) {
4509 	    columnWidth = width;
4510 
4511 	/* More than one column is visible, or this is not the visible
4512 	 * column. */
4513 	} else {
4514 	    columnWidth = spans[spanIndex].width;
4515 	}
4516 	if (columnWidth <= 0)
4517 	    continue;
4518 
4519 	if ((dragPosition & WALKSPAN_IGNORE_DRAGGED) &&
4520 		spans[spanIndex].isDragColumn)
4521 	    goto next;
4522 
4523 	if (itemColumn != NULL) {
4524 	    drawArgs.state = item->state | itemColumn->cstate;
4525 	    drawArgs.style = itemColumn->style; /* may be NULL */
4526 	} else {
4527 	    drawArgs.state = item->state;
4528 	    drawArgs.style = NULL;
4529 	}
4530 	if ((dragPosition & WALKSPAN_DRAG_ORDER) && (item->header != NULL)) {
4531 	    if ((spanIndex == 0) && (TreeColumn_Lock(treeColumn) == COLUMN_LOCK_NONE))
4532 		drawArgs.indent = tree->canvasPadX[PAD_TOP_LEFT];
4533 	    else
4534 		drawArgs.indent = 0;
4535 	} else
4536 	    drawArgs.indent = TreeItem_Indent(tree, treeColumn, item);
4537 	drawArgs.x = x + totalWidth;
4538 	if (dragPosition & WALKSPAN_ONLY_DRAGGED) {
4539 #ifdef TREECTRL_DEBUG
4540 	    if (item->header == NULL) panic("TreeItem_WalkSpans header == NULL");
4541 #endif
4542 	    drawArgs.x += tree->columnDrag.offset;
4543 	    drawArgs.indent = 0;
4544 	}
4545 	drawArgs.y = y;
4546 	drawArgs.width = columnWidth;
4547 	drawArgs.height = height;
4548 	drawArgs.spanIndex = spanIndex;
4549 	if (item->header != NULL)
4550 	    drawArgs.justify = TreeHeaderColumn_Justify(item->header,
4551 		itemColumn->headerColumn);
4552 	else
4553 	    drawArgs.justify = TreeColumn_ItemJustify(treeColumn);
4554 	drawArgs.column = treeColumn; /* needed for gradients */
4555 
4556 	if ((*proc)(tree, item, &spans[spanIndex], &drawArgs, clientData))
4557 	    break;
4558 next:
4559 	totalWidth += columnWidth;
4560     }
4561 
4562     siStack->inUse = 0;
4563 }
4564 
4565 /*
4566  *----------------------------------------------------------------------
4567  *
4568  * SpanWalkProc_Draw --
4569  *
4570  *	Callback routine to TreeItem_WalkSpans for TreeItem_Draw.
4571  *
4572  * Results:
4573  *	None.
4574  *
4575  * Side effects:
4576  *	Stuff is drawn in a drawable.
4577  *
4578  *----------------------------------------------------------------------
4579  */
4580 
4581 static int
SpanWalkProc_Draw(TreeCtrl * tree,TreeItem item,SpanInfo * spanPtr,StyleDrawArgs * drawArgs,ClientData clientData)4582 SpanWalkProc_Draw(
4583     TreeCtrl *tree,
4584     TreeItem item,
4585     SpanInfo *spanPtr,
4586     StyleDrawArgs *drawArgs,
4587     ClientData clientData
4588     )
4589 {
4590     TreeColumn treeColumn = spanPtr->treeColumn;
4591     TreeItemColumn itemColumn = spanPtr->itemColumn;
4592 #if COLUMNGRID == 1
4593     TreeColor *leftColor, *rightColor;
4594     int leftWidth, rightWidth;
4595 #endif
4596     int i, x;
4597     struct {
4598 	TreeDrawable td;
4599 	int minX;
4600 	int maxX;
4601 	int index;
4602 	int dragPosition;
4603     } *data = clientData;
4604 
4605     /* Draw nothing if the entire span is out-of-bounds. */
4606     if ((drawArgs->x >= data->maxX) ||
4607 	    (drawArgs->x + drawArgs->width <= data->minX))
4608 	return 0;
4609 
4610     drawArgs->td = data->td;
4611 
4612     if (item->header != NULL) {
4613 	TreeHeaderColumn_Draw(item->header,
4614 	    itemColumn ? itemColumn->headerColumn : NULL,
4615 	    spanPtr->visIndex, drawArgs, data->dragPosition);
4616 
4617 	return drawArgs->x + drawArgs->width >= data->maxX;
4618     }
4619 
4620     /* Draw background colors. */
4621     if (spanPtr->span == 1) {
4622 	/* Important point: use drawArgs->width since an item's width may
4623 	 * be totally different than tree->columnVis' width. */
4624 	ItemDrawBackground(tree, treeColumn, item, itemColumn,
4625 		drawArgs->td, drawArgs->x, drawArgs->y,
4626 		drawArgs->width, drawArgs->height, data->index);
4627     } else {
4628 	x = drawArgs->x;
4629 	for (i = 0; i < spanPtr->span; i++) {
4630 	    int columnWidth = TreeColumn_UseWidth(treeColumn);
4631 	    if ((columnWidth > 0) && (x < data->maxX) &&
4632 		    (x + columnWidth > data->minX)) {
4633 		ItemDrawBackground(tree, treeColumn, item, itemColumn,
4634 			drawArgs->td, x, drawArgs->y,
4635 			columnWidth, drawArgs->height, data->index);
4636 	    }
4637 	    x += columnWidth;
4638 	    treeColumn = TreeColumn_Next(treeColumn);
4639 	}
4640     }
4641 
4642     if (drawArgs->style != NULL) {
4643 	StyleDrawArgs drawArgsCopy = *drawArgs;
4644 	TreeStyle_Draw(&drawArgsCopy);
4645     }
4646 
4647 #if COLUMNGRID == 1
4648     if (TreeColumn_GridColors(spanPtr->treeColumn, &leftColor, &rightColor,
4649 	    &leftWidth, &rightWidth) != 0) {
4650 	TreeRectangle tr, trBrush;
4651 
4652 	if (leftColor != NULL && leftWidth > 0) {
4653 	    TreeRect_SetXYWH(tr, drawArgs->x, drawArgs->y, leftWidth,
4654 		    drawArgs->height);
4655 	    TreeColor_GetBrushBounds(tree, leftColor, tr,
4656 		    tree->drawableXOrigin, tree->drawableYOrigin,
4657 		    spanPtr->treeColumn, item, &trBrush);
4658 	    TreeColor_FillRect(tree, data->td, NULL, leftColor, trBrush, tr);
4659 	}
4660 	if (rightColor != NULL && rightWidth > 0) {
4661 	    TreeRect_SetXYWH(tr, drawArgs->x + drawArgs->width - rightWidth,
4662 		    drawArgs->y, rightWidth, drawArgs->height);
4663 	    TreeColor_GetBrushBounds(tree, rightColor, tr,
4664 		    tree->drawableXOrigin, tree->drawableYOrigin,
4665 		    spanPtr->treeColumn, item, &trBrush);
4666 	    TreeColor_FillRect(tree, data->td, NULL, rightColor, trBrush, tr);
4667 	}
4668     }
4669 #endif
4670 
4671     if (spanPtr->treeColumn == tree->columnTree) {
4672 	if (tree->showLines)
4673 	    TreeItem_DrawLines(tree, item, drawArgs->x, drawArgs->y,
4674 		    drawArgs->width, drawArgs->height, data->td,
4675 		    drawArgs->style);
4676 	if (tree->showButtons)
4677 	    TreeItem_DrawButton(tree, item, drawArgs->x, drawArgs->y,
4678 		    drawArgs->width, drawArgs->height, data->td,
4679 		    drawArgs->style);
4680     }
4681 
4682     /* Stop walking if we went past the right edge of the dirty area. */
4683     return drawArgs->x + drawArgs->width >= data->maxX;
4684 }
4685 
4686 /*
4687  *----------------------------------------------------------------------
4688  *
4689  * TreeItem_Draw --
4690  *
4691  *	Draws part of an Item.
4692  *
4693  * Results:
4694  *	None.
4695  *
4696  * Side effects:
4697  *	Stuff is drawn in a drawable.
4698  *
4699  *----------------------------------------------------------------------
4700  */
4701 
4702 void
TreeItem_Draw(TreeCtrl * tree,TreeItem item,int lock,int x,int y,int width,int height,TreeDrawable td,int minX,int maxX,int index)4703 TreeItem_Draw(
4704     TreeCtrl *tree,		/* Widget info. */
4705     TreeItem item,		/* Item token. */
4706     int lock,			/* Which columns. */
4707     int x, int y,		/* Drawable coordinates of the item. */
4708     int width, int height,	/* Total size of the item. */
4709     TreeDrawable td,		/* Where to draw. */
4710     int minX, int maxX,		/* Left/right edge that needs to be drawn. */
4711     int index			/* Used to select a color from a
4712 				 * tree-column's -itembackground option. */
4713     )
4714 {
4715     struct {
4716 	TreeDrawable td;
4717 	int minX;
4718 	int maxX;
4719 	int index;
4720 	int dragPosition;
4721     } clientData;
4722 
4723     clientData.td = td;
4724     clientData.minX = minX;
4725     clientData.maxX = maxX;
4726     clientData.index = index;
4727     clientData.dragPosition = FALSE;
4728 
4729     TreeItem_WalkSpans(tree, item, lock,
4730 	    x, y, width, height,
4731 	    WALKSPAN_DRAG_ORDER,
4732 	    SpanWalkProc_Draw, (ClientData) &clientData);
4733 
4734     if (item->header != NULL) {
4735 	clientData.dragPosition = TRUE;
4736 	TreeItem_WalkSpans(tree, item, lock,
4737 		x, y, width, height,
4738 		WALKSPAN_ONLY_DRAGGED,
4739 		SpanWalkProc_Draw, (ClientData) &clientData);
4740     }
4741 }
4742 
4743 /*
4744  *----------------------------------------------------------------------
4745  *
4746  * TreeItem_DrawLines --
4747  *
4748  *	Draws horizontal and vertical lines indicating parent-child
4749  *	relationship in an item.
4750  *
4751  * Results:
4752  *	None.
4753  *
4754  * Side effects:
4755  *	Stuff is drawn in a drawable.
4756  *
4757  *----------------------------------------------------------------------
4758  */
4759 
4760 void
TreeItem_DrawLines(TreeCtrl * tree,TreeItem item,int x,int y,int width,int height,TreeDrawable td,TreeStyle style)4761 TreeItem_DrawLines(
4762     TreeCtrl *tree,		/* Widget info. */
4763     TreeItem item,		/* Item token. */
4764     int x, int y,		/* Drawable coordinates of columnTree. */
4765     int width, int height,	/* Total size of columnTree. */
4766     TreeDrawable td,		/* Where to draw. */
4767     TreeStyle style		/* Instance style or NULL. Used to get
4768 				 * optional vertical offset of the button. */
4769     )
4770 {
4771     TreeItem parent, walk;
4772     int buttonY = -1;
4773     int indent, left, lineLeft, lineTop;
4774     int hasPrev, hasNext;
4775     int i, vert = 0;
4776 
4777     indent = TreeItem_Indent(tree, tree->columnTree, item);
4778 
4779     if (style != NULL)
4780 	buttonY = TreeStyle_GetButtonY(tree, style);
4781 
4782     /* Left edge of button/line area */
4783     left = x /* + tree->columnTreeLeft */ + indent - tree->useIndent;
4784 
4785     /* Left edge of vertical line */
4786     lineLeft = left + (tree->useIndent - tree->lineThickness) / 2;
4787 
4788     /* Top edge of horizontal line */
4789     if (buttonY < 0)
4790 	lineTop = y + (height - tree->lineThickness) / 2;
4791     else
4792 	lineTop = y + buttonY + (tree->buttonHeightMax - tree->lineThickness) / 2;
4793 
4794     /* NOTE: The next three checks do not call TreeItem_ReallyVisible()
4795      * since 'item' is ReallyVisible */
4796 
4797     /* Check for ReallyVisible previous sibling */
4798     walk = item->prevSibling;
4799     while ((walk != NULL) && !IS_VISIBLE(walk))
4800 	walk = walk->prevSibling;
4801     hasPrev = (walk != NULL);
4802 
4803     /* Check for ReallyVisible parent */
4804     if ((item->parent != NULL) && (!IS_ROOT(item->parent) || tree->showRoot))
4805 	hasPrev = TRUE;
4806 
4807     /* Check for ReallyVisible next sibling */
4808     walk = item->nextSibling;
4809     while ((walk != NULL) && !IS_VISIBLE(walk))
4810 	walk = walk->nextSibling;
4811     hasNext = (walk != NULL);
4812 
4813     /* Option: Don't connect children of root item */
4814     if ((item->parent != NULL) && IS_ROOT(item->parent) && !tree->showRootLines)
4815 	hasPrev = hasNext = FALSE;
4816 
4817     /* Vertical line to parent and/or previous/next sibling */
4818     if (hasPrev || hasNext) {
4819 	int top = y, bottom = y + height;
4820 
4821 	if (!hasPrev)
4822 	    top = lineTop;
4823 	if (!hasNext)
4824 	    bottom = lineTop + tree->lineThickness;
4825 
4826 	if (tree->lineStyle == LINE_STYLE_DOT) {
4827 	    for (i = 0; i < tree->lineThickness; i++) {
4828 		Tree_VDotLine(tree, td.drawable,
4829 			lineLeft + i,
4830 			top,
4831 			bottom);
4832 	    }
4833 	} else {
4834 	    XFillRectangle(tree->display, td.drawable, tree->lineGC[0],
4835 		    lineLeft,
4836 		    top,
4837 		    tree->lineThickness,
4838 		    bottom - top);
4839 	}
4840 
4841 	/* Don't overlap horizontal line */
4842 	vert = tree->lineThickness;
4843     }
4844 
4845     /* Horizontal line to self */
4846     if (hasPrev || hasNext) {
4847 	if (tree->lineStyle == LINE_STYLE_DOT) {
4848 	    for (i = 0; i < tree->lineThickness; i++) {
4849 		Tree_HDotLine(tree, td.drawable,
4850 			lineLeft + vert,
4851 			lineTop + i,
4852 			x /* + tree->columnTreeLeft */ + indent);
4853 	    }
4854 	} else {
4855 	    XFillRectangle(tree->display, td.drawable, tree->lineGC[0],
4856 		    lineLeft + vert,
4857 		    lineTop,
4858 		    left + tree->useIndent - (lineLeft + vert),
4859 		    tree->lineThickness);
4860 	}
4861     }
4862 
4863     /* Vertical lines from ancestors to their next siblings */
4864     for (parent = item->parent;
4865 	 parent != NULL;
4866 	 parent = parent->parent) {
4867 	lineLeft -= tree->useIndent;
4868 
4869 	/* Option: Don't connect children of root item */
4870 	if ((parent->parent != NULL) && IS_ROOT(parent->parent) && !tree->showRootLines)
4871 	    continue;
4872 
4873 	/* Check for ReallyVisible next sibling */
4874 	item = parent->nextSibling;
4875 	while ((item != NULL) && !IS_VISIBLE(item))
4876 	    item = item->nextSibling;
4877 
4878 	if (item != NULL) {
4879 	    if (tree->lineStyle == LINE_STYLE_DOT) {
4880 		for (i = 0; i < tree->lineThickness; i++) {
4881 		    Tree_VDotLine(tree, td.drawable,
4882 			    lineLeft + i,
4883 			    y,
4884 			    y + height);
4885 		}
4886 	    } else {
4887 		XFillRectangle(tree->display, td.drawable, tree->lineGC[0],
4888 			lineLeft,
4889 			y,
4890 			tree->lineThickness,
4891 			height);
4892 	    }
4893 	}
4894     }
4895 }
4896 
4897 /*
4898  *----------------------------------------------------------------------
4899  *
4900  * TreeItem_DrawButton --
4901  *
4902  *	Draws the button (if any) in an item.
4903  *
4904  * Results:
4905  *	None.
4906  *
4907  * Side effects:
4908  *	Stuff is drawn in a drawable.
4909  *
4910  *----------------------------------------------------------------------
4911  */
4912 
4913 void
TreeItem_DrawButton(TreeCtrl * tree,TreeItem item,int x,int y,int width,int height,TreeDrawable td,TreeStyle style)4914 TreeItem_DrawButton(
4915     TreeCtrl *tree,		/* Widget info. */
4916     TreeItem item,		/* Item token. */
4917     int x, int y,		/* Drawable coordinates of columnTree. */
4918     int width, int height,	/* Total size of columnTree. */
4919     TreeDrawable td,		/* Where to draw. */
4920     TreeStyle style		/* Instance style or NULL. Used to get
4921 				 * optional vertical offset of the button. */
4922     )
4923 {
4924     int indent, left, lineLeft, lineTop;
4925     int buttonLeft, buttonTop, buttonY = -1, w1;
4926     Tk_Image image;
4927     Pixmap bitmap;
4928 
4929     if (!TreeItem_HasButton(tree, item))
4930 	return;
4931 
4932     indent = TreeItem_Indent(tree, tree->columnTree, item);
4933 
4934     if (style != NULL)
4935 	buttonY = TreeStyle_GetButtonY(tree, style);
4936 
4937     /* Left edge of button/line area */
4938     left = x /* + tree->columnTreeLeft */ + indent - tree->useIndent;
4939 
4940     image = PerStateImage_ForState(tree, &tree->buttonImage, item->state, NULL);
4941     if (image != NULL) {
4942 	int imgW, imgH;
4943 	Tk_SizeOfImage(image, &imgW, &imgH);
4944 	if (buttonY < 0)
4945 	    buttonY = (height - imgH) / 2;
4946 	Tree_RedrawImage(image, 0, 0, imgW, imgH, td,
4947 	    left + (tree->useIndent - imgW) / 2,
4948 	    y + buttonY);
4949 	return;
4950     }
4951 
4952     bitmap = PerStateBitmap_ForState(tree, &tree->buttonBitmap, item->state, NULL);
4953     if (bitmap != None) {
4954 	int bmpW, bmpH;
4955 	int bx, by;
4956 	Tk_SizeOfBitmap(tree->display, bitmap, &bmpW, &bmpH);
4957 	if (buttonY < 0)
4958 	    buttonY = (height - bmpH) / 2;
4959 	bx = left + (tree->useIndent - bmpW) / 2;
4960 	by = y + buttonY;
4961 	Tree_DrawBitmap(tree, bitmap, td.drawable, NULL, NULL,
4962 		0, 0, (unsigned int) bmpW, (unsigned int) bmpH,
4963 		bx, by);
4964 	return;
4965     }
4966 
4967     if (tree->useTheme) {
4968 	int bw, bh;
4969 	int buttonState = item->state;
4970 
4971 	/* FIXME: These may overwrite [state define] states */
4972 	buttonState &= ~ITEM_FLAGS_BUTTONSTATE;
4973 	if (item->flags & ITEM_FLAG_BUTTONSTATE_ACTIVE)
4974 	    buttonState |= BUTTON_STATE_ACTIVE;
4975 	if (item->flags & ITEM_FLAG_BUTTONSTATE_PRESSED)
4976 	    buttonState |= BUTTON_STATE_PRESSED;
4977 
4978 	if (TreeTheme_GetButtonSize(tree, td.drawable,
4979 		(buttonState & STATE_ITEM_OPEN) != 0, &bw, &bh) == TCL_OK) {
4980 	    if (buttonY < 0)
4981 		buttonY = (height - bh) / 2;
4982 	    if (TreeTheme_DrawButton(tree, td, item, buttonState,
4983 		    left + (tree->useIndent - bw) / 2, y + buttonY,
4984 		    bw, bh) == TCL_OK) {
4985 		return;
4986 	    }
4987 	}
4988     }
4989 
4990     w1 = tree->buttonThickness / 2;
4991 
4992     /* Left edge of vertical line */
4993     /* Make sure this matches TreeItem_DrawLines() */
4994     lineLeft = left + (tree->useIndent - tree->buttonThickness) / 2;
4995 
4996     /* Top edge of horizontal line */
4997     /* Make sure this matches TreeItem_DrawLines() */
4998     if (buttonY < 0)
4999 	lineTop = y + (height - tree->lineThickness) / 2;
5000     else
5001 	lineTop = y + buttonY + (tree->buttonHeightMax - tree->lineThickness) / 2;
5002 
5003     buttonLeft = left + (tree->useIndent - tree->buttonSize) / 2;
5004     if (buttonY < 0)
5005 	buttonTop = y + (height - tree->buttonSize) / 2;
5006     else
5007 	buttonTop = y + buttonY + (tree->buttonHeightMax - tree->buttonSize) / 2;
5008 
5009     /* Erase button background */
5010     XFillRectangle(tree->display, td.drawable,
5011 	    Tk_3DBorderGC(tree->tkwin, tree->border, TK_3D_FLAT_GC),
5012 	    buttonLeft + tree->buttonThickness,
5013 	    buttonTop + tree->buttonThickness,
5014 	    tree->buttonSize - tree->buttonThickness,
5015 	    tree->buttonSize - tree->buttonThickness);
5016 
5017     /* Draw button outline */
5018     XDrawRectangle(tree->display, td.drawable, tree->buttonGC,
5019 	    buttonLeft + w1,
5020 	    buttonTop + w1,
5021 	    tree->buttonSize - tree->buttonThickness,
5022 	    tree->buttonSize - tree->buttonThickness);
5023 
5024     /* Horizontal '-' */
5025     XFillRectangle(tree->display, td.drawable, tree->buttonGC,
5026 	    buttonLeft + tree->buttonThickness * 2,
5027 	    lineTop,
5028 	    tree->buttonSize - tree->buttonThickness * 4,
5029 	    tree->buttonThickness);
5030 
5031     if (!(item->state & STATE_ITEM_OPEN)) {
5032 	/* Finish '+' */
5033 	XFillRectangle(tree->display, td.drawable, tree->buttonGC,
5034 		lineLeft,
5035 		buttonTop + tree->buttonThickness * 2,
5036 		tree->buttonThickness,
5037 		tree->buttonSize - tree->buttonThickness * 4);
5038     }
5039 }
5040 
5041 /*
5042  *----------------------------------------------------------------------
5043  *
5044  * SpanWalkProc_UpdateWindowPositions --
5045  *
5046  *	Callback routine to TreeItem_WalkSpans for
5047  *	TreeItem_UpdateWindowPositions.
5048  *
5049  * Results:
5050  *	None.
5051  *
5052  * Side effects:
5053  *	Windows in window elements may be resized/repositioned.
5054  *
5055  *----------------------------------------------------------------------
5056  */
5057 
5058 static int
SpanWalkProc_UpdateWindowPositions(TreeCtrl * tree,TreeItem item,SpanInfo * spanPtr,StyleDrawArgs * drawArgs,ClientData clientData)5059 SpanWalkProc_UpdateWindowPositions(
5060     TreeCtrl *tree,
5061     TreeItem item,
5062     SpanInfo *spanPtr,
5063     StyleDrawArgs *drawArgs,
5064     ClientData clientData
5065     )
5066 {
5067     StyleDrawArgs drawArgsCopy;
5068     int requests;
5069 
5070     if ((drawArgs->x >= TreeRect_Right(drawArgs->bounds)) ||
5071 	    (drawArgs->x + drawArgs->width <= TreeRect_Left(drawArgs->bounds)) ||
5072 	    (drawArgs->style == NULL))
5073 	return 0;
5074 
5075     TreeDisplay_GetReadyForTrouble(tree, &requests);
5076 
5077     drawArgsCopy = *drawArgs;
5078     TreeStyle_UpdateWindowPositions(&drawArgsCopy);
5079 
5080     if (TreeDisplay_WasThereTrouble(tree, requests))
5081 	return 1;
5082 
5083     /* Stop walking if we went past the right edge of the display area. */
5084     return drawArgs->x + drawArgs->width >= TreeRect_Right(drawArgs->bounds);
5085 }
5086 
5087 /*
5088  *----------------------------------------------------------------------
5089  *
5090  * TreeItem_UpdateWindowPositions --
5091  *
5092  *	Updates the geometry of any on-screen window elements. Called
5093  *	by the display code when an item was possibly scrolled.
5094  *
5095  * Results:
5096  *	None.
5097  *
5098  * Side effects:
5099  *	Windows in window elements may be resized/repositioned.
5100  *
5101  *----------------------------------------------------------------------
5102  */
5103 
5104 void
TreeItem_UpdateWindowPositions(TreeCtrl * tree,TreeItem item,int lock,int x,int y,int width,int height)5105 TreeItem_UpdateWindowPositions(
5106     TreeCtrl *tree,		/* Widget info. */
5107     TreeItem item,		/* Item token. */
5108     int lock,			/* Columns we care about. */
5109     int x, int y,		/* Window coordinates of the item. */
5110     int width, int height	/* Total size of the item. */
5111     )
5112 {
5113     TreeItem_WalkSpans(tree, item, lock,
5114 	    x, y, width, height,
5115 	    WALKSPAN_DRAG_ORDER | WALKSPAN_IGNORE_DRAGGED,
5116 	    SpanWalkProc_UpdateWindowPositions, (ClientData) NULL);
5117 
5118     if (item->header != NULL) {
5119 	TreeItem_WalkSpans(tree, item, lock,
5120 		x, y, width, height,
5121 		WALKSPAN_ONLY_DRAGGED,
5122 		SpanWalkProc_UpdateWindowPositions, (ClientData) NULL);
5123     }
5124 }
5125 
5126 /*
5127  *----------------------------------------------------------------------
5128  *
5129  * SpanWalkProc_GetOnScreenColumns --
5130  *
5131  *	Callback routine to TreeItem_WalkSpans for
5132  *	TreeItem_GetOnScreenColumns.
5133  *
5134  * Results:
5135  *	None.
5136  *
5137  * Side effects:
5138  *	None.
5139  *
5140  *----------------------------------------------------------------------
5141  */
5142 
5143 static int
SpanWalkProc_GetOnScreenColumns(TreeCtrl * tree,TreeItem item,SpanInfo * spanPtr,StyleDrawArgs * drawArgs,ClientData clientData)5144 SpanWalkProc_GetOnScreenColumns(
5145     TreeCtrl *tree,
5146     TreeItem item,
5147     SpanInfo *spanPtr,
5148     StyleDrawArgs *drawArgs,
5149     ClientData clientData
5150     )
5151 {
5152     TreeColumnList *columns = clientData;
5153 
5154     if ((drawArgs->x >= TreeRect_Right(drawArgs->bounds)) ||
5155 	    (drawArgs->x + drawArgs->width <= TreeRect_Left(drawArgs->bounds)))
5156 	return 0;
5157 
5158     TreeColumnList_Append(columns, drawArgs->column);
5159 
5160     /* Stop walking if we went past the right edge of the display area. */
5161     return drawArgs->x + drawArgs->width >= TreeRect_Right(drawArgs->bounds);
5162 }
5163 
5164 /*
5165  *----------------------------------------------------------------------
5166  *
5167  * TreeItem_GetOnScreenColumns --
5168  *
5169  *	Get a list of onscreen columns for an item.
5170  *
5171  * Results:
5172  *	None.
5173  *
5174  * Side effects:
5175  *	None.
5176  *
5177  *----------------------------------------------------------------------
5178  */
5179 
5180 void
TreeItem_GetOnScreenColumns(TreeCtrl * tree,TreeItem item,int lock,int x,int y,int width,int height,TreeColumnList * columns)5181 TreeItem_GetOnScreenColumns(
5182     TreeCtrl *tree,		/* Widget info. */
5183     TreeItem item,		/* Item token. */
5184     int lock,			/* Which columns. */
5185     int x, int y,		/* Drawable coordinates of the item. */
5186     int width, int height,	/* Total size of the item. */
5187     TreeColumnList *columns	/* Out: list of onscreen columns. */
5188     )
5189 {
5190     TreeItem_WalkSpans(tree, item, lock,
5191 	x, y, width, height,
5192 	WALKSPAN_DRAG_ORDER | WALKSPAN_IGNORE_DRAGGED,
5193 	SpanWalkProc_GetOnScreenColumns, (ClientData) columns);
5194 
5195     if (item->header != NULL) {
5196 	TreeItem_WalkSpans(tree, item, lock,
5197 	    x, y, width, height,
5198 	    WALKSPAN_ONLY_DRAGGED,
5199 	    SpanWalkProc_GetOnScreenColumns, (ClientData) columns);
5200     }
5201 }
5202 
5203 /*
5204  *----------------------------------------------------------------------
5205  *
5206  * TreeItem_OnScreen --
5207  *
5208  *	Called by the display code when the item becomes visible
5209  *	(i.e., actually displayed) or hidden.
5210  *
5211  * Results:
5212  *	None.
5213  *
5214  * Side effects:
5215  *	Windows in window elements may be mapped/unmapped.
5216  *
5217  *----------------------------------------------------------------------
5218  */
5219 
5220 void
TreeItem_OnScreen(TreeCtrl * tree,TreeItem item,int onScreen)5221 TreeItem_OnScreen(
5222     TreeCtrl *tree,		/* Widget info. */
5223     TreeItem item,		/* Item token. */
5224     int onScreen		/* TRUE if item is displayed. */
5225     )
5226 {
5227 #if 0
5228     TreeItemColumn column = item->columns;
5229 
5230     while (column != NULL) {
5231 	if (column->style != NULL) {
5232 	    TreeStyle_OnScreen(tree, column->style, onScreen);
5233 	}
5234 	column = column->next;
5235     }
5236 #endif
5237 }
5238 
5239 /*
5240  *----------------------------------------------------------------------
5241  *
5242  * TreeItem_ReallyVisible --
5243  *
5244  *	Return whether the given Item could be displayed.
5245  *
5246  * Results:
5247  *	TRUE if the item's -visible is TRUE, all of its ancestors'
5248  *	-visible options are TRUE, and all of its ancestors are open.
5249  *
5250  * Side effects:
5251  *	None.
5252  *
5253  *----------------------------------------------------------------------
5254  */
5255 
5256 int
TreeItem_ReallyVisible(TreeCtrl * tree,TreeItem item)5257 TreeItem_ReallyVisible(
5258     TreeCtrl *tree,		/* Widget info. */
5259     TreeItem item		/* Item token. */
5260     )
5261 {
5262     TreeItem parent = item->parent;
5263 
5264     if (item->header != NULL) {
5265 	if (!tree->showHeader || !IS_VISIBLE(item))
5266 	    return 0;
5267 
5268 	/* For compatibility, if there are no visible columns then don't
5269 	 * display the lone tail column. */
5270 	(void) TreeColumns_UpdateCounts(tree);
5271 	if (tree->columnCountVis +
5272 		tree->columnCountVisLeft +
5273 		tree->columnCountVisRight == 0)
5274 	    return 0;
5275 
5276 	return 1;
5277     }
5278 
5279     if (!tree->updateIndex)
5280 	return item->indexVis != -1;
5281 
5282     if (!IS_VISIBLE(item))
5283 	return 0;
5284     if (parent == NULL)
5285 	return IS_ROOT(item) ? tree->showRoot : 0;
5286     if (IS_ROOT(parent)) {
5287 	if (!IS_VISIBLE(parent))
5288 	    return 0;
5289 	if (!tree->showRoot)
5290 	    return 1;
5291 	if (!(parent->state & STATE_ITEM_OPEN))
5292 	    return 0;
5293     }
5294     if (!IS_VISIBLE(parent) || !(parent->state & STATE_ITEM_OPEN))
5295 	return 0;
5296     return TreeItem_ReallyVisible(tree, parent);
5297 }
5298 
5299 /*
5300  *----------------------------------------------------------------------
5301  *
5302  * TreeItem_RootAncestor --
5303  *
5304  *	Return the toplevel ancestor of an Item.
5305  *
5306  * Results:
5307  *	Returns the root, or an orphan ancestor, or the given Item.
5308  *
5309  * Side effects:
5310  *	None.
5311  *
5312  *----------------------------------------------------------------------
5313  */
5314 
5315 TreeItem
TreeItem_RootAncestor(TreeCtrl * tree,TreeItem item)5316 TreeItem_RootAncestor(
5317     TreeCtrl *tree,		/* Widget info. */
5318     TreeItem item		/* Item token. */
5319     )
5320 {
5321     while (item->parent != NULL)
5322 	item = item->parent;
5323     return item;
5324 }
5325 
5326 /*
5327  *----------------------------------------------------------------------
5328  *
5329  * TreeItem_IsAncestor --
5330  *
5331  *	Determine if one Item is the ancestor of another.
5332  *
5333  * Results:
5334  *	TRUE if item1 is an ancestor of item2.
5335  *
5336  * Side effects:
5337  *	None.
5338  *
5339  *----------------------------------------------------------------------
5340  */
5341 
5342 int
TreeItem_IsAncestor(TreeCtrl * tree,TreeItem item1,TreeItem item2)5343 TreeItem_IsAncestor(
5344     TreeCtrl *tree,		/* Widget info. */
5345     TreeItem item1,		/* Possible ancestor. */
5346     TreeItem item2		/* Item to check ancestry of. */
5347     )
5348 {
5349     if (item1 == item2)
5350 	return 0;
5351     while (item2 && item2 != item1)
5352 	item2 = item2->parent;
5353     return item2 != NULL;
5354 }
5355 
5356 /*
5357  *----------------------------------------------------------------------
5358  *
5359  * TreeItem_ToObj --
5360  *
5361  *	Convert an Item to a Tcl_Obj.
5362  *
5363  * Results:
5364  *	A new Tcl_Obj representing the Item.
5365  *
5366  * Side effects:
5367  *	Memory is allocated.
5368  *
5369  *----------------------------------------------------------------------
5370  */
5371 
5372 Tcl_Obj *
TreeItem_ToObj(TreeCtrl * tree,TreeItem item)5373 TreeItem_ToObj(
5374     TreeCtrl *tree,		/* Widget info. */
5375     TreeItem item		/* Item token. */
5376     )
5377 {
5378     if (tree->itemPrefixLen) {
5379 	char buf[100 + TCL_INTEGER_SPACE];
5380 	(void) sprintf(buf, "%s%d", tree->itemPrefix, item->id);
5381 	return Tcl_NewStringObj(buf, -1);
5382     }
5383     return Tcl_NewIntObj(item->id);
5384 }
5385 
5386 /*
5387  *----------------------------------------------------------------------
5388  *
5389  * Item_Configure --
5390  *
5391  *	This procedure is called to process an objc/objv list, plus
5392  *	the Tk option database, in order to configure (or reconfigure)
5393  *	an Item.
5394  *
5395  * Results:
5396  *	The return value is a standard Tcl result.  If TCL_ERROR is
5397  *	returned, then the interp's result contains an error message.
5398  *
5399  * Side effects:
5400  *	Configuration information gets set for item;  old resources get
5401  *	freed, if there were any.
5402  *
5403  *----------------------------------------------------------------------
5404  */
5405 
5406 static int
Item_Configure(TreeCtrl * tree,TreeItem item,int objc,Tcl_Obj * CONST objv[])5407 Item_Configure(
5408     TreeCtrl *tree,		/* Widget info. */
5409     TreeItem item,		/* Item to configure. */
5410     int objc,			/* Number of arguments */
5411     Tcl_Obj *CONST objv[]	/* Array of arguments */
5412     )
5413 {
5414     Tk_SavedOptions savedOptions;
5415     int error;
5416     Tcl_Obj *errorResult = NULL;
5417     int mask;
5418     int lastVisible = IS_VISIBLE(item);
5419     int lastWrap = IS_WRAP(item);
5420 
5421     for (error = 0; error <= 1; error++) {
5422 	if (error == 0) {
5423 	    if (Tree_SetOptions(tree, STATE_DOMAIN_ITEM, item, tree->itemOptionTable,
5424 			objc, objv, &savedOptions, &mask) != TCL_OK) {
5425 		mask = 0;
5426 		continue;
5427 	    }
5428 
5429 	    /* xxx */
5430 
5431 	    Tk_FreeSavedOptions(&savedOptions);
5432 	    break;
5433 	} else {
5434 	    errorResult = Tcl_GetObjResult(tree->interp);
5435 	    Tcl_IncrRefCount(errorResult);
5436 	    Tk_RestoreSavedOptions(&savedOptions);
5437 
5438 	    /* xxx */
5439 
5440 	    Tcl_SetObjResult(tree->interp, errorResult);
5441 	    Tcl_DecrRefCount(errorResult);
5442 	    return TCL_ERROR;
5443 	}
5444     }
5445 
5446     if (mask & ITEM_CONF_SIZE) {
5447 	Tree_FreeItemDInfo(tree, item, NULL);
5448 	Tree_DInfoChanged(tree, DINFO_REDO_RANGES);
5449     }
5450 
5451     if (mask & ITEM_CONF_BUTTON) {
5452 	if (tree->columnTree != NULL)
5453 	    Tree_InvalidateItemDInfo(tree, tree->columnTree, item, NULL);
5454     }
5455 
5456     if ((mask & ITEM_CONF_VISIBLE) && (IS_VISIBLE(item) != lastVisible)) {
5457 
5458 	/* Changing the visibility of an item can change the width of
5459 	 * any column. This is due to column expansion (a style may
5460 	 * be the widest in a column) or when any span > 1. */
5461 	TreeColumns_InvalidateWidthOfItems(tree, NULL);
5462 	TreeColumns_InvalidateSpans(tree);
5463 
5464 	/* If this is the last child, redraw the lines of the previous
5465 	 * sibling and all of its descendants because the line from
5466 	 * the previous sibling to us is appearing/disappearing. */
5467 	if ((item->prevSibling != NULL) &&
5468 		(item->nextSibling == NULL) &&
5469 		tree->showLines && (tree->columnTree != NULL)) {
5470 	    TreeItem last = item->prevSibling;
5471 	    while (last->lastChild != NULL)
5472 		last = last->lastChild;
5473 	    Tree_InvalidateItemDInfo(tree, tree->columnTree,
5474 		    item->prevSibling,
5475 		    last);
5476 	}
5477 
5478 	/* Redraw the parent if the parent has "-button auto". */
5479 	if ((item->parent != NULL) &&
5480 		(item->parent->flags & ITEM_FLAG_BUTTON_AUTO) &&
5481 		tree->showButtons && (tree->columnTree != NULL)) {
5482 	    Tree_InvalidateItemDInfo(tree, tree->columnTree, item->parent,
5483 		    NULL);
5484 	}
5485 
5486 	tree->updateIndex = 1;
5487 	Tree_DInfoChanged(tree, DINFO_REDO_RANGES | DINFO_REDO_SELECTION);
5488     }
5489 
5490     if ((mask & ITEM_CONF_WRAP) && (IS_WRAP(item) != lastWrap)) {
5491 	tree->updateIndex = 1;
5492 	TreeColumns_InvalidateWidthOfItems(tree, NULL);
5493 	Tree_DInfoChanged(tree, DINFO_REDO_RANGES);
5494     }
5495 
5496     return TCL_OK;
5497 }
5498 
5499 /*
5500  *----------------------------------------------------------------------
5501  *
5502  * NoStyleMsg --
5503  *
5504  *	Utility to set the interpreter result with a message indicating
5505  *	a Column has no assigned style.
5506  *
5507  * Results:
5508  *	None.
5509  *
5510  * Side effects:
5511  *	Interpreter result is changed.
5512  *
5513  *----------------------------------------------------------------------
5514  */
5515 
5516 static void
NoStyleMsg(TreeCtrl * tree,TreeItem item,int columnIndex)5517 NoStyleMsg(
5518     TreeCtrl *tree,		/* Widget info. */
5519     TreeItem item,		/* Item record. */
5520     int columnIndex		/* 0-based index of the column that
5521 				 * has no style. */
5522     )
5523 {
5524     FormatResult(tree->interp,
5525 	    "%s %s%d column %s%d has no style",
5526 	    item->header ? "header" : "item",
5527 	    item->header ? "" : tree->itemPrefix, item->id,
5528 	    tree->columnPrefix,
5529 	    TreeColumn_GetID(Tree_FindColumn(tree, columnIndex)));
5530 }
5531 
5532 /*
5533  *----------------------------------------------------------------------
5534  *
5535  * StateDomainErrMsg --
5536  *
5537  *	Utility to set the interpreter result with a message indicating
5538  *	a style's state-domain isn't compatible.
5539  *
5540  * Results:
5541  *	Interpreter result is changed.
5542  *
5543  * Side effects:
5544  *	None.
5545  *
5546  *----------------------------------------------------------------------
5547  */
5548 
5549 static void
StateDomainErrMsg(TreeCtrl * tree,TreeItem item,TreeStyle style)5550 StateDomainErrMsg(
5551     TreeCtrl *tree,		/* Widget info. */
5552     TreeItem item,		/* Item record. */
5553     TreeStyle style		/* Style token. */
5554     )
5555 {
5556     FormatResult(tree->interp,
5557 	    "state domain conflict between %s \"%s%d\" and style \"%s\"",
5558 	    item->header ? "header" : "item",
5559 	    item->header ? "" : tree->itemPrefix, item->id,
5560 	    TreeStyle_GetName(tree, style));
5561 }
5562 
5563 /*
5564  *----------------------------------------------------------------------
5565  *
5566  * TreeItemCmd_Bbox --
5567  *
5568  *	This procedure is invoked to process the [item bbox] widget
5569  *	command.  See the user documentation for details on what
5570  *	it does.
5571  *
5572  * Results:
5573  *	A standard Tcl result.
5574  *
5575  * Side effects:
5576  *	See the user documentation.
5577  *
5578  *----------------------------------------------------------------------
5579  */
5580 
5581 int
TreeItemCmd_Bbox(TreeCtrl * tree,int objc,Tcl_Obj * CONST objv[],int doHeaders)5582 TreeItemCmd_Bbox(
5583     TreeCtrl *tree,		/* Widget info. */
5584     int objc,			/* Number of arguments. */
5585     Tcl_Obj *CONST objv[],	/* Argument values. */
5586     int doHeaders		/* TRUE to operate on headers, FALSE
5587 				 * to operate on items. */
5588     )
5589 {
5590     Tcl_Interp *interp = tree->interp;
5591     TreeItem item;
5592     int count;
5593     TreeColumn treeColumn;
5594     TreeRectangle rect;
5595 
5596     if (objc < 4 || objc > 6) {
5597 	Tcl_WrongNumArgs(interp, 3, objv,
5598 	    doHeaders ?
5599 		"header ?column? ?element?" :
5600 		"item ?column? ?element?");
5601 	return TCL_ERROR;
5602     }
5603 
5604     if (doHeaders) {
5605 	TreeHeader header;
5606 	if (TreeHeader_FromObj(tree, objv[3], &header) != TCL_OK)
5607 	    return TCL_ERROR;
5608 	item = TreeHeader_GetItem(header);
5609     } else {
5610 	if (TreeItem_FromObj(tree, objv[3], &item, IFO_NOT_NULL) != TCL_OK)
5611 	    return TCL_ERROR;
5612     }
5613 
5614     (void) Tree_GetOriginX(tree);
5615     (void) Tree_GetOriginY(tree);
5616 
5617     if (objc == 4) {
5618 	/* If an item is visible but has zero height a valid bbox
5619 	 * is returned. */
5620 	if (Tree_ItemBbox(tree, item, COLUMN_LOCK_NONE, &rect) < 0)
5621 	    return TCL_OK;
5622 
5623 	if (doHeaders)
5624 	    rect.width -= tree->tailExtend;
5625     } else {
5626 	if (TreeColumn_FromObj(tree, objv[4], &treeColumn,
5627 		CFO_NOT_NULL | CFO_NOT_TAIL) != TCL_OK)
5628 	    return TCL_ERROR;
5629 
5630 	/* Bounds of a column. */
5631 	if (objc == 5) {
5632 	    objc = 0;
5633 	    objv = NULL;
5634 
5635 	/* Single element in a column. */
5636 	} else {
5637 	    TreeItemColumn column;
5638 	    TreeElement elem;
5639 
5640 	    objc -= 5;
5641 	    objv += 5;
5642 
5643 	    /* Validate the style + element here.  If a span has zero size
5644 	     * it won't be done. */
5645 	    column = TreeItem_FindColumn(tree, item, TreeColumn_Index(treeColumn));
5646 	    if (column == NULL || column->style == NULL || TreeStyle_IsHeaderStyle(tree, column->style)) {
5647 		NoStyleMsg(tree, item, TreeColumn_Index(treeColumn));
5648 		return TCL_ERROR;
5649 	    }
5650 	    if (TreeElement_FromObj(tree, objv[0], &elem) != TCL_OK)
5651 		return TCL_ERROR;
5652 	    if (TreeStyle_FindElement(tree, column->style, elem, NULL) != TCL_OK)
5653 		return TCL_ERROR;
5654 	}
5655 
5656 	count = TreeItem_GetRects(tree, item, treeColumn, objc, objv, &rect);
5657 	if (count == 0)
5658 	    return TCL_OK;
5659 	if (count == -1)
5660 	    return TCL_ERROR;
5661     }
5662 
5663     /* Canvas -> window coordinates */
5664     FormatResult(interp, "%d %d %d %d",
5665 	    TreeRect_Left(rect) - tree->xOrigin,
5666 	    TreeRect_Top(rect) - tree->yOrigin,
5667 	    TreeRect_Left(rect) - tree->xOrigin + TreeRect_Width(rect),
5668 	    TreeRect_Top(rect) - tree->yOrigin + TreeRect_Height(rect));
5669 
5670     return TCL_OK;
5671 }
5672 
5673 static int
ItemBboxCmd(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])5674 ItemBboxCmd(
5675     ClientData clientData,	/* Widget info. */
5676     Tcl_Interp *interp,		/* Current interpreter. */
5677     int objc,			/* Number of arguments. */
5678     Tcl_Obj *CONST objv[]	/* Argument values. */
5679     )
5680 {
5681     TreeCtrl *tree = clientData;
5682 
5683     return TreeItemCmd_Bbox(tree, objc, objv, FALSE);
5684 }
5685 
5686 /*
5687  *----------------------------------------------------------------------
5688  *
5689  * ItemCreateCmd --
5690  *
5691  *	This procedure is invoked to process the [item create] widget
5692  *	command.  See the user documentation for details on what
5693  *	it does.
5694  *
5695  * Results:
5696  *	A standard Tcl result.
5697  *
5698  * Side effects:
5699  *	See the user documentation.
5700  *
5701  *----------------------------------------------------------------------
5702  */
5703 
5704 static int
ItemCreateCmd(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])5705 ItemCreateCmd(
5706     ClientData clientData,	/* Widget info. */
5707     Tcl_Interp *interp,		/* Current interpreter. */
5708     int objc,			/* Number of arguments. */
5709     Tcl_Obj *CONST objv[]	/* Argument values. */
5710     )
5711 {
5712     TreeCtrl *tree = clientData;
5713     static CONST char *optionNames[] = { "-button", "-count", "-enabled",
5714 	"-height", "-nextsibling", "-open", "-parent", "-prevsibling",
5715 	"-returnid", "-tags", "-visible", "-wrap",
5716 	(char *) NULL };
5717     enum { OPT_BUTTON, OPT_COUNT, OPT_ENABLED, OPT_HEIGHT, OPT_NEXTSIBLING,
5718 	OPT_OPEN, OPT_PARENT, OPT_PREVSIBLING, OPT_RETURNID, OPT_TAGS,
5719 	OPT_VISIBLE, OPT_WRAP };
5720     int index, i, count = 1, button = 0, returnId = 1, open = 1, visible = 1;
5721     int enabled = 1, wrap = 0, height = 0;
5722     TreeItem item, parent = NULL, prevSibling = NULL, nextSibling = NULL;
5723     TreeItem head = NULL, tail = NULL;
5724     Tcl_Obj *listObj = NULL, *tagsObj = NULL;
5725     TagInfo *tagInfo = NULL;
5726     TreeColumn treeColumn;
5727 
5728     for (i = 3; i < objc; i += 2) {
5729 	if (Tcl_GetIndexFromObj(interp, objv[i], optionNames, "option", 0,
5730 		&index) != TCL_OK) {
5731 	    return TCL_ERROR;
5732 	}
5733 	if (i + 1 == objc) {
5734 	    FormatResult(interp, "missing value for \"%s\" option",
5735 		    optionNames[index]);
5736 	    return TCL_ERROR;
5737 	}
5738 	switch (index) {
5739 	    case OPT_BUTTON: {
5740 		int length;
5741 		char *s = Tcl_GetStringFromObj(objv[i + 1], &length);
5742 		if (s[0] == 'a' && strncmp(s, "auto", length) == 0) {
5743 		    button = ITEM_FLAG_BUTTON_AUTO;
5744 		} else {
5745 		    if (Tcl_GetBooleanFromObj(interp, objv[i + 1], &button) != TCL_OK) {
5746 			FormatResult(interp, "expected boolean or auto but got \"%s\"", s);
5747 			return TCL_ERROR;
5748 		    }
5749 		    if (button) {
5750 			button = ITEM_FLAG_BUTTON;
5751 		    }
5752 		}
5753 		break;
5754 	    }
5755 	    case OPT_COUNT:
5756 		if (Tcl_GetIntFromObj(interp, objv[i + 1], &count) != TCL_OK)
5757 		    return TCL_ERROR;
5758 		if (count <= 0) {
5759 		    FormatResult(interp, "bad count \"%d\": must be > 0",
5760 			    count);
5761 		    return TCL_ERROR;
5762 		}
5763 		break;
5764 	    case OPT_ENABLED:
5765 		if (Tcl_GetBooleanFromObj(interp, objv[i + 1], &enabled)
5766 			!= TCL_OK) {
5767 		    return TCL_ERROR;
5768 		}
5769 		break;
5770 	    case OPT_HEIGHT:
5771 		if (Tk_GetPixelsFromObj(interp, tree->tkwin, objv[i + 1],
5772 			&height) != TCL_OK)
5773 		    return TCL_ERROR;
5774 		if (height < 0) {
5775 		    FormatResult(interp, "bad screen distance \"%s\": must be > 0",
5776 			    Tcl_GetString(objv[i + 1]));
5777 		    return TCL_ERROR;
5778 		}
5779 		break;
5780 	    case OPT_NEXTSIBLING:
5781 		if (TreeItem_FromObj(tree, objv[i + 1], &nextSibling,
5782 			IFO_NOT_NULL | IFO_NOT_ROOT | IFO_NOT_ORPHAN) != TCL_OK) {
5783 		    return TCL_ERROR;
5784 		}
5785 		parent = prevSibling = NULL;
5786 		break;
5787 	    case OPT_OPEN:
5788 		if (Tcl_GetBooleanFromObj(interp, objv[i + 1], &open)
5789 			!= TCL_OK) {
5790 		    return TCL_ERROR;
5791 		}
5792 		break;
5793 	    case OPT_PARENT:
5794 		if (TreeItem_FromObj(tree, objv[i + 1], &parent, IFO_NOT_NULL) != TCL_OK) {
5795 		    return TCL_ERROR;
5796 		}
5797 		prevSibling = nextSibling = NULL;
5798 		break;
5799 	    case OPT_PREVSIBLING:
5800 		if (TreeItem_FromObj(tree, objv[i + 1], &prevSibling,
5801 			IFO_NOT_NULL | IFO_NOT_ROOT | IFO_NOT_ORPHAN) != TCL_OK) {
5802 		    return TCL_ERROR;
5803 		}
5804 		parent = nextSibling = NULL;
5805 		break;
5806 	    case OPT_RETURNID:
5807 		if (Tcl_GetBooleanFromObj(interp, objv[i + 1], &returnId)
5808 			!= TCL_OK) {
5809 		    return TCL_ERROR;
5810 		}
5811 		break;
5812 	    case OPT_TAGS:
5813 		tagsObj = objv[i + 1];
5814 		break;
5815 	    case OPT_VISIBLE:
5816 		if (Tcl_GetBooleanFromObj(interp, objv[i + 1], &visible)
5817 			!= TCL_OK) {
5818 		    return TCL_ERROR;
5819 		}
5820 		break;
5821 	    case OPT_WRAP:
5822 		if (Tcl_GetBooleanFromObj(interp, objv[i + 1], &wrap)
5823 			!= TCL_OK) {
5824 		    return TCL_ERROR;
5825 		}
5826 		break;
5827 	}
5828     }
5829 
5830     /* Do it here so I don't have to free it above if an error occurs. */
5831     if (tagsObj != NULL) {
5832 	if (TagInfo_FromObj(tree, tagsObj, &tagInfo) != TCL_OK)
5833 	    return TCL_ERROR;
5834     }
5835 
5836     if (returnId)
5837 	listObj = Tcl_NewListObj(0, NULL);
5838 
5839     /* Don't allow non-deleted items to become children of a
5840     * deleted item. */
5841     if ((parent && IS_DELETED(parent)) ||
5842 	(prevSibling && IS_DELETED(prevSibling->parent)) ||
5843 	(nextSibling && IS_DELETED(nextSibling->parent)))
5844 	parent = prevSibling = nextSibling = NULL;
5845 
5846     for (i = 0; i < count; i++) {
5847 	item = Item_Alloc(tree, FALSE);
5848 	item->flags &= ~(ITEM_FLAG_BUTTON | ITEM_FLAG_BUTTON_AUTO);
5849 	item->flags |= button;
5850 	if (enabled) item->state |= STATE_ITEM_ENABLED;
5851 	else item->state &= ~STATE_ITEM_ENABLED;
5852 	if (open) item->state |= STATE_ITEM_OPEN;
5853 	else item->state &= ~STATE_ITEM_OPEN;
5854 	if (visible) item->flags |= ITEM_FLAG_VISIBLE;
5855 	else item->flags &= ~ITEM_FLAG_VISIBLE;
5856 	if (wrap) item->flags |= ITEM_FLAG_WRAP;
5857 	else item->flags &= ~ITEM_FLAG_WRAP;
5858 	item->fixedHeight = height;
5859 
5860 	/* Apply each column's -itemstyle option. */
5861 	for (treeColumn = tree->columns; treeColumn != NULL;
5862 		treeColumn = TreeColumn_Next(treeColumn)) {
5863 	    TreeStyle style = TreeColumn_ItemStyle(treeColumn);
5864 	    if (style != NULL) {
5865 		TreeItemColumn column = Item_CreateColumn(tree, item,
5866 			TreeColumn_Index(treeColumn), NULL);
5867 		column->style = TreeStyle_NewInstance(tree, style);
5868 	    }
5869 	}
5870 #ifdef DEPRECATED
5871 	/* Apply default styles */
5872 	if (tree->defaultStyle.numStyles) {
5873 	    int i, n = MIN(tree->columnCount, tree->defaultStyle.numStyles);
5874 
5875 	    for (i = 0; i < n; i++) {
5876 		TreeItemColumn column = Item_CreateColumn(tree, item, i, NULL);
5877 		if (column->style != NULL)
5878 		    continue;
5879 		if (tree->defaultStyle.styles[i] != NULL) {
5880 		    column->style = TreeStyle_NewInstance(tree,
5881 			    tree->defaultStyle.styles[i]);
5882 		}
5883 	    }
5884 	}
5885 #endif /* DEPRECATED */
5886 
5887 	if (tagInfo != NULL) {
5888 	    if (count == 1) {
5889 		item->tagInfo = tagInfo;
5890 		tagInfo = NULL;
5891 	    } else {
5892 		item->tagInfo = TagInfo_Copy(tree, tagInfo);
5893 	    }
5894 	}
5895 
5896 	/* Link the new items together as siblings */
5897 	if (parent || prevSibling || nextSibling) {
5898 	    if (head == NULL)
5899 		head = item;
5900 	    if (tail != NULL) {
5901 		tail->nextSibling = item;
5902 		item->prevSibling = tail;
5903 	    }
5904 	    tail = item;
5905 	}
5906 
5907 	if (returnId)
5908 	    Tcl_ListObjAppendElement(interp, listObj, TreeItem_ToObj(tree,
5909 		    item));
5910     }
5911 
5912     if (parent != NULL) {
5913 	head->prevSibling = parent->lastChild;
5914 	if (parent->lastChild != NULL)
5915 	    parent->lastChild->nextSibling = head;
5916 	else
5917 	    parent->firstChild = head;
5918 	parent->lastChild = tail;
5919     } else if (prevSibling != NULL) {
5920 	parent = prevSibling->parent;
5921 	if (prevSibling->nextSibling != NULL)
5922 	    prevSibling->nextSibling->prevSibling = tail;
5923 	else
5924 	    parent->lastChild = tail;
5925 	head->prevSibling = prevSibling;
5926 	tail->nextSibling = prevSibling->nextSibling;
5927 	prevSibling->nextSibling = head;
5928     } else if (nextSibling != NULL) {
5929 	parent = nextSibling->parent;
5930 	if (nextSibling->prevSibling != NULL)
5931 	    nextSibling->prevSibling->nextSibling = head;
5932 	else
5933 	    parent->firstChild = head;
5934 	head->prevSibling = nextSibling->prevSibling;
5935 	tail->nextSibling = nextSibling;
5936 	nextSibling->prevSibling = tail;
5937     }
5938 
5939     if (parent != NULL) {
5940 	for (item = head; item != NULL; item = item->nextSibling) {
5941 	    item->parent = parent;
5942 	    item->depth = parent->depth + 1;
5943 	}
5944 	parent->numChildren += count;
5945 	TreeItem_AddToParent(tree, head);
5946     }
5947 
5948     TagInfo_Free(tree, tagInfo);
5949 
5950     if (returnId)
5951 	Tcl_SetObjResult(interp, listObj);
5952 
5953     return TCL_OK;
5954 }
5955 
5956 /*
5957  *----------------------------------------------------------------------
5958  *
5959  * ItemElementCmd --
5960  *
5961  *	This procedure is invoked to process the [item element] widget
5962  *	command.  See the user documentation for details on what
5963  *	it does.
5964  *
5965  * Results:
5966  *	A standard Tcl result.
5967  *
5968  * Side effects:
5969  *	See the user documentation.
5970  *
5971  *----------------------------------------------------------------------
5972  */
5973 
5974 int
TreeItemCmd_Element(TreeCtrl * tree,int objc,Tcl_Obj * CONST objv[],int doHeaders)5975 TreeItemCmd_Element(
5976     TreeCtrl *tree,
5977     int objc,			/* Number of arguments. */
5978     Tcl_Obj *CONST objv[],	/* Argument values. */
5979     int doHeaders		/* TRUE to operate on headers, FALSE
5980 				 * to operate on items. */
5981     )
5982 {
5983     Tcl_Interp *interp = tree->interp;
5984     static CONST char *commandNames[] = {
5985 #ifdef DEPRECATED
5986 	"actual",
5987 #endif
5988 	"cget", "configure",
5989 	"perstate", (char *) NULL };
5990     enum {
5991 #ifdef DEPRECATED
5992     COMMAND_ACTUAL,
5993 #endif
5994     COMMAND_CGET, COMMAND_CONFIGURE, COMMAND_PERSTATE };
5995     int index;
5996     int columnIndex;
5997     TreeItemColumn column;
5998     TreeItemList itemList;
5999     TreeItem item;
6000     int flags = IFO_NOT_NULL;
6001     int result = TCL_OK;
6002     int tailFlag = doHeaders ? 0 : CFO_NOT_TAIL; /* styles allowed in tail? */
6003     int domain = doHeaders ? STATE_DOMAIN_HEADER : STATE_DOMAIN_ITEM;
6004 
6005     if (objc < 7) {
6006 	Tcl_WrongNumArgs(interp, 3, objv,
6007 	    doHeaders ?
6008 		"command header column element ?arg ...?" :
6009 		"command item column element ?arg ...?");
6010 	return TCL_ERROR;
6011     }
6012 
6013     if (Tcl_GetIndexFromObj(interp, objv[3], commandNames, "command", 0,
6014 		&index) != TCL_OK)
6015 	return TCL_ERROR;
6016 
6017     /*
6018      * [configure] without an option-value pair can operate on a single item
6019      * only. [cget] and [perstate] only operate on a single item.
6020      */
6021     if ((index != COMMAND_CONFIGURE) || (objc < 9))
6022 	flags |= IFO_NOT_MANY;
6023 
6024     if (doHeaders) {
6025 	if (TreeHeaderList_FromObj(tree, objv[4], &itemList, flags) != TCL_OK) {
6026 	    return TCL_ERROR;
6027 	}
6028     } else {
6029 	if (TreeItemList_FromObj(tree, objv[4], &itemList, flags) != TCL_OK)
6030 	    return TCL_ERROR;
6031     }
6032     item = TreeItemList_Nth(&itemList, 0);
6033 
6034     switch (index) {
6035 	/* T item element perstate I C E option ?stateList? */
6036 #ifdef DEPRECATED
6037 	case COMMAND_ACTUAL:
6038 #endif
6039 	case COMMAND_PERSTATE: {
6040 	    int state;
6041 
6042 	    if (objc < 8 || objc > 9) {
6043 		Tcl_WrongNumArgs(tree->interp, 4, objv,
6044 		    doHeaders ?
6045 			"header column element option ?stateList?" :
6046 			"item column element option ?stateList?");
6047 		result = TCL_ERROR;
6048 		break;
6049 	    }
6050 	    if (TreeItem_ColumnFromObj(tree, item, objv[5], &column,
6051 		    NULL, &columnIndex, CFO_NOT_NULL | CFO_NOT_TAIL) != TCL_OK) {
6052 		result = TCL_ERROR;
6053 		break;
6054 	    }
6055 	    if ((column == NULL) || (column->style == NULL) || TreeStyle_IsHeaderStyle(tree, column->style)) {
6056 		NoStyleMsg(tree, item, columnIndex);
6057 		result = TCL_ERROR;
6058 		break;
6059 	    }
6060 	    state = item->state | column->cstate;
6061 	    if (objc == 9) {
6062 		int states[3];
6063 
6064 		if (Tree_StateFromListObj(tree, domain, objv[8], states,
6065 			    SFO_NOT_OFF | SFO_NOT_TOGGLE) != TCL_OK) {
6066 		    result = TCL_ERROR;
6067 		    break;
6068 		}
6069 		state = states[STATE_OP_ON];
6070 	    }
6071 	    result = TreeStyle_ElementActual(tree, column->style,
6072 		    state, objv[6], objv[7]);
6073 	    break;
6074 	}
6075 
6076 	/* T item element cget I C E option */
6077 	case COMMAND_CGET: {
6078 	    if (objc != 8) {
6079 		Tcl_WrongNumArgs(tree->interp, 4, objv,
6080 		    doHeaders ?
6081 			"header column element option" :
6082 			"item column element option");
6083 		result = TCL_ERROR;
6084 		break;
6085 	    }
6086 	    if (TreeItem_ColumnFromObj(tree, item, objv[5], &column,
6087 		    NULL, &columnIndex, CFO_NOT_NULL | CFO_NOT_TAIL) != TCL_OK) {
6088 		result = TCL_ERROR;
6089 		break;
6090 	    }
6091 	    if ((column == NULL) || (column->style == NULL) || TreeStyle_IsHeaderStyle(tree, column->style)) {
6092 		NoStyleMsg(tree, item, columnIndex);
6093 		result = TCL_ERROR;
6094 		break;
6095 	    }
6096 	    result = TreeStyle_ElementCget(tree, item,
6097 		    column, column->style, objv[6], objv[7]);
6098 	    break;
6099 	}
6100 
6101 	/* T item element configure I C E ... */
6102 	case COMMAND_CONFIGURE: {
6103 	    struct columnObj {
6104 		TreeColumnList columns;
6105 		int isColumn;
6106 		int numArgs;
6107 	    } staticCO[STATIC_SIZE], *co = staticCO;
6108 	    int i, index, indexElem, prevColumn;
6109 	    ItemForEach iter;
6110 
6111 	    /* If no option-value pair is given, we can't specify more than
6112 	     * one column. */
6113 	    flags = CFO_NOT_NULL | tailFlag;
6114 	    if (objc < 9)
6115 		flags |= CFO_NOT_MANY;
6116 
6117 	    STATIC_ALLOC(co, struct columnObj, objc);
6118 	    for (i = 5; i < objc; i++) {
6119 		co[i].isColumn = FALSE;
6120 		co[i].numArgs = -1;
6121 	    }
6122 	    indexElem = 6;
6123 
6124 	    /* Get the first column(s) */
6125 	    i = indexElem - 1;
6126 	    if (TreeColumnList_FromObj(tree, objv[i], &co[i].columns,
6127 		    flags) != TCL_OK) {
6128 		result = TCL_ERROR;
6129 		break;
6130 	    }
6131 	    co[i].isColumn = TRUE;
6132 	    prevColumn = i;
6133 
6134 	    while (1) {
6135 		int numArgs = 0;
6136 		char breakChar = '\0';
6137 
6138 		/* Look for a + or , */
6139 		for (index = indexElem + 1; index < objc; index++) {
6140 		    if (numArgs % 2 == 0) {
6141 			int length;
6142 			char *s = Tcl_GetStringFromObj(objv[index], &length);
6143 
6144 			if ((length == 1) && ((s[0] == '+') || (s[0] == ','))) {
6145 			    breakChar = s[0];
6146 			    break;
6147 			}
6148 		    }
6149 		    numArgs++;
6150 		}
6151 
6152 		/* Require at least one option-value pair if more than one
6153 		* element is specified. */
6154 		if ((breakChar || indexElem != 6) && (numArgs < 2)) {
6155 		    FormatResult(interp,
6156 			"missing option-value pair after element \"%s\"",
6157 			Tcl_GetString(objv[indexElem]));
6158 		    result = TCL_ERROR;
6159 		    goto doneCONF;
6160 		}
6161 
6162 		co[indexElem].numArgs = numArgs;
6163 
6164 		if (!breakChar)
6165 		    break;
6166 
6167 		if (index == objc - 1) {
6168 		    FormatResult(interp, "missing %s after \"%c\"",
6169 			(breakChar == '+') ? "element name" : "column",
6170 			breakChar);
6171 		    result = TCL_ERROR;
6172 		    goto doneCONF;
6173 		}
6174 
6175 		/* + indicates start of another element */
6176 		if (breakChar == '+') {
6177 		    indexElem = index + 1;
6178 		}
6179 
6180 		/* , indicates start of another column */
6181 		else if (breakChar == ',') {
6182 		    co[prevColumn].numArgs = index - prevColumn;
6183 
6184 		    if (TreeColumnList_FromObj(tree, objv[index + 1],
6185 			    &co[index + 1].columns, CFO_NOT_NULL |
6186 			    tailFlag) != TCL_OK) {
6187 			result = TCL_ERROR;
6188 			goto doneCONF;
6189 		    }
6190 		    co[index + 1].isColumn = TRUE;
6191 		    prevColumn = index + 1;
6192 
6193 		    indexElem = index + 2;
6194 		    if (indexElem == objc) {
6195 			FormatResult(interp,
6196 			    "missing element name after column \"%s\"",
6197 			    Tcl_GetString(objv[index + 1]));
6198 			result = TCL_ERROR;
6199 			goto doneCONF;
6200 		    }
6201 		}
6202 	    }
6203 	    co[prevColumn].numArgs = index - prevColumn;
6204 
6205 	    ITEM_FOR_EACH(item, &itemList, NULL, &iter) {
6206 		/* T item element configure I C E option value \
6207 		*     + E option value , C E option value */
6208 		int iMask = 0;
6209 
6210 		/* co[index].numArgs is the number of arguments from the C
6211 		 * to the next separator (but not including that separator). */
6212 		for (index = 5; index < objc; index += co[index].numArgs + 1) {
6213 		    ColumnForEach citer;
6214 		    TreeColumn treeColumn;
6215 #ifdef TREECTRL_DEBUG
6216 if (!co[index].isColumn) panic("isColumn == FALSE");
6217 #endif
6218 		    COLUMN_FOR_EACH(treeColumn, &co[index].columns, NULL, &citer) {
6219 			int columnIndex, cMask = 0;
6220 
6221 			columnIndex = TreeColumn_Index(treeColumn);
6222 			column = TreeItem_FindColumn(tree, item, columnIndex);
6223 			if ((column == NULL) || (column->style == NULL) || TreeStyle_IsHeaderStyle(tree, column->style)) {
6224 			    NoStyleMsg(tree, item, columnIndex);
6225 			    result = TCL_ERROR;
6226 			    break;
6227 			}
6228 
6229 			indexElem = index + 1;
6230 
6231 			/* Do each element in this column */
6232 			while (1) {
6233 			    int eMask, index2;
6234 #ifdef TREECTRL_DEBUG
6235 if (co[indexElem].numArgs == -1) panic("indexElem=%d (%s) objc=%d numArgs == -1", indexElem, Tcl_GetString(objv[indexElem]), objc);
6236 #endif
6237 			    result = TreeStyle_ElementConfigureFromObj(tree, item,
6238 				    column, column->style, objv[indexElem],
6239 				    co[indexElem].numArgs, (Tcl_Obj **) objv + indexElem + 1, &eMask);
6240 			    if (result != TCL_OK)
6241 				break;
6242 
6243 			    cMask |= eMask;
6244 
6245 			    /* co[indexElem].numArgs is the number of
6246 			     * option-value arguments after the element. */
6247 			    index2 = indexElem + co[indexElem].numArgs;
6248 			    if (index2 == objc - 1)
6249 				break;
6250 
6251 			    /* Skip the '+' or ',' */
6252 			    index2 += 2;
6253 
6254 			    if (co[index2].isColumn)
6255 				break;
6256 
6257 			    indexElem = index2;
6258 			}
6259 
6260 			if (cMask & CS_LAYOUT) {
6261 			    TreeItemColumn_InvalidateSize(tree, column);
6262 			    TreeColumns_InvalidateWidthOfItems(tree, treeColumn);
6263 			} else if (cMask & CS_DISPLAY) {
6264 			    Tree_InvalidateItemDInfo(tree, treeColumn, item, NULL);
6265 			}
6266 			iMask |= cMask;
6267 			if (result != TCL_OK)
6268 			    break;
6269 		    }
6270 		    if (result != TCL_OK)
6271 			break;
6272 		}
6273 		if (iMask & CS_LAYOUT) {
6274 		    TreeItem_InvalidateHeight(tree, item);
6275 		    Tree_FreeItemDInfo(tree, item, NULL);
6276 		    if (item->header == NULL)
6277 			Tree_DInfoChanged(tree, DINFO_REDO_RANGES);
6278 		} else if (iMask & CS_DISPLAY) {
6279 		}
6280 		if (result != TCL_OK)
6281 		    break;
6282 	    }
6283 doneCONF:
6284 	    for (i = 5; i < objc; i++) {
6285 		if (co[i].isColumn)
6286 		    TreeColumnList_Free(&co[i].columns);
6287 	    }
6288 	    STATIC_FREE(co, struct columnObj, objc);
6289 	    break;
6290 	}
6291     }
6292 
6293     TreeItemList_Free(&itemList);
6294     return result;
6295 }
6296 
6297 static int
ItemElementCmd(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])6298 ItemElementCmd(
6299     ClientData clientData,	/* Widget info. */
6300     Tcl_Interp *interp,		/* Current interpreter. */
6301     int objc,			/* Number of arguments. */
6302     Tcl_Obj *CONST objv[]	/* Argument values. */
6303     )
6304 {
6305     TreeCtrl *tree = clientData;
6306     return TreeItemCmd_Element(tree, objc, objv, FALSE);
6307 }
6308 
6309 /*
6310  *----------------------------------------------------------------------
6311  *
6312  * ItemStyleCmd --
6313  *
6314  *	This procedure is invoked to process the [item style] widget
6315  *	command.  See the user documentation for details on what
6316  *	it does.
6317  *
6318  * Results:
6319  *	A standard Tcl result.
6320  *
6321  * Side effects:
6322  *	See the user documentation.
6323  *
6324  *----------------------------------------------------------------------
6325  */
6326 
6327 int
TreeItemCmd_Style(TreeCtrl * tree,int objc,Tcl_Obj * CONST objv[],int doHeaders)6328 TreeItemCmd_Style(
6329     TreeCtrl *tree,		/* Widget info. */
6330     int objc,			/* Number of arguments. */
6331     Tcl_Obj *CONST objv[],	/* Argument values. */
6332     int doHeaders		/* TRUE to operate on headers, FALSE
6333 				 * to operate on items. */
6334     )
6335 {
6336     Tcl_Interp *interp = tree->interp;
6337     int domain = doHeaders ? STATE_DOMAIN_HEADER : STATE_DOMAIN_ITEM;
6338     int index;
6339     TreeItemList itemList;
6340     TreeItem item;
6341     int flags = IFO_NOT_NULL;
6342     int result = TCL_OK;
6343     int tailFlag = doHeaders ? 0 : CFO_NOT_TAIL;
6344     static CONST char *commandNames[] = {
6345 	"elements", "map", "set", (char *) NULL
6346     };
6347     enum { COMMAND_ELEMENTS, COMMAND_MAP, COMMAND_SET };
6348 
6349     if (objc < 5) {
6350 	Tcl_WrongNumArgs(interp, 3, objv,
6351 	    doHeaders ?
6352 		"command header ?arg ...?" :
6353 		"command item ?arg ...?");
6354 	return TCL_ERROR;
6355     }
6356 
6357     if (Tcl_GetIndexFromObj(interp, objv[3], commandNames, "command", 0,
6358 		&index) != TCL_OK) {
6359 	return TCL_ERROR;
6360     }
6361 
6362     /* [style elements] only works on a single item.
6363      * [style set] only works on a single item without a column-style pair. */
6364     if ((index == COMMAND_ELEMENTS) || (index == COMMAND_SET && objc < 7))
6365 	flags |= IFO_NOT_MANY;
6366     if (doHeaders) {
6367 	if (TreeHeaderList_FromObj(tree, objv[4], &itemList, flags) != TCL_OK) {
6368 	    return TCL_ERROR;
6369 	}
6370     } else {
6371 	if (TreeItemList_FromObj(tree, objv[4], &itemList, flags) != TCL_OK) {
6372 	    return TCL_ERROR;
6373 	}
6374     }
6375     item = TreeItemList_Nth(&itemList, 0);
6376 
6377     switch (index) {
6378 	/* T item style elements I C */
6379 	case COMMAND_ELEMENTS: {
6380 	    TreeItemColumn column;
6381 	    int columnIndex;
6382 
6383 	    if (objc != 6) {
6384 		Tcl_WrongNumArgs(interp, 4, objv,
6385 		    doHeaders ? "header column" : "item column");
6386 		result = TCL_ERROR;
6387 		break;
6388 	    }
6389 	    if (TreeItem_ColumnFromObj(tree, item, objv[5], &column,
6390 		    NULL, &columnIndex, CFO_NOT_NULL | CFO_NOT_TAIL) != TCL_OK) {
6391 		result = TCL_ERROR;
6392 		break;
6393 	    }
6394 	    if ((column == NULL) || (column->style == NULL) || TreeStyle_IsHeaderStyle(tree, column->style)) {
6395 		NoStyleMsg(tree, item, columnIndex);
6396 		result = TCL_ERROR;
6397 		break;
6398 	    }
6399 	    TreeStyle_ListElements(tree, column->style);
6400 	    break;
6401 	}
6402 
6403 	/* T item style map I C S map */
6404 	case COMMAND_MAP: {
6405 	    TreeStyle style;
6406 	    TreeColumnList columns;
6407 	    TreeColumn treeColumn;
6408 	    TreeItemColumn column;
6409 	    int columnIndex;
6410 	    int objcM;
6411 	    Tcl_Obj **objvM;
6412 	    ItemForEach iter;
6413 	    ColumnForEach citer;
6414 	    int redoRanges = !doHeaders;
6415 
6416 	    if (objc != 8) {
6417 		Tcl_WrongNumArgs(interp, 4, objv,
6418 		    doHeaders ?
6419 			"header column style map" :
6420 			"item column style map");
6421 		return TCL_ERROR;
6422 	    }
6423 	    if (TreeColumnList_FromObj(tree, objv[5], &columns,
6424 		    CFO_NOT_NULL | tailFlag) != TCL_OK) {
6425 		result = TCL_ERROR;
6426 		break;
6427 	    }
6428 	    if (TreeStyle_FromObj(tree, objv[6], &style) != TCL_OK) {
6429 		result = TCL_ERROR;
6430 		goto doneMAP;
6431 	    }
6432 	    if (TreeStyle_GetStateDomain(tree, style) != domain) {
6433 		StateDomainErrMsg(tree, item, style);
6434 		result = TCL_ERROR;
6435 		goto doneMAP;
6436 	    }
6437 	    if (Tcl_ListObjGetElements(interp, objv[7], &objcM, &objvM)
6438 		    != TCL_OK) {
6439 		result = TCL_ERROR;
6440 		goto doneMAP;
6441 	    }
6442 	    if (objcM & 1) {
6443 		FormatResult(interp, "list must contain even number of elements");
6444 		result = TCL_ERROR;
6445 		goto doneMAP;
6446 	    }
6447 	    ITEM_FOR_EACH(item, &itemList, NULL, &iter) {
6448 		COLUMN_FOR_EACH(treeColumn, &columns, NULL, &citer) {
6449 		    columnIndex = TreeColumn_Index(treeColumn);
6450 		    column = Item_CreateColumn(tree, item, columnIndex, NULL);
6451 		    if ((column->style != NULL) && !TreeStyle_IsHeaderStyle(tree, column->style)) {
6452 			if (TreeStyle_Remap(tree, column->style, style, objcM,
6453 				objvM) != TCL_OK) {
6454 			    result = TCL_ERROR;
6455 			    break;
6456 			}
6457 		    } else {
6458 			TreeItemColumn_ForgetStyle(tree, column);
6459 			column->style = TreeStyle_NewInstance(tree, style);
6460 		    }
6461 		    TreeItemColumn_InvalidateSize(tree, column);
6462 		    TreeColumns_InvalidateWidthOfItems(tree, treeColumn);
6463 		}
6464 		TreeItem_InvalidateHeight(tree, item);
6465 		Tree_FreeItemDInfo(tree, item, NULL);
6466 		if (result != TCL_OK)
6467 		    break;
6468 	    }
6469 	    if (redoRanges)
6470 		Tree_DInfoChanged(tree, DINFO_REDO_RANGES);
6471 doneMAP:
6472 	    TreeColumnList_Free(&columns);
6473 	    break;
6474 	}
6475 
6476 	/* T item style set I ?C? ?S? ?C S ...?*/
6477 	case COMMAND_SET: {
6478 	    struct columnStyle {
6479 		TreeColumnList columns;
6480 		TreeStyle style;
6481 	    };
6482 	    struct columnStyle staticCS[STATIC_SIZE], *cs = staticCS;
6483 	    TreeColumn treeColumn;
6484 	    TreeItemColumn column;
6485 	    int i, count = 0, length, changed = FALSE, changedI;
6486 	    ItemForEach iter;
6487 	    ColumnForEach citer;
6488 
6489 	    if (objc < 5) {
6490 		Tcl_WrongNumArgs(interp, 4, objv,
6491 		    doHeaders ?
6492 			"header ?column? ?style? ?column style ...?" :
6493 			"item ?column? ?style? ?column style ...?");
6494 		return TCL_ERROR;
6495 	    }
6496 	    /* Return list of styles. */
6497 	    if (objc == 5) {
6498 		Tcl_Obj *listObj = Tcl_NewListObj(0, NULL);
6499 		int tailOK = item->header != NULL;
6500 		treeColumn = Tree_FirstColumn(tree, -1, tailOK);
6501 		column = item->columns;
6502 		while (treeColumn != NULL) {
6503 		    if ((column != NULL) && (column->style != NULL) && !TreeStyle_IsHeaderStyle(tree, column->style))
6504 			Tcl_ListObjAppendElement(interp, listObj,
6505 				TreeStyle_ToObj(TreeStyle_GetMaster(
6506 				tree, column->style)));
6507 		    else
6508 			Tcl_ListObjAppendElement(interp, listObj,
6509 				Tcl_NewObj());
6510 		    treeColumn = Tree_ColumnToTheRight(treeColumn, FALSE, tailOK);
6511 		    if (column != NULL)
6512 			column = column->next;
6513 		}
6514 		Tcl_SetObjResult(interp, listObj);
6515 		break;
6516 	    }
6517 	    /* Return style in one column. */
6518 	    if (objc == 6) {
6519 		if (TreeItem_ColumnFromObj(tree, item, objv[5], &column,
6520 			NULL, NULL, CFO_NOT_NULL | tailFlag) != TCL_OK)
6521 		    return TCL_ERROR;
6522 		if ((column != NULL) && (column->style != NULL) && !TreeStyle_IsHeaderStyle(tree, column->style))
6523 		    Tcl_SetObjResult(interp, TreeStyle_ToObj(
6524 			TreeStyle_GetMaster(tree, column->style)));
6525 		break;
6526 	    }
6527 	    /* Get column/style pairs. */
6528 	    STATIC_ALLOC(cs, struct columnStyle, objc / 2);
6529 	    for (i = 5; i < objc; i += 2) {
6530 		if (TreeColumnList_FromObj(tree, objv[i], &cs[count].columns,
6531 			CFO_NOT_NULL | tailFlag) != TCL_OK) {
6532 		    result = TCL_ERROR;
6533 		    goto doneSET;
6534 		}
6535 		if (i + 1 == objc) {
6536 		    FormatResult(interp, "missing style for column \"%s\"",
6537 			Tcl_GetString(objv[i]));
6538 		    result = TCL_ERROR;
6539 		    goto doneSET;
6540 		}
6541 		(void) Tcl_GetStringFromObj(objv[i + 1], &length);
6542 		if (length == 0) {
6543 		    cs[count].style = NULL;
6544 		} else {
6545 		    if (TreeStyle_FromObj(tree, objv[i + 1], &cs[count].style)
6546 			    != TCL_OK) {
6547 			result = TCL_ERROR;
6548 			goto doneSET;
6549 		    }
6550 		    if (TreeStyle_GetStateDomain(tree, cs[count].style) != domain) {
6551 			StateDomainErrMsg(tree, item, cs[count].style);
6552 			result = TCL_ERROR;
6553 			goto doneSET;
6554 		    }
6555 		}
6556 		count++;
6557 	    }
6558 	    ITEM_FOR_EACH(item, &itemList, NULL, &iter) {
6559 		changedI = FALSE;
6560 		for (i = 0; i < count; i++) {
6561 		    COLUMN_FOR_EACH(treeColumn, &cs[i].columns, NULL, &citer) {
6562 			if (cs[i].style == NULL) {
6563 			    column = TreeItem_FindColumn(tree, item,
6564 				    TreeColumn_Index(treeColumn));
6565 			    if (column == NULL || column->style == NULL ||
6566 				    TreeStyle_IsHeaderStyle(tree, column->style))
6567 				continue;
6568 			    TreeItemColumn_ForgetStyle(tree, column);
6569 if (doHeaders) TreeHeaderColumn_EnsureStyleExists(item->header, column->headerColumn, treeColumn);
6570 			} else {
6571 			    column = Item_CreateColumn(tree, item,
6572 				    TreeColumn_Index(treeColumn), NULL);
6573 			    if (column->style != NULL) {
6574 				if (TreeStyle_GetMaster(tree, column->style)
6575 					== cs[i].style)
6576 				    continue;
6577 				TreeItemColumn_ForgetStyle(tree, column);
6578 			    }
6579 			    column->style = TreeStyle_NewInstance(tree,
6580 				    cs[i].style);
6581 			}
6582 			TreeItemColumn_InvalidateSize(tree, column);
6583 			TreeColumns_InvalidateWidthOfItems(tree, treeColumn);
6584 			changedI = TRUE;
6585 		    }
6586 		    if (changedI) {
6587 			TreeItem_InvalidateHeight(tree, item);
6588 			Tree_FreeItemDInfo(tree, item, NULL);
6589 			changed = TRUE;
6590 		    }
6591 		}
6592 	    }
6593 	    if (changed && !doHeaders)
6594 		Tree_DInfoChanged(tree, DINFO_REDO_RANGES);
6595 doneSET:
6596 	    for (i = 0; i < count; i++) {
6597 		TreeColumnList_Free(&cs[i].columns);
6598 	    }
6599 	    STATIC_FREE(cs, struct columnStyle, objc / 2);
6600 	    break;
6601 	}
6602     }
6603 
6604     TreeItemList_Free(&itemList);
6605     return result;
6606 }
6607 
6608 static int
ItemStyleCmd(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])6609 ItemStyleCmd(
6610     ClientData clientData,	/* Widget info. */
6611     Tcl_Interp *interp,		/* Current interpreter. */
6612     int objc,			/* Number of arguments. */
6613     Tcl_Obj *CONST objv[]	/* Argument values. */
6614     )
6615 {
6616     TreeCtrl *tree = clientData;
6617     return TreeItemCmd_Style(tree, objc, objv, FALSE);
6618 }
6619 
6620 /*
6621  *----------------------------------------------------------------------
6622  *
6623  * TreeItemCmd_ImageOrText --
6624  *
6625  *	This procedure is invoked to process the [item image] and
6626  *	[item text] widget commands.  See the user documentation for
6627  *	details on what it does.
6628  *
6629  * Results:
6630  *	A standard Tcl result.
6631  *
6632  * Side effects:
6633  *	See the user documentation.
6634  *
6635  *----------------------------------------------------------------------
6636  */
6637 
6638 int
TreeItemCmd_ImageOrText(TreeCtrl * tree,int objc,Tcl_Obj * CONST objv[],int doImage,int doHeaders)6639 TreeItemCmd_ImageOrText(
6640     TreeCtrl *tree,		/* Widget info. */
6641     int objc,			/* Number of arguments. */
6642     Tcl_Obj *CONST objv[],	/* Argument values. */
6643     int doImage,		/* TRUE if this is [item image] */
6644     int doHeaders		/* TRUE to operate on headers, FALSE
6645 				 * to operate on items. */
6646     )
6647 {
6648     Tcl_Interp *interp = tree->interp;
6649     TreeColumn treeColumn = tree->columns;
6650     TreeItemList itemList;
6651     TreeItem item;
6652     TreeElement elem = NULL;
6653     TreeItemColumn column;
6654     Tcl_Obj *objPtr;
6655     int isImage = doImage;
6656     struct columnObj {
6657 	TreeColumnList columns;
6658 	Tcl_Obj *obj;
6659     } staticCO[STATIC_SIZE], *co = staticCO;
6660     int i, count = 0, changed = FALSE, columnIndex;
6661     ItemForEach iter;
6662     ColumnForEach citer;
6663     int flags = 0, result = TCL_OK;
6664     int tailFlag = /*doHeaders ? 0 : */CFO_NOT_TAIL;
6665 
6666     /* T item text I ?C? ?text? ?C text ...? */
6667     if (objc < 4) {
6668 	Tcl_WrongNumArgs(interp, 3, objv,
6669 	    doHeaders ?
6670 		"header ?column? ?text? ?column text ...?" :
6671 		"item ?column? ?text? ?column text ...?");
6672 	return TCL_ERROR;
6673     }
6674 
6675     if (objc < 6)
6676 	flags = IFO_NOT_NULL | IFO_NOT_MANY;
6677     if (doHeaders) {
6678 	if (TreeHeaderList_FromObj(tree, objv[3], &itemList, flags)
6679 		!= TCL_OK)
6680 	    return TCL_ERROR;
6681     } else {
6682 	if (TreeItemList_FromObj(tree, objv[3], &itemList, flags)
6683 		!= TCL_OK)
6684 	    return TCL_ERROR;
6685     }
6686     item = TreeItemList_Nth(&itemList, 0);
6687 
6688     if (objc == 4) {
6689 	Tcl_Obj *listObj = Tcl_NewListObj(0, NULL);
6690 	column = item->columns;
6691 	while (treeColumn != NULL) {
6692 	    if ((column != NULL) && (column->style != NULL) &&
6693 		    !TreeStyle_IsHeaderStyle(tree, column->style)) {
6694 		objPtr = isImage ?
6695 		    TreeStyle_GetImage(tree, column->style, &elem) :
6696 		    TreeStyle_GetText(tree, column->style, &elem);
6697 	    } else
6698 		objPtr = NULL;
6699 	    if (doHeaders && elem == NULL)
6700 		objPtr = TreeHeaderColumn_GetImageOrText(item->header,
6701 		    column->headerColumn, isImage);
6702 	    if (objPtr == NULL)
6703 		objPtr = Tcl_NewObj();
6704 	    Tcl_ListObjAppendElement(interp, listObj, objPtr);
6705 	    treeColumn = TreeColumn_Next(treeColumn);
6706 	    if (column != NULL)
6707 		column = column->next;
6708 	}
6709 	Tcl_SetObjResult(interp, listObj);
6710 	goto okExit;
6711     }
6712     if (objc == 5) {
6713 	if (TreeItem_ColumnFromObj(tree, item, objv[4], &column, NULL, NULL,
6714 		CFO_NOT_NULL | CFO_NOT_TAIL) != TCL_OK) {
6715 	    goto errorExit;
6716 	}
6717 	if ((column != NULL) && (column->style != NULL) &&
6718 		!TreeStyle_IsHeaderStyle(tree, column->style)) {
6719 	    objPtr = isImage ?
6720 		TreeStyle_GetImage(tree, column->style, &elem) :
6721 		TreeStyle_GetText(tree, column->style, &elem);
6722 	 } else
6723 	    objPtr = NULL;
6724 	if (doHeaders && elem == NULL)
6725 	    objPtr = TreeHeaderColumn_GetImageOrText(item->header,
6726 		column->headerColumn, isImage);
6727 	if (objPtr != NULL)
6728 	    Tcl_SetObjResult(interp, objPtr);
6729 	goto okExit;
6730     }
6731     if ((objc - 4) & 1) {
6732 	FormatResult(interp, "missing argument after column \"%s\"",
6733 		Tcl_GetString(objv[objc - 1]));
6734 	goto errorExit;
6735     }
6736     /* Gather column/obj pairs. */
6737     STATIC_ALLOC(co, struct columnObj, objc / 2);
6738     for (i = 4; i < objc; i += 2) {
6739 	if (TreeColumnList_FromObj(tree, objv[i], &co[count].columns,
6740 		CFO_NOT_NULL | tailFlag) != TCL_OK) {
6741 	    result = TCL_ERROR;
6742 	    goto doneTEXT;
6743 	}
6744 	co[count].obj = objv[i + 1];
6745 	count++;
6746     }
6747     ITEM_FOR_EACH(item, &itemList, NULL, &iter) {
6748 	int changedI = FALSE;
6749 	for (i = 0; i < count; i++) {
6750 	    COLUMN_FOR_EACH(treeColumn, &co[i].columns, NULL, &citer) {
6751 		columnIndex = TreeColumn_Index(treeColumn);
6752 		column = TreeItem_FindColumn(tree, item, columnIndex);
6753 		if ((column == NULL) || (column->style == NULL) ||
6754 			TreeStyle_IsHeaderStyle(tree, column->style)) {
6755 		    if (doHeaders) {
6756 			result = TreeHeaderColumn_SetImageOrText(item->header,
6757 			    column->headerColumn, treeColumn, co[i].obj, isImage);
6758 			if (result != TCL_OK)
6759 			    goto doneTEXT;
6760 			continue;
6761 		    }
6762 		    NoStyleMsg(tree, item, columnIndex);
6763 		    result = TCL_ERROR;
6764 		    goto doneTEXT;
6765 		}
6766 		result = isImage ?
6767 		    TreeStyle_SetImage(tree, item, column, column->style,
6768 			co[i].obj, &elem) :
6769 		    TreeStyle_SetText(tree, item, column, column->style,
6770 			co[i].obj, &elem);
6771 		if (result != TCL_OK)
6772 		    goto doneTEXT;
6773 		if (elem == NULL) {
6774 		    if (doHeaders) {
6775 			result = TreeHeaderColumn_SetImageOrText(item->header,
6776 			    column->headerColumn, treeColumn, co[i].obj, isImage);
6777 			if (result != TCL_OK)
6778 			    goto doneTEXT;
6779 		    }
6780 		} else {
6781 		    TreeItemColumn_InvalidateSize(tree, column);
6782 		    TreeColumns_InvalidateWidthOfItems(tree, treeColumn);
6783 		    changedI = TRUE;
6784 		}
6785 	    }
6786 	}
6787 	if (changedI) {
6788 	    TreeItem_InvalidateHeight(tree, item);
6789 	    Tree_FreeItemDInfo(tree, item, NULL);
6790 	    changed = TRUE;
6791 	}
6792     }
6793     if (changed && !doHeaders)
6794 	Tree_DInfoChanged(tree, DINFO_REDO_RANGES);
6795 doneTEXT:
6796     for (i = 0; i < count; i++) {
6797 	TreeColumnList_Free(&co[i].columns);
6798     }
6799     STATIC_FREE(co, struct columnObj, objc / 2);
6800     TreeItemList_Free(&itemList);
6801     return result;
6802 
6803 okExit:
6804     TreeItemList_Free(&itemList);
6805     return TCL_OK;
6806 
6807 errorExit:
6808     TreeItemList_Free(&itemList);
6809     return TCL_ERROR;
6810 }
6811 
6812 static int
ItemImageCmd(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])6813 ItemImageCmd(
6814     ClientData clientData,	/* Widget info. */
6815     Tcl_Interp *interp,		/* Current interpreter. */
6816     int objc,			/* Number of arguments. */
6817     Tcl_Obj *CONST objv[]	/* Argument values. */
6818     )
6819 {
6820     TreeCtrl *tree = clientData;
6821     return TreeItemCmd_ImageOrText(tree, objc, objv, TRUE, FALSE);
6822 }
6823 
6824 static int
ItemTextCmd(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])6825 ItemTextCmd(
6826     ClientData clientData,	/* Widget info. */
6827     Tcl_Interp *interp,		/* Current interpreter. */
6828     int objc,			/* Number of arguments. */
6829     Tcl_Obj *CONST objv[]	/* Argument values. */
6830     )
6831 {
6832     TreeCtrl *tree = clientData;
6833     return TreeItemCmd_ImageOrText(tree, objc, objv, FALSE, FALSE);
6834 }
6835 
6836 /* Quicksort is not a "stable" sorting algorithm, but it can become a
6837  * stable sort by using the pre-sort order of two items as a tie-breaker
6838  * for items that would otherwise be considered equal. */
6839 #define STABLE_SORT
6840 
6841 /* one per column per SortItem */
6842 struct SortItem1
6843 {
6844     long longValue;
6845     double doubleValue;
6846     char *string;
6847 };
6848 
6849 /* one per Item */
6850 struct SortItem
6851 {
6852     TreeItem item;
6853     struct SortItem1 *item1;
6854     Tcl_Obj *obj; /* TreeItem_ToObj() */
6855 #ifdef STABLE_SORT
6856     int index; /* The pre-sort order of the item */
6857 #endif
6858 };
6859 
6860 typedef struct SortData SortData;
6861 
6862 /* Used to process -element option */
6863 struct SortElement
6864 {
6865     TreeStyle style;
6866     TreeElement elem;
6867     int elemIndex;
6868 };
6869 
6870 /* One per TreeColumn */
6871 struct SortColumn
6872 {
6873     int (*proc)(SortData *, struct SortItem *, struct SortItem *, int);
6874     int sortBy;
6875     int column;
6876     int order;
6877     Tcl_Obj *command;
6878     struct SortElement elems[20];
6879     int elemCount;
6880 };
6881 
6882 /* Data for sort as a whole */
6883 struct SortData
6884 {
6885     TreeCtrl *tree;
6886     struct SortItem *items;
6887     struct SortItem1 *item1s; /* SortItem.item1 points in here */
6888 #define MAX_SORT_COLUMNS 40
6889     struct SortColumn columns[MAX_SORT_COLUMNS];
6890     int columnCount; /* max number of columns to compare */
6891     int result;
6892 };
6893 
6894 /* from Tcl 8.4.0 */
6895 static int
DictionaryCompare(char * left,char * right)6896 DictionaryCompare(
6897     char *left,
6898     char *right
6899     )
6900 {
6901     Tcl_UniChar uniLeft, uniRight, uniLeftLower, uniRightLower;
6902     int diff, zeros;
6903     int secondaryDiff = 0;
6904 
6905     while (1) {
6906 	if (isdigit(UCHAR(*right)) && isdigit(UCHAR(*left))) { /* INTL: digit */
6907 	    /*
6908 	     * There are decimal numbers embedded in the two
6909 	     * strings.  Compare them as numbers, rather than
6910 	     * strings.  If one number has more leading zeros than
6911 	     * the other, the number with more leading zeros sorts
6912 	     * later, but only as a secondary choice.
6913 	     */
6914 
6915 	    zeros = 0;
6916 	    while ((*right == '0') && (isdigit(UCHAR(right[1])))) {
6917 		right++;
6918 		zeros--;
6919 	    }
6920 	    while ((*left == '0') && (isdigit(UCHAR(left[1])))) {
6921 		left++;
6922 		zeros++;
6923 	    }
6924 	    if (secondaryDiff == 0) {
6925 		secondaryDiff = zeros;
6926 	    }
6927 
6928 	    /*
6929 	     * The code below compares the numbers in the two
6930 	     * strings without ever converting them to integers.  It
6931 	     * does this by first comparing the lengths of the
6932 	     * numbers and then comparing the digit values.
6933 	     */
6934 
6935 	    diff = 0;
6936 	    while (1) {
6937 		if (diff == 0) {
6938 		    diff = UCHAR(*left) - UCHAR(*right);
6939 		}
6940 		right++;
6941 		left++;
6942 		if (!isdigit(UCHAR(*right))) {	/* INTL: digit */
6943 		    if (isdigit(UCHAR(*left))) {	/* INTL: digit */
6944 			return 1;
6945 		    } else {
6946 			/*
6947 			 * The two numbers have the same length. See
6948 			 * if their values are different.
6949 			 */
6950 
6951 			if (diff != 0) {
6952 			    return diff;
6953 			}
6954 			break;
6955 		    }
6956 		} else if (!isdigit(UCHAR(*left))) {	/* INTL: digit */
6957 		    return -1;
6958 		}
6959 	    }
6960 	    continue;
6961 	}
6962 
6963 	/*
6964 	 * Convert character to Unicode for comparison purposes.  If either
6965 	 * string is at the terminating null, do a byte-wise comparison and
6966 	 * bail out immediately.
6967 	 */
6968 
6969 	if ((*left != '\0') && (*right != '\0')) {
6970 	    left += Tcl_UtfToUniChar(left, &uniLeft);
6971 	    right += Tcl_UtfToUniChar(right, &uniRight);
6972 	    /*
6973 	     * Convert both chars to lower for the comparison, because
6974 	     * dictionary sorts are case insensitve.  Covert to lower, not
6975 	     * upper, so chars between Z and a will sort before A (where most
6976 	     * other interesting punctuations occur)
6977 	     */
6978 	    uniLeftLower = Tcl_UniCharToLower(uniLeft);
6979 	    uniRightLower = Tcl_UniCharToLower(uniRight);
6980 	} else {
6981 	    diff = UCHAR(*left) - UCHAR(*right);
6982 	    break;
6983 	}
6984 
6985 	diff = uniLeftLower - uniRightLower;
6986 	if (diff) {
6987 	    return diff;
6988 	} else if (secondaryDiff == 0) {
6989 	    if (Tcl_UniCharIsUpper(uniLeft) &&
6990 		    Tcl_UniCharIsLower(uniRight)) {
6991 		secondaryDiff = -1;
6992 	    } else if (Tcl_UniCharIsUpper(uniRight) &&
6993 		    Tcl_UniCharIsLower(uniLeft)) {
6994 		secondaryDiff = 1;
6995 	    }
6996 	}
6997     }
6998     if (diff == 0) {
6999 	diff = secondaryDiff;
7000     }
7001     return diff;
7002 }
7003 
7004 static int
CompareAscii(SortData * sortData,struct SortItem * a,struct SortItem * b,int n)7005 CompareAscii(
7006     SortData *sortData,
7007     struct SortItem *a,
7008     struct SortItem *b,
7009     int n			/* Column index. */
7010     )
7011 {
7012     char *left  = a->item1[n].string;
7013     char *right = b->item1[n].string;
7014 
7015     /* make sure to handle case where no string value has been set */
7016     if (left == NULL) {
7017 	return ((right == NULL) ? 0 : (0 - UCHAR(*right)));
7018     } else if (right == NULL) {
7019 	return UCHAR(*left);
7020     } else {
7021 	return strcmp(left, right);
7022     }
7023 }
7024 
7025 static int
CompareDict(SortData * sortData,struct SortItem * a,struct SortItem * b,int n)7026 CompareDict(
7027     SortData *sortData,
7028     struct SortItem *a,
7029     struct SortItem *b,
7030     int n			/* Column index. */
7031     )
7032 {
7033     char *left  = a->item1[n].string;
7034     char *right = b->item1[n].string;
7035 
7036     /* make sure to handle case where no string value has been set */
7037     if (left == NULL) {
7038 	return ((right == NULL) ? 0 : (0 - UCHAR(*right)));
7039     } else if (right == NULL) {
7040 	return UCHAR(*left);
7041     } else {
7042 	return DictionaryCompare(left, right);
7043     }
7044 }
7045 
7046 static int
CompareDouble(SortData * sortData,struct SortItem * a,struct SortItem * b,int n)7047 CompareDouble(
7048     SortData *sortData,
7049     struct SortItem *a,
7050     struct SortItem *b,
7051     int n			/* Column index. */
7052     )
7053 {
7054     return (a->item1[n].doubleValue < b->item1[n].doubleValue) ? -1 :
7055 	((a->item1[n].doubleValue == b->item1[n].doubleValue) ? 0 : 1);
7056 }
7057 
7058 static int
CompareLong(SortData * sortData,struct SortItem * a,struct SortItem * b,int n)7059 CompareLong(
7060     SortData *sortData,
7061     struct SortItem *a,
7062     struct SortItem *b,
7063     int n			/* Column index. */
7064     )
7065 {
7066     return (a->item1[n].longValue < b->item1[n].longValue) ? -1 :
7067 	((a->item1[n].longValue == b->item1[n].longValue) ? 0 : 1);
7068 }
7069 
7070 static int
CompareCmd(SortData * sortData,struct SortItem * a,struct SortItem * b,int n)7071 CompareCmd(
7072     SortData *sortData,
7073     struct SortItem *a,
7074     struct SortItem *b,
7075     int n			/* Column index. */
7076     )
7077 {
7078     Tcl_Interp *interp = sortData->tree->interp;
7079     Tcl_Obj **objv, *paramObjv[2];
7080     int objc, v;
7081 
7082     paramObjv[0] = a->obj;
7083     paramObjv[1] = b->obj;
7084 
7085     Tcl_ListObjLength(interp, sortData->columns[n].command, &objc);
7086     Tcl_ListObjReplace(interp, sortData->columns[n].command, objc - 2,
7087 	    2, 2, paramObjv);
7088     Tcl_ListObjGetElements(interp, sortData->columns[n].command,
7089 	    &objc, &objv);
7090 
7091     sortData->result = Tcl_EvalObjv(interp, objc, objv, 0);
7092 
7093     if (sortData->result != TCL_OK) {
7094 	Tcl_AddErrorInfo(interp, "\n    (evaluating item sort -command)");
7095 	return 0;
7096     }
7097 
7098     sortData->result = Tcl_GetIntFromObj(interp, Tcl_GetObjResult(interp), &v);
7099     if (sortData->result != TCL_OK) {
7100 	Tcl_ResetResult(interp);
7101 	Tcl_AppendToObj(Tcl_GetObjResult(interp),
7102 		"-command returned non-numeric result", -1);
7103 	return 0;
7104     }
7105 
7106     return v;
7107 }
7108 
7109 static int
CompareProc(SortData * sortData,struct SortItem * a,struct SortItem * b)7110 CompareProc(
7111     SortData *sortData,
7112     struct SortItem *a,
7113     struct SortItem *b
7114     )
7115 {
7116     int i, v;
7117 
7118     if (a->item == b->item)
7119 	return 0;
7120 
7121     for (i = 0; i < sortData->columnCount; i++) {
7122 	v = (*sortData->columns[i].proc)(sortData, a, b, i);
7123 
7124 	/* -command returned error */
7125 	if (sortData->result != TCL_OK)
7126 	    return 0;
7127 
7128 	if (v != 0) {
7129 	    if (i && (sortData->columns[i].order != sortData->columns[0].order))
7130 		v *= -1;
7131 	    return v;
7132 	}
7133     }
7134 #ifdef STABLE_SORT
7135     return ((a->index < b->index) == sortData->columns[0].order) ? -1 : 1;
7136 #else
7137     return 0;
7138 #endif
7139 }
7140 
7141 /* BEGIN custom quicksort() */
7142 
7143 static int
find_pivot(SortData * sortData,struct SortItem * left,struct SortItem * right,struct SortItem * pivot)7144 find_pivot(
7145     SortData *sortData,
7146     struct SortItem *left,
7147     struct SortItem *right,
7148     struct SortItem *pivot
7149     )
7150 {
7151     struct SortItem *a, *b, *c, *p, *tmp;
7152     int v;
7153 
7154     a = left;
7155     b = (left + (right - left) / 2);
7156     c = right;
7157 
7158     /* Arrange a <= b <= c. */
7159     v = CompareProc(sortData, a, b);
7160     if (sortData->result != TCL_OK)
7161 	return 0;
7162     if (v > 0) { tmp = a; a = b; b = tmp; }
7163 
7164     v = CompareProc(sortData, a, c);
7165     if (sortData->result != TCL_OK)
7166 	return 0;
7167     if (v > 0) { tmp = a; a = c; c = tmp; }
7168 
7169     v = CompareProc(sortData, b, c);
7170     if (sortData->result != TCL_OK)
7171 	return 0;
7172     if (v > 0) { tmp = b; b = c; c = tmp; }
7173 
7174     /* if (a < b) pivot = b */
7175     v = CompareProc(sortData, a, b);
7176     if (sortData->result != TCL_OK)
7177 	return 0;
7178     if (v < 0) {
7179 	(*pivot) = *b;
7180 	return 1;
7181     }
7182 
7183     /* if (b < c) pivot = c */
7184     v = CompareProc(sortData, b, c);
7185     if (sortData->result != TCL_OK)
7186 	return 0;
7187     if (v < 0) {
7188 	(*pivot) = *c;
7189 	return 1;
7190     }
7191 
7192     for (p = left + 1; p <= right; p++) {
7193 	int v = CompareProc(sortData, p, left);
7194 	if (sortData->result != TCL_OK)
7195 	    return 0;
7196 	if (v != 0) {
7197 	    (*pivot) = (v < 0) ? *left : *p;
7198 	    return 1;
7199 	}
7200     }
7201     return 0;
7202 }
7203 
7204 /* If the user provides a -command which does not properly compare two
7205  * elements, quicksort may go into an infinite loop or access illegal memory.
7206  * This #define indicates parts of the code which are not part of a normal
7207  * quicksort, but are present to detect the aforementioned bugs. */
7208 #define BUGGY_COMMAND
7209 
7210 static struct SortItem *
partition(SortData * sortData,struct SortItem * left,struct SortItem * right,struct SortItem * pivot)7211 partition(
7212     SortData *sortData,
7213     struct SortItem *left,
7214     struct SortItem *right,
7215     struct SortItem *pivot
7216     )
7217 {
7218     int v;
7219 #ifdef BUGGY_COMMAND
7220     struct SortItem *min = left, *max = right;
7221 #endif
7222 
7223     while (left <= right) {
7224 	/*
7225 	  while (*left < *pivot)
7226 	    ++left;
7227 	*/
7228 	while (1) {
7229 	    v = CompareProc(sortData, left, pivot);
7230 	    if (sortData->result != TCL_OK)
7231 		return NULL;
7232 	    if (v >= 0)
7233 		break;
7234 #ifdef BUGGY_COMMAND
7235 	    /* If -command always returns < 0, 'left' becomes invalid */
7236 	    if (left == max)
7237 		goto buggy;
7238 #endif
7239 	    left++;
7240 	}
7241 	/*
7242 	  while (*right >= *pivot)
7243 	    --right;
7244 	*/
7245 	while (1) {
7246 	    v = CompareProc(sortData, right, pivot);
7247 	    if (sortData->result != TCL_OK)
7248 		return NULL;
7249 	    if (v < 0)
7250 		break;
7251 #ifdef BUGGY_COMMAND
7252 	    /* If -command always returns >= 0, 'right' becomes invalid */
7253 	    if (right == min)
7254 		goto buggy;
7255 #endif
7256 	    right--;
7257 	}
7258 	if (left < right) {
7259 	    struct SortItem tmp = *left;
7260 	    *left = *right;
7261 	    *right = tmp;
7262 	    left++;
7263 	    right--;
7264 	}
7265     }
7266     return left;
7267 #ifdef BUGGY_COMMAND
7268     buggy:
7269     FormatResult(sortData->tree->interp, "buggy item sort -command detected");
7270     sortData->result = TCL_ERROR;
7271     return NULL;
7272 #endif
7273 }
7274 
7275 static void
quicksort(SortData * sortData,struct SortItem * left,struct SortItem * right)7276 quicksort(
7277     SortData *sortData,
7278     struct SortItem *left,
7279     struct SortItem *right
7280     )
7281 {
7282     struct SortItem *p, pivot;
7283 
7284     if (sortData->result != TCL_OK)
7285 	return;
7286 
7287     if (left == right)
7288 	return;
7289 
7290     /* FIXME: switch to insertion sort or similar when the number of
7291      * elements is small. */
7292 
7293     if (find_pivot(sortData, left, right, &pivot) == 1) {
7294 	p = partition(sortData, left, right, &pivot);
7295 	quicksort(sortData, left, p - 1);
7296 	quicksort(sortData, p, right);
7297     }
7298 }
7299 
7300 /* END custom quicksort() */
7301 
7302 /*
7303  *----------------------------------------------------------------------
7304  *
7305  * ItemSortCmd --
7306  *
7307  *	This procedure is invoked to process the [item sort] widget
7308  *	command.  See the user documentation for details on what
7309  *	it does.
7310  *
7311  * Results:
7312  *	A standard Tcl result.
7313  *
7314  * Side effects:
7315  *	See the user documentation.
7316  *
7317  *----------------------------------------------------------------------
7318  */
7319 
7320 static int
ItemSortCmd(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])7321 ItemSortCmd(
7322     ClientData clientData,	/* Widget info. */
7323     Tcl_Interp *interp,		/* Current interpreter. */
7324     int objc,			/* Number of arguments. */
7325     Tcl_Obj *CONST objv[]	/* Argument values. */
7326     )
7327 {
7328     TreeCtrl *tree = clientData;
7329     TreeItem item, first, last, walk, lastChild;
7330     TreeItemColumn column;
7331     int i, j, count, elemIndex, index, indexF = 0, indexL = 0;
7332     int sawColumn = FALSE, sawCmd = FALSE;
7333     static int (*sortProc[5])(SortData *, struct SortItem *, struct SortItem *, int) =
7334 	{ CompareAscii, CompareDict, CompareDouble, CompareLong, CompareCmd };
7335     SortData sortData;
7336     TreeColumn treeColumn;
7337     struct SortElement *elemPtr;
7338     int notReally = FALSE;
7339     int result = TCL_OK;
7340 
7341     if (objc < 4) {
7342 	Tcl_WrongNumArgs(interp, 3, objv, "item ?option ...?");
7343 	return TCL_ERROR;
7344     }
7345 
7346     if (TreeItem_FromObj(tree, objv[3], &item, IFO_NOT_NULL) != TCL_OK)
7347 	return TCL_ERROR;
7348 
7349     /* If the item has no children, then nothing is done and no error
7350      * is generated. */
7351     if (item->numChildren < 1)
7352 	return TCL_OK;
7353 
7354     /* Defaults: sort ascii strings in column 0 only */
7355     sortData.tree = tree;
7356     sortData.columnCount = 1;
7357     sortData.columns[0].column = 0;
7358     sortData.columns[0].sortBy = SORT_ASCII;
7359     sortData.columns[0].order = 1;
7360     sortData.columns[0].elemCount = 0;
7361     sortData.result = TCL_OK;
7362 
7363     first = item->firstChild;
7364     last = item->lastChild;
7365 
7366     for (i = 4; i < objc; ) {
7367 	static CONST char *optionName[] = { "-ascii", "-column", "-command",
7368 					    "-decreasing", "-dictionary", "-element", "-first", "-increasing",
7369 					    "-integer", "-last", "-notreally", "-real", NULL };
7370 	int numArgs[] = { 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 1, 1 };
7371 	enum { OPT_ASCII, OPT_COLUMN, OPT_COMMAND, OPT_DECREASING, OPT_DICT,
7372 	       OPT_ELEMENT, OPT_FIRST, OPT_INCREASING, OPT_INTEGER, OPT_LAST,
7373 	       OPT_NOT_REALLY, OPT_REAL };
7374 
7375 	if (Tcl_GetIndexFromObj(interp, objv[i], optionName, "option", 0,
7376 		    &index) != TCL_OK)
7377 	    return TCL_ERROR;
7378 	if (objc - i < numArgs[index]) {
7379 	    FormatResult(interp, "missing value for \"%s\" option",
7380 		    optionName[index]);
7381 	    return TCL_ERROR;
7382 	}
7383 	switch (index) {
7384 	    case OPT_ASCII:
7385 		sortData.columns[sortData.columnCount - 1].sortBy = SORT_ASCII;
7386 		break;
7387 	    case OPT_COLUMN:
7388 		if (TreeColumn_FromObj(tree, objv[i + 1], &treeColumn,
7389 			    CFO_NOT_NULL | CFO_NOT_TAIL) != TCL_OK)
7390 		    return TCL_ERROR;
7391 		/* The first -column we see is the first column we compare */
7392 		if (sawColumn) {
7393 		    if (sortData.columnCount + 1 > MAX_SORT_COLUMNS) {
7394 			FormatResult(interp,
7395 				"can't compare more than %d columns",
7396 				MAX_SORT_COLUMNS);
7397 			return TCL_ERROR;
7398 		    }
7399 		    sortData.columnCount++;
7400 		    /* Defaults for this column */
7401 		    sortData.columns[sortData.columnCount - 1].sortBy = SORT_ASCII;
7402 		    sortData.columns[sortData.columnCount - 1].order = 1;
7403 		    sortData.columns[sortData.columnCount - 1].elemCount = 0;
7404 		}
7405 		sortData.columns[sortData.columnCount - 1].column = TreeColumn_Index(treeColumn);
7406 		sawColumn = TRUE;
7407 		break;
7408 	    case OPT_COMMAND:
7409 		sortData.columns[sortData.columnCount - 1].command = objv[i + 1];
7410 		sortData.columns[sortData.columnCount - 1].sortBy = SORT_COMMAND;
7411 		sawCmd = TRUE;
7412 		break;
7413 	    case OPT_DECREASING:
7414 		sortData.columns[sortData.columnCount - 1].order = 0;
7415 		break;
7416 	    case OPT_DICT:
7417 		sortData.columns[sortData.columnCount - 1].sortBy = SORT_DICT;
7418 		break;
7419 	    case OPT_ELEMENT: {
7420 		int listObjc;
7421 		Tcl_Obj **listObjv;
7422 
7423 		if (Tcl_ListObjGetElements(interp, objv[i + 1], &listObjc,
7424 			    &listObjv) != TCL_OK)
7425 		    return TCL_ERROR;
7426 		elemPtr = sortData.columns[sortData.columnCount - 1].elems;
7427 		sortData.columns[sortData.columnCount - 1].elemCount = 0;
7428 		if (listObjc == 0) {
7429 		} else if (listObjc == 1) {
7430 		    if (TreeElement_FromObj(tree, listObjv[0], &elemPtr->elem)
7431 			    != TCL_OK) {
7432 			Tcl_AddErrorInfo(interp,
7433 				"\n    (processing -element option)");
7434 			return TCL_ERROR;
7435 		    }
7436 		    if (!TreeElement_IsType(tree, elemPtr->elem, "text")) {
7437 			FormatResult(interp,
7438 				"element %s is not of type \"text\"",
7439 				Tcl_GetString(listObjv[0]));
7440 			Tcl_AddErrorInfo(interp,
7441 				"\n    (processing -element option)");
7442 			return TCL_ERROR;
7443 		    }
7444 		    elemPtr->style = NULL;
7445 		    elemPtr->elemIndex = -1;
7446 		    sortData.columns[sortData.columnCount - 1].elemCount++;
7447 		} else {
7448 		    if (listObjc & 1) {
7449 			FormatResult(interp,
7450 				"list must have even number of elements");
7451 			Tcl_AddErrorInfo(interp,
7452 				"\n    (processing -element option)");
7453 			return TCL_ERROR;
7454 		    }
7455 		    for (j = 0; j < listObjc; j += 2) {
7456 			if ((TreeStyle_FromObj(tree, listObjv[j],
7457 				     &elemPtr->style) != TCL_OK) ||
7458 				(TreeElement_FromObj(tree, listObjv[j + 1],
7459 					&elemPtr->elem) != TCL_OK) ||
7460 				(TreeStyle_FindElement(tree, elemPtr->style,
7461 					elemPtr->elem, &elemPtr->elemIndex) != TCL_OK)) {
7462 			    Tcl_AddErrorInfo(interp,
7463 				    "\n    (processing -element option)");
7464 			    return TCL_ERROR;
7465 			}
7466 			if (!TreeElement_IsType(tree, elemPtr->elem, "text")) {
7467 			    FormatResult(interp,
7468 				    "element %s is not of type \"text\"",
7469 				    Tcl_GetString(listObjv[j + 1]));
7470 			    Tcl_AddErrorInfo(interp,
7471 				    "\n    (processing -element option)");
7472 			    return TCL_ERROR;
7473 			}
7474 			sortData.columns[sortData.columnCount - 1].elemCount++;
7475 			elemPtr++;
7476 		    }
7477 		}
7478 		break;
7479 	    }
7480 	    case OPT_FIRST:
7481 		if (TreeItem_FromObj(tree, objv[i + 1], &first, IFO_NOT_NULL) != TCL_OK)
7482 		    return TCL_ERROR;
7483 		if (first->parent != item) {
7484 		    FormatResult(interp,
7485 			    "item %s%d is not a child of item %s%d",
7486 			    tree->itemPrefix, first->id, tree->itemPrefix, item->id);
7487 		    return TCL_ERROR;
7488 		}
7489 		break;
7490 	    case OPT_INCREASING:
7491 		sortData.columns[sortData.columnCount - 1].order = 1;
7492 		break;
7493 	    case OPT_INTEGER:
7494 		sortData.columns[sortData.columnCount - 1].sortBy = SORT_LONG;
7495 		break;
7496 	    case OPT_LAST:
7497 		if (TreeItem_FromObj(tree, objv[i + 1], &last, IFO_NOT_NULL) != TCL_OK)
7498 		    return TCL_ERROR;
7499 		if (last->parent != item) {
7500 		    FormatResult(interp,
7501 			    "item %s%d is not a child of item %s%d",
7502 			    tree->itemPrefix, last->id, tree->itemPrefix, item->id);
7503 		    return TCL_ERROR;
7504 		}
7505 		break;
7506 	    case OPT_NOT_REALLY:
7507 		notReally = TRUE;
7508 		break;
7509 	    case OPT_REAL:
7510 		sortData.columns[sortData.columnCount - 1].sortBy = SORT_DOUBLE;
7511 		break;
7512 	}
7513 	i += numArgs[index];
7514     }
7515 
7516     /* If there are no columns, we cannot perform a sort unless -command
7517      * is specified. */
7518     if ((tree->columnCount < 1) && (sortData.columns[0].sortBy != SORT_COMMAND)) {
7519 	FormatResult(interp, "there are no columns");
7520 	return TCL_ERROR;
7521     }
7522 
7523     /* If there is only one item to sort, then return early. */
7524     if (first == last) {
7525 	if (notReally)
7526 	    Tcl_SetObjResult(interp, TreeItem_ToObj(tree, first));
7527 	return TCL_OK;
7528     }
7529 
7530     for (i = 0; i < sortData.columnCount; i++) {
7531 
7532 	/* Initialize the sort procedure for this column. */
7533 	sortData.columns[i].proc = sortProc[sortData.columns[i].sortBy];
7534 
7535 	/* Append two dummy args to the -command argument. These two dummy
7536 	 * args are replaced by the 2 item ids being compared. See
7537 	 * CompareCmd(). */
7538 	if (sortData.columns[i].sortBy == SORT_COMMAND) {
7539 	    Tcl_Obj *obj = Tcl_DuplicateObj(sortData.columns[i].command);
7540 	    Tcl_Obj *obj2 = Tcl_NewObj();
7541 	    Tcl_IncrRefCount(obj);
7542 	    if (Tcl_ListObjAppendElement(interp, obj, obj2) != TCL_OK) {
7543 		Tcl_DecrRefCount(obj);
7544 		Tcl_IncrRefCount(obj2);
7545 		Tcl_DecrRefCount(obj2);
7546 
7547 		for (j = 0; j < i; j++) {
7548 		    if (sortData.columns[j].sortBy == SORT_COMMAND) {
7549 			Tcl_DecrRefCount(sortData.columns[j].command);
7550 		    }
7551 		}
7552 
7553 		return TCL_ERROR;
7554 	    }
7555 	    (void) Tcl_ListObjAppendElement(interp, obj, obj2);
7556 	    sortData.columns[i].command = obj;
7557 	}
7558     }
7559 
7560     index = 0;
7561     walk = item->firstChild;
7562     while (walk != NULL) {
7563 	if (walk == first)
7564 	    indexF = index;
7565 	if (walk == last)
7566 	    indexL = index;
7567 	index++;
7568 	walk = walk->nextSibling;
7569     }
7570     if (indexF > indexL) {
7571 	walk = last;
7572 	last = first;
7573 	first = walk;
7574 
7575 	index = indexL;
7576 	indexL = indexF;
7577 	indexF = index;
7578     }
7579     count = indexL - indexF + 1;
7580 
7581     sortData.item1s = (struct SortItem1 *) ckalloc(sizeof(struct SortItem1) * count * sortData.columnCount);
7582     sortData.items = (struct SortItem *) ckalloc(sizeof(struct SortItem) * count);
7583     for (i = 0; i < count; i++) {
7584 	sortData.items[i].item1 = sortData.item1s + i * sortData.columnCount;
7585 	sortData.items[i].obj = NULL;
7586     }
7587 
7588     index = 0;
7589     walk = first;
7590     while (walk != last->nextSibling) {
7591 	struct SortItem *sortItem = &sortData.items[index];
7592 
7593 	sortItem->item = walk;
7594 #ifdef STABLE_SORT
7595 	sortItem->index = index;
7596 #endif
7597 	if (sawCmd) {
7598 	    Tcl_Obj *obj = TreeItem_ToObj(tree, walk);
7599 	    Tcl_IncrRefCount(obj);
7600 	    sortData.items[index].obj = obj;
7601 	}
7602 	for (i = 0; i < sortData.columnCount; i++) {
7603 	    struct SortItem1 *sortItem1 = sortItem->item1 + i;
7604 
7605 	    if (sortData.columns[i].sortBy == SORT_COMMAND)
7606 		continue;
7607 
7608 	    column = TreeItem_FindColumn(tree, walk, sortData.columns[i].column);
7609 	    if ((column == NULL) || (column->style == NULL)) {
7610 		NoStyleMsg(tree, walk, sortData.columns[i].column);
7611 		result = TCL_ERROR;
7612 		goto done;
7613 	    }
7614 
7615 	    /* -element was empty. Find the first text element in the style */
7616 	    if (sortData.columns[i].elemCount == 0)
7617 		elemIndex = -1;
7618 
7619 	    /* -element was element name. Find the element in the style */
7620 	    else if ((sortData.columns[i].elemCount == 1) &&
7621 		    (sortData.columns[i].elems[0].style == NULL)) {
7622 		if (TreeStyle_FindElement(tree, column->style,
7623 			    sortData.columns[i].elems[0].elem, &elemIndex) != TCL_OK) {
7624 		    result = TCL_ERROR;
7625 		    goto done;
7626 		}
7627 	    }
7628 
7629 	    /* -element was style/element pair list */
7630 	    else {
7631 		TreeStyle masterStyle = TreeStyle_GetMaster(tree, column->style);
7632 
7633 		/* If the item style does not match any in the -element list,
7634 		 * we will use the first text element in the item style. */
7635 		elemIndex = -1;
7636 
7637 		/* Match a style from the -element list. Look in reverse order
7638 		 * to handle duplicates. */
7639 		for (j = sortData.columns[i].elemCount - 1; j >= 0; j--) {
7640 		    if (sortData.columns[i].elems[j].style == masterStyle) {
7641 			elemIndex = sortData.columns[i].elems[j].elemIndex;
7642 			break;
7643 		    }
7644 		}
7645 	    }
7646 	    if (TreeStyle_GetSortData(tree, column->style, elemIndex,
7647 			sortData.columns[i].sortBy,
7648 			&sortItem1->longValue,
7649 			&sortItem1->doubleValue,
7650 			&sortItem1->string) != TCL_OK) {
7651 		char msg[128];
7652 		sprintf(msg, "\n    (preparing to sort item %s%d column %s%d)",
7653 			tree->itemPrefix, walk->id,
7654 			tree->columnPrefix, TreeColumn_GetID(
7655 			Tree_FindColumn(tree, sortData.columns[i].column)));
7656 		Tcl_AddErrorInfo(interp, msg);
7657 		result = TCL_ERROR;
7658 		goto done;
7659 	    }
7660 	}
7661 	index++;
7662 	walk = walk->nextSibling;
7663     }
7664 
7665     quicksort(&sortData, sortData.items, sortData.items + count - 1);
7666 
7667     if (sortData.result != TCL_OK) {
7668 	result = sortData.result;
7669 	goto done;
7670     }
7671 
7672     if (sawCmd)
7673 	Tcl_ResetResult(interp);
7674 
7675     if (notReally) {
7676 	Tcl_Obj *listObj = Tcl_NewListObj(0, NULL);
7677 	Tcl_Obj *itemObj;
7678 
7679 	/* Smallest to largest */
7680 	if (sortData.columns[0].order == 1) {
7681 	    for (i = 0; i < count; i++) {
7682 		itemObj = sortData.items[i].obj;
7683 		if (itemObj == NULL)
7684 		    itemObj = TreeItem_ToObj(tree,
7685 			    sortData.items[i].item);
7686 		Tcl_ListObjAppendElement(interp, listObj, itemObj);
7687 	    }
7688 	}
7689 
7690 	/* Largest to smallest */
7691 	else {
7692 	    for (i = count - 1; i >= 0; i--) {
7693 		itemObj = sortData.items[i].obj;
7694 		if (itemObj == NULL)
7695 		    itemObj = TreeItem_ToObj(tree,
7696 			    sortData.items[i].item);
7697 		Tcl_ListObjAppendElement(interp, listObj, itemObj);
7698 	    }
7699 	}
7700 
7701 	Tcl_SetObjResult(interp, listObj);
7702 	goto done;
7703     }
7704     first = first->prevSibling;
7705     last = last->nextSibling;
7706 
7707     /* Smallest to largest */
7708     if (sortData.columns[0].order == 1) {
7709 	for (i = 0; i < count - 1; i++) {
7710 	    sortData.items[i].item->nextSibling = sortData.items[i + 1].item;
7711 	    sortData.items[i + 1].item->prevSibling = sortData.items[i].item;
7712 	}
7713 	indexF = 0;
7714 	indexL = count - 1;
7715     }
7716 
7717     /* Largest to smallest */
7718     else {
7719 	for (i = count - 1; i > 0; i--) {
7720 	    sortData.items[i].item->nextSibling = sortData.items[i - 1].item;
7721 	    sortData.items[i - 1].item->prevSibling = sortData.items[i].item;
7722 	}
7723 	indexF = count - 1;
7724 	indexL = 0;
7725     }
7726 
7727     lastChild = item->lastChild;
7728 
7729     sortData.items[indexF].item->prevSibling = first;
7730     if (first)
7731 	first->nextSibling = sortData.items[indexF].item;
7732     else
7733 	item->firstChild = sortData.items[indexF].item;
7734 
7735     sortData.items[indexL].item->nextSibling = last;
7736     if (last)
7737 	last->prevSibling = sortData.items[indexL].item;
7738     else
7739 	item->lastChild = sortData.items[indexL].item;
7740 
7741     /* Redraw the lines of the old/new lastchild */
7742     if ((item->lastChild != lastChild) && tree->showLines && (tree->columnTree != NULL)) {
7743 	if (lastChild->dInfo != NULL)
7744 	    Tree_InvalidateItemDInfo(tree, tree->columnTree,
7745 		    lastChild,
7746 		    NULL);
7747 	if (item->lastChild->dInfo != NULL)
7748 	    Tree_InvalidateItemDInfo(tree, tree->columnTree,
7749 		    item->lastChild,
7750 		    NULL);
7751     }
7752 
7753     tree->updateIndex = 1;
7754     Tree_DInfoChanged(tree, DINFO_REDO_RANGES);
7755 
7756     done:
7757     for (i = 0; i < count; i++) {
7758 	if (sortData.items[i].obj != NULL) {
7759 	    Tcl_DecrRefCount(sortData.items[i].obj);
7760 	}
7761     }
7762     for (i = 0; i < sortData.columnCount; i++) {
7763 	if (sortData.columns[i].sortBy == SORT_COMMAND) {
7764 	    Tcl_DecrRefCount(sortData.columns[i].command);
7765 	}
7766     }
7767     ckfree((char *) sortData.item1s);
7768     ckfree((char *) sortData.items);
7769 
7770     if (tree->debug.enable && tree->debug.data) {
7771 	Tree_Debug(tree);
7772     }
7773 
7774     return result;
7775 }
7776 
7777 /*
7778  *----------------------------------------------------------------------
7779  *
7780  * TreeItemList_Sort --
7781  *
7782  *	Sorts a list of items.
7783  *
7784  * Results:
7785  *	None.
7786  *
7787  * Side effects:
7788  *	None.
7789  *
7790  *----------------------------------------------------------------------
7791  */
7792 
7793 static int
TILSCompare(CONST VOID * first_,CONST VOID * second_)7794 TILSCompare(
7795     CONST VOID *first_,
7796     CONST VOID *second_
7797     )
7798 {
7799     TreeItem first = *(TreeItem *) first_;
7800     TreeItem second = *(TreeItem *) second_;
7801 
7802     return first->index - second->index;
7803 }
7804 
7805 void
TreeItemList_Sort(TreeItemList * items)7806 TreeItemList_Sort(
7807     TreeItemList *items
7808     )
7809 {
7810     Tree_UpdateItemIndex(items->tree);
7811 
7812     /* TkTable uses this, but mentions possible lack of thread-safety. */
7813     qsort((VOID *) TreeItemList_Items(items),
7814 	    (size_t) TreeItemList_Count(items),
7815 	    sizeof(TreeItem),
7816 	    TILSCompare);
7817 }
7818 
7819 /*
7820  *----------------------------------------------------------------------
7821  *
7822  * TreeItemCmd_Span --
7823  *
7824  *	The body of the [item span] and [header span] commands.
7825  *
7826  * Results:
7827  *	A standard Tcl result.
7828  *
7829  * Side effects:
7830  *	May change the layout and schedule a redraw of the widget.
7831  *
7832  *----------------------------------------------------------------------
7833  */
7834 
7835 int
TreeItemCmd_Span(TreeCtrl * tree,int objc,Tcl_Obj * CONST objv[],int doHeaders)7836 TreeItemCmd_Span(
7837     TreeCtrl *tree,		/* Widget info. */
7838     int objc,			/* Number of arguments. */
7839     Tcl_Obj *CONST objv[],	/* Argument values. */
7840     int doHeaders		/* TRUE to operate on headers, FALSE
7841 				 * to operate on items. */
7842     )
7843 {
7844     Tcl_Interp *interp = tree->interp;
7845     TreeColumn treeColumn = tree->columns;
7846     TreeItemList itemList;
7847     TreeItem item;
7848     TreeItemColumn column;
7849     Tcl_Obj *listObj;
7850     struct columnSpan {
7851 	TreeColumnList columns;
7852 	int span;
7853     } staticCS[STATIC_SIZE], *cs = staticCS;
7854     int i, count = 0, span, changed = FALSE;
7855     ItemForEach iter;
7856     ColumnForEach citer;
7857     int flags = 0, result = TCL_OK;
7858 
7859     if (objc < 4) {
7860 	Tcl_WrongNumArgs(interp, 3, objv,
7861 	    doHeaders ?
7862 		"header ?column? ?span? ?column span ...?":
7863 		"item ?column? ?span? ?column span ...?");
7864 	return TCL_ERROR;
7865     }
7866 
7867     if (objc < 6)
7868 	flags |= IFO_NOT_NULL | IFO_NOT_MANY;
7869     if (doHeaders) {
7870 	if (TreeHeaderList_FromObj(tree, objv[3], &itemList, flags) != TCL_OK)
7871 	    return TCL_ERROR;
7872     } else {
7873 	if (TreeItemList_FromObj(tree, objv[3], &itemList, flags) != TCL_OK)
7874 	    return TCL_ERROR;
7875     }
7876     item = TreeItemList_Nth(&itemList, 0);
7877 
7878     if (objc == 4) {
7879 	listObj = Tcl_NewListObj(0, NULL);
7880 	column = item->columns;
7881 	while (treeColumn != NULL) {
7882 	    Tcl_ListObjAppendElement(interp, listObj,
7883 		    Tcl_NewIntObj(column ? column->span : 1));
7884 	    treeColumn = TreeColumn_Next(treeColumn);
7885 	    if (column != NULL)
7886 		column = column->next;
7887 	}
7888 	Tcl_SetObjResult(interp, listObj);
7889 	goto okExit;
7890     }
7891     if (objc == 5) {
7892 	if (TreeItem_ColumnFromObj(tree, item, objv[4], &column, NULL, NULL,
7893 		CFO_NOT_NULL | CFO_NOT_TAIL) != TCL_OK) {
7894 	    goto errorExit;
7895 	}
7896 	Tcl_SetObjResult(interp, Tcl_NewIntObj(column ? column->span : 1));
7897 	goto okExit;
7898     }
7899     if (objc & 1) {
7900 	FormatResult(interp, "missing argument after column \"%s\"",
7901 		Tcl_GetString(objv[objc - 1]));
7902 	goto errorExit;
7903     }
7904     /* Gather column/span pairs. */
7905     STATIC_ALLOC(cs, struct columnSpan, objc / 2);
7906     for (i = 4; i < objc; i += 2) {
7907 	if (TreeColumnList_FromObj(tree, objv[i], &cs[count].columns,
7908 		CFO_NOT_NULL | CFO_NOT_TAIL) != TCL_OK) {
7909 	    result = TCL_ERROR;
7910 	    goto doneSPAN;
7911 	}
7912 	if (Tcl_GetIntFromObj(interp, objv[i + 1], &span) != TCL_OK) {
7913 	    result = TCL_ERROR;
7914 	    goto doneSPAN;
7915 	}
7916 	if (span <= 0) {
7917 	    FormatResult(interp, "bad span \"%d\": must be > 0", span);
7918 	    result = TCL_ERROR;
7919 	    goto doneSPAN;
7920 	}
7921 	cs[count].span = span;
7922 	count++;
7923     }
7924     ITEM_FOR_EACH(item, &itemList, NULL, &iter) {
7925 	int changedI = FALSE;
7926 	for (i = 0; i < count; i++) {
7927 	    COLUMN_FOR_EACH(treeColumn, &cs[i].columns, NULL, &citer) {
7928 		column = Item_CreateColumn(tree, item,
7929 			TreeColumn_Index(treeColumn), NULL);
7930 		if (column->span != cs[i].span) {
7931 		    if (cs[i].span > 1) {
7932 			item->flags &= ~ITEM_FLAG_SPANS_SIMPLE;
7933 		    }
7934 		    TreeItem_SpansInvalidate(tree, item);
7935 		    column->span = cs[i].span;
7936 		    TreeItemColumn_InvalidateSize(tree, column);
7937 		    changedI = TRUE;
7938 		    TreeColumns_InvalidateWidthOfItems(tree, treeColumn);
7939 		}
7940 	    }
7941 	}
7942 	if (changedI) {
7943 	    TreeItem_InvalidateHeight(tree, item);
7944 	    Tree_FreeItemDInfo(tree, item, NULL);
7945 	    changed = TRUE;
7946 	}
7947     }
7948     if (changed && !doHeaders)
7949 	Tree_DInfoChanged(tree, DINFO_REDO_RANGES);
7950 doneSPAN:
7951     for (i = 0; i < count; i++) {
7952 	TreeColumnList_Free(&cs[i].columns);
7953     }
7954     STATIC_FREE(cs, struct columnSpan, objc / 2);
7955     TreeItemList_Free(&itemList);
7956     return result;
7957 
7958 okExit:
7959     TreeItemList_Free(&itemList);
7960     return TCL_OK;
7961 
7962 errorExit:
7963     TreeItemList_Free(&itemList);
7964     return TCL_ERROR;
7965 }
7966 
7967 static int
ItemSpanCmd(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])7968 ItemSpanCmd(
7969     ClientData clientData,	/* Widget info. */
7970     Tcl_Interp *interp,		/* Current interpreter. */
7971     int objc,			/* Number of arguments. */
7972     Tcl_Obj *CONST objv[]	/* Argument values. */
7973     )
7974 {
7975     TreeCtrl *tree = clientData;
7976 
7977     return TreeItemCmd_Span(tree, objc, objv, FALSE);
7978 }
7979 
7980 /*
7981  *----------------------------------------------------------------------
7982  *
7983  * ItemStateCmd --
7984  *
7985  *	This procedure is invoked to process the [item state] widget
7986  *	command.  See the user documentation for details on what
7987  *	it does.
7988  *
7989  * Results:
7990  *	A standard Tcl result.
7991  *
7992  * Side effects:
7993  *	See the user documentation.
7994  *
7995  *----------------------------------------------------------------------
7996  */
7997 
7998 int
TreeItemCmd_State(TreeCtrl * tree,int objc,Tcl_Obj * CONST objv[],int doHeaders)7999 TreeItemCmd_State(
8000     TreeCtrl *tree,		/* Widget info. */
8001     int objc,			/* Number of arguments. */
8002     Tcl_Obj *CONST objv[],	/* Argument values. */
8003     int doHeaders		/* TRUE to operate on headers, FALSE
8004 				 * to operate on items. */
8005     )
8006 {
8007     Tcl_Interp *interp = tree->interp;
8008     int domain = doHeaders ? STATE_DOMAIN_HEADER : STATE_DOMAIN_ITEM;
8009     TreeStateDomain *domainPtr = &tree->stateDomain[domain];
8010     int tailFlag = doHeaders ? 0 : CFO_NOT_TAIL;
8011     static CONST char *commandNames[] = {
8012 	"define", "forcolumn", "get", "linkage", "names", "set", "undefine",
8013 	(char *) NULL
8014     };
8015     enum {
8016 	COMMAND_DEFINE, COMMAND_FORCOLUMN, COMMAND_GET, COMMAND_LINKAGE,
8017 	COMMAND_NAMES, COMMAND_SET, COMMAND_UNDEFINE
8018     };
8019     int index;
8020     TreeItem item;
8021 
8022     if (objc < 4) {
8023 	Tcl_WrongNumArgs(interp, 3, objv,
8024 	    doHeaders ?
8025 		"command header ?arg ...?" :
8026 		"command item ?arg ...?");
8027 	return TCL_ERROR;
8028     }
8029 
8030     if (Tcl_GetIndexFromObj(interp, objv[3], commandNames, "command", 0,
8031 		&index) != TCL_OK)
8032 	return TCL_ERROR;
8033 
8034     switch (index) {
8035 	/* T item state define stateName */
8036 	case COMMAND_DEFINE: {
8037 	    char *string;
8038 	    int i, length, slot = -1;
8039 
8040 	    if (objc != 5) {
8041 		Tcl_WrongNumArgs(interp, 4, objv, "stateName");
8042 		return TCL_ERROR;
8043 	    }
8044 	    string = Tcl_GetStringFromObj(objv[4], &length);
8045 	    if (!length || (*string == '~') || (*string == '!')) {
8046 		FormatResult(interp, "invalid state name \"%s\"", string);
8047 		return TCL_ERROR;
8048 	    }
8049 	    for (i = 0; i < 32; i++) {
8050 		if (domainPtr->stateNames[i] == NULL) {
8051 		    if (slot == -1)
8052 			slot = i;
8053 		    continue;
8054 		}
8055 		if (strcmp(domainPtr->stateNames[i], string) == 0) {
8056 		    FormatResult(interp, "state \"%s\" already defined in domain \"%s\"", string, domainPtr->name);
8057 		    return TCL_ERROR;
8058 		}
8059 	    }
8060 	    if (slot == -1) {
8061 		FormatResult(interp, "cannot define any more states in domain \"%s\"", domainPtr->name);
8062 		return TCL_ERROR;
8063 	    }
8064 	    domainPtr->stateNames[slot] = ckalloc(length + 1);
8065 	    strcpy(domainPtr->stateNames[slot], string);
8066 	    break;
8067 	}
8068 
8069 	/* T item state forcolumn I C ?stateList? */
8070 	case COMMAND_FORCOLUMN: {
8071 	    TreeItemList itemList;
8072 	    TreeColumnList columns;
8073 	    TreeColumn treeColumn;
8074 	    Tcl_Obj *listObj;
8075 	    TreeItemColumn column;
8076 	    int columnIndex;
8077 	    int i, states[3], stateOn, stateOff;
8078 	    ItemForEach iter;
8079 	    ColumnForEach citer;
8080 	    int flags = IFO_NOT_NULL;
8081 	    int result = TCL_OK;
8082 
8083 	    if (objc < 6 || objc > 7) {
8084 		Tcl_WrongNumArgs(interp, 4, objv,
8085 		    doHeaders ?
8086 			"header column ?stateList?" :
8087 			"item column ?stateList?");
8088 		return TCL_ERROR;
8089 	    }
8090 	    /* Without a stateList only one item is accepted. */
8091 	    if (objc == 6)
8092 		flags |= IFO_NOT_MANY;
8093 	    if (doHeaders) {
8094 		if (TreeHeaderList_FromObj(tree, objv[4], &itemList, flags)
8095 			!= TCL_OK)
8096 		    return TCL_ERROR;
8097 	    } else {
8098 		if (TreeItemList_FromObj(tree, objv[4], &itemList, flags)
8099 			!= TCL_OK)
8100 		    return TCL_ERROR;
8101 	    }
8102 	    TreeColumnList_Init(tree, &columns, 0);
8103 	    if (objc == 6) {
8104 		item = TreeItemList_Nth(&itemList, 0);
8105 		if (TreeItem_ColumnFromObj(tree, item, objv[5], &column,
8106 			NULL, &columnIndex, CFO_NOT_NULL | CFO_NOT_TAIL) != TCL_OK) {
8107 		    result = TCL_ERROR;
8108 		    goto doneFORC;
8109 		}
8110 		if ((column == NULL) || !column->cstate)
8111 		    goto doneFORC;
8112 		listObj = Tcl_NewListObj(0, NULL);
8113 		for (i = 0; i < 32; i++) {
8114 		    if (domainPtr->stateNames[i] == NULL)
8115 			continue;
8116 		    if (column->cstate & (1L << i)) {
8117 			Tcl_ListObjAppendElement(interp, listObj,
8118 				Tcl_NewStringObj(domainPtr->stateNames[i], -1));
8119 		    }
8120 		}
8121 		Tcl_SetObjResult(interp, listObj);
8122 		goto doneFORC;
8123 	    }
8124 	    if (TreeColumnList_FromObj(tree, objv[5], &columns,
8125 		    CFO_NOT_NULL | tailFlag) != TCL_OK) {
8126 		result = TCL_ERROR;
8127 		goto doneFORC;
8128 	    }
8129 	    if (Tree_StateFromListObj(tree, domain, objv[6], states, SFO_NOT_STATIC) != TCL_OK) {
8130 		result = TCL_ERROR;
8131 		goto doneFORC;
8132 	    }
8133 	    if ((states[0] | states[1] | states[2]) == 0)
8134 		goto doneFORC;
8135 	    ITEM_FOR_EACH(item, &itemList, NULL, &iter) {
8136 		COLUMN_FOR_EACH(treeColumn, &columns, NULL, &citer) {
8137 		    columnIndex = TreeColumn_Index(treeColumn);
8138 		    column = Item_CreateColumn(tree, item, columnIndex, NULL);
8139 		    stateOn = states[STATE_OP_ON];
8140 		    stateOff = states[STATE_OP_OFF];
8141 		    stateOn |= ~column->cstate & states[STATE_OP_TOGGLE];
8142 		    stateOff |= column->cstate & states[STATE_OP_TOGGLE];
8143 		    TreeItemColumn_ChangeState(tree, item, column, treeColumn,
8144 			stateOff, stateOn);
8145 		}
8146 	    }
8147 doneFORC:
8148 	    TreeColumnList_Free(&columns);
8149 	    TreeItemList_Free(&itemList);
8150 	    return result;
8151 	}
8152 
8153 	/* T item state get I ?state? */
8154 	case COMMAND_GET: {
8155 	    Tcl_Obj *listObj;
8156 	    int i, states[3];
8157 
8158 	    if (objc < 5 || objc > 6) {
8159 		Tcl_WrongNumArgs(interp, 4, objv,
8160 		    doHeaders ? "header ?state?" : "item ?state?");
8161 		return TCL_ERROR;
8162 	    }
8163 	    if (doHeaders) {
8164 		TreeItemList itemList;
8165 		if (TreeHeaderList_FromObj(tree, objv[4], &itemList, IFO_NOT_NULL | IFO_NOT_MANY) != TCL_OK)
8166 		    return TCL_ERROR;
8167 		item = TreeItemList_Nth(&itemList, 0);
8168 		TreeItemList_Free(&itemList);
8169 	    } else {
8170 		if (TreeItem_FromObj(tree, objv[4], &item, IFO_NOT_NULL) != TCL_OK)
8171 		    return TCL_ERROR;
8172 	    }
8173 	    if (objc == 6) {
8174 		states[STATE_OP_ON] = 0;
8175 		if (Tree_StateFromObj(tree, domain, objv[5], states, NULL,
8176 			    SFO_NOT_OFF | SFO_NOT_TOGGLE) != TCL_OK)
8177 		    return TCL_ERROR;
8178 		Tcl_SetObjResult(interp,
8179 			Tcl_NewBooleanObj((item->state & states[STATE_OP_ON]) != 0));
8180 		break;
8181 	    }
8182 	    listObj = Tcl_NewListObj(0, NULL);
8183 	    for (i = 0; i < 32; i++) {
8184 		if (domainPtr->stateNames[i] == NULL)
8185 		    continue;
8186 		if (item->state & (1L << i)) {
8187 		    Tcl_ListObjAppendElement(interp, listObj,
8188 			    Tcl_NewStringObj(domainPtr->stateNames[i], -1));
8189 		}
8190 	    }
8191 	    Tcl_SetObjResult(interp, listObj);
8192 	    break;
8193 	}
8194 
8195 	/* T item state linkage state */
8196 	case COMMAND_LINKAGE: {
8197 	    int index;
8198 
8199 	    if (objc != 5) {
8200 		Tcl_WrongNumArgs(interp, 3, objv, "state");
8201 		return TCL_ERROR;
8202 	    }
8203 	    if (Tree_StateFromObj(tree, domain, objv[4], NULL, &index,
8204 		    SFO_NOT_OFF | SFO_NOT_TOGGLE) != TCL_OK)
8205 		return TCL_ERROR;
8206 	    Tcl_SetObjResult(interp, Tcl_NewStringObj(
8207 		(index < domainPtr->staticCount) ? "static" : "dynamic", -1));
8208 	    break;
8209 	}
8210 
8211 	/* T item state names */
8212 	case COMMAND_NAMES: {
8213 	    Tcl_Obj *listObj;
8214 	    int i;
8215 
8216 	    if (objc != 4) {
8217 		Tcl_WrongNumArgs(interp, 4, objv, (char *) NULL);
8218 		return TCL_ERROR;
8219 	    }
8220 	    listObj = Tcl_NewListObj(0, NULL);
8221 	    for (i = domainPtr->staticCount; i < 32; i++) {
8222 		if (domainPtr->stateNames[i] != NULL)
8223 		    Tcl_ListObjAppendElement(interp, listObj,
8224 			    Tcl_NewStringObj(domainPtr->stateNames[i], -1));
8225 	    }
8226 	    Tcl_SetObjResult(interp, listObj);
8227 	    break;
8228 	}
8229 
8230 	/* T item state set I ?I? {state ...} */
8231 	case COMMAND_SET: {
8232 	    TreeItemList itemList, item2List;
8233 	    int states[3], stateOn, stateOff;
8234 	    ItemForEach iter;
8235 	    int result = TCL_OK;
8236 
8237 	    if (objc < 6 || objc > 7) {
8238 		Tcl_WrongNumArgs(interp, 4, objv,
8239 		    doHeaders ? "header ?last? stateList" : "item ?last? stateList");
8240 		return TCL_ERROR;
8241 	    }
8242 	    if (doHeaders) {
8243 		if (TreeHeaderList_FromObj(tree, objv[4], &itemList, IFO_NOT_NULL) != TCL_OK)
8244 		    return TCL_ERROR;
8245 	    } else {
8246 		if (TreeItemList_FromObj(tree, objv[4], &itemList, IFO_NOT_NULL) != TCL_OK)
8247 		    return TCL_ERROR;
8248 	    }
8249 	    if (objc == 6) {
8250 		TreeItemList_Init(tree, &item2List, 0);
8251 	    }
8252 	    if (objc == 7) {
8253 		if (TreeItemList_FromObj(tree, objv[5], &item2List, IFO_NOT_NULL) != TCL_OK) {
8254 		    result =  TCL_ERROR;
8255 		    goto doneSET;
8256 		}
8257 	    }
8258 	    if (Tree_StateFromListObj(tree, domain, objv[objc - 1], states,
8259 			SFO_NOT_STATIC) != TCL_OK) {
8260 		result =  TCL_ERROR;
8261 		goto doneSET;
8262 	    }
8263 	    if ((states[0] | states[1] | states[2]) == 0)
8264 		goto doneSET;
8265 	    ITEM_FOR_EACH(item, &itemList, &item2List, &iter) {
8266 		stateOn = states[STATE_OP_ON];
8267 		stateOff = states[STATE_OP_OFF];
8268 		stateOn |= ~item->state & states[STATE_OP_TOGGLE];
8269 		stateOff |= item->state & states[STATE_OP_TOGGLE];
8270 		TreeItem_ChangeState(tree, item, stateOff, stateOn);
8271 	    }
8272 	    if (iter.error)
8273 		result = TCL_ERROR;
8274 doneSET:
8275 	    TreeItemList_Free(&itemList);
8276 	    TreeItemList_Free(&item2List);
8277 	    return result;
8278 	}
8279 
8280 	/* T item state undefine ?state ...? */
8281 	case COMMAND_UNDEFINE: {
8282 	    int i, index;
8283 
8284 	    for (i = 4; i < objc; i++) {
8285 		if (Tree_StateFromObj(tree, domain, objv[i], NULL, &index,
8286 			SFO_NOT_STATIC | SFO_NOT_OFF | SFO_NOT_TOGGLE) != TCL_OK)
8287 		    return TCL_ERROR;
8288 		Tree_UndefineState(tree, domain, 1L << index);
8289 		PerStateInfo_Undefine(tree, &pstBitmap, &tree->buttonBitmap,
8290 			domain, 1L << index);
8291 		PerStateInfo_Undefine(tree, &pstImage, &tree->buttonImage,
8292 			domain, 1L << index);
8293 		ckfree(domainPtr->stateNames[index]);
8294 		domainPtr->stateNames[index] = NULL;
8295 	    }
8296 	    break;
8297 	}
8298     }
8299 
8300     return TCL_OK;
8301 }
8302 
8303 static int
ItemStateCmd(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])8304 ItemStateCmd(
8305     ClientData clientData,	/* Widget info. */
8306     Tcl_Interp *interp,		/* Current interpreter. */
8307     int objc,			/* Number of arguments. */
8308     Tcl_Obj *CONST objv[]	/* Argument values. */
8309     )
8310 {
8311     TreeCtrl *tree = clientData;
8312 
8313     return TreeItemCmd_State(tree, objc, objv, FALSE);
8314 }
8315 
8316 /*
8317  *----------------------------------------------------------------------
8318  *
8319  * ItemTagCmd --
8320  *
8321  *	This procedure is invoked to process the [item tag] widget
8322  *	command.  See the user documentation for details on what
8323  *	it does.
8324  *
8325  * Results:
8326  *	A standard Tcl result.
8327  *
8328  * Side effects:
8329  *	See the user documentation.
8330  *
8331  *----------------------------------------------------------------------
8332  */
8333 
8334 int
TreeItemCmd_Tag(TreeCtrl * tree,int objc,Tcl_Obj * CONST objv[],int doHeaders)8335 TreeItemCmd_Tag(
8336     TreeCtrl *tree,		/* Widget info. */
8337     int objc,			/* Number of arguments. */
8338     Tcl_Obj *CONST objv[],	/* Argument values. */
8339     int doHeaders		/* TRUE to operate on headers, FALSE
8340 				 * to operate on items. */
8341     )
8342 {
8343     Tcl_Interp *interp = tree->interp;
8344     static CONST char *commandNames[] = {
8345 	"add", "expr", "names", "remove", (char *) NULL
8346     };
8347     enum {
8348 	COMMAND_ADD, COMMAND_EXPR, COMMAND_NAMES, COMMAND_REMOVE
8349     };
8350     int index;
8351     ItemForEach iter;
8352     TreeItemList items;
8353     TreeItem item;
8354     int result = TCL_OK;
8355 
8356     if (objc < 4) {
8357 	Tcl_WrongNumArgs(interp, 3, objv, "command ?arg arg ...?");
8358 	return TCL_ERROR;
8359     }
8360 
8361     if (Tcl_GetIndexFromObj(interp, objv[3], commandNames, "command", 0,
8362 	&index) != TCL_OK) {
8363 	return TCL_ERROR;
8364     }
8365 
8366     switch (index) {
8367 	/* T item tag add I tagList */
8368 	case COMMAND_ADD: {
8369 	    int i, numTags;
8370 	    Tcl_Obj **listObjv;
8371 	    Tk_Uid staticTags[STATIC_SIZE], *tags = staticTags;
8372 
8373 	    if (objc != 6) {
8374 		Tcl_WrongNumArgs(interp, 4, objv,
8375 		    doHeaders ? "header tagList" : "item tagList");
8376 		return TCL_ERROR;
8377 	    }
8378 	    if (doHeaders) {
8379 		if (TreeHeaderList_FromObj(tree, objv[4], &items, IFO_NOT_NULL) != TCL_OK) {
8380 		    return TCL_ERROR;
8381 		}
8382 	    } else {
8383 		if (TreeItemList_FromObj(tree, objv[4], &items, IFO_NOT_NULL) != TCL_OK) {
8384 		    return TCL_ERROR;
8385 		}
8386 	    }
8387 	    if (Tcl_ListObjGetElements(interp, objv[5], &numTags, &listObjv) != TCL_OK) {
8388 		result = TCL_ERROR;
8389 		break;
8390 	    }
8391 	    STATIC_ALLOC(tags, Tk_Uid, numTags);
8392 	    for (i = 0; i < numTags; i++) {
8393 		tags[i] = Tk_GetUid(Tcl_GetString(listObjv[i]));
8394 	    }
8395 	    ITEM_FOR_EACH(item, &items, NULL, &iter) {
8396 		item->tagInfo = TagInfo_Add(tree, item->tagInfo, tags, numTags);
8397 	    }
8398 	    STATIC_FREE(tags, Tk_Uid, numTags);
8399 	    break;
8400 	}
8401 
8402 	/* T item tag expr I tagExpr */
8403 	case COMMAND_EXPR: {
8404 	    TagExpr expr;
8405 	    int ok = TRUE;
8406 
8407 	    if (objc != 6) {
8408 		Tcl_WrongNumArgs(interp, 4, objv,
8409 		    doHeaders ? "header tagExpr" : "item tagExpr");
8410 		return TCL_ERROR;
8411 	    }
8412 	    if (doHeaders) {
8413 		if (TreeHeaderList_FromObj(tree, objv[4], &items, IFO_NOT_NULL) != TCL_OK) {
8414 		    return TCL_ERROR;
8415 		}
8416 	    } else {
8417 		if (TreeItemList_FromObj(tree, objv[4], &items, IFO_NOT_NULL) != TCL_OK) {
8418 		    return TCL_ERROR;
8419 		}
8420 	    }
8421 	    if (TagExpr_Init(tree, objv[5], &expr) != TCL_OK) {
8422 		result = TCL_ERROR;
8423 		break;
8424 	    }
8425 	    ITEM_FOR_EACH(item, &items, NULL, &iter) {
8426 		if (!TagExpr_Eval(&expr, item->tagInfo)) {
8427 		    ok = FALSE;
8428 		    break;
8429 		}
8430 	    }
8431 	    TagExpr_Free(&expr);
8432 	    Tcl_SetObjResult(interp, Tcl_NewBooleanObj(ok));
8433 	    break;
8434 	}
8435 
8436 	/* T item tag names I */
8437 	case COMMAND_NAMES: {
8438 	    Tcl_Obj *listObj;
8439 	    Tk_Uid *tags = NULL;
8440 	    int i, tagSpace = 0, numTags = 0;
8441 
8442 	    if (objc != 5) {
8443 		Tcl_WrongNumArgs(interp, 4, objv, doHeaders ? "header" : "item");
8444 		return TCL_ERROR;
8445 	    }
8446 	    if (doHeaders) {
8447 		if (TreeHeaderList_FromObj(tree, objv[4], &items, IFO_NOT_NULL) != TCL_OK) {
8448 		    return TCL_ERROR;
8449 		}
8450 	    } else {
8451 		if (TreeItemList_FromObj(tree, objv[4], &items, IFO_NOT_NULL) != TCL_OK) {
8452 		    return TCL_ERROR;
8453 		}
8454 	    }
8455 	    ITEM_FOR_EACH(item, &items, NULL, &iter) {
8456 		tags = TagInfo_Names(tree, item->tagInfo, tags, &numTags, &tagSpace);
8457 	    }
8458 	    if (numTags) {
8459 		listObj = Tcl_NewListObj(0, NULL);
8460 		for (i = 0; i < numTags; i++) {
8461 		    Tcl_ListObjAppendElement(NULL, listObj,
8462 			    Tcl_NewStringObj((char *) tags[i], -1));
8463 		}
8464 		Tcl_SetObjResult(interp, listObj);
8465 		ckfree((char *) tags);
8466 	    }
8467 	    break;
8468 	}
8469 
8470 	/* T item tag remove I tagList */
8471 	case COMMAND_REMOVE: {
8472 	    int i, numTags;
8473 	    Tcl_Obj **listObjv;
8474 	    Tk_Uid staticTags[STATIC_SIZE], *tags = staticTags;
8475 
8476 	    if (objc != 6) {
8477 		Tcl_WrongNumArgs(interp, 4, objv,
8478 		    doHeaders ? "header tagList" : "item tagList");
8479 		return TCL_ERROR;
8480 	    }
8481 	    if (doHeaders) {
8482 		if (TreeHeaderList_FromObj(tree, objv[4], &items, IFO_NOT_NULL) != TCL_OK) {
8483 		    return TCL_ERROR;
8484 		}
8485 	    } else {
8486 		if (TreeItemList_FromObj(tree, objv[4], &items, IFO_NOT_NULL) != TCL_OK) {
8487 		    return TCL_ERROR;
8488 		}
8489 	    }
8490 	    if (Tcl_ListObjGetElements(interp, objv[5], &numTags, &listObjv) != TCL_OK) {
8491 		result = TCL_ERROR;
8492 		break;
8493 	    }
8494 	    STATIC_ALLOC(tags, Tk_Uid, numTags);
8495 	    for (i = 0; i < numTags; i++) {
8496 		tags[i] = Tk_GetUid(Tcl_GetString(listObjv[i]));
8497 	    }
8498 	    ITEM_FOR_EACH(item, &items, NULL, &iter) {
8499 		item->tagInfo = TagInfo_Remove(tree, item->tagInfo, tags, numTags);
8500 	    }
8501 	    STATIC_FREE(tags, Tk_Uid, numTags);
8502 	    break;
8503 	}
8504     }
8505 
8506     TreeItemList_Free(&items);
8507     return result;
8508 }
8509 
8510 static int
ItemTagCmd(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])8511 ItemTagCmd(
8512     ClientData clientData,	/* Widget info. */
8513     Tcl_Interp *interp,		/* Current interpreter. */
8514     int objc,			/* Number of arguments. */
8515     Tcl_Obj *CONST objv[]	/* Argument values. */
8516     )
8517 {
8518     TreeCtrl *tree = clientData;
8519 
8520     return TreeItemCmd_Tag(tree, objc, objv, FALSE);
8521 }
8522 
8523 /*
8524  *----------------------------------------------------------------------
8525  *
8526  * TreeItem_GetTagInfo --
8527  *
8528  *	Returns item->tagInfo.
8529  *
8530  * Results:
8531  *	TagInfo pointer or NULL.
8532  *
8533  * Side effects:
8534  *	None.
8535  *
8536  *----------------------------------------------------------------------
8537  */
8538 
8539 TagInfo *
TreeItem_GetTagInfo(TreeCtrl * tree,TreeItem item)8540 TreeItem_GetTagInfo(
8541     TreeCtrl *tree,		/* Widget info. */
8542     TreeItem item		/* Item token. */
8543     )
8544 {
8545     return item->tagInfo;
8546 }
8547 
8548 #ifdef SELECTION_VISIBLE
8549 
8550 /*
8551  *----------------------------------------------------------------------
8552  *
8553  * Tree_DeselectHidden --
8554  *
8555  *	Removes any selected items which are no longer ReallyVisible()
8556  *	from the selection.
8557  *
8558  * Results:
8559  *	None.
8560  *
8561  * Side effects:
8562  *	<Selection> event may be generated.
8563  *
8564  *----------------------------------------------------------------------
8565  */
8566 
8567 /*
8568  * FIXME: optimize all calls to this routine.
8569  * Optionally call Tree_DInfoChanged(tree, DINFO_REDO_SELECTION) instead.
8570  */
8571 void
Tree_DeselectHidden(TreeCtrl * tree)8572 Tree_DeselectHidden(
8573     TreeCtrl *tree		/* Widget info. */
8574     )
8575 {
8576     TreeItemList items;
8577     Tcl_HashEntry *hPtr;
8578     Tcl_HashSearch search;
8579     TreeItem item;
8580     int i;
8581 
8582     if (tree->selectCount < 1)
8583 	return;
8584 
8585     /* This call is slow for large lists. */
8586     Tree_UpdateItemIndex(tree);
8587 
8588     TreeItemList_Init(tree, &items, tree->selectCount);
8589 
8590     hPtr = Tcl_FirstHashEntry(&tree->selection, &search);
8591     while (hPtr != NULL) {
8592 	item = (TreeItem) Tcl_GetHashKey(&tree->selection, hPtr);
8593 	if (!TreeItem_ReallyVisible(tree, item))
8594 	    TreeItemList_Append(&items, item);
8595 	hPtr = Tcl_NextHashEntry(&search);
8596     }
8597     for (i = 0; i < TreeItemList_Count(&items); i++)
8598 	Tree_RemoveFromSelection(tree, TreeItemList_Nth(&items, i));
8599     if (TreeItemList_Count(&items)) {
8600 	TreeNotify_Selection(tree, NULL, &items);
8601     }
8602     TreeItemList_Free(&items);
8603 }
8604 
8605 #endif /* SELECTION_VISIBLE */
8606 
8607 /*
8608  *----------------------------------------------------------------------
8609  *
8610  * TreeItemCmd --
8611  *
8612  *	This procedure is invoked to process the [item] widget
8613  *	command.  See the user documentation for details on what
8614  *	it does.
8615  *
8616  * Results:
8617  *	A standard Tcl result.
8618  *
8619  * Side effects:
8620  *	See the user documentation.
8621  *
8622  *----------------------------------------------------------------------
8623  */
8624 
8625 int
TreeItemCmd(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])8626 TreeItemCmd(
8627     ClientData clientData,	/* Widget info. */
8628     Tcl_Interp *interp,		/* Current interpreter. */
8629     int objc,			/* Number of arguments. */
8630     Tcl_Obj *CONST objv[]	/* Argument values. */
8631     )
8632 {
8633     TreeCtrl *tree = clientData;
8634     enum {
8635 	COMMAND_ANCESTORS,
8636 	COMMAND_BBOX,
8637 	COMMAND_BUTTONSTATE,
8638 	COMMAND_CGET,
8639 	COMMAND_CHILDREN,
8640 	COMMAND_COLLAPSE,
8641 	COMMAND_COMPARE,
8642 #ifdef DEPRECATED
8643 	COMMAND_COMPLEX,
8644 #endif
8645 	COMMAND_CONFIGURE,
8646 	COMMAND_COUNT,
8647 	COMMAND_CREATE,
8648 	COMMAND_DELETE,
8649 	COMMAND_DESCENDANTS,
8650 	COMMAND_DUMP,
8651 	COMMAND_ELEMENT,
8652 	COMMAND_ENABLED,
8653 	COMMAND_EXPAND,
8654 	COMMAND_FIRSTCHILD,
8655 	COMMAND_ID,
8656 	COMMAND_IMAGE,
8657 	COMMAND_ISANCESTOR,
8658 	COMMAND_ISOPEN,
8659 	COMMAND_LASTCHILD,
8660 	COMMAND_NEXTSIBLING,
8661 	COMMAND_NUMCHILDREN,
8662 	COMMAND_ORDER,
8663 	COMMAND_PARENT,
8664 	COMMAND_PREVSIBLING,
8665 	COMMAND_RANGE,
8666 	COMMAND_REMOVE,
8667 	COMMAND_RNC,
8668 	COMMAND_SORT,
8669 	COMMAND_SPAN,
8670 	COMMAND_STATE,
8671 	COMMAND_STYLE,
8672 	COMMAND_TAG,
8673 	COMMAND_TEXT,
8674 	COMMAND_TOGGLE
8675     };
8676 
8677 /* AF_xxx must not conflict with IFO_xxx. */
8678 #define AF_NOT_ANCESTOR	0x00010000 /* item can't be ancestor of other item */
8679 #define AF_NOT_EQUAL	0x00020000 /* second item can't be same as first */
8680 #define AF_SAMEROOT	0x00040000 /* both items must be descendants of a
8681 				    * common ancestor */
8682 #define AF_NOT_ITEM	0x00080000 /* arg is not an Item */
8683 #define AF_NOT_DELETED	0x00100000 /* item can't be deleted */
8684 
8685     struct {
8686 	char *cmdName;
8687 	int minArgs;
8688 	int maxArgs;
8689 	int flags;	/* AF_xxx | IFO_xxx for 1st arg. */
8690 	int flags2;	/* AF_xxx | IFO_xxx for 2nd arg. */
8691 	int flags3;	/* AF_xxx | IFO_xxx for 3rd arg. */
8692 	char *argString;
8693 	Tcl_ObjCmdProc *proc;
8694     } argInfo[] = {
8695 	{ "ancestors", 1, 1, IFO_NOT_MANY | IFO_NOT_NULL, 0, 0, "item", NULL },
8696 	{ "bbox", 0, 0, 0, 0, 0, NULL, ItemBboxCmd },
8697 	{ "buttonstate", 1, 2, IFO_NOT_MANY | IFO_NOT_NULL, AF_NOT_ITEM, 0,
8698 		"item ?state?", NULL },
8699 	{ "cget", 2, 2, IFO_NOT_MANY | IFO_NOT_NULL, AF_NOT_ITEM, 0,
8700 		"item option", NULL },
8701 	{ "children", 1, 1, IFO_NOT_MANY | IFO_NOT_NULL, 0, 0,
8702 		"item", NULL },
8703 	{ "collapse", 1, 2, IFO_NOT_NULL, AF_NOT_ITEM, 0,
8704 		"item ?-recurse?", NULL},
8705 	{ "compare", 3, 3, IFO_NOT_MANY | IFO_NOT_NULL, AF_NOT_ITEM,
8706 		IFO_NOT_MANY | IFO_NOT_NULL, "item1 op item2",
8707 		NULL },
8708 #ifdef DEPRECATED
8709 	{ "complex", 2, 100000, IFO_NOT_MANY | IFO_NOT_NULL, AF_NOT_ITEM,
8710 		AF_NOT_ITEM, "item list ...", NULL },
8711 #endif
8712 	{ "configure", 1, 100000, IFO_NOT_NULL, AF_NOT_ITEM, AF_NOT_ITEM,
8713 		"item ?option? ?value? ?option value ...?", NULL },
8714 	{ "count", 0, 1, 0, 0, 0, "?itemDesc?" , NULL},
8715 	{ "create", 0, 0, 0, 0, 0, NULL, ItemCreateCmd },
8716 	{ "delete", 1, 2, IFO_NOT_NULL, IFO_NOT_NULL | AF_SAMEROOT, 0,
8717 		"first ?last?", NULL },
8718 	{ "descendants", 1, 1, IFO_NOT_MANY | IFO_NOT_NULL, 0, 0, "item",
8719 		NULL },
8720 	{ "dump", 1, 1, IFO_NOT_MANY | IFO_NOT_NULL, 0, 0, "item", NULL },
8721 	{ "element", 0, 0, 0, 0, 0, NULL, ItemElementCmd },
8722 	{ "enabled", 1, 2, IFO_NOT_NULL, AF_NOT_ITEM, 0, "item ?boolean?",
8723 		NULL },
8724 	{ "expand", 1, 2, IFO_NOT_NULL, AF_NOT_ITEM, 0, "item ?-recurse?",
8725 		NULL},
8726 	{ "firstchild", 1, 2, IFO_NOT_MANY | IFO_NOT_NULL | AF_NOT_DELETED,
8727 		IFO_NOT_MANY | IFO_NOT_NULL | IFO_NOT_ROOT | AF_NOT_ANCESTOR |
8728 		AF_NOT_EQUAL | AF_NOT_DELETED, 0, "item ?newFirstChild?",
8729 		NULL },
8730 	{ "id", 1, 1, 0, 0, 0, "item", NULL },
8731 	{ "image", 0, 0, 0, 0, 0, NULL, ItemImageCmd },
8732 	{ "isancestor", 2, 2, IFO_NOT_MANY | IFO_NOT_NULL, IFO_NOT_MANY |
8733 		IFO_NOT_NULL, 0, "item item2", NULL },
8734 	{ "isopen", 1, 1, IFO_NOT_MANY | IFO_NOT_NULL, 0, 0, "item", NULL },
8735 	{ "lastchild", 1, 2, IFO_NOT_MANY | IFO_NOT_NULL | AF_NOT_DELETED,
8736 		IFO_NOT_MANY | IFO_NOT_NULL | IFO_NOT_ROOT | AF_NOT_ANCESTOR |
8737 		AF_NOT_EQUAL | AF_NOT_DELETED, 0, "item ?newLastChild?", NULL },
8738 	{ "nextsibling", 1, 2, IFO_NOT_MANY | IFO_NOT_NULL | IFO_NOT_ROOT |
8739 		IFO_NOT_ORPHAN, IFO_NOT_MANY | IFO_NOT_NULL | IFO_NOT_ROOT |
8740 		AF_NOT_ANCESTOR | AF_NOT_EQUAL, 0, "item ?newNextSibling?",
8741 		NULL },
8742 	{ "numchildren", 1, 1, IFO_NOT_MANY | IFO_NOT_NULL, 0, 0, "item",
8743 		NULL },
8744 	{ "order", 1, 2, IFO_NOT_MANY | IFO_NOT_NULL, AF_NOT_ITEM, 0,
8745 		"item ?-visible?", NULL },
8746 	{ "parent", 1, 1, IFO_NOT_MANY | IFO_NOT_NULL, 0, 0, "item", NULL },
8747 	{ "prevsibling", 1, 2, IFO_NOT_MANY | IFO_NOT_NULL | IFO_NOT_ROOT |
8748 		IFO_NOT_ORPHAN, IFO_NOT_MANY | IFO_NOT_NULL | IFO_NOT_ROOT |
8749 		AF_NOT_ANCESTOR | AF_NOT_EQUAL, 0, "item ?newPrevSibling?",
8750 		NULL },
8751 	{ "range", 2, 2, IFO_NOT_MANY | IFO_NOT_NULL, IFO_NOT_MANY |
8752 		IFO_NOT_NULL | AF_SAMEROOT, 0, "first last", NULL },
8753 	{ "remove", 1, 1, IFO_NOT_NULL | IFO_NOT_ROOT, 0, 0, "item", NULL },
8754 	{ "rnc", 1, 1, IFO_NOT_MANY | IFO_NOT_NULL, 0, 0, "item", NULL },
8755 	{ "sort", 0, 0, 0, 0, 0, NULL, ItemSortCmd },
8756 	{ "span", 0, 0, 0, 0, 0, NULL, ItemSpanCmd },
8757 	{ "state", 0, 0, 0, 0, 0, NULL, ItemStateCmd },
8758 	{ "style", 0, 0, 0, 0, 0, NULL, ItemStyleCmd },
8759 	{ "tag", 0, 0, 0, 0, 0, NULL, ItemTagCmd },
8760 	{ "text", 0, 0, 0, 0, 0, NULL, ItemTextCmd },
8761 	{ "toggle", 1, 2, IFO_NOT_NULL, AF_NOT_ITEM, 0, "item ?-recurse?",
8762 		NULL},
8763 	{ NULL }
8764     };
8765     int index;
8766     int numArgs = objc - 3;
8767     TreeItemList itemList, item2List;
8768     TreeItem item = NULL, item2 = NULL, child;
8769     ItemForEach iter;
8770     int result = TCL_OK;
8771 
8772     if (objc < 3) {
8773 	Tcl_WrongNumArgs(interp, 2, objv, "command ?arg arg ...?");
8774 	return TCL_ERROR;
8775     }
8776 
8777     if (Tcl_GetIndexFromObjStruct(interp, objv[2], argInfo, sizeof(argInfo[0]),
8778 	    "command", 0, &index) != TCL_OK) {
8779 	return TCL_ERROR;
8780     }
8781 
8782     if (argInfo[index].proc != NULL)
8783 	return argInfo[index].proc(clientData, interp, objc, objv);
8784 
8785     if ((numArgs < argInfo[index].minArgs) ||
8786 	    (numArgs > argInfo[index].maxArgs)) {
8787 	Tcl_WrongNumArgs(interp, 3, objv, argInfo[index].argString);
8788 	return TCL_ERROR;
8789     }
8790 
8791     TreeItemList_Init(tree, &itemList, 0);
8792     TreeItemList_Init(tree, &item2List, 0);
8793 
8794     if ((numArgs >= 1) && !(argInfo[index].flags & AF_NOT_ITEM)) {
8795 	if (TreeItemList_FromObj(tree, objv[3], &itemList,
8796 		    argInfo[index].flags & 0xFFFF) != TCL_OK) {
8797 	    goto errorExit;
8798 	}
8799 	item = TreeItemList_Nth(&itemList, 0); /* May be NULL. */
8800 	if ((argInfo[index].flags & AF_NOT_DELETED) && IS_DELETED(item)) {
8801 	    FormatResult(interp, "item %s%d is being deleted",
8802 		    tree->itemPrefix, item->id);
8803 	    goto errorExit;
8804 	}
8805     }
8806     if (((numArgs >= 2) && !(argInfo[index].flags2 & AF_NOT_ITEM)) ||
8807 	    ((numArgs >= 3) && !(argInfo[index].flags3 & AF_NOT_ITEM))) {
8808 	int flags, obji;
8809 	if (argInfo[index].flags2 & AF_NOT_ITEM) {
8810 	    obji = 5;
8811 	    flags = argInfo[index].flags3;
8812 	} else {
8813 	    obji = 4;
8814 	    flags = argInfo[index].flags2;
8815 	}
8816 	if (TreeItemList_FromObj(tree, objv[obji], &item2List,
8817 		flags & 0xFFFF) != TCL_OK) {
8818 	    goto errorExit;
8819 	}
8820 	ITEM_FOR_EACH(item2, &item2List, NULL, &iter) {
8821 	    if ((flags & AF_NOT_DELETED) && IS_DELETED(item2)) {
8822 		FormatResult(interp, "item %s%d is being deleted",
8823 			tree->itemPrefix, item2->id);
8824 		goto errorExit;
8825 	    }
8826 	    if ((flags & AF_NOT_EQUAL) && (item == item2)) {
8827 		FormatResult(interp, "item %s%d same as second item", tree->itemPrefix,
8828 			item->id);
8829 		goto errorExit;
8830 	    }
8831 	    if ((argInfo[index].flags & AF_NOT_ANCESTOR) &&
8832 		    TreeItem_IsAncestor(tree, item, item2)) {
8833 		FormatResult(interp, "item %s%d is ancestor of item %s%d",
8834 			tree->itemPrefix, item->id, tree->itemPrefix, item2->id);
8835 		goto errorExit;
8836 	    }
8837 	    if ((flags & AF_NOT_ANCESTOR) &&
8838 		    TreeItem_IsAncestor(tree, item2, item)) {
8839 		FormatResult(interp, "item %s%d is ancestor of item %s%d",
8840 			tree->itemPrefix, item2->id, tree->itemPrefix, item->id);
8841 		goto errorExit;
8842 	    }
8843 	    if ((flags & AF_SAMEROOT) &&
8844 		    TreeItem_RootAncestor(tree, item) !=
8845 		    TreeItem_RootAncestor(tree, item2)) {
8846 reqSameRoot:
8847 		FormatResult(interp,
8848 			"item %s%d and item %s%d don't share a common ancestor",
8849 			tree->itemPrefix, item->id, tree->itemPrefix, item2->id);
8850 		goto errorExit;
8851 	    }
8852 	}
8853 	item2 = TreeItemList_Nth(&item2List, 0); /* May be NULL. */
8854     }
8855 
8856     switch (index) {
8857 	case COMMAND_ANCESTORS: {
8858 	    Tcl_Obj *listObj;
8859 	    TreeItem parent = item->parent;
8860 
8861 	    if (parent == NULL)
8862 		break; /* empty list */
8863 	    listObj = Tcl_NewListObj(0, NULL);
8864 	    while (parent != NULL) {
8865 		Tcl_ListObjAppendElement(interp, listObj,
8866 			TreeItem_ToObj(tree, parent));
8867 		parent = parent->parent;
8868 	    }
8869 	    Tcl_SetObjResult(interp, listObj);
8870 	    break;
8871 	}
8872 	/* T item buttonstate I ?state? */
8873 	case COMMAND_BUTTONSTATE: {
8874 	    static const char *stateNames[] = {
8875 		"active", "normal", "pressed", NULL
8876 	    };
8877 	    int state;
8878 	    if (numArgs == 2) {
8879 		int prevFlags = item->flags;
8880 		if (Tcl_GetIndexFromObj(interp, objv[4], stateNames, "state",
8881 			0, &state) != TCL_OK) {
8882 		    goto errorExit;
8883 		}
8884 		item->flags &= ~ITEM_FLAGS_BUTTONSTATE;
8885 		if (state == 0)
8886 		    item->flags |= ITEM_FLAG_BUTTONSTATE_ACTIVE;
8887 		else if (state == 2)
8888 		    item->flags |= ITEM_FLAG_BUTTONSTATE_PRESSED;
8889 		if (item->flags != prevFlags && tree->columnTree != NULL)
8890 		    Tree_InvalidateItemDInfo(tree, tree->columnTree, item, NULL);
8891 	    } else {
8892 		if (item->flags & ITEM_FLAG_BUTTONSTATE_ACTIVE)
8893 		    state = 0;
8894 		else if (item->flags & ITEM_FLAG_BUTTONSTATE_PRESSED)
8895 		    state = 2;
8896 		else
8897 		    state = 1;
8898 	    }
8899 	    Tcl_SetObjResult(interp, Tcl_NewStringObj(stateNames[state], -1));
8900 	    break;
8901 	}
8902 	case COMMAND_CGET: {
8903 	    Tcl_Obj *resultObjPtr;
8904 
8905 	    resultObjPtr = Tk_GetOptionValue(interp, (char *) item,
8906 		    tree->itemOptionTable, objv[4], tree->tkwin);
8907 	    if (resultObjPtr == NULL)
8908 		goto errorExit;
8909 	    Tcl_SetObjResult(interp, resultObjPtr);
8910 	    break;
8911 	}
8912 	case COMMAND_CHILDREN: {
8913 	    if (item->numChildren != 0) {
8914 		Tcl_Obj *listObj;
8915 
8916 		listObj = Tcl_NewListObj(0, NULL);
8917 		child = item->firstChild;
8918 		while (child != NULL) {
8919 		    Tcl_ListObjAppendElement(interp, listObj,
8920 			    TreeItem_ToObj(tree, child));
8921 		    child = child->nextSibling;
8922 		}
8923 		Tcl_SetObjResult(interp, listObj);
8924 	    }
8925 	    break;
8926 	}
8927 	case COMMAND_COLLAPSE:
8928 	case COMMAND_EXPAND:
8929 	case COMMAND_TOGGLE: {
8930 	    int animate = 0;
8931 	    int recurse = 0;
8932 	    int mode = 0; /* lint */
8933 	    int i, count;
8934 	    TreeItemList items;
8935 
8936 	    if (numArgs > 1) {
8937 		static const char *optionName[] = { "-animate", "-recurse",
8938 		    NULL };
8939 		int option;
8940 		for (i = 4; i < objc; i++) {
8941 		    if (Tcl_GetIndexFromObj(interp, objv[i], optionName,
8942 			    "option", 0, &option) != TCL_OK) {
8943 			goto errorExit;
8944 		    }
8945 		    switch (option) {
8946 			case 0: /* -animate */
8947 			    animate = 1;
8948 			    break;
8949 			case 1: /* -recurse */
8950 			    recurse = 1;
8951 			    break;
8952 		    }
8953 		}
8954 	    }
8955 	    switch (index) {
8956 		case COMMAND_COLLAPSE:
8957 		    mode = 0;
8958 		    break;
8959 		case COMMAND_EXPAND:
8960 		    mode = 1;
8961 		    break;
8962 		case COMMAND_TOGGLE:
8963 		    mode = -1;
8964 		    break;
8965 	    }
8966 	    if (animate) {
8967 		Tk_Image image;
8968 		Pixmap bitmap;
8969 		int open;
8970 		if (IS_ALL(item) || TreeItemList_Count(&itemList) != 1) {
8971 		    FormatResult(interp,
8972 			"only 1 item may be specified with -animate");
8973 		    goto errorExit;
8974 		}
8975 		image = PerStateImage_ForState(tree, &tree->buttonImage, item->state, NULL);
8976 		bitmap = PerStateBitmap_ForState(tree, &tree->buttonBitmap, item->state, NULL);
8977 		if (image != NULL || bitmap != None || !tree->useTheme
8978 			|| !TreeItem_HasButton(tree, item)) {
8979 		    TreeItem_OpenClose(tree, item, mode);
8980 		    break;
8981 		}
8982 		open = (item->state & STATE_ITEM_OPEN) != 0;
8983 		if (mode == -1 || open != mode) {
8984 		    (void) TreeTheme_AnimateButtonStart(tree, item);
8985 		}
8986 		break;
8987 	    }
8988 	    TreeItemList_Init(tree, &items, 0);
8989 	    ITEM_FOR_EACH(item, &itemList, NULL, &iter) {
8990 		TreeItemList_Append(&items, item);
8991 		if (!iter.all && recurse) {
8992 		    TreeItem_ListDescendants(tree, item, &items);
8993 		}
8994 	    }
8995 	    count = TreeItemList_Count(&items);
8996 	    for (i = 0; i < count; i++) {
8997 		item = TreeItemList_Nth(&items, i);
8998 		TreeItem_OpenClose(tree, item, mode);
8999 	    }
9000 	    TreeItemList_Free(&items);
9001 #ifdef SELECTION_VISIBLE
9002 	    Tree_DeselectHidden(tree);
9003 #endif
9004 	    break;
9005 	}
9006 	/* T item compare I op I */
9007 	case COMMAND_COMPARE: {
9008 	    static CONST char *opName[] = { "<", "<=", "==", ">=", ">", "!=", NULL };
9009 	    enum { COP_LT, COP_LE, COP_EQ, COP_GE, COP_GT, COP_NE };
9010 	    int op, compare = 0, index1 = 0, index2 = 0;
9011 
9012 	    if (Tcl_GetIndexFromObj(interp, objv[4], opName, "comparison operator", 0,
9013 		    &op) != TCL_OK) {
9014 		goto errorExit;
9015 	    }
9016 	    if (op != COP_EQ && op != COP_NE) {
9017 		if (TreeItem_RootAncestor(tree, item) !=
9018 		    TreeItem_RootAncestor(tree, item2)) {
9019 		    goto reqSameRoot;
9020 		}
9021 		TreeItem_ToIndex(tree, item, &index1, NULL);
9022 		TreeItem_ToIndex(tree, item2, &index2, NULL);
9023 	    }
9024 	    switch (op) {
9025 		case COP_LT:
9026 		    compare = index1 < index2;
9027 		    break;
9028 		case COP_LE:
9029 		    compare = index1 <= index2;
9030 		    break;
9031 		case COP_EQ:
9032 		    compare = item == item2;
9033 		    break;
9034 		case COP_GE:
9035 		    compare = index1 >= index2;
9036 		    break;
9037 		case COP_GT:
9038 		    compare = index1 > index2;
9039 		    break;
9040 		case COP_NE:
9041 		    compare = item != item2;
9042 		    break;
9043 	    }
9044 	    Tcl_SetObjResult(interp, Tcl_NewBooleanObj(compare));
9045 	    break;
9046 	}
9047 
9048 #ifdef DEPRECATED
9049 	case COMMAND_COMPLEX: {
9050 	    int i, j, columnIndex;
9051 	    int objc1, objc2;
9052 	    Tcl_Obj **objv1, **objv2;
9053 	    TreeColumn treeColumn = tree->columns;
9054 	    TreeItemColumn column;
9055 	    int eMask, cMask, iMask = 0;
9056 
9057 	    if (objc <= 4)
9058 		break;
9059 	    columnIndex = 0;
9060 	    for (i = 4; i < objc; i++, columnIndex++,
9061 		    treeColumn = TreeColumn_Next(treeColumn)) {
9062 		if (treeColumn == NULL) {
9063 		    FormatResult(interp, "column #%d doesn't exist",
9064 			    columnIndex);
9065 		    result = TCL_ERROR;
9066 		    goto doneComplex;
9067 		}
9068 		column = TreeItem_FindColumn(tree, item, columnIndex);
9069 		if (column == NULL) {
9070 		    FormatResult(interp, "item %s%d doesn't have column %s%d",
9071 			    tree->itemPrefix, item->id,
9072 			    tree->columnPrefix, TreeColumn_GetID(treeColumn));
9073 		    result = TCL_ERROR;
9074 		    goto doneComplex;
9075 		}
9076 		/* List of element-configs per column */
9077 		if (Tcl_ListObjGetElements(interp, objv[i],
9078 			    &objc1, &objv1) != TCL_OK) {
9079 		    result = TCL_ERROR;
9080 		    goto doneComplex;
9081 		}
9082 		if (objc1 == 0)
9083 		    continue;
9084 		if (column->style == NULL) {
9085 		    FormatResult(interp, "item %s%d column %s%d has no style",
9086 			    tree->itemPrefix, item->id,
9087 			    tree->columnPrefix, TreeColumn_GetID(treeColumn));
9088 		    result = TCL_ERROR;
9089 		    goto doneComplex;
9090 		}
9091 		cMask = 0;
9092 		for (j = 0; j < objc1; j++) {
9093 		    /* elem option value... */
9094 		    if (Tcl_ListObjGetElements(interp, objv1[j],
9095 				&objc2, &objv2) != TCL_OK) {
9096 			result = TCL_ERROR;
9097 			goto doneComplex;
9098 		    }
9099 		    if (objc2 < 3) {
9100 			FormatResult(interp,
9101 				"wrong # args: should be \"element option value ...\"");
9102 			result = TCL_ERROR;
9103 			goto doneComplex;
9104 		    }
9105 		    if (TreeStyle_ElementConfigureFromObj(tree, item, column,
9106 			    column->style, objv2[0], objc2 - 1, objv2 + 1,
9107 			    &eMask) != TCL_OK) {
9108 			result = TCL_ERROR;
9109 			goto doneComplex;
9110 		    }
9111 		    cMask |= eMask;
9112 		    iMask |= eMask;
9113 		}
9114 		if (cMask & CS_LAYOUT)
9115 		    TreeItemColumn_InvalidateSize(tree, column);
9116 	    }
9117 	    doneComplex:
9118 	    if (iMask & CS_DISPLAY)
9119 		Tree_InvalidateItemDInfo(tree, NULL, item, NULL);
9120 	    if (iMask & CS_LAYOUT) {
9121 		TreeColumns_InvalidateWidthOfItems(tree, NULL);
9122 		TreeItem_InvalidateHeight(tree, item);
9123 		Tree_FreeItemDInfo(tree, item, NULL);
9124 		Tree_DInfoChanged(tree, DINFO_REDO_RANGES);
9125 	    }
9126 	    break;
9127 	}
9128 #endif /* DEPRECATED*/
9129 
9130 	/* T item configure I ?option? ?value? ?option value ...? */
9131 	case COMMAND_CONFIGURE: {
9132 	    if (objc <= 5) {
9133 		Tcl_Obj *resultObjPtr;
9134 
9135 		if (IS_ALL(item) || (TreeItemList_Count(&itemList) > 1)) {
9136 		    NotManyMsg(tree, FALSE);
9137 		    goto errorExit;
9138 		}
9139 		resultObjPtr = Tk_GetOptionInfo(interp, (char *) item,
9140 			tree->itemOptionTable,
9141 			(objc == 4) ? (Tcl_Obj *) NULL : objv[4],
9142 			tree->tkwin);
9143 		if (resultObjPtr == NULL)
9144 		    goto errorExit;
9145 		Tcl_SetObjResult(interp, resultObjPtr);
9146 		break;
9147 	    }
9148 	    ITEM_FOR_EACH(item, &itemList, NULL, &iter) {
9149 		result = Item_Configure(tree, item, objc - 4, objv + 4);
9150 		if (result != TCL_OK)
9151 		    break;
9152 	    }
9153 	    break;
9154 	}
9155 	case COMMAND_COUNT: {
9156 	    int count = tree->itemCount;
9157 
9158 	    if (objc == 4) {
9159 		count = 0;
9160 		ITEM_FOR_EACH(item, &itemList, &item2List, &iter) {
9161 		    count++;
9162 		}
9163 	    }
9164 	    Tcl_SetObjResult(interp, Tcl_NewIntObj(count));
9165 	    break;
9166 	}
9167 	case COMMAND_DELETE: {
9168 	    TreeItemList deleted, selected;
9169 	    int i, count;
9170 
9171 	    /* The root is never deleted */
9172 	    if (tree->itemCount == 1)
9173 		break;
9174 
9175 	    if (objc == 5) {
9176 		if (item == NULL || item2 == NULL) {
9177 		    FormatResult(interp, "invalid range \"%s\" to \"%s\"",
9178 			    Tcl_GetString(objv[3]), Tcl_GetString(objv[4]));
9179 		    goto errorExit;
9180 		}
9181 	    }
9182 
9183 	    /*
9184 	     * ITEM_FLAG_DELETED prevents us from adding the same item
9185 	     * twice to the 'deleted' list. It also prevents nested
9186 	     * calls to this command (through binding scripts) deleting
9187 	     * the same item twice.
9188 	     */
9189 
9190 	    TreeItemList_Init(tree, &deleted, tree->itemCount - 1);
9191 	    TreeItemList_Init(tree, &selected, tree->selectCount);
9192 
9193 	    ITEM_FOR_EACH(item, &itemList, &item2List, &iter) {
9194 		if (IS_ROOT(item))
9195 		    continue;
9196 		if (IS_DELETED(item))
9197 		    continue;
9198 		item->flags |= ITEM_FLAG_DELETED;
9199 		TreeItemList_Append(&deleted, item);
9200 		if (TreeItem_GetSelected(tree, item))
9201 		    TreeItemList_Append(&selected, item);
9202 		if (iter.all)
9203 		    continue;
9204 		/* Check every descendant. */
9205 		if (item->firstChild == NULL)
9206 		    continue;
9207 		item2 = item;
9208 		while (item2->lastChild != NULL)
9209 		    item2 = item2->lastChild;
9210 		item = item->firstChild;
9211 		while (1) {
9212 		    if (IS_DELETED(item)) {
9213 			/* Skip all descendants (they are already flagged). */
9214 			while (item->lastChild != NULL)
9215 			    item = item->lastChild;
9216 		    } else {
9217 			item->flags |= ITEM_FLAG_DELETED;
9218 			TreeItemList_Append(&deleted, item);
9219 			if (TreeItem_GetSelected(tree, item))
9220 			    TreeItemList_Append(&selected, item);
9221 		    }
9222 		    if (item == item2)
9223 			break;
9224 		    item = TreeItem_Next(tree, item);
9225 		}
9226 	    }
9227 
9228 	    count = TreeItemList_Count(&selected);
9229 	    if (count) {
9230 		for (i = 0; i < count; i++) {
9231 		    item = TreeItemList_Nth(&selected, i);
9232 		    Tree_RemoveFromSelection(tree, item);
9233 		}
9234 		/* Generate <Selection> event for selected items being deleted. */
9235 		TreeNotify_Selection(tree, NULL, &selected);
9236 	    }
9237 
9238 	    count = TreeItemList_Count(&deleted);
9239 	    if (count) {
9240 		int redoColumns = 0;
9241 
9242 		/* Generate <ItemDelete> event for items being deleted. */
9243 		TreeNotify_ItemDeleted(tree, &deleted);
9244 
9245 		/* Remove every item from its parent. Needed because items
9246 		 * are deleted recursively. */
9247 		for (i = 0; i < count; i++) {
9248 		    item = TreeItemList_Nth(&deleted, i);
9249 
9250 		    /* Deleting a visible item may change the needed width
9251 		     * of any column. */
9252 		    if (TreeItem_ReallyVisible(tree, item))
9253 			redoColumns = 1;
9254 
9255 		    TreeItem_RemoveFromParent(tree, item);
9256 		}
9257 
9258 		/* Delete the items. The item record will be freed when no
9259 		 * longer in use; however, the item cannot be referred to
9260 		 * by commands from this point on. */
9261 		for (i = 0; i < count; i++) {
9262 		    item = TreeItemList_Nth(&deleted, i);
9263 		    TreeItem_Delete(tree, item);
9264 		}
9265 
9266 		if (redoColumns) {
9267 		    TreeColumns_InvalidateWidthOfItems(tree, NULL);
9268 		    TreeColumns_InvalidateSpans(tree);
9269 		}
9270 	    }
9271 
9272 	    TreeItemList_Free(&selected);
9273 	    TreeItemList_Free(&deleted);
9274 	    break;
9275 	}
9276 	case COMMAND_DESCENDANTS: {
9277 	    Tcl_Obj *listObj;
9278 
9279 	    if (item->firstChild == NULL)
9280 		break;
9281 	    item2 = item;
9282 	    while (item2->lastChild != NULL)
9283 		item2 = item2->lastChild;
9284 	    item = item->firstChild;
9285 	    listObj = Tcl_NewListObj(0, NULL);
9286 	    while (1) {
9287 		Tcl_ListObjAppendElement(interp, listObj,
9288 			TreeItem_ToObj(tree, item));
9289 		if (item == item2)
9290 		    break;
9291 		item = TreeItem_Next(tree, item);
9292 	    }
9293 	    Tcl_SetObjResult(interp, listObj);
9294 	    break;
9295 	}
9296 	case COMMAND_DUMP: {
9297 	    Tree_UpdateItemIndex(tree);
9298 	    FormatResult(interp, "index %d indexVis %d",
9299 		    item->index, item->indexVis);
9300 	    break;
9301 	}
9302 	/* T item enabled I ?boolean? */
9303 	case COMMAND_ENABLED: {
9304 	    int enabled;
9305 	    TreeItemList newD;
9306 	    int stateOff, stateOn;
9307 
9308 	    if (objc == 4) {
9309 		if (IS_ALL(item) || (TreeItemList_Count(&itemList) > 1)) {
9310 		    NotManyMsg(tree, FALSE);
9311 		    goto errorExit;
9312 		}
9313 		Tcl_SetObjResult(interp,
9314 			Tcl_NewBooleanObj(item->state & STATE_ITEM_ENABLED));
9315 		break;
9316 	    }
9317 	    if (Tcl_GetBooleanFromObj(interp, objv[4], &enabled) != TCL_OK)
9318 		goto errorExit;
9319 	    stateOff = enabled ? 0 : STATE_ITEM_ENABLED;
9320 	    stateOn = enabled ? STATE_ITEM_ENABLED : 0;
9321 	    TreeItemList_Init(tree, &newD, tree->selectCount);
9322 	    ITEM_FOR_EACH(item, &itemList, NULL, &iter) {
9323 		if (enabled != TreeItem_GetEnabled(tree, item)) {
9324 		    TreeItem_ChangeState(tree, item, stateOff, stateOn);
9325 		    /* Disabled items cannot be selected. */
9326 		    if (!enabled && TreeItem_GetSelected(tree, item)) {
9327 			Tree_RemoveFromSelection(tree, item);
9328 			TreeItemList_Append(&newD, item);
9329 		    }
9330 		}
9331 	    }
9332 	    if (TreeItemList_Count(&newD))
9333 		TreeNotify_Selection(tree, NULL, &newD);
9334 	    TreeItemList_Free(&newD);
9335 	    Tcl_SetObjResult(interp, Tcl_NewBooleanObj(enabled));
9336 	    break;
9337 	}
9338 	case COMMAND_FIRSTCHILD: {
9339 	    if (item2 != NULL && item2 != item->firstChild) {
9340 		TreeItem_RemoveFromParent(tree, item2);
9341 		item2->nextSibling = item->firstChild;
9342 		if (item->firstChild != NULL)
9343 		    item->firstChild->prevSibling = item2;
9344 		else
9345 		    item->lastChild = item2;
9346 		item->firstChild = item2;
9347 		item2->parent = item;
9348 		item->numChildren++;
9349 		TreeItem_AddToParent(tree, item2);
9350 #ifdef SELECTION_VISIBLE
9351 		Tree_DeselectHidden(tree);
9352 #endif
9353 	    }
9354 	    if (item->firstChild != NULL)
9355 		Tcl_SetObjResult(interp, TreeItem_ToObj(tree, item->firstChild));
9356 	    break;
9357 	}
9358 	/* T item id I */
9359 	case COMMAND_ID: {
9360 	    Tcl_Obj *listObj;
9361 
9362 	    listObj = Tcl_NewListObj(0, NULL);
9363 	    ITEM_FOR_EACH(item, &itemList, NULL, &iter) {
9364 		Tcl_ListObjAppendElement(interp, listObj,
9365 			TreeItem_ToObj(tree, item));
9366 	    }
9367 	    Tcl_SetObjResult(interp, listObj);
9368 	    break;
9369 	}
9370 	case COMMAND_ISANCESTOR: {
9371 	    Tcl_SetObjResult(interp, Tcl_NewBooleanObj(
9372 				 TreeItem_IsAncestor(tree, item, item2)));
9373 	    break;
9374 	}
9375 	case COMMAND_ISOPEN: {
9376 	    Tcl_SetObjResult(interp, Tcl_NewBooleanObj(item->state & STATE_ITEM_OPEN));
9377 	    break;
9378 	}
9379 	case COMMAND_LASTCHILD: {
9380 	    /* Don't allow non-deleted items to become children of a
9381 	     * deleted item. */
9382 	    if (item2 != NULL && item2 != item->lastChild) {
9383 		TreeItem_RemoveFromParent(tree, item2);
9384 		item2->prevSibling = item->lastChild;
9385 		if (item->lastChild != NULL)
9386 		    item->lastChild->nextSibling = item2;
9387 		else
9388 		    item->firstChild = item2;
9389 		item->lastChild = item2;
9390 		item2->parent = item;
9391 		item->numChildren++;
9392 		TreeItem_AddToParent(tree, item2);
9393 #ifdef SELECTION_VISIBLE
9394 		Tree_DeselectHidden(tree);
9395 #endif
9396 	    }
9397 	    if (item->lastChild != NULL)
9398 		Tcl_SetObjResult(interp, TreeItem_ToObj(tree, item->lastChild));
9399 	    break;
9400 	}
9401 	case COMMAND_NEXTSIBLING: {
9402 	    if (item2 != NULL && item2 != item->nextSibling) {
9403 		TreeItem_RemoveFromParent(tree, item2);
9404 		item2->prevSibling = item;
9405 		if (item->nextSibling != NULL) {
9406 		    item->nextSibling->prevSibling = item2;
9407 		    item2->nextSibling = item->nextSibling;
9408 		} else
9409 		    item->parent->lastChild = item2;
9410 		item->nextSibling = item2;
9411 		item2->parent = item->parent;
9412 		item->parent->numChildren++;
9413 		TreeItem_AddToParent(tree, item2);
9414 #ifdef SELECTION_VISIBLE
9415 		Tree_DeselectHidden(tree);
9416 #endif
9417 	    }
9418 	    if (item->nextSibling != NULL)
9419 		Tcl_SetObjResult(interp, TreeItem_ToObj(tree, item->nextSibling));
9420 	    break;
9421 	}
9422 	case COMMAND_NUMCHILDREN: {
9423 	    Tcl_SetObjResult(interp, Tcl_NewIntObj(item->numChildren));
9424 	    break;
9425 	}
9426 	/* T item order I ?-visible? */
9427 	case COMMAND_ORDER: {
9428 	    int visible = FALSE;
9429 	    if (objc == 5) {
9430 		int len;
9431 		char *s = Tcl_GetStringFromObj(objv[4], &len);
9432 		if ((s[0] == '-') && (strncmp(s, "-visible", len) == 0))
9433 		    visible = TRUE;
9434 		else {
9435 		    FormatResult(interp, "bad switch \"%s\": must be -visible",
9436 			s);
9437 		    goto errorExit;
9438 		}
9439 	    }
9440 	    Tree_UpdateItemIndex(tree);
9441 	    Tcl_SetObjResult(interp,
9442 		    Tcl_NewIntObj(visible ? item->indexVis : item->index));
9443 	    break;
9444 	}
9445 	/* T item range I I */
9446 	case COMMAND_RANGE: {
9447 	    TreeItem itemFirst = item;
9448 	    TreeItem itemLast = item2;
9449 	    Tcl_Obj *listObj;
9450 
9451 	    if (itemFirst == itemLast) {
9452 		Tcl_SetObjResult(interp, TreeItem_ToObj(tree, itemFirst));
9453 		break;
9454 	    }
9455 	    (void) TreeItem_FirstAndLast(tree, &itemFirst, &itemLast);
9456 	    listObj = Tcl_NewListObj(0, NULL);
9457 	    while (itemFirst != NULL) {
9458 		Tcl_ListObjAppendElement(interp, listObj,
9459 			TreeItem_ToObj(tree, itemFirst));
9460 		if (itemFirst == itemLast)
9461 		    break;
9462 		itemFirst = TreeItem_Next(tree, itemFirst);
9463 	    }
9464 	    Tcl_SetObjResult(interp, listObj);
9465 	    break;
9466 	}
9467 	case COMMAND_PARENT: {
9468 	    if (item->parent != NULL)
9469 		Tcl_SetObjResult(interp, TreeItem_ToObj(tree, item->parent));
9470 	    break;
9471 	}
9472 	case COMMAND_PREVSIBLING: {
9473 	    if (item2 != NULL && item2 != item->prevSibling) {
9474 		TreeItem_RemoveFromParent(tree, item2);
9475 		item2->nextSibling = item;
9476 		if (item->prevSibling != NULL) {
9477 		    item->prevSibling->nextSibling = item2;
9478 		    item2->prevSibling = item->prevSibling;
9479 		} else
9480 		    item->parent->firstChild = item2;
9481 		item->prevSibling = item2;
9482 		item2->parent = item->parent;
9483 		item->parent->numChildren++;
9484 		TreeItem_AddToParent(tree, item2);
9485 #ifdef SELECTION_VISIBLE
9486 		Tree_DeselectHidden(tree);
9487 #endif
9488 	    }
9489 	    if (item->prevSibling != NULL)
9490 		Tcl_SetObjResult(interp, TreeItem_ToObj(tree, item->prevSibling));
9491 	    break;
9492 	}
9493 	case COMMAND_REMOVE: {
9494 	    int removed = FALSE;
9495 
9496 	    ITEM_FOR_EACH(item, &itemList, NULL, &iter) {
9497 		if (item->parent != NULL) {
9498 		    TreeItem_RemoveFromParent(tree, item);
9499 		    Tree_FreeItemDInfo(tree, item, NULL);
9500 		    removed = TRUE;
9501 		}
9502 	    }
9503 	    if (!removed)
9504 		break;
9505 	    if (tree->debug.enable && tree->debug.data)
9506 		Tree_Debug(tree);
9507 	    TreeColumns_InvalidateWidthOfItems(tree, NULL);
9508 	    TreeColumns_InvalidateSpans(tree);
9509 #ifdef SELECTION_VISIBLE
9510 	    Tree_DeselectHidden(tree);
9511 #endif
9512 	    break;
9513 	}
9514 	case COMMAND_RNC: {
9515 	    int row,col;
9516 
9517 	    if (Tree_ItemToRNC(tree, item, &row, &col) == TCL_OK)
9518 		FormatResult(interp, "%d %d", row, col);
9519 	    break;
9520 	}
9521     }
9522 
9523     TreeItemList_Free(&itemList);
9524     TreeItemList_Free(&item2List);
9525     return result;
9526 
9527 errorExit:
9528     TreeItemList_Free(&itemList);
9529     TreeItemList_Free(&item2List);
9530     return TCL_ERROR;
9531 }
9532 
9533 /*
9534  *----------------------------------------------------------------------
9535  *
9536  * TreeItem_Debug --
9537  *
9538  *	Perform some sanity checks on an Item and its descendants.
9539  *
9540  * Results:
9541  *	TCL_OK or TCL_ERROR.
9542  *
9543  * Side effects:
9544  *	None.
9545  *
9546  *----------------------------------------------------------------------
9547  */
9548 
9549 int
TreeItem_Debug(TreeCtrl * tree,TreeItem item)9550 TreeItem_Debug(
9551     TreeCtrl *tree,		/* Widget info. */
9552     TreeItem item		/* Item token. */
9553     )
9554 {
9555     TreeItem child;
9556     Tcl_Interp *interp = tree->interp;
9557     int count;
9558 
9559     if (item->parent == item) {
9560 	FormatResult(interp,
9561 		"parent of %d is itself", item->id);
9562 	return TCL_ERROR;
9563     }
9564 
9565     if (item->parent == NULL) {
9566 	if (item->prevSibling != NULL) {
9567 	    FormatResult(interp,
9568 		    "parent of %d is nil, prevSibling is not nil",
9569 		    item->id);
9570 	    return TCL_ERROR;
9571 	}
9572 	if (item->nextSibling != NULL) {
9573 	    FormatResult(interp,
9574 		    "parent of %d is nil, nextSibling is not nil",
9575 		    item->id);
9576 	    return TCL_ERROR;
9577 	}
9578     }
9579 
9580     if (item->prevSibling != NULL) {
9581 	if (item->prevSibling == item) {
9582 	    FormatResult(interp,
9583 		    "prevSibling of %d is itself",
9584 		    item->id);
9585 	    return TCL_ERROR;
9586 	}
9587 	if (item->prevSibling->nextSibling != item) {
9588 	    FormatResult(interp,
9589 		    "item%d.prevSibling.nextSibling is not it",
9590 		    item->id);
9591 	    return TCL_ERROR;
9592 	}
9593     }
9594 
9595     if (item->nextSibling != NULL) {
9596 	if (item->nextSibling == item) {
9597 	    FormatResult(interp,
9598 		    "nextSibling of %d is itself",
9599 		    item->id);
9600 	    return TCL_ERROR;
9601 	}
9602 	if (item->nextSibling->prevSibling != item) {
9603 	    FormatResult(interp,
9604 		    "item%d.nextSibling->prevSibling is not it",
9605 		    item->id);
9606 	    return TCL_ERROR;
9607 	}
9608     }
9609 
9610     if (item->numChildren < 0) {
9611 	FormatResult(interp,
9612 		"numChildren of %d is %d",
9613 		item->id, item->numChildren);
9614 	return TCL_ERROR;
9615     }
9616 
9617     if (item->numChildren == 0) {
9618 	if (item->firstChild != NULL) {
9619 	    FormatResult(interp,
9620 		    "item%d.numChildren is zero, firstChild is not nil",
9621 		    item->id);
9622 	    return TCL_ERROR;
9623 	}
9624 	if (item->lastChild != NULL) {
9625 	    FormatResult(interp,
9626 		    "item%d.numChildren is zero, lastChild is not nil",
9627 		    item->id);
9628 	    return TCL_ERROR;
9629 	}
9630     }
9631 
9632     if (item->numChildren > 0) {
9633 	if (item->firstChild == NULL) {
9634 	    FormatResult(interp,
9635 		    "item%d.firstChild is nil",
9636 		    item->id);
9637 	    return TCL_ERROR;
9638 	}
9639 	if (item->firstChild == item) {
9640 	    FormatResult(interp,
9641 		    "item%d.firstChild is itself",
9642 		    item->id);
9643 	    return TCL_ERROR;
9644 	}
9645 	if (item->firstChild->parent != item) {
9646 	    FormatResult(interp,
9647 		    "item%d.firstChild.parent is not it",
9648 		    item->id);
9649 	    return TCL_ERROR;
9650 	}
9651 	if (item->firstChild->prevSibling != NULL) {
9652 	    FormatResult(interp,
9653 		    "item%d.firstChild.prevSibling is not nil",
9654 		    item->id);
9655 	    return TCL_ERROR;
9656 	}
9657 
9658 	if (item->lastChild == NULL) {
9659 	    FormatResult(interp,
9660 		    "item%d.lastChild is nil",
9661 		    item->id);
9662 	    return TCL_ERROR;
9663 	}
9664 	if (item->lastChild == item) {
9665 	    FormatResult(interp,
9666 		    "item%d.lastChild is itself",
9667 		    item->id);
9668 	    return TCL_ERROR;
9669 	}
9670 	if (item->lastChild->parent != item) {
9671 	    FormatResult(interp,
9672 		    "item%d.lastChild.parent is not it",
9673 		    item->id);
9674 	    return TCL_ERROR;
9675 	}
9676 	if (item->lastChild->nextSibling != NULL) {
9677 	    FormatResult(interp,
9678 		    "item%d.lastChild.nextSibling is not nil",
9679 		    item->id);
9680 	    return TCL_ERROR;
9681 	}
9682 
9683 	/* Count number of children */
9684 	count = 0;
9685 	child = item->firstChild;
9686 	while (child != NULL) {
9687 	    count++;
9688 	    child = child->nextSibling;
9689 	}
9690 	if (count != item->numChildren) {
9691 	    FormatResult(interp,
9692 		    "item%d.numChildren is %d, but counted %d",
9693 		    item->id, item->numChildren, count);
9694 	    return TCL_ERROR;
9695 	}
9696 
9697 	/* Debug each child recursively */
9698 	child = item->firstChild;
9699 	while (child != NULL) {
9700 	    if (child->parent != item) {
9701 		FormatResult(interp,
9702 			"child->parent of %d is not it",
9703 			item->id);
9704 		return TCL_ERROR;
9705 	    }
9706 	    if (TreeItem_Debug(tree, child) != TCL_OK)
9707 		return TCL_ERROR;
9708 	    child = child->nextSibling;
9709 	}
9710     }
9711     return TCL_OK;
9712 }
9713 
9714 /*
9715  *----------------------------------------------------------------------
9716  *
9717  * SpanWalkProc_Identify --
9718  *
9719  *	Callback routine to TreeItem_WalkSpans for TreeItem_Identify.
9720  *
9721  * Results:
9722  *	None.
9723  *
9724  * Side effects:
9725  *	None.
9726  *
9727  *----------------------------------------------------------------------
9728  */
9729 
9730 static int
SpanWalkProc_Identify(TreeCtrl * tree,TreeItem item,SpanInfo * spanPtr,StyleDrawArgs * drawArgs,ClientData clientData)9731 SpanWalkProc_Identify(
9732     TreeCtrl *tree,
9733     TreeItem item,
9734     SpanInfo *spanPtr,
9735     StyleDrawArgs *drawArgs,
9736     ClientData clientData
9737     )
9738 {
9739     struct {
9740 	int x;
9741 	int y;
9742 	TreeColumn *columnPtr;
9743 	TreeElement *elemPtr;
9744     } *data = clientData;
9745 
9746     if (item->header != NULL) {
9747 	if ((data->x < drawArgs->x /*+ drawArgs->indent*/) ||
9748 		(data->x >= drawArgs->x + drawArgs->width))
9749 	    return 0;
9750     } else {
9751 	if ((data->x < drawArgs->x + drawArgs->indent) ||
9752 		(data->x >= drawArgs->x + drawArgs->width))
9753 	    return 0;
9754     }
9755 
9756     (*data->columnPtr) = spanPtr->treeColumn;
9757 
9758     if ((drawArgs->style != NULL) && !TreeStyle_IsHeaderStyle(tree, drawArgs->style))
9759 	(*data->elemPtr) = TreeStyle_Identify(drawArgs, data->x, data->y);
9760 
9761     return 1; /* stop */
9762 }
9763 
9764 /*
9765  *----------------------------------------------------------------------
9766  *
9767  * TreeItem_Identify --
9768  *
9769  *	Determine which column and element the given point is in.
9770  *	This is used by the [identify] widget command.
9771  *
9772  * Results:
9773  *	If the Item is not ReallyVisible() or no columns are visible
9774  *	or the given coordinates are not in a span then both the returned
9775  *	column and element are NULL. Otherwise the returned column is
9776  *	set to the column at the start of the span containing the coordinate;
9777  *	the returned element may be NULL if the item-column has no style or
9778  *	if the coordinates are not over an element.
9779  *
9780  * Side effects:
9781  *	None.
9782  *
9783  *----------------------------------------------------------------------
9784  */
9785 
9786 void
TreeItem_Identify(TreeCtrl * tree,TreeItem item,int lock,int x,int y,TreeColumn * columnPtr,TreeElement * elemPtr)9787 TreeItem_Identify(
9788     TreeCtrl *tree,		/* Widget info. */
9789     TreeItem item,		/* Item token. */
9790     int lock,			/* Columns to hit-test. */
9791     int x, int y,		/* Item coords to hit-test with. */
9792     TreeColumn *columnPtr,
9793     TreeElement *elemPtr
9794     )
9795 {
9796     TreeRectangle tr;
9797     struct {
9798 	int x;
9799 	int y;
9800 	TreeColumn *columnPtr;
9801 	TreeElement *elemPtr;
9802     } clientData;
9803 
9804     (*columnPtr) = NULL;
9805     (*elemPtr) = NULL;
9806 
9807     if (Tree_ItemBbox(tree, item, lock, &tr) < 0)
9808 	return;
9809 
9810     /* Tree_ItemBbox returns canvas coords. x/y are item coords. */
9811     clientData.x = x;
9812     clientData.y = y;
9813     clientData.columnPtr = columnPtr;
9814     clientData.elemPtr = elemPtr;
9815 
9816     TreeItem_WalkSpans(tree, item, lock,
9817 	    0, 0, TreeRect_Width(tr), TreeRect_Height(tr),
9818 	    WALKSPAN_IGNORE_DND,
9819 	    SpanWalkProc_Identify, (ClientData) &clientData);
9820 }
9821 
9822 /*
9823  *----------------------------------------------------------------------
9824  *
9825  * SpanWalkProc_Identify2 --
9826  *
9827  *	Callback routine to TreeItem_WalkSpans for TreeItem_Identify2.
9828  *
9829  * Results:
9830  *	None.
9831  *
9832  * Side effects:
9833  *	None.
9834  *
9835  *----------------------------------------------------------------------
9836  */
9837 
9838 static int
SpanWalkProc_Identify2(TreeCtrl * tree,TreeItem item,SpanInfo * spanPtr,StyleDrawArgs * drawArgs,ClientData clientData)9839 SpanWalkProc_Identify2(
9840     TreeCtrl *tree,
9841     TreeItem item,
9842     SpanInfo *spanPtr,
9843     StyleDrawArgs *drawArgs,
9844     ClientData clientData
9845     )
9846 {
9847     Tcl_Obj *subListObj;
9848     struct {
9849 	int x1; int y1;
9850 	int x2; int y2;
9851 	Tcl_Obj *listObj;
9852     } *data = clientData;
9853 
9854     if ((data->x2 < drawArgs->x + drawArgs->indent) ||
9855 	    (data->x1 >= drawArgs->x + drawArgs->width))
9856 	return 0;
9857 
9858     subListObj = Tcl_NewListObj(0, NULL);
9859     Tcl_ListObjAppendElement(tree->interp, subListObj,
9860 	    TreeColumn_ToObj(tree, spanPtr->treeColumn));
9861     if (drawArgs->style != NULL) {
9862 	StyleDrawArgs drawArgsCopy = *drawArgs;
9863 	TreeStyle_Identify2(&drawArgsCopy, data->x1, data->y1,
9864 		data->x2, data->y2,
9865 		subListObj);
9866     }
9867     Tcl_ListObjAppendElement(tree->interp, data->listObj, subListObj);
9868     return drawArgs->x + drawArgs->width >= data->x2;
9869 }
9870 
9871 /*
9872  *----------------------------------------------------------------------
9873  *
9874  * TreeItem_Identify2 --
9875  *
9876  *	Determine which columns and elements intersect the given
9877  *	area. This is used by the [marquee identify] widget command.
9878  *
9879  * Results:
9880  *	If the Item is not ReallyVisible() or no columns are visible
9881  *	then listObj is untouched. Otherwise the list is appended
9882  *	with C {E ...} C {E...}.
9883  *
9884  * Side effects:
9885  *	None.
9886  *
9887  *----------------------------------------------------------------------
9888  */
9889 
9890 void
TreeItem_Identify2(TreeCtrl * tree,TreeItem item,int x1,int y1,int x2,int y2,Tcl_Obj * listObj)9891 TreeItem_Identify2(
9892     TreeCtrl *tree,		/* Widget info. */
9893     TreeItem item,		/* Item token. */
9894     int x1, int y1,		/* Top-left of area to hit-test. */
9895     int x2, int y2,		/* Bottom-right of area to hit-test. */
9896     Tcl_Obj *listObj		/* Initialized list object. */
9897     )
9898 {
9899     TreeRectangle tr;
9900     struct {
9901 	int x1; int y1;
9902 	int x2; int y2;
9903 	Tcl_Obj *listObj;
9904     } clientData;
9905 
9906     if (Tree_ItemBbox(tree, item, COLUMN_LOCK_NONE, &tr) < 0)
9907 	return;
9908 
9909     /* Tree_ItemBbox returns canvas coords. x1 etc are canvas coords. */
9910     clientData.x1 = x1;
9911     clientData.y1 = y1;
9912     clientData.x2 = x2;
9913     clientData.y2 = y2;
9914     clientData.listObj = listObj;
9915 
9916     TreeItem_WalkSpans(tree, item, COLUMN_LOCK_NONE,
9917 	    TreeRect_Left(tr), TreeRect_Top(tr),
9918 	    TreeRect_Width(tr), TreeRect_Height(tr),
9919 	    WALKSPAN_IGNORE_DND,
9920 	    SpanWalkProc_Identify2, (ClientData) &clientData);
9921 }
9922 
9923 /*
9924  *----------------------------------------------------------------------
9925  *
9926  * SpanWalkProc_GetRects --
9927  *
9928  *	Callback routine to TreeItem_WalkSpans for TreeItem_GetRects.
9929  *
9930  * Results:
9931  *	None.
9932  *
9933  * Side effects:
9934  *	None.
9935  *
9936  *----------------------------------------------------------------------
9937  */
9938 
9939 static int
SpanWalkProc_GetRects(TreeCtrl * tree,TreeItem item,SpanInfo * spanPtr,StyleDrawArgs * drawArgs,ClientData clientData)9940 SpanWalkProc_GetRects(
9941     TreeCtrl *tree,
9942     TreeItem item,
9943     SpanInfo *spanPtr,
9944     StyleDrawArgs *drawArgs,
9945     ClientData clientData
9946     )
9947 {
9948     int objc;
9949     Tcl_Obj *CONST *objv;
9950     struct {
9951 	TreeColumn treeColumn;
9952 	int count;
9953 	Tcl_Obj *CONST *objv;
9954 	TreeRectangle *rects;
9955 	int result;
9956    } *data = clientData;
9957 
9958     if (spanPtr->treeColumn != data->treeColumn)
9959 	return 0;
9960 
9961     /* Bounds of span. */
9962     if (data->count == 0) {
9963 	/* Not sure why I add 'indent' but for compatibility I will leave
9964 	 * it in. */
9965 	data->rects[0].x = drawArgs->x + drawArgs->indent;
9966 	data->rects[0].y = drawArgs->y;
9967 	data->rects[0].width = drawArgs->width - drawArgs->indent;
9968 	data->rects[0].height = drawArgs->height;
9969 #if 1
9970 	if (item->header != NULL) {
9971 	    data->rects[0].x = drawArgs->x;
9972 	    data->rects[0].width = drawArgs->width;
9973 	}
9974 #endif
9975 	data->result = 1; /* # of rects */
9976 	return 1; /* stop */
9977     }
9978 
9979     if (drawArgs->style == NULL) {
9980 	NoStyleMsg(tree, item, TreeColumn_Index(spanPtr->treeColumn));
9981 	data->result = -1; /* error */
9982 	return 1; /* stop */
9983     }
9984 
9985     if (data->count == -1) {
9986 	/* All the elements. */
9987 	objc = 0;
9988 	objv = NULL;
9989     } else {
9990 	objc = data->count;
9991 	objv = data->objv;
9992     }
9993 
9994     data->result = TreeStyle_GetElemRects(drawArgs, objc, objv, data->rects);
9995     return 1; /* stop */
9996 }
9997 
9998 /*
9999  *----------------------------------------------------------------------
10000  *
10001  * TreeItem_GetRects --
10002  *
10003  *	Returns zero or more bounding boxes for an item-column or
10004  *	element(s) in an item-column.
10005  *
10006  * Results:
10007  *	If the item is not visible, or has zero height/width, or the given
10008  *	column is obscurred by a preceding span, or the column has zero
10009  *	width or is not visible, the return value is zero.
10010  *
10011  *	If count==0, the bounding box of the span is returned and the
10012  *	return value is 1 (for a single rect).
10013  *
10014  *	If count!=0, and the item-column does not have a style assigned, an
10015  *	error is left in the interpreter result and the return value is -1.
10016  *
10017  *	If count==-1, the bounding box of each element in the style is
10018  *	returned and the return value is the number of elements in the style.
10019  *
10020  *	If count>0, the bounding box of each element specified by the objv[]
10021  *	array is returned and the return value is equal to count. If any of
10022  *	the objv[] elements in not in the style, an error is left in the
10023  *	interpreter result and the return value is -1.
10024  *
10025  * Side effects:
10026  *	None.
10027  *
10028  *----------------------------------------------------------------------
10029  */
10030 
10031 int
TreeItem_GetRects(TreeCtrl * tree,TreeItem item,TreeColumn treeColumn,int count,Tcl_Obj * CONST objv[],TreeRectangle rects[])10032 TreeItem_GetRects(
10033     TreeCtrl *tree,		/* Widget info. */
10034     TreeItem item,		/* Item token. */
10035     TreeColumn treeColumn,	/* The column to get rects for. */
10036     int count,			/* -1 means get rects for all elements.
10037 				 * 0 means get bounds of the span.
10038 				 * 1+ means objv[] contains names of elements
10039 				 *  to get rects for. */
10040     Tcl_Obj *CONST objv[],	/* Array of element names or NULL. */
10041     TreeRectangle rects[]	/* Out: returned bounding boxes. */
10042     )
10043 {
10044     TreeRectangle tr;
10045     int lock = TreeColumn_Lock(treeColumn);
10046     struct {
10047 	TreeColumn treeColumn;
10048 	int count;
10049 	Tcl_Obj *CONST *objv;
10050 	TreeRectangle *rects;
10051 	int result;
10052     } clientData;
10053 
10054     if (Tree_ItemBbox(tree, item, lock, &tr) < 0)
10055 	return 0;
10056 
10057     clientData.treeColumn = treeColumn;
10058     clientData.count = count;
10059     clientData.objv = objv;
10060     clientData.rects = rects;
10061     clientData.result = 0; /* -1 error, 0 no rects, 1+ success */
10062 
10063     TreeItem_WalkSpans(tree, item, lock,
10064 	    TreeRect_Left(tr), TreeRect_Top(tr),
10065 	    TreeRect_Width(tr), TreeRect_Height(tr),
10066 	    WALKSPAN_IGNORE_DND,
10067 	    SpanWalkProc_GetRects, (ClientData) &clientData);
10068 
10069     return clientData.result;
10070 }
10071 
10072 /*
10073  *----------------------------------------------------------------------
10074  *
10075  * TreeItem_MakeColumnExist --
10076  *
10077  *	Wrapper around Item_CreateColumn, called when a tree-column is
10078  *	created.
10079  *
10080  * Results:
10081  *	A TreeItemColumn.
10082  *
10083  * Side effects:
10084  *	Memory allocation.
10085  *
10086  *----------------------------------------------------------------------
10087  */
10088 
10089 TreeItemColumn
TreeItem_MakeColumnExist(TreeCtrl * tree,TreeItem item,int columnIndex)10090 TreeItem_MakeColumnExist(
10091     TreeCtrl *tree,		/* Widget info. */
10092     TreeItem item,		/* Header token. */
10093     int columnIndex		/* Index of new column. */
10094     )
10095 {
10096     return Item_CreateColumn(tree, item, columnIndex, NULL);
10097 }
10098 
10099 /*
10100  *----------------------------------------------------------------------
10101  *
10102  * TreeItem_CreateHeader --
10103  *
10104  *	Create a new TreeHeader.  Both the header and underlying item
10105  *	are created.  For each tree-column that exists (including the
10106  *	tail), a new header-column and associated item-column are
10107  *	created.
10108  *
10109  * Results:
10110  *	A TreeItem.
10111  *
10112  * Side effects:
10113  *	Memory allocation.
10114  *
10115  *----------------------------------------------------------------------
10116  */
10117 
10118 TreeItem
TreeItem_CreateHeader(TreeCtrl * tree)10119 TreeItem_CreateHeader(
10120     TreeCtrl *tree		/* Widget info. */
10121     )
10122 {
10123     TreeItem item, walk;
10124     TreeHeader header;
10125 
10126     item = Item_Alloc(tree, TRUE);
10127     header = TreeHeader_CreateWithItem(tree, item);
10128     if (header == NULL) {
10129     }
10130     item->header = header;
10131     /* This will create a TreeItemColumn and TreeHeaderColumn for every
10132      * TreeColumn, including the tail column. */
10133     (void) Item_CreateColumn(tree, item, tree->columnCount, NULL);
10134     if (tree->headerItems == NULL)
10135 	tree->headerItems = item;
10136     else {
10137 	walk = tree->headerItems;
10138 	while (walk->nextSibling != NULL) {
10139 	    walk = walk->nextSibling;
10140 	}
10141 	walk->nextSibling = item;
10142 	item->prevSibling = walk;
10143     }
10144     return item;
10145 }
10146 
10147 /*
10148  *----------------------------------------------------------------------
10149  *
10150  * TreeItem_GetHeader --
10151  *
10152  *	Return the header associated with an item.
10153  *
10154  * Results:
10155  *	A TreeHeader or NULL if this is not a header-item.
10156  *
10157  * Side effects:
10158  *	None.
10159  *
10160  *----------------------------------------------------------------------
10161  */
10162 
10163 TreeHeader
TreeItem_GetHeader(TreeCtrl * tree,TreeItem item)10164 TreeItem_GetHeader(
10165     TreeCtrl *tree,		/* Widget info. */
10166     TreeItem item		/* Item token. */
10167     )
10168 {
10169     return item->header;
10170 }
10171 
10172 /*
10173  *----------------------------------------------------------------------
10174  *
10175  * TreeItemColumn_GetHeaderColumn --
10176  *
10177  *	Return the header-column associated with an item-column.
10178  *
10179  * Results:
10180  *	A TreeHeaderColumn or NULL if this is not a item-column in a
10181  *	header-item.
10182  *
10183  * Side effects:
10184  *	None.
10185  *
10186  *----------------------------------------------------------------------
10187  */
10188 
10189 TreeHeaderColumn
TreeItemColumn_GetHeaderColumn(TreeCtrl * tree,TreeItemColumn column)10190 TreeItemColumn_GetHeaderColumn(
10191     TreeCtrl *tree,
10192     TreeItemColumn column
10193     )
10194 {
10195     return column->headerColumn;
10196 }
10197 
10198 /*
10199  *----------------------------------------------------------------------
10200  *
10201  * IsHeaderOption --
10202  *
10203  *	Determine if an option is a valid option implemented for a header
10204  *	by its underlying item.
10205  *
10206  * Results:
10207  *	TRUE if the optoin.
10208  *
10209  * Side effects:
10210  *	None.
10211  *
10212  *----------------------------------------------------------------------
10213  */
10214 
10215 static int
IsHeaderOption(Tcl_Interp * interp,Tcl_Obj * objPtr)10216 IsHeaderOption(
10217     Tcl_Interp *interp,		/* Interp. */
10218     Tcl_Obj *objPtr		/* Name of the option. */
10219     )
10220 {
10221     static CONST char *headerOptions[] = {
10222 	"-height", "-tags", "-visible", NULL
10223     };
10224     int index;
10225     if (Tcl_GetIndexFromObj(interp, objPtr, headerOptions,
10226 	    "option", 0, &index) != TCL_OK)
10227 	return FALSE;
10228     return TRUE;
10229 }
10230 
10231 /*
10232  *----------------------------------------------------------------------
10233  *
10234  * TreeItem_ConsumeHeaderCget --
10235  *
10236  *	Sets the interpreter result with the value of a single
10237  *	configuration option for an item.  This is called when an
10238  *	unknown option is passed to [header cget].
10239  *
10240  * Results:
10241  *	A standard Tcl result.
10242  *
10243  * Side effects:
10244  *	None.
10245  *
10246  *----------------------------------------------------------------------
10247  */
10248 
10249 int
TreeItem_ConsumeHeaderCget(TreeCtrl * tree,TreeItem item,Tcl_Obj * objPtr)10250 TreeItem_ConsumeHeaderCget(
10251     TreeCtrl *tree,		/* Widget info. */
10252     TreeItem item,		/* Item token. */
10253     Tcl_Obj *objPtr		/* Option name. */
10254     )
10255 {
10256     Tcl_Interp *interp = tree->interp;
10257     Tcl_Obj *resultObjPtr;
10258 
10259     if (!IsHeaderOption(interp, objPtr)) {
10260 	FormatResult(interp, "unknown option \"%s\"", Tcl_GetString(objPtr));
10261 	return TCL_ERROR;
10262     }
10263     resultObjPtr = Tk_GetOptionValue(interp, (char *) item,
10264 	    tree->itemOptionTable, objPtr, tree->tkwin);
10265     if (resultObjPtr == NULL)
10266 	return TCL_ERROR;
10267     Tcl_SetObjResult(interp, resultObjPtr);
10268     return TCL_OK;
10269 }
10270 
10271 /*
10272  *----------------------------------------------------------------------
10273  *
10274  * TreeItem_ConsumeHeaderConfig --
10275  *
10276  *	Configures an item with option/value pairs.  This is
10277  *	called with any unknown options passed to [header configure].
10278  *
10279  * Results:
10280  *	A standard Tcl result.
10281  *
10282  * Side effects:
10283  *	Whatever [item configure] does.
10284  *
10285  *----------------------------------------------------------------------
10286  */
10287 
10288 int
TreeItem_ConsumeHeaderConfig(TreeCtrl * tree,TreeItem item,int objc,Tcl_Obj * CONST objv[])10289 TreeItem_ConsumeHeaderConfig(
10290     TreeCtrl *tree,		/* Widget info. */
10291     TreeItem item,		/* Item token. */
10292     int objc,			/* Number of arguments. */
10293     Tcl_Obj *CONST objv[]	/* Argument values. */
10294     )
10295 {
10296     Tcl_Interp *interp = tree->interp;
10297     int i;
10298 
10299     if (objc <= 0)
10300 	return TCL_OK;
10301     for (i = 0; i < objc; i += 2) {
10302 	if (!IsHeaderOption(interp, objv[i])) {
10303 	    FormatResult(interp, "unknown option \"%s\"", Tcl_GetString(objv[i]));
10304 	    return TCL_ERROR;
10305 	}
10306     }
10307     return Item_Configure(tree, item, objc, objv);
10308 }
10309 
10310 /*
10311  *----------------------------------------------------------------------
10312  *
10313  * TreeItem_AppendHeaderOptionInfo --
10314  *
10315  *	Gets a config list for each item option that is relevent
10316  *	to a header.
10317  *
10318  * Results:
10319  *	A standard Tcl result.
10320  *
10321  * Side effects:
10322  *	None.
10323  *
10324  *----------------------------------------------------------------------
10325  */
10326 
10327 int
TreeItem_GetHeaderOptionInfo(TreeCtrl * tree,TreeHeader header,Tcl_Obj * objPtr,Tcl_Obj * resultObjPtr)10328 TreeItem_GetHeaderOptionInfo(
10329     TreeCtrl *tree,		/* Widget info. */
10330     TreeHeader header,		/* Header token. */
10331     Tcl_Obj *objPtr,		/* Option name, or NULL for all options. */
10332     Tcl_Obj *resultObjPtr	/* List object to append to, or NULL
10333 				 * if objPtr is not NULL. */
10334     )
10335 {
10336     Tcl_Interp *interp = tree->interp;
10337     TreeItem item = TreeHeader_GetItem(header);
10338     Tcl_Obj *listObjPtr;
10339     static CONST char *headerOptions[] = {
10340 	"-height", "-tags", "-visible", NULL
10341     };
10342     int i;
10343 
10344     if (objPtr != NULL) {
10345 	if (!IsHeaderOption(interp, objPtr)) {
10346 	    FormatResult(interp, "unknown option \"%s\"", Tcl_GetString(objPtr));
10347 	    return TCL_ERROR;
10348 	}
10349 	listObjPtr = Tk_GetOptionInfo(tree->interp, (char *) item,
10350 		tree->itemOptionTable, objPtr, tree->tkwin);
10351 	if (listObjPtr == NULL)
10352 	    return TCL_ERROR;
10353 	Tcl_SetObjResult(interp, listObjPtr);
10354 	return TCL_OK;
10355     }
10356     for (i = 0; headerOptions[i] != NULL; i++) {
10357 	objPtr = Tcl_NewStringObj(headerOptions[i], -1);
10358 	Tcl_IncrRefCount(objPtr);
10359 	listObjPtr = Tk_GetOptionInfo(tree->interp, (char *) item,
10360 		tree->itemOptionTable, objPtr, tree->tkwin);
10361 	Tcl_DecrRefCount(objPtr);
10362 	if (listObjPtr == NULL)
10363 	    return TCL_ERROR;
10364 	if (Tcl_ListObjAppendElement(tree->interp, resultObjPtr, listObjPtr) != TCL_OK)
10365 	    return TCL_ERROR;
10366     }
10367     return TCL_OK;
10368 }
10369 
10370 /*
10371  *----------------------------------------------------------------------
10372  *
10373  * TreeItem_InitWidget --
10374  *
10375  *	Perform item-related initialization when a new TreeCtrl is
10376  *	created.
10377  *
10378  * Results:
10379  *	TCL_OK.
10380  *
10381  * Side effects:
10382  *	Memory is allocated.
10383  *
10384  *----------------------------------------------------------------------
10385  */
10386 
10387 int
TreeItem_InitWidget(TreeCtrl * tree)10388 TreeItem_InitWidget(
10389     TreeCtrl *tree		/* Widget info. */
10390     )
10391 {
10392     ItemButtonCO_Init(itemOptionSpecs, "-button", ITEM_FLAG_BUTTON,
10393 	    ITEM_FLAG_BUTTON_AUTO);
10394     BooleanFlagCO_Init(itemOptionSpecs, "-visible", ITEM_FLAG_VISIBLE);
10395     BooleanFlagCO_Init(itemOptionSpecs, "-wrap", ITEM_FLAG_WRAP);
10396 
10397     tree->itemOptionTable = Tk_CreateOptionTable(tree->interp, itemOptionSpecs);
10398 
10399     tree->root = Item_AllocRoot(tree);
10400     tree->activeItem = tree->root; /* always non-null */
10401     tree->anchorItem = tree->root; /* always non-null */
10402 
10403     return TCL_OK;
10404 }
10405 
10406 /*
10407  *----------------------------------------------------------------------
10408  *
10409  * TreeItem_FreeWidget --
10410  *
10411  *	Free item-related resources for a deleted TreeCtrl.
10412  *
10413  * Results:
10414  *	None.
10415  *
10416  * Side effects:
10417  *	None.
10418  *
10419  *----------------------------------------------------------------------
10420  */
10421 
10422 void
TreeItem_FreeWidget(TreeCtrl * tree)10423 TreeItem_FreeWidget(
10424     TreeCtrl *tree		/* Widget info. */
10425     )
10426 {
10427     SpanInfoStack *siStack = tree->itemSpanPriv;
10428 
10429     while (siStack != NULL) {
10430 	SpanInfoStack *next = siStack->next;
10431 	if (siStack->spans != NULL)
10432 	    ckfree((char *) siStack->spans);
10433 	if (siStack->columns != NULL)
10434 	    ckfree((char *) siStack->columns);
10435 	ckfree((char *) siStack);
10436 	siStack = next;
10437     }
10438 }
10439