diff options
Diffstat (limited to 'generic/tkTextMark.c')
-rw-r--r-- | generic/tkTextMark.c | 841 |
1 files changed, 521 insertions, 320 deletions
diff --git a/generic/tkTextMark.c b/generic/tkTextMark.c index c84bc9c..71a7949 100644 --- a/generic/tkTextMark.c +++ b/generic/tkTextMark.c @@ -1,19 +1,18 @@ -/* +/* * tkTextMark.c -- * - * This file contains the procedure that implement marks for - * text widgets. + * This file contains the functions 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. + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. */ #include "tkInt.h" #include "tkText.h" -#include "tkPort.h" /* * Macro that determines the size of a mark segment: @@ -23,55 +22,53 @@ + sizeof(TkTextMark))) /* - * Forward references for procedures defined in this file: + * Forward references for functions 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, CONST char *markName)); -static int MarkFindPrev _ANSI_ARGS_((Tcl_Interp *interp, - TkText *textPtr, CONST char *markName)); +static void InsertUndisplayProc(TkText *textPtr, + TkTextDispChunk *chunkPtr); +static int MarkDeleteProc(TkTextSegment *segPtr, + TkTextLine *linePtr, int treeGone); +static TkTextSegment * MarkCleanupProc(TkTextSegment *segPtr, + TkTextLine *linePtr); +static void MarkCheckProc(TkTextSegment *segPtr, + TkTextLine *linePtr); +static int MarkLayoutProc(TkText *textPtr, TkTextIndex *indexPtr, + TkTextSegment *segPtr, int offset, int maxX, + int maxChars, int noCharsYet, TkWrapMode wrapMode, + TkTextDispChunk *chunkPtr); +static int MarkFindNext(Tcl_Interp *interp, + TkText *textPtr, const char *markName); +static int MarkFindPrev(Tcl_Interp *interp, + TkText *textPtr, const 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. + * 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 */ +const Tk_SegType tkTextRightMarkType = { + "mark", /* name */ + 0, /* leftGravity */ + NULL, /* splitProc */ + MarkDeleteProc, /* deleteProc */ + MarkCleanupProc, /* cleanupProc */ + 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 */ +const Tk_SegType tkTextLeftMarkType = { + "mark", /* name */ + 1, /* leftGravity */ + NULL, /* splitProc */ + MarkDeleteProc, /* deleteProc */ + MarkCleanupProc, /* cleanupProc */ + NULL, /* lineChangeProc */ + MarkLayoutProc, /* layoutProc */ + MarkCheckProc /* checkProc */ }; /* @@ -79,9 +76,9 @@ Tk_SegType tkTextLeftMarkType = { * * 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. + * This function 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. @@ -93,44 +90,62 @@ Tk_SegType tkTextLeftMarkType = { */ int -TkTextMarkCmd(textPtr, interp, argc, argv) - register TkText *textPtr; /* Information about text widget. */ - Tcl_Interp *interp; /* Current interpreter. */ - int argc; /* Number of arguments. */ - CONST char **argv; /* Argument strings. Someone else has already +TkTextMarkCmd( + register TkText *textPtr, /* Information about text widget. */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. Someone else has already * parsed this command enough to know that - * argv[1] is "mark". */ + * objv[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); + const Tk_SegType *newTypePtr; + int optionIndex; + static const char *markOptionStrings[] = { + "gravity", "names", "next", "previous", "set", "unset", NULL + }; + enum markOptions { + MARK_GRAVITY, MARK_NAMES, MARK_NEXT, MARK_PREVIOUS, MARK_SET, + MARK_UNSET + }; + + if (objc < 3) { + Tcl_WrongNumArgs(interp, 2, objv, "option ?arg arg ...?"); + return TCL_ERROR; + } + if (Tcl_GetIndexFromObj(interp, objv[2], markOptionStrings, "mark option", + 0, &optionIndex) != TCL_OK) { 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); + + switch ((enum markOptions) optionIndex) { + case MARK_GRAVITY: { + char c; + int length; + char *str; + + if (objc < 4 || objc > 5) { + Tcl_WrongNumArgs(interp, 3, objv, "markName ?gravity?"); 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; + str = Tcl_GetStringFromObj(objv[3],&length); + if (length == 6 && !strcmp(str, "insert")) { + markPtr = textPtr->insertMarkPtr; + } else if (length == 7 && !strcmp(str, "current")) { + markPtr = textPtr->currentMarkPtr; + } else { + hPtr = Tcl_FindHashEntry(&textPtr->sharedTextPtr->markTable, str); + if (hPtr == NULL) { + Tcl_AppendResult(interp, "there is no mark named \"", + Tcl_GetString(objv[3]), "\"", NULL); + return TCL_ERROR; + } + markPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr); } - markPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr); - if (argc == 4) { + if (objc == 4) { if (markPtr->typePtr == &tkTextRightMarkType) { Tcl_SetResult(interp, "right", TCL_STATIC); } else { @@ -138,77 +153,83 @@ TkTextMarkCmd(textPtr, interp, argc, argv) } return TCL_OK; } - length = strlen(argv[4]); - c = argv[4][0]; - if ((c == 'l') && (strncmp(argv[4], "left", length) == 0)) { + str = Tcl_GetStringFromObj(objv[4],&length); + c = str[0]; + if ((c == 'l') && (strncmp(str, "left", (unsigned)length) == 0)) { newTypePtr = &tkTextLeftMarkType; - } else if ((c == 'r') && (strncmp(argv[4], "right", length) == 0)) { + } else if ((c == 'r') && + (strncmp(str, "right", (unsigned)length) == 0)) { newTypePtr = &tkTextRightMarkType; } else { - Tcl_AppendResult(interp, "bad mark gravity \"", - argv[4], "\": must be left or right", (char *) NULL); + Tcl_AppendResult(interp, "bad mark gravity \"", str, + "\": must be left or right", NULL); return TCL_ERROR; } TkTextMarkSegToIndex(textPtr, markPtr, &index); - TkBTreeUnlinkSegment(textPtr->tree, markPtr, - markPtr->body.mark.linePtr); + TkBTreeUnlinkSegment(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); + break; + } + case MARK_NAMES: + if (objc != 3) { + Tcl_WrongNumArgs(interp, 3, objv, NULL); return TCL_ERROR; } - for (hPtr = Tcl_FirstHashEntry(&textPtr->markTable, &search); - hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) { + Tcl_AppendElement(interp, "insert"); + Tcl_AppendElement(interp, "current"); + for (hPtr = Tcl_FirstHashEntry(&textPtr->sharedTextPtr->markTable, + &search); hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) { Tcl_AppendElement(interp, - Tcl_GetHashKey(&textPtr->markTable, hPtr)); + Tcl_GetHashKey(&textPtr->sharedTextPtr->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); + break; + case MARK_NEXT: + if (objc != 4) { + Tcl_WrongNumArgs(interp, 3, objv, "index"); 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 MarkFindNext(interp, textPtr, Tcl_GetString(objv[3])); + case MARK_PREVIOUS: + if (objc != 4) { + Tcl_WrongNumArgs(interp, 3, objv, "index"); 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 MarkFindPrev(interp, textPtr, Tcl_GetString(objv[3])); + case MARK_SET: + if (objc != 5) { + Tcl_WrongNumArgs(interp, 3, objv, "markName index"); return TCL_ERROR; } - if (TkTextGetIndex(interp, textPtr, argv[4], &index) != TCL_OK) { + if (TkTextGetObjIndex(interp, textPtr, objv[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]); + TkTextSetMark(textPtr, Tcl_GetString(objv[3]), &index); + return TCL_OK; + case MARK_UNSET: { + int i; + + for (i = 3; i < objc; i++) { + hPtr = Tcl_FindHashEntry(&textPtr->sharedTextPtr->markTable, + Tcl_GetString(objv[i])); if (hPtr != NULL) { markPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr); + + /* + * Special case not needed with peer widgets. + */ + if ((markPtr == textPtr->insertMarkPtr) || (markPtr == textPtr->currentMarkPtr)) { continue; } - TkBTreeUnlinkSegment(textPtr->tree, markPtr, - markPtr->body.mark.linePtr); + TkBTreeUnlinkSegment(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; + break; + } } return TCL_OK; } @@ -218,8 +239,8 @@ TkTextMarkCmd(textPtr, interp, argc, argv) * * TkTextSetMark -- * - * Set a mark to a particular position, creating a new mark if - * one doesn't already exist. + * 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. @@ -231,39 +252,67 @@ TkTextMarkCmd(textPtr, interp, argc, argv) */ TkTextSegment * -TkTextSetMark(textPtr, name, indexPtr) - TkText *textPtr; /* Text widget in which to create mark. */ - CONST char *name; /* Name of mark to set. */ - TkTextIndex *indexPtr; /* Where to set mark. */ +TkTextSetMark( + TkText *textPtr, /* Text widget in which to create mark. */ + const char *name, /* Name of mark to set. */ + TkTextIndex *indexPtr) /* Where to set mark. */ { - Tcl_HashEntry *hPtr; + Tcl_HashEntry *hPtr = NULL; TkTextSegment *markPtr; TkTextIndex insertIndex; - int new; - - hPtr = Tcl_CreateHashEntry(&textPtr->markTable, name, &new); - markPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr); - if (!new) { + int isNew, widgetSpecific; + + if (!strcmp(name, "insert")) { + widgetSpecific = 1; + markPtr = textPtr->insertMarkPtr; + isNew = (markPtr == NULL ? 1 : 0); + } else if (!strcmp(name, "current")) { + widgetSpecific = 2; + markPtr = textPtr->currentMarkPtr; + isNew = (markPtr == NULL ? 1 : 0); + } else { + widgetSpecific = 0; + hPtr = Tcl_CreateHashEntry(&textPtr->sharedTextPtr->markTable, name, + &isNew); + markPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr); + } + if (!isNew) { /* - * 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 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; + int nblines; + TkTextMarkSegToIndex(textPtr, textPtr->insertMarkPtr, &index); - TkTextIndexForwChars(&index, 1, &index2); - TkTextChanged(textPtr, &index, &index2); - if (TkBTreeLineIndex(indexPtr->linePtr) - == TkBTreeNumLines(textPtr->tree)) { - TkTextIndexBackChars(indexPtr, 1, &insertIndex); + TkTextIndexForwChars(NULL,&index, 1, &index2, COUNT_INDICES); + + /* + * While we wish to redisplay, no heights have changed, so no need + * to call TkTextInvalidateLineMetrics. + */ + + TkTextChanged(NULL, textPtr, &index, &index2); + + /* + * The number of lines in the widget is zero if and only if it is + * a partial peer with -startline == -endline, i.e. an empty + * peer. In this case the mark shall be set exactly at the given + * index, and not one character backwards (bug 3487407). + */ + + nblines = TkBTreeNumLines(textPtr->sharedTextPtr->tree, textPtr); + if ((TkBTreeLinesTo(textPtr, indexPtr->linePtr) == nblines) + && (nblines > 0)) { + TkTextIndexBackChars(NULL,indexPtr, 1, &insertIndex, + COUNT_INDICES); indexPtr = &insertIndex; } } - TkBTreeUnlinkSegment(textPtr->tree, markPtr, - markPtr->body.mark.linePtr); + TkBTreeUnlinkSegment(markPtr, markPtr->body.mark.linePtr); } else { markPtr = (TkTextSegment *) ckalloc(MSEG_SIZE); markPtr->typePtr = &tkTextRightMarkType; @@ -271,7 +320,13 @@ TkTextSetMark(textPtr, name, indexPtr) markPtr->body.mark.textPtr = textPtr; markPtr->body.mark.linePtr = indexPtr->linePtr; markPtr->body.mark.hPtr = hPtr; - Tcl_SetHashValue(hPtr, markPtr); + if (widgetSpecific == 0) { + Tcl_SetHashValue(hPtr, markPtr); + } else if (widgetSpecific == 1) { + textPtr->insertMarkPtr = markPtr; + } else { + textPtr->currentMarkPtr = markPtr; + } } TkBTreeLinkSegment(markPtr, indexPtr); @@ -283,8 +338,14 @@ TkTextSetMark(textPtr, name, indexPtr) if (markPtr == textPtr->insertMarkPtr) { TkTextIndex index2; - TkTextIndexForwChars(indexPtr, 1, &index2); - TkTextChanged(textPtr, indexPtr, &index2); + TkTextIndexForwChars(NULL,indexPtr, 1, &index2, COUNT_INDICES); + + /* + * While we wish to redisplay, no heights have changed, so no need to + * call TkTextInvalidateLineMetrics + */ + + TkTextChanged(NULL, textPtr, indexPtr, &index2); } return markPtr; } @@ -294,9 +355,9 @@ TkTextSetMark(textPtr, name, indexPtr) * * 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. + * 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. @@ -308,14 +369,14 @@ TkTextSetMark(textPtr, name, indexPtr) */ void -TkTextMarkSegToIndex(textPtr, markPtr, indexPtr) - TkText *textPtr; /* Text widget containing mark. */ - TkTextSegment *markPtr; /* Mark segment. */ - TkTextIndex *indexPtr; /* Index information gets stored here. */ +TkTextMarkSegToIndex( + TkText *textPtr, /* Text widget containing mark. */ + TkTextSegment *markPtr, /* Mark segment. */ + TkTextIndex *indexPtr) /* Index information gets stored here. */ { TkTextSegment *segPtr; - indexPtr->tree = textPtr->tree; + indexPtr->tree = textPtr->sharedTextPtr->tree; indexPtr->linePtr = markPtr->body.mark.linePtr; indexPtr->byteIndex = 0; for (segPtr = indexPtr->linePtr->segPtr; segPtr != markPtr; @@ -329,15 +390,20 @@ TkTextMarkSegToIndex(textPtr, markPtr, indexPtr) * * TkTextMarkNameToIndex -- * - * Given the name of a mark, return an index corresponding - * to the mark name. + * 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. + * The return value is TCL_OK if "name" exists as a mark in the text + * widget and is located within its -starline/-endline range. In this + * case *indexPtr is filled in with the next segment who is after the + * mark whose size is non-zero. TCL_ERROR is returned if the mark + * doesn't exist in the text widget, or if it is out of its -starline/ + * -endline range. In this latter case *indexPtr still contains valid + * information, in particular TkTextMarkNameToIndex called with the + * "insert" or "current" mark name may return TCL_ERROR, but *indexPtr + * contains the correct index of this mark before -startline or after + * -endline. * * Side effects: * None. @@ -346,19 +412,55 @@ TkTextMarkSegToIndex(textPtr, markPtr, indexPtr) */ int -TkTextMarkNameToIndex(textPtr, name, indexPtr) - TkText *textPtr; /* Text widget containing mark. */ - CONST char *name; /* Name of mark. */ - TkTextIndex *indexPtr; /* Index information gets stored here. */ +TkTextMarkNameToIndex( + TkText *textPtr, /* Text widget containing mark. */ + const char *name, /* Name of mark. */ + TkTextIndex *indexPtr) /* Index information gets stored here. */ { - Tcl_HashEntry *hPtr; + TkTextSegment *segPtr; + TkTextIndex index; + int start, end; - hPtr = Tcl_FindHashEntry(&textPtr->markTable, name); - if (hPtr == NULL) { - return TCL_ERROR; + if (textPtr == NULL) { + return TCL_ERROR; + } + + if (!strcmp(name, "insert")) { + segPtr = textPtr->insertMarkPtr; + } else if (!strcmp(name, "current")) { + segPtr = textPtr->currentMarkPtr; + } else { + Tcl_HashEntry *hPtr; + hPtr = Tcl_FindHashEntry(&textPtr->sharedTextPtr->markTable, name); + if (hPtr == NULL) { + return TCL_ERROR; + } + segPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr); + } + TkTextMarkSegToIndex(textPtr, segPtr, indexPtr); + + /* If indexPtr refers to somewhere outside the -startline/-endline + * range limits of the widget, error out since the mark indeed is not + * reachable from this text widget (it may be reachable from a peer) + * (bug 1630271). + */ + + if (textPtr->start != NULL) { + start = TkBTreeLinesTo(NULL, textPtr->start); + TkTextMakeByteIndex(textPtr->sharedTextPtr->tree, NULL, start, 0, + &index); + if (TkTextIndexCmp(indexPtr, &index) < 0) { + return TCL_ERROR; + } + } + if (textPtr->end != NULL) { + end = TkBTreeLinesTo(NULL, textPtr->end); + TkTextMakeByteIndex(textPtr->sharedTextPtr->tree, NULL, end, 0, + &index); + if (TkTextIndexCmp(indexPtr, &index) > 0) { + return TCL_ERROR; + } } - TkTextMarkSegToIndex(textPtr, (TkTextSegment *) Tcl_GetHashValue(hPtr), - indexPtr); return TCL_OK; } @@ -367,27 +469,27 @@ TkTextMarkNameToIndex(textPtr, name, indexPtr) * * MarkDeleteProc -- * - * This procedure is invoked by the text B-tree code whenever - * a mark lies in a range of characters being deleted. + * This function 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). + * 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. */ +MarkDeleteProc( + 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; } @@ -397,8 +499,8 @@ MarkDeleteProc(segPtr, linePtr, treeGone) * * MarkCleanupProc -- * - * This procedure is invoked by the B-tree code whenever a - * mark segment is moved from one line to another. + * This function is invoked by the B-tree code whenever a mark segment is + * moved from one line to another. * * Results: * None. @@ -410,9 +512,9 @@ MarkDeleteProc(segPtr, linePtr, treeGone) */ static TkTextSegment * -MarkCleanupProc(markPtr, linePtr) - TkTextSegment *markPtr; /* Mark segment that's being moved. */ - TkTextLine *linePtr; /* Line that now contains segment. */ +MarkCleanupProc( + TkTextSegment *markPtr, /* Mark segment that's being moved. */ + TkTextLine *linePtr) /* Line that now contains segment. */ { markPtr->body.mark.linePtr = linePtr; return markPtr; @@ -423,13 +525,13 @@ MarkCleanupProc(markPtr, linePtr) * * MarkLayoutProc -- * - * This procedure is the "layoutProc" for mark segments. + * This function 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. + * 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. @@ -437,26 +539,24 @@ MarkCleanupProc(markPtr, linePtr) *-------------------------------------------------------------- */ - /*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 +MarkLayoutProc( + 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 + 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 + 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. */ + 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; @@ -464,8 +564,8 @@ MarkLayoutProc(textPtr, indexPtr, segPtr, offset, maxX, maxChars, chunkPtr->displayProc = TkTextInsertDisplayProc; chunkPtr->undisplayProc = InsertUndisplayProc; - chunkPtr->measureProc = (Tk_ChunkMeasureProc *) NULL; - chunkPtr->bboxProc = (Tk_ChunkBboxProc *) NULL; + chunkPtr->measureProc = NULL; + chunkPtr->bboxProc = NULL; chunkPtr->numBytes = 0; chunkPtr->minAscent = 0; chunkPtr->minDescent = 0; @@ -473,9 +573,8 @@ MarkLayoutProc(textPtr, indexPtr, segPtr, offset, maxX, maxChars, 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. + * 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; @@ -488,8 +587,7 @@ MarkLayoutProc(textPtr, indexPtr, segPtr, offset, maxX, maxChars, * * TkTextInsertDisplayProc -- * - * This procedure is called to display the insertion - * cursor. + * This function is called to display the insertion cursor. * * Results: * None. @@ -502,30 +600,43 @@ MarkLayoutProc(textPtr, indexPtr, segPtr, offset, maxX, maxChars, /* 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. */ +TkTextInsertDisplayProc( + TkText *textPtr, /* The current text widget. */ + 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; + /* + * We have no need for the clientData. + */ + + /* TkText *textPtr = (TkText *) chunkPtr->clientData; */ + TkTextIndex index; int halfWidth = textPtr->insertWidth/2; + int rightSideWidth; + int ix = 0, iy = 0, iw = 0, ih = 0, charWidth = 0; + + if(textPtr->insertCursorType) { + TkTextMarkSegToIndex(textPtr, textPtr->insertMarkPtr, &index); + TkTextIndexBbox(textPtr, &index, &ix, &iy, &iw, &ih, &charWidth); + rightSideWidth = charWidth + halfWidth; + } else { + rightSideWidth = halfWidth; + } - if ((x + halfWidth) < 0) { + if ((x + rightSideWidth) < 0) { /* - * The insertion cursor is off-screen. - * Indicate caret at 0,0 and return. + * The insertion cursor is off-screen. Indicate caret at 0,0 and + * return. */ Tk_SetCaretPos(textPtr->tkwin, 0, 0, height); @@ -535,20 +646,20 @@ TkTextInsertDisplayProc(chunkPtr, x, y, height, baseline, display, dst, screenY) Tk_SetCaretPos(textPtr->tkwin, x - halfWidth, screenY, height); /* - * 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. + * 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 - halfWidth, y, textPtr->insertWidth, height, + x - halfWidth, y, charWidth + textPtr->insertWidth, height, textPtr->insertBorderWidth, TK_RELIEF_RAISED); } else if (textPtr->selBorder == textPtr->insertBorder) { Tk_Fill3DRectangle(textPtr->tkwin, dst, textPtr->border, - x - halfWidth, y, textPtr->insertWidth, height, + x - halfWidth, y, charWidth + textPtr->insertWidth, height, 0, TK_RELIEF_FLAT); } } @@ -558,9 +669,8 @@ TkTextInsertDisplayProc(chunkPtr, x, y, height, baseline, display, dst, screenY) * * InsertUndisplayProc -- * - * This procedure is called when the insertion cursor is no - * longer at a visible point on the display. It does nothing - * right now. + * This function is called when the insertion cursor is no longer at a + * visible point on the display. It does nothing right now. * * Results: * None. @@ -573,10 +683,9 @@ TkTextInsertDisplayProc(chunkPtr, x, y, height, baseline, display, dst, screenY) /* ARGSUSED */ static void -InsertUndisplayProc(textPtr, chunkPtr) - TkText *textPtr; /* Overall information about text - * widget. */ - TkTextDispChunk *chunkPtr; /* Chunk that is about to be freed. */ +InsertUndisplayProc( + TkText *textPtr, /* Overall information about text widget. */ + TkTextDispChunk *chunkPtr) /* Chunk that is about to be freed. */ { return; } @@ -586,41 +695,52 @@ InsertUndisplayProc(textPtr, chunkPtr) * * MarkCheckProc -- * - * This procedure is invoked by the B-tree code to perform - * consistency checks on mark segments. + * This function 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 function panics if it detects anything wrong with * the mark. * *-------------------------------------------------------------- */ static void -MarkCheckProc(markPtr, linePtr) - TkTextSegment *markPtr; /* Segment to check. */ - TkTextLine *linePtr; /* Line containing segment. */ +MarkCheckProc( + 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"); + Tcl_Panic("MarkCheckProc: markPtr->body.mark.linePtr bogus"); } /* - * Make sure that the mark is still present in the text's mark - * hash table. + * These two marks are not in the hash table */ - for (hPtr = Tcl_FirstHashEntry(&markPtr->body.mark.textPtr->markTable, + if (markPtr->body.mark.textPtr->insertMarkPtr == markPtr) { + return; + } + if (markPtr->body.mark.textPtr->currentMarkPtr == markPtr) { + return; + } + + /* + * Make sure that the mark is still present in the text's mark hash table. + */ + + for (hPtr = Tcl_FirstHashEntry( + &markPtr->body.mark.textPtr->sharedTextPtr->markTable, &search); hPtr != markPtr->body.mark.hPtr; hPtr = Tcl_NextHashEntry(&search)) { if (hPtr == NULL) { - panic("MarkCheckProc couldn't find hash table entry for mark"); + Tcl_Panic("MarkCheckProc couldn't find hash table entry for mark"); } } } @@ -630,7 +750,7 @@ MarkCheckProc(markPtr, linePtr) * * MarkFindNext -- * - * This procedure searches forward for the next mark. + * This function searches forward for the next mark. * * Results: * A standard Tcl result, which is a mark name or an empty string. @@ -642,56 +762,88 @@ MarkCheckProc(markPtr, linePtr) */ static int -MarkFindNext(interp, textPtr, string) - Tcl_Interp *interp; /* For error reporting */ - TkText *textPtr; /* The widget */ - CONST char *string; /* The starting index or mark name */ +MarkFindNext( + Tcl_Interp *interp, /* For error reporting */ + TkText *textPtr, /* The widget */ + const 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); + if (!strcmp(string, "insert")) { + segPtr = textPtr->insertMarkPtr; + TkTextMarkSegToIndex(textPtr, segPtr, &index); + segPtr = segPtr->nextPtr; + } else if (!strcmp(string, "current")) { + segPtr = textPtr->currentMarkPtr; 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 */ ; + hPtr = Tcl_FindHashEntry(&textPtr->sharedTextPtr->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. + * 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); + if (segPtr == textPtr->currentMarkPtr) { + Tcl_SetResult(interp, "current", TCL_STATIC); + } else if (segPtr == textPtr->insertMarkPtr) { + Tcl_SetResult(interp, "insert", TCL_STATIC); + } else if (segPtr->body.mark.hPtr == NULL) { + /* + * Ignore widget-specific marks for the other widgets. + * This is either an insert or a current mark + * (markPtr->body.mark.hPtr actually receives NULL + * for these marks in TkTextSetMark). + * The insert and current marks for textPtr having + * already been tested above, the current segment is + * an insert or current mark from a peer of textPtr, + * which we don't want to return. + */ + continue; + } else { + Tcl_SetResult(interp, + Tcl_GetHashKey(&textPtr->sharedTextPtr->markTable, + segPtr->body.mark.hPtr), TCL_STATIC); + } return TCL_OK; } } - index.linePtr = TkBTreeNextLine(index.linePtr); - if (index.linePtr == (TkTextLine *) NULL) { + index.linePtr = TkBTreeNextLine(textPtr, index.linePtr); + if (index.linePtr == NULL) { return TCL_OK; } index.byteIndex = 0; @@ -704,7 +856,7 @@ MarkFindNext(interp, textPtr, string) * * MarkFindPrev -- * - * This procedure searches backwards for the previous mark. + * This function searches backwards for the previous mark. * * Results: * A standard Tcl result, which is a mark name or an empty string. @@ -716,62 +868,111 @@ MarkFindNext(interp, textPtr, string) */ static int -MarkFindPrev(interp, textPtr, string) - Tcl_Interp *interp; /* For error reporting */ - TkText *textPtr; /* The widget */ - CONST char *string; /* The starting index or mark name */ +MarkFindPrev( + Tcl_Interp *interp, /* For error reporting */ + TkText *textPtr, /* The widget */ + const 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); + if (!strcmp(string, "insert")) { + segPtr = textPtr->insertMarkPtr; + TkTextMarkSegToIndex(textPtr, segPtr, &index); + } else if (!strcmp(string, "current")) { + segPtr = textPtr->currentMarkPtr; 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 */ ; + hPtr = Tcl_FindHashEntry(&textPtr->sharedTextPtr->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. + * segPtr points just past the first possible candidate, or at the + * beginning of the line. */ - for (prevPtr = NULL, seg2Ptr = index.linePtr->segPtr; + + for (prevPtr = NULL, seg2Ptr = index.linePtr->segPtr; seg2Ptr != NULL && seg2Ptr != segPtr; seg2Ptr = seg2Ptr->nextPtr) { if (seg2Ptr->typePtr == &tkTextRightMarkType || seg2Ptr->typePtr == &tkTextLeftMarkType) { + if (seg2Ptr->body.mark.hPtr == NULL) { + if (seg2Ptr != textPtr->currentMarkPtr && + seg2Ptr != textPtr->insertMarkPtr) { + /* + * This is an insert or current mark from a + * peer of textPtr. + */ + continue; + } + } prevPtr = seg2Ptr; } } if (prevPtr != NULL) { - Tcl_SetResult(interp, - Tcl_GetHashKey(&textPtr->markTable, prevPtr->body.mark.hPtr), - TCL_STATIC); - return TCL_OK; + if (prevPtr == textPtr->currentMarkPtr) { + Tcl_SetResult(interp, "current", TCL_STATIC); + return TCL_OK; + } else if (prevPtr == textPtr->insertMarkPtr) { + Tcl_SetResult(interp, "insert", TCL_STATIC); + return TCL_OK; + } else if (prevPtr->body.mark.hPtr == NULL) { + /* + * Ignore widget-specific marks for the other widgets. + * This is either an insert or a current mark + * (markPtr->body.mark.hPtr actually receives NULL + * for these marks in TkTextSetMark). + * The insert and current marks for textPtr having + * already been tested above, the current segment is + * an insert or current mark from a peer of textPtr, + * which we don't want to return. + */ + } else { + Tcl_SetResult(interp, + Tcl_GetHashKey(&textPtr->sharedTextPtr->markTable, + prevPtr->body.mark.hPtr), TCL_STATIC); + return TCL_OK; + } } - index.linePtr = TkBTreePreviousLine(index.linePtr); - if (index.linePtr == (TkTextLine *) NULL) { + index.linePtr = TkBTreePreviousLine(textPtr, index.linePtr); + if (index.linePtr == NULL) { return TCL_OK; } segPtr = NULL; } } + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ |