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

Annotation of /projs/trunk/shared_source/c_tk_base_7_5_w_mods/tktextindex.c

Parent Directory Parent Directory | Revision Log Revision Log


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

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25