home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / tkisrc04.zip / tk / os2 / tkTextMark.c < prev    next >
C/C++ Source or Header  |  1998-08-07  |  22KB  |  776 lines

  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-1995 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.  * SCCS: @(#) tkTextMark.c 1.15 96/02/15 18:52:59
  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, Tk_Uid wrapMode,
  43.                 TkTextDispChunk *chunkPtr));
  44. static int        MarkFindNext _ANSI_ARGS_((Tcl_Interp *interp,
  45.                 TkText *textPtr, char *markName));
  46. static int        MarkFindPrev _ANSI_ARGS_((Tcl_Interp *interp,
  47.                 TkText *textPtr, 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.  
  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.     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 > 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.         interp->result = "right";
  138.         } else {
  139.         interp->result = "left";
  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.  
  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.     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.  
  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->charIndex = 0;
  323.     for (segPtr = indexPtr->linePtr->segPtr; segPtr != markPtr;
  324.         segPtr = segPtr->nextPtr) {
  325.     indexPtr->charIndex += segPtr->size;
  326.     }
  327. }
  328.  
  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.     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.  
  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.  
  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.  
  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.     Tk_Uid 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->numChars = 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.  
  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.  Just return.
  530.      */
  531.  
  532.     return;
  533.     }
  534.  
  535.     /*
  536.      * As a special hack to keep the cursor visible on mono displays
  537.      * (or anywhere else that the selection and insertion cursors
  538.      * have the same color) write the default background in the cursor
  539.      * area (instead of nothing) when the cursor isn't on.  Otherwise
  540.      * the selection might hide the cursor.
  541.      */
  542.  
  543.     if (textPtr->flags & INSERT_ON) {
  544.     Tk_Fill3DRectangle(textPtr->tkwin, dst, textPtr->insertBorder,
  545.         x - textPtr->insertWidth/2, y, textPtr->insertWidth,
  546.         height, textPtr->insertBorderWidth, TK_RELIEF_RAISED);
  547.     } else if (textPtr->selBorder == textPtr->insertBorder) {
  548.     Tk_Fill3DRectangle(textPtr->tkwin, dst, textPtr->border,
  549.         x - textPtr->insertWidth/2, y, textPtr->insertWidth,
  550.         height, 0, TK_RELIEF_FLAT);
  551.     }
  552. }
  553.  
  554. /*
  555.  *--------------------------------------------------------------
  556.  *
  557.  * InsertUndisplayProc --
  558.  *
  559.  *    This procedure is called when the insertion cursor is no
  560.  *    longer at a visible point on the display.  It does nothing
  561.  *    right now.
  562.  *
  563.  * Results:
  564.  *    None.
  565.  *
  566.  * Side effects:
  567.  *    None.
  568.  *
  569.  *--------------------------------------------------------------
  570.  */
  571.  
  572.     /* ARGSUSED */
  573. static void
  574. InsertUndisplayProc(textPtr, chunkPtr)
  575.     TkText *textPtr;            /* Overall information about text
  576.                      * widget. */
  577.     TkTextDispChunk *chunkPtr;        /* Chunk that is about to be freed. */
  578. {
  579.     return;
  580. }
  581.  
  582. /*
  583.  *--------------------------------------------------------------
  584.  *
  585.  * MarkCheckProc --
  586.  *
  587.  *    This procedure is invoked by the B-tree code to perform
  588.  *    consistency checks on mark segments.
  589.  *
  590.  * Results:
  591.  *    None.
  592.  *
  593.  * Side effects:
  594.  *    The procedure panics if it detects anything wrong with
  595.  *    the mark.
  596.  *
  597.  *--------------------------------------------------------------
  598.  */
  599.  
  600. static void
  601. MarkCheckProc(markPtr, linePtr)
  602.     TkTextSegment *markPtr;        /* Segment to check. */
  603.     TkTextLine *linePtr;        /* Line containing segment. */
  604. {
  605.     Tcl_HashSearch search;
  606.     Tcl_HashEntry *hPtr;
  607.  
  608.     if (markPtr->body.mark.linePtr != linePtr) {
  609.     panic("MarkCheckProc: markPtr->body.mark.linePtr bogus");
  610.     }
  611.  
  612.     /*
  613.      * Make sure that the mark is still present in the text's mark
  614.      * hash table.
  615.      */
  616.  
  617.     for (hPtr = Tcl_FirstHashEntry(&markPtr->body.mark.textPtr->markTable,
  618.         &search); hPtr != markPtr->body.mark.hPtr;
  619.         hPtr = Tcl_NextHashEntry(&search)) {
  620.     if (hPtr == NULL) {
  621.         panic("MarkCheckProc couldn't find hash table entry for mark");
  622.     }
  623.     }
  624. }
  625.  
  626. /*
  627.  *--------------------------------------------------------------
  628.  *
  629.  * MarkFindNext --
  630.  *
  631.  *    This procedure searches forward for the next mark.
  632.  *
  633.  * Results:
  634.  *    A standard Tcl result, which is a mark name or an empty string.
  635.  *
  636.  * Side effects:
  637.  *    None.
  638.  *
  639.  *--------------------------------------------------------------
  640.  */
  641.  
  642. static int
  643. MarkFindNext(interp, textPtr, string)
  644.     Tcl_Interp *interp;            /* For error reporting */
  645.     TkText *textPtr;            /* The widget */
  646.     char *string;            /* The starting index or mark name */
  647. {
  648.     TkTextIndex index;
  649.     Tcl_HashEntry *hPtr;
  650.     register TkTextSegment *segPtr;
  651.     int offset;
  652.  
  653.  
  654.     hPtr = Tcl_FindHashEntry(&textPtr->markTable, string);
  655.     if (hPtr != NULL) {
  656.     /*
  657.      * If given a mark name, return the next mark in the list of
  658.      * segments, even if it happens to be at the same character position.
  659.      */
  660.     segPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr);
  661.     TkTextMarkSegToIndex(textPtr, segPtr, &index);
  662.     segPtr = segPtr->nextPtr;
  663.     } else {
  664.     /*
  665.      * For non-mark name indices we want to return any marks that
  666.      * are right at the index.
  667.      */
  668.     if (TkTextGetIndex(interp, textPtr, string, &index) != TCL_OK) {
  669.         return TCL_ERROR;
  670.     }
  671.     for (offset = 0, segPtr = index.linePtr->segPtr; 
  672.         segPtr != NULL && offset < index.charIndex;
  673.         offset += segPtr->size,    segPtr = segPtr->nextPtr) {
  674.         /* Empty loop body */ ;
  675.     }
  676.     }
  677.     while (1) {
  678.     /*
  679.      * segPtr points at the first possible candidate,
  680.      * or NULL if we ran off the end of the line.
  681.      */
  682.     for ( ; segPtr != NULL ; segPtr = segPtr->nextPtr) {
  683.         if (segPtr->typePtr == &tkTextRightMarkType ||
  684.             segPtr->typePtr == &tkTextLeftMarkType) {
  685.         Tcl_SetResult(interp,
  686.             Tcl_GetHashKey(&textPtr->markTable, segPtr->body.mark.hPtr),
  687.             TCL_STATIC);
  688.         return TCL_OK;
  689.         }
  690.     }
  691.     index.linePtr = TkBTreeNextLine(index.linePtr);
  692.     if (index.linePtr == (TkTextLine *) NULL) {
  693.         return TCL_OK;
  694.     }
  695.     index.charIndex = 0;
  696.     segPtr = index.linePtr->segPtr;
  697.     }
  698. }
  699.  
  700. /*
  701.  *--------------------------------------------------------------
  702.  *
  703.  * MarkFindPrev --
  704.  *
  705.  *    This procedure searches backwards for the previous mark.
  706.  *
  707.  * Results:
  708.  *    A standard Tcl result, which is a mark name or an empty string.
  709.  *
  710.  * Side effects:
  711.  *    None.
  712.  *
  713.  *--------------------------------------------------------------
  714.  */
  715.  
  716. static int
  717. MarkFindPrev(interp, textPtr, string)
  718.     Tcl_Interp *interp;            /* For error reporting */
  719.     TkText *textPtr;            /* The widget */
  720.     char *string;            /* The starting index or mark name */
  721. {
  722.     TkTextIndex index;
  723.     Tcl_HashEntry *hPtr;
  724.     register TkTextSegment *segPtr, *seg2Ptr, *prevPtr;
  725.     int offset;
  726.  
  727.  
  728.     hPtr = Tcl_FindHashEntry(&textPtr->markTable, string);
  729.     if (hPtr != NULL) {
  730.     /*
  731.      * If given a mark name, return the previous mark in the list of
  732.      * segments, even if it happens to be at the same character position.
  733.      */
  734.     segPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr);
  735.     TkTextMarkSegToIndex(textPtr, segPtr, &index);
  736.     } else {
  737.     /*
  738.      * For non-mark name indices we do not return any marks that
  739.      * are right at the index.
  740.      */
  741.     if (TkTextGetIndex(interp, textPtr, string, &index) != TCL_OK) {
  742.         return TCL_ERROR;
  743.     }
  744.     for (offset = 0, segPtr = index.linePtr->segPtr; 
  745.         segPtr != NULL && offset < index.charIndex;
  746.         offset += segPtr->size, segPtr = segPtr->nextPtr) {
  747.         /* Empty loop body */ ;
  748.     }
  749.     }
  750.     while (1) {
  751.     /*
  752.      * segPtr points just past the first possible candidate,
  753.      * or at the begining of the line.
  754.      */
  755.     for (prevPtr = NULL, seg2Ptr = index.linePtr->segPtr; 
  756.         seg2Ptr != NULL && seg2Ptr != segPtr;
  757.         seg2Ptr = seg2Ptr->nextPtr) {
  758.         if (seg2Ptr->typePtr == &tkTextRightMarkType ||
  759.             seg2Ptr->typePtr == &tkTextLeftMarkType) {
  760.         prevPtr = seg2Ptr;
  761.         }
  762.     }
  763.     if (prevPtr != NULL) {
  764.         Tcl_SetResult(interp, 
  765.         Tcl_GetHashKey(&textPtr->markTable, prevPtr->body.mark.hPtr),
  766.         TCL_STATIC);
  767.         return TCL_OK;
  768.     }
  769.     index.linePtr = TkBTreePreviousLine(index.linePtr);
  770.     if (index.linePtr == (TkTextLine *) NULL) {
  771.         return TCL_OK;
  772.     }
  773.     segPtr = NULL;
  774.     }
  775. }
  776.