OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [tags/] [start/] [insight/] [tk/] [generic/] [tkCanvas.c] - Blame information for rev 1765

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 578 markom
/*
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-1995 Sun Microsystems, Inc.
10
 * Copyright (c) 1998 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.1.1.1 2002-01-16 10:25:51 markom Exp $
16
 */
17
 
18
#include "default.h"
19
#include "tkInt.h"
20
#include "tkPort.h"
21
#include "tkCanvas.h"
22
 
23
/*
24
 * See tkCanvas.h for key data structures used to implement canvases.
25
 */
26
 
27
/*
28
 * The structure defined below is used to keep track of a tag search
29
 * in progress.  No field should be accessed by anyone other than
30
 * StartTagSearch and NextItem.
31
 */
32
 
33
typedef struct TagSearch {
34
    TkCanvas *canvasPtr;        /* Canvas widget being searched. */
35
    Tk_Uid tag;                 /* Tag to search for.   0 means return
36
                                 * all items. */
37
    Tk_Item *currentPtr;        /* Pointer to last item returned. */
38
    Tk_Item *lastPtr;           /* The item right before the currentPtr
39
                                 * is tracked so if the currentPtr is
40
                                 * deleted we don't have to start from the
41
                                 * beginning. */
42
    int searchOver;             /* Non-zero means NextItem should always
43
                                 * return NULL. */
44
} TagSearch;
45
 
46
/*
47
 * Information used for argv parsing.
48
 */
49
 
50
static Tk_ConfigSpec configSpecs[] = {
51
    {TK_CONFIG_BORDER, "-background", "background", "Background",
52
        DEF_CANVAS_BG_COLOR, Tk_Offset(TkCanvas, bgBorder),
53
        TK_CONFIG_COLOR_ONLY},
54
    {TK_CONFIG_BORDER, "-background", "background", "Background",
55
        DEF_CANVAS_BG_MONO, Tk_Offset(TkCanvas, bgBorder),
56
        TK_CONFIG_MONO_ONLY},
57
    {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
58
        (char *) NULL, 0, 0},
59
    {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
60
        (char *) NULL, 0, 0},
61
    {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
62
        DEF_CANVAS_BORDER_WIDTH, Tk_Offset(TkCanvas, borderWidth), 0},
63
    {TK_CONFIG_DOUBLE, "-closeenough", "closeEnough", "CloseEnough",
64
        DEF_CANVAS_CLOSE_ENOUGH, Tk_Offset(TkCanvas, closeEnough), 0},
65
    {TK_CONFIG_BOOLEAN, "-confine", "confine", "Confine",
66
        DEF_CANVAS_CONFINE, Tk_Offset(TkCanvas, confine), 0},
67
    {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
68
        DEF_CANVAS_CURSOR, Tk_Offset(TkCanvas, cursor), TK_CONFIG_NULL_OK},
69
    {TK_CONFIG_PIXELS, "-height", "height", "Height",
70
        DEF_CANVAS_HEIGHT, Tk_Offset(TkCanvas, height), 0},
71
    {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
72
        "HighlightBackground", DEF_CANVAS_HIGHLIGHT_BG,
73
        Tk_Offset(TkCanvas, highlightBgColorPtr), 0},
74
    {TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
75
        DEF_CANVAS_HIGHLIGHT, Tk_Offset(TkCanvas, highlightColorPtr), 0},
76
    {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
77
        "HighlightThickness",
78
        DEF_CANVAS_HIGHLIGHT_WIDTH, Tk_Offset(TkCanvas, highlightWidth), 0},
79
    {TK_CONFIG_BORDER, "-insertbackground", "insertBackground", "Foreground",
80
        DEF_CANVAS_INSERT_BG, Tk_Offset(TkCanvas, textInfo.insertBorder), 0},
81
    {TK_CONFIG_PIXELS, "-insertborderwidth", "insertBorderWidth", "BorderWidth",
82
        DEF_CANVAS_INSERT_BD_COLOR,
83
        Tk_Offset(TkCanvas, textInfo.insertBorderWidth), TK_CONFIG_COLOR_ONLY},
84
    {TK_CONFIG_PIXELS, "-insertborderwidth", "insertBorderWidth", "BorderWidth",
85
        DEF_CANVAS_INSERT_BD_MONO,
86
        Tk_Offset(TkCanvas, textInfo.insertBorderWidth), TK_CONFIG_MONO_ONLY},
87
    {TK_CONFIG_INT, "-insertofftime", "insertOffTime", "OffTime",
88
        DEF_CANVAS_INSERT_OFF_TIME, Tk_Offset(TkCanvas, insertOffTime), 0},
89
    {TK_CONFIG_INT, "-insertontime", "insertOnTime", "OnTime",
90
        DEF_CANVAS_INSERT_ON_TIME, Tk_Offset(TkCanvas, insertOnTime), 0},
91
    {TK_CONFIG_PIXELS, "-insertwidth", "insertWidth", "InsertWidth",
92
        DEF_CANVAS_INSERT_WIDTH, Tk_Offset(TkCanvas, textInfo.insertWidth), 0},
93
    {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
94
        DEF_CANVAS_RELIEF, Tk_Offset(TkCanvas, relief), 0},
95
    {TK_CONFIG_STRING, "-scrollregion", "scrollRegion", "ScrollRegion",
96
        DEF_CANVAS_SCROLL_REGION, Tk_Offset(TkCanvas, regionString),
97
        TK_CONFIG_NULL_OK},
98
    {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
99
        DEF_CANVAS_SELECT_COLOR, Tk_Offset(TkCanvas, textInfo.selBorder),
100
        TK_CONFIG_COLOR_ONLY},
101
    {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
102
        DEF_CANVAS_SELECT_MONO, Tk_Offset(TkCanvas, textInfo.selBorder),
103
        TK_CONFIG_MONO_ONLY},
104
    {TK_CONFIG_PIXELS, "-selectborderwidth", "selectBorderWidth", "BorderWidth",
105
        DEF_CANVAS_SELECT_BD_COLOR,
106
        Tk_Offset(TkCanvas, textInfo.selBorderWidth), TK_CONFIG_COLOR_ONLY},
107
    {TK_CONFIG_PIXELS, "-selectborderwidth", "selectBorderWidth", "BorderWidth",
108
        DEF_CANVAS_SELECT_BD_MONO, Tk_Offset(TkCanvas, textInfo.selBorderWidth),
109
        TK_CONFIG_MONO_ONLY},
110
    {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
111
        DEF_CANVAS_SELECT_FG_COLOR, Tk_Offset(TkCanvas, textInfo.selFgColorPtr),
112
        TK_CONFIG_COLOR_ONLY},
113
    {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
114
        DEF_CANVAS_SELECT_FG_MONO, Tk_Offset(TkCanvas, textInfo.selFgColorPtr),
115
        TK_CONFIG_MONO_ONLY},
116
    {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
117
        DEF_CANVAS_TAKE_FOCUS, Tk_Offset(TkCanvas, takeFocus),
118
        TK_CONFIG_NULL_OK},
119
    {TK_CONFIG_PIXELS, "-width", "width", "Width",
120
        DEF_CANVAS_WIDTH, Tk_Offset(TkCanvas, width), 0},
121
    {TK_CONFIG_STRING, "-xscrollcommand", "xScrollCommand", "ScrollCommand",
122
        DEF_CANVAS_X_SCROLL_CMD, Tk_Offset(TkCanvas, xScrollCmd),
123
        TK_CONFIG_NULL_OK},
124
    {TK_CONFIG_PIXELS, "-xscrollincrement", "xScrollIncrement",
125
        "ScrollIncrement",
126
        DEF_CANVAS_X_SCROLL_INCREMENT, Tk_Offset(TkCanvas, xScrollIncrement),
127
        0},
128
    {TK_CONFIG_STRING, "-yscrollcommand", "yScrollCommand", "ScrollCommand",
129
        DEF_CANVAS_Y_SCROLL_CMD, Tk_Offset(TkCanvas, yScrollCmd),
130
        TK_CONFIG_NULL_OK},
131
    {TK_CONFIG_PIXELS, "-yscrollincrement", "yScrollIncrement",
132
        "ScrollIncrement",
133
        DEF_CANVAS_Y_SCROLL_INCREMENT, Tk_Offset(TkCanvas, yScrollIncrement),
134
        0},
135
    {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
136
        (char *) NULL, 0, 0}
137
};
138
 
139
/*
140
 * List of all the item types known at present:
141
 */
142
 
143
static Tk_ItemType *typeList = NULL;    /* NULL means initialization hasn't
144
                                         * been done yet. */
145
 
146
/*
147
 * Standard item types provided by Tk:
148
 */
149
 
150
extern Tk_ItemType tkArcType, tkBitmapType, tkImageType, tkLineType;
151
extern Tk_ItemType tkOvalType, tkPolygonType;
152
extern Tk_ItemType tkRectangleType, tkTextType, tkWindowType;
153
 
154
/*
155
 * Various Tk_Uid's used by this module (set up during initialization):
156
 */
157
 
158
static Tk_Uid allUid = NULL;
159
static Tk_Uid currentUid = NULL;
160
 
161
/*
162
 * Statistics counters:
163
 */
164
 
165
static int numIdSearches;
166
static int numSlowSearches;
167
 
168
/*
169
 * Prototypes for procedures defined later in this file:
170
 */
171
 
172
static void             CanvasBindProc _ANSI_ARGS_((ClientData clientData,
173
                            XEvent *eventPtr));
174
static void             CanvasBlinkProc _ANSI_ARGS_((ClientData clientData));
175
static void             CanvasCmdDeletedProc _ANSI_ARGS_((
176
                            ClientData clientData));
177
static void             CanvasDoEvent _ANSI_ARGS_((TkCanvas *canvasPtr,
178
                            XEvent *eventPtr));
179
static void             CanvasEventProc _ANSI_ARGS_((ClientData clientData,
180
                            XEvent *eventPtr));
181
static int              CanvasFetchSelection _ANSI_ARGS_((
182
                            ClientData clientData, int offset,
183
                            char *buffer, int maxBytes));
184
static Tk_Item *        CanvasFindClosest _ANSI_ARGS_((TkCanvas *canvasPtr,
185
                            double coords[2]));
186
static void             CanvasFocusProc _ANSI_ARGS_((TkCanvas *canvasPtr,
187
                            int gotFocus));
188
static void             CanvasLostSelection _ANSI_ARGS_((
189
                            ClientData clientData));
190
static void             CanvasSelectTo _ANSI_ARGS_((TkCanvas *canvasPtr,
191
                            Tk_Item *itemPtr, int index));
192
static void             CanvasSetOrigin _ANSI_ARGS_((TkCanvas *canvasPtr,
193
                            int xOrigin, int yOrigin));
194
static void             CanvasUpdateScrollbars _ANSI_ARGS_((
195
                            TkCanvas *canvasPtr));
196
static int              CanvasWidgetCmd _ANSI_ARGS_((ClientData clientData,
197
                            Tcl_Interp *interp, int argc, char **argv));
198
static void             CanvasWorldChanged _ANSI_ARGS_((
199
                            ClientData instanceData));
200
static int              ConfigureCanvas _ANSI_ARGS_((Tcl_Interp *interp,
201
                            TkCanvas *canvasPtr, int argc, char **argv,
202
                            int flags));
203
static void             DestroyCanvas _ANSI_ARGS_((char *memPtr));
204
static void             DisplayCanvas _ANSI_ARGS_((ClientData clientData));
205
static void             DoItem _ANSI_ARGS_((Tcl_Interp *interp,
206
                            Tk_Item *itemPtr, Tk_Uid tag));
207
static int              FindItems _ANSI_ARGS_((Tcl_Interp *interp,
208
                            TkCanvas *canvasPtr, int argc, char **argv,
209
                            char *newTag, char *cmdName, char *option));
210
static int              FindArea _ANSI_ARGS_((Tcl_Interp *interp,
211
                            TkCanvas *canvasPtr, char **argv, Tk_Uid uid,
212
                            int enclosed));
213
static double           GridAlign _ANSI_ARGS_((double coord, double spacing));
214
static void             InitCanvas _ANSI_ARGS_((void));
215
static Tk_Item *        NextItem _ANSI_ARGS_((TagSearch *searchPtr));
216
static void             PickCurrentItem _ANSI_ARGS_((TkCanvas *canvasPtr,
217
                            XEvent *eventPtr));
218
static void             PrintScrollFractions _ANSI_ARGS_((int screen1,
219
                            int screen2, int object1, int object2,
220
                            char *string));
221
static void             RelinkItems _ANSI_ARGS_((TkCanvas *canvasPtr,
222
                            char *tag, Tk_Item *prevPtr));
223
static Tk_Item *        StartTagSearch _ANSI_ARGS_((TkCanvas *canvasPtr,
224
                            char *tag, TagSearch *searchPtr));
225
 
226
/*
227
 * The structure below defines canvas class behavior by means of procedures
228
 * that can be invoked from generic window code.
229
 */
230
 
231
static TkClassProcs canvasClass = {
232
    NULL,                       /* createProc. */
233
    CanvasWorldChanged,         /* geometryProc. */
234
    NULL                        /* modalProc. */
235
};
236
 
237
 
238
/*
239
 *--------------------------------------------------------------
240
 *
241
 * Tk_CanvasCmd --
242
 *
243
 *      This procedure is invoked to process the "canvas" Tcl
244
 *      command.  See the user documentation for details on what
245
 *      it does.
246
 *
247
 * Results:
248
 *      A standard Tcl result.
249
 *
250
 * Side effects:
251
 *      See the user documentation.
252
 *
253
 *--------------------------------------------------------------
254
 */
255
 
256
int
257
Tk_CanvasCmd(clientData, interp, argc, argv)
258
    ClientData clientData;              /* Main window associated with
259
                                 * interpreter. */
260
    Tcl_Interp *interp;         /* Current interpreter. */
261
    int argc;                   /* Number of arguments. */
262
    char **argv;                /* Argument strings. */
263
{
264
    Tk_Window tkwin = (Tk_Window) clientData;
265
    TkCanvas *canvasPtr;
266
    Tk_Window new;
267
 
268
    if (typeList == NULL) {
269
        InitCanvas();
270
    }
271
 
272
    if (argc < 2) {
273
        Tcl_AppendResult(interp, "wrong # args: should be \"",
274
                argv[0], " pathName ?options?\"", (char *) NULL);
275
        return TCL_ERROR;
276
    }
277
 
278
    new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
279
    if (new == NULL) {
280
        return TCL_ERROR;
281
    }
282
 
283
    /*
284
     * Initialize fields that won't be initialized by ConfigureCanvas,
285
     * or which ConfigureCanvas expects to have reasonable values
286
     * (e.g. resource pointers).
287
     */
288
 
289
    canvasPtr = (TkCanvas *) ckalloc(sizeof(TkCanvas));
290
    canvasPtr->tkwin = new;
291
    canvasPtr->display = Tk_Display(new);
292
    canvasPtr->interp = interp;
293
    canvasPtr->widgetCmd = Tcl_CreateCommand(interp,
294
            Tk_PathName(canvasPtr->tkwin), CanvasWidgetCmd,
295
            (ClientData) canvasPtr, CanvasCmdDeletedProc);
296
    canvasPtr->firstItemPtr = NULL;
297
    canvasPtr->lastItemPtr = NULL;
298
    canvasPtr->borderWidth = 0;
299
    canvasPtr->bgBorder = NULL;
300
    canvasPtr->relief = TK_RELIEF_FLAT;
301
    canvasPtr->highlightWidth = 0;
302
    canvasPtr->highlightBgColorPtr = NULL;
303
    canvasPtr->highlightColorPtr = NULL;
304
    canvasPtr->inset = 0;
305
    canvasPtr->pixmapGC = None;
306
    canvasPtr->width = None;
307
    canvasPtr->height = None;
308
    canvasPtr->confine = 0;
309
    canvasPtr->textInfo.selBorder = NULL;
310
    canvasPtr->textInfo.selBorderWidth = 0;
311
    canvasPtr->textInfo.selFgColorPtr = NULL;
312
    canvasPtr->textInfo.selItemPtr = NULL;
313
    canvasPtr->textInfo.selectFirst = -1;
314
    canvasPtr->textInfo.selectLast = -1;
315
    canvasPtr->textInfo.anchorItemPtr = NULL;
316
    canvasPtr->textInfo.selectAnchor = 0;
317
    canvasPtr->textInfo.insertBorder = NULL;
318
    canvasPtr->textInfo.insertWidth = 0;
319
    canvasPtr->textInfo.insertBorderWidth = 0;
320
    canvasPtr->textInfo.focusItemPtr = NULL;
321
    canvasPtr->textInfo.gotFocus = 0;
322
    canvasPtr->textInfo.cursorOn = 0;
323
    canvasPtr->insertOnTime = 0;
324
    canvasPtr->insertOffTime = 0;
325
    canvasPtr->insertBlinkHandler = (Tcl_TimerToken) NULL;
326
    canvasPtr->xOrigin = canvasPtr->yOrigin = 0;
327
    canvasPtr->drawableXOrigin = canvasPtr->drawableYOrigin = 0;
328
    canvasPtr->bindingTable = NULL;
329
    canvasPtr->currentItemPtr = NULL;
330
    canvasPtr->newCurrentPtr = NULL;
331
    canvasPtr->closeEnough = 0.0;
332
    canvasPtr->pickEvent.type = LeaveNotify;
333
    canvasPtr->pickEvent.xcrossing.x = 0;
334
    canvasPtr->pickEvent.xcrossing.y = 0;
335
    canvasPtr->state = 0;
336
    canvasPtr->xScrollCmd = NULL;
337
    canvasPtr->yScrollCmd = NULL;
338
    canvasPtr->scrollX1 = 0;
339
    canvasPtr->scrollY1 = 0;
340
    canvasPtr->scrollX2 = 0;
341
    canvasPtr->scrollY2 = 0;
342
    canvasPtr->regionString = NULL;
343
    canvasPtr->xScrollIncrement = 0;
344
    canvasPtr->yScrollIncrement = 0;
345
    canvasPtr->scanX = 0;
346
    canvasPtr->scanXOrigin = 0;
347
    canvasPtr->scanY = 0;
348
    canvasPtr->scanYOrigin = 0;
349
    canvasPtr->hotPtr = NULL;
350
    canvasPtr->hotPrevPtr = NULL;
351
    canvasPtr->cursor = None;
352
    canvasPtr->takeFocus = NULL;
353
    canvasPtr->pixelsPerMM = WidthOfScreen(Tk_Screen(new));
354
    canvasPtr->pixelsPerMM /= WidthMMOfScreen(Tk_Screen(new));
355
    canvasPtr->flags = 0;
356
    canvasPtr->nextId = 1;
357
    canvasPtr->psInfoPtr = NULL;
358
    Tcl_InitHashTable(&canvasPtr->idTable, TCL_ONE_WORD_KEYS);
359
 
360
    Tk_SetClass(canvasPtr->tkwin, "Canvas");
361
    TkSetClassProcs(canvasPtr->tkwin, &canvasClass, (ClientData) canvasPtr);
362
    Tk_CreateEventHandler(canvasPtr->tkwin,
363
            ExposureMask|StructureNotifyMask|FocusChangeMask,
364
            CanvasEventProc, (ClientData) canvasPtr);
365
    Tk_CreateEventHandler(canvasPtr->tkwin, KeyPressMask|KeyReleaseMask
366
            |ButtonPressMask|ButtonReleaseMask|EnterWindowMask
367
            |LeaveWindowMask|PointerMotionMask|VirtualEventMask,
368
            CanvasBindProc, (ClientData) canvasPtr);
369
    Tk_CreateSelHandler(canvasPtr->tkwin, XA_PRIMARY, XA_STRING,
370
            CanvasFetchSelection, (ClientData) canvasPtr, XA_STRING);
371
    if (ConfigureCanvas(interp, canvasPtr, argc-2, argv+2, 0) != TCL_OK) {
372
        goto error;
373
    }
374
 
375
    interp->result = Tk_PathName(canvasPtr->tkwin);
376
    return TCL_OK;
377
 
378
    error:
379
    Tk_DestroyWindow(canvasPtr->tkwin);
380
    return TCL_ERROR;
381
}
382
 
383
/*
384
 *--------------------------------------------------------------
385
 *
386
 * CanvasWidgetCmd --
387
 *
388
 *      This procedure is invoked to process the Tcl command
389
 *      that corresponds to a widget managed by this module.
390
 *      See the user documentation for details on what it does.
391
 *
392
 * Results:
393
 *      A standard Tcl result.
394
 *
395
 * Side effects:
396
 *      See the user documentation.
397
 *
398
 *--------------------------------------------------------------
399
 */
