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

Annotation of /projs/trunk/shared_source/c_tk_base_7_5_w_mods/tkcanvas.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 42 - (hide annotations) (download)
Fri Oct 14 01:50:00 2016 UTC (7 years, 5 months ago) by dashley
Original Path: projs/trunk/shared_source/tk_base/tkcanvas.c
File MIME type: text/plain
File size: 180368 byte(s)
Move shared source code to commonize.
1 dashley 25 /* $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