PageRenderTime 29ms CodeModel.GetById 40ms RepoModel.GetById 0ms app.codeStats 0ms

/tags/SN-NG4.1/tk/generic/tkTextMark.c

https://gitlab.com/OpenSourceMirror/sourcenav
C | 776 lines | 429 code | 44 blank | 303 comment | 130 complexity | 0859145b4343026fd5041031d34c5c42 MD5 | raw file
  1. /*
  2. * tkTextMark.c --
  3. *
  4. * This file contains the procedure that implement marks for
  5. * text widgets.
  6. *
  7. * Copyright (c) 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 "tkInt.h"
  16. #include "tkText.h"
  17. #include "tkPort.h"
  18. /*
  19. * Macro that determines the size of a mark segment:
  20. */
  21. #define MSEG_SIZE ((unsigned) (Tk_Offset(TkTextSegment, body) \
  22. + sizeof(TkTextMark)))
  23. /*
  24. * Forward references for procedures defined in this file:
  25. */
  26. static void InsertUndisplayProc _ANSI_ARGS_((TkText *textPtr,
  27. TkTextDispChunk *chunkPtr));
  28. static int MarkDeleteProc _ANSI_ARGS_((TkTextSegment *segPtr,
  29. TkTextLine *linePtr, int treeGone));
  30. static TkTextSegment * MarkCleanupProc _ANSI_ARGS_((TkTextSegment *segPtr,
  31. TkTextLine *linePtr));
  32. static void MarkCheckProc _ANSI_ARGS_((TkTextSegment *segPtr,
  33. TkTextLine *linePtr));
  34. static int MarkLayoutProc _ANSI_ARGS_((TkText *textPtr,
  35. TkTextIndex *indexPtr, TkTextSegment *segPtr,
  36. int offset, int maxX, int maxChars,
  37. int noCharsYet, TkWrapMode wrapMode,
  38. TkTextDispChunk *chunkPtr));
  39. static int MarkFindNext _ANSI_ARGS_((Tcl_Interp *interp,
  40. TkText *textPtr, char *markName));
  41. static int MarkFindPrev _ANSI_ARGS_((Tcl_Interp *interp,
  42. TkText *textPtr, char *markName));
  43. /*
  44. * The following structures declare the "mark" segment types.
  45. * There are actually two types for marks, one with left gravity
  46. * and one with right gravity. They are identical except for
  47. * their gravity property.
  48. */
  49. Tk_SegType tkTextRightMarkType = {
  50. "mark", /* name */
  51. 0, /* leftGravity */
  52. (Tk_SegSplitProc *) NULL, /* splitProc */
  53. MarkDeleteProc, /* deleteProc */
  54. MarkCleanupProc, /* cleanupProc */
  55. (Tk_SegLineChangeProc *) NULL, /* lineChangeProc */
  56. MarkLayoutProc, /* layoutProc */
  57. MarkCheckProc /* checkProc */
  58. };
  59. Tk_SegType tkTextLeftMarkType = {
  60. "mark", /* name */
  61. 1, /* leftGravity */
  62. (Tk_SegSplitProc *) NULL, /* splitProc */
  63. MarkDeleteProc, /* deleteProc */
  64. MarkCleanupProc, /* cleanupProc */
  65. (Tk_SegLineChangeProc *) NULL, /* lineChangeProc */
  66. MarkLayoutProc, /* layoutProc */
  67. MarkCheckProc /* checkProc */
  68. };
  69. /*
  70. *--------------------------------------------------------------
  71. *
  72. * TkTextMarkCmd --
  73. *
  74. * This procedure is invoked to process the "mark" options of
  75. * the widget command for text widgets. See the user documentation
  76. * for details on what it does.
  77. *
  78. * Results:
  79. * A standard Tcl result.
  80. *
  81. * Side effects:
  82. * See the user documentation.
  83. *
  84. *--------------------------------------------------------------
  85. */
  86. int
  87. TkTextMarkCmd(textPtr, interp, argc, argv)
  88. register TkText *textPtr; /* Information about text widget. */
  89. Tcl_Interp *interp; /* Current interpreter. */
  90. int argc; /* Number of arguments. */
  91. char **argv; /* Argument strings. Someone else has already
  92. * parsed this command enough to know that
  93. * argv[1] is "mark". */
  94. {
  95. int c, i;
  96. size_t length;
  97. Tcl_HashEntry *hPtr;
  98. TkTextSegment *markPtr;
  99. Tcl_HashSearch search;
  100. TkTextIndex index;
  101. Tk_SegType *newTypePtr;
  102. if (argc < 3) {
  103. Tcl_AppendResult(interp, "wrong # args: should be \"",
  104. argv[0], " mark option ?arg arg ...?\"", (char *) NULL);
  105. return TCL_ERROR;
  106. }
  107. c = argv[2][0];
  108. length = strlen(argv[2]);
  109. if ((c == 'g') && (strncmp(argv[2], "gravity", length) == 0)) {
  110. if (argc < 4 || argc > 5) {
  111. Tcl_AppendResult(interp, "wrong # args: should be \"",
  112. argv[0], " mark gravity markName ?gravity?\"",
  113. (char *) NULL);
  114. return TCL_ERROR;
  115. }
  116. hPtr = Tcl_FindHashEntry(&textPtr->markTable, argv[3]);
  117. if (hPtr == NULL) {
  118. Tcl_AppendResult(interp, "there is no mark named \"",
  119. argv[3], "\"", (char *) NULL);
  120. return TCL_ERROR;
  121. }
  122. markPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr);
  123. if (argc == 4) {
  124. if (markPtr->typePtr == &tkTextRightMarkType) {
  125. Tcl_SetResult(interp, "right", TCL_STATIC);
  126. } else {
  127. Tcl_SetResult(interp, "left", TCL_STATIC);
  128. }
  129. return TCL_OK;
  130. }
  131. length = strlen(argv[4]);
  132. c = argv[4][0];
  133. if ((c == 'l') && (strncmp(argv[4], "left", length) == 0)) {
  134. newTypePtr = &tkTextLeftMarkType;
  135. } else if ((c == 'r') && (strncmp(argv[4], "right", length) == 0)) {
  136. newTypePtr = &tkTextRightMarkType;
  137. } else {
  138. Tcl_AppendResult(interp, "bad mark gravity \"",
  139. argv[4], "\": must be left or right", (char *) NULL);
  140. return TCL_ERROR;
  141. }
  142. TkTextMarkSegToIndex(textPtr, markPtr, &index);
  143. TkBTreeUnlinkSegment(textPtr->tree, markPtr,
  144. markPtr->body.mark.linePtr);
  145. markPtr->typePtr = newTypePtr;
  146. TkBTreeLinkSegment(markPtr, &index);
  147. } else if ((c == 'n') && (strncmp(argv[2], "names", length) == 0)) {
  148. if (argc != 3) {
  149. Tcl_AppendResult(interp, "wrong # args: should be \"",
  150. argv[0], " mark names\"", (char *) NULL);
  151. return TCL_ERROR;
  152. }
  153. for (hPtr = Tcl_FirstHashEntry(&textPtr->markTable, &search);
  154. hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
  155. Tcl_AppendElement(interp,
  156. Tcl_GetHashKey(&textPtr->markTable, hPtr));
  157. }
  158. } else if ((c == 'n') && (strncmp(argv[2], "next", length) == 0)) {
  159. if (argc != 4) {
  160. Tcl_AppendResult(interp, "wrong # args: should be \"",
  161. argv[0], " mark next index\"", (char *) NULL);
  162. return TCL_ERROR;
  163. }
  164. return MarkFindNext(interp, textPtr, argv[3]);
  165. } else if ((c == 'p') && (strncmp(argv[2], "previous", length) == 0)) {
  166. if (argc != 4) {
  167. Tcl_AppendResult(interp, "wrong # args: should be \"",
  168. argv[0], " mark previous index\"", (char *) NULL);
  169. return TCL_ERROR;
  170. }
  171. return MarkFindPrev(interp, textPtr, argv[3]);
  172. } else if ((c == 's') && (strncmp(argv[2], "set", length) == 0)) {
  173. if (argc != 5) {
  174. Tcl_AppendResult(interp, "wrong # args: should be \"",
  175. argv[0], " mark set markName index\"", (char *) NULL);
  176. return TCL_ERROR;
  177. }
  178. if (TkTextGetIndex(interp, textPtr, argv[4], &index) != TCL_OK) {
  179. return TCL_ERROR;
  180. }
  181. TkTextSetMark(textPtr, argv[3], &index);
  182. } else if ((c == 'u') && (strncmp(argv[2], "unset", length) == 0)) {
  183. for (i = 3; i < argc; i++) {
  184. hPtr = Tcl_FindHashEntry(&textPtr->markTable, argv[i]);
  185. if (hPtr != NULL) {
  186. markPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr);
  187. if ((markPtr == textPtr->insertMarkPtr)
  188. || (markPtr == textPtr->currentMarkPtr)) {
  189. continue;
  190. }
  191. TkBTreeUnlinkSegment(textPtr->tree, markPtr,
  192. markPtr->body.mark.linePtr);
  193. Tcl_DeleteHashEntry(hPtr);
  194. ckfree((char *) markPtr);
  195. }
  196. }
  197. } else {
  198. Tcl_AppendResult(interp, "bad mark option \"", argv[2],
  199. "\": must be gravity, names, next, previous, set, or unset",
  200. (char *) NULL);
  201. return TCL_ERROR;
  202. }
  203. return TCL_OK;
  204. }
  205. /*
  206. *----------------------------------------------------------------------
  207. *
  208. * TkTextSetMark --
  209. *
  210. * Set a mark to a particular position, creating a new mark if
  211. * one doesn't already exist.
  212. *
  213. * Results:
  214. * The return value is a pointer to the mark that was just set.
  215. *
  216. * Side effects:
  217. * A new mark is created, or an existing mark is moved.
  218. *
  219. *----------------------------------------------------------------------
  220. */
  221. TkTextSegment *
  222. TkTextSetMark(textPtr, name, indexPtr)
  223. TkText *textPtr; /* Text widget in which to create mark. */
  224. char *name; /* Name of mark to set. */
  225. TkTextIndex *indexPtr; /* Where to set mark. */
  226. {
  227. Tcl_HashEntry *hPtr;
  228. TkTextSegment *markPtr;
  229. TkTextIndex insertIndex;
  230. int new;
  231. hPtr = Tcl_CreateHashEntry(&textPtr->markTable, name, &new);
  232. markPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr);
  233. if (!new) {
  234. /*
  235. * If this is the insertion point that's being moved, be sure
  236. * to force a display update at the old position. Also, don't
  237. * let the insertion cursor be after the final newline of the
  238. * file.
  239. */
  240. if (markPtr == textPtr->insertMarkPtr) {
  241. TkTextIndex index, index2;
  242. TkTextMarkSegToIndex(textPtr, textPtr->insertMarkPtr, &index);
  243. TkTextIndexForwChars(&index, 1, &index2);
  244. TkTextChanged(textPtr, &index, &index2);
  245. if (TkBTreeLineIndex(indexPtr->linePtr)
  246. == TkBTreeNumLines(textPtr->tree)) {
  247. TkTextIndexBackChars(indexPtr, 1, &insertIndex);
  248. indexPtr = &insertIndex;
  249. }
  250. }
  251. TkBTreeUnlinkSegment(textPtr->tree, markPtr,
  252. markPtr->body.mark.linePtr);
  253. } else {
  254. markPtr = (TkTextSegment *) ckalloc(MSEG_SIZE);
  255. markPtr->typePtr = &tkTextRightMarkType;
  256. markPtr->size = 0;
  257. markPtr->body.mark.textPtr = textPtr;
  258. markPtr->body.mark.linePtr = indexPtr->linePtr;
  259. markPtr->body.mark.hPtr = hPtr;
  260. Tcl_SetHashValue(hPtr, markPtr);
  261. }
  262. TkBTreeLinkSegment(markPtr, indexPtr);
  263. /*
  264. * If the mark is the insertion cursor, then update the screen at the
  265. * mark's new location.
  266. */
  267. if (markPtr == textPtr->insertMarkPtr) {
  268. TkTextIndex index2;
  269. TkTextIndexForwChars(indexPtr, 1, &index2);
  270. TkTextChanged(textPtr, indexPtr, &index2);
  271. }
  272. return markPtr;
  273. }
  274. /*
  275. *--------------------------------------------------------------
  276. *
  277. * TkTextMarkSegToIndex --
  278. *
  279. * Given a segment that is a mark, create an index that
  280. * refers to the next text character (or other text segment
  281. * with non-zero size) after the mark.
  282. *
  283. * Results:
  284. * *IndexPtr is filled in with index information.
  285. *
  286. * Side effects:
  287. * None.
  288. *
  289. *--------------------------------------------------------------
  290. */
  291. void
  292. TkTextMarkSegToIndex(textPtr, markPtr, indexPtr)
  293. TkText *textPtr; /* Text widget containing mark. */
  294. TkTextSegment *markPtr; /* Mark segment. */
  295. TkTextIndex *indexPtr; /* Index information gets stored here. */
  296. {
  297. TkTextSegment *segPtr;
  298. indexPtr->tree = textPtr->tree;
  299. indexPtr->linePtr = markPtr->body.mark.linePtr;
  300. indexPtr->byteIndex = 0;
  301. for (segPtr = indexPtr->linePtr->segPtr; segPtr != markPtr;
  302. segPtr = segPtr->nextPtr) {
  303. indexPtr->byteIndex += segPtr->size;
  304. }
  305. }
  306. /*
  307. *--------------------------------------------------------------
  308. *
  309. * TkTextMarkNameToIndex --
  310. *
  311. * Given the name of a mark, return an index corresponding
  312. * to the mark name.
  313. *
  314. * Results:
  315. * The return value is TCL_OK if "name" exists as a mark in
  316. * the text widget. In this case *indexPtr is filled in with
  317. * the next segment whose after the mark whose size is
  318. * non-zero. TCL_ERROR is returned if the mark doesn't exist
  319. * in the text widget.
  320. *
  321. * Side effects:
  322. * None.
  323. *
  324. *--------------------------------------------------------------
  325. */
  326. int
  327. TkTextMarkNameToIndex(textPtr, name, indexPtr)
  328. TkText *textPtr; /* Text widget containing mark. */
  329. char *name; /* Name of mark. */
  330. TkTextIndex *indexPtr; /* Index information gets stored here. */
  331. {
  332. Tcl_HashEntry *hPtr;
  333. hPtr = Tcl_FindHashEntry(&textPtr->markTable, name);
  334. if (hPtr == NULL) {
  335. return TCL_ERROR;
  336. }
  337. TkTextMarkSegToIndex(textPtr, (TkTextSegment *) Tcl_GetHashValue(hPtr),
  338. indexPtr);
  339. return TCL_OK;
  340. }
  341. /*
  342. *--------------------------------------------------------------
  343. *
  344. * MarkDeleteProc --
  345. *
  346. * This procedure is invoked by the text B-tree code whenever
  347. * a mark lies in a range of characters being deleted.
  348. *
  349. * Results:
  350. * Returns 1 to indicate that deletion has been rejected.
  351. *
  352. * Side effects:
  353. * None (even if the whole tree is being deleted we don't
  354. * free up the mark; it will be done elsewhere).
  355. *
  356. *--------------------------------------------------------------
  357. */
  358. /* ARGSUSED */
  359. static int
  360. MarkDeleteProc(segPtr, linePtr, treeGone)
  361. TkTextSegment *segPtr; /* Segment being deleted. */
  362. TkTextLine *linePtr; /* Line containing segment. */
  363. int treeGone; /* Non-zero means the entire tree is
  364. * being deleted, so everything must
  365. * get cleaned up. */
  366. {
  367. return 1;
  368. }
  369. /*
  370. *--------------------------------------------------------------
  371. *
  372. * MarkCleanupProc --
  373. *
  374. * This procedure is invoked by the B-tree code whenever a
  375. * mark segment is moved from one line to another.
  376. *
  377. * Results:
  378. * None.
  379. *
  380. * Side effects:
  381. * The linePtr field of the segment gets updated.
  382. *
  383. *--------------------------------------------------------------
  384. */
  385. static TkTextSegment *
  386. MarkCleanupProc(markPtr, linePtr)
  387. TkTextSegment *markPtr; /* Mark segment that's being moved. */
  388. TkTextLine *linePtr; /* Line that now contains segment. */
  389. {
  390. markPtr->body.mark.linePtr = linePtr;
  391. return markPtr;
  392. }
  393. /*
  394. *--------------------------------------------------------------
  395. *
  396. * MarkLayoutProc --
  397. *
  398. * This procedure is the "layoutProc" for mark segments.
  399. *
  400. * Results:
  401. * If the mark isn't the insertion cursor then the return
  402. * value is -1 to indicate that this segment shouldn't be
  403. * displayed. If the mark is the insertion character then
  404. * 1 is returned and the chunkPtr structure is filled in.
  405. *
  406. * Side effects:
  407. * None, except for filling in chunkPtr.
  408. *
  409. *--------------------------------------------------------------
  410. */
  411. /*ARGSUSED*/
  412. static int
  413. MarkLayoutProc(textPtr, indexPtr, segPtr, offset, maxX, maxChars,
  414. noCharsYet, wrapMode, chunkPtr)
  415. TkText *textPtr; /* Text widget being layed out. */
  416. TkTextIndex *indexPtr; /* Identifies first character in chunk. */
  417. TkTextSegment *segPtr; /* Segment corresponding to indexPtr. */
  418. int offset; /* Offset within segPtr corresponding to
  419. * indexPtr (always 0). */
  420. int maxX; /* Chunk must not occupy pixels at this
  421. * position or higher. */
  422. int maxChars; /* Chunk must not include more than this
  423. * many characters. */
  424. int noCharsYet; /* Non-zero means no characters have been
  425. * assigned to this line yet. */
  426. TkWrapMode wrapMode; /* Not used. */
  427. register TkTextDispChunk *chunkPtr;
  428. /* Structure to fill in with information
  429. * about this chunk. The x field has already
  430. * been set by the caller. */
  431. {
  432. if (segPtr != textPtr->insertMarkPtr) {
  433. return -1;
  434. }
  435. chunkPtr->displayProc = TkTextInsertDisplayProc;
  436. chunkPtr->undisplayProc = InsertUndisplayProc;
  437. chunkPtr->measureProc = (Tk_ChunkMeasureProc *) NULL;
  438. chunkPtr->bboxProc = (Tk_ChunkBboxProc *) NULL;
  439. chunkPtr->numBytes = 0;
  440. chunkPtr->minAscent = 0;
  441. chunkPtr->minDescent = 0;
  442. chunkPtr->minHeight = 0;
  443. chunkPtr->width = 0;
  444. /*
  445. * Note: can't break a line after the insertion cursor: this
  446. * prevents the insertion cursor from being stranded at the end
  447. * of a line.
  448. */
  449. chunkPtr->breakIndex = -1;
  450. chunkPtr->clientData = (ClientData) textPtr;
  451. return 1;
  452. }
  453. /*
  454. *--------------------------------------------------------------
  455. *
  456. * TkTextInsertDisplayProc --
  457. *
  458. * This procedure is called to display the insertion
  459. * cursor.
  460. *
  461. * Results:
  462. * None.
  463. *
  464. * Side effects:
  465. * Graphics are drawn.
  466. *
  467. *--------------------------------------------------------------
  468. */
  469. /* ARGSUSED */
  470. void
  471. TkTextInsertDisplayProc(chunkPtr, x, y, height, baseline, display, dst, screenY)
  472. TkTextDispChunk *chunkPtr; /* Chunk that is to be drawn. */
  473. int x; /* X-position in dst at which to
  474. * draw this chunk (may differ from
  475. * the x-position in the chunk because
  476. * of scrolling). */
  477. int y; /* Y-position at which to draw this
  478. * chunk in dst (x-position is in
  479. * the chunk itself). */
  480. int height; /* Total height of line. */
  481. int baseline; /* Offset of baseline from y. */
  482. Display *display; /* Display to use for drawing. */
  483. Drawable dst; /* Pixmap or window in which to draw
  484. * chunk. */
  485. int screenY; /* Y-coordinate in text window that
  486. * corresponds to y. */
  487. {
  488. TkText *textPtr = (TkText *) chunkPtr->clientData;
  489. int halfWidth = textPtr->insertWidth/2;
  490. if ((x + halfWidth) < 0) {
  491. /*
  492. * The insertion cursor is off-screen. Just return.
  493. */
  494. return;
  495. }
  496. /*
  497. * As a special hack to keep the cursor visible on mono displays
  498. * (or anywhere else that the selection and insertion cursors
  499. * have the same color) write the default background in the cursor
  500. * area (instead of nothing) when the cursor isn't on. Otherwise
  501. * the selection might hide the cursor.
  502. */
  503. if (textPtr->flags & INSERT_ON) {
  504. Tk_Fill3DRectangle(textPtr->tkwin, dst, textPtr->insertBorder,
  505. x - textPtr->insertWidth/2, y, textPtr->insertWidth,
  506. height, textPtr->insertBorderWidth, TK_RELIEF_RAISED);
  507. } else if (textPtr->selBorder == textPtr->insertBorder) {
  508. Tk_Fill3DRectangle(textPtr->tkwin, dst, textPtr->border,
  509. x - textPtr->insertWidth/2, y, textPtr->insertWidth,
  510. height, 0, TK_RELIEF_FLAT);
  511. }
  512. }
  513. /*
  514. *--------------------------------------------------------------
  515. *
  516. * InsertUndisplayProc --
  517. *
  518. * This procedure is called when the insertion cursor is no
  519. * longer at a visible point on the display. It does nothing
  520. * right now.
  521. *
  522. * Results:
  523. * None.
  524. *
  525. * Side effects:
  526. * None.
  527. *
  528. *--------------------------------------------------------------
  529. */
  530. /* ARGSUSED */
  531. static void
  532. InsertUndisplayProc(textPtr, chunkPtr)
  533. TkText *textPtr; /* Overall information about text
  534. * widget. */
  535. TkTextDispChunk *chunkPtr; /* Chunk that is about to be freed. */
  536. {
  537. return;
  538. }
  539. /*
  540. *--------------------------------------------------------------
  541. *
  542. * MarkCheckProc --
  543. *
  544. * This procedure is invoked by the B-tree code to perform
  545. * consistency checks on mark segments.
  546. *
  547. * Results:
  548. * None.
  549. *
  550. * Side effects:
  551. * The procedure panics if it detects anything wrong with
  552. * the mark.
  553. *
  554. *--------------------------------------------------------------
  555. */
  556. static void
  557. MarkCheckProc(markPtr, linePtr)
  558. TkTextSegment *markPtr; /* Segment to check. */
  559. TkTextLine *linePtr; /* Line containing segment. */
  560. {
  561. Tcl_HashSearch search;
  562. Tcl_HashEntry *hPtr;
  563. if (markPtr->body.mark.linePtr != linePtr) {
  564. panic("MarkCheckProc: markPtr->body.mark.linePtr bogus");
  565. }
  566. /*
  567. * Make sure that the mark is still present in the text's mark
  568. * hash table.
  569. */
  570. for (hPtr = Tcl_FirstHashEntry(&markPtr->body.mark.textPtr->markTable,
  571. &search); hPtr != markPtr->body.mark.hPtr;
  572. hPtr = Tcl_NextHashEntry(&search)) {
  573. if (hPtr == NULL) {
  574. panic("MarkCheckProc couldn't find hash table entry for mark");
  575. }
  576. }
  577. }
  578. /*
  579. *--------------------------------------------------------------
  580. *
  581. * MarkFindNext --
  582. *
  583. * This procedure searches forward for the next mark.
  584. *
  585. * Results:
  586. * A standard Tcl result, which is a mark name or an empty string.
  587. *
  588. * Side effects:
  589. * None.
  590. *
  591. *--------------------------------------------------------------
  592. */
  593. static int
  594. MarkFindNext(interp, textPtr, string)
  595. Tcl_Interp *interp; /* For error reporting */
  596. TkText *textPtr; /* The widget */
  597. char *string; /* The starting index or mark name */
  598. {
  599. TkTextIndex index;
  600. Tcl_HashEntry *hPtr;
  601. register TkTextSegment *segPtr;
  602. int offset;
  603. hPtr = Tcl_FindHashEntry(&textPtr->markTable, string);
  604. if (hPtr != NULL) {
  605. /*
  606. * If given a mark name, return the next mark in the list of
  607. * segments, even if it happens to be at the same character position.
  608. */
  609. segPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr);
  610. TkTextMarkSegToIndex(textPtr, segPtr, &index);
  611. segPtr = segPtr->nextPtr;
  612. } else {
  613. /*
  614. * For non-mark name indices we want to return any marks that
  615. * are right at the index.
  616. */
  617. if (TkTextGetIndex(interp, textPtr, string, &index) != TCL_OK) {
  618. return TCL_ERROR;
  619. }
  620. for (offset = 0, segPtr = index.linePtr->segPtr;
  621. segPtr != NULL && offset < index.byteIndex;
  622. offset += segPtr->size, segPtr = segPtr->nextPtr) {
  623. /* Empty loop body */ ;
  624. }
  625. }
  626. while (1) {
  627. /*
  628. * segPtr points at the first possible candidate,
  629. * or NULL if we ran off the end of the line.
  630. */
  631. for ( ; segPtr != NULL ; segPtr = segPtr->nextPtr) {
  632. if (segPtr->typePtr == &tkTextRightMarkType ||
  633. segPtr->typePtr == &tkTextLeftMarkType) {
  634. Tcl_SetResult(interp,
  635. Tcl_GetHashKey(&textPtr->markTable, segPtr->body.mark.hPtr),
  636. TCL_STATIC);
  637. return TCL_OK;
  638. }
  639. }
  640. index.linePtr = TkBTreeNextLine(index.linePtr);
  641. if (index.linePtr == (TkTextLine *) NULL) {
  642. return TCL_OK;
  643. }
  644. index.byteIndex = 0;
  645. segPtr = index.linePtr->segPtr;
  646. }
  647. }
  648. /*
  649. *--------------------------------------------------------------
  650. *
  651. * MarkFindPrev --
  652. *
  653. * This procedure searches backwards for the previous mark.
  654. *
  655. * Results:
  656. * A standard Tcl result, which is a mark name or an empty string.
  657. *
  658. * Side effects:
  659. * None.
  660. *
  661. *--------------------------------------------------------------
  662. */
  663. static int
  664. MarkFindPrev(interp, textPtr, string)
  665. Tcl_Interp *interp; /* For error reporting */
  666. TkText *textPtr; /* The widget */
  667. char *string; /* The starting index or mark name */
  668. {
  669. TkTextIndex index;
  670. Tcl_HashEntry *hPtr;
  671. register TkTextSegment *segPtr, *seg2Ptr, *prevPtr;
  672. int offset;
  673. hPtr = Tcl_FindHashEntry(&textPtr->markTable, string);
  674. if (hPtr != NULL) {
  675. /*
  676. * If given a mark name, return the previous mark in the list of
  677. * segments, even if it happens to be at the same character position.
  678. */
  679. segPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr);
  680. TkTextMarkSegToIndex(textPtr, segPtr, &index);
  681. } else {
  682. /*
  683. * For non-mark name indices we do not return any marks that
  684. * are right at the index.
  685. */
  686. if (TkTextGetIndex(interp, textPtr, string, &index) != TCL_OK) {
  687. return TCL_ERROR;
  688. }
  689. for (offset = 0, segPtr = index.linePtr->segPtr;
  690. segPtr != NULL && offset < index.byteIndex;
  691. offset += segPtr->size, segPtr = segPtr->nextPtr) {
  692. /* Empty loop body */ ;
  693. }
  694. }
  695. while (1) {
  696. /*
  697. * segPtr points just past the first possible candidate,
  698. * or at the begining of the line.
  699. */
  700. for (prevPtr = NULL, seg2Ptr = index.linePtr->segPtr;
  701. seg2Ptr != NULL && seg2Ptr != segPtr;
  702. seg2Ptr = seg2Ptr->nextPtr) {
  703. if (seg2Ptr->typePtr == &tkTextRightMarkType ||
  704. seg2Ptr->typePtr == &tkTextLeftMarkType) {
  705. prevPtr = seg2Ptr;
  706. }
  707. }
  708. if (prevPtr != NULL) {
  709. Tcl_SetResult(interp,
  710. Tcl_GetHashKey(&textPtr->markTable, prevPtr->body.mark.hPtr),
  711. TCL_STATIC);
  712. return TCL_OK;
  713. }
  714. index.linePtr = TkBTreePreviousLine(index.linePtr);
  715. if (index.linePtr == (TkTextLine *) NULL) {
  716. return TCL_OK;
  717. }
  718. segPtr = NULL;
  719. }
  720. }