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

通讯编程

开发平台:

Visual C++

  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: tkTextMark.c,v 1.6 2002/08/05 04:30:40 dgp Exp $
  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, CONST char *markName));
  41. static int MarkFindPrev _ANSI_ARGS_((Tcl_Interp *interp,
  42.     TkText *textPtr, CONST 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.     CONST 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.     CONST 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.     CONST 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.
  493.  * Indicate caret at 0,0 and return.
  494.  */
  495. Tk_SetCaretPos(textPtr->tkwin, 0, 0, height);
  496. return;
  497.     }
  498.     Tk_SetCaretPos(textPtr->tkwin, x - halfWidth, screenY, height);
  499.     /*
  500.      * As a special hack to keep the cursor visible on mono displays
  501.      * (or anywhere else that the selection and insertion cursors
  502.      * have the same color) write the default background in the cursor
  503.      * area (instead of nothing) when the cursor isn't on.  Otherwise
  504.      * the selection might hide the cursor.
  505.      */
  506.     if (textPtr->flags & INSERT_ON) {
  507. Tk_Fill3DRectangle(textPtr->tkwin, dst, textPtr->insertBorder,
  508. x - halfWidth, y, textPtr->insertWidth, height,
  509. textPtr->insertBorderWidth, TK_RELIEF_RAISED);
  510.     } else if (textPtr->selBorder == textPtr->insertBorder) {
  511. Tk_Fill3DRectangle(textPtr->tkwin, dst, textPtr->border,
  512. x - halfWidth, y, textPtr->insertWidth, height,
  513. 0, TK_RELIEF_FLAT);
  514.     }
  515. }
  516. /*
  517.  *--------------------------------------------------------------
  518.  *
  519.  * InsertUndisplayProc --
  520.  *
  521.  * This procedure is called when the insertion cursor is no
  522.  * longer at a visible point on the display.  It does nothing
  523.  * right now.
  524.  *
  525.  * Results:
  526.  * None.
  527.  *
  528.  * Side effects:
  529.  * None.
  530.  *
  531.  *--------------------------------------------------------------
  532.  */
  533. /* ARGSUSED */
  534. static void
  535. InsertUndisplayProc(textPtr, chunkPtr)
  536.     TkText *textPtr; /* Overall information about text
  537.  * widget. */
  538.     TkTextDispChunk *chunkPtr; /* Chunk that is about to be freed. */
  539. {
  540.     return;
  541. }
  542. /*
  543.  *--------------------------------------------------------------
  544.  *
  545.  * MarkCheckProc --
  546.  *
  547.  * This procedure is invoked by the B-tree code to perform
  548.  * consistency checks on mark segments.
  549.  *
  550.  * Results:
  551.  * None.
  552.  *
  553.  * Side effects:
  554.  * The procedure panics if it detects anything wrong with
  555.  * the mark.
  556.  *
  557.  *--------------------------------------------------------------
  558.  */
  559. static void
  560. MarkCheckProc(markPtr, linePtr)
  561.     TkTextSegment *markPtr; /* Segment to check. */
  562.     TkTextLine *linePtr; /* Line containing segment. */
  563. {
  564.     Tcl_HashSearch search;
  565.     Tcl_HashEntry *hPtr;
  566.     if (markPtr->body.mark.linePtr != linePtr) {
  567. panic("MarkCheckProc: markPtr->body.mark.linePtr bogus");
  568.     }
  569.     /*
  570.      * Make sure that the mark is still present in the text's mark
  571.      * hash table.
  572.      */
  573.     for (hPtr = Tcl_FirstHashEntry(&markPtr->body.mark.textPtr->markTable,
  574.     &search); hPtr != markPtr->body.mark.hPtr;
  575.     hPtr = Tcl_NextHashEntry(&search)) {
  576. if (hPtr == NULL) {
  577.     panic("MarkCheckProc couldn't find hash table entry for mark");
  578. }
  579.     }
  580. }
  581. /*
  582.  *--------------------------------------------------------------
  583.  *
  584.  * MarkFindNext --
  585.  *
  586.  * This procedure searches forward for the next mark.
  587.  *
  588.  * Results:
  589.  * A standard Tcl result, which is a mark name or an empty string.
  590.  *
  591.  * Side effects:
  592.  * None.
  593.  *
  594.  *--------------------------------------------------------------
  595.  */
  596. static int
  597. MarkFindNext(interp, textPtr, string)
  598.     Tcl_Interp *interp; /* For error reporting */
  599.     TkText *textPtr; /* The widget */
  600.     CONST char *string; /* The starting index or mark name */
  601. {
  602.     TkTextIndex index;
  603.     Tcl_HashEntry *hPtr;
  604.     register TkTextSegment *segPtr;
  605.     int offset;
  606.     hPtr = Tcl_FindHashEntry(&textPtr->markTable, string);
  607.     if (hPtr != NULL) {
  608. /*
  609.  * If given a mark name, return the next mark in the list of
  610.  * segments, even if it happens to be at the same character position.
  611.  */
  612. segPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr);
  613. TkTextMarkSegToIndex(textPtr, segPtr, &index);
  614. segPtr = segPtr->nextPtr;
  615.     } else {
  616. /*
  617.  * For non-mark name indices we want to return any marks that
  618.  * are right at the index.
  619.  */
  620. if (TkTextGetIndex(interp, textPtr, string, &index) != TCL_OK) {
  621.     return TCL_ERROR;
  622. }
  623. for (offset = 0, segPtr = index.linePtr->segPtr; 
  624. segPtr != NULL && offset < index.byteIndex;
  625. offset += segPtr->size, segPtr = segPtr->nextPtr) {
  626.     /* Empty loop body */ ;
  627. }
  628.     }
  629.     while (1) {
  630. /*
  631.  * segPtr points at the first possible candidate,
  632.  * or NULL if we ran off the end of the line.
  633.  */
  634. for ( ; segPtr != NULL ; segPtr = segPtr->nextPtr) {
  635.     if (segPtr->typePtr == &tkTextRightMarkType ||
  636.     segPtr->typePtr == &tkTextLeftMarkType) {
  637. Tcl_SetResult(interp,
  638.     Tcl_GetHashKey(&textPtr->markTable, segPtr->body.mark.hPtr),
  639.     TCL_STATIC);
  640. return TCL_OK;
  641.     }
  642. }
  643. index.linePtr = TkBTreeNextLine(index.linePtr);
  644. if (index.linePtr == (TkTextLine *) NULL) {
  645.     return TCL_OK;
  646. }
  647. index.byteIndex = 0;
  648. segPtr = index.linePtr->segPtr;
  649.     }
  650. }
  651. /*
  652.  *--------------------------------------------------------------
  653.  *
  654.  * MarkFindPrev --
  655.  *
  656.  * This procedure searches backwards for the previous mark.
  657.  *
  658.  * Results:
  659.  * A standard Tcl result, which is a mark name or an empty string.
  660.  *
  661.  * Side effects:
  662.  * None.
  663.  *
  664.  *--------------------------------------------------------------
  665.  */
  666. static int
  667. MarkFindPrev(interp, textPtr, string)
  668.     Tcl_Interp *interp; /* For error reporting */
  669.     TkText *textPtr; /* The widget */
  670.     CONST char *string; /* The starting index or mark name */
  671. {
  672.     TkTextIndex index;
  673.     Tcl_HashEntry *hPtr;
  674.     register TkTextSegment *segPtr, *seg2Ptr, *prevPtr;
  675.     int offset;
  676.     hPtr = Tcl_FindHashEntry(&textPtr->markTable, string);
  677.     if (hPtr != NULL) {
  678. /*
  679.  * If given a mark name, return the previous mark in the list of
  680.  * segments, even if it happens to be at the same character position.
  681.  */
  682. segPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr);
  683. TkTextMarkSegToIndex(textPtr, segPtr, &index);
  684.     } else {
  685. /*
  686.  * For non-mark name indices we do not return any marks that
  687.  * are right at the index.
  688.  */
  689. if (TkTextGetIndex(interp, textPtr, string, &index) != TCL_OK) {
  690.     return TCL_ERROR;
  691. }
  692. for (offset = 0, segPtr = index.linePtr->segPtr; 
  693. segPtr != NULL && offset < index.byteIndex;
  694. offset += segPtr->size, segPtr = segPtr->nextPtr) {
  695.     /* Empty loop body */ ;
  696. }
  697.     }
  698.     while (1) {
  699. /*
  700.  * segPtr points just past the first possible candidate,
  701.  * or at the begining of the line.
  702.  */
  703. for (prevPtr = NULL, seg2Ptr = index.linePtr->segPtr; 
  704. seg2Ptr != NULL && seg2Ptr != segPtr;
  705. seg2Ptr = seg2Ptr->nextPtr) {
  706.     if (seg2Ptr->typePtr == &tkTextRightMarkType ||
  707.     seg2Ptr->typePtr == &tkTextLeftMarkType) {
  708. prevPtr = seg2Ptr;
  709.     }
  710. }
  711. if (prevPtr != NULL) {
  712.     Tcl_SetResult(interp, 
  713. Tcl_GetHashKey(&textPtr->markTable, prevPtr->body.mark.hPtr),
  714. TCL_STATIC);
  715.     return TCL_OK;
  716. }
  717. index.linePtr = TkBTreePreviousLine(index.linePtr);
  718. if (index.linePtr == (TkTextLine *) NULL) {
  719.     return TCL_OK;
  720. }
  721. segPtr = NULL;
  722.     }
  723. }