PageRenderTime 58ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/tags/SN-52B2-2004-03-19/tk/generic/tkTextIndex.c

https://gitlab.com/OpenSourceMirror/sourcenav
C | 1187 lines | 666 code | 96 blank | 425 comment | 242 complexity | 53cee59e8198ec7710271a54a271928d MD5 | raw file
  1. /*
  2. * tkTextIndex.c --
  3. *
  4. * This module provides procedures that manipulate indices for
  5. * text widgets.
  6. *
  7. * Copyright (c) 1992-1994 The Regents of the University of California.
  8. * Copyright (c) 1994-1997 Sun Microsystems, Inc.
  9. *
  10. * See the file "license.terms" for information on usage and redistribution
  11. * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  12. *
  13. * RCS: @(#) $Id$
  14. */
  15. #include "default.h"
  16. #include "tkPort.h"
  17. #include "tkInt.h"
  18. #include "tkText.h"
  19. /*
  20. * Index to use to select last character in line (very large integer):
  21. */
  22. #define LAST_CHAR 1000000
  23. /*
  24. * Forward declarations for procedures defined later in this file:
  25. */
  26. static char * ForwBack _ANSI_ARGS_((char *string,
  27. TkTextIndex *indexPtr));
  28. static char * StartEnd _ANSI_ARGS_(( char *string,
  29. TkTextIndex *indexPtr));
  30. /*
  31. *---------------------------------------------------------------------------
  32. *
  33. * TkTextMakeByteIndex --
  34. *
  35. * Given a line index and a byte index, look things up in the B-tree
  36. * and fill in a TkTextIndex structure.
  37. *
  38. * Results:
  39. * The structure at *indexPtr is filled in with information about the
  40. * character at lineIndex and byteIndex (or the closest existing
  41. * character, if the specified one doesn't exist), and indexPtr is
  42. * returned as result.
  43. *
  44. * Side effects:
  45. * None.
  46. *
  47. *---------------------------------------------------------------------------
  48. */
  49. TkTextIndex *
  50. TkTextMakeByteIndex(tree, lineIndex, byteIndex, indexPtr)
  51. TkTextBTree tree; /* Tree that lineIndex and charIndex refer
  52. * to. */
  53. int lineIndex; /* Index of desired line (0 means first
  54. * line of text). */
  55. int byteIndex; /* Byte index of desired character. */
  56. TkTextIndex *indexPtr; /* Structure to fill in. */
  57. {
  58. TkTextSegment *segPtr;
  59. int index;
  60. char *p, *start;
  61. Tcl_UniChar ch;
  62. indexPtr->tree = tree;
  63. if (lineIndex < 0) {
  64. lineIndex = 0;
  65. byteIndex = 0;
  66. }
  67. if (byteIndex < 0) {
  68. byteIndex = 0;
  69. }
  70. indexPtr->linePtr = TkBTreeFindLine(tree, lineIndex);
  71. if (indexPtr->linePtr == NULL) {
  72. indexPtr->linePtr = TkBTreeFindLine(tree, TkBTreeNumLines(tree));
  73. byteIndex = 0;
  74. }
  75. if (byteIndex == 0) {
  76. indexPtr->byteIndex = byteIndex;
  77. return indexPtr;
  78. }
  79. /*
  80. * Verify that the index is within the range of the line and points
  81. * to a valid character boundary.
  82. */
  83. index = 0;
  84. for (segPtr = indexPtr->linePtr->segPtr; ; segPtr = segPtr->nextPtr) {
  85. if (segPtr == NULL) {
  86. /*
  87. * Use the index of the last character in the line. Since
  88. * the last character on the line is guaranteed to be a '\n',
  89. * we can back up a constant sizeof(char) bytes.
  90. */
  91. indexPtr->byteIndex = index - sizeof(char);
  92. break;
  93. }
  94. if (index + segPtr->size > byteIndex) {
  95. indexPtr->byteIndex = byteIndex;
  96. if ((byteIndex > index) && (segPtr->typePtr == &tkTextCharType)) {
  97. /*
  98. * Prevent UTF-8 character from being split up by ensuring
  99. * that byteIndex falls on a character boundary. If index
  100. * falls in the middle of a UTF-8 character, it will be
  101. * adjusted to the end of that UTF-8 character.
  102. */
  103. start = segPtr->body.chars + (byteIndex - index);
  104. p = Tcl_UtfPrev(start, segPtr->body.chars);
  105. p += Tcl_UtfToUniChar(p, &ch);
  106. indexPtr->byteIndex += p - start;
  107. }
  108. break;
  109. }
  110. index += segPtr->size;
  111. }
  112. return indexPtr;
  113. }
  114. /*
  115. *---------------------------------------------------------------------------
  116. *
  117. * TkTextMakeCharIndex --
  118. *
  119. * Given a line index and a character index, look things up in the
  120. * B-tree and fill in a TkTextIndex structure.
  121. *
  122. * Results:
  123. * The structure at *indexPtr is filled in with information about the
  124. * character at lineIndex and charIndex (or the closest existing
  125. * character, if the specified one doesn't exist), and indexPtr is
  126. * returned as result.
  127. *
  128. * Side effects:
  129. * None.
  130. *
  131. *---------------------------------------------------------------------------
  132. */
  133. TkTextIndex *
  134. TkTextMakeCharIndex(tree, lineIndex, charIndex, indexPtr)
  135. TkTextBTree tree; /* Tree that lineIndex and charIndex refer
  136. * to. */
  137. int lineIndex; /* Index of desired line (0 means first
  138. * line of text). */
  139. int charIndex; /* Index of desired character. */
  140. TkTextIndex *indexPtr; /* Structure to fill in. */
  141. {
  142. register TkTextSegment *segPtr;
  143. char *p, *start, *end;
  144. int index, offset;
  145. Tcl_UniChar ch;
  146. indexPtr->tree = tree;
  147. if (lineIndex < 0) {
  148. lineIndex = 0;
  149. charIndex = 0;
  150. }
  151. if (charIndex < 0) {
  152. charIndex = 0;
  153. }
  154. indexPtr->linePtr = TkBTreeFindLine(tree, lineIndex);
  155. if (indexPtr->linePtr == NULL) {
  156. indexPtr->linePtr = TkBTreeFindLine(tree, TkBTreeNumLines(tree));
  157. charIndex = 0;
  158. }
  159. /*
  160. * Verify that the index is within the range of the line.
  161. * If not, just use the index of the last character in the line.
  162. */
  163. index = 0;
  164. for (segPtr = indexPtr->linePtr->segPtr; ; segPtr = segPtr->nextPtr) {
  165. if (segPtr == NULL) {
  166. /*
  167. * Use the index of the last character in the line. Since
  168. * the last character on the line is guaranteed to be a '\n',
  169. * we can back up a constant sizeof(char) bytes.
  170. */
  171. indexPtr->byteIndex = index - sizeof(char);
  172. break;
  173. }
  174. if (segPtr->typePtr == &tkTextCharType) {
  175. /*
  176. * Turn character offset into a byte offset.
  177. */
  178. start = segPtr->body.chars;
  179. end = start + segPtr->size;
  180. for (p = start; p < end; p += offset) {
  181. if (charIndex == 0) {
  182. indexPtr->byteIndex = index;
  183. return indexPtr;
  184. }
  185. charIndex--;
  186. offset = Tcl_UtfToUniChar(p, &ch);
  187. index += offset;
  188. }
  189. } else {
  190. if (charIndex < segPtr->size) {
  191. indexPtr->byteIndex = index;
  192. break;
  193. }
  194. charIndex -= segPtr->size;
  195. index += segPtr->size;
  196. }
  197. }
  198. return indexPtr;
  199. }
  200. /*
  201. *---------------------------------------------------------------------------
  202. *
  203. * TkTextIndexToSeg --
  204. *
  205. * Given an index, this procedure returns the segment and offset
  206. * within segment for the index.
  207. *
  208. * Results:
  209. * The return value is a pointer to the segment referred to by
  210. * indexPtr; this will always be a segment with non-zero size. The
  211. * variable at *offsetPtr is set to hold the integer offset within
  212. * the segment of the character given by indexPtr.
  213. *
  214. * Side effects:
  215. * None.
  216. *
  217. *---------------------------------------------------------------------------
  218. */
  219. TkTextSegment *
  220. TkTextIndexToSeg(indexPtr, offsetPtr)
  221. CONST TkTextIndex *indexPtr;/* Text index. */
  222. int *offsetPtr; /* Where to store offset within segment, or
  223. * NULL if offset isn't wanted. */
  224. {
  225. TkTextSegment *segPtr;
  226. int offset;
  227. for (offset = indexPtr->byteIndex, segPtr = indexPtr->linePtr->segPtr;
  228. offset >= segPtr->size;
  229. offset -= segPtr->size, segPtr = segPtr->nextPtr) {
  230. /* Empty loop body. */
  231. }
  232. if (offsetPtr != NULL) {
  233. *offsetPtr = offset;
  234. }
  235. return segPtr;
  236. }
  237. /*
  238. *---------------------------------------------------------------------------
  239. *
  240. * TkTextSegToOffset --
  241. *
  242. * Given a segment pointer and the line containing it, this procedure
  243. * returns the offset of the segment within its line.
  244. *
  245. * Results:
  246. * The return value is the offset (within its line) of the first
  247. * character in segPtr.
  248. *
  249. * Side effects:
  250. * None.
  251. *
  252. *---------------------------------------------------------------------------
  253. */
  254. int
  255. TkTextSegToOffset(segPtr, linePtr)
  256. CONST TkTextSegment *segPtr;/* Segment whose offset is desired. */
  257. CONST TkTextLine *linePtr; /* Line containing segPtr. */
  258. {
  259. CONST TkTextSegment *segPtr2;
  260. int offset;
  261. offset = 0;
  262. for (segPtr2 = linePtr->segPtr; segPtr2 != segPtr;
  263. segPtr2 = segPtr2->nextPtr) {
  264. offset += segPtr2->size;
  265. }
  266. return offset;
  267. }
  268. /*
  269. *---------------------------------------------------------------------------
  270. *
  271. * TkTextGetIndex --
  272. *
  273. * Given a string, return the index that is described.
  274. *
  275. * Results:
  276. * The return value is a standard Tcl return result. If TCL_OK is
  277. * returned, then everything went well and the index at *indexPtr is
  278. * filled in; otherwise TCL_ERROR is returned and an error message
  279. * is left in the interp's result.
  280. *
  281. * Side effects:
  282. * None.
  283. *
  284. *---------------------------------------------------------------------------
  285. */
  286. int
  287. TkTextGetIndex(interp, textPtr, string, indexPtr)
  288. Tcl_Interp *interp; /* Use this for error reporting. */
  289. TkText *textPtr; /* Information about text widget. */
  290. char *string; /* Textual description of position. */
  291. TkTextIndex *indexPtr; /* Index structure to fill in. */
  292. {
  293. char *p, *end, *endOfBase;
  294. Tcl_HashEntry *hPtr;
  295. TkTextTag *tagPtr;
  296. TkTextSearch search;
  297. TkTextIndex first, last;
  298. int wantLast, result;
  299. char c;
  300. /*
  301. *---------------------------------------------------------------------
  302. * Stage 1: check to see if the index consists of nothing but a mark
  303. * name. We do this check now even though it's also done later, in
  304. * order to allow mark names that include funny characters such as
  305. * spaces or "+1c".
  306. *---------------------------------------------------------------------
  307. */
  308. if (TkTextMarkNameToIndex(textPtr, string, indexPtr) == TCL_OK) {
  309. return TCL_OK;
  310. }
  311. /*
  312. *------------------------------------------------
  313. * Stage 2: start again by parsing the base index.
  314. *------------------------------------------------
  315. */
  316. indexPtr->tree = textPtr->tree;
  317. /*
  318. * First look for the form "tag.first" or "tag.last" where "tag"
  319. * is the name of a valid tag. Try to use up as much as possible
  320. * of the string in this check (strrchr instead of strchr below).
  321. * Doing the check now, and in this way, allows tag names to include
  322. * funny characters like "@" or "+1c".
  323. */
  324. p = strrchr(string, '.');
  325. if (p != NULL) {
  326. if ((p[1] == 'f') && (strncmp(p+1, "first", 5) == 0)) {
  327. wantLast = 0;
  328. endOfBase = p+6;
  329. } else if ((p[1] == 'l') && (strncmp(p+1, "last", 4) == 0)) {
  330. wantLast = 1;
  331. endOfBase = p+5;
  332. } else {
  333. goto tryxy;
  334. }
  335. *p = 0;
  336. hPtr = Tcl_FindHashEntry(&textPtr->tagTable, string);
  337. *p = '.';
  338. if (hPtr == NULL) {
  339. goto tryxy;
  340. }
  341. tagPtr = (TkTextTag *) Tcl_GetHashValue(hPtr);
  342. TkTextMakeByteIndex(textPtr->tree, 0, 0, &first);
  343. TkTextMakeByteIndex(textPtr->tree, TkBTreeNumLines(textPtr->tree), 0,
  344. &last);
  345. TkBTreeStartSearch(&first, &last, tagPtr, &search);
  346. if (!TkBTreeCharTagged(&first, tagPtr) && !TkBTreeNextTag(&search)) {
  347. Tcl_AppendResult(interp,
  348. "text doesn't contain any characters tagged with \"",
  349. Tcl_GetHashKey(&textPtr->tagTable, hPtr), "\"",
  350. (char *) NULL);
  351. return TCL_ERROR;
  352. }
  353. *indexPtr = search.curIndex;
  354. if (wantLast) {
  355. while (TkBTreeNextTag(&search)) {
  356. *indexPtr = search.curIndex;
  357. }
  358. }
  359. goto gotBase;
  360. }
  361. tryxy:
  362. if (string[0] == '@') {
  363. /*
  364. * Find character at a given x,y location in the window.
  365. */
  366. int x, y;
  367. p = string+1;
  368. x = strtol(p, &end, 0);
  369. if ((end == p) || (*end != ',')) {
  370. goto error;
  371. }
  372. p = end+1;
  373. y = strtol(p, &end, 0);
  374. if (end == p) {
  375. goto error;
  376. }
  377. TkTextPixelIndex(textPtr, x, y, indexPtr);
  378. endOfBase = end;
  379. goto gotBase;
  380. }
  381. if (isdigit(UCHAR(string[0])) || (string[0] == '-')) {
  382. int lineIndex, charIndex;
  383. /*
  384. * Base is identified with line and character indices.
  385. */
  386. lineIndex = strtol(string, &end, 0) - 1;
  387. if ((end == string) || (*end != '.')) {
  388. goto error;
  389. }
  390. p = end+1;
  391. if ((*p == 'e') && (strncmp(p, "end", 3) == 0)) {
  392. charIndex = LAST_CHAR;
  393. endOfBase = p+3;
  394. } else {
  395. charIndex = strtol(p, &end, 0);
  396. if (end == p) {
  397. goto error;
  398. }
  399. endOfBase = end;
  400. }
  401. TkTextMakeCharIndex(textPtr->tree, lineIndex, charIndex, indexPtr);
  402. goto gotBase;
  403. }
  404. for (p = string; *p != 0; p++) {
  405. if (isspace(UCHAR(*p)) || (*p == '+') || (*p == '-')) {
  406. break;
  407. }
  408. }
  409. endOfBase = p;
  410. if (string[0] == '.') {
  411. /*
  412. * See if the base position is the name of an embedded window.
  413. */
  414. c = *endOfBase;
  415. *endOfBase = 0;
  416. result = TkTextWindowIndex(textPtr, string, indexPtr);
  417. *endOfBase = c;
  418. if (result != 0) {
  419. goto gotBase;
  420. }
  421. }
  422. if ((string[0] == 'e')
  423. && (strncmp(string, "end", (size_t) (endOfBase-string)) == 0)) {
  424. /*
  425. * Base position is end of text.
  426. */
  427. TkTextMakeByteIndex(textPtr->tree, TkBTreeNumLines(textPtr->tree),
  428. 0, indexPtr);
  429. goto gotBase;
  430. } else {
  431. /*
  432. * See if the base position is the name of a mark.
  433. */
  434. c = *endOfBase;
  435. *endOfBase = 0;
  436. result = TkTextMarkNameToIndex(textPtr, string, indexPtr);
  437. *endOfBase = c;
  438. if (result == TCL_OK) {
  439. goto gotBase;
  440. }
  441. /*
  442. * See if the base position is the name of an embedded image
  443. */
  444. c = *endOfBase;
  445. *endOfBase = 0;
  446. result = TkTextImageIndex(textPtr, string, indexPtr);
  447. *endOfBase = c;
  448. if (result != 0) {
  449. goto gotBase;
  450. }
  451. }
  452. goto error;
  453. /*
  454. *-------------------------------------------------------------------
  455. * Stage 3: process zero or more modifiers. Each modifier is either
  456. * a keyword like "wordend" or "linestart", or it has the form
  457. * "op count units" where op is + or -, count is a number, and units
  458. * is "chars" or "lines".
  459. *-------------------------------------------------------------------
  460. */
  461. gotBase:
  462. p = endOfBase;
  463. while (1) {
  464. while (isspace(UCHAR(*p))) {
  465. p++;
  466. }
  467. if (*p == 0) {
  468. break;
  469. }
  470. if ((*p == '+') || (*p == '-')) {
  471. p = ForwBack(p, indexPtr);
  472. } else {
  473. p = StartEnd(p, indexPtr);
  474. }
  475. if (p == NULL) {
  476. goto error;
  477. }
  478. }
  479. return TCL_OK;
  480. error:
  481. Tcl_AppendResult(interp, "bad text index \"", string, "\"",
  482. (char *) NULL);
  483. return TCL_ERROR;
  484. }
  485. /*
  486. *---------------------------------------------------------------------------
  487. *
  488. * TkTextPrintIndex --
  489. *
  490. * This procedure generates a string description of an index, suitable
  491. * for reading in again later.
  492. *
  493. * Results:
  494. * The characters pointed to by string are modified.
  495. *
  496. * Side effects:
  497. * None.
  498. *
  499. *---------------------------------------------------------------------------
  500. */
  501. void
  502. TkTextPrintIndex(indexPtr, string)
  503. CONST TkTextIndex *indexPtr;/* Pointer to index. */
  504. char *string; /* Place to store the position. Must have
  505. * at least TK_POS_CHARS characters. */
  506. {
  507. TkTextSegment *segPtr;
  508. int numBytes, charIndex;
  509. numBytes = indexPtr->byteIndex;
  510. charIndex = 0;
  511. for (segPtr = indexPtr->linePtr->segPtr; ; segPtr = segPtr->nextPtr) {
  512. if (numBytes <= segPtr->size) {
  513. break;
  514. }
  515. if (segPtr->typePtr == &tkTextCharType) {
  516. charIndex += Tcl_NumUtfChars(segPtr->body.chars, segPtr->size);
  517. } else {
  518. charIndex += segPtr->size;
  519. }
  520. numBytes -= segPtr->size;
  521. }
  522. if (segPtr->typePtr == &tkTextCharType) {
  523. charIndex += Tcl_NumUtfChars(segPtr->body.chars, numBytes);
  524. } else {
  525. charIndex += numBytes;
  526. }
  527. sprintf(string, "%d.%d", TkBTreeLineIndex(indexPtr->linePtr) + 1,
  528. charIndex);
  529. }
  530. /*
  531. *---------------------------------------------------------------------------
  532. *
  533. * TkTextIndexCmp --
  534. *
  535. * Compare two indices to see which one is earlier in the text.
  536. *
  537. * Results:
  538. * The return value is 0 if index1Ptr and index2Ptr refer to the same
  539. * position in the file, -1 if index1Ptr refers to an earlier position
  540. * than index2Ptr, and 1 otherwise.
  541. *
  542. * Side effects:
  543. * None.
  544. *
  545. *---------------------------------------------------------------------------
  546. */
  547. int
  548. TkTextIndexCmp(index1Ptr, index2Ptr)
  549. CONST TkTextIndex *index1Ptr; /* First index. */
  550. CONST TkTextIndex *index2Ptr; /* Second index. */
  551. {
  552. int line1, line2;
  553. if (index1Ptr->linePtr == index2Ptr->linePtr) {
  554. if (index1Ptr->byteIndex < index2Ptr->byteIndex) {
  555. return -1;
  556. } else if (index1Ptr->byteIndex > index2Ptr->byteIndex) {
  557. return 1;
  558. } else {
  559. return 0;
  560. }
  561. }
  562. line1 = TkBTreeLineIndex(index1Ptr->linePtr);
  563. line2 = TkBTreeLineIndex(index2Ptr->linePtr);
  564. if (line1 < line2) {
  565. return -1;
  566. }
  567. if (line1 > line2) {
  568. return 1;
  569. }
  570. return 0;
  571. }
  572. /*
  573. *---------------------------------------------------------------------------
  574. *
  575. * ForwBack --
  576. *
  577. * This procedure handles +/- modifiers for indices to adjust the
  578. * index forwards or backwards.
  579. *
  580. * Results:
  581. * If the modifier in string is successfully parsed then the return
  582. * value is the address of the first character after the modifier,
  583. * and *indexPtr is updated to reflect the modifier. If there is a
  584. * syntax error in the modifier then NULL is returned.
  585. *
  586. * Side effects:
  587. * None.
  588. *
  589. *---------------------------------------------------------------------------
  590. */
  591. static char *
  592. ForwBack(string, indexPtr)
  593. char *string; /* String to parse for additional info
  594. * about modifier (count and units).
  595. * Points to "+" or "-" that starts
  596. * modifier. */
  597. TkTextIndex *indexPtr; /* Index to update as specified in string. */
  598. {
  599. register char *p;
  600. char *end, *units;
  601. int count, lineIndex;
  602. size_t length;
  603. /*
  604. * Get the count (how many units forward or backward).
  605. */
  606. p = string+1;
  607. while (isspace(UCHAR(*p))) {
  608. p++;
  609. }
  610. count = strtol(p, &end, 0);
  611. if (end == p) {
  612. return NULL;
  613. }
  614. p = end;
  615. while (isspace(UCHAR(*p))) {
  616. p++;
  617. }
  618. /*
  619. * Find the end of this modifier (next space or + or - character),
  620. * then parse the unit specifier and update the position
  621. * accordingly.
  622. */
  623. units = p;
  624. while ((*p != '\0') && !isspace(UCHAR(*p)) && (*p != '+') && (*p != '-')) {
  625. p++;
  626. }
  627. length = p - units;
  628. if ((*units == 'c') && (strncmp(units, "chars", length) == 0)) {
  629. if (*string == '+') {
  630. TkTextIndexForwChars(indexPtr, count, indexPtr);
  631. } else {
  632. TkTextIndexBackChars(indexPtr, count, indexPtr);
  633. }
  634. } else if ((*units == 'l') && (strncmp(units, "lines", length) == 0)) {
  635. lineIndex = TkBTreeLineIndex(indexPtr->linePtr);
  636. if (*string == '+') {
  637. lineIndex += count;
  638. } else {
  639. lineIndex -= count;
  640. /*
  641. * The check below retains the character position, even
  642. * if the line runs off the start of the file. Without
  643. * it, the character position will get reset to 0 by
  644. * TkTextMakeIndex.
  645. */
  646. if (lineIndex < 0) {
  647. lineIndex = 0;
  648. }
  649. }
  650. /*
  651. * This doesn't work quite right if using a proportional font or
  652. * UTF-8 characters with varying numbers of bytes. The cursor will
  653. * bop around, keeping a constant number of bytes (not characters)
  654. * from the left edge (but making sure not to split any UTF-8
  655. * characters), regardless of the x-position the index corresponds
  656. * to. The proper way to do this is to get the x-position of the
  657. * index and then pick the character at the same x-position in the
  658. * new line.
  659. */
  660. TkTextMakeByteIndex(indexPtr->tree, lineIndex, indexPtr->byteIndex,
  661. indexPtr);
  662. } else {
  663. return NULL;
  664. }
  665. return p;
  666. }
  667. /*
  668. *---------------------------------------------------------------------------
  669. *
  670. * TkTextIndexForwBytes --
  671. *
  672. * Given an index for a text widget, this procedure creates a new
  673. * index that points "count" bytes ahead of the source index.
  674. *
  675. * Results:
  676. * *dstPtr is modified to refer to the character "count" bytes after
  677. * srcPtr, or to the last character in the TkText if there aren't
  678. * "count" bytes left.
  679. *
  680. * Side effects:
  681. * None.
  682. *
  683. *---------------------------------------------------------------------------
  684. */
  685. void
  686. TkTextIndexForwBytes(srcPtr, byteCount, dstPtr)
  687. CONST TkTextIndex *srcPtr; /* Source index. */
  688. int byteCount; /* How many bytes forward to move. May be
  689. * negative. */
  690. TkTextIndex *dstPtr; /* Destination index: gets modified. */
  691. {
  692. TkTextLine *linePtr;
  693. TkTextSegment *segPtr;
  694. int lineLength;
  695. if (byteCount < 0) {
  696. TkTextIndexBackBytes(srcPtr, -byteCount, dstPtr);
  697. return;
  698. }
  699. *dstPtr = *srcPtr;
  700. dstPtr->byteIndex += byteCount;
  701. while (1) {
  702. /*
  703. * Compute the length of the current line.
  704. */
  705. lineLength = 0;
  706. for (segPtr = dstPtr->linePtr->segPtr; segPtr != NULL;
  707. segPtr = segPtr->nextPtr) {
  708. lineLength += segPtr->size;
  709. }
  710. /*
  711. * If the new index is in the same line then we're done.
  712. * Otherwise go on to the next line.
  713. */
  714. if (dstPtr->byteIndex < lineLength) {
  715. return;
  716. }
  717. dstPtr->byteIndex -= lineLength;
  718. linePtr = TkBTreeNextLine(dstPtr->linePtr);
  719. if (linePtr == NULL) {
  720. dstPtr->byteIndex = lineLength - 1;
  721. return;
  722. }
  723. dstPtr->linePtr = linePtr;
  724. }
  725. }
  726. /*
  727. *---------------------------------------------------------------------------
  728. *
  729. * TkTextIndexForwChars --
  730. *
  731. * Given an index for a text widget, this procedure creates a new
  732. * index that points "count" characters ahead of the source index.
  733. *
  734. * Results:
  735. * *dstPtr is modified to refer to the character "count" characters
  736. * after srcPtr, or to the last character in the TkText if there
  737. * aren't "count" characters left in the file.
  738. *
  739. * Side effects:
  740. * None.
  741. *
  742. *---------------------------------------------------------------------------
  743. */
  744. void
  745. TkTextIndexForwChars(srcPtr, charCount, dstPtr)
  746. CONST TkTextIndex *srcPtr; /* Source index. */
  747. int charCount; /* How many characters forward to move.
  748. * May be negative. */
  749. TkTextIndex *dstPtr; /* Destination index: gets modified. */
  750. {
  751. TkTextLine *linePtr;
  752. TkTextSegment *segPtr;
  753. int byteOffset;
  754. char *start, *end, *p;
  755. Tcl_UniChar ch;
  756. if (charCount < 0) {
  757. TkTextIndexBackChars(srcPtr, -charCount, dstPtr);
  758. return;
  759. }
  760. *dstPtr = *srcPtr;
  761. /*
  762. * Find seg that contains src byteIndex.
  763. * Move forward specified number of chars.
  764. */
  765. segPtr = TkTextIndexToSeg(dstPtr, &byteOffset);
  766. while (1) {
  767. /*
  768. * Go through each segment in line looking for specified character
  769. * index.
  770. */
  771. for ( ; segPtr != NULL; segPtr = segPtr->nextPtr) {
  772. if (segPtr->typePtr == &tkTextCharType) {
  773. start = segPtr->body.chars + byteOffset;
  774. end = segPtr->body.chars + segPtr->size;
  775. for (p = start; p < end; p += Tcl_UtfToUniChar(p, &ch)) {
  776. if (charCount == 0) {
  777. dstPtr->byteIndex += (p - start);
  778. return;
  779. }
  780. charCount--;
  781. }
  782. } else {
  783. if (charCount < segPtr->size - byteOffset) {
  784. dstPtr->byteIndex += charCount;
  785. return;
  786. }
  787. charCount -= segPtr->size - byteOffset;
  788. }
  789. dstPtr->byteIndex += segPtr->size - byteOffset;
  790. byteOffset = 0;
  791. }
  792. /*
  793. * Go to the next line. If we are at the end of the text item,
  794. * back up one byte (for the terminal '\n' character) and return
  795. * that index.
  796. */
  797. linePtr = TkBTreeNextLine(dstPtr->linePtr);
  798. if (linePtr == NULL) {
  799. dstPtr->byteIndex -= sizeof(char);
  800. return;
  801. }
  802. dstPtr->linePtr = linePtr;
  803. dstPtr->byteIndex = 0;
  804. segPtr = dstPtr->linePtr->segPtr;
  805. }
  806. }
  807. /*
  808. *---------------------------------------------------------------------------
  809. *
  810. * TkTextIndexBackBytes --
  811. *
  812. * Given an index for a text widget, this procedure creates a new
  813. * index that points "count" bytes earlier than the source index.
  814. *
  815. * Results:
  816. * *dstPtr is modified to refer to the character "count" bytes before
  817. * srcPtr, or to the first character in the TkText if there aren't
  818. * "count" bytes earlier than srcPtr.
  819. *
  820. * Side effects:
  821. * None.
  822. *
  823. *---------------------------------------------------------------------------
  824. */
  825. void
  826. TkTextIndexBackBytes(srcPtr, byteCount, dstPtr)
  827. CONST TkTextIndex *srcPtr; /* Source index. */
  828. int byteCount; /* How many bytes backward to move. May be
  829. * negative. */
  830. TkTextIndex *dstPtr; /* Destination index: gets modified. */
  831. {
  832. TkTextSegment *segPtr;
  833. int lineIndex;
  834. if (byteCount < 0) {
  835. TkTextIndexForwBytes(srcPtr, -byteCount, dstPtr);
  836. return;
  837. }
  838. *dstPtr = *srcPtr;
  839. dstPtr->byteIndex -= byteCount;
  840. lineIndex = -1;
  841. while (dstPtr->byteIndex < 0) {
  842. /*
  843. * Move back one line in the text. If we run off the beginning
  844. * of the file then just return the first character in the text.
  845. */
  846. if (lineIndex < 0) {
  847. lineIndex = TkBTreeLineIndex(dstPtr->linePtr);
  848. }
  849. if (lineIndex == 0) {
  850. dstPtr->byteIndex = 0;
  851. return;
  852. }
  853. lineIndex--;
  854. dstPtr->linePtr = TkBTreeFindLine(dstPtr->tree, lineIndex);
  855. /*
  856. * Compute the length of the line and add that to dstPtr->charIndex.
  857. */
  858. for (segPtr = dstPtr->linePtr->segPtr; segPtr != NULL;
  859. segPtr = segPtr->nextPtr) {
  860. dstPtr->byteIndex += segPtr->size;
  861. }
  862. }
  863. }
  864. /*
  865. *---------------------------------------------------------------------------
  866. *
  867. * TkTextIndexBackChars --
  868. *
  869. * Given an index for a text widget, this procedure creates a new
  870. * index that points "count" characters earlier than the source index.
  871. *
  872. * Results:
  873. * *dstPtr is modified to refer to the character "count" characters
  874. * before srcPtr, or to the first character in the file if there
  875. * aren't "count" characters earlier than srcPtr.
  876. *
  877. * Side effects:
  878. * None.
  879. *
  880. *---------------------------------------------------------------------------
  881. */
  882. void
  883. TkTextIndexBackChars(srcPtr, charCount, dstPtr)
  884. CONST TkTextIndex *srcPtr; /* Source index. */
  885. int charCount; /* How many characters backward to move.
  886. * May be negative. */
  887. TkTextIndex *dstPtr; /* Destination index: gets modified. */
  888. {
  889. TkTextSegment *segPtr, *oldPtr;
  890. int lineIndex, segSize;
  891. char *p, *start, *end;
  892. if (charCount <= 0) {
  893. TkTextIndexForwChars(srcPtr, -charCount, dstPtr);
  894. return;
  895. }
  896. *dstPtr = *srcPtr;
  897. /*
  898. * Find offset within seg that contains byteIndex.
  899. * Move backward specified number of chars.
  900. */
  901. lineIndex = -1;
  902. segSize = dstPtr->byteIndex;
  903. for (segPtr = dstPtr->linePtr->segPtr; ; segPtr = segPtr->nextPtr) {
  904. if (segSize <= segPtr->size) {
  905. break;
  906. }
  907. segSize -= segPtr->size;
  908. }
  909. while (1) {
  910. if (segPtr->typePtr == &tkTextCharType) {
  911. start = segPtr->body.chars;
  912. end = segPtr->body.chars + segSize;
  913. for (p = end; ; p = Tcl_UtfPrev(p, start)) {
  914. if (charCount == 0) {
  915. dstPtr->byteIndex -= (end - p);
  916. return;
  917. }
  918. if (p == start) {
  919. break;
  920. }
  921. charCount--;
  922. }
  923. } else {
  924. if (charCount <= segSize) {
  925. dstPtr->byteIndex -= charCount;
  926. return;
  927. }
  928. charCount -= segSize;
  929. }
  930. dstPtr->byteIndex -= segSize;
  931. /*
  932. * Move back into previous segment.
  933. */
  934. oldPtr = segPtr;
  935. segPtr = dstPtr->linePtr->segPtr;
  936. if (segPtr != oldPtr) {
  937. for ( ; segPtr->nextPtr != oldPtr; segPtr = segPtr->nextPtr) {
  938. /* Empty body. */
  939. }
  940. segSize = segPtr->size;
  941. continue;
  942. }
  943. /*
  944. * Move back to previous line.
  945. */
  946. if (lineIndex < 0) {
  947. lineIndex = TkBTreeLineIndex(dstPtr->linePtr);
  948. }
  949. if (lineIndex == 0) {
  950. dstPtr->byteIndex = 0;
  951. return;
  952. }
  953. lineIndex--;
  954. dstPtr->linePtr = TkBTreeFindLine(dstPtr->tree, lineIndex);
  955. /*
  956. * Compute the length of the line and add that to dstPtr->byteIndex.
  957. */
  958. oldPtr = dstPtr->linePtr->segPtr;
  959. for (segPtr = oldPtr; segPtr != NULL; segPtr = segPtr->nextPtr) {
  960. dstPtr->byteIndex += segPtr->size;
  961. oldPtr = segPtr;
  962. }
  963. segPtr = oldPtr;
  964. segSize = segPtr->size;
  965. }
  966. }
  967. /*
  968. *----------------------------------------------------------------------
  969. *
  970. * StartEnd --
  971. *
  972. * This procedure handles modifiers like "wordstart" and "lineend"
  973. * to adjust indices forwards or backwards.
  974. *
  975. * Results:
  976. * If the modifier is successfully parsed then the return value
  977. * is the address of the first character after the modifier, and
  978. * *indexPtr is updated to reflect the modifier. If there is a
  979. * syntax error in the modifier then NULL is returned.
  980. *
  981. * Side effects:
  982. * None.
  983. *
  984. *----------------------------------------------------------------------
  985. */
  986. static char *
  987. StartEnd(string, indexPtr)
  988. char *string; /* String to parse for additional info
  989. * about modifier (count and units).
  990. * Points to first character of modifer
  991. * word. */
  992. TkTextIndex *indexPtr; /* Index to mdoify based on string. */
  993. {
  994. char *p;
  995. int c, offset;
  996. size_t length;
  997. register TkTextSegment *segPtr;
  998. /*
  999. * Find the end of the modifier word.
  1000. */
  1001. for (p = string; isalnum(UCHAR(*p)); p++) {
  1002. /* Empty loop body. */
  1003. }
  1004. length = p-string;
  1005. if ((*string == 'l') && (strncmp(string, "lineend", length) == 0)
  1006. && (length >= 5)) {
  1007. indexPtr->byteIndex = 0;
  1008. for (segPtr = indexPtr->linePtr->segPtr; segPtr != NULL;
  1009. segPtr = segPtr->nextPtr) {
  1010. indexPtr->byteIndex += segPtr->size;
  1011. }
  1012. indexPtr->byteIndex -= sizeof(char);
  1013. } else if ((*string == 'l') && (strncmp(string, "linestart", length) == 0)
  1014. && (length >= 5)) {
  1015. indexPtr->byteIndex = 0;
  1016. } else if ((*string == 'w') && (strncmp(string, "wordend", length) == 0)
  1017. && (length >= 5)) {
  1018. int firstChar = 1;
  1019. /*
  1020. * If the current character isn't part of a word then just move
  1021. * forward one character. Otherwise move forward until finding
  1022. * a character that isn't part of a word and stop there.
  1023. */
  1024. segPtr = TkTextIndexToSeg(indexPtr, &offset);
  1025. while (1) {
  1026. if (segPtr->typePtr == &tkTextCharType) {
  1027. c = segPtr->body.chars[offset];
  1028. if (!isalnum(UCHAR(c)) && (c != '_')) {
  1029. break;
  1030. }
  1031. firstChar = 0;
  1032. }
  1033. offset += 1;
  1034. indexPtr->byteIndex += sizeof(char);
  1035. if (offset >= segPtr->size) {
  1036. segPtr = TkTextIndexToSeg(indexPtr, &offset);
  1037. }
  1038. }
  1039. if (firstChar) {
  1040. TkTextIndexForwChars(indexPtr, 1, indexPtr);
  1041. }
  1042. } else if ((*string == 'w') && (strncmp(string, "wordstart", length) == 0)
  1043. && (length >= 5)) {
  1044. int firstChar = 1;
  1045. /*
  1046. * Starting with the current character, look for one that's not
  1047. * part of a word and keep moving backward until you find one.
  1048. * Then if the character found wasn't the first one, move forward
  1049. * again one position.
  1050. */
  1051. segPtr = TkTextIndexToSeg(indexPtr, &offset);
  1052. while (1) {
  1053. if (segPtr->typePtr == &tkTextCharType) {
  1054. c = segPtr->body.chars[offset];
  1055. if (!isalnum(UCHAR(c)) && (c != '_')) {
  1056. break;
  1057. }
  1058. firstChar = 0;
  1059. }
  1060. offset -= 1;
  1061. indexPtr->byteIndex -= sizeof(char);
  1062. if (offset < 0) {
  1063. if (indexPtr->byteIndex < 0) {
  1064. indexPtr->byteIndex = 0;
  1065. goto done;
  1066. }
  1067. segPtr = TkTextIndexToSeg(indexPtr, &offset);
  1068. }
  1069. }
  1070. if (!firstChar) {
  1071. TkTextIndexForwChars(indexPtr, 1, indexPtr);
  1072. }
  1073. } else {
  1074. return NULL;
  1075. }
  1076. done:
  1077. return p;
  1078. }