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