|
|
1.1 root 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:
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:
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:
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:
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:
1489: #if defined(USE_XPM3)
1490: //#include "xpmtk.h"
1491: #include <xpm.h>
1492: /*
1493: *--------------------------------------------------------------
1494: *
1495: * SaveCanvas --
1496: *
1497: * This procedure saves the contents of a canvas window.
1498: *
1499: * Results:
1500: * The return value is a standard Tcl result. If TCL_ERROR is
1501: * returned, then interp->result contains an error message.
1502: *
1503: * Side effects:
1504: * A pixmap is written to a file.
1505: *
1506: *--------------------------------------------------------------
1507: */
1508:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.