/[dtapublic]/projs/ets/trunk/src/c_tk_base_7_5_w_mods/tktextmark.c
ViewVC logotype

Diff of /projs/ets/trunk/src/c_tk_base_7_5_w_mods/tktextmark.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

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

Legend:
Removed from v.42  
changed lines
  Added in v.220

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25