Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/lib/libtk/generic/tkCanvas.c
1810 views
1
/*
2
* tkCanvas.c --
3
*
4
* This module implements canvas widgets for the Tk toolkit.
5
* A canvas displays a background and a collection of graphical
6
* objects such as rectangles, lines, and texts.
7
*
8
* Copyright (c) 1991-1994 The Regents of the University of California.
9
* Copyright (c) 1994-1995 Sun Microsystems, Inc.
10
*
11
* See the file "license.terms" for information on usage and redistribution
12
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
13
*
14
* SCCS: @(#) tkCanvas.c 1.119 96/03/21 11:26:39
15
*/
16
17
#include "tkInt.h"
18
#include "tkCanvas.h"
19
#include "tkDefault.h"
20
21
/*
22
* See tkCanvas.h for key data structures used to implement canvases.
23
*/
24
25
/*
26
* The structure defined below is used to keep track of a tag search
27
* in progress. Only the "prevPtr" field should be accessed by anyone
28
* other than StartTagSearch and NextItem.
29
*/
30
31
typedef struct TagSearch {
32
TkCanvas *canvasPtr; /* Canvas widget being searched. */
33
Tk_Uid tag; /* Tag to search for. 0 means return
34
* all items. */
35
Tk_Item *prevPtr; /* Item just before last one found (or NULL
36
* if last one found was first in the item
37
* list of canvasPtr). */
38
Tk_Item *currentPtr; /* Pointer to last item returned. */
39
int searchOver; /* Non-zero means NextItem should always
40
* return NULL. */
41
} TagSearch;
42
43
/*
44
* Information used for argv parsing.
45
*/
46
47
static Tk_ConfigSpec configSpecs[] = {
48
{TK_CONFIG_BORDER, "-background", "background", "Background",
49
DEF_CANVAS_BG_COLOR, Tk_Offset(TkCanvas, bgBorder),
50
TK_CONFIG_COLOR_ONLY},
51
{TK_CONFIG_BORDER, "-background", "background", "Background",
52
DEF_CANVAS_BG_MONO, Tk_Offset(TkCanvas, bgBorder),
53
TK_CONFIG_MONO_ONLY},
54
{TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
55
(char *) NULL, 0, 0},
56
{TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
57
(char *) NULL, 0, 0},
58
{TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
59
DEF_CANVAS_BORDER_WIDTH, Tk_Offset(TkCanvas, borderWidth), 0},
60
{TK_CONFIG_DOUBLE, "-closeenough", "closeEnough", "CloseEnough",
61
DEF_CANVAS_CLOSE_ENOUGH, Tk_Offset(TkCanvas, closeEnough), 0},
62
{TK_CONFIG_BOOLEAN, "-confine", "confine", "Confine",
63
DEF_CANVAS_CONFINE, Tk_Offset(TkCanvas, confine), 0},
64
{TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
65
DEF_CANVAS_CURSOR, Tk_Offset(TkCanvas, cursor), TK_CONFIG_NULL_OK},
66
{TK_CONFIG_PIXELS, "-height", "height", "Height",
67
DEF_CANVAS_HEIGHT, Tk_Offset(TkCanvas, height), 0},
68
{TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
69
"HighlightBackground", DEF_CANVAS_HIGHLIGHT_BG,
70
Tk_Offset(TkCanvas, highlightBgColorPtr), 0},
71
{TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
72
DEF_CANVAS_HIGHLIGHT, Tk_Offset(TkCanvas, highlightColorPtr), 0},
73
{TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
74
"HighlightThickness",
75
DEF_CANVAS_HIGHLIGHT_WIDTH, Tk_Offset(TkCanvas, highlightWidth), 0},
76
{TK_CONFIG_BORDER, "-insertbackground", "insertBackground", "Foreground",
77
DEF_CANVAS_INSERT_BG, Tk_Offset(TkCanvas, textInfo.insertBorder), 0},
78
{TK_CONFIG_PIXELS, "-insertborderwidth", "insertBorderWidth", "BorderWidth",
79
DEF_CANVAS_INSERT_BD_COLOR,
80
Tk_Offset(TkCanvas, textInfo.insertBorderWidth), TK_CONFIG_COLOR_ONLY},
81
{TK_CONFIG_PIXELS, "-insertborderwidth", "insertBorderWidth", "BorderWidth",
82
DEF_CANVAS_INSERT_BD_MONO,
83
Tk_Offset(TkCanvas, textInfo.insertBorderWidth), TK_CONFIG_MONO_ONLY},
84
{TK_CONFIG_INT, "-insertofftime", "insertOffTime", "OffTime",
85
DEF_CANVAS_INSERT_OFF_TIME, Tk_Offset(TkCanvas, insertOffTime), 0},
86
{TK_CONFIG_INT, "-insertontime", "insertOnTime", "OnTime",
87
DEF_CANVAS_INSERT_ON_TIME, Tk_Offset(TkCanvas, insertOnTime), 0},
88
{TK_CONFIG_PIXELS, "-insertwidth", "insertWidth", "InsertWidth",
89
DEF_CANVAS_INSERT_WIDTH, Tk_Offset(TkCanvas, textInfo.insertWidth), 0},
90
{TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
91
DEF_CANVAS_RELIEF, Tk_Offset(TkCanvas, relief), 0},
92
{TK_CONFIG_STRING, "-scrollregion", "scrollRegion", "ScrollRegion",
93
DEF_CANVAS_SCROLL_REGION, Tk_Offset(TkCanvas, regionString),
94
TK_CONFIG_NULL_OK},
95
{TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
96
DEF_CANVAS_SELECT_COLOR, Tk_Offset(TkCanvas, textInfo.selBorder),
97
TK_CONFIG_COLOR_ONLY},
98
{TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
99
DEF_CANVAS_SELECT_MONO, Tk_Offset(TkCanvas, textInfo.selBorder),
100
TK_CONFIG_MONO_ONLY},
101
{TK_CONFIG_PIXELS, "-selectborderwidth", "selectBorderWidth", "BorderWidth",
102
DEF_CANVAS_SELECT_BD_COLOR,
103
Tk_Offset(TkCanvas, textInfo.selBorderWidth), TK_CONFIG_COLOR_ONLY},
104
{TK_CONFIG_PIXELS, "-selectborderwidth", "selectBorderWidth", "BorderWidth",
105
DEF_CANVAS_SELECT_BD_MONO, Tk_Offset(TkCanvas, textInfo.selBorderWidth),
106
TK_CONFIG_MONO_ONLY},
107
{TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
108
DEF_CANVAS_SELECT_FG_COLOR, Tk_Offset(TkCanvas, textInfo.selFgColorPtr),
109
TK_CONFIG_COLOR_ONLY},
110
{TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
111
DEF_CANVAS_SELECT_FG_MONO, Tk_Offset(TkCanvas, textInfo.selFgColorPtr),
112
TK_CONFIG_MONO_ONLY},
113
{TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
114
DEF_CANVAS_TAKE_FOCUS, Tk_Offset(TkCanvas, takeFocus),
115
TK_CONFIG_NULL_OK},
116
{TK_CONFIG_PIXELS, "-width", "width", "Width",
117
DEF_CANVAS_WIDTH, Tk_Offset(TkCanvas, width), 0},
118
{TK_CONFIG_STRING, "-xscrollcommand", "xScrollCommand", "ScrollCommand",
119
DEF_CANVAS_X_SCROLL_CMD, Tk_Offset(TkCanvas, xScrollCmd),
120
TK_CONFIG_NULL_OK},
121
{TK_CONFIG_PIXELS, "-xscrollincrement", "xScrollIncrement",
122
"ScrollIncrement",
123
DEF_CANVAS_X_SCROLL_INCREMENT, Tk_Offset(TkCanvas, xScrollIncrement),
124
0},
125
{TK_CONFIG_STRING, "-yscrollcommand", "yScrollCommand", "ScrollCommand",
126
DEF_CANVAS_Y_SCROLL_CMD, Tk_Offset(TkCanvas, yScrollCmd),
127
TK_CONFIG_NULL_OK},
128
{TK_CONFIG_PIXELS, "-yscrollincrement", "yScrollIncrement",
129
"ScrollIncrement",
130
DEF_CANVAS_Y_SCROLL_INCREMENT, Tk_Offset(TkCanvas, yScrollIncrement),
131
0},
132
{TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
133
(char *) NULL, 0, 0}
134
};
135
136
/*
137
* List of all the item types known at present:
138
*/
139
140
static Tk_ItemType *typeList = NULL; /* NULL means initialization hasn't
141
* been done yet. */
142
143
/*
144
* Standard item types provided by Tk:
145
*/
146
147
extern Tk_ItemType tkArcType, tkBitmapType, tkImageType, tkLineType;
148
extern Tk_ItemType tkOvalType, tkPolygonType;
149
extern Tk_ItemType tkRectangleType, tkTextType, tkWindowType;
150
151
/*
152
* Various Tk_Uid's used by this module (set up during initialization):
153
*/
154
155
static Tk_Uid allUid = NULL;
156
static Tk_Uid currentUid = NULL;
157
158
/*
159
* Statistics counters:
160
*/
161
162
static int numIdSearches;
163
static int numSlowSearches;
164
165
/*
166
* Prototypes for procedures defined later in this file:
167
*/
168
169
static void CanvasBindProc _ANSI_ARGS_((ClientData clientData,
170
XEvent *eventPtr));
171
static void CanvasBlinkProc _ANSI_ARGS_((ClientData clientData));
172
static void CanvasCmdDeletedProc _ANSI_ARGS_((
173
ClientData clientData));
174
static void CanvasDoEvent _ANSI_ARGS_((TkCanvas *canvasPtr,
175
XEvent *eventPtr));
176
static void CanvasEventProc _ANSI_ARGS_((ClientData clientData,
177
XEvent *eventPtr));
178
static int CanvasFetchSelection _ANSI_ARGS_((
179
ClientData clientData, int offset,
180
char *buffer, int maxBytes));
181
static Tk_Item * CanvasFindClosest _ANSI_ARGS_((TkCanvas *canvasPtr,
182
double coords[2]));
183
static void CanvasFocusProc _ANSI_ARGS_((TkCanvas *canvasPtr,
184
int gotFocus));
185
static void CanvasLostSelection _ANSI_ARGS_((
186
ClientData clientData));
187
static void CanvasSelectTo _ANSI_ARGS_((TkCanvas *canvasPtr,
188
Tk_Item *itemPtr, int index));
189
static void CanvasSetOrigin _ANSI_ARGS_((TkCanvas *canvasPtr,
190
int xOrigin, int yOrigin));
191
static void CanvasUpdateScrollbars _ANSI_ARGS_((
192
TkCanvas *canvasPtr));
193
static int CanvasWidgetCmd _ANSI_ARGS_((ClientData clientData,
194
Tcl_Interp *interp, int argc, char **argv));
195
static int ConfigureCanvas _ANSI_ARGS_((Tcl_Interp *interp,
196
TkCanvas *canvasPtr, int argc, char **argv,
197
int flags));
198
static void DestroyCanvas _ANSI_ARGS_((char *memPtr));
199
static void DisplayCanvas _ANSI_ARGS_((ClientData clientData));
200
static void DoItem _ANSI_ARGS_((Tcl_Interp *interp,
201
Tk_Item *itemPtr, Tk_Uid tag));
202
static int FindItems _ANSI_ARGS_((Tcl_Interp *interp,
203
TkCanvas *canvasPtr, int argc, char **argv,
204
char *newTag, char *cmdName, char *option));
205
static int FindArea _ANSI_ARGS_((Tcl_Interp *interp,
206
TkCanvas *canvasPtr, char **argv, Tk_Uid uid,
207
int enclosed));
208
static double GridAlign _ANSI_ARGS_((double coord, double spacing));
209
static void InitCanvas _ANSI_ARGS_((void));
210
static Tk_Item * NextItem _ANSI_ARGS_((TagSearch *searchPtr));
211
static void PickCurrentItem _ANSI_ARGS_((TkCanvas *canvasPtr,
212
XEvent *eventPtr));
213
static void PrintScrollFractions _ANSI_ARGS_((int screen1,
214
int screen2, int object1, int object2,
215
char *string));
216
static void RelinkItems _ANSI_ARGS_((TkCanvas *canvasPtr,
217
char *tag, Tk_Item *prevPtr));
218
static Tk_Item * StartTagSearch _ANSI_ARGS_((TkCanvas *canvasPtr,
219
char *tag, TagSearch *searchPtr));
220
221
/*
222
*--------------------------------------------------------------
223
*
224
* Tk_CanvasCmd --
225
*
226
* This procedure is invoked to process the "canvas" Tcl
227
* command. See the user documentation for details on what
228
* it does.
229
*
230
* Results:
231
* A standard Tcl result.
232
*
233
* Side effects:
234
* See the user documentation.
235
*
236
*--------------------------------------------------------------
237
*/
238
239
int
240
Tk_CanvasCmd(clientData, interp, argc, argv)
241
ClientData clientData; /* Main window associated with
242
* interpreter. */
243
Tcl_Interp *interp; /* Current interpreter. */
244
int argc; /* Number of arguments. */
245
char **argv; /* Argument strings. */
246
{
247
Tk_Window tkwin = (Tk_Window) clientData;
248
TkCanvas *canvasPtr;
249
Tk_Window new;
250
251
if (typeList == NULL) {
252
InitCanvas();
253
}
254
255
if (argc < 2) {
256
Tcl_AppendResult(interp, "wrong # args: should be \"",
257
argv[0], " pathName ?options?\"", (char *) NULL);
258
return TCL_ERROR;
259
}
260
261
new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
262
if (new == NULL) {
263
return TCL_ERROR;
264
}
265
266
/*
267
* Initialize fields that won't be initialized by ConfigureCanvas,
268
* or which ConfigureCanvas expects to have reasonable values
269
* (e.g. resource pointers).
270
*/
271
272
canvasPtr = (TkCanvas *) ckalloc(sizeof(TkCanvas));
273
canvasPtr->tkwin = new;
274
canvasPtr->display = Tk_Display(new);
275
canvasPtr->interp = interp;
276
canvasPtr->widgetCmd = Tcl_CreateCommand(interp,
277
Tk_PathName(canvasPtr->tkwin), CanvasWidgetCmd,
278
(ClientData) canvasPtr, CanvasCmdDeletedProc);
279
canvasPtr->firstItemPtr = NULL;
280
canvasPtr->lastItemPtr = NULL;
281
canvasPtr->borderWidth = 0;
282
canvasPtr->bgBorder = NULL;
283
canvasPtr->relief = TK_RELIEF_FLAT;
284
canvasPtr->highlightWidth = 0;
285
canvasPtr->highlightBgColorPtr = NULL;
286
canvasPtr->highlightColorPtr = NULL;
287
canvasPtr->inset = 0;
288
canvasPtr->pixmapGC = None;
289
canvasPtr->width = None;
290
canvasPtr->height = None;
291
canvasPtr->confine = 0;
292
canvasPtr->textInfo.selBorder = NULL;
293
canvasPtr->textInfo.selBorderWidth = 0;
294
canvasPtr->textInfo.selFgColorPtr = NULL;
295
canvasPtr->textInfo.selItemPtr = NULL;
296
canvasPtr->textInfo.selectFirst = -1;
297
canvasPtr->textInfo.selectLast = -1;
298
canvasPtr->textInfo.anchorItemPtr = NULL;
299
canvasPtr->textInfo.selectAnchor = 0;
300
canvasPtr->textInfo.insertBorder = NULL;
301
canvasPtr->textInfo.insertWidth = 0;
302
canvasPtr->textInfo.insertBorderWidth = 0;
303
canvasPtr->textInfo.focusItemPtr = NULL;
304
canvasPtr->textInfo.gotFocus = 0;
305
canvasPtr->textInfo.cursorOn = 0;
306
canvasPtr->insertOnTime = 0;
307
canvasPtr->insertOffTime = 0;
308
canvasPtr->insertBlinkHandler = (Tcl_TimerToken) NULL;
309
canvasPtr->xOrigin = canvasPtr->yOrigin = 0;
310
canvasPtr->drawableXOrigin = canvasPtr->drawableYOrigin = 0;
311
canvasPtr->bindingTable = NULL;
312
canvasPtr->currentItemPtr = NULL;
313
canvasPtr->newCurrentPtr = NULL;
314
canvasPtr->closeEnough = 0.0;
315
canvasPtr->pickEvent.type = LeaveNotify;
316
canvasPtr->pickEvent.xcrossing.x = 0;
317
canvasPtr->pickEvent.xcrossing.y = 0;
318
canvasPtr->state = 0;
319
canvasPtr->xScrollCmd = NULL;
320
canvasPtr->yScrollCmd = NULL;
321
canvasPtr->scrollX1 = 0;
322
canvasPtr->scrollY1 = 0;
323
canvasPtr->scrollX2 = 0;
324
canvasPtr->scrollY2 = 0;
325
canvasPtr->regionString = NULL;
326
canvasPtr->xScrollIncrement = 0;
327
canvasPtr->yScrollIncrement = 0;
328
canvasPtr->scanX = 0;
329
canvasPtr->scanXOrigin = 0;
330
canvasPtr->scanY = 0;
331
canvasPtr->scanYOrigin = 0;
332
canvasPtr->hotPtr = NULL;
333
canvasPtr->hotPrevPtr = NULL;
334
canvasPtr->cursor = None;
335
canvasPtr->takeFocus = NULL;
336
canvasPtr->pixelsPerMM = WidthOfScreen(Tk_Screen(new));
337
canvasPtr->pixelsPerMM /= WidthMMOfScreen(Tk_Screen(new));
338
canvasPtr->flags = 0;
339
canvasPtr->nextId = 1;
340
canvasPtr->psInfoPtr = NULL;
341
342
Tk_SetClass(canvasPtr->tkwin, "Canvas");
343
Tk_CreateEventHandler(canvasPtr->tkwin,
344
ExposureMask|StructureNotifyMask|FocusChangeMask,
345
CanvasEventProc, (ClientData) canvasPtr);
346
Tk_CreateEventHandler(canvasPtr->tkwin, KeyPressMask|KeyReleaseMask
347
|ButtonPressMask|ButtonReleaseMask|EnterWindowMask
348
|LeaveWindowMask|PointerMotionMask, CanvasBindProc,
349
(ClientData) canvasPtr);
350
Tk_CreateSelHandler(canvasPtr->tkwin, XA_PRIMARY, XA_STRING,
351
CanvasFetchSelection, (ClientData) canvasPtr, XA_STRING);
352
if (ConfigureCanvas(interp, canvasPtr, argc-2, argv+2, 0) != TCL_OK) {
353
goto error;
354
}
355
356
interp->result = Tk_PathName(canvasPtr->tkwin);
357
return TCL_OK;
358
359
error:
360
Tk_DestroyWindow(canvasPtr->tkwin);
361
return TCL_ERROR;
362
}
363
364
/*
365
*--------------------------------------------------------------
366
*
367
* CanvasWidgetCmd --
368
*
369
* This procedure is invoked to process the Tcl command
370
* that corresponds to a widget managed by this module.
371
* See the user documentation for details on what it does.
372
*
373
* Results:
374
* A standard Tcl result.
375
*
376
* Side effects:
377
* See the user documentation.
378
*
379
*--------------------------------------------------------------
380
*/
381
382
static int
383
CanvasWidgetCmd(clientData, interp, argc, argv)
384
ClientData clientData; /* Information about canvas
385
* widget. */
386
Tcl_Interp *interp; /* Current interpreter. */
387
int argc; /* Number of arguments. */
388
char **argv; /* Argument strings. */
389
{
390
TkCanvas *canvasPtr = (TkCanvas *) clientData;
391
size_t length;
392
int c, result;
393
Tk_Item *itemPtr = NULL; /* Initialization needed only to
394
* prevent compiler warning. */
395
TagSearch search;
396
397
if (argc < 2) {
398
Tcl_AppendResult(interp, "wrong # args: should be \"",
399
argv[0], " option ?arg arg ...?\"", (char *) NULL);
400
return TCL_ERROR;
401
}
402
Tcl_Preserve((ClientData) canvasPtr);
403
result = TCL_OK;
404
c = argv[1][0];
405
length = strlen(argv[1]);
406
if ((c == 'a') && (strncmp(argv[1], "addtag", length) == 0)) {
407
if (argc < 4) {
408
Tcl_AppendResult(interp, "wrong # args: should be \"",
409
argv[0], " addtags tag searchCommand ?arg arg ...?\"",
410
(char *) NULL);
411
goto error;
412
}
413
result = FindItems(interp, canvasPtr, argc-3, argv+3, argv[2], argv[0],
414
" addtag tag");
415
} else if ((c == 'b') && (strncmp(argv[1], "bbox", length) == 0)
416
&& (length >= 2)) {
417
int i, gotAny;
418
int x1 = 0, y1 = 0, x2 = 0, y2 = 0; /* Initializations needed
419
* only to prevent compiler
420
* warnings. */
421
422
if (argc < 3) {
423
Tcl_AppendResult(interp, "wrong # args: should be \"",
424
argv[0], " bbox tagOrId ?tagOrId ...?\"",
425
(char *) NULL);
426
goto error;
427
}
428
gotAny = 0;
429
for (i = 2; i < argc; i++) {
430
for (itemPtr = StartTagSearch(canvasPtr, argv[i], &search);
431
itemPtr != NULL; itemPtr = NextItem(&search)) {
432
if ((itemPtr->x1 >= itemPtr->x2)
433
|| (itemPtr->y1 >= itemPtr->y2)) {
434
continue;
435
}
436
if (!gotAny) {
437
x1 = itemPtr->x1;
438
y1 = itemPtr->y1;
439
x2 = itemPtr->x2;
440
y2 = itemPtr->y2;
441
gotAny = 1;
442
} else {
443
if (itemPtr->x1 < x1) {
444
x1 = itemPtr->x1;
445
}
446
if (itemPtr->y1 < y1) {
447
y1 = itemPtr->y1;
448
}
449
if (itemPtr->x2 > x2) {
450
x2 = itemPtr->x2;
451
}
452
if (itemPtr->y2 > y2) {
453
y2 = itemPtr->y2;
454
}
455
}
456
}
457
}
458
if (gotAny) {
459
sprintf(interp->result, "%d %d %d %d", x1, y1, x2, y2);
460
}
461
} else if ((c == 'b') && (strncmp(argv[1], "bind", length) == 0)
462
&& (length >= 2)) {
463
ClientData object;
464
465
if ((argc < 3) || (argc > 5)) {
466
Tcl_AppendResult(interp, "wrong # args: should be \"",
467
argv[0], " bind tagOrId ?sequence? ?command?\"",
468
(char *) NULL);
469
goto error;
470
}
471
472
/*
473
* Figure out what object to use for the binding (individual
474
* item vs. tag).
475
*/
476
477
object = 0;
478
if (isdigit(UCHAR(argv[2][0]))) {
479
int id;
480
char *end;
481
482
id = strtoul(argv[2], &end, 0);
483
if (*end != 0) {
484
goto bindByTag;
485
}
486
for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
487
itemPtr = itemPtr->nextPtr) {
488
if (itemPtr->id == id) {
489
object = (ClientData) itemPtr;
490
break;
491
}
492
}
493
if (object == 0) {
494
Tcl_AppendResult(interp, "item \"", argv[2],
495
"\" doesn't exist", (char *) NULL);
496
goto error;
497
}
498
} else {
499
bindByTag:
500
object = (ClientData) Tk_GetUid(argv[2]);
501
}
502
503
/*
504
* Make a binding table if the canvas doesn't already have
505
* one.
506
*/
507
508
if (canvasPtr->bindingTable == NULL) {
509
canvasPtr->bindingTable = Tk_CreateBindingTable(interp);
510
}
511
512
if (argc == 5) {
513
int append = 0;
514
unsigned long mask;
515
516
if (argv[4][0] == 0) {
517
result = Tk_DeleteBinding(interp, canvasPtr->bindingTable,
518
object, argv[3]);
519
goto done;
520
}
521
if (argv[4][0] == '+') {
522
argv[4]++;
523
append = 1;
524
}
525
mask = Tk_CreateBinding(interp, canvasPtr->bindingTable,
526
object, argv[3], argv[4], append);
527
if (mask == 0) {
528
goto error;
529
}
530
if (mask & (unsigned) ~(ButtonMotionMask|Button1MotionMask
531
|Button2MotionMask|Button3MotionMask|Button4MotionMask
532
|Button5MotionMask|ButtonPressMask|ButtonReleaseMask
533
|EnterWindowMask|LeaveWindowMask|KeyPressMask
534
|KeyReleaseMask|PointerMotionMask)) {
535
Tk_DeleteBinding(interp, canvasPtr->bindingTable,
536
object, argv[3]);
537
Tcl_ResetResult(interp);
538
Tcl_AppendResult(interp, "requested illegal events; ",
539
"only key, button, motion, and enter/leave ",
540
"events may be used", (char *) NULL);
541
goto error;
542
}
543
} else if (argc == 4) {
544
char *command;
545
546
command = Tk_GetBinding(interp, canvasPtr->bindingTable,
547
object, argv[3]);
548
if (command == NULL) {
549
goto error;
550
}
551
interp->result = command;
552
} else {
553
Tk_GetAllBindings(interp, canvasPtr->bindingTable, object);
554
}
555
} else if ((c == 'c') && (strcmp(argv[1], "canvasx") == 0)) {
556
int x;
557
double grid;
558
559
if ((argc < 3) || (argc > 4)) {
560
Tcl_AppendResult(interp, "wrong # args: should be \"",
561
argv[0], " canvasx screenx ?gridspacing?\"",
562
(char *) NULL);
563
goto error;
564
}
565
if (Tk_GetPixels(interp, canvasPtr->tkwin, argv[2], &x) != TCL_OK) {
566
goto error;
567
}
568
if (argc == 4) {
569
if (Tk_CanvasGetCoord(interp, (Tk_Canvas) canvasPtr, argv[3],
570
&grid) != TCL_OK) {
571
goto error;
572
}
573
} else {
574
grid = 0.0;
575
}
576
x += canvasPtr->xOrigin;
577
Tcl_PrintDouble(interp, GridAlign((double) x, grid), interp->result);
578
} else if ((c == 'c') && (strcmp(argv[1], "canvasy") == 0)) {
579
int y;
580
double grid;
581
582
if ((argc < 3) || (argc > 4)) {
583
Tcl_AppendResult(interp, "wrong # args: should be \"",
584
argv[0], " canvasy screeny ?gridspacing?\"",
585
(char *) NULL);
586
goto error;
587
}
588
if (Tk_GetPixels(interp, canvasPtr->tkwin, argv[2], &y) != TCL_OK) {
589
goto error;
590
}
591
if (argc == 4) {
592
if (Tk_CanvasGetCoord(interp, (Tk_Canvas) canvasPtr,
593
argv[3], &grid) != TCL_OK) {
594
goto error;
595
}
596
} else {
597
grid = 0.0;
598
}
599
y += canvasPtr->yOrigin;
600
Tcl_PrintDouble(interp, GridAlign((double) y, grid), interp->result);
601
} else if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
602
&& (length >= 2)) {
603
if (argc != 3) {
604
Tcl_AppendResult(interp, "wrong # args: should be \"",
605
argv[0], " cget option\"",
606
(char *) NULL);
607
goto error;
608
}
609
result = Tk_ConfigureValue(interp, canvasPtr->tkwin, configSpecs,
610
(char *) canvasPtr, argv[2], 0);
611
} else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
612
&& (length >= 3)) {
613
if (argc == 2) {
614
result = Tk_ConfigureInfo(interp, canvasPtr->tkwin, configSpecs,
615
(char *) canvasPtr, (char *) NULL, 0);
616
} else if (argc == 3) {
617
result = Tk_ConfigureInfo(interp, canvasPtr->tkwin, configSpecs,
618
(char *) canvasPtr, argv[2], 0);
619
} else {
620
result = ConfigureCanvas(interp, canvasPtr, argc-2, argv+2,
621
TK_CONFIG_ARGV_ONLY);
622
}
623
} else if ((c == 'c') && (strncmp(argv[1], "coords", length) == 0)
624
&& (length >= 3)) {
625
if (argc < 3) {
626
Tcl_AppendResult(interp, "wrong # args: should be \"",
627
argv[0], " coords tagOrId ?x y x y ...?\"",
628
(char *) NULL);
629
goto error;
630
}
631
itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
632
if (itemPtr != NULL) {
633
if (argc != 3) {
634
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
635
itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
636
}
637
if (itemPtr->typePtr->coordProc != NULL) {
638
result = (*itemPtr->typePtr->coordProc)(interp,
639
(Tk_Canvas) canvasPtr, itemPtr, argc-3, argv+3);
640
}
641
if (argc != 3) {
642
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
643
itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
644
}
645
}
646
} else if ((c == 'c') && (strncmp(argv[1], "create", length) == 0)
647
&& (length >= 2)) {
648
Tk_ItemType *typePtr;
649
Tk_ItemType *matchPtr = NULL;
650
Tk_Item *itemPtr;
651
652
if (argc < 3) {
653
Tcl_AppendResult(interp, "wrong # args: should be \"",
654
argv[0], " create type ?arg arg ...?\"", (char *) NULL);
655
goto error;
656
}
657
c = argv[2][0];
658
length = strlen(argv[2]);
659
for (typePtr = typeList; typePtr != NULL; typePtr = typePtr->nextPtr) {
660
if ((c == typePtr->name[0])
661
&& (strncmp(argv[2], typePtr->name, length) == 0)) {
662
if (matchPtr != NULL) {
663
badType:
664
Tcl_AppendResult(interp,
665
"unknown or ambiguous item type \"",
666
argv[2], "\"", (char *) NULL);
667
goto error;
668
}
669
matchPtr = typePtr;
670
}
671
}
672
if (matchPtr == NULL) {
673
goto badType;
674
}
675
typePtr = matchPtr;
676
itemPtr = (Tk_Item *) ckalloc((unsigned) typePtr->itemSize);
677
itemPtr->id = canvasPtr->nextId;
678
canvasPtr->nextId++;
679
itemPtr->tagPtr = itemPtr->staticTagSpace;
680
itemPtr->tagSpace = TK_TAG_SPACE;
681
itemPtr->numTags = 0;
682
itemPtr->typePtr = typePtr;
683
if ((*typePtr->createProc)(interp, (Tk_Canvas) canvasPtr,
684
itemPtr, argc-3, argv+3) != TCL_OK) {
685
ckfree((char *) itemPtr);
686
goto error;
687
}
688
itemPtr->nextPtr = NULL;
689
canvasPtr->hotPtr = itemPtr;
690
canvasPtr->hotPrevPtr = canvasPtr->lastItemPtr;
691
if (canvasPtr->lastItemPtr == NULL) {
692
canvasPtr->firstItemPtr = itemPtr;
693
} else {
694
canvasPtr->lastItemPtr->nextPtr = itemPtr;
695
}
696
canvasPtr->lastItemPtr = itemPtr;
697
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
698
itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
699
canvasPtr->flags |= REPICK_NEEDED;
700
sprintf(interp->result, "%d", itemPtr->id);
701
} else if ((c == 'd') && (strncmp(argv[1], "dchars", length) == 0)
702
&& (length >= 2)) {
703
int first, last;
704
705
if ((argc != 4) && (argc != 5)) {
706
Tcl_AppendResult(interp, "wrong # args: should be \"",
707
argv[0], " dchars tagOrId first ?last?\"",
708
(char *) NULL);
709
goto error;
710
}
711
for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
712
itemPtr != NULL; itemPtr = NextItem(&search)) {
713
if ((itemPtr->typePtr->indexProc == NULL)
714
|| (itemPtr->typePtr->dCharsProc == NULL)) {
715
continue;
716
}
717
if ((*itemPtr->typePtr->indexProc)(interp, (Tk_Canvas) canvasPtr,
718
itemPtr, argv[3], &first) != TCL_OK) {
719
goto error;
720
}
721
if (argc == 5) {
722
if ((*itemPtr->typePtr->indexProc)(interp,
723
(Tk_Canvas) canvasPtr, itemPtr, argv[4], &last)
724
!= TCL_OK) {
725
goto error;
726
}
727
} else {
728
last = first;
729
}
730
731
/*
732
* Redraw both item's old and new areas: it's possible
733
* that a delete could result in a new area larger than
734
* the old area.
735
*/
736
737
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
738
itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
739
(*itemPtr->typePtr->dCharsProc)((Tk_Canvas) canvasPtr,
740
itemPtr, first, last);
741
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
742
itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
743
}
744
} else if ((c == 'd') && (strncmp(argv[1], "delete", length) == 0)
745
&& (length >= 2)) {
746
int i;
747
748
for (i = 2; i < argc; i++) {
749
for (itemPtr = StartTagSearch(canvasPtr, argv[i], &search);
750
itemPtr != NULL; itemPtr = NextItem(&search)) {
751
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
752
itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
753
if (canvasPtr->bindingTable != NULL) {
754
Tk_DeleteAllBindings(canvasPtr->bindingTable,
755
(ClientData) itemPtr);
756
}
757
(*itemPtr->typePtr->deleteProc)((Tk_Canvas) canvasPtr, itemPtr,
758
canvasPtr->display);
759
if (itemPtr->tagPtr != itemPtr->staticTagSpace) {
760
ckfree((char *) itemPtr->tagPtr);
761
}
762
if (search.prevPtr == NULL) {
763
canvasPtr->firstItemPtr = itemPtr->nextPtr;
764
if (canvasPtr->firstItemPtr == NULL) {
765
canvasPtr->lastItemPtr = NULL;
766
}
767
} else {
768
search.prevPtr->nextPtr = itemPtr->nextPtr;
769
}
770
if (canvasPtr->lastItemPtr == itemPtr) {
771
canvasPtr->lastItemPtr = search.prevPtr;
772
}
773
ckfree((char *) itemPtr);
774
if (itemPtr == canvasPtr->currentItemPtr) {
775
canvasPtr->currentItemPtr = NULL;
776
canvasPtr->flags |= REPICK_NEEDED;
777
}
778
if (itemPtr == canvasPtr->newCurrentPtr) {
779
canvasPtr->newCurrentPtr = NULL;
780
canvasPtr->flags |= REPICK_NEEDED;
781
}
782
if (itemPtr == canvasPtr->textInfo.focusItemPtr) {
783
canvasPtr->textInfo.focusItemPtr = NULL;
784
}
785
if (itemPtr == canvasPtr->textInfo.selItemPtr) {
786
canvasPtr->textInfo.selItemPtr = NULL;
787
}
788
if ((itemPtr == canvasPtr->hotPtr)
789
|| (itemPtr == canvasPtr->hotPrevPtr)) {
790
canvasPtr->hotPtr = NULL;
791
}
792
}
793
}
794
} else if ((c == 'd') && (strncmp(argv[1], "dtag", length) == 0)
795
&& (length >= 2)) {
796
Tk_Uid tag;
797
int i;
798
799
if ((argc != 3) && (argc != 4)) {
800
Tcl_AppendResult(interp, "wrong # args: should be \"",
801
argv[0], " dtag tagOrId ?tagToDelete?\"",
802
(char *) NULL);
803
goto error;
804
}
805
if (argc == 4) {
806
tag = Tk_GetUid(argv[3]);
807
} else {
808
tag = Tk_GetUid(argv[2]);
809
}
810
for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
811
itemPtr != NULL; itemPtr = NextItem(&search)) {
812
for (i = itemPtr->numTags-1; i >= 0; i--) {
813
if (itemPtr->tagPtr[i] == tag) {
814
itemPtr->tagPtr[i] = itemPtr->tagPtr[itemPtr->numTags-1];
815
itemPtr->numTags--;
816
}
817
}
818
}
819
} else if ((c == 'f') && (strncmp(argv[1], "find", length) == 0)
820
&& (length >= 2)) {
821
if (argc < 3) {
822
Tcl_AppendResult(interp, "wrong # args: should be \"",
823
argv[0], " find searchCommand ?arg arg ...?\"",
824
(char *) NULL);
825
goto error;
826
}
827
result = FindItems(interp, canvasPtr, argc-2, argv+2, (char *) NULL,
828
argv[0]," find");
829
} else if ((c == 'f') && (strncmp(argv[1], "focus", length) == 0)
830
&& (length >= 2)) {
831
if (argc > 3) {
832
Tcl_AppendResult(interp, "wrong # args: should be \"",
833
argv[0], " focus ?tagOrId?\"",
834
(char *) NULL);
835
goto error;
836
}
837
itemPtr = canvasPtr->textInfo.focusItemPtr;
838
if (argc == 2) {
839
if (itemPtr != NULL) {
840
sprintf(interp->result, "%d", itemPtr->id);
841
}
842
goto done;
843
}
844
if ((itemPtr != NULL) && (canvasPtr->textInfo.gotFocus)) {
845
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
846
itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
847
}
848
if (argv[2][0] == 0) {
849
canvasPtr->textInfo.focusItemPtr = NULL;
850
goto done;
851
}
852
for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
853
itemPtr != NULL; itemPtr = NextItem(&search)) {
854
if (itemPtr->typePtr->icursorProc != NULL) {
855
break;
856
}
857
}
858
if (itemPtr == NULL) {
859
goto done;
860
}
861
canvasPtr->textInfo.focusItemPtr = itemPtr;
862
if (canvasPtr->textInfo.gotFocus) {
863
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
864
itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
865
}
866
} else if ((c == 'g') && (strncmp(argv[1], "gettags", length) == 0)) {
867
if (argc != 3) {
868
Tcl_AppendResult(interp, "wrong # args: should be \"",
869
argv[0], " gettags tagOrId\"", (char *) NULL);
870
goto error;
871
}
872
itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
873
if (itemPtr != NULL) {
874
int i;
875
for (i = 0; i < itemPtr->numTags; i++) {
876
Tcl_AppendElement(interp, (char *) itemPtr->tagPtr[i]);
877
}
878
}
879
} else if ((c == 'i') && (strncmp(argv[1], "icursor", length) == 0)
880
&& (length >= 2)) {
881
int index;
882
883
if (argc != 4) {
884
Tcl_AppendResult(interp, "wrong # args: should be \"",
885
argv[0], " icursor tagOrId index\"",
886
(char *) NULL);
887
goto error;
888
}
889
for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
890
itemPtr != NULL; itemPtr = NextItem(&search)) {
891
if ((itemPtr->typePtr->indexProc == NULL)
892
|| (itemPtr->typePtr->icursorProc == NULL)) {
893
goto done;
894
}
895
if ((*itemPtr->typePtr->indexProc)(interp, (Tk_Canvas) canvasPtr,
896
itemPtr, argv[3], &index) != TCL_OK) {
897
goto error;
898
}
899
(*itemPtr->typePtr->icursorProc)((Tk_Canvas) canvasPtr, itemPtr,
900
index);
901
if ((itemPtr == canvasPtr->textInfo.focusItemPtr)
902
&& (canvasPtr->textInfo.cursorOn)) {
903
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
904
itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
905
}
906
}
907
} else if ((c == 'i') && (strncmp(argv[1], "index", length) == 0)
908
&& (length >= 3)) {
909
int index;
910
911
if (argc != 4) {
912
Tcl_AppendResult(interp, "wrong # args: should be \"",
913
argv[0], " index tagOrId string\"",
914
(char *) NULL);
915
goto error;
916
}
917
for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
918
itemPtr != NULL; itemPtr = NextItem(&search)) {
919
if (itemPtr->typePtr->indexProc != NULL) {
920
break;
921
}
922
}
923
if (itemPtr == NULL) {
924
Tcl_AppendResult(interp, "can't find an indexable item \"",
925
argv[2], "\"", (char *) NULL);
926
goto error;
927
}
928
if ((*itemPtr->typePtr->indexProc)(interp, (Tk_Canvas) canvasPtr,
929
itemPtr, argv[3], &index) != TCL_OK) {
930
goto error;
931
}
932
sprintf(interp->result, "%d", index);
933
} else if ((c == 'i') && (strncmp(argv[1], "insert", length) == 0)
934
&& (length >= 3)) {
935
int beforeThis;
936
937
if (argc != 5) {
938
Tcl_AppendResult(interp, "wrong # args: should be \"",
939
argv[0], " insert tagOrId beforeThis string\"",
940
(char *) NULL);
941
goto error;
942
}
943
for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
944
itemPtr != NULL; itemPtr = NextItem(&search)) {
945
if ((itemPtr->typePtr->indexProc == NULL)
946
|| (itemPtr->typePtr->insertProc == NULL)) {
947
continue;
948
}
949
if ((*itemPtr->typePtr->indexProc)(interp, (Tk_Canvas) canvasPtr,
950
itemPtr, argv[3], &beforeThis) != TCL_OK) {
951
goto error;
952
}
953
954
/*
955
* Redraw both item's old and new areas: it's possible
956
* that an insertion could result in a new area either
957
* larger or smaller than the old area.
958
*/
959
960
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
961
itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
962
(*itemPtr->typePtr->insertProc)((Tk_Canvas) canvasPtr,
963
itemPtr, beforeThis, argv[4]);
964
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr, itemPtr->x1,
965
itemPtr->y1, itemPtr->x2, itemPtr->y2);
966
}
967
} else if ((c == 'i') && (strncmp(argv[1], "itemcget", length) == 0)
968
&& (length >= 6)) {
969
if (argc != 4) {
970
Tcl_AppendResult(interp, "wrong # args: should be \"",
971
argv[0], " itemcget tagOrId option\"",
972
(char *) NULL);
973
return TCL_ERROR;
974
}
975
itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
976
if (itemPtr != NULL) {
977
result = Tk_ConfigureValue(canvasPtr->interp, canvasPtr->tkwin,
978
itemPtr->typePtr->configSpecs, (char *) itemPtr,
979
argv[3], 0);
980
}
981
} else if ((c == 'i') && (strncmp(argv[1], "itemconfigure", length) == 0)
982
&& (length >= 6)) {
983
if (argc < 3) {
984
Tcl_AppendResult(interp, "wrong # args: should be \"",
985
argv[0], " itemconfigure tagOrId ?option value ...?\"",
986
(char *) NULL);
987
goto error;
988
}
989
for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
990
itemPtr != NULL; itemPtr = NextItem(&search)) {
991
if (argc == 3) {
992
result = Tk_ConfigureInfo(canvasPtr->interp, canvasPtr->tkwin,
993
itemPtr->typePtr->configSpecs, (char *) itemPtr,
994
(char *) NULL, 0);
995
} else if (argc == 4) {
996
result = Tk_ConfigureInfo(canvasPtr->interp, canvasPtr->tkwin,
997
itemPtr->typePtr->configSpecs, (char *) itemPtr,
998
argv[3], 0);
999
} else {
1000
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
1001
itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
1002
result = (*itemPtr->typePtr->configProc)(interp,
1003
(Tk_Canvas) canvasPtr, itemPtr, argc-3, argv+3,
1004
TK_CONFIG_ARGV_ONLY);
1005
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
1006
itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
1007
canvasPtr->flags |= REPICK_NEEDED;
1008
}
1009
if ((result != TCL_OK) || (argc < 5)) {
1010
break;
1011
}
1012
}
1013
} else if ((c == 'l') && (strncmp(argv[1], "lower", length) == 0)) {
1014
Tk_Item *prevPtr;
1015
1016
if ((argc != 3) && (argc != 4)) {
1017
Tcl_AppendResult(interp, "wrong # args: should be \"",
1018
argv[0], " lower tagOrId ?belowThis?\"",
1019
(char *) NULL);
1020
goto error;
1021
}
1022
1023
/*
1024
* First find the item just after which we'll insert the
1025
* named items.
1026
*/
1027
1028
if (argc == 3) {
1029
prevPtr = NULL;
1030
} else {
1031
prevPtr = StartTagSearch(canvasPtr, argv[3], &search);
1032
if (prevPtr != NULL) {
1033
prevPtr = search.prevPtr;
1034
} else {
1035
Tcl_AppendResult(interp, "tag \"", argv[3],
1036
"\" doesn't match any items", (char *) NULL);
1037
goto error;
1038
}
1039
}
1040
RelinkItems(canvasPtr, argv[2], prevPtr);
1041
} else if ((c == 'm') && (strncmp(argv[1], "move", length) == 0)) {
1042
double xAmount, yAmount;
1043
1044
if (argc != 5) {
1045
Tcl_AppendResult(interp, "wrong # args: should be \"",
1046
argv[0], " move tagOrId xAmount yAmount\"",
1047
(char *) NULL);
1048
goto error;
1049
}
1050
if ((Tk_CanvasGetCoord(interp, (Tk_Canvas) canvasPtr, argv[3],
1051
&xAmount) != TCL_OK) || (Tk_CanvasGetCoord(interp,
1052
(Tk_Canvas) canvasPtr, argv[4], &yAmount) != TCL_OK)) {
1053
goto error;
1054
}
1055
for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
1056
itemPtr != NULL; itemPtr = NextItem(&search)) {
1057
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
1058
itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
1059
(void) (*itemPtr->typePtr->translateProc)((Tk_Canvas) canvasPtr,
1060
itemPtr, xAmount, yAmount);
1061
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
1062
itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
1063
canvasPtr->flags |= REPICK_NEEDED;
1064
}
1065
} else if ((c == 'p') && (strncmp(argv[1], "postscript", length) == 0)) {
1066
result = TkCanvPostscriptCmd(canvasPtr, interp, argc, argv);
1067
} else if ((c == 'r') && (strncmp(argv[1], "raise", length) == 0)) {
1068
Tk_Item *prevPtr;
1069
1070
if ((argc != 3) && (argc != 4)) {
1071
Tcl_AppendResult(interp, "wrong # args: should be \"",
1072
argv[0], " raise tagOrId ?aboveThis?\"",
1073
(char *) NULL);
1074
goto error;
1075
}
1076
1077
/*
1078
* First find the item just after which we'll insert the
1079
* named items.
1080
*/
1081
1082
if (argc == 3) {
1083
prevPtr = canvasPtr->lastItemPtr;
1084
} else {
1085
prevPtr = NULL;
1086
for (itemPtr = StartTagSearch(canvasPtr, argv[3], &search);
1087
itemPtr != NULL; itemPtr = NextItem(&search)) {
1088
prevPtr = itemPtr;
1089
}
1090
if (prevPtr == NULL) {
1091
Tcl_AppendResult(interp, "tagOrId \"", argv[3],
1092
"\" doesn't match any items", (char *) NULL);
1093
goto error;
1094
}
1095
}
1096
RelinkItems(canvasPtr, argv[2], prevPtr);
1097
} else if ((c == 's') && (strncmp(argv[1], "scale", length) == 0)
1098
&& (length >= 3)) {
1099
double xOrigin, yOrigin, xScale, yScale;
1100
1101
if (argc != 7) {
1102
Tcl_AppendResult(interp, "wrong # args: should be \"",
1103
argv[0], " scale tagOrId xOrigin yOrigin xScale yScale\"",
1104
(char *) NULL);
1105
goto error;
1106
}
1107
if ((Tk_CanvasGetCoord(interp, (Tk_Canvas) canvasPtr,
1108
argv[3], &xOrigin) != TCL_OK)
1109
|| (Tk_CanvasGetCoord(interp, (Tk_Canvas) canvasPtr,
1110
argv[4], &yOrigin) != TCL_OK)
1111
|| (Tcl_GetDouble(interp, argv[5], &xScale) != TCL_OK)
1112
|| (Tcl_GetDouble(interp, argv[6], &yScale) != TCL_OK)) {
1113
goto error;
1114
}
1115
if ((xScale == 0.0) || (yScale == 0.0)) {
1116
interp->result = "scale factor cannot be zero";
1117
goto error;
1118
}
1119
for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
1120
itemPtr != NULL; itemPtr = NextItem(&search)) {
1121
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
1122
itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
1123
(void) (*itemPtr->typePtr->scaleProc)((Tk_Canvas) canvasPtr,
1124
itemPtr, xOrigin, yOrigin, xScale, yScale);
1125
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
1126
itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
1127
canvasPtr->flags |= REPICK_NEEDED;
1128
}
1129
} else if ((c == 's') && (strncmp(argv[1], "scan", length) == 0)
1130
&& (length >= 3)) {
1131
int x, y;
1132
1133
if (argc != 5) {
1134
Tcl_AppendResult(interp, "wrong # args: should be \"",
1135
argv[0], " scan mark|dragto x y\"", (char *) NULL);
1136
goto error;
1137
}
1138
if ((Tcl_GetInt(interp, argv[3], &x) != TCL_OK)
1139
|| (Tcl_GetInt(interp, argv[4], &y) != TCL_OK)){
1140
goto error;
1141
}
1142
if ((argv[2][0] == 'm')
1143
&& (strncmp(argv[2], "mark", strlen(argv[2])) == 0)) {
1144
canvasPtr->scanX = x;
1145
canvasPtr->scanXOrigin = canvasPtr->xOrigin;
1146
canvasPtr->scanY = y;
1147
canvasPtr->scanYOrigin = canvasPtr->yOrigin;
1148
} else if ((argv[2][0] == 'd')
1149
&& (strncmp(argv[2], "dragto", strlen(argv[2])) == 0)) {
1150
int newXOrigin, newYOrigin, tmp;
1151
1152
/*
1153
* Compute a new view origin for the canvas, amplifying the
1154
* mouse motion.
1155
*/
1156
1157
tmp = canvasPtr->scanXOrigin - 10*(x - canvasPtr->scanX)
1158
- canvasPtr->scrollX1;
1159
newXOrigin = canvasPtr->scrollX1 + tmp;
1160
tmp = canvasPtr->scanYOrigin - 10*(y - canvasPtr->scanY)
1161
- canvasPtr->scrollY1;
1162
newYOrigin = canvasPtr->scrollY1 + tmp;
1163
CanvasSetOrigin(canvasPtr, newXOrigin, newYOrigin);
1164
} else {
1165
Tcl_AppendResult(interp, "bad scan option \"", argv[2],
1166
"\": must be mark or dragto", (char *) NULL);
1167
goto error;
1168
}
1169
} else if ((c == 's') && (strncmp(argv[1], "select", length) == 0)
1170
&& (length >= 2)) {
1171
int index;
1172
1173
if (argc < 3) {
1174
Tcl_AppendResult(interp, "wrong # args: should be \"",
1175
argv[0], " select option ?tagOrId? ?arg?\"", (char *) NULL);
1176
goto error;
1177
}
1178
if (argc >= 4) {
1179
for (itemPtr = StartTagSearch(canvasPtr, argv[3], &search);
1180
itemPtr != NULL; itemPtr = NextItem(&search)) {
1181
if ((itemPtr->typePtr->indexProc != NULL)
1182
&& (itemPtr->typePtr->selectionProc != NULL)){
1183
break;
1184
}
1185
}
1186
if (itemPtr == NULL) {
1187
Tcl_AppendResult(interp,
1188
"can't find an indexable and selectable item \"",
1189
argv[3], "\"", (char *) NULL);
1190
goto error;
1191
}
1192
}
1193
if (argc == 5) {
1194
if ((*itemPtr->typePtr->indexProc)(interp, (Tk_Canvas) canvasPtr,
1195
itemPtr, argv[4], &index) != TCL_OK) {
1196
goto error;
1197
}
1198
}
1199
length = strlen(argv[2]);
1200
c = argv[2][0];
1201
if ((c == 'a') && (strncmp(argv[2], "adjust", length) == 0)) {
1202
if (argc != 5) {
1203
Tcl_AppendResult(interp, "wrong # args: should be \"",
1204
argv[0], " select adjust tagOrId index\"",
1205
(char *) NULL);
1206
goto error;
1207
}
1208
if (canvasPtr->textInfo.selItemPtr == itemPtr) {
1209
if (index < (canvasPtr->textInfo.selectFirst
1210
+ canvasPtr->textInfo.selectLast)/2) {
1211
canvasPtr->textInfo.selectAnchor =
1212
canvasPtr->textInfo.selectLast + 1;
1213
} else {
1214
canvasPtr->textInfo.selectAnchor =
1215
canvasPtr->textInfo.selectFirst;
1216
}
1217
}
1218
CanvasSelectTo(canvasPtr, itemPtr, index);
1219
} else if ((c == 'c') && (argv[2] != NULL)
1220
&& (strncmp(argv[2], "clear", length) == 0)) {
1221
if (argc != 3) {
1222
Tcl_AppendResult(interp, "wrong # args: should be \"",
1223
argv[0], " select clear\"", (char *) NULL);
1224
goto error;
1225
}
1226
if (canvasPtr->textInfo.selItemPtr != NULL) {
1227
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
1228
canvasPtr->textInfo.selItemPtr->x1,
1229
canvasPtr->textInfo.selItemPtr->y1,
1230
canvasPtr->textInfo.selItemPtr->x2,
1231
canvasPtr->textInfo.selItemPtr->y2);
1232
canvasPtr->textInfo.selItemPtr = NULL;
1233
}
1234
goto done;
1235
} else if ((c == 'f') && (strncmp(argv[2], "from", length) == 0)) {
1236
if (argc != 5) {
1237
Tcl_AppendResult(interp, "wrong # args: should be \"",
1238
argv[0], " select from tagOrId index\"",
1239
(char *) NULL);
1240
goto error;
1241
}
1242
canvasPtr->textInfo.anchorItemPtr = itemPtr;
1243
canvasPtr->textInfo.selectAnchor = index;
1244
} else if ((c == 'i') && (strncmp(argv[2], "item", length) == 0)) {
1245
if (argc != 3) {
1246
Tcl_AppendResult(interp, "wrong # args: should be \"",
1247
argv[0], " select item\"", (char *) NULL);
1248
goto error;
1249
}
1250
if (canvasPtr->textInfo.selItemPtr != NULL) {
1251
sprintf(interp->result, "%d",
1252
canvasPtr->textInfo.selItemPtr->id);
1253
}
1254
} else if ((c == 't') && (strncmp(argv[2], "to", length) == 0)) {
1255
if (argc != 5) {
1256
Tcl_AppendResult(interp, "wrong # args: should be \"",
1257
argv[0], " select to tagOrId index\"",
1258
(char *) NULL);
1259
goto error;
1260
}
1261
CanvasSelectTo(canvasPtr, itemPtr, index);
1262
} else {
1263
Tcl_AppendResult(interp, "bad select option \"", argv[2],
1264
"\": must be adjust, clear, from, item, or to",
1265
(char *) NULL);
1266
goto error;
1267
}
1268
} else if ((c == 't') && (strncmp(argv[1], "type", length) == 0)) {
1269
if (argc != 3) {
1270
Tcl_AppendResult(interp, "wrong # args: should be \"",
1271
argv[0], " type tag\"", (char *) NULL);
1272
goto error;
1273
}
1274
itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
1275
if (itemPtr != NULL) {
1276
interp->result = itemPtr->typePtr->name;
1277
}
1278
} else if ((c == 'x') && (strncmp(argv[1], "xview", length) == 0)) {
1279
int count, type;
1280
int newX = 0; /* Initialization needed only to prevent
1281
* gcc warnings. */
1282
double fraction;
1283
1284
if (argc == 2) {
1285
PrintScrollFractions(canvasPtr->xOrigin + canvasPtr->inset,
1286
canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin)
1287
- canvasPtr->inset, canvasPtr->scrollX1,
1288
canvasPtr->scrollX2, interp->result);
1289
} else {
1290
type = Tk_GetScrollInfo(interp, argc, argv, &fraction, &count);
1291
switch (type) {
1292
case TK_SCROLL_ERROR:
1293
goto error;
1294
case TK_SCROLL_MOVETO:
1295
newX = canvasPtr->scrollX1 - canvasPtr->inset
1296
+ (int) (fraction * (canvasPtr->scrollX2
1297
- canvasPtr->scrollX1) + 0.5);
1298
break;
1299
case TK_SCROLL_PAGES:
1300
newX = canvasPtr->xOrigin + count * .9
1301
* (Tk_Width(canvasPtr->tkwin) - 2*canvasPtr->inset);
1302
break;
1303
case TK_SCROLL_UNITS:
1304
if (canvasPtr->xScrollIncrement > 0) {
1305
newX = canvasPtr->xOrigin
1306
+ count*canvasPtr->xScrollIncrement;
1307
} else {
1308
newX = canvasPtr->xOrigin + count * .1
1309
* (Tk_Width(canvasPtr->tkwin)
1310
- 2*canvasPtr->inset);
1311
}
1312
break;
1313
}
1314
CanvasSetOrigin(canvasPtr, newX, canvasPtr->yOrigin);
1315
}
1316
} else if ((c == 'y') && (strncmp(argv[1], "yview", length) == 0)) {
1317
int count, type;
1318
int newY = 0; /* Initialization needed only to prevent
1319
* gcc warnings. */
1320
double fraction;
1321
1322
if (argc == 2) {
1323
PrintScrollFractions(canvasPtr->yOrigin + canvasPtr->inset,
1324
canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin)
1325
- canvasPtr->inset, canvasPtr->scrollY1,
1326
canvasPtr->scrollY2, interp->result);
1327
} else {
1328
type = Tk_GetScrollInfo(interp, argc, argv, &fraction, &count);
1329
switch (type) {
1330
case TK_SCROLL_ERROR:
1331
goto error;
1332
case TK_SCROLL_MOVETO:
1333
newY = canvasPtr->scrollY1 - canvasPtr->inset
1334
+ (int) (fraction*(canvasPtr->scrollY2
1335
- canvasPtr->scrollY1) + 0.5);
1336
break;
1337
case TK_SCROLL_PAGES:
1338
newY = canvasPtr->yOrigin + count * .9
1339
* (Tk_Height(canvasPtr->tkwin)
1340
- 2*canvasPtr->inset);
1341
break;
1342
case TK_SCROLL_UNITS:
1343
if (canvasPtr->yScrollIncrement > 0) {
1344
newY = canvasPtr->yOrigin
1345
+ count*canvasPtr->yScrollIncrement;
1346
} else {
1347
newY = canvasPtr->yOrigin + count * .1
1348
* (Tk_Height(canvasPtr->tkwin)
1349
- 2*canvasPtr->inset);
1350
}
1351
break;
1352
}
1353
CanvasSetOrigin(canvasPtr, canvasPtr->xOrigin, newY);
1354
}
1355
} else {
1356
Tcl_AppendResult(interp, "bad option \"", argv[1],
1357
"\": must be addtag, bbox, bind, ",
1358
"canvasx, canvasy, cget, configure, coords, create, ",
1359
"dchars, delete, dtag, find, focus, ",
1360
"gettags, icursor, index, insert, itemcget, itemconfigure, ",
1361
"lower, move, postscript, raise, scale, scan, ",
1362
"select, type, xview, or yview",
1363
(char *) NULL);
1364
goto error;
1365
}
1366
done:
1367
Tcl_Release((ClientData) canvasPtr);
1368
return result;
1369
1370
error:
1371
Tcl_Release((ClientData) canvasPtr);
1372
return TCL_ERROR;
1373
}
1374
1375
/*
1376
*----------------------------------------------------------------------
1377
*
1378
* DestroyCanvas --
1379
*
1380
* This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
1381
* to clean up the internal structure of a canvas at a safe time
1382
* (when no-one is using it anymore).
1383
*
1384
* Results:
1385
* None.
1386
*
1387
* Side effects:
1388
* Everything associated with the canvas is freed up.
1389
*
1390
*----------------------------------------------------------------------
1391
*/
1392
1393
static void
1394
DestroyCanvas(memPtr)
1395
char *memPtr; /* Info about canvas widget. */
1396
{
1397
TkCanvas *canvasPtr = (TkCanvas *) memPtr;
1398
Tk_Item *itemPtr;
1399
1400
/*
1401
* Free up all of the items in the canvas.
1402
*/
1403
1404
for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
1405
itemPtr = canvasPtr->firstItemPtr) {
1406
canvasPtr->firstItemPtr = itemPtr->nextPtr;
1407
(*itemPtr->typePtr->deleteProc)((Tk_Canvas) canvasPtr, itemPtr,
1408
canvasPtr->display);
1409
if (itemPtr->tagPtr != itemPtr->staticTagSpace) {
1410
ckfree((char *) itemPtr->tagPtr);
1411
}
1412
ckfree((char *) itemPtr);
1413
}
1414
1415
/*
1416
* Free up all the stuff that requires special handling,
1417
* then let Tk_FreeOptions handle all the standard option-related
1418
* stuff.
1419
*/
1420
1421
if (canvasPtr->pixmapGC != None) {
1422
Tk_FreeGC(canvasPtr->display, canvasPtr->pixmapGC);
1423
}
1424
Tcl_DeleteTimerHandler(canvasPtr->insertBlinkHandler);
1425
if (canvasPtr->bindingTable != NULL) {
1426
Tk_DeleteBindingTable(canvasPtr->bindingTable);
1427
}
1428
Tk_FreeOptions(configSpecs, (char *) canvasPtr, canvasPtr->display, 0);
1429
ckfree((char *) canvasPtr);
1430
}
1431
1432
/*
1433
*----------------------------------------------------------------------
1434
*
1435
* ConfigureCanvas --
1436
*
1437
* This procedure is called to process an argv/argc list, plus
1438
* the Tk option database, in order to configure (or
1439
* reconfigure) a canvas widget.
1440
*
1441
* Results:
1442
* The return value is a standard Tcl result. If TCL_ERROR is
1443
* returned, then interp->result contains an error message.
1444
*
1445
* Side effects:
1446
* Configuration information, such as colors, border width,
1447
* etc. get set for canvasPtr; old resources get freed,
1448
* if there were any.
1449
*
1450
*----------------------------------------------------------------------
1451
*/
1452
1453
static int
1454
ConfigureCanvas(interp, canvasPtr, argc, argv, flags)
1455
Tcl_Interp *interp; /* Used for error reporting. */
1456
TkCanvas *canvasPtr; /* Information about widget; may or may
1457
* not already have values for some fields. */
1458
int argc; /* Number of valid entries in argv. */
1459
char **argv; /* Arguments. */
1460
int flags; /* Flags to pass to Tk_ConfigureWidget. */
1461
{
1462
XGCValues gcValues;
1463
GC new;
1464
1465
if (Tk_ConfigureWidget(interp, canvasPtr->tkwin, configSpecs,
1466
argc, argv, (char *) canvasPtr, flags) != TCL_OK) {
1467
return TCL_ERROR;
1468
}
1469
1470
/*
1471
* A few options need special processing, such as setting the
1472
* background from a 3-D border and creating a GC for copying
1473
* bits to the screen.
1474
*/
1475
1476
Tk_SetBackgroundFromBorder(canvasPtr->tkwin, canvasPtr->bgBorder);
1477
1478
if (canvasPtr->highlightWidth < 0) {
1479
canvasPtr->highlightWidth = 0;
1480
}
1481
canvasPtr->inset = canvasPtr->borderWidth + canvasPtr->highlightWidth;
1482
1483
gcValues.function = GXcopy;
1484
gcValues.foreground = Tk_3DBorderColor(canvasPtr->bgBorder)->pixel;
1485
gcValues.graphics_exposures = False;
1486
new = Tk_GetGC(canvasPtr->tkwin,
1487
GCFunction|GCForeground|GCGraphicsExposures, &gcValues);
1488
if (canvasPtr->pixmapGC != None) {
1489
Tk_FreeGC(canvasPtr->display, canvasPtr->pixmapGC);
1490
}
1491
canvasPtr->pixmapGC = new;
1492
1493
/*
1494
* Reset the desired dimensions for the window.
1495
*/
1496
1497
Tk_GeometryRequest(canvasPtr->tkwin, canvasPtr->width + 2*canvasPtr->inset,
1498
canvasPtr->height + 2*canvasPtr->inset);
1499
1500
/*
1501
* Restart the cursor timing sequence in case the on-time or off-time
1502
* just changed.
1503
*/
1504
1505
if (canvasPtr->textInfo.gotFocus) {
1506
CanvasFocusProc(canvasPtr, 1);
1507
}
1508
1509
/*
1510
* Recompute the scroll region.
1511
*/
1512
1513
canvasPtr->scrollX1 = 0;
1514
canvasPtr->scrollY1 = 0;
1515
canvasPtr->scrollX2 = 0;
1516
canvasPtr->scrollY2 = 0;
1517
if (canvasPtr->regionString != NULL) {
1518
int argc2;
1519
char **argv2;
1520
1521
if (Tcl_SplitList(canvasPtr->interp, canvasPtr->regionString,
1522
&argc2, &argv2) != TCL_OK) {
1523
return TCL_ERROR;
1524
}
1525
if (argc2 != 4) {
1526
Tcl_AppendResult(interp, "bad scrollRegion \"",
1527
canvasPtr->regionString, "\"", (char *) NULL);
1528
badRegion:
1529
ckfree(canvasPtr->regionString);
1530
ckfree((char *) argv2);
1531
canvasPtr->regionString = NULL;
1532
return TCL_ERROR;
1533
}
1534
if ((Tk_GetPixels(canvasPtr->interp, canvasPtr->tkwin,
1535
argv2[0], &canvasPtr->scrollX1) != TCL_OK)
1536
|| (Tk_GetPixels(canvasPtr->interp, canvasPtr->tkwin,
1537
argv2[1], &canvasPtr->scrollY1) != TCL_OK)
1538
|| (Tk_GetPixels(canvasPtr->interp, canvasPtr->tkwin,
1539
argv2[2], &canvasPtr->scrollX2) != TCL_OK)
1540
|| (Tk_GetPixels(canvasPtr->interp, canvasPtr->tkwin,
1541
argv2[3], &canvasPtr->scrollY2) != TCL_OK)) {
1542
goto badRegion;
1543
}
1544
ckfree((char *) argv2);
1545
}
1546
1547
/*
1548
* Reset the canvas's origin (this is a no-op unless confine
1549
* mode has just been turned on or the scroll region has changed).
1550
*/
1551
1552
CanvasSetOrigin(canvasPtr, canvasPtr->xOrigin, canvasPtr->yOrigin);
1553
canvasPtr->flags |= UPDATE_SCROLLBARS|REDRAW_BORDERS;
1554
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
1555
canvasPtr->xOrigin, canvasPtr->yOrigin,
1556
canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin),
1557
canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin));
1558
return TCL_OK;
1559
}
1560
1561
/*
1562
*--------------------------------------------------------------
1563
*
1564
* DisplayCanvas --
1565
*
1566
* This procedure redraws the contents of a canvas window.
1567
* It is invoked as a do-when-idle handler, so it only runs
1568
* when there's nothing else for the application to do.
1569
*
1570
* Results:
1571
* None.
1572
*
1573
* Side effects:
1574
* Information appears on the screen.
1575
*
1576
*--------------------------------------------------------------
1577
*/
1578
1579
static void
1580
DisplayCanvas(clientData)
1581
ClientData clientData; /* Information about widget. */
1582
{
1583
TkCanvas *canvasPtr = (TkCanvas *) clientData;
1584
Tk_Window tkwin = canvasPtr->tkwin;
1585
Tk_Item *itemPtr;
1586
Pixmap pixmap;
1587
int screenX1, screenX2, screenY1, screenY2, width, height;
1588
1589
if (canvasPtr->tkwin == NULL) {
1590
return;
1591
}
1592
if (!Tk_IsMapped(tkwin)) {
1593
goto done;
1594
}
1595
1596
/*
1597
* Choose a new current item if that is needed (this could cause
1598
* event handlers to be invoked).
1599
*/
1600
1601
while (canvasPtr->flags & REPICK_NEEDED) {
1602
Tcl_Preserve((ClientData) canvasPtr);
1603
canvasPtr->flags &= ~REPICK_NEEDED;
1604
PickCurrentItem(canvasPtr, &canvasPtr->pickEvent);
1605
tkwin = canvasPtr->tkwin;
1606
Tcl_Release((ClientData) canvasPtr);
1607
if (tkwin == NULL) {
1608
return;
1609
}
1610
}
1611
1612
/*
1613
* Compute the intersection between the area that needs redrawing
1614
* and the area that's visible on the screen.
1615
*/
1616
1617
if ((canvasPtr->redrawX1 < canvasPtr->redrawX2)
1618
&& (canvasPtr->redrawY1 < canvasPtr->redrawY2)) {
1619
screenX1 = canvasPtr->xOrigin + canvasPtr->inset;
1620
screenY1 = canvasPtr->yOrigin + canvasPtr->inset;
1621
screenX2 = canvasPtr->xOrigin + Tk_Width(tkwin) - canvasPtr->inset;
1622
screenY2 = canvasPtr->yOrigin + Tk_Height(tkwin) - canvasPtr->inset;
1623
if (canvasPtr->redrawX1 > screenX1) {
1624
screenX1 = canvasPtr->redrawX1;
1625
}
1626
if (canvasPtr->redrawY1 > screenY1) {
1627
screenY1 = canvasPtr->redrawY1;
1628
}
1629
if (canvasPtr->redrawX2 < screenX2) {
1630
screenX2 = canvasPtr->redrawX2;
1631
}
1632
if (canvasPtr->redrawY2 < screenY2) {
1633
screenY2 = canvasPtr->redrawY2;
1634
}
1635
if ((screenX1 >= screenX2) || (screenY1 >= screenY2)) {
1636
goto borders;
1637
}
1638
1639
/*
1640
* Redrawing is done in a temporary pixmap that is allocated
1641
* here and freed at the end of the procedure. All drawing
1642
* is done to the pixmap, and the pixmap is copied to the
1643
* screen at the end of the procedure. The temporary pixmap
1644
* serves two purposes:
1645
*
1646
* 1. It provides a smoother visual effect (no clearing and
1647
* gradual redraw will be visible to users).
1648
* 2. It allows us to redraw only the objects that overlap
1649
* the redraw area. Otherwise incorrect results could
1650
* occur from redrawing things that stick outside of
1651
* the redraw area (we'd have to redraw everything in
1652
* order to make the overlaps look right).
1653
*
1654
* Some tricky points about the pixmap:
1655
*
1656
* 1. We only allocate a large enough pixmap to hold the
1657
* area that has to be redisplayed. This saves time in
1658
* in the X server for large objects that cover much
1659
* more than the area being redisplayed: only the area
1660
* of the pixmap will actually have to be redrawn.
1661
* 2. Some X servers (e.g. the one for DECstations) have troubles
1662
* with characters that overlap an edge of the pixmap (on the
1663
* DEC servers, as of 8/18/92, such characters are drawn one
1664
* pixel too far to the right). To handle this problem,
1665
* make the pixmap a bit larger than is absolutely needed
1666
* so that for normal-sized fonts the characters that overlap
1667
* the edge of the pixmap will be outside the area we care
1668
* about.
1669
*/
1670
1671
canvasPtr->drawableXOrigin = screenX1 - 30;
1672
canvasPtr->drawableYOrigin = screenY1 - 30;
1673
pixmap = Tk_GetPixmap(Tk_Display(tkwin), Tk_WindowId(tkwin),
1674
(screenX2 + 30 - canvasPtr->drawableXOrigin),
1675
(screenY2 + 30 - canvasPtr->drawableYOrigin),
1676
Tk_Depth(tkwin));
1677
1678
/*
1679
* Clear the area to be redrawn.
1680
*/
1681
1682
width = screenX2 - screenX1;
1683
height = screenY2 - screenY1;
1684
1685
XFillRectangle(Tk_Display(tkwin), pixmap, canvasPtr->pixmapGC,
1686
screenX1 - canvasPtr->drawableXOrigin,
1687
screenY1 - canvasPtr->drawableYOrigin, (unsigned int) width,
1688
(unsigned int) height);
1689
1690
/*
1691
* Scan through the item list, redrawing those items that need it.
1692
* An item must be redraw if either (a) it intersects the smaller
1693
* on-screen area or (b) it intersects the full canvas area and its
1694
* type requests that it be redrawn always (e.g. so subwindows can
1695
* be unmapped when they move off-screen).
1696
*/
1697
1698
for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
1699
itemPtr = itemPtr->nextPtr) {
1700
if ((itemPtr->x1 >= screenX2)
1701
|| (itemPtr->y1 >= screenY2)
1702
|| (itemPtr->x2 < screenX1)
1703
|| (itemPtr->y2 < screenY1)) {
1704
if (!itemPtr->typePtr->alwaysRedraw
1705
|| (itemPtr->x1 >= canvasPtr->redrawX2)
1706
|| (itemPtr->y1 >= canvasPtr->redrawY2)
1707
|| (itemPtr->x2 < canvasPtr->redrawX1)
1708
|| (itemPtr->y2 < canvasPtr->redrawY1)) {
1709
continue;
1710
}
1711
}
1712
(*itemPtr->typePtr->displayProc)((Tk_Canvas) canvasPtr, itemPtr,
1713
canvasPtr->display, pixmap, screenX1, screenY1, width,
1714
height);
1715
}
1716
1717
/*
1718
* Copy from the temporary pixmap to the screen, then free up
1719
* the temporary pixmap.
1720
*/
1721
1722
XCopyArea(Tk_Display(tkwin), pixmap, Tk_WindowId(tkwin),
1723
canvasPtr->pixmapGC,
1724
screenX1 - canvasPtr->drawableXOrigin,
1725
screenY1 - canvasPtr->drawableYOrigin,
1726
(unsigned) (screenX2 - screenX1),
1727
(unsigned) (screenY2 - screenY1),
1728
screenX1 - canvasPtr->xOrigin, screenY1 - canvasPtr->yOrigin);
1729
Tk_FreePixmap(Tk_Display(tkwin), pixmap);
1730
}
1731
1732
/*
1733
* Draw the window borders, if needed.
1734
*/
1735
1736
borders:
1737
if (canvasPtr->flags & REDRAW_BORDERS) {
1738
canvasPtr->flags &= ~REDRAW_BORDERS;
1739
if (canvasPtr->borderWidth > 0) {
1740
Tk_Draw3DRectangle(tkwin, Tk_WindowId(tkwin),
1741
canvasPtr->bgBorder, canvasPtr->highlightWidth,
1742
canvasPtr->highlightWidth,
1743
Tk_Width(tkwin) - 2*canvasPtr->highlightWidth,
1744
Tk_Height(tkwin) - 2*canvasPtr->highlightWidth,
1745
canvasPtr->borderWidth, canvasPtr->relief);
1746
}
1747
if (canvasPtr->highlightWidth != 0) {
1748
GC gc;
1749
1750
if (canvasPtr->textInfo.gotFocus) {
1751
gc = Tk_GCForColor(canvasPtr->highlightColorPtr,
1752
Tk_WindowId(tkwin));
1753
} else {
1754
gc = Tk_GCForColor(canvasPtr->highlightBgColorPtr,
1755
Tk_WindowId(tkwin));
1756
}
1757
Tk_DrawFocusHighlight(tkwin, gc, canvasPtr->highlightWidth,
1758
Tk_WindowId(tkwin));
1759
}
1760
}
1761
1762
done:
1763
canvasPtr->flags &= ~REDRAW_PENDING;
1764
canvasPtr->redrawX1 = canvasPtr->redrawX2 = 0;
1765
canvasPtr->redrawY1 = canvasPtr->redrawY2 = 0;
1766
if (canvasPtr->flags & UPDATE_SCROLLBARS) {
1767
CanvasUpdateScrollbars(canvasPtr);
1768
}
1769
}
1770
1771
/*
1772
*--------------------------------------------------------------
1773
*
1774
* CanvasEventProc --
1775
*
1776
* This procedure is invoked by the Tk dispatcher for various
1777
* events on canvases.
1778
*
1779
* Results:
1780
* None.
1781
*
1782
* Side effects:
1783
* When the window gets deleted, internal structures get
1784
* cleaned up. When it gets exposed, it is redisplayed.
1785
*
1786
*--------------------------------------------------------------
1787
*/
1788
1789
static void
1790
CanvasEventProc(clientData, eventPtr)
1791
ClientData clientData; /* Information about window. */
1792
XEvent *eventPtr; /* Information about event. */
1793
{
1794
TkCanvas *canvasPtr = (TkCanvas *) clientData;
1795
1796
if (eventPtr->type == Expose) {
1797
int x, y;
1798
1799
x = eventPtr->xexpose.x + canvasPtr->xOrigin;
1800
y = eventPtr->xexpose.y + canvasPtr->yOrigin;
1801
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr, x, y,
1802
x + eventPtr->xexpose.width,
1803
y + eventPtr->xexpose.height);
1804
if ((eventPtr->xexpose.x < canvasPtr->inset)
1805
|| (eventPtr->xexpose.y < canvasPtr->inset)
1806
|| ((eventPtr->xexpose.x + eventPtr->xexpose.width)
1807
> (Tk_Width(canvasPtr->tkwin) - canvasPtr->inset))
1808
|| ((eventPtr->xexpose.y + eventPtr->xexpose.height)
1809
> (Tk_Height(canvasPtr->tkwin) - canvasPtr->inset))) {
1810
canvasPtr->flags |= REDRAW_BORDERS;
1811
}
1812
} else if (eventPtr->type == DestroyNotify) {
1813
if (canvasPtr->tkwin != NULL) {
1814
canvasPtr->tkwin = NULL;
1815
Tcl_DeleteCommand(canvasPtr->interp,
1816
Tcl_GetCommandName(canvasPtr->interp,
1817
canvasPtr->widgetCmd));
1818
}
1819
if (canvasPtr->flags & REDRAW_PENDING) {
1820
Tcl_CancelIdleCall(DisplayCanvas, (ClientData) canvasPtr);
1821
}
1822
Tcl_EventuallyFree((ClientData) canvasPtr, DestroyCanvas);
1823
} else if (eventPtr->type == ConfigureNotify) {
1824
canvasPtr->flags |= UPDATE_SCROLLBARS;
1825
1826
/*
1827
* The call below is needed in order to recenter the canvas if
1828
* it's confined and its scroll region is smaller than the window.
1829
*/
1830
1831
CanvasSetOrigin(canvasPtr, canvasPtr->xOrigin, canvasPtr->yOrigin);
1832
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr, canvasPtr->xOrigin,
1833
canvasPtr->yOrigin,
1834
canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin),
1835
canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin));
1836
canvasPtr->flags |= REDRAW_BORDERS;
1837
} else if (eventPtr->type == FocusIn) {
1838
if (eventPtr->xfocus.detail != NotifyInferior) {
1839
CanvasFocusProc(canvasPtr, 1);
1840
}
1841
} else if (eventPtr->type == FocusOut) {
1842
if (eventPtr->xfocus.detail != NotifyInferior) {
1843
CanvasFocusProc(canvasPtr, 0);
1844
}
1845
} else if (eventPtr->type == UnmapNotify) {
1846
Tk_Item *itemPtr;
1847
1848
/*
1849
* Special hack: if the canvas is unmapped, then must notify
1850
* all items with "alwaysRedraw" set, so that they know that
1851
* they are no longer displayed.
1852
*/
1853
1854
for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
1855
itemPtr = itemPtr->nextPtr) {
1856
if (itemPtr->typePtr->alwaysRedraw) {
1857
(*itemPtr->typePtr->displayProc)((Tk_Canvas) canvasPtr,
1858
itemPtr, canvasPtr->display, None, 0, 0, 0, 0);
1859
}
1860
}
1861
}
1862
}
1863
1864
/*
1865
*----------------------------------------------------------------------
1866
*
1867
* CanvasCmdDeletedProc --
1868
*
1869
* This procedure is invoked when a widget command is deleted. If
1870
* the widget isn't already in the process of being destroyed,
1871
* this command destroys it.
1872
*
1873
* Results:
1874
* None.
1875
*
1876
* Side effects:
1877
* The widget is destroyed.
1878
*
1879
*----------------------------------------------------------------------
1880
*/
1881
1882
static void
1883
CanvasCmdDeletedProc(clientData)
1884
ClientData clientData; /* Pointer to widget record for widget. */
1885
{
1886
TkCanvas *canvasPtr = (TkCanvas *) clientData;
1887
Tk_Window tkwin = canvasPtr->tkwin;
1888
1889
/*
1890
* This procedure could be invoked either because the window was
1891
* destroyed and the command was then deleted (in which case tkwin
1892
* is NULL) or because the command was deleted, and then this procedure
1893
* destroys the widget.
1894
*/
1895
1896
if (tkwin != NULL) {
1897
canvasPtr->tkwin = NULL;
1898
Tk_DestroyWindow(tkwin);
1899
}
1900
}
1901
1902
/*
1903
*--------------------------------------------------------------
1904
*
1905
* Tk_CanvasEventuallyRedraw --
1906
*
1907
* Arrange for part or all of a canvas widget to redrawn at
1908
* some convenient time in the future.
1909
*
1910
* Results:
1911
* None.
1912
*
1913
* Side effects:
1914
* The screen will eventually be refreshed.
1915
*
1916
*--------------------------------------------------------------
1917
*/
1918
1919
void
1920
Tk_CanvasEventuallyRedraw(canvas, x1, y1, x2, y2)
1921
Tk_Canvas canvas; /* Information about widget. */
1922
int x1, y1; /* Upper left corner of area to redraw.
1923
* Pixels on edge are redrawn. */
1924
int x2, y2; /* Lower right corner of area to redraw.
1925
* Pixels on edge are not redrawn. */
1926
{
1927
TkCanvas *canvasPtr = (TkCanvas *) canvas;
1928
if ((x1 == x2) || (y1 == y2)) {
1929
return;
1930
}
1931
if (canvasPtr->flags & REDRAW_PENDING) {
1932
if (x1 <= canvasPtr->redrawX1) {
1933
canvasPtr->redrawX1 = x1;
1934
}
1935
if (y1 <= canvasPtr->redrawY1) {
1936
canvasPtr->redrawY1 = y1;
1937
}
1938
if (x2 >= canvasPtr->redrawX2) {
1939
canvasPtr->redrawX2 = x2;
1940
}
1941
if (y2 >= canvasPtr->redrawY2) {
1942
canvasPtr->redrawY2 = y2;
1943
}
1944
} else {
1945
canvasPtr->redrawX1 = x1;
1946
canvasPtr->redrawY1 = y1;
1947
canvasPtr->redrawX2 = x2;
1948
canvasPtr->redrawY2 = y2;
1949
Tcl_DoWhenIdle(DisplayCanvas, (ClientData) canvasPtr);
1950
canvasPtr->flags |= REDRAW_PENDING;
1951
}
1952
}
1953
1954
/*
1955
*--------------------------------------------------------------
1956
*
1957
* Tk_CreateItemType --
1958
*
1959
* This procedure may be invoked to add a new kind of canvas
1960
* element to the core item types supported by Tk.
1961
*
1962
* Results:
1963
* None.
1964
*
1965
* Side effects:
1966
* From now on, the new item type will be useable in canvas
1967
* widgets (e.g. typePtr->name can be used as the item type
1968
* in "create" widget commands). If there was already a
1969
* type with the same name as in typePtr, it is replaced with
1970
* the new type.
1971
*
1972
*--------------------------------------------------------------
1973
*/
1974
1975
void
1976
Tk_CreateItemType(typePtr)
1977
Tk_ItemType *typePtr; /* Information about item type;
1978
* storage must be statically
1979
* allocated (must live forever). */
1980
{
1981
Tk_ItemType *typePtr2, *prevPtr;
1982
1983
if (typeList == NULL) {
1984
InitCanvas();
1985
}
1986
1987
/*
1988
* If there's already an item type with the given name, remove it.
1989
*/
1990
1991
for (typePtr2 = typeList, prevPtr = NULL; typePtr2 != NULL;
1992
prevPtr = typePtr2, typePtr2 = typePtr2->nextPtr) {
1993
if (strcmp(typePtr2->name, typePtr->name) == 0) {
1994
if (prevPtr == NULL) {
1995
typeList = typePtr2->nextPtr;
1996
} else {
1997
prevPtr->nextPtr = typePtr2->nextPtr;
1998
}
1999
break;
2000
}
2001
}
2002
typePtr->nextPtr = typeList;
2003
typeList = typePtr;
2004
}
2005
2006
/*
2007
*----------------------------------------------------------------------
2008
*
2009
* Tk_GetItemTypes --
2010
*
2011
* This procedure returns a pointer to the list of all item
2012
* types.
2013
*
2014
* Results:
2015
* The return value is a pointer to the first in the list
2016
* of item types currently supported by canvases.
2017
*
2018
* Side effects:
2019
* None.
2020
*
2021
*----------------------------------------------------------------------
2022
*/
2023
2024
Tk_ItemType *
2025
Tk_GetItemTypes()
2026
{
2027
if (typeList == NULL) {
2028
InitCanvas();
2029
}
2030
return typeList;
2031
}
2032
2033
/*
2034
*--------------------------------------------------------------
2035
*
2036
* InitCanvas --
2037
*
2038
* This procedure is invoked to perform once-only-ever
2039
* initialization for the module, such as setting up
2040
* the type table.
2041
*
2042
* Results:
2043
* None.
2044
*
2045
* Side effects:
2046
* None.
2047
*
2048
*--------------------------------------------------------------
2049
*/
2050
2051
static void
2052
InitCanvas()
2053
{
2054
if (typeList != NULL) {
2055
return;
2056
}
2057
typeList = &tkRectangleType;
2058
tkRectangleType.nextPtr = &tkTextType;
2059
tkTextType.nextPtr = &tkLineType;
2060
tkLineType.nextPtr = &tkPolygonType;
2061
tkPolygonType.nextPtr = &tkImageType;
2062
tkImageType.nextPtr = &tkOvalType;
2063
tkOvalType.nextPtr = &tkBitmapType;
2064
tkBitmapType.nextPtr = &tkArcType;
2065
tkArcType.nextPtr = &tkWindowType;
2066
tkWindowType.nextPtr = NULL;
2067
allUid = Tk_GetUid("all");
2068
currentUid = Tk_GetUid("current");
2069
}
2070
2071
/*
2072
*--------------------------------------------------------------
2073
*
2074
* StartTagSearch --
2075
*
2076
* This procedure is called to initiate an enumeration of
2077
* all items in a given canvas that contain a given tag.
2078
*
2079
* Results:
2080
* The return value is a pointer to the first item in
2081
* canvasPtr that matches tag, or NULL if there is no
2082
* such item. The information at *searchPtr is initialized
2083
* such that successive calls to NextItem will return
2084
* successive items that match tag.
2085
*
2086
* Side effects:
2087
* SearchPtr is linked into a list of searches in progress
2088
* on canvasPtr, so that elements can safely be deleted
2089
* while the search is in progress. EndTagSearch must be
2090
* called at the end of the search to unlink searchPtr from
2091
* this list.
2092
*
2093
*--------------------------------------------------------------
2094
*/
2095
2096
static Tk_Item *
2097
StartTagSearch(canvasPtr, tag, searchPtr)
2098
TkCanvas *canvasPtr; /* Canvas whose items are to be
2099
* searched. */
2100
char *tag; /* String giving tag value. */
2101
TagSearch *searchPtr; /* Record describing tag search;
2102
* will be initialized here. */
2103
{
2104
int id;
2105
Tk_Item *itemPtr, *prevPtr;
2106
Tk_Uid *tagPtr;
2107
Tk_Uid uid;
2108
int count;
2109
2110
/*
2111
* Initialize the search.
2112
*/
2113
2114
searchPtr->canvasPtr = canvasPtr;
2115
searchPtr->searchOver = 0;
2116
2117
/*
2118
* Find the first matching item in one of several ways. If the tag
2119
* is a number then it selects the single item with the matching
2120
* identifier. In this case see if the item being requested is the
2121
* hot item, in which case the search can be skipped.
2122
*/
2123
2124
if (isdigit(UCHAR(*tag))) {
2125
char *end;
2126
2127
numIdSearches++;
2128
id = strtoul(tag, &end, 0);
2129
if (*end == 0) {
2130
itemPtr = canvasPtr->hotPtr;
2131
prevPtr = canvasPtr->hotPrevPtr;
2132
if ((itemPtr == NULL) || (itemPtr->id != id) || (prevPtr == NULL)
2133
|| (prevPtr->nextPtr != itemPtr)) {
2134
numSlowSearches++;
2135
for (prevPtr = NULL, itemPtr = canvasPtr->firstItemPtr;
2136
itemPtr != NULL;
2137
prevPtr = itemPtr, itemPtr = itemPtr->nextPtr) {
2138
if (itemPtr->id == id) {
2139
break;
2140
}
2141
}
2142
}
2143
searchPtr->prevPtr = prevPtr;
2144
searchPtr->searchOver = 1;
2145
canvasPtr->hotPtr = itemPtr;
2146
canvasPtr->hotPrevPtr = prevPtr;
2147
return itemPtr;
2148
}
2149
}
2150
2151
searchPtr->tag = uid = Tk_GetUid(tag);
2152
if (uid == allUid) {
2153
2154
/*
2155
* All items match.
2156
*/
2157
2158
searchPtr->tag = NULL;
2159
searchPtr->prevPtr = NULL;
2160
searchPtr->currentPtr = canvasPtr->firstItemPtr;
2161
return canvasPtr->firstItemPtr;
2162
}
2163
2164
/*
2165
* None of the above. Search for an item with a matching tag.
2166
*/
2167
2168
for (prevPtr = NULL, itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
2169
prevPtr = itemPtr, itemPtr = itemPtr->nextPtr) {
2170
for (tagPtr = itemPtr->tagPtr, count = itemPtr->numTags;
2171
count > 0; tagPtr++, count--) {
2172
if (*tagPtr == uid) {
2173
searchPtr->prevPtr = prevPtr;
2174
searchPtr->currentPtr = itemPtr;
2175
return itemPtr;
2176
}
2177
}
2178
}
2179
searchPtr->prevPtr = prevPtr;
2180
searchPtr->searchOver = 1;
2181
return NULL;
2182
}
2183
2184
/*
2185
*--------------------------------------------------------------
2186
*
2187
* NextItem --
2188
*
2189
* This procedure returns successive items that match a given
2190
* tag; it should be called only after StartTagSearch has been
2191
* used to begin a search.
2192
*
2193
* Results:
2194
* The return value is a pointer to the next item that matches
2195
* the tag specified to StartTagSearch, or NULL if no such
2196
* item exists. *SearchPtr is updated so that the next call
2197
* to this procedure will return the next item.
2198
*
2199
* Side effects:
2200
* None.
2201
*
2202
*--------------------------------------------------------------
2203
*/
2204
2205
static Tk_Item *
2206
NextItem(searchPtr)
2207
TagSearch *searchPtr; /* Record describing search in
2208
* progress. */
2209
{
2210
Tk_Item *itemPtr, *prevPtr;
2211
int count;
2212
Tk_Uid uid;
2213
Tk_Uid *tagPtr;
2214
2215
/*
2216
* Find next item in list (this may not actually be a suitable
2217
* one to return), and return if there are no items left.
2218
*/
2219
2220
prevPtr = searchPtr->prevPtr;
2221
if (prevPtr == NULL) {
2222
itemPtr = searchPtr->canvasPtr->firstItemPtr;
2223
} else {
2224
itemPtr = prevPtr->nextPtr;
2225
}
2226
if ((itemPtr == NULL) || (searchPtr->searchOver)) {
2227
searchPtr->searchOver = 1;
2228
return NULL;
2229
}
2230
if (itemPtr != searchPtr->currentPtr) {
2231
/*
2232
* The structure of the list has changed. Probably the
2233
* previously-returned item was removed from the list.
2234
* In this case, don't advance prevPtr; just return
2235
* its new successor (i.e. do nothing here).
2236
*/
2237
} else {
2238
prevPtr = itemPtr;
2239
itemPtr = prevPtr->nextPtr;
2240
}
2241
2242
/*
2243
* Handle special case of "all" search by returning next item.
2244
*/
2245
2246
uid = searchPtr->tag;
2247
if (uid == NULL) {
2248
searchPtr->prevPtr = prevPtr;
2249
searchPtr->currentPtr = itemPtr;
2250
return itemPtr;
2251
}
2252
2253
/*
2254
* Look for an item with a particular tag.
2255
*/
2256
2257
for ( ; itemPtr != NULL; prevPtr = itemPtr, itemPtr = itemPtr->nextPtr) {
2258
for (tagPtr = itemPtr->tagPtr, count = itemPtr->numTags;
2259
count > 0; tagPtr++, count--) {
2260
if (*tagPtr == uid) {
2261
searchPtr->prevPtr = prevPtr;
2262
searchPtr->currentPtr = itemPtr;
2263
return itemPtr;
2264
}
2265
}
2266
}
2267
searchPtr->prevPtr = prevPtr;
2268
searchPtr->searchOver = 1;
2269
return NULL;
2270
}
2271
2272
/*
2273
*--------------------------------------------------------------
2274
*
2275
* DoItem --
2276
*
2277
* This is a utility procedure called by FindItems. It
2278
* either adds itemPtr's id to the result forming in interp,
2279
* or it adds a new tag to itemPtr, depending on the value
2280
* of tag.
2281
*
2282
* Results:
2283
* None.
2284
*
2285
* Side effects:
2286
* If tag is NULL then itemPtr's id is added as a list element
2287
* to interp->result; otherwise tag is added to itemPtr's
2288
* list of tags.
2289
*
2290
*--------------------------------------------------------------
2291
*/
2292
2293
static void
2294
DoItem(interp, itemPtr, tag)
2295
Tcl_Interp *interp; /* Interpreter in which to (possibly)
2296
* record item id. */
2297
Tk_Item *itemPtr; /* Item to (possibly) modify. */
2298
Tk_Uid tag; /* Tag to add to those already
2299
* present for item, or NULL. */
2300
{
2301
Tk_Uid *tagPtr;
2302
int count;
2303
2304
/*
2305
* Handle the "add-to-result" case and return, if appropriate.
2306
*/
2307
2308
if (tag == NULL) {
2309
char msg[30];
2310
sprintf(msg, "%d", itemPtr->id);
2311
Tcl_AppendElement(interp, msg);
2312
return;
2313
}
2314
2315
for (tagPtr = itemPtr->tagPtr, count = itemPtr->numTags;
2316
count > 0; tagPtr++, count--) {
2317
if (tag == *tagPtr) {
2318
return;
2319
}
2320
}
2321
2322
/*
2323
* Grow the tag space if there's no more room left in the current
2324
* block.
2325
*/
2326
2327
if (itemPtr->tagSpace == itemPtr->numTags) {
2328
Tk_Uid *newTagPtr;
2329
2330
itemPtr->tagSpace += 5;
2331
newTagPtr = (Tk_Uid *) ckalloc((unsigned)
2332
(itemPtr->tagSpace * sizeof(Tk_Uid)));
2333
memcpy((VOID *) newTagPtr, (VOID *) itemPtr->tagPtr,
2334
(itemPtr->numTags * sizeof(Tk_Uid)));
2335
if (itemPtr->tagPtr != itemPtr->staticTagSpace) {
2336
ckfree((char *) itemPtr->tagPtr);
2337
}
2338
itemPtr->tagPtr = newTagPtr;
2339
tagPtr = &itemPtr->tagPtr[itemPtr->numTags];
2340
}
2341
2342
/*
2343
* Add in the new tag.
2344
*/
2345
2346
*tagPtr = tag;
2347
itemPtr->numTags++;
2348
}
2349
2350
/*
2351
*--------------------------------------------------------------
2352
*
2353
* FindItems --
2354
*
2355
* This procedure does all the work of implementing the
2356
* "find" and "addtag" options of the canvas widget command,
2357
* which locate items that have certain features (location,
2358
* tags, position in display list, etc.).
2359
*
2360
* Results:
2361
* A standard Tcl return value. If newTag is NULL, then a
2362
* list of ids from all the items that match argc/argv is
2363
* returned in interp->result. If newTag is NULL, then
2364
* the normal interp->result is an empty string. If an error
2365
* occurs, then interp->result will hold an error message.
2366
*
2367
* Side effects:
2368
* If newTag is non-NULL, then all the items that match the
2369
* information in argc/argv have that tag added to their
2370
* lists of tags.
2371
*
2372
*--------------------------------------------------------------
2373
*/
2374
2375
static int
2376
FindItems(interp, canvasPtr, argc, argv, newTag, cmdName, option)
2377
Tcl_Interp *interp; /* Interpreter for error reporting. */
2378
TkCanvas *canvasPtr; /* Canvas whose items are to be
2379
* searched. */
2380
int argc; /* Number of entries in argv. Must be
2381
* greater than zero. */
2382
char **argv; /* Arguments that describe what items
2383
* to search for (see user doc on
2384
* "find" and "addtag" options). */
2385
char *newTag; /* If non-NULL, gives new tag to set
2386
* on all found items; if NULL, then
2387
* ids of found items are returned
2388
* in interp->result. */
2389
char *cmdName; /* Name of original Tcl command, for
2390
* use in error messages. */
2391
char *option; /* For error messages: gives option
2392
* from Tcl command and other stuff
2393
* up to what's in argc/argv. */
2394
{
2395
int c;
2396
size_t length;
2397
TagSearch search;
2398
Tk_Item *itemPtr;
2399
Tk_Uid uid;
2400
2401
if (newTag != NULL) {
2402
uid = Tk_GetUid(newTag);
2403
} else {
2404
uid = NULL;
2405
}
2406
c = argv[0][0];
2407
length = strlen(argv[0]);
2408
if ((c == 'a') && (strncmp(argv[0], "above", length) == 0)
2409
&& (length >= 2)) {
2410
Tk_Item *lastPtr = NULL;
2411
if (argc != 2) {
2412
Tcl_AppendResult(interp, "wrong # args: should be \"",
2413
cmdName, option, " above tagOrId", (char *) NULL);
2414
return TCL_ERROR;
2415
}
2416
for (itemPtr = StartTagSearch(canvasPtr, argv[1], &search);
2417
itemPtr != NULL; itemPtr = NextItem(&search)) {
2418
lastPtr = itemPtr;
2419
}
2420
if ((lastPtr != NULL) && (lastPtr->nextPtr != NULL)) {
2421
DoItem(interp, lastPtr->nextPtr, uid);
2422
}
2423
} else if ((c == 'a') && (strncmp(argv[0], "all", length) == 0)
2424
&& (length >= 2)) {
2425
if (argc != 1) {
2426
Tcl_AppendResult(interp, "wrong # args: should be \"",
2427
cmdName, option, " all", (char *) NULL);
2428
return TCL_ERROR;
2429
}
2430
2431
for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
2432
itemPtr = itemPtr->nextPtr) {
2433
DoItem(interp, itemPtr, uid);
2434
}
2435
} else if ((c == 'b') && (strncmp(argv[0], "below", length) == 0)) {
2436
if (argc != 2) {
2437
Tcl_AppendResult(interp, "wrong # args: should be \"",
2438
cmdName, option, " below tagOrId", (char *) NULL);
2439
return TCL_ERROR;
2440
}
2441
itemPtr = StartTagSearch(canvasPtr, argv[1], &search);
2442
if (search.prevPtr != NULL) {
2443
DoItem(interp, search.prevPtr, uid);
2444
}
2445
} else if ((c == 'c') && (strncmp(argv[0], "closest", length) == 0)) {
2446
double closestDist;
2447
Tk_Item *startPtr, *closestPtr;
2448
double coords[2], halo;
2449
int x1, y1, x2, y2;
2450
2451
if ((argc < 3) || (argc > 5)) {
2452
Tcl_AppendResult(interp, "wrong # args: should be \"",
2453
cmdName, option, " closest x y ?halo? ?start?",
2454
(char *) NULL);
2455
return TCL_ERROR;
2456
}
2457
if ((Tk_CanvasGetCoord(interp, (Tk_Canvas) canvasPtr, argv[1],
2458
&coords[0]) != TCL_OK) || (Tk_CanvasGetCoord(interp,
2459
(Tk_Canvas) canvasPtr, argv[2], &coords[1]) != TCL_OK)) {
2460
return TCL_ERROR;
2461
}
2462
if (argc > 3) {
2463
if (Tk_CanvasGetCoord(interp, (Tk_Canvas) canvasPtr, argv[3],
2464
&halo) != TCL_OK) {
2465
return TCL_ERROR;
2466
}
2467
if (halo < 0.0) {
2468
Tcl_AppendResult(interp, "can't have negative halo value \"",
2469
argv[3], "\"", (char *) NULL);
2470
return TCL_ERROR;
2471
}
2472
} else {
2473
halo = 0.0;
2474
}
2475
2476
/*
2477
* Find the item at which to start the search.
2478
*/
2479
2480
startPtr = canvasPtr->firstItemPtr;
2481
if (argc == 5) {
2482
itemPtr = StartTagSearch(canvasPtr, argv[4], &search);
2483
if (itemPtr != NULL) {
2484
startPtr = itemPtr;
2485
}
2486
}
2487
2488
/*
2489
* The code below is optimized so that it can eliminate most
2490
* items without having to call their item-specific procedures.
2491
* This is done by keeping a bounding box (x1, y1, x2, y2) that
2492
* an item's bbox must overlap if the item is to have any
2493
* chance of being closer than the closest so far.
2494
*/
2495
2496
itemPtr = startPtr;
2497
if (itemPtr == NULL) {
2498
return TCL_OK;
2499
}
2500
closestDist = (*itemPtr->typePtr->pointProc)((Tk_Canvas) canvasPtr,
2501
itemPtr, coords) - halo;
2502
if (closestDist < 0.0) {
2503
closestDist = 0.0;
2504
}
2505
while (1) {
2506
double newDist;
2507
2508
/*
2509
* Update the bounding box using itemPtr, which is the
2510
* new closest item.
2511
*/
2512
2513
x1 = (coords[0] - closestDist - halo - 1);
2514
y1 = (coords[1] - closestDist - halo - 1);
2515
x2 = (coords[0] + closestDist + halo + 1);
2516
y2 = (coords[1] + closestDist + halo + 1);
2517
closestPtr = itemPtr;
2518
2519
/*
2520
* Search for an item that beats the current closest one.
2521
* Work circularly through the canvas's item list until
2522
* getting back to the starting item.
2523
*/
2524
2525
while (1) {
2526
itemPtr = itemPtr->nextPtr;
2527
if (itemPtr == NULL) {
2528
itemPtr = canvasPtr->firstItemPtr;
2529
}
2530
if (itemPtr == startPtr) {
2531
DoItem(interp, closestPtr, uid);
2532
return TCL_OK;
2533
}
2534
if ((itemPtr->x1 >= x2) || (itemPtr->x2 <= x1)
2535
|| (itemPtr->y1 >= y2) || (itemPtr->y2 <= y1)) {
2536
continue;
2537
}
2538
newDist = (*itemPtr->typePtr->pointProc)((Tk_Canvas) canvasPtr,
2539
itemPtr, coords) - halo;
2540
if (newDist < 0.0) {
2541
newDist = 0.0;
2542
}
2543
if (newDist <= closestDist) {
2544
closestDist = newDist;
2545
break;
2546
}
2547
}
2548
}
2549
} else if ((c == 'e') && (strncmp(argv[0], "enclosed", length) == 0)) {
2550
if (argc != 5) {
2551
Tcl_AppendResult(interp, "wrong # args: should be \"",
2552
cmdName, option, " enclosed x1 y1 x2 y2", (char *) NULL);
2553
return TCL_ERROR;
2554
}
2555
return FindArea(interp, canvasPtr, argv+1, uid, 1);
2556
} else if ((c == 'o') && (strncmp(argv[0], "overlapping", length) == 0)) {
2557
if (argc != 5) {
2558
Tcl_AppendResult(interp, "wrong # args: should be \"",
2559
cmdName, option, " overlapping x1 y1 x2 y2",
2560
(char *) NULL);
2561
return TCL_ERROR;
2562
}
2563
return FindArea(interp, canvasPtr, argv+1, uid, 0);
2564
} else if ((c == 'w') && (strncmp(argv[0], "withtag", length) == 0)) {
2565
if (argc != 2) {
2566
Tcl_AppendResult(interp, "wrong # args: should be \"",
2567
cmdName, option, " withtag tagOrId", (char *) NULL);
2568
return TCL_ERROR;
2569
}
2570
for (itemPtr = StartTagSearch(canvasPtr, argv[1], &search);
2571
itemPtr != NULL; itemPtr = NextItem(&search)) {
2572
DoItem(interp, itemPtr, uid);
2573
}
2574
} else {
2575
Tcl_AppendResult(interp, "bad search command \"", argv[0],
2576
"\": must be above, all, below, closest, enclosed, ",
2577
"overlapping, or withtag", (char *) NULL);
2578
return TCL_ERROR;
2579
}
2580
return TCL_OK;
2581
}
2582
2583
/*
2584
*--------------------------------------------------------------
2585
*
2586
* FindArea --
2587
*
2588
* This procedure implements area searches for the "find"
2589
* and "addtag" options.
2590
*
2591
* Results:
2592
* A standard Tcl return value. If newTag is NULL, then a
2593
* list of ids from all the items overlapping or enclosed
2594
* by the rectangle given by argc is returned in interp->result.
2595
* If newTag is NULL, then the normal interp->result is an
2596
* empty string. If an error occurs, then interp->result will
2597
* hold an error message.
2598
*
2599
* Side effects:
2600
* If uid is non-NULL, then all the items overlapping
2601
* or enclosed by the area in argv have that tag added to
2602
* their lists of tags.
2603
*
2604
*--------------------------------------------------------------
2605
*/
2606
2607
static int
2608
FindArea(interp, canvasPtr, argv, uid, enclosed)
2609
Tcl_Interp *interp; /* Interpreter for error reporting
2610
* and result storing. */
2611
TkCanvas *canvasPtr; /* Canvas whose items are to be
2612
* searched. */
2613
char **argv; /* Array of four arguments that
2614
* give the coordinates of the
2615
* rectangular area to search. */
2616
Tk_Uid uid; /* If non-NULL, gives new tag to set
2617
* on all found items; if NULL, then
2618
* ids of found items are returned
2619
* in interp->result. */
2620
int enclosed; /* 0 means overlapping or enclosed
2621
* items are OK, 1 means only enclosed
2622
* items are OK. */
2623
{
2624
double rect[4], tmp;
2625
int x1, y1, x2, y2;
2626
Tk_Item *itemPtr;
2627
2628
if ((Tk_CanvasGetCoord(interp, (Tk_Canvas) canvasPtr, argv[0],
2629
&rect[0]) != TCL_OK)
2630
|| (Tk_CanvasGetCoord(interp, (Tk_Canvas) canvasPtr, argv[1],
2631
&rect[1]) != TCL_OK)
2632
|| (Tk_CanvasGetCoord(interp, (Tk_Canvas) canvasPtr, argv[2],
2633
&rect[2]) != TCL_OK)
2634
|| (Tk_CanvasGetCoord(interp, (Tk_Canvas) canvasPtr, argv[3],
2635
&rect[3]) != TCL_OK)) {
2636
return TCL_ERROR;
2637
}
2638
if (rect[0] > rect[2]) {
2639
tmp = rect[0]; rect[0] = rect[2]; rect[2] = tmp;
2640
}
2641
if (rect[1] > rect[3]) {
2642
tmp = rect[1]; rect[1] = rect[3]; rect[3] = tmp;
2643
}
2644
2645
/*
2646
* Use an integer bounding box for a quick test, to avoid
2647
* calling item-specific code except for items that are close.
2648
*/
2649
2650
x1 = (rect[0]-1.0);
2651
y1 = (rect[1]-1.0);
2652
x2 = (rect[2]+1.0);
2653
y2 = (rect[3]+1.0);
2654
for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
2655
itemPtr = itemPtr->nextPtr) {
2656
if ((itemPtr->x1 >= x2) || (itemPtr->x2 <= x1)
2657
|| (itemPtr->y1 >= y2) || (itemPtr->y2 <= y1)) {
2658
continue;
2659
}
2660
if ((*itemPtr->typePtr->areaProc)((Tk_Canvas) canvasPtr, itemPtr, rect)
2661
>= enclosed) {
2662
DoItem(interp, itemPtr, uid);
2663
}
2664
}
2665
return TCL_OK;
2666
}
2667
2668
/*
2669
*--------------------------------------------------------------
2670
*
2671
* RelinkItems --
2672
*
2673
* Move one or more items to a different place in the
2674
* display order for a canvas.
2675
*
2676
* Results:
2677
* None.
2678
*
2679
* Side effects:
2680
* The items identified by "tag" are moved so that they
2681
* are all together in the display list and immediately
2682
* after prevPtr. The order of the moved items relative
2683
* to each other is not changed.
2684
*
2685
*--------------------------------------------------------------
2686
*/
2687
2688
static void
2689
RelinkItems(canvasPtr, tag, prevPtr)
2690
TkCanvas *canvasPtr; /* Canvas to be modified. */
2691
char *tag; /* Tag identifying items to be moved
2692
* in the redisplay list. */
2693
Tk_Item *prevPtr; /* Reposition the items so that they
2694
* go just after this item (NULL means
2695
* put at beginning of list). */
2696
{
2697
Tk_Item *itemPtr;
2698
TagSearch search;
2699
Tk_Item *firstMovePtr, *lastMovePtr;
2700
2701
/*
2702
* Find all of the items to be moved and remove them from
2703
* the list, making an auxiliary list running from firstMovePtr
2704
* to lastMovePtr. Record their areas for redisplay.
2705
*/
2706
2707
firstMovePtr = lastMovePtr = NULL;
2708
for (itemPtr = StartTagSearch(canvasPtr, tag, &search);
2709
itemPtr != NULL; itemPtr = NextItem(&search)) {
2710
if (itemPtr == prevPtr) {
2711
/*
2712
* Item after which insertion is to occur is being
2713
* moved! Switch to insert after its predecessor.
2714
*/
2715
2716
prevPtr = search.prevPtr;
2717
}
2718
if (search.prevPtr == NULL) {
2719
canvasPtr->firstItemPtr = itemPtr->nextPtr;
2720
} else {
2721
search.prevPtr->nextPtr = itemPtr->nextPtr;
2722
}
2723
if (canvasPtr->lastItemPtr == itemPtr) {
2724
canvasPtr->lastItemPtr = search.prevPtr;
2725
}
2726
if (firstMovePtr == NULL) {
2727
firstMovePtr = itemPtr;
2728
} else {
2729
lastMovePtr->nextPtr = itemPtr;
2730
}
2731
lastMovePtr = itemPtr;
2732
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr, itemPtr->x1, itemPtr->y1,
2733
itemPtr->x2, itemPtr->y2);
2734
canvasPtr->flags |= REPICK_NEEDED;
2735
}
2736
2737
/*
2738
* Insert the list of to-be-moved items back into the canvas's
2739
* at the desired position.
2740
*/
2741
2742
if (firstMovePtr == NULL) {
2743
return;
2744
}
2745
if (prevPtr == NULL) {
2746
lastMovePtr->nextPtr = canvasPtr->firstItemPtr;
2747
canvasPtr->firstItemPtr = firstMovePtr;
2748
} else {
2749
lastMovePtr->nextPtr = prevPtr->nextPtr;
2750
prevPtr->nextPtr = firstMovePtr;
2751
}
2752
if (canvasPtr->lastItemPtr == prevPtr) {
2753
canvasPtr->lastItemPtr = lastMovePtr;
2754
}
2755
}
2756
2757
/*
2758
*--------------------------------------------------------------
2759
*
2760
* CanvasBindProc --
2761
*
2762
* This procedure is invoked by the Tk dispatcher to handle
2763
* events associated with bindings on items.
2764
*
2765
* Results:
2766
* None.
2767
*
2768
* Side effects:
2769
* Depends on the command invoked as part of the binding
2770
* (if there was any).
2771
*
2772
*--------------------------------------------------------------
2773
*/
2774
2775
static void
2776
CanvasBindProc(clientData, eventPtr)
2777
ClientData clientData; /* Pointer to canvas structure. */
2778
XEvent *eventPtr; /* Pointer to X event that just
2779
* happened. */
2780
{
2781
TkCanvas *canvasPtr = (TkCanvas *) clientData;
2782
2783
Tcl_Preserve((ClientData) canvasPtr);
2784
2785
/*
2786
* This code below keeps track of the current modifier state in
2787
* canvasPtr>state. This information is used to defer repicks of
2788
* the current item while buttons are down.
2789
*/
2790
2791
if ((eventPtr->type == ButtonPress) || (eventPtr->type == ButtonRelease)) {
2792
int mask;
2793
2794
switch (eventPtr->xbutton.button) {
2795
case Button1:
2796
mask = Button1Mask;
2797
break;
2798
case Button2:
2799
mask = Button2Mask;
2800
break;
2801
case Button3:
2802
mask = Button3Mask;
2803
break;
2804
case Button4:
2805
mask = Button4Mask;
2806
break;
2807
case Button5:
2808
mask = Button5Mask;
2809
break;
2810
default:
2811
mask = 0;
2812
break;
2813
}
2814
2815
/*
2816
* For button press events, repick the current item using the
2817
* button state before the event, then process the event. For
2818
* button release events, first process the event, then repick
2819
* the current item using the button state *after* the event
2820
* (the button has logically gone up before we change the
2821
* current item).
2822
*/
2823
2824
if (eventPtr->type == ButtonPress) {
2825
/*
2826
* On a button press, first repick the current item using
2827
* the button state before the event, the process the event.
2828
*/
2829
2830
canvasPtr->state = eventPtr->xbutton.state;
2831
PickCurrentItem(canvasPtr, eventPtr);
2832
canvasPtr->state ^= mask;
2833
CanvasDoEvent(canvasPtr, eventPtr);
2834
} else {
2835
/*
2836
* Button release: first process the event, with the button
2837
* still considered to be down. Then repick the current
2838
* item under the assumption that the button is no longer down.
2839
*/
2840
2841
canvasPtr->state = eventPtr->xbutton.state;
2842
CanvasDoEvent(canvasPtr, eventPtr);
2843
eventPtr->xbutton.state ^= mask;
2844
canvasPtr->state = eventPtr->xbutton.state;
2845
PickCurrentItem(canvasPtr, eventPtr);
2846
eventPtr->xbutton.state ^= mask;
2847
}
2848
goto done;
2849
} else if ((eventPtr->type == EnterNotify)
2850
|| (eventPtr->type == LeaveNotify)) {
2851
canvasPtr->state = eventPtr->xcrossing.state;
2852
PickCurrentItem(canvasPtr, eventPtr);
2853
goto done;
2854
} else if (eventPtr->type == MotionNotify) {
2855
canvasPtr->state = eventPtr->xmotion.state;
2856
PickCurrentItem(canvasPtr, eventPtr);
2857
}
2858
CanvasDoEvent(canvasPtr, eventPtr);
2859
2860
done:
2861
Tcl_Release((ClientData) canvasPtr);
2862
}
2863
2864
/*
2865
*--------------------------------------------------------------
2866
*
2867
* PickCurrentItem --
2868
*
2869
* Find the topmost item in a canvas that contains a given
2870
* location and mark the the current item. If the current
2871
* item has changed, generate a fake exit event on the old
2872
* current item and a fake enter event on the new current
2873
* item.
2874
*
2875
* Results:
2876
* None.
2877
*
2878
* Side effects:
2879
* The current item for canvasPtr may change. If it does,
2880
* then the commands associated with item entry and exit
2881
* could do just about anything. A binding script could
2882
* delete the canvas, so callers should protect themselves
2883
* with Tcl_Preserve and Tcl_Release.
2884
*
2885
*--------------------------------------------------------------
2886
*/
2887
2888
static void
2889
PickCurrentItem(canvasPtr, eventPtr)
2890
TkCanvas *canvasPtr; /* Canvas widget in which to select
2891
* current item. */
2892
XEvent *eventPtr; /* Event describing location of
2893
* mouse cursor. Must be EnterWindow,
2894
* LeaveWindow, ButtonRelease, or
2895
* MotionNotify. */
2896
{
2897
double coords[2];
2898
int buttonDown;
2899
2900
/*
2901
* Check whether or not a button is down. If so, we'll log entry
2902
* and exit into and out of the current item, but not entry into
2903
* any other item. This implements a form of grabbing equivalent
2904
* to what the X server does for windows.
2905
*/
2906
2907
buttonDown = canvasPtr->state
2908
& (Button1Mask|Button2Mask|Button3Mask|Button4Mask|Button5Mask);
2909
if (!buttonDown) {
2910
canvasPtr->flags &= ~LEFT_GRABBED_ITEM;
2911
}
2912
2913
/*
2914
* Save information about this event in the canvas. The event in
2915
* the canvas is used for two purposes:
2916
*
2917
* 1. Event bindings: if the current item changes, fake events are
2918
* generated to allow item-enter and item-leave bindings to trigger.
2919
* 2. Reselection: if the current item gets deleted, can use the
2920
* saved event to find a new current item.
2921
* Translate MotionNotify events into EnterNotify events, since that's
2922
* what gets reported to item handlers.
2923
*/
2924
2925
if (eventPtr != &canvasPtr->pickEvent) {
2926
if ((eventPtr->type == MotionNotify)
2927
|| (eventPtr->type == ButtonRelease)) {
2928
canvasPtr->pickEvent.xcrossing.type = EnterNotify;
2929
canvasPtr->pickEvent.xcrossing.serial = eventPtr->xmotion.serial;
2930
canvasPtr->pickEvent.xcrossing.send_event
2931
= eventPtr->xmotion.send_event;
2932
canvasPtr->pickEvent.xcrossing.display = eventPtr->xmotion.display;
2933
canvasPtr->pickEvent.xcrossing.window = eventPtr->xmotion.window;
2934
canvasPtr->pickEvent.xcrossing.root = eventPtr->xmotion.root;
2935
canvasPtr->pickEvent.xcrossing.subwindow = None;
2936
canvasPtr->pickEvent.xcrossing.time = eventPtr->xmotion.time;
2937
canvasPtr->pickEvent.xcrossing.x = eventPtr->xmotion.x;
2938
canvasPtr->pickEvent.xcrossing.y = eventPtr->xmotion.y;
2939
canvasPtr->pickEvent.xcrossing.x_root = eventPtr->xmotion.x_root;
2940
canvasPtr->pickEvent.xcrossing.y_root = eventPtr->xmotion.y_root;
2941
canvasPtr->pickEvent.xcrossing.mode = NotifyNormal;
2942
canvasPtr->pickEvent.xcrossing.detail = NotifyNonlinear;
2943
canvasPtr->pickEvent.xcrossing.same_screen
2944
= eventPtr->xmotion.same_screen;
2945
canvasPtr->pickEvent.xcrossing.focus = False;
2946
canvasPtr->pickEvent.xcrossing.state = eventPtr->xmotion.state;
2947
} else {
2948
canvasPtr->pickEvent = *eventPtr;
2949
}
2950
}
2951
2952
/*
2953
* If this is a recursive call (there's already a partially completed
2954
* call pending on the stack; it's in the middle of processing a
2955
* Leave event handler for the old current item) then just return;
2956
* the pending call will do everything that's needed.
2957
*/
2958
2959
if (canvasPtr->flags & REPICK_IN_PROGRESS) {
2960
return;
2961
}
2962
2963
/*
2964
* A LeaveNotify event automatically means that there's no current
2965
* object, so the check for closest item can be skipped.
2966
*/
2967
2968
coords[0] = canvasPtr->pickEvent.xcrossing.x + canvasPtr->xOrigin;
2969
coords[1] = canvasPtr->pickEvent.xcrossing.y + canvasPtr->yOrigin;
2970
if (canvasPtr->pickEvent.type != LeaveNotify) {
2971
canvasPtr->newCurrentPtr = CanvasFindClosest(canvasPtr, coords);
2972
} else {
2973
canvasPtr->newCurrentPtr = NULL;
2974
}
2975
2976
if ((canvasPtr->newCurrentPtr == canvasPtr->currentItemPtr)
2977
&& !(canvasPtr->flags & LEFT_GRABBED_ITEM)) {
2978
/*
2979
* Nothing to do: the current item hasn't changed.
2980
*/
2981
2982
return;
2983
}
2984
2985
/*
2986
* Simulate a LeaveNotify event on the previous current item and
2987
* an EnterNotify event on the new current item. Remove the "current"
2988
* tag from the previous current item and place it on the new current
2989
* item.
2990
*/
2991
2992
if ((canvasPtr->newCurrentPtr != canvasPtr->currentItemPtr)
2993
&& (canvasPtr->currentItemPtr != NULL)
2994
&& !(canvasPtr->flags & LEFT_GRABBED_ITEM)) {
2995
XEvent event;
2996
Tk_Item *itemPtr = canvasPtr->currentItemPtr;
2997
int i;
2998
2999
event = canvasPtr->pickEvent;
3000
event.type = LeaveNotify;
3001
3002
/*
3003
* If the event's detail happens to be NotifyInferior the
3004
* binding mechanism will discard the event. To be consistent,
3005
* always use NotifyAncestor.
3006
*/
3007
3008
event.xcrossing.detail = NotifyAncestor;
3009
canvasPtr->flags |= REPICK_IN_PROGRESS;
3010
CanvasDoEvent(canvasPtr, &event);
3011
canvasPtr->flags &= ~REPICK_IN_PROGRESS;
3012
3013
/*
3014
* The check below is needed because there could be an event
3015
* handler for <LeaveNotify> that deletes the current item.
3016
*/
3017
3018
if ((itemPtr == canvasPtr->currentItemPtr) && !buttonDown) {
3019
for (i = itemPtr->numTags-1; i >= 0; i--) {
3020
if (itemPtr->tagPtr[i] == currentUid) {
3021
itemPtr->tagPtr[i] = itemPtr->tagPtr[itemPtr->numTags-1];
3022
itemPtr->numTags--;
3023
break;
3024
}
3025
}
3026
}
3027
3028
/*
3029
* Note: during CanvasDoEvent above, it's possible that
3030
* canvasPtr->newCurrentPtr got reset to NULL because the
3031
* item was deleted.
3032
*/
3033
}
3034
if ((canvasPtr->newCurrentPtr != canvasPtr->currentItemPtr) && buttonDown) {
3035
canvasPtr->flags |= LEFT_GRABBED_ITEM;
3036
return;
3037
}
3038
3039
/*
3040
* Special note: it's possible that canvasPtr->newCurrentPtr ==
3041
* canvasPtr->currentItemPtr here. This can happen, for example,
3042
* if LEFT_GRABBED_ITEM was set.
3043
*/
3044
3045
canvasPtr->flags &= ~LEFT_GRABBED_ITEM;
3046
canvasPtr->currentItemPtr = canvasPtr->newCurrentPtr;
3047
if (canvasPtr->currentItemPtr != NULL) {
3048
XEvent event;
3049
3050
DoItem((Tcl_Interp *) NULL, canvasPtr->currentItemPtr, currentUid);
3051
event = canvasPtr->pickEvent;
3052
event.type = EnterNotify;
3053
event.xcrossing.detail = NotifyAncestor;
3054
CanvasDoEvent(canvasPtr, &event);
3055
}
3056
}
3057
3058
/*
3059
*----------------------------------------------------------------------
3060
*
3061
* CanvasFindClosest --
3062
*
3063
* Given x and y coordinates, find the topmost canvas item that
3064
* is "close" to the coordinates.
3065
*
3066
* Results:
3067
* The return value is a pointer to the topmost item that is
3068
* close to (x,y), or NULL if no item is close.
3069
*
3070
* Side effects:
3071
* None.
3072
*
3073
*----------------------------------------------------------------------
3074
*/
3075
3076
static Tk_Item *
3077
CanvasFindClosest(canvasPtr, coords)
3078
TkCanvas *canvasPtr; /* Canvas widget to search. */
3079
double coords[2]; /* Desired x,y position in canvas,
3080
* not screen, coordinates.) */
3081
{
3082
Tk_Item *itemPtr;
3083
Tk_Item *bestPtr;
3084
int x1, y1, x2, y2;
3085
3086
x1 = coords[0] - canvasPtr->closeEnough;
3087
y1 = coords[1] - canvasPtr->closeEnough;
3088
x2 = coords[0] + canvasPtr->closeEnough;
3089
y2 = coords[1] + canvasPtr->closeEnough;
3090
3091
bestPtr = NULL;
3092
for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
3093
itemPtr = itemPtr->nextPtr) {
3094
if ((itemPtr->x1 > x2) || (itemPtr->x2 < x1)
3095
|| (itemPtr->y1 > y2) || (itemPtr->y2 < y1)) {
3096
continue;
3097
}
3098
if ((*itemPtr->typePtr->pointProc)((Tk_Canvas) canvasPtr,
3099
itemPtr, coords) <= canvasPtr->closeEnough) {
3100
bestPtr = itemPtr;
3101
}
3102
}
3103
return bestPtr;
3104
}
3105
3106
/*
3107
*--------------------------------------------------------------
3108
*
3109
* CanvasDoEvent --
3110
*
3111
* This procedure is called to invoke binding processing
3112
* for a new event that is associated with the current item
3113
* for a canvas.
3114
*
3115
* Results:
3116
* None.
3117
*
3118
* Side effects:
3119
* Depends on the bindings for the canvas. A binding script
3120
* could delete the canvas, so callers should protect themselves
3121
* with Tcl_Preserve and Tcl_Release.
3122
*
3123
*--------------------------------------------------------------
3124
*/
3125
3126
static void
3127
CanvasDoEvent(canvasPtr, eventPtr)
3128
TkCanvas *canvasPtr; /* Canvas widget in which event
3129
* occurred. */
3130
XEvent *eventPtr; /* Real or simulated X event that
3131
* is to be processed. */
3132
{
3133
#define NUM_STATIC 3
3134
ClientData staticObjects[NUM_STATIC];
3135
ClientData *objectPtr;
3136
int numObjects, i;
3137
Tk_Item *itemPtr;
3138
3139
if (canvasPtr->bindingTable == NULL) {
3140
return;
3141
}
3142
3143
itemPtr = canvasPtr->currentItemPtr;
3144
if ((eventPtr->type == KeyPress) || (eventPtr->type == KeyRelease)) {
3145
itemPtr = canvasPtr->textInfo.focusItemPtr;
3146
}
3147
if (itemPtr == NULL) {
3148
return;
3149
}
3150
3151
/*
3152
* Set up an array with all the relevant objects for processing
3153
* this event. The relevant objects are (a) the event's item,
3154
* (b) the tags associated with the event's item, and (c) the
3155
* tag "all". If there are a lot of tags then malloc an array
3156
* to hold all of the objects.
3157
*/
3158
3159
numObjects = itemPtr->numTags + 2;
3160
if (numObjects <= NUM_STATIC) {
3161
objectPtr = staticObjects;
3162
} else {
3163
objectPtr = (ClientData *) ckalloc((unsigned)
3164
(numObjects * sizeof(ClientData)));
3165
}
3166
objectPtr[0] = (ClientData) allUid;
3167
for (i = itemPtr->numTags-1; i >= 0; i--) {
3168
objectPtr[i+1] = (ClientData) itemPtr->tagPtr[i];
3169
}
3170
objectPtr[itemPtr->numTags+1] = (ClientData) itemPtr;
3171
3172
/*
3173
* Invoke the binding system, then free up the object array if
3174
* it was malloc-ed.
3175
*/
3176
3177
if (canvasPtr->tkwin != NULL) {
3178
Tk_BindEvent(canvasPtr->bindingTable, eventPtr, canvasPtr->tkwin,
3179
numObjects, objectPtr);
3180
}
3181
if (objectPtr != staticObjects) {
3182
ckfree((char *) objectPtr);
3183
}
3184
}
3185
3186
/*
3187
*----------------------------------------------------------------------
3188
*
3189
* CanvasBlinkProc --
3190
*
3191
* This procedure is called as a timer handler to blink the
3192
* insertion cursor off and on.
3193
*
3194
* Results:
3195
* None.
3196
*
3197
* Side effects:
3198
* The cursor gets turned on or off, redisplay gets invoked,
3199
* and this procedure reschedules itself.
3200
*
3201
*----------------------------------------------------------------------
3202
*/
3203
3204
static void
3205
CanvasBlinkProc(clientData)
3206
ClientData clientData; /* Pointer to record describing entry. */
3207
{
3208
TkCanvas *canvasPtr = (TkCanvas *) clientData;
3209
3210
if (!canvasPtr->textInfo.gotFocus || (canvasPtr->insertOffTime == 0)) {
3211
return;
3212
}
3213
if (canvasPtr->textInfo.cursorOn) {
3214
canvasPtr->textInfo.cursorOn = 0;
3215
canvasPtr->insertBlinkHandler = Tcl_CreateTimerHandler(
3216
canvasPtr->insertOffTime, CanvasBlinkProc,
3217
(ClientData) canvasPtr);
3218
} else {
3219
canvasPtr->textInfo.cursorOn = 1;
3220
canvasPtr->insertBlinkHandler = Tcl_CreateTimerHandler(
3221
canvasPtr->insertOnTime, CanvasBlinkProc,
3222
(ClientData) canvasPtr);
3223
}
3224
if (canvasPtr->textInfo.focusItemPtr != NULL) {
3225
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
3226
canvasPtr->textInfo.focusItemPtr->x1,
3227
canvasPtr->textInfo.focusItemPtr->y1,
3228
canvasPtr->textInfo.focusItemPtr->x2,
3229
canvasPtr->textInfo.focusItemPtr->y2);
3230
}
3231
}
3232
3233
/*
3234
*----------------------------------------------------------------------
3235
*
3236
* CanvasFocusProc --
3237
*
3238
* This procedure is called whenever a canvas gets or loses the
3239
* input focus. It's also called whenever the window is
3240
* reconfigured while it has the focus.
3241
*
3242
* Results:
3243
* None.
3244
*
3245
* Side effects:
3246
* The cursor gets turned on or off.
3247
*
3248
*----------------------------------------------------------------------
3249
*/
3250
3251
static void
3252
CanvasFocusProc(canvasPtr, gotFocus)
3253
TkCanvas *canvasPtr; /* Canvas that just got or lost focus. */
3254
int gotFocus; /* 1 means window is getting focus, 0 means
3255
* it's losing it. */
3256
{
3257
Tcl_DeleteTimerHandler(canvasPtr->insertBlinkHandler);
3258
if (gotFocus) {
3259
canvasPtr->textInfo.gotFocus = 1;
3260
canvasPtr->textInfo.cursorOn = 1;
3261
if (canvasPtr->insertOffTime != 0) {
3262
canvasPtr->insertBlinkHandler = Tcl_CreateTimerHandler(
3263
canvasPtr->insertOffTime, CanvasBlinkProc,
3264
(ClientData) canvasPtr);
3265
}
3266
} else {
3267
canvasPtr->textInfo.gotFocus = 0;
3268
canvasPtr->textInfo.cursorOn = 0;
3269
canvasPtr->insertBlinkHandler = (Tcl_TimerToken) NULL;
3270
}
3271
if (canvasPtr->textInfo.focusItemPtr != NULL) {
3272
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
3273
canvasPtr->textInfo.focusItemPtr->x1,
3274
canvasPtr->textInfo.focusItemPtr->y1,
3275
canvasPtr->textInfo.focusItemPtr->x2,
3276
canvasPtr->textInfo.focusItemPtr->y2);
3277
}
3278
if (canvasPtr->highlightWidth > 0) {
3279
canvasPtr->flags |= REDRAW_BORDERS;
3280
if (!(canvasPtr->flags & REDRAW_PENDING)) {
3281
Tcl_DoWhenIdle(DisplayCanvas, (ClientData) canvasPtr);
3282
canvasPtr->flags |= REDRAW_PENDING;
3283
}
3284
}
3285
}
3286
3287
/*
3288
*----------------------------------------------------------------------
3289
*
3290
* CanvasSelectTo --
3291
*
3292
* Modify the selection by moving its un-anchored end. This could
3293
* make the selection either larger or smaller.
3294
*
3295
* Results:
3296
* None.
3297
*
3298
* Side effects:
3299
* The selection changes.
3300
*
3301
*----------------------------------------------------------------------
3302
*/
3303
3304
static void
3305
CanvasSelectTo(canvasPtr, itemPtr, index)
3306
TkCanvas *canvasPtr; /* Information about widget. */
3307
Tk_Item *itemPtr; /* Item that is to hold selection. */
3308
int index; /* Index of element that is to become the
3309
* "other" end of the selection. */
3310
{
3311
int oldFirst, oldLast;
3312
Tk_Item *oldSelPtr;
3313
3314
oldFirst = canvasPtr->textInfo.selectFirst;
3315
oldLast = canvasPtr->textInfo.selectLast;
3316
oldSelPtr = canvasPtr->textInfo.selItemPtr;
3317
3318
/*
3319
* Grab the selection if we don't own it already.
3320
*/
3321
3322
if (canvasPtr->textInfo.selItemPtr == NULL) {
3323
Tk_OwnSelection(canvasPtr->tkwin, XA_PRIMARY, CanvasLostSelection,
3324
(ClientData) canvasPtr);
3325
} else if (canvasPtr->textInfo.selItemPtr != itemPtr) {
3326
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
3327
canvasPtr->textInfo.selItemPtr->x1,
3328
canvasPtr->textInfo.selItemPtr->y1,
3329
canvasPtr->textInfo.selItemPtr->x2,
3330
canvasPtr->textInfo.selItemPtr->y2);
3331
}
3332
canvasPtr->textInfo.selItemPtr = itemPtr;
3333
3334
if (canvasPtr->textInfo.anchorItemPtr != itemPtr) {
3335
canvasPtr->textInfo.anchorItemPtr = itemPtr;
3336
canvasPtr->textInfo.selectAnchor = index;
3337
}
3338
if (canvasPtr->textInfo.selectAnchor <= index) {
3339
canvasPtr->textInfo.selectFirst = canvasPtr->textInfo.selectAnchor;
3340
canvasPtr->textInfo.selectLast = index;
3341
} else {
3342
canvasPtr->textInfo.selectFirst = index;
3343
canvasPtr->textInfo.selectLast = canvasPtr->textInfo.selectAnchor - 1;
3344
}
3345
if ((canvasPtr->textInfo.selectFirst != oldFirst)
3346
|| (canvasPtr->textInfo.selectLast != oldLast)
3347
|| (itemPtr != oldSelPtr)) {
3348
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
3349
itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
3350
}
3351
}
3352
3353
/*
3354
*--------------------------------------------------------------
3355
*
3356
* CanvasFetchSelection --
3357
*
3358
* This procedure is invoked by Tk to return part or all of
3359
* the selection, when the selection is in a canvas widget.
3360
* This procedure always returns the selection as a STRING.
3361
*
3362
* Results:
3363
* The return value is the number of non-NULL bytes stored
3364
* at buffer. Buffer is filled (or partially filled) with a
3365
* NULL-terminated string containing part or all of the selection,
3366
* as given by offset and maxBytes.
3367
*
3368
* Side effects:
3369
* None.
3370
*
3371
*--------------------------------------------------------------
3372
*/
3373
3374
static int
3375
CanvasFetchSelection(clientData, offset, buffer, maxBytes)
3376
ClientData clientData; /* Information about canvas widget. */
3377
int offset; /* Offset within selection of first
3378
* character to be returned. */
3379
char *buffer; /* Location in which to place
3380
* selection. */
3381
int maxBytes; /* Maximum number of bytes to place
3382
* at buffer, not including terminating
3383
* NULL character. */
3384
{
3385
TkCanvas *canvasPtr = (TkCanvas *) clientData;
3386
3387
if (canvasPtr->textInfo.selItemPtr == NULL) {
3388
return -1;
3389
}
3390
if (canvasPtr->textInfo.selItemPtr->typePtr->selectionProc == NULL) {
3391
return -1;
3392
}
3393
return (*canvasPtr->textInfo.selItemPtr->typePtr->selectionProc)(
3394
(Tk_Canvas) canvasPtr, canvasPtr->textInfo.selItemPtr, offset,
3395
buffer, maxBytes);
3396
}
3397
3398
/*
3399
*----------------------------------------------------------------------
3400
*
3401
* CanvasLostSelection --
3402
*
3403
* This procedure is called back by Tk when the selection is
3404
* grabbed away from a canvas widget.
3405
*
3406
* Results:
3407
* None.
3408
*
3409
* Side effects:
3410
* The existing selection is unhighlighted, and the window is
3411
* marked as not containing a selection.
3412
*
3413
*----------------------------------------------------------------------
3414
*/
3415
3416
static void
3417
CanvasLostSelection(clientData)
3418
ClientData clientData; /* Information about entry widget. */
3419
{
3420
TkCanvas *canvasPtr = (TkCanvas *) clientData;
3421
3422
if (canvasPtr->textInfo.selItemPtr != NULL) {
3423
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
3424
canvasPtr->textInfo.selItemPtr->x1,
3425
canvasPtr->textInfo.selItemPtr->y1,
3426
canvasPtr->textInfo.selItemPtr->x2,
3427
canvasPtr->textInfo.selItemPtr->y2);
3428
}
3429
canvasPtr->textInfo.selItemPtr = NULL;
3430
}
3431
3432
/*
3433
*--------------------------------------------------------------
3434
*
3435
* GridAlign --
3436
*
3437
* Given a coordinate and a grid spacing, this procedure
3438
* computes the location of the nearest grid line to the
3439
* coordinate.
3440
*
3441
* Results:
3442
* The return value is the location of the grid line nearest
3443
* to coord.
3444
*
3445
* Side effects:
3446
* None.
3447
*
3448
*--------------------------------------------------------------
3449
*/
3450
3451
static double
3452
GridAlign(coord, spacing)
3453
double coord; /* Coordinate to grid-align. */
3454
double spacing; /* Spacing between grid lines. If <= 0
3455
* then no alignment is done. */
3456
{
3457
if (spacing <= 0.0) {
3458
return coord;
3459
}
3460
if (coord < 0) {
3461
return -((int) ((-coord)/spacing + 0.5)) * spacing;
3462
}
3463
return ((int) (coord/spacing + 0.5)) * spacing;
3464
}
3465
3466
/*
3467
*----------------------------------------------------------------------
3468
*
3469
* PrintScrollFractions --
3470
*
3471
* Given the range that's visible in the window and the "100%
3472
* range" for what's in the canvas, print a string containing
3473
* the scroll fractions. This procedure is used for both x
3474
* and y scrolling.
3475
*
3476
* Results:
3477
* The memory pointed to by string is modified to hold
3478
* two real numbers containing the scroll fractions (between
3479
* 0 and 1) corresponding to the other arguments.
3480
*
3481
* Side effects:
3482
* None.
3483
*
3484
*----------------------------------------------------------------------
3485
*/
3486
3487
static void
3488
PrintScrollFractions(screen1, screen2, object1, object2, string)
3489
int screen1; /* Lowest coordinate visible in the window. */
3490
int screen2; /* Highest coordinate visible in the window. */
3491
int object1; /* Lowest coordinate in the object. */
3492
int object2; /* Highest coordinate in the object. */
3493
char *string; /* Two real numbers get printed here. Must
3494
* have enough storage for two %g
3495
* conversions. */
3496
{
3497
double range, f1, f2;
3498
3499
range = object2 - object1;
3500
if (range <= 0) {
3501
f1 = 0;
3502
f2 = 1.0;
3503
} else {
3504
f1 = (screen1 - object1)/range;
3505
if (f1 < 0) {
3506
f1 = 0.0;
3507
}
3508
f2 = (screen2 - object1)/range;
3509
if (f2 > 1.0) {
3510
f2 = 1.0;
3511
}
3512
if (f2 < f1) {
3513
f2 = f1;
3514
}
3515
}
3516
sprintf(string, "%g %g", f1, f2);
3517
}
3518
3519
/*
3520
*--------------------------------------------------------------
3521
*
3522
* CanvasUpdateScrollbars --
3523
*
3524
* This procedure is invoked whenever a canvas has changed in
3525
* a way that requires scrollbars to be redisplayed (e.g. the
3526
* view in the canvas has changed).
3527
*
3528
* Results:
3529
* None.
3530
*
3531
* Side effects:
3532
* If there are scrollbars associated with the canvas, then
3533
* their scrolling commands are invoked to cause them to
3534
* redisplay. If errors occur, additional Tcl commands may
3535
* be invoked to process the errors.
3536
*
3537
*--------------------------------------------------------------
3538
*/
3539
3540
static void
3541
CanvasUpdateScrollbars(canvasPtr)
3542
TkCanvas *canvasPtr; /* Information about canvas. */
3543
{
3544
int result;
3545
char buffer[200];
3546
Tcl_Interp *interp;
3547
int xOrigin, yOrigin, inset, width, height, scrollX1, scrollX2,
3548
scrollY1, scrollY2;
3549
char *xScrollCmd, *yScrollCmd;
3550
3551
/*
3552
* Save all the relevant values from the canvasPtr, because it might be
3553
* deleted as part of either of the two calls to Tcl_VarEval below.
3554
*/
3555
3556
interp = canvasPtr->interp;
3557
Tcl_Preserve((ClientData) interp);
3558
xScrollCmd = canvasPtr->xScrollCmd;
3559
if (xScrollCmd != (char *) NULL) {
3560
Tcl_Preserve((ClientData) xScrollCmd);
3561
}
3562
yScrollCmd = canvasPtr->yScrollCmd;
3563
if (yScrollCmd != (char *) NULL) {
3564
Tcl_Preserve((ClientData) yScrollCmd);
3565
}
3566
xOrigin = canvasPtr->xOrigin;
3567
yOrigin = canvasPtr->yOrigin;
3568
inset = canvasPtr->inset;
3569
width = Tk_Width(canvasPtr->tkwin);
3570
height = Tk_Height(canvasPtr->tkwin);
3571
scrollX1 = canvasPtr->scrollX1;
3572
scrollX2 = canvasPtr->scrollX2;
3573
scrollY1 = canvasPtr->scrollY1;
3574
scrollY2 = canvasPtr->scrollY2;
3575
canvasPtr->flags &= ~UPDATE_SCROLLBARS;
3576
if (canvasPtr->xScrollCmd != NULL) {
3577
PrintScrollFractions(xOrigin + inset, xOrigin + width - inset,
3578
scrollX1, scrollX2, buffer);
3579
result = Tcl_VarEval(interp, xScrollCmd, " ", buffer, (char *) NULL);
3580
if (result != TCL_OK) {
3581
Tcl_BackgroundError(interp);
3582
}
3583
Tcl_ResetResult(interp);
3584
Tcl_Release((ClientData) xScrollCmd);
3585
}
3586
3587
if (yScrollCmd != NULL) {
3588
PrintScrollFractions(yOrigin + inset, yOrigin + height - inset,
3589
scrollY1, scrollY2, buffer);
3590
result = Tcl_VarEval(interp, yScrollCmd, " ", buffer, (char *) NULL);
3591
if (result != TCL_OK) {
3592
Tcl_BackgroundError(interp);
3593
}
3594
Tcl_ResetResult(interp);
3595
Tcl_Release((ClientData) yScrollCmd);
3596
}
3597
Tcl_Release((ClientData) interp);
3598
}
3599
3600
/*
3601
*--------------------------------------------------------------
3602
*
3603
* CanvasSetOrigin --
3604
*
3605
* This procedure is invoked to change the mapping between
3606
* canvas coordinates and screen coordinates in the canvas
3607
* window.
3608
*
3609
* Results:
3610
* None.
3611
*
3612
* Side effects:
3613
* The canvas will be redisplayed to reflect the change in
3614
* view. In addition, scrollbars will be updated if there
3615
* are any.
3616
*
3617
*--------------------------------------------------------------
3618
*/
3619
3620
static void
3621
CanvasSetOrigin(canvasPtr, xOrigin, yOrigin)
3622
TkCanvas *canvasPtr; /* Information about canvas. */
3623
int xOrigin; /* New X origin for canvas (canvas x-coord
3624
* corresponding to left edge of canvas
3625
* window). */
3626
int yOrigin; /* New Y origin for canvas (canvas y-coord
3627
* corresponding to top edge of canvas
3628
* window). */
3629
{
3630
int left, right, top, bottom, delta;
3631
3632
/*
3633
* If scroll increments have been set, round the window origin
3634
* to the nearest multiple of the increments. Remember, the
3635
* origin is the place just inside the borders, not the upper
3636
* left corner.
3637
*/
3638
3639
if (canvasPtr->xScrollIncrement > 0) {
3640
if (xOrigin >= 0) {
3641
xOrigin += canvasPtr->xScrollIncrement/2;
3642
xOrigin -= (xOrigin + canvasPtr->inset)
3643
% canvasPtr->xScrollIncrement;
3644
} else {
3645
xOrigin = (-xOrigin) + canvasPtr->xScrollIncrement/2;
3646
xOrigin = -(xOrigin - (xOrigin - canvasPtr->inset)
3647
% canvasPtr->xScrollIncrement);
3648
}
3649
}
3650
if (canvasPtr->yScrollIncrement > 0) {
3651
if (yOrigin >= 0) {
3652
yOrigin += canvasPtr->yScrollIncrement/2;
3653
yOrigin -= (yOrigin + canvasPtr->inset)
3654
% canvasPtr->yScrollIncrement;
3655
} else {
3656
yOrigin = (-yOrigin) + canvasPtr->yScrollIncrement/2;
3657
yOrigin = -(yOrigin - (yOrigin - canvasPtr->inset)
3658
% canvasPtr->yScrollIncrement);
3659
}
3660
}
3661
3662
/*
3663
* Adjust the origin if necessary to keep as much as possible of the
3664
* canvas in the view. The variables left, right, etc. keep track of
3665
* how much extra space there is on each side of the view before it
3666
* will stick out past the scroll region. If one side sticks out past
3667
* the edge of the scroll region, adjust the view to bring that side
3668
* back to the edge of the scrollregion (but don't move it so much that
3669
* the other side sticks out now). If scroll increments are in effect,
3670
* be sure to adjust only by full increments.
3671
*/
3672
3673
if ((canvasPtr->confine) && (canvasPtr->regionString != NULL)) {
3674
left = xOrigin + canvasPtr->inset - canvasPtr->scrollX1;
3675
right = canvasPtr->scrollX2
3676
- (xOrigin + Tk_Width(canvasPtr->tkwin) - canvasPtr->inset);
3677
top = yOrigin + canvasPtr->inset - canvasPtr->scrollY1;
3678
bottom = canvasPtr->scrollY2
3679
- (yOrigin + Tk_Height(canvasPtr->tkwin) - canvasPtr->inset);
3680
if ((left < 0) && (right > 0)) {
3681
delta = (right > -left) ? -left : right;
3682
if (canvasPtr->xScrollIncrement > 0) {
3683
delta -= delta % canvasPtr->xScrollIncrement;
3684
}
3685
xOrigin += delta;
3686
} else if ((right < 0) && (left > 0)) {
3687
delta = (left > -right) ? -right : left;
3688
if (canvasPtr->xScrollIncrement > 0) {
3689
delta -= delta % canvasPtr->xScrollIncrement;
3690
}
3691
xOrigin -= delta;
3692
}
3693
if ((top < 0) && (bottom > 0)) {
3694
delta = (bottom > -top) ? -top : bottom;
3695
if (canvasPtr->yScrollIncrement > 0) {
3696
delta -= delta % canvasPtr->yScrollIncrement;
3697
}
3698
yOrigin += delta;
3699
} else if ((bottom < 0) && (top > 0)) {
3700
delta = (top > -bottom) ? -bottom : top;
3701
if (canvasPtr->yScrollIncrement > 0) {
3702
delta -= delta % canvasPtr->yScrollIncrement;
3703
}
3704
yOrigin -= delta;
3705
}
3706
}
3707
3708
if ((xOrigin == canvasPtr->xOrigin) && (yOrigin == canvasPtr->yOrigin)) {
3709
return;
3710
}
3711
3712
/*
3713
* Tricky point: must redisplay not only everything that's visible
3714
* in the window's final configuration, but also everything that was
3715
* visible in the initial configuration. This is needed because some
3716
* item types, like windows, need to know when they move off-screen
3717
* so they can explicitly undisplay themselves.
3718
*/
3719
3720
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
3721
canvasPtr->xOrigin, canvasPtr->yOrigin,
3722
canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin),
3723
canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin));
3724
canvasPtr->xOrigin = xOrigin;
3725
canvasPtr->yOrigin = yOrigin;
3726
canvasPtr->flags |= UPDATE_SCROLLBARS;
3727
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
3728
canvasPtr->xOrigin, canvasPtr->yOrigin,
3729
canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin),
3730
canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin));
3731
}
3732
3733