summaryrefslogtreecommitdiffstats
path: root/generic/tkTextIndex.c
diff options
context:
space:
mode:
authorvincentdarley <vincentdarley>2003-10-31 09:02:06 (GMT)
committervincentdarley <vincentdarley>2003-10-31 09:02:06 (GMT)
commit5aca2c785542acbefba1c55007389e41d19ea6f3 (patch)
tree1a7d95870c1e63f3d43b706e7e97421c104b19b7 /generic/tkTextIndex.c
parent51c19c5c75da3a7e10f2000ef8cf96bf9fed814c (diff)
downloadtk-5aca2c785542acbefba1c55007389e41d19ea6f3.zip
tk-5aca2c785542acbefba1c55007389e41d19ea6f3.tar.gz
tk-5aca2c785542acbefba1c55007389e41d19ea6f3.tar.bz2
TIP 155 implementation
Diffstat (limited to 'generic/tkTextIndex.c')
-rw-r--r--generic/tkTextIndex.c679
1 files changed, 579 insertions, 100 deletions
diff --git a/generic/tkTextIndex.c b/generic/tkTextIndex.c
index 06396db..66caa5a 100644
--- a/generic/tkTextIndex.c
+++ b/generic/tkTextIndex.c
@@ -10,7 +10,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tkTextIndex.c,v 1.8 2003/10/10 15:56:22 dkf Exp $
+ * RCS: @(#) $Id: tkTextIndex.c,v 1.9 2003/10/31 09:02:11 vincentdarley Exp $
*/
#include "default.h"
@@ -25,18 +25,28 @@
#define LAST_CHAR 1000000
/*
+ * Modifiers for index parsing: 'display', 'any' or nothing.
+ */
+#define TKINDEX_NONE 0
+#define TKINDEX_DISPLAY 1
+#define TKINDEX_ANY 2
+
+/*
* Forward declarations for procedures defined later in this file:
*/
-static CONST char * ForwBack _ANSI_ARGS_((CONST char *string,
- TkTextIndex *indexPtr));
-static CONST char * StartEnd _ANSI_ARGS_((CONST char *string,
- TkTextIndex *indexPtr));
+static CONST char * ForwBack _ANSI_ARGS_((TkText *textPtr,
+ CONST char *string, TkTextIndex *indexPtr));
+static CONST char * StartEnd _ANSI_ARGS_((TkText *textPtr,
+ CONST char *string, TkTextIndex *indexPtr));
static int GetIndex _ANSI_ARGS_((Tcl_Interp *interp,
TkText *textPtr, CONST char *string,
TkTextIndex *indexPtr, int *canCachePtr));
-
+/*
+ * The "textindex" Tcl_Obj definition:
+ */
+
static void DupTextIndexInternalRep _ANSI_ARGS_((Tcl_Obj *srcPtr,
Tcl_Obj *copyPtr));
static void FreeTextIndexInternalRep _ANSI_ARGS_((Tcl_Obj *listPtr));
@@ -96,7 +106,6 @@ DupTextIndexInternalRep(srcPtr, copyPtr)
SET_TEXTINDEX(copyPtr, dupIndexPtr);
SET_INDEXEPOCH(copyPtr, epoch);
}
-
/*
* This will not be called except by TkTextNewIndexObj below.
* This is because if a TkTextIndex is no longer valid, it is
@@ -117,7 +126,6 @@ UpdateStringOfTextIndex(objPtr)
strcpy(objPtr->bytes, buffer);
objPtr->length = len;
}
-
static int
SetTextIndexFromAny(interp, objPtr)
Tcl_Interp *interp; /* Used for error reporting if not NULL. */
@@ -128,7 +136,7 @@ SetTextIndexFromAny(interp, objPtr)
-1);
return TCL_ERROR;
}
-
+
/*
*---------------------------------------------------------------------------
*
@@ -176,7 +184,7 @@ MakeObjIndex(textPtr, objPtr, origPtr)
}
return indexPtr;
}
-
+
CONST TkTextIndex*
TkTextGetIndexFromObj(interp, textPtr, objPtr)
Tcl_Interp *interp; /* Use this for error reporting. */
@@ -274,6 +282,62 @@ TkTextNewIndexObj(textPtr, indexPtr)
/*
*---------------------------------------------------------------------------
*
+ * TkTextMakePixelIndex --
+ *
+ * Given a pixel 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 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(textPtr, pixelIndex, indexPtr)
+ 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->tree;
+ indexPtr->textPtr = textPtr;
+
+ if (pixelIndex < 0) {
+ pixelIndex = 0;
+ }
+ indexPtr->linePtr = TkBTreeFindPixelLine(textPtr->tree, pixelIndex,
+ &pixelOffset);
+ if (indexPtr->linePtr == NULL) {
+ indexPtr->linePtr = TkBTreeFindPixelLine(textPtr->tree,
+ TkBTreeNumPixels(textPtr->tree)-1, &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
@@ -293,7 +357,7 @@ TkTextNewIndexObj(textPtr, indexPtr)
TkTextIndex *
TkTextMakeByteIndex(tree, lineIndex, byteIndex, indexPtr)
- TkTextBTree tree; /* Tree that lineIndex and charIndex refer
+ TkTextBTree tree; /* Tree that lineIndex and byteIndex refer
* to. */
int lineIndex; /* Index of desired line (0 means first
* line of text). */
@@ -827,9 +891,9 @@ GetIndex(interp, textPtr, string, indexPtr, canCachePtr)
}
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;
@@ -965,7 +1029,8 @@ TkTextIndexCmp(index1Ptr, index2Ptr)
*/
static CONST char *
-ForwBack(string, indexPtr)
+ForwBack(textPtr, string, indexPtr)
+ 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
@@ -974,7 +1039,7 @@ ForwBack(string, indexPtr)
{
register CONST char *p, *units;
char *end;
- int count, lineIndex;
+ int count, lineIndex, modifier;
size_t length;
/*
@@ -996,8 +1061,12 @@ ForwBack(string, indexPtr)
/*
* Find the end of this modifier (next space or + or - character),
- * then parse the unit specifier and update the position
- * accordingly.
+ * 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;
@@ -1005,43 +1074,166 @@ ForwBack(string, indexPtr)
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(indexPtr, count, indexPtr, type);
} else {
- TkTextIndexBackChars(indexPtr, count, indexPtr);
+ TkTextIndexBackChars(indexPtr, count, indexPtr, type);
+ }
+ } else if ((*units == 'i') && (strncmp(units, "indices", length) == 0)) {
+ TkTextCountType type;
+ if (modifier == TKINDEX_DISPLAY) {
+ type = COUNT_DISPLAY_INDICES;
+ } else {
+ type = COUNT_INDICES;
}
- } else if ((*units == 'l') && (strncmp(units, "lines", length) == 0)) {
- lineIndex = TkBTreeLineIndex(indexPtr->linePtr);
if (*string == '+') {
- lineIndex += count;
+ TkTextIndexForwChars(indexPtr, count, indexPtr, type);
+ } else {
+ TkTextIndexBackChars(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)) {
+ /* Go forward to the first non-elided index */
+ TkTextIndexForwChars(indexPtr, 0, indexPtr,
+ COUNT_DISPLAY_INDICES | COUNT_IS_ELIDED);
+ }
+ /*
+ * 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 ((count < 0) ^ (*string == '-')) {
+ forward = 0;
+ } else {
+ forward = 1;
+ }
+ 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(indexPtr, 1, indexPtr,
+ COUNT_DISPLAY_INDICES
+ | (COUNT_IS_ELIDED * TkTextIsElided(textPtr, indexPtr)));
+ }
+ } 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(indexPtr, 1, indexPtr,
+ COUNT_DISPLAY_INDICES
+ | (COUNT_IS_ELIDED * TkTextIsElided(textPtr, indexPtr)));
+ }
+ 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 -= count;
+ lineIndex = TkBTreeLineIndex(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;
+ }
+ }
/*
- * 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.
+ * 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.
*/
- if (lineIndex < 0) {
- lineIndex = 0;
- }
+ TkTextMakeByteIndex(indexPtr->tree, lineIndex, indexPtr->byteIndex,
+ indexPtr);
}
- /*
- * 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);
} else {
return NULL;
}
@@ -1060,6 +1252,9 @@ ForwBack(string, indexPtr)
* *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.
+ *
+ * In this latter case, the function returns '1' to indicate
+ * that not all of 'byteCount' could be used.
*
* Side effects:
* None.
@@ -1067,7 +1262,7 @@ ForwBack(string, indexPtr)
*---------------------------------------------------------------------------
*/
-void
+int
TkTextIndexForwBytes(srcPtr, byteCount, dstPtr)
CONST TkTextIndex *srcPtr; /* Source index. */
int byteCount; /* How many bytes forward to move. May be
@@ -1080,7 +1275,7 @@ TkTextIndexForwBytes(srcPtr, byteCount, dstPtr)
if (byteCount < 0) {
TkTextIndexBackBytes(srcPtr, -byteCount, dstPtr);
- return;
+ return 0;
}
*dstPtr = *srcPtr;
@@ -1102,13 +1297,13 @@ TkTextIndexForwBytes(srcPtr, byteCount, dstPtr)
*/
if (dstPtr->byteIndex < lineLength) {
- return;
+ return 0;
}
dstPtr->byteIndex -= lineLength;
linePtr = TkBTreeNextLine(dstPtr->linePtr);
if (linePtr == NULL) {
dstPtr->byteIndex = lineLength - 1;
- return;
+ return 1;
}
dstPtr->linePtr = linePtr;
}
@@ -1120,12 +1315,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.
+ * 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
+ * *dstPtr is modified to refer to the character "count" items
* after srcPtr, or to the last character in the TkText if there
- * aren't "count" characters left in the file.
+ * aren't sufficient items left in the widget.
*
* Side effects:
* None.
@@ -1134,22 +1335,28 @@ TkTextIndexForwBytes(srcPtr, byteCount, dstPtr)
*/
void
-TkTextIndexForwChars(srcPtr, charCount, dstPtr)
+TkTextIndexForwChars(srcPtr, charCount, dstPtr, type)
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;
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(srcPtr, -charCount, dstPtr, type);
return;
}
+ if (checkElided) {
+ elide = ((type & COUNT_IS_ELIDED) ? 1 : 0);
+ }
*dstPtr = *srcPtr;
@@ -1166,23 +1373,49 @@ 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) {
+ if ((segPtr->typePtr == &tkTextToggleOffType)
+ || (segPtr->typePtr == &tkTextToggleOnType)) {
+ if (segPtr->body.toggle.tagPtr->elideString != NULL) {
+ if (elide) {
+ elide = (segPtr->typePtr == &tkTextToggleOffType)
+ & !segPtr->body.toggle.tagPtr->elide;
+ } else {
+ elide = (segPtr->typePtr == &tkTextToggleOnType)
+ & segPtr->body.toggle.tagPtr->elide;
+ }
}
- 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);
+ return;
+ }
+ charCount--;
+ }
+ } else {
+ if (type & COUNT_INDICES) {
+ if (charCount < segPtr->size - byteOffset) {
+ dstPtr->byteIndex += charCount;
+ return;
+ }
+ charCount -= segPtr->size - byteOffset;
+ }
}
- charCount -= segPtr->size - byteOffset;
}
+
dstPtr->byteIndex += segPtr->size - byteOffset;
byteOffset = 0;
}
@@ -1207,6 +1440,155 @@ TkTextIndexForwChars(srcPtr, charCount, dstPtr)
/*
*---------------------------------------------------------------------------
*
+ * TkTextIndexCount --
+ *
+ * Given an ordered pair of indices in a text widget, this
+ * procedure counts how many characters (not bytes) are between
+ * the two indices.
+ *
+ * It is illegal to call this procedure 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(textPtr, indexPtr1, indexPtr2, type)
+ 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;
+ int byteOffset, maxBytes;
+ int count = 0;
+ int 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) {
+ elide = TkTextIsElided(textPtr, indexPtr1);
+ }
+
+ 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)) {
+ if (segPtr->body.toggle.tagPtr->elideString != NULL) {
+ if (elide) {
+ elide = (segPtr->typePtr == &tkTextToggleOffType)
+ & !segPtr->body.toggle.tagPtr->elide;
+ } else {
+ elide = (segPtr->typePtr == &tkTextToggleOnType)
+ & segPtr->body.toggle.tagPtr->elide;
+ }
+ }
+ }
+ if (elide) {
+ if (segPtr == seg2Ptr) {
+ return count;
+ }
+ 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) {
+ return count;
+ }
+ 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(linePtr1);
+ if (linePtr1 == NULL) {
+ panic("Reached end of text widget when counting characters");
+ }
+ segPtr = linePtr1->segPtr;
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
* TkTextIndexBackBytes --
*
* Given an index for a text widget, this procedure creates a new
@@ -1274,12 +1656,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.
+ * 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.
@@ -1288,21 +1676,27 @@ TkTextIndexBackBytes(srcPtr, byteCount, dstPtr)
*/
void
-TkTextIndexBackChars(srcPtr, charCount, dstPtr)
+TkTextIndexBackChars(srcPtr, charCount, dstPtr, type)
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;
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(srcPtr, -charCount, dstPtr, type);
return;
}
-
+ if (checkElided) {
+ elide = ((type & COUNT_IS_ELIDED) ? 1 : 0);
+ }
+
*dstPtr = *srcPtr;
/*
@@ -1320,25 +1714,50 @@ TkTextIndexBackChars(srcPtr, charCount, dstPtr)
segSize -= segPtr->size;
}
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 (p == start) {
- break;
+ /*
+ * 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)) {
+ if (segPtr->body.toggle.tagPtr->elideString != NULL) {
+ if (elide) {
+ elide = (segPtr->typePtr == &tkTextToggleOnType)
+ & !segPtr->body.toggle.tagPtr->elide;
+ } else {
+ elide = (segPtr->typePtr == &tkTextToggleOffType)
+ & segPtr->body.toggle.tagPtr->elide;
+ }
}
- 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);
+ return;
+ }
+ if (p == start) {
+ break;
+ }
+ charCount--;
+ }
+ } else {
+ if (type & COUNT_INDICES) {
+ if (charCount <= segSize) {
+ dstPtr->byteIndex -= charCount;
+ return;
+ }
+ charCount -= segSize;
+ }
}
- charCount -= segSize;
}
dstPtr->byteIndex -= segSize;
@@ -1405,18 +1824,20 @@ TkTextIndexBackChars(srcPtr, charCount, dstPtr)
*/
static CONST char *
-StartEnd(string, indexPtr)
+StartEnd(textPtr, string, indexPtr)
+ 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 mdoify based on string. */
+ 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.
*/
@@ -1425,17 +1846,57 @@ StartEnd(string, indexPtr)
/* 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;
@@ -1446,6 +1907,10 @@ StartEnd(string, indexPtr)
* a character that isn't part of a word and stop there.
*/
+ if (modifier == TKINDEX_DISPLAY) {
+ TkTextIndexForwChars(indexPtr, 0, indexPtr, COUNT_DISPLAY_INDICES
+ | (COUNT_IS_ELIDED * TkTextIsElided(textPtr, indexPtr)));
+ }
segPtr = TkTextIndexToSeg(indexPtr, &offset);
while (1) {
if (segPtr->typePtr == &tkTextCharType) {
@@ -1462,12 +1927,21 @@ StartEnd(string, indexPtr)
}
}
if (firstChar) {
- TkTextIndexForwChars(indexPtr, 1, indexPtr);
+ if (modifier == TKINDEX_DISPLAY) {
+ TkTextIndexForwChars(indexPtr, 1, indexPtr, COUNT_DISPLAY_INDICES
+ | (COUNT_IS_ELIDED * TkTextIsElided(textPtr, indexPtr)));
+ } else {
+ TkTextIndexForwChars(indexPtr, 1, indexPtr, COUNT_INDICES);
+ }
}
} else if ((*string == 'w') && (strncmp(string, "wordstart", length) == 0)
&& (length >= 5)) {
int firstChar = 1;
+ if (modifier == TKINDEX_DISPLAY) {
+ TkTextIndexForwChars(indexPtr, 0, indexPtr, COUNT_DISPLAY_INDICES
+ | (COUNT_IS_ELIDED * TkTextIsElided(textPtr, indexPtr)));
+ }
/*
* Starting with the current character, look for one that's not
* part of a word and keep moving backward until you find one.
@@ -1495,7 +1969,12 @@ StartEnd(string, indexPtr)
}
}
if (!firstChar) {
- TkTextIndexForwChars(indexPtr, 1, indexPtr);
+ if (modifier == TKINDEX_DISPLAY) {
+ TkTextIndexForwChars(indexPtr, 1, indexPtr, COUNT_DISPLAY_INDICES
+ | (COUNT_IS_ELIDED * TkTextIsElided(textPtr, indexPtr)));
+ } else {
+ TkTextIndexForwChars(indexPtr, 1, indexPtr, COUNT_INDICES);
+ }
}
} else {
return NULL;