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