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