4 * This module implements a tree widget.
6 * Copyright (c) 1996 Christian Werner
8 * See the file "license.terms" for information on usage and redistribution
9 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
20 #define DEF_TREE_ACTIVE_ATTR_COLOR "normal"
21 #define DEF_TREE_ACTIVE_ATTR_MONO "reverse"
22 #define DEF_TREE_ACTIVE_BG_COLOR "white"
23 #define DEF_TREE_ACTIVE_BG_MONO "black"
24 #define DEF_TREE_ACTIVE_FG_COLOR "black"
25 #define DEF_TREE_ACTIVE_FG_MONO "white"
26 #define DEF_TREE_ATTR_COLOR "normal"
27 #define DEF_TREE_ATTR_MONO "normal"
28 #define DEF_TREE_BG_COLOR "black"
29 #define DEF_TREE_BG_MONO "black"
30 #define DEF_TREE_FG_COLOR "white"
31 #define DEF_TREE_FG_MONO "white"
32 #define DEF_TREE_HEIGHT "10"
33 #define DEF_TREE_SELECT_ATTR_COLOR "bold"
34 #define DEF_TREE_SELECT_ATTR_MONO "bold"
35 #define DEF_TREE_SELECT_BG_COLOR "black"
36 #define DEF_TREE_SELECT_BG_MONO "black"
37 #define DEF_TREE_SELECT_FG_COLOR "white"
38 #define DEF_TREE_SELECT_FG_MONO "white"
39 #define DEF_TREE_TAKE_FOCUS "1"
40 #define DEF_TREE_WIDTH "40"
41 #define DEF_TREE_SCROLL_COMMAND NULL
44 * A node in the tree is represented by this data structure.
50 int id; /* Unique id of the node. */
51 int level; /* Level in tree, 0 means root. */
52 struct Tree *tree; /* Pointer to widget. */
53 struct Node *parent; /* Pointer to parent node or NULL. */
54 struct Node *next; /* Pointer to next node in this level. */
55 struct Node *firstChild, *lastChild;
56 Ck_Uid staticTagSpace[TAG_SPACE];
57 Ck_Uid *tagPtr; /* Pointer to tag array. */
58 int tagSpace; /* Total size of tag array. */
59 int numTags; /* Number of tags in tag array. */
60 int fg; /* Foreground color of node's text. */
61 int bg; /* Background color of node's text. */
62 int attr; /* Video attributes of node's text. */
63 char *text; /* Text to display for this node. */
64 int textWidth; /* Width of node's text. */
65 int flags; /* Flag bits (see below). */
71 * SELECTED: Non-zero means node is selected
72 * SHOWCHILDREN: Non-zero means if node has children
73 * they shall be displayed.
77 #define SHOWCHILDREN 2
80 * Custom option for handling "-tags" options for tree nodes:
83 static int TreeTagsParseProc _ANSI_ARGS_((ClientData clientData,
84 Tcl_Interp *interp, CkWindow *winPtr, char *value,
85 char *widgRec, int offset));
86 static char * TreeTagsPrintProc _ANSI_ARGS_((ClientData clientData,
87 CkWindow *winPtr, char *widgRec, int offset,
88 Tcl_FreeProc **freeProcPtr));
90 Ck_CustomOption treeTagsOption = {
97 * Information used for parsing configuration specs for nodes:
100 static Ck_ConfigSpec nodeConfigSpecs[] = {
101 {CK_CONFIG_ATTR, "-attributes", (char *) NULL, (char *) NULL,
102 "", Ck_Offset(Node, attr), CK_CONFIG_DONT_SET_DEFAULT },
103 {CK_CONFIG_COLOR, "-background", (char *) NULL, (char *) NULL,
104 "", Ck_Offset(Node, bg), CK_CONFIG_DONT_SET_DEFAULT },
105 {CK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
106 (char *) NULL, 0, 0},
107 {CK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
108 (char *) NULL, 0, 0},
109 {CK_CONFIG_COLOR, "-foreground", (char *) NULL, (char *) NULL,
110 "", Ck_Offset(Node, fg), CK_CONFIG_DONT_SET_DEFAULT},
111 {CK_CONFIG_CUSTOM, "-tags", (char *) NULL, (char *) NULL,
112 (char *) NULL, 0, CK_CONFIG_NULL_OK, &treeTagsOption},
113 {CK_CONFIG_STRING, "-text", (char *) NULL, (char *) NULL,
114 NULL, Ck_Offset(Node, text), CK_CONFIG_NULL_OK},
115 {CK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
120 * A data structure of the following type is kept for each
121 * widget managed by this file:
124 typedef struct Tree {
125 CkWindow *winPtr; /* Window that embodies the widget. NULL
126 * means that the window has been destroyed
127 * but the data structures haven't yet been
129 Tcl_Interp *interp; /* Interpreter associated with menubutton. */
130 Tcl_Command widgetCmd; /* Token for menubutton's widget command. */
132 int idCount; /* For unique ids for nodes. */
135 * Information about what's displayed in the menu button:
138 Node *firstChild, *lastChild;
139 Tcl_HashTable nodeTable;
142 * Information used when displaying widget:
145 int normalFg; /* Foreground color in normal mode. */
146 int normalBg; /* Background color in normal mode. */
147 int normalAttr; /* Attributes in normal mode. */
148 int activeFg; /* Foreground color in active mode. */
149 int activeBg; /* Ditto, background color. */
150 int activeAttr; /* Attributes in active mode. */
151 int selectFg; /* Foreground color for selected nodes. */
152 int selectBg; /* Ditto, background color. */
153 int selectAttr; /* Attributes for selected nodes. */
155 int width, height; /* If > 0, these specify dimensions to request
156 * for window, in characters for text and in
157 * pixels for bitmaps. In this case the actual
158 * size of the text string or bitmap is
159 * ignored in computing desired window size. */
161 int visibleNodes; /* Total number of visible nodes. */
162 int topIndex; /* Index of starting line. */
163 Node *topNode; /* Node at top line of window. */
164 Node *activeNode; /* Node which has active tag or NULL. */
166 int leadingSpace; /* For displaying: size of leadingString. */
167 int *leadingString; /* Malloc'ed leading vertical lines for
171 * Miscellaneous information:
174 char *takeFocus; /* Value of -takefocus option; not used in
175 * the C code, but used by keyboard traversal
176 * scripts. Malloc'ed, but may be NULL. */
177 char *yScrollCmd; /* Command prefix for communicating with
178 * vertical scrollbar. NULL means no command
179 * to issue. Malloc'ed. */
180 char *xScrollCmd; /* Command prefix for communicating with
181 * horizontal scrollbar. NULL means no command
182 * to issue. Malloc'ed. */
183 int flags; /* Various flags; see below for
188 * Flag bits for entire tree:
190 * REDRAW_PENDING: Non-zero means a DoWhenIdle handler
191 * has already been queued to redraw
193 * GOT_FOCUS: Non-zero means this button currently
194 * has the input focus.
195 * UPDATE_V_SCROLLBAR: Non-zero means vertical scrollbar needs
197 * UPDATE_H_SCROLLBAR: Non-zero means horizontal scrollbar needs
201 #define REDRAW_PENDING 1
203 #define UPDATE_V_SCROLLBAR 4
204 #define UPDATE_H_SCROLLBAR 4
207 * Information used for parsing configuration specs:
210 static Ck_ConfigSpec configSpecs[] = {
211 {CK_CONFIG_ATTR, "-activeattributes", "activeAttributes",
212 "ActiveAttributes", DEF_TREE_ACTIVE_ATTR_COLOR,
213 Ck_Offset(Tree, activeAttr), CK_CONFIG_COLOR_ONLY},
214 {CK_CONFIG_ATTR, "-activeattributes", "activeAttributes",
215 "ActiveAttributes", DEF_TREE_ACTIVE_ATTR_MONO,
216 Ck_Offset(Tree, activeAttr), CK_CONFIG_MONO_ONLY},
217 {CK_CONFIG_ATTR, "-attributes", "attributes", "Attributes",
218 DEF_TREE_ATTR_COLOR, Ck_Offset(Tree, normalAttr),
219 CK_CONFIG_COLOR_ONLY},
220 {CK_CONFIG_ATTR, "-attributes", "attributes", "Attributes",
221 DEF_TREE_ATTR_MONO, Ck_Offset(Tree, normalAttr),
222 CK_CONFIG_MONO_ONLY},
223 {CK_CONFIG_COLOR, "-activebackground", "activeBackground", "Foreground",
224 DEF_TREE_ACTIVE_BG_COLOR, Ck_Offset(Tree, activeBg),
225 CK_CONFIG_COLOR_ONLY},
226 {CK_CONFIG_COLOR, "-activebackground", "activeBackground", "Foreground",
227 DEF_TREE_ACTIVE_BG_MONO, Ck_Offset(Tree, activeBg),
228 CK_CONFIG_MONO_ONLY},
229 {CK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
230 DEF_TREE_ACTIVE_FG_COLOR, Ck_Offset(Tree, activeFg),
231 CK_CONFIG_COLOR_ONLY},
232 {CK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
233 DEF_TREE_ACTIVE_FG_MONO, Ck_Offset(Tree, activeFg),
234 CK_CONFIG_MONO_ONLY},
235 {CK_CONFIG_COLOR, "-background", "background", "Background",
236 DEF_TREE_BG_COLOR, Ck_Offset(Tree, normalBg),
237 CK_CONFIG_COLOR_ONLY},
238 {CK_CONFIG_COLOR, "-background", "background", "Background",
239 DEF_TREE_BG_MONO, Ck_Offset(Tree, normalBg),
240 CK_CONFIG_MONO_ONLY},
241 {CK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
242 (char *) NULL, 0, 0},
243 {CK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
244 (char *) NULL, 0, 0},
245 {CK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
246 DEF_TREE_FG_COLOR, Ck_Offset(Tree, normalFg), CK_CONFIG_COLOR_ONLY},
247 {CK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
248 DEF_TREE_FG_MONO, Ck_Offset(Tree, normalFg), CK_CONFIG_MONO_ONLY},
249 {CK_CONFIG_COORD, "-height", "height", "Height",
250 DEF_TREE_HEIGHT, Ck_Offset(Tree, height), 0},
251 {CK_CONFIG_ATTR, "-selectattributes", "selectAttributes",
252 "SelectAttributes", DEF_TREE_SELECT_ATTR_COLOR,
253 Ck_Offset(Tree, selectAttr), CK_CONFIG_COLOR_ONLY},
254 {CK_CONFIG_ATTR, "-selectattributes", "selectAttributes",
255 "SelectAttributes", DEF_TREE_SELECT_ATTR_MONO,
256 Ck_Offset(Tree, selectAttr), CK_CONFIG_MONO_ONLY},
257 {CK_CONFIG_COLOR, "-selectbackground", "selectBackground", "Foreground",
258 DEF_TREE_SELECT_BG_COLOR, Ck_Offset(Tree, selectBg),
259 CK_CONFIG_COLOR_ONLY},
260 {CK_CONFIG_COLOR, "-selectbackground", "selectBackground", "Foreground",
261 DEF_TREE_SELECT_BG_MONO, Ck_Offset(Tree, selectBg),
262 CK_CONFIG_MONO_ONLY},
263 {CK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
264 DEF_TREE_SELECT_FG_COLOR, Ck_Offset(Tree, selectFg),
265 CK_CONFIG_COLOR_ONLY},
266 {CK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
267 DEF_TREE_SELECT_FG_MONO, Ck_Offset(Tree, selectFg),
268 CK_CONFIG_MONO_ONLY},
269 {CK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
270 DEF_TREE_TAKE_FOCUS, Ck_Offset(Tree, takeFocus),
272 {CK_CONFIG_COORD, "-width", "width", "Width",
273 DEF_TREE_WIDTH, Ck_Offset(Tree, width), 0},
274 {CK_CONFIG_STRING, "-xscrollcommand", "xScrollCommand", "ScrollCommand",
275 DEF_TREE_SCROLL_COMMAND, Ck_Offset(Tree, xScrollCmd),
277 {CK_CONFIG_STRING, "-yscrollcommand", "yScrollCommand", "ScrollCommand",
278 DEF_TREE_SCROLL_COMMAND, Ck_Offset(Tree, yScrollCmd),
280 {CK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
285 * The structure defined below is used to keep track of a tag search
286 * in progress. Only the "prevPtr" field should be accessed by anyone
287 * other than StartTagSearch and NextNode.
290 typedef struct TagSearch {
291 Tree *treePtr; /* Tree widget being searched. */
292 Tcl_HashSearch search; /* Hash search for nodeTable. */
293 Ck_Uid tag; /* Tag to search for. 0 means return
295 int searchOver; /* Non-zero means NextNode should always
299 static Ck_Uid allUid = NULL;
300 static Ck_Uid hideChildrenUid = NULL;
301 static Ck_Uid activeUid = NULL;
304 * Forward declarations for procedures defined later in this file:
307 static Node * StartTagSearch _ANSI_ARGS_((Tree *treePtr,
308 char *tag, TagSearch *searchPtr));
309 static Node * NextNode _ANSI_ARGS_((TagSearch *searchPtr));
310 static void DoNode _ANSI_ARGS_((Tcl_Interp *interp,
311 Node *nodePtr, Ck_Uid tag));
312 static void TreeCmdDeletedProc _ANSI_ARGS_((
313 ClientData clientData));
314 static void TreeEventProc _ANSI_ARGS_((ClientData clientData,
316 static int TreeWidgetCmd _ANSI_ARGS_((ClientData clientData,
317 Tcl_Interp *interp, int argc, char **argv));
318 static int ConfigureTree _ANSI_ARGS_((Tcl_Interp *interp,
319 Tree *treePtr, int argc, char **argv,
321 static void DestroyTree _ANSI_ARGS_((ClientData clientData));
322 static void DisplayTree _ANSI_ARGS_((ClientData clientData));
323 static void TreeEventuallyRedraw _ANSI_ARGS_((Tree *treePtr));
324 static int FindNodes _ANSI_ARGS_((Tcl_Interp *interp,
325 Tree *treePtr, int argc, char **argv,
326 char *newTag, char *cmdName, char *option));
327 static void DeleteNode _ANSI_ARGS_((Tree *treePtr, Node *nodePtr));
328 static void RecomputeVisibleNodes _ANSI_ARGS_((Tree *treePtr));
329 static void ChangeTreeView _ANSI_ARGS_((Tree *treePtr, int index));
330 static void TreeUpdateVScrollbar _ANSI_ARGS_((Tree *treePtr));
331 static int GetNodeYCoord _ANSI_ARGS_((Tree *treePtr,
332 Node *thisPtr, int *yPtr));
335 *--------------------------------------------------------------
339 * This procedure is invoked to process the "tree"
340 * Tcl commands. See the user documentation for details
344 * A standard Tcl result.
347 * See the user documentation.
349 *--------------------------------------------------------------
353 Ck_TreeCmd(clientData, interp, argc, argv)
354 ClientData clientData; /* Main window associated with
356 Tcl_Interp *interp; /* Current interpreter. */
357 int argc; /* Number of arguments. */
358 char **argv; /* Argument strings. */
361 CkWindow *mainPtr = (CkWindow *) clientData;
364 allUid = Ck_GetUid("all");
365 hideChildrenUid = Ck_GetUid("hidechildren");
366 activeUid = Ck_GetUid("active");
369 Tcl_AppendResult(interp, "wrong # args: should be \"",
370 argv[0], " pathName ?options?\"", (char *) NULL);
375 * Create the new window.
378 new = Ck_CreateWindowFromPath(interp, mainPtr, argv[1], 0);
384 * Initialize the data structure for the button.
387 treePtr = (Tree *) ckalloc(sizeof (Tree));
388 treePtr->winPtr = new;
389 treePtr->interp = interp;
390 treePtr->widgetCmd = Tcl_CreateCommand(interp, treePtr->winPtr->pathName,
391 TreeWidgetCmd, (ClientData) treePtr, TreeCmdDeletedProc);
392 treePtr->idCount = 0;
393 treePtr->firstChild = treePtr->lastChild = NULL;
394 Tcl_InitHashTable(&treePtr->nodeTable, TCL_ONE_WORD_KEYS);
395 treePtr->normalBg = 0;
396 treePtr->normalFg = 0;
397 treePtr->normalAttr = 0;
398 treePtr->activeBg = 0;
399 treePtr->activeFg = 0;
400 treePtr->activeAttr = 0;
401 treePtr->selectBg = 0;
402 treePtr->selectFg = 0;
403 treePtr->selectAttr = 0;
406 treePtr->visibleNodes = 0;
407 treePtr->topIndex = 0;
408 treePtr->topNode = NULL;
409 treePtr->activeNode = NULL;
410 treePtr->leadingSpace = 0;
411 treePtr->leadingString = NULL;
412 treePtr->takeFocus = NULL;
413 treePtr->xScrollCmd = NULL;
414 treePtr->yScrollCmd = NULL;
417 Ck_SetClass(treePtr->winPtr, "Tree");
418 Ck_CreateEventHandler(treePtr->winPtr,
419 CK_EV_EXPOSE | CK_EV_MAP | CK_EV_DESTROY |
420 CK_EV_FOCUSIN | CK_EV_FOCUSOUT, TreeEventProc, (ClientData) treePtr);
421 if (ConfigureTree(interp, treePtr, argc-2, argv+2, 0) != TCL_OK) {
422 Ck_DestroyWindow(treePtr->winPtr);
426 interp->result = treePtr->winPtr->pathName;
431 *--------------------------------------------------------------
435 * This procedure is invoked to process the Tcl command
436 * that corresponds to a widget managed by this module.
437 * See the user documentation for details on what it does.
440 * A standard Tcl result.
443 * See the user documentation.
445 *--------------------------------------------------------------
449 TreeWidgetCmd(clientData, interp, argc, argv)
450 ClientData clientData; /* Information about button widget. */
451 Tcl_Interp *interp; /* Current interpreter. */
452 int argc; /* Number of arguments. */
453 char **argv; /* Argument strings. */
455 Tree *treePtr = (Tree *) clientData;
456 int result = TCL_OK, redraw = 0, recompute = 0;
461 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
462 " option ?arg arg ...?\"", (char *) NULL);
465 Ck_Preserve((ClientData) treePtr);
467 length = strlen(argv[1]);
469 if ((c == 'a') && (strncmp(argv[1], "addtag", length) == 0)) {
471 Tcl_AppendResult(interp, "wrong # args: should be \"",
472 argv[0], " addtags tag searchCommand ?arg arg ...?\"",
476 result = FindNodes(interp, treePtr, argc-3, argv+3, argv[2], argv[0],
478 } else if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
481 Tcl_AppendResult(interp, "wrong # args: should be \"",
482 argv[0], " cget option\"",
486 result = Ck_ConfigureValue(interp, treePtr->winPtr, configSpecs,
487 (char *) treePtr, argv[2], 0);
488 } else if ((c == 'c') && (strncmp(argv[1], "children", length) == 0)
490 Node *nodePtr = NULL;
494 Tcl_AppendResult(interp, "wrong # args: should be \"",
495 argv[0], " children ?tagOrId?\"",
500 nodePtr = StartTagSearch(treePtr, argv[2], &search);
505 nodePtr = treePtr->firstChild;
507 nodePtr = nodePtr->firstChild;
508 while (nodePtr != NULL) {
509 DoNode(interp, nodePtr, (Ck_Uid) NULL);
510 nodePtr = nodePtr->next;
513 } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
516 result = Ck_ConfigureInfo(interp, treePtr->winPtr, configSpecs,
517 (char *) treePtr, (char *) NULL, 0);
518 } else if (argc == 3) {
519 result = Ck_ConfigureInfo(interp, treePtr->winPtr, configSpecs,
520 (char *) treePtr, argv[2], 0);
522 result = ConfigureTree(interp, treePtr, argc-2, argv+2,
523 CK_CONFIG_ARGV_ONLY);
525 } else if ((c == 'd') && (strncmp(argv[1], "delete", length) == 0)
529 for (i = 2; i < argc; i++) {
534 nodePtr = StartTagSearch(treePtr, argv[i], &search);
537 DeleteNode(treePtr, nodePtr);
543 } else if ((c == 'd') && (strncmp(argv[1], "dtag", length) == 0)
550 if ((argc != 3) && (argc != 4)) {
551 Tcl_AppendResult(interp, "wrong # args: should be \"",
552 argv[0], " dtag tagOrId ?tagToDelete?\"",
557 tag = Ck_GetUid(argv[3]);
559 tag = Ck_GetUid(argv[2]);
561 for (nodePtr = StartTagSearch(treePtr, argv[2], &search);
562 nodePtr != NULL; nodePtr = NextNode(&search)) {
563 for (i = nodePtr->numTags-1; i >= 0; i--) {
564 if (nodePtr->tagPtr[i] == tag) {
565 nodePtr->tagPtr[i] = nodePtr->tagPtr[nodePtr->numTags-1];
567 if (tag == activeUid)
569 else if (tag == hideChildrenUid) {
570 if (!(nodePtr->flags & SHOWCHILDREN)) {
571 nodePtr->flags |= SHOWCHILDREN;
579 } else if ((c == 'f') && (strncmp(argv[1], "find", length) == 0)
582 Tcl_AppendResult(interp, "wrong # args: should be \"",
583 argv[0], " find searchCommand ?arg arg ...?\"",
587 result = FindNodes(interp, treePtr, argc - 2, argv + 2, (char *) NULL,
589 } else if ((c == 'g') && (strncmp(argv[1], "gettags", length) == 0)) {
594 Tcl_AppendResult(interp, "wrong # args: should be \"",
595 argv[0], " gettags tagOrId\"", (char *) NULL);
598 nodePtr = StartTagSearch(treePtr, argv[2], &search);
599 if (nodePtr != NULL) {
602 for (i = 0; i < nodePtr->numTags; i++) {
603 Tcl_AppendElement(interp, (char *) nodePtr->tagPtr[i]);
606 } else if ((c == 'i') && (strncmp(argv[1], "insert", length) == 0)) {
608 Node *nodePtr = NULL, *new;
612 Tcl_AppendResult(interp, "wrong # args: should be \"",
613 argv[0], " insert ?id? ?option value ...?\"",
617 id = strtoul(argv[2], &end, 0);
619 if (end != argv[2]) {
622 hPtr = Tcl_FindHashEntry(&treePtr->nodeTable, (char *) id);
624 Tcl_AppendResult(interp, "no node with id \"", argv[2],
625 "\"", (char *) NULL);
628 nodePtr = (Node *) Tcl_GetHashValue(hPtr);
633 new = (Node *) ckalloc (sizeof (Node));
634 new->id = treePtr->idCount++;
635 new->level = nodePtr == NULL ? 0 : nodePtr->level + 1;
637 new->parent = nodePtr;
639 new->firstChild = new->lastChild = NULL;
640 new->tagPtr = new->staticTagSpace;
641 new->tagSpace = TAG_SPACE;
643 new->fg = new->bg = new->attr = -1;
646 new->flags = SHOWCHILDREN;
648 if (new->level * 2 > treePtr->leadingSpace) {
651 treePtr->leadingSpace = new->level * 8;
652 newString = (int *) ckalloc(treePtr->leadingSpace * sizeof (int));
653 if (treePtr->leadingString != NULL)
654 ckfree((char *) treePtr->leadingString);
655 treePtr->leadingString = newString;
658 result = Ck_ConfigureWidget(interp, treePtr->winPtr,
659 nodeConfigSpecs, argc - optargc, &argv[optargc],
660 (char *) new, CK_CONFIG_ARGV_ONLY);
662 if (result == TCL_OK) {
667 hPtr = Tcl_CreateHashEntry(&treePtr->nodeTable,
668 (char *) new->id, &newHash);
669 Tcl_SetHashValue(hPtr, (ClientData) new);
670 if (new->parent == NULL) {
672 if (treePtr->lastChild == NULL)
673 treePtr->firstChild = new;
675 treePtr->lastChild->next = new;
676 treePtr->lastChild = new;
678 new->level = nodePtr->level + 1;
679 if (nodePtr->lastChild == NULL)
680 nodePtr->firstChild = new;
682 nodePtr->lastChild->next = new;
683 nodePtr->lastChild = new;
687 sprintf(buf, "%d", new->id);
688 Tcl_AppendResult(interp, buf, (char *) NULL);
690 ckfree((char *) new);
692 } else if ((c == 'n') && (strncmp(argv[1], "nodecget", length) == 0)
698 Tcl_AppendResult(interp, "wrong # args: should be \"",
699 argv[0], " nodecget tagOrId option\"",
703 nodePtr = StartTagSearch(treePtr, argv[2], &search);
704 if (nodePtr != NULL) {
705 result = Ck_ConfigureValue(treePtr->interp, treePtr->winPtr,
706 nodeConfigSpecs, (char *) nodePtr,
709 } else if ((c == 'n') && (strncmp(argv[1], "nodeconfigure", length) == 0)
715 Tcl_AppendResult(interp, "wrong # args: should be \"",
716 argv[0], " nodeconfigure tagOrId ?option value ...?\"",
720 for (nodePtr = StartTagSearch(treePtr, argv[2], &search);
721 nodePtr != NULL; nodePtr = NextNode(&search)) {
723 result = Ck_ConfigureInfo(treePtr->interp, treePtr->winPtr,
724 nodeConfigSpecs, (char *) nodePtr,
726 } else if (argc == 4) {
727 result = Ck_ConfigureInfo(treePtr->interp, treePtr->winPtr,
728 nodeConfigSpecs, (char *) nodePtr,
731 result = Ck_ConfigureWidget(interp, treePtr->winPtr,
732 nodeConfigSpecs, argc - 3, &argv[3],
733 (char *) nodePtr, CK_CONFIG_ARGV_ONLY);
736 if ((result != TCL_OK) || (argc < 5)) {
740 } else if ((c == 'p') && (strncmp(argv[1], "parent", length) == 0)
746 Tcl_AppendResult(interp, "wrong # args: should be \"",
747 argv[0], " parent tagOrId\"",
751 nodePtr = StartTagSearch(treePtr, argv[2], &search);
754 if (nodePtr->parent != NULL)
755 DoNode(interp, nodePtr->parent, (Ck_Uid) NULL);
757 } else if ((c == 's') && (strncmp(argv[1], "select", length) == 0)
763 Tcl_AppendResult(interp, "wrong # args: should be \"",
764 argv[0], " select option tagOrId\"", (char *) NULL);
767 nodePtr = StartTagSearch(treePtr, argv[3], &search);
768 if (nodePtr == NULL) {
769 Tcl_AppendResult(interp, "can't find a selectable node \"",
770 argv[3], "\"", (char *) NULL);
773 length = strlen(argv[2]);
775 if ((c == 'c') && (argv[2] != NULL) &&
776 (strncmp(argv[2], "clear", length) == 0)) {
778 nodePtr->flags &= ~SELECTED;
779 nodePtr = NextNode(&search);
781 } while (nodePtr != NULL);
782 } else if ((c == 'i') && (strncmp(argv[2], "includes", length) == 0)) {
783 interp->result = (nodePtr->flags & SELECTED) ? "1" : "0";
784 } else if ((c == 's') && (strncmp(argv[2], "set", length) == 0)) {
786 nodePtr->flags |= SELECTED;
787 nodePtr = NextNode(&search);
789 } while (nodePtr != NULL);
791 Tcl_AppendResult(interp, "bad select option \"", argv[2],
792 "\": must be clear, includes, or set", (char *) NULL);
795 } else if ((c == 'x') && (strncmp(argv[1], "xview", length) == 0)) {
801 type = Ck_GetScrollInfo(interp, argc, argv, &fraction, &count);
803 case CK_SCROLL_ERROR:
805 case CK_SCROLL_MOVETO:
807 case CK_SCROLL_PAGES:
809 case CK_SCROLL_UNITS:
813 } else if ((c == 'y') && (strncmp(argv[1], "yview", length) == 0)) {
814 int type, count, index;
818 if (treePtr->visibleNodes == 0) {
819 interp->result = "0 1";
823 fraction = treePtr->topIndex / (double) treePtr->visibleNodes;
824 fraction2 = (treePtr->topIndex + treePtr->winPtr->height) /
825 (double) treePtr->visibleNodes;
826 if (fraction2 > 1.0) {
829 sprintf(interp->result, "%g %g", fraction, fraction2);
831 } else if (argc == 3) {
835 nodePtr = StartTagSearch(treePtr, argv[2], &search);
836 if (nodePtr == NULL) {
837 Tcl_AppendResult(interp, "can't find a selectable node \"",
838 argv[3], "\"", (char *) NULL);
841 if (GetNodeYCoord(treePtr, nodePtr, &index) == TCL_OK) {
842 if (index < treePtr->topIndex ||
843 index >= treePtr->topIndex + treePtr->winPtr->height)
844 ChangeTreeView(treePtr,
845 index - treePtr->winPtr->height / 2);
848 type = Ck_GetScrollInfo(interp, argc, argv, &fraction, &count);
850 case CK_SCROLL_ERROR:
852 case CK_SCROLL_MOVETO:
853 index = (int) (treePtr->visibleNodes * fraction + 0.5);
855 case CK_SCROLL_PAGES:
856 if (treePtr->visibleNodes > 2) {
857 index = treePtr->topIndex
858 + count * (treePtr->winPtr->height - 2);
860 index = treePtr->topIndex + count;
863 case CK_SCROLL_UNITS:
864 index = treePtr->topIndex + count;
867 ChangeTreeView(treePtr, index);
870 Tcl_AppendResult(interp, "bad option \"", argv[1],
871 "\": must be cget or configure",
876 RecomputeVisibleNodes(treePtr);
878 TreeEventuallyRedraw(treePtr);
880 Ck_Release((ClientData) treePtr);
884 Ck_Release((ClientData) treePtr);
889 *----------------------------------------------------------------------
893 * This procedure is invoked to recycle all of the resources
894 * associated with a tree widget. It is invoked as a
895 * when-idle handler in order to make sure that there is no
896 * other use of the tree pending at the time of the deletion.
902 * Everything associated with the widget is freed up.
904 *----------------------------------------------------------------------
908 DestroyTree(clientData)
909 ClientData clientData; /* Info about tree widget. */
911 Tree *treePtr = (Tree *) clientData;
913 Tcl_HashSearch search;
917 * Free up all the stuff that requires special handling, then
918 * let Ck_FreeOptions handle all the standard option-related
922 if (treePtr->leadingString != NULL) {
923 ckfree((char *) treePtr->leadingString);
924 treePtr->leadingString = NULL;
927 hPtr = Tcl_FirstHashEntry(&treePtr->nodeTable, &search);
928 while (hPtr != NULL) {
929 nodePtr = (Node *) Tcl_GetHashValue(hPtr);
930 Ck_FreeOptions(nodeConfigSpecs, (char *) nodePtr, 0);
931 if (nodePtr->tagPtr != nodePtr->staticTagSpace)
932 ckfree((char *) nodePtr->tagPtr);
933 ckfree((char *) nodePtr);
934 hPtr = Tcl_NextHashEntry(&search);
936 Tcl_DeleteHashTable(&treePtr->nodeTable);
938 Ck_FreeOptions(configSpecs, (char *) treePtr, 0);
939 ckfree((char *) treePtr);
943 *----------------------------------------------------------------------
947 * This procedure is called to process an argv/argc list, plus
948 * the option database, in order to configure (or
949 * reconfigure) a tree widget.
952 * The return value is a standard Tcl result. If TCL_ERROR is
953 * returned, then interp->result contains an error message.
956 * Configuration information, such as text string, colors, font,
957 * etc. get set for treePtr; old resources get freed, if there
958 * were any. The tree is redisplayed.
960 *----------------------------------------------------------------------
964 ConfigureTree(interp, treePtr, argc, argv, flags)
965 Tcl_Interp *interp; /* Used for error reporting. */
966 Tree *treePtr; /* Information about widget; may or may
967 * not already have values for some fields. */
968 int argc; /* Number of valid entries in argv. */
969 char **argv; /* Arguments. */
970 int flags; /* Flags to pass to Ck_ConfigureWidget. */
972 int result, width, height;
974 result = Ck_ConfigureWidget(interp, treePtr->winPtr, configSpecs,
975 argc, argv, (char *) treePtr, flags);
976 if (result != TCL_OK)
978 width = treePtr->width;
981 height = treePtr->height;
984 Ck_GeometryRequest(treePtr->winPtr, width, height);
985 TreeEventuallyRedraw(treePtr);
990 *----------------------------------------------------------------------
992 * TreeEventuallyRedraw --
994 * This procedure is called to dispatch a do-when-idle
995 * handler for redrawing the tree.
1000 *----------------------------------------------------------------------
1004 TreeEventuallyRedraw(treePtr)
1007 if ((treePtr->winPtr->flags & CK_MAPPED)
1008 && !(treePtr->flags & REDRAW_PENDING)) {
1009 Tk_DoWhenIdle(DisplayTree, (ClientData) treePtr);
1010 treePtr->flags |= REDRAW_PENDING;
1015 *----------------------------------------------------------------------
1019 * This procedure is called to delete a node and its children.
1024 *----------------------------------------------------------------------
1028 DeleteNode(treePtr, nodePtr)
1032 Node *childPtr, *thisPtr, *prevPtr;
1033 Tcl_HashEntry *hPtr;
1035 while ((childPtr = nodePtr->firstChild) != NULL)
1036 DeleteNode(treePtr, childPtr);
1038 if (treePtr->topNode == nodePtr)
1039 treePtr->topNode = nodePtr->parent;
1040 if (treePtr->activeNode == nodePtr)
1041 treePtr->activeNode = NULL;
1044 if (nodePtr->parent == NULL) {
1045 thisPtr = treePtr->firstChild;
1047 thisPtr = nodePtr->parent->firstChild;
1049 for (; thisPtr != NULL; prevPtr = thisPtr, thisPtr = thisPtr->next) {
1050 if (thisPtr == nodePtr) {
1051 if (prevPtr == NULL) {
1052 if (nodePtr->parent == NULL)
1053 treePtr->firstChild = nodePtr->next;
1055 nodePtr->parent->firstChild = nodePtr->next;
1057 prevPtr->next = nodePtr->next;
1058 if (nodePtr->next == NULL) {
1059 if (nodePtr->parent == NULL)
1060 treePtr->lastChild = prevPtr;
1062 nodePtr->parent->lastChild = prevPtr;
1068 hPtr = Tcl_FindHashEntry(&treePtr->nodeTable, (char *) nodePtr->id);
1069 Tcl_DeleteHashEntry(hPtr);
1070 Ck_FreeOptions(nodeConfigSpecs, (char *) nodePtr, 0);
1071 if (nodePtr->tagPtr != nodePtr->staticTagSpace)
1072 ckfree((char *) nodePtr->tagPtr);
1073 ckfree((char *) nodePtr);
1078 DeleteActiveTag(treePtr)
1082 Node *nodePtr = treePtr->activeNode;
1084 if (nodePtr == NULL)
1087 for (i = nodePtr->numTags-1; i >= 0; i--) {
1088 if (nodePtr->tagPtr[i] == activeUid) {
1089 nodePtr->tagPtr[i] = nodePtr->tagPtr[nodePtr->numTags-1];
1093 treePtr->activeNode = NULL;
1098 *----------------------------------------------------------------------
1102 * This procedure is invoked to display a tree widget.
1108 * Commands are output to X to display the tree.
1110 *----------------------------------------------------------------------
1114 DisplayTree(clientData)
1115 ClientData clientData; /* Information about widget. */
1117 Tree *treePtr = (Tree *) clientData;
1118 CkWindow *winPtr = treePtr->winPtr;
1119 Node *nodePtr, *nextPtr, *parentPtr;
1120 int i, x, y, mustRestore, rarrow;
1121 int ulcorner, urcorner, llcorner, lrcorner, lvline, lhline, ltee, ttee;
1124 treePtr->flags &= ~REDRAW_PENDING;
1125 if ((treePtr->winPtr == NULL) || !(winPtr->flags & CK_MAPPED)) {
1128 if (treePtr->flags & UPDATE_V_SCROLLBAR) {
1129 TreeUpdateVScrollbar(treePtr);
1131 treePtr->flags &= ~(REDRAW_PENDING|UPDATE_H_SCROLLBAR|UPDATE_V_SCROLLBAR);
1133 if (treePtr->firstChild == NULL) {
1134 Ck_ClearToBot(winPtr, 0, 0);
1135 Ck_EventuallyRefresh(winPtr);
1139 Ck_GetGChar(NULL, "rarrow", &rarrow);
1140 Ck_GetGChar(NULL, "ulcorner", &ulcorner);
1141 Ck_GetGChar(NULL, "urcorner", &urcorner);
1142 Ck_GetGChar(NULL, "llcorner", &llcorner);
1143 Ck_GetGChar(NULL, "lrcorner", &lrcorner);
1144 Ck_GetGChar(NULL, "vline", &lvline);
1145 Ck_GetGChar(NULL, "hline", &lhline);
1146 Ck_GetGChar(NULL, "ltee", <ee);
1147 Ck_GetGChar(NULL, "ttee", &ttee);
1149 Ck_SetWindowAttr(winPtr, treePtr->normalFg, treePtr->normalBg,
1150 treePtr->normalAttr);
1152 nodePtr = treePtr->topNode;
1154 i = nodePtr->level * 2 - 1;
1155 nextPtr = nodePtr->parent;
1157 treePtr->leadingString[i] = ' ';
1158 parentPtr = nextPtr->parent;
1159 if (parentPtr != NULL) {
1160 if (parentPtr->lastChild == nextPtr)
1161 treePtr->leadingString[i - 1] = ' ';
1163 treePtr->leadingString[i - 1] = lvline;
1164 } else if (treePtr->lastChild == nextPtr)
1165 treePtr->leadingString[i - 1] = ' ';
1167 treePtr->leadingString[i - 1] = lvline;
1168 nextPtr = parentPtr;
1172 window = winPtr->window;
1174 while (nodePtr != NULL && y < winPtr->height) {
1175 x = mustRestore = 0;
1176 wmove(window, y, x);
1177 if (nodePtr == treePtr->firstChild) {
1178 waddch(window, (nodePtr == treePtr->lastChild) ? lhline : ulcorner);
1180 } else if (nodePtr == treePtr->lastChild) {
1181 waddch(window, llcorner);
1183 } else if (nodePtr->level == 0) {
1184 waddch(window, ltee);
1187 for (i = 0; i < nodePtr->level * 2 && x < winPtr->width; i++) {
1188 waddch(window, treePtr->leadingString[i]);
1191 if (nodePtr->parent != NULL) {
1192 if (x < winPtr->width) {
1193 if (nodePtr == nodePtr->parent->lastChild)
1194 waddch(window, llcorner);
1196 waddch(window, ltee);
1200 if (x < winPtr->width) {
1201 waddch(window, lhline);
1204 if (nodePtr->firstChild != NULL && (nodePtr->flags & SHOWCHILDREN)) {
1205 if (x < winPtr->width) {
1206 waddch(window, ttee);
1209 nextPtr = nodePtr->firstChild;
1211 if (x < winPtr->width) {
1212 waddch(window, (nodePtr->firstChild == NULL) ? lhline : rarrow);
1215 nextPtr = nodePtr->next;
1216 if (nextPtr == NULL) {
1217 parentPtr = nodePtr->parent;
1218 while (nextPtr == NULL) {
1219 if (parentPtr == NULL) {
1222 nextPtr = parentPtr->next;
1223 if (nextPtr == NULL) {
1224 parentPtr = parentPtr->parent;
1229 if (x < winPtr->width) {
1230 waddch(window, ' ');
1234 if (nodePtr == treePtr->activeNode && (treePtr->flags & GOT_FOCUS)) {
1235 Ck_SetWindowAttr(winPtr, treePtr->activeFg, treePtr->activeBg,
1236 treePtr->activeAttr | ((nodePtr->flags & SELECTED) ?
1237 treePtr->selectAttr : 0));
1239 } else if (nodePtr->flags & SELECTED) {
1240 Ck_SetWindowAttr(winPtr, treePtr->selectFg, treePtr->selectBg,
1241 treePtr->selectAttr);
1244 CkDisplayChars(winPtr->mainPtr, window,
1245 nodePtr->text, strlen(nodePtr->text),
1247 CK_NEWLINES_NOT_SPECIAL | CK_IGNORE_TABS);
1249 Ck_SetWindowAttr(winPtr, treePtr->normalFg, treePtr->normalBg,
1250 treePtr->normalAttr);
1251 Ck_ClearToEol(winPtr, -1, -1);
1253 i = nodePtr->level * 2;
1254 treePtr->leadingString[i] = nodePtr->next != NULL ? lvline : ' ';
1255 treePtr->leadingString[i + 1] = ' ';
1260 if (y < winPtr->height)
1261 Ck_ClearToBot(winPtr, 0, y);
1262 Ck_EventuallyRefresh(winPtr);
1266 *--------------------------------------------------------------
1270 * This procedure is invoked by the dispatcher for various
1277 * When the window gets deleted, internal structures get
1278 * cleaned up. When it gets exposed, it is redisplayed.
1280 *--------------------------------------------------------------
1284 TreeEventProc(clientData, eventPtr)
1285 ClientData clientData; /* Information about window. */
1286 CkEvent *eventPtr; /* Information about event. */
1288 Tree *treePtr = (Tree *) clientData;
1290 if (eventPtr->type == CK_EV_EXPOSE) {
1291 TreeEventuallyRedraw(treePtr);
1292 } else if (eventPtr->type == CK_EV_DESTROY) {
1293 if (treePtr->winPtr != NULL) {
1294 treePtr->winPtr = NULL;
1295 Tcl_DeleteCommand(treePtr->interp,
1296 Tcl_GetCommandName(treePtr->interp, treePtr->widgetCmd));
1298 if (treePtr->flags & REDRAW_PENDING) {
1299 Tk_CancelIdleCall(DisplayTree, (ClientData) treePtr);
1301 Ck_EventuallyFree((ClientData) treePtr, (Ck_FreeProc *) DestroyTree);
1302 } else if (eventPtr->type == CK_EV_FOCUSIN) {
1303 treePtr->flags |= GOT_FOCUS;
1304 TreeEventuallyRedraw(treePtr);
1305 } else if (eventPtr->type == CK_EV_FOCUSOUT) {
1306 treePtr->flags &= ~GOT_FOCUS;
1307 TreeEventuallyRedraw(treePtr);
1312 *----------------------------------------------------------------------
1314 * TreeCmdDeletedProc --
1316 * This procedure is invoked when a widget command is deleted. If
1317 * the widget isn't already in the process of being destroyed,
1318 * this command destroys it.
1324 * The widget is destroyed.
1326 *----------------------------------------------------------------------
1330 TreeCmdDeletedProc(clientData)
1331 ClientData clientData; /* Pointer to widget record for widget. */
1333 Tree *treePtr = (Tree *) clientData;
1334 CkWindow *winPtr = treePtr->winPtr;
1337 * This procedure could be invoked either because the window was
1338 * destroyed and the command was then deleted (in which case winPtr
1339 * is NULL) or because the command was deleted, and then this procedure
1340 * destroys the widget.
1343 if (winPtr != NULL) {
1344 treePtr->winPtr = NULL;
1345 Ck_DestroyWindow(winPtr);
1350 *----------------------------------------------------------------------
1352 * RecomputeVisibleNodes --
1354 * Display parameters are recomputed.
1359 *----------------------------------------------------------------------
1363 RecomputeVisibleNodes(treePtr)
1366 int count = 0, top = -1;
1367 Node *nodePtr, *nextPtr = NULL;
1369 nodePtr = treePtr->firstChild;
1370 if (nodePtr == NULL)
1371 treePtr->topNode = NULL;
1372 while (nodePtr != NULL) {
1373 if (nodePtr->parent == NULL)
1374 nextPtr = nodePtr->next;
1375 if (nodePtr == treePtr->topNode)
1377 if (nodePtr->firstChild != NULL && (nodePtr->flags & SHOWCHILDREN)) {
1378 nodePtr = nodePtr->firstChild;
1379 } else if (nodePtr->next != NULL)
1380 nodePtr = nodePtr->next;
1382 while (nodePtr != NULL) {
1383 nodePtr = nodePtr->parent;
1384 if (nodePtr != NULL && nodePtr->next != NULL) {
1385 nodePtr = nodePtr->next;
1389 if (nodePtr == NULL)
1395 treePtr->topNode = treePtr->firstChild;
1398 if (top != treePtr->topIndex || count != treePtr->visibleNodes)
1399 treePtr->flags |= UPDATE_V_SCROLLBAR;
1400 treePtr->topIndex = top;
1401 treePtr->visibleNodes = count;
1405 *----------------------------------------------------------------------
1409 * Change the vertical view on a tree widget so that a given element
1410 * is displayed at the top.
1416 * What's displayed on the screen is changed. If there is a
1417 * scrollbar associated with this widget, then the scrollbar
1418 * is instructed to change its display too.
1420 *----------------------------------------------------------------------
1424 ChangeTreeView(treePtr, index)
1425 Tree *treePtr; /* Information about widget. */
1426 int index; /* Index of element in treePtr
1427 * that should now appear at the
1428 * top of the tree. */
1431 Node *nodePtr, *nextPtr = NULL;
1433 if (index >= treePtr->visibleNodes - treePtr->winPtr->height)
1434 index = treePtr->visibleNodes - treePtr->winPtr->height;
1437 if (treePtr->topIndex != index) {
1438 if (index < treePtr->topIndex) {
1440 nodePtr = treePtr->firstChild;
1442 count = treePtr->topIndex;
1443 nodePtr = treePtr->topNode;
1445 while (nodePtr != NULL) {
1446 if (nodePtr->parent == NULL)
1447 nextPtr = nodePtr->next;
1450 if (nodePtr->firstChild != NULL &&
1451 (nodePtr->flags & SHOWCHILDREN)) {
1452 nodePtr = nodePtr->firstChild;
1453 } else if (nodePtr->next != NULL)
1454 nodePtr = nodePtr->next;
1456 while (nodePtr != NULL) {
1457 nodePtr = nodePtr->parent;
1458 if (nodePtr != NULL && nodePtr->next != NULL) {
1459 nodePtr = nodePtr->next;
1463 if (nodePtr == NULL)
1468 treePtr->topNode = nodePtr;
1469 treePtr->topIndex = count;
1470 treePtr->flags |= UPDATE_V_SCROLLBAR;
1471 TreeEventuallyRedraw(treePtr);
1476 *----------------------------------------------------------------------
1480 * Given node return the window Y coordinate corresponding to it.
1482 *----------------------------------------------------------------------
1486 GetNodeYCoord(treePtr, thisPtr, yPtr)
1487 Tree *treePtr; /* Information about widget. */
1492 Node *nodePtr, *nextPtr = NULL;
1495 nodePtr = treePtr->firstChild;
1496 while (nodePtr != NULL) {
1497 if (thisPtr == nodePtr) {
1501 if (nodePtr->parent == NULL)
1502 nextPtr = nodePtr->next;
1503 if (nodePtr->firstChild != NULL && (nodePtr->flags & SHOWCHILDREN)) {
1504 nodePtr = nodePtr->firstChild;
1505 } else if (nodePtr->next != NULL)
1506 nodePtr = nodePtr->next;
1508 while (nodePtr != NULL) {
1509 nodePtr = nodePtr->parent;
1510 if (nodePtr != NULL && nodePtr->next != NULL) {
1511 nodePtr = nodePtr->next;
1515 if (nodePtr == NULL)
1524 *--------------------------------------------------------------
1526 * TreeTagsParseProc --
1528 * This procedure is invoked during option processing to handle
1529 * "-tags" options for tree nodes.
1532 * A standard Tcl return value.
1535 * The tags for a given node get replaced by those indicated
1536 * in the value argument.
1538 *--------------------------------------------------------------
1542 TreeTagsParseProc(clientData, interp, winPtr, value, widgRec, offset)
1543 ClientData clientData; /* Not used.*/
1544 Tcl_Interp *interp; /* Used for reporting errors. */
1545 CkWindow *winPtr; /* Window containing tree widget. */
1546 char *value; /* Value of option (list of tag
1548 char *widgRec; /* Pointer to record for item. */
1549 int offset; /* Offset into item (ignored). */
1551 Node *nodePtr = (Node *) widgRec, *activeNode = NULL;
1552 int argc, i, hideChildren = 0, redraw = 0, recompute = 0;
1557 * Break the value up into the individual tag names.
1560 if (Tcl_SplitList(interp, value, &argc, &argv) != TCL_OK) {
1565 * Check for special tags.
1567 for (i = 0; i < nodePtr->numTags; i++) {
1568 if (nodePtr->tagPtr[i] == activeUid) {
1569 DeleteActiveTag(nodePtr->tree);
1575 * Make sure that there's enough space in the node to hold the
1579 if (nodePtr->tagSpace < argc) {
1580 newPtr = (Ck_Uid *) ckalloc((unsigned) (argc * sizeof(Ck_Uid)));
1581 for (i = nodePtr->numTags-1; i >= 0; i--) {
1582 newPtr[i] = nodePtr->tagPtr[i];
1584 if (nodePtr->tagPtr != nodePtr->staticTagSpace) {
1585 ckfree((char *) nodePtr->tagPtr);
1587 nodePtr->tagPtr = newPtr;
1588 nodePtr->tagSpace = argc;
1590 nodePtr->numTags = argc;
1591 for (i = 0; i < argc; i++) {
1592 nodePtr->tagPtr[i] = Ck_GetUid(argv[i]);
1593 if (nodePtr->tagPtr[i] == hideChildrenUid)
1595 else if (nodePtr->tagPtr[i] == activeUid)
1596 activeNode = nodePtr;
1598 ckfree((char *) argv);
1599 if (hideChildren && (nodePtr->flags & SHOWCHILDREN)) {
1600 nodePtr->flags &= ~SHOWCHILDREN;
1603 } else if (!hideChildren && !(nodePtr->flags & SHOWCHILDREN)) {
1604 nodePtr->flags |= SHOWCHILDREN;
1608 if (activeNode != NULL) {
1609 DeleteActiveTag(nodePtr->tree);
1610 nodePtr->tree->activeNode = activeNode;
1614 RecomputeVisibleNodes(nodePtr->tree);
1616 TreeEventuallyRedraw(nodePtr->tree);
1621 *--------------------------------------------------------------
1623 * TreeTagsPrintProc --
1625 * This procedure is invoked by the Ck configuration code
1626 * to produce a printable string for the "-tags" configuration
1627 * option for tree nodes.
1630 * The return value is a string describing all the tags for
1631 * the node referred to by "widgRec". In addition, *freeProcPtr
1632 * is filled in with the address of a procedure to call to free
1633 * the result string when it's no longer needed (or NULL to
1634 * indicate that the string doesn't need to be freed).
1639 *--------------------------------------------------------------
1643 TreeTagsPrintProc(clientData, winPtr, widgRec, offset, freeProcPtr)
1644 ClientData clientData; /* Ignored. */
1645 CkWindow *winPtr; /* Window containing tree widget. */
1646 char *widgRec; /* Pointer to record for item. */
1647 int offset; /* Ignored. */
1648 Tcl_FreeProc **freeProcPtr; /* Pointer to variable to fill in with
1649 * information about how to reclaim
1650 * storage for return string. */
1652 Node *nodePtr = (Node *) widgRec;
1654 if (nodePtr->numTags == 0) {
1655 *freeProcPtr = (Tcl_FreeProc *) NULL;
1658 if (nodePtr->numTags == 1) {
1659 *freeProcPtr = (Tcl_FreeProc *) NULL;
1660 return (char *) nodePtr->tagPtr[0];
1662 *freeProcPtr = (Tcl_FreeProc *) free;
1663 return Tcl_Merge(nodePtr->numTags, (char **) nodePtr->tagPtr);
1667 *--------------------------------------------------------------
1671 * This procedure is called to initiate an enumeration of
1672 * all nodes in a given tree that contain a given tag.
1675 * The return value is a pointer to the first node in
1676 * treePtr that matches tag, or NULL if there is no
1677 * such node. The information at *searchPtr is initialized
1678 * such that successive calls to NextNode will return
1679 * successive nodes that match tag.
1682 * SearchPtr is linked into a list of searches in progress
1683 * on treePtr, so that elements can safely be deleted
1684 * while the search is in progress. EndTagSearch must be
1685 * called at the end of the search to unlink searchPtr from
1688 *--------------------------------------------------------------
1692 StartTagSearch(treePtr, tag, searchPtr)
1693 Tree *treePtr; /* Tree whose nodes are to be
1695 char *tag; /* String giving tag value. */
1696 TagSearch *searchPtr; /* Record describing tag search;
1697 * will be initialized here. */
1700 Tcl_HashEntry *hPtr;
1707 * Initialize the search.
1711 searchPtr->treePtr = treePtr;
1712 searchPtr->searchOver = 0;
1715 * Find the first matching node in one of several ways. If the tag
1716 * is a number then it selects the single node with the matching
1720 if (isdigit((unsigned char) (*tag))) {
1723 id = strtoul(tag, &end, 0);
1726 hPtr = Tcl_FindHashEntry(&treePtr->nodeTable, (char *) id);
1728 nodePtr = (Node *) Tcl_GetHashValue(hPtr);
1729 searchPtr->searchOver = 1;
1734 hPtr = Tcl_FirstHashEntry(&treePtr->nodeTable, &searchPtr->search);
1736 searchPtr->searchOver = 1;
1739 searchPtr->tag = uid = Ck_GetUid(tag);
1740 if (uid == allUid) {
1746 searchPtr->tag = NULL;
1747 return (Node *) Tcl_GetHashValue(hPtr);
1751 nodePtr = (Node *) Tcl_GetHashValue(hPtr);
1752 for (tagPtr = nodePtr->tagPtr, count = nodePtr->numTags;
1754 tagPtr++, count--) {
1755 if (*tagPtr == uid) {
1759 hPtr = Tcl_NextHashEntry(&searchPtr->search);
1760 } while (hPtr != NULL);
1762 searchPtr->searchOver = 1;
1767 *--------------------------------------------------------------
1771 * This procedure returns successive nodes that match a given
1772 * tag; it should be called only after StartTagSearch has been
1773 * used to begin a search.
1776 * The return value is a pointer to the next node that matches
1777 * the tag specified to StartTagSearch, or NULL if no such
1778 * node exists. *SearchPtr is updated so that the next call
1779 * to this procedure will return the next node.
1784 *--------------------------------------------------------------
1789 TagSearch *searchPtr; /* Record describing search in
1793 Tcl_HashEntry *hPtr;
1798 if (searchPtr->searchOver)
1801 hPtr = Tcl_NextHashEntry(&searchPtr->search);
1803 searchPtr->searchOver = 1;
1808 * Handle special case of "all" search by returning next node.
1811 uid = searchPtr->tag;
1813 return (Node *) Tcl_GetHashValue(hPtr);
1817 * Look for a node with a particular tag.
1821 nodePtr = (Node *) Tcl_GetHashValue(hPtr);
1822 for (tagPtr = nodePtr->tagPtr, count = nodePtr->numTags;
1824 tagPtr++, count--) {
1825 if (*tagPtr == uid) {
1829 hPtr = Tcl_NextHashEntry(&searchPtr->search);
1830 } while (hPtr != NULL);
1832 searchPtr->searchOver = 1;
1837 *--------------------------------------------------------------
1841 * This is a utility procedure called by FindNodes. It
1842 * either adds nodePtr's id to the result forming in interp,
1843 * or it adds a new tag to nodePtr, depending on the value
1850 * If tag is NULL then nodePtr's id is added as a list element
1851 * to interp->result; otherwise tag is added to nodePtr's
1854 *--------------------------------------------------------------
1858 DoNode(interp, nodePtr, tag)
1859 Tcl_Interp *interp; /* Interpreter in which to (possibly)
1860 * record node id. */
1861 Node *nodePtr; /* Node to (possibly) modify. */
1862 Ck_Uid tag; /* Tag to add to those already
1863 * present for node, or NULL. */
1869 * Handle the "add-to-result" case and return, if appropriate.
1875 sprintf(msg, "%d", nodePtr->id);
1876 Tcl_AppendElement(interp, msg);
1880 for (tagPtr = nodePtr->tagPtr, count = nodePtr->numTags;
1881 count > 0; tagPtr++, count--) {
1887 * Grow the tag space if there's no more room left in the current
1891 if (nodePtr->tagSpace == nodePtr->numTags) {
1894 nodePtr->tagSpace += TAG_SPACE;
1895 newTagPtr = (Ck_Uid *) ckalloc((unsigned)
1896 (nodePtr->tagSpace * sizeof (Ck_Uid)));
1897 memcpy(newTagPtr, nodePtr->tagPtr, nodePtr->numTags * sizeof (Ck_Uid));
1898 if (nodePtr->tagPtr != nodePtr->staticTagSpace) {
1899 ckfree((char *) nodePtr->tagPtr);
1901 nodePtr->tagPtr = newTagPtr;
1902 tagPtr = &nodePtr->tagPtr[nodePtr->numTags];
1906 * Add in the new tag.
1912 if (tag == activeUid) {
1913 DeleteActiveTag(nodePtr->tree);
1914 nodePtr->tree->activeNode = nodePtr;
1915 TreeEventuallyRedraw(nodePtr->tree);
1916 } else if (tag == hideChildrenUid) {
1917 nodePtr->flags &= ~SHOWCHILDREN;
1918 RecomputeVisibleNodes(nodePtr->tree);
1919 TreeEventuallyRedraw(nodePtr->tree);
1924 *--------------------------------------------------------------
1928 * This procedure does all the work of implementing the
1929 * "find" and "addtag" options of the tree widget command,
1930 * which locate nodes that have certain features (location,
1934 * A standard Tcl return value. If newTag is NULL, then a
1935 * list of ids from all the nodes that match argc/argv is
1936 * returned in interp->result. If newTag is NULL, then
1937 * the normal interp->result is an empty string. If an error
1938 * occurs, then interp->result will hold an error message.
1941 * If newTag is non-NULL, then all the nodes that match the
1942 * information in argc/argv have that tag added to their
1945 *--------------------------------------------------------------
1949 FindNodes(interp, treePtr, argc, argv, newTag, cmdName, option)
1950 Tcl_Interp *interp; /* Interpreter for error reporting. */
1951 Tree *treePtr; /* Tree whose nodes are to be
1953 int argc; /* Number of entries in argv. Must be
1954 * greater than zero. */
1955 char **argv; /* Arguments that describe what items
1956 * to search for (see user doc on
1957 * "find" and "addtag" options). */
1958 char *newTag; /* If non-NULL, gives new tag to set
1959 * on all found items; if NULL, then
1960 * ids of found items are returned
1961 * in interp->result. */
1962 char *cmdName; /* Name of original Tcl command, for
1963 * use in error messages. */
1964 char *option; /* For error messages: gives option
1965 * from Tcl command and other stuff
1966 * up to what's in argc/argv. */
1974 if (newTag != NULL) {
1975 uid = Ck_GetUid(newTag);
1980 length = strlen(argv[0]);
1981 if ((c == 'a') && (strncmp(argv[0], "all", length) == 0)
1984 Tcl_AppendResult(interp, "wrong # args: must be \"",
1985 cmdName, option, " all", (char *) NULL);
1988 for (nodePtr = StartTagSearch(treePtr, "all", &search);
1989 nodePtr != NULL; nodePtr = NextNode(&search)) {
1990 DoNode(interp, nodePtr, uid);
1992 } else if ((c == 'n') && (strncmp(argv[0], "next", length) == 0) &&
1996 Tcl_AppendResult(interp, "wrong # args: must be \"",
1997 cmdName, option, " next tagOrId", (char *) NULL);
2000 nodePtr = StartTagSearch(treePtr, argv[1], &search);
2001 if (nodePtr == NULL)
2002 nodePtr = treePtr->firstChild;
2003 if (nodePtr != NULL) {
2004 if (nodePtr->firstChild != NULL && (nodePtr->flags & SHOWCHILDREN))
2005 nodePtr = nodePtr->firstChild;
2006 else if (nodePtr->next != NULL)
2007 nodePtr = nodePtr->next;
2009 while (nodePtr != NULL) {
2010 nodePtr = nodePtr->parent;
2011 if (nodePtr != NULL && nodePtr->next != NULL) {
2012 nodePtr = nodePtr->next;
2017 if (nodePtr != NULL)
2018 DoNode(interp, nodePtr, uid);
2020 } else if ((c == 'n') && (strncmp(argv[0], "nearest", length) == 0) &&
2023 Node *nextPtr = NULL;
2026 Tcl_AppendResult(interp, "wrong # args: must be \"",
2027 cmdName, option, " nearest x y", (char *) NULL);
2030 if (Ck_GetCoord(interp, treePtr->winPtr, argv[1], &x) != TCL_OK ||
2031 Ck_GetCoord(interp, treePtr->winPtr, argv[2], &y) != TCL_OK)
2033 if (y >= treePtr->winPtr->height)
2034 y = treePtr->winPtr->height - 1;
2037 nodePtr = treePtr->topNode;
2038 while (nodePtr != NULL) {
2041 if (nodePtr->parent == NULL)
2042 nextPtr = nodePtr->next;
2043 if (nodePtr->firstChild != NULL &&
2044 (nodePtr->flags & SHOWCHILDREN)) {
2045 nodePtr = nodePtr->firstChild;
2046 } else if (nodePtr->next != NULL)
2047 nodePtr = nodePtr->next;
2049 while (nodePtr != NULL) {
2050 nodePtr = nodePtr->parent;
2051 if (nodePtr != NULL && nodePtr->next != NULL) {
2052 nodePtr = nodePtr->next;
2056 if (nodePtr == NULL)
2061 if (nodePtr != NULL)
2062 sprintf(interp->result, "%d", nodePtr->id);
2063 } else if ((c == 'p') && (strncmp(argv[0], "prev", length) == 0)) {
2065 Node *parentPtr, *nextPtr;
2068 Tcl_AppendResult(interp, "wrong # args: must be \"",
2069 cmdName, option, " prev tagOrId", (char *) NULL);
2072 nodePtr = StartTagSearch(treePtr, argv[1], &search);
2073 if (nodePtr == NULL)
2074 nodePtr = treePtr->firstChild;
2075 if (nodePtr != NULL) {
2076 parentPtr = nodePtr->parent;
2077 if (parentPtr != NULL) {
2078 if (nodePtr == parentPtr->firstChild) {
2079 nextPtr = parentPtr;
2082 nextPtr = parentPtr->firstChild;
2084 parentPtr = nextPtr = treePtr->firstChild;
2086 for (;nextPtr != NULL && nextPtr->next != nodePtr;
2087 nextPtr = nextPtr->next) {
2088 /* Empty loop body. */
2090 if (nextPtr == NULL)
2091 nextPtr = parentPtr->parent;
2092 if (nextPtr == NULL)
2093 nextPtr = treePtr->firstChild;
2095 while (nextPtr->lastChild != NULL &&
2096 (nextPtr->flags & SHOWCHILDREN))
2097 nextPtr = nextPtr->lastChild;
2100 DoNode(interp, nextPtr, uid);
2102 } else if ((c == 'w') && (strncmp(argv[0], "withtag", length) == 0)) {
2104 Tcl_AppendResult(interp, "wrong # args: must be \"",
2105 cmdName, option, " withtag tagOrId", (char *) NULL);
2108 for (nodePtr = StartTagSearch(treePtr, argv[1], &search);
2109 nodePtr != NULL; nodePtr = NextNode(&search)) {
2110 DoNode(interp, nodePtr, uid);
2113 Tcl_AppendResult(interp, "bad search command \"", argv[0],
2114 "\": must be all, nearest, or withtag", (char *) NULL);
2121 *----------------------------------------------------------------------
2123 * TreeUpdateVScrollbar --
2125 * This procedure is invoked whenever information has changed in
2126 * a tree in a way that would invalidate a vertical scrollbar
2127 * display. If there is an associated scrollbar, then this command
2128 * updates it by invoking a Tcl command.
2134 * A Tcl command is invoked, and an additional command may be
2135 * invoked to process errors in the command.
2137 *----------------------------------------------------------------------
2141 TreeUpdateVScrollbar(treePtr)
2142 Tree *treePtr; /* Information about widget. */
2148 if (treePtr->yScrollCmd == NULL) {
2151 if (treePtr->visibleNodes == 0) {
2155 first = treePtr->topIndex / ((double) treePtr->visibleNodes);
2156 last = (treePtr->topIndex + treePtr->winPtr->height)
2157 / ((double) treePtr->visibleNodes);
2162 sprintf(string, " %g %g", first, last);
2163 result = Tcl_VarEval(treePtr->interp, treePtr->yScrollCmd, string,
2165 if (result != TCL_OK) {
2166 Tcl_AddErrorInfo(treePtr->interp,
2167 "\n (vertical scrolling command executed by tree)");
2168 Tk_BackgroundError(treePtr->interp);