400
 
401
static int
402
CanvasWidgetCmd(clientData, interp, argc, argv)
403
    ClientData clientData;              /* Information about canvas
404
                                         * widget. */
405
    Tcl_Interp *interp;                 /* Current interpreter. */
406
    int argc;                           /* Number of arguments. */
407
    char **argv;                        /* Argument strings. */
408
{
409
    TkCanvas *canvasPtr = (TkCanvas *) clientData;
410
    size_t length;
411
    int c, result;
412
    Tk_Item *itemPtr = NULL;            /* Initialization needed only to
413
                                         * prevent compiler warning. */
414
    TagSearch search;
415
 
416
    if (argc < 2) {
417
        Tcl_AppendResult(interp, "wrong # args: should be \"",
418
                argv[0], " option ?arg arg ...?\"", (char *) NULL);
419
        return TCL_ERROR;
420
    }
421
    Tcl_Preserve((ClientData) canvasPtr);
422
    result = TCL_OK;
423
    c = argv[1][0];
424
    length = strlen(argv[1]);
425
    if ((c == 'a') && (strncmp(argv[1], "addtag", length) == 0)) {
426
        if (argc < 4) {
427
            Tcl_AppendResult(interp, "wrong # args: should be \"",
428
                    argv[0], " addtags tag searchCommand ?arg arg ...?\"",
429
                    (char *) NULL);
430
            goto error;
431
        }
432
        result = FindItems(interp, canvasPtr, argc-3, argv+3, argv[2], argv[0],
433
                " addtag tag");
434
    } else if ((c == 'b') && (strncmp(argv[1], "bbox", length) == 0)
435
            && (length >= 2)) {
436
        int i, gotAny;
437
        int x1 = 0, y1 = 0, x2 = 0, y2 = 0; /* Initializations needed
438
                                                 * only to prevent compiler
439
                                                 * warnings. */
440
 
441
        if (argc < 3) {
442
            Tcl_AppendResult(interp, "wrong # args: should be \"",
443
                    argv[0], " bbox tagOrId ?tagOrId ...?\"",
444
                    (char *) NULL);
445
            goto error;
446
        }
447
        gotAny = 0;
448
        for (i = 2; i < argc; i++) {
449
            for (itemPtr = StartTagSearch(canvasPtr, argv[i], &search);
450
                    itemPtr != NULL; itemPtr = NextItem(&search)) {
451
                if ((itemPtr->x1 >= itemPtr->x2)
452
                        || (itemPtr->y1 >= itemPtr->y2)) {
453
                    continue;
454
                }
455
                if (!gotAny) {
456
                    x1 = itemPtr->x1;
457
                    y1 = itemPtr->y1;
458
                    x2 = itemPtr->x2;
459
                    y2 = itemPtr->y2;
460
                    gotAny = 1;
461
                } else {
462
                    if (itemPtr->x1 < x1) {
463
                        x1 = itemPtr->x1;
464
                    }
465
                    if (itemPtr->y1 < y1) {
466
                        y1 = itemPtr->y1;
467
                    }
468
                    if (itemPtr->x2 > x2) {
469
                        x2 = itemPtr->x2;
470
                    }
471
                    if (itemPtr->y2 > y2) {
472
                        y2 = itemPtr->y2;
473
                    }
474
                }
475
            }
476
        }
477
        if (gotAny) {
478
            sprintf(interp->result, "%d %d %d %d", x1, y1, x2, y2);
479
        }
480
    } else if ((c == 'b') && (strncmp(argv[1], "bind", length) == 0)
481
            && (length >= 2)) {
482
        ClientData object;
483
 
484
        if ((argc < 3) || (argc > 5)) {
485
            Tcl_AppendResult(interp, "wrong # args: should be \"",
486
                    argv[0], " bind tagOrId ?sequence? ?command?\"",
487
                    (char *) NULL);
488
            goto error;
489
        }
490
 
491
        /*
492
         * Figure out what object to use for the binding (individual
493
         * item vs. tag).
494
         */
495
 
496
        object = 0;
497
        if (isdigit(UCHAR(argv[2][0]))) {
498
            int id;
499
            char *end;
500
            Tcl_HashEntry *entryPtr;
501
 
502
            id = strtoul(argv[2], &end, 0);
503
            if (*end != 0) {
504
                goto bindByTag;
505
            }
506
            entryPtr = Tcl_FindHashEntry(&canvasPtr->idTable, (char *) id);
507
            if (entryPtr != NULL) {
508
                itemPtr = (Tk_Item *) Tcl_GetHashValue(entryPtr);
509
                object = (ClientData) itemPtr;
510
            }
511
 
512
            if (object == 0) {
513
                Tcl_AppendResult(interp, "item \"", argv[2],
514
                        "\" doesn't exist", (char *) NULL);
515
                goto error;
516
            }
517
        } else {
518
            bindByTag:
519
            object = (ClientData) Tk_GetUid(argv[2]);
520
        }
521
 
522
        /*
523
         * Make a binding table if the canvas doesn't already have
524
         * one.
525
         */
526
 
527
        if (canvasPtr->bindingTable == NULL) {
528
            canvasPtr->bindingTable = Tk_CreateBindingTable(interp);
529
        }
530
 
531
        if (argc == 5) {
532
            int append = 0;
533
            unsigned long mask;
534
 
535
            if (argv[4][0] == 0) {
536
                result = Tk_DeleteBinding(interp, canvasPtr->bindingTable,
537
                        object, argv[3]);
538
                goto done;
539
            }
540
            if (argv[4][0] == '+') {
541
                argv[4]++;
542
                append = 1;
543
            }
544
            mask = Tk_CreateBinding(interp, canvasPtr->bindingTable,
545
                    object, argv[3], argv[4], append);
546
            if (mask == 0) {
547
                goto error;
548
            }
549
            if (mask & (unsigned) ~(ButtonMotionMask|Button1MotionMask
550
                    |Button2MotionMask|Button3MotionMask|Button4MotionMask
551
                    |Button5MotionMask|ButtonPressMask|ButtonReleaseMask
552
                    |EnterWindowMask|LeaveWindowMask|KeyPressMask
553
                    |KeyReleaseMask|PointerMotionMask|VirtualEventMask)) {
554
                Tk_DeleteBinding(interp, canvasPtr->bindingTable,
555
                        object, argv[3]);
556
                Tcl_ResetResult(interp);
557
                Tcl_AppendResult(interp, "requested illegal events; ",
558
                        "only key, button, motion, enter, leave, and virtual ",
559
                        "events may be used", (char *) NULL);
560
                goto error;
561
            }
562
        } else if (argc == 4) {
563
            char *command;
564
 
565
            command = Tk_GetBinding(interp, canvasPtr->bindingTable,
566
                    object, argv[3]);
567
            if (command == NULL) {
568
                goto error;
569
            }
570
            interp->result = command;
571
        } else {
572
            Tk_GetAllBindings(interp, canvasPtr->bindingTable, object);
573
        }
574
    } else if ((c == 'c') && (strcmp(argv[1], "canvasx") == 0)) {
575
        int x;
576
        double grid;
577
 
578
        if ((argc < 3) || (argc > 4)) {
579
            Tcl_AppendResult(interp, "wrong # args: should be \"",
580
                    argv[0], " canvasx screenx ?gridspacing?\"",
581
                    (char *) NULL);
582
            goto error;
583
        }
584
        if (Tk_GetPixels(interp, canvasPtr->tkwin, argv[2], &x) != TCL_OK) {
585
            goto error;
586
        }
587
        if (argc == 4) {
588
            if (Tk_CanvasGetCoord(interp, (Tk_Canvas) canvasPtr, argv[3],
589
                    &grid) != TCL_OK) {
590
                goto error;
591
            }
592
        } else {
593
            grid = 0.0;
594
        }
595
        x += canvasPtr->xOrigin;
596
        Tcl_PrintDouble(interp, GridAlign((double) x, grid), interp->result);
597
    } else if ((c == 'c') && (strcmp(argv[1], "canvasy") == 0)) {
598
        int y;
599
        double grid;
600
 
601
        if ((argc < 3) || (argc > 4)) {
602
            Tcl_AppendResult(interp, "wrong # args: should be \"",
603
                    argv[0], " canvasy screeny ?gridspacing?\"",
604
                    (char *) NULL);
605
            goto error;
606
        }
607
        if (Tk_GetPixels(interp, canvasPtr->tkwin, argv[2], &y) != TCL_OK) {
608
            goto error;
609
        }
610
        if (argc == 4) {
611
            if (Tk_CanvasGetCoord(interp, (Tk_Canvas) canvasPtr,
612
                    argv[3], &grid) != TCL_OK) {
613
                goto error;
614
            }
615
        } else {
616
            grid = 0.0;
617
        }
618
        y += canvasPtr->yOrigin;
619
        Tcl_PrintDouble(interp, GridAlign((double) y, grid), interp->result);
620
    } else if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
621
            && (length >= 2)) {
622
        if (argc != 3) {
623
            Tcl_AppendResult(interp, "wrong # args: should be \"",
624
                    argv[0], " cget option\"",
625
                    (char *) NULL);
626
            goto error;
627
        }
628
        result = Tk_ConfigureValue(interp, canvasPtr->tkwin, configSpecs,
629
                (char *) canvasPtr, argv[2], 0);
630
    } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
631
            && (length >= 3)) {
632
        if (argc == 2) {
633
            result = Tk_ConfigureInfo(interp, canvasPtr->tkwin, configSpecs,
634
                    (char *) canvasPtr, (char *) NULL, 0);
635
        } else if (argc == 3) {
636
            result = Tk_ConfigureInfo(interp, canvasPtr->tkwin, configSpecs,
637
                    (char *) canvasPtr, argv[2], 0);
638
        } else {
639
            result = ConfigureCanvas(interp, canvasPtr, argc-2, argv+2,
640
                    TK_CONFIG_ARGV_ONLY);
641
        }
642
    } else if ((c == 'c') && (strncmp(argv[1], "coords", length) == 0)
643
            && (length >= 3)) {
644
        if (argc < 3) {
645
            Tcl_AppendResult(interp, "wrong # args: should be \"",
646
                    argv[0], " coords tagOrId ?x y x y ...?\"",
647
                    (char *) NULL);
648
            goto error;
649
        }
650
        itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
651
        if (itemPtr != NULL) {
652
            if (argc != 3) {
653
                Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
654
                        itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
655
            }
656
            if (itemPtr->typePtr->coordProc != NULL) {
657
                result = (*itemPtr->typePtr->coordProc)(interp,
658
                        (Tk_Canvas) canvasPtr, itemPtr, argc-3, argv+3);
659
            }
660
            if (argc != 3) {
661
                Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
662
                        itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
663
            }
664
        }
665
    } else if ((c == 'c') && (strncmp(argv[1], "create", length) == 0)
666
            && (length >= 2)) {
667
        Tk_ItemType *typePtr;
668
        Tk_ItemType *matchPtr = NULL;
669
        Tk_Item *itemPtr;
670
        int isNew = 0;
671
        Tcl_HashEntry *entryPtr;
672
 
673
        if (argc < 3) {
674
            Tcl_AppendResult(interp, "wrong # args: should be \"",
675
                    argv[0], " create type ?arg arg ...?\"", (char *) NULL);
676
            goto error;
677
        }
678
        c = argv[2][0];
679
        length = strlen(argv[2]);
680
        for (typePtr = typeList; typePtr != NULL; typePtr = typePtr->nextPtr) {
681
            if ((c == typePtr->name[0])
682
                    && (strncmp(argv[2], typePtr->name, length) == 0)) {
683
                if (matchPtr != NULL) {
684
                    badType:
685
                    Tcl_AppendResult(interp,
686
                            "unknown or ambiguous item type \"",
687
                            argv[2], "\"", (char *) NULL);
688
                    goto error;
689
                }
690
                matchPtr = typePtr;
691
            }
692
        }
693
        if (matchPtr == NULL) {
694
            goto badType;
695
        }
696
        typePtr = matchPtr;
697
        itemPtr = (Tk_Item *) ckalloc((unsigned) typePtr->itemSize);
698
        itemPtr->id = canvasPtr->nextId;
699
        canvasPtr->nextId++;
700
        itemPtr->tagPtr = itemPtr->staticTagSpace;
701
        itemPtr->tagSpace = TK_TAG_SPACE;
702
        itemPtr->numTags = 0;
703
        itemPtr->typePtr = typePtr;
704
        if ((*typePtr->createProc)(interp, (Tk_Canvas) canvasPtr,
705
                itemPtr, argc-3, argv+3) != TCL_OK) {
706
            ckfree((char *) itemPtr);
707
            goto error;
708
        }
709
        itemPtr->nextPtr = NULL;
710
        entryPtr = Tcl_CreateHashEntry(&canvasPtr->idTable,
711
                (char *) itemPtr->id, &isNew);
712
        Tcl_SetHashValue(entryPtr, itemPtr);
713
        itemPtr->prevPtr = canvasPtr->lastItemPtr;
714
        canvasPtr->hotPtr = itemPtr;
715
        canvasPtr->hotPrevPtr = canvasPtr->lastItemPtr;
716
        if (canvasPtr->lastItemPtr == NULL) {
717
            canvasPtr->firstItemPtr = itemPtr;
718
        } else {
719
            canvasPtr->lastItemPtr->nextPtr = itemPtr;
720
        }
721
        canvasPtr->lastItemPtr = itemPtr;
722
        Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
723
                itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
724
        canvasPtr->flags |= REPICK_NEEDED;
725
        sprintf(interp->result, "%d", itemPtr->id);
726
    } else if ((c == 'd') && (strncmp(argv[1], "dchars", length) == 0)
727
            && (length >= 2)) {
728
        int first, last;
729
 
730
        if ((argc != 4) && (argc != 5)) {
731
            Tcl_AppendResult(interp, "wrong # args: should be \"",
732
                    argv[0], " dchars tagOrId first ?last?\"",
733
                    (char *) NULL);
734
            goto error;
735
        }
736
        for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
737
                itemPtr != NULL; itemPtr = NextItem(&search)) {
738
            if ((itemPtr->typePtr->indexProc == NULL)
739
                    || (itemPtr->typePtr->dCharsProc == NULL)) {
740
                continue;
741
            }
742
            if ((*itemPtr->typePtr->indexProc)(interp, (Tk_Canvas) canvasPtr,
743
                    itemPtr, argv[3], &first) != TCL_OK) {
744
                goto error;
745
            }
746
            if (argc == 5) {
747
                if ((*itemPtr->typePtr->indexProc)(interp,
748
                        (Tk_Canvas) canvasPtr, itemPtr, argv[4], &last)
749
                        != TCL_OK) {
750
                    goto error;
751
                }
752
            } else {
753
                last = first;
754
            }
755
 
756
            /*
757
             * Redraw both item's old and new areas:  it's possible
758
             * that a delete could result in a new area larger than
759
             * the old area.
760
             */
761
 
762
            Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
763
                    itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
764
            (*itemPtr->typePtr->dCharsProc)((Tk_Canvas) canvasPtr,
765
                    itemPtr, first, last);
766
            Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
767
                    itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
768
        }
769
    } else if ((c == 'd') && (strncmp(argv[1], "delete", length) == 0)
770
            && (length >= 2)) {
771
        int i;
772
        Tcl_HashEntry *entryPtr;
773
 
774
        for (i = 2; i < argc; i++) {
775
            for (itemPtr = StartTagSearch(canvasPtr, argv[i], &search);
776
                itemPtr != NULL; itemPtr = NextItem(&search)) {
777
                Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
778
                        itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
779
                if (canvasPtr->bindingTable != NULL) {
780
                    Tk_DeleteAllBindings(canvasPtr->bindingTable,
781
                            (ClientData) itemPtr);
782
                }
783
                (*itemPtr->typePtr->deleteProc)((Tk_Canvas) canvasPtr, itemPtr,
784
                        canvasPtr->display);
785
                if (itemPtr->tagPtr != itemPtr->staticTagSpace) {
786
                    ckfree((char *) itemPtr->tagPtr);
787
                }
788
                entryPtr = Tcl_FindHashEntry(&canvasPtr->idTable,
789
                        (char *) itemPtr->id);
790
                Tcl_DeleteHashEntry(entryPtr);
791
                if (itemPtr->nextPtr != NULL) {
792
                    itemPtr->nextPtr->prevPtr = itemPtr->prevPtr;
793
                }
794
                if (itemPtr->prevPtr != NULL) {
795
                    itemPtr->prevPtr->nextPtr = itemPtr->nextPtr;
796
                }
797
                if (canvasPtr->firstItemPtr == itemPtr) {
798
                    canvasPtr->firstItemPtr = itemPtr->nextPtr;
799
                    if (canvasPtr->firstItemPtr == NULL) {
800
                        canvasPtr->lastItemPtr = NULL;
801
                    }
802
                }
803
                if (canvasPtr->lastItemPtr == itemPtr) {
804
                    canvasPtr->lastItemPtr = itemPtr->prevPtr;
805
                }
806
                ckfree((char *) itemPtr);
807
                if (itemPtr == canvasPtr->currentItemPtr) {
808
                    canvasPtr->currentItemPtr = NULL;
809
                    canvasPtr->flags |= REPICK_NEEDED;
810
                }
811
                if (itemPtr == canvasPtr->newCurrentPtr) {
812
                    canvasPtr->newCurrentPtr = NULL;
813
                    canvasPtr->flags |= REPICK_NEEDED;
814
                }
815
                if (itemPtr == canvasPtr->textInfo.focusItemPtr) {
816
                    canvasPtr->textInfo.focusItemPtr = NULL;
817
                }
818
                if (itemPtr == canvasPtr->textInfo.selItemPtr) {
819
                    canvasPtr->textInfo.selItemPtr = NULL;
820
                }
821
                if ((itemPtr == canvasPtr->hotPtr)
822
                        || (itemPtr == canvasPtr->hotPrevPtr)) {
823
                    canvasPtr->hotPtr = NULL;
824
                }
825
            }
826
        }
827
    } else if ((c == 'd') && (strncmp(argv[1], "dtag", length) == 0)
828
            && (length >= 2)) {
829
        Tk_Uid tag;
830
        int i;
831
 
832
        if ((argc != 3) && (argc != 4)) {
833
            Tcl_AppendResult(interp, "wrong # args: should be \"",
834
                    argv[0], " dtag tagOrId ?tagToDelete?\"",
835
                    (char *) NULL);
836
            goto error;
837
        }
838
        if (argc == 4) {
839
            tag = Tk_GetUid(argv[3]);
840
        } else {
841
            tag = Tk_GetUid(argv[2]);
842
        }
843
        for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
844
                itemPtr != NULL; itemPtr = NextItem(&search)) {
845
            for (i = itemPtr->numTags-1; i >= 0; i--) {
846
                if (itemPtr->tagPtr[i] == tag) {
847
                    itemPtr->tagPtr[i] = itemPtr->tagPtr[itemPtr->numTags-1];
848
                    itemPtr->numTags--;
849
                }
850
            }
851
        }
852
    } else if ((c == 'f') && (strncmp(argv[1], "find", length) == 0)
853
            && (length >= 2)) {
854
        if (argc < 3) {
855
            Tcl_AppendResult(interp, "wrong # args: should be \"",
856
                    argv[0], " find searchCommand ?arg arg ...?\"",
857
                    (char *) NULL);
858
            goto error;
859
        }
860
        result = FindItems(interp, canvasPtr, argc-2, argv+2, (char *) NULL,
861
                argv[0]," find");
862
    } else if ((c == 'f') && (strncmp(argv[1], "focus", length) == 0)
863
            && (length >= 2)) {
864
        if (argc > 3) {
865
            Tcl_AppendResult(interp, "wrong # args: should be \"",
866
                    argv[0], " focus ?tagOrId?\"",
867
                    (char *) NULL);
868
            goto error;
869
        }
870
        itemPtr = canvasPtr->textInfo.focusItemPtr;
871
        if (argc == 2) {
872
            if (itemPtr != NULL) {
873
                sprintf(interp->result, "%d", itemPtr->id);
874
            }
875
            goto done;
876
        }
877
        if ((itemPtr != NULL) && (canvasPtr->textInfo.gotFocus)) {
878
            Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
879
                    itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
880
        }
881
        if (argv[2][0] == 0) {
882
            canvasPtr->textInfo.focusItemPtr = NULL;
883
            goto done;
884
        }
885
        for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
886
                itemPtr != NULL; itemPtr = NextItem(&search)) {
887
            if (itemPtr->typePtr->icursorProc != NULL) {
888
                break;
889
            }
890
        }
