4 * This module implements canvas widgets for the Tk toolkit.
5 * A canvas displays a background and a collection of graphical
6 * objects such as rectangles, lines, and texts.
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.
12 * See the file "license.terms" for information on usage and redistribution
13 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
18 /* #define USE_OLD_TAG_SEARCH 1 */
26 * See tkCanvas.h for key data structures used to implement canvases.
29 #ifdef USE_OLD_TAG_SEARCH
31 * The structure defined below is used to keep track of a tag search
32 * in progress. No field should be accessed by anyone other than
33 * StartTagSearch and NextItem.
36 typedef struct TagSearch {
37 TkCanvas *canvasPtr; /* Canvas widget being searched. */
38 Tk_Uid tag; /* Tag to search for. 0 means return
40 Tk_Item *currentPtr; /* Pointer to last item returned. */
41 Tk_Item *lastPtr; /* The item right before the currentPtr
42 * is tracked so if the currentPtr is
43 * deleted we don't have to start from the
45 int searchOver; /* Non-zero means NextItem should always
49 #else /* USE_OLD_TAG_SEARCH */
51 * The structure defined below is used to keep track of a tag search
52 * in progress. No field should be accessed by anyone other than
53 * TagSearchScan, TagSearchFirst, TagSearchNext,
54 * TagSearchScanExpr, TagSearchEvalExpr,
55 * TagSearchExprInit, TagSearchExprDestroy,
58 * Not quite accurate: the TagSearch structure is also accessed from:
59 * CanvasWidgetCmd, FindItems, RelinkItems
60 * The only instances of the structure are owned by:
62 * CanvasWidgetCmd is the only function that calls:
63 * FindItems, RelinkItems
64 * CanvasWidgetCmd, FindItems, RelinkItems, are the only functions that call
69 typedef struct TagSearch {
70 TkCanvas *canvasPtr; /* Canvas widget being searched. */
71 Tk_Item *currentPtr; /* Pointer to last item returned. */
72 Tk_Item *lastPtr; /* The item right before the currentPtr
73 * is tracked so if the currentPtr is
74 * deleted we don't have to start from the
76 int searchOver; /* Non-zero means NextItem should always
78 int type; /* search type */
79 int id; /* item id for searches by id */
81 char *string; /* tag expression string */
82 int stringIndex; /* current position in string scan */
83 int stringLength; /* length of tag expression string */
85 char *rewritebuffer; /* tag string (after removing escapes) */
86 unsigned int rewritebufferAllocated; /* available space for rewrites */
88 TagSearchExpr *expr; /* compiled tag expression */
90 #endif /* USE_OLD_TAG_SEARCH */
93 * Custom option for handling "-state" and "-offset"
96 static Tk_CustomOption stateOption = {
97 (Tk_OptionParseProc *) TkStateParseProc,
99 (ClientData) NULL /* only "normal" and "disabled" */
102 static Tk_CustomOption offsetOption = {
103 (Tk_OptionParseProc *) TkOffsetParseProc,
105 (ClientData) TK_OFFSET_RELATIVE
109 * Information used for argv parsing.
112 static Tk_ConfigSpec configSpecs[] = {
113 {TK_CONFIG_BORDER, "-background", "background", "Background",
114 DEF_CANVAS_BG_COLOR, Tk_Offset(TkCanvas, bgBorder),
115 TK_CONFIG_COLOR_ONLY},
116 {TK_CONFIG_BORDER, "-background", "background", "Background",
117 DEF_CANVAS_BG_MONO, Tk_Offset(TkCanvas, bgBorder),
118 TK_CONFIG_MONO_ONLY},
119 {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
120 (char *) NULL, 0, 0},
121 {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
122 (char *) NULL, 0, 0},
123 {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
124 DEF_CANVAS_BORDER_WIDTH, Tk_Offset(TkCanvas, borderWidth), 0},
125 {TK_CONFIG_DOUBLE, "-closeenough", "closeEnough", "CloseEnough",
126 DEF_CANVAS_CLOSE_ENOUGH, Tk_Offset(TkCanvas, closeEnough), 0},
127 {TK_CONFIG_BOOLEAN, "-confine", "confine", "Confine",
128 DEF_CANVAS_CONFINE, Tk_Offset(TkCanvas, confine), 0},
129 {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
130 DEF_CANVAS_CURSOR, Tk_Offset(TkCanvas, cursor), TK_CONFIG_NULL_OK},
131 {TK_CONFIG_PIXELS, "-height", "height", "Height",
132 DEF_CANVAS_HEIGHT, Tk_Offset(TkCanvas, height), 0},
133 {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
134 "HighlightBackground", DEF_CANVAS_HIGHLIGHT_BG,
135 Tk_Offset(TkCanvas, highlightBgColorPtr), 0},
136 {TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
137 DEF_CANVAS_HIGHLIGHT, Tk_Offset(TkCanvas, highlightColorPtr), 0},
138 {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
139 "HighlightThickness",
140 DEF_CANVAS_HIGHLIGHT_WIDTH, Tk_Offset(TkCanvas, highlightWidth), 0},
141 {TK_CONFIG_BORDER, "-insertbackground", "insertBackground", "Foreground",
142 DEF_CANVAS_INSERT_BG, Tk_Offset(TkCanvas, textInfo.insertBorder), 0},
143 {TK_CONFIG_PIXELS, "-insertborderwidth", "insertBorderWidth", "BorderWidth",
144 DEF_CANVAS_INSERT_BD_COLOR,
145 Tk_Offset(TkCanvas, textInfo.insertBorderWidth), TK_CONFIG_COLOR_ONLY},
146 {TK_CONFIG_PIXELS, "-insertborderwidth", "insertBorderWidth", "BorderWidth",
147 DEF_CANVAS_INSERT_BD_MONO,
148 Tk_Offset(TkCanvas, textInfo.insertBorderWidth), TK_CONFIG_MONO_ONLY},
149 {TK_CONFIG_INT, "-insertofftime", "insertOffTime", "OffTime",
150 DEF_CANVAS_INSERT_OFF_TIME, Tk_Offset(TkCanvas, insertOffTime), 0},
151 {TK_CONFIG_INT, "-insertontime", "insertOnTime", "OnTime",
152 DEF_CANVAS_INSERT_ON_TIME, Tk_Offset(TkCanvas, insertOnTime), 0},
153 {TK_CONFIG_PIXELS, "-insertwidth", "insertWidth", "InsertWidth",
154 DEF_CANVAS_INSERT_WIDTH, Tk_Offset(TkCanvas, textInfo.insertWidth), 0},
155 {TK_CONFIG_CUSTOM, "-offset", "offset", "Offset", "0,0",
156 Tk_Offset(TkCanvas, tsoffset),TK_CONFIG_DONT_SET_DEFAULT,
158 {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
159 DEF_CANVAS_RELIEF, Tk_Offset(TkCanvas, relief), 0},
160 {TK_CONFIG_STRING, "-scrollregion", "scrollRegion", "ScrollRegion",
161 DEF_CANVAS_SCROLL_REGION, Tk_Offset(TkCanvas, regionString),
163 {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
164 DEF_CANVAS_SELECT_COLOR, Tk_Offset(TkCanvas, textInfo.selBorder),
165 TK_CONFIG_COLOR_ONLY},
166 {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
167 DEF_CANVAS_SELECT_MONO, Tk_Offset(TkCanvas, textInfo.selBorder),
168 TK_CONFIG_MONO_ONLY},
169 {TK_CONFIG_PIXELS, "-selectborderwidth", "selectBorderWidth", "BorderWidth",
170 DEF_CANVAS_SELECT_BD_COLOR,
171 Tk_Offset(TkCanvas, textInfo.selBorderWidth), TK_CONFIG_COLOR_ONLY},
172 {TK_CONFIG_PIXELS, "-selectborderwidth", "selectBorderWidth", "BorderWidth",
173 DEF_CANVAS_SELECT_BD_MONO, Tk_Offset(TkCanvas, textInfo.selBorderWidth),
174 TK_CONFIG_MONO_ONLY},
175 {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
176 DEF_CANVAS_SELECT_FG_COLOR, Tk_Offset(TkCanvas, textInfo.selFgColorPtr),
177 TK_CONFIG_COLOR_ONLY},
178 {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
179 DEF_CANVAS_SELECT_FG_MONO, Tk_Offset(TkCanvas, textInfo.selFgColorPtr),
180 TK_CONFIG_MONO_ONLY},
181 {TK_CONFIG_CUSTOM, "-state", "state", "State",
182 "normal", Tk_Offset(TkCanvas, canvas_state), TK_CONFIG_DONT_SET_DEFAULT,
184 {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
185 DEF_CANVAS_TAKE_FOCUS, Tk_Offset(TkCanvas, takeFocus),
187 {TK_CONFIG_PIXELS, "-width", "width", "Width",
188 DEF_CANVAS_WIDTH, Tk_Offset(TkCanvas, width), 0},
189 {TK_CONFIG_STRING, "-xscrollcommand", "xScrollCommand", "ScrollCommand",
190 DEF_CANVAS_X_SCROLL_CMD, Tk_Offset(TkCanvas, xScrollCmd),
192 {TK_CONFIG_PIXELS, "-xscrollincrement", "xScrollIncrement",
194 DEF_CANVAS_X_SCROLL_INCREMENT, Tk_Offset(TkCanvas, xScrollIncrement),
196 {TK_CONFIG_STRING, "-yscrollcommand", "yScrollCommand", "ScrollCommand",
197 DEF_CANVAS_Y_SCROLL_CMD, Tk_Offset(TkCanvas, yScrollCmd),
199 {TK_CONFIG_PIXELS, "-yscrollincrement", "yScrollIncrement",
201 DEF_CANVAS_Y_SCROLL_INCREMENT, Tk_Offset(TkCanvas, yScrollIncrement),
203 {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
208 * List of all the item types known at present:
211 static Tk_ItemType *typeList = NULL; /* NULL means initialization hasn't
214 #ifndef USE_OLD_TAG_SEARCH
216 * Uids for operands in compiled advanced tag search expressions
217 * Initialization is done by InitCanvas()
219 static Tk_Uid allUid = NULL;
220 static Tk_Uid currentUid = NULL;
221 static Tk_Uid andUid = NULL;
222 static Tk_Uid orUid = NULL;
223 static Tk_Uid xorUid = NULL;
224 static Tk_Uid parenUid = NULL;
225 static Tk_Uid negparenUid = NULL;
226 static Tk_Uid endparenUid = NULL;
227 static Tk_Uid tagvalUid = NULL;
228 static Tk_Uid negtagvalUid = NULL;
229 #endif /* USE_OLD_TAG_SEARCH */
232 * Standard item types provided by Tk:
235 extern Tk_ItemType tkArcType, tkBitmapType, tkImageType, tkLineType;
236 extern Tk_ItemType tkOvalType, tkPolygonType;
237 extern Tk_ItemType tkRectangleType, tkTextType, tkWindowType;
240 * Prototypes for procedures defined later in this file:
243 static void CanvasBindProc _ANSI_ARGS_((ClientData clientData,
245 static void CanvasBlinkProc _ANSI_ARGS_((ClientData clientData));
246 static void CanvasCmdDeletedProc _ANSI_ARGS_((
247 ClientData clientData));
248 static void CanvasDoEvent _ANSI_ARGS_((TkCanvas *canvasPtr,
250 static void CanvasEventProc _ANSI_ARGS_((ClientData clientData,
252 static int CanvasFetchSelection _ANSI_ARGS_((
253 ClientData clientData, int offset,
254 char *buffer, int maxBytes));
255 static Tk_Item * CanvasFindClosest _ANSI_ARGS_((TkCanvas *canvasPtr,
257 static void CanvasFocusProc _ANSI_ARGS_((TkCanvas *canvasPtr,
259 static void CanvasLostSelection _ANSI_ARGS_((
260 ClientData clientData));
261 static void CanvasSelectTo _ANSI_ARGS_((TkCanvas *canvasPtr,
262 Tk_Item *itemPtr, int index));
263 static void CanvasSetOrigin _ANSI_ARGS_((TkCanvas *canvasPtr,
264 int xOrigin, int yOrigin));
265 static void CanvasUpdateScrollbars _ANSI_ARGS_((
266 TkCanvas *canvasPtr));
267 static int CanvasWidgetCmd _ANSI_ARGS_((ClientData clientData,
268 Tcl_Interp *interp, int argc, Tcl_Obj *CONST *argv));
269 static void CanvasWorldChanged _ANSI_ARGS_((
270 ClientData instanceData));
271 static int ConfigureCanvas _ANSI_ARGS_((Tcl_Interp *interp,
272 TkCanvas *canvasPtr, int argc, Tcl_Obj *CONST *argv,
274 static void DestroyCanvas _ANSI_ARGS_((char *memPtr));
275 static void DisplayCanvas _ANSI_ARGS_((ClientData clientData));
276 static void DoItem _ANSI_ARGS_((Tcl_Interp *interp,
277 Tk_Item *itemPtr, Tk_Uid tag));
278 static void EventuallyRedrawItem _ANSI_ARGS_((Tk_Canvas canvas,
280 #ifdef USE_OLD_TAG_SEARCH
281 static int FindItems _ANSI_ARGS_((Tcl_Interp *interp,
282 TkCanvas *canvasPtr, int argc, Tcl_Obj *CONST *argv,
283 Tcl_Obj *newTagObj, int first));
284 #else /* USE_OLD_TAG_SEARCH */
285 static int FindItems _ANSI_ARGS_((Tcl_Interp *interp,
286 TkCanvas *canvasPtr, int argc, Tcl_Obj *CONST *argv,
287 Tcl_Obj *newTagObj, int first,
288 TagSearch **searchPtrPtr));
289 #endif /* USE_OLD_TAG_SEARCH */
290 static int FindArea _ANSI_ARGS_((Tcl_Interp *interp,
291 TkCanvas *canvasPtr, Tcl_Obj *CONST *argv, Tk_Uid uid,
293 static double GridAlign _ANSI_ARGS_((double coord, double spacing));
294 static char** GetStringsFromObjs _ANSI_ARGS_((int argc,
295 Tcl_Obj *CONST *objv));
296 static void InitCanvas _ANSI_ARGS_((void));
297 #ifdef USE_OLD_TAG_SEARCH
298 static Tk_Item * NextItem _ANSI_ARGS_((TagSearch *searchPtr));
299 #endif /* USE_OLD_TAG_SEARCH */
300 static void PickCurrentItem _ANSI_ARGS_((TkCanvas *canvasPtr,
302 static void PrintScrollFractions _ANSI_ARGS_((int screen1,
303 int screen2, int object1, int object2,
305 #ifdef USE_OLD_TAG_SEARCH
306 static void RelinkItems _ANSI_ARGS_((TkCanvas *canvasPtr,
307 Tcl_Obj *tag, Tk_Item *prevPtr));
308 static Tk_Item * StartTagSearch _ANSI_ARGS_((TkCanvas *canvasPtr,
309 Tcl_Obj *tag, TagSearch *searchPtr));
310 #else /* USE_OLD_TAG_SEARCH */
311 static int RelinkItems _ANSI_ARGS_((TkCanvas *canvasPtr,
312 Tcl_Obj *tag, Tk_Item *prevPtr,
313 TagSearch **searchPtrPtr));
314 static void TagSearchExprInit _ANSI_ARGS_ ((
315 TagSearchExpr **exprPtrPtr));
316 static void TagSearchExprDestroy _ANSI_ARGS_((TagSearchExpr *expr));
317 static void TagSearchDestroy _ANSI_ARGS_((TagSearch *searchPtr));
318 static int TagSearchScan _ANSI_ARGS_((TkCanvas *canvasPtr,
319 Tcl_Obj *tag, TagSearch **searchPtrPtr));
320 static int TagSearchScanExpr _ANSI_ARGS_((Tcl_Interp *interp,
321 TagSearch *searchPtr, TagSearchExpr *expr));
322 static int TagSearchEvalExpr _ANSI_ARGS_((TagSearchExpr *expr,
324 static Tk_Item * TagSearchFirst _ANSI_ARGS_((TagSearch *searchPtr));
325 static Tk_Item * TagSearchNext _ANSI_ARGS_((TagSearch *searchPtr));
326 #endif /* USE_OLD_TAG_SEARCH */
329 * The structure below defines canvas class behavior by means of procedures
330 * that can be invoked from generic window code.
333 static TkClassProcs canvasClass = {
334 NULL, /* createProc. */
335 CanvasWorldChanged, /* geometryProc. */
336 NULL /* modalProc. */
341 *--------------------------------------------------------------
345 * This procedure is invoked to process the "canvas" Tcl
346 * command. See the user documentation for details on what
350 * A standard Tcl result.
353 * See the user documentation.
355 *--------------------------------------------------------------
359 Tk_CanvasObjCmd(clientData, interp, argc, argv)
360 ClientData clientData; /* Main window associated with
362 Tcl_Interp *interp; /* Current interpreter. */
363 int argc; /* Number of arguments. */
364 Tcl_Obj *CONST argv[]; /* Argument objects. */
366 Tk_Window tkwin = (Tk_Window) clientData;
370 if (typeList == NULL) {
375 Tcl_WrongNumArgs(interp, 1, argv, "pathName ?options?");
379 new = Tk_CreateWindowFromPath(interp, tkwin,
380 Tcl_GetString(argv[1]), (char *) NULL);
386 * Initialize fields that won't be initialized by ConfigureCanvas,
387 * or which ConfigureCanvas expects to have reasonable values
388 * (e.g. resource pointers).
391 canvasPtr = (TkCanvas *) ckalloc(sizeof(TkCanvas));
392 canvasPtr->tkwin = new;
393 canvasPtr->display = Tk_Display(new);
394 canvasPtr->interp = interp;
395 canvasPtr->widgetCmd = Tcl_CreateObjCommand(interp,
396 Tk_PathName(canvasPtr->tkwin), CanvasWidgetCmd,
397 (ClientData) canvasPtr, CanvasCmdDeletedProc);
398 canvasPtr->firstItemPtr = NULL;
399 canvasPtr->lastItemPtr = NULL;
400 canvasPtr->borderWidth = 0;
401 canvasPtr->bgBorder = NULL;
402 canvasPtr->relief = TK_RELIEF_FLAT;
403 canvasPtr->highlightWidth = 0;
404 canvasPtr->highlightBgColorPtr = NULL;
405 canvasPtr->highlightColorPtr = NULL;
406 canvasPtr->inset = 0;
407 canvasPtr->pixmapGC = None;
408 canvasPtr->width = None;
409 canvasPtr->height = None;
410 canvasPtr->confine = 0;
411 canvasPtr->textInfo.selBorder = NULL;
412 canvasPtr->textInfo.selBorderWidth = 0;
413 canvasPtr->textInfo.selFgColorPtr = NULL;
414 canvasPtr->textInfo.selItemPtr = NULL;
415 canvasPtr->textInfo.selectFirst = -1;
416 canvasPtr->textInfo.selectLast = -1;
417 canvasPtr->textInfo.anchorItemPtr = NULL;
418 canvasPtr->textInfo.selectAnchor = 0;
419 canvasPtr->textInfo.insertBorder = NULL;
420 canvasPtr->textInfo.insertWidth = 0;
421 canvasPtr->textInfo.insertBorderWidth = 0;
422 canvasPtr->textInfo.focusItemPtr = NULL;
423 canvasPtr->textInfo.gotFocus = 0;
424 canvasPtr->textInfo.cursorOn = 0;
425 canvasPtr->insertOnTime = 0;
426 canvasPtr->insertOffTime = 0;
427 canvasPtr->insertBlinkHandler = (Tcl_TimerToken) NULL;
428 canvasPtr->xOrigin = canvasPtr->yOrigin = 0;
429 canvasPtr->drawableXOrigin = canvasPtr->drawableYOrigin = 0;
430 canvasPtr->bindingTable = NULL;
431 canvasPtr->currentItemPtr = NULL;
432 canvasPtr->newCurrentPtr = NULL;
433 canvasPtr->closeEnough = 0.0;
434 canvasPtr->pickEvent.type = LeaveNotify;
435 canvasPtr->pickEvent.xcrossing.x = 0;
436 canvasPtr->pickEvent.xcrossing.y = 0;
437 canvasPtr->state = 0;
438 canvasPtr->xScrollCmd = NULL;
439 canvasPtr->yScrollCmd = NULL;
440 canvasPtr->scrollX1 = 0;
441 canvasPtr->scrollY1 = 0;
442 canvasPtr->scrollX2 = 0;
443 canvasPtr->scrollY2 = 0;
444 canvasPtr->regionString = NULL;
445 canvasPtr->xScrollIncrement = 0;
446 canvasPtr->yScrollIncrement = 0;
447 canvasPtr->scanX = 0;
448 canvasPtr->scanXOrigin = 0;
449 canvasPtr->scanY = 0;
450 canvasPtr->scanYOrigin = 0;
451 canvasPtr->hotPtr = NULL;
452 canvasPtr->hotPrevPtr = NULL;
453 canvasPtr->cursor = None;
454 canvasPtr->takeFocus = NULL;
455 canvasPtr->pixelsPerMM = WidthOfScreen(Tk_Screen(new));
456 canvasPtr->pixelsPerMM /= WidthMMOfScreen(Tk_Screen(new));
457 canvasPtr->flags = 0;
458 canvasPtr->nextId = 1;
459 canvasPtr->psInfo = NULL;
460 canvasPtr->canvas_state = TK_STATE_NORMAL;
461 canvasPtr->tsoffset.flags = 0;
462 canvasPtr->tsoffset.xoffset = 0;
463 canvasPtr->tsoffset.yoffset = 0;
464 #ifndef USE_OLD_TAG_SEARCH
465 canvasPtr->bindTagExprs = NULL;
467 Tcl_InitHashTable(&canvasPtr->idTable, TCL_ONE_WORD_KEYS);
469 Tk_SetClass(canvasPtr->tkwin, "Canvas");
470 TkSetClassProcs(canvasPtr->tkwin, &canvasClass, (ClientData) canvasPtr);
471 Tk_CreateEventHandler(canvasPtr->tkwin,
472 ExposureMask|StructureNotifyMask|FocusChangeMask,
473 CanvasEventProc, (ClientData) canvasPtr);
474 Tk_CreateEventHandler(canvasPtr->tkwin, KeyPressMask|KeyReleaseMask
475 |ButtonPressMask|ButtonReleaseMask|EnterWindowMask
476 |LeaveWindowMask|PointerMotionMask|VirtualEventMask,
477 CanvasBindProc, (ClientData) canvasPtr);
478 Tk_CreateSelHandler(canvasPtr->tkwin, XA_PRIMARY, XA_STRING,
479 CanvasFetchSelection, (ClientData) canvasPtr, XA_STRING);
480 if (ConfigureCanvas(interp, canvasPtr, argc-2, argv+2, 0) != TCL_OK) {
484 Tcl_SetResult(interp, Tk_PathName(canvasPtr->tkwin), TCL_STATIC);
488 Tk_DestroyWindow(canvasPtr->tkwin);
493 *--------------------------------------------------------------
497 * This procedure is invoked to process the Tcl command
498 * that corresponds to a widget managed by this module.
499 * See the user documentation for details on what it does.
502 * A standard Tcl result.
505 * See the user documentation.
507 *--------------------------------------------------------------
511 CanvasWidgetCmd(clientData, interp, argc, argv)
512 ClientData clientData; /* Information about canvas
514 Tcl_Interp *interp; /* Current interpreter. */
515 int argc; /* Number of arguments. */
516 Tcl_Obj *CONST argv[]; /* Argument objects. */
518 TkCanvas *canvasPtr = (TkCanvas *) clientData;
521 Tk_Item *itemPtr = NULL; /* Initialization needed only to
522 * prevent compiler warning. */
523 #ifdef USE_OLD_TAG_SEARCH
525 #else /* USE_OLD_TAG_SEARCH */
526 TagSearch *searchPtr = NULL; /* Allocated by first TagSearchScan
527 * Freed by TagSearchDestroy */
528 #endif /* USE_OLD_TAG_SEARCH */
531 static char *optionStrings[] = {
532 "addtag", "bbox", "bind", "canvasx",
533 "canvasy", "cget", "configure", "coords",
534 "create", "dchars", "delete", "dtag",
535 "find", "focus", "gettags", "icursor",
536 "index", "insert", "itemcget", "itemconfigure",
537 "lower", "move", "postscript", "raise",
538 "scale", "scan", "select", "type",
543 CANV_ADDTAG, CANV_BBOX, CANV_BIND, CANV_CANVASX,
544 CANV_CANVASY, CANV_CGET, CANV_CONFIGURE, CANV_COORDS,
545 CANV_CREATE, CANV_DCHARS, CANV_DELETE, CANV_DTAG,
546 CANV_FIND, CANV_FOCUS, CANV_GETTAGS, CANV_ICURSOR,
547 CANV_INDEX, CANV_INSERT, CANV_ITEMCGET, CANV_ITEMCONFIGURE,
548 CANV_LOWER, CANV_MOVE, CANV_POSTSCRIPT,CANV_RAISE,
549 CANV_SCALE, CANV_SCAN, CANV_SELECT, CANV_TYPE,
550 CANV_XVIEW, CANV_YVIEW
554 Tcl_WrongNumArgs(interp, 1, argv, "option ?arg arg ...?");
557 if (Tcl_GetIndexFromObj(interp, argv[1], optionStrings, "option", 0,
561 Tcl_Preserve((ClientData) canvasPtr);
564 switch ((enum options) index) {
567 Tcl_WrongNumArgs(interp, 2, argv, "tag searchCommand ?arg arg ...?");
571 #ifdef USE_OLD_TAG_SEARCH
572 result = FindItems(interp, canvasPtr, argc, argv, argv[2], 3);
573 #else /* USE_OLD_TAG_SEARCH */
574 result = FindItems(interp, canvasPtr, argc, argv, argv[2], 3, &searchPtr);
575 #endif /* USE_OLD_TAG_SEARCH */
581 int x1 = 0, y1 = 0, x2 = 0, y2 = 0; /* Initializations needed
582 * only to prevent compiler
586 Tcl_WrongNumArgs(interp, 2, argv, "tagOrId ?tagOrId ...?");
591 for (i = 2; i < argc; i++) {
592 #ifdef USE_OLD_TAG_SEARCH
593 for (itemPtr = StartTagSearch(canvasPtr, argv[i], &search);
594 itemPtr != NULL; itemPtr = NextItem(&search)) {
595 #else /* USE_OLD_TAG_SEARCH */
596 if ((result = TagSearchScan(canvasPtr, argv[i], &searchPtr)) != TCL_OK) {
599 for (itemPtr = TagSearchFirst(searchPtr);
600 itemPtr != NULL; itemPtr = TagSearchNext(searchPtr)) {
601 #endif /* USE_OLD_TAG_SEARCH */
603 if ((itemPtr->x1 >= itemPtr->x2)
604 || (itemPtr->y1 >= itemPtr->y2)) {
614 if (itemPtr->x1 < x1) {
617 if (itemPtr->y1 < y1) {
620 if (itemPtr->x2 > x2) {
623 if (itemPtr->y2 > y2) {
630 char buf[TCL_INTEGER_SPACE * 4];
632 sprintf(buf, "%d %d %d %d", x1, y1, x2, y2);
633 Tcl_SetResult(interp, buf, TCL_VOLATILE);
640 if ((argc < 3) || (argc > 5)) {
641 Tcl_WrongNumArgs(interp, 2, argv, "tagOrId ?sequence? ?command?");
647 * Figure out what object to use for the binding (individual
652 #ifdef USE_OLD_TAG_SEARCH
653 if (isdigit(UCHAR(Tcl_GetString(argv[2])[0]))) {
656 Tcl_HashEntry *entryPtr;
658 id = strtoul(Tcl_GetString(argv[2]), &end, 0);
662 entryPtr = Tcl_FindHashEntry(&canvasPtr->idTable, (char *) id);
663 if (entryPtr != NULL) {
664 itemPtr = (Tk_Item *) Tcl_GetHashValue(entryPtr);
665 object = (ClientData) itemPtr;
669 Tcl_AppendResult(interp, "item \"", Tcl_GetString(argv[2]),
670 "\" doesn't exist", (char *) NULL);
676 object = (ClientData) Tk_GetUid(Tcl_GetString(argv[2]));
678 #else /* USE_OLD_TAG_SEARCH */
679 if ((result = TagSearchScan(canvasPtr, argv[2], &searchPtr)) != TCL_OK) {
682 if (searchPtr->type == 1) {
683 Tcl_HashEntry *entryPtr;
685 entryPtr = Tcl_FindHashEntry(&canvasPtr->idTable, (char *) searchPtr->id);
686 if (entryPtr != NULL) {
687 itemPtr = (Tk_Item *) Tcl_GetHashValue(entryPtr);
688 object = (ClientData) itemPtr;
692 Tcl_AppendResult(interp, "item \"", Tcl_GetString(argv[2]),
693 "\" doesn't exist", (char *) NULL);
698 object = (ClientData) searchPtr->expr->uid;
700 #endif /* USE_OLD_TAG_SEARCH */
703 * Make a binding table if the canvas doesn't already have
707 if (canvasPtr->bindingTable == NULL) {
708 canvasPtr->bindingTable = Tk_CreateBindingTable(interp);
714 char* argv4 = Tcl_GetStringFromObj(argv[4],NULL);
717 result = Tk_DeleteBinding(interp, canvasPtr->bindingTable,
718 object, Tcl_GetStringFromObj(argv[3], NULL));
721 #ifndef USE_OLD_TAG_SEARCH
722 if (searchPtr->type == 4) {
724 * if new tag expression, then insert in linked list
726 TagSearchExpr *expr, **lastPtr;
728 lastPtr = &(canvasPtr->bindTagExprs);
729 while ((expr = *lastPtr) != NULL) {
730 if (expr->uid == searchPtr->expr->uid) {
733 lastPtr = &(expr->next);
737 * transfer ownership of expr to bindTagExprs list
739 *lastPtr = searchPtr->expr;
740 searchPtr->expr->next = NULL;
743 * flag in TagSearch that expr has changed ownership
744 * so that TagSearchDestroy doesn't try to free it
746 searchPtr->expr = NULL;
749 #endif /* not USE_OLD_TAG_SEARCH */
750 if (argv4[0] == '+') {
754 mask = Tk_CreateBinding(interp, canvasPtr->bindingTable,
755 object, Tcl_GetStringFromObj(argv[3],NULL), argv4, append);
760 if (mask & (unsigned) ~(ButtonMotionMask|Button1MotionMask
761 |Button2MotionMask|Button3MotionMask|Button4MotionMask
762 |Button5MotionMask|ButtonPressMask|ButtonReleaseMask
763 |EnterWindowMask|LeaveWindowMask|KeyPressMask
764 |KeyReleaseMask|PointerMotionMask|VirtualEventMask)) {
765 Tk_DeleteBinding(interp, canvasPtr->bindingTable,
766 object, Tcl_GetStringFromObj(argv[3], NULL));
767 Tcl_ResetResult(interp);
768 Tcl_AppendResult(interp, "requested illegal events; ",
769 "only key, button, motion, enter, leave, and virtual ",
770 "events may be used", (char *) NULL);
774 } else if (argc == 4) {
777 command = Tk_GetBinding(interp, canvasPtr->bindingTable,
778 object, Tcl_GetStringFromObj(argv[3], NULL));
779 if (command == NULL) {
782 string = Tcl_GetStringResult(interp);
784 * Ignore missing binding errors. This is a special hack
785 * that relies on the error message returned by FindSequence
789 if (string[0] != '\0') {
793 Tcl_ResetResult(interp);
796 Tcl_SetResult(interp, command, TCL_STATIC);
799 Tk_GetAllBindings(interp, canvasPtr->bindingTable, object);
806 char buf[TCL_DOUBLE_SPACE];
808 if ((argc < 3) || (argc > 4)) {
809 Tcl_WrongNumArgs(interp, 2, argv, "screenx ?gridspacing?");
813 if (Tk_GetPixelsFromObj(interp, canvasPtr->tkwin, argv[2], &x) != TCL_OK) {
818 if (Tk_CanvasGetCoordFromObj(interp, (Tk_Canvas) canvasPtr, argv[3],
826 x += canvasPtr->xOrigin;
827 Tcl_PrintDouble(interp, GridAlign((double) x, grid), buf);
828 Tcl_SetResult(interp, buf, TCL_VOLATILE);
834 char buf[TCL_DOUBLE_SPACE];
836 if ((argc < 3) || (argc > 4)) {
837 Tcl_WrongNumArgs(interp, 2, argv, "screeny ?gridspacing?");
841 if (Tk_GetPixelsFromObj(interp, canvasPtr->tkwin, argv[2], &y) != TCL_OK) {
846 if (Tk_CanvasGetCoordFromObj(interp, (Tk_Canvas) canvasPtr,
847 argv[3], &grid) != TCL_OK) {
854 y += canvasPtr->yOrigin;
855 Tcl_PrintDouble(interp, GridAlign((double) y, grid), buf);
856 Tcl_SetResult(interp, buf, TCL_VOLATILE);
861 Tcl_WrongNumArgs(interp, 2, argv, "option");
865 result = Tk_ConfigureValue(interp, canvasPtr->tkwin, configSpecs,
866 (char *) canvasPtr, Tcl_GetString(argv[2]), 0);
869 case CANV_CONFIGURE: {
871 result = Tk_ConfigureInfo(interp, canvasPtr->tkwin, configSpecs,
872 (char *) canvasPtr, (char *) NULL, 0);
873 } else if (argc == 3) {
874 result = Tk_ConfigureInfo(interp, canvasPtr->tkwin, configSpecs,
875 (char *) canvasPtr, Tcl_GetString(argv[2]), 0);
877 result = ConfigureCanvas(interp, canvasPtr, argc-2, argv+2,
878 TK_CONFIG_ARGV_ONLY);
884 Tcl_WrongNumArgs(interp, 2, argv, "tagOrId ?x y x y ...?");
888 #ifdef USE_OLD_TAG_SEARCH
889 itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
890 #else /* USE_OLD_TAG_SEARCH */
891 if ((result = TagSearchScan(canvasPtr, argv[2], &searchPtr)) != TCL_OK) {
894 itemPtr = TagSearchFirst(searchPtr);
895 #endif /* USE_OLD_TAG_SEARCH */
896 if (itemPtr != NULL) {
898 EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
900 if (itemPtr->typePtr->coordProc != NULL) {
901 if (itemPtr->typePtr->alwaysRedraw & TK_CONFIG_OBJS) {
902 result = (*itemPtr->typePtr->coordProc)(interp,
903 (Tk_Canvas) canvasPtr, itemPtr, argc-3, argv+3);
905 char **args = GetStringsFromObjs(argc-3, argv+3);
906 result = (*itemPtr->typePtr->coordProc)(interp,
907 (Tk_Canvas) canvasPtr, itemPtr, argc-3, (Tcl_Obj **) args);
908 if (args) ckfree((char *) args);
912 EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
918 Tk_ItemType *typePtr;
919 Tk_ItemType *matchPtr = NULL;
921 char buf[TCL_INTEGER_SPACE];
923 Tcl_HashEntry *entryPtr;
927 Tcl_WrongNumArgs(interp, 2, argv, "type ?arg arg ...?");
931 arg = Tcl_GetStringFromObj(argv[2], (int *) &length);
933 for (typePtr = typeList; typePtr != NULL; typePtr = typePtr->nextPtr) {
934 if ((c == typePtr->name[0])
935 && (strncmp(arg, typePtr->name, length) == 0)) {
936 if (matchPtr != NULL) {
938 Tcl_AppendResult(interp,
939 "unknown or ambiguous item type \"",
940 arg, "\"", (char *) NULL);
947 if (matchPtr == NULL) {
951 itemPtr = (Tk_Item *) ckalloc((unsigned) typePtr->itemSize);
952 itemPtr->id = canvasPtr->nextId;
954 itemPtr->tagPtr = itemPtr->staticTagSpace;
955 itemPtr->tagSpace = TK_TAG_SPACE;
956 itemPtr->numTags = 0;
957 itemPtr->typePtr = typePtr;
958 itemPtr->state = TK_STATE_NULL;
959 itemPtr->redraw_flags = 0;
960 if (itemPtr->typePtr->alwaysRedraw & TK_CONFIG_OBJS) {
961 result = (*typePtr->createProc)(interp, (Tk_Canvas) canvasPtr,
962 itemPtr, argc-3, argv+3);
964 char **args = GetStringsFromObjs(argc-3, argv+3);
965 result = (*typePtr->createProc)(interp, (Tk_Canvas) canvasPtr,
966 itemPtr, argc-3, (Tcl_Obj **) args);
967 if (args) ckfree((char *) args);
969 if (result != TCL_OK) {
970 ckfree((char *) itemPtr);
974 itemPtr->nextPtr = NULL;
975 entryPtr = Tcl_CreateHashEntry(&canvasPtr->idTable,
976 (char *) itemPtr->id, &isNew);
977 Tcl_SetHashValue(entryPtr, itemPtr);
978 itemPtr->prevPtr = canvasPtr->lastItemPtr;
979 canvasPtr->hotPtr = itemPtr;
980 canvasPtr->hotPrevPtr = canvasPtr->lastItemPtr;
981 if (canvasPtr->lastItemPtr == NULL) {
982 canvasPtr->firstItemPtr = itemPtr;
984 canvasPtr->lastItemPtr->nextPtr = itemPtr;
986 canvasPtr->lastItemPtr = itemPtr;
987 itemPtr->redraw_flags |= FORCE_REDRAW;
988 EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
989 canvasPtr->flags |= REPICK_NEEDED;
990 sprintf(buf, "%d", itemPtr->id);
991 Tcl_SetResult(interp, buf, TCL_VOLATILE);
998 if ((argc != 4) && (argc != 5)) {
999 Tcl_WrongNumArgs(interp, 2, argv, "tagOrId first ?last?");
1003 #ifdef USE_OLD_TAG_SEARCH
1004 for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
1005 itemPtr != NULL; itemPtr = NextItem(&search)) {
1006 #else /* USE_OLD_TAG_SEARCH */
1007 if ((result = TagSearchScan(canvasPtr, argv[2], &searchPtr)) != TCL_OK) {
1010 for (itemPtr = TagSearchFirst(searchPtr);
1011 itemPtr != NULL; itemPtr = TagSearchNext(searchPtr)) {
1012 #endif /* USE_OLD_TAG_SEARCH */
1013 if ((itemPtr->typePtr->indexProc == NULL)
1014 || (itemPtr->typePtr->dCharsProc == NULL)) {
1017 if (itemPtr->typePtr->alwaysRedraw & TK_CONFIG_OBJS) {
1018 result = itemPtr->typePtr->indexProc(interp, (Tk_Canvas) canvasPtr,
1019 itemPtr, (char *) argv[3], &first);
1021 result = itemPtr->typePtr->indexProc(interp, (Tk_Canvas) canvasPtr,
1022 itemPtr, Tcl_GetStringFromObj(argv[3], NULL), &first);
1024 if (result != TCL_OK) {
1028 if (itemPtr->typePtr->alwaysRedraw & TK_CONFIG_OBJS) {
1029 result = itemPtr->typePtr->indexProc(interp, (Tk_Canvas) canvasPtr,
1030 itemPtr, (char *) argv[4], &last);
1032 result = itemPtr->typePtr->indexProc(interp, (Tk_Canvas) canvasPtr,
1033 itemPtr, Tcl_GetStringFromObj(argv[4], NULL), &last);
1035 if (result != TCL_OK) {
1043 * Redraw both item's old and new areas: it's possible
1044 * that a delete could result in a new area larger than
1045 * the old area. Except if the insertProc sets the
1046 * TK_ITEM_DONT_REDRAW flag, nothing more needs to be done.
1049 x1 = itemPtr->x1; y1 = itemPtr->y1;
1050 x2 = itemPtr->x2; y2 = itemPtr->y2;
1051 itemPtr->redraw_flags &= ~TK_ITEM_DONT_REDRAW;
1052 (*itemPtr->typePtr->dCharsProc)((Tk_Canvas) canvasPtr,
1053 itemPtr, first, last);
1054 if (!(itemPtr->redraw_flags & TK_ITEM_DONT_REDRAW)) {
1055 Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
1057 EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
1059 itemPtr->redraw_flags &= ~TK_ITEM_DONT_REDRAW;
1065 Tcl_HashEntry *entryPtr;
1067 for (i = 2; i < argc; i++) {
1068 #ifdef USE_OLD_TAG_SEARCH
1069 for (itemPtr = StartTagSearch(canvasPtr, argv[i], &search);
1070 itemPtr != NULL; itemPtr = NextItem(&search)) {
1071 #else /* USE_OLD_TAG_SEARCH */
1072 if ((result = TagSearchScan(canvasPtr, argv[i], &searchPtr)) != TCL_OK) {
1075 for (itemPtr = TagSearchFirst(searchPtr);
1076 itemPtr != NULL; itemPtr = TagSearchNext(searchPtr)) {
1077 #endif /* USE_OLD_TAG_SEARCH */
1078 EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
1079 if (canvasPtr->bindingTable != NULL) {
1080 Tk_DeleteAllBindings(canvasPtr->bindingTable,
1081 (ClientData) itemPtr);
1083 (*itemPtr->typePtr->deleteProc)((Tk_Canvas) canvasPtr, itemPtr,
1084 canvasPtr->display);
1085 if (itemPtr->tagPtr != itemPtr->staticTagSpace) {
1086 ckfree((char *) itemPtr->tagPtr);
1088 entryPtr = Tcl_FindHashEntry(&canvasPtr->idTable,
1089 (char *) itemPtr->id);
1090 Tcl_DeleteHashEntry(entryPtr);
1091 if (itemPtr->nextPtr != NULL) {
1092 itemPtr->nextPtr->prevPtr = itemPtr->prevPtr;
1094 if (itemPtr->prevPtr != NULL) {
1095 itemPtr->prevPtr->nextPtr = itemPtr->nextPtr;
1097 if (canvasPtr->firstItemPtr == itemPtr) {
1098 canvasPtr->firstItemPtr = itemPtr->nextPtr;
1099 if (canvasPtr->firstItemPtr == NULL) {
1100 canvasPtr->lastItemPtr = NULL;
1103 if (canvasPtr->lastItemPtr == itemPtr) {
1104 canvasPtr->lastItemPtr = itemPtr->prevPtr;
1106 ckfree((char *) itemPtr);
1107 if (itemPtr == canvasPtr->currentItemPtr) {
1108 canvasPtr->currentItemPtr = NULL;
1109 canvasPtr->flags |= REPICK_NEEDED;
1111 if (itemPtr == canvasPtr->newCurrentPtr) {
1112 canvasPtr->newCurrentPtr = NULL;
1113 canvasPtr->flags |= REPICK_NEEDED;
1115 if (itemPtr == canvasPtr->textInfo.focusItemPtr) {
1116 canvasPtr->textInfo.focusItemPtr = NULL;
1118 if (itemPtr == canvasPtr->textInfo.selItemPtr) {
1119 canvasPtr->textInfo.selItemPtr = NULL;
1121 if ((itemPtr == canvasPtr->hotPtr)
1122 || (itemPtr == canvasPtr->hotPrevPtr)) {
1123 canvasPtr->hotPtr = NULL;
1133 if ((argc != 3) && (argc != 4)) {
1134 Tcl_WrongNumArgs(interp, 2, argv, "tagOrId ?tagToDelete?");
1139 tag = Tk_GetUid(Tcl_GetStringFromObj(argv[3], NULL));
1141 tag = Tk_GetUid(Tcl_GetStringFromObj(argv[2], NULL));
1143 #ifdef USE_OLD_TAG_SEARCH
1144 for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
1145 itemPtr != NULL; itemPtr = NextItem(&search)) {
1146 #else /* USE_OLD_TAG_SEARCH */
1147 if ((result = TagSearchScan(canvasPtr, argv[2], &searchPtr)) != TCL_OK) {
1150 for (itemPtr = TagSearchFirst(searchPtr);
1151 itemPtr != NULL; itemPtr = TagSearchNext(searchPtr)) {
1152 #endif /* USE_OLD_TAG_SEARCH */
1153 for (i = itemPtr->numTags-1; i >= 0; i--) {
1154 if (itemPtr->tagPtr[i] == tag) {
1155 itemPtr->tagPtr[i] = itemPtr->tagPtr[itemPtr->numTags-1];
1164 Tcl_WrongNumArgs(interp, 2, argv, "searchCommand ?arg arg ...?");
1168 #ifdef USE_OLD_TAG_SEARCH
1169 result = FindItems(interp, canvasPtr, argc, argv, (Tcl_Obj *) NULL, 2);
1170 #else /* USE_OLD_TAG_SEARCH */
1171 result = FindItems(interp, canvasPtr, argc, argv,
1172 (Tcl_Obj *) NULL, 2, &searchPtr);
1173 #endif /* USE_OLD_TAG_SEARCH */
1178 Tcl_WrongNumArgs(interp, 2, argv, "?tagOrId?");
1182 itemPtr = canvasPtr->textInfo.focusItemPtr;
1184 if (itemPtr != NULL) {
1185 char buf[TCL_INTEGER_SPACE];
1187 sprintf(buf, "%d", itemPtr->id);
1188 Tcl_SetResult(interp, buf, TCL_VOLATILE);
1192 if ((itemPtr != NULL) && (canvasPtr->textInfo.gotFocus)) {
1193 EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
1195 if (Tcl_GetStringFromObj(argv[2], NULL)[0] == 0) {
1196 canvasPtr->textInfo.focusItemPtr = NULL;
1199 #ifdef USE_OLD_TAG_SEARCH
1200 for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
1201 itemPtr != NULL; itemPtr = NextItem(&search)) {
1202 #else /* USE_OLD_TAG_SEARCH */
1203 if ((result = TagSearchScan(canvasPtr, argv[2], &searchPtr)) != TCL_OK) {
1206 for (itemPtr = TagSearchFirst(searchPtr);
1207 itemPtr != NULL; itemPtr = TagSearchNext(searchPtr)) {
1208 #endif /* USE_OLD_TAG_SEARCH */
1209 if (itemPtr->typePtr->icursorProc != NULL) {
1213 if (itemPtr == NULL) {
1216 canvasPtr->textInfo.focusItemPtr = itemPtr;
1217 if (canvasPtr->textInfo.gotFocus) {
1218 EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
1222 case CANV_GETTAGS: {
1224 Tcl_WrongNumArgs(interp, 2, argv, "tagOrId");
1228 #ifdef USE_OLD_TAG_SEARCH
1229 itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
1230 #else /* USE_OLD_TAG_SEARCH */
1231 if ((result = TagSearchScan(canvasPtr, argv[2], &searchPtr)) != TCL_OK) {
1234 itemPtr = TagSearchFirst(searchPtr);
1235 #endif /* USE_OLD_TAG_SEARCH */
1236 if (itemPtr != NULL) {
1238 for (i = 0; i < itemPtr->numTags; i++) {
1239 Tcl_AppendElement(interp, (char *) itemPtr->tagPtr[i]);
1244 case CANV_ICURSOR: {
1248 Tcl_WrongNumArgs(interp, 2, argv, "tagOrId index");
1252 #ifdef USE_OLD_TAG_SEARCH
1253 for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
1254 itemPtr != NULL; itemPtr = NextItem(&search)) {
1255 #else /* USE_OLD_TAG_SEARCH */
1256 if ((result = TagSearchScan(canvasPtr, argv[2], &searchPtr)) != TCL_OK) {
1259 for (itemPtr = TagSearchFirst(searchPtr);
1260 itemPtr != NULL; itemPtr = TagSearchNext(searchPtr)) {
1261 #endif /* USE_OLD_TAG_SEARCH */
1262 if ((itemPtr->typePtr->indexProc == NULL)
1263 || (itemPtr->typePtr->icursorProc == NULL)) {
1266 if (itemPtr->typePtr->alwaysRedraw & TK_CONFIG_OBJS) {
1267 result = itemPtr->typePtr->indexProc(interp, (Tk_Canvas) canvasPtr,
1268 itemPtr, (char *) argv[3], &index);
1270 result = itemPtr->typePtr->indexProc(interp, (Tk_Canvas) canvasPtr,
1271 itemPtr, Tcl_GetStringFromObj(argv[3], NULL), &index);
1273 if (result != TCL_OK) {
1276 (*itemPtr->typePtr->icursorProc)((Tk_Canvas) canvasPtr, itemPtr,
1278 if ((itemPtr == canvasPtr->textInfo.focusItemPtr)
1279 && (canvasPtr->textInfo.cursorOn)) {
1280 EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
1288 char buf[TCL_INTEGER_SPACE];
1291 Tcl_WrongNumArgs(interp, 2, argv, "tagOrId string");
1295 #ifdef USE_OLD_TAG_SEARCH
1296 for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
1297 itemPtr != NULL; itemPtr = NextItem(&search)) {
1298 #else /* USE_OLD_TAG_SEARCH */
1299 if ((result = TagSearchScan(canvasPtr, argv[2], &searchPtr)) != TCL_OK) {
1302 for (itemPtr = TagSearchFirst(searchPtr);
1303 itemPtr != NULL; itemPtr = TagSearchNext(searchPtr)) {
1304 #endif /* USE_OLD_TAG_SEARCH */
1305 if (itemPtr->typePtr->indexProc != NULL) {
1309 if (itemPtr == NULL) {
1310 Tcl_AppendResult(interp, "can't find an indexable item \"",
1311 Tcl_GetStringFromObj(argv[2], NULL), "\"", (char *) NULL);
1315 if (itemPtr->typePtr->alwaysRedraw & TK_CONFIG_OBJS) {
1316 result = itemPtr->typePtr->indexProc(interp, (Tk_Canvas) canvasPtr,
1317 itemPtr, (char *) argv[3], &index);
1319 result = itemPtr->typePtr->indexProc(interp, (Tk_Canvas) canvasPtr,
1320 itemPtr, Tcl_GetStringFromObj(argv[3], NULL), &index);
1322 if (result != TCL_OK) {
1325 sprintf(buf, "%d", index);
1326 Tcl_SetResult(interp, buf, TCL_VOLATILE);
1334 Tcl_WrongNumArgs(interp, 2, argv, "tagOrId beforeThis string");
1338 #ifdef USE_OLD_TAG_SEARCH
1339 for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
1340 itemPtr != NULL; itemPtr = NextItem(&search)) {
1341 #else /* USE_OLD_TAG_SEARCH */
1342 if ((result = TagSearchScan(canvasPtr, argv[2], &searchPtr)) != TCL_OK) {
1345 for (itemPtr = TagSearchFirst(searchPtr);
1346 itemPtr != NULL; itemPtr = TagSearchNext(searchPtr)) {
1347 #endif /* USE_OLD_TAG_SEARCH */
1348 if ((itemPtr->typePtr->indexProc == NULL)
1349 || (itemPtr->typePtr->insertProc == NULL)) {
1352 if (itemPtr->typePtr->alwaysRedraw & TK_CONFIG_OBJS) {
1353 result = itemPtr->typePtr->indexProc(interp, (Tk_Canvas) canvasPtr,
1354 itemPtr, (char *) argv[3], &beforeThis);
1356 result = itemPtr->typePtr->indexProc(interp, (Tk_Canvas) canvasPtr,
1357 itemPtr, Tcl_GetStringFromObj(argv[3], NULL), &beforeThis);
1359 if (result != TCL_OK) {
1364 * Redraw both item's old and new areas: it's possible
1365 * that an insertion could result in a new area either
1366 * larger or smaller than the old area. Except if the
1367 * insertProc sets the TK_ITEM_DONT_REDRAW flag, nothing
1368 * more needs to be done.
1371 x1 = itemPtr->x1; y1 = itemPtr->y1;
1372 x2 = itemPtr->x2; y2 = itemPtr->y2;
1373 itemPtr->redraw_flags &= ~TK_ITEM_DONT_REDRAW;
1374 if (itemPtr->typePtr->alwaysRedraw & TK_CONFIG_OBJS) {
1375 (*itemPtr->typePtr->insertProc)((Tk_Canvas) canvasPtr,
1376 itemPtr, beforeThis, (char *) argv[4]);
1378 (*itemPtr->typePtr->insertProc)((Tk_Canvas) canvasPtr,
1379 itemPtr, beforeThis, Tcl_GetStringFromObj(argv[4], NULL));
1381 if (!(itemPtr->redraw_flags & TK_ITEM_DONT_REDRAW)) {
1382 Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
1384 EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
1386 itemPtr->redraw_flags &= ~TK_ITEM_DONT_REDRAW;
1390 case CANV_ITEMCGET: {
1392 Tcl_WrongNumArgs(interp, 2, argv, "tagOrId option");
1395 #ifdef USE_OLD_TAG_SEARCH
1396 itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
1397 #else /* USE_OLD_TAG_SEARCH */
1398 if ((result = TagSearchScan(canvasPtr, argv[2], &searchPtr)) != TCL_OK) {
1401 itemPtr = TagSearchFirst(searchPtr);
1402 #endif /* USE_OLD_TAG_SEARCH */
1403 if (itemPtr != NULL) {
1404 result = Tk_ConfigureValue(canvasPtr->interp, canvasPtr->tkwin,
1405 itemPtr->typePtr->configSpecs, (char *) itemPtr,
1406 Tcl_GetStringFromObj(argv[3], NULL), 0);
1410 case CANV_ITEMCONFIGURE: {
1412 Tcl_WrongNumArgs(interp, 2, argv, "tagOrId ?option value ...?");
1416 #ifdef USE_OLD_TAG_SEARCH
1417 for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
1418 itemPtr != NULL; itemPtr = NextItem(&search)) {
1419 #else /* USE_OLD_TAG_SEARCH */
1420 if ((result = TagSearchScan(canvasPtr, argv[2], &searchPtr)) != TCL_OK) {
1423 for (itemPtr = TagSearchFirst(searchPtr);
1424 itemPtr != NULL; itemPtr = TagSearchNext(searchPtr)) {
1425 #endif /* USE_OLD_TAG_SEARCH */
1427 result = Tk_ConfigureInfo(canvasPtr->interp, canvasPtr->tkwin,
1428 itemPtr->typePtr->configSpecs, (char *) itemPtr,
1430 } else if (argc == 4) {
1431 result = Tk_ConfigureInfo(canvasPtr->interp, canvasPtr->tkwin,
1432 itemPtr->typePtr->configSpecs, (char *) itemPtr,
1433 Tcl_GetString(argv[3]), 0);
1435 EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
1436 if (itemPtr->typePtr->alwaysRedraw & TK_CONFIG_OBJS) {
1437 result = (*itemPtr->typePtr->configProc)(interp,
1438 (Tk_Canvas) canvasPtr, itemPtr, argc-3, argv+3,
1439 TK_CONFIG_ARGV_ONLY);
1441 char **args = GetStringsFromObjs(argc-3, argv+3);
1442 result = (*itemPtr->typePtr->configProc)(interp,
1443 (Tk_Canvas) canvasPtr, itemPtr, argc-3, (Tcl_Obj **) args,
1444 TK_CONFIG_ARGV_ONLY);
1445 if (args) ckfree((char *) args);
1447 EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
1448 canvasPtr->flags |= REPICK_NEEDED;
1450 if ((result != TCL_OK) || (argc < 5)) {
1459 if ((argc != 3) && (argc != 4)) {
1460 Tcl_WrongNumArgs(interp, 2, argv, "tagOrId ?belowThis?");
1466 * First find the item just after which we'll insert the
1473 #ifdef USE_OLD_TAG_SEARCH
1474 itemPtr = StartTagSearch(canvasPtr, argv[3], &search);
1475 #else /* USE_OLD_TAG_SEARCH */
1476 if ((result = TagSearchScan(canvasPtr, argv[3], &searchPtr)) != TCL_OK) {
1479 itemPtr = TagSearchFirst(searchPtr);
1480 #endif /* USE_OLD_TAG_SEARCH */
1481 if (itemPtr == NULL) {
1482 Tcl_AppendResult(interp, "tag \"", Tcl_GetString(argv[3]),
1483 "\" doesn't match any items", (char *) NULL);
1486 itemPtr = itemPtr->prevPtr;
1488 #ifdef USE_OLD_TAG_SEARCH
1489 RelinkItems(canvasPtr, argv[2], itemPtr);
1490 #else /* USE_OLD_TAG_SEARCH */
1491 if ((result = RelinkItems(canvasPtr, argv[2], itemPtr, &searchPtr)) != TCL_OK) {
1494 #endif /* USE_OLD_TAG_SEARCH */
1498 double xAmount, yAmount;
1501 Tcl_WrongNumArgs(interp, 2, argv, "tagOrId xAmount yAmount");
1505 if ((Tk_CanvasGetCoordFromObj(interp, (Tk_Canvas) canvasPtr, argv[3],
1506 &xAmount) != TCL_OK) || (Tk_CanvasGetCoordFromObj(interp,
1507 (Tk_Canvas) canvasPtr, argv[4], &yAmount) != TCL_OK)) {
1511 #ifdef USE_OLD_TAG_SEARCH
1512 for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
1513 itemPtr != NULL; itemPtr = NextItem(&search)) {
1514 #else /* USE_OLD_TAG_SEARCH */
1515 if ((result = TagSearchScan(canvasPtr, argv[2], &searchPtr)) != TCL_OK) {
1518 for (itemPtr = TagSearchFirst(searchPtr);
1519 itemPtr != NULL; itemPtr = TagSearchNext(searchPtr)) {
1520 #endif /* USE_OLD_TAG_SEARCH */
1521 EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
1522 (void) (*itemPtr->typePtr->translateProc)((Tk_Canvas) canvasPtr,
1523 itemPtr, xAmount, yAmount);
1524 EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
1525 canvasPtr->flags |= REPICK_NEEDED;
1529 case CANV_POSTSCRIPT: {
1530 char **args = GetStringsFromObjs(argc, argv);
1531 result = TkCanvPostscriptCmd(canvasPtr, interp, argc, args);
1532 if (args) ckfree((char *) args);
1538 if ((argc != 3) && (argc != 4)) {
1539 Tcl_WrongNumArgs(interp, 2, argv, "tagOrId ?aboveThis?");
1545 * First find the item just after which we'll insert the
1550 prevPtr = canvasPtr->lastItemPtr;
1553 #ifdef USE_OLD_TAG_SEARCH
1554 for (itemPtr = StartTagSearch(canvasPtr, argv[3], &search);
1555 itemPtr != NULL; itemPtr = NextItem(&search)) {
1556 #else /* USE_OLD_TAG_SEARCH */
1557 if ((result = TagSearchScan(canvasPtr, argv[3], &searchPtr)) != TCL_OK) {
1560 for (itemPtr = TagSearchFirst(searchPtr);
1561 itemPtr != NULL; itemPtr = TagSearchNext(searchPtr)) {
1562 #endif /* USE_OLD_TAG_SEARCH */
1565 if (prevPtr == NULL) {
1566 Tcl_AppendResult(interp, "tagOrId \"", Tcl_GetStringFromObj(argv[3], NULL),
1567 "\" doesn't match any items", (char *) NULL);
1572 #ifdef USE_OLD_TAG_SEARCH
1573 RelinkItems(canvasPtr, argv[2], prevPtr);
1574 #else /* USE_OLD_TAG_SEARCH */
1575 result = RelinkItems(canvasPtr, argv[2], prevPtr, &searchPtr);
1576 if (result != TCL_OK) {
1579 #endif /* USE_OLD_TAG_SEARCH */
1583 double xOrigin, yOrigin, xScale, yScale;
1586 Tcl_WrongNumArgs(interp, 2, argv, "tagOrId xOrigin yOrigin xScale yScale");
1590 if ((Tk_CanvasGetCoordFromObj(interp, (Tk_Canvas) canvasPtr,
1591 argv[3], &xOrigin) != TCL_OK)
1592 || (Tk_CanvasGetCoordFromObj(interp, (Tk_Canvas) canvasPtr,
1593 argv[4], &yOrigin) != TCL_OK)
1594 || (Tcl_GetDoubleFromObj(interp, argv[5], &xScale) != TCL_OK)
1595 || (Tcl_GetDoubleFromObj(interp, argv[6], &yScale) != TCL_OK)) {
1599 if ((xScale == 0.0) || (yScale == 0.0)) {
1600 Tcl_SetResult(interp, "scale factor cannot be zero", TCL_STATIC);
1604 #ifdef USE_OLD_TAG_SEARCH
1605 for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
1606 itemPtr != NULL; itemPtr = NextItem(&search)) {
1607 #else /* USE_OLD_TAG_SEARCH */
1608 if ((result = TagSearchScan(canvasPtr, argv[2], &searchPtr)) != TCL_OK) {
1611 for (itemPtr = TagSearchFirst(searchPtr);
1612 itemPtr != NULL; itemPtr = TagSearchNext(searchPtr)) {
1613 #endif /* USE_OLD_TAG_SEARCH */
1614 EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
1615 (void) (*itemPtr->typePtr->scaleProc)((Tk_Canvas) canvasPtr,
1616 itemPtr, xOrigin, yOrigin, xScale, yScale);
1617 EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
1618 canvasPtr->flags |= REPICK_NEEDED;
1624 static char *optionStrings[] = {
1625 "mark", "dragto", NULL
1628 if (Tcl_GetIndexFromObj(interp, argv[2], optionStrings, "scan option", 0,
1629 &index) != TCL_OK) {
1633 if ((argc != 5) && (argc != 5+index)) {
1634 Tcl_WrongNumArgs(interp, 3, argv, index?"x y ?gain?":"x y");
1638 if ((Tcl_GetIntFromObj(interp, argv[3], &x) != TCL_OK)
1639 || (Tcl_GetIntFromObj(interp, argv[4], &y) != TCL_OK)){
1643 if ((argc == 6) && (Tcl_GetIntFromObj(interp, argv[5], &gain) != TCL_OK)) {
1648 canvasPtr->scanX = x;
1649 canvasPtr->scanXOrigin = canvasPtr->xOrigin;
1650 canvasPtr->scanY = y;
1651 canvasPtr->scanYOrigin = canvasPtr->yOrigin;
1653 int newXOrigin, newYOrigin, tmp;
1656 * Compute a new view origin for the canvas, amplifying the
1660 tmp = canvasPtr->scanXOrigin - gain*(x - canvasPtr->scanX)
1661 - canvasPtr->scrollX1;
1662 newXOrigin = canvasPtr->scrollX1 + tmp;
1663 tmp = canvasPtr->scanYOrigin - gain*(y - canvasPtr->scanY)
1664 - canvasPtr->scrollY1;
1665 newYOrigin = canvasPtr->scrollY1 + tmp;
1666 CanvasSetOrigin(canvasPtr, newXOrigin, newYOrigin);
1671 int index, optionindex;
1672 static char *optionStrings[] = {
1673 "adjust", "clear", "from", "item", "to", NULL
1676 CANV_ADJUST, CANV_CLEAR, CANV_FROM, CANV_ITEM, CANV_TO
1680 Tcl_WrongNumArgs(interp, 2, argv, "option ?tagOrId? ?arg?");
1685 #ifdef USE_OLD_TAG_SEARCH
1686 for (itemPtr = StartTagSearch(canvasPtr, argv[3], &search);
1687 itemPtr != NULL; itemPtr = NextItem(&search)) {
1688 #else /* USE_OLD_TAG_SEARCH */
1689 if ((result = TagSearchScan(canvasPtr, argv[3], &searchPtr)) != TCL_OK) {
1692 for (itemPtr = TagSearchFirst(searchPtr);
1693 itemPtr != NULL; itemPtr = TagSearchNext(searchPtr)) {
1694 #endif /* USE_OLD_TAG_SEARCH */
1695 if ((itemPtr->typePtr->indexProc != NULL)
1696 && (itemPtr->typePtr->selectionProc != NULL)){
1700 if (itemPtr == NULL) {
1701 Tcl_AppendResult(interp,
1702 "can't find an indexable and selectable item \"",
1703 Tcl_GetStringFromObj(argv[3], NULL), "\"", (char *) NULL);
1709 if (itemPtr->typePtr->alwaysRedraw & TK_CONFIG_OBJS) {
1710 result = itemPtr->typePtr->indexProc(interp, (Tk_Canvas) canvasPtr,
1711 itemPtr, (char *) argv[4], &index);
1713 result = itemPtr->typePtr->indexProc(interp, (Tk_Canvas) canvasPtr,
1714 itemPtr, Tcl_GetStringFromObj(argv[4], NULL), &index);
1716 if (result != TCL_OK) {
1720 if (Tcl_GetIndexFromObj(interp, argv[2], optionStrings, "select option", 0,
1721 &optionindex) != TCL_OK) {
1724 switch ((enum options) optionindex) {
1727 Tcl_WrongNumArgs(interp, 3, argv, "tagOrId index");
1731 if (canvasPtr->textInfo.selItemPtr == itemPtr) {
1732 if (index < (canvasPtr->textInfo.selectFirst
1733 + canvasPtr->textInfo.selectLast)/2) {
1734 canvasPtr->textInfo.selectAnchor =
1735 canvasPtr->textInfo.selectLast + 1;
1737 canvasPtr->textInfo.selectAnchor =
1738 canvasPtr->textInfo.selectFirst;
1741 CanvasSelectTo(canvasPtr, itemPtr, index);
1746 Tcl_AppendResult(interp, 3, argv, (char *) NULL);
1750 if (canvasPtr->textInfo.selItemPtr != NULL) {
1751 EventuallyRedrawItem((Tk_Canvas) canvasPtr,
1752 canvasPtr->textInfo.selItemPtr);
1753 canvasPtr->textInfo.selItemPtr = NULL;
1760 Tcl_WrongNumArgs(interp, 3, argv, "tagOrId index");
1764 canvasPtr->textInfo.anchorItemPtr = itemPtr;
1765 canvasPtr->textInfo.selectAnchor = index;
1770 Tcl_WrongNumArgs(interp, 3, argv, (char *) NULL);
1774 if (canvasPtr->textInfo.selItemPtr != NULL) {
1775 char buf[TCL_INTEGER_SPACE];
1777 sprintf(buf, "%d", canvasPtr->textInfo.selItemPtr->id);
1778 Tcl_SetResult(interp, buf, TCL_VOLATILE);
1784 Tcl_WrongNumArgs(interp, 2, argv, "tagOrId index");
1788 CanvasSelectTo(canvasPtr, itemPtr, index);
1796 Tcl_WrongNumArgs(interp, 2, argv, "tag");
1800 #ifdef USE_OLD_TAG_SEARCH
1801 itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
1802 #else /* USE_OLD_TAG_SEARCH */
1803 if ((result = TagSearchScan(canvasPtr, argv[2], &searchPtr)) != TCL_OK) {
1806 itemPtr = TagSearchFirst(searchPtr);
1807 #endif /* USE_OLD_TAG_SEARCH */
1808 if (itemPtr != NULL) {
1809 Tcl_SetResult(interp, itemPtr->typePtr->name, TCL_STATIC);
1815 int newX = 0; /* Initialization needed only to prevent
1820 PrintScrollFractions(canvasPtr->xOrigin + canvasPtr->inset,
1821 canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin)
1822 - canvasPtr->inset, canvasPtr->scrollX1,
1823 canvasPtr->scrollX2, Tcl_GetStringResult(interp));
1825 char **args = GetStringsFromObjs(argc, argv);
1826 type = Tk_GetScrollInfo(interp, argc, args, &fraction, &count);
1827 if (args) ckfree((char *) args);
1829 case TK_SCROLL_ERROR:
1832 case TK_SCROLL_MOVETO:
1833 newX = canvasPtr->scrollX1 - canvasPtr->inset
1834 + (int) (fraction * (canvasPtr->scrollX2
1835 - canvasPtr->scrollX1) + 0.5);
1837 case TK_SCROLL_PAGES:
1838 newX = (int) (canvasPtr->xOrigin + count * .9
1839 * (Tk_Width(canvasPtr->tkwin) - 2*canvasPtr->inset));
1841 case TK_SCROLL_UNITS:
1842 if (canvasPtr->xScrollIncrement > 0) {
1843 newX = canvasPtr->xOrigin
1844 + count*canvasPtr->xScrollIncrement;
1846 newX = (int) (canvasPtr->xOrigin + count * .1
1847 * (Tk_Width(canvasPtr->tkwin)
1848 - 2*canvasPtr->inset));
1852 CanvasSetOrigin(canvasPtr, newX, canvasPtr->yOrigin);
1858 int newY = 0; /* Initialization needed only to prevent
1863 PrintScrollFractions(canvasPtr->yOrigin + canvasPtr->inset,
1864 canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin)
1865 - canvasPtr->inset, canvasPtr->scrollY1,
1866 canvasPtr->scrollY2, Tcl_GetStringResult(interp));
1868 char **args = GetStringsFromObjs(argc, argv);
1869 type = Tk_GetScrollInfo(interp, argc, args, &fraction, &count);
1870 if (args) ckfree((char *) args);
1872 case TK_SCROLL_ERROR:
1875 case TK_SCROLL_MOVETO:
1876 newY = canvasPtr->scrollY1 - canvasPtr->inset
1877 + (int) (fraction*(canvasPtr->scrollY2
1878 - canvasPtr->scrollY1) + 0.5);
1880 case TK_SCROLL_PAGES:
1881 newY = (int) (canvasPtr->yOrigin + count * .9
1882 * (Tk_Height(canvasPtr->tkwin)
1883 - 2*canvasPtr->inset));
1885 case TK_SCROLL_UNITS:
1886 if (canvasPtr->yScrollIncrement > 0) {
1887 newY = canvasPtr->yOrigin
1888 + count*canvasPtr->yScrollIncrement;
1890 newY = (int) (canvasPtr->yOrigin + count * .1
1891 * (Tk_Height(canvasPtr->tkwin)
1892 - 2*canvasPtr->inset));
1896 CanvasSetOrigin(canvasPtr, canvasPtr->xOrigin, newY);
1902 #ifndef USE_OLD_TAG_SEARCH
1903 TagSearchDestroy(searchPtr);
1904 #endif /* not USE_OLD_TAG_SEARCH */
1905 Tcl_Release((ClientData) canvasPtr);
1910 *----------------------------------------------------------------------
1914 * This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
1915 * to clean up the internal structure of a canvas at a safe time
1916 * (when no-one is using it anymore).
1922 * Everything associated with the canvas is freed up.
1924 *----------------------------------------------------------------------
1928 DestroyCanvas(memPtr)
1929 char *memPtr; /* Info about canvas widget. */
1931 TkCanvas *canvasPtr = (TkCanvas *) memPtr;
1934 if (canvasPtr->tkwin != NULL) {
1935 Tcl_DeleteCommandFromToken(canvasPtr->interp, canvasPtr->widgetCmd);
1937 if (canvasPtr->flags & REDRAW_PENDING) {
1938 Tcl_CancelIdleCall(DisplayCanvas, (ClientData) canvasPtr);
1942 * Free up all of the items in the canvas.
1945 for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
1946 itemPtr = canvasPtr->firstItemPtr) {
1947 canvasPtr->firstItemPtr = itemPtr->nextPtr;
1948 (*itemPtr->typePtr->deleteProc)((Tk_Canvas) canvasPtr, itemPtr,
1949 canvasPtr->display);
1950 if (itemPtr->tagPtr != itemPtr->staticTagSpace) {
1951 ckfree((char *) itemPtr->tagPtr);
1953 ckfree((char *) itemPtr);
1957 * Free up all the stuff that requires special handling,
1958 * then let Tk_FreeOptions handle all the standard option-related
1962 Tcl_DeleteHashTable(&canvasPtr->idTable);
1963 if (canvasPtr->pixmapGC != None) {
1964 Tk_FreeGC(canvasPtr->display, canvasPtr->pixmapGC);
1966 #ifndef USE_OLD_TAG_SEARCH
1968 TagSearchExpr *expr, *next;
1970 expr = canvasPtr->bindTagExprs;
1973 TagSearchExprDestroy(expr);
1978 Tcl_DeleteTimerHandler(canvasPtr->insertBlinkHandler);
1979 if (canvasPtr->bindingTable != NULL) {
1980 Tk_DeleteBindingTable(canvasPtr->bindingTable);
1982 Tk_FreeOptions(configSpecs, (char *) canvasPtr, canvasPtr->display, 0);
1983 canvasPtr->tkwin = NULL;
1984 ckfree((char *) canvasPtr);
1988 *----------------------------------------------------------------------
1990 * ConfigureCanvas --
1992 * This procedure is called to process an argv/argc list, plus
1993 * the Tk option database, in order to configure (or
1994 * reconfigure) a canvas widget.
1997 * The return value is a standard Tcl result. If TCL_ERROR is
1998 * returned, then the interp's result contains an error message.
2001 * Configuration information, such as colors, border width,
2002 * etc. get set for canvasPtr; old resources get freed,
2003 * if there were any.
2005 *----------------------------------------------------------------------
2009 ConfigureCanvas(interp, canvasPtr, argc, argv, flags)
2010 Tcl_Interp *interp; /* Used for error reporting. */
2011 TkCanvas *canvasPtr; /* Information about widget; may or may
2012 * not already have values for some fields. */
2013 int argc; /* Number of valid entries in argv. */
2014 Tcl_Obj *CONST argv[]; /* Argument objects. */
2015 int flags; /* Flags to pass to Tk_ConfigureWidget. */
2020 if (Tk_ConfigureWidget(interp, canvasPtr->tkwin, configSpecs,
2021 argc, (char **) argv, (char *) canvasPtr, flags|TK_CONFIG_OBJS) != TCL_OK) {
2026 * A few options need special processing, such as setting the
2027 * background from a 3-D border and creating a GC for copying
2028 * bits to the screen.
2031 Tk_SetBackgroundFromBorder(canvasPtr->tkwin, canvasPtr->bgBorder);
2033 if (canvasPtr->highlightWidth < 0) {
2034 canvasPtr->highlightWidth = 0;
2036 canvasPtr->inset = canvasPtr->borderWidth + canvasPtr->highlightWidth;
2038 gcValues.function = GXcopy;
2039 gcValues.graphics_exposures = False;
2040 gcValues.foreground = Tk_3DBorderColor(canvasPtr->bgBorder)->pixel;
2041 new = Tk_GetGC(canvasPtr->tkwin,
2042 GCFunction|GCGraphicsExposures|GCForeground, &gcValues);
2043 if (canvasPtr->pixmapGC != None) {
2044 Tk_FreeGC(canvasPtr->display, canvasPtr->pixmapGC);
2046 canvasPtr->pixmapGC = new;
2049 * Reset the desired dimensions for the window.
2052 Tk_GeometryRequest(canvasPtr->tkwin, canvasPtr->width + 2*canvasPtr->inset,
2053 canvasPtr->height + 2*canvasPtr->inset);
2056 * Restart the cursor timing sequence in case the on-time or off-time
2060 if (canvasPtr->textInfo.gotFocus) {
2061 CanvasFocusProc(canvasPtr, 1);
2065 * Recompute the scroll region.
2068 canvasPtr->scrollX1 = 0;
2069 canvasPtr->scrollY1 = 0;
2070 canvasPtr->scrollX2 = 0;
2071 canvasPtr->scrollY2 = 0;
2072 if (canvasPtr->regionString != NULL) {
2076 if (Tcl_SplitList(canvasPtr->interp, canvasPtr->regionString,
2077 &argc2, &argv2) != TCL_OK) {
2081 Tcl_AppendResult(interp, "bad scrollRegion \"",
2082 canvasPtr->regionString, "\"", (char *) NULL);
2084 ckfree(canvasPtr->regionString);
2085 ckfree((char *) argv2);
2086 canvasPtr->regionString = NULL;
2089 if ((Tk_GetPixels(canvasPtr->interp, canvasPtr->tkwin,
2090 argv2[0], &canvasPtr->scrollX1) != TCL_OK)
2091 || (Tk_GetPixels(canvasPtr->interp, canvasPtr->tkwin,
2092 argv2[1], &canvasPtr->scrollY1) != TCL_OK)
2093 || (Tk_GetPixels(canvasPtr->interp, canvasPtr->tkwin,
2094 argv2[2], &canvasPtr->scrollX2) != TCL_OK)
2095 || (Tk_GetPixels(canvasPtr->interp, canvasPtr->tkwin,
2096 argv2[3], &canvasPtr->scrollY2) != TCL_OK)) {
2099 ckfree((char *) argv2);
2102 flags = canvasPtr->tsoffset.flags;
2103 if (flags & TK_OFFSET_LEFT) {
2104 canvasPtr->tsoffset.xoffset = 0;
2105 } else if (flags & TK_OFFSET_CENTER) {
2106 canvasPtr->tsoffset.xoffset = canvasPtr->width/2;
2107 } else if (flags & TK_OFFSET_RIGHT) {
2108 canvasPtr->tsoffset.xoffset = canvasPtr->width;
2110 if (flags & TK_OFFSET_TOP) {
2111 canvasPtr->tsoffset.yoffset = 0;
2112 } else if (flags & TK_OFFSET_MIDDLE) {
2113 canvasPtr->tsoffset.yoffset = canvasPtr->height/2;
2114 } else if (flags & TK_OFFSET_BOTTOM) {
2115 canvasPtr->tsoffset.yoffset = canvasPtr->height;
2119 * Reset the canvas's origin (this is a no-op unless confine
2120 * mode has just been turned on or the scroll region has changed).
2123 CanvasSetOrigin(canvasPtr, canvasPtr->xOrigin, canvasPtr->yOrigin);
2124 canvasPtr->flags |= UPDATE_SCROLLBARS|REDRAW_BORDERS;
2125 Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
2126 canvasPtr->xOrigin, canvasPtr->yOrigin,
2127 canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin),
2128 canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin));
2133 *---------------------------------------------------------------------------
2135 * CanvasWorldChanged --
2137 * This procedure is called when the world has changed in some
2138 * way and the widget needs to recompute all its graphics contexts
2139 * and determine its new geometry.
2145 * Configures all items in the canvas with a empty argc/argv, for
2146 * the side effect of causing all the items to recompute their
2147 * geometry and to be redisplayed.
2149 *---------------------------------------------------------------------------
2153 CanvasWorldChanged(instanceData)
2154 ClientData instanceData; /* Information about widget. */
2156 TkCanvas *canvasPtr;
2160 canvasPtr = (TkCanvas *) instanceData;
2161 itemPtr = canvasPtr->firstItemPtr;
2162 for ( ; itemPtr != NULL; itemPtr = itemPtr->nextPtr) {
2163 result = (*itemPtr->typePtr->configProc)(canvasPtr->interp,
2164 (Tk_Canvas) canvasPtr, itemPtr, 0, NULL,
2165 TK_CONFIG_ARGV_ONLY);
2166 if (result != TCL_OK) {
2167 Tcl_ResetResult(canvasPtr->interp);
2170 canvasPtr->flags |= REPICK_NEEDED;
2171 Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
2172 canvasPtr->xOrigin, canvasPtr->yOrigin,
2173 canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin),
2174 canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin));
2178 *--------------------------------------------------------------
2182 * This procedure redraws the contents of a canvas window.
2183 * It is invoked as a do-when-idle handler, so it only runs
2184 * when there's nothing else for the application to do.
2190 * Information appears on the screen.
2192 *--------------------------------------------------------------
2196 DisplayCanvas(clientData)
2197 ClientData clientData; /* Information about widget. */
2199 TkCanvas *canvasPtr = (TkCanvas *) clientData;
2200 Tk_Window tkwin = canvasPtr->tkwin;
2203 int screenX1, screenX2, screenY1, screenY2, width, height;
2205 if (canvasPtr->tkwin == NULL) {
2209 if (!Tk_IsMapped(tkwin)) {
2214 * Choose a new current item if that is needed (this could cause
2215 * event handlers to be invoked).
2218 while (canvasPtr->flags & REPICK_NEEDED) {
2219 Tcl_Preserve((ClientData) canvasPtr);
2220 canvasPtr->flags &= ~REPICK_NEEDED;
2221 PickCurrentItem(canvasPtr, &canvasPtr->pickEvent);
2222 tkwin = canvasPtr->tkwin;
2223 Tcl_Release((ClientData) canvasPtr);
2224 if (tkwin == NULL) {
2230 * Scan through the item list, registering the bounding box
2231 * for all items that didn't do that for the final coordinates
2232 * yet. This can be determined by the FORCE_REDRAW flag.
2235 for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
2236 itemPtr = itemPtr->nextPtr) {
2237 if (itemPtr->redraw_flags & FORCE_REDRAW) {
2238 itemPtr->redraw_flags &= ~FORCE_REDRAW;
2239 EventuallyRedrawItem((Tk_Canvas)canvasPtr, itemPtr);
2240 itemPtr->redraw_flags &= ~FORCE_REDRAW;
2244 * Compute the intersection between the area that needs redrawing
2245 * and the area that's visible on the screen.
2248 if ((canvasPtr->redrawX1 < canvasPtr->redrawX2)
2249 && (canvasPtr->redrawY1 < canvasPtr->redrawY2)) {
2250 screenX1 = canvasPtr->xOrigin + canvasPtr->inset;
2251 screenY1 = canvasPtr->yOrigin + canvasPtr->inset;
2252 screenX2 = canvasPtr->xOrigin + Tk_Width(tkwin) - canvasPtr->inset;
2253 screenY2 = canvasPtr->yOrigin + Tk_Height(tkwin) - canvasPtr->inset;
2254 if (canvasPtr->redrawX1 > screenX1) {
2255 screenX1 = canvasPtr->redrawX1;
2257 if (canvasPtr->redrawY1 > screenY1) {
2258 screenY1 = canvasPtr->redrawY1;
2260 if (canvasPtr->redrawX2 < screenX2) {
2261 screenX2 = canvasPtr->redrawX2;
2263 if (canvasPtr->redrawY2 < screenY2) {
2264 screenY2 = canvasPtr->redrawY2;
2266 if ((screenX1 >= screenX2) || (screenY1 >= screenY2)) {
2271 * Redrawing is done in a temporary pixmap that is allocated
2272 * here and freed at the end of the procedure. All drawing
2273 * is done to the pixmap, and the pixmap is copied to the
2274 * screen at the end of the procedure. The temporary pixmap
2275 * serves two purposes:
2277 * 1. It provides a smoother visual effect (no clearing and
2278 * gradual redraw will be visible to users).
2279 * 2. It allows us to redraw only the objects that overlap
2280 * the redraw area. Otherwise incorrect results could
2281 * occur from redrawing things that stick outside of
2282 * the redraw area (we'd have to redraw everything in
2283 * order to make the overlaps look right).
2285 * Some tricky points about the pixmap:
2287 * 1. We only allocate a large enough pixmap to hold the
2288 * area that has to be redisplayed. This saves time in
2289 * in the X server for large objects that cover much
2290 * more than the area being redisplayed: only the area
2291 * of the pixmap will actually have to be redrawn.
2292 * 2. Some X servers (e.g. the one for DECstations) have troubles
2293 * with characters that overlap an edge of the pixmap (on the
2294 * DEC servers, as of 8/18/92, such characters are drawn one
2295 * pixel too far to the right). To handle this problem,
2296 * make the pixmap a bit larger than is absolutely needed
2297 * so that for normal-sized fonts the characters that overlap
2298 * the edge of the pixmap will be outside the area we care
2302 canvasPtr->drawableXOrigin = screenX1 - 30;
2303 canvasPtr->drawableYOrigin = screenY1 - 30;
2304 pixmap = Tk_GetPixmap(Tk_Display(tkwin), Tk_WindowId(tkwin),
2305 (screenX2 + 30 - canvasPtr->drawableXOrigin),
2306 (screenY2 + 30 - canvasPtr->drawableYOrigin),
2310 * Clear the area to be redrawn.
2313 width = screenX2 - screenX1;
2314 height = screenY2 - screenY1;
2316 XFillRectangle(Tk_Display(tkwin), pixmap, canvasPtr->pixmapGC,
2317 screenX1 - canvasPtr->drawableXOrigin,
2318 screenY1 - canvasPtr->drawableYOrigin, (unsigned int) width,
2319 (unsigned int) height);
2322 * Scan through the item list, redrawing those items that need it.
2323 * An item must be redraw if either (a) it intersects the smaller
2324 * on-screen area or (b) it intersects the full canvas area and its
2325 * type requests that it be redrawn always (e.g. so subwindows can
2326 * be unmapped when they move off-screen).
2329 for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
2330 itemPtr = itemPtr->nextPtr) {
2331 if ((itemPtr->x1 >= screenX2)
2332 || (itemPtr->y1 >= screenY2)
2333 || (itemPtr->x2 < screenX1)
2334 || (itemPtr->y2 < screenY1)) {
2335 if (!(itemPtr->typePtr->alwaysRedraw & 1)
2336 || (itemPtr->x1 >= canvasPtr->redrawX2)
2337 || (itemPtr->y1 >= canvasPtr->redrawY2)
2338 || (itemPtr->x2 < canvasPtr->redrawX1)
2339 || (itemPtr->y2 < canvasPtr->redrawY1)) {
2343 if (itemPtr->state == TK_STATE_HIDDEN ||
2344 (itemPtr->state == TK_STATE_NULL &&
2345 canvasPtr->canvas_state == TK_STATE_HIDDEN)) {
2348 (*itemPtr->typePtr->displayProc)((Tk_Canvas) canvasPtr, itemPtr,
2349 canvasPtr->display, pixmap, screenX1, screenY1, width,
2354 * Copy from the temporary pixmap to the screen, then free up
2355 * the temporary pixmap.
2358 XCopyArea(Tk_Display(tkwin), pixmap, Tk_WindowId(tkwin),
2359 canvasPtr->pixmapGC,
2360 screenX1 - canvasPtr->drawableXOrigin,
2361 screenY1 - canvasPtr->drawableYOrigin,
2362 (unsigned) (screenX2 - screenX1),
2363 (unsigned) (screenY2 - screenY1),
2364 screenX1 - canvasPtr->xOrigin, screenY1 - canvasPtr->yOrigin);
2365 Tk_FreePixmap(Tk_Display(tkwin), pixmap);
2369 * Draw the window borders, if needed.
2373 if (canvasPtr->flags & REDRAW_BORDERS) {
2374 canvasPtr->flags &= ~REDRAW_BORDERS;
2375 if (canvasPtr->borderWidth > 0) {
2376 Tk_Draw3DRectangle(tkwin, Tk_WindowId(tkwin),
2377 canvasPtr->bgBorder, canvasPtr->highlightWidth,
2378 canvasPtr->highlightWidth,
2379 Tk_Width(tkwin) - 2*canvasPtr->highlightWidth,
2380 Tk_Height(tkwin) - 2*canvasPtr->highlightWidth,
2381 canvasPtr->borderWidth, canvasPtr->relief);
2383 if (canvasPtr->highlightWidth != 0) {
2386 bgGC = Tk_GCForColor(canvasPtr->highlightBgColorPtr,
2387 Tk_WindowId(tkwin));
2388 if (canvasPtr->textInfo.gotFocus) {
2389 fgGC = Tk_GCForColor(canvasPtr->highlightColorPtr,
2390 Tk_WindowId(tkwin));
2391 TkpDrawHighlightBorder(tkwin, fgGC, bgGC,
2392 canvasPtr->highlightWidth, Tk_WindowId(tkwin));
2394 TkpDrawHighlightBorder(tkwin, bgGC, bgGC,
2395 canvasPtr->highlightWidth, Tk_WindowId(tkwin));
2401 canvasPtr->flags &= ~(REDRAW_PENDING|BBOX_NOT_EMPTY);
2402 canvasPtr->redrawX1 = canvasPtr->redrawX2 = 0;
2403 canvasPtr->redrawY1 = canvasPtr->redrawY2 = 0;
2404 if (canvasPtr->flags & UPDATE_SCROLLBARS) {
2405 CanvasUpdateScrollbars(canvasPtr);
2410 *--------------------------------------------------------------
2412 * CanvasEventProc --
2414 * This procedure is invoked by the Tk dispatcher for various
2415 * events on canvases.
2421 * When the window gets deleted, internal structures get
2422 * cleaned up. When it gets exposed, it is redisplayed.
2424 *--------------------------------------------------------------
2428 CanvasEventProc(clientData, eventPtr)
2429 ClientData clientData; /* Information about window. */
2430 XEvent *eventPtr; /* Information about event. */
2432 TkCanvas *canvasPtr = (TkCanvas *) clientData;
2434 if (eventPtr->type == Expose) {
2437 x = eventPtr->xexpose.x + canvasPtr->xOrigin;
2438 y = eventPtr->xexpose.y + canvasPtr->yOrigin;
2439 Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr, x, y,
2440 x + eventPtr->xexpose.width,
2441 y + eventPtr->xexpose.height);
2442 if ((eventPtr->xexpose.x < canvasPtr->inset)
2443 || (eventPtr->xexpose.y < canvasPtr->inset)
2444 || ((eventPtr->xexpose.x + eventPtr->xexpose.width)
2445 > (Tk_Width(canvasPtr->tkwin) - canvasPtr->inset))
2446 || ((eventPtr->xexpose.y + eventPtr->xexpose.height)
2447 > (Tk_Height(canvasPtr->tkwin) - canvasPtr->inset))) {
2448 canvasPtr->flags |= REDRAW_BORDERS;
2450 } else if (eventPtr->type == DestroyNotify) {
2451 DestroyCanvas((char *) canvasPtr);
2452 } else if (eventPtr->type == ConfigureNotify) {
2453 canvasPtr->flags |= UPDATE_SCROLLBARS;
2456 * The call below is needed in order to recenter the canvas if
2457 * it's confined and its scroll region is smaller than the window.
2460 CanvasSetOrigin(canvasPtr, canvasPtr->xOrigin, canvasPtr->yOrigin);
2461 Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr, canvasPtr->xOrigin,
2463 canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin),
2464 canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin));
2465 canvasPtr->flags |= REDRAW_BORDERS;
2466 } else if (eventPtr->type == FocusIn) {
2467 if (eventPtr->xfocus.detail != NotifyInferior) {
2468 CanvasFocusProc(canvasPtr, 1);
2470 } else if (eventPtr->type == FocusOut) {
2471 if (eventPtr->xfocus.detail != NotifyInferior) {
2472 CanvasFocusProc(canvasPtr, 0);
2474 } else if (eventPtr->type == UnmapNotify) {
2478 * Special hack: if the canvas is unmapped, then must notify
2479 * all items with "alwaysRedraw" set, so that they know that
2480 * they are no longer displayed.
2483 for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
2484 itemPtr = itemPtr->nextPtr) {
2485 if (itemPtr->typePtr->alwaysRedraw & 1) {
2486 (*itemPtr->typePtr->displayProc)((Tk_Canvas) canvasPtr,
2487 itemPtr, canvasPtr->display, None, 0, 0, 0, 0);
2494 *----------------------------------------------------------------------
2496 * CanvasCmdDeletedProc --
2498 * This procedure is invoked when a widget command is deleted. If
2499 * the widget isn't already in the process of being destroyed,
2500 * this command destroys it.
2506 * The widget is destroyed.
2508 *----------------------------------------------------------------------
2512 CanvasCmdDeletedProc(clientData)
2513 ClientData clientData; /* Pointer to widget record for widget. */
2515 TkCanvas *canvasPtr = (TkCanvas *) clientData;
2516 Tk_Window tkwin = canvasPtr->tkwin;
2519 * This procedure could be invoked either because the window was
2520 * destroyed and the command was then deleted (in which case tkwin
2521 * is NULL) or because the command was deleted, and then this procedure
2522 * destroys the widget.
2525 if (tkwin != NULL) {
2526 canvasPtr->tkwin = NULL;
2527 Tk_DestroyWindow(tkwin);
2532 *--------------------------------------------------------------
2534 * Tk_CanvasEventuallyRedraw --
2536 * Arrange for part or all of a canvas widget to redrawn at
2537 * some convenient time in the future.
2543 * The screen will eventually be refreshed.
2545 *--------------------------------------------------------------
2549 Tk_CanvasEventuallyRedraw(canvas, x1, y1, x2, y2)
2550 Tk_Canvas canvas; /* Information about widget. */
2551 int x1, y1; /* Upper left corner of area to redraw.
2552 * Pixels on edge are redrawn. */
2553 int x2, y2; /* Lower right corner of area to redraw.
2554 * Pixels on edge are not redrawn. */
2556 TkCanvas *canvasPtr = (TkCanvas *) canvas;
2558 * If tkwin is NULL, the canvas has been destroyed, so we can't really
2561 if (canvasPtr->tkwin == NULL) {
2565 if ((x1 >= x2) || (y1 >= y2) ||
2566 (x2 < canvasPtr->xOrigin) || (y2 < canvasPtr->yOrigin) ||
2567 (x1 >= canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin)) ||
2568 (y1 >= canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin))) {
2571 if (canvasPtr->flags & BBOX_NOT_EMPTY) {
2572 if (x1 <= canvasPtr->redrawX1) {
2573 canvasPtr->redrawX1 = x1;
2575 if (y1 <= canvasPtr->redrawY1) {
2576 canvasPtr->redrawY1 = y1;
2578 if (x2 >= canvasPtr->redrawX2) {
2579 canvasPtr->redrawX2 = x2;
2581 if (y2 >= canvasPtr->redrawY2) {
2582 canvasPtr->redrawY2 = y2;
2585 canvasPtr->redrawX1 = x1;
2586 canvasPtr->redrawY1 = y1;
2587 canvasPtr->redrawX2 = x2;
2588 canvasPtr->redrawY2 = y2;
2589 canvasPtr->flags |= BBOX_NOT_EMPTY;
2591 if (!(canvasPtr->flags & REDRAW_PENDING)) {
2592 Tcl_DoWhenIdle(DisplayCanvas, (ClientData) canvasPtr);
2593 canvasPtr->flags |= REDRAW_PENDING;
2598 *--------------------------------------------------------------
2600 * EventuallyRedrawItem --
2602 * Arrange for part or all of a canvas widget to redrawn at
2603 * some convenient time in the future.
2609 * The screen will eventually be refreshed.
2611 *--------------------------------------------------------------
2615 EventuallyRedrawItem(canvas, itemPtr)
2616 Tk_Canvas canvas; /* Information about widget. */
2617 Tk_Item *itemPtr; /* item to be redrawn. */
2619 TkCanvas *canvasPtr = (TkCanvas *) canvas;
2620 if ((itemPtr->x1 >= itemPtr->x2) || (itemPtr->y1 >= itemPtr->y2) ||
2621 (itemPtr->x2 < canvasPtr->xOrigin) ||
2622 (itemPtr->y2 < canvasPtr->yOrigin) ||
2623 (itemPtr->x1 >= canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin)) ||
2624 (itemPtr->y1 >= canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin))) {
2625 if (!(itemPtr->typePtr->alwaysRedraw & 1)) {
2629 if (!(itemPtr->redraw_flags & FORCE_REDRAW)) {
2630 if (canvasPtr->flags & BBOX_NOT_EMPTY) {
2631 if (itemPtr->x1 <= canvasPtr->redrawX1) {
2632 canvasPtr->redrawX1 = itemPtr->x1;
2634 if (itemPtr->y1 <= canvasPtr->redrawY1) {
2635 canvasPtr->redrawY1 = itemPtr->y1;
2637 if (itemPtr->x2 >= canvasPtr->redrawX2) {
2638 canvasPtr->redrawX2 = itemPtr->x2;
2640 if (itemPtr->y2 >= canvasPtr->redrawY2) {
2641 canvasPtr->redrawY2 = itemPtr->y2;
2644 canvasPtr->redrawX1 = itemPtr->x1;
2645 canvasPtr->redrawY1 = itemPtr->y1;
2646 canvasPtr->redrawX2 = itemPtr->x2;
2647 canvasPtr->redrawY2 = itemPtr->y2;
2648 canvasPtr->flags |= BBOX_NOT_EMPTY;
2650 itemPtr->redraw_flags |= FORCE_REDRAW;
2652 if (!(canvasPtr->flags & REDRAW_PENDING)) {
2653 Tcl_DoWhenIdle(DisplayCanvas, (ClientData) canvasPtr);
2654 canvasPtr->flags |= REDRAW_PENDING;
2659 *--------------------------------------------------------------
2661 * Tk_CreateItemType --
2663 * This procedure may be invoked to add a new kind of canvas
2664 * element to the core item types supported by Tk.
2670 * From now on, the new item type will be useable in canvas
2671 * widgets (e.g. typePtr->name can be used as the item type
2672 * in "create" widget commands). If there was already a
2673 * type with the same name as in typePtr, it is replaced with
2676 *--------------------------------------------------------------
2680 Tk_CreateItemType(typePtr)
2681 Tk_ItemType *typePtr; /* Information about item type;
2682 * storage must be statically
2683 * allocated (must live forever). */
2685 Tk_ItemType *typePtr2, *prevPtr;
2687 if (typeList == NULL) {
2692 * If there's already an item type with the given name, remove it.
2695 for (typePtr2 = typeList, prevPtr = NULL; typePtr2 != NULL;
2696 prevPtr = typePtr2, typePtr2 = typePtr2->nextPtr) {
2697 if (strcmp(typePtr2->name, typePtr->name) == 0) {
2698 if (prevPtr == NULL) {
2699 typeList = typePtr2->nextPtr;
2701 prevPtr->nextPtr = typePtr2->nextPtr;
2706 typePtr->nextPtr = typeList;
2711 *----------------------------------------------------------------------
2713 * Tk_GetItemTypes --
2715 * This procedure returns a pointer to the list of all item
2719 * The return value is a pointer to the first in the list
2720 * of item types currently supported by canvases.
2725 *----------------------------------------------------------------------
2731 if (typeList == NULL) {
2738 *--------------------------------------------------------------
2742 * This procedure is invoked to perform once-only-ever
2743 * initialization for the module, such as setting up
2752 *--------------------------------------------------------------
2758 if (typeList != NULL) {
2761 typeList = &tkRectangleType;
2762 tkRectangleType.nextPtr = &tkTextType;
2763 tkTextType.nextPtr = &tkLineType;
2764 tkLineType.nextPtr = &tkPolygonType;
2765 tkPolygonType.nextPtr = &tkImageType;
2766 tkImageType.nextPtr = &tkOvalType;
2767 tkOvalType.nextPtr = &tkBitmapType;
2768 tkBitmapType.nextPtr = &tkArcType;
2769 tkArcType.nextPtr = &tkWindowType;
2770 tkWindowType.nextPtr = NULL;
2771 #ifndef USE_OLD_TAG_SEARCH
2772 allUid = Tk_GetUid("all");
2773 currentUid = Tk_GetUid("current");
2774 andUid = Tk_GetUid("&&");
2775 orUid = Tk_GetUid("||");
2776 xorUid = Tk_GetUid("^");
2777 parenUid = Tk_GetUid("(");
2778 endparenUid = Tk_GetUid(")");
2779 negparenUid = Tk_GetUid("!(");
2780 tagvalUid = Tk_GetUid("!!");
2781 negtagvalUid = Tk_GetUid("!");
2782 #endif /* USE_OLD_TAG_SEARCH */
2785 #ifdef USE_OLD_TAG_SEARCH
2787 *--------------------------------------------------------------
2791 * This procedure is called to initiate an enumeration of
2792 * all items in a given canvas that contain a given tag.
2795 * The return value is a pointer to the first item in
2796 * canvasPtr that matches tag, or NULL if there is no
2797 * such item. The information at *searchPtr is initialized
2798 * such that successive calls to NextItem will return
2799 * successive items that match tag.
2802 * SearchPtr is linked into a list of searches in progress
2803 * on canvasPtr, so that elements can safely be deleted
2804 * while the search is in progress. EndTagSearch must be
2805 * called at the end of the search to unlink searchPtr from
2808 *--------------------------------------------------------------
2812 StartTagSearch(canvasPtr, tagObj, searchPtr)
2813 TkCanvas *canvasPtr; /* Canvas whose items are to be
2815 Tcl_Obj *tagObj; /* Object giving tag value. */
2816 TagSearch *searchPtr; /* Record describing tag search;
2817 * will be initialized here. */
2820 Tk_Item *itemPtr, *lastPtr;
2823 char *tag = Tcl_GetString(tagObj);
2828 tkwin = (TkWindow *) canvasPtr->tkwin;
2829 dispPtr = tkwin->dispPtr;
2832 * Initialize the search.
2835 searchPtr->canvasPtr = canvasPtr;
2836 searchPtr->searchOver = 0;
2839 * Find the first matching item in one of several ways. If the tag
2840 * is a number then it selects the single item with the matching
2841 * identifier. In this case see if the item being requested is the
2842 * hot item, in which case the search can be skipped.
2845 if (isdigit(UCHAR(*tag))) {
2847 Tcl_HashEntry *entryPtr;
2849 dispPtr->numIdSearches++;
2850 id = strtoul(tag, &end, 0);
2852 itemPtr = canvasPtr->hotPtr;
2853 lastPtr = canvasPtr->hotPrevPtr;
2854 if ((itemPtr == NULL) || (itemPtr->id != id) || (lastPtr == NULL)
2855 || (lastPtr->nextPtr != itemPtr)) {
2856 dispPtr->numSlowSearches++;
2857 entryPtr = Tcl_FindHashEntry(&canvasPtr->idTable, (char *) id);
2858 if (entryPtr != NULL) {
2859 itemPtr = (Tk_Item *)Tcl_GetHashValue(entryPtr);
2860 lastPtr = itemPtr->prevPtr;
2862 lastPtr = itemPtr = NULL;
2865 searchPtr->lastPtr = lastPtr;
2866 searchPtr->searchOver = 1;
2867 canvasPtr->hotPtr = itemPtr;
2868 canvasPtr->hotPrevPtr = lastPtr;
2873 searchPtr->tag = uid = Tk_GetUid(tag);
2874 if (uid == Tk_GetUid("all")) {
2879 searchPtr->tag = NULL;
2880 searchPtr->lastPtr = NULL;
2881 searchPtr->currentPtr = canvasPtr->firstItemPtr;
2882 return canvasPtr->firstItemPtr;
2886 * None of the above. Search for an item with a matching tag.
2889 for (lastPtr = NULL, itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
2890 lastPtr = itemPtr, itemPtr = itemPtr->nextPtr) {
2891 for (tagPtr = itemPtr->tagPtr, count = itemPtr->numTags;
2892 count > 0; tagPtr++, count--) {
2893 if (*tagPtr == uid) {
2894 searchPtr->lastPtr = lastPtr;
2895 searchPtr->currentPtr = itemPtr;
2900 searchPtr->lastPtr = lastPtr;
2901 searchPtr->searchOver = 1;
2906 *--------------------------------------------------------------
2910 * This procedure returns successive items that match a given
2911 * tag; it should be called only after StartTagSearch has been
2912 * used to begin a search.
2915 * The return value is a pointer to the next item that matches
2916 * the tag specified to StartTagSearch, or NULL if no such
2917 * item exists. *SearchPtr is updated so that the next call
2918 * to this procedure will return the next item.
2923 *--------------------------------------------------------------
2928 TagSearch *searchPtr; /* Record describing search in
2931 Tk_Item *itemPtr, *lastPtr;
2937 * Find next item in list (this may not actually be a suitable
2938 * one to return), and return if there are no items left.
2941 lastPtr = searchPtr->lastPtr;
2942 if (lastPtr == NULL) {
2943 itemPtr = searchPtr->canvasPtr->firstItemPtr;
2945 itemPtr = lastPtr->nextPtr;
2947 if ((itemPtr == NULL) || (searchPtr->searchOver)) {
2948 searchPtr->searchOver = 1;
2951 if (itemPtr != searchPtr->currentPtr) {
2953 * The structure of the list has changed. Probably the
2954 * previously-returned item was removed from the list.
2955 * In this case, don't advance lastPtr; just return
2956 * its new successor (i.e. do nothing here).
2960 itemPtr = lastPtr->nextPtr;
2964 * Handle special case of "all" search by returning next item.
2967 uid = searchPtr->tag;
2969 searchPtr->lastPtr = lastPtr;
2970 searchPtr->currentPtr = itemPtr;
2975 * Look for an item with a particular tag.
2978 for ( ; itemPtr != NULL; lastPtr = itemPtr, itemPtr = itemPtr->nextPtr) {
2979 for (tagPtr = itemPtr->tagPtr, count = itemPtr->numTags;
2980 count > 0; tagPtr++, count--) {
2981 if (*tagPtr == uid) {
2982 searchPtr->lastPtr = lastPtr;
2983 searchPtr->currentPtr = itemPtr;
2988 searchPtr->lastPtr = lastPtr;
2989 searchPtr->searchOver = 1;
2993 #else /* USE_OLD_TAG_SEARCH */
2995 *--------------------------------------------------------------
2997 * TagSearchExprInit --
2999 * This procedure allocates and initializes one TagSearchExpr struct.
3005 *--------------------------------------------------------------
3009 TagSearchExprInit(exprPtrPtr)
3010 TagSearchExpr **exprPtrPtr;
3012 TagSearchExpr* expr = *exprPtrPtr;
3015 expr = (TagSearchExpr *) ckalloc(sizeof(TagSearchExpr));
3016 expr->allocated = 0;
3027 *--------------------------------------------------------------
3029 * TagSearchExprDestroy --
3031 * This procedure destroys one TagSearchExpr structure.
3037 *--------------------------------------------------------------
3041 TagSearchExprDestroy(expr)
3042 TagSearchExpr *expr;
3046 ckfree((char *)expr->uids);
3048 ckfree((char *)expr);
3053 *--------------------------------------------------------------
3057 * This procedure is called to initiate an enumeration of
3058 * all items in a given canvas that contain a tag that matches
3059 * the tagOrId expression.
3062 * The return value indicates if the tagOrId expression
3063 * was successfully scanned (syntax).
3064 * The information at *searchPtr is initialized
3065 * such that a call to TagSearchFirst, followed by
3066 * successive calls to TagSearchNext will return items
3070 * SearchPtr is linked into a list of searches in progress
3071 * on canvasPtr, so that elements can safely be deleted
3072 * while the search is in progress.
3074 *--------------------------------------------------------------
3078 TagSearchScan(canvasPtr, tagObj, searchPtrPtr)
3079 TkCanvas *canvasPtr; /* Canvas whose items are to be
3081 Tcl_Obj *tagObj; /* Object giving tag value. */
3082 TagSearch **searchPtrPtr; /* Record describing tag search;
3083 * will be initialized here. */
3085 char *tag = Tcl_GetStringFromObj(tagObj,NULL);
3087 TagSearch *searchPtr;
3090 * Initialize the search.
3093 if (*searchPtrPtr) {
3094 searchPtr = *searchPtrPtr;
3096 /* Allocate primary search struct on first call */
3097 *searchPtrPtr = searchPtr = (TagSearch *) ckalloc(sizeof(TagSearch));
3098 searchPtr->expr = NULL;
3100 /* Allocate buffer for rewritten tags (after de-escaping) */
3101 searchPtr->rewritebufferAllocated = 100;
3102 searchPtr->rewritebuffer =
3103 ckalloc(searchPtr->rewritebufferAllocated);
3105 TagSearchExprInit(&(searchPtr->expr));
3107 /* How long is the tagOrId ? */
3108 searchPtr->stringLength = strlen(tag);
3110 /* Make sure there is enough buffer to hold rewritten tags */
3111 if ((unsigned int)searchPtr->stringLength >=
3112 searchPtr->rewritebufferAllocated) {
3113 searchPtr->rewritebufferAllocated = searchPtr->stringLength + 100;
3114 searchPtr->rewritebuffer =
3115 ckrealloc(searchPtr->rewritebuffer,
3116 searchPtr->rewritebufferAllocated);
3119 /* Initialize search */
3120 searchPtr->canvasPtr = canvasPtr;
3121 searchPtr->searchOver = 0;
3122 searchPtr->type = 0;
3125 * Find the first matching item in one of several ways. If the tag
3126 * is a number then it selects the single item with the matching
3127 * identifier. In this case see if the item being requested is the
3128 * hot item, in which case the search can be skipped.
3131 if (searchPtr->stringLength && isdigit(UCHAR(*tag))) {
3134 searchPtr->id = strtoul(tag, &end, 0);
3136 searchPtr->type = 1;
3142 * For all other tags and tag expressions convert to a UID.
3143 * This UID is kept forever, but this should be thought of
3144 * as a cache rather than as a memory leak.
3146 searchPtr->expr->uid = Tk_GetUid(tag);
3148 /* short circuit impossible searches for null tags */
3149 if (searchPtr->stringLength == 0) {
3154 * Pre-scan tag for at least one unquoted "&&" "||" "^" "!"
3155 * if not found then use string as simple tag
3157 for (i = 0; i < searchPtr->stringLength ; i++) {
3158 if (tag[i] == '"') {
3160 for ( ; i < searchPtr->stringLength; i++) {
3161 if (tag[i] == '\\') {
3165 if (tag[i] == '"') {
3170 if ((tag[i] == '&' && tag[i+1] == '&')
3171 || (tag[i] == '|' && tag[i+1] == '|')
3173 || (tag[i] == '!')) {
3174 searchPtr->type = 4;
3180 searchPtr->string = tag;
3181 searchPtr->stringIndex = 0;
3182 if (searchPtr->type == 4) {
3184 * an operator was found in the prescan, so
3185 * now compile the tag expression into array of Tk_Uid
3186 * flagging any syntax errors found
3188 if (TagSearchScanExpr(canvasPtr->interp, searchPtr, searchPtr->expr) != TCL_OK) {
3189 /* Syntax error in tag expression */
3190 /* Result message set by TagSearchScanExpr */
3193 searchPtr->expr->length = searchPtr->expr->index;
3195 if (searchPtr->expr->uid == allUid) {
3199 searchPtr->type = 2;
3202 * Optimized single-tag search
3204 searchPtr->type = 3;
3211 *--------------------------------------------------------------
3213 * TagSearchDestroy --
3215 * This procedure destroys any dynamic structures that
3216 * may have been allocated by TagSearchScan.
3222 *--------------------------------------------------------------
3226 TagSearchDestroy(searchPtr)
3227 TagSearch *searchPtr; /* Record describing tag search */
3230 TagSearchExprDestroy(searchPtr->expr);
3231 ckfree((char *)searchPtr->rewritebuffer);
3232 ckfree((char *)searchPtr);
3237 *--------------------------------------------------------------
3239 * TagSearchScanExpr --
3241 * This recursive procedure is called to scan a tag expression
3242 * and compile it into an array of Tk_Uids.
3245 * The return value indicates if the tagOrId expression
3246 * was successfully scanned (syntax).
3247 * The information at *searchPtr is initialized
3248 * such that a call to TagSearchFirst, followed by
3249 * successive calls to TagSearchNext will return items
3254 *--------------------------------------------------------------
3258 TagSearchScanExpr(interp, searchPtr, expr)
3259 Tcl_Interp *interp; /* Current interpreter. */
3260 TagSearch *searchPtr; /* Search data */
3261 TagSearchExpr *expr; /* compiled expression result */
3263 int looking_for_tag; /* When true, scanner expects
3264 * next char(s) to be a tag,
3265 * else operand expected */
3266 int found_tag; /* One or more tags found */
3267 int found_endquote; /* For quoted tag string parsing */
3268 int negate_result; /* Pending negation of next tag value */
3269 char *tag; /* tag from tag expression string */
3274 looking_for_tag = 1;
3275 while (searchPtr->stringIndex < searchPtr->stringLength) {
3276 c = searchPtr->string[searchPtr->stringIndex++];
3278 if (expr->allocated == expr->index) {
3279 expr->allocated += 15;
3282 (Tk_Uid *) ckrealloc((char *)(expr->uids),
3283 (expr->allocated)*sizeof(Tk_Uid));
3286 (Tk_Uid *) ckalloc((expr->allocated)*sizeof(Tk_Uid));
3290 if (looking_for_tag) {
3293 case ' ' : /* ignore unquoted whitespace */
3299 case '!' : /* negate next tag or subexpr */
3300 if (looking_for_tag > 1) {
3301 Tcl_AppendResult(interp,
3302 "Too many '!' in tag search expression",
3310 case '(' : /* scan (negated) subexpr recursively */
3311 if (negate_result) {
3312 expr->uids[expr->index++] = negparenUid;
3315 expr->uids[expr->index++] = parenUid;
3317 if (TagSearchScanExpr(interp, searchPtr, expr) != TCL_OK) {
3318 /* Result string should be already set
3319 * by nested call to tag_expr_scan() */
3322 looking_for_tag = 0;
3326 case '"' : /* quoted tag string */
3327 if (negate_result) {
3328 expr->uids[expr->index++] = negtagvalUid;
3331 expr->uids[expr->index++] = tagvalUid;
3333 tag = searchPtr->rewritebuffer;
3335 while (searchPtr->stringIndex < searchPtr->stringLength) {
3336 c = searchPtr->string[searchPtr->stringIndex++];
3338 c = searchPtr->string[searchPtr->stringIndex++];
3346 if (! found_endquote) {
3347 Tcl_AppendResult(interp,
3348 "Missing endquote in tag search expression",
3352 if (! (tag - searchPtr->rewritebuffer)) {
3353 Tcl_AppendResult(interp,
3354 "Null quoted tag string in tag search expression",
3359 expr->uids[expr->index++] =
3360 Tk_GetUid(searchPtr->rewritebuffer);
3361 looking_for_tag = 0;
3365 case '&' : /* illegal chars when looking for tag */
3369 Tcl_AppendResult(interp,
3370 "Unexpected operator in tag search expression",
3374 default : /* unquoted tag string */
3375 if (negate_result) {
3376 expr->uids[expr->index++] = negtagvalUid;
3379 expr->uids[expr->index++] = tagvalUid;
3381 tag = searchPtr->rewritebuffer;
3383 /* copy rest of tag, including any embedded whitespace */
3384 while (searchPtr->stringIndex < searchPtr->stringLength) {
3385 c = searchPtr->string[searchPtr->stringIndex];
3386 if (c == '!' || c == '&' || c == '|' || c == '^'
3387 || c == '(' || c == ')' || c == '"') {
3391 searchPtr->stringIndex++;
3393 /* remove trailing whitespace */
3396 /* there must have been one non-whitespace char,
3397 * so this will terminate */
3398 if (c != ' ' && c != '\t' && c != '\n' && c != '\r') {
3403 expr->uids[expr->index++] =
3404 Tk_GetUid(searchPtr->rewritebuffer);
3405 looking_for_tag = 0;
3409 } else { /* ! looking_for_tag */
3412 case ' ' : /* ignore whitespace */
3418 case '&' : /* AND operator */
3419 c = searchPtr->string[searchPtr->stringIndex++];
3421 Tcl_AppendResult(interp,
3422 "Singleton '&' in tag search expression",
3426 expr->uids[expr->index++] = andUid;
3427 looking_for_tag = 1;
3430 case '|' : /* OR operator */
3431 c = searchPtr->string[searchPtr->stringIndex++];
3433 Tcl_AppendResult(interp,
3434 "Singleton '|' in tag search expression",
3438 expr->uids[expr->index++] = orUid;
3439 looking_for_tag = 1;
3442 case '^' : /* XOR operator */
3443 expr->uids[expr->index++] = xorUid;
3444 looking_for_tag = 1;
3447 case ')' : /* end subexpression */
3448 expr->uids[expr->index++] = endparenUid;
3451 default : /* syntax error */
3452 Tcl_AppendResult(interp,
3453 "Invalid boolean operator in tag search expression",
3460 if (found_tag && ! looking_for_tag) {
3463 Tcl_AppendResult(interp, "Missing tag in tag search expression",
3469 *--------------------------------------------------------------
3471 * TagSearchEvalExpr --
3473 * This recursive procedure is called to eval a tag expression.
3476 * The return value indicates if the tagOrId expression
3477 * successfully matched the tags of the current item.
3481 *--------------------------------------------------------------
3485 TagSearchEvalExpr(expr, itemPtr)
3486 TagSearchExpr *expr; /* Search expression */
3487 Tk_Item *itemPtr; /* Item being test for match */
3489 int looking_for_tag; /* When true, scanner expects
3490 * next char(s) to be a tag,
3491 * else operand expected */
3492 int negate_result; /* Pending negation of next tag value */
3496 int result; /* Value of expr so far */
3499 result = 0; /* just to keep the compiler quiet */
3502 looking_for_tag = 1;
3503 while (expr->index < expr->length) {
3504 uid = expr->uids[expr->index++];
3505 if (looking_for_tag) {
3506 if (uid == tagvalUid) {
3508 * assert(expr->index < expr->length);
3510 uid = expr->uids[expr->index++];
3513 * set result 1 if tag is found in item's tags
3515 for (tagPtr = itemPtr->tagPtr, count = itemPtr->numTags;
3516 count > 0; tagPtr++, count--) {
3517 if (*tagPtr == uid) {
3523 } else if (uid == negtagvalUid) {
3524 negate_result = ! negate_result;
3526 * assert(expr->index < expr->length);
3528 uid = expr->uids[expr->index++];
3531 * set result 1 if tag is found in item's tags
3533 for (tagPtr = itemPtr->tagPtr, count = itemPtr->numTags;
3534 count > 0; tagPtr++, count--) {
3535 if (*tagPtr == uid) {
3541 } else if (uid == parenUid) {
3543 * evaluate subexpressions with recursion
3545 result = TagSearchEvalExpr(expr, itemPtr);
3547 } else if (uid == negparenUid) {
3548 negate_result = ! negate_result;
3550 * evaluate subexpressions with recursion
3552 result = TagSearchEvalExpr(expr, itemPtr);
3558 if (negate_result) {
3562 looking_for_tag = 0;
3563 } else { /* ! looking_for_tag */
3564 if (((uid == andUid) && (!result)) || ((uid == orUid) && result)) {
3566 * short circuit expression evaluation
3568 * if result before && is 0, or result before || is 1,
3569 * then the expression is decided and no further
3570 * evaluation is needed.
3574 while (expr->index < expr->length) {
3575 uid = expr->uids[expr->index++];
3576 if (uid == tagvalUid || uid == negtagvalUid) {
3580 if (uid == parenUid || uid == negparenUid) {
3584 if (uid == endparenUid) {
3586 if (parendepth < 0) {
3593 } else if (uid == xorUid) {
3595 * if the previous result was 1
3596 * then negate the next result
3598 negate_result = result;
3600 } else if (uid == endparenUid) {
3607 looking_for_tag = 1;
3611 * assert(! looking_for_tag);
3617 *--------------------------------------------------------------
3621 * This procedure is called to get the first item
3622 * item that matches a preestablished search predicate
3623 * that was set by TagSearchScan.
3626 * The return value is a pointer to the first item, or NULL
3627 * if there is no such item. The information at *searchPtr
3628 * is updated such that successive calls to TagSearchNext
3629 * will return successive items.
3632 * SearchPtr is linked into a list of searches in progress
3633 * on canvasPtr, so that elements can safely be deleted
3634 * while the search is in progress.
3636 *--------------------------------------------------------------
3640 TagSearchFirst(searchPtr)
3641 TagSearch *searchPtr; /* Record describing tag search */
3643 Tk_Item *itemPtr, *lastPtr;
3644 Tk_Uid uid, *tagPtr;
3647 /* short circuit impossible searches for null tags */
3648 if (searchPtr->stringLength == 0) {
3653 * Find the first matching item in one of several ways. If the tag
3654 * is a number then it selects the single item with the matching
3655 * identifier. In this case see if the item being requested is the
3656 * hot item, in which case the search can be skipped.
3659 if (searchPtr->type == 1) {
3660 Tcl_HashEntry *entryPtr;
3662 itemPtr = searchPtr->canvasPtr->hotPtr;
3663 lastPtr = searchPtr->canvasPtr->hotPrevPtr;
3664 if ((itemPtr == NULL) || (itemPtr->id != searchPtr->id) || (lastPtr == NULL)
3665 || (lastPtr->nextPtr != itemPtr)) {
3666 entryPtr = Tcl_FindHashEntry(&searchPtr->canvasPtr->idTable,
3667 (char *) searchPtr->id);
3668 if (entryPtr != NULL) {
3669 itemPtr = (Tk_Item *)Tcl_GetHashValue(entryPtr);
3670 lastPtr = itemPtr->prevPtr;
3672 lastPtr = itemPtr = NULL;
3675 searchPtr->lastPtr = lastPtr;
3676 searchPtr->searchOver = 1;
3677 searchPtr->canvasPtr->hotPtr = itemPtr;
3678 searchPtr->canvasPtr->hotPrevPtr = lastPtr;
3682 if (searchPtr->type == 2) {
3688 searchPtr->lastPtr = NULL;
3689 searchPtr->currentPtr = searchPtr->canvasPtr->firstItemPtr;
3690 return searchPtr->canvasPtr->firstItemPtr;
3693 if (searchPtr->type == 3) {
3696 * Optimized single-tag search
3699 uid = searchPtr->expr->uid;
3700 for (lastPtr = NULL, itemPtr = searchPtr->canvasPtr->firstItemPtr;
3701 itemPtr != NULL; lastPtr = itemPtr, itemPtr = itemPtr->nextPtr) {
3702 for (tagPtr = itemPtr->tagPtr, count = itemPtr->numTags;
3703 count > 0; tagPtr++, count--) {
3704 if (*tagPtr == uid) {
3705 searchPtr->lastPtr = lastPtr;
3706 searchPtr->currentPtr = itemPtr;
3714 * None of the above. Search for an item matching the tag expression.
3717 for (lastPtr = NULL, itemPtr = searchPtr->canvasPtr->firstItemPtr;
3718 itemPtr != NULL; lastPtr = itemPtr, itemPtr = itemPtr->nextPtr) {
3719 searchPtr->expr->index = 0;
3720 if (TagSearchEvalExpr(searchPtr->expr, itemPtr)) {
3721 searchPtr->lastPtr = lastPtr;
3722 searchPtr->currentPtr = itemPtr;
3727 searchPtr->lastPtr = lastPtr;
3728 searchPtr->searchOver = 1;
3733 *--------------------------------------------------------------
3737 * This procedure returns successive items that match a given
3738 * tag; it should be called only after TagSearchFirst has been
3739 * used to begin a search.
3742 * The return value is a pointer to the next item that matches
3743 * the tag expr specified to TagSearchScan, or NULL if no such
3744 * item exists. *SearchPtr is updated so that the next call
3745 * to this procedure will return the next item.
3750 *--------------------------------------------------------------
3754 TagSearchNext(searchPtr)
3755 TagSearch *searchPtr; /* Record describing search in
3758 Tk_Item *itemPtr, *lastPtr;
3759 Tk_Uid uid, *tagPtr;
3763 * Find next item in list (this may not actually be a suitable
3764 * one to return), and return if there are no items left.
3767 lastPtr = searchPtr->lastPtr;
3768 if (lastPtr == NULL) {
3769 itemPtr = searchPtr->canvasPtr->firstItemPtr;
3771 itemPtr = lastPtr->nextPtr;
3773 if ((itemPtr == NULL) || (searchPtr->searchOver)) {
3774 searchPtr->searchOver = 1;
3777 if (itemPtr != searchPtr->currentPtr) {
3779 * The structure of the list has changed. Probably the
3780 * previously-returned item was removed from the list.
3781 * In this case, don't advance lastPtr; just return
3782 * its new successor (i.e. do nothing here).
3786 itemPtr = lastPtr->nextPtr;
3789 if (searchPtr->type == 2) {
3795 searchPtr->lastPtr = lastPtr;
3796 searchPtr->currentPtr = itemPtr;
3800 if (searchPtr->type == 3) {
3803 * Optimized single-tag search
3806 uid = searchPtr->expr->uid;
3807 for ( ; itemPtr != NULL; lastPtr = itemPtr, itemPtr = itemPtr->nextPtr) {
3808 for (tagPtr = itemPtr->tagPtr, count = itemPtr->numTags;
3809 count > 0; tagPtr++, count--) {
3810 if (*tagPtr == uid) {
3811 searchPtr->lastPtr = lastPtr;
3812 searchPtr->currentPtr = itemPtr;
3817 searchPtr->lastPtr = lastPtr;
3818 searchPtr->searchOver = 1;
3823 * Else.... evaluate tag expression
3826 for ( ; itemPtr != NULL; lastPtr = itemPtr, itemPtr = itemPtr->nextPtr) {
3827 searchPtr->expr->index = 0;
3828 if (TagSearchEvalExpr(searchPtr->expr, itemPtr)) {
3829 searchPtr->lastPtr = lastPtr;
3830 searchPtr->currentPtr = itemPtr;
3834 searchPtr->lastPtr = lastPtr;
3835 searchPtr->searchOver = 1;
3838 #endif /* USE_OLD_TAG_SEARCH */
3841 *--------------------------------------------------------------
3845 * This is a utility procedure called by FindItems. It
3846 * either adds itemPtr's id to the result forming in interp,
3847 * or it adds a new tag to itemPtr, depending on the value
3854 * If tag is NULL then itemPtr's id is added as a list element
3855 * to the interp's result; otherwise tag is added to itemPtr's
3858 *--------------------------------------------------------------
3862 DoItem(interp, itemPtr, tag)
3863 Tcl_Interp *interp; /* Interpreter in which to (possibly)
3864 * record item id. */
3865 Tk_Item *itemPtr; /* Item to (possibly) modify. */
3866 Tk_Uid tag; /* Tag to add to those already
3867 * present for item, or NULL. */
3873 * Handle the "add-to-result" case and return, if appropriate.
3877 char msg[TCL_INTEGER_SPACE];
3879 sprintf(msg, "%d", itemPtr->id);
3880 Tcl_AppendElement(interp, msg);
3884 for (tagPtr = itemPtr->tagPtr, count = itemPtr->numTags;
3885 count > 0; tagPtr++, count--) {
3886 if (tag == *tagPtr) {
3892 * Grow the tag space if there's no more room left in the current
3896 if (itemPtr->tagSpace == itemPtr->numTags) {
3899 itemPtr->tagSpace += 5;
3900 newTagPtr = (Tk_Uid *) ckalloc((unsigned)
3901 (itemPtr->tagSpace * sizeof(Tk_Uid)));
3902 memcpy((VOID *) newTagPtr, (VOID *) itemPtr->tagPtr,
3903 (itemPtr->numTags * sizeof(Tk_Uid)));
3904 if (itemPtr->tagPtr != itemPtr->staticTagSpace) {
3905 ckfree((char *) itemPtr->tagPtr);
3907 itemPtr->tagPtr = newTagPtr;
3908 tagPtr = &itemPtr->tagPtr[itemPtr->numTags];
3912 * Add in the new tag.
3920 *--------------------------------------------------------------
3924 * This procedure does all the work of implementing the
3925 * "find" and "addtag" options of the canvas widget command,
3926 * which locate items that have certain features (location,
3927 * tags, position in display list, etc.).
3930 * A standard Tcl return value. If newTag is NULL, then a
3931 * list of ids from all the items that match argc/argv is
3932 * returned in the interp's result. If newTag is NULL, then
3933 * the normal the interp's result is an empty string. If an error
3934 * occurs, then the interp's result will hold an error message.
3937 * If newTag is non-NULL, then all the items that match the
3938 * information in argc/argv have that tag added to their
3941 *--------------------------------------------------------------
3945 #ifdef USE_OLD_TAG_SEARCH
3946 FindItems(interp, canvasPtr, argc, argv, newTag, first)
3947 #else /* USE_OLD_TAG_SEARCH */
3948 FindItems(interp, canvasPtr, argc, argv, newTag, first, searchPtrPtr)
3949 #endif /* USE_OLD_TAG_SEARCH */
3950 Tcl_Interp *interp; /* Interpreter for error reporting. */
3951 TkCanvas *canvasPtr; /* Canvas whose items are to be
3953 int argc; /* Number of entries in argv. Must be
3954 * greater than zero. */
3955 Tcl_Obj *CONST *argv; /* Arguments that describe what items
3956 * to search for (see user doc on
3957 * "find" and "addtag" options). */
3958 Tcl_Obj *newTag; /* If non-NULL, gives new tag to set
3959 * on all found items; if NULL, then
3960 * ids of found items are returned
3961 * in the interp's result. */
3962 int first; /* For error messages: gives number
3963 * of elements of argv which are already
3965 #ifndef USE_OLD_TAG_SEARCH
3966 TagSearch **searchPtrPtr; /* From CanvasWidgetCmd local vars*/
3967 #endif /* not USE_OLD_TAG_SEARCH */
3969 #ifdef USE_OLD_TAG_SEARCH
3971 #endif /* USE_OLD_TAG_SEARCH */
3975 static char *optionStrings[] = {
3976 "above", "all", "below", "closest",
3977 "enclosed", "overlapping", "withtag", NULL
3980 CANV_ABOVE, CANV_ALL, CANV_BELOW, CANV_CLOSEST,
3981 CANV_ENCLOSED, CANV_OVERLAPPING, CANV_WITHTAG
3984 if (newTag != NULL) {
3985 uid = Tk_GetUid(Tcl_GetStringFromObj(newTag, NULL));
3989 if (Tcl_GetIndexFromObj(interp, argv[first], optionStrings, "search command", 0,
3990 &index) != TCL_OK) {
3993 switch ((enum options) index) {
3995 Tk_Item *lastPtr = NULL;
3996 if (argc != first+2) {
3997 Tcl_WrongNumArgs(interp, first+1, argv, "tagOrId");
4000 #ifdef USE_OLD_TAG_SEARCH
4001 for (itemPtr = StartTagSearch(canvasPtr, argv[first+1], &search);
4002 itemPtr != NULL; itemPtr = NextItem(&search)) {
4003 #else /* USE_OLD_TAG_SEARCH */
4004 if (TagSearchScan(canvasPtr, argv[first+1], searchPtrPtr) != TCL_OK) {
4007 for (itemPtr = TagSearchFirst(*searchPtrPtr);
4008 itemPtr != NULL; itemPtr = TagSearchNext(*searchPtrPtr)) {
4009 #endif /* USE_OLD_TAG_SEARCH */
4012 if ((lastPtr != NULL) && (lastPtr->nextPtr != NULL)) {
4013 DoItem(interp, lastPtr->nextPtr, uid);
4018 if (argc != first+1) {
4019 Tcl_WrongNumArgs(interp, first+1, argv, (char *) NULL);
4023 for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
4024 itemPtr = itemPtr->nextPtr) {
4025 DoItem(interp, itemPtr, uid);
4032 if (argc != first+2) {
4033 Tcl_WrongNumArgs(interp, first+1, argv, "tagOrId");
4036 #ifdef USE_OLD_TAG_SEARCH
4037 itemPtr = StartTagSearch(canvasPtr, argv[first+1], &search);
4038 #else /* USE_OLD_TAG_SEARCH */
4039 if (TagSearchScan(canvasPtr, argv[first+1], searchPtrPtr) != TCL_OK) {
4042 itemPtr = TagSearchFirst(*searchPtrPtr);
4043 #endif /* USE_OLD_TAG_SEARCH */
4044 if (itemPtr != NULL) {
4045 if (itemPtr->prevPtr != NULL) {
4046 DoItem(interp, itemPtr->prevPtr, uid);
4051 case CANV_CLOSEST: {
4053 Tk_Item *startPtr, *closestPtr;
4054 double coords[2], halo;
4057 if ((argc < first+3) || (argc > first+5)) {
4058 Tcl_WrongNumArgs(interp, first+1, argv, "x y ?halo? ?start?");
4061 if ((Tk_CanvasGetCoordFromObj(interp, (Tk_Canvas) canvasPtr, argv[first+1],
4062 &coords[0]) != TCL_OK) || (Tk_CanvasGetCoordFromObj(interp,
4063 (Tk_Canvas) canvasPtr, argv[first+2], &coords[1]) != TCL_OK)) {
4066 if (argc > first+3) {
4067 if (Tk_CanvasGetCoordFromObj(interp, (Tk_Canvas) canvasPtr, argv[first+3],
4072 Tcl_AppendResult(interp, "can't have negative halo value \"",
4073 Tcl_GetString(argv[3]), "\"", (char *) NULL);
4081 * Find the item at which to start the search.
4084 startPtr = canvasPtr->firstItemPtr;
4085 if (argc == first+5) {
4086 #ifdef USE_OLD_TAG_SEARCH
4087 itemPtr = StartTagSearch(canvasPtr, argv[first+4], &search);
4088 #else /* USE_OLD_TAG_SEARCH */
4089 if (TagSearchScan(canvasPtr, argv[first+4], searchPtrPtr) != TCL_OK) {
4092 itemPtr = TagSearchFirst(*searchPtrPtr);
4093 #endif /* USE_OLD_TAG_SEARCH */
4094 if (itemPtr != NULL) {
4100 * The code below is optimized so that it can eliminate most
4101 * items without having to call their item-specific procedures.
4102 * This is done by keeping a bounding box (x1, y1, x2, y2) that
4103 * an item's bbox must overlap if the item is to have any
4104 * chance of being closer than the closest so far.
4108 while(itemPtr && (itemPtr->state == TK_STATE_HIDDEN ||
4109 (itemPtr->state == TK_STATE_NULL && canvasPtr->canvas_state == TK_STATE_HIDDEN))) {
4110 itemPtr = itemPtr->nextPtr;
4112 if (itemPtr == NULL) {
4115 closestDist = (*itemPtr->typePtr->pointProc)((Tk_Canvas) canvasPtr,
4116 itemPtr, coords) - halo;
4117 if (closestDist < 0.0) {
4124 * Update the bounding box using itemPtr, which is the
4128 x1 = (int) (coords[0] - closestDist - halo - 1);
4129 y1 = (int) (coords[1] - closestDist - halo - 1);
4130 x2 = (int) (coords[0] + closestDist + halo + 1);
4131 y2 = (int) (coords[1] + closestDist + halo + 1);
4132 closestPtr = itemPtr;
4135 * Search for an item that beats the current closest one.
4136 * Work circularly through the canvas's item list until
4137 * getting back to the starting item.
4141 itemPtr = itemPtr->nextPtr;
4142 if (itemPtr == NULL) {
4143 itemPtr = canvasPtr->firstItemPtr;
4145 if (itemPtr == startPtr) {
4146 DoItem(interp, closestPtr, uid);
4149 if (itemPtr->state == TK_STATE_HIDDEN || (itemPtr->state == TK_STATE_NULL &&
4150 canvasPtr->canvas_state == TK_STATE_HIDDEN)) {
4153 if ((itemPtr->x1 >= x2) || (itemPtr->x2 <= x1)
4154 || (itemPtr->y1 >= y2) || (itemPtr->y2 <= y1)) {
4157 newDist = (*itemPtr->typePtr->pointProc)((Tk_Canvas) canvasPtr,
4158 itemPtr, coords) - halo;
4159 if (newDist < 0.0) {
4162 if (newDist <= closestDist) {
4163 closestDist = newDist;
4170 case CANV_ENCLOSED: {
4171 if (argc != first+5) {
4172 Tcl_WrongNumArgs(interp, first+1, argv, "x1 y1 x2 y2");
4175 return FindArea(interp, canvasPtr, argv+first+1, uid, 1);
4177 case CANV_OVERLAPPING: {
4178 if (argc != first+5) {
4179 Tcl_WrongNumArgs(interp, first+1, argv, "x1 y1 x2 y2");
4182 return FindArea(interp, canvasPtr, argv+first+1, uid, 0);
4184 case CANV_WITHTAG: {
4185 if (argc != first+2) {
4186 Tcl_WrongNumArgs(interp, first+1, argv, "tagOrId");
4189 #ifdef USE_OLD_TAG_SEARCH
4190 for (itemPtr = StartTagSearch(canvasPtr, argv[first+1], &search);
4191 itemPtr != NULL; itemPtr = NextItem(&search)) {
4192 #else /* USE_OLD_TAG_SEARCH */
4193 if (TagSearchScan(canvasPtr, argv[first+1], searchPtrPtr) != TCL_OK) {
4196 for (itemPtr = TagSearchFirst(*searchPtrPtr);
4197 itemPtr != NULL; itemPtr = TagSearchNext(*searchPtrPtr)) {
4198 #endif /* USE_OLD_TAG_SEARCH */
4199 DoItem(interp, itemPtr, uid);
4207 *--------------------------------------------------------------
4211 * This procedure implements area searches for the "find"
4212 * and "addtag" options.
4215 * A standard Tcl return value. If newTag is NULL, then a
4216 * list of ids from all the items overlapping or enclosed
4217 * by the rectangle given by argc is returned in the interp's result.
4218 * If newTag is NULL, then the normal the interp's result is an
4219 * empty string. If an error occurs, then the interp's result will
4220 * hold an error message.
4223 * If uid is non-NULL, then all the items overlapping
4224 * or enclosed by the area in argv have that tag added to
4225 * their lists of tags.
4227 *--------------------------------------------------------------
4231 FindArea(interp, canvasPtr, argv, uid, enclosed)
4232 Tcl_Interp *interp; /* Interpreter for error reporting
4233 * and result storing. */
4234 TkCanvas *canvasPtr; /* Canvas whose items are to be
4236 Tcl_Obj *CONST *argv; /* Array of four arguments that
4237 * give the coordinates of the
4238 * rectangular area to search. */
4239 Tk_Uid uid; /* If non-NULL, gives new tag to set
4240 * on all found items; if NULL, then
4241 * ids of found items are returned
4242 * in the interp's result. */
4243 int enclosed; /* 0 means overlapping or enclosed
4244 * items are OK, 1 means only enclosed
4247 double rect[4], tmp;
4251 if ((Tk_CanvasGetCoordFromObj(interp, (Tk_Canvas) canvasPtr, argv[0],
4252 &rect[0]) != TCL_OK)
4253 || (Tk_CanvasGetCoordFromObj(interp, (Tk_Canvas) canvasPtr, argv[1],
4254 &rect[1]) != TCL_OK)
4255 || (Tk_CanvasGetCoordFromObj(interp, (Tk_Canvas) canvasPtr, argv[2],
4256 &rect[2]) != TCL_OK)
4257 || (Tk_CanvasGetCoordFromObj(interp, (Tk_Canvas) canvasPtr, argv[3],
4258 &rect[3]) != TCL_OK)) {
4261 if (rect[0] > rect[2]) {
4262 tmp = rect[0]; rect[0] = rect[2]; rect[2] = tmp;
4264 if (rect[1] > rect[3]) {
4265 tmp = rect[1]; rect[1] = rect[3]; rect[3] = tmp;
4269 * Use an integer bounding box for a quick test, to avoid
4270 * calling item-specific code except for items that are close.
4273 x1 = (int) (rect[0]-1.0);
4274 y1 = (int) (rect[1]-1.0);
4275 x2 = (int) (rect[2]+1.0);
4276 y2 = (int) (rect[3]+1.0);
4277 for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
4278 itemPtr = itemPtr->nextPtr) {
4279 if (itemPtr->state == TK_STATE_HIDDEN || (itemPtr->state == TK_STATE_NULL &&
4280 canvasPtr->canvas_state == TK_STATE_HIDDEN)) {
4283 if ((itemPtr->x1 >= x2) || (itemPtr->x2 <= x1)
4284 || (itemPtr->y1 >= y2) || (itemPtr->y2 <= y1)) {
4287 if ((*itemPtr->typePtr->areaProc)((Tk_Canvas) canvasPtr, itemPtr, rect)
4289 DoItem(interp, itemPtr, uid);
4296 *--------------------------------------------------------------
4300 * Move one or more items to a different place in the
4301 * display order for a canvas.
4307 * The items identified by "tag" are moved so that they
4308 * are all together in the display list and immediately
4309 * after prevPtr. The order of the moved items relative
4310 * to each other is not changed.
4312 *--------------------------------------------------------------
4315 #ifdef USE_OLD_TAG_SEARCH
4317 RelinkItems(canvasPtr, tag, prevPtr)
4318 #else /* USE_OLD_TAG_SEARCH */
4320 RelinkItems(canvasPtr, tag, prevPtr, searchPtrPtr)
4321 #endif /* USE_OLD_TAG_SEARCH */
4322 TkCanvas *canvasPtr; /* Canvas to be modified. */
4323 Tcl_Obj *tag; /* Tag identifying items to be moved
4324 * in the redisplay list. */
4325 Tk_Item *prevPtr; /* Reposition the items so that they
4326 * go just after this item (NULL means
4327 * put at beginning of list). */
4328 #ifndef USE_OLD_TAG_SEARCH
4329 TagSearch **searchPtrPtr; /* From CanvasWidgetCmd local vars */
4330 #endif /* not USE_OLD_TAG_SEARCH */
4333 #ifdef USE_OLD_TAG_SEARCH
4335 #endif /* USE_OLD_TAG_SEARCH */
4336 Tk_Item *firstMovePtr, *lastMovePtr;
4339 * Find all of the items to be moved and remove them from
4340 * the list, making an auxiliary list running from firstMovePtr
4341 * to lastMovePtr. Record their areas for redisplay.
4344 firstMovePtr = lastMovePtr = NULL;
4345 #ifdef USE_OLD_TAG_SEARCH
4346 for (itemPtr = StartTagSearch(canvasPtr, tag, &search);
4347 itemPtr != NULL; itemPtr = NextItem(&search)) {
4348 #else /* USE_OLD_TAG_SEARCH */
4349 if (TagSearchScan(canvasPtr, tag, searchPtrPtr) != TCL_OK) {
4352 for (itemPtr = TagSearchFirst(*searchPtrPtr);
4353 itemPtr != NULL; itemPtr = TagSearchNext(*searchPtrPtr)) {
4354 #endif /* USE_OLD_TAG_SEARCH */
4355 if (itemPtr == prevPtr) {
4357 * Item after which insertion is to occur is being
4358 * moved! Switch to insert after its predecessor.
4361 prevPtr = prevPtr->prevPtr;
4363 if (itemPtr->prevPtr == NULL) {
4364 if (itemPtr->nextPtr != NULL) {
4365 itemPtr->nextPtr->prevPtr = NULL;
4367 canvasPtr->firstItemPtr = itemPtr->nextPtr;
4369 if (itemPtr->nextPtr != NULL) {
4370 itemPtr->nextPtr->prevPtr = itemPtr->prevPtr;
4372 itemPtr->prevPtr->nextPtr = itemPtr->nextPtr;
4374 if (canvasPtr->lastItemPtr == itemPtr) {
4375 canvasPtr->lastItemPtr = itemPtr->prevPtr;
4377 if (firstMovePtr == NULL) {
4378 itemPtr->prevPtr = NULL;
4379 firstMovePtr = itemPtr;
4381 itemPtr->prevPtr = lastMovePtr;
4382 lastMovePtr->nextPtr = itemPtr;
4384 lastMovePtr = itemPtr;
4385 EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
4386 canvasPtr->flags |= REPICK_NEEDED;
4390 * Insert the list of to-be-moved items back into the canvas's
4391 * at the desired position.
4394 if (firstMovePtr == NULL) {
4395 #ifdef USE_OLD_TAG_SEARCH
4397 #else /* USE_OLD_TAG_SEARCH */
4399 #endif /* USE_OLD_TAG_SEARCH */
4401 if (prevPtr == NULL) {
4402 if (canvasPtr->firstItemPtr != NULL) {
4403 canvasPtr->firstItemPtr->prevPtr = lastMovePtr;
4405 lastMovePtr->nextPtr = canvasPtr->firstItemPtr;
4406 canvasPtr->firstItemPtr = firstMovePtr;
4408 if (prevPtr->nextPtr != NULL) {
4409 prevPtr->nextPtr->prevPtr = lastMovePtr;
4411 lastMovePtr->nextPtr = prevPtr->nextPtr;
4412 if (firstMovePtr != NULL) {
4413 firstMovePtr->prevPtr = prevPtr;
4415 prevPtr->nextPtr = firstMovePtr;
4417 if (canvasPtr->lastItemPtr == prevPtr) {
4418 canvasPtr->lastItemPtr = lastMovePtr;
4420 #ifndef USE_OLD_TAG_SEARCH
4422 #endif /* not USE_OLD_TAG_SEARCH */
4426 *--------------------------------------------------------------
4430 * This procedure is invoked by the Tk dispatcher to handle
4431 * events associated with bindings on items.
4437 * Depends on the command invoked as part of the binding
4438 * (if there was any).
4440 *--------------------------------------------------------------
4444 CanvasBindProc(clientData, eventPtr)
4445 ClientData clientData; /* Pointer to canvas structure. */
4446 XEvent *eventPtr; /* Pointer to X event that just
4449 TkCanvas *canvasPtr = (TkCanvas *) clientData;
4451 Tcl_Preserve((ClientData) canvasPtr);
4454 * This code below keeps track of the current modifier state in
4455 * canvasPtr>state. This information is used to defer repicks of
4456 * the current item while buttons are down.
4459 if ((eventPtr->type == ButtonPress) || (eventPtr->type == ButtonRelease)) {
4462 switch (eventPtr->xbutton.button) {
4484 * For button press events, repick the current item using the
4485 * button state before the event, then process the event. For
4486 * button release events, first process the event, then repick
4487 * the current item using the button state *after* the event
4488 * (the button has logically gone up before we change the
4492 if (eventPtr->type == ButtonPress) {
4494 * On a button press, first repick the current item using
4495 * the button state before the event, the process the event.
4498 canvasPtr->state = eventPtr->xbutton.state;
4499 PickCurrentItem(canvasPtr, eventPtr);
4500 canvasPtr->state ^= mask;
4501 CanvasDoEvent(canvasPtr, eventPtr);
4504 * Button release: first process the event, with the button
4505 * still considered to be down. Then repick the current
4506 * item under the assumption that the button is no longer down.
4509 canvasPtr->state = eventPtr->xbutton.state;
4510 CanvasDoEvent(canvasPtr, eventPtr);
4511 eventPtr->xbutton.state ^= mask;
4512 canvasPtr->state = eventPtr->xbutton.state;
4513 PickCurrentItem(canvasPtr, eventPtr);
4514 eventPtr->xbutton.state ^= mask;
4517 } else if ((eventPtr->type == EnterNotify)
4518 || (eventPtr->type == LeaveNotify)) {
4519 canvasPtr->state = eventPtr->xcrossing.state;
4520 PickCurrentItem(canvasPtr, eventPtr);
4522 } else if (eventPtr->type == MotionNotify) {
4523 canvasPtr->state = eventPtr->xmotion.state;
4524 PickCurrentItem(canvasPtr, eventPtr);
4526 CanvasDoEvent(canvasPtr, eventPtr);
4529 Tcl_Release((ClientData) canvasPtr);
4533 *--------------------------------------------------------------
4535 * PickCurrentItem --
4537 * Find the topmost item in a canvas that contains a given
4538 * location and mark the the current item. If the current
4539 * item has changed, generate a fake exit event on the old
4540 * current item, a fake enter event on the new current item
4541 * item and force a redraw of the two items. Canvas items
4542 * that are hidden or disabled are ignored.
4548 * The current item for canvasPtr may change. If it does,
4549 * then the commands associated with item entry and exit
4550 * could do just about anything. A binding script could
4551 * delete the canvas, so callers should protect themselves
4552 * with Tcl_Preserve and Tcl_Release.
4554 *--------------------------------------------------------------
4558 PickCurrentItem(canvasPtr, eventPtr)
4559 TkCanvas *canvasPtr; /* Canvas widget in which to select
4561 XEvent *eventPtr; /* Event describing location of
4562 * mouse cursor. Must be EnterWindow,
4563 * LeaveWindow, ButtonRelease, or
4568 Tk_Item *prevItemPtr;
4571 * Check whether or not a button is down. If so, we'll log entry
4572 * and exit into and out of the current item, but not entry into
4573 * any other item. This implements a form of grabbing equivalent
4574 * to what the X server does for windows.
4577 buttonDown = canvasPtr->state
4578 & (Button1Mask|Button2Mask|Button3Mask|Button4Mask|Button5Mask);
4580 canvasPtr->flags &= ~LEFT_GRABBED_ITEM;
4584 * Save information about this event in the canvas. The event in
4585 * the canvas is used for two purposes:
4587 * 1. Event bindings: if the current item changes, fake events are
4588 * generated to allow item-enter and item-leave bindings to trigger.
4589 * 2. Reselection: if the current item gets deleted, can use the
4590 * saved event to find a new current item.
4591 * Translate MotionNotify events into EnterNotify events, since that's
4592 * what gets reported to item handlers.
4595 if (eventPtr != &canvasPtr->pickEvent) {
4596 if ((eventPtr->type == MotionNotify)
4597 || (eventPtr->type == ButtonRelease)) {
4598 canvasPtr->pickEvent.xcrossing.type = EnterNotify;
4599 canvasPtr->pickEvent.xcrossing.serial = eventPtr->xmotion.serial;
4600 canvasPtr->pickEvent.xcrossing.send_event
4601 = eventPtr->xmotion.send_event;
4602 canvasPtr->pickEvent.xcrossing.display = eventPtr->xmotion.display;
4603 canvasPtr->pickEvent.xcrossing.window = eventPtr->xmotion.window;
4604 canvasPtr->pickEvent.xcrossing.root = eventPtr->xmotion.root;
4605 canvasPtr->pickEvent.xcrossing.subwindow = None;
4606 canvasPtr->pickEvent.xcrossing.time = eventPtr->xmotion.time;
4607 canvasPtr->pickEvent.xcrossing.x = eventPtr->xmotion.x;
4608 canvasPtr->pickEvent.xcrossing.y = eventPtr->xmotion.y;
4609 canvasPtr->pickEvent.xcrossing.x_root = eventPtr->xmotion.x_root;
4610 canvasPtr->pickEvent.xcrossing.y_root = eventPtr->xmotion.y_root;
4611 canvasPtr->pickEvent.xcrossing.mode = NotifyNormal;
4612 canvasPtr->pickEvent.xcrossing.detail = NotifyNonlinear;
4613 canvasPtr->pickEvent.xcrossing.same_screen
4614 = eventPtr->xmotion.same_screen;
4615 canvasPtr->pickEvent.xcrossing.focus = False;
4616 canvasPtr->pickEvent.xcrossing.state = eventPtr->xmotion.state;
4618 canvasPtr->pickEvent = *eventPtr;
4623 * If this is a recursive call (there's already a partially completed
4624 * call pending on the stack; it's in the middle of processing a
4625 * Leave event handler for the old current item) then just return;
4626 * the pending call will do everything that's needed.
4629 if (canvasPtr->flags & REPICK_IN_PROGRESS) {
4634 * A LeaveNotify event automatically means that there's no current
4635 * object, so the check for closest item can be skipped.
4638 coords[0] = canvasPtr->pickEvent.xcrossing.x + canvasPtr->xOrigin;
4639 coords[1] = canvasPtr->pickEvent.xcrossing.y + canvasPtr->yOrigin;
4640 if (canvasPtr->pickEvent.type != LeaveNotify) {
4641 canvasPtr->newCurrentPtr = CanvasFindClosest(canvasPtr, coords);
4643 canvasPtr->newCurrentPtr = NULL;
4646 if ((canvasPtr->newCurrentPtr == canvasPtr->currentItemPtr)
4647 && !(canvasPtr->flags & LEFT_GRABBED_ITEM)) {
4649 * Nothing to do: the current item hasn't changed.
4656 * Simulate a LeaveNotify event on the previous current item and
4657 * an EnterNotify event on the new current item. Remove the "current"
4658 * tag from the previous current item and place it on the new current
4662 if ((canvasPtr->newCurrentPtr != canvasPtr->currentItemPtr)
4663 && (canvasPtr->currentItemPtr != NULL)
4664 && !(canvasPtr->flags & LEFT_GRABBED_ITEM)) {
4666 Tk_Item *itemPtr = canvasPtr->currentItemPtr;
4669 event = canvasPtr->pickEvent;
4670 event.type = LeaveNotify;
4673 * If the event's detail happens to be NotifyInferior the
4674 * binding mechanism will discard the event. To be consistent,
4675 * always use NotifyAncestor.
4678 event.xcrossing.detail = NotifyAncestor;
4679 canvasPtr->flags |= REPICK_IN_PROGRESS;
4680 CanvasDoEvent(canvasPtr, &event);
4681 canvasPtr->flags &= ~REPICK_IN_PROGRESS;
4684 * The check below is needed because there could be an event
4685 * handler for <LeaveNotify> that deletes the current item.
4688 if ((itemPtr == canvasPtr->currentItemPtr) && !buttonDown) {
4689 for (i = itemPtr->numTags-1; i >= 0; i--) {
4690 #ifdef USE_OLD_TAG_SEARCH
4691 if (itemPtr->tagPtr[i] == Tk_GetUid("current")) {
4692 #else /* USE_OLD_TAG_SEARCH */
4693 if (itemPtr->tagPtr[i] == currentUid) {
4694 #endif /* USE_OLD_TAG_SEARCH */
4695 itemPtr->tagPtr[i] = itemPtr->tagPtr[itemPtr->numTags-1];
4703 * Note: during CanvasDoEvent above, it's possible that
4704 * canvasPtr->newCurrentPtr got reset to NULL because the
4708 if ((canvasPtr->newCurrentPtr != canvasPtr->currentItemPtr) && buttonDown) {
4709 canvasPtr->flags |= LEFT_GRABBED_ITEM;
4714 * Special note: it's possible that canvasPtr->newCurrentPtr ==
4715 * canvasPtr->currentItemPtr here. This can happen, for example,
4716 * if LEFT_GRABBED_ITEM was set.
4719 prevItemPtr = canvasPtr->currentItemPtr;
4720 canvasPtr->flags &= ~LEFT_GRABBED_ITEM;
4721 canvasPtr->currentItemPtr = canvasPtr->newCurrentPtr;
4722 if (prevItemPtr != NULL && prevItemPtr != canvasPtr->currentItemPtr &&
4723 (prevItemPtr->redraw_flags & TK_ITEM_STATE_DEPENDANT)) {
4724 EventuallyRedrawItem((Tk_Canvas) canvasPtr, prevItemPtr);
4725 (*prevItemPtr->typePtr->configProc)(canvasPtr->interp,
4726 (Tk_Canvas) canvasPtr, prevItemPtr, 0, (Tcl_Obj **) NULL,
4727 TK_CONFIG_ARGV_ONLY);
4729 if (canvasPtr->currentItemPtr != NULL) {
4732 #ifdef USE_OLD_TAG_SEARCH
4733 DoItem((Tcl_Interp *) NULL, canvasPtr->currentItemPtr,
4734 Tk_GetUid("current"));
4735 #else /* USE_OLD_TAG_SEARCH */
4736 DoItem((Tcl_Interp *) NULL, canvasPtr->currentItemPtr, currentUid);
4737 #endif /* USE_OLD_TAG_SEA */
4738 if ((canvasPtr->currentItemPtr->redraw_flags & TK_ITEM_STATE_DEPENDANT &&
4739 prevItemPtr != canvasPtr->currentItemPtr)) {
4740 (*canvasPtr->currentItemPtr->typePtr->configProc)(canvasPtr->interp,
4741 (Tk_Canvas) canvasPtr, canvasPtr->currentItemPtr, 0, (Tcl_Obj **) NULL,
4742 TK_CONFIG_ARGV_ONLY);
4743 EventuallyRedrawItem((Tk_Canvas) canvasPtr,
4744 canvasPtr->currentItemPtr);
4746 event = canvasPtr->pickEvent;
4747 event.type = EnterNotify;
4748 event.xcrossing.detail = NotifyAncestor;
4749 CanvasDoEvent(canvasPtr, &event);
4754 *----------------------------------------------------------------------
4756 * CanvasFindClosest --
4758 * Given x and y coordinates, find the topmost canvas item that
4759 * is "close" to the coordinates. Canvas items that are hidden
4760 * or disabled are ignored.
4763 * The return value is a pointer to the topmost item that is
4764 * close to (x,y), or NULL if no item is close.
4769 *----------------------------------------------------------------------
4773 CanvasFindClosest(canvasPtr, coords)
4774 TkCanvas *canvasPtr; /* Canvas widget to search. */
4775 double coords[2]; /* Desired x,y position in canvas,
4776 * not screen, coordinates.) */
4782 x1 = (int) (coords[0] - canvasPtr->closeEnough);
4783 y1 = (int) (coords[1] - canvasPtr->closeEnough);
4784 x2 = (int) (coords[0] + canvasPtr->closeEnough);
4785 y2 = (int) (coords[1] + canvasPtr->closeEnough);
4788 for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
4789 itemPtr = itemPtr->nextPtr) {
4790 if (itemPtr->state == TK_STATE_HIDDEN || itemPtr->state==TK_STATE_DISABLED ||
4791 (itemPtr->state == TK_STATE_NULL && (canvasPtr->canvas_state == TK_STATE_HIDDEN ||
4792 canvasPtr->canvas_state == TK_STATE_DISABLED))) {
4795 if ((itemPtr->x1 > x2) || (itemPtr->x2 < x1)
4796 || (itemPtr->y1 > y2) || (itemPtr->y2 < y1)) {
4799 if ((*itemPtr->typePtr->pointProc)((Tk_Canvas) canvasPtr,
4800 itemPtr, coords) <= canvasPtr->closeEnough) {
4808 *--------------------------------------------------------------
4812 * This procedure is called to invoke binding processing
4813 * for a new event that is associated with the current item
4820 * Depends on the bindings for the canvas. A binding script
4821 * could delete the canvas, so callers should protect themselves
4822 * with Tcl_Preserve and Tcl_Release.
4824 *--------------------------------------------------------------
4828 CanvasDoEvent(canvasPtr, eventPtr)
4829 TkCanvas *canvasPtr; /* Canvas widget in which event
4831 XEvent *eventPtr; /* Real or simulated X event that
4832 * is to be processed. */
4834 #define NUM_STATIC 3
4835 ClientData staticObjects[NUM_STATIC];
4836 ClientData *objectPtr;
4839 #ifndef USE_OLD_TAG_SEARCH
4840 TagSearchExpr *expr;
4842 #endif /* not USE_OLD_TAG_SEARCH */
4844 if (canvasPtr->bindingTable == NULL) {
4848 itemPtr = canvasPtr->currentItemPtr;
4849 if ((eventPtr->type == KeyPress) || (eventPtr->type == KeyRelease)) {
4850 itemPtr = canvasPtr->textInfo.focusItemPtr;
4852 if (itemPtr == NULL) {
4856 #ifdef USE_OLD_TAG_SEARCH
4858 * Set up an array with all the relevant objects for processing
4859 * this event. The relevant objects are (a) the event's item,
4860 * (b) the tags associated with the event's item, and (c) the
4861 * tag "all". If there are a lot of tags then malloc an array
4862 * to hold all of the objects.
4865 numObjects = itemPtr->numTags + 2;
4866 #else /* USE_OLD_TAG_SEARCH */
4868 * Set up an array with all the relevant objects for processing
4869 * this event. The relevant objects are:
4870 * (a) the event's item,
4871 * (b) the tags associated with the event's item,
4872 * (c) the expressions that are true for the event's item's tags, and
4873 * (d) the tag "all".
4875 * If there are a lot of tags then malloc an array to hold all of
4880 * flag and count all expressions that match item's tags
4883 expr = canvasPtr->bindTagExprs;
4886 expr->match = TagSearchEvalExpr(expr, itemPtr);
4893 numObjects = itemPtr->numTags + numExprs + 2;
4894 #endif /* not USE_OLD_TAG_SEARCH */
4895 if (numObjects <= NUM_STATIC) {
4896 objectPtr = staticObjects;
4898 objectPtr = (ClientData *) ckalloc((unsigned)
4899 (numObjects * sizeof(ClientData)));
4901 #ifdef USE_OLD_TAG_SEARCH
4902 objectPtr[0] = (ClientData) Tk_GetUid("all");
4903 #else /* USE_OLD_TAG_SEARCH */
4904 objectPtr[0] = (ClientData) allUid;
4905 #endif /* USE_OLD_TAG_SEARCH */
4906 for (i = itemPtr->numTags-1; i >= 0; i--) {
4907 objectPtr[i+1] = (ClientData) itemPtr->tagPtr[i];
4909 objectPtr[itemPtr->numTags+1] = (ClientData) itemPtr;
4910 #ifndef USE_OLD_TAG_SEARCH
4912 * copy uids of matching expressions into object array
4914 i = itemPtr->numTags+2;
4915 expr = canvasPtr->bindTagExprs;
4918 objectPtr[i++] = (int *) expr->uid;
4922 #endif /* not USE_OLD_TAG_SEARCH */
4925 * Invoke the binding system, then free up the object array if
4929 if (canvasPtr->tkwin != NULL) {
4930 Tk_BindEvent(canvasPtr->bindingTable, eventPtr, canvasPtr->tkwin,
4931 numObjects, objectPtr);
4933 if (objectPtr != staticObjects) {
4934 ckfree((char *) objectPtr);
4939 *----------------------------------------------------------------------
4941 * CanvasBlinkProc --
4943 * This procedure is called as a timer handler to blink the
4944 * insertion cursor off and on.
4950 * The cursor gets turned on or off, redisplay gets invoked,
4951 * and this procedure reschedules itself.
4953 *----------------------------------------------------------------------
4957 CanvasBlinkProc(clientData)
4958 ClientData clientData; /* Pointer to record describing entry. */
4960 TkCanvas *canvasPtr = (TkCanvas *) clientData;
4962 if (!canvasPtr->textInfo.gotFocus || (canvasPtr->insertOffTime == 0)) {
4965 if (canvasPtr->textInfo.cursorOn) {
4966 canvasPtr->textInfo.cursorOn = 0;
4967 canvasPtr->insertBlinkHandler = Tcl_CreateTimerHandler(
4968 canvasPtr->insertOffTime, CanvasBlinkProc,
4969 (ClientData) canvasPtr);
4971 canvasPtr->textInfo.cursorOn = 1;
4972 canvasPtr->insertBlinkHandler = Tcl_CreateTimerHandler(
4973 canvasPtr->insertOnTime, CanvasBlinkProc,
4974 (ClientData) canvasPtr);
4976 if (canvasPtr->textInfo.focusItemPtr != NULL) {
4977 EventuallyRedrawItem((Tk_Canvas) canvasPtr,
4978 canvasPtr->textInfo.focusItemPtr);
4983 *----------------------------------------------------------------------
4985 * CanvasFocusProc --
4987 * This procedure is called whenever a canvas gets or loses the
4988 * input focus. It's also called whenever the window is
4989 * reconfigured while it has the focus.
4995 * The cursor gets turned on or off.
4997 *----------------------------------------------------------------------
5001 CanvasFocusProc(canvasPtr, gotFocus)
5002 TkCanvas *canvasPtr; /* Canvas that just got or lost focus. */
5003 int gotFocus; /* 1 means window is getting focus, 0 means
5004 * it's losing it. */
5006 Tcl_DeleteTimerHandler(canvasPtr->insertBlinkHandler);
5008 canvasPtr->textInfo.gotFocus = 1;
5009 canvasPtr->textInfo.cursorOn = 1;
5010 if (canvasPtr->insertOffTime != 0) {
5011 canvasPtr->insertBlinkHandler = Tcl_CreateTimerHandler(
5012 canvasPtr->insertOffTime, CanvasBlinkProc,
5013 (ClientData) canvasPtr);
5016 canvasPtr->textInfo.gotFocus = 0;
5017 canvasPtr->textInfo.cursorOn = 0;
5018 canvasPtr->insertBlinkHandler = (Tcl_TimerToken) NULL;
5020 if (canvasPtr->textInfo.focusItemPtr != NULL) {
5021 EventuallyRedrawItem((Tk_Canvas) canvasPtr,
5022 canvasPtr->textInfo.focusItemPtr);
5024 if (canvasPtr->highlightWidth > 0) {
5025 canvasPtr->flags |= REDRAW_BORDERS;
5026 if (!(canvasPtr->flags & REDRAW_PENDING)) {
5027 Tcl_DoWhenIdle(DisplayCanvas, (ClientData) canvasPtr);
5028 canvasPtr->flags |= REDRAW_PENDING;
5034 *----------------------------------------------------------------------
5038 * Modify the selection by moving its un-anchored end. This could
5039 * make the selection either larger or smaller.
5045 * The selection changes.
5047 *----------------------------------------------------------------------
5051 CanvasSelectTo(canvasPtr, itemPtr, index)
5052 TkCanvas *canvasPtr; /* Information about widget. */
5053 Tk_Item *itemPtr; /* Item that is to hold selection. */
5054 int index; /* Index of element that is to become the
5055 * "other" end of the selection. */
5057 int oldFirst, oldLast;
5060 oldFirst = canvasPtr->textInfo.selectFirst;
5061 oldLast = canvasPtr->textInfo.selectLast;
5062 oldSelPtr = canvasPtr->textInfo.selItemPtr;
5065 * Grab the selection if we don't own it already.
5068 if (canvasPtr->textInfo.selItemPtr == NULL) {
5069 Tk_OwnSelection(canvasPtr->tkwin, XA_PRIMARY, CanvasLostSelection,
5070 (ClientData) canvasPtr);
5071 } else if (canvasPtr->textInfo.selItemPtr != itemPtr) {
5072 EventuallyRedrawItem((Tk_Canvas) canvasPtr,
5073 canvasPtr->textInfo.selItemPtr);
5075 canvasPtr->textInfo.selItemPtr = itemPtr;
5077 if (canvasPtr->textInfo.anchorItemPtr != itemPtr) {
5078 canvasPtr->textInfo.anchorItemPtr = itemPtr;
5079 canvasPtr->textInfo.selectAnchor = index;
5081 if (canvasPtr->textInfo.selectAnchor <= index) {
5082 canvasPtr->textInfo.selectFirst = canvasPtr->textInfo.selectAnchor;
5083 canvasPtr->textInfo.selectLast = index;
5085 canvasPtr->textInfo.selectFirst = index;
5086 canvasPtr->textInfo.selectLast = canvasPtr->textInfo.selectAnchor - 1;
5088 if ((canvasPtr->textInfo.selectFirst != oldFirst)
5089 || (canvasPtr->textInfo.selectLast != oldLast)
5090 || (itemPtr != oldSelPtr)) {
5091 EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
5096 *--------------------------------------------------------------
5098 * CanvasFetchSelection --
5100 * This procedure is invoked by Tk to return part or all of
5101 * the selection, when the selection is in a canvas widget.
5102 * This procedure always returns the selection as a STRING.
5105 * The return value is the number of non-NULL bytes stored
5106 * at buffer. Buffer is filled (or partially filled) with a
5107 * NULL-terminated string containing part or all of the selection,
5108 * as given by offset and maxBytes.
5113 *--------------------------------------------------------------
5117 CanvasFetchSelection(clientData, offset, buffer, maxBytes)
5118 ClientData clientData; /* Information about canvas widget. */
5119 int offset; /* Offset within selection of first
5120 * character to be returned. */
5121 char *buffer; /* Location in which to place
5123 int maxBytes; /* Maximum number of bytes to place
5124 * at buffer, not including terminating
5125 * NULL character. */
5127 TkCanvas *canvasPtr = (TkCanvas *) clientData;
5129 if (canvasPtr->textInfo.selItemPtr == NULL) {
5132 if (canvasPtr->textInfo.selItemPtr->typePtr->selectionProc == NULL) {
5135 return (*canvasPtr->textInfo.selItemPtr->typePtr->selectionProc)(
5136 (Tk_Canvas) canvasPtr, canvasPtr->textInfo.selItemPtr, offset,
5141 *----------------------------------------------------------------------
5143 * CanvasLostSelection --
5145 * This procedure is called back by Tk when the selection is
5146 * grabbed away from a canvas widget.
5152 * The existing selection is unhighlighted, and the window is
5153 * marked as not containing a selection.
5155 *----------------------------------------------------------------------
5159 CanvasLostSelection(clientData)
5160 ClientData clientData; /* Information about entry widget. */
5162 TkCanvas *canvasPtr = (TkCanvas *) clientData;
5164 if (canvasPtr->textInfo.selItemPtr != NULL) {
5165 EventuallyRedrawItem((Tk_Canvas) canvasPtr,
5166 canvasPtr->textInfo.selItemPtr);
5168 canvasPtr->textInfo.selItemPtr = NULL;
5172 *--------------------------------------------------------------
5176 * Given a coordinate and a grid spacing, this procedure
5177 * computes the location of the nearest grid line to the
5181 * The return value is the location of the grid line nearest
5187 *--------------------------------------------------------------
5191 GridAlign(coord, spacing)
5192 double coord; /* Coordinate to grid-align. */
5193 double spacing; /* Spacing between grid lines. If <= 0
5194 * then no alignment is done. */
5196 if (spacing <= 0.0) {
5200 return -((int) ((-coord)/spacing + 0.5)) * spacing;
5202 return ((int) (coord/spacing + 0.5)) * spacing;
5206 *----------------------------------------------------------------------
5208 * PrintScrollFractions --
5210 * Given the range that's visible in the window and the "100%
5211 * range" for what's in the canvas, print a string containing
5212 * the scroll fractions. This procedure is used for both x
5216 * The memory pointed to by string is modified to hold
5217 * two real numbers containing the scroll fractions (between
5218 * 0 and 1) corresponding to the other arguments.
5223 *----------------------------------------------------------------------
5227 PrintScrollFractions(screen1, screen2, object1, object2, string)
5228 int screen1; /* Lowest coordinate visible in the window. */
5229 int screen2; /* Highest coordinate visible in the window. */
5230 int object1; /* Lowest coordinate in the object. */
5231 int object2; /* Highest coordinate in the object. */
5232 char *string; /* Two real numbers get printed here. Must
5233 * have enough storage for two %g
5236 double range, f1, f2;
5238 range = object2 - object1;
5243 f1 = (screen1 - object1)/range;
5247 f2 = (screen2 - object1)/range;
5255 sprintf(string, "%g %g", f1, f2);
5259 *--------------------------------------------------------------
5261 * CanvasUpdateScrollbars --
5263 * This procedure is invoked whenever a canvas has changed in
5264 * a way that requires scrollbars to be redisplayed (e.g. the
5265 * view in the canvas has changed).
5271 * If there are scrollbars associated with the canvas, then
5272 * their scrolling commands are invoked to cause them to
5273 * redisplay. If errors occur, additional Tcl commands may
5274 * be invoked to process the errors.
5276 *--------------------------------------------------------------
5280 CanvasUpdateScrollbars(canvasPtr)
5281 TkCanvas *canvasPtr; /* Information about canvas. */
5286 int xOrigin, yOrigin, inset, width, height, scrollX1, scrollX2,
5288 char *xScrollCmd, *yScrollCmd;
5291 * Save all the relevant values from the canvasPtr, because it might be
5292 * deleted as part of either of the two calls to Tcl_VarEval below.
5295 interp = canvasPtr->interp;
5296 Tcl_Preserve((ClientData) interp);
5297 xScrollCmd = canvasPtr->xScrollCmd;
5298 if (xScrollCmd != (char *) NULL) {
5299 Tcl_Preserve((ClientData) xScrollCmd);
5301 yScrollCmd = canvasPtr->yScrollCmd;
5302 if (yScrollCmd != (char *) NULL) {
5303 Tcl_Preserve((ClientData) yScrollCmd);
5305 xOrigin = canvasPtr->xOrigin;
5306 yOrigin = canvasPtr->yOrigin;
5307 inset = canvasPtr->inset;
5308 width = Tk_Width(canvasPtr->tkwin);
5309 height = Tk_Height(canvasPtr->tkwin);
5310 scrollX1 = canvasPtr->scrollX1;
5311 scrollX2 = canvasPtr->scrollX2;
5312 scrollY1 = canvasPtr->scrollY1;
5313 scrollY2 = canvasPtr->scrollY2;
5314 canvasPtr->flags &= ~UPDATE_SCROLLBARS;
5315 if (canvasPtr->xScrollCmd != NULL) {
5316 PrintScrollFractions(xOrigin + inset, xOrigin + width - inset,
5317 scrollX1, scrollX2, buffer);
5318 result = Tcl_VarEval(interp, xScrollCmd, " ", buffer, (char *) NULL);
5319 if (result != TCL_OK) {
5320 Tcl_BackgroundError(interp);
5322 Tcl_ResetResult(interp);
5323 Tcl_Release((ClientData) xScrollCmd);
5326 if (yScrollCmd != NULL) {
5327 PrintScrollFractions(yOrigin + inset, yOrigin + height - inset,
5328 scrollY1, scrollY2, buffer);
5329 result = Tcl_VarEval(interp, yScrollCmd, " ", buffer, (char *) NULL);
5330 if (result != TCL_OK) {
5331 Tcl_BackgroundError(interp);
5333 Tcl_ResetResult(interp);
5334 Tcl_Release((ClientData) yScrollCmd);
5336 Tcl_Release((ClientData) interp);
5340 *--------------------------------------------------------------
5342 * CanvasSetOrigin --
5344 * This procedure is invoked to change the mapping between
5345 * canvas coordinates and screen coordinates in the canvas
5352 * The canvas will be redisplayed to reflect the change in
5353 * view. In addition, scrollbars will be updated if there
5356 *--------------------------------------------------------------
5360 CanvasSetOrigin(canvasPtr, xOrigin, yOrigin)
5361 TkCanvas *canvasPtr; /* Information about canvas. */
5362 int xOrigin; /* New X origin for canvas (canvas x-coord
5363 * corresponding to left edge of canvas
5365 int yOrigin; /* New Y origin for canvas (canvas y-coord
5366 * corresponding to top edge of canvas
5369 int left, right, top, bottom, delta;
5372 * If scroll increments have been set, round the window origin
5373 * to the nearest multiple of the increments. Remember, the
5374 * origin is the place just inside the borders, not the upper
5378 if (canvasPtr->xScrollIncrement > 0) {
5380 xOrigin += canvasPtr->xScrollIncrement/2;
5381 xOrigin -= (xOrigin + canvasPtr->inset)
5382 % canvasPtr->xScrollIncrement;
5384 xOrigin = (-xOrigin) + canvasPtr->xScrollIncrement/2;
5385 xOrigin = -(xOrigin - (xOrigin - canvasPtr->inset)
5386 % canvasPtr->xScrollIncrement);
5389 if (canvasPtr->yScrollIncrement > 0) {
5391 yOrigin += canvasPtr->yScrollIncrement/2;
5392 yOrigin -= (yOrigin + canvasPtr->inset)
5393 % canvasPtr->yScrollIncrement;
5395 yOrigin = (-yOrigin) + canvasPtr->yScrollIncrement/2;
5396 yOrigin = -(yOrigin - (yOrigin - canvasPtr->inset)
5397 % canvasPtr->yScrollIncrement);
5402 * Adjust the origin if necessary to keep as much as possible of the
5403 * canvas in the view. The variables left, right, etc. keep track of
5404 * how much extra space there is on each side of the view before it
5405 * will stick out past the scroll region. If one side sticks out past
5406 * the edge of the scroll region, adjust the view to bring that side
5407 * back to the edge of the scrollregion (but don't move it so much that
5408 * the other side sticks out now). If scroll increments are in effect,
5409 * be sure to adjust only by full increments.
5412 if ((canvasPtr->confine) && (canvasPtr->regionString != NULL)) {
5413 left = xOrigin + canvasPtr->inset - canvasPtr->scrollX1;
5414 right = canvasPtr->scrollX2
5415 - (xOrigin + Tk_Width(canvasPtr->tkwin) - canvasPtr->inset);
5416 top = yOrigin + canvasPtr->inset - canvasPtr->scrollY1;
5417 bottom = canvasPtr->scrollY2
5418 - (yOrigin + Tk_Height(canvasPtr->tkwin) - canvasPtr->inset);
5419 if ((left < 0) && (right > 0)) {
5420 delta = (right > -left) ? -left : right;
5421 if (canvasPtr->xScrollIncrement > 0) {
5422 delta -= delta % canvasPtr->xScrollIncrement;
5425 } else if ((right < 0) && (left > 0)) {
5426 delta = (left > -right) ? -right : left;
5427 if (canvasPtr->xScrollIncrement > 0) {
5428 delta -= delta % canvasPtr->xScrollIncrement;
5432 if ((top < 0) && (bottom > 0)) {
5433 delta = (bottom > -top) ? -top : bottom;
5434 if (canvasPtr->yScrollIncrement > 0) {
5435 delta -= delta % canvasPtr->yScrollIncrement;
5438 } else if ((bottom < 0) && (top > 0)) {
5439 delta = (top > -bottom) ? -bottom : top;
5440 if (canvasPtr->yScrollIncrement > 0) {
5441 delta -= delta % canvasPtr->yScrollIncrement;
5447 if ((xOrigin == canvasPtr->xOrigin) && (yOrigin == canvasPtr->yOrigin)) {
5452 * Tricky point: must redisplay not only everything that's visible
5453 * in the window's final configuration, but also everything that was
5454 * visible in the initial configuration. This is needed because some
5455 * item types, like windows, need to know when they move off-screen
5456 * so they can explicitly undisplay themselves.
5459 Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
5460 canvasPtr->xOrigin, canvasPtr->yOrigin,
5461 canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin),
5462 canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin));
5463 canvasPtr->xOrigin = xOrigin;
5464 canvasPtr->yOrigin = yOrigin;
5465 canvasPtr->flags |= UPDATE_SCROLLBARS;
5466 Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
5467 canvasPtr->xOrigin, canvasPtr->yOrigin,
5468 canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin),
5469 canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin));
5473 *----------------------------------------------------------------------
5475 * GetStringsFromObjs
5478 * Converts object list into string list.
5481 * Memory is allocated for the argv array, which must
5482 * be freed using ckfree() when no longer needed.
5484 *----------------------------------------------------------------------
5488 GetStringsFromObjs(argc, objv)
5490 Tcl_Obj *CONST objv[];
5497 argv = (char **) ckalloc((argc+1) * sizeof(char *));
5498 for (i = 0; i < argc; i++) {
5499 argv[i]=Tcl_GetStringFromObj(objv[i], (int *) NULL);
5506 *--------------------------------------------------------------
5508 * Tk_CanvasPsColor --
5510 * This procedure is called by individual canvas items when
5511 * they want to set a color value for output. Given information
5512 * about an X color, this procedure will generate Postscript
5513 * commands to set up an appropriate color in Postscript.
5516 * Returns a standard Tcl return value. If an error occurs
5517 * then an error message will be left in interp->result.
5518 * If no error occurs, then additional Postscript will be
5519 * appended to interp->result.
5524 *--------------------------------------------------------------
5528 Tk_CanvasPsColor(interp, canvas, colorPtr)
5529 Tcl_Interp *interp; /* Interpreter for returning Postscript
5530 * or error message. */
5531 Tk_Canvas canvas; /* Information about canvas. */
5532 XColor *colorPtr; /* Information about color. */
5534 return Tk_PostscriptColor(interp, ((TkCanvas *) canvas)->psInfo,
5539 *--------------------------------------------------------------
5541 * Tk_CanvasPsFont --
5543 * This procedure is called by individual canvas items when
5544 * they want to output text. Given information about an X
5545 * font, this procedure will generate Postscript commands
5546 * to set up an appropriate font in Postscript.
5549 * Returns a standard Tcl return value. If an error occurs
5550 * then an error message will be left in interp->result.
5551 * If no error occurs, then additional Postscript will be
5552 * appended to the interp->result.
5555 * The Postscript font name is entered into psInfoPtr->fontTable
5556 * if it wasn't already there.
5558 *--------------------------------------------------------------
5562 Tk_CanvasPsFont(interp, canvas, tkfont)
5563 Tcl_Interp *interp; /* Interpreter for returning Postscript
5564 * or error message. */
5565 Tk_Canvas canvas; /* Information about canvas. */
5566 Tk_Font tkfont; /* Information about font in which text
5567 * is to be printed. */
5569 return Tk_PostscriptFont(interp, ((TkCanvas *) canvas)->psInfo, tkfont);
5573 *--------------------------------------------------------------
5575 * Tk_CanvasPsBitmap --
5577 * This procedure is called to output the contents of a
5578 * sub-region of a bitmap in proper image data format for
5579 * Postscript (i.e. data between angle brackets, one bit
5583 * Returns a standard Tcl return value. If an error occurs
5584 * then an error message will be left in interp->result.
5585 * If no error occurs, then additional Postscript will be
5586 * appended to interp->result.
5591 *--------------------------------------------------------------
5595 Tk_CanvasPsBitmap(interp, canvas, bitmap, startX, startY, width, height)
5596 Tcl_Interp *interp; /* Interpreter for returning Postscript
5597 * or error message. */
5598 Tk_Canvas canvas; /* Information about canvas. */
5599 Pixmap bitmap; /* Bitmap for which to generate
5601 int startX, startY; /* Coordinates of upper-left corner
5602 * of rectangular region to output. */
5603 int width, height; /* Height of rectangular region. */
5605 return Tk_PostscriptBitmap(interp, ((TkCanvas *) canvas)->tkwin,
5606 ((TkCanvas *) canvas)->psInfo, bitmap, startX, startY,
5611 *--------------------------------------------------------------
5613 * Tk_CanvasPsStipple --
5615 * This procedure is called by individual canvas items when
5616 * they have created a path that they'd like to be filled with
5617 * a stipple pattern. Given information about an X bitmap,
5618 * this procedure will generate Postscript commands to fill
5619 * the current clip region using a stipple pattern defined by the
5623 * Returns a standard Tcl return value. If an error occurs
5624 * then an error message will be left in interp->result.
5625 * If no error occurs, then additional Postscript will be
5626 * appended to interp->result.
5631 *--------------------------------------------------------------
5635 Tk_CanvasPsStipple(interp, canvas, bitmap)
5636 Tcl_Interp *interp; /* Interpreter for returning Postscript
5637 * or error message. */
5638 Tk_Canvas canvas; /* Information about canvas. */
5639 Pixmap bitmap; /* Bitmap to use for stippling. */
5641 return Tk_PostscriptStipple(interp, ((TkCanvas *) canvas)->tkwin,
5642 ((TkCanvas *) canvas)->psInfo, bitmap);
5646 *--------------------------------------------------------------
5650 * Given a y-coordinate in canvas coordinates, this procedure
5651 * returns a y-coordinate to use for Postscript output.
5654 * Returns the Postscript coordinate that corresponds to
5660 *--------------------------------------------------------------
5664 Tk_CanvasPsY(canvas, y)
5665 Tk_Canvas canvas; /* Token for canvas on whose behalf
5666 * Postscript is being generated. */
5667 double y; /* Y-coordinate in canvas coords. */
5669 return Tk_PostscriptY(y, ((TkCanvas *) canvas)->psInfo);
5673 *--------------------------------------------------------------
5675 * Tk_CanvasPsPath --
5677 * Given an array of points for a path, generate Postscript
5678 * commands to create the path.
5681 * Postscript commands get appended to what's in interp->result.
5686 *--------------------------------------------------------------
5690 Tk_CanvasPsPath(interp, canvas, coordPtr, numPoints)
5691 Tcl_Interp *interp; /* Put generated Postscript in this
5692 * interpreter's result field. */
5693 Tk_Canvas canvas; /* Canvas on whose behalf Postscript
5694 * is being generated. */
5695 double *coordPtr; /* Pointer to first in array of
5696 * 2*numPoints coordinates giving
5697 * points for path. */
5698 int numPoints; /* Number of points at *coordPtr. */
5700 Tk_PostscriptPath(interp, ((TkCanvas *) canvas)->psInfo,
5701 coordPtr, numPoints);