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