source: trunk/kitgen/8.x/blt/generic/bltTreeViewCmd.c@ 187

Last change on this file since 187 was 175, checked in by demin, 12 years ago

initial commit

File size: 130.5 KB
RevLine 
[175]1
2/*
3 * bltTreeViewCmd.c --
4 *
5 * This module implements an hierarchy widget for the BLT toolkit.
6 *
7 * Copyright 1998-1999 Lucent Technologies, Inc.
8 *
9 * Permission to use, copy, modify, and distribute this software and
10 * its documentation for any purpose and without fee is hereby
11 * granted, provided that the above copyright notice appear in all
12 * copies and that both that the copyright notice and warranty
13 * disclaimer appear in supporting documentation, and that the names
14 * of Lucent Technologies or any of their entities not be used in
15 * advertising or publicity pertaining to distribution of the software
16 * without specific, written prior permission.
17 *
18 * Lucent Technologies disclaims all warranties with regard to this
19 * software, including all implied warranties of merchantability and
20 * fitness. In no event shall Lucent Technologies be liable for any
21 * special, indirect or consequential damages or any damages
22 * whatsoever resulting from loss of use, data or profits, whether in
23 * an action of contract, negligence or other tortuous action, arising
24 * out of or in connection with the use or performance of this
25 * software.
26 *
27 * The "treeview" widget was created by George A. Howlett.
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
51static TreeViewCompareProc ExactCompare, GlobCompare, RegexpCompare;
52static TreeViewApplyProc ShowEntryApplyProc, HideEntryApplyProc,
53 MapAncestorsApplyProc, FixSelectionsApplyProc;
54static Tk_LostSelProc LostSelection;
55static TreeViewApplyProc SelectEntryApplyProc;
56
57extern Blt_CustomOption bltTreeViewIconsOption;
58extern Blt_CustomOption bltTreeViewUidOption;
59extern Blt_CustomOption bltTreeViewTreeOption;
60
61extern Blt_ConfigSpec bltTreeViewButtonSpecs[];
62extern Blt_ConfigSpec bltTreeViewSpecs[];
63extern Blt_ConfigSpec bltTreeViewEntrySpecs[];
64
65#define TAG_UNKNOWN (1<<0)
66#define TAG_RESERVED (1<<1)
67#define TAG_USER_DEFINED (1<<2)
68
69#define TAG_SINGLE (1<<3)
70#define TAG_MULTIPLE (1<<4)
71#define TAG_ALL (1<<5)
72
73/*
74 *----------------------------------------------------------------------
75 *
76 * SkipSeparators --
77 *
78 * Moves the character pointer past one of more separators.
79 *
80 * Results:
81 * Returns the updates character pointer.
82 *
83 *----------------------------------------------------------------------
84 */
85static char *
86SkipSeparators(path, separator, length)
87 char *path, *separator;
88 int length;
89{
90 while ((path[0] == separator[0]) &&
91 (strncmp(path, separator, length) == 0)) {
92 path += length;
93 }
94 return path;
95}
96
97/*
98 *----------------------------------------------------------------------
99 *
100 * DeleteNode --
101 *
102 * Delete the node and its descendants. Don't remove the root
103 * node, though. If the root node is specified, simply remove
104 * all its children.
105 *
106 *----------------------------------------------------------------------
107 */
108static void
109DeleteNode(tvPtr, node)
110 TreeView *tvPtr;
111 Blt_TreeNode node;
112{
113 Blt_TreeNode root;
114
115 if (!Blt_TreeTagTableIsShared(tvPtr->tree)) {
116 Blt_TreeClearTags(tvPtr->tree, node);
117 }
118 root = Blt_TreeRootNode(tvPtr->tree);
119 if (node == root) {
120 Blt_TreeNode next;
121 /* Don't delete the root node. Simply clean out the tree. */
122 for (node = Blt_TreeFirstChild(node); node != NULL; node = next) {
123 next = Blt_TreeNextSibling(node);
124 Blt_TreeDeleteNode(tvPtr->tree, node);
125 }
126 } else if (Blt_TreeIsAncestor(root, node)) {
127 Blt_TreeDeleteNode(tvPtr->tree, node);
128 }
129}
130
131/*
132 *----------------------------------------------------------------------
133 *
134 * SplitPath --
135 *
136 * Returns the trailing component of the given path. Trailing
137 * separators are ignored.
138 *
139 * Results:
140 * Returns the string of the tail component.
141 *
142 *----------------------------------------------------------------------
143 */
144static int
145SplitPath(tvPtr, path, depthPtr, compPtrPtr)
146 TreeView *tvPtr;
147 char *path;
148 int *depthPtr;
149 char ***compPtrPtr;
150{
151 int skipLen, pathLen;
152 int depth, listSize;
153 char **components;
154 register char *p;
155 char *sep;
156
157 if (tvPtr->pathSep == SEPARATOR_LIST) {
158 if (Tcl_SplitList(tvPtr->interp, path, depthPtr, compPtrPtr)
159 != TCL_OK) {
160 return TCL_ERROR;
161 }
162 return TCL_OK;
163 }
164 pathLen = strlen(path);
165
166 skipLen = strlen(tvPtr->pathSep);
167 path = SkipSeparators(path, tvPtr->pathSep, skipLen);
168 depth = pathLen / skipLen;
169
170 listSize = (depth + 1) * sizeof(char *);
171 components = Blt_Malloc(listSize + (pathLen + 1));
172 assert(components);
173 p = (char *)components + listSize;
174 strcpy(p, path);
175
176 sep = strstr(p, tvPtr->pathSep);
177 depth = 0;
178 while ((*p != '\0') && (sep != NULL)) {
179 *sep = '\0';
180 components[depth++] = p;
181 p = SkipSeparators(sep + skipLen, tvPtr->pathSep, skipLen);
182 sep = strstr(p, tvPtr->pathSep);
183 }
184 if (*p != '\0') {
185 components[depth++] = p;
186 }
187 components[depth] = NULL;
188 *depthPtr = depth;
189 *compPtrPtr = components;
190 return TCL_OK;
191}
192
193
194static TreeViewEntry *
195LastEntry(tvPtr, entryPtr, mask)
196 TreeView *tvPtr;
197 TreeViewEntry *entryPtr;
198 unsigned int mask;
199{
200 Blt_TreeNode next;
201 TreeViewEntry *nextPtr;
202
203 next = Blt_TreeLastChild(entryPtr->node);
204 while (next != NULL) {
205 nextPtr = Blt_NodeToEntry(tvPtr, next);
206 if ((nextPtr->flags & mask) != mask) {
207 break;
208 }
209 entryPtr = nextPtr;
210 next = Blt_TreeLastChild(next);
211 }
212 return entryPtr;
213}
214
215
216/*
217 *----------------------------------------------------------------------
218 *
219 * ShowEntryApplyProc --
220 *
221 * Results:
222 * Always returns TCL_OK.
223 *
224 *----------------------------------------------------------------------
225 */
226/*ARGSUSED*/
227static int
228ShowEntryApplyProc(tvPtr, entryPtr)
229 TreeView *tvPtr; /* Not used. */
230 TreeViewEntry *entryPtr;
231{
232 entryPtr->flags &= ~ENTRY_HIDDEN;
233 return TCL_OK;
234}
235
236/*
237 *----------------------------------------------------------------------
238 *
239 * HideEntryApplyProc --
240 *
241 * Results:
242 * Always returns TCL_OK.
243 *
244 *----------------------------------------------------------------------
245 */
246/*ARGSUSED*/
247static int
248HideEntryApplyProc(tvPtr, entryPtr)
249 TreeView *tvPtr; /* Not used. */
250 TreeViewEntry *entryPtr;
251{
252 entryPtr->flags |= ENTRY_HIDDEN;
253 return TCL_OK;
254}
255
256static void
257MapAncestors(tvPtr, entryPtr)
258 TreeView *tvPtr;
259 TreeViewEntry *entryPtr;
260{
261 while (entryPtr != tvPtr->rootPtr) {
262 entryPtr = Blt_TreeViewParentEntry(entryPtr);
263 if (entryPtr->flags & (ENTRY_CLOSED | ENTRY_HIDDEN)) {
264 tvPtr->flags |= TV_LAYOUT;
265 entryPtr->flags &= ~(ENTRY_CLOSED | ENTRY_HIDDEN);
266 }
267 }
268}
269
270/*
271 *----------------------------------------------------------------------
272 *
273 * MapAncestorsApplyProc --
274 *
275 * If a node in mapped, then all its ancestors must be mapped also.
276 * This routine traverses upwards and maps each unmapped ancestor.
277 * It's assumed that for any mapped ancestor, all it's ancestors
278 * will already be mapped too.
279 *
280 * Results:
281 * Always returns TCL_OK.
282 *
283 *----------------------------------------------------------------------
284 */
285static int
286MapAncestorsApplyProc(tvPtr, entryPtr)
287 TreeView *tvPtr;
288 TreeViewEntry *entryPtr;
289{
290 /*
291 * Make sure that all the ancestors of this entry are mapped too.
292 */
293 while (entryPtr != tvPtr->rootPtr) {
294 entryPtr = Blt_TreeViewParentEntry(entryPtr);
295 if ((entryPtr->flags & (ENTRY_HIDDEN | ENTRY_CLOSED)) == 0) {
296 break; /* Assume ancestors are also mapped. */
297 }
298 entryPtr->flags &= ~(ENTRY_HIDDEN | ENTRY_CLOSED);
299 }
300 return TCL_OK;
301}
302
303/*
304 *----------------------------------------------------------------------
305 *
306 * FindPath --
307 *
308 * Finds the node designated by the given path. Each path
309 * component is searched for as the tree is traversed.
310 *
311 * A leading character string is trimmed off the path if it
312 * matches the one designated (see the -trimleft option).
313 *
314 * If no separator is designated (see the -separator
315 * configuration option), the path is considered a Tcl list.
316 * Otherwise the each component of the path is separated by a
317 * character string. Leading and trailing separators are
318 * ignored. Multiple separators are treated as one.
319 *
320 * Results:
321 * Returns the pointer to the designated node. If any component
322 * can't be found, NULL is returned.
323 *
324 *----------------------------------------------------------------------
325 */
326static TreeViewEntry *
327FindPath(tvPtr, rootPtr, path)
328 TreeView *tvPtr;
329 TreeViewEntry *rootPtr;
330 char *path;
331{
332 Blt_TreeNode child;
333 char **compArr;
334 char *name;
335 int nComp;
336 register char **p;
337 TreeViewEntry *entryPtr;
338
339 /* Trim off characters that we don't want */
340 if (tvPtr->trimLeft != NULL) {
341 register char *s1, *s2;
342
343 /* Trim off leading character string if one exists. */
344 for (s1 = path, s2 = tvPtr->trimLeft; *s2 != '\0'; s2++, s1++) {
345 if (*s1 != *s2) {
346 break;
347 }
348 }
349 if (*s2 == '\0') {
350 path = s1;
351 }
352 }
353 if (*path == '\0') {
354 return rootPtr;
355 }
356 name = path;
357 entryPtr = rootPtr;
358 if (tvPtr->pathSep == SEPARATOR_NONE) {
359 child = Blt_TreeFindChild(entryPtr->node, name);
360 if (child == NULL) {
361 goto error;
362 }
363 return Blt_NodeToEntry(tvPtr, child);
364 }
365
366 if (SplitPath(tvPtr, path, &nComp, &compArr) != TCL_OK) {
367 return NULL;
368 }
369 for (p = compArr; *p != NULL; p++) {
370 name = *p;
371 child = Blt_TreeFindChild(entryPtr->node, name);
372 if (child == NULL) {
373 Blt_Free(compArr);
374 goto error;
375 }
376 entryPtr = Blt_NodeToEntry(tvPtr, child);
377 }
378 Blt_Free(compArr);
379 return entryPtr;
380 error:
381 {
382 Tcl_DString dString;
383
384 Blt_TreeViewGetFullName(tvPtr, entryPtr, FALSE, &dString);
385 Tcl_AppendResult(tvPtr->interp, "can't find node \"", name,
386 "\" in parent node \"", Tcl_DStringValue(&dString), "\"",
387 (char *)NULL);
388 Tcl_DStringFree(&dString);
389 }
390 return NULL;
391
392}
393
394/*
395 *----------------------------------------------------------------------
396 *
397 * NodeToObj --
398 *
399 * Converts a node pointer to a string representation.
400 * The string contains the node's index which is unique.
401 *
402 * Results:
403 * The string representation of the node is returned. Note that
404 * the string is stored statically, so that callers must save the
405 * string before the next call to this routine overwrites the
406 * static array again.
407 *
408 *----------------------------------------------------------------------
409 */
410static Tcl_Obj *
411NodeToObj(node)
412 Blt_TreeNode node;
413{
414 char string[200];
415
416 sprintf(string, "%d", Blt_TreeNodeId(node));
417 return Tcl_NewStringObj(string, -1);
418}
419
420
421static int
422GetEntryFromSpecialId(tvPtr, string, entryPtrPtr)
423 TreeView *tvPtr;
424 char *string;
425 TreeViewEntry **entryPtrPtr;
426{
427 Blt_TreeNode node;
428 TreeViewEntry *fromPtr, *entryPtr;
429 char c;
430
431 entryPtr = NULL;
432 fromPtr = tvPtr->fromPtr;
433 if (fromPtr == NULL) {
434 fromPtr = tvPtr->focusPtr;
435 }
436 if (fromPtr == NULL) {
437 fromPtr = tvPtr->rootPtr;
438 }
439 c = string[0];
440 if (c == '@') {
441 int x, y;
442
443 if (Blt_GetXY(tvPtr->interp, tvPtr->tkwin, string, &x, &y) == TCL_OK) {
444 *entryPtrPtr = Blt_TreeViewNearestEntry(tvPtr, x, y, TRUE);
445 }
446 } else if ((c == 'b') && (strcmp(string, "bottom") == 0)) {
447 if (tvPtr->flatView) {
448 entryPtr = tvPtr->flatArr[tvPtr->nEntries - 1];
449 } else {
450 entryPtr = LastEntry(tvPtr, tvPtr->rootPtr, ENTRY_MASK);
451 }
452 } else if ((c == 't') && (strcmp(string, "top") == 0)) {
453 if (tvPtr->flatView) {
454 entryPtr = tvPtr->flatArr[0];
455 } else {
456 entryPtr = tvPtr->rootPtr;
457 if (tvPtr->flags & TV_HIDE_ROOT) {
458 entryPtr = Blt_TreeViewNextEntry(entryPtr, ENTRY_MASK);
459 }
460 }
461 } else if ((c == 'e') && (strcmp(string, "end") == 0)) {
462 entryPtr = LastEntry(tvPtr, tvPtr->rootPtr, ENTRY_MASK);
463 } else if ((c == 'a') && (strcmp(string, "anchor") == 0)) {
464 entryPtr = tvPtr->selAnchorPtr;
465 } else if ((c == 'f') && (strcmp(string, "focus") == 0)) {
466 entryPtr = tvPtr->focusPtr;
467 if ((entryPtr == tvPtr->rootPtr) && (tvPtr->flags & TV_HIDE_ROOT)) {
468 entryPtr = Blt_TreeViewNextEntry(tvPtr->rootPtr, ENTRY_MASK);
469 }
470 } else if ((c == 'r') && (strcmp(string, "root") == 0)) {
471 entryPtr = tvPtr->rootPtr;
472 } else if ((c == 'p') && (strcmp(string, "parent") == 0)) {
473 if (fromPtr != tvPtr->rootPtr) {
474 entryPtr = Blt_TreeViewParentEntry(fromPtr);
475 }
476 } else if ((c == 'c') && (strcmp(string, "current") == 0)) {
477 /* Can't trust picked item, if entries have been
478 * added or deleted. */
479 if (!(tvPtr->flags & TV_DIRTY)) {
480 ClientData context;
481
482 context = Blt_GetCurrentContext(tvPtr->bindTable);
483 if ((context == ITEM_ENTRY) ||
484 (context == ITEM_ENTRY_BUTTON) ||
485 (context >= ITEM_STYLE)) {
486 entryPtr = Blt_GetCurrentItem(tvPtr->bindTable);
487 }
488 }
489 } else if ((c == 'u') && (strcmp(string, "up") == 0)) {
490 entryPtr = fromPtr;
491 if (tvPtr->flatView) {
492 int i;
493
494 i = entryPtr->flatIndex - 1;
495 if (i >= 0) {
496 entryPtr = tvPtr->flatArr[i];
497 }
498 } else {
499 entryPtr = Blt_TreeViewPrevEntry(fromPtr, ENTRY_MASK);
500 if (entryPtr == NULL) {
501 entryPtr = fromPtr;
502 }
503 if ((entryPtr == tvPtr->rootPtr) &&
504 (tvPtr->flags & TV_HIDE_ROOT)) {
505 entryPtr = Blt_TreeViewNextEntry(entryPtr, ENTRY_MASK);
506 }
507 }
508 } else if ((c == 'd') && (strcmp(string, "down") == 0)) {
509 entryPtr = fromPtr;
510 if (tvPtr->flatView) {
511 int i;
512
513 i = entryPtr->flatIndex + 1;
514 if (i < tvPtr->nEntries) {
515 entryPtr = tvPtr->flatArr[i];
516 }
517 } else {
518 entryPtr = Blt_TreeViewNextEntry(fromPtr, ENTRY_MASK);
519 if (entryPtr == NULL) {
520 entryPtr = fromPtr;
521 }
522 if ((entryPtr == tvPtr->rootPtr) &&
523 (tvPtr->flags & TV_HIDE_ROOT)) {
524 entryPtr = Blt_TreeViewNextEntry(entryPtr, ENTRY_MASK);
525 }
526 }
527 } else if (((c == 'l') && (strcmp(string, "last") == 0)) ||
528 ((c == 'p') && (strcmp(string, "prev") == 0))) {
529 entryPtr = fromPtr;
530 if (tvPtr->flatView) {
531 int i;
532
533 i = entryPtr->flatIndex - 1;
534 if (i < 0) {
535 i = tvPtr->nEntries - 1;
536 }
537 entryPtr = tvPtr->flatArr[i];
538 } else {
539 entryPtr = Blt_TreeViewPrevEntry(fromPtr, ENTRY_MASK);
540 if (entryPtr == NULL) {
541 entryPtr = LastEntry(tvPtr, tvPtr->rootPtr, ENTRY_MASK);
542 }
543 if ((entryPtr == tvPtr->rootPtr) &&
544 (tvPtr->flags & TV_HIDE_ROOT)) {
545 entryPtr = Blt_TreeViewNextEntry(entryPtr, ENTRY_MASK);
546 }
547 }
548 } else if ((c == 'n') && (strcmp(string, "next") == 0)) {
549 entryPtr = fromPtr;
550 if (tvPtr->flatView) {
551 int i;
552
553 i = entryPtr->flatIndex + 1;
554 if (i >= tvPtr->nEntries) {
555 i = 0;
556 }
557 entryPtr = tvPtr->flatArr[i];
558 } else {
559 entryPtr = Blt_TreeViewNextEntry(fromPtr, ENTRY_MASK);
560 if (entryPtr == NULL) {
561 if (tvPtr->flags & TV_HIDE_ROOT) {
562 entryPtr = Blt_TreeViewNextEntry(tvPtr->rootPtr,ENTRY_MASK);
563 } else {
564 entryPtr = tvPtr->rootPtr;
565 }
566 }
567 }
568 } else if ((c == 'n') && (strcmp(string, "nextsibling") == 0)) {
569 node = Blt_TreeNextSibling(fromPtr->node);
570 if (node != NULL) {
571 entryPtr = Blt_NodeToEntry(tvPtr, node);
572 }
573 } else if ((c == 'p') && (strcmp(string, "prevsibling") == 0)) {
574 node = Blt_TreePrevSibling(fromPtr->node);
575 if (node != NULL) {
576 entryPtr = Blt_NodeToEntry(tvPtr, node);
577 }
578 } else if ((c == 'v') && (strcmp(string, "view.top") == 0)) {
579 if (tvPtr->nVisible > 0) {
580 entryPtr = tvPtr->visibleArr[0];
581 }
582 } else if ((c == 'v') && (strcmp(string, "view.bottom") == 0)) {
583 if (tvPtr->nVisible > 0) {
584 entryPtr = tvPtr->visibleArr[tvPtr->nVisible - 1];
585 }
586 } else {
587 return TCL_ERROR;
588 }
589 *entryPtrPtr = entryPtr;
590 return TCL_OK;
591}
592
593static int
594GetTagInfo(tvPtr, tagName, infoPtr)
595 TreeView *tvPtr;
596 char *tagName;
597 TreeViewTagInfo *infoPtr;
598{
599
600 infoPtr->tagType = TAG_RESERVED | TAG_SINGLE;
601 infoPtr->entryPtr = NULL;
602
603 if (strcmp(tagName, "all") == 0) {
604 infoPtr->entryPtr = tvPtr->rootPtr;
605 infoPtr->tagType |= TAG_ALL;
606 } else {
607 Blt_HashTable *tablePtr;
608
609 tablePtr = Blt_TreeTagHashTable(tvPtr->tree, tagName);
610 if (tablePtr != NULL) {
611 Blt_HashEntry *hPtr;
612
613 infoPtr->tagType = TAG_USER_DEFINED; /* Empty tags are not
614 * an error. */
615 hPtr = Blt_FirstHashEntry(tablePtr, &infoPtr->cursor);
616 if (hPtr != NULL) {
617 Blt_TreeNode node;
618
619 node = Blt_GetHashValue(hPtr);
620 infoPtr->entryPtr = Blt_NodeToEntry(tvPtr, node);
621 if (tablePtr->numEntries > 1) {
622 infoPtr->tagType |= TAG_MULTIPLE;
623 }
624 }
625 } else {
626 infoPtr->tagType = TAG_UNKNOWN;
627 Tcl_AppendResult(tvPtr->interp, "can't find tag or id \"", tagName,
628 "\" in \"", Tk_PathName(tvPtr->tkwin), "\"", (char *)NULL);
629 return TCL_ERROR;
630 }
631 }
632 return TCL_OK;
633}
634
635/*ARGSUSED*/
636void
637Blt_TreeViewGetTags(interp, tvPtr, entryPtr, list)
638 Tcl_Interp *interp; /* Not used. */
639 TreeView *tvPtr;
640 TreeViewEntry *entryPtr;
641 Blt_List list;
642{
643 Blt_HashEntry *hPtr;
644 Blt_HashSearch cursor;
645 Blt_TreeTagEntry *tPtr;
646
647 for (hPtr = Blt_TreeFirstTag(tvPtr->tree, &cursor); hPtr != NULL;
648 hPtr = Blt_NextHashEntry(&cursor)) {
649 tPtr = Blt_GetHashValue(hPtr);
650 hPtr = Blt_FindHashEntry(&tPtr->nodeTable, (char *)entryPtr->node);
651 if (hPtr != NULL) {
652 Blt_ListAppend(list, Blt_TreeViewGetUid(tvPtr, tPtr->tagName),0);
653 }
654 }
655}
656
657/*
658 *----------------------------------------------------------------------
659 *
660 * AddTag --
661 *
662 *----------------------------------------------------------------------
663 */
664static int
665AddTag(tvPtr, node, tagName)
666 TreeView *tvPtr;
667 Blt_TreeNode node;
668 char *tagName;
669{
670 TreeViewEntry *entryPtr;
671
672 if (strcmp(tagName, "root") == 0) {
673 Tcl_AppendResult(tvPtr->interp, "can't add reserved tag \"",
674 tagName, "\"", (char *)NULL);
675 return TCL_ERROR;
676 }
677 if (isdigit(UCHAR(tagName[0]))) {
678 Tcl_AppendResult(tvPtr->interp, "invalid tag \"", tagName,
679 "\": can't start with digit", (char *)NULL);
680 return TCL_ERROR;
681 }
682 if (isdigit(UCHAR(tagName[0]))) {
683 Tcl_AppendResult(tvPtr->interp, "invalid tag \"", tagName,
684 "\": can't start with digit", (char *)NULL);
685 return TCL_ERROR;
686 }
687 if (tagName[0] == '@') {
688 Tcl_AppendResult(tvPtr->interp, "invalid tag \"", tagName,
689 "\": can't start with \"@\"", (char *)NULL);
690 return TCL_ERROR;
691 }
692 tvPtr->fromPtr = NULL;
693 if (GetEntryFromSpecialId(tvPtr, tagName, &entryPtr) == TCL_OK) {
694 Tcl_AppendResult(tvPtr->interp, "invalid tag \"", tagName,
695 "\": is a special id", (char *)NULL);
696 return TCL_ERROR;
697 }
698 /* Add the tag to the node. */
699 Blt_TreeAddTag(tvPtr->tree, node, tagName);
700 return TCL_OK;
701}
702
703TreeViewEntry *
704Blt_TreeViewFirstTaggedEntry(infoPtr)
705 TreeViewTagInfo *infoPtr;
706{
707 return infoPtr->entryPtr;
708}
709
710int
711Blt_TreeViewFindTaggedEntries(tvPtr, objPtr, infoPtr)
712 TreeView *tvPtr;
713 Tcl_Obj *objPtr;
714 TreeViewTagInfo *infoPtr;
715{
716 char *tagName;
717 TreeViewEntry *entryPtr;
718
719 tagName = Tcl_GetString(objPtr);
720 tvPtr->fromPtr = NULL;
721 if (isdigit(UCHAR(tagName[0]))) {
722 int inode;
723 Blt_TreeNode node;
724
725 if (Tcl_GetIntFromObj(tvPtr->interp, objPtr, &inode) != TCL_OK) {
726 return TCL_ERROR;
727 }
728 node = Blt_TreeGetNode(tvPtr->tree, inode);
729 infoPtr->entryPtr = Blt_NodeToEntry(tvPtr, node);
730 infoPtr->tagType = (TAG_RESERVED | TAG_SINGLE);
731 } else if (GetEntryFromSpecialId(tvPtr, tagName, &entryPtr) == TCL_OK) {
732 infoPtr->entryPtr = entryPtr;
733 infoPtr->tagType = (TAG_RESERVED | TAG_SINGLE);
734 } else {
735 if (GetTagInfo(tvPtr, tagName, infoPtr) != TCL_OK) {
736 return TCL_ERROR;
737 }
738 }
739 return TCL_OK;
740}
741
742TreeViewEntry *
743Blt_TreeViewNextTaggedEntry(infoPtr)
744 TreeViewTagInfo *infoPtr;
745{
746 TreeViewEntry *entryPtr;
747
748 entryPtr = NULL;
749 if (infoPtr->entryPtr != NULL) {
750 TreeView *tvPtr = infoPtr->entryPtr->tvPtr;
751
752 if (infoPtr->tagType & TAG_ALL) {
753 entryPtr = Blt_TreeViewNextEntry(infoPtr->entryPtr, 0);
754 } else if (infoPtr->tagType & TAG_MULTIPLE) {
755 Blt_HashEntry *hPtr;
756
757 hPtr = Blt_NextHashEntry(&infoPtr->cursor);
758 if (hPtr != NULL) {
759 Blt_TreeNode node;
760
761 node = Blt_GetHashValue(hPtr);
762 entryPtr = Blt_NodeToEntry(tvPtr, node);
763 }
764 }
765 infoPtr->entryPtr = entryPtr;
766 }
767 return entryPtr;
768}
769
770/*
771 *----------------------------------------------------------------------
772 *
773 * GetEntryFromObj2 --
774 *
775 * Converts a string into node pointer. The string may be in one
776 * of the following forms:
777 *
778 * NNN - inode.
779 * "active" - Currently active node.
780 * "anchor" - anchor of selected region.
781 * "current" - Currently picked node in bindtable.
782 * "focus" - The node currently with focus.
783 * "root" - Root node.
784 * "end" - Last open node in the entire hierarchy.
785 * "next" - Next open node from the currently active
786 * node. Wraps around back to top.
787 * "last" - Previous open node from the currently active
788 * node. Wraps around back to bottom.
789 * "up" - Next open node from the currently active
790 * node. Does not wrap around.
791 * "down" - Previous open node from the currently active
792 * node. Does not wrap around.
793 * "nextsibling" - Next sibling of the current node.
794 * "prevsibling" - Previous sibling of the current node.
795 * "parent" - Parent of the current node.
796 * "view.top" - Top of viewport.
797 * "view.bottom" - Bottom of viewport.
798 * @x,y - Closest node to the specified X-Y position.
799 *
800 * Results:
801 * If the string is successfully converted, TCL_OK is returned.
802 * The pointer to the node is returned via nodePtr.
803 * Otherwise, TCL_ERROR is returned and an error message is left
804 * in interpreter's result field.
805 *
806 *----------------------------------------------------------------------
807 */
808static int
809GetEntryFromObj2(tvPtr, objPtr, entryPtrPtr)
810 TreeView *tvPtr;
811 Tcl_Obj *objPtr;
812 TreeViewEntry **entryPtrPtr;
813{
814 Tcl_Interp *interp;
815 char *string;
816 TreeViewTagInfo info;
817
818 interp = tvPtr->interp;
819
820 string = Tcl_GetString(objPtr);
821 *entryPtrPtr = NULL;
822 if (isdigit(UCHAR(string[0]))) {
823 Blt_TreeNode node;
824 int inode;
825
826 if (Tcl_GetIntFromObj(interp, objPtr, &inode) != TCL_OK) {
827 return TCL_ERROR;
828 }
829 node = Blt_TreeGetNode(tvPtr->tree, inode);
830 if (node != NULL) {
831 *entryPtrPtr = Blt_NodeToEntry(tvPtr, node);
832 }
833 return TCL_OK; /* Node Id. */
834 }
835 if (GetEntryFromSpecialId(tvPtr, string, entryPtrPtr) == TCL_OK) {
836 return TCL_OK; /* Special Id. */
837 }
838 if (GetTagInfo(tvPtr, string, &info) != TCL_OK) {
839 return TCL_ERROR;
840 }
841 if (info.tagType & TAG_MULTIPLE) {
842 Tcl_AppendResult(interp, "more than one entry tagged as \"", string,
843 "\"", (char *)NULL);
844 return TCL_ERROR;
845 }
846 *entryPtrPtr = info.entryPtr;
847 return TCL_OK; /* Singleton tag. */
848}
849
850static int
851GetEntryFromObj(tvPtr, objPtr, entryPtrPtr)
852 TreeView *tvPtr;
853 Tcl_Obj *objPtr;
854 TreeViewEntry **entryPtrPtr;
855{
856 tvPtr->fromPtr = NULL;
857 return GetEntryFromObj2(tvPtr, objPtr, entryPtrPtr);
858}
859
860/*
861 *----------------------------------------------------------------------
862 *
863 * Blt_TreeViewGetEntry --
864 *
865 * Returns an entry based upon its index.
866 *
867 * Results:
868 * If the string is successfully converted, TCL_OK is returned.
869 * The pointer to the node is returned via nodePtr.
870 * Otherwise, TCL_ERROR is returned and an error message is left
871 * in interpreter's result field.
872 *
873 *----------------------------------------------------------------------
874 */
875int
876Blt_TreeViewGetEntry(tvPtr, objPtr, entryPtrPtr)
877 TreeView *tvPtr;
878 Tcl_Obj *objPtr;
879 TreeViewEntry **entryPtrPtr;
880{
881 TreeViewEntry *entryPtr;
882
883 if (GetEntryFromObj(tvPtr, objPtr, &entryPtr) != TCL_OK) {
884 return TCL_ERROR;
885 }
886 if (entryPtr == NULL) {
887 Tcl_ResetResult(tvPtr->interp);
888 Tcl_AppendResult(tvPtr->interp, "can't find entry \"",
889 Tcl_GetString(objPtr), "\" in \"", Tk_PathName(tvPtr->tkwin),
890 "\"", (char *)NULL);
891 return TCL_ERROR;
892 }
893 *entryPtrPtr = entryPtr;
894 return TCL_OK;
895}
896
897static Blt_TreeNode
898GetNthNode(parent, position)
899 Blt_TreeNode parent;
900 int position;
901{
902 Blt_TreeNode node;
903 int count;
904
905 count = 0;
906 for(node = Blt_TreeFirstChild(parent); node != NULL;
907 node = Blt_TreeNextSibling(node)) {
908 if (count == position) {
909 return node;
910 }
911 }
912 return Blt_TreeLastChild(parent);
913}
914
915static TreeViewEntry *
916GetNthEntry(
917 TreeViewEntry *parentPtr,
918 int position,
919 unsigned int mask)
920{
921 TreeViewEntry *entryPtr;
922 int count;
923
924 count = 0;
925 for(entryPtr = Blt_TreeViewFirstChild(parentPtr, mask); entryPtr != NULL;
926 entryPtr = Blt_TreeViewNextSibling(entryPtr, mask)) {
927 if (count == position) {
928 return entryPtr;
929 }
930 }
931 return Blt_TreeViewLastChild(parentPtr, mask);
932}
933
934/*
935 * Preprocess the command string for percent substitution.
936 */
937void
938Blt_TreeViewPercentSubst(tvPtr, entryPtr, command, resultPtr)
939 TreeView *tvPtr;
940 TreeViewEntry *entryPtr;
941 char *command;
942 Tcl_DString *resultPtr;
943{
944 register char *last, *p;
945 char *fullName;
946 Tcl_DString dString;
947
948 /*
949 * Get the full path name of the node, in case we need to
950 * substitute for it.
951 */
952 fullName = Blt_TreeViewGetFullName(tvPtr, entryPtr, TRUE, &dString);
953 Tcl_DStringInit(resultPtr);
954 /* Append the widget name and the node .t 0 */
955 for (last = p = command; *p != '\0'; p++) {
956 if (*p == '%') {
957 char *string;
958 char buf[3];
959
960 if (p > last) {
961 *p = '\0';
962 Tcl_DStringAppend(resultPtr, last, -1);
963 *p = '%';
964 }
965 switch (*(p + 1)) {
966 case '%': /* Percent sign */
967 string = "%";
968 break;
969 case 'W': /* Widget name */
970 string = Tk_PathName(tvPtr->tkwin);
971 break;
972 case 'P': /* Full pathname */
973 string = fullName;
974 break;
975 case 'p': /* Name of the node */
976 string = GETLABEL(entryPtr);
977 break;
978 case '#': /* Node identifier */
979 string = Blt_Itoa(Blt_TreeNodeId(entryPtr->node));
980 break;
981 default:
982 if (*(p + 1) == '\0') {
983 p--;
984 }
985 buf[0] = *p, buf[1] = *(p + 1), buf[2] = '\0';
986 string = buf;
987 break;
988 }
989 Tcl_DStringAppend(resultPtr, string, -1);
990 p++;
991 last = p + 1;
992 }
993 }
994 if (p > last) {
995 *p = '\0';
996 Tcl_DStringAppend(resultPtr, last, -1);
997 }
998 Tcl_DStringFree(&dString);
999}
1000
1001/*
1002 *----------------------------------------------------------------------
1003 *
1004 * SelectEntryApplyProc --
1005 *
1006 * Sets the selection flag for a node. The selection flag is
1007 * set/cleared/toggled based upon the flag set in the treeview
1008 * widget.
1009 *
1010 * Results:
1011 * Always returns TCL_OK.
1012 *
1013 *----------------------------------------------------------------------
1014 */
1015static int
1016SelectEntryApplyProc(tvPtr, entryPtr)
1017 TreeView *tvPtr;
1018 TreeViewEntry *entryPtr;
1019{
1020 Blt_HashEntry *hPtr;
1021
1022 switch (tvPtr->flags & TV_SELECT_MASK) {
1023 case TV_SELECT_CLEAR:
1024 Blt_TreeViewDeselectEntry(tvPtr, entryPtr);
1025 break;
1026
1027 case TV_SELECT_SET:
1028 Blt_TreeViewSelectEntry(tvPtr, entryPtr);
1029 break;
1030
1031 case TV_SELECT_TOGGLE:
1032 hPtr = Blt_FindHashEntry(&tvPtr->selectTable, (char *)entryPtr);
1033 if (hPtr != NULL) {
1034 Blt_TreeViewDeselectEntry(tvPtr, entryPtr);
1035 } else {
1036 Blt_TreeViewSelectEntry(tvPtr, entryPtr);
1037 }
1038 break;
1039 }
1040 return TCL_OK;
1041}
1042
1043/*
1044 *----------------------------------------------------------------------
1045 *
1046 * EventuallyInvokeSelectCmd --
1047 *
1048 * Queues a request to execute the -selectcommand code associated
1049 * with the widget at the next idle point. Invoked whenever the
1050 * selection changes.
1051 *
1052 * Results:
1053 * None.
1054 *
1055 * Side effects:
1056 * Tcl code gets executed for some application-specific task.
1057 *
1058 *----------------------------------------------------------------------
1059 */
1060static void
1061EventuallyInvokeSelectCmd(tvPtr)
1062 TreeView *tvPtr;
1063{
1064 if (!(tvPtr->flags & TV_SELECT_PENDING)) {
1065 tvPtr->flags |= TV_SELECT_PENDING;
1066 Tcl_DoWhenIdle(Blt_TreeViewSelectCmdProc, tvPtr);
1067 }
1068}
1069
1070/*
1071 *----------------------------------------------------------------------
1072 *
1073 * Blt_TreeViewPruneSelection --
1074 *
1075 * The root entry being deleted or closed. Deselect any of its
1076 * descendants that are currently selected.
1077 *
1078 * Results:
1079 * None.
1080 *
1081 * Side effects:
1082 * If any of the entry's descendants are deselected the widget
1083 * is redrawn and the a selection command callback is invoked
1084 * (if there's one configured).
1085 *
1086 *----------------------------------------------------------------------
1087 */
1088void
1089Blt_TreeViewPruneSelection(tvPtr, rootPtr)
1090 TreeView *tvPtr;
1091 TreeViewEntry *rootPtr;
1092{
1093 Blt_ChainLink *linkPtr, *nextPtr;
1094 TreeViewEntry *entryPtr;
1095 int selectionChanged;
1096
1097 /*
1098 * Check if any of the currently selected entries are a descendant
1099 * of of the current root entry. Deselect the entry and indicate
1100 * that the treeview widget needs to be redrawn.
1101 */
1102 selectionChanged = FALSE;
1103 for (linkPtr = Blt_ChainFirstLink(tvPtr->selChainPtr); linkPtr != NULL;
1104 linkPtr = nextPtr) {
1105 nextPtr = Blt_ChainNextLink(linkPtr);
1106 entryPtr = Blt_ChainGetValue(linkPtr);
1107 if (Blt_TreeIsAncestor(rootPtr->node, entryPtr->node)) {
1108 Blt_TreeViewDeselectEntry(tvPtr, entryPtr);
1109 selectionChanged = TRUE;
1110 }
1111 }
1112 if (selectionChanged) {
1113 Blt_TreeViewEventuallyRedraw(tvPtr);
1114 if (tvPtr->selectCmd != NULL) {
1115 EventuallyInvokeSelectCmd(tvPtr);
1116 }
1117 }
1118}
1119
1120
1121/*
1122 * --------------------------------------------------------------
1123 *
1124 * TreeView operations
1125 *
1126 * --------------------------------------------------------------
1127 */
1128
1129/*ARGSUSED*/
1130static int
1131FocusOp(tvPtr, interp, objc, objv)
1132 TreeView *tvPtr;
1133 Tcl_Interp *interp;
1134 int objc;
1135 Tcl_Obj *CONST *objv;
1136{
1137 if (objc == 3) {
1138 TreeViewEntry *entryPtr;
1139
1140 if (GetEntryFromObj(tvPtr, objv[2], &entryPtr) != TCL_OK) {
1141 return TCL_ERROR;
1142 }
1143 if ((entryPtr != NULL) && (entryPtr != tvPtr->focusPtr)) {
1144 if (entryPtr->flags & ENTRY_HIDDEN) {
1145 /* Doesn't make sense to set focus to a node you can't see. */
1146 MapAncestors(tvPtr, entryPtr);
1147 }
1148 /* Changing focus can only affect the visible entries. The
1149 * entry layout stays the same. */
1150 if (tvPtr->focusPtr != NULL) {
1151 tvPtr->focusPtr->flags |= ENTRY_REDRAW;
1152 }
1153 entryPtr->flags |= ENTRY_REDRAW;
1154 tvPtr->flags |= TV_SCROLL;
1155 tvPtr->focusPtr = entryPtr;
1156 }
1157 Blt_TreeViewEventuallyRedraw(tvPtr);
1158 }
1159 Blt_SetFocusItem(tvPtr->bindTable, tvPtr->focusPtr, ITEM_ENTRY);
1160 if (tvPtr->focusPtr != NULL) {
1161 Tcl_SetObjResult(interp, NodeToObj(tvPtr->focusPtr->node));
1162 }
1163 return TCL_OK;
1164}
1165
1166/*
1167 *----------------------------------------------------------------------
1168 *
1169 * BboxOp --
1170 *
1171 *----------------------------------------------------------------------
1172 */
1173/*ARGSUSED*/
1174static int
1175BboxOp(tvPtr, interp, objc, objv)
1176 TreeView *tvPtr;
1177 Tcl_Interp *interp;
1178 int objc; /* Not used. */
1179 Tcl_Obj *CONST *objv;
1180{
1181 register int i;
1182 TreeViewEntry *entryPtr;
1183 int width, height, yBot;
1184 int left, top, right, bottom;
1185 int screen;
1186 int lWidth;
1187 char *string;
1188
1189 if (tvPtr->flags & TV_LAYOUT) {
1190 /*
1191 * The layout is dirty. Recompute it now, before we use the
1192 * world dimensions. But remember, the "bbox" operation isn't
1193 * valid for hidden entries (since they're not visible, they
1194 * don't have world coordinates).
1195 */
1196 Blt_TreeViewComputeLayout(tvPtr);
1197 }
1198 left = tvPtr->worldWidth;
1199 top = tvPtr->worldHeight;
1200 right = bottom = 0;
1201
1202 screen = FALSE;
1203 string = Tcl_GetString(objv[2]);
1204 if ((string[0] == '-') && (strcmp(string, "-screen") == 0)) {
1205 screen = TRUE;
1206 objc--, objv++;
1207 }
1208 for (i = 2; i < objc; i++) {
1209 string = Tcl_GetString(objv[i]);
1210 if ((string[0] == 'a') && (strcmp(string, "all") == 0)) {
1211 left = top = 0;
1212 right = tvPtr->worldWidth;
1213 bottom = tvPtr->worldHeight;
1214 break;
1215 }
1216 if (GetEntryFromObj(tvPtr, objv[i], &entryPtr) != TCL_OK) {
1217 return TCL_ERROR;
1218 }
1219 if (entryPtr == NULL) {
1220 continue;
1221 }
1222 if (entryPtr->flags & ENTRY_HIDDEN) {
1223 continue;
1224 }
1225 yBot = entryPtr->worldY + entryPtr->height;
1226 height = VPORTHEIGHT(tvPtr);
1227 if ((yBot <= tvPtr->yOffset) &&
1228 (entryPtr->worldY >= (tvPtr->yOffset + height))) {
1229 continue;
1230 }
1231 if (bottom < yBot) {
1232 bottom = yBot;
1233 }
1234 if (top > entryPtr->worldY) {
1235 top = entryPtr->worldY;
1236 }
1237 lWidth = ICONWIDTH(DEPTH(tvPtr, entryPtr->node));
1238 if (right < (entryPtr->worldX + entryPtr->width + lWidth)) {
1239 right = (entryPtr->worldX + entryPtr->width + lWidth);
1240 }
1241 if (left > entryPtr->worldX) {
1242 left = entryPtr->worldX;
1243 }
1244 }
1245
1246 if (screen) {
1247 width = VPORTWIDTH(tvPtr);
1248 height = VPORTHEIGHT(tvPtr);
1249 /*
1250 * Do a min-max text for the intersection of the viewport and
1251 * the computed bounding box. If there is no intersection, return
1252 * the empty string.
1253 */
1254 if ((right < tvPtr->xOffset) || (bottom < tvPtr->yOffset) ||
1255 (left >= (tvPtr->xOffset + width)) ||
1256 (top >= (tvPtr->yOffset + height))) {
1257 return TCL_OK;
1258 }
1259 /* Otherwise clip the coordinates at the view port boundaries. */
1260 if (left < tvPtr->xOffset) {
1261 left = tvPtr->xOffset;
1262 } else if (right > (tvPtr->xOffset + width)) {
1263 right = tvPtr->xOffset + width;
1264 }
1265 if (top < tvPtr->yOffset) {
1266 top = tvPtr->yOffset;
1267 } else if (bottom > (tvPtr->yOffset + height)) {
1268 bottom = tvPtr->yOffset + height;
1269 }
1270 left = SCREENX(tvPtr, left), top = SCREENY(tvPtr, top);
1271 right = SCREENX(tvPtr, right), bottom = SCREENY(tvPtr, bottom);
1272 }
1273 if ((left < right) && (top < bottom)) {
1274 Tcl_Obj *listObjPtr;
1275
1276 listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
1277 Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewIntObj(left));
1278 Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewIntObj(top));
1279 Tcl_ListObjAppendElement(interp, listObjPtr,
1280 Tcl_NewIntObj(right - left));
1281 Tcl_ListObjAppendElement(interp, listObjPtr,
1282 Tcl_NewIntObj(bottom - top));
1283 Tcl_SetObjResult(interp, listObjPtr);
1284 }
1285 return TCL_OK;
1286}
1287
1288static void
1289DrawButton(tvPtr, entryPtr)
1290 TreeView *tvPtr;
1291 TreeViewEntry *entryPtr;
1292{
1293 Drawable drawable;
1294 int sx, sy, dx, dy;
1295 int width, height;
1296 int left, right, top, bottom;
1297
1298 dx = SCREENX(tvPtr, entryPtr->worldX) + entryPtr->buttonX;
1299 dy = SCREENY(tvPtr, entryPtr->worldY) + entryPtr->buttonY;
1300 width = tvPtr->button.width;
1301 height = tvPtr->button.height;
1302
1303 top = tvPtr->titleHeight + tvPtr->inset;
1304 bottom = Tk_Height(tvPtr->tkwin) - tvPtr->inset;
1305 left = tvPtr->inset;
1306 right = Tk_Width(tvPtr->tkwin) - tvPtr->inset;
1307
1308 if (((dx + width) < left) || (dx > right) ||
1309 ((dy + height) < top) || (dy > bottom)) {
1310 return; /* Value is clipped. */
1311 }
1312 drawable = Tk_GetPixmap(tvPtr->display, Tk_WindowId(tvPtr->tkwin),
1313 width, height, Tk_Depth(tvPtr->tkwin));
1314 /* Draw the background of the value. */
1315 Blt_TreeViewDrawButton(tvPtr, entryPtr, drawable, 0, 0);
1316
1317 /* Clip the drawable if necessary */
1318 sx = sy = 0;
1319 if (dx < left) {
1320 width -= left - dx;
1321 sx += left - dx;
1322 dx = left;
1323 }
1324 if ((dx + width) >= right) {
1325 width -= (dx + width) - right;
1326 }
1327 if (dy < top) {
1328 height -= top - dy;
1329 sy += top - dy;
1330 dy = top;
1331 }
1332 if ((dy + height) >= bottom) {
1333 height -= (dy + height) - bottom;
1334 }
1335 XCopyArea(tvPtr->display, drawable, Tk_WindowId(tvPtr->tkwin),
1336 tvPtr->lineGC, sx, sy, width, height, dx, dy);
1337 Tk_FreePixmap(tvPtr->display, drawable);
1338}
1339
1340/*
1341 *----------------------------------------------------------------------
1342 *
1343 * ButtonActivateOp --
1344 *
1345 * Selects the button to appear active.
1346 *
1347 *----------------------------------------------------------------------
1348 */
1349/*ARGSUSED*/
1350static int
1351ButtonActivateOp(tvPtr, interp, objc, objv)
1352 TreeView *tvPtr;
1353 Tcl_Interp *interp;
1354 int objc; /* Not used. */
1355 Tcl_Obj *CONST *objv;
1356{
1357 TreeViewEntry *oldPtr, *newPtr;
1358 char *string;
1359
1360 string = Tcl_GetString(objv[3]);
1361 if (string[0] == '\0') {
1362 newPtr = NULL;
1363 } else if (GetEntryFromObj(tvPtr, objv[3], &newPtr) != TCL_OK) {
1364 return TCL_ERROR;
1365 }
1366 if (tvPtr->treeColumn.hidden) {
1367 return TCL_OK;
1368 }
1369 if ((newPtr != NULL) && !(newPtr->flags & ENTRY_HAS_BUTTON)) {
1370 newPtr = NULL;
1371 }
1372 oldPtr = tvPtr->activeButtonPtr;
1373 tvPtr->activeButtonPtr = newPtr;
1374 if (!(tvPtr->flags & TV_REDRAW) && (newPtr != oldPtr)) {
1375 if ((oldPtr != NULL) && (oldPtr != tvPtr->rootPtr)) {
1376 DrawButton(tvPtr, oldPtr);
1377 }
1378 if ((newPtr != NULL) && (newPtr != tvPtr->rootPtr)) {
1379 DrawButton(tvPtr, newPtr);
1380 }
1381 }
1382 return TCL_OK;
1383}
1384
1385/*
1386 *----------------------------------------------------------------------
1387 *
1388 * ButtonBindOp --
1389 *
1390 * .t bind tag sequence command
1391 *
1392 *----------------------------------------------------------------------
1393 */
1394/*ARGSUSED*/
1395static int
1396ButtonBindOp(tvPtr, interp, objc, objv)
1397 TreeView *tvPtr;
1398 Tcl_Interp *interp;
1399 int objc; /* Not used. */
1400 Tcl_Obj *CONST *objv;
1401{
1402 ClientData object;
1403 char *string;
1404
1405 string = Tcl_GetString(objv[3]);
1406 /* Assume that this is a binding tag. */
1407 object = Blt_TreeViewButtonTag(tvPtr, string);
1408 return Blt_ConfigureBindingsFromObj(interp, tvPtr->bindTable, object,
1409 objc - 4, objv + 4);
1410}
1411
1412/*
1413 *----------------------------------------------------------------------
1414 *
1415 * ButtonCgetOp --
1416 *
1417 *----------------------------------------------------------------------
1418 */
1419/*ARGSUSED*/
1420static int
1421ButtonCgetOp(tvPtr, interp, objc, objv)
1422 TreeView *tvPtr;
1423 Tcl_Interp *interp;
1424 int objc; /* Not used. */
1425 Tcl_Obj *CONST *objv;
1426{
1427 return Blt_ConfigureValueFromObj(interp, tvPtr->tkwin,
1428 bltTreeViewButtonSpecs, (char *)tvPtr, objv[3], 0);
1429}
1430
1431/*
1432 *----------------------------------------------------------------------
1433 *
1434 * ButtonConfigureOp --
1435 *
1436 * This procedure is called to process a list of configuration
1437 * options database, in order to reconfigure the one of more
1438 * entries in the widget.
1439 *
1440 * .h button configure option value
1441 *
1442 * Results:
1443 * A standard Tcl result. If TCL_ERROR is returned, then
1444 * interp->result contains an error message.
1445 *
1446 * Side effects:
1447 * Configuration information, such as text string, colors, font,
1448 * etc. get set for tvPtr; old resources get freed, if there
1449 * were any. The hypertext is redisplayed.
1450 *
1451 *----------------------------------------------------------------------
1452 */
1453static int
1454ButtonConfigureOp(tvPtr, interp, objc, objv)
1455 TreeView *tvPtr;
1456 Tcl_Interp *interp;
1457 int objc;
1458 Tcl_Obj *CONST *objv;
1459{
1460 if (objc == 3) {
1461 return Blt_ConfigureInfoFromObj(interp, tvPtr->tkwin,
1462 bltTreeViewButtonSpecs, (char *)tvPtr, (Tcl_Obj *)NULL, 0);
1463 } else if (objc == 4) {
1464 return Blt_ConfigureInfoFromObj(interp, tvPtr->tkwin,
1465 bltTreeViewButtonSpecs, (char *)tvPtr, objv[3], 0);
1466 }
1467 bltTreeViewIconsOption.clientData = tvPtr;
1468 if (Blt_ConfigureWidgetFromObj(tvPtr->interp, tvPtr->tkwin,
1469 bltTreeViewButtonSpecs, objc - 3, objv + 3, (char *)tvPtr,
1470 BLT_CONFIG_OBJV_ONLY) != TCL_OK) {
1471 return TCL_ERROR;
1472 }
1473 Blt_TreeViewConfigureButtons(tvPtr);
1474 Blt_TreeViewEventuallyRedraw(tvPtr);
1475 return TCL_OK;
1476}
1477
1478/*
1479 *----------------------------------------------------------------------
1480 *
1481 * ButtonOp --
1482 *
1483 * This procedure handles button operations.
1484 *
1485 * Results:
1486 * A standard Tcl result.
1487 *
1488 *----------------------------------------------------------------------
1489 */
1490static Blt_OpSpec buttonOps[] =
1491{
1492 {"activate", 1, (Blt_Op)ButtonActivateOp, 4, 4, "tagOrId",},
1493 {"bind", 1, (Blt_Op)ButtonBindOp, 4, 6, "tagName ?sequence command?",},
1494 {"cget", 2, (Blt_Op)ButtonCgetOp, 4, 4, "option",},
1495 {"configure", 2, (Blt_Op)ButtonConfigureOp, 3, 0, "?option value?...",},
1496 {"highlight", 1, (Blt_Op)ButtonActivateOp, 4, 4, "tagOrId",},
1497};
1498
1499static int nButtonOps = sizeof(buttonOps) / sizeof(Blt_OpSpec);
1500
1501static int
1502ButtonOp(tvPtr, interp, objc, objv)
1503 TreeView *tvPtr;
1504 Tcl_Interp *interp;
1505 int objc;
1506 Tcl_Obj *CONST *objv;
1507{
1508 Blt_Op proc;
1509 int result;
1510
1511 proc = Blt_GetOpFromObj(interp, nButtonOps, buttonOps, BLT_OP_ARG2, objc,
1512 objv, 0);
1513 if (proc == NULL) {
1514 return TCL_ERROR;
1515 }
1516 result = (*proc) (tvPtr, interp, objc, objv);
1517 return result;
1518}
1519
1520/*
1521 *----------------------------------------------------------------------
1522 *
1523 * CgetOp --
1524 *
1525 *----------------------------------------------------------------------
1526 */
1527/*ARGSUSED*/
1528static int
1529CgetOp(tvPtr, interp, objc, objv)
1530 TreeView *tvPtr;
1531 Tcl_Interp *interp;
1532 int objc; /* Not used. */
1533 Tcl_Obj *CONST *objv;
1534{
1535 return Blt_ConfigureValueFromObj(interp, tvPtr->tkwin, bltTreeViewSpecs,
1536 (char *)tvPtr, objv[2], 0);
1537}
1538
1539/*ARGSUSED*/
1540static int
1541CloseOp(tvPtr, interp, objc, objv)
1542 TreeView *tvPtr;
1543 Tcl_Interp *interp; /* Not used. */
1544 int objc;
1545 Tcl_Obj *CONST *objv;
1546{
1547 TreeViewEntry *entryPtr;
1548 TreeViewTagInfo info;
1549 int recurse, result;
1550 register int i;
1551
1552 recurse = FALSE;
1553
1554 if (objc > 2) {
1555 char *string;
1556 int length;
1557
1558 string = Tcl_GetStringFromObj(objv[2], &length);
1559 if ((string[0] == '-') && (length > 1) &&
1560 (strncmp(string, "-recurse", length) == 0)) {
1561 objv++, objc--;
1562 recurse = TRUE;
1563 }
1564 }
1565 for (i = 2; i < objc; i++) {
1566 if (Blt_TreeViewFindTaggedEntries(tvPtr, objv[i], &info) != TCL_OK) {
1567 return TCL_ERROR;
1568 }
1569 for (entryPtr = Blt_TreeViewFirstTaggedEntry(&info); entryPtr != NULL;
1570 entryPtr = Blt_TreeViewNextTaggedEntry(&info)) {
1571 /*
1572 * Clear the selections for any entries that may have become
1573 * hidden by closing the node.
1574 */
1575 Blt_TreeViewPruneSelection(tvPtr, entryPtr);
1576
1577 /*
1578 * -----------------------------------------------------------
1579 *
1580 * Check if either the "focus" entry or selection anchor
1581 * is in this hierarchy. Must move it or disable it before
1582 * we close the node. Otherwise it may be deleted by a Tcl
1583 * "close" script, and we'll be left pointing to a bogus
1584 * memory location.
1585 *
1586 * -----------------------------------------------------------
1587 */
1588 if ((tvPtr->focusPtr != NULL) &&
1589 (Blt_TreeIsAncestor(entryPtr->node, tvPtr->focusPtr->node))) {
1590 tvPtr->focusPtr = entryPtr;
1591 Blt_SetFocusItem(tvPtr->bindTable, tvPtr->focusPtr, ITEM_ENTRY);
1592 }
1593 if ((tvPtr->selAnchorPtr != NULL) &&
1594 (Blt_TreeIsAncestor(entryPtr->node,
1595 tvPtr->selAnchorPtr->node))) {
1596 tvPtr->selMarkPtr = tvPtr->selAnchorPtr = NULL;
1597 }
1598 if ((tvPtr->activePtr != NULL) &&
1599 (Blt_TreeIsAncestor(entryPtr->node, tvPtr->activePtr->node))) {
1600 tvPtr->activePtr = entryPtr;
1601 }
1602 if (recurse) {
1603 result = Blt_TreeViewApply(tvPtr, entryPtr,
1604 Blt_TreeViewCloseEntry, 0);
1605 } else {
1606 result = Blt_TreeViewCloseEntry(tvPtr, entryPtr);
1607 }
1608 if (result != TCL_OK) {
1609 return TCL_ERROR;
1610 }
1611 }
1612 }
1613 /* Closing a node may affect the visible entries and the
1614 * the world layout of the entries. */
1615 tvPtr->flags |= (TV_LAYOUT | TV_DIRTY | TV_RESORT);
1616 Blt_TreeViewEventuallyRedraw(tvPtr);
1617 return TCL_OK;
1618}
1619
1620/*
1621 *----------------------------------------------------------------------
1622 *
1623 * ConfigureOp --
1624 *
1625 * This procedure is called to process an objv/objc list, plus
1626 * the Tk option database, in order to configure (or reconfigure)
1627 * the widget.
1628 *
1629 * Results:
1630 * A standard Tcl result. If TCL_ERROR is returned, then
1631 * interp->result contains an error message.
1632 *
1633 * Side effects:
1634 * Configuration information, such as text string, colors, font,
1635 * etc. get set for tvPtr; old resources get freed, if there
1636 * were any. The widget is redisplayed.
1637 *
1638 *----------------------------------------------------------------------
1639 */
1640static int
1641ConfigureOp(tvPtr, interp, objc, objv)
1642 TreeView *tvPtr;
1643 Tcl_Interp *interp;
1644 int objc;
1645 Tcl_Obj *CONST *objv;
1646{
1647 if (objc == 2) {
1648 return Blt_ConfigureInfoFromObj(interp, tvPtr->tkwin, bltTreeViewSpecs,
1649 (char *)tvPtr, (Tcl_Obj *)NULL, 0);
1650 } else if (objc == 3) {
1651 return Blt_ConfigureInfoFromObj(interp, tvPtr->tkwin,
1652 bltTreeViewSpecs, (char *)tvPtr, objv[2], 0);
1653 }
1654 bltTreeViewIconsOption.clientData = tvPtr;
1655 bltTreeViewTreeOption.clientData = tvPtr;
1656 if (Blt_ConfigureWidgetFromObj(interp, tvPtr->tkwin, bltTreeViewSpecs,
1657 objc - 2, objv + 2, (char *)tvPtr, BLT_CONFIG_OBJV_ONLY) != TCL_OK) {
1658 return TCL_ERROR;
1659 }
1660 if (Blt_TreeViewUpdateWidget(interp, tvPtr) != TCL_OK) {
1661 return TCL_ERROR;
1662 }
1663 Blt_TreeViewEventuallyRedraw(tvPtr);
1664 return TCL_OK;
1665}
1666
1667/*ARGSUSED*/
1668static int
1669CurselectionOp(tvPtr, interp, objc, objv)
1670 TreeView *tvPtr;
1671 Tcl_Interp *interp; /* Not used. */
1672 int objc; /* Not used. */
1673 Tcl_Obj *CONST *objv; /* Not used. */
1674{
1675 TreeViewEntry *entryPtr;
1676 Tcl_Obj *listObjPtr, *objPtr;
1677
1678 listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
1679 if (tvPtr->flags & TV_SELECT_SORTED) {
1680 Blt_ChainLink *linkPtr;
1681
1682 for (linkPtr = Blt_ChainFirstLink(tvPtr->selChainPtr); linkPtr != NULL;
1683 linkPtr = Blt_ChainNextLink(linkPtr)) {
1684 entryPtr = Blt_ChainGetValue(linkPtr);
1685 objPtr = NodeToObj(entryPtr->node);
1686 Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
1687
1688 }
1689 } else {
1690 for (entryPtr = tvPtr->rootPtr; entryPtr != NULL;
1691 entryPtr = Blt_TreeViewNextEntry(entryPtr, ENTRY_MASK)) {
1692 if (Blt_TreeViewEntryIsSelected(tvPtr, entryPtr)) {
1693 objPtr = NodeToObj(entryPtr->node);
1694 Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
1695 }
1696 }
1697 }
1698 Tcl_SetObjResult(interp, listObjPtr);
1699 return TCL_OK;
1700}
1701
1702/*
1703 *----------------------------------------------------------------------
1704 *
1705 * BindOp --
1706 *
1707 * .t bind tagOrId sequence command
1708 *
1709 *----------------------------------------------------------------------
1710 */
1711/*ARGSUSED*/
1712static int
1713BindOp(tvPtr, interp, objc, objv)
1714 TreeView *tvPtr;
1715 Tcl_Interp *interp;
1716 int objc;
1717 Tcl_Obj *CONST *objv;
1718{
1719 ClientData object;
1720 TreeViewEntry *entryPtr;
1721 char *string;
1722
1723 /*
1724 * Entries are selected by id only. All other strings are
1725 * interpreted as a binding tag.
1726 */
1727 string = Tcl_GetString(objv[2]);
1728 if (isdigit(UCHAR(string[0]))) {
1729 Blt_TreeNode node;
1730 int inode;
1731
1732 if (Tcl_GetIntFromObj(tvPtr->interp, objv[2], &inode) != TCL_OK) {
1733 return TCL_ERROR;
1734 }
1735 node = Blt_TreeGetNode(tvPtr->tree, inode);
1736 object = Blt_NodeToEntry(tvPtr, node);
1737 } else if (GetEntryFromSpecialId(tvPtr, string, &entryPtr) == TCL_OK) {
1738 if (entryPtr != NULL) {
1739 return TCL_OK; /* Special id doesn't currently exist. */
1740 }
1741 object = entryPtr;
1742 } else {
1743 /* Assume that this is a binding tag. */
1744 object = Blt_TreeViewEntryTag(tvPtr, string);
1745 }
1746 return Blt_ConfigureBindingsFromObj(interp, tvPtr->bindTable, object,
1747 objc - 3, objv + 3);
1748}
1749
1750
1751/*ARGSUSED*/
1752static int
1753EditOp(tvPtr, interp, objc, objv)
1754 TreeView *tvPtr;
1755 Tcl_Interp *interp; /* Not used. */
1756 int objc;
1757 Tcl_Obj *CONST *objv;
1758{
1759 TreeViewEntry *entryPtr;
1760 char *string;
1761 int isRoot, isTest;
1762 int x, y;
1763
1764 isRoot = isTest = FALSE;
1765 string = Tcl_GetString(objv[2]);
1766 if (strcmp("-root", string) == 0) {
1767 isRoot = TRUE;
1768 objv++, objc--;
1769 }
1770 string = Tcl_GetString(objv[2]);
1771 if (strcmp("-test", string) == 0) {
1772 isTest = TRUE;
1773 objv++, objc--;
1774 }
1775 if (objc != 4) {
1776 Tcl_AppendResult(interp, "wrong # args: should be \"",
1777 Tcl_GetString(objv[0]), " ", Tcl_GetString(objv[1]),
1778 " ?-root? x y\"", (char *)NULL);
1779 return TCL_ERROR;
1780
1781 }
1782 if ((Tcl_GetIntFromObj(interp, objv[2], &x) != TCL_OK) ||
1783 (Tcl_GetIntFromObj(interp, objv[3], &y) != TCL_OK)) {
1784 return TCL_ERROR;
1785 }
1786 if (isRoot) {
1787 int rootX, rootY;
1788
1789 Tk_GetRootCoords(tvPtr->tkwin, &rootX, &rootY);
1790 x -= rootX;
1791 y -= rootY;
1792 }
1793 entryPtr = Blt_TreeViewNearestEntry(tvPtr, x, y, FALSE);
1794 if (entryPtr != NULL) {
1795 Blt_ChainLink *linkPtr;
1796 TreeViewColumn *columnPtr;
1797 int worldX;
1798
1799 worldX = WORLDX(tvPtr, x);
1800 for (linkPtr = Blt_ChainFirstLink(tvPtr->colChainPtr);
1801 linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
1802 columnPtr = Blt_ChainGetValue(linkPtr);
1803 if (!columnPtr->editable) {
1804 continue; /* Column isn't editable. */
1805 }
1806 if ((worldX >= columnPtr->worldX) &&
1807 (worldX < (columnPtr->worldX + columnPtr->width))) {
1808 TreeViewValue *valuePtr;
1809
1810 valuePtr = Blt_TreeViewFindValue(entryPtr, columnPtr);
1811 if (valuePtr != NULL) {
1812 TreeViewStyle *stylePtr;
1813
1814 stylePtr = valuePtr->stylePtr;
1815 if (stylePtr == NULL) {
1816 stylePtr = columnPtr->stylePtr;
1817 }
1818 if ((stylePtr->classPtr->editProc != NULL) && (!isTest)) {
1819 if ((*stylePtr->classPtr->editProc)(tvPtr, entryPtr,
1820 valuePtr, stylePtr) != TCL_OK) {
1821 return TCL_ERROR;
1822 }
1823 Blt_TreeViewEventuallyRedraw(tvPtr);
1824 }
1825 Tcl_SetObjResult(interp, Tcl_NewIntObj(1));
1826 return TCL_OK;
1827 }
1828 }
1829 }
1830 }
1831 Tcl_SetObjResult(interp, Tcl_NewIntObj(0));
1832 return TCL_OK;
1833}
1834
1835/*
1836 *----------------------------------------------------------------------
1837 *
1838 * EntryActivateOp --
1839 *
1840 * Selects the entry to appear active.
1841 *
1842 *----------------------------------------------------------------------
1843 */
1844/*ARGSUSED*/
1845static int
1846EntryActivateOp(tvPtr, interp, objc, objv)
1847 TreeView *tvPtr;
1848 Tcl_Interp *interp;
1849 int objc; /* Not used. */
1850 Tcl_Obj *CONST *objv;
1851{
1852 TreeViewEntry *newPtr, *oldPtr;
1853 char *string;
1854
1855 string = Tcl_GetString(objv[3]);
1856 if (string[0] == '\0') {
1857 newPtr = NULL;
1858 } else if (GetEntryFromObj(tvPtr, objv[3], &newPtr) != TCL_OK) {
1859 return TCL_ERROR;
1860 }
1861 if (tvPtr->treeColumn.hidden) {
1862 return TCL_OK;
1863 }
1864 oldPtr = tvPtr->activePtr;
1865 tvPtr->activePtr = newPtr;
1866 if (!(tvPtr->flags & TV_REDRAW) && (newPtr != oldPtr)) {
1867 Drawable drawable;
1868 int x, y;
1869
1870 drawable = Tk_WindowId(tvPtr->tkwin);
1871 if (oldPtr != NULL) {
1872 x = SCREENX(tvPtr, oldPtr->worldX);
1873 if (!tvPtr->flatView) {
1874 x += ICONWIDTH(DEPTH(tvPtr, oldPtr->node));
1875 }
1876 y = SCREENY(tvPtr, oldPtr->worldY);
1877 oldPtr->flags |= ENTRY_ICON;
1878 Blt_TreeViewDrawIcon(tvPtr, oldPtr, drawable, x, y);
1879 }
1880 if (newPtr != NULL) {
1881 x = SCREENX(tvPtr, newPtr->worldX);
1882 if (!tvPtr->flatView) {
1883 x += ICONWIDTH(DEPTH(tvPtr, newPtr->node));
1884 }
1885 y = SCREENY(tvPtr, newPtr->worldY);
1886 newPtr->flags |= ENTRY_ICON;
1887 Blt_TreeViewDrawIcon(tvPtr, newPtr, drawable, x, y);
1888 }
1889 }
1890 return TCL_OK;
1891}
1892
1893/*
1894 *----------------------------------------------------------------------
1895 *
1896 * EntryCgetOp --
1897 *
1898 *----------------------------------------------------------------------
1899 */
1900/*ARGSUSED*/
1901static int
1902EntryCgetOp(tvPtr, interp, objc, objv)
1903 TreeView *tvPtr;
1904 Tcl_Interp *interp;
1905 int objc; /* Not used. */
1906 Tcl_Obj *CONST *objv;
1907{
1908 TreeViewEntry *entryPtr;
1909
1910 if (Blt_TreeViewGetEntry(tvPtr, objv[3], &entryPtr) != TCL_OK) {
1911 return TCL_ERROR;
1912 }
1913 return Blt_ConfigureValueFromObj(interp, tvPtr->tkwin,
1914 bltTreeViewEntrySpecs, (char *)entryPtr, objv[4], 0);
1915}
1916
1917/*
1918 *----------------------------------------------------------------------
1919 *
1920 * EntryConfigureOp --
1921 *
1922 * This procedure is called to process a list of configuration
1923 * options database, in order to reconfigure the one of more
1924 * entries in the widget.
1925 *
1926 * .h entryconfigure node node node node option value
1927 *
1928 * Results:
1929 * A standard Tcl result. If TCL_ERROR is returned, then
1930 * interp->result contains an error message.
1931 *
1932 * Side effects:
1933 * Configuration information, such as text string, colors, font,
1934 * etc. get set for tvPtr; old resources get freed, if there
1935 * were any. The hypertext is redisplayed.
1936 *
1937 *----------------------------------------------------------------------
1938 */
1939static int
1940EntryConfigureOp(tvPtr, interp, objc, objv)
1941 TreeView *tvPtr;
1942 Tcl_Interp *interp;
1943 int objc;
1944 Tcl_Obj *CONST *objv;
1945{
1946 int nIds, configObjc;
1947 Tcl_Obj *CONST *configObjv;
1948 register int i;
1949 TreeViewEntry *entryPtr;
1950 TreeViewTagInfo info;
1951 char *string;
1952
1953 /* Figure out where the option value pairs begin */
1954 objc -= 3, objv += 3;
1955 for (i = 0; i < objc; i++) {
1956 string = Tcl_GetString(objv[i]);
1957 if (string[0] == '-') {
1958 break;
1959 }
1960 }
1961 nIds = i; /* # of tags or ids specified */
1962 configObjc = objc - i; /* # of options specified */
1963 configObjv = objv + i; /* Start of options in objv */
1964
1965 bltTreeViewIconsOption.clientData = tvPtr;
1966 bltTreeViewUidOption.clientData = tvPtr;
1967
1968 for (i = 0; i < nIds; i++) {
1969 if (Blt_TreeViewFindTaggedEntries(tvPtr, objv[i], &info) != TCL_OK) {
1970 return TCL_ERROR;
1971 }
1972 for (entryPtr = Blt_TreeViewFirstTaggedEntry(&info); entryPtr != NULL;
1973 entryPtr = Blt_TreeViewNextTaggedEntry(&info)) {
1974 if (configObjc == 0) {
1975 return Blt_ConfigureInfoFromObj(interp, tvPtr->tkwin,
1976 bltTreeViewEntrySpecs, (char *)entryPtr,
1977 (Tcl_Obj *)NULL, 0);
1978 } else if (configObjc == 1) {
1979 return Blt_ConfigureInfoFromObj(interp, tvPtr->tkwin,
1980 bltTreeViewEntrySpecs, (char *)entryPtr,
1981 configObjv[0], 0);
1982 }
1983 if (Blt_TreeViewConfigureEntry(tvPtr, entryPtr, configObjc,
1984 configObjv, BLT_CONFIG_OBJV_ONLY) != TCL_OK) {
1985 return TCL_ERROR;
1986 }
1987 }
1988 }
1989 tvPtr->flags |= (TV_DIRTY | TV_LAYOUT | TV_SCROLL | TV_RESORT);
1990 Blt_TreeViewEventuallyRedraw(tvPtr);
1991 return TCL_OK;
1992}
1993
1994/*
1995 *----------------------------------------------------------------------
1996 *
1997 * EntryIsOpenOp --
1998 *
1999 *----------------------------------------------------------------------
2000 */
2001/*ARGSUSED*/
2002static int
2003EntryIsBeforeOp(tvPtr, interp, objc, objv)
2004 TreeView *tvPtr;
2005 Tcl_Interp *interp;
2006 int objc; /* Not used. */
2007 Tcl_Obj *CONST *objv;
2008{
2009 TreeViewEntry *e1Ptr, *e2Ptr;
2010 int bool;
2011
2012 if ((Blt_TreeViewGetEntry(tvPtr, objv[3], &e1Ptr) != TCL_OK) ||
2013 (Blt_TreeViewGetEntry(tvPtr, objv[4], &e2Ptr) != TCL_OK)) {
2014 return TCL_ERROR;
2015 }
2016 bool = Blt_TreeIsBefore(e1Ptr->node, e2Ptr->node);
2017 Tcl_SetObjResult(interp, Tcl_NewBooleanObj(bool));
2018 return TCL_OK;
2019}
2020
2021/*
2022 *----------------------------------------------------------------------
2023 *
2024 * EntryIsHiddenOp --
2025 *
2026 *----------------------------------------------------------------------
2027 */
2028/*ARGSUSED*/
2029static int
2030EntryIsHiddenOp(tvPtr, interp, objc, objv)
2031 TreeView *tvPtr;
2032 Tcl_Interp *interp;
2033 int objc; /* Not used. */
2034 Tcl_Obj *CONST *objv;
2035{
2036 TreeViewEntry *entryPtr;
2037 int bool;
2038
2039 if (Blt_TreeViewGetEntry(tvPtr, objv[3], &entryPtr) != TCL_OK) {
2040 return TCL_ERROR;
2041 }
2042 bool = (entryPtr->flags & ENTRY_HIDDEN);
2043 Tcl_SetObjResult(interp, Tcl_NewBooleanObj(bool));
2044 return TCL_OK;
2045}
2046
2047
2048/*
2049 *----------------------------------------------------------------------
2050 *
2051 * EntryIsOpenOp --
2052 *
2053 *----------------------------------------------------------------------
2054 */
2055/*ARGSUSED*/
2056static int
2057EntryIsOpenOp(tvPtr, interp, objc, objv)
2058 TreeView *tvPtr;
2059 Tcl_Interp *interp;
2060 int objc; /* Not used. */
2061 Tcl_Obj *CONST *objv;
2062{
2063 TreeViewEntry *entryPtr;
2064 int bool;
2065
2066 if (Blt_TreeViewGetEntry(tvPtr, objv[3], &entryPtr) != TCL_OK) {
2067 return TCL_ERROR;
2068 }
2069 bool = ((entryPtr->flags & ENTRY_CLOSED) == 0);
2070 Tcl_SetObjResult(interp, Tcl_NewBooleanObj(bool));
2071 return TCL_OK;
2072}
2073
2074/*ARGSUSED*/
2075static int
2076EntryChildrenOp(tvPtr, interp, objc, objv)
2077 TreeView *tvPtr;
2078 Tcl_Interp *interp;
2079 int objc;
2080 Tcl_Obj *CONST *objv;
2081{
2082 TreeViewEntry *parentPtr;
2083 Tcl_Obj *listObjPtr, *objPtr;
2084 unsigned int mask;
2085
2086 mask = 0;
2087 if (Blt_TreeViewGetEntry(tvPtr, objv[3], &parentPtr) != TCL_OK) {
2088 return TCL_ERROR;
2089 }
2090 listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
2091 if (objc == 4) {
2092 TreeViewEntry *entryPtr;
2093
2094 for (entryPtr = Blt_TreeViewFirstChild(parentPtr, mask);
2095 entryPtr != NULL;
2096 entryPtr = Blt_TreeViewNextSibling(entryPtr, mask)) {
2097 objPtr = NodeToObj(entryPtr->node);
2098 Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
2099 }
2100 } else if (objc == 6) {
2101 TreeViewEntry *entryPtr, *lastPtr, *firstPtr;
2102 int firstPos, lastPos;
2103 int nNodes;
2104
2105 if ((Blt_GetPositionFromObj(interp, objv[4], &firstPos) != TCL_OK) ||
2106 (Blt_GetPositionFromObj(interp, objv[5], &lastPos) != TCL_OK)) {
2107 return TCL_ERROR;
2108 }
2109 nNodes = Blt_TreeNodeDegree(parentPtr->node);
2110 if (nNodes == 0) {
2111 return TCL_OK;
2112 }
2113 if ((lastPos == END) || (lastPos >= nNodes)) {
2114 lastPtr = Blt_TreeViewLastChild(parentPtr, mask);
2115 } else {
2116 lastPtr = GetNthEntry(parentPtr, lastPos, mask);
2117 }
2118 if ((firstPos == END) || (firstPos >= nNodes)) {
2119 firstPtr = Blt_TreeViewLastChild(parentPtr, mask);
2120 } else {
2121 firstPtr = GetNthEntry(parentPtr, firstPos, mask);
2122 }
2123 if ((lastPos != END) && (firstPos > lastPos)) {
2124 for (entryPtr = lastPtr; entryPtr != NULL;
2125 entryPtr = Blt_TreeViewPrevEntry(entryPtr, mask)) {
2126 objPtr = NodeToObj(entryPtr->node);
2127 Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
2128 if (entryPtr == firstPtr) {
2129 break;
2130 }
2131 }
2132 } else {
2133 for (entryPtr = firstPtr; entryPtr != NULL;
2134 entryPtr = Blt_TreeViewNextEntry(entryPtr, mask)) {
2135 objPtr = NodeToObj(entryPtr->node);
2136 Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
2137 if (entryPtr == lastPtr) {
2138 break;
2139 }
2140 }
2141 }
2142 } else {
2143 Tcl_AppendResult(interp, "wrong # args: should be \"",
2144 Tcl_GetString(objv[0]), " ",
2145 Tcl_GetString(objv[1]), " ",
2146 Tcl_GetString(objv[2]), " tagOrId ?first last?",
2147 (char *)NULL);
2148 return TCL_ERROR;
2149 }
2150 Tcl_SetObjResult(interp, listObjPtr);
2151 return TCL_OK;
2152}
2153
2154
2155/*
2156 *----------------------------------------------------------------------
2157 *
2158 * EntryDeleteOp --
2159 *
2160 *----------------------------------------------------------------------
2161 */
2162/*ARGSUSED*/
2163static int
2164EntryDeleteOp(tvPtr, interp, objc, objv)
2165 TreeView *tvPtr;
2166 Tcl_Interp *interp;
2167 int objc; /* Not used. */
2168 Tcl_Obj *CONST *objv;
2169{
2170 TreeViewEntry *entryPtr;
2171
2172 if (Blt_TreeViewGetEntry(tvPtr, objv[3], &entryPtr) != TCL_OK) {
2173 return TCL_ERROR;
2174 }
2175 if (objc == 5) {
2176 int entryPos;
2177 Blt_TreeNode node;
2178 /*
2179 * Delete a single child node from a hierarchy specified
2180 * by its numeric position.
2181 */
2182 if (Blt_GetPositionFromObj(interp, objv[3], &entryPos) != TCL_OK) {
2183 return TCL_ERROR;
2184 }
2185 if (entryPos >= (int)Blt_TreeNodeDegree(entryPtr->node)) {
2186 return TCL_OK; /* Bad first index */
2187 }
2188 if (entryPos == END) {
2189 node = Blt_TreeLastChild(entryPtr->node);
2190 } else {
2191 node = GetNthNode(entryPtr->node, entryPos);
2192 }
2193 DeleteNode(tvPtr, node);
2194 } else {
2195 int firstPos, lastPos;
2196 Blt_TreeNode node, first, last, next;
2197 int nEntries;
2198 /*
2199 * Delete range of nodes in hierarchy specified by first/last
2200 * positions.
2201 */
2202 if ((Blt_GetPositionFromObj(interp, objv[4], &firstPos) != TCL_OK) ||
2203 (Blt_GetPositionFromObj(interp, objv[5], &lastPos) != TCL_OK)) {
2204 return TCL_ERROR;
2205 }
2206 nEntries = Blt_TreeNodeDegree(entryPtr->node);
2207 if (nEntries == 0) {
2208 return TCL_OK;
2209 }
2210 if (firstPos == END) {
2211 firstPos = nEntries - 1;
2212 }
2213 if (firstPos >= nEntries) {
2214 Tcl_AppendResult(interp, "first position \"",
2215 Tcl_GetString(objv[4]), " is out of range", (char *)NULL);
2216 return TCL_ERROR;
2217 }
2218 if ((lastPos == END) || (lastPos >= nEntries)) {
2219 lastPos = nEntries - 1;
2220 }
2221 if (firstPos > lastPos) {
2222 Tcl_AppendResult(interp, "bad range: \"", Tcl_GetString(objv[4]),
2223 " > ", Tcl_GetString(objv[5]), "\"", (char *)NULL);
2224 return TCL_ERROR;
2225 }
2226 first = GetNthNode(entryPtr->node, firstPos);
2227 last = GetNthNode(entryPtr->node, lastPos);
2228 for (node = first; node != NULL; node = next) {
2229 next = Blt_TreeNextSibling(node);
2230 DeleteNode(tvPtr, node);
2231 if (node == last) {
2232 break;
2233 }
2234 }
2235 }
2236 tvPtr->flags |= (TV_LAYOUT | TV_DIRTY | TV_RESORT);
2237 Blt_TreeViewEventuallyRedraw(tvPtr);
2238 return TCL_OK;
2239}
2240
2241/*
2242 *----------------------------------------------------------------------
2243 *
2244 * EntrySizeOp --
2245 *
2246 * Counts the number of entries at this node.
2247 *
2248 * Results:
2249 * A standard Tcl result. If an error occurred TCL_ERROR is
2250 * returned and interp->result will contain an error message.
2251 * Otherwise, TCL_OK is returned and interp->result contains
2252 * the number of entries.
2253 *
2254 *----------------------------------------------------------------------
2255 */
2256static int
2257EntrySizeOp(tvPtr, interp, objc, objv)
2258 TreeView *tvPtr;
2259 Tcl_Interp *interp;
2260 int objc;
2261 Tcl_Obj *CONST *objv;
2262{
2263 TreeViewEntry *entryPtr;
2264 int length, sum, recurse;
2265 char *string;
2266
2267 recurse = FALSE;
2268 string = Tcl_GetStringFromObj(objv[3], &length);
2269 if ((string[0] == '-') && (length > 1) &&
2270 (strncmp(string, "-recurse", length) == 0)) {
2271 objv++, objc--;
2272 recurse = TRUE;
2273 }
2274 if (objc == 3) {
2275 Tcl_AppendResult(interp, "missing node argument: should be \"",
2276 Tcl_GetString(objv[0]), " entry open node\"", (char *)NULL);
2277 return TCL_ERROR;
2278 }
2279 if (Blt_TreeViewGetEntry(tvPtr, objv[3], &entryPtr) != TCL_OK) {
2280 return TCL_ERROR;
2281 }
2282 if (recurse) {
2283 sum = Blt_TreeSize(entryPtr->node);
2284 } else {
2285 sum = Blt_TreeNodeDegree(entryPtr->node);
2286 }
2287 Tcl_SetObjResult(interp, Tcl_NewIntObj(sum));
2288 return TCL_OK;
2289}
2290
2291/*
2292 *----------------------------------------------------------------------
2293 *
2294 * EntryOp --
2295 *
2296 * This procedure handles entry operations.
2297 *
2298 * Results:
2299 * A standard Tcl result.
2300 *
2301 *----------------------------------------------------------------------
2302 */
2303
2304static Blt_OpSpec entryOps[] =
2305{
2306 {"activate", 1, (Blt_Op)EntryActivateOp, 4, 4, "tagOrId",},
2307 /*bbox*/
2308 /*bind*/
2309 {"cget", 2, (Blt_Op)EntryCgetOp, 5, 5, "tagOrId option",},
2310 {"children", 2, (Blt_Op)EntryChildrenOp, 4, 6,
2311 "tagOrId firstPos lastPos",},
2312 /*close*/
2313 {"configure", 2, (Blt_Op)EntryConfigureOp, 4, 0,
2314 "tagOrId ?tagOrId...? ?option value?...",},
2315 {"delete", 2, (Blt_Op)EntryDeleteOp, 5, 6, "tagOrId firstPos ?lastPos?",},
2316 /*focus*/
2317 /*hide*/
2318 {"highlight", 1, (Blt_Op)EntryActivateOp, 4, 4, "tagOrId",},
2319 /*index*/
2320 {"isbefore", 3, (Blt_Op)EntryIsBeforeOp, 5, 5, "tagOrId tagOrId",},
2321 {"ishidden", 3, (Blt_Op)EntryIsHiddenOp, 4, 4, "tagOrId",},
2322 {"isopen", 3, (Blt_Op)EntryIsOpenOp, 4, 4, "tagOrId",},
2323 /*move*/
2324 /*nearest*/
2325 /*open*/
2326 /*see*/
2327 /*show*/
2328 {"size", 1, (Blt_Op)EntrySizeOp, 4, 5, "?-recurse? tagOrId",},
2329 /*toggle*/
2330};
2331static int nEntryOps = sizeof(entryOps) / sizeof(Blt_OpSpec);
2332
2333static int
2334EntryOp(tvPtr, interp, objc, objv)
2335 TreeView *tvPtr;
2336 Tcl_Interp *interp;
2337 int objc;
2338 Tcl_Obj *CONST *objv;
2339{
2340 Blt_Op proc;
2341 int result;
2342
2343 proc = Blt_GetOpFromObj(interp, nEntryOps, entryOps, BLT_OP_ARG2, objc,
2344 objv, 0);
2345 if (proc == NULL) {
2346 return TCL_ERROR;
2347 }
2348 result = (*proc) (tvPtr, interp, objc, objv);
2349 return result;
2350}
2351
2352/*ARGSUSED*/
2353static int
2354ExactCompare(interp, name, pattern)
2355 Tcl_Interp *interp; /* Not used. */
2356 char *name;
2357 char *pattern;
2358{
2359 return (strcmp(name, pattern) == 0);
2360}
2361
2362/*ARGSUSED*/
2363static int
2364GlobCompare(interp, name, pattern)
2365 Tcl_Interp *interp; /* Not used. */
2366 char *name;
2367 char *pattern;
2368{
2369 return Tcl_StringMatch(name, pattern);
2370}
2371
2372static int
2373RegexpCompare(interp, name, pattern)
2374 Tcl_Interp *interp;
2375 char *name;
2376 char *pattern;
2377{
2378 return Tcl_RegExpMatch(interp, name, pattern);
2379}
2380
2381/*
2382 *----------------------------------------------------------------------
2383 *
2384 * FindOp --
2385 *
2386 * Find one or more nodes based upon the pattern provided.
2387 *
2388 * Results:
2389 * A standard Tcl result. The interpreter result will contain a
2390 * list of the node serial identifiers.
2391 *
2392 *----------------------------------------------------------------------
2393 */
2394static int
2395FindOp(tvPtr, interp, objc, objv)
2396 TreeView *tvPtr;
2397 Tcl_Interp *interp;
2398 int objc;
2399 Tcl_Obj *CONST *objv;
2400{
2401 TreeViewEntry *firstPtr, *lastPtr;
2402 int nMatches, maxMatches;
2403 char c;
2404 int length;
2405 TreeViewCompareProc *compareProc;
2406 TreeViewIterProc *nextProc;
2407 int invertMatch; /* normal search mode (matching entries) */
2408 char *namePattern, *fullPattern;
2409 char *execCmd;
2410 register int i;
2411 int result;
2412 char *pattern, *option;
2413 Tcl_DString dString;
2414 Blt_List options;
2415 Blt_ListNode node;
2416 char *addTag, *withTag;
2417 register TreeViewEntry *entryPtr;
2418 char *string;
2419 Tcl_Obj *listObjPtr, *objPtr;
2420
2421 invertMatch = FALSE;
2422 maxMatches = 0;
2423 execCmd = namePattern = fullPattern = NULL;
2424 compareProc = ExactCompare;
2425 nextProc = Blt_TreeViewNextEntry;
2426 options = Blt_ListCreate(BLT_ONE_WORD_KEYS);
2427 withTag = addTag = NULL;
2428
2429 entryPtr = tvPtr->rootPtr;
2430 /*
2431 * Step 1: Process flags for find operation.
2432 */
2433 for (i = 2; i < objc; i++) {
2434 string = Tcl_GetStringFromObj(objv[i], &length);
2435 if (string[0] != '-') {
2436 break;
2437 }
2438 option = string + 1;
2439 length--;
2440 c = option[0];
2441 if ((c == 'e') && (length > 2) &&
2442 (strncmp(option, "exact", length) == 0)) {
2443 compareProc = ExactCompare;
2444 } else if ((c == 'g') && (strncmp(option, "glob", length) == 0)) {
2445 compareProc = GlobCompare;
2446 } else if ((c == 'r') && (strncmp(option, "regexp", length) == 0)) {
2447 compareProc = RegexpCompare;
2448 } else if ((c == 'n') && (length > 1) &&
2449 (strncmp(option, "nonmatching", length) == 0)) {
2450 invertMatch = TRUE;
2451 } else if ((c == 'n') && (length > 1) &&
2452 (strncmp(option, "name", length) == 0)) {
2453 if ((i + 1) == objc) {
2454 goto missingArg;
2455 }
2456 i++;
2457 namePattern = Tcl_GetString(objv[i]);
2458 } else if ((c == 'f') && (strncmp(option, "full", length) == 0)) {
2459 if ((i + 1) == objc) {
2460 goto missingArg;
2461 }
2462 i++;
2463 fullPattern = Tcl_GetString(objv[i]);
2464 } else if ((c == 'e') && (length > 2) &&
2465 (strncmp(option, "exec", length) == 0)) {
2466 if ((i + 1) == objc) {
2467 goto missingArg;
2468 }
2469 i++;
2470 execCmd = Tcl_GetString(objv[i]);
2471 } else if ((c == 'a') && (length > 1) &&
2472 (strncmp(option, "addtag", length) == 0)) {
2473 if ((i + 1) == objc) {
2474 goto missingArg;
2475 }
2476 i++;
2477 addTag = Tcl_GetString(objv[i]);
2478 } else if ((c == 't') && (length > 1) &&
2479 (strncmp(option, "tag", length) == 0)) {
2480 if ((i + 1) == objc) {
2481 goto missingArg;
2482 }
2483 i++;
2484 withTag = Tcl_GetString(objv[i]);
2485 } else if ((c == 'c') && (strncmp(option, "count", length) == 0)) {
2486 if ((i + 1) == objc) {
2487 goto missingArg;
2488 }
2489 i++;
2490 if (Tcl_GetIntFromObj(interp, objv[i], &maxMatches) != TCL_OK) {
2491 return TCL_ERROR;
2492 }
2493 if (maxMatches < 0) {
2494 Tcl_AppendResult(interp, "bad match count \"", objv[i],
2495 "\": should be a positive number", (char *)NULL);
2496 Blt_ListDestroy(options);
2497 return TCL_ERROR;
2498 }
2499 } else if ((option[0] == '-') && (option[1] == '\0')) {
2500 break;
2501 } else {
2502 /*
2503 * Verify that the switch is actually an entry configuration
2504 * option.
2505 */
2506 if (Blt_ConfigureValueFromObj(interp, tvPtr->tkwin,
2507 bltTreeViewEntrySpecs, (char *)entryPtr, objv[i], 0)
2508 != TCL_OK) {
2509 Tcl_ResetResult(interp);
2510 Tcl_AppendResult(interp, "bad find switch \"", string, "\"",
2511 (char *)NULL);
2512 Blt_ListDestroy(options);
2513 return TCL_ERROR;
2514 }
2515 if ((i + 1) == objc) {
2516 goto missingArg;
2517 }
2518 /* Save the option in the list of configuration options */
2519 node = Blt_ListGetNode(options, (char *)objv[i]);
2520 if (node == NULL) {
2521 node = Blt_ListCreateNode(options, (char *)objv[i]);
2522 Blt_ListAppendNode(options, node);
2523 }
2524 i++;
2525 Blt_ListSetValue(node, Tcl_GetString(objv[i]));
2526 }
2527 }
2528
2529 if ((objc - i) > 2) {
2530 Blt_ListDestroy(options);
2531 Tcl_AppendResult(interp, "too many args", (char *)NULL);
2532 return TCL_ERROR;
2533 }
2534 /*
2535 * Step 2: Find the range of the search. Check the order of two
2536 * nodes and arrange the search accordingly.
2537 *
2538 * Note: Be careful to treat "end" as the end of all nodes, instead
2539 * of the end of visible nodes. That way, we can search the
2540 * entire tree, even if the last folder is closed.
2541 */
2542 firstPtr = tvPtr->rootPtr; /* Default to root node */
2543 lastPtr = LastEntry(tvPtr, firstPtr, 0);
2544
2545 if (i < objc) {
2546 string = Tcl_GetString(objv[i]);
2547 if ((string[0] == 'e') && (strcmp(string, "end") == 0)) {
2548 firstPtr = LastEntry(tvPtr, tvPtr->rootPtr, 0);
2549 } else if (Blt_TreeViewGetEntry(tvPtr, objv[i], &firstPtr) != TCL_OK) {
2550 return TCL_ERROR;
2551 }
2552 i++;
2553 }
2554 if (i < objc) {
2555 string = Tcl_GetString(objv[i]);
2556 if ((string[0] == 'e') && (strcmp(string, "end") == 0)) {
2557 lastPtr = LastEntry(tvPtr, tvPtr->rootPtr, 0);
2558 } else if (Blt_TreeViewGetEntry(tvPtr, objv[i], &lastPtr) != TCL_OK) {
2559 return TCL_ERROR;
2560 }
2561 }
2562 if (Blt_TreeIsBefore(lastPtr->node, firstPtr->node)) {
2563 nextProc = Blt_TreeViewPrevEntry;
2564 }
2565 nMatches = 0;
2566
2567 /*
2568 * Step 3: Search through the tree and look for nodes that match the
2569 * current pattern specifications. Save the name of each of
2570 * the matching nodes.
2571 */
2572 listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
2573 for (entryPtr = firstPtr; entryPtr != NULL;
2574 entryPtr = (*nextProc) (entryPtr, 0)) {
2575 if (namePattern != NULL) {
2576 result = (*compareProc)(interp, Blt_TreeNodeLabel(entryPtr->node),
2577 namePattern);
2578 if (result == invertMatch) {
2579 goto nextEntry; /* Failed to match */
2580 }
2581 }
2582 if (fullPattern != NULL) {
2583 Tcl_DString fullName;
2584
2585 Blt_TreeViewGetFullName(tvPtr, entryPtr, FALSE, &fullName);
2586 result = (*compareProc) (interp, Tcl_DStringValue(&fullName),
2587 fullPattern);
2588 Tcl_DStringFree(&fullName);
2589 if (result == invertMatch) {
2590 goto nextEntry; /* Failed to match */
2591 }
2592 }
2593 if (withTag != NULL) {
2594 result = Blt_TreeHasTag(tvPtr->tree, entryPtr->node, withTag);
2595 if (result == invertMatch) {
2596 goto nextEntry; /* Failed to match */
2597 }
2598 }
2599 for (node = Blt_ListFirstNode(options); node != NULL;
2600 node = Blt_ListNextNode(node)) {
2601 objPtr = (Tcl_Obj *)Blt_ListGetKey(node);
2602 Tcl_ResetResult(interp);
2603 Blt_ConfigureValueFromObj(interp, tvPtr->tkwin,
2604 bltTreeViewEntrySpecs, (char *)entryPtr, objPtr, 0);
2605 pattern = Blt_ListGetValue(node);
2606 objPtr = Tcl_GetObjResult(interp);
2607 result = (*compareProc) (interp, Tcl_GetString(objPtr), pattern);
2608 if (result == invertMatch) {
2609 goto nextEntry; /* Failed to match */
2610 }
2611 }
2612 /*
2613 * Someone may actually delete the current node in the "exec"
2614 * callback. Preserve the entry.
2615 */
2616 Tcl_Preserve(entryPtr);
2617 if (execCmd != NULL) {
2618 Tcl_DString cmdString;
2619
2620 Blt_TreeViewPercentSubst(tvPtr, entryPtr, execCmd, &cmdString);
2621 result = Tcl_GlobalEval(interp, Tcl_DStringValue(&cmdString));
2622 Tcl_DStringFree(&cmdString);
2623 if (result != TCL_OK) {
2624 Tcl_Release(entryPtr);
2625 goto error;
2626 }
2627 }
2628 /* A NULL node reference in an entry indicates that the entry
2629 * was deleted, but its memory not released yet. */
2630 if (entryPtr->node != NULL) {
2631 /* Finally, save the matching node name. */
2632 objPtr = NodeToObj(entryPtr->node);
2633 Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
2634 if (addTag != NULL) {
2635 if (AddTag(tvPtr, entryPtr->node, addTag) != TCL_OK) {
2636 goto error;
2637 }
2638 }
2639 }
2640
2641 Tcl_Release(entryPtr);
2642 nMatches++;
2643 if ((nMatches == maxMatches) && (maxMatches > 0)) {
2644 break;
2645 }
2646 nextEntry:
2647 if (entryPtr == lastPtr) {
2648 break;
2649 }
2650 }
2651 Tcl_ResetResult(interp);
2652 Blt_ListDestroy(options);
2653 Tcl_SetObjResult(interp, listObjPtr);
2654 return TCL_OK;
2655
2656 missingArg:
2657 Tcl_AppendResult(interp, "missing argument for find option \"", objv[i],
2658 "\"", (char *)NULL);
2659 error:
2660 Tcl_DStringFree(&dString);
2661 Blt_ListDestroy(options);
2662 return TCL_ERROR;
2663}
2664
2665
2666/*
2667 *----------------------------------------------------------------------
2668 *
2669 * GetOp --
2670 *
2671 * Converts one or more node identifiers to its path component.
2672 * The path may be either the single entry name or the full path
2673 * of the entry.
2674 *
2675 * Results:
2676 * A standard Tcl result. The interpreter result will contain a
2677 * list of the convert names.
2678 *
2679 *----------------------------------------------------------------------
2680 */
2681static int
2682GetOp(tvPtr, interp, objc, objv)
2683 TreeView *tvPtr;
2684 Tcl_Interp *interp;
2685 int objc;
2686 Tcl_Obj *CONST *objv;
2687{
2688 TreeViewTagInfo info;
2689 TreeViewEntry *entryPtr;
2690 int useFullName;
2691 register int i;
2692 Tcl_DString dString1, dString2;
2693 int count;
2694
2695 useFullName = FALSE;
2696 if (objc > 2) {
2697 char *string;
2698
2699 string = Tcl_GetString(objv[2]);
2700 if ((string[0] == '-') && (strcmp(string, "-full") == 0)) {
2701 useFullName = TRUE;
2702 objv++, objc--;
2703 }
2704 }
2705 Tcl_DStringInit(&dString1);
2706 Tcl_DStringInit(&dString2);
2707 count = 0;
2708 for (i = 2; i < objc; i++) {
2709 if (Blt_TreeViewFindTaggedEntries(tvPtr, objv[i], &info) != TCL_OK) {
2710 return TCL_ERROR;
2711 }
2712 for (entryPtr = Blt_TreeViewFirstTaggedEntry(&info); entryPtr != NULL;
2713 entryPtr = Blt_TreeViewNextTaggedEntry(&info)) {
2714 Tcl_DStringSetLength(&dString2, 0);
2715 count++;
2716 if (entryPtr->node == NULL) {
2717 Tcl_DStringAppendElement(&dString1, "");
2718 continue;
2719 }
2720 if (useFullName) {
2721 Blt_TreeViewGetFullName(tvPtr, entryPtr, FALSE, &dString2);
2722 Tcl_DStringAppendElement(&dString1,
2723 Tcl_DStringValue(&dString2));
2724 } else {
2725 Tcl_DStringAppendElement(&dString1,
2726 Blt_TreeNodeLabel(entryPtr->node));
2727 }
2728 }
2729 }
2730 /* This handles the single element list problem. */
2731 if (count == 1) {
2732 Tcl_DStringResult(interp, &dString2);
2733 Tcl_DStringFree(&dString1);
2734 } else {
2735 Tcl_DStringResult(interp, &dString1);
2736 Tcl_DStringFree(&dString2);
2737 }
2738 return TCL_OK;
2739}
2740
2741/*
2742 *----------------------------------------------------------------------
2743 *
2744 * SearchAndApplyToTree --
2745 *
2746 * Searches through the current tree and applies a procedure
2747 * to matching nodes. The search specification is taken from
2748 * the following command-line arguments:
2749 *
2750 * ?-exact? ?-glob? ?-regexp? ?-nonmatching?
2751 * ?-data string?
2752 * ?-name string?
2753 * ?-full string?
2754 * ?--?
2755 * ?inode...?
2756 *
2757 * Results:
2758 * A standard Tcl result. If the result is valid, and if the
2759 * nonmatchPtr is specified, it returns a boolean value
2760 * indicating whether or not the search was inverted. This
2761 * is needed to fix things properly for the "hide nonmatching"
2762 * case.
2763 *
2764 *----------------------------------------------------------------------
2765 */
2766static int
2767SearchAndApplyToTree(tvPtr, interp, objc, objv, proc, nonMatchPtr)
2768 TreeView *tvPtr;
2769 Tcl_Interp *interp;
2770 int objc;
2771 Tcl_Obj *CONST *objv;
2772 TreeViewApplyProc *proc;
2773 int *nonMatchPtr; /* returns: inverted search indicator */
2774{
2775 TreeViewCompareProc *compareProc;
2776 int invertMatch; /* normal search mode (matching entries) */
2777 char *namePattern, *fullPattern;
2778 register int i;
2779 int length;
2780 int result;
2781 char *option, *pattern;
2782 char c;
2783 Blt_List options;
2784 TreeViewEntry *entryPtr;
2785 register Blt_ListNode node;
2786 char *string;
2787 char *withTag;
2788 Tcl_Obj *objPtr;
2789 TreeViewTagInfo info;
2790
2791 options = Blt_ListCreate(BLT_ONE_WORD_KEYS);
2792 invertMatch = FALSE;
2793 namePattern = fullPattern = NULL;
2794 compareProc = ExactCompare;
2795 withTag = NULL;
2796
2797 entryPtr = tvPtr->rootPtr;
2798 for (i = 2; i < objc; i++) {
2799 string = Tcl_GetStringFromObj(objv[i], &length);
2800 if (string[0] != '-') {
2801 break;
2802 }
2803 option = string + 1;
2804 length--;
2805 c = option[0];
2806 if ((c == 'e') && (strncmp(option, "exact", length) == 0)) {
2807 compareProc = ExactCompare;
2808 } else if ((c == 'g') && (strncmp(option, "glob", length) == 0)) {
2809 compareProc = GlobCompare;
2810 } else if ((c == 'r') && (strncmp(option, "regexp", length) == 0)) {
2811 compareProc = RegexpCompare;
2812 } else if ((c == 'n') && (length > 1) &&
2813 (strncmp(option, "nonmatching", length) == 0)) {
2814 invertMatch = TRUE;
2815 } else if ((c == 'f') && (strncmp(option, "full", length) == 0)) {
2816 if ((i + 1) == objc) {
2817 goto missingArg;
2818 }
2819 i++;
2820 fullPattern = Tcl_GetString(objv[i]);
2821 } else if ((c == 'n') && (length > 1) &&
2822 (strncmp(option, "name", length) == 0)) {
2823 if ((i + 1) == objc) {
2824 goto missingArg;
2825 }
2826 i++;
2827 namePattern = Tcl_GetString(objv[i]);
2828 } else if ((c == 't') && (length > 1) &&
2829 (strncmp(option, "tag", length) == 0)) {
2830 if ((i + 1) == objc) {
2831 goto missingArg;
2832 }
2833 i++;
2834 withTag = Tcl_GetString(objv[i]);
2835 } else if ((option[0] == '-') && (option[1] == '\0')) {
2836 break;
2837 } else {
2838 /*
2839 * Verify that the switch is actually an entry configuration option.
2840 */
2841 if (Blt_ConfigureValueFromObj(interp, tvPtr->tkwin,
2842 bltTreeViewEntrySpecs, (char *)entryPtr, objv[i], 0)
2843 != TCL_OK) {
2844 Tcl_ResetResult(interp);
2845 Tcl_AppendResult(interp, "bad switch \"", string,
2846 "\": must be -exact, -glob, -regexp, -name, -full, or -nonmatching",
2847 (char *)NULL);
2848 return TCL_ERROR;
2849 }
2850 if ((i + 1) == objc) {
2851 goto missingArg;
2852 }
2853 /* Save the option in the list of configuration options */
2854 node = Blt_ListGetNode(options, (char *)objv[i]);
2855 if (node == NULL) {
2856 node = Blt_ListCreateNode(options, (char *)objv[i]);
2857 Blt_ListAppendNode(options, node);
2858 }
2859 i++;
2860 Blt_ListSetValue(node, Tcl_GetString(objv[i]));
2861 }
2862 }
2863
2864 if ((namePattern != NULL) || (fullPattern != NULL) ||
2865 (Blt_ListGetLength(options) > 0)) {
2866 /*
2867 * Search through the tree and look for nodes that match the
2868 * current spec. Apply the input procedure to each of the
2869 * matching nodes.
2870 */
2871 for (entryPtr = tvPtr->rootPtr; entryPtr != NULL;
2872 entryPtr = Blt_TreeViewNextEntry(entryPtr, 0)) {
2873 if (namePattern != NULL) {
2874 result = (*compareProc) (interp,
2875 Blt_TreeNodeLabel(entryPtr->node), namePattern);
2876 if (result == invertMatch) {
2877 continue; /* Failed to match */
2878 }
2879 }
2880 if (fullPattern != NULL) {
2881 Tcl_DString dString;
2882
2883 Blt_TreeViewGetFullName(tvPtr, entryPtr, FALSE, &dString);
2884 result = (*compareProc) (interp, Tcl_DStringValue(&dString),
2885 fullPattern);
2886 Tcl_DStringFree(&dString);
2887 if (result == invertMatch) {
2888 continue; /* Failed to match */
2889 }
2890 }
2891 if (withTag != NULL) {
2892 result = Blt_TreeHasTag(tvPtr->tree, entryPtr->node, withTag);
2893 if (result == invertMatch) {
2894 continue; /* Failed to match */
2895 }
2896 }
2897 for (node = Blt_ListFirstNode(options); node != NULL;
2898 node = Blt_ListNextNode(node)) {
2899 objPtr = (Tcl_Obj *)Blt_ListGetKey(node);
2900 Tcl_ResetResult(interp);
2901 if (Blt_ConfigureValueFromObj(interp, tvPtr->tkwin,
2902 bltTreeViewEntrySpecs, (char *)entryPtr, objPtr, 0)
2903 != TCL_OK) {
2904 return TCL_ERROR; /* This shouldn't happen. */
2905 }
2906 pattern = Blt_ListGetValue(node);
2907 objPtr = Tcl_GetObjResult(interp);
2908 result = (*compareProc)(interp, Tcl_GetString(objPtr), pattern);
2909 if (result == invertMatch) {
2910 continue; /* Failed to match */
2911 }
2912 }
2913 /* Finally, apply the procedure to the node */
2914 (*proc) (tvPtr, entryPtr);
2915 }
2916 Tcl_ResetResult(interp);
2917 Blt_ListDestroy(options);
2918 }
2919 /*
2920 * Apply the procedure to nodes that have been specified
2921 * individually.
2922 */
2923 for ( /*empty*/ ; i < objc; i++) {
2924 if (Blt_TreeViewFindTaggedEntries(tvPtr, objv[i], &info) != TCL_OK) {
2925 return TCL_ERROR;
2926 }
2927 for (entryPtr = Blt_TreeViewFirstTaggedEntry(&info); entryPtr != NULL;
2928 entryPtr = Blt_TreeViewNextTaggedEntry(&info)) {
2929 if ((*proc) (tvPtr, entryPtr) != TCL_OK) {
2930 return TCL_ERROR;
2931 }
2932 }
2933 }
2934 if (nonMatchPtr != NULL) {
2935 *nonMatchPtr = invertMatch; /* return "inverted search" status */
2936 }
2937 return TCL_OK;
2938
2939 missingArg:
2940 Blt_ListDestroy(options);
2941 Tcl_AppendResult(interp, "missing pattern for search option \"", objv[i],
2942 "\"", (char *)NULL);
2943 return TCL_ERROR;
2944
2945}
2946
2947static int
2948FixSelectionsApplyProc(tvPtr, entryPtr)
2949 TreeView *tvPtr;
2950 TreeViewEntry *entryPtr;
2951{
2952 if (entryPtr->flags & ENTRY_HIDDEN) {
2953 Blt_TreeViewDeselectEntry(tvPtr, entryPtr);
2954 if ((tvPtr->focusPtr != NULL) &&
2955 (Blt_TreeIsAncestor(entryPtr->node, tvPtr->focusPtr->node))) {
2956 if (entryPtr != tvPtr->rootPtr) {
2957 entryPtr = Blt_TreeViewParentEntry(entryPtr);
2958 tvPtr->focusPtr = (entryPtr == NULL)
2959 ? tvPtr->focusPtr : entryPtr;
2960 Blt_SetFocusItem(tvPtr->bindTable, tvPtr->focusPtr, ITEM_ENTRY);
2961 }
2962 }
2963 if ((tvPtr->selAnchorPtr != NULL) &&
2964 (Blt_TreeIsAncestor(entryPtr->node, tvPtr->selAnchorPtr->node))) {
2965 tvPtr->selMarkPtr = tvPtr->selAnchorPtr = NULL;
2966 }
2967 if ((tvPtr->activePtr != NULL) &&
2968 (Blt_TreeIsAncestor(entryPtr->node, tvPtr->activePtr->node))) {
2969 tvPtr->activePtr = NULL;
2970 }
2971 Blt_TreeViewPruneSelection(tvPtr, entryPtr);
2972 }
2973 return TCL_OK;
2974}
2975
2976/*
2977 *----------------------------------------------------------------------
2978 *
2979 * HideOp --
2980 *
2981 * Hides one or more nodes. Nodes can be specified by their
2982 * inode, or by matching a name or data value pattern. By
2983 * default, the patterns are matched exactly. They can also
2984 * be matched using glob-style and regular expression rules.
2985 *
2986 * Results:
2987 * A standard Tcl result.
2988 *
2989 *----------------------------------------------------------------------
2990 */
2991static int
2992HideOp(tvPtr, interp, objc, objv)
2993 TreeView *tvPtr;
2994 Tcl_Interp *interp;
2995 int objc;
2996 Tcl_Obj *CONST *objv;
2997{
2998 int status, nonmatching;
2999
3000 status = SearchAndApplyToTree(tvPtr, interp, objc, objv,
3001 HideEntryApplyProc, &nonmatching);
3002
3003 if (status != TCL_OK) {
3004 return TCL_ERROR;
3005 }
3006 /*
3007 * If this was an inverted search, scan back through the
3008 * tree and make sure that the parents for all visible
3009 * nodes are also visible. After all, if a node is supposed
3010 * to be visible, its parent can't be hidden.
3011 */
3012 if (nonmatching) {
3013 Blt_TreeViewApply(tvPtr, tvPtr->rootPtr, MapAncestorsApplyProc, 0);
3014 }
3015 /*
3016 * Make sure that selections are cleared from any hidden
3017 * nodes. This wasn't done earlier--we had to delay it until
3018 * we fixed the visibility status for the parents.
3019 */
3020 Blt_TreeViewApply(tvPtr, tvPtr->rootPtr, FixSelectionsApplyProc, 0);
3021
3022 /* Hiding an entry only effects the visible nodes. */
3023 tvPtr->flags |= (TV_LAYOUT | TV_SCROLL);
3024 Blt_TreeViewEventuallyRedraw(tvPtr);
3025 return TCL_OK;
3026}
3027
3028/*
3029 *----------------------------------------------------------------------
3030 *
3031 * ShowOp --
3032 *
3033 * Mark one or more nodes to be exposed. Nodes can be specified
3034 * by their inode, or by matching a name or data value pattern. By
3035 * default, the patterns are matched exactly. They can also
3036 * be matched using glob-style and regular expression rules.
3037 *
3038 * Results:
3039 * A standard Tcl result.
3040 *
3041 *----------------------------------------------------------------------
3042 */
3043static int
3044ShowOp(tvPtr, interp, objc, objv)
3045 TreeView *tvPtr;
3046 Tcl_Interp *interp;
3047 int objc;
3048 Tcl_Obj *CONST *objv;
3049{
3050 if (SearchAndApplyToTree(tvPtr, interp, objc, objv, ShowEntryApplyProc,
3051 (int *)NULL) != TCL_OK) {
3052 return TCL_ERROR;
3053 }
3054 tvPtr->flags |= (TV_LAYOUT | TV_SCROLL);
3055 Blt_TreeViewEventuallyRedraw(tvPtr);
3056 return TCL_OK;
3057}
3058
3059/*
3060 *----------------------------------------------------------------------
3061 *
3062 * IndexOp --
3063 *
3064 * Converts one of more words representing indices of the entries
3065 * in the treeview widget to their respective serial identifiers.
3066 *
3067 * Results:
3068 * A standard Tcl result. Interp->result will contain the
3069 * identifier of each inode found. If an inode could not be found,
3070 * then the serial identifier will be the empty string.
3071 *
3072 *----------------------------------------------------------------------
3073 */
3074/*ARGSUSED*/
3075static int
3076IndexOp(tvPtr, interp, objc, objv)
3077 TreeView *tvPtr;
3078 Tcl_Interp *interp;
3079 int objc; /* Not used. */
3080 Tcl_Obj *CONST *objv;
3081{
3082 TreeViewEntry *entryPtr;
3083 char *string;
3084 TreeViewEntry *fromPtr;
3085 int usePath;
3086
3087 usePath = FALSE;
3088 fromPtr = NULL;
3089 string = Tcl_GetString(objv[2]);
3090 if ((string[0] == '-') && (strcmp(string, "-path") == 0)) {
3091 usePath = TRUE;
3092 objv++, objc--;
3093 }
3094 if ((string[0] == '-') && (strcmp(string, "-at") == 0)) {
3095 if (Blt_TreeViewGetEntry(tvPtr, objv[3], &fromPtr) != TCL_OK) {
3096 return TCL_ERROR;
3097 }
3098 objv += 2, objc -= 2;
3099 }
3100 if (objc != 3) {
3101 Tcl_AppendResult(interp, "wrong # args: should be \"",
3102 Tcl_GetString(objv[0]),
3103 " index ?-at tagOrId? ?-path? tagOrId\"",
3104 (char *)NULL);
3105 return TCL_ERROR;
3106 }
3107 tvPtr->fromPtr = fromPtr;
3108 if (tvPtr->fromPtr == NULL) {
3109 tvPtr->fromPtr = tvPtr->focusPtr;
3110 }
3111 if (tvPtr->fromPtr == NULL) {
3112 tvPtr->fromPtr = tvPtr->rootPtr;
3113 }
3114 if (usePath) {
3115 if (fromPtr == NULL) {
3116 fromPtr = tvPtr->rootPtr;
3117 }
3118 string = Tcl_GetString(objv[2]);
3119 entryPtr = FindPath(tvPtr, fromPtr, string);
3120 if (entryPtr != NULL) {
3121 Tcl_SetObjResult(interp, NodeToObj(entryPtr->node));
3122 }
3123 } else {
3124 if ((GetEntryFromObj2(tvPtr, objv[2], &entryPtr) == TCL_OK) &&
3125 (entryPtr != NULL)) {
3126 Tcl_SetObjResult(interp, NodeToObj(entryPtr->node));
3127 }
3128 }
3129 return TCL_OK;
3130}
3131
3132/*
3133 *----------------------------------------------------------------------
3134 *
3135 * InsertOp --
3136 *
3137 * Add new entries into a hierarchy. If no node is specified,
3138 * new entries will be added to the root of the hierarchy.
3139 *
3140 *----------------------------------------------------------------------
3141 */
3142static int
3143InsertOp(tvPtr, interp, objc, objv)
3144 TreeView *tvPtr;
3145 Tcl_Interp *interp;
3146 int objc;
3147 Tcl_Obj *CONST *objv;
3148{
3149 Blt_TreeNode node, parent;
3150 int insertPos;
3151 int depth, count;
3152 char *path;
3153 Tcl_Obj *CONST *options;
3154 Tcl_Obj *listObjPtr;
3155 char **compArr;
3156 register char **p;
3157 register int n;
3158 TreeViewEntry *rootPtr;
3159 char *string;
3160
3161 rootPtr = tvPtr->rootPtr;
3162 string = Tcl_GetString(objv[2]);
3163 if ((string[0] == '-') && (strcmp(string, "-at") == 0)) {
3164 if (objc > 2) {
3165 if (Blt_TreeViewGetEntry(tvPtr, objv[3], &rootPtr) != TCL_OK) {
3166 return TCL_ERROR;
3167 }
3168 objv += 2, objc -= 2;
3169 } else {
3170 Tcl_AppendResult(interp, "missing argument for \"-at\" flag",
3171 (char *)NULL);
3172 return TCL_ERROR;
3173 }
3174 }
3175 if (objc == 2) {
3176 Tcl_AppendResult(interp, "missing position argument", (char *)NULL);
3177 return TCL_ERROR;
3178 }
3179 if (Blt_GetPositionFromObj(interp, objv[2], &insertPos) != TCL_OK) {
3180 return TCL_ERROR;
3181 }
3182 node = NULL;
3183 objc -= 3, objv += 3;
3184
3185 listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
3186 while (objc > 0) {
3187 path = Tcl_GetString(objv[0]);
3188 objv++, objc--;
3189
3190 /*
3191 * Count the option-value pairs that follow. Count until we
3192 * spot one that looks like an entry name (i.e. doesn't start
3193 * with a minus "-").
3194 */
3195 for (count = 0; count < objc; count += 2) {
3196 string = Tcl_GetString(objv[count]);
3197 if (string[0] != '-') {
3198 break;
3199 }
3200 }
3201 if (count > objc) {
3202 count = objc;
3203 }
3204 options = objv;
3205 objc -= count, objv += count;
3206
3207 if (tvPtr->trimLeft != NULL) {
3208 register char *s1, *s2;
3209
3210 /* Trim off leading character string if one exists. */
3211 for (s1 = path, s2 = tvPtr->trimLeft; *s2 != '\0'; s2++, s1++) {
3212 if (*s1 != *s2) {
3213 break;
3214 }
3215 }
3216 if (*s2 == '\0') {
3217 path = s1;
3218 }
3219 }
3220 /*
3221 * Split the path and find the parent node of the path.
3222 */
3223 compArr = &path;
3224 depth = 1;
3225 if (tvPtr->pathSep != SEPARATOR_NONE) {
3226 if (SplitPath(tvPtr, path, &depth, &compArr) != TCL_OK) {
3227 goto error;
3228 }
3229 if (depth == 0) {
3230 Blt_Free(compArr);
3231 continue; /* Root already exists. */
3232 }
3233 }
3234 parent = rootPtr->node;
3235 depth--;
3236
3237 /* Verify each component in the path preceding the tail. */
3238 for (n = 0, p = compArr; n < depth; n++, p++) {
3239 node = Blt_TreeFindChild(parent, *p);
3240 if (node == NULL) {
3241 if ((tvPtr->flags & TV_FILL_ANCESTORS) == 0) {
3242 Tcl_AppendResult(interp, "can't find path component \"",
3243 *p, "\" in \"", path, "\"", (char *)NULL);
3244 goto error;
3245 }
3246 node = Blt_TreeCreateNode(tvPtr->tree, parent, *p, END);
3247 if (node == NULL) {
3248 goto error;
3249 }
3250 }
3251 parent = node;
3252 }
3253 node = NULL;
3254 if (((tvPtr->flags & TV_ALLOW_DUPLICATES) == 0) &&
3255 (Blt_TreeFindChild(parent, *p) != NULL)) {
3256 Tcl_AppendResult(interp, "entry \"", *p, "\" already exists in \"",
3257 path, "\"", (char *)NULL);
3258 goto error;
3259 }
3260 node = Blt_TreeCreateNode(tvPtr->tree, parent, *p, insertPos);
3261 if (node == NULL) {
3262 goto error;
3263 }
3264 if (Blt_TreeViewCreateEntry(tvPtr, node, count, options, 0) != TCL_OK) {
3265 goto error;
3266 }
3267 if (compArr != &path) {
3268 Blt_Free(compArr);
3269 }
3270 Tcl_ListObjAppendElement(interp, listObjPtr, NodeToObj(node));
3271 }
3272 tvPtr->flags |= (TV_LAYOUT | TV_SCROLL | TV_DIRTY | TV_RESORT);
3273 Blt_TreeViewEventuallyRedraw(tvPtr);
3274 Tcl_SetObjResult(interp, listObjPtr);
3275 return TCL_OK;
3276
3277 error:
3278 if (compArr != &path) {
3279 Blt_Free(compArr);
3280 }
3281 Tcl_DecrRefCount(listObjPtr);
3282 if (node != NULL) {
3283 DeleteNode(tvPtr, node);
3284 }
3285 return TCL_ERROR;
3286}
3287
3288#ifdef notdef
3289/*
3290 *----------------------------------------------------------------------
3291 *
3292 * AddOp --
3293 *
3294 * Add new entries into a hierarchy. If no node is specified,
3295 * new entries will be added to the root of the hierarchy.
3296 *
3297 *----------------------------------------------------------------------
3298 */
3299
3300static Blt_SwitchParseProc StringToChild;
3301#define INSERT_BEFORE (ClientData)0
3302#define INSERT_AFTER (ClientData)1
3303static Blt_SwitchCustom beforeSwitch =
3304{
3305 StringToChild, (Blt_SwitchFreeProc *)NULL, INSERT_BEFORE,
3306};
3307static Blt_SwitchCustom afterSwitch =
3308{
3309 StringToChild, (Blt_SwitchFreeProc *)NULL, INSERT_AFTER,
3310};
3311
3312typedef struct {
3313 int insertPos;
3314 Blt_TreeNode parent;
3315} InsertData;
3316
3317static Blt_SwitchSpec insertSwitches[] =
3318{
3319 {BLT_SWITCH_CUSTOM, "-after", Blt_Offset(InsertData, insertPos), 0,
3320 &afterSwitch},
3321 {BLT_SWITCH_INT_NONNEGATIVE, "-at", Blt_Offset(InsertData, insertPos), 0},
3322 {BLT_SWITCH_CUSTOM, "-before", Blt_Offset(InsertData, insertPos), 0,
3323 &beforeSwitch},
3324 {BLT_SWITCH_END, NULL, 0, 0}
3325};
3326
3327static int
3328AddOp(tvPtr, interp, objc, objv)
3329 TreeView *tvPtr;
3330 Tcl_Interp *interp;
3331 int objc;
3332 Tcl_Obj *CONST *objv;
3333{
3334 Blt_TreeNode node, parent;
3335 int insertPos;
3336 int depth, count;
3337 char *path;
3338 Tcl_Obj *CONST *options;
3339 Tcl_Obj *listObjPtr;
3340 char **compArr;
3341 register char **p;
3342 register int n;
3343 TreeViewEntry *rootPtr;
3344 char *string;
3345
3346 memset(&data, 0, sizeof(data));
3347 data.maxDepth = -1;
3348 data.cmdPtr = cmdPtr;
3349
3350 /* Process any leading switches */
3351 i = Blt_ProcessObjSwitches(interp, addSwitches, objc - 2, objv + 2,
3352 (char *)&data, BLT_CONFIG_OBJV_PARTIAL);
3353 if (i < 0) {
3354 return TCL_ERROR;
3355 }
3356 i += 2;
3357 /* Should have at the starting node */
3358 if (i >= objc) {
3359 Tcl_AppendResult(interp, "starting node argument is missing",
3360 (char *)NULL);
3361 return TCL_ERROR;
3362 }
3363 if (Blt_TreeViewGetEntry(tvPtr, objv[i], &rootPtr) != TCL_OK) {
3364 return TCL_ERROR;
3365 }
3366 objv += i, objc -= i;
3367 node = NULL;
3368
3369 /* Process sections of path ?options? */
3370 listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
3371 while (objc > 0) {
3372 path = Tcl_GetString(objv[0]);
3373 objv++, objc--;
3374 /*
3375 * Count the option-value pairs that follow. Count until we
3376 * spot one that looks like an entry name (i.e. doesn't start
3377 * with a minus "-").
3378 */
3379 for (count = 0; count < objc; count += 2) {
3380 if (!Blt_ObjIsOption(bltTreeViewEntrySpecs, objv[count], 0)) {
3381 break;
3382 }
3383 }
3384 if (count > objc) {
3385 count = objc;
3386 }
3387 options = objv;
3388 objc -= count, objv += count;
3389
3390 if (tvPtr->trimLeft != NULL) {
3391 register char *s1, *s2;
3392
3393 /* Trim off leading character string if one exists. */
3394 for (s1 = path, s2 = tvPtr->trimLeft; *s2 != '\0'; s2++, s1++) {
3395 if (*s1 != *s2) {
3396 break;
3397 }
3398 }
3399 if (*s2 == '\0') {
3400 path = s1;
3401 }
3402 }
3403 /*
3404 * Split the path and find the parent node of the path.
3405 */
3406 compArr = &path;
3407 depth = 1;
3408 if (tvPtr->pathSep != SEPARATOR_NONE) {
3409 if (SplitPath(tvPtr, path, &depth, &compArr) != TCL_OK) {
3410 goto error;
3411 }
3412 if (depth == 0) {
3413 Blt_Free(compArr);
3414 continue; /* Root already exists. */
3415 }
3416 }
3417 parent = rootPtr->node;
3418 depth--;
3419
3420 /* Verify each component in the path preceding the tail. */
3421 for (n = 0, p = compArr; n < depth; n++, p++) {
3422 node = Blt_TreeFindChild(parent, *p);
3423 if (node == NULL) {
3424 if ((tvPtr->flags & TV_FILL_ANCESTORS) == 0) {
3425 Tcl_AppendResult(interp, "can't find path component \"",
3426 *p, "\" in \"", path, "\"", (char *)NULL);
3427 goto error;
3428 }
3429 node = Blt_TreeCreateNode(tvPtr->tree, parent, *p, END);
3430 if (node == NULL) {
3431 goto error;
3432 }
3433 }
3434 parent = node;
3435 }
3436 node = NULL;
3437 if (((tvPtr->flags & TV_ALLOW_DUPLICATES) == 0) &&
3438 (Blt_TreeFindChild(parent, *p) != NULL)) {
3439 Tcl_AppendResult(interp, "entry \"", *p, "\" already exists in \"",
3440 path, "\"", (char *)NULL);
3441 goto error;
3442 }
3443 node = Blt_TreeCreateNode(tvPtr->tree, parent, *p, insertPos);
3444 if (node == NULL) {
3445 goto error;
3446 }
3447 if (Blt_TreeViewCreateEntry(tvPtr, node, count, options, 0) != TCL_OK) {
3448 goto error;
3449 }
3450 if (compArr != &path) {
3451 Blt_Free(compArr);
3452 }
3453 Tcl_ListObjAppendElement(interp, listObjPtr, NodeToObj(node));
3454 }
3455 tvPtr->flags |= (TV_LAYOUT | TV_SCROLL | TV_DIRTY | TV_RESORT);
3456 Blt_TreeViewEventuallyRedraw(tvPtr);
3457 Tcl_SetObjResult(interp, listObjPtr);
3458 return TCL_OK;
3459
3460 error:
3461 if (compArr != &path) {
3462 Blt_Free(compArr);
3463 }
3464 Tcl_DecrRefCount(listObjPtr);
3465 if (node != NULL) {
3466 DeleteNode(tvPtr, node);
3467 }
3468 return TCL_ERROR;
3469}
3470#endif
3471
3472/*
3473 *----------------------------------------------------------------------
3474 *
3475 * DeleteOp --
3476 *
3477 * Deletes nodes from the hierarchy. Deletes one or more entries
3478 * (except root). In all cases, nodes are removed recursively.
3479 *
3480 * Note: There's no need to explicitly clean up Entry structures
3481 * or request a redraw of the widget. When a node is
3482 * deleted in the tree, all of the Tcl_Objs representing
3483 * the various data fields are also removed. The treeview
3484 * widget store the Entry structure in a data field. So it's
3485 * automatically cleaned up when FreeEntryInternalRep is
3486 * called.
3487 *
3488 *----------------------------------------------------------------------
3489 */
3490/*ARGSUSED*/
3491static int
3492DeleteOp(tvPtr, interp, objc, objv)
3493 TreeView *tvPtr;
3494 Tcl_Interp *interp;
3495 int objc; /* Not used. */
3496 Tcl_Obj *CONST *objv;
3497{
3498 TreeViewTagInfo info;
3499 TreeViewEntry *entryPtr;
3500 register int i;
3501
3502 for (i = 2; i < objc; i++) {
3503 if (Blt_TreeViewFindTaggedEntries(tvPtr, objv[i], &info) != TCL_OK) {
3504 return TCL_ERROR;
3505 }
3506 for (entryPtr = Blt_TreeViewFirstTaggedEntry(&info); entryPtr != NULL;
3507 entryPtr = Blt_TreeViewNextTaggedEntry(&info)) {
3508 if (entryPtr == tvPtr->rootPtr) {
3509 Blt_TreeNode next, node;
3510
3511 /*
3512 * Don't delete the root node. We implicitly assume
3513 * that even an empty tree has at a root. Instead
3514 * delete all the children regardless if they're closed
3515 * or hidden.
3516 */
3517 for (node = Blt_TreeFirstChild(entryPtr->node); node != NULL;
3518 node = next) {
3519 next = Blt_TreeNextSibling(node);
3520 DeleteNode(tvPtr, node);
3521 }
3522 } else {
3523 DeleteNode(tvPtr, entryPtr->node);
3524 }
3525 }
3526 }
3527 return TCL_OK;
3528}
3529
3530/*
3531 *----------------------------------------------------------------------
3532 *
3533 * MoveOp --
3534 *
3535 * Move an entry into a new location in the hierarchy.
3536 *
3537 *
3538 *----------------------------------------------------------------------
3539 */
3540/*ARGSUSED*/
3541static int
3542MoveOp(tvPtr, interp, objc, objv)
3543 TreeView *tvPtr;
3544 Tcl_Interp *interp;
3545 int objc; /* Not used. */
3546 Tcl_Obj *CONST *objv;
3547{
3548 Blt_TreeNode parent;
3549 TreeViewEntry *srcPtr, *destPtr;
3550 char c;
3551 int action;
3552 char *string;
3553 TreeViewTagInfo info;
3554
3555#define MOVE_INTO (1<<0)
3556#define MOVE_BEFORE (1<<1)
3557#define MOVE_AFTER (1<<2)
3558 if (Blt_TreeViewFindTaggedEntries(tvPtr, objv[2], &info) != TCL_OK) {
3559 return TCL_ERROR;
3560 }
3561 string = Tcl_GetString(objv[3]);
3562 c = string[0];
3563 if ((c == 'i') && (strcmp(string, "into") == 0)) {
3564 action = MOVE_INTO;
3565 } else if ((c == 'b') && (strcmp(string, "before") == 0)) {
3566 action = MOVE_BEFORE;
3567 } else if ((c == 'a') && (strcmp(string, "after") == 0)) {
3568 action = MOVE_AFTER;
3569 } else {
3570 Tcl_AppendResult(interp, "bad position \"", string,
3571 "\": should be into, before, or after", (char *)NULL);
3572 return TCL_ERROR;
3573 }
3574 if (Blt_TreeViewGetEntry(tvPtr, objv[4], &destPtr) != TCL_OK) {
3575 return TCL_ERROR;
3576 }
3577 for (srcPtr = Blt_TreeViewFirstTaggedEntry(&info); srcPtr != NULL;
3578 srcPtr = Blt_TreeViewNextTaggedEntry(&info)) {
3579 /* Verify they aren't ancestors. */
3580 if (Blt_TreeIsAncestor(srcPtr->node, destPtr->node)) {
3581 Tcl_DString dString;
3582 char *path;
3583
3584 path = Blt_TreeViewGetFullName(tvPtr, srcPtr, 1, &dString);
3585 Tcl_AppendResult(interp, "can't move node: \"", path,
3586 "\" is an ancestor of \"", Tcl_GetString(objv[4]),
3587 "\"", (char *)NULL);
3588 Tcl_DStringFree(&dString);
3589 return TCL_ERROR;
3590 }
3591 parent = Blt_TreeNodeParent(destPtr->node);
3592 if (parent == NULL) {
3593 action = MOVE_INTO;
3594 }
3595 switch (action) {
3596 case MOVE_INTO:
3597 Blt_TreeMoveNode(tvPtr->tree, srcPtr->node, destPtr->node,
3598 (Blt_TreeNode)NULL);
3599 break;
3600
3601 case MOVE_BEFORE:
3602 Blt_TreeMoveNode(tvPtr->tree, srcPtr->node, parent, destPtr->node);
3603 break;
3604
3605 case MOVE_AFTER:
3606 Blt_TreeMoveNode(tvPtr->tree, srcPtr->node, parent,
3607 Blt_TreeNextSibling(destPtr->node));
3608 break;
3609 }
3610 }
3611 tvPtr->flags |= (TV_LAYOUT | TV_DIRTY | TV_RESORT);
3612 Blt_TreeViewEventuallyRedraw(tvPtr);
3613 return TCL_OK;
3614}
3615
3616/*ARGSUSED*/
3617static int
3618NearestOp(tvPtr, interp, objc, objv)
3619 TreeView *tvPtr;
3620 Tcl_Interp *interp;
3621 int objc; /* Not used. */
3622 Tcl_Obj *CONST *objv;
3623{
3624 TreeViewButton *buttonPtr = &tvPtr->button;
3625 int x, y; /* Screen coordinates of the test point. */
3626 register TreeViewEntry *entryPtr;
3627 int isRoot;
3628 char *string;
3629
3630 isRoot = FALSE;
3631 string = Tcl_GetString(objv[2]);
3632 if (strcmp("-root", string) == 0) {
3633 isRoot = TRUE;
3634 objv++, objc--;
3635 }
3636 if (objc < 4) {
3637 Tcl_AppendResult(interp, "wrong # args: should be \"",
3638 Tcl_GetString(objv[0]), " ", Tcl_GetString(objv[1]),
3639 " ?-root? x y\"", (char *)NULL);
3640 return TCL_ERROR;
3641
3642 }
3643 if ((Tk_GetPixelsFromObj(interp, tvPtr->tkwin, objv[2], &x) != TCL_OK) ||
3644 (Tk_GetPixelsFromObj(interp, tvPtr->tkwin, objv[3], &y) != TCL_OK)) {
3645 return TCL_ERROR;
3646 }
3647 if (tvPtr->nVisible == 0) {
3648 return TCL_OK;
3649 }
3650 if (isRoot) {
3651 int rootX, rootY;
3652
3653 Tk_GetRootCoords(tvPtr->tkwin, &rootX, &rootY);
3654 x -= rootX;
3655 y -= rootY;
3656 }
3657 entryPtr = Blt_TreeViewNearestEntry(tvPtr, x, y, TRUE);
3658 if (entryPtr == NULL) {
3659 return TCL_OK;
3660 }
3661 x = WORLDX(tvPtr, x);
3662 y = WORLDY(tvPtr, y);
3663 if (objc > 4) {
3664 char *where;
3665 int labelX, labelY, depth;
3666 TreeViewIcon icon;
3667
3668 where = "";
3669 if (entryPtr->flags & ENTRY_HAS_BUTTON) {
3670 int buttonX, buttonY;
3671
3672 buttonX = entryPtr->worldX + entryPtr->buttonX;
3673 buttonY = entryPtr->worldY + entryPtr->buttonY;
3674 if ((x >= buttonX) && (x < (buttonX + buttonPtr->width)) &&
3675 (y >= buttonY) && (y < (buttonY + buttonPtr->height))) {
3676 where = "button";
3677 goto done;
3678 }
3679 }
3680 depth = DEPTH(tvPtr, entryPtr->node);
3681
3682 icon = Blt_TreeViewGetEntryIcon(tvPtr, entryPtr);
3683 if (icon != NULL) {
3684 int iconWidth, iconHeight, entryHeight;
3685 int iconX, iconY;
3686
3687 entryHeight = MAX(entryPtr->iconHeight, tvPtr->button.height);
3688 iconHeight = TreeViewIconHeight(icon);
3689 iconWidth = TreeViewIconWidth(icon);
3690 iconX = entryPtr->worldX + ICONWIDTH(depth);
3691 iconY = entryPtr->worldY;
3692 if (tvPtr->flatView) {
3693 iconX += (ICONWIDTH(0) - iconWidth) / 2;
3694 } else {
3695 iconX += (ICONWIDTH(depth + 1) - iconWidth) / 2;
3696 }
3697 iconY += (entryHeight - iconHeight) / 2;
3698 if ((x >= iconX) && (x <= (iconX + iconWidth)) &&
3699 (y >= iconY) && (y < (iconY + iconHeight))) {
3700 where = "icon";
3701 goto done;
3702 }
3703 }
3704 labelX = entryPtr->worldX + ICONWIDTH(depth);
3705 labelY = entryPtr->worldY;
3706 if (!tvPtr->flatView) {
3707 labelX += ICONWIDTH(depth + 1) + 4;
3708 }
3709 if ((x >= labelX) && (x < (labelX + entryPtr->labelWidth)) &&
3710 (y >= labelY) && (y < (labelY + entryPtr->labelHeight))) {
3711 where = "label";
3712 }
3713 done:
3714 if (Tcl_SetVar(interp, Tcl_GetString(objv[4]), where,
3715 TCL_LEAVE_ERR_MSG) == NULL) {
3716 return TCL_ERROR;
3717 }
3718 }
3719 Tcl_SetObjResult(interp, NodeToObj(entryPtr->node));
3720 return TCL_OK;
3721}
3722
3723
3724/*ARGSUSED*/
3725static int
3726OpenOp(tvPtr, interp, objc, objv)
3727 TreeView *tvPtr;
3728 Tcl_Interp *interp; /* Not used. */
3729 int objc;
3730 Tcl_Obj *CONST *objv;
3731{
3732 TreeViewEntry *entryPtr;
3733 TreeViewTagInfo info;
3734 int recurse, result;
3735 register int i;
3736
3737 recurse = FALSE;
3738 if (objc > 2) {
3739 int length;
3740 char *string;
3741
3742 string = Tcl_GetStringFromObj(objv[2], &length);
3743 if ((string[0] == '-') && (length > 1) &&
3744 (strncmp(string, "-recurse", length) == 0)) {
3745 objv++, objc--;
3746 recurse = TRUE;
3747 }
3748 }
3749 for (i = 2; i < objc; i++) {
3750 if (Blt_TreeViewFindTaggedEntries(tvPtr, objv[i], &info) != TCL_OK) {
3751 return TCL_ERROR;
3752 }
3753 for (entryPtr = Blt_TreeViewFirstTaggedEntry(&info); entryPtr != NULL;
3754 entryPtr = Blt_TreeViewNextTaggedEntry(&info)) {
3755 if (recurse) {
3756 result = Blt_TreeViewApply(tvPtr, entryPtr,
3757 Blt_TreeViewOpenEntry, 0);
3758 } else {
3759 result = Blt_TreeViewOpenEntry(tvPtr, entryPtr);
3760 }
3761 if (result != TCL_OK) {
3762 return TCL_ERROR;
3763 }
3764 /* Make sure ancestors of this node aren't hidden. */
3765 MapAncestors(tvPtr, entryPtr);
3766 }
3767 }
3768 /*FIXME: This is only for flattened entries. */
3769 tvPtr->flags |= (TV_LAYOUT | TV_DIRTY | TV_RESORT);
3770 Blt_TreeViewEventuallyRedraw(tvPtr);
3771 return TCL_OK;
3772}
3773
3774/*
3775 *----------------------------------------------------------------------
3776 *
3777 * RangeOp --
3778 *
3779 * Returns the node identifiers in a given range.
3780 *
3781 *----------------------------------------------------------------------
3782 */
3783static int
3784RangeOp(tvPtr, interp, objc, objv)
3785 TreeView *tvPtr;
3786 Tcl_Interp *interp;
3787 int objc;
3788 Tcl_Obj *CONST *objv;
3789{
3790 TreeViewEntry *entryPtr, *firstPtr, *lastPtr;
3791 unsigned int mask;
3792 int length;
3793 Tcl_Obj *listObjPtr, *objPtr;
3794 char *string;
3795
3796 mask = 0;
3797 string = Tcl_GetStringFromObj(objv[2], &length);
3798 if ((string[0] == '-') && (length > 1) &&
3799 (strncmp(string, "-open", length) == 0)) {
3800 objv++, objc--;
3801 mask |= ENTRY_CLOSED;
3802 }
3803 if (Blt_TreeViewGetEntry(tvPtr, objv[2], &firstPtr) != TCL_OK) {
3804 return TCL_ERROR;
3805 }
3806 if (objc > 3) {
3807 if (Blt_TreeViewGetEntry(tvPtr, objv[3], &lastPtr) != TCL_OK) {
3808 return TCL_ERROR;
3809 }
3810 } else {
3811 lastPtr = LastEntry(tvPtr, firstPtr, mask);
3812 }
3813 if (mask & ENTRY_CLOSED) {
3814 if (firstPtr->flags & ENTRY_HIDDEN) {
3815 Tcl_AppendResult(interp, "first node \"", Tcl_GetString(objv[2]),
3816 "\" is hidden.", (char *)NULL);
3817 return TCL_ERROR;
3818 }
3819 if (lastPtr->flags & ENTRY_HIDDEN) {
3820 Tcl_AppendResult(interp, "last node \"", Tcl_GetString(objv[3]),
3821 "\" is hidden.", (char *)NULL);
3822 return TCL_ERROR;
3823 }
3824 }
3825
3826 /*
3827 * The relative order of the first/last markers determines the
3828 * direction.
3829 */
3830 listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
3831 if (Blt_TreeIsBefore(lastPtr->node, firstPtr->node)) {
3832 for (entryPtr = lastPtr; entryPtr != NULL;
3833 entryPtr = Blt_TreeViewPrevEntry(entryPtr, mask)) {
3834 objPtr = NodeToObj(entryPtr->node);
3835 Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
3836 if (entryPtr == firstPtr) {
3837 break;
3838 }
3839 }
3840 } else {
3841 for (entryPtr = firstPtr; entryPtr != NULL;
3842 entryPtr = Blt_TreeViewNextEntry(entryPtr, mask)) {
3843 objPtr = NodeToObj(entryPtr->node);
3844 Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
3845 if (entryPtr == lastPtr) {
3846 break;
3847 }
3848 }
3849 }
3850 Tcl_SetObjResult(interp, listObjPtr);
3851 return TCL_OK;
3852}
3853
3854/*
3855 *----------------------------------------------------------------------
3856 *
3857 * ScanOp --
3858 *
3859 * Implements the quick scan.
3860 *
3861 *----------------------------------------------------------------------
3862 */
3863/*ARGSUSED*/
3864static int
3865ScanOp(tvPtr, interp, objc, objv)
3866 TreeView *tvPtr;
3867 Tcl_Interp *interp;
3868 int objc; /* Not used. */
3869 Tcl_Obj *CONST *objv;
3870{
3871 int x, y;
3872 char c;
3873 int length;
3874 int oper;
3875 char *string;
3876 Tk_Window tkwin;
3877
3878#define SCAN_MARK 1
3879#define SCAN_DRAGTO 2
3880 string = Tcl_GetStringFromObj(objv[2], &length);
3881 c = string[0];
3882 tkwin = tvPtr->tkwin;
3883 if ((c == 'm') && (strncmp(string, "mark", length) == 0)) {
3884 oper = SCAN_MARK;
3885 } else if ((c == 'd') && (strncmp(string, "dragto", length) == 0)) {
3886 oper = SCAN_DRAGTO;
3887 } else {
3888 Tcl_AppendResult(interp, "bad scan operation \"", string,
3889 "\": should be either \"mark\" or \"dragto\"", (char *)NULL);
3890 return TCL_ERROR;
3891 }
3892 if ((Blt_GetPixelsFromObj(interp, tkwin, objv[3], 0, &x) != TCL_OK) ||
3893 (Blt_GetPixelsFromObj(interp, tkwin, objv[4], 0, &y) != TCL_OK)) {
3894 return TCL_ERROR;
3895 }
3896 if (oper == SCAN_MARK) {
3897 tvPtr->scanAnchorX = x;
3898 tvPtr->scanAnchorY = y;
3899 tvPtr->scanX = tvPtr->xOffset;
3900 tvPtr->scanY = tvPtr->yOffset;
3901 } else {
3902 int worldX, worldY;
3903 int dx, dy;
3904
3905 dx = tvPtr->scanAnchorX - x;
3906 dy = tvPtr->scanAnchorY - y;
3907 worldX = tvPtr->scanX + (10 * dx);
3908 worldY = tvPtr->scanY + (10 * dy);
3909
3910 if (worldX < 0) {
3911 worldX = 0;
3912 } else if (worldX >= tvPtr->worldWidth) {
3913 worldX = tvPtr->worldWidth - tvPtr->xScrollUnits;
3914 }
3915 if (worldY < 0) {
3916 worldY = 0;
3917 } else if (worldY >= tvPtr->worldHeight) {
3918 worldY = tvPtr->worldHeight - tvPtr->yScrollUnits;
3919 }
3920 tvPtr->xOffset = worldX;
3921 tvPtr->yOffset = worldY;
3922 tvPtr->flags |= TV_SCROLL;
3923 Blt_TreeViewEventuallyRedraw(tvPtr);
3924 }
3925 return TCL_OK;
3926}
3927
3928/*ARGSUSED*/
3929static int
3930SeeOp(tvPtr, interp, objc, objv)
3931 TreeView *tvPtr;
3932 Tcl_Interp *interp; /* Not used. */
3933 int objc;
3934 Tcl_Obj *CONST *objv;
3935{
3936 TreeViewEntry *entryPtr;
3937 int width, height;
3938 int x, y;
3939 Tk_Anchor anchor;
3940 int left, right, top, bottom;
3941 char *string;
3942
3943 string = Tcl_GetString(objv[2]);
3944 anchor = TK_ANCHOR_W; /* Default anchor is West */
3945 if ((string[0] == '-') && (strcmp(string, "-anchor") == 0)) {
3946 if (objc == 3) {
3947 Tcl_AppendResult(interp, "missing \"-anchor\" argument",
3948 (char *)NULL);
3949 return TCL_ERROR;
3950 }
3951 if (Tk_GetAnchorFromObj(interp, objv[3], &anchor) != TCL_OK) {
3952 return TCL_ERROR;
3953 }
3954 objc -= 2, objv += 2;
3955 }
3956 if (objc == 2) {
3957 Tcl_AppendResult(interp, "wrong # args: should be \"", objv[0],
3958 "see ?-anchor anchor? tagOrId\"", (char *)NULL);
3959 return TCL_ERROR;
3960 }
3961 if (GetEntryFromObj(tvPtr, objv[2], &entryPtr) != TCL_OK) {
3962 return TCL_ERROR;
3963 }
3964 if (entryPtr == NULL) {
3965 return TCL_OK;
3966 }
3967 if (entryPtr->flags & ENTRY_HIDDEN) {
3968 MapAncestors(tvPtr, entryPtr);
3969 tvPtr->flags |= TV_SCROLL;
3970 /*
3971 * If the entry wasn't previously exposed, its world coordinates
3972 * aren't likely to be valid. So re-compute the layout before
3973 * we try to see the viewport to the entry's location.
3974 */
3975 Blt_TreeViewComputeLayout(tvPtr);
3976 }
3977 width = VPORTWIDTH(tvPtr);
3978 height = VPORTHEIGHT(tvPtr);
3979
3980 /*
3981 * XVIEW: If the entry is left or right of the current view, adjust
3982 * the offset. If the entry is nearby, adjust the view just
3983 * a bit. Otherwise, center the entry.
3984 */
3985 left = tvPtr->xOffset;
3986 right = tvPtr->xOffset + width;
3987
3988 switch (anchor) {
3989 case TK_ANCHOR_W:
3990 case TK_ANCHOR_NW:
3991 case TK_ANCHOR_SW:
3992 x = 0;
3993 break;
3994 case TK_ANCHOR_E:
3995 case TK_ANCHOR_NE:
3996 case TK_ANCHOR_SE:
3997 x = entryPtr->worldX + entryPtr->width +
3998 ICONWIDTH(DEPTH(tvPtr, entryPtr->node)) - width;
3999 break;
4000 default:
4001 if (entryPtr->worldX < left) {
4002 x = entryPtr->worldX;
4003 } else if ((entryPtr->worldX + entryPtr->width) > right) {
4004 x = entryPtr->worldX + entryPtr->width - width;
4005 } else {
4006 x = tvPtr->xOffset;
4007 }
4008 break;
4009 }
4010 /*
4011 * YVIEW: If the entry is above or below the current view, adjust
4012 * the offset. If the entry is nearby, adjust the view just
4013 * a bit. Otherwise, center the entry.
4014 */
4015 top = tvPtr->yOffset;
4016 bottom = tvPtr->yOffset + height;
4017
4018 switch (anchor) {
4019 case TK_ANCHOR_N:
4020 y = tvPtr->yOffset;
4021 break;
4022 case TK_ANCHOR_NE:
4023 case TK_ANCHOR_NW:
4024 y = entryPtr->worldY - (height / 2);
4025 break;
4026 case TK_ANCHOR_S:
4027 case TK_ANCHOR_SE:
4028 case TK_ANCHOR_SW:
4029 y = entryPtr->worldY + entryPtr->height - height;
4030 break;
4031 default:
4032 if (entryPtr->worldY < top) {
4033 y = entryPtr->worldY;
4034 } else if ((entryPtr->worldY + entryPtr->height) > bottom) {
4035 y = entryPtr->worldY + entryPtr->height - height;
4036 } else {
4037 y = tvPtr->yOffset;
4038 }
4039 break;
4040 }
4041 if ((y != tvPtr->yOffset) || (x != tvPtr->xOffset)) {
4042 /* tvPtr->xOffset = x; */
4043 tvPtr->yOffset = y;
4044 tvPtr->flags |= TV_SCROLL;
4045 }
4046 Blt_TreeViewEventuallyRedraw(tvPtr);
4047 return TCL_OK;
4048}
4049
4050void
4051Blt_TreeViewClearSelection(tvPtr)
4052 TreeView *tvPtr;
4053{
4054 Blt_DeleteHashTable(&tvPtr->selectTable);
4055 Blt_InitHashTable(&tvPtr->selectTable, BLT_ONE_WORD_KEYS);
4056 Blt_ChainReset(tvPtr->selChainPtr);
4057 Blt_TreeViewEventuallyRedraw(tvPtr);
4058 if (tvPtr->selectCmd != NULL) {
4059 EventuallyInvokeSelectCmd(tvPtr);
4060 }
4061}
4062
4063/*
4064 *----------------------------------------------------------------------
4065 *
4066 * LostSelection --
4067 *
4068 * This procedure is called back by Tk when the selection is grabbed
4069 * away.
4070 *
4071 * Results:
4072 * None.
4073 *
4074 * Side effects:
4075 * The existing selection is unhighlighted, and the window is
4076 * marked as not containing a selection.
4077 *
4078 *----------------------------------------------------------------------
4079 */
4080static void
4081LostSelection(clientData)
4082 ClientData clientData; /* Information about the widget. */
4083{
4084 TreeView *tvPtr = clientData;
4085
4086 if ((tvPtr->flags & TV_SELECT_EXPORT) == 0) {
4087 return;
4088 }
4089 Blt_TreeViewClearSelection(tvPtr);
4090}
4091
4092/*
4093 *----------------------------------------------------------------------
4094 *
4095 * SelectRange --
4096 *
4097 * Sets the selection flag for a range of nodes. The range is
4098 * determined by two pointers which designate the first/last
4099 * nodes of the range.
4100 *
4101 * Results:
4102 * Always returns TCL_OK.
4103 *
4104 *----------------------------------------------------------------------
4105 */
4106static int
4107SelectRange(tvPtr, fromPtr, toPtr)
4108 TreeView *tvPtr;
4109 TreeViewEntry *fromPtr, *toPtr;
4110{
4111 if (tvPtr->flatView) {
4112 register int i;
4113
4114 if (fromPtr->flatIndex > toPtr->flatIndex) {
4115 for (i = fromPtr->flatIndex; i >= toPtr->flatIndex; i--) {
4116 SelectEntryApplyProc(tvPtr, tvPtr->flatArr[i]);
4117 }
4118 } else {
4119 for (i = fromPtr->flatIndex; i <= toPtr->flatIndex; i++) {
4120 SelectEntryApplyProc(tvPtr, tvPtr->flatArr[i]);
4121 }
4122 }
4123 } else {
4124 TreeViewEntry *entryPtr;
4125 TreeViewIterProc *proc;
4126 /* From the range determine the direction to select entries. */
4127
4128 proc = (Blt_TreeIsBefore(toPtr->node, fromPtr->node))
4129 ? Blt_TreeViewPrevEntry : Blt_TreeViewNextEntry;
4130 for (entryPtr = fromPtr; entryPtr != NULL;
4131 entryPtr = (*proc)(entryPtr, ENTRY_MASK)) {
4132 SelectEntryApplyProc(tvPtr, entryPtr);
4133 if (entryPtr == toPtr) {
4134 break;
4135 }
4136 }
4137 }
4138 return TCL_OK;
4139}
4140
4141/*
4142 *----------------------------------------------------------------------
4143 *
4144 * SelectionAnchorOp --
4145 *
4146 * Sets the selection anchor to the element given by a index.
4147 * The selection anchor is the end of the selection that is fixed
4148 * while dragging out a selection with the mouse. The index
4149 * "anchor" may be used to refer to the anchor element.
4150 *
4151 * Results:
4152 * None.
4153 *
4154 * Side effects:
4155 * The selection changes.
4156 *
4157 *----------------------------------------------------------------------
4158 */
4159/*ARGSUSED*/
4160static int
4161SelectionAnchorOp(tvPtr, interp, objc, objv)
4162 TreeView *tvPtr;
4163 Tcl_Interp *interp; /* Not used. */
4164 int objc; /* Not used. */
4165 Tcl_Obj *CONST *objv;
4166{
4167 TreeViewEntry *entryPtr;
4168
4169 if (GetEntryFromObj(tvPtr, objv[3], &entryPtr) != TCL_OK) {
4170 return TCL_ERROR;
4171 }
4172 /* Set both the anchor and the mark. Indicates that a single entry
4173 * is selected. */
4174 tvPtr->selAnchorPtr = entryPtr;
4175 tvPtr->selMarkPtr = NULL;
4176 if (entryPtr != NULL) {
4177 Tcl_SetObjResult(interp, NodeToObj(entryPtr->node));
4178 }
4179 Blt_TreeViewEventuallyRedraw(tvPtr);
4180 return TCL_OK;
4181}
4182
4183
4184/*
4185 *----------------------------------------------------------------------
4186 *
4187 * SelectionClearallOp
4188 *
4189 * Clears the entire selection.
4190 *
4191 * Results:
4192 * None.
4193 *
4194 * Side effects:
4195 * The selection changes.
4196 *
4197 *----------------------------------------------------------------------
4198 */
4199/*ARGSUSED*/
4200static int
4201SelectionClearallOp(tvPtr, interp, objc, objv)
4202 TreeView *tvPtr;
4203 Tcl_Interp *interp; /* Not used. */
4204 int objc; /* Not used. */
4205 Tcl_Obj *CONST *objv; /* Not used. */
4206{
4207 Blt_TreeViewClearSelection(tvPtr);
4208 return TCL_OK;
4209}
4210
4211/*
4212 *----------------------------------------------------------------------
4213 *
4214 * SelectionIncludesOp
4215 *
4216 * Returns 1 if the element indicated by index is currently
4217 * selected, 0 if it isn't.
4218 *
4219 * Results:
4220 * None.
4221 *
4222 * Side effects:
4223 * The selection changes.
4224 *
4225 *----------------------------------------------------------------------
4226 */
4227/*ARGSUSED*/
4228static int
4229SelectionIncludesOp(tvPtr, interp, objc, objv)
4230 TreeView *tvPtr;
4231 Tcl_Interp *interp;
4232 int objc; /* Not used. */
4233 Tcl_Obj *CONST *objv;
4234{
4235 TreeViewEntry *entryPtr;
4236 int bool;
4237
4238 if (Blt_TreeViewGetEntry(tvPtr, objv[3], &entryPtr) != TCL_OK) {
4239 return TCL_ERROR;
4240 }
4241 bool = Blt_TreeViewEntryIsSelected(tvPtr, entryPtr);
4242 Tcl_SetObjResult(interp, Tcl_NewBooleanObj(bool));
4243 return TCL_OK;
4244}
4245
4246/*
4247 *----------------------------------------------------------------------
4248 *
4249 * SelectionMarkOp --
4250 *
4251 * Sets the selection mark to the element given by a index.
4252 * The selection anchor is the end of the selection that is movable
4253 * while dragging out a selection with the mouse. The index
4254 * "mark" may be used to refer to the anchor element.
4255 *
4256 * Results:
4257 * None.
4258 *
4259 * Side effects:
4260 * The selection changes.
4261 *
4262 *----------------------------------------------------------------------
4263 */
4264/*ARGSUSED*/
4265static int
4266SelectionMarkOp(tvPtr, interp, objc, objv)
4267 TreeView *tvPtr;
4268 Tcl_Interp *interp; /* Not used. */
4269 int objc; /* Not used. */
4270 Tcl_Obj *CONST *objv;
4271{
4272 TreeViewEntry *entryPtr;
4273
4274 if (GetEntryFromObj(tvPtr, objv[3], &entryPtr) != TCL_OK) {
4275 return TCL_ERROR;
4276 }
4277 if (tvPtr->selAnchorPtr == NULL) {
4278 Tcl_AppendResult(interp, "selection anchor must be set first",
4279 (char *)NULL);
4280 return TCL_ERROR;
4281 }
4282 if (tvPtr->selMarkPtr != entryPtr) {
4283 Blt_ChainLink *linkPtr, *nextPtr;
4284 TreeViewEntry *selectPtr;
4285
4286 /* Deselect entry from the list all the way back to the anchor. */
4287 for (linkPtr = Blt_ChainLastLink(tvPtr->selChainPtr); linkPtr != NULL;
4288 linkPtr = nextPtr) {
4289 nextPtr = Blt_ChainPrevLink(linkPtr);
4290 selectPtr = Blt_ChainGetValue(linkPtr);
4291 if (selectPtr == tvPtr->selAnchorPtr) {
4292 break;
4293 }
4294 Blt_TreeViewDeselectEntry(tvPtr, selectPtr);
4295 }
4296 tvPtr->flags &= ~TV_SELECT_MASK;
4297 tvPtr->flags |= TV_SELECT_SET;
4298 SelectRange(tvPtr, tvPtr->selAnchorPtr, entryPtr);
4299 Tcl_SetObjResult(interp, NodeToObj(entryPtr->node));
4300 tvPtr->selMarkPtr = entryPtr;
4301
4302 Blt_TreeViewEventuallyRedraw(tvPtr);
4303 if (tvPtr->selectCmd != NULL) {
4304 EventuallyInvokeSelectCmd(tvPtr);
4305 }
4306 }
4307 return TCL_OK;
4308}
4309
4310/*
4311 *----------------------------------------------------------------------
4312 *
4313 * SelectionPresentOp
4314 *
4315 * Returns 1 if there is a selection and 0 if it isn't.
4316 *
4317 * Results:
4318 * A standard Tcl result. interp->result will contain a
4319 * boolean string indicating if there is a selection.
4320 *
4321 *----------------------------------------------------------------------
4322 */
4323/*ARGSUSED*/
4324static int
4325SelectionPresentOp(tvPtr, interp, objc, objv)
4326 TreeView *tvPtr;
4327 Tcl_Interp *interp;
4328 int objc; /* Not used. */
4329 Tcl_Obj *CONST *objv;
4330{
4331 int bool;
4332
4333 bool = (Blt_ChainGetLength(tvPtr->selChainPtr) > 0);
4334 Tcl_SetObjResult(interp, Tcl_NewBooleanObj(bool));
4335 return TCL_OK;
4336}
4337
4338/*
4339 *----------------------------------------------------------------------
4340 *
4341 * SelectionSetOp
4342 *
4343 * Selects, deselects, or toggles all of the elements in the
4344 * range between first and last, inclusive, without affecting the
4345 * selection state of elements outside that range.
4346 *
4347 * Results:
4348 * None.
4349 *
4350 * Side effects:
4351 * The selection changes.
4352 *
4353 *----------------------------------------------------------------------
4354 */
4355/*ARGSUSED*/
4356static int
4357SelectionSetOp(tvPtr, interp, objc, objv)
4358 TreeView *tvPtr;
4359 Tcl_Interp *interp;
4360 int objc; /* Not used. */
4361 Tcl_Obj *CONST *objv;
4362{
4363 TreeViewEntry *firstPtr, *lastPtr;
4364 char *string;
4365
4366 tvPtr->flags &= ~TV_SELECT_MASK;
4367 string = Tcl_GetString(objv[2]);
4368 switch (string[0]) {
4369 case 's':
4370 tvPtr->flags |= TV_SELECT_SET;
4371 break;
4372 case 'c':
4373 tvPtr->flags |= TV_SELECT_CLEAR;
4374 break;
4375 case 't':
4376 tvPtr->flags |= TV_SELECT_TOGGLE;
4377 break;
4378 }
4379 if (Blt_TreeViewGetEntry(tvPtr, objv[3], &firstPtr) != TCL_OK) {
4380 return TCL_ERROR;
4381 }
4382 if ((firstPtr->flags & ENTRY_HIDDEN) &&
4383 (!(tvPtr->flags & TV_SELECT_CLEAR))) {
4384 Tcl_AppendResult(interp, "can't select hidden node \"",
4385 Tcl_GetString(objv[3]), "\"", (char *)NULL);
4386 return TCL_ERROR;
4387 }
4388 lastPtr = firstPtr;
4389 if (objc > 4) {
4390 if (Blt_TreeViewGetEntry(tvPtr, objv[4], &lastPtr) != TCL_OK) {
4391 return TCL_ERROR;
4392 }
4393 if ((lastPtr->flags & ENTRY_HIDDEN) &&
4394 (!(tvPtr->flags & TV_SELECT_CLEAR))) {
4395 Tcl_AppendResult(interp, "can't select hidden node \"",
4396 Tcl_GetString(objv[4]), "\"", (char *)NULL);
4397 return TCL_ERROR;
4398 }
4399 }
4400 if (firstPtr == lastPtr) {
4401 SelectEntryApplyProc(tvPtr, firstPtr);
4402 } else {
4403 SelectRange(tvPtr, firstPtr, lastPtr);
4404 }
4405 /* Set both the anchor and the mark. Indicates that a single entry
4406 * is selected. */
4407 if (tvPtr->selAnchorPtr == NULL) {
4408 tvPtr->selAnchorPtr = firstPtr;
4409 }
4410 if (tvPtr->flags & TV_SELECT_EXPORT) {
4411 Tk_OwnSelection(tvPtr->tkwin, XA_PRIMARY, LostSelection, tvPtr);
4412 }
4413 Blt_TreeViewEventuallyRedraw(tvPtr);
4414 if (tvPtr->selectCmd != NULL) {
4415 EventuallyInvokeSelectCmd(tvPtr);
4416 }
4417 return TCL_OK;
4418}
4419
4420/*
4421 *----------------------------------------------------------------------
4422 *
4423 * SelectionOp --
4424 *
4425 * This procedure handles the individual options for text
4426 * selections. The selected text is designated by start and end
4427 * indices into the text pool. The selected segment has both a
4428 * anchored and unanchored ends.
4429 *
4430 * Results:
4431 * None.
4432 *
4433 * Side effects:
4434 * The selection changes.
4435 *
4436 *----------------------------------------------------------------------
4437 */
4438static Blt_OpSpec selectionOps[] =
4439{
4440 {"anchor", 1, (Blt_Op)SelectionAnchorOp, 4, 4, "tagOrId",},
4441 {"clear", 5, (Blt_Op)SelectionSetOp, 4, 5, "first ?last?",},
4442 {"clearall", 6, (Blt_Op)SelectionClearallOp, 3, 3, "",},
4443 {"includes", 1, (Blt_Op)SelectionIncludesOp, 4, 4, "tagOrId",},
4444 {"mark", 1, (Blt_Op)SelectionMarkOp, 4, 4, "tagOrId",},
4445 {"present", 1, (Blt_Op)SelectionPresentOp, 3, 3, "",},
4446 {"set", 1, (Blt_Op)SelectionSetOp, 4, 5, "first ?last?",},
4447 {"toggle", 1, (Blt_Op)SelectionSetOp, 4, 5, "first ?last?",},
4448};
4449static int nSelectionOps = sizeof(selectionOps) / sizeof(Blt_OpSpec);
4450
4451static int
4452SelectionOp(tvPtr, interp, objc, objv)
4453 TreeView *tvPtr;
4454 Tcl_Interp *interp;
4455 int objc;
4456 Tcl_Obj *CONST *objv;
4457{
4458 Blt_Op proc;
4459 int result;
4460
4461 proc = Blt_GetOpFromObj(interp, nSelectionOps, selectionOps, BLT_OP_ARG2,
4462 objc, objv, 0);
4463 if (proc == NULL) {
4464 return TCL_ERROR;
4465 }
4466 result = (*proc) (tvPtr, interp, objc, objv);
4467 return result;
4468}
4469
4470
4471/*
4472 *----------------------------------------------------------------------
4473 *
4474 * TagForgetOp --
4475 *
4476 *----------------------------------------------------------------------
4477 */
4478/*ARGSUSED*/
4479static int
4480TagForgetOp(tvPtr, interp, objc, objv)
4481 TreeView *tvPtr;
4482 Tcl_Interp *interp;
4483 int objc; /* Not used. */
4484 Tcl_Obj *CONST *objv;
4485{
4486 register int i;
4487
4488 for (i = 3; i < objc; i++) {
4489 Blt_TreeForgetTag(tvPtr->tree, Tcl_GetString(objv[i]));
4490 }
4491 return TCL_OK;
4492}
4493
4494/*
4495 *----------------------------------------------------------------------
4496 *
4497 * TagNamesOp --
4498 *
4499 *----------------------------------------------------------------------
4500 */
4501static int
4502TagNamesOp(tvPtr, interp, objc, objv)
4503 TreeView *tvPtr;
4504 Tcl_Interp *interp;
4505 int objc;
4506 Tcl_Obj *CONST *objv;
4507{
4508 Tcl_Obj *listObjPtr, *objPtr;
4509
4510 listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
4511 objPtr = Tcl_NewStringObj("all", -1);
4512 Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
4513 if (objc == 3) {
4514 Blt_HashEntry *hPtr;
4515 Blt_HashSearch cursor;
4516 Blt_TreeTagEntry *tPtr;
4517
4518 objPtr = Tcl_NewStringObj("root", -1);
4519 Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
4520 for (hPtr = Blt_TreeFirstTag(tvPtr->tree, &cursor); hPtr != NULL;
4521 hPtr = Blt_NextHashEntry(&cursor)) {
4522 tPtr = Blt_GetHashValue(hPtr);
4523 objPtr = Tcl_NewStringObj(tPtr->tagName, -1);
4524 Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
4525 }
4526 } else {
4527 register int i;
4528 TreeViewEntry *entryPtr;
4529 Blt_List list;
4530 Blt_ListNode listNode;
4531
4532 for (i = 3; i < objc; i++) {
4533 if (Blt_TreeViewGetEntry(tvPtr, objv[i], &entryPtr) != TCL_OK) {
4534 return TCL_ERROR;
4535 }
4536 list = Blt_ListCreate(BLT_ONE_WORD_KEYS);
4537 Blt_TreeViewGetTags(interp, tvPtr, entryPtr, list);
4538 for (listNode = Blt_ListFirstNode(list); listNode != NULL;
4539 listNode = Blt_ListNextNode(listNode)) {
4540 objPtr = Tcl_NewStringObj(Blt_ListGetKey(listNode), -1);
4541 Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
4542 }
4543 Blt_ListDestroy(list);
4544 }
4545 }
4546 Tcl_SetObjResult(interp, listObjPtr);
4547 return TCL_OK;
4548}
4549
4550/*
4551 *----------------------------------------------------------------------
4552 *
4553 * TagNodesOp --
4554 *
4555 *----------------------------------------------------------------------
4556 */
4557static int
4558TagNodesOp(tvPtr, interp, objc, objv)
4559 TreeView *tvPtr;
4560 Tcl_Interp *interp;
4561 int objc;
4562 Tcl_Obj *CONST *objv;
4563{
4564 Blt_HashEntry *hPtr;
4565 Blt_HashSearch cursor;
4566 Blt_HashTable nodeTable;
4567 Blt_TreeNode node;
4568 TreeViewTagInfo info;
4569 Tcl_Obj *listObjPtr;
4570 Tcl_Obj *objPtr;
4571 TreeViewEntry *entryPtr;
4572 int isNew;
4573 register int i;
4574
4575 Blt_InitHashTable(&nodeTable, BLT_ONE_WORD_KEYS);
4576 for (i = 3; i < objc; i++) {
4577 if (Blt_TreeViewFindTaggedEntries(tvPtr, objv[i], &info) != TCL_OK) {
4578 return TCL_ERROR;
4579 }
4580 for (entryPtr = Blt_TreeViewFirstTaggedEntry(&info); entryPtr != NULL;
4581 entryPtr = Blt_TreeViewNextTaggedEntry(&info)) {
4582 Blt_CreateHashEntry(&nodeTable, (char *)entryPtr->node, &isNew);
4583 }
4584 }
4585 listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
4586 for (hPtr = Blt_FirstHashEntry(&nodeTable, &cursor); hPtr != NULL;
4587 hPtr = Blt_NextHashEntry(&cursor)) {
4588 node = (Blt_TreeNode)Blt_GetHashKey(&nodeTable, hPtr);
4589 objPtr = Tcl_NewIntObj(Blt_TreeNodeId(node));
4590 Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
4591 }
4592 Tcl_SetObjResult(interp, listObjPtr);
4593 Blt_DeleteHashTable(&nodeTable);
4594 return TCL_OK;
4595}
4596
4597/*
4598 *----------------------------------------------------------------------
4599 *
4600 * TagAddOp --
4601 *
4602 *----------------------------------------------------------------------
4603 */
4604static int
4605TagAddOp(tvPtr, interp, objc, objv)
4606 TreeView *tvPtr;
4607 Tcl_Interp *interp;
4608 int objc;
4609 Tcl_Obj *CONST *objv;
4610{
4611 TreeViewEntry *entryPtr;
4612 register int i;
4613 char *tagName;
4614 TreeViewTagInfo info;
4615
4616 tagName = Tcl_GetString(objv[3]);
4617 tvPtr->fromPtr = NULL;
4618 if (strcmp(tagName, "root") == 0) {
4619 Tcl_AppendResult(interp, "can't add reserved tag \"", tagName, "\"",
4620 (char *)NULL);
4621 return TCL_ERROR;
4622 }
4623 if (isdigit(UCHAR(tagName[0]))) {
4624 Tcl_AppendResult(interp, "invalid tag \"", tagName,
4625 "\": can't start with digit", (char *)NULL);
4626 return TCL_ERROR;
4627 }
4628 if (tagName[0] == '@') {
4629 Tcl_AppendResult(tvPtr->interp, "invalid tag \"", tagName,
4630 "\": can't start with \"@\"", (char *)NULL);
4631 return TCL_ERROR;
4632 }
4633 if (GetEntryFromSpecialId(tvPtr, tagName, &entryPtr) == TCL_OK) {
4634 Tcl_AppendResult(interp, "invalid tag \"", tagName,
4635 "\": is a special id", (char *)NULL);
4636 return TCL_ERROR;
4637 }
4638 for (i = 4; i < objc; i++) {
4639 if (Blt_TreeViewFindTaggedEntries(tvPtr, objv[i], &info) != TCL_OK) {
4640 return TCL_ERROR;
4641 }
4642 for (entryPtr = Blt_TreeViewFirstTaggedEntry(&info); entryPtr != NULL;
4643 entryPtr = Blt_TreeViewNextTaggedEntry(&info)) {
4644 if (AddTag(tvPtr, entryPtr->node, tagName) != TCL_OK) {
4645 return TCL_ERROR;
4646 }
4647 }
4648 }
4649 return TCL_OK;
4650}
4651
4652
4653/*
4654 *----------------------------------------------------------------------
4655 *
4656 * TagDeleteOp --
4657 *
4658 *----------------------------------------------------------------------
4659 */
4660/*ARGSUSED*/
4661static int
4662TagDeleteOp(tvPtr, interp, objc, objv)
4663 TreeView *tvPtr;
4664 Tcl_Interp *interp; /* Not used. */
4665 int objc;
4666 Tcl_Obj *CONST *objv;
4667{
4668 char *tagName;
4669 Blt_HashTable *tablePtr;
4670 TreeViewTagInfo info;
4671
4672 tagName = Tcl_GetString(objv[3]);
4673 tablePtr = Blt_TreeTagHashTable(tvPtr->tree, tagName);
4674 if (tablePtr != NULL) {
4675 register int i;
4676 Blt_HashEntry *hPtr;
4677 TreeViewEntry *entryPtr;
4678
4679 for (i = 4; i < objc; i++) {
4680 if (Blt_TreeViewFindTaggedEntries(tvPtr, objv[i], &info)!= TCL_OK) {
4681 return TCL_ERROR;
4682 }
4683 for (entryPtr = Blt_TreeViewFirstTaggedEntry(&info);
4684 entryPtr != NULL;
4685 entryPtr = Blt_TreeViewNextTaggedEntry(&info)) {
4686 hPtr = Blt_FindHashEntry(tablePtr, (char *)entryPtr->node);
4687 if (hPtr != NULL) {
4688 Blt_DeleteHashEntry(tablePtr, hPtr);
4689 }
4690 }
4691 }
4692 }
4693 return TCL_OK;
4694}
4695
4696/*
4697 *----------------------------------------------------------------------
4698 *
4699 * TagOp --
4700 *
4701 *----------------------------------------------------------------------
4702 */
4703static Blt_OpSpec tagOps[] = {
4704 {"add", 1, (Blt_Op)TagAddOp, 5, 0, "tag id...",},
4705 {"delete", 2, (Blt_Op)TagDeleteOp, 5, 0, "tag id...",},
4706 {"forget", 1, (Blt_Op)TagForgetOp, 4, 0, "tag...",},
4707 {"names", 2, (Blt_Op)TagNamesOp, 3, 0, "?id...?",},
4708 {"nodes", 2, (Blt_Op)TagNodesOp, 4, 0, "tag ?tag...?",},
4709};
4710
4711static int nTagOps = sizeof(tagOps) / sizeof(Blt_OpSpec);
4712
4713static int
4714TagOp(tvPtr, interp, objc, objv)
4715 TreeView *tvPtr;
4716 Tcl_Interp *interp;
4717 int objc;
4718 Tcl_Obj *CONST *objv;
4719{
4720 Blt_Op proc;
4721 int result;
4722
4723 proc = Blt_GetOpFromObj(interp, nTagOps, tagOps, BLT_OP_ARG2, objc, objv,
4724 0);
4725 if (proc == NULL) {
4726 return TCL_ERROR;
4727 }
4728 result = (*proc)(tvPtr, interp, objc, objv);
4729 return result;
4730}
4731
4732/*ARGSUSED*/
4733static int
4734ToggleOp(tvPtr, interp, objc, objv)
4735 TreeView *tvPtr;
4736 Tcl_Interp *interp; /* Not used. */
4737 int objc;
4738 Tcl_Obj *CONST *objv;
4739{
4740 TreeViewEntry *entryPtr;
4741 TreeViewTagInfo info;
4742
4743 if (Blt_TreeViewFindTaggedEntries(tvPtr, objv[2], &info) != TCL_OK) {
4744 return TCL_ERROR;
4745 }
4746 for (entryPtr = Blt_TreeViewFirstTaggedEntry(&info); entryPtr != NULL;
4747 entryPtr = Blt_TreeViewNextTaggedEntry(&info)) {
4748 if (entryPtr == NULL) {
4749 return TCL_OK;
4750 }
4751 if (entryPtr->flags & ENTRY_CLOSED) {
4752 Blt_TreeViewOpenEntry(tvPtr, entryPtr);
4753 } else {
4754 Blt_TreeViewPruneSelection(tvPtr, entryPtr);
4755 if ((tvPtr->focusPtr != NULL) &&
4756 (Blt_TreeIsAncestor(entryPtr->node, tvPtr->focusPtr->node))) {
4757 tvPtr->focusPtr = entryPtr;
4758 Blt_SetFocusItem(tvPtr->bindTable, tvPtr->focusPtr, ITEM_ENTRY);
4759 }
4760 if ((tvPtr->selAnchorPtr != NULL) &&
4761 (Blt_TreeIsAncestor(entryPtr->node,
4762 tvPtr->selAnchorPtr->node))) {
4763 tvPtr->selAnchorPtr = NULL;
4764 }
4765 Blt_TreeViewCloseEntry(tvPtr, entryPtr);
4766 }
4767 }
4768 tvPtr->flags |= TV_SCROLL;
4769 Blt_TreeViewEventuallyRedraw(tvPtr);
4770 return TCL_OK;
4771}
4772
4773static int
4774XViewOp(tvPtr, interp, objc, objv)
4775 TreeView *tvPtr;
4776 Tcl_Interp *interp;
4777 int objc;
4778 Tcl_Obj *CONST *objv;
4779{
4780 int width, worldWidth;
4781
4782 width = VPORTWIDTH(tvPtr);
4783 worldWidth = tvPtr->worldWidth;
4784 if (objc == 2) {
4785 double fract;
4786 Tcl_Obj *listObjPtr;
4787
4788 /*
4789 * Note that we are bounding the fractions between 0.0 and 1.0
4790 * to support the "canvas"-style of scrolling.
4791 */
4792 listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
4793 fract = (double)tvPtr->xOffset / worldWidth;
4794 fract = CLAMP(fract, 0.0, 1.0);
4795 Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(fract));
4796 fract = (double)(tvPtr->xOffset + width) / worldWidth;
4797 fract = CLAMP(fract, 0.0, 1.0);
4798 Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(fract));
4799 Tcl_SetObjResult(interp, listObjPtr);
4800 return TCL_OK;
4801 }
4802 if (Blt_GetScrollInfoFromObj(interp, objc - 2, objv + 2, &tvPtr->xOffset,
4803 worldWidth, width, tvPtr->xScrollUnits, tvPtr->scrollMode)
4804 != TCL_OK) {
4805 return TCL_ERROR;
4806 }
4807 tvPtr->flags |= TV_XSCROLL;
4808 Blt_TreeViewEventuallyRedraw(tvPtr);
4809 return TCL_OK;
4810}
4811
4812static int
4813YViewOp(tvPtr, interp, objc, objv)
4814 TreeView *tvPtr;
4815 Tcl_Interp *interp;
4816 int objc;
4817 Tcl_Obj *CONST *objv;
4818{
4819 int height, worldHeight;
4820
4821 height = VPORTHEIGHT(tvPtr);
4822 worldHeight = tvPtr->worldHeight;
4823 if (objc == 2) {
4824 double fract;
4825 Tcl_Obj *listObjPtr;
4826
4827 listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
4828 /* Report first and last fractions */
4829 fract = (double)tvPtr->yOffset / worldHeight;
4830 fract = CLAMP(fract, 0.0, 1.0);
4831 Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(fract));
4832 fract = (double)(tvPtr->yOffset + height) / worldHeight;
4833 fract = CLAMP(fract, 0.0, 1.0);
4834 Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewDoubleObj(fract));
4835 Tcl_SetObjResult(interp, listObjPtr);
4836 return TCL_OK;
4837 }
4838 if (Blt_GetScrollInfoFromObj(interp, objc - 2, objv + 2, &tvPtr->yOffset,
4839 worldHeight, height, tvPtr->yScrollUnits, tvPtr->scrollMode)
4840 != TCL_OK) {
4841 return TCL_ERROR;
4842 }
4843 tvPtr->flags |= TV_SCROLL;
4844 Blt_TreeViewEventuallyRedraw(tvPtr);
4845 return TCL_OK;
4846}
4847
4848/*
4849 * --------------------------------------------------------------
4850 *
4851 * Blt_TreeViewWidgetInstCmd --
4852 *
4853 * This procedure is invoked to process commands on behalf of
4854 * the treeview widget.
4855 *
4856 * Results:
4857 * A standard Tcl result.
4858 *
4859 * Side effects:
4860 * See the user documentation.
4861 *
4862 * --------------------------------------------------------------
4863 */
4864static Blt_OpSpec treeViewOps[] =
4865{
4866 {"bbox", 2, (Blt_Op)BboxOp, 3, 0, "tagOrId...",},
4867 {"bind", 2, (Blt_Op)BindOp, 3, 5, "tagName ?sequence command?",},
4868 {"button", 2, (Blt_Op)ButtonOp, 2, 0, "args",},
4869 {"cget", 2, (Blt_Op)CgetOp, 3, 3, "option",},
4870 {"close", 2, (Blt_Op)CloseOp, 2, 0, "?-recurse? tagOrId...",},
4871 {"column", 3, (Blt_Op)Blt_TreeViewColumnOp, 2, 0, "oper args",},
4872 {"configure", 3, (Blt_Op)ConfigureOp, 2, 0, "?option value?...",},
4873 {"curselection", 2, (Blt_Op)CurselectionOp, 2, 2, "",},
4874 {"delete", 1, (Blt_Op)DeleteOp, 2, 0, "tagOrId ?tagOrId...?",},
4875 {"edit", 2, (Blt_Op)EditOp, 4, 6, "?-root|-test? x y",},
4876 {"entry", 2, (Blt_Op)EntryOp, 2, 0, "oper args",},
4877 {"find", 2, (Blt_Op)FindOp, 2, 0, "?flags...? ?first last?",},
4878 {"focus", 2, (Blt_Op)FocusOp, 3, 3, "tagOrId",},
4879 {"get", 1, (Blt_Op)GetOp, 2, 0, "?-full? tagOrId ?tagOrId...?",},
4880 {"hide", 1, (Blt_Op)HideOp, 2, 0, "?-exact? ?-glob? ?-regexp? ?-nonmatching? ?-name string? ?-full string? ?-data string? ?--? ?tagOrId...?",},
4881 {"index", 3, (Blt_Op)IndexOp, 3, 6, "?-at tagOrId? ?-path? string",},
4882 {"insert", 3, (Blt_Op)InsertOp, 3, 0, "?-at tagOrId? position label ?label...? ?option value?",},
4883 {"move", 1, (Blt_Op)MoveOp, 5, 5, "tagOrId into|before|after tagOrId",},
4884 {"nearest", 1, (Blt_Op)NearestOp, 4, 5, "x y ?varName?",},
4885 {"open", 1, (Blt_Op)OpenOp, 2, 0, "?-recurse? tagOrId...",},
4886 {"range", 1, (Blt_Op)RangeOp, 4, 5, "?-open? tagOrId tagOrId",},
4887 {"scan", 2, (Blt_Op)ScanOp, 5, 5, "dragto|mark x y",},
4888 {"see", 3, (Blt_Op)SeeOp, 3, 0, "?-anchor anchor? tagOrId",},
4889 {"selection", 3, (Blt_Op)SelectionOp, 2, 0, "oper args",},
4890 {"show", 2, (Blt_Op)ShowOp, 2, 0, "?-exact? ?-glob? ?-regexp? ?-nonmatching? ?-name string? ?-full string? ?-data string? ?--? ?tagOrId...?",},
4891 {"sort", 2, (Blt_Op)Blt_TreeViewSortOp, 2, 0, "args",},
4892 {"style", 2, (Blt_Op)Blt_TreeViewStyleOp, 2, 0, "args",},
4893 {"tag", 2, (Blt_Op)TagOp, 2, 0, "oper args",},
4894 {"toggle", 2, (Blt_Op)ToggleOp, 3, 3, "tagOrId",},
4895 {"xview", 1, (Blt_Op)XViewOp, 2, 5, "?moveto fract? ?scroll number what?",},
4896 {"yview", 1, (Blt_Op)YViewOp, 2, 5, "?moveto fract? ?scroll number what?",},
4897};
4898
4899static int nTreeViewOps = sizeof(treeViewOps) / sizeof(Blt_OpSpec);
4900
4901int
4902Blt_TreeViewWidgetInstCmd(clientData, interp, objc, objv)
4903 ClientData clientData; /* Information about the widget. */
4904 Tcl_Interp *interp; /* Interpreter to report errors back to. */
4905 int objc; /* Number of arguments. */
4906 Tcl_Obj *CONST *objv; /* Vector of argument strings. */
4907{
4908 Blt_Op proc;
4909 TreeView *tvPtr = clientData;
4910 int result;
4911
4912 proc = Blt_GetOpFromObj(interp, nTreeViewOps, treeViewOps, BLT_OP_ARG1,
4913 objc, objv, 0);
4914 if (proc == NULL) {
4915 return TCL_ERROR;
4916 }
4917 Tcl_Preserve(tvPtr);
4918 result = (*proc) (tvPtr, interp, objc, objv);
4919 Tcl_Release(tvPtr);
4920 return result;
4921}
4922
4923#endif /* NO_TREEVIEW */
Note: See TracBrowser for help on using the repository browser.