891
        if (itemPtr == NULL) {
892
            goto done;
893
        }
894
        canvasPtr->textInfo.focusItemPtr = itemPtr;
895
        if (canvasPtr->textInfo.gotFocus) {
896
            Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
897
                    itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
898
        }
899
    } else if ((c == 'g') && (strncmp(argv[1], "gettags", length) == 0)) {
900
        if (argc != 3) {
901
            Tcl_AppendResult(interp, "wrong # args: should be \"",
902
                    argv[0], " gettags tagOrId\"", (char *) NULL);
903
            goto error;
904
        }
905
        itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
906
        if (itemPtr != NULL) {
907
            int i;
908
            for (i = 0; i < itemPtr->numTags; i++) {
909
                Tcl_AppendElement(interp, (char *) itemPtr->tagPtr[i]);
910
            }
911
        }
912
    } else if ((c == 'i') && (strncmp(argv[1], "icursor", length) == 0)
913
            && (length >= 2)) {
914
        int index;
915
 
916
        if (argc != 4) {
917
            Tcl_AppendResult(interp, "wrong # args: should be \"",
918
                    argv[0], " icursor tagOrId index\"",
919
                    (char *) NULL);
920
            goto error;
921
        }
922
        for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
923
                itemPtr != NULL; itemPtr = NextItem(&search)) {
924
            if ((itemPtr->typePtr->indexProc == NULL)
925
                    || (itemPtr->typePtr->icursorProc == NULL)) {
926
                goto done;
927
            }
928
            if ((*itemPtr->typePtr->indexProc)(interp, (Tk_Canvas) canvasPtr,
929
                    itemPtr, argv[3], &index) != TCL_OK) {
930
                goto error;
931
            }
932
            (*itemPtr->typePtr->icursorProc)((Tk_Canvas) canvasPtr, itemPtr,
933
                    index);
934
            if ((itemPtr == canvasPtr->textInfo.focusItemPtr)
935
                    && (canvasPtr->textInfo.cursorOn)) {
936
                Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
937
                        itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
938
            }
939
        }
940
    } else if ((c == 'i') && (strncmp(argv[1], "index", length) == 0)
941
            && (length >= 3)) {
942
        int index;
943
 
944
        if (argc != 4) {
945
            Tcl_AppendResult(interp, "wrong # args: should be \"",
946
                    argv[0], " index tagOrId string\"",
947
                    (char *) NULL);
948
            goto error;
949
        }
950
        for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
951
                itemPtr != NULL; itemPtr = NextItem(&search)) {
952
            if (itemPtr->typePtr->indexProc != NULL) {
953
                break;
954
            }
955
        }
956
        if (itemPtr == NULL) {
957
            Tcl_AppendResult(interp, "can't find an indexable item \"",
958
                    argv[2], "\"", (char *) NULL);
959
            goto error;
960
        }
961
        if ((*itemPtr->typePtr->indexProc)(interp, (Tk_Canvas) canvasPtr,
962
                itemPtr, argv[3], &index) != TCL_OK) {
963
            goto error;
964
        }
965
        sprintf(interp->result, "%d", index);
966
    } else if ((c == 'i') && (strncmp(argv[1], "insert", length) == 0)
967
            && (length >= 3)) {
968
        int beforeThis;
969
 
970
        if (argc != 5) {
971
            Tcl_AppendResult(interp, "wrong # args: should be \"",
972
                    argv[0], " insert tagOrId beforeThis string\"",
973
                    (char *) NULL);
974
            goto error;
975
        }
976
        for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
977
                itemPtr != NULL; itemPtr = NextItem(&search)) {
978
            if ((itemPtr->typePtr->indexProc == NULL)
979
                    || (itemPtr->typePtr->insertProc == NULL)) {
980
                continue;
981
            }
982
            if ((*itemPtr->typePtr->indexProc)(interp, (Tk_Canvas) canvasPtr,
983
                    itemPtr, argv[3], &beforeThis) != TCL_OK) {
984
                goto error;
985
            }
986
 
987
            /*
988
             * Redraw both item's old and new areas:  it's possible
989
             * that an insertion could result in a new area either
990
             * larger or smaller than the old area.
991
             */
992
 
993
            Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
994
                    itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
995
            (*itemPtr->typePtr->insertProc)((Tk_Canvas) canvasPtr,
996
                    itemPtr, beforeThis, argv[4]);
997
            Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr, itemPtr->x1,
998
                    itemPtr->y1, itemPtr->x2, itemPtr->y2);
999
        }
1000
    } else if ((c == 'i') && (strncmp(argv[1], "itemcget", length) == 0)
1001
            && (length >= 6)) {
1002
        if (argc != 4) {
1003
            Tcl_AppendResult(interp, "wrong # args: should be \"",
1004
                    argv[0], " itemcget tagOrId option\"",
1005
                    (char *) NULL);
1006
            return TCL_ERROR;
1007
        }
1008
        itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
1009
        if (itemPtr != NULL) {
1010
            result = Tk_ConfigureValue(canvasPtr->interp, canvasPtr->tkwin,
1011
                    itemPtr->typePtr->configSpecs, (char *) itemPtr,
1012
                    argv[3], 0);
1013
        }
1014
    } else if ((c == 'i') && (strncmp(argv[1], "itemconfigure", length) == 0)
1015
            && (length >= 6)) {
1016
        if (argc < 3) {
1017
            Tcl_AppendResult(interp, "wrong # args: should be \"",
1018
                    argv[0], " itemconfigure tagOrId ?option value ...?\"",
1019
                    (char *) NULL);
1020
            goto error;
1021
        }
1022
        for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
1023
                itemPtr != NULL; itemPtr = NextItem(&search)) {
1024
            if (argc == 3) {
1025
                result = Tk_ConfigureInfo(canvasPtr->interp, canvasPtr->tkwin,
1026
                        itemPtr->typePtr->configSpecs, (char *) itemPtr,
1027
                        (char *) NULL, 0);
1028
            } else if (argc == 4) {
1029
                result = Tk_ConfigureInfo(canvasPtr->interp, canvasPtr->tkwin,
1030
                        itemPtr->typePtr->configSpecs, (char *) itemPtr,
1031
                        argv[3], 0);
1032
            } else {
1033
                Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
1034
                        itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
1035
                result = (*itemPtr->typePtr->configProc)(interp,
1036
                        (Tk_Canvas) canvasPtr, itemPtr, argc-3, argv+3,
1037
                        TK_CONFIG_ARGV_ONLY);
1038
                Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
1039
                        itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
1040
                canvasPtr->flags |= REPICK_NEEDED;
1041
            }
1042
            if ((result != TCL_OK) || (argc < 5)) {
1043
                break;
1044
            }
1045
        }
1046
    } else if ((c == 'l') && (strncmp(argv[1], "lower", length) == 0)) {
1047
        Tk_Item *itemPtr;
1048
 
1049
        if ((argc != 3) && (argc != 4)) {
1050
            Tcl_AppendResult(interp, "wrong # args: should be \"",
1051
                    argv[0], " lower tagOrId ?belowThis?\"",
1052
                    (char *) NULL);
1053
            goto error;
1054
        }
1055
 
1056
        /*
1057
         * First find the item just after which we'll insert the
1058
         * named items.
1059
         */
1060
 
1061
        if (argc == 3) {
1062
            itemPtr = NULL;
1063
        } else {
1064
            itemPtr = StartTagSearch(canvasPtr, argv[3], &search);
1065
            if (itemPtr == NULL) {
1066
                Tcl_AppendResult(interp, "tag \"", argv[3],
1067
                        "\" doesn't match any items", (char *) NULL);
1068
                goto error;
1069
            }
1070
            itemPtr = itemPtr->prevPtr;
1071
        }
1072
        RelinkItems(canvasPtr, argv[2], itemPtr);
1073
    } else if ((c == 'm') && (strncmp(argv[1], "move", length) == 0)) {
1074
        double xAmount, yAmount;
1075
 
1076
        if (argc != 5) {
1077
            Tcl_AppendResult(interp, "wrong # args: should be \"",
1078
                    argv[0], " move tagOrId xAmount yAmount\"",
1079
                    (char *) NULL);
1080
            goto error;
1081
        }
1082
        if ((Tk_CanvasGetCoord(interp, (Tk_Canvas) canvasPtr, argv[3],
1083
                &xAmount) != TCL_OK) || (Tk_CanvasGetCoord(interp,
1084
                (Tk_Canvas) canvasPtr, argv[4], &yAmount) != TCL_OK)) {
1085
            goto error;
1086
        }
1087
        for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
1088
                itemPtr != NULL; itemPtr = NextItem(&search)) {
1089
            Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
1090
                    itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
1091
            (void) (*itemPtr->typePtr->translateProc)((Tk_Canvas) canvasPtr,
1092
                    itemPtr,  xAmount, yAmount);
1093
            Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
1094
                    itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
1095
            canvasPtr->flags |= REPICK_NEEDED;
1096
        }
1097
    } else if ((c == 'p') && (strncmp(argv[1], "postscript", length) == 0)) {
1098
        result = TkCanvPostscriptCmd(canvasPtr, interp, argc, argv);
1099
    } else if ((c == 'r') && (strncmp(argv[1], "raise", length) == 0)) {
1100
        Tk_Item *prevPtr;
1101
 
1102
        if ((argc != 3) && (argc != 4)) {
1103
            Tcl_AppendResult(interp, "wrong # args: should be \"",
1104
                    argv[0], " raise tagOrId ?aboveThis?\"",
1105
                    (char *) NULL);
1106
            goto error;
1107
        }
1108
 
1109
        /*
1110
         * First find the item just after which we'll insert the
1111
         * named items.
1112
         */
1113
 
1114
        if (argc == 3) {
1115
            prevPtr = canvasPtr->lastItemPtr;
1116
        } else {
1117
            prevPtr = NULL;
1118
            for (itemPtr = StartTagSearch(canvasPtr, argv[3], &search);
1119
                    itemPtr != NULL; itemPtr = NextItem(&search)) {
1120
                prevPtr = itemPtr;
1121
            }
1122
            if (prevPtr == NULL) {
1123
                Tcl_AppendResult(interp, "tagOrId \"", argv[3],
1124
                        "\" doesn't match any items", (char *) NULL);
1125
                goto error;
1126
            }
1127
        }
1128
        RelinkItems(canvasPtr, argv[2], prevPtr);
1129
    } else if ((c == 's') && (strncmp(argv[1], "scale", length) == 0)
1130
            && (length >= 3)) {
1131
        double xOrigin, yOrigin, xScale, yScale;
1132
 
1133
        if (argc != 7) {
1134
            Tcl_AppendResult(interp, "wrong # args: should be \"",
1135
                    argv[0], " scale tagOrId xOrigin yOrigin xScale yScale\"",
1136
                    (char *) NULL);
1137
            goto error;
1138
        }
1139
        if ((Tk_CanvasGetCoord(interp, (Tk_Canvas) canvasPtr,
1140
                    argv[3], &xOrigin) != TCL_OK)
1141
                || (Tk_CanvasGetCoord(interp, (Tk_Canvas) canvasPtr,
1142
                    argv[4], &yOrigin) != TCL_OK)
1143
                || (Tcl_GetDouble(interp, argv[5], &xScale) != TCL_OK)
1144
                || (Tcl_GetDouble(interp, argv[6], &yScale) != TCL_OK)) {
1145
            goto error;
1146
        }
1147
        if ((xScale == 0.0) || (yScale == 0.0)) {
1148
            interp->result = "scale factor cannot be zero";
1149
            goto error;
1150
        }
1151
        for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
1152
                itemPtr != NULL; itemPtr = NextItem(&search)) {
1153
            Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
1154
                    itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
1155
            (void) (*itemPtr->typePtr->scaleProc)((Tk_Canvas) canvasPtr,
1156
                    itemPtr, xOrigin, yOrigin, xScale, yScale);
1157
            Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
1158
                    itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
1159
            canvasPtr->flags |= REPICK_NEEDED;
1160
        }
1161
    } else if ((c == 's') && (strncmp(argv[1], "scan", length) == 0)
1162
            && (length >= 3)) {
1163
        int x, y;
1164
 
1165
        if (argc != 5) {
1166
            Tcl_AppendResult(interp, "wrong # args: should be \"",
1167
                    argv[0], " scan mark|dragto x y\"", (char *) NULL);
1168
            goto error;
1169
        }
1170
        if ((Tcl_GetInt(interp, argv[3], &x) != TCL_OK)
1171
                || (Tcl_GetInt(interp, argv[4], &y) != TCL_OK)){
1172
            goto error;
1173
        }
1174
        if ((argv[2][0] == 'm')
1175
                && (strncmp(argv[2], "mark", strlen(argv[2])) == 0)) {
1176
            canvasPtr->scanX = x;
1177
            canvasPtr->scanXOrigin = canvasPtr->xOrigin;
1178
            canvasPtr->scanY = y;
1179
            canvasPtr->scanYOrigin = canvasPtr->yOrigin;
1180
        } else if ((argv[2][0] == 'd')
1181
                && (strncmp(argv[2], "dragto", strlen(argv[2])) == 0)) {
1182
            int newXOrigin, newYOrigin, tmp;
1183
 
1184
            /*
1185
             * Compute a new view origin for the canvas, amplifying the
1186
             * mouse motion.
1187
             */
1188
 
1189
            tmp = canvasPtr->scanXOrigin - 10*(x - canvasPtr->scanX)
1190
                    - canvasPtr->scrollX1;
1191
            newXOrigin = canvasPtr->scrollX1 + tmp;
1192
            tmp = canvasPtr->scanYOrigin - 10*(y - canvasPtr->scanY)
1193
                    - canvasPtr->scrollY1;
1194
            newYOrigin = canvasPtr->scrollY1 + tmp;
1195
            CanvasSetOrigin(canvasPtr, newXOrigin, newYOrigin);
1196
        } else {
1197
            Tcl_AppendResult(interp, "bad scan option \"", argv[2],
1198
                    "\": must be mark or dragto", (char *) NULL);
1199
            goto error;
1200
        }
1201
    } else if ((c == 's') && (strncmp(argv[1], "select", length) == 0)
1202
            && (length >= 2)) {
1203
        int index;
1204
 
1205
        if (argc < 3) {
1206
            Tcl_AppendResult(interp, "wrong # args: should be \"",
1207
                    argv[0], " select option ?tagOrId? ?arg?\"", (char *) NULL);
1208
            goto error;
1209
        }
1210
        if (argc >= 4) {
1211
            for (itemPtr = StartTagSearch(canvasPtr, argv[3], &search);
1212
                    itemPtr != NULL; itemPtr = NextItem(&search)) {
1213
                if ((itemPtr->typePtr->indexProc != NULL)
1214
                        && (itemPtr->typePtr->selectionProc != NULL)){
1215
                    break;
1216
                }
1217
            }
1218
            if (itemPtr == NULL) {
1219
                Tcl_AppendResult(interp,
1220
                        "can't find an indexable and selectable item \"",
1221
                        argv[3], "\"", (char *) NULL);
1222
                goto error;
1223
            }
1224
        }
1225
        if (argc == 5) {
1226
            if ((*itemPtr->typePtr->indexProc)(interp, (Tk_Canvas) canvasPtr,
1227
                    itemPtr, argv[4], &index) != TCL_OK) {
1228
                goto error;
1229
            }
1230
        }
1231
        length = strlen(argv[2]);
1232
        c = argv[2][0];
1233
        if ((c == 'a') && (strncmp(argv[2], "adjust", length) == 0)) {
1234
            if (argc != 5) {
1235
                Tcl_AppendResult(interp, "wrong # args: should be \"",
1236
                        argv[0], " select adjust tagOrId index\"",
1237
                        (char *) NULL);
1238
                goto error;
1239
            }
1240
            if (canvasPtr->textInfo.selItemPtr == itemPtr) {
1241
                if (index < (canvasPtr->textInfo.selectFirst
1242
                        + canvasPtr->textInfo.selectLast)/2) {
1243
                    canvasPtr->textInfo.selectAnchor =
1244
                            canvasPtr->textInfo.selectLast + 1;
1245
                } else {
1246
                    canvasPtr->textInfo.selectAnchor =
1247
                            canvasPtr->textInfo.selectFirst;
1248
                }
1249
            }
1250
            CanvasSelectTo(canvasPtr, itemPtr, index);
1251
        } else if ((c == 'c') && (argv[2] != NULL)
1252
                && (strncmp(argv[2], "clear", length) == 0)) {
1253
            if (argc != 3) {
1254
                Tcl_AppendResult(interp, "wrong # args: should be \"",
1255
                        argv[0], " select clear\"", (char *) NULL);
1256
                goto error;
1257
            }
1258
            if (canvasPtr->textInfo.selItemPtr != NULL) {
1259
                Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
1260
                        canvasPtr->textInfo.selItemPtr->x1,
1261
                        canvasPtr->textInfo.selItemPtr->y1,
1262
                        canvasPtr->textInfo.selItemPtr->x2,
1263
                        canvasPtr->textInfo.selItemPtr->y2);
1264
                canvasPtr->textInfo.selItemPtr = NULL;
1265
            }
1266
            goto done;
1267
        } else if ((c == 'f') && (strncmp(argv[2], "from", length) == 0)) {
1268
            if (argc != 5) {
1269
                Tcl_AppendResult(interp, "wrong # args: should be \"",
1270
                        argv[0], " select from tagOrId index\"",
1271
                        (char *) NULL);
1272
                goto error;
1273
            }
1274
            canvasPtr->textInfo.anchorItemPtr = itemPtr;
1275
            canvasPtr->textInfo.selectAnchor = index;
1276
        } else if ((c == 'i') && (strncmp(argv[2], "item", length) == 0)) {
1277
            if (argc != 3) {
1278
                Tcl_AppendResult(interp, "wrong # args: should be \"",
1279
                        argv[0], " select item\"", (char *) NULL);
1280
                goto error;
1281
            }
1282
            if (canvasPtr->textInfo.selItemPtr != NULL) {
1283
                sprintf(interp->result, "%d",
1284
                        canvasPtr->textInfo.selItemPtr->id);
1285
            }
1286
        } else if ((c == 't') && (strncmp(argv[2], "to", length) == 0)) {
1287
            if (argc != 5) {
1288
                Tcl_AppendResult(interp, "wrong # args: should be \"",
1289
                        argv[0], " select to tagOrId index\"",
1290
                        (char *) NULL);
1291
                goto error;
1292
            }
1293
            CanvasSelectTo(canvasPtr, itemPtr, index);
1294
        } else {
1295
            Tcl_AppendResult(interp, "bad select option \"", argv[2],
1296
                    "\": must be adjust, clear, from, item, or to",
1297
                    (char *) NULL);
1298
            goto error;
1299
        }
1300
    } else if ((c == 't') && (strncmp(argv[1], "type", length) == 0)) {
1301
        if (argc != 3) {
1302
            Tcl_AppendResult(interp, "wrong # args: should be \"",
1303
                    argv[0], " type tag\"", (char *) NULL);
1304
            goto error;
1305
        }
1306
        itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
1307
        if (itemPtr != NULL) {
1308
            interp->result = itemPtr->typePtr->name;
1309
        }
1310
    } else if ((c == 'x') && (strncmp(argv[1], "xview", length) == 0)) {
1311
        int count, type;
1312
        int newX = 0;            /* Initialization needed only to prevent
1313
                                 * gcc warnings. */
1314
        double fraction;
1315
 
1316
        if (argc == 2) {
1317
            PrintScrollFractions(canvasPtr->xOrigin + canvasPtr->inset,
1318
                    canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin)
1319
                    - canvasPtr->inset, canvasPtr->scrollX1,
1320
                    canvasPtr->scrollX2, interp->result);
1321
        } else {
1322
            type = Tk_GetScrollInfo(interp, argc, argv, &fraction, &count);
1323
            switch (type) {
1324
                case TK_SCROLL_ERROR:
1325
                    goto error;
1326
                case TK_SCROLL_MOVETO:
1327
                    newX = canvasPtr->scrollX1 - canvasPtr->inset
1328
                            + (int) (fraction * (canvasPtr->scrollX2
1329
                            - canvasPtr->scrollX1) + 0.5);
1330
                    break;
1331
                case TK_SCROLL_PAGES:
1332
                    newX = (int) (canvasPtr->xOrigin + count * .9
1333
                            * (Tk_Width(canvasPtr->tkwin) - 2*canvasPtr->inset));
1334
                    break;
1335
                case TK_SCROLL_UNITS:
1336
                    if (canvasPtr->xScrollIncrement > 0) {
1337
                        newX = canvasPtr->xOrigin
1338
                                + count*canvasPtr->xScrollIncrement;
1339
                    } else {
1340
                        newX = (int) (canvasPtr->xOrigin + count * .1
1341
                                * (Tk_Width(canvasPtr->tkwin)
1342
                                - 2*canvasPtr->inset));
1343
                    }
1344
                    break;
1345
            }
1346
            CanvasSetOrigin(canvasPtr, newX, canvasPtr->yOrigin);
1347
        }
