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

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

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

revision 70 by dashley, Sat Nov 5 10:54:17 2016 UTC revision 71 by dashley, Sat Nov 5 11:07:06 2016 UTC
# Line 1  Line 1 
1  /* $Header$ */  /* $Header$ */
2    
3  /*  /*
4   * tkTextIndex.c --   * tkTextIndex.c --
5   *   *
6   *      This module provides procedures that manipulate indices for   *      This module provides procedures that manipulate indices for
7   *      text widgets.   *      text widgets.
8   *   *
9   * Copyright (c) 1992-1994 The Regents of the University of California.   * Copyright (c) 1992-1994 The Regents of the University of California.
10   * Copyright (c) 1994-1997 Sun Microsystems, Inc.   * Copyright (c) 1994-1997 Sun Microsystems, Inc.
11   *   *
12   * See the file "license.terms" for information on usage and redistribution   * See the file "license.terms" for information on usage and redistribution
13   * of this file, and for a DISCLAIMER OF ALL WARRANTIES.   * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
14   *   *
15   * RCS: @(#) $Id: tktextindex.c,v 1.1.1.1 2001/06/13 05:10:33 dtashley Exp $   * RCS: @(#) $Id: tktextindex.c,v 1.1.1.1 2001/06/13 05:10:33 dtashley Exp $
16   */   */
17    
18  #include "default.h"  #include "default.h"
19  #include "tkPort.h"  #include "tkPort.h"
20  #include "tkInt.h"  #include "tkInt.h"
21  #include "tkText.h"  #include "tkText.h"
22    
23  /*  /*
24   * Index to use to select last character in line (very large integer):   * Index to use to select last character in line (very large integer):
25   */   */
26    
27  #define LAST_CHAR 1000000  #define LAST_CHAR 1000000
28    
29  /*  /*
30   * Forward declarations for procedures defined later in this file:   * Forward declarations for procedures defined later in this file:
31   */   */
32    
33  static char *           ForwBack _ANSI_ARGS_((char *string,  static char *           ForwBack _ANSI_ARGS_((char *string,
34                              TkTextIndex *indexPtr));                              TkTextIndex *indexPtr));
35  static char *           StartEnd _ANSI_ARGS_(( char *string,  static char *           StartEnd _ANSI_ARGS_(( char *string,
36                              TkTextIndex *indexPtr));                              TkTextIndex *indexPtr));
37    
38  /*  /*
39   *---------------------------------------------------------------------------   *---------------------------------------------------------------------------
40   *   *
41   * TkTextMakeByteIndex --   * TkTextMakeByteIndex --
42   *   *
43   *      Given a line index and a byte index, look things up in the B-tree   *      Given a line index and a byte index, look things up in the B-tree
44   *      and fill in a TkTextIndex structure.   *      and fill in a TkTextIndex structure.
45   *   *
46   * Results:   * Results:
47   *      The structure at *indexPtr is filled in with information about the   *      The structure at *indexPtr is filled in with information about the
48   *      character at lineIndex and byteIndex (or the closest existing   *      character at lineIndex and byteIndex (or the closest existing
49   *      character, if the specified one doesn't exist), and indexPtr is   *      character, if the specified one doesn't exist), and indexPtr is
50   *      returned as result.   *      returned as result.
51   *   *
52   * Side effects:   * Side effects:
53   *      None.   *      None.
54   *   *
55   *---------------------------------------------------------------------------   *---------------------------------------------------------------------------
56   */   */
57    
58  TkTextIndex *  TkTextIndex *
59  TkTextMakeByteIndex(tree, lineIndex, byteIndex, indexPtr)  TkTextMakeByteIndex(tree, lineIndex, byteIndex, indexPtr)
60      TkTextBTree tree;           /* Tree that lineIndex and charIndex refer      TkTextBTree tree;           /* Tree that lineIndex and charIndex refer
61                                   * to. */                                   * to. */
62      int lineIndex;              /* Index of desired line (0 means first      int lineIndex;              /* Index of desired line (0 means first
63                                   * line of text). */                                   * line of text). */
64      int byteIndex;              /* Byte index of desired character. */      int byteIndex;              /* Byte index of desired character. */
65      TkTextIndex *indexPtr;      /* Structure to fill in. */      TkTextIndex *indexPtr;      /* Structure to fill in. */
66  {  {
67      TkTextSegment *segPtr;      TkTextSegment *segPtr;
68      int index;      int index;
69      char *p, *start;      char *p, *start;
70      Tcl_UniChar ch;      Tcl_UniChar ch;
71    
72      indexPtr->tree = tree;      indexPtr->tree = tree;
73      if (lineIndex < 0) {      if (lineIndex < 0) {
74          lineIndex = 0;          lineIndex = 0;
75          byteIndex = 0;          byteIndex = 0;
76      }      }
77      if (byteIndex < 0) {      if (byteIndex < 0) {
78          byteIndex = 0;          byteIndex = 0;
79      }      }
80      indexPtr->linePtr = TkBTreeFindLine(tree, lineIndex);      indexPtr->linePtr = TkBTreeFindLine(tree, lineIndex);
81      if (indexPtr->linePtr == NULL) {      if (indexPtr->linePtr == NULL) {
82          indexPtr->linePtr = TkBTreeFindLine(tree, TkBTreeNumLines(tree));          indexPtr->linePtr = TkBTreeFindLine(tree, TkBTreeNumLines(tree));
83          byteIndex = 0;          byteIndex = 0;
84      }      }
85      if (byteIndex == 0) {      if (byteIndex == 0) {
86          indexPtr->byteIndex = byteIndex;          indexPtr->byteIndex = byteIndex;
87          return indexPtr;          return indexPtr;
88      }      }
89    
90      /*      /*
91       * Verify that the index is within the range of the line and points       * Verify that the index is within the range of the line and points
92       * to a valid character boundary.         * to a valid character boundary.  
93       */       */
94    
95      index = 0;      index = 0;
96      for (segPtr = indexPtr->linePtr->segPtr; ; segPtr = segPtr->nextPtr) {      for (segPtr = indexPtr->linePtr->segPtr; ; segPtr = segPtr->nextPtr) {
97          if (segPtr == NULL) {          if (segPtr == NULL) {
98              /*              /*
99               * Use the index of the last character in the line.  Since               * Use the index of the last character in the line.  Since
100               * the last character on the line is guaranteed to be a '\n',               * the last character on the line is guaranteed to be a '\n',
101               * we can back up a constant sizeof(char) bytes.               * we can back up a constant sizeof(char) bytes.
102               */               */
103                            
104              indexPtr->byteIndex = index - sizeof(char);              indexPtr->byteIndex = index - sizeof(char);
105              break;              break;
106          }          }
107          if (index + segPtr->size > byteIndex) {          if (index + segPtr->size > byteIndex) {
108              indexPtr->byteIndex = byteIndex;              indexPtr->byteIndex = byteIndex;
109              if ((byteIndex > index) && (segPtr->typePtr == &tkTextCharType)) {              if ((byteIndex > index) && (segPtr->typePtr == &tkTextCharType)) {
110                  /*                  /*
111                   * Prevent UTF-8 character from being split up by ensuring                   * Prevent UTF-8 character from being split up by ensuring
112                   * that byteIndex falls on a character boundary.  If index                   * that byteIndex falls on a character boundary.  If index
113                   * falls in the middle of a UTF-8 character, it will be                   * falls in the middle of a UTF-8 character, it will be
114                   * adjusted to the end of that UTF-8 character.                   * adjusted to the end of that UTF-8 character.
115                   */                   */
116    
117                  start = segPtr->body.chars + (byteIndex - index);                  start = segPtr->body.chars + (byteIndex - index);
118                  p = Tcl_UtfPrev(start, segPtr->body.chars);                  p = Tcl_UtfPrev(start, segPtr->body.chars);
119                  p += Tcl_UtfToUniChar(p, &ch);                  p += Tcl_UtfToUniChar(p, &ch);
120                  indexPtr->byteIndex += p - start;                  indexPtr->byteIndex += p - start;
121              }              }
122              break;              break;
123          }          }
124          index += segPtr->size;          index += segPtr->size;
125      }      }
126      return indexPtr;      return indexPtr;
127  }  }
128    
129  /*  /*
130   *---------------------------------------------------------------------------   *---------------------------------------------------------------------------
131   *   *
132   * TkTextMakeCharIndex --   * TkTextMakeCharIndex --
133   *   *
134   *      Given a line index and a character index, look things up in the   *      Given a line index and a character index, look things up in the
135   *      B-tree and fill in a TkTextIndex structure.   *      B-tree and fill in a TkTextIndex structure.
136   *   *
137   * Results:   * Results:
138   *      The structure at *indexPtr is filled in with information about the   *      The structure at *indexPtr is filled in with information about the
139   *      character at lineIndex and charIndex (or the closest existing   *      character at lineIndex and charIndex (or the closest existing
140   *      character, if the specified one doesn't exist), and indexPtr is   *      character, if the specified one doesn't exist), and indexPtr is
141   *      returned as result.   *      returned as result.
142   *   *
143   * Side effects:   * Side effects:
144   *      None.   *      None.
145   *   *
146   *---------------------------------------------------------------------------   *---------------------------------------------------------------------------
147   */   */
148    
149  TkTextIndex *  TkTextIndex *
150  TkTextMakeCharIndex(tree, lineIndex, charIndex, indexPtr)  TkTextMakeCharIndex(tree, lineIndex, charIndex, indexPtr)
151      TkTextBTree tree;           /* Tree that lineIndex and charIndex refer      TkTextBTree tree;           /* Tree that lineIndex and charIndex refer
152                                   * to. */                                   * to. */
153      int lineIndex;              /* Index of desired line (0 means first      int lineIndex;              /* Index of desired line (0 means first
154                                   * line of text). */                                   * line of text). */
155      int charIndex;              /* Index of desired character. */      int charIndex;              /* Index of desired character. */
156      TkTextIndex *indexPtr;      /* Structure to fill in. */      TkTextIndex *indexPtr;      /* Structure to fill in. */
157  {  {
158      register TkTextSegment *segPtr;      register TkTextSegment *segPtr;
159      char *p, *start, *end;      char *p, *start, *end;
160      int index, offset;      int index, offset;
161      Tcl_UniChar ch;      Tcl_UniChar ch;
162    
163      indexPtr->tree = tree;      indexPtr->tree = tree;
164      if (lineIndex < 0) {      if (lineIndex < 0) {
165          lineIndex = 0;          lineIndex = 0;
166          charIndex = 0;          charIndex = 0;
167      }      }
168      if (charIndex < 0) {      if (charIndex < 0) {
169          charIndex = 0;          charIndex = 0;
170      }      }
171      indexPtr->linePtr = TkBTreeFindLine(tree, lineIndex);      indexPtr->linePtr = TkBTreeFindLine(tree, lineIndex);
172      if (indexPtr->linePtr == NULL) {      if (indexPtr->linePtr == NULL) {
173          indexPtr->linePtr = TkBTreeFindLine(tree, TkBTreeNumLines(tree));          indexPtr->linePtr = TkBTreeFindLine(tree, TkBTreeNumLines(tree));
174          charIndex = 0;          charIndex = 0;
175      }      }
176    
177      /*      /*
178       * Verify that the index is within the range of the line.       * Verify that the index is within the range of the line.
179       * If not, just use the index of the last character in the line.       * If not, just use the index of the last character in the line.
180       */       */
181    
182      index = 0;      index = 0;
183      for (segPtr = indexPtr->linePtr->segPtr; ; segPtr = segPtr->nextPtr) {      for (segPtr = indexPtr->linePtr->segPtr; ; segPtr = segPtr->nextPtr) {
184          if (segPtr == NULL) {          if (segPtr == NULL) {
185              /*              /*
186               * Use the index of the last character in the line.  Since               * Use the index of the last character in the line.  Since
187               * the last character on the line is guaranteed to be a '\n',               * the last character on the line is guaranteed to be a '\n',
188               * we can back up a constant sizeof(char) bytes.               * we can back up a constant sizeof(char) bytes.
189               */               */
190                            
191              indexPtr->byteIndex = index - sizeof(char);              indexPtr->byteIndex = index - sizeof(char);
192              break;              break;
193          }          }
194          if (segPtr->typePtr == &tkTextCharType) {          if (segPtr->typePtr == &tkTextCharType) {
195              /*              /*
196               * Turn character offset into a byte offset.               * Turn character offset into a byte offset.
197               */               */
198    
199              start = segPtr->body.chars;              start = segPtr->body.chars;
200              end = start + segPtr->size;              end = start + segPtr->size;
201              for (p = start; p < end; p += offset) {              for (p = start; p < end; p += offset) {
202                  if (charIndex == 0) {                  if (charIndex == 0) {
203                      indexPtr->byteIndex = index;                      indexPtr->byteIndex = index;
204                      return indexPtr;                      return indexPtr;
205                  }                  }
206                  charIndex--;                  charIndex--;
207                  offset = Tcl_UtfToUniChar(p, &ch);                  offset = Tcl_UtfToUniChar(p, &ch);
208                  index += offset;                  index += offset;
209              }              }
210          } else {          } else {
211              if (charIndex < segPtr->size) {              if (charIndex < segPtr->size) {
212                  indexPtr->byteIndex = index;                  indexPtr->byteIndex = index;
213                  break;                  break;
214              }              }
215              charIndex -= segPtr->size;              charIndex -= segPtr->size;
216              index += segPtr->size;              index += segPtr->size;
217          }          }
218      }      }
219      return indexPtr;      return indexPtr;
220  }  }
221    
222  /*  /*
223   *---------------------------------------------------------------------------   *---------------------------------------------------------------------------
224   *   *
225   * TkTextIndexToSeg --   * TkTextIndexToSeg --
226   *   *
227   *      Given an index, this procedure returns the segment and offset   *      Given an index, this procedure returns the segment and offset
228   *      within segment for the index.   *      within segment for the index.
229   *   *
230   * Results:   * Results:
231   *      The return value is a pointer to the segment referred to by   *      The return value is a pointer to the segment referred to by
232   *      indexPtr; this will always be a segment with non-zero size.  The   *      indexPtr; this will always be a segment with non-zero size.  The
233   *      variable at *offsetPtr is set to hold the integer offset within   *      variable at *offsetPtr is set to hold the integer offset within
234   *      the segment of the character given by indexPtr.   *      the segment of the character given by indexPtr.
235   *   *
236   * Side effects:   * Side effects:
237   *      None.   *      None.
238   *   *
239   *---------------------------------------------------------------------------   *---------------------------------------------------------------------------
240   */   */
241    
242  TkTextSegment *  TkTextSegment *
243  TkTextIndexToSeg(indexPtr, offsetPtr)  TkTextIndexToSeg(indexPtr, offsetPtr)
244      CONST TkTextIndex *indexPtr;/* Text index. */      CONST TkTextIndex *indexPtr;/* Text index. */
245      int *offsetPtr;             /* Where to store offset within segment, or      int *offsetPtr;             /* Where to store offset within segment, or
246                                   * NULL if offset isn't wanted. */                                   * NULL if offset isn't wanted. */
247  {  {
248      TkTextSegment *segPtr;      TkTextSegment *segPtr;
249      int offset;      int offset;
250    
251      for (offset = indexPtr->byteIndex, segPtr = indexPtr->linePtr->segPtr;      for (offset = indexPtr->byteIndex, segPtr = indexPtr->linePtr->segPtr;
252              offset >= segPtr->size;              offset >= segPtr->size;
253              offset -= segPtr->size, segPtr = segPtr->nextPtr) {              offset -= segPtr->size, segPtr = segPtr->nextPtr) {
254          /* Empty loop body. */          /* Empty loop body. */
255      }      }
256      if (offsetPtr != NULL) {      if (offsetPtr != NULL) {
257          *offsetPtr = offset;          *offsetPtr = offset;
258      }      }
259      return segPtr;      return segPtr;
260  }  }
261    
262  /*  /*
263   *---------------------------------------------------------------------------   *---------------------------------------------------------------------------
264   *   *
265   * TkTextSegToOffset --   * TkTextSegToOffset --
266   *   *
267   *      Given a segment pointer and the line containing it, this procedure   *      Given a segment pointer and the line containing it, this procedure
268   *      returns the offset of the segment within its line.   *      returns the offset of the segment within its line.
269   *   *
270   * Results:   * Results:
271   *      The return value is the offset (within its line) of the first   *      The return value is the offset (within its line) of the first
272   *      character in segPtr.   *      character in segPtr.
273   *   *
274   * Side effects:   * Side effects:
275   *      None.   *      None.
276   *   *
277   *---------------------------------------------------------------------------   *---------------------------------------------------------------------------
278   */   */
279    
280  int  int
281  TkTextSegToOffset(segPtr, linePtr)  TkTextSegToOffset(segPtr, linePtr)
282      CONST TkTextSegment *segPtr;/* Segment whose offset is desired. */      CONST TkTextSegment *segPtr;/* Segment whose offset is desired. */
283      CONST TkTextLine *linePtr;  /* Line containing segPtr. */      CONST TkTextLine *linePtr;  /* Line containing segPtr. */
284  {  {
285      CONST TkTextSegment *segPtr2;      CONST TkTextSegment *segPtr2;
286      int offset;      int offset;
287    
288      offset = 0;      offset = 0;
289      for (segPtr2 = linePtr->segPtr; segPtr2 != segPtr;      for (segPtr2 = linePtr->segPtr; segPtr2 != segPtr;
290              segPtr2 = segPtr2->nextPtr) {              segPtr2 = segPtr2->nextPtr) {
291          offset += segPtr2->size;          offset += segPtr2->size;
292      }      }
293      return offset;      return offset;
294  }  }
295    
296  /*  /*
297   *---------------------------------------------------------------------------   *---------------------------------------------------------------------------
298   *   *
299   * TkTextGetIndex --   * TkTextGetIndex --
300   *   *
301   *      Given a string, return the index that is described.   *      Given a string, return the index that is described.
302   *   *
303   * Results:   * Results:
304   *      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
305   *      returned, then everything went well and the index at *indexPtr is   *      returned, then everything went well and the index at *indexPtr is
306   *      filled in; otherwise TCL_ERROR is returned and an error message   *      filled in; otherwise TCL_ERROR is returned and an error message
307   *      is left in the interp's result.   *      is left in the interp's result.
308   *   *
309   * Side effects:   * Side effects:
310   *      None.   *      None.
311   *   *
312   *---------------------------------------------------------------------------   *---------------------------------------------------------------------------
313   */   */
314    
315  int  int
316  TkTextGetIndex(interp, textPtr, string, indexPtr)  TkTextGetIndex(interp, textPtr, string, indexPtr)
317      Tcl_Interp *interp;         /* Use this for error reporting. */      Tcl_Interp *interp;         /* Use this for error reporting. */
318      TkText *textPtr;            /* Information about text widget. */      TkText *textPtr;            /* Information about text widget. */
319      char *string;               /* Textual description of position. */      char *string;               /* Textual description of position. */
320      TkTextIndex *indexPtr;      /* Index structure to fill in. */      TkTextIndex *indexPtr;      /* Index structure to fill in. */
321  {  {
322      char *p, *end, *endOfBase;      char *p, *end, *endOfBase;
323      Tcl_HashEntry *hPtr;      Tcl_HashEntry *hPtr;
324      TkTextTag *tagPtr;      TkTextTag *tagPtr;
325      TkTextSearch search;      TkTextSearch search;
326      TkTextIndex first, last;      TkTextIndex first, last;
327      int wantLast, result;      int wantLast, result;
328      char c;      char c;
329    
330      /*      /*
331       *---------------------------------------------------------------------       *---------------------------------------------------------------------
332       * Stage 1: check to see if the index consists of nothing but a mark       * Stage 1: check to see if the index consists of nothing but a mark
333       * name.  We do this check now even though it's also done later, in       * name.  We do this check now even though it's also done later, in
334       * order to allow mark names that include funny characters such as       * order to allow mark names that include funny characters such as
335       * spaces or "+1c".       * spaces or "+1c".
336       *---------------------------------------------------------------------       *---------------------------------------------------------------------
337       */       */
338    
339      if (TkTextMarkNameToIndex(textPtr, string, indexPtr) == TCL_OK) {      if (TkTextMarkNameToIndex(textPtr, string, indexPtr) == TCL_OK) {
340          return TCL_OK;          return TCL_OK;
341      }      }
342    
343      /*      /*
344       *------------------------------------------------       *------------------------------------------------
345       * Stage 2: start again by parsing the base index.       * Stage 2: start again by parsing the base index.
346       *------------------------------------------------       *------------------------------------------------
347       */       */
348    
349      indexPtr->tree = textPtr->tree;      indexPtr->tree = textPtr->tree;
350    
351      /*      /*
352       * First look for the form "tag.first" or "tag.last" where "tag"       * First look for the form "tag.first" or "tag.last" where "tag"
353       * is the name of a valid tag.  Try to use up as much as possible       * is the name of a valid tag.  Try to use up as much as possible
354       * of the string in this check (strrchr instead of strchr below).       * of the string in this check (strrchr instead of strchr below).
355       * Doing the check now, and in this way, allows tag names to include       * Doing the check now, and in this way, allows tag names to include
356       * funny characters like "@" or "+1c".       * funny characters like "@" or "+1c".
357       */       */
358    
359      p = strrchr(string, '.');      p = strrchr(string, '.');
360      if (p != NULL) {      if (p != NULL) {
361          if ((p[1] == 'f') && (strncmp(p+1, "first", 5) == 0)) {          if ((p[1] == 'f') && (strncmp(p+1, "first", 5) == 0)) {
362              wantLast = 0;              wantLast = 0;
363              endOfBase = p+6;              endOfBase = p+6;
364          } else if ((p[1] == 'l') && (strncmp(p+1, "last", 4) == 0)) {          } else if ((p[1] == 'l') && (strncmp(p+1, "last", 4) == 0)) {
365              wantLast = 1;              wantLast = 1;
366              endOfBase = p+5;              endOfBase = p+5;
367          } else {          } else {
368              goto tryxy;              goto tryxy;
369          }          }
370          *p = 0;          *p = 0;
371          hPtr = Tcl_FindHashEntry(&textPtr->tagTable, string);          hPtr = Tcl_FindHashEntry(&textPtr->tagTable, string);
372          *p = '.';          *p = '.';
373          if (hPtr == NULL) {          if (hPtr == NULL) {
374              goto tryxy;              goto tryxy;
375          }          }
376          tagPtr = (TkTextTag *) Tcl_GetHashValue(hPtr);          tagPtr = (TkTextTag *) Tcl_GetHashValue(hPtr);
377          TkTextMakeByteIndex(textPtr->tree, 0, 0, &first);          TkTextMakeByteIndex(textPtr->tree, 0, 0, &first);
378          TkTextMakeByteIndex(textPtr->tree, TkBTreeNumLines(textPtr->tree), 0,          TkTextMakeByteIndex(textPtr->tree, TkBTreeNumLines(textPtr->tree), 0,
379                  &last);                  &last);
380          TkBTreeStartSearch(&first, &last, tagPtr, &search);          TkBTreeStartSearch(&first, &last, tagPtr, &search);
381          if (!TkBTreeCharTagged(&first, tagPtr) && !TkBTreeNextTag(&search)) {          if (!TkBTreeCharTagged(&first, tagPtr) && !TkBTreeNextTag(&search)) {
382              Tcl_AppendResult(interp,              Tcl_AppendResult(interp,
383                      "text doesn't contain any characters tagged with \"",                      "text doesn't contain any characters tagged with \"",
384                      Tcl_GetHashKey(&textPtr->tagTable, hPtr), "\"",                      Tcl_GetHashKey(&textPtr->tagTable, hPtr), "\"",
385                              (char *) NULL);                              (char *) NULL);
386              return TCL_ERROR;              return TCL_ERROR;
387          }          }
388          *indexPtr = search.curIndex;          *indexPtr = search.curIndex;
389          if (wantLast) {          if (wantLast) {
390              while (TkBTreeNextTag(&search)) {              while (TkBTreeNextTag(&search)) {
391                  *indexPtr = search.curIndex;                  *indexPtr = search.curIndex;
392              }              }
393          }          }
394          goto gotBase;          goto gotBase;
395      }      }
396    
397      tryxy:      tryxy:
398      if (string[0] == '@') {      if (string[0] == '@') {
399          /*          /*
400           * Find character at a given x,y location in the window.           * Find character at a given x,y location in the window.
401           */           */
402    
403          int x, y;          int x, y;
404    
405          p = string+1;          p = string+1;
406          x = strtol(p, &end, 0);          x = strtol(p, &end, 0);
407          if ((end == p) || (*end != ',')) {          if ((end == p) || (*end != ',')) {
408              goto error;              goto error;
409          }          }
410          p = end+1;          p = end+1;
411          y = strtol(p, &end, 0);          y = strtol(p, &end, 0);
412          if (end == p) {          if (end == p) {
413              goto error;              goto error;
414          }          }
415          TkTextPixelIndex(textPtr, x, y, indexPtr);          TkTextPixelIndex(textPtr, x, y, indexPtr);
416          endOfBase = end;          endOfBase = end;
417          goto gotBase;          goto gotBase;
418      }      }
419    
420      if (isdigit(UCHAR(string[0])) || (string[0] == '-')) {      if (isdigit(UCHAR(string[0])) || (string[0] == '-')) {
421          int lineIndex, charIndex;          int lineIndex, charIndex;
422    
423          /*          /*
424           * Base is identified with line and character indices.           * Base is identified with line and character indices.
425           */           */
426    
427          lineIndex = strtol(string, &end, 0) - 1;          lineIndex = strtol(string, &end, 0) - 1;
428          if ((end == string) || (*end != '.')) {          if ((end == string) || (*end != '.')) {
429              goto error;              goto error;
430          }          }
431          p = end+1;          p = end+1;
432          if ((*p == 'e') && (strncmp(p, "end", 3) == 0)) {          if ((*p == 'e') && (strncmp(p, "end", 3) == 0)) {
433              charIndex = LAST_CHAR;              charIndex = LAST_CHAR;
434              endOfBase = p+3;              endOfBase = p+3;
435          } else {          } else {
436              charIndex = strtol(p, &end, 0);              charIndex = strtol(p, &end, 0);
437              if (end == p) {              if (end == p) {
438                  goto error;                  goto error;
439              }              }
440              endOfBase = end;              endOfBase = end;
441          }          }
442          TkTextMakeCharIndex(textPtr->tree, lineIndex, charIndex, indexPtr);          TkTextMakeCharIndex(textPtr->tree, lineIndex, charIndex, indexPtr);
443          goto gotBase;          goto gotBase;
444      }      }
445    
446      for (p = string; *p != 0; p++) {      for (p = string; *p != 0; p++) {
447          if (isspace(UCHAR(*p)) || (*p == '+') || (*p == '-')) {          if (isspace(UCHAR(*p)) || (*p == '+') || (*p == '-')) {
448              break;              break;
449          }          }
450      }      }
451      endOfBase = p;      endOfBase = p;
452      if (string[0] == '.') {      if (string[0] == '.') {
453          /*          /*
454           * See if the base position is the name of an embedded window.           * See if the base position is the name of an embedded window.
455           */           */
456    
457          c = *endOfBase;          c = *endOfBase;
458          *endOfBase = 0;          *endOfBase = 0;
459          result = TkTextWindowIndex(textPtr, string, indexPtr);          result = TkTextWindowIndex(textPtr, string, indexPtr);
460          *endOfBase = c;          *endOfBase = c;
461          if (result != 0) {          if (result != 0) {
462              goto gotBase;              goto gotBase;
463          }          }
464      }      }
465      if ((string[0] == 'e')      if ((string[0] == 'e')
466              && (strncmp(string, "end", (size_t) (endOfBase-string)) == 0)) {              && (strncmp(string, "end", (size_t) (endOfBase-string)) == 0)) {
467          /*          /*
468           * Base position is end of text.           * Base position is end of text.
469           */           */
470    
471          TkTextMakeByteIndex(textPtr->tree, TkBTreeNumLines(textPtr->tree),          TkTextMakeByteIndex(textPtr->tree, TkBTreeNumLines(textPtr->tree),
472                  0, indexPtr);                  0, indexPtr);
473          goto gotBase;          goto gotBase;
474      } else {      } else {
475          /*          /*
476           * See if the base position is the name of a mark.           * See if the base position is the name of a mark.
477           */           */
478    
479          c = *endOfBase;          c = *endOfBase;
480          *endOfBase = 0;          *endOfBase = 0;
481          result = TkTextMarkNameToIndex(textPtr, string, indexPtr);          result = TkTextMarkNameToIndex(textPtr, string, indexPtr);
482          *endOfBase = c;          *endOfBase = c;
483          if (result == TCL_OK) {          if (result == TCL_OK) {
484              goto gotBase;              goto gotBase;
485          }          }
486    
487          /*          /*
488           * See if the base position is the name of an embedded image           * See if the base position is the name of an embedded image
489           */           */
490    
491          c = *endOfBase;          c = *endOfBase;
492          *endOfBase = 0;          *endOfBase = 0;
493          result = TkTextImageIndex(textPtr, string, indexPtr);          result = TkTextImageIndex(textPtr, string, indexPtr);
494          *endOfBase = c;          *endOfBase = c;
495          if (result != 0) {          if (result != 0) {
496              goto gotBase;              goto gotBase;
497          }          }
498      }      }
499      goto error;      goto error;
500    
501      /*      /*
502       *-------------------------------------------------------------------       *-------------------------------------------------------------------
503       * Stage 3: process zero or more modifiers.  Each modifier is either       * Stage 3: process zero or more modifiers.  Each modifier is either
504       * a keyword like "wordend" or "linestart", or it has the form       * a keyword like "wordend" or "linestart", or it has the form
505       * "op count units" where op is + or -, count is a number, and units       * "op count units" where op is + or -, count is a number, and units
506       * is "chars" or "lines".       * is "chars" or "lines".
507       *-------------------------------------------------------------------       *-------------------------------------------------------------------
508       */       */
509    
510      gotBase:      gotBase:
511      p = endOfBase;      p = endOfBase;
512      while (1) {      while (1) {
513          while (isspace(UCHAR(*p))) {          while (isspace(UCHAR(*p))) {
514              p++;              p++;
515          }          }
516          if (*p == 0) {          if (*p == 0) {
517              break;              break;
518          }          }
519            
520          if ((*p == '+') || (*p == '-')) {          if ((*p == '+') || (*p == '-')) {
521              p = ForwBack(p, indexPtr);              p = ForwBack(p, indexPtr);
522          } else {          } else {
523              p = StartEnd(p, indexPtr);              p = StartEnd(p, indexPtr);
524          }          }
525          if (p == NULL) {          if (p == NULL) {
526              goto error;              goto error;
527          }          }
528      }      }
529      return TCL_OK;      return TCL_OK;
530    
531      error:      error:
532      Tcl_AppendResult(interp, "bad text index \"", string, "\"",      Tcl_AppendResult(interp, "bad text index \"", string, "\"",
533              (char *) NULL);              (char *) NULL);
534      return TCL_ERROR;      return TCL_ERROR;
535  }  }
536    
537  /*  /*
538   *---------------------------------------------------------------------------   *---------------------------------------------------------------------------
539   *   *
540   * TkTextPrintIndex --   * TkTextPrintIndex --
541   *         *      
542   *      This procedure generates a string description of an index, suitable   *      This procedure generates a string description of an index, suitable
543   *      for reading in again later.   *      for reading in again later.
544   *   *
545   * Results:   * Results:
546   *      The characters pointed to by string are modified.   *      The characters pointed to by string are modified.
547   *   *
548   * Side effects:   * Side effects:
549   *      None.   *      None.
550   *   *
551   *---------------------------------------------------------------------------   *---------------------------------------------------------------------------
552   */   */
553    
554  void  void
555  TkTextPrintIndex(indexPtr, string)  TkTextPrintIndex(indexPtr, string)
556      CONST TkTextIndex *indexPtr;/* Pointer to index. */      CONST TkTextIndex *indexPtr;/* Pointer to index. */
557      char *string;               /* Place to store the position.  Must have      char *string;               /* Place to store the position.  Must have
558                                   * at least TK_POS_CHARS characters. */                                   * at least TK_POS_CHARS characters. */
559  {  {
560      TkTextSegment *segPtr;      TkTextSegment *segPtr;
561      int numBytes, charIndex;      int numBytes, charIndex;
562    
563      numBytes = indexPtr->byteIndex;      numBytes = indexPtr->byteIndex;
564      charIndex = 0;      charIndex = 0;
565      for (segPtr = indexPtr->linePtr->segPtr; ; segPtr = segPtr->nextPtr) {      for (segPtr = indexPtr->linePtr->segPtr; ; segPtr = segPtr->nextPtr) {
566          if (numBytes <= segPtr->size) {          if (numBytes <= segPtr->size) {
567              break;              break;
568          }          }
569          if (segPtr->typePtr == &tkTextCharType) {          if (segPtr->typePtr == &tkTextCharType) {
570              charIndex += Tcl_NumUtfChars(segPtr->body.chars, segPtr->size);              charIndex += Tcl_NumUtfChars(segPtr->body.chars, segPtr->size);
571          } else {          } else {
572              charIndex += segPtr->size;              charIndex += segPtr->size;
573          }          }
574          numBytes -= segPtr->size;          numBytes -= segPtr->size;
575      }      }
576      if (segPtr->typePtr == &tkTextCharType) {      if (segPtr->typePtr == &tkTextCharType) {
577          charIndex += Tcl_NumUtfChars(segPtr->body.chars, numBytes);          charIndex += Tcl_NumUtfChars(segPtr->body.chars, numBytes);
578      } else {      } else {
579          charIndex += numBytes;          charIndex += numBytes;
580      }      }
581      sprintf(string, "%d.%d", TkBTreeLineIndex(indexPtr->linePtr) + 1,      sprintf(string, "%d.%d", TkBTreeLineIndex(indexPtr->linePtr) + 1,
582              charIndex);              charIndex);
583  }  }
584    
585  /*  /*
586   *---------------------------------------------------------------------------   *---------------------------------------------------------------------------
587   *   *
588   * TkTextIndexCmp --   * TkTextIndexCmp --
589   *   *
590   *      Compare two indices to see which one is earlier in the text.   *      Compare two indices to see which one is earlier in the text.
591   *   *
592   * Results:   * Results:
593   *      The return value is 0 if index1Ptr and index2Ptr refer to the same   *      The return value is 0 if index1Ptr and index2Ptr refer to the same
594   *      position in the file, -1 if index1Ptr refers to an earlier position   *      position in the file, -1 if index1Ptr refers to an earlier position
595   *      than index2Ptr, and 1 otherwise.   *      than index2Ptr, and 1 otherwise.
596   *   *
597   * Side effects:   * Side effects:
598   *      None.   *      None.
599   *   *
600   *---------------------------------------------------------------------------   *---------------------------------------------------------------------------
601   */   */
602    
603  int  int
604  TkTextIndexCmp(index1Ptr, index2Ptr)  TkTextIndexCmp(index1Ptr, index2Ptr)
605      CONST TkTextIndex *index1Ptr;               /* First index. */      CONST TkTextIndex *index1Ptr;               /* First index. */
606      CONST TkTextIndex *index2Ptr;               /* Second index. */      CONST TkTextIndex *index2Ptr;               /* Second index. */
607  {  {
608      int line1, line2;      int line1, line2;
609    
610      if (index1Ptr->linePtr == index2Ptr->linePtr) {      if (index1Ptr->linePtr == index2Ptr->linePtr) {
611          if (index1Ptr->byteIndex < index2Ptr->byteIndex) {          if (index1Ptr->byteIndex < index2Ptr->byteIndex) {
612              return -1;              return -1;
613          } else if (index1Ptr->byteIndex > index2Ptr->byteIndex) {          } else if (index1Ptr->byteIndex > index2Ptr->byteIndex) {
614              return 1;              return 1;
615          } else {          } else {
616              return 0;              return 0;
617          }          }
618      }      }
619      line1 = TkBTreeLineIndex(index1Ptr->linePtr);      line1 = TkBTreeLineIndex(index1Ptr->linePtr);
620      line2 = TkBTreeLineIndex(index2Ptr->linePtr);      line2 = TkBTreeLineIndex(index2Ptr->linePtr);
621      if (line1 < line2) {      if (line1 < line2) {
622          return -1;          return -1;
623      }      }
624      if (line1 > line2) {      if (line1 > line2) {
625          return 1;          return 1;
626      }      }
627      return 0;      return 0;
628  }  }
629    
630  /*  /*
631   *---------------------------------------------------------------------------   *---------------------------------------------------------------------------
632   *   *
633   * ForwBack --   * ForwBack --
634   *   *
635   *      This procedure handles +/- modifiers for indices to adjust the   *      This procedure handles +/- modifiers for indices to adjust the
636   *      index forwards or backwards.   *      index forwards or backwards.
637   *   *
638   * Results:   * Results:
639   *      If the modifier in string is successfully parsed then the return   *      If the modifier in string is successfully parsed then the return
640   *      value is the address of the first character after the modifier,   *      value is the address of the first character after the modifier,
641   *      and *indexPtr is updated to reflect the modifier.  If there is a   *      and *indexPtr is updated to reflect the modifier.  If there is a
642   *      syntax error in the modifier then NULL is returned.   *      syntax error in the modifier then NULL is returned.
643   *   *
644   * Side effects:   * Side effects:
645   *      None.   *      None.
646   *   *
647   *---------------------------------------------------------------------------   *---------------------------------------------------------------------------
648   */   */
649    
650  static char *  static char *
651  ForwBack(string, indexPtr)  ForwBack(string, indexPtr)
652      char *string;               /* String to parse for additional info      char *string;               /* String to parse for additional info
653                                   * about modifier (count and units).                                   * about modifier (count and units).
654                                   * Points to "+" or "-" that starts                                   * Points to "+" or "-" that starts
655                                   * modifier. */                                   * modifier. */
656      TkTextIndex *indexPtr;      /* Index to update as specified in string. */      TkTextIndex *indexPtr;      /* Index to update as specified in string. */
657  {  {
658      register char *p;      register char *p;
659      char *end, *units;      char *end, *units;
660      int count, lineIndex;      int count, lineIndex;
661      size_t length;      size_t length;
662    
663      /*      /*
664       * Get the count (how many units forward or backward).       * Get the count (how many units forward or backward).
665       */       */
666    
667      p = string+1;      p = string+1;
668      while (isspace(UCHAR(*p))) {      while (isspace(UCHAR(*p))) {
669          p++;          p++;
670      }      }
671      count = strtol(p, &end, 0);      count = strtol(p, &end, 0);
672      if (end == p) {      if (end == p) {
673          return NULL;          return NULL;
674      }      }
675      p = end;      p = end;
676      while (isspace(UCHAR(*p))) {      while (isspace(UCHAR(*p))) {
677          p++;          p++;
678      }      }
679    
680      /*      /*
681       * Find the end of this modifier (next space or + or - character),       * Find the end of this modifier (next space or + or - character),
682       * then parse the unit specifier and update the position       * then parse the unit specifier and update the position
683       * accordingly.       * accordingly.
684       */       */
685    
686      units = p;      units = p;
687      while ((*p != '\0') && !isspace(UCHAR(*p)) && (*p != '+') && (*p != '-')) {      while ((*p != '\0') && !isspace(UCHAR(*p)) && (*p != '+') && (*p != '-')) {
688          p++;          p++;
689      }      }
690      length = p - units;      length = p - units;
691      if ((*units == 'c') && (strncmp(units, "chars", length) == 0)) {      if ((*units == 'c') && (strncmp(units, "chars", length) == 0)) {
692          if (*string == '+') {          if (*string == '+') {
693              TkTextIndexForwChars(indexPtr, count, indexPtr);              TkTextIndexForwChars(indexPtr, count, indexPtr);
694          } else {          } else {
695              TkTextIndexBackChars(indexPtr, count, indexPtr);              TkTextIndexBackChars(indexPtr, count, indexPtr);
696          }          }
697      } else if ((*units == 'l') && (strncmp(units, "lines", length) == 0)) {      } else if ((*units == 'l') && (strncmp(units, "lines", length) == 0)) {
698          lineIndex = TkBTreeLineIndex(indexPtr->linePtr);          lineIndex = TkBTreeLineIndex(indexPtr->linePtr);
699          if (*string == '+') {          if (*string == '+') {
700              lineIndex += count;              lineIndex += count;
701          } else {          } else {
702              lineIndex -= count;              lineIndex -= count;
703    
704              /*              /*
705               * The check below retains the character position, even               * The check below retains the character position, even
706               * if the line runs off the start of the file.  Without               * if the line runs off the start of the file.  Without
707               * it, the character position will get reset to 0 by               * it, the character position will get reset to 0 by
708               * TkTextMakeIndex.               * TkTextMakeIndex.
709               */               */
710    
711              if (lineIndex < 0) {              if (lineIndex < 0) {
712                  lineIndex = 0;                  lineIndex = 0;
713              }              }
714          }          }
715          /*          /*
716           * This doesn't work quite right if using a proportional font or           * This doesn't work quite right if using a proportional font or
717           * UTF-8 characters with varying numbers of bytes.  The cursor will           * UTF-8 characters with varying numbers of bytes.  The cursor will
718           * bop around, keeping a constant number of bytes (not characters)           * bop around, keeping a constant number of bytes (not characters)
719           * from the left edge (but making sure not to split any UTF-8           * from the left edge (but making sure not to split any UTF-8
720           * characters), regardless of the x-position the index corresponds           * characters), regardless of the x-position the index corresponds
721           * to.  The proper way to do this is to get the x-position of the           * to.  The proper way to do this is to get the x-position of the
722           * index and then pick the character at the same x-position in the           * index and then pick the character at the same x-position in the
723           * new line.           * new line.
724           */           */
725    
726          TkTextMakeByteIndex(indexPtr->tree, lineIndex, indexPtr->byteIndex,          TkTextMakeByteIndex(indexPtr->tree, lineIndex, indexPtr->byteIndex,
727                  indexPtr);                  indexPtr);
728      } else {      } else {
729          return NULL;          return NULL;
730      }      }
731      return p;      return p;
732  }  }
733    
734  /*  /*
735   *---------------------------------------------------------------------------   *---------------------------------------------------------------------------
736   *   *
737   * TkTextIndexForwBytes --   * TkTextIndexForwBytes --
738   *   *
739   *      Given an index for a text widget, this procedure creates a new   *      Given an index for a text widget, this procedure creates a new
740   *      index that points "count" bytes ahead of the source index.   *      index that points "count" bytes ahead of the source index.
741   *   *
742   * Results:   * Results:
743   *      *dstPtr is modified to refer to the character "count" bytes after   *      *dstPtr is modified to refer to the character "count" bytes after
744   *      srcPtr, or to the last character in the TkText if there aren't   *      srcPtr, or to the last character in the TkText if there aren't
745   *      "count" bytes left.   *      "count" bytes left.
746   *   *
747   * Side effects:   * Side effects:
748   *      None.   *      None.
749   *   *
750   *---------------------------------------------------------------------------   *---------------------------------------------------------------------------
751   */   */
752    
753  void  void
754  TkTextIndexForwBytes(srcPtr, byteCount, dstPtr)  TkTextIndexForwBytes(srcPtr, byteCount, dstPtr)
755      CONST TkTextIndex *srcPtr;  /* Source index. */      CONST TkTextIndex *srcPtr;  /* Source index. */
756      int byteCount;              /* How many bytes forward to move.  May be      int byteCount;              /* How many bytes forward to move.  May be
757                                   * negative. */                                   * negative. */
758      TkTextIndex *dstPtr;        /* Destination index: gets modified. */      TkTextIndex *dstPtr;        /* Destination index: gets modified. */
759  {  {
760      TkTextLine *linePtr;      TkTextLine *linePtr;
761      TkTextSegment *segPtr;      TkTextSegment *segPtr;
762      int lineLength;      int lineLength;
763    
764      if (byteCount < 0) {      if (byteCount < 0) {
765          TkTextIndexBackBytes(srcPtr, -byteCount, dstPtr);          TkTextIndexBackBytes(srcPtr, -byteCount, dstPtr);
766          return;          return;
767      }      }
768    
769      *dstPtr = *srcPtr;      *dstPtr = *srcPtr;
770      dstPtr->byteIndex += byteCount;      dstPtr->byteIndex += byteCount;
771      while (1) {      while (1) {
772          /*          /*
773           * Compute the length of the current line.           * Compute the length of the current line.
774           */           */
775    
776          lineLength = 0;          lineLength = 0;
777          for (segPtr = dstPtr->linePtr->segPtr; segPtr != NULL;          for (segPtr = dstPtr->linePtr->segPtr; segPtr != NULL;
778                  segPtr = segPtr->nextPtr) {                  segPtr = segPtr->nextPtr) {
779              lineLength += segPtr->size;              lineLength += segPtr->size;
780          }          }
781    
782          /*          /*
783           * If the new index is in the same line then we're done.           * If the new index is in the same line then we're done.
784           * Otherwise go on to the next line.           * Otherwise go on to the next line.
785           */           */
786    
787          if (dstPtr->byteIndex < lineLength) {          if (dstPtr->byteIndex < lineLength) {
788              return;              return;
789          }          }
790          dstPtr->byteIndex -= lineLength;          dstPtr->byteIndex -= lineLength;
791          linePtr = TkBTreeNextLine(dstPtr->linePtr);          linePtr = TkBTreeNextLine(dstPtr->linePtr);
792          if (linePtr == NULL) {          if (linePtr == NULL) {
793              dstPtr->byteIndex = lineLength - 1;              dstPtr->byteIndex = lineLength - 1;
794              return;              return;
795          }          }
796          dstPtr->linePtr = linePtr;          dstPtr->linePtr = linePtr;
797      }      }
798  }  }
799    
800  /*  /*
801   *---------------------------------------------------------------------------   *---------------------------------------------------------------------------
802   *   *
803   * TkTextIndexForwChars --   * TkTextIndexForwChars --
804   *   *
805   *      Given an index for a text widget, this procedure creates a new   *      Given an index for a text widget, this procedure creates a new
806   *      index that points "count" characters ahead of the source index.   *      index that points "count" characters ahead of the source index.
807   *   *
808   * Results:   * Results:
809   *      *dstPtr is modified to refer to the character "count" characters   *      *dstPtr is modified to refer to the character "count" characters
810   *      after srcPtr, or to the last character in the TkText if there   *      after srcPtr, or to the last character in the TkText if there
811   *      aren't "count" characters left in the file.   *      aren't "count" characters left in the file.
812   *   *
813   * Side effects:   * Side effects:
814   *      None.   *      None.
815   *   *
816   *---------------------------------------------------------------------------   *---------------------------------------------------------------------------
817   */   */
818    
819  void  void
820  TkTextIndexForwChars(srcPtr, charCount, dstPtr)  TkTextIndexForwChars(srcPtr, charCount, dstPtr)
821      CONST TkTextIndex *srcPtr;  /* Source index. */      CONST TkTextIndex *srcPtr;  /* Source index. */
822      int charCount;              /* How many characters forward to move.      int charCount;              /* How many characters forward to move.
823                                   * May be negative. */                                   * May be negative. */
824      TkTextIndex *dstPtr;        /* Destination index: gets modified. */      TkTextIndex *dstPtr;        /* Destination index: gets modified. */
825  {  {
826      TkTextLine *linePtr;      TkTextLine *linePtr;
827      TkTextSegment *segPtr;      TkTextSegment *segPtr;
828      int byteOffset;      int byteOffset;
829      char *start, *end, *p;      char *start, *end, *p;
830      Tcl_UniChar ch;      Tcl_UniChar ch;
831    
832      if (charCount < 0) {      if (charCount < 0) {
833          TkTextIndexBackChars(srcPtr, -charCount, dstPtr);          TkTextIndexBackChars(srcPtr, -charCount, dstPtr);
834          return;          return;
835      }      }
836    
837      *dstPtr = *srcPtr;      *dstPtr = *srcPtr;
838    
839      /*      /*
840       * Find seg that contains src byteIndex.       * Find seg that contains src byteIndex.
841       * Move forward specified number of chars.       * Move forward specified number of chars.
842       */       */
843    
844      segPtr = TkTextIndexToSeg(dstPtr, &byteOffset);      segPtr = TkTextIndexToSeg(dstPtr, &byteOffset);
845      while (1) {      while (1) {
846          /*          /*
847           * Go through each segment in line looking for specified character           * Go through each segment in line looking for specified character
848           * index.           * index.
849           */           */
850    
851          for ( ; segPtr != NULL; segPtr = segPtr->nextPtr) {          for ( ; segPtr != NULL; segPtr = segPtr->nextPtr) {
852              if (segPtr->typePtr == &tkTextCharType) {              if (segPtr->typePtr == &tkTextCharType) {
853                  start = segPtr->body.chars + byteOffset;                  start = segPtr->body.chars + byteOffset;
854                  end = segPtr->body.chars + segPtr->size;                  end = segPtr->body.chars + segPtr->size;
855                  for (p = start; p < end; p += Tcl_UtfToUniChar(p, &ch)) {                  for (p = start; p < end; p += Tcl_UtfToUniChar(p, &ch)) {
856                      if (charCount == 0) {                      if (charCount == 0) {
857                          dstPtr->byteIndex += (p - start);                          dstPtr->byteIndex += (p - start);
858                          return;                          return;
859                      }                      }
860                      charCount--;                      charCount--;
861                  }                  }
862              } else {              } else {
863                  if (charCount < segPtr->size - byteOffset) {                  if (charCount < segPtr->size - byteOffset) {
864                      dstPtr->byteIndex += charCount;                      dstPtr->byteIndex += charCount;
865                      return;                      return;
866                  }                  }
867                  charCount -= segPtr->size - byteOffset;                  charCount -= segPtr->size - byteOffset;
868              }              }
869              dstPtr->byteIndex += segPtr->size - byteOffset;              dstPtr->byteIndex += segPtr->size - byteOffset;
870              byteOffset = 0;              byteOffset = 0;
871          }          }
872    
873          /*          /*
874           * Go to the next line.  If we are at the end of the text item,           * Go to the next line.  If we are at the end of the text item,
875           * back up one byte (for the terminal '\n' character) and return           * back up one byte (for the terminal '\n' character) and return
876           * that index.           * that index.
877           */           */
878                    
879          linePtr = TkBTreeNextLine(dstPtr->linePtr);          linePtr = TkBTreeNextLine(dstPtr->linePtr);
880          if (linePtr == NULL) {          if (linePtr == NULL) {
881              dstPtr->byteIndex -= sizeof(char);              dstPtr->byteIndex -= sizeof(char);
882              return;              return;
883          }          }
884          dstPtr->linePtr = linePtr;          dstPtr->linePtr = linePtr;
885          dstPtr->byteIndex = 0;          dstPtr->byteIndex = 0;
886          segPtr = dstPtr->linePtr->segPtr;          segPtr = dstPtr->linePtr->segPtr;
887      }      }
888  }  }
889    
890  /*  /*
891   *---------------------------------------------------------------------------   *---------------------------------------------------------------------------
892   *   *
893   * TkTextIndexBackBytes --   * TkTextIndexBackBytes --
894   *   *
895   *      Given an index for a text widget, this procedure creates a new   *      Given an index for a text widget, this procedure creates a new
896   *      index that points "count" bytes earlier than the source index.   *      index that points "count" bytes earlier than the source index.
897   *   *
898   * Results:   * Results:
899   *      *dstPtr is modified to refer to the character "count" bytes before   *      *dstPtr is modified to refer to the character "count" bytes before
900   *      srcPtr, or to the first character in the TkText if there aren't   *      srcPtr, or to the first character in the TkText if there aren't
901   *      "count" bytes earlier than srcPtr.   *      "count" bytes earlier than srcPtr.
902   *   *
903   * Side effects:   * Side effects:
904   *      None.   *      None.
905   *   *
906   *---------------------------------------------------------------------------   *---------------------------------------------------------------------------
907   */   */
908    
909  void  void
910  TkTextIndexBackBytes(srcPtr, byteCount, dstPtr)  TkTextIndexBackBytes(srcPtr, byteCount, dstPtr)
911      CONST TkTextIndex *srcPtr;  /* Source index. */      CONST TkTextIndex *srcPtr;  /* Source index. */
912      int byteCount;              /* How many bytes backward to move.  May be      int byteCount;              /* How many bytes backward to move.  May be
913                                   * negative. */                                   * negative. */
914      TkTextIndex *dstPtr;        /* Destination index: gets modified. */      TkTextIndex *dstPtr;        /* Destination index: gets modified. */
915  {  {
916      TkTextSegment *segPtr;      TkTextSegment *segPtr;
917      int lineIndex;      int lineIndex;
918    
919      if (byteCount < 0) {      if (byteCount < 0) {
920          TkTextIndexForwBytes(srcPtr, -byteCount, dstPtr);          TkTextIndexForwBytes(srcPtr, -byteCount, dstPtr);
921          return;          return;
922      }      }
923    
924      *dstPtr = *srcPtr;      *dstPtr = *srcPtr;
925      dstPtr->byteIndex -= byteCount;      dstPtr->byteIndex -= byteCount;
926      lineIndex = -1;      lineIndex = -1;
927      while (dstPtr->byteIndex < 0) {      while (dstPtr->byteIndex < 0) {
928          /*          /*
929           * Move back one line in the text.  If we run off the beginning           * Move back one line in the text.  If we run off the beginning
930           * of the file then just return the first character in the text.           * of the file then just return the first character in the text.
931           */           */
932    
933          if (lineIndex < 0) {          if (lineIndex < 0) {
934              lineIndex = TkBTreeLineIndex(dstPtr->linePtr);              lineIndex = TkBTreeLineIndex(dstPtr->linePtr);
935          }          }
936          if (lineIndex == 0) {          if (lineIndex == 0) {
937              dstPtr->byteIndex = 0;              dstPtr->byteIndex = 0;
938              return;              return;
939          }          }
940          lineIndex--;          lineIndex--;
941          dstPtr->linePtr = TkBTreeFindLine(dstPtr->tree, lineIndex);          dstPtr->linePtr = TkBTreeFindLine(dstPtr->tree, lineIndex);
942    
943          /*          /*
944           * Compute the length of the line and add that to dstPtr->charIndex.           * Compute the length of the line and add that to dstPtr->charIndex.
945           */           */
946    
947          for (segPtr = dstPtr->linePtr->segPtr; segPtr != NULL;          for (segPtr = dstPtr->linePtr->segPtr; segPtr != NULL;
948                  segPtr = segPtr->nextPtr) {                  segPtr = segPtr->nextPtr) {
949              dstPtr->byteIndex += segPtr->size;              dstPtr->byteIndex += segPtr->size;
950          }          }
951      }      }
952  }  }
953    
954  /*  /*
955   *---------------------------------------------------------------------------   *---------------------------------------------------------------------------
956   *   *
957   * TkTextIndexBackChars --   * TkTextIndexBackChars --
958   *   *
959   *      Given an index for a text widget, this procedure creates a new   *      Given an index for a text widget, this procedure creates a new
960   *      index that points "count" characters earlier than the source index.   *      index that points "count" characters earlier than the source index.
961   *   *
962   * Results:   * Results:
963   *      *dstPtr is modified to refer to the character "count" characters   *      *dstPtr is modified to refer to the character "count" characters
964   *      before srcPtr, or to the first character in the file if there   *      before srcPtr, or to the first character in the file if there
965   *      aren't "count" characters earlier than srcPtr.   *      aren't "count" characters earlier than srcPtr.
966   *   *
967   * Side effects:   * Side effects:
968   *      None.   *      None.
969   *   *
970   *---------------------------------------------------------------------------   *---------------------------------------------------------------------------
971   */   */
972    
973  void  void
974  TkTextIndexBackChars(srcPtr, charCount, dstPtr)  TkTextIndexBackChars(srcPtr, charCount, dstPtr)
975      CONST TkTextIndex *srcPtr;  /* Source index. */      CONST TkTextIndex *srcPtr;  /* Source index. */
976      int charCount;              /* How many characters backward to move.      int charCount;              /* How many characters backward to move.
977                                   * May be negative. */                                   * May be negative. */
978      TkTextIndex *dstPtr;        /* Destination index: gets modified. */      TkTextIndex *dstPtr;        /* Destination index: gets modified. */
979  {  {
980      TkTextSegment *segPtr, *oldPtr;      TkTextSegment *segPtr, *oldPtr;
981      int lineIndex, segSize;      int lineIndex, segSize;
982      char *p, *start, *end;      char *p, *start, *end;
983    
984      if (charCount <= 0) {      if (charCount <= 0) {
985          TkTextIndexForwChars(srcPtr, -charCount, dstPtr);          TkTextIndexForwChars(srcPtr, -charCount, dstPtr);
986          return;          return;
987      }      }
988    
989      *dstPtr = *srcPtr;      *dstPtr = *srcPtr;
990    
991      /*      /*
992       * Find offset within seg that contains byteIndex.       * Find offset within seg that contains byteIndex.
993       * Move backward specified number of chars.       * Move backward specified number of chars.
994       */       */
995    
996      lineIndex = -1;      lineIndex = -1;
997            
998      segSize = dstPtr->byteIndex;      segSize = dstPtr->byteIndex;
999      for (segPtr = dstPtr->linePtr->segPtr; ; segPtr = segPtr->nextPtr) {      for (segPtr = dstPtr->linePtr->segPtr; ; segPtr = segPtr->nextPtr) {
1000          if (segSize <= segPtr->size) {          if (segSize <= segPtr->size) {
1001              break;              break;
1002          }          }
1003          segSize -= segPtr->size;          segSize -= segPtr->size;
1004      }      }
1005      while (1) {      while (1) {
1006          if (segPtr->typePtr == &tkTextCharType) {          if (segPtr->typePtr == &tkTextCharType) {
1007              start = segPtr->body.chars;              start = segPtr->body.chars;
1008              end = segPtr->body.chars + segSize;              end = segPtr->body.chars + segSize;
1009              for (p = end; ; p = Tcl_UtfPrev(p, start)) {              for (p = end; ; p = Tcl_UtfPrev(p, start)) {
1010                  if (charCount == 0) {                  if (charCount == 0) {
1011                      dstPtr->byteIndex -= (end - p);                      dstPtr->byteIndex -= (end - p);
1012                      return;                      return;
1013                  }                  }
1014                  if (p == start) {                  if (p == start) {
1015                      break;                      break;
1016                  }                  }
1017                  charCount--;                  charCount--;
1018              }              }
1019          } else {          } else {
1020              if (charCount <= segSize) {              if (charCount <= segSize) {
1021                  dstPtr->byteIndex -= charCount;                  dstPtr->byteIndex -= charCount;
1022                  return;                  return;
1023              }              }
1024              charCount -= segSize;              charCount -= segSize;
1025          }          }
1026          dstPtr->byteIndex -= segSize;          dstPtr->byteIndex -= segSize;
1027    
1028          /*          /*
1029           * Move back into previous segment.           * Move back into previous segment.
1030           */           */
1031    
1032          oldPtr = segPtr;          oldPtr = segPtr;
1033          segPtr = dstPtr->linePtr->segPtr;          segPtr = dstPtr->linePtr->segPtr;
1034          if (segPtr != oldPtr) {          if (segPtr != oldPtr) {
1035              for ( ; segPtr->nextPtr != oldPtr; segPtr = segPtr->nextPtr) {              for ( ; segPtr->nextPtr != oldPtr; segPtr = segPtr->nextPtr) {
1036                  /* Empty body. */                  /* Empty body. */
1037              }              }
1038              segSize = segPtr->size;              segSize = segPtr->size;
1039              continue;              continue;
1040          }          }
1041    
1042          /*          /*
1043           * Move back to previous line.           * Move back to previous line.
1044           */           */
1045    
1046          if (lineIndex < 0) {          if (lineIndex < 0) {
1047              lineIndex = TkBTreeLineIndex(dstPtr->linePtr);              lineIndex = TkBTreeLineIndex(dstPtr->linePtr);
1048          }          }
1049          if (lineIndex == 0) {          if (lineIndex == 0) {
1050              dstPtr->byteIndex = 0;              dstPtr->byteIndex = 0;
1051              return;              return;
1052          }          }
1053          lineIndex--;          lineIndex--;
1054          dstPtr->linePtr = TkBTreeFindLine(dstPtr->tree, lineIndex);          dstPtr->linePtr = TkBTreeFindLine(dstPtr->tree, lineIndex);
1055    
1056          /*          /*
1057           * Compute the length of the line and add that to dstPtr->byteIndex.           * Compute the length of the line and add that to dstPtr->byteIndex.
1058           */           */
1059    
1060          oldPtr = dstPtr->linePtr->segPtr;          oldPtr = dstPtr->linePtr->segPtr;
1061          for (segPtr = oldPtr; segPtr != NULL; segPtr = segPtr->nextPtr) {          for (segPtr = oldPtr; segPtr != NULL; segPtr = segPtr->nextPtr) {
1062              dstPtr->byteIndex += segPtr->size;              dstPtr->byteIndex += segPtr->size;
1063              oldPtr = segPtr;              oldPtr = segPtr;
1064          }          }
1065          segPtr = oldPtr;          segPtr = oldPtr;
1066          segSize = segPtr->size;          segSize = segPtr->size;
1067      }      }
1068  }  }
1069    
1070  /*  /*
1071   *----------------------------------------------------------------------   *----------------------------------------------------------------------
1072   *   *
1073   * StartEnd --   * StartEnd --
1074   *   *
1075   *      This procedure handles modifiers like "wordstart" and "lineend"   *      This procedure handles modifiers like "wordstart" and "lineend"
1076   *      to adjust indices forwards or backwards.   *      to adjust indices forwards or backwards.
1077   *   *
1078   * Results:   * Results:
1079   *      If the modifier is successfully parsed then the return value   *      If the modifier is successfully parsed then the return value
1080   *      is the address of the first character after the modifier, and   *      is the address of the first character after the modifier, and
1081   *      *indexPtr is updated to reflect the modifier. If there is a   *      *indexPtr is updated to reflect the modifier. If there is a
1082   *      syntax error in the modifier then NULL is returned.   *      syntax error in the modifier then NULL is returned.
1083   *   *
1084   * Side effects:   * Side effects:
1085   *      None.   *      None.
1086   *   *
1087   *----------------------------------------------------------------------   *----------------------------------------------------------------------
1088   */   */
1089    
1090  static char *  static char *
1091  StartEnd(string, indexPtr)  StartEnd(string, indexPtr)
1092      char *string;               /* String to parse for additional info      char *string;               /* String to parse for additional info
1093                                   * about modifier (count and units).                                   * about modifier (count and units).
1094                                   * Points to first character of modifer                                   * Points to first character of modifer
1095                                   * word. */                                   * word. */
1096      TkTextIndex *indexPtr;      /* Index to mdoify based on string. */      TkTextIndex *indexPtr;      /* Index to mdoify based on string. */
1097  {  {
1098      char *p;      char *p;
1099      int c, offset;      int c, offset;
1100      size_t length;      size_t length;
1101      register TkTextSegment *segPtr;      register TkTextSegment *segPtr;
1102    
1103      /*      /*
1104       * Find the end of the modifier word.       * Find the end of the modifier word.
1105       */       */
1106    
1107      for (p = string; isalnum(UCHAR(*p)); p++) {      for (p = string; isalnum(UCHAR(*p)); p++) {
1108          /* Empty loop body. */          /* Empty loop body. */
1109      }      }
1110      length = p-string;      length = p-string;
1111      if ((*string == 'l') && (strncmp(string, "lineend", length) == 0)      if ((*string == 'l') && (strncmp(string, "lineend", length) == 0)
1112              && (length >= 5)) {              && (length >= 5)) {
1113          indexPtr->byteIndex = 0;          indexPtr->byteIndex = 0;
1114          for (segPtr = indexPtr->linePtr->segPtr; segPtr != NULL;          for (segPtr = indexPtr->linePtr->segPtr; segPtr != NULL;
1115                  segPtr = segPtr->nextPtr) {                  segPtr = segPtr->nextPtr) {
1116              indexPtr->byteIndex += segPtr->size;              indexPtr->byteIndex += segPtr->size;
1117          }          }
1118          indexPtr->byteIndex -= sizeof(char);          indexPtr->byteIndex -= sizeof(char);
1119      } else if ((*string == 'l') && (strncmp(string, "linestart", length) == 0)      } else if ((*string == 'l') && (strncmp(string, "linestart", length) == 0)
1120              && (length >= 5)) {              && (length >= 5)) {
1121          indexPtr->byteIndex = 0;          indexPtr->byteIndex = 0;
1122      } else if ((*string == 'w') && (strncmp(string, "wordend", length) == 0)      } else if ((*string == 'w') && (strncmp(string, "wordend", length) == 0)
1123              && (length >= 5)) {              && (length >= 5)) {
1124          int firstChar = 1;          int firstChar = 1;
1125    
1126          /*          /*
1127           * If the current character isn't part of a word then just move           * If the current character isn't part of a word then just move
1128           * forward one character.  Otherwise move forward until finding           * forward one character.  Otherwise move forward until finding
1129           * a character that isn't part of a word and stop there.           * a character that isn't part of a word and stop there.
1130           */           */
1131    
1132          segPtr = TkTextIndexToSeg(indexPtr, &offset);          segPtr = TkTextIndexToSeg(indexPtr, &offset);
1133          while (1) {          while (1) {
1134              if (segPtr->typePtr == &tkTextCharType) {              if (segPtr->typePtr == &tkTextCharType) {
1135                  c = segPtr->body.chars[offset];                  c = segPtr->body.chars[offset];
1136                  if (!isalnum(UCHAR(c)) && (c != '_')) {                  if (!isalnum(UCHAR(c)) && (c != '_')) {
1137                      break;                      break;
1138                  }                  }
1139                  firstChar = 0;                  firstChar = 0;
1140              }              }
1141              offset += 1;              offset += 1;
1142              indexPtr->byteIndex += sizeof(char);              indexPtr->byteIndex += sizeof(char);
1143              if (offset >= segPtr->size) {              if (offset >= segPtr->size) {
1144                  segPtr = TkTextIndexToSeg(indexPtr, &offset);                  segPtr = TkTextIndexToSeg(indexPtr, &offset);
1145              }              }
1146          }          }
1147          if (firstChar) {          if (firstChar) {
1148              TkTextIndexForwChars(indexPtr, 1, indexPtr);              TkTextIndexForwChars(indexPtr, 1, indexPtr);
1149          }          }
1150      } else if ((*string == 'w') && (strncmp(string, "wordstart", length) == 0)      } else if ((*string == 'w') && (strncmp(string, "wordstart", length) == 0)
1151              && (length >= 5)) {              && (length >= 5)) {
1152          int firstChar = 1;          int firstChar = 1;
1153    
1154          /*          /*
1155           * Starting with the current character, look for one that's not           * Starting with the current character, look for one that's not
1156           * part of a word and keep moving backward until you find one.           * part of a word and keep moving backward until you find one.
1157           * Then if the character found wasn't the first one, move forward           * Then if the character found wasn't the first one, move forward
1158           * again one position.           * again one position.
1159           */           */
1160    
1161          segPtr = TkTextIndexToSeg(indexPtr, &offset);          segPtr = TkTextIndexToSeg(indexPtr, &offset);
1162          while (1) {          while (1) {
1163              if (segPtr->typePtr == &tkTextCharType) {              if (segPtr->typePtr == &tkTextCharType) {
1164                  c = segPtr->body.chars[offset];                  c = segPtr->body.chars[offset];
1165                  if (!isalnum(UCHAR(c)) && (c != '_')) {                  if (!isalnum(UCHAR(c)) && (c != '_')) {
1166                      break;                      break;
1167                  }                  }
1168                  firstChar = 0;                  firstChar = 0;
1169              }              }
1170              offset -= 1;              offset -= 1;
1171              indexPtr->byteIndex -= sizeof(char);              indexPtr->byteIndex -= sizeof(char);
1172              if (offset < 0) {              if (offset < 0) {
1173                  if (indexPtr->byteIndex < 0) {                  if (indexPtr->byteIndex < 0) {
1174                      indexPtr->byteIndex = 0;                      indexPtr->byteIndex = 0;
1175                      goto done;                      goto done;
1176                  }                  }
1177                  segPtr = TkTextIndexToSeg(indexPtr, &offset);                  segPtr = TkTextIndexToSeg(indexPtr, &offset);
1178              }              }
1179          }          }
1180          if (!firstChar) {          if (!firstChar) {
1181              TkTextIndexForwChars(indexPtr, 1, indexPtr);              TkTextIndexForwChars(indexPtr, 1, indexPtr);
1182          }          }
1183      } else {      } else {
1184          return NULL;          return NULL;
1185      }      }
1186      done:      done:
1187      return p;      return p;
1188  }  }
1189    
1190  /* End of tktextindex.c */  /* End of tktextindex.c */

Legend:
Removed from v.70  
changed lines
  Added in v.71

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25