4 * This file implements text items for canvas widgets.
6 * Copyright 1991-1992 Regents of the University of California.
7 * Permission to use, copy, modify, and distribute this
8 * software and its documentation for any purpose and without
9 * fee is hereby granted, provided that the above copyright
10 * notice appear in all copies. The University of California
11 * makes no representations about the suitability of this
12 * software for any purpose. It is provided "as is" without
13 * express or implied warranty.
17 static char rcsid
[] = "$Header: /user6/ouster/wish/RCS/tkCanvText.c,v 1.12 92/08/19 08:34:12 ouster Exp $ SPRITE (Berkeley)";
26 * One of the following structures is kept for each line of text
27 * in a text item. It contains geometry and display information
31 typedef struct TextLine
{
32 char *firstChar
; /* Pointer to the first character in this
33 * line (in the "text" field of enclosing
35 int numChars
; /* Number of characters displayed in this
37 int totalChars
; /* Total number of characters included as
38 * part of this line (may include an extra
39 * space character at the end that isn't
41 int x
, y
; /* Origin at which to draw line on screen
42 * (in integer pixel units, but in canvas
43 * coordinates, not screen coordinates). */
44 int x1
, y1
; /* Upper-left pixel that is part of text
45 * line on screen (again, in integer canvas
47 int x2
, y2
; /* Lower-left pixel that is part of text
48 * line on screen (again, in integer canvas
53 * The structure below defines the record for each text item.
56 typedef struct TextItem
{
57 Tk_Item header
; /* Generic stuff that's the same for all
58 * types. MUST BE FIRST IN STRUCTURE. */
59 char *text
; /* Text for item (malloc-ed). */
60 int numChars
; /* Number of non-NULL characters in text. */
61 double x
, y
; /* Positioning point for text. */
62 Tk_Anchor anchor
; /* Where to anchor text relative to (x,y). */
63 int width
; /* Width of lines for word-wrap, pixels.
64 * Zero means no word-wrap. */
65 Tk_Justify justify
; /* Justification mode for text. */
66 int rightEdge
; /* Pixel just to right of right edge of
67 * area of text item. Used for selecting
68 * up to end of line. */
69 XFontStruct
*fontPtr
; /* Font for drawing text. */
70 XColor
*color
; /* Color for text. */
71 Pixmap stipple
; /* Stipple bitmap for text, or None. */
72 GC gc
; /* Graphics context for drawing text. */
73 TextLine
*linePtr
; /* Pointer to array of structures describing
74 * individual lines of text item (malloc-ed). */
75 int numLines
; /* Number of structs at *linePtr. */
76 int cursorPos
; /* Insertion cursor is displayed just to left
77 * of character with this index. */
78 GC selTextGC
; /* Graphics context for selected text. */
82 * Information used for parsing configuration specs:
85 static Tk_ConfigSpec configSpecs
[] = {
86 {TK_CONFIG_ANCHOR
, "-anchor", (char *) NULL
, (char *) NULL
,
87 "center", Tk_Offset(TextItem
, anchor
),
88 TK_CONFIG_DONT_SET_DEFAULT
},
89 {TK_CONFIG_COLOR
, "-fill", (char *) NULL
, (char *) NULL
,
90 "black", Tk_Offset(TextItem
, color
), 0},
91 {TK_CONFIG_FONT
, "-font", (char *) NULL
, (char *) NULL
,
92 "-Adobe-Helvetica-Bold-R-Normal-*-120-*",
93 Tk_Offset(TextItem
, fontPtr
), 0},
94 {TK_CONFIG_JUSTIFY
, "-justify", (char *) NULL
, (char *) NULL
,
95 "left", Tk_Offset(TextItem
, justify
),
96 TK_CONFIG_DONT_SET_DEFAULT
},
97 {TK_CONFIG_BITMAP
, "-stipple", (char *) NULL
, (char *) NULL
,
98 (char *) NULL
, Tk_Offset(TextItem
, stipple
), TK_CONFIG_NULL_OK
},
99 {TK_CONFIG_CUSTOM
, "-tags", (char *) NULL
, (char *) NULL
,
100 (char *) NULL
, 0, TK_CONFIG_NULL_OK
, &tkCanvasTagsOption
},
101 {TK_CONFIG_STRING
, "-text", (char *) NULL
, (char *) NULL
,
102 "", Tk_Offset(TextItem
, text
), 0},
103 {TK_CONFIG_PIXELS
, "-width", (char *) NULL
, (char *) NULL
,
104 "0", Tk_Offset(TextItem
, width
), TK_CONFIG_DONT_SET_DEFAULT
},
105 {TK_CONFIG_END
, (char *) NULL
, (char *) NULL
, (char *) NULL
,
110 * Prototypes for procedures defined in this file:
113 static void ComputeTextBbox
_ANSI_ARGS_((Tk_Canvas
*canvasPtr
,
115 static int ConfigureText
_ANSI_ARGS_((
116 Tk_Canvas
*canvasPtr
, Tk_Item
*itemPtr
, int argc
,
117 char **argv
, int flags
));
118 static int CreateText
_ANSI_ARGS_((Tk_Canvas
*canvasPtr
,
119 struct Tk_Item
*itemPtr
, int argc
, char **argv
));
120 static void DeleteText
_ANSI_ARGS_((Tk_Item
*itemPtr
));
121 static void DisplayText
_ANSI_ARGS_((Tk_Canvas
*canvasPtr
,
122 Tk_Item
*itemPtr
, Drawable dst
));
123 static int GetSelText
_ANSI_ARGS_((Tk_Canvas
*canvasPtr
,
124 Tk_Item
*itemPtr
, int offset
, char *buffer
,
126 static int GetTextIndex
_ANSI_ARGS_((Tk_Canvas
*canvasPtr
,
127 Tk_Item
*itemPtr
, char *indexString
,
129 static void ScaleText
_ANSI_ARGS_((Tk_Canvas
*canvasPtr
,
130 Tk_Item
*itemPtr
, double originX
, double originY
,
131 double scaleX
, double scaleY
));
132 static void SetTextCursor
_ANSI_ARGS_((Tk_Canvas
*canvasPtr
,
133 Tk_Item
*itemPtr
, int index
));
134 static int TextCoords
_ANSI_ARGS_((Tk_Canvas
*canvasPtr
,
135 Tk_Item
*itemPtr
, int argc
, char **argv
));
136 static int TextDeleteChars
_ANSI_ARGS_((Tk_Canvas
*canvasPtr
,
137 Tk_Item
*itemPtr
, int first
, int last
));
138 static int TextInsert
_ANSI_ARGS_((Tk_Canvas
*canvasPtr
,
139 Tk_Item
*itemPtr
, int beforeThis
, char *string
));
140 static int TextToArea
_ANSI_ARGS_((Tk_Canvas
*canvasPtr
,
141 Tk_Item
*itemPtr
, double *rectPtr
));
142 static double TextToPoint
_ANSI_ARGS_((Tk_Canvas
*canvasPtr
,
143 Tk_Item
*itemPtr
, double *pointPtr
));
144 static void TranslateText
_ANSI_ARGS_((Tk_Canvas
*canvasPtr
,
145 Tk_Item
*itemPtr
, double deltaX
, double deltaY
));
148 * The structures below defines the rectangle and oval item types
149 * by means of procedures that can be invoked by generic item code.
152 Tk_ItemType TkTextType
= {
154 sizeof(TextItem
), /* itemSize */
155 CreateText
, /* createProc */
156 configSpecs
, /* configSpecs */
157 ConfigureText
, /* configureProc */
158 TextCoords
, /* coordProc */
159 DeleteText
, /* deleteProc */
160 DisplayText
, /* displayProc */
161 0, /* alwaysRedraw */
162 TextToPoint
, /* pointProc */
163 TextToArea
, /* areaProc */
164 (Tk_ItemPostscriptProc
*) NULL
, /* postscriptProc */
165 ScaleText
, /* scaleProc */
166 TranslateText
, /* translateProc */
167 GetTextIndex
, /* indexProc */
168 SetTextCursor
, /* cursorProc */
169 GetSelText
, /* selectionProc */
170 TextInsert
, /* insertProc */
171 TextDeleteChars
, /* dTextProc */
172 (Tk_ItemType
*) NULL
/* nextPtr */
176 *--------------------------------------------------------------
180 * This procedure is invoked to create a new text item
184 * A standard Tcl return value. If an error occurred in
185 * creating the item then an error message is left in
186 * canvasPtr->interp->result; in this case itemPtr is
187 * left uninitialized so it can be safely freed by the
191 * A new text item is created.
193 *--------------------------------------------------------------
198 register Tk_Canvas
*canvasPtr
, /* Canvas to hold new item. */
199 Tk_Item
*itemPtr
, /* Record to hold new item; header
200 * has been initialized by caller. */
201 int argc
, /* Number of arguments in argv. */
202 char **argv
/* Arguments describing rectangle. */
205 register TextItem
*textPtr
= (TextItem
*) itemPtr
;
208 Tcl_AppendResult(canvasPtr
->interp
, "wrong # args: should be \"",
209 Tk_PathName(canvasPtr
->tkwin
),
210 "\" create text x y [options]", (char *) NULL
);
215 * Carry out initialization that is needed in order to clean
216 * up after errors during the the remainder of this procedure.
219 textPtr
->text
= NULL
;
220 textPtr
->anchor
= TK_ANCHOR_CENTER
;
222 textPtr
->justify
= TK_JUSTIFY_LEFT
;
223 textPtr
->fontPtr
= NULL
;
224 textPtr
->color
= NULL
;
225 textPtr
->stipple
= None
;
227 textPtr
->linePtr
= NULL
;
228 textPtr
->numLines
= 0;
229 textPtr
->cursorPos
= 0;
230 textPtr
->selTextGC
= None
;
233 * Process the arguments to fill in the item record.
236 if ((TkGetCanvasCoord(canvasPtr
, argv
[0], &textPtr
->x
) != TCL_OK
)
237 || (TkGetCanvasCoord(canvasPtr
, argv
[1], &textPtr
->y
) != TCL_OK
)) {
241 if (ConfigureText(canvasPtr
, itemPtr
, argc
-2, argv
+2, 0) != TCL_OK
) {
249 *--------------------------------------------------------------
253 * This procedure is invoked to process the "coords" widget
254 * command on text items. See the user documentation for
255 * details on what it does.
258 * Returns TCL_OK or TCL_ERROR, and sets canvasPtr->interp->result.
261 * The coordinates for the given item may be changed.
263 *--------------------------------------------------------------
268 register Tk_Canvas
*canvasPtr
, /* Canvas containing item. */
269 Tk_Item
*itemPtr
, /* Item whose coordinates are to be
270 * read or modified. */
271 int argc
, /* Number of coordinates supplied in
273 char **argv
/* Array of coordinates: x1, y1,
277 register TextItem
*textPtr
= (TextItem
*) itemPtr
;
280 sprintf(canvasPtr
->interp
->result
, "%g %g", textPtr
->x
, textPtr
->y
);
281 } else if (argc
== 2) {
282 if ((TkGetCanvasCoord(canvasPtr
, argv
[0], &textPtr
->x
) != TCL_OK
)
283 || (TkGetCanvasCoord(canvasPtr
, argv
[1],
284 &textPtr
->y
) != TCL_OK
)) {
287 ComputeTextBbox(canvasPtr
, textPtr
);
289 sprintf(canvasPtr
->interp
->result
,
290 "wrong # coordinates: expected 0 or 2, got %d",
298 *--------------------------------------------------------------
302 * This procedure is invoked to configure various aspects
303 * of a text item, such as its border and background colors.
306 * A standard Tcl result code. If an error occurs, then
307 * an error message is left in canvasPtr->interp->result.
310 * Configuration information, such as colors and stipple
311 * patterns, may be set for itemPtr.
313 *--------------------------------------------------------------
318 Tk_Canvas
*canvasPtr
, /* Canvas containing itemPtr. */
319 Tk_Item
*itemPtr
, /* Rectangle item to reconfigure. */
320 int argc
, /* Number of elements in argv. */
321 char **argv
, /* Arguments describing things to configure. */
322 int flags
/* Flags to pass to Tk_ConfigureWidget. */
325 register TextItem
*textPtr
= (TextItem
*) itemPtr
;
330 if (Tk_ConfigureWidget(canvasPtr
->interp
, canvasPtr
->tkwin
,
331 configSpecs
, argc
, argv
, (char *) textPtr
, flags
) != TCL_OK
) {
336 * A few of the options require additional processing, such as
340 textPtr
->numChars
= strlen(textPtr
->text
);
341 newGC
= newSelGC
= None
;
342 if ((textPtr
->color
!= NULL
) && (textPtr
->fontPtr
!= NULL
)) {
343 gcValues
.foreground
= textPtr
->color
->pixel
;
344 gcValues
.font
= textPtr
->fontPtr
->fid
;
345 mask
= GCForeground
|GCFont
;
346 if (textPtr
->stipple
!= None
) {
347 gcValues
.stipple
= textPtr
->stipple
;
348 gcValues
.fill_style
= FillStippled
;
349 mask
|= GCForeground
|GCStipple
|GCFillStyle
;
351 newGC
= Tk_GetGC(canvasPtr
->tkwin
, mask
, &gcValues
);
352 gcValues
.foreground
= canvasPtr
->selFgColorPtr
->pixel
;
353 newSelGC
= Tk_GetGC(canvasPtr
->tkwin
, mask
, &gcValues
);
355 if (textPtr
->gc
!= None
) {
356 Tk_FreeGC(textPtr
->gc
);
359 if (textPtr
->selTextGC
!= None
) {
360 Tk_FreeGC(textPtr
->selTextGC
);
362 textPtr
->selTextGC
= newSelGC
;
365 * If the text was changed, move the selection and insertion indices
366 * to keep them inside the item.
369 if (canvasPtr
->selItemPtr
== itemPtr
) {
370 if (canvasPtr
->selectFirst
>= textPtr
->numChars
) {
371 canvasPtr
->selItemPtr
= NULL
;
373 if (canvasPtr
->selectLast
>= textPtr
->numChars
) {
374 canvasPtr
->selectLast
= textPtr
->numChars
-1;
376 if ((canvasPtr
->anchorItemPtr
== itemPtr
)
377 && (canvasPtr
->selectAnchor
>= textPtr
->numChars
)) {
378 canvasPtr
->selectAnchor
= textPtr
->numChars
-1;
382 if (textPtr
->cursorPos
>= textPtr
->numChars
) {
383 textPtr
->cursorPos
= textPtr
->numChars
;
386 ComputeTextBbox(canvasPtr
, textPtr
);
391 *--------------------------------------------------------------
395 * This procedure is called to clean up the data structure
396 * associated with a text item.
402 * Resources associated with itemPtr are released.
404 *--------------------------------------------------------------
409 Tk_Item
*itemPtr
/* Item that is being deleted. */
412 register TextItem
*textPtr
= (TextItem
*) itemPtr
;
414 if (textPtr
->text
!= NULL
) {
415 ckfree(textPtr
->text
);
417 if (textPtr
->fontPtr
!= NULL
) {
418 Tk_FreeFontStruct(textPtr
->fontPtr
);
420 if (textPtr
->color
!= NULL
) {
421 Tk_FreeColor(textPtr
->color
);
423 if (textPtr
->stipple
!= None
) {
424 Tk_FreeBitmap(textPtr
->stipple
);
426 if (textPtr
->gc
!= None
) {
427 Tk_FreeGC(textPtr
->gc
);
429 if (textPtr
->linePtr
!= NULL
) {
430 ckfree((char *) textPtr
->linePtr
);
432 if (textPtr
->selTextGC
!= None
) {
433 Tk_FreeGC(textPtr
->selTextGC
);
438 *--------------------------------------------------------------
442 * This procedure is invoked to compute the bounding box of
443 * all the pixels that may be drawn as part of a text item.
444 * In addition, it recomputes all of the geometry information
445 * used to display a text item or check for mouse hits.
451 * The fields x1, y1, x2, and y2 are updated in the header
452 * for itemPtr, and the linePtr structure is regenerated
455 *--------------------------------------------------------------
460 register Tk_Canvas
*canvasPtr
, /* Canvas that contains item. */
461 register TextItem
*textPtr
/* Item whose bbos is to be
465 register TextLine
*linePtr
;
466 #define MAX_LINES 100
467 char *lineStart
[MAX_LINES
];
468 int lineChars
[MAX_LINES
];
469 int linePixels
[MAX_LINES
];
470 int numLines
, wrapPixels
, maxLinePixels
, leftX
, topY
, y
;
471 int lineHeight
, i
, fudge
;
473 XCharStruct
*maxBoundsPtr
= &textPtr
->fontPtr
->max_bounds
;
475 if (textPtr
->linePtr
!= NULL
) {
476 ckfree((char *) textPtr
->linePtr
);
477 textPtr
->linePtr
= NULL
;
481 * Work through the text computing the starting point, number of
482 * characters, and number of pixels in each line.
486 if (textPtr
->width
> 0) {
487 wrapPixels
= maxLinePixels
= textPtr
->width
;
489 wrapPixels
= 10000000;
492 for (numLines
= 0; (numLines
< MAX_LINES
) && (*p
!= 0);
494 int numChars
, numPixels
;
495 numChars
= TkMeasureChars(textPtr
->fontPtr
, p
,
496 (textPtr
->text
+ textPtr
->numChars
) - p
, 0,
497 wrapPixels
, TK_WHOLE_WORDS
|TK_AT_LEAST_ONE
, &numPixels
);
498 if (numPixels
> maxLinePixels
) {
499 maxLinePixels
= numPixels
;
501 lineStart
[numLines
] = p
;
502 lineChars
[numLines
] = numChars
;
503 linePixels
[numLines
] = numPixels
;
507 * Skip space character that terminates a line, if there is one.
508 * In the case of multiple spaces, all but one will be displayed.
509 * This is important to make sure the insertion cursor gets
510 * displayed when it is in the middle of a multi-space.
519 * Use overall geometry information to compute the top-left corner
520 * of the bounding box for the text item.
523 leftX
= textPtr
->x
+ 0.5;
524 topY
= textPtr
->y
+ 0.5;
525 lineHeight
= textPtr
->fontPtr
->ascent
+ textPtr
->fontPtr
->descent
;
526 switch (textPtr
->anchor
) {
533 case TK_ANCHOR_CENTER
:
535 topY
-= (lineHeight
* numLines
)/2;
541 topY
-= lineHeight
* numLines
;
544 switch (textPtr
->anchor
) {
551 case TK_ANCHOR_CENTER
:
553 leftX
-= maxLinePixels
/2;
559 leftX
-= maxLinePixels
;
562 textPtr
->rightEdge
= leftX
+ maxLinePixels
;
565 * Create the new TextLine array and fill it in using the geometry
566 * information gathered already.
570 textPtr
->linePtr
= (TextLine
*) ckalloc((unsigned)
571 (numLines
* sizeof(TextLine
)));
573 textPtr
->linePtr
= NULL
;
575 textPtr
->numLines
= numLines
;
576 for (i
= 0, linePtr
= textPtr
->linePtr
, y
= topY
;
577 i
< numLines
; i
++, linePtr
++, y
+= lineHeight
) {
578 linePtr
->firstChar
= lineStart
[i
];
579 linePtr
->numChars
= lineChars
[i
];
580 if (i
== (numLines
-1)) {
581 linePtr
->totalChars
= linePtr
->numChars
;
583 linePtr
->totalChars
= lineStart
[i
+1] - lineStart
[i
];
585 switch (textPtr
->justify
) {
586 case TK_JUSTIFY_LEFT
:
587 case TK_JUSTIFY_FILL
:
590 case TK_JUSTIFY_CENTER
:
591 linePtr
->x
= leftX
+ maxLinePixels
/2 - linePixels
[i
]/2;
593 case TK_JUSTIFY_RIGHT
:
594 linePtr
->x
= leftX
+ maxLinePixels
- linePixels
[i
];
597 linePtr
->y
= y
+ textPtr
->fontPtr
->ascent
;
598 linePtr
->x1
= linePtr
->x
- maxBoundsPtr
->lbearing
;
600 linePtr
->x2
= linePtr
->x
+ linePixels
[i
] + maxBoundsPtr
->rbearing
601 - textPtr
->fontPtr
->min_bounds
.rbearing
;
602 linePtr
->y2
= linePtr
->y
+ textPtr
->fontPtr
->descent
- 1;
606 * Last of all, update the bounding box for the item. The item's
607 * bounding box includes the bounding box of all its lines, plus
608 * an extra fudge factor for the cursor border (which could
609 * potentially be quite large).
612 linePtr
= textPtr
->linePtr
;
613 textPtr
->header
.x1
= textPtr
->header
.x2
= leftX
;
614 textPtr
->header
.y1
= topY
;
615 textPtr
->header
.y2
= topY
+ numLines
*lineHeight
;
616 for (linePtr
= textPtr
->linePtr
, i
= textPtr
->numLines
; i
> 0;
618 if (linePtr
->x1
< textPtr
->header
.x1
) {
619 textPtr
->header
.x1
= linePtr
->x1
;
621 if (linePtr
->x2
>= textPtr
->header
.x2
) {
622 textPtr
->header
.x2
= linePtr
->x2
+ 1;
626 fudge
= canvasPtr
->cursorWidth
/2;
627 if (canvasPtr
->selBorderWidth
> fudge
) {
628 fudge
= canvasPtr
->selBorderWidth
;
630 textPtr
->header
.x1
-= fudge
;
631 textPtr
->header
.x2
+= fudge
;
635 *--------------------------------------------------------------
639 * This procedure is invoked to draw a text item in a given
646 * ItemPtr is drawn in drawable using the transformation
647 * information in canvasPtr.
649 *--------------------------------------------------------------
654 register Tk_Canvas
*canvasPtr
, /* Canvas that contains item. */
655 Tk_Item
*itemPtr
, /* Item to be displayed. */
656 Drawable drawable
/* Pixmap or window in which to draw
660 register TextItem
*textPtr
= (TextItem
*) itemPtr
;
661 Display
*display
= Tk_Display(canvasPtr
->tkwin
);
662 register TextLine
*linePtr
;
663 int i
, focusHere
, cursorX
, cursorIndex
, lineIndex
;
664 int beforeSelect
, inSelect
, afterSelect
, selStartX
, selEndX
;
666 if (textPtr
->gc
== None
) {
669 focusHere
= (canvasPtr
->focusItemPtr
== itemPtr
) &&
670 (canvasPtr
->flags
& GOT_FOCUS
);
671 for (linePtr
= textPtr
->linePtr
, i
= textPtr
->numLines
;
672 i
> 0; linePtr
++, i
--) {
675 * If part or all of this line is selected, then draw a special
676 * background under the selected part of the line.
679 lineIndex
= linePtr
->firstChar
- textPtr
->text
;
680 if ((canvasPtr
->selItemPtr
!= itemPtr
)
681 || (canvasPtr
->selectLast
< lineIndex
)
682 || (canvasPtr
->selectFirst
>= (lineIndex
683 + linePtr
->totalChars
))) {
684 beforeSelect
= linePtr
->numChars
;
687 beforeSelect
= canvasPtr
->selectFirst
- lineIndex
;
688 if (beforeSelect
<= 0) {
690 selStartX
= linePtr
->x
;
692 (void) TkMeasureChars(textPtr
->fontPtr
,
693 linePtr
->firstChar
, beforeSelect
, 0,
694 (int) 1000000, TK_PARTIAL_OK
, &selStartX
);
695 selStartX
+= linePtr
->x
;
697 inSelect
= canvasPtr
->selectLast
+ 1 - (lineIndex
+ beforeSelect
);
700 * If the selection spans the end of this line, then display
701 * selection background all the way to the end of the line.
702 * However, for the last line we only want to display up to
703 * the last character, not the end of the line, hence the
707 if (inSelect
>= (linePtr
->totalChars
- beforeSelect
)) {
708 inSelect
= linePtr
->numChars
- beforeSelect
;
710 selEndX
= textPtr
->rightEdge
;
711 goto fillSelectBackground
;
714 (void) TkMeasureChars(textPtr
->fontPtr
,
715 linePtr
->firstChar
+ beforeSelect
, inSelect
,
716 selStartX
-linePtr
->x
, (int) 1000000, TK_PARTIAL_OK
,
718 selEndX
+= linePtr
->x
;
719 fillSelectBackground
:
720 Tk_Fill3DRectangle(display
, drawable
, canvasPtr
->selBorder
,
721 selStartX
- canvasPtr
->drawableXOrigin
722 - canvasPtr
->selBorderWidth
,
723 linePtr
->y
- canvasPtr
->drawableYOrigin
724 - textPtr
->fontPtr
->ascent
,
725 selEndX
- selStartX
+ 2*canvasPtr
->selBorderWidth
,
726 textPtr
->fontPtr
->ascent
+ textPtr
->fontPtr
->descent
,
727 canvasPtr
->selBorderWidth
, TK_RELIEF_RAISED
);
731 * If the insertion cursor is in this line, then draw a special
732 * background for the cursor before drawing the text. Note:
733 * if we're the cursor item but the cursor is turned off, then
734 * redraw background over the area of the cursor. This guarantees
735 * that the selection won't make the cursor invisible on mono
736 * displays, where both are drawn in the same color.
740 cursorIndex
= textPtr
->cursorPos
741 - (linePtr
->firstChar
- textPtr
->text
);
742 if ((cursorIndex
>= 0) && (cursorIndex
<= linePtr
->numChars
)) {
743 (void) TkMeasureChars(textPtr
->fontPtr
, linePtr
->firstChar
,
744 cursorIndex
, 0, (int) 1000000, TK_PARTIAL_OK
, &cursorX
);
745 if (canvasPtr
->flags
& CURSOR_ON
) {
746 Tk_Fill3DRectangle(display
, drawable
,
747 canvasPtr
->cursorBorder
,
748 linePtr
->x
- canvasPtr
->drawableXOrigin
749 + cursorX
- (canvasPtr
->cursorWidth
)/2,
750 linePtr
->y
- canvasPtr
->drawableYOrigin
751 - textPtr
->fontPtr
->ascent
,
752 canvasPtr
->cursorWidth
,
753 textPtr
->fontPtr
->ascent
754 + textPtr
->fontPtr
->descent
,
755 canvasPtr
->cursorBorderWidth
, TK_RELIEF_RAISED
);
756 } else if (Tk_DefaultDepth(
757 Tk_Screen(canvasPtr
->tkwin
)) == 1){
758 Tk_Fill3DRectangle(display
, drawable
,
760 linePtr
->x
- canvasPtr
->drawableXOrigin
761 + cursorX
- (canvasPtr
->cursorWidth
)/2,
762 linePtr
->y
- canvasPtr
->drawableYOrigin
763 - textPtr
->fontPtr
->ascent
,
764 canvasPtr
->cursorWidth
,
765 textPtr
->fontPtr
->ascent
766 + textPtr
->fontPtr
->descent
,
773 * Display the text in three pieces: the part before the
774 * selection, the selected part (which needs a different graphics
775 * context), and the part after the selection.
778 if (beforeSelect
!= 0) {
779 TkDisplayChars(display
, drawable
, textPtr
->gc
, textPtr
->fontPtr
,
780 linePtr
->firstChar
, beforeSelect
,
781 linePtr
->x
- canvasPtr
->drawableXOrigin
,
782 linePtr
->y
- canvasPtr
->drawableYOrigin
, 0);
785 TkDisplayChars(display
, drawable
, textPtr
->selTextGC
,
786 textPtr
->fontPtr
, linePtr
->firstChar
+ beforeSelect
,
787 inSelect
, selStartX
- canvasPtr
->drawableXOrigin
,
788 linePtr
->y
- canvasPtr
->drawableYOrigin
, 0);
790 afterSelect
= linePtr
->numChars
- beforeSelect
- inSelect
;
791 if (afterSelect
> 0) {
792 TkDisplayChars(display
, drawable
, textPtr
->gc
, textPtr
->fontPtr
,
793 linePtr
->firstChar
+ beforeSelect
+ inSelect
,
794 afterSelect
, selEndX
- canvasPtr
->drawableXOrigin
,
795 linePtr
->y
- canvasPtr
->drawableYOrigin
, 0);
801 *--------------------------------------------------------------
805 * Insert characters into a text item at a given position.
808 * Always returns TCL_OK.
811 * The text in the given item is modified. The cursor and
812 * selection positions are also modified to reflect the
815 *--------------------------------------------------------------
820 Tk_Canvas
*canvasPtr
, /* Canvas containing text item. */
821 Tk_Item
*itemPtr
, /* Text item to be modified. */
822 int beforeThis
, /* Index of character before which text is
824 char *string
/* New characters to be inserted. */
827 register TextItem
*textPtr
= (TextItem
*) itemPtr
;
831 length
= strlen(string
);
835 if (beforeThis
< 0) {
838 if (beforeThis
> textPtr
->numChars
) {
839 beforeThis
= textPtr
->numChars
;
842 new = (char *) ckalloc((unsigned) (textPtr
->numChars
+ length
+ 1));
843 strncpy(new, textPtr
->text
, beforeThis
);
844 strcpy(new+beforeThis
, string
);
845 strcpy(new+beforeThis
+length
, textPtr
->text
+beforeThis
);
846 ckfree(textPtr
->text
);
848 textPtr
->numChars
+= length
;
851 * Inserting characters invalidates indices such as those for the
852 * selection and cursor. Update the indices appropriately.
855 if (canvasPtr
->selItemPtr
== itemPtr
) {
856 if (canvasPtr
->selectFirst
>= beforeThis
) {
857 canvasPtr
->selectFirst
+= length
;
859 if (canvasPtr
->selectLast
>= beforeThis
) {
860 canvasPtr
->selectLast
+= length
;
862 if ((canvasPtr
->anchorItemPtr
== itemPtr
)
863 && (canvasPtr
->selectAnchor
>= beforeThis
)) {
864 canvasPtr
->selectAnchor
+= length
;
867 if (textPtr
->cursorPos
>= beforeThis
) {
868 textPtr
->cursorPos
+= length
;
870 ComputeTextBbox(canvasPtr
, textPtr
);
875 *--------------------------------------------------------------
879 * Delete one or more characters from a text item.
882 * Always returns TCL_OK.
885 * Characters between "first" and "last", inclusive, get
886 * deleted from itemPtr, and things like the selection
887 * position get updated.
889 *--------------------------------------------------------------
894 Tk_Canvas
*canvasPtr
, /* Canvas containing itemPtr. */
895 Tk_Item
*itemPtr
, /* Item in which to delete characters. */
896 int first
, /* Index of first character to delete. */
897 int last
/* Index of last character to delete. */
900 register TextItem
*textPtr
= (TextItem
*) itemPtr
;
907 if (last
>= textPtr
->numChars
) {
908 last
= textPtr
->numChars
-1;
913 count
= last
+ 1 - first
;
915 new = ckalloc((unsigned) (textPtr
->numChars
+ 1 - count
));
916 strncpy(new, textPtr
->text
, first
);
917 strcpy(new+first
, textPtr
->text
+last
+1);
918 ckfree(textPtr
->text
);
920 textPtr
->numChars
-= count
;
923 * Update indexes for the selection and cursor to reflect the
924 * renumbering of the remaining characters.
927 if (canvasPtr
->selItemPtr
== itemPtr
) {
928 if (canvasPtr
->selectFirst
> first
) {
929 canvasPtr
->selectFirst
-= count
;
930 if (canvasPtr
->selectFirst
< first
) {
931 canvasPtr
->selectFirst
= first
;
934 if (canvasPtr
->selectLast
>= first
) {
935 canvasPtr
->selectLast
-= count
;
936 if (canvasPtr
->selectLast
< (first
-1)) {
937 canvasPtr
->selectLast
= (first
-1);
940 if (canvasPtr
->selectFirst
> canvasPtr
->selectLast
) {
941 canvasPtr
->selItemPtr
= NULL
;
943 if ((canvasPtr
->anchorItemPtr
== itemPtr
)
944 && (canvasPtr
->selectAnchor
> first
)) {
945 canvasPtr
->selectAnchor
-= count
;
946 if (canvasPtr
->selectAnchor
< first
) {
947 canvasPtr
->selectAnchor
= first
;
951 if (textPtr
->cursorPos
> first
) {
952 textPtr
->cursorPos
-= count
;
953 if (textPtr
->cursorPos
< first
) {
954 textPtr
->cursorPos
= first
;
957 ComputeTextBbox(canvasPtr
, textPtr
);
962 *--------------------------------------------------------------
966 * Computes the distance from a given point to a given
967 * text item, in canvas units.
970 * The return value is 0 if the point whose x and y coordinates
971 * are pointPtr[0] and pointPtr[1] is inside the arc. If the
972 * point isn't inside the arc then the return value is the
973 * distance from the point to the arc.
978 *--------------------------------------------------------------
984 Tk_Canvas
*canvasPtr
, /* Canvas containing itemPtr. */
985 Tk_Item
*itemPtr
, /* Item to check against point. */
986 double *pointPtr
/* Pointer to x and y coordinates. */
989 TextItem
*textPtr
= (TextItem
*) itemPtr
;
990 register TextLine
*linePtr
;
992 double xDiff
, yDiff
, dist
, minDist
;
995 * Treat each line in the text item as a rectangle, compute the
996 * distance to that rectangle, and take the minimum of these
997 * distances. Perform most of the calculations in integer pixel
998 * units, since that's how the dimensions of the text are defined.
1002 for (linePtr
= textPtr
->linePtr
, i
= textPtr
->numLines
;
1003 i
> 0; linePtr
++, i
--) {
1006 * If the point is inside the line's rectangle, then can
1007 * return immediately.
1010 if ((pointPtr
[0] >= linePtr
->x1
)
1011 && (pointPtr
[0] <= linePtr
->x2
)
1012 && (pointPtr
[1] >= linePtr
->y1
)
1013 && (pointPtr
[1] <= linePtr
->y2
)) {
1018 * Point is outside line's rectangle; compute distance to nearest
1022 if (pointPtr
[0] < linePtr
->x1
) {
1023 xDiff
= linePtr
->x1
- pointPtr
[0];
1024 } else if (pointPtr
[0] > linePtr
->x2
) {
1025 xDiff
= pointPtr
[0] - linePtr
->x2
;
1030 if (pointPtr
[1] < linePtr
->y1
) {
1031 yDiff
= linePtr
->y1
- pointPtr
[1];
1032 } else if (pointPtr
[1] > linePtr
->y2
) {
1033 yDiff
= pointPtr
[1] - linePtr
->y2
;
1038 dist
= hypot((float) xDiff
, (float) yDiff
);
1039 if ((dist
< minDist
) || (minDist
< 0.0)) {
1047 *--------------------------------------------------------------
1051 * This procedure is called to determine whether an item
1052 * lies entirely inside, entirely outside, or overlapping
1053 * a given rectangle.
1056 * -1 is returned if the item is entirely outside the area
1057 * given by rectPtr, 0 if it overlaps, and 1 if it is entirely
1058 * inside the given area.
1063 *--------------------------------------------------------------
1069 Tk_Canvas
*canvasPtr
, /* Canvas containing itemPtr. */
1070 Tk_Item
*itemPtr
, /* Item to check against rectangle. */
1071 double *rectPtr
/* Pointer to array of four coordinates
1072 * (x1, y1, x2, y2) describing rectangular
1076 TextItem
*textPtr
= (TextItem
*) itemPtr
;
1077 register TextLine
*linePtr
;
1081 * Scan the lines one at a time, seeing whether each line is
1082 * entirely in, entirely out, or overlapping the rectangle. If
1083 * an overlap is detected, return immediately; otherwise wait
1084 * until all lines have been processed and see if they were all
1085 * inside or all outside.
1089 for (linePtr
= textPtr
->linePtr
, i
= textPtr
->numLines
;
1090 i
> 0; linePtr
++, i
--) {
1091 if ((rectPtr
[2] < linePtr
->x1
) || (rectPtr
[0] > linePtr
->x2
)
1092 || (rectPtr
[3] < linePtr
->y1
) || (rectPtr
[1] > linePtr
->y2
)) {
1099 if ((linePtr
->x1
< rectPtr
[0]) || (linePtr
->x2
> rectPtr
[2])
1100 || (linePtr
->y1
< rectPtr
[1]) || (linePtr
->y2
> rectPtr
[3])) {
1112 *--------------------------------------------------------------
1116 * This procedure is invoked to rescale a text item.
1122 * Scales the position of the text, but not the size
1123 * of the font for the text.
1125 *--------------------------------------------------------------
1131 Tk_Canvas
*canvasPtr
, /* Canvas containing rectangle. */
1132 Tk_Item
*itemPtr
, /* Rectangle to be scaled. */
1134 double originY
, /* Origin about which to scale rect. */
1135 double scaleX
, /* Amount to scale in X direction. */
1136 double scaleY
/* Amount to scale in Y direction. */
1139 register TextItem
*textPtr
= (TextItem
*) itemPtr
;
1141 textPtr
->x
= originX
+ scaleX
*(textPtr
->x
- originX
);
1142 textPtr
->y
= originY
+ scaleY
*(textPtr
->y
- originY
);
1143 ComputeTextBbox(canvasPtr
, textPtr
);
1148 *--------------------------------------------------------------
1152 * This procedure is called to move a text item by a
1159 * The position of the text item is offset by (xDelta, yDelta),
1160 * and the bounding box is updated in the generic part of the
1163 *--------------------------------------------------------------
1168 Tk_Canvas
*canvasPtr
, /* Canvas containing item. */
1169 Tk_Item
*itemPtr
, /* Item that is being moved. */
1171 double deltaY
/* Amount by which item is to be
1175 register TextItem
*textPtr
= (TextItem
*) itemPtr
;
1177 textPtr
->x
+= deltaX
;
1178 textPtr
->y
+= deltaY
;
1179 ComputeTextBbox(canvasPtr
, textPtr
);
1183 *--------------------------------------------------------------
1187 * Parse an index into a text item and return either its value
1191 * A standard Tcl result. If all went well, then *indexPtr is
1192 * filled in with the index (into itemPtr) corresponding to
1193 * string. Otherwise an error message is left in
1194 * canvasPtr->interp->result.
1199 *--------------------------------------------------------------
1204 Tk_Canvas
*canvasPtr
, /* Canvas containing item. */
1205 Tk_Item
*itemPtr
, /* Item for which the index is being
1207 char *string
, /* Specification of a particular character
1208 * in itemPtr's text. */
1209 int *indexPtr
/* Where to store converted index. */
1212 register TextItem
*textPtr
= (TextItem
*) itemPtr
;
1215 length
= strlen(string
);
1217 if (string
[0] == 'e') {
1218 if (strncmp(string
, "end", length
) == 0) {
1219 *indexPtr
= textPtr
->numChars
;
1224 * Some of the paths here leave messages in
1225 * canvasPtr->interp->result, so we have to clear it out
1226 * before storing our own message.
1229 Tcl_SetResult(canvasPtr
->interp
, (char *) NULL
, TCL_STATIC
);
1230 Tcl_AppendResult(canvasPtr
->interp
, "bad index \"", string
,
1231 "\"", (char *) NULL
);
1234 } else if (string
[0] == 'c') {
1235 if (strncmp(string
, "cursor", length
) == 0) {
1236 *indexPtr
= textPtr
->cursorPos
;
1240 } else if (string
[0] == 's') {
1241 if (canvasPtr
->selItemPtr
!= itemPtr
) {
1242 canvasPtr
->interp
->result
= "selection isn't in item";
1248 if (strncmp(string
, "sel.first", length
) == 0) {
1249 *indexPtr
= canvasPtr
->selectFirst
;
1250 } else if (strncmp(string
, "sel.last", length
) == 0) {
1251 *indexPtr
= canvasPtr
->selectLast
;
1255 } else if (string
[0] == '@') {
1258 register TextLine
*linePtr
;
1261 x
= strtol(p
, &end
, 0);
1262 if ((end
== p
) || (*end
!= ',')) {
1266 y
= strtol(p
, &end
, 0);
1267 if ((end
== p
) || (*end
!= 0)) {
1270 if ((textPtr
->numChars
== 0) || (y
< textPtr
->linePtr
[0].y1
)) {
1274 for (i
= 0, linePtr
= textPtr
->linePtr
; ; i
++, linePtr
++) {
1275 if (i
>= textPtr
->numLines
) {
1276 *indexPtr
= textPtr
->numChars
;
1279 if (y
<= linePtr
->y2
) {
1283 *indexPtr
= TkMeasureChars(textPtr
->fontPtr
, linePtr
->firstChar
,
1284 linePtr
->numChars
, linePtr
->x
, x
, 0, &dummy
);
1285 *indexPtr
+= linePtr
->firstChar
- textPtr
->text
;
1287 if (Tcl_GetInt(canvasPtr
->interp
, string
, indexPtr
) != TCL_OK
) {
1292 } else if (*indexPtr
> textPtr
->numChars
) {
1293 *indexPtr
= textPtr
->numChars
;
1300 *--------------------------------------------------------------
1304 * Set the position of the insertion cursor in this item.
1310 * The cursor position will change.
1312 *--------------------------------------------------------------
1318 Tk_Canvas
*canvasPtr
, /* Record describing canvas widget. */
1319 Tk_Item
*itemPtr
, /* Text item in which cursor position
1321 int index
/* Index of character just before which
1322 * cursor is to be positioned. */
1325 register TextItem
*textPtr
= (TextItem
*) itemPtr
;
1328 textPtr
->cursorPos
= 0;
1329 } else if (index
> textPtr
->numChars
) {
1330 textPtr
->cursorPos
= textPtr
->numChars
;
1332 textPtr
->cursorPos
= index
;
1337 *--------------------------------------------------------------
1341 * This procedure is invoked to return the selected portion
1342 * of a text item. It is only called when this item has
1346 * The return value is the number of non-NULL bytes stored
1347 * at buffer. Buffer is filled (or partially filled) with a
1348 * NULL-terminated string containing part or all of the selection,
1349 * as given by offset and maxBytes.
1354 *--------------------------------------------------------------
1359 Tk_Canvas
*canvasPtr
, /* Canvas containing selection. */
1360 Tk_Item
*itemPtr
, /* Text item containing selection. */
1361 int offset
, /* Offset within selection of first
1362 * character to be returned. */
1363 char *buffer
, /* Location in which to place
1365 int maxBytes
/* Maximum number of bytes to place
1366 * at buffer, not including terminating
1367 * NULL character. */
1370 TextItem
*textPtr
= (TextItem
*) itemPtr
;
1373 count
= canvasPtr
->selectLast
+ 1 - canvasPtr
->selectFirst
- offset
;
1374 if (canvasPtr
->selectLast
== textPtr
->numChars
) {
1377 if (count
> maxBytes
) {
1383 strncpy(buffer
, textPtr
->text
+ canvasPtr
->selectFirst
+ offset
, count
);
1384 buffer
[count
] = '\0';