1348
    } else if ((c == 'y') && (strncmp(argv[1], "yview", length) == 0)) {
1349
        int count, type;
1350
        int newY = 0;            /* Initialization needed only to prevent
1351
                                 * gcc warnings. */
1352
        double fraction;
1353
 
1354
        if (argc == 2) {
1355
            PrintScrollFractions(canvasPtr->yOrigin + canvasPtr->inset,
1356
                    canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin)
1357
                    - canvasPtr->inset, canvasPtr->scrollY1,
1358
                    canvasPtr->scrollY2, interp->result);
1359
        } else {
1360
            type = Tk_GetScrollInfo(interp, argc, argv, &fraction, &count);
1361
            switch (type) {
1362
                case TK_SCROLL_ERROR:
1363
                    goto error;
1364
                case TK_SCROLL_MOVETO:
1365
                    newY = canvasPtr->scrollY1 - canvasPtr->inset
1366
                            + (int) (fraction*(canvasPtr->scrollY2
1367
                            - canvasPtr->scrollY1) + 0.5);
1368
                    break;
1369
                case TK_SCROLL_PAGES:
1370
                    newY = (int) (canvasPtr->yOrigin + count * .9
1371
                            * (Tk_Height(canvasPtr->tkwin)
1372
                            - 2*canvasPtr->inset));
1373
                    break;
1374
                case TK_SCROLL_UNITS:
1375
                    if (canvasPtr->yScrollIncrement > 0) {
1376
                        newY = canvasPtr->yOrigin
1377
                                + count*canvasPtr->yScrollIncrement;
1378
                    } else {
1379
                        newY = (int) (canvasPtr->yOrigin + count * .1
1380
                                * (Tk_Height(canvasPtr->tkwin)
1381
                                - 2*canvasPtr->inset));
1382
                    }
1383
                    break;
1384
            }
1385
            CanvasSetOrigin(canvasPtr, canvasPtr->xOrigin, newY);
1386
        }
1387
    } else {
1388
        Tcl_AppendResult(interp, "bad option \"", argv[1],
1389
                "\": must be addtag, bbox, bind, ",
1390
                "canvasx, canvasy, cget, configure, coords, create, ",
1391
                "dchars, delete, dtag, find, focus, ",
1392
                "gettags, icursor, index, insert, itemcget, itemconfigure, ",
1393
                "lower, move, postscript, raise, scale, scan, ",
1394
                "select, type, xview, or yview",
1395
                (char *) NULL);
1396
        goto error;
1397
    }
1398
    done:
1399
    Tcl_Release((ClientData) canvasPtr);
1400
    return result;
1401
 
1402
    error:
1403
    Tcl_Release((ClientData) canvasPtr);
1404
    return TCL_ERROR;
1405
}
1406
 
1407
/*
1408
 *----------------------------------------------------------------------
1409
 *
1410
 * DestroyCanvas --
1411
 *
1412
 *      This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
1413
 *      to clean up the internal structure of a canvas at a safe time
1414
 *      (when no-one is using it anymore).
1415
 *
1416
 * Results:
1417
 *      None.
1418
 *
1419
 * Side effects:
1420
 *      Everything associated with the canvas is freed up.
1421
 *
1422
 *----------------------------------------------------------------------
1423
 */
1424
 
1425
static void
1426
DestroyCanvas(memPtr)
1427
    char *memPtr;               /* Info about canvas widget. */
1428
{
1429
    TkCanvas *canvasPtr = (TkCanvas *) memPtr;
1430
    Tk_Item *itemPtr;
1431
 
1432
    /*
1433
     * Free up all of the items in the canvas.
1434
     */
1435
 
1436
    for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
1437
            itemPtr = canvasPtr->firstItemPtr) {
1438
        canvasPtr->firstItemPtr = itemPtr->nextPtr;
1439
        (*itemPtr->typePtr->deleteProc)((Tk_Canvas) canvasPtr, itemPtr,
1440
                canvasPtr->display);
1441
        if (itemPtr->tagPtr != itemPtr->staticTagSpace) {
1442
            ckfree((char *) itemPtr->tagPtr);
1443
        }
1444
        ckfree((char *) itemPtr);
1445
    }
1446
 
1447
    /*
1448
     * Free up all the stuff that requires special handling,
1449
     * then let Tk_FreeOptions handle all the standard option-related
1450
     * stuff.
1451
     */
1452
 
1453
    Tcl_DeleteHashTable(&canvasPtr->idTable);
1454
    if (canvasPtr->pixmapGC != None) {
1455
        Tk_FreeGC(canvasPtr->display, canvasPtr->pixmapGC);
1456
    }
1457
    Tcl_DeleteTimerHandler(canvasPtr->insertBlinkHandler);
1458
    if (canvasPtr->bindingTable != NULL) {
1459
        Tk_DeleteBindingTable(canvasPtr->bindingTable);
1460
    }
1461
    Tk_FreeOptions(configSpecs, (char *) canvasPtr, canvasPtr->display, 0);
1462
    ckfree((char *) canvasPtr);
1463
}
1464
 
1465
/*
1466
 *----------------------------------------------------------------------
1467
 *
1468
 * ConfigureCanvas --
1469
 *
1470
 *      This procedure is called to process an argv/argc list, plus
1471
 *      the Tk option database, in order to configure (or
1472
 *      reconfigure) a canvas widget.
1473
 *
1474
 * Results:
1475
 *      The return value is a standard Tcl result.  If TCL_ERROR is
1476
 *      returned, then interp->result contains an error message.
1477
 *
1478
 * Side effects:
1479
 *      Configuration information, such as colors, border width,
1480
 *      etc. get set for canvasPtr;  old resources get freed,
1481
 *      if there were any.
1482
 *
1483
 *----------------------------------------------------------------------
1484
 */
1485
 
1486
static int
1487
ConfigureCanvas(interp, canvasPtr, argc, argv, flags)
1488
    Tcl_Interp *interp;         /* Used for error reporting. */
1489
    TkCanvas *canvasPtr;        /* Information about widget;  may or may
1490
                                 * not already have values for some fields. */
1491
    int argc;                   /* Number of valid entries in argv. */
1492
    char **argv;                /* Arguments. */
1493
    int flags;                  /* Flags to pass to Tk_ConfigureWidget. */
1494
{
1495
    XGCValues gcValues;
1496
    GC new;
1497
 
1498
    if (Tk_ConfigureWidget(interp, canvasPtr->tkwin, configSpecs,
1499
            argc, argv, (char *) canvasPtr, flags) != TCL_OK) {
1500
        return TCL_ERROR;
1501
    }
1502
 
1503
    /*
1504
     * A few options need special processing, such as setting the
1505
     * background from a 3-D border and creating a GC for copying
1506
     * bits to the screen.
1507
     */
1508
 
1509
    Tk_SetBackgroundFromBorder(canvasPtr->tkwin, canvasPtr->bgBorder);
1510
 
1511
    if (canvasPtr->highlightWidth < 0) {
1512
        canvasPtr->highlightWidth = 0;
1513
    }
1514
    canvasPtr->inset = canvasPtr->borderWidth + canvasPtr->highlightWidth;
1515
 
1516
    gcValues.function = GXcopy;
1517
    gcValues.foreground = Tk_3DBorderColor(canvasPtr->bgBorder)->pixel;
1518
    gcValues.graphics_exposures = False;
1519
    new = Tk_GetGCColor(canvasPtr->tkwin,
1520
            GCFunction|GCForeground|GCGraphicsExposures, &gcValues,
1521
            Tk_3DBorderColor(canvasPtr->bgBorder), NULL);
1522
    if (canvasPtr->pixmapGC != None) {
1523
        Tk_FreeGC(canvasPtr->display, canvasPtr->pixmapGC);
1524
    }
1525
    canvasPtr->pixmapGC = new;
1526
 
1527
    /*
1528
     * Reset the desired dimensions for the window.
1529
     */
1530
 
1531
    Tk_GeometryRequest(canvasPtr->tkwin, canvasPtr->width + 2*canvasPtr->inset,
1532
            canvasPtr->height + 2*canvasPtr->inset);
1533
 
1534
    /*
1535
     * Restart the cursor timing sequence in case the on-time or off-time
1536
     * just changed.
1537
     */
1538
 
1539
    if (canvasPtr->textInfo.gotFocus) {
1540
        CanvasFocusProc(canvasPtr, 1);
1541
    }
1542
 
1543
    /*
1544
     * Recompute the scroll region.
1545
     */
1546
 
1547
    canvasPtr->scrollX1 = 0;
1548
    canvasPtr->scrollY1 = 0;
1549
    canvasPtr->scrollX2 = 0;
1550
    canvasPtr->scrollY2 = 0;
1551
    if (canvasPtr->regionString != NULL) {
1552
        int argc2;
1553
        char **argv2;
1554
 
1555
        if (Tcl_SplitList(canvasPtr->interp, canvasPtr->regionString,
1556
                &argc2, &argv2) != TCL_OK) {
1557
            return TCL_ERROR;
1558
        }
1559
        if (argc2 != 4) {
1560
            Tcl_AppendResult(interp, "bad scrollRegion \"",
1561
                    canvasPtr->regionString, "\"", (char *) NULL);
1562
            badRegion:
1563
            ckfree(canvasPtr->regionString);
1564
            ckfree((char *) argv2);
1565
            canvasPtr->regionString = NULL;
1566
            return TCL_ERROR;
1567
        }
1568
        if ((Tk_GetPixels(canvasPtr->interp, canvasPtr->tkwin,
1569
                    argv2[0], &canvasPtr->scrollX1) != TCL_OK)
1570
                || (Tk_GetPixels(canvasPtr->interp, canvasPtr->tkwin,
1571
                    argv2[1], &canvasPtr->scrollY1) != TCL_OK)
1572
                || (Tk_GetPixels(canvasPtr->interp, canvasPtr->tkwin,
1573
                    argv2[2], &canvasPtr->scrollX2) != TCL_OK)
1574
                || (Tk_GetPixels(canvasPtr->interp, canvasPtr->tkwin,
1575
                    argv2[3], &canvasPtr->scrollY2) != TCL_OK)) {
1576
            goto badRegion;
1577
        }
1578
        ckfree((char *) argv2);
1579
    }
1580
 
1581
    /*
1582
     * Reset the canvas's origin (this is a no-op unless confine
1583
     * mode has just been turned on or the scroll region has changed).
1584
     */
1585
 
1586
    CanvasSetOrigin(canvasPtr, canvasPtr->xOrigin, canvasPtr->yOrigin);
1587
    canvasPtr->flags |= UPDATE_SCROLLBARS|REDRAW_BORDERS;
1588
    Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
1589
            canvasPtr->xOrigin, canvasPtr->yOrigin,
1590
            canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin),
1591
            canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin));
1592
    return TCL_OK;
1593
}
1594
 
1595
/*
1596
 *---------------------------------------------------------------------------
1597
 *
1598
 * CanvasWorldChanged --
1599
 *
1600
 *      This procedure is called when the world has changed in some
1601
 *      way and the widget needs to recompute all its graphics contexts
1602
 *      and determine its new geometry.
1603
 *
1604
 * Results:
1605
 *      None.
1606
 *
1607
 * Side effects:
1608
 *      Configures all items in the canvas with a empty argc/argv, for
1609
 *      the side effect of causing all the items to recompute their
1610
 *      geometry and to be redisplayed.
1611
 *
1612
 *---------------------------------------------------------------------------
1613
 */
1614
 
1615
static void
1616
CanvasWorldChanged(instanceData)
1617
    ClientData instanceData;    /* Information about widget. */
1618
{
1619
    TkCanvas *canvasPtr;
1620
    Tk_Item *itemPtr;
1621
    int result;
1622
 
1623
    canvasPtr = (TkCanvas *) instanceData;
1624
    itemPtr = canvasPtr->firstItemPtr;
1625
    for ( ; itemPtr != NULL; itemPtr = itemPtr->nextPtr) {
1626
        result = (*itemPtr->typePtr->configProc)(canvasPtr->interp,
1627
                (Tk_Canvas) canvasPtr, itemPtr, 0, NULL,
1628
                TK_CONFIG_ARGV_ONLY);
1629
        if (result != TCL_OK) {
1630
            Tcl_ResetResult(canvasPtr->interp);
1631
        }
1632
    }
1633
    canvasPtr->flags |= REPICK_NEEDED;
1634
    Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
1635
            canvasPtr->xOrigin, canvasPtr->yOrigin,
1636
            canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin),
1637
            canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin));
1638
}
1639
 
1640
/*
1641
 *--------------------------------------------------------------
1642
 *
1643
 * DisplayCanvas --
1644
 *
1645
 *      This procedure redraws the contents of a canvas window.
1646
 *      It is invoked as a do-when-idle handler, so it only runs
1647
 *      when there's nothing else for the application to do.
1648
 *
1649
 * Results:
1650
 *      None.
1651
 *
1652
 * Side effects:
1653
 *      Information appears on the screen.
1654
 *
1655
 *--------------------------------------------------------------
1656
 */
1657
 
1658
static void
1659
DisplayCanvas(clientData)
1660
    ClientData clientData;      /* Information about widget. */
1661
{
1662
    TkCanvas *canvasPtr = (TkCanvas *) clientData;
1663
    Tk_Window tkwin = canvasPtr->tkwin;
1664
    Tk_Item *itemPtr;
1665
    Pixmap pixmap;
1666
    int screenX1, screenX2, screenY1, screenY2, width, height;
1667
 
1668
    if (canvasPtr->tkwin == NULL) {
1669
        return;
1670
    }
1671
    if (!Tk_IsMapped(tkwin)) {
1672
        goto done;
1673
    }
1674
 
1675
    /*
1676
     * Choose a new current item if that is needed (this could cause
1677
     * event handlers to be invoked).
1678
     */
1679
 
1680
    while (canvasPtr->flags & REPICK_NEEDED) {
1681
        Tcl_Preserve((ClientData) canvasPtr);
1682
        canvasPtr->flags &= ~REPICK_NEEDED;
1683
        PickCurrentItem(canvasPtr, &canvasPtr->pickEvent);
1684
        tkwin = canvasPtr->tkwin;
1685
        Tcl_Release((ClientData) canvasPtr);
1686
        if (tkwin == NULL) {
1687
            return;
1688
        }
1689
    }
1690
 
1691
    /*
1692
     * Compute the intersection between the area that needs redrawing
1693
     * and the area that's visible on the screen.
1694
     */
1695
 
1696
    if ((canvasPtr->redrawX1 < canvasPtr->redrawX2)
1697
            && (canvasPtr->redrawY1 < canvasPtr->redrawY2)) {
1698
        screenX1 = canvasPtr->xOrigin + canvasPtr->inset;
1699
        screenY1 = canvasPtr->yOrigin + canvasPtr->inset;
1700
        screenX2 = canvasPtr->xOrigin + Tk_Width(tkwin) - canvasPtr->inset;
1701
        screenY2 = canvasPtr->yOrigin + Tk_Height(tkwin) - canvasPtr->inset;
1702
        if (canvasPtr->redrawX1 > screenX1) {
1703
            screenX1 = canvasPtr->redrawX1;
1704
        }
1705
        if (canvasPtr->redrawY1 > screenY1) {
1706
            screenY1 = canvasPtr->redrawY1;
1707
        }
1708
        if (canvasPtr->redrawX2 < screenX2) {
1709
            screenX2 = canvasPtr->redrawX2;
1710
        }
1711
        if (canvasPtr->redrawY2 < screenY2) {
1712
            screenY2 = canvasPtr->redrawY2;
1713
        }
1714
        if ((screenX1 >= screenX2) || (screenY1 >= screenY2)) {
1715
            goto borders;
1716
        }
1717
 
1718
        /*
1719
         * Redrawing is done in a temporary pixmap that is allocated
1720
         * here and freed at the end of the procedure.  All drawing
1721
         * is done to the pixmap, and the pixmap is copied to the
1722
         * screen at the end of the procedure. The temporary pixmap
1723
         * serves two purposes:
1724
         *
1725
         * 1. It provides a smoother visual effect (no clearing and
1726
         *    gradual redraw will be visible to users).
1727
         * 2. It allows us to redraw only the objects that overlap
1728
         *    the redraw area.  Otherwise incorrect results could
1729
         *        occur from redrawing things that stick outside of
1730
         *        the redraw area (we'd have to redraw everything in
1731
         *    order to make the overlaps look right).
1732
         *
1733
         * Some tricky points about the pixmap:
1734
         *
1735
         * 1. We only allocate a large enough pixmap to hold the
1736
         *    area that has to be redisplayed.  This saves time in
1737
         *    in the X server for large objects that cover much
1738
         *    more than the area being redisplayed:  only the area
1739
         *    of the pixmap will actually have to be redrawn.
1740
         * 2. Some X servers (e.g. the one for DECstations) have troubles
1741
         *    with characters that overlap an edge of the pixmap (on the
1742
         *    DEC servers, as of 8/18/92, such characters are drawn one
1743
         *    pixel too far to the right).  To handle this problem,
1744
         *    make the pixmap a bit larger than is absolutely needed
1745
         *    so that for normal-sized fonts the characters that overlap
1746
         *    the edge of the pixmap will be outside the area we care
1747
         *    about.
1748
         */
1749
 
1750
        canvasPtr->drawableXOrigin = screenX1 - 30;
1751
        canvasPtr->drawableYOrigin = screenY1 - 30;
1752
        pixmap = Tk_GetPixmap(Tk_Display(tkwin), Tk_WindowId(tkwin),
1753
            (screenX2 + 30 - canvasPtr->drawableXOrigin),
1754
            (screenY2 + 30 - canvasPtr->drawableYOrigin),
1755
            Tk_Depth(tkwin));
1756
 
1757
        /*
1758
         * Clear the area to be redrawn.
1759
         */
1760
 
1761
        width = screenX2 - screenX1;
1762
        height = screenY2 - screenY1;
1763
 
1764
        XFillRectangle(Tk_Display(tkwin), pixmap, canvasPtr->pixmapGC,
1765
                screenX1 - canvasPtr->drawableXOrigin,
1766
                screenY1 - canvasPtr->drawableYOrigin, (unsigned int) width,
1767
                (unsigned int) height);
1768
 
1769
        /*
1770
         * Scan through the item list, redrawing those items that need it.
1771
         * An item must be redraw if either (a) it intersects the smaller
1772
         * on-screen area or (b) it intersects the full canvas area and its
1773
         * type requests that it be redrawn always (e.g. so subwindows can
1774
         * be unmapped when they move off-screen).
1775
         */
1776
 
1777
        for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
1778
                itemPtr = itemPtr->nextPtr) {
1779
            if ((itemPtr->x1 >= screenX2)
1780
                    || (itemPtr->y1 >= screenY2)
1781
                    || (itemPtr->x2 < screenX1)
1782
                    || (itemPtr->y2 < screenY1)) {
1783
                if (!itemPtr->typePtr->alwaysRedraw
1784
                        || (itemPtr->x1 >= canvasPtr->redrawX2)
1785
                        || (itemPtr->y1 >= canvasPtr->redrawY2)
1786
                        || (itemPtr->x2 < canvasPtr->redrawX1)
1787
                        || (itemPtr->y2 < canvasPtr->redrawY1)) {
1788
                    continue;
1789
                }
1790
            }
1791
            (*itemPtr->typePtr->displayProc)((Tk_Canvas) canvasPtr, itemPtr,
1792
                    canvasPtr->display, pixmap, screenX1, screenY1, width,
1793
                    height);
1794
        }
1795
 
1796
        /*
1797
         * Copy from the temporary pixmap to the screen, then free up
1798
         * the temporary pixmap.
1799
         */
1800
 
1801
        XCopyArea(Tk_Display(tkwin), pixmap, Tk_WindowId(tkwin),
1802
                canvasPtr->pixmapGC,
1803
                screenX1 - canvasPtr->drawableXOrigin,
1804
                screenY1 - canvasPtr->drawableYOrigin,
1805
                (unsigned) (screenX2 - screenX1),
1806
                (unsigned) (screenY2 - screenY1),
1807
                screenX1 - canvasPtr->xOrigin, screenY1 - canvasPtr->yOrigin);
1808
        Tk_FreePixmap(Tk_Display(tkwin), pixmap);
1809
    }
1810
 
