OSDN Git Service

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