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