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 (c) 1991-1994 The Regents of the University of California. 9 * Copyright (c) 1994-1997 Sun Microsystems, Inc. 10 * Copyright (c) 1998-1999 by Scriptics Corporation. 11 * 12 * See the file "license.terms" for information on usage and redistribution 13 * of this file, and for a DISCLAIMER OF ALL WARRANTIES. 14 * 15 * RCS: @(#) $Id: tkCanvas.c,v 1.21.2.5 2008/04/17 14:48:23 dgp Exp $ 16 */ 17 18/* #define USE_OLD_TAG_SEARCH 1 */ 19 20#include "default.h" 21#include "tkInt.h" 22#include "tkPort.h" 23#include "tkCanvas.h" 24#ifdef TK_NO_DOUBLE_BUFFERING 25#ifdef MAC_OSX_TK 26#include "tkMacOSXInt.h" 27#endif 28#endif /* TK_NO_DOUBLE_BUFFERING */ 29 30/* 31 * See tkCanvas.h for key data structures used to implement canvases. 32 */ 33 34#ifdef USE_OLD_TAG_SEARCH 35/* 36 * The structure defined below is used to keep track of a tag search 37 * in progress. No field should be accessed by anyone other than 38 * StartTagSearch and NextItem. 39 */ 40 41typedef struct TagSearch { 42 TkCanvas *canvasPtr; /* Canvas widget being searched. */ 43 Tk_Uid tag; /* Tag to search for. 0 means return 44 * all items. */ 45 Tk_Item *currentPtr; /* Pointer to last item returned. */ 46 Tk_Item *lastPtr; /* The item right before the currentPtr 47 * is tracked so if the currentPtr is 48 * deleted we don't have to start from the 49 * beginning. */ 50 int searchOver; /* Non-zero means NextItem should always 51 * return NULL. */ 52} TagSearch; 53 54#else /* USE_OLD_TAG_SEARCH */ 55/* 56 * The structure defined below is used to keep track of a tag search 57 * in progress. No field should be accessed by anyone other than 58 * TagSearchScan, TagSearchFirst, TagSearchNext, 59 * TagSearchScanExpr, TagSearchEvalExpr, 60 * TagSearchExprInit, TagSearchExprDestroy, 61 * TagSearchDestroy. 62 * ( 63 * Not quite accurate: the TagSearch structure is also accessed from: 64 * CanvasWidgetCmd, FindItems, RelinkItems 65 * The only instances of the structure are owned by: 66 * CanvasWidgetCmd 67 * CanvasWidgetCmd is the only function that calls: 68 * FindItems, RelinkItems 69 * CanvasWidgetCmd, FindItems, RelinkItems, are the only functions that call 70 * TagSearch* 71 * ) 72 */ 73 74typedef struct TagSearch { 75 TkCanvas *canvasPtr; /* Canvas widget being searched. */ 76 Tk_Item *currentPtr; /* Pointer to last item returned. */ 77 Tk_Item *lastPtr; /* The item right before the currentPtr 78 * is tracked so if the currentPtr is 79 * deleted we don't have to start from the 80 * beginning. */ 81 int searchOver; /* Non-zero means NextItem should always 82 * return NULL. */ 83 int type; /* search type */ 84 int id; /* item id for searches by id */ 85 86 char *string; /* tag expression string */ 87 int stringIndex; /* current position in string scan */ 88 int stringLength; /* length of tag expression string */ 89 90 char *rewritebuffer; /* tag string (after removing escapes) */ 91 unsigned int rewritebufferAllocated; /* available space for rewrites */ 92 93 TagSearchExpr *expr; /* compiled tag expression */ 94} TagSearch; 95#endif /* USE_OLD_TAG_SEARCH */ 96 97/* 98 * Custom option for handling "-state" and "-offset" 99 */ 100 101static Tk_CustomOption stateOption = { 102 (Tk_OptionParseProc *) TkStateParseProc, 103 TkStatePrintProc, 104 (ClientData) NULL /* only "normal" and "disabled" */ 105}; 106 107static Tk_CustomOption offsetOption = { 108 (Tk_OptionParseProc *) TkOffsetParseProc, 109 TkOffsetPrintProc, 110 (ClientData) TK_OFFSET_RELATIVE 111}; 112 113/* 114 * Information used for argv parsing. 115 */ 116 117static Tk_ConfigSpec configSpecs[] = { 118 {TK_CONFIG_BORDER, "-background", "background", "Background", 119 DEF_CANVAS_BG_COLOR, Tk_Offset(TkCanvas, bgBorder), 120 TK_CONFIG_COLOR_ONLY}, 121 {TK_CONFIG_BORDER, "-background", "background", "Background", 122 DEF_CANVAS_BG_MONO, Tk_Offset(TkCanvas, bgBorder), 123 TK_CONFIG_MONO_ONLY}, 124 {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL, 125 (char *) NULL, 0, 0}, 126 {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL, 127 (char *) NULL, 0, 0}, 128 {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth", 129 DEF_CANVAS_BORDER_WIDTH, Tk_Offset(TkCanvas, borderWidth), 0}, 130 {TK_CONFIG_DOUBLE, "-closeenough", "closeEnough", "CloseEnough", 131 DEF_CANVAS_CLOSE_ENOUGH, Tk_Offset(TkCanvas, closeEnough), 0}, 132 {TK_CONFIG_BOOLEAN, "-confine", "confine", "Confine", 133 DEF_CANVAS_CONFINE, Tk_Offset(TkCanvas, confine), 0}, 134 {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor", 135 DEF_CANVAS_CURSOR, Tk_Offset(TkCanvas, cursor), TK_CONFIG_NULL_OK}, 136 {TK_CONFIG_PIXELS, "-height", "height", "Height", 137 DEF_CANVAS_HEIGHT, Tk_Offset(TkCanvas, height), 0}, 138 {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground", 139 "HighlightBackground", DEF_CANVAS_HIGHLIGHT_BG, 140 Tk_Offset(TkCanvas, highlightBgColorPtr), 0}, 141 {TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor", 142 DEF_CANVAS_HIGHLIGHT, Tk_Offset(TkCanvas, highlightColorPtr), 0}, 143 {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness", 144 "HighlightThickness", 145 DEF_CANVAS_HIGHLIGHT_WIDTH, Tk_Offset(TkCanvas, highlightWidth), 0}, 146 {TK_CONFIG_BORDER, "-insertbackground", "insertBackground", "Foreground", 147 DEF_CANVAS_INSERT_BG, Tk_Offset(TkCanvas, textInfo.insertBorder), 0}, 148 {TK_CONFIG_PIXELS, "-insertborderwidth", "insertBorderWidth", "BorderWidth", 149 DEF_CANVAS_INSERT_BD_COLOR, 150 Tk_Offset(TkCanvas, textInfo.insertBorderWidth), TK_CONFIG_COLOR_ONLY}, 151 {TK_CONFIG_PIXELS, "-insertborderwidth", "insertBorderWidth", "BorderWidth", 152 DEF_CANVAS_INSERT_BD_MONO, 153 Tk_Offset(TkCanvas, textInfo.insertBorderWidth), TK_CONFIG_MONO_ONLY}, 154 {TK_CONFIG_INT, "-insertofftime", "insertOffTime", "OffTime", 155 DEF_CANVAS_INSERT_OFF_TIME, Tk_Offset(TkCanvas, insertOffTime), 0}, 156 {TK_CONFIG_INT, "-insertontime", "insertOnTime", "OnTime", 157 DEF_CANVAS_INSERT_ON_TIME, Tk_Offset(TkCanvas, insertOnTime), 0}, 158 {TK_CONFIG_PIXELS, "-insertwidth", "insertWidth", "InsertWidth", 159 DEF_CANVAS_INSERT_WIDTH, Tk_Offset(TkCanvas, textInfo.insertWidth), 0}, 160 {TK_CONFIG_CUSTOM, "-offset", "offset", "Offset", "0,0", 161 Tk_Offset(TkCanvas, tsoffset),TK_CONFIG_DONT_SET_DEFAULT, 162 &offsetOption}, 163 {TK_CONFIG_RELIEF, "-relief", "relief", "Relief", 164 DEF_CANVAS_RELIEF, Tk_Offset(TkCanvas, relief), 0}, 165 {TK_CONFIG_STRING, "-scrollregion", "scrollRegion", "ScrollRegion", 166 DEF_CANVAS_SCROLL_REGION, Tk_Offset(TkCanvas, regionString), 167 TK_CONFIG_NULL_OK}, 168 {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground", 169 DEF_CANVAS_SELECT_COLOR, Tk_Offset(TkCanvas, textInfo.selBorder), 170 TK_CONFIG_COLOR_ONLY}, 171 {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground", 172 DEF_CANVAS_SELECT_MONO, Tk_Offset(TkCanvas, textInfo.selBorder), 173 TK_CONFIG_MONO_ONLY}, 174 {TK_CONFIG_PIXELS, "-selectborderwidth", "selectBorderWidth", "BorderWidth", 175 DEF_CANVAS_SELECT_BD_COLOR, 176 Tk_Offset(TkCanvas, textInfo.selBorderWidth), TK_CONFIG_COLOR_ONLY}, 177 {TK_CONFIG_PIXELS, "-selectborderwidth", "selectBorderWidth", "BorderWidth", 178 DEF_CANVAS_SELECT_BD_MONO, Tk_Offset(TkCanvas, textInfo.selBorderWidth), 179 TK_CONFIG_MONO_ONLY}, 180 {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background", 181 DEF_CANVAS_SELECT_FG_COLOR, Tk_Offset(TkCanvas, textInfo.selFgColorPtr), 182 TK_CONFIG_COLOR_ONLY|TK_CONFIG_NULL_OK}, 183 {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background", 184 DEF_CANVAS_SELECT_FG_MONO, Tk_Offset(TkCanvas, textInfo.selFgColorPtr), 185 TK_CONFIG_MONO_ONLY|TK_CONFIG_NULL_OK}, 186 {TK_CONFIG_CUSTOM, "-state", "state", "State", 187 "normal", Tk_Offset(TkCanvas, canvas_state), TK_CONFIG_DONT_SET_DEFAULT, 188 &stateOption}, 189 {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus", 190 DEF_CANVAS_TAKE_FOCUS, Tk_Offset(TkCanvas, takeFocus), 191 TK_CONFIG_NULL_OK}, 192 {TK_CONFIG_PIXELS, "-width", "width", "Width", 193 DEF_CANVAS_WIDTH, Tk_Offset(TkCanvas, width), 0}, 194 {TK_CONFIG_STRING, "-xscrollcommand", "xScrollCommand", "ScrollCommand", 195 DEF_CANVAS_X_SCROLL_CMD, Tk_Offset(TkCanvas, xScrollCmd), 196 TK_CONFIG_NULL_OK}, 197 {TK_CONFIG_PIXELS, "-xscrollincrement", "xScrollIncrement", 198 "ScrollIncrement", 199 DEF_CANVAS_X_SCROLL_INCREMENT, Tk_Offset(TkCanvas, xScrollIncrement), 200 0}, 201 {TK_CONFIG_STRING, "-yscrollcommand", "yScrollCommand", "ScrollCommand", 202 DEF_CANVAS_Y_SCROLL_CMD, Tk_Offset(TkCanvas, yScrollCmd), 203 TK_CONFIG_NULL_OK}, 204 {TK_CONFIG_PIXELS, "-yscrollincrement", "yScrollIncrement", 205 "ScrollIncrement", 206 DEF_CANVAS_Y_SCROLL_INCREMENT, Tk_Offset(TkCanvas, yScrollIncrement), 207 0}, 208 {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL, 209 (char *) NULL, 0, 0} 210}; 211 212/* 213 * List of all the item types known at present. This is *global* and 214 * is protected by typeListMutex. 215 */ 216 217static Tk_ItemType *typeList = NULL; /* NULL means initialization hasn't 218 * been done yet. */ 219TCL_DECLARE_MUTEX(typeListMutex) 220 221#ifndef USE_OLD_TAG_SEARCH 222/* 223 * Uids for operands in compiled advanced tag search expressions 224 * Initialization is done by GetStaticUids() 225 */ 226typedef struct { 227 Tk_Uid allUid; 228 Tk_Uid currentUid; 229 Tk_Uid andUid; 230 Tk_Uid orUid; 231 Tk_Uid xorUid; 232 Tk_Uid parenUid; 233 Tk_Uid negparenUid; 234 Tk_Uid endparenUid; 235 Tk_Uid tagvalUid; 236 Tk_Uid negtagvalUid; 237} SearchUids; 238 239static Tcl_ThreadDataKey dataKey; 240static SearchUids *GetStaticUids _ANSI_ARGS_((void)); 241#endif /* USE_OLD_TAG_SEARCH */ 242 243/* 244 * Standard item types provided by Tk: 245 */ 246 247extern Tk_ItemType tkArcType, tkBitmapType, tkImageType, tkLineType; 248extern Tk_ItemType tkOvalType, tkPolygonType; 249extern Tk_ItemType tkRectangleType, tkTextType, tkWindowType; 250 251/* 252 * Prototypes for procedures defined later in this file: 253 */ 254 255static void CanvasBindProc _ANSI_ARGS_((ClientData clientData, 256 XEvent *eventPtr)); 257static void CanvasBlinkProc _ANSI_ARGS_((ClientData clientData)); 258static void CanvasCmdDeletedProc _ANSI_ARGS_(( 259 ClientData clientData)); 260static void CanvasDoEvent _ANSI_ARGS_((TkCanvas *canvasPtr, 261 XEvent *eventPtr)); 262static void CanvasEventProc _ANSI_ARGS_((ClientData clientData, 263 XEvent *eventPtr)); 264static int CanvasFetchSelection _ANSI_ARGS_(( 265 ClientData clientData, int offset, 266 char *buffer, int maxBytes)); 267static Tk_Item * CanvasFindClosest _ANSI_ARGS_((TkCanvas *canvasPtr, 268 double coords[2])); 269static void CanvasFocusProc _ANSI_ARGS_((TkCanvas *canvasPtr, 270 int gotFocus)); 271static void CanvasLostSelection _ANSI_ARGS_(( 272 ClientData clientData)); 273static void CanvasSelectTo _ANSI_ARGS_((TkCanvas *canvasPtr, 274 Tk_Item *itemPtr, int index)); 275static void CanvasSetOrigin _ANSI_ARGS_((TkCanvas *canvasPtr, 276 int xOrigin, int yOrigin)); 277static void CanvasUpdateScrollbars _ANSI_ARGS_(( 278 TkCanvas *canvasPtr)); 279static int CanvasWidgetCmd _ANSI_ARGS_((ClientData clientData, 280 Tcl_Interp *interp, int argc, Tcl_Obj *CONST *argv)); 281static void CanvasWorldChanged _ANSI_ARGS_(( 282 ClientData instanceData)); 283static int ConfigureCanvas _ANSI_ARGS_((Tcl_Interp *interp, 284 TkCanvas *canvasPtr, int argc, Tcl_Obj *CONST *argv, 285 int flags)); 286static void DestroyCanvas _ANSI_ARGS_((char *memPtr)); 287static void DisplayCanvas _ANSI_ARGS_((ClientData clientData)); 288static void DoItem _ANSI_ARGS_((Tcl_Interp *interp, 289 Tk_Item *itemPtr, Tk_Uid tag)); 290static void EventuallyRedrawItem _ANSI_ARGS_((Tk_Canvas canvas, 291 Tk_Item *itemPtr)); 292#ifdef USE_OLD_TAG_SEARCH 293static int FindItems _ANSI_ARGS_((Tcl_Interp *interp, 294 TkCanvas *canvasPtr, int argc, Tcl_Obj *CONST *argv, 295 Tcl_Obj *newTagObj, int first)); 296#else /* USE_OLD_TAG_SEARCH */ 297static int FindItems _ANSI_ARGS_((Tcl_Interp *interp, 298 TkCanvas *canvasPtr, int argc, Tcl_Obj *CONST *argv, 299 Tcl_Obj *newTagObj, int first, 300 TagSearch **searchPtrPtr)); 301#endif /* USE_OLD_TAG_SEARCH */ 302static int FindArea _ANSI_ARGS_((Tcl_Interp *interp, 303 TkCanvas *canvasPtr, Tcl_Obj *CONST *argv, Tk_Uid uid, 304 int enclosed)); 305static double GridAlign _ANSI_ARGS_((double coord, double spacing)); 306static CONST char** GetStringsFromObjs _ANSI_ARGS_((int argc, 307 Tcl_Obj *CONST *objv)); 308static void InitCanvas _ANSI_ARGS_((void)); 309#ifdef USE_OLD_TAG_SEARCH 310static Tk_Item * NextItem _ANSI_ARGS_((TagSearch *searchPtr)); 311#endif /* USE_OLD_TAG_SEARCH */ 312static void PickCurrentItem _ANSI_ARGS_((TkCanvas *canvasPtr, 313 XEvent *eventPtr)); 314static Tcl_Obj * ScrollFractions _ANSI_ARGS_((int screen1, 315 int screen2, int object1, int object2)); 316#ifdef USE_OLD_TAG_SEARCH 317static void RelinkItems _ANSI_ARGS_((TkCanvas *canvasPtr, 318 Tcl_Obj *tag, Tk_Item *prevPtr)); 319static Tk_Item * StartTagSearch _ANSI_ARGS_((TkCanvas *canvasPtr, 320 Tcl_Obj *tag, TagSearch *searchPtr)); 321#else /* USE_OLD_TAG_SEARCH */ 322static int RelinkItems _ANSI_ARGS_((TkCanvas *canvasPtr, 323 Tcl_Obj *tag, Tk_Item *prevPtr, 324 TagSearch **searchPtrPtr)); 325static void TagSearchExprInit _ANSI_ARGS_ (( 326 TagSearchExpr **exprPtrPtr)); 327static void TagSearchExprDestroy _ANSI_ARGS_((TagSearchExpr *expr)); 328static void TagSearchDestroy _ANSI_ARGS_((TagSearch *searchPtr)); 329static int TagSearchScan _ANSI_ARGS_((TkCanvas *canvasPtr, 330 Tcl_Obj *tag, TagSearch **searchPtrPtr)); 331static int TagSearchScanExpr _ANSI_ARGS_((Tcl_Interp *interp, 332 TagSearch *searchPtr, TagSearchExpr *expr)); 333static int TagSearchEvalExpr _ANSI_ARGS_((TagSearchExpr *expr, 334 Tk_Item *itemPtr)); 335static Tk_Item * TagSearchFirst _ANSI_ARGS_((TagSearch *searchPtr)); 336static Tk_Item * TagSearchNext _ANSI_ARGS_((TagSearch *searchPtr)); 337#endif /* USE_OLD_TAG_SEARCH */ 338 339/* 340 * The structure below defines canvas class behavior by means of procedures 341 * that can be invoked from generic window code. 342 */ 343 344static Tk_ClassProcs canvasClass = { 345 sizeof(Tk_ClassProcs), /* size */ 346 CanvasWorldChanged, /* worldChangedProc */ 347}; 348 349 350/* 351 *-------------------------------------------------------------- 352 * 353 * Tk_CanvasObjCmd -- 354 * 355 * This procedure is invoked to process the "canvas" Tcl 356 * command. See the user documentation for details on what 357 * it does. 358 * 359 * Results: 360 * A standard Tcl result. 361 * 362 * Side effects: 363 * See the user documentation. 364 * 365 *-------------------------------------------------------------- 366 */ 367 368int 369Tk_CanvasObjCmd(clientData, interp, argc, argv) 370 ClientData clientData; /* Main window associated with 371 * interpreter. */ 372 Tcl_Interp *interp; /* Current interpreter. */ 373 int argc; /* Number of arguments. */ 374 Tcl_Obj *CONST argv[]; /* Argument objects. */ 375{ 376 Tk_Window tkwin = (Tk_Window) clientData; 377 TkCanvas *canvasPtr; 378 Tk_Window new; 379 380 if (typeList == NULL) { 381 InitCanvas(); 382 } 383 384 if (argc < 2) { 385 Tcl_WrongNumArgs(interp, 1, argv, "pathName ?options?"); 386 return TCL_ERROR; 387 } 388 389 new = Tk_CreateWindowFromPath(interp, tkwin, 390 Tcl_GetString(argv[1]), (char *) NULL); 391 if (new == NULL) { 392 return TCL_ERROR; 393 } 394 395 /* 396 * Initialize fields that won't be initialized by ConfigureCanvas, 397 * or which ConfigureCanvas expects to have reasonable values 398 * (e.g. resource pointers). 399 */ 400 401 canvasPtr = (TkCanvas *) ckalloc(sizeof(TkCanvas)); 402 canvasPtr->tkwin = new; 403 canvasPtr->display = Tk_Display(new); 404 canvasPtr->interp = interp; 405 canvasPtr->widgetCmd = Tcl_CreateObjCommand(interp, 406 Tk_PathName(canvasPtr->tkwin), CanvasWidgetCmd, 407 (ClientData) canvasPtr, CanvasCmdDeletedProc); 408 canvasPtr->firstItemPtr = NULL; 409 canvasPtr->lastItemPtr = NULL; 410 canvasPtr->borderWidth = 0; 411 canvasPtr->bgBorder = NULL; 412 canvasPtr->relief = TK_RELIEF_FLAT; 413 canvasPtr->highlightWidth = 0; 414 canvasPtr->highlightBgColorPtr = NULL; 415 canvasPtr->highlightColorPtr = NULL; 416 canvasPtr->inset = 0; 417 canvasPtr->pixmapGC = None; 418 canvasPtr->width = None; 419 canvasPtr->height = None; 420 canvasPtr->confine = 0; 421 canvasPtr->textInfo.selBorder = NULL; 422 canvasPtr->textInfo.selBorderWidth = 0; 423 canvasPtr->textInfo.selFgColorPtr = NULL; 424 canvasPtr->textInfo.selItemPtr = NULL; 425 canvasPtr->textInfo.selectFirst = -1; 426 canvasPtr->textInfo.selectLast = -1; 427 canvasPtr->textInfo.anchorItemPtr = NULL; 428 canvasPtr->textInfo.selectAnchor = 0; 429 canvasPtr->textInfo.insertBorder = NULL; 430 canvasPtr->textInfo.insertWidth = 0; 431 canvasPtr->textInfo.insertBorderWidth = 0; 432 canvasPtr->textInfo.focusItemPtr = NULL; 433 canvasPtr->textInfo.gotFocus = 0; 434 canvasPtr->textInfo.cursorOn = 0; 435 canvasPtr->insertOnTime = 0; 436 canvasPtr->insertOffTime = 0; 437 canvasPtr->insertBlinkHandler = (Tcl_TimerToken) NULL; 438 canvasPtr->xOrigin = canvasPtr->yOrigin = 0; 439 canvasPtr->drawableXOrigin = canvasPtr->drawableYOrigin = 0; 440 canvasPtr->bindingTable = NULL; 441 canvasPtr->currentItemPtr = NULL; 442 canvasPtr->newCurrentPtr = NULL; 443 canvasPtr->closeEnough = 0.0; 444 canvasPtr->pickEvent.type = LeaveNotify; 445 canvasPtr->pickEvent.xcrossing.x = 0; 446 canvasPtr->pickEvent.xcrossing.y = 0; 447 canvasPtr->state = 0; 448 canvasPtr->xScrollCmd = NULL; 449 canvasPtr->yScrollCmd = NULL; 450 canvasPtr->scrollX1 = 0; 451 canvasPtr->scrollY1 = 0; 452 canvasPtr->scrollX2 = 0; 453 canvasPtr->scrollY2 = 0; 454 canvasPtr->regionString = NULL; 455 canvasPtr->xScrollIncrement = 0; 456 canvasPtr->yScrollIncrement = 0; 457 canvasPtr->scanX = 0; 458 canvasPtr->scanXOrigin = 0; 459 canvasPtr->scanY = 0; 460 canvasPtr->scanYOrigin = 0; 461 canvasPtr->hotPtr = NULL; 462 canvasPtr->hotPrevPtr = NULL; 463 canvasPtr->cursor = None; 464 canvasPtr->takeFocus = NULL; 465 canvasPtr->pixelsPerMM = WidthOfScreen(Tk_Screen(new)); 466 canvasPtr->pixelsPerMM /= WidthMMOfScreen(Tk_Screen(new)); 467 canvasPtr->flags = 0; 468 canvasPtr->nextId = 1; 469 canvasPtr->psInfo = NULL; 470 canvasPtr->canvas_state = TK_STATE_NORMAL; 471 canvasPtr->tsoffset.flags = 0; 472 canvasPtr->tsoffset.xoffset = 0; 473 canvasPtr->tsoffset.yoffset = 0; 474#ifndef USE_OLD_TAG_SEARCH 475 canvasPtr->bindTagExprs = NULL; 476#endif 477 Tcl_InitHashTable(&canvasPtr->idTable, TCL_ONE_WORD_KEYS); 478 479 Tk_SetClass(canvasPtr->tkwin, "Canvas"); 480 Tk_SetClassProcs(canvasPtr->tkwin, &canvasClass, (ClientData) canvasPtr); 481 Tk_CreateEventHandler(canvasPtr->tkwin, 482 ExposureMask|StructureNotifyMask|FocusChangeMask, 483 CanvasEventProc, (ClientData) canvasPtr); 484 Tk_CreateEventHandler(canvasPtr->tkwin, KeyPressMask|KeyReleaseMask 485 |ButtonPressMask|ButtonReleaseMask|EnterWindowMask 486 |LeaveWindowMask|PointerMotionMask|VirtualEventMask, 487 CanvasBindProc, (ClientData) canvasPtr); 488 Tk_CreateSelHandler(canvasPtr->tkwin, XA_PRIMARY, XA_STRING, 489 CanvasFetchSelection, (ClientData) canvasPtr, XA_STRING); 490 if (ConfigureCanvas(interp, canvasPtr, argc-2, argv+2, 0) != TCL_OK) { 491 goto error; 492 } 493 494 Tcl_SetResult(interp, Tk_PathName(canvasPtr->tkwin), TCL_STATIC); 495 return TCL_OK; 496 497 error: 498 Tk_DestroyWindow(canvasPtr->tkwin); 499 return TCL_ERROR; 500} 501 502/* 503 *-------------------------------------------------------------- 504 * 505 * CanvasWidgetCmd -- 506 * 507 * This procedure is invoked to process the Tcl command 508 * that corresponds to a widget managed by this module. 509 * See the user documentation for details on what it does. 510 * 511 * Results: 512 * A standard Tcl result. 513 * 514 * Side effects: 515 * See the user documentation. 516 * 517 *-------------------------------------------------------------- 518 */ 519 520static int 521CanvasWidgetCmd(clientData, interp, objc, objv) 522 ClientData clientData; /* Information about canvas 523 * widget. */ 524 Tcl_Interp *interp; /* Current interpreter. */ 525 int objc; /* Number of arguments. */ 526 Tcl_Obj *CONST objv[]; /* Argument objects. */ 527{ 528 TkCanvas *canvasPtr = (TkCanvas *) clientData; 529 int c, length, result; 530 Tk_Item *itemPtr = NULL; /* Initialization needed only to 531 * prevent compiler warning. */ 532#ifdef USE_OLD_TAG_SEARCH 533 TagSearch search; 534#else /* USE_OLD_TAG_SEARCH */ 535 TagSearch *searchPtr = NULL; /* Allocated by first TagSearchScan 536 * Freed by TagSearchDestroy */ 537#endif /* USE_OLD_TAG_SEARCH */ 538 539 int index; 540 static CONST char *optionStrings[] = { 541 "addtag", "bbox", "bind", "canvasx", 542 "canvasy", "cget", "configure", "coords", 543 "create", "dchars", "delete", "dtag", 544 "find", "focus", "gettags", "icursor", 545 "index", "insert", "itemcget", "itemconfigure", 546 "lower", "move", "postscript", "raise", 547 "scale", "scan", "select", "type", 548 "xview", "yview", 549 NULL 550 }; 551 enum options { 552 CANV_ADDTAG, CANV_BBOX, CANV_BIND, CANV_CANVASX, 553 CANV_CANVASY, CANV_CGET, CANV_CONFIGURE, CANV_COORDS, 554 CANV_CREATE, CANV_DCHARS, CANV_DELETE, CANV_DTAG, 555 CANV_FIND, CANV_FOCUS, CANV_GETTAGS, CANV_ICURSOR, 556 CANV_INDEX, CANV_INSERT, CANV_ITEMCGET, CANV_ITEMCONFIGURE, 557 CANV_LOWER, CANV_MOVE, CANV_POSTSCRIPT,CANV_RAISE, 558 CANV_SCALE, CANV_SCAN, CANV_SELECT, CANV_TYPE, 559 CANV_XVIEW, CANV_YVIEW 560 }; 561 562 if (objc < 2) { 563 Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?"); 564 return TCL_ERROR; 565 } 566 if (Tcl_GetIndexFromObj(interp, objv[1], optionStrings, "option", 0, 567 &index) != TCL_OK) { 568 return TCL_ERROR; 569 } 570 Tcl_Preserve((ClientData) canvasPtr); 571 572 result = TCL_OK; 573 switch ((enum options) index) { 574 case CANV_ADDTAG: { 575 if (objc < 4) { 576 Tcl_WrongNumArgs(interp, 2, objv, "tag searchCommand ?arg arg ...?"); 577 result = TCL_ERROR; 578 goto done; 579 } 580#ifdef USE_OLD_TAG_SEARCH 581 result = FindItems(interp, canvasPtr, objc, objv, objv[2], 3); 582#else /* USE_OLD_TAG_SEARCH */ 583 result = FindItems(interp, canvasPtr, objc, objv, objv[2], 3, &searchPtr); 584#endif /* USE_OLD_TAG_SEARCH */ 585 break; 586 } 587 588 case CANV_BBOX: { 589 int i, gotAny; 590 int x1 = 0, y1 = 0, x2 = 0, y2 = 0; /* Initializations needed 591 * only to prevent compiler 592 * warnings. */ 593 594 if (objc < 3) { 595 Tcl_WrongNumArgs(interp, 2, objv, "tagOrId ?tagOrId ...?"); 596 result = TCL_ERROR; 597 goto done; 598 } 599 gotAny = 0; 600 for (i = 2; i < objc; i++) { 601#ifdef USE_OLD_TAG_SEARCH 602 for (itemPtr = StartTagSearch(canvasPtr, objv[i], &search); 603 itemPtr != NULL; itemPtr = NextItem(&search)) { 604#else /* USE_OLD_TAG_SEARCH */ 605 if ((result = TagSearchScan(canvasPtr, objv[i], &searchPtr)) != TCL_OK) { 606 goto done; 607 } 608 for (itemPtr = TagSearchFirst(searchPtr); 609 itemPtr != NULL; itemPtr = TagSearchNext(searchPtr)) { 610#endif /* USE_OLD_TAG_SEARCH */ 611 612 if ((itemPtr->x1 >= itemPtr->x2) 613 || (itemPtr->y1 >= itemPtr->y2)) { 614 continue; 615 } 616 if (!gotAny) { 617 x1 = itemPtr->x1; 618 y1 = itemPtr->y1; 619 x2 = itemPtr->x2; 620 y2 = itemPtr->y2; 621 gotAny = 1; 622 } else { 623 if (itemPtr->x1 < x1) { 624 x1 = itemPtr->x1; 625 } 626 if (itemPtr->y1 < y1) { 627 y1 = itemPtr->y1; 628 } 629 if (itemPtr->x2 > x2) { 630 x2 = itemPtr->x2; 631 } 632 if (itemPtr->y2 > y2) { 633 y2 = itemPtr->y2; 634 } 635 } 636 } 637 } 638 if (gotAny) { 639 char buf[TCL_INTEGER_SPACE * 4]; 640 641 sprintf(buf, "%d %d %d %d", x1, y1, x2, y2); 642 Tcl_SetResult(interp, buf, TCL_VOLATILE); 643 } 644 break; 645 } 646 case CANV_BIND: { 647 ClientData object; 648 649 if ((objc < 3) || (objc > 5)) { 650 Tcl_WrongNumArgs(interp, 2, objv, "tagOrId ?sequence? ?command?"); 651 result = TCL_ERROR; 652 goto done; 653 } 654 655 /* 656 * Figure out what object to use for the binding (individual 657 * item vs. tag). 658 */ 659 660 object = 0; 661#ifdef USE_OLD_TAG_SEARCH 662 if (isdigit(UCHAR(Tcl_GetString(objv[2])[0]))) { 663 int id; 664 char *end; 665 Tcl_HashEntry *entryPtr; 666 667 id = strtoul(Tcl_GetString(objv[2]), &end, 0); 668 if (*end != 0) { 669 goto bindByTag; 670 } 671 entryPtr = Tcl_FindHashEntry(&canvasPtr->idTable, (char *) id); 672 if (entryPtr != NULL) { 673 itemPtr = (Tk_Item *) Tcl_GetHashValue(entryPtr); 674 object = (ClientData) itemPtr; 675 } 676 677 if (object == 0) { 678 Tcl_AppendResult(interp, "item \"", Tcl_GetString(objv[2]), 679 "\" doesn't exist", (char *) NULL); 680 result = TCL_ERROR; 681 goto done; 682 } 683 } else { 684 bindByTag: 685 object = (ClientData) Tk_GetUid(Tcl_GetString(objv[2])); 686 } 687#else /* USE_OLD_TAG_SEARCH */ 688 if ((result = TagSearchScan(canvasPtr, objv[2], &searchPtr)) != TCL_OK) { 689 goto done; 690 } 691 if (searchPtr->type == 1) { 692 Tcl_HashEntry *entryPtr; 693 694 entryPtr = Tcl_FindHashEntry(&canvasPtr->idTable, (char *) searchPtr->id); 695 if (entryPtr != NULL) { 696 itemPtr = (Tk_Item *) Tcl_GetHashValue(entryPtr); 697 object = (ClientData) itemPtr; 698 } 699 700 if (object == 0) { 701 Tcl_AppendResult(interp, "item \"", Tcl_GetString(objv[2]), 702 "\" doesn't exist", (char *) NULL); 703 result = TCL_ERROR; 704 goto done; 705 } 706 } else { 707 object = (ClientData) searchPtr->expr->uid; 708 } 709#endif /* USE_OLD_TAG_SEARCH */ 710 711 /* 712 * Make a binding table if the canvas doesn't already have 713 * one. 714 */ 715 716 if (canvasPtr->bindingTable == NULL) { 717 canvasPtr->bindingTable = Tk_CreateBindingTable(interp); 718 } 719 720 if (objc == 5) { 721 int append = 0; 722 unsigned long mask; 723 char* argv4 = Tcl_GetStringFromObj(objv[4],NULL); 724 725 if (argv4[0] == 0) { 726 result = Tk_DeleteBinding(interp, canvasPtr->bindingTable, 727 object, Tcl_GetStringFromObj(objv[3], NULL)); 728 goto done; 729 } 730#ifndef USE_OLD_TAG_SEARCH 731 if (searchPtr->type == 4) { 732 /* 733 * if new tag expression, then insert in linked list 734 */ 735 TagSearchExpr *expr, **lastPtr; 736 737 lastPtr = &(canvasPtr->bindTagExprs); 738 while ((expr = *lastPtr) != NULL) { 739 if (expr->uid == searchPtr->expr->uid) { 740 break; 741 } 742 lastPtr = &(expr->next); 743 } 744 if (!expr) { 745 /* 746 * transfer ownership of expr to bindTagExprs list 747 */ 748 *lastPtr = searchPtr->expr; 749 searchPtr->expr->next = NULL; 750 751 /* 752 * flag in TagSearch that expr has changed ownership 753 * so that TagSearchDestroy doesn't try to free it 754 */ 755 searchPtr->expr = NULL; 756 } 757 } 758#endif /* not USE_OLD_TAG_SEARCH */ 759 if (argv4[0] == '+') { 760 argv4++; 761 append = 1; 762 } 763 mask = Tk_CreateBinding(interp, canvasPtr->bindingTable, 764 object, Tcl_GetStringFromObj(objv[3],NULL), argv4, append); 765 if (mask == 0) { 766 result = TCL_ERROR; 767 goto done; 768 } 769 if (mask & (unsigned) ~(ButtonMotionMask|Button1MotionMask 770 |Button2MotionMask|Button3MotionMask|Button4MotionMask 771 |Button5MotionMask|ButtonPressMask|ButtonReleaseMask 772 |EnterWindowMask|LeaveWindowMask|KeyPressMask 773 |KeyReleaseMask|PointerMotionMask|VirtualEventMask)) { 774 Tk_DeleteBinding(interp, canvasPtr->bindingTable, 775 object, Tcl_GetStringFromObj(objv[3], NULL)); 776 Tcl_ResetResult(interp); 777 Tcl_AppendResult(interp, "requested illegal events; ", 778 "only key, button, motion, enter, leave, and virtual ", 779 "events may be used", (char *) NULL); 780 result = TCL_ERROR; 781 goto done; 782 } 783 } else if (objc == 4) { 784 CONST char *command; 785 786 command = Tk_GetBinding(interp, canvasPtr->bindingTable, 787 object, Tcl_GetStringFromObj(objv[3], NULL)); 788 if (command == NULL) { 789 CONST char *string; 790 791 string = Tcl_GetStringResult(interp); 792 /* 793 * Ignore missing binding errors. This is a special hack 794 * that relies on the error message returned by FindSequence 795 * in tkBind.c. 796 */ 797 798 if (string[0] != '\0') { 799 result = TCL_ERROR; 800 goto done; 801 } else { 802 Tcl_ResetResult(interp); 803 } 804 } else { 805 Tcl_SetResult(interp, (char *) command, TCL_STATIC); 806 } 807 } else { 808 Tk_GetAllBindings(interp, canvasPtr->bindingTable, object); 809 } 810 break; 811 } 812 case CANV_CANVASX: { 813 int x; 814 double grid; 815 char buf[TCL_DOUBLE_SPACE]; 816 817 if ((objc < 3) || (objc > 4)) { 818 Tcl_WrongNumArgs(interp, 2, objv, "screenx ?gridspacing?"); 819 result = TCL_ERROR; 820 goto done; 821 } 822 if (Tk_GetPixelsFromObj(interp, canvasPtr->tkwin, objv[2], &x) != TCL_OK) { 823 result = TCL_ERROR; 824 goto done; 825 } 826 if (objc == 4) { 827 if (Tk_CanvasGetCoordFromObj(interp, (Tk_Canvas) canvasPtr, objv[3], 828 &grid) != TCL_OK) { 829 result = TCL_ERROR; 830 goto done; 831 } 832 } else { 833 grid = 0.0; 834 } 835 x += canvasPtr->xOrigin; 836 Tcl_PrintDouble(interp, GridAlign((double) x, grid), buf); 837 Tcl_SetResult(interp, buf, TCL_VOLATILE); 838 break; 839 } 840 case CANV_CANVASY: { 841 int y; 842 double grid; 843 char buf[TCL_DOUBLE_SPACE]; 844 845 if ((objc < 3) || (objc > 4)) { 846 Tcl_WrongNumArgs(interp, 2, objv, "screeny ?gridspacing?"); 847 result = TCL_ERROR; 848 goto done; 849 } 850 if (Tk_GetPixelsFromObj(interp, canvasPtr->tkwin, objv[2], &y) != TCL_OK) { 851 result = TCL_ERROR; 852 goto done; 853 } 854 if (objc == 4) { 855 if (Tk_CanvasGetCoordFromObj(interp, (Tk_Canvas) canvasPtr, 856 objv[3], &grid) != TCL_OK) { 857 result = TCL_ERROR; 858 goto done; 859 } 860 } else { 861 grid = 0.0; 862 } 863 y += canvasPtr->yOrigin; 864 Tcl_PrintDouble(interp, GridAlign((double) y, grid), buf); 865 Tcl_SetResult(interp, buf, TCL_VOLATILE); 866 break; 867 } 868 case CANV_CGET: { 869 if (objc != 3) { 870 Tcl_WrongNumArgs(interp, 2, objv, "option"); 871 result = TCL_ERROR; 872 goto done; 873 } 874 result = Tk_ConfigureValue(interp, canvasPtr->tkwin, configSpecs, 875 (char *) canvasPtr, Tcl_GetString(objv[2]), 0); 876 break; 877 } 878 case CANV_CONFIGURE: { 879 if (objc == 2) { 880 result = Tk_ConfigureInfo(interp, canvasPtr->tkwin, configSpecs, 881 (char *) canvasPtr, (char *) NULL, 0); 882 } else if (objc == 3) { 883 result = Tk_ConfigureInfo(interp, canvasPtr->tkwin, configSpecs, 884 (char *) canvasPtr, Tcl_GetString(objv[2]), 0); 885 } else { 886 result = ConfigureCanvas(interp, canvasPtr, objc-2, objv+2, 887 TK_CONFIG_ARGV_ONLY); 888 } 889 break; 890 } 891 case CANV_COORDS: { 892 if (objc < 3) { 893 Tcl_WrongNumArgs(interp, 2, objv, "tagOrId ?x y x y ...?"); 894 result = TCL_ERROR; 895 goto done; 896 } 897#ifdef USE_OLD_TAG_SEARCH 898 itemPtr = StartTagSearch(canvasPtr, objv[2], &search); 899#else /* USE_OLD_TAG_SEARCH */ 900 if ((result = TagSearchScan(canvasPtr, objv[2], &searchPtr)) != TCL_OK) { 901 goto done; 902 } 903 itemPtr = TagSearchFirst(searchPtr); 904#endif /* USE_OLD_TAG_SEARCH */ 905 if (itemPtr != NULL) { 906 if (objc != 3) { 907 EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr); 908 } 909 if (itemPtr->typePtr->coordProc != NULL) { 910 if (itemPtr->typePtr->alwaysRedraw & TK_CONFIG_OBJS) { 911 result = (*itemPtr->typePtr->coordProc)(interp, 912 (Tk_Canvas) canvasPtr, itemPtr, objc-3, objv+3); 913 } else { 914 CONST char **args = GetStringsFromObjs(objc-3, objv+3); 915 result = (*itemPtr->typePtr->coordProc)(interp, 916 (Tk_Canvas) canvasPtr, itemPtr, objc-3, (Tcl_Obj **) args); 917 if (args) ckfree((char *) args); 918 } 919 } 920 if (objc != 3) { 921 EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr); 922 } 923 } 924 break; 925 } 926 case CANV_CREATE: { 927 Tk_ItemType *typePtr; 928 Tk_ItemType *matchPtr = NULL; 929 Tk_Item *itemPtr; 930 char buf[TCL_INTEGER_SPACE]; 931 int isNew = 0; 932 Tcl_HashEntry *entryPtr; 933 char *arg; 934 935 if (objc < 3) { 936 Tcl_WrongNumArgs(interp, 2, objv, "type coords ?arg arg ...?"); 937 result = TCL_ERROR; 938 goto done; 939 } 940 arg = Tcl_GetStringFromObj(objv[2], &length); 941 c = arg[0]; 942 Tcl_MutexLock(&typeListMutex); 943 for (typePtr = typeList; typePtr != NULL; typePtr = typePtr->nextPtr) { 944 if ((c == typePtr->name[0]) 945 && (strncmp(arg, typePtr->name, (unsigned) length) == 0)) { 946 if (matchPtr != NULL) { 947 Tcl_MutexUnlock(&typeListMutex); 948 badType: 949 Tcl_AppendResult(interp, 950 "unknown or ambiguous item type \"", 951 arg, "\"", (char *) NULL); 952 result = TCL_ERROR; 953 goto done; 954 } 955 matchPtr = typePtr; 956 } 957 } 958 /* 959 * Can unlock now because we no longer look at the fields of 960 * the matched item type that are potentially modified by 961 * other threads. 962 */ 963 Tcl_MutexUnlock(&typeListMutex); 964 if (matchPtr == NULL) { 965 goto badType; 966 } 967 if (objc < 4) { 968 /* 969 * Allow more specific error return. 970 */ 971 Tcl_WrongNumArgs(interp, 3, objv, "coords ?arg arg ...?"); 972 result = TCL_ERROR; 973 goto done; 974 } 975 typePtr = matchPtr; 976 itemPtr = (Tk_Item *) ckalloc((unsigned) typePtr->itemSize); 977 itemPtr->id = canvasPtr->nextId; 978 canvasPtr->nextId++; 979 itemPtr->tagPtr = itemPtr->staticTagSpace; 980 itemPtr->tagSpace = TK_TAG_SPACE; 981 itemPtr->numTags = 0; 982 itemPtr->typePtr = typePtr; 983 itemPtr->state = TK_STATE_NULL; 984 itemPtr->redraw_flags = 0; 985 if (itemPtr->typePtr->alwaysRedraw & TK_CONFIG_OBJS) { 986 result = (*typePtr->createProc)(interp, (Tk_Canvas) canvasPtr, 987 itemPtr, objc-3, objv+3); 988 } else { 989 CONST char **args = GetStringsFromObjs(objc-3, objv+3); 990 result = (*typePtr->createProc)(interp, (Tk_Canvas) canvasPtr, 991 itemPtr, objc-3, (Tcl_Obj **) args); 992 if (args) ckfree((char *) args); 993 } 994 if (result != TCL_OK) { 995 ckfree((char *) itemPtr); 996 result = TCL_ERROR; 997 goto done; 998 } 999 itemPtr->nextPtr = NULL; 1000 entryPtr = Tcl_CreateHashEntry(&canvasPtr->idTable, 1001 (char *) itemPtr->id, &isNew); 1002 Tcl_SetHashValue(entryPtr, itemPtr); 1003 itemPtr->prevPtr = canvasPtr->lastItemPtr; 1004 canvasPtr->hotPtr = itemPtr; 1005 canvasPtr->hotPrevPtr = canvasPtr->lastItemPtr; 1006 if (canvasPtr->lastItemPtr == NULL) { 1007 canvasPtr->firstItemPtr = itemPtr; 1008 } else { 1009 canvasPtr->lastItemPtr->nextPtr = itemPtr; 1010 } 1011 canvasPtr->lastItemPtr = itemPtr; 1012 itemPtr->redraw_flags |= FORCE_REDRAW; 1013 EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr); 1014 canvasPtr->flags |= REPICK_NEEDED; 1015 sprintf(buf, "%d", itemPtr->id); 1016 Tcl_SetResult(interp, buf, TCL_VOLATILE); 1017 break; 1018 } 1019 case CANV_DCHARS: { 1020 int first, last; 1021 int x1,x2,y1,y2; 1022 1023 if ((objc != 4) && (objc != 5)) { 1024 Tcl_WrongNumArgs(interp, 2, objv, "tagOrId first ?last?"); 1025 result = TCL_ERROR; 1026 goto done; 1027 } 1028#ifdef USE_OLD_TAG_SEARCH 1029 for (itemPtr = StartTagSearch(canvasPtr, objv[2], &search); 1030 itemPtr != NULL; itemPtr = NextItem(&search)) { 1031#else /* USE_OLD_TAG_SEARCH */ 1032 if ((result = TagSearchScan(canvasPtr, objv[2], &searchPtr)) != TCL_OK) { 1033 goto done; 1034 } 1035 for (itemPtr = TagSearchFirst(searchPtr); 1036 itemPtr != NULL; itemPtr = TagSearchNext(searchPtr)) { 1037#endif /* USE_OLD_TAG_SEARCH */ 1038 if ((itemPtr->typePtr->indexProc == NULL) 1039 || (itemPtr->typePtr->dCharsProc == NULL)) { 1040 continue; 1041 } 1042 if (itemPtr->typePtr->alwaysRedraw & TK_CONFIG_OBJS) { 1043 result = itemPtr->typePtr->indexProc(interp, (Tk_Canvas) canvasPtr, 1044 itemPtr, (char *) objv[3], &first); 1045 } else { 1046 result = itemPtr->typePtr->indexProc(interp, (Tk_Canvas) canvasPtr, 1047 itemPtr, Tcl_GetStringFromObj(objv[3], NULL), &first); 1048 } 1049 if (result != TCL_OK) { 1050 goto done; 1051 } 1052 if (objc == 5) { 1053 if (itemPtr->typePtr->alwaysRedraw & TK_CONFIG_OBJS) { 1054 result = itemPtr->typePtr->indexProc(interp, (Tk_Canvas) canvasPtr, 1055 itemPtr, (char *) objv[4], &last); 1056 } else { 1057 result = itemPtr->typePtr->indexProc(interp, (Tk_Canvas) canvasPtr, 1058 itemPtr, Tcl_GetStringFromObj(objv[4], NULL), &last); 1059 } 1060 if (result != TCL_OK) { 1061 goto done; 1062 } 1063 } else { 1064 last = first; 1065 } 1066 1067 /* 1068 * Redraw both item's old and new areas: it's possible 1069 * that a delete could result in a new area larger than 1070 * the old area. Except if the insertProc sets the 1071 * TK_ITEM_DONT_REDRAW flag, nothing more needs to be done. 1072 */ 1073 1074 x1 = itemPtr->x1; y1 = itemPtr->y1; 1075 x2 = itemPtr->x2; y2 = itemPtr->y2; 1076 itemPtr->redraw_flags &= ~TK_ITEM_DONT_REDRAW; 1077 (*itemPtr->typePtr->dCharsProc)((Tk_Canvas) canvasPtr, 1078 itemPtr, first, last); 1079 if (!(itemPtr->redraw_flags & TK_ITEM_DONT_REDRAW)) { 1080 Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr, 1081 x1, y1, x2, y2); 1082 EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr); 1083 } 1084 itemPtr->redraw_flags &= ~TK_ITEM_DONT_REDRAW; 1085 } 1086 break; 1087 } 1088 case CANV_DELETE: { 1089 int i; 1090 Tcl_HashEntry *entryPtr; 1091 1092 for (i = 2; i < objc; i++) { 1093#ifdef USE_OLD_TAG_SEARCH 1094 for (itemPtr = StartTagSearch(canvasPtr, objv[i], &search); 1095 itemPtr != NULL; itemPtr = NextItem(&search)) { 1096#else /* USE_OLD_TAG_SEARCH */ 1097 if ((result = TagSearchScan(canvasPtr, objv[i], &searchPtr)) != TCL_OK) { 1098 goto done; 1099 } 1100 for (itemPtr = TagSearchFirst(searchPtr); 1101 itemPtr != NULL; itemPtr = TagSearchNext(searchPtr)) { 1102#endif /* USE_OLD_TAG_SEARCH */ 1103 EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr); 1104 if (canvasPtr->bindingTable != NULL) { 1105 Tk_DeleteAllBindings(canvasPtr->bindingTable, 1106 (ClientData) itemPtr); 1107 } 1108 (*itemPtr->typePtr->deleteProc)((Tk_Canvas) canvasPtr, itemPtr, 1109 canvasPtr->display); 1110 if (itemPtr->tagPtr != itemPtr->staticTagSpace) { 1111 ckfree((char *) itemPtr->tagPtr); 1112 } 1113 entryPtr = Tcl_FindHashEntry(&canvasPtr->idTable, 1114 (char *) itemPtr->id); 1115 Tcl_DeleteHashEntry(entryPtr); 1116 if (itemPtr->nextPtr != NULL) { 1117 itemPtr->nextPtr->prevPtr = itemPtr->prevPtr; 1118 } 1119 if (itemPtr->prevPtr != NULL) { 1120 itemPtr->prevPtr->nextPtr = itemPtr->nextPtr; 1121 } 1122 if (canvasPtr->firstItemPtr == itemPtr) { 1123 canvasPtr->firstItemPtr = itemPtr->nextPtr; 1124 if (canvasPtr->firstItemPtr == NULL) { 1125 canvasPtr->lastItemPtr = NULL; 1126 } 1127 } 1128 if (canvasPtr->lastItemPtr == itemPtr) { 1129 canvasPtr->lastItemPtr = itemPtr->prevPtr; 1130 } 1131 ckfree((char *) itemPtr); 1132 if (itemPtr == canvasPtr->currentItemPtr) { 1133 canvasPtr->currentItemPtr = NULL; 1134 canvasPtr->flags |= REPICK_NEEDED; 1135 } 1136 if (itemPtr == canvasPtr->newCurrentPtr) { 1137 canvasPtr->newCurrentPtr = NULL; 1138 canvasPtr->flags |= REPICK_NEEDED; 1139 } 1140 if (itemPtr == canvasPtr->textInfo.focusItemPtr) { 1141 canvasPtr->textInfo.focusItemPtr = NULL; 1142 } 1143 if (itemPtr == canvasPtr->textInfo.selItemPtr) { 1144 canvasPtr->textInfo.selItemPtr = NULL; 1145 } 1146 if ((itemPtr == canvasPtr->hotPtr) 1147 || (itemPtr == canvasPtr->hotPrevPtr)) { 1148 canvasPtr->hotPtr = NULL; 1149 } 1150 } 1151 } 1152 break; 1153 } 1154 case CANV_DTAG: { 1155 Tk_Uid tag; 1156 int i; 1157 1158 if ((objc != 3) && (objc != 4)) { 1159 Tcl_WrongNumArgs(interp, 2, objv, "tagOrId ?tagToDelete?"); 1160 result = TCL_ERROR; 1161 goto done; 1162 } 1163 if (objc == 4) { 1164 tag = Tk_GetUid(Tcl_GetStringFromObj(objv[3], NULL)); 1165 } else { 1166 tag = Tk_GetUid(Tcl_GetStringFromObj(objv[2], NULL)); 1167 } 1168#ifdef USE_OLD_TAG_SEARCH 1169 for (itemPtr = StartTagSearch(canvasPtr, objv[2], &search); 1170 itemPtr != NULL; itemPtr = NextItem(&search)) { 1171#else /* USE_OLD_TAG_SEARCH */ 1172 if ((result = TagSearchScan(canvasPtr, objv[2], &searchPtr)) != TCL_OK) { 1173 goto done; 1174 } 1175 for (itemPtr = TagSearchFirst(searchPtr); 1176 itemPtr != NULL; itemPtr = TagSearchNext(searchPtr)) { 1177#endif /* USE_OLD_TAG_SEARCH */ 1178 for (i = itemPtr->numTags-1; i >= 0; i--) { 1179 if (itemPtr->tagPtr[i] == tag) { 1180 itemPtr->tagPtr[i] = itemPtr->tagPtr[itemPtr->numTags-1]; 1181 itemPtr->numTags--; 1182 } 1183 } 1184 } 1185 break; 1186 } 1187 case CANV_FIND: { 1188 if (objc < 3) { 1189 Tcl_WrongNumArgs(interp, 2, objv, "searchCommand ?arg arg ...?"); 1190 result = TCL_ERROR; 1191 goto done; 1192 } 1193#ifdef USE_OLD_TAG_SEARCH 1194 result = FindItems(interp, canvasPtr, objc, objv, (Tcl_Obj *) NULL, 2); 1195#else /* USE_OLD_TAG_SEARCH */ 1196 result = FindItems(interp, canvasPtr, objc, objv, 1197 (Tcl_Obj *) NULL, 2, &searchPtr); 1198#endif /* USE_OLD_TAG_SEARCH */ 1199 break; 1200 } 1201 case CANV_FOCUS: { 1202 if (objc > 3) { 1203 Tcl_WrongNumArgs(interp, 2, objv, "?tagOrId?"); 1204 result = TCL_ERROR; 1205 goto done; 1206 } 1207 itemPtr = canvasPtr->textInfo.focusItemPtr; 1208 if (objc == 2) { 1209 if (itemPtr != NULL) { 1210 char buf[TCL_INTEGER_SPACE]; 1211 1212 sprintf(buf, "%d", itemPtr->id); 1213 Tcl_SetResult(interp, buf, TCL_VOLATILE); 1214 } 1215 goto done; 1216 } 1217 if ((itemPtr != NULL) && (canvasPtr->textInfo.gotFocus)) { 1218 EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr); 1219 } 1220 if (Tcl_GetStringFromObj(objv[2], NULL)[0] == 0) { 1221 canvasPtr->textInfo.focusItemPtr = NULL; 1222 goto done; 1223 } 1224#ifdef USE_OLD_TAG_SEARCH 1225 for (itemPtr = StartTagSearch(canvasPtr, objv[2], &search); 1226 itemPtr != NULL; itemPtr = NextItem(&search)) { 1227#else /* USE_OLD_TAG_SEARCH */ 1228 if ((result = TagSearchScan(canvasPtr, objv[2], &searchPtr)) != TCL_OK) { 1229 goto done; 1230 } 1231 for (itemPtr = TagSearchFirst(searchPtr); 1232 itemPtr != NULL; itemPtr = TagSearchNext(searchPtr)) { 1233#endif /* USE_OLD_TAG_SEARCH */ 1234 if (itemPtr->typePtr->icursorProc != NULL) { 1235 break; 1236 } 1237 } 1238 if (itemPtr == NULL) { 1239 goto done; 1240 } 1241 canvasPtr->textInfo.focusItemPtr = itemPtr; 1242 if (canvasPtr->textInfo.gotFocus) { 1243 EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr); 1244 } 1245 break; 1246 } 1247 case CANV_GETTAGS: { 1248 if (objc != 3) { 1249 Tcl_WrongNumArgs(interp, 2, objv, "tagOrId"); 1250 result = TCL_ERROR; 1251 goto done; 1252 } 1253#ifdef USE_OLD_TAG_SEARCH 1254 itemPtr = StartTagSearch(canvasPtr, objv[2], &search); 1255#else /* USE_OLD_TAG_SEARCH */ 1256 if ((result = TagSearchScan(canvasPtr, objv[2], &searchPtr)) != TCL_OK) { 1257 goto done; 1258 } 1259 itemPtr = TagSearchFirst(searchPtr); 1260#endif /* USE_OLD_TAG_SEARCH */ 1261 if (itemPtr != NULL) { 1262 int i; 1263 for (i = 0; i < itemPtr->numTags; i++) { 1264 Tcl_AppendElement(interp, (char *) itemPtr->tagPtr[i]); 1265 } 1266 } 1267 break; 1268 } 1269 case CANV_ICURSOR: { 1270 int index; 1271 1272 if (objc != 4) { 1273 Tcl_WrongNumArgs(interp, 2, objv, "tagOrId index"); 1274 result = TCL_ERROR; 1275 goto done; 1276 } 1277#ifdef USE_OLD_TAG_SEARCH 1278 for (itemPtr = StartTagSearch(canvasPtr, objv[2], &search); 1279 itemPtr != NULL; itemPtr = NextItem(&search)) { 1280#else /* USE_OLD_TAG_SEARCH */ 1281 if ((result = TagSearchScan(canvasPtr, objv[2], &searchPtr)) != TCL_OK) { 1282 goto done; 1283 } 1284 for (itemPtr = TagSearchFirst(searchPtr); 1285 itemPtr != NULL; itemPtr = TagSearchNext(searchPtr)) { 1286#endif /* USE_OLD_TAG_SEARCH */ 1287 if ((itemPtr->typePtr->indexProc == NULL) 1288 || (itemPtr->typePtr->icursorProc == NULL)) { 1289 goto done; 1290 } 1291 if (itemPtr->typePtr->alwaysRedraw & TK_CONFIG_OBJS) { 1292 result = itemPtr->typePtr->indexProc(interp, (Tk_Canvas) canvasPtr, 1293 itemPtr, (char *) objv[3], &index); 1294 } else { 1295 result = itemPtr->typePtr->indexProc(interp, (Tk_Canvas) canvasPtr, 1296 itemPtr, Tcl_GetStringFromObj(objv[3], NULL), &index); 1297 } 1298 if (result != TCL_OK) { 1299 goto done; 1300 } 1301 (*itemPtr->typePtr->icursorProc)((Tk_Canvas) canvasPtr, itemPtr, 1302 index); 1303 if ((itemPtr == canvasPtr->textInfo.focusItemPtr) 1304 && (canvasPtr->textInfo.cursorOn)) { 1305 EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr); 1306 } 1307 } 1308 break; 1309 } 1310 case CANV_INDEX: { 1311 1312 int index; 1313 char buf[TCL_INTEGER_SPACE]; 1314 1315 if (objc != 4) { 1316 Tcl_WrongNumArgs(interp, 2, objv, "tagOrId string"); 1317 result = TCL_ERROR; 1318 goto done; 1319 } 1320#ifdef USE_OLD_TAG_SEARCH 1321 for (itemPtr = StartTagSearch(canvasPtr, objv[2], &search); 1322 itemPtr != NULL; itemPtr = NextItem(&search)) { 1323#else /* USE_OLD_TAG_SEARCH */ 1324 if ((result = TagSearchScan(canvasPtr, objv[2], &searchPtr)) != TCL_OK) { 1325 goto done; 1326 } 1327 for (itemPtr = TagSearchFirst(searchPtr); 1328 itemPtr != NULL; itemPtr = TagSearchNext(searchPtr)) { 1329#endif /* USE_OLD_TAG_SEARCH */ 1330 if (itemPtr->typePtr->indexProc != NULL) { 1331 break; 1332 } 1333 } 1334 if (itemPtr == NULL) { 1335 Tcl_AppendResult(interp, "can't find an indexable item \"", 1336 Tcl_GetStringFromObj(objv[2], NULL), "\"", (char *) NULL); 1337 result = TCL_ERROR; 1338 goto done; 1339 } 1340 if (itemPtr->typePtr->alwaysRedraw & TK_CONFIG_OBJS) { 1341 result = itemPtr->typePtr->indexProc(interp, (Tk_Canvas) canvasPtr, 1342 itemPtr, (char *) objv[3], &index); 1343 } else { 1344 result = itemPtr->typePtr->indexProc(interp, (Tk_Canvas) canvasPtr, 1345 itemPtr, Tcl_GetStringFromObj(objv[3], NULL), &index); 1346 } 1347 if (result != TCL_OK) { 1348 goto done; 1349 } 1350 sprintf(buf, "%d", index); 1351 Tcl_SetResult(interp, buf, TCL_VOLATILE); 1352 break; 1353 } 1354 case CANV_INSERT: { 1355 int beforeThis; 1356 int x1,x2,y1,y2; 1357 1358 if (objc != 5) { 1359 Tcl_WrongNumArgs(interp, 2, objv, "tagOrId beforeThis string"); 1360 result = TCL_ERROR; 1361 goto done; 1362 } 1363#ifdef USE_OLD_TAG_SEARCH 1364 for (itemPtr = StartTagSearch(canvasPtr, objv[2], &search); 1365 itemPtr != NULL; itemPtr = NextItem(&search)) { 1366#else /* USE_OLD_TAG_SEARCH */ 1367 if ((result = TagSearchScan(canvasPtr, objv[2], &searchPtr)) != TCL_OK) { 1368 goto done; 1369 } 1370 for (itemPtr = TagSearchFirst(searchPtr); 1371 itemPtr != NULL; itemPtr = TagSearchNext(searchPtr)) { 1372#endif /* USE_OLD_TAG_SEARCH */ 1373 if ((itemPtr->typePtr->indexProc == NULL) 1374 || (itemPtr->typePtr->insertProc == NULL)) { 1375 continue; 1376 } 1377 if (itemPtr->typePtr->alwaysRedraw & TK_CONFIG_OBJS) { 1378 result = itemPtr->typePtr->indexProc(interp, (Tk_Canvas) canvasPtr, 1379 itemPtr, (char *) objv[3], &beforeThis); 1380 } else { 1381 result = itemPtr->typePtr->indexProc(interp, (Tk_Canvas) canvasPtr, 1382 itemPtr, Tcl_GetStringFromObj(objv[3], NULL), &beforeThis); 1383 } 1384 if (result != TCL_OK) { 1385 goto done; 1386 } 1387 1388 /* 1389 * Redraw both item's old and new areas: it's possible 1390 * that an insertion could result in a new area either 1391 * larger or smaller than the old area. Except if the 1392 * insertProc sets the TK_ITEM_DONT_REDRAW flag, nothing 1393 * more needs to be done. 1394 */ 1395 1396 x1 = itemPtr->x1; y1 = itemPtr->y1; 1397 x2 = itemPtr->x2; y2 = itemPtr->y2; 1398 itemPtr->redraw_flags &= ~TK_ITEM_DONT_REDRAW; 1399 if (itemPtr->typePtr->alwaysRedraw & TK_CONFIG_OBJS) { 1400 (*itemPtr->typePtr->insertProc)((Tk_Canvas) canvasPtr, 1401 itemPtr, beforeThis, (char *) objv[4]); 1402 } else { 1403 (*itemPtr->typePtr->insertProc)((Tk_Canvas) canvasPtr, 1404 itemPtr, beforeThis, Tcl_GetStringFromObj(objv[4], NULL)); 1405 } 1406 if (!(itemPtr->redraw_flags & TK_ITEM_DONT_REDRAW)) { 1407 Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr, 1408 x1, y1, x2, y2); 1409 EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr); 1410 } 1411 itemPtr->redraw_flags &= ~TK_ITEM_DONT_REDRAW; 1412 } 1413 break; 1414 } 1415 case CANV_ITEMCGET: { 1416 if (objc != 4) { 1417 Tcl_WrongNumArgs(interp, 2, objv, "tagOrId option"); 1418 result = TCL_ERROR; 1419 goto done; 1420 } 1421#ifdef USE_OLD_TAG_SEARCH 1422 itemPtr = StartTagSearch(canvasPtr, objv[2], &search); 1423#else /* USE_OLD_TAG_SEARCH */ 1424 if ((result = TagSearchScan(canvasPtr, objv[2], &searchPtr)) != TCL_OK) { 1425 goto done; 1426 } 1427 itemPtr = TagSearchFirst(searchPtr); 1428#endif /* USE_OLD_TAG_SEARCH */ 1429 if (itemPtr != NULL) { 1430 result = Tk_ConfigureValue(canvasPtr->interp, canvasPtr->tkwin, 1431 itemPtr->typePtr->configSpecs, (char *) itemPtr, 1432 Tcl_GetStringFromObj(objv[3], NULL), 0); 1433 } 1434 break; 1435 } 1436 case CANV_ITEMCONFIGURE: { 1437 if (objc < 3) { 1438 Tcl_WrongNumArgs(interp, 2, objv, "tagOrId ?option value ...?"); 1439 result = TCL_ERROR; 1440 goto done; 1441 } 1442#ifdef USE_OLD_TAG_SEARCH 1443 for (itemPtr = StartTagSearch(canvasPtr, objv[2], &search); 1444 itemPtr != NULL; itemPtr = NextItem(&search)) { 1445#else /* USE_OLD_TAG_SEARCH */ 1446 if ((result = TagSearchScan(canvasPtr, objv[2], &searchPtr)) != TCL_OK) { 1447 goto done; 1448 } 1449 for (itemPtr = TagSearchFirst(searchPtr); 1450 itemPtr != NULL; itemPtr = TagSearchNext(searchPtr)) { 1451#endif /* USE_OLD_TAG_SEARCH */ 1452 if (objc == 3) { 1453 result = Tk_ConfigureInfo(canvasPtr->interp, canvasPtr->tkwin, 1454 itemPtr->typePtr->configSpecs, (char *) itemPtr, 1455 (char *) NULL, 0); 1456 } else if (objc == 4) { 1457 result = Tk_ConfigureInfo(canvasPtr->interp, canvasPtr->tkwin, 1458 itemPtr->typePtr->configSpecs, (char *) itemPtr, 1459 Tcl_GetString(objv[3]), 0); 1460 } else { 1461 EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr); 1462 if (itemPtr->typePtr->alwaysRedraw & TK_CONFIG_OBJS) { 1463 result = (*itemPtr->typePtr->configProc)(interp, 1464 (Tk_Canvas) canvasPtr, itemPtr, objc-3, objv+3, 1465 TK_CONFIG_ARGV_ONLY); 1466 } else { 1467 CONST char **args = GetStringsFromObjs(objc-3, objv+3); 1468 result = (*itemPtr->typePtr->configProc)(interp, 1469 (Tk_Canvas) canvasPtr, itemPtr, objc-3, (Tcl_Obj **) args, 1470 TK_CONFIG_ARGV_ONLY); 1471 if (args) ckfree((char *) args); 1472 } 1473 EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr); 1474 canvasPtr->flags |= REPICK_NEEDED; 1475 } 1476 if ((result != TCL_OK) || (objc < 5)) { 1477 break; 1478 } 1479 } 1480 break; 1481 } 1482 case CANV_LOWER: { 1483 Tk_Item *itemPtr; 1484 1485 if ((objc != 3) && (objc != 4)) { 1486 Tcl_WrongNumArgs(interp, 2, objv, "tagOrId ?belowThis?"); 1487 result = TCL_ERROR; 1488 goto done; 1489 } 1490 1491 /* 1492 * First find the item just after which we'll insert the 1493 * named items. 1494 */ 1495 1496 if (objc == 3) { 1497 itemPtr = NULL; 1498 } else { 1499#ifdef USE_OLD_TAG_SEARCH 1500 itemPtr = StartTagSearch(canvasPtr, objv[3], &search); 1501#else /* USE_OLD_TAG_SEARCH */ 1502 if ((result = TagSearchScan(canvasPtr, objv[3], &searchPtr)) != TCL_OK) { 1503 goto done; 1504 } 1505 itemPtr = TagSearchFirst(searchPtr); 1506#endif /* USE_OLD_TAG_SEARCH */ 1507 if (itemPtr == NULL) { 1508 Tcl_AppendResult(interp, "tag \"", Tcl_GetString(objv[3]), 1509 "\" doesn't match any items", (char *) NULL); 1510 goto done; 1511 } 1512 itemPtr = itemPtr->prevPtr; 1513 } 1514#ifdef USE_OLD_TAG_SEARCH 1515 RelinkItems(canvasPtr, objv[2], itemPtr); 1516#else /* USE_OLD_TAG_SEARCH */ 1517 if ((result = RelinkItems(canvasPtr, objv[2], itemPtr, &searchPtr)) != TCL_OK) { 1518 goto done; 1519 } 1520#endif /* USE_OLD_TAG_SEARCH */ 1521 break; 1522 } 1523 case CANV_MOVE: { 1524 double xAmount, yAmount; 1525 1526 if (objc != 5) { 1527 Tcl_WrongNumArgs(interp, 2, objv, "tagOrId xAmount yAmount"); 1528 result = TCL_ERROR; 1529 goto done; 1530 } 1531 if ((Tk_CanvasGetCoordFromObj(interp, (Tk_Canvas) canvasPtr, objv[3], 1532 &xAmount) != TCL_OK) || (Tk_CanvasGetCoordFromObj(interp, 1533 (Tk_Canvas) canvasPtr, objv[4], &yAmount) != TCL_OK)) { 1534 result = TCL_ERROR; 1535 goto done; 1536 } 1537#ifdef USE_OLD_TAG_SEARCH 1538 for (itemPtr = StartTagSearch(canvasPtr, objv[2], &search); 1539 itemPtr != NULL; itemPtr = NextItem(&search)) { 1540#else /* USE_OLD_TAG_SEARCH */ 1541 if ((result = TagSearchScan(canvasPtr, objv[2], &searchPtr)) != TCL_OK) { 1542 goto done; 1543 } 1544 for (itemPtr = TagSearchFirst(searchPtr); 1545 itemPtr != NULL; itemPtr = TagSearchNext(searchPtr)) { 1546#endif /* USE_OLD_TAG_SEARCH */ 1547 EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr); 1548 (void) (*itemPtr->typePtr->translateProc)((Tk_Canvas) canvasPtr, 1549 itemPtr, xAmount, yAmount); 1550 EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr); 1551 canvasPtr->flags |= REPICK_NEEDED; 1552 } 1553 break; 1554 } 1555 case CANV_POSTSCRIPT: { 1556 CONST char **args = GetStringsFromObjs(objc, objv); 1557 result = TkCanvPostscriptCmd(canvasPtr, interp, objc, args); 1558 if (args) ckfree((char *) args); 1559 break; 1560 } 1561 case CANV_RAISE: { 1562 Tk_Item *prevPtr; 1563 1564 if ((objc != 3) && (objc != 4)) { 1565 Tcl_WrongNumArgs(interp, 2, objv, "tagOrId ?aboveThis?"); 1566 result = TCL_ERROR; 1567 goto done; 1568 } 1569 1570 /* 1571 * First find the item just after which we'll insert the 1572 * named items. 1573 */ 1574 1575 if (objc == 3) { 1576 prevPtr = canvasPtr->lastItemPtr; 1577 } else { 1578 prevPtr = NULL; 1579#ifdef USE_OLD_TAG_SEARCH 1580 for (itemPtr = StartTagSearch(canvasPtr, objv[3], &search); 1581 itemPtr != NULL; itemPtr = NextItem(&search)) { 1582#else /* USE_OLD_TAG_SEARCH */ 1583 if ((result = TagSearchScan(canvasPtr, objv[3], &searchPtr)) != TCL_OK) { 1584 goto done; 1585 } 1586 for (itemPtr = TagSearchFirst(searchPtr); 1587 itemPtr != NULL; itemPtr = TagSearchNext(searchPtr)) { 1588#endif /* USE_OLD_TAG_SEARCH */ 1589 prevPtr = itemPtr; 1590 } 1591 if (prevPtr == NULL) { 1592 Tcl_AppendResult(interp, "tagOrId \"", Tcl_GetStringFromObj(objv[3], NULL), 1593 "\" doesn't match any items", (char *) NULL); 1594 result = TCL_ERROR; 1595 goto done; 1596 } 1597 } 1598#ifdef USE_OLD_TAG_SEARCH 1599 RelinkItems(canvasPtr, objv[2], prevPtr); 1600#else /* USE_OLD_TAG_SEARCH */ 1601 result = RelinkItems(canvasPtr, objv[2], prevPtr, &searchPtr); 1602 if (result != TCL_OK) { 1603 goto done; 1604 } 1605#endif /* USE_OLD_TAG_SEARCH */ 1606 break; 1607 } 1608 case CANV_SCALE: { 1609 double xOrigin, yOrigin, xScale, yScale; 1610 1611 if (objc != 7) { 1612 Tcl_WrongNumArgs(interp, 2, objv, "tagOrId xOrigin yOrigin xScale yScale"); 1613 result = TCL_ERROR; 1614 goto done; 1615 } 1616 if ((Tk_CanvasGetCoordFromObj(interp, (Tk_Canvas) canvasPtr, 1617 objv[3], &xOrigin) != TCL_OK) 1618 || (Tk_CanvasGetCoordFromObj(interp, (Tk_Canvas) canvasPtr, 1619 objv[4], &yOrigin) != TCL_OK) 1620 || (Tcl_GetDoubleFromObj(interp, objv[5], &xScale) != TCL_OK) 1621 || (Tcl_GetDoubleFromObj(interp, objv[6], &yScale) != TCL_OK)) { 1622 result = TCL_ERROR; 1623 goto done; 1624 } 1625 if ((xScale == 0.0) || (yScale == 0.0)) { 1626 Tcl_SetResult(interp, "scale factor cannot be zero", TCL_STATIC); 1627 result = TCL_ERROR; 1628 goto done; 1629 } 1630#ifdef USE_OLD_TAG_SEARCH 1631 for (itemPtr = StartTagSearch(canvasPtr, objv[2], &search); 1632 itemPtr != NULL; itemPtr = NextItem(&search)) { 1633#else /* USE_OLD_TAG_SEARCH */ 1634 if ((result = TagSearchScan(canvasPtr, objv[2], &searchPtr)) != TCL_OK) { 1635 goto done; 1636 } 1637 for (itemPtr = TagSearchFirst(searchPtr); 1638 itemPtr != NULL; itemPtr = TagSearchNext(searchPtr)) { 1639#endif /* USE_OLD_TAG_SEARCH */ 1640 EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr); 1641 (void) (*itemPtr->typePtr->scaleProc)((Tk_Canvas) canvasPtr, 1642 itemPtr, xOrigin, yOrigin, xScale, yScale); 1643 EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr); 1644 canvasPtr->flags |= REPICK_NEEDED; 1645 } 1646 break; 1647 } 1648 case CANV_SCAN: { 1649 int x, y, gain=10; 1650 static CONST char *optionStrings[] = { 1651 "mark", "dragto", NULL 1652 }; 1653 1654 if (objc < 5) { 1655 Tcl_WrongNumArgs(interp, 2, objv, "mark|dragto x y ?dragGain?"); 1656 result = TCL_ERROR; 1657 } else if (Tcl_GetIndexFromObj(interp, objv[2], optionStrings, 1658 "scan option", 0, &index) != TCL_OK) { 1659 result = TCL_ERROR; 1660 } else if ((objc != 5) && (objc != 5+index)) { 1661 Tcl_WrongNumArgs(interp, 3, objv, index?"x y ?gain?":"x y"); 1662 result = TCL_ERROR; 1663 } else if ((Tcl_GetIntFromObj(interp, objv[3], &x) != TCL_OK) 1664 || (Tcl_GetIntFromObj(interp, objv[4], &y) != TCL_OK)){ 1665 result = TCL_ERROR; 1666 } else if ((objc == 6) && 1667 (Tcl_GetIntFromObj(interp, objv[5], &gain) != TCL_OK)) { 1668 result = TCL_ERROR; 1669 } else if (!index) { 1670 canvasPtr->scanX = x; 1671 canvasPtr->scanXOrigin = canvasPtr->xOrigin; 1672 canvasPtr->scanY = y; 1673 canvasPtr->scanYOrigin = canvasPtr->yOrigin; 1674 } else { 1675 int newXOrigin, newYOrigin, tmp; 1676 1677 /* 1678 * Compute a new view origin for the canvas, amplifying the 1679 * mouse motion. 1680 */ 1681 1682 tmp = canvasPtr->scanXOrigin - gain*(x - canvasPtr->scanX) 1683 - canvasPtr->scrollX1; 1684 newXOrigin = canvasPtr->scrollX1 + tmp; 1685 tmp = canvasPtr->scanYOrigin - gain*(y - canvasPtr->scanY) 1686 - canvasPtr->scrollY1; 1687 newYOrigin = canvasPtr->scrollY1 + tmp; 1688 CanvasSetOrigin(canvasPtr, newXOrigin, newYOrigin); 1689 } 1690 break; 1691 } 1692 case CANV_SELECT: { 1693 int index, optionindex; 1694 static CONST char *optionStrings[] = { 1695 "adjust", "clear", "from", "item", "to", NULL 1696 }; 1697 enum options { 1698 CANV_ADJUST, CANV_CLEAR, CANV_FROM, CANV_ITEM, CANV_TO 1699 }; 1700 1701 if (objc < 3) { 1702 Tcl_WrongNumArgs(interp, 2, objv, "option ?tagOrId? ?arg?"); 1703 result = TCL_ERROR; 1704 goto done; 1705 } 1706 if (objc >= 4) { 1707#ifdef USE_OLD_TAG_SEARCH 1708 for (itemPtr = StartTagSearch(canvasPtr, objv[3], &search); 1709 itemPtr != NULL; itemPtr = NextItem(&search)) { 1710#else /* USE_OLD_TAG_SEARCH */ 1711 if ((result = TagSearchScan(canvasPtr, objv[3], &searchPtr)) != TCL_OK) { 1712 goto done; 1713 } 1714 for (itemPtr = TagSearchFirst(searchPtr); 1715 itemPtr != NULL; itemPtr = TagSearchNext(searchPtr)) { 1716#endif /* USE_OLD_TAG_SEARCH */ 1717 if ((itemPtr->typePtr->indexProc != NULL) 1718 && (itemPtr->typePtr->selectionProc != NULL)){ 1719 break; 1720 } 1721 } 1722 if (itemPtr == NULL) { 1723 Tcl_AppendResult(interp, 1724 "can't find an indexable and selectable item \"", 1725 Tcl_GetStringFromObj(objv[3], NULL), "\"", (char *) NULL); 1726 result = TCL_ERROR; 1727 goto done; 1728 } 1729 } 1730 if (objc == 5) { 1731 if (itemPtr->typePtr->alwaysRedraw & TK_CONFIG_OBJS) { 1732 result = itemPtr->typePtr->indexProc(interp, (Tk_Canvas) canvasPtr, 1733 itemPtr, (char *) objv[4], &index); 1734 } else { 1735 result = itemPtr->typePtr->indexProc(interp, (Tk_Canvas) canvasPtr, 1736 itemPtr, Tcl_GetStringFromObj(objv[4], NULL), &index); 1737 } 1738 if (result != TCL_OK) { 1739 goto done; 1740 } 1741 } 1742 if (Tcl_GetIndexFromObj(interp, objv[2], optionStrings, "select option", 0, 1743 &optionindex) != TCL_OK) { 1744 result = TCL_ERROR; 1745 goto done; 1746 } 1747 switch ((enum options) optionindex) { 1748 case CANV_ADJUST: { 1749 if (objc != 5) { 1750 Tcl_WrongNumArgs(interp, 3, objv, "tagOrId index"); 1751 result = TCL_ERROR; 1752 goto done; 1753 } 1754 if (canvasPtr->textInfo.selItemPtr == itemPtr) { 1755 if (index < (canvasPtr->textInfo.selectFirst 1756 + canvasPtr->textInfo.selectLast)/2) { 1757 canvasPtr->textInfo.selectAnchor = 1758 canvasPtr->textInfo.selectLast + 1; 1759 } else { 1760 canvasPtr->textInfo.selectAnchor = 1761 canvasPtr->textInfo.selectFirst; 1762 } 1763 } 1764 CanvasSelectTo(canvasPtr, itemPtr, index); 1765 break; 1766 } 1767 case CANV_CLEAR: { 1768 if (objc != 3) { 1769 Tcl_AppendResult(interp, 3, objv, (char *) NULL); 1770 result = TCL_ERROR; 1771 goto done; 1772 } 1773 if (canvasPtr->textInfo.selItemPtr != NULL) { 1774 EventuallyRedrawItem((Tk_Canvas) canvasPtr, 1775 canvasPtr->textInfo.selItemPtr); 1776 canvasPtr->textInfo.selItemPtr = NULL; 1777 } 1778 goto done; 1779 break; 1780 } 1781 case CANV_FROM: { 1782 if (objc != 5) { 1783 Tcl_WrongNumArgs(interp, 3, objv, "tagOrId index"); 1784 result = TCL_ERROR; 1785 goto done; 1786 } 1787 canvasPtr->textInfo.anchorItemPtr = itemPtr; 1788 canvasPtr->textInfo.selectAnchor = index; 1789 break; 1790 } 1791 case CANV_ITEM: { 1792 if (objc != 3) { 1793 Tcl_WrongNumArgs(interp, 3, objv, (char *) NULL); 1794 result = TCL_ERROR; 1795 goto done; 1796 } 1797 if (canvasPtr->textInfo.selItemPtr != NULL) { 1798 char buf[TCL_INTEGER_SPACE]; 1799 1800 sprintf(buf, "%d", canvasPtr->textInfo.selItemPtr->id); 1801 Tcl_SetResult(interp, buf, TCL_VOLATILE); 1802 } 1803 break; 1804 } 1805 case CANV_TO: { 1806 if (objc != 5) { 1807 Tcl_WrongNumArgs(interp, 2, objv, "tagOrId index"); 1808 result = TCL_ERROR; 1809 goto done; 1810 } 1811 CanvasSelectTo(canvasPtr, itemPtr, index); 1812 break; 1813 } 1814 } 1815 break; 1816 } 1817 case CANV_TYPE: { 1818 if (objc != 3) { 1819 Tcl_WrongNumArgs(interp, 2, objv, "tag"); 1820 result = TCL_ERROR; 1821 goto done; 1822 } 1823#ifdef USE_OLD_TAG_SEARCH 1824 itemPtr = StartTagSearch(canvasPtr, objv[2], &search); 1825#else /* USE_OLD_TAG_SEARCH */ 1826 if ((result = TagSearchScan(canvasPtr, objv[2], &searchPtr)) != TCL_OK) { 1827 goto done; 1828 } 1829 itemPtr = TagSearchFirst(searchPtr); 1830#endif /* USE_OLD_TAG_SEARCH */ 1831 if (itemPtr != NULL) { 1832 Tcl_SetResult(interp, itemPtr->typePtr->name, TCL_STATIC); 1833 } 1834 break; 1835 } 1836 case CANV_XVIEW: { 1837 int count, type; 1838 int newX = 0; /* Initialization needed only to prevent 1839 * gcc warnings. */ 1840 double fraction; 1841 1842 if (objc == 2) { 1843 Tcl_SetObjResult(interp, ScrollFractions( 1844 canvasPtr->xOrigin + canvasPtr->inset, 1845 canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin) 1846 - canvasPtr->inset, canvasPtr->scrollX1, 1847 canvasPtr->scrollX2)); 1848 } else { 1849 CONST char **args = GetStringsFromObjs(objc, objv); 1850 type = Tk_GetScrollInfo(interp, objc, args, &fraction, &count); 1851 if (args) ckfree((char *) args); 1852 switch (type) { 1853 case TK_SCROLL_ERROR: 1854 result = TCL_ERROR; 1855 goto done; 1856 case TK_SCROLL_MOVETO: 1857 newX = canvasPtr->scrollX1 - canvasPtr->inset 1858 + (int) (fraction * (canvasPtr->scrollX2 1859 - canvasPtr->scrollX1) + 0.5); 1860 break; 1861 case TK_SCROLL_PAGES: 1862 newX = (int) (canvasPtr->xOrigin + count * .9 1863 * (Tk_Width(canvasPtr->tkwin) - 2*canvasPtr->inset)); 1864 break; 1865 case TK_SCROLL_UNITS: 1866 if (canvasPtr->xScrollIncrement > 0) { 1867 newX = canvasPtr->xOrigin 1868 + count*canvasPtr->xScrollIncrement; 1869 } else { 1870 newX = (int) (canvasPtr->xOrigin + count * .1 1871 * (Tk_Width(canvasPtr->tkwin) 1872 - 2*canvasPtr->inset)); 1873 } 1874 break; 1875 } 1876 CanvasSetOrigin(canvasPtr, newX, canvasPtr->yOrigin); 1877 } 1878 break; 1879 } 1880 case CANV_YVIEW: { 1881 int count, type; 1882 int newY = 0; /* Initialization needed only to prevent 1883 * gcc warnings. */ 1884 double fraction; 1885 1886 if (objc == 2) { 1887 Tcl_SetObjResult(interp,ScrollFractions(\ 1888 canvasPtr->yOrigin + canvasPtr->inset, 1889 canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin) 1890 - canvasPtr->inset, canvasPtr->scrollY1, 1891 canvasPtr->scrollY2)); 1892 } else { 1893 CONST char **args = GetStringsFromObjs(objc, objv); 1894 type = Tk_GetScrollInfo(interp, objc, args, &fraction, &count); 1895 if (args) ckfree((char *) args); 1896 switch (type) { 1897 case TK_SCROLL_ERROR: 1898 result = TCL_ERROR; 1899 goto done; 1900 case TK_SCROLL_MOVETO: 1901 newY = canvasPtr->scrollY1 - canvasPtr->inset 1902 + (int) (fraction*(canvasPtr->scrollY2 1903 - canvasPtr->scrollY1) + 0.5); 1904 break; 1905 case TK_SCROLL_PAGES: 1906 newY = (int) (canvasPtr->yOrigin + count * .9 1907 * (Tk_Height(canvasPtr->tkwin) 1908 - 2*canvasPtr->inset)); 1909 break; 1910 case TK_SCROLL_UNITS: 1911 if (canvasPtr->yScrollIncrement > 0) { 1912 newY = canvasPtr->yOrigin 1913 + count*canvasPtr->yScrollIncrement; 1914 } else { 1915 newY = (int) (canvasPtr->yOrigin + count * .1 1916 * (Tk_Height(canvasPtr->tkwin) 1917 - 2*canvasPtr->inset)); 1918 } 1919 break; 1920 } 1921 CanvasSetOrigin(canvasPtr, canvasPtr->xOrigin, newY); 1922 } 1923 break; 1924 } 1925 } 1926 done: 1927#ifndef USE_OLD_TAG_SEARCH 1928 TagSearchDestroy(searchPtr); 1929#endif /* not USE_OLD_TAG_SEARCH */ 1930 Tcl_Release((ClientData) canvasPtr); 1931 return result; 1932} 1933 1934/* 1935 *---------------------------------------------------------------------- 1936 * 1937 * DestroyCanvas -- 1938 * 1939 * This procedure is invoked by Tcl_EventuallyFree or Tcl_Release 1940 * to clean up the internal structure of a canvas at a safe time 1941 * (when no-one is using it anymore). 1942 * 1943 * Results: 1944 * None. 1945 * 1946 * Side effects: 1947 * Everything associated with the canvas is freed up. 1948 * 1949 *---------------------------------------------------------------------- 1950 */ 1951 1952static void 1953DestroyCanvas(memPtr) 1954 char *memPtr; /* Info about canvas widget. */ 1955{ 1956 TkCanvas *canvasPtr = (TkCanvas *) memPtr; 1957 Tk_Item *itemPtr; 1958#ifndef USE_OLD_TAG_SEARCH 1959 TagSearchExpr *expr, *next; 1960#endif 1961 1962 /* 1963 * Free up all of the items in the canvas. 1964 */ 1965 1966 for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL; 1967 itemPtr = canvasPtr->firstItemPtr) { 1968 canvasPtr->firstItemPtr = itemPtr->nextPtr; 1969 (*itemPtr->typePtr->deleteProc)((Tk_Canvas) canvasPtr, itemPtr, 1970 canvasPtr->display); 1971 if (itemPtr->tagPtr != itemPtr->staticTagSpace) { 1972 ckfree((char *) itemPtr->tagPtr); 1973 } 1974 ckfree((char *) itemPtr); 1975 } 1976 1977 /* 1978 * Free up all the stuff that requires special handling, 1979 * then let Tk_FreeOptions handle all the standard option-related 1980 * stuff. 1981 */ 1982 1983 Tcl_DeleteHashTable(&canvasPtr->idTable); 1984 if (canvasPtr->pixmapGC != None) { 1985 Tk_FreeGC(canvasPtr->display, canvasPtr->pixmapGC); 1986 } 1987#ifndef USE_OLD_TAG_SEARCH 1988 expr = canvasPtr->bindTagExprs; 1989 while (expr) { 1990 next = expr->next; 1991 TagSearchExprDestroy(expr); 1992 expr = next; 1993 } 1994#endif 1995 Tcl_DeleteTimerHandler(canvasPtr->insertBlinkHandler); 1996 if (canvasPtr->bindingTable != NULL) { 1997 Tk_DeleteBindingTable(canvasPtr->bindingTable); 1998 } 1999 Tk_FreeOptions(configSpecs, (char *) canvasPtr, canvasPtr->display, 0); 2000 canvasPtr->tkwin = NULL; 2001 ckfree((char *) canvasPtr); 2002} 2003 2004/* 2005 *---------------------------------------------------------------------- 2006 * 2007 * ConfigureCanvas -- 2008 * 2009 * This procedure is called to process an objv/objc list, plus 2010 * the Tk option database, in order to configure (or 2011 * reconfigure) a canvas widget. 2012 * 2013 * Results: 2014 * The return value is a standard Tcl result. If TCL_ERROR is 2015 * returned, then the interp's result contains an error message. 2016 * 2017 * Side effects: 2018 * Configuration information, such as colors, border width, 2019 * etc. get set for canvasPtr; old resources get freed, 2020 * if there were any. 2021 * 2022 *---------------------------------------------------------------------- 2023 */ 2024 2025static int 2026ConfigureCanvas(interp, canvasPtr, objc, objv, flags) 2027 Tcl_Interp *interp; /* Used for error reporting. */ 2028 TkCanvas *canvasPtr; /* Information about widget; may or may 2029 * not already have values for some fields. */ 2030 int objc; /* Number of valid entries in objv. */ 2031 Tcl_Obj *CONST objv[]; /* Argument objects. */ 2032 int flags; /* Flags to pass to Tk_ConfigureWidget. */ 2033{ 2034 XGCValues gcValues; 2035 GC new; 2036 2037 if (Tk_ConfigureWidget(interp, canvasPtr->tkwin, configSpecs, 2038 objc, (CONST char **) objv, (char *) canvasPtr, 2039 flags|TK_CONFIG_OBJS) != TCL_OK) { 2040 return TCL_ERROR; 2041 } 2042 2043 /* 2044 * A few options need special processing, such as setting the 2045 * background from a 3-D border and creating a GC for copying 2046 * bits to the screen. 2047 */ 2048 2049 Tk_SetBackgroundFromBorder(canvasPtr->tkwin, canvasPtr->bgBorder); 2050 2051 if (canvasPtr->highlightWidth < 0) { 2052 canvasPtr->highlightWidth = 0; 2053 } 2054 canvasPtr->inset = canvasPtr->borderWidth + canvasPtr->highlightWidth; 2055 2056 gcValues.function = GXcopy; 2057 gcValues.graphics_exposures = False; 2058 gcValues.foreground = Tk_3DBorderColor(canvasPtr->bgBorder)->pixel; 2059 new = Tk_GetGC(canvasPtr->tkwin, 2060 GCFunction|GCGraphicsExposures|GCForeground, &gcValues); 2061 if (canvasPtr->pixmapGC != None) { 2062 Tk_FreeGC(canvasPtr->display, canvasPtr->pixmapGC); 2063 } 2064 canvasPtr->pixmapGC = new; 2065 2066 /* 2067 * Reset the desired dimensions for the window. 2068 */ 2069 2070 Tk_GeometryRequest(canvasPtr->tkwin, canvasPtr->width + 2*canvasPtr->inset, 2071 canvasPtr->height + 2*canvasPtr->inset); 2072 2073 /* 2074 * Restart the cursor timing sequence in case the on-time or off-time 2075 * just changed. 2076 */ 2077 2078 if (canvasPtr->textInfo.gotFocus) { 2079 CanvasFocusProc(canvasPtr, 1); 2080 } 2081 2082 /* 2083 * Recompute the scroll region. 2084 */ 2085 2086 canvasPtr->scrollX1 = 0; 2087 canvasPtr->scrollY1 = 0; 2088 canvasPtr->scrollX2 = 0; 2089 canvasPtr->scrollY2 = 0; 2090 if (canvasPtr->regionString != NULL) { 2091 int argc2; 2092 CONST char **argv2; 2093 2094 if (Tcl_SplitList(canvasPtr->interp, canvasPtr->regionString, 2095 &argc2, &argv2) != TCL_OK) { 2096 return TCL_ERROR; 2097 } 2098 if (argc2 != 4) { 2099 Tcl_AppendResult(interp, "bad scrollRegion \"", 2100 canvasPtr->regionString, "\"", (char *) NULL); 2101 badRegion: 2102 ckfree(canvasPtr->regionString); 2103 ckfree((char *) argv2); 2104 canvasPtr->regionString = NULL; 2105 return TCL_ERROR; 2106 } 2107 if ((Tk_GetPixels(canvasPtr->interp, canvasPtr->tkwin, 2108 argv2[0], &canvasPtr->scrollX1) != TCL_OK) 2109 || (Tk_GetPixels(canvasPtr->interp, canvasPtr->tkwin, 2110 argv2[1], &canvasPtr->scrollY1) != TCL_OK) 2111 || (Tk_GetPixels(canvasPtr->interp, canvasPtr->tkwin, 2112 argv2[2], &canvasPtr->scrollX2) != TCL_OK) 2113 || (Tk_GetPixels(canvasPtr->interp, canvasPtr->tkwin, 2114 argv2[3], &canvasPtr->scrollY2) != TCL_OK)) { 2115 goto badRegion; 2116 } 2117 ckfree((char *) argv2); 2118 } 2119 2120 flags = canvasPtr->tsoffset.flags; 2121 if (flags & TK_OFFSET_LEFT) { 2122 canvasPtr->tsoffset.xoffset = 0; 2123 } else if (flags & TK_OFFSET_CENTER) { 2124 canvasPtr->tsoffset.xoffset = canvasPtr->width/2; 2125 } else if (flags & TK_OFFSET_RIGHT) { 2126 canvasPtr->tsoffset.xoffset = canvasPtr->width; 2127 } 2128 if (flags & TK_OFFSET_TOP) { 2129 canvasPtr->tsoffset.yoffset = 0; 2130 } else if (flags & TK_OFFSET_MIDDLE) { 2131 canvasPtr->tsoffset.yoffset = canvasPtr->height/2; 2132 } else if (flags & TK_OFFSET_BOTTOM) { 2133 canvasPtr->tsoffset.yoffset = canvasPtr->height; 2134 } 2135 2136 /* 2137 * Reset the canvas's origin (this is a no-op unless confine 2138 * mode has just been turned on or the scroll region has changed). 2139 */ 2140 2141 CanvasSetOrigin(canvasPtr, canvasPtr->xOrigin, canvasPtr->yOrigin); 2142 canvasPtr->flags |= UPDATE_SCROLLBARS|REDRAW_BORDERS; 2143 Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr, 2144 canvasPtr->xOrigin, canvasPtr->yOrigin, 2145 canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin), 2146 canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin)); 2147 return TCL_OK; 2148} 2149 2150/* 2151 *--------------------------------------------------------------------------- 2152 * 2153 * CanvasWorldChanged -- 2154 * 2155 * This procedure is called when the world has changed in some 2156 * way and the widget needs to recompute all its graphics contexts 2157 * and determine its new geometry. 2158 * 2159 * Results: 2160 * None. 2161 * 2162 * Side effects: 2163 * Configures all items in the canvas with a empty argc/argv, for 2164 * the side effect of causing all the items to recompute their 2165 * geometry and to be redisplayed. 2166 * 2167 *--------------------------------------------------------------------------- 2168 */ 2169 2170static void 2171CanvasWorldChanged(instanceData) 2172 ClientData instanceData; /* Information about widget. */ 2173{ 2174 TkCanvas *canvasPtr; 2175 Tk_Item *itemPtr; 2176 int result; 2177 2178 canvasPtr = (TkCanvas *) instanceData; 2179 itemPtr = canvasPtr->firstItemPtr; 2180 for ( ; itemPtr != NULL; itemPtr = itemPtr->nextPtr) { 2181 result = (*itemPtr->typePtr->configProc)(canvasPtr->interp, 2182 (Tk_Canvas) canvasPtr, itemPtr, 0, NULL, 2183 TK_CONFIG_ARGV_ONLY); 2184 if (result != TCL_OK) { 2185 Tcl_ResetResult(canvasPtr->interp); 2186 } 2187 } 2188 canvasPtr->flags |= REPICK_NEEDED; 2189 Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr, 2190 canvasPtr->xOrigin, canvasPtr->yOrigin, 2191 canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin), 2192 canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin)); 2193} 2194 2195/* 2196 *-------------------------------------------------------------- 2197 * 2198 * DisplayCanvas -- 2199 * 2200 * This procedure redraws the contents of a canvas window. 2201 * It is invoked as a do-when-idle handler, so it only runs 2202 * when there's nothing else for the application to do. 2203 * 2204 * Results: 2205 * None. 2206 * 2207 * Side effects: 2208 * Information appears on the screen. 2209 * 2210 *-------------------------------------------------------------- 2211 */ 2212 2213static void 2214DisplayCanvas(clientData) 2215 ClientData clientData; /* Information about widget. */ 2216{ 2217 TkCanvas *canvasPtr = (TkCanvas *) clientData; 2218 Tk_Window tkwin = canvasPtr->tkwin; 2219 Tk_Item *itemPtr; 2220 Pixmap pixmap; 2221 int screenX1, screenX2, screenY1, screenY2, width, height; 2222 2223 if (canvasPtr->tkwin == NULL) { 2224 return; 2225 } 2226 2227 if (!Tk_IsMapped(tkwin)) { 2228 goto done; 2229 } 2230 2231 /* 2232 * Choose a new current item if that is needed (this could cause 2233 * event handlers to be invoked). 2234 */ 2235 2236 while (canvasPtr->flags & REPICK_NEEDED) { 2237 Tcl_Preserve((ClientData) canvasPtr); 2238 canvasPtr->flags &= ~REPICK_NEEDED; 2239 PickCurrentItem(canvasPtr, &canvasPtr->pickEvent); 2240 tkwin = canvasPtr->tkwin; 2241 Tcl_Release((ClientData) canvasPtr); 2242 if (tkwin == NULL) { 2243 return; 2244 } 2245 } 2246 2247 /* 2248 * Scan through the item list, registering the bounding box 2249 * for all items that didn't do that for the final coordinates 2250 * yet. This can be determined by the FORCE_REDRAW flag. 2251 */ 2252 2253 for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL; 2254 itemPtr = itemPtr->nextPtr) { 2255 if (itemPtr->redraw_flags & FORCE_REDRAW) { 2256 itemPtr->redraw_flags &= ~FORCE_REDRAW; 2257 EventuallyRedrawItem((Tk_Canvas)canvasPtr, itemPtr); 2258 itemPtr->redraw_flags &= ~FORCE_REDRAW; 2259 } 2260 } 2261 /* 2262 * Compute the intersection between the area that needs redrawing 2263 * and the area that's visible on the screen. 2264 */ 2265 2266 if ((canvasPtr->redrawX1 < canvasPtr->redrawX2) 2267 && (canvasPtr->redrawY1 < canvasPtr->redrawY2)) { 2268 screenX1 = canvasPtr->xOrigin + canvasPtr->inset; 2269 screenY1 = canvasPtr->yOrigin + canvasPtr->inset; 2270 screenX2 = canvasPtr->xOrigin + Tk_Width(tkwin) - canvasPtr->inset; 2271 screenY2 = canvasPtr->yOrigin + Tk_Height(tkwin) - canvasPtr->inset; 2272 if (canvasPtr->redrawX1 > screenX1) { 2273 screenX1 = canvasPtr->redrawX1; 2274 } 2275 if (canvasPtr->redrawY1 > screenY1) { 2276 screenY1 = canvasPtr->redrawY1; 2277 } 2278 if (canvasPtr->redrawX2 < screenX2) { 2279 screenX2 = canvasPtr->redrawX2; 2280 } 2281 if (canvasPtr->redrawY2 < screenY2) { 2282 screenY2 = canvasPtr->redrawY2; 2283 } 2284 if ((screenX1 >= screenX2) || (screenY1 >= screenY2)) { 2285 goto borders; 2286 } 2287 2288 width = screenX2 - screenX1; 2289 height = screenY2 - screenY1; 2290 2291#ifndef TK_NO_DOUBLE_BUFFERING 2292 /* 2293 * Redrawing is done in a temporary pixmap that is allocated 2294 * here and freed at the end of the procedure. All drawing 2295 * is done to the pixmap, and the pixmap is copied to the 2296 * screen at the end of the procedure. The temporary pixmap 2297 * serves two purposes: 2298 * 2299 * 1. It provides a smoother visual effect (no clearing and 2300 * gradual redraw will be visible to users). 2301 * 2. It allows us to redraw only the objects that overlap 2302 * the redraw area. Otherwise incorrect results could 2303 * occur from redrawing things that stick outside of 2304 * the redraw area (we'd have to redraw everything in 2305 * order to make the overlaps look right). 2306 * 2307 * Some tricky points about the pixmap: 2308 * 2309 * 1. We only allocate a large enough pixmap to hold the 2310 * area that has to be redisplayed. This saves time in 2311 * in the X server for large objects that cover much 2312 * more than the area being redisplayed: only the area 2313 * of the pixmap will actually have to be redrawn. 2314 * 2. Some X servers (e.g. the one for DECstations) have troubles 2315 * with characters that overlap an edge of the pixmap (on the 2316 * DEC servers, as of 8/18/92, such characters are drawn one 2317 * pixel too far to the right). To handle this problem, 2318 * make the pixmap a bit larger than is absolutely needed 2319 * so that for normal-sized fonts the characters that overlap 2320 * the edge of the pixmap will be outside the area we care 2321 * about. 2322 */ 2323 2324 canvasPtr->drawableXOrigin = screenX1 - 30; 2325 canvasPtr->drawableYOrigin = screenY1 - 30; 2326 pixmap = Tk_GetPixmap(Tk_Display(tkwin), Tk_WindowId(tkwin), 2327 (screenX2 + 30 - canvasPtr->drawableXOrigin), 2328 (screenY2 + 30 - canvasPtr->drawableYOrigin), 2329 Tk_Depth(tkwin)); 2330#else 2331 canvasPtr->drawableXOrigin = canvasPtr->xOrigin; 2332 canvasPtr->drawableYOrigin = canvasPtr->yOrigin; 2333 pixmap = Tk_WindowId(tkwin); 2334 TkpClipDrawableToRect(Tk_Display(tkwin), pixmap, 2335 screenX1 - canvasPtr->xOrigin, screenY1 - canvasPtr->yOrigin, 2336 width, height); 2337#endif /* TK_NO_DOUBLE_BUFFERING */ 2338 2339 /* 2340 * Clear the area to be redrawn. 2341 */ 2342 2343 XFillRectangle(Tk_Display(tkwin), pixmap, canvasPtr->pixmapGC, 2344 screenX1 - canvasPtr->drawableXOrigin, 2345 screenY1 - canvasPtr->drawableYOrigin, (unsigned int) width, 2346 (unsigned int) height); 2347 2348 /* 2349 * Scan through the item list, redrawing those items that need it. 2350 * An item must be redraw if either (a) it intersects the smaller 2351 * on-screen area or (b) it intersects the full canvas area and its 2352 * type requests that it be redrawn always (e.g. so subwindows can 2353 * be unmapped when they move off-screen). 2354 */ 2355 2356 for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL; 2357 itemPtr = itemPtr->nextPtr) { 2358 if ((itemPtr->x1 >= screenX2) 2359 || (itemPtr->y1 >= screenY2) 2360 || (itemPtr->x2 < screenX1) 2361 || (itemPtr->y2 < screenY1)) { 2362 if (!(itemPtr->typePtr->alwaysRedraw & 1) 2363 || (itemPtr->x1 >= canvasPtr->redrawX2) 2364 || (itemPtr->y1 >= canvasPtr->redrawY2) 2365 || (itemPtr->x2 < canvasPtr->redrawX1) 2366 || (itemPtr->y2 < canvasPtr->redrawY1)) { 2367 continue; 2368 } 2369 } 2370 if (itemPtr->state == TK_STATE_HIDDEN || 2371 (itemPtr->state == TK_STATE_NULL && 2372 canvasPtr->canvas_state == TK_STATE_HIDDEN)) { 2373 continue; 2374 } 2375 (*itemPtr->typePtr->displayProc)((Tk_Canvas) canvasPtr, itemPtr, 2376 canvasPtr->display, pixmap, screenX1, screenY1, width, 2377 height); 2378 } 2379 2380#ifndef TK_NO_DOUBLE_BUFFERING 2381 /* 2382 * Copy from the temporary pixmap to the screen, then free up 2383 * the temporary pixmap. 2384 */ 2385 2386 XCopyArea(Tk_Display(tkwin), pixmap, Tk_WindowId(tkwin), 2387 canvasPtr->pixmapGC, 2388 screenX1 - canvasPtr->drawableXOrigin, 2389 screenY1 - canvasPtr->drawableYOrigin, 2390 (unsigned int) width, (unsigned int) height, 2391 screenX1 - canvasPtr->xOrigin, screenY1 - canvasPtr->yOrigin); 2392 Tk_FreePixmap(Tk_Display(tkwin), pixmap); 2393#else 2394 TkpClipDrawableToRect(Tk_Display(tkwin), pixmap, 0, 0, -1, -1); 2395#endif /* TK_NO_DOUBLE_BUFFERING */ 2396 } 2397 2398 /* 2399 * Draw the window borders, if needed. 2400 */ 2401 2402 borders: 2403 if (canvasPtr->flags & REDRAW_BORDERS) { 2404 canvasPtr->flags &= ~REDRAW_BORDERS; 2405 if (canvasPtr->borderWidth > 0) { 2406 Tk_Draw3DRectangle(tkwin, Tk_WindowId(tkwin), 2407 canvasPtr->bgBorder, canvasPtr->highlightWidth, 2408 canvasPtr->highlightWidth, 2409 Tk_Width(tkwin) - 2*canvasPtr->highlightWidth, 2410 Tk_Height(tkwin) - 2*canvasPtr->highlightWidth, 2411 canvasPtr->borderWidth, canvasPtr->relief); 2412 } 2413 if (canvasPtr->highlightWidth != 0) { 2414 GC fgGC, bgGC; 2415 2416 bgGC = Tk_GCForColor(canvasPtr->highlightBgColorPtr, 2417 Tk_WindowId(tkwin)); 2418 if (canvasPtr->textInfo.gotFocus) { 2419 fgGC = Tk_GCForColor(canvasPtr->highlightColorPtr, 2420 Tk_WindowId(tkwin)); 2421 TkpDrawHighlightBorder(tkwin, fgGC, bgGC, 2422 canvasPtr->highlightWidth, Tk_WindowId(tkwin)); 2423 } else { 2424 TkpDrawHighlightBorder(tkwin, bgGC, bgGC, 2425 canvasPtr->highlightWidth, Tk_WindowId(tkwin)); 2426 } 2427 } 2428 } 2429 2430 done: 2431 canvasPtr->flags &= ~(REDRAW_PENDING|BBOX_NOT_EMPTY); 2432 canvasPtr->redrawX1 = canvasPtr->redrawX2 = 0; 2433 canvasPtr->redrawY1 = canvasPtr->redrawY2 = 0; 2434 if (canvasPtr->flags & UPDATE_SCROLLBARS) { 2435 CanvasUpdateScrollbars(canvasPtr); 2436 } 2437} 2438 2439/* 2440 *-------------------------------------------------------------- 2441 * 2442 * CanvasEventProc -- 2443 * 2444 * This procedure is invoked by the Tk dispatcher for various 2445 * events on canvases. 2446 * 2447 * Results: 2448 * None. 2449 * 2450 * Side effects: 2451 * When the window gets deleted, internal structures get 2452 * cleaned up. When it gets exposed, it is redisplayed. 2453 * 2454 *-------------------------------------------------------------- 2455 */ 2456 2457static void 2458CanvasEventProc(clientData, eventPtr) 2459 ClientData clientData; /* Information about window. */ 2460 XEvent *eventPtr; /* Information about event. */ 2461{ 2462 TkCanvas *canvasPtr = (TkCanvas *) clientData; 2463 2464 if (eventPtr->type == Expose) { 2465 int x, y; 2466 2467 x = eventPtr->xexpose.x + canvasPtr->xOrigin; 2468 y = eventPtr->xexpose.y + canvasPtr->yOrigin; 2469 Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr, x, y, 2470 x + eventPtr->xexpose.width, 2471 y + eventPtr->xexpose.height); 2472 if ((eventPtr->xexpose.x < canvasPtr->inset) 2473 || (eventPtr->xexpose.y < canvasPtr->inset) 2474 || ((eventPtr->xexpose.x + eventPtr->xexpose.width) 2475 > (Tk_Width(canvasPtr->tkwin) - canvasPtr->inset)) 2476 || ((eventPtr->xexpose.y + eventPtr->xexpose.height) 2477 > (Tk_Height(canvasPtr->tkwin) - canvasPtr->inset))) { 2478 canvasPtr->flags |= REDRAW_BORDERS; 2479 } 2480 } else if (eventPtr->type == DestroyNotify) { 2481 if (canvasPtr->tkwin != NULL) { 2482 canvasPtr->tkwin = NULL; 2483 Tcl_DeleteCommandFromToken(canvasPtr->interp, 2484 canvasPtr->widgetCmd); 2485 } 2486 if (canvasPtr->flags & REDRAW_PENDING) { 2487 Tcl_CancelIdleCall(DisplayCanvas, (ClientData) canvasPtr); 2488 } 2489 Tcl_EventuallyFree((ClientData) canvasPtr, 2490 (Tcl_FreeProc *) DestroyCanvas); 2491 } else if (eventPtr->type == ConfigureNotify) { 2492 canvasPtr->flags |= UPDATE_SCROLLBARS; 2493 2494 /* 2495 * The call below is needed in order to recenter the canvas if 2496 * it's confined and its scroll region is smaller than the window. 2497 */ 2498 2499 CanvasSetOrigin(canvasPtr, canvasPtr->xOrigin, canvasPtr->yOrigin); 2500 Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr, canvasPtr->xOrigin, 2501 canvasPtr->yOrigin, 2502 canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin), 2503 canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin)); 2504 canvasPtr->flags |= REDRAW_BORDERS; 2505 } else if (eventPtr->type == FocusIn) { 2506 if (eventPtr->xfocus.detail != NotifyInferior) { 2507 CanvasFocusProc(canvasPtr, 1); 2508 } 2509 } else if (eventPtr->type == FocusOut) { 2510 if (eventPtr->xfocus.detail != NotifyInferior) { 2511 CanvasFocusProc(canvasPtr, 0); 2512 } 2513 } else if (eventPtr->type == UnmapNotify) { 2514 Tk_Item *itemPtr; 2515 2516 /* 2517 * Special hack: if the canvas is unmapped, then must notify 2518 * all items with "alwaysRedraw" set, so that they know that 2519 * they are no longer displayed. 2520 */ 2521 2522 for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL; 2523 itemPtr = itemPtr->nextPtr) { 2524 if (itemPtr->typePtr->alwaysRedraw & 1) { 2525 (*itemPtr->typePtr->displayProc)((Tk_Canvas) canvasPtr, 2526 itemPtr, canvasPtr->display, None, 0, 0, 0, 0); 2527 } 2528 } 2529 } 2530} 2531 2532/* 2533 *---------------------------------------------------------------------- 2534 * 2535 * CanvasCmdDeletedProc -- 2536 * 2537 * This procedure is invoked when a widget command is deleted. If 2538 * the widget isn't already in the process of being destroyed, 2539 * this command destroys it. 2540 * 2541 * Results: 2542 * None. 2543 * 2544 * Side effects: 2545 * The widget is destroyed. 2546 * 2547 *---------------------------------------------------------------------- 2548 */ 2549 2550static void 2551CanvasCmdDeletedProc(clientData) 2552 ClientData clientData; /* Pointer to widget record for widget. */ 2553{ 2554 TkCanvas *canvasPtr = (TkCanvas *) clientData; 2555 Tk_Window tkwin = canvasPtr->tkwin; 2556 2557 /* 2558 * This procedure could be invoked either because the window was 2559 * destroyed and the command was then deleted (in which case tkwin 2560 * is NULL) or because the command was deleted, and then this procedure 2561 * destroys the widget. 2562 */ 2563 2564 if (tkwin != NULL) { 2565 canvasPtr->tkwin = NULL; 2566 Tk_DestroyWindow(tkwin); 2567 } 2568} 2569 2570/* 2571 *-------------------------------------------------------------- 2572 * 2573 * Tk_CanvasEventuallyRedraw -- 2574 * 2575 * Arrange for part or all of a canvas widget to redrawn at 2576 * some convenient time in the future. 2577 * 2578 * Results: 2579 * None. 2580 * 2581 * Side effects: 2582 * The screen will eventually be refreshed. 2583 * 2584 *-------------------------------------------------------------- 2585 */ 2586 2587void 2588Tk_CanvasEventuallyRedraw(canvas, x1, y1, x2, y2) 2589 Tk_Canvas canvas; /* Information about widget. */ 2590 int x1, y1; /* Upper left corner of area to redraw. 2591 * Pixels on edge are redrawn. */ 2592 int x2, y2; /* Lower right corner of area to redraw. 2593 * Pixels on edge are not redrawn. */ 2594{ 2595 TkCanvas *canvasPtr = (TkCanvas *) canvas; 2596 /* 2597 * If tkwin is NULL, the canvas has been destroyed, so we can't really 2598 * redraw it. 2599 */ 2600 if (canvasPtr->tkwin == NULL) { 2601 return; 2602 } 2603 2604 if ((x1 >= x2) || (y1 >= y2) || 2605 (x2 < canvasPtr->xOrigin) || (y2 < canvasPtr->yOrigin) || 2606 (x1 >= canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin)) || 2607 (y1 >= canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin))) { 2608 return; 2609 } 2610 if (canvasPtr->flags & BBOX_NOT_EMPTY) { 2611 if (x1 <= canvasPtr->redrawX1) { 2612 canvasPtr->redrawX1 = x1; 2613 } 2614 if (y1 <= canvasPtr->redrawY1) { 2615 canvasPtr->redrawY1 = y1; 2616 } 2617 if (x2 >= canvasPtr->redrawX2) { 2618 canvasPtr->redrawX2 = x2; 2619 } 2620 if (y2 >= canvasPtr->redrawY2) { 2621 canvasPtr->redrawY2 = y2; 2622 } 2623 } else { 2624 canvasPtr->redrawX1 = x1; 2625 canvasPtr->redrawY1 = y1; 2626 canvasPtr->redrawX2 = x2; 2627 canvasPtr->redrawY2 = y2; 2628 canvasPtr->flags |= BBOX_NOT_EMPTY; 2629 } 2630 if (!(canvasPtr->flags & REDRAW_PENDING)) { 2631 Tcl_DoWhenIdle(DisplayCanvas, (ClientData) canvasPtr); 2632 canvasPtr->flags |= REDRAW_PENDING; 2633 } 2634} 2635 2636/* 2637 *-------------------------------------------------------------- 2638 * 2639 * EventuallyRedrawItem -- 2640 * 2641 * Arrange for part or all of a canvas widget to redrawn at 2642 * some convenient time in the future. 2643 * 2644 * Results: 2645 * None. 2646 * 2647 * Side effects: 2648 * The screen will eventually be refreshed. 2649 * 2650 *-------------------------------------------------------------- 2651 */ 2652 2653static void 2654EventuallyRedrawItem(canvas, itemPtr) 2655 Tk_Canvas canvas; /* Information about widget. */ 2656 Tk_Item *itemPtr; /* item to be redrawn. */ 2657{ 2658 TkCanvas *canvasPtr = (TkCanvas *) canvas; 2659 if ((itemPtr->x1 >= itemPtr->x2) || (itemPtr->y1 >= itemPtr->y2) || 2660 (itemPtr->x2 < canvasPtr->xOrigin) || 2661 (itemPtr->y2 < canvasPtr->yOrigin) || 2662 (itemPtr->x1 >= canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin)) || 2663 (itemPtr->y1 >= canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin))) { 2664 if (!(itemPtr->typePtr->alwaysRedraw & 1)) { 2665 return; 2666 } 2667 } 2668 if (!(itemPtr->redraw_flags & FORCE_REDRAW)) { 2669 if (canvasPtr->flags & BBOX_NOT_EMPTY) { 2670 if (itemPtr->x1 <= canvasPtr->redrawX1) { 2671 canvasPtr->redrawX1 = itemPtr->x1; 2672 } 2673 if (itemPtr->y1 <= canvasPtr->redrawY1) { 2674 canvasPtr->redrawY1 = itemPtr->y1; 2675 } 2676 if (itemPtr->x2 >= canvasPtr->redrawX2) { 2677 canvasPtr->redrawX2 = itemPtr->x2; 2678 } 2679 if (itemPtr->y2 >= canvasPtr->redrawY2) { 2680 canvasPtr->redrawY2 = itemPtr->y2; 2681 } 2682 } else { 2683 canvasPtr->redrawX1 = itemPtr->x1; 2684 canvasPtr->redrawY1 = itemPtr->y1; 2685 canvasPtr->redrawX2 = itemPtr->x2; 2686 canvasPtr->redrawY2 = itemPtr->y2; 2687 canvasPtr->flags |= BBOX_NOT_EMPTY; 2688 } 2689 itemPtr->redraw_flags |= FORCE_REDRAW; 2690 } 2691 if (!(canvasPtr->flags & REDRAW_PENDING)) { 2692 Tcl_DoWhenIdle(DisplayCanvas, (ClientData) canvasPtr); 2693 canvasPtr->flags |= REDRAW_PENDING; 2694 } 2695} 2696 2697/* 2698 *-------------------------------------------------------------- 2699 * 2700 * Tk_CreateItemType -- 2701 * 2702 * This procedure may be invoked to add a new kind of canvas 2703 * element to the core item types supported by Tk. 2704 * 2705 * Results: 2706 * None. 2707 * 2708 * Side effects: 2709 * From now on, the new item type will be useable in canvas 2710 * widgets (e.g. typePtr->name can be used as the item type 2711 * in "create" widget commands). If there was already a 2712 * type with the same name as in typePtr, it is replaced with 2713 * the new type. 2714 * 2715 *-------------------------------------------------------------- 2716 */ 2717 2718void 2719Tk_CreateItemType(typePtr) 2720 Tk_ItemType *typePtr; /* Information about item type; 2721 * storage must be statically 2722 * allocated (must live forever). */ 2723{ 2724 Tk_ItemType *typePtr2, *prevPtr; 2725 2726 if (typeList == NULL) { 2727 InitCanvas(); 2728 } 2729 2730 /* 2731 * If there's already an item type with the given name, remove it. 2732 */ 2733 2734 Tcl_MutexLock(&typeListMutex); 2735 for (typePtr2 = typeList, prevPtr = NULL; typePtr2 != NULL; 2736 prevPtr = typePtr2, typePtr2 = typePtr2->nextPtr) { 2737 if (strcmp(typePtr2->name, typePtr->name) == 0) { 2738 if (prevPtr == NULL) { 2739 typeList = typePtr2->nextPtr; 2740 } else { 2741 prevPtr->nextPtr = typePtr2->nextPtr; 2742 } 2743 break; 2744 } 2745 } 2746 typePtr->nextPtr = typeList; 2747 typeList = typePtr; 2748 Tcl_MutexUnlock(&typeListMutex); 2749} 2750 2751/* 2752 *---------------------------------------------------------------------- 2753 * 2754 * Tk_GetItemTypes -- 2755 * 2756 * This procedure returns a pointer to the list of all item 2757 * types. Note that this is inherently thread-unsafe, but since 2758 * item types are only ever registered very rarely this is 2759 * unlikely to be a problem in practice. 2760 * 2761 * Results: 2762 * The return value is a pointer to the first in the list 2763 * of item types currently supported by canvases. 2764 * 2765 * Side effects: 2766 * None. 2767 * 2768 *---------------------------------------------------------------------- 2769 */ 2770 2771Tk_ItemType * 2772Tk_GetItemTypes() 2773{ 2774 if (typeList == NULL) { 2775 InitCanvas(); 2776 } 2777 return typeList; 2778} 2779 2780/* 2781 *-------------------------------------------------------------- 2782 * 2783 * InitCanvas -- 2784 * 2785 * This procedure is invoked to perform once-only-ever 2786 * initialization for the module, such as setting up the type 2787 * table. 2788 * 2789 * Results: 2790 * None. 2791 * 2792 * Side effects: 2793 * None. 2794 * 2795 *-------------------------------------------------------------- 2796 */ 2797 2798static void 2799InitCanvas() 2800{ 2801 Tcl_MutexLock(&typeListMutex); 2802 if (typeList != NULL) { 2803 Tcl_MutexUnlock(&typeListMutex); 2804 return; 2805 } 2806 typeList = &tkRectangleType; 2807 tkRectangleType.nextPtr = &tkTextType; 2808 tkTextType.nextPtr = &tkLineType; 2809 tkLineType.nextPtr = &tkPolygonType; 2810 tkPolygonType.nextPtr = &tkImageType; 2811 tkImageType.nextPtr = &tkOvalType; 2812 tkOvalType.nextPtr = &tkBitmapType; 2813 tkBitmapType.nextPtr = &tkArcType; 2814 tkArcType.nextPtr = &tkWindowType; 2815 tkWindowType.nextPtr = NULL; 2816 Tcl_MutexUnlock(&typeListMutex); 2817} 2818 2819#ifdef USE_OLD_TAG_SEARCH 2820/* 2821 *-------------------------------------------------------------- 2822 * 2823 * StartTagSearch -- 2824 * 2825 * This procedure is called to initiate an enumeration of 2826 * all items in a given canvas that contain a given tag. 2827 * 2828 * Results: 2829 * The return value is a pointer to the first item in 2830 * canvasPtr that matches tag, or NULL if there is no 2831 * such item. The information at *searchPtr is initialized 2832 * such that successive calls to NextItem will return 2833 * successive items that match tag. 2834 * 2835 * Side effects: 2836 * SearchPtr is linked into a list of searches in progress 2837 * on canvasPtr, so that elements can safely be deleted 2838 * while the search is in progress. EndTagSearch must be 2839 * called at the end of the search to unlink searchPtr from 2840 * this list. 2841 * 2842 *-------------------------------------------------------------- 2843 */ 2844 2845static Tk_Item * 2846StartTagSearch(canvasPtr, tagObj, searchPtr) 2847 TkCanvas *canvasPtr; /* Canvas whose items are to be 2848 * searched. */ 2849 Tcl_Obj *tagObj; /* Object giving tag value. */ 2850 TagSearch *searchPtr; /* Record describing tag search; 2851 * will be initialized here. */ 2852{ 2853 int id; 2854 Tk_Item *itemPtr, *lastPtr; 2855 Tk_Uid *tagPtr; 2856 Tk_Uid uid; 2857 char *tag = Tcl_GetString(tagObj); 2858 int count; 2859 TkWindow *tkwin; 2860 TkDisplay *dispPtr; 2861 2862 tkwin = (TkWindow *) canvasPtr->tkwin; 2863 dispPtr = tkwin->dispPtr; 2864 2865 /* 2866 * Initialize the search. 2867 */ 2868 2869 searchPtr->canvasPtr = canvasPtr; 2870 searchPtr->searchOver = 0; 2871 2872 /* 2873 * Find the first matching item in one of several ways. If the tag 2874 * is a number then it selects the single item with the matching 2875 * identifier. In this case see if the item being requested is the 2876 * hot item, in which case the search can be skipped. 2877 */ 2878 2879 if (isdigit(UCHAR(*tag))) { 2880 char *end; 2881 Tcl_HashEntry *entryPtr; 2882 2883 dispPtr->numIdSearches++; 2884 id = strtoul(tag, &end, 0); 2885 if (*end == 0) { 2886 itemPtr = canvasPtr->hotPtr; 2887 lastPtr = canvasPtr->hotPrevPtr; 2888 if ((itemPtr == NULL) || (itemPtr->id != id) || (lastPtr == NULL) 2889 || (lastPtr->nextPtr != itemPtr)) { 2890 dispPtr->numSlowSearches++; 2891 entryPtr = Tcl_FindHashEntry(&canvasPtr->idTable, (char *) id); 2892 if (entryPtr != NULL) { 2893 itemPtr = (Tk_Item *)Tcl_GetHashValue(entryPtr); 2894 lastPtr = itemPtr->prevPtr; 2895 } else { 2896 lastPtr = itemPtr = NULL; 2897 } 2898 } 2899 searchPtr->lastPtr = lastPtr; 2900 searchPtr->searchOver = 1; 2901 canvasPtr->hotPtr = itemPtr; 2902 canvasPtr->hotPrevPtr = lastPtr; 2903 return itemPtr; 2904 } 2905 } 2906 2907 searchPtr->tag = uid = Tk_GetUid(tag); 2908 if (uid == Tk_GetUid("all")) { 2909 /* 2910 * All items match. 2911 */ 2912 2913 searchPtr->tag = NULL; 2914 searchPtr->lastPtr = NULL; 2915 searchPtr->currentPtr = canvasPtr->firstItemPtr; 2916 return canvasPtr->firstItemPtr; 2917 } 2918 2919 /* 2920 * None of the above. Search for an item with a matching tag. 2921 */ 2922 2923 for (lastPtr = NULL, itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL; 2924 lastPtr = itemPtr, itemPtr = itemPtr->nextPtr) { 2925 for (tagPtr = itemPtr->tagPtr, count = itemPtr->numTags; 2926 count > 0; tagPtr++, count--) { 2927 if (*tagPtr == uid) { 2928 searchPtr->lastPtr = lastPtr; 2929 searchPtr->currentPtr = itemPtr; 2930 return itemPtr; 2931 } 2932 } 2933 } 2934 searchPtr->lastPtr = lastPtr; 2935 searchPtr->searchOver = 1; 2936 return NULL; 2937} 2938 2939/* 2940 *-------------------------------------------------------------- 2941 * 2942 * NextItem -- 2943 * 2944 * This procedure returns successive items that match a given 2945 * tag; it should be called only after StartTagSearch has been 2946 * used to begin a search. 2947 * 2948 * Results: 2949 * The return value is a pointer to the next item that matches 2950 * the tag specified to StartTagSearch, or NULL if no such 2951 * item exists. *SearchPtr is updated so that the next call 2952 * to this procedure will return the next item. 2953 * 2954 * Side effects: 2955 * None. 2956 * 2957 *-------------------------------------------------------------- 2958 */ 2959 2960static Tk_Item * 2961NextItem(searchPtr) 2962 TagSearch *searchPtr; /* Record describing search in 2963 * progress. */ 2964{ 2965 Tk_Item *itemPtr, *lastPtr; 2966 int count; 2967 Tk_Uid uid; 2968 Tk_Uid *tagPtr; 2969 2970 /* 2971 * Find next item in list (this may not actually be a suitable 2972 * one to return), and return if there are no items left. 2973 */ 2974 2975 lastPtr = searchPtr->lastPtr; 2976 if (lastPtr == NULL) { 2977 itemPtr = searchPtr->canvasPtr->firstItemPtr; 2978 } else { 2979 itemPtr = lastPtr->nextPtr; 2980 } 2981 if ((itemPtr == NULL) || (searchPtr->searchOver)) { 2982 searchPtr->searchOver = 1; 2983 return NULL; 2984 } 2985 if (itemPtr != searchPtr->currentPtr) { 2986 /* 2987 * The structure of the list has changed. Probably the 2988 * previously-returned item was removed from the list. 2989 * In this case, don't advance lastPtr; just return 2990 * its new successor (i.e. do nothing here). 2991 */ 2992 } else { 2993 lastPtr = itemPtr; 2994 itemPtr = lastPtr->nextPtr; 2995 } 2996 2997 /* 2998 * Handle special case of "all" search by returning next item. 2999 */ 3000 3001 uid = searchPtr->tag; 3002 if (uid == NULL) { 3003 searchPtr->lastPtr = lastPtr; 3004 searchPtr->currentPtr = itemPtr; 3005 return itemPtr; 3006 } 3007 3008 /* 3009 * Look for an item with a particular tag. 3010 */ 3011 3012 for ( ; itemPtr != NULL; lastPtr = itemPtr, itemPtr = itemPtr->nextPtr) { 3013 for (tagPtr = itemPtr->tagPtr, count = itemPtr->numTags; 3014 count > 0; tagPtr++, count--) { 3015 if (*tagPtr == uid) { 3016 searchPtr->lastPtr = lastPtr; 3017 searchPtr->currentPtr = itemPtr; 3018 return itemPtr; 3019 } 3020 } 3021 } 3022 searchPtr->lastPtr = lastPtr; 3023 searchPtr->searchOver = 1; 3024 return NULL; 3025} 3026 3027#else /* USE_OLD_TAG_SEARCH */ 3028/* 3029 *---------------------------------------------------------------------- 3030 * 3031 * GetStaticUids -- 3032 * 3033 *This procedure is invoked to return a structure filled with 3034 *the Uids used when doing tag searching. If it was never before 3035 *called in the current thread, it initializes the structure for 3036 *that thread (uids are only ever local to one thread [Bug 3037 *1114977]). 3038 * 3039 * Results: 3040 *None. 3041 * 3042 * Side effects: 3043 *None. 3044 * 3045 *---------------------------------------------------------------------- 3046 */ 3047 3048static SearchUids * 3049GetStaticUids() 3050{ 3051 SearchUids *searchUids = (SearchUids *) 3052 Tcl_GetThreadData(&dataKey, sizeof(SearchUids)); 3053 3054 if (searchUids->allUid == NULL) { 3055 searchUids->allUid = Tk_GetUid("all"); 3056 searchUids->currentUid = Tk_GetUid("current"); 3057 searchUids->andUid = Tk_GetUid("&&"); 3058 searchUids->orUid = Tk_GetUid("||"); 3059 searchUids->xorUid = Tk_GetUid("^"); 3060 searchUids->parenUid = Tk_GetUid("("); 3061 searchUids->endparenUid = Tk_GetUid(")"); 3062 searchUids->negparenUid = Tk_GetUid("!("); 3063 searchUids->tagvalUid = Tk_GetUid("!!"); 3064 searchUids->negtagvalUid = Tk_GetUid("!"); 3065 } 3066 return searchUids; 3067} 3068 3069/* 3070 *-------------------------------------------------------------- 3071 * 3072 * TagSearchExprInit -- 3073 * 3074 * This procedure allocates and initializes one TagSearchExpr struct. 3075 * 3076 * Results: 3077 * 3078 * Side effects: 3079 * 3080 *-------------------------------------------------------------- 3081 */ 3082 3083static void 3084TagSearchExprInit(exprPtrPtr) 3085 TagSearchExpr **exprPtrPtr; 3086{ 3087 TagSearchExpr* expr = *exprPtrPtr; 3088 3089 if (! expr) { 3090 expr = (TagSearchExpr *) ckalloc(sizeof(TagSearchExpr)); 3091 expr->allocated = 0; 3092 expr->uids = NULL; 3093 expr->next = NULL; 3094 } 3095 expr->uid = NULL; 3096 expr->index = 0; 3097 expr->length = 0; 3098 *exprPtrPtr = expr; 3099} 3100 3101/* 3102 *-------------------------------------------------------------- 3103 * 3104 * TagSearchExprDestroy -- 3105 * 3106 * This procedure destroys one TagSearchExpr structure. 3107 * 3108 * Results: 3109 * 3110 * Side effects: 3111 * 3112 *-------------------------------------------------------------- 3113 */ 3114 3115static void 3116TagSearchExprDestroy(expr) 3117 TagSearchExpr *expr; 3118{ 3119 if (expr) { 3120 if (expr->uids) { 3121 ckfree((char *)expr->uids); 3122 } 3123 ckfree((char *)expr); 3124 } 3125} 3126 3127/* 3128 *-------------------------------------------------------------- 3129 * 3130 * TagSearchScan -- 3131 * 3132 * This procedure is called to initiate an enumeration of 3133 * all items in a given canvas that contain a tag that matches 3134 * the tagOrId expression. 3135 * 3136 * Results: 3137 * The return value indicates if the tagOrId expression 3138 * was successfully scanned (syntax). 3139 * The information at *searchPtr is initialized 3140 * such that a call to TagSearchFirst, followed by 3141 * successive calls to TagSearchNext will return items 3142 * that match tag. 3143 * 3144 * Side effects: 3145 * SearchPtr is linked into a list of searches in progress 3146 * on canvasPtr, so that elements can safely be deleted 3147 * while the search is in progress. 3148 * 3149 *-------------------------------------------------------------- 3150 */ 3151 3152static int 3153TagSearchScan(canvasPtr, tagObj, searchPtrPtr) 3154 TkCanvas *canvasPtr; /* Canvas whose items are to be 3155 * searched. */ 3156 Tcl_Obj *tagObj; /* Object giving tag value. */ 3157 TagSearch **searchPtrPtr; /* Record describing tag search; 3158 * will be initialized here. */ 3159{ 3160 char *tag = Tcl_GetStringFromObj(tagObj,NULL); 3161 int i; 3162 TagSearch *searchPtr; 3163 3164 /* 3165 * Initialize the search. 3166 */ 3167 3168 if (*searchPtrPtr) { 3169 searchPtr = *searchPtrPtr; 3170 } else { 3171 /* Allocate primary search struct on first call */ 3172 *searchPtrPtr = searchPtr = (TagSearch *) ckalloc(sizeof(TagSearch)); 3173 searchPtr->expr = NULL; 3174 3175 /* Allocate buffer for rewritten tags (after de-escaping) */ 3176 searchPtr->rewritebufferAllocated = 100; 3177 searchPtr->rewritebuffer = 3178 ckalloc(searchPtr->rewritebufferAllocated); 3179 } 3180 TagSearchExprInit(&(searchPtr->expr)); 3181 3182 /* How long is the tagOrId ? */ 3183 searchPtr->stringLength = strlen(tag); 3184 3185 /* Make sure there is enough buffer to hold rewritten tags */ 3186 if ((unsigned int)searchPtr->stringLength >= 3187 searchPtr->rewritebufferAllocated) { 3188 searchPtr->rewritebufferAllocated = searchPtr->stringLength + 100; 3189 searchPtr->rewritebuffer = 3190 ckrealloc(searchPtr->rewritebuffer, 3191 searchPtr->rewritebufferAllocated); 3192 } 3193 3194 /* Initialize search */ 3195 searchPtr->canvasPtr = canvasPtr; 3196 searchPtr->searchOver = 0; 3197 searchPtr->type = 0; 3198 3199 /* 3200 * Find the first matching item in one of several ways. If the tag 3201 * is a number then it selects the single item with the matching 3202 * identifier. In this case see if the item being requested is the 3203 * hot item, in which case the search can be skipped. 3204 */ 3205 3206 if (searchPtr->stringLength && isdigit(UCHAR(*tag))) { 3207 char *end; 3208 3209 searchPtr->id = strtoul(tag, &end, 0); 3210 if (*end == 0) { 3211 searchPtr->type = 1; 3212 return TCL_OK; 3213 } 3214 } 3215 3216 /* 3217 * For all other tags and tag expressions convert to a UID. 3218 * This UID is kept forever, but this should be thought of 3219 * as a cache rather than as a memory leak. 3220 */ 3221 searchPtr->expr->uid = Tk_GetUid(tag); 3222 3223 /* short circuit impossible searches for null tags */ 3224 if (searchPtr->stringLength == 0) { 3225 return TCL_OK; 3226 } 3227 3228 /* 3229 * Pre-scan tag for at least one unquoted "&&" "||" "^" "!" 3230 * if not found then use string as simple tag 3231 */ 3232 for (i = 0; i < searchPtr->stringLength ; i++) { 3233 if (tag[i] == '"') { 3234 i++; 3235 for ( ; i < searchPtr->stringLength; i++) { 3236 if (tag[i] == '\\') { 3237 i++; 3238 continue; 3239 } 3240 if (tag[i] == '"') { 3241 break; 3242 } 3243 } 3244 } else { 3245 if ((tag[i] == '&' && tag[i+1] == '&') 3246 || (tag[i] == '|' && tag[i+1] == '|') 3247 || (tag[i] == '^') 3248 || (tag[i] == '!')) { 3249 searchPtr->type = 4; 3250 break; 3251 } 3252 } 3253 } 3254 3255 searchPtr->string = tag; 3256 searchPtr->stringIndex = 0; 3257 if (searchPtr->type == 4) { 3258 /* 3259 * an operator was found in the prescan, so 3260 * now compile the tag expression into array of Tk_Uid 3261 * flagging any syntax errors found 3262 */ 3263 if (TagSearchScanExpr(canvasPtr->interp, searchPtr, searchPtr->expr) != TCL_OK) { 3264 /* Syntax error in tag expression */ 3265 /* Result message set by TagSearchScanExpr */ 3266 return TCL_ERROR; 3267 } 3268 searchPtr->expr->length = searchPtr->expr->index; 3269 } else { 3270 if (searchPtr->expr->uid == GetStaticUids()->allUid) { 3271 /* 3272 * All items match. 3273 */ 3274 searchPtr->type = 2; 3275 } else { 3276 /* 3277 * Optimized single-tag search 3278 */ 3279 searchPtr->type = 3; 3280 } 3281 } 3282 return TCL_OK; 3283} 3284 3285/* 3286 *-------------------------------------------------------------- 3287 * 3288 * TagSearchDestroy -- 3289 * 3290 * This procedure destroys any dynamic structures that 3291 * may have been allocated by TagSearchScan. 3292 * 3293 * Results: 3294 * 3295 * Side effects: 3296 * 3297 *-------------------------------------------------------------- 3298 */ 3299 3300static void 3301TagSearchDestroy(searchPtr) 3302 TagSearch *searchPtr; /* Record describing tag search */ 3303{ 3304 if (searchPtr) { 3305 TagSearchExprDestroy(searchPtr->expr); 3306 ckfree((char *)searchPtr->rewritebuffer); 3307 ckfree((char *)searchPtr); 3308 } 3309} 3310 3311/* 3312 *-------------------------------------------------------------- 3313 * 3314 * TagSearchScanExpr -- 3315 * 3316 * This recursive procedure is called to scan a tag expression 3317 * and compile it into an array of Tk_Uids. 3318 * 3319 * Results: 3320 * The return value indicates if the tagOrId expression 3321 * was successfully scanned (syntax). 3322 * The information at *searchPtr is initialized 3323 * such that a call to TagSearchFirst, followed by 3324 * successive calls to TagSearchNext will return items 3325 * that match tag. 3326 * 3327 * Side effects: 3328 * 3329 *-------------------------------------------------------------- 3330 */ 3331 3332static int 3333TagSearchScanExpr(interp, searchPtr, expr) 3334 Tcl_Interp *interp; /* Current interpreter. */ 3335 TagSearch *searchPtr; /* Search data */ 3336 TagSearchExpr *expr; /* compiled expression result */ 3337{ 3338 int looking_for_tag; /* When true, scanner expects 3339 * next char(s) to be a tag, 3340 * else operand expected */ 3341 int found_tag; /* One or more tags found */ 3342 int found_endquote; /* For quoted tag string parsing */ 3343 int negate_result; /* Pending negation of next tag value */ 3344 char *tag; /* tag from tag expression string */ 3345 char c; 3346 SearchUids *searchUids; /* Collection of uids for basic search 3347 * expression terms. */ 3348 3349 searchUids = GetStaticUids(); 3350 negate_result = 0; 3351 found_tag = 0; 3352 looking_for_tag = 1; 3353 while (searchPtr->stringIndex < searchPtr->stringLength) { 3354 c = searchPtr->string[searchPtr->stringIndex++]; 3355 3356 if (expr->allocated == expr->index) { 3357 expr->allocated += 15; 3358 if (expr->uids) { 3359 expr->uids = 3360 (Tk_Uid *) ckrealloc((char *)(expr->uids), 3361 (expr->allocated)*sizeof(Tk_Uid)); 3362 } else { 3363 expr->uids = 3364 (Tk_Uid *) ckalloc((expr->allocated)*sizeof(Tk_Uid)); 3365 } 3366 } 3367 3368 if (looking_for_tag) { 3369 3370 switch (c) { 3371 case ' ' : /* ignore unquoted whitespace */ 3372 case '\t' : 3373 case '\n' : 3374 case '\r' : 3375 break; 3376 3377 case '!' : /* negate next tag or subexpr */ 3378 if (looking_for_tag > 1) { 3379 Tcl_AppendResult(interp, 3380 "Too many '!' in tag search expression", 3381 (char *) NULL); 3382 return TCL_ERROR; 3383 } 3384 looking_for_tag++; 3385 negate_result = 1; 3386 break; 3387 3388 case '(' : /* scan (negated) subexpr recursively */ 3389 if (negate_result) { 3390 expr->uids[expr->index++] = searchUids->negparenUid; 3391 negate_result = 0; 3392 } else { 3393 expr->uids[expr->index++] = searchUids->parenUid; 3394 } 3395 if (TagSearchScanExpr(interp, searchPtr, expr) != TCL_OK) { 3396 /* Result string should be already set 3397 * by nested call to tag_expr_scan() */ 3398 return TCL_ERROR; 3399 } 3400 looking_for_tag = 0; 3401 found_tag = 1; 3402 break; 3403 3404 case '"' : /* quoted tag string */ 3405 if (negate_result) { 3406 expr->uids[expr->index++] = searchUids->negtagvalUid; 3407 negate_result = 0; 3408 } else { 3409 expr->uids[expr->index++] = searchUids->tagvalUid; 3410 } 3411 tag = searchPtr->rewritebuffer; 3412 found_endquote = 0; 3413 while (searchPtr->stringIndex < searchPtr->stringLength) { 3414 c = searchPtr->string[searchPtr->stringIndex++]; 3415 if (c == '\\') { 3416 c = searchPtr->string[searchPtr->stringIndex++]; 3417 } 3418 if (c == '"') { 3419 found_endquote = 1; 3420 break; 3421 } 3422 *tag++ = c; 3423 } 3424 if (! found_endquote) { 3425 Tcl_AppendResult(interp, 3426 "Missing endquote in tag search expression", 3427 (char *) NULL); 3428 return TCL_ERROR; 3429 } 3430 if (! (tag - searchPtr->rewritebuffer)) { 3431 Tcl_AppendResult(interp, 3432 "Null quoted tag string in tag search expression", 3433 (char *) NULL); 3434 return TCL_ERROR; 3435 } 3436 *tag++ = '\0'; 3437 expr->uids[expr->index++] = 3438 Tk_GetUid(searchPtr->rewritebuffer); 3439 looking_for_tag = 0; 3440 found_tag = 1; 3441 break; 3442 3443 case '&' : /* illegal chars when looking for tag */ 3444 case '|' : 3445 case '^' : 3446 case ')' : 3447 Tcl_AppendResult(interp, 3448 "Unexpected operator in tag search expression", 3449 (char *) NULL); 3450 return TCL_ERROR; 3451 3452 default : /* unquoted tag string */ 3453 if (negate_result) { 3454 expr->uids[expr->index++] = searchUids->negtagvalUid; 3455 negate_result = 0; 3456 } else { 3457 expr->uids[expr->index++] = searchUids->tagvalUid; 3458 } 3459 tag = searchPtr->rewritebuffer; 3460 *tag++ = c; 3461 /* copy rest of tag, including any embedded whitespace */ 3462 while (searchPtr->stringIndex < searchPtr->stringLength) { 3463 c = searchPtr->string[searchPtr->stringIndex]; 3464 if (c == '!' || c == '&' || c == '|' || c == '^' 3465 || c == '(' || c == ')' || c == '"') { 3466 break; 3467 } 3468 *tag++ = c; 3469 searchPtr->stringIndex++; 3470 } 3471 /* remove trailing whitespace */ 3472 while (1) { 3473 c = *--tag; 3474 /* there must have been one non-whitespace char, 3475 * so this will terminate */ 3476 if (c != ' ' && c != '\t' && c != '\n' && c != '\r') { 3477 break; 3478 } 3479 } 3480 *++tag = '\0'; 3481 expr->uids[expr->index++] = 3482 Tk_GetUid(searchPtr->rewritebuffer); 3483 looking_for_tag = 0; 3484 found_tag = 1; 3485 } 3486 3487 } else { /* ! looking_for_tag */ 3488 3489 switch (c) { 3490 case ' ' : /* ignore whitespace */ 3491 case '\t' : 3492 case '\n' : 3493 case '\r' : 3494 break; 3495 3496 case '&' : /* AND operator */ 3497 c = searchPtr->string[searchPtr->stringIndex++]; 3498 if (c != '&') { 3499 Tcl_AppendResult(interp, 3500 "Singleton '&' in tag search expression", 3501 (char *) NULL); 3502 return TCL_ERROR; 3503 } 3504 expr->uids[expr->index++] = searchUids->andUid; 3505 looking_for_tag = 1; 3506 break; 3507 3508 case '|' : /* OR operator */ 3509 c = searchPtr->string[searchPtr->stringIndex++]; 3510 if (c != '|') { 3511 Tcl_AppendResult(interp, 3512 "Singleton '|' in tag search expression", 3513 (char *) NULL); 3514 return TCL_ERROR; 3515 } 3516 expr->uids[expr->index++] = searchUids->orUid; 3517 looking_for_tag = 1; 3518 break; 3519 3520 case '^' : /* XOR operator */ 3521 expr->uids[expr->index++] = searchUids->xorUid; 3522 looking_for_tag = 1; 3523 break; 3524 3525 case ')' : /* end subexpression */ 3526 expr->uids[expr->index++] = searchUids->endparenUid; 3527 goto breakwhile; 3528 3529 default : /* syntax error */ 3530 Tcl_AppendResult(interp, 3531 "Invalid boolean operator in tag search expression", 3532 (char *) NULL); 3533 return TCL_ERROR; 3534 } 3535 } 3536 } 3537 breakwhile: 3538 if (found_tag && ! looking_for_tag) { 3539 return TCL_OK; 3540 } 3541 Tcl_AppendResult(interp, "Missing tag in tag search expression", 3542 (char *) NULL); 3543 return TCL_ERROR; 3544} 3545 3546/* 3547 *-------------------------------------------------------------- 3548 * 3549 * TagSearchEvalExpr -- 3550 * 3551 * This recursive procedure is called to eval a tag expression. 3552 * 3553 * Results: 3554 * The return value indicates if the tagOrId expression 3555 * successfully matched the tags of the current item. 3556 * 3557 * Side effects: 3558 * 3559 *-------------------------------------------------------------- 3560 */ 3561 3562static int 3563TagSearchEvalExpr(expr, itemPtr) 3564 TagSearchExpr *expr; /* Search expression */ 3565 Tk_Item *itemPtr; /* Item being test for match */ 3566{ 3567 int looking_for_tag; /* When true, scanner expects 3568 * next char(s) to be a tag, 3569 * else operand expected */ 3570 int negate_result; /* Pending negation of next tag value */ 3571 Tk_Uid uid; 3572 Tk_Uid *tagPtr; 3573 int count; 3574 int result; /* Value of expr so far */ 3575 int parendepth; 3576 SearchUids *searchUids; /* Collection of uids for basic search 3577 * expression terms. */ 3578 3579 searchUids = GetStaticUids(); 3580 result = 0; /* just to keep the compiler quiet */ 3581 3582 negate_result = 0; 3583 looking_for_tag = 1; 3584 while (expr->index < expr->length) { 3585 uid = expr->uids[expr->index++]; 3586 if (looking_for_tag) { 3587 if (uid == searchUids->tagvalUid) { 3588/* 3589 * assert(expr->index < expr->length); 3590 */ 3591 uid = expr->uids[expr->index++]; 3592 result = 0; 3593 /* 3594 * set result 1 if tag is found in item's tags 3595 */ 3596 for (tagPtr = itemPtr->tagPtr, count = itemPtr->numTags; 3597 count > 0; tagPtr++, count--) { 3598 if (*tagPtr == uid) { 3599 result = 1; 3600 break; 3601 } 3602 } 3603 3604 } else if (uid == searchUids->negtagvalUid) { 3605 negate_result = ! negate_result; 3606/* 3607 * assert(expr->index < expr->length); 3608 */ 3609 uid = expr->uids[expr->index++]; 3610 result = 0; 3611 /* 3612 * set result 1 if tag is found in item's tags 3613 */ 3614 for (tagPtr = itemPtr->tagPtr, count = itemPtr->numTags; 3615 count > 0; tagPtr++, count--) { 3616 if (*tagPtr == uid) { 3617 result = 1; 3618 break; 3619 } 3620 } 3621 3622 } else if (uid == searchUids->parenUid) { 3623 /* 3624 * evaluate subexpressions with recursion 3625 */ 3626 result = TagSearchEvalExpr(expr, itemPtr); 3627 3628 } else if (uid == searchUids->negparenUid) { 3629 negate_result = ! negate_result; 3630 /* 3631 * evaluate subexpressions with recursion 3632 */ 3633 result = TagSearchEvalExpr(expr, itemPtr); 3634/* 3635 * } else { 3636 * assert(0); 3637 */ 3638 } 3639 if (negate_result) { 3640 result = ! result; 3641 negate_result = 0; 3642 } 3643 looking_for_tag = 0; 3644 } else { /* ! looking_for_tag */ 3645 if (((uid == searchUids->andUid) && (!result)) || 3646 ((uid == searchUids->orUid) && result)) { 3647 /* 3648 * short circuit expression evaluation 3649 * 3650 * if result before && is 0, or result before || is 1, 3651 * then the expression is decided and no further 3652 * evaluation is needed. 3653 */ 3654 3655 parendepth = 0; 3656 while (expr->index < expr->length) { 3657 uid = expr->uids[expr->index++]; 3658 if (uid == searchUids->tagvalUid || 3659 uid == searchUids->negtagvalUid) { 3660 expr->index++; 3661 continue; 3662 } 3663 if (uid == searchUids->parenUid || 3664 uid == searchUids->negparenUid) { 3665 parendepth++; 3666 continue; 3667 } 3668 if (uid == searchUids->endparenUid) { 3669 parendepth--; 3670 if (parendepth < 0) { 3671 break; 3672 } 3673 } 3674 } 3675 return result; 3676 3677 } else if (uid == searchUids->xorUid) { 3678 /* 3679 * if the previous result was 1 3680 * then negate the next result 3681 */ 3682 negate_result = result; 3683 3684 } else if (uid == searchUids->endparenUid) { 3685 return result; 3686/* 3687 * } else { 3688 * assert(0); 3689 */ 3690 } 3691 looking_for_tag = 1; 3692 } 3693 } 3694/* 3695 * assert(! looking_for_tag); 3696 */ 3697 return result; 3698} 3699 3700/* 3701 *-------------------------------------------------------------- 3702 * 3703 * TagSearchFirst -- 3704 * 3705 * This procedure is called to get the first item 3706 * item that matches a preestablished search predicate 3707 * that was set by TagSearchScan. 3708 * 3709 * Results: 3710 * The return value is a pointer to the first item, or NULL 3711 * if there is no such item. The information at *searchPtr 3712 * is updated such that successive calls to TagSearchNext 3713 * will return successive items. 3714 * 3715 * Side effects: 3716 * SearchPtr is linked into a list of searches in progress 3717 * on canvasPtr, so that elements can safely be deleted 3718 * while the search is in progress. 3719 * 3720 *-------------------------------------------------------------- 3721 */ 3722 3723static Tk_Item * 3724TagSearchFirst(searchPtr) 3725 TagSearch *searchPtr; /* Record describing tag search */ 3726{ 3727 Tk_Item *itemPtr, *lastPtr; 3728 Tk_Uid uid, *tagPtr; 3729 int count; 3730 3731 /* short circuit impossible searches for null tags */ 3732 if (searchPtr->stringLength == 0) { 3733 return NULL; 3734 } 3735 3736 /* 3737 * Find the first matching item in one of several ways. If the tag 3738 * is a number then it selects the single item with the matching 3739 * identifier. In this case see if the item being requested is the 3740 * hot item, in which case the search can be skipped. 3741 */ 3742 3743 if (searchPtr->type == 1) { 3744 Tcl_HashEntry *entryPtr; 3745 3746 itemPtr = searchPtr->canvasPtr->hotPtr; 3747 lastPtr = searchPtr->canvasPtr->hotPrevPtr; 3748 if ((itemPtr == NULL) || (itemPtr->id != searchPtr->id) || 3749 (lastPtr == NULL) || (lastPtr->nextPtr != itemPtr)) { 3750 entryPtr = Tcl_FindHashEntry(&searchPtr->canvasPtr->idTable, 3751 (char *) searchPtr->id); 3752 if (entryPtr != NULL) { 3753 itemPtr = (Tk_Item *)Tcl_GetHashValue(entryPtr); 3754 lastPtr = itemPtr->prevPtr; 3755 } else { 3756 lastPtr = itemPtr = NULL; 3757 } 3758 } 3759 searchPtr->lastPtr = lastPtr; 3760 searchPtr->searchOver = 1; 3761 searchPtr->canvasPtr->hotPtr = itemPtr; 3762 searchPtr->canvasPtr->hotPrevPtr = lastPtr; 3763 return itemPtr; 3764 } 3765 3766 if (searchPtr->type == 2) { 3767 3768 /* 3769 * All items match. 3770 */ 3771 3772 searchPtr->lastPtr = NULL; 3773 searchPtr->currentPtr = searchPtr->canvasPtr->firstItemPtr; 3774 return searchPtr->canvasPtr->firstItemPtr; 3775 } 3776 3777 if (searchPtr->type == 3) { 3778 3779 /* 3780 * Optimized single-tag search 3781 */ 3782 3783 uid = searchPtr->expr->uid; 3784 for (lastPtr = NULL, itemPtr = searchPtr->canvasPtr->firstItemPtr; 3785 itemPtr != NULL; lastPtr = itemPtr, itemPtr = itemPtr->nextPtr) { 3786 for (tagPtr = itemPtr->tagPtr, count = itemPtr->numTags; 3787 count > 0; tagPtr++, count--) { 3788 if (*tagPtr == uid) { 3789 searchPtr->lastPtr = lastPtr; 3790 searchPtr->currentPtr = itemPtr; 3791 return itemPtr; 3792 } 3793 } 3794 } 3795 } else { 3796 3797 /* 3798 * None of the above. Search for an item matching the tag expression. 3799 */ 3800 3801 for (lastPtr = NULL, itemPtr = searchPtr->canvasPtr->firstItemPtr; 3802 itemPtr != NULL; lastPtr=itemPtr, itemPtr=itemPtr->nextPtr) { 3803 searchPtr->expr->index = 0; 3804 if (TagSearchEvalExpr(searchPtr->expr, itemPtr)) { 3805 searchPtr->lastPtr = lastPtr; 3806 searchPtr->currentPtr = itemPtr; 3807 return itemPtr; 3808 } 3809 } 3810 } 3811 searchPtr->lastPtr = lastPtr; 3812 searchPtr->searchOver = 1; 3813 return NULL; 3814} 3815 3816/* 3817 *-------------------------------------------------------------- 3818 * 3819 * TagSearchNext -- 3820 * 3821 * This procedure returns successive items that match a given 3822 * tag; it should be called only after TagSearchFirst has been 3823 * used to begin a search. 3824 * 3825 * Results: 3826 * The return value is a pointer to the next item that matches 3827 * the tag expr specified to TagSearchScan, or NULL if no such 3828 * item exists. *SearchPtr is updated so that the next call 3829 * to this procedure will return the next item. 3830 * 3831 * Side effects: 3832 * None. 3833 * 3834 *-------------------------------------------------------------- 3835 */ 3836 3837static Tk_Item * 3838TagSearchNext(searchPtr) 3839 TagSearch *searchPtr; /* Record describing search in 3840 * progress. */ 3841{ 3842 Tk_Item *itemPtr, *lastPtr; 3843 Tk_Uid uid, *tagPtr; 3844 int count; 3845 3846 /* 3847 * Find next item in list (this may not actually be a suitable 3848 * one to return), and return if there are no items left. 3849 */ 3850 3851 lastPtr = searchPtr->lastPtr; 3852 if (lastPtr == NULL) { 3853 itemPtr = searchPtr->canvasPtr->firstItemPtr; 3854 } else { 3855 itemPtr = lastPtr->nextPtr; 3856 } 3857 if ((itemPtr == NULL) || (searchPtr->searchOver)) { 3858 searchPtr->searchOver = 1; 3859 return NULL; 3860 } 3861 if (itemPtr != searchPtr->currentPtr) { 3862 /* 3863 * The structure of the list has changed. Probably the 3864 * previously-returned item was removed from the list. 3865 * In this case, don't advance lastPtr; just return 3866 * its new successor (i.e. do nothing here). 3867 */ 3868 } else { 3869 lastPtr = itemPtr; 3870 itemPtr = lastPtr->nextPtr; 3871 } 3872 3873 if (searchPtr->type == 2) { 3874 3875 /* 3876 * All items match. 3877 */ 3878 3879 searchPtr->lastPtr = lastPtr; 3880 searchPtr->currentPtr = itemPtr; 3881 return itemPtr; 3882 } 3883 3884 if (searchPtr->type == 3) { 3885 3886 /* 3887 * Optimized single-tag search 3888 */ 3889 3890 uid = searchPtr->expr->uid; 3891 for ( ; itemPtr != NULL; lastPtr = itemPtr, itemPtr = itemPtr->nextPtr) { 3892 for (tagPtr = itemPtr->tagPtr, count = itemPtr->numTags; 3893 count > 0; tagPtr++, count--) { 3894 if (*tagPtr == uid) { 3895 searchPtr->lastPtr = lastPtr; 3896 searchPtr->currentPtr = itemPtr; 3897 return itemPtr; 3898 } 3899 } 3900 } 3901 searchPtr->lastPtr = lastPtr; 3902 searchPtr->searchOver = 1; 3903 return NULL; 3904 } 3905 3906 /* 3907 * Else.... evaluate tag expression 3908 */ 3909 3910 for ( ; itemPtr != NULL; lastPtr = itemPtr, itemPtr = itemPtr->nextPtr) { 3911 searchPtr->expr->index = 0; 3912 if (TagSearchEvalExpr(searchPtr->expr, itemPtr)) { 3913 searchPtr->lastPtr = lastPtr; 3914 searchPtr->currentPtr = itemPtr; 3915 return itemPtr; 3916 } 3917 } 3918 searchPtr->lastPtr = lastPtr; 3919 searchPtr->searchOver = 1; 3920 return NULL; 3921} 3922#endif /* USE_OLD_TAG_SEARCH */ 3923 3924/* 3925 *-------------------------------------------------------------- 3926 * 3927 * DoItem -- 3928 * 3929 * This is a utility procedure called by FindItems. It 3930 * either adds itemPtr's id to the result forming in interp, 3931 * or it adds a new tag to itemPtr, depending on the value 3932 * of tag. 3933 * 3934 * Results: 3935 * None. 3936 * 3937 * Side effects: 3938 * If tag is NULL then itemPtr's id is added as a list element 3939 * to the interp's result; otherwise tag is added to itemPtr's 3940 * list of tags. 3941 * 3942 *-------------------------------------------------------------- 3943 */ 3944 3945static void 3946DoItem(interp, itemPtr, tag) 3947 Tcl_Interp *interp; /* Interpreter in which to (possibly) 3948 * record item id. */ 3949 Tk_Item *itemPtr; /* Item to (possibly) modify. */ 3950 Tk_Uid tag; /* Tag to add to those already 3951 * present for item, or NULL. */ 3952{ 3953 Tk_Uid *tagPtr; 3954 int count; 3955 3956 /* 3957 * Handle the "add-to-result" case and return, if appropriate. 3958 */ 3959 3960 if (tag == NULL) { 3961 char msg[TCL_INTEGER_SPACE]; 3962 3963 sprintf(msg, "%d", itemPtr->id); 3964 Tcl_AppendElement(interp, msg); 3965 return; 3966 } 3967 3968 for (tagPtr = itemPtr->tagPtr, count = itemPtr->numTags; 3969 count > 0; tagPtr++, count--) { 3970 if (tag == *tagPtr) { 3971 return; 3972 } 3973 } 3974 3975 /* 3976 * Grow the tag space if there's no more room left in the current 3977 * block. 3978 */ 3979 3980 if (itemPtr->tagSpace == itemPtr->numTags) { 3981 Tk_Uid *newTagPtr; 3982 3983 itemPtr->tagSpace += 5; 3984 newTagPtr = (Tk_Uid *) ckalloc((unsigned) 3985 (itemPtr->tagSpace * sizeof(Tk_Uid))); 3986 memcpy((VOID *) newTagPtr, (VOID *) itemPtr->tagPtr, 3987 (itemPtr->numTags * sizeof(Tk_Uid))); 3988 if (itemPtr->tagPtr != itemPtr->staticTagSpace) { 3989 ckfree((char *) itemPtr->tagPtr); 3990 } 3991 itemPtr->tagPtr = newTagPtr; 3992 tagPtr = &itemPtr->tagPtr[itemPtr->numTags]; 3993 } 3994 3995 /* 3996 * Add in the new tag. 3997 */ 3998 3999 *tagPtr = tag; 4000 itemPtr->numTags++; 4001} 4002 4003/* 4004 *-------------------------------------------------------------- 4005 * 4006 * FindItems -- 4007 * 4008 * This procedure does all the work of implementing the 4009 * "find" and "addtag" options of the canvas widget command, 4010 * which locate items that have certain features (location, 4011 * tags, position in display list, etc.). 4012 * 4013 * Results: 4014 * A standard Tcl return value. If newTag is NULL, then a 4015 * list of ids from all the items that match argc/argv is 4016 * returned in the interp's result. If newTag is NULL, then 4017 * the normal the interp's result is an empty string. If an error 4018 * occurs, then the interp's result will hold an error message. 4019 * 4020 * Side effects: 4021 * If newTag is non-NULL, then all the items that match the 4022 * information in argc/argv have that tag added to their 4023 * lists of tags. 4024 * 4025 *-------------------------------------------------------------- 4026 */ 4027 4028static int 4029#ifdef USE_OLD_TAG_SEARCH 4030FindItems(interp, canvasPtr, argc, argv, newTag, first) 4031#else /* USE_OLD_TAG_SEARCH */ 4032FindItems(interp, canvasPtr, argc, argv, newTag, first, searchPtrPtr) 4033#endif /* USE_OLD_TAG_SEARCH */ 4034 Tcl_Interp *interp; /* Interpreter for error reporting. */ 4035 TkCanvas *canvasPtr; /* Canvas whose items are to be 4036 * searched. */ 4037 int argc; /* Number of entries in argv. Must be 4038 * greater than zero. */ 4039 Tcl_Obj *CONST *argv; /* Arguments that describe what items 4040 * to search for (see user doc on 4041 * "find" and "addtag" options). */ 4042 Tcl_Obj *newTag; /* If non-NULL, gives new tag to set 4043 * on all found items; if NULL, then 4044 * ids of found items are returned 4045 * in the interp's result. */ 4046 int first; /* For error messages: gives number 4047 * of elements of argv which are already 4048 * handled. */ 4049#ifndef USE_OLD_TAG_SEARCH 4050 TagSearch **searchPtrPtr; /* From CanvasWidgetCmd local vars*/ 4051#endif /* not USE_OLD_TAG_SEARCH */ 4052{ 4053#ifdef USE_OLD_TAG_SEARCH 4054 TagSearch search; 4055#endif /* USE_OLD_TAG_SEARCH */ 4056 Tk_Item *itemPtr; 4057 Tk_Uid uid; 4058 int index; 4059 static CONST char *optionStrings[] = { 4060 "above", "all", "below", "closest", 4061 "enclosed", "overlapping", "withtag", NULL 4062 }; 4063 enum options { 4064 CANV_ABOVE, CANV_ALL, CANV_BELOW, CANV_CLOSEST, 4065 CANV_ENCLOSED, CANV_OVERLAPPING, CANV_WITHTAG 4066 }; 4067 4068 if (newTag != NULL) { 4069 uid = Tk_GetUid(Tcl_GetStringFromObj(newTag, NULL)); 4070 } else { 4071 uid = NULL; 4072 } 4073 if (Tcl_GetIndexFromObj(interp, argv[first], optionStrings, "search command", 0, 4074 &index) != TCL_OK) { 4075 return TCL_ERROR; 4076 } 4077 switch ((enum options) index) { 4078 case CANV_ABOVE: { 4079 Tk_Item *lastPtr = NULL; 4080 if (argc != first+2) { 4081 Tcl_WrongNumArgs(interp, first+1, argv, "tagOrId"); 4082 return TCL_ERROR; 4083 } 4084#ifdef USE_OLD_TAG_SEARCH 4085 for (itemPtr = StartTagSearch(canvasPtr, argv[first+1], &search); 4086 itemPtr != NULL; itemPtr = NextItem(&search)) { 4087#else /* USE_OLD_TAG_SEARCH */ 4088 if (TagSearchScan(canvasPtr, argv[first+1], searchPtrPtr) != TCL_OK) { 4089 return TCL_ERROR; 4090 } 4091 for (itemPtr = TagSearchFirst(*searchPtrPtr); 4092 itemPtr != NULL; itemPtr = TagSearchNext(*searchPtrPtr)) { 4093#endif /* USE_OLD_TAG_SEARCH */ 4094 lastPtr = itemPtr; 4095 } 4096 if ((lastPtr != NULL) && (lastPtr->nextPtr != NULL)) { 4097 DoItem(interp, lastPtr->nextPtr, uid); 4098 } 4099 break; 4100 } 4101 case CANV_ALL: { 4102 if (argc != first+1) { 4103 Tcl_WrongNumArgs(interp, first+1, argv, (char *) NULL); 4104 return TCL_ERROR; 4105 } 4106 4107 for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL; 4108 itemPtr = itemPtr->nextPtr) { 4109 DoItem(interp, itemPtr, uid); 4110 } 4111 break; 4112 } 4113 case CANV_BELOW: { 4114 Tk_Item *itemPtr; 4115 4116 if (argc != first+2) { 4117 Tcl_WrongNumArgs(interp, first+1, argv, "tagOrId"); 4118 return TCL_ERROR; 4119 } 4120#ifdef USE_OLD_TAG_SEARCH 4121 itemPtr = StartTagSearch(canvasPtr, argv[first+1], &search); 4122#else /* USE_OLD_TAG_SEARCH */ 4123 if (TagSearchScan(canvasPtr, argv[first+1], searchPtrPtr) != TCL_OK) { 4124 return TCL_ERROR; 4125 } 4126 itemPtr = TagSearchFirst(*searchPtrPtr); 4127#endif /* USE_OLD_TAG_SEARCH */ 4128 if (itemPtr != NULL) { 4129 if (itemPtr->prevPtr != NULL) { 4130 DoItem(interp, itemPtr->prevPtr, uid); 4131 } 4132 } 4133 break; 4134 } 4135 case CANV_CLOSEST: { 4136 double closestDist; 4137 Tk_Item *startPtr, *closestPtr; 4138 double coords[2], halo; 4139 int x1, y1, x2, y2; 4140 4141 if ((argc < first+3) || (argc > first+5)) { 4142 Tcl_WrongNumArgs(interp, first+1, argv, "x y ?halo? ?start?"); 4143 return TCL_ERROR; 4144 } 4145 if ((Tk_CanvasGetCoordFromObj(interp, (Tk_Canvas) canvasPtr, argv[first+1], 4146 &coords[0]) != TCL_OK) || (Tk_CanvasGetCoordFromObj(interp, 4147 (Tk_Canvas) canvasPtr, argv[first+2], &coords[1]) != TCL_OK)) { 4148 return TCL_ERROR; 4149 } 4150 if (argc > first+3) { 4151 if (Tk_CanvasGetCoordFromObj(interp, (Tk_Canvas) canvasPtr, argv[first+3], 4152 &halo) != TCL_OK) { 4153 return TCL_ERROR; 4154 } 4155 if (halo < 0.0) { 4156 Tcl_AppendResult(interp, "can't have negative halo value \"", 4157 Tcl_GetString(argv[3]), "\"", (char *) NULL); 4158 return TCL_ERROR; 4159 } 4160 } else { 4161 halo = 0.0; 4162 } 4163 4164 /* 4165 * Find the item at which to start the search. 4166 */ 4167 4168 startPtr = canvasPtr->firstItemPtr; 4169 if (argc == first+5) { 4170#ifdef USE_OLD_TAG_SEARCH 4171 itemPtr = StartTagSearch(canvasPtr, argv[first+4], &search); 4172#else /* USE_OLD_TAG_SEARCH */ 4173 if (TagSearchScan(canvasPtr, argv[first+4], searchPtrPtr) != TCL_OK) { 4174 return TCL_ERROR; 4175 } 4176 itemPtr = TagSearchFirst(*searchPtrPtr); 4177#endif /* USE_OLD_TAG_SEARCH */ 4178 if (itemPtr != NULL) { 4179 startPtr = itemPtr; 4180 } 4181 } 4182 4183 /* 4184 * The code below is optimized so that it can eliminate most 4185 * items without having to call their item-specific procedures. 4186 * This is done by keeping a bounding box (x1, y1, x2, y2) that 4187 * an item's bbox must overlap if the item is to have any 4188 * chance of being closer than the closest so far. 4189 */ 4190 4191 itemPtr = startPtr; 4192 while(itemPtr && (itemPtr->state == TK_STATE_HIDDEN || 4193 (itemPtr->state == TK_STATE_NULL && canvasPtr->canvas_state == TK_STATE_HIDDEN))) { 4194 itemPtr = itemPtr->nextPtr; 4195 } 4196 if (itemPtr == NULL) { 4197 return TCL_OK; 4198 } 4199 closestDist = (*itemPtr->typePtr->pointProc)((Tk_Canvas) canvasPtr, 4200 itemPtr, coords) - halo; 4201 if (closestDist < 0.0) { 4202 closestDist = 0.0; 4203 } 4204 while (1) { 4205 double newDist; 4206 4207 /* 4208 * Update the bounding box using itemPtr, which is the 4209 * new closest item. 4210 */ 4211 4212 x1 = (int) (coords[0] - closestDist - halo - 1); 4213 y1 = (int) (coords[1] - closestDist - halo - 1); 4214 x2 = (int) (coords[0] + closestDist + halo + 1); 4215 y2 = (int) (coords[1] + closestDist + halo + 1); 4216 closestPtr = itemPtr; 4217 4218 /* 4219 * Search for an item that beats the current closest one. 4220 * Work circularly through the canvas's item list until 4221 * getting back to the starting item. 4222 */ 4223 4224 while (1) { 4225 itemPtr = itemPtr->nextPtr; 4226 if (itemPtr == NULL) { 4227 itemPtr = canvasPtr->firstItemPtr; 4228 } 4229 if (itemPtr == startPtr) { 4230 DoItem(interp, closestPtr, uid); 4231 return TCL_OK; 4232 } 4233 if (itemPtr->state == TK_STATE_HIDDEN || (itemPtr->state == TK_STATE_NULL && 4234 canvasPtr->canvas_state == TK_STATE_HIDDEN)) { 4235 continue; 4236 } 4237 if ((itemPtr->x1 >= x2) || (itemPtr->x2 <= x1) 4238 || (itemPtr->y1 >= y2) || (itemPtr->y2 <= y1)) { 4239 continue; 4240 } 4241 newDist = (*itemPtr->typePtr->pointProc)((Tk_Canvas) canvasPtr, 4242 itemPtr, coords) - halo; 4243 if (newDist < 0.0) { 4244 newDist = 0.0; 4245 } 4246 if (newDist <= closestDist) { 4247 closestDist = newDist; 4248 break; 4249 } 4250 } 4251 } 4252 break; 4253 } 4254 case CANV_ENCLOSED: { 4255 if (argc != first+5) { 4256 Tcl_WrongNumArgs(interp, first+1, argv, "x1 y1 x2 y2"); 4257 return TCL_ERROR; 4258 } 4259 return FindArea(interp, canvasPtr, argv+first+1, uid, 1); 4260 } 4261 case CANV_OVERLAPPING: { 4262 if (argc != first+5) { 4263 Tcl_WrongNumArgs(interp, first+1, argv, "x1 y1 x2 y2"); 4264 return TCL_ERROR; 4265 } 4266 return FindArea(interp, canvasPtr, argv+first+1, uid, 0); 4267 } 4268 case CANV_WITHTAG: { 4269 if (argc != first+2) { 4270 Tcl_WrongNumArgs(interp, first+1, argv, "tagOrId"); 4271 return TCL_ERROR; 4272 } 4273#ifdef USE_OLD_TAG_SEARCH 4274 for (itemPtr = StartTagSearch(canvasPtr, argv[first+1], &search); 4275 itemPtr != NULL; itemPtr = NextItem(&search)) { 4276#else /* USE_OLD_TAG_SEARCH */ 4277 if (TagSearchScan(canvasPtr, argv[first+1], searchPtrPtr) != TCL_OK) { 4278 return TCL_ERROR; 4279 } 4280 for (itemPtr = TagSearchFirst(*searchPtrPtr); 4281 itemPtr != NULL; itemPtr = TagSearchNext(*searchPtrPtr)) { 4282#endif /* USE_OLD_TAG_SEARCH */ 4283 DoItem(interp, itemPtr, uid); 4284 } 4285 } 4286 } 4287 return TCL_OK; 4288} 4289 4290/* 4291 *-------------------------------------------------------------- 4292 * 4293 * FindArea -- 4294 * 4295 * This procedure implements area searches for the "find" 4296 * and "addtag" options. 4297 * 4298 * Results: 4299 * A standard Tcl return value. If newTag is NULL, then a 4300 * list of ids from all the items overlapping or enclosed 4301 * by the rectangle given by argc is returned in the interp's result. 4302 * If newTag is NULL, then the normal the interp's result is an 4303 * empty string. If an error occurs, then the interp's result will 4304 * hold an error message. 4305 * 4306 * Side effects: 4307 * If uid is non-NULL, then all the items overlapping 4308 * or enclosed by the area in argv have that tag added to 4309 * their lists of tags. 4310 * 4311 *-------------------------------------------------------------- 4312 */ 4313 4314static int 4315FindArea(interp, canvasPtr, argv, uid, enclosed) 4316 Tcl_Interp *interp; /* Interpreter for error reporting 4317 * and result storing. */ 4318 TkCanvas *canvasPtr; /* Canvas whose items are to be 4319 * searched. */ 4320 Tcl_Obj *CONST *argv; /* Array of four arguments that 4321 * give the coordinates of the 4322 * rectangular area to search. */ 4323 Tk_Uid uid; /* If non-NULL, gives new tag to set 4324 * on all found items; if NULL, then 4325 * ids of found items are returned 4326 * in the interp's result. */ 4327 int enclosed; /* 0 means overlapping or enclosed 4328 * items are OK, 1 means only enclosed 4329 * items are OK. */ 4330{ 4331 double rect[4], tmp; 4332 int x1, y1, x2, y2; 4333 Tk_Item *itemPtr; 4334 4335 if ((Tk_CanvasGetCoordFromObj(interp, (Tk_Canvas) canvasPtr, argv[0], 4336 &rect[0]) != TCL_OK) 4337 || (Tk_CanvasGetCoordFromObj(interp, (Tk_Canvas) canvasPtr, argv[1], 4338 &rect[1]) != TCL_OK) 4339 || (Tk_CanvasGetCoordFromObj(interp, (Tk_Canvas) canvasPtr, argv[2], 4340 &rect[2]) != TCL_OK) 4341 || (Tk_CanvasGetCoordFromObj(interp, (Tk_Canvas) canvasPtr, argv[3], 4342 &rect[3]) != TCL_OK)) { 4343 return TCL_ERROR; 4344 } 4345 if (rect[0] > rect[2]) { 4346 tmp = rect[0]; rect[0] = rect[2]; rect[2] = tmp; 4347 } 4348 if (rect[1] > rect[3]) { 4349 tmp = rect[1]; rect[1] = rect[3]; rect[3] = tmp; 4350 } 4351 4352 /* 4353 * Use an integer bounding box for a quick test, to avoid 4354 * calling item-specific code except for items that are close. 4355 */ 4356 4357 x1 = (int) (rect[0]-1.0); 4358 y1 = (int) (rect[1]-1.0); 4359 x2 = (int) (rect[2]+1.0); 4360 y2 = (int) (rect[3]+1.0); 4361 for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL; 4362 itemPtr = itemPtr->nextPtr) { 4363 if (itemPtr->state == TK_STATE_HIDDEN || (itemPtr->state == TK_STATE_NULL && 4364 canvasPtr->canvas_state == TK_STATE_HIDDEN)) { 4365 continue; 4366 } 4367 if ((itemPtr->x1 >= x2) || (itemPtr->x2 <= x1) 4368 || (itemPtr->y1 >= y2) || (itemPtr->y2 <= y1)) { 4369 continue; 4370 } 4371 if ((*itemPtr->typePtr->areaProc)((Tk_Canvas) canvasPtr, itemPtr, rect) 4372 >= enclosed) { 4373 DoItem(interp, itemPtr, uid); 4374 } 4375 } 4376 return TCL_OK; 4377} 4378 4379/* 4380 *-------------------------------------------------------------- 4381 * 4382 * RelinkItems -- 4383 * 4384 * Move one or more items to a different place in the 4385 * display order for a canvas. 4386 * 4387 * Results: 4388 * None. 4389 * 4390 * Side effects: 4391 * The items identified by "tag" are moved so that they 4392 * are all together in the display list and immediately 4393 * after prevPtr. The order of the moved items relative 4394 * to each other is not changed. 4395 * 4396 *-------------------------------------------------------------- 4397 */ 4398 4399#ifdef USE_OLD_TAG_SEARCH 4400static void 4401RelinkItems(canvasPtr, tag, prevPtr) 4402#else /* USE_OLD_TAG_SEARCH */ 4403static int 4404RelinkItems(canvasPtr, tag, prevPtr, searchPtrPtr) 4405#endif /* USE_OLD_TAG_SEARCH */ 4406 TkCanvas *canvasPtr; /* Canvas to be modified. */ 4407 Tcl_Obj *tag; /* Tag identifying items to be moved 4408 * in the redisplay list. */ 4409 Tk_Item *prevPtr; /* Reposition the items so that they 4410 * go just after this item (NULL means 4411 * put at beginning of list). */ 4412#ifndef USE_OLD_TAG_SEARCH 4413 TagSearch **searchPtrPtr; /* From CanvasWidgetCmd local vars */ 4414#endif /* not USE_OLD_TAG_SEARCH */ 4415{ 4416 Tk_Item *itemPtr; 4417#ifdef USE_OLD_TAG_SEARCH 4418 TagSearch search; 4419#endif /* USE_OLD_TAG_SEARCH */ 4420 Tk_Item *firstMovePtr, *lastMovePtr; 4421 4422 /* 4423 * Find all of the items to be moved and remove them from 4424 * the list, making an auxiliary list running from firstMovePtr 4425 * to lastMovePtr. Record their areas for redisplay. 4426 */ 4427 4428 firstMovePtr = lastMovePtr = NULL; 4429#ifdef USE_OLD_TAG_SEARCH 4430 for (itemPtr = StartTagSearch(canvasPtr, tag, &search); 4431 itemPtr != NULL; itemPtr = NextItem(&search)) { 4432#else /* USE_OLD_TAG_SEARCH */ 4433 if (TagSearchScan(canvasPtr, tag, searchPtrPtr) != TCL_OK) { 4434 return TCL_ERROR; 4435 } 4436 for (itemPtr = TagSearchFirst(*searchPtrPtr); 4437 itemPtr != NULL; itemPtr = TagSearchNext(*searchPtrPtr)) { 4438#endif /* USE_OLD_TAG_SEARCH */ 4439 if (itemPtr == prevPtr) { 4440 /* 4441 * Item after which insertion is to occur is being 4442 * moved! Switch to insert after its predecessor. 4443 */ 4444 4445 prevPtr = prevPtr->prevPtr; 4446 } 4447 if (itemPtr->prevPtr == NULL) { 4448 if (itemPtr->nextPtr != NULL) { 4449 itemPtr->nextPtr->prevPtr = NULL; 4450 } 4451 canvasPtr->firstItemPtr = itemPtr->nextPtr; 4452 } else { 4453 if (itemPtr->nextPtr != NULL) { 4454 itemPtr->nextPtr->prevPtr = itemPtr->prevPtr; 4455 } 4456 itemPtr->prevPtr->nextPtr = itemPtr->nextPtr; 4457 } 4458 if (canvasPtr->lastItemPtr == itemPtr) { 4459 canvasPtr->lastItemPtr = itemPtr->prevPtr; 4460 } 4461 if (firstMovePtr == NULL) { 4462 itemPtr->prevPtr = NULL; 4463 firstMovePtr = itemPtr; 4464 } else { 4465 itemPtr->prevPtr = lastMovePtr; 4466 lastMovePtr->nextPtr = itemPtr; 4467 } 4468 lastMovePtr = itemPtr; 4469 EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr); 4470 canvasPtr->flags |= REPICK_NEEDED; 4471 } 4472 4473 /* 4474 * Insert the list of to-be-moved items back into the canvas's 4475 * at the desired position. 4476 */ 4477 4478 if (firstMovePtr == NULL) { 4479#ifdef USE_OLD_TAG_SEARCH 4480 return; 4481#else /* USE_OLD_TAG_SEARCH */ 4482 return TCL_OK; 4483#endif /* USE_OLD_TAG_SEARCH */ 4484 } 4485 if (prevPtr == NULL) { 4486 if (canvasPtr->firstItemPtr != NULL) { 4487 canvasPtr->firstItemPtr->prevPtr = lastMovePtr; 4488 } 4489 lastMovePtr->nextPtr = canvasPtr->firstItemPtr; 4490 canvasPtr->firstItemPtr = firstMovePtr; 4491 } else { 4492 if (prevPtr->nextPtr != NULL) { 4493 prevPtr->nextPtr->prevPtr = lastMovePtr; 4494 } 4495 lastMovePtr->nextPtr = prevPtr->nextPtr; 4496 if (firstMovePtr != NULL) { 4497 firstMovePtr->prevPtr = prevPtr; 4498 } 4499 prevPtr->nextPtr = firstMovePtr; 4500 } 4501 if (canvasPtr->lastItemPtr == prevPtr) { 4502 canvasPtr->lastItemPtr = lastMovePtr; 4503 } 4504#ifndef USE_OLD_TAG_SEARCH 4505 return TCL_OK; 4506#endif /* not USE_OLD_TAG_SEARCH */ 4507} 4508 4509/* 4510 *-------------------------------------------------------------- 4511 * 4512 * CanvasBindProc -- 4513 * 4514 * This procedure is invoked by the Tk dispatcher to handle 4515 * events associated with bindings on items. 4516 * 4517 * Results: 4518 * None. 4519 * 4520 * Side effects: 4521 * Depends on the command invoked as part of the binding 4522 * (if there was any). 4523 * 4524 *-------------------------------------------------------------- 4525 */ 4526 4527static void 4528CanvasBindProc(clientData, eventPtr) 4529 ClientData clientData; /* Pointer to canvas structure. */ 4530 XEvent *eventPtr; /* Pointer to X event that just 4531 * happened. */ 4532{ 4533 TkCanvas *canvasPtr = (TkCanvas *) clientData; 4534 4535 Tcl_Preserve((ClientData) canvasPtr); 4536 4537 /* 4538 * This code below keeps track of the current modifier state in 4539 * canvasPtr>state. This information is used to defer repicks of 4540 * the current item while buttons are down. 4541 */ 4542 4543 if ((eventPtr->type == ButtonPress) || (eventPtr->type == ButtonRelease)) { 4544 int mask; 4545 4546 switch (eventPtr->xbutton.button) { 4547 case Button1: 4548 mask = Button1Mask; 4549 break; 4550 case Button2: 4551 mask = Button2Mask; 4552 break; 4553 case Button3: 4554 mask = Button3Mask; 4555 break; 4556 case Button4: 4557 mask = Button4Mask; 4558 break; 4559 case Button5: 4560 mask = Button5Mask; 4561 break; 4562 default: 4563 mask = 0; 4564 break; 4565 } 4566 4567 /* 4568 * For button press events, repick the current item using the 4569 * button state before the event, then process the event. For 4570 * button release events, first process the event, then repick 4571 * the current item using the button state *after* the event 4572 * (the button has logically gone up before we change the 4573 * current item). 4574 */ 4575 4576 if (eventPtr->type == ButtonPress) { 4577 /* 4578 * On a button press, first repick the current item using 4579 * the button state before the event, the process the event. 4580 */ 4581 4582 canvasPtr->state = eventPtr->xbutton.state; 4583 PickCurrentItem(canvasPtr, eventPtr); 4584 canvasPtr->state ^= mask; 4585 CanvasDoEvent(canvasPtr, eventPtr); 4586 } else { 4587 /* 4588 * Button release: first process the event, with the button 4589 * still considered to be down. Then repick the current 4590 * item under the assumption that the button is no longer down. 4591 */ 4592 4593 canvasPtr->state = eventPtr->xbutton.state; 4594 CanvasDoEvent(canvasPtr, eventPtr); 4595 eventPtr->xbutton.state ^= mask; 4596 canvasPtr->state = eventPtr->xbutton.state; 4597 PickCurrentItem(canvasPtr, eventPtr); 4598 eventPtr->xbutton.state ^= mask; 4599 } 4600 goto done; 4601 } else if ((eventPtr->type == EnterNotify) 4602 || (eventPtr->type == LeaveNotify)) { 4603 canvasPtr->state = eventPtr->xcrossing.state; 4604 PickCurrentItem(canvasPtr, eventPtr); 4605 goto done; 4606 } else if (eventPtr->type == MotionNotify) { 4607 canvasPtr->state = eventPtr->xmotion.state; 4608 PickCurrentItem(canvasPtr, eventPtr); 4609 } 4610 CanvasDoEvent(canvasPtr, eventPtr); 4611 4612 done: 4613 Tcl_Release((ClientData) canvasPtr); 4614} 4615 4616/* 4617 *-------------------------------------------------------------- 4618 * 4619 * PickCurrentItem -- 4620 * 4621 * Find the topmost item in a canvas that contains a given 4622 * location and mark the the current item. If the current 4623 * item has changed, generate a fake exit event on the old 4624 * current item, a fake enter event on the new current item 4625 * item and force a redraw of the two items. Canvas items 4626 * that are hidden or disabled are ignored. 4627 * 4628 * Results: 4629 * None. 4630 * 4631 * Side effects: 4632 * The current item for canvasPtr may change. If it does, 4633 * then the commands associated with item entry and exit 4634 * could do just about anything. A binding script could 4635 * delete the canvas, so callers should protect themselves 4636 * with Tcl_Preserve and Tcl_Release. 4637 * 4638 *-------------------------------------------------------------- 4639 */ 4640 4641static void 4642PickCurrentItem(canvasPtr, eventPtr) 4643 TkCanvas *canvasPtr; /* Canvas widget in which to select 4644 * current item. */ 4645 XEvent *eventPtr; /* Event describing location of 4646 * mouse cursor. Must be EnterWindow, 4647 * LeaveWindow, ButtonRelease, or 4648 * MotionNotify. */ 4649{ 4650 double coords[2]; 4651 int buttonDown; 4652 Tk_Item *prevItemPtr; 4653#ifndef USE_OLD_TAG_SEARCH 4654 SearchUids *searchUids = GetStaticUids(); 4655#endif 4656 4657 /* 4658 * Check whether or not a button is down. If so, we'll log entry 4659 * and exit into and out of the current item, but not entry into 4660 * any other item. This implements a form of grabbing equivalent 4661 * to what the X server does for windows. 4662 */ 4663 4664 buttonDown = canvasPtr->state 4665 & (Button1Mask|Button2Mask|Button3Mask|Button4Mask|Button5Mask); 4666 4667 /* 4668 * Save information about this event in the canvas. The event in 4669 * the canvas is used for two purposes: 4670 * 4671 * 1. Event bindings: if the current item changes, fake events are 4672 * generated to allow item-enter and item-leave bindings to trigger. 4673 * 2. Reselection: if the current item gets deleted, can use the 4674 * saved event to find a new current item. 4675 * Translate MotionNotify events into EnterNotify events, since that's 4676 * what gets reported to item handlers. 4677 */ 4678 4679 if (eventPtr != &canvasPtr->pickEvent) { 4680 if ((eventPtr->type == MotionNotify) 4681 || (eventPtr->type == ButtonRelease)) { 4682 canvasPtr->pickEvent.xcrossing.type = EnterNotify; 4683 canvasPtr->pickEvent.xcrossing.serial = eventPtr->xmotion.serial; 4684 canvasPtr->pickEvent.xcrossing.send_event 4685 = eventPtr->xmotion.send_event; 4686 canvasPtr->pickEvent.xcrossing.display = eventPtr->xmotion.display; 4687 canvasPtr->pickEvent.xcrossing.window = eventPtr->xmotion.window; 4688 canvasPtr->pickEvent.xcrossing.root = eventPtr->xmotion.root; 4689 canvasPtr->pickEvent.xcrossing.subwindow = None; 4690 canvasPtr->pickEvent.xcrossing.time = eventPtr->xmotion.time; 4691 canvasPtr->pickEvent.xcrossing.x = eventPtr->xmotion.x; 4692 canvasPtr->pickEvent.xcrossing.y = eventPtr->xmotion.y; 4693 canvasPtr->pickEvent.xcrossing.x_root = eventPtr->xmotion.x_root; 4694 canvasPtr->pickEvent.xcrossing.y_root = eventPtr->xmotion.y_root; 4695 canvasPtr->pickEvent.xcrossing.mode = NotifyNormal; 4696 canvasPtr->pickEvent.xcrossing.detail = NotifyNonlinear; 4697 canvasPtr->pickEvent.xcrossing.same_screen 4698 = eventPtr->xmotion.same_screen; 4699 canvasPtr->pickEvent.xcrossing.focus = False; 4700 canvasPtr->pickEvent.xcrossing.state = eventPtr->xmotion.state; 4701 } else { 4702 canvasPtr->pickEvent = *eventPtr; 4703 } 4704 } 4705 4706 /* 4707 * If this is a recursive call (there's already a partially completed 4708 * call pending on the stack; it's in the middle of processing a 4709 * Leave event handler for the old current item) then just return; 4710 * the pending call will do everything that's needed. 4711 */ 4712 4713 if (canvasPtr->flags & REPICK_IN_PROGRESS) { 4714 return; 4715 } 4716 4717 /* 4718 * A LeaveNotify event automatically means that there's no current 4719 * object, so the check for closest item can be skipped. 4720 */ 4721 4722 coords[0] = canvasPtr->pickEvent.xcrossing.x + canvasPtr->xOrigin; 4723 coords[1] = canvasPtr->pickEvent.xcrossing.y + canvasPtr->yOrigin; 4724 if (canvasPtr->pickEvent.type != LeaveNotify) { 4725 canvasPtr->newCurrentPtr = CanvasFindClosest(canvasPtr, coords); 4726 } else { 4727 canvasPtr->newCurrentPtr = NULL; 4728 } 4729 4730 if ((canvasPtr->newCurrentPtr == canvasPtr->currentItemPtr) 4731 && !(canvasPtr->flags & LEFT_GRABBED_ITEM)) { 4732 /* 4733 * Nothing to do: the current item hasn't changed. 4734 */ 4735 4736 return; 4737 } 4738 4739 if (!buttonDown) { 4740 canvasPtr->flags &= ~LEFT_GRABBED_ITEM; 4741 } 4742 4743 /* 4744 * Simulate a LeaveNotify event on the previous current item and 4745 * an EnterNotify event on the new current item. Remove the "current" 4746 * tag from the previous current item and place it on the new current 4747 * item. 4748 */ 4749 4750 if ((canvasPtr->newCurrentPtr != canvasPtr->currentItemPtr) 4751 && (canvasPtr->currentItemPtr != NULL) 4752 && !(canvasPtr->flags & LEFT_GRABBED_ITEM)) { 4753 XEvent event; 4754 Tk_Item *itemPtr = canvasPtr->currentItemPtr; 4755 int i; 4756 4757 event = canvasPtr->pickEvent; 4758 event.type = LeaveNotify; 4759 4760 /* 4761 * If the event's detail happens to be NotifyInferior the 4762 * binding mechanism will discard the event. To be consistent, 4763 * always use NotifyAncestor. 4764 */ 4765 4766 event.xcrossing.detail = NotifyAncestor; 4767 canvasPtr->flags |= REPICK_IN_PROGRESS; 4768 CanvasDoEvent(canvasPtr, &event); 4769 canvasPtr->flags &= ~REPICK_IN_PROGRESS; 4770 4771 /* 4772 * The check below is needed because there could be an event 4773 * handler for <LeaveNotify> that deletes the current item. 4774 */ 4775 4776 if ((itemPtr == canvasPtr->currentItemPtr) && !buttonDown) { 4777 for (i = itemPtr->numTags-1; i >= 0; i--) { 4778#ifdef USE_OLD_TAG_SEARCH 4779 if (itemPtr->tagPtr[i] == Tk_GetUid("current")) { 4780#else /* USE_OLD_TAG_SEARCH */ 4781 if (itemPtr->tagPtr[i] == searchUids->currentUid) { 4782#endif /* USE_OLD_TAG_SEARCH */ 4783 itemPtr->tagPtr[i] = itemPtr->tagPtr[itemPtr->numTags-1]; 4784 itemPtr->numTags--; 4785 break; 4786 } 4787 } 4788 } 4789 4790 /* 4791 * Note: during CanvasDoEvent above, it's possible that 4792 * canvasPtr->newCurrentPtr got reset to NULL because the 4793 * item was deleted. 4794 */ 4795 } 4796 if ((canvasPtr->newCurrentPtr != canvasPtr->currentItemPtr) && buttonDown) { 4797 canvasPtr->flags |= LEFT_GRABBED_ITEM; 4798 return; 4799 } 4800 4801 /* 4802 * Special note: it's possible that canvasPtr->newCurrentPtr == 4803 * canvasPtr->currentItemPtr here. This can happen, for example, 4804 * if LEFT_GRABBED_ITEM was set. 4805 */ 4806 4807 prevItemPtr = canvasPtr->currentItemPtr; 4808 canvasPtr->flags &= ~LEFT_GRABBED_ITEM; 4809 canvasPtr->currentItemPtr = canvasPtr->newCurrentPtr; 4810 if (prevItemPtr != NULL && prevItemPtr != canvasPtr->currentItemPtr && 4811 (prevItemPtr->redraw_flags & TK_ITEM_STATE_DEPENDANT)) { 4812 EventuallyRedrawItem((Tk_Canvas) canvasPtr, prevItemPtr); 4813 (*prevItemPtr->typePtr->configProc)(canvasPtr->interp, 4814 (Tk_Canvas) canvasPtr, prevItemPtr, 0, (Tcl_Obj **) NULL, 4815 TK_CONFIG_ARGV_ONLY); 4816 } 4817 if (canvasPtr->currentItemPtr != NULL) { 4818 XEvent event; 4819 4820#ifdef USE_OLD_TAG_SEARCH 4821 DoItem((Tcl_Interp *) NULL, canvasPtr->currentItemPtr, 4822 Tk_GetUid("current")); 4823#else /* USE_OLD_TAG_SEARCH */ 4824 DoItem((Tcl_Interp *) NULL, canvasPtr->currentItemPtr, 4825 searchUids->currentUid); 4826#endif /* USE_OLD_TAG_SEA */ 4827 if ((canvasPtr->currentItemPtr->redraw_flags & TK_ITEM_STATE_DEPENDANT && 4828 prevItemPtr != canvasPtr->currentItemPtr)) { 4829 (*canvasPtr->currentItemPtr->typePtr->configProc)(canvasPtr->interp, 4830 (Tk_Canvas) canvasPtr, canvasPtr->currentItemPtr, 0, (Tcl_Obj **) NULL, 4831 TK_CONFIG_ARGV_ONLY); 4832 EventuallyRedrawItem((Tk_Canvas) canvasPtr, 4833 canvasPtr->currentItemPtr); 4834 } 4835 event = canvasPtr->pickEvent; 4836 event.type = EnterNotify; 4837 event.xcrossing.detail = NotifyAncestor; 4838 CanvasDoEvent(canvasPtr, &event); 4839 } 4840} 4841 4842/* 4843 *---------------------------------------------------------------------- 4844 * 4845 * CanvasFindClosest -- 4846 * 4847 * Given x and y coordinates, find the topmost canvas item that 4848 * is "close" to the coordinates. Canvas items that are hidden 4849 * or disabled are ignored. 4850 * 4851 * Results: 4852 * The return value is a pointer to the topmost item that is 4853 * close to (x,y), or NULL if no item is close. 4854 * 4855 * Side effects: 4856 * None. 4857 * 4858 *---------------------------------------------------------------------- 4859 */ 4860 4861static Tk_Item * 4862CanvasFindClosest(canvasPtr, coords) 4863 TkCanvas *canvasPtr; /* Canvas widget to search. */ 4864 double coords[2]; /* Desired x,y position in canvas, 4865 * not screen, coordinates.) */ 4866{ 4867 Tk_Item *itemPtr; 4868 Tk_Item *bestPtr; 4869 int x1, y1, x2, y2; 4870 4871 x1 = (int) (coords[0] - canvasPtr->closeEnough); 4872 y1 = (int) (coords[1] - canvasPtr->closeEnough); 4873 x2 = (int) (coords[0] + canvasPtr->closeEnough); 4874 y2 = (int) (coords[1] + canvasPtr->closeEnough); 4875 4876 bestPtr = NULL; 4877 for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL; 4878 itemPtr = itemPtr->nextPtr) { 4879 if (itemPtr->state == TK_STATE_HIDDEN || itemPtr->state==TK_STATE_DISABLED || 4880 (itemPtr->state == TK_STATE_NULL && (canvasPtr->canvas_state == TK_STATE_HIDDEN || 4881 canvasPtr->canvas_state == TK_STATE_DISABLED))) { 4882 continue; 4883 } 4884 if ((itemPtr->x1 > x2) || (itemPtr->x2 < x1) 4885 || (itemPtr->y1 > y2) || (itemPtr->y2 < y1)) { 4886 continue; 4887 } 4888 if ((*itemPtr->typePtr->pointProc)((Tk_Canvas) canvasPtr, 4889 itemPtr, coords) <= canvasPtr->closeEnough) { 4890 bestPtr = itemPtr; 4891 } 4892 } 4893 return bestPtr; 4894} 4895 4896/* 4897 *-------------------------------------------------------------- 4898 * 4899 * CanvasDoEvent -- 4900 * 4901 * This procedure is called to invoke binding processing 4902 * for a new event that is associated with the current item 4903 * for a canvas. 4904 * 4905 * Results: 4906 * None. 4907 * 4908 * Side effects: 4909 * Depends on the bindings for the canvas. A binding script 4910 * could delete the canvas, so callers should protect themselves 4911 * with Tcl_Preserve and Tcl_Release. 4912 * 4913 *-------------------------------------------------------------- 4914 */ 4915 4916static void 4917CanvasDoEvent(canvasPtr, eventPtr) 4918 TkCanvas *canvasPtr; /* Canvas widget in which event 4919 * occurred. */ 4920 XEvent *eventPtr; /* Real or simulated X event that 4921 * is to be processed. */ 4922{ 4923#define NUM_STATIC 3 4924 ClientData staticObjects[NUM_STATIC]; 4925 ClientData *objectPtr; 4926 int numObjects, i; 4927 Tk_Item *itemPtr; 4928#ifndef USE_OLD_TAG_SEARCH 4929 TagSearchExpr *expr; 4930 int numExprs; 4931 SearchUids *searchUids = GetStaticUids(); 4932#endif /* not USE_OLD_TAG_SEARCH */ 4933 4934 if (canvasPtr->bindingTable == NULL) { 4935 return; 4936 } 4937 4938 itemPtr = canvasPtr->currentItemPtr; 4939 if ((eventPtr->type == KeyPress) || (eventPtr->type == KeyRelease)) { 4940 itemPtr = canvasPtr->textInfo.focusItemPtr; 4941 } 4942 if (itemPtr == NULL) { 4943 return; 4944 } 4945 4946#ifdef USE_OLD_TAG_SEARCH 4947 /* 4948 * Set up an array with all the relevant objects for processing 4949 * this event. The relevant objects are (a) the event's item, 4950 * (b) the tags associated with the event's item, and (c) the 4951 * tag "all". If there are a lot of tags then malloc an array 4952 * to hold all of the objects. 4953 */ 4954 4955 numObjects = itemPtr->numTags + 2; 4956#else /* USE_OLD_TAG_SEARCH */ 4957 /* 4958 * Set up an array with all the relevant objects for processing 4959 * this event. The relevant objects are: 4960 * (a) the event's item, 4961 * (b) the tags associated with the event's item, 4962 * (c) the expressions that are true for the event's item's tags, and 4963 * (d) the tag "all". 4964 * 4965 * If there are a lot of tags then malloc an array to hold all of 4966 * the objects. 4967 */ 4968 4969 /* 4970 * flag and count all expressions that match item's tags 4971 */ 4972 numExprs = 0; 4973 expr = canvasPtr->bindTagExprs; 4974 while (expr) { 4975 expr->index = 0; 4976 expr->match = TagSearchEvalExpr(expr, itemPtr); 4977 if (expr->match) { 4978 numExprs++; 4979 } 4980 expr = expr->next; 4981 } 4982 4983 numObjects = itemPtr->numTags + numExprs + 2; 4984#endif /* not USE_OLD_TAG_SEARCH */ 4985 if (numObjects <= NUM_STATIC) { 4986 objectPtr = staticObjects; 4987 } else { 4988 objectPtr = (ClientData *) 4989 ckalloc((unsigned) (numObjects * sizeof(ClientData))); 4990 } 4991#ifdef USE_OLD_TAG_SEARCH 4992 objectPtr[0] = (ClientData) Tk_GetUid("all"); 4993#else /* USE_OLD_TAG_SEARCH */ 4994 objectPtr[0] = (ClientData) searchUids->allUid; 4995#endif /* USE_OLD_TAG_SEARCH */ 4996 for (i = itemPtr->numTags-1; i >= 0; i--) { 4997 objectPtr[i+1] = (ClientData) itemPtr->tagPtr[i]; 4998 } 4999 objectPtr[itemPtr->numTags+1] = (ClientData) itemPtr; 5000#ifndef USE_OLD_TAG_SEARCH 5001 /* 5002 * copy uids of matching expressions into object array 5003 */ 5004 i = itemPtr->numTags+2; 5005 expr = canvasPtr->bindTagExprs; 5006 while (expr) { 5007 if (expr->match) { 5008 objectPtr[i++] = (int *) expr->uid; 5009 } 5010 expr = expr->next; 5011 } 5012#endif /* not USE_OLD_TAG_SEARCH */ 5013 5014 /* 5015 * Invoke the binding system, then free up the object array if 5016 * it was malloc-ed. 5017 */ 5018 5019 if (canvasPtr->tkwin != NULL) { 5020 Tk_BindEvent(canvasPtr->bindingTable, eventPtr, canvasPtr->tkwin, 5021 numObjects, objectPtr); 5022 } 5023 if (objectPtr != staticObjects) { 5024 ckfree((char *) objectPtr); 5025 } 5026} 5027 5028/* 5029 *---------------------------------------------------------------------- 5030 * 5031 * CanvasBlinkProc -- 5032 * 5033 * This procedure is called as a timer handler to blink the 5034 * insertion cursor off and on. 5035 * 5036 * Results: 5037 * None. 5038 * 5039 * Side effects: 5040 * The cursor gets turned on or off, redisplay gets invoked, 5041 * and this procedure reschedules itself. 5042 * 5043 *---------------------------------------------------------------------- 5044 */ 5045 5046static void 5047CanvasBlinkProc(clientData) 5048 ClientData clientData; /* Pointer to record describing entry. */ 5049{ 5050 TkCanvas *canvasPtr = (TkCanvas *) clientData; 5051 5052 if (!canvasPtr->textInfo.gotFocus || (canvasPtr->insertOffTime == 0)) { 5053 return; 5054 } 5055 if (canvasPtr->textInfo.cursorOn) { 5056 canvasPtr->textInfo.cursorOn = 0; 5057 canvasPtr->insertBlinkHandler = Tcl_CreateTimerHandler( 5058 canvasPtr->insertOffTime, CanvasBlinkProc, 5059 (ClientData) canvasPtr); 5060 } else { 5061 canvasPtr->textInfo.cursorOn = 1; 5062 canvasPtr->insertBlinkHandler = Tcl_CreateTimerHandler( 5063 canvasPtr->insertOnTime, CanvasBlinkProc, 5064 (ClientData) canvasPtr); 5065 } 5066 if (canvasPtr->textInfo.focusItemPtr != NULL) { 5067 EventuallyRedrawItem((Tk_Canvas) canvasPtr, 5068 canvasPtr->textInfo.focusItemPtr); 5069 } 5070} 5071 5072/* 5073 *---------------------------------------------------------------------- 5074 * 5075 * CanvasFocusProc -- 5076 * 5077 * This procedure is called whenever a canvas gets or loses the 5078 * input focus. It's also called whenever the window is 5079 * reconfigured while it has the focus. 5080 * 5081 * Results: 5082 * None. 5083 * 5084 * Side effects: 5085 * The cursor gets turned on or off. 5086 * 5087 *---------------------------------------------------------------------- 5088 */ 5089 5090static void 5091CanvasFocusProc(canvasPtr, gotFocus) 5092 TkCanvas *canvasPtr; /* Canvas that just got or lost focus. */ 5093 int gotFocus; /* 1 means window is getting focus, 0 means 5094 * it's losing it. */ 5095{ 5096 Tcl_DeleteTimerHandler(canvasPtr->insertBlinkHandler); 5097 if (gotFocus) { 5098 canvasPtr->textInfo.gotFocus = 1; 5099 canvasPtr->textInfo.cursorOn = 1; 5100 if (canvasPtr->insertOffTime != 0) { 5101 canvasPtr->insertBlinkHandler = Tcl_CreateTimerHandler( 5102 canvasPtr->insertOffTime, CanvasBlinkProc, 5103 (ClientData) canvasPtr); 5104 } 5105 } else { 5106 canvasPtr->textInfo.gotFocus = 0; 5107 canvasPtr->textInfo.cursorOn = 0; 5108 canvasPtr->insertBlinkHandler = (Tcl_TimerToken) NULL; 5109 } 5110 if (canvasPtr->textInfo.focusItemPtr != NULL) { 5111 EventuallyRedrawItem((Tk_Canvas) canvasPtr, 5112 canvasPtr->textInfo.focusItemPtr); 5113 } 5114 if (canvasPtr->highlightWidth > 0) { 5115 canvasPtr->flags |= REDRAW_BORDERS; 5116 if (!(canvasPtr->flags & REDRAW_PENDING)) { 5117 Tcl_DoWhenIdle(DisplayCanvas, (ClientData) canvasPtr); 5118 canvasPtr->flags |= REDRAW_PENDING; 5119 } 5120 } 5121} 5122 5123/* 5124 *---------------------------------------------------------------------- 5125 * 5126 * CanvasSelectTo -- 5127 * 5128 * Modify the selection by moving its un-anchored end. This could 5129 * make the selection either larger or smaller. 5130 * 5131 * Results: 5132 * None. 5133 * 5134 * Side effects: 5135 * The selection changes. 5136 * 5137 *---------------------------------------------------------------------- 5138 */ 5139 5140static void 5141CanvasSelectTo(canvasPtr, itemPtr, index) 5142 TkCanvas *canvasPtr; /* Information about widget. */ 5143 Tk_Item *itemPtr; /* Item that is to hold selection. */ 5144 int index; /* Index of element that is to become the 5145 * "other" end of the selection. */ 5146{ 5147 int oldFirst, oldLast; 5148 Tk_Item *oldSelPtr; 5149 5150 oldFirst = canvasPtr->textInfo.selectFirst; 5151 oldLast = canvasPtr->textInfo.selectLast; 5152 oldSelPtr = canvasPtr->textInfo.selItemPtr; 5153 5154 /* 5155 * Grab the selection if we don't own it already. 5156 */ 5157 5158 if (canvasPtr->textInfo.selItemPtr == NULL) { 5159 Tk_OwnSelection(canvasPtr->tkwin, XA_PRIMARY, CanvasLostSelection, 5160 (ClientData) canvasPtr); 5161 } else if (canvasPtr->textInfo.selItemPtr != itemPtr) { 5162 EventuallyRedrawItem((Tk_Canvas) canvasPtr, 5163 canvasPtr->textInfo.selItemPtr); 5164 } 5165 canvasPtr->textInfo.selItemPtr = itemPtr; 5166 5167 if (canvasPtr->textInfo.anchorItemPtr != itemPtr) { 5168 canvasPtr->textInfo.anchorItemPtr = itemPtr; 5169 canvasPtr->textInfo.selectAnchor = index; 5170 } 5171 if (canvasPtr->textInfo.selectAnchor <= index) { 5172 canvasPtr->textInfo.selectFirst = canvasPtr->textInfo.selectAnchor; 5173 canvasPtr->textInfo.selectLast = index; 5174 } else { 5175 canvasPtr->textInfo.selectFirst = index; 5176 canvasPtr->textInfo.selectLast = canvasPtr->textInfo.selectAnchor - 1; 5177 } 5178 if ((canvasPtr->textInfo.selectFirst != oldFirst) 5179 || (canvasPtr->textInfo.selectLast != oldLast) 5180 || (itemPtr != oldSelPtr)) { 5181 EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr); 5182 } 5183} 5184 5185/* 5186 *-------------------------------------------------------------- 5187 * 5188 * CanvasFetchSelection -- 5189 * 5190 * This procedure is invoked by Tk to return part or all of 5191 * the selection, when the selection is in a canvas widget. 5192 * This procedure always returns the selection as a STRING. 5193 * 5194 * Results: 5195 * The return value is the number of non-NULL bytes stored 5196 * at buffer. Buffer is filled (or partially filled) with a 5197 * NULL-terminated string containing part or all of the selection, 5198 * as given by offset and maxBytes. 5199 * 5200 * Side effects: 5201 * None. 5202 * 5203 *-------------------------------------------------------------- 5204 */ 5205 5206static int 5207CanvasFetchSelection(clientData, offset, buffer, maxBytes) 5208 ClientData clientData; /* Information about canvas widget. */ 5209 int offset; /* Offset within selection of first 5210 * character to be returned. */ 5211 char *buffer; /* Location in which to place 5212 * selection. */ 5213 int maxBytes; /* Maximum number of bytes to place 5214 * at buffer, not including terminating 5215 * NULL character. */ 5216{ 5217 TkCanvas *canvasPtr = (TkCanvas *) clientData; 5218 5219 if (canvasPtr->textInfo.selItemPtr == NULL) { 5220 return -1; 5221 } 5222 if (canvasPtr->textInfo.selItemPtr->typePtr->selectionProc == NULL) { 5223 return -1; 5224 } 5225 return (*canvasPtr->textInfo.selItemPtr->typePtr->selectionProc)( 5226 (Tk_Canvas) canvasPtr, canvasPtr->textInfo.selItemPtr, offset, 5227 buffer, maxBytes); 5228} 5229 5230/* 5231 *---------------------------------------------------------------------- 5232 * 5233 * CanvasLostSelection -- 5234 * 5235 * This procedure is called back by Tk when the selection is 5236 * grabbed away from a canvas widget. 5237 * 5238 * Results: 5239 * None. 5240 * 5241 * Side effects: 5242 * The existing selection is unhighlighted, and the window is 5243 * marked as not containing a selection. 5244 * 5245 *---------------------------------------------------------------------- 5246 */ 5247 5248static void 5249CanvasLostSelection(clientData) 5250 ClientData clientData; /* Information about entry widget. */ 5251{ 5252 TkCanvas *canvasPtr = (TkCanvas *) clientData; 5253 5254 if (canvasPtr->textInfo.selItemPtr != NULL) { 5255 EventuallyRedrawItem((Tk_Canvas) canvasPtr, 5256 canvasPtr->textInfo.selItemPtr); 5257 } 5258 canvasPtr->textInfo.selItemPtr = NULL; 5259} 5260 5261/* 5262 *-------------------------------------------------------------- 5263 * 5264 * GridAlign -- 5265 * 5266 * Given a coordinate and a grid spacing, this procedure 5267 * computes the location of the nearest grid line to the 5268 * coordinate. 5269 * 5270 * Results: 5271 * The return value is the location of the grid line nearest 5272 * to coord. 5273 * 5274 * Side effects: 5275 * None. 5276 * 5277 *-------------------------------------------------------------- 5278 */ 5279 5280static double 5281GridAlign(coord, spacing) 5282 double coord; /* Coordinate to grid-align. */ 5283 double spacing; /* Spacing between grid lines. If <= 0 5284 * then no alignment is done. */ 5285{ 5286 if (spacing <= 0.0) { 5287 return coord; 5288 } 5289 if (coord < 0) { 5290 return -((int) ((-coord)/spacing + 0.5)) * spacing; 5291 } 5292 return ((int) (coord/spacing + 0.5)) * spacing; 5293} 5294 5295/* 5296 *---------------------------------------------------------------------- 5297 * 5298 * ScrollFractions -- 5299 * 5300 * Given the range that's visible in the window and the "100% 5301 * range" for what's in the canvas, return a list of two 5302 * doubles representing the scroll fractions. This procedure 5303 * is used for both x and y scrolling. 5304 * 5305 * Results: 5306 * The memory pointed to by string is modified to hold 5307 * two real numbers containing the scroll fractions (between 5308 * 0 and 1) corresponding to the other arguments. 5309 * 5310 * Side effects: 5311 * None. 5312 * 5313 *---------------------------------------------------------------------- 5314 */ 5315 5316static Tcl_Obj * 5317ScrollFractions(screen1, screen2, object1, object2) 5318 int screen1; /* Lowest coordinate visible in the window. */ 5319 int screen2; /* Highest coordinate visible in the window. */ 5320 int object1; /* Lowest coordinate in the object. */ 5321 int object2; /* Highest coordinate in the object. */ 5322{ 5323 double range, f1, f2; 5324 char buffer[2*TCL_DOUBLE_SPACE+2]; 5325 5326 range = object2 - object1; 5327 if (range <= 0) { 5328 f1 = 0; 5329 f2 = 1.0; 5330 } else { 5331 f1 = (screen1 - object1)/range; 5332 if (f1 < 0) { 5333 f1 = 0.0; 5334 } 5335 f2 = (screen2 - object1)/range; 5336 if (f2 > 1.0) { 5337 f2 = 1.0; 5338 } 5339 if (f2 < f1) { 5340 f2 = f1; 5341 } 5342 } 5343 sprintf(buffer, "%g %g", f1, f2); 5344 return Tcl_NewStringObj(buffer, -1); 5345} 5346 5347/* 5348 *-------------------------------------------------------------- 5349 * 5350 * CanvasUpdateScrollbars -- 5351 * 5352 * This procedure is invoked whenever a canvas has changed in 5353 * a way that requires scrollbars to be redisplayed (e.g. the 5354 * view in the canvas has changed). 5355 * 5356 * Results: 5357 * None. 5358 * 5359 * Side effects: 5360 * If there are scrollbars associated with the canvas, then 5361 * their scrolling commands are invoked to cause them to 5362 * redisplay. If errors occur, additional Tcl commands may 5363 * be invoked to process the errors. 5364 * 5365 *-------------------------------------------------------------- 5366 */ 5367 5368static void 5369CanvasUpdateScrollbars(canvasPtr) 5370 TkCanvas *canvasPtr; /* Information about canvas. */ 5371{ 5372 int result; 5373 Tcl_Interp *interp; 5374 int xOrigin, yOrigin, inset, width, height, scrollX1, scrollX2, 5375 scrollY1, scrollY2; 5376 char *xScrollCmd, *yScrollCmd; 5377 5378 /* 5379 * Save all the relevant values from the canvasPtr, because it might be 5380 * deleted as part of either of the two calls to Tcl_VarEval below. 5381 */ 5382 5383 interp = canvasPtr->interp; 5384 Tcl_Preserve((ClientData) interp); 5385 xScrollCmd = canvasPtr->xScrollCmd; 5386 if (xScrollCmd != (char *) NULL) { 5387 Tcl_Preserve((ClientData) xScrollCmd); 5388 } 5389 yScrollCmd = canvasPtr->yScrollCmd; 5390 if (yScrollCmd != (char *) NULL) { 5391 Tcl_Preserve((ClientData) yScrollCmd); 5392 } 5393 xOrigin = canvasPtr->xOrigin; 5394 yOrigin = canvasPtr->yOrigin; 5395 inset = canvasPtr->inset; 5396 width = Tk_Width(canvasPtr->tkwin); 5397 height = Tk_Height(canvasPtr->tkwin); 5398 scrollX1 = canvasPtr->scrollX1; 5399 scrollX2 = canvasPtr->scrollX2; 5400 scrollY1 = canvasPtr->scrollY1; 5401 scrollY2 = canvasPtr->scrollY2; 5402 canvasPtr->flags &= ~UPDATE_SCROLLBARS; 5403 if (canvasPtr->xScrollCmd != NULL) { 5404 Tcl_Obj *fractions = ScrollFractions(xOrigin + inset, 5405 xOrigin + width - inset, scrollX1, scrollX2); 5406 result = Tcl_VarEval(interp, xScrollCmd, " ", 5407 Tcl_GetString(fractions), (char *) NULL); 5408 Tcl_DecrRefCount(fractions); 5409 if (result != TCL_OK) { 5410 Tcl_BackgroundError(interp); 5411 } 5412 Tcl_ResetResult(interp); 5413 Tcl_Release((ClientData) xScrollCmd); 5414 } 5415 5416 if (yScrollCmd != NULL) { 5417 Tcl_Obj *fractions = ScrollFractions(yOrigin + inset, 5418 yOrigin + height - inset, scrollY1, scrollY2); 5419 result = Tcl_VarEval(interp, yScrollCmd, " ", 5420 Tcl_GetString(fractions), (char *) NULL); 5421 Tcl_DecrRefCount(fractions); 5422 if (result != TCL_OK) { 5423 Tcl_BackgroundError(interp); 5424 } 5425 Tcl_ResetResult(interp); 5426 Tcl_Release((ClientData) yScrollCmd); 5427 } 5428 Tcl_Release((ClientData) interp); 5429} 5430 5431/* 5432 *-------------------------------------------------------------- 5433 * 5434 * CanvasSetOrigin -- 5435 * 5436 * This procedure is invoked to change the mapping between 5437 * canvas coordinates and screen coordinates in the canvas 5438 * window. 5439 * 5440 * Results: 5441 * None. 5442 * 5443 * Side effects: 5444 * The canvas will be redisplayed to reflect the change in 5445 * view. In addition, scrollbars will be updated if there 5446 * are any. 5447 * 5448 *-------------------------------------------------------------- 5449 */ 5450 5451static void 5452CanvasSetOrigin(canvasPtr, xOrigin, yOrigin) 5453 TkCanvas *canvasPtr; /* Information about canvas. */ 5454 int xOrigin; /* New X origin for canvas (canvas x-coord 5455 * corresponding to left edge of canvas 5456 * window). */ 5457 int yOrigin; /* New Y origin for canvas (canvas y-coord 5458 * corresponding to top edge of canvas 5459 * window). */ 5460{ 5461 int left, right, top, bottom, delta; 5462 5463 /* 5464 * If scroll increments have been set, round the window origin 5465 * to the nearest multiple of the increments. Remember, the 5466 * origin is the place just inside the borders, not the upper 5467 * left corner. 5468 */ 5469 5470 if (canvasPtr->xScrollIncrement > 0) { 5471 if (xOrigin >= 0) { 5472 xOrigin += canvasPtr->xScrollIncrement/2; 5473 xOrigin -= (xOrigin + canvasPtr->inset) 5474 % canvasPtr->xScrollIncrement; 5475 } else { 5476 xOrigin = (-xOrigin) + canvasPtr->xScrollIncrement/2; 5477 xOrigin = -(xOrigin - (xOrigin - canvasPtr->inset) 5478 % canvasPtr->xScrollIncrement); 5479 } 5480 } 5481 if (canvasPtr->yScrollIncrement > 0) { 5482 if (yOrigin >= 0) { 5483 yOrigin += canvasPtr->yScrollIncrement/2; 5484 yOrigin -= (yOrigin + canvasPtr->inset) 5485 % canvasPtr->yScrollIncrement; 5486 } else { 5487 yOrigin = (-yOrigin) + canvasPtr->yScrollIncrement/2; 5488 yOrigin = -(yOrigin - (yOrigin - canvasPtr->inset) 5489 % canvasPtr->yScrollIncrement); 5490 } 5491 } 5492 5493 /* 5494 * Adjust the origin if necessary to keep as much as possible of the 5495 * canvas in the view. The variables left, right, etc. keep track of 5496 * how much extra space there is on each side of the view before it 5497 * will stick out past the scroll region. If one side sticks out past 5498 * the edge of the scroll region, adjust the view to bring that side 5499 * back to the edge of the scrollregion (but don't move it so much that 5500 * the other side sticks out now). If scroll increments are in effect, 5501 * be sure to adjust only by full increments. 5502 */ 5503 5504 if ((canvasPtr->confine) && (canvasPtr->regionString != NULL)) { 5505 left = xOrigin + canvasPtr->inset - canvasPtr->scrollX1; 5506 right = canvasPtr->scrollX2 5507 - (xOrigin + Tk_Width(canvasPtr->tkwin) - canvasPtr->inset); 5508 top = yOrigin + canvasPtr->inset - canvasPtr->scrollY1; 5509 bottom = canvasPtr->scrollY2 5510 - (yOrigin + Tk_Height(canvasPtr->tkwin) - canvasPtr->inset); 5511 if ((left < 0) && (right > 0)) { 5512 delta = (right > -left) ? -left : right; 5513 if (canvasPtr->xScrollIncrement > 0) { 5514 delta -= delta % canvasPtr->xScrollIncrement; 5515 } 5516 xOrigin += delta; 5517 } else if ((right < 0) && (left > 0)) { 5518 delta = (left > -right) ? -right : left; 5519 if (canvasPtr->xScrollIncrement > 0) { 5520 delta -= delta % canvasPtr->xScrollIncrement; 5521 } 5522 xOrigin -= delta; 5523 } 5524 if ((top < 0) && (bottom > 0)) { 5525 delta = (bottom > -top) ? -top : bottom; 5526 if (canvasPtr->yScrollIncrement > 0) { 5527 delta -= delta % canvasPtr->yScrollIncrement; 5528 } 5529 yOrigin += delta; 5530 } else if ((bottom < 0) && (top > 0)) { 5531 delta = (top > -bottom) ? -bottom : top; 5532 if (canvasPtr->yScrollIncrement > 0) { 5533 delta -= delta % canvasPtr->yScrollIncrement; 5534 } 5535 yOrigin -= delta; 5536 } 5537 } 5538 5539 if ((xOrigin == canvasPtr->xOrigin) && (yOrigin == canvasPtr->yOrigin)) { 5540 return; 5541 } 5542 5543 /* 5544 * Tricky point: must redisplay not only everything that's visible 5545 * in the window's final configuration, but also everything that was 5546 * visible in the initial configuration. This is needed because some 5547 * item types, like windows, need to know when they move off-screen 5548 * so they can explicitly undisplay themselves. 5549 */ 5550 5551 Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr, 5552 canvasPtr->xOrigin, canvasPtr->yOrigin, 5553 canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin), 5554 canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin)); 5555 canvasPtr->xOrigin = xOrigin; 5556 canvasPtr->yOrigin = yOrigin; 5557 canvasPtr->flags |= UPDATE_SCROLLBARS; 5558 Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr, 5559 canvasPtr->xOrigin, canvasPtr->yOrigin, 5560 canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin), 5561 canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin)); 5562} 5563 5564/* 5565 *---------------------------------------------------------------------- 5566 * 5567 * GetStringsFromObjs 5568 * 5569 * Results: 5570 * Converts object list into string list. 5571 * 5572 * Side effects: 5573 * Memory is allocated for the argv array, which must 5574 * be freed using ckfree() when no longer needed. 5575 * 5576 *---------------------------------------------------------------------- 5577 */ 5578/* ARGSUSED */ 5579static CONST char ** 5580GetStringsFromObjs(argc, objv) 5581 int argc; 5582 Tcl_Obj *CONST objv[]; 5583{ 5584 register int i; 5585 CONST char **argv; 5586 if (argc <= 0) { 5587 return NULL; 5588 } 5589 argv = (CONST char **) ckalloc((argc+1) * sizeof(char *)); 5590 for (i = 0; i < argc; i++) { 5591 argv[i] = Tcl_GetStringFromObj(objv[i], NULL); 5592 } 5593 argv[argc] = 0; 5594 return argv; 5595} 5596 5597/* 5598 *-------------------------------------------------------------- 5599 * 5600 * Tk_CanvasPsColor -- 5601 * 5602 * This procedure is called by individual canvas items when 5603 * they want to set a color value for output. Given information 5604 * about an X color, this procedure will generate Postscript 5605 * commands to set up an appropriate color in Postscript. 5606 * 5607 * Results: 5608 * Returns a standard Tcl return value. If an error occurs 5609 * then an error message will be left in interp->result. 5610 * If no error occurs, then additional Postscript will be 5611 * appended to interp->result. 5612 * 5613 * Side effects: 5614 * None. 5615 * 5616 *-------------------------------------------------------------- 5617 */ 5618 5619int 5620Tk_CanvasPsColor(interp, canvas, colorPtr) 5621 Tcl_Interp *interp; /* Interpreter for returning Postscript 5622 * or error message. */ 5623 Tk_Canvas canvas; /* Information about canvas. */ 5624 XColor *colorPtr; /* Information about color. */ 5625{ 5626 return Tk_PostscriptColor(interp, ((TkCanvas *) canvas)->psInfo, 5627 colorPtr); 5628} 5629 5630/* 5631 *-------------------------------------------------------------- 5632 * 5633 * Tk_CanvasPsFont -- 5634 * 5635 * This procedure is called by individual canvas items when 5636 * they want to output text. Given information about an X 5637 * font, this procedure will generate Postscript commands 5638 * to set up an appropriate font in Postscript. 5639 * 5640 * Results: 5641 * Returns a standard Tcl return value. If an error occurs 5642 * then an error message will be left in interp->result. 5643 * If no error occurs, then additional Postscript will be 5644 * appended to the interp->result. 5645 * 5646 * Side effects: 5647 * The Postscript font name is entered into psInfoPtr->fontTable 5648 * if it wasn't already there. 5649 * 5650 *-------------------------------------------------------------- 5651 */ 5652 5653int 5654Tk_CanvasPsFont(interp, canvas, tkfont) 5655 Tcl_Interp *interp; /* Interpreter for returning Postscript 5656 * or error message. */ 5657 Tk_Canvas canvas; /* Information about canvas. */ 5658 Tk_Font tkfont; /* Information about font in which text 5659 * is to be printed. */ 5660{ 5661 return Tk_PostscriptFont(interp, ((TkCanvas *) canvas)->psInfo, tkfont); 5662} 5663 5664/* 5665 *-------------------------------------------------------------- 5666 * 5667 * Tk_CanvasPsBitmap -- 5668 * 5669 * This procedure is called to output the contents of a 5670 * sub-region of a bitmap in proper image data format for 5671 * Postscript (i.e. data between angle brackets, one bit 5672 * per pixel). 5673 * 5674 * Results: 5675 * Returns a standard Tcl return value. If an error occurs 5676 * then an error message will be left in interp->result. 5677 * If no error occurs, then additional Postscript will be 5678 * appended to interp->result. 5679 * 5680 * Side effects: 5681 * None. 5682 * 5683 *-------------------------------------------------------------- 5684 */ 5685 5686int 5687Tk_CanvasPsBitmap(interp, canvas, bitmap, startX, startY, width, height) 5688 Tcl_Interp *interp; /* Interpreter for returning Postscript 5689 * or error message. */ 5690 Tk_Canvas canvas; /* Information about canvas. */ 5691 Pixmap bitmap; /* Bitmap for which to generate 5692 * Postscript. */ 5693 int startX, startY; /* Coordinates of upper-left corner 5694 * of rectangular region to output. */ 5695 int width, height; /* Height of rectangular region. */ 5696{ 5697 return Tk_PostscriptBitmap(interp, ((TkCanvas *) canvas)->tkwin, 5698 ((TkCanvas *) canvas)->psInfo, bitmap, startX, startY, 5699 width, height); 5700} 5701 5702/* 5703 *-------------------------------------------------------------- 5704 * 5705 * Tk_CanvasPsStipple -- 5706 * 5707 * This procedure is called by individual canvas items when 5708 * they have created a path that they'd like to be filled with 5709 * a stipple pattern. Given information about an X bitmap, 5710 * this procedure will generate Postscript commands to fill 5711 * the current clip region using a stipple pattern defined by the 5712 * bitmap. 5713 * 5714 * Results: 5715 * Returns a standard Tcl return value. If an error occurs 5716 * then an error message will be left in interp->result. 5717 * If no error occurs, then additional Postscript will be 5718 * appended to interp->result. 5719 * 5720 * Side effects: 5721 * None. 5722 * 5723 *-------------------------------------------------------------- 5724 */ 5725 5726int 5727Tk_CanvasPsStipple(interp, canvas, bitmap) 5728 Tcl_Interp *interp; /* Interpreter for returning Postscript 5729 * or error message. */ 5730 Tk_Canvas canvas; /* Information about canvas. */ 5731 Pixmap bitmap; /* Bitmap to use for stippling. */ 5732{ 5733 return Tk_PostscriptStipple(interp, ((TkCanvas *) canvas)->tkwin, 5734 ((TkCanvas *) canvas)->psInfo, bitmap); 5735} 5736 5737/* 5738 *-------------------------------------------------------------- 5739 * 5740 * Tk_CanvasPsY -- 5741 * 5742 * Given a y-coordinate in canvas coordinates, this procedure 5743 * returns a y-coordinate to use for Postscript output. 5744 * 5745 * Results: 5746 * Returns the Postscript coordinate that corresponds to 5747 * "y". 5748 * 5749 * Side effects: 5750 * None. 5751 * 5752 *-------------------------------------------------------------- 5753 */ 5754 5755double 5756Tk_CanvasPsY(canvas, y) 5757 Tk_Canvas canvas; /* Token for canvas on whose behalf 5758 * Postscript is being generated. */ 5759 double y; /* Y-coordinate in canvas coords. */ 5760{ 5761 return Tk_PostscriptY(y, ((TkCanvas *) canvas)->psInfo); 5762} 5763 5764/* 5765 *-------------------------------------------------------------- 5766 * 5767 * Tk_CanvasPsPath -- 5768 * 5769 * Given an array of points for a path, generate Postscript 5770 * commands to create the path. 5771 * 5772 * Results: 5773 * Postscript commands get appended to what's in interp->result. 5774 * 5775 * Side effects: 5776 * None. 5777 * 5778 *-------------------------------------------------------------- 5779 */ 5780 5781void 5782Tk_CanvasPsPath(interp, canvas, coordPtr, numPoints) 5783 Tcl_Interp *interp; /* Put generated Postscript in this 5784 * interpreter's result field. */ 5785 Tk_Canvas canvas; /* Canvas on whose behalf Postscript 5786 * is being generated. */ 5787 double *coordPtr; /* Pointer to first in array of 5788 * 2*numPoints coordinates giving 5789 * points for path. */ 5790 int numPoints; /* Number of points at *coordPtr. */ 5791{ 5792 Tk_PostscriptPath(interp, ((TkCanvas *) canvas)->psInfo, 5793 coordPtr, numPoints); 5794} 5795