|
|
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.