Commit | Line | Data |
---|---|---|
6a5fa4e0 MG |
1 | /* |
2 | * tkCanvText.c -- | |
3 | * | |
4 | * This file implements text items for canvas widgets. | |
5 | * | |
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. | |
14 | */ | |
15 | ||
16 | #ifndef lint | |
17 | static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkCanvText.c,v 1.12 92/08/19 08:34:12 ouster Exp $ SPRITE (Berkeley)"; | |
18 | #endif | |
19 | ||
20 | #include <stdio.h> | |
21 | #include "tkint.h" | |
22 | #include "tkcanvas.h" | |
23 | #include "tkconfig.h" | |
24 | ||
25 | /* | |
26 | * One of the following structures is kept for each line of text | |
27 | * in a text item. It contains geometry and display information | |
28 | * for that line. | |
29 | */ | |
30 | ||
31 | typedef struct TextLine { | |
32 | char *firstChar; /* Pointer to the first character in this | |
33 | * line (in the "text" field of enclosing | |
34 | * text item). */ | |
35 | int numChars; /* Number of characters displayed in this | |
36 | * line. */ | |
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 | |
40 | * displayed). */ | |
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 | |
46 | * pixel units). */ | |
47 | int x2, y2; /* Lower-left pixel that is part of text | |
48 | * line on screen (again, in integer canvas | |
49 | * pixel units). */ | |
50 | } TextLine; | |
51 | ||
52 | /* | |
53 | * The structure below defines the record for each text item. | |
54 | */ | |
55 | ||
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. */ | |
79 | } TextItem; | |
80 | ||
81 | /* | |
82 | * Information used for parsing configuration specs: | |
83 | */ | |
84 | ||
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, | |
106 | (char *) NULL, 0, 0} | |
107 | }; | |
108 | ||
109 | /* | |
110 | * Prototypes for procedures defined in this file: | |
111 | */ | |
112 | ||
113 | static void ComputeTextBbox _ANSI_ARGS_((Tk_Canvas *canvasPtr, | |
114 | TextItem *textPtr)); | |
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, | |
125 | int maxBytes)); | |
126 | static int GetTextIndex _ANSI_ARGS_((Tk_Canvas *canvasPtr, | |
127 | Tk_Item *itemPtr, char *indexString, | |
128 | int *indexPtr)); | |
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)); | |
146 | ||
147 | /* | |
148 | * The structures below defines the rectangle and oval item types | |
149 | * by means of procedures that can be invoked by generic item code. | |
150 | */ | |
151 | ||
152 | Tk_ItemType TkTextType = { | |
153 | "text", /* name */ | |
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 */ | |
173 | }; | |
174 | \f | |
175 | /* | |
176 | *-------------------------------------------------------------- | |
177 | * | |
178 | * CreateText -- | |
179 | * | |
180 | * This procedure is invoked to create a new text item | |
181 | * in a canvas. | |
182 | * | |
183 | * Results: | |
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 | |
188 | * caller. | |
189 | * | |
190 | * Side effects: | |
191 | * A new text item is created. | |
192 | * | |
193 | *-------------------------------------------------------------- | |
194 | */ | |
195 | ||
196 | static int | |
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. */ | |
203 | { | |
204 | register TextItem *textPtr = (TextItem *) itemPtr; | |
205 | ||
206 | if (argc < 2) { | |
207 | Tcl_AppendResult(canvasPtr->interp, "wrong # args: should be \"", | |
208 | Tk_PathName(canvasPtr->tkwin), | |
209 | "\" create text x y [options]", (char *) NULL); | |
210 | return TCL_ERROR; | |
211 | } | |
212 | ||
213 | /* | |
214 | * Carry out initialization that is needed in order to clean | |
215 | * up after errors during the the remainder of this procedure. | |
216 | */ | |
217 | ||
218 | textPtr->text = NULL; | |
219 | textPtr->anchor = TK_ANCHOR_CENTER; | |
220 | textPtr->width = 0; | |
221 | textPtr->justify = TK_JUSTIFY_LEFT; | |
222 | textPtr->fontPtr = NULL; | |
223 | textPtr->color = NULL; | |
224 | textPtr->stipple = None; | |
225 | textPtr->gc = None; | |
226 | textPtr->linePtr = NULL; | |
227 | textPtr->numLines = 0; | |
228 | textPtr->cursorPos = 0; | |
229 | textPtr->selTextGC = None; | |
230 | ||
231 | /* | |
232 | * Process the arguments to fill in the item record. | |
233 | */ | |
234 | ||
235 | if ((TkGetCanvasCoord(canvasPtr, argv[0], &textPtr->x) != TCL_OK) | |
236 | || (TkGetCanvasCoord(canvasPtr, argv[1], &textPtr->y) != TCL_OK)) { | |
237 | return TCL_ERROR; | |
238 | } | |
239 | ||
240 | if (ConfigureText(canvasPtr, itemPtr, argc-2, argv+2, 0) != TCL_OK) { | |
241 | DeleteText(itemPtr); | |
242 | return TCL_ERROR; | |
243 | } | |
244 | return TCL_OK; | |
245 | } | |
246 | \f | |
247 | /* | |
248 | *-------------------------------------------------------------- | |
249 | * | |
250 | * TextCoords -- | |
251 | * | |
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. | |
255 | * | |
256 | * Results: | |
257 | * Returns TCL_OK or TCL_ERROR, and sets canvasPtr->interp->result. | |
258 | * | |
259 | * Side effects: | |
260 | * The coordinates for the given item may be changed. | |
261 | * | |
262 | *-------------------------------------------------------------- | |
263 | */ | |
264 | ||
265 | static int | |
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 | |
271 | * argv. */ | |
272 | char **argv; /* Array of coordinates: x1, y1, | |
273 | * x2, y2, ... */ | |
274 | { | |
275 | register TextItem *textPtr = (TextItem *) itemPtr; | |
276 | ||
277 | if (argc == 0) { | |
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)) { | |
283 | return TCL_ERROR; | |
284 | } | |
285 | ComputeTextBbox(canvasPtr, textPtr); | |
286 | } else { | |
287 | sprintf(canvasPtr->interp->result, | |
288 | "wrong # coordinates: expected 0 or 2, got %d", | |
289 | argc); | |
290 | return TCL_ERROR; | |
291 | } | |
292 | return TCL_OK; | |
293 | } | |
294 | \f | |
295 | /* | |
296 | *-------------------------------------------------------------- | |
297 | * | |
298 | * ConfigureText -- | |
299 | * | |
300 | * This procedure is invoked to configure various aspects | |
301 | * of a text item, such as its border and background colors. | |
302 | * | |
303 | * Results: | |
304 | * A standard Tcl result code. If an error occurs, then | |
305 | * an error message is left in canvasPtr->interp->result. | |
306 | * | |
307 | * Side effects: | |
308 | * Configuration information, such as colors and stipple | |
309 | * patterns, may be set for itemPtr. | |
310 | * | |
311 | *-------------------------------------------------------------- | |
312 | */ | |
313 | ||
314 | static int | |
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. */ | |
321 | { | |
322 | register TextItem *textPtr = (TextItem *) itemPtr; | |
323 | XGCValues gcValues; | |
324 | GC newGC, newSelGC; | |
325 | unsigned long mask; | |
326 | ||
327 | if (Tk_ConfigureWidget(canvasPtr->interp, canvasPtr->tkwin, | |
328 | configSpecs, argc, argv, (char *) textPtr, flags) != TCL_OK) { | |
329 | return TCL_ERROR; | |
330 | } | |
331 | ||
332 | /* | |
333 | * A few of the options require additional processing, such as | |
334 | * graphics contexts. | |
335 | */ | |
336 | ||
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; | |
347 | } | |
348 | newGC = Tk_GetGC(canvasPtr->tkwin, mask, &gcValues); | |
349 | gcValues.foreground = canvasPtr->selFgColorPtr->pixel; | |
350 | newSelGC = Tk_GetGC(canvasPtr->tkwin, mask, &gcValues); | |
351 | } | |
352 | if (textPtr->gc != None) { | |
353 | Tk_FreeGC(textPtr->gc); | |
354 | } | |
355 | textPtr->gc = newGC; | |
356 | if (textPtr->selTextGC != None) { | |
357 | Tk_FreeGC(textPtr->selTextGC); | |
358 | } | |
359 | textPtr->selTextGC = newSelGC; | |
360 | ||
361 | /* | |
362 | * If the text was changed, move the selection and insertion indices | |
363 | * to keep them inside the item. | |
364 | */ | |
365 | ||
366 | if (canvasPtr->selItemPtr == itemPtr) { | |
367 | if (canvasPtr->selectFirst >= textPtr->numChars) { | |
368 | canvasPtr->selItemPtr = NULL; | |
369 | } else { | |
370 | if (canvasPtr->selectLast >= textPtr->numChars) { | |
371 | canvasPtr->selectLast = textPtr->numChars-1; | |
372 | } | |
373 | if ((canvasPtr->anchorItemPtr == itemPtr) | |
374 | && (canvasPtr->selectAnchor >= textPtr->numChars)) { | |
375 | canvasPtr->selectAnchor = textPtr->numChars-1; | |
376 | } | |
377 | } | |
378 | } | |
379 | if (textPtr->cursorPos >= textPtr->numChars) { | |
380 | textPtr->cursorPos = textPtr->numChars; | |
381 | } | |
382 | ||
383 | ComputeTextBbox(canvasPtr, textPtr); | |
384 | return TCL_OK; | |
385 | } | |
386 | \f | |
387 | /* | |
388 | *-------------------------------------------------------------- | |
389 | * | |
390 | * DeleteText -- | |
391 | * | |
392 | * This procedure is called to clean up the data structure | |
393 | * associated with a text item. | |
394 | * | |
395 | * Results: | |
396 | * None. | |
397 | * | |
398 | * Side effects: | |
399 | * Resources associated with itemPtr are released. | |
400 | * | |
401 | *-------------------------------------------------------------- | |
402 | */ | |
403 | ||
404 | static void | |
405 | DeleteText(itemPtr) | |
406 | Tk_Item *itemPtr; /* Item that is being deleted. */ | |
407 | { | |
408 | register TextItem *textPtr = (TextItem *) itemPtr; | |
409 | ||
410 | if (textPtr->text != NULL) { | |
411 | ckfree(textPtr->text); | |
412 | } | |
413 | if (textPtr->fontPtr != NULL) { | |
414 | Tk_FreeFontStruct(textPtr->fontPtr); | |
415 | } | |
416 | if (textPtr->color != NULL) { | |
417 | Tk_FreeColor(textPtr->color); | |
418 | } | |
419 | if (textPtr->stipple != None) { | |
420 | Tk_FreeBitmap(textPtr->stipple); | |
421 | } | |
422 | if (textPtr->gc != None) { | |
423 | Tk_FreeGC(textPtr->gc); | |
424 | } | |
425 | if (textPtr->linePtr != NULL) { | |
426 | ckfree((char *) textPtr->linePtr); | |
427 | } | |
428 | if (textPtr->selTextGC != None) { | |
429 | Tk_FreeGC(textPtr->selTextGC); | |
430 | } | |
431 | } | |
432 | \f | |
433 | /* | |
434 | *-------------------------------------------------------------- | |
435 | * | |
436 | * ComputeTextBbox -- | |
437 | * | |
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. | |
442 | * | |
443 | * Results: | |
444 | * None. | |
445 | * | |
446 | * Side effects: | |
447 | * The fields x1, y1, x2, and y2 are updated in the header | |
448 | * for itemPtr, and the linePtr structure is regenerated | |
449 | * for itemPtr. | |
450 | * | |
451 | *-------------------------------------------------------------- | |
452 | */ | |
453 | ||
454 | static void | |
455 | ComputeTextBbox(canvasPtr, textPtr) | |
456 | register Tk_Canvas *canvasPtr; /* Canvas that contains item. */ | |
457 | register TextItem *textPtr; /* Item whose bbos is to be | |
458 | * recomputed. */ | |
459 | { | |
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; | |
467 | char *p; | |
468 | XCharStruct *maxBoundsPtr = &textPtr->fontPtr->max_bounds; | |
469 | ||
470 | if (textPtr->linePtr != NULL) { | |
471 | ckfree((char *) textPtr->linePtr); | |
472 | textPtr->linePtr = NULL; | |
473 | } | |
474 | ||
475 | /* | |
476 | * Work through the text computing the starting point, number of | |
477 | * characters, and number of pixels in each line. | |
478 | */ | |
479 | ||
480 | p = textPtr->text; | |
481 | if (textPtr->width > 0) { | |
482 | wrapPixels = maxLinePixels = textPtr->width; | |
483 | } else { | |
484 | wrapPixels = 10000000; | |
485 | maxLinePixels = 0; | |
486 | } | |
487 | for (numLines = 0; (numLines < MAX_LINES) && (*p != 0); | |
488 | numLines++) { | |
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; | |
495 | } | |
496 | lineStart[numLines] = p; | |
497 | lineChars[numLines] = numChars; | |
498 | linePixels[numLines] = numPixels; | |
499 | p += numChars; | |
500 | ||
501 | /* | |
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. | |
506 | */ | |
507 | ||
508 | if (isspace(*p)) { | |
509 | p++; | |
510 | } | |
511 | } | |
512 | ||
513 | /* | |
514 | * Use overall geometry information to compute the top-left corner | |
515 | * of the bounding box for the text item. | |
516 | */ | |
517 | ||
518 | leftX = textPtr->x + 0.5; | |
519 | topY = textPtr->y + 0.5; | |
520 | lineHeight = textPtr->fontPtr->ascent + textPtr->fontPtr->descent; | |
521 | switch (textPtr->anchor) { | |
522 | case TK_ANCHOR_NW: | |
523 | case TK_ANCHOR_N: | |
524 | case TK_ANCHOR_NE: | |
525 | break; | |
526 | ||
527 | case TK_ANCHOR_W: | |
528 | case TK_ANCHOR_CENTER: | |
529 | case TK_ANCHOR_E: | |
530 | topY -= (lineHeight * numLines)/2; | |
531 | break; | |
532 | ||
533 | case TK_ANCHOR_SW: | |
534 | case TK_ANCHOR_S: | |
535 | case TK_ANCHOR_SE: | |
536 | topY -= lineHeight * numLines; | |
537 | break; | |
538 | } | |
539 | switch (textPtr->anchor) { | |
540 | case TK_ANCHOR_NW: | |
541 | case TK_ANCHOR_W: | |
542 | case TK_ANCHOR_SW: | |
543 | break; | |
544 | ||
545 | case TK_ANCHOR_N: | |
546 | case TK_ANCHOR_CENTER: | |
547 | case TK_ANCHOR_S: | |
548 | leftX -= maxLinePixels/2; | |
549 | break; | |
550 | ||
551 | case TK_ANCHOR_NE: | |
552 | case TK_ANCHOR_E: | |
553 | case TK_ANCHOR_SE: | |
554 | leftX -= maxLinePixels; | |
555 | break; | |
556 | } | |
557 | textPtr->rightEdge = leftX + maxLinePixels; | |
558 | ||
559 | /* | |
560 | * Create the new TextLine array and fill it in using the geometry | |
561 | * information gathered already. | |
562 | */ | |
563 | ||
564 | if (numLines > 0) { | |
565 | textPtr->linePtr = (TextLine *) ckalloc((unsigned) | |
566 | (numLines * sizeof(TextLine))); | |
567 | } else { | |
568 | textPtr->linePtr = NULL; | |
569 | } | |
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; | |
577 | } else { | |
578 | linePtr->totalChars = lineStart[i+1] - lineStart[i]; | |
579 | } | |
580 | switch (textPtr->justify) { | |
581 | case TK_JUSTIFY_LEFT: | |
582 | case TK_JUSTIFY_FILL: | |
583 | linePtr->x = leftX; | |
584 | break; | |
585 | case TK_JUSTIFY_CENTER: | |
586 | linePtr->x = leftX + maxLinePixels/2 - linePixels[i]/2; | |
587 | break; | |
588 | case TK_JUSTIFY_RIGHT: | |
589 | linePtr->x = leftX + maxLinePixels - linePixels[i]; | |
590 | break; | |
591 | } | |
592 | linePtr->y = y + textPtr->fontPtr->ascent; | |
593 | linePtr->x1 = linePtr->x - maxBoundsPtr->lbearing; | |
594 | linePtr->y1 = y; | |
595 | linePtr->x2 = linePtr->x + linePixels[i] + maxBoundsPtr->rbearing | |
596 | - textPtr->fontPtr->min_bounds.rbearing; | |
597 | linePtr->y2 = linePtr->y + textPtr->fontPtr->descent - 1; | |
598 | } | |
599 | ||
600 | /* | |
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). | |
605 | */ | |
606 | ||
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; | |
612 | i--, linePtr++) { | |
613 | if (linePtr->x1 < textPtr->header.x1) { | |
614 | textPtr->header.x1 = linePtr->x1; | |
615 | } | |
616 | if (linePtr->x2 >= textPtr->header.x2) { | |
617 | textPtr->header.x2 = linePtr->x2 + 1; | |
618 | } | |
619 | } | |
620 | ||
621 | fudge = canvasPtr->cursorWidth/2; | |
622 | if (canvasPtr->selBorderWidth > fudge) { | |
623 | fudge = canvasPtr->selBorderWidth; | |
624 | } | |
625 | textPtr->header.x1 -= fudge; | |
626 | textPtr->header.x2 += fudge; | |
627 | } | |
628 | \f | |
629 | /* | |
630 | *-------------------------------------------------------------- | |
631 | * | |
632 | * DisplayText -- | |
633 | * | |
634 | * This procedure is invoked to draw a text item in a given | |
635 | * drawable. | |
636 | * | |
637 | * Results: | |
638 | * None. | |
639 | * | |
640 | * Side effects: | |
641 | * ItemPtr is drawn in drawable using the transformation | |
642 | * information in canvasPtr. | |
643 | * | |
644 | *-------------------------------------------------------------- | |
645 | */ | |
646 | ||
647 | static void | |
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 | |
652 | * item. */ | |
653 | { | |
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; | |
659 | ||
660 | if (textPtr->gc == None) { | |
661 | return; | |
662 | } | |
663 | focusHere = (canvasPtr->focusItemPtr == itemPtr) && | |
664 | (canvasPtr->flags & GOT_FOCUS); | |
665 | for (linePtr = textPtr->linePtr, i = textPtr->numLines; | |
666 | i > 0; linePtr++, i--) { | |
667 | ||
668 | /* | |
669 | * If part or all of this line is selected, then draw a special | |
670 | * background under the selected part of the line. | |
671 | */ | |
672 | ||
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; | |
679 | inSelect = 0; | |
680 | } else { | |
681 | beforeSelect = canvasPtr->selectFirst - lineIndex; | |
682 | if (beforeSelect <= 0) { | |
683 | beforeSelect = 0; | |
684 | selStartX = linePtr->x; | |
685 | } else { | |
686 | (void) TkMeasureChars(textPtr->fontPtr, | |
687 | linePtr->firstChar, beforeSelect, 0, | |
688 | (int) 1000000, TK_PARTIAL_OK, &selStartX); | |
689 | selStartX += linePtr->x; | |
690 | } | |
691 | inSelect = canvasPtr->selectLast + 1 - (lineIndex + beforeSelect); | |
692 | ||
693 | /* | |
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 | |
698 | * "i != 1" check. | |
699 | */ | |
700 | ||
701 | if (inSelect >= (linePtr->totalChars - beforeSelect)) { | |
702 | inSelect = linePtr->numChars - beforeSelect; | |
703 | if (i != 1) { | |
704 | selEndX = textPtr->rightEdge; | |
705 | goto fillSelectBackground; | |
706 | } | |
707 | } | |
708 | (void) TkMeasureChars(textPtr->fontPtr, | |
709 | linePtr->firstChar + beforeSelect, inSelect, | |
710 | selStartX-linePtr->x, (int) 1000000, TK_PARTIAL_OK, | |
711 | &selEndX); | |
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); | |
722 | } | |
723 | ||
724 | /* | |
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. | |
731 | */ | |
732 | ||
733 | if (focusHere) { | |
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, | |
753 | canvasPtr->bgBorder, | |
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, | |
761 | 0, TK_RELIEF_FLAT); | |
762 | } | |
763 | } | |
764 | } | |
765 | ||
766 | /* | |
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. | |
770 | */ | |
771 | ||
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); | |
777 | } | |
778 | if (inSelect != 0) { | |
779 | TkDisplayChars(display, drawable, textPtr->selTextGC, | |
780 | textPtr->fontPtr, linePtr->firstChar + beforeSelect, | |
781 | inSelect, selStartX - canvasPtr->drawableXOrigin, | |
782 | linePtr->y - canvasPtr->drawableYOrigin, 0); | |
783 | } | |
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); | |
790 | } | |
791 | } | |
792 | } | |
793 | \f | |
794 | /* | |
795 | *-------------------------------------------------------------- | |
796 | * | |
797 | * TextInsert -- | |
798 | * | |
799 | * Insert characters into a text item at a given position. | |
800 | * | |
801 | * Results: | |
802 | * Always returns TCL_OK. | |
803 | * | |
804 | * Side effects: | |
805 | * The text in the given item is modified. The cursor and | |
806 | * selection positions are also modified to reflect the | |
807 | * insertion. | |
808 | * | |
809 | *-------------------------------------------------------------- | |
810 | */ | |
811 | ||
812 | static int | |
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 | |
817 | * to be inserted. */ | |
818 | char *string; /* New characters to be inserted. */ | |
819 | { | |
820 | register TextItem *textPtr = (TextItem *) itemPtr; | |
821 | int length; | |
822 | char *new; | |
823 | ||
824 | length = strlen(string); | |
825 | if (length == 0) { | |
826 | return TCL_OK; | |
827 | } | |
828 | if (beforeThis < 0) { | |
829 | beforeThis = 0; | |
830 | } | |
831 | if (beforeThis > textPtr->numChars) { | |
832 | beforeThis = textPtr->numChars; | |
833 | } | |
834 | ||
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); | |
840 | textPtr->text = new; | |
841 | textPtr->numChars += length; | |
842 | ||
843 | /* | |
844 | * Inserting characters invalidates indices such as those for the | |
845 | * selection and cursor. Update the indices appropriately. | |
846 | */ | |
847 | ||
848 | if (canvasPtr->selItemPtr == itemPtr) { | |
849 | if (canvasPtr->selectFirst >= beforeThis) { | |
850 | canvasPtr->selectFirst += length; | |
851 | } | |
852 | if (canvasPtr->selectLast >= beforeThis) { | |
853 | canvasPtr->selectLast += length; | |
854 | } | |
855 | if ((canvasPtr->anchorItemPtr == itemPtr) | |
856 | && (canvasPtr->selectAnchor >= beforeThis)) { | |
857 | canvasPtr->selectAnchor += length; | |
858 | } | |
859 | } | |
860 | if (textPtr->cursorPos >= beforeThis) { | |
861 | textPtr->cursorPos += length; | |
862 | } | |
863 | ComputeTextBbox(canvasPtr, textPtr); | |
864 | return TCL_OK; | |
865 | } | |
866 | \f | |
867 | /* | |
868 | *-------------------------------------------------------------- | |
869 | * | |
870 | * TextDeleteChars -- | |
871 | * | |
872 | * Delete one or more characters from a text item. | |
873 | * | |
874 | * Results: | |
875 | * Always returns TCL_OK. | |
876 | * | |
877 | * Side effects: | |
878 | * Characters between "first" and "last", inclusive, get | |
879 | * deleted from itemPtr, and things like the selection | |
880 | * position get updated. | |
881 | * | |
882 | *-------------------------------------------------------------- | |
883 | */ | |
884 | ||
885 | static int | |
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. */ | |
891 | { | |
892 | register TextItem *textPtr = (TextItem *) itemPtr; | |
893 | int count; | |
894 | char *new; | |
895 | ||
896 | if (first < 0) { | |
897 | first = 0; | |
898 | } | |
899 | if (last >= textPtr->numChars) { | |
900 | last = textPtr->numChars-1; | |
901 | } | |
902 | if (first > last) { | |
903 | return TCL_OK; | |
904 | } | |
905 | count = last + 1 - first; | |
906 | ||
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); | |
911 | textPtr->text = new; | |
912 | textPtr->numChars -= count; | |
913 | ||
914 | /* | |
915 | * Update indexes for the selection and cursor to reflect the | |
916 | * renumbering of the remaining characters. | |
917 | */ | |
918 | ||
919 | if (canvasPtr->selItemPtr == itemPtr) { | |
920 | if (canvasPtr->selectFirst > first) { | |
921 | canvasPtr->selectFirst -= count; | |
922 | if (canvasPtr->selectFirst < first) { | |
923 | canvasPtr->selectFirst = first; | |
924 | } | |
925 | } | |
926 | if (canvasPtr->selectLast >= first) { | |
927 | canvasPtr->selectLast -= count; | |
928 | if (canvasPtr->selectLast < (first-1)) { | |
929 | canvasPtr->selectLast = (first-1); | |
930 | } | |
931 | } | |
932 | if (canvasPtr->selectFirst > canvasPtr->selectLast) { | |
933 | canvasPtr->selItemPtr = NULL; | |
934 | } | |
935 | if ((canvasPtr->anchorItemPtr == itemPtr) | |
936 | && (canvasPtr->selectAnchor > first)) { | |
937 | canvasPtr->selectAnchor -= count; | |
938 | if (canvasPtr->selectAnchor < first) { | |
939 | canvasPtr->selectAnchor = first; | |
940 | } | |
941 | } | |
942 | } | |
943 | if (textPtr->cursorPos > first) { | |
944 | textPtr->cursorPos -= count; | |
945 | if (textPtr->cursorPos < first) { | |
946 | textPtr->cursorPos = first; | |
947 | } | |
948 | } | |
949 | ComputeTextBbox(canvasPtr, textPtr); | |
950 | return TCL_OK; | |
951 | } | |
952 | \f | |
953 | /* | |
954 | *-------------------------------------------------------------- | |
955 | * | |
956 | * TextToPoint -- | |
957 | * | |
958 | * Computes the distance from a given point to a given | |
959 | * text item, in canvas units. | |
960 | * | |
961 | * Results: | |
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. | |
966 | * | |
967 | * Side effects: | |
968 | * None. | |
969 | * | |
970 | *-------------------------------------------------------------- | |
971 | */ | |
972 | ||
973 | /* ARGSUSED */ | |
974 | static double | |
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. */ | |
979 | { | |
980 | TextItem *textPtr = (TextItem *) itemPtr; | |
981 | register TextLine *linePtr; | |
982 | int i; | |
983 | double xDiff, yDiff, dist, minDist; | |
984 | ||
985 | /* | |
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. | |
990 | */ | |
991 | ||
992 | minDist = -1.0; | |
993 | for (linePtr = textPtr->linePtr, i = textPtr->numLines; | |
994 | i > 0; linePtr++, i--) { | |
995 | ||
996 | /* | |
997 | * If the point is inside the line's rectangle, then can | |
998 | * return immediately. | |
999 | */ | |
1000 | ||
1001 | if ((pointPtr[0] >= linePtr->x1) | |
1002 | && (pointPtr[0] <= linePtr->x2) | |
1003 | && (pointPtr[1] >= linePtr->y1) | |
1004 | && (pointPtr[1] <= linePtr->y2)) { | |
1005 | return 0.0; | |
1006 | } | |
1007 | ||
1008 | /* | |
1009 | * Point is outside line's rectangle; compute distance to nearest | |
1010 | * side. | |
1011 | */ | |
1012 | ||
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; | |
1017 | } else { | |
1018 | xDiff = 0; | |
1019 | } | |
1020 | ||
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; | |
1025 | } else { | |
1026 | yDiff = 0; | |
1027 | } | |
1028 | ||
1029 | dist = hypot((float) xDiff, (float) yDiff); | |
1030 | if ((dist < minDist) || (minDist < 0.0)) { | |
1031 | minDist = dist; | |
1032 | } | |
1033 | } | |
1034 | return minDist; | |
1035 | } | |
1036 | \f | |
1037 | /* | |
1038 | *-------------------------------------------------------------- | |
1039 | * | |
1040 | * TextToArea -- | |
1041 | * | |
1042 | * This procedure is called to determine whether an item | |
1043 | * lies entirely inside, entirely outside, or overlapping | |
1044 | * a given rectangle. | |
1045 | * | |
1046 | * Results: | |
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. | |
1050 | * | |
1051 | * Side effects: | |
1052 | * None. | |
1053 | * | |
1054 | *-------------------------------------------------------------- | |
1055 | */ | |
1056 | ||
1057 | /* ARGSUSED */ | |
1058 | static int | |
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 | |
1064 | * area. */ | |
1065 | { | |
1066 | TextItem *textPtr = (TextItem *) itemPtr; | |
1067 | register TextLine *linePtr; | |
1068 | int i, result; | |
1069 | ||
1070 | /* | |
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. | |
1076 | */ | |
1077 | ||
1078 | result = 0; | |
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)) { | |
1083 | if (result == 1) { | |
1084 | return 0; | |
1085 | } | |
1086 | result = -1; | |
1087 | continue; | |
1088 | } | |
1089 | if ((linePtr->x1 < rectPtr[0]) || (linePtr->x2 > rectPtr[2]) | |
1090 | || (linePtr->y1 < rectPtr[1]) || (linePtr->y2 > rectPtr[3])) { | |
1091 | return 0; | |
1092 | } | |
1093 | if (result == -1) { | |
1094 | return 0; | |
1095 | } | |
1096 | result = 1; | |
1097 | } | |
1098 | return result; | |
1099 | } | |
1100 | \f | |
1101 | /* | |
1102 | *-------------------------------------------------------------- | |
1103 | * | |
1104 | * ScaleText -- | |
1105 | * | |
1106 | * This procedure is invoked to rescale a text item. | |
1107 | * | |
1108 | * Results: | |
1109 | * None. | |
1110 | * | |
1111 | * Side effects: | |
1112 | * Scales the position of the text, but not the size | |
1113 | * of the font for the text. | |
1114 | * | |
1115 | *-------------------------------------------------------------- | |
1116 | */ | |
1117 | ||
1118 | /* ARGSUSED */ | |
1119 | static void | |
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. */ | |
1126 | { | |
1127 | register TextItem *textPtr = (TextItem *) itemPtr; | |
1128 | ||
1129 | textPtr->x = originX + scaleX*(textPtr->x - originX); | |
1130 | textPtr->y = originY + scaleY*(textPtr->y - originY); | |
1131 | ComputeTextBbox(canvasPtr, textPtr); | |
1132 | return; | |
1133 | } | |
1134 | \f | |
1135 | /* | |
1136 | *-------------------------------------------------------------- | |
1137 | * | |
1138 | * TranslateText -- | |
1139 | * | |
1140 | * This procedure is called to move a text item by a | |
1141 | * given amount. | |
1142 | * | |
1143 | * Results: | |
1144 | * None. | |
1145 | * | |
1146 | * Side effects: | |
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 | |
1149 | * item structure. | |
1150 | * | |
1151 | *-------------------------------------------------------------- | |
1152 | */ | |
1153 | ||
1154 | static void | |
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 | |
1159 | * moved. */ | |
1160 | { | |
1161 | register TextItem *textPtr = (TextItem *) itemPtr; | |
1162 | ||
1163 | textPtr->x += deltaX; | |
1164 | textPtr->y += deltaY; | |
1165 | ComputeTextBbox(canvasPtr, textPtr); | |
1166 | } | |
1167 | \f | |
1168 | /* | |
1169 | *-------------------------------------------------------------- | |
1170 | * | |
1171 | * GetTextIndex -- | |
1172 | * | |
1173 | * Parse an index into a text item and return either its value | |
1174 | * or an error. | |
1175 | * | |
1176 | * Results: | |
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. | |
1181 | * | |
1182 | * Side effects: | |
1183 | * None. | |
1184 | * | |
1185 | *-------------------------------------------------------------- | |
1186 | */ | |
1187 | ||
1188 | static int | |
1189 | GetTextIndex(canvasPtr, itemPtr, string, indexPtr) | |
1190 | Tk_Canvas *canvasPtr; /* Canvas containing item. */ | |
1191 | Tk_Item *itemPtr; /* Item for which the index is being | |
1192 | * specified. */ | |
1193 | char *string; /* Specification of a particular character | |
1194 | * in itemPtr's text. */ | |
1195 | int *indexPtr; /* Where to store converted index. */ | |
1196 | { | |
1197 | register TextItem *textPtr = (TextItem *) itemPtr; | |
1198 | int length; | |
1199 | ||
1200 | length = strlen(string); | |
1201 | ||
1202 | if (string[0] == 'e') { | |
1203 | if (strncmp(string, "end", length) == 0) { | |
1204 | *indexPtr = textPtr->numChars; | |
1205 | } else { | |
1206 | badIndex: | |
1207 | ||
1208 | /* | |
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. | |
1212 | */ | |
1213 | ||
1214 | Tcl_SetResult(canvasPtr->interp, (char *) NULL, TCL_STATIC); | |
1215 | Tcl_AppendResult(canvasPtr->interp, "bad index \"", string, | |
1216 | "\"", (char *) NULL); | |
1217 | return TCL_ERROR; | |
1218 | } | |
1219 | } else if (string[0] == 'c') { | |
1220 | if (strncmp(string, "cursor", length) == 0) { | |
1221 | *indexPtr = textPtr->cursorPos; | |
1222 | } else { | |
1223 | goto badIndex; | |
1224 | } | |
1225 | } else if (string[0] == 's') { | |
1226 | if (canvasPtr->selItemPtr != itemPtr) { | |
1227 | canvasPtr->interp->result = "selection isn't in item"; | |
1228 | return TCL_ERROR; | |
1229 | } | |
1230 | if (length < 5) { | |
1231 | goto badIndex; | |
1232 | } | |
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; | |
1237 | } else { | |
1238 | goto badIndex; | |
1239 | } | |
1240 | } else if (string[0] == '@') { | |
1241 | int x, y, dummy, i; | |
1242 | char *end, *p; | |
1243 | register TextLine *linePtr; | |
1244 | ||
1245 | p = string+1; | |
1246 | x = strtol(p, &end, 0); | |
1247 | if ((end == p) || (*end != ',')) { | |
1248 | goto badIndex; | |
1249 | } | |
1250 | p = end+1; | |
1251 | y = strtol(p, &end, 0); | |
1252 | if ((end == p) || (*end != 0)) { | |
1253 | goto badIndex; | |
1254 | } | |
1255 | if ((textPtr->numChars == 0) || (y < textPtr->linePtr[0].y1)) { | |
1256 | *indexPtr = 0; | |
1257 | return TCL_OK; | |
1258 | } | |
1259 | for (i = 0, linePtr = textPtr->linePtr; ; i++, linePtr++) { | |
1260 | if (i >= textPtr->numLines) { | |
1261 | *indexPtr = textPtr->numChars; | |
1262 | return TCL_OK; | |
1263 | } | |
1264 | if (y <= linePtr->y2) { | |
1265 | break; | |
1266 | } | |
1267 | } | |
1268 | *indexPtr = TkMeasureChars(textPtr->fontPtr, linePtr->firstChar, | |
1269 | linePtr->numChars, linePtr->x, x, 0, &dummy); | |
1270 | *indexPtr += linePtr->firstChar - textPtr->text; | |
1271 | } else { | |
1272 | if (Tcl_GetInt(canvasPtr->interp, string, indexPtr) != TCL_OK) { | |
1273 | goto badIndex; | |
1274 | } | |
1275 | if (*indexPtr < 0){ | |
1276 | *indexPtr = 0; | |
1277 | } else if (*indexPtr > textPtr->numChars) { | |
1278 | *indexPtr = textPtr->numChars; | |
1279 | } | |
1280 | } | |
1281 | return TCL_OK; | |
1282 | } | |
1283 | \f | |
1284 | /* | |
1285 | *-------------------------------------------------------------- | |
1286 | * | |
1287 | * SetTextCursor -- | |
1288 | * | |
1289 | * Set the position of the insertion cursor in this item. | |
1290 | * | |
1291 | * Results: | |
1292 | * None. | |
1293 | * | |
1294 | * Side effects: | |
1295 | * The cursor position will change. | |
1296 | * | |
1297 | *-------------------------------------------------------------- | |
1298 | */ | |
1299 | ||
1300 | /* ARGSUSED */ | |
1301 | static void | |
1302 | SetTextCursor(canvasPtr, itemPtr, index) | |
1303 | Tk_Canvas *canvasPtr; /* Record describing canvas widget. */ | |
1304 | Tk_Item *itemPtr; /* Text item in which cursor position | |
1305 | * is to be set. */ | |
1306 | int index; /* Index of character just before which | |
1307 | * cursor is to be positioned. */ | |
1308 | { | |
1309 | register TextItem *textPtr = (TextItem *) itemPtr; | |
1310 | ||
1311 | if (index < 0) { | |
1312 | textPtr->cursorPos = 0; | |
1313 | } else if (index > textPtr->numChars) { | |
1314 | textPtr->cursorPos = textPtr->numChars; | |
1315 | } else { | |
1316 | textPtr->cursorPos = index; | |
1317 | } | |
1318 | } | |
1319 | \f | |
1320 | /* | |
1321 | *-------------------------------------------------------------- | |
1322 | * | |
1323 | * GetSelText -- | |
1324 | * | |
1325 | * This procedure is invoked to return the selected portion | |
1326 | * of a text item. It is only called when this item has | |
1327 | * the selection. | |
1328 | * | |
1329 | * Results: | |
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. | |
1334 | * | |
1335 | * Side effects: | |
1336 | * None. | |
1337 | * | |
1338 | *-------------------------------------------------------------- | |
1339 | */ | |
1340 | ||
1341 | static int | |
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 | |
1348 | * selection. */ | |
1349 | int maxBytes; /* Maximum number of bytes to place | |
1350 | * at buffer, not including terminating | |
1351 | * NULL character. */ | |
1352 | { | |
1353 | TextItem *textPtr = (TextItem *) itemPtr; | |
1354 | int count; | |
1355 | ||
1356 | count = canvasPtr->selectLast + 1 - canvasPtr->selectFirst - offset; | |
1357 | if (canvasPtr->selectLast == textPtr->numChars) { | |
1358 | count -= 1; | |
1359 | } | |
1360 | if (count > maxBytes) { | |
1361 | count = maxBytes; | |
1362 | } | |
1363 | if (count <= 0) { | |
1364 | return 0; | |
1365 | } | |
1366 | strncpy(buffer, textPtr->text + canvasPtr->selectFirst + offset, count); | |
1367 | buffer[count] = '\0'; | |
1368 | return count; | |
1369 | } |