]>
git.zerfleddert.de Git - micropolis/blob - src/tk/tktxidx.c
4 * This module provides procedures that manipulate indices for
7 * Copyright 1992 Regents of the University of California.
8 * Permission to use, copy, modify, and distribute this
9 * software and its documentation for any purpose and without
10 * fee is hereby granted, provided that the above copyright
11 * notice appear in all copies. The University of California
12 * makes no representations about the suitability of this
13 * software for any purpose. It is provided "as is" without
14 * express or implied warranty.
18 static char rcsid
[] = "$Header: /user6/ouster/wish/RCS/tkTextIndex.c,v 1.2 92/07/16 16:32:26 ouster Exp $ SPRITE (Berkeley)";
27 * Forward declarations for procedures defined later in this file:
30 static void BackwardChars
_ANSI_ARGS_((TkText
*textPtr
,
31 TkTextLine
*linePtr
, int *lineIndexPtr
,
32 int *chPtr
, int count
));
33 static char * ForwBack
_ANSI_ARGS_((TkText
*textPtr
,
34 char *string
, int *lineIndexPtr
, int *chPtr
));
35 static void ForwardChars
_ANSI_ARGS_((TkText
*textPtr
,
36 TkTextLine
*linePtr
, int *lineIndexPtr
,
37 int *chPtr
, int count
));
38 static char * StartEnd
_ANSI_ARGS_((TkText
*textPtr
,
39 char *string
, int *lineIndexPtr
, int *chPtr
));
42 *----------------------------------------------------------------------
46 * Given a string, return the line and character indices that
50 * The return value is a standard Tcl return result. If
51 * TCL_OK is returned, then everything went well and information
52 * is stored at *lineIndexPtr and *chPtr; otherwise TCL_ERROR
53 * is returned and an error message is left in interp->result.
58 *----------------------------------------------------------------------
62 TkTextGetIndex(interp
, textPtr
, string
, lineIndexPtr
, chPtr
)
63 Tcl_Interp
*interp
; /* Use this for error reporting. */
64 TkText
*textPtr
; /* Information about text widget. */
65 char *string
; /* Textual description of position. */
66 int *lineIndexPtr
; /* Store line number here. */
67 int *chPtr
; /* Store character position here. */
70 char *end
, *endOfBase
;
73 TkAnnotation
*markPtr
;
80 *------------------------------------------------
81 * Stage 1: parse the base index.
82 *------------------------------------------------
85 if (string
[0] == '@') {
87 * Find character at a given x,y location in the window.
93 x
= strtol(p
, &end
, 0);
94 if ((end
== p
) || (*end
!= ',')) {
98 y
= strtol(p
, &end
, 0);
102 *lineIndexPtr
= TkBTreeLineIndex(TkTextCharAtLoc(textPtr
, x
,
106 } else if (isdigit(string
[0]) || (string
[0] == '-')) {
108 * Base is identified with line and character indices.
111 *lineIndexPtr
= strtol(string
, &end
, 0) - 1;
112 if ((end
== string
) || (*end
!= '.')) {
116 if ((*p
== 'e') && (strncmp(p
, "end", 3) == 0)) {
117 linePtr
= TkBTreeFindLine(textPtr
->tree
, *lineIndexPtr
);
118 if (linePtr
== NULL
) {
119 Tcl_AppendResult(interp
, "bad text index \"", string
,
120 "\": no such line in text", (char *) NULL
);
123 *chPtr
= linePtr
->numBytes
- 1;
127 *chPtr
= strtol(p
, &end
, 0);
136 for (p
= string
; *p
!= 0; p
++) {
137 if (isspace(*p
) || (*p
== '+') || (*p
== '-')) {
142 if ((string
[0] == 'e')
143 && (strncmp(string
, "end", endOfBase
-string
) == 0)) {
145 * Base position is end of text.
148 *lineIndexPtr
= TkBTreeNumLines(textPtr
->tree
) - 1;
149 linePtr
= TkBTreeFindLine(textPtr
->tree
, *lineIndexPtr
);
150 *chPtr
= linePtr
->numBytes
- 1;
154 * See if the base position is the name of a mark.
159 hPtr
= Tcl_FindHashEntry(&textPtr
->markTable
, string
);
162 markPtr
= (TkAnnotation
*) Tcl_GetHashValue(hPtr
);
163 *lineIndexPtr
= TkBTreeLineIndex(markPtr
->linePtr
);
164 *chPtr
= markPtr
->ch
;
170 * Nothing has worked so far. See if the base has the form
171 * "tag.first" or "tag.last" where "tag" is the name of a valid
175 p
= strchr(string
, '.');
179 if ((p
[1] == 'f') && (endOfBase
== (p
+6))
180 && (strncmp(p
+1, "first", endOfBase
- (p
+1)) == 0)) {
182 } else if ((p
[1] == 'l') && (endOfBase
== (p
+5))
183 && (strncmp(p
+1, "last", endOfBase
- (p
+1)) == 0)) {
189 hPtr
= Tcl_FindHashEntry(&textPtr
->tagTable
, string
);
194 tagPtr
= (TkTextTag
*) Tcl_GetHashValue(hPtr
);
195 TkBTreeStartSearch(textPtr
->tree
, 0, 0, TkBTreeNumLines(textPtr
->tree
),
197 if (!TkBTreeNextTag(&search
)) {
198 Tcl_AppendResult(interp
,
199 "text doesn't contain any characters tagged with \"",
200 Tcl_GetHashKey(&textPtr
->tagTable
, hPtr
), "\"", (char *) NULL
);
204 *lineIndexPtr
= search
.line1
;
207 while (TkBTreeNextTag(&search
)) {
208 *lineIndexPtr
= search
.line1
;
214 *-------------------------------------------------------------------
215 * Stage 2: process zero or more modifiers. Each modifier is either
216 * a keyword like "wordend" or "linestart", or it has the form
217 * "op count units" where op is + or -, count is a number, and units
218 * is "chars" or "lines".
219 *-------------------------------------------------------------------
225 while (isspace(*p
)) {
232 if ((*p
== '+') || (*p
== '-')) {
233 p
= ForwBack(textPtr
, p
, lineIndexPtr
, chPtr
);
235 p
= StartEnd(textPtr
, p
, lineIndexPtr
, chPtr
);
243 Tcl_AppendResult(interp
, "bad text index \"", string
, "\"",
249 *----------------------------------------------------------------------
251 * TkTextPrintIndex --
253 * Given a line number and a character index, this procedure
254 * generates a string description of the position, which is
255 * suitable for reading in again later.
258 * The characters pointed to by string are modified.
263 *----------------------------------------------------------------------
267 TkTextPrintIndex(line
, ch
, string
)
268 int line
; /* Line number. */
269 int ch
; /* Character position within line. */
270 char *string
; /* Place to store the position. Must have
271 * at least POS_CHARS characters. */
273 sprintf(string
, "%d.%d", line
+1, ch
);
277 *----------------------------------------------------------------------
279 * TkTextRoundIndex --
281 * Given a line index and a character index, this procedure
282 * adjusts those positions if necessary to correspond to the
283 * nearest actual character within the text.
286 * The return value is a pointer to the line structure for
287 * the line of the text's B-tree that contains the indicated
288 * character. In addition, *lineIndexPtr and *chPtr are
289 * modified if necessary to refer to an existing character
295 *----------------------------------------------------------------------
300 TkTextRoundIndex(textPtr
, lineIndexPtr
, chPtr
)
301 TkText
*textPtr
; /* Information about text widget. */
302 int *lineIndexPtr
; /* Points to initial line index,
303 * which is overwritten with actual
305 int *chPtr
; /* Points to initial character index,
306 * which is overwritten with actual
307 * character index. */
309 int line
, ch
, lastLine
;
312 line
= *lineIndexPtr
;
318 lastLine
= TkBTreeNumLines(textPtr
->tree
) - 1;
319 if (line
> lastLine
) {
321 linePtr
= TkBTreeFindLine(textPtr
->tree
, line
);
322 ch
= linePtr
->numBytes
- 1;
324 linePtr
= TkBTreeFindLine(textPtr
->tree
, line
);
328 if (ch
>= linePtr
->numBytes
) {
329 if (line
== lastLine
) {
330 ch
= linePtr
->numBytes
- 1;
333 linePtr
= TkBTreeNextLine(linePtr
);
338 *lineIndexPtr
= line
;
344 *----------------------------------------------------------------------
348 * This procedure handles +/- modifiers for indices to adjust
349 * the index forwards or backwards.
352 * If the modifier is successfully parsed then the return value
353 * is the address of the first character after the modifier, and
354 * *lineIndexPtr and *chPtr are updated to reflect the modifier.
355 * If there is a syntax error in the modifier then NULL is returned.
360 *----------------------------------------------------------------------
364 ForwBack(textPtr
, string
, lineIndexPtr
, chPtr
)
365 TkText
*textPtr
; /* Information about widget that index
367 char *string
; /* String to parse for additional info
368 * about modifier (count and units).
369 * Points to "+" or "-" that starts
371 int *lineIndexPtr
; /* Points to current line index, which will
372 * be updated to reflect modifier. */
373 int *chPtr
; /* Points to current character index, which
374 * will be updated to reflect modifier. */
378 int count
, length
, lastLine
;
382 * Get the count (how many units forward or backward).
386 while (isspace(*p
)) {
389 count
= strtoul(p
, &end
, 0);
394 while (isspace(*p
)) {
399 * Find the end of this modifier (next space or + or - character),
400 * then parse the unit specifier and update the position
405 while ((*p
!= 0) && !isspace(*p
) && (*p
!= '+') && (*p
!= '-')) {
409 if ((*units
== 'c') && (strncmp(units
, "chars", length
) == 0)) {
410 linePtr
= TkTextRoundIndex(textPtr
, lineIndexPtr
, chPtr
);
411 if (*string
== '+') {
412 ForwardChars(textPtr
, linePtr
, lineIndexPtr
, chPtr
, count
);
414 BackwardChars(textPtr
, linePtr
, lineIndexPtr
, chPtr
, count
);
416 } else if ((*units
== 'l') && (strncmp(units
, "lines", length
) == 0)) {
417 if (*string
== '+') {
418 *lineIndexPtr
+= count
;
419 lastLine
= TkBTreeNumLines(textPtr
->tree
) - 1;
420 if (*lineIndexPtr
> lastLine
) {
421 *lineIndexPtr
= lastLine
;
424 *lineIndexPtr
-= count
;
425 if (*lineIndexPtr
< 0) {
429 linePtr
= TkBTreeFindLine(textPtr
->tree
, *lineIndexPtr
);
430 if (*chPtr
>= linePtr
->numBytes
) {
431 *chPtr
= linePtr
->numBytes
- 1;
443 *----------------------------------------------------------------------
447 * Given a position in a text widget, this procedure computes
448 * a new position that is "count" characters ahead of the given
452 * *LineIndexPtr and *chPtr are overwritten with new values
453 * corresponding to the new position.
458 *----------------------------------------------------------------------
463 ForwardChars(textPtr
, linePtr
, lineIndexPtr
, chPtr
, count
)
464 TkText
*textPtr
; /* Information about text widget. */
465 register TkTextLine
*linePtr
; /* Text line corresponding to
467 int *lineIndexPtr
; /* Points to initial line index,
468 * which is overwritten with final
470 int *chPtr
; /* Points to initial character index,
471 * which is overwritten with final
472 * character index. */
473 int count
; /* How many characters forward to
474 * move. Must not be negative. */
480 bytesInLine
= linePtr
->numBytes
- *chPtr
;
481 if (bytesInLine
> count
) {
485 nextPtr
= TkBTreeNextLine(linePtr
);
486 if (nextPtr
== NULL
) {
487 *chPtr
= linePtr
->numBytes
- 1;
493 count
-= bytesInLine
;
498 *----------------------------------------------------------------------
502 * Given a position in a text widget, this procedure computes
503 * a new position that is "count" characters earlier than the given
507 * *LineIndexPtr and *chPtr are overwritten with new values
508 * corresponding to the new position.
513 *----------------------------------------------------------------------
517 BackwardChars(textPtr
, linePtr
, lineIndexPtr
, chPtr
, count
)
518 TkText
*textPtr
; /* Information about text widget. */
519 register TkTextLine
*linePtr
; /* Text line corresponding to
521 int *lineIndexPtr
; /* Points to initial line index,
522 * which is overwritten with final
524 int *chPtr
; /* Points to initial character index,
525 * which is overwritten with final
526 * character index. */
527 int count
; /* How many characters backward to
528 * move. Must not be negative. */
533 bytesInLine
= *chPtr
;
534 if (bytesInLine
>= count
) {
538 if (*lineIndexPtr
<= 0) {
543 linePtr
= TkBTreeFindLine(textPtr
->tree
, *lineIndexPtr
);
544 count
-= bytesInLine
;
545 *chPtr
= linePtr
->numBytes
;
550 *----------------------------------------------------------------------
554 * This procedure handles modifiers like "wordstart" and "lineend"
555 * to adjust indices forwards or backwards.
558 * If the modifier is successfully parsed then the return value
559 * is the address of the first character after the modifier, and
560 * *lineIndexPtr and *chPtr are updated to reflect the modifier.
561 * If there is a syntax error in the modifier then NULL is returned.
566 *----------------------------------------------------------------------
570 StartEnd(textPtr
, string
, lineIndexPtr
, chPtr
)
571 TkText
*textPtr
; /* Information about widget that index
573 char *string
; /* String to parse for additional info
574 * about modifier (count and units).
575 * Points to first character of modifer
577 int *lineIndexPtr
; /* Points to current line index, which will
578 * be updated to reflect modifier. */
579 int *chPtr
; /* Points to current character index, which
580 * will be updated to reflect modifier. */
584 register TkTextLine
*linePtr
;
587 * Find the end of the modifier word.
590 for (p
= string
; isalnum(*p
); p
++) {
591 /* Empty loop body. */
594 linePtr
= TkTextRoundIndex(textPtr
, lineIndexPtr
, chPtr
);
595 if ((*string
== 'l') && (strncmp(string
, "lineend", length
) == 0)
597 *chPtr
= linePtr
->numBytes
- 1;
598 } else if ((*string
== 'l') && (strncmp(string
, "linestart", length
) == 0)
601 } else if ((*string
== 'w') && (strncmp(string
, "wordend", length
) == 0)
603 c
= linePtr
->bytes
[*chPtr
];
604 if (!isalnum(c
) && (c
!= '_')) {
605 if (*chPtr
>= (linePtr
->numBytes
- 1)) {
607 * End of line: go to start of next line unless this is the
608 * last line in the text.
611 if (TkBTreeNextLine(linePtr
) != NULL
) {
621 c
= linePtr
->bytes
[*chPtr
];
622 } while (isalnum(c
) || (c
== '_'));
624 } else if ((*string
== 'w') && (strncmp(string
, "wordstart", length
) == 0)
626 c
= linePtr
->bytes
[*chPtr
];
627 if (isalnum(c
) || (c
== '_')) {
629 c
= linePtr
->bytes
[(*chPtr
) - 1];
630 if (!isalnum(c
) && (c
!= '_')) {