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 *--------------------------------------------------------------
197 CreateText(canvasPtr
, itemPtr
, argc
, argv
)
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. */
204 register TextItem
*textPtr
= (TextItem
*) itemPtr
;
207 Tcl_AppendResult(canvasPtr
->interp
, "wrong # args: should be \"",
208 Tk_PathName(canvasPtr
->tkwin
),
209 "\" create text x y [options]", (char *) NULL
);
214 * Carry out initialization that is needed in order to clean
215 * up after errors during the the remainder of this procedure.
218 textPtr
->text
= NULL
;
219 textPtr
->anchor
= TK_ANCHOR_CENTER
;
221 textPtr
->justify
= TK_JUSTIFY_LEFT
;
222 textPtr
->fontPtr
= NULL
;
223 textPtr
->color
= NULL
;
224 textPtr
->stipple
= None
;
226 textPtr
->linePtr
= NULL
;
227 textPtr
->numLines
= 0;
228 textPtr
->cursorPos
= 0;
229 textPtr
->selTextGC
= None
;
232 * Process the arguments to fill in the item record.
235 if ((TkGetCanvasCoord(canvasPtr
, argv
[0], &textPtr
->x
) != TCL_OK
)
236 || (TkGetCanvasCoord(canvasPtr
, argv
[1], &textPtr
->y
) != TCL_OK
)) {
240 if (ConfigureText(canvasPtr
, itemPtr
, argc
-2, argv
+2, 0) != TCL_OK
) {
248 *--------------------------------------------------------------
252 * This procedure is invoked to process the "coords" widget
253 * command on text items. See the user documentation for
254 * details on what it does.
257 * Returns TCL_OK or TCL_ERROR, and sets canvasPtr->interp->result.
260 * The coordinates for the given item may be changed.
262 *--------------------------------------------------------------
266 TextCoords(canvasPtr
, itemPtr
, argc
, argv
)
267 register Tk_Canvas
*canvasPtr
; /* Canvas containing item. */
268 Tk_Item
*itemPtr
; /* Item whose coordinates are to be
269 * read or modified. */
270 int argc
; /* Number of coordinates supplied in
272 char **argv
; /* Array of coordinates: x1, y1,
275 register TextItem
*textPtr
= (TextItem
*) itemPtr
;
278 sprintf(canvasPtr
->interp
->result
, "%g %g", textPtr
->x
, textPtr
->y
);
279 } else if (argc
== 2) {
280 if ((TkGetCanvasCoord(canvasPtr
, argv
[0], &textPtr
->x
) != TCL_OK
)
281 || (TkGetCanvasCoord(canvasPtr
, argv
[1],
282 &textPtr
->y
) != TCL_OK
)) {
285 ComputeTextBbox(canvasPtr
, textPtr
);
287 sprintf(canvasPtr
->interp
->result
,
288 "wrong # coordinates: expected 0 or 2, got %d",
296 *--------------------------------------------------------------
300 * This procedure is invoked to configure various aspects
301 * of a text item, such as its border and background colors.
304 * A standard Tcl result code. If an error occurs, then
305 * an error message is left in canvasPtr->interp->result.
308 * Configuration information, such as colors and stipple
309 * patterns, may be set for itemPtr.
311 *--------------------------------------------------------------
315 ConfigureText(canvasPtr
, itemPtr
, argc
, argv
, flags
)
316 Tk_Canvas
*canvasPtr
; /* Canvas containing itemPtr. */
317 Tk_Item
*itemPtr
; /* Rectangle item to reconfigure. */
318 int argc
; /* Number of elements in argv. */
319 char **argv
; /* Arguments describing things to configure. */
320 int flags
; /* Flags to pass to Tk_ConfigureWidget. */
322 register TextItem
*textPtr
= (TextItem
*) itemPtr
;
327 if (Tk_ConfigureWidget(canvasPtr
->interp
, canvasPtr
->tkwin
,
328 configSpecs
, argc
, argv
, (char *) textPtr
, flags
) != TCL_OK
) {
333 * A few of the options require additional processing, such as
337 textPtr
->numChars
= strlen(textPtr
->text
);
338 newGC
= newSelGC
= None
;
339 if ((textPtr
->color
!= NULL
) && (textPtr
->fontPtr
!= NULL
)) {
340 gcValues
.foreground
= textPtr
->color
->pixel
;
341 gcValues
.font
= textPtr
->fontPtr
->fid
;
342 mask
= GCForeground
|GCFont
;
343 if (textPtr
->stipple
!= None
) {
344 gcValues
.stipple
= textPtr
->stipple
;
345 gcValues
.fill_style
= FillStippled
;
346 mask
|= GCForeground
|GCStipple
|GCFillStyle
;
348 newGC
= Tk_GetGC(canvasPtr
->tkwin
, mask
, &gcValues
);
349 gcValues
.foreground
= canvasPtr
->selFgColorPtr
->pixel
;
350 newSelGC
= Tk_GetGC(canvasPtr
->tkwin
, mask
, &gcValues
);
352 if (textPtr
->gc
!= None
) {
353 Tk_FreeGC(textPtr
->gc
);
356 if (textPtr
->selTextGC
!= None
) {
357 Tk_FreeGC(textPtr
->selTextGC
);
359 textPtr
->selTextGC
= newSelGC
;
362 * If the text was changed, move the selection and insertion indices
363 * to keep them inside the item.
366 if (canvasPtr
->selItemPtr
== itemPtr
) {
367 if (canvasPtr
->selectFirst
>= textPtr
->numChars
) {
368 canvasPtr
->selItemPtr
= NULL
;
370 if (canvasPtr
->selectLast
>= textPtr
->numChars
) {
371 canvasPtr
->selectLast
= textPtr
->numChars
-1;
373 if ((canvasPtr
->anchorItemPtr
== itemPtr
)
374 && (canvasPtr
->selectAnchor
>= textPtr
->numChars
)) {
375 canvasPtr
->selectAnchor
= textPtr
->numChars
-1;
379 if (textPtr
->cursorPos
>= textPtr
->numChars
) {
380 textPtr
->cursorPos
= textPtr
->numChars
;
383 ComputeTextBbox(canvasPtr
, textPtr
);
388 *--------------------------------------------------------------
392 * This procedure is called to clean up the data structure
393 * associated with a text item.
399 * Resources associated with itemPtr are released.
401 *--------------------------------------------------------------
406 Tk_Item
*itemPtr
; /* Item that is being deleted. */
408 register TextItem
*textPtr
= (TextItem
*) itemPtr
;
410 if (textPtr
->text
!= NULL
) {
411 ckfree(textPtr
->text
);
413 if (textPtr
->fontPtr
!= NULL
) {
414 Tk_FreeFontStruct(textPtr
->fontPtr
);
416 if (textPtr
->color
!= NULL
) {
417 Tk_FreeColor(textPtr
->color
);
419 if (textPtr
->stipple
!= None
) {
420 Tk_FreeBitmap(textPtr
->stipple
);
422 if (textPtr
->gc
!= None
) {
423 Tk_FreeGC(textPtr
->gc
);
425 if (textPtr
->linePtr
!= NULL
) {
426 ckfree((char *) textPtr
->linePtr
);
428 if (textPtr
->selTextGC
!= None
) {
429 Tk_FreeGC(textPtr
->selTextGC
);
434 *--------------------------------------------------------------
438 * This procedure is invoked to compute the bounding box of
439 * all the pixels that may be drawn as part of a text item.
440 * In addition, it recomputes all of the geometry information
441 * used to display a text item or check for mouse hits.
447 * The fields x1, y1, x2, and y2 are updated in the header
448 * for itemPtr, and the linePtr structure is regenerated
451 *--------------------------------------------------------------
455 ComputeTextBbox(canvasPtr
, textPtr
)
456 register Tk_Canvas
*canvasPtr
; /* Canvas that contains item. */
457 register TextItem
*textPtr
; /* Item whose bbos is to be
460 register TextLine
*linePtr
;
461 #define MAX_LINES 100
462 char *lineStart
[MAX_LINES
];
463 int lineChars
[MAX_LINES
];
464 int linePixels
[MAX_LINES
];
465 int numLines
, wrapPixels
, maxLinePixels
, leftX
, topY
, y
;
466 int lineHeight
, i
, fudge
;
468 XCharStruct
*maxBoundsPtr
= &textPtr
->fontPtr
->max_bounds
;
470 if (textPtr
->linePtr
!= NULL
) {
471 ckfree((char *) textPtr
->linePtr
);
472 textPtr
->linePtr
= NULL
;
476 * Work through the text computing the starting point, number of
477 * characters, and number of pixels in each line.
481 if (textPtr
->width
> 0) {
482 wrapPixels
= maxLinePixels
= textPtr
->width
;
484 wrapPixels
= 10000000;
487 for (numLines
= 0; (numLines
< MAX_LINES
) && (*p
!= 0);
489 int numChars
, numPixels
;
490 numChars
= TkMeasureChars(textPtr
->fontPtr
, p
,
491 (textPtr
->text
+ textPtr
->numChars
) - p
, 0,
492 wrapPixels
, TK_WHOLE_WORDS
|TK_AT_LEAST_ONE
, &numPixels
);
493 if (numPixels
> maxLinePixels
) {
494 maxLinePixels
= numPixels
;
496 lineStart
[numLines
] = p
;
497 lineChars
[numLines
] = numChars
;
498 linePixels
[numLines
] = numPixels
;
502 * Skip space character that terminates a line, if there is one.
503 * In the case of multiple spaces, all but one will be displayed.
504 * This is important to make sure the insertion cursor gets
505 * displayed when it is in the middle of a multi-space.
514 * Use overall geometry information to compute the top-left corner
515 * of the bounding box for the text item.
518 leftX
= textPtr
->x
+ 0.5;
519 topY
= textPtr
->y
+ 0.5;
520 lineHeight
= textPtr
->fontPtr
->ascent
+ textPtr
->fontPtr
->descent
;
521 switch (textPtr
->anchor
) {
528 case TK_ANCHOR_CENTER
:
530 topY
-= (lineHeight
* numLines
)/2;
536 topY
-= lineHeight
* numLines
;
539 switch (textPtr
->anchor
) {
546 case TK_ANCHOR_CENTER
:
548 leftX
-= maxLinePixels
/2;
554 leftX
-= maxLinePixels
;
557 textPtr
->rightEdge
= leftX
+ maxLinePixels
;
560 * Create the new TextLine array and fill it in using the geometry
561 * information gathered already.
565 textPtr
->linePtr
= (TextLine
*) ckalloc((unsigned)
566 (numLines
* sizeof(TextLine
)));
568 textPtr
->linePtr
= NULL
;
570 textPtr
->numLines
= numLines
;
571 for (i
= 0, linePtr
= textPtr
->linePtr
, y
= topY
;
572 i
< numLines
; i
++, linePtr
++, y
+= lineHeight
) {
573 linePtr
->firstChar
= lineStart
[i
];
574 linePtr
->numChars
= lineChars
[i
];
575 if (i
== (numLines
-1)) {
576 linePtr
->totalChars
= linePtr
->numChars
;
578 linePtr
->totalChars
= lineStart
[i
+1] - lineStart
[i
];
580 switch (textPtr
->justify
) {
581 case TK_JUSTIFY_LEFT
:
582 case TK_JUSTIFY_FILL
:
585 case TK_JUSTIFY_CENTER
:
586 linePtr
->x
= leftX
+ maxLinePixels
/2 - linePixels
[i
]/2;
588 case TK_JUSTIFY_RIGHT
:
589 linePtr
->x
= leftX
+ maxLinePixels
- linePixels
[i
];
592 linePtr
->y
= y
+ textPtr
->fontPtr
->ascent
;
593 linePtr
->x1
= linePtr
->x
- maxBoundsPtr
->lbearing
;
595 linePtr
->x2
= linePtr
->x
+ linePixels
[i
] + maxBoundsPtr
->rbearing
596 - textPtr
->fontPtr
->min_bounds
.rbearing
;
597 linePtr
->y2
= linePtr
->y
+ textPtr
->fontPtr
->descent
- 1;
601 * Last of all, update the bounding box for the item. The item's
602 * bounding box includes the bounding box of all its lines, plus
603 * an extra fudge factor for the cursor border (which could
604 * potentially be quite large).
607 linePtr
= textPtr
->linePtr
;
608 textPtr
->header
.x1
= textPtr
->header
.x2
= leftX
;
609 textPtr
->header
.y1
= topY
;
610 textPtr
->header
.y2
= topY
+ numLines
*lineHeight
;
611 for (linePtr
= textPtr
->linePtr
, i
= textPtr
->numLines
; i
> 0;
613 if (linePtr
->x1
< textPtr
->header
.x1
) {
614 textPtr
->header
.x1
= linePtr
->x1
;
616 if (linePtr
->x2
>= textPtr
->header
.x2
) {
617 textPtr
->header
.x2
= linePtr
->x2
+ 1;
621 fudge
= canvasPtr
->cursorWidth
/2;
622 if (canvasPtr
->selBorderWidth
> fudge
) {
623 fudge
= canvasPtr
->selBorderWidth
;
625 textPtr
->header
.x1
-= fudge
;
626 textPtr
->header
.x2
+= fudge
;
630 *--------------------------------------------------------------
634 * This procedure is invoked to draw a text item in a given
641 * ItemPtr is drawn in drawable using the transformation
642 * information in canvasPtr.
644 *--------------------------------------------------------------
648 DisplayText(canvasPtr
, itemPtr
, drawable
)
649 register Tk_Canvas
*canvasPtr
; /* Canvas that contains item. */
650 Tk_Item
*itemPtr
; /* Item to be displayed. */
651 Drawable drawable
; /* Pixmap or window in which to draw
654 register TextItem
*textPtr
= (TextItem
*) itemPtr
;
655 Display
*display
= Tk_Display(canvasPtr
->tkwin
);
656 register TextLine
*linePtr
;
657 int i
, focusHere
, cursorX
, cursorIndex
, lineIndex
;
658 int beforeSelect
, inSelect
, afterSelect
, selStartX
, selEndX
;
660 if (textPtr
->gc
== None
) {
663 focusHere
= (canvasPtr
->focusItemPtr
== itemPtr
) &&
664 (canvasPtr
->flags
& GOT_FOCUS
);
665 for (linePtr
= textPtr
->linePtr
, i
= textPtr
->numLines
;
666 i
> 0; linePtr
++, i
--) {
669 * If part or all of this line is selected, then draw a special
670 * background under the selected part of the line.
673 lineIndex
= linePtr
->firstChar
- textPtr
->text
;
674 if ((canvasPtr
->selItemPtr
!= itemPtr
)
675 || (canvasPtr
->selectLast
< lineIndex
)
676 || (canvasPtr
->selectFirst
>= (lineIndex
677 + linePtr
->totalChars
))) {
678 beforeSelect
= linePtr
->numChars
;
681 beforeSelect
= canvasPtr
->selectFirst
- lineIndex
;
682 if (beforeSelect
<= 0) {
684 selStartX
= linePtr
->x
;
686 (void) TkMeasureChars(textPtr
->fontPtr
,
687 linePtr
->firstChar
, beforeSelect
, 0,
688 (int) 1000000, TK_PARTIAL_OK
, &selStartX
);
689 selStartX
+= linePtr
->x
;
691 inSelect
= canvasPtr
->selectLast
+ 1 - (lineIndex
+ beforeSelect
);
694 * If the selection spans the end of this line, then display
695 * selection background all the way to the end of the line.
696 * However, for the last line we only want to display up to
697 * the last character, not the end of the line, hence the
701 if (inSelect
>= (linePtr
->totalChars
- beforeSelect
)) {
702 inSelect
= linePtr
->numChars
- beforeSelect
;
704 selEndX
= textPtr
->rightEdge
;
705 goto fillSelectBackground
;
708 (void) TkMeasureChars(textPtr
->fontPtr
,
709 linePtr
->firstChar
+ beforeSelect
, inSelect
,
710 selStartX
-linePtr
->x
, (int) 1000000, TK_PARTIAL_OK
,
712 selEndX
+= linePtr
->x
;
713 fillSelectBackground
:
714 Tk_Fill3DRectangle(display
, drawable
, canvasPtr
->selBorder
,
715 selStartX
- canvasPtr
->drawableXOrigin
716 - canvasPtr
->selBorderWidth
,
717 linePtr
->y
- canvasPtr
->drawableYOrigin
718 - textPtr
->fontPtr
->ascent
,
719 selEndX
- selStartX
+ 2*canvasPtr
->selBorderWidth
,
720 textPtr
->fontPtr
->ascent
+ textPtr
->fontPtr
->descent
,
721 canvasPtr
->selBorderWidth
, TK_RELIEF_RAISED
);
725 * If the insertion cursor is in this line, then draw a special
726 * background for the cursor before drawing the text. Note:
727 * if we're the cursor item but the cursor is turned off, then
728 * redraw background over the area of the cursor. This guarantees
729 * that the selection won't make the cursor invisible on mono
730 * displays, where both are drawn in the same color.
734 cursorIndex
= textPtr
->cursorPos
735 - (linePtr
->firstChar
- textPtr
->text
);
736 if ((cursorIndex
>= 0) && (cursorIndex
<= linePtr
->numChars
)) {
737 (void) TkMeasureChars(textPtr
->fontPtr
, linePtr
->firstChar
,
738 cursorIndex
, 0, (int) 1000000, TK_PARTIAL_OK
, &cursorX
);
739 if (canvasPtr
->flags
& CURSOR_ON
) {
740 Tk_Fill3DRectangle(display
, drawable
,
741 canvasPtr
->cursorBorder
,
742 linePtr
->x
- canvasPtr
->drawableXOrigin
743 + cursorX
- (canvasPtr
->cursorWidth
)/2,
744 linePtr
->y
- canvasPtr
->drawableYOrigin
745 - textPtr
->fontPtr
->ascent
,
746 canvasPtr
->cursorWidth
,
747 textPtr
->fontPtr
->ascent
748 + textPtr
->fontPtr
->descent
,
749 canvasPtr
->cursorBorderWidth
, TK_RELIEF_RAISED
);
750 } else if (Tk_DefaultDepth(
751 Tk_Screen(canvasPtr
->tkwin
)) == 1){
752 Tk_Fill3DRectangle(display
, drawable
,
754 linePtr
->x
- canvasPtr
->drawableXOrigin
755 + cursorX
- (canvasPtr
->cursorWidth
)/2,
756 linePtr
->y
- canvasPtr
->drawableYOrigin
757 - textPtr
->fontPtr
->ascent
,
758 canvasPtr
->cursorWidth
,
759 textPtr
->fontPtr
->ascent
760 + textPtr
->fontPtr
->descent
,
767 * Display the text in three pieces: the part before the
768 * selection, the selected part (which needs a different graphics
769 * context), and the part after the selection.
772 if (beforeSelect
!= 0) {
773 TkDisplayChars(display
, drawable
, textPtr
->gc
, textPtr
->fontPtr
,
774 linePtr
->firstChar
, beforeSelect
,
775 linePtr
->x
- canvasPtr
->drawableXOrigin
,
776 linePtr
->y
- canvasPtr
->drawableYOrigin
, 0);
779 TkDisplayChars(display
, drawable
, textPtr
->selTextGC
,
780 textPtr
->fontPtr
, linePtr
->firstChar
+ beforeSelect
,
781 inSelect
, selStartX
- canvasPtr
->drawableXOrigin
,
782 linePtr
->y
- canvasPtr
->drawableYOrigin
, 0);
784 afterSelect
= linePtr
->numChars
- beforeSelect
- inSelect
;
785 if (afterSelect
> 0) {
786 TkDisplayChars(display
, drawable
, textPtr
->gc
, textPtr
->fontPtr
,
787 linePtr
->firstChar
+ beforeSelect
+ inSelect
,
788 afterSelect
, selEndX
- canvasPtr
->drawableXOrigin
,
789 linePtr
->y
- canvasPtr
->drawableYOrigin
, 0);
795 *--------------------------------------------------------------
799 * Insert characters into a text item at a given position.
802 * Always returns TCL_OK.
805 * The text in the given item is modified. The cursor and
806 * selection positions are also modified to reflect the
809 *--------------------------------------------------------------
813 TextInsert(canvasPtr
, itemPtr
, beforeThis
, string
)
814 Tk_Canvas
*canvasPtr
; /* Canvas containing text item. */
815 Tk_Item
*itemPtr
; /* Text item to be modified. */
816 int beforeThis
; /* Index of character before which text is
818 char *string
; /* New characters to be inserted. */
820 register TextItem
*textPtr
= (TextItem
*) itemPtr
;
824 length
= strlen(string
);
828 if (beforeThis
< 0) {
831 if (beforeThis
> textPtr
->numChars
) {
832 beforeThis
= textPtr
->numChars
;
835 new = (char *) ckalloc((unsigned) (textPtr
->numChars
+ length
+ 1));
836 strncpy(new, textPtr
->text
, beforeThis
);
837 strcpy(new+beforeThis
, string
);
838 strcpy(new+beforeThis
+length
, textPtr
->text
+beforeThis
);
839 ckfree(textPtr
->text
);
841 textPtr
->numChars
+= length
;
844 * Inserting characters invalidates indices such as those for the
845 * selection and cursor. Update the indices appropriately.
848 if (canvasPtr
->selItemPtr
== itemPtr
) {
849 if (canvasPtr
->selectFirst
>= beforeThis
) {
850 canvasPtr
->selectFirst
+= length
;
852 if (canvasPtr
->selectLast
>= beforeThis
) {
853 canvasPtr
->selectLast
+= length
;
855 if ((canvasPtr
->anchorItemPtr
== itemPtr
)
856 && (canvasPtr
->selectAnchor
>= beforeThis
)) {
857 canvasPtr
->selectAnchor
+= length
;
860 if (textPtr
->cursorPos
>= beforeThis
) {
861 textPtr
->cursorPos
+= length
;
863 ComputeTextBbox(canvasPtr
, textPtr
);
868 *--------------------------------------------------------------
872 * Delete one or more characters from a text item.
875 * Always returns TCL_OK.
878 * Characters between "first" and "last", inclusive, get
879 * deleted from itemPtr, and things like the selection
880 * position get updated.
882 *--------------------------------------------------------------
886 TextDeleteChars(canvasPtr
, itemPtr
, first
, last
)
887 Tk_Canvas
*canvasPtr
; /* Canvas containing itemPtr. */
888 Tk_Item
*itemPtr
; /* Item in which to delete characters. */
889 int first
; /* Index of first character to delete. */
890 int last
; /* Index of last character to delete. */
892 register TextItem
*textPtr
= (TextItem
*) itemPtr
;
899 if (last
>= textPtr
->numChars
) {
900 last
= textPtr
->numChars
-1;
905 count
= last
+ 1 - first
;
907 new = ckalloc((unsigned) (textPtr
->numChars
+ 1 - count
));
908 strncpy(new, textPtr
->text
, first
);
909 strcpy(new+first
, textPtr
->text
+last
+1);
910 ckfree(textPtr
->text
);
912 textPtr
->numChars
-= count
;
915 * Update indexes for the selection and cursor to reflect the
916 * renumbering of the remaining characters.
919 if (canvasPtr
->selItemPtr
== itemPtr
) {
920 if (canvasPtr
->selectFirst
> first
) {
921 canvasPtr
->selectFirst
-= count
;
922 if (canvasPtr
->selectFirst
< first
) {
923 canvasPtr
->selectFirst
= first
;
926 if (canvasPtr
->selectLast
>= first
) {
927 canvasPtr
->selectLast
-= count
;
928 if (canvasPtr
->selectLast
< (first
-1)) {
929 canvasPtr
->selectLast
= (first
-1);
932 if (canvasPtr
->selectFirst
> canvasPtr
->selectLast
) {
933 canvasPtr
->selItemPtr
= NULL
;
935 if ((canvasPtr
->anchorItemPtr
== itemPtr
)
936 && (canvasPtr
->selectAnchor
> first
)) {
937 canvasPtr
->selectAnchor
-= count
;
938 if (canvasPtr
->selectAnchor
< first
) {
939 canvasPtr
->selectAnchor
= first
;
943 if (textPtr
->cursorPos
> first
) {
944 textPtr
->cursorPos
-= count
;
945 if (textPtr
->cursorPos
< first
) {
946 textPtr
->cursorPos
= first
;
949 ComputeTextBbox(canvasPtr
, textPtr
);
954 *--------------------------------------------------------------
958 * Computes the distance from a given point to a given
959 * text item, in canvas units.
962 * The return value is 0 if the point whose x and y coordinates
963 * are pointPtr[0] and pointPtr[1] is inside the arc. If the
964 * point isn't inside the arc then the return value is the
965 * distance from the point to the arc.
970 *--------------------------------------------------------------
975 TextToPoint(canvasPtr
, itemPtr
, pointPtr
)
976 Tk_Canvas
*canvasPtr
; /* Canvas containing itemPtr. */
977 Tk_Item
*itemPtr
; /* Item to check against point. */
978 double *pointPtr
; /* Pointer to x and y coordinates. */
980 TextItem
*textPtr
= (TextItem
*) itemPtr
;
981 register TextLine
*linePtr
;
983 double xDiff
, yDiff
, dist
, minDist
;
986 * Treat each line in the text item as a rectangle, compute the
987 * distance to that rectangle, and take the minimum of these
988 * distances. Perform most of the calculations in integer pixel
989 * units, since that's how the dimensions of the text are defined.
993 for (linePtr
= textPtr
->linePtr
, i
= textPtr
->numLines
;
994 i
> 0; linePtr
++, i
--) {
997 * If the point is inside the line's rectangle, then can
998 * return immediately.
1001 if ((pointPtr
[0] >= linePtr
->x1
)
1002 && (pointPtr
[0] <= linePtr
->x2
)
1003 && (pointPtr
[1] >= linePtr
->y1
)
1004 && (pointPtr
[1] <= linePtr
->y2
)) {
1009 * Point is outside line's rectangle; compute distance to nearest
1013 if (pointPtr
[0] < linePtr
->x1
) {
1014 xDiff
= linePtr
->x1
- pointPtr
[0];
1015 } else if (pointPtr
[0] > linePtr
->x2
) {
1016 xDiff
= pointPtr
[0] - linePtr
->x2
;
1021 if (pointPtr
[1] < linePtr
->y1
) {
1022 yDiff
= linePtr
->y1
- pointPtr
[1];
1023 } else if (pointPtr
[1] > linePtr
->y2
) {
1024 yDiff
= pointPtr
[1] - linePtr
->y2
;
1029 dist
= hypot((float) xDiff
, (float) yDiff
);
1030 if ((dist
< minDist
) || (minDist
< 0.0)) {
1038 *--------------------------------------------------------------
1042 * This procedure is called to determine whether an item
1043 * lies entirely inside, entirely outside, or overlapping
1044 * a given rectangle.
1047 * -1 is returned if the item is entirely outside the area
1048 * given by rectPtr, 0 if it overlaps, and 1 if it is entirely
1049 * inside the given area.
1054 *--------------------------------------------------------------
1059 TextToArea(canvasPtr
, itemPtr
, rectPtr
)
1060 Tk_Canvas
*canvasPtr
; /* Canvas containing itemPtr. */
1061 Tk_Item
*itemPtr
; /* Item to check against rectangle. */
1062 double *rectPtr
; /* Pointer to array of four coordinates
1063 * (x1, y1, x2, y2) describing rectangular
1066 TextItem
*textPtr
= (TextItem
*) itemPtr
;
1067 register TextLine
*linePtr
;
1071 * Scan the lines one at a time, seeing whether each line is
1072 * entirely in, entirely out, or overlapping the rectangle. If
1073 * an overlap is detected, return immediately; otherwise wait
1074 * until all lines have been processed and see if they were all
1075 * inside or all outside.
1079 for (linePtr
= textPtr
->linePtr
, i
= textPtr
->numLines
;
1080 i
> 0; linePtr
++, i
--) {
1081 if ((rectPtr
[2] < linePtr
->x1
) || (rectPtr
[0] > linePtr
->x2
)
1082 || (rectPtr
[3] < linePtr
->y1
) || (rectPtr
[1] > linePtr
->y2
)) {
1089 if ((linePtr
->x1
< rectPtr
[0]) || (linePtr
->x2
> rectPtr
[2])
1090 || (linePtr
->y1
< rectPtr
[1]) || (linePtr
->y2
> rectPtr
[3])) {
1102 *--------------------------------------------------------------
1106 * This procedure is invoked to rescale a text item.
1112 * Scales the position of the text, but not the size
1113 * of the font for the text.
1115 *--------------------------------------------------------------
1120 ScaleText(canvasPtr
, itemPtr
, originX
, originY
, scaleX
, scaleY
)
1121 Tk_Canvas
*canvasPtr
; /* Canvas containing rectangle. */
1122 Tk_Item
*itemPtr
; /* Rectangle to be scaled. */
1123 double originX
, originY
; /* Origin about which to scale rect. */
1124 double scaleX
; /* Amount to scale in X direction. */
1125 double scaleY
; /* Amount to scale in Y direction. */
1127 register TextItem
*textPtr
= (TextItem
*) itemPtr
;
1129 textPtr
->x
= originX
+ scaleX
*(textPtr
->x
- originX
);
1130 textPtr
->y
= originY
+ scaleY
*(textPtr
->y
- originY
);
1131 ComputeTextBbox(canvasPtr
, textPtr
);
1136 *--------------------------------------------------------------
1140 * This procedure is called to move a text item by a
1147 * The position of the text item is offset by (xDelta, yDelta),
1148 * and the bounding box is updated in the generic part of the
1151 *--------------------------------------------------------------
1155 TranslateText(canvasPtr
, itemPtr
, deltaX
, deltaY
)
1156 Tk_Canvas
*canvasPtr
; /* Canvas containing item. */
1157 Tk_Item
*itemPtr
; /* Item that is being moved. */
1158 double deltaX
, deltaY
; /* Amount by which item is to be
1161 register TextItem
*textPtr
= (TextItem
*) itemPtr
;
1163 textPtr
->x
+= deltaX
;
1164 textPtr
->y
+= deltaY
;
1165 ComputeTextBbox(canvasPtr
, textPtr
);
1169 *--------------------------------------------------------------
1173 * Parse an index into a text item and return either its value
1177 * A standard Tcl result. If all went well, then *indexPtr is
1178 * filled in with the index (into itemPtr) corresponding to
1179 * string. Otherwise an error message is left in
1180 * canvasPtr->interp->result.
1185 *--------------------------------------------------------------
1189 GetTextIndex(canvasPtr
, itemPtr
, string
, indexPtr
)
1190 Tk_Canvas
*canvasPtr
; /* Canvas containing item. */
1191 Tk_Item
*itemPtr
; /* Item for which the index is being
1193 char *string
; /* Specification of a particular character
1194 * in itemPtr's text. */
1195 int *indexPtr
; /* Where to store converted index. */
1197 register TextItem
*textPtr
= (TextItem
*) itemPtr
;
1200 length
= strlen(string
);
1202 if (string
[0] == 'e') {
1203 if (strncmp(string
, "end", length
) == 0) {
1204 *indexPtr
= textPtr
->numChars
;
1209 * Some of the paths here leave messages in
1210 * canvasPtr->interp->result, so we have to clear it out
1211 * before storing our own message.
1214 Tcl_SetResult(canvasPtr
->interp
, (char *) NULL
, TCL_STATIC
);
1215 Tcl_AppendResult(canvasPtr
->interp
, "bad index \"", string
,
1216 "\"", (char *) NULL
);
1219 } else if (string
[0] == 'c') {
1220 if (strncmp(string
, "cursor", length
) == 0) {
1221 *indexPtr
= textPtr
->cursorPos
;
1225 } else if (string
[0] == 's') {
1226 if (canvasPtr
->selItemPtr
!= itemPtr
) {
1227 canvasPtr
->interp
->result
= "selection isn't in item";
1233 if (strncmp(string
, "sel.first", length
) == 0) {
1234 *indexPtr
= canvasPtr
->selectFirst
;
1235 } else if (strncmp(string
, "sel.last", length
) == 0) {
1236 *indexPtr
= canvasPtr
->selectLast
;
1240 } else if (string
[0] == '@') {
1243 register TextLine
*linePtr
;
1246 x
= strtol(p
, &end
, 0);
1247 if ((end
== p
) || (*end
!= ',')) {
1251 y
= strtol(p
, &end
, 0);
1252 if ((end
== p
) || (*end
!= 0)) {
1255 if ((textPtr
->numChars
== 0) || (y
< textPtr
->linePtr
[0].y1
)) {
1259 for (i
= 0, linePtr
= textPtr
->linePtr
; ; i
++, linePtr
++) {
1260 if (i
>= textPtr
->numLines
) {
1261 *indexPtr
= textPtr
->numChars
;
1264 if (y
<= linePtr
->y2
) {
1268 *indexPtr
= TkMeasureChars(textPtr
->fontPtr
, linePtr
->firstChar
,
1269 linePtr
->numChars
, linePtr
->x
, x
, 0, &dummy
);
1270 *indexPtr
+= linePtr
->firstChar
- textPtr
->text
;
1272 if (Tcl_GetInt(canvasPtr
->interp
, string
, indexPtr
) != TCL_OK
) {
1277 } else if (*indexPtr
> textPtr
->numChars
) {
1278 *indexPtr
= textPtr
->numChars
;
1285 *--------------------------------------------------------------
1289 * Set the position of the insertion cursor in this item.
1295 * The cursor position will change.
1297 *--------------------------------------------------------------
1302 SetTextCursor(canvasPtr
, itemPtr
, index
)
1303 Tk_Canvas
*canvasPtr
; /* Record describing canvas widget. */
1304 Tk_Item
*itemPtr
; /* Text item in which cursor position
1306 int index
; /* Index of character just before which
1307 * cursor is to be positioned. */
1309 register TextItem
*textPtr
= (TextItem
*) itemPtr
;
1312 textPtr
->cursorPos
= 0;
1313 } else if (index
> textPtr
->numChars
) {
1314 textPtr
->cursorPos
= textPtr
->numChars
;
1316 textPtr
->cursorPos
= index
;
1321 *--------------------------------------------------------------
1325 * This procedure is invoked to return the selected portion
1326 * of a text item. It is only called when this item has
1330 * The return value is the number of non-NULL bytes stored
1331 * at buffer. Buffer is filled (or partially filled) with a
1332 * NULL-terminated string containing part or all of the selection,
1333 * as given by offset and maxBytes.
1338 *--------------------------------------------------------------
1342 GetSelText(canvasPtr
, itemPtr
, offset
, buffer
, maxBytes
)
1343 Tk_Canvas
*canvasPtr
; /* Canvas containing selection. */
1344 Tk_Item
*itemPtr
; /* Text item containing selection. */
1345 int offset
; /* Offset within selection of first
1346 * character to be returned. */
1347 char *buffer
; /* Location in which to place
1349 int maxBytes
; /* Maximum number of bytes to place
1350 * at buffer, not including terminating
1351 * NULL character. */
1353 TextItem
*textPtr
= (TextItem
*) itemPtr
;
1356 count
= canvasPtr
->selectLast
+ 1 - canvasPtr
->selectFirst
- offset
;
1357 if (canvasPtr
->selectLast
== textPtr
->numChars
) {
1360 if (count
> maxBytes
) {
1366 strncpy(buffer
, textPtr
->text
+ canvasPtr
->selectFirst
+ offset
, count
);
1367 buffer
[count
] = '\0';