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 *--------------------------------------------------------------
255 Tk_CanvasCmd(clientData
, interp
, argc
, argv
)
256 ClientData clientData
; /* Main window associated with
258 Tcl_Interp
*interp
; /* Current interpreter. */
259 int argc
; /* Number of arguments. */
260 char **argv
; /* Argument strings. */
262 Tk_Window tkwin
= (Tk_Window
) clientData
;
263 register Tk_Canvas
*canvasPtr
;
266 if (typeList
== NULL
) {
271 Tcl_AppendResult(interp
, "wrong # args: should be \"",
272 argv
[0], " pathName ?options?\"", (char *) NULL
);
276 new = Tk_CreateWindowFromPath(interp
, tkwin
, argv
[1], (char *) NULL
);
282 * Initialize fields that won't be initialized by ConfigureCanvas,
283 * or which ConfigureCanvas expects to have reasonable values
284 * (e.g. resource pointers).
287 canvasPtr
= (Tk_Canvas
*) ckalloc(sizeof(Tk_Canvas
));
288 canvasPtr
->tkwin
= new;
289 canvasPtr
->interp
= interp
;
290 canvasPtr
->firstItemPtr
= NULL
;
291 canvasPtr
->lastItemPtr
= NULL
;
292 canvasPtr
->bgBorder
= NULL
;
293 canvasPtr
->bgColor
= NULL
;
294 canvasPtr
->pixmapGC
= None
;
295 canvasPtr
->selBorder
= NULL
;
296 canvasPtr
->selFgColorPtr
= NULL
;
297 canvasPtr
->selItemPtr
= NULL
;
298 canvasPtr
->selectFirst
= -1;
299 canvasPtr
->selectLast
= -1;
300 canvasPtr
->cursorBorder
= NULL
;
301 canvasPtr
->cursorBlinkHandler
= (Tk_TimerToken
) NULL
;
302 canvasPtr
->focusItemPtr
= NULL
;
303 canvasPtr
->xOrigin
= canvasPtr
->yOrigin
= 0;
304 canvasPtr
->drawableXOrigin
= canvasPtr
->drawableYOrigin
= 0;
305 canvasPtr
->bindingTable
= NULL
;
306 canvasPtr
->currentItemPtr
= NULL
;
307 canvasPtr
->pickEvent
.type
= LeaveNotify
;
308 canvasPtr
->xScrollCmd
= NULL
;
309 canvasPtr
->yScrollCmd
= NULL
;
310 canvasPtr
->regionString
= NULL
;
311 canvasPtr
->hotPtr
= NULL
;
312 canvasPtr
->cursor
= None
;
313 canvasPtr
->pixelsPerMM
= WidthOfScreen(Tk_Screen(new));
314 canvasPtr
->pixelsPerMM
/= WidthMMOfScreen(Tk_Screen(new));
315 canvasPtr
->flags
= 0;
316 canvasPtr
->nextId
= 1;
317 canvasPtr
->updateTimerToken
= NULL
;
319 Tk_SetClass(canvasPtr
->tkwin
, "Canvas");
320 Tk_CreateEventHandler(canvasPtr
->tkwin
, ExposureMask
|StructureNotifyMask
,
321 CanvasEventProc
, (ClientData
) canvasPtr
);
322 Tk_CreateEventHandler(canvasPtr
->tkwin
, KeyPressMask
|KeyReleaseMask
323 |ButtonPressMask
|ButtonReleaseMask
|EnterWindowMask
324 |LeaveWindowMask
|PointerMotionMask
, CanvasBindProc
,
325 (ClientData
) canvasPtr
);
326 Tk_CreateSelHandler(canvasPtr
->tkwin
, XA_STRING
, CanvasFetchSelection
,
327 (ClientData
) canvasPtr
, XA_STRING
);
328 Tcl_CreateCommand(interp
, Tk_PathName(canvasPtr
->tkwin
), CanvasWidgetCmd
,
329 (ClientData
) canvasPtr
, (void (*)()) NULL
);
330 if (ConfigureCanvas(interp
, canvasPtr
, argc
-2, argv
+2, 0) != TCL_OK
) {
333 Tk_CreateFocusHandler(canvasPtr
->tkwin
, CanvasFocusProc
,
334 (ClientData
) canvasPtr
);
336 interp
->result
= Tk_PathName(canvasPtr
->tkwin
);
340 Tk_DestroyWindow(canvasPtr
->tkwin
);
345 *--------------------------------------------------------------
349 * This procedure is invoked to process the Tcl command
350 * that corresponds to a widget managed by this module.
351 * See the user documentation for details on what it does.
354 * A standard Tcl result.
357 * See the user documentation.
359 *--------------------------------------------------------------
363 CanvasWidgetCmd(clientData
, interp
, argc
, argv
)
364 ClientData clientData
; /* Information about canvas
366 Tcl_Interp
*interp
; /* Current interpreter. */
367 int argc
; /* Number of arguments. */
368 char **argv
; /* Argument strings. */
370 register Tk_Canvas
*canvasPtr
= (Tk_Canvas
*) clientData
;
373 Tk_Item
*itemPtr
= NULL
; /* Initialization needed only to
374 * prevent compiler warning. */
378 Tcl_AppendResult(interp
, "wrong # args: should be \"",
379 argv
[0], " option ?arg arg ...?\"", (char *) NULL
);
382 Tk_Preserve((ClientData
) canvasPtr
);
385 length
= strlen(argv
[1]);
386 if ((c
== 'a') && (strncmp(argv
[1], "addtag", length
) == 0)) {
388 Tcl_AppendResult(interp
, "wrong # args: should be \"",
389 argv
[0], " addtags tag searchCommand ?arg arg ...?\"",
393 result
= FindItems(interp
, canvasPtr
, argc
-3, argv
+3, argv
[2], argv
[0],
395 } else if ((c
== 'b') && (strncmp(argv
[1], "bbox", length
) == 0)
398 int x1
= 0, y1
= 0, x2
= 0, y2
= 0; /* Initializations needed
399 * only to prevent compiler
403 Tcl_AppendResult(interp
, "wrong # args: should be \"",
404 argv
[0], " bbox tagOrId ?tagOrId ...?\"",
409 for (i
= 2; i
< argc
; i
++) {
410 for (itemPtr
= StartTagSearch(canvasPtr
, argv
[i
], &search
);
411 itemPtr
!= NULL
; itemPtr
= NextItem(&search
)) {
419 if (itemPtr
->x1
< x1
) {
422 if (itemPtr
->y1
< y1
) {
425 if (itemPtr
->x2
> x2
) {
428 if (itemPtr
->y2
> y2
) {
435 sprintf(interp
->result
, "%d %d %d %d", x1
, y1
, x2
, y2
);
437 } else if ((c
== 'b') && (strncmp(argv
[1], "bind", length
) == 0)
441 if ((argc
< 3) || (argc
> 5)) {
442 Tcl_AppendResult(interp
, "wrong # args: should be \"",
443 argv
[0], " bind tagOrId ?sequence? ?command?\"",
449 * Figure out what object to use for the binding (individual
454 if (isdigit(argv
[2][0])) {
458 id
= strtoul(argv
[2], &end
, 0);
462 for (itemPtr
= canvasPtr
->firstItemPtr
; itemPtr
!= NULL
;
463 itemPtr
= itemPtr
->nextPtr
) {
464 if (itemPtr
->id
== id
) {
465 object
= (ClientData
) itemPtr
;
470 Tcl_AppendResult(interp
, "item \"", argv
[2],
471 "\" doesn't exist", (char *) NULL
);
476 object
= (ClientData
) Tk_GetUid(argv
[2]);
480 * Make a binding table if the canvas doesn't already have
484 if (canvasPtr
->bindingTable
== NULL
) {
485 canvasPtr
->bindingTable
= Tk_CreateBindingTable(interp
);
492 if (argv
[4][0] == 0) {
493 result
= Tk_DeleteBinding(interp
, canvasPtr
->bindingTable
,
497 if (argv
[4][0] == '+') {
501 mask
= Tk_CreateBinding(interp
, canvasPtr
->bindingTable
,
502 object
, argv
[3], argv
[4], append
);
506 if (mask
& ~(ButtonMotionMask
|Button1MotionMask
|Button2MotionMask
507 |Button3MotionMask
|Button4MotionMask
|Button5MotionMask
508 |ButtonPressMask
|ButtonReleaseMask
|EnterWindowMask
509 |LeaveWindowMask
|KeyPressMask
|KeyReleaseMask
510 |PointerMotionMask
)) {
511 Tk_DeleteBinding(interp
, canvasPtr
->bindingTable
,
513 Tcl_ResetResult(interp
);
514 Tcl_AppendResult(interp
, "requested illegal events; ",
515 "only key, button, motion, and enter/leave ",
516 "events may be used", (char *) NULL
);
519 } else if (argc
== 4) {
522 command
= Tk_GetBinding(interp
, canvasPtr
->bindingTable
,
524 if (command
== NULL
) {
527 interp
->result
= command
;
529 Tk_GetAllBindings(interp
, canvasPtr
->bindingTable
, object
);
531 } else if ((c
== 'c') && (strcmp(argv
[1], "canvasx") == 0)) {
535 if ((argc
< 3) || (argc
> 4)) {
536 Tcl_AppendResult(interp
, "wrong # args: should be \"",
537 argv
[0], " canvasx screenx ?gridspacing?\"",
541 if (Tk_GetPixels(interp
, canvasPtr
->tkwin
, argv
[2], &x
) != TCL_OK
) {
545 if (TkGetCanvasCoord(canvasPtr
, argv
[3], &grid
) != TCL_OK
) {
551 x
+= canvasPtr
->xOrigin
;
552 sprintf(interp
->result
, "%g", GridAlign((double) x
, grid
));
553 } else if ((c
== 'c') && (strcmp(argv
[1], "canvasy") == 0)) {
557 if ((argc
< 3) || (argc
> 4)) {
558 Tcl_AppendResult(interp
, "wrong # args: should be \"",
559 argv
[0], " canvasy screeny ?gridspacing?\"",
563 if (Tk_GetPixels(interp
, canvasPtr
->tkwin
, argv
[2], &y
) != TCL_OK
) {
567 if (TkGetCanvasCoord(canvasPtr
, argv
[3], &grid
) != TCL_OK
) {
573 y
+= canvasPtr
->yOrigin
;
574 sprintf(interp
->result
, "%g", GridAlign((double) y
, grid
));
575 } else if ((c
== 'c') && (strncmp(argv
[1], "configure", length
) == 0)
578 result
= Tk_ConfigureInfo(interp
, canvasPtr
->tkwin
, configSpecs
,
579 (char *) canvasPtr
, (char *) NULL
, 0);
580 } else if (argc
== 3) {
581 result
= Tk_ConfigureInfo(interp
, canvasPtr
->tkwin
, configSpecs
,
582 (char *) canvasPtr
, argv
[2], 0);
584 result
= ConfigureCanvas(interp
, canvasPtr
, argc
-2, argv
+2,
585 TK_CONFIG_ARGV_ONLY
);
587 } else if ((c
== 'c') && (strncmp(argv
[1], "coords", length
) == 0)
590 Tcl_AppendResult(interp
, "wrong # args: should be \"",
591 argv
[0], " coords tagOrId ?x y x y ...?\"",
595 itemPtr
= StartTagSearch(canvasPtr
, argv
[2], &search
);
596 if (itemPtr
!= NULL
) {
598 EventuallyRedrawArea(canvasPtr
, itemPtr
->x1
, itemPtr
->y1
,
599 itemPtr
->x2
, itemPtr
->y2
);
601 if (itemPtr
->typePtr
->coordProc
!= NULL
) {
602 result
= (*itemPtr
->typePtr
->coordProc
)(canvasPtr
, itemPtr
,
606 EventuallyRedrawArea(canvasPtr
, itemPtr
->x1
, itemPtr
->y1
,
607 itemPtr
->x2
, itemPtr
->y2
);
610 } else if ((c
== 'c') && (strncmp(argv
[1], "create", length
) == 0)
612 register Tk_ItemType
*typePtr
;
613 Tk_ItemType
*matchPtr
= NULL
;
614 register Tk_Item
*itemPtr
;
617 Tcl_AppendResult(interp
, "wrong # args: should be \"",
618 argv
[0], " create type ?arg arg ...?\"", (char *) NULL
);
622 length
= strlen(argv
[2]);
623 for (typePtr
= typeList
; typePtr
!= NULL
; typePtr
= typePtr
->nextPtr
) {
624 if ((c
== typePtr
->name
[0])
625 && (strncmp(argv
[2], typePtr
->name
, length
) == 0)) {
626 if (matchPtr
!= NULL
) {
628 Tcl_AppendResult(interp
,
629 "unknown or ambiguous item type \"",
630 argv
[2], "\"", (char *) NULL
);
636 if (matchPtr
== NULL
) {
640 itemPtr
= (Tk_Item
*) ckalloc((unsigned) typePtr
->itemSize
);
641 itemPtr
->id
= canvasPtr
->nextId
;
643 itemPtr
->tagPtr
= itemPtr
->staticTagSpace
;
644 itemPtr
->tagSpace
= TK_TAG_SPACE
;
645 itemPtr
->numTags
= 0;
646 itemPtr
->typePtr
= typePtr
;
647 if ((*typePtr
->createProc
)(canvasPtr
, itemPtr
, argc
-3, argv
+3)
649 ckfree((char *) itemPtr
);
652 itemPtr
->nextPtr
= NULL
;
653 canvasPtr
->hotPtr
= itemPtr
;
654 canvasPtr
->hotPrevPtr
= canvasPtr
->lastItemPtr
;
655 if (canvasPtr
->lastItemPtr
== NULL
) {
656 canvasPtr
->firstItemPtr
= itemPtr
;
658 canvasPtr
->lastItemPtr
->nextPtr
= itemPtr
;
660 canvasPtr
->lastItemPtr
= itemPtr
;
661 EventuallyRedrawArea(canvasPtr
, itemPtr
->x1
, itemPtr
->y1
,
662 itemPtr
->x2
, itemPtr
->y2
);
663 canvasPtr
->flags
|= REPICK_NEEDED
;
664 sprintf(interp
->result
, "%d", itemPtr
->id
);
665 } else if ((c
== 'c') && (strncmp(argv
[1], "cursor", length
) == 0)
670 Tcl_AppendResult(interp
, "wrong # args: should be \"",
671 argv
[0], " cursor tagOrId index\"",
675 for (itemPtr
= StartTagSearch(canvasPtr
, argv
[2], &search
);
676 itemPtr
!= NULL
; itemPtr
= NextItem(&search
)) {
677 if ((itemPtr
->typePtr
->indexProc
== NULL
)
678 || (itemPtr
->typePtr
->cursorProc
== NULL
)) {
681 if ((*itemPtr
->typePtr
->indexProc
)(canvasPtr
, itemPtr
,
682 argv
[3], &index
) != TCL_OK
) {
685 (*itemPtr
->typePtr
->cursorProc
)(canvasPtr
, itemPtr
, index
);
686 if ((itemPtr
== canvasPtr
->focusItemPtr
)
687 && (canvasPtr
->flags
& CURSOR_ON
)) {
688 EventuallyRedrawArea(canvasPtr
, itemPtr
->x1
, itemPtr
->y1
,
689 itemPtr
->x2
, itemPtr
->y2
);
692 } else if ((c
== 'd') && (strncmp(argv
[1], "dchars", length
) == 0)
696 if ((argc
!= 4) && (argc
!= 5)) {
697 Tcl_AppendResult(interp
, "wrong # args: should be \"",
698 argv
[0], " dchars tagOrId first ?last?\"",
702 for (itemPtr
= StartTagSearch(canvasPtr
, argv
[2], &search
);
703 itemPtr
!= NULL
; itemPtr
= NextItem(&search
)) {
704 if ((itemPtr
->typePtr
->indexProc
== NULL
)
705 || (itemPtr
->typePtr
->dCharsProc
== NULL
)) {
708 if ((*itemPtr
->typePtr
->indexProc
)(canvasPtr
, itemPtr
,
709 argv
[3], &first
) != TCL_OK
) {
713 if ((*itemPtr
->typePtr
->indexProc
)(canvasPtr
, itemPtr
,
714 argv
[4], &last
) != TCL_OK
) {
722 * Redraw both item's old and new areas: it's possible
723 * that a delete could result in a new area larger than
727 EventuallyRedrawArea(canvasPtr
, itemPtr
->x1
, itemPtr
->y1
,
728 itemPtr
->x2
, itemPtr
->y2
);
729 result
= (*itemPtr
->typePtr
->dCharsProc
)(canvasPtr
, itemPtr
,
731 EventuallyRedrawArea(canvasPtr
, itemPtr
->x1
, itemPtr
->y1
,
732 itemPtr
->x2
, itemPtr
->y2
);
733 if (result
!= TCL_OK
) {
737 } else if ((c
== 'd') && (strncmp(argv
[1], "delete", length
) == 0)
740 Tcl_AppendResult(interp
, "wrong # args: should be \"",
741 argv
[0], " delete tagOrId\"",
745 for (itemPtr
= StartTagSearch(canvasPtr
, argv
[2], &search
);
746 itemPtr
!= NULL
; itemPtr
= NextItem(&search
)) {
747 EventuallyRedrawArea(canvasPtr
, itemPtr
->x1
, itemPtr
->y1
,
748 itemPtr
->x2
, itemPtr
->y2
);
749 (*itemPtr
->typePtr
->deleteProc
)(itemPtr
);
750 if (itemPtr
->tagPtr
!= itemPtr
->staticTagSpace
) {
751 ckfree((char *) itemPtr
->tagPtr
);
753 if (search
.prevPtr
== NULL
) {
754 canvasPtr
->firstItemPtr
= itemPtr
->nextPtr
;
755 if (canvasPtr
->firstItemPtr
== NULL
) {
756 canvasPtr
->lastItemPtr
= NULL
;
759 search
.prevPtr
->nextPtr
= itemPtr
->nextPtr
;
761 if (canvasPtr
->lastItemPtr
== itemPtr
) {
762 canvasPtr
->lastItemPtr
= search
.prevPtr
;
764 ckfree((char *) itemPtr
);
765 if (itemPtr
== canvasPtr
->currentItemPtr
) {
766 canvasPtr
->currentItemPtr
= NULL
;
767 canvasPtr
->flags
|= REPICK_NEEDED
;
769 if (itemPtr
== canvasPtr
->focusItemPtr
) {
770 canvasPtr
->focusItemPtr
= NULL
;
772 if (itemPtr
== canvasPtr
->selItemPtr
) {
773 canvasPtr
->selItemPtr
= NULL
;
775 if ((itemPtr
== canvasPtr
->hotPtr
)
776 || (itemPtr
= canvasPtr
->hotPrevPtr
)) {
777 canvasPtr
->hotPtr
= NULL
;
780 } else if ((c
== 'd') && (strncmp(argv
[1], "dtag", length
) == 0)
785 if ((argc
!= 3) && (argc
!= 4)) {
786 Tcl_AppendResult(interp
, "wrong # args: should be \"",
787 argv
[0], " dtag tagOrId ?tagToDelete?\"",
792 tag
= Tk_GetUid(argv
[3]);
794 tag
= Tk_GetUid(argv
[2]);
796 for (itemPtr
= StartTagSearch(canvasPtr
, argv
[2], &search
);
797 itemPtr
!= NULL
; itemPtr
= NextItem(&search
)) {
798 for (i
= itemPtr
->numTags
-1; i
>= 0; i
--) {
799 if (itemPtr
->tagPtr
[i
] == tag
) {
800 itemPtr
->tagPtr
[i
] = itemPtr
->tagPtr
[itemPtr
->numTags
-1];
805 } else if ((c
== 'f') && (strncmp(argv
[1], "find", length
) == 0)
808 Tcl_AppendResult(interp
, "wrong # args: should be \"",
809 argv
[0], " find searchCommand ?arg arg ...?\"",
813 result
= FindItems(interp
, canvasPtr
, argc
-2, argv
+2, (char *) NULL
,
815 } else if ((c
== 'f') && (strncmp(argv
[1], "focus", length
) == 0)
818 Tcl_AppendResult(interp
, "wrong # args: should be \"",
819 argv
[0], " focus ?tagOrId?\"",
823 itemPtr
= canvasPtr
->focusItemPtr
;
825 if (itemPtr
!= NULL
) {
826 sprintf(interp
->result
, "%d", itemPtr
->id
);
830 if ((itemPtr
!= NULL
) && (canvasPtr
->flags
& GOT_FOCUS
)) {
831 EventuallyRedrawArea(canvasPtr
, itemPtr
->x1
, itemPtr
->y1
,
832 itemPtr
->x2
, itemPtr
->y2
);
834 if (argv
[2][0] == 0) {
835 canvasPtr
->focusItemPtr
= NULL
;
838 for (itemPtr
= StartTagSearch(canvasPtr
, argv
[2], &search
);
839 itemPtr
!= NULL
; itemPtr
= NextItem(&search
)) {
840 if (itemPtr
->typePtr
->cursorProc
!= NULL
) {
844 if (itemPtr
== NULL
) {
847 canvasPtr
->focusItemPtr
= itemPtr
;
848 if (canvasPtr
->flags
& GOT_FOCUS
) {
849 EventuallyRedrawArea(canvasPtr
, itemPtr
->x1
, itemPtr
->y1
,
850 itemPtr
->x2
, itemPtr
->y2
);
852 } else if ((c
== 'g') && (strncmp(argv
[1], "gettags", length
) == 0)) {
854 Tcl_AppendResult(interp
, "wrong # args: should be \"",
855 argv
[0], " gettags tagOrId\"", (char *) NULL
);
858 itemPtr
= StartTagSearch(canvasPtr
, argv
[2], &search
);
859 if (itemPtr
!= NULL
) {
861 for (i
= 0; i
< itemPtr
->numTags
; i
++) {
862 Tcl_AppendElement(interp
, (char *) itemPtr
->tagPtr
[i
], 0);
865 } else if ((c
== 'i') && (strncmp(argv
[1], "index", length
) == 0)
870 Tcl_AppendResult(interp
, "wrong # args: should be \"",
871 argv
[0], " index tagOrId string\"",
875 for (itemPtr
= StartTagSearch(canvasPtr
, argv
[2], &search
);
876 itemPtr
!= NULL
; itemPtr
= NextItem(&search
)) {
877 if (itemPtr
->typePtr
->indexProc
!= NULL
) {
881 if (itemPtr
== NULL
) {
882 Tcl_AppendResult(interp
, "can't find an indexable item \"",
883 argv
[2], "\"", (char *) NULL
);
886 if ((*itemPtr
->typePtr
->indexProc
)(canvasPtr
, itemPtr
,
887 argv
[3], &index
) != TCL_OK
) {
890 sprintf(interp
->result
, "%d", index
);
891 } else if ((c
== 'i') && (strncmp(argv
[1], "insert", length
) == 0)
896 Tcl_AppendResult(interp
, "wrong # args: should be \"",
897 argv
[0], " insert tagOrId beforeThis string\"",
901 for (itemPtr
= StartTagSearch(canvasPtr
, argv
[2], &search
);
902 itemPtr
!= NULL
; itemPtr
= NextItem(&search
)) {
903 if ((itemPtr
->typePtr
->indexProc
== NULL
)
904 || (itemPtr
->typePtr
->insertProc
== NULL
)) {
907 if ((*itemPtr
->typePtr
->indexProc
)(canvasPtr
, itemPtr
,
908 argv
[3], &beforeThis
) != TCL_OK
) {
913 * Redraw both item's old and new areas: it's possible
914 * that an insertion could result in a new area either
915 * larger or smaller than the old area.
918 EventuallyRedrawArea(canvasPtr
, itemPtr
->x1
, itemPtr
->y1
,
919 itemPtr
->x2
, itemPtr
->y2
);
920 result
= (*itemPtr
->typePtr
->insertProc
)(canvasPtr
, itemPtr
,
921 beforeThis
, argv
[4]);
922 EventuallyRedrawArea(canvasPtr
, itemPtr
->x1
, itemPtr
->y1
,
923 itemPtr
->x2
, itemPtr
->y2
);
924 if (result
!= TCL_OK
) {
928 } else if ((c
== 'i') && (strncmp(argv
[1], "itemconfigure", length
) == 0)
931 Tcl_AppendResult(interp
, "wrong # args: should be \"",
932 argv
[0], " itemconfigure tagOrId ?option value ...?\"",
936 for (itemPtr
= StartTagSearch(canvasPtr
, argv
[2], &search
);
937 itemPtr
!= NULL
; itemPtr
= NextItem(&search
)) {
939 result
= Tk_ConfigureInfo(canvasPtr
->interp
, canvasPtr
->tkwin
,
940 itemPtr
->typePtr
->configSpecs
, (char *) itemPtr
,
942 } else if (argc
== 4) {
943 result
= Tk_ConfigureInfo(canvasPtr
->interp
, canvasPtr
->tkwin
,
944 itemPtr
->typePtr
->configSpecs
, (char *) itemPtr
,
947 EventuallyRedrawArea(canvasPtr
, itemPtr
->x1
, itemPtr
->y1
,
948 itemPtr
->x2
, itemPtr
->y2
);
949 result
= (*itemPtr
->typePtr
->configProc
)(canvasPtr
, itemPtr
,
950 argc
-3, argv
+3, TK_CONFIG_ARGV_ONLY
);
951 EventuallyRedrawArea(canvasPtr
, itemPtr
->x1
, itemPtr
->y1
,
952 itemPtr
->x2
, itemPtr
->y2
);
953 canvasPtr
->flags
|= REPICK_NEEDED
;
955 if ((result
!= TCL_OK
) || (argc
< 5)) {
959 } else if ((c
== 'l') && (strncmp(argv
[1], "lower", length
) == 0)) {
962 if ((argc
!= 3) && (argc
!= 4)) {
963 Tcl_AppendResult(interp
, "wrong # args: should be \"",
964 argv
[0], " lower tagOrId ?belowThis?\"",
970 * First find the item just after which we'll insert the
977 prevPtr
= StartTagSearch(canvasPtr
, argv
[3], &search
);
978 if (prevPtr
!= NULL
) {
979 prevPtr
= search
.prevPtr
;
981 Tcl_AppendResult(interp
, "tag \"", argv
[3],
982 "\" doesn't match any items", (char *) NULL
);
986 RelinkItems(canvasPtr
, argv
[2], prevPtr
);
987 } else if ((c
== 'm') && (strncmp(argv
[1], "move", length
) == 0)) {
988 double xAmount
, yAmount
;
991 Tcl_AppendResult(interp
, "wrong # args: should be \"",
992 argv
[0], " move tagOrId xAmount yAmount\"",
996 if ((TkGetCanvasCoord(canvasPtr
, argv
[3], &xAmount
) != TCL_OK
)
997 || (TkGetCanvasCoord(canvasPtr
, argv
[4], &yAmount
) != TCL_OK
)) {
1000 for (itemPtr
= StartTagSearch(canvasPtr
, argv
[2], &search
);
1001 itemPtr
!= NULL
; itemPtr
= NextItem(&search
)) {
1002 EventuallyRedrawArea(canvasPtr
, itemPtr
->x1
, itemPtr
->y1
,
1003 itemPtr
->x2
, itemPtr
->y2
);
1004 (void) (*itemPtr
->typePtr
->translateProc
)(canvasPtr
, itemPtr
,
1006 EventuallyRedrawArea(canvasPtr
, itemPtr
->x1
, itemPtr
->y1
,
1007 itemPtr
->x2
, itemPtr
->y2
);
1008 canvasPtr
->flags
|= REPICK_NEEDED
;
1010 } else if ((c
== 'r') && (strncmp(argv
[1], "raise", length
) == 0)) {
1013 if ((argc
!= 3) && (argc
!= 4)) {
1014 Tcl_AppendResult(interp
, "wrong # args: should be \"",
1015 argv
[0], " raise tagOrId ?aboveThis?\"",
1021 * First find the item just after which we'll insert the
1026 prevPtr
= canvasPtr
->lastItemPtr
;
1029 for (itemPtr
= StartTagSearch(canvasPtr
, argv
[3], &search
);
1030 itemPtr
!= NULL
; itemPtr
= NextItem(&search
)) {
1033 if (prevPtr
== NULL
) {
1034 Tcl_AppendResult(interp
, "tagOrId \"", argv
[3],
1035 "\" doesn't match any items", (char *) NULL
);
1039 RelinkItems(canvasPtr
, argv
[2], prevPtr
);
1040 #if defined(USE_XPM3)
1041 } else if ((c
== 's') && (strncmp(argv
[1], "save", length
) == 0)
1043 if (argc
!= 3 && argc
!= 7) {
1044 Tcl_AppendResult(interp
, "wrong # args: should be \"",
1045 argv
[0], " save fileName ?x y width height?\"",
1050 if (SaveCanvas(interp
, canvasPtr
, argv
[2], 0, 0, 0, 0) != TCL_OK
) {
1054 if (SaveCanvas(interp
, canvasPtr
, argv
[2], atol(argv
[3]),
1055 atol(argv
[4]), atol(argv
[5]), atol(argv
[6]))) {
1060 } else if ((c
== 's') && (strncmp(argv
[1], "scale", length
) == 0)
1062 double xOrigin
, yOrigin
, xScale
, yScale
;
1065 Tcl_AppendResult(interp
, "wrong # args: should be \"",
1066 argv
[0], " scale tagOrId xOrigin yOrigin xScale yScale\"",
1070 if ((TkGetCanvasCoord(canvasPtr
, argv
[3], &xOrigin
) != TCL_OK
)
1071 || (TkGetCanvasCoord(canvasPtr
, argv
[4], &yOrigin
) != TCL_OK
)
1072 || (Tcl_GetDouble(interp
, argv
[5], &xScale
) != TCL_OK
)
1073 || (Tcl_GetDouble(interp
, argv
[6], &yScale
) != TCL_OK
)) {
1076 if ((xScale
<= 0.0) || (yScale
<= 0.0)) {
1077 interp
->result
= "scale factors must be greater than zero";
1080 for (itemPtr
= StartTagSearch(canvasPtr
, argv
[2], &search
);
1081 itemPtr
!= NULL
; itemPtr
= NextItem(&search
)) {
1082 EventuallyRedrawArea(canvasPtr
, itemPtr
->x1
, itemPtr
->y1
,
1083 itemPtr
->x2
, itemPtr
->y2
);
1084 (void) (*itemPtr
->typePtr
->scaleProc
)(canvasPtr
, itemPtr
,
1085 xOrigin
, yOrigin
, xScale
, yScale
);
1086 EventuallyRedrawArea(canvasPtr
, itemPtr
->x1
, itemPtr
->y1
,
1087 itemPtr
->x2
, itemPtr
->y2
);
1088 canvasPtr
->flags
|= REPICK_NEEDED
;
1090 } else if ((c
== 's') && (strncmp(argv
[1], "scan", length
) == 0)
1095 Tcl_AppendResult(interp
, "wrong # args: should be \"",
1096 argv
[0], " scan mark|dragto x y\"", (char *) NULL
);
1099 if ((Tcl_GetInt(interp
, argv
[3], &x
) != TCL_OK
)
1100 || (Tcl_GetInt(interp
, argv
[4], &y
) != TCL_OK
)){
1103 if ((argv
[2][0] == 'm')
1104 && (strncmp(argv
[2], "mark", strlen(argv
[2])) == 0)) {
1105 canvasPtr
->scanX
= x
;
1106 canvasPtr
->scanXOrigin
= canvasPtr
->xOrigin
;
1107 canvasPtr
->scanY
= y
;
1108 canvasPtr
->scanYOrigin
= canvasPtr
->yOrigin
;
1109 } else if ((argv
[2][0] == 'd')
1110 && (strncmp(argv
[2], "dragto", strlen(argv
[2])) == 0)) {
1111 int newXOrigin
, newYOrigin
, tmp
;
1114 * Compute a new view origin for the canvas, amplifying the
1115 * mouse motion and rounding to the nearest multiple of the
1119 tmp
= canvasPtr
->scanXOrigin
- 10*(x
- canvasPtr
->scanX
)
1120 - canvasPtr
->scrollX1
;
1122 tmp
= (tmp
+ canvasPtr
->scrollIncrement
/2)
1123 /canvasPtr
->scrollIncrement
;
1125 tmp
= -(((-tmp
) + canvasPtr
->scrollIncrement
/2)
1126 /canvasPtr
->scrollIncrement
);
1128 newXOrigin
= canvasPtr
->scrollX1
+ tmp
*canvasPtr
->scrollIncrement
;
1129 tmp
= canvasPtr
->scanYOrigin
- 10*(y
- canvasPtr
->scanY
)
1130 - canvasPtr
->scrollY1
;
1132 tmp
= (tmp
+ canvasPtr
->scrollIncrement
/2)
1133 /canvasPtr
->scrollIncrement
;
1135 tmp
= -(((-tmp
) + canvasPtr
->scrollIncrement
/2)
1136 /canvasPtr
->scrollIncrement
);
1138 newYOrigin
= canvasPtr
->scrollY1
+ tmp
*canvasPtr
->scrollIncrement
;
1139 CanvasSetOrigin(canvasPtr
, newXOrigin
, newYOrigin
);
1141 Tcl_AppendResult(interp
, "bad scan option \"", argv
[2],
1142 "\": must be mark or dragto", (char *) NULL
);
1145 } else if ((c
== 's') && (strncmp(argv
[1], "select", length
) == 0)
1150 Tcl_AppendResult(interp
, "wrong # args: should be \"",
1151 argv
[0], " select option ?tagOrId? ?arg?\"", (char *) NULL
);
1155 for (itemPtr
= StartTagSearch(canvasPtr
, argv
[3], &search
);
1156 itemPtr
!= NULL
; itemPtr
= NextItem(&search
)) {
1157 if ((itemPtr
->typePtr
->indexProc
!= NULL
)
1158 && (itemPtr
->typePtr
->selectionProc
!= NULL
)){
1162 if (itemPtr
== NULL
) {
1163 Tcl_AppendResult(interp
,
1164 "can't find an indexable and selectable item \"",
1165 argv
[3], "\"", (char *) NULL
);
1170 if ((*itemPtr
->typePtr
->indexProc
)(canvasPtr
, itemPtr
,
1171 argv
[4], &index
) != TCL_OK
) {
1175 length
= strlen(argv
[2]);
1177 if ((c
== 'a') && (strncmp(argv
[2], "adjust", length
) == 0)) {
1179 Tcl_AppendResult(interp
, "wrong # args: should be \"",
1180 argv
[0], " select adjust tagOrId index\"",
1184 if (canvasPtr
->selItemPtr
== itemPtr
) {
1185 if (index
< (canvasPtr
->selectFirst
1186 + canvasPtr
->selectLast
)/2) {
1187 canvasPtr
->selectAnchor
= canvasPtr
->selectLast
+ 1;
1189 canvasPtr
->selectAnchor
= canvasPtr
->selectFirst
;
1192 CanvasSelectTo(canvasPtr
, itemPtr
, index
);
1193 } else if ((c
== 'c') && (argv
[2] != NULL
)
1194 && (strncmp(argv
[2], "clear", length
) == 0)) {
1196 Tcl_AppendResult(interp
, "wrong # args: should be \"",
1197 argv
[0], " select clear\"", (char *) NULL
);
1200 if (canvasPtr
->selItemPtr
!= NULL
) {
1201 EventuallyRedrawArea(canvasPtr
, canvasPtr
->selItemPtr
->x1
,
1202 canvasPtr
->selItemPtr
->y1
, canvasPtr
->selItemPtr
->x2
,
1203 canvasPtr
->selItemPtr
->y2
);
1204 canvasPtr
->selItemPtr
= NULL
;
1207 } else if ((c
== 'f') && (strncmp(argv
[2], "from", length
) == 0)) {
1209 Tcl_AppendResult(interp
, "wrong # args: should be \"",
1210 argv
[0], " select from tagOrId index\"",
1214 canvasPtr
->anchorItemPtr
= itemPtr
;
1215 canvasPtr
->selectAnchor
= index
;
1216 } else if ((c
== 'i') && (strncmp(argv
[2], "item", length
) == 0)) {
1218 Tcl_AppendResult(interp
, "wrong # args: should be \"",
1219 argv
[0], " select item\"", (char *) NULL
);
1222 if (canvasPtr
->selItemPtr
!= NULL
) {
1223 sprintf(interp
->result
, "%d", canvasPtr
->selItemPtr
->id
);
1225 } else if ((c
== 't') && (strncmp(argv
[2], "to", length
) == 0)) {
1227 Tcl_AppendResult(interp
, "wrong # args: should be \"",
1228 argv
[0], " select to tagOrId index\"",
1232 CanvasSelectTo(canvasPtr
, itemPtr
, index
);
1234 Tcl_AppendResult(interp
, "bad select option \"", argv
[2],
1235 "\": must be adjust, clear, from, item, or to",
1239 } else if ((c
== 't') && (strncmp(argv
[1], "type", length
) == 0)) {
1241 Tcl_AppendResult(interp
, "wrong # args: should be \"",
1242 argv
[0], " type tag\"", (char *) NULL
);
1245 itemPtr
= StartTagSearch(canvasPtr
, argv
[2], &search
);
1246 if (itemPtr
!= NULL
) {
1247 interp
->result
= itemPtr
->typePtr
->name
;
1249 } else if ((c
== 'x') && (strncmp(argv
[1], "xview", length
) == 0)) {
1253 Tcl_AppendResult(interp
, "wrong # args: should be \"",
1254 argv
[0], " xview index\"", (char *) NULL
);
1257 if (Tcl_GetInt(canvasPtr
->interp
, argv
[2], &index
) != TCL_OK
) {
1260 CanvasSetOrigin(canvasPtr
,
1261 (canvasPtr
->scrollX1
+ index
*canvasPtr
->scrollIncrement
),
1262 canvasPtr
->yOrigin
);
1263 } else if ((c
== 'y') && (strncmp(argv
[1], "yview", length
) == 0)) {
1267 Tcl_AppendResult(interp
, "wrong # args: should be \"",
1268 argv
[0], " yview index\"", (char *) NULL
);
1271 if (Tcl_GetInt(canvasPtr
->interp
, argv
[2], &index
) != TCL_OK
) {
1274 CanvasSetOrigin(canvasPtr
, canvasPtr
->xOrigin
,
1275 (canvasPtr
->scrollY1
+ index
*canvasPtr
->scrollIncrement
));
1277 Tcl_AppendResult(interp
, "bad option \"", argv
[1],
1278 "\": must be addtag, bbox, bind, ",
1279 "canvasx, canvasy, configure, coords, create, ",
1280 "cursor, dchars, delete, dtag, find, focus, ",
1281 "gettags, index, insert, itemconfigure, lower, ",
1282 "move, raise, scale, scan, select, type, xview, or yview",
1287 Tk_Release((ClientData
) canvasPtr
);
1291 Tk_Release((ClientData
) canvasPtr
);
1296 *----------------------------------------------------------------------
1300 * This procedure is invoked by Tk_EventuallyFree or Tk_Release
1301 * to clean up the internal structure of a canvas at a safe time
1302 * (when no-one is using it anymore).
1308 * Everything associated with the canvas is freed up.
1310 *----------------------------------------------------------------------
1314 DestroyCanvas(clientData
)
1315 ClientData clientData
; /* Info about canvas widget. */
1317 register Tk_Canvas
*canvasPtr
= (Tk_Canvas
*) clientData
;
1318 register Tk_Item
*itemPtr
;
1320 for (itemPtr
= canvasPtr
->firstItemPtr
; itemPtr
!= NULL
;
1321 itemPtr
= canvasPtr
->firstItemPtr
) {
1322 canvasPtr
->firstItemPtr
= itemPtr
->nextPtr
;
1323 (*itemPtr
->typePtr
->deleteProc
)(itemPtr
);
1324 if (itemPtr
->tagPtr
!= itemPtr
->staticTagSpace
) {
1325 ckfree((char *) itemPtr
->tagPtr
);
1327 ckfree((char *) itemPtr
);
1330 if (canvasPtr
->bgBorder
!= NULL
) {
1331 Tk_Free3DBorder(canvasPtr
->bgBorder
);
1333 if (canvasPtr
->bgColor
!= NULL
) {
1334 Tk_FreeColor(canvasPtr
->bgColor
);
1336 if (canvasPtr
->pixmapGC
!= None
) {
1337 Tk_FreeGC(canvasPtr
->pixmapGC
);
1339 if (canvasPtr
->selBorder
!= NULL
) {
1340 Tk_Free3DBorder(canvasPtr
->selBorder
);
1342 if (canvasPtr
->selFgColorPtr
!= NULL
) {
1343 Tk_FreeColor(canvasPtr
->selFgColorPtr
);
1345 if (canvasPtr
->cursorBorder
!= NULL
) {
1346 Tk_Free3DBorder(canvasPtr
->cursorBorder
);
1348 Tk_DeleteTimerHandler(canvasPtr
->cursorBlinkHandler
);
1349 if (canvasPtr
->bindingTable
!= NULL
) {
1350 Tk_DeleteBindingTable(canvasPtr
->bindingTable
);
1352 if (canvasPtr
->xScrollCmd
!= NULL
) {
1353 ckfree(canvasPtr
->xScrollCmd
);
1355 if (canvasPtr
->yScrollCmd
!= NULL
) {
1356 ckfree(canvasPtr
->yScrollCmd
);
1358 if (canvasPtr
->regionString
!= NULL
) {
1359 ckfree(canvasPtr
->regionString
);
1361 if (canvasPtr
->cursor
!= None
) {
1362 Tk_FreeCursor(canvasPtr
->cursor
);
1364 ckfree((char *) canvasPtr
);
1368 *----------------------------------------------------------------------
1370 * ConfigureCanvas --
1372 * This procedure is called to process an argv/argc list, plus
1373 * the Tk option database, in order to configure (or
1374 * reconfigure) a canvas widget.
1377 * The return value is a standard Tcl result. If TCL_ERROR is
1378 * returned, then interp->result contains an error message.
1381 * Configuration information, such as colors, border width,
1382 * etc. get set for canvasPtr; old resources get freed,
1383 * if there were any.
1385 *----------------------------------------------------------------------
1389 ConfigureCanvas(interp
, canvasPtr
, argc
, argv
, flags
)
1390 Tcl_Interp
*interp
; /* Used for error reporting. */
1391 register Tk_Canvas
*canvasPtr
; /* Information about widget; may or may
1392 * not already have values for some fields. */
1393 int argc
; /* Number of valid entries in argv. */
1394 char **argv
; /* Arguments. */
1395 int flags
; /* Flags to pass to Tk_ConfigureWidget. */
1400 if (Tk_ConfigureWidget(interp
, canvasPtr
->tkwin
, configSpecs
,
1401 argc
, argv
, (char *) canvasPtr
, flags
) != TCL_OK
) {
1406 * A few options need special processing, such as setting the
1407 * background from a 3-D border and creating a GC for copying
1408 * bits to the screen.
1411 Tk_SetBackgroundFromBorder(canvasPtr
->tkwin
, canvasPtr
->bgBorder
);
1413 gcValues
.function
= GXcopy
;
1414 gcValues
.foreground
= canvasPtr
->bgColor
->pixel
;
1415 gcValues
.graphics_exposures
= False
;
1416 new = Tk_GetGC(canvasPtr
->tkwin
,
1417 GCFunction
|GCForeground
|GCGraphicsExposures
, &gcValues
);
1418 if (canvasPtr
->pixmapGC
!= None
) {
1419 Tk_FreeGC(canvasPtr
->pixmapGC
);
1421 canvasPtr
->pixmapGC
= new;
1424 * Reset the desired dimensions for the window.
1427 Tk_GeometryRequest(canvasPtr
->tkwin
, canvasPtr
->width
, canvasPtr
->height
);
1430 * Restart the cursor timing sequence in case the on-time or off-time
1434 if (canvasPtr
->flags
& GOT_FOCUS
) {
1435 CanvasFocusProc((ClientData
) canvasPtr
, 1);
1439 * Recompute the scroll region.
1442 canvasPtr
->scrollX1
= 0;
1443 canvasPtr
->scrollY1
= 0;
1444 canvasPtr
->scrollX2
= 0;
1445 canvasPtr
->scrollY2
= 0;
1446 if (canvasPtr
->regionString
!= NULL
) {
1450 if (Tcl_SplitList(canvasPtr
->interp
, canvasPtr
->regionString
,
1451 &argc2
, &argv2
) != TCL_OK
) {
1456 Tcl_AppendResult(interp
, "bad scrollRegion \"",
1457 canvasPtr
->regionString
, "\"", (char *) NULL
);
1458 ckfree(canvasPtr
->regionString
);
1459 ckfree((char *) argv2
);
1460 canvasPtr
->regionString
= NULL
;
1463 if ((Tk_GetPixels(canvasPtr
->interp
, canvasPtr
->tkwin
,
1464 argv2
[0], &canvasPtr
->scrollX1
) != TCL_OK
)
1465 || (Tk_GetPixels(canvasPtr
->interp
, canvasPtr
->tkwin
,
1466 argv2
[1], &canvasPtr
->scrollY1
) != TCL_OK
)
1467 || (Tk_GetPixels(canvasPtr
->interp
, canvasPtr
->tkwin
,
1468 argv2
[2], &canvasPtr
->scrollX2
) != TCL_OK
)
1469 || (Tk_GetPixels(canvasPtr
->interp
, canvasPtr
->tkwin
,
1470 argv2
[3], &canvasPtr
->scrollY2
) != TCL_OK
)) {
1473 ckfree((char *) argv2
);
1477 * Reset the canvases origin (this is a no-op unless confine
1478 * mode has just been turned on or the scroll region has changed).
1481 CanvasSetOrigin(canvasPtr
, canvasPtr
->xOrigin
, canvasPtr
->yOrigin
);
1482 canvasPtr
->flags
|= UPDATE_SCROLLBARS
;
1483 EventuallyRedrawArea(canvasPtr
, canvasPtr
->xOrigin
, canvasPtr
->yOrigin
,
1484 canvasPtr
->xOrigin
+ Tk_Width(canvasPtr
->tkwin
),
1485 canvasPtr
->yOrigin
+ Tk_Height(canvasPtr
->tkwin
));
1489 #if defined(USE_XPM3)
1490 //#include "xpmtk.h"
1493 *--------------------------------------------------------------
1497 * This procedure saves the contents of a canvas window.
1500 * The return value is a standard Tcl result. If TCL_ERROR is
1501 * returned, then interp->result contains an error message.
1504 * A pixmap is written to a file.
1506 *--------------------------------------------------------------
1510 SaveCanvas(interp
, canvasPtr
, fileName
, x
, y
, width
, height
)
1511 Tcl_Interp
*interp
; /* Used for error reporting. */
1512 register Tk_Canvas
*canvasPtr
; /* Information about widget */
1513 char *fileName
; /* the output file name. */
1514 int x
; /* upper left x coordinate. */
1515 int y
; /* upper left y coordinate. */
1516 unsigned int width
; /* width of pixmap area to save. */
1517 unsigned int height
; /* height of pixmap area to save. */
1519 register Tk_Window tkwin
= canvasPtr
->tkwin
;
1520 register Tk_Item
*itemPtr
;
1523 int screenX1
, screenX2
, screenY1
, screenY2
;
1524 XpmAttributes xpm_attributes
;
1526 if (canvasPtr
->tkwin
== NULL
) {
1529 if (!Tk_IsMapped(tkwin
)) {
1532 if (!(fileName
&& *fileName
)) {
1533 Tcl_ResetResult(interp
);
1534 Tcl_AppendResult(interp
, "no filename specified for canvas saving",
1540 * Choose a new current item if that is needed (this could cause
1541 * event handlers to be invoked).
1544 while (canvasPtr
->flags
& REPICK_NEEDED
) {
1545 Tk_Preserve((ClientData
) canvasPtr
);
1546 canvasPtr
->flags
&= ~REPICK_NEEDED
;
1547 PickCurrentItem(canvasPtr
, &canvasPtr
->pickEvent
);
1548 tkwin
= canvasPtr
->tkwin
;
1549 Tk_Release((ClientData
) canvasPtr
);
1550 if (tkwin
== NULL
) {
1555 if(x
== 0 && y
== 0 && width
== 0 && height
== 0) {
1558 screenX2
= Tk_Width(tkwin
);
1559 screenY2
= Tk_Height(tkwin
);
1560 width
= Tk_Width(tkwin
);
1561 height
= Tk_Height(tkwin
);
1563 if(width
!= 0 && height
!= 0) {
1566 screenX2
= x
+ width
;
1567 screenY2
= y
+ height
;
1569 Tcl_ResetResult(interp
);
1570 Tcl_AppendResult(interp
, "no correct size specified for canvas saving",
1577 * Saving is done in a temporary pixmap that is allocated
1578 * here and freed at the end of the procedure. All drawing
1579 * is done to the pixmap, and the pixmap is saved to the
1580 * file at the end of the procedure.
1582 * Some tricky points about the pixmap:
1584 * 1. We only allocate a large enough pixmap to hold the
1585 * area that has to be saved. This saves time in
1586 * in the X server for large objects that cover much
1587 * more than the area being saved: only the area
1588 * of the pixmap will actually have to be saved.
1589 * 2. The origin of the pixmap is adjusted to an even multiple
1590 * of 32 bits. This is so that stipple patterns with a size
1591 * of 8 or 16 or 32 bits will always line up when information
1592 * is copied back to the screen.
1593 * 3. Some X servers (e.g. the one for DECstations) have troubles
1594 * with characters that overlap an edge of the pixmap (on the
1595 * DEC servers, as of 8/18/92, such characters are drawn one
1596 * pixel too far to the right). To handle this problem,
1597 * make the pixmap a bit larger than is absolutely needed
1598 * so that for normal-sized fonts the characters that ovelap
1599 * the edge of the pixmap will be outside the area we care
1603 canvasPtr
->drawableXOrigin
= (screenX1
- 30) & ~0x1f;
1604 canvasPtr
->drawableYOrigin
= (screenY1
- 30) & ~0x1f;
1605 pixmap
= XCreatePixmap(Tk_Display(tkwin
), Tk_WindowId(tkwin
),
1606 screenX2
+ 30 - canvasPtr
->drawableXOrigin
,
1607 screenY2
+ 30 - canvasPtr
->drawableYOrigin
,
1608 Tk_DefaultDepth(Tk_Screen(tkwin
)));
1609 savePixmap
= XCreatePixmap(Tk_Display(tkwin
), Tk_WindowId(tkwin
),
1610 width
, height
, Tk_DefaultDepth(Tk_Screen(tkwin
)));
1613 * Clear the area to be redrawn.
1616 XFillRectangle(Tk_Display(tkwin
), pixmap
, canvasPtr
->pixmapGC
,
1617 screenX1
- canvasPtr
->drawableXOrigin
,
1618 screenY1
- canvasPtr
->drawableYOrigin
,
1619 (unsigned int) (screenX2
- screenX1
),
1620 (unsigned int) (screenY2
- screenY1
));
1621 XFillRectangle(Tk_Display(tkwin
), savePixmap
, canvasPtr
->pixmapGC
,
1622 0, 0, width
, height
);
1625 * Scan through the item list, redrawing those items that need it.
1626 * An item must be redraw if either (a) it intersects the smaller
1627 * on-screen area or (b) it intersects the full canvas area and its
1628 * type requests that it be redrawn always (e.g. so subwindows can
1629 * be unmapped when they move off-screen).
1632 for (itemPtr
= canvasPtr
->firstItemPtr
; itemPtr
!= NULL
;
1633 itemPtr
= itemPtr
->nextPtr
) {
1634 if ((itemPtr
->x1
>= screenX2
)
1635 || (itemPtr
->y1
>= screenY2
)
1636 || (itemPtr
->x2
< screenX1
)
1637 || (itemPtr
->y2
< screenY1
)) {
1638 if (!itemPtr
->typePtr
->alwaysRedraw
1639 || (itemPtr
->x1
>= canvasPtr
->redrawX2
)
1640 || (itemPtr
->y1
>= canvasPtr
->redrawY2
)
1641 || (itemPtr
->x2
< canvasPtr
->redrawX1
)
1642 || (itemPtr
->y2
< canvasPtr
->redrawY1
)) {
1646 (*itemPtr
->typePtr
->displayProc
)(canvasPtr
, itemPtr
, pixmap
);
1650 * Copy from the temporary pixmap to the save pixmap.
1653 XCopyArea(Tk_Display(tkwin
), pixmap
, savePixmap
,
1654 canvasPtr
->pixmapGC
,
1655 screenX1
- canvasPtr
->drawableXOrigin
,
1656 screenY1
- canvasPtr
->drawableYOrigin
,
1657 screenX2
- screenX1
, screenY2
- screenY1
, 0, 0);
1660 * Save temporary pixmap.
1663 xpm_attributes
.width
= width
;
1664 xpm_attributes
.height
= height
;
1665 xpm_attributes
.visual
= Tk_DefaultVisual(Tk_Screen(tkwin
));
1666 xpm_attributes
.colormap
= Tk_DefaultColormap(Tk_Screen(tkwin
));
1667 xpm_attributes
.valuemask
= XpmSize
| XpmVisual
| XpmColormap
;
1668 if(XpmWriteFileFromPixmap(Tk_Display(tkwin
), fileName
,
1669 savePixmap
, (Pixmap
) NULL
,
1670 &xpm_attributes
) != XpmSuccess
) {
1671 XFreePixmap(Tk_Display(tkwin
), pixmap
);
1672 XFreePixmap(Tk_Display(tkwin
), savePixmap
);
1673 Tcl_ResetResult(interp
);
1674 Tcl_AppendResult(interp
, "could not save pixmap for canvas",
1678 XFreePixmap(Tk_Display(tkwin
), pixmap
);
1679 XFreePixmap(Tk_Display(tkwin
), savePixmap
);
1686 *--------------------------------------------------------------
1690 * This procedure redraws the contents of a canvas window.
1691 * It is invoked as a do-when-idle handler, so it only runs
1692 * when there's nothing else for the application to do.
1698 * Information appears on the screen.
1700 *--------------------------------------------------------------
1704 DisplayCanvas(clientData
)
1705 ClientData clientData
; /* Information about widget. */
1707 register Tk_Canvas
*canvasPtr
= (Tk_Canvas
*) clientData
;
1708 register Tk_Window tkwin
= canvasPtr
->tkwin
;
1709 register Tk_Item
*itemPtr
;
1711 int screenX1
, screenX2
, screenY1
, screenY2
;
1713 if (canvasPtr
->tkwin
== NULL
) {
1716 if (!Tk_IsMapped(tkwin
)) {
1721 * Choose a new current item if that is needed (this could cause
1722 * event handlers to be invoked).
1725 while (canvasPtr
->flags
& REPICK_NEEDED
) {
1726 Tk_Preserve((ClientData
) canvasPtr
);
1727 canvasPtr
->flags
&= ~REPICK_NEEDED
;
1728 PickCurrentItem(canvasPtr
, &canvasPtr
->pickEvent
);
1729 tkwin
= canvasPtr
->tkwin
;
1730 Tk_Release((ClientData
) canvasPtr
);
1731 if (tkwin
== NULL
) {
1737 * Compute the intersection between the area that needs redrawing
1738 * and the area that's visible on the screen.
1741 screenX1
= canvasPtr
->xOrigin
;
1742 screenY1
= canvasPtr
->yOrigin
;
1743 screenX2
= screenX1
+ Tk_Width(tkwin
);
1744 screenY2
= screenY1
+ Tk_Height(tkwin
);
1745 if (canvasPtr
->redrawX1
> screenX1
) {
1746 screenX1
= canvasPtr
->redrawX1
;
1748 if (canvasPtr
->redrawY1
> screenY1
) {
1749 screenY1
= canvasPtr
->redrawY1
;
1751 if (canvasPtr
->redrawX2
< screenX2
) {
1752 screenX2
= canvasPtr
->redrawX2
;
1754 if (canvasPtr
->redrawY2
< screenY2
) {
1755 screenY2
= canvasPtr
->redrawY2
;
1757 if ((screenX1
>= screenX2
) || (screenY1
>= screenY2
)) {
1762 * Redrawing is done in a temporary pixmap that is allocated
1763 * here and freed at the end of the procedure. All drawing
1764 * is done to the pixmap, and the pixmap is copied to the
1765 * screen at the end of the procedure. The temporary pixmap
1766 * serves two purposes:
1768 * 1. It provides a smoother visual effect (no clearing and
1769 * gradual redraw will be visible to users).
1770 * 2. It allows us to redraw only the objects that overlap
1771 * the redraw area. Otherwise incorrect results could
1772 * occur from redrawing things that stick outside of
1773 * the redraw area (we'd have to redraw everything in
1774 * order to make the overlaps look right).
1776 * Some tricky points about the pixmap:
1778 * 1. We only allocate a large enough pixmap to hold the
1779 * area that has to be redisplayed. This saves time in
1780 * in the X server for large objects that cover much
1781 * more than the area being redisplayed: only the area
1782 * of the pixmap will actually have to be redrawn.
1783 * 2. The origin of the pixmap is adjusted to an even multiple
1784 * of 32 bits. This is so that stipple patterns with a size
1785 * of 8 or 16 or 32 bits will always line up when information
1786 * is copied back to the screen.
1787 * 3. Some X servers (e.g. the one for DECstations) have troubles
1788 * with characters that overlap an edge of the pixmap (on the
1789 * DEC servers, as of 8/18/92, such characters are drawn one
1790 * pixel too far to the right). To handle this problem,
1791 * make the pixmap a bit larger than is absolutely needed
1792 * so that for normal-sized fonts the characters that ovelap
1793 * the edge of the pixmap will be outside the area we care
1797 canvasPtr
->drawableXOrigin
= (screenX1
- 30) & ~0x1f;
1798 canvasPtr
->drawableYOrigin
= (screenY1
- 30) & ~0x1f;
1799 pixmap
= XCreatePixmap(Tk_Display(tkwin
), Tk_WindowId(tkwin
),
1800 screenX2
+ 30 - canvasPtr
->drawableXOrigin
,
1801 screenY2
+ 30 - canvasPtr
->drawableYOrigin
,
1802 Tk_DefaultDepth(Tk_Screen(tkwin
)));
1805 * Clear the area to be redrawn.
1808 XFillRectangle(Tk_Display(tkwin
), pixmap
, canvasPtr
->pixmapGC
,
1809 screenX1
- canvasPtr
->drawableXOrigin
,
1810 screenY1
- canvasPtr
->drawableYOrigin
,
1811 (unsigned int) (screenX2
- screenX1
),
1812 (unsigned int) (screenY2
- screenY1
));
1815 * Scan through the item list, redrawing those items that need it.
1816 * An item must be redraw if either (a) it intersects the smaller
1817 * on-screen area or (b) it intersects the full canvas area and its
1818 * type requests that it be redrawn always (e.g. so subwindows can
1819 * be unmapped when they move off-screen).
1822 for (itemPtr
= canvasPtr
->firstItemPtr
; itemPtr
!= NULL
;
1823 itemPtr
= itemPtr
->nextPtr
) {
1824 if ((itemPtr
->x1
>= screenX2
)
1825 || (itemPtr
->y1
>= screenY2
)
1826 || (itemPtr
->x2
< screenX1
)
1827 || (itemPtr
->y2
< screenY1
)) {
1828 if (!itemPtr
->typePtr
->alwaysRedraw
1829 || (itemPtr
->x1
>= canvasPtr
->redrawX2
)
1830 || (itemPtr
->y1
>= canvasPtr
->redrawY2
)
1831 || (itemPtr
->x2
< canvasPtr
->redrawX1
)
1832 || (itemPtr
->y2
< canvasPtr
->redrawY1
)) {
1836 (*itemPtr
->typePtr
->displayProc
)(canvasPtr
, itemPtr
, pixmap
);
1840 * Draw the window border.
1843 if (canvasPtr
->relief
!= TK_RELIEF_FLAT
) {
1844 Tk_Draw3DRectangle(Tk_Display(tkwin
), pixmap
,
1845 canvasPtr
->bgBorder
,
1846 canvasPtr
->xOrigin
- canvasPtr
->drawableXOrigin
,
1847 canvasPtr
->yOrigin
- canvasPtr
->drawableYOrigin
,
1848 Tk_Width(tkwin
), Tk_Height(tkwin
),
1849 canvasPtr
->borderWidth
, canvasPtr
->relief
);
1853 * Copy from the temporary pixmap to the screen, then free up
1854 * the temporary pixmap.
1857 XCopyArea(Tk_Display(tkwin
), pixmap
, Tk_WindowId(tkwin
),
1858 canvasPtr
->pixmapGC
,
1859 screenX1
- canvasPtr
->drawableXOrigin
,
1860 screenY1
- canvasPtr
->drawableYOrigin
,
1861 screenX2
- screenX1
, screenY2
- screenY1
,
1862 screenX1
- canvasPtr
->xOrigin
, screenY1
- canvasPtr
->yOrigin
);
1863 XFreePixmap(Tk_Display(tkwin
), pixmap
);
1866 canvasPtr
->flags
&= ~REDRAW_PENDING
;
1867 assert(canvasPtr
->updateTimerToken
!= NULL
);
1868 canvasPtr
->updateTimerToken
= NULL
;
1869 if (canvasPtr
->flags
& UPDATE_SCROLLBARS
) {
1870 CanvasUpdateScrollbars(canvasPtr
);
1875 *--------------------------------------------------------------
1877 * CanvasEventProc --
1879 * This procedure is invoked by the Tk dispatcher for various
1880 * events on canvases.
1886 * When the window gets deleted, internal structures get
1887 * cleaned up. When it gets exposed, it is redisplayed.
1889 *--------------------------------------------------------------
1893 CanvasEventProc(clientData
, eventPtr
)
1894 ClientData clientData
; /* Information about window. */
1895 XEvent
*eventPtr
; /* Information about event. */
1897 Tk_Canvas
*canvasPtr
= (Tk_Canvas
*) clientData
;
1899 if (eventPtr
->type
== Expose
) {
1902 x
= eventPtr
->xexpose
.x
+ canvasPtr
->xOrigin
;
1903 y
= eventPtr
->xexpose
.y
+ canvasPtr
->yOrigin
;
1904 EventuallyRedrawArea(canvasPtr
, x
, y
, x
+ eventPtr
->xexpose
.width
,
1905 y
+ eventPtr
->xexpose
.height
);
1906 } else if (eventPtr
->type
== DestroyNotify
) {
1907 Tcl_DeleteCommand(canvasPtr
->interp
, Tk_PathName(canvasPtr
->tkwin
));
1908 canvasPtr
->tkwin
= NULL
;
1909 if (canvasPtr
->flags
& REDRAW_PENDING
) {
1910 canvasPtr
->flags
&= ~REDRAW_PENDING
;
1911 // Tk_CancelIdleCall(DisplayCanvas, (ClientData) canvasPtr);
1912 assert(canvasPtr
->updateTimerToken
!= NULL
);
1913 if (canvasPtr
->updateTimerToken
!= NULL
) {
1914 Tk_DeleteTimerHandler(canvasPtr
->updateTimerToken
);
1915 canvasPtr
->updateTimerToken
= 0;
1918 Tk_EventuallyFree((ClientData
) canvasPtr
, DestroyCanvas
);
1919 } else if (eventPtr
->type
== ConfigureNotify
) {
1920 canvasPtr
->flags
|= UPDATE_SCROLLBARS
;
1923 * The call below is needed in order to recenter the canvas if
1924 * it's confined and its scroll region is smaller than the window.
1927 CanvasSetOrigin(canvasPtr
, canvasPtr
->xOrigin
, canvasPtr
->yOrigin
);
1928 EventuallyRedrawArea(canvasPtr
, 0, 0, Tk_Width(canvasPtr
->tkwin
),
1929 Tk_Height(canvasPtr
->tkwin
));
1934 *--------------------------------------------------------------
1936 * EventuallyRedrawArea --
1938 * Arrange for part or all of a canvas widget to redrawn at
1939 * the next convenient time in the future.
1945 * The screen will eventually be refreshed.
1947 *--------------------------------------------------------------
1951 EventuallyRedrawArea(canvasPtr
, x1
, y1
, x2
, y2
)
1952 register Tk_Canvas
*canvasPtr
; /* Information about widget. */
1953 int x1
, y1
; /* Upper left corner of area to
1954 * redraw. Pixels on edge are
1956 int x2
, y2
; /* Lower right corner of area to
1957 * redraw. Pixels on edge are
1960 if ((canvasPtr
->tkwin
== NULL
) || !Tk_IsMapped(canvasPtr
->tkwin
)) {
1963 if (canvasPtr
->flags
& REDRAW_PENDING
) {
1964 if (x1
<= canvasPtr
->redrawX1
) {
1965 canvasPtr
->redrawX1
= x1
;
1967 if (y1
<= canvasPtr
->redrawY1
) {
1968 canvasPtr
->redrawY1
= y1
;
1970 if (x2
>= canvasPtr
->redrawX2
) {
1971 canvasPtr
->redrawX2
= x2
;
1973 if (y2
>= canvasPtr
->redrawY2
) {
1974 canvasPtr
->redrawY2
= y2
;
1977 canvasPtr
->redrawX1
= x1
;
1978 canvasPtr
->redrawY1
= y1
;
1979 canvasPtr
->redrawX2
= x2
;
1980 canvasPtr
->redrawY2
= y2
;
1981 // Tk_DoWhenIdle(DisplayCanvas, (ClientData) canvasPtr);
1982 canvasPtr
->flags
|= REDRAW_PENDING
;
1983 assert(canvasPtr
->updateTimerToken
== NULL
);
1984 if (canvasPtr
->updateTimerToken
== 0) {
1985 canvasPtr
->updateTimerToken
=
1986 Tk_CreateTimerHandler(
1989 (ClientData
) canvasPtr
);
1995 *--------------------------------------------------------------
1997 * Tk_CreateItemType --
1999 * This procedure may be invoked to add a new kind of canvas
2000 * element to the core item types supported by Tk.
2006 * From now on, the new item type will be useable in canvas
2007 * widgets (e.g. typePtr->name can be used as the item type
2008 * in "create" widget commands). If there was already a
2009 * type with the same name as in typePtr, it is replaced with
2012 *--------------------------------------------------------------
2016 Tk_CreateItemType(typePtr
)
2017 Tk_ItemType
*typePtr
; /* Information about item type;
2018 * storage must be statically
2019 * allocated (must live forever). */
2021 if (typeList
== NULL
) {
2024 typePtr
->nextPtr
= typeList
;
2029 *--------------------------------------------------------------
2033 * This procedure is invoked to perform once-only-ever
2034 * initialization for the module, such as setting up
2043 *--------------------------------------------------------------
2049 if (typeList
!= NULL
) {
2052 typeList
= &TkRectangleType
;
2053 TkRectangleType
.nextPtr
= &TkTextType
;
2054 TkTextType
.nextPtr
= &TkPolygonType
;
2055 TkPolygonType
.nextPtr
= &TkOvalType
;
2056 TkOvalType
.nextPtr
= &TkLineType
;
2057 TkLineType
.nextPtr
= &TkWindowType
;
2058 TkWindowType
.nextPtr
= &TkBitmapType
;
2059 TkBitmapType
.nextPtr
= &TkArcType
;
2060 TkArcType
.nextPtr
= NULL
;
2061 allUid
= Tk_GetUid("all");
2062 currentUid
= Tk_GetUid("current");
2066 *--------------------------------------------------------------
2070 * This procedure is called to initiate an enumeration of
2071 * all items in a given canvas that contain a given tag.
2074 * The return value is a pointer to the first item in
2075 * canvasPtr that matches tag, or NULL if there is no
2076 * such item. The information at *searchPtr is initialized
2077 * such that successive calls to NextItem will return
2078 * successive items that match tag.
2081 * SearchPtr is linked into a list of searches in progress
2082 * on canvasPtr, so that elements can safely be deleted
2083 * while the search is in progress. EndTagSearch must be
2084 * called at the end of the search to unlink searchPtr from
2087 *--------------------------------------------------------------
2091 StartTagSearch(canvasPtr
, tag
, searchPtr
)
2092 Tk_Canvas
*canvasPtr
; /* Canvas whose items are to be
2094 char *tag
; /* String giving tag value. */
2095 TagSearch
*searchPtr
; /* Record describing tag search;
2096 * will be initialized here. */
2099 register Tk_Item
*itemPtr
, *prevPtr
;
2100 register Tk_Uid
*tagPtr
;
2101 register Tk_Uid uid
;
2105 * Initialize the search.
2108 searchPtr
->canvasPtr
= canvasPtr
;
2109 searchPtr
->searchOver
= 0;
2112 * Find the first matching item in one of several ways. If the tag
2113 * is a number then it selects the single item with the matching
2114 * identifier. In this case see if the item being requested is the
2115 * hot item, in which case the search can be skipped.
2118 if (isdigit(*tag
)) {
2122 id
= strtoul(tag
, &end
, 0);
2124 itemPtr
= canvasPtr
->hotPtr
;
2125 prevPtr
= canvasPtr
->hotPrevPtr
;
2126 if ((itemPtr
== NULL
) || (itemPtr
->id
!= id
) || (prevPtr
== NULL
)
2127 || (prevPtr
->nextPtr
!= itemPtr
)) {
2129 for (prevPtr
= NULL
, itemPtr
= canvasPtr
->firstItemPtr
;
2131 prevPtr
= itemPtr
, itemPtr
= itemPtr
->nextPtr
) {
2132 if (itemPtr
->id
== id
) {
2137 searchPtr
->prevPtr
= prevPtr
;
2138 searchPtr
->searchOver
= 1;
2139 canvasPtr
->hotPtr
= itemPtr
;
2140 canvasPtr
->hotPrevPtr
= prevPtr
;
2145 searchPtr
->tag
= uid
= Tk_GetUid(tag
);
2146 if (uid
== allUid
) {
2152 searchPtr
->tag
= NULL
;
2153 searchPtr
->prevPtr
= NULL
;
2154 searchPtr
->currentPtr
= canvasPtr
->firstItemPtr
;
2155 return canvasPtr
->firstItemPtr
;
2159 * None of the above. Search for an item with a matching tag.
2162 for (prevPtr
= NULL
, itemPtr
= canvasPtr
->firstItemPtr
; itemPtr
!= NULL
;
2163 prevPtr
= itemPtr
, itemPtr
= itemPtr
->nextPtr
) {
2164 for (tagPtr
= itemPtr
->tagPtr
, count
= itemPtr
->numTags
;
2165 count
> 0; tagPtr
++, count
--) {
2166 if (*tagPtr
== uid
) {
2167 searchPtr
->prevPtr
= prevPtr
;
2168 searchPtr
->currentPtr
= itemPtr
;
2173 searchPtr
->prevPtr
= prevPtr
;
2174 searchPtr
->searchOver
= 1;
2179 *--------------------------------------------------------------
2183 * This procedure returns successive items that match a given
2184 * tag; it should be called only after StartTagSearch has been
2185 * used to begin a search.
2188 * The return value is a pointer to the next item that matches
2189 * the tag specified to StartTagSearch, or NULL if no such
2190 * item exists. *SearchPtr is updated so that the next call
2191 * to this procedure will return the next item.
2196 *--------------------------------------------------------------
2201 TagSearch
*searchPtr
; /* Record describing search in
2204 register Tk_Item
*itemPtr
, *prevPtr
;
2206 register Tk_Uid uid
;
2207 register Tk_Uid
*tagPtr
;
2210 * Find next item in list (this may not actually be a suitable
2211 * one to return), and return if there are no items left.
2214 prevPtr
= searchPtr
->prevPtr
;
2215 if (prevPtr
== NULL
) {
2216 itemPtr
= searchPtr
->canvasPtr
->firstItemPtr
;
2218 itemPtr
= prevPtr
->nextPtr
;
2220 if ((itemPtr
== NULL
) || (searchPtr
->searchOver
)) {
2221 searchPtr
->searchOver
= 1;
2224 if (itemPtr
!= searchPtr
->currentPtr
) {
2226 * The structure of the list has changed. Probably the
2227 * previously-returned item was removed from the list.
2228 * In this case, don't advance prevPtr; just return
2229 * its new successor (i.e. do nothing here).
2233 itemPtr
= prevPtr
->nextPtr
;
2237 * Handle special case of "all" search by returning next item.
2240 uid
= searchPtr
->tag
;
2242 searchPtr
->prevPtr
= prevPtr
;
2243 searchPtr
->currentPtr
= itemPtr
;
2248 * Look for an item with a particular tag.
2251 for ( ; itemPtr
!= NULL
; prevPtr
= itemPtr
, itemPtr
= itemPtr
->nextPtr
) {
2252 for (tagPtr
= itemPtr
->tagPtr
, count
= itemPtr
->numTags
;
2253 count
> 0; tagPtr
++, count
--) {
2254 if (*tagPtr
== uid
) {
2255 searchPtr
->prevPtr
= prevPtr
;
2256 searchPtr
->currentPtr
= itemPtr
;
2261 searchPtr
->prevPtr
= prevPtr
;
2262 searchPtr
->searchOver
= 1;
2267 *--------------------------------------------------------------
2271 * This is a utility procedure called by FindItems. It
2272 * either adds itemPtr's id to the result forming in interp,
2273 * or it adds a new tag to itemPtr, depending on the value
2280 * If tag is NULL then itemPtr's id is added as a list element
2281 * to interp->result; otherwise tag is added to itemPtr's
2284 *--------------------------------------------------------------
2288 DoItem(interp
, itemPtr
, tag
)
2289 Tcl_Interp
*interp
; /* Interpreter in which to (possibly)
2290 * record item id. */
2291 register Tk_Item
*itemPtr
; /* Item to (possibly) modify. */
2292 Tk_Uid tag
; /* Tag to add to those already
2293 * present for item, or NULL. */
2295 register Tk_Uid
*tagPtr
;
2299 * Handle the "add-to-result" case and return, if appropriate.
2304 sprintf(msg
, "%d", itemPtr
->id
);
2305 Tcl_AppendElement(interp
, msg
, 0);
2309 for (tagPtr
= itemPtr
->tagPtr
, count
= itemPtr
->numTags
;
2310 count
> 0; tagPtr
++, count
--) {
2311 if (tag
== *tagPtr
) {
2317 * Grow the tag space if there's no more room left in the current
2321 if (itemPtr
->tagSpace
== itemPtr
->numTags
) {
2324 itemPtr
->tagSpace
+= 5;
2325 newTagPtr
= (Tk_Uid
*) ckalloc((unsigned)
2326 (itemPtr
->tagSpace
* sizeof(Tk_Uid
)));
2327 memcpy((VOID
*) newTagPtr
, (VOID
*) itemPtr
->tagPtr
,
2328 (itemPtr
->numTags
* sizeof(Tk_Uid
)));
2329 if (itemPtr
->tagPtr
!= itemPtr
->staticTagSpace
) {
2330 ckfree((char *) itemPtr
->tagPtr
);
2332 itemPtr
->tagPtr
= newTagPtr
;
2333 tagPtr
= &itemPtr
->tagPtr
[itemPtr
->numTags
];
2337 * Add in the new tag.
2345 *--------------------------------------------------------------
2349 * This procedure does all the work of implementing the
2350 * "find" and "addtag" options of the canvas widget command,
2351 * which locate items that have certain features (location,
2352 * tags, position in display list, etc.).
2355 * A standard Tcl return value. If newTag is NULL, then a
2356 * list of ids from all the items that match argc/argv is
2357 * returned in interp->result. If newTag is NULL, then
2358 * the normal interp->result is an empty string. If an error
2359 * occurs, then interp->result will hold an error message.
2362 * If newTag is non-NULL, then all the items that match the
2363 * information in argc/argv have that tag added to their
2366 *--------------------------------------------------------------
2370 FindItems(interp
, canvasPtr
, argc
, argv
, newTag
, cmdName
, option
)
2371 Tcl_Interp
*interp
; /* Interpreter for error reporting. */
2372 Tk_Canvas
*canvasPtr
; /* Canvas whose items are to be
2374 int argc
; /* Number of entries in argv. Must be
2375 * greater than zero. */
2376 char **argv
; /* Arguments that describe what items
2377 * to search for (see user doc on
2378 * "find" and "addtag" options). */
2379 char *newTag
; /* If non-NULL, gives new tag to set
2380 * on all found items; if NULL, then
2381 * ids of found items are returned
2382 * in interp->result. */
2383 char *cmdName
; /* Name of original Tcl command, for
2384 * use in error messages. */
2385 char *option
; /* For error messages: gives option
2386 * from Tcl command and other stuff
2387 * up to what's in argc/argv. */
2392 register Tk_Item
*itemPtr
;
2395 if (newTag
!= NULL
) {
2396 uid
= Tk_GetUid(newTag
);
2401 length
= strlen(argv
[0]);
2402 if ((c
== 'a') && (strncmp(argv
[0], "above", length
) == 0)
2404 Tk_Item
*lastPtr
= NULL
;
2406 Tcl_AppendResult(interp
, "wrong # args: must be \"",
2407 cmdName
, option
, " above tagOrId", (char *) NULL
);
2410 for (itemPtr
= StartTagSearch(canvasPtr
, argv
[1], &search
);
2411 itemPtr
!= NULL
; itemPtr
= NextItem(&search
)) {
2414 if ((lastPtr
!= NULL
) && (lastPtr
->nextPtr
!= NULL
)) {
2415 DoItem(interp
, lastPtr
->nextPtr
, uid
);
2417 } else if ((c
== 'a') && (strncmp(argv
[0], "all", length
) == 0)
2420 Tcl_AppendResult(interp
, "wrong # args: must be \"",
2421 cmdName
, option
, " all", (char *) NULL
);
2425 for (itemPtr
= canvasPtr
->firstItemPtr
; itemPtr
!= NULL
;
2426 itemPtr
= itemPtr
->nextPtr
) {
2427 DoItem(interp
, itemPtr
, uid
);
2429 } else if ((c
== 'b') && (strncmp(argv
[0], "below", length
) == 0)) {
2431 Tcl_AppendResult(interp
, "wrong # args: must be \"",
2432 cmdName
, option
, " below tagOrId", (char *) NULL
);
2435 itemPtr
= StartTagSearch(canvasPtr
, argv
[1], &search
);
2436 if (search
.prevPtr
!= NULL
) {
2437 DoItem(interp
, search
.prevPtr
, uid
);
2439 } else if ((c
== 'c') && (strncmp(argv
[0], "closest", length
) == 0)) {
2441 Tk_Item
*startPtr
, *closestPtr
;
2442 double coords
[2], halo
;
2445 if ((argc
< 3) || (argc
> 5)) {
2446 Tcl_AppendResult(interp
, "wrong # args: must be \"",
2447 cmdName
, option
, " closest x y ?halo? ?start?",
2451 if ((TkGetCanvasCoord(canvasPtr
, argv
[1], &coords
[0]) != TCL_OK
)
2452 || (TkGetCanvasCoord(canvasPtr
, argv
[2], &coords
[1])
2457 if (TkGetCanvasCoord(canvasPtr
, argv
[3], &halo
) != TCL_OK
) {
2461 Tcl_AppendResult(interp
, "can't have negative halo value \"",
2462 argv
[3], "\"", (char *) NULL
);
2470 * Find the item at which to start the search.
2473 startPtr
= canvasPtr
->firstItemPtr
;
2475 itemPtr
= StartTagSearch(canvasPtr
, argv
[4], &search
);
2476 if (itemPtr
!= NULL
) {
2482 * The code below is optimized so that it can eliminate most
2483 * items without having to call their item-specific procedures.
2484 * This is done by keeping a bounding box (x1, y1, x2, y2) that
2485 * an item's bbox must overlap if the item is to have any
2486 * chance of being closer than the closest so far.
2490 if (itemPtr
== NULL
) {
2493 closestDist
= (*itemPtr
->typePtr
->pointProc
)(canvasPtr
,
2494 itemPtr
, coords
) - halo
;
2495 if (closestDist
< 0.0) {
2502 * Update the bounding box using itemPtr, which is the
2506 x1
= (coords
[0] - closestDist
- halo
- 1);
2507 y1
= (coords
[1] - closestDist
- halo
- 1);
2508 x2
= (coords
[0] + closestDist
+ halo
+ 1);
2509 y2
= (coords
[1] + closestDist
+ halo
+ 1);
2510 closestPtr
= itemPtr
;
2513 * Search for an item that beats the current closest one.
2514 * Work circularly through the canvas's item list until
2515 * getting back to the starting item.
2519 itemPtr
= itemPtr
->nextPtr
;
2520 if (itemPtr
== NULL
) {
2521 itemPtr
= canvasPtr
->firstItemPtr
;
2523 if (itemPtr
== startPtr
) {
2524 DoItem(interp
, closestPtr
, uid
);
2527 if ((itemPtr
->x1
>= x2
) || (itemPtr
->x2
<= x1
)
2528 || (itemPtr
->y1
>= y2
) || (itemPtr
->y2
<= y1
)) {
2531 newDist
= (*itemPtr
->typePtr
->pointProc
)(canvasPtr
,
2532 itemPtr
, coords
) - halo
;
2533 if (newDist
< 0.0) {
2536 if (newDist
<= closestDist
) {
2537 closestDist
= newDist
;
2542 } else if ((c
== 'e') && (strncmp(argv
[0], "enclosed", length
) == 0)) {
2544 Tcl_AppendResult(interp
, "wrong # args: must be \"",
2545 cmdName
, option
, " enclosed x1 y1 x2 y2", (char *) NULL
);
2548 return FindArea(interp
, canvasPtr
, argv
+1, uid
, 1);
2549 } else if ((c
== 'o') && (strncmp(argv
[0], "overlapping", length
) == 0)) {
2551 Tcl_AppendResult(interp
, "wrong # args: must be \"",
2552 cmdName
, option
, " overlapping x1 y1 x2 y2",
2556 return FindArea(interp
, canvasPtr
, argv
+1, uid
, 0);
2557 } else if ((c
== 'w') && (strncmp(argv
[0], "withtag", length
) == 0)) {
2559 Tcl_AppendResult(interp
, "wrong # args: must be \"",
2560 cmdName
, option
, " withtag tagOrId", (char *) NULL
);
2563 for (itemPtr
= StartTagSearch(canvasPtr
, argv
[1], &search
);
2564 itemPtr
!= NULL
; itemPtr
= NextItem(&search
)) {
2565 DoItem(interp
, itemPtr
, uid
);
2568 Tcl_AppendResult(interp
, "bad search command \"", argv
[0],
2569 "\": must be above, all, below, closest, enclosed, ",
2570 "overlapping, or withtag", (char *) NULL
);
2577 *--------------------------------------------------------------
2581 * This procedure implements area searches for the "find"
2582 * and "addtag" options.
2585 * A standard Tcl return value. If newTag is NULL, then a
2586 * list of ids from all the items overlapping or enclosed
2587 * by the rectangle given by argc is returned in interp->result.
2588 * If newTag is NULL, then the normal interp->result is an
2589 * empty string. If an error occurs, then interp->result will
2590 * hold an error message.
2593 * If uid is non-NULL, then all the items overlapping
2594 * or enclosed by the area in argv have that tag added to
2595 * their lists of tags.
2597 *--------------------------------------------------------------
2601 FindArea(interp
, canvasPtr
, argv
, uid
, enclosed
)
2602 Tcl_Interp
*interp
; /* Interpreter for error reporting
2603 * and result storing. */
2604 Tk_Canvas
*canvasPtr
; /* Canvas whose items are to be
2606 char **argv
; /* Array of four arguments that
2607 * give the coordinates of the
2608 * rectangular area to search. */
2609 Tk_Uid uid
; /* If non-NULL, gives new tag to set
2610 * on all found items; if NULL, then
2611 * ids of found items are returned
2612 * in interp->result. */
2613 int enclosed
; /* 0 means overlapping or enclosed
2614 * items are OK, 1 means only enclosed
2617 double rect
[4], tmp
;
2619 register Tk_Item
*itemPtr
;
2621 if ((TkGetCanvasCoord(canvasPtr
, argv
[0], &rect
[0]) != TCL_OK
)
2622 || (TkGetCanvasCoord(canvasPtr
, argv
[1], &rect
[1]) != TCL_OK
)
2623 || (TkGetCanvasCoord(canvasPtr
, argv
[2], &rect
[2]) != TCL_OK
)
2624 || (TkGetCanvasCoord(canvasPtr
, argv
[3], &rect
[3]) != TCL_OK
)) {
2627 if (rect
[0] > rect
[2]) {
2628 tmp
= rect
[0]; rect
[0] = rect
[2]; rect
[2] = tmp
;
2630 if (rect
[1] > rect
[3]) {
2631 tmp
= rect
[1]; rect
[1] = rect
[3]; rect
[3] = tmp
;
2635 * Use an integer bounding box for a quick test, to avoid
2636 * calling item-specific code except for items that are close.
2643 for (itemPtr
= canvasPtr
->firstItemPtr
; itemPtr
!= NULL
;
2644 itemPtr
= itemPtr
->nextPtr
) {
2645 if ((itemPtr
->x1
>= x2
) || (itemPtr
->x2
<= x1
)
2646 || (itemPtr
->y1
>= y2
) || (itemPtr
->y2
<= y1
)) {
2649 if ((*itemPtr
->typePtr
->areaProc
)(canvasPtr
, itemPtr
, rect
)
2651 DoItem(interp
, itemPtr
, uid
);
2658 *--------------------------------------------------------------
2662 * Move one or more items to a different place in the
2663 * display order for a canvas.
2669 * The items identified by "tag" are moved so that they
2670 * are all together in the display list and immediately
2671 * after prevPtr. The order of the moved items relative
2672 * to each other is not changed.
2674 *--------------------------------------------------------------
2678 RelinkItems(canvasPtr
, tag
, prevPtr
)
2679 Tk_Canvas
*canvasPtr
; /* Canvas to be modified. */
2680 char *tag
; /* Tag identifying items to be moved
2681 * in the redisplay list. */
2682 Tk_Item
*prevPtr
; /* Reposition the items so that they
2683 * go just after this item (NULL means
2684 * put at beginning of list). */
2686 register Tk_Item
*itemPtr
;
2688 Tk_Item
*firstMovePtr
, *lastMovePtr
;
2691 * Find all of the items to be moved and remove them from
2692 * the list, making an auxiliary list running from firstMovePtr
2693 * to lastMovePtr. Record their areas for redisplay.
2696 firstMovePtr
= lastMovePtr
= NULL
;
2697 for (itemPtr
= StartTagSearch(canvasPtr
, tag
, &search
);
2698 itemPtr
!= NULL
; itemPtr
= NextItem(&search
)) {
2699 if (itemPtr
== prevPtr
) {
2701 * Item after which insertion is to occur is being
2702 * moved! Switch to insert after its predecessor.
2705 prevPtr
= search
.prevPtr
;
2707 if (search
.prevPtr
== NULL
) {
2708 canvasPtr
->firstItemPtr
= itemPtr
->nextPtr
;
2710 search
.prevPtr
->nextPtr
= itemPtr
->nextPtr
;
2712 if (canvasPtr
->lastItemPtr
== itemPtr
) {
2713 canvasPtr
->lastItemPtr
= search
.prevPtr
;
2715 if (firstMovePtr
== NULL
) {
2716 firstMovePtr
= itemPtr
;
2718 lastMovePtr
->nextPtr
= itemPtr
;
2720 lastMovePtr
= itemPtr
;
2721 EventuallyRedrawArea(canvasPtr
, itemPtr
->x1
, itemPtr
->y1
,
2722 itemPtr
->x2
, itemPtr
->y2
);
2723 canvasPtr
->flags
|= REPICK_NEEDED
;
2727 * Insert the list of to-be-moved items back into the canvas's
2728 * at the desired position.
2731 if (firstMovePtr
== NULL
) {
2734 if (prevPtr
== NULL
) {
2735 lastMovePtr
->nextPtr
= canvasPtr
->firstItemPtr
;
2736 canvasPtr
->firstItemPtr
= firstMovePtr
;
2738 lastMovePtr
->nextPtr
= prevPtr
->nextPtr
;
2739 prevPtr
->nextPtr
= firstMovePtr
;
2741 if (canvasPtr
->lastItemPtr
== prevPtr
) {
2742 canvasPtr
->lastItemPtr
= lastMovePtr
;
2747 *--------------------------------------------------------------
2751 * This procedure is invoked by the Tk dispatcher to handle
2752 * events associated with bindings on items.
2758 * Depends on the command invoked as part of the binding
2759 * (if there was any).
2761 *--------------------------------------------------------------
2765 CanvasBindProc(clientData
, eventPtr
)
2766 ClientData clientData
; /* Pointer to canvas structure. */
2767 XEvent
*eventPtr
; /* Pointer to X event that just
2770 Tk_Canvas
*canvasPtr
= (Tk_Canvas
*) clientData
;
2773 Tk_Preserve((ClientData
) canvasPtr
);
2776 * This code simulates grabs for mouse buttons by refusing to
2777 * pick a new current item between the time a mouse button goes
2778 * down and the time when the last mouse button is released is
2782 if (eventPtr
->type
== ButtonPress
) {
2783 canvasPtr
->flags
|= BUTTON_DOWN
;
2784 } else if (eventPtr
->type
== ButtonRelease
) {
2787 switch (eventPtr
->xbutton
.button
) {
2807 if ((eventPtr
->xbutton
.state
& (Button1Mask
|Button2Mask
2808 |Button3Mask
|Button4Mask
|Button5Mask
)) == mask
) {
2809 canvasPtr
->flags
&= ~BUTTON_DOWN
;
2812 } else if ((eventPtr
->type
== EnterNotify
)
2813 || (eventPtr
->type
== LeaveNotify
)) {
2814 PickCurrentItem(canvasPtr
, eventPtr
);
2816 } else if (eventPtr
->type
== MotionNotify
) {
2817 PickCurrentItem(canvasPtr
, eventPtr
);
2819 CanvasDoEvent(canvasPtr
, eventPtr
);
2821 unsigned int oldState
;
2823 oldState
= eventPtr
->xbutton
.state
;
2824 eventPtr
->xbutton
.state
&= ~(Button1Mask
|Button2Mask
2825 |Button3Mask
|Button4Mask
|Button5Mask
);
2826 PickCurrentItem(canvasPtr
, eventPtr
);
2827 eventPtr
->xbutton
.state
= oldState
;
2831 Tk_Release((ClientData
) canvasPtr
);
2835 *--------------------------------------------------------------
2837 * PickCurrentItem --
2839 * Find the topmost item in a canvas that contains a given
2840 * location and mark the the current item. If the current
2841 * item has changed, generate a fake exit event on the old
2842 * current item and a fake enter event on the new current
2849 * The current item for canvasPtr may change. If it does,
2850 * then the commands associated with item entry and exit
2851 * could do just about anything.
2853 *--------------------------------------------------------------
2857 PickCurrentItem(canvasPtr
, eventPtr
)
2858 register Tk_Canvas
*canvasPtr
; /* Canvas pointer in which to select
2860 XEvent
*eventPtr
; /* Event describing location of
2861 * mouse cursor. Must be EnterWindow,
2862 * LeaveWindow, ButtonRelease, or
2865 Tk_Item
*closestPtr
= NULL
;
2868 * If a button is down, then don't do anything at all; we'll be
2869 * called again when all buttons are up, and we can repick then.
2870 * This implements a form of mouse grabbing for canvases.
2873 if (canvasPtr
->flags
& BUTTON_DOWN
) {
2878 * Save information about this event in the canvas. The event in
2879 * the canvas is used for two purposes:
2881 * 1. Event bindings: if the current item changes, fake events are
2882 * generated to allow item-enter and item-leave bindings to trigger.
2883 * 2. Reselection: if the current item gets deleted, can use the
2884 * saved event to find a new current item.
2885 * Translate MotionNotify events into EnterNotify events, since that's
2886 * what gets reported to item handlers.
2889 if (eventPtr
!= &canvasPtr
->pickEvent
) {
2890 if ((eventPtr
->type
== MotionNotify
)
2891 || (eventPtr
->type
== ButtonRelease
)) {
2892 canvasPtr
->pickEvent
.xcrossing
.type
= EnterNotify
;
2893 canvasPtr
->pickEvent
.xcrossing
.serial
= eventPtr
->xmotion
.serial
;
2894 canvasPtr
->pickEvent
.xcrossing
.send_event
2895 = eventPtr
->xmotion
.send_event
;
2896 canvasPtr
->pickEvent
.xcrossing
.display
= eventPtr
->xmotion
.display
;
2897 canvasPtr
->pickEvent
.xcrossing
.window
= eventPtr
->xmotion
.window
;
2898 canvasPtr
->pickEvent
.xcrossing
.root
= eventPtr
->xmotion
.root
;
2899 canvasPtr
->pickEvent
.xcrossing
.subwindow
= None
;
2900 canvasPtr
->pickEvent
.xcrossing
.time
= eventPtr
->xmotion
.time
;
2901 canvasPtr
->pickEvent
.xcrossing
.x
= eventPtr
->xmotion
.x
;
2902 canvasPtr
->pickEvent
.xcrossing
.y
= eventPtr
->xmotion
.y
;
2903 canvasPtr
->pickEvent
.xcrossing
.x_root
= eventPtr
->xmotion
.x_root
;
2904 canvasPtr
->pickEvent
.xcrossing
.y_root
= eventPtr
->xmotion
.y_root
;
2905 canvasPtr
->pickEvent
.xcrossing
.mode
= NotifyNormal
;
2906 canvasPtr
->pickEvent
.xcrossing
.detail
= NotifyNonlinear
;
2907 canvasPtr
->pickEvent
.xcrossing
.same_screen
2908 = eventPtr
->xmotion
.same_screen
;
2909 canvasPtr
->pickEvent
.xcrossing
.focus
= False
;
2910 canvasPtr
->pickEvent
.xcrossing
.state
= eventPtr
->xmotion
.state
;
2912 canvasPtr
->pickEvent
= *eventPtr
;
2917 * A LeaveNotify event automatically means that there's no current
2918 * object, so the rest of the code below can be skipped.
2921 if (canvasPtr
->pickEvent
.type
!= LeaveNotify
) {
2924 register Tk_Item
*itemPtr
;
2926 coords
[0] = canvasPtr
->pickEvent
.xcrossing
.x
+ canvasPtr
->xOrigin
;
2927 coords
[1] = canvasPtr
->pickEvent
.xcrossing
.y
+ canvasPtr
->yOrigin
;
2928 x1
= coords
[0] - canvasPtr
->closeEnough
;
2929 y1
= coords
[1] - canvasPtr
->closeEnough
;
2930 x2
= coords
[0] + canvasPtr
->closeEnough
;
2931 y2
= coords
[1] + canvasPtr
->closeEnough
;
2933 for (itemPtr
= canvasPtr
->firstItemPtr
; itemPtr
!= NULL
;
2934 itemPtr
= itemPtr
->nextPtr
) {
2935 if ((itemPtr
->x1
>= x2
) || (itemPtr
->x2
< x1
)
2936 || (itemPtr
->y1
>= y2
) || (itemPtr
->y2
< y1
)) {
2939 if ((*itemPtr
->typePtr
->pointProc
)(canvasPtr
,
2940 itemPtr
, coords
) <= canvasPtr
->closeEnough
) {
2941 closestPtr
= itemPtr
;
2947 * Simulate a LeaveNotify event on the previous current item and
2948 * an EnterNotify event on the new current item. Remove the "current"
2949 * tag from the previous current item and place it on the new current
2953 if (closestPtr
== canvasPtr
->currentItemPtr
) {
2956 if (canvasPtr
->currentItemPtr
!= NULL
) {
2958 Tk_Item
*itemPtr
= canvasPtr
->currentItemPtr
;
2961 event
= canvasPtr
->pickEvent
;
2962 event
.type
= LeaveNotify
;
2963 CanvasDoEvent(canvasPtr
, &event
);
2964 for (i
= itemPtr
->numTags
-1; i
>= 0; i
--) {
2965 if (itemPtr
->tagPtr
[i
] == currentUid
) {
2966 itemPtr
->tagPtr
[i
] = itemPtr
->tagPtr
[itemPtr
->numTags
-1];
2972 canvasPtr
->currentItemPtr
= closestPtr
;
2973 if (canvasPtr
->currentItemPtr
!= NULL
) {
2976 DoItem((Tcl_Interp
*) NULL
, closestPtr
, currentUid
);
2977 event
= canvasPtr
->pickEvent
;
2978 event
.type
= EnterNotify
;
2979 CanvasDoEvent(canvasPtr
, &event
);
2984 *--------------------------------------------------------------
2988 * This procedure is called to invoke binding processing
2989 * for a new event that is associated with the current item
2996 * Depends on the bindings for the canvas.
2998 *--------------------------------------------------------------
3002 CanvasDoEvent(canvasPtr
, eventPtr
)
3003 Tk_Canvas
*canvasPtr
; /* Canvas widget in which event
3005 XEvent
*eventPtr
; /* Real or simulated X event that
3006 * is to be processed. */
3008 #define NUM_STATIC 3
3009 ClientData staticObjects
[NUM_STATIC
];
3010 ClientData
*objectPtr
;
3012 register Tk_Item
*itemPtr
;
3014 if (canvasPtr
->bindingTable
== NULL
) {
3018 itemPtr
= canvasPtr
->currentItemPtr
;
3019 if ((eventPtr
->type
== KeyPress
) || (eventPtr
->type
== KeyRelease
)) {
3020 itemPtr
= canvasPtr
->focusItemPtr
;
3022 if (itemPtr
== NULL
) {
3027 * Set up an array with all the relevant objects for processing
3028 * this event. The relevant objects are (a) the event's item,
3029 * (b) the tags associated with the event's item, and (c) the
3030 * tag "all". If there are a lot of tags then malloc an array
3031 * to hold all of the objects.
3034 numObjects
= itemPtr
->numTags
+ 2;
3035 if (numObjects
<= NUM_STATIC
) {
3036 objectPtr
= staticObjects
;
3038 objectPtr
= (ClientData
*) ckalloc((unsigned)
3039 (numObjects
* sizeof(ClientData
)));
3041 objectPtr
[0] = (ClientData
) itemPtr
;
3042 for (i
= itemPtr
->numTags
-1; i
>= 0; i
--) {
3043 objectPtr
[i
+1] = (ClientData
) itemPtr
->tagPtr
[i
];
3045 objectPtr
[itemPtr
->numTags
+1] = (ClientData
) allUid
;
3048 * Invoke the binding system, then free up the object array if
3052 Tk_BindEvent(canvasPtr
->bindingTable
, eventPtr
, canvasPtr
->tkwin
,
3053 numObjects
, objectPtr
);
3054 if (objectPtr
!= staticObjects
) {
3055 ckfree((char *) objectPtr
);
3060 *----------------------------------------------------------------------
3062 * CanvasBlinkProc --
3064 * This procedure is called as a timer handler to blink the
3065 * insertion cursor off and on.
3071 * The cursor gets turned on or off, redisplay gets invoked,
3072 * and this procedure reschedules itself.
3074 *----------------------------------------------------------------------
3078 CanvasBlinkProc(clientData
)
3079 ClientData clientData
; /* Pointer to record describing entry. */
3081 register Tk_Canvas
*canvasPtr
= (Tk_Canvas
*) clientData
;
3083 if (!(canvasPtr
->flags
& GOT_FOCUS
) || (canvasPtr
->cursorOffTime
== 0)) {
3086 if (canvasPtr
->flags
& CURSOR_ON
) {
3087 canvasPtr
->flags
&= ~CURSOR_ON
;
3088 canvasPtr
->cursorBlinkHandler
= Tk_CreateTimerHandler(
3089 canvasPtr
->cursorOffTime
, CanvasBlinkProc
,
3090 (ClientData
) canvasPtr
);
3092 canvasPtr
->flags
|= CURSOR_ON
;
3093 canvasPtr
->cursorBlinkHandler
= Tk_CreateTimerHandler(
3094 canvasPtr
->cursorOnTime
, CanvasBlinkProc
,
3095 (ClientData
) canvasPtr
);
3097 if (canvasPtr
->focusItemPtr
!= NULL
) {
3098 EventuallyRedrawArea(canvasPtr
, canvasPtr
->focusItemPtr
->x1
,
3099 canvasPtr
->focusItemPtr
->y1
, canvasPtr
->focusItemPtr
->x2
,
3100 canvasPtr
->focusItemPtr
->y2
);
3105 *----------------------------------------------------------------------
3107 * CanvasFocusProc --
3109 * This procedure is called whenever a canvas gets or loses the
3110 * input focus. It's also called whenever the window is
3111 * reconfigured while it has the focus.
3117 * The cursor gets turned on or off.
3119 *----------------------------------------------------------------------
3123 CanvasFocusProc(clientData
, gotFocus
)
3124 ClientData clientData
; /* Pointer to structure describing entry. */
3125 int gotFocus
; /* 1 means window is getting focus, 0 means
3126 * it's losing it. */
3128 register Tk_Canvas
*canvasPtr
= (Tk_Canvas
*) clientData
;
3130 Tk_DeleteTimerHandler(canvasPtr
->cursorBlinkHandler
);
3132 canvasPtr
->flags
|= GOT_FOCUS
| CURSOR_ON
;
3133 if (canvasPtr
->cursorOffTime
!= 0) {
3134 canvasPtr
->cursorBlinkHandler
= Tk_CreateTimerHandler(
3135 canvasPtr
->cursorOnTime
, CanvasBlinkProc
,
3136 (ClientData
) canvasPtr
);
3139 canvasPtr
->flags
&= ~(GOT_FOCUS
| CURSOR_ON
);
3140 canvasPtr
->cursorBlinkHandler
= (Tk_TimerToken
) NULL
;
3142 if (canvasPtr
->focusItemPtr
!= NULL
) {
3143 EventuallyRedrawArea(canvasPtr
, canvasPtr
->focusItemPtr
->x1
,
3144 canvasPtr
->focusItemPtr
->y1
, canvasPtr
->focusItemPtr
->x2
,
3145 canvasPtr
->focusItemPtr
->y2
);
3150 *----------------------------------------------------------------------
3154 * Modify the selection by moving its un-anchored end. This could
3155 * make the selection either larger or smaller.
3161 * The selection changes.
3163 *----------------------------------------------------------------------
3167 CanvasSelectTo(canvasPtr
, itemPtr
, index
)
3168 register Tk_Canvas
*canvasPtr
; /* Information about widget. */
3169 register Tk_Item
*itemPtr
; /* Item that is to hold selection. */
3170 int index
; /* Index of element that is to
3171 * become the "other" end of the
3174 int oldFirst
, oldLast
;
3177 oldFirst
= canvasPtr
->selectFirst
;
3178 oldLast
= canvasPtr
->selectLast
;
3179 oldSelPtr
= canvasPtr
->selItemPtr
;
3182 * Grab the selection if we don't own it already.
3185 if (canvasPtr
->selItemPtr
== NULL
) {
3186 Tk_OwnSelection(canvasPtr
->tkwin
, CanvasLostSelection
,
3187 (ClientData
) canvasPtr
);
3188 } else if (canvasPtr
->selItemPtr
!= itemPtr
) {
3189 EventuallyRedrawArea(canvasPtr
, canvasPtr
->selItemPtr
->x1
,
3190 canvasPtr
->selItemPtr
->y1
, canvasPtr
->selItemPtr
->x2
,
3191 canvasPtr
->selItemPtr
->y2
);
3193 canvasPtr
->selItemPtr
= itemPtr
;
3195 if (canvasPtr
->anchorItemPtr
!= itemPtr
) {
3196 canvasPtr
->anchorItemPtr
= itemPtr
;
3197 canvasPtr
->selectAnchor
= index
;
3199 if (canvasPtr
->selectAnchor
<= index
) {
3200 canvasPtr
->selectFirst
= canvasPtr
->selectAnchor
;
3201 canvasPtr
->selectLast
= index
;
3203 canvasPtr
->selectFirst
= index
;
3204 canvasPtr
->selectLast
= canvasPtr
->selectAnchor
- 1;
3206 if ((canvasPtr
->selectFirst
!= oldFirst
)
3207 || (canvasPtr
->selectLast
!= oldLast
)
3208 || (itemPtr
!= oldSelPtr
)) {
3209 EventuallyRedrawArea(canvasPtr
, itemPtr
->x1
, itemPtr
->y1
,
3210 itemPtr
->x2
, itemPtr
->y2
);
3215 *--------------------------------------------------------------
3217 * CanvasFetchSelection --
3219 * This procedure is invoked by Tk to return part or all of
3220 * the selection, when the selection is in a canvas widget.
3221 * This procedure always returns the selection as a STRING.
3224 * The return value is the number of non-NULL bytes stored
3225 * at buffer. Buffer is filled (or partially filled) with a
3226 * NULL-terminated string containing part or all of the selection,
3227 * as given by offset and maxBytes.
3232 *--------------------------------------------------------------
3236 CanvasFetchSelection(clientData
, offset
, buffer
, maxBytes
)
3237 ClientData clientData
; /* Information about canvas widget. */
3238 int offset
; /* Offset within selection of first
3239 * character to be returned. */
3240 char *buffer
; /* Location in which to place
3242 int maxBytes
; /* Maximum number of bytes to place
3243 * at buffer, not including terminating
3244 * NULL character. */
3246 register Tk_Canvas
*canvasPtr
= (Tk_Canvas
*) clientData
;
3248 if (canvasPtr
->selItemPtr
== NULL
) {
3251 if (canvasPtr
->selItemPtr
->typePtr
->selectionProc
== NULL
) {
3254 return (*canvasPtr
->selItemPtr
->typePtr
->selectionProc
)(
3255 canvasPtr
, canvasPtr
->selItemPtr
, offset
, buffer
, maxBytes
);
3259 *----------------------------------------------------------------------
3261 * CanvasLostSelection --
3263 * This procedure is called back by Tk when the selection is
3264 * grabbed away from a canvas widget.
3270 * The existing selection is unhighlighted, and the window is
3271 * marked as not containing a selection.
3273 *----------------------------------------------------------------------
3277 CanvasLostSelection(clientData
)
3278 ClientData clientData
; /* Information about entry widget. */
3280 Tk_Canvas
*canvasPtr
= (Tk_Canvas
*) clientData
;
3282 if (canvasPtr
->selItemPtr
!= NULL
) {
3283 EventuallyRedrawArea(canvasPtr
, canvasPtr
->selItemPtr
->x1
,
3284 canvasPtr
->selItemPtr
->y1
, canvasPtr
->selItemPtr
->x2
,
3285 canvasPtr
->selItemPtr
->y2
);
3287 canvasPtr
->selItemPtr
= NULL
;
3291 *--------------------------------------------------------------
3293 * TkGetCanvasCoord --
3295 * Given a string, returns a floating-point canvas coordinate
3296 * corresponding to that string.
3299 * The return value is a standard Tcl return result. If
3300 * TCL_OK is returned, then everything went well and the
3301 * canvas coordinate is stored at *doublePtr; otherwise
3302 * TCL_ERROR is returned and an error message is left in
3303 * canvasPtr->interp->result.
3308 *--------------------------------------------------------------
3312 TkGetCanvasCoord(canvasPtr
, string
, doublePtr
)
3313 Tk_Canvas
*canvasPtr
; /* Canvas to which coordinate applies. */
3314 char *string
; /* Describes coordinate (any screen
3315 * coordinate form may be used here). */
3316 double *doublePtr
; /* Place to store converted coordinate. */
3318 if (Tk_GetScreenMM(canvasPtr
->interp
, canvasPtr
->tkwin
, string
,
3319 doublePtr
) != TCL_OK
) {
3322 *doublePtr
*= canvasPtr
->pixelsPerMM
;
3327 *--------------------------------------------------------------
3331 * Given a coordinate and a grid spacing, this procedure
3332 * computes the location of the nearest grid line to the
3336 * The return value is the location of the grid line nearest
3342 *--------------------------------------------------------------
3346 GridAlign(coord
, spacing
)
3347 double coord
; /* Coordinate to grid-align. */
3348 double spacing
; /* Spacing between grid lines. If <= 0
3349 * then no alignment is done. */
3351 if (spacing
<= 0.0) {
3355 return -((int) ((-coord
)/spacing
+ 0.5)) * spacing
;
3357 return ((int) (coord
/spacing
+ 0.5)) * spacing
;
3361 *--------------------------------------------------------------
3363 * CanvasUpdateScrollbars --
3365 * This procedure is invoked whenever a canvas has changed in
3366 * a way that requires scrollbars to be redisplayed (e.g. the
3367 * view in the canvas has changed).
3373 * If there are scrollbars associated with the canvas, then
3374 * their scrolling commands are invoked to cause them to
3375 * redisplay. If errors occur, additional Tcl commands may
3376 * be invoked to process the errors.
3378 *--------------------------------------------------------------
3382 CanvasUpdateScrollbars(canvasPtr
)
3383 register Tk_Canvas
*canvasPtr
; /* Information about canvas. */
3385 int result
, size
, first
, last
, page
;
3388 #define ROUND(number) \
3389 if (number >= 0) { \
3390 number = (number + canvasPtr->scrollIncrement/2) \
3391 /canvasPtr->scrollIncrement; \
3393 number = -(((-number) + canvasPtr->scrollIncrement/2) \
3394 /canvasPtr->scrollIncrement); \
3397 canvasPtr
->flags
&= ~UPDATE_SCROLLBARS
;
3398 if (canvasPtr
->xScrollCmd
!= NULL
) {
3399 size
= ((canvasPtr
->scrollX2
- canvasPtr
->scrollX1
)
3400 /canvasPtr
->scrollIncrement
) + 1;
3401 first
= canvasPtr
->xOrigin
- canvasPtr
->scrollX1
;
3403 last
= canvasPtr
->xOrigin
+ Tk_Width(canvasPtr
->tkwin
)
3404 - 1 - canvasPtr
->scrollX1
;
3406 page
= last
- first
- 1;
3410 sprintf(args
, " %d %d %d %d", size
, page
, first
, last
);
3411 result
= Tcl_VarEval(canvasPtr
->interp
, canvasPtr
->xScrollCmd
, args
,
3413 if (result
!= TCL_OK
) {
3414 TkBindError(canvasPtr
->interp
);
3416 Tcl_ResetResult(canvasPtr
->interp
);
3419 if (canvasPtr
->yScrollCmd
!= NULL
) {
3420 size
= ((canvasPtr
->scrollY2
- canvasPtr
->scrollY1
)
3421 /canvasPtr
->scrollIncrement
) + 1;
3422 first
= canvasPtr
->yOrigin
- canvasPtr
->scrollY1
;
3424 last
= canvasPtr
->yOrigin
+ Tk_Height(canvasPtr
->tkwin
)
3425 - 1 - canvasPtr
->scrollY1
;
3427 page
= last
- first
- 1;
3431 sprintf(args
, " %d %d %d %d", size
, page
, first
, last
);
3432 result
= Tcl_VarEval(canvasPtr
->interp
, canvasPtr
->yScrollCmd
, args
,
3434 if (result
!= TCL_OK
) {
3435 TkBindError(canvasPtr
->interp
);
3437 Tcl_ResetResult(canvasPtr
->interp
);
3442 *--------------------------------------------------------------
3444 * CanvasSetOrigin --
3446 * This procedure is invoked to change the mapping between
3447 * canvas coordinates and screen coordinates in the canvas
3454 * The canvas will be redisplayed to reflect the change in
3455 * view. In addition, scrollbars will be updated if there
3458 *--------------------------------------------------------------
3462 CanvasSetOrigin(canvasPtr
, xOrigin
, yOrigin
)
3463 register Tk_Canvas
*canvasPtr
; /* Information about canvas. */
3464 int xOrigin
; /* New X origin for canvas (canvas
3465 * x-coord corresponding to left edge
3466 * of canvas window). */
3467 int yOrigin
; /* New Y origin for canvas (canvas
3468 * y-coord corresponding to top edge
3469 * of canvas window). */
3472 * Adjust the origin if necessary to keep as much as possible of the
3473 * canvas in the view.
3476 if ((canvasPtr
->confine
) && (canvasPtr
->regionString
!= NULL
)) {
3477 int windowWidth
, windowHeight
, canvasWidth
, canvasHeight
;
3479 windowWidth
= Tk_Width(canvasPtr
->tkwin
);
3480 windowHeight
= Tk_Height(canvasPtr
->tkwin
);
3481 canvasWidth
= canvasPtr
->scrollX2
- canvasPtr
->scrollX1
;
3482 canvasHeight
= canvasPtr
->scrollY2
- canvasPtr
->scrollY1
;
3483 if (canvasWidth
< windowWidth
) {
3484 xOrigin
= (canvasPtr
->scrollX1
) - (windowWidth
-canvasWidth
)/2;
3485 } else if (xOrigin
< canvasPtr
->scrollX1
) {
3486 xOrigin
= canvasPtr
->scrollX1
;
3487 } else if (xOrigin
> (canvasPtr
->scrollX2
- windowWidth
)) {
3488 xOrigin
= canvasPtr
->scrollX2
- windowWidth
;
3490 if (canvasHeight
< windowHeight
) {
3491 yOrigin
= (canvasPtr
->scrollY1
) - (windowHeight
-canvasHeight
)/2;
3492 } else if (yOrigin
< canvasPtr
->scrollY1
) {
3493 yOrigin
= canvasPtr
->scrollY1
;
3494 } else if (yOrigin
> (canvasPtr
->scrollY2
- windowHeight
)) {
3495 yOrigin
= canvasPtr
->scrollY2
- windowHeight
;
3499 if ((xOrigin
== canvasPtr
->xOrigin
) && (yOrigin
== canvasPtr
->yOrigin
)) {
3504 * Tricky point: must redisplay not only everything that's visible
3505 * in the window's final configuration, but also everything that was
3506 * visible in the initial configuration. This is needed because some
3507 * item types, like windows, need to know when they move off-screen
3508 * so they can explicitly undisplay themselves.
3511 EventuallyRedrawArea(canvasPtr
, canvasPtr
->xOrigin
, canvasPtr
->yOrigin
,
3512 canvasPtr
->xOrigin
+ Tk_Width(canvasPtr
->tkwin
),
3513 canvasPtr
->yOrigin
+ Tk_Height(canvasPtr
->tkwin
));
3514 canvasPtr
->xOrigin
= xOrigin
;
3515 canvasPtr
->yOrigin
= yOrigin
;
3516 canvasPtr
->flags
|= UPDATE_SCROLLBARS
;
3517 EventuallyRedrawArea(canvasPtr
, canvasPtr
->xOrigin
, canvasPtr
->yOrigin
,
3518 canvasPtr
->xOrigin
+ Tk_Width(canvasPtr
->tkwin
),
3519 canvasPtr
->yOrigin
+ Tk_Height(canvasPtr
->tkwin
));
3523 *--------------------------------------------------------------
3525 * CanvasTagsParseProc --
3527 * This procedure is invoked during option processing to handle
3528 * "-tags" options for canvas items.
3531 * A standard Tcl return value.
3534 * The tags for a given item get replaced by those indicated
3535 * in the value argument.
3537 *--------------------------------------------------------------
3542 CanvasTagsParseProc(clientData
, interp
, tkwin
, value
, widgRec
, offset
)
3543 ClientData clientData
; /* Not used.*/
3544 Tcl_Interp
*interp
; /* Used for reporting errors. */
3545 Tk_Window tkwin
; /* Window containing canvas widget. */
3546 char *value
; /* Value of option (list of tag
3548 char *widgRec
; /* Pointer to record for item. */
3549 int offset
; /* Offset into item (ignored). */
3551 register Tk_Item
*itemPtr
= (Tk_Item
*) widgRec
;
3557 * Break the value up into the individual tag names.
3560 if (Tcl_SplitList(interp
, value
, &argc
, &argv
) != TCL_OK
) {
3565 * Make sure that there's enough space in the item to hold the
3569 if (itemPtr
->tagSpace
< argc
) {
3570 newPtr
= (Tk_Uid
*) ckalloc((unsigned) (argc
* sizeof(Tk_Uid
)));
3571 for (i
= itemPtr
->numTags
-1; i
>= 0; i
--) {
3572 newPtr
[i
] = itemPtr
->tagPtr
[i
];
3574 if (itemPtr
->tagPtr
!= itemPtr
->staticTagSpace
) {
3575 ckfree((char *) itemPtr
->tagPtr
);
3577 itemPtr
->tagPtr
= newPtr
;
3578 itemPtr
->tagSpace
= argc
;
3580 itemPtr
->numTags
= argc
;
3581 for (i
= 0; i
< argc
; i
++) {
3582 itemPtr
->tagPtr
[i
] = Tk_GetUid(argv
[i
]);
3584 ckfree((char *) argv
);
3589 *--------------------------------------------------------------
3591 * CanvasTagsPrintProc --
3593 * This procedure is invoked by the Tk configuration code
3594 * to produce a printable string for the "-tags" configuration
3595 * option for canvas items.
3598 * The return value is a string describing all the tags for
3599 * the item referred to by "widgRec". In addition, *freeProcPtr
3600 * is filled in with the address of a procedure to call to free
3601 * the result string when it's no longer needed (or NULL to
3602 * indicate that the string doesn't need to be freed).
3607 *--------------------------------------------------------------
3612 CanvasTagsPrintProc(clientData
, tkwin
, widgRec
, offset
, freeProcPtr
)
3613 ClientData clientData
; /* Ignored. */
3614 Tk_Window tkwin
; /* Window containing canvas widget. */
3615 char *widgRec
; /* Pointer to record for item. */
3616 int offset
; /* Ignored. */
3617 Tcl_FreeProc
**freeProcPtr
; /* Pointer to variable to fill in with
3618 * information about how to reclaim
3619 * storage for return string. */
3621 register Tk_Item
*itemPtr
= (Tk_Item
*) widgRec
;
3623 if (itemPtr
->numTags
== 0) {
3624 *freeProcPtr
= (Tcl_FreeProc
*) NULL
;
3627 if (itemPtr
->numTags
== 1) {
3628 *freeProcPtr
= (Tcl_FreeProc
*) NULL
;
3629 return (char *) itemPtr
->tagPtr
[0];
3631 *freeProcPtr
= (Tcl_FreeProc
*) free
;
3632 return Tcl_Merge(itemPtr
->numTags
, (char **) itemPtr
->tagPtr
);