]>
cvs.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 *----------------------------------------------------------------------
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. */
71 char *end
, *endOfBase
;
74 TkAnnotation
*markPtr
;
81 *------------------------------------------------
82 * Stage 1: parse the base index.
83 *------------------------------------------------
86 if (string
[0] == '@') {
88 * Find character at a given x,y location in the window.
94 x
= strtol(p
, &end
, 0);
95 if ((end
== p
) || (*end
!= ',')) {
99 y
= strtol(p
, &end
, 0);
103 *lineIndexPtr
= TkBTreeLineIndex(TkTextCharAtLoc(textPtr
, x
,
107 } else if (isdigit(string
[0]) || (string
[0] == '-')) {
109 * Base is identified with line and character indices.
112 *lineIndexPtr
= strtol(string
, &end
, 0) - 1;
113 if ((end
== string
) || (*end
!= '.')) {
117 if ((*p
== 'e') && (strncmp(p
, "end", 3) == 0)) {
118 linePtr
= TkBTreeFindLine(textPtr
->tree
, *lineIndexPtr
);
119 if (linePtr
== NULL
) {
120 Tcl_AppendResult(interp
, "bad text index \"", string
,
121 "\": no such line in text", (char *) NULL
);
124 *chPtr
= linePtr
->numBytes
- 1;
128 *chPtr
= strtol(p
, &end
, 0);
137 for (p
= string
; *p
!= 0; p
++) {
138 if (isspace(*p
) || (*p
== '+') || (*p
== '-')) {
143 if ((string
[0] == 'e')
144 && (strncmp(string
, "end", endOfBase
-string
) == 0)) {
146 * Base position is end of text.
149 *lineIndexPtr
= TkBTreeNumLines(textPtr
->tree
) - 1;
150 linePtr
= TkBTreeFindLine(textPtr
->tree
, *lineIndexPtr
);
151 *chPtr
= linePtr
->numBytes
- 1;
155 * See if the base position is the name of a mark.
160 hPtr
= Tcl_FindHashEntry(&textPtr
->markTable
, string
);
163 markPtr
= (TkAnnotation
*) Tcl_GetHashValue(hPtr
);
164 *lineIndexPtr
= TkBTreeLineIndex(markPtr
->linePtr
);
165 *chPtr
= markPtr
->ch
;
171 * Nothing has worked so far. See if the base has the form
172 * "tag.first" or "tag.last" where "tag" is the name of a valid
176 p
= strchr(string
, '.');
180 if ((p
[1] == 'f') && (endOfBase
== (p
+6))
181 && (strncmp(p
+1, "first", endOfBase
- (p
+1)) == 0)) {
183 } else if ((p
[1] == 'l') && (endOfBase
== (p
+5))
184 && (strncmp(p
+1, "last", endOfBase
- (p
+1)) == 0)) {
190 hPtr
= Tcl_FindHashEntry(&textPtr
->tagTable
, string
);
195 tagPtr
= (TkTextTag
*) Tcl_GetHashValue(hPtr
);
196 TkBTreeStartSearch(textPtr
->tree
, 0, 0, TkBTreeNumLines(textPtr
->tree
),
198 if (!TkBTreeNextTag(&search
)) {
199 Tcl_AppendResult(interp
,
200 "text doesn't contain any characters tagged with \"",
201 Tcl_GetHashKey(&textPtr
->tagTable
, hPtr
), "\"", (char *) NULL
);
205 *lineIndexPtr
= search
.line1
;
208 while (TkBTreeNextTag(&search
)) {
209 *lineIndexPtr
= search
.line1
;
215 *-------------------------------------------------------------------
216 * Stage 2: process zero or more modifiers. Each modifier is either
217 * a keyword like "wordend" or "linestart", or it has the form
218 * "op count units" where op is + or -, count is a number, and units
219 * is "chars" or "lines".
220 *-------------------------------------------------------------------
226 while (isspace(*p
)) {
233 if ((*p
== '+') || (*p
== '-')) {
234 p
= ForwBack(textPtr
, p
, lineIndexPtr
, chPtr
);
236 p
= StartEnd(textPtr
, p
, lineIndexPtr
, chPtr
);
244 Tcl_AppendResult(interp
, "bad text index \"", string
, "\"",
250 *----------------------------------------------------------------------
252 * TkTextPrintIndex --
254 * Given a line number and a character index, this procedure
255 * generates a string description of the position, which is
256 * suitable for reading in again later.
259 * The characters pointed to by string are modified.
264 *----------------------------------------------------------------------
269 int line
, /* Line number. */
270 int ch
, /* Character position within line. */
271 char *string
/* Place to store the position. Must have
272 * at least POS_CHARS characters. */
275 sprintf(string
, "%d.%d", line
+1, ch
);
279 *----------------------------------------------------------------------
281 * TkTextRoundIndex --
283 * Given a line index and a character index, this procedure
284 * adjusts those positions if necessary to correspond to the
285 * nearest actual character within the text.
288 * The return value is a pointer to the line structure for
289 * the line of the text's B-tree that contains the indicated
290 * character. In addition, *lineIndexPtr and *chPtr are
291 * modified if necessary to refer to an existing character
297 *----------------------------------------------------------------------
303 TkText
*textPtr
, /* Information about text widget. */
304 int *lineIndexPtr
, /* Points to initial line index,
305 * which is overwritten with actual
307 int *chPtr
/* Points to initial character index,
308 * which is overwritten with actual
309 * character index. */
312 int line
, ch
, lastLine
;
315 line
= *lineIndexPtr
;
321 lastLine
= TkBTreeNumLines(textPtr
->tree
) - 1;
322 if (line
> lastLine
) {
324 linePtr
= TkBTreeFindLine(textPtr
->tree
, line
);
325 ch
= linePtr
->numBytes
- 1;
327 linePtr
= TkBTreeFindLine(textPtr
->tree
, line
);
331 if (ch
>= linePtr
->numBytes
) {
332 if (line
== lastLine
) {
333 ch
= linePtr
->numBytes
- 1;
336 linePtr
= TkBTreeNextLine(linePtr
);
341 *lineIndexPtr
= line
;
347 *----------------------------------------------------------------------
351 * This procedure handles +/- modifiers for indices to adjust
352 * the index forwards or backwards.
355 * If the modifier is successfully parsed then the return value
356 * is the address of the first character after the modifier, and
357 * *lineIndexPtr and *chPtr are updated to reflect the modifier.
358 * If there is a syntax error in the modifier then NULL is returned.
363 *----------------------------------------------------------------------
368 TkText
*textPtr
, /* Information about widget that index
370 char *string
, /* String to parse for additional info
371 * about modifier (count and units).
372 * Points to "+" or "-" that starts
374 int *lineIndexPtr
, /* Points to current line index, which will
375 * be updated to reflect modifier. */
376 int *chPtr
/* Points to current character index, which
377 * will be updated to reflect modifier. */
382 int count
, length
, lastLine
;
386 * Get the count (how many units forward or backward).
390 while (isspace(*p
)) {
393 count
= strtoul(p
, &end
, 0);
398 while (isspace(*p
)) {
403 * Find the end of this modifier (next space or + or - character),
404 * then parse the unit specifier and update the position
409 while ((*p
!= 0) && !isspace(*p
) && (*p
!= '+') && (*p
!= '-')) {
413 if ((*units
== 'c') && (strncmp(units
, "chars", length
) == 0)) {
414 linePtr
= TkTextRoundIndex(textPtr
, lineIndexPtr
, chPtr
);
415 if (*string
== '+') {
416 ForwardChars(textPtr
, linePtr
, lineIndexPtr
, chPtr
, count
);
418 BackwardChars(textPtr
, linePtr
, lineIndexPtr
, chPtr
, count
);
420 } else if ((*units
== 'l') && (strncmp(units
, "lines", length
) == 0)) {
421 if (*string
== '+') {
422 *lineIndexPtr
+= count
;
423 lastLine
= TkBTreeNumLines(textPtr
->tree
) - 1;
424 if (*lineIndexPtr
> lastLine
) {
425 *lineIndexPtr
= lastLine
;
428 *lineIndexPtr
-= count
;
429 if (*lineIndexPtr
< 0) {
433 linePtr
= TkBTreeFindLine(textPtr
->tree
, *lineIndexPtr
);
434 if (*chPtr
>= linePtr
->numBytes
) {
435 *chPtr
= linePtr
->numBytes
- 1;
447 *----------------------------------------------------------------------
451 * Given a position in a text widget, this procedure computes
452 * a new position that is "count" characters ahead of the given
456 * *LineIndexPtr and *chPtr are overwritten with new values
457 * corresponding to the new position.
462 *----------------------------------------------------------------------
468 TkText
*textPtr
, /* Information about text widget. */
469 register TkTextLine
*linePtr
, /* Text line corresponding to
471 int *lineIndexPtr
, /* Points to initial line index,
472 * which is overwritten with final
474 int *chPtr
, /* Points to initial character index,
475 * which is overwritten with final
476 * character index. */
477 int count
/* How many characters forward to
478 * move. Must not be negative. */
485 bytesInLine
= linePtr
->numBytes
- *chPtr
;
486 if (bytesInLine
> count
) {
490 nextPtr
= TkBTreeNextLine(linePtr
);
491 if (nextPtr
== NULL
) {
492 *chPtr
= linePtr
->numBytes
- 1;
498 count
-= bytesInLine
;
503 *----------------------------------------------------------------------
507 * Given a position in a text widget, this procedure computes
508 * a new position that is "count" characters earlier than the given
512 * *LineIndexPtr and *chPtr are overwritten with new values
513 * corresponding to the new position.
518 *----------------------------------------------------------------------
523 TkText
*textPtr
, /* Information about text widget. */
524 register TkTextLine
*linePtr
, /* Text line corresponding to
526 int *lineIndexPtr
, /* Points to initial line index,
527 * which is overwritten with final
529 int *chPtr
, /* Points to initial character index,
530 * which is overwritten with final
531 * character index. */
532 int count
/* How many characters backward to
533 * move. Must not be negative. */
539 bytesInLine
= *chPtr
;
540 if (bytesInLine
>= count
) {
544 if (*lineIndexPtr
<= 0) {
549 linePtr
= TkBTreeFindLine(textPtr
->tree
, *lineIndexPtr
);
550 count
-= bytesInLine
;
551 *chPtr
= linePtr
->numBytes
;
556 *----------------------------------------------------------------------
560 * This procedure handles modifiers like "wordstart" and "lineend"
561 * to adjust indices forwards or backwards.
564 * If the modifier is successfully parsed then the return value
565 * is the address of the first character after the modifier, and
566 * *lineIndexPtr and *chPtr are updated to reflect the modifier.
567 * If there is a syntax error in the modifier then NULL is returned.
572 *----------------------------------------------------------------------
577 TkText
*textPtr
, /* Information about widget that index
579 char *string
, /* String to parse for additional info
580 * about modifier (count and units).
581 * Points to first character of modifer
583 int *lineIndexPtr
, /* Points to current line index, which will
584 * be updated to reflect modifier. */
585 int *chPtr
/* Points to current character index, which
586 * will be updated to reflect modifier. */
591 register TkTextLine
*linePtr
;
594 * Find the end of the modifier word.
597 for (p
= string
; isalnum(*p
); p
++) {
598 /* Empty loop body. */
601 linePtr
= TkTextRoundIndex(textPtr
, lineIndexPtr
, chPtr
);
602 if ((*string
== 'l') && (strncmp(string
, "lineend", length
) == 0)
604 *chPtr
= linePtr
->numBytes
- 1;
605 } else if ((*string
== 'l') && (strncmp(string
, "linestart", length
) == 0)
608 } else if ((*string
== 'w') && (strncmp(string
, "wordend", length
) == 0)
610 c
= linePtr
->bytes
[*chPtr
];
611 if (!isalnum(c
) && (c
!= '_')) {
612 if (*chPtr
>= (linePtr
->numBytes
- 1)) {
614 * End of line: go to start of next line unless this is the
615 * last line in the text.
618 if (TkBTreeNextLine(linePtr
) != NULL
) {
628 c
= linePtr
->bytes
[*chPtr
];
629 } while (isalnum(c
) || (c
== '_'));
631 } else if ((*string
== 'w') && (strncmp(string
, "wordstart", length
) == 0)
633 c
= linePtr
->bytes
[*chPtr
];
634 if (isalnum(c
) || (c
== '_')) {
636 c
= linePtr
->bytes
[(*chPtr
) - 1];
637 if (!isalnum(c
) && (c
!= '_')) {