PageRenderTime 57ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/tags/SN-6.0/tk/generic/tkTextBTree.c

https://gitlab.com/OpenSourceMirror/sourcenav
C | 1756 lines | 921 code | 141 blank | 694 comment | 264 complexity | 22649187a12fc51006218309f4e38e8d MD5 | raw file
  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$
  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. 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 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. for (prevPtr = NULL, summaryPtr = nodePtr->summaryPtr;
  1207. summaryPtr != NULL;
  1208. prevPtr = summaryPtr, summaryPtr = summaryPtr->nextPtr) {
  1209. if (summaryPtr->tagPtr == tagPtr) {
  1210. break;
  1211. }
  1212. }
  1213. if (summaryPtr != NULL) {
  1214. summaryPtr->toggleCount += delta;
  1215. if (summaryPtr->toggleCount > 0 &&
  1216. summaryPtr->toggleCount < tagPtr->toggleCount) {
  1217. continue;
  1218. }
  1219. if (summaryPtr->toggleCount != 0) {
  1220. /*
  1221. * Should never find a node with max toggle count at this
  1222. * point (there shouldn't have been a summary entry in the
  1223. * first place).
  1224. */
  1225. panic("ChangeNodeToggleCount: bad toggle count (%d) max (%d)",
  1226. summaryPtr->toggleCount, tagPtr->toggleCount);
  1227. }
  1228. /*
  1229. * Zero toggle count; must remove this tag from the list.
  1230. */
  1231. if (prevPtr == NULL) {
  1232. nodePtr->summaryPtr = summaryPtr->nextPtr;
  1233. } else {
  1234. prevPtr->nextPtr = summaryPtr->nextPtr;
  1235. }
  1236. ckfree((char *) summaryPtr);
  1237. } else {
  1238. /*
  1239. * This tag isn't currently in the summary information list.
  1240. */
  1241. if (rootLevel == nodePtr->level) {
  1242. /*
  1243. * The old tag root is at the same level in the tree as this
  1244. * node, but it isn't at this node. Move the tag root up
  1245. * a level, in the hopes that it will now cover this node
  1246. * as well as the old root (if not, we'll move it up again
  1247. * the next time through the loop). To push it up one level
  1248. * we copy the original toggle count into the summary
  1249. * information at the old root and change the root to its
  1250. * parent node.
  1251. */
  1252. Node *rootNodePtr = tagPtr->tagRootPtr;
  1253. summaryPtr = (Summary *) ckalloc(sizeof(Summary));
  1254. summaryPtr->tagPtr = tagPtr;
  1255. summaryPtr->toggleCount = tagPtr->toggleCount - delta;
  1256. summaryPtr->nextPtr = rootNodePtr->summaryPtr;
  1257. rootNodePtr->summaryPtr = summaryPtr;
  1258. rootNodePtr = rootNodePtr->parentPtr;
  1259. rootLevel = rootNodePtr->level;
  1260. tagPtr->tagRootPtr = rootNodePtr;
  1261. }
  1262. summaryPtr = (Summary *) ckalloc(sizeof(Summary));
  1263. summaryPtr->tagPtr = tagPtr;
  1264. summaryPtr->toggleCount = delta;
  1265. summaryPtr->nextPtr = nodePtr->summaryPtr;
  1266. nodePtr->summaryPtr = summaryPtr;
  1267. }
  1268. }
  1269. /*
  1270. * If we've decremented the toggle count, then it may be necessary
  1271. * to push the tag root down one or more levels.
  1272. */
  1273. if (delta >= 0) {
  1274. return;
  1275. }
  1276. if (tagPtr->toggleCount == 0) {
  1277. tagPtr->tagRootPtr = (Node *) NULL;
  1278. return;
  1279. }
  1280. nodePtr = tagPtr->tagRootPtr;
  1281. while (nodePtr->level > 0) {
  1282. /*
  1283. * See if a single child node accounts for all of the tag's
  1284. * toggles. If so, push the root down one level.
  1285. */
  1286. for (node2Ptr = nodePtr->children.nodePtr;
  1287. node2Ptr != (Node *)NULL ;
  1288. node2Ptr = node2Ptr->nextPtr) {
  1289. for (prevPtr = NULL, summaryPtr = node2Ptr->summaryPtr;
  1290. summaryPtr != NULL;
  1291. prevPtr = summaryPtr, summaryPtr = summaryPtr->nextPtr) {
  1292. if (summaryPtr->tagPtr == tagPtr) {
  1293. break;
  1294. }
  1295. }
  1296. if (summaryPtr == NULL) {
  1297. continue;
  1298. }
  1299. if (summaryPtr->toggleCount != tagPtr->toggleCount) {
  1300. /*
  1301. * No node has all toggles, so the root is still valid.
  1302. */
  1303. return;
  1304. }
  1305. /*
  1306. * This node has all the toggles, so push down the root.
  1307. */
  1308. if (prevPtr == NULL) {
  1309. node2Ptr->summaryPtr = summaryPtr->nextPtr;
  1310. } else {
  1311. prevPtr->nextPtr = summaryPtr->nextPtr;
  1312. }
  1313. ckfree((char *) summaryPtr);
  1314. tagPtr->tagRootPtr = node2Ptr;
  1315. break;
  1316. }
  1317. nodePtr = tagPtr->tagRootPtr;
  1318. }
  1319. }
  1320. /*
  1321. *----------------------------------------------------------------------
  1322. *
  1323. * FindTagStart --
  1324. *
  1325. * Find the start of the first range of a tag.
  1326. *
  1327. * Results:
  1328. * The return value is a pointer to the first tag toggle segment
  1329. * for the tag. This can be either a tagon or tagoff segments because
  1330. * of the way TkBTreeAdd removes a tag.
  1331. * Sets *indexPtr to be the index of the tag toggle.
  1332. *
  1333. * Side effects:
  1334. * None.
  1335. *
  1336. *----------------------------------------------------------------------
  1337. */
  1338. static TkTextSegment *
  1339. FindTagStart(tree, tagPtr, indexPtr)
  1340. TkTextBTree tree; /* Tree to search within */
  1341. TkTextTag *tagPtr; /* Tag to search for. */
  1342. TkTextIndex *indexPtr; /* Return - index information */
  1343. {
  1344. register Node *nodePtr;
  1345. register TkTextLine *linePtr;
  1346. register TkTextSegment *segPtr;
  1347. register Summary *summaryPtr;
  1348. int offset;
  1349. nodePtr = tagPtr->tagRootPtr;
  1350. if (nodePtr == (Node *) NULL) {
  1351. return NULL;
  1352. }
  1353. /*
  1354. * Search from the root of the subtree that contains the tag down
  1355. * to the level 0 node.
  1356. */
  1357. while (nodePtr->level > 0) {
  1358. for (nodePtr = nodePtr->children.nodePtr ; nodePtr != (Node *) NULL;
  1359. nodePtr = nodePtr->nextPtr) {
  1360. for (summaryPtr = nodePtr->summaryPtr ; summaryPtr != NULL;
  1361. summaryPtr = summaryPtr->nextPtr) {
  1362. if (summaryPtr->tagPtr == tagPtr) {
  1363. goto gotNodeWithTag;
  1364. }
  1365. }
  1366. }
  1367. gotNodeWithTag:
  1368. continue;
  1369. }
  1370. /*
  1371. * Work through the lines attached to the level-0 node.
  1372. */
  1373. for (linePtr = nodePtr->children.linePtr; linePtr != (TkTextLine *) NULL;
  1374. linePtr = linePtr->nextPtr) {
  1375. for (offset = 0, segPtr = linePtr->segPtr ; segPtr != NULL;
  1376. offset += segPtr->size, segPtr = segPtr->nextPtr) {
  1377. if (((segPtr->typePtr == &tkTextToggleOnType)
  1378. || (segPtr->typePtr == &tkTextToggleOffType))
  1379. && (segPtr->body.toggle.tagPtr == tagPtr)) {
  1380. /*
  1381. * It is possible that this is a tagoff tag, but that
  1382. * gets cleaned up later.
  1383. */
  1384. indexPtr->tree = tree;
  1385. indexPtr->linePtr = linePtr;
  1386. indexPtr->byteIndex = offset;
  1387. return segPtr;
  1388. }
  1389. }
  1390. }
  1391. return NULL;
  1392. }
  1393. /*
  1394. *----------------------------------------------------------------------
  1395. *
  1396. * FindTagEnd --
  1397. *
  1398. * Find the end of the last range of a tag.
  1399. *
  1400. * Results:
  1401. * The return value is a pointer to the last tag toggle segment
  1402. * for the tag. This can be either a tagon or tagoff segments because
  1403. * of the way TkBTreeAdd removes a tag.
  1404. * Sets *indexPtr to be the index of the tag toggle.
  1405. *
  1406. * Side effects:
  1407. * None.
  1408. *
  1409. *----------------------------------------------------------------------
  1410. */
  1411. static TkTextSegment *
  1412. FindTagEnd(tree, tagPtr, indexPtr)
  1413. TkTextBTree tree; /* Tree to search within */
  1414. TkTextTag *tagPtr; /* Tag to search for. */
  1415. TkTextIndex *indexPtr; /* Return - index information */
  1416. {
  1417. register Node *nodePtr, *lastNodePtr;
  1418. register TkTextLine *linePtr ,*lastLinePtr;
  1419. register TkTextSegment *segPtr, *lastSegPtr, *last2SegPtr;
  1420. register Summary *summaryPtr;
  1421. int lastoffset, lastoffset2, offset;
  1422. nodePtr = tagPtr->tagRootPtr;
  1423. if (nodePtr == (Node *) NULL) {
  1424. return NULL;
  1425. }
  1426. /*
  1427. * Search from the root of the subtree that contains the tag down
  1428. * to the level 0 node.
  1429. */
  1430. while (nodePtr->level > 0) {
  1431. for (lastNodePtr = NULL, nodePtr = nodePtr->children.nodePtr ;
  1432. nodePtr != (Node *) NULL; nodePtr = nodePtr->nextPtr) {
  1433. for (summaryPtr = nodePtr->summaryPtr ; summaryPtr != NULL;
  1434. summaryPtr = summaryPtr->nextPtr) {
  1435. if (summaryPtr->tagPtr == tagPtr) {
  1436. lastNodePtr = nodePtr;
  1437. break;
  1438. }
  1439. }
  1440. }
  1441. nodePtr = lastNodePtr;
  1442. }
  1443. /*
  1444. * Work through the lines attached to the level-0 node.
  1445. */
  1446. last2SegPtr = NULL;
  1447. lastoffset2 = 0;
  1448. lastoffset = 0;
  1449. for (lastLinePtr = NULL, linePtr = nodePtr->children.linePtr;
  1450. linePtr != (TkTextLine *) NULL; linePtr = linePtr->nextPtr) {
  1451. for (offset = 0, lastSegPtr = NULL, segPtr = linePtr->segPtr ;
  1452. segPtr != NULL;
  1453. offset += segPtr->size, segPtr = segPtr->nextPtr) {
  1454. if (((segPtr->typePtr == &tkTextToggleOnType)
  1455. || (segPtr->typePtr == &tkTextToggleOffType))
  1456. && (segPtr->body.toggle.tagPtr == tagPtr)) {
  1457. lastSegPtr = segPtr;
  1458. lastoffset = offset;
  1459. }
  1460. }
  1461. if (lastSegPtr != NULL) {
  1462. lastLinePtr = linePtr;
  1463. last2SegPtr = lastSegPtr;
  1464. lastoffset2 = lastoffset;
  1465. }
  1466. }
  1467. indexPtr->tree = tree;
  1468. indexPtr->linePtr = lastLinePtr;
  1469. indexPtr->byteIndex = lastoffset2;
  1470. return last2SegPtr;
  1471. }
  1472. /*
  1473. *----------------------------------------------------------------------
  1474. *
  1475. * TkBTreeStartSearch --
  1476. *
  1477. * This procedure sets up a search for tag transitions involving
  1478. * a given tag (or all tags) in a given range of the text.
  1479. *
  1480. * Results:
  1481. * None.
  1482. *
  1483. * Side effects:
  1484. * The information at *searchPtr is set up so that subsequent calls
  1485. * to TkBTreeNextTag or TkBTreePrevTag will return information about the
  1486. * locations of tag transitions. Note that TkBTreeNextTag or
  1487. * TkBTreePrevTag must be called to get the first transition.
  1488. * Note: unlike TkBTreeNextTag and TkBTreePrevTag, this routine does not
  1489. * guarantee that searchPtr->curIndex is equal to *index1Ptr. It may be
  1490. * greater than that if *index1Ptr is less than the first tag transition.
  1491. *
  1492. *----------------------------------------------------------------------
  1493. */
  1494. void
  1495. TkBTreeStartSearch(index1Ptr, index2Ptr, tagPtr, searchPtr)
  1496. TkTextIndex *index1Ptr; /* Search starts here. Tag toggles
  1497. * at this position will not be
  1498. * returned. */
  1499. TkTextIndex *index2Ptr; /* Search stops here. Tag toggles
  1500. * at this position *will* be
  1501. * returned. */
  1502. TkTextTag *tagPtr; /* Tag to search for. NULL means
  1503. * search for any tag. */
  1504. register TkTextSearch *searchPtr; /* Where to store information about
  1505. * search's progress. */
  1506. {
  1507. int offset;
  1508. TkTextIndex index0; /* First index of the tag */
  1509. TkTextSegment *seg0Ptr; /* First segment of the tag */
  1510. /*
  1511. * Find the segment that contains the first toggle for the tag. This
  1512. * may become the starting point in the search.
  1513. */
  1514. seg0Ptr = FindTagStart(index1Ptr->tree, tagPtr, &index0);
  1515. if (seg0Ptr == (TkTextSegment *) NULL) {
  1516. /*
  1517. * Even though there are no toggles, the display code still
  1518. * uses the search curIndex, so initialize that anyway.
  1519. */
  1520. searchPtr->linesLeft = 0;
  1521. searchPtr->curIndex = *index1Ptr;
  1522. searchPtr->segPtr = NULL;
  1523. searchPtr->nextPtr = NULL;
  1524. return;
  1525. }
  1526. if (TkTextIndexCmp(index1Ptr, &index0) < 0) {
  1527. /*
  1528. * Adjust start of search up to the first range of the tag
  1529. */
  1530. searchPtr->curIndex = index0;
  1531. searchPtr->segPtr = NULL;
  1532. searchPtr->nextPtr = seg0Ptr; /* Will be returned by NextTag */
  1533. index1Ptr = &index0;
  1534. } else {
  1535. searchPtr->curIndex = *index1Ptr;
  1536. searchPtr->segPtr = NULL;
  1537. searchPtr->nextPtr = TkTextIndexToSeg(index1Ptr, &offset);
  1538. searchPtr->curIndex.byteIndex -= offset;
  1539. }
  1540. searchPtr->lastPtr = TkTextIndexToSeg(index2Ptr, (int *) NULL);
  1541. searchPtr->tagPtr = tagPtr;
  1542. searchPtr->linesLeft = TkBTreeLineIndex(index2Ptr->linePtr) + 1
  1543. - TkBTreeLineIndex(index1Ptr->linePtr);
  1544. searchPtr->allTags = (tagPtr == NULL);
  1545. if (searchPtr->linesLeft == 1) {
  1546. /*
  1547. * Starting and stopping segments are in the same line; mark the
  1548. * search as over immediately if the second segment is before the
  1549. * first. A search does not return a toggle at the very start of
  1550. * the range, unless the range is artificially moved up to index0.
  1551. */
  1552. if (((index1Ptr == &index0) &&
  1553. (index1Ptr->byteIndex > index2Ptr->byteIndex)) ||
  1554. ((index1Ptr != &index0) &&
  1555. (index1Ptr->byteIndex >= index2Ptr->byteIndex))) {
  1556. searchPtr->linesLeft = 0;
  1557. }
  1558. }
  1559. }
  1560. /*
  1561. *----------------------------------------------------------------------
  1562. *
  1563. * TkBTreeStartSearchBack --
  1564. *
  1565. * This procedure sets up a search backwards for tag transitions involving
  1566. * a given tag (or all tags) in a given range of the text. In the
  1567. * normal case the first index (*index1Ptr) is beyond the second
  1568. * index (*index2Ptr).
  1569. *
  1570. *
  1571. * Results:
  1572. * None.
  1573. *
  1574. * Side effects:
  1575. * The information at *searchPtr is set up so that subsequent calls
  1576. * to TkBTreePrevTag will return information about the
  1577. * locations of tag transitions. Note that TkBTreePrevTag must be called
  1578. * to get the first transition.
  1579. * Note: unlike TkBTreeNextTag and TkBTreePrevTag, this routine does not
  1580. * guarantee that searchPtr->curIndex is equal to *index1Ptr. It may be
  1581. * less than that if *index1Ptr is greater than the last tag transition.
  1582. *
  1583. *----------------------------------------------------------------------
  1584. */
  1585. void
  1586. TkBTreeStartSearchBack(index1Ptr, index2Ptr, tagPtr, searchPtr)
  1587. TkTextIndex *index1Ptr; /* Search starts here. Tag toggles
  1588. * at this position will not be
  1589. * returned. */
  1590. TkTextIndex *index2Ptr; /* Search stops here. Tag toggles
  1591. * at this position *will* be
  1592. * returned. */
  1593. TkTextTag *tagPtr; /* Tag to search for. NULL means
  1594. * search for any tag. */
  1595. register TkTextSearch *sea