1811
    /*
1812
     * Draw the window borders, if needed.
1813
     */
1814
 
1815
    borders:
1816
    if (canvasPtr->flags & REDRAW_BORDERS) {
1817
        canvasPtr->flags &= ~REDRAW_BORDERS;
1818
        if (canvasPtr->borderWidth > 0) {
1819
            Tk_Draw3DRectangle(tkwin, Tk_WindowId(tkwin),
1820
                    canvasPtr->bgBorder, canvasPtr->highlightWidth,
1821
                    canvasPtr->highlightWidth,
1822
                    Tk_Width(tkwin) - 2*canvasPtr->highlightWidth,
1823
                    Tk_Height(tkwin) - 2*canvasPtr->highlightWidth,
1824
                    canvasPtr->borderWidth, canvasPtr->relief);
1825
        }
1826
        if (canvasPtr->highlightWidth != 0) {
1827
            GC gc;
1828
 
1829
            if (canvasPtr->textInfo.gotFocus) {
1830
                gc = Tk_GCForColor(canvasPtr->highlightColorPtr,
1831
                        Tk_WindowId(tkwin));
1832
            } else {
1833
                gc = Tk_GCForColor(canvasPtr->highlightBgColorPtr,
1834
                        Tk_WindowId(tkwin));
1835
            }
1836
            Tk_DrawFocusHighlight(tkwin, gc, canvasPtr->highlightWidth,
1837
                    Tk_WindowId(tkwin));
1838
        }
1839
    }
1840
 
1841
    done:
1842
    canvasPtr->flags &= ~REDRAW_PENDING;
1843
    canvasPtr->redrawX1 = canvasPtr->redrawX2 = 0;
1844
    canvasPtr->redrawY1 = canvasPtr->redrawY2 = 0;
1845
    if (canvasPtr->flags & UPDATE_SCROLLBARS) {
1846
        CanvasUpdateScrollbars(canvasPtr);
1847
    }
1848
}
1849
 
1850
/*
1851
 *--------------------------------------------------------------
1852
 *
1853
 * CanvasEventProc --
1854
 *
1855
 *      This procedure is invoked by the Tk dispatcher for various
1856
 *      events on canvases.
1857
 *
1858
 * Results:
1859
 *      None.
1860
 *
1861
 * Side effects:
1862
 *      When the window gets deleted, internal structures get
1863
 *      cleaned up.  When it gets exposed, it is redisplayed.
1864
 *
1865
 *--------------------------------------------------------------
1866
 */
1867
 
1868
static void
1869
CanvasEventProc(clientData, eventPtr)
1870
    ClientData clientData;      /* Information about window. */
1871
    XEvent *eventPtr;           /* Information about event. */
1872
{
1873
    TkCanvas *canvasPtr = (TkCanvas *) clientData;
1874
 
1875
    if (eventPtr->type == Expose) {
1876
        int x, y;
1877
 
1878
        x = eventPtr->xexpose.x + canvasPtr->xOrigin;
1879
        y = eventPtr->xexpose.y + canvasPtr->yOrigin;
1880
        Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr, x, y,
1881
                x + eventPtr->xexpose.width,
1882
                y + eventPtr->xexpose.height);
1883
        if ((eventPtr->xexpose.x < canvasPtr->inset)
1884
                || (eventPtr->xexpose.y < canvasPtr->inset)
1885
                || ((eventPtr->xexpose.x + eventPtr->xexpose.width)
1886
                    > (Tk_Width(canvasPtr->tkwin) - canvasPtr->inset))
1887
                || ((eventPtr->xexpose.y + eventPtr->xexpose.height)
1888
                    > (Tk_Height(canvasPtr->tkwin) - canvasPtr->inset))) {
1889
            canvasPtr->flags |= REDRAW_BORDERS;
1890
        }
1891
    } else if (eventPtr->type == DestroyNotify) {
1892
        if (canvasPtr->tkwin != NULL) {
1893
            canvasPtr->tkwin = NULL;
1894
            Tcl_DeleteCommandFromToken(canvasPtr->interp,
1895
                    canvasPtr->widgetCmd);
1896
        }
1897
        if (canvasPtr->flags & REDRAW_PENDING) {
1898
            Tcl_CancelIdleCall(DisplayCanvas, (ClientData) canvasPtr);
1899
        }
1900
        Tcl_EventuallyFree((ClientData) canvasPtr, DestroyCanvas);
1901
    } else if (eventPtr->type == ConfigureNotify) {
1902
        canvasPtr->flags |= UPDATE_SCROLLBARS;
1903
 
1904
        /*
1905
         * The call below is needed in order to recenter the canvas if
1906
         * it's confined and its scroll region is smaller than the window.
1907
         */
1908
 
1909
        CanvasSetOrigin(canvasPtr, canvasPtr->xOrigin, canvasPtr->yOrigin);
1910
        Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr, canvasPtr->xOrigin,
1911
                canvasPtr->yOrigin,
1912
                canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin),
1913
                canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin));
1914
        canvasPtr->flags |= REDRAW_BORDERS;
1915
    } else if (eventPtr->type == FocusIn) {
1916
        if (eventPtr->xfocus.detail != NotifyInferior) {
1917
            CanvasFocusProc(canvasPtr, 1);
1918
        }
1919
    } else if (eventPtr->type == FocusOut) {
1920
        if (eventPtr->xfocus.detail != NotifyInferior) {
1921
            CanvasFocusProc(canvasPtr, 0);
1922
        }
1923
    } else if (eventPtr->type == UnmapNotify) {
1924
        Tk_Item *itemPtr;
1925
 
1926
        /*
1927
         * Special hack:  if the canvas is unmapped, then must notify
1928
         * all items with "alwaysRedraw" set, so that they know that
1929
         * they are no longer displayed.
1930
         */
1931
 
1932
        for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
1933
                itemPtr = itemPtr->nextPtr) {
1934
            if (itemPtr->typePtr->alwaysRedraw) {
1935
                (*itemPtr->typePtr->displayProc)((Tk_Canvas) canvasPtr,
1936
                        itemPtr, canvasPtr->display, None, 0, 0, 0, 0);
1937
            }
1938
        }
1939
    }
1940
}
1941
 
1942
/*
1943
 *----------------------------------------------------------------------
1944
 *
1945
 * CanvasCmdDeletedProc --
1946
 *
1947
 *      This procedure is invoked when a widget command is deleted.  If
1948
 *      the widget isn't already in the process of being destroyed,
1949
 *      this command destroys it.
1950
 *
1951
 * Results:
1952
 *      None.
1953
 *
1954
 * Side effects:
1955
 *      The widget is destroyed.
1956
 *
1957
 *----------------------------------------------------------------------
1958
 */
1959
 
1960
static void
1961
CanvasCmdDeletedProc(clientData)
1962
    ClientData clientData;      /* Pointer to widget record for widget. */
1963
{
1964
    TkCanvas *canvasPtr = (TkCanvas *) clientData;
1965
    Tk_Window tkwin = canvasPtr->tkwin;
1966
 
1967
    /*
1968
     * This procedure could be invoked either because the window was
1969
     * destroyed and the command was then deleted (in which case tkwin
1970
     * is NULL) or because the command was deleted, and then this procedure
1971
     * destroys the widget.
1972
     */
1973
 
1974
    if (tkwin != NULL) {
1975
        canvasPtr->tkwin = NULL;
1976
        Tk_DestroyWindow(tkwin);
1977
    }
1978
}
1979
 
1980
/*
1981
 *--------------------------------------------------------------
1982
 *
1983
 * Tk_CanvasEventuallyRedraw --
1984
 *
1985
 *      Arrange for part or all of a canvas widget to redrawn at
1986
 *      some convenient time in the future.
1987
 *
1988
 * Results:
1989
 *      None.
1990
 *
1991
 * Side effects:
1992
 *      The screen will eventually be refreshed.
1993
 *
1994
 *--------------------------------------------------------------
1995
 */
1996
 
1997
void
1998
Tk_CanvasEventuallyRedraw(canvas, x1, y1, x2, y2)
1999
    Tk_Canvas canvas;           /* Information about widget. */
2000
    int x1, y1;                 /* Upper left corner of area to redraw.
2001
                                 * Pixels on edge are redrawn. */
2002
    int x2, y2;                 /* Lower right corner of area to redraw.
2003
                                 * Pixels on edge are not redrawn. */
2004
{
2005
    TkCanvas *canvasPtr = (TkCanvas *) canvas;
2006
    if ((x1 == x2) || (y1 == y2)) {
2007
        return;
2008
    }
2009
    if (canvasPtr->flags & REDRAW_PENDING) {
2010
        if (x1 <= canvasPtr->redrawX1) {
2011
            canvasPtr->redrawX1 = x1;
2012
        }
2013
        if (y1 <= canvasPtr->redrawY1) {
2014
            canvasPtr->redrawY1 = y1;
2015
        }
2016
        if (x2 >= canvasPtr->redrawX2) {
2017
            canvasPtr->redrawX2 = x2;
2018
        }
2019
        if (y2 >= canvasPtr->redrawY2) {
2020
            canvasPtr->redrawY2 = y2;
2021
        }
2022
    } else {
2023
        canvasPtr->redrawX1 = x1;
2024
        canvasPtr->redrawY1 = y1;
2025
        canvasPtr->redrawX2 = x2;
2026
        canvasPtr->redrawY2 = y2;
2027
        Tcl_DoWhenIdle(DisplayCanvas, (ClientData) canvasPtr);
2028
        canvasPtr->flags |= REDRAW_PENDING;
2029
    }
2030
}
2031
 
2032
/*
2033
 *--------------------------------------------------------------
2034
 *
2035
 * Tk_CreateItemType --
2036
 *
2037
 *      This procedure may be invoked to add a new kind of canvas
2038
 *      element to the core item types supported by Tk.
2039
 *
2040
 * Results:
2041
 *      None.
2042
 *
2043
 * Side effects:
2044
 *      From now on, the new item type will be useable in canvas
2045
 *      widgets (e.g. typePtr->name can be used as the item type
2046
 *      in "create" widget commands).  If there was already a
2047
 *      type with the same name as in typePtr, it is replaced with
2048
 *      the new type.
2049
 *
2050
 *--------------------------------------------------------------
2051
 */
2052
 
2053
void
2054
Tk_CreateItemType(typePtr)
2055
    Tk_ItemType *typePtr;               /* Information about item type;
2056
                                         * storage must be statically
2057
                                         * allocated (must live forever). */
2058
{
2059
    Tk_ItemType *typePtr2, *prevPtr;
2060
 
2061
    if (typeList == NULL) {
2062
        InitCanvas();
2063
    }
2064
 
2065
    /*
2066
     * If there's already an item type with the given name, remove it.
2067
     */
2068
 
2069
    for (typePtr2 = typeList, prevPtr = NULL; typePtr2 != NULL;
2070
            prevPtr = typePtr2, typePtr2 = typePtr2->nextPtr) {
2071
        if (strcmp(typePtr2->name, typePtr->name) == 0) {
2072
            if (prevPtr == NULL) {
2073
                typeList = typePtr2->nextPtr;
2074
            } else {
2075
                prevPtr->nextPtr = typePtr2->nextPtr;
2076
            }
2077
            break;
2078
        }
2079
    }
2080
    typePtr->nextPtr = typeList;
2081
    typeList = typePtr;
2082
}
2083
 
2084
/*
2085
 *----------------------------------------------------------------------
2086
 *
2087
 * Tk_GetItemTypes --
2088
 *
2089
 *      This procedure returns a pointer to the list of all item
2090
 *      types.
2091
 *
2092
 * Results:
2093
 *      The return value is a pointer to the first in the list
2094
 *      of item types currently supported by canvases.
2095
 *
2096
 * Side effects:
2097
 *      None.
2098
 *
2099
 *----------------------------------------------------------------------
2100
 */
2101
 
2102
Tk_ItemType *
2103
Tk_GetItemTypes()
2104
{
2105
    if (typeList == NULL) {
2106
        InitCanvas();
2107
    }
2108
    return typeList;
2109
}
2110
 
2111
/*
2112
 *--------------------------------------------------------------
2113
 *
2114
 * InitCanvas --
2115
 *
2116
 *      This procedure is invoked to perform once-only-ever
2117
 *      initialization for the module, such as setting up
2118
 *      the type table.
2119
 *
2120
 * Results:
2121
 *      None.
2122
 *
2123
 * Side effects:
2124
 *      None.
2125
 *
2126
 *--------------------------------------------------------------
2127
 */
2128
 
2129
static void
2130
InitCanvas()
2131
{
2132
    if (typeList != NULL) {
2133
        return;
2134
    }
2135
    typeList = &tkRectangleType;
2136
    tkRectangleType.nextPtr = &tkTextType;
2137
    tkTextType.nextPtr = &tkLineType;
2138
    tkLineType.nextPtr = &tkPolygonType;
2139
    tkPolygonType.nextPtr = &tkImageType;
2140
    tkImageType.nextPtr = &tkOvalType;
2141
    tkOvalType.nextPtr = &tkBitmapType;
2142
    tkBitmapType.nextPtr = &tkArcType;
2143
    tkArcType.nextPtr = &tkWindowType;
2144
    tkWindowType.nextPtr = NULL;
2145
    allUid = Tk_GetUid("all");
2146
    currentUid = Tk_GetUid("current");
2147
}
2148
 
2149
/*
2150
 *--------------------------------------------------------------
2151
 *
2152
 * StartTagSearch --
2153
 *
2154
 *      This procedure is called to initiate an enumeration of
2155
 *      all items in a given canvas that contain a given tag.
2156
 *
2157
 * Results:
2158
 *      The return value is a pointer to the first item in
2159
 *      canvasPtr that matches tag, or NULL if there is no
2160
 *      such item.  The information at *searchPtr is initialized
2161
 *      such that successive calls to NextItem will return
2162
 *      successive items that match tag.
2163
 *
2164
 * Side effects:
2165
 *      SearchPtr is linked into a list of searches in progress
2166
 *      on canvasPtr, so that elements can safely be deleted
2167
 *      while the search is in progress.  EndTagSearch must be
2168
 *      called at the end of the search to unlink searchPtr from
2169
 *      this list.
2170
 *
2171
 *--------------------------------------------------------------
2172
 */
2173
 
2174
static Tk_Item *
2175
StartTagSearch(canvasPtr, tag, searchPtr)
2176
    TkCanvas *canvasPtr;                /* Canvas whose items are to be
2177
                                         * searched. */
2178
    char *tag;                          /* String giving tag value. */
2179
    TagSearch *searchPtr;               /* Record describing tag search;
2180
                                         * will be initialized here. */
2181
{
2182
    int id;
2183
    Tk_Item *itemPtr, *lastPtr;
2184
    Tk_Uid *tagPtr;
2185
    Tk_Uid uid;
2186
    int count;
2187
 
2188
    /*
2189
     * Initialize the search.
2190
     */
2191
 
2192
    searchPtr->canvasPtr = canvasPtr;
2193
    searchPtr->searchOver = 0;
2194
 
2195
    /*
2196
     * Find the first matching item in one of several ways. If the tag
2197
     * is a number then it selects the single item with the matching
2198
     * identifier.  In this case see if the item being requested is the
2199
     * hot item, in which case the search can be skipped.
2200
     */
2201
 
2202
    if (isdigit(UCHAR(*tag))) {
2203
        char *end;
2204
        Tcl_HashEntry *entryPtr;
2205
 
2206
        numIdSearches++;
2207
        id = strtoul(tag, &end, 0);
2208
        if (*end == 0) {
2209
            itemPtr = canvasPtr->hotPtr;
2210
            lastPtr = canvasPtr->hotPrevPtr;
2211
            if ((itemPtr == NULL) || (itemPtr->id != id) || (lastPtr == NULL)
2212
                    || (lastPtr->nextPtr != itemPtr)) {
2213
                numSlowSearches++;
2214
                entryPtr = Tcl_FindHashEntry(&canvasPtr->idTable, (char *) id);
2215
                if (entryPtr != NULL) {
2216
                    itemPtr = (Tk_Item *)Tcl_GetHashValue(entryPtr);
2217
                    lastPtr = itemPtr->prevPtr;
2218
                } else {
2219
                    lastPtr = itemPtr = NULL;
2220
                }
2221
            }
2222
            searchPtr->lastPtr = lastPtr;
2223
            searchPtr->searchOver = 1;
2224
            canvasPtr->hotPtr = itemPtr;
2225
            canvasPtr->hotPrevPtr = lastPtr;
2226
            return itemPtr;
2227
        }
2228
    }
2229
 
2230
    searchPtr->tag = uid = Tk_GetUid(tag);
2231
    if (uid == allUid) {
2232
 
2233
        /*
2234
         * All items match.
2235
         */
2236
 
2237
        searchPtr->tag = NULL;
2238
        searchPtr->lastPtr = NULL;
2239
        searchPtr->currentPtr = canvasPtr->firstItemPtr;
2240
        return canvasPtr->firstItemPtr;
2241
    }
2242
 
2243
    /*
2244
     * None of the above.  Search for an item with a matching tag.
2245
     */
2246
 
2247
    for (lastPtr = NULL, itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
2248
            lastPtr = itemPtr, itemPtr = itemPtr->nextPtr) {
2249
        for (tagPtr = itemPtr->tagPtr, count = itemPtr->numTags;
2250
                count > 0; tagPtr++, count--) {
2251
            if (*tagPtr == uid) {
2252
                searchPtr->lastPtr = lastPtr;
2253
                searchPtr->currentPtr = itemPtr;
2254
                return itemPtr;
2255
            }
2256
        }
2257
    }
2258
    searchPtr->lastPtr = lastPtr;
2259
    searchPtr->searchOver = 1;
2260
    return NULL;
2261
}
2262
 
2263
/*
2264
 *--------------------------------------------------------------
2265
 *
2266
 * NextItem --
2267
 *
2268
 *      This procedure returns successive items that match a given
2269
 *      tag;  it should be called only after StartTagSearch has been
2270
 *      used to begin a search.
2271
 *
2272
 * Results:
2273
 *      The return value is a pointer to the next item that matches
2274
 *      the tag specified to StartTagSearch, or NULL if no such
2275
 *      item exists.  *SearchPtr is updated so that the next call
2276
 *      to this procedure will return the next item.
2277
 *
2278
 * Side effects:
2279
 *      None.
2280
 *
2281
 *--------------------------------------------------------------
2282
 */
2283
 
2284
static Tk_Item *
2285
NextItem(searchPtr)
2286
    TagSearch *searchPtr;               /* Record describing search in
2287
                                         * progress. */
2288
{
2289
    Tk_Item *itemPtr, *lastPtr;
2290
    int count;
2291
    Tk_Uid uid;
2292
    Tk_Uid *tagPtr;
2293
 
2294
    /*
2295
     * Find next item in list (this may not actually be a suitable
2296
     * one to return), and return if there are no items left.
2297
     */
2298
 
2299
    lastPtr = searchPtr->lastPtr;
2300
    if (lastPtr == NULL) {
2301
        itemPtr = searchPtr->canvasPtr->firstItemPtr;
2302
    } else {
2303
        itemPtr = lastPtr->nextPtr;
2304
    }
2305
    if ((itemPtr == NULL) || (searchPtr->searchOver)) {
2306
        searchPtr->searchOver = 1;
2307
        return NULL;
2308
    }
2309
    if (itemPtr != searchPtr->currentPtr) {
2310
        /*
2311
         * The structure of the list has changed.  Probably the
2312
         * previously-returned item was removed from the list.
2313
         * In this case, don't advance lastPtr;  just return
2314
         * its new successor (i.e. do nothing here).
2315
         */
2316
    } else {
2317
        lastPtr = itemPtr;
2318
        itemPtr = lastPtr->nextPtr;
2319
    }
2320
 
2321
    /*
2322
     * Handle special case of "all" search by returning next item.
2323
     */
2324
 
2325
    uid = searchPtr->tag;
2326
    if (uid == NULL) {
2327
        searchPtr->lastPtr = lastPtr;
2328
        searchPtr->currentPtr = itemPtr;
2329
        return itemPtr;
2330
    }
2331
 
2332
    /*
2333
     * Look for an item with a particular tag.
2334
     */
2335
 
2336
    for ( ; itemPtr != NULL; lastPtr = itemPtr, itemPtr = itemPtr->nextPtr) {
2337
        for (tagPtr = itemPtr->tagPtr, count = itemPtr->numTags;
2338
                count > 0; tagPtr++, count--) {
2339
            if (*tagPtr == uid) {
2340
                searchPtr->lastPtr = lastPtr;
2341
                searchPtr->currentPtr = itemPtr;
2342
                return itemPtr;
2343
            }
2344
        }
2345
    }
2346
    searchPtr->lastPtr = lastPtr;
2347
    searchPtr->searchOver = 1;
2348
    return NULL;
2349
}
2350
 
