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