5 * Copyright 1998-1999 Lucent Technologies, Inc.
7 * Permission to use, copy, modify, and distribute this software and
8 * its documentation for any purpose and without fee is hereby
9 * granted, provided that the above copyright notice appear in all
10 * copies and that both that the copyright notice and warranty
11 * disclaimer appear in supporting documentation, and that the names
12 * of Lucent Technologies or any of their entities not be used in
13 * advertising or publicity pertaining to distribution of the software
14 * without specific, written prior permission.
16 * Lucent Technologies disclaims all warranties with regard to this
17 * software, including all implied warranties of merchantability and
18 * fitness. In no event shall Lucent Technologies be liable for any
19 * special, indirect or consequential damages or any damages
20 * whatsoever resulting from loss of use, data or profits, whether in
21 * an action of contract, negligence or other tortuous action, arising
22 * out of or in connection with the use or performance of this
25 * The "tree" data object was created by George A. Howlett.
26 * Extensive cleanups and enhancements by Peter MacDonald.
33 * traces and notifiers should be in one list in tree object.
34 * notifier is always fired.
35 * incorporate first/next tag routines ?
44 static int IsTclDict(Tcl_Interp *interp,Tcl_Obj *objPtr) {
45 static Tcl_ObjType *dictType = NULL;
47 if (dictType == NULL) {
49 obj = Tcl_NewDictObj();
50 dictType = obj->typePtr;
51 Tcl_DecrRefCount(obj);
53 return (objPtr->typePtr == dictType);
56 /* 2 = per-tree key, 1 for per-interp, 0 for global (original behavior). */
57 int bltTreeUseLocalKeys = 0;
59 static Tcl_InterpDeleteProc TreeInterpDeleteProc;
60 static Blt_TreeApplyProc SizeApplyProc;
61 static Tcl_IdleProc NotifyIdleProc;
63 #define TREE_THREAD_KEY "BLT Tree Data"
64 #define TREE_MAGIC ((unsigned int) 0x46170277)
65 #define TREE_DESTROYED (1<<0)
67 typedef struct Blt_TreeNodeStruct Node;
68 typedef struct Blt_TreeClientStruct TreeClient;
69 typedef struct Blt_TreeObjectStruct TreeObject;
70 typedef struct Blt_TreeValueStruct Value;
75 * Tree nodes contain heterogeneous data fields, represented as a
76 * chain of these structures. Each field contains the key of the
77 * field (Blt_TreeKey) and the value (Tcl_Obj) containing the
78 * actual data representations.
81 struct Blt_TreeValueStruct {
82 Blt_TreeKey key; /* String identifying the data field */
83 Tcl_Obj *objPtr; /* Data representation. */
84 Blt_Tree owner; /* Non-NULL if privately owned. */
85 Blt_TreeValue next; /* Next value in the chain. */
90 /* The following header is required for LP64 compilation */
95 static void TreeDestroyValues _ANSI_ARGS_((Blt_TreeNode node));
97 static Value *TreeFindValue _ANSI_ARGS_((Blt_TreeNode node,
99 static Value *TreeCreateValue _ANSI_ARGS_((Blt_TreeNode node,
100 Blt_TreeKey key, int *newPtr));
102 static int TreeDeleteValue _ANSI_ARGS_((Blt_TreeNode node,
103 Blt_TreeValue value));
105 static Value *TreeFirstValue _ANSI_ARGS_((Blt_TreeNode,
106 Blt_TreeKeySearch *searchPtr));
108 static Value *TreeNextValue _ANSI_ARGS_((Blt_TreeKeySearch *srchPtr));
111 * When there are this many entries per bucket, on average, rebuild
112 * the hash table to make it larger.
115 #define REBUILD_MULTIPLIER 3
117 #define START_LOGSIZE 5 /* Initial hash table size is 32. */
118 #define MAX_LIST_VALUES 21 /* Convert to hash table when node
119 * value list gets bigger than this
122 #if (SIZEOF_VOID_P == 8)
123 #define RANDOM_INDEX(i) HashOneWord(mask, downshift, i)
124 #define BITSPERWORD 64
128 * The following macro takes a preliminary integer hash value and
129 * produces an index into a hash tables bucket list. The idea is
130 * to make it so that preliminary values that are arbitrarily similar
131 * will end up in different buckets. The hash function was taken
132 * from a random-number generator.
134 #define RANDOM_INDEX(i) \
135 (((((long) (i))*1103515245) >> downshift) & mask)
136 #define BITSPERWORD 32
139 #define DOWNSHIFT_START (BITSPERWORD - 2)
142 * Procedure prototypes for static procedures in this file:
146 #if (SIZEOF_VOID_P == 8)
147 static Blt_Hash HashOneWord _ANSI_ARGS_((uint64_t mask, unsigned int downshift,
150 #endif /* SIZEOF_VOID_P == 8 */
153 * The hash table below is used to keep track of all the Blt_TreeKeys
156 static Blt_HashTable keyTable;
157 static int keyTableInitialized = 0;
160 Blt_HashTable treeTable; /* Table of trees. */
163 Blt_HashTable keyTable;
168 ClientData clientData;
171 Blt_TreeNotifyEventProc *proc;
172 Blt_TreeNotifyEvent event;
177 ClientData clientData;
182 Blt_TreeTraceProc *proc;
183 TreeClient *clientPtr;
184 Blt_ChainLink *linkPtr;
188 * --------------------------------------------------------------
190 * GetTreeInterpData --
192 * Creates or retrieves data associated with tree data objects
193 * for a particular thread. We're using Tcl_GetAssocData rather
194 * than the Tcl thread routines so BLT can work with pre-8.0
195 * Tcl versions that don't have thread support.
198 * Returns a pointer to the tree interpreter data.
200 * --------------------------------------------------------------
202 static TreeInterpData *
203 GetTreeInterpData(Tcl_Interp *interp)
205 Tcl_InterpDeleteProc *proc;
206 TreeInterpData *dataPtr;
208 dataPtr = (TreeInterpData *)
209 Tcl_GetAssocData(interp, TREE_THREAD_KEY, &proc);
210 if (dataPtr == NULL) {
211 dataPtr = Blt_Malloc(sizeof(TreeInterpData));
213 dataPtr->interp = interp;
214 Tcl_SetAssocData(interp, TREE_THREAD_KEY, TreeInterpDeleteProc,
216 Blt_InitHashTable(&dataPtr->treeTable, BLT_STRING_KEYS);
217 Blt_InitHashTable(&dataPtr->keyTable, BLT_STRING_KEYS);
223 * --------------------------------------------------------------
227 * Creates a new node in the tree without installing it. The
228 * number of nodes in the tree is incremented and a unique serial
229 * number is generated for the node.
231 * Also, all nodes have a label. If no label was provided (name
232 * is NULL) then automatically generate a serial number for the node.
235 * Returns a pointer to the new node.
237 * --------------------------------------------------------------
240 NewNode(TreeObject *treeObjPtr, CONST char *name, int inode)
244 /* Create the node structure */
245 nodePtr = Blt_PoolAllocItem(treeObjPtr->nodePool, sizeof(Node));
246 nodePtr->inode = inode;
247 nodePtr->treeObject = treeObjPtr;
248 nodePtr->parent = NULL;
251 nodePtr->next = nodePtr->prev = NULL;
252 nodePtr->first = nodePtr->last = NULL;
253 nodePtr->nChildren = 0;
254 nodePtr->values = NULL;
255 nodePtr->logSize = 0;
256 nodePtr->nValues = 0;
257 nodePtr->label = NULL;
259 nodePtr->label = Blt_TreeKeyGet(NULL, treeObjPtr, name);
261 treeObjPtr->nNodes++;
266 *----------------------------------------------------------------------
270 *----------------------------------------------------------------------
273 ReleaseTagTable(Blt_TreeTagTable *tablePtr)
275 tablePtr->refCount--;
276 if (tablePtr->refCount <= 0) {
278 Blt_HashSearch cursor;
280 for(hPtr = Blt_FirstHashEntry(&tablePtr->tagTable, &cursor);
281 hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
282 Blt_TreeTagEntry *tPtr;
284 tPtr = Blt_GetHashValue(hPtr);
285 Blt_DeleteHashTable(&tPtr->nodeTable);
286 Blt_TreeTagRefDecr(tPtr);
288 Blt_DeleteHashTable(&tablePtr->tagTable);
294 * ----------------------------------------------------------------------
298 * Called after moving a node, resets the depths of each node
299 * for the entire branch (node and it's decendants).
304 * ----------------------------------------------------------------------
308 Node *nodePtr, /* Root node. */
309 int depth) /* Depth of the node. */
311 nodePtr->depth = depth;
312 /* Also reset the depth for each descendant node. */
313 for (nodePtr = nodePtr->first; nodePtr != NULL; nodePtr = nodePtr->next) {
314 ResetDepths(nodePtr, depth + 1);
319 *----------------------------------------------------------------------
323 * Inserts a link preceding a given link.
328 *----------------------------------------------------------------------
332 Node *parentPtr, /* Parent to hold the new entry. */
333 Node *nodePtr, /* New node to be inserted. */
334 Node *beforePtr) /* Node to link before. */
336 if (parentPtr->first == NULL) {
337 parentPtr->last = parentPtr->first = nodePtr;
338 } else if (beforePtr == NULL) { /* Append onto the end of the chain */
339 nodePtr->next = NULL;
340 nodePtr->prev = parentPtr->last;
341 parentPtr->last->next = nodePtr;
342 parentPtr->last = nodePtr;
344 nodePtr->prev = beforePtr->prev;
345 nodePtr->next = beforePtr;
346 if (beforePtr == parentPtr->first) {
347 parentPtr->first = nodePtr;
349 beforePtr->prev->next = nodePtr;
351 beforePtr->prev = nodePtr;
353 parentPtr->nChildren++;
354 nodePtr->parent = parentPtr;
359 *----------------------------------------------------------------------
363 * Unlinks a link from the chain. The link is not deallocated,
364 * but only removed from the chain.
369 *----------------------------------------------------------------------
372 UnlinkNode(Node *nodePtr)
375 int unlinked; /* Indicates if the link is actually
376 * removed from the chain. */
377 parentPtr = nodePtr->parent;
379 if (parentPtr->first == nodePtr) {
380 parentPtr->first = nodePtr->next;
383 if (parentPtr->last == nodePtr) {
384 parentPtr->last = nodePtr->prev;
387 if (nodePtr->next != NULL) {
388 nodePtr->next->prev = nodePtr->prev;
391 if (nodePtr->prev != NULL) {
392 nodePtr->prev->next = nodePtr->next;
396 parentPtr->nChildren--;
398 nodePtr->prev = nodePtr->next = nodePtr->parent = NULL;
402 * --------------------------------------------------------------
406 * Unlinks a given node from the tree, removes its data, and
407 * frees memory allocated to the node.
412 * --------------------------------------------------------------
415 FreeNode(TreeObject *treeObjPtr, Node *nodePtr)
420 * Destroy any data fields associated with this node.
422 TreeDestroyValues(nodePtr);
424 treeObjPtr->nNodes--;
425 hPtr = Blt_FindHashEntry(&treeObjPtr->nodeTable, (char *)nodePtr->inode);
427 Blt_DeleteHashEntry(&treeObjPtr->nodeTable, hPtr);
430 Blt_PoolFreeItem(treeObjPtr->nodePool, (char *)nodePtr);
434 * --------------------------------------------------------------
438 * Creates and initializes a new tree object. Trees always
439 * contain a root node, so one is allocated here.
442 * Returns a pointer to the new tree object is successful, NULL
443 * otherwise. If a tree can't be generated, interp->result will
444 * contain an error message.
446 * -------------------------------------------------------------- */
448 NewTreeObject(TreeInterpData *dataPtr, Tcl_Interp *interp, CONST char *treeName)
450 TreeObject *treeObjPtr;
454 treeObjPtr = Blt_Calloc(1, sizeof(TreeObject));
455 if (treeObjPtr == NULL) {
457 Tcl_AppendResult(interp, "can't allocate tree", (char *)NULL);
460 treeObjPtr->name = Blt_Strdup(treeName);
461 treeObjPtr->interp = interp;
462 treeObjPtr->valuePool = Blt_PoolCreate(BLT_FIXED_SIZE_ITEMS);
463 treeObjPtr->nodePool = Blt_PoolCreate(BLT_FIXED_SIZE_ITEMS);
464 treeObjPtr->clients = Blt_ChainCreate();
465 treeObjPtr->depth = 1;
466 treeObjPtr->flags = 0;
467 treeObjPtr->maxKeyList = 0;
468 if (bltTreeUseLocalKeys) {
469 if (bltTreeUseLocalKeys>1) {
470 treeObjPtr->interpKeyPtr = &treeObjPtr->keyTable;
472 treeObjPtr->interpKeyPtr = &dataPtr->keyTable;
475 treeObjPtr->notifyFlags = 0;
476 Blt_InitHashTable(&treeObjPtr->keyTable, BLT_STRING_KEYS);
477 Blt_InitHashTableWithPool(&treeObjPtr->nodeTable, BLT_ONE_WORD_KEYS);
479 hPtr = Blt_CreateHashEntry(&treeObjPtr->nodeTable, (char *)0, &isNew);
480 treeObjPtr->root = NewNode(treeObjPtr, treeName, 0);
481 Blt_SetHashValue(hPtr, treeObjPtr->root);
483 treeObjPtr->tablePtr = &dataPtr->treeTable;
484 treeObjPtr->hashPtr = Blt_CreateHashEntry(treeObjPtr->tablePtr, treeName,
486 Blt_SetHashValue(treeObjPtr->hashPtr, treeObjPtr);
493 TreeInterpData *dataPtr, /* Interpreter-specific data. */
494 Tcl_Namespace *nsPtr,
495 CONST char *treeName)
501 name = Blt_GetQualifiedName(nsPtr, treeName, &dString);
502 hPtr = Blt_FindHashEntry(&dataPtr->treeTable, name);
503 Tcl_DStringFree(&dString);
505 return Blt_GetHashValue(hPtr);
511 * ----------------------------------------------------------------------
515 * Searches for the tree object associated by the name given.
518 * Returns a pointer to the tree if found, otherwise NULL.
520 * ----------------------------------------------------------------------
523 GetTreeObject(Tcl_Interp *interp, CONST char *name, int flags)
525 CONST char *treeName;
526 Tcl_Namespace *nsPtr; /* Namespace associated with the tree object.
527 * If NULL, indicates to look in first the
528 * current namespace and then the global
530 TreeInterpData *dataPtr; /* Interpreter-specific data. */
531 TreeObject *treeObjPtr;
534 if (Blt_ParseQualifiedName(interp, name, &nsPtr, &treeName) != TCL_OK) {
536 Tcl_AppendResult(interp, "can't find namespace in \"", name, "\"",
540 dataPtr = GetTreeInterpData(interp);
542 treeObjPtr = FindTreeInNamespace(dataPtr, nsPtr, treeName);
544 if (flags & NS_SEARCH_CURRENT) {
545 /* Look first in the current namespace. */
546 nsPtr = Tcl_GetCurrentNamespace(interp);
547 treeObjPtr = FindTreeInNamespace(dataPtr, nsPtr, treeName);
549 if ((treeObjPtr == NULL) && (flags & NS_SEARCH_GLOBAL)) {
550 nsPtr = Tcl_GetGlobalNamespace(interp);
551 treeObjPtr = FindTreeInNamespace(dataPtr, nsPtr, treeName);
558 * ----------------------------------------------------------------------
562 * Destroys an entire branch. This is a special purpose routine
563 * used to speed up the final clean up of the tree.
568 * ----------------------------------------------------------------------
571 TeardownTree(TreeObject *treeObjPtr, Node *nodePtr)
573 if (nodePtr->first != NULL) {
574 Node *childPtr, *nextPtr;
576 for (childPtr = nodePtr->first; childPtr != NULL; childPtr = nextPtr) {
577 nextPtr = childPtr->next;
578 TeardownTree(treeObjPtr, childPtr);
581 if (nodePtr->values != NULL) {
582 TreeDestroyValues(nodePtr);
584 Blt_PoolFreeItem(treeObjPtr->nodePool, (char *)nodePtr);
588 DestroyTreeObject(char *treeObj)
590 Blt_ChainLink *linkPtr;
591 TreeClient *clientPtr;
592 TreeObject *treeObjPtr = (TreeObject*)treeObj;
594 if (treeObjPtr->flags & TREE_DESTROYED) return;
595 treeObjPtr->flags |= TREE_DESTROYED;
596 treeObjPtr->nNodes = 0;
598 /* Remove the remaining clients. */
599 for (linkPtr = Blt_ChainFirstLink(treeObjPtr->clients); linkPtr != NULL;
600 linkPtr = Blt_ChainNextLink(linkPtr)) {
601 clientPtr = Blt_ChainGetValue(linkPtr);
602 Blt_ChainDestroy(clientPtr->events);
603 Blt_ChainDestroy(clientPtr->traces);
606 Blt_ChainDestroy(treeObjPtr->clients);
608 TeardownTree(treeObjPtr, treeObjPtr->root);
609 Blt_PoolDestroy(treeObjPtr->nodePool);
610 Blt_PoolDestroy(treeObjPtr->valuePool);
611 Blt_DeleteHashTable(&treeObjPtr->nodeTable);
612 Blt_DeleteHashTable(&treeObjPtr->keyTable);
614 if (treeObjPtr->hashPtr != NULL) {
615 /* Remove the entry from the global tree table. */
616 Blt_DeleteHashEntry(treeObjPtr->tablePtr, treeObjPtr->hashPtr);
617 if ((treeObjPtr->tablePtr->numEntries == 0) && (keyTableInitialized)) {
618 keyTableInitialized = FALSE;
619 Blt_DeleteHashTable(&keyTable);
622 if (treeObjPtr->name != NULL) {
623 Blt_Free(treeObjPtr->name);
625 Blt_Free(treeObjPtr);
629 * -----------------------------------------------------------------------
631 * TreeInterpDeleteProc --
633 * This is called when the interpreter hosting the tree object
634 * is deleted from the interpreter.
640 * Destroys all remaining trees and removes the hash table
641 * used to register tree names.
643 * ------------------------------------------------------------------------
647 TreeInterpDeleteProc(
648 ClientData clientData, /* Interpreter-specific data. */
651 TreeInterpData *dataPtr = clientData;
653 Blt_HashSearch cursor;
654 TreeObject *treeObjPtr;
656 for (hPtr = Blt_FirstHashEntry(&dataPtr->treeTable, &cursor);
657 hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
658 treeObjPtr = (TreeObject *)Blt_GetHashValue(hPtr);
659 treeObjPtr->hashPtr = NULL;
660 treeObjPtr->delete = 1;
661 Tcl_EventuallyFree(treeObjPtr, DestroyTreeObject);
662 /*DestroyTreeObject(treeObjPtr); */
664 if (keyTableInitialized) {
665 keyTableInitialized = FALSE;
666 Blt_DeleteHashTable(&keyTable);
668 Blt_DeleteHashTable(&dataPtr->treeTable);
669 Blt_DeleteHashTable(&dataPtr->keyTable);
670 Tcl_DeleteAssocData(interp, TREE_THREAD_KEY);
675 *----------------------------------------------------------------------
679 * Used to invoke event handler routines at some idle point.
680 * This routine is called from the Tcl event loop. Errors
681 * generated by the event handler routines are backgrounded.
686 *----------------------------------------------------------------------
689 NotifyIdleProc(ClientData clientData)
691 EventHandler *notifyPtr = clientData;
694 notifyPtr->notifyPending = FALSE;
695 notifyPtr->mask |= TREE_NOTIFY_ACTIVE;
696 result = (*notifyPtr->proc)(notifyPtr->clientData, ¬ifyPtr->event);
697 notifyPtr->mask &= ~TREE_NOTIFY_ACTIVE;
698 if (result != TCL_OK) {
699 Tcl_BackgroundError(notifyPtr->interp);
704 *----------------------------------------------------------------------
706 * CheckEventHandlers --
708 * Traverses the list of client event callbacks and checks
709 * if one matches the given event. A client may trigger an
710 * action that causes the tree to notify it. The can be
711 * prevented by setting the TREE_NOTIFY_FOREIGN_ONLY bit in
714 * If a matching handler is found, a callback may be called either
715 * immediately or at the next idle time depending upon the
716 * TREE_NOTIFY_WHENIDLE bit.
718 * Since a handler routine may trigger yet another call to
719 * itself, callbacks are ignored while the event handler is
725 *----------------------------------------------------------------------
729 TreeClient *clientPtr,
730 int isSource, /* Indicates if the client is the source
732 Blt_TreeNotifyEvent *eventPtr)
734 Blt_ChainLink *linkPtr, *nextPtr;
735 EventHandler *notifyPtr;
737 eventPtr->tree = clientPtr;
738 for (linkPtr = Blt_ChainFirstLink(clientPtr->events);
739 linkPtr != NULL; linkPtr = nextPtr) {
740 nextPtr = Blt_ChainNextLink(linkPtr);
741 notifyPtr = Blt_ChainGetValue(linkPtr);
742 if ((notifyPtr->mask & TREE_NOTIFY_ACTIVE) ||
743 (notifyPtr->mask & eventPtr->type) == 0) {
744 continue; /* Ignore callbacks that are generated
745 * inside of a notify handler routine. */
747 if ((isSource) && (notifyPtr->mask & TREE_NOTIFY_FOREIGN_ONLY)) {
748 continue; /* Don't notify yourself. */
750 if (notifyPtr->mask & TREE_NOTIFY_WHENIDLE) {
751 if (!notifyPtr->notifyPending) {
752 notifyPtr->notifyPending = TRUE;
753 notifyPtr->event = *eventPtr;
754 Tcl_DoWhenIdle(NotifyIdleProc, notifyPtr);
759 notifyPtr->mask |= TREE_NOTIFY_ACTIVE;
760 result = (*notifyPtr->proc) (notifyPtr->clientData, eventPtr);
761 notifyPtr->mask &= ~TREE_NOTIFY_ACTIVE;
762 if (result != TCL_OK) {
763 if (notifyPtr->mask & TREE_NOTIFY_BGERROR) {
764 Tcl_BackgroundError(notifyPtr->interp);
774 *----------------------------------------------------------------------
778 * Traverses the list of clients for a particular tree and
779 * notifies each client that an event occurred. Clients
780 * indicate interest in a particular event through a bit
783 *----------------------------------------------------------------------
787 TreeClient *sourcePtr,
788 TreeObject *treeObjPtr,
792 Blt_ChainLink *linkPtr;
793 Blt_TreeNotifyEvent event;
794 TreeClient *clientPtr;
795 int isSource, result;
797 if (Tcl_InterpDeleted(treeObjPtr->interp) ||
798 Tcl_InterpDeleted(sourcePtr->root->treeObject->interp)) {
801 event.type = eventFlag;
802 event.inode = nodePtr->inode;
805 * Issue callbacks to each client indicating that a new node has
808 for (linkPtr = Blt_ChainFirstLink(treeObjPtr->clients);
809 linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
810 clientPtr = Blt_ChainGetValue(linkPtr);
811 isSource = (clientPtr == sourcePtr);
812 result = CheckEventHandlers(clientPtr, isSource, &event);
813 if (result != TCL_OK) {
816 if (Blt_TreeNodeDeleted(nodePtr) || nodePtr->inode != event.inode) {
824 FreeValue(Node *nodePtr, Value *valuePtr)
826 if (valuePtr->objPtr != NULL) {
827 Tcl_DecrRefCount(valuePtr->objPtr);
829 Blt_PoolFreeItem(nodePtr->treeObject->valuePool, valuePtr);
833 /* Public Routines */
835 *----------------------------------------------------------------------
839 * Given a string, returns a unique identifier for the string.
841 *----------------------------------------------------------------------
844 Blt_TreeGetKey(string)
845 CONST char *string; /* String to convert. */
850 if (!keyTableInitialized) {
851 Blt_InitHashTable(&keyTable, BLT_STRING_KEYS);
852 keyTableInitialized = 1;
854 hPtr = Blt_CreateHashEntry(&keyTable, string, &isNew);
855 return (Blt_TreeKey)Blt_GetHashKey(&keyTable, hPtr);
859 *----------------------------------------------------------------------
864 * TODO: use per-interp keyTable rather than global one.
866 *----------------------------------------------------------------------
869 Blt_TreeKeyGet(interp, treeObjPtr, string)
871 TreeObject *treeObjPtr;
872 CONST char *string; /* String to convert. */
874 Blt_HashTable *tablePtr;
879 if (treeObjPtr != NULL && treeObjPtr->interpKeyPtr != NULL) {
880 tablePtr = treeObjPtr->interpKeyPtr;
882 if (tablePtr == NULL && interp != NULL && bltTreeUseLocalKeys != 0) {
883 TreeInterpData *dataPtr;
884 dataPtr = GetTreeInterpData(interp);
885 tablePtr = &dataPtr->keyTable;
887 if (tablePtr == NULL) {
888 return Blt_TreeGetKey(string);
890 hPtr = Blt_CreateHashEntry(tablePtr, string, &isNew);
891 return (Blt_TreeKey)Blt_GetHashKey(tablePtr, hPtr);
894 /* Clear the unmodified flag for node. */
895 static void SetModified(Node *nodePtr)
897 nodePtr->flags &= ~TREE_NODE_UNMODIFIED;
898 nodePtr->treeObject->flags &= ~TREE_UNMODIFIED;
902 *----------------------------------------------------------------------
904 * Blt_TreeCreateNode --
906 * Creates a new node in the given parent node. The name and
907 * position in the parent are also provided.
909 *----------------------------------------------------------------------
913 TreeClient *clientPtr, /* The tree client that is creating
914 * this node. If NULL, indicates to
915 * trigger notify events on behalf of
916 * the initiating client also. */
917 Node *parentPtr, /* Parent node where the new node will
919 CONST char *name, /* Name of node. */
920 int pos) /* Position in the parent's list of children
921 * where to insert the new node. */
925 Node *nodePtr; /* Node to be inserted. */
926 TreeObject *treeObjPtr;
930 treeObjPtr = parentPtr->treeObject;
932 /* Generate an unique serial number for this node. */
934 inode = treeObjPtr->nextInode++;
935 hPtr = Blt_CreateHashEntry(&treeObjPtr->nodeTable,(char *)inode,
938 nodePtr = NewNode(treeObjPtr, name, inode);
939 Blt_SetHashValue(hPtr, nodePtr);
941 if ((pos == -1) || (pos >= (int)parentPtr->nChildren)) {
944 beforePtr = parentPtr->first;
945 while ((pos > 0) && (beforePtr != NULL)) {
947 beforePtr = beforePtr->next;
950 LinkBefore(parentPtr, nodePtr, beforePtr);
951 nodePtr->depth = parentPtr->depth + 1;
953 * Issue callbacks to each client indicating that a new node has
956 if (NotifyClients(clientPtr, treeObjPtr, nodePtr, TREE_NOTIFY_CREATE) != TCL_OK) {
957 nodePtr->flags |= TREE_NODE_INSERT_FAIL;
958 Blt_TreeDeleteNode(clientPtr, nodePtr);
961 treeObjPtr->flags &= ~TREE_UNMODIFIED;
965 *----------------------------------------------------------------------
967 * Blt_TreeInsertPost --
969 * Trigger notify for -insert
971 *----------------------------------------------------------------------
975 TreeClient *clientPtr, /* The tree client that is creating
976 * this node. If NULL, indicates to
977 * trigger notify events on behalf of
978 * the initiating client also. */
979 Node *nodePtr) /* node */
981 if (NotifyClients(clientPtr, nodePtr->treeObject, nodePtr, TREE_NOTIFY_INSERT) != TCL_OK) {
982 nodePtr->flags |= TREE_NODE_INSERT_FAIL;
983 Blt_TreeDeleteNode(clientPtr, nodePtr);
991 TreeClient *clientPtr, /* The tree client that is creating
992 * this node. If NULL, indicates to
993 * trigger notify events on behalf of
994 * the initiating client also. */
995 Node *nodePtr) /* node */
997 if (nodePtr->nValues != 0) return TCL_OK;
998 return NotifyClients(clientPtr, nodePtr->treeObject, nodePtr, TREE_NOTIFY_GET);
1002 *----------------------------------------------------------------------
1004 * Blt_TreeCreateNodeWithId --
1006 * Like Blt_TreeCreateNode, but provides a specific id to use
1007 * for the node. If the tree already contains a node by that
1008 * id, NULL is returned.
1010 *----------------------------------------------------------------------
1013 Blt_TreeCreateNodeWithId(
1014 TreeClient *clientPtr,
1015 Node *parentPtr, /* Parent node where the new node will
1017 CONST char *name, /* Name of node. */
1018 unsigned int inode,/* Requested id of the new node. If a
1019 * node by this id already exists in the
1020 * tree, no node is created. */
1021 int position) /* Position in the parent's list of children
1022 * where to insert the new node. */
1024 Blt_HashEntry *hPtr;
1026 Node *nodePtr; /* Node to be inserted. */
1027 TreeObject *treeObjPtr;
1030 treeObjPtr = parentPtr->treeObject;
1031 hPtr = Blt_CreateHashEntry(&treeObjPtr->nodeTable,(char *)inode, &isNew);
1035 nodePtr = NewNode(treeObjPtr, name, inode);
1036 Blt_SetHashValue(hPtr, nodePtr);
1038 if ((position == -1) || (position >= (int)parentPtr->nChildren)) {
1041 beforePtr = parentPtr->first;
1042 while ((position > 0) && (beforePtr != NULL)) {
1044 beforePtr = beforePtr->next;
1047 LinkBefore(parentPtr, nodePtr, beforePtr);
1048 nodePtr->depth = parentPtr->depth + 1;
1050 * Issue callbacks to each client indicating that a new node has
1053 result=NotifyClients(clientPtr, treeObjPtr, nodePtr, TREE_NOTIFY_CREATE);
1054 if (result != TCL_OK) {
1055 if (result != TCL_BREAK) {
1056 nodePtr->flags |= TREE_NODE_INSERT_FAIL;
1057 Blt_TreeDeleteNode(clientPtr, nodePtr);
1061 treeObjPtr->flags &= ~TREE_UNMODIFIED;
1066 *----------------------------------------------------------------------
1068 * Blt_TreeMoveNode --
1070 * Move an entry into a new location in the hierarchy.
1072 *----------------------------------------------------------------------
1077 TreeClient *clientPtr,
1082 TreeObject *treeObjPtr = nodePtr->treeObject;
1083 int newDepth, result;
1085 if (nodePtr == beforePtr) {
1088 if ((beforePtr != NULL) && (beforePtr->parent != parentPtr)) {
1091 if (nodePtr->parent == NULL) {
1092 return TCL_ERROR; /* Can't move root. */
1094 /* Verify that the node isn't an ancestor of the new parent. */
1095 if (Blt_TreeIsAncestor(nodePtr, parentPtr)) {
1099 * Issue callbacks to each client indicating that a node has
1102 if (NotifyClients(clientPtr, treeObjPtr, nodePtr, TREE_NOTIFY_MOVE) != TCL_OK) {
1106 UnlinkNode(nodePtr);
1108 * Relink the node as a child of the new parent.
1110 LinkBefore(parentPtr, nodePtr, beforePtr);
1111 newDepth = parentPtr->depth + 1;
1112 if (nodePtr->depth != newDepth) {
1113 /* Reset the depths of all descendant nodes. */
1114 ResetDepths(nodePtr, newDepth);
1117 * Issue callbacks to each client indicating that a node has
1120 if ((result=NotifyClients(clientPtr, treeObjPtr, nodePtr, TREE_NOTIFY_MOVEPOST)) != TCL_OK) {
1128 Blt_TreeDeleteNode(TreeClient *clientPtr, Node *nodePtr)
1130 TreeObject *treeObjPtr = nodePtr->treeObject;
1131 Node *childPtr, *nextPtr;
1135 * Issue callbacks to each client indicating that the node can
1136 * no longer be used.
1138 if (Blt_TreeNodeDeleted(nodePtr)) {
1141 if ((nodePtr->flags & TREE_NODE_INSERT_FAIL) == 0 &&
1142 (result=NotifyClients(clientPtr, treeObjPtr, nodePtr, TREE_NOTIFY_DELETE)) != TCL_OK) {
1145 nodePtr->flags &= ~TREE_NODE_FIXED_FIELDS;
1147 /* In depth-first order, delete each descendant node. */
1148 for (childPtr = nodePtr->first; childPtr != NULL; childPtr = nextPtr) {
1149 nextPtr = childPtr->next;
1150 Blt_TreeDeleteNode(clientPtr, childPtr);
1153 /* Now remove the actual node. */
1154 FreeNode(treeObjPtr, nodePtr);
1155 if (treeObjPtr->nodeTable.numEntries <= 1) {
1156 treeObjPtr->nextInode = 1;
1162 Blt_TreeGetNode(TreeClient *clientPtr, unsigned int inode)
1164 TreeObject *treeObjPtr = clientPtr->treeObject;
1165 Blt_HashEntry *hPtr;
1167 hPtr = Blt_FindHashEntry(&treeObjPtr->nodeTable, (char *)inode);
1169 return (Blt_TreeNode)Blt_GetHashValue(hPtr);
1176 GetNode(TreeObject *treeObjPtr, unsigned int inode)
1178 Blt_HashEntry *hPtr;
1180 hPtr = Blt_FindHashEntry(&treeObjPtr->nodeTable, (char *)inode);
1182 return (Node*)Blt_GetHashValue(hPtr);
1189 Blt_TreeCreateTrace(
1190 TreeClient *clientPtr,
1192 CONST char *keyPattern,
1193 CONST char *tagName,
1195 Blt_TreeTraceProc *proc,
1196 ClientData clientData)
1198 TraceHandler *tracePtr;
1200 tracePtr = Blt_Calloc(1, sizeof (TraceHandler));
1202 tracePtr->linkPtr = Blt_ChainAppend(clientPtr->traces, tracePtr);
1203 if (keyPattern != NULL) {
1204 tracePtr->keyPattern = Blt_Strdup(keyPattern);
1206 if (tagName != NULL) {
1207 tracePtr->withTag = Blt_Strdup(tagName);
1209 tracePtr->clientPtr = clientPtr;
1210 tracePtr->proc = proc;
1211 tracePtr->clientData = clientData;
1212 tracePtr->mask = mask;
1213 tracePtr->nodePtr = nodePtr;
1214 return (Blt_TreeTrace)tracePtr;
1218 Blt_TreeDeleteTrace(Blt_TreeTrace trace)
1220 TraceHandler *tracePtr = (TraceHandler *)trace;
1222 Blt_ChainDeleteLink(tracePtr->clientPtr->traces, tracePtr->linkPtr);
1223 if (tracePtr->keyPattern != NULL) {
1224 Blt_Free(tracePtr->keyPattern);
1226 if (tracePtr->withTag != NULL) {
1227 Blt_Free(tracePtr->withTag);
1233 Blt_TreeRelabelNode(TreeClient *clientPtr, Node *nodePtr, CONST char *string)
1236 if ((result=NotifyClients(clientPtr, clientPtr->treeObject, nodePtr,
1237 TREE_NOTIFY_RELABEL)) != TCL_OK) {
1240 nodePtr->label = Blt_TreeKeyGet(NULL, clientPtr->treeObject,string);
1242 * Issue callbacks to each client indicating that a new node has
1245 SetModified(nodePtr);
1246 inode = nodePtr->inode;
1247 result = NotifyClients(clientPtr, clientPtr->treeObject, nodePtr,
1248 TREE_NOTIFY_RELABELPOST);
1253 Blt_TreeNotifyAttach(TreeClient *clientPtr)
1255 /* return NotifyClients(clientPtr, clientPtr->treeObject, clientPtr->root,
1256 TREE_NOTIFY_ATTACH); */
1262 Blt_TreeRelabelNode2(Node *nodePtr, CONST char *string)
1264 nodePtr->label = Blt_TreeKeyGet(NULL, nodePtr->treeObject,string);
1265 SetModified(nodePtr);
1270 *----------------------------------------------------------------------
1272 * Blt_TreeFindChild --
1274 * Searches for the named node in a parent's chain of siblings.
1278 * If found, the child node is returned, otherwise NULL.
1280 *----------------------------------------------------------------------
1283 Blt_TreeFindChild(Node *parentPtr, CONST char *string)
1286 register Node *nodePtr;
1288 label = Blt_TreeKeyGet(NULL, parentPtr->treeObject,string);
1289 for (nodePtr = parentPtr->first; nodePtr != NULL; nodePtr = nodePtr->next) {
1290 if (label == nodePtr->label) {
1297 * Like Blt_TreeFindChild above, but looks forward firstN elements
1298 * from front, then falls back to reverse search starting from the end.
1299 * This speeds up insertion at the end of really long trees in treeview.
1300 * If firstN is < 0, just calls Blt_TreeFindChild.
1303 Blt_TreeFindChildRev(Node *parentPtr, CONST char *string, int firstN)
1306 register Node *nodePtr, *endNode;
1310 return Blt_TreeFindChild(parentPtr, string);
1312 label = Blt_TreeKeyGet(NULL, parentPtr->treeObject,string);
1313 for (nodePtr = parentPtr->first, n = 0;
1314 nodePtr != NULL && n < firstN;
1315 nodePtr = nodePtr->next, n++) {
1316 if (label == nodePtr->label) {
1320 if (nodePtr == NULL) {
1324 for (nodePtr = parentPtr->last; nodePtr != NULL; nodePtr = nodePtr->prev) {
1325 if (label == nodePtr->label) {
1328 if (nodePtr == endNode) break;
1334 *----------------------------------------------------------------------
1336 * Blt_TreeNodePosition --
1338 * Returns the position of the node in its parent's list of
1339 * children. The root's position is 0.
1341 *----------------------------------------------------------------------
1344 Blt_TreeNodePosition(Node *nodePtr)
1350 parentPtr = nodePtr->parent;
1351 if (parentPtr != NULL) {
1354 for (childPtr = parentPtr->first; childPtr != NULL;
1355 childPtr = childPtr->next) {
1356 if (nodePtr == childPtr) {
1367 *----------------------------------------------------------------------
1369 * Blt_TreePrevNode --
1371 * Returns the "previous" node in the tree. This node (in
1372 * depth-first order) is its parent, if the node has no siblings
1373 * that are previous to it. Otherwise it is the last descendant
1374 * of the last sibling. In this case, descend the sibling's
1375 * hierarchy, using the last child at any ancestor, with we
1378 *----------------------------------------------------------------------
1381 Blt_TreePrevNode(Node *rootPtr, Node *nodePtr)
1385 if (nodePtr == rootPtr) {
1386 return NULL; /* The root is the first node. */
1388 prevPtr = nodePtr->prev;
1389 if (prevPtr == NULL) {
1390 /* There are no siblings previous to this one, so pick the parent. */
1391 return nodePtr->parent;
1394 * Traverse down the right-most thread, in order to select the
1395 * next entry. Stop when we reach a leaf.
1398 while ((prevPtr = nodePtr->last) != NULL) {
1405 *----------------------------------------------------------------------
1407 * Blt_TreeNextNode --
1409 * Returns the "next" node in relation to the given node.
1410 * The next node (in depth-first order) is either the first
1411 * child of the given node the next sibling if the node has
1412 * no children (the node is a leaf). If the given node is the
1413 * last sibling, then try it's parent next sibling. Continue
1414 * until we either find a next sibling for some ancestor or
1415 * we reach the root node. In this case the current node is
1416 * the last node in the tree.
1418 *----------------------------------------------------------------------
1421 Blt_TreeNextNode(Node *rootPtr, Node *nodePtr)
1425 /* Pick the first sub-node. */
1426 nextPtr = nodePtr->first;
1427 if (nextPtr != NULL) {
1431 * Back up until we can find a level where we can pick a
1432 * "next sibling". For the last entry we'll thread our
1433 * way back to the root.
1435 while (nodePtr != rootPtr) {
1436 nextPtr = nodePtr->next;
1437 if (nextPtr != NULL) {
1440 nodePtr = nodePtr->parent;
1442 return NULL; /* At root, no next node. */
1447 Blt_TreeIsBefore(Node *n1Ptr, Node *n2Ptr)
1453 if (n1Ptr == n2Ptr) {
1456 depth = MIN(n1Ptr->depth, n2Ptr->depth);
1457 if (depth == 0) { /* One of the nodes is root. */
1458 return (n1Ptr->parent == NULL);
1461 * Traverse back from the deepest node, until both nodes are at
1462 * the same depth. Check if this ancestor node is the same for
1465 for (i = n1Ptr->depth; i > depth; i--) {
1466 n1Ptr = n1Ptr->parent;
1468 if (n1Ptr == n2Ptr) {
1471 for (i = n2Ptr->depth; i > depth; i--) {
1472 n2Ptr = n2Ptr->parent;
1474 if (n2Ptr == n1Ptr) {
1479 * First find the mutual ancestor of both nodes. Look at each
1480 * preceding ancestor level-by-level for both nodes. Eventually
1481 * we'll find a node that's the parent of both ancestors. Then
1482 * find the first ancestor in the parent's list of subnodes.
1484 for (i = depth; i > 0; i--) {
1485 if (n1Ptr->parent == n2Ptr->parent) {
1488 n1Ptr = n1Ptr->parent;
1489 n2Ptr = n2Ptr->parent;
1491 for (nodePtr = n1Ptr->parent->first; nodePtr != NULL;
1492 nodePtr = nodePtr->next) {
1493 if (nodePtr == n1Ptr) {
1495 } else if (nodePtr == n2Ptr) {
1505 TreeClient *sourcePtr, /* Client holding a reference to the
1506 * tree. If NULL, indicates to
1507 * execute all handlers, including
1508 * those of the caller. */
1509 TreeObject *treeObjPtr, /* Tree that was changed. */
1510 Node *nodePtr, /* Node that received the event. */
1515 Blt_ChainLink *l1Ptr, *l2Ptr;
1516 TreeClient *clientPtr;
1517 TraceHandler *tracePtr;
1518 unsigned int inode = nodePtr->inode;
1521 for(l1Ptr = Blt_ChainFirstLink(treeObjPtr->clients);
1522 l1Ptr != NULL; l1Ptr = Blt_ChainNextLink(l1Ptr)) {
1523 clientPtr = Blt_ChainGetValue(l1Ptr);
1524 for(l2Ptr = Blt_ChainFirstLink(clientPtr->traces);
1525 l2Ptr != NULL; l2Ptr = Blt_ChainNextLink(l2Ptr)) {
1526 tracePtr = Blt_ChainGetValue(l2Ptr);
1527 if ((tracePtr->mask & flags) == 0) {
1528 continue; /* Flags don't match. */
1530 if ((clientPtr == sourcePtr) &&
1531 (tracePtr->mask & TREE_TRACE_FOREIGN_ONLY)) {
1532 continue; /* This client initiated the trace. */
1534 if ((tracePtr->nodePtr != NULL) && (tracePtr->nodePtr != nodePtr)) {
1535 continue; /* Nodes don't match. */
1537 if ((tracePtr->keyPattern != NULL) &&
1538 (!Tcl_StringMatch(key, tracePtr->keyPattern))) {
1539 continue; /* Key pattern doesn't match. */
1541 if ((tracePtr->withTag != NULL) &&
1542 (!Blt_TreeHasTag(clientPtr, nodePtr, tracePtr->withTag))) {
1543 continue; /* Doesn't have the tag. */
1545 nodePtr->flags |= TREE_TRACE_ACTIVE;
1548 Tcl_Preserve(treeObjPtr);
1549 result = (*tracePtr->proc) (tracePtr->clientData, treeObjPtr->interp,
1550 nodePtr, key, flags);
1551 if (result != TCL_OK) {
1552 Tcl_Release(treeObjPtr);
1553 if ((tracePtr->mask & TREE_TRACE_BGERROR) && interp != NULL) {
1554 Tcl_BackgroundError(interp);
1556 nodePtr->flags &= ~TREE_TRACE_ACTIVE;
1560 nodePtr->flags &= ~TREE_TRACE_ACTIVE;
1561 if (Blt_TreeNodeDeleted(nodePtr) || nodePtr->inode != inode) {
1562 Tcl_Release(treeObjPtr);
1565 if (treeObjPtr->delete) {
1566 Tcl_Release(treeObjPtr);
1567 if (interp != NULL) {
1568 Tcl_AppendResult(interp, "tree deleted", 0);
1572 Tcl_Release(treeObjPtr);
1573 /* nodePtr = GetNode(treeObjPtr, inode);
1574 if (nodePtr == NULL) {
1575 if (interp != NULL) {
1576 Tcl_AppendResult(interp, "node deleted", 0);
1588 TreeClient *clientPtr,
1592 register Value *valuePtr;
1594 valuePtr = TreeFindValue(nodePtr, key);
1595 if (valuePtr == NULL) {
1596 if (interp != NULL) {
1597 Tcl_AppendResult(interp, "can't find field \"", key, "\"",
1602 if ((valuePtr->owner != NULL) && (valuePtr->owner != clientPtr)) {
1603 if (interp != NULL) {
1604 Tcl_AppendResult(interp, "can't access private field \"",
1605 key, "\"", (char *)NULL);
1613 Blt_TreePrivateValue(
1615 TreeClient *clientPtr,
1621 valuePtr = TreeFindValue(nodePtr, key);
1622 if (valuePtr == NULL) {
1623 if (interp != NULL) {
1624 Tcl_AppendResult(interp, "can't find field \"", key, "\"",
1629 valuePtr->owner = clientPtr;
1634 Blt_TreePublicValue(
1636 TreeClient *clientPtr,
1642 valuePtr = TreeFindValue(nodePtr, key);
1643 if (valuePtr == NULL) {
1644 if (interp != NULL) {
1645 Tcl_AppendResult(interp, "can't find field \"", key, "\"",
1650 if (valuePtr->owner != clientPtr) {
1651 if (interp != NULL) {
1652 Tcl_AppendResult(interp, "not the owner of \"", key, "\"",
1657 valuePtr->owner = NULL;
1662 Blt_TreeValueExistsByKey(clientPtr, nodePtr, key)
1663 TreeClient *clientPtr;
1667 register Value *valuePtr;
1669 TreeObject *treeObjPtr = nodePtr->treeObject;
1670 Tcl_Interp *interp = treeObjPtr->interp;
1672 valuePtr = GetTreeValue((Tcl_Interp *)NULL, clientPtr, nodePtr, key);
1673 if (valuePtr == NULL && (!(nodePtr->flags & TREE_TRACE_ACTIVE))) {
1674 if (CallTraces(interp, clientPtr, treeObjPtr, nodePtr, key,
1675 TREE_TRACE_EXISTS, &cnt) != TCL_OK) {
1676 Tcl_ResetResult(interp);
1678 valuePtr = GetTreeValue((Tcl_Interp *)NULL, clientPtr, nodePtr, key);
1681 if (valuePtr == NULL) {
1688 Blt_TreeGetValueByKey(
1690 TreeClient *clientPtr,
1693 Tcl_Obj **objPtrPtr)
1695 register Value *valuePtr;
1696 TreeObject *treeObjPtr = nodePtr->treeObject;
1699 if (!(nodePtr->flags & TREE_TRACE_ACTIVE)) {
1700 if (CallTraces(interp, clientPtr, treeObjPtr, nodePtr, key,
1701 TREE_TRACE_READ, &cnt) != TCL_OK) {
1705 valuePtr = GetTreeValue(interp, clientPtr, nodePtr, key);
1706 if (valuePtr == NULL) {
1709 *objPtrPtr = valuePtr->objPtr;
1714 bltTreeGetValueByKey(
1716 TreeClient *clientPtr,
1719 Value** valuePtrPtr)
1721 register Value *valuePtr;
1722 TreeObject *treeObjPtr = nodePtr->treeObject;
1725 if (!(nodePtr->flags & TREE_TRACE_ACTIVE)) {
1726 if (CallTraces(interp, clientPtr, treeObjPtr, nodePtr, key,
1727 TREE_TRACE_READ, &cnt) != TCL_OK) {
1731 valuePtr = GetTreeValue(interp, clientPtr, nodePtr, key);
1732 if (valuePtr == NULL) {
1735 *valuePtrPtr = valuePtr;
1741 TreeClient *clientPtr,
1744 if (clientPtr->oldValue != NULL) {
1745 Tcl_DecrRefCount(clientPtr->oldValue);
1747 clientPtr->oldValue = objPtr;
1753 TreeClient *clientPtr,
1758 if (clientPtr->oldValue != NULL) {
1759 Tcl_DecrRefCount(clientPtr->oldValue);
1761 clientPtr->oldValue = newPtr;
1762 Tcl_IncrRefCount(clientPtr->oldValue);
1763 } else if (oldPtr != NULL) {
1764 *oldPtr = clientPtr->oldValue;
1769 Blt_TreeSetValueByKey(
1771 TreeClient *clientPtr,
1772 Node *nodePtr, /* Node to be updated. */
1773 Blt_TreeKey key, /* Identifies the field key. */
1774 Tcl_Obj *objPtr) /* New value of field. */
1776 TreeObject *treeObjPtr;
1779 int isNew = 0, cnt = 0;
1781 if (nodePtr == NULL) {
1784 treeObjPtr = nodePtr->treeObject;
1785 assert(objPtr != NULL);
1786 if (nodePtr->flags & TREE_NODE_FIXED_FIELDS) {
1787 valuePtr = TreeFindValue(nodePtr, key);
1788 if (valuePtr == NULL) {
1789 if (interp != NULL) {
1790 Tcl_AppendResult(interp, "fixed field \"", key, "\"",
1796 valuePtr = TreeCreateValue(nodePtr, key, &isNew);
1798 if ((valuePtr->owner != NULL) && (valuePtr->owner != clientPtr)) {
1799 if (interp != NULL) {
1800 Tcl_AppendResult(interp, "can't set private field \"",
1801 key, "\"", (char *)NULL);
1805 SetModified(nodePtr);
1806 if (!(nodePtr->flags & TREE_TRACE_ACTIVE)) {
1807 UpdateOldValue(clientPtr, valuePtr->objPtr);
1808 valuePtr->objPtr = NULL;
1810 if (objPtr != valuePtr->objPtr) {
1811 Tcl_IncrRefCount(objPtr);
1812 if (valuePtr->objPtr != NULL) {
1813 Tcl_DecrRefCount(valuePtr->objPtr);
1815 valuePtr->objPtr = objPtr;
1817 flags = TREE_TRACE_WRITE;
1819 flags |= TREE_TRACE_CREATE;
1821 if (!(nodePtr->flags & TREE_TRACE_ACTIVE)) {
1822 return CallTraces(interp, clientPtr, treeObjPtr, nodePtr, valuePtr->key,
1829 Blt_TreeUnsetValueByKey(
1831 TreeClient *clientPtr,
1832 Node *nodePtr, /* Node to be updated. */
1833 Blt_TreeKey key) /* Name of field in node. */
1835 TreeObject *treeObjPtr = nodePtr->treeObject;
1839 if (nodePtr->flags & TREE_NODE_FIXED_FIELDS) {
1840 if (interp != NULL) {
1841 Tcl_AppendResult(interp, "fixed field", 0);
1845 valuePtr = TreeFindValue(nodePtr, key);
1846 if (valuePtr == NULL) {
1847 return TCL_OK; /* It's okay to unset values that don't
1848 * exist in the node. */
1850 if ((valuePtr->owner != NULL) && (valuePtr->owner != clientPtr)) {
1851 if (interp != NULL) {
1852 Tcl_AppendResult(interp, "can't unset private field \"",
1853 key, "\"", (char *)NULL);
1857 SetModified(nodePtr);
1858 if (!(nodePtr->flags & TREE_TRACE_ACTIVE)) {
1859 UpdateOldValue(clientPtr, valuePtr->objPtr);
1860 valuePtr->objPtr = NULL;
1862 TreeDeleteValue(nodePtr, valuePtr);
1863 return CallTraces(interp, clientPtr, treeObjPtr, nodePtr, key, TREE_TRACE_UNSET, &cnt);
1876 left = right = NULL;
1877 for (p = (char *)string; *p != '\0'; p++) {
1880 } else if (*p == ')') {
1884 if (left != right) {
1885 if (((left != NULL) && (right == NULL)) ||
1886 ((left == NULL) && (right != NULL)) ||
1887 (left > right) || (right != (p - 1))) {
1888 if (interp != NULL) {
1889 Tcl_AppendResult(interp, "bad array specification \"", string,
1890 "\"", (char *)NULL);
1903 TreeClient *clientPtr,
1905 CONST char *string, /* String identifying the field in node. */
1906 Tcl_Obj **objPtrPtr)
1911 if (ParseParentheses(interp, string, &left, &right) != TCL_OK) {
1915 Tcl_DString kStr, iStr;
1916 Tcl_DStringInit(&kStr);
1917 Tcl_DStringInit(&iStr);
1918 Tcl_DStringAppend(&kStr, left+1, right-left-1);
1919 Tcl_DStringAppend(&iStr, string, left-string);
1920 result = Blt_TreeGetArrayValue(interp, clientPtr, nodePtr,
1921 Tcl_DStringValue(&iStr), Tcl_DStringValue(&kStr), objPtrPtr);
1922 Tcl_DStringFree(&kStr);
1923 Tcl_DStringFree(&iStr);
1925 result = Blt_TreeGetValueByKey(interp, clientPtr, nodePtr,
1926 Blt_TreeKeyGet(NULL, clientPtr->treeObject,string), objPtrPtr);
1934 TreeClient *clientPtr,
1935 Node *nodePtr, /* Node to be updated. */
1936 CONST char *string, /* String identifying the field in node. */
1937 Tcl_Obj *valueObjPtr) /* New value of field. If NULL, field
1943 if (nodePtr->flags & TREE_NODE_FIXED_FIELDS) {
1944 return Blt_TreeUpdateValue( interp, clientPtr, nodePtr, string, valueObjPtr);
1946 if (ParseParentheses(interp, string, &left, &right) != TCL_OK) {
1950 Tcl_DString kStr, iStr;
1951 Tcl_DStringInit(&kStr);
1952 Tcl_DStringInit(&iStr);
1953 Tcl_DStringAppend(&kStr, left+1, right-left-1);
1954 Tcl_DStringAppend(&iStr, string, left-string);
1955 result = Blt_TreeSetArrayValue(interp, clientPtr, nodePtr,
1956 Tcl_DStringValue(&iStr), Tcl_DStringValue(&kStr), valueObjPtr);
1957 Tcl_DStringFree(&kStr);
1958 Tcl_DStringFree(&iStr);
1960 result = Blt_TreeSetValueByKey(interp, clientPtr, nodePtr,
1961 Blt_TreeKeyGet(NULL, clientPtr->treeObject,string), valueObjPtr);
1967 Blt_TreeUpdateValue(
1969 TreeClient *clientPtr,
1970 Node *nodePtr, /* Node to be updated. */
1971 CONST char *string, /* String identifying the field in node. */
1972 Tcl_Obj *valueObjPtr) /* New value of field. If NULL, field
1979 if (ParseParentheses(interp, string, &left, &right) != TCL_OK) {
1983 Tcl_DString kStr, iStr;
1984 Tcl_DStringInit(&kStr);
1985 Tcl_DStringInit(&iStr);
1986 Tcl_DStringAppend(&kStr, left+1, right-left-1);
1987 Tcl_DStringAppend(&iStr, string, left-string);
1988 result = Blt_TreeUpdateArrayValue(interp, clientPtr, nodePtr,
1989 Tcl_DStringValue(&iStr), Tcl_DStringValue(&kStr), valueObjPtr);
1990 Tcl_DStringFree(&kStr);
1991 Tcl_DStringFree(&iStr);
1993 key = Blt_TreeKeyGet(NULL, clientPtr->treeObject,string);
1994 if (GetTreeValue((Tcl_Interp *)NULL, clientPtr, nodePtr, key) == NULL) {
1995 if (interp != NULL) {
1996 Tcl_AppendResult(interp, "unknown key: ", string, 0);
2000 result = Blt_TreeSetValueByKey(interp, clientPtr, nodePtr,
2009 TreeClient *clientPtr,
2010 Node *nodePtr, /* Node to be updated. */
2011 CONST char *string) /* String identifying the field in node. */
2016 if (nodePtr->flags & TREE_NODE_FIXED_FIELDS) {
2017 if (interp != NULL) {
2018 Tcl_AppendResult(interp, "fixed field", 0);
2022 if (ParseParentheses(interp, string, &left, &right) != TCL_OK) {
2026 Tcl_DString kStr, iStr;
2027 Tcl_DStringInit(&kStr);
2028 Tcl_DStringInit(&iStr);
2029 Tcl_DStringAppend(&kStr, left+1, right-left-1);
2030 Tcl_DStringAppend(&iStr, string, left-string);
2031 result = Blt_TreeUnsetArrayValue(interp, clientPtr, nodePtr,
2032 Tcl_DStringValue(&iStr), Tcl_DStringValue(&kStr));
2033 Tcl_DStringFree(&kStr);
2034 Tcl_DStringFree(&iStr);
2036 result = Blt_TreeUnsetValueByKey(interp, clientPtr, nodePtr,
2037 Blt_TreeKeyGet(NULL, clientPtr->treeObject,string));
2043 Blt_TreeValueExists(TreeClient *clientPtr, Node *nodePtr, CONST char *string)
2048 if (ParseParentheses((Tcl_Interp *)NULL, string, &left, &right) != TCL_OK) {
2052 Tcl_DString kStr, iStr;
2053 Tcl_DStringInit(&kStr);
2054 Tcl_DStringInit(&iStr);
2055 Tcl_DStringAppend(&kStr, left+1, right-left-1);
2056 Tcl_DStringAppend(&iStr, string, left-string);
2057 result = Blt_TreeArrayValueExists(clientPtr, nodePtr,
2058 Tcl_DStringValue(&iStr), Tcl_DStringValue(&kStr));
2059 Tcl_DStringFree(&kStr);
2060 Tcl_DStringFree(&iStr);
2062 result = Blt_TreeValueExistsByKey(clientPtr, nodePtr,
2063 Blt_TreeKeyGet(NULL, clientPtr->treeObject,string));
2070 TreeClient *clientPtr,
2072 Blt_TreeKeySearch *iterPtr)
2076 valuePtr = TreeFirstValue(nodePtr, iterPtr);
2077 if (valuePtr == NULL) {
2080 while ((valuePtr->owner != NULL) && (valuePtr->owner != clientPtr)) {
2081 valuePtr = TreeNextValue(iterPtr);
2082 if (valuePtr == NULL) {
2086 return valuePtr->key;
2090 Blt_TreeNextKey(TreeClient *clientPtr, Blt_TreeKeySearch *iterPtr)
2094 valuePtr = TreeNextValue(iterPtr);
2095 if (valuePtr == NULL) {
2098 while ((valuePtr->owner != NULL) && (valuePtr->owner != clientPtr)) {
2099 valuePtr = TreeNextValue(iterPtr);
2100 if (valuePtr == NULL) {
2104 return valuePtr->key;
2107 int Blt_TreeCountKeys(TreeClient *clientPtr, Node *nodePtr) {
2110 Blt_TreeKeySearch cursor;
2113 for (key = Blt_TreeFirstKey(clientPtr, nodePtr, &cursor); key != NULL;
2114 key = Blt_TreeNextKey(clientPtr, &cursor)) {
2122 Blt_TreeIsAncestor(Node *n1Ptr, Node *n2Ptr)
2124 if (n2Ptr != NULL) {
2125 n2Ptr = n2Ptr->parent;
2126 while (n2Ptr != NULL) {
2127 if (n2Ptr == n1Ptr) {
2130 n2Ptr = n2Ptr->parent;
2137 *----------------------------------------------------------------------
2139 * Blt_TreeSortNode --
2141 * Sorts the subnodes at a given node.
2144 * Always returns TCL_OK.
2146 *----------------------------------------------------------------------
2150 TreeClient *clientPtr,
2152 Blt_TreeCompareNodesProc *proc)
2159 nNodes = nodePtr->nChildren;
2163 nodeArr = Blt_Malloc((nNodes + 1) * sizeof(Node *));
2164 if (nodeArr == NULL) {
2165 return TCL_ERROR; /* Out of memory. */
2167 for (p = nodeArr, childPtr = nodePtr->first; childPtr != NULL;
2168 childPtr = childPtr->next, p++) {
2173 qsort((char *)nodeArr, nNodes, sizeof(Node *), (QSortCompareProc *)proc);
2174 for (p = nodeArr; *p != NULL; p++) {
2176 LinkBefore(nodePtr, *p, (Blt_TreeNode)NULL);
2179 return NotifyClients(clientPtr, nodePtr->treeObject, nodePtr, TREE_NOTIFY_SORT);
2182 #define TEST_RESULT(result) \
2184 case TCL_CONTINUE: \
2194 Node *nodePtr, /* Root node of subtree. */
2195 Blt_TreeApplyProc *proc, /* Procedure to call for each node. */
2196 ClientData clientData) /* One-word of data passed when calling
2199 Node *childPtr, *nextPtr;
2202 for (childPtr = nodePtr->first; childPtr != NULL; childPtr = nextPtr) {
2205 * Get the next link in the chain before calling Blt_TreeApply
2206 * recursively. This is because the apply callback may delete
2207 * the node and its link.
2210 nextPtr = childPtr->next;
2211 if (Blt_TreeNodeDeleted(childPtr)) return TCL_OK;
2212 result = Blt_TreeApply(childPtr, proc, clientData);
2213 TEST_RESULT(result);
2215 if (Blt_TreeNodeDeleted(nodePtr)) return TCL_OK;
2216 return (*proc) (nodePtr, clientData, TREE_POSTORDER);
2221 Node *nodePtr, /* Root node of subtree. */
2222 Blt_TreeApplyProc *proc, /* Procedure to call for each node. */
2223 ClientData clientData, /* One-word of data passed when calling
2225 int order) /* Order of traversal. */
2227 Node *childPtr, *nextPtr;
2230 if (Blt_TreeNodeDeleted(nodePtr)) return TCL_OK;
2231 if (order & TREE_PREORDER) {
2232 result = (*proc) (nodePtr, clientData, TREE_PREORDER);
2233 TEST_RESULT(result);
2235 childPtr = nodePtr->first;
2236 if (order & TREE_INORDER) {
2237 if (childPtr != NULL) {
2238 result = Blt_TreeApplyDFS(childPtr, proc, clientData, order);
2239 TEST_RESULT(result);
2240 childPtr = childPtr->next;
2242 result = (*proc) (nodePtr, clientData, TREE_INORDER);
2243 TEST_RESULT(result);
2245 for (/* empty */; childPtr != NULL; childPtr = nextPtr) {
2247 * Get the next link in the chain before calling
2248 * Blt_TreeApply recursively. This is because the
2249 * apply callback may delete the node and its link.
2251 nextPtr = childPtr->next;
2252 if (Blt_TreeNodeDeleted(childPtr)) break;
2253 result = Blt_TreeApplyDFS(childPtr, proc, clientData, order);
2254 TEST_RESULT(result);
2256 if (Blt_TreeNodeDeleted(nodePtr)) return TCL_OK;
2257 if (order & TREE_POSTORDER) {
2258 return (*proc) (nodePtr, clientData, TREE_POSTORDER);
2264 Blt_TreeApplyBFS(nodePtr, proc, clientData)
2265 Node *nodePtr; /* Root node of subtree. */
2266 Blt_TreeApplyProc *proc; /* Procedure to call for each node. */
2267 ClientData clientData; /* One-word of data passed when calling
2270 Blt_Chain *queuePtr;
2271 Blt_ChainLink *linkPtr, *nextPtr;
2275 queuePtr = Blt_ChainCreate();
2276 linkPtr = Blt_ChainAppend(queuePtr, nodePtr);
2277 while (linkPtr != NULL) {
2278 nodePtr = Blt_ChainGetValue(linkPtr);
2279 /* Add the children to the queue. */
2280 for (childPtr = nodePtr->first; childPtr != NULL;
2281 childPtr = childPtr->next) {
2282 Blt_ChainAppend(queuePtr, childPtr);
2284 if (Blt_TreeNodeDeleted(nodePtr)) break;
2285 /* Process the node. */
2286 result = (*proc) (nodePtr, clientData, TREE_BREADTHFIRST);
2289 Blt_ChainDestroy(queuePtr);
2294 Blt_ChainDestroy(queuePtr);
2297 /* Remove the node from the queue. */
2298 nextPtr = Blt_ChainNextLink(linkPtr);
2299 Blt_ChainDeleteLink(queuePtr, linkPtr);
2302 Blt_ChainDestroy(queuePtr);
2307 NewTreeClient(TreeObject *treeObjPtr, int sharetags)
2309 TreeClient *clientPtr;
2311 clientPtr = Blt_Calloc(1, sizeof(TreeClient));
2312 if (clientPtr != NULL) {
2313 Blt_TreeTagTable *tablePtr;
2314 int hascl = (treeObjPtr->clients->headPtr != NULL);
2316 clientPtr->magic = TREE_MAGIC;
2317 clientPtr->linkPtr = Blt_ChainAppend(treeObjPtr->clients, clientPtr);
2318 clientPtr->events = Blt_ChainCreate();
2319 clientPtr->traces = Blt_ChainCreate();
2320 clientPtr->treeObject = treeObjPtr;
2321 clientPtr->root = treeObjPtr->root;
2322 if (sharetags && hascl) {
2324 ctPtr = Blt_ChainGetValue(treeObjPtr->clients->headPtr);
2325 if (ctPtr && ctPtr->tagTablePtr) {
2326 clientPtr->tagTablePtr = ctPtr->tagTablePtr;
2327 clientPtr->tagTablePtr->refCount++;
2330 if (clientPtr->tagTablePtr == NULL) {
2331 tablePtr = Blt_Malloc(sizeof(Blt_TreeTagTable));
2332 Blt_InitHashTable(&tablePtr->tagTable, BLT_STRING_KEYS);
2333 tablePtr->refCount = 1;
2334 clientPtr->tagTablePtr = tablePtr;
2342 Tcl_Interp *interp, /* Interpreter to report errors back to. */
2343 CONST char *name, /* Name of tree in namespace. Tree
2344 * must not already exist. */
2345 TreeClient **clientPtrPtr) /* (out) Client token of newly created
2346 * tree. Releasing the token will
2347 * free the tree. If NULL, no token
2351 Tcl_DString dString;
2352 Tcl_Namespace *nsPtr;
2353 TreeInterpData *dataPtr;
2354 TreeObject *treeObjPtr;
2355 CONST char *treeName;
2358 dataPtr = GetTreeInterpData(interp);
2360 /* Check if this tree already exists the current namespace. */
2361 treeObjPtr = GetTreeObject(interp, name, NS_SEARCH_CURRENT);
2362 if (treeObjPtr != NULL) {
2364 Tcl_AppendResult(interp, "a tree object \"", name,
2365 "\" already exists", (char *)NULL);
2369 /* Generate a unique tree name in the current namespace. */
2371 sprintf(string, "tree%d", dataPtr->nextId++);
2372 } while (GetTreeObject(interp, string, NS_SEARCH_CURRENT) != NULL);
2376 * Tear apart and put back together the namespace-qualified name
2377 * of the tree. This is to ensure that naming is consistent.
2380 if (Blt_ParseQualifiedName(interp, name, &nsPtr, &treeName) != TCL_OK) {
2382 Tcl_AppendResult(interp, "can't find namespace in \"", name, "\"",
2386 if (nsPtr == NULL) {
2388 * Note: Unlike Tcl_CreateCommand, an unqualified name
2389 * doesn't imply the global namespace, but the current one.
2391 nsPtr = Tcl_GetCurrentNamespace(interp);
2393 name = Blt_GetQualifiedName(nsPtr, treeName, &dString);
2394 treeObjPtr = NewTreeObject(dataPtr, interp, name);
2395 if (treeObjPtr == NULL) {
2397 Tcl_AppendResult(interp, "can't allocate tree \"", name, "\"",
2399 Tcl_DStringFree(&dString);
2402 Tcl_DStringFree(&dString);
2403 if (clientPtrPtr != NULL) {
2404 TreeClient *clientPtr;
2406 clientPtr = NewTreeClient(treeObjPtr, 0);
2407 if (clientPtr == NULL) {
2409 Tcl_AppendResult(interp, "can't allocate tree token",(char *)NULL);
2412 *clientPtrPtr = clientPtr;
2419 Tcl_Interp *interp, /* Interpreter to report errors back to. */
2420 CONST char *name, /* Name of tree in namespace. */
2421 TreeClient **clientPtrPtr)
2423 TreeClient *clientPtr;
2424 TreeObject *treeObjPtr;
2426 treeObjPtr = GetTreeObject(interp, name, NS_SEARCH_BOTH);
2427 if (treeObjPtr == NULL) {
2429 Tcl_AppendResult(interp, "can't find a tree object \"", name, "\"",
2433 clientPtr = NewTreeClient(treeObjPtr, 0);
2434 if (clientPtr == NULL) {
2436 Tcl_AppendResult(interp, "can't allocate token for tree \"",
2437 name, "\"", (char *)NULL);
2440 *clientPtrPtr = clientPtr;
2445 Blt_TreeGetTokenTag(
2446 Tcl_Interp *interp, /* Interpreter to report errors back to. */
2447 CONST char *name, /* Name of tree in namespace. */
2448 TreeClient **clientPtrPtr)
2450 TreeClient *clientPtr;
2451 TreeObject *treeObjPtr;
2453 treeObjPtr = GetTreeObject(interp, name, NS_SEARCH_BOTH);
2454 if (treeObjPtr == NULL) {
2456 Tcl_AppendResult(interp, "can't find a tree object \"", name, "\"",
2460 clientPtr = NewTreeClient(treeObjPtr, 1);
2461 if (clientPtr == NULL) {
2463 Tcl_AppendResult(interp, "can't allocate token for tree \"",
2464 name, "\"", (char *)NULL);
2467 *clientPtrPtr = clientPtr;
2472 Blt_TreeReleaseToken(TreeClient *clientPtr)
2474 TreeObject *treeObjPtr;
2475 Blt_ChainLink *linkPtr;
2476 EventHandler *notifyPtr;
2477 TraceHandler *tracePtr;
2479 if (clientPtr->magic != TREE_MAGIC) {
2480 fprintf(stderr, "invalid tree object token 0x%lx\n",
2481 (unsigned long)clientPtr);
2484 /* Remove any traces that may be set. */
2485 for (linkPtr = Blt_ChainFirstLink(clientPtr->traces); linkPtr != NULL;
2486 linkPtr = Blt_ChainNextLink(linkPtr)) {
2487 tracePtr = Blt_ChainGetValue(linkPtr);
2488 if (tracePtr->keyPattern != NULL) {
2489 Blt_Free(tracePtr->keyPattern);
2493 Blt_ChainDestroy(clientPtr->traces);
2494 /* And any event handlers. */
2495 for(linkPtr = Blt_ChainFirstLink(clientPtr->events);
2496 linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
2497 notifyPtr = Blt_ChainGetValue(linkPtr);
2498 if (notifyPtr->notifyPending) {
2499 Tcl_CancelIdleCall(NotifyIdleProc, notifyPtr);
2501 Blt_Free(notifyPtr);
2503 if (clientPtr->tagTablePtr != NULL) {
2504 ReleaseTagTable(clientPtr->tagTablePtr);
2506 Blt_ChainDestroy(clientPtr->events);
2507 treeObjPtr = clientPtr->treeObject;
2508 if (treeObjPtr != NULL) {
2509 /* Remove the client from the server's list */
2510 Blt_ChainDeleteLink(treeObjPtr->clients, clientPtr->linkPtr);
2511 if (Blt_ChainGetLength(treeObjPtr->clients) == 0) {
2512 treeObjPtr->delete = 1;
2513 Tcl_EventuallyFree(treeObjPtr, DestroyTreeObject);
2514 /* DestroyTreeObject(treeObjPtr); */
2517 clientPtr->magic = 0;
2518 Blt_Free(clientPtr);
2522 Blt_TreeExists(interp, name)
2523 Tcl_Interp *interp; /* Interpreter to report errors back to. */
2524 CONST char *name; /* Name of tree in designated namespace. */
2526 TreeObject *treeObjPtr;
2528 treeObjPtr = GetTreeObject(interp, name, NS_SEARCH_BOTH);
2529 if (treeObjPtr == NULL) {
2530 Tcl_ResetResult(interp);
2539 Node *nodePtr, /* Not used. */
2540 ClientData clientData,
2541 int order) /* Not used. */
2543 int *sumPtr = clientData;
2544 *sumPtr = *sumPtr + 1;
2549 Blt_TreeSize(Node *nodePtr)
2554 Blt_TreeApply(nodePtr, SizeApplyProc, &sum);
2560 Blt_TreeCreateEventHandler(
2561 TreeClient *clientPtr,
2563 Blt_TreeNotifyEventProc *proc,
2564 ClientData clientData)
2566 Blt_ChainLink *linkPtr;
2567 EventHandler *notifyPtr;
2569 notifyPtr = NULL; /* Suppress compiler warning. */
2571 /* Check if the event is already handled. */
2572 for(linkPtr = Blt_ChainFirstLink(clientPtr->events);
2573 linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
2574 notifyPtr = Blt_ChainGetValue(linkPtr);
2575 if ((notifyPtr->proc == proc) &&
2576 (notifyPtr->mask == mask) &&
2577 (notifyPtr->clientData == clientData)) {
2581 if (linkPtr == NULL) {
2582 notifyPtr = Blt_Malloc(sizeof (EventHandler));
2584 linkPtr = Blt_ChainAppend(clientPtr->events, notifyPtr);
2587 Blt_ChainDeleteLink(clientPtr->events, linkPtr);
2588 Blt_Free(notifyPtr);
2590 notifyPtr->proc = proc;
2591 notifyPtr->clientData = clientData;
2592 notifyPtr->mask = mask;
2593 notifyPtr->notifyPending = FALSE;
2594 notifyPtr->interp = clientPtr->treeObject->interp;
2599 Blt_TreeDeleteEventHandler(
2600 TreeClient *clientPtr,
2602 Blt_TreeNotifyEventProc *proc,
2603 ClientData clientData)
2605 Blt_ChainLink *linkPtr;
2606 EventHandler *notifyPtr;
2608 if (!clientPtr) return;
2609 for(linkPtr = Blt_ChainFirstLink(clientPtr->events);
2610 linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
2611 notifyPtr = Blt_ChainGetValue(linkPtr);
2612 if ((notifyPtr->proc == proc) && (notifyPtr->mask == mask) &&
2613 (notifyPtr->clientData == clientData)) {
2614 if (notifyPtr->notifyPending) {
2615 Tcl_CancelIdleCall(NotifyIdleProc, notifyPtr);
2617 Blt_ChainDeleteLink(clientPtr->events, linkPtr);
2618 Blt_Free(notifyPtr);
2625 *----------------------------------------------------------------------
2627 * Blt_TreeNodePath --
2629 *----------------------------------------------------------------------
2632 Blt_TreeNodePath(Node *nodePtr, Tcl_DString *resultPtr)
2634 char **nameArr; /* Used to stack the component names. */
2635 char *staticSpace[64];
2639 nLevels = nodePtr->depth;
2641 nameArr = Blt_Malloc(nLevels * sizeof(char *));
2644 nameArr = staticSpace;
2646 for (i = nLevels - 1; i >= 0; i--) {
2647 /* Save the name of each ancestor in the name array.
2648 * Note that we ignore the root. */
2649 nameArr[i] = nodePtr->label;
2650 nodePtr = nodePtr->parent;
2652 /* Append each the names in the array. */
2653 Tcl_DStringInit(resultPtr);
2654 for (i = 0; i < nLevels; i++) {
2655 Tcl_DStringAppendElement(resultPtr, nameArr[i]);
2657 if (nameArr != staticSpace) {
2660 return Tcl_DStringValue(resultPtr);
2664 Blt_TreeNodePathStr(Node *nodePtr, Tcl_DString *resultPtr, char *prefix, char *delim)
2666 char **nameArr; /* Used to stack the component names. */
2667 char *staticSpace[64];
2671 nLevels = nodePtr->depth;
2673 nameArr = Blt_Malloc(nLevels * sizeof(char *));
2676 nameArr = staticSpace;
2678 for (i = nLevels - 1; i >= 0; i--) {
2679 /* Save the name of each ancestor in the name array.
2680 * Note that we ignore the root. */
2681 nameArr[i] = nodePtr->label;
2682 nodePtr = nodePtr->parent;
2684 /* Append each of the names in the array. */
2685 Tcl_DStringInit(resultPtr);
2686 if (prefix != NULL) {
2687 Tcl_DStringAppend(resultPtr, prefix, -1);
2689 for (i = 0; i < nLevels; i++) {
2690 if (i > 0 && delim != NULL) {
2691 Tcl_DStringAppend(resultPtr, delim, -1);
2693 Tcl_DStringAppend(resultPtr, nameArr[i], -1);
2695 if (nameArr != staticSpace) {
2698 return Tcl_DStringValue(resultPtr);
2702 Blt_TreeArrayValueExists(
2703 TreeClient *clientPtr,
2705 CONST char *arrayName,
2706 CONST char *elemName)
2709 Blt_HashEntry *hPtr;
2710 Blt_HashTable *tablePtr;
2711 register Value *valuePtr;
2713 TreeObject *treeObjPtr = nodePtr->treeObject;
2714 Tcl_Interp *interp = treeObjPtr->interp;
2716 key = Blt_TreeKeyGet(NULL, clientPtr->treeObject,arrayName);
2718 valuePtr = GetTreeValue((Tcl_Interp *)NULL, clientPtr, nodePtr, key);
2719 if (valuePtr == NULL && (!(nodePtr->flags & TREE_TRACE_ACTIVE))) {
2720 if (CallTraces(interp, clientPtr, treeObjPtr, nodePtr, key,
2721 TREE_TRACE_EXISTS, &cnt) != TCL_OK) {
2722 Tcl_ResetResult(interp);
2724 valuePtr = GetTreeValue((Tcl_Interp *)NULL, clientPtr, nodePtr, key);
2727 if (valuePtr == NULL) {
2730 if (IsTclDict(interp, valuePtr->objPtr)) {
2731 /* Preserve type if this was a dict */
2733 Tcl_Obj *keyPtr, *valueObjPtr = NULL;
2735 keyPtr = Tcl_NewStringObj(elemName, -1);
2736 Tcl_IncrRefCount(keyPtr);
2737 result = Tcl_DictObjGet(interp, valuePtr->objPtr, keyPtr, &valueObjPtr);
2738 Tcl_DecrRefCount(keyPtr);
2739 if (result != TCL_OK) {
2742 if (valueObjPtr == NULL) {
2748 if (Blt_IsArrayObj(valuePtr->objPtr) == 0 && Tcl_IsShared(valuePtr->objPtr)) {
2749 Tcl_DecrRefCount(valuePtr->objPtr);
2750 valuePtr->objPtr = Tcl_DuplicateObj(valuePtr->objPtr);
2751 Tcl_IncrRefCount(valuePtr->objPtr);
2753 if (Blt_GetArrayFromObj((Tcl_Interp *)NULL, valuePtr->objPtr, &tablePtr)
2757 hPtr = Blt_FindHashEntry(tablePtr, elemName);
2758 return (hPtr != NULL);
2762 Blt_TreeGetArrayValue(
2764 TreeClient *clientPtr,
2766 CONST char *arrayName,
2767 CONST char *elemName,
2768 Tcl_Obj **valueObjPtrPtr)
2771 Blt_HashEntry *hPtr;
2772 Blt_HashTable *tablePtr;
2773 register Value *valuePtr;
2776 key = Blt_TreeKeyGet(interp, clientPtr->treeObject,arrayName);
2778 /* Reading any element of the array can cause a trace to fire. */
2779 if (!(nodePtr->flags & TREE_TRACE_ACTIVE)) {
2780 if (CallTraces(interp, clientPtr, nodePtr->treeObject, nodePtr, key,
2781 TREE_TRACE_READ, &cnt) != TCL_OK) {
2785 valuePtr = GetTreeValue(interp, clientPtr, nodePtr, key);
2786 if (valuePtr == NULL) {
2789 if (IsTclDict(interp, valuePtr->objPtr)) {
2790 /* Preserve type if this was a dict */
2793 keyPtr = Tcl_NewStringObj(elemName, -1);
2794 Tcl_IncrRefCount(keyPtr);
2795 result = Tcl_DictObjGet(interp, valuePtr->objPtr, keyPtr, valueObjPtrPtr);
2796 Tcl_DecrRefCount(keyPtr);
2797 if (result != TCL_OK) {
2800 if (*valueObjPtrPtr == NULL) {
2801 if (interp != NULL) {
2802 Tcl_AppendResult(interp, "can't find \"", arrayName, "(",
2803 elemName, ")\"", (char *)NULL);
2809 if (Blt_IsArrayObj(valuePtr->objPtr) == 0 && Tcl_IsShared(valuePtr->objPtr)) {
2810 Tcl_DecrRefCount(valuePtr->objPtr);
2811 valuePtr->objPtr = Tcl_DuplicateObj(valuePtr->objPtr);
2812 Tcl_IncrRefCount(valuePtr->objPtr);
2814 if (Blt_GetArrayFromObj(interp, valuePtr->objPtr, &tablePtr) != TCL_OK) {
2817 hPtr = Blt_FindHashEntry(tablePtr, elemName);
2819 if (interp != NULL) {
2820 Tcl_AppendResult(interp, "can't find \"", arrayName, "(",
2821 elemName, ")\"", (char *)NULL);
2826 *valueObjPtrPtr = (Tcl_Obj *)Blt_GetHashValue(hPtr);
2833 TreeClient *clientPtr,
2834 Node *nodePtr, /* Node to be updated. */
2835 CONST char *arrayName,
2836 CONST char *elemName,
2837 Tcl_Obj *valueObjPtr, /* New value of element. */
2838 int create) /* Create the node key if required. */
2841 Blt_HashEntry *hPtr;
2842 Blt_HashTable *tablePtr;
2843 register Value *valuePtr;
2847 assert(valueObjPtr != NULL);
2850 * Search for the array in the list of data fields. If one
2851 * doesn't exist, create it.
2853 key = Blt_TreeKeyGet(interp, clientPtr->treeObject,arrayName);
2854 valuePtr = GetTreeValue((Tcl_Interp *)NULL, clientPtr, nodePtr, key);
2855 if (valuePtr == NULL && create == 0) {
2858 if (valuePtr == NULL) {
2859 if ((nodePtr->flags & TREE_NODE_FIXED_FIELDS)) {
2862 valuePtr = TreeCreateValue(nodePtr, key, &isNew);
2867 if ((valuePtr->owner != NULL) && (valuePtr->owner != clientPtr)) {
2868 if (interp != NULL) {
2869 Tcl_AppendResult(interp, "can't set private field \"",
2870 key, "\"", (char *)NULL);
2874 flags = TREE_TRACE_WRITE;
2876 valuePtr->objPtr = Blt_NewArrayObj(0, (Tcl_Obj **)NULL);
2877 Tcl_IncrRefCount(valuePtr->objPtr);
2878 flags |= TREE_TRACE_CREATE;
2880 } else if (Tcl_IsShared(valuePtr->objPtr)) {
2881 Tcl_DecrRefCount(valuePtr->objPtr);
2882 valuePtr->objPtr = Tcl_DuplicateObj(valuePtr->objPtr);
2883 Tcl_IncrRefCount(valuePtr->objPtr);
2886 if ((clientPtr->treeObject->flags & TREE_DICT_KEYS) &&
2887 IsTclDict(interp, valuePtr->objPtr)) {
2890 if (Tcl_DictObjSize(interp, valuePtr->objPtr, &dSiz) != TCL_OK) {
2894 if (IsTclDict(interp, valuePtr->objPtr)) {
2895 /* Preserve type if this was a dict */
2897 Tcl_Obj *keyPtr, *valObjPtr;
2899 keyPtr = Tcl_NewStringObj(elemName, -1);
2900 Tcl_IncrRefCount(keyPtr);
2902 result = Tcl_DictObjGet(interp, valuePtr->objPtr, keyPtr, &valObjPtr);
2903 if (result != TCL_OK || valObjPtr == NULL) {
2904 Tcl_AppendResult(interp, "can't find field: ", elemName, 0);
2905 Tcl_DecrRefCount(keyPtr);
2909 result = Tcl_DictObjPut(interp, valuePtr->objPtr, keyPtr, valueObjPtr);
2910 Tcl_DecrRefCount(keyPtr);
2911 if (result != TCL_OK) {
2918 if (Blt_GetArrayFromObj(interp, valuePtr->objPtr, &tablePtr) != TCL_OK) {
2921 Tcl_InvalidateStringRep(valuePtr->objPtr);
2923 hPtr = Blt_CreateHashEntry(tablePtr, elemName, &isNew);
2926 hPtr = Blt_FindHashEntry(tablePtr, elemName);
2928 if (interp != NULL) {
2929 Tcl_AppendResult(interp, "can't find array field: ", elemName, 0);
2936 SetModified(nodePtr);
2937 Tcl_IncrRefCount(valueObjPtr);
2939 Tcl_Obj *oldValueObjPtr;
2941 /* An element by the same name already exists. Decrement the
2942 * reference count of the old value. */
2944 oldValueObjPtr = (Tcl_Obj *)Blt_GetHashValue(hPtr);
2945 if (!(nodePtr->flags & TREE_TRACE_ACTIVE)) {
2946 UpdateOldValue(clientPtr, oldValueObjPtr);
2947 oldValueObjPtr = NULL;
2949 if (oldValueObjPtr != NULL) {
2950 Tcl_DecrRefCount(oldValueObjPtr);
2953 if (!(nodePtr->flags & TREE_TRACE_ACTIVE)) {
2954 UpdateOldValue(clientPtr, NULL);
2957 Blt_SetHashValue(hPtr, valueObjPtr);
2961 * We don't handle traces on a per array element basis. Setting
2962 * any element can fire traces for the value.
2964 if (!(nodePtr->flags & TREE_TRACE_ACTIVE)) {
2965 return CallTraces(interp, clientPtr, nodePtr->treeObject, nodePtr,
2966 valuePtr->key, flags, &cnt);
2972 Blt_TreeSetArrayValue(
2974 TreeClient *clientPtr,
2975 Node *nodePtr, /* Node to be updated. */
2976 CONST char *arrayName,
2977 CONST char *elemName,
2978 Tcl_Obj *valueObjPtr) /* New value of element. */
2980 return TreeSetArrayValue(interp, clientPtr, nodePtr, arrayName, elemName, valueObjPtr, 1);
2984 Blt_TreeUpdateArrayValue(
2986 TreeClient *clientPtr,
2987 Node *nodePtr, /* Node to be updated. */
2988 CONST char *arrayName,
2989 CONST char *elemName,
2990 Tcl_Obj *valueObjPtr) /* New value of element. */
2992 return TreeSetArrayValue(interp, clientPtr, nodePtr, arrayName, elemName, valueObjPtr, 0);
2996 Blt_TreeUnsetArrayValue(
2998 TreeClient *clientPtr,
2999 Node *nodePtr, /* Node to be updated. */
3000 CONST char *arrayName,
3001 CONST char *elemName)
3003 Blt_TreeKey key; /* Name of field in node. */
3004 Blt_HashEntry *hPtr;
3005 Blt_HashTable *tablePtr;
3006 Tcl_Obj *valueObjPtr;
3010 key = Blt_TreeKeyGet(interp, clientPtr->treeObject,arrayName);
3011 valuePtr = TreeFindValue(nodePtr, key);
3012 if (valuePtr == NULL) {
3015 if ((valuePtr->owner != NULL) && (valuePtr->owner != clientPtr)) {
3016 if (interp != NULL) {
3017 Tcl_AppendResult(interp, "can't unset private field \"",
3018 key, "\"", (char *)NULL);
3023 if (Tcl_IsShared(valuePtr->objPtr)) {
3024 Tcl_DecrRefCount(valuePtr->objPtr);
3025 valuePtr->objPtr = Tcl_DuplicateObj(valuePtr->objPtr);
3026 Tcl_IncrRefCount(valuePtr->objPtr);
3029 if (IsTclDict(interp, valuePtr->objPtr)) {
3030 /* Preserve type if this was a dict */
3033 keyPtr = Tcl_NewStringObj(elemName, -1);
3034 Tcl_IncrRefCount(keyPtr);
3035 result = Tcl_DictObjRemove(interp, valuePtr->objPtr, keyPtr);
3036 Tcl_DecrRefCount(keyPtr);
3037 if (result != TCL_OK) {
3043 if (Blt_GetArrayFromObj(interp, valuePtr->objPtr, &tablePtr) != TCL_OK) {
3046 hPtr = Blt_FindHashEntry(tablePtr, elemName);
3048 goto finishrm; /* Element doesn't exist, Ok. */
3050 SetModified(nodePtr);
3051 valueObjPtr = (Tcl_Obj *)Blt_GetHashValue(hPtr);
3052 if (!(nodePtr->flags & TREE_TRACE_ACTIVE)) {
3053 UpdateOldValue(clientPtr, valueObjPtr);
3055 Tcl_DecrRefCount(valueObjPtr);
3057 Blt_DeleteHashEntry(tablePtr, hPtr);
3058 Tcl_InvalidateStringRep(valuePtr->objPtr);
3062 * Un-setting any element in the array can cause the trace on the value
3065 if (!(nodePtr->flags & TREE_TRACE_ACTIVE)) {
3066 return CallTraces(interp, clientPtr, nodePtr->treeObject, nodePtr,
3067 valuePtr->key, TREE_TRACE_WRITE, &cnt);
3075 TreeClient *clientPtr,
3077 CONST char *arrayName,
3078 Tcl_Obj *listObjPtr,
3079 CONST char *pattern)
3081 Blt_HashEntry *hPtr;
3082 Blt_HashSearch cursor;
3083 Blt_HashTable *tablePtr;
3088 key = Blt_TreeKeyGet(interp, clientPtr->treeObject,arrayName);
3089 valuePtr = GetTreeValue(interp, clientPtr, nodePtr, key);
3090 if (valuePtr == NULL) {
3093 if (IsTclDict(interp, valuePtr->objPtr)) {
3094 /* Preserve type if this was a dict */
3096 Tcl_DictSearch search;
3100 Tcl_DictObjFirst(NULL, valuePtr->objPtr, &search, &keyPtr, NULL, &done);
3101 for (; !done ; Tcl_DictObjNext(&search, &keyPtr, NULL, &done)) {
3102 if (!pattern || Tcl_StringMatch(Tcl_GetString(keyPtr), pattern)) {
3103 Tcl_ListObjAppendElement(NULL, listObjPtr, keyPtr);
3106 Tcl_DictObjDone(&search);
3110 if (Blt_IsArrayObj(valuePtr->objPtr) == 0 && Tcl_IsShared(valuePtr->objPtr)) {
3111 Tcl_DecrRefCount(valuePtr->objPtr);
3112 valuePtr->objPtr = Tcl_DuplicateObj(valuePtr->objPtr);
3113 Tcl_IncrRefCount(valuePtr->objPtr);
3115 if (Blt_GetArrayFromObj(interp, valuePtr->objPtr, &tablePtr) != TCL_OK) {
3118 /*tablePtr = (Blt_HashTable *)valuePtr->objPtr; */
3119 for (hPtr = Blt_FirstHashEntry(tablePtr, &cursor); hPtr != NULL;
3120 hPtr = Blt_NextHashEntry(&cursor)) {
3123 str = Blt_GetHashKey(tablePtr, hPtr);
3124 if (pattern == NULL || Tcl_StringMatch(str, pattern)) {
3125 objPtr = Tcl_NewStringObj(str, -1);
3126 Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
3133 Blt_TreeArrayValues(
3135 TreeClient *clientPtr,
3137 CONST char *arrayName,
3138 Tcl_Obj *listObjPtr,
3141 Blt_HashEntry *hPtr;
3142 Blt_HashSearch cursor;
3143 Blt_HashTable *tablePtr;
3148 key = Blt_TreeKeyGet(interp, clientPtr->treeObject,arrayName);
3149 if ( bltTreeGetValueByKey(interp, clientPtr, nodePtr, key, &valuePtr) != TCL_OK) {
3152 if (IsTclDict(interp, valuePtr->objPtr)) {
3153 /* Preserve type if this was a dict */
3155 Tcl_DictSearch search;
3160 Tcl_DictObjFirst(NULL, valuePtr->objPtr, &search, &keyPtr, NULL, &done);
3161 for (; !done ; Tcl_DictObjNext(&search, &keyPtr, NULL, &done)) {
3162 Tcl_Obj *valueObjPtr;
3164 Tcl_ListObjAppendElement(NULL, listObjPtr, keyPtr);
3168 result = Tcl_DictObjGet(interp, valuePtr->objPtr, keyPtr, &valueObjPtr);
3169 if (result != TCL_OK) {
3172 if (valueObjPtr == NULL) {
3173 valueObjPtr = Tcl_NewStringObj("",-1);
3175 Tcl_ListObjAppendElement(NULL, listObjPtr, valueObjPtr);
3177 Tcl_DictObjDone(&search);
3180 if (Blt_IsArrayObj(valuePtr->objPtr) == 0 && Tcl_IsShared(valuePtr->objPtr)) {
3181 Tcl_DecrRefCount(valuePtr->objPtr);
3182 valuePtr->objPtr = Tcl_DuplicateObj(valuePtr->objPtr);
3183 Tcl_IncrRefCount(valuePtr->objPtr);
3185 if (Blt_GetArrayFromObj(interp, valuePtr->objPtr, &tablePtr) != TCL_OK) {
3188 /*tablePtr = (Blt_HashTable *)valuePtr->objPtr; */
3189 for (hPtr = Blt_FirstHashEntry(tablePtr, &cursor); hPtr != NULL;
3190 hPtr = Blt_NextHashEntry(&cursor)) {
3192 Tcl_ListObjAppendElement(interp, listObjPtr,
3193 Tcl_NewStringObj( Blt_GetHashKey(tablePtr, hPtr), -1));
3195 objPtr = Blt_GetHashValue(hPtr);
3196 Tcl_ListObjAppendElement(interp, listObjPtr, objPtr?objPtr:Tcl_NewStringObj("",-1));
3203 *----------------------------------------------------------------------
3205 * Blt_TreeShareTagTable --
3207 *----------------------------------------------------------------------
3210 Blt_TreeShareTagTable(
3211 TreeClient *sourcePtr,
3212 TreeClient *targetPtr)
3214 sourcePtr->tagTablePtr->refCount++;
3215 if (targetPtr->tagTablePtr != NULL) {
3216 ReleaseTagTable(targetPtr->tagTablePtr);
3218 targetPtr->tagTablePtr = sourcePtr->tagTablePtr;
3223 Blt_TreeTagTableIsShared(TreeClient *clientPtr)
3225 return (clientPtr->tagTablePtr->refCount > 1);
3229 Blt_TreeClearTags(TreeClient *clientPtr, Blt_TreeNode node)
3231 Blt_HashEntry *hPtr, *h2Ptr;
3232 Blt_HashSearch cursor;
3234 for (hPtr = Blt_FirstHashEntry(&clientPtr->tagTablePtr->tagTable, &cursor);
3235 hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
3236 Blt_TreeTagEntry *tPtr;
3238 tPtr = Blt_GetHashValue(hPtr);
3239 h2Ptr = Blt_FindHashEntry(&tPtr->nodeTable, (char *)node);
3240 if (h2Ptr != NULL) {
3242 Blt_DeleteHashEntry(&tPtr->nodeTable, h2Ptr);
3249 TreeClient *clientPtr,
3251 CONST char *tagName)
3253 Blt_HashEntry *hPtr;
3254 Blt_TreeTagEntry *tPtr;
3256 if (strcmp(tagName, "all") == 0) {
3259 if (strcmp(tagName, "nonroot") == 0) {
3262 if (strcmp(tagName, "rootchildren") == 0) {
3265 if ((strcmp(tagName, "root") == 0) &&
3266 (node == Blt_TreeRootNode(clientPtr))) {
3269 hPtr = Blt_FindHashEntry(&clientPtr->tagTablePtr->tagTable, tagName);
3273 tPtr = Blt_GetHashValue(hPtr);
3274 hPtr = Blt_FindHashEntry(&tPtr->nodeTable, (char *)node);
3283 TreeClient *clientPtr,
3285 CONST char *tagName)
3287 int isNew, isNewN, cnt = 0, flags;
3288 Blt_HashEntry *hPtr;
3289 Blt_HashTable *tablePtr;
3290 Blt_TreeTagEntry *tPtr;
3291 Tcl_Interp *interp = clientPtr->treeObject->interp;
3293 if ((strcmp(tagName, "all") == 0) || (strcmp(tagName, "root") == 0)
3294 || (strcmp(tagName, "nonroot") == 0) || (strcmp(tagName, "rootchildren") == 0)) {
3295 Tcl_AppendResult(interp, "reserved tag", 0);
3298 tablePtr = &clientPtr->tagTablePtr->tagTable;
3299 hPtr = Blt_CreateHashEntry(tablePtr, tagName, &isNewN);
3303 tPtr = Blt_Calloc(sizeof(Blt_TreeTagEntry), 1);
3304 Blt_InitHashTable(&tPtr->nodeTable, BLT_ONE_WORD_KEYS);
3305 Blt_SetHashValue(hPtr, tPtr);
3306 tPtr->hashPtr = hPtr;
3307 tPtr->tagName = Blt_GetHashKey(tablePtr, hPtr);
3308 Blt_TreeTagRefIncr(tPtr);
3310 tPtr = Blt_GetHashValue(hPtr);
3315 if (!(node->flags & TREE_TRACE_ACTIVE)) {
3317 flags = TREE_TRACE_TAGADD;
3318 if (tPtr->nodeTable.numEntries > 0) {
3319 flags |= TREE_TRACE_TAGMULTIPLE;
3321 result = CallTraces(interp, clientPtr, node->treeObject, node, tagName,
3323 if (result == TCL_BREAK) {
3326 if (result != TCL_OK) {
3330 hPtr = Blt_CreateHashEntry(&tPtr->nodeTable, (char *)node, &isNew);
3334 Blt_SetHashValue(hPtr, node);
3339 /* Trigger tag delete traces. */
3341 Blt_TreeTagDelTrace(
3342 TreeClient *clientPtr,
3344 CONST char *tagName)
3346 Tcl_Interp *interp = clientPtr->treeObject->interp;
3349 if (!(node->flags & TREE_TRACE_ACTIVE)) {
3350 return CallTraces(interp, clientPtr, node->treeObject, node, tagName,
3351 (TREE_TRACE_TAGDELETE), &cnt);
3357 Blt_TreeForgetTag(TreeClient *clientPtr, CONST char *tagName)
3359 Blt_HashEntry *hPtr;
3360 if ((strcmp(tagName, "all") == 0) || (strcmp(tagName, "root") == 0)
3361 || (strcmp(tagName, "nonroot") == 0)
3362 || (strcmp(tagName, "rootchildren") == 0)) {
3366 hPtr = Blt_FindHashEntry(&clientPtr->tagTablePtr->tagTable, tagName);
3368 Blt_TreeTagEntry *tPtr;
3370 Blt_HashSearch cursor;
3372 Blt_DeleteHashEntry(&clientPtr->tagTablePtr->tagTable, hPtr);
3373 tPtr = Blt_GetHashValue(hPtr);
3374 for (hPtr = Blt_FirstHashEntry(&tPtr->nodeTable, &cursor);
3375 hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
3377 node = Blt_GetHashKey(&tPtr->nodeTable, hPtr);
3378 if (Blt_TreeTagDelTrace(clientPtr, node, tagName) != TCL_OK) {
3383 Blt_DeleteHashTable(&tPtr->nodeTable);
3384 Blt_TreeTagRefDecr(tPtr);
3390 *----------------------------------------------------------------------
3392 * Blt_TreeTagHashTable --
3394 *----------------------------------------------------------------------
3397 Blt_TreeTagHashTable(TreeClient *clientPtr, CONST char *tagName)
3399 Blt_HashEntry *hPtr;
3401 hPtr = Blt_FindHashEntry(&clientPtr->tagTablePtr->tagTable, tagName);
3403 Blt_TreeTagEntry *tPtr;
3405 tPtr = Blt_GetHashValue(hPtr);
3406 return &tPtr->nodeTable;
3412 Blt_TreeTagHashEntry(TreeClient *clientPtr, CONST char *tagName)
3414 Blt_HashEntry *hPtr;
3416 hPtr = Blt_FindHashEntry(&clientPtr->tagTablePtr->tagTable, tagName);
3418 Blt_TreeTagEntry *tPtr;
3420 tPtr = Blt_GetHashValue(hPtr);
3427 Blt_TreeFirstTag(TreeClient *clientPtr, Blt_HashSearch *cursorPtr)
3429 return Blt_FirstHashEntry(&clientPtr->tagTablePtr->tagTable, cursorPtr);
3432 #if (SIZEOF_VOID_P == 8)
3434 *----------------------------------------------------------------------
3438 * Compute a one-word hash value of a 64-bit word, which then can
3439 * be used to generate a hash index.
3441 * From Knuth, it's a multiplicative hash. Multiplies an unsigned
3442 * 64-bit value with the golden ratio (sqrt(5) - 1) / 2. The
3443 * downshift value is 64 - n, when n is the log2 of the size of
3447 * The return value is a one-word summary of the information in
3453 *----------------------------------------------------------------------
3458 unsigned int downshift,
3467 /* Compute key * GOLDEN_RATIO in 128-bit arithmetic */
3468 a0 = (uint64_t)key & 0x00000000FFFFFFFF;
3469 a1 = (uint64_t)key >> 32;
3471 y0 = a0 * 0x000000007f4a7c13;
3472 y1 = a0 * 0x000000009e3779b9;
3473 y2 = a1 * 0x000000007f4a7c13;
3474 y3 = a1 * 0x000000009e3779b9;
3475 y1 += y0 >> 32; /* Can't carry */
3476 y1 += y2; /* Might carry */
3478 y3 += (1LL << 32); /* Propagate */
3481 /* 128-bit product: p1 = loword, p2 = hiword */
3482 p1 = ((y1 & 0x00000000FFFFFFFF) << 32) + (y0 & 0x00000000FFFFFFFF);
3483 p2 = y3 + (y1 >> 32);
3485 /* Left shift the value downward by the size of the table */
3486 if (downshift > 0) {
3487 if (downshift < 64) {
3488 result = ((p2 << (64 - downshift)) | (p1 >> (downshift & 63)));
3490 result = p2 >> (downshift & 63);
3495 /* Finally mask off the high bits */
3496 return (Blt_Hash)(result & mask);
3499 #endif /* SIZEOF_VOID_P == 8 */
3502 *----------------------------------------------------------------------
3506 * This procedure is invoked when the ratio of entries to hash
3507 * buckets becomes too large. It creates a new table with a
3508 * larger bucket array and moves all of the entries into the
3515 * Memory gets reallocated and entries get re-hashed to new
3518 *----------------------------------------------------------------------
3521 RebuildTable(Node *nodePtr) /* Table to enlarge. */
3523 Value **newBucketPtr, **oldBuckets;
3524 register Value **bucketPtr, **endPtr;
3525 register Value *valuePtr, *nextPtr;
3526 unsigned int downshift;
3531 oldBuckets = (Value **)nodePtr->values;
3532 nBuckets = (1 << nodePtr->logSize);
3533 endPtr = oldBuckets + nBuckets;
3536 * Allocate and initialize the new bucket array, and set up
3537 * hashing constants for new array size.
3539 nodePtr->logSize += 2;
3540 nBuckets = (1 << nodePtr->logSize);
3541 buckets = Blt_Calloc(nBuckets, sizeof(Value *));
3544 * Move all of the existing entries into the new bucket array,
3545 * based on their hash values.
3547 mask = nBuckets - 1;
3548 downshift = DOWNSHIFT_START - nodePtr->logSize;
3549 for (bucketPtr = oldBuckets; bucketPtr < endPtr; bucketPtr++) {
3550 for (valuePtr = *bucketPtr; valuePtr != NULL; valuePtr = nextPtr) {
3551 nextPtr = valuePtr->next;
3552 newBucketPtr = buckets + RANDOM_INDEX(valuePtr->key);
3553 valuePtr->next = *newBucketPtr;
3554 *newBucketPtr = valuePtr;
3557 nodePtr->values = (Value *)buckets;
3558 Blt_Free(oldBuckets);
3562 ConvertValues(Node *nodePtr)
3564 unsigned int nBuckets;
3568 Value *valuePtr, *nextPtr, **bucketPtr;
3571 * Convert list of values into a hash table.
3573 nodePtr->logSize = START_LOGSIZE;
3574 nBuckets = 1 << nodePtr->logSize;
3575 buckets = Blt_Calloc(nBuckets, sizeof(Value *));
3576 mask = nBuckets - 1;
3577 downshift = DOWNSHIFT_START - nodePtr->logSize;
3578 for (valuePtr = nodePtr->values; valuePtr != NULL; valuePtr = nextPtr) {
3579 nextPtr = valuePtr->next;
3580 bucketPtr = buckets + RANDOM_INDEX(valuePtr->key);
3581 valuePtr->next = *bucketPtr;
3582 *bucketPtr = valuePtr;
3584 nodePtr->values = (Value *)buckets;
3588 *----------------------------------------------------------------------
3590 * TreeDeleteValue --
3592 * Remove a single entry from a hash table.
3598 * The entry given by entryPtr is deleted from its table and
3599 * should never again be used by the caller. It is up to the
3600 * caller to free the clientData field of the entry, if that
3603 *----------------------------------------------------------------------
3606 TreeDeleteValue(Node *nodePtr, Blt_TreeValue value)
3608 Value *valuePtr = value;
3609 register Value *prevPtr;
3611 if (nodePtr->logSize > 0) {
3613 unsigned int downshift;
3616 mask = (1 << nodePtr->logSize) - 1;
3617 downshift = DOWNSHIFT_START - nodePtr->logSize;
3619 bucketPtr = (Value **)nodePtr->values + RANDOM_INDEX(valuePtr->key);
3620 if (*bucketPtr == valuePtr) {
3621 *bucketPtr = valuePtr->next;
3623 for (prevPtr = *bucketPtr; /*empty*/; prevPtr = prevPtr->next) {
3624 if (prevPtr == NULL) {
3625 return TCL_ERROR; /* Can't find value in hash bucket. */
3627 if (prevPtr->next == valuePtr) {
3628 prevPtr->next = valuePtr->next;
3635 for (valuePtr = nodePtr->values; valuePtr != NULL;
3636 valuePtr = valuePtr->next) {
3637 if (valuePtr == value) {
3642 if (valuePtr == NULL) {
3643 return TCL_ERROR; /* Can't find value in list. */
3645 if (prevPtr == NULL) {
3646 nodePtr->values = valuePtr->next;
3648 prevPtr->next = valuePtr->next;
3652 FreeValue(nodePtr, valuePtr);
3657 *----------------------------------------------------------------------
3659 * TreeDestroyValues --
3661 * Free up everything associated with a hash table except for
3662 * the record for the table itself.
3668 * The hash table is no longer useable.
3670 *----------------------------------------------------------------------
3673 TreeDestroyValues(Node *nodePtr)
3675 register Value *valuePtr;
3679 * Free up all the entries in the table.
3681 if (nodePtr->values == NULL) {
3684 if (nodePtr->logSize > 0) {
3689 buckets = (Value **)nodePtr->values;
3690 nBuckets = (1 << nodePtr->logSize);
3691 for (i = 0; i < nBuckets; i++) {
3692 for (valuePtr = buckets[i]; valuePtr != NULL; valuePtr = nextPtr) {
3693 nextPtr = valuePtr->next;
3694 FreeValue(nodePtr, valuePtr);
3699 for (valuePtr = nodePtr->values; valuePtr != NULL; valuePtr = nextPtr) {
3700 nextPtr = valuePtr->next;
3701 FreeValue(nodePtr, valuePtr);
3704 nodePtr->values = NULL;
3705 nodePtr->nValues = 0;
3706 nodePtr->logSize = 0;
3710 *----------------------------------------------------------------------
3714 * Locate the first entry in a hash table and set up a record
3715 * that can be used to step through all the remaining entries
3719 * The return value is a pointer to the first value in tablePtr,
3720 * or NULL if tablePtr has no entries in it. The memory at
3721 * *searchPtr is initialized so that subsequent calls to
3722 * Blt_TreeNextValue will return all of the values in the table,
3728 *----------------------------------------------------------------------
3733 Blt_TreeKeySearch *searchPtr) /* Place to store information about
3734 * progress through the table. */
3736 searchPtr->node = nodePtr;
3737 searchPtr->nextIndex = 0;
3739 if (nodePtr->logSize > 0) {
3740 searchPtr->nextValue = NULL;
3742 searchPtr->nextValue = nodePtr->values;
3744 return TreeNextValue(searchPtr);
3748 *----------------------------------------------------------------------
3752 * Once a hash table enumeration has been initiated by calling
3753 * Blt_TreeFirstValue, this procedure may be called to return
3754 * successive elements of the table.
3757 * The return value is the next entry in the hash table being
3758 * enumerated, or NULL if the end of the table is reached.
3763 *----------------------------------------------------------------------
3767 Blt_TreeKeySearch *searchPtr) /* Place to store information about
3768 * progress through the table. Must
3769 * have been initialized by calling
3770 * Blt_TreeFirstValue. */
3774 if (searchPtr->node->logSize > 0) {
3778 nBuckets = (1 << searchPtr->node->logSize);
3779 buckets = (Value **)searchPtr->node->values;
3780 while (searchPtr->nextValue == NULL) {
3781 if (searchPtr->nextIndex >= nBuckets) {
3784 searchPtr->nextValue = buckets[searchPtr->nextIndex];
3785 searchPtr->nextIndex++;
3790 if (searchPtr->cnt>100000000) return NULL;
3791 valuePtr = searchPtr->nextValue;
3792 if (valuePtr != NULL) {
3793 searchPtr->nextValue = valuePtr->next;
3799 *----------------------------------------------------------------------
3803 * Given a hash table with one-word keys, and a one-word key, find
3804 * the entry with a matching key.
3807 * The return value is a token for the matching entry in the
3808 * hash table, or NULL if there was no matching entry.
3813 *----------------------------------------------------------------------
3818 Blt_TreeKey key) /* Key to use to find matching entry. */
3820 register Value *valuePtr;
3823 if (nodePtr->logSize > 0) {
3824 unsigned int downshift;
3827 mask = (1 << nodePtr->logSize) - 1;
3828 downshift = DOWNSHIFT_START - nodePtr->logSize;
3829 bucket = ((Value **)(nodePtr->values))[RANDOM_INDEX((void *)key)];
3831 bucket = nodePtr->values; /* Single chain list. */
3834 * Search all of the entries in the appropriate bucket.
3836 for (valuePtr = bucket; valuePtr != NULL; valuePtr = valuePtr->next) {
3837 if (valuePtr->key == key) {
3845 *----------------------------------------------------------------------
3847 * Blt_TreeCreateValue --
3849 * Find the value with a matching key. If there is no matching
3850 * value, then create a new one.
3853 * The return value is a pointer to the matching value. If this
3854 * is a newly-created value, then *newPtr will be set to a non-zero
3855 * value; otherwise *newPtr will be set to 0.
3858 * A new value may be added to the hash table.
3860 *----------------------------------------------------------------------
3865 Blt_TreeKey key, /* Key to use to find or create matching
3867 int *newPtr) /* Store info here telling whether a new
3868 * entry was created. */
3870 register Value *valuePtr;
3871 TreeObject *treeObjPtr = nodePtr->treeObject;
3874 if (treeObjPtr->maxKeyList>0) {
3875 maxVals = treeObjPtr->maxKeyList;
3877 maxVals = MAX_LIST_VALUES;
3880 * Check if there as so many values that storage should be
3881 * converted from a hash table instead of a list.
3883 if ((nodePtr->logSize == 0) && (nodePtr->nValues >= maxVals)) {
3884 ConvertValues(nodePtr);
3886 if (nodePtr->logSize > 0) {
3889 unsigned int downshift;
3892 nBuckets = (1 << nodePtr->logSize);
3893 mask = nBuckets - 1;
3894 downshift = DOWNSHIFT_START - nodePtr->logSize;
3895 bucketPtr = (Value **)nodePtr->values + RANDOM_INDEX((void *)key);
3898 for (valuePtr = *bucketPtr; valuePtr != NULL;
3899 valuePtr = valuePtr->next) {
3900 if (valuePtr->key == key) {
3905 /* Value not found. Add a new value to the bucket. */
3907 valuePtr = Blt_PoolAllocItem(nodePtr->treeObject->valuePool,
3909 valuePtr->key = key;
3910 valuePtr->owner = NULL;
3911 valuePtr->next = *bucketPtr;
3912 valuePtr->objPtr = NULL;
3913 *bucketPtr = valuePtr;
3917 * If the table has exceeded a decent size, rebuild it with many
3920 if ((unsigned int)nodePtr->nValues >= (nBuckets * 3)) {
3921 RebuildTable(nodePtr);
3928 for (valuePtr = nodePtr->values; valuePtr != NULL;
3929 valuePtr = valuePtr->next) {
3930 if (valuePtr->key == key) {
3935 /* Value not found. Add a new value to the list. */
3937 valuePtr = Blt_PoolAllocItem(nodePtr->treeObject->valuePool,
3939 valuePtr->key = key;
3940 valuePtr->owner = NULL;
3941 valuePtr->next = NULL;
3942 valuePtr->objPtr = NULL;
3943 if (prevPtr == NULL) {
3944 nodePtr->values = valuePtr;
3946 prevPtr->next = valuePtr;
3954 Blt_TreeNode Blt_TreeEndNode (Blt_TreeNode node,
3955 unsigned int nodeFlags) {
3960 #undef Blt_TreeFirstChild
3961 #undef Blt_TreeLastChild
3962 #undef Blt_TreeNextSibling
3963 #undef Blt_TreePrevSibling
3964 #undef Blt_TreeChangeRoot
3966 Blt_TreeNode Blt_TreeFirstChild (Blt_TreeNode parent) {
3967 return parent->first;
3970 Blt_TreeNode Blt_TreeNextSibling (Blt_TreeNode node) {
3971 return (node == NULL ? NULL : node->next);
3974 Blt_TreeNode Blt_TreeLastChild (Blt_TreeNode parent) {
3975 return parent->last;
3978 Blt_TreeNode Blt_TreePrevSibling (Blt_TreeNode node) {
3979 return (node == NULL ? NULL : node->prev);
3982 Blt_TreeNode Blt_TreeChangeRoot (Blt_Tree tree, Blt_TreeNode node) {
3987 #endif /* NO_TREE */