tkTextIndex.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:30k
- /*
- * tkTextIndex.c --
- *
- * This module provides procedures 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.
- *
- * RCS: @(#) $Id: tkTextIndex.c,v 1.6 2002/08/05 04:30:40 dgp Exp $
- */
- #include "default.h"
- #include "tkPort.h"
- #include "tkInt.h"
- #include "tkText.h"
- /*
- * Index to use to select last character in line (very large integer):
- */
- #define LAST_CHAR 1000000
- /*
- * 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));
- /*
- *---------------------------------------------------------------------------
- *
- * TkTextMakeByteIndex --
- *
- * 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
- * character at lineIndex and byteIndex (or the closest existing
- * character, if the specified one doesn't exist), and indexPtr is
- * returned as result.
- *
- * Side effects:
- * None.
- *
- *---------------------------------------------------------------------------
- */
- TkTextIndex *
- TkTextMakeByteIndex(tree, lineIndex, byteIndex, indexPtr)
- TkTextBTree tree; /* Tree that lineIndex and charIndex 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. */
- {
- TkTextSegment *segPtr;
- int index;
- CONST char *p, *start;
- Tcl_UniChar ch;
- indexPtr->tree = tree;
- if (lineIndex < 0) {
- lineIndex = 0;
- byteIndex = 0;
- }
- if (byteIndex < 0) {
- byteIndex = 0;
- }
- indexPtr->linePtr = TkBTreeFindLine(tree, lineIndex);
- if (indexPtr->linePtr == NULL) {
- indexPtr->linePtr = TkBTreeFindLine(tree, TkBTreeNumLines(tree));
- byteIndex = 0;
- }
- if (byteIndex == 0) {
- indexPtr->byteIndex = byteIndex;
- return indexPtr;
- }
- /*
- * 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.
- */
-
- indexPtr->byteIndex = index - sizeof(char);
- break;
- }
- if (index + segPtr->size > byteIndex) {
- indexPtr->byteIndex = byteIndex;
- 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
- * falls in the middle of a UTF-8 character, it will be
- * adjusted to the end of that UTF-8 character.
- */
- start = segPtr->body.chars + (byteIndex - index);
- p = Tcl_UtfPrev(start, segPtr->body.chars);
- p += Tcl_UtfToUniChar(p, &ch);
- indexPtr->byteIndex += p - start;
- }
- break;
- }
- index += segPtr->size;
- }
- return indexPtr;
- }
- /*
- *---------------------------------------------------------------------------
- *
- * TkTextMakeCharIndex --
- *
- * 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
- * character at lineIndex and charIndex (or the closest existing
- * character, if the specified one doesn't exist), and indexPtr is
- * returned as result.
- *
- * Side effects:
- * None.
- *
- *---------------------------------------------------------------------------
- */
- TkTextIndex *
- TkTextMakeCharIndex(tree, lineIndex, charIndex, indexPtr)
- 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. */
- {
- register TkTextSegment *segPtr;
- char *p, *start, *end;
- int index, offset;
- Tcl_UniChar ch;
- indexPtr->tree = tree;
- if (lineIndex < 0) {
- lineIndex = 0;
- charIndex = 0;
- }
- if (charIndex < 0) {
- charIndex = 0;
- }
- indexPtr->linePtr = TkBTreeFindLine(tree, lineIndex);
- if (indexPtr->linePtr == NULL) {
- indexPtr->linePtr = TkBTreeFindLine(tree, TkBTreeNumLines(tree));
- 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.
- */
- 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.
- */
-
- indexPtr->byteIndex = index - sizeof(char);
- break;
- }
- if (segPtr->typePtr == &tkTextCharType) {
- /*
- * Turn character offset into a byte offset.
- */
- start = segPtr->body.chars;
- end = start + segPtr->size;
- for (p = start; p < end; p += offset) {
- if (charIndex == 0) {
- indexPtr->byteIndex = index;
- return indexPtr;
- }
- charIndex--;
- offset = Tcl_UtfToUniChar(p, &ch);
- index += offset;
- }
- } else {
- if (charIndex < segPtr->size) {
- indexPtr->byteIndex = index;
- break;
- }
- charIndex -= segPtr->size;
- index += segPtr->size;
- }
- }
- return indexPtr;
- }
- /*
- *---------------------------------------------------------------------------
- *
- * TkTextIndexToSeg --
- *
- * Given an index, this procedure 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.
- *
- * Side effects:
- * None.
- *
- *---------------------------------------------------------------------------
- */
- TkTextSegment *
- TkTextIndexToSeg(indexPtr, offsetPtr)
- CONST TkTextIndex *indexPtr;/* Text index. */
- int *offsetPtr; /* Where to store offset within segment, or
- * NULL if offset isn't wanted. */
- {
- TkTextSegment *segPtr;
- int offset;
- for (offset = indexPtr->byteIndex, segPtr = indexPtr->linePtr->segPtr;
- offset >= segPtr->size;
- offset -= segPtr->size, segPtr = segPtr->nextPtr) {
- /* Empty loop body. */
- }
- if (offsetPtr != NULL) {
- *offsetPtr = offset;
- }
- return segPtr;
- }
- /*
- *---------------------------------------------------------------------------
- *
- * TkTextSegToOffset --
- *
- * Given a segment pointer and the line containing it, this procedure
- * returns the offset of the segment within its line.
- *
- * Results:
- * The return value is the offset (within its line) of the first
- * character in segPtr.
- *
- * Side effects:
- * None.
- *
- *---------------------------------------------------------------------------
- */
- int
- TkTextSegToOffset(segPtr, linePtr)
- CONST TkTextSegment *segPtr;/* Segment whose offset is desired. */
- CONST TkTextLine *linePtr; /* Line containing segPtr. */
- {
- CONST TkTextSegment *segPtr2;
- int offset;
- offset = 0;
- for (segPtr2 = linePtr->segPtr; segPtr2 != segPtr;
- segPtr2 = segPtr2->nextPtr) {
- offset += segPtr2->size;
- }
- return offset;
- }
- /*
- *---------------------------------------------------------------------------
- *
- * TkTextGetIndex --
- *
- * 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.
- *
- * Side effects:
- * None.
- *
- *---------------------------------------------------------------------------
- */
- 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. */
- {
- 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;
- /*
- *---------------------------------------------------------------------
- * 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;
- }
- /*
- *------------------------------------------------
- * Stage 2: start again by parsing the base index.
- *------------------------------------------------
- */
- indexPtr->tree = textPtr->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".
- */
- Tcl_DStringInit(©);
- p = strrchr(Tcl_DStringAppend(©, string, -1), '.');
- if (p != NULL) {
- if ((p[1] == 'f') && (strncmp(p+1, "first", 5) == 0)) {
- wantLast = 0;
- endOfBase = p+6;
- } else if ((p[1] == 'l') && (strncmp(p+1, "last", 4) == 0)) {
- wantLast = 1;
- endOfBase = p+5;
- } else {
- goto tryxy;
- }
- *p = 0;
- hPtr = Tcl_FindHashEntry(&textPtr->tagTable, Tcl_DStringValue(©));
- *p = '.';
- if (hPtr == NULL) {
- goto tryxy;
- }
- tagPtr = (TkTextTag *) Tcl_GetHashValue(hPtr);
- TkTextMakeByteIndex(textPtr->tree, 0, 0, &first);
- TkTextMakeByteIndex(textPtr->tree, TkBTreeNumLines(textPtr->tree), 0,
- &last);
- TkBTreeStartSearch(&first, &last, tagPtr, &search);
- if (!TkBTreeCharTagged(&first, tagPtr) && !TkBTreeNextTag(&search)) {
- Tcl_ResetResult(interp);
- Tcl_AppendResult(interp,
- "text doesn't contain any characters tagged with "",
- Tcl_GetHashKey(&textPtr->tagTable, hPtr), """,
- (char *) NULL);
- Tcl_DStringFree(©);
- return TCL_ERROR;
- }
- *indexPtr = search.curIndex;
- if (wantLast) {
- while (TkBTreeNextTag(&search)) {
- *indexPtr = search.curIndex;
- }
- }
- goto gotBase;
- }
- tryxy:
- if (string[0] == '@') {
- /*
- * Find character at a given x,y location in the window.
- */
- int x, y;
- cp = string+1;
- x = strtol(cp, &end, 0);
- if ((end == cp) || (*end != ',')) {
- goto error;
- }
- cp = end+1;
- y = strtol(cp, &end, 0);
- if (end == cp) {
- goto error;
- }
- TkTextPixelIndex(textPtr, x, y, indexPtr);
- endOfBase = end;
- goto gotBase;
- }
- if (isdigit(UCHAR(string[0])) || (string[0] == '-')) {
- int lineIndex, charIndex;
- /*
- * Base is identified with line and character indices.
- */
- lineIndex = strtol(string, &end, 0) - 1;
- if ((end == string) || (*end != '.')) {
- goto error;
- }
- p = end+1;
- if ((*p == 'e') && (strncmp(p, "end", 3) == 0)) {
- charIndex = LAST_CHAR;
- endOfBase = p+3;
- } else {
- charIndex = strtol(p, &end, 0);
- if (end == p) {
- goto error;
- }
- endOfBase = end;
- }
- TkTextMakeCharIndex(textPtr->tree, lineIndex, charIndex, indexPtr);
- goto gotBase;
- }
- for (p = Tcl_DStringValue(©); *p != 0; p++) {
- if (isspace(UCHAR(*p)) || (*p == '+') || (*p == '-')) {
- break;
- }
- }
- endOfBase = p;
- if (string[0] == '.') {
- /*
- * See if the base position is the name of an embedded window.
- */
- c = *endOfBase;
- *endOfBase = 0;
- result = TkTextWindowIndex(textPtr, Tcl_DStringValue(©), indexPtr);
- *endOfBase = c;
- if (result != 0) {
- goto gotBase;
- }
- }
- if ((string[0] == 'e')
- && (strncmp(string, "end",
- (size_t) (endOfBase-Tcl_DStringValue(©))) == 0)) {
- /*
- * Base position is end of text.
- */
- TkTextMakeByteIndex(textPtr->tree, TkBTreeNumLines(textPtr->tree),
- 0, indexPtr);
- goto gotBase;
- } else {
- /*
- * See if the base position is the name of a mark.
- */
- c = *endOfBase;
- *endOfBase = 0;
- result = TkTextMarkNameToIndex(textPtr, Tcl_DStringValue(©),
- indexPtr);
- *endOfBase = c;
- if (result == TCL_OK) {
- goto gotBase;
- }
- /*
- * See if the base position is the name of an embedded image
- */
- c = *endOfBase;
- *endOfBase = 0;
- result = TkTextImageIndex(textPtr, Tcl_DStringValue(©), indexPtr);
- *endOfBase = c;
- if (result != 0) {
- goto gotBase;
- }
- }
- goto error;
- /*
- *-------------------------------------------------------------------
- * 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:
- cp = endOfBase;
- while (1) {
- while (isspace(UCHAR(*cp))) {
- cp++;
- }
- if (*cp == 0) {
- break;
- }
-
- if ((*cp == '+') || (*cp == '-')) {
- cp = ForwBack(cp, indexPtr);
- } else {
- cp = StartEnd(cp, indexPtr);
- }
- if (cp == NULL) {
- goto error;
- }
- }
- Tcl_DStringFree(©);
- return TCL_OK;
- error:
- Tcl_DStringFree(©);
- Tcl_ResetResult(interp);
- Tcl_AppendResult(interp, "bad text index "", string, """,
- (char *) NULL);
- return TCL_ERROR;
- }
- /*
- *---------------------------------------------------------------------------
- *
- * TkTextPrintIndex --
- *
- * This procedure generates a string description of an index, suitable
- * for reading in again later.
- *
- * Results:
- * The characters pointed to by string are modified.
- *
- * Side effects:
- * None.
- *
- *---------------------------------------------------------------------------
- */
- 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. */
- {
- TkTextSegment *segPtr;
- int numBytes, charIndex;
- numBytes = indexPtr->byteIndex;
- charIndex = 0;
- for (segPtr = indexPtr->linePtr->segPtr; ; segPtr = segPtr->nextPtr) {
- if (numBytes <= segPtr->size) {
- break;
- }
- if (segPtr->typePtr == &tkTextCharType) {
- charIndex += Tcl_NumUtfChars(segPtr->body.chars, segPtr->size);
- } else {
- charIndex += segPtr->size;
- }
- 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);
- }
- /*
- *---------------------------------------------------------------------------
- *
- * TkTextIndexCmp --
- *
- * Compare two indices to see which one is earlier in the text.
- *
- * Results:
- * The return value is 0 if index1Ptr and index2Ptr refer to the same
- * position in the file, -1 if index1Ptr refers to an earlier position
- * than index2Ptr, and 1 otherwise.
- *
- * Side effects:
- * None.
- *
- *---------------------------------------------------------------------------
- */
- int
- TkTextIndexCmp(index1Ptr, index2Ptr)
- CONST TkTextIndex *index1Ptr; /* First index. */
- CONST TkTextIndex *index2Ptr; /* Second index. */
- {
- int line1, line2;
- if (index1Ptr->linePtr == index2Ptr->linePtr) {
- if (index1Ptr->byteIndex < index2Ptr->byteIndex) {
- return -1;
- } else if (index1Ptr->byteIndex > index2Ptr->byteIndex) {
- return 1;
- } else {
- return 0;
- }
- }
- line1 = TkBTreeLineIndex(index1Ptr->linePtr);
- line2 = TkBTreeLineIndex(index2Ptr->linePtr);
- if (line1 < line2) {
- return -1;
- }
- if (line1 > line2) {
- return 1;
- }
- return 0;
- }
- /*
- *---------------------------------------------------------------------------
- *
- * ForwBack --
- *
- * This procedure 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.
- *
- * Side effects:
- * None.
- *
- *---------------------------------------------------------------------------
- */
- 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. */
- {
- register CONST char *p, *units;
- char *end;
- int count, lineIndex;
- size_t length;
- /*
- * Get the count (how many units forward or backward).
- */
- p = string+1;
- while (isspace(UCHAR(*p))) {
- p++;
- }
- count = strtol(p, &end, 0);
- if (end == p) {
- return NULL;
- }
- p = end;
- while (isspace(UCHAR(*p))) {
- p++;
- }
- /*
- * Find the end of this modifier (next space or + or - character),
- * then parse the unit specifier and update the position
- * accordingly.
- */
- units = p;
- while ((*p != ' ') && !isspace(UCHAR(*p)) && (*p != '+') && (*p != '-')) {
- p++;
- }
- length = p - units;
- if ((*units == 'c') && (strncmp(units, "chars", length) == 0)) {
- if (*string == '+') {
- TkTextIndexForwChars(indexPtr, count, indexPtr);
- } else {
- TkTextIndexBackChars(indexPtr, count, indexPtr);
- }
- } else if ((*units == 'l') && (strncmp(units, "lines", length) == 0)) {
- 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;
- }
- }
- /*
- * 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;
- }
- return p;
- }
- /*
- *---------------------------------------------------------------------------
- *
- * TkTextIndexForwBytes --
- *
- * Given an index for a text widget, this procedure 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.
- *
- * Side effects:
- * None.
- *
- *---------------------------------------------------------------------------
- */
- void
- TkTextIndexForwBytes(srcPtr, byteCount, dstPtr)
- CONST TkTextIndex *srcPtr; /* Source index. */
- int byteCount; /* How many bytes forward to move. May be
- * negative. */
- TkTextIndex *dstPtr; /* Destination index: gets modified. */
- {
- TkTextLine *linePtr;
- TkTextSegment *segPtr;
- int lineLength;
- if (byteCount < 0) {
- TkTextIndexBackBytes(srcPtr, -byteCount, dstPtr);
- return;
- }
- *dstPtr = *srcPtr;
- dstPtr->byteIndex += byteCount;
- while (1) {
- /*
- * Compute the length of the current line.
- */
- lineLength = 0;
- for (segPtr = dstPtr->linePtr->segPtr; segPtr != NULL;
- segPtr = segPtr->nextPtr) {
- lineLength += segPtr->size;
- }
- /*
- * 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;
- }
- dstPtr->byteIndex -= lineLength;
- linePtr = TkBTreeNextLine(dstPtr->linePtr);
- if (linePtr == NULL) {
- dstPtr->byteIndex = lineLength - 1;
- return;
- }
- dstPtr->linePtr = linePtr;
- }
- }
- /*
- *---------------------------------------------------------------------------
- *
- * TkTextIndexForwChars --
- *
- * Given an index for a text widget, this procedure creates a new
- * index that points "count" characters ahead of the source index.
- *
- * 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.
- *
- * Side effects:
- * None.
- *
- *---------------------------------------------------------------------------
- */
- 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. */
- {
- TkTextLine *linePtr;
- TkTextSegment *segPtr;
- int byteOffset;
- char *start, *end, *p;
- Tcl_UniChar ch;
- if (charCount < 0) {
- TkTextIndexBackChars(srcPtr, -charCount, dstPtr);
- return;
- }
- *dstPtr = *srcPtr;
- /*
- * Find seg that contains src byteIndex.
- * Move forward specified number of chars.
- */
- segPtr = TkTextIndexToSeg(dstPtr, &byteOffset);
- while (1) {
- /*
- * Go through each segment in line looking for specified character
- * index.
- */
- 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;
- }
- charCount--;
- }
- } else {
- if (charCount < segPtr->size - byteOffset) {
- dstPtr->byteIndex += charCount;
- return;
- }
- 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.
- */
-
- linePtr = TkBTreeNextLine(dstPtr->linePtr);
- if (linePtr == NULL) {
- dstPtr->byteIndex -= sizeof(char);
- return;
- }
- dstPtr->linePtr = linePtr;
- dstPtr->byteIndex = 0;
- segPtr = dstPtr->linePtr->segPtr;
- }
- }
- /*
- *---------------------------------------------------------------------------
- *
- * TkTextIndexBackBytes --
- *
- * Given an index for a text widget, this procedure 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.
- *
- * Side effects:
- * None.
- *
- *---------------------------------------------------------------------------
- */
- void
- TkTextIndexBackBytes(srcPtr, byteCount, dstPtr)
- CONST TkTextIndex *srcPtr; /* Source index. */
- int byteCount; /* How many bytes backward to move. May be
- * negative. */
- TkTextIndex *dstPtr; /* Destination index: gets modified. */
- {
- TkTextSegment *segPtr;
- int lineIndex;
- if (byteCount < 0) {
- TkTextIndexForwBytes(srcPtr, -byteCount, dstPtr);
- return;
- }
- *dstPtr = *srcPtr;
- dstPtr->byteIndex -= byteCount;
- 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.
- */
- if (lineIndex < 0) {
- lineIndex = TkBTreeLineIndex(dstPtr->linePtr);
- }
- if (lineIndex == 0) {
- dstPtr->byteIndex = 0;
- return;
- }
- lineIndex--;
- dstPtr->linePtr = TkBTreeFindLine(dstPtr->tree, lineIndex);
- /*
- * Compute the length of the line and add that to dstPtr->charIndex.
- */
- for (segPtr = dstPtr->linePtr->segPtr; segPtr != NULL;
- segPtr = segPtr->nextPtr) {
- dstPtr->byteIndex += segPtr->size;
- }
- }
- }
- /*
- *---------------------------------------------------------------------------
- *
- * TkTextIndexBackChars --
- *
- * Given an index for a text widget, this procedure creates a new
- * index that points "count" characters earlier than the source index.
- *
- * 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.
- *
- * Side effects:
- * None.
- *
- *---------------------------------------------------------------------------
- */
- 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. */
- {
- TkTextSegment *segPtr, *oldPtr;
- int lineIndex, segSize;
- CONST char *p, *start, *end;
- if (charCount <= 0) {
- TkTextIndexForwChars(srcPtr, -charCount, dstPtr);
- return;
- }
- *dstPtr = *srcPtr;
- /*
- * 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;
- }
- 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;
- }
- charCount--;
- }
- } else {
- if (charCount <= segSize) {
- dstPtr->byteIndex -= charCount;
- return;
- }
- charCount -= segSize;
- }
- dstPtr->byteIndex -= segSize;
- /*
- * Move back into previous segment.
- */
- oldPtr = segPtr;
- segPtr = dstPtr->linePtr->segPtr;
- if (segPtr != oldPtr) {
- for ( ; segPtr->nextPtr != oldPtr; segPtr = segPtr->nextPtr) {
- /* Empty body. */
- }
- segSize = segPtr->size;
- continue;
- }
- /*
- * Move back to previous line.
- */
- if (lineIndex < 0) {
- lineIndex = TkBTreeLineIndex(dstPtr->linePtr);
- }
- if (lineIndex == 0) {
- dstPtr->byteIndex = 0;
- return;
- }
- lineIndex--;
- dstPtr->linePtr = TkBTreeFindLine(dstPtr->tree, lineIndex);
- /*
- * Compute the length of the line and add that to dstPtr->byteIndex.
- */
- oldPtr = dstPtr->linePtr->segPtr;
- for (segPtr = oldPtr; segPtr != NULL; segPtr = segPtr->nextPtr) {
- dstPtr->byteIndex += segPtr->size;
- oldPtr = segPtr;
- }
- segPtr = oldPtr;
- segSize = segPtr->size;
- }
- }
- /*
- *----------------------------------------------------------------------
- *
- * StartEnd --
- *
- * This procedure 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.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
- 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. */
- {
- CONST char *p;
- int c, offset;
- size_t length;
- register TkTextSegment *segPtr;
- /*
- * Find the end of the modifier word.
- */
- for (p = string; isalnum(UCHAR(*p)); p++) {
- /* Empty loop body. */
- }
- 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;
- }
- indexPtr->byteIndex -= sizeof(char);
- } else if ((*string == 'l') && (strncmp(string, "linestart", length) == 0)
- && (length >= 5)) {
- indexPtr->byteIndex = 0;
- } else if ((*string == 'w') && (strncmp(string, "wordend", length) == 0)
- && (length >= 5)) {
- int firstChar = 1;
- /*
- * 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.
- */
- segPtr = TkTextIndexToSeg(indexPtr, &offset);
- while (1) {
- if (segPtr->typePtr == &tkTextCharType) {
- c = segPtr->body.chars[offset];
- if (!isalnum(UCHAR(c)) && (c != '_')) {
- break;
- }
- firstChar = 0;
- }
- offset += 1;
- indexPtr->byteIndex += sizeof(char);
- if (offset >= segPtr->size) {
- segPtr = TkTextIndexToSeg(indexPtr, &offset);
- }
- }
- if (firstChar) {
- TkTextIndexForwChars(indexPtr, 1, indexPtr);
- }
- } else if ((*string == 'w') && (strncmp(string, "wordstart", length) == 0)
- && (length >= 5)) {
- int firstChar = 1;
- /*
- * 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) {
- if (segPtr->typePtr == &tkTextCharType) {
- c = segPtr->body.chars[offset];
- if (!isalnum(UCHAR(c)) && (c != '_')) {
- break;
- }
- firstChar = 0;
- }
- offset -= 1;
- indexPtr->byteIndex -= sizeof(char);
- if (offset < 0) {
- if (indexPtr->byteIndex < 0) {
- indexPtr->byteIndex = 0;
- goto done;
- }
- segPtr = TkTextIndexToSeg(indexPtr, &offset);
- }
- }
- if (!firstChar) {
- TkTextIndexForwChars(indexPtr, 1, indexPtr);
- }
- } else {
- return NULL;
- }
- done:
- return p;
- }