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.
8 * Copyright 1991-1992 Regents of the University of California.
9 * Permission to use, copy, modify, and distribute this
10 * software and its documentation for any purpose and without
11 * fee is hereby granted, provided that the above copyright
12 * notice appear in all copies. The University of California
13 * makes no representations about the suitability of this
14 * software for any purpose. It is provided "as is" without
15 * express or implied warranty.
19 static char rcsid
[] = "$Header: /user6/ouster/wish/RCS/tkCanvas.c,v 1.28 92/08/19 08:47:57 ouster Exp $ SPRITE (Berkeley)";
32 * See tkCanvas.h for key data structures used to implement canvases.
36 * The structure defined below is used to keep track of a tag search
37 * in progress. Only the "prevPtr" field should be accessed by anyone
38 * other than StartTagSearch and NextItem.
41 typedef struct TagSearch
{
42 Tk_Canvas
*canvasPtr
; /* Canvas widget being searched. */
43 Tk_Uid tag
; /* Tag to search for. 0 means return
45 Tk_Item
*prevPtr
; /* Item just before last one found (or NULL
46 * if last one found was first in the item
47 * list of canvasPtr). */
48 Tk_Item
*currentPtr
; /* Pointer to last item returned. */
49 int searchOver
; /* Non-zero means NextItem should always
54 * Information used for argv parsing.
58 static Tk_ConfigSpec configSpecs
[] = {
59 {TK_CONFIG_BORDER
, "-background", "background", "Background",
60 DEF_CANVAS_BG_COLOR
, Tk_Offset(Tk_Canvas
, bgBorder
),
61 TK_CONFIG_COLOR_ONLY
},
62 {TK_CONFIG_COLOR
, (char *) NULL
, (char *) NULL
, (char *) NULL
,
63 (char *) NULL
, Tk_Offset(Tk_Canvas
, bgColor
),
64 TK_CONFIG_COLOR_ONLY
},
65 {TK_CONFIG_BORDER
, "-background", "background", "Background",
66 DEF_CANVAS_BG_MONO
, Tk_Offset(Tk_Canvas
, bgBorder
),
68 {TK_CONFIG_COLOR
, (char *) NULL
, (char *) NULL
, (char *) NULL
,
69 (char *) NULL
, Tk_Offset(Tk_Canvas
, bgColor
),
71 {TK_CONFIG_SYNONYM
, "-bd", "borderWidth", (char *) NULL
,
73 {TK_CONFIG_SYNONYM
, "-bg", "background", (char *) NULL
,
75 {TK_CONFIG_PIXELS
, "-borderwidth", "borderWidth", "BorderWidth",
76 DEF_CANVAS_BORDER_WIDTH
, Tk_Offset(Tk_Canvas
, borderWidth
), 0},
77 {TK_CONFIG_DOUBLE
, "-closeenough", "closeEnough", "CloseEnough",
78 DEF_CANVAS_CLOSE_ENOUGH
, Tk_Offset(Tk_Canvas
, closeEnough
), 0},
79 {TK_CONFIG_BOOLEAN
, "-confine", "confine", "Confine",
80 DEF_CANVAS_CONFINE
, Tk_Offset(Tk_Canvas
, confine
), 0},
81 {TK_CONFIG_ACTIVE_CURSOR
, "-cursor", "cursor", "Cursor",
82 DEF_CANVAS_CURSOR
, Tk_Offset(Tk_Canvas
, cursor
), TK_CONFIG_NULL_OK
},
83 {TK_CONFIG_BORDER
, "-cursorbackground", "cursorBackground", "Foreground",
84 DEF_CANVAS_CURSOR_BG
, Tk_Offset(Tk_Canvas
, cursorBorder
), 0},
85 {TK_CONFIG_PIXELS
, "-cursorborderwidth", "cursorBorderWidth", "BorderWidth",
86 DEF_CANVAS_CURSOR_BD_COLOR
, Tk_Offset(Tk_Canvas
, cursorBorderWidth
),
87 TK_CONFIG_COLOR_ONLY
},
88 {TK_CONFIG_PIXELS
, "-cursorborderwidth", "cursorBorderWidth", "BorderWidth",
89 DEF_CANVAS_CURSOR_BD_MONO
, Tk_Offset(Tk_Canvas
, cursorBorderWidth
),
91 {TK_CONFIG_INT
, "-cursorofftime", "cursorOffTime", "OffTime",
92 DEF_CANVAS_CURSOR_OFF_TIME
, Tk_Offset(Tk_Canvas
, cursorOffTime
), 0},
93 {TK_CONFIG_INT
, "-cursorontime", "cursorOnTime", "OnTime",
94 DEF_CANVAS_CURSOR_ON_TIME
, Tk_Offset(Tk_Canvas
, cursorOnTime
), 0},
95 {TK_CONFIG_PIXELS
, "-cursorwidth", "cursorWidth", "CursorWidth",
96 DEF_CANVAS_CURSOR_WIDTH
, Tk_Offset(Tk_Canvas
, cursorWidth
), 0},
97 {TK_CONFIG_PIXELS
, "-height", "height", "Height",
98 DEF_CANVAS_HEIGHT
, Tk_Offset(Tk_Canvas
, height
), 0},
99 {TK_CONFIG_RELIEF
, "-relief", "relief", "Relief",
100 DEF_CANVAS_RELIEF
, Tk_Offset(Tk_Canvas
, relief
), 0},
101 {TK_CONFIG_PIXELS
, "-scrollincrement", "scrollIncrement", "ScrollIncrement",
102 DEF_CANVAS_SCROLL_INCREMENT
, Tk_Offset(Tk_Canvas
, scrollIncrement
), 0},
103 {TK_CONFIG_STRING
, "-scrollregion", "scrollRegion", "ScrollRegion",
104 DEF_CANVAS_SCROLL_REGION
, Tk_Offset(Tk_Canvas
, regionString
), 0},
105 {TK_CONFIG_BORDER
, "-selectbackground", "selectBackground", "Foreground",
106 DEF_CANVAS_SELECT_COLOR
, Tk_Offset(Tk_Canvas
, selBorder
),
107 TK_CONFIG_COLOR_ONLY
},
108 {TK_CONFIG_BORDER
, "-selectbackground", "selectBackground", "Foreground",
109 DEF_CANVAS_SELECT_MONO
, Tk_Offset(Tk_Canvas
, selBorder
),
110 TK_CONFIG_MONO_ONLY
},
111 {TK_CONFIG_PIXELS
, "-selectborderwidth", "selectBorderWidth", "BorderWidth",
112 DEF_CANVAS_SELECT_BD_COLOR
, Tk_Offset(Tk_Canvas
, selBorderWidth
),
113 TK_CONFIG_COLOR_ONLY
},
114 {TK_CONFIG_PIXELS
, "-selectborderwidth", "selectBorderWidth", "BorderWidth",
115 DEF_CANVAS_SELECT_BD_MONO
, Tk_Offset(Tk_Canvas
, selBorderWidth
),
116 TK_CONFIG_MONO_ONLY
},
117 {TK_CONFIG_COLOR
, "-selectforeground", "selectForeground", "Background",
118 DEF_CANVAS_SELECT_FG_COLOR
, Tk_Offset(Tk_Canvas
, selFgColorPtr
),
119 TK_CONFIG_COLOR_ONLY
},
120 {TK_CONFIG_COLOR
, "-selectforeground", "selectForeground", "Background",
121 DEF_CANVAS_SELECT_FG_MONO
, Tk_Offset(Tk_Canvas
, selFgColorPtr
),
122 TK_CONFIG_MONO_ONLY
},
123 {TK_CONFIG_PIXELS
, "-width", "width", "Width",
124 DEF_CANVAS_WIDTH
, Tk_Offset(Tk_Canvas
, width
), 0},
125 {TK_CONFIG_STRING
, "-xscrollcommand", "xScrollCommand", "ScrollCommand",
126 DEF_CANVAS_X_SCROLL_CMD
, Tk_Offset(Tk_Canvas
, xScrollCmd
), 0},
127 {TK_CONFIG_STRING
, "-yscrollcommand", "yScrollCommand", "ScrollCommand",
128 DEF_CANVAS_Y_SCROLL_CMD
, Tk_Offset(Tk_Canvas
, yScrollCmd
), 0},
129 {TK_CONFIG_END
, (char *) NULL
, (char *) NULL
, (char *) NULL
,
134 * List of all the item types known at present:
137 static Tk_ItemType
*typeList
= NULL
; /* NULL means initialization hasn't
141 * Standard item types provided by Tk:
144 extern Tk_ItemType TkArcType
, TkBitmapType
, TkLineType
;
145 extern Tk_ItemType TkOvalType
, TkPolygonType
;
146 extern Tk_ItemType TkRectangleType
, TkTextType
, TkWindowType
;
149 * Various Tk_Uid's used by this module (set up during initialization):
152 static Tk_Uid allUid
= NULL
;
153 static Tk_Uid currentUid
= NULL
;
156 * Statistics counters:
159 static int numIdSearches
;
160 static int numSlowSearches
;
162 static int CanvasUpdateTime
= 200; // Added by Don.
165 * Prototypes for procedures defined later in this file:
168 static void CanvasBindProc
_ANSI_ARGS_((ClientData clientData
,
170 static void CanvasBlinkProc
_ANSI_ARGS_((ClientData clientData
));
171 static void CanvasDoEvent
_ANSI_ARGS_((Tk_Canvas
*canvasPtr
,
173 static void CanvasEventProc
_ANSI_ARGS_((ClientData clientData
,
175 static int CanvasFetchSelection
_ANSI_ARGS_((
176 ClientData clientData
, int offset
,
177 char *buffer
, int maxBytes
));
178 static void CanvasFocusProc
_ANSI_ARGS_((ClientData clientData
,
180 static void CanvasLostSelection
_ANSI_ARGS_((
181 ClientData clientData
));
182 static void CanvasSelectTo
_ANSI_ARGS_((Tk_Canvas
*canvasPtr
,
183 Tk_Item
*itemPtr
, int index
));
184 static void CanvasSetOrigin
_ANSI_ARGS_((Tk_Canvas
*canvasPtr
,
185 int xOrigin
, int yOrigin
));
186 static int CanvasTagsParseProc
_ANSI_ARGS_((ClientData clientData
,
187 Tcl_Interp
*interp
, Tk_Window tkwin
, char *value
,
188 char *widgRec
, int offset
));
189 static char * CanvasTagsPrintProc
_ANSI_ARGS_((ClientData clientData
,
190 Tk_Window tkwin
, char *widgRec
, int offset
,
191 Tcl_FreeProc
**freeProcPtr
));
192 static void CanvasUpdateScrollbars
_ANSI_ARGS_((
193 Tk_Canvas
*canvasPtr
));
194 static int CanvasWidgetCmd
_ANSI_ARGS_((ClientData clientData
,
195 Tcl_Interp
*interp
, int argc
, char **argv
));
196 static int ConfigureCanvas
_ANSI_ARGS_((Tcl_Interp
*interp
,
197 Tk_Canvas
*canvasPtr
, int argc
, char **argv
,
199 static void DestroyCanvas
_ANSI_ARGS_((ClientData clientData
));
200 static void DisplayCanvas
_ANSI_ARGS_((ClientData clientData
));
201 static void DoItem
_ANSI_ARGS_((Tcl_Interp
*interp
,
202 Tk_Item
*itemPtr
, Tk_Uid tag
));
203 static void EventuallyRedrawArea
_ANSI_ARGS_((Tk_Canvas
*canvasPtr
,
204 int x1
, int y1
, int x2
, int y2
));
205 static int FindItems
_ANSI_ARGS_((Tcl_Interp
*interp
,
206 Tk_Canvas
*canvasPtr
, int argc
, char **argv
,
207 char *newTag
, char *cmdName
, char *option
));
208 static int FindArea
_ANSI_ARGS_((Tcl_Interp
*interp
,
209 Tk_Canvas
*canvasPtr
, char **argv
, Tk_Uid uid
,
211 static double GridAlign
_ANSI_ARGS_((double coord
, double spacing
));
212 static void InitCanvas
_ANSI_ARGS_((void));
213 static Tk_Item
* NextItem
_ANSI_ARGS_((TagSearch
*searchPtr
));
214 static void PickCurrentItem
_ANSI_ARGS_((Tk_Canvas
*canvasPtr
,
216 static void RelinkItems
_ANSI_ARGS_((Tk_Canvas
*canvasPtr
,
217 char *tag
, Tk_Item
*prevPtr
));
218 #if defined(USE_XPM3)
219 static int SaveCanvas
_ANSI_ARGS_((Tcl_Interp
*interp
,
220 Tk_Canvas
*canvasPtr
, char *fileName
, int x
,
221 int y
, unsigned int width
, unsigned int height
));
223 static Tk_Item
* StartTagSearch
_ANSI_ARGS_((Tk_Canvas
*canvasPtr
,
224 char *tag
, TagSearch
*searchPtr
));
227 * Custom option for handling "-tags" options for canvas items:
230 Tk_CustomOption tkCanvasTagsOption
= {
237 *--------------------------------------------------------------
241 * This procedure is invoked to process the "canvas" Tcl
242 * command. See the user documentation for details on what
246 * A standard Tcl result.
249 * See the user documentation.
251 *--------------------------------------------------------------
256 ClientData clientData
, /* Main window associated with
258 Tcl_Interp
*interp
, /* Current interpreter. */
259 int argc
, /* Number of arguments. */
260 char **argv
/* Argument strings. */
263 Tk_Window tkwin
= (Tk_Window
) clientData
;
264 register Tk_Canvas
*canvasPtr
;
267 if (typeList
== NULL
) {
272 Tcl_AppendResult(interp
, "wrong # args: should be \"",
273 argv
[0], " pathName ?options?\"", (char *) NULL
);
277 new = Tk_CreateWindowFromPath(interp
, tkwin
, argv
[1], (char *) NULL
);
283 * Initialize fields that won't be initialized by ConfigureCanvas,
284 * or which ConfigureCanvas expects to have reasonable values
285 * (e.g. resource pointers).
288 canvasPtr
= (Tk_Canvas
*) ckalloc(sizeof(Tk_Canvas
));
289 canvasPtr
->tkwin
= new;
290 canvasPtr
->interp
= interp
;
291 canvasPtr
->firstItemPtr
= NULL
;
292 canvasPtr
->lastItemPtr
= NULL
;
293 canvasPtr
->bgBorder
= NULL
;
294 canvasPtr
->bgColor
= NULL
;
295 canvasPtr
->pixmapGC
= None
;
296 canvasPtr
->selBorder
= NULL
;
297 canvasPtr
->selFgColorPtr
= NULL
;
298 canvasPtr
->selItemPtr
= NULL
;
299 canvasPtr
->selectFirst
= -1;
300 canvasPtr
->selectLast
= -1;
301 canvasPtr
->cursorBorder
= NULL
;
302 canvasPtr
->cursorBlinkHandler
= (Tk_TimerToken
) NULL
;
303 canvasPtr
->focusItemPtr
= NULL
;
304 canvasPtr
->xOrigin
= canvasPtr
->yOrigin
= 0;
305 canvasPtr
->drawableXOrigin
= canvasPtr
->drawableYOrigin
= 0;
306 canvasPtr
->bindingTable
= NULL
;
307 canvasPtr
->currentItemPtr
= NULL
;
308 canvasPtr
->pickEvent
.type
= LeaveNotify
;
309 canvasPtr
->xScrollCmd
= NULL
;
310 canvasPtr
->yScrollCmd
= NULL
;
311 canvasPtr
->regionString
= NULL
;
312 canvasPtr
->hotPtr
= NULL
;
313 canvasPtr
->cursor
= None
;
314 canvasPtr
->pixelsPerMM
= WidthOfScreen(Tk_Screen(new));
315 canvasPtr
->pixelsPerMM
/= WidthMMOfScreen(Tk_Screen(new));
316 canvasPtr
->flags
= 0;
317 canvasPtr
->nextId
= 1;
318 canvasPtr
->updateTimerToken
= NULL
;
320 Tk_SetClass(canvasPtr
->tkwin
, "Canvas");
321 Tk_CreateEventHandler(canvasPtr
->tkwin
, ExposureMask
|StructureNotifyMask
,
322 CanvasEventProc
, (ClientData
) canvasPtr
);
323 Tk_CreateEventHandler(canvasPtr
->tkwin
, KeyPressMask
|KeyReleaseMask
324 |ButtonPressMask
|ButtonReleaseMask
|EnterWindowMask
325 |LeaveWindowMask
|PointerMotionMask
, CanvasBindProc
,
326 (ClientData
) canvasPtr
);
327 Tk_CreateSelHandler(canvasPtr
->tkwin
, XA_STRING
, CanvasFetchSelection
,
328 (ClientData
) canvasPtr
, XA_STRING
);
329 Tcl_CreateCommand(interp
, Tk_PathName(canvasPtr
->tkwin
), CanvasWidgetCmd
,
330 (ClientData
) canvasPtr
, (void (*)(int *)) NULL
);
331 if (ConfigureCanvas(interp
, canvasPtr
, argc
-2, argv
+2, 0) != TCL_OK
) {
334 Tk_CreateFocusHandler(canvasPtr
->tkwin
, CanvasFocusProc
,
335 (ClientData
) canvasPtr
);
337 interp
->result
= Tk_PathName(canvasPtr
->tkwin
);
341 Tk_DestroyWindow(canvasPtr
->tkwin
);
346 *--------------------------------------------------------------
350 * This procedure is invoked to process the Tcl command
351 * that corresponds to a widget managed by this module.
352 * See the user documentation for details on what it does.
355 * A standard Tcl result.
358 * See the user documentation.
360 *--------------------------------------------------------------
365 ClientData clientData
, /* Information about canvas
367 Tcl_Interp
*interp
, /* Current interpreter. */
368 int argc
, /* Number of arguments. */
369 char **argv
/* Argument strings. */
372 register Tk_Canvas
*canvasPtr
= (Tk_Canvas
*) clientData
;
375 Tk_Item
*itemPtr
= NULL
; /* Initialization needed only to
376 * prevent compiler warning. */
380 Tcl_AppendResult(interp
, "wrong # args: should be \"",
381 argv
[0], " option ?arg arg ...?\"", (char *) NULL
);
384 Tk_Preserve((ClientData
) canvasPtr
);
387 length
= strlen(argv
[1]);
388 if ((c
== 'a') && (strncmp(argv
[1], "addtag", length
) == 0)) {
390 Tcl_AppendResult(interp
, "wrong # args: should be \"",
391 argv
[0], " addtags tag searchCommand ?arg arg ...?\"",
395 result
= FindItems(interp
, canvasPtr
, argc
-3, argv
+3, argv
[2], argv
[0],
397 } else if ((c
== 'b') && (strncmp(argv
[1], "bbox", length
) == 0)
400 int x1
= 0, y1
= 0, x2
= 0, y2
= 0; /* Initializations needed
401 * only to prevent compiler
405 Tcl_AppendResult(interp
, "wrong # args: should be \"",
406 argv
[0], " bbox tagOrId ?tagOrId ...?\"",
411 for (i
= 2; i
< argc
; i
++) {
412 for (itemPtr
= StartTagSearch(canvasPtr
, argv
[i
], &search
);
413 itemPtr
!= NULL
; itemPtr
= NextItem(&search
)) {
421 if (itemPtr
->x1
< x1
) {
424 if (itemPtr
->y1
< y1
) {
427 if (itemPtr
->x2
> x2
) {
430 if (itemPtr
->y2
> y2
) {
437 sprintf(interp
->result
, "%d %d %d %d", x1
, y1
, x2
, y2
);
439 } else if ((c
== 'b') && (strncmp(argv
[1], "bind", length
) == 0)
443 if ((argc
< 3) || (argc
> 5)) {
444 Tcl_AppendResult(interp
, "wrong # args: should be \"",
445 argv
[0], " bind tagOrId ?sequence? ?command?\"",
451 * Figure out what object to use for the binding (individual
456 if (isdigit(argv
[2][0])) {
460 id
= strtoul(argv
[2], &end
, 0);
464 for (itemPtr
= canvasPtr
->firstItemPtr
; itemPtr
!= NULL
;
465 itemPtr
= itemPtr
->nextPtr
) {
466 if (itemPtr
->id
== id
) {
467 object
= (ClientData
) itemPtr
;
472 Tcl_AppendResult(interp
, "item \"", argv
[2],
473 "\" doesn't exist", (char *) NULL
);
478 object
= (ClientData
) Tk_GetUid(argv
[2]);
482 * Make a binding table if the canvas doesn't already have
486 if (canvasPtr
->bindingTable
== NULL
) {
487 canvasPtr
->bindingTable
= Tk_CreateBindingTable(interp
);
494 if (argv
[4][0] == 0) {
495 result
= Tk_DeleteBinding(interp
, canvasPtr
->bindingTable
,
499 if (argv
[4][0] == '+') {
503 mask
= Tk_CreateBinding(interp
, canvasPtr
->bindingTable
,
504 object
, argv
[3], argv
[4], append
);
508 if (mask
& ~(ButtonMotionMask
|Button1MotionMask
|Button2MotionMask
509 |Button3MotionMask
|Button4MotionMask
|Button5MotionMask
510 |ButtonPressMask
|ButtonReleaseMask
|EnterWindowMask
511 |LeaveWindowMask
|KeyPressMask
|KeyReleaseMask
512 |PointerMotionMask
)) {
513 Tk_DeleteBinding(interp
, canvasPtr
->bindingTable
,
515 Tcl_ResetResult(interp
);
516 Tcl_AppendResult(interp
, "requested illegal events; ",
517 "only key, button, motion, and enter/leave ",
518 "events may be used", (char *) NULL
);
521 } else if (argc
== 4) {
524 command
= Tk_GetBinding(interp
, canvasPtr
->bindingTable
,
526 if (command
== NULL
) {
529 interp
->result
= command
;
531 Tk_GetAllBindings(interp
, canvasPtr
->bindingTable
, object
);
533 } else if ((c
== 'c') && (strcmp(argv
[1], "canvasx") == 0)) {
537 if ((argc
< 3) || (argc
> 4)) {
538 Tcl_AppendResult(interp
, "wrong # args: should be \"",
539 argv
[0], " canvasx screenx ?gridspacing?\"",
543 if (Tk_GetPixels(interp
, canvasPtr
->tkwin
, argv
[2], &x
) != TCL_OK
) {
547 if (TkGetCanvasCoord(canvasPtr
, argv
[3], &grid
) != TCL_OK
) {
553 x
+= canvasPtr
->xOrigin
;
554 sprintf(interp
->result
, "%g", GridAlign((double) x
, grid
));
555 } else if ((c
== 'c') && (strcmp(argv
[1], "canvasy") == 0)) {
559 if ((argc
< 3) || (argc
> 4)) {
560 Tcl_AppendResult(interp
, "wrong # args: should be \"",
561 argv
[0], " canvasy screeny ?gridspacing?\"",
565 if (Tk_GetPixels(interp
, canvasPtr
->tkwin
, argv
[2], &y
) != TCL_OK
) {
569 if (TkGetCanvasCoord(canvasPtr
, argv
[3], &grid
) != TCL_OK
) {
575 y
+= canvasPtr
->yOrigin
;
576 sprintf(interp
->result
, "%g", GridAlign((double) y
, grid
));
577 } else if ((c
== 'c') && (strncmp(argv
[1], "configure", length
) == 0)
580 result
= Tk_ConfigureInfo(interp
, canvasPtr
->tkwin
, configSpecs
,
581 (char *) canvasPtr
, (char *) NULL
, 0);
582 } else if (argc
== 3) {
583 result
= Tk_ConfigureInfo(interp
, canvasPtr
->tkwin
, configSpecs
,
584 (char *) canvasPtr
, argv
[2], 0);
586 result
= ConfigureCanvas(interp
, canvasPtr
, argc
-2, argv
+2,
587 TK_CONFIG_ARGV_ONLY
);
589 } else if ((c
== 'c') && (strncmp(argv
[1], "coords", length
) == 0)
592 Tcl_AppendResult(interp
, "wrong # args: should be \"",
593 argv
[0], " coords tagOrId ?x y x y ...?\"",
597 itemPtr
= StartTagSearch(canvasPtr
, argv
[2], &search
);
598 if (itemPtr
!= NULL
) {
600 EventuallyRedrawArea(canvasPtr
, itemPtr
->x1
, itemPtr
->y1
,
601 itemPtr
->x2
, itemPtr
->y2
);
603 if (itemPtr
->typePtr
->coordProc
!= NULL
) {
604 result
= (*itemPtr
->typePtr
->coordProc
)(canvasPtr
, itemPtr
,
608 EventuallyRedrawArea(canvasPtr
, itemPtr
->x1
, itemPtr
->y1
,
609 itemPtr
->x2
, itemPtr
->y2
);
612 } else if ((c
== 'c') && (strncmp(argv
[1], "create", length
) == 0)
614 register Tk_ItemType
*typePtr
;
615 Tk_ItemType
*matchPtr
= NULL
;
616 register Tk_Item
*itemPtr
;
619 Tcl_AppendResult(interp
, "wrong # args: should be \"",
620 argv
[0], " create type ?arg arg ...?\"", (char *) NULL
);
624 length
= strlen(argv
[2]);
625 for (typePtr
= typeList
; typePtr
!= NULL
; typePtr
= typePtr
->nextPtr
) {
626 if ((c
== typePtr
->name
[0])
627 && (strncmp(argv
[2], typePtr
->name
, length
) == 0)) {
628 if (matchPtr
!= NULL
) {
630 Tcl_AppendResult(interp
,
631 "unknown or ambiguous item type \"",
632 argv
[2], "\"", (char *) NULL
);
638 if (matchPtr
== NULL
) {
642 itemPtr
= (Tk_Item
*) ckalloc((unsigned) typePtr
->itemSize
);
643 itemPtr
->id
= canvasPtr
->nextId
;
645 itemPtr
->tagPtr
= itemPtr
->staticTagSpace
;
646 itemPtr
->tagSpace
= TK_TAG_SPACE
;
647 itemPtr
->numTags
= 0;
648 itemPtr
->typePtr
= typePtr
;
649 if ((*typePtr
->createProc
)(canvasPtr
, itemPtr
, argc
-3, argv
+3)
651 ckfree((char *) itemPtr
);
654 itemPtr
->nextPtr
= NULL
;
655 canvasPtr
->hotPtr
= itemPtr
;
656 canvasPtr
->hotPrevPtr
= canvasPtr
->lastItemPtr
;
657 if (canvasPtr
->lastItemPtr
== NULL
) {
658 canvasPtr
->firstItemPtr
= itemPtr
;
660 canvasPtr
->lastItemPtr
->nextPtr
= itemPtr
;
662 canvasPtr
->lastItemPtr
= itemPtr
;
663 EventuallyRedrawArea(canvasPtr
, itemPtr
->x1
, itemPtr
->y1
,
664 itemPtr
->x2
, itemPtr
->y2
);
665 canvasPtr
->flags
|= REPICK_NEEDED
;
666 sprintf(interp
->result
, "%d", itemPtr
->id
);
667 } else if ((c
== 'c') && (strncmp(argv
[1], "cursor", length
) == 0)
672 Tcl_AppendResult(interp
, "wrong # args: should be \"",
673 argv
[0], " cursor tagOrId index\"",
677 for (itemPtr
= StartTagSearch(canvasPtr
, argv
[2], &search
);
678 itemPtr
!= NULL
; itemPtr
= NextItem(&search
)) {
679 if ((itemPtr
->typePtr
->indexProc
== NULL
)
680 || (itemPtr
->typePtr
->cursorProc
== NULL
)) {
683 if ((*itemPtr
->typePtr
->indexProc
)(canvasPtr
, itemPtr
,
684 argv
[3], &index
) != TCL_OK
) {
687 (*itemPtr
->typePtr
->cursorProc
)(canvasPtr
, itemPtr
, index
);
688 if ((itemPtr
== canvasPtr
->focusItemPtr
)
689 && (canvasPtr
->flags
& CURSOR_ON
)) {
690 EventuallyRedrawArea(canvasPtr
, itemPtr
->x1
, itemPtr
->y1
,
691 itemPtr
->x2
, itemPtr
->y2
);
694 } else if ((c
== 'd') && (strncmp(argv
[1], "dchars", length
) == 0)
698 if ((argc
!= 4) && (argc
!= 5)) {
699 Tcl_AppendResult(interp
, "wrong # args: should be \"",
700 argv
[0], " dchars tagOrId first ?last?\"",
704 for (itemPtr
= StartTagSearch(canvasPtr
, argv
[2], &search
);
705 itemPtr
!= NULL
; itemPtr
= NextItem(&search
)) {
706 if ((itemPtr
->typePtr
->indexProc
== NULL
)
707 || (itemPtr
->typePtr
->dCharsProc
== NULL
)) {
710 if ((*itemPtr
->typePtr
->indexProc
)(canvasPtr
, itemPtr
,
711 argv
[3], &first
) != TCL_OK
) {
715 if ((*itemPtr
->typePtr
->indexProc
)(canvasPtr
, itemPtr
,
716 argv
[4], &last
) != TCL_OK
) {
724 * Redraw both item's old and new areas: it's possible
725 * that a delete could result in a new area larger than
729 EventuallyRedrawArea(canvasPtr
, itemPtr
->x1
, itemPtr
->y1
,
730 itemPtr
->x2
, itemPtr
->y2
);
731 result
= (*itemPtr
->typePtr
->dCharsProc
)(canvasPtr
, itemPtr
,
733 EventuallyRedrawArea(canvasPtr
, itemPtr
->x1
, itemPtr
->y1
,
734 itemPtr
->x2
, itemPtr
->y2
);
735 if (result
!= TCL_OK
) {
739 } else if ((c
== 'd') && (strncmp(argv
[1], "delete", length
) == 0)
742 Tcl_AppendResult(interp
, "wrong # args: should be \"",
743 argv
[0], " delete tagOrId\"",
747 for (itemPtr
= StartTagSearch(canvasPtr
, argv
[2], &search
);
748 itemPtr
!= NULL
; itemPtr
= NextItem(&search
)) {
749 EventuallyRedrawArea(canvasPtr
, itemPtr
->x1
, itemPtr
->y1
,
750 itemPtr
->x2
, itemPtr
->y2
);
751 (*itemPtr
->typePtr
->deleteProc
)(itemPtr
);
752 if (itemPtr
->tagPtr
!= itemPtr
->staticTagSpace
) {
753 ckfree((char *) itemPtr
->tagPtr
);
755 if (search
.prevPtr
== NULL
) {
756 canvasPtr
->firstItemPtr
= itemPtr
->nextPtr
;
757 if (canvasPtr
->firstItemPtr
== NULL
) {
758 canvasPtr
->lastItemPtr
= NULL
;
761 search
.prevPtr
->nextPtr
= itemPtr
->nextPtr
;
763 if (canvasPtr
->lastItemPtr
== itemPtr
) {
764 canvasPtr
->lastItemPtr
= search
.prevPtr
;
766 ckfree((char *) itemPtr
);
767 if (itemPtr
== canvasPtr
->currentItemPtr
) {
768 canvasPtr
->currentItemPtr
= NULL
;
769 canvasPtr
->flags
|= REPICK_NEEDED
;
771 if (itemPtr
== canvasPtr
->focusItemPtr
) {
772 canvasPtr
->focusItemPtr
= NULL
;
774 if (itemPtr
== canvasPtr
->selItemPtr
) {
775 canvasPtr
->selItemPtr
= NULL
;
777 if ((itemPtr
== canvasPtr
->hotPtr
)
778 || (itemPtr
= canvasPtr
->hotPrevPtr
)) {
779 canvasPtr
->hotPtr
= NULL
;
782 } else if ((c
== 'd') && (strncmp(argv
[1], "dtag", length
) == 0)
787 if ((argc
!= 3) && (argc
!= 4)) {
788 Tcl_AppendResult(interp
, "wrong # args: should be \"",
789 argv
[0], " dtag tagOrId ?tagToDelete?\"",
794 tag
= Tk_GetUid(argv
[3]);
796 tag
= Tk_GetUid(argv
[2]);
798 for (itemPtr
= StartTagSearch(canvasPtr
, argv
[2], &search
);
799 itemPtr
!= NULL
; itemPtr
= NextItem(&search
)) {
800 for (i
= itemPtr
->numTags
-1; i
>= 0; i
--) {
801 if (itemPtr
->tagPtr
[i
] == tag
) {
802 itemPtr
->tagPtr
[i
] = itemPtr
->tagPtr
[itemPtr
->numTags
-1];
807 } else if ((c
== 'f') && (strncmp(argv
[1], "find", length
) == 0)
810 Tcl_AppendResult(interp
, "wrong # args: should be \"",
811 argv
[0], " find searchCommand ?arg arg ...?\"",
815 result
= FindItems(interp
, canvasPtr
, argc
-2, argv
+2, (char *) NULL
,
817 } else if ((c
== 'f') && (strncmp(argv
[1], "focus", length
) == 0)
820 Tcl_AppendResult(interp
, "wrong # args: should be \"",
821 argv
[0], " focus ?tagOrId?\"",
825 itemPtr
= canvasPtr
->focusItemPtr
;
827 if (itemPtr
!= NULL
) {
828 sprintf(interp
->result
, "%d", itemPtr
->id
);
832 if ((itemPtr
!= NULL
) && (canvasPtr
->flags
& GOT_FOCUS
)) {
833 EventuallyRedrawArea(canvasPtr
, itemPtr
->x1
, itemPtr
->y1
,
834 itemPtr
->x2
, itemPtr
->y2
);
836 if (argv
[2][0] == 0) {
837 canvasPtr
->focusItemPtr
= NULL
;
840 for (itemPtr
= StartTagSearch(canvasPtr
, argv
[2], &search
);
841 itemPtr
!= NULL
; itemPtr
= NextItem(&search
)) {
842 if (itemPtr
->typePtr
->cursorProc
!= NULL
) {
846 if (itemPtr
== NULL
) {
849 canvasPtr
->focusItemPtr
= itemPtr
;
850 if (canvasPtr
->flags
& GOT_FOCUS
) {
851 EventuallyRedrawArea(canvasPtr
, itemPtr
->x1
, itemPtr
->y1
,
852 itemPtr
->x2
, itemPtr
->y2
);
854 } else if ((c
== 'g') && (strncmp(argv
[1], "gettags", length
) == 0)) {
856 Tcl_AppendResult(interp
, "wrong # args: should be \"",
857 argv
[0], " gettags tagOrId\"", (char *) NULL
);
860 itemPtr
= StartTagSearch(canvasPtr
, argv
[2], &search
);
861 if (itemPtr
!= NULL
) {
863 for (i
= 0; i
< itemPtr
->numTags
; i
++) {
864 Tcl_AppendElement(interp
, (char *) itemPtr
->tagPtr
[i
], 0);
867 } else if ((c
== 'i') && (strncmp(argv
[1], "index", length
) == 0)
872 Tcl_AppendResult(interp
, "wrong # args: should be \"",
873 argv
[0], " index tagOrId string\"",
877 for (itemPtr
= StartTagSearch(canvasPtr
, argv
[2], &search
);
878 itemPtr
!= NULL
; itemPtr
= NextItem(&search
)) {
879 if (itemPtr
->typePtr
->indexProc
!= NULL
) {
883 if (itemPtr
== NULL
) {
884 Tcl_AppendResult(interp
, "can't find an indexable item \"",
885 argv
[2], "\"", (char *) NULL
);
888 if ((*itemPtr
->typePtr
->indexProc
)(canvasPtr
, itemPtr
,
889 argv
[3], &index
) != TCL_OK
) {
892 sprintf(interp
->result
, "%d", index
);
893 } else if ((c
== 'i') && (strncmp(argv
[1], "insert", length
) == 0)
898 Tcl_AppendResult(interp
, "wrong # args: should be \"",
899 argv
[0], " insert tagOrId beforeThis string\"",
903 for (itemPtr
= StartTagSearch(canvasPtr
, argv
[2], &search
);
904 itemPtr
!= NULL
; itemPtr
= NextItem(&search
)) {
905 if ((itemPtr
->typePtr
->indexProc
== NULL
)
906 || (itemPtr
->typePtr
->insertProc
== NULL
)) {
909 if ((*itemPtr
->typePtr
->indexProc
)(canvasPtr
, itemPtr
,
910 argv
[3], &beforeThis
) != TCL_OK
) {
915 * Redraw both item's old and new areas: it's possible
916 * that an insertion could result in a new area either
917 * larger or smaller than the old area.
920 EventuallyRedrawArea(canvasPtr
, itemPtr
->x1
, itemPtr
->y1
,
921 itemPtr
->x2
, itemPtr
->y2
);
922 result
= (*itemPtr
->typePtr
->insertProc
)(canvasPtr
, itemPtr
,
923 beforeThis
, argv
[4]);
924 EventuallyRedrawArea(canvasPtr
, itemPtr
->x1
, itemPtr
->y1
,
925 itemPtr
->x2
, itemPtr
->y2
);
926 if (result
!= TCL_OK
) {
930 } else if ((c
== 'i') && (strncmp(argv
[1], "itemconfigure", length
) == 0)
933 Tcl_AppendResult(interp
, "wrong # args: should be \"",
934 argv
[0], " itemconfigure tagOrId ?option value ...?\"",
938 for (itemPtr
= StartTagSearch(canvasPtr
, argv
[2], &search
);
939 itemPtr
!= NULL
; itemPtr
= NextItem(&search
)) {
941 result
= Tk_ConfigureInfo(canvasPtr
->interp
, canvasPtr
->tkwin
,
942 itemPtr
->typePtr
->configSpecs
, (char *) itemPtr
,
944 } else if (argc
== 4) {
945 result
= Tk_ConfigureInfo(canvasPtr
->interp
, canvasPtr
->tkwin
,
946 itemPtr
->typePtr
->configSpecs
, (char *) itemPtr
,
949 EventuallyRedrawArea(canvasPtr
, itemPtr
->x1
, itemPtr
->y1
,
950 itemPtr
->x2
, itemPtr
->y2
);
951 result
= (*itemPtr
->typePtr
->configProc
)(canvasPtr
, itemPtr
,
952 argc
-3, argv
+3, TK_CONFIG_ARGV_ONLY
);
953 EventuallyRedrawArea(canvasPtr
, itemPtr
->x1
, itemPtr
->y1
,
954 itemPtr
->x2
, itemPtr
->y2
);
955 canvasPtr
->flags
|= REPICK_NEEDED
;
957 if ((result
!= TCL_OK
) || (argc
< 5)) {
961 } else if ((c
== 'l') && (strncmp(argv
[1], "lower", length
) == 0)) {
964 if ((argc
!= 3) && (argc
!= 4)) {
965 Tcl_AppendResult(interp
, "wrong # args: should be \"",
966 argv
[0], " lower tagOrId ?belowThis?\"",
972 * First find the item just after which we'll insert the
979 prevPtr
= StartTagSearch(canvasPtr
, argv
[3], &search
);
980 if (prevPtr
!= NULL
) {
981 prevPtr
= search
.prevPtr
;
983 Tcl_AppendResult(interp
, "tag \"", argv
[3],
984 "\" doesn't match any items", (char *) NULL
);
988 RelinkItems(canvasPtr
, argv
[2], prevPtr
);
989 } else if ((c
== 'm') && (strncmp(argv
[1], "move", length
) == 0)) {
990 double xAmount
, yAmount
;
993 Tcl_AppendResult(interp
, "wrong # args: should be \"",
994 argv
[0], " move tagOrId xAmount yAmount\"",
998 if ((TkGetCanvasCoord(canvasPtr
, argv
[3], &xAmount
) != TCL_OK
)
999 || (TkGetCanvasCoord(canvasPtr
, argv
[4], &yAmount
) != TCL_OK
)) {
1002 for (itemPtr
= StartTagSearch(canvasPtr
, argv
[2], &search
);
1003 itemPtr
!= NULL
; itemPtr
= NextItem(&search
)) {
1004 EventuallyRedrawArea(canvasPtr
, itemPtr
->x1
, itemPtr
->y1
,
1005 itemPtr
->x2
, itemPtr
->y2
);
1006 (void) (*itemPtr
->typePtr
->translateProc
)(canvasPtr
, itemPtr
,
1008 EventuallyRedrawArea(canvasPtr
, itemPtr
->x1
, itemPtr
->y1
,
1009 itemPtr
->x2
, itemPtr
->y2
);
1010 canvasPtr
->flags
|= REPICK_NEEDED
;
1012 } else if ((c
== 'r') && (strncmp(argv
[1], "raise", length
) == 0)) {
1015 if ((argc
!= 3) && (argc
!= 4)) {
1016 Tcl_AppendResult(interp
, "wrong # args: should be \"",
1017 argv
[0], " raise tagOrId ?aboveThis?\"",
1023 * First find the item just after which we'll insert the
1028 prevPtr
= canvasPtr
->lastItemPtr
;
1031 for (itemPtr
= StartTagSearch(canvasPtr
, argv
[3], &search
);
1032 itemPtr
!= NULL
; itemPtr
= NextItem(&search
)) {
1035 if (prevPtr
== NULL
) {
1036 Tcl_AppendResult(interp
, "tagOrId \"", argv
[3],
1037 "\" doesn't match any items", (char *) NULL
);
1041 RelinkItems(canvasPtr
, argv
[2], prevPtr
);
1042 #if defined(USE_XPM3)
1043 } else if ((c
== 's') && (strncmp(argv
[1], "save", length
) == 0)
1045 if (argc
!= 3 && argc
!= 7) {
1046 Tcl_AppendResult(interp
, "wrong # args: should be \"",
1047 argv
[0], " save fileName ?x y width height?\"",
1052 if (SaveCanvas(interp
, canvasPtr
, argv
[2], 0, 0, 0, 0) != TCL_OK
) {
1056 if (SaveCanvas(interp
, canvasPtr
, argv
[2], atol(argv
[3]),
1057 atol(argv
[4]), atol(argv
[5]), atol(argv
[6]))) {
1062 } else if ((c
== 's') && (strncmp(argv
[1], "scale", length
) == 0)
1064 double xOrigin
, yOrigin
, xScale
, yScale
;
1067 Tcl_AppendResult(interp
, "wrong # args: should be \"",
1068 argv
[0], " scale tagOrId xOrigin yOrigin xScale yScale\"",
1072 if ((TkGetCanvasCoord(canvasPtr
, argv
[3], &xOrigin
) != TCL_OK
)
1073 || (TkGetCanvasCoord(canvasPtr
, argv
[4], &yOrigin
) != TCL_OK
)
1074 || (Tcl_GetDouble(interp
, argv
[5], &xScale
) != TCL_OK
)
1075 || (Tcl_GetDouble(interp
, argv
[6], &yScale
) != TCL_OK
)) {
1078 if ((xScale
<= 0.0) || (yScale
<= 0.0)) {
1079 interp
->result
= "scale factors must be greater than zero";
1082 for (itemPtr
= StartTagSearch(canvasPtr
, argv
[2], &search
);
1083 itemPtr
!= NULL
; itemPtr
= NextItem(&search
)) {
1084 EventuallyRedrawArea(canvasPtr
, itemPtr
->x1
, itemPtr
->y1
,
1085 itemPtr
->x2
, itemPtr
->y2
);
1086 (void) (*itemPtr
->typePtr
->scaleProc
)(canvasPtr
, itemPtr
,
1087 xOrigin
, yOrigin
, xScale
, yScale
);
1088 EventuallyRedrawArea(canvasPtr
, itemPtr
->x1
, itemPtr
->y1
,
1089 itemPtr
->x2
, itemPtr
->y2
);
1090 canvasPtr
->flags
|= REPICK_NEEDED
;
1092 } else if ((c
== 's') && (strncmp(argv
[1], "scan", length
) == 0)
1097 Tcl_AppendResult(interp
, "wrong # args: should be \"",
1098 argv
[0], " scan mark|dragto x y\"", (char *) NULL
);
1101 if ((Tcl_GetInt(interp
, argv
[3], &x
) != TCL_OK
)
1102 || (Tcl_GetInt(interp
, argv
[4], &y
) != TCL_OK
)){
1105 if ((argv
[2][0] == 'm')
1106 && (strncmp(argv
[2], "mark", strlen(argv
[2])) == 0)) {
1107 canvasPtr
->scanX
= x
;
1108 canvasPtr
->scanXOrigin
= canvasPtr
->xOrigin
;
1109 canvasPtr
->scanY
= y
;
1110 canvasPtr
->scanYOrigin
= canvasPtr
->yOrigin
;
1111 } else if ((argv
[2][0] == 'd')
1112 && (strncmp(argv
[2], "dragto", strlen(argv
[2])) == 0)) {
1113 int newXOrigin
, newYOrigin
, tmp
;
1116 * Compute a new view origin for the canvas, amplifying the
1117 * mouse motion and rounding to the nearest multiple of the
1121 tmp
= canvasPtr
->scanXOrigin
- 10*(x
- canvasPtr
->scanX
)
1122 - canvasPtr
->scrollX1
;
1124 tmp
= (tmp
+ canvasPtr
->scrollIncrement
/2)
1125 /canvasPtr
->scrollIncrement
;
1127 tmp
= -(((-tmp
) + canvasPtr
->scrollIncrement
/2)
1128 /canvasPtr
->scrollIncrement
);
1130 newXOrigin
= canvasPtr
->scrollX1
+ tmp
*canvasPtr
->scrollIncrement
;
1131 tmp
= canvasPtr
->scanYOrigin
- 10*(y
- canvasPtr
->scanY
)
1132 - canvasPtr
->scrollY1
;
1134 tmp
= (tmp
+ canvasPtr
->scrollIncrement
/2)
1135 /canvasPtr
->scrollIncrement
;
1137 tmp
= -(((-tmp
) + canvasPtr
->scrollIncrement
/2)
1138 /canvasPtr
->scrollIncrement
);
1140 newYOrigin
= canvasPtr
->scrollY1
+ tmp
*canvasPtr
->scrollIncrement
;
1141 CanvasSetOrigin(canvasPtr
, newXOrigin
, newYOrigin
);
1143 Tcl_AppendResult(interp
, "bad scan option \"", argv
[2],
1144 "\": must be mark or dragto", (char *) NULL
);
1147 } else if ((c
== 's') && (strncmp(argv
[1], "select", length
) == 0)
1152 Tcl_AppendResult(interp
, "wrong # args: should be \"",
1153 argv
[0], " select option ?tagOrId? ?arg?\"", (char *) NULL
);
1157 for (itemPtr
= StartTagSearch(canvasPtr
, argv
[3], &search
);
1158 itemPtr
!= NULL
; itemPtr
= NextItem(&search
)) {
1159 if ((itemPtr
->typePtr
->indexProc
!= NULL
)
1160 && (itemPtr
->typePtr
->selectionProc
!= NULL
)){
1164 if (itemPtr
== NULL
) {
1165 Tcl_AppendResult(interp
,
1166 "can't find an indexable and selectable item \"",
1167 argv
[3], "\"", (char *) NULL
);
1172 if ((*itemPtr
->typePtr
->indexProc
)(canvasPtr
, itemPtr
,
1173 argv
[4], &index
) != TCL_OK
) {
1177 length
= strlen(argv
[2]);
1179 if ((c
== 'a') && (strncmp(argv
[2], "adjust", length
) == 0)) {
1181 Tcl_AppendResult(interp
, "wrong # args: should be \"",
1182 argv
[0], " select adjust tagOrId index\"",
1186 if (canvasPtr
->selItemPtr
== itemPtr
) {
1187 if (index
< (canvasPtr
->selectFirst
1188 + canvasPtr
->selectLast
)/2) {
1189 canvasPtr
->selectAnchor
= canvasPtr
->selectLast
+ 1;
1191 canvasPtr
->selectAnchor
= canvasPtr
->selectFirst
;
1194 CanvasSelectTo(canvasPtr
, itemPtr
, index
);
1195 } else if ((c
== 'c') && (argv
[2] != NULL
)
1196 && (strncmp(argv
[2], "clear", length
) == 0)) {
1198 Tcl_AppendResult(interp
, "wrong # args: should be \"",
1199 argv
[0], " select clear\"", (char *) NULL
);
1202 if (canvasPtr
->selItemPtr
!= NULL
) {
1203 EventuallyRedrawArea(canvasPtr
, canvasPtr
->selItemPtr
->x1
,
1204 canvasPtr
->selItemPtr
->y1
, canvasPtr
->selItemPtr
->x2
,
1205 canvasPtr
->selItemPtr
->y2
);
1206 canvasPtr
->selItemPtr
= NULL
;
1209 } else if ((c
== 'f') && (strncmp(argv
[2], "from", length
) == 0)) {
1211 Tcl_AppendResult(interp
, "wrong # args: should be \"",
1212 argv
[0], " select from tagOrId index\"",
1216 canvasPtr
->anchorItemPtr
= itemPtr
;
1217 canvasPtr
->selectAnchor
= index
;
1218 } else if ((c
== 'i') && (strncmp(argv
[2], "item", length
) == 0)) {
1220 Tcl_AppendResult(interp
, "wrong # args: should be \"",
1221 argv
[0], " select item\"", (char *) NULL
);
1224 if (canvasPtr
->selItemPtr
!= NULL
) {
1225 sprintf(interp
->result
, "%d", canvasPtr
->selItemPtr
->id
);
1227 } else if ((c
== 't') && (strncmp(argv
[2], "to", length
) == 0)) {
1229 Tcl_AppendResult(interp
, "wrong # args: should be \"",
1230 argv
[0], " select to tagOrId index\"",
1234 CanvasSelectTo(canvasPtr
, itemPtr
, index
);
1236 Tcl_AppendResult(interp
, "bad select option \"", argv
[2],
1237 "\": must be adjust, clear, from, item, or to",
1241 } else if ((c
== 't') && (strncmp(argv
[1], "type", length
) == 0)) {
1243 Tcl_AppendResult(interp
, "wrong # args: should be \"",
1244 argv
[0], " type tag\"", (char *) NULL
);
1247 itemPtr
= StartTagSearch(canvasPtr
, argv
[2], &search
);
1248 if (itemPtr
!= NULL
) {
1249 interp
->result
= itemPtr
->typePtr
->name
;
1251 } else if ((c
== 'x') && (strncmp(argv
[1], "xview", length
) == 0)) {
1255 Tcl_AppendResult(interp
, "wrong # args: should be \"",
1256 argv
[0], " xview index\"", (char *) NULL
);
1259 if (Tcl_GetInt(canvasPtr
->interp
, argv
[2], &index
) != TCL_OK
) {
1262 CanvasSetOrigin(canvasPtr
,
1263 (canvasPtr
->scrollX1
+ index
*canvasPtr
->scrollIncrement
),
1264 canvasPtr
->yOrigin
);
1265 } else if ((c
== 'y') && (strncmp(argv
[1], "yview", length
) == 0)) {
1269 Tcl_AppendResult(interp
, "wrong # args: should be \"",
1270 argv
[0], " yview index\"", (char *) NULL
);
1273 if (Tcl_GetInt(canvasPtr
->interp
, argv
[2], &index
) != TCL_OK
) {
1276 CanvasSetOrigin(canvasPtr
, canvasPtr
->xOrigin
,
1277 (canvasPtr
->scrollY1
+ index
*canvasPtr
->scrollIncrement
));
1279 Tcl_AppendResult(interp
, "bad option \"", argv
[1],
1280 "\": must be addtag, bbox, bind, ",
1281 "canvasx, canvasy, configure, coords, create, ",
1282 "cursor, dchars, delete, dtag, find, focus, ",
1283 "gettags, index, insert, itemconfigure, lower, ",
1284 "move, raise, scale, scan, select, type, xview, or yview",
1289 Tk_Release((ClientData
) canvasPtr
);
1293 Tk_Release((ClientData
) canvasPtr
);
1298 *----------------------------------------------------------------------
1302 * This procedure is invoked by Tk_EventuallyFree or Tk_Release
1303 * to clean up the internal structure of a canvas at a safe time
1304 * (when no-one is using it anymore).
1310 * Everything associated with the canvas is freed up.
1312 *----------------------------------------------------------------------
1317 ClientData clientData
/* Info about canvas widget. */
1320 register Tk_Canvas
*canvasPtr
= (Tk_Canvas
*) clientData
;
1321 register Tk_Item
*itemPtr
;
1323 for (itemPtr
= canvasPtr
->firstItemPtr
; itemPtr
!= NULL
;
1324 itemPtr
= canvasPtr
->firstItemPtr
) {
1325 canvasPtr
->firstItemPtr
= itemPtr
->nextPtr
;
1326 (*itemPtr
->typePtr
->deleteProc
)(itemPtr
);
1327 if (itemPtr
->tagPtr
!= itemPtr
->staticTagSpace
) {
1328 ckfree((char *) itemPtr
->tagPtr
);
1330 ckfree((char *) itemPtr
);
1333 if (canvasPtr
->bgBorder
!= NULL
) {
1334 Tk_Free3DBorder(canvasPtr
->bgBorder
);
1336 if (canvasPtr
->bgColor
!= NULL
) {
1337 Tk_FreeColor(canvasPtr
->bgColor
);
1339 if (canvasPtr
->pixmapGC
!= None
) {
1340 Tk_FreeGC(canvasPtr
->pixmapGC
);
1342 if (canvasPtr
->selBorder
!= NULL
) {
1343 Tk_Free3DBorder(canvasPtr
->selBorder
);
1345 if (canvasPtr
->selFgColorPtr
!= NULL
) {
1346 Tk_FreeColor(canvasPtr
->selFgColorPtr
);
1348 if (canvasPtr
->cursorBorder
!= NULL
) {
1349 Tk_Free3DBorder(canvasPtr
->cursorBorder
);
1351 Tk_DeleteTimerHandler(canvasPtr
->cursorBlinkHandler
);
1352 if (canvasPtr
->bindingTable
!= NULL
) {
1353 Tk_DeleteBindingTable(canvasPtr
->bindingTable
);
1355 if (canvasPtr
->xScrollCmd
!= NULL
) {
1356 ckfree(canvasPtr
->xScrollCmd
);
1358 if (canvasPtr
->yScrollCmd
!= NULL
) {
1359 ckfree(canvasPtr
->yScrollCmd
);
1361 if (canvasPtr
->regionString
!= NULL
) {
1362 ckfree(canvasPtr
->regionString
);
1364 if (canvasPtr
->cursor
!= None
) {
1365 Tk_FreeCursor(canvasPtr
->cursor
);
1367 ckfree((char *) canvasPtr
);
1371 *----------------------------------------------------------------------
1373 * ConfigureCanvas --
1375 * This procedure is called to process an argv/argc list, plus
1376 * the Tk option database, in order to configure (or
1377 * reconfigure) a canvas widget.
1380 * The return value is a standard Tcl result. If TCL_ERROR is
1381 * returned, then interp->result contains an error message.
1384 * Configuration information, such as colors, border width,
1385 * etc. get set for canvasPtr; old resources get freed,
1386 * if there were any.
1388 *----------------------------------------------------------------------
1393 Tcl_Interp
*interp
, /* Used for error reporting. */
1394 register Tk_Canvas
*canvasPtr
, /* Information about widget; may or may
1395 * not already have values for some fields. */
1396 int argc
, /* Number of valid entries in argv. */
1397 char **argv
, /* Arguments. */
1398 int flags
/* Flags to pass to Tk_ConfigureWidget. */
1404 if (Tk_ConfigureWidget(interp
, canvasPtr
->tkwin
, configSpecs
,
1405 argc
, argv
, (char *) canvasPtr
, flags
) != TCL_OK
) {
1410 * A few options need special processing, such as setting the
1411 * background from a 3-D border and creating a GC for copying
1412 * bits to the screen.
1415 Tk_SetBackgroundFromBorder(canvasPtr
->tkwin
, canvasPtr
->bgBorder
);
1417 gcValues
.function
= GXcopy
;
1418 gcValues
.foreground
= canvasPtr
->bgColor
->pixel
;
1419 gcValues
.graphics_exposures
= False
;
1420 new = Tk_GetGC(canvasPtr
->tkwin
,
1421 GCFunction
|GCForeground
|GCGraphicsExposures
, &gcValues
);
1422 if (canvasPtr
->pixmapGC
!= None
) {
1423 Tk_FreeGC(canvasPtr
->pixmapGC
);
1425 canvasPtr
->pixmapGC
= new;
1428 * Reset the desired dimensions for the window.
1431 Tk_GeometryRequest(canvasPtr
->tkwin
, canvasPtr
->width
, canvasPtr
->height
);
1434 * Restart the cursor timing sequence in case the on-time or off-time
1438 if (canvasPtr
->flags
& GOT_FOCUS
) {
1439 CanvasFocusProc((ClientData
) canvasPtr
, 1);
1443 * Recompute the scroll region.
1446 canvasPtr
->scrollX1
= 0;
1447 canvasPtr
->scrollY1
= 0;
1448 canvasPtr
->scrollX2
= 0;
1449 canvasPtr
->scrollY2
= 0;
1450 if (canvasPtr
->regionString
!= NULL
) {
1454 if (Tcl_SplitList(canvasPtr
->interp
, canvasPtr
->regionString
,
1455 &argc2
, &argv2
) != TCL_OK
) {
1460 Tcl_AppendResult(interp
, "bad scrollRegion \"",
1461 canvasPtr
->regionString
, "\"", (char *) NULL
);
1462 ckfree(canvasPtr
->regionString
);
1463 ckfree((char *) argv2
);
1464 canvasPtr
->regionString
= NULL
;
1467 if ((Tk_GetPixels(canvasPtr
->interp
, canvasPtr
->tkwin
,
1468 argv2
[0], &canvasPtr
->scrollX1
) != TCL_OK
)
1469 || (Tk_GetPixels(canvasPtr
->interp
, canvasPtr
->tkwin
,
1470 argv2
[1], &canvasPtr
->scrollY1
) != TCL_OK
)
1471 || (Tk_GetPixels(canvasPtr
->interp
, canvasPtr
->tkwin
,
1472 argv2
[2], &canvasPtr
->scrollX2
) != TCL_OK
)
1473 || (Tk_GetPixels(canvasPtr
->interp
, canvasPtr
->tkwin
,
1474 argv2
[3], &canvasPtr
->scrollY2
) != TCL_OK
)) {
1477 ckfree((char *) argv2
);
1481 * Reset the canvases origin (this is a no-op unless confine
1482 * mode has just been turned on or the scroll region has changed).
1485 CanvasSetOrigin(canvasPtr
, canvasPtr
->xOrigin
, canvasPtr
->yOrigin
);
1486 canvasPtr
->flags
|= UPDATE_SCROLLBARS
;
1487 EventuallyRedrawArea(canvasPtr
, canvasPtr
->xOrigin
, canvasPtr
->yOrigin
,
1488 canvasPtr
->xOrigin
+ Tk_Width(canvasPtr
->tkwin
),
1489 canvasPtr
->yOrigin
+ Tk_Height(canvasPtr
->tkwin
));
1493 #if defined(USE_XPM3)
1494 //#include "xpmtk.h"
1495 #include <X11/xpm.h>
1497 *--------------------------------------------------------------
1501 * This procedure saves the contents of a canvas window.
1504 * The return value is a standard Tcl result. If TCL_ERROR is
1505 * returned, then interp->result contains an error message.
1508 * A pixmap is written to a file.
1510 *--------------------------------------------------------------
1515 Tcl_Interp
*interp
, /* Used for error reporting. */
1516 register Tk_Canvas
*canvasPtr
, /* Information about widget */
1517 char *fileName
, /* the output file name. */
1518 int x
, /* upper left x coordinate. */
1519 int y
, /* upper left y coordinate. */
1520 unsigned int width
, /* width of pixmap area to save. */
1521 unsigned int height
/* height of pixmap area to save. */
1524 register Tk_Window tkwin
= canvasPtr
->tkwin
;
1525 register Tk_Item
*itemPtr
;
1528 int screenX1
, screenX2
, screenY1
, screenY2
;
1529 XpmAttributes xpm_attributes
;
1531 if (canvasPtr
->tkwin
== NULL
) {
1534 if (!Tk_IsMapped(tkwin
)) {
1537 if (!(fileName
&& *fileName
)) {
1538 Tcl_ResetResult(interp
);
1539 Tcl_AppendResult(interp
, "no filename specified for canvas saving",
1545 * Choose a new current item if that is needed (this could cause
1546 * event handlers to be invoked).
1549 while (canvasPtr
->flags
& REPICK_NEEDED
) {
1550 Tk_Preserve((ClientData
) canvasPtr
);
1551 canvasPtr
->flags
&= ~REPICK_NEEDED
;
1552 PickCurrentItem(canvasPtr
, &canvasPtr
->pickEvent
);
1553 tkwin
= canvasPtr
->tkwin
;
1554 Tk_Release((ClientData
) canvasPtr
);
1555 if (tkwin
== NULL
) {
1560 if(x
== 0 && y
== 0 && width
== 0 && height
== 0) {
1563 screenX2
= Tk_Width(tkwin
);
1564 screenY2
= Tk_Height(tkwin
);
1565 width
= Tk_Width(tkwin
);
1566 height
= Tk_Height(tkwin
);
1568 if(width
!= 0 && height
!= 0) {
1571 screenX2
= x
+ width
;
1572 screenY2
= y
+ height
;
1574 Tcl_ResetResult(interp
);
1575 Tcl_AppendResult(interp
, "no correct size specified for canvas saving",
1582 * Saving is done in a temporary pixmap that is allocated
1583 * here and freed at the end of the procedure. All drawing
1584 * is done to the pixmap, and the pixmap is saved to the
1585 * file at the end of the procedure.
1587 * Some tricky points about the pixmap:
1589 * 1. We only allocate a large enough pixmap to hold the
1590 * area that has to be saved. This saves time in
1591 * in the X server for large objects that cover much
1592 * more than the area being saved: only the area
1593 * of the pixmap will actually have to be saved.
1594 * 2. The origin of the pixmap is adjusted to an even multiple
1595 * of 32 bits. This is so that stipple patterns with a size
1596 * of 8 or 16 or 32 bits will always line up when information
1597 * is copied back to the screen.
1598 * 3. Some X servers (e.g. the one for DECstations) have troubles
1599 * with characters that overlap an edge of the pixmap (on the
1600 * DEC servers, as of 8/18/92, such characters are drawn one
1601 * pixel too far to the right). To handle this problem,
1602 * make the pixmap a bit larger than is absolutely needed
1603 * so that for normal-sized fonts the characters that ovelap
1604 * the edge of the pixmap will be outside the area we care
1608 canvasPtr
->drawableXOrigin
= (screenX1
- 30) & ~0x1f;
1609 canvasPtr
->drawableYOrigin
= (screenY1
- 30) & ~0x1f;
1610 pixmap
= XCreatePixmap(Tk_Display(tkwin
), Tk_WindowId(tkwin
),
1611 screenX2
+ 30 - canvasPtr
->drawableXOrigin
,
1612 screenY2
+ 30 - canvasPtr
->drawableYOrigin
,
1613 Tk_DefaultDepth(Tk_Screen(tkwin
)));
1614 savePixmap
= XCreatePixmap(Tk_Display(tkwin
), Tk_WindowId(tkwin
),
1615 width
, height
, Tk_DefaultDepth(Tk_Screen(tkwin
)));
1618 * Clear the area to be redrawn.
1621 XFillRectangle(Tk_Display(tkwin
), pixmap
, canvasPtr
->pixmapGC
,
1622 screenX1
- canvasPtr
->drawableXOrigin
,
1623 screenY1
- canvasPtr
->drawableYOrigin
,
1624 (unsigned int) (screenX2
- screenX1
),
1625 (unsigned int) (screenY2
- screenY1
));
1626 XFillRectangle(Tk_Display(tkwin
), savePixmap
, canvasPtr
->pixmapGC
,
1627 0, 0, width
, height
);
1630 * Scan through the item list, redrawing those items that need it.
1631 * An item must be redraw if either (a) it intersects the smaller
1632 * on-screen area or (b) it intersects the full canvas area and its
1633 * type requests that it be redrawn always (e.g. so subwindows can
1634 * be unmapped when they move off-screen).
1637 for (itemPtr
= canvasPtr
->firstItemPtr
; itemPtr
!= NULL
;
1638 itemPtr
= itemPtr
->nextPtr
) {
1639 if ((itemPtr
->x1
>= screenX2
)
1640 || (itemPtr
->y1
>= screenY2
)
1641 || (itemPtr
->x2
< screenX1
)
1642 || (itemPtr
->y2
< screenY1
)) {
1643 if (!itemPtr
->typePtr
->alwaysRedraw
1644 || (itemPtr
->x1
>= canvasPtr
->redrawX2
)
1645 || (itemPtr
->y1
>= canvasPtr
->redrawY2
)
1646 || (itemPtr
->x2
< canvasPtr
->redrawX1
)
1647 || (itemPtr
->y2
< canvasPtr
->redrawY1
)) {
1651 (*itemPtr
->typePtr
->displayProc
)(canvasPtr
, itemPtr
, pixmap
);
1655 * Copy from the temporary pixmap to the save pixmap.
1658 XCopyArea(Tk_Display(tkwin
), pixmap
, savePixmap
,
1659 canvasPtr
->pixmapGC
,
1660 screenX1
- canvasPtr
->drawableXOrigin
,
1661 screenY1
- canvasPtr
->drawableYOrigin
,
1662 screenX2
- screenX1
, screenY2
- screenY1
, 0, 0);
1665 * Save temporary pixmap.
1668 xpm_attributes
.width
= width
;
1669 xpm_attributes
.height
= height
;
1670 xpm_attributes
.visual
= Tk_DefaultVisual(Tk_Screen(tkwin
));
1671 xpm_attributes
.colormap
= Tk_DefaultColormap(Tk_Screen(tkwin
));
1672 xpm_attributes
.valuemask
= XpmSize
| XpmVisual
| XpmColormap
;
1673 if(XpmWriteFileFromPixmap(Tk_Display(tkwin
), fileName
,
1674 savePixmap
, (Pixmap
) NULL
,
1675 &xpm_attributes
) != XpmSuccess
) {
1676 XFreePixmap(Tk_Display(tkwin
), pixmap
);
1677 XFreePixmap(Tk_Display(tkwin
), savePixmap
);
1678 Tcl_ResetResult(interp
);
1679 Tcl_AppendResult(interp
, "could not save pixmap for canvas",
1683 XFreePixmap(Tk_Display(tkwin
), pixmap
);
1684 XFreePixmap(Tk_Display(tkwin
), savePixmap
);
1691 *--------------------------------------------------------------
1695 * This procedure redraws the contents of a canvas window.
1696 * It is invoked as a do-when-idle handler, so it only runs
1697 * when there's nothing else for the application to do.
1703 * Information appears on the screen.
1705 *--------------------------------------------------------------
1710 ClientData clientData
/* Information about widget. */
1713 register Tk_Canvas
*canvasPtr
= (Tk_Canvas
*) clientData
;
1714 register Tk_Window tkwin
= canvasPtr
->tkwin
;
1715 register Tk_Item
*itemPtr
;
1717 int screenX1
, screenX2
, screenY1
, screenY2
;
1719 if (canvasPtr
->tkwin
== NULL
) {
1722 if (!Tk_IsMapped(tkwin
)) {
1727 * Choose a new current item if that is needed (this could cause
1728 * event handlers to be invoked).
1731 while (canvasPtr
->flags
& REPICK_NEEDED
) {
1732 Tk_Preserve((ClientData
) canvasPtr
);
1733 canvasPtr
->flags
&= ~REPICK_NEEDED
;
1734 PickCurrentItem(canvasPtr
, &canvasPtr
->pickEvent
);
1735 tkwin
= canvasPtr
->tkwin
;
1736 Tk_Release((ClientData
) canvasPtr
);
1737 if (tkwin
== NULL
) {
1743 * Compute the intersection between the area that needs redrawing
1744 * and the area that's visible on the screen.
1747 screenX1
= canvasPtr
->xOrigin
;
1748 screenY1
= canvasPtr
->yOrigin
;
1749 screenX2
= screenX1
+ Tk_Width(tkwin
);
1750 screenY2
= screenY1
+ Tk_Height(tkwin
);
1751 if (canvasPtr
->redrawX1
> screenX1
) {
1752 screenX1
= canvasPtr
->redrawX1
;
1754 if (canvasPtr
->redrawY1
> screenY1
) {
1755 screenY1
= canvasPtr
->redrawY1
;
1757 if (canvasPtr
->redrawX2
< screenX2
) {
1758 screenX2
= canvasPtr
->redrawX2
;
1760 if (canvasPtr
->redrawY2
< screenY2
) {
1761 screenY2
= canvasPtr
->redrawY2
;
1763 if ((screenX1
>= screenX2
) || (screenY1
>= screenY2
)) {
1768 * Redrawing is done in a temporary pixmap that is allocated
1769 * here and freed at the end of the procedure. All drawing
1770 * is done to the pixmap, and the pixmap is copied to the
1771 * screen at the end of the procedure. The temporary pixmap
1772 * serves two purposes:
1774 * 1. It provides a smoother visual effect (no clearing and
1775 * gradual redraw will be visible to users).
1776 * 2. It allows us to redraw only the objects that overlap
1777 * the redraw area. Otherwise incorrect results could
1778 * occur from redrawing things that stick outside of
1779 * the redraw area (we'd have to redraw everything in
1780 * order to make the overlaps look right).
1782 * Some tricky points about the pixmap:
1784 * 1. We only allocate a large enough pixmap to hold the
1785 * area that has to be redisplayed. This saves time in
1786 * in the X server for large objects that cover much
1787 * more than the area being redisplayed: only the area
1788 * of the pixmap will actually have to be redrawn.
1789 * 2. The origin of the pixmap is adjusted to an even multiple
1790 * of 32 bits. This is so that stipple patterns with a size
1791 * of 8 or 16 or 32 bits will always line up when information
1792 * is copied back to the screen.
1793 * 3. Some X servers (e.g. the one for DECstations) have troubles
1794 * with characters that overlap an edge of the pixmap (on the
1795 * DEC servers, as of 8/18/92, such characters are drawn one
1796 * pixel too far to the right). To handle this problem,
1797 * make the pixmap a bit larger than is absolutely needed
1798 * so that for normal-sized fonts the characters that ovelap
1799 * the edge of the pixmap will be outside the area we care
1803 canvasPtr
->drawableXOrigin
= (screenX1
- 30) & ~0x1f;
1804 canvasPtr
->drawableYOrigin
= (screenY1
- 30) & ~0x1f;
1805 pixmap
= XCreatePixmap(Tk_Display(tkwin
), Tk_WindowId(tkwin
),
1806 screenX2
+ 30 - canvasPtr
->drawableXOrigin
,
1807 screenY2
+ 30 - canvasPtr
->drawableYOrigin
,
1808 Tk_DefaultDepth(Tk_Screen(tkwin
)));
1811 * Clear the area to be redrawn.
1814 XFillRectangle(Tk_Display(tkwin
), pixmap
, canvasPtr
->pixmapGC
,
1815 screenX1
- canvasPtr
->drawableXOrigin
,
1816 screenY1
- canvasPtr
->drawableYOrigin
,
1817 (unsigned int) (screenX2
- screenX1
),
1818 (unsigned int) (screenY2
- screenY1
));
1821 * Scan through the item list, redrawing those items that need it.
1822 * An item must be redraw if either (a) it intersects the smaller
1823 * on-screen area or (b) it intersects the full canvas area and its
1824 * type requests that it be redrawn always (e.g. so subwindows can
1825 * be unmapped when they move off-screen).
1828 for (itemPtr
= canvasPtr
->firstItemPtr
; itemPtr
!= NULL
;
1829 itemPtr
= itemPtr
->nextPtr
) {
1830 if ((itemPtr
->x1
>= screenX2
)
1831 || (itemPtr
->y1
>= screenY2
)
1832 || (itemPtr
->x2
< screenX1
)
1833 || (itemPtr
->y2
< screenY1
)) {
1834 if (!itemPtr
->typePtr
->alwaysRedraw
1835 || (itemPtr
->x1
>= canvasPtr
->redrawX2
)
1836 || (itemPtr
->y1
>= canvasPtr
->redrawY2
)
1837 || (itemPtr
->x2
< canvasPtr
->redrawX1
)
1838 || (itemPtr
->y2
< canvasPtr
->redrawY1
)) {
1842 (*itemPtr
->typePtr
->displayProc
)(canvasPtr
, itemPtr
, pixmap
);
1846 * Draw the window border.
1849 if (canvasPtr
->relief
!= TK_RELIEF_FLAT
) {
1850 Tk_Draw3DRectangle(Tk_Display(tkwin
), pixmap
,
1851 canvasPtr
->bgBorder
,
1852 canvasPtr
->xOrigin
- canvasPtr
->drawableXOrigin
,
1853 canvasPtr
->yOrigin
- canvasPtr
->drawableYOrigin
,
1854 Tk_Width(tkwin
), Tk_Height(tkwin
),
1855 canvasPtr
->borderWidth
, canvasPtr
->relief
);
1859 * Copy from the temporary pixmap to the screen, then free up
1860 * the temporary pixmap.
1863 XCopyArea(Tk_Display(tkwin
), pixmap
, Tk_WindowId(tkwin
),
1864 canvasPtr
->pixmapGC
,
1865 screenX1
- canvasPtr
->drawableXOrigin
,
1866 screenY1
- canvasPtr
->drawableYOrigin
,
1867 screenX2
- screenX1
, screenY2
- screenY1
,
1868 screenX1
- canvasPtr
->xOrigin
, screenY1
- canvasPtr
->yOrigin
);
1869 XFreePixmap(Tk_Display(tkwin
), pixmap
);
1872 canvasPtr
->flags
&= ~REDRAW_PENDING
;
1873 assert(canvasPtr
->updateTimerToken
!= NULL
);
1874 canvasPtr
->updateTimerToken
= NULL
;
1875 if (canvasPtr
->flags
& UPDATE_SCROLLBARS
) {
1876 CanvasUpdateScrollbars(canvasPtr
);
1881 *--------------------------------------------------------------
1883 * CanvasEventProc --
1885 * This procedure is invoked by the Tk dispatcher for various
1886 * events on canvases.
1892 * When the window gets deleted, internal structures get
1893 * cleaned up. When it gets exposed, it is redisplayed.
1895 *--------------------------------------------------------------
1900 ClientData clientData
, /* Information about window. */
1901 XEvent
*eventPtr
/* Information about event. */
1904 Tk_Canvas
*canvasPtr
= (Tk_Canvas
*) clientData
;
1906 if (eventPtr
->type
== Expose
) {
1909 x
= eventPtr
->xexpose
.x
+ canvasPtr
->xOrigin
;
1910 y
= eventPtr
->xexpose
.y
+ canvasPtr
->yOrigin
;
1911 EventuallyRedrawArea(canvasPtr
, x
, y
, x
+ eventPtr
->xexpose
.width
,
1912 y
+ eventPtr
->xexpose
.height
);
1913 } else if (eventPtr
->type
== DestroyNotify
) {
1914 Tcl_DeleteCommand(canvasPtr
->interp
, Tk_PathName(canvasPtr
->tkwin
));
1915 canvasPtr
->tkwin
= NULL
;
1916 if (canvasPtr
->flags
& REDRAW_PENDING
) {
1917 canvasPtr
->flags
&= ~REDRAW_PENDING
;
1918 // Tk_CancelIdleCall(DisplayCanvas, (ClientData) canvasPtr);
1919 assert(canvasPtr
->updateTimerToken
!= NULL
);
1920 if (canvasPtr
->updateTimerToken
!= NULL
) {
1921 Tk_DeleteTimerHandler(canvasPtr
->updateTimerToken
);
1922 canvasPtr
->updateTimerToken
= 0;
1925 Tk_EventuallyFree((ClientData
) canvasPtr
, DestroyCanvas
);
1926 } else if (eventPtr
->type
== ConfigureNotify
) {
1927 canvasPtr
->flags
|= UPDATE_SCROLLBARS
;
1930 * The call below is needed in order to recenter the canvas if
1931 * it's confined and its scroll region is smaller than the window.
1934 CanvasSetOrigin(canvasPtr
, canvasPtr
->xOrigin
, canvasPtr
->yOrigin
);
1935 EventuallyRedrawArea(canvasPtr
, 0, 0, Tk_Width(canvasPtr
->tkwin
),
1936 Tk_Height(canvasPtr
->tkwin
));
1941 *--------------------------------------------------------------
1943 * EventuallyRedrawArea --
1945 * Arrange for part or all of a canvas widget to redrawn at
1946 * the next convenient time in the future.
1952 * The screen will eventually be refreshed.
1954 *--------------------------------------------------------------
1958 EventuallyRedrawArea (
1959 register Tk_Canvas
*canvasPtr
, /* Information about widget. */
1961 int y1
, /* Upper left corner of area to
1962 * redraw. Pixels on edge are
1965 int y2
/* Lower right corner of area to
1966 * redraw. Pixels on edge are
1970 if ((canvasPtr
->tkwin
== NULL
) || !Tk_IsMapped(canvasPtr
->tkwin
)) {
1973 if (canvasPtr
->flags
& REDRAW_PENDING
) {
1974 if (x1
<= canvasPtr
->redrawX1
) {
1975 canvasPtr
->redrawX1
= x1
;
1977 if (y1
<= canvasPtr
->redrawY1
) {
1978 canvasPtr
->redrawY1
= y1
;
1980 if (x2
>= canvasPtr
->redrawX2
) {
1981 canvasPtr
->redrawX2
= x2
;
1983 if (y2
>= canvasPtr
->redrawY2
) {
1984 canvasPtr
->redrawY2
= y2
;
1987 canvasPtr
->redrawX1
= x1
;
1988 canvasPtr
->redrawY1
= y1
;
1989 canvasPtr
->redrawX2
= x2
;
1990 canvasPtr
->redrawY2
= y2
;
1991 // Tk_DoWhenIdle(DisplayCanvas, (ClientData) canvasPtr);
1992 canvasPtr
->flags
|= REDRAW_PENDING
;
1993 assert(canvasPtr
->updateTimerToken
== NULL
);
1994 if (canvasPtr
->updateTimerToken
== 0) {
1995 canvasPtr
->updateTimerToken
=
1996 Tk_CreateTimerHandler(
1999 (ClientData
) canvasPtr
);
2005 *--------------------------------------------------------------
2007 * Tk_CreateItemType --
2009 * This procedure may be invoked to add a new kind of canvas
2010 * element to the core item types supported by Tk.
2016 * From now on, the new item type will be useable in canvas
2017 * widgets (e.g. typePtr->name can be used as the item type
2018 * in "create" widget commands). If there was already a
2019 * type with the same name as in typePtr, it is replaced with
2022 *--------------------------------------------------------------
2027 Tk_ItemType
*typePtr
/* Information about item type;
2028 * storage must be statically
2029 * allocated (must live forever). */
2032 if (typeList
== NULL
) {
2035 typePtr
->nextPtr
= typeList
;
2040 *--------------------------------------------------------------
2044 * This procedure is invoked to perform once-only-ever
2045 * initialization for the module, such as setting up
2054 *--------------------------------------------------------------
2060 if (typeList
!= NULL
) {
2063 typeList
= &TkRectangleType
;
2064 TkRectangleType
.nextPtr
= &TkTextType
;
2065 TkTextType
.nextPtr
= &TkPolygonType
;
2066 TkPolygonType
.nextPtr
= &TkOvalType
;
2067 TkOvalType
.nextPtr
= &TkLineType
;
2068 TkLineType
.nextPtr
= &TkWindowType
;
2069 TkWindowType
.nextPtr
= &TkBitmapType
;
2070 TkBitmapType
.nextPtr
= &TkArcType
;
2071 TkArcType
.nextPtr
= NULL
;
2072 allUid
= Tk_GetUid("all");
2073 currentUid
= Tk_GetUid("current");
2077 *--------------------------------------------------------------
2081 * This procedure is called to initiate an enumeration of
2082 * all items in a given canvas that contain a given tag.
2085 * The return value is a pointer to the first item in
2086 * canvasPtr that matches tag, or NULL if there is no
2087 * such item. The information at *searchPtr is initialized
2088 * such that successive calls to NextItem will return
2089 * successive items that match tag.
2092 * SearchPtr is linked into a list of searches in progress
2093 * on canvasPtr, so that elements can safely be deleted
2094 * while the search is in progress. EndTagSearch must be
2095 * called at the end of the search to unlink searchPtr from
2098 *--------------------------------------------------------------
2103 Tk_Canvas
*canvasPtr
, /* Canvas whose items are to be
2105 char *tag
, /* String giving tag value. */
2106 TagSearch
*searchPtr
/* Record describing tag search;
2107 * will be initialized here. */
2111 register Tk_Item
*itemPtr
, *prevPtr
;
2112 register Tk_Uid
*tagPtr
;
2113 register Tk_Uid uid
;
2117 * Initialize the search.
2120 searchPtr
->canvasPtr
= canvasPtr
;
2121 searchPtr
->searchOver
= 0;
2124 * Find the first matching item in one of several ways. If the tag
2125 * is a number then it selects the single item with the matching
2126 * identifier. In this case see if the item being requested is the
2127 * hot item, in which case the search can be skipped.
2130 if (isdigit(*tag
)) {
2134 id
= strtoul(tag
, &end
, 0);
2136 itemPtr
= canvasPtr
->hotPtr
;
2137 prevPtr
= canvasPtr
->hotPrevPtr
;
2138 if ((itemPtr
== NULL
) || (itemPtr
->id
!= id
) || (prevPtr
== NULL
)
2139 || (prevPtr
->nextPtr
!= itemPtr
)) {
2141 for (prevPtr
= NULL
, itemPtr
= canvasPtr
->firstItemPtr
;
2143 prevPtr
= itemPtr
, itemPtr
= itemPtr
->nextPtr
) {
2144 if (itemPtr
->id
== id
) {
2149 searchPtr
->prevPtr
= prevPtr
;
2150 searchPtr
->searchOver
= 1;
2151 canvasPtr
->hotPtr
= itemPtr
;
2152 canvasPtr
->hotPrevPtr
= prevPtr
;
2157 searchPtr
->tag
= uid
= Tk_GetUid(tag
);
2158 if (uid
== allUid
) {
2164 searchPtr
->tag
= NULL
;
2165 searchPtr
->prevPtr
= NULL
;
2166 searchPtr
->currentPtr
= canvasPtr
->firstItemPtr
;
2167 return canvasPtr
->firstItemPtr
;
2171 * None of the above. Search for an item with a matching tag.
2174 for (prevPtr
= NULL
, itemPtr
= canvasPtr
->firstItemPtr
; itemPtr
!= NULL
;
2175 prevPtr
= itemPtr
, itemPtr
= itemPtr
->nextPtr
) {
2176 for (tagPtr
= itemPtr
->tagPtr
, count
= itemPtr
->numTags
;
2177 count
> 0; tagPtr
++, count
--) {
2178 if (*tagPtr
== uid
) {
2179 searchPtr
->prevPtr
= prevPtr
;
2180 searchPtr
->currentPtr
= itemPtr
;
2185 searchPtr
->prevPtr
= prevPtr
;
2186 searchPtr
->searchOver
= 1;
2191 *--------------------------------------------------------------
2195 * This procedure returns successive items that match a given
2196 * tag; it should be called only after StartTagSearch has been
2197 * used to begin a search.
2200 * The return value is a pointer to the next item that matches
2201 * the tag specified to StartTagSearch, or NULL if no such
2202 * item exists. *SearchPtr is updated so that the next call
2203 * to this procedure will return the next item.
2208 *--------------------------------------------------------------
2213 TagSearch
*searchPtr
/* Record describing search in
2217 register Tk_Item
*itemPtr
, *prevPtr
;
2219 register Tk_Uid uid
;
2220 register Tk_Uid
*tagPtr
;
2223 * Find next item in list (this may not actually be a suitable
2224 * one to return), and return if there are no items left.
2227 prevPtr
= searchPtr
->prevPtr
;
2228 if (prevPtr
== NULL
) {
2229 itemPtr
= searchPtr
->canvasPtr
->firstItemPtr
;
2231 itemPtr
= prevPtr
->nextPtr
;
2233 if ((itemPtr
== NULL
) || (searchPtr
->searchOver
)) {
2234 searchPtr
->searchOver
= 1;
2237 if (itemPtr
!= searchPtr
->currentPtr
) {
2239 * The structure of the list has changed. Probably the
2240 * previously-returned item was removed from the list.
2241 * In this case, don't advance prevPtr; just return
2242 * its new successor (i.e. do nothing here).
2246 itemPtr
= prevPtr
->nextPtr
;
2250 * Handle special case of "all" search by returning next item.
2253 uid
= searchPtr
->tag
;
2255 searchPtr
->prevPtr
= prevPtr
;
2256 searchPtr
->currentPtr
= itemPtr
;
2261 * Look for an item with a particular tag.
2264 for ( ; itemPtr
!= NULL
; prevPtr
= itemPtr
, itemPtr
= itemPtr
->nextPtr
) {
2265 for (tagPtr
= itemPtr
->tagPtr
, count
= itemPtr
->numTags
;
2266 count
> 0; tagPtr
++, count
--) {
2267 if (*tagPtr
== uid
) {
2268 searchPtr
->prevPtr
= prevPtr
;
2269 searchPtr
->currentPtr
= itemPtr
;
2274 searchPtr
->prevPtr
= prevPtr
;
2275 searchPtr
->searchOver
= 1;
2280 *--------------------------------------------------------------
2284 * This is a utility procedure called by FindItems. It
2285 * either adds itemPtr's id to the result forming in interp,
2286 * or it adds a new tag to itemPtr, depending on the value
2293 * If tag is NULL then itemPtr's id is added as a list element
2294 * to interp->result; otherwise tag is added to itemPtr's
2297 *--------------------------------------------------------------
2302 Tcl_Interp
*interp
, /* Interpreter in which to (possibly)
2303 * record item id. */
2304 register Tk_Item
*itemPtr
, /* Item to (possibly) modify. */
2305 Tk_Uid tag
/* Tag to add to those already
2306 * present for item, or NULL. */
2309 register Tk_Uid
*tagPtr
;
2313 * Handle the "add-to-result" case and return, if appropriate.
2318 sprintf(msg
, "%d", itemPtr
->id
);
2319 Tcl_AppendElement(interp
, msg
, 0);
2323 for (tagPtr
= itemPtr
->tagPtr
, count
= itemPtr
->numTags
;
2324 count
> 0; tagPtr
++, count
--) {
2325 if (tag
== *tagPtr
) {
2331 * Grow the tag space if there's no more room left in the current
2335 if (itemPtr
->tagSpace
== itemPtr
->numTags
) {
2338 itemPtr
->tagSpace
+= 5;
2339 newTagPtr
= (Tk_Uid
*) ckalloc((unsigned)
2340 (itemPtr
->tagSpace
* sizeof(Tk_Uid
)));
2341 memcpy((VOID
*) newTagPtr
, (VOID
*) itemPtr
->tagPtr
,
2342 (itemPtr
->numTags
* sizeof(Tk_Uid
)));
2343 if (itemPtr
->tagPtr
!= itemPtr
->staticTagSpace
) {
2344 ckfree((char *) itemPtr
->tagPtr
);
2346 itemPtr
->tagPtr
= newTagPtr
;
2347 tagPtr
= &itemPtr
->tagPtr
[itemPtr
->numTags
];
2351 * Add in the new tag.
2359 *--------------------------------------------------------------
2363 * This procedure does all the work of implementing the
2364 * "find" and "addtag" options of the canvas widget command,
2365 * which locate items that have certain features (location,
2366 * tags, position in display list, etc.).
2369 * A standard Tcl return value. If newTag is NULL, then a
2370 * list of ids from all the items that match argc/argv is
2371 * returned in interp->result. If newTag is NULL, then
2372 * the normal interp->result is an empty string. If an error
2373 * occurs, then interp->result will hold an error message.
2376 * If newTag is non-NULL, then all the items that match the
2377 * information in argc/argv have that tag added to their
2380 *--------------------------------------------------------------
2385 Tcl_Interp
*interp
, /* Interpreter for error reporting. */
2386 Tk_Canvas
*canvasPtr
, /* Canvas whose items are to be
2388 int argc
, /* Number of entries in argv. Must be
2389 * greater than zero. */
2390 char **argv
, /* Arguments that describe what items
2391 * to search for (see user doc on
2392 * "find" and "addtag" options). */
2393 char *newTag
, /* If non-NULL, gives new tag to set
2394 * on all found items, if NULL, then
2395 * ids of found items are returned
2396 * in interp->result. */
2397 char *cmdName
, /* Name of original Tcl command, for
2398 * use in error messages. */
2399 char *option
/* For error messages: gives option
2400 * from Tcl command and other stuff
2401 * up to what's in argc/argv. */
2407 register Tk_Item
*itemPtr
;
2410 if (newTag
!= NULL
) {
2411 uid
= Tk_GetUid(newTag
);
2416 length
= strlen(argv
[0]);
2417 if ((c
== 'a') && (strncmp(argv
[0], "above", length
) == 0)
2419 Tk_Item
*lastPtr
= NULL
;
2421 Tcl_AppendResult(interp
, "wrong # args: must be \"",
2422 cmdName
, option
, " above tagOrId", (char *) NULL
);
2425 for (itemPtr
= StartTagSearch(canvasPtr
, argv
[1], &search
);
2426 itemPtr
!= NULL
; itemPtr
= NextItem(&search
)) {
2429 if ((lastPtr
!= NULL
) && (lastPtr
->nextPtr
!= NULL
)) {
2430 DoItem(interp
, lastPtr
->nextPtr
, uid
);
2432 } else if ((c
== 'a') && (strncmp(argv
[0], "all", length
) == 0)
2435 Tcl_AppendResult(interp
, "wrong # args: must be \"",
2436 cmdName
, option
, " all", (char *) NULL
);
2440 for (itemPtr
= canvasPtr
->firstItemPtr
; itemPtr
!= NULL
;
2441 itemPtr
= itemPtr
->nextPtr
) {
2442 DoItem(interp
, itemPtr
, uid
);
2444 } else if ((c
== 'b') && (strncmp(argv
[0], "below", length
) == 0)) {
2446 Tcl_AppendResult(interp
, "wrong # args: must be \"",
2447 cmdName
, option
, " below tagOrId", (char *) NULL
);
2450 itemPtr
= StartTagSearch(canvasPtr
, argv
[1], &search
);
2451 if (search
.prevPtr
!= NULL
) {
2452 DoItem(interp
, search
.prevPtr
, uid
);
2454 } else if ((c
== 'c') && (strncmp(argv
[0], "closest", length
) == 0)) {
2456 Tk_Item
*startPtr
, *closestPtr
;
2457 double coords
[2], halo
;
2460 if ((argc
< 3) || (argc
> 5)) {
2461 Tcl_AppendResult(interp
, "wrong # args: must be \"",
2462 cmdName
, option
, " closest x y ?halo? ?start?",
2466 if ((TkGetCanvasCoord(canvasPtr
, argv
[1], &coords
[0]) != TCL_OK
)
2467 || (TkGetCanvasCoord(canvasPtr
, argv
[2], &coords
[1])
2472 if (TkGetCanvasCoord(canvasPtr
, argv
[3], &halo
) != TCL_OK
) {
2476 Tcl_AppendResult(interp
, "can't have negative halo value \"",
2477 argv
[3], "\"", (char *) NULL
);
2485 * Find the item at which to start the search.
2488 startPtr
= canvasPtr
->firstItemPtr
;
2490 itemPtr
= StartTagSearch(canvasPtr
, argv
[4], &search
);
2491 if (itemPtr
!= NULL
) {
2497 * The code below is optimized so that it can eliminate most
2498 * items without having to call their item-specific procedures.
2499 * This is done by keeping a bounding box (x1, y1, x2, y2) that
2500 * an item's bbox must overlap if the item is to have any
2501 * chance of being closer than the closest so far.
2505 if (itemPtr
== NULL
) {
2508 closestDist
= (*itemPtr
->typePtr
->pointProc
)(canvasPtr
,
2509 itemPtr
, coords
) - halo
;
2510 if (closestDist
< 0.0) {
2517 * Update the bounding box using itemPtr, which is the
2521 x1
= (coords
[0] - closestDist
- halo
- 1);
2522 y1
= (coords
[1] - closestDist
- halo
- 1);
2523 x2
= (coords
[0] + closestDist
+ halo
+ 1);
2524 y2
= (coords
[1] + closestDist
+ halo
+ 1);
2525 closestPtr
= itemPtr
;
2528 * Search for an item that beats the current closest one.
2529 * Work circularly through the canvas's item list until
2530 * getting back to the starting item.
2534 itemPtr
= itemPtr
->nextPtr
;
2535 if (itemPtr
== NULL
) {
2536 itemPtr
= canvasPtr
->firstItemPtr
;
2538 if (itemPtr
== startPtr
) {
2539 DoItem(interp
, closestPtr
, uid
);
2542 if ((itemPtr
->x1
>= x2
) || (itemPtr
->x2
<= x1
)
2543 || (itemPtr
->y1
>= y2
) || (itemPtr
->y2
<= y1
)) {
2546 newDist
= (*itemPtr
->typePtr
->pointProc
)(canvasPtr
,
2547 itemPtr
, coords
) - halo
;
2548 if (newDist
< 0.0) {
2551 if (newDist
<= closestDist
) {
2552 closestDist
= newDist
;
2557 } else if ((c
== 'e') && (strncmp(argv
[0], "enclosed", length
) == 0)) {
2559 Tcl_AppendResult(interp
, "wrong # args: must be \"",
2560 cmdName
, option
, " enclosed x1 y1 x2 y2", (char *) NULL
);
2563 return FindArea(interp
, canvasPtr
, argv
+1, uid
, 1);
2564 } else if ((c
== 'o') && (strncmp(argv
[0], "overlapping", length
) == 0)) {
2566 Tcl_AppendResult(interp
, "wrong # args: must be \"",
2567 cmdName
, option
, " overlapping x1 y1 x2 y2",
2571 return FindArea(interp
, canvasPtr
, argv
+1, uid
, 0);
2572 } else if ((c
== 'w') && (strncmp(argv
[0], "withtag", length
) == 0)) {
2574 Tcl_AppendResult(interp
, "wrong # args: must be \"",
2575 cmdName
, option
, " withtag tagOrId", (char *) NULL
);
2578 for (itemPtr
= StartTagSearch(canvasPtr
, argv
[1], &search
);
2579 itemPtr
!= NULL
; itemPtr
= NextItem(&search
)) {
2580 DoItem(interp
, itemPtr
, uid
);
2583 Tcl_AppendResult(interp
, "bad search command \"", argv
[0],
2584 "\": must be above, all, below, closest, enclosed, ",
2585 "overlapping, or withtag", (char *) NULL
);
2592 *--------------------------------------------------------------
2596 * This procedure implements area searches for the "find"
2597 * and "addtag" options.
2600 * A standard Tcl return value. If newTag is NULL, then a
2601 * list of ids from all the items overlapping or enclosed
2602 * by the rectangle given by argc is returned in interp->result.
2603 * If newTag is NULL, then the normal interp->result is an
2604 * empty string. If an error occurs, then interp->result will
2605 * hold an error message.
2608 * If uid is non-NULL, then all the items overlapping
2609 * or enclosed by the area in argv have that tag added to
2610 * their lists of tags.
2612 *--------------------------------------------------------------
2617 Tcl_Interp
*interp
, /* Interpreter for error reporting
2618 * and result storing. */
2619 Tk_Canvas
*canvasPtr
, /* Canvas whose items are to be
2621 char **argv
, /* Array of four arguments that
2622 * give the coordinates of the
2623 * rectangular area to search. */
2624 Tk_Uid uid
, /* If non-NULL, gives new tag to set
2625 * on all found items, if NULL, then
2626 * ids of found items are returned
2627 * in interp->result. */
2628 int enclosed
/* 0 means overlapping or enclosed
2629 * items are OK, 1 means only enclosed
2633 double rect
[4], tmp
;
2635 register Tk_Item
*itemPtr
;
2637 if ((TkGetCanvasCoord(canvasPtr
, argv
[0], &rect
[0]) != TCL_OK
)
2638 || (TkGetCanvasCoord(canvasPtr
, argv
[1], &rect
[1]) != TCL_OK
)
2639 || (TkGetCanvasCoord(canvasPtr
, argv
[2], &rect
[2]) != TCL_OK
)
2640 || (TkGetCanvasCoord(canvasPtr
, argv
[3], &rect
[3]) != TCL_OK
)) {
2643 if (rect
[0] > rect
[2]) {
2644 tmp
= rect
[0]; rect
[0] = rect
[2]; rect
[2] = tmp
;
2646 if (rect
[1] > rect
[3]) {
2647 tmp
= rect
[1]; rect
[1] = rect
[3]; rect
[3] = tmp
;
2651 * Use an integer bounding box for a quick test, to avoid
2652 * calling item-specific code except for items that are close.
2659 for (itemPtr
= canvasPtr
->firstItemPtr
; itemPtr
!= NULL
;
2660 itemPtr
= itemPtr
->nextPtr
) {
2661 if ((itemPtr
->x1
>= x2
) || (itemPtr
->x2
<= x1
)
2662 || (itemPtr
->y1
>= y2
) || (itemPtr
->y2
<= y1
)) {
2665 if ((*itemPtr
->typePtr
->areaProc
)(canvasPtr
, itemPtr
, rect
)
2667 DoItem(interp
, itemPtr
, uid
);
2674 *--------------------------------------------------------------
2678 * Move one or more items to a different place in the
2679 * display order for a canvas.
2685 * The items identified by "tag" are moved so that they
2686 * are all together in the display list and immediately
2687 * after prevPtr. The order of the moved items relative
2688 * to each other is not changed.
2690 *--------------------------------------------------------------
2695 Tk_Canvas
*canvasPtr
, /* Canvas to be modified. */
2696 char *tag
, /* Tag identifying items to be moved
2697 * in the redisplay list. */
2698 Tk_Item
*prevPtr
/* Reposition the items so that they
2699 * go just after this item (NULL means
2700 * put at beginning of list). */
2703 register Tk_Item
*itemPtr
;
2705 Tk_Item
*firstMovePtr
, *lastMovePtr
;
2708 * Find all of the items to be moved and remove them from
2709 * the list, making an auxiliary list running from firstMovePtr
2710 * to lastMovePtr. Record their areas for redisplay.
2713 firstMovePtr
= lastMovePtr
= NULL
;
2714 for (itemPtr
= StartTagSearch(canvasPtr
, tag
, &search
);
2715 itemPtr
!= NULL
; itemPtr
= NextItem(&search
)) {
2716 if (itemPtr
== prevPtr
) {
2718 * Item after which insertion is to occur is being
2719 * moved! Switch to insert after its predecessor.
2722 prevPtr
= search
.prevPtr
;
2724 if (search
.prevPtr
== NULL
) {
2725 canvasPtr
->firstItemPtr
= itemPtr
->nextPtr
;
2727 search
.prevPtr
->nextPtr
= itemPtr
->nextPtr
;
2729 if (canvasPtr
->lastItemPtr
== itemPtr
) {
2730 canvasPtr
->lastItemPtr
= search
.prevPtr
;
2732 if (firstMovePtr
== NULL
) {
2733 firstMovePtr
= itemPtr
;
2735 lastMovePtr
->nextPtr
= itemPtr
;
2737 lastMovePtr
= itemPtr
;
2738 EventuallyRedrawArea(canvasPtr
, itemPtr
->x1
, itemPtr
->y1
,
2739 itemPtr
->x2
, itemPtr
->y2
);
2740 canvasPtr
->flags
|= REPICK_NEEDED
;
2744 * Insert the list of to-be-moved items back into the canvas's
2745 * at the desired position.
2748 if (firstMovePtr
== NULL
) {
2751 if (prevPtr
== NULL
) {
2752 lastMovePtr
->nextPtr
= canvasPtr
->firstItemPtr
;
2753 canvasPtr
->firstItemPtr
= firstMovePtr
;
2755 lastMovePtr
->nextPtr
= prevPtr
->nextPtr
;
2756 prevPtr
->nextPtr
= firstMovePtr
;
2758 if (canvasPtr
->lastItemPtr
== prevPtr
) {
2759 canvasPtr
->lastItemPtr
= lastMovePtr
;
2764 *--------------------------------------------------------------
2768 * This procedure is invoked by the Tk dispatcher to handle
2769 * events associated with bindings on items.
2775 * Depends on the command invoked as part of the binding
2776 * (if there was any).
2778 *--------------------------------------------------------------
2783 ClientData clientData
, /* Pointer to canvas structure. */
2784 XEvent
*eventPtr
/* Pointer to X event that just
2788 Tk_Canvas
*canvasPtr
= (Tk_Canvas
*) clientData
;
2791 Tk_Preserve((ClientData
) canvasPtr
);
2794 * This code simulates grabs for mouse buttons by refusing to
2795 * pick a new current item between the time a mouse button goes
2796 * down and the time when the last mouse button is released is
2800 if (eventPtr
->type
== ButtonPress
) {
2801 canvasPtr
->flags
|= BUTTON_DOWN
;
2802 } else if (eventPtr
->type
== ButtonRelease
) {
2805 switch (eventPtr
->xbutton
.button
) {
2825 if ((eventPtr
->xbutton
.state
& (Button1Mask
|Button2Mask
2826 |Button3Mask
|Button4Mask
|Button5Mask
)) == mask
) {
2827 canvasPtr
->flags
&= ~BUTTON_DOWN
;
2830 } else if ((eventPtr
->type
== EnterNotify
)
2831 || (eventPtr
->type
== LeaveNotify
)) {
2832 PickCurrentItem(canvasPtr
, eventPtr
);
2834 } else if (eventPtr
->type
== MotionNotify
) {
2835 PickCurrentItem(canvasPtr
, eventPtr
);
2837 CanvasDoEvent(canvasPtr
, eventPtr
);
2839 unsigned int oldState
;
2841 oldState
= eventPtr
->xbutton
.state
;
2842 eventPtr
->xbutton
.state
&= ~(Button1Mask
|Button2Mask
2843 |Button3Mask
|Button4Mask
|Button5Mask
);
2844 PickCurrentItem(canvasPtr
, eventPtr
);
2845 eventPtr
->xbutton
.state
= oldState
;
2849 Tk_Release((ClientData
) canvasPtr
);
2853 *--------------------------------------------------------------
2855 * PickCurrentItem --
2857 * Find the topmost item in a canvas that contains a given
2858 * location and mark the the current item. If the current
2859 * item has changed, generate a fake exit event on the old
2860 * current item and a fake enter event on the new current
2867 * The current item for canvasPtr may change. If it does,
2868 * then the commands associated with item entry and exit
2869 * could do just about anything.
2871 *--------------------------------------------------------------
2876 register Tk_Canvas
*canvasPtr
, /* Canvas pointer in which to select
2878 XEvent
*eventPtr
/* Event describing location of
2879 * mouse cursor. Must be EnterWindow,
2880 * LeaveWindow, ButtonRelease, or
2884 Tk_Item
*closestPtr
= NULL
;
2887 * If a button is down, then don't do anything at all; we'll be
2888 * called again when all buttons are up, and we can repick then.
2889 * This implements a form of mouse grabbing for canvases.
2892 if (canvasPtr
->flags
& BUTTON_DOWN
) {
2897 * Save information about this event in the canvas. The event in
2898 * the canvas is used for two purposes:
2900 * 1. Event bindings: if the current item changes, fake events are
2901 * generated to allow item-enter and item-leave bindings to trigger.
2902 * 2. Reselection: if the current item gets deleted, can use the
2903 * saved event to find a new current item.
2904 * Translate MotionNotify events into EnterNotify events, since that's
2905 * what gets reported to item handlers.
2908 if (eventPtr
!= &canvasPtr
->pickEvent
) {
2909 if ((eventPtr
->type
== MotionNotify
)
2910 || (eventPtr
->type
== ButtonRelease
)) {
2911 canvasPtr
->pickEvent
.xcrossing
.type
= EnterNotify
;
2912 canvasPtr
->pickEvent
.xcrossing
.serial
= eventPtr
->xmotion
.serial
;
2913 canvasPtr
->pickEvent
.xcrossing
.send_event
2914 = eventPtr
->xmotion
.send_event
;
2915 canvasPtr
->pickEvent
.xcrossing
.display
= eventPtr
->xmotion
.display
;
2916 canvasPtr
->pickEvent
.xcrossing
.window
= eventPtr
->xmotion
.window
;
2917 canvasPtr
->pickEvent
.xcrossing
.root
= eventPtr
->xmotion
.root
;
2918 canvasPtr
->pickEvent
.xcrossing
.subwindow
= None
;
2919 canvasPtr
->pickEvent
.xcrossing
.time
= eventPtr
->xmotion
.time
;
2920 canvasPtr
->pickEvent
.xcrossing
.x
= eventPtr
->xmotion
.x
;
2921 canvasPtr
->pickEvent
.xcrossing
.y
= eventPtr
->xmotion
.y
;
2922 canvasPtr
->pickEvent
.xcrossing
.x_root
= eventPtr
->xmotion
.x_root
;
2923 canvasPtr
->pickEvent
.xcrossing
.y_root
= eventPtr
->xmotion
.y_root
;
2924 canvasPtr
->pickEvent
.xcrossing
.mode
= NotifyNormal
;
2925 canvasPtr
->pickEvent
.xcrossing
.detail
= NotifyNonlinear
;
2926 canvasPtr
->pickEvent
.xcrossing
.same_screen
2927 = eventPtr
->xmotion
.same_screen
;
2928 canvasPtr
->pickEvent
.xcrossing
.focus
= False
;
2929 canvasPtr
->pickEvent
.xcrossing
.state
= eventPtr
->xmotion
.state
;
2931 canvasPtr
->pickEvent
= *eventPtr
;
2936 * A LeaveNotify event automatically means that there's no current
2937 * object, so the rest of the code below can be skipped.
2940 if (canvasPtr
->pickEvent
.type
!= LeaveNotify
) {
2943 register Tk_Item
*itemPtr
;
2945 coords
[0] = canvasPtr
->pickEvent
.xcrossing
.x
+ canvasPtr
->xOrigin
;
2946 coords
[1] = canvasPtr
->pickEvent
.xcrossing
.y
+ canvasPtr
->yOrigin
;
2947 x1
= coords
[0] - canvasPtr
->closeEnough
;
2948 y1
= coords
[1] - canvasPtr
->closeEnough
;
2949 x2
= coords
[0] + canvasPtr
->closeEnough
;
2950 y2
= coords
[1] + canvasPtr
->closeEnough
;
2952 for (itemPtr
= canvasPtr
->firstItemPtr
; itemPtr
!= NULL
;
2953 itemPtr
= itemPtr
->nextPtr
) {
2954 if ((itemPtr
->x1
>= x2
) || (itemPtr
->x2
< x1
)
2955 || (itemPtr
->y1
>= y2
) || (itemPtr
->y2
< y1
)) {
2958 if ((*itemPtr
->typePtr
->pointProc
)(canvasPtr
,
2959 itemPtr
, coords
) <= canvasPtr
->closeEnough
) {
2960 closestPtr
= itemPtr
;
2966 * Simulate a LeaveNotify event on the previous current item and
2967 * an EnterNotify event on the new current item. Remove the "current"
2968 * tag from the previous current item and place it on the new current
2972 if (closestPtr
== canvasPtr
->currentItemPtr
) {
2975 if (canvasPtr
->currentItemPtr
!= NULL
) {
2977 Tk_Item
*itemPtr
= canvasPtr
->currentItemPtr
;
2980 event
= canvasPtr
->pickEvent
;
2981 event
.type
= LeaveNotify
;
2982 CanvasDoEvent(canvasPtr
, &event
);
2983 for (i
= itemPtr
->numTags
-1; i
>= 0; i
--) {
2984 if (itemPtr
->tagPtr
[i
] == currentUid
) {
2985 itemPtr
->tagPtr
[i
] = itemPtr
->tagPtr
[itemPtr
->numTags
-1];
2991 canvasPtr
->currentItemPtr
= closestPtr
;
2992 if (canvasPtr
->currentItemPtr
!= NULL
) {
2995 DoItem((Tcl_Interp
*) NULL
, closestPtr
, currentUid
);
2996 event
= canvasPtr
->pickEvent
;
2997 event
.type
= EnterNotify
;
2998 CanvasDoEvent(canvasPtr
, &event
);
3003 *--------------------------------------------------------------
3007 * This procedure is called to invoke binding processing
3008 * for a new event that is associated with the current item
3015 * Depends on the bindings for the canvas.
3017 *--------------------------------------------------------------
3022 Tk_Canvas
*canvasPtr
, /* Canvas widget in which event
3024 XEvent
*eventPtr
/* Real or simulated X event that
3025 * is to be processed. */
3028 #define NUM_STATIC 3
3029 ClientData staticObjects
[NUM_STATIC
];
3030 ClientData
*objectPtr
;
3032 register Tk_Item
*itemPtr
;
3034 if (canvasPtr
->bindingTable
== NULL
) {
3038 itemPtr
= canvasPtr
->currentItemPtr
;
3039 if ((eventPtr
->type
== KeyPress
) || (eventPtr
->type
== KeyRelease
)) {
3040 itemPtr
= canvasPtr
->focusItemPtr
;
3042 if (itemPtr
== NULL
) {
3047 * Set up an array with all the relevant objects for processing
3048 * this event. The relevant objects are (a) the event's item,
3049 * (b) the tags associated with the event's item, and (c) the
3050 * tag "all". If there are a lot of tags then malloc an array
3051 * to hold all of the objects.
3054 numObjects
= itemPtr
->numTags
+ 2;
3055 if (numObjects
<= NUM_STATIC
) {
3056 objectPtr
= staticObjects
;
3058 objectPtr
= (ClientData
*) ckalloc((unsigned)
3059 (numObjects
* sizeof(ClientData
)));
3061 objectPtr
[0] = (ClientData
) itemPtr
;
3062 for (i
= itemPtr
->numTags
-1; i
>= 0; i
--) {
3063 objectPtr
[i
+1] = (ClientData
) itemPtr
->tagPtr
[i
];
3065 objectPtr
[itemPtr
->numTags
+1] = (ClientData
) allUid
;
3068 * Invoke the binding system, then free up the object array if
3072 Tk_BindEvent(canvasPtr
->bindingTable
, eventPtr
, canvasPtr
->tkwin
,
3073 numObjects
, objectPtr
);
3074 if (objectPtr
!= staticObjects
) {
3075 ckfree((char *) objectPtr
);
3080 *----------------------------------------------------------------------
3082 * CanvasBlinkProc --
3084 * This procedure is called as a timer handler to blink the
3085 * insertion cursor off and on.
3091 * The cursor gets turned on or off, redisplay gets invoked,
3092 * and this procedure reschedules itself.
3094 *----------------------------------------------------------------------
3099 ClientData clientData
/* Pointer to record describing entry. */
3102 register Tk_Canvas
*canvasPtr
= (Tk_Canvas
*) clientData
;
3104 if (!(canvasPtr
->flags
& GOT_FOCUS
) || (canvasPtr
->cursorOffTime
== 0)) {
3107 if (canvasPtr
->flags
& CURSOR_ON
) {
3108 canvasPtr
->flags
&= ~CURSOR_ON
;
3109 canvasPtr
->cursorBlinkHandler
= Tk_CreateTimerHandler(
3110 canvasPtr
->cursorOffTime
, CanvasBlinkProc
,
3111 (ClientData
) canvasPtr
);
3113 canvasPtr
->flags
|= CURSOR_ON
;
3114 canvasPtr
->cursorBlinkHandler
= Tk_CreateTimerHandler(
3115 canvasPtr
->cursorOnTime
, CanvasBlinkProc
,
3116 (ClientData
) canvasPtr
);
3118 if (canvasPtr
->focusItemPtr
!= NULL
) {
3119 EventuallyRedrawArea(canvasPtr
, canvasPtr
->focusItemPtr
->x1
,
3120 canvasPtr
->focusItemPtr
->y1
, canvasPtr
->focusItemPtr
->x2
,
3121 canvasPtr
->focusItemPtr
->y2
);
3126 *----------------------------------------------------------------------
3128 * CanvasFocusProc --
3130 * This procedure is called whenever a canvas gets or loses the
3131 * input focus. It's also called whenever the window is
3132 * reconfigured while it has the focus.
3138 * The cursor gets turned on or off.
3140 *----------------------------------------------------------------------
3145 ClientData clientData
, /* Pointer to structure describing entry. */
3146 int gotFocus
/* 1 means window is getting focus, 0 means
3147 * it's losing it. */
3150 register Tk_Canvas
*canvasPtr
= (Tk_Canvas
*) clientData
;
3152 Tk_DeleteTimerHandler(canvasPtr
->cursorBlinkHandler
);
3154 canvasPtr
->flags
|= GOT_FOCUS
| CURSOR_ON
;
3155 if (canvasPtr
->cursorOffTime
!= 0) {
3156 canvasPtr
->cursorBlinkHandler
= Tk_CreateTimerHandler(
3157 canvasPtr
->cursorOnTime
, CanvasBlinkProc
,
3158 (ClientData
) canvasPtr
);
3161 canvasPtr
->flags
&= ~(GOT_FOCUS
| CURSOR_ON
);
3162 canvasPtr
->cursorBlinkHandler
= (Tk_TimerToken
) NULL
;
3164 if (canvasPtr
->focusItemPtr
!= NULL
) {
3165 EventuallyRedrawArea(canvasPtr
, canvasPtr
->focusItemPtr
->x1
,
3166 canvasPtr
->focusItemPtr
->y1
, canvasPtr
->focusItemPtr
->x2
,
3167 canvasPtr
->focusItemPtr
->y2
);
3172 *----------------------------------------------------------------------
3176 * Modify the selection by moving its un-anchored end. This could
3177 * make the selection either larger or smaller.
3183 * The selection changes.
3185 *----------------------------------------------------------------------
3190 register Tk_Canvas
*canvasPtr
, /* Information about widget. */
3191 register Tk_Item
*itemPtr
, /* Item that is to hold selection. */
3192 int index
/* Index of element that is to
3193 * become the "other" end of the
3197 int oldFirst
, oldLast
;
3200 oldFirst
= canvasPtr
->selectFirst
;
3201 oldLast
= canvasPtr
->selectLast
;
3202 oldSelPtr
= canvasPtr
->selItemPtr
;
3205 * Grab the selection if we don't own it already.
3208 if (canvasPtr
->selItemPtr
== NULL
) {
3209 Tk_OwnSelection(canvasPtr
->tkwin
, CanvasLostSelection
,
3210 (ClientData
) canvasPtr
);
3211 } else if (canvasPtr
->selItemPtr
!= itemPtr
) {
3212 EventuallyRedrawArea(canvasPtr
, canvasPtr
->selItemPtr
->x1
,
3213 canvasPtr
->selItemPtr
->y1
, canvasPtr
->selItemPtr
->x2
,
3214 canvasPtr
->selItemPtr
->y2
);
3216 canvasPtr
->selItemPtr
= itemPtr
;
3218 if (canvasPtr
->anchorItemPtr
!= itemPtr
) {
3219 canvasPtr
->anchorItemPtr
= itemPtr
;
3220 canvasPtr
->selectAnchor
= index
;
3222 if (canvasPtr
->selectAnchor
<= index
) {
3223 canvasPtr
->selectFirst
= canvasPtr
->selectAnchor
;
3224 canvasPtr
->selectLast
= index
;
3226 canvasPtr
->selectFirst
= index
;
3227 canvasPtr
->selectLast
= canvasPtr
->selectAnchor
- 1;
3229 if ((canvasPtr
->selectFirst
!= oldFirst
)
3230 || (canvasPtr
->selectLast
!= oldLast
)
3231 || (itemPtr
!= oldSelPtr
)) {
3232 EventuallyRedrawArea(canvasPtr
, itemPtr
->x1
, itemPtr
->y1
,
3233 itemPtr
->x2
, itemPtr
->y2
);
3238 *--------------------------------------------------------------
3240 * CanvasFetchSelection --
3242 * This procedure is invoked by Tk to return part or all of
3243 * the selection, when the selection is in a canvas widget.
3244 * This procedure always returns the selection as a STRING.
3247 * The return value is the number of non-NULL bytes stored
3248 * at buffer. Buffer is filled (or partially filled) with a
3249 * NULL-terminated string containing part or all of the selection,
3250 * as given by offset and maxBytes.
3255 *--------------------------------------------------------------
3259 CanvasFetchSelection(
3260 ClientData clientData
, /* Information about canvas widget. */
3261 int offset
, /* Offset within selection of first
3262 * character to be returned. */
3263 char *buffer
, /* Location in which to place
3265 int maxBytes
/* Maximum number of bytes to place
3266 * at buffer, not including terminating
3267 * NULL character. */
3270 register Tk_Canvas
*canvasPtr
= (Tk_Canvas
*) clientData
;
3272 if (canvasPtr
->selItemPtr
== NULL
) {
3275 if (canvasPtr
->selItemPtr
->typePtr
->selectionProc
== NULL
) {
3278 return (*canvasPtr
->selItemPtr
->typePtr
->selectionProc
)(
3279 canvasPtr
, canvasPtr
->selItemPtr
, offset
, buffer
, maxBytes
);
3283 *----------------------------------------------------------------------
3285 * CanvasLostSelection --
3287 * This procedure is called back by Tk when the selection is
3288 * grabbed away from a canvas widget.
3294 * The existing selection is unhighlighted, and the window is
3295 * marked as not containing a selection.
3297 *----------------------------------------------------------------------
3301 CanvasLostSelection(
3302 ClientData clientData
/* Information about entry widget. */
3305 Tk_Canvas
*canvasPtr
= (Tk_Canvas
*) clientData
;
3307 if (canvasPtr
->selItemPtr
!= NULL
) {
3308 EventuallyRedrawArea(canvasPtr
, canvasPtr
->selItemPtr
->x1
,
3309 canvasPtr
->selItemPtr
->y1
, canvasPtr
->selItemPtr
->x2
,
3310 canvasPtr
->selItemPtr
->y2
);
3312 canvasPtr
->selItemPtr
= NULL
;
3316 *--------------------------------------------------------------
3318 * TkGetCanvasCoord --
3320 * Given a string, returns a floating-point canvas coordinate
3321 * corresponding to that string.
3324 * The return value is a standard Tcl return result. If
3325 * TCL_OK is returned, then everything went well and the
3326 * canvas coordinate is stored at *doublePtr; otherwise
3327 * TCL_ERROR is returned and an error message is left in
3328 * canvasPtr->interp->result.
3333 *--------------------------------------------------------------
3338 Tk_Canvas
*canvasPtr
, /* Canvas to which coordinate applies. */
3339 char *string
, /* Describes coordinate (any screen
3340 * coordinate form may be used here). */
3341 double *doublePtr
/* Place to store converted coordinate. */
3344 if (Tk_GetScreenMM(canvasPtr
->interp
, canvasPtr
->tkwin
, string
,
3345 doublePtr
) != TCL_OK
) {
3348 *doublePtr
*= canvasPtr
->pixelsPerMM
;
3353 *--------------------------------------------------------------
3357 * Given a coordinate and a grid spacing, this procedure
3358 * computes the location of the nearest grid line to the
3362 * The return value is the location of the grid line nearest
3368 *--------------------------------------------------------------
3373 double coord
, /* Coordinate to grid-align. */
3374 double spacing
/* Spacing between grid lines. If <= 0
3375 * then no alignment is done. */
3378 if (spacing
<= 0.0) {
3382 return -((int) ((-coord
)/spacing
+ 0.5)) * spacing
;
3384 return ((int) (coord
/spacing
+ 0.5)) * spacing
;
3388 *--------------------------------------------------------------
3390 * CanvasUpdateScrollbars --
3392 * This procedure is invoked whenever a canvas has changed in
3393 * a way that requires scrollbars to be redisplayed (e.g. the
3394 * view in the canvas has changed).
3400 * If there are scrollbars associated with the canvas, then
3401 * their scrolling commands are invoked to cause them to
3402 * redisplay. If errors occur, additional Tcl commands may
3403 * be invoked to process the errors.
3405 *--------------------------------------------------------------
3409 CanvasUpdateScrollbars (
3410 register Tk_Canvas
*canvasPtr
/* Information about canvas. */
3413 int result
, size
, first
, last
, page
;
3416 #define ROUND(number) \
3417 if (number >= 0) { \
3418 number = (number + canvasPtr->scrollIncrement/2) \
3419 /canvasPtr->scrollIncrement; \
3421 number = -(((-number) + canvasPtr->scrollIncrement/2) \
3422 /canvasPtr->scrollIncrement); \
3425 canvasPtr
->flags
&= ~UPDATE_SCROLLBARS
;
3426 if (canvasPtr
->xScrollCmd
!= NULL
) {
3427 size
= ((canvasPtr
->scrollX2
- canvasPtr
->scrollX1
)
3428 /canvasPtr
->scrollIncrement
) + 1;
3429 first
= canvasPtr
->xOrigin
- canvasPtr
->scrollX1
;
3431 last
= canvasPtr
->xOrigin
+ Tk_Width(canvasPtr
->tkwin
)
3432 - 1 - canvasPtr
->scrollX1
;
3434 page
= last
- first
- 1;
3438 sprintf(args
, " %d %d %d %d", size
, page
, first
, last
);
3439 result
= Tcl_VarEval(canvasPtr
->interp
, canvasPtr
->xScrollCmd
, args
,
3441 if (result
!= TCL_OK
) {
3442 TkBindError(canvasPtr
->interp
);
3444 Tcl_ResetResult(canvasPtr
->interp
);
3447 if (canvasPtr
->yScrollCmd
!= NULL
) {
3448 size
= ((canvasPtr
->scrollY2
- canvasPtr
->scrollY1
)
3449 /canvasPtr
->scrollIncrement
) + 1;
3450 first
= canvasPtr
->yOrigin
- canvasPtr
->scrollY1
;
3452 last
= canvasPtr
->yOrigin
+ Tk_Height(canvasPtr
->tkwin
)
3453 - 1 - canvasPtr
->scrollY1
;
3455 page
= last
- first
- 1;
3459 sprintf(args
, " %d %d %d %d", size
, page
, first
, last
);
3460 result
= Tcl_VarEval(canvasPtr
->interp
, canvasPtr
->yScrollCmd
, args
,
3462 if (result
!= TCL_OK
) {
3463 TkBindError(canvasPtr
->interp
);
3465 Tcl_ResetResult(canvasPtr
->interp
);
3470 *--------------------------------------------------------------
3472 * CanvasSetOrigin --
3474 * This procedure is invoked to change the mapping between
3475 * canvas coordinates and screen coordinates in the canvas
3482 * The canvas will be redisplayed to reflect the change in
3483 * view. In addition, scrollbars will be updated if there
3486 *--------------------------------------------------------------
3491 register Tk_Canvas
*canvasPtr
, /* Information about canvas. */
3492 int xOrigin
, /* New X origin for canvas (canvas
3493 * x-coord corresponding to left edge
3494 * of canvas window). */
3495 int yOrigin
/* New Y origin for canvas (canvas
3496 * y-coord corresponding to top edge
3497 * of canvas window). */
3501 * Adjust the origin if necessary to keep as much as possible of the
3502 * canvas in the view.
3505 if ((canvasPtr
->confine
) && (canvasPtr
->regionString
!= NULL
)) {
3506 int windowWidth
, windowHeight
, canvasWidth
, canvasHeight
;
3508 windowWidth
= Tk_Width(canvasPtr
->tkwin
);
3509 windowHeight
= Tk_Height(canvasPtr
->tkwin
);
3510 canvasWidth
= canvasPtr
->scrollX2
- canvasPtr
->scrollX1
;
3511 canvasHeight
= canvasPtr
->scrollY2
- canvasPtr
->scrollY1
;
3512 if (canvasWidth
< windowWidth
) {
3513 xOrigin
= (canvasPtr
->scrollX1
) - (windowWidth
-canvasWidth
)/2;
3514 } else if (xOrigin
< canvasPtr
->scrollX1
) {
3515 xOrigin
= canvasPtr
->scrollX1
;
3516 } else if (xOrigin
> (canvasPtr
->scrollX2
- windowWidth
)) {
3517 xOrigin
= canvasPtr
->scrollX2
- windowWidth
;
3519 if (canvasHeight
< windowHeight
) {
3520 yOrigin
= (canvasPtr
->scrollY1
) - (windowHeight
-canvasHeight
)/2;
3521 } else if (yOrigin
< canvasPtr
->scrollY1
) {
3522 yOrigin
= canvasPtr
->scrollY1
;
3523 } else if (yOrigin
> (canvasPtr
->scrollY2
- windowHeight
)) {
3524 yOrigin
= canvasPtr
->scrollY2
- windowHeight
;
3528 if ((xOrigin
== canvasPtr
->xOrigin
) && (yOrigin
== canvasPtr
->yOrigin
)) {
3533 * Tricky point: must redisplay not only everything that's visible
3534 * in the window's final configuration, but also everything that was
3535 * visible in the initial configuration. This is needed because some
3536 * item types, like windows, need to know when they move off-screen
3537 * so they can explicitly undisplay themselves.
3540 EventuallyRedrawArea(canvasPtr
, canvasPtr
->xOrigin
, canvasPtr
->yOrigin
,
3541 canvasPtr
->xOrigin
+ Tk_Width(canvasPtr
->tkwin
),
3542 canvasPtr
->yOrigin
+ Tk_Height(canvasPtr
->tkwin
));
3543 canvasPtr
->xOrigin
= xOrigin
;
3544 canvasPtr
->yOrigin
= yOrigin
;
3545 canvasPtr
->flags
|= UPDATE_SCROLLBARS
;
3546 EventuallyRedrawArea(canvasPtr
, canvasPtr
->xOrigin
, canvasPtr
->yOrigin
,
3547 canvasPtr
->xOrigin
+ Tk_Width(canvasPtr
->tkwin
),
3548 canvasPtr
->yOrigin
+ Tk_Height(canvasPtr
->tkwin
));
3552 *--------------------------------------------------------------
3554 * CanvasTagsParseProc --
3556 * This procedure is invoked during option processing to handle
3557 * "-tags" options for canvas items.
3560 * A standard Tcl return value.
3563 * The tags for a given item get replaced by those indicated
3564 * in the value argument.
3566 *--------------------------------------------------------------
3571 CanvasTagsParseProc(
3572 ClientData clientData
, /* Not used.*/
3573 Tcl_Interp
*interp
, /* Used for reporting errors. */
3574 Tk_Window tkwin
, /* Window containing canvas widget. */
3575 char *value
, /* Value of option (list of tag
3577 char *widgRec
, /* Pointer to record for item. */
3578 int offset
/* Offset into item (ignored). */
3581 register Tk_Item
*itemPtr
= (Tk_Item
*) widgRec
;
3587 * Break the value up into the individual tag names.
3590 if (Tcl_SplitList(interp
, value
, &argc
, &argv
) != TCL_OK
) {
3595 * Make sure that there's enough space in the item to hold the
3599 if (itemPtr
->tagSpace
< argc
) {
3600 newPtr
= (Tk_Uid
*) ckalloc((unsigned) (argc
* sizeof(Tk_Uid
)));
3601 for (i
= itemPtr
->numTags
-1; i
>= 0; i
--) {
3602 newPtr
[i
] = itemPtr
->tagPtr
[i
];
3604 if (itemPtr
->tagPtr
!= itemPtr
->staticTagSpace
) {
3605 ckfree((char *) itemPtr
->tagPtr
);
3607 itemPtr
->tagPtr
= newPtr
;
3608 itemPtr
->tagSpace
= argc
;
3610 itemPtr
->numTags
= argc
;
3611 for (i
= 0; i
< argc
; i
++) {
3612 itemPtr
->tagPtr
[i
] = Tk_GetUid(argv
[i
]);
3614 ckfree((char *) argv
);
3619 *--------------------------------------------------------------
3621 * CanvasTagsPrintProc --
3623 * This procedure is invoked by the Tk configuration code
3624 * to produce a printable string for the "-tags" configuration
3625 * option for canvas items.
3628 * The return value is a string describing all the tags for
3629 * the item referred to by "widgRec". In addition, *freeProcPtr
3630 * is filled in with the address of a procedure to call to free
3631 * the result string when it's no longer needed (or NULL to
3632 * indicate that the string doesn't need to be freed).
3637 *--------------------------------------------------------------
3642 CanvasTagsPrintProc(
3643 ClientData clientData
, /* Ignored. */
3644 Tk_Window tkwin
, /* Window containing canvas widget. */
3645 char *widgRec
, /* Pointer to record for item. */
3646 int offset
, /* Ignored. */
3647 Tcl_FreeProc
**freeProcPtr
/* Pointer to variable to fill in with
3648 * information about how to reclaim
3649 * storage for return string. */
3652 register Tk_Item
*itemPtr
= (Tk_Item
*) widgRec
;
3654 if (itemPtr
->numTags
== 0) {
3655 *freeProcPtr
= (Tcl_FreeProc
*) NULL
;
3658 if (itemPtr
->numTags
== 1) {
3659 *freeProcPtr
= (Tcl_FreeProc
*) NULL
;
3660 return (char *) itemPtr
->tagPtr
[0];
3662 *freeProcPtr
= (Tcl_FreeProc
*) free
;
3663 return Tcl_Merge(itemPtr
->numTags
, (char **) itemPtr
->tagPtr
);