1 /*
2 * tkTextMark.c --
3 *
4 * This file contains the functions that implement marks for text
5 * widgets.
6 *
7 * Copyright (c) 1994 The Regents of the University of California.
8 * Copyright (c) 1994-1997 Sun Microsystems, Inc.
9 *
10 * See the file "license.terms" for information on usage and redistribution of
11 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
12 */
13
14 #include "tkInt.h"
15 #include "tkText.h"
16
17 /*
18 * Macro that determines the size of a mark segment:
19 */
20
21 #define MSEG_SIZE ((unsigned) (Tk_Offset(TkTextSegment, body) \
22 + sizeof(TkTextMark)))
23
24 /*
25 * Forward references for functions defined in this file:
26 */
27
28 static void InsertUndisplayProc(TkText *textPtr,
29 TkTextDispChunk *chunkPtr);
30 static int MarkDeleteProc(TkTextSegment *segPtr,
31 TkTextLine *linePtr, int treeGone);
32 static TkTextSegment * MarkCleanupProc(TkTextSegment *segPtr,
33 TkTextLine *linePtr);
34 static void MarkCheckProc(TkTextSegment *segPtr,
35 TkTextLine *linePtr);
36 static int MarkLayoutProc(TkText *textPtr, TkTextIndex *indexPtr,
37 TkTextSegment *segPtr, int offset, int maxX,
38 int maxChars, int noCharsYet, TkWrapMode wrapMode,
39 TkTextDispChunk *chunkPtr);
40 static int MarkFindNext(Tcl_Interp *interp,
41 TkText *textPtr, const char *markName);
42 static int MarkFindPrev(Tcl_Interp *interp,
43 TkText *textPtr, const char *markName);
44
45
46 /*
47 * The following structures declare the "mark" segment types. There are
48 * actually two types for marks, one with left gravity and one with right
49 * gravity. They are identical except for their gravity property.
50 */
51
52 const Tk_SegType tkTextRightMarkType = {
53 "mark", /* name */
54 0, /* leftGravity */
55 NULL, /* splitProc */
56 MarkDeleteProc, /* deleteProc */
57 MarkCleanupProc, /* cleanupProc */
58 NULL, /* lineChangeProc */
59 MarkLayoutProc, /* layoutProc */
60 MarkCheckProc /* checkProc */
61 };
62
63 const Tk_SegType tkTextLeftMarkType = {
64 "mark", /* name */
65 1, /* leftGravity */
66 NULL, /* splitProc */
67 MarkDeleteProc, /* deleteProc */
68 MarkCleanupProc, /* cleanupProc */
69 NULL, /* lineChangeProc */
70 MarkLayoutProc, /* layoutProc */
71 MarkCheckProc /* checkProc */
72 };
73
74 /*
75 *--------------------------------------------------------------
76 *
77 * TkTextMarkCmd --
78 *
79 * This function is invoked to process the "mark" options of the widget
80 * command for text widgets. See the user documentation for details on
81 * what it does.
82 *
83 * Results:
84 * A standard Tcl result.
85 *
86 * Side effects:
87 * See the user documentation.
88 *
89 *--------------------------------------------------------------
90 */
91
92 int
TkTextMarkCmd(register TkText * textPtr,Tcl_Interp * interp,int objc,Tcl_Obj * const objv[])93 TkTextMarkCmd(
94 register TkText *textPtr, /* Information about text widget. */
95 Tcl_Interp *interp, /* Current interpreter. */
96 int objc, /* Number of arguments. */
97 Tcl_Obj *const objv[]) /* Argument objects. Someone else has already
98 * parsed this command enough to know that
99 * objv[1] is "mark". */
100 {
101 Tcl_HashEntry *hPtr;
102 TkTextSegment *markPtr;
103 Tcl_HashSearch search;
104 TkTextIndex index;
105 const Tk_SegType *newTypePtr;
106 int optionIndex;
107 static const char *markOptionStrings[] = {
108 "gravity", "names", "next", "previous", "set", "unset", NULL
109 };
110 enum markOptions {
111 MARK_GRAVITY, MARK_NAMES, MARK_NEXT, MARK_PREVIOUS, MARK_SET,
112 MARK_UNSET
113 };
114
115 if (objc < 3) {
116 Tcl_WrongNumArgs(interp, 2, objv, "option ?arg arg ...?");
117 return TCL_ERROR;
118 }
119 if (Tcl_GetIndexFromObj(interp, objv[2], markOptionStrings, "mark option",
120 0, &optionIndex) != TCL_OK) {
121 return TCL_ERROR;
122 }
123
124 switch ((enum markOptions) optionIndex) {
125 case MARK_GRAVITY: {
126 char c;
127 int length;
128 char *str;
129
130 if (objc < 4 || objc > 5) {
131 Tcl_WrongNumArgs(interp, 3, objv, "markName ?gravity?");
132 return TCL_ERROR;
133 }
134 str = Tcl_GetStringFromObj(objv[3],&length);
135 if (length == 6 && !strcmp(str, "insert")) {
136 markPtr = textPtr->insertMarkPtr;
137 } else if (length == 7 && !strcmp(str, "current")) {
138 markPtr = textPtr->currentMarkPtr;
139 } else {
140 hPtr = Tcl_FindHashEntry(&textPtr->sharedTextPtr->markTable, str);
141 if (hPtr == NULL) {
142 Tcl_AppendResult(interp, "there is no mark named \"",
143 Tcl_GetString(objv[3]), "\"", NULL);
144 return TCL_ERROR;
145 }
146 markPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr);
147 }
148 if (objc == 4) {
149 if (markPtr->typePtr == &tkTextRightMarkType) {
150 Tcl_SetResult(interp, "right", TCL_STATIC);
151 } else {
152 Tcl_SetResult(interp, "left", TCL_STATIC);
153 }
154 return TCL_OK;
155 }
156 str = Tcl_GetStringFromObj(objv[4],&length);
157 c = str[0];
158 if ((c == 'l') && (strncmp(str, "left", (unsigned)length) == 0)) {
159 newTypePtr = &tkTextLeftMarkType;
160 } else if ((c == 'r') &&
161 (strncmp(str, "right", (unsigned)length) == 0)) {
162 newTypePtr = &tkTextRightMarkType;
163 } else {
164 Tcl_AppendResult(interp, "bad mark gravity \"", str,
165 "\": must be left or right", NULL);
166 return TCL_ERROR;
167 }
168 TkTextMarkSegToIndex(textPtr, markPtr, &index);
169 TkBTreeUnlinkSegment(markPtr, markPtr->body.mark.linePtr);
170 markPtr->typePtr = newTypePtr;
171 TkBTreeLinkSegment(markPtr, &index);
172 break;
173 }
174 case MARK_NAMES:
175 if (objc != 3) {
176 Tcl_WrongNumArgs(interp, 3, objv, NULL);
177 return TCL_ERROR;
178 }
179 Tcl_AppendElement(interp, "insert");
180 Tcl_AppendElement(interp, "current");
181 for (hPtr = Tcl_FirstHashEntry(&textPtr->sharedTextPtr->markTable,
182 &search); hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
183 Tcl_AppendElement(interp,
184 Tcl_GetHashKey(&textPtr->sharedTextPtr->markTable, hPtr));
185 }
186 break;
187 case MARK_NEXT:
188 if (objc != 4) {
189 Tcl_WrongNumArgs(interp, 3, objv, "index");
190 return TCL_ERROR;
191 }
192 return MarkFindNext(interp, textPtr, Tcl_GetString(objv[3]));
193 case MARK_PREVIOUS:
194 if (objc != 4) {
195 Tcl_WrongNumArgs(interp, 3, objv, "index");
196 return TCL_ERROR;
197 }
198 return MarkFindPrev(interp, textPtr, Tcl_GetString(objv[3]));
199 case MARK_SET:
200 if (objc != 5) {
201 Tcl_WrongNumArgs(interp, 3, objv, "markName index");
202 return TCL_ERROR;
203 }
204 if (TkTextGetObjIndex(interp, textPtr, objv[4], &index) != TCL_OK) {
205 return TCL_ERROR;
206 }
207 TkTextSetMark(textPtr, Tcl_GetString(objv[3]), &index);
208 return TCL_OK;
209 case MARK_UNSET: {
210 int i;
211
212 for (i = 3; i < objc; i++) {
213 hPtr = Tcl_FindHashEntry(&textPtr->sharedTextPtr->markTable,
214 Tcl_GetString(objv[i]));
215 if (hPtr != NULL) {
216 markPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr);
217
218 /*
219 * Special case not needed with peer widgets.
220 */
221
222 if ((markPtr == textPtr->insertMarkPtr)
223 || (markPtr == textPtr->currentMarkPtr)) {
224 continue;
225 }
226 TkBTreeUnlinkSegment(markPtr, markPtr->body.mark.linePtr);
227 Tcl_DeleteHashEntry(hPtr);
228 ckfree((char *) markPtr);
229 }
230 }
231 break;
232 }
233 }
234 return TCL_OK;
235 }
236
237 /*
238 *----------------------------------------------------------------------
239 *
240 * TkTextSetMark --
241 *
242 * Set a mark to a particular position, creating a new mark if one
243 * doesn't already exist.
244 *
245 * Results:
246 * The return value is a pointer to the mark that was just set.
247 *
248 * Side effects:
249 * A new mark is created, or an existing mark is moved.
250 *
251 *----------------------------------------------------------------------
252 */
253
254 TkTextSegment *
TkTextSetMark(TkText * textPtr,const char * name,TkTextIndex * indexPtr)255 TkTextSetMark(
256 TkText *textPtr, /* Text widget in which to create mark. */
257 const char *name, /* Name of mark to set. */
258 TkTextIndex *indexPtr) /* Where to set mark. */
259 {
260 Tcl_HashEntry *hPtr = NULL;
261 TkTextSegment *markPtr;
262 TkTextIndex insertIndex;
263 int isNew, widgetSpecific;
264
265 if (!strcmp(name, "insert")) {
266 widgetSpecific = 1;
267 markPtr = textPtr->insertMarkPtr;
268 isNew = (markPtr == NULL ? 1 : 0);
269 } else if (!strcmp(name, "current")) {
270 widgetSpecific = 2;
271 markPtr = textPtr->currentMarkPtr;
272 isNew = (markPtr == NULL ? 1 : 0);
273 } else {
274 widgetSpecific = 0;
275 hPtr = Tcl_CreateHashEntry(&textPtr->sharedTextPtr->markTable, name,
276 &isNew);
277 markPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr);
278 }
279 if (!isNew) {
280 /*
281 * If this is the insertion point that's being moved, be sure to force
282 * a display update at the old position. Also, don't let the insertion
283 * cursor be after the final newline of the file.
284 */
285
286 if (markPtr == textPtr->insertMarkPtr) {
287 TkTextIndex index, index2;
288 int nblines;
289
290 TkTextMarkSegToIndex(textPtr, textPtr->insertMarkPtr, &index);
291 TkTextIndexForwChars(NULL,&index, 1, &index2, COUNT_INDICES);
292
293 /*
294 * While we wish to redisplay, no heights have changed, so no need
295 * to call TkTextInvalidateLineMetrics.
296 */
297
298 TkTextChanged(NULL, textPtr, &index, &index2);
299
300 /*
301 * The number of lines in the widget is zero if and only if it is
302 * a partial peer with -startline == -endline, i.e. an empty
303 * peer. In this case the mark shall be set exactly at the given
304 * index, and not one character backwards (bug 3487407).
305 */
306
307 nblines = TkBTreeNumLines(textPtr->sharedTextPtr->tree, textPtr);
308 if ((TkBTreeLinesTo(textPtr, indexPtr->linePtr) == nblines)
309 && (nblines > 0)) {
310 TkTextIndexBackChars(NULL,indexPtr, 1, &insertIndex,
311 COUNT_INDICES);
312 indexPtr = &insertIndex;
313 }
314 }
315 TkBTreeUnlinkSegment(markPtr, markPtr->body.mark.linePtr);
316 } else {
317 markPtr = (TkTextSegment *) ckalloc(MSEG_SIZE);
318 markPtr->typePtr = &tkTextRightMarkType;
319 markPtr->size = 0;
320 markPtr->body.mark.textPtr = textPtr;
321 markPtr->body.mark.linePtr = indexPtr->linePtr;
322 markPtr->body.mark.hPtr = hPtr;
323 if (widgetSpecific == 0) {
324 Tcl_SetHashValue(hPtr, markPtr);
325 } else if (widgetSpecific == 1) {
326 textPtr->insertMarkPtr = markPtr;
327 } else {
328 textPtr->currentMarkPtr = markPtr;
329 }
330 }
331 TkBTreeLinkSegment(markPtr, indexPtr);
332
333 /*
334 * If the mark is the insertion cursor, then update the screen at the
335 * mark's new location.
336 */
337
338 if (markPtr == textPtr->insertMarkPtr) {
339 TkTextIndex index2;
340
341 TkTextIndexForwChars(NULL,indexPtr, 1, &index2, COUNT_INDICES);
342
343 /*
344 * While we wish to redisplay, no heights have changed, so no need to
345 * call TkTextInvalidateLineMetrics
346 */
347
348 TkTextChanged(NULL, textPtr, indexPtr, &index2);
349 }
350 return markPtr;
351 }
352
353 /*
354 *--------------------------------------------------------------
355 *
356 * TkTextMarkSegToIndex --
357 *
358 * Given a segment that is a mark, create an index that refers to the
359 * next text character (or other text segment with non-zero size) after
360 * the mark.
361 *
362 * Results:
363 * *IndexPtr is filled in with index information.
364 *
365 * Side effects:
366 * None.
367 *
368 *--------------------------------------------------------------
369 */
370
371 void
TkTextMarkSegToIndex(TkText * textPtr,TkTextSegment * markPtr,TkTextIndex * indexPtr)372 TkTextMarkSegToIndex(
373 TkText *textPtr, /* Text widget containing mark. */
374 TkTextSegment *markPtr, /* Mark segment. */
375 TkTextIndex *indexPtr) /* Index information gets stored here. */
376 {
377 TkTextSegment *segPtr;
378
379 indexPtr->tree = textPtr->sharedTextPtr->tree;
380 indexPtr->linePtr = markPtr->body.mark.linePtr;
381 indexPtr->byteIndex = 0;
382 for (segPtr = indexPtr->linePtr->segPtr; segPtr != markPtr;
383 segPtr = segPtr->nextPtr) {
384 indexPtr->byteIndex += segPtr->size;
385 }
386 }
387
388 /*
389 *--------------------------------------------------------------
390 *
391 * TkTextMarkNameToIndex --
392 *
393 * Given the name of a mark, return an index corresponding to the mark
394 * name.
395 *
396 * Results:
397 * The return value is TCL_OK if "name" exists as a mark in the text
398 * widget and is located within its -starline/-endline range. In this
399 * case *indexPtr is filled in with the next segment who is after the
400 * mark whose size is non-zero. TCL_ERROR is returned if the mark
401 * doesn't exist in the text widget, or if it is out of its -starline/
402 * -endline range. In this latter case *indexPtr still contains valid
403 * information, in particular TkTextMarkNameToIndex called with the
404 * "insert" or "current" mark name may return TCL_ERROR, but *indexPtr
405 * contains the correct index of this mark before -startline or after
406 * -endline.
407 *
408 * Side effects:
409 * None.
410 *
411 *--------------------------------------------------------------
412 */
413
414 int
TkTextMarkNameToIndex(TkText * textPtr,const char * name,TkTextIndex * indexPtr)415 TkTextMarkNameToIndex(
416 TkText *textPtr, /* Text widget containing mark. */
417 const char *name, /* Name of mark. */
418 TkTextIndex *indexPtr) /* Index information gets stored here. */
419 {
420 TkTextSegment *segPtr;
421 TkTextIndex index;
422 int start, end;
423
424 if (textPtr == NULL) {
425 return TCL_ERROR;
426 }
427
428 if (!strcmp(name, "insert")) {
429 segPtr = textPtr->insertMarkPtr;
430 } else if (!strcmp(name, "current")) {
431 segPtr = textPtr->currentMarkPtr;
432 } else {
433 Tcl_HashEntry *hPtr;
434 hPtr = Tcl_FindHashEntry(&textPtr->sharedTextPtr->markTable, name);
435 if (hPtr == NULL) {
436 return TCL_ERROR;
437 }
438 segPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr);
439 }
440 TkTextMarkSegToIndex(textPtr, segPtr, indexPtr);
441
442 /* If indexPtr refers to somewhere outside the -startline/-endline
443 * range limits of the widget, error out since the mark indeed is not
444 * reachable from this text widget (it may be reachable from a peer)
445 * (bug 1630271).
446 */
447
448 if (textPtr->start != NULL) {
449 start = TkBTreeLinesTo(NULL, textPtr->start);
450 TkTextMakeByteIndex(textPtr->sharedTextPtr->tree, NULL, start, 0,
451 &index);
452 if (TkTextIndexCmp(indexPtr, &index) < 0) {
453 return TCL_ERROR;
454 }
455 }
456 if (textPtr->end != NULL) {
457 end = TkBTreeLinesTo(NULL, textPtr->end);
458 TkTextMakeByteIndex(textPtr->sharedTextPtr->tree, NULL, end, 0,
459 &index);
460 if (TkTextIndexCmp(indexPtr, &index) > 0) {
461 return TCL_ERROR;
462 }
463 }
464 return TCL_OK;
465 }
466
467 /*
468 *--------------------------------------------------------------
469 *
470 * MarkDeleteProc --
471 *
472 * This function is invoked by the text B-tree code whenever a mark lies
473 * in a range of characters being deleted.
474 *
475 * Results:
476 * Returns 1 to indicate that deletion has been rejected.
477 *
478 * Side effects:
479 * None (even if the whole tree is being deleted we don't free up the
480 * mark; it will be done elsewhere).
481 *
482 *--------------------------------------------------------------
483 */
484
485 /* ARGSUSED */
486 static int
MarkDeleteProc(TkTextSegment * segPtr,TkTextLine * linePtr,int treeGone)487 MarkDeleteProc(
488 TkTextSegment *segPtr, /* Segment being deleted. */
489 TkTextLine *linePtr, /* Line containing segment. */
490 int treeGone) /* Non-zero means the entire tree is being
491 * deleted, so everything must get cleaned
492 * up. */
493 {
494 return 1;
495 }
496
497 /*
498 *--------------------------------------------------------------
499 *
500 * MarkCleanupProc --
501 *
502 * This function is invoked by the B-tree code whenever a mark segment is
503 * moved from one line to another.
504 *
505 * Results:
506 * None.
507 *
508 * Side effects:
509 * The linePtr field of the segment gets updated.
510 *
511 *--------------------------------------------------------------
512 */
513
514 static TkTextSegment *
MarkCleanupProc(TkTextSegment * markPtr,TkTextLine * linePtr)515 MarkCleanupProc(
516 TkTextSegment *markPtr, /* Mark segment that's being moved. */
517 TkTextLine *linePtr) /* Line that now contains segment. */
518 {
519 markPtr->body.mark.linePtr = linePtr;
520 return markPtr;
521 }
522
523 /*
524 *--------------------------------------------------------------
525 *
526 * MarkLayoutProc --
527 *
528 * This function is the "layoutProc" for mark segments.
529 *
530 * Results:
531 * If the mark isn't the insertion cursor then the return value is -1 to
532 * indicate that this segment shouldn't be displayed. If the mark is the
533 * insertion character then 1 is returned and the chunkPtr structure is
534 * filled in.
535 *
536 * Side effects:
537 * None, except for filling in chunkPtr.
538 *
539 *--------------------------------------------------------------
540 */
541
542 static int
MarkLayoutProc(TkText * textPtr,TkTextIndex * indexPtr,TkTextSegment * segPtr,int offset,int maxX,int maxChars,int noCharsYet,TkWrapMode wrapMode,register TkTextDispChunk * chunkPtr)543 MarkLayoutProc(
544 TkText *textPtr, /* Text widget being layed out. */
545 TkTextIndex *indexPtr, /* Identifies first character in chunk. */
546 TkTextSegment *segPtr, /* Segment corresponding to indexPtr. */
547 int offset, /* Offset within segPtr corresponding to
548 * indexPtr (always 0). */
549 int maxX, /* Chunk must not occupy pixels at this
550 * position or higher. */
551 int maxChars, /* Chunk must not include more than this many
552 * characters. */
553 int noCharsYet, /* Non-zero means no characters have been
554 * assigned to this line yet. */
555 TkWrapMode wrapMode, /* Not used. */
556 register TkTextDispChunk *chunkPtr)
557 /* Structure to fill in with information about
558 * this chunk. The x field has already been
559 * set by the caller. */
560 {
561 if (segPtr != textPtr->insertMarkPtr) {
562 return -1;
563 }
564
565 chunkPtr->displayProc = TkTextInsertDisplayProc;
566 chunkPtr->undisplayProc = InsertUndisplayProc;
567 chunkPtr->measureProc = NULL;
568 chunkPtr->bboxProc = NULL;
569 chunkPtr->numBytes = 0;
570 chunkPtr->minAscent = 0;
571 chunkPtr->minDescent = 0;
572 chunkPtr->minHeight = 0;
573 chunkPtr->width = 0;
574
575 /*
576 * Note: can't break a line after the insertion cursor: this prevents the
577 * insertion cursor from being stranded at the end of a line.
578 */
579
580 chunkPtr->breakIndex = -1;
581 chunkPtr->clientData = (ClientData) textPtr;
582 return 1;
583 }
584
585 /*
586 *--------------------------------------------------------------
587 *
588 * TkTextInsertDisplayProc --
589 *
590 * This function is called to display the insertion cursor.
591 *
592 * Results:
593 * None.
594 *
595 * Side effects:
596 * Graphics are drawn.
597 *
598 *--------------------------------------------------------------
599 */
600
601 /* ARGSUSED */
602 void
TkTextInsertDisplayProc(TkText * textPtr,TkTextDispChunk * chunkPtr,int x,int y,int height,int baseline,Display * display,Drawable dst,int screenY)603 TkTextInsertDisplayProc(
604 TkText *textPtr, /* The current text widget. */
605 TkTextDispChunk *chunkPtr, /* Chunk that is to be drawn. */
606 int x, /* X-position in dst at which to draw this
607 * chunk (may differ from the x-position in
608 * the chunk because of scrolling). */
609 int y, /* Y-position at which to draw this chunk in
610 * dst (x-position is in the chunk itself). */
611 int height, /* Total height of line. */
612 int baseline, /* Offset of baseline from y. */
613 Display *display, /* Display to use for drawing. */
614 Drawable dst, /* Pixmap or window in which to draw chunk. */
615 int screenY) /* Y-coordinate in text window that
616 * corresponds to y. */
617 {
618 /*
619 * We have no need for the clientData.
620 */
621
622 /* TkText *textPtr = (TkText *) chunkPtr->clientData; */
623 TkTextIndex index;
624 int halfWidth = textPtr->insertWidth/2;
625 int rightSideWidth;
626 int ix = 0, iy = 0, iw = 0, ih = 0, charWidth = 0;
627
628 if(textPtr->insertCursorType) {
629 TkTextMarkSegToIndex(textPtr, textPtr->insertMarkPtr, &index);
630 TkTextIndexBbox(textPtr, &index, &ix, &iy, &iw, &ih, &charWidth);
631 rightSideWidth = charWidth + halfWidth;
632 } else {
633 rightSideWidth = halfWidth;
634 }
635
636 if ((x + rightSideWidth) < 0) {
637 /*
638 * The insertion cursor is off-screen. Indicate caret at 0,0 and
639 * return.
640 */
641
642 Tk_SetCaretPos(textPtr->tkwin, 0, 0, height);
643 return;
644 }
645
646 Tk_SetCaretPos(textPtr->tkwin, x - halfWidth, screenY, height);
647
648 /*
649 * As a special hack to keep the cursor visible on mono displays (or
650 * anywhere else that the selection and insertion cursors have the same
651 * color) write the default background in the cursor area (instead of
652 * nothing) when the cursor isn't on. Otherwise the selection might hide
653 * the cursor.
654 */
655
656 if (textPtr->flags & INSERT_ON) {
657 Tk_Fill3DRectangle(textPtr->tkwin, dst, textPtr->insertBorder,
658 x - halfWidth, y, charWidth + textPtr->insertWidth, height,
659 textPtr->insertBorderWidth, TK_RELIEF_RAISED);
660 } else if (textPtr->selBorder == textPtr->insertBorder) {
661 Tk_Fill3DRectangle(textPtr->tkwin, dst, textPtr->border,
662 x - halfWidth, y, charWidth + textPtr->insertWidth, height,
663 0, TK_RELIEF_FLAT);
664 }
665 }
666
667 /*
668 *--------------------------------------------------------------
669 *
670 * InsertUndisplayProc --
671 *
672 * This function is called when the insertion cursor is no longer at a
673 * visible point on the display. It does nothing right now.
674 *
675 * Results:
676 * None.
677 *
678 * Side effects:
679 * None.
680 *
681 *--------------------------------------------------------------
682 */
683
684 /* ARGSUSED */
685 static void
InsertUndisplayProc(TkText * textPtr,TkTextDispChunk * chunkPtr)686 InsertUndisplayProc(
687 TkText *textPtr, /* Overall information about text widget. */
688 TkTextDispChunk *chunkPtr) /* Chunk that is about to be freed. */
689 {
690 return;
691 }
692
693 /*
694 *--------------------------------------------------------------
695 *
696 * MarkCheckProc --
697 *
698 * This function is invoked by the B-tree code to perform consistency
699 * checks on mark segments.
700 *
701 * Results:
702 * None.
703 *
704 * Side effects:
705 * The function panics if it detects anything wrong with
706 * the mark.
707 *
708 *--------------------------------------------------------------
709 */
710
711 static void
MarkCheckProc(TkTextSegment * markPtr,TkTextLine * linePtr)712 MarkCheckProc(
713 TkTextSegment *markPtr, /* Segment to check. */
714 TkTextLine *linePtr) /* Line containing segment. */
715 {
716 Tcl_HashSearch search;
717 Tcl_HashEntry *hPtr;
718
719 if (markPtr->body.mark.linePtr != linePtr) {
720 Tcl_Panic("MarkCheckProc: markPtr->body.mark.linePtr bogus");
721 }
722
723 /*
724 * These two marks are not in the hash table
725 */
726
727 if (markPtr->body.mark.textPtr->insertMarkPtr == markPtr) {
728 return;
729 }
730 if (markPtr->body.mark.textPtr->currentMarkPtr == markPtr) {
731 return;
732 }
733
734 /*
735 * Make sure that the mark is still present in the text's mark hash table.
736 */
737
738 for (hPtr = Tcl_FirstHashEntry(
739 &markPtr->body.mark.textPtr->sharedTextPtr->markTable,
740 &search); hPtr != markPtr->body.mark.hPtr;
741 hPtr = Tcl_NextHashEntry(&search)) {
742 if (hPtr == NULL) {
743 Tcl_Panic("MarkCheckProc couldn't find hash table entry for mark");
744 }
745 }
746 }
747
748 /*
749 *--------------------------------------------------------------
750 *
751 * MarkFindNext --
752 *
753 * This function searches forward for the next mark.
754 *
755 * Results:
756 * A standard Tcl result, which is a mark name or an empty string.
757 *
758 * Side effects:
759 * None.
760 *
761 *--------------------------------------------------------------
762 */
763
764 static int
MarkFindNext(Tcl_Interp * interp,TkText * textPtr,const char * string)765 MarkFindNext(
766 Tcl_Interp *interp, /* For error reporting */
767 TkText *textPtr, /* The widget */
768 const char *string) /* The starting index or mark name */
769 {
770 TkTextIndex index;
771 Tcl_HashEntry *hPtr;
772 register TkTextSegment *segPtr;
773 int offset;
774
775 if (!strcmp(string, "insert")) {
776 segPtr = textPtr->insertMarkPtr;
777 TkTextMarkSegToIndex(textPtr, segPtr, &index);
778 segPtr = segPtr->nextPtr;
779 } else if (!strcmp(string, "current")) {
780 segPtr = textPtr->currentMarkPtr;
781 TkTextMarkSegToIndex(textPtr, segPtr, &index);
782 segPtr = segPtr->nextPtr;
783 } else {
784 hPtr = Tcl_FindHashEntry(&textPtr->sharedTextPtr->markTable, string);
785 if (hPtr != NULL) {
786 /*
787 * If given a mark name, return the next mark in the list of
788 * segments, even if it happens to be at the same character
789 * position.
790 */
791
792 segPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr);
793 TkTextMarkSegToIndex(textPtr, segPtr, &index);
794 segPtr = segPtr->nextPtr;
795 } else {
796 /*
797 * For non-mark name indices we want to return any marks that are
798 * right at the index.
799 */
800
801 if (TkTextGetIndex(interp, textPtr, string, &index) != TCL_OK) {
802 return TCL_ERROR;
803 }
804 for (offset = 0, segPtr = index.linePtr->segPtr;
805 segPtr != NULL && offset < index.byteIndex;
806 offset += segPtr->size, segPtr = segPtr->nextPtr) {
807 /* Empty loop body */ ;
808 }
809 }
810 }
811
812 while (1) {
813 /*
814 * segPtr points at the first possible candidate, or NULL if we ran
815 * off the end of the line.
816 */
817
818 for ( ; segPtr != NULL ; segPtr = segPtr->nextPtr) {
819 if (segPtr->typePtr == &tkTextRightMarkType ||
820 segPtr->typePtr == &tkTextLeftMarkType) {
821 if (segPtr == textPtr->currentMarkPtr) {
822 Tcl_SetResult(interp, "current", TCL_STATIC);
823 } else if (segPtr == textPtr->insertMarkPtr) {
824 Tcl_SetResult(interp, "insert", TCL_STATIC);
825 } else if (segPtr->body.mark.hPtr == NULL) {
826 /*
827 * Ignore widget-specific marks for the other widgets.
828 * This is either an insert or a current mark
829 * (markPtr->body.mark.hPtr actually receives NULL
830 * for these marks in TkTextSetMark).
831 * The insert and current marks for textPtr having
832 * already been tested above, the current segment is
833 * an insert or current mark from a peer of textPtr,
834 * which we don't want to return.
835 */
836 continue;
837 } else {
838 Tcl_SetResult(interp,
839 Tcl_GetHashKey(&textPtr->sharedTextPtr->markTable,
840 segPtr->body.mark.hPtr), TCL_STATIC);
841 }
842 return TCL_OK;
843 }
844 }
845 index.linePtr = TkBTreeNextLine(textPtr, index.linePtr);
846 if (index.linePtr == NULL) {
847 return TCL_OK;
848 }
849 index.byteIndex = 0;
850 segPtr = index.linePtr->segPtr;
851 }
852 }
853
854 /*
855 *--------------------------------------------------------------
856 *
857 * MarkFindPrev --
858 *
859 * This function searches backwards for the previous mark.
860 *
861 * Results:
862 * A standard Tcl result, which is a mark name or an empty string.
863 *
864 * Side effects:
865 * None.
866 *
867 *--------------------------------------------------------------
868 */
869
870 static int
MarkFindPrev(Tcl_Interp * interp,TkText * textPtr,const char * string)871 MarkFindPrev(
872 Tcl_Interp *interp, /* For error reporting */
873 TkText *textPtr, /* The widget */
874 const char *string) /* The starting index or mark name */
875 {
876 TkTextIndex index;
877 Tcl_HashEntry *hPtr;
878 register TkTextSegment *segPtr, *seg2Ptr, *prevPtr;
879 int offset;
880
881 if (!strcmp(string, "insert")) {
882 segPtr = textPtr->insertMarkPtr;
883 TkTextMarkSegToIndex(textPtr, segPtr, &index);
884 } else if (!strcmp(string, "current")) {
885 segPtr = textPtr->currentMarkPtr;
886 TkTextMarkSegToIndex(textPtr, segPtr, &index);
887 } else {
888 hPtr = Tcl_FindHashEntry(&textPtr->sharedTextPtr->markTable, string);
889 if (hPtr != NULL) {
890 /*
891 * If given a mark name, return the previous mark in the list of
892 * segments, even if it happens to be at the same character
893 * position.
894 */
895
896 segPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr);
897 TkTextMarkSegToIndex(textPtr, segPtr, &index);
898 } else {
899 /*
900 * For non-mark name indices we do not return any marks that are
901 * right at the index.
902 */
903
904 if (TkTextGetIndex(interp, textPtr, string, &index) != TCL_OK) {
905 return TCL_ERROR;
906 }
907 for (offset = 0, segPtr = index.linePtr->segPtr;
908 segPtr != NULL && offset < index.byteIndex;
909 offset += segPtr->size, segPtr = segPtr->nextPtr) {
910 /* Empty loop body */
911 }
912 }
913 }
914
915 while (1) {
916 /*
917 * segPtr points just past the first possible candidate, or at the
918 * beginning of the line.
919 */
920
921 for (prevPtr = NULL, seg2Ptr = index.linePtr->segPtr;
922 seg2Ptr != NULL && seg2Ptr != segPtr;
923 seg2Ptr = seg2Ptr->nextPtr) {
924 if (seg2Ptr->typePtr == &tkTextRightMarkType ||
925 seg2Ptr->typePtr == &tkTextLeftMarkType) {
926 if (seg2Ptr->body.mark.hPtr == NULL) {
927 if (seg2Ptr != textPtr->currentMarkPtr &&
928 seg2Ptr != textPtr->insertMarkPtr) {
929 /*
930 * This is an insert or current mark from a
931 * peer of textPtr.
932 */
933 continue;
934 }
935 }
936 prevPtr = seg2Ptr;
937 }
938 }
939 if (prevPtr != NULL) {
940 if (prevPtr == textPtr->currentMarkPtr) {
941 Tcl_SetResult(interp, "current", TCL_STATIC);
942 return TCL_OK;
943 } else if (prevPtr == textPtr->insertMarkPtr) {
944 Tcl_SetResult(interp, "insert", TCL_STATIC);
945 return TCL_OK;
946 } else if (prevPtr->body.mark.hPtr == NULL) {
947 /*
948 * Ignore widget-specific marks for the other widgets.
949 * This is either an insert or a current mark
950 * (markPtr->body.mark.hPtr actually receives NULL
951 * for these marks in TkTextSetMark).
952 * The insert and current marks for textPtr having
953 * already been tested above, the current segment is
954 * an insert or current mark from a peer of textPtr,
955 * which we don't want to return.
956 */
957 } else {
958 Tcl_SetResult(interp,
959 Tcl_GetHashKey(&textPtr->sharedTextPtr->markTable,
960 prevPtr->body.mark.hPtr), TCL_STATIC);
961 return TCL_OK;
962 }
963 }
964 index.linePtr = TkBTreePreviousLine(textPtr, index.linePtr);
965 if (index.linePtr == NULL) {
966 return TCL_OK;
967 }
968 segPtr = NULL;
969 }
970 }
971
972 /*
973 * Local Variables:
974 * mode: c
975 * c-basic-offset: 4
976 * fill-column: 78
977 * End:
978 */
979