/[dtapublic]/projs/trunk/shared_source/tk_base/tkcanvas.c
ViewVC logotype

Contents of /projs/trunk/shared_source/tk_base/tkcanvas.c

Parent Directory Parent Directory | Revision Log Revision Log


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

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25