]>
Commit | Line | Data |
---|---|---|
6a5fa4e0 MG |
1 | /* |
2 | * tkCanvas.c -- | |
3 | * | |
4 | * This module implements canvas widgets for the Tk toolkit. | |
5 | * A canvas displays a background and a collection of graphical | |
6 | * objects such as rectangles, lines, and texts. | |
7 | * | |
8 | * Copyright 1991-1992 Regents of the University of California. | |
9 | * Permission to use, copy, modify, and distribute this | |
10 | * software and its documentation for any purpose and without | |
11 | * fee is hereby granted, provided that the above copyright | |
12 | * notice appear in all copies. The University of California | |
13 | * makes no representations about the suitability of this | |
14 | * software for any purpose. It is provided "as is" without | |
15 | * express or implied warranty. | |
16 | */ | |
17 | ||
18 | #ifndef lint | |
19 | static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkCanvas.c,v 1.28 92/08/19 08:47:57 ouster Exp $ SPRITE (Berkeley)"; | |
20 | #endif | |
21 | ||
22 | #include <stdio.h> | |
23 | #include <stdlib.h> | |
24 | #include <string.h> | |
25 | #include <assert.h> | |
26 | #include "default.h" | |
27 | #include "tkint.h" | |
28 | #include "tkconfig.h" | |
29 | #include "tkcanvas.h" | |
30 | ||
31 | /* | |
32 | * See tkCanvas.h for key data structures used to implement canvases. | |
33 | */ | |
34 | ||
35 | /* | |
36 | * The structure defined below is used to keep track of a tag search | |
37 | * in progress. Only the "prevPtr" field should be accessed by anyone | |
38 | * other than StartTagSearch and NextItem. | |
39 | */ | |
40 | ||
41 | typedef struct TagSearch { | |
42 | Tk_Canvas *canvasPtr; /* Canvas widget being searched. */ | |
43 | Tk_Uid tag; /* Tag to search for. 0 means return | |
44 | * all items. */ | |
45 | Tk_Item *prevPtr; /* Item just before last one found (or NULL | |
46 | * if last one found was first in the item | |
47 | * list of canvasPtr). */ | |
48 | Tk_Item *currentPtr; /* Pointer to last item returned. */ | |
49 | int searchOver; /* Non-zero means NextItem should always | |
50 | * return NULL. */ | |
51 | } TagSearch; | |
52 | ||
53 | /* | |
54 | * Information used for argv parsing. | |
55 | */ | |
56 | ||
57 | ||
58 | static Tk_ConfigSpec configSpecs[] = { | |
59 | {TK_CONFIG_BORDER, "-background", "background", "Background", | |
60 | DEF_CANVAS_BG_COLOR, Tk_Offset(Tk_Canvas, bgBorder), | |
61 | TK_CONFIG_COLOR_ONLY}, | |
62 | {TK_CONFIG_COLOR, (char *) NULL, (char *) NULL, (char *) NULL, | |
63 | (char *) NULL, Tk_Offset(Tk_Canvas, bgColor), | |
64 | TK_CONFIG_COLOR_ONLY}, | |
65 | {TK_CONFIG_BORDER, "-background", "background", "Background", | |
66 | DEF_CANVAS_BG_MONO, Tk_Offset(Tk_Canvas, bgBorder), | |
67 | TK_CONFIG_MONO_ONLY}, | |
68 | {TK_CONFIG_COLOR, (char *) NULL, (char *) NULL, (char *) NULL, | |
69 | (char *) NULL, Tk_Offset(Tk_Canvas, bgColor), | |
70 | TK_CONFIG_MONO_ONLY}, | |
71 | {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL, | |
72 | (char *) NULL, 0, 0}, | |
73 | {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL, | |
74 | (char *) NULL, 0, 0}, | |
75 | {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth", | |
76 | DEF_CANVAS_BORDER_WIDTH, Tk_Offset(Tk_Canvas, borderWidth), 0}, | |
77 | {TK_CONFIG_DOUBLE, "-closeenough", "closeEnough", "CloseEnough", | |
78 | DEF_CANVAS_CLOSE_ENOUGH, Tk_Offset(Tk_Canvas, closeEnough), 0}, | |
79 | {TK_CONFIG_BOOLEAN, "-confine", "confine", "Confine", | |
80 | DEF_CANVAS_CONFINE, Tk_Offset(Tk_Canvas, confine), 0}, | |
81 | {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor", | |
82 | DEF_CANVAS_CURSOR, Tk_Offset(Tk_Canvas, cursor), TK_CONFIG_NULL_OK}, | |
83 | {TK_CONFIG_BORDER, "-cursorbackground", "cursorBackground", "Foreground", | |
84 | DEF_CANVAS_CURSOR_BG, Tk_Offset(Tk_Canvas, cursorBorder), 0}, | |
85 | {TK_CONFIG_PIXELS, "-cursorborderwidth", "cursorBorderWidth", "BorderWidth", | |
86 | DEF_CANVAS_CURSOR_BD_COLOR, Tk_Offset(Tk_Canvas, cursorBorderWidth), | |
87 | TK_CONFIG_COLOR_ONLY}, | |
88 | {TK_CONFIG_PIXELS, "-cursorborderwidth", "cursorBorderWidth", "BorderWidth", | |
89 | DEF_CANVAS_CURSOR_BD_MONO, Tk_Offset(Tk_Canvas, cursorBorderWidth), | |
90 | TK_CONFIG_MONO_ONLY}, | |
91 | {TK_CONFIG_INT, "-cursorofftime", "cursorOffTime", "OffTime", | |
92 | DEF_CANVAS_CURSOR_OFF_TIME, Tk_Offset(Tk_Canvas, cursorOffTime), 0}, | |
93 | {TK_CONFIG_INT, "-cursorontime", "cursorOnTime", "OnTime", | |
94 | DEF_CANVAS_CURSOR_ON_TIME, Tk_Offset(Tk_Canvas, cursorOnTime), 0}, | |
95 | {TK_CONFIG_PIXELS, "-cursorwidth", "cursorWidth", "CursorWidth", | |
96 | DEF_CANVAS_CURSOR_WIDTH, Tk_Offset(Tk_Canvas, cursorWidth), 0}, | |
97 | {TK_CONFIG_PIXELS, "-height", "height", "Height", | |
98 | DEF_CANVAS_HEIGHT, Tk_Offset(Tk_Canvas, height), 0}, | |
99 | {TK_CONFIG_RELIEF, "-relief", "relief", "Relief", | |
100 | DEF_CANVAS_RELIEF, Tk_Offset(Tk_Canvas, relief), 0}, | |
101 | {TK_CONFIG_PIXELS, "-scrollincrement", "scrollIncrement", "ScrollIncrement", | |
102 | DEF_CANVAS_SCROLL_INCREMENT, Tk_Offset(Tk_Canvas, scrollIncrement), 0}, | |
103 | {TK_CONFIG_STRING, "-scrollregion", "scrollRegion", "ScrollRegion", | |
104 | DEF_CANVAS_SCROLL_REGION, Tk_Offset(Tk_Canvas, regionString), 0}, | |
105 | {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground", | |
106 | DEF_CANVAS_SELECT_COLOR, Tk_Offset(Tk_Canvas, selBorder), | |
107 | TK_CONFIG_COLOR_ONLY}, | |
108 | {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground", | |
109 | DEF_CANVAS_SELECT_MONO, Tk_Offset(Tk_Canvas, selBorder), | |
110 | TK_CONFIG_MONO_ONLY}, | |
111 | {TK_CONFIG_PIXELS, "-selectborderwidth", "selectBorderWidth", "BorderWidth", | |
112 | DEF_CANVAS_SELECT_BD_COLOR, Tk_Offset(Tk_Canvas, selBorderWidth), | |
113 | TK_CONFIG_COLOR_ONLY}, | |
114 | {TK_CONFIG_PIXELS, "-selectborderwidth", "selectBorderWidth", "BorderWidth", | |
115 | DEF_CANVAS_SELECT_BD_MONO, Tk_Offset(Tk_Canvas, selBorderWidth), | |
116 | TK_CONFIG_MONO_ONLY}, | |
117 | {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background", | |
118 | DEF_CANVAS_SELECT_FG_COLOR, Tk_Offset(Tk_Canvas, selFgColorPtr), | |
119 | TK_CONFIG_COLOR_ONLY}, | |
120 | {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background", | |
121 | DEF_CANVAS_SELECT_FG_MONO, Tk_Offset(Tk_Canvas, selFgColorPtr), | |
122 | TK_CONFIG_MONO_ONLY}, | |
123 | {TK_CONFIG_PIXELS, "-width", "width", "Width", | |
124 | DEF_CANVAS_WIDTH, Tk_Offset(Tk_Canvas, width), 0}, | |
125 | {TK_CONFIG_STRING, "-xscrollcommand", "xScrollCommand", "ScrollCommand", | |
126 | DEF_CANVAS_X_SCROLL_CMD, Tk_Offset(Tk_Canvas, xScrollCmd), 0}, | |
127 | {TK_CONFIG_STRING, "-yscrollcommand", "yScrollCommand", "ScrollCommand", | |
128 | DEF_CANVAS_Y_SCROLL_CMD, Tk_Offset(Tk_Canvas, yScrollCmd), 0}, | |
129 | {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL, | |
130 | (char *) NULL, 0, 0} | |
131 | }; | |
132 | ||
133 | /* | |
134 | * List of all the item types known at present: | |
135 | */ | |
136 | ||
137 | static Tk_ItemType *typeList = NULL; /* NULL means initialization hasn't | |
138 | * been done yet. */ | |
139 | ||
140 | /* | |
141 | * Standard item types provided by Tk: | |
142 | */ | |
143 | ||
144 | extern Tk_ItemType TkArcType, TkBitmapType, TkLineType; | |
145 | extern Tk_ItemType TkOvalType, TkPolygonType; | |
146 | extern Tk_ItemType TkRectangleType, TkTextType, TkWindowType; | |
147 | ||
148 | /* | |
149 | * Various Tk_Uid's used by this module (set up during initialization): | |
150 | */ | |
151 | ||
152 | static Tk_Uid allUid = NULL; | |
153 | static Tk_Uid currentUid = NULL; | |
154 | ||
155 | /* | |
156 | * Statistics counters: | |
157 | */ | |
158 | ||
159 | static int numIdSearches; | |
160 | static int numSlowSearches; | |
161 | ||
162 | static int CanvasUpdateTime = 200; // Added by Don. | |
163 | ||
164 | /* | |
165 | * Prototypes for procedures defined later in this file: | |
166 | */ | |
167 | ||
168 | static void CanvasBindProc _ANSI_ARGS_((ClientData clientData, | |
169 | XEvent *eventPtr)); | |
170 | static void CanvasBlinkProc _ANSI_ARGS_((ClientData clientData)); | |
171 | static void CanvasDoEvent _ANSI_ARGS_((Tk_Canvas *canvasPtr, | |
172 | XEvent *eventPtr)); | |
173 | static void CanvasEventProc _ANSI_ARGS_((ClientData clientData, | |
174 | XEvent *eventPtr)); | |
175 | static int CanvasFetchSelection _ANSI_ARGS_(( | |
176 | ClientData clientData, int offset, | |
177 | char *buffer, int maxBytes)); | |
178 | static void CanvasFocusProc _ANSI_ARGS_((ClientData clientData, | |
179 | int gotFocus)); | |
180 | static void CanvasLostSelection _ANSI_ARGS_(( | |
181 | ClientData clientData)); | |
182 | static void CanvasSelectTo _ANSI_ARGS_((Tk_Canvas *canvasPtr, | |
183 | Tk_Item *itemPtr, int index)); | |
184 | static void CanvasSetOrigin _ANSI_ARGS_((Tk_Canvas *canvasPtr, | |
185 | int xOrigin, int yOrigin)); | |
186 | static int CanvasTagsParseProc _ANSI_ARGS_((ClientData clientData, | |
187 | Tcl_Interp *interp, Tk_Window tkwin, char *value, | |
188 | char *widgRec, int offset)); | |
189 | static char * CanvasTagsPrintProc _ANSI_ARGS_((ClientData clientData, | |
190 | Tk_Window tkwin, char *widgRec, int offset, | |
191 | Tcl_FreeProc **freeProcPtr)); | |
192 | static void CanvasUpdateScrollbars _ANSI_ARGS_(( | |
193 | Tk_Canvas *canvasPtr)); | |
194 | static int CanvasWidgetCmd _ANSI_ARGS_((ClientData clientData, | |
195 | Tcl_Interp *interp, int argc, char **argv)); | |
196 | static int ConfigureCanvas _ANSI_ARGS_((Tcl_Interp *interp, | |
197 | Tk_Canvas *canvasPtr, int argc, char **argv, | |
198 | int flags)); | |
199 | static void DestroyCanvas _ANSI_ARGS_((ClientData clientData)); | |
200 | static void DisplayCanvas _ANSI_ARGS_((ClientData clientData)); | |
201 | static void DoItem _ANSI_ARGS_((Tcl_Interp *interp, | |
202 | Tk_Item *itemPtr, Tk_Uid tag)); | |
203 | static void EventuallyRedrawArea _ANSI_ARGS_((Tk_Canvas *canvasPtr, | |
204 | int x1, int y1, int x2, int y2)); | |
205 | static int FindItems _ANSI_ARGS_((Tcl_Interp *interp, | |
206 | Tk_Canvas *canvasPtr, int argc, char **argv, | |
207 | char *newTag, char *cmdName, char *option)); | |
208 | static int FindArea _ANSI_ARGS_((Tcl_Interp *interp, | |
209 | Tk_Canvas *canvasPtr, char **argv, Tk_Uid uid, | |
210 | int enclosed)); | |
211 | static double GridAlign _ANSI_ARGS_((double coord, double spacing)); | |
212 | static void InitCanvas _ANSI_ARGS_((void)); | |
213 | static Tk_Item * NextItem _ANSI_ARGS_((TagSearch *searchPtr)); | |
214 | static void PickCurrentItem _ANSI_ARGS_((Tk_Canvas *canvasPtr, | |
215 | XEvent *eventPtr)); | |
216 | static void RelinkItems _ANSI_ARGS_((Tk_Canvas *canvasPtr, | |
217 | char *tag, Tk_Item *prevPtr)); | |
218 | #if defined(USE_XPM3) | |
219 | static int SaveCanvas _ANSI_ARGS_((Tcl_Interp *interp, | |
220 | Tk_Canvas *canvasPtr, char *fileName, int x, | |
221 | int y, unsigned int width, unsigned int height)); | |
222 | #endif | |
223 | static Tk_Item * StartTagSearch _ANSI_ARGS_((Tk_Canvas *canvasPtr, | |
224 | char *tag, TagSearch *searchPtr)); | |
225 | ||
226 | /* | |
227 | * Custom option for handling "-tags" options for canvas items: | |
228 | */ | |
229 | ||
230 | Tk_CustomOption tkCanvasTagsOption = { | |
231 | CanvasTagsParseProc, | |
232 | CanvasTagsPrintProc, | |
233 | (ClientData) NULL | |
234 | }; | |
235 | \f | |
236 | /* | |
237 | *-------------------------------------------------------------- | |
238 | * | |
239 | * Tk_CanvasCmd -- | |
240 | * | |
241 | * This procedure is invoked to process the "canvas" Tcl | |
242 | * command. See the user documentation for details on what | |
243 | * it does. | |
244 | * | |
245 | * Results: | |
246 | * A standard Tcl result. | |
247 | * | |
248 | * Side effects: | |
249 | * See the user documentation. | |
250 | * | |
251 | *-------------------------------------------------------------- | |
252 | */ | |
253 | ||
254 | int | |
255 | Tk_CanvasCmd(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 | ||
362 | static int | |
363 | CanvasWidgetCmd(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 | ||
1313 | static void | |
1314 | DestroyCanvas(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 | ||
1388 | static int | |
1389 | ConfigureCanvas(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" | |
112e5018 | 1491 | #include <X11/xpm.h> |
6a5fa4e0 MG |
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 | ||
1509 | static int | |
1510 | SaveCanvas(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 | ||
1703 | static void | |
1704 | DisplayCanvas(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 | ||
1892 | static void | |
1893 | CanvasEventProc(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 | ||
1950 | static void | |
1951 | EventuallyRedrawArea(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 | ||
2015 | void | |
2016 | Tk_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 | ||
2046 | static void | |
2047 | InitCanvas() | |
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 | ||
2090 | static Tk_Item * | |
2091 | StartTagSearch(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 | ||
2199 | static Tk_Item * | |
2200 | NextItem(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 | ||
2287 | static void | |
2288 | DoItem(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 | ||
2369 | static int | |
2370 | FindItems(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 | ||
2600 | static int | |
2601 | FindArea(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 | ||
2677 | static void | |
2678 | RelinkItems(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 | ||
2764 | static void | |
2765 | CanvasBindProc(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 | ||
2856 | static void | |
2857 | PickCurrentItem(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 | ||
3001 | static void | |
3002 | CanvasDoEvent(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 | ||
3077 | static void | |
3078 | CanvasBlinkProc(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 | ||
3122 | static void | |
3123 | CanvasFocusProc(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 | ||
3166 | static void | |
3167 | CanvasSelectTo(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 | ||
3235 | static int | |
3236 | CanvasFetchSelection(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 | ||
3276 | static void | |
3277 | CanvasLostSelection(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 | ||
3311 | int | |
3312 | TkGetCanvasCoord(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 | ||
3345 | static double | |
3346 | GridAlign(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 | ||
3381 | static void | |
3382 | CanvasUpdateScrollbars(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 | ||
3461 | static void | |
3462 | CanvasSetOrigin(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 */ | |
3541 | static int | |
3542 | CanvasTagsParseProc(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 */ | |
3611 | static char * | |
3612 | CanvasTagsPrintProc(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 | } |