2351
/*
2352
 *--------------------------------------------------------------
2353
 *
2354
 * DoItem --
2355
 *
2356
 *      This is a utility procedure called by FindItems.  It
2357
 *      either adds itemPtr's id to the result forming in interp,
2358
 *      or it adds a new tag to itemPtr, depending on the value
2359
 *      of tag.
2360
 *
2361
 * Results:
2362
 *      None.
2363
 *
2364
 * Side effects:
2365
 *      If tag is NULL then itemPtr's id is added as a list element
2366
 *      to interp->result;  otherwise tag is added to itemPtr's
2367
 *      list of tags.
2368
 *
2369
 *--------------------------------------------------------------
2370
 */
2371
 
2372
static void
2373
DoItem(interp, itemPtr, tag)
2374
    Tcl_Interp *interp;                 /* Interpreter in which to (possibly)
2375
                                         * record item id. */
2376
    Tk_Item *itemPtr;                   /* Item to (possibly) modify. */
2377
    Tk_Uid tag;                         /* Tag to add to those already
2378
                                         * present for item, or NULL. */
2379
{
2380
    Tk_Uid *tagPtr;
2381
    int count;
2382
 
2383
    /*
2384
     * Handle the "add-to-result" case and return, if appropriate.
2385
     */
2386
 
2387
    if (tag == NULL) {
2388
        char msg[30];
2389
        sprintf(msg, "%d", itemPtr->id);
2390
        Tcl_AppendElement(interp, msg);
2391
        return;
2392
    }
2393
 
2394
    for (tagPtr = itemPtr->tagPtr, count = itemPtr->numTags;
2395
            count > 0; tagPtr++, count--) {
2396
        if (tag == *tagPtr) {
2397
            return;
2398
        }
2399
    }
2400
 
2401
    /*
2402
     * Grow the tag space if there's no more room left in the current
2403
     * block.
2404
     */
2405
 
2406
    if (itemPtr->tagSpace == itemPtr->numTags) {
2407
        Tk_Uid *newTagPtr;
2408
 
2409
        itemPtr->tagSpace += 5;
2410
        newTagPtr = (Tk_Uid *) ckalloc((unsigned)
2411
                (itemPtr->tagSpace * sizeof(Tk_Uid)));
2412
        memcpy((VOID *) newTagPtr, (VOID *) itemPtr->tagPtr,
2413
                (itemPtr->numTags * sizeof(Tk_Uid)));
2414
        if (itemPtr->tagPtr != itemPtr->staticTagSpace) {
2415
            ckfree((char *) itemPtr->tagPtr);
2416
        }
2417
        itemPtr->tagPtr = newTagPtr;
2418
        tagPtr = &itemPtr->tagPtr[itemPtr->numTags];
2419
    }
2420
 
2421
    /*
2422
     * Add in the new tag.
2423
     */
2424
 
2425
    *tagPtr = tag;
2426
    itemPtr->numTags++;
2427
}
2428
 
2429
/*
2430
 *--------------------------------------------------------------
2431
 *
2432
 * FindItems --
2433
 *
2434
 *      This procedure does all the work of implementing the
2435
 *      "find" and "addtag" options of the canvas widget command,
2436
 *      which locate items that have certain features (location,
2437
 *      tags, position in display list, etc.).
2438
 *
2439
 * Results:
2440
 *      A standard Tcl return value.  If newTag is NULL, then a
2441
 *      list of ids from all the items that match argc/argv is
2442
 *      returned in interp->result.  If newTag is NULL, then
2443
 *      the normal interp->result is an empty string.  If an error
2444
 *      occurs, then interp->result will hold an error message.
2445
 *
2446
 * Side effects:
2447
 *      If newTag is non-NULL, then all the items that match the
2448
 *      information in argc/argv have that tag added to their
2449
 *      lists of tags.
2450
 *
2451
 *--------------------------------------------------------------
2452
 */
2453
 
2454
static int
2455
FindItems(interp, canvasPtr, argc, argv, newTag, cmdName, option)
2456
    Tcl_Interp *interp;                 /* Interpreter for error reporting. */
2457
    TkCanvas *canvasPtr;                /* Canvas whose items are to be
2458
                                         * searched. */
2459
    int argc;                           /* Number of entries in argv.  Must be
2460
                                         * greater than zero. */
2461
    char **argv;                        /* Arguments that describe what items
2462
                                         * to search for (see user doc on
2463
                                         * "find" and "addtag" options). */
2464
    char *newTag;                       /* If non-NULL, gives new tag to set
2465
                                         * on all found items;  if NULL, then
2466
                                         * ids of found items are returned
2467
                                         * in interp->result. */
2468
    char *cmdName;                      /* Name of original Tcl command, for
2469
                                         * use in error messages. */
2470
    char *option;                       /* For error messages:  gives option
2471
                                         * from Tcl command and other stuff
2472
                                         * up to what's in argc/argv. */
2473
{
2474
    int c;
2475
    size_t length;
2476
    TagSearch search;
2477
    Tk_Item *itemPtr;
2478
    Tk_Uid uid;
2479
 
2480
    if (newTag != NULL) {
2481
        uid = Tk_GetUid(newTag);
2482
    } else {
2483
        uid = NULL;
2484
    }
2485
    c = argv[0][0];
2486
    length = strlen(argv[0]);
2487
    if ((c == 'a') && (strncmp(argv[0], "above", length) == 0)
2488
            && (length >= 2)) {
2489
        Tk_Item *lastPtr = NULL;
2490
        if (argc != 2) {
2491
            Tcl_AppendResult(interp, "wrong # args: should be \"",
2492
                    cmdName, option, " above tagOrId", (char *) NULL);
2493
            return TCL_ERROR;
2494
        }
2495
        for (itemPtr = StartTagSearch(canvasPtr, argv[1], &search);
2496
                itemPtr != NULL; itemPtr = NextItem(&search)) {
2497
            lastPtr = itemPtr;
2498
        }
2499
        if ((lastPtr != NULL) && (lastPtr->nextPtr != NULL)) {
2500
            DoItem(interp, lastPtr->nextPtr, uid);
2501
        }
2502
    } else if ((c == 'a') && (strncmp(argv[0], "all", length) == 0)
2503
            && (length >= 2)) {
2504
        if (argc != 1) {
2505
            Tcl_AppendResult(interp, "wrong # args: should be \"",
2506
                    cmdName, option, " all", (char *) NULL);
2507
            return TCL_ERROR;
2508
        }
2509
 
2510
        for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
2511
                itemPtr = itemPtr->nextPtr) {
2512
            DoItem(interp, itemPtr, uid);
2513
        }
2514
    } else if ((c == 'b') && (strncmp(argv[0], "below", length) == 0)) {
2515
        Tk_Item *itemPtr;
2516
 
2517
        if (argc != 2) {
2518
            Tcl_AppendResult(interp, "wrong # args: should be \"",
2519
                    cmdName, option, " below tagOrId", (char *) NULL);
2520
            return TCL_ERROR;
2521
        }
2522
        itemPtr = StartTagSearch(canvasPtr, argv[1], &search);
2523
        if (itemPtr->prevPtr != NULL) {
2524
            DoItem(interp, itemPtr->prevPtr, uid);
2525
        }
2526
    } else if ((c == 'c') && (strncmp(argv[0], "closest", length) == 0)) {
2527
        double closestDist;
2528
        Tk_Item *startPtr, *closestPtr;
2529
        double coords[2], halo;
2530
        int x1, y1, x2, y2;
2531
 
2532
        if ((argc < 3) || (argc > 5)) {
2533
            Tcl_AppendResult(interp, "wrong # args: should be \"",
2534
                    cmdName, option, " closest x y ?halo? ?start?",
2535
                    (char *) NULL);
2536
            return TCL_ERROR;
2537
        }
2538
        if ((Tk_CanvasGetCoord(interp, (Tk_Canvas) canvasPtr, argv[1],
2539
                &coords[0]) != TCL_OK) || (Tk_CanvasGetCoord(interp,
2540
                (Tk_Canvas) canvasPtr, argv[2], &coords[1]) != TCL_OK)) {
2541
            return TCL_ERROR;
2542
        }
2543
        if (argc > 3) {
2544
            if (Tk_CanvasGetCoord(interp, (Tk_Canvas) canvasPtr, argv[3],
2545
                    &halo) != TCL_OK) {
2546
                return TCL_ERROR;
2547
            }
2548
            if (halo < 0.0) {
2549
                Tcl_AppendResult(interp, "can't have negative halo value \"",
2550
                        argv[3], "\"", (char *) NULL);
2551
                return TCL_ERROR;
2552
            }
2553
        } else {
2554
            halo = 0.0;
2555
        }
2556
 
2557
        /*
2558
         * Find the item at which to start the search.
2559
         */
2560
 
2561
        startPtr = canvasPtr->firstItemPtr;
2562
        if (argc == 5) {
2563
            itemPtr = StartTagSearch(canvasPtr, argv[4], &search);
2564
            if (itemPtr != NULL) {
2565
                startPtr = itemPtr;
2566
            }
2567
        }
2568
 
2569
        /*
2570
         * The code below is optimized so that it can eliminate most
2571
         * items without having to call their item-specific procedures.
2572
         * This is done by keeping a bounding box (x1, y1, x2, y2) that
2573
         * an item's bbox must overlap if the item is to have any
2574
         * chance of being closer than the closest so far.
2575
         */
2576
 
2577
        itemPtr = startPtr;
2578
        if (itemPtr == NULL) {
2579
            return TCL_OK;
2580
        }
2581
        closestDist = (*itemPtr->typePtr->pointProc)((Tk_Canvas) canvasPtr,
2582
                itemPtr, coords) - halo;
2583
        if (closestDist < 0.0) {
2584
            closestDist = 0.0;
2585
        }
2586
        while (1) {
2587
            double newDist;
2588
 
2589
            /*
2590
             * Update the bounding box using itemPtr, which is the
2591
             * new closest item.
2592
             */
2593
 
2594
            x1 = (int) (coords[0] - closestDist - halo - 1);
2595
            y1 = (int) (coords[1] - closestDist - halo - 1);
2596
            x2 = (int) (coords[0] + closestDist + halo + 1);
2597
            y2 = (int) (coords[1] + closestDist + halo + 1);
2598
            closestPtr = itemPtr;
2599
 
2600
            /*
2601
             * Search for an item that beats the current closest one.
2602
             * Work circularly through the canvas's item list until
2603
             * getting back to the starting item.
2604
             */
2605
 
2606
            while (1) {
2607
                itemPtr = itemPtr->nextPtr;
2608
                if (itemPtr == NULL) {
2609
                    itemPtr = canvasPtr->firstItemPtr;
2610
                }
2611
                if (itemPtr == startPtr) {
2612
                    DoItem(interp, closestPtr, uid);
2613
                    return TCL_OK;
2614
                }
2615
                if ((itemPtr->x1 >= x2) || (itemPtr->x2 <= x1)
2616
                        || (itemPtr->y1 >= y2) || (itemPtr->y2 <= y1)) {
2617
                    continue;
2618
                }
2619
                newDist = (*itemPtr->typePtr->pointProc)((Tk_Canvas) canvasPtr,
2620
                        itemPtr, coords) - halo;
2621
                if (newDist < 0.0) {
2622
                    newDist = 0.0;
2623
                }
2624
                if (newDist <= closestDist) {
2625
                    closestDist = newDist;
2626
                    break;
2627
                }
2628
            }
2629
        }
2630
    } else if ((c == 'e') && (strncmp(argv[0], "enclosed", length) == 0)) {
2631
        if (argc != 5) {
2632
            Tcl_AppendResult(interp, "wrong # args: should be \"",
2633
                    cmdName, option, " enclosed x1 y1 x2 y2", (char *) NULL);
2634
            return TCL_ERROR;
2635
        }
2636
        return FindArea(interp, canvasPtr, argv+1, uid, 1);
2637
    } else if ((c == 'o') && (strncmp(argv[0], "overlapping", length) == 0)) {
2638
        if (argc != 5) {
2639
            Tcl_AppendResult(interp, "wrong # args: should be \"",
2640
                    cmdName, option, " overlapping x1 y1 x2 y2",
2641
                    (char *) NULL);
2642
            return TCL_ERROR;
2643
        }
2644
        return FindArea(interp, canvasPtr, argv+1, uid, 0);
2645
    } else if ((c == 'w') && (strncmp(argv[0], "withtag", length) == 0)) {
2646
                if (argc != 2) {
2647
            Tcl_AppendResult(interp, "wrong # args: should be \"",
2648
                    cmdName, option, " withtag tagOrId", (char *) NULL);
2649
            return TCL_ERROR;
2650
        }
2651
        for (itemPtr = StartTagSearch(canvasPtr, argv[1], &search);
2652
                itemPtr != NULL; itemPtr = NextItem(&search)) {
2653
            DoItem(interp, itemPtr, uid);
2654
        }
2655
    } else  {
2656
        Tcl_AppendResult(interp, "bad search command \"", argv[0],
2657
                "\": must be above, all, below, closest, enclosed, ",
2658
                "overlapping, or withtag", (char *) NULL);
2659
        return TCL_ERROR;
2660
    }
2661
    return TCL_OK;
2662
}
2663
 
2664
/*
2665
 *--------------------------------------------------------------
2666
 *
2667
 * FindArea --
2668
 *
2669
 *      This procedure implements area searches for the "find"
2670
 *      and "addtag" options.
2671
 *
2672
 * Results:
2673
 *      A standard Tcl return value.  If newTag is NULL, then a
2674
 *      list of ids from all the items overlapping or enclosed
2675
 *      by the rectangle given by argc is returned in interp->result.
2676
 *      If newTag is NULL, then the normal interp->result is an
2677
 *      empty string.  If an error occurs, then interp->result will
2678
 *      hold an error message.
2679
 *
2680
 * Side effects:
2681
 *      If uid is non-NULL, then all the items overlapping
2682
 *      or enclosed by the area in argv have that tag added to
2683
 *      their lists of tags.
2684
 *
2685
 *--------------------------------------------------------------
2686
 */
2687
 
2688
static int
2689
FindArea(interp, canvasPtr, argv, uid, enclosed)
2690
    Tcl_Interp *interp;                 /* Interpreter for error reporting
2691
                                         * and result storing. */
2692
    TkCanvas *canvasPtr;                /* Canvas whose items are to be
2693
                                         * searched. */
2694
    char **argv;                        /* Array of four arguments that
2695
                                         * give the coordinates of the
2696
                                         * rectangular area to search. */
2697
    Tk_Uid uid;                         /* If non-NULL, gives new tag to set
2698
                                         * on all found items;  if NULL, then
2699
                                         * ids of found items are returned
2700
                                         * in interp->result. */
2701
    int enclosed;                       /* 0 means overlapping or enclosed
2702
                                         * items are OK, 1 means only enclosed
2703
                                         * items are OK. */
2704
{
2705
    double rect[4], tmp;
2706
    int x1, y1, x2, y2;
2707
    Tk_Item *itemPtr;
2708
 
2709
    if ((Tk_CanvasGetCoord(interp, (Tk_Canvas) canvasPtr, argv[0],
2710
                &rect[0]) != TCL_OK)
2711
            || (Tk_CanvasGetCoord(interp, (Tk_Canvas) canvasPtr, argv[1],
2712
                &rect[1]) != TCL_OK)
2713
            || (Tk_CanvasGetCoord(interp, (Tk_Canvas) canvasPtr, argv[2],
2714
                &rect[2]) != TCL_OK)
2715
            || (Tk_CanvasGetCoord(interp, (Tk_Canvas) canvasPtr, argv[3],
2716
                &rect[3]) != TCL_OK)) {
2717
        return TCL_ERROR;
2718
    }
2719
    if (rect[0] > rect[2]) {
2720
        tmp = rect[0]; rect[0] = rect[2]; rect[2] = tmp;
2721
    }
2722
    if (rect[1] > rect[3]) {
2723
        tmp = rect[1]; rect[1] = rect[3]; rect[3] = tmp;
2724
    }
2725
 
2726
    /*
2727
     * Use an integer bounding box for a quick test, to avoid
2728
     * calling item-specific code except for items that are close.
2729
     */
2730
 
2731
    x1 = (int) (rect[0]-1.0);
2732
    y1 = (int) (rect[1]-1.0);
2733
    x2 = (int) (rect[2]+1.0);
2734
    y2 = (int) (rect[3]+1.0);
2735
    for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
2736
            itemPtr = itemPtr->nextPtr) {
2737
        if ((itemPtr->x1 >= x2) || (itemPtr->x2 <= x1)
2738
                || (itemPtr->y1 >= y2) || (itemPtr->y2 <= y1)) {
2739
            continue;
2740
        }
2741
        if ((*itemPtr->typePtr->areaProc)((Tk_Canvas) canvasPtr, itemPtr, rect)
2742
                >= enclosed) {
2743
            DoItem(interp, itemPtr, uid);
2744
        }
2745
    }
2746
    return TCL_OK;
2747
}
2748
 
2749
/*
2750
 *--------------------------------------------------------------
2751
 *
2752
 * RelinkItems --
2753
 *
2754
 *      Move one or more items to a different place in the
2755
 *      display order for a canvas.
2756
 *
2757
 * Results:
2758
 *      None.
2759
 *
2760
 * Side effects:
2761
 *      The items identified by "tag" are moved so that they
2762
 *      are all together in the display list and immediately
2763
 *      after prevPtr.  The order of the moved items relative
2764
 *      to each other is not changed.
2765
 *
2766
 *--------------------------------------------------------------
2767
 */
2768
 
2769
static void
2770
RelinkItems(canvasPtr, tag, prevPtr)
2771
    TkCanvas *canvasPtr;        /* Canvas to be modified. */
2772
    char *tag;                  /* Tag identifying items to be moved
2773
                                 * in the redisplay list. */
2774
    Tk_Item *prevPtr;           /* Reposition the items so that they
2775
                                 * go just after this item (NULL means
2776
                                 * put at beginning of list). */
2777
{
2778
    Tk_Item *itemPtr;
2779
    TagSearch search;
2780
    Tk_Item *firstMovePtr, *lastMovePtr;
2781
 
2782
    /*
2783
     * Find all of the items to be moved and remove them from
2784
     * the list, making an auxiliary list running from firstMovePtr
2785
     * to lastMovePtr.  Record their areas for redisplay.
2786
     */
2787
 
2788
    firstMovePtr = lastMovePtr = NULL;
2789
    for (itemPtr = StartTagSearch(canvasPtr, tag, &search);
2790
            itemPtr != NULL; itemPtr = NextItem(&search)) {
2791
        if (itemPtr == prevPtr) {
2792
            /*
2793
             * Item after which insertion is to occur is being
2794
             * moved!  Switch to insert after its predecessor.
2795
             */
2796
 
2797
            prevPtr = prevPtr->prevPtr;
2798
        }
2799
        if (itemPtr->prevPtr == NULL) {
2800
            if (itemPtr->nextPtr != NULL) {
2801
                itemPtr->nextPtr->prevPtr = NULL;
2802
            }
2803
            canvasPtr->firstItemPtr = itemPtr->nextPtr;
2804
        } else {
2805
            if (itemPtr->nextPtr != NULL) {
2806
                itemPtr->nextPtr->prevPtr = itemPtr->prevPtr;
2807
            }
2808
            itemPtr->prevPtr->nextPtr = itemPtr->nextPtr;
2809
        }
2810
        if (canvasPtr->lastItemPtr == itemPtr) {
2811
            canvasPtr->lastItemPtr = itemPtr->prevPtr;
2812
        }
2813
        if (firstMovePtr == NULL) {
2814
            itemPtr->prevPtr = NULL;
2815
            firstMovePtr = itemPtr;
2816
        } else {
2817
            itemPtr->prevPtr = lastMovePtr;
2818
            lastMovePtr->nextPtr = itemPtr;
2819
        }
2820
        lastMovePtr = itemPtr;
2821
        Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr, itemPtr->x1, itemPtr->y1,
2822
                itemPtr->x2, itemPtr->y2);
2823
        canvasPtr->flags |= REPICK_NEEDED;
2824
    }
2825
 
2826
    /*
2827
     * Insert the list of to-be-moved items back into the canvas's
2828
     * at the desired position.
2829
     */
2830
 
2831
    if (firstMovePtr == NULL) {
2832
        return;
2833
    }
