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