]>
cvs.zerfleddert.de Git - micropolis/blob - src/tk/tktxbtre.c
4 * This file contains code that manages the B-tree representation
5 * of text for Tk's text widget. The B-tree holds both the text
6 * and tag information related to the text.
8 * Copyright 1992 Regents of the University of California
9 * Permission to use, copy, modify, and distribute this
10 * software and its documentation for any purpose and without
11 * fee is hereby granted, provided that this copyright
12 * notice appears in all copies. The University of California
13 * makes no representations about the suitability of this
14 * software for any purpose. It is provided "as is" without
15 * express or implied warranty.
19 static char rcsid
[] = "$Header: /user6/ouster/wish/RCS/tkTextBTree.c,v 1.16 92/08/17 09:13:58 ouster Exp $ SPRITE (Berkeley)";
28 * The data structure below keeps summary information about one tag as part
29 * of the tag information in a node.
32 typedef struct Summary
{
33 TkTextTag
*tagPtr
; /* Handle for tag. */
34 int toggleCount
; /* Number of transitions into or
35 * out of this tag that occur in
36 * the subtree rooted at this node. */
37 struct Summary
*nextPtr
; /* Next in list of all tags for same
38 * node, or NULL if at end of list. */
42 * The data structure below defines a node in the B-tree representing
43 * all of the lines in a text widget.
47 struct Node
*parentPtr
; /* Pointer to parent node, or NULL if
48 * this is the root. */
49 struct Node
*nextPtr
; /* Next in list of children of the
50 * same parent node, or NULL for end
52 Summary
*summaryPtr
; /* First in malloc-ed list of info
53 * about tags in this subtree (NULL if
54 * no tag info in the subtree). */
55 int level
; /* Level of this node in the B-tree.
56 * 0 refers to the bottom of the tree
57 * (children are lines, not nodes). */
58 union { /* First in linked list of children. */
59 struct Node
*nodePtr
; /* Used if level > 0. */
60 TkTextLine
*linePtr
; /* Used if level == 0. */
62 int numChildren
; /* Number of children of this node. */
63 int numLines
; /* Total number of lines (leaves) in
64 * the subtree rooted here. */
68 * Upper and lower bounds on how many children a node may have:
69 * rebalance when either of these limits is exceeded. MAX_CHILDREN
70 * should be twice MIN_CHILDREN and MIN_CHILDREN must be >= 2.
73 #define MAX_CHILDREN 12
74 #define MIN_CHILDREN 6
77 * The data structure below defines an entire B-tree.
80 typedef struct BTree
{
81 Node
*rootPtr
; /* Pointer to root of B-tree. */
85 * The structure below is used to pass information between
86 * TkBTreeGetTags and IncCount:
89 typedef struct TagInfo
{
90 int numTags
; /* Number of tags for which there
91 * is currently information in
93 int arraySize
; /* Number of entries allocated for
95 TkTextTag
**tagPtrs
; /* Array of tags seen so far.
97 int *counts
; /* Toggle count (so far) for each
98 * entry in tags. Malloc-ed. */
102 * Macro to compute the space needed for a line that holds n non-null
106 #define LINE_SIZE(n) ((unsigned) (sizeof(TkTextLine) - 3 + (n)))
109 * Variable that indicates whether to enable consistency checks for
113 int tkBTreeDebug
= 0;
116 * Forward declarations for procedures defined in this file:
119 static void AddToggleToLine
_ANSI_ARGS_((TkTextLine
*linePtr
,
120 int index
, TkTextTag
*tagPtr
));
121 static void ChangeNodeToggleCount
_ANSI_ARGS_((Node
*nodePtr
,
122 TkTextTag
*tagPtr
, int delta
));
123 static void CheckNodeConsistency
_ANSI_ARGS_((Node
*nodePtr
));
124 static void DeleteSummaries
_ANSI_ARGS_((Summary
*tagPtr
));
125 static void DestroyNode
_ANSI_ARGS_((Node
*nodePtr
));
126 static void IncCount
_ANSI_ARGS_((TkTextTag
*tagPtr
, int inc
,
127 TagInfo
*tagInfoPtr
));
128 static void Rebalance
_ANSI_ARGS_((BTree
*treePtr
, Node
*nodePtr
));
129 static void RecomputeNodeCounts
_ANSI_ARGS_((Node
*nodePtr
));
132 *----------------------------------------------------------------------
136 * This procedure is called to create a new text B-tree.
139 * The return value is a pointer to a new B-tree containing
140 * one line with nothing but a newline character.
143 * Memory is allocated and initialized.
145 *----------------------------------------------------------------------
151 register BTree
*treePtr
;
152 register Node
*rootPtr
;
153 register TkTextLine
*linePtr
;
155 rootPtr
= (Node
*) ckalloc(sizeof(Node
));
156 linePtr
= (TkTextLine
*) ckalloc(LINE_SIZE(1));
157 rootPtr
->parentPtr
= NULL
;
158 rootPtr
->nextPtr
= NULL
;
159 rootPtr
->summaryPtr
= NULL
;
161 rootPtr
->children
.linePtr
= linePtr
;
162 rootPtr
->numChildren
= 1;
163 rootPtr
->numLines
= 1;
165 linePtr
->parentPtr
= rootPtr
;
166 linePtr
->nextPtr
= NULL
;
167 linePtr
->annotPtr
= NULL
;
168 linePtr
->numBytes
= 1;
169 linePtr
->bytes
[0] = '\n';
170 linePtr
->bytes
[1] = 0;
172 treePtr
= (BTree
*) ckalloc(sizeof(BTree
));
173 treePtr
->rootPtr
= rootPtr
;
175 return (TkTextBTree
) treePtr
;
179 *----------------------------------------------------------------------
183 * Delete a B-tree, recycling all of the storage it contains.
186 * The tree given by treePtr is deleted. TreePtr should never
192 *----------------------------------------------------------------------
197 TkTextBTree tree
/* Pointer to tree to delete. */
200 BTree
*treePtr
= (BTree
*) tree
;
202 DestroyNode(treePtr
->rootPtr
);
203 ckfree((char *) treePtr
);
207 *----------------------------------------------------------------------
211 * This is a recursive utility procedure used during the deletion
218 * All the storage for nodePtr and its descendants is freed.
220 *----------------------------------------------------------------------
224 DestroyNode (register Node
*nodePtr
)
226 if (nodePtr
->level
== 0) {
227 register TkTextLine
*curPtr
, *nextLinePtr
;
228 register TkAnnotation
*annotPtr
, *nextAnnotPtr
;
230 for (curPtr
= nodePtr
->children
.linePtr
; curPtr
!= NULL
; ) {
231 nextLinePtr
= curPtr
->nextPtr
;
232 for (annotPtr
= curPtr
->annotPtr
; annotPtr
!= NULL
; ) {
233 nextAnnotPtr
= annotPtr
->nextPtr
;
234 if (annotPtr
->type
== TK_ANNOT_TOGGLE
) {
235 ckfree((char *) annotPtr
);
237 annotPtr
= nextAnnotPtr
;
239 ckfree((char *) curPtr
);
240 curPtr
= nextLinePtr
;
243 register Node
*curPtr
, *nextPtr
;
245 for (curPtr
= nodePtr
->children
.nodePtr
; curPtr
!= NULL
; ) {
246 nextPtr
= curPtr
->nextPtr
;
251 DeleteSummaries(nodePtr
->summaryPtr
);
252 ckfree((char *) nodePtr
);
256 *----------------------------------------------------------------------
260 * Free up all of the memory in a list of tag summaries associated
267 * Storage is released.
269 *----------------------------------------------------------------------
274 register Summary
*summaryPtr
/* First in list of node's tag
278 register Summary
*nextPtr
;
279 while (summaryPtr
!= NULL
) {
280 nextPtr
= summaryPtr
->nextPtr
;
281 ckfree((char *) summaryPtr
);
282 summaryPtr
= nextPtr
;
287 *----------------------------------------------------------------------
289 * TkBTreeInsertChars --
291 * Insert characters at a given position in a B-tree.
297 * NumBytes characters are added to the B-tree at the given
298 * character position. This can cause the structure of the
301 *----------------------------------------------------------------------
306 TkTextBTree tree
, /* B-tree in which to insert. */
307 register TkTextLine
*linePtr
, /* Pointer to line in which to
309 int ch
, /* Index of character before which
310 * to insert. Must not be after
311 * last character in line.*/
312 char *string
/* Pointer to bytes to insert (may
313 * contain newlines, must be null-
317 BTree
*treePtr
= (BTree
*) tree
;
318 register Node
*nodePtr
;
319 register TkAnnotation
*annotPtr
;
321 int newChunkLength
; /* # chars in current line being
323 register char *eol
; /* Pointer to last character in
324 * current line being inserted. */
325 int changeToLineCount
; /* Counts change to total number of
327 TkAnnotation
*afterPtr
; /* List of annotations that occur
328 * at or after the insertion point
329 * in the line of the insertion. */
330 int prefixLength
, suffixLength
, totalLength
;
331 register TkTextLine
*newPtr
;
334 * Find the line just before the one where the insertion will occur
335 * but with the same parent node (if there is one). This is needed
336 * so we can replace the insertion line with a new one. Remove this
337 * line from the list for its parent, since it's going to be discarded
338 * when we're all done).
341 nodePtr
= linePtr
->parentPtr
;
342 prevPtr
= nodePtr
->children
.linePtr
;
343 if (prevPtr
== linePtr
) {
345 nodePtr
->children
.linePtr
= linePtr
->nextPtr
;
347 for ( ; prevPtr
->nextPtr
!= linePtr
; prevPtr
= prevPtr
->nextPtr
) {
348 /* Empty loop body. */
350 prevPtr
->nextPtr
= linePtr
->nextPtr
;
354 * Break up the annotations for the insertion line into two pieces:
355 * those before the insertion point, and those at or after the insertion
360 if ((linePtr
->annotPtr
!= NULL
) && (linePtr
->annotPtr
->ch
>= ch
)) {
361 afterPtr
= linePtr
->annotPtr
;
362 linePtr
->annotPtr
= NULL
;
364 for (annotPtr
= linePtr
->annotPtr
; annotPtr
!= NULL
;
365 annotPtr
= annotPtr
->nextPtr
) {
366 if ((annotPtr
->nextPtr
!= NULL
)
367 && (annotPtr
->nextPtr
->ch
>= ch
)) {
368 afterPtr
= annotPtr
->nextPtr
;
369 annotPtr
->nextPtr
= NULL
;
376 * Chop the string up into lines and insert each line individually.
379 changeToLineCount
= -1;
382 for (newChunkLength
= 0, eol
= string
; *eol
!= 0; eol
++) {
390 * Create a new line consisting of up to three parts: a prefix
391 * from linePtr, some material from string, and a suffix from
395 if ((newChunkLength
== 0) || (*eol
!= '\n')) {
396 suffixLength
= linePtr
->numBytes
- ch
;
400 totalLength
= prefixLength
+ newChunkLength
+ suffixLength
;
401 newPtr
= (TkTextLine
*) ckalloc(LINE_SIZE(totalLength
));
402 newPtr
->parentPtr
= nodePtr
;
403 if (prevPtr
== NULL
) {
404 newPtr
->nextPtr
= nodePtr
->children
.linePtr
;
405 nodePtr
->children
.linePtr
= newPtr
;
407 newPtr
->nextPtr
= prevPtr
->nextPtr
;
408 prevPtr
->nextPtr
= newPtr
;
410 if (linePtr
->annotPtr
!= NULL
) {
411 newPtr
->annotPtr
= linePtr
->annotPtr
;
412 for (annotPtr
= newPtr
->annotPtr
; annotPtr
!= NULL
;
413 annotPtr
= annotPtr
->nextPtr
) {
414 annotPtr
->linePtr
= newPtr
;
416 linePtr
->annotPtr
= NULL
;
418 newPtr
->annotPtr
= NULL
;
420 newPtr
->numBytes
= totalLength
;
421 if (prefixLength
!= 0) {
422 memcpy((VOID
*) newPtr
->bytes
, (VOID
*) linePtr
->bytes
,
425 if (newChunkLength
!= 0) {
426 memcpy((VOID
*) (newPtr
->bytes
+ prefixLength
), (VOID
*) string
,
429 if (suffixLength
!= 0) {
430 memcpy((VOID
*) (newPtr
->bytes
+ prefixLength
+ newChunkLength
),
431 (VOID
*) (linePtr
->bytes
+ ch
), suffixLength
);
433 newPtr
->bytes
[totalLength
] = 0;
434 changeToLineCount
+= 1;
437 * Quit after the suffix has been output (there is always at least
438 * one character of suffix: the newline). Before jumping out of the
439 * loop, put back the annotations that pertain to the suffix.
440 * Careful! If no newlines were inserted, there could already be
441 * annotations at the beginning of the line; add back to the end.
444 if (suffixLength
!= 0) {
445 if (newPtr
->annotPtr
== NULL
) {
446 newPtr
->annotPtr
= afterPtr
;
448 for (annotPtr
= newPtr
->annotPtr
; annotPtr
->nextPtr
!= NULL
;
449 annotPtr
= annotPtr
->nextPtr
) {
450 /* Empty loop body. */
452 annotPtr
->nextPtr
= afterPtr
;
454 for (annotPtr
= afterPtr
; annotPtr
!= NULL
;
455 annotPtr
= annotPtr
->nextPtr
) {
456 annotPtr
->linePtr
= newPtr
;
457 annotPtr
->ch
+= prefixLength
+newChunkLength
-ch
;
463 * Advance to insert the next line chunk.
466 string
+= newChunkLength
;
472 * Increment the line counts in all the parent nodes of the insertion
473 * point, then rebalance the tree if necessary.
476 for ( ; nodePtr
!= NULL
; nodePtr
= nodePtr
->parentPtr
) {
477 nodePtr
->numLines
+= changeToLineCount
;
479 nodePtr
= linePtr
->parentPtr
;
480 nodePtr
->numChildren
+= changeToLineCount
;
481 if (nodePtr
->numChildren
> MAX_CHILDREN
) {
482 Rebalance(treePtr
, nodePtr
);
485 ckfree((char *) linePtr
);
492 *----------------------------------------------------------------------
494 * TkBTreeDeleteChars --
496 * Delete a range of characters from a B-tree.
502 * Information is deleted from the B-tree. This can cause the
503 * internal structure of the B-tree to change. Note: the two
504 * lines given by line1Ptr and line2Ptr will be replaced with
505 * a single line containing the undeleted parts of the original
506 * lines. This could potentially result in an empty line;
507 * normally the caller should adjust the deletion range to prevent
508 * this sort of behavior.
510 *----------------------------------------------------------------------
515 TkTextBTree tree
, /* B-tree in which to delete. */
516 register TkTextLine
*line1Ptr
, /* Line containing first character
518 int ch1
, /* Index within linePtr1 of first
519 * character to delete. */
520 register TkTextLine
*line2Ptr
, /* Line containing character just
521 * after last one to delete. */
522 int ch2
/* Index within linePtr2 of character
523 * just after last one to delete. */
526 BTree
*treePtr
= (BTree
*) tree
;
527 TkTextLine
*linePtr
, *nextPtr
, *prevLinePtr
;
528 Node
*nodePtr
, *parentPtr
, *nextNodePtr
;
529 TkAnnotation
*annotPtr
, *annotPtr2
;
531 int linesDeleted
; /* Counts lines deleted from current
535 * Work through the tree deleting all of the lines between line1Ptr
536 * and line2Ptr (but don't delete line1Ptr or line2Ptr yet). Also
537 * delete any nodes in the B-tree that become empty because of
541 linePtr
= line1Ptr
->nextPtr
;
542 nodePtr
= line1Ptr
->parentPtr
;
543 if (line1Ptr
== line2Ptr
) {
544 goto middleLinesDeleted
;
549 * Delete all relevant lines within the same level-0 node.
553 while ((linePtr
!= line2Ptr
) && (linePtr
!= NULL
)) {
555 * Move any annotations in this line to the end of the
556 * deletion range. If both the starting and ending toggle
557 * for a tagged range get moved, they'll cancel each other
558 * automatically and be dropped, which is the right behavior.
561 for (annotPtr
= linePtr
->annotPtr
; annotPtr
!= NULL
;
562 annotPtr
= annotPtr2
) {
563 if (annotPtr
->type
== TK_ANNOT_TOGGLE
) {
564 AddToggleToLine(line2Ptr
, ch2
, annotPtr
->info
.tagPtr
);
565 ChangeNodeToggleCount(nodePtr
, annotPtr
->info
.tagPtr
, -1);
566 annotPtr2
= annotPtr
->nextPtr
;
567 ckfree((char *) annotPtr
);
569 annotPtr2
= annotPtr
->nextPtr
;
570 TkBTreeRemoveAnnotation(annotPtr
);
571 annotPtr
->linePtr
= line2Ptr
;
573 TkBTreeAddAnnotation(annotPtr
);
576 nextPtr
= linePtr
->nextPtr
;
577 ckfree((char *) linePtr
);
581 if (nodePtr
== line1Ptr
->parentPtr
) {
582 line1Ptr
->nextPtr
= linePtr
;
584 nodePtr
->children
.linePtr
= linePtr
;
586 for (parentPtr
= nodePtr
; parentPtr
!= NULL
;
587 parentPtr
= parentPtr
->parentPtr
) {
588 parentPtr
->numLines
-= linesDeleted
;
590 nodePtr
->numChildren
-= linesDeleted
;
591 if (linePtr
== line2Ptr
) {
596 * Find the next level-0 node to visit, and its first line (but
597 * remember the current node so we can come back to delete it if
601 nextNodePtr
= nodePtr
;
602 while (nextNodePtr
->nextPtr
== NULL
) {
603 nextNodePtr
= nextNodePtr
->parentPtr
;
605 nextNodePtr
= nextNodePtr
->nextPtr
;
606 while (nextNodePtr
->level
> 0) {
607 nextNodePtr
= nextNodePtr
->children
.nodePtr
;
609 linePtr
= nextNodePtr
->children
.linePtr
;
612 * Now go back to the node we just left and delete it if
613 * it's empty, along with any of its ancestors that are
614 * empty. It may seem funny to go back like this, but it's
615 * simpler to find the next place to visit before modifying
616 * the tree structure.
619 while (nodePtr
->numChildren
== 0) {
620 parentPtr
= nodePtr
->parentPtr
;
621 if (parentPtr
->children
.nodePtr
== nodePtr
) {
622 parentPtr
->children
.nodePtr
= nodePtr
->nextPtr
;
626 for (prevPtr
= parentPtr
->children
.nodePtr
;
627 prevPtr
->nextPtr
!= nodePtr
;
628 prevPtr
= prevPtr
->nextPtr
) {
630 prevPtr
->nextPtr
= nodePtr
->nextPtr
;
632 parentPtr
->numChildren
--;
633 DeleteSummaries(nodePtr
->summaryPtr
);
634 ckfree((char *) nodePtr
);
637 nodePtr
= nextNodePtr
;
641 * Make a new line that consists of the first part of the first
642 * line of the deletion range and the last part of the last line
643 * of the deletion range.
647 nodePtr
= line1Ptr
->parentPtr
;
648 linePtr
= (TkTextLine
*) ckalloc(LINE_SIZE(ch1
+ line2Ptr
->numBytes
- ch2
));
649 linePtr
->parentPtr
= nodePtr
;
650 linePtr
->nextPtr
= line1Ptr
->nextPtr
;
651 linePtr
->annotPtr
= NULL
;
652 linePtr
->numBytes
= ch1
+ line2Ptr
->numBytes
- ch2
;
654 memcpy((VOID
*) linePtr
->bytes
, (VOID
*) line1Ptr
->bytes
, ch1
);
656 strcpy(linePtr
->bytes
+ ch1
, line2Ptr
->bytes
+ ch2
);
659 * Process the annotations for the starting and ending lines. Enter
660 * a new annotation on linePtr (the joined line) for each of these
661 * annotations, then delete the originals. The code below is a little
662 * tricky (e.g. the "break" in the first loop) to handle the case where
663 * the starting and ending lines are the same.
666 for (annotPtr
= line1Ptr
->annotPtr
; annotPtr
!= NULL
;
667 annotPtr
= line1Ptr
->annotPtr
) {
668 if (annotPtr
->ch
<= ch1
) {
671 if (line1Ptr
== line2Ptr
) {
676 line1Ptr
->annotPtr
= annotPtr
->nextPtr
;
677 if (annotPtr
->type
== TK_ANNOT_TOGGLE
) {
678 AddToggleToLine(linePtr
, ch
, annotPtr
->info
.tagPtr
);
679 ChangeNodeToggleCount(line1Ptr
->parentPtr
, annotPtr
->info
.tagPtr
,
681 ckfree((char *) annotPtr
);
683 annotPtr
->linePtr
= linePtr
;
685 TkBTreeAddAnnotation(annotPtr
);
688 for (annotPtr
= line2Ptr
->annotPtr
; annotPtr
!= NULL
;
689 annotPtr
= line2Ptr
->annotPtr
) {
690 if (annotPtr
->ch
>= ch2
) {
691 ch
= annotPtr
->ch
- ch2
+ ch1
;
695 line2Ptr
->annotPtr
= annotPtr
->nextPtr
;
696 if (annotPtr
->type
== TK_ANNOT_TOGGLE
) {
697 AddToggleToLine(linePtr
, ch
, annotPtr
->info
.tagPtr
);
698 ChangeNodeToggleCount(line2Ptr
->parentPtr
, annotPtr
->info
.tagPtr
,
700 ckfree((char *) annotPtr
);
702 annotPtr
->linePtr
= linePtr
;
704 TkBTreeAddAnnotation(annotPtr
);
709 * Delete the original starting and stopping lines (don't forget
710 * that the annotations have already been deleted) and insert the
711 * new line in place of line1Ptr.
714 nodePtr
= line1Ptr
->parentPtr
;
715 if (nodePtr
->children
.linePtr
== line1Ptr
) {
716 nodePtr
->children
.linePtr
= linePtr
;
718 for (prevLinePtr
= nodePtr
->children
.linePtr
;
719 prevLinePtr
->nextPtr
!= line1Ptr
;
720 prevLinePtr
= prevLinePtr
->nextPtr
) {
721 /* Empty loop body. */
723 prevLinePtr
->nextPtr
= linePtr
;
725 ckfree((char *) line1Ptr
);
726 nodePtr
= line2Ptr
->parentPtr
;
727 if (line2Ptr
!= line1Ptr
) {
728 if (nodePtr
->children
.linePtr
== line2Ptr
) {
729 nodePtr
->children
.linePtr
= line2Ptr
->nextPtr
;
731 for (prevLinePtr
= nodePtr
->children
.linePtr
;
732 prevLinePtr
->nextPtr
!= line2Ptr
;
733 prevLinePtr
= prevLinePtr
->nextPtr
) {
734 /* Empty loop body. */
736 prevLinePtr
->nextPtr
= line2Ptr
->nextPtr
;
738 ckfree((char *) line2Ptr
);
739 for (parentPtr
= nodePtr
; parentPtr
!= NULL
;
740 parentPtr
= parentPtr
->parentPtr
) {
741 parentPtr
->numLines
--;
743 nodePtr
->numChildren
--;
747 * Rebalance the tree, starting from each of the endpoints of the
748 * deletion range. This code is a tricky, because the act of
749 * rebalancing the parent of one endpoint can cause the parent of
750 * the other endpoint to be reallocated. The only thing it's safe
751 * to hold onto is a pointer to a line. Thus, rebalance line2Ptr's
752 * parent first, then use linePtr find the second parent to rebalance
756 if (nodePtr
!= linePtr
->parentPtr
) {
757 Rebalance(treePtr
, nodePtr
);
759 Rebalance(treePtr
, linePtr
->parentPtr
);
766 *----------------------------------------------------------------------
770 * Turn a given tag on or off for a given range of characters in
777 * The given tag is added to the given range of characters
778 * in the tree or removed from all those characters, depending
779 * on the "add" argument.
781 *----------------------------------------------------------------------
786 TkTextBTree tree
, /* B-tree in which to add tag
789 int ch1
, /* Position of first character to
792 int ch2
, /* Position of character just after
793 * last one to tag. */
794 TkTextTag
*tagPtr
, /* Tag to associate with the range
796 int add
/* One means add tag to the given
797 * range of characters; zero means
798 * remove the tag from the range. */
801 BTree
*treePtr
= (BTree
*) tree
;
802 register TkTextLine
*line1Ptr
, *line2Ptr
;
807 * Find the lines containing the first and last characters to be tagged,
808 * and adjust the starting and stopping locations if they don't already
809 * point within lines. If the range would have started or stopped at the
810 * end of a line, round it up to the beginning of the next line (right
811 * now this restriction keeps the final newline from being tagged).
818 line1Ptr
= TkBTreeFindLine(tree
, line1
);
819 if (line1Ptr
== NULL
) {
822 if (ch1
>= line1Ptr
->numBytes
) {
823 TkTextLine
*nextLinePtr
;
825 nextLinePtr
= TkBTreeNextLine(line1Ptr
);
826 if (nextLinePtr
== NULL
) {
829 line1Ptr
= nextLinePtr
;
837 line2Ptr
= TkBTreeFindLine(tree
, line2
);
838 if (line2Ptr
== NULL
) {
839 line2Ptr
= TkBTreeFindLine(tree
, treePtr
->rootPtr
->numLines
-1);
840 ch2
= line2Ptr
->numBytes
-1;
842 if (ch2
>= line2Ptr
->numBytes
) {
843 TkTextLine
*nextLinePtr
;
845 nextLinePtr
= TkBTreeNextLine(line2Ptr
);
846 if (nextLinePtr
== NULL
) {
847 ch2
= line2Ptr
->numBytes
-1;
849 line2Ptr
= nextLinePtr
;
856 * See if the tag is already present or absent at the start of the
857 * range. If the state doesn't already match what we want then add
861 oldState
= TkBTreeCharTagged(line1Ptr
, ch1
, tagPtr
);
862 if ((add
!= 0) ^ oldState
) {
863 AddToggleToLine(line1Ptr
, ch1
, tagPtr
);
867 * Scan the range of characters covered by the change and delete
868 * any existing tag transitions except those on the first and
869 * last characters. Keep track of whether the old state just before
870 * the last character (not including any tags on it) is what we
871 * want now; if not, then add a tag toggle there.
874 TkBTreeStartSearch(tree
, line1
, ch1
+1, line2
, ch2
, tagPtr
, &search
);
875 while (TkBTreeNextTag(&search
)) {
876 if ((search
.linePtr
== line2Ptr
) && (search
.ch1
== ch2
)) {
880 AddToggleToLine(search
.linePtr
, search
.ch1
, tagPtr
);
882 if ((add
!= 0) ^ oldState
) {
883 AddToggleToLine(line2Ptr
, ch2
, tagPtr
);
892 *----------------------------------------------------------------------
894 * TkBTreeAddAnnotation --
896 * Given a filled in annotation, this procedure links it into
897 * a B-tree structure so that it will track changes to the B-tree.
903 * AnnotPtr will be linked into its tree. Note: the storage for
904 * annotPtr is assumed to have been malloc'ed by the caller.
906 *----------------------------------------------------------------------
911 TkBTreeAddAnnotation (
912 TkAnnotation
*annotPtr
/* Pointer to annotation. The caller must
913 * have filled in all the fields except the
914 * "nextPtr" field. The type should NOT be
915 * TK_ANNOT_TOGGLE; these annotations are
916 * managed by the TkBTreeTag procedure. */
919 register TkAnnotation
*annotPtr2
, *prevPtr
;
921 for (prevPtr
= NULL
, annotPtr2
= annotPtr
->linePtr
->annotPtr
;
923 prevPtr
= annotPtr2
, annotPtr2
= annotPtr2
->nextPtr
) {
924 if (annotPtr2
->ch
> annotPtr
->ch
) {
928 if (prevPtr
== NULL
) {
929 annotPtr
->nextPtr
= annotPtr
->linePtr
->annotPtr
;
930 annotPtr
->linePtr
->annotPtr
= annotPtr
;
932 annotPtr
->nextPtr
= prevPtr
->nextPtr
;
933 prevPtr
->nextPtr
= annotPtr
;
938 *----------------------------------------------------------------------
940 * TkBTreeRemoveAnnotation --
942 * This procedure unlinks an annotation from a B-tree so that
943 * the annotation will no longer be managed by the B-tree code.
949 * AnnotPtr will be unlinked from its tree. Note: it is up to the
950 * caller to free the storage for annotPtr, if that is desired.
952 *----------------------------------------------------------------------
957 TkBTreeRemoveAnnotation (
958 TkAnnotation
*annotPtr
/* Pointer to annotation, which must
959 * have been linked into tree by a previous
960 * call to TkBTreeAddAnnotation. */
963 register TkAnnotation
*prevPtr
;
965 if (annotPtr
->linePtr
->annotPtr
== annotPtr
) {
966 annotPtr
->linePtr
->annotPtr
= annotPtr
->nextPtr
;
968 for (prevPtr
= annotPtr
->linePtr
->annotPtr
;
969 /* BUG: fixed by dhopkins, prevPtr was null!
970 prevPtr->nextPtr != annotPtr;
972 (prevPtr
!= NULL
) && (prevPtr
->nextPtr
!= annotPtr
);
973 prevPtr
= prevPtr
->nextPtr
) {
974 /* Empty loop body. */
976 if (prevPtr
!= NULL
) { /* Bullet proofing by dhopkins */
977 prevPtr
->nextPtr
= annotPtr
->nextPtr
;
983 *----------------------------------------------------------------------
987 * Find a particular line in a B-tree based on its line number.
990 * The return value is a pointer to the line structure for the
991 * line whose index is "line", or NULL if no such line exists.
996 *----------------------------------------------------------------------
1001 TkTextBTree tree
, /* B-tree in which to find line. */
1002 int line
/* Index of desired line. */
1005 BTree
*treePtr
= (BTree
*) tree
;
1006 register Node
*nodePtr
;
1007 register TkTextLine
*linePtr
;
1010 nodePtr
= treePtr
->rootPtr
;
1012 if ((line
< 0) || (line
>= nodePtr
->numLines
)) {
1017 * Work down through levels of the tree until a node is found at
1021 while (nodePtr
->level
!= 0) {
1022 for (nodePtr
= nodePtr
->children
.nodePtr
;
1023 nodePtr
->numLines
<= linesLeft
;
1024 nodePtr
= nodePtr
->nextPtr
) {
1025 if (nodePtr
== NULL
) {
1026 panic("TkBTreeFindLine ran out of nodes");
1028 linesLeft
-= nodePtr
->numLines
;
1033 * Work through the lines attached to the level-0 node.
1036 for (linePtr
= nodePtr
->children
.linePtr
; linesLeft
> 0;
1037 linePtr
= linePtr
->nextPtr
) {
1038 if (linePtr
== NULL
) {
1039 panic("TkBTreeFindLine ran out of lines");
1047 *----------------------------------------------------------------------
1049 * TkBTreeNextLine --
1051 * Given an existing line in a B-tree, this procedure locates the
1052 * next line in the B-tree. This procedure is used for scanning
1053 * through the B-tree.
1056 * The return value is a pointer to the line that immediately
1057 * follows linePtr, or NULL if there is no such line.
1062 *----------------------------------------------------------------------
1067 register TkTextLine
*linePtr
/* Pointer to existing line in
1071 register Node
*nodePtr
;
1073 if (linePtr
->nextPtr
!= NULL
) {
1074 return linePtr
->nextPtr
;
1078 * This was the last line associated with the particular parent node.
1079 * Search up the tree for the next node, then search down from that
1080 * node to find the first line,
1083 for (nodePtr
= linePtr
->parentPtr
; ; nodePtr
= nodePtr
->parentPtr
) {
1084 if (nodePtr
->nextPtr
!= NULL
) {
1085 nodePtr
= nodePtr
->nextPtr
;
1088 if (nodePtr
->parentPtr
== NULL
) {
1089 return (TkTextLine
*) NULL
;
1092 while (nodePtr
->level
> 0) {
1093 nodePtr
= nodePtr
->children
.nodePtr
;
1095 return nodePtr
->children
.linePtr
;
1099 *----------------------------------------------------------------------
1101 * TkBTreeLineIndex --
1103 * Given a pointer to a line in a B-tree, return the numerical
1104 * index of that line.
1107 * The result is the index of linePtr within the tree, where 0
1108 * corresponds to the first line in the tree.
1113 *----------------------------------------------------------------------
1118 TkTextLine
*linePtr
/* Pointer to existing line in
1122 register TkTextLine
*linePtr2
;
1123 register Node
*nodePtr
, *parentPtr
, *nodePtr2
;
1127 * First count how many lines precede this one in its level-0
1131 nodePtr
= linePtr
->parentPtr
;
1133 for (linePtr2
= nodePtr
->children
.linePtr
; linePtr2
!= linePtr
;
1134 linePtr2
= linePtr2
->nextPtr
) {
1135 if (linePtr2
== NULL
) {
1136 panic("TkBTreeLineIndex couldn't find line");
1142 * Now work up through the levels of the tree one at a time,
1143 * counting how many lines are in nodes preceding the current
1147 for (parentPtr
= nodePtr
->parentPtr
; parentPtr
!= NULL
;
1148 nodePtr
= parentPtr
, parentPtr
= parentPtr
->parentPtr
) {
1149 for (nodePtr2
= parentPtr
->children
.nodePtr
; nodePtr2
!= nodePtr
;
1150 nodePtr2
= nodePtr2
->nextPtr
) {
1151 if (nodePtr2
== NULL
) {
1152 panic("TkBTreeLineIndex couldn't find node");
1154 index
+= nodePtr2
->numLines
;
1161 *----------------------------------------------------------------------
1163 * TkBTreeStartSearch --
1165 * This procedure sets up a search for tag transitions involving
1166 * a given tag (or all tags) in a given range of the text.
1172 * The information at *searchPtr is set up so that subsequent calls
1173 * to TkBTreeNextTag will return information about the locations of
1174 * tag transitions. Note that TkBTreeNextTag must be called to get
1175 * the first transition.
1177 *----------------------------------------------------------------------
1181 TkBTreeStartSearch (
1182 TkTextBTree tree
, /* Tree to search. */
1184 int ch1
, /* Character position at which to * start search (tags at this position
1185 * will be returned). */
1187 int ch2
, /* Character position at which to * stop search (tags at this position
1188 * will be returned). */
1189 TkTextTag
*tagPtr
, /* Tag to search for. NULL means
1190 * search for any tag. */
1191 register TkTextSearch
*searchPtr
/* Where to store information about
1192 * search's progress. */
1195 register TkAnnotation
*annotPtr
;
1197 searchPtr
->tree
= tree
;
1199 searchPtr
->line1
= 0;
1202 searchPtr
->line1
= line1
;
1203 searchPtr
->ch1
= ch1
;
1205 searchPtr
->line2
= line2
;
1206 searchPtr
->ch2
= ch2
;
1207 searchPtr
->tagPtr
= tagPtr
;
1208 searchPtr
->allTags
= (tagPtr
== NULL
);
1210 searchPtr
->linePtr
= TkBTreeFindLine(searchPtr
->tree
, searchPtr
->line1
);
1211 if (searchPtr
->linePtr
== NULL
) {
1212 searchPtr
->line1
= searchPtr
->line2
;
1213 searchPtr
->ch1
= searchPtr
->ch2
;
1214 searchPtr
->annotPtr
= NULL
;
1216 for (annotPtr
= searchPtr
->linePtr
->annotPtr
;
1217 (annotPtr
!= NULL
) && (annotPtr
->ch
< ch1
);
1218 annotPtr
= annotPtr
->nextPtr
) {
1219 /* Empty loop body. */
1221 searchPtr
->annotPtr
= annotPtr
;
1226 *----------------------------------------------------------------------
1230 * Once a tag search has begun, successive calls to this procedure
1231 * return successive tag toggles. Note: it is NOT SAFE to call this
1232 * procedure if characters have been inserted into or deleted from
1233 * the B-tree since the call to TkBTreeStartSearch.
1236 * The return value is 1 if another toggle was found that met the
1237 * criteria specified in the call to TkBTreeStartSearch. 0 is
1238 * returned if no more matching tag transitions were found.
1241 * Information in *searchPtr is modified to update the state of the
1242 * search and indicate where the next tag toggle is located.
1244 *----------------------------------------------------------------------
1249 register TkTextSearch
*searchPtr
/* Information about search in
1250 * progress; must have been set up by
1251 * call to TkBTreeStartSearch. */
1254 register TkAnnotation
*annotPtr
;
1255 register Node
*nodePtr
;
1256 register Summary
*summaryPtr
;
1258 if (searchPtr
->linePtr
== NULL
) {
1263 * The outermost loop iterates over lines that may potentially contain
1264 * a relevant tag transition, starting from the current line and tag.
1269 * See if there are more tags on the current line that are relevant.
1272 for (annotPtr
= searchPtr
->annotPtr
; annotPtr
!= NULL
;
1273 annotPtr
= annotPtr
->nextPtr
) {
1274 if ((annotPtr
->type
== TK_ANNOT_TOGGLE
)
1275 && (searchPtr
->allTags
1276 || (annotPtr
->info
.tagPtr
== searchPtr
->tagPtr
))) {
1277 if ((searchPtr
->line1
== searchPtr
->line2
)
1278 && (annotPtr
->ch
> searchPtr
->ch2
)) {
1281 searchPtr
->tagPtr
= annotPtr
->info
.tagPtr
;
1282 searchPtr
->ch1
= annotPtr
->ch
;
1283 searchPtr
->annotPtr
= annotPtr
->nextPtr
;
1289 * See if there are more lines associated with the current parent
1290 * node. If so, go back to the top of the loop to search the next
1294 if (searchPtr
->line1
>= searchPtr
->line2
) {
1298 if (searchPtr
->linePtr
->nextPtr
!= NULL
) {
1299 searchPtr
->linePtr
= searchPtr
->linePtr
->nextPtr
;
1300 searchPtr
->annotPtr
= searchPtr
->linePtr
->annotPtr
;
1305 * Search across and up through the B-tree's node hierarchy looking
1306 * for the next node that has a relevant tag transition somewhere in
1307 * its subtree. Be sure to update the current line number as we
1308 * skip over large chunks of lines.
1311 nodePtr
= searchPtr
->linePtr
->parentPtr
;
1313 while (nodePtr
->nextPtr
== NULL
) {
1314 if (nodePtr
->parentPtr
== NULL
) {
1317 nodePtr
= nodePtr
->parentPtr
;
1319 nodePtr
= nodePtr
->nextPtr
;
1320 for (summaryPtr
= nodePtr
->summaryPtr
; summaryPtr
!= NULL
;
1321 summaryPtr
= summaryPtr
->nextPtr
) {
1322 if ((searchPtr
->allTags
) ||
1323 (summaryPtr
->tagPtr
== searchPtr
->tagPtr
)) {
1324 goto gotNodeWithTag
;
1327 searchPtr
->line1
+= nodePtr
->numLines
;
1331 * At this point we've found a subtree that has a relevant tag
1332 * transition. Now search down (and across) through that subtree
1333 * to find the first level-0 node that has a relevant tag transition.
1337 while (nodePtr
->level
> 0) {
1338 for (nodePtr
= nodePtr
->children
.nodePtr
; ;
1339 nodePtr
= nodePtr
->nextPtr
) {
1340 for (summaryPtr
= nodePtr
->summaryPtr
; summaryPtr
!= NULL
;
1341 summaryPtr
= summaryPtr
->nextPtr
) {
1342 if ((searchPtr
->allTags
)
1343 || (summaryPtr
->tagPtr
== searchPtr
->tagPtr
)) {
1347 searchPtr
->line1
+= nodePtr
->numLines
;
1348 if (nodePtr
->nextPtr
== NULL
) {
1349 panic("TkBTreeNextTag found incorrect tag summary info.");
1357 * Now we're down to a level-0 node that contains a line that contains
1358 * a relevant tag transition. Set up line information and go back to
1359 * the beginning of the loop to search through lines.
1362 searchPtr
->linePtr
= nodePtr
->children
.linePtr
;
1363 searchPtr
->annotPtr
= searchPtr
->linePtr
->annotPtr
;
1364 if (searchPtr
->line1
> searchPtr
->line2
) {
1371 searchPtr
->line1
= searchPtr
->line2
;
1372 searchPtr
->ch1
= searchPtr
->ch2
;
1373 searchPtr
->annotPtr
= NULL
;
1374 searchPtr
->linePtr
= NULL
;
1379 *----------------------------------------------------------------------
1383 * This procedure runs a set of consistency checks over a B-tree
1384 * and panics if any inconsistencies are found.
1390 * If a structural defect is found, the procedure panics with an
1393 *----------------------------------------------------------------------
1398 TkTextBTree tree
/* Tree to check. */
1401 BTree
*treePtr
= (BTree
*) tree
;
1402 register Summary
*summaryPtr
;
1405 * Make sure that overall there is an even count of tag transitions
1406 * for the whole text.
1409 for (summaryPtr
= treePtr
->rootPtr
->summaryPtr
; summaryPtr
!= NULL
;
1410 summaryPtr
= summaryPtr
->nextPtr
) {
1411 if (summaryPtr
->toggleCount
& 1) {
1412 panic("TkBTreeCheck found odd toggle count for \"%s\" (%d)",
1413 summaryPtr
->tagPtr
->name
, summaryPtr
->toggleCount
);
1418 * Call a recursive procedure to do all of the rest of the checks.
1421 CheckNodeConsistency(treePtr
->rootPtr
);
1425 *----------------------------------------------------------------------
1429 * This procedure is called when a node of a B-tree appears to be
1430 * out of balance (too many children, or too few). It rebalances
1431 * that node and all of its ancestors in the tree.
1437 * The internal structure of treePtr may change.
1439 *----------------------------------------------------------------------
1444 BTree
*treePtr
, /* Tree that is being rebalanced. */
1445 register Node
*nodePtr
/* Node that may be out of balance. */
1449 * Loop over the entire ancestral chain of the node, working up
1450 * through the tree one node at a time until the root node has
1454 for ( ; nodePtr
!= NULL
; nodePtr
= nodePtr
->parentPtr
) {
1455 register Node
*newPtr
, *childPtr
;
1456 register TkTextLine
*linePtr
;
1460 * Check to see if the node has too many children. If it does,
1461 * then split off all but the first MIN_CHILDREN into a separate
1462 * node following the original one. Then repeat until the
1463 * node has a decent size.
1466 if (nodePtr
->numChildren
> MAX_CHILDREN
) {
1469 * If the node being split is the root node, then make a
1470 * new root node above it first.
1473 if (nodePtr
->parentPtr
== NULL
) {
1474 newPtr
= (Node
*) ckalloc(sizeof(Node
));
1475 newPtr
->parentPtr
= NULL
;
1476 newPtr
->nextPtr
= NULL
;
1477 newPtr
->summaryPtr
= NULL
;
1478 newPtr
->level
= nodePtr
->level
+ 1;
1479 newPtr
->children
.nodePtr
= nodePtr
;
1480 newPtr
->numChildren
= 1;
1481 newPtr
->numLines
= nodePtr
->numLines
;
1482 RecomputeNodeCounts(newPtr
);
1483 treePtr
->rootPtr
= newPtr
;
1485 newPtr
= (Node
*) ckalloc(sizeof(Node
));
1486 newPtr
->parentPtr
= nodePtr
->parentPtr
;
1487 newPtr
->nextPtr
= nodePtr
->nextPtr
;
1488 nodePtr
->nextPtr
= newPtr
;
1489 newPtr
->summaryPtr
= NULL
;
1490 newPtr
->level
= nodePtr
->level
;
1491 newPtr
->numChildren
= nodePtr
->numChildren
- MIN_CHILDREN
;
1492 if (nodePtr
->level
== 0) {
1493 for (i
= MIN_CHILDREN
-1,
1494 linePtr
= nodePtr
->children
.linePtr
;
1495 i
> 0; i
--, linePtr
= linePtr
->nextPtr
) {
1496 /* Empty loop body. */
1498 newPtr
->children
.linePtr
= linePtr
->nextPtr
;
1499 linePtr
->nextPtr
= NULL
;
1501 for (i
= MIN_CHILDREN
-1,
1502 childPtr
= nodePtr
->children
.nodePtr
;
1503 i
> 0; i
--, childPtr
= childPtr
->nextPtr
) {
1504 /* Empty loop body. */
1506 newPtr
->children
.nodePtr
= childPtr
->nextPtr
;
1507 childPtr
->nextPtr
= NULL
;
1509 RecomputeNodeCounts(nodePtr
);
1510 nodePtr
->parentPtr
->numChildren
++;
1512 if (nodePtr
->numChildren
<= MAX_CHILDREN
) {
1513 RecomputeNodeCounts(nodePtr
);
1519 while (nodePtr
->numChildren
< MIN_CHILDREN
) {
1520 register Node
*otherPtr
;
1521 Node
*halfwayNodePtr
= NULL
; /* Initialization needed only */
1522 TkTextLine
*halfwayLinePtr
= NULL
; /* to prevent cc warnings. */
1523 int totalChildren
, firstChildren
, i
;
1526 * Too few children for this node. If this is the root,
1527 * it's OK for it to have less than MIN_CHILDREN children
1528 * as long as it's got at least two. If it has only one
1529 * (and isn't at level 0), then chop the root node out of
1530 * the tree and use its child as the new root.
1533 if (nodePtr
->parentPtr
== NULL
) {
1534 if ((nodePtr
->numChildren
== 1) && (nodePtr
->level
> 0)) {
1535 treePtr
->rootPtr
= nodePtr
->children
.nodePtr
;
1536 treePtr
->rootPtr
->parentPtr
= NULL
;
1537 DeleteSummaries(nodePtr
->summaryPtr
);
1538 ckfree((char *) nodePtr
);
1544 * Not the root. Make sure that there are siblings to
1548 if (nodePtr
->parentPtr
->numChildren
< 2) {
1549 Rebalance(treePtr
, nodePtr
->parentPtr
);
1554 * Find a sibling to borrow from, and arrange for nodePtr to
1555 * be the earlier of the pair.
1558 if (nodePtr
->nextPtr
== NULL
) {
1559 for (otherPtr
= nodePtr
->parentPtr
->children
.nodePtr
;
1560 otherPtr
->nextPtr
!= nodePtr
;
1561 otherPtr
= otherPtr
->nextPtr
) {
1562 /* Empty loop body. */
1566 otherPtr
= nodePtr
->nextPtr
;
1569 * We're going to either merge the two siblings together
1570 * into one node or redivide the children among them to
1571 * balance their loads. As preparation, join their two
1572 * child lists into a single list and remember the half-way
1573 * point in the list.
1576 totalChildren
= nodePtr
->numChildren
+ otherPtr
->numChildren
;
1577 firstChildren
= totalChildren
/2;
1578 if (nodePtr
->children
.nodePtr
== NULL
) {
1579 nodePtr
->children
= otherPtr
->children
;
1580 } else if (nodePtr
->level
== 0) {
1581 register TkTextLine
*linePtr
;
1583 for (linePtr
= nodePtr
->children
.linePtr
, i
= 1;
1584 linePtr
->nextPtr
!= NULL
;
1585 linePtr
= linePtr
->nextPtr
, i
++) {
1586 if (i
== firstChildren
) {
1587 halfwayLinePtr
= linePtr
;
1590 linePtr
->nextPtr
= otherPtr
->children
.linePtr
;
1591 while (i
<= firstChildren
) {
1592 halfwayLinePtr
= linePtr
;
1593 linePtr
= linePtr
->nextPtr
;
1597 register Node
*childPtr
;
1599 for (childPtr
= nodePtr
->children
.nodePtr
, i
= 1;
1600 childPtr
->nextPtr
!= NULL
;
1601 childPtr
= childPtr
->nextPtr
, i
++) {
1602 if (i
<= firstChildren
) {
1603 if (i
== firstChildren
) {
1604 halfwayNodePtr
= childPtr
;
1608 childPtr
->nextPtr
= otherPtr
->children
.nodePtr
;
1609 while (i
<= firstChildren
) {
1610 halfwayNodePtr
= childPtr
;
1611 childPtr
= childPtr
->nextPtr
;
1617 * If the two siblings can simply be merged together, do it.
1620 if (totalChildren
< MAX_CHILDREN
) {
1621 RecomputeNodeCounts(nodePtr
);
1622 nodePtr
->nextPtr
= otherPtr
->nextPtr
;
1623 nodePtr
->parentPtr
->numChildren
--;
1624 DeleteSummaries(otherPtr
->summaryPtr
);
1625 ckfree((char *) otherPtr
);
1630 * The siblings can't be merged, so just divide their
1631 * children evenly between them.
1634 if (nodePtr
->level
== 0) {
1635 otherPtr
->children
.linePtr
= halfwayLinePtr
->nextPtr
;
1636 halfwayLinePtr
->nextPtr
= NULL
;
1638 otherPtr
->children
.nodePtr
= halfwayNodePtr
->nextPtr
;
1639 halfwayNodePtr
->nextPtr
= NULL
;
1641 RecomputeNodeCounts(nodePtr
);
1642 RecomputeNodeCounts(otherPtr
);
1648 *----------------------------------------------------------------------
1650 * RecomputeNodeCounts --
1652 * This procedure is called to recompute all the counts in a node
1653 * (tags, child information, etc.) by scaning the information in
1654 * its descendants. This procedure is called during rebalancing
1655 * when a node's child structure has changed.
1661 * The tag counts for nodePtr are modified to reflect its current
1662 * child structure, as are its numChildren and numLines fields.
1663 * Also, all of the children's parentPtr fields are made to point
1666 *----------------------------------------------------------------------
1670 RecomputeNodeCounts (
1671 register Node
*nodePtr
/* Node whose tag summary information
1672 * must be recomputed. */
1675 register Summary
*summaryPtr
, *summaryPtr2
;
1676 register Node
*childPtr
;
1677 register TkTextLine
*linePtr
;
1678 register TkAnnotation
*annotPtr
;
1681 * Zero out all the existing counts for the node, but don't delete
1682 * the existing Summary records (most of them will probably be reused).
1685 for (summaryPtr
= nodePtr
->summaryPtr
; summaryPtr
!= NULL
;
1686 summaryPtr
= summaryPtr
->nextPtr
) {
1687 summaryPtr
->toggleCount
= 0;
1689 nodePtr
->numChildren
= 0;
1690 nodePtr
->numLines
= 0;
1693 * Scan through the children, adding the childrens' tag counts into
1694 * the node's tag counts and adding new Summarys to the node if
1698 if (nodePtr
->level
== 0) {
1699 for (linePtr
= nodePtr
->children
.linePtr
; linePtr
!= NULL
;
1700 linePtr
= linePtr
->nextPtr
) {
1701 nodePtr
->numChildren
++;
1702 nodePtr
->numLines
++;
1703 linePtr
->parentPtr
= nodePtr
;
1704 for (annotPtr
= linePtr
->annotPtr
; annotPtr
!= NULL
;
1705 annotPtr
= annotPtr
->nextPtr
) {
1706 if (annotPtr
->type
!= TK_ANNOT_TOGGLE
) {
1709 for (summaryPtr
= nodePtr
->summaryPtr
; ;
1710 summaryPtr
= summaryPtr
->nextPtr
) {
1711 if (summaryPtr
== NULL
) {
1712 summaryPtr
= (Summary
*) ckalloc(sizeof(Summary
));
1713 summaryPtr
->tagPtr
= annotPtr
->info
.tagPtr
;
1714 summaryPtr
->toggleCount
= 1;
1715 summaryPtr
->nextPtr
= nodePtr
->summaryPtr
;
1716 nodePtr
->summaryPtr
= summaryPtr
;
1719 if (summaryPtr
->tagPtr
== annotPtr
->info
.tagPtr
) {
1720 summaryPtr
->toggleCount
++;
1727 for (childPtr
= nodePtr
->children
.nodePtr
; childPtr
!= NULL
;
1728 childPtr
= childPtr
->nextPtr
) {
1729 nodePtr
->numChildren
++;
1730 nodePtr
->numLines
+= childPtr
->numLines
;
1731 childPtr
->parentPtr
= nodePtr
;
1732 for (summaryPtr2
= childPtr
->summaryPtr
; summaryPtr2
!= NULL
;
1733 summaryPtr2
= summaryPtr2
->nextPtr
) {
1734 for (summaryPtr
= nodePtr
->summaryPtr
; ;
1735 summaryPtr
= summaryPtr
->nextPtr
) {
1736 if (summaryPtr
== NULL
) {
1737 summaryPtr
= (Summary
*) ckalloc(sizeof(Summary
));
1738 summaryPtr
->tagPtr
= summaryPtr2
->tagPtr
;
1739 summaryPtr
->toggleCount
= summaryPtr2
->toggleCount
;
1740 summaryPtr
->nextPtr
= nodePtr
->summaryPtr
;
1741 nodePtr
->summaryPtr
= summaryPtr
;
1744 if (summaryPtr
->tagPtr
== summaryPtr2
->tagPtr
) {
1745 summaryPtr
->toggleCount
+= summaryPtr2
->toggleCount
;
1754 * Scan through the node's tag records again and delete any Summary
1755 * records that still have a zero count.
1759 for (summaryPtr
= nodePtr
->summaryPtr
; summaryPtr
!= NULL
; ) {
1760 if (summaryPtr
->toggleCount
> 0) {
1761 summaryPtr2
= summaryPtr
;
1762 summaryPtr
= summaryPtr
->nextPtr
;
1765 if (summaryPtr2
!= NULL
) {
1766 summaryPtr2
->nextPtr
= summaryPtr
->nextPtr
;
1767 ckfree((char *) summaryPtr
);
1768 summaryPtr
= summaryPtr2
->nextPtr
;
1770 nodePtr
->summaryPtr
= summaryPtr
->nextPtr
;
1771 ckfree((char *) summaryPtr
);
1772 summaryPtr
= nodePtr
->summaryPtr
;
1778 *----------------------------------------------------------------------
1780 * AddToggleToLine --
1782 * Insert a tag transition at a particular point in a particular
1789 * LinePtr and all its ancestors in the B-tree stucture are modified
1790 * to indicate the presence of a transition (either on or off) on
1791 * tag at the given place in the given line.
1793 *----------------------------------------------------------------------
1798 TkTextLine
*linePtr
, /* Line within which to add
1800 int index
, /* Character before which to
1801 * add transition. */
1802 TkTextTag
*tagPtr
/* Information about tag. */
1805 register TkAnnotation
*annotPtr
, *prevPtr
;
1809 * Find the position where the toggle should be inserted into
1810 * the array (just after prevPtr), and see if there is already
1811 * a toggle at exactly the point where we're going to insert a
1812 * new toggle. If so then the two toggles cancel; just delete
1813 * the existing toggle.
1816 for (prevPtr
= NULL
, annotPtr
= linePtr
->annotPtr
; annotPtr
!= NULL
;
1817 prevPtr
= annotPtr
, annotPtr
= annotPtr
->nextPtr
) {
1818 if (annotPtr
->ch
> index
) {
1821 if ((annotPtr
->type
== TK_ANNOT_TOGGLE
)
1822 && (annotPtr
->ch
== index
)
1823 && (annotPtr
->info
.tagPtr
== tagPtr
)) {
1824 if (prevPtr
== NULL
) {
1825 linePtr
->annotPtr
= annotPtr
->nextPtr
;
1827 prevPtr
->nextPtr
= annotPtr
->nextPtr
;
1829 ckfree((char *) annotPtr
);
1836 * Create a new toggle and insert it into the list.
1839 annotPtr
= (TkAnnotation
*) ckalloc(sizeof(TkAnnotation
));
1840 annotPtr
->type
= TK_ANNOT_TOGGLE
;
1841 annotPtr
->linePtr
= linePtr
;
1842 annotPtr
->ch
= index
;
1843 annotPtr
->info
.tagPtr
= tagPtr
;
1844 if (prevPtr
== NULL
) {
1845 annotPtr
->nextPtr
= linePtr
->annotPtr
;
1846 linePtr
->annotPtr
= annotPtr
;
1848 annotPtr
->nextPtr
= prevPtr
->nextPtr
;
1849 prevPtr
->nextPtr
= annotPtr
;
1853 * Update all the nodes above this line to reflect the change in
1858 ChangeNodeToggleCount(linePtr
->parentPtr
, tagPtr
, delta
);
1862 *----------------------------------------------------------------------
1864 * ChangeNodeToggleCount --
1866 * This procedure increments or decrements the toggle count for
1867 * a particular tag in a particular node and all its ancestors.
1873 * The toggle count for tag is adjusted up or down by "delta" in
1876 *----------------------------------------------------------------------
1880 ChangeNodeToggleCount (
1881 register Node
*nodePtr
, /* Node whose toggle count for a tag
1882 * must be changed. */
1883 TkTextTag
*tagPtr
, /* Information about tag. */
1884 int delta
/* Amount to add to current toggle
1885 * count for tag (may be negative). */
1888 register Summary
*summaryPtr
, *prevPtr
;
1891 * Iterate over the node and all of its ancestors.
1894 for ( ; nodePtr
!= NULL
; nodePtr
= nodePtr
->parentPtr
) {
1896 * See if there's already an entry for this tag for this node. If so,
1897 * perhaps all we have to do is adjust its count.
1900 for (prevPtr
= NULL
, summaryPtr
= nodePtr
->summaryPtr
;
1902 prevPtr
= summaryPtr
, summaryPtr
= summaryPtr
->nextPtr
) {
1903 if (summaryPtr
->tagPtr
!= tagPtr
) {
1906 summaryPtr
->toggleCount
+= delta
;
1907 if (summaryPtr
->toggleCount
> 0) {
1910 if (summaryPtr
->toggleCount
< 0) {
1911 panic("ChangeNodeToggleCount: negative toggle count");
1915 * Zero count; must remove this tag from the list.
1918 if (prevPtr
== NULL
) {
1919 nodePtr
->summaryPtr
= summaryPtr
->nextPtr
;
1921 prevPtr
->nextPtr
= summaryPtr
->nextPtr
;
1923 ckfree((char *) summaryPtr
);
1928 * This tag isn't in the list. Add a new entry to the list.
1932 panic("ChangeNodeToggleCount: negative delta, no tag entry");
1934 summaryPtr
= (Summary
*) ckalloc(sizeof(Summary
));
1935 summaryPtr
->tagPtr
= tagPtr
;
1936 summaryPtr
->toggleCount
= delta
;
1937 summaryPtr
->nextPtr
= nodePtr
->summaryPtr
;
1938 nodePtr
->summaryPtr
= summaryPtr
;
1946 *----------------------------------------------------------------------
1948 * TkBTreeCharTagged --
1950 * Determine whether a particular character has a particular tag.
1953 * The return value is 1 if the given tag is in effect at the
1954 * character given by linePtr and ch, and 0 otherwise.
1959 *----------------------------------------------------------------------
1964 TkTextLine
*linePtr
, /* Line containing character of
1966 int ch
, /* Index of character in linePtr. */
1967 TkTextTag
*tagPtr
/* Tag of interest. */
1970 register Node
*nodePtr
;
1971 register TkTextLine
*siblingLinePtr
;
1975 * Count the number of toggles for the tag at the line level (i.e.
1976 * in all the sibling lines that precede this one, plus in this line
1977 * up to the character of interest.
1981 for (siblingLinePtr
= linePtr
->parentPtr
->children
.linePtr
; ;
1982 siblingLinePtr
= siblingLinePtr
->nextPtr
) {
1983 register TkAnnotation
*annotPtr
;
1985 for (annotPtr
= siblingLinePtr
->annotPtr
;
1986 (annotPtr
!= NULL
) && ((siblingLinePtr
!= linePtr
)
1987 || (annotPtr
->ch
<= ch
));
1988 annotPtr
= annotPtr
->nextPtr
) {
1989 if ((annotPtr
->type
== TK_ANNOT_TOGGLE
)
1990 && (annotPtr
->info
.tagPtr
== tagPtr
)) {
1994 if (siblingLinePtr
== linePtr
) {
2000 * For each node in the ancestry of this line, count the number of
2001 * toggles of the given tag in siblings that precede that node.
2004 for (nodePtr
= linePtr
->parentPtr
; nodePtr
->parentPtr
!= NULL
;
2005 nodePtr
= nodePtr
->parentPtr
) {
2006 register Node
*siblingPtr
;
2007 register Summary
*summaryPtr
;
2009 for (siblingPtr
= nodePtr
->parentPtr
->children
.nodePtr
;
2010 siblingPtr
!= nodePtr
; siblingPtr
= siblingPtr
->nextPtr
) {
2011 for (summaryPtr
= siblingPtr
->summaryPtr
; summaryPtr
!= NULL
;
2012 summaryPtr
= summaryPtr
->nextPtr
) {
2013 if (summaryPtr
->tagPtr
== tagPtr
) {
2014 toggles
+= summaryPtr
->toggleCount
;
2021 * An odd number of toggles means that the tag is present at the
2029 *----------------------------------------------------------------------
2033 * Return information about all of the tags that are associated
2034 * with a particular character in a B-tree of text.
2037 * The return value is a malloc-ed array containing pointers to
2038 * information for each of the tags that is associated with
2039 * the character at the position given by linePtr and ch. The
2040 * word at *numTagsPtr is filled in with the number of pointers
2041 * in the array. It is up to the caller to free the array by
2042 * passing it to free. If there are no tags at the given character
2043 * then a NULL pointer is returned and *numTagsPtr will be set to 0.
2048 *----------------------------------------------------------------------
2054 TkTextBTree tree
, /* Tree to check. */
2055 TkTextLine
*linePtr
, /* Line containing character of interest. */
2056 int ch
, /* Index within linePtr of character for
2057 * which tag information is wanted. */
2058 int *numTagsPtr
/* Store number of tags found at this
2062 register Node
*nodePtr
;
2063 register TkTextLine
*siblingLinePtr
;
2066 #define NUM_TAG_INFOS 10
2068 tagInfo
.numTags
= 0;
2069 tagInfo
.arraySize
= NUM_TAG_INFOS
;
2070 tagInfo
.tagPtrs
= (TkTextTag
**) ckalloc((unsigned)
2071 NUM_TAG_INFOS
*sizeof(TkTextTag
*));
2072 tagInfo
.counts
= (int *) ckalloc((unsigned)
2073 NUM_TAG_INFOS
*sizeof(int));
2076 * Record tag toggles at the line level (i.e. in all the sibling
2077 * lines that precede this one, plus in this line up to the character
2081 for (siblingLinePtr
= linePtr
->parentPtr
->children
.linePtr
; ;
2082 siblingLinePtr
= siblingLinePtr
->nextPtr
) {
2083 register TkAnnotation
*annotPtr
;
2085 for (annotPtr
= siblingLinePtr
->annotPtr
;
2086 (annotPtr
!= NULL
) && ((siblingLinePtr
!= linePtr
)
2087 || (annotPtr
->ch
<= ch
));
2088 annotPtr
= annotPtr
->nextPtr
) {
2089 if (annotPtr
->type
== TK_ANNOT_TOGGLE
) {
2090 IncCount(annotPtr
->info
.tagPtr
, 1, &tagInfo
);
2093 if (siblingLinePtr
== linePtr
) {
2099 * For each node in the ancestry of this line, record tag toggles
2100 * for all siblings that precede that node.
2103 for (nodePtr
= linePtr
->parentPtr
; nodePtr
->parentPtr
!= NULL
;
2104 nodePtr
= nodePtr
->parentPtr
) {
2105 register Node
*siblingPtr
;
2106 register Summary
*summaryPtr
;
2108 for (siblingPtr
= nodePtr
->parentPtr
->children
.nodePtr
;
2109 siblingPtr
!= nodePtr
; siblingPtr
= siblingPtr
->nextPtr
) {
2110 for (summaryPtr
= siblingPtr
->summaryPtr
; summaryPtr
!= NULL
;
2111 summaryPtr
= summaryPtr
->nextPtr
) {
2112 IncCount(summaryPtr
->tagPtr
, summaryPtr
->toggleCount
, &tagInfo
);
2118 * Go through the tag information and squash out all of the tags
2119 * that have even toggle counts (these tags exist before the point
2120 * of interest, but not at the desired character itself).
2123 for (src
= 0, dst
= 0; src
< tagInfo
.numTags
; src
++) {
2124 if (tagInfo
.counts
[src
] & 1) {
2125 tagInfo
.tagPtrs
[dst
] = tagInfo
.tagPtrs
[src
];
2130 ckfree((char *) tagInfo
.counts
);
2132 ckfree((char *) tagInfo
.tagPtrs
);
2135 return tagInfo
.tagPtrs
;
2139 *----------------------------------------------------------------------
2143 * This is a utility procedure used by TkBTreeGetTags. It
2144 * increments the count for a particular tag, adding a new
2145 * entry for that tag if there wasn't one previously.
2151 * The information at *tagInfoPtr may be modified, and the arrays
2152 * may be reallocated to make them larger.
2154 *----------------------------------------------------------------------
2159 TkTextTag
*tagPtr
, /* Handle for tag. */
2160 int inc
, /* Amount by which to increment tag count. */
2161 TagInfo
*tagInfoPtr
/* Holds cumulative information about tags;
2162 * increment count here. */
2165 register TkTextTag
**tagPtrPtr
;
2168 for (tagPtrPtr
= tagInfoPtr
->tagPtrs
, count
= tagInfoPtr
->numTags
;
2169 count
> 0; tagPtrPtr
++, count
--) {
2170 if (*tagPtrPtr
== tagPtr
) {
2171 tagInfoPtr
->counts
[tagInfoPtr
->numTags
-count
] += inc
;
2177 * There isn't currently an entry for this tag, so we have to
2178 * make a new one. If the arrays are full, then enlarge the
2182 if (tagInfoPtr
->numTags
== tagInfoPtr
->arraySize
) {
2183 TkTextTag
**newTags
;
2184 int *newCounts
, newSize
;
2186 newSize
= 2*tagInfoPtr
->arraySize
;
2187 newTags
= (TkTextTag
**) ckalloc((unsigned)
2188 (newSize
*sizeof(TkTextTag
*)));
2189 memcpy((VOID
*) newTags
, (VOID
*) tagInfoPtr
->tagPtrs
,
2190 tagInfoPtr
->arraySize
* sizeof(TkTextTag
*));
2191 ckfree((char *) tagInfoPtr
->tagPtrs
);
2192 tagInfoPtr
->tagPtrs
= newTags
;
2193 newCounts
= (int *) ckalloc((unsigned) (newSize
*sizeof(int)));
2194 memcpy((VOID
*) newCounts
, (VOID
*) tagInfoPtr
->counts
,
2195 tagInfoPtr
->arraySize
* sizeof(int));
2196 ckfree((char *) tagInfoPtr
->counts
);
2197 tagInfoPtr
->counts
= newCounts
;
2198 tagInfoPtr
->arraySize
= newSize
;
2201 tagInfoPtr
->tagPtrs
[tagInfoPtr
->numTags
] = tagPtr
;
2202 tagInfoPtr
->counts
[tagInfoPtr
->numTags
] = inc
;
2203 tagInfoPtr
->numTags
++;
2207 *----------------------------------------------------------------------
2209 * CheckNodeConsistency --
2211 * This procedure is called as part of consistency checking for
2212 * B-trees: it checks several aspects of a node and also runs
2213 * checks recursively on the node's children.
2219 * If anything suspicious is found in the tree structure, the
2222 *----------------------------------------------------------------------
2226 CheckNodeConsistency (
2227 register Node
*nodePtr
/* Node whose subtree should be
2231 register Node
*childNodePtr
;
2232 register Summary
*summaryPtr
, *summaryPtr2
;
2233 register TkAnnotation
*annotPtr
;
2234 register TkTextLine
*linePtr
;
2236 int numChildren
, numLines
, toggleCount
, minChildren
, index
, numBytes
;
2238 if (nodePtr
->parentPtr
!= NULL
) {
2239 minChildren
= MIN_CHILDREN
;
2240 } else if (nodePtr
->level
> 0) {
2245 if ((nodePtr
->numChildren
< minChildren
)
2246 || (nodePtr
->numChildren
> MAX_CHILDREN
)) {
2247 panic("CheckNodeConsistency found bad child count (%d)",
2248 nodePtr
->numChildren
);
2253 if (nodePtr
->level
== 0) {
2254 for (linePtr
= nodePtr
->children
.linePtr
; linePtr
!= NULL
;
2255 linePtr
= linePtr
->nextPtr
) {
2256 if (linePtr
->parentPtr
!= nodePtr
) {
2257 panic("CheckNodeConsistency found line that %s",
2258 "didn't point to parent");
2260 for (p
= linePtr
->bytes
, numBytes
= 0; *p
!= 0; p
++, numBytes
++) {
2261 if ((*p
== '\n') && (numBytes
!= linePtr
->numBytes
-1)) {
2262 panic("CheckNodeConsistency found line with extra newline");
2265 if (numBytes
!= linePtr
->numBytes
) {
2266 panic("CheckNodeConsistency found line with bad numBytes");
2268 if (linePtr
->bytes
[numBytes
-1] != '\n') {
2269 panic("CheckNodeConsistency found line with no newline");
2272 for (annotPtr
= linePtr
->annotPtr
; annotPtr
!= NULL
;
2273 annotPtr
= annotPtr
->nextPtr
) {
2274 if (annotPtr
->ch
< index
) {
2275 panic("CheckNodeConsistency found %s (%d %d)",
2276 "out-of-order tag indices", index
,
2279 index
= annotPtr
->ch
;
2280 if (annotPtr
->type
== TK_ANNOT_TOGGLE
) {
2281 for (summaryPtr
= nodePtr
->summaryPtr
; ;
2282 summaryPtr
= summaryPtr
->nextPtr
) {
2283 if (summaryPtr
== NULL
) {
2284 panic("CheckNodeConsistency found line %s",
2285 "tag with no node tag: %s",
2286 summaryPtr
->tagPtr
->name
);
2288 if (summaryPtr
->tagPtr
== annotPtr
->info
.tagPtr
) {
2298 for (childNodePtr
= nodePtr
->children
.nodePtr
; childNodePtr
!= NULL
;
2299 childNodePtr
= childNodePtr
->nextPtr
) {
2300 CheckNodeConsistency(childNodePtr
);
2301 for (summaryPtr
= childNodePtr
->summaryPtr
; summaryPtr
!= NULL
;
2302 summaryPtr
= summaryPtr
->nextPtr
) {
2303 for (summaryPtr2
= nodePtr
->summaryPtr
; ;
2304 summaryPtr2
= summaryPtr2
->nextPtr
) {
2305 if (summaryPtr2
== NULL
) {
2306 panic("CheckNodeConsistency found %s (%s)",
2307 "node tag with no parent tag",
2308 summaryPtr
->tagPtr
->name
);
2310 if (summaryPtr
->tagPtr
== summaryPtr2
->tagPtr
) {
2316 numLines
+= childNodePtr
->numLines
;
2317 if (childNodePtr
->parentPtr
!= nodePtr
) {
2318 panic("CheckNodeConsistency found node that %s",
2319 "didn't point to parent");
2321 if (childNodePtr
->level
!= (nodePtr
->level
-1)) {
2322 panic("CheckNodeConsistency found level mismatch (%d %d)",
2323 nodePtr
->level
, childNodePtr
->level
);
2327 if (numChildren
!= nodePtr
->numChildren
) {
2328 panic("CheckNodeConsistency found mismatch in numChildren (%d %d)",
2329 numChildren
, nodePtr
->numChildren
);
2331 if (numLines
!= nodePtr
->numLines
) {
2332 panic("CheckNodeConsistency found mismatch in numLines (%d %d)",
2333 numLines
, nodePtr
->numLines
);
2336 for (summaryPtr
= nodePtr
->summaryPtr
; summaryPtr
!= NULL
;
2337 summaryPtr
= summaryPtr
->nextPtr
) {
2339 if (nodePtr
->level
== 0) {
2340 for (linePtr
= nodePtr
->children
.linePtr
; linePtr
!= NULL
;
2341 linePtr
= linePtr
->nextPtr
) {
2342 for (annotPtr
= linePtr
->annotPtr
; annotPtr
!= NULL
;
2343 annotPtr
= annotPtr
->nextPtr
) {
2344 if (annotPtr
->info
.tagPtr
== summaryPtr
->tagPtr
) {
2350 for (childNodePtr
= nodePtr
->children
.nodePtr
;
2351 childNodePtr
!= NULL
;
2352 childNodePtr
= childNodePtr
->nextPtr
) {
2353 for (summaryPtr2
= childNodePtr
->summaryPtr
;
2354 summaryPtr2
!= NULL
;
2355 summaryPtr2
= summaryPtr2
->nextPtr
) {
2356 if (summaryPtr2
->tagPtr
== summaryPtr
->tagPtr
) {
2357 toggleCount
+= summaryPtr2
->toggleCount
;
2362 if (toggleCount
!= summaryPtr
->toggleCount
) {
2363 panic("CheckNodeConsistency found mismatch in toggleCount (%d %d)",
2364 toggleCount
, summaryPtr
->toggleCount
);
2366 for (summaryPtr2
= summaryPtr
->nextPtr
; summaryPtr2
!= NULL
;
2367 summaryPtr2
= summaryPtr2
->nextPtr
) {
2368 if (summaryPtr2
->tagPtr
== summaryPtr
->tagPtr
) {
2369 panic("CheckNodeConsistency found duplicated node tag: %s",
2370 summaryPtr
->tagPtr
->name
);
2377 *----------------------------------------------------------------------
2379 * TkBTreeNumLines --
2381 * This procedure returns a count of the number of lines of
2382 * text present in a given B-tree.
2385 * The return value is a count of the number of lines in tree.
2390 *----------------------------------------------------------------------
2395 TkTextBTree tree
/* Information about tree. */
2398 BTree
*treePtr
= (BTree
*) tree
;
2399 return treePtr
->rootPtr
->numLines
;