2834
    if (prevPtr == NULL) {
2835
        if (canvasPtr->firstItemPtr != NULL) {
2836
            canvasPtr->firstItemPtr->prevPtr = lastMovePtr;
2837
        }
2838
        lastMovePtr->nextPtr = canvasPtr->firstItemPtr;
2839
        canvasPtr->firstItemPtr = firstMovePtr;
2840
    } else {
2841
        if (prevPtr->nextPtr != NULL) {
2842
            prevPtr->nextPtr->prevPtr = lastMovePtr;
2843
        }
2844
        lastMovePtr->nextPtr = prevPtr->nextPtr;
2845
        if (firstMovePtr != NULL) {
2846
            firstMovePtr->prevPtr = prevPtr;
2847
        }
2848
        prevPtr->nextPtr = firstMovePtr;
2849
    }
2850
    if (canvasPtr->lastItemPtr == prevPtr) {
2851
        canvasPtr->lastItemPtr = lastMovePtr;
2852
    }
2853
}
2854
 
2855
/*
2856
 *--------------------------------------------------------------
2857
 *
2858
 * CanvasBindProc --
2859
 *
2860
 *      This procedure is invoked by the Tk dispatcher to handle
2861
 *      events associated with bindings on items.
2862
 *
2863
 * Results:
2864
 *      None.
2865
 *
2866
 * Side effects:
2867
 *      Depends on the command invoked as part of the binding
2868
 *      (if there was any).
2869
 *
2870
 *--------------------------------------------------------------
2871
 */
2872
 
2873
static void
2874
CanvasBindProc(clientData, eventPtr)
2875
    ClientData clientData;              /* Pointer to canvas structure. */
2876
    XEvent *eventPtr;                   /* Pointer to X event that just
2877
                                         * happened. */
2878
{
2879
    TkCanvas *canvasPtr = (TkCanvas *) clientData;
2880
 
2881
    Tcl_Preserve((ClientData) canvasPtr);
2882
 
2883
    /*
2884
     * This code below keeps track of the current modifier state in
2885
     * canvasPtr>state.  This information is used to defer repicks of
2886
     * the current item while buttons are down.
2887
     */
2888
 
2889
    if ((eventPtr->type == ButtonPress) || (eventPtr->type == ButtonRelease)) {
2890
        int mask;
2891
 
2892
        switch (eventPtr->xbutton.button) {
2893
            case Button1:
2894
                mask = Button1Mask;
2895
                break;
2896
            case Button2:
2897
                mask = Button2Mask;
2898
                break;
2899
            case Button3:
2900
                mask = Button3Mask;
2901
                break;
2902
            case Button4:
2903
                mask = Button4Mask;
2904
                break;
2905
            case Button5:
2906
                mask = Button5Mask;
2907
                break;
2908
            default:
2909
                mask = 0;
2910
                break;
2911
        }
2912
 
2913
        /*
2914
         * For button press events, repick the current item using the
2915
         * button state before the event, then process the event.  For
2916
         * button release events, first process the event, then repick
2917
         * the current item using the button state *after* the event
2918
         * (the button has logically gone up before we change the
2919
         * current item).
2920
         */
2921
 
2922
        if (eventPtr->type == ButtonPress) {
2923
            /*
2924
             * On a button press, first repick the current item using
2925
             * the button state before the event, the process the event.
2926
             */
2927
 
2928
            canvasPtr->state = eventPtr->xbutton.state;
2929
            PickCurrentItem(canvasPtr, eventPtr);
2930
            canvasPtr->state ^= mask;
2931
            CanvasDoEvent(canvasPtr, eventPtr);
2932
        } else {
2933
            /*
2934
             * Button release: first process the event, with the button
2935
             * still considered to be down.  Then repick the current
2936
             * item under the assumption that the button is no longer down.
2937
             */
2938
 
2939
            canvasPtr->state = eventPtr->xbutton.state;
2940
            CanvasDoEvent(canvasPtr, eventPtr);
2941
            eventPtr->xbutton.state ^= mask;
2942
            canvasPtr->state = eventPtr->xbutton.state;
2943
            PickCurrentItem(canvasPtr, eventPtr);
2944
            eventPtr->xbutton.state ^= mask;
2945
        }
2946
        goto done;
2947
    } else if ((eventPtr->type == EnterNotify)
2948
            || (eventPtr->type == LeaveNotify)) {
2949
        canvasPtr->state = eventPtr->xcrossing.state;
2950
        PickCurrentItem(canvasPtr, eventPtr);
2951
        goto done;
2952
    } else if (eventPtr->type == MotionNotify) {
2953
        canvasPtr->state = eventPtr->xmotion.state;
2954
        PickCurrentItem(canvasPtr, eventPtr);
2955
    }
2956
    CanvasDoEvent(canvasPtr, eventPtr);
2957
 
2958
    done:
2959
    Tcl_Release((ClientData) canvasPtr);
2960
}
2961
 
2962
/*
2963
 *--------------------------------------------------------------
2964
 *
2965
 * PickCurrentItem --
2966
 *
2967
 *      Find the topmost item in a canvas that contains a given
2968
 *      location and mark the the current item.  If the current
2969
 *      item has changed, generate a fake exit event on the old
2970
 *      current item and a fake enter event on the new current
2971
 *      item.
2972
 *
2973
 * Results:
2974
 *      None.
2975
 *
2976
 * Side effects:
2977
 *      The current item for canvasPtr may change.  If it does,
2978
 *      then the commands associated with item entry and exit
2979
 *      could do just about anything.  A binding script could
2980
 *      delete the canvas, so callers should protect themselves
2981
 *      with Tcl_Preserve and Tcl_Release.
2982
 *
2983
 *--------------------------------------------------------------
2984
 */
2985
 
2986
static void
2987
PickCurrentItem(canvasPtr, eventPtr)
2988
    TkCanvas *canvasPtr;                /* Canvas widget in which to select
2989
                                         * current item. */
2990
    XEvent *eventPtr;                   /* Event describing location of
2991
                                         * mouse cursor.  Must be EnterWindow,
2992
                                         * LeaveWindow, ButtonRelease, or
2993
                                         * MotionNotify. */
2994
{
2995
    double coords[2];
2996
    int buttonDown;
2997
 
2998
    /*
2999
     * Check whether or not a button is down.  If so, we'll log entry
3000
     * and exit into and out of the current item, but not entry into
3001
     * any other item.  This implements a form of grabbing equivalent
3002
     * to what the X server does for windows.
3003
     */
3004
 
3005
    buttonDown = canvasPtr->state
3006
            & (Button1Mask|Button2Mask|Button3Mask|Button4Mask|Button5Mask);
3007
    if (!buttonDown) {
3008
        canvasPtr->flags &= ~LEFT_GRABBED_ITEM;
3009
    }
3010
 
3011
    /*
3012
     * Save information about this event in the canvas.  The event in
3013
     * the canvas is used for two purposes:
3014
     *
3015
     * 1. Event bindings: if the current item changes, fake events are
3016
     *    generated to allow item-enter and item-leave bindings to trigger.
3017
     * 2. Reselection: if the current item gets deleted, can use the
3018
     *    saved event to find a new current item.
3019
     * Translate MotionNotify events into EnterNotify events, since that's
3020
     * what gets reported to item handlers.
3021
     */
3022
 
3023
    if (eventPtr != &canvasPtr->pickEvent) {
3024
        if ((eventPtr->type == MotionNotify)
3025
                || (eventPtr->type == ButtonRelease)) {
3026
            canvasPtr->pickEvent.xcrossing.type = EnterNotify;
3027
            canvasPtr->pickEvent.xcrossing.serial = eventPtr->xmotion.serial;
3028
            canvasPtr->pickEvent.xcrossing.send_event
3029
                    = eventPtr->xmotion.send_event;
3030
            canvasPtr->pickEvent.xcrossing.display = eventPtr->xmotion.display;
3031
            canvasPtr->pickEvent.xcrossing.window = eventPtr->xmotion.window;
3032
            canvasPtr->pickEvent.xcrossing.root = eventPtr->xmotion.root;
3033
            canvasPtr->pickEvent.xcrossing.subwindow = None;
3034
            canvasPtr->pickEvent.xcrossing.time = eventPtr->xmotion.time;
3035
            canvasPtr->pickEvent.xcrossing.x = eventPtr->xmotion.x;
3036
            canvasPtr->pickEvent.xcrossing.y = eventPtr->xmotion.y;
3037
            canvasPtr->pickEvent.xcrossing.x_root = eventPtr->xmotion.x_root;
3038
            canvasPtr->pickEvent.xcrossing.y_root = eventPtr->xmotion.y_root;
3039
            canvasPtr->pickEvent.xcrossing.mode = NotifyNormal;
3040
            canvasPtr->pickEvent.xcrossing.detail = NotifyNonlinear;
3041
            canvasPtr->pickEvent.xcrossing.same_screen
3042
                    = eventPtr->xmotion.same_screen;
3043
            canvasPtr->pickEvent.xcrossing.focus = False;
3044
            canvasPtr->pickEvent.xcrossing.state = eventPtr->xmotion.state;
3045
        } else  {
3046
            canvasPtr->pickEvent = *eventPtr;
3047
        }
3048
    }
3049
 
3050
    /*
3051
     * If this is a recursive call (there's already a partially completed
3052
     * call pending on the stack;  it's in the middle of processing a
3053
     * Leave event handler for the old current item) then just return;
3054
     * the pending call will do everything that's needed.
3055
     */
3056
 
3057
    if (canvasPtr->flags & REPICK_IN_PROGRESS) {
3058
        return;
3059
    }
3060
 
3061
    /*
3062
     * A LeaveNotify event automatically means that there's no current
3063
     * object, so the check for closest item can be skipped.
3064
     */
3065
 
3066
    coords[0] = canvasPtr->pickEvent.xcrossing.x + canvasPtr->xOrigin;
3067
    coords[1] = canvasPtr->pickEvent.xcrossing.y + canvasPtr->yOrigin;
3068
    if (canvasPtr->pickEvent.type != LeaveNotify) {
3069
        canvasPtr->newCurrentPtr = CanvasFindClosest(canvasPtr, coords);
3070
    } else {
3071
        canvasPtr->newCurrentPtr = NULL;
3072
    }
3073
 
3074
    if ((canvasPtr->newCurrentPtr == canvasPtr->currentItemPtr)
3075
            && !(canvasPtr->flags & LEFT_GRABBED_ITEM)) {
3076
        /*
3077
         * Nothing to do:  the current item hasn't changed.
3078
         */
3079
 
3080
        return;
3081
    }
3082
 
3083
    /*
3084
     * Simulate a LeaveNotify event on the previous current item and
3085
     * an EnterNotify event on the new current item.  Remove the "current"
3086
     * tag from the previous current item and place it on the new current
3087
     * item.
3088
     */
3089
 
3090
    if ((canvasPtr->newCurrentPtr != canvasPtr->currentItemPtr)
3091
            && (canvasPtr->currentItemPtr != NULL)
3092
            && !(canvasPtr->flags & LEFT_GRABBED_ITEM)) {
3093
        XEvent event;
3094
        Tk_Item *itemPtr = canvasPtr->currentItemPtr;
3095
        int i;
3096
 
3097
        event = canvasPtr->pickEvent;
3098
        event.type = LeaveNotify;
3099
 
3100
        /*
3101
         * If the event's detail happens to be NotifyInferior the
3102
         * binding mechanism will discard the event.  To be consistent,
3103
         * always use NotifyAncestor.
3104
         */
3105
 
3106
        event.xcrossing.detail = NotifyAncestor;
3107
        canvasPtr->flags |= REPICK_IN_PROGRESS;
3108
        CanvasDoEvent(canvasPtr, &event);
3109
        canvasPtr->flags &= ~REPICK_IN_PROGRESS;
3110
 
3111
        /*
3112
         * The check below is needed because there could be an event
3113
         * handler for <LeaveNotify> that deletes the current item.
3114
         */
3115
 
3116
        if ((itemPtr == canvasPtr->currentItemPtr) && !buttonDown) {
3117
            for (i = itemPtr->numTags-1; i >= 0; i--) {
3118
                if (itemPtr->tagPtr[i] == currentUid) {
3119
                    itemPtr->tagPtr[i] = itemPtr->tagPtr[itemPtr->numTags-1];
3120
                    itemPtr->numTags--;
3121
                    break;
3122
                }
3123
            }
3124
        }
3125
 
3126
        /*
3127
         * Note:  during CanvasDoEvent above, it's possible that
3128
         * canvasPtr->newCurrentPtr got reset to NULL because the
3129
         * item was deleted.
3130
         */
3131
    }
3132
    if ((canvasPtr->newCurrentPtr != canvasPtr->currentItemPtr) && buttonDown) {
3133
        canvasPtr->flags |= LEFT_GRABBED_ITEM;
3134
        return;
3135
    }
3136
 
3137
    /*
3138
     * Special note:  it's possible that canvasPtr->newCurrentPtr ==
3139
     * canvasPtr->currentItemPtr here.  This can happen, for example,
3140
     * if LEFT_GRABBED_ITEM was set.
3141
     */
3142
 
3143
    canvasPtr->flags &= ~LEFT_GRABBED_ITEM;
3144
    canvasPtr->currentItemPtr = canvasPtr->newCurrentPtr;
3145
    if (canvasPtr->currentItemPtr != NULL) {
3146
        XEvent event;
3147
 
3148
        DoItem((Tcl_Interp *) NULL, canvasPtr->currentItemPtr, currentUid);
3149
        event = canvasPtr->pickEvent;
3150
        event.type = EnterNotify;
3151
        event.xcrossing.detail = NotifyAncestor;
3152
        CanvasDoEvent(canvasPtr, &event);
3153
    }
3154
}
3155
 
3156
/*
3157
 *----------------------------------------------------------------------
3158
 *
3159
 * CanvasFindClosest --
3160
 *
3161
 *      Given x and y coordinates, find the topmost canvas item that
3162
 *      is "close" to the coordinates.
3163
 *
3164
 * Results:
3165
 *      The return value is a pointer to the topmost item that is
3166
 *      close to (x,y), or NULL if no item is close.
3167
 *
3168
 * Side effects:
3169
 *      None.
3170
 *
3171
 *----------------------------------------------------------------------
3172
 */
3173
 
3174
static Tk_Item *
3175
CanvasFindClosest(canvasPtr, coords)
3176
    TkCanvas *canvasPtr;                /* Canvas widget to search. */
3177
    double coords[2];                   /* Desired x,y position in canvas,
3178
                                         * not screen, coordinates.) */
3179
{
3180
    Tk_Item *itemPtr;
3181
    Tk_Item *bestPtr;
3182
    int x1, y1, x2, y2;
3183
 
3184
    x1 = (int) (coords[0] - canvasPtr->closeEnough);
3185
    y1 = (int) (coords[1] - canvasPtr->closeEnough);
3186
    x2 = (int) (coords[0] + canvasPtr->closeEnough);
3187
    y2 = (int) (coords[1] + canvasPtr->closeEnough);
3188
 
3189
    bestPtr = NULL;
3190
    for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
3191
            itemPtr = itemPtr->nextPtr) {
3192
        if ((itemPtr->x1 > x2) || (itemPtr->x2 < x1)
3193
                || (itemPtr->y1 > y2) || (itemPtr->y2 < y1)) {
3194
            continue;
3195
        }
3196
        if ((*itemPtr->typePtr->pointProc)((Tk_Canvas) canvasPtr,
3197
                itemPtr, coords) <= canvasPtr->closeEnough) {
3198
            bestPtr = itemPtr;
3199
        }
3200
    }
3201
    return bestPtr;
3202
}
3203
 
3204
/*
3205
 *--------------------------------------------------------------
3206
 *
3207
 * CanvasDoEvent --
3208
 *
3209
 *      This procedure is called to invoke binding processing
3210
 *      for a new event that is associated with the current item
3211
 *      for a canvas.
3212
 *
3213
 * Results:
3214
 *      None.
3215
 *
3216
 * Side effects:
3217
 *      Depends on the bindings for the canvas.  A binding script
3218
 *      could delete the canvas, so callers should protect themselves
3219
 *      with Tcl_Preserve and Tcl_Release.
3220
 *
3221
 *--------------------------------------------------------------
3222
 */
3223
 
3224
static void
3225
CanvasDoEvent(canvasPtr, eventPtr)
3226
    TkCanvas *canvasPtr;                /* Canvas widget in which event
3227
                                         * occurred. */
3228
    XEvent *eventPtr;                   /* Real or simulated X event that
3229
                                         * is to be processed. */
3230
{
3231
#define NUM_STATIC 3
3232
    ClientData staticObjects[NUM_STATIC];
3233
    ClientData *objectPtr;
3234
    int numObjects, i;
3235
    Tk_Item *itemPtr;
3236
 
3237
    if (canvasPtr->bindingTable == NULL) {
3238
        return;
3239
    }
3240
 
3241
    itemPtr = canvasPtr->currentItemPtr;
3242
    if ((eventPtr->type == KeyPress) || (eventPtr->type == KeyRelease)) {
3243
        itemPtr = canvasPtr->textInfo.focusItemPtr;
3244
    }
3245
    if (itemPtr == NULL) {
3246
        return;
3247
    }
3248
 
3249
    /*
3250
     * Set up an array with all the relevant objects for processing
3251
     * this event.  The relevant objects are (a) the event's item,
3252
     * (b) the tags associated with the event's item, and (c) the
3253
     * tag "all".  If there are a lot of tags then malloc an array
3254
     * to hold all of the objects.
3255
     */
3256
 
3257
    numObjects = itemPtr->numTags + 2;
3258
    if (numObjects <= NUM_STATIC) {
3259
        objectPtr = staticObjects;
3260
    } else {
3261
        objectPtr = (ClientData *) ckalloc((unsigned)
3262
                (numObjects * sizeof(ClientData)));
3263
    }
3264
    objectPtr[0] = (ClientData) allUid;
3265
    for (i = itemPtr->numTags-1; i >= 0; i--) {
3266
        objectPtr[i+1] = (ClientData) itemPtr->tagPtr[i];
3267
    }
3268
    objectPtr[itemPtr->numTags+1] = (ClientData) itemPtr;
3269
 
3270
    /*
3271
     * Invoke the binding system, then free up the object array if
3272
     * it was malloc-ed.
3273
     */
3274
 
3275
    if (canvasPtr->tkwin != NULL) {
3276
        Tk_BindEvent(canvasPtr->bindingTable, eventPtr, canvasPtr->tkwin,
3277
                numObjects, objectPtr);
3278
    }
3279
    if (objectPtr != staticObjects) {
3280
        ckfree((char *) objectPtr);
3281
    }
3282
}
3283
 
3284
/*
3285
 *----------------------------------------------------------------------
3286
 *
3287
 * CanvasBlinkProc --
3288
 *
3289
 *      This procedure is called as a timer handler to blink the
3290
 *      insertion cursor off and on.
3291
 *
3292
 * Results:
3293
 *      None.
3294
 *
3295
 * Side effects:
3296
 *      The cursor gets turned on or off, redisplay gets invoked,
3297
 *      and this procedure reschedules itself.
3298
 *
3299
 *----------------------------------------------------------------------
3300
 */
3301
 
3302
static void
3303
CanvasBlinkProc(clientData)
3304
    ClientData clientData;      /* Pointer to record describing entry. */
3305
{
3306
    TkCanvas *canvasPtr = (TkCanvas *) clientData;
3307
 
3308
    if (!canvasPtr->textInfo.gotFocus || (canvasPtr->insertOffTime == 0)) {
3309
        return;
3310
    }
3311
    if (canvasPtr->textInfo.cursorOn) {
3312
        canvasPtr->textInfo.cursorOn = 0;
3313
        canvasPtr->insertBlinkHandler = Tcl_CreateTimerHandler(
3314
                canvasPtr->insertOffTime, CanvasBlinkProc,
3315
                (ClientData) canvasPtr);
3316
    } else {
3317
        canvasPtr->textInfo.cursorOn = 1;
3318
        canvasPtr->insertBlinkHandler = Tcl_CreateTimerHandler(
3319
                canvasPtr->insertOnTime, CanvasBlinkProc,
3320
                (ClientData) canvasPtr);
3321
    }
3322
    if (canvasPtr->textInfo.focusItemPtr != NULL) {
3323
        Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
3324
                canvasPtr->textInfo.focusItemPtr->x1,
3325
                canvasPtr->textInfo.focusItemPtr->y1,
3326
                canvasPtr->textInfo.focusItemPtr->x2,
3327
                canvasPtr->textInfo.focusItemPtr->y2);
3328
    }
3329
}
3330
 
