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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 71 - (show annotations) (download)
Sat Nov 5 11:07:06 2016 UTC (7 years, 11 months ago) by dashley
File MIME type: text/plain
File size: 30510 byte(s)
Set EOL properties appropriately to facilitate simultaneous Linux and Windows development.
1 /* $Header$ */
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 /* End of tktextindex.c */

Properties

Name Value
svn:eol-style native
svn:keywords Header

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25