1/*
2 * tkCanvText.c --
3 *
4 *	This file implements text items for canvas widgets.
5 *
6 * Copyright (c) 1991-1994 The Regents of the University of California.
7 * Copyright (c) 1994-1997 Sun Microsystems, Inc.
8 *
9 * See the file "license.terms" for information on usage and redistribution of
10 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
11 *
12 * RCS: @(#) $Id$
13 */
14
15#include <stdio.h>
16#include "tkInt.h"
17#include "tkCanvas.h"
18#include "default.h"
19
20/*
21 * The structure below defines the record for each text item.
22 */
23
24typedef struct TextItem {
25    Tk_Item header;		/* Generic stuff that's the same for all
26				 * types. MUST BE FIRST IN STRUCTURE. */
27    Tk_CanvasTextInfo *textInfoPtr;
28				/* Pointer to a structure containing
29				 * information about the selection and
30				 * insertion cursor. The structure is owned by
31				 * (and shared with) the generic canvas
32				 * code. */
33    /*
34     * Fields that are set by widget commands other than "configure".
35     */
36
37    double x, y;		/* Positioning point for text. */
38    int insertPos;		/* Character index of character just before
39				 * which the insertion cursor is displayed. */
40
41    /*
42     * Configuration settings that are updated by Tk_ConfigureWidget.
43     */
44
45    Tk_Anchor anchor;		/* Where to anchor text relative to (x,y). */
46    Tk_TSOffset tsoffset;
47    XColor *color;		/* Color for text. */
48    XColor *activeColor;	/* Color for text. */
49    XColor *disabledColor;	/* Color for text. */
50    Tk_Font tkfont;		/* Font for drawing text. */
51    Tk_Justify justify;		/* Justification mode for text. */
52    Pixmap stipple;		/* Stipple bitmap for text, or None. */
53    Pixmap activeStipple;	/* Stipple bitmap for text, or None. */
54    Pixmap disabledStipple;	/* Stipple bitmap for text, or None. */
55    char *text;			/* Text for item (malloc-ed). */
56    int width;			/* Width of lines for word-wrap, pixels. Zero
57				 * means no word-wrap. */
58    int underline;		/* Index of character to put underline beneath
59				 * or -1 for no underlining. */
60
61    /*
62     * Fields whose values are derived from the current values of the
63     * configuration settings above.
64     */
65
66    int numChars;		/* Length of text in characters. */
67    int numBytes;		/* Length of text in bytes. */
68    Tk_TextLayout textLayout;	/* Cached text layout information. */
69    int leftEdge;		/* Pixel location of the left edge of the text
70				 * item; where the left border of the text
71				 * layout is drawn. */
72    int rightEdge;		/* Pixel just to right of right edge of area
73				 * of text item. Used for selecting up to end
74				 * of line. */
75    GC gc;			/* Graphics context for drawing text. */
76    GC selTextGC;		/* Graphics context for selected text. */
77    GC cursorOffGC;		/* If not None, this gives a graphics context
78				 * to use to draw the insertion cursor when
79				 * it's off. Used if the selection and
80				 * insertion cursor colors are the same. */
81} TextItem;
82
83/*
84 * Information used for parsing configuration specs:
85 */
86
87static Tk_CustomOption stateOption = {
88    (Tk_OptionParseProc *) TkStateParseProc,
89    TkStatePrintProc, (ClientData) 2
90};
91static Tk_CustomOption tagsOption = {
92    (Tk_OptionParseProc *) Tk_CanvasTagsParseProc,
93    Tk_CanvasTagsPrintProc, (ClientData) NULL
94};
95static Tk_CustomOption offsetOption = {
96    (Tk_OptionParseProc *) TkOffsetParseProc,
97    TkOffsetPrintProc, (ClientData) (TK_OFFSET_RELATIVE)
98};
99
100static Tk_ConfigSpec configSpecs[] = {
101    {TK_CONFIG_COLOR, "-activefill", NULL, NULL,
102	NULL, Tk_Offset(TextItem, activeColor), TK_CONFIG_NULL_OK},
103    {TK_CONFIG_BITMAP, "-activestipple", NULL, NULL,
104	NULL, Tk_Offset(TextItem, activeStipple), TK_CONFIG_NULL_OK},
105    {TK_CONFIG_ANCHOR, "-anchor", NULL, NULL,
106	"center", Tk_Offset(TextItem, anchor), TK_CONFIG_DONT_SET_DEFAULT},
107    {TK_CONFIG_COLOR, "-disabledfill", NULL, NULL,
108	NULL, Tk_Offset(TextItem, disabledColor), TK_CONFIG_NULL_OK},
109    {TK_CONFIG_BITMAP, "-disabledstipple", NULL, NULL,
110	NULL, Tk_Offset(TextItem, disabledStipple), TK_CONFIG_NULL_OK},
111    {TK_CONFIG_COLOR, "-fill", NULL, NULL,
112	"black", Tk_Offset(TextItem, color), TK_CONFIG_NULL_OK},
113    {TK_CONFIG_FONT, "-font", NULL, NULL,
114	DEF_CANVTEXT_FONT, Tk_Offset(TextItem, tkfont), 0},
115    {TK_CONFIG_JUSTIFY, "-justify", NULL, NULL,
116	"left", Tk_Offset(TextItem, justify), TK_CONFIG_DONT_SET_DEFAULT},
117    {TK_CONFIG_CUSTOM, "-offset", NULL, NULL,
118	"0,0", Tk_Offset(TextItem, tsoffset),
119	TK_CONFIG_DONT_SET_DEFAULT, &offsetOption},
120    {TK_CONFIG_CUSTOM, "-state", NULL, NULL,
121	NULL, Tk_Offset(Tk_Item, state), TK_CONFIG_NULL_OK, &stateOption},
122    {TK_CONFIG_BITMAP, "-stipple", NULL, NULL,
123	NULL, Tk_Offset(TextItem, stipple), TK_CONFIG_NULL_OK},
124    {TK_CONFIG_CUSTOM, "-tags", NULL, NULL,
125	NULL, 0, TK_CONFIG_NULL_OK, &tagsOption},
126    {TK_CONFIG_STRING, "-text", NULL, NULL,
127	"", Tk_Offset(TextItem, text), 0},
128    {TK_CONFIG_INT, "-underline", NULL, NULL,
129	"-1", Tk_Offset(TextItem, underline), 0},
130    {TK_CONFIG_PIXELS, "-width", NULL, NULL,
131	"0", Tk_Offset(TextItem, width), TK_CONFIG_DONT_SET_DEFAULT},
132    {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
133};
134
135/*
136 * Prototypes for functions defined in this file:
137 */
138
139static void		ComputeTextBbox(Tk_Canvas canvas, TextItem *textPtr);
140static int		ConfigureText(Tcl_Interp *interp,
141			    Tk_Canvas canvas, Tk_Item *itemPtr, int argc,
142			    Tcl_Obj *CONST objv[], int flags);
143static int		CreateText(Tcl_Interp *interp,
144			    Tk_Canvas canvas, struct Tk_Item *itemPtr,
145			    int argc, Tcl_Obj *CONST objv[]);
146static void		DeleteText(Tk_Canvas canvas,
147			    Tk_Item *itemPtr, Display *display);
148static void		DisplayCanvText(Tk_Canvas canvas,
149			    Tk_Item *itemPtr, Display *display, Drawable dst,
150			    int x, int y, int width, int height);
151static int		GetSelText(Tk_Canvas canvas,
152			    Tk_Item *itemPtr, int offset, char *buffer,
153			    int maxBytes);
154static int		GetTextIndex(Tcl_Interp *interp,
155			    Tk_Canvas canvas, Tk_Item *itemPtr,
156			    Tcl_Obj *obj, int *indexPtr);
157static void		ScaleText(Tk_Canvas canvas,
158			    Tk_Item *itemPtr, double originX, double originY,
159			    double scaleX, double scaleY);
160static void		SetTextCursor(Tk_Canvas canvas,
161			    Tk_Item *itemPtr, int index);
162static int		TextCoords(Tcl_Interp *interp,
163			    Tk_Canvas canvas, Tk_Item *itemPtr,
164			    int argc, Tcl_Obj *CONST objv[]);
165static void		TextDeleteChars(Tk_Canvas canvas,
166			    Tk_Item *itemPtr, int first, int last);
167static void		TextInsert(Tk_Canvas canvas,
168			    Tk_Item *itemPtr, int beforeThis, char *string);
169static int		TextToArea(Tk_Canvas canvas,
170			    Tk_Item *itemPtr, double *rectPtr);
171static double		TextToPoint(Tk_Canvas canvas,
172			    Tk_Item *itemPtr, double *pointPtr);
173static int		TextToPostscript(Tcl_Interp *interp,
174			    Tk_Canvas canvas, Tk_Item *itemPtr, int prepass);
175static void		TranslateText(Tk_Canvas canvas,
176			    Tk_Item *itemPtr, double deltaX, double deltaY);
177
178/*
179 * The structures below defines the rectangle and oval item types by means of
180 * functions that can be invoked by generic item code.
181 */
182
183Tk_ItemType tkTextType = {
184    "text",			/* name */
185    sizeof(TextItem),		/* itemSize */
186    CreateText,			/* createProc */
187    configSpecs,		/* configSpecs */
188    ConfigureText,		/* configureProc */
189    TextCoords,			/* coordProc */
190    DeleteText,			/* deleteProc */
191    DisplayCanvText,		/* displayProc */
192    TK_CONFIG_OBJS,		/* flags */
193    TextToPoint,		/* pointProc */
194    TextToArea,			/* areaProc */
195    TextToPostscript,		/* postscriptProc */
196    ScaleText,			/* scaleProc */
197    TranslateText,		/* translateProc */
198    (Tk_ItemIndexProc *) GetTextIndex,/* indexProc */
199    SetTextCursor,		/* icursorProc */
200    GetSelText,			/* selectionProc */
201    TextInsert,			/* insertProc */
202    TextDeleteChars,		/* dTextProc */
203    NULL,			/* nextPtr */
204};
205
206/*
207 *--------------------------------------------------------------
208 *
209 * CreateText --
210 *
211 *	This function is invoked to create a new text item in a canvas.
212 *
213 * Results:
214 *	A standard Tcl return value. If an error occurred in creating the item
215 *	then an error message is left in the interp's result; in this case
216 *	itemPtr is left uninitialized so it can be safely freed by the caller.
217 *
218 * Side effects:
219 *	A new text item is created.
220 *
221 *--------------------------------------------------------------
222 */
223
224static int
225CreateText(
226    Tcl_Interp *interp,		/* Interpreter for error reporting. */
227    Tk_Canvas canvas,		/* Canvas to hold new item. */
228    Tk_Item *itemPtr,		/* Record to hold new item; header has been
229				 * initialized by caller. */
230    int objc,			/* Number of arguments in objv. */
231    Tcl_Obj *CONST objv[])	/* Arguments describing rectangle. */
232{
233    TextItem *textPtr = (TextItem *) itemPtr;
234    int i;
235
236    if (objc == 0) {
237	Tcl_Panic("canvas did not pass any coords\n");
238    }
239
240    /*
241     * Carry out initialization that is needed in order to clean up after
242     * errors during the the remainder of this function.
243     */
244
245    textPtr->textInfoPtr = Tk_CanvasGetTextInfo(canvas);
246
247    textPtr->insertPos	= 0;
248
249    textPtr->anchor	= TK_ANCHOR_CENTER;
250    textPtr->tsoffset.flags = 0;
251    textPtr->tsoffset.xoffset = 0;
252    textPtr->tsoffset.yoffset = 0;
253    textPtr->color	= NULL;
254    textPtr->activeColor = NULL;
255    textPtr->disabledColor = NULL;
256    textPtr->tkfont	= NULL;
257    textPtr->justify	= TK_JUSTIFY_LEFT;
258    textPtr->stipple	= None;
259    textPtr->activeStipple = None;
260    textPtr->disabledStipple = None;
261    textPtr->text	= NULL;
262    textPtr->width	= 0;
263    textPtr->underline	= -1;
264
265    textPtr->numChars	= 0;
266    textPtr->numBytes	= 0;
267    textPtr->textLayout = NULL;
268    textPtr->leftEdge	= 0;
269    textPtr->rightEdge	= 0;
270    textPtr->gc		= None;
271    textPtr->selTextGC	= None;
272    textPtr->cursorOffGC = None;
273
274    /*
275     * Process the arguments to fill in the item record. Only 1 (list) or 2 (x
276     * y) coords are allowed.
277     */
278
279    if (objc == 1) {
280	i = 1;
281    } else {
282	char *arg = Tcl_GetString(objv[1]);
283
284	i = 2;
285	if ((arg[0] == '-') && (arg[1] >= 'a') && (arg[1] <= 'z')) {
286	    i = 1;
287	}
288    }
289    if ((TextCoords(interp, canvas, itemPtr, i, objv) != TCL_OK)) {
290	goto error;
291    }
292    if (ConfigureText(interp, canvas, itemPtr, objc-i, objv+i, 0) == TCL_OK) {
293	return TCL_OK;
294    }
295
296  error:
297    DeleteText(canvas, itemPtr, Tk_Display(Tk_CanvasTkwin(canvas)));
298    return TCL_ERROR;
299}
300
301/*
302 *--------------------------------------------------------------
303 *
304 * TextCoords --
305 *
306 *	This function is invoked to process the "coords" widget command on
307 *	text items. See the user documentation for details on what it does.
308 *
309 * Results:
310 *	Returns TCL_OK or TCL_ERROR, and sets the interp's result.
311 *
312 * Side effects:
313 *	The coordinates for the given item may be changed.
314 *
315 *--------------------------------------------------------------
316 */
317
318static int
319TextCoords(
320    Tcl_Interp *interp,		/* Used for error reporting. */
321    Tk_Canvas canvas,		/* Canvas containing item. */
322    Tk_Item *itemPtr,		/* Item whose coordinates are to be read or
323				 * modified. */
324    int objc,			/* Number of coordinates supplied in objv. */
325    Tcl_Obj *CONST objv[])	/* Array of coordinates: x1, y1, x2, y2, ... */
326{
327    TextItem *textPtr = (TextItem *) itemPtr;
328
329    if (objc == 0) {
330	Tcl_Obj *obj = Tcl_NewObj();
331
332	Tcl_Obj *subobj = Tcl_NewDoubleObj(textPtr->x);
333	Tcl_ListObjAppendElement(interp, obj, subobj);
334	subobj = Tcl_NewDoubleObj(textPtr->y);
335	Tcl_ListObjAppendElement(interp, obj, subobj);
336	Tcl_SetObjResult(interp, obj);
337    } else if (objc < 3) {
338	if (objc==1) {
339	    if (Tcl_ListObjGetElements(interp, objv[0], &objc,
340		    (Tcl_Obj ***) &objv) != TCL_OK) {
341		return TCL_ERROR;
342	    } else if (objc != 2) {
343		char buf[64 + TCL_INTEGER_SPACE];
344
345		sprintf(buf, "wrong # coordinates: expected 2, got %d", objc);
346		Tcl_SetResult(interp, buf, TCL_VOLATILE);
347		return TCL_ERROR;
348	    }
349	}
350	if ((Tk_CanvasGetCoordFromObj(interp, canvas, objv[0],
351		    &textPtr->x) != TCL_OK)
352		|| (Tk_CanvasGetCoordFromObj(interp, canvas, objv[1],
353		    &textPtr->y) != TCL_OK)) {
354	    return TCL_ERROR;
355	}
356	ComputeTextBbox(canvas, textPtr);
357    } else {
358	char buf[64 + TCL_INTEGER_SPACE];
359
360	sprintf(buf, "wrong # coordinates: expected 0 or 2, got %d", objc);
361	Tcl_SetResult(interp, buf, TCL_VOLATILE);
362	return TCL_ERROR;
363    }
364    return TCL_OK;
365}
366
367/*
368 *--------------------------------------------------------------
369 *
370 * ConfigureText --
371 *
372 *	This function is invoked to configure various aspects of a text item,
373 *	such as its border and background colors.
374 *
375 * Results:
376 *	A standard Tcl result code. If an error occurs, then an error message
377 *	is left in the interp's result.
378 *
379 * Side effects:
380 *	Configuration information, such as colors and stipple patterns, may be
381 *	set for itemPtr.
382 *
383 *--------------------------------------------------------------
384 */
385
386static int
387ConfigureText(
388    Tcl_Interp *interp,		/* Interpreter for error reporting. */
389    Tk_Canvas canvas,		/* Canvas containing itemPtr. */
390    Tk_Item *itemPtr,		/* Rectangle item to reconfigure. */
391    int objc,			/* Number of elements in objv. */
392    Tcl_Obj *CONST objv[],	/* Arguments describing things to configure. */
393    int flags)			/* Flags to pass to Tk_ConfigureWidget. */
394{
395    TextItem *textPtr = (TextItem *) itemPtr;
396    XGCValues gcValues;
397    GC newGC, newSelGC;
398    unsigned long mask;
399    Tk_Window tkwin;
400    Tk_CanvasTextInfo *textInfoPtr = textPtr->textInfoPtr;
401    XColor *selBgColorPtr;
402    XColor *color;
403    Pixmap stipple;
404    Tk_State state;
405
406    tkwin = Tk_CanvasTkwin(canvas);
407    if (TCL_OK != Tk_ConfigureWidget(interp, tkwin, configSpecs, objc,
408	    (CONST char **) objv, (char *) textPtr, flags|TK_CONFIG_OBJS)) {
409	return TCL_ERROR;
410    }
411
412    /*
413     * A few of the options require additional processing, such as graphics
414     * contexts.
415     */
416
417    state = itemPtr->state;
418
419    if (textPtr->activeColor != NULL || textPtr->activeStipple != None) {
420	itemPtr->redraw_flags |= TK_ITEM_STATE_DEPENDANT;
421    } else {
422	itemPtr->redraw_flags &= ~TK_ITEM_STATE_DEPENDANT;
423    }
424
425    if(state == TK_STATE_NULL) {
426	state = ((TkCanvas *)canvas)->canvas_state;
427    }
428
429    color = textPtr->color;
430    stipple = textPtr->stipple;
431    if (((TkCanvas *)canvas)->currentItemPtr == itemPtr) {
432	if (textPtr->activeColor!=NULL) {
433	    color = textPtr->activeColor;
434	}
435	if (textPtr->activeStipple!=None) {
436	    stipple = textPtr->activeStipple;
437	}
438    } else if (state==TK_STATE_DISABLED) {
439	if (textPtr->disabledColor!=NULL) {
440	    color = textPtr->disabledColor;
441	}
442	if (textPtr->disabledStipple!=None) {
443	    stipple = textPtr->disabledStipple;
444	}
445    }
446
447    newGC = newSelGC = None;
448    if (textPtr->tkfont != NULL) {
449	gcValues.font = Tk_FontId(textPtr->tkfont);
450	mask = GCFont;
451	if (color != NULL) {
452	    gcValues.foreground = color->pixel;
453	    mask |= GCForeground;
454	    if (stipple != None) {
455		gcValues.stipple = stipple;
456		gcValues.fill_style = FillStippled;
457		mask |= GCStipple|GCFillStyle;
458	    }
459	    newGC = Tk_GetGC(tkwin, mask, &gcValues);
460	}
461	mask &= ~(GCTile|GCFillStyle|GCStipple);
462	if (stipple != None) {
463	    gcValues.stipple = stipple;
464	    gcValues.fill_style = FillStippled;
465	    mask |= GCStipple|GCFillStyle;
466	}
467	if (textInfoPtr->selFgColorPtr != NULL) {
468	    gcValues.foreground = textInfoPtr->selFgColorPtr->pixel;
469	}
470	newSelGC = Tk_GetGC(tkwin, mask|GCForeground, &gcValues);
471    }
472    if (textPtr->gc != None) {
473	Tk_FreeGC(Tk_Display(tkwin), textPtr->gc);
474    }
475    textPtr->gc = newGC;
476    if (textPtr->selTextGC != None) {
477	Tk_FreeGC(Tk_Display(tkwin), textPtr->selTextGC);
478    }
479    textPtr->selTextGC = newSelGC;
480
481    selBgColorPtr = Tk_3DBorderColor(textInfoPtr->selBorder);
482    if (Tk_3DBorderColor(textInfoPtr->insertBorder)->pixel
483	    == selBgColorPtr->pixel) {
484	if (selBgColorPtr->pixel == BlackPixelOfScreen(Tk_Screen(tkwin))) {
485	    gcValues.foreground = WhitePixelOfScreen(Tk_Screen(tkwin));
486	} else {
487	    gcValues.foreground = BlackPixelOfScreen(Tk_Screen(tkwin));
488	}
489	newGC = Tk_GetGC(tkwin, GCForeground, &gcValues);
490    } else {
491	newGC = None;
492    }
493    if (textPtr->cursorOffGC != None) {
494	Tk_FreeGC(Tk_Display(tkwin), textPtr->cursorOffGC);
495    }
496    textPtr->cursorOffGC = newGC;
497
498
499    /*
500     * If the text was changed, move the selection and insertion indices to
501     * keep them inside the item.
502     */
503
504    textPtr->numBytes = strlen(textPtr->text);
505    textPtr->numChars = Tcl_NumUtfChars(textPtr->text, textPtr->numBytes);
506    if (textInfoPtr->selItemPtr == itemPtr) {
507
508	if (textInfoPtr->selectFirst >= textPtr->numChars) {
509	    textInfoPtr->selItemPtr = NULL;
510	} else {
511	    if (textInfoPtr->selectLast >= textPtr->numChars) {
512		textInfoPtr->selectLast = textPtr->numChars - 1;
513	    }
514	    if ((textInfoPtr->anchorItemPtr == itemPtr)
515		    && (textInfoPtr->selectAnchor >= textPtr->numChars)) {
516		textInfoPtr->selectAnchor = textPtr->numChars - 1;
517	    }
518	}
519    }
520    if (textPtr->insertPos >= textPtr->numChars) {
521	textPtr->insertPos = textPtr->numChars;
522    }
523
524    ComputeTextBbox(canvas, textPtr);
525    return TCL_OK;
526}
527
528/*
529 *--------------------------------------------------------------
530 *
531 * DeleteText --
532 *
533 *	This function is called to clean up the data structure associated with
534 *	a text item.
535 *
536 * Results:
537 *	None.
538 *
539 * Side effects:
540 *	Resources associated with itemPtr are released.
541 *
542 *--------------------------------------------------------------
543 */
544
545static void
546DeleteText(
547    Tk_Canvas canvas,		/* Info about overall canvas widget. */
548    Tk_Item *itemPtr,		/* Item that is being deleted. */
549    Display *display)		/* Display containing window for canvas. */
550{
551    TextItem *textPtr = (TextItem *) itemPtr;
552
553    if (textPtr->color != NULL) {
554	Tk_FreeColor(textPtr->color);
555    }
556    if (textPtr->activeColor != NULL) {
557	Tk_FreeColor(textPtr->activeColor);
558    }
559    if (textPtr->disabledColor != NULL) {
560	Tk_FreeColor(textPtr->disabledColor);
561    }
562    Tk_FreeFont(textPtr->tkfont);
563    if (textPtr->stipple != None) {
564	Tk_FreeBitmap(display, textPtr->stipple);
565    }
566    if (textPtr->activeStipple != None) {
567	Tk_FreeBitmap(display, textPtr->activeStipple);
568    }
569    if (textPtr->disabledStipple != None) {
570	Tk_FreeBitmap(display, textPtr->disabledStipple);
571    }
572    if (textPtr->text != NULL) {
573	ckfree(textPtr->text);
574    }
575
576    Tk_FreeTextLayout(textPtr->textLayout);
577    if (textPtr->gc != None) {
578	Tk_FreeGC(display, textPtr->gc);
579    }
580    if (textPtr->selTextGC != None) {
581	Tk_FreeGC(display, textPtr->selTextGC);
582    }
583    if (textPtr->cursorOffGC != None) {
584	Tk_FreeGC(display, textPtr->cursorOffGC);
585    }
586}
587
588/*
589 *--------------------------------------------------------------
590 *
591 * ComputeTextBbox --
592 *
593 *	This function is invoked to compute the bounding box of all the pixels
594 *	that may be drawn as part of a text item. In addition, it recomputes
595 *	all of the geometry information used to display a text item or check
596 *	for mouse hits.
597 *
598 * Results:
599 *	None.
600 *
601 * Side effects:
602 *	The fields x1, y1, x2, and y2 are updated in the header for itemPtr,
603 *	and the linePtr structure is regenerated for itemPtr.
604 *
605 *--------------------------------------------------------------
606 */
607
608static void
609ComputeTextBbox(
610    Tk_Canvas canvas,		/* Canvas that contains item. */
611    TextItem *textPtr)		/* Item whose bbox is to be recomputed. */
612{
613    Tk_CanvasTextInfo *textInfoPtr;
614    int leftX, topY, width, height, fudge;
615    Tk_State state = textPtr->header.state;
616
617    if(state == TK_STATE_NULL) {
618	state = ((TkCanvas *)canvas)->canvas_state;
619    }
620
621    Tk_FreeTextLayout(textPtr->textLayout);
622    textPtr->textLayout = Tk_ComputeTextLayout(textPtr->tkfont,
623	    textPtr->text, textPtr->numChars, textPtr->width,
624	    textPtr->justify, 0, &width, &height);
625
626    if (state == TK_STATE_HIDDEN || textPtr->color == NULL) {
627	width = height = 0;
628    }
629
630    /*
631     * Use overall geometry information to compute the top-left corner of the
632     * bounding box for the text item.
633     */
634
635    leftX = (int) floor(textPtr->x + 0.5);
636    topY = (int) floor(textPtr->y + 0.5);
637    switch (textPtr->anchor) {
638    case TK_ANCHOR_NW:
639    case TK_ANCHOR_N:
640    case TK_ANCHOR_NE:
641	break;
642
643    case TK_ANCHOR_W:
644    case TK_ANCHOR_CENTER:
645    case TK_ANCHOR_E:
646	topY -= height / 2;
647	break;
648
649    case TK_ANCHOR_SW:
650    case TK_ANCHOR_S:
651    case TK_ANCHOR_SE:
652	topY -= height;
653	break;
654    }
655    switch (textPtr->anchor) {
656    case TK_ANCHOR_NW:
657    case TK_ANCHOR_W:
658    case TK_ANCHOR_SW:
659	break;
660
661    case TK_ANCHOR_N:
662    case TK_ANCHOR_CENTER:
663    case TK_ANCHOR_S:
664	leftX -= width / 2;
665	break;
666
667    case TK_ANCHOR_NE:
668    case TK_ANCHOR_E:
669    case TK_ANCHOR_SE:
670	leftX -= width;
671	break;
672    }
673
674    textPtr->leftEdge = leftX;
675    textPtr->rightEdge = leftX + width;
676
677    /*
678     * Last of all, update the bounding box for the item. The item's bounding
679     * box includes the bounding box of all its lines, plus an extra fudge
680     * factor for the cursor border (which could potentially be quite large).
681     */
682
683    textInfoPtr = textPtr->textInfoPtr;
684    fudge = (textInfoPtr->insertWidth + 1) / 2;
685    if (textInfoPtr->selBorderWidth > fudge) {
686	fudge = textInfoPtr->selBorderWidth;
687    }
688    textPtr->header.x1 = leftX - fudge;
689    textPtr->header.y1 = topY;
690    textPtr->header.x2 = leftX + width + fudge;
691    textPtr->header.y2 = topY + height;
692}
693
694/*
695 *--------------------------------------------------------------
696 *
697 * DisplayCanvText --
698 *
699 *	This function is invoked to draw a text item in a given drawable.
700 *
701 * Results:
702 *	None.
703 *
704 * Side effects:
705 *	ItemPtr is drawn in drawable using the transformation information in
706 *	canvas.
707 *
708 *--------------------------------------------------------------
709 */
710
711static void
712DisplayCanvText(
713    Tk_Canvas canvas,		/* Canvas that contains item. */
714    Tk_Item *itemPtr,		/* Item to be displayed. */
715    Display *display,		/* Display on which to draw item. */
716    Drawable drawable,		/* Pixmap or window in which to draw item. */
717    int x, int y, int width, int height)
718				/* Describes region of canvas that must be
719				 * redisplayed (not used). */
720{
721    TextItem *textPtr;
722    Tk_CanvasTextInfo *textInfoPtr;
723    int selFirstChar, selLastChar;
724    short drawableX, drawableY;
725    Pixmap stipple;
726    Tk_State state = itemPtr->state;
727
728    textPtr = (TextItem *) itemPtr;
729    textInfoPtr = textPtr->textInfoPtr;
730
731    if(state == TK_STATE_NULL) {
732	state = ((TkCanvas *)canvas)->canvas_state;
733    }
734    stipple = textPtr->stipple;
735    if (((TkCanvas *)canvas)->currentItemPtr == itemPtr) {
736	if (textPtr->activeStipple!=None) {
737	    stipple = textPtr->activeStipple;
738	}
739    } else if (state==TK_STATE_DISABLED) {
740	if (textPtr->disabledStipple!=None) {
741	    stipple = textPtr->disabledStipple;
742	}
743    }
744
745    if (textPtr->gc == None) {
746	return;
747    }
748
749    /*
750     * If we're stippling, then modify the stipple offset in the GC. Be sure
751     * to reset the offset when done, since the GC is supposed to be
752     * read-only.
753     */
754
755    if (stipple != None) {
756	Tk_CanvasSetOffset(canvas, textPtr->gc, &textPtr->tsoffset);
757    }
758
759    selFirstChar = -1;
760    selLastChar = 0;		/* lint. */
761
762    if (textInfoPtr->selItemPtr == itemPtr) {
763	selFirstChar = textInfoPtr->selectFirst;
764	selLastChar = textInfoPtr->selectLast;
765	if (selLastChar > textPtr->numChars) {
766	    selLastChar = textPtr->numChars - 1;
767	}
768	if ((selFirstChar >= 0) && (selFirstChar <= selLastChar)) {
769	    int xFirst, yFirst, hFirst;
770	    int xLast, yLast, wLast;
771
772	    /*
773	     * Draw a special background under the selection.
774	     */
775
776	    Tk_CharBbox(textPtr->textLayout, selFirstChar, &xFirst, &yFirst,
777		    NULL, &hFirst);
778	    Tk_CharBbox(textPtr->textLayout, selLastChar, &xLast, &yLast,
779		    &wLast, NULL);
780
781	    /*
782	     * If the selection spans the end of this line, then display
783	     * selection background all the way to the end of the line.
784	     * However, for the last line we only want to display up to the
785	     * last character, not the end of the line.
786	     */
787
788	    x = xFirst;
789	    height = hFirst;
790	    for (y = yFirst ; y <= yLast; y += height) {
791		if (y == yLast) {
792		    width = xLast + wLast - x;
793		} else {
794		    width = textPtr->rightEdge - textPtr->leftEdge - x;
795		}
796		Tk_CanvasDrawableCoords(canvas,
797			(double) (textPtr->leftEdge + x
798				- textInfoPtr->selBorderWidth),
799			(double) (textPtr->header.y1 + y),
800			&drawableX, &drawableY);
801		Tk_Fill3DRectangle(Tk_CanvasTkwin(canvas), drawable,
802			textInfoPtr->selBorder, drawableX, drawableY,
803			width + 2 * textInfoPtr->selBorderWidth,
804			height, textInfoPtr->selBorderWidth, TK_RELIEF_RAISED);
805		x = 0;
806	    }
807	}
808    }
809
810    /*
811     * If the insertion point should be displayed, then draw a special
812     * background for the cursor before drawing the text. Note: if we're the
813     * cursor item but the cursor is turned off, then redraw background over
814     * the area of the cursor. This guarantees that the selection won't make
815     * the cursor invisible on mono displays, where both are drawn in the same
816     * color.
817     */
818
819    if ((textInfoPtr->focusItemPtr == itemPtr) && (textInfoPtr->gotFocus)) {
820	if (Tk_CharBbox(textPtr->textLayout, textPtr->insertPos,
821		&x, &y, NULL, &height)) {
822	    Tk_CanvasDrawableCoords(canvas,
823		    (double) (textPtr->leftEdge + x
824			    - (textInfoPtr->insertWidth / 2)),
825		    (double) (textPtr->header.y1 + y),
826		    &drawableX, &drawableY);
827	    Tk_SetCaretPos(Tk_CanvasTkwin(canvas), drawableX, drawableY,
828		    height);
829	    if (textInfoPtr->cursorOn) {
830		Tk_Fill3DRectangle(Tk_CanvasTkwin(canvas), drawable,
831			textInfoPtr->insertBorder,
832			drawableX, drawableY,
833			textInfoPtr->insertWidth, height,
834			textInfoPtr->insertBorderWidth, TK_RELIEF_RAISED);
835	    } else if (textPtr->cursorOffGC != None) {
836		/*
837		 * Redraw the background over the area of the cursor, even
838		 * though the cursor is turned off. This guarantees that the
839		 * selection won't make the cursor invisible on mono displays,
840		 * where both may be drawn in the same color.
841		 */
842
843		XFillRectangle(display, drawable, textPtr->cursorOffGC,
844			drawableX, drawableY,
845			(unsigned) textInfoPtr->insertWidth,
846			(unsigned) height);
847	    }
848	}
849    }
850
851    /*
852     * If there is no selected text or the selected text foreground is the
853     * same as the regular text foreground, then draw one text string. If
854     * there is selected text and the foregrounds differ, draw the regular
855     * text up to the selection, draw the selection, then draw the rest of the
856     * regular text. Drawing the regular text and then the selected text over
857     * it would causes problems with anti-aliased text because the two
858     * anti-aliasing colors would blend together.
859     */
860
861    Tk_CanvasDrawableCoords(canvas, (double) textPtr->leftEdge,
862	    (double) textPtr->header.y1, &drawableX, &drawableY);
863
864    if ((selFirstChar >= 0) && (textPtr->selTextGC != textPtr->gc)) {
865	Tk_DrawTextLayout(display, drawable, textPtr->gc, textPtr->textLayout,
866	    drawableX, drawableY, 0, selFirstChar);
867	Tk_DrawTextLayout(display, drawable, textPtr->selTextGC,
868	    textPtr->textLayout, drawableX, drawableY, selFirstChar,
869	    selLastChar + 1);
870	Tk_DrawTextLayout(display, drawable, textPtr->gc, textPtr->textLayout,
871	    drawableX, drawableY, selLastChar + 1, -1);
872    } else {
873	Tk_DrawTextLayout(display, drawable, textPtr->gc, textPtr->textLayout,
874	    drawableX, drawableY, 0, -1);
875    }
876    Tk_UnderlineTextLayout(display, drawable, textPtr->gc, textPtr->textLayout,
877	    drawableX, drawableY, textPtr->underline);
878
879    if (stipple != None) {
880	XSetTSOrigin(display, textPtr->gc, 0, 0);
881    }
882}
883
884/*
885 *--------------------------------------------------------------
886 *
887 * TextInsert --
888 *
889 *	Insert characters into a text item at a given position.
890 *
891 * Results:
892 *	None.
893 *
894 * Side effects:
895 *	The text in the given item is modified. The cursor and selection
896 *	positions are also modified to reflect the insertion.
897 *
898 *--------------------------------------------------------------
899 */
900
901static void
902TextInsert(
903    Tk_Canvas canvas,		/* Canvas containing text item. */
904    Tk_Item *itemPtr,		/* Text item to be modified. */
905    int index,			/* Character index before which string is to
906				 * be inserted. */
907    char *string)		/* New characters to be inserted. */
908{
909    TextItem *textPtr = (TextItem *) itemPtr;
910    int byteIndex, byteCount, charsAdded;
911    char *newStr, *text;
912    Tk_CanvasTextInfo *textInfoPtr = textPtr->textInfoPtr;
913
914    string = Tcl_GetStringFromObj((Tcl_Obj *) string, &byteCount);
915
916    text = textPtr->text;
917
918    if (index < 0) {
919	index = 0;
920    }
921    if (index > textPtr->numChars) {
922	index = textPtr->numChars;
923    }
924    byteIndex = Tcl_UtfAtIndex(text, index) - text;
925    byteCount = strlen(string);
926    if (byteCount == 0) {
927	return;
928    }
929
930    newStr = (char *) ckalloc((unsigned) textPtr->numBytes + byteCount + 1);
931    memcpy(newStr, text, (size_t) byteIndex);
932    strcpy(newStr + byteIndex, string);
933    strcpy(newStr + byteIndex + byteCount, text + byteIndex);
934
935    ckfree(text);
936    textPtr->text = newStr;
937    charsAdded = Tcl_NumUtfChars(string, byteCount);
938    textPtr->numChars += charsAdded;
939    textPtr->numBytes += byteCount;
940
941    /*
942     * Inserting characters invalidates indices such as those for the
943     * selection and cursor. Update the indices appropriately.
944     */
945
946    if (textInfoPtr->selItemPtr == itemPtr) {
947	if (textInfoPtr->selectFirst >= index) {
948	    textInfoPtr->selectFirst += charsAdded;
949	}
950	if (textInfoPtr->selectLast >= index) {
951	    textInfoPtr->selectLast += charsAdded;
952	}
953	if ((textInfoPtr->anchorItemPtr == itemPtr)
954		&& (textInfoPtr->selectAnchor >= index)) {
955	    textInfoPtr->selectAnchor += charsAdded;
956	}
957    }
958    if (textPtr->insertPos >= index) {
959	textPtr->insertPos += charsAdded;
960    }
961    ComputeTextBbox(canvas, textPtr);
962}
963
964/*
965 *--------------------------------------------------------------
966 *
967 * TextDeleteChars --
968 *
969 *	Delete one or more characters from a text item.
970 *
971 * Results:
972 *	None.
973 *
974 * Side effects:
975 *	Characters between "first" and "last", inclusive, get deleted from
976 *	itemPtr, and things like the selection position get updated.
977 *
978 *--------------------------------------------------------------
979 */
980
981static void
982TextDeleteChars(
983    Tk_Canvas canvas,		/* Canvas containing itemPtr. */
984    Tk_Item *itemPtr,		/* Item in which to delete characters. */
985    int first,			/* Character index of first character to
986				 * delete. */
987    int last)			/* Character index of last character to delete
988				 * (inclusive). */
989{
990    TextItem *textPtr = (TextItem *) itemPtr;
991    int byteIndex, byteCount, charsRemoved;
992    char *newStr, *text;
993    Tk_CanvasTextInfo *textInfoPtr = textPtr->textInfoPtr;
994
995    text = textPtr->text;
996    if (first < 0) {
997	first = 0;
998    }
999    if (last >= textPtr->numChars) {
1000	last = textPtr->numChars - 1;
1001    }
1002    if (first > last) {
1003	return;
1004    }
1005    charsRemoved = last + 1 - first;
1006
1007    byteIndex = Tcl_UtfAtIndex(text, first) - text;
1008    byteCount = Tcl_UtfAtIndex(text + byteIndex, charsRemoved)
1009	- (text + byteIndex);
1010
1011    newStr = (char *) ckalloc((unsigned) (textPtr->numBytes + 1 - byteCount));
1012    memcpy(newStr, text, (size_t) byteIndex);
1013    strcpy(newStr + byteIndex, text + byteIndex + byteCount);
1014
1015    ckfree(text);
1016    textPtr->text = newStr;
1017    textPtr->numChars -= charsRemoved;
1018    textPtr->numBytes -= byteCount;
1019
1020    /*
1021     * Update indexes for the selection and cursor to reflect the renumbering
1022     * of the remaining characters.
1023     */
1024
1025    if (textInfoPtr->selItemPtr == itemPtr) {
1026	if (textInfoPtr->selectFirst > first) {
1027	    textInfoPtr->selectFirst -= charsRemoved;
1028	    if (textInfoPtr->selectFirst < first) {
1029		textInfoPtr->selectFirst = first;
1030	    }
1031	}
1032	if (textInfoPtr->selectLast >= first) {
1033	    textInfoPtr->selectLast -= charsRemoved;
1034	    if (textInfoPtr->selectLast < first - 1) {
1035		textInfoPtr->selectLast = first - 1;
1036	    }
1037	}
1038	if (textInfoPtr->selectFirst > textInfoPtr->selectLast) {
1039	    textInfoPtr->selItemPtr = NULL;
1040	}
1041	if ((textInfoPtr->anchorItemPtr == itemPtr)
1042		&& (textInfoPtr->selectAnchor > first)) {
1043	    textInfoPtr->selectAnchor -= charsRemoved;
1044	    if (textInfoPtr->selectAnchor < first) {
1045		textInfoPtr->selectAnchor = first;
1046	    }
1047	}
1048    }
1049    if (textPtr->insertPos > first) {
1050	textPtr->insertPos -= charsRemoved;
1051	if (textPtr->insertPos < first) {
1052	    textPtr->insertPos = first;
1053	}
1054    }
1055    ComputeTextBbox(canvas, textPtr);
1056    return;
1057}
1058
1059/*
1060 *--------------------------------------------------------------
1061 *
1062 * TextToPoint --
1063 *
1064 *	Computes the distance from a given point to a given text item, in
1065 *	canvas units.
1066 *
1067 * Results:
1068 *	The return value is 0 if the point whose x and y coordinates are
1069 *	pointPtr[0] and pointPtr[1] is inside the text item. If the point
1070 *	isn't inside the text item then the return value is the distance from
1071 *	the point to the text item.
1072 *
1073 * Side effects:
1074 *	None.
1075 *
1076 *--------------------------------------------------------------
1077 */
1078
1079static double
1080TextToPoint(
1081    Tk_Canvas canvas,		/* Canvas containing itemPtr. */
1082    Tk_Item *itemPtr,		/* Item to check against point. */
1083    double *pointPtr)		/* Pointer to x and y coordinates. */
1084{
1085    TextItem *textPtr;
1086    Tk_State state = itemPtr->state;
1087    double value;
1088
1089    if (state == TK_STATE_NULL) {
1090	state = ((TkCanvas *)canvas)->canvas_state;
1091    }
1092    textPtr = (TextItem *) itemPtr;
1093    value = (double) Tk_DistanceToTextLayout(textPtr->textLayout,
1094	    (int) pointPtr[0] - textPtr->leftEdge,
1095	    (int) pointPtr[1] - textPtr->header.y1);
1096
1097    if ((state == TK_STATE_HIDDEN) || (textPtr->color == NULL) ||
1098	    (textPtr->text == NULL) || (*textPtr->text == 0)) {
1099	value = 1.0e36;
1100    }
1101    return value;
1102}
1103
1104/*
1105 *--------------------------------------------------------------
1106 *
1107 * TextToArea --
1108 *
1109 *	This function is called to determine whether an item lies entirely
1110 *	inside, entirely outside, or overlapping a given rectangle.
1111 *
1112 * Results:
1113 *	-1 is returned if the item is entirely outside the area given by
1114 *	rectPtr, 0 if it overlaps, and 1 if it is entirely inside the given
1115 *	area.
1116 *
1117 * Side effects:
1118 *	None.
1119 *
1120 *--------------------------------------------------------------
1121 */
1122
1123static int
1124TextToArea(
1125    Tk_Canvas canvas,		/* Canvas containing itemPtr. */
1126    Tk_Item *itemPtr,		/* Item to check against rectangle. */
1127    double *rectPtr)		/* Pointer to array of four coordinates
1128				 * (x1,y1,x2,y2) describing rectangular
1129				 * area. */
1130{
1131    TextItem *textPtr;
1132    Tk_State state = itemPtr->state;
1133
1134    if (state == TK_STATE_NULL) {
1135	state = ((TkCanvas *)canvas)->canvas_state;
1136    }
1137
1138    textPtr = (TextItem *) itemPtr;
1139    return Tk_IntersectTextLayout(textPtr->textLayout,
1140	    (int) (rectPtr[0] + 0.5) - textPtr->leftEdge,
1141	    (int) (rectPtr[1] + 0.5) - textPtr->header.y1,
1142	    (int) (rectPtr[2] - rectPtr[0] + 0.5),
1143	    (int) (rectPtr[3] - rectPtr[1] + 0.5));
1144}
1145
1146/*
1147 *--------------------------------------------------------------
1148 *
1149 * ScaleText --
1150 *
1151 *	This function is invoked to rescale a text item.
1152 *
1153 * Results:
1154 *	None.
1155 *
1156 * Side effects:
1157 *	Scales the position of the text, but not the size of the font for the
1158 *	text.
1159 *
1160 *--------------------------------------------------------------
1161 */
1162
1163	/* ARGSUSED */
1164static void
1165ScaleText(
1166    Tk_Canvas canvas,		/* Canvas containing rectangle. */
1167    Tk_Item *itemPtr,		/* Rectangle to be scaled. */
1168    double originX, double originY,
1169				/* Origin about which to scale rect. */
1170    double scaleX,		/* Amount to scale in X direction. */
1171    double scaleY)		/* Amount to scale in Y direction. */
1172{
1173    TextItem *textPtr = (TextItem *) itemPtr;
1174
1175    textPtr->x = originX + scaleX*(textPtr->x - originX);
1176    textPtr->y = originY + scaleY*(textPtr->y - originY);
1177    ComputeTextBbox(canvas, textPtr);
1178    return;
1179}
1180
1181/*
1182 *--------------------------------------------------------------
1183 *
1184 * TranslateText --
1185 *
1186 *	This function is called to move a text item by a given amount.
1187 *
1188 * Results:
1189 *	None.
1190 *
1191 * Side effects:
1192 *	The position of the text item is offset by (xDelta, yDelta), and the
1193 *	bounding box is updated in the generic part of the item structure.
1194 *
1195 *--------------------------------------------------------------
1196 */
1197
1198static void
1199TranslateText(
1200    Tk_Canvas canvas,		/* Canvas containing item. */
1201    Tk_Item *itemPtr,		/* Item that is being moved. */
1202    double deltaX, double deltaY)
1203				/* Amount by which item is to be moved. */
1204{
1205    TextItem *textPtr = (TextItem *) itemPtr;
1206
1207    textPtr->x += deltaX;
1208    textPtr->y += deltaY;
1209    ComputeTextBbox(canvas, textPtr);
1210}
1211
1212/*
1213 *--------------------------------------------------------------
1214 *
1215 * GetTextIndex --
1216 *
1217 *	Parse an index into a text item and return either its value or an
1218 *	error.
1219 *
1220 * Results:
1221 *	A standard Tcl result. If all went well, then *indexPtr is filled in
1222 *	with the index (into itemPtr) corresponding to string. Otherwise an
1223 *	error message is left in the interp's result.
1224 *
1225 * Side effects:
1226 *	None.
1227 *
1228 *--------------------------------------------------------------
1229 */
1230
1231static int
1232GetTextIndex(
1233    Tcl_Interp *interp,		/* Used for error reporting. */
1234    Tk_Canvas canvas,		/* Canvas containing item. */
1235    Tk_Item *itemPtr,		/* Item for which the index is being
1236				 * specified. */
1237    Tcl_Obj *obj,		/* Specification of a particular character in
1238				 * itemPtr's text. */
1239    int *indexPtr)		/* Where to store converted character
1240				 * index. */
1241{
1242    TextItem *textPtr = (TextItem *) itemPtr;
1243    int length;
1244    int c;
1245    TkCanvas *canvasPtr = (TkCanvas *) canvas;
1246    Tk_CanvasTextInfo *textInfoPtr = textPtr->textInfoPtr;
1247    char *string = Tcl_GetStringFromObj(obj, &length);
1248
1249    c = string[0];
1250
1251    if ((c == 'e') && (strncmp(string, "end", (unsigned) length) == 0)) {
1252	*indexPtr = textPtr->numChars;
1253    } else if ((c == 'i')
1254	    && (strncmp(string, "insert", (unsigned) length) == 0)) {
1255	*indexPtr = textPtr->insertPos;
1256    } else if ((c == 's') && (length >= 5)
1257	    && (strncmp(string, "sel.first", (unsigned) length) == 0)) {
1258	if (textInfoPtr->selItemPtr != itemPtr) {
1259	    Tcl_SetResult(interp, "selection isn't in item", TCL_STATIC);
1260	    return TCL_ERROR;
1261	}
1262	*indexPtr = textInfoPtr->selectFirst;
1263    } else if ((c == 's') && (length >= 5)
1264	    && (strncmp(string, "sel.last", (unsigned) length) == 0)) {
1265	if (textInfoPtr->selItemPtr != itemPtr) {
1266	    Tcl_SetResult(interp, "selection isn't in item", TCL_STATIC);
1267	    return TCL_ERROR;
1268	}
1269	*indexPtr = textInfoPtr->selectLast;
1270    } else if (c == '@') {
1271	int x, y;
1272	double tmp;
1273	char *end, *p;
1274
1275	p = string+1;
1276	tmp = strtod(p, &end);
1277	if ((end == p) || (*end != ',')) {
1278	    goto badIndex;
1279	}
1280	x = (int) ((tmp < 0) ? tmp - 0.5 : tmp + 0.5);
1281	p = end+1;
1282	tmp = strtod(p, &end);
1283	if ((end == p) || (*end != 0)) {
1284	    goto badIndex;
1285	}
1286	y = (int) ((tmp < 0) ? tmp - 0.5 : tmp + 0.5);
1287	*indexPtr = Tk_PointToChar(textPtr->textLayout,
1288		x + canvasPtr->scrollX1 - textPtr->leftEdge,
1289		y + canvasPtr->scrollY1 - textPtr->header.y1);
1290    } else if (Tcl_GetIntFromObj(NULL, obj, indexPtr) == TCL_OK) {
1291	if (*indexPtr < 0){
1292	    *indexPtr = 0;
1293	} else if (*indexPtr > textPtr->numChars) {
1294	    *indexPtr = textPtr->numChars;
1295	}
1296    } else {
1297	/*
1298	 * Some of the paths here leave messages in the interp's result, so we
1299	 * have to clear it out before storing our own message.
1300	 */
1301
1302    badIndex:
1303	Tcl_SetResult(interp, NULL, TCL_STATIC);
1304	Tcl_AppendResult(interp, "bad index \"", string, "\"", NULL);
1305	return TCL_ERROR;
1306    }
1307    return TCL_OK;
1308}
1309
1310/*
1311 *--------------------------------------------------------------
1312 *
1313 * SetTextCursor --
1314 *
1315 *	Set the position of the insertion cursor in this item.
1316 *
1317 * Results:
1318 *	None.
1319 *
1320 * Side effects:
1321 *	The cursor position will change.
1322 *
1323 *--------------------------------------------------------------
1324 */
1325
1326	/* ARGSUSED */
1327static void
1328SetTextCursor(
1329    Tk_Canvas canvas,		/* Record describing canvas widget. */
1330    Tk_Item *itemPtr,		/* Text item in which cursor position is to be
1331				 * set. */
1332    int index)			/* Character index of character just before
1333				 * which cursor is to be positioned. */
1334{
1335    TextItem *textPtr = (TextItem *) itemPtr;
1336
1337    if (index < 0) {
1338	textPtr->insertPos = 0;
1339    } else if (index > textPtr->numChars) {
1340	textPtr->insertPos = textPtr->numChars;
1341    } else {
1342	textPtr->insertPos = index;
1343    }
1344}
1345
1346/*
1347 *--------------------------------------------------------------
1348 *
1349 * GetSelText --
1350 *
1351 *	This function is invoked to return the selected portion of a text
1352 *	item. It is only called when this item has the selection.
1353 *
1354 * Results:
1355 *	The return value is the number of non-NULL bytes stored at buffer.
1356 *	Buffer is filled (or partially filled) with a NULL-terminated string
1357 *	containing part or all of the selection, as given by offset and
1358 *	maxBytes.
1359 *
1360 * Side effects:
1361 *	None.
1362 *
1363 *--------------------------------------------------------------
1364 */
1365
1366static int
1367GetSelText(
1368    Tk_Canvas canvas,		/* Canvas containing selection. */
1369    Tk_Item *itemPtr,		/* Text item containing selection. */
1370    int offset,			/* Byte offset within selection of first
1371				 * character to be returned. */
1372    char *buffer,		/* Location in which to place selection. */
1373    int maxBytes)		/* Maximum number of bytes to place at buffer,
1374				 * not including terminating NULL
1375				 * character. */
1376{
1377    TextItem *textPtr = (TextItem *) itemPtr;
1378    int byteCount;
1379    char *text;
1380    CONST char *selStart, *selEnd;
1381    Tk_CanvasTextInfo *textInfoPtr = textPtr->textInfoPtr;
1382
1383    if ((textInfoPtr->selectFirst < 0) ||
1384	    (textInfoPtr->selectFirst > textInfoPtr->selectLast)) {
1385	return 0;
1386    }
1387    text = textPtr->text;
1388    selStart = Tcl_UtfAtIndex(text, textInfoPtr->selectFirst);
1389    selEnd = Tcl_UtfAtIndex(selStart,
1390	    textInfoPtr->selectLast + 1 - textInfoPtr->selectFirst);
1391    byteCount = selEnd - selStart - offset;
1392    if (byteCount > maxBytes) {
1393	byteCount = maxBytes;
1394    }
1395    if (byteCount <= 0) {
1396	return 0;
1397    }
1398    memcpy(buffer, selStart + offset, (size_t) byteCount);
1399    buffer[byteCount] = '\0';
1400    return byteCount;
1401}
1402
1403/*
1404 *--------------------------------------------------------------
1405 *
1406 * TextToPostscript --
1407 *
1408 *	This function is called to generate Postscript for text items.
1409 *
1410 * Results:
1411 *	The return value is a standard Tcl result. If an error occurs in
1412 *	generating Postscript then an error message is left in the interp's
1413 *	result, replacing whatever used to be there. If no error occurs, then
1414 *	Postscript for the item is appended to the result.
1415 *
1416 * Side effects:
1417 *	None.
1418 *
1419 *--------------------------------------------------------------
1420 */
1421
1422static int
1423TextToPostscript(
1424    Tcl_Interp *interp,		/* Leave Postscript or error message here. */
1425    Tk_Canvas canvas,		/* Information about overall canvas. */
1426    Tk_Item *itemPtr,		/* Item for which Postscript is wanted. */
1427    int prepass)		/* 1 means this is a prepass to collect font
1428				 * information; 0 means final Postscript is
1429				 * being created. */
1430{
1431    TextItem *textPtr = (TextItem *) itemPtr;
1432    int x, y;
1433    Tk_FontMetrics fm;
1434    char *justify;
1435    char buffer[500];
1436    XColor *color;
1437    Pixmap stipple;
1438    Tk_State state = itemPtr->state;
1439
1440    if (state == TK_STATE_NULL) {
1441	state = ((TkCanvas *)canvas)->canvas_state;
1442    }
1443    color = textPtr->color;
1444    stipple = textPtr->stipple;
1445    if (state == TK_STATE_HIDDEN || textPtr->color == NULL ||
1446	    textPtr->text == NULL || *textPtr->text == 0) {
1447	return TCL_OK;
1448    } else if (((TkCanvas *)canvas)->currentItemPtr == itemPtr) {
1449	if (textPtr->activeColor!=NULL) {
1450	    color = textPtr->activeColor;
1451	}
1452	if (textPtr->activeStipple!=None) {
1453	    stipple = textPtr->activeStipple;
1454	}
1455    } else if (state==TK_STATE_DISABLED) {
1456	if (textPtr->disabledColor!=NULL) {
1457	    color = textPtr->disabledColor;
1458	}
1459	if (textPtr->disabledStipple!=None) {
1460	    stipple = textPtr->disabledStipple;
1461	}
1462    }
1463
1464    if (Tk_CanvasPsFont(interp, canvas, textPtr->tkfont) != TCL_OK) {
1465	return TCL_ERROR;
1466    }
1467    if (prepass != 0) {
1468	return TCL_OK;
1469    }
1470    if (Tk_CanvasPsColor(interp, canvas, color) != TCL_OK) {
1471	return TCL_ERROR;
1472    }
1473    if (stipple != None) {
1474	Tcl_AppendResult(interp, "/StippleText {\n    ", NULL);
1475	Tk_CanvasPsStipple(interp, canvas, stipple);
1476	Tcl_AppendResult(interp, "} bind def\n", NULL);
1477    }
1478
1479    sprintf(buffer, "%.15g %.15g [\n", textPtr->x,
1480	    Tk_CanvasPsY(canvas, textPtr->y));
1481    Tcl_AppendResult(interp, buffer, NULL);
1482
1483    Tk_TextLayoutToPostscript(interp, textPtr->textLayout);
1484
1485    x = 0;  y = 0;  justify = NULL;	/* lint. */
1486    switch (textPtr->anchor) {
1487    case TK_ANCHOR_NW:	   x = 0; y = 0; break;
1488    case TK_ANCHOR_N:	   x = 1; y = 0; break;
1489    case TK_ANCHOR_NE:	   x = 2; y = 0; break;
1490    case TK_ANCHOR_E:	   x = 2; y = 1; break;
1491    case TK_ANCHOR_SE:	   x = 2; y = 2; break;
1492    case TK_ANCHOR_S:	   x = 1; y = 2; break;
1493    case TK_ANCHOR_SW:	   x = 0; y = 2; break;
1494    case TK_ANCHOR_W:	   x = 0; y = 1; break;
1495    case TK_ANCHOR_CENTER: x = 1; y = 1; break;
1496    }
1497    switch (textPtr->justify) {
1498    case TK_JUSTIFY_LEFT:   justify = "0";   break;
1499    case TK_JUSTIFY_CENTER: justify = "0.5"; break;
1500    case TK_JUSTIFY_RIGHT:  justify = "1";   break;
1501    }
1502
1503    Tk_GetFontMetrics(textPtr->tkfont, &fm);
1504    sprintf(buffer, "] %d ", fm.linespace);
1505    Tcl_AppendResult(interp, buffer, NULL);
1506    Tcl_PrintDouble(NULL, x / -2.0, buffer);
1507    Tcl_AppendResult(interp, buffer, NULL);
1508    Tcl_PrintDouble(NULL, y / 2.0, buffer);
1509    Tcl_AppendResult(interp, " ", buffer, NULL);
1510    sprintf(buffer, " %s %s DrawText\n",
1511	    justify, ((stipple == None) ? "false" : "true"));
1512    Tcl_AppendResult(interp, buffer, NULL);
1513
1514    return TCL_OK;
1515}
1516
1517/*
1518 * Local Variables:
1519 * mode: c
1520 * c-basic-offset: 4
1521 * fill-column: 78
1522 * End:
1523 */
1524