3331
/*
3332
 *----------------------------------------------------------------------
3333
 *
3334
 * CanvasFocusProc --
3335
 *
3336
 *      This procedure is called whenever a canvas gets or loses the
3337
 *      input focus.  It's also called whenever the window is
3338
 *      reconfigured while it has the focus.
3339
 *
3340
 * Results:
3341
 *      None.
3342
 *
3343
 * Side effects:
3344
 *      The cursor gets turned on or off.
3345
 *
3346
 *----------------------------------------------------------------------
3347
 */
3348
 
3349
static void
3350
CanvasFocusProc(canvasPtr, gotFocus)
3351
    TkCanvas *canvasPtr;        /* Canvas that just got or lost focus. */
3352
    int gotFocus;               /* 1 means window is getting focus, 0 means
3353
                                 * it's losing it. */
3354
{
3355
    Tcl_DeleteTimerHandler(canvasPtr->insertBlinkHandler);
3356
    if (gotFocus) {
3357
        canvasPtr->textInfo.gotFocus = 1;
3358
        canvasPtr->textInfo.cursorOn = 1;
3359
        if (canvasPtr->insertOffTime != 0) {
3360
            canvasPtr->insertBlinkHandler = Tcl_CreateTimerHandler(
3361
                    canvasPtr->insertOffTime, CanvasBlinkProc,
3362
                    (ClientData) canvasPtr);
3363
        }
3364
    } else {
3365
        canvasPtr->textInfo.gotFocus = 0;
3366
        canvasPtr->textInfo.cursorOn = 0;
3367
        canvasPtr->insertBlinkHandler = (Tcl_TimerToken) NULL;
3368
    }
3369
    if (canvasPtr->textInfo.focusItemPtr != NULL) {
3370
        Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
3371
                canvasPtr->textInfo.focusItemPtr->x1,
3372
                canvasPtr->textInfo.focusItemPtr->y1,
3373
                canvasPtr->textInfo.focusItemPtr->x2,
3374
                canvasPtr->textInfo.focusItemPtr->y2);
3375
    }
3376
    if (canvasPtr->highlightWidth > 0) {
3377
        canvasPtr->flags |= REDRAW_BORDERS;
3378
        if (!(canvasPtr->flags & REDRAW_PENDING)) {
3379
            Tcl_DoWhenIdle(DisplayCanvas, (ClientData) canvasPtr);
3380
            canvasPtr->flags |= REDRAW_PENDING;
3381
        }
3382
    }
3383
}
3384
 
3385
/*
3386
 *----------------------------------------------------------------------
3387
 *
3388
 * CanvasSelectTo --
3389
 *
3390
 *      Modify the selection by moving its un-anchored end.  This could
3391
 *      make the selection either larger or smaller.
3392
 *
3393
 * Results:
3394
 *      None.
3395
 *
3396
 * Side effects:
3397
 *      The selection changes.
3398
 *
3399
 *----------------------------------------------------------------------
3400
 */
3401
 
3402
static void
3403
CanvasSelectTo(canvasPtr, itemPtr, index)
3404
    TkCanvas *canvasPtr;        /* Information about widget. */
3405
    Tk_Item *itemPtr;           /* Item that is to hold selection. */
3406
    int index;                  /* Index of element that is to become the
3407
                                 * "other" end of the selection. */
3408
{
3409
    int oldFirst, oldLast;
3410
    Tk_Item *oldSelPtr;
3411
 
3412
    oldFirst = canvasPtr->textInfo.selectFirst;
3413
    oldLast = canvasPtr->textInfo.selectLast;
3414
    oldSelPtr = canvasPtr->textInfo.selItemPtr;
3415
 
3416
    /*
3417
     * Grab the selection if we don't own it already.
3418
     */
3419
 
3420
    if (canvasPtr->textInfo.selItemPtr == NULL) {
3421
        Tk_OwnSelection(canvasPtr->tkwin, XA_PRIMARY, CanvasLostSelection,
3422
                (ClientData) canvasPtr);
3423
    } else if (canvasPtr->textInfo.selItemPtr != itemPtr) {
3424
        Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
3425
                canvasPtr->textInfo.selItemPtr->x1,
3426
                canvasPtr->textInfo.selItemPtr->y1,
3427
                canvasPtr->textInfo.selItemPtr->x2,
3428
                canvasPtr->textInfo.selItemPtr->y2);
3429
    }
3430
    canvasPtr->textInfo.selItemPtr = itemPtr;
3431
 
3432
    if (canvasPtr->textInfo.anchorItemPtr != itemPtr) {
3433
        canvasPtr->textInfo.anchorItemPtr = itemPtr;
3434
        canvasPtr->textInfo.selectAnchor = index;
3435
    }
3436
    if (canvasPtr->textInfo.selectAnchor <= index) {
3437
        canvasPtr->textInfo.selectFirst = canvasPtr->textInfo.selectAnchor;
3438
        canvasPtr->textInfo.selectLast = index;
3439
    } else {
3440
        canvasPtr->textInfo.selectFirst = index;
3441
        canvasPtr->textInfo.selectLast = canvasPtr->textInfo.selectAnchor - 1;
3442
    }
3443
    if ((canvasPtr->textInfo.selectFirst != oldFirst)
3444
            || (canvasPtr->textInfo.selectLast != oldLast)
3445
            || (itemPtr != oldSelPtr)) {
3446
        Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
3447
                itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
3448
    }
3449
}
3450
 
3451
/*
3452
 *--------------------------------------------------------------
3453
 *
3454
 * CanvasFetchSelection --
3455
 *
3456
 *      This procedure is invoked by Tk to return part or all of
3457
 *      the selection, when the selection is in a canvas widget.
3458
 *      This procedure always returns the selection as a STRING.
3459
 *
3460
 * Results:
3461
 *      The return value is the number of non-NULL bytes stored
3462
 *      at buffer.  Buffer is filled (or partially filled) with a
3463
 *      NULL-terminated string containing part or all of the selection,
3464
 *      as given by offset and maxBytes.
3465
 *
3466
 * Side effects:
3467
 *      None.
3468
 *
3469
 *--------------------------------------------------------------
3470
 */
3471
 
3472
static int
3473
CanvasFetchSelection(clientData, offset, buffer, maxBytes)
3474
    ClientData clientData;              /* Information about canvas widget. */
3475
    int offset;                         /* Offset within selection of first
3476
                                         * character to be returned. */
3477
    char *buffer;                       /* Location in which to place
3478
                                         * selection. */
3479
    int maxBytes;                       /* Maximum number of bytes to place
3480
                                         * at buffer, not including terminating
3481
                                         * NULL character. */
3482
{
3483
    TkCanvas *canvasPtr = (TkCanvas *) clientData;
3484
 
3485
    if (canvasPtr->textInfo.selItemPtr == NULL) {
3486
        return -1;
3487
    }
3488
    if (canvasPtr->textInfo.selItemPtr->typePtr->selectionProc == NULL) {
3489
        return -1;
3490
    }
3491
    return (*canvasPtr->textInfo.selItemPtr->typePtr->selectionProc)(
3492
            (Tk_Canvas) canvasPtr, canvasPtr->textInfo.selItemPtr, offset,
3493
            buffer, maxBytes);
3494
}
3495
 
3496
/*
3497
 *----------------------------------------------------------------------
3498
 *
3499
 * CanvasLostSelection --
3500
 *
3501
 *      This procedure is called back by Tk when the selection is
3502
 *      grabbed away from a canvas widget.
3503
 *
3504
 * Results:
3505
 *      None.
3506
 *
3507
 * Side effects:
3508
 *      The existing selection is unhighlighted, and the window is
3509
 *      marked as not containing a selection.
3510
 *
3511
 *----------------------------------------------------------------------
3512
 */
3513
 
3514
static void
3515
CanvasLostSelection(clientData)
3516
    ClientData clientData;              /* Information about entry widget. */
3517
{
3518
    TkCanvas *canvasPtr = (TkCanvas *) clientData;
3519
 
3520
    if (canvasPtr->textInfo.selItemPtr != NULL) {
3521
        Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
3522
                canvasPtr->textInfo.selItemPtr->x1,
3523
                canvasPtr->textInfo.selItemPtr->y1,
3524
                canvasPtr->textInfo.selItemPtr->x2,
3525
                canvasPtr->textInfo.selItemPtr->y2);
3526
    }
3527
    canvasPtr->textInfo.selItemPtr = NULL;
3528
}
3529
 
3530
/*
3531
 *--------------------------------------------------------------
3532
 *
3533
 * GridAlign --
3534
 *
3535
 *      Given a coordinate and a grid spacing, this procedure
3536
 *      computes the location of the nearest grid line to the
3537
 *      coordinate.
3538
 *
3539
 * Results:
3540
 *      The return value is the location of the grid line nearest
3541
 *      to coord.
3542
 *
3543
 * Side effects:
3544
 *      None.
3545
 *
3546
 *--------------------------------------------------------------
3547
 */
3548
 
3549
static double
3550
GridAlign(coord, spacing)
3551
    double coord;               /* Coordinate to grid-align. */
3552
    double spacing;             /* Spacing between grid lines.   If <= 0
3553
                                 * then no alignment is done. */
3554
{
3555
    if (spacing <= 0.0) {
3556
        return coord;
3557
    }
3558
    if (coord < 0) {
3559
        return -((int) ((-coord)/spacing + 0.5)) * spacing;
3560
    }
3561
    return ((int) (coord/spacing + 0.5)) * spacing;
3562
}
3563
 
3564
/*
3565
 *----------------------------------------------------------------------
3566
 *
3567
 * PrintScrollFractions --
3568
 *
3569
 *      Given the range that's visible in the window and the "100%
3570
 *      range" for what's in the canvas, print a string containing
3571
 *      the scroll fractions.  This procedure is used for both x
3572
 *      and y scrolling.
3573
 *
3574
 * Results:
3575
 *      The memory pointed to by string is modified to hold
3576
 *      two real numbers containing the scroll fractions (between
3577
 *      0 and 1) corresponding to the other arguments.
3578
 *
3579
 * Side effects:
3580
 *      None.
3581
 *
3582
 *----------------------------------------------------------------------
3583
 */
3584
 
3585
static void
3586
PrintScrollFractions(screen1, screen2, object1, object2, string)
3587
    int screen1;                /* Lowest coordinate visible in the window. */
3588
    int screen2;                /* Highest coordinate visible in the window. */
3589
    int object1;                /* Lowest coordinate in the object. */
3590
    int object2;                /* Highest coordinate in the object. */
3591
    char *string;               /* Two real numbers get printed here.  Must
3592
                                 * have enough storage for two %g
3593
                                 * conversions. */
3594
{
3595
    double range, f1, f2;
3596
 
3597
    range = object2 - object1;
3598
    if (range <= 0) {
3599
        f1 = 0;
3600
        f2 = 1.0;
3601
    } else {
3602
        f1 = (screen1 - object1)/range;
3603
        if (f1 < 0) {
3604
            f1 = 0.0;
3605
        }
3606
        f2 = (screen2 - object1)/range;
3607
        if (f2 > 1.0) {
3608
            f2 = 1.0;
3609
        }
3610
        if (f2 < f1) {
3611
            f2 = f1;
3612
        }
3613
    }
3614
    sprintf(string, "%g %g", f1, f2);
3615
}
3616
 
3617
/*
3618
 *--------------------------------------------------------------
3619
 *
3620
 * CanvasUpdateScrollbars --
3621
 *
3622
 *      This procedure is invoked whenever a canvas has changed in
3623
 *      a way that requires scrollbars to be redisplayed (e.g. the
3624
 *      view in the canvas has changed).
3625
 *
3626
 * Results:
3627
 *      None.
3628
 *
3629
 * Side effects:
3630
 *      If there are scrollbars associated with the canvas, then
3631
 *      their scrolling commands are invoked to cause them to
3632
 *      redisplay.  If errors occur, additional Tcl commands may
3633
 *      be invoked to process the errors.
3634
 *
3635
 *--------------------------------------------------------------
3636
 */
3637
 
3638
static void
3639
CanvasUpdateScrollbars(canvasPtr)
3640
    TkCanvas *canvasPtr;                /* Information about canvas. */
3641
{
3642
    int result;
3643
    char buffer[200];
3644
    Tcl_Interp *interp;
3645
    int xOrigin, yOrigin, inset, width, height, scrollX1, scrollX2,
3646
        scrollY1, scrollY2;
3647
    char *xScrollCmd, *yScrollCmd;
3648
 
3649
    /*
3650
     * Save all the relevant values from the canvasPtr, because it might be
3651
     * deleted as part of either of the two calls to Tcl_VarEval below.
3652
     */
3653
 
3654
    interp = canvasPtr->interp;
3655
    Tcl_Preserve((ClientData) interp);
3656
    xScrollCmd = canvasPtr->xScrollCmd;
3657
    if (xScrollCmd != (char *) NULL) {
3658
        Tcl_Preserve((ClientData) xScrollCmd);
3659
    }
3660
    yScrollCmd = canvasPtr->yScrollCmd;
3661
    if (yScrollCmd != (char *) NULL) {
3662
        Tcl_Preserve((ClientData) yScrollCmd);
3663
    }
3664
    xOrigin = canvasPtr->xOrigin;
3665
    yOrigin = canvasPtr->yOrigin;
3666
    inset = canvasPtr->inset;
3667
    width = Tk_Width(canvasPtr->tkwin);
3668
    height = Tk_Height(canvasPtr->tkwin);
3669
    scrollX1 = canvasPtr->scrollX1;
3670
    scrollX2 = canvasPtr->scrollX2;
3671
    scrollY1 = canvasPtr->scrollY1;
3672
    scrollY2 = canvasPtr->scrollY2;
3673
    canvasPtr->flags &= ~UPDATE_SCROLLBARS;
3674
    if (canvasPtr->xScrollCmd != NULL) {
3675
        PrintScrollFractions(xOrigin + inset, xOrigin + width - inset,
3676
                scrollX1, scrollX2, buffer);
3677
        result = Tcl_VarEval(interp, xScrollCmd, " ", buffer, (char *) NULL);
3678
        if (result != TCL_OK) {
3679
            Tcl_BackgroundError(interp);
3680
        }
3681
        Tcl_ResetResult(interp);
3682
        Tcl_Release((ClientData) xScrollCmd);
3683
    }
3684
 
3685
    if (yScrollCmd != NULL) {
3686
        PrintScrollFractions(yOrigin + inset, yOrigin + height - inset,
3687
                scrollY1, scrollY2, buffer);
3688
        result = Tcl_VarEval(interp, yScrollCmd, " ", buffer, (char *) NULL);
3689
        if (result != TCL_OK) {
3690
            Tcl_BackgroundError(interp);
3691
        }
3692
        Tcl_ResetResult(interp);
3693
        Tcl_Release((ClientData) yScrollCmd);
3694
    }
3695
    Tcl_Release((ClientData) interp);
3696
}
3697
 
3698
/*
3699
 *--------------------------------------------------------------
3700
 *
3701
 * CanvasSetOrigin --
3702
 *
3703
 *      This procedure is invoked to change the mapping between
3704
 *      canvas coordinates and screen coordinates in the canvas
3705
 *      window.
3706
 *
3707
 * Results:
3708
 *      None.
3709
 *
3710
 * Side effects:
3711
 *      The canvas will be redisplayed to reflect the change in
3712
 *      view.  In addition, scrollbars will be updated if there
3713
 *      are any.
3714
 *
3715
 *--------------------------------------------------------------
3716
 */
3717
 
3718
static void
3719
CanvasSetOrigin(canvasPtr, xOrigin, yOrigin)
3720
    TkCanvas *canvasPtr;        /* Information about canvas. */
3721
    int xOrigin;                /* New X origin for canvas (canvas x-coord
3722
                                 * corresponding to left edge of canvas
3723
                                 * window). */
3724
    int yOrigin;                /* New Y origin for canvas (canvas y-coord
3725
                                 * corresponding to top edge of canvas
3726
                                 * window). */
3727
{
3728
    int left, right, top, bottom, delta;
3729
 
3730
    /*
3731
     * If scroll increments have been set, round the window origin
3732
     * to the nearest multiple of the increments.  Remember, the
3733
     * origin is the place just inside the borders,  not the upper
3734
     * left corner.
3735
     */
3736
 
3737
    if (canvasPtr->xScrollIncrement > 0) {
3738
        if (xOrigin >= 0) {
3739
            xOrigin += canvasPtr->xScrollIncrement/2;
3740
            xOrigin -= (xOrigin + canvasPtr->inset)
3741
                    % canvasPtr->xScrollIncrement;
3742
        } else {
3743
            xOrigin = (-xOrigin) + canvasPtr->xScrollIncrement/2;
3744
            xOrigin = -(xOrigin - (xOrigin - canvasPtr->inset)
3745
                    % canvasPtr->xScrollIncrement);
3746
        }
3747
    }
3748
    if (canvasPtr->yScrollIncrement > 0) {
3749
        if (yOrigin >= 0) {
3750
            yOrigin += canvasPtr->yScrollIncrement/2;
3751
            yOrigin -= (yOrigin + canvasPtr->inset)
3752
                    % canvasPtr->yScrollIncrement;
3753
        } else {
3754
            yOrigin = (-yOrigin) + canvasPtr->yScrollIncrement/2;
3755
            yOrigin = -(yOrigin - (yOrigin - canvasPtr->inset)
3756
                    % canvasPtr->yScrollIncrement);
3757
        }
3758
    }
3759
 
3760
    /*
3761
     * Adjust the origin if necessary to keep as much as possible of the
3762
     * canvas in the view.  The variables left, right, etc. keep track of
3763
     * how much extra space there is on each side of the view before it
3764
     * will stick out past the scroll region.  If one side sticks out past
3765
     * the edge of the scroll region, adjust the view to bring that side
3766
     * back to the edge of the scrollregion (but don't move it so much that
3767
     * the other side sticks out now).  If scroll increments are in effect,
3768
     * be sure to adjust only by full increments.
3769
     */
3770
 
3771
    if ((canvasPtr->confine) && (canvasPtr->regionString != NULL)) {
3772
        left = xOrigin + canvasPtr->inset - canvasPtr->scrollX1;
3773
        right = canvasPtr->scrollX2
3774
                - (xOrigin + Tk_Width(canvasPtr->tkwin) - canvasPtr->inset);
3775
        top = yOrigin + canvasPtr->inset - canvasPtr->scrollY1;
3776
        bottom = canvasPtr->scrollY2
3777
                - (yOrigin + Tk_Height(canvasPtr->tkwin) - canvasPtr->inset);
3778
        if ((left < 0) && (right > 0)) {
3779
            delta = (right > -left) ? -left : right;
3780
            if (canvasPtr->xScrollIncrement > 0) {
3781
                delta -= delta % canvasPtr->xScrollIncrement;
3782
            }
3783
            xOrigin += delta;
3784
        } else if ((right < 0) && (left > 0)) {
3785
            delta = (left > -right) ? -right : left;
3786
            if (canvasPtr->xScrollIncrement > 0) {
3787
                delta -= delta % canvasPtr->xScrollIncrement;
3788
            }
3789
            xOrigin -= delta;
3790
        }
3791
        if ((top < 0) && (bottom > 0)) {
3792
            delta = (bottom > -top) ? -top : bottom;
3793
            if (canvasPtr->yScrollIncrement > 0) {
3794
                delta -= delta % canvasPtr->yScrollIncrement;
3795
            }
3796
            yOrigin += delta;
3797
        } else if ((bottom < 0) && (top > 0)) {
3798
            delta = (top > -bottom) ? -bottom : top;
3799
            if (canvasPtr->yScrollIncrement > 0) {
3800
                delta -= delta % canvasPtr->yScrollIncrement;
3801
            }
3802
            yOrigin -= delta;
3803
        }
3804
    }
3805
 
3806
    if ((xOrigin == canvasPtr->xOrigin) && (yOrigin == canvasPtr->yOrigin)) {
3807
        return;
3808
    }
3809
 
3810
    /*
3811
     * Tricky point: must redisplay not only everything that's visible
3812
     * in the window's final configuration, but also everything that was
3813
     * visible in the initial configuration.  This is needed because some
3814
     * item types, like windows, need to know when they move off-screen
3815
     * so they can explicitly undisplay themselves.
3816
     */
3817
 
3818
    Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
3819
            canvasPtr->xOrigin, canvasPtr->yOrigin,
3820
            canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin),
3821
            canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin));
3822
    canvasPtr->xOrigin = xOrigin;
3823
    canvasPtr->yOrigin = yOrigin;
3824
    canvasPtr->flags |= UPDATE_SCROLLBARS;
3825
    Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
3826
            canvasPtr->xOrigin, canvasPtr->yOrigin,
3827
            canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin),
3828
            canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin));
3829
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.