1 /*
2  * bltTreeViewCmd.c --
3  *
4  *	This module implements an hierarchy widget for the BLT toolkit.
5  *
6  * Copyright 1998-1999 Lucent Technologies, Inc.
7  *
8  * Permission to use, copy, modify, and distribute this software and
9  * its documentation for any purpose and without fee is hereby
10  * granted, provided that the above copyright notice appear in all
11  * copies and that both that the copyright notice and warranty
12  * disclaimer appear in supporting documentation, and that the names
13  * of Lucent Technologies or any of their entities not be used in
14  * advertising or publicity pertaining to distribution of the software
15  * without specific, written prior permission.
16  *
17  * Lucent Technologies disclaims all warranties with regard to this
18  * software, including all implied warranties of merchantability and
19  * fitness.  In no event shall Lucent Technologies be liable for any
20  * special, indirect or consequential damages or any damages
21  * whatsoever resulting from loss of use, data or profits, whether in
22  * an action of contract, negligence or other tortuous action, arising
23  * out of or in connection with the use or performance of this
24  * software.
25  *
26  *	The "treeview" widget was created by George A. Howlett.
27  *      Extensive cleanups and enhancements by Peter MacDonald.
28  */
29 
30 /*
31  * TODO:
32  *
33  * BUGS:
34  *   1.  "open" operation should change scroll offset so that as many
35  *	 new entries (up to half a screen) can be seen.
36  *   2.  "open" needs to adjust the scrolloffset so that the same entry
37  *	 is seen at the same place.
38  */
39 #include "bltInt.h"
40 
41 #ifndef NO_TREEVIEW
42 
43 #include "bltTreeView.h"
44 #include "bltList.h"
45 #include <X11/Xutil.h>
46 #include <X11/Xatom.h>
47 
48 #define CLAMP(val,low,hi)	\
49 	(((val) < (low)) ? (low) : ((val) > (hi)) ? (hi) : (val))
50 
51 static TreeViewCompareProc ExactCompare, GlobCompare, RegexpCompare, InlistCompare;
52 static TreeViewApplyProc ShowEntryApplyProc, HideEntryApplyProc,
53 	MapAncestorsApplyProc, FixSelectionsApplyProc;
54 static Tk_LostSelProc LostSelection;
55 static int SelectEntryApplyProc( TreeView *tvPtr, TreeViewEntry *entryPtr, TreeViewColumn *columnPtr);
56 static int GetEntryFromObj2( TreeView *tvPtr, Tcl_Obj *objPtr, TreeViewEntry **entryPtrPtr);
57 static int TagDefine( TreeView *tvPtr, Tcl_Interp *interp, char *tagName);
58 
59 extern Blt_CustomOption bltTreeViewIconsOption;
60 extern Blt_CustomOption bltTreeViewUidOption;
61 extern Blt_CustomOption bltTreeViewTreeOption;
62 extern Blt_CustomOption bltTreeViewStyleOption;
63 
64 extern Blt_ConfigSpec bltTreeViewButtonSpecs[];
65 extern Blt_ConfigSpec bltTreeViewSpecs[];
66 extern Blt_ConfigSpec bltTreeViewEntrySpecs[];
67 
68 #define TAG_UNKNOWN	 (1<<0)
69 #define TAG_RESERVED	 (1<<1)
70 #define TAG_USER_DEFINED (1<<2)
71 
72 #define TAG_SINGLE	(1<<3)
73 #define TAG_MULTIPLE	(1<<4)
74 #define TAG_LIST	(1<<5)
75 #define TAG_ALL		(1<<6)
76 #define TAG_ROOTCHILDREN	(1<<7)
77 
78 /*
79  *----------------------------------------------------------------------
80  *
81  * SkipSeparators --
82  *
83  *	Moves the character pointer past one of more separators.
84  *
85  * Results:
86  *	Returns the updates character pointer.
87  *
88  *----------------------------------------------------------------------
89  */
90 static char *
SkipSeparators(path,separator,length)91 SkipSeparators(path, separator, length)
92     char *path, *separator;
93     int length;
94 {
95     while ((path[0] == separator[0]) &&
96 	   (strncmp(path, separator, length) == 0)) {
97 	path += length;
98     }
99     return path;
100 }
101 
102 /*
103  *----------------------------------------------------------------------
104  *
105  * DeleteNode --
106  *
107  *	Delete the node and its descendants.  Don't remove the root
108  *	node, though.  If the root node is specified, simply remove
109  *	all its children.
110  *
111  *----------------------------------------------------------------------
112  */
113 static void
DeleteNode(tvPtr,node)114 DeleteNode(tvPtr, node)
115     TreeView *tvPtr;
116     Blt_TreeNode node;
117 {
118     Blt_TreeNode root;
119 
120     if (!Blt_TreeTagTableIsShared(tvPtr->tree)) {
121 	Blt_TreeClearTags(tvPtr->tree, node);
122     }
123     root = tvPtr->rootNode;
124     if (node == root) {
125 	Blt_TreeNode next;
126 	/* Don't delete the root node. Simply clean out the tree. */
127 	for (node = Blt_TreeFirstChild(node); node != NULL; node = next) {
128 	    next = Blt_TreeNextSibling(node);
129 	    Blt_TreeDeleteNode(tvPtr->tree, node);
130 	}
131     } else if (Blt_TreeIsAncestor(root, node)) {
132 	Blt_TreeDeleteNode(tvPtr->tree, node);
133     }
134 }
135 
136 /*
137  *----------------------------------------------------------------------
138  *
139  * SplitPath --
140  *
141  *	Returns the trailing component of the given path.  Trailing
142  *	separators are ignored.
143  *
144  * Results:
145  *	Returns the string of the tail component.
146  *
147  *----------------------------------------------------------------------
148  */
149 static int
SplitPath(tvPtr,path,depthPtr,compPtrPtr)150 SplitPath(tvPtr, path, depthPtr, compPtrPtr)
151     TreeView *tvPtr;
152     char *path;
153     int *depthPtr;
154     char ***compPtrPtr;
155 {
156     int skipLen, pathLen;
157     int depth, listSize;
158     char **components;
159     register char *p;
160     char *sep;
161 
162     if (tvPtr->pathSep == SEPARATOR_LIST) {
163 	if (Tcl_SplitList(tvPtr->interp, path, depthPtr, compPtrPtr)
164 	    != TCL_OK) {
165 	    return TCL_ERROR;
166 	}
167 	return TCL_OK;
168     }
169     pathLen = strlen(path);
170 
171     skipLen = strlen(tvPtr->pathSep);
172     path = SkipSeparators(path, tvPtr->pathSep, skipLen);
173     depth = pathLen / skipLen;
174 
175     listSize = (depth + 1) * sizeof(char *);
176     components = Blt_Malloc(listSize + (pathLen + 1));
177     assert(components);
178     p = (char *)components + listSize;
179     strcpy(p, path);
180 
181     sep = strstr(p, tvPtr->pathSep);
182     depth = 0;
183     while ((*p != '\0') && (sep != NULL)) {
184 	*sep = '\0';
185 	components[depth++] = p;
186 	p = SkipSeparators(sep + skipLen, tvPtr->pathSep, skipLen);
187 	sep = strstr(p, tvPtr->pathSep);
188     }
189     if (*p != '\0') {
190 	components[depth++] = p;
191     }
192     components[depth] = NULL;
193     *depthPtr = depth;
194     *compPtrPtr = components;
195     return TCL_OK;
196 }
197 
198 
199 static TreeViewEntry *
LastEntry(tvPtr,entryPtr,mask)200 LastEntry(tvPtr, entryPtr, mask)
201     TreeView *tvPtr;
202     TreeViewEntry *entryPtr;
203     unsigned int mask;
204 {
205     Blt_TreeNode next;
206     TreeViewEntry *nextPtr;
207 
208     next = Blt_TreeLastChild(entryPtr->node);
209     while (next != NULL) {
210 	nextPtr = Blt_NodeToEntry(tvPtr, next);
211 	if ((nextPtr->flags & mask) != mask) {
212              entryPtr = nextPtr;
213              break;
214 	}
215 	entryPtr = nextPtr;
216 	next = Blt_TreeLastChild(next);
217     }
218     return entryPtr;
219 }
220 
221 static TreeViewEntry *
LastNode(tvPtr,entryPtr,mask)222 LastNode(tvPtr, entryPtr, mask)
223     TreeView *tvPtr;
224     TreeViewEntry *entryPtr;
225     unsigned int mask;
226 {
227     TreeViewEntry *nextPtr;
228 
229     while ((nextPtr = LastEntry(tvPtr, entryPtr, mask)) != NULL &&
230         nextPtr != entryPtr) {
231 	entryPtr = nextPtr;
232 	if ((nextPtr->flags & ENTRY_CLOSED) && entryPtr != tvPtr->rootPtr) {
233 	    break;
234 	}
235     }
236     return entryPtr;
237 }
238 
239 
240 /*
241  *----------------------------------------------------------------------
242  *
243  * ShowEntryApplyProc --
244  *
245  * Results:
246  *	Always returns TCL_OK.
247  *
248  *----------------------------------------------------------------------
249  */
250 /*ARGSUSED*/
251 static int
ShowEntryApplyProc(tvPtr,entryPtr)252 ShowEntryApplyProc(tvPtr, entryPtr)
253     TreeView *tvPtr;		/* Not used. */
254     TreeViewEntry *entryPtr;
255 {
256     entryPtr->flags &= ~ENTRY_HIDDEN;
257     return TCL_OK;
258 }
259 
260 /*
261  *----------------------------------------------------------------------
262  *
263  * HideEntryApplyProc --
264  *
265  * Results:
266  *	Always returns TCL_OK.
267  *
268  *----------------------------------------------------------------------
269  */
270 /*ARGSUSED*/
271 static int
HideEntryApplyProc(tvPtr,entryPtr)272 HideEntryApplyProc(tvPtr, entryPtr)
273     TreeView *tvPtr;		/* Not used. */
274     TreeViewEntry *entryPtr;
275 {
276     entryPtr->flags |= ENTRY_HIDDEN;
277     return TCL_OK;
278 }
279 
280 static void
MapAncestors(tvPtr,entryPtr)281 MapAncestors(tvPtr, entryPtr)
282     TreeView *tvPtr;
283     TreeViewEntry *entryPtr;
284 {
285     while (entryPtr && entryPtr != tvPtr->rootPtr) {
286 	entryPtr = Blt_TreeViewParentEntry(entryPtr);
287 	if (entryPtr && entryPtr->flags & (ENTRY_CLOSED | ENTRY_HIDDEN)) {
288 	    tvPtr->flags |= TV_LAYOUT;
289 	    entryPtr->flags &= ~(ENTRY_CLOSED | ENTRY_HIDDEN);
290 	}
291     }
292 }
293 
294 /*
295  *----------------------------------------------------------------------
296  *
297  * MapAncestorsApplyProc --
298  *
299  *	If a node in mapped, then all its ancestors must be mapped also.
300  *	This routine traverses upwards and maps each unmapped ancestor.
301  *	It's assumed that for any mapped ancestor, all it's ancestors
302  *	will already be mapped too.
303  *
304  * Results:
305  *	Always returns TCL_OK.
306  *
307  *----------------------------------------------------------------------
308  */
309 static int
MapAncestorsApplyProc(tvPtr,entryPtr)310 MapAncestorsApplyProc(tvPtr, entryPtr)
311     TreeView *tvPtr;
312     TreeViewEntry *entryPtr;
313 {
314     /*
315      * Make sure that all the ancestors of this entry are mapped too.
316      */
317     while (entryPtr != tvPtr->rootPtr) {
318 	entryPtr = Blt_TreeViewParentEntry(entryPtr);
319 	if ((entryPtr->flags & (ENTRY_HIDDEN | ENTRY_CLOSED)) == 0) {
320 	    break;		/* Assume ancestors are also mapped. */
321 	}
322 	entryPtr->flags &= ~(ENTRY_HIDDEN | ENTRY_CLOSED);
323     }
324     return TCL_OK;
325 }
326 
327 /*
328  *----------------------------------------------------------------------
329  *
330  * FindPath --
331  *
332  *	Finds the node designated by the given path.  Each path
333  *	component is searched for as the tree is traversed.
334  *
335  *	A leading character string is trimmed off the path if it
336  *	matches the one designated (see the -trimleft option).
337  *
338  *	If no separator is designated (see the -separator
339  *	configuration option), the path is considered a Tcl list.
340  *	Otherwise the each component of the path is separated by a
341  *	character string.  Leading and trailing separators are
342  *	ignored.  Multiple separators are treated as one.
343  *
344  * Results:
345  *	Returns the pointer to the designated node.  If any component
346  *	can't be found, NULL is returned.
347  *
348  *----------------------------------------------------------------------
349  */
350 static TreeViewEntry *
FindPath(tvPtr,rootPtr,path)351 FindPath(tvPtr, rootPtr, path)
352     TreeView *tvPtr;
353     TreeViewEntry *rootPtr;
354     char *path;
355 {
356     Blt_TreeNode child;
357     char **compArr;
358     char *name;
359     int nComp;
360     register char **p;
361     TreeViewEntry *entryPtr;
362 
363     /* Trim off characters that we don't want */
364     if (tvPtr->trimLeft != NULL) {
365 	register char *s1, *s2;
366 
367 	/* Trim off leading character string if one exists. */
368 	for (s1 = path, s2 = tvPtr->trimLeft; *s2 != '\0'; s2++, s1++) {
369 	    if (*s1 != *s2) {
370 		break;
371 	    }
372 	}
373 	if (*s2 == '\0') {
374 	    path = s1;
375 	}
376     }
377     if (*path == '\0') {
378 	return rootPtr;
379     }
380     name = path;
381     entryPtr = rootPtr;
382     if (tvPtr->pathSep == SEPARATOR_NONE) {
383         child = Blt_TreeFindChildRev(entryPtr->node, name, tvPtr->insertFirst);
384 	if (child == NULL) {
385 	    goto error;
386 	}
387 	return Blt_NodeToEntry(tvPtr, child);
388     }
389 
390     if (SplitPath(tvPtr, path, &nComp, &compArr) != TCL_OK) {
391 	return NULL;
392     }
393     for (p = compArr; *p != NULL; p++) {
394 	name = *p;
395 	child = Blt_TreeFindChildRev(entryPtr->node, name, tvPtr->insertFirst);
396 	if (child == NULL) {
397 	    Blt_Free(compArr);
398 	    goto error;
399 	}
400 	entryPtr = Blt_NodeToEntry(tvPtr, child);
401     }
402     Blt_Free(compArr);
403     return entryPtr;
404  error:
405     {
406 	Tcl_DString dString;
407 
408         Tcl_DStringInit(&dString);
409 	Blt_TreeViewGetFullName(tvPtr, entryPtr, FALSE, &dString);
410 	Tcl_AppendResult(tvPtr->interp, "can't find node \"", name,
411 		 "\" in parent node \"", Tcl_DStringValue(&dString), "\"",
412 		(char *)NULL);
413 	Tcl_DStringFree(&dString);
414     }
415     return NULL;
416 
417 }
418 
419 /*
420  *----------------------------------------------------------------------
421  *
422  * NodeToObj --
423  *
424  *	Converts a node pointer to a string representation.
425  *	The string contains the node's index which is unique.
426  *
427  * Results:
428  *	The string representation of the node is returned.  Note that
429  *	the string is stored statically, so that callers must save the
430  *	string before the next call to this routine overwrites the
431  *	static array again.
432  *
433  *----------------------------------------------------------------------
434  */
435 static Tcl_Obj *
NodeToObj(node)436 NodeToObj(node)
437     Blt_TreeNode node;
438 {
439     return Tcl_NewIntObj(Blt_TreeNodeId(node));
440     /*char string[200];
441 
442     sprintf(string, "%d", Blt_TreeNodeId(node));
443     return Tcl_NewStringObj(string, -1);*/
444 }
445 
446 
447 static int
GetEntryFromSpecialId(tvPtr,string,entryPtrPtr)448 GetEntryFromSpecialId(tvPtr, string, entryPtrPtr)
449     TreeView *tvPtr;
450     char *string;
451     TreeViewEntry **entryPtrPtr;
452 {
453     Blt_TreeNode node;
454     TreeViewEntry *fromPtr, *entryPtr;
455     char c;
456 
457     entryPtr = NULL;
458     fromPtr = tvPtr->fromPtr;
459     if (fromPtr == NULL) {
460 	fromPtr = tvPtr->focusPtr;
461     }
462     if (fromPtr == NULL) {
463 	fromPtr = tvPtr->rootPtr;
464     }
465     c = string[0];
466     if (c == '@') {
467 	int x, y;
468 
469 	if (Blt_GetXY(tvPtr->interp, tvPtr->tkwin, string, &x, &y) == TCL_OK) {
470 	    entryPtr = Blt_TreeViewNearestEntry(tvPtr, x, y, TRUE);
471 	}
472     } else if ((c == 'b') && (strcmp(string, "bottom") == 0)) {
473 	if (tvPtr->flatView) {
474 	    entryPtr = FLATIND(tvPtr, tvPtr->nEntries - 1);
475 	} else {
476 	    entryPtr = LastNode(tvPtr, tvPtr->rootPtr, ENTRY_MASK);
477 	}
478     } else if ((c == 'l') && (strcmp(string, "last") == 0)) {
479         entryPtr = LastNode(tvPtr, tvPtr->rootPtr, 0);
480     } else if ((c == 't') && (strcmp(string, "tail") == 0)) {
481         entryPtr = LastNode(tvPtr, tvPtr->rootPtr, 0);
482     } else if ((c == 't') && (strcmp(string, "top") == 0)) {
483 	if (tvPtr->flatView) {
484 	    entryPtr = FLATIND(tvPtr,0);
485 	} else {
486 	    entryPtr = tvPtr->rootPtr;
487 	}
488         if (entryPtr != NULL && entryPtr == tvPtr->rootPtr &&
489             tvPtr->flags & TV_HIDE_ROOT) {
490              entryPtr = Blt_TreeViewNextEntry(entryPtr, ENTRY_MASK);
491         }
492      } else if ((c == 'e') && (strcmp(string, "end") == 0)) {
493 	entryPtr = LastEntry(tvPtr, tvPtr->rootPtr, ENTRY_MASK);
494     } else if ((c == 'a') && (strcmp(string, "anchor") == 0)) {
495 	entryPtr = tvPtr->selAnchorPtr;
496      } else if ((c == 'a') && (strcmp(string, "active") == 0)) {
497          entryPtr = tvPtr->activePtr;
498      } else if ((c == 'f') && (strcmp(string, "focus") == 0)) {
499 	entryPtr = tvPtr->focusPtr;
500 	if ((entryPtr == tvPtr->rootPtr) && (tvPtr->flags & TV_HIDE_ROOT)) {
501 	    entryPtr = Blt_TreeViewNextEntry(tvPtr->rootPtr, ENTRY_MASK);
502 	}
503     } else if ((c == 'r') && (strcmp(string, "root") == 0)) {
504 	entryPtr = tvPtr->rootPtr;
505     } else if ((c == 'p') && (strcmp(string, "parent") == 0)) {
506 	if (fromPtr != tvPtr->rootPtr) {
507 	    entryPtr = Blt_TreeViewParentEntry(fromPtr);
508 	}
509     } else if ((c == 'c') && (strcmp(string, "current") == 0)) {
510 	/* Can't trust picked item, if entries have been
511 	 * added or deleted. */
512 	if (!(tvPtr->flags & TV_DIRTY)) {
513 	    ClientData context;
514 
515 	    context = Blt_GetCurrentContext(tvPtr->bindTable);
516 
517 	    if ((context == ITEM_ENTRY) ||
518 		(context == ITEM_ENTRY_BUTTON) ||
519 		((unsigned int)context >= (unsigned int)ITEM_STYLE)) {
520 		entryPtr = Blt_GetCurrentItem(tvPtr->bindTable);
521 	    }
522 	}
523     } else if ((c == 'u') && (strcmp(string, "up") == 0)) {
524 	entryPtr = fromPtr;
525 	if (tvPtr->flatView) {
526 	    int i;
527 
528 	    i = entryPtr->flatIndex - 1;
529 	    if (i >= 0) {
530 		entryPtr = FLATIND( tvPtr, i);
531 	    }
532 	} else {
533 	    entryPtr = Blt_TreeViewPrevEntry(fromPtr, ENTRY_MASK);
534 	    if (entryPtr == NULL) {
535 		entryPtr = fromPtr;
536 	    }
537 	    if ((entryPtr == tvPtr->rootPtr) &&
538 		(tvPtr->flags & TV_HIDE_ROOT)) {
539 		entryPtr = Blt_TreeViewNextEntry(entryPtr, ENTRY_MASK);
540 	    }
541 	}
542     } else if ((c == 'd') && (strcmp(string, "down") == 0)) {
543 	entryPtr = fromPtr;
544 	if (tvPtr->flatView) {
545 	    int i;
546 
547 	    i = entryPtr->flatIndex + 1;
548 	    if (i < tvPtr->nEntries) {
549 		entryPtr = FLATIND(tvPtr, i);
550 	    }
551 	} else {
552 	    entryPtr = Blt_TreeViewNextEntry(fromPtr, ENTRY_MASK);
553 	    if (entryPtr == NULL) {
554 		entryPtr = fromPtr;
555 	    }
556 	    if ((entryPtr == tvPtr->rootPtr) &&
557 		(tvPtr->flags & TV_HIDE_ROOT)) {
558 		entryPtr = Blt_TreeViewNextEntry(entryPtr, ENTRY_MASK);
559 	    }
560 	}
561     } else if ((c == 'p') && (strcmp(string, "prev") == 0)) {
562 	entryPtr = fromPtr;
563 	if (tvPtr->flatView) {
564 	    int i;
565 
566 	    i = entryPtr->flatIndex - 1;
567 	    if (i < 0) {
568 		i = tvPtr->nEntries - 1;
569 	    }
570 	    entryPtr = FLATIND( tvPtr, i);
571 	} else {
572 	    entryPtr = Blt_TreeViewPrevEntry(fromPtr, ENTRY_MASK);
573 	    if (entryPtr == NULL) {
574 		entryPtr = LastEntry(tvPtr, tvPtr->rootPtr, ENTRY_MASK);
575 	    }
576 	    if ((entryPtr == tvPtr->rootPtr) &&
577 		(tvPtr->flags & TV_HIDE_ROOT)) {
578 		entryPtr = LastEntry(tvPtr, tvPtr->rootPtr, ENTRY_MASK);
579 		/*entryPtr = Blt_TreeViewNextEntry(entryPtr, ENTRY_MASK); */
580 	    }
581 	}
582     } else if ((c == 'n') && (strcmp(string, "next") == 0)) {
583 	entryPtr = fromPtr;
584 	if (tvPtr->flatView) {
585 	    int i;
586 
587 	    i = entryPtr->flatIndex + 1;
588 	    if (i >= tvPtr->nEntries) {
589 		i = 0;
590 	    }
591 	    entryPtr = FLATIND(tvPtr, i);
592 	} else {
593 	    entryPtr = Blt_TreeViewNextEntry(fromPtr, ENTRY_MASK);
594 	    if (entryPtr == NULL) {
595 		if (tvPtr->flags & TV_HIDE_ROOT) {
596 		    entryPtr = Blt_TreeViewNextEntry(tvPtr->rootPtr,ENTRY_MASK);
597 		} else {
598 		    entryPtr = tvPtr->rootPtr;
599 		}
600 	    }
601 	}
602     } else if ((c == 'n') && (strcmp(string, "nextsibling") == 0)) {
603 	node = Blt_TreeNextSibling(fromPtr->node);
604 	if (node != NULL) {
605 	    entryPtr = Blt_NodeToEntry(tvPtr, node);
606 	}
607     } else if ((c == 'p') && (strcmp(string, "prevsibling") == 0)) {
608 	node = Blt_TreePrevSibling(fromPtr->node);
609 	if (node != NULL) {
610 	    entryPtr = Blt_NodeToEntry(tvPtr, node);
611 	}
612     } else if ((c == 'v') && (strcmp(string, "view.top") == 0)) {
613 	if (tvPtr->nVisible > 0) {
614 	    entryPtr = tvPtr->visibleArr[0];
615 	}
616     } else if ((c == 'v') && (strcmp(string, "view.bottom") == 0)) {
617 	if (tvPtr->nVisible > 0) {
618 	    entryPtr = tvPtr->visibleArr[tvPtr->nVisible - 1];
619 	}
620     } else {
621 	return TCL_ERROR;
622     }
623     *entryPtrPtr = entryPtr;
624     return TCL_OK;
625 }
626 
627 static int
GetTagInfo(tvPtr,tagName,infoPtr)628 GetTagInfo(tvPtr, tagName, infoPtr)
629     TreeView *tvPtr;
630     char *tagName;
631     TreeViewTagInfo *infoPtr;
632 {
633     static int cnt = 0;
634 
635     infoPtr->tagType = TAG_RESERVED | TAG_SINGLE;
636     infoPtr->entryPtr = NULL;
637 
638     if (strcmp(tagName, "all") == 0) {
639 	infoPtr->entryPtr = tvPtr->rootPtr;
640 	infoPtr->tagType |= (TAG_ALL|TAG_MULTIPLE);
641 
642         infoPtr->node = infoPtr->entryPtr->node;
643         infoPtr->inode = infoPtr->node->inode;
644     } else if (strcmp(tagName, "nonroot") == 0) {
645         infoPtr->entryPtr = Blt_TreeViewNextEntry(tvPtr->rootPtr, 0);
646 	infoPtr->tagType |= (TAG_ALL|TAG_MULTIPLE);
647          if (infoPtr->entryPtr) {
648              infoPtr->node = infoPtr->entryPtr->node;
649              infoPtr->inode = infoPtr->node->inode;
650          }
651      } else if (strcmp(tagName, "rootchildren") == 0) {
652          infoPtr->entryPtr = Blt_TreeViewNextEntry(tvPtr->rootPtr, 0);
653          infoPtr->tagType |= (TAG_ROOTCHILDREN|TAG_MULTIPLE);
654          if (infoPtr->entryPtr) {
655              infoPtr->node = infoPtr->entryPtr->node;
656              infoPtr->inode = infoPtr->node->inode;
657          }
658      } else {
659         /* TODO: is this broken: are nodes not being found for tags.  FIXED???. */
660 	Blt_HashTable *tablePtr;
661 
662 	tablePtr = Blt_TreeTagHashTable(tvPtr->tree, tagName);
663 	if (tablePtr != NULL) {
664 	    Blt_HashEntry *hPtr;
665 
666 	    infoPtr->tagType = TAG_USER_DEFINED; /* Empty tags are not
667 						  * an error. */
668 	    hPtr = Blt_FirstHashEntry(tablePtr, &infoPtr->cursor);
669 	    if (hPtr != NULL) {
670 		Blt_TreeNode node;
671 		int inode;
672 
673 		node = Blt_GetHashValue(hPtr);
674 		inode = node->inode;
675 		infoPtr->entryPtr = Blt_NodeToEntry(tvPtr, node);
676                 if (infoPtr->entryPtr == NULL && cnt++ == 0) {
677                     /* fprintf(stderr, "Dangling node: %d\n", node->inode); */
678                 }
679                 if (infoPtr->entryPtr != NULL) {
680                     infoPtr->node = infoPtr->entryPtr->node;
681                     infoPtr->inode = infoPtr->node->inode;
682                 }
683                 if (Blt_TreeNodeDeleted(infoPtr->entryPtr->node)) {
684                     return TCL_ERROR;
685                 }
686                 if (tablePtr->numEntries > 1) {
687                     infoPtr->tagType |= TAG_MULTIPLE;
688                 }
689 	    }
690 	}  else {
691 	    infoPtr->tagType = TAG_UNKNOWN;
692 	    Tcl_AppendResult(tvPtr->interp, "can't find tag or id \"", tagName,
693 		"\" in \"", Tk_PathName(tvPtr->tkwin), "\"", (char *)NULL);
694 	    return TCL_ERROR;
695 	}
696     }
697     return TCL_OK;
698 }
699 
700 TreeViewEntry *
Blt_TreeViewFirstTaggedEntry(infoPtr)701 Blt_TreeViewFirstTaggedEntry(infoPtr)
702     TreeViewTagInfo *infoPtr;
703 {
704     return infoPtr->entryPtr;
705 }
706 
707 TreeViewEntry *
Blt_TreeViewNextTaggedEntry(infoPtr)708 Blt_TreeViewNextTaggedEntry(infoPtr)
709     TreeViewTagInfo *infoPtr;
710 {
711     TreeViewEntry *entryPtr;
712     static int cnt = 0;
713     Blt_TreeNode node;
714 
715     entryPtr = NULL;
716     if (infoPtr->entryPtr != NULL) {
717 	TreeView *tvPtr = infoPtr->entryPtr->tvPtr;
718 
719          if (infoPtr->tagType == TAG_LIST) {
720              int inode, i;
721              i = ++infoPtr->idx;
722 
723              if (i >= infoPtr->objc) {
724                  return NULL;
725              }
726              if (Tcl_GetIntFromObj(tvPtr->interp, infoPtr->objv[i], &inode) != TCL_OK) {
727                  return NULL;
728              }
729              node = Blt_TreeGetNode(tvPtr->tree, inode);
730              infoPtr->entryPtr = Blt_NodeToEntry(tvPtr, node);
731              return infoPtr->entryPtr;
732          }
733 
734          if (infoPtr->tagType & TAG_ALL) {
735              if (Blt_TreeNodeDeleted(infoPtr->node) || (infoPtr->inode != infoPtr->node->inode)) {
736                  return NULL;
737              }
738              entryPtr = Blt_TreeViewNextEntry(infoPtr->entryPtr, 0);
739              if (entryPtr != NULL) {
740                  infoPtr->node =entryPtr->node;
741                  infoPtr->inode = infoPtr->node->inode;
742              }
743 
744          } else if (infoPtr->tagType & TAG_ROOTCHILDREN) {
745              if (Blt_TreeNodeDeleted(infoPtr->node) || (infoPtr->inode != infoPtr->node->inode)) {
746                  return NULL;
747              }
748              entryPtr = Blt_TreeViewNextSibling(infoPtr->entryPtr, 0);
749              if (entryPtr != NULL) {
750                  infoPtr->node =entryPtr->node;
751                  infoPtr->inode = infoPtr->node->inode;
752              }
753          } else if (infoPtr->tagType & TAG_MULTIPLE) {
754 	    Blt_HashEntry *hPtr;
755 	    if (infoPtr->tPtr && infoPtr->tPtr->refCount<=1) {
756                 /* Tag was deleted. */
757                 return NULL;
758             }
759 	    hPtr = Blt_NextHashEntry(&infoPtr->cursor);
760 	    if (hPtr != NULL) {
761 
762 		node = Blt_GetHashValue(hPtr);
763 		entryPtr = Blt_NodeToEntry(tvPtr, node);
764 		/*TODO: sometimes this fails. Fixed tag deleted problem. */
765 		if (entryPtr == NULL && cnt++ == 0) {
766 		    /*fprintf(stderr, "dangling node: %d\n", node->inode); */
767 		}
768 	    }
769 	}
770 	infoPtr->entryPtr = entryPtr;
771     }
772     return entryPtr;
773 }
774 
775 /*ARGSUSED*/
776 void
Blt_TreeViewGetTags(interp,tvPtr,entryPtr,list)777 Blt_TreeViewGetTags(interp, tvPtr, entryPtr, list)
778     Tcl_Interp *interp;		/* Not used. */
779     TreeView *tvPtr;
780     TreeViewEntry *entryPtr;
781     Blt_List list;
782 {
783     Blt_HashEntry *hPtr;
784     Blt_HashSearch cursor;
785     Blt_TreeTagEntry *tPtr;
786 
787     for (hPtr = Blt_TreeFirstTag(tvPtr->tree, &cursor); hPtr != NULL;
788 	hPtr = Blt_NextHashEntry(&cursor)) {
789 	tPtr = Blt_GetHashValue(hPtr);
790 	hPtr = Blt_FindHashEntry(&tPtr->nodeTable, (char *)entryPtr->node);
791 	if (hPtr != NULL) {
792 	    Blt_ListAppend(list, Blt_TreeViewGetUid(tvPtr, tPtr->tagName),0);
793 	}
794     }
795 }
796 
797 /*
798  *----------------------------------------------------------------------
799  *
800  * AddTag --
801  *
802  *----------------------------------------------------------------------
803  */
804 static int
AddTag(tvPtr,node,tagName)805 AddTag(tvPtr, node, tagName)
806     TreeView *tvPtr;
807     Blt_TreeNode node;
808     char *tagName;
809 {
810     TreeViewEntry *entryPtr;
811 
812     if (strcmp(tagName, "root") == 0 || strcmp(tagName, "all") == 0 ||
813         strcmp(tagName, "nonroot") == 0 || strcmp(tagName, "rootchildren") == 0) {
814 	Tcl_AppendResult(tvPtr->interp, "can't add reserved tag \"",
815 			 tagName, "\"", (char *)NULL);
816 	return TCL_ERROR;
817     }
818     if (isdigit(UCHAR(tagName[0]))) {
819 	Tcl_AppendResult(tvPtr->interp, "invalid tag \"", tagName,
820 		"\": can't start with digit", (char *)NULL);
821 	return TCL_ERROR;
822     }
823     if (isdigit(UCHAR(tagName[0]))) {
824 	Tcl_AppendResult(tvPtr->interp, "invalid tag \"", tagName,
825 		"\": can't start with digit", (char *)NULL);
826 	return TCL_ERROR;
827     }
828     if (tagName[0] == '@') {
829 	Tcl_AppendResult(tvPtr->interp, "invalid tag \"", tagName,
830 		"\": can't start with \"@\"", (char *)NULL);
831 	return TCL_ERROR;
832     }
833     tvPtr->fromPtr = NULL;
834     if (GetEntryFromSpecialId(tvPtr, tagName, &entryPtr) == TCL_OK) {
835 	Tcl_AppendResult(tvPtr->interp, "invalid tag \"", tagName,
836 		"\": is a special id", (char *)NULL);
837 	return TCL_ERROR;
838     }
839     /* Add the tag to the node. */
840     return Blt_TreeAddTag(tvPtr->tree, node, tagName);
841 }
842 
843 int
Blt_TreeViewFindTaggedEntries(tvPtr,objPtr,infoPtr)844 Blt_TreeViewFindTaggedEntries(tvPtr, objPtr, infoPtr)
845     TreeView *tvPtr;
846     Tcl_Obj *objPtr;
847     TreeViewTagInfo *infoPtr;
848 {
849     char *tagName;
850     TreeViewEntry *entryPtr;
851 
852     memset(infoPtr, 0, sizeof(*infoPtr));
853     infoPtr->init = 1;
854     tagName = Tcl_GetString(objPtr);
855     infoPtr->tvPtr = tvPtr;
856     tvPtr->fromPtr = NULL;
857     if (tagName[0] == 0) {
858         infoPtr->tagType = TAG_LIST;
859         infoPtr->entryPtr = NULL;
860         infoPtr->objc = 0;
861         infoPtr->idx = 0;
862         return TCL_OK;
863     }
864     if (strstr(tagName, "->") != NULL) {
865         if (GetEntryFromObj2(tvPtr, objPtr, &infoPtr->entryPtr) != TCL_OK) {
866             return TCL_ERROR;
867         }
868         infoPtr->tagType = (TAG_RESERVED | TAG_SINGLE);
869         return TCL_OK;
870     }
871 
872     if (isdigit(UCHAR(tagName[0]))) {
873 	int inode;
874         Blt_TreeNode node;
875         char *cp = tagName;
876         int i;
877 
878         while (isdigit(UCHAR(*cp)) && *cp != 0) {
879             cp++;
880         }
881         if (*cp != 0) {
882             if (Tcl_ListObjGetElements(tvPtr->interp, objPtr, &infoPtr->objc,
883                 &infoPtr->objv) != TCL_OK) {
884                     return TCL_ERROR;
885             }
886             if (infoPtr->objc<1) {
887                 return TCL_ERROR;
888             }
889             for (i=infoPtr->objc-1; i>=0; i--) {
890                 if (Tcl_GetIntFromObj(tvPtr->interp, infoPtr->objv[i], &inode) != TCL_OK) {
891                     return TCL_ERROR;
892                 }
893                 /* No use checking nodes here as they can later be deleted. */
894                 /*node = Blt_TreeGetNode(tvPtr->tree, inode);
895                 if (node == NULL) {
896                     Tcl_AppendResult(interp, "unknown node: ", Blt_Itoa(inode), 0);
897                     return TCL_ERROR;
898                 }*/
899             }
900             node = Blt_TreeGetNode(tvPtr->tree, inode);
901             infoPtr->objPtr = objPtr;
902             Tcl_IncrRefCount(objPtr);
903             infoPtr->entryPtr = Blt_NodeToEntry(tvPtr, node);
904             infoPtr->tagType = TAG_LIST;
905             infoPtr->idx = 0;
906             return TCL_OK;
907         }
908 
909         if (Tcl_GetIntFromObj(tvPtr->interp, objPtr, &inode) != TCL_OK) {
910 	    return TCL_ERROR;
911         }
912         node = Blt_TreeGetNode(tvPtr->tree, inode);
913         infoPtr->entryPtr = Blt_NodeToEntry(tvPtr, node);
914 	infoPtr->tagType = (TAG_RESERVED | TAG_SINGLE);
915     } else if (GetEntryFromSpecialId(tvPtr, tagName, &entryPtr) == TCL_OK) {
916 	infoPtr->entryPtr = entryPtr;
917 	infoPtr->tagType = (TAG_RESERVED | TAG_SINGLE);
918     } else {
919 	if (GetTagInfo(tvPtr, tagName, infoPtr) != TCL_OK) {
920 	    return TCL_ERROR;
921 	}
922         if ((infoPtr->tagType & TAG_USER_DEFINED)) {
923             infoPtr->tPtr = Blt_TreeTagHashEntry(tvPtr->tree, tagName);
924             Blt_TreeTagRefIncr(infoPtr->tPtr);
925         }
926     }
927     return TCL_OK;
928 }
929 
930 
931 /* Cleanup and release resources. */
932 int
Blt_TreeViewDoneTaggedEntries(infoPtr)933 Blt_TreeViewDoneTaggedEntries(infoPtr)
934     TreeViewTagInfo *infoPtr;
935 {
936     if (infoPtr->init == 1) {
937         infoPtr->init = 0;
938         if (infoPtr->objPtr != NULL) {
939             Tcl_DecrRefCount(infoPtr->objPtr);
940             infoPtr->objPtr = NULL;
941         }
942         if ((infoPtr->tagType & TAG_USER_DEFINED) && infoPtr->tPtr) {
943             Blt_TreeTagRefDecr(infoPtr->tPtr);
944             infoPtr->tPtr = NULL;
945         }
946     }
947     return TCL_OK;
948 }
949 
950 static Blt_TreeNode
MaxNode(Blt_Tree tree)951 MaxNode(Blt_Tree tree) {
952     Blt_TreeNode node, root, mnode;
953     int max = 0;
954 
955     mnode = root = Blt_TreeRootNode(tree);
956     for (node = root; node != NULL; node = Blt_TreeNextNode(root, node)) {
957         if (node->inode > max) {
958             max = node->inode;
959             mnode = node;
960         }
961     }
962     return mnode;
963 }
964 
965 static Blt_TreeNode
ParseModifiers(Tcl_Interp * interp,Blt_Tree tree,Blt_TreeNode node,char * modifiers)966 ParseModifiers(
967     Tcl_Interp *interp,
968     Blt_Tree tree,
969     Blt_TreeNode node,
970     char *modifiers)
971 {
972     char *p, *np;
973 
974     p = modifiers;
975     do {
976 	p += 2;			/* Skip the initial "->" */
977 	np = strstr(p, "->");
978 	if (np != NULL) {
979 	    *np = '\0';
980 	}
981 	if ((*p == 'p') && (strcmp(p, "parentnode") == 0)) {
982 	    node = Blt_TreeNodeParent(node);
983 	} else if ((*p == 'f') && (strcmp(p, "firstchild") == 0)) {
984 	    node = Blt_TreeFirstChild(node);
985 	} else if ((*p == 'l') && (strcmp(p, "lastchild") == 0)) {
986 	    node = Blt_TreeLastChild(node);
987 	} else if ((*p == 'n') && (strcmp(p, "nextnode") == 0)) {
988 	    node = Blt_TreeNextNode(Blt_TreeRootNode(tree), node);
989 	} else if ((*p == 'n') && (strcmp(p, "nextsibling") == 0)) {
990 	    node = Blt_TreeNextSibling(node);
991 	} else if ((*p == 'p') && (strcmp(p, "prevnode") == 0)) {
992 	    node = Blt_TreePrevNode(Blt_TreeRootNode(tree), node);
993 	} else if ((*p == 'p') && (strcmp(p, "prevsibling") == 0)) {
994 	    node = Blt_TreePrevSibling(node);
995 	} else if ((*p == 'm') && (strcmp(p, "maxnode") == 0)) {
996 	    node = MaxNode(tree);
997 	} else if (isdigit(UCHAR(*p))) {
998 	    int inode;
999 
1000 	    if (Tcl_GetInt(interp, p, &inode) != TCL_OK) {
1001 		node = NULL;
1002 	    } else {
1003 		node = Blt_TreeGetNode(tree, inode);
1004 	    }
1005 	} else {
1006 	    char *endp;
1007 
1008 	    if (np != NULL) {
1009 		endp = np - 1;
1010 	    } else {
1011 		endp = p + strlen(p) - 1;
1012 	    }
1013 	    if ((*p == '\'') && (*endp == '\'')) {
1014 		*endp = '\0';
1015 		node = Blt_TreeFindChild(node, p + 1);
1016 		*endp = '\'';
1017 	    } else if ((*p == '"') && (*endp == '"')) {
1018 		*endp = '\0';
1019 		node = Blt_TreeFindChild(node, p + 1);
1020 		*endp = '"';
1021 	    } else {
1022 		node = Blt_TreeFindChild(node, p);
1023 	    }
1024 	}
1025 	if (node == NULL) {
1026 	    goto error;
1027 	}
1028 	if (np != NULL) {
1029 	    *np = '-';		/* Repair the string */
1030 	}
1031 	p = np;
1032     } while (np != NULL);
1033     return node;
1034  error:
1035     if (np != NULL) {
1036 	*np = '-';		/* Repair the string */
1037     }
1038     return NULL;
1039 }
1040 
1041 
1042 /*
1043  *----------------------------------------------------------------------
1044  *
1045  * GetEntryFromObj2 --
1046  *
1047  *	Converts a string into node pointer.  The string may be in one
1048  *	of the following forms:
1049  *
1050  *	    NNN			- inode.
1051  *	    "active"		- Currently active node.
1052  *	    "anchor"		- anchor of selected region.
1053  *	    "current"		- Currently picked node in bindtable.
1054  *	    "focus"		- The node currently with focus.
1055  *	    "root"		- Root node.
1056  *	    "end"		- Last open node in the entire hierarchy.
1057  *	    "next"		- Next open node from the currently active
1058  *				  node. Wraps around back to top.
1059  *	    "prev"		- Previous open node from the currently active
1060  *				  node. Wraps around back to bottom.
1061  *	    "up"		- Next open node from the currently active
1062  *				  node. Does not wrap around.
1063  *	    "down"		- Previous open node from the currently active
1064  *				  node. Does not wrap around.
1065  *	    "nextsibling"	- Next sibling of the current node.
1066  *	    "prevsibling"	- Previous sibling of the current node.
1067  *	    "parent"		- Parent of the current node.
1068  *	    "view.top"		- Top of viewport.
1069  *	    "view.bottom"	- Bottom of viewport.
1070  *	    @x,y		- Closest node to the specified X-Y position.
1071  *
1072  * Results:
1073  *	If the string is successfully converted, TCL_OK is returned.
1074  *	The pointer to the node is returned via nodePtr.
1075  *	Otherwise, TCL_ERROR is returned and an error message is left
1076  *	in interpreter's result field.
1077  *
1078  *----------------------------------------------------------------------
1079  */
1080 static int
GetEntryFromObj2(tvPtr,objPtr,entryPtrPtr)1081 GetEntryFromObj2(tvPtr, objPtr, entryPtrPtr)
1082     TreeView *tvPtr;
1083     Tcl_Obj *objPtr;
1084     TreeViewEntry **entryPtrPtr;
1085 {
1086     Tcl_Interp *interp;
1087     char *string;
1088     TreeViewTagInfo info = {0};
1089     char *p;
1090     char save;
1091     int result;
1092     Blt_TreeNode node;
1093 
1094     interp = tvPtr->interp;
1095 
1096     string = Tcl_GetString(objPtr);
1097     *entryPtrPtr = NULL;
1098     p = strstr(string, "->");
1099     if (isdigit(UCHAR(string[0]))) {
1100 	int inode;
1101 
1102          if (p != NULL) {
1103 
1104              save = *p;
1105              *p = '\0';
1106              result = Tcl_GetInt(interp, string, &inode);
1107              *p = save;
1108              if (result != TCL_OK) {
1109                  return TCL_ERROR;
1110              }
1111          } else {
1112 
1113              if (Tcl_GetIntFromObj(interp, objPtr, &inode) != TCL_OK) {
1114                  return TCL_ERROR;
1115              }
1116 	}
1117 	node = Blt_TreeGetNode(tvPtr->tree, inode);
1118 	if (node == NULL) {
1119             Tcl_AppendResult(interp, "unknown entry: ", string, 0);
1120             return TCL_ERROR;
1121         }
1122         if (p != NULL) {
1123             node = ParseModifiers(interp, tvPtr->tree, node, p);
1124             if (node == NULL) {
1125                 Tcl_AppendResult(interp, "can't find tag or id:", string, 0);
1126                 return TCL_ERROR;
1127             }
1128         }
1129 
1130         *entryPtrPtr = Blt_NodeToEntry(tvPtr, node);
1131 	return TCL_OK;		/* Node Id. */
1132     }
1133     if (p == NULL) {
1134         if (GetEntryFromSpecialId(tvPtr, string, entryPtrPtr) == TCL_OK) {
1135             return TCL_OK;		/* Special Id. */
1136         }
1137         if (GetTagInfo(tvPtr, string, &info) != TCL_OK) {
1138             return TCL_ERROR;
1139         }
1140     } else {
1141         save = *p;
1142         *p = '\0';
1143         info.tagType = 0;
1144         result = GetEntryFromSpecialId(tvPtr, string, &info.entryPtr);
1145         if (result != TCL_OK) {
1146             result = GetTagInfo(tvPtr, string, &info);
1147         }
1148         *p = save;
1149         if (result != TCL_OK) {
1150             return TCL_ERROR;
1151         }
1152         if (info.tagType & TAG_MULTIPLE) {
1153             Tcl_AppendResult(interp, "more than one entry tagged as \"", string,
1154                 "\"", (char *)NULL);
1155                 return TCL_ERROR;
1156         }
1157         node = ParseModifiers(interp, tvPtr->tree, info.entryPtr->node, p);
1158         if (node == NULL) {
1159             Tcl_AppendResult(interp, "can't find tag or id:", string, 0);
1160             return TCL_ERROR;
1161         }
1162         info.entryPtr = Blt_NodeToEntry(tvPtr, node);
1163     }
1164     if (info.tagType & TAG_MULTIPLE) {
1165 	Tcl_AppendResult(interp, "more than one entry tagged as \"", string,
1166 		"\"", (char *)NULL);
1167 	return TCL_ERROR;
1168     }
1169     *entryPtrPtr = info.entryPtr;
1170     return TCL_OK;		/* Singleton tag. */
1171 }
1172 
1173 static int
GetEntryFromObj(tvPtr,objPtr,entryPtrPtr)1174 GetEntryFromObj(tvPtr, objPtr, entryPtrPtr)
1175     TreeView *tvPtr;
1176     Tcl_Obj *objPtr;
1177     TreeViewEntry **entryPtrPtr;
1178 {
1179     tvPtr->fromPtr = NULL;
1180     return GetEntryFromObj2(tvPtr, objPtr, entryPtrPtr);
1181 }
1182 
1183 /*
1184  *----------------------------------------------------------------------
1185  *
1186  * Blt_TreeViewGetEntry --
1187  *
1188  *	Returns an entry based upon its index.
1189  *
1190  * Results:
1191  *	If the string is successfully converted, TCL_OK is returned.
1192  *	The pointer to the node is returned via nodePtr.
1193  *	Otherwise, TCL_ERROR is returned and an error message is left
1194  *	in interpreter's result field.
1195  *
1196  *----------------------------------------------------------------------
1197  */
1198 int
Blt_TreeViewGetEntry(tvPtr,objPtr,entryPtrPtr)1199 Blt_TreeViewGetEntry(tvPtr, objPtr, entryPtrPtr)
1200     TreeView *tvPtr;
1201     Tcl_Obj *objPtr;
1202     TreeViewEntry **entryPtrPtr;
1203 {
1204     TreeViewEntry *entryPtr;
1205 
1206     if (GetEntryFromObj(tvPtr, objPtr, &entryPtr) != TCL_OK) {
1207 	return TCL_ERROR;
1208     }
1209     if (entryPtr == NULL) {
1210 	Tcl_ResetResult(tvPtr->interp);
1211 	Tcl_AppendResult(tvPtr->interp, "can't find entry \"",
1212 		Tcl_GetString(objPtr), "\" in \"", Tk_PathName(tvPtr->tkwin),
1213 		"\"", (char *)NULL);
1214 	return TCL_ERROR;
1215     }
1216     *entryPtrPtr = entryPtr;
1217     return TCL_OK;
1218 }
1219 
1220 static Blt_TreeNode
GetNthNode(parent,position)1221 GetNthNode(parent, position)
1222     Blt_TreeNode parent;
1223     int position;
1224 {
1225     Blt_TreeNode node;
1226     int count;
1227 
1228     count = 0;
1229     for(node = Blt_TreeFirstChild(parent); node != NULL;
1230 	node = Blt_TreeNextSibling(node)) {
1231 	if (count++ == position) {
1232 	    return node;
1233 	}
1234     }
1235     return Blt_TreeLastChild(parent);
1236 }
1237 
1238 static TreeViewEntry *
GetNthEntry(TreeViewEntry * parentPtr,int position,unsigned int mask)1239 GetNthEntry(
1240     TreeViewEntry *parentPtr,
1241     int position,
1242     unsigned int mask)
1243 {
1244     TreeViewEntry *entryPtr;
1245     int count;
1246 
1247     count = 0;
1248     for(entryPtr = Blt_TreeViewFirstChild(parentPtr, mask); entryPtr != NULL;
1249 	entryPtr = Blt_TreeViewNextSibling(entryPtr, mask)) {
1250 	if (count++ == position) {
1251 	    return entryPtr;
1252 	}
1253     }
1254     return Blt_TreeViewLastChild(parentPtr, mask);
1255 }
1256 
1257 /*
1258  * Preprocess the command string for percent substitution.
1259  */
1260 void
Blt_TreeViewPercentSubst(tvPtr,entryPtr,columnPtr,command,value,resultPtr)1261 Blt_TreeViewPercentSubst(tvPtr, entryPtr, columnPtr, command, value, resultPtr)
1262     TreeView *tvPtr;
1263     TreeViewEntry *entryPtr;
1264     TreeViewColumn *columnPtr;
1265     char *command;
1266     char *value;
1267     Tcl_DString *resultPtr;
1268 {
1269     register char *last, *p;
1270     char *fullName;
1271     Tcl_DString dString;
1272     TreeViewValue *valuePtr;
1273     int one = (command[0] == '%' && strlen(command)==2);
1274 
1275     /*
1276      * Get the full path name of the node, in case we need to
1277      * substitute for it.
1278      */
1279     Tcl_DStringInit(&dString);
1280     fullName = Blt_TreeViewGetFullName(tvPtr, entryPtr, TRUE, &dString);
1281     Tcl_DStringInit(resultPtr);
1282     /* Append the widget name and the node .t 0 */
1283     for (last = p = command; *p != '\0'; p++) {
1284 	if (*p == '%') {
1285 	    char *string;
1286 	    char buf[3];
1287 
1288 	    if (p > last) {
1289 		*p = '\0';
1290 		Tcl_DStringAppend(resultPtr, last, -1);
1291 		*p = '%';
1292 	    }
1293 	    switch (*(p + 1)) {
1294 	    case '%':		/* Percent sign */
1295 		string = "%";
1296 		break;
1297 	    case 'W':		/* Widget name */
1298 		string = Tk_PathName(tvPtr->tkwin);
1299 		if (!one) {
1300                       Tcl_DStringAppendElement(resultPtr, string);
1301                       string = NULL;
1302                 }
1303 		break;
1304 	    case 'F':		/* Formatted value of the data. */
1305 		if (entryPtr && columnPtr &&
1306 		((valuePtr = Blt_TreeViewFindValue(entryPtr, columnPtr))) &&
1307 		valuePtr->textPtr) {
1308                       Tcl_DString dStr;
1309                       Tcl_DStringInit(&dStr);
1310                       Blt_TextLayoutValue( valuePtr->textPtr, &dStr);
1311                       if (!one) {
1312                           Tcl_DStringAppendElement(resultPtr, Tcl_DStringValue(&dStr));
1313                       } else {
1314                           Tcl_DStringAppend(resultPtr, Tcl_DStringValue(&dStr), -1);
1315                       }
1316                       Tcl_DStringFree(&dStr);
1317                       string = NULL;
1318                       break;
1319                   }
1320 	    case 'V':		/* Value */
1321 	        string = value;
1322                 if (!one) {
1323                    Tcl_DStringAppendElement(resultPtr, value);
1324 	           string = NULL;
1325 	        }
1326 		break;
1327 	    case 'P':		/* Full pathname */
1328 	        string = fullName;
1329                 if (!one) {
1330                     Tcl_DStringAppendElement(resultPtr, string);
1331                     string = NULL;
1332                 }
1333 		break;
1334 	    case 'p':		/* Name of the node */
1335 		string = (entryPtr?GETLABEL(entryPtr):"");
1336                 if (!one) {
1337                     Tcl_DStringAppendElement(resultPtr, string);
1338                     string = NULL;
1339                 }
1340 		break;
1341 	    case '#':		/* Node identifier */
1342 		string = (entryPtr?Blt_Itoa(Blt_TreeNodeId(entryPtr->node)):"-1");
1343 		break;
1344 	    case 'C':		/* Column key */
1345 		string = (columnPtr?columnPtr->key:"??");
1346                 if (!one) {
1347                     Tcl_DStringAppendElement(resultPtr, string);
1348                     string = NULL;
1349                 }
1350 		break;
1351 	    default:
1352 		if (*(p + 1) == '\0') {
1353 		    p--;
1354 		}
1355 		buf[0] = *p, buf[1] = *(p + 1), buf[2] = '\0';
1356 		string = buf;
1357 		break;
1358 	    }
1359             if (string) {
1360                 Tcl_DStringAppend(resultPtr, string, -1);
1361             }
1362 	    p++;
1363 	    last = p + 1;
1364 	}
1365     }
1366     if (p > last) {
1367 	*p = '\0';
1368 	Tcl_DStringAppend(resultPtr, last, -1);
1369     }
1370     Tcl_DStringFree(&dString);
1371 }
1372 
1373 /*
1374  *----------------------------------------------------------------------
1375  *
1376  * SelectEntryApplyProc --
1377  *
1378  *	Sets the selection flag for a node.  The selection flag is
1379  *	set/cleared/toggled based upon the flag set in the treeview
1380  *	widget.
1381  *
1382  * Results:
1383  *	Always returns TCL_OK.
1384  *
1385  *----------------------------------------------------------------------
1386  */
1387 static int
SelectEntryApplyProc(tvPtr,entryPtr,columnPtr)1388 SelectEntryApplyProc(tvPtr, entryPtr, columnPtr)
1389     TreeView *tvPtr;
1390     TreeViewEntry *entryPtr;
1391     TreeViewColumn *columnPtr;
1392 {
1393     Blt_HashEntry *hPtr;
1394 
1395     switch (tvPtr->flags & TV_SELECT_MASK) {
1396     case TV_SELECT_CLEAR:
1397 	Blt_TreeViewDeselectEntry(tvPtr, entryPtr, columnPtr);
1398 	break;
1399 
1400     case TV_SELECT_SET:
1401 	Blt_TreeViewSelectEntry(tvPtr, entryPtr, columnPtr);
1402 	break;
1403 
1404     case TV_SELECT_TOGGLE:
1405         if ((tvPtr->selectMode & SELECT_MODE_CELLMASK) && columnPtr != NULL) {
1406             if (Blt_TreeViewEntryIsSelected(tvPtr, entryPtr, columnPtr)) {
1407                 Blt_TreeViewDeselectEntry(tvPtr, entryPtr, columnPtr);
1408             } else {
1409                 Blt_TreeViewSelectEntry(tvPtr, entryPtr, columnPtr);
1410             }
1411         } else {
1412             hPtr = Blt_FindHashEntry(&tvPtr->selectTable, (char *)entryPtr);
1413             if (hPtr != NULL) {
1414                 Blt_TreeViewDeselectEntry(tvPtr, entryPtr, columnPtr);
1415             } else {
1416                 Blt_TreeViewSelectEntry(tvPtr, entryPtr, columnPtr);
1417             }
1418         }
1419 	break;
1420     }
1421     return TCL_OK;
1422 }
1423 
1424 /*
1425  *----------------------------------------------------------------------
1426  *
1427  * EventuallyInvokeSelectCmd --
1428  *
1429  *      Queues a request to execute the -selectcommand code associated
1430  *      with the widget at the next idle point.  Invoked whenever the
1431  *      selection changes.
1432  *
1433  * Results:
1434  *      None.
1435  *
1436  * Side effects:
1437  *      Tcl code gets executed for some application-specific task.
1438  *
1439  *----------------------------------------------------------------------
1440  */
1441 static void
EventuallyInvokeSelectCmd(tvPtr)1442 EventuallyInvokeSelectCmd(tvPtr)
1443     TreeView *tvPtr;
1444 {
1445     if (!(tvPtr->flags & TV_SELECT_PENDING)) {
1446 	tvPtr->flags |= TV_SELECT_PENDING;
1447 	Tcl_DoWhenIdle(Blt_TreeViewSelectCmdProc, tvPtr);
1448     }
1449 }
1450 
1451 /*
1452  *----------------------------------------------------------------------
1453  *
1454  * Blt_TreeViewPruneSelection --
1455  *
1456  *	The root entry being deleted or closed.  Deselect any of its
1457  *	descendants that are currently selected.
1458  *
1459  * Results:
1460  *      None.
1461  *
1462  * Side effects:
1463  *      If any of the entry's descendants are deselected the widget
1464  *	is redrawn and the a selection command callback is invoked
1465  *	(if there's one configured).
1466  *
1467  *----------------------------------------------------------------------
1468  */
1469 void
Blt_TreeViewPruneSelection(tvPtr,rootPtr)1470 Blt_TreeViewPruneSelection(tvPtr, rootPtr)
1471     TreeView *tvPtr;
1472     TreeViewEntry *rootPtr;
1473 {
1474     Blt_ChainLink *linkPtr, *nextPtr;
1475     TreeViewEntry *entryPtr;
1476     int selectionChanged;
1477 
1478     /*
1479      * Check if any of the currently selected entries are a descendant
1480      * of of the current root entry.  Deselect the entry and indicate
1481      * that the treeview widget needs to be redrawn.
1482      */
1483     selectionChanged = FALSE;
1484     for (linkPtr = Blt_ChainFirstLink(tvPtr->selChainPtr); linkPtr != NULL;
1485 	 linkPtr = nextPtr) {
1486 	nextPtr = Blt_ChainNextLink(linkPtr);
1487 	entryPtr = Blt_ChainGetValue(linkPtr);
1488 	if (Blt_TreeIsAncestor(rootPtr->node, entryPtr->node)) {
1489 	    Blt_TreeViewDeselectEntry(tvPtr, entryPtr, NULL);
1490 	    selectionChanged = TRUE;
1491 	}
1492     }
1493     if (selectionChanged) {
1494 	Blt_TreeViewEventuallyRedraw(tvPtr);
1495 	if (tvPtr->selectCmd != NULL) {
1496 	    EventuallyInvokeSelectCmd(tvPtr);
1497 	}
1498     }
1499 }
1500 
1501 
1502 /*
1503  * --------------------------------------------------------------
1504  *
1505  * TreeView operations
1506  *
1507  * --------------------------------------------------------------
1508  */
1509 
1510 /*ARGSUSED*/
1511 static int
FocusOp(tvPtr,interp,objc,objv)1512 FocusOp(tvPtr, interp, objc, objv)
1513     TreeView *tvPtr;
1514     Tcl_Interp *interp;
1515     int objc;
1516     Tcl_Obj *CONST *objv;
1517 {
1518     if (objc == 3) {
1519 	TreeViewEntry *entryPtr;
1520 
1521 	if (GetEntryFromObj(tvPtr, objv[2], &entryPtr) != TCL_OK) {
1522 	    return TCL_ERROR;
1523 	}
1524 	if ((entryPtr != NULL) && (entryPtr != tvPtr->focusPtr)) {
1525 	    if (entryPtr->flags & ENTRY_HIDDEN) {
1526 		/* Doesn't make sense to set focus to a node you can't see. */
1527 		MapAncestors(tvPtr, entryPtr);
1528 	    }
1529 	    /* Changing focus can only affect the visible entries.  The
1530 	     * entry layout stays the same. */
1531 	    if (tvPtr->focusPtr != NULL) {
1532 		tvPtr->focusPtr->flags |= ENTRY_REDRAW;
1533 	    }
1534 	    entryPtr->flags |= ENTRY_REDRAW;
1535 	    tvPtr->flags |= TV_SCROLL;
1536 	    tvPtr->focusPtr = entryPtr;
1537 	}
1538 	Blt_TreeViewEventuallyRedraw(tvPtr);
1539     }
1540     Blt_SetFocusItem(tvPtr->bindTable, tvPtr->focusPtr, ITEM_ENTRY);
1541     if (tvPtr->focusPtr != NULL) {
1542 	Tcl_SetObjResult(interp, NodeToObj(tvPtr->focusPtr->node));
1543     }
1544     return TCL_OK;
1545 }
1546 
1547 /*
1548  * .t entry isset entry col
1549  *
1550  */
1551 /*ARGSUSED*/
1552 static int
EntryIssetOp(tvPtr,interp,objc,objv)1553 EntryIssetOp(tvPtr, interp, objc, objv)
1554     TreeView *tvPtr;
1555     Tcl_Interp *interp;
1556     int objc;
1557     Tcl_Obj *CONST *objv;
1558 {
1559     TreeViewEntry *entryPtr;
1560     TreeViewColumn *columnPtr;
1561     Tcl_Obj *objPtr;
1562     int rc;
1563 
1564     if (Blt_TreeViewGetColumn(interp, tvPtr, objv[4], &columnPtr)
1565         != TCL_OK || columnPtr == NULL) {
1566         return TCL_ERROR;
1567     }
1568     if (columnPtr == &tvPtr->treeColumn) {
1569         Tcl_AppendResult(interp, "can not use tree column", 0);
1570         return TCL_ERROR;
1571     }
1572     if (Blt_TreeViewGetEntry(tvPtr, objv[3], &entryPtr) != TCL_OK ||
1573         entryPtr == NULL) {
1574         return TCL_ERROR;
1575     }
1576 
1577     rc = (Blt_TreeGetValueByKey(tvPtr->interp, tvPtr->tree, entryPtr->node,
1578         columnPtr->key, &objPtr) == TCL_OK);
1579     Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
1580     return TCL_OK;
1581 }
1582 
1583 
1584 /*
1585  * .t entry isvisible entry
1586  *
1587  */
1588 /*ARGSUSED*/
1589 static int
EntryIsvisibleOp(tvPtr,interp,objc,objv)1590 EntryIsvisibleOp(tvPtr, interp, objc, objv)
1591     TreeView *tvPtr;
1592     Tcl_Interp *interp;
1593     int objc;
1594     Tcl_Obj *CONST *objv;
1595 {
1596     TreeViewEntry *entryPtr;
1597     int rc;
1598 
1599     if (Blt_TreeViewGetEntry(tvPtr, objv[3], &entryPtr) != TCL_OK ||
1600         entryPtr == NULL) {
1601         return TCL_ERROR;
1602     }
1603     rc = Blt_TreeViewEntryIsHidden(entryPtr);
1604     Tcl_SetObjResult(interp, Tcl_NewIntObj(!rc));
1605     return TCL_OK;
1606 }
1607 
1608 /*
1609  * .t entry ismapped entry
1610  *
1611  */
1612 /*ARGSUSED*/
1613 static int
EntryIsmappedOp(tvPtr,interp,objc,objv)1614 EntryIsmappedOp(tvPtr, interp, objc, objv)
1615     TreeView *tvPtr;
1616     Tcl_Interp *interp;
1617     int objc;
1618     Tcl_Obj *CONST *objv;
1619 {
1620     TreeViewEntry *entryPtr;
1621     int rc;
1622 
1623     if (Blt_TreeViewGetEntry(tvPtr, objv[3], &entryPtr) != TCL_OK ||
1624         entryPtr == NULL) {
1625         return TCL_ERROR;
1626     }
1627     rc = Blt_TreeViewEntryIsMapped(entryPtr);
1628     Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
1629     return TCL_OK;
1630 }
1631 
1632 /*
1633  * .t entry unset ENTRY KEY
1634  *
1635  */
1636 /*ARGSUSED*/
1637 static int
EntryUnsetOp(tvPtr,interp,objc,objv)1638 EntryUnsetOp(tvPtr, interp, objc, objv)
1639     TreeView *tvPtr;
1640     Tcl_Interp *interp;
1641     int objc;
1642     Tcl_Obj *CONST *objv;
1643 {
1644     TreeViewEntry *entryPtr;
1645     TreeViewColumn *columnPtr;
1646     char *left;
1647 
1648     if (GetEntryFromObj(tvPtr, objv[3], &entryPtr) != TCL_OK) {
1649         return TCL_ERROR;
1650     }
1651     if (Blt_TreeViewGetColumnKey(interp, tvPtr, objv[4], &columnPtr, &left)
1652         != TCL_OK || columnPtr == NULL) {
1653         return TCL_ERROR;
1654     }
1655 
1656     if (left == NULL) {
1657         if (Blt_TreeUnsetValueByKey(tvPtr->interp, tvPtr->tree, entryPtr->node,
1658             columnPtr->key) != TCL_OK) {
1659             Tcl_ResetResult(interp);
1660             return TCL_OK;
1661         }
1662         Blt_TreeViewDeleteValue(entryPtr, columnPtr->key);
1663     } else {
1664         if (Blt_TreeUnsetValue(tvPtr->interp, tvPtr->tree, entryPtr->node,
1665             Tcl_GetString(objv[4])) != TCL_OK) {
1666             Tcl_ResetResult(interp);
1667             return TCL_OK;
1668         }
1669         Blt_TreeViewAddValue(entryPtr, columnPtr);
1670     }
1671     tvPtr->flags |= (TV_LAYOUT | TV_DIRTY | TV_RESORT);
1672     entryPtr->flags |= ENTRY_DIRTY;
1673     Blt_TreeViewEventuallyRedraw(tvPtr);
1674     return TCL_OK;
1675 }
1676 
1677 /*
1678  * .t entry set ENTRY KEY ?VALUE?
1679  *
1680  */
1681 /*ARGSUSED*/
1682 static int
EntrySetOp(tvPtr,interp,objc,objv)1683 EntrySetOp(tvPtr, interp, objc, objv)
1684     TreeView *tvPtr;
1685     Tcl_Interp *interp;
1686     int objc;
1687     Tcl_Obj *CONST *objv;
1688 {
1689     TreeViewEntry *entryPtr;
1690     TreeViewColumn *columnPtr;
1691     int n, result;
1692     Blt_TreeNode node;
1693     char *left, *string;
1694 
1695     if (GetEntryFromObj(tvPtr, objv[3], &entryPtr) != TCL_OK) {
1696         return TCL_ERROR;
1697     }
1698     node = entryPtr->node;
1699     string = Tcl_GetString(objv[4]);
1700     if (Blt_TreeViewGetColumnKey(interp, tvPtr, objv[4], &columnPtr, &left)
1701         != TCL_OK || columnPtr == NULL) {
1702         return TCL_ERROR;
1703     }
1704 
1705     if (objc == 5) {
1706         Tcl_Obj *objPtr;
1707         if (Blt_TreeGetValue(tvPtr->interp, tvPtr->tree, entryPtr->node,
1708             string /*columnPtr->key*/, &objPtr) != TCL_OK) {
1709                 Tcl_ResetResult(interp);
1710                 return TCL_OK;
1711         }
1712         Tcl_SetObjResult(interp, objPtr);
1713         return TCL_OK;
1714     }
1715 
1716     if (objc % 2) {
1717         Tcl_AppendResult( interp, "odd number of arguments", 0);
1718         return TCL_ERROR;
1719     }
1720 
1721     Tcl_Preserve(entryPtr);
1722     result = TCL_OK;
1723     if (objc == 6) {
1724         result = Blt_TreeSetValue(tvPtr->interp, tvPtr->tree, entryPtr->node,
1725             string /*columnPtr->key*/, objv[5]);
1726         if ((entryPtr->flags & ENTRY_DELETED) || (tvPtr->flags & TV_DELETED)) {
1727             Tcl_Release(entryPtr);
1728             return TCL_ERROR;
1729         }
1730         if (result != TCL_OK) {
1731             Tcl_Release(entryPtr);
1732             return TCL_ERROR;
1733         }
1734         Blt_TreeViewAddValue(entryPtr, columnPtr);
1735         Tcl_SetObjResult(interp, objv[5]);
1736         Blt_TreeViewEventuallyRedraw(tvPtr);
1737         return TCL_OK;
1738     }
1739 
1740     n = 4;
1741     while (n<objc) {
1742         string = Tcl_GetString(objv[n]);
1743         result = Blt_TreeSetValue(tvPtr->interp, tvPtr->tree, entryPtr->node,
1744             string /*columnPtr->key*/, objv[n+1]);
1745         if ((entryPtr->flags & ENTRY_DELETED) || (tvPtr->flags & TV_DELETED)) {
1746             Tcl_Release(entryPtr);
1747             return TCL_ERROR;
1748         }
1749         if (result != TCL_OK) {
1750             break;
1751         }
1752         Blt_TreeViewAddValue(entryPtr, columnPtr);
1753         n += 2;
1754         if (n>=objc) break;
1755         if (Blt_TreeViewGetColumnKey(interp, tvPtr, objv[n], &columnPtr, &left)
1756             != TCL_OK || columnPtr == NULL) {
1757             result = TCL_ERROR;
1758             break;
1759         }
1760     }
1761     Tcl_Release(entryPtr);
1762     Blt_TreeViewEventuallyRedraw(tvPtr);
1763     return result;
1764 }
1765 
1766 /*
1767  * .t entry incr ENTRY KEY ?AMOUNT?
1768  *
1769  */
1770 /*ARGSUSED*/
1771 static int
EntryIncrOp(tvPtr,interp,objc,objv)1772 EntryIncrOp(tvPtr, interp, objc, objv)
1773     TreeView *tvPtr;
1774     Tcl_Interp *interp;
1775     int objc;
1776     Tcl_Obj *CONST *objv;
1777 {
1778     TreeViewEntry *entryPtr;
1779     TreeViewColumn *columnPtr;
1780     Blt_TreeNode node;
1781     double dVal, dIncr = 1.0;
1782     int iVal, iIncr = 1, isInt = 0;
1783     Tcl_Obj *objPtr, *valueObjPtr;
1784     char *left, *string;
1785 
1786     if (GetEntryFromObj(tvPtr, objv[3], &entryPtr) != TCL_OK) {
1787         return TCL_ERROR;
1788     }
1789     node = entryPtr->node;
1790     string = Tcl_GetString(objv[4]);
1791     if (Blt_TreeViewGetColumnKey(interp, tvPtr, objv[4], &columnPtr, &left)
1792         != TCL_OK || columnPtr == NULL) {
1793         return TCL_ERROR;
1794     }
1795 
1796     if (Blt_TreeGetValue(tvPtr->interp, tvPtr->tree, entryPtr->node,
1797         string /* columnPtr->key */, &valueObjPtr) != TCL_OK) {
1798             return TCL_ERROR;
1799     }
1800 
1801     if (Tcl_GetIntFromObj(NULL, valueObjPtr, &iVal) == TCL_OK &&
1802          (objc <= 5 || Tcl_GetIntFromObj(NULL, objv[5], &iIncr) == TCL_OK)) {
1803         isInt = 1;
1804     } else {
1805         if (objc > 5 && Tcl_GetDoubleFromObj(interp, objv[5], &dIncr) != TCL_OK) {
1806             return TCL_ERROR;
1807         }
1808         if (Tcl_GetDoubleFromObj(interp, valueObjPtr, &dVal) != TCL_OK) {
1809             return TCL_ERROR;
1810         }
1811     }
1812     if (isInt) {
1813         iVal += iIncr;
1814         objPtr = Tcl_NewIntObj(iVal);
1815     } else {
1816         dVal += dIncr;
1817         objPtr = Tcl_NewDoubleObj(dVal);
1818     }
1819 
1820     if (Blt_TreeSetValue(tvPtr->interp, tvPtr->tree, entryPtr->node,
1821         string /*columnPtr->key*/, objPtr) != TCL_OK) {
1822             return TCL_ERROR;
1823     }
1824     Blt_TreeViewAddValue(entryPtr, columnPtr);
1825     Tcl_SetObjResult(interp, objPtr);
1826     Blt_TreeViewEventuallyRedraw(tvPtr);
1827     return TCL_OK;
1828 }
1829 
1830 
1831 /*
1832  * .t entry get ENTRY ?KEY? ?DEFAULT?
1833  *
1834  */
1835 /*ARGSUSED*/
1836 static int
EntryGetOp(tvPtr,interp,objc,objv)1837 EntryGetOp(tvPtr, interp, objc, objv)
1838     TreeView *tvPtr;
1839     Tcl_Interp *interp;
1840     int objc;
1841     Tcl_Obj *CONST *objv;
1842 {
1843     TreeViewEntry *entryPtr;
1844     Blt_TreeNode node;
1845     char *key;
1846     Tcl_Obj *objPtr;
1847 
1848     if (GetEntryFromObj(tvPtr, objv[3], &entryPtr) != TCL_OK) {
1849         return TCL_ERROR;
1850     }
1851     Tcl_Preserve(entryPtr);
1852     if (objc<=4) {
1853         Blt_ChainLink *linkPtr;
1854         TreeViewColumn *columnPtr;
1855         Tcl_Obj *listObjPtr;
1856 
1857         listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
1858         for (linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr); linkPtr != NULL;
1859             linkPtr = Blt_ChainNextLink(linkPtr)) {
1860             columnPtr = Blt_ChainGetValue(linkPtr);
1861             if (columnPtr->hidden) {
1862                 continue;
1863             }
1864             if (Blt_TreeGetValue(tvPtr->interp, tvPtr->tree, entryPtr->node,
1865                 columnPtr->key, &objPtr) != TCL_OK) {
1866                 objPtr = Tcl_NewStringObj("", -1);
1867             }
1868             if ((entryPtr->flags & ENTRY_DELETED) || (tvPtr->flags & TV_DELETED)) {
1869                 Tcl_Release(entryPtr);
1870                 Tcl_DecrRefCount(listObjPtr);
1871                 return TCL_ERROR;
1872             }
1873             Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
1874         }
1875         Tcl_SetObjResult(interp, listObjPtr);
1876         Tcl_Release(entryPtr);
1877         return TCL_OK;
1878     }
1879     key = Tcl_GetString(objv[4]);
1880     node = entryPtr->node;
1881     Tcl_Release(entryPtr);
1882     if (Blt_TreeGetValue(tvPtr->interp, tvPtr->tree, entryPtr->node,
1883         key, &objPtr) != TCL_OK) {
1884         if (objc != 6) {
1885             return TCL_ERROR;
1886         }
1887         Tcl_SetObjResult(interp, objv[5]);
1888         return TCL_OK;
1889     }
1890     Tcl_SetObjResult(interp, objPtr);
1891     return TCL_OK;
1892 }
1893 
1894 /*
1895  * .t entry value ENTRY ?KEY?
1896  *
1897  */
1898 /*ARGSUSED*/
1899 static int
EntryValueOp(tvPtr,interp,objc,objv)1900 EntryValueOp(tvPtr, interp, objc, objv)
1901     TreeView *tvPtr;
1902     Tcl_Interp *interp;
1903     int objc;
1904     Tcl_Obj *CONST *objv;
1905 {
1906     TreeViewEntry *entryPtr;
1907     TreeViewColumn *columnPtr = NULL;
1908     TreeViewValue *valuePtr;
1909     Blt_ChainLink *linkPtr;
1910     Tcl_Obj *objPtr, *listObjPtr;
1911 
1912     if (GetEntryFromObj(tvPtr, objv[3], &entryPtr) != TCL_OK) {
1913         return TCL_ERROR;
1914     }
1915     if (objc>4 && Blt_TreeViewGetColumn(interp, tvPtr, objv[4], &columnPtr)
1916         != TCL_OK) {
1917         return TCL_ERROR;
1918     }
1919 
1920     Tcl_Preserve(entryPtr);
1921     if (columnPtr != NULL) {
1922         valuePtr = Blt_TreeViewFindValue(entryPtr, columnPtr);
1923         if ((entryPtr->flags & ENTRY_DELETED) || (tvPtr->flags & TV_DELETED)) {
1924             Tcl_Release(entryPtr);
1925             return TCL_ERROR;
1926         }
1927         Tcl_Release(entryPtr);
1928         if (valuePtr && valuePtr->textPtr) {
1929             Tcl_DString dStr;
1930             Tcl_DStringInit(&dStr);
1931             Blt_TextLayoutValue( valuePtr->textPtr, &dStr);
1932             objPtr = Tcl_NewStringObj( Tcl_DStringValue(&dStr), -1);
1933             Tcl_DStringFree(&dStr);
1934             Tcl_SetObjResult(tvPtr->interp, objPtr);
1935             return TCL_OK;
1936         }
1937 
1938         if (Blt_TreeGetValueByKey(tvPtr->interp, tvPtr->tree, entryPtr->node,
1939             columnPtr->key, &objPtr) != TCL_OK) {
1940             Tcl_ResetResult(interp);
1941         } else {
1942             Tcl_SetObjResult(interp, objPtr);
1943         }
1944         return TCL_OK;
1945     }
1946 
1947     listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
1948     for(linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr); linkPtr != NULL;
1949 	linkPtr = Blt_ChainNextLink(linkPtr)) {
1950 	columnPtr = Blt_ChainGetValue(linkPtr);
1951 	if (columnPtr->hidden) {
1952 	    continue;
1953 	}
1954         valuePtr = Blt_TreeViewFindValue(entryPtr, columnPtr);
1955         if ((entryPtr->flags & ENTRY_DELETED) || (tvPtr->flags & TV_DELETED)) {
1956              Tcl_Release(entryPtr);
1957              Tcl_DecrRefCount(listObjPtr);
1958              return TCL_ERROR;
1959          }
1960          if (valuePtr && valuePtr->textPtr) {
1961              Tcl_DString dStr;
1962              Tcl_DStringInit(&dStr);
1963              Blt_TextLayoutValue( valuePtr->textPtr, &dStr);
1964              objPtr = Tcl_NewStringObj( Tcl_DStringValue(&dStr), -1);
1965              Tcl_DStringFree(&dStr);
1966              Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
1967              continue;
1968          }
1969          if ((entryPtr->flags & ENTRY_DELETED) || (tvPtr->flags & TV_DELETED)) {
1970              Tcl_Release(entryPtr);
1971              Tcl_DecrRefCount(listObjPtr);
1972              return TCL_ERROR;
1973          }
1974          if (Blt_TreeGetValueByKey(tvPtr->interp, tvPtr->tree, entryPtr->node,
1975              columnPtr->key, &objPtr) != TCL_OK) {
1976              objPtr = Tcl_NewStringObj("",0);
1977          }
1978          Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
1979      }
1980      Tcl_Release(entryPtr);
1981      Tcl_SetObjResult(interp, listObjPtr);
1982 
1983     return TCL_OK;
1984 }
1985 
1986 /*
1987  *----------------------------------------------------------------------
1988  *
1989  * BboxOp --
1990  *
1991  *----------------------------------------------------------------------
1992  */
1993 /*ARGSUSED*/
1994 static int
BboxOp(tvPtr,interp,objc,objv)1995 BboxOp(tvPtr, interp, objc, objv)
1996     TreeView *tvPtr;
1997     Tcl_Interp *interp;
1998     int objc;			/* Not used. */
1999     Tcl_Obj *CONST *objv;
2000 {
2001     register int i;
2002     TreeViewEntry *entryPtr;
2003     int height, yBot;
2004     int left, top, right, bottom;
2005     int screen;
2006     int lWidth;
2007     char *string;
2008 
2009     if (tvPtr->flags & TV_LAYOUT) {
2010 	/*
2011 	 * The layout is dirty.  Recompute it now, before we use the
2012 	 * world dimensions.  But remember, the "bbox" operation isn't
2013 	 * valid for hidden entries (since they're not visible, they
2014 	 * don't have world coordinates).
2015 	 */
2016 	Blt_TreeViewComputeLayout(tvPtr);
2017     }
2018     left = tvPtr->worldWidth;
2019     top = tvPtr->worldHeight;
2020     right = bottom = 0;
2021 
2022     screen = FALSE;
2023     string = Tcl_GetString(objv[2]);
2024     if ((string[0] == '-') && (strcmp(string, "-screen") == 0)) {
2025         screen = TRUE;
2026         objc--, objv++;
2027     }
2028     for (i = 2; i < objc; i++) {
2029 	string = Tcl_GetString(objv[i]);
2030 	if ((string[0] == 'a') && (strcmp(string, "all") == 0)) {
2031 	    left = top = 0;
2032 	    right = tvPtr->worldWidth;
2033 	    bottom = tvPtr->worldHeight;
2034 	    break;
2035 	}
2036 	if (GetEntryFromObj(tvPtr, objv[i], &entryPtr) != TCL_OK) {
2037 	    return TCL_ERROR;
2038 	}
2039 	if (entryPtr == NULL) {
2040 	    continue;
2041 	}
2042 	if (entryPtr->flags & ENTRY_HIDDEN) {
2043 	    continue;
2044 	}
2045 	yBot = entryPtr->worldY + entryPtr->height;
2046 	height = VPORTHEIGHT(tvPtr);
2047 	if ((yBot <= tvPtr->yOffset) &&
2048 	    (entryPtr->worldY >= (tvPtr->yOffset + height))) {
2049 	    continue;
2050 	}
2051 	if (bottom < yBot) {
2052 	    bottom = yBot;
2053 	}
2054 	if (top > entryPtr->worldY) {
2055 	    top = entryPtr->worldY;
2056 	}
2057 	lWidth = ICONWIDTH(DEPTH(tvPtr, entryPtr->node));
2058 	if (right < (entryPtr->worldX + entryPtr->width + lWidth)) {
2059 	    right = (entryPtr->worldX + entryPtr->width + lWidth);
2060 	}
2061 	if (left > entryPtr->worldX) {
2062 	    left = entryPtr->worldX;
2063 	}
2064     }
2065 
2066     if (screen) {
2067 #if 0
2068         width = VPORTWIDTH(tvPtr);
2069 	height = VPORTHEIGHT(tvPtr);
2070 	/*
2071 	 * Do a min-max text for the intersection of the viewport and
2072 	 * the computed bounding box.  If there is no intersection, return
2073 	 * the empty string.
2074 	 */
2075           if (((right < tvPtr->xOffset) &&
2076 	    (left >= (tvPtr->xOffset + width))) ||
2077 	    ((bottom < tvPtr->yOffset) &&
2078 	    (top >= (tvPtr->yOffset + height)))) {
2079 	    return TCL_OK;
2080 	}
2081 	/* Otherwise clip the coordinates at the view port boundaries. */
2082 	if (left < tvPtr->xOffset) {
2083 	    left = tvPtr->xOffset;
2084 	} else if (right > (tvPtr->xOffset + width)) {
2085 	    right = tvPtr->xOffset + width;
2086 	}
2087 	if (top < tvPtr->yOffset) {
2088 	    top = tvPtr->yOffset;
2089 	} else if (bottom > (tvPtr->yOffset + height)) {
2090 	    bottom = tvPtr->yOffset + height;
2091 	}
2092 #endif
2093 	left = SCREENX(tvPtr, left), top = SCREENY(tvPtr, top);
2094 	right = SCREENX(tvPtr, right), bottom = SCREENY(tvPtr, bottom);
2095     }
2096     if ((left <= right) && (top <= bottom)) {
2097 	Tcl_Obj *listObjPtr;
2098 
2099 	listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
2100 	Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewIntObj(left));
2101 	Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewIntObj(top));
2102 	Tcl_ListObjAppendElement(interp, listObjPtr,
2103 				 Tcl_NewIntObj(right - left));
2104 	Tcl_ListObjAppendElement(interp, listObjPtr,
2105 				 Tcl_NewIntObj(bottom - top));
2106 	Tcl_SetObjResult(interp, listObjPtr);
2107     }
2108     return TCL_OK;
2109 }
2110 
2111 static void
DrawButton(tvPtr,entryPtr)2112 DrawButton(tvPtr, entryPtr)
2113     TreeView *tvPtr;
2114     TreeViewEntry *entryPtr;
2115 {
2116     Drawable drawable;
2117     int sx, sy, dx, dy;
2118     int width, height;
2119     int left, right, top, bottom;
2120 
2121     dx = SCREENX(tvPtr, entryPtr->worldX) + entryPtr->buttonX;
2122     dy = SCREENY(tvPtr, entryPtr->worldY) + entryPtr->buttonY;
2123     width = tvPtr->button.width;
2124     height = tvPtr->button.height;
2125 
2126     top = tvPtr->titleHeight + tvPtr->insetY;
2127     bottom = Tk_Height(tvPtr->tkwin) - tvPtr->insetY;
2128     left = tvPtr->insetX;
2129     right = Tk_Width(tvPtr->tkwin) - tvPtr->insetX;
2130 
2131     if (((dx + width) < left) || (dx > right) ||
2132 	((dy + height) < top) || (dy > bottom)) {
2133 	return;			/* Value is clipped. */
2134     }
2135     drawable = Tk_GetPixmap(tvPtr->display, Tk_WindowId(tvPtr->tkwin),
2136 	width, height, Tk_Depth(tvPtr->tkwin));
2137     /* Draw the background of the value. */
2138     Blt_TreeViewDrawButton(tvPtr, entryPtr, drawable, 0, 0);
2139 
2140     /* Clip the drawable if necessary */
2141     sx = sy = 0;
2142     if (dx < left) {
2143 	width -= left - dx;
2144 	sx += left - dx;
2145 	dx = left;
2146     }
2147     if ((dx + width) >= right) {
2148 	width -= (dx + width) - right;
2149     }
2150     if (dy < top) {
2151 	height -= top - dy;
2152 	sy += top - dy;
2153 	dy = top;
2154     }
2155     if ((dy + height) >= bottom) {
2156 	height -= (dy + height) - bottom;
2157     }
2158     XCopyArea(tvPtr->display, drawable, Tk_WindowId(tvPtr->tkwin),
2159       tvPtr->lineGC, sx, sy, width,  height, dx, dy);
2160     Tk_FreePixmap(tvPtr->display, drawable);
2161 }
2162 
2163 /*
2164  *----------------------------------------------------------------------
2165  *
2166  * ButtonActivateOp --
2167  *
2168  *	Selects the button to appear active.
2169  *
2170  *----------------------------------------------------------------------
2171  */
2172 /*ARGSUSED*/
2173 static int
ButtonActivateOp(tvPtr,interp,objc,objv)2174 ButtonActivateOp(tvPtr, interp, objc, objv)
2175     TreeView *tvPtr;
2176     Tcl_Interp *interp;
2177     int objc;			/* Not used. */
2178     Tcl_Obj *CONST *objv;
2179 {
2180     TreeViewEntry *oldPtr, *newPtr;
2181     char *string;
2182 
2183     string = Tcl_GetString(objv[3]);
2184     if (string[0] == '\0') {
2185 	newPtr = NULL;
2186     } else if (GetEntryFromObj(tvPtr, objv[3], &newPtr) != TCL_OK) {
2187 	return TCL_ERROR;
2188     }
2189     if (tvPtr->treeColumn.hidden) {
2190 	return TCL_OK;
2191     }
2192     if (tvPtr->button.reqSize==0) {
2193         return TCL_OK;
2194     }
2195     if ((newPtr != NULL) && !(newPtr->flags & ENTRY_HAS_BUTTON)) {
2196 	newPtr = NULL;
2197     }
2198     oldPtr = tvPtr->activeButtonPtr;
2199     tvPtr->activeButtonPtr = newPtr;
2200     if (!(tvPtr->flags & TV_REDRAW) && (newPtr != oldPtr)) {
2201 	if ((oldPtr != NULL) && (oldPtr != tvPtr->rootPtr)) {
2202 	    DrawButton(tvPtr, oldPtr);
2203 	}
2204 	if ((newPtr != NULL) && (newPtr != tvPtr->rootPtr)) {
2205 	    DrawButton(tvPtr, newPtr);
2206 	}
2207     }
2208     return TCL_OK;
2209 }
2210 
2211 /*
2212  *----------------------------------------------------------------------
2213  *
2214  * ButtonBindOp --
2215  *
2216  *	  .t bind tag sequence command
2217  *
2218  *----------------------------------------------------------------------
2219  */
2220 /*ARGSUSED*/
2221 static int
ButtonBindOp(tvPtr,interp,objc,objv)2222 ButtonBindOp(tvPtr, interp, objc, objv)
2223     TreeView *tvPtr;
2224     Tcl_Interp *interp;
2225     int objc;			/* Not used. */
2226     Tcl_Obj *CONST *objv;
2227 {
2228     ClientData object;
2229     char *string;
2230 
2231     string = Tcl_GetString(objv[3]);
2232     /* Assume that this is a binding tag. */
2233     object = Blt_TreeViewButtonTag(tvPtr, string);
2234     if (object == NULL) {
2235         return TCL_OK;
2236     }
2237     return Blt_ConfigureBindingsFromObj(interp, tvPtr->bindTable, object,
2238 	objc - 4, objv + 4);
2239 }
2240 
2241 /*
2242  *----------------------------------------------------------------------
2243  *
2244  * ButtonCgetOp --
2245  *
2246  *----------------------------------------------------------------------
2247  */
2248 /*ARGSUSED*/
2249 static int
ButtonCgetOp(tvPtr,interp,objc,objv)2250 ButtonCgetOp(tvPtr, interp, objc, objv)
2251     TreeView *tvPtr;
2252     Tcl_Interp *interp;
2253     int objc;			/* Not used. */
2254     Tcl_Obj *CONST *objv;
2255 {
2256     Blt_TreeViewOptsInit(tvPtr);
2257     return Blt_ConfigureValueFromObj(interp, tvPtr->tkwin,
2258 	bltTreeViewButtonSpecs, (char *)tvPtr, objv[3], 0);
2259 }
2260 
2261 /*
2262  *----------------------------------------------------------------------
2263  *
2264  * ButtonConfigureOp --
2265  *
2266  * 	This procedure is called to process a list of configuration
2267  *	options database, in order to reconfigure the one of more
2268  *	entries in the widget.
2269  *
2270  *	  .h button configure option value
2271  *
2272  * Results:
2273  *	A standard Tcl result.  If TCL_ERROR is returned, then
2274  *	interp->result contains an error message.
2275  *
2276  * Side effects:
2277  *	Configuration information, such as text string, colors, font,
2278  *	etc. get set for tvPtr; old resources get freed, if there
2279  *	were any.  The hypertext is redisplayed.
2280  *
2281  *----------------------------------------------------------------------
2282  */
2283 static int
ButtonConfigureOp(tvPtr,interp,objc,objv)2284 ButtonConfigureOp(tvPtr, interp, objc, objv)
2285     TreeView *tvPtr;
2286     Tcl_Interp *interp;
2287     int objc;
2288     Tcl_Obj *CONST *objv;
2289 {
2290     Blt_TreeViewOptsInit(tvPtr);
2291     if (objc == 3) {
2292 	return Blt_ConfigureInfoFromObj(interp, tvPtr->tkwin,
2293 	    bltTreeViewButtonSpecs, (char *)tvPtr, (Tcl_Obj *)NULL, 0);
2294     } else if (objc == 4) {
2295 	return Blt_ConfigureInfoFromObj(interp, tvPtr->tkwin,
2296 		bltTreeViewButtonSpecs, (char *)tvPtr, objv[3], 0);
2297     }
2298     if (Blt_ConfigureWidgetFromObj(tvPtr->interp, tvPtr->tkwin,
2299 		bltTreeViewButtonSpecs, objc - 3, objv + 3, (char *)tvPtr,
2300 		BLT_CONFIG_OBJV_ONLY, NULL) != TCL_OK) {
2301 	return TCL_ERROR;
2302     }
2303     if (tvPtr->tile != NULL) {
2304         Blt_SetTileChangedProc(tvPtr->tile, Blt_TreeViewTileChangedProc, tvPtr);
2305     }
2306     if (tvPtr->selectTile != NULL) {
2307         Blt_SetTileChangedProc(tvPtr->selectTile, Blt_TreeViewTileChangedProc, tvPtr);
2308     }
2309 
2310     Blt_TreeViewConfigureButtons(tvPtr);
2311     Blt_TreeViewEventuallyRedraw(tvPtr);
2312     return TCL_OK;
2313 }
2314 
2315 /*
2316  *----------------------------------------------------------------------
2317  *
2318  * ButtonOp --
2319  *
2320  *	This procedure handles button operations.
2321  *
2322  * Results:
2323  *	A standard Tcl result.
2324  *
2325  *----------------------------------------------------------------------
2326  */
2327 static Blt_OpSpec buttonOps[] =
2328 {
2329     {"activate", 1, (Blt_Op)ButtonActivateOp, 4, 4, "tagOrId",},
2330     {"bind", 1, (Blt_Op)ButtonBindOp, 4, 6, "tagName ?sequence command?",},
2331     {"cget", 2, (Blt_Op)ButtonCgetOp, 4, 4, "option",},
2332     {"configure", 2, (Blt_Op)ButtonConfigureOp, 3, 0, "?option value?...",},
2333 };
2334 
2335 static int nButtonOps = sizeof(buttonOps) / sizeof(Blt_OpSpec);
2336 
2337 static int
ButtonOp(tvPtr,interp,objc,objv)2338 ButtonOp(tvPtr, interp, objc, objv)
2339     TreeView *tvPtr;
2340     Tcl_Interp *interp;
2341     int objc;
2342     Tcl_Obj *CONST *objv;
2343 {
2344     Blt_Op proc;
2345     int result;
2346 
2347     proc = Blt_GetOpFromObj(interp, nButtonOps, buttonOps, BLT_OP_ARG2, objc,
2348 	objv, 0);
2349     if (proc == NULL) {
2350 	return TCL_ERROR;
2351     }
2352     result = (*proc) (tvPtr, interp, objc, objv);
2353     return result;
2354 }
2355 
2356 /*
2357  *----------------------------------------------------------------------
2358  *
2359  * CgetOp --
2360  *
2361  *----------------------------------------------------------------------
2362  */
2363 /*ARGSUSED*/
2364 static int
CgetOp(tvPtr,interp,objc,objv)2365 CgetOp(tvPtr, interp, objc, objv)
2366     TreeView *tvPtr;
2367     Tcl_Interp *interp;
2368     int objc;			/* Not used. */
2369     Tcl_Obj *CONST *objv;
2370 {
2371     Blt_TreeViewOptsInit(tvPtr);
2372     return Blt_ConfigureValueFromObj(interp, tvPtr->tkwin, bltTreeViewSpecs,
2373 	(char *)tvPtr, objv[2], 0);
2374 }
2375 
2376 static int
CloseTreeEntry(TreeView * tvPtr,TreeViewEntry * entryPtr)2377 CloseTreeEntry(TreeView *tvPtr, TreeViewEntry *entryPtr) {
2378     if (Blt_TreeViewFirstChild(entryPtr, 0) != NULL &&
2379         entryPtr != tvPtr->rootPtr) {
2380         return Blt_TreeViewCloseEntry(tvPtr, entryPtr);
2381     }
2382     return TCL_OK;
2383 }
2384 
2385 
2386 /*ARGSUSED*/
2387 static int
CloseOp(tvPtr,interp,objc,objv)2388 CloseOp(tvPtr, interp, objc, objv)
2389     TreeView *tvPtr;
2390     Tcl_Interp *interp;		/* Not used. */
2391     int objc;
2392     Tcl_Obj *CONST *objv;
2393 {
2394     TreeViewEntry *entryPtr;
2395     TreeViewTagInfo info = {0};
2396     int recurse, trees, result;
2397     register int i;
2398 
2399     recurse = FALSE;
2400     trees = FALSE;
2401 
2402     while (objc > 2) {
2403 	char *string;
2404 	int length;
2405 
2406 	string = Tcl_GetStringFromObj(objv[2], &length);
2407 	if ((string[0] == '-') && (length > 1) &&
2408 	    (strncmp(string, "-recurse", length) == 0)) {
2409 	    objv++, objc--;
2410 	    recurse = TRUE;
2411 	} else if ((string[0] == '-') && (length > 1) &&
2412 	    (strncmp(string, "-trees", length) == 0)) {
2413 	    objv++, objc--;
2414 	    trees = TRUE;
2415 	} else break;
2416     }
2417     for (i = 2; i < objc; i++) {
2418 	if (Blt_TreeViewFindTaggedEntries(tvPtr, objv[i], &info) != TCL_OK) {
2419 	    return TCL_ERROR;
2420 	}
2421 	for (entryPtr = Blt_TreeViewFirstTaggedEntry(&info); entryPtr != NULL;
2422 	     entryPtr = Blt_TreeViewNextTaggedEntry(&info)) {
2423 	    /*
2424 	     * Clear the selections for any entries that may have become
2425 	     * hidden by closing the node.
2426 	     */
2427 	    Blt_TreeViewPruneSelection(tvPtr, entryPtr);
2428 
2429 	    /*
2430 	     * -----------------------------------------------------------
2431 	     *
2432 	     *  Check if either the "focus" entry or selection anchor
2433 	     *  is in this hierarchy.  Must move it or disable it before
2434 	     *  we close the node.  Otherwise it may be deleted by a Tcl
2435 	     *  "close" script, and we'll be left pointing to a bogus
2436 	     *  memory location.
2437 	     *
2438 	     * -----------------------------------------------------------
2439 	     */
2440 	    if ((tvPtr->focusPtr != NULL) &&
2441 		(Blt_TreeIsAncestor(entryPtr->node, tvPtr->focusPtr->node))) {
2442 		tvPtr->focusPtr = entryPtr;
2443 		Blt_SetFocusItem(tvPtr->bindTable, tvPtr->focusPtr, ITEM_ENTRY);
2444 	    }
2445 	    if ((tvPtr->selAnchorPtr != NULL) &&
2446 		(Blt_TreeIsAncestor(entryPtr->node,
2447 				    tvPtr->selAnchorPtr->node))) {
2448 		tvPtr->selMarkPtr = tvPtr->selAnchorPtr = NULL;
2449 	    }
2450 	    if ((tvPtr->activePtr != NULL) &&
2451 		(Blt_TreeIsAncestor(entryPtr->node, tvPtr->activePtr->node))) {
2452 		tvPtr->activePtr = entryPtr;
2453 	    }
2454 	    if (trees) {
2455 		result = Blt_TreeViewApply(tvPtr, entryPtr,
2456 					   CloseTreeEntry, 0);
2457 	    } else if (recurse) {
2458 		result = Blt_TreeViewApply(tvPtr, entryPtr,
2459 					   Blt_TreeViewCloseEntry, 0);
2460 	    } else {
2461 		result = Blt_TreeViewCloseEntry(tvPtr, entryPtr);
2462 	    }
2463 	    if (result != TCL_OK) {
2464                  tvPtr->flags |= (TV_LAYOUT | TV_DIRTY | TV_RESORT);
2465                  Blt_TreeViewDoneTaggedEntries(&info);
2466                  return TCL_ERROR;
2467 	    }
2468 	}
2469         Blt_TreeViewDoneTaggedEntries(&info);
2470      }
2471     /* Closing a node may affect the visible entries and the
2472      * the world layout of the entries. */
2473     tvPtr->flags |= (TV_LAYOUT | TV_DIRTY | TV_RESORT);
2474     Blt_TreeViewEventuallyRedraw(tvPtr);
2475     return TCL_OK;
2476 }
2477 
2478 /*
2479  *----------------------------------------------------------------------
2480  *
2481  * ConfigureOp --
2482  *
2483  * 	This procedure is called to process an objv/objc list, plus
2484  *	the Tk option database, in order to configure (or reconfigure)
2485  *	the widget.
2486  *
2487  * Results:
2488  *	A standard Tcl result.  If TCL_ERROR is returned, then
2489  *	interp->result contains an error message.
2490  *
2491  * Side effects:
2492  *	Configuration information, such as text string, colors, font,
2493  *	etc. get set for tvPtr; old resources get freed, if there
2494  *	were any.  The widget is redisplayed.
2495  *
2496  *----------------------------------------------------------------------
2497  */
2498 static int
ConfigureOp(tvPtr,interp,objc,objv)2499 ConfigureOp(tvPtr, interp, objc, objv)
2500     TreeView *tvPtr;
2501     Tcl_Interp *interp;
2502     int objc;
2503     Tcl_Obj *CONST *objv;
2504 {
2505     Blt_TreeViewOptsInit(tvPtr);
2506     if (objc == 2) {
2507 	return Blt_ConfigureInfoFromObj(interp, tvPtr->tkwin, bltTreeViewSpecs,
2508 		(char *)tvPtr, (Tcl_Obj *)NULL, 0);
2509     } else if (objc == 3) {
2510 	return Blt_ConfigureInfoFromObj(interp, tvPtr->tkwin,
2511 		bltTreeViewSpecs, (char *)tvPtr, objv[2], 0);
2512     }
2513     if (Blt_ConfigureWidgetFromObj(interp, tvPtr->tkwin, bltTreeViewSpecs,
2514 	objc - 2, objv + 2, (char *)tvPtr, BLT_CONFIG_OBJV_ONLY, NULL) != TCL_OK) {
2515 	return TCL_ERROR;
2516     }
2517     if (Blt_TreeViewUpdateWidget(interp, tvPtr) != TCL_OK) {
2518 	return TCL_ERROR;
2519     }
2520     if (tvPtr->tile != NULL) {
2521         Blt_SetTileChangedProc(tvPtr->tile, Blt_TreeViewTileChangedProc, tvPtr);
2522     }
2523     if (tvPtr->selectTile != NULL) {
2524         Blt_SetTileChangedProc(tvPtr->selectTile, Blt_TreeViewTileChangedProc, tvPtr);
2525     }
2526     Blt_TreeViewEventuallyRedraw(tvPtr);
2527     return TCL_OK;
2528 }
2529 
2530 /*ARGSUSED*/
2531 static int
CurselectionOp(tvPtr,interp,objc,objv)2532 CurselectionOp(tvPtr, interp, objc, objv)
2533     TreeView *tvPtr;
2534     Tcl_Interp *interp;		/* Not used. */
2535     int objc;			/* Not used. */
2536     Tcl_Obj *CONST *objv;	/* Not used. */
2537 {
2538 #if 0
2539     TreeViewEntry *entryPtr;
2540     TreeViewColumn *columnPtr;
2541     Tcl_Obj *listObjPtr, *objPtr;
2542 
2543     Blt_HashEntry *hPtr;
2544     Blt_HashSearch cursor;
2545     TreeViewValue *valuePtr;
2546     Blt_ChainLink *linkPtr;
2547 
2548     if (tvPtr->selectMode & SELECT_MODE_CELLMASK) {
2549         Tcl_AppendResult(interp, "-selectmode must not be 'cell' or 'multicell'", 0);
2550         return TCL_ERROR;
2551     }
2552     listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
2553 
2554     for (hPtr = Blt_FirstHashEntry(&tvPtr->selectTable, &cursor);
2555         hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
2556         entryPtr = (TreeViewEntry *)Blt_GetHashKey(&tvPtr->selectTable, hPtr);
2557         objPtr = NodeToObj(entryPtr->node);
2558         Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
2559     }
2560 
2561 
2562     Tcl_SetObjResult(interp, listObjPtr);
2563 
2564     return TCL_OK;
2565 #else
2566     TreeViewEntry *entryPtr;
2567     Tcl_Obj *listObjPtr, *objPtr;
2568 
2569     listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
2570     if (tvPtr->flags & TV_SELECT_SORTED) {
2571 	Blt_ChainLink *linkPtr;
2572 
2573 	for (linkPtr = Blt_ChainFirstLink(tvPtr->selChainPtr); linkPtr != NULL;
2574 	     linkPtr = Blt_ChainNextLink(linkPtr)) {
2575 	    entryPtr = Blt_ChainGetValue(linkPtr);
2576 	    objPtr = NodeToObj(entryPtr->node);
2577 	    Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
2578 
2579 	}
2580     } else {
2581 	for (entryPtr = tvPtr->rootPtr; entryPtr != NULL;
2582 	     entryPtr = Blt_TreeViewNextEntry(entryPtr, ENTRY_MASK)) {
2583 	    if (Blt_TreeViewEntryIsSelected(tvPtr, entryPtr, NULL)) {
2584 		objPtr = NodeToObj(entryPtr->node);
2585 		Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
2586 	    }
2587 	}
2588     }
2589     Tcl_SetObjResult(interp, listObjPtr);
2590     return TCL_OK;
2591 #endif
2592 }
2593 
2594 /*
2595  *----------------------------------------------------------------------
2596  *
2597  * BindOp --
2598  *
2599  *	  .t bind tagOrId sequence command
2600  *
2601  *----------------------------------------------------------------------
2602  */
2603 /*ARGSUSED*/
2604 static int
BindOp(tvPtr,interp,objc,objv)2605 BindOp(tvPtr, interp, objc, objv)
2606     TreeView *tvPtr;
2607     Tcl_Interp *interp;
2608     int objc;
2609     Tcl_Obj *CONST *objv;
2610 {
2611     ClientData object;
2612     TreeViewEntry *entryPtr;
2613     char *string;
2614 
2615     /*
2616      * Entries are selected by id only.  All other strings are
2617      * interpreted as a binding tag.
2618      */
2619     object = NULL;
2620     string = Tcl_GetString(objv[2]);
2621     if (isdigit(UCHAR(string[0]))) {
2622 	Blt_TreeNode node;
2623 	int inode;
2624 
2625 	if (Tcl_GetIntFromObj(tvPtr->interp, objv[2], &inode) != TCL_OK) {
2626 	    return TCL_ERROR;
2627 	}
2628 	node = Blt_TreeGetNode(tvPtr->tree, inode);
2629 	object = Blt_NodeToEntry(tvPtr, node);
2630     } else if (GetEntryFromSpecialId(tvPtr, string, &entryPtr) == TCL_OK) {
2631 	if (entryPtr != NULL) {
2632 	    return TCL_OK;	/* Special id doesn't currently exist. */
2633 	}
2634 	object = entryPtr;
2635     } else {
2636 	/* Assume that this is a binding tag. */
2637 	object = Blt_TreeViewEntryTag(tvPtr, string);
2638     }
2639     if (object == NULL) {
2640         Tcl_AppendResult(interp, "unknown object", string, 0);
2641         return TCL_ERROR;
2642     }
2643     return Blt_ConfigureBindingsFromObj(interp, tvPtr->bindTable, object,
2644 	 objc - 3, objv + 3);
2645 }
2646 
2647 
2648 /*ARGSUSED*/
2649 static int
EditOp(tvPtr,interp,objc,objv)2650 EditOp(tvPtr, interp, objc, objv)
2651     TreeView *tvPtr;
2652     Tcl_Interp *interp;		/* Not used. */
2653     int objc;
2654     Tcl_Obj *CONST *objv;
2655 {
2656     TreeViewEntry *entryPtr;
2657     char *string;
2658     int isRoot, isTest;
2659     int x, y, wx, wy;
2660     int rootX, rootY;
2661 
2662     Tk_GetRootCoords(tvPtr->tkwin, &rootX, &rootY);
2663 
2664     isRoot = isTest = FALSE;
2665     while (objc>2) {
2666         string = Tcl_GetString(objv[2]);
2667         if (strcmp("-root", string) == 0) {
2668             isRoot = TRUE;
2669             objv++, objc--;
2670         } else if (strcmp("-test", string) == 0) {
2671             isTest = TRUE;
2672             objv++, objc--;
2673         } else if (strcmp("-noscroll", string) == 0) {
2674             tvPtr->noScroll = 1;
2675             if (objc == 3) { return TCL_OK;}
2676             objv++, objc--;
2677         } else if (strcmp("-scroll", string) == 0) {
2678             tvPtr->noScroll = 0;
2679             if (objc == 3) { return TCL_OK;}
2680             objv++, objc--;
2681         } else {
2682             break;
2683         }
2684     }
2685     if (objc != 4) {
2686 	Tcl_AppendResult(interp, "wrong # args: should be \"",
2687 		Tcl_GetString(objv[0]), " ", Tcl_GetString(objv[1]),
2688 			" ?-root? x y\"", (char *)NULL);
2689 	return TCL_ERROR;
2690 
2691     }
2692     if ((Tcl_GetIntFromObj(interp, objv[2], &x) != TCL_OK) ||
2693 	(Tcl_GetIntFromObj(interp, objv[3], &y) != TCL_OK)) {
2694 	return TCL_ERROR;
2695     }
2696     wx = x;
2697     wy = y;
2698     if (isRoot) {
2699 	x -= rootX;
2700 	y -= rootY;
2701     }
2702     entryPtr = Blt_TreeViewNearestEntry(tvPtr, x, y, FALSE);
2703     if (entryPtr != NULL) {
2704 	Blt_ChainLink *linkPtr;
2705 	TreeViewColumn *columnPtr;
2706 	int worldX;
2707 
2708 	worldX = WORLDX(tvPtr, x);
2709 	for (linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr);
2710 	     linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
2711 	    columnPtr = Blt_ChainGetValue(linkPtr);
2712 	    if (!columnPtr->editable) {
2713 		continue;		/* Column isn't editable. */
2714 	    }
2715 	    if ((worldX >= columnPtr->worldX) &&
2716 		(worldX < (columnPtr->worldX + columnPtr->width))) {
2717 		TreeViewValue *valuePtr;
2718 
2719 		valuePtr = Blt_TreeViewFindValue(entryPtr, columnPtr);
2720 		if (valuePtr != NULL) {
2721 		    TreeViewStyle *stylePtr;
2722 		    int retVal = isTest;
2723 
2724 		    stylePtr = CHOOSE3(tvPtr->stylePtr,columnPtr->stylePtr, valuePtr->stylePtr);
2725 		    if (stylePtr->classPtr->editProc != NULL) {
2726 			if ((*stylePtr->classPtr->editProc)(tvPtr, entryPtr,
2727 				    valuePtr, stylePtr, x, y, &retVal) != TCL_OK) {
2728 			    return TCL_ERROR;
2729 			}
2730 			Blt_TreeViewEventuallyRedraw(tvPtr);
2731 		    }
2732 		    Tcl_SetObjResult(interp, Tcl_NewIntObj(retVal));
2733 		    return TCL_OK;
2734 		}
2735 	    }
2736 	}
2737     }
2738     Tcl_SetObjResult(interp, Tcl_NewIntObj(0));
2739     return TCL_OK;
2740 }
2741 
2742 /*
2743  *----------------------------------------------------------------------
2744  *
2745  * EntryActivateOp --
2746  *
2747  *	Selects the entry to appear active.
2748  *
2749  *----------------------------------------------------------------------
2750  */
2751 /*ARGSUSED*/
2752 static int
EntryActivateOp(tvPtr,interp,objc,objv)2753 EntryActivateOp(tvPtr, interp, objc, objv)
2754     TreeView *tvPtr;
2755     Tcl_Interp *interp;
2756     int objc;			/* Not used. */
2757     Tcl_Obj *CONST *objv;
2758 {
2759     TreeViewEntry *newPtr, *oldPtr;
2760     char *string;
2761 
2762     string = Tcl_GetString(objv[3]);
2763     if (string[0] == '\0') {
2764 	newPtr = NULL;
2765     } else if (GetEntryFromObj(tvPtr, objv[3], &newPtr) != TCL_OK) {
2766 	return TCL_ERROR;
2767     }
2768     if (tvPtr->treeColumn.hidden) {
2769 	return TCL_OK;
2770     }
2771     oldPtr = tvPtr->activePtr;
2772     tvPtr->activePtr = newPtr;
2773     if (!(tvPtr->flags & TV_REDRAW) && (newPtr != oldPtr)) {
2774 	Drawable drawable;
2775 	int x, y;
2776 
2777 	drawable = Tk_WindowId(tvPtr->tkwin);
2778 	if (oldPtr != NULL) {
2779 	    x = SCREENX(tvPtr, oldPtr->worldX);
2780 	    if (!tvPtr->flatView) {
2781 	        int hr, level;
2782 	        level = DEPTH(tvPtr, oldPtr->node);
2783 		x += ICONWIDTH(level);
2784                 hr = ((tvPtr->flags & TV_HIDE_ROOT) ? 1 : 0);
2785                 if (!(tvPtr->lineWidth>0 || tvPtr->button.reqSize>0 || level>hr)) {
2786                     x = 2;
2787                 }
2788               }
2789 	    y = SCREENY(tvPtr, oldPtr->worldY);
2790 	    oldPtr->flags |= ENTRY_ICON;
2791 	    Blt_TreeViewDrawIcon(tvPtr, oldPtr, drawable, x, y, 1);
2792 	}
2793 	if (newPtr != NULL) {
2794 	    x = SCREENX(tvPtr, newPtr->worldX);
2795 	    if (!tvPtr->flatView) {
2796 	        int hr, level;
2797 	        level = DEPTH(tvPtr, newPtr->node);
2798                  x += ICONWIDTH(level);
2799                 hr = ((tvPtr->flags & TV_HIDE_ROOT) ? 1 : 0);
2800                 if (!(tvPtr->lineWidth>0 || tvPtr->button.reqSize>0 || level>hr)) {
2801                     x = 2;
2802                 }
2803 	    }
2804 	    y = SCREENY(tvPtr, newPtr->worldY);
2805 	    newPtr->flags |= ENTRY_ICON;
2806 	    Blt_TreeViewDrawIcon(tvPtr, newPtr, drawable, x, y, 1);
2807 	}
2808     }
2809     return TCL_OK;
2810 }
2811 
2812 /*
2813  *----------------------------------------------------------------------
2814  *
2815  * EntryCgetOp --
2816  *
2817  *----------------------------------------------------------------------
2818  */
2819 /*ARGSUSED*/
2820 static int
EntryCgetOp(tvPtr,interp,objc,objv)2821 EntryCgetOp(tvPtr, interp, objc, objv)
2822     TreeView *tvPtr;
2823     Tcl_Interp *interp;
2824     int objc;			/* Not used. */
2825     Tcl_Obj *CONST *objv;
2826 {
2827     TreeViewEntry *entryPtr;
2828 
2829     if (Blt_TreeViewGetEntry(tvPtr, objv[3], &entryPtr) != TCL_OK) {
2830 	return TCL_ERROR;
2831     }
2832     Blt_TreeViewOptsInit(tvPtr);
2833     return Blt_ConfigureValueFromObj(interp, tvPtr->tkwin,
2834 		bltTreeViewEntrySpecs, (char *)entryPtr, objv[4], 0);
2835 }
2836 
2837 /*
2838  *----------------------------------------------------------------------
2839  *
2840  * EntryConfigureOp --
2841  *
2842  * 	This procedure is called to process a list of configuration
2843  *	options database, in order to reconfigure the one of more
2844  *	entries in the widget.
2845  *
2846  *	  .h entryconfigure node node node node option value
2847  *
2848  * Results:
2849  *	A standard Tcl result.  If TCL_ERROR is returned, then
2850  *	interp->result contains an error message.
2851  *
2852  * Side effects:
2853  *	Configuration information, such as text string, colors, font,
2854  *	etc. get set for tvPtr; old resources get freed, if there
2855  *	were any.  The hypertext is redisplayed.
2856  *
2857  *----------------------------------------------------------------------
2858  */
2859 static int
EntryConfigureOp(tvPtr,interp,objc,objv)2860 EntryConfigureOp(tvPtr, interp, objc, objv)
2861     TreeView *tvPtr;
2862     Tcl_Interp *interp;
2863     int objc;
2864     Tcl_Obj *CONST *objv;
2865 {
2866     int nIds, configObjc;
2867     Tcl_Obj *CONST *configObjv;
2868     register int i;
2869     TreeViewEntry *entryPtr;
2870     TreeViewTagInfo info = {0};
2871     char *string;
2872 
2873     /* Figure out where the option value pairs begin */
2874     for (i = 3; i < objc; i++) {
2875 	string = Tcl_GetString(objv[i]);
2876 	if (string[0] == '-') {
2877 	    break;
2878 	}
2879     }
2880     nIds = i-3;		  /* # of tags or ids specified */
2881     if (nIds<1) {
2882         Tcl_AppendResult(interp, "no ids specified", 0);
2883         return TCL_ERROR;
2884     }
2885     objc -= 3, objv += 3;
2886     configObjc = objc - nIds;	/* # of options specified */
2887     configObjv = objv + nIds;	/* Start of options in objv  */
2888 
2889     Blt_TreeViewOptsInit(tvPtr);
2890     for (i = 0; i < nIds; i++) {
2891 	if (Blt_TreeViewFindTaggedEntries(tvPtr, objv[i], &info) != TCL_OK) {
2892 	    return TCL_ERROR;
2893 	}
2894 	for (entryPtr = Blt_TreeViewFirstTaggedEntry(&info); entryPtr != NULL;
2895 	     entryPtr = Blt_TreeViewNextTaggedEntry(&info)) {
2896 	    if (configObjc == 0) {
2897 		return Blt_ConfigureInfoFromObj(interp, tvPtr->tkwin,
2898 			bltTreeViewEntrySpecs, (char *)entryPtr,
2899 			(Tcl_Obj *)NULL, 0);
2900 	    } else if (configObjc == 1) {
2901 		return Blt_ConfigureInfoFromObj(interp, tvPtr->tkwin,
2902 			bltTreeViewEntrySpecs, (char *)entryPtr,
2903 			configObjv[0], 0);
2904 	    }
2905 	    if (Blt_TreeViewConfigureEntry(tvPtr, entryPtr, configObjc,
2906 		configObjv, BLT_CONFIG_OBJV_ONLY) != TCL_OK) {
2907                 Blt_TreeViewDoneTaggedEntries(&info);
2908 		return TCL_ERROR;
2909 	    }
2910 	}
2911          Blt_TreeViewDoneTaggedEntries(&info);
2912      }
2913     tvPtr->flags |= (TV_DIRTY | TV_LAYOUT | TV_SCROLL | TV_RESORT|TV_DIRTYALL);
2914     Blt_TreeViewEventuallyRedraw(tvPtr);
2915     return TCL_OK;
2916 }
2917 
2918 /*
2919  *----------------------------------------------------------------------
2920  *
2921  * EntryIsOpenOp --
2922  *
2923  *----------------------------------------------------------------------
2924  */
2925 /*ARGSUSED*/
2926 static int
EntryIsBeforeOp(tvPtr,interp,objc,objv)2927 EntryIsBeforeOp(tvPtr, interp, objc, objv)
2928     TreeView *tvPtr;
2929     Tcl_Interp *interp;
2930     int objc;			/* Not used. */
2931     Tcl_Obj *CONST *objv;
2932 {
2933     TreeViewEntry *e1Ptr, *e2Ptr;
2934     int bool;
2935 
2936     if ((Blt_TreeViewGetEntry(tvPtr, objv[3], &e1Ptr) != TCL_OK) ||
2937 	(Blt_TreeViewGetEntry(tvPtr, objv[4], &e2Ptr) != TCL_OK)) {
2938 	return TCL_ERROR;
2939     }
2940     bool = Blt_TreeIsBefore(e1Ptr->node, e2Ptr->node);
2941     Tcl_SetObjResult(interp, Tcl_NewBooleanObj(bool));
2942     return TCL_OK;
2943 }
2944 
2945 /*
2946  *----------------------------------------------------------------------
2947  *
2948  * EntryIsHiddenOp --
2949  *
2950  *----------------------------------------------------------------------
2951  */
2952 /*ARGSUSED*/
2953 static int
EntryIsHiddenOp(tvPtr,interp,objc,objv)2954 EntryIsHiddenOp(tvPtr, interp, objc, objv)
2955     TreeView *tvPtr;
2956     Tcl_Interp *interp;
2957     int objc;			/* Not used. */
2958     Tcl_Obj *CONST *objv;
2959 {
2960     TreeViewEntry *entryPtr;
2961     int bool;
2962 
2963     if (Blt_TreeViewGetEntry(tvPtr, objv[3], &entryPtr) != TCL_OK) {
2964 	return TCL_ERROR;
2965     }
2966     bool = (entryPtr->flags & ENTRY_HIDDEN);
2967     Tcl_SetObjResult(interp, Tcl_NewBooleanObj(bool));
2968     return TCL_OK;
2969 }
2970 
2971 /*
2972  *----------------------------------------------------------------------
2973  *
2974  * EntryIsLeafOp --
2975  *
2976  *----------------------------------------------------------------------
2977  */
2978 /*ARGSUSED*/
2979 static int
EntryIsLeafOp(tvPtr,interp,objc,objv)2980 EntryIsLeafOp(tvPtr, interp, objc, objv)
2981     TreeView *tvPtr;
2982     Tcl_Interp *interp;
2983     int objc;			/* Not used. */
2984     Tcl_Obj *CONST *objv;
2985 {
2986     TreeViewEntry *entryPtr;
2987     int bool;
2988 
2989     if (Blt_TreeViewGetEntry(tvPtr, objv[3], &entryPtr) != TCL_OK) {
2990 	return TCL_ERROR;
2991     }
2992     bool = Blt_TreeViewIsLeaf(entryPtr);
2993     Tcl_SetObjResult(interp, Tcl_NewBooleanObj(bool));
2994     return TCL_OK;
2995 }
2996 
2997 
2998 /*
2999  *----------------------------------------------------------------------
3000  *
3001  * EntryIsOpenOp --
3002  *
3003  *----------------------------------------------------------------------
3004  */
3005 /*ARGSUSED*/
3006 static int
EntryIsOpenOp(tvPtr,interp,objc,objv)3007 EntryIsOpenOp(tvPtr, interp, objc, objv)
3008     TreeView *tvPtr;
3009     Tcl_Interp *interp;
3010     int objc;			/* Not used. */
3011     Tcl_Obj *CONST *objv;
3012 {
3013     TreeViewEntry *entryPtr;
3014     int bool;
3015 
3016     if (Blt_TreeViewGetEntry(tvPtr, objv[3], &entryPtr) != TCL_OK) {
3017 	return TCL_ERROR;
3018     }
3019     bool = ((entryPtr->flags & ENTRY_CLOSED) == 0);
3020     Tcl_SetObjResult(interp, Tcl_NewBooleanObj(bool));
3021     return TCL_OK;
3022 }
3023 
3024 /*ARGSUSED*/
3025 static int
EntryParentOp(tvPtr,interp,objc,objv)3026 EntryParentOp(tvPtr, interp, objc, objv)
3027     TreeView *tvPtr;
3028     Tcl_Interp *interp;
3029     int objc;
3030     Tcl_Obj *CONST *objv;
3031 {
3032     TreeViewEntry *entryPtr;
3033 
3034     if (Blt_TreeViewGetEntry(tvPtr, objv[3], &entryPtr) != TCL_OK) {
3035 	return TCL_ERROR;
3036     }
3037     entryPtr = Blt_TreeViewParentEntry(entryPtr);
3038     if (entryPtr != NULL) {
3039         Tcl_SetObjResult(interp, NodeToObj(entryPtr->node));
3040     }
3041     return TCL_OK;
3042 }
3043 
3044 /*ARGSUSED*/
3045 static int
EntryUpOp(tvPtr,interp,objc,objv)3046 EntryUpOp(tvPtr, interp, objc, objv)
3047     TreeView *tvPtr;
3048     Tcl_Interp *interp;
3049     int objc;
3050     Tcl_Obj *CONST *objv;
3051 {
3052     TreeViewEntry *entryPtr, *fromPtr;
3053 
3054     if (Blt_TreeViewGetEntry(tvPtr, objv[3], &entryPtr) != TCL_OK) {
3055 	return TCL_ERROR;
3056     }
3057     fromPtr = entryPtr;
3058     if (tvPtr->flatView) {
3059         int i;
3060 
3061         i = entryPtr->flatIndex - 1;
3062         if (i >= 0) {
3063             entryPtr = FLATIND(tvPtr,i);
3064         }
3065     } else {
3066         entryPtr = Blt_TreeViewPrevEntry(fromPtr, ENTRY_MASK);
3067         if (entryPtr == NULL) {
3068             entryPtr = fromPtr;
3069         }
3070         if ((entryPtr == tvPtr->rootPtr) &&
3071             (tvPtr->flags & TV_HIDE_ROOT)) {
3072             entryPtr = Blt_TreeViewNextEntry(entryPtr, ENTRY_MASK);
3073         }
3074     }
3075     if (entryPtr != NULL) {
3076         Tcl_SetObjResult(interp, NodeToObj(entryPtr->node));
3077     }
3078     return TCL_OK;
3079 }
3080 
3081 /*ARGSUSED*/
3082 static int
EntryDepthOp(tvPtr,interp,objc,objv)3083 EntryDepthOp(tvPtr, interp, objc, objv)
3084     TreeView *tvPtr;
3085     Tcl_Interp *interp;
3086     int objc;
3087     Tcl_Obj *CONST *objv;
3088 {
3089     TreeViewEntry *entryPtr;
3090 
3091     if (Blt_TreeViewGetEntry(tvPtr, objv[3], &entryPtr) != TCL_OK) {
3092 	return TCL_ERROR;
3093     }
3094     if (entryPtr != NULL) {
3095         Tcl_SetObjResult(interp, Tcl_NewIntObj(DEPTH(tvPtr, entryPtr->node)));
3096     }
3097     return TCL_OK;
3098 }
3099 
3100 
3101 /*ARGSUSED*/
3102 static int
EntryDownOp(tvPtr,interp,objc,objv)3103 EntryDownOp(tvPtr, interp, objc, objv)
3104     TreeView *tvPtr;
3105     Tcl_Interp *interp;
3106     int objc;
3107     Tcl_Obj *CONST *objv;
3108 {
3109     TreeViewEntry *entryPtr, *fromPtr;
3110 
3111     if (Blt_TreeViewGetEntry(tvPtr, objv[3], &entryPtr) != TCL_OK) {
3112 	return TCL_ERROR;
3113     }
3114     fromPtr = entryPtr;
3115     if (tvPtr->flatView) {
3116         int i;
3117 
3118         i = entryPtr->flatIndex + 1;
3119         if (i < tvPtr->nEntries) {
3120             entryPtr = FLATIND(tvPtr, i);
3121         }
3122     } else {
3123         entryPtr = Blt_TreeViewNextEntry(fromPtr, ENTRY_MASK);
3124         if (entryPtr == NULL) {
3125             entryPtr = fromPtr;
3126         }
3127         if ((entryPtr == tvPtr->rootPtr) &&
3128         (tvPtr->flags & TV_HIDE_ROOT)) {
3129             entryPtr = Blt_TreeViewNextEntry(entryPtr, ENTRY_MASK);
3130         }
3131     }
3132     if (entryPtr != NULL) {
3133         Tcl_SetObjResult(interp, NodeToObj(entryPtr->node));
3134     }
3135     return TCL_OK;
3136 }
3137 
3138 /*ARGSUSED*/
3139 static int
EntryExistsOp(tvPtr,interp,objc,objv)3140 EntryExistsOp(tvPtr, interp, objc, objv)
3141     TreeView *tvPtr;
3142     Tcl_Interp *interp;
3143     int objc;
3144     Tcl_Obj *CONST *objv;
3145 {
3146     TreeViewEntry *entryPtr;
3147     TreeViewColumn *columnPtr;
3148 
3149     int exists;
3150 
3151     if (objc==5 && Blt_TreeViewGetColumn(interp, tvPtr, objv[4], &columnPtr) != TCL_OK) {
3152         return TCL_ERROR;
3153     }
3154     exists = (Blt_TreeViewGetEntry(tvPtr, objv[3], &entryPtr) == TCL_OK);
3155 
3156     if (exists && objc == 5) {
3157         if (!Blt_TreeValueExists(tvPtr->tree, entryPtr->node, Tcl_GetString(objv[4]))) {
3158             exists = FALSE;
3159         }
3160     }
3161     Tcl_SetObjResult(interp, Tcl_NewBooleanObj(exists));
3162     return TCL_OK;
3163 }
3164 
3165 
3166 /*ARGSUSED*/
3167 static int
EntryPrevOp(tvPtr,interp,objc,objv)3168 EntryPrevOp(tvPtr, interp, objc, objv)
3169     TreeView *tvPtr;
3170     Tcl_Interp *interp;
3171     int objc;
3172     Tcl_Obj *CONST *objv;
3173 {
3174     TreeViewEntry *entryPtr, *fromPtr;
3175 
3176     if (Blt_TreeViewGetEntry(tvPtr, objv[3], &entryPtr) != TCL_OK) {
3177 	return TCL_ERROR;
3178     }
3179     fromPtr = entryPtr;
3180     if (tvPtr->flatView) {
3181         int i;
3182 
3183         i = entryPtr->flatIndex - 1;
3184         if (i < 0) {
3185             i = tvPtr->nEntries - 1;
3186         }
3187         entryPtr = FLATIND(tvPtr, i);
3188     } else {
3189         entryPtr = Blt_TreeViewPrevEntry(fromPtr, ENTRY_MASK);
3190         if (entryPtr == NULL) {
3191             entryPtr = LastEntry(tvPtr, tvPtr->rootPtr, ENTRY_MASK);
3192         }
3193         if ((entryPtr == tvPtr->rootPtr) &&
3194             (tvPtr->flags & TV_HIDE_ROOT)) {
3195             /*entryPtr = Blt_TreeViewNextEntry(entryPtr, ENTRY_MASK); */
3196             entryPtr = LastEntry(tvPtr, tvPtr->rootPtr, ENTRY_MASK);
3197         }
3198     }
3199     if (entryPtr != NULL) {
3200         Tcl_SetObjResult(interp, NodeToObj(entryPtr->node));
3201     }
3202     return TCL_OK;
3203 }
3204 
3205 /*ARGSUSED*/
3206 static int
EntryRelabelOp(tvPtr,interp,objc,objv)3207 EntryRelabelOp(tvPtr, interp, objc, objv)
3208     TreeView *tvPtr;
3209     Tcl_Interp *interp;
3210     int objc;
3211     Tcl_Obj *CONST *objv;
3212 {
3213     TreeViewEntry *entryPtr;
3214     char *string;
3215     if ((tvPtr->flags & TV_ALLOW_DUPLICATES) == 0) {
3216         Tcl_AppendResult(interp, "must enable -allowduplicates to use relabel", 0);
3217         return TCL_ERROR;
3218     }
3219 
3220     if (Blt_TreeViewGetEntry(tvPtr, objv[3], &entryPtr) != TCL_OK) {
3221 	return TCL_ERROR;
3222     }
3223     string = Tcl_GetString(objv[4]);
3224     Blt_TreeRelabelNode(tvPtr->tree, entryPtr->node, string);
3225     return TCL_OK;
3226 }
3227 
3228 /*ARGSUSED*/
3229 static int
EntrySelectOp(tvPtr,interp,objc,objv)3230 EntrySelectOp(tvPtr, interp, objc, objv)
3231     TreeView *tvPtr;
3232     Tcl_Interp *interp;
3233     int objc;
3234     Tcl_Obj *CONST *objv;
3235 {
3236     TreeViewEntry *entryPtr;
3237     Tcl_DString dStr;
3238     int rc;
3239 
3240     if (Blt_TreeViewGetEntry(tvPtr, objv[3], &entryPtr) != TCL_OK) {
3241 	return TCL_ERROR;
3242     }
3243     if ((entryPtr != NULL) && (entryPtr != tvPtr->focusPtr)) {
3244         if (entryPtr->flags & ENTRY_HIDDEN) {
3245             /* Doesn't make sense to set focus to a node you can't see. */
3246             MapAncestors(tvPtr, entryPtr);
3247         }
3248         /* Changing focus can only affect the visible entries.  The
3249         * entry layout stays the same. */
3250         if (tvPtr->focusPtr != NULL) {
3251             tvPtr->focusPtr->flags |= ENTRY_REDRAW;
3252         }
3253         entryPtr->flags |= ENTRY_REDRAW;
3254         tvPtr->flags |= TV_SCROLL;
3255         tvPtr->focusPtr = entryPtr;
3256     }
3257     Tcl_DStringInit(&dStr);
3258     Tcl_DStringAppend(&dStr, "::blt::tv::MoveFocus ", -1);
3259     Tcl_DStringAppend(&dStr, Tk_PathName(tvPtr->tkwin), -1);
3260     Tcl_DStringAppend(&dStr, " focus", -1);
3261     rc = Tcl_GlobalEval(interp, Tcl_DStringValue(&dStr));
3262     Tcl_DStringFree(&dStr);
3263     return rc;
3264 }
3265 
3266 /*ARGSUSED*/
3267 static int
EntrySiblingOp(tvPtr,interp,objc,objv)3268 EntrySiblingOp(tvPtr, interp, objc, objv)
3269     TreeView *tvPtr;
3270     Tcl_Interp *interp;
3271     int objc;
3272     Tcl_Obj *CONST *objv;
3273 {
3274     TreeViewEntry *entryPtr = NULL, *fromPtr;
3275     Blt_TreeNode node;
3276     int next = 1;
3277 
3278     if (objc>4) {
3279         const char *cp;
3280         cp = Tcl_GetString(objv[3]);
3281         if (!strcmp(cp, "-before")) {
3282             next = 0;
3283         } else {
3284             Tcl_AppendResult(interp, "expected \"-before\"", 0);
3285             return TCL_ERROR;
3286         }
3287         objc--;
3288         objv++;
3289     }
3290     if (Blt_TreeViewGetEntry(tvPtr, objv[3], &fromPtr) != TCL_OK) {
3291 	return TCL_ERROR;
3292     }
3293     if (next) {
3294         node = Blt_TreeNextSibling(fromPtr->node);
3295         if (node != NULL) {
3296             entryPtr = Blt_NodeToEntry(tvPtr, node);
3297         }
3298     } else {
3299         node = Blt_TreePrevSibling(fromPtr->node);
3300         if (node != NULL) {
3301             entryPtr = Blt_NodeToEntry(tvPtr, node);
3302         }
3303     }
3304 
3305     if (entryPtr != NULL) {
3306         Tcl_SetObjResult(interp, NodeToObj(entryPtr->node));
3307     }
3308     return TCL_OK;
3309 }
3310 
3311 /*ARGSUSED*/
3312 static int
EntryNextOp(tvPtr,interp,objc,objv)3313 EntryNextOp(tvPtr, interp, objc, objv)
3314     TreeView *tvPtr;
3315     Tcl_Interp *interp;
3316     int objc;
3317     Tcl_Obj *CONST *objv;
3318 {
3319     TreeViewEntry *entryPtr;
3320 
3321     if (Blt_TreeViewGetEntry(tvPtr, objv[3], &entryPtr) != TCL_OK) {
3322 	return TCL_ERROR;
3323     }
3324     if (tvPtr->flatView) {
3325         int i;
3326 
3327         i = entryPtr->flatIndex + 1;
3328         if (i >= tvPtr->nEntries) {
3329             i = 0;
3330         }
3331         entryPtr = FLATIND(tvPtr, i);
3332     } else {
3333         entryPtr = Blt_TreeViewNextEntry(entryPtr, ENTRY_MASK);
3334         if (entryPtr == NULL) {
3335             if (tvPtr->flags & TV_HIDE_ROOT) {
3336                 entryPtr = Blt_TreeViewNextEntry(tvPtr->rootPtr,ENTRY_MASK);
3337             } else {
3338                 entryPtr = tvPtr->rootPtr;
3339             }
3340         }
3341     }
3342     if (entryPtr != NULL) {
3343         Tcl_SetObjResult(interp, NodeToObj(entryPtr->node));
3344     }
3345     return TCL_OK;
3346 }
3347 
3348 /*ARGSUSED*/
3349 static int
EntryChildrenOp(tvPtr,interp,objc,objv)3350 EntryChildrenOp(tvPtr, interp, objc, objv)
3351     TreeView *tvPtr;
3352     Tcl_Interp *interp;
3353     int objc;
3354     Tcl_Obj *CONST *objv;
3355 {
3356     TreeViewEntry *parentPtr;
3357     Tcl_Obj *listObjPtr, *objPtr;
3358     unsigned int mask;
3359 
3360     mask = 0;
3361     if (Blt_TreeViewGetEntry(tvPtr, objv[3], &parentPtr) != TCL_OK) {
3362 	return TCL_ERROR;
3363     }
3364     listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
3365     if (objc == 4) {
3366 	TreeViewEntry *entryPtr;
3367 
3368 	for (entryPtr = Blt_TreeViewFirstChild(parentPtr, mask);
3369 	     entryPtr != NULL;
3370 	     entryPtr = Blt_TreeViewNextSibling(entryPtr, mask)) {
3371 	    objPtr = NodeToObj(entryPtr->node);
3372 	    Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
3373 	}
3374     } else if (objc == 6) {
3375 	TreeViewEntry *entryPtr, *lastPtr, *firstPtr;
3376 	int firstPos, lastPos;
3377 	int nNodes;
3378 
3379 	if ((Blt_GetPositionFromObj(interp, objv[4], &firstPos) != TCL_OK) ||
3380 	    (Blt_GetPositionFromObj(interp, objv[5], &lastPos) != TCL_OK)) {
3381 	    return TCL_ERROR;
3382 	}
3383 	nNodes = Blt_TreeNodeDegree(parentPtr->node);
3384 	if (nNodes == 0) {
3385 	    return TCL_OK;
3386 	}
3387 	if ((lastPos == END) || (lastPos >= nNodes)) {
3388 	    lastPtr = Blt_TreeViewLastChild(parentPtr, mask);
3389 	} else {
3390 	    lastPtr = GetNthEntry(parentPtr, lastPos, mask);
3391 	}
3392 	if ((firstPos == END) || (firstPos >= nNodes)) {
3393 	    firstPtr = Blt_TreeViewLastChild(parentPtr, mask);
3394 	} else {
3395 	    firstPtr = GetNthEntry(parentPtr, firstPos, mask);
3396 	}
3397 	if ((lastPos != END) && (firstPos > lastPos)) {
3398 	    for (entryPtr = lastPtr; entryPtr != NULL;
3399 		entryPtr = Blt_TreeViewPrevEntry(entryPtr, mask)) {
3400 		objPtr = NodeToObj(entryPtr->node);
3401 		Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
3402 		if (entryPtr == firstPtr) {
3403 		    break;
3404 		}
3405 	    }
3406 	} else {
3407 	    for (entryPtr = firstPtr; entryPtr != NULL;
3408 		 entryPtr = Blt_TreeViewNextEntry(entryPtr, mask)) {
3409 		objPtr = NodeToObj(entryPtr->node);
3410 		Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
3411 		if (entryPtr == lastPtr) {
3412 		    break;
3413 		}
3414 	    }
3415 	}
3416     } else {
3417 	Tcl_AppendResult(interp, "wrong # args: should be \"",
3418 			 Tcl_GetString(objv[0]), " ",
3419 			 Tcl_GetString(objv[1]), " ",
3420 			 Tcl_GetString(objv[2]), " tagOrId ?first last?",
3421 			 (char *)NULL);
3422 	return TCL_ERROR;
3423     }
3424     Tcl_SetObjResult(interp, listObjPtr);
3425     return TCL_OK;
3426 }
3427 
3428 
3429 /*
3430  *----------------------------------------------------------------------
3431  *
3432  * EntryDeleteOp --
3433  *
3434  *----------------------------------------------------------------------
3435  */
3436 /*ARGSUSED*/
3437 static int
EntryDeleteOp(tvPtr,interp,objc,objv)3438 EntryDeleteOp(tvPtr, interp, objc, objv)
3439     TreeView *tvPtr;
3440     Tcl_Interp *interp;
3441     int objc;			/* Not used. */
3442     Tcl_Obj *CONST *objv;
3443 {
3444     TreeViewEntry *entryPtr;
3445 
3446     if (Blt_TreeViewGetEntry(tvPtr, objv[3], &entryPtr) != TCL_OK) {
3447 	return TCL_ERROR;
3448     }
3449     if (objc == 5) {
3450 	int entryPos;
3451 	Blt_TreeNode node;
3452 	/*
3453 	 * Delete a single child node from a hierarchy specified
3454 	 * by its numeric position.
3455 	 */
3456 	if (Blt_GetPositionFromObj(interp, objv[3], &entryPos) != TCL_OK) {
3457 	    return TCL_ERROR;
3458 	}
3459 	if (entryPos >= (int)Blt_TreeNodeDegree(entryPtr->node)) {
3460 	    return TCL_OK;	/* Bad first index */
3461 	}
3462 	if (entryPos == END) {
3463 	    node = Blt_TreeLastChild(entryPtr->node);
3464 	} else {
3465 	    node = GetNthNode(entryPtr->node, entryPos);
3466 	}
3467 	DeleteNode(tvPtr, node);
3468     } else {
3469 	int firstPos, lastPos;
3470 	Blt_TreeNode node, first, last, next;
3471 	int nEntries;
3472 	/*
3473 	 * Delete range of nodes in hierarchy specified by first/last
3474 	 * positions.
3475 	 */
3476 	if ((Blt_GetPositionFromObj(interp, objv[4], &firstPos) != TCL_OK) ||
3477 	    (Blt_GetPositionFromObj(interp, objv[5], &lastPos) != TCL_OK)) {
3478 	    return TCL_ERROR;
3479 	}
3480 	nEntries = Blt_TreeNodeDegree(entryPtr->node);
3481 	if (nEntries == 0) {
3482 	    return TCL_OK;
3483 	}
3484 	if (firstPos == END) {
3485 	    firstPos = nEntries - 1;
3486 	}
3487 	if (firstPos >= nEntries) {
3488 	    Tcl_AppendResult(interp, "first position \"",
3489 		Tcl_GetString(objv[4]), " is out of range", (char *)NULL);
3490 	    return TCL_ERROR;
3491 	}
3492 	if ((lastPos == END) || (lastPos >= nEntries)) {
3493 	    lastPos = nEntries - 1;
3494 	}
3495 	if (firstPos > lastPos) {
3496 	    Tcl_AppendResult(interp, "bad range: \"", Tcl_GetString(objv[4]),
3497 		" > ", Tcl_GetString(objv[5]), "\"", (char *)NULL);
3498 	    return TCL_ERROR;
3499 	}
3500 	first = GetNthNode(entryPtr->node, firstPos);
3501 	last = GetNthNode(entryPtr->node, lastPos);
3502 	for (node = first; node != NULL; node = next) {
3503 	    next = Blt_TreeNextSibling(node);
3504 	    DeleteNode(tvPtr, node);
3505 	    if (node == last) {
3506 		break;
3507 	    }
3508 	}
3509     }
3510     tvPtr->flags |= (TV_LAYOUT | TV_DIRTY | TV_RESORT);
3511     Blt_TreeViewEventuallyRedraw(tvPtr);
3512     return TCL_OK;
3513 }
3514 
3515 /*
3516  *----------------------------------------------------------------------
3517  *
3518  * EntrySizeOp --
3519  *
3520  *	Counts the number of entries at this node.
3521  *
3522  * Results:
3523  *	A standard Tcl result.  If an error occurred TCL_ERROR is
3524  *	returned and interp->result will contain an error message.
3525  *	Otherwise, TCL_OK is returned and interp->result contains
3526  *	the number of entries.
3527  *
3528  *----------------------------------------------------------------------
3529  */
3530 static int
EntrySizeOp(tvPtr,interp,objc,objv)3531 EntrySizeOp(tvPtr, interp, objc, objv)
3532     TreeView *tvPtr;
3533     Tcl_Interp *interp;
3534     int objc;
3535     Tcl_Obj *CONST *objv;
3536 {
3537     TreeViewEntry *entryPtr;
3538     int length, sum, recurse;
3539     char *string;
3540 
3541     recurse = FALSE;
3542     string = Tcl_GetStringFromObj(objv[3], &length);
3543     if ((string[0] == '-') && (length > 1) &&
3544 	(strncmp(string, "-recurse", length) == 0)) {
3545 	objv++, objc--;
3546 	recurse = TRUE;
3547     }
3548     if (objc != 4) {
3549 	Tcl_AppendResult(interp, "wrong or missing args", (char *)NULL);
3550 	return TCL_ERROR;
3551     }
3552     if (Blt_TreeViewGetEntry(tvPtr, objv[3], &entryPtr) != TCL_OK) {
3553 	return TCL_ERROR;
3554     }
3555     if (recurse) {
3556 	sum = Blt_TreeSize(entryPtr->node);
3557     } else {
3558 	sum = Blt_TreeNodeDegree(entryPtr->node);
3559     }
3560     Tcl_SetObjResult(interp, Tcl_NewIntObj(sum));
3561     return TCL_OK;
3562 }
3563 
3564 /*
3565  *----------------------------------------------------------------------
3566  *
3567  * EntryOp --
3568  *
3569  *	This procedure handles entry operations.
3570  *
3571  * Results:
3572  *	A standard Tcl result.
3573  *
3574  *----------------------------------------------------------------------
3575  */
3576 
3577 static Blt_OpSpec entryOps[] =
3578 {
3579     {"activate", 1, (Blt_Op)EntryActivateOp, 4, 4, "tagOrId",},
3580     /*bbox*/
3581     /*bind*/
3582     {"cget", 2, (Blt_Op)EntryCgetOp, 5, 5, "tagOrId option",},
3583     {"children", 2, (Blt_Op)EntryChildrenOp, 4, 6,
3584 	"tagOrId firstPos lastPos",},
3585     /*close*/
3586     {"configure", 2, (Blt_Op)EntryConfigureOp, 4, 0,
3587 	"tagOrId ?tagOrId...? ?option value?...",},
3588     {"delete", 3, (Blt_Op)EntryDeleteOp, 5, 6, "tagOrId firstPos ?lastPos?",},
3589     {"depth",  3, (Blt_Op)EntryDepthOp, 4, 4, "tagOrId",},
3590     {"down",   2, (Blt_Op)EntryDownOp, 4, 4, "tagOrId",},
3591     {"exists", 1, (Blt_Op)EntryExistsOp, 4, 5, "tagOrId ?col?",},
3592     /*focus*/
3593     /*hide*/
3594     {"get", 2, (Blt_Op)EntryGetOp, 4, -1, "tagOrId ?key? ?default?",},
3595     {"incr", 3, (Blt_Op)EntryIncrOp, 5, 6, "tagOrId key ?amount?",},
3596     /*index*/
3597     {"isbefore", 3, (Blt_Op)EntryIsBeforeOp, 5, 5, "tagOrId tagOrId",},
3598     {"ishidden", 3, (Blt_Op)EntryIsHiddenOp, 4, 4, "tagOrId",},
3599     {"isleaf", 3, (Blt_Op)EntryIsLeafOp, 4, 4, "tagOrId",},
3600     {"ismapped", 3, (Blt_Op)EntryIsmappedOp, 4, 4, "tagOrId",},
3601     {"isopen", 3, (Blt_Op)EntryIsOpenOp, 4, 4, "tagOrId",},
3602     {"isset", 3, (Blt_Op)EntryIssetOp, 5, 5, "tagOrId col",},
3603     {"isvisible", 3, (Blt_Op)EntryIsvisibleOp, 4, 4, "tagOrId",},
3604     /*move*/
3605     /*nearest*/
3606     /*open*/
3607     {"next",    2, (Blt_Op)EntryNextOp, 4, 4, "tagOrId",},
3608     {"parent",  2, (Blt_Op)EntryParentOp, 4, 4, "tagOrId",},
3609     {"prev",    2, (Blt_Op)EntryPrevOp, 4, 4, "tagOrId",},
3610     {"relabel",  2, (Blt_Op)EntryRelabelOp, 5, 5, "tagOrId newLabel",},
3611     {"select",  2, (Blt_Op)EntrySelectOp, 4, 4, "tagOrId",},
3612     /*see*/
3613     {"set", 2, (Blt_Op)EntrySetOp, 5, -1, "tagOrId col ?value ...?",},
3614     /*show*/
3615     {"sibling", 3, (Blt_Op)EntrySiblingOp, 4, 5, "?-before? tagOrId",},
3616     {"size", 3, (Blt_Op)EntrySizeOp, 4, 5, "?-recurse? tagOrId",},
3617     {"unset", 2, (Blt_Op)EntryUnsetOp, 5, 5, "tagOrId col",},
3618     {"up",   1, (Blt_Op)EntryUpOp, 4, 4, "tagOrId",},
3619     {"value", 2, (Blt_Op)EntryValueOp, 4, 5, "tagOrId ?col?",},
3620     /*toggle*/
3621 };
3622 static int nEntryOps = sizeof(entryOps) / sizeof(Blt_OpSpec);
3623 
3624 static int
EntryOp(tvPtr,interp,objc,objv)3625 EntryOp(tvPtr, interp, objc, objv)
3626     TreeView *tvPtr;
3627     Tcl_Interp *interp;
3628     int objc;
3629     Tcl_Obj *CONST *objv;
3630 {
3631     Blt_Op proc;
3632     int result;
3633 
3634     proc = Blt_GetOpFromObj(interp, nEntryOps, entryOps, BLT_OP_ARG2, objc,
3635 	objv, 0);
3636     if (proc == NULL) {
3637 	return TCL_ERROR;
3638     }
3639     result = (*proc) (tvPtr, interp, objc, objv);
3640     return result;
3641 }
3642 
3643 /*ARGSUSED*/
3644 static int
ExactCompare(interp,name,patternPtr,nocase)3645 ExactCompare(interp, name, patternPtr, nocase)
3646     Tcl_Interp *interp;		/* Not used. */
3647     char *name;
3648     Tcl_Obj *patternPtr;
3649     int nocase;
3650 {
3651     char *pattern = Tcl_GetString(patternPtr);
3652     if (!nocase) {
3653         return (strcmp(name, pattern) == 0);
3654     } else {
3655         return (strcasecmp(name, pattern) == 0);
3656     }
3657 }
3658 
3659 /*ARGSUSED*/
3660 static int
GlobCompare(interp,name,patternPtr,nocase)3661 GlobCompare(interp, name, patternPtr, nocase)
3662     Tcl_Interp *interp;		/* Not used. */
3663     char *name;
3664     Tcl_Obj *patternPtr;
3665     int nocase;
3666 {
3667     char *pattern = Tcl_GetString(patternPtr);
3668     return Tcl_StringCaseMatch(name, pattern, nocase);
3669 }
3670 
3671 static int
RegexpCompare(interp,name,patternPtr,nocase)3672 RegexpCompare(interp, name, patternPtr, nocase)
3673     Tcl_Interp *interp;
3674     char *name;
3675     Tcl_Obj *patternPtr;
3676     int nocase;
3677 {
3678     Tcl_DString dStr;
3679     int len, i, result;
3680     char *cp;
3681 
3682     Tcl_Obj *namePtr;
3683     if (!nocase) {
3684         namePtr = Tcl_NewStringObj(name, -1);
3685         result = Tcl_RegExpMatchObj(interp, namePtr, patternPtr);
3686     } else {
3687         len = strlen(name);
3688         Tcl_DStringInit(&dStr);
3689         Tcl_DStringSetLength(&dStr, len + 1);
3690         cp = Tcl_DStringValue(&dStr);
3691         for (i=0; i<len; i++) {
3692             cp[i] = tolower(name[i]);
3693         }
3694         cp[len] = 0;
3695         namePtr = Tcl_NewStringObj(cp, len);
3696         result = Tcl_RegExpMatchObj(interp, namePtr, patternPtr);
3697         Tcl_DStringFree(&dStr);
3698     }
3699     Tcl_DecrRefCount(namePtr);
3700     return result;
3701 }
3702 
3703 static int
InlistCompare(interp,name,patternPtr,nocase)3704 InlistCompare(interp, name, patternPtr, nocase)
3705     Tcl_Interp *interp;		/* Not used. */
3706     char *name;
3707     Tcl_Obj *patternPtr;
3708     int nocase;
3709 {
3710     Tcl_Obj **objv;
3711     int objc, i;
3712     char *pattern;
3713 
3714     if (Tcl_ListObjGetElements(interp, patternPtr, &objc, &objv) != TCL_OK) {
3715         return 1;
3716     }
3717     for (i = 0; i < objc; i++) {
3718         pattern = Tcl_GetString(objv[i]);
3719         if (!nocase) {
3720             if (strcmp(name, pattern) == 0) {
3721                 return 1;
3722             }
3723         } else {
3724             if (strcasecmp(name, pattern) == 0) {
3725                 return 1;
3726             }
3727         }
3728     }
3729     return 0;
3730 }
3731 
3732 /*
3733  *----------------------------------------------------------------------
3734  *
3735  * FindOp --
3736  *
3737  *	Find one or more nodes based upon the pattern provided.
3738  *
3739  * Results:
3740  *	A standard Tcl result.  The interpreter result will contain a
3741  *	list of the node serial identifiers.
3742  *
3743  *----------------------------------------------------------------------
3744  */
3745 static int
FindOp(tvPtr,interp,objc,objv)3746 FindOp(tvPtr, interp, objc, objv)
3747     TreeView *tvPtr;
3748     Tcl_Interp *interp;
3749     int objc;
3750     Tcl_Obj *CONST *objv;
3751 {
3752     TreeViewEntry *firstPtr, *lastPtr, *afterPtr;
3753     TreeViewCompareProc *compareProc;
3754     TreeViewIterProc *nextProc;
3755     int invertMatch;		/* normal search mode (matching entries) */
3756     Tcl_Obj *namePattern/*, *fullPattern*/;
3757     char *string, *addTag, *withTag, *withoutTag, *curValue;
3758     register int i;
3759     Blt_List options;
3760     Blt_ListNode node;
3761     register TreeViewEntry *entryPtr;
3762     TreeViewEntry *topPtr = NULL;
3763     Tcl_Obj *listObjPtr, *objPtr, *command, *iObj;
3764     Tcl_Obj *vObj = NULL, *execObj = NULL;
3765     TreeViewColumn *columnPtr = NULL, *retColPtr = NULL;
3766     char *retPctPtr = NULL;
3767     Tcl_DString fullName, dStr, fStr;
3768     int nMatches, maxMatches, result, length, ocnt, useformat,userow, isvis;
3769     int depth, maxdepth, mindepth, mask, istree, isleaf, invis, ismap, uselabel;
3770     int isopen, isclosed, notop, nocase, isfull, cmdLen, docount, optInd, reldepth;
3771     int isnull, retLabel, isret, cmdValue;
3772     char *colStr, *keysub;
3773     Tcl_Obj *cmdObj, *cmdArgs;
3774     Tcl_Obj **aobjv;
3775     int aobjc;
3776 
3777     enum optInd {
3778         OP_ADDTAG, OP_AFTER, OP_CMDARGS,
3779         OP_COMMAND, OP_COLUMN, OP_COUNT, OP_DEPTH, OP_EXACT,
3780         OP_EXEC, OP_GLOB, OP_INLIST,
3781         OP_INVERT, OP_ISCLOSED, OP_ISNULL, OP_ISHIDDEN,
3782         OP_ISLEAF, OP_ISMAPPED, OP_ISOPEN, OP_ISTREE, OP_LIMIT,
3783         OP_MAXDEPTH, OP_MINDEPTH, OP_NAME, OP_NOCASE, OP_NOTOP,
3784         OP_OPTION, OP_REGEXP, OP_RELDEPTH, OP_RETURN, OP_TOP,
3785         OP_USEFORMAT, OP_USELABEL, OP_USEPATH, OP_USEROW,
3786         OP_VAR, OP_VISIBLE, OP_WITHTAG, OP_WITHOUTTAG
3787     };
3788     static char *optArr[] = {
3789         "-addtag",  "-after", "-cmdargs",
3790         "-command", "-column", "-count", "-depth", "-exact",
3791         "-exec", "-glob", "-inlist", "-invert", "-isclosed", "-isempty", "-ishidden",
3792         "-isleaf", "-ismapped", "-isopen", "-istree", "-limit",
3793         "-maxdepth", "-mindepth", "-name", "-nocase", "-notop",
3794         "-option", "-regexp", "-reldepth", "-return", "-top",
3795         "-useformat", "-uselabel", "-usepath", "-userow",
3796         "-var", "-visible", "-withtag", "-withouttag",
3797         0
3798     };
3799     isnull = retLabel = isret = 0;
3800     afterPtr = NULL;
3801     listObjPtr = NULL;
3802     invertMatch = FALSE;
3803     maxMatches = 0;
3804     namePattern = NULL;
3805     command = NULL;
3806     compareProc = ExactCompare;
3807     nextProc = Blt_TreeViewNextEntry;
3808     options = Blt_ListCreate(BLT_ONE_WORD_KEYS);
3809     withTag = withoutTag = addTag = NULL;
3810     mask = istree = isleaf = isopen = isclosed = docount = 0;
3811     depth = maxdepth = mindepth = -1;
3812     uselabel = 0, userow = 0;
3813     invis = 0, isvis = 0;
3814     ismap = 0;
3815     reldepth = notop = 0;
3816     nocase = 0, isfull = 0, useformat = 0;
3817     keysub = colStr = NULL;
3818     cmdValue = 0;
3819     curValue = NULL;
3820     cmdObj = NULL;
3821     cmdArgs = NULL;
3822 
3823     entryPtr = tvPtr->rootPtr;
3824     Blt_TreeViewOptsInit(tvPtr);
3825 
3826     Tcl_DStringInit(&fullName);
3827     Tcl_DStringInit(&dStr);
3828     Tcl_DStringInit(&fStr);
3829     /*
3830      * Step 1:  Process flags for find operation.
3831      */
3832     for (i = 2; i < objc; i++) {
3833 	string = Tcl_GetStringFromObj(objv[i], &length);
3834 	if (string[0] != '-') {
3835 	    break;
3836 	}
3837         if (length == 2 && string[0] == '-' && string[0] == '-') {
3838             break;
3839         }
3840         if (Tcl_GetIndexFromObj(interp, objv[i], optArr, "option",
3841             TCL_EXACT, &optInd) != TCL_OK) {
3842                 return TCL_ERROR;
3843         }
3844         switch (optInd) {
3845         case OP_RELDEPTH:
3846 	    reldepth = 1;
3847 	    break;
3848         case OP_EXACT:
3849 	    compareProc = ExactCompare;
3850 	    break;
3851         case OP_COUNT:
3852 	    docount = 1;
3853 	    break;
3854         case OP_NOCASE:
3855 	    nocase = 1;
3856 	    break;
3857         case OP_NOTOP:
3858 	    notop = 1;
3859 	    break;
3860         case OP_USELABEL:
3861 	    uselabel = 1;
3862 	    break;
3863         case OP_USEFORMAT:
3864 	    useformat = 1;
3865 	    break;
3866         case OP_ISOPEN:
3867 	    isopen = 1;
3868 	    break;
3869         case OP_ISLEAF:
3870 	    isleaf = 1;
3871 	    break;
3872         case OP_ISTREE:
3873 	    istree = 1;
3874 	    break;
3875         case OP_USEPATH:
3876 	    isfull = 1;
3877 	    break;
3878         case OP_USEROW:
3879 	    userow = 1;
3880 	    break;
3881         case OP_ISHIDDEN:
3882 	    invis = 1;
3883 	    break;
3884         case OP_ISMAPPED:
3885 	    ismap = 1;
3886 	    break;
3887         case OP_ISNULL:
3888 	    isnull = 1;
3889 	    break;
3890         case OP_VISIBLE:
3891 	    mask = ENTRY_MASK;
3892 	    isvis = 1;
3893 	    break;
3894         case OP_INLIST:
3895 	    compareProc = InlistCompare;
3896 	    break;
3897         case OP_REGEXP:
3898 	    compareProc = RegexpCompare;
3899 	    break;
3900         case OP_GLOB:
3901 	    compareProc = GlobCompare;
3902 	    break;
3903         case OP_INVERT:
3904 	    invertMatch = TRUE;
3905 	    break;
3906         case OP_ISCLOSED:
3907 	    isclosed = 1;
3908 	    break;
3909 
3910 	/* All the rest of these take an argument. */
3911         case OP_AFTER:
3912 	    if (++i >= objc) { goto missingArg; }
3913             if (Blt_TreeViewGetEntry(tvPtr, objv[i], &afterPtr) != TCL_OK) {
3914                 goto error;
3915             }
3916 	    break;
3917         case OP_VAR:
3918 	    if (++i >= objc) { goto missingArg; }
3919 	    vObj = objv[i];
3920 	    break;
3921         case OP_EXEC:
3922 	    if (++i >= objc) { goto missingArg; }
3923 	    execObj = objv[i];
3924 	    break;
3925         case OP_TOP:
3926 	    if (++i >= objc) { goto missingArg; }
3927             if (Blt_TreeViewGetEntry(tvPtr, objv[i], &topPtr) != TCL_OK) {
3928                 goto error;
3929             }
3930 	    break;
3931         case OP_RETURN:
3932             isret = 1;
3933 	    if (++i >= objc) { goto missingArg; }
3934 	    if (Blt_TreeViewGetColumn(NULL, tvPtr, objv[i], &retColPtr)
3935                 != TCL_OK) {
3936                 if (strlen(Tcl_GetString(objv[i]))==0) {
3937                     retLabel = 1;
3938                 } else if ('%' == *Tcl_GetString(objv[i])) {
3939                     retPctPtr = Tcl_GetString(objv[i]);
3940                 } else {
3941                     Tcl_AppendResult(interp, "-return is not a column or a percent subst", 0);
3942 
3943                     goto error;
3944                 }
3945             }
3946             if (retColPtr == &tvPtr->treeColumn) {
3947                 retColPtr = NULL;
3948             }
3949 	    break;
3950         case OP_COLUMN:
3951 	    if (++i >= objc) { goto missingArg; }
3952 	    if (Blt_TreeViewGetColumnKey(interp, tvPtr, objv[i], &columnPtr, &keysub)
3953 	       != TCL_OK) {
3954                goto error;
3955             }
3956             if (columnPtr == &tvPtr->treeColumn) {
3957                 columnPtr = NULL;
3958             } else if (keysub) {
3959                 colStr = Tcl_GetString(objv[i]);
3960             }
3961 	    break;
3962         case OP_NAME:
3963 	    if (++i >= objc) { goto missingArg; }
3964 	    namePattern = objv[i];
3965 	    break;
3966         case OP_LIMIT:
3967 	    if (++i >= objc) { goto missingArg; }
3968 	    if (Tcl_GetIntFromObj(interp, objv[i], &maxMatches) != TCL_OK) {
3969 		return TCL_ERROR;
3970 	    }
3971 	    if (maxMatches < 0) {
3972 		Tcl_AppendResult(interp, "bad match limit \"",
3973 		    Tcl_GetString(objv[i]),
3974 		    "\": should be a positive number", (char *)NULL);
3975                 goto error;
3976 	    }
3977 	    break;
3978 
3979         case OP_WITHOUTTAG:
3980 	    if (++i >= objc) { goto missingArg; }
3981 	    withoutTag = Tcl_GetString(objv[i]);
3982 	    break;
3983 
3984         case OP_WITHTAG:
3985 	    if (++i >= objc) { goto missingArg; }
3986              withTag = Tcl_GetString(objv[i]);
3987              break;
3988 
3989         case OP_ADDTAG:
3990 	    if (++i >= objc) { goto missingArg; }
3991              addTag = Tcl_GetString(objv[i]);
3992              if (TagDefine(tvPtr, interp, addTag) != TCL_OK) {
3993                  goto error;
3994              }
3995              break;
3996 
3997         case OP_CMDARGS:
3998             if (cmdArgs != NULL) {
3999                 Tcl_AppendResult(interp, "duplicate -cmdargs", 0);
4000                 goto error;
4001             }
4002             if (++i >= objc) { goto missingArg; }
4003             cmdArgs = objv[i];
4004             break;
4005         case OP_COMMAND:
4006             if (cmdObj != NULL) {
4007                 Tcl_AppendResult(interp, "duplicate -command", 0);
4008                 goto error;
4009             }
4010             if (++i >= objc) { goto missingArg; }
4011             cmdObj = objv[i];
4012 	    break;
4013 
4014         case OP_DEPTH:
4015 	    if (++i >= objc) { goto missingArg; }
4016 	    if (Tcl_GetIntFromObj(interp,objv[i],&depth) != TCL_OK) {
4017                 goto error;
4018 	    }
4019 	    break;
4020         case OP_MAXDEPTH:
4021 	    if (++i >= objc) { goto missingArg; }
4022 	    if (Tcl_GetIntFromObj(interp,objv[i],&maxdepth) != TCL_OK) {
4023 	        return TCL_OK;
4024                 goto error;
4025 	    }
4026 	    break;
4027         case OP_MINDEPTH:
4028 	    if (++i >= objc) { goto missingArg; }
4029 	    if (Tcl_GetIntFromObj(interp,objv[i],&mindepth) != TCL_OK) {
4030                 goto error;
4031 	    }
4032 	    break;
4033 
4034 	case OP_OPTION:
4035 	    /*
4036 	     * Verify that the switch is actually an entry configuration
4037 	     * option.
4038 	     */
4039 	    if ((i + 2) >= objc) {
4040 		goto missingArg;
4041 	    }
4042 	    i++;
4043 	    if (Blt_ConfigureValueFromObj(interp, tvPtr->tkwin,
4044 		  bltTreeViewEntrySpecs, (char *)entryPtr, objv[i], 0)
4045 		!= TCL_OK) {
4046 		Tcl_ResetResult(interp);
4047                 goto error;
4048 	    }
4049 	    /* Save the option in the list of configuration options */
4050 	    node = Blt_ListGetNode(options, (char *)objv[i]);
4051 	    if (node == NULL) {
4052 		node = Blt_ListCreateNode(options, (char *)objv[i]);
4053 		Blt_ListAppendNode(options, node);
4054 	    }
4055 	    i++;
4056 	    Blt_ListSetValue(node, Tcl_GetString(objv[i]));
4057 	}
4058     }
4059     if (isnull) {
4060         if (namePattern != NULL) {
4061             Tcl_AppendResult(interp, "can not use -isempty & -name", (char *)NULL);
4062             goto error;
4063         }
4064         if (columnPtr == NULL) {
4065             Tcl_AppendResult(interp, "must use -isempty with -column", (char *)NULL);
4066             goto error;
4067         }
4068         if (invertMatch) {
4069             Tcl_AppendResult(interp, "can not use -isempty & -invert", (char *)NULL);
4070             goto error;
4071         }
4072         if (isret) {
4073             Tcl_AppendResult(interp, "can not use -isempty & -return", (char *)NULL);
4074             goto error;
4075         }
4076         if (command) {
4077             Tcl_AppendResult(interp, "can not use -isempty & -command", (char *)NULL);
4078             goto error;
4079         }
4080     }
4081     if (docount && (retColPtr != NULL || retLabel)) {
4082         Tcl_AppendResult(interp, "can not use -count & -return", (char *)NULL);
4083         goto error;
4084     }
4085     if (cmdObj != NULL) {
4086         command = Tcl_DuplicateObj(cmdObj);
4087         Tcl_IncrRefCount(command);
4088         iObj = Tcl_NewIntObj(0);
4089         if (Tcl_ListObjAppendElement(interp, command, iObj) != TCL_OK) {
4090             Tcl_DecrRefCount(iObj);
4091             goto error;
4092         }
4093         Tcl_ListObjLength(interp, command, &cmdLen);
4094         aobjc = 0;
4095         if (cmdArgs != NULL) {
4096             int ai;
4097             if (Tcl_ListObjGetElements(interp, cmdArgs, &aobjc, &aobjv) != TCL_OK) {
4098                 goto error;
4099             }
4100             for (ai = 0; ai < aobjc; ai++) {
4101                 TreeViewColumn *sretColPtr;
4102                 if (Blt_TreeViewGetColumn(interp, tvPtr, aobjv[ai], &sretColPtr) != TCL_OK) {
4103                     goto error;
4104                 }
4105 
4106                 iObj = Tcl_NewStringObj("",-1);
4107                 if (Tcl_ListObjAppendElement(interp, command, iObj) != TCL_OK) {
4108                     Tcl_DecrRefCount(iObj);
4109                     goto error;
4110                 }
4111             }
4112         }
4113     }
4114 
4115     if (columnPtr) {
4116         if (namePattern && (userow|isfull)) {
4117             Tcl_AppendResult(interp, "can not use -usepath|-userow & -column", (char *)NULL);
4118             goto error;
4119         }
4120        /* if (namePattern == NULL && execObj == NULL && command == NULL) {
4121             Tcl_AppendResult(interp, "-column must use -name/-exec/-command", (char *)NULL);
4122             goto error;
4123         }*/
4124         if (uselabel) {
4125             Tcl_AppendResult(interp, "can not use -uselabel & -column", (char *)NULL);
4126             goto error;
4127         }
4128     }
4129     if (vObj != NULL) {
4130         if (execObj == NULL) {
4131             Tcl_AppendResult(interp, "must use -exec with -var", (char *)NULL);
4132             goto error;
4133         }
4134     }
4135     if (namePattern && isfull &&  uselabel) {
4136         Tcl_AppendResult(interp, "can not use -uselabel & -usepath", (char *)NULL);
4137         goto error;
4138     }
4139 
4140     if ((objc - i) > 2) {
4141 	Tcl_AppendResult(interp, "too many args", (char *)NULL);
4142         goto error;
4143     }
4144     /*
4145      * Step 2:  Find the range of the search.  Check the order of two
4146      *		nodes and arrange the search accordingly.
4147      *
4148      *	Note:	Be careful to treat "end" as the end of all nodes, instead
4149      *		of the end of visible nodes.  That way, we can search the
4150      *		entire tree, even if the last folder is closed.
4151      */
4152     if (topPtr != NULL) {
4153         firstPtr = topPtr;
4154     } else {
4155         firstPtr = tvPtr->rootPtr;	/* Default to root node */
4156     }
4157     if (reldepth) {
4158         if (depth>=0) {
4159             depth += firstPtr->node->depth;
4160         }
4161         if (mindepth>=0) {
4162             mindepth += firstPtr->node->depth;
4163         }
4164         if (maxdepth>=0) {
4165             maxdepth += firstPtr->node->depth;
4166         }
4167     }
4168     lastPtr = LastEntry(tvPtr, firstPtr, 0);
4169     if (afterPtr != NULL) {
4170         TreeViewEntry *nfPtr;
4171         nfPtr = (*nextProc)(afterPtr, mask);
4172         if (nfPtr != NULL) { firstPtr = nfPtr; }
4173     }
4174 
4175     if (i < objc) {
4176 	string = Tcl_GetString(objv[i]);
4177 	if ((string[0] == 'e') && (strcmp(string, "end") == 0)) {
4178 	    firstPtr = LastEntry(tvPtr, tvPtr->rootPtr, 0);
4179 	} else if (Blt_TreeViewGetEntry(tvPtr, objv[i], &firstPtr) != TCL_OK) {
4180             goto error;
4181 	}
4182 	i++;
4183     }
4184     if (i < objc) {
4185 	string = Tcl_GetString(objv[i]);
4186 	if ((string[0] == 'e') && (strcmp(string, "end") == 0)) {
4187 	    lastPtr = LastEntry(tvPtr, tvPtr->rootPtr, 0);
4188 	} else if (Blt_TreeViewGetEntry(tvPtr, objv[i], &lastPtr) != TCL_OK) {
4189             goto error;
4190 	}
4191     }
4192     if (Blt_TreeIsBefore(lastPtr->node, firstPtr->node)) {
4193 	nextProc = Blt_TreeViewPrevEntry;
4194     }
4195     if (notop || (mask && ((tvPtr->flags & TV_HIDE_ROOT)) && firstPtr == tvPtr->rootPtr)) {
4196         firstPtr = Blt_TreeViewNextEntry(firstPtr, mask);
4197     } else if (mask && (firstPtr->flags & mask)) {
4198         firstPtr = (*nextProc)(firstPtr, mask);
4199     }
4200     nMatches = 0;
4201 
4202     /*
4203      * Step 3:	Search through the tree and look for nodes that match the
4204      *		current pattern specifications.  Save the name of each of
4205      *		the matching nodes.
4206      */
4207     if (!docount) {
4208         listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
4209     }
4210     for (entryPtr = firstPtr; entryPtr != NULL;
4211 	 entryPtr = (*nextProc) (entryPtr, mask)) {
4212 
4213         if (topPtr != NULL && entryPtr != topPtr) {
4214             if (DEPTH(tvPtr, entryPtr->node) <= DEPTH(tvPtr, topPtr->node)) {
4215                 break;
4216             }
4217         }
4218 	if (invis && (!(entryPtr->flags & ENTRY_HIDDEN))) {
4219 	       goto nextEntry;
4220 	}
4221 
4222 	if (ismap &&  !Blt_TreeViewEntryIsMapped(entryPtr)) {
4223            goto nextEntry;
4224 	}
4225 
4226 	if (istree &&  Blt_TreeViewIsLeaf(entryPtr)) {
4227             goto nextEntry;
4228 	}
4229 	if (isleaf &&  !Blt_TreeViewIsLeaf(entryPtr)) {
4230             goto nextEntry;
4231 	}
4232 	if (depth >=0 && entryPtr->node && entryPtr->node->depth != depth) {
4233             goto nextEntry;
4234         }
4235         if (maxdepth >=0 && entryPtr->node && entryPtr->node->depth > maxdepth) {
4236             goto nextEntry;
4237         }
4238         if (mindepth >=0 && entryPtr->node && entryPtr->node->depth < mindepth) {
4239             goto nextEntry;
4240         }
4241         if (isopen && ((entryPtr->flags & ENTRY_CLOSED) != 0)) {
4242             goto nextEntry;
4243         }
4244         if (isclosed && ((entryPtr->flags & ENTRY_CLOSED) == 0)) {
4245             goto nextEntry;
4246         }
4247 
4248         if (columnPtr) {
4249             TreeViewValue *valuePtr;
4250             if (useformat &&
4251             ((valuePtr = Blt_TreeViewFindValue(entryPtr, columnPtr))) &&
4252                 valuePtr->textPtr) {
4253                 Blt_TextLayoutValue( valuePtr->textPtr, &dStr);
4254                 curValue = Tcl_DStringValue(&dStr);
4255 
4256             } else {
4257                 if (colStr != NULL) {
4258                     if (Blt_TreeGetValue(NULL, tvPtr->tree, entryPtr->node,
4259                         colStr, &objPtr) != TCL_OK) {
4260                         if (!isnull) {
4261                             goto nextEntry;
4262                         } else {
4263                             goto dochecks;
4264                         }
4265                     } else if (isnull) {
4266                         goto nextEntry;
4267                     }
4268                 } else if (Blt_TreeGetValueByKey(NULL, tvPtr->tree, entryPtr->node,
4269                     columnPtr->key, &objPtr) != TCL_OK) {
4270                     if (!isnull) {
4271                         goto nextEntry;
4272                     } else {
4273                         goto dochecks;
4274                     }
4275                 } else if (isnull) {
4276                     goto nextEntry;
4277                 }
4278                 curValue = Tcl_GetString(objPtr);
4279             }
4280         } else if (useformat && entryPtr->textPtr != NULL) {
4281             Blt_TextLayoutValue( entryPtr->textPtr, &dStr);
4282             curValue = Tcl_DStringValue(&dStr);
4283         } else if (uselabel && entryPtr->labelUid != NULL) {
4284             curValue = entryPtr->labelUid;
4285         } else {
4286             curValue = Blt_TreeNodeLabel(entryPtr->node);
4287         }
4288 	if (namePattern != NULL) {
4289 	    if (userow) {
4290                 Blt_ChainLink *linkPtr;
4291                 TreeViewColumn *colPtr;
4292 
4293                 Tcl_DStringSetLength(&fStr,0);
4294                 for (linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr);
4295                     linkPtr != NULL;
4296                     linkPtr = Blt_ChainNextLink(linkPtr)) {
4297                     colPtr = Blt_ChainGetValue(linkPtr);
4298                     if (colPtr->hidden && (isvis)) continue;
4299                     if (colPtr == &tvPtr->treeColumn) {
4300                         Tcl_DStringAppend(&fStr, curValue, -1);
4301                         continue;
4302                     }
4303                     if (Blt_TreeGetValue(tvPtr->interp, tvPtr->tree,
4304                         entryPtr->node, colPtr->key, &objPtr) == TCL_OK) {
4305                         Tcl_DStringAppend(&fStr, " ", -1);
4306                         Tcl_DStringAppend(&fStr, Tcl_GetString(objPtr), -1);
4307                     }
4308                 }
4309 
4310                 curValue = Tcl_DStringValue(&fStr);
4311                 result = (*compareProc) (interp, curValue, namePattern, nocase);
4312             } else if (isfull == 0) {
4313                 result = (*compareProc)(interp, curValue, namePattern, nocase);
4314             } else {
4315                 curValue = Blt_TreeViewGetFullName(tvPtr, entryPtr, FALSE, &fullName);
4316                 result = (*compareProc) (interp, curValue, namePattern, nocase);
4317             }
4318             if (result == invertMatch) {
4319                 goto nextEntry;	/* Failed to match */
4320             }
4321         }
4322 dochecks:
4323 	if (withTag != NULL) {
4324 	    result = Blt_TreeHasTag(tvPtr->tree, entryPtr->node, withTag);
4325 	    if (!result) {
4326 		goto nextEntry;	/* Failed to match */
4327 	    }
4328 	}
4329 	if (withoutTag != NULL) {
4330 	    result = Blt_TreeHasTag(tvPtr->tree, entryPtr->node, withoutTag);
4331 	    if (result) {
4332 		goto nextEntry;	/* Failed to match */
4333 	    }
4334 	}
4335         Blt_TreeViewOptsInit(tvPtr);
4336         ocnt = 0;
4337         for (node = Blt_ListFirstNode(options); node != NULL;
4338 	    node = Blt_ListNextNode(node)) {
4339 	    Tcl_Obj *kPtr;
4340 	    ocnt++;
4341 	    kPtr = (Tcl_Obj *)Blt_ListGetKey(node);
4342 	    Tcl_ResetResult(interp);
4343 	    Blt_ConfigureValueFromObj(interp, tvPtr->tkwin,
4344 		bltTreeViewEntrySpecs, (char *)entryPtr, kPtr, 0);
4345             Blt_TreeViewOptsInit(tvPtr);
4346 	    objPtr = Tcl_GetObjResult(interp);
4347 	    result = (*compareProc) (interp, Tcl_GetString(objPtr), kPtr, nocase);
4348            if (result == invertMatch) {
4349                break;
4350            }
4351 	}
4352         if (ocnt && result == invertMatch) {
4353             goto nextEntry;	/* Failed to match */
4354         }
4355 	/*
4356 	 * Someone may actually delete the current node in the "exec"
4357 	 * callback.  Preserve the entry.
4358 	 */
4359 	Tcl_Preserve(entryPtr);
4360 	if (execObj != NULL) {
4361             if (vObj != NULL) {
4362                 Tcl_Obj *intObj;
4363                 intObj = Tcl_NewIntObj(Blt_TreeNodeId(entryPtr->node));
4364                 Tcl_IncrRefCount(intObj);
4365                 if (Tcl_ObjSetVar2(interp, vObj, NULL, intObj, 0) == NULL) {
4366                     Tcl_DecrRefCount(intObj);
4367                     goto error;
4368                 }
4369                 Tcl_DecrRefCount(intObj);
4370                 result = Tcl_EvalObjEx(interp, execObj, 0);
4371             } else {
4372                 Tcl_DString cmdString;
4373 
4374                 Tcl_DStringFree(&cmdString);
4375                 Blt_TreeViewPercentSubst(tvPtr, entryPtr, NULL, Tcl_GetString(execObj), curValue, &cmdString);
4376                 result = Tcl_GlobalEval(interp, Tcl_DStringValue(&cmdString));
4377                 Tcl_DStringFree(&cmdString);
4378             }
4379             if ((entryPtr->flags & ENTRY_DELETED) || (tvPtr->flags & TV_DELETED)) {
4380                 Tcl_Release(entryPtr);
4381                 goto error;
4382             }
4383             Blt_TreeViewOptsInit(tvPtr);
4384             if (result == TCL_CONTINUE) {
4385 		Tcl_Release(entryPtr);
4386 		goto nextEntry;
4387 	    }
4388 	    if (result != TCL_OK) {
4389 		Tcl_Release(entryPtr);
4390 		goto error;
4391 	    }
4392             if (!docount) {
4393                 Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_GetObjResult(interp));
4394             }
4395             goto finishnode;
4396 	}
4397 	if (command != NULL) {
4398              Tcl_Obj **cobjv;
4399              int cobjc, ai;
4400 
4401              iObj = Tcl_NewIntObj(Blt_TreeNodeId(entryPtr->node));
4402              if (Tcl_ListObjReplace(interp, command, cmdLen-1, 1, 1, &iObj) != TCL_OK) {
4403                  Tcl_Release(entryPtr);
4404                  goto error;
4405              }
4406              for (ai = 0; ai < aobjc; ai++) {
4407                  if (Blt_TreeGetValue(NULL, tvPtr->tree, entryPtr->node,
4408                     Tcl_GetString(aobjv[ai]), &iObj) != TCL_OK) {
4409                     iObj = Tcl_NewStringObj("", 0);
4410                  }
4411                  if (Tcl_ListObjReplace(interp, command, cmdLen+ai, 1, 1, &iObj) != TCL_OK) {
4412                      Tcl_Release(entryPtr);
4413                      goto error;
4414                  }
4415              }
4416              if (Tcl_ListObjGetElements(interp, command, &cobjc, &cobjv) != TCL_OK) {
4417                  Tcl_Release(entryPtr);
4418                  goto error;
4419              }
4420              result = Tcl_EvalObjv(interp, cobjc, cobjv, 0);
4421              if ((entryPtr->flags & ENTRY_DELETED) || (tvPtr->flags & TV_DELETED)) {
4422                   result = TCL_ERROR;
4423              } else if (result == TCL_RETURN) {
4424                  int eRes;
4425                  if (Tcl_GetIntFromObj(interp, Tcl_GetObjResult(interp),
4426                      &eRes) != TCL_OK) {
4427                      result = TCL_ERROR;
4428                      goto error;
4429                  }
4430                  result = TCL_OK;
4431                  if (eRes != 0) {
4432                      if (!docount) {
4433                         objPtr = NodeToObj(entryPtr->node);
4434                         Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
4435                      }
4436                      goto finishnode;
4437                  } else {
4438                      goto nextEntry;
4439                  }
4440              }
4441 
4442              if (result != TCL_OK) {
4443                  Tcl_Release(entryPtr);
4444                  goto error;
4445              }
4446              goto finishnode;
4447          }
4448 	/* A NULL node reference in an entry indicates that the entry
4449 	 * was deleted, but its memory not released yet. */
4450 	if (entryPtr->node != NULL) {
4451              if (retLabel) {
4452                  objPtr = Tcl_NewStringObj(Blt_TreeNodeLabel(entryPtr->node), -1);
4453              } else if (retColPtr != NULL) {
4454                  if (Blt_TreeViewGetData(entryPtr, retColPtr->key, &objPtr)
4455                  != TCL_OK) {
4456                      objPtr = Tcl_NewStringObj("", -1);
4457                  }
4458              } else if (retPctPtr != NULL) {
4459                  Tcl_DString cmdString;
4460 
4461                  Tcl_DStringInit(&cmdString);
4462 
4463                  Blt_TreeViewPercentSubst(tvPtr, entryPtr, columnPtr, retPctPtr, curValue, &cmdString);
4464                  objPtr = Tcl_NewStringObj(Tcl_DStringValue(&cmdString), -1);
4465                  Tcl_DStringFree(&cmdString);
4466 
4467              } else {
4468                 /* Finally, save the matching node name. */
4469                 objPtr = NodeToObj(entryPtr->node);
4470             }
4471             if (!docount) {
4472                 Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
4473             }
4474             if (addTag != NULL) {
4475                 if (AddTag(tvPtr, entryPtr->node, addTag) != TCL_OK) {
4476                     Tcl_Release(entryPtr);
4477                     goto error;
4478                 }
4479             }
4480 	}
4481 finishnode:
4482 	Tcl_Release(entryPtr);
4483 	nMatches++;
4484 	if ((nMatches == maxMatches) && (maxMatches > 0)) {
4485 	    break;
4486 	}
4487       nextEntry:
4488 	if (entryPtr == lastPtr) {
4489 	    break;
4490 	}
4491     }
4492     if (command != NULL) {
4493         Tcl_DecrRefCount(command);
4494     }
4495     Tcl_ResetResult(interp);
4496     Blt_ListDestroy(options);
4497     if (docount) {
4498         Tcl_SetObjResult(interp, Tcl_NewIntObj(nMatches));
4499     } else {
4500         Tcl_SetObjResult(interp, listObjPtr);
4501     }
4502     Tcl_DStringFree(&fStr);
4503     Tcl_DStringFree(&dStr);
4504     Tcl_DStringFree(&fullName);
4505     return TCL_OK;
4506 
4507 missingArg:
4508     if (command != NULL) {
4509         Tcl_DecrRefCount(command);
4510     }
4511     Tcl_AppendResult(interp, "missing argument for find option \"",
4512         Tcl_GetString(objv[i]), "\"", (char *)NULL);
4513 error:
4514     if (listObjPtr != NULL) {
4515         Tcl_DecrRefCount(listObjPtr);
4516     }
4517     Tcl_DStringFree(&fStr);
4518     Tcl_DStringFree(&dStr);
4519     Tcl_DStringFree(&fullName);
4520     Blt_ListDestroy(options);
4521     return TCL_ERROR;
4522 }
4523 
4524 
4525 /*
4526  *----------------------------------------------------------------------
4527  *
4528  * GetOp --
4529  *
4530  *	Converts one or more node identifiers to its path component.
4531  *	The path may be either the single entry name or the full path
4532  *	of the entry.
4533  *
4534  * Results:
4535  *	A standard Tcl result.  The interpreter result will contain a
4536  *	list of the convert names.
4537  *
4538  *----------------------------------------------------------------------
4539  */
4540 static int
GetOp(tvPtr,interp,objc,objv)4541 GetOp(tvPtr, interp, objc, objv)
4542     TreeView *tvPtr;
4543     Tcl_Interp *interp;
4544     int objc;
4545     Tcl_Obj *CONST *objv;
4546 {
4547     TreeViewTagInfo info = {0};
4548     TreeViewEntry *entryPtr;
4549     int useFullName;
4550     int useLabels = 0;
4551     register int i;
4552     Tcl_DString dString1, dString2;
4553     int count, single;
4554     char *string;
4555 
4556     useFullName = FALSE;
4557     while (objc > 2) {
4558 
4559 	string = Tcl_GetString(objv[2]);
4560 	if ((string[0] == '-') && (strcmp(string, "-full") == 0)) {
4561 	    useFullName = TRUE;
4562 	    objv++, objc--;
4563 	} else if ((string[0] == '-') && (strcmp(string, "-labels") == 0)) {
4564 	    useFullName = TRUE;
4565 	    useLabels = TRUE;
4566 	    objv++, objc--;
4567 	} else {
4568 	    break;
4569 	}
4570     }
4571     Tcl_DStringInit(&dString1);
4572     Tcl_DStringInit(&dString2);
4573     single = 0;
4574     count = 0;
4575     for (i = 2; i < objc; i++) {
4576 	if (Blt_TreeViewFindTaggedEntries(tvPtr, objv[i], &info) != TCL_OK) {
4577              Tcl_DStringFree(&dString1);
4578              Tcl_DStringFree(&dString2);
4579              return TCL_ERROR;
4580 	}
4581         if (i==2 && objc<=3) {
4582             string = Tcl_GetString(objv[2]);
4583             single = (isdigit(UCHAR(string[0])) && strchr(string,' ')==NULL);
4584         } else {
4585             single = 0;
4586         }
4587 
4588 	for (entryPtr = Blt_TreeViewFirstTaggedEntry(&info); entryPtr != NULL;
4589 	     entryPtr = Blt_TreeViewNextTaggedEntry(&info)) {
4590 	    Tcl_DStringSetLength(&dString2, 0);
4591 	    count++;
4592 	    if (entryPtr->node == NULL) {
4593 		Tcl_DStringAppendElement(&dString1, "");
4594 		continue;
4595 	    }
4596 	    if (useFullName) {
4597 		Blt_TreeViewGetFullName(tvPtr, entryPtr, useLabels, &dString2);
4598 		Tcl_DStringAppendElement(&dString1,
4599 			 Tcl_DStringValue(&dString2));
4600 	    } else {
4601 	        if (single && count == 1) {
4602 		    Tcl_DStringAppend(&dString2,
4603 			 Blt_TreeNodeLabel(entryPtr->node), -1);
4604 		}
4605 		Tcl_DStringAppendElement(&dString1,
4606 			 Blt_TreeNodeLabel(entryPtr->node));
4607 	    }
4608 	}
4609         Blt_TreeViewDoneTaggedEntries(&info);
4610      }
4611     /* This handles the single element list problem. */
4612     if (count == 1 && single) {
4613 	Tcl_DStringResult(interp, &dString2);
4614 	Tcl_DStringFree(&dString1);
4615     } else {
4616 	Tcl_DStringResult(interp, &dString1);
4617 	Tcl_DStringFree(&dString2);
4618     }
4619     return TCL_OK;
4620 }
4621 
4622 /*
4623  *----------------------------------------------------------------------
4624  *
4625  * SearchAndApplyToTree --
4626  *
4627  *	Searches through the current tree and applies a procedure
4628  *	to matching nodes.  The search specification is taken from
4629  *	the following command-line arguments:
4630  *
4631  *      ?-exact? ?-glob? ?-regexp? ?-invert?
4632  *      ?-data string?
4633  *      ?-name string?
4634  *      ?-path?
4635  *      ?-depth N?
4636  *      ?-mindepth N?
4637  *      ?-maxdepth N?
4638  *      ?--?
4639  *      ?inode...?
4640  *
4641  * Results:
4642  *	A standard Tcl result.  If the result is valid, and if the
4643  *      nonmatchPtr is specified, it returns a boolean value
4644  *      indicating whether or not the search was inverted.  This
4645  *      is needed to fix things properly for the "hide invert"
4646  *      case.
4647  *
4648  *----------------------------------------------------------------------
4649  */
4650 
4651 static int
SearchAndApplyToTree(tvPtr,interp,objc,objv,proc,nonMatchPtr)4652 SearchAndApplyToTree(tvPtr, interp, objc, objv, proc, nonMatchPtr)
4653     TreeView *tvPtr;
4654     Tcl_Interp *interp;
4655     int objc;
4656     Tcl_Obj *CONST *objv;
4657     TreeViewApplyProc *proc;
4658     int *nonMatchPtr;		/* returns: inverted search indicator */
4659 {
4660     TreeViewCompareProc *compareProc;
4661     int invertMatch;		/* normal search mode (matching entries) */
4662     Tcl_Obj *namePattern;
4663     register int i;
4664     int length, depth = -1, maxdepth = -1, mindepth = -1, noArgs, usefull = 0;
4665     int result, nocase, uselabel = 0, optInd, ocnt;
4666     char *pattern, *curValue;
4667     Blt_List options;
4668     TreeViewEntry *entryPtr;
4669     register Blt_ListNode node;
4670     char *string, *colStr = NULL, *keysub = NULL;
4671     char *withTag, *withoutTag;
4672     Tcl_Obj *objPtr;
4673     TreeViewTagInfo info = {0};
4674     TreeViewColumn *columnPtr;
4675     Tcl_DString fullName;
4676 
4677     enum optInd {
4678         OP_COLUMN, OP_DEPTH, OP_EXACT, OP_GLOB, OP_INLIST, OP_INVERT,
4679         OP_MAXDEPTH, OP_MINDEPTH, OP_NAME, OP_NOCASE, OP_OPTION,
4680         OP_REGEXP, OP_USELABEL, OP_USEPATH,
4681         OP_WITHTAG, OP_WITHOUTTAG
4682     };
4683     static char *optArr[] = {
4684         "-column", "-depth", "-exact", "-glob", "-inlist", "-invert",
4685         "-maxdepth", "-mindepth", "-name", "-nocase", "-option",
4686         "-regexp", "-uselabel", "-usepath",
4687         "-withtag", "-withouttag",
4688         0
4689     };
4690 
4691 
4692     Tcl_DStringInit(&fullName);
4693 
4694     options = Blt_ListCreate(BLT_ONE_WORD_KEYS);
4695     invertMatch = FALSE;
4696     namePattern = NULL;
4697     compareProc = ExactCompare;
4698     withTag = NULL;
4699     withoutTag = NULL;
4700     columnPtr = NULL;
4701     nocase = 0;
4702 
4703     entryPtr = tvPtr->rootPtr;
4704     for (i = 2; i < objc; i++) {
4705 	string = Tcl_GetStringFromObj(objv[i], &length);
4706 	if (string[0] != '-') {
4707 	    break;
4708 	}
4709          if (length == 2 && string[0] == '-' && string[0] == '-') {
4710              break;
4711          }
4712          if (Tcl_GetIndexFromObj(interp, objv[i], optArr, "option",
4713             0, &optInd) != TCL_OK) {
4714                 return TCL_ERROR;
4715         }
4716         switch (optInd) {
4717         case OP_EXACT:
4718 	    compareProc = ExactCompare;
4719 	    break;
4720         case OP_NOCASE:
4721 	    nocase = 1;
4722 	    break;
4723         case OP_USELABEL:
4724 	    uselabel = 1;
4725 	    break;
4726         case OP_USEPATH:
4727 	    usefull = 1;
4728 	    break;
4729         case OP_REGEXP:
4730 	    compareProc = RegexpCompare;
4731 	    break;
4732         case OP_INLIST:
4733 	    compareProc = InlistCompare;
4734 	    break;
4735         case OP_GLOB:
4736 	    compareProc = GlobCompare;
4737 	    break;
4738         case OP_INVERT:
4739 	    invertMatch = TRUE;
4740 	    break;
4741 
4742 	/* All the rest of these take arguments. */
4743         case OP_COLUMN:
4744 	    if (++i >= objc) { goto missingArg; }
4745 	    if (Blt_TreeViewGetColumnKey(interp, tvPtr, objv[i], &columnPtr, &keysub)
4746 	       != TCL_OK) {
4747                goto error;
4748             }
4749             if (columnPtr == &tvPtr->treeColumn) {
4750                 columnPtr = NULL;
4751             } else if (keysub) {
4752                 colStr = Tcl_GetString(objv[i]);
4753 	    }
4754 	    break;
4755 
4756         case OP_NAME:
4757 	    if (++i >= objc) { goto missingArg; }
4758 	    namePattern = objv[i];
4759 	    break;
4760 
4761         case OP_WITHOUTTAG:
4762 	    if (++i >= objc) { goto missingArg; }
4763 	    withoutTag = Tcl_GetString(objv[i]);
4764 	    break;
4765 
4766         case OP_WITHTAG:
4767 	    if (++i >= objc) { goto missingArg; }
4768              withTag = Tcl_GetString(objv[i]);
4769              break;
4770 
4771         case OP_DEPTH:
4772 	    if (++i >= objc) { goto missingArg; }
4773 	    if (Tcl_GetIntFromObj(interp,objv[i],&depth) != TCL_OK) {
4774                 goto error;
4775 	    }
4776 	    break;
4777         case OP_MAXDEPTH:
4778 	    if (++i >= objc) { goto missingArg; }
4779 	    if (Tcl_GetIntFromObj(interp,objv[i],&maxdepth) != TCL_OK) {
4780 	        return TCL_OK;
4781                 goto error;
4782 	    }
4783 	    break;
4784         case OP_MINDEPTH:
4785 	    if (++i >= objc) { goto missingArg; }
4786 	    if (Tcl_GetIntFromObj(interp,objv[i],&mindepth) != TCL_OK) {
4787                 goto error;
4788 	    }
4789 	    break;
4790 
4791 	case OP_OPTION:
4792 	    /*
4793 	     * Verify that the switch is actually an entry configuration
4794 	     * option.
4795 	     */
4796 	    if ((i + 2) >= objc) {
4797 		goto missingArg;
4798 	    }
4799 	    i++;
4800 	    if (Blt_ConfigureValueFromObj(interp, tvPtr->tkwin,
4801 		  bltTreeViewEntrySpecs, (char *)entryPtr, objv[i], 0)
4802 		!= TCL_OK) {
4803 		Tcl_ResetResult(interp);
4804                 goto error;
4805 	    }
4806 	    /* Save the option in the list of configuration options */
4807 	    node = Blt_ListGetNode(options, (char *)objv[i]);
4808 	    if (node == NULL) {
4809 		node = Blt_ListCreateNode(options, (char *)objv[i]);
4810 		Blt_ListAppendNode(options, node);
4811 	    }
4812 	    i++;
4813 	    Blt_ListSetValue(node, Tcl_GetString(objv[i]));
4814 	}
4815     }
4816 
4817     noArgs = (i >= objc);
4818     if (columnPtr) {
4819         if (!namePattern) {
4820             Tcl_AppendResult(interp, "must use -name with -column", (char *)NULL);
4821             goto error;
4822         }
4823         if (namePattern && usefull) {
4824             Tcl_AppendResult(interp, "can not use -usepath & -column", (char *)NULL);
4825             goto error;
4826         }
4827         if (uselabel) {
4828             Tcl_AppendResult(interp, "can not use -uselabel & -column", (char *)NULL);
4829             goto error;
4830         }
4831     }
4832     if (namePattern && usefull &&  uselabel) {
4833         Tcl_AppendResult(interp, "can not use -uselabel & -usepath", (char *)NULL);
4834         goto error;
4835     }
4836 
4837     for ( ;; i++) {
4838         if (noArgs) {
4839             if (i>objc) break;
4840             info.entryPtr = tvPtr->rootPtr;
4841             info.tagType = TAG_ALL;
4842             info.node = info.entryPtr->node;
4843         } else {
4844             if (i>=objc) break;
4845             if (Blt_TreeViewFindTaggedEntries(tvPtr, objv[i], &info) != TCL_OK) {
4846                 goto error;
4847             }
4848         }
4849         for (entryPtr = Blt_TreeViewFirstTaggedEntry(&info); entryPtr != NULL;
4850             entryPtr = Blt_TreeViewNextTaggedEntry(&info)) {
4851 
4852             if (entryPtr == tvPtr->rootPtr && (info.tagType & TAG_ALL)) {
4853                 entryPtr = Blt_TreeViewNextTaggedEntry(&info);
4854                 if (entryPtr == NULL) break;
4855             }
4856 	    if (depth >=0 && entryPtr->node && entryPtr->node->depth != depth) {
4857 	        continue;
4858 	    }
4859 	    if (maxdepth >=0 && entryPtr->node && entryPtr->node->depth > maxdepth) {
4860 	        continue;
4861 	    }
4862 	    if (mindepth >=0 && entryPtr->node && entryPtr->node->depth < mindepth) {
4863 	        continue;
4864 	    }
4865             if (columnPtr) {
4866                 if (colStr) {
4867                     if (Blt_TreeGetValue(NULL, tvPtr->tree, entryPtr->node, colStr,
4868                     &objPtr) != TCL_OK)
4869                     continue;
4870                 } else if (Blt_TreeGetValueByKey(NULL, tvPtr->tree, entryPtr->node,
4871                     columnPtr->key, &objPtr) != TCL_OK) {
4872                     continue;
4873                 }
4874 
4875                 curValue = Tcl_GetString(objPtr);
4876             } else if (uselabel && entryPtr->labelUid != NULL) {
4877                 curValue = entryPtr->labelUid;
4878             } else {
4879                 curValue = Blt_TreeNodeLabel(entryPtr->node);
4880             }
4881             if (namePattern != NULL) {
4882                 if (usefull == 0) {
4883                     result = (*compareProc) (interp, curValue, namePattern, nocase);
4884                     if (result == invertMatch) {
4885                         continue;	/* Failed to match */
4886                     }
4887                 } else {
4888                     Blt_TreeViewGetFullName(tvPtr, entryPtr, FALSE, &fullName);
4889                     result = (*compareProc) (interp, Tcl_DStringValue(&fullName),
4890                         namePattern, nocase);
4891                     if (result == invertMatch) {
4892                         continue;	/* Failed to match */
4893                     }
4894                 }
4895 	    }
4896 	    if (withTag != NULL) {
4897 		result = Blt_TreeHasTag(tvPtr->tree, entryPtr->node, withTag);
4898 		if (!result) {
4899 		    continue;	/* Failed to match */
4900 		}
4901 	    }
4902 	    if (withoutTag != NULL) {
4903 		result = Blt_TreeHasTag(tvPtr->tree, entryPtr->node, withoutTag);
4904 		if (result) {
4905 		    continue;	/* Failed to match */
4906 		}
4907 	    }
4908 	    ocnt = 0;
4909 	    for (node = Blt_ListFirstNode(options); node != NULL;
4910 		node = Blt_ListNextNode(node)) {
4911 		Tcl_Obj *kPtr;
4912 		ocnt++;
4913 		kPtr = (Tcl_Obj *)Blt_ListGetKey(node);
4914 		Tcl_ResetResult(interp);
4915                 Blt_TreeViewOptsInit(tvPtr);
4916                 if (Blt_ConfigureValueFromObj(interp, tvPtr->tkwin,
4917 			bltTreeViewEntrySpecs, (char *)entryPtr, kPtr, 0)
4918 		    != TCL_OK) {
4919 		    goto error;	/* This shouldn't happen. */
4920 		}
4921 		pattern = Blt_ListGetValue(node);
4922 		objPtr = Tcl_GetObjResult(interp);
4923 		result = (*compareProc)(interp, Tcl_GetString(objPtr), kPtr, nocase);
4924 		if (result == invertMatch) {
4925 		    break;	/* Failed to match */
4926 		}
4927 	    }
4928             /* if (result == invertMatch) {
4929                 continue;
4930             }*/
4931             /* Finally, apply the procedure to the node */
4932 	    (*proc) (tvPtr, entryPtr);
4933 	}
4934         Blt_TreeViewDoneTaggedEntries(&info);
4935 	Tcl_ResetResult(interp);
4936     }
4937     if (nonMatchPtr != NULL) {
4938 	*nonMatchPtr = invertMatch;	/* return "inverted search" status */
4939     }
4940     Blt_ListDestroy(options);
4941     Tcl_DStringFree(&fullName);
4942     return TCL_OK;
4943 
4944 missingArg:
4945     Tcl_AppendResult(interp, "missing pattern for search option \"",
4946         Tcl_GetString(objv[i]), "\"", (char *)NULL);
4947 error:
4948     Blt_TreeViewDoneTaggedEntries(&info);
4949     Tcl_DStringFree(&fullName);
4950     Blt_ListDestroy(options);
4951     return TCL_ERROR;
4952 
4953 }
4954 
4955 static int
FixSelectionsApplyProc(tvPtr,entryPtr)4956 FixSelectionsApplyProc(tvPtr, entryPtr)
4957     TreeView *tvPtr;
4958     TreeViewEntry *entryPtr;
4959 {
4960     if (entryPtr->flags & ENTRY_HIDDEN) {
4961 	Blt_TreeViewDeselectEntry(tvPtr, entryPtr, NULL);
4962 	if ((tvPtr->focusPtr != NULL) &&
4963 	    (Blt_TreeIsAncestor(entryPtr->node, tvPtr->focusPtr->node))) {
4964 	    if (entryPtr != tvPtr->rootPtr) {
4965 		entryPtr = Blt_TreeViewParentEntry(entryPtr);
4966 		tvPtr->focusPtr = (entryPtr == NULL)
4967 		    ? tvPtr->focusPtr : entryPtr;
4968 		Blt_SetFocusItem(tvPtr->bindTable, tvPtr->focusPtr, ITEM_ENTRY);
4969 	    }
4970 	}
4971 	if ((tvPtr->selAnchorPtr != NULL) &&
4972 	    (Blt_TreeIsAncestor(entryPtr->node, tvPtr->selAnchorPtr->node))) {
4973 	    tvPtr->selMarkPtr = tvPtr->selAnchorPtr = NULL;
4974 	}
4975 	if ((tvPtr->activePtr != NULL) &&
4976 	    (Blt_TreeIsAncestor(entryPtr->node, tvPtr->activePtr->node))) {
4977 	    tvPtr->activePtr = NULL;
4978 	}
4979 	Blt_TreeViewPruneSelection(tvPtr, entryPtr);
4980     }
4981     return TCL_OK;
4982 }
4983 
4984 /*
4985  *----------------------------------------------------------------------
4986  *
4987  * HideOp --
4988  *
4989  *	Hides one or more nodes.  Nodes can be specified by their
4990  *      inode, or by matching a name or data value pattern.  By
4991  *      default, the patterns are matched exactly.  They can also
4992  *      be matched using glob-style and regular expression rules.
4993  *
4994  * Results:
4995  *	A standard Tcl result.
4996  *
4997  *----------------------------------------------------------------------
4998  */
4999 static int
HideOp(tvPtr,interp,objc,objv)5000 HideOp(tvPtr, interp, objc, objv)
5001     TreeView *tvPtr;
5002     Tcl_Interp *interp;
5003     int objc;
5004     Tcl_Obj *CONST *objv;
5005 {
5006     int status, nonmatching;
5007 
5008     status = SearchAndApplyToTree(tvPtr, interp, objc, objv,
5009 	HideEntryApplyProc, &nonmatching);
5010 
5011     if (status != TCL_OK) {
5012 	return TCL_ERROR;
5013     }
5014     /*
5015      * If this was an inverted search, scan back through the
5016      * tree and make sure that the parents for all visible
5017      * nodes are also visible.  After all, if a node is supposed
5018      * to be visible, its parent can't be hidden.
5019      */
5020     if (nonmatching) {
5021 	Blt_TreeViewApply(tvPtr, tvPtr->rootPtr, MapAncestorsApplyProc, 0);
5022     }
5023     /*
5024      * Make sure that selections are cleared from any hidden
5025      * nodes.  This wasn't done earlier--we had to delay it until
5026      * we fixed the visibility status for the parents.
5027      */
5028     Blt_TreeViewApply(tvPtr, tvPtr->rootPtr, FixSelectionsApplyProc, 0);
5029 
5030     /* Hiding an entry only effects the visible nodes. */
5031     tvPtr->flags |= (TV_LAYOUT | TV_SCROLL | TV_UPDATE | TV_RESORT);
5032     Blt_TreeViewEventuallyRedraw(tvPtr);
5033     return TCL_OK;
5034 }
5035 
5036 /*
5037  *----------------------------------------------------------------------
5038  *
5039  * ShowOp --
5040  *
5041  *	Mark one or more nodes to be exposed.  Nodes can be specified
5042  *	by their inode, or by matching a name or data value pattern.  By
5043  *      default, the patterns are matched exactly.  They can also
5044  *      be matched using glob-style and regular expression rules.
5045  *
5046  * Results:
5047  *	A standard Tcl result.
5048  *
5049  *----------------------------------------------------------------------
5050  */
5051 static int
ShowOp(tvPtr,interp,objc,objv)5052 ShowOp(tvPtr, interp, objc, objv)
5053     TreeView *tvPtr;
5054     Tcl_Interp *interp;
5055     int objc;
5056     Tcl_Obj *CONST *objv;
5057 {
5058     if (SearchAndApplyToTree(tvPtr, interp, objc, objv, ShowEntryApplyProc,
5059 	    (int *)NULL) != TCL_OK) {
5060 	return TCL_ERROR;
5061     }
5062     tvPtr->flags |= (TV_LAYOUT | TV_SCROLL | TV_UPDATE|TV_RESORT);
5063     Blt_TreeViewEventuallyRedraw(tvPtr);
5064     return TCL_OK;
5065 }
5066 
5067 /*
5068  *----------------------------------------------------------------------
5069  *
5070  * IndexOp --
5071  *
5072  *	Converts one of more words representing indices of the entries
5073  *	in the treeview widget to their respective serial identifiers.
5074  *
5075  * Results:
5076  *	A standard Tcl result.  Interp->result will contain the
5077  *	identifier of each inode found. If an inode could not be found,
5078  *	then the serial identifier will be the empty string.
5079  *
5080  *----------------------------------------------------------------------
5081  */
5082 /*ARGSUSED*/
5083 static int
IndexOp(tvPtr,interp,objc,objv)5084 IndexOp(tvPtr, interp, objc, objv)
5085     TreeView *tvPtr;
5086     Tcl_Interp *interp;
5087     int objc;			/* Not used. */
5088     Tcl_Obj *CONST *objv;
5089 {
5090     TreeViewEntry *entryPtr;
5091     char *string;
5092     TreeViewEntry *fromPtr;
5093     int usePath, quiet;
5094 
5095     quiet = FALSE;
5096     usePath = FALSE;
5097     fromPtr = NULL;
5098     string = Tcl_GetString(objv[2]);
5099     while (string[0] == '-' && objc>3) {
5100         if ((strcmp(string, "-path") == 0)) {
5101     	   usePath = TRUE;
5102     	   objv++, objc--;
5103         } else if ((strcmp(string, "-quiet") == 0)) {
5104     	   quiet = TRUE;
5105     	   objv++, objc--;
5106         } else if ((strcmp(string, "-at") == 0)) {
5107     	   if (Blt_TreeViewGetEntry(tvPtr, objv[3], &fromPtr) != TCL_OK) {
5108     	      return TCL_ERROR;
5109     	   }
5110     	   objv += 2, objc -= 2;
5111         } else {
5112             break;
5113         }
5114         string = Tcl_GetString(objv[2]);
5115     }
5116     if (objc != 3) {
5117     	Tcl_AppendResult(interp, "wrong # args: should be \"",
5118     		Tcl_GetString(objv[0]),
5119     		" ?-at tagOrId? ?-path? ?-quiet? index\"",
5120     		(char *)NULL);
5121     	return TCL_ERROR;
5122     }
5123     tvPtr->fromPtr = fromPtr;
5124     if (usePath) {
5125 	if (fromPtr == NULL) {
5126 	    fromPtr = tvPtr->rootPtr;
5127 	}
5128 	string = Tcl_GetString(objv[2]);
5129 	entryPtr = FindPath(tvPtr, fromPtr, string);
5130 	if (entryPtr != NULL) {
5131 	    Tcl_SetObjResult(interp, NodeToObj(entryPtr->node));
5132 	} else if (quiet) {
5133              Tcl_ResetResult(interp);
5134              return TCL_OK;
5135          } else {
5136 	    return TCL_ERROR;
5137 	}
5138     } else {
5139         if (tvPtr->fromPtr == NULL) {
5140             tvPtr->fromPtr = tvPtr->focusPtr;
5141         }
5142         if (tvPtr->fromPtr == NULL) {
5143             tvPtr->fromPtr = tvPtr->rootPtr;
5144         }
5145         if ((GetEntryFromObj2(tvPtr, objv[2], &entryPtr) == TCL_OK)) {
5146              if (entryPtr != NULL && entryPtr->node != NULL) {
5147                  Tcl_SetObjResult(interp, NodeToObj(entryPtr->node));
5148              }
5149 	} else if (quiet) {
5150              Tcl_ResetResult(interp);
5151              return TCL_OK;
5152 	} else {
5153 	    return TCL_ERROR;
5154 	}
5155     }
5156     return TCL_OK;
5157 }
5158 
5159 /*
5160  *----------------------------------------------------------------------
5161  *
5162  * InsertOp --
5163  *
5164  *	Add new entries into a hierarchy.  If no node is specified,
5165  *	new entries will be added to the root of the hierarchy.
5166  *
5167  *----------------------------------------------------------------------
5168  */
5169 static int
InsertOp(tvPtr,interp,objc,objv)5170 InsertOp(tvPtr, interp, objc, objv)
5171     TreeView *tvPtr;
5172     Tcl_Interp *interp;
5173     int objc;
5174     Tcl_Obj *CONST *objv;
5175 {
5176     Blt_TreeNode node, parent;
5177     int insertPos;
5178     int depth, count;
5179     char *path;
5180     Tcl_Obj *CONST *options;
5181     Tcl_Obj *listObjPtr;
5182     char **compArr = NULL;
5183     register char **p;
5184     register int n;
5185     TreeViewEntry *rootPtr;
5186     char *string, *subPath;
5187     int nLen, idx, useid, oLen;
5188     int sobjc, tobjc;
5189     Tcl_Obj *CONST *sobjv;
5190     Tcl_Obj *CONST *tobjv;
5191     TreeViewStyle *stylePtr;
5192     TreeViewColumn *columnPtr;
5193     TreeViewEntry *entryPtr;
5194     Tcl_DString dStr;
5195     int optSkips, i, m, start, nOptions, inode;
5196 
5197     useid = -1;
5198     inode = -1;
5199     optSkips = 0;
5200     sobjc = 0;
5201     tobjc = 0;
5202     entryPtr = NULL;
5203     rootPtr = tvPtr->rootPtr;
5204 
5205     if (objc == 2) {
5206 	Tcl_AppendResult(interp, "missing position argument", (char *)NULL);
5207 	return TCL_ERROR;
5208     }
5209     if (Blt_GetPositionFromObj(interp, objv[2], &insertPos) != TCL_OK) {
5210 	return TCL_ERROR;
5211     }
5212     for (i = 4; i < objc; i++) {
5213         char *cp = Tcl_GetString(objv[i]);
5214         if (cp[0] == '-') break;
5215     }
5216     start = i;
5217     nOptions = (objc<4?0:objc - i);
5218     options = objv + start;
5219 
5220     if (nOptions%2) {
5221         Tcl_AppendResult(interp, "odd number of options", 0);
5222         return TCL_ERROR;
5223     }
5224     for (count = start; count < objc; count += 2) {
5225         string = Tcl_GetString(objv[count]);
5226         if (string[0] != '-') {
5227             Tcl_AppendResult(interp, "option must start with a dash: ", string, 0);
5228             return TCL_ERROR;
5229         }
5230         if (!strcmp("-tags", string)) {
5231             if (Tcl_ListObjGetElements(interp, objv[count+1], &tobjc, &tobjv) != TCL_OK) {
5232                 return TCL_ERROR;
5233             }
5234 
5235         } else if (!strcmp("-styles", string)) {
5236             if (Tcl_ListObjGetElements(interp, objv[count+1], &sobjc, &sobjv) != TCL_OK) {
5237                 return TCL_ERROR;
5238             }
5239             if (sobjc%2) {
5240                 Tcl_AppendResult(interp, "odd arguments for \"-styles\" flag", 0);
5241                 return TCL_ERROR;
5242             }
5243             for (n=0; n<sobjc; n += 2) {
5244                 if (Blt_TreeViewGetColumn(interp, tvPtr, sobjv[n], &columnPtr) != TCL_OK) {
5245                     return TCL_ERROR;
5246                 }
5247                 if (columnPtr == &tvPtr->treeColumn) {
5248                     Tcl_AppendResult(interp, "tree column invalid in \"-styles\" flag", 0);
5249                     return TCL_ERROR;
5250                 }
5251                 if (Blt_TreeViewGetStyleMake(interp, tvPtr,
5252                     Tcl_GetString(sobjv[n+1]), &stylePtr, columnPtr, NULL,
5253                     NULL) != TCL_OK) {
5254                     return TCL_ERROR;
5255                 }
5256                 stylePtr->refCount--;
5257             }
5258 
5259         } else if (!strcmp("-at", string)) {
5260             if (Blt_TreeViewGetEntry(tvPtr, objv[count+1], &rootPtr) != TCL_OK) {
5261                 return TCL_ERROR;
5262             }
5263 
5264         } else if (!strcmp("-node", string)) {
5265             if (Tcl_GetIntFromObj(tvPtr->interp, objv[count+1], &useid) != TCL_OK) {
5266                 return TCL_ERROR;
5267             }
5268         } else {
5269             continue;
5270         }
5271         optSkips++;
5272     }
5273     Tcl_DStringInit(&dStr);
5274     node = NULL;
5275     listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
5276     m = 2;
5277     if (objc == 3) {
5278         path = "#auto";
5279         options = NULL;
5280         oLen = 5;
5281         goto addpath;
5282     }
5283     while (++m < start && m<objc) {
5284 	path = Tcl_GetStringFromObj(objv[m], &oLen);
5285 
5286         if (oLen == 0) {
5287             path = "#auto";
5288             oLen = 5;
5289         }
5290 addpath:
5291         node = NULL;
5292         inode = -1;
5293 	if (oLen == 5
5294             && (tvPtr->pathSep == SEPARATOR_NONE || tvPtr->pathSep == NULL)
5295             && path[0] == '#' && strcmp(path, "#auto") == 0) {
5296 
5297 	    parent = rootPtr->node;
5298 	    if (useid>0) {
5299                 node = Blt_TreeCreateNodeWithId(tvPtr->tree, parent,
5300                      Blt_Itoa(useid), useid, insertPos);
5301                 if (node != NULL) {
5302                     goto makeent;
5303                 }
5304                 Tcl_AppendResult(interp, "node already exists", 0);
5305                 goto error;
5306             }
5307             node = Blt_TreeCreateNode(tvPtr->tree, parent, NULL,
5308                     insertPos);
5309             if (node == NULL) {
5310                 Tcl_AppendResult(interp, "node create failed", 0);
5311                 goto error;
5312             }
5313             if ((tvPtr->flags & TV_ALLOW_DUPLICATES) != 0) {
5314                 if (Blt_TreeRelabelNode2(node, Blt_Itoa(Blt_TreeNodeId(node))) != TCL_OK) {
5315                     goto error;
5316                 }
5317                 goto makeent;
5318             }
5319             if (Blt_TreeFindChildRev(parent, Blt_Itoa(Blt_TreeNodeId(node)),
5320                     tvPtr->insertFirst) == NULL) {
5321                 if (Blt_TreeRelabelNode2(node, Blt_Itoa(Blt_TreeNodeId(node))) != TCL_OK) {
5322                     goto error;
5323                 }
5324                 goto makeent;
5325             }
5326             /* Should never happen. */
5327             idx = (parent == tvPtr->rootNode ? tvPtr->nextIdx : tvPtr->nextSubIdx);
5328             for (;;) {
5329                 idx++;
5330                 if (Blt_TreeFindChildRev(parent, Blt_Itoa(idx),
5331                     tvPtr->insertFirst) == NULL) {
5332                     if (Blt_TreeRelabelNode2(node, Blt_Itoa(idx)) != TCL_OK) {
5333                         goto error;
5334                     }
5335                     goto makeent;
5336                 }
5337             }
5338 	}
5339 	if (tvPtr->trimLeft != NULL) {
5340 	    register char *s1, *s2;
5341 
5342 	    /* Trim off leading character string if one exists. */
5343 	    for (s1 = path, s2 = tvPtr->trimLeft; *s2 != '\0'; s2++, s1++) {
5344 		if (*s1 != *s2) {
5345 		    break;
5346 		}
5347 	    }
5348 	    if (*s2 == '\0') {
5349 		path = s1;
5350 	    }
5351 	}
5352 	/*
5353 	 * Split the path and find the parent node of the path.
5354 	 */
5355 	compArr = &path;
5356 	depth = 1;
5357 	if (tvPtr->pathSep != SEPARATOR_NONE) {
5358 	    if (SplitPath(tvPtr, path, &depth, &compArr) != TCL_OK) {
5359 		goto error;
5360 	    }
5361 	    if (depth == 0 && compArr != &path) {
5362 		if (compArr) { Blt_Free(compArr); }
5363 		compArr = NULL;
5364 		continue;		/* Root already exists. */
5365 	    }
5366 	}
5367 	parent = rootPtr->node;
5368 	depth--;
5369 
5370 	/* Verify each component in the path preceding the tail.  */
5371 	for (n = 0, p = compArr; n < depth; n++, p++) {
5372             node = Blt_TreeFindChildRev(parent, *p, tvPtr->insertFirst);
5373 	    if (node == NULL) {
5374 		if ((tvPtr->flags & TV_FILL_ANCESTORS) == 0) {
5375 		    Tcl_AppendResult(interp, "can't find path component \"",
5376 		         *p, "\" in \"", path, "\"", (char *)NULL);
5377 		    goto error;
5378 		}
5379 		node = Blt_TreeCreateNode(tvPtr->tree, parent, *p, END);
5380 		if (node == NULL) {
5381 		    goto error;
5382 		}
5383 	    }
5384 	    parent = node;
5385 	}
5386 	node = NULL;
5387 	subPath = *p;
5388         nLen = strlen(subPath);
5389         if (nLen>=5 && (tvPtr->flags & TV_ALLOW_DUPLICATES) == 0 && strcmp(subPath+nLen-5,"#auto") == 0) {
5390 
5391              nLen -= 5;
5392              idx = (parent == tvPtr->rootNode ? tvPtr->nextIdx : tvPtr->nextSubIdx);
5393              for (;;) {
5394                  Tcl_DStringSetLength(&dStr, 0);
5395                  Tcl_DStringAppend(&dStr, *p, nLen);
5396                  Tcl_DStringAppend(&dStr, Blt_Itoa(idx), -1);
5397                  idx++;
5398                  node = Blt_TreeFindChildRev(parent, Tcl_DStringValue(&dStr),
5399                     tvPtr->insertFirst);
5400                  if (node == NULL) break;
5401              }
5402              if (parent == tvPtr->rootNode) {
5403                  tvPtr->nextIdx = idx;
5404              }
5405              subPath = Tcl_DStringValue(&dStr);
5406          } else if (((tvPtr->flags & TV_ALLOW_DUPLICATES) == 0) &&
5407 	    (Blt_TreeFindChildRev(parent, subPath, tvPtr->insertFirst) != NULL)) {
5408 	    Tcl_AppendResult(interp, "entry \"", subPath, "\" already exists in \"",
5409 		 path, "\"", (char *)NULL);
5410 	    goto error;
5411 	}
5412          if (useid<=0) {
5413              node = Blt_TreeCreateNode(tvPtr->tree, parent, subPath, insertPos);
5414          } else {
5415              node = Blt_TreeCreateNodeWithId(tvPtr->tree, parent, subPath, useid, insertPos);
5416              useid++;
5417          }
5418 	if (node == NULL) {
5419 	    Tcl_AppendResult(interp, "failed to create node: ", subPath, 0);
5420 	    goto error;
5421 	}
5422 makeent:
5423         inode = node->inode;
5424         if (Blt_TreeViewCreateEntry(tvPtr, node, 0, options, BLT_CONFIG_OBJV_ONLY) != TCL_OK) {
5425             goto opterror;
5426         }
5427         entryPtr = Blt_NodeToEntry(tvPtr, node);
5428         Tcl_Preserve(entryPtr);
5429         for (i = 0; i<nOptions; i+=2) {
5430             char *str;
5431             int cRes;
5432 
5433             str = Tcl_GetString(options[i]);
5434             if (strcmp("-tags",str) && strcmp("-styles",str) && strcmp("-at",str) && strcmp("-node",str)) {
5435                 cRes = Blt_TreeViewConfigureEntry(tvPtr, entryPtr, 2, options+i, 0);
5436                 if ((entryPtr->flags & ENTRY_DELETED) || (tvPtr->flags & TV_DELETED)) {
5437                     Tcl_Release(entryPtr);
5438                     node = NULL;
5439                     goto error;
5440                 }
5441 
5442                 if (cRes != TCL_OK) {
5443                     Tcl_Release(entryPtr);
5444                     Blt_TreeViewFreeEntry(tvPtr, entryPtr);
5445                     goto error;
5446                 }
5447             }
5448         }
5449         if (Blt_TreeInsertPost(tvPtr->tree, node) == NULL) {
5450             Tcl_Release(entryPtr);
5451             goto error;
5452         }
5453         if ((entryPtr->flags & ENTRY_DELETED) || (tvPtr->flags & TV_DELETED)) {
5454             Tcl_Release(entryPtr);
5455             node = NULL;
5456             goto error;
5457         }
5458         if (compArr != &path && compArr) {
5459 	    Blt_Free(compArr);
5460 	    compArr = NULL;
5461 	}
5462 	Tcl_ListObjAppendElement(interp, listObjPtr, NodeToObj(node));
5463         for (n=0; n<sobjc; n += 2) {
5464             TreeViewValue *valuePtr;
5465 
5466             if (entryPtr == NULL) {
5467                 entryPtr = Blt_NodeToEntry(tvPtr, node);
5468             }
5469 
5470             Blt_TreeViewGetColumn(interp, tvPtr, sobjv[n], &columnPtr);
5471             for (valuePtr = entryPtr->values; valuePtr != NULL;
5472                 valuePtr = valuePtr->nextPtr) {
5473                 if (valuePtr->columnPtr == columnPtr) {
5474                     if (Blt_TreeViewGetStyle(interp, tvPtr, Tcl_GetString(sobjv[n+1]), &stylePtr) == TCL_OK) {
5475                         stylePtr->refCount++;
5476                         valuePtr->stylePtr = stylePtr;
5477                     }
5478                     break;
5479                 }
5480             }
5481         }
5482         for (n=0; n<tobjc; n++) {
5483             int tRes;
5484 
5485             tRes = AddTag(tvPtr, node, Tcl_GetString(tobjv[n]));
5486             if ((entryPtr->flags & ENTRY_DELETED) || (tvPtr->flags & TV_DELETED)) {
5487                 Tcl_Release(entryPtr);
5488                 node = NULL;
5489                 goto error;
5490             }
5491             if (tRes != TCL_OK) {
5492                 Tcl_Release(entryPtr);
5493                 goto error;
5494             }
5495         }
5496         Tcl_Release(entryPtr);
5497     }
5498     tvPtr->flags |= (TV_LAYOUT | TV_SCROLL | TV_DIRTY | TV_RESORT);
5499     if (compArr && compArr != &path) {
5500         Blt_Free(compArr);
5501     }
5502     Blt_TreeViewEventuallyRedraw(tvPtr);
5503     Tcl_SetObjResult(interp, listObjPtr);
5504     Tcl_DStringFree(&dStr);
5505     return TCL_OK;
5506 
5507   opterror:
5508     if (strncmp(Tcl_GetStringResult(interp), "unknown option", 14) == 0) {
5509         Tcl_AppendResult(interp, "\nshould be one of one of the insert options: -at, -isopen, -node, -styles, -tags\n or one of the entry options:  ", 0);
5510         Blt_FormatSpecOptions(interp, bltTreeViewEntrySpecs);
5511     }
5512   error:
5513     Tcl_DStringFree(&dStr);
5514     if (compArr && compArr != &path) {
5515 	Blt_Free(compArr);
5516     }
5517     Tcl_DecrRefCount(listObjPtr);
5518     /* if (node != NULL && inode > = 0 && tvPtr->tree) {
5519         node = Blt_TreeGetNode(tvPtr->tree, inode)
5520     }*/
5521     if (node != NULL) {
5522 	DeleteNode(tvPtr, node);
5523     }
5524     return TCL_ERROR;
5525 }
5526 
5527 #ifdef notdef
5528 /*
5529  *----------------------------------------------------------------------
5530  *
5531  * AddOp -- UNUSED.
5532  *
5533  *	Add new entries into a hierarchy.  If no node is specified,
5534  *	new entries will be added to the root of the hierarchy.
5535  *
5536  *----------------------------------------------------------------------
5537  */
5538 
5539 static Blt_SwitchParseProc StringToChild;
5540 #define INSERT_BEFORE	(ClientData)0
5541 #define INSERT_AFTER	(ClientData)1
5542 static Blt_SwitchCustom beforeSwitch =
5543 {
5544     StringToChild, (Blt_SwitchFreeProc *)NULL, INSERT_BEFORE,
5545 };
5546 static Blt_SwitchCustom afterSwitch =
5547 {
5548     StringToChild, (Blt_SwitchFreeProc *)NULL, INSERT_AFTER,
5549 };
5550 
5551 typedef struct {
5552     int insertPos;
5553     Blt_TreeNode parent;
5554 } InsertData;
5555 
5556 static Blt_SwitchSpec insertSwitches[] =
5557 {
5558     {BLT_SWITCH_CUSTOM, "-after", Blt_Offset(InsertData, insertPos), 0,
5559 	&afterSwitch},
5560     {BLT_SWITCH_INT_NONNEGATIVE, "-at", Blt_Offset(InsertData, insertPos), 0},
5561     {BLT_SWITCH_CUSTOM, "-before", Blt_Offset(InsertData, insertPos), 0,
5562 	&beforeSwitch},
5563     {BLT_SWITCH_END, NULL, 0, 0}
5564 };
5565 
5566 static int
AddOp(tvPtr,interp,objc,objv)5567 AddOp(tvPtr, interp, objc, objv)
5568     TreeView *tvPtr;
5569     Tcl_Interp *interp;
5570     int objc;
5571     Tcl_Obj *CONST *objv;
5572 {
5573     Blt_TreeNode node, parent;
5574     int insertPos;
5575     int depth, count;
5576     char *path;
5577     Tcl_Obj *CONST *options;
5578     Tcl_Obj *listObjPtr;
5579     char **compArr;
5580     register char **p;
5581     register int n;
5582     TreeViewEntry *rootPtr;
5583     char *string;
5584 
5585     memset(&data, 0, sizeof(data));
5586     data.maxDepth = -1;
5587     data.cmdPtr = cmdPtr;
5588 
5589     /* Process any leading switches  */
5590     i = Blt_ProcessObjSwitches(interp, addSwitches, objc - 2, objv + 2,
5591 	     (char *)&data, BLT_CONFIG_OBJV_PARTIAL);
5592     if (i < 0) {
5593 	return TCL_ERROR;
5594     }
5595     i += 2;
5596     /* Should have at the starting node */
5597     if (i >= objc) {
5598 	Tcl_AppendResult(interp, "starting node argument is missing",
5599 		(char *)NULL);
5600 	return TCL_ERROR;
5601     }
5602     if (Blt_TreeViewGetEntry(tvPtr, objv[i], &rootPtr) != TCL_OK) {
5603 	return TCL_ERROR;
5604     }
5605     objv += i, objc -= i;
5606     node = NULL;
5607 
5608     /* Process sections of path ?options? */
5609     listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
5610     while (objc > 0) {
5611 	path = Tcl_GetString(objv[0]);
5612 	objv++, objc--;
5613 	/*
5614 	 * Count the option-value pairs that follow.  Count until we
5615 	 * spot one that looks like an entry name (i.e. doesn't start
5616 	 * with a minus "-").
5617 	 */
5618 	for (count = 0; count < objc; count += 2) {
5619 	    if (!Blt_ObjIsOption(interp, bltTreeViewEntrySpecs, objv[count], 0)) {
5620 		break;
5621 	    }
5622 	}
5623 	if (count > objc) {
5624 	    count = objc;
5625 	}
5626 	options = objv;
5627 	objc -= count, objv += count;
5628 
5629 	if (tvPtr->trimLeft != NULL) {
5630 	    register char *s1, *s2;
5631 
5632 	    /* Trim off leading character string if one exists. */
5633 	    for (s1 = path, s2 = tvPtr->trimLeft; *s2 != '\0'; s2++, s1++) {
5634 		if (*s1 != *s2) {
5635 		    break;
5636 		}
5637 	    }
5638 	    if (*s2 == '\0') {
5639 		path = s1;
5640 	    }
5641 	}
5642 	/*
5643 	 * Split the path and find the parent node of the path.
5644 	 */
5645 	compArr = &path;
5646 	depth = 1;
5647 	if (tvPtr->pathSep != SEPARATOR_NONE) {
5648 	    if (SplitPath(tvPtr, path, &depth, &compArr) != TCL_OK) {
5649 		goto error;
5650 	    }
5651 	    if (depth == 0) {
5652 		Blt_Free(compArr);
5653 		continue;		/* Root already exists. */
5654 	    }
5655 	}
5656 	parent = rootPtr->node;
5657 	depth--;
5658 
5659 	/* Verify each component in the path preceding the tail.  */
5660 	for (n = 0, p = compArr; n < depth; n++, p++) {
5661 	    node = Blt_TreeFindChildRev(parent, *p, tvPtr->insertFirst);
5662 	    if (node == NULL) {
5663 		if ((tvPtr->flags & TV_FILL_ANCESTORS) == 0) {
5664 		    Tcl_AppendResult(interp, "can't find path component \"",
5665 		         *p, "\" in \"", path, "\"", (char *)NULL);
5666 		    goto error;
5667 		}
5668 		node = Blt_TreeCreateNode(tvPtr->tree, parent, *p, END);
5669 		if (node == NULL) {
5670 		    goto error;
5671 		}
5672 	    }
5673 	    parent = node;
5674 	}
5675 	node = NULL;
5676 	if (((tvPtr->flags & ) == 0) &&
5677 	    (Blt_TreeFindChildRev(parent, *p, tvPtr->insertFirst) != NULL)) {
5678 	    Tcl_AppendResult(interp, "entry \"", *p, "\" already exists in \"",
5679 		 path, "\"", (char *)NULL);
5680 	    goto error;
5681 	}
5682 	node = Blt_TreeCreateNode(tvPtr->tree, parent, *p, insertPos);
5683 	if (node == NULL) {
5684 	    goto error;
5685 	}
5686 	if (Blt_TreeViewCreateEntry(tvPtr, node, count, options, 0) != TCL_OK) {
5687 	    goto error;
5688 	}
5689 	if (compArr != &path) {
5690 	    Blt_Free(compArr);
5691 	}
5692 	Tcl_ListObjAppendElement(interp, listObjPtr, NodeToObj(node));
5693     }
5694     tvPtr->flags |= (TV_LAYOUT | TV_SCROLL | TV_DIRTY | TV_RESORT);
5695     Blt_TreeViewEventuallyRedraw(tvPtr);
5696     Tcl_SetObjResult(interp, listObjPtr);
5697     return TCL_OK;
5698 
5699   error:
5700     if (compArr != &path) {
5701 	Blt_Free(compArr);
5702     }
5703     Tcl_DecrRefCount(listObjPtr);
5704     if (node != NULL) {
5705 	DeleteNode(tvPtr, node);
5706     }
5707     return TCL_ERROR;
5708 }
5709 #endif
5710 
5711 /*
5712  *----------------------------------------------------------------------
5713  *
5714  * DeleteOp --
5715  *
5716  *	Deletes nodes from the hierarchy. Deletes one or more entries
5717  *	(except root). In all cases, nodes are removed recursively.
5718  *
5719  *	Note: There's no need to explicitly clean up Entry structures
5720  *	      or request a redraw of the widget. When a node is
5721  *	      deleted in the tree, all of the Tcl_Objs representing
5722  *	      the various data fields are also removed.  The treeview
5723  *	      widget store the Entry structure in a data field. So it's
5724  *	      automatically cleaned up when FreeEntryInternalRep is
5725  *	      called.
5726  *
5727  *----------------------------------------------------------------------
5728  */
5729 /*ARGSUSED*/
5730 static int
DeleteOp(tvPtr,interp,objc,objv)5731 DeleteOp(tvPtr, interp, objc, objv)
5732     TreeView *tvPtr;
5733     Tcl_Interp *interp;
5734     int objc;			/* Not used. */
5735     Tcl_Obj *CONST *objv;
5736 {
5737     TreeViewTagInfo info = {0};
5738     TreeViewEntry *entryPtr;
5739     register int i;
5740 
5741     for (i = 2; i < objc; i++) {
5742 	if (Blt_TreeViewFindTaggedEntries(tvPtr, objv[i], &info) != TCL_OK) {
5743 	    return TCL_ERROR;
5744 	}
5745 	for (entryPtr = Blt_TreeViewFirstTaggedEntry(&info); entryPtr != NULL;
5746 	     entryPtr = Blt_TreeViewNextTaggedEntry(&info)) {
5747 	    if (entryPtr == tvPtr->rootPtr) {
5748 		Blt_TreeNode next, node;
5749 
5750 		/*
5751 		 *   Don't delete the root node.  We implicitly assume
5752 		 *   that even an empty tree has at a root.  Instead
5753 		 *   delete all the children regardless if they're closed
5754 		 *   or hidden.
5755 		 */
5756 		for (node = Blt_TreeFirstChild(entryPtr->node); node != NULL;
5757 		     node = next) {
5758 		    next = Blt_TreeNextSibling(node);
5759 		    DeleteNode(tvPtr, node);
5760 		}
5761 	    } else {
5762 		DeleteNode(tvPtr, entryPtr->node);
5763 	    }
5764 	}
5765         Blt_TreeViewDoneTaggedEntries(&info);
5766      }
5767     if (objc == 3 && !strcmp(Tcl_GetString(objv[2]), "all")) {
5768         tvPtr->nextIdx = 1;
5769         tvPtr->nextSubIdx = 1;
5770     }
5771     return TCL_OK;
5772 }
5773 
5774 /*
5775  *----------------------------------------------------------------------
5776  *
5777  * MoveOp --
5778  *
5779  *	Move an entry into a new location in the hierarchy.
5780  *
5781  *
5782  *----------------------------------------------------------------------
5783  */
5784 /*ARGSUSED*/
5785 static int
MoveOp(tvPtr,interp,objc,objv)5786 MoveOp(tvPtr, interp, objc, objv)
5787     TreeView *tvPtr;
5788     Tcl_Interp *interp;
5789     int objc;			/* Not used. */
5790     Tcl_Obj *CONST *objv;
5791 {
5792     Blt_TreeNode parent;
5793     TreeViewEntry *srcPtr, *destPtr;
5794     char c;
5795     int action;
5796     char *string;
5797     TreeViewTagInfo info = {0};
5798 
5799 #define MOVE_INTO	(1<<0)
5800 #define MOVE_BEFORE	(1<<1)
5801 #define MOVE_AFTER	(1<<2)
5802     string = Tcl_GetString(objv[3]);
5803     c = string[0];
5804     if ((c == 'i') && (strcmp(string, "into") == 0)) {
5805 	action = MOVE_INTO;
5806     } else if ((c == 'b') && (strcmp(string, "before") == 0)) {
5807 	action = MOVE_BEFORE;
5808     } else if ((c == 'a') && (strcmp(string, "after") == 0)) {
5809 	action = MOVE_AFTER;
5810     } else {
5811 	Tcl_AppendResult(interp, "bad position \"", string,
5812 	    "\": should be into, before, or after", (char *)NULL);
5813 	return TCL_ERROR;
5814     }
5815     if (Blt_TreeViewGetEntry(tvPtr, objv[4], &destPtr) != TCL_OK) {
5816         return TCL_ERROR;
5817     }
5818     if (Blt_TreeViewFindTaggedEntries(tvPtr, objv[2], &info) != TCL_OK) {
5819         return TCL_ERROR;
5820     }
5821     for (srcPtr = Blt_TreeViewFirstTaggedEntry(&info); srcPtr != NULL;
5822 	 srcPtr = Blt_TreeViewNextTaggedEntry(&info)) {
5823 	/* Verify they aren't ancestors. */
5824 	if (Blt_TreeIsAncestor(srcPtr->node, destPtr->node)) {
5825 	    Tcl_DString dString;
5826 	    char *path;
5827 
5828             Tcl_DStringInit(&dString);
5829 	    path = Blt_TreeViewGetFullName(tvPtr, srcPtr, 1, &dString);
5830 	    Tcl_AppendResult(interp, "can't move node: \"", path,
5831 			"\" is an ancestor of \"", Tcl_GetString(objv[4]),
5832 			"\"", (char *)NULL);
5833 	    Tcl_DStringFree(&dString);
5834             Blt_TreeViewDoneTaggedEntries(&info);
5835 	    return TCL_ERROR;
5836 	}
5837 	parent = Blt_TreeNodeParent(destPtr->node);
5838 	if (parent == NULL) {
5839 	    action = MOVE_INTO;
5840 	}
5841 	switch (action) {
5842 	case MOVE_INTO:
5843 	    Blt_TreeMoveNode(tvPtr->tree, srcPtr->node, destPtr->node,
5844 			     (Blt_TreeNode)NULL);
5845 	    break;
5846 
5847 	case MOVE_BEFORE:
5848 	    Blt_TreeMoveNode(tvPtr->tree, srcPtr->node, parent, destPtr->node);
5849 	    break;
5850 
5851 	case MOVE_AFTER:
5852 	    Blt_TreeMoveNode(tvPtr->tree, srcPtr->node, parent,
5853 			     Blt_TreeNextSibling(destPtr->node));
5854 	    break;
5855 	}
5856     }
5857     Blt_TreeViewDoneTaggedEntries(&info);
5858     tvPtr->flags |= (TV_LAYOUT | TV_DIRTY | TV_RESORT);
5859     Blt_TreeViewEventuallyRedraw(tvPtr);
5860     return TCL_OK;
5861 }
5862 
5863 /* .t nearest ?-root? ?-strict? x y ?var? */
5864 /*ARGSUSED*/
5865 static int
NearestOp(tvPtr,interp,objc,objv)5866 NearestOp(tvPtr, interp, objc, objv)
5867     TreeView *tvPtr;
5868     Tcl_Interp *interp;
5869     int objc;			/* Not used. */
5870     Tcl_Obj *CONST *objv;
5871 {
5872     TreeViewColumn *columnPtr = NULL;
5873     TreeViewButton *buttonPtr = &tvPtr->button;
5874     int x, y, ox, oy;			/* Screen coordinates of the test point. */
5875     register TreeViewEntry *entryPtr;
5876     int isRoot, isStrict;
5877     char *string;
5878 
5879     isRoot = FALSE;
5880     isStrict = TRUE;
5881     while (objc>2) {
5882         string = Tcl_GetString(objv[2]);
5883         if (strcmp("-root", string) == 0) {
5884             isRoot = TRUE;
5885             objv++, objc--;
5886         } else if (strcmp("-strict", string) == 0) {
5887             isStrict = FALSE;
5888             objv++, objc--;
5889         } else break;
5890     }
5891     if (objc < 4 || objc > 5) {
5892 	Tcl_AppendResult(interp, "wrong # args: should be \"",
5893 		Tcl_GetString(objv[0]), " ", Tcl_GetString(objv[1]),
5894 		" ?-root? ?-strict? x y ?var?\"", (char *)NULL);
5895 	return TCL_ERROR;
5896 
5897     }
5898     if ((Tk_GetPixelsFromObj(interp, tvPtr->tkwin, objv[2], &x) != TCL_OK) ||
5899 	(Tk_GetPixelsFromObj(interp, tvPtr->tkwin, objv[3], &y) != TCL_OK)) {
5900 	return TCL_ERROR;
5901     }
5902     ox = x;
5903     oy = y;
5904     if (tvPtr->nVisible == 0) {
5905 	return TCL_OK;
5906     }
5907     if (isRoot) {
5908 	int rootX, rootY;
5909 
5910 	Tk_GetRootCoords(tvPtr->tkwin, &rootX, &rootY);
5911 	x -= rootX;
5912 	y -= rootY;
5913     }
5914     entryPtr = Blt_TreeViewNearestEntry(tvPtr, x, y, isStrict);
5915     if (entryPtr == NULL) {
5916 	return TCL_OK;
5917     }
5918     columnPtr = Blt_TreeViewNearestColumn(tvPtr, x, y, NULL);
5919     x = WORLDX(tvPtr, x);
5920     y = WORLDY(tvPtr, y);
5921 
5922     if (objc > 4) {
5923 	char *where;
5924 	int labelX, labelY, depth, height, ly, lx, butEnd = 0, entryHeight, isTree;
5925 	TreeViewIcon icon;
5926 
5927         entryHeight = MAX3(entryPtr->lineHeight, entryPtr->iconHeight,
5928              tvPtr->button.height);
5929         isTree = (columnPtr && columnPtr == &tvPtr->treeColumn);
5930 
5931 	where = "";
5932 
5933         if ((tvPtr->flags & TV_SHOW_COLUMN_TITLES)
5934              && oy<(tvPtr->insetY+tvPtr->titleHeight)) {
5935             TreeViewColumn *cPtr = columnPtr;
5936 
5937             ly = oy;
5938             lx = ox;
5939 
5940             if (cPtr->tW && (lx >= cPtr->tX) && (lx < (cPtr->tX + cPtr->tW)) &&
5941             (ly >= cPtr->tY) && (ly < (cPtr->tY + cPtr->tH))) {
5942                 where = "titlelabel";
5943                 goto done;
5944             }
5945 
5946             if (cPtr->iW && (lx >= cPtr->iX) && (lx < (cPtr->iX + cPtr->iW)) &&
5947             (ly >= cPtr->iY) && (ly < (cPtr->iY + cPtr->iH))) {
5948                 where = "titleicon";
5949                 goto done;
5950             }
5951 
5952             where = "title";
5953             goto done;
5954 	}
5955 	if (isTree && entryPtr->flags & ENTRY_HAS_BUTTON) {
5956 	    int buttonX, buttonY;
5957 
5958 	    buttonX = entryPtr->worldX + entryPtr->buttonX;
5959 	    buttonY = entryPtr->worldY + entryPtr->buttonY;
5960 	    butEnd = entryPtr->buttonX + buttonPtr->width;
5961 	    if ((x >= buttonX) && (x < (buttonX + buttonPtr->width)) &&
5962 		(y >= buttonY) && (y < (buttonY + buttonPtr->height))) {
5963 		where = "button";
5964 		goto done;
5965 	    }
5966 	}
5967 	depth = DEPTH(tvPtr, entryPtr->node);
5968 
5969 	icon = Blt_TreeViewGetEntryIcon(tvPtr, entryPtr);
5970 	if (isTree && icon != NULL) {
5971 	    int iconWidth, iconHeight;
5972 	    int iconX, iconY;
5973 
5974 	    /* entryHeight = MAX(entryPtr->iconHeight, tvPtr->button.height); */
5975 	    butEnd = entryPtr->buttonX + buttonPtr->width;
5976 	    iconHeight = TreeViewIconHeight(icon);
5977 	    iconWidth = TreeViewIconWidth(icon);
5978 	    iconX = entryPtr->worldX + butEnd;
5979 	    iconY = entryPtr->worldY + tvPtr->leader/2;
5980 	    if (tvPtr->flatView) {
5981 		iconX += (ICONWIDTH(0) - iconWidth) / 2;
5982 	    } else {
5983 		iconX += (ICONWIDTH(depth + 1) - iconWidth) / 2;
5984 	    }
5985 	    iconY += (entryHeight - iconHeight) / 2;
5986 	    if ((x >= iconX) && (x <= (iconX + iconWidth)) &&
5987 		(y >= iconY) && (y < (iconY + iconHeight))) {
5988 		where = "icon";
5989 		goto done;
5990 	    }
5991 	}
5992 	labelX = entryPtr->worldX + ICONWIDTH(depth);
5993 	labelY = entryPtr->worldY + (icon == NULL ? 0 : tvPtr->leader/2);
5994 	if (!tvPtr->flatView) {
5995 	    labelX += ICONWIDTH(depth + 1) + 4;
5996 	}
5997 	height = entryPtr->labelHeight;
5998 	ly = y;
5999         if (height < entryHeight) {
6000              ly -= (entryHeight - height) / 2;
6001         }
6002 
6003 	if (isTree && (x >= labelX) && (x < (labelX + entryPtr->labelWidth)) &&
6004 	    (ly >= labelY) && (ly < (labelY + entryPtr->labelHeight))) {
6005 	    where = "label";
6006 	}
6007 
6008 	if (columnPtr && isTree == 0) {
6009              TreeViewValue *vPtr;
6010 
6011              vPtr = Blt_TreeViewFindValue(entryPtr, columnPtr);
6012              if (vPtr == NULL) goto done;
6013              ly = y - tvPtr->yOffset + tvPtr->titleHeight;
6014              lx = x - tvPtr->xOffset;
6015 
6016              if (vPtr->tW && (lx >= vPtr->tX) && (lx < (vPtr->tX + vPtr->tW)) &&
6017              (ly >= vPtr->tY) && (ly < (vPtr->tY + vPtr->tH))) {
6018                  where = "datalabel";
6019                  goto done;
6020              }
6021 
6022              if (vPtr->iW && (lx >= vPtr->iX) && (lx < (vPtr->iX + vPtr->iW)) &&
6023              (ly >= vPtr->iY) && (ly < (vPtr->iY + vPtr->iH))) {
6024                  where = "dataicon";
6025                  goto done;
6026              }
6027 
6028 	}
6029     done:
6030 	if (Tcl_SetVar(interp, Tcl_GetString(objv[4]), where,
6031 		TCL_LEAVE_ERR_MSG) == NULL) {
6032 	    return TCL_ERROR;
6033 	}
6034     }
6035     Tcl_SetObjResult(interp, NodeToObj(entryPtr->node));
6036     return TCL_OK;
6037 }
6038 
6039 
6040 static int
OpenTreeEntry(TreeView * tvPtr,TreeViewEntry * entryPtr)6041 OpenTreeEntry(TreeView *tvPtr, TreeViewEntry *entryPtr) {
6042     if (!Blt_TreeViewIsLeaf(entryPtr)) {
6043         return Blt_TreeViewOpenEntry(tvPtr, entryPtr);
6044     }
6045     return TCL_OK;
6046 }
6047 
6048 /*ARGSUSED*/
6049 static int
OpenOp(tvPtr,interp,objc,objv)6050 OpenOp(tvPtr, interp, objc, objv)
6051     TreeView *tvPtr;
6052     Tcl_Interp *interp;		/* Not used. */
6053     int objc;
6054     Tcl_Obj *CONST *objv;
6055 {
6056     TreeViewEntry *entryPtr;
6057     TreeViewTagInfo info = {0};
6058     int recurse, trees, parent, result;
6059     register int i;
6060 
6061     recurse = FALSE;
6062     trees = FALSE;
6063     parent = FALSE;
6064     while (objc > 2) {
6065 	int length;
6066 	char *string;
6067 
6068 	string = Tcl_GetStringFromObj(objv[2], &length);
6069 	if ((string[0] == '-') && (length > 1) &&
6070 	    (strncmp(string, "-recurse", length) == 0)) {
6071 	    objv++, objc--;
6072 	    recurse = TRUE;
6073 	} else if ((string[0] == '-') && (length > 1) &&
6074 	    (strncmp(string, "-trees", length) == 0)) {
6075 	    objv++, objc--;
6076 	    trees = TRUE;
6077 	} else if ((string[0] == '-') && (length > 1) &&
6078 	    (strncmp(string, "-parent", length) == 0)) {
6079 	    objv++, objc--;
6080 	    parent = TRUE;
6081 	} else break;
6082     }
6083     for (i = 2; i < objc; i++) {
6084 	if (Blt_TreeViewFindTaggedEntries(tvPtr, objv[i], &info) != TCL_OK) {
6085 	    return TCL_ERROR;
6086 	}
6087 	for (entryPtr = Blt_TreeViewFirstTaggedEntry(&info); entryPtr != NULL;
6088 	     entryPtr = Blt_TreeViewNextTaggedEntry(&info)) {
6089 	    if (parent) {
6090                  while ((entryPtr = Blt_TreeViewParentEntry(entryPtr))) {
6091                      if (entryPtr) {
6092                          result = Blt_TreeViewOpenEntry(tvPtr, entryPtr);
6093                      }
6094                  }
6095                  continue;
6096             }
6097 
6098 	    if (trees) {
6099 		result = Blt_TreeViewApply(tvPtr, entryPtr,
6100 					   OpenTreeEntry, 0);
6101 	    } else if (recurse) {
6102 		result = Blt_TreeViewApply(tvPtr, entryPtr,
6103 					   Blt_TreeViewOpenEntry, 0);
6104 	    } else {
6105 		result = Blt_TreeViewOpenEntry(tvPtr, entryPtr);
6106 	    }
6107 	    if (result != TCL_OK) {
6108                  tvPtr->flags |= (TV_LAYOUT | TV_DIRTY | TV_RESORT);
6109                  Blt_TreeViewDoneTaggedEntries(&info);
6110                  return TCL_ERROR;
6111 	    }
6112 	    /* Make sure ancestors of this node aren't hidden. */
6113 	    MapAncestors(tvPtr, entryPtr);
6114 	}
6115         Blt_TreeViewDoneTaggedEntries(&info);
6116     }
6117     /*FIXME: This is only for flattened entries.  */
6118     tvPtr->flags |= (TV_LAYOUT | TV_DIRTY | TV_RESORT);
6119     Blt_TreeViewEventuallyRedraw(tvPtr);
6120     return TCL_OK;
6121 }
6122 
6123 /*
6124  *----------------------------------------------------------------------
6125  *
6126  * RangeOp --
6127  *
6128  *	Returns the node identifiers in a given range.
6129  *
6130  *----------------------------------------------------------------------
6131  */
6132 static int
RangeOp(tvPtr,interp,objc,objv)6133 RangeOp(tvPtr, interp, objc, objv)
6134     TreeView *tvPtr;
6135     Tcl_Interp *interp;
6136     int objc;
6137     Tcl_Obj *CONST *objv;
6138 {
6139     TreeViewEntry *entryPtr, *firstPtr, *lastPtr;
6140     unsigned int mask;
6141     int length;
6142     Tcl_Obj *listObjPtr, *objPtr;
6143     char *string;
6144 
6145     mask = 0;
6146     string = Tcl_GetStringFromObj(objv[2], &length);
6147     if ((string[0] == '-') && (length > 1) &&
6148 	(strncmp(string, "-open", length) == 0)) {
6149 	objv++, objc--;
6150 	mask |= ENTRY_CLOSED;
6151     }
6152     if (Blt_TreeViewGetEntry(tvPtr, objv[2], &firstPtr) != TCL_OK) {
6153 	return TCL_ERROR;
6154     }
6155     if (objc > 3) {
6156 	if (Blt_TreeViewGetEntry(tvPtr, objv[3], &lastPtr) != TCL_OK) {
6157 	    return TCL_ERROR;
6158 	}
6159     } else {
6160 	lastPtr = LastEntry(tvPtr, firstPtr, mask);
6161     }
6162     if (mask & ENTRY_CLOSED) {
6163 	if (firstPtr->flags & ENTRY_HIDDEN) {
6164 	    Tcl_AppendResult(interp, "first node \"", Tcl_GetString(objv[2]),
6165 		"\" is hidden.", (char *)NULL);
6166 	    return TCL_ERROR;
6167 	}
6168 	if (lastPtr->flags & ENTRY_HIDDEN) {
6169 	    Tcl_AppendResult(interp, "last node \"", Tcl_GetString(objv[3]),
6170 		"\" is hidden.", (char *)NULL);
6171 	    return TCL_ERROR;
6172 	}
6173     }
6174 
6175     /*
6176      * The relative order of the first/last markers determines the
6177      * direction.
6178      */
6179     listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
6180     if (Blt_TreeIsBefore(lastPtr->node, firstPtr->node)) {
6181 	for (entryPtr = lastPtr; entryPtr != NULL;
6182 	     entryPtr = Blt_TreeViewPrevEntry(entryPtr, mask)) {
6183 	    objPtr = NodeToObj(entryPtr->node);
6184 	    Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
6185 	    if (entryPtr == firstPtr) {
6186 		break;
6187 	    }
6188 	}
6189     } else {
6190 	for (entryPtr = firstPtr; entryPtr != NULL;
6191 	     entryPtr = Blt_TreeViewNextEntry(entryPtr, mask)) {
6192 	    objPtr = NodeToObj(entryPtr->node);
6193 	    Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
6194 	    if (entryPtr == lastPtr) {
6195 		break;
6196 	    }
6197 	}
6198     }
6199     Tcl_SetObjResult(interp, listObjPtr);
6200     return TCL_OK;
6201 }
6202 
6203 /*
6204  *----------------------------------------------------------------------
6205  *
6206  * ScanOp --
6207  *
6208  *	Implements the quick scan.
6209  *
6210  *----------------------------------------------------------------------
6211  */
6212 /*ARGSUSED*/
6213 static int
ScanOp(tvPtr,interp,objc,objv)6214 ScanOp(tvPtr, interp, objc, objv)
6215     TreeView *tvPtr;
6216     Tcl_Interp *interp;
6217     int objc;			/* Not used. */
6218     Tcl_Obj *CONST *objv;
6219 {
6220     int x, y;
6221     char c;
6222     int length;
6223     int oper;
6224     char *string;
6225     Tk_Window tkwin;
6226 
6227 #define SCAN_MARK	1
6228 #define SCAN_DRAGTO	2
6229     string = Tcl_GetStringFromObj(objv[2], &length);
6230     c = string[0];
6231     tkwin = tvPtr->tkwin;
6232     if ((c == 'm') && (strncmp(string, "mark", length) == 0)) {
6233 	oper = SCAN_MARK;
6234     } else if ((c == 'd') && (strncmp(string, "dragto", length) == 0)) {
6235 	oper = SCAN_DRAGTO;
6236     } else {
6237 	Tcl_AppendResult(interp, "bad scan operation \"", string,
6238 	    "\": should be either \"mark\" or \"dragto\"", (char *)NULL);
6239 	return TCL_ERROR;
6240     }
6241     if ((Blt_GetPixelsFromObj(interp, tkwin, objv[3], 0, &x) != TCL_OK) ||
6242 	(Blt_GetPixelsFromObj(interp, tkwin, objv[4], 0, &y) != TCL_OK)) {
6243 	return TCL_ERROR;
6244     }
6245     if (oper == SCAN_MARK) {
6246 	tvPtr->scanAnchorX = x;
6247 	tvPtr->scanAnchorY = y;
6248 	tvPtr->scanX = tvPtr->xOffset;
6249 	tvPtr->scanY = tvPtr->yOffset;
6250     } else {
6251 	int worldX, worldY;
6252 	int dx, dy;
6253 
6254 	dx = tvPtr->scanAnchorX - x;
6255 	dy = tvPtr->scanAnchorY - y;
6256 	worldX = tvPtr->scanX + (10 * dx);
6257 	worldY = tvPtr->scanY + (10 * dy);
6258 
6259 	if (worldX < 0) {
6260 	    worldX = 0;
6261 	} else if (worldX >= tvPtr->worldWidth) {
6262 	    worldX = tvPtr->worldWidth - tvPtr->xScrollUnits;
6263 	}
6264 	if (worldY < 0) {
6265 	    worldY = 0;
6266 	} else if (worldY >= tvPtr->worldHeight) {
6267 	    worldY = tvPtr->worldHeight - tvPtr->yScrollUnits;
6268 	}
6269 	tvPtr->xOffset = worldX;
6270 	tvPtr->yOffset = worldY;
6271 	tvPtr->flags |= TV_SCROLL;
6272 	Blt_TreeViewEventuallyRedraw(tvPtr);
6273     }
6274     return TCL_OK;
6275 }
6276 
6277 /*ARGSUSED*/
6278 static int
SeeOp(tvPtr,interp,objc,objv)6279 SeeOp(tvPtr, interp, objc, objv)
6280     TreeView *tvPtr;
6281     Tcl_Interp *interp;		/* Not used. */
6282     int objc;
6283     Tcl_Obj *CONST *objv;
6284 {
6285     TreeViewEntry *entryPtr;
6286     int width, height;
6287     int x, y;
6288     Tk_Anchor anchor;
6289     int left, right, top, bottom;
6290     char *string;
6291 
6292     string = Tcl_GetString(objv[2]);
6293     anchor = TK_ANCHOR_W;	/* Default anchor is West */
6294     if ((string[0] == '-') && (strcmp(string, "-anchor") == 0)) {
6295 	if (objc == 3) {
6296 	    Tcl_AppendResult(interp, "missing \"-anchor\" argument",
6297 		(char *)NULL);
6298 	    return TCL_ERROR;
6299 	}
6300 	if (Tk_GetAnchorFromObj(interp, objv[3], &anchor) != TCL_OK) {
6301 	    return TCL_ERROR;
6302 	}
6303 	objc -= 2, objv += 2;
6304     }
6305     if (objc == 2) {
6306 	Tcl_AppendResult(interp, "wrong # args: should be \"",
6307 	    Tcl_GetString(objv[0]),
6308 	    "see ?-anchor anchor? tagOrId\"", (char *)NULL);
6309 	return TCL_ERROR;
6310     }
6311     if (GetEntryFromObj(tvPtr, objv[2], &entryPtr) != TCL_OK) {
6312 	return TCL_ERROR;
6313     }
6314     if (entryPtr == NULL || tvPtr->noScroll) {
6315 	return TCL_OK;
6316     }
6317     if (entryPtr->flags & ENTRY_HIDDEN) {
6318 	MapAncestors(tvPtr, entryPtr);
6319 	tvPtr->flags |= TV_SCROLL;
6320 	/*
6321 	 * If the entry wasn't previously exposed, its world coordinates
6322 	 * aren't likely to be valid.  So re-compute the layout before
6323 	 * we try to see the viewport to the entry's location.
6324 	 */
6325 	Blt_TreeViewComputeLayout(tvPtr);
6326     }
6327     width = VPORTWIDTH(tvPtr);
6328     height = VPORTHEIGHT(tvPtr);
6329 
6330     /*
6331      * XVIEW:	If the entry is left or right of the current view, adjust
6332      *		the offset.  If the entry is nearby, adjust the view just
6333      *		a bit.  Otherwise, center the entry.
6334      */
6335     left = tvPtr->xOffset;
6336     right = tvPtr->xOffset + width;
6337 
6338     switch (anchor) {
6339     case TK_ANCHOR_W:
6340     case TK_ANCHOR_NW:
6341     case TK_ANCHOR_SW:
6342 	x = 0;
6343 	break;
6344     case TK_ANCHOR_E:
6345     case TK_ANCHOR_NE:
6346     case TK_ANCHOR_SE:
6347 	x = entryPtr->worldX + entryPtr->width +
6348 	    ICONWIDTH(DEPTH(tvPtr, entryPtr->node)) - width;
6349 	break;
6350     default:
6351 	if (entryPtr->worldX < left) {
6352 	    x = entryPtr->worldX;
6353 	} else if ((entryPtr->worldX + entryPtr->width) > right) {
6354 	    x = entryPtr->worldX + entryPtr->width - width;
6355 	} else {
6356 	    x = tvPtr->xOffset;
6357 	}
6358 	break;
6359     }
6360     /*
6361      * YVIEW:	If the entry is above or below the current view, adjust
6362      *		the offset.  If the entry is nearby, adjust the view just
6363      *		a bit.  Otherwise, center the entry.
6364      */
6365     top = tvPtr->yOffset;
6366     bottom = tvPtr->yOffset + height;
6367 
6368     switch (anchor) {
6369     case TK_ANCHOR_N:
6370     case TK_ANCHOR_NE:
6371     case TK_ANCHOR_NW:
6372 	y = entryPtr->worldY;
6373 	break;
6374     case TK_ANCHOR_CENTER:
6375 	y = entryPtr->worldY - (height / 2);
6376 	break;
6377     case TK_ANCHOR_S:
6378     case TK_ANCHOR_SE:
6379     case TK_ANCHOR_SW:
6380 	y = entryPtr->worldY + entryPtr->height - height;
6381 	break;
6382     default:
6383 	if (entryPtr->worldY < top) {
6384 	    y = entryPtr->worldY;
6385 	} else if ((entryPtr->worldY + entryPtr->height) > bottom) {
6386 	    y = entryPtr->worldY + entryPtr->height - height;
6387 	} else {
6388 	    y = tvPtr->yOffset;
6389 	}
6390 	break;
6391     }
6392     if ((y != tvPtr->yOffset) || (x != tvPtr->xOffset)) {
6393 	/* tvPtr->xOffset = x; */
6394 	tvPtr->yOffset = y;
6395 	tvPtr->flags |= TV_SCROLL;
6396     }
6397     Blt_TreeViewEventuallyRedraw(tvPtr);
6398     return TCL_OK;
6399 }
6400 
6401 void
Blt_TreeViewClearSelection(tvPtr)6402 Blt_TreeViewClearSelection(tvPtr)
6403     TreeView *tvPtr;
6404 {
6405     if (tvPtr->selectMode & SELECT_MODE_CELLMASK) {
6406         Blt_HashEntry *hPtr;
6407         Blt_HashSearch cursor;
6408         TreeViewValue *valuePtr;
6409         TreeViewEntry *entryPtr;
6410         TreeViewColumn *columnPtr;
6411         Blt_ChainLink *linkPtr;
6412 
6413         for (hPtr = Blt_FirstHashEntry(&tvPtr->selectTable, &cursor);
6414             hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
6415             entryPtr = (TreeViewEntry *)Blt_GetHashKey(&tvPtr->selectTable, hPtr);
6416             for (linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr);
6417                 linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
6418                 columnPtr = Blt_ChainGetValue(linkPtr);
6419                 valuePtr = (TreeViewValue *)Blt_TreeViewFindValue(entryPtr, columnPtr);
6420                 if (valuePtr != NULL) {
6421                     valuePtr->selected = 0;
6422                 }
6423             }
6424         }
6425     }
6426     Blt_DeleteHashTable(&tvPtr->selectTable);
6427     Blt_InitHashTable(&tvPtr->selectTable, BLT_ONE_WORD_KEYS);
6428     Blt_ChainReset(tvPtr->selChainPtr);
6429     Blt_TreeViewEventuallyRedraw(tvPtr);
6430     if (tvPtr->selectCmd != NULL) {
6431 	EventuallyInvokeSelectCmd(tvPtr);
6432     }
6433 }
6434 
6435 /*
6436  *----------------------------------------------------------------------
6437  *
6438  * LostSelection --
6439  *
6440  *	This procedure is called back by Tk when the selection is grabbed
6441  *	away.
6442  *
6443  * Results:
6444  *	None.
6445  *
6446  * Side effects:
6447  *	The existing selection is unhighlighted, and the window is
6448  *	marked as not containing a selection.
6449  *
6450  *----------------------------------------------------------------------
6451  */
6452 static void
LostSelection(clientData)6453 LostSelection(clientData)
6454     ClientData clientData;	/* Information about the widget. */
6455 {
6456     TreeView *tvPtr = clientData;
6457 
6458     if ((tvPtr->flags & TV_SELECT_EXPORT) == 0) {
6459 	return;
6460     }
6461     Blt_TreeViewClearSelection(tvPtr);
6462 }
6463 
6464 /*
6465  *----------------------------------------------------------------------
6466  *
6467  * SelectRange --
6468  *
6469  *	Sets the selection flag for a range of nodes.  The range is
6470  *	determined by two pointers which designate the first/last
6471  *	nodes of the range.
6472  *
6473  * Results:
6474  *	Always returns TCL_OK.
6475  *
6476  *----------------------------------------------------------------------
6477  */
6478 static int
SelectRange(tvPtr,fromPtr,toPtr)6479 SelectRange(tvPtr, fromPtr, toPtr)
6480     TreeView *tvPtr;
6481     TreeViewEntry *fromPtr, *toPtr;
6482 {
6483     if (tvPtr->flatView) {
6484 	register int i;
6485 
6486 	if (fromPtr->flatIndex > toPtr->flatIndex) {
6487 	    for (i = fromPtr->flatIndex; i >= toPtr->flatIndex; i--) {
6488 		SelectEntryApplyProc(tvPtr, tvPtr->flatArr[i], NULL);
6489 	    }
6490 	} else {
6491 	    for (i = fromPtr->flatIndex; i <= toPtr->flatIndex; i++) {
6492 		SelectEntryApplyProc(tvPtr, tvPtr->flatArr[i], NULL);
6493 	    }
6494 	}
6495     } else {
6496 	TreeViewEntry *entryPtr;
6497 	TreeViewIterProc *proc;
6498 	/* From the range determine the direction to select entries. */
6499 
6500 	proc = (Blt_TreeIsBefore(toPtr->node, fromPtr->node))
6501 	    ? Blt_TreeViewPrevEntry : Blt_TreeViewNextEntry;
6502 	for (entryPtr = fromPtr; entryPtr != NULL;
6503 	     entryPtr = (*proc)(entryPtr, ENTRY_MASK)) {
6504 	    SelectEntryApplyProc(tvPtr, entryPtr, NULL);
6505 	    if (entryPtr == toPtr) {
6506 		break;
6507 	    }
6508 	}
6509     }
6510     return TCL_OK;
6511 }
6512 
6513 /*
6514  *----------------------------------------------------------------------
6515  *
6516  * SelectionAnchorOp --
6517  *
6518  *	Sets the selection anchor to the element given by a index.
6519  *	The selection anchor is the end of the selection that is fixed
6520  *	while dragging out a selection with the mouse.  The index
6521  *	"anchor" may be used to refer to the anchor element.
6522  *
6523  * Results:
6524  *	None.
6525  *
6526  * Side effects:
6527  *	The selection changes.
6528  *
6529  *----------------------------------------------------------------------
6530  */
6531 /*ARGSUSED*/
6532 static int
SelectionAnchorOp(tvPtr,interp,objc,objv)6533 SelectionAnchorOp(tvPtr, interp, objc, objv)
6534     TreeView *tvPtr;
6535     Tcl_Interp *interp;		/* Not used. */
6536     int objc;			/* Not used. */
6537     Tcl_Obj *CONST *objv;
6538 {
6539     TreeViewEntry *entryPtr;
6540     TreeViewColumn *columnPtr;
6541 
6542     columnPtr = NULL;
6543 
6544     if (objc<=3) {
6545         Tcl_Obj *listObjPtr, *objPtr;
6546         if (tvPtr->selAnchorPtr == NULL) return TCL_OK;
6547         listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
6548         objPtr = NodeToObj(tvPtr->selAnchorPtr);
6549         Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
6550         if (tvPtr->selAnchorCol != NULL) {
6551             objPtr = Tcl_NewStringObj( tvPtr->selAnchorCol->key, -1);
6552             Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
6553         }
6554         Tcl_SetObjResult(interp, listObjPtr);
6555         return TCL_OK;
6556     }
6557     if (GetEntryFromObj(tvPtr, objv[3], &entryPtr) != TCL_OK) {
6558 	return TCL_ERROR;
6559     }
6560     if (objc > 4) {
6561         if (Blt_TreeViewGetColumn(interp, tvPtr, objv[4], &columnPtr) != TCL_OK) {
6562             return TCL_ERROR;
6563         }
6564     }
6565     /* Set both the anchor and the mark. Indicates that a single entry
6566      * is selected. */
6567     tvPtr->selAnchorPtr = entryPtr;
6568     tvPtr->selMarkPtr = NULL;
6569     tvPtr->selAnchorCol = columnPtr;
6570     if (entryPtr != NULL) {
6571 	Tcl_SetObjResult(interp, NodeToObj(entryPtr->node));
6572     }
6573     Blt_TreeViewEventuallyRedraw(tvPtr);
6574     return TCL_OK;
6575 }
6576 
6577 
6578 /*
6579  *----------------------------------------------------------------------
6580  *
6581  * SelectionClearallOp
6582  *
6583  *	Clears the entire selection.
6584  *
6585  * Results:
6586  *	None.
6587  *
6588  * Side effects:
6589  *	The selection changes.
6590  *
6591  *----------------------------------------------------------------------
6592  */
6593 /*ARGSUSED*/
6594 static int
SelectionClearallOp(tvPtr,interp,objc,objv)6595 SelectionClearallOp(tvPtr, interp, objc, objv)
6596     TreeView *tvPtr;
6597     Tcl_Interp *interp;		/* Not used. */
6598     int objc;			/* Not used. */
6599     Tcl_Obj *CONST *objv;	/* Not used. */
6600 {
6601     Blt_TreeViewClearSelection(tvPtr);
6602     return TCL_OK;
6603 }
6604 
6605 /*
6606  *----------------------------------------------------------------------
6607  *
6608  * SelectionIncludesOp
6609  *
6610  *	Returns 1 if the element indicated by index is currently
6611  *	selected, 0 if it isn't.
6612  *
6613  * Results:
6614  *	None.
6615  *
6616  * Side effects:
6617  *	The selection changes.
6618  *
6619  *----------------------------------------------------------------------
6620  */
6621 /*ARGSUSED*/
6622 static int
SelectionIncludesOp(tvPtr,interp,objc,objv)6623 SelectionIncludesOp(tvPtr, interp, objc, objv)
6624     TreeView *tvPtr;
6625     Tcl_Interp *interp;
6626     int objc;			/* Not used. */
6627     Tcl_Obj *CONST *objv;
6628 {
6629     TreeViewEntry *entryPtr;
6630     int bool;
6631     TreeViewColumn *columnPtr;
6632 
6633     columnPtr = NULL;
6634 
6635     if (Blt_TreeViewGetEntry(tvPtr, objv[3], &entryPtr) != TCL_OK) {
6636 	return TCL_ERROR;
6637     }
6638     if (objc > 4) {
6639         if (Blt_TreeViewGetColumn(interp, tvPtr, objv[4], &columnPtr) != TCL_OK) {
6640             return TCL_ERROR;
6641         }
6642     }
6643     bool = Blt_TreeViewEntryIsSelected(tvPtr, entryPtr, columnPtr);
6644     Tcl_SetObjResult(interp, Tcl_NewBooleanObj(bool));
6645     return TCL_OK;
6646 }
6647 
6648 /*
6649  *----------------------------------------------------------------------
6650  *
6651  * SelectionCellsOp
6652  *
6653  *	Returns pairs of node and col for selected cells.
6654  *
6655  * Results:
6656  *	None.
6657  *
6658  *----------------------------------------------------------------------
6659  */
6660 /*ARGSUSED*/
6661 static int
SelectionCellsOp(tvPtr,interp,objc,objv)6662 SelectionCellsOp(tvPtr, interp, objc, objv)
6663     TreeView *tvPtr;
6664     Tcl_Interp *interp;
6665     int objc;			/* Not used. */
6666     Tcl_Obj *CONST *objv;
6667 {
6668     TreeViewEntry *entryPtr;
6669     TreeViewColumn *columnPtr;
6670     Tcl_Obj *listObjPtr, *objPtr;
6671 
6672     Blt_HashEntry *hPtr;
6673     Blt_HashSearch cursor;
6674     TreeViewValue *valuePtr;
6675     Blt_ChainLink *linkPtr;
6676 
6677     if (!(tvPtr->selectMode & SELECT_MODE_CELLMASK)) {
6678         Tcl_AppendResult(interp, "-selectmode not 'cell' or 'multicell'", 0);
6679         return TCL_ERROR;
6680     }
6681     listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
6682 
6683     for (hPtr = Blt_FirstHashEntry(&tvPtr->selectTable, &cursor);
6684         hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
6685         entryPtr = (TreeViewEntry *)Blt_GetHashKey(&tvPtr->selectTable, hPtr);
6686         for (linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr);
6687             linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
6688 
6689             columnPtr = Blt_ChainGetValue(linkPtr);
6690             valuePtr = (TreeViewValue *)Blt_TreeViewFindValue(entryPtr, columnPtr);
6691             if (valuePtr != NULL && valuePtr->selected) {
6692                 objPtr = NodeToObj(entryPtr->node);
6693                 Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
6694                 objPtr = Tcl_NewStringObj(columnPtr->key, -1);
6695                 Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
6696             }
6697         }
6698     }
6699 
6700 
6701     Tcl_SetObjResult(interp, listObjPtr);
6702 
6703     return TCL_OK;
6704 }
6705 /*
6706  *----------------------------------------------------------------------
6707  *
6708  * SelectionMarkOp --
6709  *
6710  *	Sets the selection mark to the element given by a index.
6711  *	The selection anchor is the end of the selection that is movable
6712  *	while dragging out a selection with the mouse.  The index
6713  *	"mark" may be used to refer to the anchor element.
6714  *
6715  * Results:
6716  *	None.
6717  *
6718  * Side effects:
6719  *	The selection changes.
6720  *
6721  *----------------------------------------------------------------------
6722  */
6723 /*ARGSUSED*/
6724 static int
SelectionMarkOp(tvPtr,interp,objc,objv)6725 SelectionMarkOp(tvPtr, interp, objc, objv)
6726     TreeView *tvPtr;
6727     Tcl_Interp *interp;		/* Not used. */
6728     int objc;			/* Not used. */
6729     Tcl_Obj *CONST *objv;
6730 {
6731     TreeViewEntry *entryPtr;
6732     TreeViewColumn *columnPtr;
6733 
6734     columnPtr = NULL;
6735 
6736     if (GetEntryFromObj(tvPtr, objv[3], &entryPtr) != TCL_OK) {
6737 	return TCL_ERROR;
6738     }
6739     if (objc > 4) {
6740         if (Blt_TreeViewGetColumn(interp, tvPtr, objv[5], &columnPtr) != TCL_OK) {
6741             return TCL_ERROR;
6742         }
6743     }
6744     if (tvPtr->selAnchorPtr == NULL) {
6745 	Tcl_AppendResult(interp, "selection anchor must be set first",
6746 		 (char *)NULL);
6747 	return TCL_ERROR;
6748     }
6749     if (tvPtr->selMarkPtr != entryPtr) {
6750 	Blt_ChainLink *linkPtr, *nextPtr;
6751 	TreeViewEntry *selectPtr;
6752 
6753 	/* Deselect entry from the list all the way back to the anchor. */
6754 	for (linkPtr = Blt_ChainLastLink(tvPtr->selChainPtr); linkPtr != NULL;
6755 	     linkPtr = nextPtr) {
6756 	    nextPtr = Blt_ChainPrevLink(linkPtr);
6757 	    selectPtr = Blt_ChainGetValue(linkPtr);
6758 	    if (selectPtr == tvPtr->selAnchorPtr) {
6759 		break;
6760 	    }
6761 	    Blt_TreeViewDeselectEntry(tvPtr, selectPtr, NULL);
6762 	}
6763 	tvPtr->flags &= ~TV_SELECT_MASK;
6764 	tvPtr->flags |= TV_SELECT_SET;
6765 	SelectRange(tvPtr, tvPtr->selAnchorPtr, entryPtr);
6766 	Tcl_SetObjResult(interp, NodeToObj(entryPtr->node));
6767 	tvPtr->selMarkPtr = entryPtr;
6768 
6769 	Blt_TreeViewEventuallyRedraw(tvPtr);
6770 	if (tvPtr->selectCmd != NULL) {
6771 	    EventuallyInvokeSelectCmd(tvPtr);
6772 	}
6773     }
6774     return TCL_OK;
6775 }
6776 
6777 /*
6778  *----------------------------------------------------------------------
6779  *
6780  * SelectionPresentOp
6781  *
6782  *	Returns 1 if there is a selection and 0 if it isn't.
6783  *
6784  * Results:
6785  *	A standard Tcl result.  interp->result will contain a
6786  *	boolean string indicating if there is a selection.
6787  *
6788  *----------------------------------------------------------------------
6789  */
6790 /*ARGSUSED*/
6791 static int
SelectionPresentOp(tvPtr,interp,objc,objv)6792 SelectionPresentOp(tvPtr, interp, objc, objv)
6793     TreeView *tvPtr;
6794     Tcl_Interp *interp;
6795     int objc;			/* Not used. */
6796     Tcl_Obj *CONST *objv;
6797 {
6798     int bool;
6799 
6800     bool = (Blt_ChainGetLength(tvPtr->selChainPtr) > 0);
6801     Tcl_SetObjResult(interp, Tcl_NewBooleanObj(bool));
6802     return TCL_OK;
6803 }
6804 
6805 /*
6806  *----------------------------------------------------------------------
6807  *
6808  * SelectionSetOp
6809  *
6810  *	Selects, deselects, or toggles all of the elements in the
6811  *	range between first and last, inclusive, without affecting the
6812  *	selection state of elements outside that range.
6813  *
6814  * Results:
6815  *	None.
6816  *
6817  * Side effects:
6818  *	The selection changes.
6819  *
6820  *----------------------------------------------------------------------
6821  */
6822 /*ARGSUSED*/
6823 static int
SelectionSetOp(tvPtr,interp,objc,objv)6824 SelectionSetOp(tvPtr, interp, objc, objv)
6825     TreeView *tvPtr;
6826     Tcl_Interp *interp;
6827     int objc;			/* Not used. */
6828     Tcl_Obj *CONST *objv;
6829 {
6830     TreeViewEntry *firstPtr, *lastPtr;
6831     TreeViewColumn *columnPtr;
6832     char *string;
6833 
6834     columnPtr = NULL;
6835     tvPtr->flags &= ~TV_SELECT_MASK;
6836     string = Tcl_GetString(objv[2]);
6837     switch (string[0]) {
6838     case 's':
6839 	tvPtr->flags |= TV_SELECT_SET;
6840 	break;
6841     case 'c':
6842 	tvPtr->flags |= TV_SELECT_CLEAR;
6843 	break;
6844     case 't':
6845 	tvPtr->flags |= TV_SELECT_TOGGLE;
6846 	break;
6847     }
6848     if (Blt_TreeViewGetEntry(tvPtr, objv[3], &firstPtr) != TCL_OK) {
6849 	return TCL_ERROR;
6850     }
6851     if ((firstPtr->flags & ENTRY_HIDDEN) &&
6852 	(!(tvPtr->flags & TV_SELECT_CLEAR))) {
6853 	Tcl_AppendResult(interp, "can't select hidden node \"",
6854 		Tcl_GetString(objv[3]), "\"", (char *)NULL);
6855 	return TCL_ERROR;
6856     }
6857     lastPtr = firstPtr;
6858     if (objc > 4) {
6859 	if (Blt_TreeViewGetEntry(tvPtr, objv[4], &lastPtr) != TCL_OK) {
6860 	    return TCL_ERROR;
6861 	}
6862 	if ((lastPtr->flags & ENTRY_HIDDEN) &&
6863 		(!(tvPtr->flags & TV_SELECT_CLEAR))) {
6864 	    Tcl_AppendResult(interp, "can't select hidden node \"",
6865 		     Tcl_GetString(objv[4]), "\"", (char *)NULL);
6866 	    return TCL_ERROR;
6867 	}
6868     }
6869     if (objc > 5) {
6870         if (Blt_TreeViewGetColumn(interp, tvPtr, objv[5], &columnPtr) != TCL_OK) {
6871             return TCL_ERROR;
6872         }
6873     }
6874     if (firstPtr == lastPtr) {
6875 	SelectEntryApplyProc(tvPtr, firstPtr, columnPtr);
6876     } else {
6877 	SelectRange(tvPtr, firstPtr, lastPtr, columnPtr);
6878     }
6879     /* Set both the anchor and the mark. Indicates that a single entry
6880      * is selected. */
6881     if (tvPtr->selAnchorPtr == NULL) {
6882 	tvPtr->selAnchorPtr = firstPtr;
6883     }
6884     if (tvPtr->flags & TV_SELECT_EXPORT) {
6885 	Tk_OwnSelection(tvPtr->tkwin, XA_PRIMARY, LostSelection, tvPtr);
6886     }
6887     Blt_TreeViewEventuallyRedraw(tvPtr);
6888     if (tvPtr->selectCmd != NULL) {
6889 	EventuallyInvokeSelectCmd(tvPtr);
6890     }
6891     return TCL_OK;
6892 }
6893 
6894 /*
6895  *----------------------------------------------------------------------
6896  *
6897  * SelectionOp --
6898  *
6899  *	This procedure handles the individual options for text
6900  *	selections.  The selected text is designated by start and end
6901  *	indices into the text pool.  The selected segment has both a
6902  *	anchored and unanchored ends.
6903  *
6904  * Results:
6905  *	None.
6906  *
6907  * Side effects:
6908  *	The selection changes.
6909  *
6910  *----------------------------------------------------------------------
6911  */
6912 static Blt_OpSpec selectionOps[] =
6913 {
6914     {"anchor", 1, (Blt_Op)SelectionAnchorOp, 3, 5, "?tagOrId? ?column?",},
6915     {"cells", 1, (Blt_Op)SelectionCellsOp, 3, 3, "",},
6916     {"clear", 5, (Blt_Op)SelectionSetOp, 4, 6, "first ?last? ?column?",},
6917     {"clearall", 6, (Blt_Op)SelectionClearallOp, 3, 3, "",},
6918     {"includes", 1, (Blt_Op)SelectionIncludesOp, 4, 5, "tagOrId  ?column?",},
6919     {"mark", 1, (Blt_Op)SelectionMarkOp, 4, 5, "tagOrId ?column?",},
6920     {"present", 1, (Blt_Op)SelectionPresentOp, 3, 3, "",},
6921     {"set", 1, (Blt_Op)SelectionSetOp, 4, 6, "first ?last? ?column?",},
6922     {"toggle", 1, (Blt_Op)SelectionSetOp, 4, 6, "first ?last? ?column?",},
6923 };
6924 static int nSelectionOps = sizeof(selectionOps) / sizeof(Blt_OpSpec);
6925 
6926 static int
SelectionOp(tvPtr,interp,objc,objv)6927 SelectionOp(tvPtr, interp, objc, objv)
6928     TreeView *tvPtr;
6929     Tcl_Interp *interp;
6930     int objc;
6931     Tcl_Obj *CONST *objv;
6932 {
6933     Blt_Op proc;
6934     int result;
6935 
6936     proc = Blt_GetOpFromObj(interp, nSelectionOps, selectionOps, BLT_OP_ARG2,
6937 	objc, objv, 0);
6938     if (proc == NULL) {
6939 	return TCL_ERROR;
6940     }
6941     result = (*proc) (tvPtr, interp, objc, objv);
6942     return result;
6943 }
6944 
6945 
6946 /*
6947  *----------------------------------------------------------------------
6948  *
6949  * TagForgetOp --
6950  *
6951  *----------------------------------------------------------------------
6952  */
6953 /*ARGSUSED*/
6954 static int
TagForgetOp(tvPtr,interp,objc,objv)6955 TagForgetOp(tvPtr, interp, objc, objv)
6956     TreeView *tvPtr;
6957     Tcl_Interp *interp;
6958     int objc;			/* Not used. */
6959     Tcl_Obj *CONST *objv;
6960 {
6961     register int i;
6962 
6963     for (i = 3; i < objc; i++) {
6964 	if (Blt_TreeForgetTag(tvPtr->tree, Tcl_GetString(objv[i])) != TCL_OK) {
6965 	    return TCL_ERROR;
6966 	}
6967     }
6968     return TCL_OK;
6969 }
6970 
6971 /*
6972  *----------------------------------------------------------------------
6973  *
6974  * TagNamesOp --
6975  *
6976  *----------------------------------------------------------------------
6977  */
6978 static int
TagNamesOp(tvPtr,interp,objc,objv)6979 TagNamesOp(tvPtr, interp, objc, objv)
6980     TreeView *tvPtr;
6981     Tcl_Interp *interp;
6982     int objc;
6983     Tcl_Obj *CONST *objv;
6984 {
6985     Tcl_Obj *listObjPtr, *objPtr;
6986 
6987     listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
6988     /* objPtr = Tcl_NewStringObj("all", -1);
6989     Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
6990     objPtr = Tcl_NewStringObj("nonroot", -1);
6991     Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
6992     objPtr = Tcl_NewStringObj("rootchildren", -1);
6993     Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);*/
6994     if (objc == 3) {
6995 	Blt_HashEntry *hPtr;
6996 	Blt_HashSearch cursor;
6997 	Blt_TreeTagEntry *tPtr;
6998 
6999 	objPtr = Tcl_NewStringObj("root", -1);
7000 	Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
7001 	for (hPtr = Blt_TreeFirstTag(tvPtr->tree, &cursor); hPtr != NULL;
7002 	     hPtr = Blt_NextHashEntry(&cursor)) {
7003 	    tPtr = Blt_GetHashValue(hPtr);
7004 	    objPtr = Tcl_NewStringObj(tPtr->tagName, -1);
7005 	    Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
7006 	}
7007     } else {
7008 	register int i;
7009 	TreeViewEntry *entryPtr;
7010 	Blt_List list;
7011 	Blt_ListNode listNode;
7012 
7013 	for (i = 3; i < objc; i++) {
7014 	    if (Blt_TreeViewGetEntry(tvPtr, objv[i], &entryPtr) != TCL_OK) {
7015 		return TCL_ERROR;
7016 	    }
7017 	    list = Blt_ListCreate(BLT_ONE_WORD_KEYS);
7018 	    Blt_TreeViewGetTags(interp, tvPtr, entryPtr, list);
7019 	    for (listNode = Blt_ListFirstNode(list); listNode != NULL;
7020 		 listNode = Blt_ListNextNode(listNode)) {
7021 		objPtr = Tcl_NewStringObj(Blt_ListGetKey(listNode), -1);
7022 		Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
7023 	    }
7024 	    Blt_ListDestroy(list);
7025 	}
7026     }
7027     Tcl_SetObjResult(interp, listObjPtr);
7028     return TCL_OK;
7029 }
7030 
7031 /*
7032  *----------------------------------------------------------------------
7033  *
7034  * TagNodesOp --
7035  *
7036  *----------------------------------------------------------------------
7037  */
7038 static int
TagNodesOp(tvPtr,interp,objc,objv)7039 TagNodesOp(tvPtr, interp, objc, objv)
7040     TreeView *tvPtr;
7041     Tcl_Interp *interp;
7042     int objc;
7043     Tcl_Obj *CONST *objv;
7044 {
7045     Blt_HashEntry *hPtr;
7046     Blt_HashSearch cursor;
7047     Blt_HashTable nodeTable;
7048     Blt_TreeNode node;
7049     TreeViewTagInfo info = {0};
7050     Tcl_Obj *listObjPtr;
7051     Tcl_Obj *objPtr;
7052     TreeViewEntry *entryPtr;
7053     int isNew;
7054     register int i;
7055 
7056     Blt_InitHashTable(&nodeTable, BLT_ONE_WORD_KEYS);
7057     for (i = 3; i < objc; i++) {
7058 	if (Blt_TreeViewFindTaggedEntries(tvPtr, objv[i], &info) != TCL_OK) {
7059              Tcl_ResetResult(interp);
7060              Blt_TreeViewDoneTaggedEntries(&info);
7061              continue;
7062 	}
7063 	for (entryPtr = Blt_TreeViewFirstTaggedEntry(&info); entryPtr != NULL;
7064 	     entryPtr = Blt_TreeViewNextTaggedEntry(&info)) {
7065 	    Blt_CreateHashEntry(&nodeTable, (char *)entryPtr->node, &isNew);
7066 	}
7067         Blt_TreeViewDoneTaggedEntries(&info);
7068     }
7069     listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
7070     for (hPtr = Blt_FirstHashEntry(&nodeTable, &cursor); hPtr != NULL;
7071 	 hPtr = Blt_NextHashEntry(&cursor)) {
7072 	node = (Blt_TreeNode)Blt_GetHashKey(&nodeTable, hPtr);
7073 	objPtr = Tcl_NewIntObj(Blt_TreeNodeId(node));
7074 	Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
7075     }
7076     Tcl_SetObjResult(interp, listObjPtr);
7077     Blt_DeleteHashTable(&nodeTable);
7078     return TCL_OK;
7079 }
7080 
7081 static int
TagDefine(tvPtr,interp,tagName)7082 TagDefine(tvPtr, interp, tagName)
7083     TreeView *tvPtr;
7084     Tcl_Interp *interp;
7085     char *tagName;
7086 {
7087     TreeViewEntry *entryPtr;
7088 
7089     if (strcmp(tagName, "root") == 0 || strcmp(tagName, "all") == 0 ||
7090     strcmp(tagName, "nonroot") == 0 || strcmp(tagName, "rootchildren") == 0) {
7091         Tcl_AppendResult(interp, "can't add reserved tag \"", tagName, "\"",
7092             (char *)NULL);
7093             return TCL_ERROR;
7094     }
7095     if (isdigit(UCHAR(tagName[0]))) {
7096         Tcl_AppendResult(interp, "invalid tag \"", tagName,
7097             "\": can't start with digit", (char *)NULL);
7098             return TCL_ERROR;
7099     }
7100     if (strstr(tagName,"->") != NULL) {
7101         Tcl_AppendResult(tvPtr->interp, "invalid tag \"", tagName,
7102             "\": can't contain \"->\"", (char *)NULL);
7103             return TCL_ERROR;
7104     }
7105     if (tagName[0] == '@') {
7106         Tcl_AppendResult(tvPtr->interp, "invalid tag \"", tagName,
7107             "\": can't start with \"@\"", (char *)NULL);
7108             return TCL_ERROR;
7109     }
7110     if (GetEntryFromSpecialId(tvPtr, tagName, &entryPtr) == TCL_OK) {
7111         Tcl_AppendResult(interp, "invalid tag \"", tagName,
7112             "\": is a special id", (char *)NULL);
7113             return TCL_ERROR;
7114     }
7115     return Blt_TreeAddTag(tvPtr->tree, NULL, tagName);
7116 }
7117 
7118 /*
7119  *----------------------------------------------------------------------
7120  *
7121  * TagAddOp --
7122  *
7123  *----------------------------------------------------------------------
7124  */
7125 static int
TagAddOp(tvPtr,interp,objc,objv)7126 TagAddOp(tvPtr, interp, objc, objv)
7127     TreeView *tvPtr;
7128     Tcl_Interp *interp;
7129     int objc;
7130     Tcl_Obj *CONST *objv;
7131 {
7132     TreeViewEntry *entryPtr;
7133     register int i;
7134     char *tagName;
7135     TreeViewTagInfo info = {0};
7136 
7137     tagName = Tcl_GetString(objv[3]);
7138     TagDefine(tvPtr, interp, tagName);
7139     for (i = 4; i < objc; i++) {
7140 	if (Blt_TreeViewFindTaggedEntries(tvPtr, objv[i], &info) != TCL_OK) {
7141 	    return TCL_ERROR;
7142 	}
7143 	for (entryPtr = Blt_TreeViewFirstTaggedEntry(&info); entryPtr != NULL;
7144 	     entryPtr = Blt_TreeViewNextTaggedEntry(&info)) {
7145 	    if (AddTag(tvPtr, entryPtr->node, tagName) != TCL_OK) {
7146                 Blt_TreeViewDoneTaggedEntries(&info);
7147 		return TCL_ERROR;
7148 	    }
7149          }
7150          Blt_TreeViewDoneTaggedEntries(&info);
7151      }
7152     return TCL_OK;
7153 }
7154 
7155 /*
7156  *----------------------------------------------------------------------
7157  *
7158  * TagExistsOp --
7159  *
7160  *----------------------------------------------------------------------
7161  */
7162 static int
TagExistsOp(tvPtr,interp,objc,objv)7163 TagExistsOp(tvPtr, interp, objc, objv)
7164     TreeView *tvPtr;
7165     Tcl_Interp *interp;
7166     int objc;
7167     Tcl_Obj *CONST *objv;
7168 {
7169     TreeViewEntry *entryPtr;
7170     char *tagName;
7171     TreeViewTagInfo info = {0};
7172     int exists = 0;
7173 
7174     if (objc == 4) {
7175 	if (Blt_TreeViewFindTaggedEntries(tvPtr, objv[3], &info) == TCL_OK) {
7176 	    exists = 1;
7177 	} else {
7178 	    Tcl_ResetResult(interp);
7179 	}
7180         Blt_TreeViewDoneTaggedEntries(&info);
7181     } else {
7182         tagName = Tcl_GetString(objv[3]);
7183         if (GetEntryFromObj(tvPtr, objv[4], &entryPtr) != TCL_OK) {
7184             return TCL_ERROR;
7185         }
7186         exists = Blt_TreeHasTag(tvPtr->tree, entryPtr->node, tagName);
7187     }
7188     Tcl_SetObjResult(interp, Tcl_NewIntObj(exists));
7189     return TCL_OK;
7190 }
7191 
7192 
7193 /*
7194  *----------------------------------------------------------------------
7195  *
7196  * TagDeleteOp --
7197  *
7198  *----------------------------------------------------------------------
7199  */
7200 /*ARGSUSED*/
7201 static int
TagDeleteOp(tvPtr,interp,objc,objv)7202 TagDeleteOp(tvPtr, interp, objc, objv)
7203     TreeView *tvPtr;
7204     Tcl_Interp *interp;		/* Not used. */
7205     int objc;
7206     Tcl_Obj *CONST *objv;
7207 {
7208     char *tagName;
7209     int result;
7210     Blt_HashTable *tablePtr;
7211     TreeViewTagInfo info = {0};
7212 
7213     tagName = Tcl_GetString(objv[3]);
7214     tablePtr = Blt_TreeTagHashTable(tvPtr->tree, tagName);
7215     if (tablePtr != NULL) {
7216         register int i;
7217         Blt_HashEntry *hPtr;
7218 	TreeViewEntry *entryPtr;
7219 
7220         for (i = 4; i < objc; i++) {
7221 	    if (Blt_TreeViewFindTaggedEntries(tvPtr, objv[i], &info)!= TCL_OK) {
7222 		return TCL_ERROR;
7223 	    }
7224 	    for (entryPtr = Blt_TreeViewFirstTaggedEntry(&info);
7225 		entryPtr != NULL;
7226 		entryPtr = Blt_TreeViewNextTaggedEntry(&info)) {
7227 	        hPtr = Blt_FindHashEntry(tablePtr, (char *)entryPtr->node);
7228 	        if (hPtr != NULL) {
7229                      result = Blt_TreeTagDelTrace(tvPtr->tree, entryPtr->node, tagName);
7230                      if (result != TCL_OK) {
7231                          if (result != TCL_BREAK) {
7232                              Blt_TreeViewDoneTaggedEntries(&info);
7233                              return TCL_ERROR;
7234                          }
7235                          continue;
7236                      }
7237                      Blt_DeleteHashEntry(tablePtr, hPtr);
7238 	        }
7239 	   }
7240            Blt_TreeViewDoneTaggedEntries(&info);
7241        }
7242     }
7243     return TCL_OK;
7244 }
7245 
7246 /*
7247  *----------------------------------------------------------------------
7248  *
7249  * TagOp --
7250  *
7251  *----------------------------------------------------------------------
7252  */
7253 static Blt_OpSpec tagOps[] = {
7254     {"add", 1, (Blt_Op)TagAddOp, 4, 0, "tag id...",},
7255     {"delete", 2, (Blt_Op)TagDeleteOp, 5, 0, "tag id...",},
7256     {"exists", 2, (Blt_Op)TagExistsOp, 4, 5, "tag ?id?",},
7257     {"forget", 1, (Blt_Op)TagForgetOp, 4, 0, "tag...",},
7258     {"names", 2, (Blt_Op)TagNamesOp, 3, 0, "?id...?",},
7259     {"nodes", 2, (Blt_Op)TagNodesOp, 4, 0, "tag ?tag...?",},
7260 };
7261 
7262 static int nTagOps = sizeof(tagOps) / sizeof(Blt_OpSpec);
7263 
7264 static int
TagOp(tvPtr,interp,objc,objv)7265 TagOp(tvPtr, interp, objc, objv)
7266     TreeView *tvPtr;
7267     Tcl_Interp *interp;
7268     int objc;
7269     Tcl_Obj *CONST *objv;
7270 {
7271     Blt_Op proc;
7272     int result;
7273 
7274     proc = Blt_GetOpFromObj(interp, nTagOps, tagOps, BLT_OP_ARG2, objc, objv,
7275 	0);
7276     if (proc == NULL) {
7277 	return TCL_ERROR;
7278     }
7279     result = (*proc)(tvPtr, interp, objc, objv);
7280     return result;
7281 }
7282 
7283 /*ARGSUSED*/
7284 static int
ToggleOp(tvPtr,interp,objc,objv)7285 ToggleOp(tvPtr, interp, objc, objv)
7286     TreeView *tvPtr;
7287     Tcl_Interp *interp;		/* Not used. */
7288     int objc;
7289     Tcl_Obj *CONST *objv;
7290 {
7291     TreeViewEntry *entryPtr;
7292     TreeViewTagInfo info = {0};
7293     int result = TCL_OK;
7294 
7295     if (Blt_TreeViewFindTaggedEntries(tvPtr, objv[2], &info) != TCL_OK) {
7296 	return TCL_ERROR;
7297     }
7298     for (entryPtr = Blt_TreeViewFirstTaggedEntry(&info); entryPtr != NULL && result == TCL_OK;
7299 	 entryPtr = Blt_TreeViewNextTaggedEntry(&info)) {
7300 	if (entryPtr == NULL) {
7301             Blt_TreeViewDoneTaggedEntries(&info);
7302 	    return TCL_OK;
7303 	}
7304 	if (entryPtr->flags & ENTRY_CLOSED) {
7305 	    result = Blt_TreeViewOpenEntry(tvPtr, entryPtr);
7306 	} else {
7307 	    Blt_TreeViewPruneSelection(tvPtr, entryPtr);
7308 	    if ((tvPtr->focusPtr != NULL) &&
7309 		(Blt_TreeIsAncestor(entryPtr->node, tvPtr->focusPtr->node))) {
7310 		tvPtr->focusPtr = entryPtr;
7311 		Blt_SetFocusItem(tvPtr->bindTable, tvPtr->focusPtr, ITEM_ENTRY);
7312 	    }
7313 	    if ((tvPtr->selAnchorPtr != NULL) &&
7314 		(Blt_TreeIsAncestor(entryPtr->node,
7315 				    tvPtr->selAnchorPtr->node))) {
7316 		tvPtr->selAnchorPtr = NULL;
7317 	    }
7318 	    result = Blt_TreeViewCloseEntry(tvPtr, entryPtr);
7319 	}
7320     }
7321     Blt_TreeViewDoneTaggedEntries(&info);
7322     if (result == TCL_OK) {
7323         tvPtr->flags |= TV_SCROLL;
7324         Blt_TreeViewEventuallyRedraw(tvPtr);
7325     }
7326     return result;
7327 }
7328 
7329 static int
XViewOp(tvPtr,interp,objc,objv)7330 XViewOp(tvPtr, interp, objc, objv)
7331     TreeView *tvPtr;
7332     Tcl_Interp *interp;
7333     int objc;
7334     Tcl_Obj *CONST *objv;
7335 {
7336     int width, worldWidth;
7337 
7338     width = VPORTWIDTH(tvPtr);
7339     worldWidth = tvPtr->worldWidth;
7340     if (objc == 2) {
7341 	double fract;
7342 	Tcl_Obj *listObjPtr;
7343 
7344 	/*
7345 	 * Note that we are bounding the fractions between 0.0 and 1.0
7346 	 * to support the "canvas"-style of scrolling.
7347 	 */
7348 	listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
7349 	fract = (double)tvPtr->xOffset / worldWidth;
7350 	fract = CLAMP(fract, 0.0, 1.0);
7351 	Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(fract));
7352 	fract = (double)(tvPtr->xOffset + width) / worldWidth;
7353 	fract = CLAMP(fract, 0.0, 1.0);
7354 	Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(fract));
7355 	Tcl_SetObjResult(interp, listObjPtr);
7356 	return TCL_OK;
7357     }
7358     if (tvPtr->noScroll) { return TCL_OK; }
7359     if (Blt_GetScrollInfoFromObj(interp, objc - 2, objv + 2, &tvPtr->xOffset,
7360 	    worldWidth, width, tvPtr->xScrollUnits, tvPtr->scrollMode)
7361 	    != TCL_OK) {
7362 	return TCL_ERROR;
7363     }
7364     tvPtr->flags |= TV_XSCROLL;
7365     Blt_TreeViewEventuallyRedraw(tvPtr);
7366     return TCL_OK;
7367 }
7368 
7369 static int
YViewOp(tvPtr,interp,objc,objv)7370 YViewOp(tvPtr, interp, objc, objv)
7371     TreeView *tvPtr;
7372     Tcl_Interp *interp;
7373     int objc;
7374     Tcl_Obj *CONST *objv;
7375 {
7376     int height, worldHeight;
7377 
7378     height = VPORTHEIGHT(tvPtr);
7379     worldHeight = tvPtr->worldHeight;
7380     if (objc == 2) {
7381 	double fract;
7382 	Tcl_Obj *listObjPtr;
7383 
7384 	listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
7385 	/* Report first and last fractions */
7386 	fract = (double)tvPtr->yOffset / worldHeight;
7387 	fract = CLAMP(fract, 0.0, 1.0);
7388 	Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(fract));
7389 	fract = (double)(tvPtr->yOffset + height) / worldHeight;
7390 	fract = CLAMP(fract, 0.0, 1.0);
7391 	Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(fract));
7392 	Tcl_SetObjResult(interp, listObjPtr);
7393 	return TCL_OK;
7394     }
7395     if (tvPtr->noScroll) { return TCL_OK; }
7396     if (Blt_GetScrollInfoFromObj(interp, objc - 2, objv + 2, &tvPtr->yOffset,
7397 	    worldHeight, height, tvPtr->yScrollUnits, tvPtr->scrollMode)
7398 	!= TCL_OK) {
7399 	return TCL_ERROR;
7400     }
7401     tvPtr->flags |= TV_SCROLL;
7402     Blt_TreeViewEventuallyRedraw(tvPtr);
7403     return TCL_OK;
7404 }
7405 
7406 /*
7407  * --------------------------------------------------------------
7408  *
7409  * Blt_TreeViewWidgetInstCmd --
7410  *
7411  * 	This procedure is invoked to process commands on behalf of
7412  *	the treeview widget.
7413  *
7414  * Results:
7415  *	A standard Tcl result.
7416  *
7417  * Side effects:
7418  *	See the user documentation.
7419  *
7420  * --------------------------------------------------------------
7421  */
7422 static Blt_OpSpec treeViewOps[] =
7423 {
7424     {"bbox", 2, (Blt_Op)BboxOp, 3, 0, "tagOrId...",},
7425     {"bind", 2, (Blt_Op)BindOp, 3, 5, "tagName ?sequence command?",},
7426     {"button", 2, (Blt_Op)ButtonOp, 2, 0, "args",},
7427     {"cget", 2, (Blt_Op)CgetOp, 3, 3, "option",},
7428     {"close", 2, (Blt_Op)CloseOp, 2, 0, "?-recurse? ?-parent? tagOrId...",},
7429     {"column", 3, (Blt_Op)Blt_TreeViewColumnOp, 2, 0, "oper args",},
7430     {"configure", 3, (Blt_Op)ConfigureOp, 2, 0, "?option value?...",},
7431     {"curselection", 2, (Blt_Op)CurselectionOp, 2, 2, "",},
7432     {"delete", 1, (Blt_Op)DeleteOp, 2, 0, "tagOrId ?tagOrId...?",},
7433     {"edit", 2, (Blt_Op)EditOp, 3, 0, "?-root|-test|-noscroll|-scroll? ?x y?",},
7434     {"entry", 2, (Blt_Op)EntryOp, 2, 0, "oper args",},
7435     {"find", 3, (Blt_Op)FindOp, 2, 0, "?switches? ?first last?",},
7436     {"focus", 2, (Blt_Op)FocusOp, 2, 3, "?tagOrId?",},
7437     {"get", 1, (Blt_Op)GetOp, 2, 0, "?-full? ?-labels? tagOrId ?tagOrId...?",},
7438     {"hide", 1, (Blt_Op)HideOp, 2, 0, "?switches? ?tagOrId...?",},
7439     {"index", 3, (Blt_Op)IndexOp, 3, 8, "?-at tagOrId? ?-path? ?-quiet? string",},
7440     {"insert", 3, (Blt_Op)InsertOp, 3, 0, "?-at tagOrId? ?-styles styleslist? ?-tags taglist? position label ?label...? ?option value?",},
7441     {"move", 1, (Blt_Op)MoveOp, 5, 5, "tagOrId into|before|after tagOrId",},
7442     {"nearest", 1, (Blt_Op)NearestOp, 4, 7, "?-root? ?-strict? x y ?varName?",},
7443     {"open", 1, (Blt_Op)OpenOp, 2, 0, "?-recurse? ?-parent? tagOrId...",},
7444     {"range", 1, (Blt_Op)RangeOp, 4, 5, "?-open? tagOrId tagOrId",},
7445     {"scan", 2, (Blt_Op)ScanOp, 5, 5, "dragto|mark x y",},
7446     {"see", 3, (Blt_Op)SeeOp, 3, 5, "?-anchor anchor? tagOrId",},
7447     {"selection", 3, (Blt_Op)SelectionOp, 2, 0, "oper args",},
7448     {"show", 2, (Blt_Op)ShowOp, 2, 0, "?switches? ?tagOrId...?",},
7449     {"sort", 2, (Blt_Op)Blt_TreeViewSortOp, 2, 0, "args",},
7450     {"style", 2, (Blt_Op)Blt_TreeViewStyleOp, 2, 0, "args",},
7451     {"tag", 2, (Blt_Op)TagOp, 2, 0, "oper args",},
7452     {"toggle", 2, (Blt_Op)ToggleOp, 3, 3, "tagOrId",},
7453     {"xview", 1, (Blt_Op)XViewOp, 2, 5, "?moveto fract? ?scroll number what?",},
7454     {"yview", 1, (Blt_Op)YViewOp, 2, 5, "?moveto fract? ?scroll number what?",},
7455 };
7456 
7457 static int nTreeViewOps = sizeof(treeViewOps) / sizeof(Blt_OpSpec);
7458 
7459 int
Blt_TreeViewWidgetInstCmd(clientData,interp,objc,objv)7460 Blt_TreeViewWidgetInstCmd(clientData, interp, objc, objv)
7461     ClientData clientData;	/* Information about the widget. */
7462     Tcl_Interp *interp;		/* Interpreter to report errors back to. */
7463     int objc;			/* Number of arguments. */
7464     Tcl_Obj *CONST *objv;	/* Vector of argument strings. */
7465 {
7466     Blt_Op proc;
7467     TreeView *tvPtr = clientData;
7468     int result;
7469 
7470     proc = Blt_GetOpFromObj(interp, nTreeViewOps, treeViewOps, BLT_OP_ARG1,
7471 	objc, objv, 0);
7472     if (proc == NULL) {
7473 	return TCL_ERROR;
7474     }
7475     Blt_TreeViewChanged(tvPtr);
7476     Tcl_Preserve(tvPtr);
7477     result = (*proc) (tvPtr, interp, objc, objv);
7478     Tcl_Release(tvPtr);
7479     return result;
7480 }
7481 
7482 #endif /* NO_TREEVIEW */
7483