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