summaryrefslogtreecommitdiffstats
path: root/generic/tkTextIndex.c
diff options
context:
space:
mode:
Diffstat (limited to 'generic/tkTextIndex.c')
-rw-r--r--generic/tkTextIndex.c1723
1 files changed, 1419 insertions, 304 deletions
diff --git a/generic/tkTextIndex.c b/generic/tkTextIndex.c
index 7cfeaea..a9b0bed 100644
--- a/generic/tkTextIndex.c
+++ b/generic/tkTextIndex.c
@@ -1,18 +1,17 @@
-/*
+/*
* tkTextIndex.c --
*
- * This module provides procedures that manipulate indices for
- * text widgets.
+ * This module provides functions that manipulate indices for text
+ * widgets.
*
* Copyright (c) 1992-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 "default.h"
-#include "tkPort.h"
#include "tkInt.h"
#include "tkText.h"
@@ -23,21 +22,354 @@
#define LAST_CHAR 1000000
/*
- * Forward declarations for procedures defined later in this file:
+ * Modifiers for index parsing: 'display', 'any' or nothing.
+ */
+
+#define TKINDEX_NONE 0
+#define TKINDEX_DISPLAY 1
+#define TKINDEX_ANY 2
+
+/*
+ * Forward declarations for functions defined later in this file:
+ */
+
+static CONST char * ForwBack(TkText *textPtr, CONST char *string,
+ TkTextIndex *indexPtr);
+static CONST char * StartEnd(TkText *textPtr, CONST char *string,
+ TkTextIndex *indexPtr);
+static int GetIndex(Tcl_Interp *interp, TkSharedText *sharedPtr,
+ TkText *textPtr, CONST char *string,
+ TkTextIndex *indexPtr, int *canCachePtr);
+
+/*
+ * The "textindex" Tcl_Obj definition:
+ */
+
+static void DupTextIndexInternalRep(Tcl_Obj *srcPtr,
+ Tcl_Obj *copyPtr);
+static void FreeTextIndexInternalRep(Tcl_Obj *listPtr);
+static int SetTextIndexFromAny(Tcl_Interp *interp,
+ Tcl_Obj *objPtr);
+static void UpdateStringOfTextIndex(Tcl_Obj *objPtr);
+
+/*
+ * Accessor macros for the "textindex" type.
+ */
+
+#define GET_TEXTINDEX(objPtr) \
+ ((TkTextIndex *) (objPtr)->internalRep.twoPtrValue.ptr1)
+#define GET_INDEXEPOCH(objPtr) \
+ (PTR2INT((objPtr)->internalRep.twoPtrValue.ptr2))
+#define SET_TEXTINDEX(objPtr, indexPtr) \
+ ((objPtr)->internalRep.twoPtrValue.ptr1 = (VOID *) (indexPtr))
+#define SET_INDEXEPOCH(objPtr, epoch) \
+ ((objPtr)->internalRep.twoPtrValue.ptr2 = INT2PTR(epoch))
+
+/*
+ * Define the 'textindex' object type, which Tk uses to represent indices in
+ * text widgets internally.
+ */
+
+Tcl_ObjType tkTextIndexType = {
+ "textindex", /* name */
+ FreeTextIndexInternalRep, /* freeIntRepProc */
+ DupTextIndexInternalRep, /* dupIntRepProc */
+ NULL, /* updateStringProc */
+ SetTextIndexFromAny /* setFromAnyProc */
+};
+
+static void
+FreeTextIndexInternalRep(
+ Tcl_Obj *indexObjPtr) /* TextIndex object with internal rep to
+ * free. */
+{
+ TkTextIndex *indexPtr = GET_TEXTINDEX(indexObjPtr);
+ if (indexPtr->textPtr != NULL) {
+ if (--indexPtr->textPtr->refCount == 0) {
+ /*
+ * The text widget has been deleted and we need to free it now.
+ */
+
+ ckfree((char *) (indexPtr->textPtr));
+ }
+ }
+ ckfree((char *) indexPtr);
+}
+
+static void
+DupTextIndexInternalRep(
+ Tcl_Obj *srcPtr, /* TextIndex obj with internal rep to copy. */
+ Tcl_Obj *copyPtr) /* TextIndex obj with internal rep to set. */
+{
+ int epoch;
+ TkTextIndex *dupIndexPtr, *indexPtr;
+
+ dupIndexPtr = (TkTextIndex *) ckalloc(sizeof(TkTextIndex));
+ indexPtr = GET_TEXTINDEX(srcPtr);
+ epoch = GET_INDEXEPOCH(srcPtr);
+
+ dupIndexPtr->tree = indexPtr->tree;
+ dupIndexPtr->linePtr = indexPtr->linePtr;
+ dupIndexPtr->byteIndex = indexPtr->byteIndex;
+ dupIndexPtr->textPtr = indexPtr->textPtr;
+ if (dupIndexPtr->textPtr != NULL) {
+ dupIndexPtr->textPtr->refCount++;
+ }
+ SET_TEXTINDEX(copyPtr, dupIndexPtr);
+ SET_INDEXEPOCH(copyPtr, epoch);
+ copyPtr->typePtr = &tkTextIndexType;
+}
+
+/*
+ * This will not be called except by TkTextNewIndexObj below. This is because
+ * if a TkTextIndex is no longer valid, it is not possible to regenerate the
+ * string representation.
*/
-static CONST char * ForwBack _ANSI_ARGS_((CONST char *string,
- TkTextIndex *indexPtr));
-static CONST char * StartEnd _ANSI_ARGS_((CONST char *string,
- TkTextIndex *indexPtr));
+static void
+UpdateStringOfTextIndex(
+ Tcl_Obj *objPtr)
+{
+ char buffer[TK_POS_CHARS];
+ register int len;
+
+ CONST TkTextIndex *indexPtr = GET_TEXTINDEX(objPtr);
+
+ len = TkTextPrintIndex(indexPtr->textPtr, indexPtr, buffer);
+
+ objPtr->bytes = ckalloc((unsigned) len + 1);
+ strcpy(objPtr->bytes, buffer);
+ objPtr->length = len;
+}
+
+static int
+SetTextIndexFromAny(
+ Tcl_Interp *interp, /* Used for error reporting if not NULL. */
+ Tcl_Obj *objPtr) /* The object to convert. */
+{
+ Tcl_AppendToObj(Tcl_GetObjResult(interp),
+ "can't convert value to textindex except via TkTextGetIndexFromObj API",
+ -1);
+ return TCL_ERROR;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * MakeObjIndex --
+ *
+ * This function generates a Tcl_Obj description of an index, suitable
+ * for reading in again later. If the 'textPtr' is NULL then we still
+ * generate an index object, but it's internal description is deemed
+ * non-cacheable, and therefore effectively useless (apart from as a
+ * temporary memory storage). This is used for indices whose meaning is
+ * very temporary (like @0,0 or the name of a mark or tag). The mapping
+ * from such strings/objects to actual TkTextIndex pointers is not stable
+ * to minor text widget changes which we do not track (we track
+ * insertions and deletions).
+ *
+ * Results:
+ * A pointer to an allocated TkTextIndex which will be freed
+ * automatically when the Tcl_Obj is used for other purposes.
+ *
+ * Side effects:
+ * A small amount of memory is allocated.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+static TkTextIndex *
+MakeObjIndex(
+ TkText *textPtr, /* Information about text widget. */
+ Tcl_Obj *objPtr, /* Object containing description of
+ * position. */
+ CONST TkTextIndex *origPtr) /* Pointer to index. */
+{
+ TkTextIndex *indexPtr = (TkTextIndex *) ckalloc(sizeof(TkTextIndex));
+
+ indexPtr->tree = origPtr->tree;
+ indexPtr->linePtr = origPtr->linePtr;
+ indexPtr->byteIndex = origPtr->byteIndex;
+ SET_TEXTINDEX(objPtr, indexPtr);
+ objPtr->typePtr = &tkTextIndexType;
+ indexPtr->textPtr = textPtr;
+
+ if (textPtr != NULL) {
+ textPtr->refCount++;
+ SET_INDEXEPOCH(objPtr, textPtr->sharedTextPtr->stateEpoch);
+ } else {
+ SET_INDEXEPOCH(objPtr, 0);
+ }
+ return indexPtr;
+}
+
+CONST TkTextIndex *
+TkTextGetIndexFromObj(
+ Tcl_Interp *interp, /* Use this for error reporting. */
+ TkText *textPtr, /* Information about text widget. */
+ Tcl_Obj *objPtr) /* Object containing description of
+ * position. */
+{
+ TkTextIndex index;
+ TkTextIndex *indexPtr = NULL;
+ int cache;
+
+ if (objPtr->typePtr == &tkTextIndexType) {
+ int epoch;
+
+ indexPtr = GET_TEXTINDEX(objPtr);
+ epoch = GET_INDEXEPOCH(objPtr);
+
+ if (epoch == textPtr->sharedTextPtr->stateEpoch) {
+ if (indexPtr->textPtr == textPtr) {
+ return indexPtr;
+ }
+ }
+ }
+
+ /*
+ * The object is either not an index type or referred to a different text
+ * widget, or referred to the correct widget, but it is out of date (text
+ * has been added/deleted since).
+ */
+
+ if (GetIndex(interp, NULL, textPtr, Tcl_GetString(objPtr), &index,
+ &cache) != TCL_OK) {
+ return NULL;
+ }
+
+ if (objPtr->typePtr != NULL) {
+ if (objPtr->bytes == NULL) {
+ objPtr->typePtr->updateStringProc(objPtr);
+ }
+ if ((objPtr->typePtr->freeIntRepProc) != NULL) {
+ (*objPtr->typePtr->freeIntRepProc)(objPtr);
+ }
+ }
+
+ return MakeObjIndex((cache ? textPtr : NULL), objPtr, &index);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * TkTextNewIndexObj --
+ *
+ * This function generates a Tcl_Obj description of an index, suitable
+ * for reading in again later. The index generated is effectively stable
+ * to all except insertion/deletion operations on the widget.
+ *
+ * Results:
+ * A new Tcl_Obj with refCount zero.
+ *
+ * Side effects:
+ * A small amount of memory is allocated.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+Tcl_Obj *
+TkTextNewIndexObj(
+ TkText *textPtr, /* Text widget for this index */
+ CONST TkTextIndex *indexPtr)/* Pointer to index. */
+{
+ Tcl_Obj *retVal;
+
+ retVal = Tcl_NewObj();
+ retVal->bytes = NULL;
+
+ /*
+ * Assumption that the above call returns an object with:
+ * retVal->typePtr == NULL
+ */
+
+ MakeObjIndex(textPtr, retVal, indexPtr);
+
+ /*
+ * Unfortunately, it isn't possible for us to regenerate the string
+ * representation so we have to create it here, while we can be sure the
+ * contents of the index are still valid.
+ */
+
+ UpdateStringOfTextIndex(retVal);
+ return retVal;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * TkTextMakePixelIndex --
+ *
+ * Given a pixel index and a byte index, look things up in the B-tree and
+ * fill in a TkTextIndex structure.
+ *
+ * The valid input range for pixelIndex is from 0 to the number of pixels
+ * in the widget-1. Anything outside that range will be rounded to the
+ * closest acceptable value.
+ *
+ * Results:
+ *
+ * The structure at *indexPtr is filled in with information about the
+ * character at pixelIndex (or the closest existing character, if the
+ * specified one doesn't exist), and the number of excess pixels is
+ * returned as a result. This means if the given pixel index is exactly
+ * correct for the top-edge of the indexPtr, then zero will be returned,
+ * and otherwise we will return the calculation 'desired pixelIndex' -
+ * 'actual pixel index of indexPtr'.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+int
+TkTextMakePixelIndex(
+ TkText *textPtr, /* The Text Widget */
+ int pixelIndex, /* Pixel-index of desired line (0 means first
+ * pixel of first line of text). */
+ TkTextIndex *indexPtr) /* Structure to fill in. */
+{
+ int pixelOffset = 0;
+
+ indexPtr->tree = textPtr->sharedTextPtr->tree;
+ indexPtr->textPtr = textPtr;
+
+ if (pixelIndex < 0) {
+ pixelIndex = 0;
+ }
+ indexPtr->linePtr = TkBTreeFindPixelLine(textPtr->sharedTextPtr->tree,
+ textPtr, pixelIndex, &pixelOffset);
+
+ /*
+ * 'pixelIndex' was too large, so we try again, just to find the last
+ * pixel in the window.
+ */
+
+ if (indexPtr->linePtr == NULL) {
+ int lastMinusOne = TkBTreeNumPixels(textPtr->sharedTextPtr->tree,
+ textPtr)-1;
+
+ indexPtr->linePtr = TkBTreeFindPixelLine(textPtr->sharedTextPtr->tree,
+ textPtr, lastMinusOne, &pixelOffset);
+ indexPtr->byteIndex = 0;
+ return pixelOffset;
+ }
+ indexPtr->byteIndex = 0;
+
+ if (pixelOffset <= 0) {
+ return 0;
+ }
+ return TkTextMeasureDown(textPtr, indexPtr, pixelOffset);
+}
/*
*---------------------------------------------------------------------------
*
* TkTextMakeByteIndex --
*
- * Given a line index and a byte index, look things up in the B-tree
- * and fill in a TkTextIndex structure.
+ * Given a line index and a byte index, look things up in the B-tree and
+ * fill in a TkTextIndex structure.
*
* Results:
* The structure at *indexPtr is filled in with information about the
@@ -52,13 +384,14 @@ static CONST char * StartEnd _ANSI_ARGS_((CONST char *string,
*/
TkTextIndex *
-TkTextMakeByteIndex(tree, lineIndex, byteIndex, indexPtr)
- TkTextBTree tree; /* Tree that lineIndex and charIndex refer
+TkTextMakeByteIndex(
+ TkTextBTree tree, /* Tree that lineIndex and byteIndex refer
* to. */
- int lineIndex; /* Index of desired line (0 means first
- * line of text). */
- int byteIndex; /* Byte index of desired character. */
- TkTextIndex *indexPtr; /* Structure to fill in. */
+ CONST TkText *textPtr,
+ int lineIndex, /* Index of desired line (0 means first line
+ * of text). */
+ int byteIndex, /* Byte index of desired character. */
+ TkTextIndex *indexPtr) /* Structure to fill in. */
{
TkTextSegment *segPtr;
int index;
@@ -73,9 +406,10 @@ TkTextMakeByteIndex(tree, lineIndex, byteIndex, indexPtr)
if (byteIndex < 0) {
byteIndex = 0;
}
- indexPtr->linePtr = TkBTreeFindLine(tree, lineIndex);
+ indexPtr->linePtr = TkBTreeFindLine(tree, textPtr, lineIndex);
if (indexPtr->linePtr == NULL) {
- indexPtr->linePtr = TkBTreeFindLine(tree, TkBTreeNumLines(tree));
+ indexPtr->linePtr = TkBTreeFindLine(tree, textPtr,
+ TkBTreeNumLines(tree, textPtr));
byteIndex = 0;
}
if (byteIndex == 0) {
@@ -84,19 +418,19 @@ TkTextMakeByteIndex(tree, lineIndex, byteIndex, indexPtr)
}
/*
- * Verify that the index is within the range of the line and points
- * to a valid character boundary.
+ * Verify that the index is within the range of the line and points to a
+ * valid character boundary.
*/
index = 0;
for (segPtr = indexPtr->linePtr->segPtr; ; segPtr = segPtr->nextPtr) {
if (segPtr == NULL) {
/*
- * Use the index of the last character in the line. Since
- * the last character on the line is guaranteed to be a '\n',
- * we can back up a constant sizeof(char) bytes.
+ * Use the index of the last character in the line. Since the last
+ * character on the line is guaranteed to be a '\n', we can back
+ * up a constant sizeof(char) bytes.
*/
-
+
indexPtr->byteIndex = index - sizeof(char);
break;
}
@@ -105,7 +439,7 @@ TkTextMakeByteIndex(tree, lineIndex, byteIndex, indexPtr)
if ((byteIndex > index) && (segPtr->typePtr == &tkTextCharType)) {
/*
* Prevent UTF-8 character from being split up by ensuring
- * that byteIndex falls on a character boundary. If index
+ * that byteIndex falls on a character boundary. If index
* falls in the middle of a UTF-8 character, it will be
* adjusted to the end of that UTF-8 character.
*/
@@ -127,8 +461,8 @@ TkTextMakeByteIndex(tree, lineIndex, byteIndex, indexPtr)
*
* TkTextMakeCharIndex --
*
- * Given a line index and a character index, look things up in the
- * B-tree and fill in a TkTextIndex structure.
+ * Given a line index and a character index, look things up in the B-tree
+ * and fill in a TkTextIndex structure.
*
* Results:
* The structure at *indexPtr is filled in with information about the
@@ -143,13 +477,14 @@ TkTextMakeByteIndex(tree, lineIndex, byteIndex, indexPtr)
*/
TkTextIndex *
-TkTextMakeCharIndex(tree, lineIndex, charIndex, indexPtr)
- TkTextBTree tree; /* Tree that lineIndex and charIndex refer
+TkTextMakeCharIndex(
+ TkTextBTree tree, /* Tree that lineIndex and charIndex refer
* to. */
- int lineIndex; /* Index of desired line (0 means first
- * line of text). */
- int charIndex; /* Index of desired character. */
- TkTextIndex *indexPtr; /* Structure to fill in. */
+ TkText *textPtr,
+ int lineIndex, /* Index of desired line (0 means first line
+ * of text). */
+ int charIndex, /* Index of desired character. */
+ TkTextIndex *indexPtr) /* Structure to fill in. */
{
register TkTextSegment *segPtr;
char *p, *start, *end;
@@ -164,26 +499,27 @@ TkTextMakeCharIndex(tree, lineIndex, charIndex, indexPtr)
if (charIndex < 0) {
charIndex = 0;
}
- indexPtr->linePtr = TkBTreeFindLine(tree, lineIndex);
+ indexPtr->linePtr = TkBTreeFindLine(tree, textPtr, lineIndex);
if (indexPtr->linePtr == NULL) {
- indexPtr->linePtr = TkBTreeFindLine(tree, TkBTreeNumLines(tree));
+ indexPtr->linePtr = TkBTreeFindLine(tree, textPtr,
+ TkBTreeNumLines(tree, textPtr));
charIndex = 0;
}
/*
- * Verify that the index is within the range of the line.
- * If not, just use the index of the last character in the line.
+ * Verify that the index is within the range of the line. If not, just use
+ * the index of the last character in the line.
*/
index = 0;
for (segPtr = indexPtr->linePtr->segPtr; ; segPtr = segPtr->nextPtr) {
if (segPtr == NULL) {
/*
- * Use the index of the last character in the line. Since
- * the last character on the line is guaranteed to be a '\n',
- * we can back up a constant sizeof(char) bytes.
+ * Use the index of the last character in the line. Since the last
+ * character on the line is guaranteed to be a '\n', we can back
+ * up a constant sizeof(char) bytes.
*/
-
+
indexPtr->byteIndex = index - sizeof(char);
break;
}
@@ -220,14 +556,14 @@ TkTextMakeCharIndex(tree, lineIndex, charIndex, indexPtr)
*
* TkTextIndexToSeg --
*
- * Given an index, this procedure returns the segment and offset
- * within segment for the index.
+ * Given an index, this function returns the segment and offset within
+ * segment for the index.
*
* Results:
- * The return value is a pointer to the segment referred to by
- * indexPtr; this will always be a segment with non-zero size. The
- * variable at *offsetPtr is set to hold the integer offset within
- * the segment of the character given by indexPtr.
+ * The return value is a pointer to the segment referred to by indexPtr;
+ * this will always be a segment with non-zero size. The variable at
+ * *offsetPtr is set to hold the integer offset within the segment of the
+ * character given by indexPtr.
*
* Side effects:
* None.
@@ -236,9 +572,9 @@ TkTextMakeCharIndex(tree, lineIndex, charIndex, indexPtr)
*/
TkTextSegment *
-TkTextIndexToSeg(indexPtr, offsetPtr)
- CONST TkTextIndex *indexPtr;/* Text index. */
- int *offsetPtr; /* Where to store offset within segment, or
+TkTextIndexToSeg(
+ CONST TkTextIndex *indexPtr,/* Text index. */
+ int *offsetPtr) /* Where to store offset within segment, or
* NULL if offset isn't wanted. */
{
TkTextSegment *segPtr;
@@ -260,7 +596,7 @@ TkTextIndexToSeg(indexPtr, offsetPtr)
*
* TkTextSegToOffset --
*
- * Given a segment pointer and the line containing it, this procedure
+ * Given a segment pointer and the line containing it, this function
* returns the offset of the segment within its line.
*
* Results:
@@ -274,14 +610,13 @@ TkTextIndexToSeg(indexPtr, offsetPtr)
*/
int
-TkTextSegToOffset(segPtr, linePtr)
- CONST TkTextSegment *segPtr;/* Segment whose offset is desired. */
- CONST TkTextLine *linePtr; /* Line containing segPtr. */
+TkTextSegToOffset(
+ CONST TkTextSegment *segPtr,/* Segment whose offset is desired. */
+ CONST TkTextLine *linePtr) /* Line containing segPtr. */
{
CONST TkTextSegment *segPtr2;
- int offset;
+ int offset = 0;
- offset = 0;
for (segPtr2 = linePtr->segPtr; segPtr2 != segPtr;
segPtr2 = segPtr2->nextPtr) {
offset += segPtr2->size;
@@ -292,15 +627,73 @@ TkTextSegToOffset(segPtr, linePtr)
/*
*---------------------------------------------------------------------------
*
+ * TkTextGetObjIndex --
+ *
+ * Simpler wrapper around the string based function, but could be
+ * enhanced with a new object type in the future.
+ *
+ * Results:
+ * see TkTextGetIndex
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+int
+TkTextGetObjIndex(
+ Tcl_Interp *interp, /* Use this for error reporting. */
+ TkText *textPtr, /* Information about text widget. */
+ Tcl_Obj *idxObj, /* Object containing textual description of
+ * position. */
+ TkTextIndex *indexPtr) /* Index structure to fill in. */
+{
+ return GetIndex(interp, NULL, textPtr, Tcl_GetString(idxObj), indexPtr,
+ NULL);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * TkTextSharedGetObjIndex --
+ *
+ * Simpler wrapper around the string based function, but could be
+ * enhanced with a new object type in the future.
+ *
+ * Results:
+ * see TkTextGetIndex
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+int
+TkTextSharedGetObjIndex(
+ Tcl_Interp *interp, /* Use this for error reporting. */
+ TkSharedText *sharedTextPtr,/* Information about text widget. */
+ Tcl_Obj *idxObj, /* Object containing textual description of
+ * position. */
+ TkTextIndex *indexPtr) /* Index structure to fill in. */
+{
+ return GetIndex(interp, sharedTextPtr, NULL, Tcl_GetString(idxObj),
+ indexPtr, NULL);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
* TkTextGetIndex --
*
* Given a string, return the index that is described.
*
* Results:
- * The return value is a standard Tcl return result. If TCL_OK is
+ * The return value is a standard Tcl return result. If TCL_OK is
* returned, then everything went well and the index at *indexPtr is
- * filled in; otherwise TCL_ERROR is returned and an error message
- * is left in the interp's result.
+ * filled in; otherwise TCL_ERROR is returned and an error message is
+ * left in the interp's result.
*
* Side effects:
* None.
@@ -309,33 +702,70 @@ TkTextSegToOffset(segPtr, linePtr)
*/
int
-TkTextGetIndex(interp, textPtr, string, indexPtr)
- Tcl_Interp *interp; /* Use this for error reporting. */
- TkText *textPtr; /* Information about text widget. */
- CONST char *string; /* Textual description of position. */
- TkTextIndex *indexPtr; /* Index structure to fill in. */
+TkTextGetIndex(
+ Tcl_Interp *interp, /* Use this for error reporting. */
+ TkText *textPtr, /* Information about text widget. */
+ CONST char *string, /* Textual description of position. */
+ TkTextIndex *indexPtr) /* Index structure to fill in. */
+{
+ return GetIndex(interp, NULL, textPtr, string, indexPtr, NULL);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * GetIndex --
+ *
+ * Given a string, return the index that is described.
+ *
+ * Results:
+ * The return value is a standard Tcl return result. If TCL_OK is
+ * returned, then everything went well and the index at *indexPtr is
+ * filled in; otherwise TCL_ERROR is returned and an error message is
+ * left in the interp's result.
+ *
+ * If *canCachePtr is non-NULL, and everything went well, the integer it
+ * points to is set to 1 if the indexPtr is something which can be
+ * cached, and zero otherwise.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+static int
+GetIndex(
+ Tcl_Interp *interp, /* Use this for error reporting. */
+ TkSharedText *sharedPtr,
+ TkText *textPtr, /* Information about text widget. */
+ CONST char *string, /* Textual description of position. */
+ TkTextIndex *indexPtr, /* Index structure to fill in. */
+ int *canCachePtr) /* Pointer to integer to store whether we can
+ * cache the index (or NULL). */
{
char *p, *end, *endOfBase;
- Tcl_HashEntry *hPtr;
- TkTextTag *tagPtr;
- TkTextSearch search;
TkTextIndex first, last;
int wantLast, result;
char c;
CONST char *cp;
Tcl_DString copy;
+ int canCache = 0;
+
+ if (sharedPtr == NULL) {
+ sharedPtr = textPtr->sharedTextPtr;
+ }
/*
*---------------------------------------------------------------------
- * Stage 1: check to see if the index consists of nothing but a mark
- * name. We do this check now even though it's also done later, in
- * order to allow mark names that include funny characters such as
- * spaces or "+1c".
+ * Stage 1: check to see if the index consists of nothing but a mark name.
+ * We do this check now even though it's also done later, in order to
+ * allow mark names that include funny characters such as spaces or "+1c".
*---------------------------------------------------------------------
*/
if (TkTextMarkNameToIndex(textPtr, string, indexPtr) == TCL_OK) {
- return TCL_OK;
+ goto done;
}
/*
@@ -344,19 +774,24 @@ TkTextGetIndex(interp, textPtr, string, indexPtr)
*------------------------------------------------
*/
- indexPtr->tree = textPtr->tree;
+ indexPtr->tree = sharedPtr->tree;
/*
- * First look for the form "tag.first" or "tag.last" where "tag"
- * is the name of a valid tag. Try to use up as much as possible
- * of the string in this check (strrchr instead of strchr below).
- * Doing the check now, and in this way, allows tag names to include
- * funny characters like "@" or "+1c".
+ * First look for the form "tag.first" or "tag.last" where "tag" is the
+ * name of a valid tag. Try to use up as much as possible of the string in
+ * this check (strrchr instead of strchr below). Doing the check now, and
+ * in this way, allows tag names to include funny characters like "@" or
+ * "+1c".
*/
Tcl_DStringInit(&copy);
p = strrchr(Tcl_DStringAppend(&copy, string, -1), '.');
if (p != NULL) {
+ TkTextSearch search;
+ TkTextTag *tagPtr;
+ Tcl_HashEntry *hPtr = NULL;
+ CONST char *tagName;
+
if ((p[1] == 'f') && (strncmp(p+1, "first", 5) == 0)) {
wantLast = 0;
endOfBase = p+6;
@@ -366,23 +801,42 @@ TkTextGetIndex(interp, textPtr, string, indexPtr)
} else {
goto tryxy;
}
- *p = 0;
- hPtr = Tcl_FindHashEntry(&textPtr->tagTable, Tcl_DStringValue(&copy));
- *p = '.';
- if (hPtr == NULL) {
+
+ tagPtr = NULL;
+ tagName = Tcl_DStringValue(&copy);
+ if (((p - tagName) == 3) && !strncmp(tagName, "sel", 3)) {
+ /*
+ * Special case for sel tag which is not stored in the hash table.
+ */
+
+ tagPtr = textPtr->selTagPtr;
+ } else {
+ *p = 0;
+ hPtr = Tcl_FindHashEntry(&sharedPtr->tagTable, tagName);
+ *p = '.';
+ if (hPtr != NULL) {
+ tagPtr = (TkTextTag *) Tcl_GetHashValue(hPtr);
+ }
+ }
+
+ if (tagPtr == NULL) {
goto tryxy;
}
- tagPtr = (TkTextTag *) Tcl_GetHashValue(hPtr);
- TkTextMakeByteIndex(textPtr->tree, 0, 0, &first);
- TkTextMakeByteIndex(textPtr->tree, TkBTreeNumLines(textPtr->tree), 0,
- &last);
+
+ TkTextMakeByteIndex(sharedPtr->tree, textPtr, 0, 0, &first);
+ TkTextMakeByteIndex(sharedPtr->tree, textPtr,
+ TkBTreeNumLines(sharedPtr->tree, textPtr), 0, &last);
TkBTreeStartSearch(&first, &last, tagPtr, &search);
if (!TkBTreeCharTagged(&first, tagPtr) && !TkBTreeNextTag(&search)) {
+ if (tagPtr == textPtr->selTagPtr) {
+ tagName = "sel";
+ } else {
+ tagName = Tcl_GetHashKey(&sharedPtr->tagTable, hPtr);
+ }
Tcl_ResetResult(interp);
Tcl_AppendResult(interp,
"text doesn't contain any characters tagged with \"",
- Tcl_GetHashKey(&textPtr->tagTable, hPtr), "\"",
- (char *) NULL);
+ tagName, "\"", NULL);
Tcl_DStringFree(&copy);
return TCL_ERROR;
}
@@ -395,7 +849,7 @@ TkTextGetIndex(interp, textPtr, string, indexPtr)
goto gotBase;
}
- tryxy:
+ tryxy:
if (string[0] == '@') {
/*
* Find character at a given x,y location in the window.
@@ -413,9 +867,9 @@ TkTextGetIndex(interp, textPtr, string, indexPtr)
if (end == cp) {
goto error;
}
- TkTextPixelIndex(textPtr, x, y, indexPtr);
+ TkTextPixelIndex(textPtr, x, y, indexPtr, NULL);
endOfBase = end;
- goto gotBase;
+ goto gotBase;
}
if (isdigit(UCHAR(string[0])) || (string[0] == '-')) {
@@ -440,7 +894,9 @@ TkTextGetIndex(interp, textPtr, string, indexPtr)
}
endOfBase = end;
}
- TkTextMakeCharIndex(textPtr->tree, lineIndex, charIndex, indexPtr);
+ TkTextMakeCharIndex(sharedPtr->tree, textPtr, lineIndex, charIndex,
+ indexPtr);
+ canCache = 1;
goto gotBase;
}
@@ -470,8 +926,9 @@ TkTextGetIndex(interp, textPtr, string, indexPtr)
* Base position is end of text.
*/
- TkTextMakeByteIndex(textPtr->tree, TkBTreeNumLines(textPtr->tree),
- 0, indexPtr);
+ TkTextMakeByteIndex(sharedPtr->tree, textPtr,
+ TkBTreeNumLines(sharedPtr->tree, textPtr), 0, indexPtr);
+ canCache = 1;
goto gotBase;
} else {
/*
@@ -488,7 +945,7 @@ TkTextGetIndex(interp, textPtr, string, indexPtr)
}
/*
- * See if the base position is the name of an embedded image
+ * See if the base position is the name of an embedded image.
*/
c = *endOfBase;
@@ -503,14 +960,14 @@ TkTextGetIndex(interp, textPtr, string, indexPtr)
/*
*-------------------------------------------------------------------
- * Stage 3: process zero or more modifiers. Each modifier is either
- * a keyword like "wordend" or "linestart", or it has the form
- * "op count units" where op is + or -, count is a number, and units
- * is "chars" or "lines".
+ * Stage 3: process zero or more modifiers. Each modifier is either a
+ * keyword like "wordend" or "linestart", or it has the form "op count
+ * units" where op is + or -, count is a number, and units is "chars" or
+ * "lines".
*-------------------------------------------------------------------
*/
- gotBase:
+ gotBase:
cp = endOfBase;
while (1) {
while (isspace(UCHAR(*cp))) {
@@ -519,24 +976,31 @@ TkTextGetIndex(interp, textPtr, string, indexPtr)
if (*cp == 0) {
break;
}
-
+
if ((*cp == '+') || (*cp == '-')) {
- cp = ForwBack(cp, indexPtr);
+ cp = ForwBack(textPtr, cp, indexPtr);
} else {
- cp = StartEnd(cp, indexPtr);
+ cp = StartEnd(textPtr, cp, indexPtr);
}
if (cp == NULL) {
goto error;
}
}
Tcl_DStringFree(&copy);
+
+ done:
+ if (canCachePtr != NULL) {
+ *canCachePtr = canCache;
+ }
+ if (indexPtr->linePtr == NULL) {
+ Tcl_Panic("Bad index created");
+ }
return TCL_OK;
- error:
+ error:
Tcl_DStringFree(&copy);
Tcl_ResetResult(interp);
- Tcl_AppendResult(interp, "bad text index \"", string, "\"",
- (char *) NULL);
+ Tcl_AppendResult(interp, "bad text index \"", string, "\"", NULL);
return TCL_ERROR;
}
@@ -544,12 +1008,13 @@ TkTextGetIndex(interp, textPtr, string, indexPtr)
*---------------------------------------------------------------------------
*
* TkTextPrintIndex --
- *
- * This procedure generates a string description of an index, suitable
- * for reading in again later.
+ *
+ * This function generates a string description of an index, suitable for
+ * reading in again later.
*
* Results:
- * The characters pointed to by string are modified.
+ * The characters pointed to by string are modified. Returns the number
+ * of characters in the string.
*
* Side effects:
* None.
@@ -557,18 +1022,31 @@ TkTextGetIndex(interp, textPtr, string, indexPtr)
*---------------------------------------------------------------------------
*/
-void
-TkTextPrintIndex(indexPtr, string)
- CONST TkTextIndex *indexPtr;/* Pointer to index. */
- char *string; /* Place to store the position. Must have
- * at least TK_POS_CHARS characters. */
+int
+TkTextPrintIndex(
+ CONST TkText *textPtr,
+ CONST TkTextIndex *indexPtr,/* Pointer to index. */
+ char *string) /* Place to store the position. Must have at
+ * least TK_POS_CHARS characters. */
{
TkTextSegment *segPtr;
+ TkTextLine *linePtr;
int numBytes, charIndex;
numBytes = indexPtr->byteIndex;
charIndex = 0;
- for (segPtr = indexPtr->linePtr->segPtr; ; segPtr = segPtr->nextPtr) {
+ linePtr = indexPtr->linePtr;
+
+ for (segPtr = linePtr->segPtr; ; segPtr = segPtr->nextPtr) {
+ if (segPtr == NULL) {
+ /*
+ * Two logical lines merged into one display line through eliding
+ * of a newline.
+ */
+
+ linePtr = TkBTreeNextLine(NULL, linePtr);
+ segPtr = linePtr->segPtr;
+ }
if (numBytes <= segPtr->size) {
break;
}
@@ -579,13 +1057,15 @@ TkTextPrintIndex(indexPtr, string)
}
numBytes -= segPtr->size;
}
+
if (segPtr->typePtr == &tkTextCharType) {
charIndex += Tcl_NumUtfChars(segPtr->body.chars, numBytes);
} else {
charIndex += numBytes;
}
- sprintf(string, "%d.%d", TkBTreeLineIndex(indexPtr->linePtr) + 1,
- charIndex);
+
+ return sprintf(string, "%d.%d",
+ TkBTreeLinesTo(textPtr, indexPtr->linePtr) + 1, charIndex);
}
/*
@@ -607,9 +1087,9 @@ TkTextPrintIndex(indexPtr, string)
*/
int
-TkTextIndexCmp(index1Ptr, index2Ptr)
- CONST TkTextIndex *index1Ptr; /* First index. */
- CONST TkTextIndex *index2Ptr; /* Second index. */
+TkTextIndexCmp(
+ CONST TkTextIndex*index1Ptr,/* First index. */
+ CONST TkTextIndex*index2Ptr)/* Second index. */
{
int line1, line2;
@@ -622,8 +1102,16 @@ TkTextIndexCmp(index1Ptr, index2Ptr)
return 0;
}
}
- line1 = TkBTreeLineIndex(index1Ptr->linePtr);
- line2 = TkBTreeLineIndex(index2Ptr->linePtr);
+
+ /*
+ * Assumption here that it is ok for comparisons to reflect the full
+ * B-tree and not just the portion that is available to any client. This
+ * should be true because the only indexPtr's we should be given are ones
+ * which are valid for the current client.
+ */
+
+ line1 = TkBTreeLinesTo(NULL, index1Ptr->linePtr);
+ line2 = TkBTreeLinesTo(NULL, index2Ptr->linePtr);
if (line1 < line2) {
return -1;
}
@@ -638,14 +1126,14 @@ TkTextIndexCmp(index1Ptr, index2Ptr)
*
* ForwBack --
*
- * This procedure handles +/- modifiers for indices to adjust the
- * index forwards or backwards.
+ * This function handles +/- modifiers for indices to adjust the index
+ * forwards or backwards.
*
* Results:
- * If the modifier in string is successfully parsed then the return
- * value is the address of the first character after the modifier,
- * and *indexPtr is updated to reflect the modifier. If there is a
- * syntax error in the modifier then NULL is returned.
+ * If the modifier in string is successfully parsed then the return value
+ * is the address of the first character after the modifier, and
+ * *indexPtr is updated to reflect the modifier. If there is a syntax
+ * error in the modifier then NULL is returned.
*
* Side effects:
* None.
@@ -654,16 +1142,16 @@ TkTextIndexCmp(index1Ptr, index2Ptr)
*/
static CONST char *
-ForwBack(string, indexPtr)
- CONST char *string; /* String to parse for additional info
- * about modifier (count and units).
- * Points to "+" or "-" that starts
- * modifier. */
- TkTextIndex *indexPtr; /* Index to update as specified in string. */
+ForwBack(
+ TkText *textPtr, /* Information about text widget. */
+ CONST char *string, /* String to parse for additional info about
+ * modifier (count and units). Points to "+"
+ * or "-" that starts modifier. */
+ TkTextIndex *indexPtr) /* Index to update as specified in string. */
{
register CONST char *p, *units;
char *end;
- int count, lineIndex;
+ int count, lineIndex, modifier;
size_t length;
/*
@@ -684,53 +1172,193 @@ ForwBack(string, indexPtr)
}
/*
- * Find the end of this modifier (next space or + or - character),
- * then parse the unit specifier and update the position
- * accordingly.
+ * Find the end of this modifier (next space or + or - character), then
+ * check if there is a textual 'display' or 'any' modifier. These
+ * modifiers can be their own word (in which case they can be abbreviated)
+ * or they can follow on to the actual unit in a single word (in which
+ * case no abbreviation is allowed). So, 'display lines', 'd lines',
+ * 'displaylin' are all ok, but 'dline' is not.
*/
- units = p;
+ units = p;
while ((*p != '\0') && !isspace(UCHAR(*p)) && (*p != '+') && (*p != '-')) {
p++;
}
length = p - units;
+ if ((*units == 'd') &&
+ (strncmp(units, "display", (length > 7 ? 7 : length)) == 0)) {
+ modifier = TKINDEX_DISPLAY;
+ if (length > 7) {
+ p -= (length - 7);
+ }
+ } else if ((*units == 'a') &&
+ (strncmp(units, "any", (length > 3 ? 3 : length)) == 0)) {
+ modifier = TKINDEX_ANY;
+ if (length > 3) {
+ p -= (length - 3);
+ }
+ } else {
+ modifier = TKINDEX_NONE;
+ }
+
+ /*
+ * If we had a modifier, which we interpreted ok, so now forward to the
+ * actual units.
+ */
+
+ if (modifier != TKINDEX_NONE) {
+ while (isspace(UCHAR(*p))) {
+ p++;
+ }
+ units = p;
+ while (*p!='\0' && !isspace(UCHAR(*p)) && *p!='+' && *p!='-') {
+ p++;
+ }
+ length = p - units;
+ }
+
+ /*
+ * Finally parse the units.
+ */
+
if ((*units == 'c') && (strncmp(units, "chars", length) == 0)) {
+ TkTextCountType type;
+
+ if (modifier == TKINDEX_NONE) {
+ type = COUNT_INDICES;
+ } else if (modifier == TKINDEX_ANY) {
+ type = COUNT_CHARS;
+ } else {
+ type = COUNT_DISPLAY_CHARS;
+ }
+
if (*string == '+') {
- TkTextIndexForwChars(indexPtr, count, indexPtr);
+ TkTextIndexForwChars(textPtr, indexPtr, count, indexPtr, type);
} else {
- TkTextIndexBackChars(indexPtr, count, indexPtr);
+ TkTextIndexBackChars(textPtr, indexPtr, count, indexPtr, type);
}
- } else if ((*units == 'l') && (strncmp(units, "lines", length) == 0)) {
- lineIndex = TkBTreeLineIndex(indexPtr->linePtr);
+ } else if ((*units == 'i') && (strncmp(units, "indices", length) == 0)) {
+ TkTextCountType type;
+
+ if (modifier == TKINDEX_DISPLAY) {
+ type = COUNT_DISPLAY_INDICES;
+ } else {
+ type = COUNT_INDICES;
+ }
+
if (*string == '+') {
- lineIndex += count;
+ TkTextIndexForwChars(textPtr, indexPtr, count, indexPtr, type);
} else {
- lineIndex -= count;
+ TkTextIndexBackChars(textPtr, indexPtr, count, indexPtr, type);
+ }
+ } else if ((*units == 'l') && (strncmp(units, "lines", length) == 0)) {
+ if (modifier == TKINDEX_DISPLAY) {
+ /*
+ * Find the appropriate pixel offset of the current position
+ * within its display line. This also has the side-effect of
+ * moving indexPtr, but that doesn't matter since we will do it
+ * again below.
+ *
+ * Then find the right display line, and finally calculated the
+ * index we want in that display line, based on the original pixel
+ * offset.
+ */
+
+ int xOffset, forward;
+
+ if (TkTextIsElided(textPtr, indexPtr, NULL)) {
+ /*
+ * Go forward to the first non-elided index.
+ */
+
+ TkTextIndexForwChars(textPtr, indexPtr, 0, indexPtr,
+ COUNT_DISPLAY_INDICES);
+ }
/*
- * The check below retains the character position, even
- * if the line runs off the start of the file. Without
- * it, the character position will get reset to 0 by
- * TkTextMakeIndex.
+ * Unlike the Forw/BackChars code, the display line code is
+ * sensitive to whether we are genuinely going forwards or
+ * backwards. So, we need to determine that. This is important in
+ * the case where we have "+ -3 displaylines", for example.
*/
- if (lineIndex < 0) {
- lineIndex = 0;
+ if ((count < 0) ^ (*string == '-')) {
+ forward = 0;
+ } else {
+ forward = 1;
}
- }
- /*
- * This doesn't work quite right if using a proportional font or
- * UTF-8 characters with varying numbers of bytes. The cursor will
- * bop around, keeping a constant number of bytes (not characters)
- * from the left edge (but making sure not to split any UTF-8
- * characters), regardless of the x-position the index corresponds
- * to. The proper way to do this is to get the x-position of the
- * index and then pick the character at the same x-position in the
- * new line.
- */
- TkTextMakeByteIndex(indexPtr->tree, lineIndex, indexPtr->byteIndex,
- indexPtr);
+ count = abs(count);
+ if (count == 0) {
+ return p;
+ }
+
+ if (forward) {
+ TkTextFindDisplayLineEnd(textPtr, indexPtr, 1, &xOffset);
+ while (count-- > 0) {
+ /*
+ * Go to the end of the line, then forward one char/byte
+ * to get to the beginning of the next line.
+ */
+
+ TkTextFindDisplayLineEnd(textPtr, indexPtr, 1, NULL);
+ TkTextIndexForwChars(textPtr, indexPtr, 1, indexPtr,
+ COUNT_DISPLAY_INDICES);
+ }
+ } else {
+ TkTextFindDisplayLineEnd(textPtr, indexPtr, 0, &xOffset);
+ while (count-- > 0) {
+ /*
+ * Go to the beginning of the line, then backward one
+ * char/byte to get to the end of the previous line.
+ */
+
+ TkTextFindDisplayLineEnd(textPtr, indexPtr, 0, NULL);
+ TkTextIndexBackChars(textPtr, indexPtr, 1, indexPtr,
+ COUNT_DISPLAY_INDICES);
+ }
+ TkTextFindDisplayLineEnd(textPtr, indexPtr, 0, NULL);
+ }
+
+ /*
+ * This call assumes indexPtr is the beginning of a display line
+ * and moves it to the 'xOffset' position of that line, which is
+ * just what we want.
+ */
+
+ TkTextIndexOfX(textPtr, xOffset, indexPtr);
+ } else {
+ lineIndex = TkBTreeLinesTo(textPtr, indexPtr->linePtr);
+ if (*string == '+') {
+ lineIndex += count;
+ } else {
+ lineIndex -= count;
+
+ /*
+ * The check below retains the character position, even if the
+ * line runs off the start of the file. Without it, the
+ * character position will get reset to 0 by TkTextMakeIndex.
+ */
+
+ if (lineIndex < 0) {
+ lineIndex = 0;
+ }
+ }
+
+ /*
+ * This doesn't work quite right if using a proportional font or
+ * UTF-8 characters with varying numbers of bytes, or if there are
+ * embedded windows, images, etc. The cursor will bop around,
+ * keeping a constant number of bytes (not characters) from the
+ * left edge (but making sure not to split any UTF-8 characters),
+ * regardless of the x-position the index corresponds to. The
+ * proper way to do this is to get the x-position of the index and
+ * then pick the character at the same x-position in the new line.
+ */
+
+ TkTextMakeByteIndex(indexPtr->tree, textPtr, lineIndex,
+ indexPtr->byteIndex, indexPtr);
+ }
} else {
return NULL;
}
@@ -742,13 +1370,16 @@ ForwBack(string, indexPtr)
*
* TkTextIndexForwBytes --
*
- * Given an index for a text widget, this procedure creates a new
- * index that points "count" bytes ahead of the source index.
+ * Given an index for a text widget, this function creates a new index
+ * that points "count" bytes ahead of the source index.
*
* Results:
* *dstPtr is modified to refer to the character "count" bytes after
- * srcPtr, or to the last character in the TkText if there aren't
- * "count" bytes left.
+ * srcPtr, or to the last character in the TkText if there aren't "count"
+ * bytes left.
+ *
+ * In this latter case, the function returns '1' to indicate that not all
+ * of 'byteCount' could be used.
*
* Side effects:
* None.
@@ -756,20 +1387,21 @@ ForwBack(string, indexPtr)
*---------------------------------------------------------------------------
*/
-void
-TkTextIndexForwBytes(srcPtr, byteCount, dstPtr)
- CONST TkTextIndex *srcPtr; /* Source index. */
- int byteCount; /* How many bytes forward to move. May be
+int
+TkTextIndexForwBytes(
+ CONST TkText *textPtr,
+ CONST TkTextIndex *srcPtr, /* Source index. */
+ int byteCount, /* How many bytes forward to move. May be
* negative. */
- TkTextIndex *dstPtr; /* Destination index: gets modified. */
+ TkTextIndex *dstPtr) /* Destination index: gets modified. */
{
TkTextLine *linePtr;
TkTextSegment *segPtr;
int lineLength;
if (byteCount < 0) {
- TkTextIndexBackBytes(srcPtr, -byteCount, dstPtr);
- return;
+ TkTextIndexBackBytes(textPtr, srcPtr, -byteCount, dstPtr);
+ return 0;
}
*dstPtr = *srcPtr;
@@ -786,18 +1418,18 @@ TkTextIndexForwBytes(srcPtr, byteCount, dstPtr)
}
/*
- * If the new index is in the same line then we're done.
- * Otherwise go on to the next line.
+ * If the new index is in the same line then we're done. Otherwise go
+ * on to the next line.
*/
if (dstPtr->byteIndex < lineLength) {
- return;
+ return 0;
}
dstPtr->byteIndex -= lineLength;
- linePtr = TkBTreeNextLine(dstPtr->linePtr);
+ linePtr = TkBTreeNextLine(textPtr, dstPtr->linePtr);
if (linePtr == NULL) {
dstPtr->byteIndex = lineLength - 1;
- return;
+ return 1;
}
dstPtr->linePtr = linePtr;
}
@@ -808,13 +1440,18 @@ TkTextIndexForwBytes(srcPtr, byteCount, dstPtr)
*
* TkTextIndexForwChars --
*
- * Given an index for a text widget, this procedure creates a new
- * index that points "count" characters ahead of the source index.
+ * Given an index for a text widget, this function creates a new index
+ * that points "count" items of type given by "type" ahead of the source
+ * index. "count" can be zero, which is useful in the case where one
+ * wishes to move forward by display (non-elided) chars or indices or one
+ * wishes to move forward by chars, skipping any intervening indices. In
+ * this case dstPtr will point to the first acceptable index which is
+ * encountered.
*
* Results:
- * *dstPtr is modified to refer to the character "count" characters
- * after srcPtr, or to the last character in the TkText if there
- * aren't "count" characters left in the file.
+ * *dstPtr is modified to refer to the character "count" items after
+ * srcPtr, or to the last character in the TkText if there aren't
+ * sufficient items left in the widget.
*
* Side effects:
* None.
@@ -823,31 +1460,52 @@ TkTextIndexForwBytes(srcPtr, byteCount, dstPtr)
*/
void
-TkTextIndexForwChars(srcPtr, charCount, dstPtr)
- CONST TkTextIndex *srcPtr; /* Source index. */
- int charCount; /* How many characters forward to move.
- * May be negative. */
- TkTextIndex *dstPtr; /* Destination index: gets modified. */
+TkTextIndexForwChars(
+ CONST TkText *textPtr, /* Overall information about text widget. */
+ CONST TkTextIndex *srcPtr, /* Source index. */
+ int charCount, /* How many characters forward to move. May
+ * be negative. */
+ TkTextIndex *dstPtr, /* Destination index: gets modified. */
+ TkTextCountType type) /* The type of item to count */
{
TkTextLine *linePtr;
TkTextSegment *segPtr;
+ TkTextElideInfo *infoPtr = NULL;
int byteOffset;
char *start, *end, *p;
Tcl_UniChar ch;
+ int elide = 0;
+ int checkElided = (type & COUNT_DISPLAY);
if (charCount < 0) {
- TkTextIndexBackChars(srcPtr, -charCount, dstPtr);
+ TkTextIndexBackChars(textPtr, srcPtr, -charCount, dstPtr, type);
return;
}
+ if (checkElided) {
+ infoPtr = (TkTextElideInfo *)
+ ckalloc((unsigned) sizeof(TkTextElideInfo));
+ elide = TkTextIsElided(textPtr, srcPtr, infoPtr);
+ }
*dstPtr = *srcPtr;
/*
- * Find seg that contains src byteIndex.
- * Move forward specified number of chars.
+ * Find seg that contains src byteIndex. Move forward specified number of
+ * chars.
*/
- segPtr = TkTextIndexToSeg(dstPtr, &byteOffset);
+ if (checkElided) {
+ /*
+ * In this case we have already calculated the information we need, so
+ * no need to use TkTextIndexToSeg()
+ */
+
+ segPtr = infoPtr->segPtr;
+ byteOffset = dstPtr->byteIndex - infoPtr->segOffset;
+ } else {
+ segPtr = TkTextIndexToSeg(dstPtr, &byteOffset);
+ }
+
while (1) {
/*
* Go through each segment in line looking for specified character
@@ -855,76 +1513,341 @@ TkTextIndexForwChars(srcPtr, charCount, dstPtr)
*/
for ( ; segPtr != NULL; segPtr = segPtr->nextPtr) {
- if (segPtr->typePtr == &tkTextCharType) {
- start = segPtr->body.chars + byteOffset;
- end = segPtr->body.chars + segPtr->size;
- for (p = start; p < end; p += Tcl_UtfToUniChar(p, &ch)) {
- if (charCount == 0) {
- dstPtr->byteIndex += (p - start);
- return;
+ /*
+ * If we do need to pay attention to the visibility of
+ * characters/indices, check that first. If the current segment
+ * isn't visible, then we simply continue the loop.
+ */
+
+ if (checkElided && ((segPtr->typePtr == &tkTextToggleOffType)
+ || (segPtr->typePtr == &tkTextToggleOnType))) {
+ TkTextTag *tagPtr = segPtr->body.toggle.tagPtr;
+
+ /*
+ * The elide state only changes if this tag is either the
+ * current highest priority tag (and is therefore being
+ * toggled off), or it's a new tag with higher priority.
+ */
+
+ if (tagPtr->elideString != NULL) {
+ infoPtr->tagCnts[tagPtr->priority]++;
+ if (infoPtr->tagCnts[tagPtr->priority] & 1) {
+ infoPtr->tagPtrs[tagPtr->priority] = tagPtr;
+ }
+
+ if (tagPtr->priority >= infoPtr->elidePriority) {
+ if (segPtr->typePtr == &tkTextToggleOffType) {
+ /*
+ * If it is being toggled off, and it has an elide
+ * string, it must actually be the current highest
+ * priority tag, so this check is redundant:
+ */
+
+ if (tagPtr->priority != infoPtr->elidePriority) {
+ Tcl_Panic("Bad tag priority being toggled off");
+ }
+
+ /*
+ * Find previous elide tag, if any (if not then
+ * elide will be zero, of course).
+ */
+
+ elide = 0;
+ while (--infoPtr->elidePriority > 0) {
+ if (infoPtr->tagCnts[infoPtr->elidePriority]
+ & 1) {
+ elide = infoPtr->tagPtrs
+ [infoPtr->elidePriority]->elide;
+ break;
+ }
+ }
+ } else {
+ elide = tagPtr->elide;
+ infoPtr->elidePriority = tagPtr->priority;
+ }
}
- charCount--;
}
- } else {
- if (charCount < segPtr->size - byteOffset) {
- dstPtr->byteIndex += charCount;
- return;
+ }
+
+ if (!elide) {
+ if (segPtr->typePtr == &tkTextCharType) {
+ start = segPtr->body.chars + byteOffset;
+ end = segPtr->body.chars + segPtr->size;
+ for (p = start; p < end; p += Tcl_UtfToUniChar(p, &ch)) {
+ if (charCount == 0) {
+ dstPtr->byteIndex += (p - start);
+ goto forwardCharDone;
+ }
+ charCount--;
+ }
+ } else if (type & COUNT_INDICES) {
+ if (charCount < segPtr->size - byteOffset) {
+ dstPtr->byteIndex += charCount;
+ goto forwardCharDone;
+ }
+ charCount -= segPtr->size - byteOffset;
}
- charCount -= segPtr->size - byteOffset;
}
+
dstPtr->byteIndex += segPtr->size - byteOffset;
byteOffset = 0;
}
/*
- * Go to the next line. If we are at the end of the text item,
- * back up one byte (for the terminal '\n' character) and return
- * that index.
+ * Go to the next line. If we are at the end of the text item, back up
+ * one byte (for the terminal '\n' character) and return that index.
*/
-
- linePtr = TkBTreeNextLine(dstPtr->linePtr);
+
+ linePtr = TkBTreeNextLine(textPtr, dstPtr->linePtr);
if (linePtr == NULL) {
dstPtr->byteIndex -= sizeof(char);
- return;
+ goto forwardCharDone;
}
dstPtr->linePtr = linePtr;
dstPtr->byteIndex = 0;
segPtr = dstPtr->linePtr->segPtr;
}
+
+ forwardCharDone:
+ if (infoPtr != NULL) {
+ TkTextFreeElideInfo(infoPtr);
+ ckfree((char *) infoPtr);
+ }
}
/*
*---------------------------------------------------------------------------
*
+ * TkTextIndexCount --
+ *
+ * Given an ordered pair of indices in a text widget, this function
+ * counts how many characters (not bytes) are between the two indices.
+ *
+ * It is illegal to call this function with unordered indices.
+ *
+ * Note that 'textPtr' is only used if we need to check for elided
+ * attributes, i.e. if type is COUNT_DISPLAY_INDICES or
+ * COUNT_DISPLAY_CHARS.
+ *
+ * Results:
+ * The number of characters in the given range, which meet the
+ * appropriate 'type' attributes.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+int
+TkTextIndexCount(
+ CONST TkText *textPtr, /* Overall information about text widget. */
+ CONST TkTextIndex *indexPtr1,
+ /* Index describing location of character from
+ * which to count. */
+ CONST TkTextIndex *indexPtr2,
+ /* Index describing location of last character
+ * at which to stop the count. */
+ TkTextCountType type) /* The kind of indices to count. */
+{
+ TkTextLine *linePtr1;
+ TkTextSegment *segPtr, *seg2Ptr = NULL;
+ TkTextElideInfo *infoPtr = NULL;
+ int byteOffset, maxBytes, count = 0, elide = 0;
+ int checkElided = (type & COUNT_DISPLAY);
+
+ /*
+ * Find seg that contains src index, and remember how many bytes not to
+ * count in the given segment.
+ */
+
+ segPtr = TkTextIndexToSeg(indexPtr1, &byteOffset);
+ linePtr1 = indexPtr1->linePtr;
+
+ seg2Ptr = TkTextIndexToSeg(indexPtr2, &maxBytes);
+
+ if (checkElided) {
+ infoPtr = (TkTextElideInfo *)
+ ckalloc((unsigned) sizeof(TkTextElideInfo));
+ elide = TkTextIsElided(textPtr, indexPtr1, infoPtr);
+ }
+
+ while (1) {
+ /*
+ * Go through each segment in line adding up the number of characters.
+ */
+
+ for ( ; segPtr != NULL; segPtr = segPtr->nextPtr) {
+ /*
+ * If we do need to pay attention to the visibility of
+ * characters/indices, check that first. If the current segment
+ * isn't visible, then we simply continue the loop.
+ */
+
+ if (checkElided) {
+ if ((segPtr->typePtr == &tkTextToggleOffType)
+ || (segPtr->typePtr == &tkTextToggleOnType)) {
+ TkTextTag *tagPtr = segPtr->body.toggle.tagPtr;
+
+ /*
+ * The elide state only changes if this tag is either the
+ * current highest priority tag (and is therefore being
+ * toggled off), or it's a new tag with higher priority.
+ */
+
+ if (tagPtr->elideString != NULL) {
+ infoPtr->tagCnts[tagPtr->priority]++;
+ if (infoPtr->tagCnts[tagPtr->priority] & 1) {
+ infoPtr->tagPtrs[tagPtr->priority] = tagPtr;
+ }
+ if (tagPtr->priority >= infoPtr->elidePriority) {
+ if (segPtr->typePtr == &tkTextToggleOffType) {
+ /*
+ * If it is being toggled off, and it has an
+ * elide string, it must actually be the
+ * current highest priority tag, so this check
+ * is redundant:
+ */
+
+ if (tagPtr->priority!=infoPtr->elidePriority) {
+ Tcl_Panic("Bad tag priority being toggled off");
+ }
+
+ /*
+ * Find previous elide tag, if any (if not
+ * then elide will be zero, of course).
+ */
+
+ elide = 0;
+ while (--infoPtr->elidePriority > 0) {
+ if (infoPtr->tagCnts[
+ infoPtr->elidePriority] & 1) {
+ elide = infoPtr->tagPtrs[
+ infoPtr->elidePriority]->elide;
+ break;
+ }
+ }
+ } else {
+ elide = tagPtr->elide;
+ infoPtr->elidePriority = tagPtr->priority;
+ }
+ }
+ }
+ }
+ if (elide) {
+ if (segPtr == seg2Ptr) {
+ goto countDone;
+ }
+ byteOffset = 0;
+ continue;
+ }
+ }
+
+ if (segPtr->typePtr == &tkTextCharType) {
+ int byteLen = segPtr->size - byteOffset;
+ register unsigned char *str = (unsigned char *)
+ segPtr->body.chars + byteOffset;
+ register int i;
+
+ if (segPtr == seg2Ptr) {
+ if (byteLen > (maxBytes - byteOffset)) {
+ byteLen = maxBytes - byteOffset;
+ }
+ }
+ i = byteLen;
+
+ /*
+ * This is a speed sensitive function, so run specially over
+ * the string to count continuous ascii characters before
+ * resorting to the Tcl_NumUtfChars call. This is a long form
+ * of:
+ *
+ * stringPtr->numChars =
+ * Tcl_NumUtfChars(objPtr->bytes, objPtr->length);
+ */
+
+ while (i && (*str < 0xC0)) {
+ i--;
+ str++;
+ }
+ count += byteLen - i;
+ if (i) {
+ count += Tcl_NumUtfChars(segPtr->body.chars + byteOffset
+ + (byteLen - i), i);
+ }
+ } else {
+ if (type & COUNT_INDICES) {
+ int byteLen = segPtr->size - byteOffset;
+
+ if (segPtr == seg2Ptr) {
+ if (byteLen > (maxBytes - byteOffset)) {
+ byteLen = maxBytes - byteOffset;
+ }
+ }
+ count += byteLen;
+ }
+ }
+ if (segPtr == seg2Ptr) {
+ goto countDone;
+ }
+ byteOffset = 0;
+ }
+
+ /*
+ * Go to the next line. If we are at the end of the text item, back up
+ * one byte (for the terminal '\n' character) and return that index.
+ */
+
+ linePtr1 = TkBTreeNextLine(textPtr, linePtr1);
+ if (linePtr1 == NULL) {
+ Tcl_Panic("Reached end of text widget when counting characters");
+ }
+ segPtr = linePtr1->segPtr;
+ }
+
+ countDone:
+ if (infoPtr != NULL) {
+ TkTextFreeElideInfo(infoPtr);
+ ckfree((char *) infoPtr);
+ }
+ return count;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
* TkTextIndexBackBytes --
*
- * Given an index for a text widget, this procedure creates a new
- * index that points "count" bytes earlier than the source index.
+ * Given an index for a text widget, this function creates a new index
+ * that points "count" bytes earlier than the source index.
*
* Results:
* *dstPtr is modified to refer to the character "count" bytes before
* srcPtr, or to the first character in the TkText if there aren't
* "count" bytes earlier than srcPtr.
*
+ * Returns 1 if we couldn't use all of 'byteCount' because we have run
+ * into the beginning or end of the text, and zero otherwise.
+ *
* Side effects:
* None.
*
*---------------------------------------------------------------------------
*/
-void
-TkTextIndexBackBytes(srcPtr, byteCount, dstPtr)
- CONST TkTextIndex *srcPtr; /* Source index. */
- int byteCount; /* How many bytes backward to move. May be
+int
+TkTextIndexBackBytes(
+ CONST TkText *textPtr,
+ CONST TkTextIndex *srcPtr, /* Source index. */
+ int byteCount, /* How many bytes backward to move. May be
* negative. */
- TkTextIndex *dstPtr; /* Destination index: gets modified. */
+ TkTextIndex *dstPtr) /* Destination index: gets modified. */
{
TkTextSegment *segPtr;
int lineIndex;
if (byteCount < 0) {
- TkTextIndexForwBytes(srcPtr, -byteCount, dstPtr);
- return;
+ return TkTextIndexForwBytes(textPtr, srcPtr, -byteCount, dstPtr);
}
*dstPtr = *srcPtr;
@@ -932,19 +1855,19 @@ TkTextIndexBackBytes(srcPtr, byteCount, dstPtr)
lineIndex = -1;
while (dstPtr->byteIndex < 0) {
/*
- * Move back one line in the text. If we run off the beginning
- * of the file then just return the first character in the text.
+ * Move back one line in the text. If we run off the beginning of the
+ * file then just return the first character in the text.
*/
if (lineIndex < 0) {
- lineIndex = TkBTreeLineIndex(dstPtr->linePtr);
+ lineIndex = TkBTreeLinesTo(textPtr, dstPtr->linePtr);
}
if (lineIndex == 0) {
dstPtr->byteIndex = 0;
- return;
+ return 1;
}
lineIndex--;
- dstPtr->linePtr = TkBTreeFindLine(dstPtr->tree, lineIndex);
+ dstPtr->linePtr = TkBTreeFindLine(dstPtr->tree, textPtr, lineIndex);
/*
* Compute the length of the line and add that to dstPtr->charIndex.
@@ -955,6 +1878,7 @@ TkTextIndexBackBytes(srcPtr, byteCount, dstPtr)
dstPtr->byteIndex += segPtr->size;
}
}
+ return 0;
}
/*
@@ -962,13 +1886,18 @@ TkTextIndexBackBytes(srcPtr, byteCount, dstPtr)
*
* TkTextIndexBackChars --
*
- * Given an index for a text widget, this procedure creates a new
- * index that points "count" characters earlier than the source index.
+ * Given an index for a text widget, this function creates a new index
+ * that points "count" items of type given by "type" earlier than the
+ * source index. "count" can be zero, which is useful in the case where
+ * one wishes to move backward by display (non-elided) chars or indices
+ * or one wishes to move backward by chars, skipping any intervening
+ * indices. In this case the returned index *dstPtr will point just
+ * _after_ the first acceptable index which is encountered.
*
* Results:
- * *dstPtr is modified to refer to the character "count" characters
- * before srcPtr, or to the first character in the file if there
- * aren't "count" characters earlier than srcPtr.
+ * *dstPtr is modified to refer to the character "count" items before
+ * srcPtr, or to the first index in the window if there aren't sufficient
+ * items earlier than srcPtr.
*
* Side effects:
* None.
@@ -977,57 +1906,145 @@ TkTextIndexBackBytes(srcPtr, byteCount, dstPtr)
*/
void
-TkTextIndexBackChars(srcPtr, charCount, dstPtr)
- CONST TkTextIndex *srcPtr; /* Source index. */
- int charCount; /* How many characters backward to move.
- * May be negative. */
- TkTextIndex *dstPtr; /* Destination index: gets modified. */
+TkTextIndexBackChars(
+ CONST TkText *textPtr, /* Overall information about text widget. */
+ CONST TkTextIndex *srcPtr, /* Source index. */
+ int charCount, /* How many characters backward to move. May
+ * be negative. */
+ TkTextIndex *dstPtr, /* Destination index: gets modified. */
+ TkTextCountType type) /* The type of item to count */
{
TkTextSegment *segPtr, *oldPtr;
+ TkTextElideInfo *infoPtr = NULL;
int lineIndex, segSize;
CONST char *p, *start, *end;
+ int elide = 0;
+ int checkElided = (type & COUNT_DISPLAY);
- if (charCount <= 0) {
- TkTextIndexForwChars(srcPtr, -charCount, dstPtr);
+ if (charCount < 0) {
+ TkTextIndexForwChars(textPtr, srcPtr, -charCount, dstPtr, type);
return;
}
+ if (checkElided) {
+ infoPtr = (TkTextElideInfo *) ckalloc(sizeof(TkTextElideInfo));
+ elide = TkTextIsElided(textPtr, srcPtr, infoPtr);
+ }
*dstPtr = *srcPtr;
/*
- * Find offset within seg that contains byteIndex.
- * Move backward specified number of chars.
+ * Find offset within seg that contains byteIndex. Move backward specified
+ * number of chars.
*/
lineIndex = -1;
-
+
segSize = dstPtr->byteIndex;
- for (segPtr = dstPtr->linePtr->segPtr; ; segPtr = segPtr->nextPtr) {
- if (segSize <= segPtr->size) {
- break;
+
+ if (checkElided) {
+ segPtr = infoPtr->segPtr;
+ segSize -= infoPtr->segOffset;
+ } else {
+ TkTextLine *linePtr = dstPtr->linePtr;
+ for (segPtr = linePtr->segPtr; ; segPtr = segPtr->nextPtr) {
+ if (segPtr == NULL) {
+ /*
+ * Two logical lines merged into one display line through
+ * eliding of a newline.
+ */
+
+ linePtr = TkBTreeNextLine(NULL, linePtr);
+ segPtr = linePtr->segPtr;
+ }
+ if (segSize <= segPtr->size) {
+ break;
+ }
+ segSize -= segPtr->size;
}
- segSize -= segPtr->size;
}
+
+ /*
+ * Now segPtr points to the segment containing the starting index.
+ */
+
while (1) {
- if (segPtr->typePtr == &tkTextCharType) {
- start = segPtr->body.chars;
- end = segPtr->body.chars + segSize;
- for (p = end; ; p = Tcl_UtfPrev(p, start)) {
- if (charCount == 0) {
- dstPtr->byteIndex -= (end - p);
- return;
+ /*
+ * If we do need to pay attention to the visibility of
+ * characters/indices, check that first. If the current segment isn't
+ * visible, then we simply continue the loop.
+ */
+
+ if (checkElided && ((segPtr->typePtr == &tkTextToggleOffType)
+ || (segPtr->typePtr == &tkTextToggleOnType))) {
+ TkTextTag *tagPtr = segPtr->body.toggle.tagPtr;
+
+ /*
+ * The elide state only changes if this tag is either the current
+ * highest priority tag (and is therefore being toggled off), or
+ * it's a new tag with higher priority.
+ */
+
+ if (tagPtr->elideString != NULL) {
+ infoPtr->tagCnts[tagPtr->priority]++;
+ if (infoPtr->tagCnts[tagPtr->priority] & 1) {
+ infoPtr->tagPtrs[tagPtr->priority] = tagPtr;
}
- if (p == start) {
- break;
+ if (tagPtr->priority >= infoPtr->elidePriority) {
+ if (segPtr->typePtr == &tkTextToggleOnType) {
+ /*
+ * If it is being toggled on, and it has an elide
+ * string, it must actually be the current highest
+ * priority tag, so this check is redundant:
+ */
+
+ if (tagPtr->priority != infoPtr->elidePriority) {
+ Tcl_Panic("Bad tag priority being toggled on");
+ }
+
+ /*
+ * Find previous elide tag, if any (if not then elide
+ * will be zero, of course).
+ */
+
+ elide = 0;
+ while (--infoPtr->elidePriority > 0) {
+ if (infoPtr->tagCnts[infoPtr->elidePriority] & 1) {
+ elide = infoPtr->tagPtrs[
+ infoPtr->elidePriority]->elide;
+ break;
+ }
+ }
+ } else {
+ elide = tagPtr->elide;
+ infoPtr->elidePriority = tagPtr->priority;
+ }
}
- charCount--;
}
- } else {
- if (charCount <= segSize) {
- dstPtr->byteIndex -= charCount;
- return;
+ }
+
+ if (!elide) {
+ if (segPtr->typePtr == &tkTextCharType) {
+ start = segPtr->body.chars;
+ end = segPtr->body.chars + segSize;
+ for (p = end; ; p = Tcl_UtfPrev(p, start)) {
+ if (charCount == 0) {
+ dstPtr->byteIndex -= (end - p);
+ goto backwardCharDone;
+ }
+ if (p == start) {
+ break;
+ }
+ charCount--;
+ }
+ } else {
+ if (type & COUNT_INDICES) {
+ if (charCount <= segSize) {
+ dstPtr->byteIndex -= charCount;
+ goto backwardCharDone;
+ }
+ charCount -= segSize;
+ }
}
- charCount -= segSize;
}
dstPtr->byteIndex -= segSize;
@@ -1050,14 +2067,14 @@ TkTextIndexBackChars(srcPtr, charCount, dstPtr)
*/
if (lineIndex < 0) {
- lineIndex = TkBTreeLineIndex(dstPtr->linePtr);
+ lineIndex = TkBTreeLinesTo(textPtr, dstPtr->linePtr);
}
if (lineIndex == 0) {
dstPtr->byteIndex = 0;
- return;
+ goto backwardCharDone;
}
lineIndex--;
- dstPtr->linePtr = TkBTreeFindLine(dstPtr->tree, lineIndex);
+ dstPtr->linePtr = TkBTreeFindLine(dstPtr->tree, textPtr, lineIndex);
/*
* Compute the length of the line and add that to dstPtr->byteIndex.
@@ -1071,6 +2088,12 @@ TkTextIndexBackChars(srcPtr, charCount, dstPtr)
segPtr = oldPtr;
segSize = segPtr->size;
}
+
+ backwardCharDone:
+ if (infoPtr != NULL) {
+ TkTextFreeElideInfo(infoPtr);
+ ckfree((char *) infoPtr);
+ }
}
/*
@@ -1078,14 +2101,14 @@ TkTextIndexBackChars(srcPtr, charCount, dstPtr)
*
* StartEnd --
*
- * This procedure handles modifiers like "wordstart" and "lineend"
- * to adjust indices forwards or backwards.
+ * This function handles modifiers like "wordstart" and "lineend" to
+ * adjust indices forwards or backwards.
*
* Results:
- * If the modifier is successfully parsed then the return value
- * is the address of the first character after the modifier, and
- * *indexPtr is updated to reflect the modifier. If there is a
- * syntax error in the modifier then NULL is returned.
+ * If the modifier is successfully parsed then the return value is the
+ * address of the first character after the modifier, and *indexPtr is
+ * updated to reflect the modifier. If there is a syntax error in the
+ * modifier then NULL is returned.
*
* Side effects:
* None.
@@ -1094,17 +2117,17 @@ TkTextIndexBackChars(srcPtr, charCount, dstPtr)
*/
static CONST char *
-StartEnd(string, indexPtr)
- CONST char *string; /* String to parse for additional info
- * about modifier (count and units).
- * Points to first character of modifer
- * word. */
- TkTextIndex *indexPtr; /* Index to mdoify based on string. */
+StartEnd(
+ TkText *textPtr, /* Information about text widget. */
+ CONST char *string, /* String to parse for additional info about
+ * modifier (count and units). Points to first
+ * character of modifer word. */
+ TkTextIndex *indexPtr) /* Index to modify based on string. */
{
CONST char *p;
- int c, offset;
size_t length;
register TkTextSegment *segPtr;
+ int modifier;
/*
* Find the end of the modifier word.
@@ -1113,68 +2136,144 @@ StartEnd(string, indexPtr)
for (p = string; isalnum(UCHAR(*p)); p++) {
/* Empty loop body. */
}
+
length = p-string;
+ if ((*string == 'd') &&
+ (strncmp(string, "display", (length > 7 ? 7 : length)) == 0)) {
+ modifier = TKINDEX_DISPLAY;
+ if (length > 7) {
+ p -= (length - 7);
+ }
+ } else if ((*string == 'a') &&
+ (strncmp(string, "any", (length > 3 ? 3 : length)) == 0)) {
+ modifier = TKINDEX_ANY;
+ if (length > 3) {
+ p -= (length - 3);
+ }
+ } else {
+ modifier = TKINDEX_NONE;
+ }
+
+ /*
+ * If we had a modifier, which we interpreted ok, so now forward to the
+ * actual units.
+ */
+
+ if (modifier != TKINDEX_NONE) {
+ while (isspace(UCHAR(*p))) {
+ p++;
+ }
+ string = p;
+ while ((*p!='\0') && !isspace(UCHAR(*p)) && (*p!='+') && (*p!='-')) {
+ p++;
+ }
+ length = p - string;
+ }
+
if ((*string == 'l') && (strncmp(string, "lineend", length) == 0)
&& (length >= 5)) {
- indexPtr->byteIndex = 0;
- for (segPtr = indexPtr->linePtr->segPtr; segPtr != NULL;
- segPtr = segPtr->nextPtr) {
- indexPtr->byteIndex += segPtr->size;
+ if (modifier == TKINDEX_DISPLAY) {
+ TkTextFindDisplayLineEnd(textPtr, indexPtr, 1, NULL);
+ } else {
+ indexPtr->byteIndex = 0;
+ for (segPtr = indexPtr->linePtr->segPtr; segPtr != NULL;
+ segPtr = segPtr->nextPtr) {
+ indexPtr->byteIndex += segPtr->size;
+ }
+
+ /*
+ * We know '\n' is encoded with a single byte index.
+ */
+
+ indexPtr->byteIndex -= sizeof(char);
}
- indexPtr->byteIndex -= sizeof(char);
} else if ((*string == 'l') && (strncmp(string, "linestart", length) == 0)
&& (length >= 5)) {
- indexPtr->byteIndex = 0;
+ if (modifier == TKINDEX_DISPLAY) {
+ TkTextFindDisplayLineEnd(textPtr, indexPtr, 0, NULL);
+ } else {
+ indexPtr->byteIndex = 0;
+ }
} else if ((*string == 'w') && (strncmp(string, "wordend", length) == 0)
&& (length >= 5)) {
int firstChar = 1;
+ int offset;
/*
* If the current character isn't part of a word then just move
- * forward one character. Otherwise move forward until finding
- * a character that isn't part of a word and stop there.
+ * forward one character. Otherwise move forward until finding a
+ * character that isn't part of a word and stop there.
*/
+ if (modifier == TKINDEX_DISPLAY) {
+ TkTextIndexForwChars(textPtr, indexPtr, 0, indexPtr,
+ COUNT_DISPLAY_INDICES);
+ }
segPtr = TkTextIndexToSeg(indexPtr, &offset);
while (1) {
+ int chSize = 1;
+
if (segPtr->typePtr == &tkTextCharType) {
- c = segPtr->body.chars[offset];
- if (!isalnum(UCHAR(c)) && (c != '_')) {
+ Tcl_UniChar ch;
+
+ chSize = Tcl_UtfToUniChar(segPtr->body.chars + offset, &ch);
+ if (!Tcl_UniCharIsWordChar(ch)) {
break;
}
firstChar = 0;
}
- offset += 1;
- indexPtr->byteIndex += sizeof(char);
+ offset += chSize;
+ indexPtr->byteIndex += chSize;
if (offset >= segPtr->size) {
segPtr = TkTextIndexToSeg(indexPtr, &offset);
}
}
if (firstChar) {
- TkTextIndexForwChars(indexPtr, 1, indexPtr);
+ if (modifier == TKINDEX_DISPLAY) {
+ TkTextIndexForwChars(textPtr, indexPtr, 1, indexPtr,
+ COUNT_DISPLAY_INDICES);
+ } else {
+ TkTextIndexForwChars(NULL, indexPtr, 1, indexPtr,
+ COUNT_INDICES);
+ }
}
} else if ((*string == 'w') && (strncmp(string, "wordstart", length) == 0)
&& (length >= 5)) {
int firstChar = 1;
+ int offset;
+
+ if (modifier == TKINDEX_DISPLAY) {
+ TkTextIndexForwChars(NULL, indexPtr, 0, indexPtr,
+ COUNT_DISPLAY_INDICES);
+ }
/*
- * Starting with the current character, look for one that's not
- * part of a word and keep moving backward until you find one.
- * Then if the character found wasn't the first one, move forward
- * again one position.
+ * Starting with the current character, look for one that's not part
+ * of a word and keep moving backward until you find one. Then if the
+ * character found wasn't the first one, move forward again one
+ * position.
*/
segPtr = TkTextIndexToSeg(indexPtr, &offset);
while (1) {
+ int chSize = 1;
+
if (segPtr->typePtr == &tkTextCharType) {
- c = segPtr->body.chars[offset];
- if (!isalnum(UCHAR(c)) && (c != '_')) {
+ Tcl_UniChar ch;
+
+ Tcl_UtfToUniChar(segPtr->body.chars + offset, &ch);
+ if (!Tcl_UniCharIsWordChar(ch)) {
break;
}
+ if (offset > 0) {
+ chSize = (segPtr->body.chars + offset
+ - Tcl_UtfPrev(segPtr->body.chars + offset,
+ segPtr->body.chars));
+ }
firstChar = 0;
}
- offset -= 1;
- indexPtr->byteIndex -= sizeof(char);
+ offset -= chSize;
+ indexPtr->byteIndex -= chSize;
if (offset < 0) {
if (indexPtr->byteIndex < 0) {
indexPtr->byteIndex = 0;
@@ -1183,12 +2282,28 @@ StartEnd(string, indexPtr)
segPtr = TkTextIndexToSeg(indexPtr, &offset);
}
}
+
if (!firstChar) {
- TkTextIndexForwChars(indexPtr, 1, indexPtr);
+ if (modifier == TKINDEX_DISPLAY) {
+ TkTextIndexForwChars(textPtr, indexPtr, 1, indexPtr,
+ COUNT_DISPLAY_INDICES);
+ } else {
+ TkTextIndexForwChars(NULL, indexPtr, 1, indexPtr,
+ COUNT_INDICES);
+ }
}
} else {
return NULL;
}
- done:
+
+ done:
return p;
}
+
+/*
+ * Local Variables:
+ * mode: c
+ * c-basic-offset: 4
+ * fill-column: 78
+ * End:
+ */