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