tkTextBTree.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:106k
源码类别:

通讯编程

开发平台:

Visual C++

  1. /* 
  2.  * tkTextBTree.c --
  3.  *
  4.  * This file contains code that manages the B-tree representation
  5.  * of text for Tk's text widget and implements character and
  6.  * toggle segment types.
  7.  *
  8.  * Copyright (c) 1992-1994 The Regents of the University of California.
  9.  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
  10.  *
  11.  * See the file "license.terms" for information on usage and redistribution
  12.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  13.  *
  14.  * RCS: @(#) $Id: tkTextBTree.c,v 1.6.2.3 2006/09/10 17:07:35 das Exp $
  15.  */
  16. #include "tkInt.h"
  17. #include "tkPort.h"
  18. #include "tkText.h"
  19. /*
  20.  * The data structure below keeps summary information about one tag as part
  21.  * of the tag information in a node.
  22.  */
  23. typedef struct Summary {
  24.     TkTextTag *tagPtr; /* Handle for tag. */
  25.     int toggleCount; /* Number of transitions into or
  26.  * out of this tag that occur in
  27.  * the subtree rooted at this node. */
  28.     struct Summary *nextPtr; /* Next in list of all tags for same
  29.  * node, or NULL if at end of list. */
  30. } Summary;
  31. /*
  32.  * The data structure below defines a node in the B-tree.
  33.  */
  34. typedef struct Node {
  35.     struct Node *parentPtr; /* Pointer to parent node, or NULL if
  36.  * this is the root. */
  37.     struct Node *nextPtr; /* Next in list of siblings with the
  38.  * same parent node, or NULL for end
  39.  * of list. */
  40.     Summary *summaryPtr; /* First in malloc-ed list of info
  41.  * about tags in this subtree (NULL if
  42.  * no tag info in the subtree). */
  43.     int level; /* Level of this node in the B-tree.
  44.  * 0 refers to the bottom of the tree
  45.  * (children are lines, not nodes). */
  46.     union { /* First in linked list of children. */
  47. struct Node *nodePtr; /* Used if level > 0. */
  48. TkTextLine *linePtr; /* Used if level == 0. */
  49.     } children;
  50.     int numChildren; /* Number of children of this node. */
  51.     int numLines; /* Total number of lines (leaves) in
  52.  * the subtree rooted here. */
  53. } Node;
  54. /*
  55.  * Upper and lower bounds on how many children a node may have:
  56.  * rebalance when either of these limits is exceeded.  MAX_CHILDREN
  57.  * should be twice MIN_CHILDREN and MIN_CHILDREN must be >= 2.
  58.  */
  59. #define MAX_CHILDREN 12
  60. #define MIN_CHILDREN 6
  61. /*
  62.  * The data structure below defines an entire B-tree.
  63.  */
  64. typedef struct BTree {
  65.     Node *rootPtr; /* Pointer to root of B-tree. */
  66.     TkText *textPtr; /* Used to find tagTable in consistency
  67.  * checking code */
  68. } BTree;
  69. /*
  70.  * The structure below is used to pass information between
  71.  * TkBTreeGetTags and IncCount:
  72.  */
  73. typedef struct TagInfo {
  74.     int numTags; /* Number of tags for which there
  75.  * is currently information in
  76.  * tags and counts. */
  77.     int arraySize; /* Number of entries allocated for
  78.  * tags and counts. */
  79.     TkTextTag **tagPtrs; /* Array of tags seen so far.
  80.  * Malloc-ed. */
  81.     int *counts; /* Toggle count (so far) for each
  82.  * entry in tags.  Malloc-ed. */
  83. } TagInfo;
  84. /*
  85.  * Variable that indicates whether to enable consistency checks for
  86.  * debugging.
  87.  */
  88. int tkBTreeDebug = 0;
  89. /*
  90.  * Macros that determine how much space to allocate for new segments:
  91.  */
  92. #define CSEG_SIZE(chars) ((unsigned) (Tk_Offset(TkTextSegment, body) 
  93. + 1 + (chars)))
  94. #define TSEG_SIZE ((unsigned) (Tk_Offset(TkTextSegment, body) 
  95. + sizeof(TkTextToggle)))
  96. /*
  97.  * Forward declarations for procedures defined in this file:
  98.  */
  99. static void ChangeNodeToggleCount _ANSI_ARGS_((Node *nodePtr,
  100.     TkTextTag *tagPtr, int delta));
  101. static void CharCheckProc _ANSI_ARGS_((TkTextSegment *segPtr,
  102.     TkTextLine *linePtr));
  103. static int CharDeleteProc _ANSI_ARGS_((TkTextSegment *segPtr,
  104.     TkTextLine *linePtr, int treeGone));
  105. static TkTextSegment * CharCleanupProc _ANSI_ARGS_((TkTextSegment *segPtr,
  106.     TkTextLine *linePtr));
  107. static TkTextSegment * CharSplitProc _ANSI_ARGS_((TkTextSegment *segPtr,
  108.     int index));
  109. static void CheckNodeConsistency _ANSI_ARGS_((Node *nodePtr));
  110. static void CleanupLine _ANSI_ARGS_((TkTextLine *linePtr));
  111. static void DeleteSummaries _ANSI_ARGS_((Summary *tagPtr));
  112. static void DestroyNode _ANSI_ARGS_((Node *nodePtr));
  113. static TkTextSegment * FindTagEnd _ANSI_ARGS_((TkTextBTree tree, 
  114.     TkTextTag *tagPtr, TkTextIndex *indexPtr));
  115. static void IncCount _ANSI_ARGS_((TkTextTag *tagPtr, int inc,
  116.     TagInfo *tagInfoPtr));
  117. static void Rebalance _ANSI_ARGS_((BTree *treePtr, Node *nodePtr));
  118. static void RecomputeNodeCounts _ANSI_ARGS_((Node *nodePtr));
  119. static TkTextSegment * SplitSeg _ANSI_ARGS_((TkTextIndex *indexPtr));
  120. static void ToggleCheckProc _ANSI_ARGS_((TkTextSegment *segPtr,
  121.     TkTextLine *linePtr));
  122. static TkTextSegment * ToggleCleanupProc _ANSI_ARGS_((TkTextSegment *segPtr,
  123.     TkTextLine *linePtr));
  124. static int ToggleDeleteProc _ANSI_ARGS_((TkTextSegment *segPtr,
  125.     TkTextLine *linePtr, int treeGone));
  126. static void ToggleLineChangeProc _ANSI_ARGS_((TkTextSegment *segPtr,
  127.     TkTextLine *linePtr));
  128. static TkTextSegment * FindTagStart _ANSI_ARGS_((TkTextBTree tree,
  129.     TkTextTag *tagPtr, TkTextIndex *indexPtr));
  130. /*
  131.  * Type record for character segments:
  132.  */
  133. Tk_SegType tkTextCharType = {
  134.     "character", /* name */
  135.     0, /* leftGravity */
  136.     CharSplitProc, /* splitProc */
  137.     CharDeleteProc, /* deleteProc */
  138.     CharCleanupProc, /* cleanupProc */
  139.     (Tk_SegLineChangeProc *) NULL, /* lineChangeProc */
  140.     TkTextCharLayoutProc, /* layoutProc */
  141.     CharCheckProc /* checkProc */
  142. };
  143. /*
  144.  * Type record for segments marking the beginning of a tagged
  145.  * range:
  146.  */
  147. Tk_SegType tkTextToggleOnType = {
  148.     "toggleOn", /* name */
  149.     0, /* leftGravity */
  150.     (Tk_SegSplitProc *) NULL, /* splitProc */
  151.     ToggleDeleteProc, /* deleteProc */
  152.     ToggleCleanupProc, /* cleanupProc */
  153.     ToggleLineChangeProc, /* lineChangeProc */
  154.     (Tk_SegLayoutProc *) NULL, /* layoutProc */
  155.     ToggleCheckProc /* checkProc */
  156. };
  157. /*
  158.  * Type record for segments marking the end of a tagged
  159.  * range:
  160.  */
  161. Tk_SegType tkTextToggleOffType = {
  162.     "toggleOff", /* name */
  163.     1, /* leftGravity */
  164.     (Tk_SegSplitProc *) NULL, /* splitProc */
  165.     ToggleDeleteProc, /* deleteProc */
  166.     ToggleCleanupProc, /* cleanupProc */
  167.     ToggleLineChangeProc, /* lineChangeProc */
  168.     (Tk_SegLayoutProc *) NULL, /* layoutProc */
  169.     ToggleCheckProc /* checkProc */
  170. };
  171. /*
  172.  *----------------------------------------------------------------------
  173.  *
  174.  * TkBTreeCreate --
  175.  *
  176.  * This procedure is called to create a new text B-tree.
  177.  *
  178.  * Results:
  179.  * The return value is a pointer to a new B-tree containing
  180.  * one line with nothing but a newline character.
  181.  *
  182.  * Side effects:
  183.  * Memory is allocated and initialized.
  184.  *
  185.  *----------------------------------------------------------------------
  186.  */
  187. TkTextBTree
  188. TkBTreeCreate(textPtr)
  189.     TkText *textPtr;
  190. {
  191.     register BTree *treePtr;
  192.     register Node *rootPtr;
  193.     register TkTextLine *linePtr, *linePtr2;
  194.     register TkTextSegment *segPtr;
  195.     /*
  196.      * The tree will initially have two empty lines.  The second line
  197.      * isn't actually part of the tree's contents, but its presence
  198.      * makes several operations easier.  The tree will have one node,
  199.      * which is also the root of the tree.
  200.      */
  201.     rootPtr = (Node *) ckalloc(sizeof(Node));
  202.     linePtr = (TkTextLine *) ckalloc(sizeof(TkTextLine));
  203.     linePtr2 = (TkTextLine *) ckalloc(sizeof(TkTextLine));
  204.     rootPtr->parentPtr = NULL;
  205.     rootPtr->nextPtr = NULL;
  206.     rootPtr->summaryPtr = NULL;
  207.     rootPtr->level = 0;
  208.     rootPtr->children.linePtr = linePtr;
  209.     rootPtr->numChildren = 2;
  210.     rootPtr->numLines = 2;
  211.     linePtr->parentPtr = rootPtr;
  212.     linePtr->nextPtr = linePtr2;
  213.     segPtr = (TkTextSegment *) ckalloc(CSEG_SIZE(1));
  214.     linePtr->segPtr = segPtr;
  215.     segPtr->typePtr = &tkTextCharType;
  216.     segPtr->nextPtr = NULL;
  217.     segPtr->size = 1;
  218.     segPtr->body.chars[0] = 'n';
  219.     segPtr->body.chars[1] = 0;
  220.     linePtr2->parentPtr = rootPtr;
  221.     linePtr2->nextPtr = NULL;
  222.     segPtr = (TkTextSegment *) ckalloc(CSEG_SIZE(1));
  223.     linePtr2->segPtr = segPtr;
  224.     segPtr->typePtr = &tkTextCharType;
  225.     segPtr->nextPtr = NULL;
  226.     segPtr->size = 1;
  227.     segPtr->body.chars[0] = 'n';
  228.     segPtr->body.chars[1] = 0;
  229.     treePtr = (BTree *) ckalloc(sizeof(BTree));
  230.     treePtr->rootPtr = rootPtr;
  231.     treePtr->textPtr = textPtr;
  232.     return (TkTextBTree) treePtr;
  233. }
  234. /*
  235.  *----------------------------------------------------------------------
  236.  *
  237.  * TkBTreeDestroy --
  238.  *
  239.  * Delete a B-tree, recycling all of the storage it contains.
  240.  *
  241.  * Results:
  242.  * The tree given by treePtr is deleted.  TreePtr should never
  243.  * again be used.
  244.  *
  245.  * Side effects:
  246.  * Memory is freed.
  247.  *
  248.  *----------------------------------------------------------------------
  249.  */
  250. void
  251. TkBTreeDestroy(tree)
  252.     TkTextBTree tree; /* Pointer to tree to delete. */ 
  253. {
  254.     BTree *treePtr = (BTree *) tree;
  255.     DestroyNode(treePtr->rootPtr);
  256.     ckfree((char *) treePtr);
  257. }
  258. /*
  259.  *----------------------------------------------------------------------
  260.  *
  261.  * DestroyNode --
  262.  *
  263.  * This is a recursive utility procedure used during the deletion
  264.  * of a B-tree.
  265.  *
  266.  * Results:
  267.  * None.
  268.  *
  269.  * Side effects:
  270.  * All the storage for nodePtr and its descendants is freed.
  271.  *
  272.  *----------------------------------------------------------------------
  273.  */
  274. static void
  275. DestroyNode(nodePtr)
  276.     register Node *nodePtr;
  277. {
  278.     if (nodePtr->level == 0) {
  279. TkTextLine *linePtr;
  280. TkTextSegment *segPtr;
  281. while (nodePtr->children.linePtr != NULL) {
  282.     linePtr = nodePtr->children.linePtr;
  283.     nodePtr->children.linePtr = linePtr->nextPtr;
  284.     while (linePtr->segPtr != NULL) {
  285. segPtr = linePtr->segPtr;
  286. linePtr->segPtr = segPtr->nextPtr;
  287. (*segPtr->typePtr->deleteProc)(segPtr, linePtr, 1);
  288.     }
  289.     ckfree((char *) linePtr);
  290. }
  291.     } else {
  292. register Node *childPtr;
  293. while (nodePtr->children.nodePtr != NULL) {
  294.     childPtr = nodePtr->children.nodePtr;
  295.     nodePtr->children.nodePtr = childPtr->nextPtr;
  296.     DestroyNode(childPtr);
  297. }
  298.     }
  299.     DeleteSummaries(nodePtr->summaryPtr);
  300.     ckfree((char *) nodePtr);
  301. }
  302. /*
  303.  *----------------------------------------------------------------------
  304.  *
  305.  * DeleteSummaries --
  306.  *
  307.  * Free up all of the memory in a list of tag summaries associated
  308.  * with a node.
  309.  *
  310.  * Results:
  311.  * None.
  312.  *
  313.  * Side effects:
  314.  * Storage is released.
  315.  *
  316.  *----------------------------------------------------------------------
  317.  */
  318. static void
  319. DeleteSummaries(summaryPtr)
  320.     register Summary *summaryPtr; /* First in list of node's tag
  321.  * summaries. */
  322. {
  323.     register Summary *nextPtr;
  324.     while (summaryPtr != NULL) {
  325. nextPtr = summaryPtr->nextPtr;
  326. ckfree((char *) summaryPtr);
  327. summaryPtr = nextPtr;
  328.     }
  329. }
  330. /*
  331.  *----------------------------------------------------------------------
  332.  *
  333.  * TkBTreeInsertChars --
  334.  *
  335.  * Insert characters at a given position in a B-tree.
  336.  *
  337.  * Results:
  338.  * None.
  339.  *
  340.  * Side effects:
  341.  * Characters are added to the B-tree at the given position.
  342.  * If the string contains newlines, new lines will be added,
  343.  * which could cause the structure of the B-tree to change.
  344.  *
  345.  *----------------------------------------------------------------------
  346.  */
  347. void
  348. TkBTreeInsertChars(indexPtr, string)
  349.     register TkTextIndex *indexPtr; /* Indicates where to insert text.
  350.  * When the procedure returns, this
  351.  * index is no longer valid because
  352.  * of changes to the segment
  353.  * structure. */
  354.     CONST char *string; /* Pointer to bytes to insert (may
  355.  * contain newlines, must be null-
  356.  * terminated). */
  357. {
  358.     register Node *nodePtr;
  359.     register TkTextSegment *prevPtr; /* The segment just before the first
  360.  * new segment (NULL means new segment
  361.  * is at beginning of line). */
  362.     TkTextSegment *curPtr; /* Current segment;  new characters
  363.  * are inserted just after this one. 
  364.  * NULL means insert at beginning of
  365.  * line. */
  366.     TkTextLine *linePtr; /* Current line (new segments are
  367.  * added to this line). */
  368.     register TkTextSegment *segPtr;
  369.     TkTextLine *newLinePtr;
  370.     int chunkSize; /* # characters in current chunk. */
  371.     register CONST char *eol; /* Pointer to character just after last
  372.  * one in current chunk. */
  373.     int changeToLineCount; /* Counts change to total number of
  374.  * lines in file. */
  375.     prevPtr = SplitSeg(indexPtr);
  376.     linePtr = indexPtr->linePtr;
  377.     curPtr = prevPtr;
  378.     /*
  379.      * Chop the string up into lines and create a new segment for
  380.      * each line, plus a new line for the leftovers from the
  381.      * previous line.
  382.      */
  383.     changeToLineCount = 0;
  384.     while (*string != 0) {
  385. for (eol = string; *eol != 0; eol++) {
  386.     if (*eol == 'n') {
  387. eol++;
  388. break;
  389.     }
  390. }
  391. chunkSize = eol-string;
  392. segPtr = (TkTextSegment *) ckalloc(CSEG_SIZE(chunkSize));
  393. segPtr->typePtr = &tkTextCharType;
  394. if (curPtr == NULL) {
  395.     segPtr->nextPtr = linePtr->segPtr;
  396.     linePtr->segPtr = segPtr;
  397. } else {
  398.     segPtr->nextPtr = curPtr->nextPtr;
  399.     curPtr->nextPtr = segPtr;
  400. }
  401. segPtr->size = chunkSize;
  402. strncpy(segPtr->body.chars, string, (size_t) chunkSize);
  403. segPtr->body.chars[chunkSize] = 0;
  404. if (eol[-1] != 'n') {
  405.     break;
  406. }
  407. /*
  408.  * The chunk ended with a newline, so create a new TkTextLine
  409.  * and move the remainder of the old line to it.
  410.  */
  411. newLinePtr = (TkTextLine *) ckalloc(sizeof(TkTextLine));
  412. newLinePtr->parentPtr = linePtr->parentPtr;
  413. newLinePtr->nextPtr = linePtr->nextPtr;
  414. linePtr->nextPtr = newLinePtr;
  415. newLinePtr->segPtr = segPtr->nextPtr;
  416. segPtr->nextPtr = NULL;
  417. linePtr = newLinePtr;
  418. curPtr = NULL;
  419. changeToLineCount++;
  420. string = eol;
  421.     }
  422.     /*
  423.      * Cleanup the starting line for the insertion, plus the ending
  424.      * line if it's different.
  425.      */
  426.     CleanupLine(indexPtr->linePtr);
  427.     if (linePtr != indexPtr->linePtr) {
  428. CleanupLine(linePtr);
  429.     }
  430.     /*
  431.      * Increment the line counts in all the parent nodes of the insertion
  432.      * point, then rebalance the tree if necessary.
  433.      */
  434.     for (nodePtr = linePtr->parentPtr ; nodePtr != NULL;
  435.     nodePtr = nodePtr->parentPtr) {
  436. nodePtr->numLines += changeToLineCount;
  437.     }
  438.     nodePtr = linePtr->parentPtr;
  439.     nodePtr->numChildren += changeToLineCount;
  440.     if (nodePtr->numChildren > MAX_CHILDREN) {
  441. Rebalance((BTree *) indexPtr->tree, nodePtr);
  442.     }
  443.     if (tkBTreeDebug) {
  444. TkBTreeCheck(indexPtr->tree);
  445.     }
  446. }
  447. /*
  448.  *--------------------------------------------------------------
  449.  *
  450.  * SplitSeg --
  451.  *
  452.  * This procedure is called before adding or deleting
  453.  * segments.  It does three things: (a) it finds the segment
  454.  * containing indexPtr;  (b) if there are several such
  455.  * segments (because some segments have zero length) then
  456.  * it picks the first segment that does not have left
  457.  * gravity;  (c) if the index refers to the middle of
  458.  * a segment then it splits the segment so that the
  459.  * index now refers to the beginning of a segment.
  460.  *
  461.  * Results:
  462.  * The return value is a pointer to the segment just
  463.  * before the segment corresponding to indexPtr (as
  464.  * described above).  If the segment corresponding to
  465.  * indexPtr is the first in its line then the return
  466.  * value is NULL.
  467.  *
  468.  * Side effects:
  469.  * The segment referred to by indexPtr is split unless
  470.  * indexPtr refers to its first character.
  471.  *
  472.  *--------------------------------------------------------------
  473.  */
  474. static TkTextSegment *
  475. SplitSeg(indexPtr)
  476.     TkTextIndex *indexPtr; /* Index identifying position
  477.  * at which to split a segment. */
  478. {
  479.     TkTextSegment *prevPtr, *segPtr;
  480.     int count;
  481.     for (count = indexPtr->byteIndex, prevPtr = NULL,
  482.     segPtr = indexPtr->linePtr->segPtr; segPtr != NULL;
  483.     count -= segPtr->size, prevPtr = segPtr, segPtr = segPtr->nextPtr) {
  484. if (segPtr->size > count) {
  485.     if (count == 0) {
  486. return prevPtr;
  487.     }
  488.     segPtr = (*segPtr->typePtr->splitProc)(segPtr, count);
  489.     if (prevPtr == NULL) {
  490. indexPtr->linePtr->segPtr = segPtr;
  491.     } else {
  492. prevPtr->nextPtr = segPtr;
  493.     }
  494.     return segPtr;
  495. } else if ((segPtr->size == 0) && (count == 0)
  496. && !segPtr->typePtr->leftGravity) {
  497.     return prevPtr;
  498. }
  499.     }
  500.     panic("SplitSeg reached end of line!");
  501.     return NULL;
  502. }
  503. /*
  504.  *--------------------------------------------------------------
  505.  *
  506.  * CleanupLine --
  507.  *
  508.  * This procedure is called after modifications have been
  509.  * made to a line.  It scans over all of the segments in
  510.  * the line, giving each a chance to clean itself up, e.g.
  511.  * by merging with the following segments, updating internal
  512.  * information, etc.
  513.  *
  514.  * Results:
  515.  * None.
  516.  *
  517.  * Side effects:
  518.  * Depends on what the segment-specific cleanup procedures do.
  519.  *
  520.  *--------------------------------------------------------------
  521.  */
  522. static void
  523. CleanupLine(linePtr)
  524.     TkTextLine *linePtr; /* Line to be cleaned up. */
  525. {
  526.     TkTextSegment *segPtr, **prevPtrPtr;
  527.     int anyChanges;
  528.     /*
  529.      * Make a pass over all of the segments in the line, giving each
  530.      * a chance to clean itself up.  This could potentially change
  531.      * the structure of the line, e.g. by merging two segments
  532.      * together or having two segments cancel themselves;  if so,
  533.      * then repeat the whole process again, since the first structure
  534.      * change might make other structure changes possible.  Repeat
  535.      * until eventually there are no changes.
  536.      */
  537.     while (1) {
  538. anyChanges = 0;
  539. for (prevPtrPtr = &linePtr->segPtr, segPtr = *prevPtrPtr;
  540. segPtr != NULL;
  541. prevPtrPtr = &(*prevPtrPtr)->nextPtr, segPtr = *prevPtrPtr) {
  542.     if (segPtr->typePtr->cleanupProc != NULL) {
  543. *prevPtrPtr = (*segPtr->typePtr->cleanupProc)(segPtr, linePtr);
  544. if (segPtr != *prevPtrPtr) {
  545.     anyChanges = 1;
  546. }
  547.     }
  548. }
  549. if (!anyChanges) {
  550.     break;
  551. }
  552.     }
  553. }
  554. /*
  555.  *----------------------------------------------------------------------
  556.  *
  557.  * TkBTreeDeleteChars --
  558.  *
  559.  * Delete a range of characters from a B-tree.  The caller
  560.  * must make sure that the final newline of the B-tree is
  561.  * never deleted.
  562.  *
  563.  * Results:
  564.  * None.
  565.  *
  566.  * Side effects:
  567.  * Information is deleted from the B-tree.  This can cause the
  568.  * internal structure of the B-tree to change.  Note: because
  569.  * of changes to the B-tree structure, the indices pointed
  570.  * to by index1Ptr and index2Ptr should not be used after this
  571.  * procedure returns.
  572.  *
  573.  *----------------------------------------------------------------------
  574.  */
  575. void
  576. TkBTreeDeleteChars(index1Ptr, index2Ptr)
  577.     register TkTextIndex *index1Ptr; /* Indicates first character that is
  578.  * to be deleted. */
  579.     register TkTextIndex *index2Ptr; /* Indicates character just after the
  580.  * last one that is to be deleted. */
  581. {
  582.     TkTextSegment *prevPtr; /* The segment just before the start
  583.  * of the deletion range. */
  584.     TkTextSegment *lastPtr; /* The segment just after the end
  585.  * of the deletion range. */
  586.     TkTextSegment *segPtr, *nextPtr;
  587.     TkTextLine *curLinePtr;
  588.     Node *curNodePtr, *nodePtr;
  589.     /*
  590.      * Tricky point:  split at index2Ptr first;  otherwise the split
  591.      * at index2Ptr may invalidate segPtr and/or prevPtr.
  592.      */
  593.     lastPtr = SplitSeg(index2Ptr);
  594.     if (lastPtr != NULL) {
  595. lastPtr = lastPtr->nextPtr;
  596.     }  else {
  597. lastPtr = index2Ptr->linePtr->segPtr;
  598.     }
  599.     prevPtr = SplitSeg(index1Ptr);
  600.     if (prevPtr != NULL) {
  601. segPtr = prevPtr->nextPtr;
  602. prevPtr->nextPtr = lastPtr;
  603.     } else {
  604. segPtr = index1Ptr->linePtr->segPtr;
  605. index1Ptr->linePtr->segPtr = lastPtr;
  606.     }
  607.     /*
  608.      * Delete all of the segments between prevPtr and lastPtr.
  609.      */
  610.     curLinePtr = index1Ptr->linePtr;
  611.     curNodePtr = curLinePtr->parentPtr;
  612.     while (segPtr != lastPtr) {
  613. if (segPtr == NULL) {
  614.     TkTextLine *nextLinePtr;
  615.     /*
  616.      * We just ran off the end of a line.  First find the
  617.      * next line, then go back to the old line and delete it
  618.      * (unless it's the starting line for the range).
  619.      */
  620.     nextLinePtr = TkBTreeNextLine(curLinePtr);
  621.     if (curLinePtr != index1Ptr->linePtr) {
  622. if (curNodePtr == index1Ptr->linePtr->parentPtr) {
  623.     index1Ptr->linePtr->nextPtr = curLinePtr->nextPtr;
  624. } else {
  625.     curNodePtr->children.linePtr = curLinePtr->nextPtr;
  626. }
  627. for (nodePtr = curNodePtr; nodePtr != NULL;
  628. nodePtr = nodePtr->parentPtr) {
  629.     nodePtr->numLines--;
  630. }
  631. curNodePtr->numChildren--;
  632. ckfree((char *) curLinePtr);
  633.     }
  634.     curLinePtr = nextLinePtr;
  635.     segPtr = curLinePtr->segPtr;
  636.     /*
  637.      * If the node is empty then delete it and its parents,
  638.      * recursively upwards until a non-empty node is found.
  639.      */
  640.     while (curNodePtr->numChildren == 0) {
  641. Node *parentPtr;
  642. parentPtr = curNodePtr->parentPtr;
  643. if (parentPtr->children.nodePtr == curNodePtr) {
  644.     parentPtr->children.nodePtr = curNodePtr->nextPtr;
  645. } else {
  646.     Node *prevNodePtr = parentPtr->children.nodePtr;
  647.     while (prevNodePtr->nextPtr != curNodePtr) {
  648. prevNodePtr = prevNodePtr->nextPtr;
  649.     }
  650.     prevNodePtr->nextPtr = curNodePtr->nextPtr;
  651. }
  652. parentPtr->numChildren--;
  653. ckfree((char *) curNodePtr);
  654. curNodePtr = parentPtr;
  655.     }
  656.     curNodePtr = curLinePtr->parentPtr;
  657.     continue;
  658. }
  659. nextPtr = segPtr->nextPtr;
  660. if ((*segPtr->typePtr->deleteProc)(segPtr, curLinePtr, 0) != 0) {
  661.     /*
  662.      * This segment refuses to die.  Move it to prevPtr and
  663.      * advance prevPtr if the segment has left gravity.
  664.      */
  665.     if (prevPtr == NULL) {
  666. segPtr->nextPtr = index1Ptr->linePtr->segPtr;
  667. index1Ptr->linePtr->segPtr = segPtr;
  668.     } else {
  669. segPtr->nextPtr = prevPtr->nextPtr;
  670. prevPtr->nextPtr = segPtr;
  671.     }
  672.     if (segPtr->typePtr->leftGravity) {
  673. prevPtr = segPtr;
  674.     }
  675. }
  676. segPtr = nextPtr;
  677.     }
  678.     /*
  679.      * If the beginning and end of the deletion range are in different
  680.      * lines, join the two lines together and discard the ending line.
  681.      */
  682.     if (index1Ptr->linePtr != index2Ptr->linePtr) {
  683. TkTextLine *prevLinePtr;
  684. for (segPtr = lastPtr; segPtr != NULL;
  685. segPtr = segPtr->nextPtr) {
  686.     if (segPtr->typePtr->lineChangeProc != NULL) {
  687. (*segPtr->typePtr->lineChangeProc)(segPtr, index2Ptr->linePtr);
  688.     }
  689. }
  690. curNodePtr = index2Ptr->linePtr->parentPtr;
  691. for (nodePtr = curNodePtr; nodePtr != NULL;
  692. nodePtr = nodePtr->parentPtr) {
  693.     nodePtr->numLines--;
  694. }
  695. curNodePtr->numChildren--;
  696. prevLinePtr = curNodePtr->children.linePtr;
  697. if (prevLinePtr == index2Ptr->linePtr) {
  698.     curNodePtr->children.linePtr = index2Ptr->linePtr->nextPtr;
  699. } else {
  700.     while (prevLinePtr->nextPtr != index2Ptr->linePtr) {
  701. prevLinePtr = prevLinePtr->nextPtr;
  702.     }
  703.     prevLinePtr->nextPtr = index2Ptr->linePtr->nextPtr;
  704. }
  705. ckfree((char *) index2Ptr->linePtr);
  706. Rebalance((BTree *) index2Ptr->tree, curNodePtr);
  707.     }
  708.     /*
  709.      * Cleanup the segments in the new line.
  710.      */
  711.     CleanupLine(index1Ptr->linePtr);
  712.     /*
  713.      * Lastly, rebalance the first node of the range.
  714.      */
  715.     Rebalance((BTree *) index1Ptr->tree, index1Ptr->linePtr->parentPtr);
  716.     if (tkBTreeDebug) {
  717. TkBTreeCheck(index1Ptr->tree);
  718.     }
  719. }
  720. /*
  721.  *----------------------------------------------------------------------
  722.  *
  723.  * TkBTreeFindLine --
  724.  *
  725.  * Find a particular line in a B-tree based on its line number.
  726.  *
  727.  * Results:
  728.  * The return value is a pointer to the line structure for the
  729.  * line whose index is "line", or NULL if no such line exists.
  730.  *
  731.  * Side effects:
  732.  * None.
  733.  *
  734.  *----------------------------------------------------------------------
  735.  */
  736. TkTextLine *
  737. TkBTreeFindLine(tree, line)
  738.     TkTextBTree tree; /* B-tree in which to find line. */
  739.     int line; /* Index of desired line. */
  740. {
  741.     BTree *treePtr = (BTree *) tree;
  742.     register Node *nodePtr;
  743.     register TkTextLine *linePtr;
  744.     int linesLeft;
  745.     nodePtr = treePtr->rootPtr;
  746.     linesLeft = line;
  747.     if ((line < 0) || (line >= nodePtr->numLines)) {
  748. return NULL;
  749.     }
  750.     /*
  751.      * Work down through levels of the tree until a node is found at
  752.      * level 0.
  753.      */
  754.     while (nodePtr->level != 0) {
  755. for (nodePtr = nodePtr->children.nodePtr;
  756. nodePtr->numLines <= linesLeft;
  757. nodePtr = nodePtr->nextPtr) {
  758.     if (nodePtr == NULL) {
  759. panic("TkBTreeFindLine ran out of nodes");
  760.     }
  761.     linesLeft -= nodePtr->numLines;
  762. }
  763.     }
  764.     /*
  765.      * Work through the lines attached to the level-0 node.
  766.      */
  767.     for (linePtr = nodePtr->children.linePtr; linesLeft > 0;
  768.     linePtr = linePtr->nextPtr) {
  769. if (linePtr == NULL) {
  770.     panic("TkBTreeFindLine ran out of lines");
  771. }
  772. linesLeft -= 1;
  773.     }
  774.     return linePtr;
  775. }
  776. /*
  777.  *----------------------------------------------------------------------
  778.  *
  779.  * TkBTreeNextLine --
  780.  *
  781.  * Given an existing line in a B-tree, this procedure locates the
  782.  * next line in the B-tree.  This procedure is used for scanning
  783.  * through the B-tree.
  784.  *
  785.  * Results:
  786.  * The return value is a pointer to the line that immediately
  787.  * follows linePtr, or NULL if there is no such line.
  788.  *
  789.  * Side effects:
  790.  * None.
  791.  *
  792.  *----------------------------------------------------------------------
  793.  */
  794. TkTextLine *
  795. TkBTreeNextLine(linePtr)
  796.     register TkTextLine *linePtr; /* Pointer to existing line in
  797.  * B-tree. */
  798. {
  799.     register Node *nodePtr;
  800.     if (linePtr->nextPtr != NULL) {
  801. return linePtr->nextPtr;
  802.     }
  803.     /*
  804.      * This was the last line associated with the particular parent node.
  805.      * Search up the tree for the next node, then search down from that
  806.      * node to find the first line.
  807.      */
  808.     for (nodePtr = linePtr->parentPtr; ; nodePtr = nodePtr->parentPtr) {
  809. if (nodePtr->nextPtr != NULL) {
  810.     nodePtr = nodePtr->nextPtr;
  811.     break;
  812. }
  813. if (nodePtr->parentPtr == NULL) {
  814.     return (TkTextLine *) NULL;
  815. }
  816.     }
  817.     while (nodePtr->level > 0) {
  818. nodePtr = nodePtr->children.nodePtr;
  819.     }
  820.     return nodePtr->children.linePtr;
  821. }
  822. /*
  823.  *----------------------------------------------------------------------
  824.  *
  825.  * TkBTreePreviousLine --
  826.  *
  827.  * Given an existing line in a B-tree, this procedure locates the
  828.  * previous line in the B-tree.  This procedure is used for scanning
  829.  * through the B-tree in the reverse direction.
  830.  *
  831.  * Results:
  832.  * The return value is a pointer to the line that immediately
  833.  * preceeds linePtr, or NULL if there is no such line.
  834.  *
  835.  * Side effects:
  836.  * None.
  837.  *
  838.  *----------------------------------------------------------------------
  839.  */
  840. TkTextLine *
  841. TkBTreePreviousLine(linePtr)
  842.     register TkTextLine *linePtr; /* Pointer to existing line in
  843.  * B-tree. */
  844. {
  845.     register Node *nodePtr;
  846.     register Node *node2Ptr;
  847.     register TkTextLine *prevPtr;
  848.     /*
  849.      * Find the line under this node just before the starting line.
  850.      */
  851.     prevPtr = linePtr->parentPtr->children.linePtr; /* First line at leaf */
  852.     while (prevPtr != linePtr) {
  853. if (prevPtr->nextPtr == linePtr) {
  854.     return prevPtr;
  855. }
  856. prevPtr = prevPtr->nextPtr;
  857. if (prevPtr == (TkTextLine *) NULL) {
  858.     panic("TkBTreePreviousLine ran out of lines");
  859. }
  860.     }
  861.     /*
  862.      * This was the first line associated with the particular parent node.
  863.      * Search up the tree for the previous node, then search down from that
  864.      * node to find its last line.
  865.      */
  866.     for (nodePtr = linePtr->parentPtr; ; nodePtr = nodePtr->parentPtr) {
  867. if (nodePtr == (Node *) NULL || nodePtr->parentPtr == (Node *) NULL) {
  868.     return (TkTextLine *) NULL;
  869. }
  870. if (nodePtr != nodePtr->parentPtr->children.nodePtr) {
  871.     break;
  872. }
  873.     }
  874.     for (node2Ptr = nodePtr->parentPtr->children.nodePtr; ; 
  875.     node2Ptr = node2Ptr->children.nodePtr) {
  876. while (node2Ptr->nextPtr != nodePtr) {
  877.     node2Ptr = node2Ptr->nextPtr;
  878. }
  879. if (node2Ptr->level == 0) {
  880.     break;
  881. }
  882. nodePtr = (Node *)NULL;
  883.     }
  884.     for (prevPtr = node2Ptr->children.linePtr ; ; prevPtr = prevPtr->nextPtr) {
  885. if (prevPtr->nextPtr == (TkTextLine *) NULL) {
  886.     return prevPtr;
  887. }
  888.     }
  889. }
  890. /*
  891.  *----------------------------------------------------------------------
  892.  *
  893.  * TkBTreeLineIndex --
  894.  *
  895.  * Given a pointer to a line in a B-tree, return the numerical
  896.  * index of that line.
  897.  *
  898.  * Results:
  899.  * The result is the index of linePtr within the tree, where 0
  900.  * corresponds to the first line in the tree.
  901.  *
  902.  * Side effects:
  903.  * None.
  904.  *
  905.  *----------------------------------------------------------------------
  906.  */
  907. int
  908. TkBTreeLineIndex(linePtr)
  909.     TkTextLine *linePtr; /* Pointer to existing line in
  910.  * B-tree. */
  911. {
  912.     register TkTextLine *linePtr2;
  913.     register Node *nodePtr, *parentPtr, *nodePtr2;
  914.     int index;
  915.     /*
  916.      * First count how many lines precede this one in its level-0
  917.      * node.
  918.      */
  919.     nodePtr = linePtr->parentPtr;
  920.     index = 0;
  921.     for (linePtr2 = nodePtr->children.linePtr; linePtr2 != linePtr;
  922.     linePtr2 = linePtr2->nextPtr) {
  923. if (linePtr2 == NULL) {
  924.     panic("TkBTreeLineIndex couldn't find line");
  925. }
  926. index += 1;
  927.     }
  928.     /*
  929.      * Now work up through the levels of the tree one at a time,
  930.      * counting how many lines are in nodes preceding the current
  931.      * node.
  932.      */
  933.     for (parentPtr = nodePtr->parentPtr ; parentPtr != NULL;
  934.     nodePtr = parentPtr, parentPtr = parentPtr->parentPtr) {
  935. for (nodePtr2 = parentPtr->children.nodePtr; nodePtr2 != nodePtr;
  936. nodePtr2 = nodePtr2->nextPtr) {
  937.     if (nodePtr2 == NULL) {
  938. panic("TkBTreeLineIndex couldn't find node");
  939.     }
  940.     index += nodePtr2->numLines;
  941. }
  942.     }
  943.     return index;
  944. }
  945. /*
  946.  *----------------------------------------------------------------------
  947.  *
  948.  * TkBTreeLinkSegment --
  949.  *
  950.  * This procedure adds a new segment to a B-tree at a given
  951.  * location.
  952.  *
  953.  * Results:
  954.  * None.
  955.  *
  956.  * Side effects:
  957.  * SegPtr will be linked into its tree.
  958.  *
  959.  *----------------------------------------------------------------------
  960.  */
  961. /* ARGSUSED */
  962. void
  963. TkBTreeLinkSegment(segPtr, indexPtr)
  964.     TkTextSegment *segPtr; /* Pointer to new segment to be added to
  965.  * B-tree.  Should be completely initialized
  966.  * by caller except for nextPtr field. */
  967.     TkTextIndex *indexPtr; /* Where to add segment:  it gets linked
  968.  * in just before the segment indicated
  969.  * here. */
  970. {
  971.     register TkTextSegment *prevPtr;
  972.     prevPtr = SplitSeg(indexPtr);
  973.     if (prevPtr == NULL) {
  974. segPtr->nextPtr = indexPtr->linePtr->segPtr;
  975. indexPtr->linePtr->segPtr = segPtr;
  976.     } else {
  977. segPtr->nextPtr = prevPtr->nextPtr;
  978. prevPtr->nextPtr = segPtr;
  979.     }
  980.     CleanupLine(indexPtr->linePtr);
  981.     if (tkBTreeDebug) {
  982. TkBTreeCheck(indexPtr->tree);
  983.     }
  984. }
  985. /*
  986.  *----------------------------------------------------------------------
  987.  *
  988.  * TkBTreeUnlinkSegment --
  989.  *
  990.  * This procedure unlinks a segment from its line in a B-tree.
  991.  *
  992.  * Results:
  993.  * None.
  994.  *
  995.  * Side effects:
  996.  * SegPtr will be unlinked from linePtr.  The segment itself
  997.  * isn't modified by this procedure.
  998.  *
  999.  *----------------------------------------------------------------------
  1000.  */
  1001. /* ARGSUSED */
  1002. void
  1003. TkBTreeUnlinkSegment(tree, segPtr, linePtr)
  1004.     TkTextBTree tree; /* Tree containing segment. */
  1005.     TkTextSegment *segPtr; /* Segment to be unlinked. */
  1006.     TkTextLine *linePtr; /* Line that currently contains
  1007.  * segment. */
  1008. {
  1009.     register TkTextSegment *prevPtr;
  1010.     if (linePtr->segPtr == segPtr) {
  1011. linePtr->segPtr = segPtr->nextPtr;
  1012.     } else {
  1013. for (prevPtr = linePtr->segPtr; prevPtr->nextPtr != segPtr;
  1014. prevPtr = prevPtr->nextPtr) {
  1015.     /* Empty loop body. */
  1016. }
  1017. prevPtr->nextPtr = segPtr->nextPtr;
  1018.     }
  1019.     CleanupLine(linePtr);
  1020. }
  1021. /*
  1022.  *----------------------------------------------------------------------
  1023.  *
  1024.  * TkBTreeTag --
  1025.  *
  1026.  * Turn a given tag on or off for a given range of characters in
  1027.  * a B-tree of text.
  1028.  *
  1029.  * Results:
  1030.  * None.
  1031.  *
  1032.  * Side effects:
  1033.  * The given tag is added to the given range of characters
  1034.  * in the tree or removed from all those characters, depending
  1035.  * on the "add" argument.  The structure of the btree is modified
  1036.  * enough that index1Ptr and index2Ptr are no longer valid after
  1037.  * this procedure returns, and the indexes may be modified by
  1038.  * this procedure.
  1039.  *
  1040.  *----------------------------------------------------------------------
  1041.  */
  1042. void
  1043. TkBTreeTag(index1Ptr, index2Ptr, tagPtr, add)
  1044.     register TkTextIndex *index1Ptr; /* Indicates first character in
  1045.  * range. */
  1046.     register TkTextIndex *index2Ptr; /* Indicates character just after the
  1047.  * last one in range. */
  1048.     TkTextTag *tagPtr; /* Tag to add or remove. */
  1049.     int add; /* One means add tag to the given
  1050.  * range of characters;  zero means
  1051.  * remove the tag from the range. */
  1052. {
  1053.     TkTextSegment *segPtr, *prevPtr;
  1054.     TkTextSearch search;
  1055.     TkTextLine *cleanupLinePtr;
  1056.     int oldState;
  1057.     int changed;
  1058.     /*
  1059.      * See whether the tag is present at the start of the range.  If
  1060.      * the state doesn't already match what we want then add a toggle
  1061.      * there.
  1062.      */
  1063.     oldState = TkBTreeCharTagged(index1Ptr, tagPtr);
  1064.     if ((add != 0) ^ oldState) {
  1065. segPtr = (TkTextSegment *) ckalloc(TSEG_SIZE);
  1066. segPtr->typePtr = (add) ? &tkTextToggleOnType : &tkTextToggleOffType;
  1067. prevPtr = SplitSeg(index1Ptr);
  1068. if (prevPtr == NULL) {
  1069.     segPtr->nextPtr = index1Ptr->linePtr->segPtr;
  1070.     index1Ptr->linePtr->segPtr = segPtr;
  1071. } else {
  1072.     segPtr->nextPtr = prevPtr->nextPtr;
  1073.     prevPtr->nextPtr = segPtr;
  1074. }
  1075. segPtr->size = 0;
  1076. segPtr->body.toggle.tagPtr = tagPtr;
  1077. segPtr->body.toggle.inNodeCounts = 0;
  1078.     }
  1079.     /*
  1080.      * Scan the range of characters and delete any internal tag
  1081.      * transitions.  Keep track of what the old state was at the end
  1082.      * of the range, and add a toggle there if it's needed.
  1083.      */
  1084.     TkBTreeStartSearch(index1Ptr, index2Ptr, tagPtr, &search);
  1085.     cleanupLinePtr = index1Ptr->linePtr;
  1086.     while (TkBTreeNextTag(&search)) {
  1087. oldState ^= 1;
  1088. segPtr = search.segPtr;
  1089. prevPtr = search.curIndex.linePtr->segPtr;
  1090. if (prevPtr == segPtr) {
  1091.     search.curIndex.linePtr->segPtr = segPtr->nextPtr;
  1092. } else {
  1093.     while (prevPtr->nextPtr != segPtr) {
  1094. prevPtr = prevPtr->nextPtr;
  1095.     }
  1096.     prevPtr->nextPtr = segPtr->nextPtr;
  1097. }
  1098. if (segPtr->body.toggle.inNodeCounts) {
  1099.     ChangeNodeToggleCount(search.curIndex.linePtr->parentPtr,
  1100.     segPtr->body.toggle.tagPtr, -1);
  1101.     segPtr->body.toggle.inNodeCounts = 0;
  1102.     changed = 1;
  1103. } else {
  1104.     changed = 0;
  1105. }
  1106. ckfree((char *) segPtr);
  1107. /*
  1108.  * The code below is a bit tricky.  After deleting a toggle
  1109.  * we eventually have to call CleanupLine, in order to allow
  1110.  * character segments to be merged together.  To do this, we
  1111.  * remember in cleanupLinePtr a line that needs to be
  1112.  * cleaned up, but we don't clean it up until we've moved
  1113.  * on to a different line.  That way the cleanup process
  1114.  * won't goof up segPtr.
  1115.  */
  1116. if (cleanupLinePtr != search.curIndex.linePtr) {
  1117.     CleanupLine(cleanupLinePtr);
  1118.     cleanupLinePtr = search.curIndex.linePtr;
  1119. }
  1120. /*
  1121.  * Quick hack.  ChangeNodeToggleCount may move the tag's root
  1122.  * location around and leave the search in the void.  This resets
  1123.  * the search.
  1124.  */
  1125. if (changed) {
  1126.     TkBTreeStartSearch(index1Ptr, index2Ptr, tagPtr, &search);
  1127. }
  1128.     }
  1129.     if ((add != 0) ^ oldState) {
  1130. segPtr = (TkTextSegment *) ckalloc(TSEG_SIZE);
  1131. segPtr->typePtr = (add) ? &tkTextToggleOffType : &tkTextToggleOnType;
  1132. prevPtr = SplitSeg(index2Ptr);
  1133. if (prevPtr == NULL) {
  1134.     segPtr->nextPtr = index2Ptr->linePtr->segPtr;
  1135.     index2Ptr->linePtr->segPtr = segPtr;
  1136. } else {
  1137.     segPtr->nextPtr = prevPtr->nextPtr;
  1138.     prevPtr->nextPtr = segPtr;
  1139. }
  1140. segPtr->size = 0;
  1141. segPtr->body.toggle.tagPtr = tagPtr;
  1142. segPtr->body.toggle.inNodeCounts = 0;
  1143.     }
  1144.     /*
  1145.      * Cleanup cleanupLinePtr and the last line of the range, if
  1146.      * these are different.
  1147.      */
  1148.     CleanupLine(cleanupLinePtr);
  1149.     if (cleanupLinePtr != index2Ptr->linePtr) {
  1150. CleanupLine(index2Ptr->linePtr);
  1151.     }
  1152.     if (tkBTreeDebug) {
  1153. TkBTreeCheck(index1Ptr->tree);
  1154.     }
  1155. }
  1156. /*
  1157.  *----------------------------------------------------------------------
  1158.  *
  1159.  * ChangeNodeToggleCount --
  1160.  *
  1161.  * This procedure increments or decrements the toggle count for
  1162.  * a particular tag in a particular node and all its ancestors
  1163.  * up to the per-tag root node.
  1164.  *
  1165.  * Results:
  1166.  * None.
  1167.  *
  1168.  * Side effects:
  1169.  * The toggle count for tag is adjusted up or down by "delta" in
  1170.  * nodePtr.  This routine maintains the tagRootPtr that identifies
  1171.  * the root node for the tag, moving it up or down the tree as needed.
  1172.  *
  1173.  *----------------------------------------------------------------------
  1174.  */
  1175. static void
  1176. ChangeNodeToggleCount(nodePtr, tagPtr, delta)
  1177.     register Node *nodePtr; /* Node whose toggle count for a tag
  1178.  * must be changed. */
  1179.     TkTextTag *tagPtr; /* Information about tag. */
  1180.     int delta; /* Amount to add to current toggle
  1181.  * count for tag (may be negative). */
  1182. {
  1183.     register Summary *summaryPtr, *prevPtr;
  1184.     register Node *node2Ptr;
  1185.     int rootLevel; /* Level of original tag root */
  1186.     tagPtr->toggleCount += delta;
  1187.     if (tagPtr->tagRootPtr == (Node *) NULL) {
  1188. tagPtr->tagRootPtr = nodePtr;
  1189. return;
  1190.     }
  1191.     /*
  1192.      * Note the level of the existing root for the tag so we can detect
  1193.      * if it needs to be moved because of the toggle count change.
  1194.      */
  1195.     rootLevel = tagPtr->tagRootPtr->level;
  1196.     /*
  1197.      * Iterate over the node and its ancestors up to the tag root, adjusting
  1198.      * summary counts at each node and moving the tag's root upwards if
  1199.      * necessary.
  1200.      */
  1201.     for ( ; nodePtr != tagPtr->tagRootPtr; nodePtr = nodePtr->parentPtr) {
  1202. /*
  1203.  * See if there's already an entry for this tag for this node.  If so,
  1204.  * perhaps all we have to do is adjust its count.
  1205.  */
  1206.     
  1207. for (prevPtr = NULL, summaryPtr = nodePtr->summaryPtr;
  1208. summaryPtr != NULL;
  1209. prevPtr = summaryPtr, summaryPtr = summaryPtr->nextPtr) {
  1210.     if (summaryPtr->tagPtr == tagPtr) {
  1211. break;
  1212.     }
  1213. }
  1214. if (summaryPtr != NULL) {
  1215.     summaryPtr->toggleCount += delta;
  1216.     if (summaryPtr->toggleCount > 0 &&
  1217.     summaryPtr->toggleCount < tagPtr->toggleCount) {
  1218. continue;
  1219.     }
  1220.     if (summaryPtr->toggleCount != 0) {
  1221. /*
  1222.  * Should never find a node with max toggle count at this
  1223.  * point (there shouldn't have been a summary entry in the
  1224.  * first place).
  1225.  */
  1226. panic("ChangeNodeToggleCount: bad toggle count (%d) max (%d)",
  1227.     summaryPtr->toggleCount, tagPtr->toggleCount);
  1228.     }
  1229.     
  1230.     /*
  1231.      * Zero toggle count;  must remove this tag from the list.
  1232.      */
  1233.     if (prevPtr == NULL) {
  1234. nodePtr->summaryPtr = summaryPtr->nextPtr;
  1235.     } else {
  1236. prevPtr->nextPtr = summaryPtr->nextPtr;
  1237.     }
  1238.     ckfree((char *) summaryPtr);
  1239. } else {
  1240.     /*
  1241.      * This tag isn't currently in the summary information list.
  1242.      */
  1243.     
  1244.     if (rootLevel == nodePtr->level) {
  1245.     
  1246. /*
  1247.  * The old tag root is at the same level in the tree as this
  1248.  * node, but it isn't at this node.  Move the tag root up
  1249.  * a level, in the hopes that it will now cover this node
  1250.  * as well as the old root (if not, we'll move it up again
  1251.  * the next time through the loop).  To push it up one level
  1252.  * we copy the original toggle count into the summary
  1253.  * information at the old root and change the root to its
  1254.  * parent node.
  1255.  */
  1256.     
  1257. Node *rootNodePtr = tagPtr->tagRootPtr;
  1258. summaryPtr = (Summary *) ckalloc(sizeof(Summary));
  1259. summaryPtr->tagPtr = tagPtr;
  1260. summaryPtr->toggleCount = tagPtr->toggleCount - delta;
  1261. summaryPtr->nextPtr = rootNodePtr->summaryPtr;
  1262. rootNodePtr->summaryPtr = summaryPtr;
  1263. rootNodePtr = rootNodePtr->parentPtr;
  1264. rootLevel = rootNodePtr->level;
  1265. tagPtr->tagRootPtr = rootNodePtr;
  1266.     }
  1267.     summaryPtr = (Summary *) ckalloc(sizeof(Summary));
  1268.     summaryPtr->tagPtr = tagPtr;
  1269.     summaryPtr->toggleCount = delta;
  1270.     summaryPtr->nextPtr = nodePtr->summaryPtr;
  1271.     nodePtr->summaryPtr = summaryPtr;
  1272. }
  1273.     }
  1274.     /*
  1275.      * If we've decremented the toggle count, then it may be necessary
  1276.      * to push the tag root down one or more levels.
  1277.      */
  1278.     if (delta >= 0) {
  1279. return;
  1280.     }
  1281.     if (tagPtr->toggleCount == 0) {
  1282. tagPtr->tagRootPtr = (Node *) NULL;
  1283. return;
  1284.     }
  1285.     nodePtr = tagPtr->tagRootPtr;
  1286.     while (nodePtr->level > 0) {
  1287. /*
  1288.  * See if a single child node accounts for all of the tag's
  1289.  * toggles.  If so, push the root down one level.
  1290.  */
  1291. for (node2Ptr = nodePtr->children.nodePtr;
  1292. node2Ptr != (Node *)NULL ;
  1293. node2Ptr = node2Ptr->nextPtr) {
  1294.     for (prevPtr = NULL, summaryPtr = node2Ptr->summaryPtr;
  1295.     summaryPtr != NULL;
  1296.     prevPtr = summaryPtr, summaryPtr = summaryPtr->nextPtr) {
  1297. if (summaryPtr->tagPtr == tagPtr) {
  1298.     break;
  1299. }
  1300.     }
  1301.     if (summaryPtr == NULL) {
  1302. continue;
  1303.     }
  1304.     if (summaryPtr->toggleCount != tagPtr->toggleCount) {
  1305. /*
  1306.  * No node has all toggles, so the root is still valid.
  1307.  */
  1308. return;
  1309.     }
  1310.     /*
  1311.      * This node has all the toggles, so push down the root.
  1312.      */
  1313.     if (prevPtr == NULL) {
  1314. node2Ptr->summaryPtr = summaryPtr->nextPtr;
  1315.     } else {
  1316. prevPtr->nextPtr = summaryPtr->nextPtr;
  1317.     }
  1318.     ckfree((char *) summaryPtr);
  1319.     tagPtr->tagRootPtr = node2Ptr;
  1320.     break;
  1321. }
  1322. nodePtr = tagPtr->tagRootPtr;
  1323.     }
  1324. }
  1325. /*
  1326.  *----------------------------------------------------------------------
  1327.  *
  1328.  * FindTagStart --
  1329.  *
  1330.  * Find the start of the first range of a tag.
  1331.  *
  1332.  * Results:
  1333.  * The return value is a pointer to the first tag toggle segment
  1334.  * for the tag.  This can be either a tagon or tagoff segments because
  1335.  * of the way TkBTreeAdd removes a tag.
  1336.  * Sets *indexPtr to be the index of the tag toggle.
  1337.  *
  1338.  * Side effects:
  1339.  * None.
  1340.  *
  1341.  *----------------------------------------------------------------------
  1342.  */
  1343. static TkTextSegment *
  1344. FindTagStart(tree, tagPtr, indexPtr)
  1345.     TkTextBTree tree; /* Tree to search within */
  1346.     TkTextTag *tagPtr; /* Tag to search for. */
  1347.     TkTextIndex *indexPtr; /* Return - index information */
  1348. {
  1349.     register Node *nodePtr;
  1350.     register TkTextLine *linePtr;
  1351.     register TkTextSegment *segPtr;
  1352.     register Summary *summaryPtr;
  1353.     int offset;
  1354.     nodePtr = tagPtr->tagRootPtr;
  1355.     if (nodePtr == (Node *) NULL) {
  1356. return NULL;
  1357.     }
  1358.     /*
  1359.      * Search from the root of the subtree that contains the tag down
  1360.      * to the level 0 node.
  1361.      */
  1362.     while (nodePtr->level > 0) {
  1363. for (nodePtr = nodePtr->children.nodePtr ; nodePtr != (Node *) NULL;
  1364. nodePtr = nodePtr->nextPtr) {
  1365.     for (summaryPtr = nodePtr->summaryPtr ; summaryPtr != NULL;
  1366.     summaryPtr = summaryPtr->nextPtr) {
  1367. if (summaryPtr->tagPtr == tagPtr) {
  1368.     goto gotNodeWithTag;
  1369. }
  1370.     }
  1371. }
  1372. gotNodeWithTag:
  1373. continue;
  1374.     }
  1375.     /*
  1376.      * Work through the lines attached to the level-0 node.
  1377.      */
  1378.     for (linePtr = nodePtr->children.linePtr; linePtr != (TkTextLine *) NULL;
  1379.     linePtr = linePtr->nextPtr) {
  1380. for (offset = 0, segPtr = linePtr->segPtr ; segPtr != NULL;
  1381. offset += segPtr->size, segPtr = segPtr->nextPtr) {
  1382.     if (((segPtr->typePtr == &tkTextToggleOnType)
  1383.     || (segPtr->typePtr == &tkTextToggleOffType))
  1384.     && (segPtr->body.toggle.tagPtr == tagPtr)) {
  1385. /*
  1386.  * It is possible that this is a tagoff tag, but that
  1387.  * gets cleaned up later.
  1388.  */
  1389. indexPtr->tree = tree;
  1390. indexPtr->linePtr = linePtr;
  1391. indexPtr->byteIndex = offset;
  1392. return segPtr;
  1393.     }
  1394. }
  1395.     }
  1396.     return NULL;
  1397. }
  1398. /*
  1399.  *----------------------------------------------------------------------
  1400.  *
  1401.  * FindTagEnd --
  1402.  *
  1403.  * Find the end of the last range of a tag.
  1404.  *
  1405.  * Results:
  1406.  * The return value is a pointer to the last tag toggle segment
  1407.  * for the tag.  This can be either a tagon or tagoff segments because
  1408.  * of the way TkBTreeAdd removes a tag.
  1409.  * Sets *indexPtr to be the index of the tag toggle.
  1410.  *
  1411.  * Side effects:
  1412.  * None.
  1413.  *
  1414.  *----------------------------------------------------------------------
  1415.  */
  1416. static TkTextSegment *
  1417. FindTagEnd(tree, tagPtr, indexPtr)
  1418.     TkTextBTree tree; /* Tree to search within */
  1419.     TkTextTag *tagPtr; /* Tag to search for. */
  1420.     TkTextIndex *indexPtr; /* Return - index information */
  1421. {
  1422.     register Node *nodePtr, *lastNodePtr;
  1423.     register TkTextLine *linePtr ,*lastLinePtr;
  1424.     register TkTextSegment *segPtr, *lastSegPtr, *last2SegPtr;
  1425.     register Summary *summaryPtr;
  1426.     int lastoffset, lastoffset2, offset;
  1427.     nodePtr = tagPtr->tagRootPtr;
  1428.     if (nodePtr == (Node *) NULL) {
  1429. return NULL;
  1430.     }
  1431.     /*
  1432.      * Search from the root of the subtree that contains the tag down
  1433.      * to the level 0 node.
  1434.      */
  1435.     while (nodePtr->level > 0) {
  1436. for (lastNodePtr = NULL, nodePtr = nodePtr->children.nodePtr ;
  1437. nodePtr != (Node *) NULL; nodePtr = nodePtr->nextPtr) {
  1438.     for (summaryPtr = nodePtr->summaryPtr ; summaryPtr != NULL;
  1439.     summaryPtr = summaryPtr->nextPtr) {
  1440. if (summaryPtr->tagPtr == tagPtr) {
  1441.     lastNodePtr = nodePtr;
  1442.     break;
  1443. }
  1444.     }
  1445. }
  1446. nodePtr = lastNodePtr;
  1447.     }
  1448.     /*
  1449.      * Work through the lines attached to the level-0 node.
  1450.      */
  1451.     last2SegPtr = NULL;
  1452.     lastoffset2 = 0;
  1453.     lastoffset = 0;
  1454.     for (lastLinePtr = NULL, linePtr = nodePtr->children.linePtr;
  1455.     linePtr != (TkTextLine *) NULL; linePtr = linePtr->nextPtr) {
  1456. for (offset = 0, lastSegPtr = NULL, segPtr = linePtr->segPtr ;
  1457. segPtr != NULL; 
  1458. offset += segPtr->size, segPtr = segPtr->nextPtr) {
  1459.     if (((segPtr->typePtr == &tkTextToggleOnType)
  1460.     || (segPtr->typePtr == &tkTextToggleOffType))
  1461.     && (segPtr->body.toggle.tagPtr == tagPtr)) {
  1462. lastSegPtr = segPtr;
  1463. lastoffset = offset;
  1464.     }
  1465. }
  1466. if (lastSegPtr != NULL) {
  1467.     lastLinePtr = linePtr;
  1468.     last2SegPtr = lastSegPtr;
  1469.     lastoffset2 = lastoffset;
  1470. }
  1471.     }
  1472.     indexPtr->tree = tree;
  1473.     indexPtr->linePtr = lastLinePtr;
  1474.     indexPtr->byteIndex = lastoffset2;
  1475.     return last2SegPtr;
  1476. }
  1477. /*
  1478.  *----------------------------------------------------------------------
  1479.  *
  1480.  * TkBTreeStartSearch --
  1481.  *
  1482.  * This procedure sets up a search for tag transitions involving
  1483.  * a given tag (or all tags) in a given range of the text.
  1484.  *
  1485.  * Results:
  1486.  * None.
  1487.  *
  1488.  * Side effects:
  1489.  * The information at *searchPtr is set up so that subsequent calls
  1490.  * to TkBTreeNextTag or TkBTreePrevTag will return information about the
  1491.  * locations of tag transitions.  Note that TkBTreeNextTag or 
  1492.  * TkBTreePrevTag must be called to get the first transition.
  1493.  * Note: unlike TkBTreeNextTag and TkBTreePrevTag, this routine does not
  1494.  * guarantee that searchPtr->curIndex is equal to *index1Ptr.  It may be
  1495.  * greater than that if *index1Ptr is less than the first tag transition.
  1496.  *
  1497.  *----------------------------------------------------------------------
  1498.  */
  1499. void
  1500. TkBTreeStartSearch(index1Ptr, index2Ptr, tagPtr, searchPtr)
  1501.     TkTextIndex *index1Ptr; /* Search starts here.  Tag toggles
  1502.  * at this position will not be
  1503.  * returned. */
  1504.     TkTextIndex *index2Ptr; /* Search stops here.  Tag toggles
  1505.  * at this position *will* be
  1506.  * returned. */
  1507.     TkTextTag *tagPtr; /* Tag to search for.  NULL means
  1508.  * search for any tag. */
  1509.     register TkTextSearch *searchPtr; /* Where to store information about
  1510.  * search's progress. */
  1511. {
  1512.     int offset;
  1513.     TkTextIndex index0; /* First index of the tag */
  1514.     TkTextSegment *seg0Ptr; /* First segment of the tag */
  1515.     /*
  1516.      * Find the segment that contains the first toggle for the tag.  This
  1517.      * may become the starting point in the search.
  1518.      */
  1519.     seg0Ptr = FindTagStart(index1Ptr->tree, tagPtr, &index0);
  1520.     if (seg0Ptr == (TkTextSegment *) NULL) {
  1521. /*
  1522.  * Even though there are no toggles, the display code still
  1523.  * uses the search curIndex, so initialize that anyway.
  1524.  */
  1525. searchPtr->linesLeft = 0;
  1526. searchPtr->curIndex = *index1Ptr;
  1527. searchPtr->segPtr = NULL;
  1528. searchPtr->nextPtr = NULL;
  1529. return;
  1530.     }
  1531.     if (TkTextIndexCmp(index1Ptr, &index0) < 0) {
  1532. /*
  1533.  * Adjust start of search up to the first range of the tag
  1534.  */
  1535. searchPtr->curIndex = index0;
  1536. searchPtr->segPtr = NULL;
  1537. searchPtr->nextPtr = seg0Ptr; /* Will be returned by NextTag */
  1538. index1Ptr = &index0;
  1539.     } else {
  1540. searchPtr->curIndex = *index1Ptr;
  1541. searchPtr->segPtr = NULL;
  1542. searchPtr->nextPtr = TkTextIndexToSeg(index1Ptr, &offset);
  1543. searchPtr->curIndex.byteIndex -= offset;
  1544.     }
  1545.     searchPtr->lastPtr = TkTextIndexToSeg(index2Ptr, (int *) NULL);
  1546.     searchPtr->tagPtr = tagPtr;
  1547.     searchPtr->linesLeft = TkBTreeLineIndex(index2Ptr->linePtr) + 1
  1548.     - TkBTreeLineIndex(index1Ptr->linePtr);
  1549.     searchPtr->allTags = (tagPtr == NULL);
  1550.     if (searchPtr->linesLeft == 1) {
  1551. /*
  1552.  * Starting and stopping segments are in the same line; mark the
  1553.  * search as over immediately if the second segment is before the
  1554.  * first.  A search does not return a toggle at the very start of
  1555.  * the range, unless the range is artificially moved up to index0.
  1556.  */
  1557. if (((index1Ptr == &index0) && 
  1558. (index1Ptr->byteIndex > index2Ptr->byteIndex)) ||
  1559.     ((index1Ptr != &index0) && 
  1560. (index1Ptr->byteIndex >= index2Ptr->byteIndex))) {
  1561. searchPtr->linesLeft = 0;
  1562. }
  1563.     }
  1564. }
  1565. /*
  1566.  *----------------------------------------------------------------------
  1567.  *
  1568.  * TkBTreeStartSearchBack --
  1569.  *
  1570.  * This procedure sets up a search backwards for tag transitions involving
  1571.  * a given tag (or all tags) in a given range of the text.  In the
  1572.  * normal case the first index (*index1Ptr) is beyond the second
  1573.  * index (*index2Ptr).
  1574.  *
  1575.  *
  1576.  * Results:
  1577.  * None.
  1578.  *
  1579.  * Side effects:
  1580.  * The information at *searchPtr is set up so that subsequent calls
  1581.  * to TkBTreePrevTag will return information about the
  1582.  * locations of tag transitions.  Note that TkBTreePrevTag must be called
  1583.  * to get the first transition.
  1584.  * Note: unlike TkBTreeNextTag and TkBTreePrevTag, this routine does not
  1585.  * guarantee that searchPtr->curIndex is equal to *index1Ptr.  It may be
  1586.  * less than that if *index1Ptr is greater than the last tag transition.
  1587.  *
  1588.  *----------------------------------------------------------------------
  1589.  */
  1590. void
  1591. TkBTreeStartSearchBack(index1Ptr, index2Ptr, tagPtr, searchPtr)
  1592.     TkTextIndex *index1Ptr; /* Search starts here.  Tag toggles
  1593.  * at this position will not be
  1594.  * returned. */
  1595.     TkTextIndex *index2Ptr; /* Search stops here.  Tag toggles
  1596.  * at this position *will* be
  1597.  * returned. */
  1598.     TkTextTag *tagPtr; /* Tag to search for.  NULL means
  1599.  * search for any tag. */
  1600.     register TkTextSearch *searchPtr; /* Where to store information about
  1601.  * search's progress. */
  1602. {
  1603.     int offset;
  1604.     TkTextIndex index0; /* Last index of the tag */
  1605.     TkTextIndex backOne; /* One character before starting index */
  1606.     TkTextSegment *seg0Ptr; /* Last segment of the tag */
  1607.     /*
  1608.      * Find the segment that contains the last toggle for the tag.  This
  1609.      * may become the starting point in the search.
  1610.      */
  1611.     seg0Ptr = FindTagEnd(index1Ptr->tree, tagPtr, &index0);
  1612.     if (seg0Ptr == (TkTextSegment *) NULL) {
  1613. /*
  1614.  * Even though there are no toggles, the display code still
  1615.  * uses the search curIndex, so initialize that anyway.
  1616.  */
  1617. searchPtr->linesLeft = 0;
  1618. searchPtr->curIndex = *index1Ptr;
  1619. searchPtr->segPtr = NULL;
  1620. searchPtr->nextPtr = NULL;
  1621. return;
  1622.     }
  1623.     /*
  1624.      * Adjust the start of the search so it doesn't find any tag toggles
  1625.      * that are right at the index specified by the user.
  1626.      */
  1627.     if (TkTextIndexCmp(index1Ptr, &index0) > 0) {
  1628. searchPtr->curIndex = index0;
  1629. index1Ptr = &index0;
  1630.     } else {
  1631. TkTextIndexBackChars(index1Ptr, 1, &searchPtr->curIndex);
  1632.     }
  1633.     searchPtr->segPtr = NULL;
  1634.     searchPtr->nextPtr = TkTextIndexToSeg(&searchPtr->curIndex, &offset);
  1635.     searchPtr->curIndex.byteIndex -= offset;
  1636.     /*
  1637.      * Adjust the end of the search so it does find toggles that are right
  1638.      * at the second index specified by the user.
  1639.      */
  1640.     if ((TkBTreeLineIndex(index2Ptr->linePtr) == 0) &&
  1641.     (index2Ptr->byteIndex == 0)) {
  1642. backOne = *index2Ptr;
  1643. searchPtr->lastPtr = NULL; /* Signals special case for 1.0 */
  1644.     } else {
  1645. TkTextIndexBackChars(index2Ptr, 1, &backOne);
  1646. searchPtr->lastPtr = TkTextIndexToSeg(&backOne, (int *) NULL);
  1647.     }
  1648.     searchPtr->tagPtr = tagPtr;
  1649.     searchPtr->linesLeft = TkBTreeLineIndex(index1Ptr->linePtr) + 1
  1650.     - TkBTreeLineIndex(backOne.linePtr);
  1651.     searchPtr->allTags = (tagPtr == NULL);
  1652.     if (searchPtr->linesLeft == 1) {
  1653. /*
  1654.  * Starting and stopping segments are in the same line; mark the
  1655.  * search as over immediately if the second segment is after the
  1656.  * first.
  1657.  */
  1658. if (index1Ptr->byteIndex <= backOne.byteIndex) {
  1659.     searchPtr->linesLeft = 0;
  1660. }
  1661.     }
  1662. }
  1663. /*
  1664.  *----------------------------------------------------------------------
  1665.  *
  1666.  * TkBTreeNextTag --
  1667.  *
  1668.  * Once a tag search has begun, successive calls to this procedure
  1669.  * return successive tag toggles.  Note:  it is NOT SAFE to call this
  1670.  * procedure if characters have been inserted into or deleted from
  1671.  * the B-tree since the call to TkBTreeStartSearch.
  1672.  *
  1673.  * Results:
  1674.  * The return value is 1 if another toggle was found that met the
  1675.  * criteria specified in the call to TkBTreeStartSearch;  in this
  1676.  * case searchPtr->curIndex gives the toggle's position and
  1677.  * searchPtr->curTagPtr points to its segment.  0 is returned if
  1678.  * no more matching tag transitions were found; in this case
  1679.  * searchPtr->curIndex is the same as searchPtr->stopIndex.
  1680.  *
  1681.  * Side effects:
  1682.  * Information in *searchPtr is modified to update the state of the
  1683.  * search and indicate where the next tag toggle is located.
  1684.  *
  1685.  *----------------------------------------------------------------------
  1686.  */
  1687. int
  1688. TkBTreeNextTag(searchPtr)
  1689.     register TkTextSearch *searchPtr; /* Information about search in
  1690.  * progress;  must have been set up by
  1691.  * call to TkBTreeStartSearch. */
  1692. {
  1693.     register TkTextSegment *segPtr;
  1694.     register Node *nodePtr;
  1695.     register Summary *summaryPtr;
  1696.     if (searchPtr->linesLeft <= 0) {
  1697. goto searchOver;
  1698.     }
  1699.     /*
  1700.      * The outermost loop iterates over lines that may potentially contain
  1701.      * a relevant tag transition, starting from the current segment in
  1702.      * the current line.
  1703.      */
  1704.     segPtr = searchPtr->nextPtr;
  1705.     while (1) {
  1706. /*
  1707.  * Check for more tags on the current line.
  1708.  */
  1709. for ( ; segPtr != NULL; segPtr = segPtr->nextPtr) {
  1710.     if (segPtr == searchPtr->lastPtr) {
  1711. goto searchOver;
  1712.     }
  1713.     if (((segPtr->typePtr == &tkTextToggleOnType)
  1714.     || (segPtr->typePtr == &tkTextToggleOffType))
  1715.     && (searchPtr->allTags
  1716.     || (segPtr->body.toggle.tagPtr == searchPtr->tagPtr))) {
  1717. searchPtr->segPtr = segPtr;
  1718. searchPtr->nextPtr = segPtr->nextPtr;
  1719. searchPtr->tagPtr = segPtr->body.toggle.tagPtr;
  1720. return 1;
  1721.     }
  1722.     searchPtr->curIndex.byteIndex += segPtr->size;
  1723. }
  1724.     
  1725. /*
  1726.  * See if there are more lines associated with the current parent
  1727.  * node.  If so, go back to the top of the loop to search the next
  1728.  * one.
  1729.  */
  1730. nodePtr = searchPtr->curIndex.linePtr->parentPtr;
  1731. searchPtr->curIndex.linePtr = searchPtr->curIndex.linePtr->nextPtr;
  1732. searchPtr->linesLeft--;
  1733. if (searchPtr->linesLeft <= 0) {
  1734.     goto searchOver;
  1735. }
  1736. if (searchPtr->curIndex.linePtr != NULL) {
  1737.     segPtr = searchPtr->curIndex.linePtr->segPtr;
  1738.     searchPtr->curIndex.byteIndex = 0;
  1739.     continue;
  1740. }
  1741. if (nodePtr == searchPtr->tagPtr->tagRootPtr) {
  1742.     goto searchOver;
  1743. }
  1744.     
  1745. /*
  1746.  * Search across and up through the B-tree's node hierarchy looking
  1747.  * for the next node that has a relevant tag transition somewhere in
  1748.  * its subtree.  Be sure to update linesLeft as we skip over large
  1749.  * chunks of lines.
  1750.  */
  1751.     
  1752. while (1) {
  1753.     while (nodePtr->nextPtr == NULL) {
  1754. if (nodePtr->parentPtr == NULL ||
  1755.     nodePtr->parentPtr == searchPtr->tagPtr->tagRootPtr) {
  1756.     goto searchOver;
  1757. }
  1758. nodePtr = nodePtr->parentPtr;
  1759.     }
  1760.     nodePtr = nodePtr->nextPtr;
  1761.     for (summaryPtr = nodePtr->summaryPtr; summaryPtr != NULL;
  1762.     summaryPtr = summaryPtr->nextPtr) {
  1763. if ((searchPtr->allTags) ||
  1764. (summaryPtr->tagPtr == searchPtr->tagPtr)) {
  1765.     goto gotNodeWithTag;
  1766. }
  1767.     }
  1768.     searchPtr->linesLeft -= nodePtr->numLines;
  1769. }
  1770.     
  1771. /*
  1772.  * At this point we've found a subtree that has a relevant tag
  1773.  * transition.  Now search down (and across) through that subtree
  1774.  * to find the first level-0 node that has a relevant tag transition.
  1775.  */
  1776.     
  1777. gotNodeWithTag:
  1778. while (nodePtr->level > 0) {
  1779.     for (nodePtr = nodePtr->children.nodePtr; ;
  1780.     nodePtr = nodePtr->nextPtr) {
  1781. for (summaryPtr = nodePtr->summaryPtr; summaryPtr != NULL;
  1782. summaryPtr = summaryPtr->nextPtr) {
  1783.     if ((searchPtr->allTags)
  1784.     || (summaryPtr->tagPtr == searchPtr->tagPtr)) {
  1785. goto nextChild;
  1786.     }
  1787. }
  1788. searchPtr->linesLeft -= nodePtr->numLines;
  1789. if (nodePtr->nextPtr == NULL) {
  1790.     panic("TkBTreeNextTag found incorrect tag summary info.");
  1791. }
  1792.     }
  1793.     nextChild:
  1794.     continue;
  1795. }
  1796.     
  1797. /*
  1798.  * Now we're down to a level-0 node that contains a line that contains
  1799.  * a relevant tag transition.  Set up line information and go back to
  1800.  * the beginning of the loop to search through lines.
  1801.  */
  1802. searchPtr->curIndex.linePtr = nodePtr->children.linePtr;
  1803. searchPtr->curIndex.byteIndex = 0;
  1804. segPtr = searchPtr->curIndex.linePtr->segPtr;
  1805. if (searchPtr->linesLeft <= 0) {
  1806.     goto searchOver;
  1807. }
  1808. continue;
  1809.     }
  1810.     searchOver:
  1811.     searchPtr->linesLeft = 0;
  1812.     searchPtr->segPtr = NULL;
  1813.     return 0;
  1814. }
  1815. /*
  1816.  *----------------------------------------------------------------------
  1817.  *
  1818.  * TkBTreePrevTag --
  1819.  *
  1820.  * Once a tag search has begun, successive calls to this procedure
  1821.  * return successive tag toggles in the reverse direction.
  1822.  * Note:  it is NOT SAFE to call this
  1823.  * procedure if characters have been inserted into or deleted from
  1824.  * the B-tree since the call to TkBTreeStartSearch.
  1825.  *
  1826.  * Results:
  1827.  * The return value is 1 if another toggle was found that met the
  1828.  * criteria specified in the call to TkBTreeStartSearch;  in this
  1829.  * case searchPtr->curIndex gives the toggle's position and
  1830.  * searchPtr->curTagPtr points to its segment.  0 is returned if
  1831.  * no more matching tag transitions were found; in this case
  1832.  * searchPtr->curIndex is the same as searchPtr->stopIndex.
  1833.  *
  1834.  * Side effects:
  1835.  * Information in *searchPtr is modified to update the state of the
  1836.  * search and indicate where the next tag toggle is located.
  1837.  *
  1838.  *----------------------------------------------------------------------
  1839.  */
  1840. int
  1841. TkBTreePrevTag(searchPtr)
  1842.     register TkTextSearch *searchPtr; /* Information about search in
  1843.  * progress;  must have been set up by
  1844.  * call to TkBTreeStartSearch. */
  1845. {
  1846.     register TkTextSegment *segPtr, *prevPtr;
  1847.     register TkTextLine *linePtr, *prevLinePtr;
  1848.     register Node *nodePtr, *node2Ptr, *prevNodePtr;
  1849.     register Summary *summaryPtr;
  1850.     int byteIndex;
  1851.     int pastLast; /* Saw last marker during scan */
  1852.     int linesSkipped;
  1853.     if (searchPtr->linesLeft <= 0) {
  1854. goto searchOver;
  1855.     }
  1856.     /*
  1857.      * The outermost loop iterates over lines that may potentially contain
  1858.      * a relevant tag transition, starting from the current segment in
  1859.      * the current line.  "nextPtr" is maintained as the last segment in
  1860.      * a line that we can look at. 
  1861.      */
  1862.     while (1) {
  1863. /*
  1864.  * Check for the last toggle before the current segment on this line.
  1865.  */
  1866. byteIndex = 0;
  1867. if (searchPtr->lastPtr == NULL) {
  1868.     /* 
  1869.      * Search back to the very beginning, so pastLast is irrelevent.
  1870.      */
  1871.     pastLast = 1; 
  1872. } else {
  1873.     pastLast = 0;
  1874. }
  1875. for (prevPtr = NULL, segPtr = searchPtr->curIndex.linePtr->segPtr ;
  1876. segPtr != NULL && segPtr != searchPtr->nextPtr;
  1877. segPtr = segPtr->nextPtr) {
  1878.     if (((segPtr->typePtr == &tkTextToggleOnType)
  1879.     || (segPtr->typePtr == &tkTextToggleOffType))
  1880.     && (searchPtr->allTags
  1881.     || (segPtr->body.toggle.tagPtr == searchPtr->tagPtr))) {
  1882. prevPtr = segPtr;
  1883. searchPtr->curIndex.byteIndex = byteIndex;
  1884.     }
  1885.     if (segPtr == searchPtr->lastPtr) {
  1886.         prevPtr = NULL;   /* Segments earlier than last don't count */
  1887. pastLast = 1;
  1888.     }
  1889.     byteIndex += segPtr->size;
  1890. }
  1891. if (prevPtr != NULL) {
  1892.     if (searchPtr->linesLeft == 1 && !pastLast) {
  1893. /*
  1894.  * We found a segment that is before the stopping index.
  1895.  * Note that it is OK if prevPtr == lastPtr.
  1896.  */
  1897. goto searchOver;
  1898.     }
  1899.     searchPtr->segPtr = prevPtr;
  1900.     searchPtr->nextPtr = prevPtr;
  1901.     searchPtr->tagPtr = prevPtr->body.toggle.tagPtr;
  1902.     return 1;
  1903. }
  1904.     
  1905. searchPtr->linesLeft--;
  1906. if (searchPtr->linesLeft <= 0) {
  1907.     goto searchOver;
  1908. }
  1909. /*
  1910.  * See if there are more lines associated with the current parent
  1911.  * node.  If so, go back to the top of the loop to search the previous
  1912.  * one.
  1913.  */
  1914. nodePtr = searchPtr->curIndex.linePtr->parentPtr;
  1915. for (prevLinePtr = NULL, linePtr = nodePtr->children.linePtr;
  1916. linePtr != NULL && linePtr != searchPtr->curIndex.linePtr;
  1917. prevLinePtr = linePtr, linePtr = linePtr->nextPtr) {
  1918.     /* empty loop body */ ;
  1919. }
  1920. if (prevLinePtr != NULL) {
  1921.     searchPtr->curIndex.linePtr = prevLinePtr;
  1922.     searchPtr->nextPtr = NULL;
  1923.     continue;
  1924. }
  1925. if (nodePtr == searchPtr->tagPtr->tagRootPtr) {
  1926.     goto searchOver;
  1927. }
  1928.     
  1929. /*
  1930.  * Search across and up through the B-tree's node hierarchy looking
  1931.  * for the previous node that has a relevant tag transition somewhere in
  1932.  * its subtree.  The search and line counting is trickier with/out
  1933.  * back pointers. We'll scan all the nodes under a parent up to
  1934.  * the current node, searching all of them for tag state.  The last
  1935.  * one we find, if any, is recorded in prevNodePtr, and any nodes
  1936.  * past prevNodePtr that don't have tag state increment linesSkipped.
  1937.  */
  1938.     
  1939. while (1) {
  1940.     for (prevNodePtr = NULL, linesSkipped = 0,
  1941.     node2Ptr = nodePtr->parentPtr->children.nodePtr ;
  1942.     node2Ptr != nodePtr;  node2Ptr = node2Ptr->nextPtr) {
  1943. for (summaryPtr = node2Ptr->summaryPtr; summaryPtr != NULL;
  1944. summaryPtr = summaryPtr->nextPtr) {
  1945.     if ((searchPtr->allTags) ||
  1946.     (summaryPtr->tagPtr == searchPtr->tagPtr)) {
  1947. prevNodePtr = node2Ptr;
  1948. linesSkipped = 0;
  1949. goto keepLooking;
  1950.     }
  1951. }
  1952. linesSkipped += node2Ptr->numLines;
  1953. keepLooking:
  1954. continue;
  1955.     }
  1956.     if (prevNodePtr != NULL) {
  1957. nodePtr = prevNodePtr;
  1958. searchPtr->linesLeft -= linesSkipped;
  1959. goto gotNodeWithTag;
  1960.     }
  1961.     nodePtr = nodePtr->parentPtr;
  1962.     if (nodePtr->parentPtr == NULL ||
  1963. nodePtr == searchPtr->tagPtr->tagRootPtr) {
  1964. goto searchOver;
  1965.     }
  1966. }
  1967.     
  1968. /*
  1969.  * At this point we've found a subtree that has a relevant tag
  1970.  * transition.  Now search down (and across) through that subtree
  1971.  * to find the last level-0 node that has a relevant tag transition.
  1972.  */
  1973.     
  1974. gotNodeWithTag:
  1975. while (nodePtr->level > 0) {
  1976.     for (linesSkipped = 0, prevNodePtr = NULL,
  1977.     nodePtr = nodePtr->children.nodePtr; nodePtr != NULL ;
  1978.     nodePtr = nodePtr->nextPtr) {
  1979. for (summaryPtr = nodePtr->summaryPtr; summaryPtr != NULL;
  1980. summaryPtr = summaryPtr->nextPtr) {
  1981.     if ((searchPtr->allTags)
  1982.     || (summaryPtr->tagPtr == searchPtr->tagPtr)) {
  1983. prevNodePtr = nodePtr;
  1984. linesSkipped = 0;
  1985. goto keepLooking2;
  1986.     }
  1987. }
  1988. linesSkipped += nodePtr->numLines;
  1989. keepLooking2:
  1990. continue;
  1991.     }
  1992.     if (prevNodePtr == NULL) {
  1993. panic("TkBTreePrevTag found incorrect tag summary info.");
  1994.     }
  1995.     searchPtr->linesLeft -= linesSkipped;
  1996.     nodePtr = prevNodePtr;
  1997. }
  1998.     
  1999. /*
  2000.  * Now we're down to a level-0 node that contains a line that contains
  2001.  * a relevant tag transition.  Set up line information and go back to
  2002.  * the beginning of the loop to search through lines.  We start with
  2003.  * the last line below the node.
  2004.  */
  2005. for (prevLinePtr = NULL, linePtr = nodePtr->children.linePtr;
  2006. linePtr != NULL ;
  2007. prevLinePtr = linePtr, linePtr = linePtr->nextPtr) {
  2008.     /* empty loop body */ ;
  2009. }
  2010. searchPtr->curIndex.linePtr = prevLinePtr;
  2011. searchPtr->curIndex.byteIndex = 0;
  2012. if (searchPtr->linesLeft <= 0) {
  2013.     goto searchOver;
  2014. }
  2015. continue;
  2016.     }
  2017.     searchOver:
  2018.     searchPtr->linesLeft = 0;
  2019.     searchPtr->segPtr = NULL;
  2020.     return 0;
  2021. }
  2022. /*
  2023.  *----------------------------------------------------------------------
  2024.  *
  2025.  * TkBTreeCharTagged --
  2026.  *
  2027.  * Determine whether a particular character has a particular tag.
  2028.  *
  2029.  * Results:
  2030.  * The return value is 1 if the given tag is in effect at the
  2031.  * character given by linePtr and ch, and 0 otherwise.
  2032.  *
  2033.  * Side effects:
  2034.  * None.
  2035.  *
  2036.  *----------------------------------------------------------------------
  2037.  */
  2038. int
  2039. TkBTreeCharTagged(indexPtr, tagPtr)
  2040.     TkTextIndex *indexPtr; /* Indicates a character position at
  2041.  * which to check for a tag. */
  2042.     TkTextTag *tagPtr; /* Tag of interest. */
  2043. {
  2044.     register Node *nodePtr;
  2045.     register TkTextLine *siblingLinePtr;
  2046.     register TkTextSegment *segPtr;
  2047.     TkTextSegment *toggleSegPtr;
  2048.     int toggles, index;
  2049.     /* 
  2050.      * Check for toggles for the tag in indexPtr's line but before
  2051.      * indexPtr.  If there is one, its type indicates whether or
  2052.      * not the character is tagged.
  2053.      */
  2054.     toggleSegPtr = NULL;
  2055.     for (index = 0, segPtr = indexPtr->linePtr->segPtr;
  2056.     (index + segPtr->size) <= indexPtr->byteIndex;
  2057.     index += segPtr->size, segPtr = segPtr->nextPtr) {
  2058. if (((segPtr->typePtr == &tkTextToggleOnType)
  2059. || (segPtr->typePtr == &tkTextToggleOffType))
  2060. && (segPtr->body.toggle.tagPtr == tagPtr)) {
  2061.     toggleSegPtr = segPtr;
  2062. }
  2063.     }
  2064.     if (toggleSegPtr != NULL) {
  2065. return (toggleSegPtr->typePtr == &tkTextToggleOnType);
  2066.     }
  2067.     /*
  2068.      * No toggle in this line.  Look for toggles for the tag in lines
  2069.      * that are predecessors of indexPtr->linePtr but under the same
  2070.      * level-0 node.
  2071.      */
  2072.     for (siblingLinePtr = indexPtr->linePtr->parentPtr->children.linePtr;
  2073.     siblingLinePtr != indexPtr->linePtr;
  2074.     siblingLinePtr = siblingLinePtr->nextPtr) {
  2075. for (segPtr = siblingLinePtr->segPtr; segPtr != NULL;
  2076. segPtr = segPtr->nextPtr) {
  2077.     if (((segPtr->typePtr == &tkTextToggleOnType)
  2078.     || (segPtr->typePtr == &tkTextToggleOffType))
  2079.     && (segPtr->body.toggle.tagPtr == tagPtr)) {
  2080. toggleSegPtr = segPtr;
  2081.     }
  2082. }
  2083.     }
  2084.     if (toggleSegPtr != NULL) {
  2085. return (toggleSegPtr->typePtr == &tkTextToggleOnType);
  2086.     }
  2087.     /*
  2088.      * No toggle in this node.  Scan upwards through the ancestors of
  2089.      * this node, counting the number of toggles of the given tag in
  2090.      * siblings that precede that node.
  2091.      */
  2092.     toggles = 0;
  2093.     for (nodePtr = indexPtr->linePtr->parentPtr; nodePtr->parentPtr != NULL;
  2094.     nodePtr = nodePtr->parentPtr) {
  2095. register Node *siblingPtr;
  2096. register Summary *summaryPtr;
  2097. for (siblingPtr = nodePtr->parentPtr->children.nodePtr; 
  2098. siblingPtr != nodePtr; siblingPtr = siblingPtr->nextPtr) {
  2099.     for (summaryPtr = siblingPtr->summaryPtr; summaryPtr != NULL;
  2100.     summaryPtr = summaryPtr->nextPtr) {
  2101. if (summaryPtr->tagPtr == tagPtr) {
  2102.     toggles += summaryPtr->toggleCount;
  2103. }
  2104.     }
  2105. }
  2106. if (nodePtr == tagPtr->tagRootPtr) {
  2107.     break;
  2108. }
  2109.     }
  2110.     /*
  2111.      * An odd number of toggles means that the tag is present at the
  2112.      * given point.
  2113.      */
  2114.     return toggles & 1;
  2115. }
  2116. /*
  2117.  *----------------------------------------------------------------------
  2118.  *
  2119.  * TkBTreeGetTags --
  2120.  *
  2121.  * Return information about all of the tags that are associated
  2122.  * with a particular character in a B-tree of text.
  2123.  *
  2124.  * Results:
  2125.  * The return value is a malloc-ed array containing pointers to
  2126.  * information for each of the tags that is associated with
  2127.  * the character at the position given by linePtr and ch.  The
  2128.  * word at *numTagsPtr is filled in with the number of pointers
  2129.  * in the array.  It is up to the caller to free the array by
  2130.  * passing it to free.  If there are no tags at the given character
  2131.  * then a NULL pointer is returned and *numTagsPtr will be set to 0.
  2132.  *
  2133.  * Side effects:
  2134.  * None.
  2135.  *
  2136.  *----------------------------------------------------------------------
  2137.  */
  2138. /* ARGSUSED */
  2139. TkTextTag **
  2140. TkBTreeGetTags(indexPtr, numTagsPtr)
  2141.     TkTextIndex *indexPtr; /* Indicates a particular position in
  2142.  * the B-tree. */
  2143.     int *numTagsPtr; /* Store number of tags found at this
  2144.  * location. */
  2145. {
  2146.     register Node *nodePtr;
  2147.     register TkTextLine *siblingLinePtr;
  2148.     register TkTextSegment *segPtr;
  2149.     int src, dst, index;
  2150.     TagInfo tagInfo;
  2151. #define NUM_TAG_INFOS 10
  2152.     tagInfo.numTags = 0;
  2153.     tagInfo.arraySize = NUM_TAG_INFOS;
  2154.     tagInfo.tagPtrs = (TkTextTag **) ckalloc((unsigned)
  2155.     NUM_TAG_INFOS*sizeof(TkTextTag *));
  2156.     tagInfo.counts = (int *) ckalloc((unsigned)
  2157.     NUM_TAG_INFOS*sizeof(int));
  2158.     /*
  2159.      * Record tag toggles within the line of indexPtr but preceding
  2160.      * indexPtr.
  2161.      */
  2162.     for (index = 0, segPtr = indexPtr->linePtr->segPtr;
  2163.     (index + segPtr->size) <= indexPtr->byteIndex;
  2164.     index += segPtr->size, segPtr = segPtr->nextPtr) {
  2165. if ((segPtr->typePtr == &tkTextToggleOnType)
  2166. || (segPtr->typePtr == &tkTextToggleOffType)) {
  2167.     IncCount(segPtr->body.toggle.tagPtr, 1, &tagInfo);
  2168. }
  2169.     }
  2170.     /*
  2171.      * Record toggles for tags in lines that are predecessors of
  2172.      * indexPtr->linePtr but under the same level-0 node.
  2173.      */
  2174.     for (siblingLinePtr = indexPtr->linePtr->parentPtr->children.linePtr;
  2175.     siblingLinePtr != indexPtr->linePtr;
  2176.     siblingLinePtr = siblingLinePtr->nextPtr) {
  2177. for (segPtr = siblingLinePtr->segPtr; segPtr != NULL;
  2178. segPtr = segPtr->nextPtr) {
  2179.     if ((segPtr->typePtr == &tkTextToggleOnType)
  2180.     || (segPtr->typePtr == &tkTextToggleOffType)) {
  2181. IncCount(segPtr->body.toggle.tagPtr, 1, &tagInfo);
  2182.     }
  2183. }
  2184.     }
  2185.     /*
  2186.      * For each node in the ancestry of this line, record tag toggles
  2187.      * for all siblings that precede that node.
  2188.      */
  2189.     for (nodePtr = indexPtr->linePtr->parentPtr; nodePtr->parentPtr != NULL;
  2190.     nodePtr = nodePtr->parentPtr) {
  2191. register Node *siblingPtr;
  2192. register Summary *summaryPtr;
  2193. for (siblingPtr = nodePtr->parentPtr->children.nodePtr; 
  2194. siblingPtr != nodePtr; siblingPtr = siblingPtr->nextPtr) {
  2195.     for (summaryPtr = siblingPtr->summaryPtr; summaryPtr != NULL;
  2196.     summaryPtr = summaryPtr->nextPtr) {
  2197. if (summaryPtr->toggleCount & 1) {
  2198.     IncCount(summaryPtr->tagPtr, summaryPtr->toggleCount,
  2199.     &tagInfo);
  2200. }
  2201.     }
  2202. }
  2203.     }
  2204.     /*
  2205.      * Go through the tag information and squash out all of the tags
  2206.      * that have even toggle counts (these tags exist before the point
  2207.      * of interest, but not at the desired character itself).
  2208.      */
  2209.     for (src = 0, dst = 0; src < tagInfo.numTags; src++) {
  2210. if (tagInfo.counts[src] & 1) {
  2211.     tagInfo.tagPtrs[dst] = tagInfo.tagPtrs[src];
  2212.     dst++;
  2213. }
  2214.     }
  2215.     *numTagsPtr = dst;
  2216.     ckfree((char *) tagInfo.counts);
  2217.     if (dst == 0) {
  2218. ckfree((char *) tagInfo.tagPtrs);
  2219. return NULL;
  2220.     }
  2221.     return tagInfo.tagPtrs;
  2222. }
  2223. /*
  2224.  *----------------------------------------------------------------------
  2225.  *
  2226.  * TkTextIsElided --
  2227.  *
  2228.  * Special case to just return information about elided attribute.
  2229.  * Specialized from TkBTreeGetTags(indexPtr, numTagsPtr)
  2230.  * and GetStyle(textPtr, indexPtr).
  2231.  * Just need to keep track of invisibility settings for each priority,
  2232.  * pick highest one active at end
  2233.  *
  2234.  * Results:
  2235.  * Returns whether this text should be elided or not.
  2236.  *
  2237.  * Side effects:
  2238.  * None.
  2239.  *
  2240.  *----------------------------------------------------------------------
  2241.  */
  2242. /* ARGSUSED */
  2243. int
  2244. TkTextIsElided(textPtr, indexPtr)
  2245.     TkText *textPtr; /* Overall information about text widget. */
  2246.     TkTextIndex *indexPtr; /* The character in the text for which
  2247.  * display information is wanted. */
  2248. {
  2249. #define LOTSA_TAGS 1000
  2250.     int elide = 0; /* if nobody says otherwise, it's visible */
  2251.     int deftagCnts[LOTSA_TAGS];
  2252.     int *tagCnts = deftagCnts;
  2253.     TkTextTag *deftagPtrs[LOTSA_TAGS];
  2254.     TkTextTag **tagPtrs = deftagPtrs;
  2255.     int numTags = textPtr->numTags;
  2256.     register Node *nodePtr;
  2257.     register TkTextLine *siblingLinePtr;
  2258.     register TkTextSegment *segPtr;
  2259.     register TkTextTag *tagPtr = NULL; /* silence gcc 4 warning */
  2260.     register int i, index;
  2261. /* almost always avoid malloc, so stay out of system calls */
  2262.     if (LOTSA_TAGS < numTags) {
  2263. tagCnts = (int *)ckalloc((unsigned)sizeof(int) * numTags);
  2264. tagPtrs = (TkTextTag **)ckalloc((unsigned)sizeof(TkTextTag *) * numTags);
  2265.     }
  2266.  
  2267.     for (i=0; i<numTags; i++) {
  2268. tagCnts[i] = 0;
  2269.     }
  2270.     /*
  2271.      * Record tag toggles within the line of indexPtr but preceding
  2272.      * indexPtr.
  2273.      */
  2274.     for (index = 0, segPtr = indexPtr->linePtr->segPtr;
  2275.  (index + segPtr->size) <= indexPtr->byteIndex;
  2276.  index += segPtr->size, segPtr = segPtr->nextPtr) {
  2277. if ((segPtr->typePtr == &tkTextToggleOnType)
  2278. || (segPtr->typePtr == &tkTextToggleOffType)) {
  2279.     tagPtr = segPtr->body.toggle.tagPtr;
  2280.     if (tagPtr->elideString != NULL) {
  2281. tagPtrs[tagPtr->priority] = tagPtr;
  2282. tagCnts[tagPtr->priority]++;
  2283.     }
  2284. }
  2285.     }
  2286.     /*
  2287.      * Record toggles for tags in lines that are predecessors of
  2288.      * indexPtr->linePtr but under the same level-0 node.
  2289.      */
  2290.     for (siblingLinePtr = indexPtr->linePtr->parentPtr->children.linePtr;
  2291.  siblingLinePtr != indexPtr->linePtr;
  2292.  siblingLinePtr = siblingLinePtr->nextPtr) {
  2293. for (segPtr = siblingLinePtr->segPtr; segPtr != NULL;
  2294.      segPtr = segPtr->nextPtr) {
  2295.     if ((segPtr->typePtr == &tkTextToggleOnType)
  2296.     || (segPtr->typePtr == &tkTextToggleOffType)) {
  2297. tagPtr = segPtr->body.toggle.tagPtr;
  2298. if (tagPtr->elideString != NULL) {
  2299.     tagPtrs[tagPtr->priority] = tagPtr;
  2300.     tagCnts[tagPtr->priority]++;
  2301. }
  2302.     }
  2303. }
  2304.     }
  2305.     /*
  2306.      * For each node in the ancestry of this line, record tag toggles
  2307.      * for all siblings that precede that node.
  2308.      */
  2309.     for (nodePtr = indexPtr->linePtr->parentPtr; nodePtr->parentPtr != NULL;
  2310.  nodePtr = nodePtr->parentPtr) {
  2311. register Node *siblingPtr;
  2312. register Summary *summaryPtr;
  2313. for (siblingPtr = nodePtr->parentPtr->children.nodePtr; 
  2314.      siblingPtr != nodePtr; siblingPtr = siblingPtr->nextPtr) {
  2315.     for (summaryPtr = siblingPtr->summaryPtr; summaryPtr != NULL;
  2316.  summaryPtr = summaryPtr->nextPtr) {
  2317. if (summaryPtr->toggleCount & 1) {
  2318.     tagPtr = summaryPtr->tagPtr;
  2319.     if (tagPtr->elideString != NULL) {
  2320. tagPtrs[tagPtr->priority] = tagPtr;
  2321. tagCnts[tagPtr->priority] += summaryPtr->toggleCount;
  2322.     }
  2323. }
  2324.     }
  2325. }
  2326.     }
  2327.     /*
  2328.      * Now traverse from highest priority to lowest, 
  2329.      * take elided value from first odd count (= on)
  2330.      */
  2331.     for (i = numTags-1; i >=0; i--) {
  2332. if (tagCnts[i] & 1) {
  2333.     /* who would make the selection elided? */
  2334.     if (
  2335. #ifndef MAC_OSX_TK
  2336.     !TkpAlwaysShowSelection(textPtr->tkwin)
  2337. #else
  2338.     /* Don't show inactive selection in disabled widgets. */
  2339.     textPtr->state == TK_STATE_DISABLED
  2340. #endif
  2341.     && (tagPtr == textPtr->selTagPtr)
  2342.     && !(textPtr->flags & GOT_FOCUS)) {
  2343. continue;
  2344.     }
  2345.     elide = tagPtrs[i]->elide;
  2346.     break;
  2347. }
  2348.     }
  2349.     if (LOTSA_TAGS < numTags) {
  2350. ckfree((char *) tagCnts);
  2351. ckfree((char *) tagPtrs);
  2352.     }
  2353.     return elide;
  2354. }
  2355. /*
  2356.  *----------------------------------------------------------------------
  2357.  *
  2358.  * IncCount --
  2359.  *
  2360.  * This is a utility procedure used by TkBTreeGetTags.  It
  2361.  * increments the count for a particular tag, adding a new
  2362.  * entry for that tag if there wasn't one previously.
  2363.  *
  2364.  * Results:
  2365.  * None.
  2366.  *
  2367.  * Side effects:
  2368.  * The information at *tagInfoPtr may be modified, and the arrays
  2369.  * may be reallocated to make them larger.
  2370.  *
  2371.  *----------------------------------------------------------------------
  2372.  */
  2373. static void
  2374. IncCount(tagPtr, inc, tagInfoPtr)
  2375.     TkTextTag *tagPtr; /* Handle for tag. */
  2376.     int inc; /* Amount by which to increment tag count. */
  2377.     TagInfo *tagInfoPtr; /* Holds cumulative information about tags;
  2378.  * increment count here. */
  2379. {
  2380.     register TkTextTag **tagPtrPtr;
  2381.     int count;
  2382.     for (tagPtrPtr = tagInfoPtr->tagPtrs, count = tagInfoPtr->numTags;
  2383.     count > 0; tagPtrPtr++, count--) {
  2384. if (*tagPtrPtr == tagPtr) {
  2385.     tagInfoPtr->counts[tagInfoPtr->numTags-count] += inc;
  2386.     return;
  2387. }
  2388.     }
  2389.     /*
  2390.      * There isn't currently an entry for this tag, so we have to
  2391.      * make a new one.  If the arrays are full, then enlarge the
  2392.      * arrays first.
  2393.      */
  2394.     if (tagInfoPtr->numTags == tagInfoPtr->arraySize) {
  2395. TkTextTag **newTags;
  2396. int *newCounts, newSize;
  2397. newSize = 2*tagInfoPtr->arraySize;
  2398. newTags = (TkTextTag **) ckalloc((unsigned)
  2399. (newSize*sizeof(TkTextTag *)));
  2400. memcpy((VOID *) newTags, (VOID *) tagInfoPtr->tagPtrs,
  2401. tagInfoPtr->arraySize * sizeof(TkTextTag *));
  2402. ckfree((char *) tagInfoPtr->tagPtrs);
  2403. tagInfoPtr->tagPtrs = newTags;
  2404. newCounts = (int *) ckalloc((unsigned) (newSize*sizeof(int)));
  2405. memcpy((VOID *) newCounts, (VOID *) tagInfoPtr->counts,
  2406. tagInfoPtr->arraySize * sizeof(int));
  2407. ckfree((char *) tagInfoPtr->counts);
  2408. tagInfoPtr->counts = newCounts;
  2409. tagInfoPtr->arraySize = newSize;
  2410.     }
  2411.     tagInfoPtr->tagPtrs[tagInfoPtr->numTags] = tagPtr;
  2412.     tagInfoPtr->counts[tagInfoPtr->numTags] = inc;
  2413.     tagInfoPtr->numTags++;
  2414. }
  2415. /*
  2416.  *----------------------------------------------------------------------
  2417.  *
  2418.  * TkBTreeCheck --
  2419.  *
  2420.  * This procedure runs a set of consistency checks over a B-tree
  2421.  * and panics if any inconsistencies are found.
  2422.  *
  2423.  * Results:
  2424.  * None.
  2425.  *
  2426.  * Side effects:
  2427.  * If a structural defect is found, the procedure panics with an
  2428.  * error message.
  2429.  *
  2430.  *----------------------------------------------------------------------
  2431.  */
  2432. void
  2433. TkBTreeCheck(tree)
  2434.     TkTextBTree tree; /* Tree to check. */
  2435. {
  2436.     BTree *treePtr = (BTree *) tree;
  2437.     register Summary *summaryPtr;
  2438.     register Node *nodePtr;
  2439.     register TkTextLine *linePtr;
  2440.     register TkTextSegment *segPtr;
  2441.     register TkTextTag *tagPtr;
  2442.     Tcl_HashEntry *entryPtr;
  2443.     Tcl_HashSearch search;
  2444.     int count;
  2445.     /*
  2446.      * Make sure that the tag toggle counts and the tag root pointers are OK.
  2447.      */
  2448.     for (entryPtr = Tcl_FirstHashEntry(&treePtr->textPtr->tagTable, &search);
  2449.     entryPtr != NULL ; entryPtr = Tcl_NextHashEntry(&search)) {
  2450. tagPtr = (TkTextTag *) Tcl_GetHashValue(entryPtr);
  2451. nodePtr = tagPtr->tagRootPtr;
  2452. if (nodePtr == (Node *) NULL) {
  2453.     if (tagPtr->toggleCount != 0) {
  2454. panic("TkBTreeCheck found "%s" with toggles (%d) but no root",
  2455.     tagPtr->name, tagPtr->toggleCount);
  2456.     }
  2457.     continue; /* no ranges for the tag */
  2458. } else if (tagPtr->toggleCount == 0) {
  2459.     panic("TkBTreeCheck found root for "%s" with no toggles",
  2460.     tagPtr->name);
  2461. } else if (tagPtr->toggleCount & 1) {
  2462.     panic("TkBTreeCheck found odd toggle count for "%s" (%d)",
  2463.     tagPtr->name, tagPtr->toggleCount);
  2464. }
  2465. for (summaryPtr = nodePtr->summaryPtr; summaryPtr != NULL;
  2466. summaryPtr = summaryPtr->nextPtr) {
  2467.     if (summaryPtr->tagPtr == tagPtr) {
  2468. panic("TkBTreeCheck found root node with summary info");
  2469.     }
  2470. }
  2471. count = 0;
  2472. if (nodePtr->level > 0) {
  2473.     for (nodePtr = nodePtr->children.nodePtr ; nodePtr != NULL ;
  2474.     nodePtr = nodePtr->nextPtr) {
  2475. for (summaryPtr = nodePtr->summaryPtr; summaryPtr != NULL;
  2476. summaryPtr = summaryPtr->nextPtr) {
  2477.     if (summaryPtr->tagPtr == tagPtr) {
  2478. count += summaryPtr->toggleCount;
  2479.     }
  2480. }
  2481.     }
  2482. } else {
  2483.     for (linePtr = nodePtr->children.linePtr ; linePtr != NULL ;
  2484.     linePtr = linePtr->nextPtr) {
  2485. for (segPtr = linePtr->segPtr; segPtr != NULL;
  2486. segPtr = segPtr->nextPtr) {
  2487.     if ((segPtr->typePtr == &tkTextToggleOnType ||
  2488.  segPtr->typePtr == &tkTextToggleOffType) &&
  2489.  segPtr->body.toggle.tagPtr == tagPtr) {
  2490. count++;
  2491.     }
  2492. }
  2493.     }
  2494. }
  2495. if (count != tagPtr->toggleCount) {
  2496.     panic("TkBTreeCheck toggleCount (%d) wrong for "%s" should be (%d)",
  2497. tagPtr->toggleCount, tagPtr->name, count);
  2498. }
  2499.     }
  2500.     /*
  2501.      * Call a recursive procedure to do the main body of checks.
  2502.      */
  2503.     nodePtr = treePtr->rootPtr;
  2504.     CheckNodeConsistency(treePtr->rootPtr);
  2505.     /*
  2506.      * Make sure that there are at least two lines in the text and
  2507.      * that the last line has no characters except a newline.
  2508.      */
  2509.     if (nodePtr->numLines < 2) {
  2510. panic("TkBTreeCheck: less than 2 lines in tree");
  2511.     }
  2512.     while (nodePtr->level > 0) {
  2513. nodePtr = nodePtr->children.nodePtr;
  2514. while (nodePtr->nextPtr != NULL) {
  2515.     nodePtr = nodePtr->nextPtr;
  2516. }
  2517.     }
  2518.     linePtr = nodePtr->children.linePtr;
  2519.     while (linePtr->nextPtr != NULL) {
  2520. linePtr = linePtr->nextPtr;
  2521.     }
  2522.     segPtr = linePtr->segPtr;
  2523.     while ((segPtr->typePtr == &tkTextToggleOffType)
  2524.     || (segPtr->typePtr == &tkTextRightMarkType)
  2525.     || (segPtr->typePtr == &tkTextLeftMarkType)) {
  2526. /*
  2527.  * It's OK to toggle a tag off in the last line, but
  2528.  * not to start a new range.  It's also OK to have marks
  2529.  * in the last line.
  2530.  */
  2531. segPtr = segPtr->nextPtr;
  2532.     }
  2533.     if (segPtr->typePtr != &tkTextCharType) {
  2534. panic("TkBTreeCheck: last line has bogus segment type");
  2535.     }
  2536.     if (segPtr->nextPtr != NULL) {
  2537. panic("TkBTreeCheck: last line has too many segments");
  2538.     }
  2539.     if (segPtr->size != 1) {
  2540. panic("TkBTreeCheck: last line has wrong # characters: %d",
  2541. segPtr->size);
  2542.     }
  2543.     if ((segPtr->body.chars[0] != 'n') || (segPtr->body.chars[1] != 0)) {
  2544. panic("TkBTreeCheck: last line had bad value: %s",
  2545. segPtr->body.chars);
  2546.     }
  2547. }
  2548. /*
  2549.  *----------------------------------------------------------------------
  2550.  *
  2551.  * CheckNodeConsistency --
  2552.  *
  2553.  * This procedure is called as part of consistency checking for
  2554.  * B-trees:  it checks several aspects of a node and also runs
  2555.  * checks recursively on the node's children.
  2556.  *
  2557.  * Results:
  2558.  * None.
  2559.  *
  2560.  * Side effects:
  2561.  * If anything suspicious is found in the tree structure, the
  2562.  * procedure panics.
  2563.  *
  2564.  *----------------------------------------------------------------------
  2565.  */
  2566. static void
  2567. CheckNodeConsistency(nodePtr)
  2568.     register Node *nodePtr; /* Node whose subtree should be
  2569.  * checked. */
  2570. {
  2571.     register Node *childNodePtr;
  2572.     register Summary *summaryPtr, *summaryPtr2;
  2573.     register TkTextLine *linePtr;
  2574.     register TkTextSegment *segPtr;
  2575.     int numChildren, numLines, toggleCount, minChildren;
  2576.     if (nodePtr->parentPtr != NULL) {
  2577. minChildren = MIN_CHILDREN;
  2578.     } else if (nodePtr->level > 0) {
  2579. minChildren = 2;
  2580.     } else  {
  2581. minChildren = 1;
  2582.     }
  2583.     if ((nodePtr->numChildren < minChildren)
  2584.     || (nodePtr->numChildren > MAX_CHILDREN)) {
  2585. panic("CheckNodeConsistency: bad child count (%d)",
  2586. nodePtr->numChildren);
  2587.     }
  2588.     numChildren = 0;
  2589.     numLines = 0;
  2590.     if (nodePtr->level == 0) {
  2591. for (linePtr = nodePtr->children.linePtr; linePtr != NULL;
  2592. linePtr = linePtr->nextPtr) {
  2593.     if (linePtr->parentPtr != nodePtr) {
  2594. panic("CheckNodeConsistency: line doesn't point to parent");
  2595.     }
  2596.     if (linePtr->segPtr == NULL) {
  2597. panic("CheckNodeConsistency: line has no segments");
  2598.     }
  2599.     for (segPtr = linePtr->segPtr; segPtr != NULL;
  2600.     segPtr = segPtr->nextPtr) {
  2601. if (segPtr->typePtr->checkProc != NULL) {
  2602.     (*segPtr->typePtr->checkProc)(segPtr, linePtr);
  2603. }
  2604. if ((segPtr->size == 0) && (!segPtr->typePtr->leftGravity)
  2605. && (segPtr->nextPtr != NULL)
  2606. && (segPtr->nextPtr->size == 0)
  2607. && (segPtr->nextPtr->typePtr->leftGravity)) {
  2608.     panic("CheckNodeConsistency: wrong segment order for gravity");
  2609. }
  2610. if ((segPtr->nextPtr == NULL)
  2611. && (segPtr->typePtr != &tkTextCharType)) {
  2612.     panic("CheckNodeConsistency: line ended with wrong type");
  2613. }
  2614.     }
  2615.     numChildren++;
  2616.     numLines++;
  2617. }
  2618.     } else {
  2619. for (childNodePtr = nodePtr->children.nodePtr; childNodePtr != NULL;
  2620. childNodePtr = childNodePtr->nextPtr) {
  2621.     if (childNodePtr->parentPtr != nodePtr) {
  2622. panic("CheckNodeConsistency: node doesn't point to parent");
  2623.     }
  2624.     if (childNodePtr->level != (nodePtr->level-1)) {
  2625. panic("CheckNodeConsistency: level mismatch (%d %d)",
  2626. nodePtr->level, childNodePtr->level);
  2627.     }
  2628.     CheckNodeConsistency(childNodePtr);
  2629.     for (summaryPtr = childNodePtr->summaryPtr; summaryPtr != NULL;
  2630. summaryPtr = summaryPtr->nextPtr) {
  2631. for (summaryPtr2 = nodePtr->summaryPtr; ;
  2632. summaryPtr2 = summaryPtr2->nextPtr) {
  2633.     if (summaryPtr2 == NULL) {
  2634. if (summaryPtr->tagPtr->tagRootPtr == nodePtr) {
  2635.     break;
  2636. }
  2637. panic("CheckNodeConsistency: node tag "%s" not %s",
  2638. summaryPtr->tagPtr->name,
  2639. "present in parent summaries");
  2640.     }
  2641.     if (summaryPtr->tagPtr == summaryPtr2->tagPtr) {
  2642. break;
  2643.     }
  2644. }
  2645.     }
  2646.     numChildren++;
  2647.     numLines += childNodePtr->numLines;
  2648. }
  2649.     }
  2650.     if (numChildren != nodePtr->numChildren) {
  2651. panic("CheckNodeConsistency: mismatch in numChildren (%d %d)",
  2652. numChildren, nodePtr->numChildren);
  2653.     }
  2654.     if (numLines != nodePtr->numLines) {
  2655. panic("CheckNodeConsistency: mismatch in numLines (%d %d)",
  2656. numLines, nodePtr->numLines);
  2657.     }
  2658.     for (summaryPtr = nodePtr->summaryPtr; summaryPtr != NULL;
  2659.     summaryPtr = summaryPtr->nextPtr) {
  2660. if (summaryPtr->tagPtr->toggleCount == summaryPtr->toggleCount) {
  2661.     panic("CheckNodeConsistency: found unpruned root for "%s"",
  2662. summaryPtr->tagPtr->name);
  2663. }
  2664. toggleCount = 0;
  2665. if (nodePtr->level == 0) {
  2666.     for (linePtr = nodePtr->children.linePtr; linePtr != NULL;
  2667.     linePtr = linePtr->nextPtr) {
  2668. for (segPtr = linePtr->segPtr; segPtr != NULL;
  2669. segPtr = segPtr->nextPtr) {
  2670.     if ((segPtr->typePtr != &tkTextToggleOnType)
  2671.     && (segPtr->typePtr != &tkTextToggleOffType)) {
  2672. continue;
  2673.     }
  2674.     if (segPtr->body.toggle.tagPtr == summaryPtr->tagPtr) {
  2675. toggleCount ++;
  2676.     }
  2677. }
  2678.     }
  2679. } else {
  2680.     for (childNodePtr = nodePtr->children.nodePtr;
  2681.     childNodePtr != NULL;
  2682.     childNodePtr = childNodePtr->nextPtr) {
  2683. for (summaryPtr2 = childNodePtr->summaryPtr;
  2684. summaryPtr2 != NULL;
  2685. summaryPtr2 = summaryPtr2->nextPtr) {
  2686.     if (summaryPtr2->tagPtr == summaryPtr->tagPtr) {
  2687. toggleCount += summaryPtr2->toggleCount;
  2688.     }
  2689. }
  2690.     }
  2691. }
  2692. if (toggleCount != summaryPtr->toggleCount) {
  2693.     panic("CheckNodeConsistency: mismatch in toggleCount (%d %d)",
  2694.     toggleCount, summaryPtr->toggleCount);
  2695. }
  2696. for (summaryPtr2 = summaryPtr->nextPtr; summaryPtr2 != NULL;
  2697. summaryPtr2 = summaryPtr2->nextPtr) {
  2698.     if (summaryPtr2->tagPtr == summaryPtr->tagPtr) {
  2699. panic("CheckNodeConsistency: duplicated node tag: %s",
  2700. summaryPtr->tagPtr->name);
  2701.     }
  2702. }
  2703.     }
  2704. }
  2705. /*
  2706.  *----------------------------------------------------------------------
  2707.  *
  2708.  * Rebalance --
  2709.  *
  2710.  * This procedure is called when a node of a B-tree appears to be
  2711.  * out of balance (too many children, or too few).  It rebalances
  2712.  * that node and all of its ancestors in the tree.
  2713.  *
  2714.  * Results:
  2715.  * None.
  2716.  *
  2717.  * Side effects:
  2718.  * The internal structure of treePtr may change.
  2719.  *
  2720.  *----------------------------------------------------------------------
  2721.  */
  2722. static void
  2723. Rebalance(treePtr, nodePtr)
  2724.     BTree *treePtr; /* Tree that is being rebalanced. */
  2725.     register Node *nodePtr; /* Node that may be out of balance. */
  2726. {
  2727.     /*
  2728.      * Loop over the entire ancestral chain of the node, working up
  2729.      * through the tree one node at a time until the root node has
  2730.      * been processed.
  2731.      */
  2732.     for ( ; nodePtr != NULL; nodePtr = nodePtr->parentPtr) {
  2733. register Node *newPtr, *childPtr;
  2734. register TkTextLine *linePtr;
  2735. int i;
  2736. /*
  2737.  * Check to see if the node has too many children.  If it does,
  2738.  * then split off all but the first MIN_CHILDREN into a separate
  2739.  * node following the original one.  Then repeat until the
  2740.  * node has a decent size.
  2741.  */
  2742. if (nodePtr->numChildren > MAX_CHILDREN) {
  2743.     while (1) {
  2744. /*
  2745.  * If the node being split is the root node, then make a
  2746.  * new root node above it first.
  2747.  */
  2748.     
  2749. if (nodePtr->parentPtr == NULL) {
  2750.     newPtr = (Node *) ckalloc(sizeof(Node));
  2751.     newPtr->parentPtr = NULL;
  2752.     newPtr->nextPtr = NULL;
  2753.     newPtr->summaryPtr = NULL;
  2754.     newPtr->level = nodePtr->level + 1;
  2755.     newPtr->children.nodePtr = nodePtr;
  2756.     newPtr->numChildren = 1;
  2757.     newPtr->numLines = nodePtr->numLines;
  2758.     RecomputeNodeCounts(newPtr);
  2759.     treePtr->rootPtr = newPtr;
  2760. }
  2761. newPtr = (Node *) ckalloc(sizeof(Node));
  2762. newPtr->parentPtr = nodePtr->parentPtr;
  2763. newPtr->nextPtr = nodePtr->nextPtr;
  2764. nodePtr->nextPtr = newPtr;
  2765. newPtr->summaryPtr = NULL;
  2766. newPtr->level = nodePtr->level;
  2767. newPtr->numChildren = nodePtr->numChildren - MIN_CHILDREN;
  2768. if (nodePtr->level == 0) {
  2769.     for (i = MIN_CHILDREN-1,
  2770.     linePtr = nodePtr->children.linePtr;
  2771.     i > 0; i--, linePtr = linePtr->nextPtr) {
  2772. /* Empty loop body. */
  2773.     }
  2774.     newPtr->children.linePtr = linePtr->nextPtr;
  2775.     linePtr->nextPtr = NULL;
  2776. } else {
  2777.     for (i = MIN_CHILDREN-1,
  2778.     childPtr = nodePtr->children.nodePtr;
  2779.     i > 0; i--, childPtr = childPtr->nextPtr) {
  2780. /* Empty loop body. */
  2781.     }
  2782.     newPtr->children.nodePtr = childPtr->nextPtr;
  2783.     childPtr->nextPtr = NULL;
  2784. }
  2785. RecomputeNodeCounts(nodePtr);
  2786. nodePtr->parentPtr->numChildren++;
  2787. nodePtr = newPtr;
  2788. if (nodePtr->numChildren <= MAX_CHILDREN) {
  2789.     RecomputeNodeCounts(nodePtr);
  2790.     break;
  2791. }
  2792.     }
  2793. }
  2794. while (nodePtr->numChildren < MIN_CHILDREN) {
  2795.     register Node *otherPtr;
  2796.     Node *halfwayNodePtr = NULL; /* Initialization needed only */
  2797.     TkTextLine *halfwayLinePtr = NULL; /* to prevent cc warnings. */
  2798.     int totalChildren, firstChildren, i;
  2799.     /*
  2800.      * Too few children for this node.  If this is the root then,
  2801.      * it's OK for it to have less than MIN_CHILDREN children
  2802.      * as long as it's got at least two.  If it has only one
  2803.      * (and isn't at level 0), then chop the root node out of
  2804.      * the tree and use its child as the new root.
  2805.      */
  2806.     if (nodePtr->parentPtr == NULL) {
  2807. if ((nodePtr->numChildren == 1) && (nodePtr->level > 0)) {
  2808.     treePtr->rootPtr = nodePtr->children.nodePtr;
  2809.     treePtr->rootPtr->parentPtr = NULL;
  2810.     DeleteSummaries(nodePtr->summaryPtr);
  2811.     ckfree((char *) nodePtr);
  2812. }
  2813. return;
  2814.     }
  2815.     /*
  2816.      * Not the root.  Make sure that there are siblings to
  2817.      * balance with.
  2818.      */
  2819.     if (nodePtr->parentPtr->numChildren < 2) {
  2820. Rebalance(treePtr, nodePtr->parentPtr);
  2821. continue;
  2822.     }
  2823.     /*
  2824.      * Find a sibling neighbor to borrow from, and arrange for
  2825.      * nodePtr to be the earlier of the pair.
  2826.      */
  2827.     if (nodePtr->nextPtr == NULL) {
  2828. for (otherPtr = nodePtr->parentPtr->children.nodePtr;
  2829. otherPtr->nextPtr != nodePtr;
  2830. otherPtr = otherPtr->nextPtr) {
  2831.     /* Empty loop body. */
  2832. }
  2833. nodePtr = otherPtr;
  2834.     }
  2835.     otherPtr = nodePtr->nextPtr;
  2836.     /*
  2837.      * We're going to either merge the two siblings together
  2838.      * into one node or redivide the children among them to
  2839.      * balance their loads.  As preparation, join their two
  2840.      * child lists into a single list and remember the half-way
  2841.      * point in the list.
  2842.      */
  2843.     totalChildren = nodePtr->numChildren + otherPtr->numChildren;
  2844.     firstChildren = totalChildren/2;
  2845.     if (nodePtr->children.nodePtr == NULL) {
  2846. nodePtr->children = otherPtr->children;
  2847. otherPtr->children.nodePtr = NULL;
  2848. otherPtr->children.linePtr = NULL;
  2849.     }
  2850.     if (nodePtr->level == 0) {
  2851. register TkTextLine *linePtr;
  2852. for (linePtr = nodePtr->children.linePtr, i = 1;
  2853. linePtr->nextPtr != NULL;
  2854. linePtr = linePtr->nextPtr, i++) {
  2855.     if (i == firstChildren) {
  2856. halfwayLinePtr = linePtr;
  2857.     }
  2858. }
  2859. linePtr->nextPtr = otherPtr->children.linePtr;
  2860. while (i <= firstChildren) {
  2861.     halfwayLinePtr = linePtr;
  2862.     linePtr = linePtr->nextPtr;
  2863.     i++;
  2864. }
  2865.     } else {
  2866. register Node *childPtr;
  2867. for (childPtr = nodePtr->children.nodePtr, i = 1;
  2868. childPtr->nextPtr != NULL;
  2869. childPtr = childPtr->nextPtr, i++) {
  2870.     if (i <= firstChildren) {
  2871. if (i == firstChildren) {
  2872.     halfwayNodePtr = childPtr;
  2873. }
  2874.     }
  2875. }
  2876. childPtr->nextPtr = otherPtr->children.nodePtr;
  2877. while (i <= firstChildren) {
  2878.     halfwayNodePtr = childPtr;
  2879.     childPtr = childPtr->nextPtr;
  2880.     i++;
  2881. }
  2882.     }
  2883.     /*
  2884.      * If the two siblings can simply be merged together, do it.
  2885.      */
  2886.     if (totalChildren <= MAX_CHILDREN) {
  2887. RecomputeNodeCounts(nodePtr);
  2888. nodePtr->nextPtr = otherPtr->nextPtr;
  2889. nodePtr->parentPtr->numChildren--;
  2890. DeleteSummaries(otherPtr->summaryPtr);
  2891. ckfree((char *) otherPtr);
  2892. continue;
  2893.     }
  2894.     /*
  2895.      * The siblings can't be merged, so just divide their
  2896.      * children evenly between them.
  2897.      */
  2898.     if (nodePtr->level == 0) {
  2899. otherPtr->children.linePtr = halfwayLinePtr->nextPtr;
  2900. halfwayLinePtr->nextPtr = NULL;
  2901.     } else {
  2902. otherPtr->children.nodePtr = halfwayNodePtr->nextPtr;
  2903. halfwayNodePtr->nextPtr = NULL;
  2904.     }
  2905.     RecomputeNodeCounts(nodePtr);
  2906.     RecomputeNodeCounts(otherPtr);
  2907. }
  2908.     }
  2909. }
  2910. /*
  2911.  *----------------------------------------------------------------------
  2912.  *
  2913.  * RecomputeNodeCounts --
  2914.  *
  2915.  * This procedure is called to recompute all the counts in a node
  2916.  * (tags, child information, etc.) by scanning the information in
  2917.  * its descendants.  This procedure is called during rebalancing
  2918.  * when a node's child structure has changed.
  2919.  *
  2920.  * Results:
  2921.  * None.
  2922.  *
  2923.  * Side effects:
  2924.  * The tag counts for nodePtr are modified to reflect its current
  2925.  * child structure, as are its numChildren and numLines fields.
  2926.  * Also, all of the childrens' parentPtr fields are made to point
  2927.  * to nodePtr.
  2928.  *
  2929.  *----------------------------------------------------------------------
  2930.  */
  2931. static void
  2932. RecomputeNodeCounts(nodePtr)
  2933.     register Node *nodePtr; /* Node whose tag summary information
  2934.  * must be recomputed. */
  2935. {
  2936.     register Summary *summaryPtr, *summaryPtr2;
  2937.     register Node *childPtr;
  2938.     register TkTextLine *linePtr;
  2939.     register TkTextSegment *segPtr;
  2940.     TkTextTag *tagPtr;
  2941.     /*
  2942.      * Zero out all the existing counts for the node, but don't delete
  2943.      * the existing Summary records (most of them will probably be reused).
  2944.      */
  2945.     for (summaryPtr = nodePtr->summaryPtr; summaryPtr != NULL;
  2946.     summaryPtr = summaryPtr->nextPtr) {
  2947. summaryPtr->toggleCount = 0;
  2948.     }
  2949.     nodePtr->numChildren = 0;
  2950.     nodePtr->numLines = 0;
  2951.     /*
  2952.      * Scan through the children, adding the childrens' tag counts into
  2953.      * the node's tag counts and adding new Summary structures if
  2954.      * necessary.
  2955.      */
  2956.     if (nodePtr->level == 0) {
  2957. for (linePtr = nodePtr->children.linePtr; linePtr != NULL;
  2958. linePtr = linePtr->nextPtr) {
  2959.     nodePtr->numChildren++;
  2960.     nodePtr->numLines++;
  2961.     linePtr->parentPtr = nodePtr;
  2962.     for (segPtr = linePtr->segPtr; segPtr != NULL;
  2963.     segPtr = segPtr->nextPtr) {
  2964. if (((segPtr->typePtr != &tkTextToggleOnType)
  2965. && (segPtr->typePtr != &tkTextToggleOffType))
  2966. || !(segPtr->body.toggle.inNodeCounts)) {
  2967.     continue;
  2968. }
  2969. tagPtr = segPtr->body.toggle.tagPtr;
  2970. for (summaryPtr = nodePtr->summaryPtr; ;
  2971. summaryPtr = summaryPtr->nextPtr) {
  2972.     if (summaryPtr == NULL) {
  2973. summaryPtr = (Summary *) ckalloc(sizeof(Summary));
  2974. summaryPtr->tagPtr = tagPtr;
  2975. summaryPtr->toggleCount = 1;
  2976. summaryPtr->nextPtr = nodePtr->summaryPtr;
  2977. nodePtr->summaryPtr = summaryPtr;
  2978. break;
  2979.     }
  2980.     if (summaryPtr->tagPtr == tagPtr) {
  2981. summaryPtr->toggleCount++;
  2982. break;
  2983.     }
  2984. }
  2985.     }
  2986. }
  2987.     } else {
  2988. for (childPtr = nodePtr->children.nodePtr; childPtr != NULL;
  2989. childPtr = childPtr->nextPtr) {
  2990.     nodePtr->numChildren++;
  2991.     nodePtr->numLines += childPtr->numLines;
  2992.     childPtr->parentPtr = nodePtr;
  2993.     for (summaryPtr2 = childPtr->summaryPtr; summaryPtr2 != NULL;
  2994.     summaryPtr2 = summaryPtr2->nextPtr) {
  2995. for (summaryPtr = nodePtr->summaryPtr; ;
  2996. summaryPtr = summaryPtr->nextPtr) {
  2997.     if (summaryPtr == NULL) {
  2998. summaryPtr = (Summary *) ckalloc(sizeof(Summary));
  2999. summaryPtr->tagPtr = summaryPtr2->tagPtr;
  3000. summaryPtr->toggleCount = summaryPtr2->toggleCount;
  3001. summaryPtr->nextPtr = nodePtr->summaryPtr;
  3002. nodePtr->summaryPtr = summaryPtr;
  3003. break;
  3004.     }
  3005.     if (summaryPtr->tagPtr == summaryPtr2->tagPtr) {
  3006. summaryPtr->toggleCount += summaryPtr2->toggleCount;
  3007. break;
  3008.     }
  3009. }
  3010.     }
  3011. }
  3012.     }
  3013.     /*
  3014.      * Scan through the node's tag records again and delete any Summary
  3015.      * records that still have a zero count, or that have all the toggles.
  3016.      * The node with the children that account for all the tags toggles
  3017.      * have no summary information, and they become the tagRootPtr for the tag.
  3018.      */
  3019.     summaryPtr2 = NULL;
  3020.     for (summaryPtr = nodePtr->summaryPtr; summaryPtr != NULL; ) {
  3021. if (summaryPtr->toggleCount > 0 && 
  3022. summaryPtr->toggleCount < summaryPtr->tagPtr->toggleCount) {
  3023.     if (nodePtr->level == summaryPtr->tagPtr->tagRootPtr->level) {
  3024. /*
  3025.  * The tag's root node split and some toggles left.
  3026.  * The tag root must move up a level.
  3027.  */
  3028. summaryPtr->tagPtr->tagRootPtr = nodePtr->parentPtr;
  3029.     }
  3030.     summaryPtr2 = summaryPtr;
  3031.     summaryPtr = summaryPtr->nextPtr;
  3032.     continue;
  3033. }
  3034. if (summaryPtr->toggleCount == summaryPtr->tagPtr->toggleCount) {
  3035.     /*
  3036.      * A node merge has collected all the toggles under one node.
  3037.      * Push the root down to this level.
  3038.      */
  3039.     summaryPtr->tagPtr->tagRootPtr = nodePtr;
  3040. }
  3041. if (summaryPtr2 != NULL) {
  3042.     summaryPtr2->nextPtr = summaryPtr->nextPtr;
  3043.     ckfree((char *) summaryPtr);
  3044.     summaryPtr = summaryPtr2->nextPtr;
  3045. } else {
  3046.     nodePtr->summaryPtr = summaryPtr->nextPtr;
  3047.     ckfree((char *) summaryPtr);
  3048.     summaryPtr = nodePtr->summaryPtr;
  3049. }
  3050.     }
  3051. }
  3052. /*
  3053.  *----------------------------------------------------------------------
  3054.  *
  3055.  * TkBTreeNumLines --
  3056.  *
  3057.  * This procedure returns a count of the number of lines of
  3058.  * text present in a given B-tree.
  3059.  *
  3060.  * Results:
  3061.  * The return value is a count of the number of usable lines
  3062.  * in tree (i.e. it doesn't include the dummy line that is just
  3063.  *  used to mark the end of the tree).
  3064.  *
  3065.  * Side effects:
  3066.  * None.
  3067.  *
  3068.  *----------------------------------------------------------------------
  3069.  */
  3070. int
  3071. TkBTreeNumLines(tree)
  3072.     TkTextBTree tree; /* Information about tree. */
  3073. {
  3074.     BTree *treePtr = (BTree *) tree;
  3075.     return treePtr->rootPtr->numLines - 1;
  3076. }
  3077. /*
  3078.  *--------------------------------------------------------------
  3079.  *
  3080.  * CharSplitProc --
  3081.  *
  3082.  * This procedure implements splitting for character segments.
  3083.  *
  3084.  * Results:
  3085.  * The return value is a pointer to a chain of two segments
  3086.  * that have the same characters as segPtr except split
  3087.  * among the two segments.
  3088.  *
  3089.  * Side effects:
  3090.  * Storage for segPtr is freed.
  3091.  *
  3092.  *--------------------------------------------------------------
  3093.  */
  3094. static TkTextSegment *
  3095. CharSplitProc(segPtr, index)
  3096.     TkTextSegment *segPtr; /* Pointer to segment to split. */
  3097.     int index; /* Position within segment at which
  3098.  * to split. */
  3099. {
  3100.     TkTextSegment *newPtr1, *newPtr2;
  3101.     newPtr1 = (TkTextSegment *) ckalloc(CSEG_SIZE(index));
  3102.     newPtr2 = (TkTextSegment *) ckalloc(
  3103.     CSEG_SIZE(segPtr->size - index));
  3104.     newPtr1->typePtr = &tkTextCharType;
  3105.     newPtr1->nextPtr = newPtr2;
  3106.     newPtr1->size = index;
  3107.     strncpy(newPtr1->body.chars, segPtr->body.chars, (size_t) index);
  3108.     newPtr1->body.chars[index] = 0;
  3109.     newPtr2->typePtr = &tkTextCharType;
  3110.     newPtr2->nextPtr = segPtr->nextPtr;
  3111.     newPtr2->size = segPtr->size - index;
  3112.     strcpy(newPtr2->body.chars, segPtr->body.chars + index);
  3113.     ckfree((char*) segPtr);
  3114.     return newPtr1;
  3115. }
  3116. /*
  3117.  *--------------------------------------------------------------
  3118.  *
  3119.  * CharCleanupProc --
  3120.  *
  3121.  * This procedure merges adjacent character segments into
  3122.  * a single character segment, if possible.
  3123.  *
  3124.  * Results:
  3125.  * The return value is a pointer to the first segment in
  3126.  * the (new) list of segments that used to start with segPtr.
  3127.  *
  3128.  * Side effects:
  3129.  * Storage for the segments may be allocated and freed.
  3130.  *
  3131.  *--------------------------------------------------------------
  3132.  */
  3133. /* ARGSUSED */
  3134. static TkTextSegment *
  3135. CharCleanupProc(segPtr, linePtr)
  3136.     TkTextSegment *segPtr; /* Pointer to first of two adjacent
  3137.  * segments to join. */
  3138.     TkTextLine *linePtr; /* Line containing segments (not
  3139.  * used). */
  3140. {
  3141.     TkTextSegment *segPtr2, *newPtr;
  3142.     segPtr2 = segPtr->nextPtr;
  3143.     if ((segPtr2 == NULL) || (segPtr2->typePtr != &tkTextCharType)) {
  3144. return segPtr;
  3145.     }
  3146.     newPtr = (TkTextSegment *) ckalloc(CSEG_SIZE(
  3147.     segPtr->size + segPtr2->size));
  3148.     newPtr->typePtr = &tkTextCharType;
  3149.     newPtr->nextPtr = segPtr2->nextPtr;
  3150.     newPtr->size = segPtr->size + segPtr2->size;
  3151.     strcpy(newPtr->body.chars, segPtr->body.chars);
  3152.     strcpy(newPtr->body.chars + segPtr->size, segPtr2->body.chars);
  3153.     ckfree((char*) segPtr);
  3154.     ckfree((char*) segPtr2);
  3155.     return newPtr;
  3156. }
  3157. /*
  3158.  *--------------------------------------------------------------
  3159.  *
  3160.  * CharDeleteProc --
  3161.  *
  3162.  * This procedure is invoked to delete a character segment.
  3163.  *
  3164.  * Results:
  3165.  * Always returns 0 to indicate that the segment was deleted.
  3166.  *
  3167.  * Side effects:
  3168.  * Storage for the segment is freed.
  3169.  *
  3170.  *--------------------------------------------------------------
  3171.  */
  3172. /* ARGSUSED */
  3173. static int
  3174. CharDeleteProc(segPtr, linePtr, treeGone)
  3175.     TkTextSegment *segPtr; /* Segment to delete. */
  3176.     TkTextLine *linePtr; /* Line containing segment. */
  3177.     int treeGone; /* Non-zero means the entire tree is
  3178.  * being deleted, so everything must
  3179.  * get cleaned up. */
  3180. {
  3181.     ckfree((char*) segPtr);
  3182.     return 0;
  3183. }
  3184. /*
  3185.  *--------------------------------------------------------------
  3186.  *
  3187.  * CharCheckProc --
  3188.  *
  3189.  * This procedure is invoked to perform consistency checks
  3190.  * on character segments.
  3191.  *
  3192.  * Results:
  3193.  * None.
  3194.  *
  3195.  * Side effects:
  3196.  * If the segment isn't inconsistent then the procedure
  3197.  * panics.
  3198.  *
  3199.  *--------------------------------------------------------------
  3200.  */
  3201. /* ARGSUSED */
  3202. static void
  3203. CharCheckProc(segPtr, linePtr)
  3204.     TkTextSegment *segPtr; /* Segment to check. */
  3205.     TkTextLine *linePtr; /* Line containing segment. */
  3206. {
  3207.     /*
  3208.      * Make sure that the segment contains the number of
  3209.      * characters indicated by its header, and that the last
  3210.      * segment in a line ends in a newline.  Also make sure
  3211.      * that there aren't ever two character segments adjacent
  3212.      * to each other:  they should be merged together.
  3213.      */
  3214.     if (segPtr->size <= 0) {
  3215. panic("CharCheckProc: segment has size <= 0");
  3216.     }
  3217.     if (strlen(segPtr->body.chars) != (size_t) segPtr->size) {
  3218. panic("CharCheckProc: segment has wrong size");
  3219.     }
  3220.     if (segPtr->nextPtr == NULL) {
  3221. if (segPtr->body.chars[segPtr->size-1] != 'n') {
  3222.     panic("CharCheckProc: line doesn't end with newline");
  3223. }
  3224.     } else {
  3225. if (segPtr->nextPtr->typePtr == &tkTextCharType) {
  3226.     panic("CharCheckProc: adjacent character segments weren't merged");
  3227. }
  3228.     }
  3229. }
  3230. /*
  3231.  *--------------------------------------------------------------
  3232.  *
  3233.  * ToggleDeleteProc --
  3234.  *
  3235.  * This procedure is invoked to delete toggle segments.
  3236.  *
  3237.  * Results:
  3238.  * Returns 1 to indicate that the segment may not be deleted,
  3239.  * unless the entire B-tree is going away.
  3240.  *
  3241.  * Side effects:
  3242.  * If the tree is going away then the toggle's memory is
  3243.  * freed;  otherwise the toggle counts in nodes above the
  3244.  * segment get updated.
  3245.  *
  3246.  *--------------------------------------------------------------
  3247.  */
  3248. static int
  3249. ToggleDeleteProc(segPtr, linePtr, treeGone)
  3250.     TkTextSegment *segPtr; /* Segment to check. */
  3251.     TkTextLine *linePtr; /* Line containing segment. */
  3252.     int treeGone; /* Non-zero means the entire tree is
  3253.  * being deleted, so everything must
  3254.  * get cleaned up. */
  3255. {
  3256.     if (treeGone) {
  3257. ckfree((char *) segPtr);
  3258. return 0;
  3259.     }
  3260.     /*
  3261.      * This toggle is in the middle of a range of characters that's
  3262.      * being deleted.  Refuse to die.  We'll be moved to the end of
  3263.      * the deleted range and our cleanup procedure will be called
  3264.      * later.  Decrement node toggle counts here, and set a flag
  3265.      * so we'll re-increment them in the cleanup procedure.
  3266.      */
  3267.     if (segPtr->body.toggle.inNodeCounts) {
  3268. ChangeNodeToggleCount(linePtr->parentPtr,
  3269. segPtr->body.toggle.tagPtr, -1);
  3270. segPtr->body.toggle.inNodeCounts = 0;
  3271.     }
  3272.     return 1;
  3273. }
  3274. /*
  3275.  *--------------------------------------------------------------
  3276.  *
  3277.  * ToggleCleanupProc --
  3278.  *
  3279.  * This procedure is called when a toggle is part of a line that's
  3280.  * been modified in some way.  It's invoked after the
  3281.  * modifications are complete.
  3282.  *
  3283.  * Results:
  3284.  * The return value is the head segment in a new list
  3285.  * that is to replace the tail of the line that used to
  3286.  * start at segPtr.  This allows the procedure to delete
  3287.  * or modify segPtr.
  3288.  *
  3289.  * Side effects:
  3290.  * Toggle counts in the nodes above the new line will be
  3291.  * updated if they're not already.  Toggles may be collapsed
  3292.  * if there are duplicate toggles at the same position.
  3293.  *
  3294.  *--------------------------------------------------------------
  3295.  */
  3296. static TkTextSegment *
  3297. ToggleCleanupProc(segPtr, linePtr)
  3298.     TkTextSegment *segPtr; /* Segment to check. */
  3299.     TkTextLine *linePtr; /* Line that now contains segment. */
  3300. {
  3301.     TkTextSegment *segPtr2, *prevPtr;
  3302.     int counts;
  3303.     /*
  3304.      * If this is a toggle-off segment, look ahead through the next
  3305.      * segments to see if there's a toggle-on segment for the same tag
  3306.      * before any segments with non-zero size.  If so then the two
  3307.      * toggles cancel each other;  remove them both.
  3308.      */
  3309.     if (segPtr->typePtr == &tkTextToggleOffType) {
  3310. for (prevPtr = segPtr, segPtr2 = prevPtr->nextPtr;
  3311. (segPtr2 != NULL) && (segPtr2->size == 0);
  3312. prevPtr = segPtr2, segPtr2 = prevPtr->nextPtr) {
  3313.     if (segPtr2->typePtr != &tkTextToggleOnType) {
  3314. continue;
  3315.     }
  3316.     if (segPtr2->body.toggle.tagPtr != segPtr->body.toggle.tagPtr) {
  3317. continue;
  3318.     }
  3319.     counts = segPtr->body.toggle.inNodeCounts
  3320.     + segPtr2->body.toggle.inNodeCounts;
  3321.     if (counts != 0) {
  3322. ChangeNodeToggleCount(linePtr->parentPtr,
  3323. segPtr->body.toggle.tagPtr, -counts);
  3324.     }
  3325.     prevPtr->nextPtr = segPtr2->nextPtr;
  3326.     ckfree((char *) segPtr2);
  3327.     segPtr2 = segPtr->nextPtr;
  3328.     ckfree((char *) segPtr);
  3329.     return segPtr2;
  3330. }
  3331.     }
  3332.     if (!segPtr->body.toggle.inNodeCounts) {
  3333. ChangeNodeToggleCount(linePtr->parentPtr,
  3334. segPtr->body.toggle.tagPtr, 1);
  3335. segPtr->body.toggle.inNodeCounts = 1;
  3336.     }
  3337.     return segPtr;
  3338. }
  3339. /*
  3340.  *--------------------------------------------------------------
  3341.  *
  3342.  * ToggleLineChangeProc --
  3343.  *
  3344.  * This procedure is invoked when a toggle segment is about
  3345.  * to move from one line to another.
  3346.  *
  3347.  * Results:
  3348.  * None.
  3349.  *
  3350.  * Side effects:
  3351.  * Toggle counts are decremented in the nodes above the line.
  3352.  *
  3353.  *--------------------------------------------------------------
  3354.  */
  3355. static void
  3356. ToggleLineChangeProc(segPtr, linePtr)
  3357.     TkTextSegment *segPtr; /* Segment to check. */
  3358.     TkTextLine *linePtr; /* Line that used to contain segment. */
  3359. {
  3360.     if (segPtr->body.toggle.inNodeCounts) {
  3361. ChangeNodeToggleCount(linePtr->parentPtr,
  3362. segPtr->body.toggle.tagPtr, -1);
  3363. segPtr->body.toggle.inNodeCounts = 0;
  3364.     }
  3365. }
  3366. /*
  3367.  *--------------------------------------------------------------
  3368.  *
  3369.  * ToggleCheckProc --
  3370.  *
  3371.  * This procedure is invoked to perform consistency checks
  3372.  * on toggle segments.
  3373.  *
  3374.  * Results:
  3375.  * None.
  3376.  *
  3377.  * Side effects:
  3378.  * If a consistency problem is found the procedure panics.
  3379.  *
  3380.  *--------------------------------------------------------------
  3381.  */
  3382. static void
  3383. ToggleCheckProc(segPtr, linePtr)
  3384.     TkTextSegment *segPtr; /* Segment to check. */
  3385.     TkTextLine *linePtr; /* Line containing segment. */
  3386. {
  3387.     register Summary *summaryPtr;
  3388.     int needSummary;
  3389.     if (segPtr->size != 0) {
  3390. panic("ToggleCheckProc: segment had non-zero size");
  3391.     }
  3392.     if (!segPtr->body.toggle.inNodeCounts) {
  3393. panic("ToggleCheckProc: toggle counts not updated in nodes");
  3394.     }
  3395.     needSummary = (segPtr->body.toggle.tagPtr->tagRootPtr != linePtr->parentPtr);
  3396.     for (summaryPtr = linePtr->parentPtr->summaryPtr; ;
  3397.     summaryPtr = summaryPtr->nextPtr) {
  3398. if (summaryPtr == NULL) {
  3399.     if (needSummary) {
  3400. panic("ToggleCheckProc: tag not present in node");
  3401.     } else {
  3402. break;
  3403.     }
  3404. }
  3405. if (summaryPtr->tagPtr == segPtr->body.toggle.tagPtr) {
  3406.     if (!needSummary) {
  3407. panic("ToggleCheckProc: tag present in root node summary");
  3408.     }
  3409.     break;
  3410. }
  3411.     }
  3412. }
  3413. /*
  3414.  *----------------------------------------------------------------------
  3415.  *
  3416.  * TkBTreeCharsInLine --
  3417.  *
  3418.  * This procedure returns a count of the number of characters
  3419.  * in a given line.
  3420.  *
  3421.  * Results:
  3422.  * The return value is the character count for linePtr.
  3423.  *
  3424.  * Side effects:
  3425.  * None.
  3426.  *
  3427.  *----------------------------------------------------------------------
  3428.  */
  3429. int
  3430. TkBTreeCharsInLine(linePtr)
  3431.     TkTextLine *linePtr; /* Line whose characters should be
  3432.  * counted. */
  3433. {
  3434.     TkTextSegment *segPtr;
  3435.     int count;
  3436.     count = 0;
  3437.     for (segPtr = linePtr->segPtr; segPtr != NULL; segPtr = segPtr->nextPtr) {
  3438. if (segPtr->typePtr == &tkTextCharType) {
  3439.     count += Tcl_NumUtfChars(segPtr->body.chars, segPtr->size);
  3440. } else {
  3441.     count += segPtr->size;
  3442. }
  3443.     }
  3444.     return count;
  3445. }
  3446. int
  3447. TkBTreeBytesInLine(linePtr)
  3448.     TkTextLine *linePtr; /* Line whose characters should be
  3449.  * counted. */
  3450. {
  3451.     TkTextSegment *segPtr;
  3452.     int count;
  3453.     count = 0;
  3454.     for (segPtr = linePtr->segPtr; segPtr != NULL; segPtr = segPtr->nextPtr) {
  3455. count += segPtr->size;
  3456.     }
  3457.     return count;
  3458. }