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