]> cvs.zerfleddert.de Git - micropolis/blob - src/tk/tkcvtext.c
Fixes for compilation with gcc 15
[micropolis] / src / tk / tkcvtext.c
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 (
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 {
205 register TextItem *textPtr = (TextItem *) itemPtr;
206
207 if (argc < 2) {
208 Tcl_AppendResult(canvasPtr->interp, "wrong # args: should be \"",
209 Tk_PathName(canvasPtr->tkwin),
210 "\" create text x y [options]", (char *) NULL);
211 return TCL_ERROR;
212 }
213
214 /*
215 * Carry out initialization that is needed in order to clean
216 * up after errors during the the remainder of this procedure.
217 */
218
219 textPtr->text = NULL;
220 textPtr->anchor = TK_ANCHOR_CENTER;
221 textPtr->width = 0;
222 textPtr->justify = TK_JUSTIFY_LEFT;
223 textPtr->fontPtr = NULL;
224 textPtr->color = NULL;
225 textPtr->stipple = None;
226 textPtr->gc = None;
227 textPtr->linePtr = NULL;
228 textPtr->numLines = 0;
229 textPtr->cursorPos = 0;
230 textPtr->selTextGC = None;
231
232 /*
233 * Process the arguments to fill in the item record.
234 */
235
236 if ((TkGetCanvasCoord(canvasPtr, argv[0], &textPtr->x) != TCL_OK)
237 || (TkGetCanvasCoord(canvasPtr, argv[1], &textPtr->y) != TCL_OK)) {
238 return TCL_ERROR;
239 }
240
241 if (ConfigureText(canvasPtr, itemPtr, argc-2, argv+2, 0) != TCL_OK) {
242 DeleteText(itemPtr);
243 return TCL_ERROR;
244 }
245 return TCL_OK;
246 }
247 \f
248 /*
249 *--------------------------------------------------------------
250 *
251 * TextCoords --
252 *
253 * This procedure is invoked to process the "coords" widget
254 * command on text items. See the user documentation for
255 * details on what it does.
256 *
257 * Results:
258 * Returns TCL_OK or TCL_ERROR, and sets canvasPtr->interp->result.
259 *
260 * Side effects:
261 * The coordinates for the given item may be changed.
262 *
263 *--------------------------------------------------------------
264 */
265
266 static int
267 TextCoords (
268 register Tk_Canvas *canvasPtr, /* Canvas containing item. */
269 Tk_Item *itemPtr, /* Item whose coordinates are to be
270 * read or modified. */
271 int argc, /* Number of coordinates supplied in
272 * argv. */
273 char **argv /* Array of coordinates: x1, y1,
274 * x2, y2, ... */
275 )
276 {
277 register TextItem *textPtr = (TextItem *) itemPtr;
278
279 if (argc == 0) {
280 sprintf(canvasPtr->interp->result, "%g %g", textPtr->x, textPtr->y);
281 } else if (argc == 2) {
282 if ((TkGetCanvasCoord(canvasPtr, argv[0], &textPtr->x) != TCL_OK)
283 || (TkGetCanvasCoord(canvasPtr, argv[1],
284 &textPtr->y) != TCL_OK)) {
285 return TCL_ERROR;
286 }
287 ComputeTextBbox(canvasPtr, textPtr);
288 } else {
289 sprintf(canvasPtr->interp->result,
290 "wrong # coordinates: expected 0 or 2, got %d",
291 argc);
292 return TCL_ERROR;
293 }
294 return TCL_OK;
295 }
296 \f
297 /*
298 *--------------------------------------------------------------
299 *
300 * ConfigureText --
301 *
302 * This procedure is invoked to configure various aspects
303 * of a text item, such as its border and background colors.
304 *
305 * Results:
306 * A standard Tcl result code. If an error occurs, then
307 * an error message is left in canvasPtr->interp->result.
308 *
309 * Side effects:
310 * Configuration information, such as colors and stipple
311 * patterns, may be set for itemPtr.
312 *
313 *--------------------------------------------------------------
314 */
315
316 static int
317 ConfigureText (
318 Tk_Canvas *canvasPtr, /* Canvas containing itemPtr. */
319 Tk_Item *itemPtr, /* Rectangle item to reconfigure. */
320 int argc, /* Number of elements in argv. */
321 char **argv, /* Arguments describing things to configure. */
322 int flags /* Flags to pass to Tk_ConfigureWidget. */
323 )
324 {
325 register TextItem *textPtr = (TextItem *) itemPtr;
326 XGCValues gcValues;
327 GC newGC, newSelGC;
328 unsigned long mask;
329
330 if (Tk_ConfigureWidget(canvasPtr->interp, canvasPtr->tkwin,
331 configSpecs, argc, argv, (char *) textPtr, flags) != TCL_OK) {
332 return TCL_ERROR;
333 }
334
335 /*
336 * A few of the options require additional processing, such as
337 * graphics contexts.
338 */
339
340 textPtr->numChars = strlen(textPtr->text);
341 newGC = newSelGC = None;
342 if ((textPtr->color != NULL) && (textPtr->fontPtr != NULL)) {
343 gcValues.foreground = textPtr->color->pixel;
344 gcValues.font = textPtr->fontPtr->fid;
345 mask = GCForeground|GCFont;
346 if (textPtr->stipple != None) {
347 gcValues.stipple = textPtr->stipple;
348 gcValues.fill_style = FillStippled;
349 mask |= GCForeground|GCStipple|GCFillStyle;
350 }
351 newGC = Tk_GetGC(canvasPtr->tkwin, mask, &gcValues);
352 gcValues.foreground = canvasPtr->selFgColorPtr->pixel;
353 newSelGC = Tk_GetGC(canvasPtr->tkwin, mask, &gcValues);
354 }
355 if (textPtr->gc != None) {
356 Tk_FreeGC(textPtr->gc);
357 }
358 textPtr->gc = newGC;
359 if (textPtr->selTextGC != None) {
360 Tk_FreeGC(textPtr->selTextGC);
361 }
362 textPtr->selTextGC = newSelGC;
363
364 /*
365 * If the text was changed, move the selection and insertion indices
366 * to keep them inside the item.
367 */
368
369 if (canvasPtr->selItemPtr == itemPtr) {
370 if (canvasPtr->selectFirst >= textPtr->numChars) {
371 canvasPtr->selItemPtr = NULL;
372 } else {
373 if (canvasPtr->selectLast >= textPtr->numChars) {
374 canvasPtr->selectLast = textPtr->numChars-1;
375 }
376 if ((canvasPtr->anchorItemPtr == itemPtr)
377 && (canvasPtr->selectAnchor >= textPtr->numChars)) {
378 canvasPtr->selectAnchor = textPtr->numChars-1;
379 }
380 }
381 }
382 if (textPtr->cursorPos >= textPtr->numChars) {
383 textPtr->cursorPos = textPtr->numChars;
384 }
385
386 ComputeTextBbox(canvasPtr, textPtr);
387 return TCL_OK;
388 }
389 \f
390 /*
391 *--------------------------------------------------------------
392 *
393 * DeleteText --
394 *
395 * This procedure is called to clean up the data structure
396 * associated with a text item.
397 *
398 * Results:
399 * None.
400 *
401 * Side effects:
402 * Resources associated with itemPtr are released.
403 *
404 *--------------------------------------------------------------
405 */
406
407 static void
408 DeleteText (
409 Tk_Item *itemPtr /* Item that is being deleted. */
410 )
411 {
412 register TextItem *textPtr = (TextItem *) itemPtr;
413
414 if (textPtr->text != NULL) {
415 ckfree(textPtr->text);
416 }
417 if (textPtr->fontPtr != NULL) {
418 Tk_FreeFontStruct(textPtr->fontPtr);
419 }
420 if (textPtr->color != NULL) {
421 Tk_FreeColor(textPtr->color);
422 }
423 if (textPtr->stipple != None) {
424 Tk_FreeBitmap(textPtr->stipple);
425 }
426 if (textPtr->gc != None) {
427 Tk_FreeGC(textPtr->gc);
428 }
429 if (textPtr->linePtr != NULL) {
430 ckfree((char *) textPtr->linePtr);
431 }
432 if (textPtr->selTextGC != None) {
433 Tk_FreeGC(textPtr->selTextGC);
434 }
435 }
436 \f
437 /*
438 *--------------------------------------------------------------
439 *
440 * ComputeTextBbox --
441 *
442 * This procedure is invoked to compute the bounding box of
443 * all the pixels that may be drawn as part of a text item.
444 * In addition, it recomputes all of the geometry information
445 * used to display a text item or check for mouse hits.
446 *
447 * Results:
448 * None.
449 *
450 * Side effects:
451 * The fields x1, y1, x2, and y2 are updated in the header
452 * for itemPtr, and the linePtr structure is regenerated
453 * for itemPtr.
454 *
455 *--------------------------------------------------------------
456 */
457
458 static void
459 ComputeTextBbox (
460 register Tk_Canvas *canvasPtr, /* Canvas that contains item. */
461 register TextItem *textPtr /* Item whose bbos is to be
462 * recomputed. */
463 )
464 {
465 register TextLine *linePtr;
466 #define MAX_LINES 100
467 char *lineStart[MAX_LINES];
468 int lineChars[MAX_LINES];
469 int linePixels[MAX_LINES];
470 int numLines, wrapPixels, maxLinePixels, leftX, topY, y;
471 int lineHeight, i, fudge;
472 char *p;
473 XCharStruct *maxBoundsPtr = &textPtr->fontPtr->max_bounds;
474
475 if (textPtr->linePtr != NULL) {
476 ckfree((char *) textPtr->linePtr);
477 textPtr->linePtr = NULL;
478 }
479
480 /*
481 * Work through the text computing the starting point, number of
482 * characters, and number of pixels in each line.
483 */
484
485 p = textPtr->text;
486 if (textPtr->width > 0) {
487 wrapPixels = maxLinePixels = textPtr->width;
488 } else {
489 wrapPixels = 10000000;
490 maxLinePixels = 0;
491 }
492 for (numLines = 0; (numLines < MAX_LINES) && (*p != 0);
493 numLines++) {
494 int numChars, numPixels;
495 numChars = TkMeasureChars(textPtr->fontPtr, p,
496 (textPtr->text + textPtr->numChars) - p, 0,
497 wrapPixels, TK_WHOLE_WORDS|TK_AT_LEAST_ONE, &numPixels);
498 if (numPixels > maxLinePixels) {
499 maxLinePixels = numPixels;
500 }
501 lineStart[numLines] = p;
502 lineChars[numLines] = numChars;
503 linePixels[numLines] = numPixels;
504 p += numChars;
505
506 /*
507 * Skip space character that terminates a line, if there is one.
508 * In the case of multiple spaces, all but one will be displayed.
509 * This is important to make sure the insertion cursor gets
510 * displayed when it is in the middle of a multi-space.
511 */
512
513 if (isspace(*p)) {
514 p++;
515 }
516 }
517
518 /*
519 * Use overall geometry information to compute the top-left corner
520 * of the bounding box for the text item.
521 */
522
523 leftX = textPtr->x + 0.5;
524 topY = textPtr->y + 0.5;
525 lineHeight = textPtr->fontPtr->ascent + textPtr->fontPtr->descent;
526 switch (textPtr->anchor) {
527 case TK_ANCHOR_NW:
528 case TK_ANCHOR_N:
529 case TK_ANCHOR_NE:
530 break;
531
532 case TK_ANCHOR_W:
533 case TK_ANCHOR_CENTER:
534 case TK_ANCHOR_E:
535 topY -= (lineHeight * numLines)/2;
536 break;
537
538 case TK_ANCHOR_SW:
539 case TK_ANCHOR_S:
540 case TK_ANCHOR_SE:
541 topY -= lineHeight * numLines;
542 break;
543 }
544 switch (textPtr->anchor) {
545 case TK_ANCHOR_NW:
546 case TK_ANCHOR_W:
547 case TK_ANCHOR_SW:
548 break;
549
550 case TK_ANCHOR_N:
551 case TK_ANCHOR_CENTER:
552 case TK_ANCHOR_S:
553 leftX -= maxLinePixels/2;
554 break;
555
556 case TK_ANCHOR_NE:
557 case TK_ANCHOR_E:
558 case TK_ANCHOR_SE:
559 leftX -= maxLinePixels;
560 break;
561 }
562 textPtr->rightEdge = leftX + maxLinePixels;
563
564 /*
565 * Create the new TextLine array and fill it in using the geometry
566 * information gathered already.
567 */
568
569 if (numLines > 0) {
570 textPtr->linePtr = (TextLine *) ckalloc((unsigned)
571 (numLines * sizeof(TextLine)));
572 } else {
573 textPtr->linePtr = NULL;
574 }
575 textPtr->numLines = numLines;
576 for (i = 0, linePtr = textPtr->linePtr, y = topY;
577 i < numLines; i++, linePtr++, y += lineHeight) {
578 linePtr->firstChar = lineStart[i];
579 linePtr->numChars = lineChars[i];
580 if (i == (numLines-1)) {
581 linePtr->totalChars = linePtr->numChars;
582 } else {
583 linePtr->totalChars = lineStart[i+1] - lineStart[i];
584 }
585 switch (textPtr->justify) {
586 case TK_JUSTIFY_LEFT:
587 case TK_JUSTIFY_FILL:
588 linePtr->x = leftX;
589 break;
590 case TK_JUSTIFY_CENTER:
591 linePtr->x = leftX + maxLinePixels/2 - linePixels[i]/2;
592 break;
593 case TK_JUSTIFY_RIGHT:
594 linePtr->x = leftX + maxLinePixels - linePixels[i];
595 break;
596 }
597 linePtr->y = y + textPtr->fontPtr->ascent;
598 linePtr->x1 = linePtr->x - maxBoundsPtr->lbearing;
599 linePtr->y1 = y;
600 linePtr->x2 = linePtr->x + linePixels[i] + maxBoundsPtr->rbearing
601 - textPtr->fontPtr->min_bounds.rbearing;
602 linePtr->y2 = linePtr->y + textPtr->fontPtr->descent - 1;
603 }
604
605 /*
606 * Last of all, update the bounding box for the item. The item's
607 * bounding box includes the bounding box of all its lines, plus
608 * an extra fudge factor for the cursor border (which could
609 * potentially be quite large).
610 */
611
612 linePtr = textPtr->linePtr;
613 textPtr->header.x1 = textPtr->header.x2 = leftX;
614 textPtr->header.y1 = topY;
615 textPtr->header.y2 = topY + numLines*lineHeight;
616 for (linePtr = textPtr->linePtr, i = textPtr->numLines; i > 0;
617 i--, linePtr++) {
618 if (linePtr->x1 < textPtr->header.x1) {
619 textPtr->header.x1 = linePtr->x1;
620 }
621 if (linePtr->x2 >= textPtr->header.x2) {
622 textPtr->header.x2 = linePtr->x2 + 1;
623 }
624 }
625
626 fudge = canvasPtr->cursorWidth/2;
627 if (canvasPtr->selBorderWidth > fudge) {
628 fudge = canvasPtr->selBorderWidth;
629 }
630 textPtr->header.x1 -= fudge;
631 textPtr->header.x2 += fudge;
632 }
633 \f
634 /*
635 *--------------------------------------------------------------
636 *
637 * DisplayText --
638 *
639 * This procedure is invoked to draw a text item in a given
640 * drawable.
641 *
642 * Results:
643 * None.
644 *
645 * Side effects:
646 * ItemPtr is drawn in drawable using the transformation
647 * information in canvasPtr.
648 *
649 *--------------------------------------------------------------
650 */
651
652 static void
653 DisplayText (
654 register Tk_Canvas *canvasPtr, /* Canvas that contains item. */
655 Tk_Item *itemPtr, /* Item to be displayed. */
656 Drawable drawable /* Pixmap or window in which to draw
657 * item. */
658 )
659 {
660 register TextItem *textPtr = (TextItem *) itemPtr;
661 Display *display = Tk_Display(canvasPtr->tkwin);
662 register TextLine *linePtr;
663 int i, focusHere, cursorX, cursorIndex, lineIndex;
664 int beforeSelect, inSelect, afterSelect, selStartX, selEndX;
665
666 if (textPtr->gc == None) {
667 return;
668 }
669 focusHere = (canvasPtr->focusItemPtr == itemPtr) &&
670 (canvasPtr->flags & GOT_FOCUS);
671 for (linePtr = textPtr->linePtr, i = textPtr->numLines;
672 i > 0; linePtr++, i--) {
673
674 /*
675 * If part or all of this line is selected, then draw a special
676 * background under the selected part of the line.
677 */
678
679 lineIndex = linePtr->firstChar - textPtr->text;
680 if ((canvasPtr->selItemPtr != itemPtr)
681 || (canvasPtr->selectLast < lineIndex)
682 || (canvasPtr->selectFirst >= (lineIndex
683 + linePtr->totalChars))) {
684 beforeSelect = linePtr->numChars;
685 inSelect = 0;
686 } else {
687 beforeSelect = canvasPtr->selectFirst - lineIndex;
688 if (beforeSelect <= 0) {
689 beforeSelect = 0;
690 selStartX = linePtr->x;
691 } else {
692 (void) TkMeasureChars(textPtr->fontPtr,
693 linePtr->firstChar, beforeSelect, 0,
694 (int) 1000000, TK_PARTIAL_OK, &selStartX);
695 selStartX += linePtr->x;
696 }
697 inSelect = canvasPtr->selectLast + 1 - (lineIndex + beforeSelect);
698
699 /*
700 * If the selection spans the end of this line, then display
701 * selection background all the way to the end of the line.
702 * However, for the last line we only want to display up to
703 * the last character, not the end of the line, hence the
704 * "i != 1" check.
705 */
706
707 if (inSelect >= (linePtr->totalChars - beforeSelect)) {
708 inSelect = linePtr->numChars - beforeSelect;
709 if (i != 1) {
710 selEndX = textPtr->rightEdge;
711 goto fillSelectBackground;
712 }
713 }
714 (void) TkMeasureChars(textPtr->fontPtr,
715 linePtr->firstChar + beforeSelect, inSelect,
716 selStartX-linePtr->x, (int) 1000000, TK_PARTIAL_OK,
717 &selEndX);
718 selEndX += linePtr->x;
719 fillSelectBackground:
720 Tk_Fill3DRectangle(display, drawable, canvasPtr->selBorder,
721 selStartX - canvasPtr->drawableXOrigin
722 - canvasPtr->selBorderWidth,
723 linePtr->y - canvasPtr->drawableYOrigin
724 - textPtr->fontPtr->ascent,
725 selEndX - selStartX + 2*canvasPtr->selBorderWidth,
726 textPtr->fontPtr->ascent + textPtr->fontPtr->descent,
727 canvasPtr->selBorderWidth, TK_RELIEF_RAISED);
728 }
729
730 /*
731 * If the insertion cursor is in this line, then draw a special
732 * background for the cursor before drawing the text. Note:
733 * if we're the cursor item but the cursor is turned off, then
734 * redraw background over the area of the cursor. This guarantees
735 * that the selection won't make the cursor invisible on mono
736 * displays, where both are drawn in the same color.
737 */
738
739 if (focusHere) {
740 cursorIndex = textPtr->cursorPos
741 - (linePtr->firstChar - textPtr->text);
742 if ((cursorIndex >= 0) && (cursorIndex <= linePtr->numChars)) {
743 (void) TkMeasureChars(textPtr->fontPtr, linePtr->firstChar,
744 cursorIndex, 0, (int) 1000000, TK_PARTIAL_OK, &cursorX);
745 if (canvasPtr->flags & CURSOR_ON) {
746 Tk_Fill3DRectangle(display, drawable,
747 canvasPtr->cursorBorder,
748 linePtr->x - canvasPtr->drawableXOrigin
749 + cursorX - (canvasPtr->cursorWidth)/2,
750 linePtr->y - canvasPtr->drawableYOrigin
751 - textPtr->fontPtr->ascent,
752 canvasPtr->cursorWidth,
753 textPtr->fontPtr->ascent
754 + textPtr->fontPtr->descent,
755 canvasPtr->cursorBorderWidth, TK_RELIEF_RAISED);
756 } else if (Tk_DefaultDepth(
757 Tk_Screen(canvasPtr->tkwin)) == 1){
758 Tk_Fill3DRectangle(display, drawable,
759 canvasPtr->bgBorder,
760 linePtr->x - canvasPtr->drawableXOrigin
761 + cursorX - (canvasPtr->cursorWidth)/2,
762 linePtr->y - canvasPtr->drawableYOrigin
763 - textPtr->fontPtr->ascent,
764 canvasPtr->cursorWidth,
765 textPtr->fontPtr->ascent
766 + textPtr->fontPtr->descent,
767 0, TK_RELIEF_FLAT);
768 }
769 }
770 }
771
772 /*
773 * Display the text in three pieces: the part before the
774 * selection, the selected part (which needs a different graphics
775 * context), and the part after the selection.
776 */
777
778 if (beforeSelect != 0) {
779 TkDisplayChars(display, drawable, textPtr->gc, textPtr->fontPtr,
780 linePtr->firstChar, beforeSelect,
781 linePtr->x - canvasPtr->drawableXOrigin,
782 linePtr->y - canvasPtr->drawableYOrigin, 0);
783 }
784 if (inSelect != 0) {
785 TkDisplayChars(display, drawable, textPtr->selTextGC,
786 textPtr->fontPtr, linePtr->firstChar + beforeSelect,
787 inSelect, selStartX - canvasPtr->drawableXOrigin,
788 linePtr->y - canvasPtr->drawableYOrigin, 0);
789 }
790 afterSelect = linePtr->numChars - beforeSelect - inSelect;
791 if (afterSelect > 0) {
792 TkDisplayChars(display, drawable, textPtr->gc, textPtr->fontPtr,
793 linePtr->firstChar + beforeSelect + inSelect,
794 afterSelect, selEndX - canvasPtr->drawableXOrigin,
795 linePtr->y - canvasPtr->drawableYOrigin, 0);
796 }
797 }
798 }
799 \f
800 /*
801 *--------------------------------------------------------------
802 *
803 * TextInsert --
804 *
805 * Insert characters into a text item at a given position.
806 *
807 * Results:
808 * Always returns TCL_OK.
809 *
810 * Side effects:
811 * The text in the given item is modified. The cursor and
812 * selection positions are also modified to reflect the
813 * insertion.
814 *
815 *--------------------------------------------------------------
816 */
817
818 static int
819 TextInsert (
820 Tk_Canvas *canvasPtr, /* Canvas containing text item. */
821 Tk_Item *itemPtr, /* Text item to be modified. */
822 int beforeThis, /* Index of character before which text is
823 * to be inserted. */
824 char *string /* New characters to be inserted. */
825 )
826 {
827 register TextItem *textPtr = (TextItem *) itemPtr;
828 int length;
829 char *new;
830
831 length = strlen(string);
832 if (length == 0) {
833 return TCL_OK;
834 }
835 if (beforeThis < 0) {
836 beforeThis = 0;
837 }
838 if (beforeThis > textPtr->numChars) {
839 beforeThis = textPtr->numChars;
840 }
841
842 new = (char *) ckalloc((unsigned) (textPtr->numChars + length + 1));
843 strncpy(new, textPtr->text, beforeThis);
844 strcpy(new+beforeThis, string);
845 strcpy(new+beforeThis+length, textPtr->text+beforeThis);
846 ckfree(textPtr->text);
847 textPtr->text = new;
848 textPtr->numChars += length;
849
850 /*
851 * Inserting characters invalidates indices such as those for the
852 * selection and cursor. Update the indices appropriately.
853 */
854
855 if (canvasPtr->selItemPtr == itemPtr) {
856 if (canvasPtr->selectFirst >= beforeThis) {
857 canvasPtr->selectFirst += length;
858 }
859 if (canvasPtr->selectLast >= beforeThis) {
860 canvasPtr->selectLast += length;
861 }
862 if ((canvasPtr->anchorItemPtr == itemPtr)
863 && (canvasPtr->selectAnchor >= beforeThis)) {
864 canvasPtr->selectAnchor += length;
865 }
866 }
867 if (textPtr->cursorPos >= beforeThis) {
868 textPtr->cursorPos += length;
869 }
870 ComputeTextBbox(canvasPtr, textPtr);
871 return TCL_OK;
872 }
873 \f
874 /*
875 *--------------------------------------------------------------
876 *
877 * TextDeleteChars --
878 *
879 * Delete one or more characters from a text item.
880 *
881 * Results:
882 * Always returns TCL_OK.
883 *
884 * Side effects:
885 * Characters between "first" and "last", inclusive, get
886 * deleted from itemPtr, and things like the selection
887 * position get updated.
888 *
889 *--------------------------------------------------------------
890 */
891
892 static int
893 TextDeleteChars (
894 Tk_Canvas *canvasPtr, /* Canvas containing itemPtr. */
895 Tk_Item *itemPtr, /* Item in which to delete characters. */
896 int first, /* Index of first character to delete. */
897 int last /* Index of last character to delete. */
898 )
899 {
900 register TextItem *textPtr = (TextItem *) itemPtr;
901 int count;
902 char *new;
903
904 if (first < 0) {
905 first = 0;
906 }
907 if (last >= textPtr->numChars) {
908 last = textPtr->numChars-1;
909 }
910 if (first > last) {
911 return TCL_OK;
912 }
913 count = last + 1 - first;
914
915 new = ckalloc((unsigned) (textPtr->numChars + 1 - count));
916 strncpy(new, textPtr->text, first);
917 strcpy(new+first, textPtr->text+last+1);
918 ckfree(textPtr->text);
919 textPtr->text = new;
920 textPtr->numChars -= count;
921
922 /*
923 * Update indexes for the selection and cursor to reflect the
924 * renumbering of the remaining characters.
925 */
926
927 if (canvasPtr->selItemPtr == itemPtr) {
928 if (canvasPtr->selectFirst > first) {
929 canvasPtr->selectFirst -= count;
930 if (canvasPtr->selectFirst < first) {
931 canvasPtr->selectFirst = first;
932 }
933 }
934 if (canvasPtr->selectLast >= first) {
935 canvasPtr->selectLast -= count;
936 if (canvasPtr->selectLast < (first-1)) {
937 canvasPtr->selectLast = (first-1);
938 }
939 }
940 if (canvasPtr->selectFirst > canvasPtr->selectLast) {
941 canvasPtr->selItemPtr = NULL;
942 }
943 if ((canvasPtr->anchorItemPtr == itemPtr)
944 && (canvasPtr->selectAnchor > first)) {
945 canvasPtr->selectAnchor -= count;
946 if (canvasPtr->selectAnchor < first) {
947 canvasPtr->selectAnchor = first;
948 }
949 }
950 }
951 if (textPtr->cursorPos > first) {
952 textPtr->cursorPos -= count;
953 if (textPtr->cursorPos < first) {
954 textPtr->cursorPos = first;
955 }
956 }
957 ComputeTextBbox(canvasPtr, textPtr);
958 return TCL_OK;
959 }
960 \f
961 /*
962 *--------------------------------------------------------------
963 *
964 * TextToPoint --
965 *
966 * Computes the distance from a given point to a given
967 * text item, in canvas units.
968 *
969 * Results:
970 * The return value is 0 if the point whose x and y coordinates
971 * are pointPtr[0] and pointPtr[1] is inside the arc. If the
972 * point isn't inside the arc then the return value is the
973 * distance from the point to the arc.
974 *
975 * Side effects:
976 * None.
977 *
978 *--------------------------------------------------------------
979 */
980
981 /* ARGSUSED */
982 static double
983 TextToPoint (
984 Tk_Canvas *canvasPtr, /* Canvas containing itemPtr. */
985 Tk_Item *itemPtr, /* Item to check against point. */
986 double *pointPtr /* Pointer to x and y coordinates. */
987 )
988 {
989 TextItem *textPtr = (TextItem *) itemPtr;
990 register TextLine *linePtr;
991 int i;
992 double xDiff, yDiff, dist, minDist;
993
994 /*
995 * Treat each line in the text item as a rectangle, compute the
996 * distance to that rectangle, and take the minimum of these
997 * distances. Perform most of the calculations in integer pixel
998 * units, since that's how the dimensions of the text are defined.
999 */
1000
1001 minDist = -1.0;
1002 for (linePtr = textPtr->linePtr, i = textPtr->numLines;
1003 i > 0; linePtr++, i--) {
1004
1005 /*
1006 * If the point is inside the line's rectangle, then can
1007 * return immediately.
1008 */
1009
1010 if ((pointPtr[0] >= linePtr->x1)
1011 && (pointPtr[0] <= linePtr->x2)
1012 && (pointPtr[1] >= linePtr->y1)
1013 && (pointPtr[1] <= linePtr->y2)) {
1014 return 0.0;
1015 }
1016
1017 /*
1018 * Point is outside line's rectangle; compute distance to nearest
1019 * side.
1020 */
1021
1022 if (pointPtr[0] < linePtr->x1) {
1023 xDiff = linePtr->x1 - pointPtr[0];
1024 } else if (pointPtr[0] > linePtr->x2) {
1025 xDiff = pointPtr[0] - linePtr->x2;
1026 } else {
1027 xDiff = 0;
1028 }
1029
1030 if (pointPtr[1] < linePtr->y1) {
1031 yDiff = linePtr->y1 - pointPtr[1];
1032 } else if (pointPtr[1] > linePtr->y2) {
1033 yDiff = pointPtr[1] - linePtr->y2;
1034 } else {
1035 yDiff = 0;
1036 }
1037
1038 dist = hypot((float) xDiff, (float) yDiff);
1039 if ((dist < minDist) || (minDist < 0.0)) {
1040 minDist = dist;
1041 }
1042 }
1043 return minDist;
1044 }
1045 \f
1046 /*
1047 *--------------------------------------------------------------
1048 *
1049 * TextToArea --
1050 *
1051 * This procedure is called to determine whether an item
1052 * lies entirely inside, entirely outside, or overlapping
1053 * a given rectangle.
1054 *
1055 * Results:
1056 * -1 is returned if the item is entirely outside the area
1057 * given by rectPtr, 0 if it overlaps, and 1 if it is entirely
1058 * inside the given area.
1059 *
1060 * Side effects:
1061 * None.
1062 *
1063 *--------------------------------------------------------------
1064 */
1065
1066 /* ARGSUSED */
1067 static int
1068 TextToArea (
1069 Tk_Canvas *canvasPtr, /* Canvas containing itemPtr. */
1070 Tk_Item *itemPtr, /* Item to check against rectangle. */
1071 double *rectPtr /* Pointer to array of four coordinates
1072 * (x1, y1, x2, y2) describing rectangular
1073 * area. */
1074 )
1075 {
1076 TextItem *textPtr = (TextItem *) itemPtr;
1077 register TextLine *linePtr;
1078 int i, result;
1079
1080 /*
1081 * Scan the lines one at a time, seeing whether each line is
1082 * entirely in, entirely out, or overlapping the rectangle. If
1083 * an overlap is detected, return immediately; otherwise wait
1084 * until all lines have been processed and see if they were all
1085 * inside or all outside.
1086 */
1087
1088 result = 0;
1089 for (linePtr = textPtr->linePtr, i = textPtr->numLines;
1090 i > 0; linePtr++, i--) {
1091 if ((rectPtr[2] < linePtr->x1) || (rectPtr[0] > linePtr->x2)
1092 || (rectPtr[3] < linePtr->y1) || (rectPtr[1] > linePtr->y2)) {
1093 if (result == 1) {
1094 return 0;
1095 }
1096 result = -1;
1097 continue;
1098 }
1099 if ((linePtr->x1 < rectPtr[0]) || (linePtr->x2 > rectPtr[2])
1100 || (linePtr->y1 < rectPtr[1]) || (linePtr->y2 > rectPtr[3])) {
1101 return 0;
1102 }
1103 if (result == -1) {
1104 return 0;
1105 }
1106 result = 1;
1107 }
1108 return result;
1109 }
1110 \f
1111 /*
1112 *--------------------------------------------------------------
1113 *
1114 * ScaleText --
1115 *
1116 * This procedure is invoked to rescale a text item.
1117 *
1118 * Results:
1119 * None.
1120 *
1121 * Side effects:
1122 * Scales the position of the text, but not the size
1123 * of the font for the text.
1124 *
1125 *--------------------------------------------------------------
1126 */
1127
1128 /* ARGSUSED */
1129 static void
1130 ScaleText (
1131 Tk_Canvas *canvasPtr, /* Canvas containing rectangle. */
1132 Tk_Item *itemPtr, /* Rectangle to be scaled. */
1133 double originX,
1134 double originY, /* Origin about which to scale rect. */
1135 double scaleX, /* Amount to scale in X direction. */
1136 double scaleY /* Amount to scale in Y direction. */
1137 )
1138 {
1139 register TextItem *textPtr = (TextItem *) itemPtr;
1140
1141 textPtr->x = originX + scaleX*(textPtr->x - originX);
1142 textPtr->y = originY + scaleY*(textPtr->y - originY);
1143 ComputeTextBbox(canvasPtr, textPtr);
1144 return;
1145 }
1146 \f
1147 /*
1148 *--------------------------------------------------------------
1149 *
1150 * TranslateText --
1151 *
1152 * This procedure is called to move a text item by a
1153 * given amount.
1154 *
1155 * Results:
1156 * None.
1157 *
1158 * Side effects:
1159 * The position of the text item is offset by (xDelta, yDelta),
1160 * and the bounding box is updated in the generic part of the
1161 * item structure.
1162 *
1163 *--------------------------------------------------------------
1164 */
1165
1166 static void
1167 TranslateText (
1168 Tk_Canvas *canvasPtr, /* Canvas containing item. */
1169 Tk_Item *itemPtr, /* Item that is being moved. */
1170 double deltaX,
1171 double deltaY /* Amount by which item is to be
1172 * moved. */
1173 )
1174 {
1175 register TextItem *textPtr = (TextItem *) itemPtr;
1176
1177 textPtr->x += deltaX;
1178 textPtr->y += deltaY;
1179 ComputeTextBbox(canvasPtr, textPtr);
1180 }
1181 \f
1182 /*
1183 *--------------------------------------------------------------
1184 *
1185 * GetTextIndex --
1186 *
1187 * Parse an index into a text item and return either its value
1188 * or an error.
1189 *
1190 * Results:
1191 * A standard Tcl result. If all went well, then *indexPtr is
1192 * filled in with the index (into itemPtr) corresponding to
1193 * string. Otherwise an error message is left in
1194 * canvasPtr->interp->result.
1195 *
1196 * Side effects:
1197 * None.
1198 *
1199 *--------------------------------------------------------------
1200 */
1201
1202 static int
1203 GetTextIndex (
1204 Tk_Canvas *canvasPtr, /* Canvas containing item. */
1205 Tk_Item *itemPtr, /* Item for which the index is being
1206 * specified. */
1207 char *string, /* Specification of a particular character
1208 * in itemPtr's text. */
1209 int *indexPtr /* Where to store converted index. */
1210 )
1211 {
1212 register TextItem *textPtr = (TextItem *) itemPtr;
1213 int length;
1214
1215 length = strlen(string);
1216
1217 if (string[0] == 'e') {
1218 if (strncmp(string, "end", length) == 0) {
1219 *indexPtr = textPtr->numChars;
1220 } else {
1221 badIndex:
1222
1223 /*
1224 * Some of the paths here leave messages in
1225 * canvasPtr->interp->result, so we have to clear it out
1226 * before storing our own message.
1227 */
1228
1229 Tcl_SetResult(canvasPtr->interp, (char *) NULL, TCL_STATIC);
1230 Tcl_AppendResult(canvasPtr->interp, "bad index \"", string,
1231 "\"", (char *) NULL);
1232 return TCL_ERROR;
1233 }
1234 } else if (string[0] == 'c') {
1235 if (strncmp(string, "cursor", length) == 0) {
1236 *indexPtr = textPtr->cursorPos;
1237 } else {
1238 goto badIndex;
1239 }
1240 } else if (string[0] == 's') {
1241 if (canvasPtr->selItemPtr != itemPtr) {
1242 canvasPtr->interp->result = "selection isn't in item";
1243 return TCL_ERROR;
1244 }
1245 if (length < 5) {
1246 goto badIndex;
1247 }
1248 if (strncmp(string, "sel.first", length) == 0) {
1249 *indexPtr = canvasPtr->selectFirst;
1250 } else if (strncmp(string, "sel.last", length) == 0) {
1251 *indexPtr = canvasPtr->selectLast;
1252 } else {
1253 goto badIndex;
1254 }
1255 } else if (string[0] == '@') {
1256 int x, y, dummy, i;
1257 char *end, *p;
1258 register TextLine *linePtr;
1259
1260 p = string+1;
1261 x = strtol(p, &end, 0);
1262 if ((end == p) || (*end != ',')) {
1263 goto badIndex;
1264 }
1265 p = end+1;
1266 y = strtol(p, &end, 0);
1267 if ((end == p) || (*end != 0)) {
1268 goto badIndex;
1269 }
1270 if ((textPtr->numChars == 0) || (y < textPtr->linePtr[0].y1)) {
1271 *indexPtr = 0;
1272 return TCL_OK;
1273 }
1274 for (i = 0, linePtr = textPtr->linePtr; ; i++, linePtr++) {
1275 if (i >= textPtr->numLines) {
1276 *indexPtr = textPtr->numChars;
1277 return TCL_OK;
1278 }
1279 if (y <= linePtr->y2) {
1280 break;
1281 }
1282 }
1283 *indexPtr = TkMeasureChars(textPtr->fontPtr, linePtr->firstChar,
1284 linePtr->numChars, linePtr->x, x, 0, &dummy);
1285 *indexPtr += linePtr->firstChar - textPtr->text;
1286 } else {
1287 if (Tcl_GetInt(canvasPtr->interp, string, indexPtr) != TCL_OK) {
1288 goto badIndex;
1289 }
1290 if (*indexPtr < 0){
1291 *indexPtr = 0;
1292 } else if (*indexPtr > textPtr->numChars) {
1293 *indexPtr = textPtr->numChars;
1294 }
1295 }
1296 return TCL_OK;
1297 }
1298 \f
1299 /*
1300 *--------------------------------------------------------------
1301 *
1302 * SetTextCursor --
1303 *
1304 * Set the position of the insertion cursor in this item.
1305 *
1306 * Results:
1307 * None.
1308 *
1309 * Side effects:
1310 * The cursor position will change.
1311 *
1312 *--------------------------------------------------------------
1313 */
1314
1315 /* ARGSUSED */
1316 static void
1317 SetTextCursor (
1318 Tk_Canvas *canvasPtr, /* Record describing canvas widget. */
1319 Tk_Item *itemPtr, /* Text item in which cursor position
1320 * is to be set. */
1321 int index /* Index of character just before which
1322 * cursor is to be positioned. */
1323 )
1324 {
1325 register TextItem *textPtr = (TextItem *) itemPtr;
1326
1327 if (index < 0) {
1328 textPtr->cursorPos = 0;
1329 } else if (index > textPtr->numChars) {
1330 textPtr->cursorPos = textPtr->numChars;
1331 } else {
1332 textPtr->cursorPos = index;
1333 }
1334 }
1335 \f
1336 /*
1337 *--------------------------------------------------------------
1338 *
1339 * GetSelText --
1340 *
1341 * This procedure is invoked to return the selected portion
1342 * of a text item. It is only called when this item has
1343 * the selection.
1344 *
1345 * Results:
1346 * The return value is the number of non-NULL bytes stored
1347 * at buffer. Buffer is filled (or partially filled) with a
1348 * NULL-terminated string containing part or all of the selection,
1349 * as given by offset and maxBytes.
1350 *
1351 * Side effects:
1352 * None.
1353 *
1354 *--------------------------------------------------------------
1355 */
1356
1357 static int
1358 GetSelText (
1359 Tk_Canvas *canvasPtr, /* Canvas containing selection. */
1360 Tk_Item *itemPtr, /* Text item containing selection. */
1361 int offset, /* Offset within selection of first
1362 * character to be returned. */
1363 char *buffer, /* Location in which to place
1364 * selection. */
1365 int maxBytes /* Maximum number of bytes to place
1366 * at buffer, not including terminating
1367 * NULL character. */
1368 )
1369 {
1370 TextItem *textPtr = (TextItem *) itemPtr;
1371 int count;
1372
1373 count = canvasPtr->selectLast + 1 - canvasPtr->selectFirst - offset;
1374 if (canvasPtr->selectLast == textPtr->numChars) {
1375 count -= 1;
1376 }
1377 if (count > maxBytes) {
1378 count = maxBytes;
1379 }
1380 if (count <= 0) {
1381 return 0;
1382 }
1383 strncpy(buffer, textPtr->text + canvasPtr->selectFirst + offset, count);
1384 buffer[count] = '\0';
1385 return count;
1386 }
Impressum, Datenschutz