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 CONST 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 Tcl_Obj * ScrollFractions _ANSI_ARGS_((int screen1,
303 int screen2, int object1, int object2));
304 #ifdef USE_OLD_TAG_SEARCH
305 static void RelinkItems _ANSI_ARGS_((TkCanvas *canvasPtr,
306 Tcl_Obj *tag, Tk_Item *prevPtr));
307 static Tk_Item * StartTagSearch _ANSI_ARGS_((TkCanvas *canvasPtr,
308 Tcl_Obj *tag, TagSearch *searchPtr));
309 #else /* USE_OLD_TAG_SEARCH */
310 static int RelinkItems _ANSI_ARGS_((TkCanvas *canvasPtr,
311 Tcl_Obj *tag, Tk_Item *prevPtr,
312 TagSearch **searchPtrPtr));
313 static void TagSearchExprInit _ANSI_ARGS_ ((
314 TagSearchExpr **exprPtrPtr));
315 static void TagSearchExprDestroy _ANSI_ARGS_((TagSearchExpr *expr));
316 static void TagSearchDestroy _ANSI_ARGS_((TagSearch *searchPtr));
317 static int TagSearchScan _ANSI_ARGS_((TkCanvas *canvasPtr,
318 Tcl_Obj *tag, TagSearch **searchPtrPtr));
319 static int TagSearchScanExpr _ANSI_ARGS_((Tcl_Interp *interp,
320 TagSearch *searchPtr, TagSearchExpr *expr));
321 static int TagSearchEvalExpr _ANSI_ARGS_((TagSearchExpr *expr,
323 static Tk_Item * TagSearchFirst _ANSI_ARGS_((TagSearch *searchPtr));
324 static Tk_Item * TagSearchNext _ANSI_ARGS_((TagSearch *searchPtr));
325 #endif /* USE_OLD_TAG_SEARCH */
328 * The structure below defines canvas class behavior by means of procedures
329 * that can be invoked from generic window code.
332 static Tk_ClassProcs canvasClass = {
333 sizeof(Tk_ClassProcs), /* size */
334 CanvasWorldChanged, /* worldChangedProc */
339 *--------------------------------------------------------------
343 * This procedure is invoked to process the "canvas" Tcl
344 * command. See the user documentation for details on what
348 * A standard Tcl result.
351 * See the user documentation.
353 *--------------------------------------------------------------
357 Tk_CanvasObjCmd(clientData, interp, argc, argv)
358 ClientData clientData; /* Main window associated with
360 Tcl_Interp *interp; /* Current interpreter. */
361 int argc; /* Number of arguments. */
362 Tcl_Obj *CONST argv[]; /* Argument objects. */
364 Tk_Window tkwin = (Tk_Window) clientData;
368 if (typeList == NULL) {
373 Tcl_WrongNumArgs(interp, 1, argv, "pathName ?options?");
377 new = Tk_CreateWindowFromPath(interp, tkwin,
378 Tcl_GetString(argv[1]), (char *) NULL);
384 * Initialize fields that won't be initialized by ConfigureCanvas,
385 * or which ConfigureCanvas expects to have reasonable values
386 * (e.g. resource pointers).
389 canvasPtr = (TkCanvas *) ckalloc(sizeof(TkCanvas));
390 canvasPtr->tkwin = new;
391 canvasPtr->display = Tk_Display(new);
392 canvasPtr->interp = interp;
393 canvasPtr->widgetCmd = Tcl_CreateObjCommand(interp,
394 Tk_PathName(canvasPtr->tkwin), CanvasWidgetCmd,
395 (ClientData) canvasPtr, CanvasCmdDeletedProc);
396 canvasPtr->firstItemPtr = NULL;
397 canvasPtr->lastItemPtr = NULL;
398 canvasPtr->borderWidth = 0;
399 canvasPtr->bgBorder = NULL;
400 canvasPtr->relief = TK_RELIEF_FLAT;
401 canvasPtr->highlightWidth = 0;
402 canvasPtr->highlightBgColorPtr = NULL;
403 canvasPtr->highlightColorPtr = NULL;
404 canvasPtr->inset = 0;
405 canvasPtr->pixmapGC = None;
406 canvasPtr->width = None;
407 canvasPtr->height = None;
408 canvasPtr->confine = 0;
409 canvasPtr->textInfo.selBorder = NULL;
410 canvasPtr->textInfo.selBorderWidth = 0;
411 canvasPtr->textInfo.selFgColorPtr = NULL;
412 canvasPtr->textInfo.selItemPtr = NULL;
413 canvasPtr->textInfo.selectFirst = -1;
414 canvasPtr->textInfo.selectLast = -1;
415 canvasPtr->textInfo.anchorItemPtr = NULL;
416 canvasPtr->textInfo.selectAnchor = 0;
417 canvasPtr->textInfo.insertBorder = NULL;
418 canvasPtr->textInfo.insertWidth = 0;
419 canvasPtr->textInfo.insertBorderWidth = 0;
420 canvasPtr->textInfo.focusItemPtr = NULL;
421 canvasPtr->textInfo.gotFocus = 0;
422 canvasPtr->textInfo.cursorOn = 0;
423 canvasPtr->insertOnTime = 0;
424 canvasPtr->insertOffTime = 0;
425 canvasPtr->insertBlinkHandler = (Tcl_TimerToken) NULL;
426 canvasPtr->xOrigin = canvasPtr->yOrigin = 0;
427 canvasPtr->drawableXOrigin = canvasPtr->drawableYOrigin = 0;
428 canvasPtr->bindingTable = NULL;
429 canvasPtr->currentItemPtr = NULL;
430 canvasPtr->newCurrentPtr = NULL;
431 canvasPtr->closeEnough = 0.0;
432 canvasPtr->pickEvent.type = LeaveNotify;
433 canvasPtr->pickEvent.xcrossing.x = 0;
434 canvasPtr->pickEvent.xcrossing.y = 0;
435 canvasPtr->state = 0;
436 canvasPtr->xScrollCmd = NULL;
437 canvasPtr->yScrollCmd = NULL;
438 canvasPtr->scrollX1 = 0;
439 canvasPtr->scrollY1 = 0;
440 canvasPtr->scrollX2 = 0;
441 canvasPtr->scrollY2 = 0;
442 canvasPtr->regionString = NULL;
443 canvasPtr->xScrollIncrement = 0;
444 canvasPtr->yScrollIncrement = 0;
445 canvasPtr->scanX = 0;
446 canvasPtr->scanXOrigin = 0;
447 canvasPtr->scanY = 0;
448 canvasPtr->scanYOrigin = 0;
449 canvasPtr->hotPtr = NULL;
450 canvasPtr->hotPrevPtr = NULL;
451 canvasPtr->cursor = None;
452 canvasPtr->takeFocus = NULL;
453 canvasPtr->pixelsPerMM = WidthOfScreen(Tk_Screen(new));
454 canvasPtr->pixelsPerMM /= WidthMMOfScreen(Tk_Screen(new));
455 canvasPtr->flags = 0;
456 canvasPtr->nextId = 1;
457 canvasPtr->psInfo = NULL;
458 canvasPtr->canvas_state = TK_STATE_NORMAL;
459 canvasPtr->tsoffset.flags = 0;
460 canvasPtr->tsoffset.xoffset = 0;
461 canvasPtr->tsoffset.yoffset = 0;
462 #ifndef USE_OLD_TAG_SEARCH
463 canvasPtr->bindTagExprs = NULL;
465 Tcl_InitHashTable(&canvasPtr->idTable, TCL_ONE_WORD_KEYS);
467 Tk_SetClass(canvasPtr->tkwin, "Canvas");
468 Tk_SetClassProcs(canvasPtr->tkwin, &canvasClass, (ClientData) canvasPtr);
469 Tk_CreateEventHandler(canvasPtr->tkwin,
470 ExposureMask|StructureNotifyMask|FocusChangeMask,
471 CanvasEventProc, (ClientData) canvasPtr);
472 Tk_CreateEventHandler(canvasPtr->tkwin, KeyPressMask|KeyReleaseMask
473 |ButtonPressMask|ButtonReleaseMask|EnterWindowMask
474 |LeaveWindowMask|PointerMotionMask|VirtualEventMask,
475 CanvasBindProc, (ClientData) canvasPtr);
476 Tk_CreateSelHandler(canvasPtr->tkwin, XA_PRIMARY, XA_STRING,
477 CanvasFetchSelection, (ClientData) canvasPtr, XA_STRING);
478 if (ConfigureCanvas(interp, canvasPtr, argc-2, argv+2, 0) != TCL_OK) {
482 Tcl_SetResult(interp, Tk_PathName(canvasPtr->tkwin), TCL_STATIC);
486 Tk_DestroyWindow(canvasPtr->tkwin);
491 *--------------------------------------------------------------
495 * This procedure is invoked to process the Tcl command
496 * that corresponds to a widget managed by this module.
497 * See the user documentation for details on what it does.
500 * A standard Tcl result.
503 * See the user documentation.
505 *--------------------------------------------------------------
509 CanvasWidgetCmd(clientData, interp, argc, argv)
510 ClientData clientData; /* Information about canvas
512 Tcl_Interp *interp; /* Current interpreter. */
513 int argc; /* Number of arguments. */
514 Tcl_Obj *CONST argv[]; /* Argument objects. */
516 TkCanvas *canvasPtr = (TkCanvas *) clientData;
519 Tk_Item *itemPtr = NULL; /* Initialization needed only to
520 * prevent compiler warning. */
521 #ifdef USE_OLD_TAG_SEARCH
523 #else /* USE_OLD_TAG_SEARCH */
524 TagSearch *searchPtr = NULL; /* Allocated by first TagSearchScan
525 * Freed by TagSearchDestroy */
526 #endif /* USE_OLD_TAG_SEARCH */
529 static CONST char *optionStrings[] = {
530 "addtag", "bbox", "bind", "canvasx",
531 "canvasy", "cget", "configure", "coords",
532 "create", "dchars", "delete", "dtag",
533 "find", "focus", "gettags", "icursor",
534 "index", "insert", "itemcget", "itemconfigure",
535 "lower", "move", "postscript", "raise",
536 "scale", "scan", "select", "type",
541 CANV_ADDTAG, CANV_BBOX, CANV_BIND, CANV_CANVASX,
542 CANV_CANVASY, CANV_CGET, CANV_CONFIGURE, CANV_COORDS,
543 CANV_CREATE, CANV_DCHARS, CANV_DELETE, CANV_DTAG,
544 CANV_FIND, CANV_FOCUS, CANV_GETTAGS, CANV_ICURSOR,
545 CANV_INDEX, CANV_INSERT, CANV_ITEMCGET, CANV_ITEMCONFIGURE,
546 CANV_LOWER, CANV_MOVE, CANV_POSTSCRIPT,CANV_RAISE,
547 CANV_SCALE, CANV_SCAN, CANV_SELECT, CANV_TYPE,
548 CANV_XVIEW, CANV_YVIEW
552 Tcl_WrongNumArgs(interp, 1, argv, "option ?arg arg ...?");
555 if (Tcl_GetIndexFromObj(interp, argv[1], optionStrings, "option", 0,
559 Tcl_Preserve((ClientData) canvasPtr);
562 switch ((enum options) index) {
565 Tcl_WrongNumArgs(interp, 2, argv, "tag searchCommand ?arg arg ...?");
569 #ifdef USE_OLD_TAG_SEARCH
570 result = FindItems(interp, canvasPtr, argc, argv, argv[2], 3);
571 #else /* USE_OLD_TAG_SEARCH */
572 result = FindItems(interp, canvasPtr, argc, argv, argv[2], 3, &searchPtr);
573 #endif /* USE_OLD_TAG_SEARCH */
579 int x1 = 0, y1 = 0, x2 = 0, y2 = 0; /* Initializations needed
580 * only to prevent compiler
584 Tcl_WrongNumArgs(interp, 2, argv, "tagOrId ?tagOrId ...?");
589 for (i = 2; i < argc; i++) {
590 #ifdef USE_OLD_TAG_SEARCH
591 for (itemPtr = StartTagSearch(canvasPtr, argv[i], &search);
592 itemPtr != NULL; itemPtr = NextItem(&search)) {
593 #else /* USE_OLD_TAG_SEARCH */
594 if ((result = TagSearchScan(canvasPtr, argv[i], &searchPtr)) != TCL_OK) {
597 for (itemPtr = TagSearchFirst(searchPtr);
598 itemPtr != NULL; itemPtr = TagSearchNext(searchPtr)) {
599 #endif /* USE_OLD_TAG_SEARCH */
601 if ((itemPtr->x1 >= itemPtr->x2)
602 || (itemPtr->y1 >= itemPtr->y2)) {
612 if (itemPtr->x1 < x1) {
615 if (itemPtr->y1 < y1) {
618 if (itemPtr->x2 > x2) {
621 if (itemPtr->y2 > y2) {
628 char buf[TCL_INTEGER_SPACE * 4];
630 sprintf(buf, "%d %d %d %d", x1, y1, x2, y2);
631 Tcl_SetResult(interp, buf, TCL_VOLATILE);
638 if ((argc < 3) || (argc > 5)) {
639 Tcl_WrongNumArgs(interp, 2, argv, "tagOrId ?sequence? ?command?");
645 * Figure out what object to use for the binding (individual
650 #ifdef USE_OLD_TAG_SEARCH
651 if (isdigit(UCHAR(Tcl_GetString(argv[2])[0]))) {
654 Tcl_HashEntry *entryPtr;
656 id = strtoul(Tcl_GetString(argv[2]), &end, 0);
660 entryPtr = Tcl_FindHashEntry(&canvasPtr->idTable, (char *) id);
661 if (entryPtr != NULL) {
662 itemPtr = (Tk_Item *) Tcl_GetHashValue(entryPtr);
663 object = (ClientData) itemPtr;
667 Tcl_AppendResult(interp, "item \"", Tcl_GetString(argv[2]),
668 "\" doesn't exist", (char *) NULL);
674 object = (ClientData) Tk_GetUid(Tcl_GetString(argv[2]));
676 #else /* USE_OLD_TAG_SEARCH */
677 if ((result = TagSearchScan(canvasPtr, argv[2], &searchPtr)) != TCL_OK) {
680 if (searchPtr->type == 1) {
681 Tcl_HashEntry *entryPtr;
683 entryPtr = Tcl_FindHashEntry(&canvasPtr->idTable, (char *) searchPtr->id);
684 if (entryPtr != NULL) {
685 itemPtr = (Tk_Item *) Tcl_GetHashValue(entryPtr);
686 object = (ClientData) itemPtr;
690 Tcl_AppendResult(interp, "item \"", Tcl_GetString(argv[2]),
691 "\" doesn't exist", (char *) NULL);
696 object = (ClientData) searchPtr->expr->uid;
698 #endif /* USE_OLD_TAG_SEARCH */
701 * Make a binding table if the canvas doesn't already have
705 if (canvasPtr->bindingTable == NULL) {
706 canvasPtr->bindingTable = Tk_CreateBindingTable(interp);
712 char* argv4 = Tcl_GetStringFromObj(argv[4],NULL);
715 result = Tk_DeleteBinding(interp, canvasPtr->bindingTable,
716 object, Tcl_GetStringFromObj(argv[3], NULL));
719 #ifndef USE_OLD_TAG_SEARCH
720 if (searchPtr->type == 4) {
722 * if new tag expression, then insert in linked list
724 TagSearchExpr *expr, **lastPtr;
726 lastPtr = &(canvasPtr->bindTagExprs);
727 while ((expr = *lastPtr) != NULL) {
728 if (expr->uid == searchPtr->expr->uid) {
731 lastPtr = &(expr->next);
735 * transfer ownership of expr to bindTagExprs list
737 *lastPtr = searchPtr->expr;
738 searchPtr->expr->next = NULL;
741 * flag in TagSearch that expr has changed ownership
742 * so that TagSearchDestroy doesn't try to free it
744 searchPtr->expr = NULL;
747 #endif /* not USE_OLD_TAG_SEARCH */
748 if (argv4[0] == '+') {
752 mask = Tk_CreateBinding(interp, canvasPtr->bindingTable,
753 object, Tcl_GetStringFromObj(argv[3],NULL), argv4, append);
758 if (mask & (unsigned) ~(ButtonMotionMask|Button1MotionMask
759 |Button2MotionMask|Button3MotionMask|Button4MotionMask
760 |Button5MotionMask|ButtonPressMask|ButtonReleaseMask
761 |EnterWindowMask|LeaveWindowMask|KeyPressMask
762 |KeyReleaseMask|PointerMotionMask|VirtualEventMask)) {
763 Tk_DeleteBinding(interp, canvasPtr->bindingTable,
764 object, Tcl_GetStringFromObj(argv[3], NULL));
765 Tcl_ResetResult(interp);
766 Tcl_AppendResult(interp, "requested illegal events; ",
767 "only key, button, motion, enter, leave, and virtual ",
768 "events may be used", (char *) NULL);
772 } else if (argc == 4) {
775 command = Tk_GetBinding(interp, canvasPtr->bindingTable,
776 object, Tcl_GetStringFromObj(argv[3], NULL));
777 if (command == NULL) {
780 string = Tcl_GetStringResult(interp);
782 * Ignore missing binding errors. This is a special hack
783 * that relies on the error message returned by FindSequence
787 if (string[0] != '\0') {
791 Tcl_ResetResult(interp);
794 Tcl_SetResult(interp, (char *) command, TCL_STATIC);
797 Tk_GetAllBindings(interp, canvasPtr->bindingTable, object);
804 char buf[TCL_DOUBLE_SPACE];
806 if ((argc < 3) || (argc > 4)) {
807 Tcl_WrongNumArgs(interp, 2, argv, "screenx ?gridspacing?");
811 if (Tk_GetPixelsFromObj(interp, canvasPtr->tkwin, argv[2], &x) != TCL_OK) {
816 if (Tk_CanvasGetCoordFromObj(interp, (Tk_Canvas) canvasPtr, argv[3],
824 x += canvasPtr->xOrigin;
825 Tcl_PrintDouble(interp, GridAlign((double) x, grid), buf);
826 Tcl_SetResult(interp, buf, TCL_VOLATILE);
832 char buf[TCL_DOUBLE_SPACE];
834 if ((argc < 3) || (argc > 4)) {
835 Tcl_WrongNumArgs(interp, 2, argv, "screeny ?gridspacing?");
839 if (Tk_GetPixelsFromObj(interp, canvasPtr->tkwin, argv[2], &y) != TCL_OK) {
844 if (Tk_CanvasGetCoordFromObj(interp, (Tk_Canvas) canvasPtr,
845 argv[3], &grid) != TCL_OK) {
852 y += canvasPtr->yOrigin;
853 Tcl_PrintDouble(interp, GridAlign((double) y, grid), buf);
854 Tcl_SetResult(interp, buf, TCL_VOLATILE);
859 Tcl_WrongNumArgs(interp, 2, argv, "option");
863 result = Tk_ConfigureValue(interp, canvasPtr->tkwin, configSpecs,
864 (char *) canvasPtr, Tcl_GetString(argv[2]), 0);
867 case CANV_CONFIGURE: {
869 result = Tk_ConfigureInfo(interp, canvasPtr->tkwin, configSpecs,
870 (char *) canvasPtr, (char *) NULL, 0);
871 } else if (argc == 3) {
872 result = Tk_ConfigureInfo(interp, canvasPtr->tkwin, configSpecs,
873 (char *) canvasPtr, Tcl_GetString(argv[2]), 0);
875 result = ConfigureCanvas(interp, canvasPtr, argc-2, argv+2,
876 TK_CONFIG_ARGV_ONLY);
882 Tcl_WrongNumArgs(interp, 2, argv, "tagOrId ?x y x y ...?");
886 #ifdef USE_OLD_TAG_SEARCH
887 itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
888 #else /* USE_OLD_TAG_SEARCH */
889 if ((result = TagSearchScan(canvasPtr, argv[2], &searchPtr)) != TCL_OK) {
892 itemPtr = TagSearchFirst(searchPtr);
893 #endif /* USE_OLD_TAG_SEARCH */
894 if (itemPtr != NULL) {
896 EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
898 if (itemPtr->typePtr->coordProc != NULL) {
899 if (itemPtr->typePtr->alwaysRedraw & TK_CONFIG_OBJS) {
900 result = (*itemPtr->typePtr->coordProc)(interp,
901 (Tk_Canvas) canvasPtr, itemPtr, argc-3, argv+3);
903 CONST char **args = GetStringsFromObjs(argc-3, argv+3);
904 result = (*itemPtr->typePtr->coordProc)(interp,
905 (Tk_Canvas) canvasPtr, itemPtr, argc-3, (Tcl_Obj **) args);
906 if (args) ckfree((char *) args);
910 EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
916 Tk_ItemType *typePtr;
917 Tk_ItemType *matchPtr = NULL;
919 char buf[TCL_INTEGER_SPACE];
921 Tcl_HashEntry *entryPtr;
925 Tcl_WrongNumArgs(interp, 2, argv, "type ?arg arg ...?");
929 arg = Tcl_GetStringFromObj(argv[2], (int *) &length);
931 for (typePtr = typeList; typePtr != NULL; typePtr = typePtr->nextPtr) {
932 if ((c == typePtr->name[0])
933 && (strncmp(arg, typePtr->name, length) == 0)) {
934 if (matchPtr != NULL) {
936 Tcl_AppendResult(interp,
937 "unknown or ambiguous item type \"",
938 arg, "\"", (char *) NULL);
945 if (matchPtr == NULL) {
949 itemPtr = (Tk_Item *) ckalloc((unsigned) typePtr->itemSize);
950 itemPtr->id = canvasPtr->nextId;
952 itemPtr->tagPtr = itemPtr->staticTagSpace;
953 itemPtr->tagSpace = TK_TAG_SPACE;
954 itemPtr->numTags = 0;
955 itemPtr->typePtr = typePtr;
956 itemPtr->state = TK_STATE_NULL;
957 itemPtr->redraw_flags = 0;
958 if (itemPtr->typePtr->alwaysRedraw & TK_CONFIG_OBJS) {
959 result = (*typePtr->createProc)(interp, (Tk_Canvas) canvasPtr,
960 itemPtr, argc-3, argv+3);
962 CONST char **args = GetStringsFromObjs(argc-3, argv+3);
963 result = (*typePtr->createProc)(interp, (Tk_Canvas) canvasPtr,
964 itemPtr, argc-3, (Tcl_Obj **) args);
965 if (args) ckfree((char *) args);
967 if (result != TCL_OK) {
968 ckfree((char *) itemPtr);
972 itemPtr->nextPtr = NULL;
973 entryPtr = Tcl_CreateHashEntry(&canvasPtr->idTable,
974 (char *) itemPtr->id, &isNew);
975 Tcl_SetHashValue(entryPtr, itemPtr);
976 itemPtr->prevPtr = canvasPtr->lastItemPtr;
977 canvasPtr->hotPtr = itemPtr;
978 canvasPtr->hotPrevPtr = canvasPtr->lastItemPtr;
979 if (canvasPtr->lastItemPtr == NULL) {
980 canvasPtr->firstItemPtr = itemPtr;
982 canvasPtr->lastItemPtr->nextPtr = itemPtr;
984 canvasPtr->lastItemPtr = itemPtr;
985 itemPtr->redraw_flags |= FORCE_REDRAW;
986 EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
987 canvasPtr->flags |= REPICK_NEEDED;
988 sprintf(buf, "%d", itemPtr->id);
989 Tcl_SetResult(interp, buf, TCL_VOLATILE);
996 if ((argc != 4) && (argc != 5)) {
997 Tcl_WrongNumArgs(interp, 2, argv, "tagOrId first ?last?");
1001 #ifdef USE_OLD_TAG_SEARCH
1002 for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
1003 itemPtr != NULL; itemPtr = NextItem(&search)) {
1004 #else /* USE_OLD_TAG_SEARCH */
1005 if ((result = TagSearchScan(canvasPtr, argv[2], &searchPtr)) != TCL_OK) {
1008 for (itemPtr = TagSearchFirst(searchPtr);
1009 itemPtr != NULL; itemPtr = TagSearchNext(searchPtr)) {
1010 #endif /* USE_OLD_TAG_SEARCH */
1011 if ((itemPtr->typePtr->indexProc == NULL)
1012 || (itemPtr->typePtr->dCharsProc == NULL)) {
1015 if (itemPtr->typePtr->alwaysRedraw & TK_CONFIG_OBJS) {
1016 result = itemPtr->typePtr->indexProc(interp, (Tk_Canvas) canvasPtr,
1017 itemPtr, (char *) argv[3], &first);
1019 result = itemPtr->typePtr->indexProc(interp, (Tk_Canvas) canvasPtr,
1020 itemPtr, Tcl_GetStringFromObj(argv[3], NULL), &first);
1022 if (result != TCL_OK) {
1026 if (itemPtr->typePtr->alwaysRedraw & TK_CONFIG_OBJS) {
1027 result = itemPtr->typePtr->indexProc(interp, (Tk_Canvas) canvasPtr,
1028 itemPtr, (char *) argv[4], &last);
1030 result = itemPtr->typePtr->indexProc(interp, (Tk_Canvas) canvasPtr,
1031 itemPtr, Tcl_GetStringFromObj(argv[4], NULL), &last);
1033 if (result != TCL_OK) {
1041 * Redraw both item's old and new areas: it's possible
1042 * that a delete could result in a new area larger than
1043 * the old area. Except if the insertProc sets the
1044 * TK_ITEM_DONT_REDRAW flag, nothing more needs to be done.
1047 x1 = itemPtr->x1; y1 = itemPtr->y1;
1048 x2 = itemPtr->x2; y2 = itemPtr->y2;
1049 itemPtr->redraw_flags &= ~TK_ITEM_DONT_REDRAW;
1050 (*itemPtr->typePtr->dCharsProc)((Tk_Canvas) canvasPtr,
1051 itemPtr, first, last);
1052 if (!(itemPtr->redraw_flags & TK_ITEM_DONT_REDRAW)) {
1053 Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
1055 EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
1057 itemPtr->redraw_flags &= ~TK_ITEM_DONT_REDRAW;
1063 Tcl_HashEntry *entryPtr;
1065 for (i = 2; i < argc; i++) {
1066 #ifdef USE_OLD_TAG_SEARCH
1067 for (itemPtr = StartTagSearch(canvasPtr, argv[i], &search);
1068 itemPtr != NULL; itemPtr = NextItem(&search)) {
1069 #else /* USE_OLD_TAG_SEARCH */
1070 if ((result = TagSearchScan(canvasPtr, argv[i], &searchPtr)) != TCL_OK) {
1073 for (itemPtr = TagSearchFirst(searchPtr);
1074 itemPtr != NULL; itemPtr = TagSearchNext(searchPtr)) {
1075 #endif /* USE_OLD_TAG_SEARCH */
1076 EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
1077 if (canvasPtr->bindingTable != NULL) {
1078 Tk_DeleteAllBindings(canvasPtr->bindingTable,
1079 (ClientData) itemPtr);
1081 (*itemPtr->typePtr->deleteProc)((Tk_Canvas) canvasPtr, itemPtr,
1082 canvasPtr->display);
1083 if (itemPtr->tagPtr != itemPtr->staticTagSpace) {
1084 ckfree((char *) itemPtr->tagPtr);
1086 entryPtr = Tcl_FindHashEntry(&canvasPtr->idTable,
1087 (char *) itemPtr->id);
1088 Tcl_DeleteHashEntry(entryPtr);
1089 if (itemPtr->nextPtr != NULL) {
1090 itemPtr->nextPtr->prevPtr = itemPtr->prevPtr;
1092 if (itemPtr->prevPtr != NULL) {
1093 itemPtr->prevPtr->nextPtr = itemPtr->nextPtr;
1095 if (canvasPtr->firstItemPtr == itemPtr) {
1096 canvasPtr->firstItemPtr = itemPtr->nextPtr;
1097 if (canvasPtr->firstItemPtr == NULL) {
1098 canvasPtr->lastItemPtr = NULL;
1101 if (canvasPtr->lastItemPtr == itemPtr) {
1102 canvasPtr->lastItemPtr = itemPtr->prevPtr;
1104 ckfree((char *) itemPtr);
1105 if (itemPtr == canvasPtr->currentItemPtr) {
1106 canvasPtr->currentItemPtr = NULL;
1107 canvasPtr->flags |= REPICK_NEEDED;
1109 if (itemPtr == canvasPtr->newCurrentPtr) {
1110 canvasPtr->newCurrentPtr = NULL;
1111 canvasPtr->flags |= REPICK_NEEDED;
1113 if (itemPtr == canvasPtr->textInfo.focusItemPtr) {
1114 canvasPtr->textInfo.focusItemPtr = NULL;
1116 if (itemPtr == canvasPtr->textInfo.selItemPtr) {
1117 canvasPtr->textInfo.selItemPtr = NULL;
1119 if ((itemPtr == canvasPtr->hotPtr)
1120 || (itemPtr == canvasPtr->hotPrevPtr)) {
1121 canvasPtr->hotPtr = NULL;
1131 if ((argc != 3) && (argc != 4)) {
1132 Tcl_WrongNumArgs(interp, 2, argv, "tagOrId ?tagToDelete?");
1137 tag = Tk_GetUid(Tcl_GetStringFromObj(argv[3], NULL));
1139 tag = Tk_GetUid(Tcl_GetStringFromObj(argv[2], NULL));
1141 #ifdef USE_OLD_TAG_SEARCH
1142 for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
1143 itemPtr != NULL; itemPtr = NextItem(&search)) {
1144 #else /* USE_OLD_TAG_SEARCH */
1145 if ((result = TagSearchScan(canvasPtr, argv[2], &searchPtr)) != TCL_OK) {
1148 for (itemPtr = TagSearchFirst(searchPtr);
1149 itemPtr != NULL; itemPtr = TagSearchNext(searchPtr)) {
1150 #endif /* USE_OLD_TAG_SEARCH */
1151 for (i = itemPtr->numTags-1; i >= 0; i--) {
1152 if (itemPtr->tagPtr[i] == tag) {
1153 itemPtr->tagPtr[i] = itemPtr->tagPtr[itemPtr->numTags-1];
1162 Tcl_WrongNumArgs(interp, 2, argv, "searchCommand ?arg arg ...?");
1166 #ifdef USE_OLD_TAG_SEARCH
1167 result = FindItems(interp, canvasPtr, argc, argv, (Tcl_Obj *) NULL, 2);
1168 #else /* USE_OLD_TAG_SEARCH */
1169 result = FindItems(interp, canvasPtr, argc, argv,
1170 (Tcl_Obj *) NULL, 2, &searchPtr);
1171 #endif /* USE_OLD_TAG_SEARCH */
1176 Tcl_WrongNumArgs(interp, 2, argv, "?tagOrId?");
1180 itemPtr = canvasPtr->textInfo.focusItemPtr;
1182 if (itemPtr != NULL) {
1183 char buf[TCL_INTEGER_SPACE];
1185 sprintf(buf, "%d", itemPtr->id);
1186 Tcl_SetResult(interp, buf, TCL_VOLATILE);
1190 if ((itemPtr != NULL) && (canvasPtr->textInfo.gotFocus)) {
1191 EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
1193 if (Tcl_GetStringFromObj(argv[2], NULL)[0] == 0) {
1194 canvasPtr->textInfo.focusItemPtr = NULL;
1197 #ifdef USE_OLD_TAG_SEARCH
1198 for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
1199 itemPtr != NULL; itemPtr = NextItem(&search)) {
1200 #else /* USE_OLD_TAG_SEARCH */
1201 if ((result = TagSearchScan(canvasPtr, argv[2], &searchPtr)) != TCL_OK) {
1204 for (itemPtr = TagSearchFirst(searchPtr);
1205 itemPtr != NULL; itemPtr = TagSearchNext(searchPtr)) {
1206 #endif /* USE_OLD_TAG_SEARCH */
1207 if (itemPtr->typePtr->icursorProc != NULL) {
1211 if (itemPtr == NULL) {
1214 canvasPtr->textInfo.focusItemPtr = itemPtr;
1215 if (canvasPtr->textInfo.gotFocus) {
1216 EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
1220 case CANV_GETTAGS: {
1222 Tcl_WrongNumArgs(interp, 2, argv, "tagOrId");
1226 #ifdef USE_OLD_TAG_SEARCH
1227 itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
1228 #else /* USE_OLD_TAG_SEARCH */
1229 if ((result = TagSearchScan(canvasPtr, argv[2], &searchPtr)) != TCL_OK) {
1232 itemPtr = TagSearchFirst(searchPtr);
1233 #endif /* USE_OLD_TAG_SEARCH */
1234 if (itemPtr != NULL) {
1236 for (i = 0; i < itemPtr->numTags; i++) {
1237 Tcl_AppendElement(interp, (char *) itemPtr->tagPtr[i]);
1242 case CANV_ICURSOR: {
1246 Tcl_WrongNumArgs(interp, 2, argv, "tagOrId index");
1250 #ifdef USE_OLD_TAG_SEARCH
1251 for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
1252 itemPtr != NULL; itemPtr = NextItem(&search)) {
1253 #else /* USE_OLD_TAG_SEARCH */
1254 if ((result = TagSearchScan(canvasPtr, argv[2], &searchPtr)) != TCL_OK) {
1257 for (itemPtr = TagSearchFirst(searchPtr);
1258 itemPtr != NULL; itemPtr = TagSearchNext(searchPtr)) {
1259 #endif /* USE_OLD_TAG_SEARCH */
1260 if ((itemPtr->typePtr->indexProc == NULL)
1261 || (itemPtr->typePtr->icursorProc == NULL)) {
1264 if (itemPtr->typePtr->alwaysRedraw & TK_CONFIG_OBJS) {
1265 result = itemPtr->typePtr->indexProc(interp, (Tk_Canvas) canvasPtr,
1266 itemPtr, (char *) argv[3], &index);
1268 result = itemPtr->typePtr->indexProc(interp, (Tk_Canvas) canvasPtr,
1269 itemPtr, Tcl_GetStringFromObj(argv[3], NULL), &index);
1271 if (result != TCL_OK) {
1274 (*itemPtr->typePtr->icursorProc)((Tk_Canvas) canvasPtr, itemPtr,
1276 if ((itemPtr == canvasPtr->textInfo.focusItemPtr)
1277 && (canvasPtr->textInfo.cursorOn)) {
1278 EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
1286 char buf[TCL_INTEGER_SPACE];
1289 Tcl_WrongNumArgs(interp, 2, argv, "tagOrId string");
1293 #ifdef USE_OLD_TAG_SEARCH
1294 for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
1295 itemPtr != NULL; itemPtr = NextItem(&search)) {
1296 #else /* USE_OLD_TAG_SEARCH */
1297 if ((result = TagSearchScan(canvasPtr, argv[2], &searchPtr)) != TCL_OK) {
1300 for (itemPtr = TagSearchFirst(searchPtr);
1301 itemPtr != NULL; itemPtr = TagSearchNext(searchPtr)) {
1302 #endif /* USE_OLD_TAG_SEARCH */
1303 if (itemPtr->typePtr->indexProc != NULL) {
1307 if (itemPtr == NULL) {
1308 Tcl_AppendResult(interp, "can't find an indexable item \"",
1309 Tcl_GetStringFromObj(argv[2], NULL), "\"", (char *) NULL);
1313 if (itemPtr->typePtr->alwaysRedraw & TK_CONFIG_OBJS) {
1314 result = itemPtr->typePtr->indexProc(interp, (Tk_Canvas) canvasPtr,
1315 itemPtr, (char *) argv[3], &index);
1317 result = itemPtr->typePtr->indexProc(interp, (Tk_Canvas) canvasPtr,
1318 itemPtr, Tcl_GetStringFromObj(argv[3], NULL), &index);
1320 if (result != TCL_OK) {
1323 sprintf(buf, "%d", index);
1324 Tcl_SetResult(interp, buf, TCL_VOLATILE);
1332 Tcl_WrongNumArgs(interp, 2, argv, "tagOrId beforeThis string");
1336 #ifdef USE_OLD_TAG_SEARCH
1337 for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
1338 itemPtr != NULL; itemPtr = NextItem(&search)) {
1339 #else /* USE_OLD_TAG_SEARCH */
1340 if ((result = TagSearchScan(canvasPtr, argv[2], &searchPtr)) != TCL_OK) {
1343 for (itemPtr = TagSearchFirst(searchPtr);
1344 itemPtr != NULL; itemPtr = TagSearchNext(searchPtr)) {
1345 #endif /* USE_OLD_TAG_SEARCH */
1346 if ((itemPtr->typePtr->indexProc == NULL)
1347 || (itemPtr->typePtr->insertProc == NULL)) {
1350 if (itemPtr->typePtr->alwaysRedraw & TK_CONFIG_OBJS) {
1351 result = itemPtr->typePtr->indexProc(interp, (Tk_Canvas) canvasPtr,
1352 itemPtr, (char *) argv[3], &beforeThis);
1354 result = itemPtr->typePtr->indexProc(interp, (Tk_Canvas) canvasPtr,
1355 itemPtr, Tcl_GetStringFromObj(argv[3], NULL), &beforeThis);
1357 if (result != TCL_OK) {
1362 * Redraw both item's old and new areas: it's possible
1363 * that an insertion could result in a new area either
1364 * larger or smaller than the old area. Except if the
1365 * insertProc sets the TK_ITEM_DONT_REDRAW flag, nothing
1366 * more needs to be done.
1369 x1 = itemPtr->x1; y1 = itemPtr->y1;
1370 x2 = itemPtr->x2; y2 = itemPtr->y2;
1371 itemPtr->redraw_flags &= ~TK_ITEM_DONT_REDRAW;
1372 if (itemPtr->typePtr->alwaysRedraw & TK_CONFIG_OBJS) {
1373 (*itemPtr->typePtr->insertProc)((Tk_Canvas) canvasPtr,
1374 itemPtr, beforeThis, (char *) argv[4]);
1376 (*itemPtr->typePtr->insertProc)((Tk_Canvas) canvasPtr,
1377 itemPtr, beforeThis, Tcl_GetStringFromObj(argv[4], NULL));
1379 if (!(itemPtr->redraw_flags & TK_ITEM_DONT_REDRAW)) {
1380 Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
1382 EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
1384 itemPtr->redraw_flags &= ~TK_ITEM_DONT_REDRAW;
1388 case CANV_ITEMCGET: {
1390 Tcl_WrongNumArgs(interp, 2, argv, "tagOrId option");
1394 #ifdef USE_OLD_TAG_SEARCH
1395 itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
1396 #else /* USE_OLD_TAG_SEARCH */
1397 if ((result = TagSearchScan(canvasPtr, argv[2], &searchPtr)) != TCL_OK) {
1400 itemPtr = TagSearchFirst(searchPtr);
1401 #endif /* USE_OLD_TAG_SEARCH */
1402 if (itemPtr != NULL) {
1403 result = Tk_ConfigureValue(canvasPtr->interp, canvasPtr->tkwin,
1404 itemPtr->typePtr->configSpecs, (char *) itemPtr,
1405 Tcl_GetStringFromObj(argv[3], NULL), 0);
1409 case CANV_ITEMCONFIGURE: {
1411 Tcl_WrongNumArgs(interp, 2, argv, "tagOrId ?option value ...?");
1415 #ifdef USE_OLD_TAG_SEARCH
1416 for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
1417 itemPtr != NULL; itemPtr = NextItem(&search)) {
1418 #else /* USE_OLD_TAG_SEARCH */
1419 if ((result = TagSearchScan(canvasPtr, argv[2], &searchPtr)) != TCL_OK) {
1422 for (itemPtr = TagSearchFirst(searchPtr);
1423 itemPtr != NULL; itemPtr = TagSearchNext(searchPtr)) {
1424 #endif /* USE_OLD_TAG_SEARCH */
1426 result = Tk_ConfigureInfo(canvasPtr->interp, canvasPtr->tkwin,
1427 itemPtr->typePtr->configSpecs, (char *) itemPtr,
1429 } else if (argc == 4) {
1430 result = Tk_ConfigureInfo(canvasPtr->interp, canvasPtr->tkwin,
1431 itemPtr->typePtr->configSpecs, (char *) itemPtr,
1432 Tcl_GetString(argv[3]), 0);
1434 EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
1435 if (itemPtr->typePtr->alwaysRedraw & TK_CONFIG_OBJS) {
1436 result = (*itemPtr->typePtr->configProc)(interp,
1437 (Tk_Canvas) canvasPtr, itemPtr, argc-3, argv+3,
1438 TK_CONFIG_ARGV_ONLY);
1440 CONST char **args = GetStringsFromObjs(argc-3, argv+3);
1441 result = (*itemPtr->typePtr->configProc)(interp,
1442 (Tk_Canvas) canvasPtr, itemPtr, argc-3, (Tcl_Obj **) args,
1443 TK_CONFIG_ARGV_ONLY);
1444 if (args) ckfree((char *) args);
1446 EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
1447 canvasPtr->flags |= REPICK_NEEDED;
1449 if ((result != TCL_OK) || (argc < 5)) {
1458 if ((argc != 3) && (argc != 4)) {
1459 Tcl_WrongNumArgs(interp, 2, argv, "tagOrId ?belowThis?");
1465 * First find the item just after which we'll insert the
1472 #ifdef USE_OLD_TAG_SEARCH
1473 itemPtr = StartTagSearch(canvasPtr, argv[3], &search);
1474 #else /* USE_OLD_TAG_SEARCH */
1475 if ((result = TagSearchScan(canvasPtr, argv[3], &searchPtr)) != TCL_OK) {
1478 itemPtr = TagSearchFirst(searchPtr);
1479 #endif /* USE_OLD_TAG_SEARCH */
1480 if (itemPtr == NULL) {
1481 Tcl_AppendResult(interp, "tag \"", Tcl_GetString(argv[3]),
1482 "\" doesn't match any items", (char *) NULL);
1485 itemPtr = itemPtr->prevPtr;
1487 #ifdef USE_OLD_TAG_SEARCH
1488 RelinkItems(canvasPtr, argv[2], itemPtr);
1489 #else /* USE_OLD_TAG_SEARCH */
1490 if ((result = RelinkItems(canvasPtr, argv[2], itemPtr, &searchPtr)) != TCL_OK) {
1493 #endif /* USE_OLD_TAG_SEARCH */
1497 double xAmount, yAmount;
1500 Tcl_WrongNumArgs(interp, 2, argv, "tagOrId xAmount yAmount");
1504 if ((Tk_CanvasGetCoordFromObj(interp, (Tk_Canvas) canvasPtr, argv[3],
1505 &xAmount) != TCL_OK) || (Tk_CanvasGetCoordFromObj(interp,
1506 (Tk_Canvas) canvasPtr, argv[4], &yAmount) != TCL_OK)) {
1510 #ifdef USE_OLD_TAG_SEARCH
1511 for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
1512 itemPtr != NULL; itemPtr = NextItem(&search)) {
1513 #else /* USE_OLD_TAG_SEARCH */
1514 if ((result = TagSearchScan(canvasPtr, argv[2], &searchPtr)) != TCL_OK) {
1517 for (itemPtr = TagSearchFirst(searchPtr);
1518 itemPtr != NULL; itemPtr = TagSearchNext(searchPtr)) {
1519 #endif /* USE_OLD_TAG_SEARCH */
1520 EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
1521 (void) (*itemPtr->typePtr->translateProc)((Tk_Canvas) canvasPtr,
1522 itemPtr, xAmount, yAmount);
1523 EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
1524 canvasPtr->flags |= REPICK_NEEDED;
1528 case CANV_POSTSCRIPT: {
1529 CONST char **args = GetStringsFromObjs(argc, argv);
1530 result = TkCanvPostscriptCmd(canvasPtr, interp, argc, args);
1531 if (args) ckfree((char *) args);
1537 if ((argc != 3) && (argc != 4)) {
1538 Tcl_WrongNumArgs(interp, 2, argv, "tagOrId ?aboveThis?");
1544 * First find the item just after which we'll insert the
1549 prevPtr = canvasPtr->lastItemPtr;
1552 #ifdef USE_OLD_TAG_SEARCH
1553 for (itemPtr = StartTagSearch(canvasPtr, argv[3], &search);
1554 itemPtr != NULL; itemPtr = NextItem(&search)) {
1555 #else /* USE_OLD_TAG_SEARCH */
1556 if ((result = TagSearchScan(canvasPtr, argv[3], &searchPtr)) != TCL_OK) {
1559 for (itemPtr = TagSearchFirst(searchPtr);
1560 itemPtr != NULL; itemPtr = TagSearchNext(searchPtr)) {
1561 #endif /* USE_OLD_TAG_SEARCH */
1564 if (prevPtr == NULL) {
1565 Tcl_AppendResult(interp, "tagOrId \"", Tcl_GetStringFromObj(argv[3], NULL),
1566 "\" doesn't match any items", (char *) NULL);
1571 #ifdef USE_OLD_TAG_SEARCH
1572 RelinkItems(canvasPtr, argv[2], prevPtr);
1573 #else /* USE_OLD_TAG_SEARCH */
1574 result = RelinkItems(canvasPtr, argv[2], prevPtr, &searchPtr);
1575 if (result != TCL_OK) {
1578 #endif /* USE_OLD_TAG_SEARCH */
1582 double xOrigin, yOrigin, xScale, yScale;
1585 Tcl_WrongNumArgs(interp, 2, argv, "tagOrId xOrigin yOrigin xScale yScale");
1589 if ((Tk_CanvasGetCoordFromObj(interp, (Tk_Canvas) canvasPtr,
1590 argv[3], &xOrigin) != TCL_OK)
1591 || (Tk_CanvasGetCoordFromObj(interp, (Tk_Canvas) canvasPtr,
1592 argv[4], &yOrigin) != TCL_OK)
1593 || (Tcl_GetDoubleFromObj(interp, argv[5], &xScale) != TCL_OK)
1594 || (Tcl_GetDoubleFromObj(interp, argv[6], &yScale) != TCL_OK)) {
1598 if ((xScale == 0.0) || (yScale == 0.0)) {
1599 Tcl_SetResult(interp, "scale factor cannot be zero", TCL_STATIC);
1603 #ifdef USE_OLD_TAG_SEARCH
1604 for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
1605 itemPtr != NULL; itemPtr = NextItem(&search)) {
1606 #else /* USE_OLD_TAG_SEARCH */
1607 if ((result = TagSearchScan(canvasPtr, argv[2], &searchPtr)) != TCL_OK) {
1610 for (itemPtr = TagSearchFirst(searchPtr);
1611 itemPtr != NULL; itemPtr = TagSearchNext(searchPtr)) {
1612 #endif /* USE_OLD_TAG_SEARCH */
1613 EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
1614 (void) (*itemPtr->typePtr->scaleProc)((Tk_Canvas) canvasPtr,
1615 itemPtr, xOrigin, yOrigin, xScale, yScale);
1616 EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
1617 canvasPtr->flags |= REPICK_NEEDED;
1623 static CONST char *optionStrings[] = {
1624 "mark", "dragto", NULL
1628 Tcl_WrongNumArgs(interp, 2, argv, "mark|dragto x y ?dragGain?");
1630 } else if (Tcl_GetIndexFromObj(interp, argv[2], optionStrings,
1631 "scan option", 0, &index) != TCL_OK) {
1633 } else if ((argc != 5) && (argc != 5+index)) {
1634 Tcl_WrongNumArgs(interp, 3, argv, index?"x y ?gain?":"x y");
1636 } else if ((Tcl_GetIntFromObj(interp, argv[3], &x) != TCL_OK)
1637 || (Tcl_GetIntFromObj(interp, argv[4], &y) != TCL_OK)){
1639 } else if ((argc == 6) &&
1640 (Tcl_GetIntFromObj(interp, argv[5], &gain) != TCL_OK)) {
1642 } else if (!index) {
1643 canvasPtr->scanX = x;
1644 canvasPtr->scanXOrigin = canvasPtr->xOrigin;
1645 canvasPtr->scanY = y;
1646 canvasPtr->scanYOrigin = canvasPtr->yOrigin;
1648 int newXOrigin, newYOrigin, tmp;
1651 * Compute a new view origin for the canvas, amplifying the
1655 tmp = canvasPtr->scanXOrigin - gain*(x - canvasPtr->scanX)
1656 - canvasPtr->scrollX1;
1657 newXOrigin = canvasPtr->scrollX1 + tmp;
1658 tmp = canvasPtr->scanYOrigin - gain*(y - canvasPtr->scanY)
1659 - canvasPtr->scrollY1;
1660 newYOrigin = canvasPtr->scrollY1 + tmp;
1661 CanvasSetOrigin(canvasPtr, newXOrigin, newYOrigin);
1666 int index, optionindex;
1667 static CONST char *optionStrings[] = {
1668 "adjust", "clear", "from", "item", "to", NULL
1671 CANV_ADJUST, CANV_CLEAR, CANV_FROM, CANV_ITEM, CANV_TO
1675 Tcl_WrongNumArgs(interp, 2, argv, "option ?tagOrId? ?arg?");
1680 #ifdef USE_OLD_TAG_SEARCH
1681 for (itemPtr = StartTagSearch(canvasPtr, argv[3], &search);
1682 itemPtr != NULL; itemPtr = NextItem(&search)) {
1683 #else /* USE_OLD_TAG_SEARCH */
1684 if ((result = TagSearchScan(canvasPtr, argv[3], &searchPtr)) != TCL_OK) {
1687 for (itemPtr = TagSearchFirst(searchPtr);
1688 itemPtr != NULL; itemPtr = TagSearchNext(searchPtr)) {
1689 #endif /* USE_OLD_TAG_SEARCH */
1690 if ((itemPtr->typePtr->indexProc != NULL)
1691 && (itemPtr->typePtr->selectionProc != NULL)){
1695 if (itemPtr == NULL) {
1696 Tcl_AppendResult(interp,
1697 "can't find an indexable and selectable item \"",
1698 Tcl_GetStringFromObj(argv[3], NULL), "\"", (char *) NULL);
1704 if (itemPtr->typePtr->alwaysRedraw & TK_CONFIG_OBJS) {
1705 result = itemPtr->typePtr->indexProc(interp, (Tk_Canvas) canvasPtr,
1706 itemPtr, (char *) argv[4], &index);
1708 result = itemPtr->typePtr->indexProc(interp, (Tk_Canvas) canvasPtr,
1709 itemPtr, Tcl_GetStringFromObj(argv[4], NULL), &index);
1711 if (result != TCL_OK) {
1715 if (Tcl_GetIndexFromObj(interp, argv[2], optionStrings, "select option", 0,
1716 &optionindex) != TCL_OK) {
1720 switch ((enum options) optionindex) {
1723 Tcl_WrongNumArgs(interp, 3, argv, "tagOrId index");
1727 if (canvasPtr->textInfo.selItemPtr == itemPtr) {
1728 if (index < (canvasPtr->textInfo.selectFirst
1729 + canvasPtr->textInfo.selectLast)/2) {
1730 canvasPtr->textInfo.selectAnchor =
1731 canvasPtr->textInfo.selectLast + 1;
1733 canvasPtr->textInfo.selectAnchor =
1734 canvasPtr->textInfo.selectFirst;
1737 CanvasSelectTo(canvasPtr, itemPtr, index);
1742 Tcl_AppendResult(interp, 3, argv, (char *) NULL);
1746 if (canvasPtr->textInfo.selItemPtr != NULL) {
1747 EventuallyRedrawItem((Tk_Canvas) canvasPtr,
1748 canvasPtr->textInfo.selItemPtr);
1749 canvasPtr->textInfo.selItemPtr = NULL;
1756 Tcl_WrongNumArgs(interp, 3, argv, "tagOrId index");
1760 canvasPtr->textInfo.anchorItemPtr = itemPtr;
1761 canvasPtr->textInfo.selectAnchor = index;
1766 Tcl_WrongNumArgs(interp, 3, argv, (char *) NULL);
1770 if (canvasPtr->textInfo.selItemPtr != NULL) {
1771 char buf[TCL_INTEGER_SPACE];
1773 sprintf(buf, "%d", canvasPtr->textInfo.selItemPtr->id);
1774 Tcl_SetResult(interp, buf, TCL_VOLATILE);
1780 Tcl_WrongNumArgs(interp, 2, argv, "tagOrId index");
1784 CanvasSelectTo(canvasPtr, itemPtr, index);
1792 Tcl_WrongNumArgs(interp, 2, argv, "tag");
1796 #ifdef USE_OLD_TAG_SEARCH
1797 itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
1798 #else /* USE_OLD_TAG_SEARCH */
1799 if ((result = TagSearchScan(canvasPtr, argv[2], &searchPtr)) != TCL_OK) {
1802 itemPtr = TagSearchFirst(searchPtr);
1803 #endif /* USE_OLD_TAG_SEARCH */
1804 if (itemPtr != NULL) {
1805 Tcl_SetResult(interp, itemPtr->typePtr->name, TCL_STATIC);
1811 int newX = 0; /* Initialization needed only to prevent
1816 Tcl_SetObjResult(interp, ScrollFractions(
1817 canvasPtr->xOrigin + canvasPtr->inset,
1818 canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin)
1819 - canvasPtr->inset, canvasPtr->scrollX1,
1820 canvasPtr->scrollX2));
1822 CONST char **args = GetStringsFromObjs(argc, argv);
1823 type = Tk_GetScrollInfo(interp, argc, args, &fraction, &count);
1824 if (args) ckfree((char *) args);
1826 case TK_SCROLL_ERROR:
1829 case TK_SCROLL_MOVETO:
1830 newX = canvasPtr->scrollX1 - canvasPtr->inset
1831 + (int) (fraction * (canvasPtr->scrollX2
1832 - canvasPtr->scrollX1) + 0.5);
1834 case TK_SCROLL_PAGES:
1835 newX = (int) (canvasPtr->xOrigin + count * .9
1836 * (Tk_Width(canvasPtr->tkwin) - 2*canvasPtr->inset));
1838 case TK_SCROLL_UNITS:
1839 if (canvasPtr->xScrollIncrement > 0) {
1840 newX = canvasPtr->xOrigin
1841 + count*canvasPtr->xScrollIncrement;
1843 newX = (int) (canvasPtr->xOrigin + count * .1
1844 * (Tk_Width(canvasPtr->tkwin)
1845 - 2*canvasPtr->inset));
1849 CanvasSetOrigin(canvasPtr, newX, canvasPtr->yOrigin);
1855 int newY = 0; /* Initialization needed only to prevent
1860 Tcl_SetObjResult(interp,ScrollFractions(\
1861 canvasPtr->yOrigin + canvasPtr->inset,
1862 canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin)
1863 - canvasPtr->inset, canvasPtr->scrollY1,
1864 canvasPtr->scrollY2));
1866 CONST char **args = GetStringsFromObjs(argc, argv);
1867 type = Tk_GetScrollInfo(interp, argc, args, &fraction, &count);
1868 if (args) ckfree((char *) args);
1870 case TK_SCROLL_ERROR:
1873 case TK_SCROLL_MOVETO:
1874 newY = canvasPtr->scrollY1 - canvasPtr->inset
1875 + (int) (fraction*(canvasPtr->scrollY2
1876 - canvasPtr->scrollY1) + 0.5);
1878 case TK_SCROLL_PAGES:
1879 newY = (int) (canvasPtr->yOrigin + count * .9
1880 * (Tk_Height(canvasPtr->tkwin)
1881 - 2*canvasPtr->inset));
1883 case TK_SCROLL_UNITS:
1884 if (canvasPtr->yScrollIncrement > 0) {
1885 newY = canvasPtr->yOrigin
1886 + count*canvasPtr->yScrollIncrement;
1888 newY = (int) (canvasPtr->yOrigin + count * .1
1889 * (Tk_Height(canvasPtr->tkwin)
1890 - 2*canvasPtr->inset));
1894 CanvasSetOrigin(canvasPtr, canvasPtr->xOrigin, newY);
1900 #ifndef USE_OLD_TAG_SEARCH
1901 TagSearchDestroy(searchPtr);
1902 #endif /* not USE_OLD_TAG_SEARCH */
1903 Tcl_Release((ClientData) canvasPtr);
1908 *----------------------------------------------------------------------
1912 * This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
1913 * to clean up the internal structure of a canvas at a safe time
1914 * (when no-one is using it anymore).
1920 * Everything associated with the canvas is freed up.
1922 *----------------------------------------------------------------------
1926 DestroyCanvas(memPtr)
1927 char *memPtr; /* Info about canvas widget. */
1929 TkCanvas *canvasPtr = (TkCanvas *) memPtr;
1931 #ifndef USE_OLD_TAG_SEARCH
1932 TagSearchExpr *expr, *next;
1936 * Free up all of the items in the canvas.
1939 for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
1940 itemPtr = canvasPtr->firstItemPtr) {
1941 canvasPtr->firstItemPtr = itemPtr->nextPtr;
1942 (*itemPtr->typePtr->deleteProc)((Tk_Canvas) canvasPtr, itemPtr,
1943 canvasPtr->display);
1944 if (itemPtr->tagPtr != itemPtr->staticTagSpace) {
1945 ckfree((char *) itemPtr->tagPtr);
1947 ckfree((char *) itemPtr);
1951 * Free up all the stuff that requires special handling,
1952 * then let Tk_FreeOptions handle all the standard option-related
1956 Tcl_DeleteHashTable(&canvasPtr->idTable);
1957 if (canvasPtr->pixmapGC != None) {
1958 Tk_FreeGC(canvasPtr->display, canvasPtr->pixmapGC);
1960 #ifndef USE_OLD_TAG_SEARCH
1961 expr = canvasPtr->bindTagExprs;
1964 TagSearchExprDestroy(expr);
1968 Tcl_DeleteTimerHandler(canvasPtr->insertBlinkHandler);
1969 if (canvasPtr->bindingTable != NULL) {
1970 Tk_DeleteBindingTable(canvasPtr->bindingTable);
1972 Tk_FreeOptions(configSpecs, (char *) canvasPtr, canvasPtr->display, 0);
1973 canvasPtr->tkwin = NULL;
1974 ckfree((char *) canvasPtr);
1978 *----------------------------------------------------------------------
1980 * ConfigureCanvas --
1982 * This procedure is called to process an argv/argc list, plus
1983 * the Tk option database, in order to configure (or
1984 * reconfigure) a canvas widget.
1987 * The return value is a standard Tcl result. If TCL_ERROR is
1988 * returned, then the interp's result contains an error message.
1991 * Configuration information, such as colors, border width,
1992 * etc. get set for canvasPtr; old resources get freed,
1993 * if there were any.
1995 *----------------------------------------------------------------------
1999 ConfigureCanvas(interp, canvasPtr, argc, argv, flags)
2000 Tcl_Interp *interp; /* Used for error reporting. */
2001 TkCanvas *canvasPtr; /* Information about widget; may or may
2002 * not already have values for some fields. */
2003 int argc; /* Number of valid entries in argv. */
2004 Tcl_Obj *CONST argv[]; /* Argument objects. */
2005 int flags; /* Flags to pass to Tk_ConfigureWidget. */
2010 if (Tk_ConfigureWidget(interp, canvasPtr->tkwin, configSpecs,
2011 argc, (CONST char **) argv, (char *) canvasPtr,
2012 flags|TK_CONFIG_OBJS) != TCL_OK) {
2017 * A few options need special processing, such as setting the
2018 * background from a 3-D border and creating a GC for copying
2019 * bits to the screen.
2022 Tk_SetBackgroundFromBorder(canvasPtr->tkwin, canvasPtr->bgBorder);
2024 if (canvasPtr->highlightWidth < 0) {
2025 canvasPtr->highlightWidth = 0;
2027 canvasPtr->inset = canvasPtr->borderWidth + canvasPtr->highlightWidth;
2029 gcValues.function = GXcopy;
2030 gcValues.graphics_exposures = False;
2031 gcValues.foreground = Tk_3DBorderColor(canvasPtr->bgBorder)->pixel;
2032 new = Tk_GetGC(canvasPtr->tkwin,
2033 GCFunction|GCGraphicsExposures|GCForeground, &gcValues);
2034 if (canvasPtr->pixmapGC != None) {
2035 Tk_FreeGC(canvasPtr->display, canvasPtr->pixmapGC);
2037 canvasPtr->pixmapGC = new;
2040 * Reset the desired dimensions for the window.
2043 Tk_GeometryRequest(canvasPtr->tkwin, canvasPtr->width + 2*canvasPtr->inset,
2044 canvasPtr->height + 2*canvasPtr->inset);
2047 * Restart the cursor timing sequence in case the on-time or off-time
2051 if (canvasPtr->textInfo.gotFocus) {
2052 CanvasFocusProc(canvasPtr, 1);
2056 * Recompute the scroll region.
2059 canvasPtr->scrollX1 = 0;
2060 canvasPtr->scrollY1 = 0;
2061 canvasPtr->scrollX2 = 0;
2062 canvasPtr->scrollY2 = 0;
2063 if (canvasPtr->regionString != NULL) {
2067 if (Tcl_SplitList(canvasPtr->interp, canvasPtr->regionString,
2068 &argc2, &argv2) != TCL_OK) {
2072 Tcl_AppendResult(interp, "bad scrollRegion \"",
2073 canvasPtr->regionString, "\"", (char *) NULL);
2075 ckfree(canvasPtr->regionString);
2076 ckfree((char *) argv2);
2077 canvasPtr->regionString = NULL;
2080 if ((Tk_GetPixels(canvasPtr->interp, canvasPtr->tkwin,
2081 argv2[0], &canvasPtr->scrollX1) != TCL_OK)
2082 || (Tk_GetPixels(canvasPtr->interp, canvasPtr->tkwin,
2083 argv2[1], &canvasPtr->scrollY1) != TCL_OK)
2084 || (Tk_GetPixels(canvasPtr->interp, canvasPtr->tkwin,
2085 argv2[2], &canvasPtr->scrollX2) != TCL_OK)
2086 || (Tk_GetPixels(canvasPtr->interp, canvasPtr->tkwin,
2087 argv2[3], &canvasPtr->scrollY2) != TCL_OK)) {
2090 ckfree((char *) argv2);
2093 flags = canvasPtr->tsoffset.flags;
2094 if (flags & TK_OFFSET_LEFT) {
2095 canvasPtr->tsoffset.xoffset = 0;
2096 } else if (flags & TK_OFFSET_CENTER) {
2097 canvasPtr->tsoffset.xoffset = canvasPtr->width/2;
2098 } else if (flags & TK_OFFSET_RIGHT) {
2099 canvasPtr->tsoffset.xoffset = canvasPtr->width;
2101 if (flags & TK_OFFSET_TOP) {
2102 canvasPtr->tsoffset.yoffset = 0;
2103 } else if (flags & TK_OFFSET_MIDDLE) {
2104 canvasPtr->tsoffset.yoffset = canvasPtr->height/2;
2105 } else if (flags & TK_OFFSET_BOTTOM) {
2106 canvasPtr->tsoffset.yoffset = canvasPtr->height;
2110 * Reset the canvas's origin (this is a no-op unless confine
2111 * mode has just been turned on or the scroll region has changed).
2114 CanvasSetOrigin(canvasPtr, canvasPtr->xOrigin, canvasPtr->yOrigin);
2115 canvasPtr->flags |= UPDATE_SCROLLBARS|REDRAW_BORDERS;
2116 Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
2117 canvasPtr->xOrigin, canvasPtr->yOrigin,
2118 canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin),
2119 canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin));
2124 *---------------------------------------------------------------------------
2126 * CanvasWorldChanged --
2128 * This procedure is called when the world has changed in some
2129 * way and the widget needs to recompute all its graphics contexts
2130 * and determine its new geometry.
2136 * Configures all items in the canvas with a empty argc/argv, for
2137 * the side effect of causing all the items to recompute their
2138 * geometry and to be redisplayed.
2140 *---------------------------------------------------------------------------
2144 CanvasWorldChanged(instanceData)
2145 ClientData instanceData; /* Information about widget. */
2147 TkCanvas *canvasPtr;
2151 canvasPtr = (TkCanvas *) instanceData;
2152 itemPtr = canvasPtr->firstItemPtr;
2153 for ( ; itemPtr != NULL; itemPtr = itemPtr->nextPtr) {
2154 result = (*itemPtr->typePtr->configProc)(canvasPtr->interp,
2155 (Tk_Canvas) canvasPtr, itemPtr, 0, NULL,
2156 TK_CONFIG_ARGV_ONLY);
2157 if (result != TCL_OK) {
2158 Tcl_ResetResult(canvasPtr->interp);
2161 canvasPtr->flags |= REPICK_NEEDED;
2162 Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
2163 canvasPtr->xOrigin, canvasPtr->yOrigin,
2164 canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin),
2165 canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin));
2169 *--------------------------------------------------------------
2173 * This procedure redraws the contents of a canvas window.
2174 * It is invoked as a do-when-idle handler, so it only runs
2175 * when there's nothing else for the application to do.
2181 * Information appears on the screen.
2183 *--------------------------------------------------------------
2187 DisplayCanvas(clientData)
2188 ClientData clientData; /* Information about widget. */
2190 TkCanvas *canvasPtr = (TkCanvas *) clientData;
2191 Tk_Window tkwin = canvasPtr->tkwin;
2194 int screenX1, screenX2, screenY1, screenY2, width, height;
2196 if (canvasPtr->tkwin == NULL) {
2200 if (!Tk_IsMapped(tkwin)) {
2205 * Choose a new current item if that is needed (this could cause
2206 * event handlers to be invoked).
2209 while (canvasPtr->flags & REPICK_NEEDED) {
2210 Tcl_Preserve((ClientData) canvasPtr);
2211 canvasPtr->flags &= ~REPICK_NEEDED;
2212 PickCurrentItem(canvasPtr, &canvasPtr->pickEvent);
2213 tkwin = canvasPtr->tkwin;
2214 Tcl_Release((ClientData) canvasPtr);
2215 if (tkwin == NULL) {
2221 * Scan through the item list, registering the bounding box
2222 * for all items that didn't do that for the final coordinates
2223 * yet. This can be determined by the FORCE_REDRAW flag.
2226 for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
2227 itemPtr = itemPtr->nextPtr) {
2228 if (itemPtr->redraw_flags & FORCE_REDRAW) {
2229 itemPtr->redraw_flags &= ~FORCE_REDRAW;
2230 EventuallyRedrawItem((Tk_Canvas)canvasPtr, itemPtr);
2231 itemPtr->redraw_flags &= ~FORCE_REDRAW;
2235 * Compute the intersection between the area that needs redrawing
2236 * and the area that's visible on the screen.
2239 if ((canvasPtr->redrawX1 < canvasPtr->redrawX2)
2240 && (canvasPtr->redrawY1 < canvasPtr->redrawY2)) {
2241 screenX1 = canvasPtr->xOrigin + canvasPtr->inset;
2242 screenY1 = canvasPtr->yOrigin + canvasPtr->inset;
2243 screenX2 = canvasPtr->xOrigin + Tk_Width(tkwin) - canvasPtr->inset;
2244 screenY2 = canvasPtr->yOrigin + Tk_Height(tkwin) - canvasPtr->inset;
2245 if (canvasPtr->redrawX1 > screenX1) {
2246 screenX1 = canvasPtr->redrawX1;
2248 if (canvasPtr->redrawY1 > screenY1) {
2249 screenY1 = canvasPtr->redrawY1;
2251 if (canvasPtr->redrawX2 < screenX2) {
2252 screenX2 = canvasPtr->redrawX2;
2254 if (canvasPtr->redrawY2 < screenY2) {
2255 screenY2 = canvasPtr->redrawY2;
2257 if ((screenX1 >= screenX2) || (screenY1 >= screenY2)) {
2262 * Redrawing is done in a temporary pixmap that is allocated
2263 * here and freed at the end of the procedure. All drawing
2264 * is done to the pixmap, and the pixmap is copied to the
2265 * screen at the end of the procedure. The temporary pixmap
2266 * serves two purposes:
2268 * 1. It provides a smoother visual effect (no clearing and
2269 * gradual redraw will be visible to users).
2270 * 2. It allows us to redraw only the objects that overlap
2271 * the redraw area. Otherwise incorrect results could
2272 * occur from redrawing things that stick outside of
2273 * the redraw area (we'd have to redraw everything in
2274 * order to make the overlaps look right).
2276 * Some tricky points about the pixmap:
2278 * 1. We only allocate a large enough pixmap to hold the
2279 * area that has to be redisplayed. This saves time in
2280 * in the X server for large objects that cover much
2281 * more than the area being redisplayed: only the area
2282 * of the pixmap will actually have to be redrawn.
2283 * 2. Some X servers (e.g. the one for DECstations) have troubles
2284 * with characters that overlap an edge of the pixmap (on the
2285 * DEC servers, as of 8/18/92, such characters are drawn one
2286 * pixel too far to the right). To handle this problem,
2287 * make the pixmap a bit larger than is absolutely needed
2288 * so that for normal-sized fonts the characters that overlap
2289 * the edge of the pixmap will be outside the area we care
2293 canvasPtr->drawableXOrigin = screenX1 - 30;
2294 canvasPtr->drawableYOrigin = screenY1 - 30;
2295 pixmap = Tk_GetPixmap(Tk_Display(tkwin), Tk_WindowId(tkwin),
2296 (screenX2 + 30 - canvasPtr->drawableXOrigin),
2297 (screenY2 + 30 - canvasPtr->drawableYOrigin),
2301 * Clear the area to be redrawn.
2304 width = screenX2 - screenX1;
2305 height = screenY2 - screenY1;
2307 XFillRectangle(Tk_Display(tkwin), pixmap, canvasPtr->pixmapGC,
2308 screenX1 - canvasPtr->drawableXOrigin,
2309 screenY1 - canvasPtr->drawableYOrigin, (unsigned int) width,
2310 (unsigned int) height);
2313 * Scan through the item list, redrawing those items that need it.
2314 * An item must be redraw if either (a) it intersects the smaller
2315 * on-screen area or (b) it intersects the full canvas area and its
2316 * type requests that it be redrawn always (e.g. so subwindows can
2317 * be unmapped when they move off-screen).
2320 for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
2321 itemPtr = itemPtr->nextPtr) {
2322 if ((itemPtr->x1 >= screenX2)
2323 || (itemPtr->y1 >= screenY2)
2324 || (itemPtr->x2 < screenX1)
2325 || (itemPtr->y2 < screenY1)) {
2326 if (!(itemPtr->typePtr->alwaysRedraw & 1)
2327 || (itemPtr->x1 >= canvasPtr->redrawX2)
2328 || (itemPtr->y1 >= canvasPtr->redrawY2)
2329 || (itemPtr->x2 < canvasPtr->redrawX1)
2330 || (itemPtr->y2 < canvasPtr->redrawY1)) {
2334 if (itemPtr->state == TK_STATE_HIDDEN ||
2335 (itemPtr->state == TK_STATE_NULL &&
2336 canvasPtr->canvas_state == TK_STATE_HIDDEN)) {
2339 (*itemPtr->typePtr->displayProc)((Tk_Canvas) canvasPtr, itemPtr,
2340 canvasPtr->display, pixmap, screenX1, screenY1, width,
2345 * Copy from the temporary pixmap to the screen, then free up
2346 * the temporary pixmap.
2349 XCopyArea(Tk_Display(tkwin), pixmap, Tk_WindowId(tkwin),
2350 canvasPtr->pixmapGC,
2351 screenX1 - canvasPtr->drawableXOrigin,
2352 screenY1 - canvasPtr->drawableYOrigin,
2353 (unsigned) (screenX2 - screenX1),
2354 (unsigned) (screenY2 - screenY1),
2355 screenX1 - canvasPtr->xOrigin, screenY1 - canvasPtr->yOrigin);
2356 Tk_FreePixmap(Tk_Display(tkwin), pixmap);
2360 * Draw the window borders, if needed.
2364 if (canvasPtr->flags & REDRAW_BORDERS) {
2365 canvasPtr->flags &= ~REDRAW_BORDERS;
2366 if (canvasPtr->borderWidth > 0) {
2367 Tk_Draw3DRectangle(tkwin, Tk_WindowId(tkwin),
2368 canvasPtr->bgBorder, canvasPtr->highlightWidth,
2369 canvasPtr->highlightWidth,
2370 Tk_Width(tkwin) - 2*canvasPtr->highlightWidth,
2371 Tk_Height(tkwin) - 2*canvasPtr->highlightWidth,
2372 canvasPtr->borderWidth, canvasPtr->relief);
2374 if (canvasPtr->highlightWidth != 0) {
2377 bgGC = Tk_GCForColor(canvasPtr->highlightBgColorPtr,
2378 Tk_WindowId(tkwin));
2379 if (canvasPtr->textInfo.gotFocus) {
2380 fgGC = Tk_GCForColor(canvasPtr->highlightColorPtr,
2381 Tk_WindowId(tkwin));
2382 TkpDrawHighlightBorder(tkwin, fgGC, bgGC,
2383 canvasPtr->highlightWidth, Tk_WindowId(tkwin));
2385 TkpDrawHighlightBorder(tkwin, bgGC, bgGC,
2386 canvasPtr->highlightWidth, Tk_WindowId(tkwin));
2392 canvasPtr->flags &= ~(REDRAW_PENDING|BBOX_NOT_EMPTY);
2393 canvasPtr->redrawX1 = canvasPtr->redrawX2 = 0;
2394 canvasPtr->redrawY1 = canvasPtr->redrawY2 = 0;
2395 if (canvasPtr->flags & UPDATE_SCROLLBARS) {
2396 CanvasUpdateScrollbars(canvasPtr);
2401 *--------------------------------------------------------------
2403 * CanvasEventProc --
2405 * This procedure is invoked by the Tk dispatcher for various
2406 * events on canvases.
2412 * When the window gets deleted, internal structures get
2413 * cleaned up. When it gets exposed, it is redisplayed.
2415 *--------------------------------------------------------------
2419 CanvasEventProc(clientData, eventPtr)
2420 ClientData clientData; /* Information about window. */
2421 XEvent *eventPtr; /* Information about event. */
2423 TkCanvas *canvasPtr = (TkCanvas *) clientData;
2425 if (eventPtr->type == Expose) {
2428 x = eventPtr->xexpose.x + canvasPtr->xOrigin;
2429 y = eventPtr->xexpose.y + canvasPtr->yOrigin;
2430 Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr, x, y,
2431 x + eventPtr->xexpose.width,
2432 y + eventPtr->xexpose.height);
2433 if ((eventPtr->xexpose.x < canvasPtr->inset)
2434 || (eventPtr->xexpose.y < canvasPtr->inset)
2435 || ((eventPtr->xexpose.x + eventPtr->xexpose.width)
2436 > (Tk_Width(canvasPtr->tkwin) - canvasPtr->inset))
2437 || ((eventPtr->xexpose.y + eventPtr->xexpose.height)
2438 > (Tk_Height(canvasPtr->tkwin) - canvasPtr->inset))) {
2439 canvasPtr->flags |= REDRAW_BORDERS;
2441 } else if (eventPtr->type == DestroyNotify) {
2442 if (canvasPtr->tkwin != NULL) {
2443 canvasPtr->tkwin = NULL;
2444 Tcl_DeleteCommandFromToken(canvasPtr->interp,
2445 canvasPtr->widgetCmd);
2447 if (canvasPtr->flags & REDRAW_PENDING) {
2448 Tcl_CancelIdleCall(DisplayCanvas, (ClientData) canvasPtr);
2450 Tcl_EventuallyFree((ClientData) canvasPtr,
2451 (Tcl_FreeProc *) DestroyCanvas);
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 CONST 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 * ScrollFractions --
5210 * Given the range that's visible in the window and the "100%
5211 * range" for what's in the canvas, return a list of two
5212 * doubles representing the scroll fractions. This procedure
5213 * is used for both x and y scrolling.
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 ScrollFractions(screen1, screen2, object1, object2)
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. */
5233 double range, f1, f2;
5234 char buffer[2*TCL_DOUBLE_SPACE+2];
5236 range = object2 - object1;
5241 f1 = (screen1 - object1)/range;
5245 f2 = (screen2 - object1)/range;
5253 sprintf(buffer, "%g %g", f1, f2);
5254 return Tcl_NewStringObj(buffer, -1);
5258 *--------------------------------------------------------------
5260 * CanvasUpdateScrollbars --
5262 * This procedure is invoked whenever a canvas has changed in
5263 * a way that requires scrollbars to be redisplayed (e.g. the
5264 * view in the canvas has changed).
5270 * If there are scrollbars associated with the canvas, then
5271 * their scrolling commands are invoked to cause them to
5272 * redisplay. If errors occur, additional Tcl commands may
5273 * be invoked to process the errors.
5275 *--------------------------------------------------------------
5279 CanvasUpdateScrollbars(canvasPtr)
5280 TkCanvas *canvasPtr; /* Information about canvas. */
5284 int xOrigin, yOrigin, inset, width, height, scrollX1, scrollX2,
5286 char *xScrollCmd, *yScrollCmd;
5289 * Save all the relevant values from the canvasPtr, because it might be
5290 * deleted as part of either of the two calls to Tcl_VarEval below.
5293 interp = canvasPtr->interp;
5294 Tcl_Preserve((ClientData) interp);
5295 xScrollCmd = canvasPtr->xScrollCmd;
5296 if (xScrollCmd != (char *) NULL) {
5297 Tcl_Preserve((ClientData) xScrollCmd);
5299 yScrollCmd = canvasPtr->yScrollCmd;
5300 if (yScrollCmd != (char *) NULL) {
5301 Tcl_Preserve((ClientData) yScrollCmd);
5303 xOrigin = canvasPtr->xOrigin;
5304 yOrigin = canvasPtr->yOrigin;
5305 inset = canvasPtr->inset;
5306 width = Tk_Width(canvasPtr->tkwin);
5307 height = Tk_Height(canvasPtr->tkwin);
5308 scrollX1 = canvasPtr->scrollX1;
5309 scrollX2 = canvasPtr->scrollX2;
5310 scrollY1 = canvasPtr->scrollY1;
5311 scrollY2 = canvasPtr->scrollY2;
5312 canvasPtr->flags &= ~UPDATE_SCROLLBARS;
5313 if (canvasPtr->xScrollCmd != NULL) {
5314 Tcl_Obj *fractions = ScrollFractions(xOrigin + inset,
5315 xOrigin + width - inset, scrollX1, scrollX2);
5316 result = Tcl_VarEval(interp, xScrollCmd, " ",
5317 Tcl_GetString(fractions), (char *) NULL);
5318 Tcl_DecrRefCount(fractions);
5319 if (result != TCL_OK) {
5320 Tcl_BackgroundError(interp);
5322 Tcl_ResetResult(interp);
5323 Tcl_Release((ClientData) xScrollCmd);
5326 if (yScrollCmd != NULL) {
5327 Tcl_Obj *fractions = ScrollFractions(yOrigin + inset,
5328 yOrigin + height - inset, scrollY1, scrollY2);
5329 result = Tcl_VarEval(interp, yScrollCmd, " ",
5330 Tcl_GetString(fractions), (char *) NULL);
5331 Tcl_DecrRefCount(fractions);
5332 if (result != TCL_OK) {
5333 Tcl_BackgroundError(interp);
5335 Tcl_ResetResult(interp);
5336 Tcl_Release((ClientData) yScrollCmd);
5338 Tcl_Release((ClientData) interp);
5342 *--------------------------------------------------------------
5344 * CanvasSetOrigin --
5346 * This procedure is invoked to change the mapping between
5347 * canvas coordinates and screen coordinates in the canvas
5354 * The canvas will be redisplayed to reflect the change in
5355 * view. In addition, scrollbars will be updated if there
5358 *--------------------------------------------------------------
5362 CanvasSetOrigin(canvasPtr, xOrigin, yOrigin)
5363 TkCanvas *canvasPtr; /* Information about canvas. */
5364 int xOrigin; /* New X origin for canvas (canvas x-coord
5365 * corresponding to left edge of canvas
5367 int yOrigin; /* New Y origin for canvas (canvas y-coord
5368 * corresponding to top edge of canvas
5371 int left, right, top, bottom, delta;
5374 * If scroll increments have been set, round the window origin
5375 * to the nearest multiple of the increments. Remember, the
5376 * origin is the place just inside the borders, not the upper
5380 if (canvasPtr->xScrollIncrement > 0) {
5382 xOrigin += canvasPtr->xScrollIncrement/2;
5383 xOrigin -= (xOrigin + canvasPtr->inset)
5384 % canvasPtr->xScrollIncrement;
5386 xOrigin = (-xOrigin) + canvasPtr->xScrollIncrement/2;
5387 xOrigin = -(xOrigin - (xOrigin - canvasPtr->inset)
5388 % canvasPtr->xScrollIncrement);
5391 if (canvasPtr->yScrollIncrement > 0) {
5393 yOrigin += canvasPtr->yScrollIncrement/2;
5394 yOrigin -= (yOrigin + canvasPtr->inset)
5395 % canvasPtr->yScrollIncrement;
5397 yOrigin = (-yOrigin) + canvasPtr->yScrollIncrement/2;
5398 yOrigin = -(yOrigin - (yOrigin - canvasPtr->inset)
5399 % canvasPtr->yScrollIncrement);
5404 * Adjust the origin if necessary to keep as much as possible of the
5405 * canvas in the view. The variables left, right, etc. keep track of
5406 * how much extra space there is on each side of the view before it
5407 * will stick out past the scroll region. If one side sticks out past
5408 * the edge of the scroll region, adjust the view to bring that side
5409 * back to the edge of the scrollregion (but don't move it so much that
5410 * the other side sticks out now). If scroll increments are in effect,
5411 * be sure to adjust only by full increments.
5414 if ((canvasPtr->confine) && (canvasPtr->regionString != NULL)) {
5415 left = xOrigin + canvasPtr->inset - canvasPtr->scrollX1;
5416 right = canvasPtr->scrollX2
5417 - (xOrigin + Tk_Width(canvasPtr->tkwin) - canvasPtr->inset);
5418 top = yOrigin + canvasPtr->inset - canvasPtr->scrollY1;
5419 bottom = canvasPtr->scrollY2
5420 - (yOrigin + Tk_Height(canvasPtr->tkwin) - canvasPtr->inset);
5421 if ((left < 0) && (right > 0)) {
5422 delta = (right > -left) ? -left : right;
5423 if (canvasPtr->xScrollIncrement > 0) {
5424 delta -= delta % canvasPtr->xScrollIncrement;
5427 } else if ((right < 0) && (left > 0)) {
5428 delta = (left > -right) ? -right : left;
5429 if (canvasPtr->xScrollIncrement > 0) {
5430 delta -= delta % canvasPtr->xScrollIncrement;
5434 if ((top < 0) && (bottom > 0)) {
5435 delta = (bottom > -top) ? -top : bottom;
5436 if (canvasPtr->yScrollIncrement > 0) {
5437 delta -= delta % canvasPtr->yScrollIncrement;
5440 } else if ((bottom < 0) && (top > 0)) {
5441 delta = (top > -bottom) ? -bottom : top;
5442 if (canvasPtr->yScrollIncrement > 0) {
5443 delta -= delta % canvasPtr->yScrollIncrement;
5449 if ((xOrigin == canvasPtr->xOrigin) && (yOrigin == canvasPtr->yOrigin)) {
5454 * Tricky point: must redisplay not only everything that's visible
5455 * in the window's final configuration, but also everything that was
5456 * visible in the initial configuration. This is needed because some
5457 * item types, like windows, need to know when they move off-screen
5458 * so they can explicitly undisplay themselves.
5461 Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
5462 canvasPtr->xOrigin, canvasPtr->yOrigin,
5463 canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin),
5464 canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin));
5465 canvasPtr->xOrigin = xOrigin;
5466 canvasPtr->yOrigin = yOrigin;
5467 canvasPtr->flags |= UPDATE_SCROLLBARS;
5468 Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
5469 canvasPtr->xOrigin, canvasPtr->yOrigin,
5470 canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin),
5471 canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin));
5475 *----------------------------------------------------------------------
5477 * GetStringsFromObjs
5480 * Converts object list into string list.
5483 * Memory is allocated for the argv array, which must
5484 * be freed using ckfree() when no longer needed.
5486 *----------------------------------------------------------------------
5489 static CONST char **
5490 GetStringsFromObjs(argc, objv)
5492 Tcl_Obj *CONST objv[];
5499 argv = (CONST char **) ckalloc((argc+1) * sizeof(char *));
5500 for (i = 0; i < argc; i++) {
5501 argv[i]=Tcl_GetStringFromObj(objv[i], (int *) NULL);
5508 *--------------------------------------------------------------
5510 * Tk_CanvasPsColor --
5512 * This procedure is called by individual canvas items when
5513 * they want to set a color value for output. Given information
5514 * about an X color, this procedure will generate Postscript
5515 * commands to set up an appropriate color in Postscript.
5518 * Returns a standard Tcl return value. If an error occurs
5519 * then an error message will be left in interp->result.
5520 * If no error occurs, then additional Postscript will be
5521 * appended to interp->result.
5526 *--------------------------------------------------------------
5530 Tk_CanvasPsColor(interp, canvas, colorPtr)
5531 Tcl_Interp *interp; /* Interpreter for returning Postscript
5532 * or error message. */
5533 Tk_Canvas canvas; /* Information about canvas. */
5534 XColor *colorPtr; /* Information about color. */
5536 return Tk_PostscriptColor(interp, ((TkCanvas *) canvas)->psInfo,
5541 *--------------------------------------------------------------
5543 * Tk_CanvasPsFont --
5545 * This procedure is called by individual canvas items when
5546 * they want to output text. Given information about an X
5547 * font, this procedure will generate Postscript commands
5548 * to set up an appropriate font in Postscript.
5551 * Returns a standard Tcl return value. If an error occurs
5552 * then an error message will be left in interp->result.
5553 * If no error occurs, then additional Postscript will be
5554 * appended to the interp->result.
5557 * The Postscript font name is entered into psInfoPtr->fontTable
5558 * if it wasn't already there.
5560 *--------------------------------------------------------------
5564 Tk_CanvasPsFont(interp, canvas, tkfont)
5565 Tcl_Interp *interp; /* Interpreter for returning Postscript
5566 * or error message. */
5567 Tk_Canvas canvas; /* Information about canvas. */
5568 Tk_Font tkfont; /* Information about font in which text
5569 * is to be printed. */
5571 return Tk_PostscriptFont(interp, ((TkCanvas *) canvas)->psInfo, tkfont);
5575 *--------------------------------------------------------------
5577 * Tk_CanvasPsBitmap --
5579 * This procedure is called to output the contents of a
5580 * sub-region of a bitmap in proper image data format for
5581 * Postscript (i.e. data between angle brackets, one bit
5585 * Returns a standard Tcl return value. If an error occurs
5586 * then an error message will be left in interp->result.
5587 * If no error occurs, then additional Postscript will be
5588 * appended to interp->result.
5593 *--------------------------------------------------------------
5597 Tk_CanvasPsBitmap(interp, canvas, bitmap, startX, startY, width, height)
5598 Tcl_Interp *interp; /* Interpreter for returning Postscript
5599 * or error message. */
5600 Tk_Canvas canvas; /* Information about canvas. */
5601 Pixmap bitmap; /* Bitmap for which to generate
5603 int startX, startY; /* Coordinates of upper-left corner
5604 * of rectangular region to output. */
5605 int width, height; /* Height of rectangular region. */
5607 return Tk_PostscriptBitmap(interp, ((TkCanvas *) canvas)->tkwin,
5608 ((TkCanvas *) canvas)->psInfo, bitmap, startX, startY,
5613 *--------------------------------------------------------------
5615 * Tk_CanvasPsStipple --
5617 * This procedure is called by individual canvas items when
5618 * they have created a path that they'd like to be filled with
5619 * a stipple pattern. Given information about an X bitmap,
5620 * this procedure will generate Postscript commands to fill
5621 * the current clip region using a stipple pattern defined by the
5625 * Returns a standard Tcl return value. If an error occurs
5626 * then an error message will be left in interp->result.
5627 * If no error occurs, then additional Postscript will be
5628 * appended to interp->result.
5633 *--------------------------------------------------------------
5637 Tk_CanvasPsStipple(interp, canvas, bitmap)
5638 Tcl_Interp *interp; /* Interpreter for returning Postscript
5639 * or error message. */
5640 Tk_Canvas canvas; /* Information about canvas. */
5641 Pixmap bitmap; /* Bitmap to use for stippling. */
5643 return Tk_PostscriptStipple(interp, ((TkCanvas *) canvas)->tkwin,
5644 ((TkCanvas *) canvas)->psInfo, bitmap);
5648 *--------------------------------------------------------------
5652 * Given a y-coordinate in canvas coordinates, this procedure
5653 * returns a y-coordinate to use for Postscript output.
5656 * Returns the Postscript coordinate that corresponds to
5662 *--------------------------------------------------------------
5666 Tk_CanvasPsY(canvas, y)
5667 Tk_Canvas canvas; /* Token for canvas on whose behalf
5668 * Postscript is being generated. */
5669 double y; /* Y-coordinate in canvas coords. */
5671 return Tk_PostscriptY(y, ((TkCanvas *) canvas)->psInfo);
5675 *--------------------------------------------------------------
5677 * Tk_CanvasPsPath --
5679 * Given an array of points for a path, generate Postscript
5680 * commands to create the path.
5683 * Postscript commands get appended to what's in interp->result.
5688 *--------------------------------------------------------------
5692 Tk_CanvasPsPath(interp, canvas, coordPtr, numPoints)
5693 Tcl_Interp *interp; /* Put generated Postscript in this
5694 * interpreter's result field. */
5695 Tk_Canvas canvas; /* Canvas on whose behalf Postscript
5696 * is being generated. */
5697 double *coordPtr; /* Pointer to first in array of
5698 * 2*numPoints coordinates giving
5699 * points for path. */
5700 int numPoints; /* Number of points at *coordPtr. */
5702 Tk_PostscriptPath(interp, ((TkCanvas *) canvas)->psInfo,
5703 coordPtr, numPoints);