1 | |
---|
2 | /* |
---|
3 | * ---------------------------------------------------------------------- |
---|
4 | * RpCanvHotspot - canvas item with text and background box |
---|
5 | * |
---|
6 | * This canvas item makes it easy to create a box with text inside. |
---|
7 | * The box is normally stretched around the text, but can be given |
---|
8 | * a max size and causing text to be clipped. |
---|
9 | * |
---|
10 | * .c create hotspot <x> <y> -anchor <nsew> \ |
---|
11 | * -text <text> << text to be displayed |
---|
12 | * -font <name> << font used for text |
---|
13 | * -image <image> << image displayed on the right of text |
---|
14 | * -maxwidth <size> << maximum size |
---|
15 | * -textcolor <color> << text color |
---|
16 | * -substtextcolor <color> << substituted text color |
---|
17 | * -background <color> << fill for rect behind text |
---|
18 | * -outline <color> << outline around text |
---|
19 | * -borderwidth <size> << for 3D border |
---|
20 | * -relief <value> << for 3D border (drawn under -outline) |
---|
21 | * -tags <taglist> << usual Tk canvas tags |
---|
22 | * -interp interp Interpreter containing substitution variables. |
---|
23 | * |
---|
24 | * ====================================================================== |
---|
25 | * AUTHOR: Michael McLennan, Purdue University |
---|
26 | * Copyright (c) 2004-2012 HUBzero Foundation, LLC |
---|
27 | * |
---|
28 | * See the file "license.terms" for information on usage and |
---|
29 | * redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES. |
---|
30 | * ====================================================================== |
---|
31 | */ |
---|
32 | #include <string.h> |
---|
33 | #include <math.h> |
---|
34 | #include "tk.h" |
---|
35 | |
---|
36 | #define SEGMENT_TEXT 0 |
---|
37 | #define SEGMENT_VALUE 1 |
---|
38 | #define VAR_FLAGS (TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS) |
---|
39 | |
---|
40 | #define REDRAW_PENDING (1<<0) |
---|
41 | #define LAYOUT_PENDING (1<<1) |
---|
42 | #define SUBSTITUTIONS_PENDING (1<<2) |
---|
43 | |
---|
44 | #define TRUE 1 |
---|
45 | #define FALSE 0 |
---|
46 | #undef MAX |
---|
47 | #define MAX(a,b) (((a)>(b))?(a):(b)) |
---|
48 | |
---|
49 | #undef ROUND |
---|
50 | #define ROUND(x) ((int)(((double)(x)) + (((x)<0.0) ? -0.5 : 0.5))) |
---|
51 | |
---|
52 | #define IMAGE_PAD 3 |
---|
53 | #define TEXT_PADX 3 |
---|
54 | #define TEXT_PADY 1 |
---|
55 | |
---|
56 | typedef struct _ItemSegment ItemSegment; |
---|
57 | typedef struct _HotspotItem HotspotItem; |
---|
58 | |
---|
59 | /* |
---|
60 | * ItemSegment |
---|
61 | * |
---|
62 | */ |
---|
63 | struct _ItemSegment { |
---|
64 | HotspotItem *itemPtr; |
---|
65 | ItemSegment *nextPtr; |
---|
66 | int type; /* Image, normal text, or substituted |
---|
67 | * value. */ |
---|
68 | short int x, y; /* Location of segment on canvas. */ |
---|
69 | short int width, height; /* Dimension of segment. */ |
---|
70 | Tk_TextLayout layout; /* Layout of normal or substituted |
---|
71 | * value */ |
---|
72 | int length; /* # of bytes in below string. */ |
---|
73 | int lineNum; /* Line where segment if located. */ |
---|
74 | char *value; /* Substitututed value. */ |
---|
75 | char text[1]; |
---|
76 | }; |
---|
77 | |
---|
78 | /* |
---|
79 | * Record for each hotspot item: |
---|
80 | */ |
---|
81 | struct _HotspotItem { |
---|
82 | Tk_Item base; /* Generic stuff that's the same for |
---|
83 | * all types. MUST BE FIRST IN |
---|
84 | * STRUCTURE. */ |
---|
85 | unsigned int flags; |
---|
86 | Tcl_Interp *interp; /* Interp that owns this item */ |
---|
87 | Display *display; |
---|
88 | Tk_Window tkwin; /* Window that represents this |
---|
89 | * canvas */ |
---|
90 | Tk_Canvas canvas; /* Canvas that owns this item */ |
---|
91 | |
---|
92 | /* |
---|
93 | * Fields that are set by widget commands other than "configure": |
---|
94 | */ |
---|
95 | double x, y; /* Positioning point for item. This in |
---|
96 | * conjunction with -anchor determine |
---|
97 | * where the item is drawn on the |
---|
98 | * canvas. */ |
---|
99 | double x1, y1, x2, y2; |
---|
100 | /* |
---|
101 | * Configuration settings that are updated by Tk_ConfigureWidget: |
---|
102 | */ |
---|
103 | Tcl_Interp *valueInterp; /* If non-NULL, interpreter containing |
---|
104 | * substituted variables. */ |
---|
105 | const char *text; /* Text to be displayed */ |
---|
106 | Tk_Anchor anchor; /* Where to anchor text relative to |
---|
107 | * (x,y) */ |
---|
108 | XColor *textColor; /* Color for normal text */ |
---|
109 | XColor *valueColor; /* Color for substitututed values. */ |
---|
110 | XColor *activeColor; /* Color for active substitututed |
---|
111 | * values */ |
---|
112 | Tk_3DBorder activeBorder; /* Background color for active |
---|
113 | * substitututed values */ |
---|
114 | XColor *outlineColor; /* Color for outline of rectangle */ |
---|
115 | Tk_3DBorder border; /* If non-NULL, background color of |
---|
116 | * rectangle enclosing the entire |
---|
117 | * hotspot item. */ |
---|
118 | int borderWidth; /* 3D border width (drawn under |
---|
119 | * -outline) */ |
---|
120 | int relief; /* Indicates whether hotspot as a |
---|
121 | * whole is raised, sunken, or flat */ |
---|
122 | Tk_Font font; /* Font for drawing normal text. */ |
---|
123 | Tk_Font valueFont; /* Font for drawing substituted |
---|
124 | * values. */ |
---|
125 | |
---|
126 | const char *imageName; /* Name of normal hotspot image. */ |
---|
127 | const char *activeImageName; /* Name of active hotspot image. */ |
---|
128 | |
---|
129 | Tk_Image image; /* The normal icon for a hotspot. */ |
---|
130 | Tk_Image activeImage; /* If non-NULL, active icon. */ |
---|
131 | |
---|
132 | /* |
---|
133 | * Fields whose values are derived from the current values of the |
---|
134 | * configuration settings above: |
---|
135 | */ |
---|
136 | GC normalGC; /* Graphics context for drawing |
---|
137 | * text. */ |
---|
138 | GC valueGC; /* GC for substituted values. */ |
---|
139 | GC activeGC; /* Gc for active substituted |
---|
140 | * values. */ |
---|
141 | GC outlineGC; /* GC for outline? */ |
---|
142 | ItemSegment *firstPtr, *lastPtr; /* List of hotspot segments. */ |
---|
143 | int width, height; /* Dimension of bounding box. */ |
---|
144 | const char *activeValue; /* Active segment. */ |
---|
145 | short int maxImageWidth, maxImageHeight; |
---|
146 | int numLines; |
---|
147 | int showIcons; /* Indicates whether to draw icons. */ |
---|
148 | }; |
---|
149 | |
---|
150 | /* |
---|
151 | * Information used for parsing configuration specs: |
---|
152 | */ |
---|
153 | static Tk_CustomOption tagsOption = { |
---|
154 | (Tk_OptionParseProc *) Tk_CanvasTagsParseProc, |
---|
155 | Tk_CanvasTagsPrintProc, (ClientData) NULL |
---|
156 | }; |
---|
157 | |
---|
158 | static Tk_OptionParseProc ValueInterpParseProc; |
---|
159 | static Tk_OptionPrintProc ValueInterpPrintProc; |
---|
160 | static Tk_CustomOption interpOption = { |
---|
161 | (Tk_OptionParseProc *)ValueInterpParseProc, ValueInterpPrintProc, |
---|
162 | (ClientData) NULL |
---|
163 | }; |
---|
164 | |
---|
165 | static Tk_ConfigSpec configSpecs[] = { |
---|
166 | {TK_CONFIG_BORDER, "-activebackground", (char *)NULL, (char*)NULL, |
---|
167 | "white", Tk_Offset(HotspotItem, activeBorder), TK_CONFIG_NULL_OK}, |
---|
168 | {TK_CONFIG_COLOR, "-activeforeground", (char*)NULL, (char*)NULL, |
---|
169 | "black", Tk_Offset(HotspotItem, activeColor), 0}, |
---|
170 | {TK_CONFIG_STRING, "-activeimage", "activeImage", (char*)NULL, (char*)NULL, |
---|
171 | Tk_Offset(HotspotItem, activeImageName), TK_CONFIG_NULL_OK}, |
---|
172 | {TK_CONFIG_STRING, "-activevalue", "activeValue", (char*)NULL, (char*)NULL, |
---|
173 | Tk_Offset(HotspotItem, activeValue), TK_CONFIG_NULL_OK}, |
---|
174 | {TK_CONFIG_ANCHOR, "-anchor", "anchor", (char*)NULL, |
---|
175 | "center", Tk_Offset(HotspotItem, anchor), TK_CONFIG_DONT_SET_DEFAULT}, |
---|
176 | {TK_CONFIG_BORDER, "-background", (char *)NULL, (char*)NULL, |
---|
177 | "", Tk_Offset(HotspotItem, border), TK_CONFIG_NULL_OK}, |
---|
178 | {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", (char*)NULL, |
---|
179 | "0", Tk_Offset(HotspotItem, borderWidth), TK_CONFIG_DONT_SET_DEFAULT}, |
---|
180 | {TK_CONFIG_BORDER, "-fill", (char *)NULL, (char*)NULL, |
---|
181 | "", Tk_Offset(HotspotItem, border), TK_CONFIG_NULL_OK}, |
---|
182 | {TK_CONFIG_FONT, "-font", "font", (char*)NULL, |
---|
183 | "helvetica -12", Tk_Offset(HotspotItem, font), 0}, |
---|
184 | {TK_CONFIG_COLOR, "-foreground", "foreground", (char*)NULL, |
---|
185 | "black", Tk_Offset(HotspotItem, textColor), 0}, |
---|
186 | {TK_CONFIG_CUSTOM, "-interp", (char*)NULL, (char*)NULL, (char*)NULL, |
---|
187 | Tk_Offset(HotspotItem, valueInterp), TK_CONFIG_NULL_OK, &interpOption}, |
---|
188 | {TK_CONFIG_FONT, "-valuefont", (char*)NULL, (char*)NULL, |
---|
189 | "helvetica -12 bold", Tk_Offset(HotspotItem, valueFont), 0}, |
---|
190 | {TK_CONFIG_COLOR, "-valueforeground", (char*)NULL, (char*)NULL, |
---|
191 | "blue", Tk_Offset(HotspotItem, valueColor), 0}, |
---|
192 | {TK_CONFIG_STRING, "-image", (char*)NULL, (char*)NULL, (char*)NULL, |
---|
193 | Tk_Offset(HotspotItem, imageName), 0}, |
---|
194 | {TK_CONFIG_COLOR, "-outline", (char*)NULL, (char*)NULL, |
---|
195 | "", Tk_Offset(HotspotItem, outlineColor), TK_CONFIG_NULL_OK}, |
---|
196 | {TK_CONFIG_RELIEF, "-relief", (char*)NULL, (char*)NULL, |
---|
197 | "flat", Tk_Offset(HotspotItem, relief), TK_CONFIG_DONT_SET_DEFAULT}, |
---|
198 | {TK_CONFIG_BOOLEAN, "-showicons", (char*)NULL, (char*)NULL, |
---|
199 | "0", Tk_Offset(HotspotItem, showIcons), TK_CONFIG_DONT_SET_DEFAULT}, |
---|
200 | {TK_CONFIG_CUSTOM, "-tags", (char*)NULL, (char*)NULL, |
---|
201 | (char*)NULL, 0, TK_CONFIG_NULL_OK, &tagsOption}, |
---|
202 | {TK_CONFIG_STRING, "-text", (char*)NULL, (char*)NULL, |
---|
203 | "", Tk_Offset(HotspotItem, text), 0}, |
---|
204 | {TK_CONFIG_END, (char*)NULL, (char*)NULL, (char*)NULL, |
---|
205 | (char*)NULL, 0, 0} |
---|
206 | }; |
---|
207 | |
---|
208 | /* |
---|
209 | * Prototypes for procedures defined in this file |
---|
210 | */ |
---|
211 | static Tk_ItemConfigureProc ConfigureProc; |
---|
212 | static Tk_ItemCreateProc CreateProc; |
---|
213 | static Tk_ItemDeleteProc DeleteProc; |
---|
214 | static Tk_ItemDisplayProc DisplayProc; |
---|
215 | static Tk_ItemScaleProc ScaleProc; |
---|
216 | static Tk_ItemTranslateProc TranslateProc; |
---|
217 | static Tk_ItemCoordProc CoordsProc; |
---|
218 | static Tk_ItemAreaProc AreaProc; |
---|
219 | static Tk_ItemPointProc PointProc; |
---|
220 | static Tk_ItemPostscriptProc PostscriptProc; |
---|
221 | |
---|
222 | static Tk_ImageChangedProc ImageChangedProc; |
---|
223 | |
---|
224 | /* |
---|
225 | * The structures below defines the canvas item type: |
---|
226 | */ |
---|
227 | static Tk_ItemType hotspotType = { |
---|
228 | "hotspot", /* name */ |
---|
229 | sizeof(HotspotItem), /* itemSize */ |
---|
230 | CreateProc, |
---|
231 | configSpecs, /* configSpecs */ |
---|
232 | ConfigureProc, |
---|
233 | CoordsProc, |
---|
234 | DeleteProc, |
---|
235 | DisplayProc, |
---|
236 | TK_CONFIG_OBJS, /* flags */ |
---|
237 | PointProc, |
---|
238 | AreaProc, |
---|
239 | PostscriptProc, |
---|
240 | ScaleProc, |
---|
241 | TranslateProc, |
---|
242 | NULL, /* indexProc */ |
---|
243 | NULL, /* icursorProc */ |
---|
244 | NULL, /* selectionProc */ |
---|
245 | NULL, /* insertProc */ |
---|
246 | NULL, /* dTextProc */ |
---|
247 | NULL, /* nextPtr */ |
---|
248 | }; |
---|
249 | |
---|
250 | /* |
---|
251 | * The record below describes a canvas widget. It is made available |
---|
252 | * to the item procedures so they can access certain shared fields such |
---|
253 | * as the overall displacement and scale factor for the canvas. |
---|
254 | */ |
---|
255 | |
---|
256 | typedef struct TkCanvas { |
---|
257 | Tk_Window tkwin; /* Window that embodies the canvas. NULL |
---|
258 | * means that the window has been destroyed |
---|
259 | * but the data structures haven't yet been |
---|
260 | * cleaned up.*/ |
---|
261 | Display *display; /* Display containing widget; needed, among |
---|
262 | * other things, to release resources after |
---|
263 | * tkwin has already gone away. */ |
---|
264 | Tcl_Interp *interp; /* Interpreter associated with canvas. */ |
---|
265 | Tcl_Command widgetCmd; /* Token for canvas's widget command. */ |
---|
266 | Tk_Item *firstItemPtr; /* First in list of all items in canvas, |
---|
267 | * or NULL if canvas empty. */ |
---|
268 | Tk_Item *lastItemPtr; /* Last in list of all items in canvas, |
---|
269 | * or NULL if canvas empty. */ |
---|
270 | |
---|
271 | /* |
---|
272 | * Information used when displaying widget: |
---|
273 | */ |
---|
274 | |
---|
275 | int borderWidth; /* Width of 3-D border around window. */ |
---|
276 | Tk_3DBorder border; /* Used for canvas background. */ |
---|
277 | int relief; /* Indicates whether window as a whole is |
---|
278 | * raised, sunken, or flat. */ |
---|
279 | int highlightWidth; /* Width in pixels of highlight to draw |
---|
280 | * around widget when it has the focus. |
---|
281 | * <= 0 means don't draw a highlight. */ |
---|
282 | XColor *highlightBgColorPtr; |
---|
283 | /* Color for drawing traversal highlight |
---|
284 | * area when highlight is off. */ |
---|
285 | XColor *highlightColorPtr; /* Color for drawing traversal highlight. */ |
---|
286 | int inset; /* Total width of all borders, including |
---|
287 | * traversal highlight and 3-D border. |
---|
288 | * Indicates how much interior stuff must |
---|
289 | * be offset from outside edges to leave |
---|
290 | * room for borders. */ |
---|
291 | GC pixmapGC; /* Used to copy bits from a pixmap to the |
---|
292 | * screen and also to clear the pixmap. */ |
---|
293 | int width, height; /* Dimensions to request for canvas window, |
---|
294 | * specified in pixels. */ |
---|
295 | int redrawX1, redrawY1; /* Upper left corner of area to redraw, |
---|
296 | * in pixel coordinates. Border pixels |
---|
297 | * are included. Only valid if |
---|
298 | * REDRAW_PENDING flag is set. */ |
---|
299 | int redrawX2, redrawY2; /* Lower right corner of area to redraw, |
---|
300 | * in integer canvas coordinates. Border |
---|
301 | * pixels will *not* be redrawn. */ |
---|
302 | int confine; /* Non-zero means constrain view to keep |
---|
303 | * as much of canvas visible as possible. */ |
---|
304 | |
---|
305 | /* |
---|
306 | * Information used to manage the selection and insertion cursor: |
---|
307 | */ |
---|
308 | |
---|
309 | Tk_CanvasTextInfo textInfo; /* Contains lots of fields; see tk.h for |
---|
310 | * details. This structure is shared with |
---|
311 | * the code that implements individual items. */ |
---|
312 | int insertOnTime; /* Number of milliseconds cursor should spend |
---|
313 | * in "on" state for each blink. */ |
---|
314 | int insertOffTime; /* Number of milliseconds cursor should spend |
---|
315 | * in "off" state for each blink. */ |
---|
316 | Tcl_TimerToken insertBlinkHandler; |
---|
317 | /* Timer handler used to blink cursor on and |
---|
318 | * off. */ |
---|
319 | |
---|
320 | /* |
---|
321 | * Transformation applied to canvas as a whole: to compute screen |
---|
322 | * coordinates (X,Y) from canvas coordinates (x,y), do the following: |
---|
323 | * |
---|
324 | * X = x - xOrigin; |
---|
325 | * Y = y - yOrigin; |
---|
326 | */ |
---|
327 | |
---|
328 | int xOrigin, yOrigin; /* Canvas coordinates corresponding to |
---|
329 | * upper-left corner of window, given in |
---|
330 | * canvas pixel units. */ |
---|
331 | int drawableXOrigin, drawableYOrigin; |
---|
332 | /* During redisplay, these fields give the |
---|
333 | * canvas coordinates corresponding to |
---|
334 | * the upper-left corner of the drawable |
---|
335 | * where items are actually being drawn |
---|
336 | * (typically a pixmap smaller than the |
---|
337 | * whole window). */ |
---|
338 | |
---|
339 | /* |
---|
340 | * Information used for event bindings associated with items. |
---|
341 | */ |
---|
342 | |
---|
343 | Tk_BindingTable bindingTable; |
---|
344 | /* Table of all bindings currently defined |
---|
345 | * for this canvas. NULL means that no |
---|
346 | * bindings exist, so the table hasn't been |
---|
347 | * created. Each "object" used for this |
---|
348 | * table is either a Tk_Uid for a tag or |
---|
349 | * the address of an item named by id. */ |
---|
350 | Tk_Item *currentItemPtr; /* The item currently containing the mouse |
---|
351 | * pointer, or NULL if none. */ |
---|
352 | Tk_Item *newCurrentPtr; /* The item that is about to become the |
---|
353 | * current one, or NULL. This field is |
---|
354 | * used to detect deletions of the new |
---|
355 | * current item pointer that occur during |
---|
356 | * Leave processing of the previous current |
---|
357 | * item. */ |
---|
358 | double closeEnough; /* The mouse is assumed to be inside an |
---|
359 | * item if it is this close to it. */ |
---|
360 | XEvent pickEvent; /* The event upon which the current choice |
---|
361 | * of currentItem is based. Must be saved |
---|
362 | * so that if the currentItem is deleted, |
---|
363 | * can pick another. */ |
---|
364 | int state; /* Last known modifier state. Used to |
---|
365 | * defer picking a new current object |
---|
366 | * while buttons are down. */ |
---|
367 | |
---|
368 | /* |
---|
369 | * Information used for managing scrollbars: |
---|
370 | */ |
---|
371 | |
---|
372 | char *xScrollCmd; /* Command prefix for communicating with |
---|
373 | * horizontal scrollbar. NULL means no |
---|
374 | * horizontal scrollbar. Malloc'ed*/ |
---|
375 | char *yScrollCmd; /* Command prefix for communicating with |
---|
376 | * vertical scrollbar. NULL means no |
---|
377 | * vertical scrollbar. Malloc'ed*/ |
---|
378 | int scrollX1, scrollY1, scrollX2, scrollY2; |
---|
379 | /* These four coordinates define the region |
---|
380 | * that is the 100% area for scrolling (i.e. |
---|
381 | * these numbers determine the size and |
---|
382 | * location of the sliders on scrollbars). |
---|
383 | * Units are pixels in canvas coords. */ |
---|
384 | char *regionString; /* The option string from which scrollX1 |
---|
385 | * etc. are derived. Malloc'ed. */ |
---|
386 | int xScrollIncrement; /* If >0, defines a grid for horizontal |
---|
387 | * scrolling. This is the size of the "unit", |
---|
388 | * and the left edge of the screen will always |
---|
389 | * lie on an even unit boundary. */ |
---|
390 | int yScrollIncrement; /* If >0, defines a grid for horizontal |
---|
391 | * scrolling. This is the size of the "unit", |
---|
392 | * and the left edge of the screen will always |
---|
393 | * lie on an even unit boundary. */ |
---|
394 | |
---|
395 | /* |
---|
396 | * Information used for scanning: |
---|
397 | */ |
---|
398 | |
---|
399 | int scanX; /* X-position at which scan started (e.g. |
---|
400 | * button was pressed here). */ |
---|
401 | int scanXOrigin; /* Value of xOrigin field when scan started. */ |
---|
402 | int scanY; /* Y-position at which scan started (e.g. |
---|
403 | * button was pressed here). */ |
---|
404 | int scanYOrigin; /* Value of yOrigin field when scan started. */ |
---|
405 | |
---|
406 | /* |
---|
407 | * Information used to speed up searches by remembering the last item |
---|
408 | * created or found with an item id search. |
---|
409 | */ |
---|
410 | |
---|
411 | Tk_Item *hotPtr; /* Pointer to "hot" item (one that's been |
---|
412 | * recently used. NULL means there's no |
---|
413 | * hot item. */ |
---|
414 | Tk_Item *hotPrevPtr; /* Pointer to predecessor to hotPtr (NULL |
---|
415 | * means item is first in list). This is |
---|
416 | * only a hint and may not really be hotPtr's |
---|
417 | * predecessor. */ |
---|
418 | |
---|
419 | /* |
---|
420 | * Miscellaneous information: |
---|
421 | */ |
---|
422 | |
---|
423 | Tk_Cursor cursor; /* Current cursor for window, or None. */ |
---|
424 | char *takeFocus; /* Value of -takefocus option; not used in |
---|
425 | * the C code, but used by keyboard traversal |
---|
426 | * scripts. Malloc'ed, but may be NULL. */ |
---|
427 | double pixelsPerMM; /* Scale factor between MM and pixels; |
---|
428 | * used when converting coordinates. */ |
---|
429 | int flags; /* Various flags; see below for |
---|
430 | * definitions. */ |
---|
431 | int nextId; /* Number to use as id for next item |
---|
432 | * created in widget. */ |
---|
433 | Tk_PostscriptInfo psInfo; |
---|
434 | /* Pointer to information used for generating |
---|
435 | * Postscript for the canvas. NULL means |
---|
436 | * no Postscript is currently being |
---|
437 | * generated. */ |
---|
438 | Tcl_HashTable idTable; /* Table of integer indices. */ |
---|
439 | /* |
---|
440 | * Additional information, added by the 'dash'-patch |
---|
441 | */ |
---|
442 | VOID *reserved1; |
---|
443 | Tk_State canvas_state; /* state of canvas */ |
---|
444 | VOID *reserved2; |
---|
445 | VOID *reserved3; |
---|
446 | Tk_TSOffset tsoffset; |
---|
447 | #ifndef USE_OLD_TAG_SEARCH |
---|
448 | void *bindTagExprs; /* linked list of tag expressions used in bindings */ |
---|
449 | #endif |
---|
450 | } TkCanvas; |
---|
451 | |
---|
452 | /* Forward declarations. */ |
---|
453 | static void ComputeBbox(HotspotItem *itemPtr); |
---|
454 | |
---|
455 | |
---|
456 | static void |
---|
457 | EventuallyRedraw(HotspotItem *itemPtr) |
---|
458 | { |
---|
459 | if ((itemPtr->flags & REDRAW_PENDING) == 0) { |
---|
460 | itemPtr->flags |= REDRAW_PENDING; |
---|
461 | Tk_CanvasEventuallyRedraw(itemPtr->canvas, itemPtr->base.x1, |
---|
462 | itemPtr->base.y1, itemPtr->base.x2, itemPtr->base.y2); |
---|
463 | } |
---|
464 | } |
---|
465 | |
---|
466 | static int |
---|
467 | GetCoordFromObj(Tcl_Interp *interp, HotspotItem *itemPtr, Tcl_Obj *objPtr, |
---|
468 | double *valuePtr) |
---|
469 | { |
---|
470 | const char *string; |
---|
471 | |
---|
472 | string = Tcl_GetString(objPtr); |
---|
473 | return Tk_CanvasGetCoord(interp, itemPtr->canvas, string, valuePtr); |
---|
474 | } |
---|
475 | |
---|
476 | |
---|
477 | /* |
---|
478 | *---------------------------------------------------------------------- |
---|
479 | * |
---|
480 | * ValueInterpParseProc -- |
---|
481 | * |
---|
482 | * Converts the name of an axis to a pointer to its axis structure. |
---|
483 | * |
---|
484 | * Results: |
---|
485 | * The return value is a standard Tcl result. The axis flags are |
---|
486 | * written into the widget record. |
---|
487 | * |
---|
488 | *---------------------------------------------------------------------- |
---|
489 | */ |
---|
490 | /*ARGSUSED*/ |
---|
491 | static int |
---|
492 | ValueInterpParseProc(ClientData clientData, Tcl_Interp *interp, Tk_Window tkwin, |
---|
493 | CONST84 char *string, char *widgRec, int offset) |
---|
494 | { |
---|
495 | HotspotItem *itemPtr = (HotspotItem *)widgRec; |
---|
496 | |
---|
497 | if (string[0] == '\0') { |
---|
498 | itemPtr->valueInterp = NULL; |
---|
499 | return TCL_OK; |
---|
500 | } |
---|
501 | itemPtr->valueInterp = Tcl_GetSlave(interp, string); |
---|
502 | if (itemPtr->valueInterp == NULL) { |
---|
503 | return TCL_ERROR; |
---|
504 | } |
---|
505 | return TCL_OK; |
---|
506 | } |
---|
507 | |
---|
508 | /* |
---|
509 | *---------------------------------------------------------------------- |
---|
510 | * |
---|
511 | * ValueInterpParseProc -- |
---|
512 | * |
---|
513 | * Convert the window coordinates into a string. |
---|
514 | * |
---|
515 | * Results: |
---|
516 | * The string representing the coordinate position is returned. |
---|
517 | * |
---|
518 | *---------------------------------------------------------------------- |
---|
519 | */ |
---|
520 | /*ARGSUSED*/ |
---|
521 | static char * |
---|
522 | ValueInterpPrintProc(ClientData clientData, Tk_Window tkwin, char *widgRec, |
---|
523 | int offset, Tcl_FreeProc **freeProcPtr) |
---|
524 | { |
---|
525 | HotspotItem *itemPtr = (HotspotItem *)widgRec; |
---|
526 | |
---|
527 | if (itemPtr->valueInterp != NULL) { |
---|
528 | Tcl_Obj *objPtr; |
---|
529 | |
---|
530 | Tcl_GetInterpPath(itemPtr->interp, itemPtr->valueInterp); |
---|
531 | objPtr = Tcl_GetObjResult(itemPtr->interp); |
---|
532 | *freeProcPtr = TCL_VOLATILE; |
---|
533 | return Tcl_GetString(objPtr); |
---|
534 | } |
---|
535 | return ""; |
---|
536 | } |
---|
537 | |
---|
538 | /* |
---|
539 | *--------------------------------------------------------------------------- |
---|
540 | * |
---|
541 | * TraceVarProc -- |
---|
542 | * |
---|
543 | * This procedure is invoked when someone changes the state variable |
---|
544 | * associated with a radiobutton or checkbutton entry. The entry's |
---|
545 | * selected state is set to match the value of the variable. |
---|
546 | * |
---|
547 | * Results: |
---|
548 | * NULL is always returned. |
---|
549 | * |
---|
550 | * Side effects: |
---|
551 | * The combobox entry may become selected or deselected. |
---|
552 | * |
---|
553 | *--------------------------------------------------------------------------- |
---|
554 | */ |
---|
555 | static char * |
---|
556 | TraceVarProc( |
---|
557 | ClientData clientData, /* Information about the item. */ |
---|
558 | Tcl_Interp *interp, /* Interpreter containing variable. */ |
---|
559 | const char *name1, /* First part of variable's name. */ |
---|
560 | const char *name2, /* Second part of variable's name. */ |
---|
561 | int flags) /* Describes what just happened. */ |
---|
562 | { |
---|
563 | HotspotItem *itemPtr = clientData; |
---|
564 | |
---|
565 | if (flags & TCL_INTERP_DESTROYED) { |
---|
566 | return NULL; /* Interpreter is going away. */ |
---|
567 | |
---|
568 | } |
---|
569 | /* |
---|
570 | * If the variable is being unset, then re-establish the trace. |
---|
571 | */ |
---|
572 | if (flags & TCL_TRACE_UNSETS) { |
---|
573 | if (flags & TCL_TRACE_DESTROYED) { |
---|
574 | Tcl_TraceVar(interp, name1, VAR_FLAGS, TraceVarProc, itemPtr); |
---|
575 | } |
---|
576 | } |
---|
577 | itemPtr->flags |= SUBSTITUTIONS_PENDING; |
---|
578 | EventuallyRedraw(itemPtr); |
---|
579 | return NULL; /* Done. */ |
---|
580 | } |
---|
581 | |
---|
582 | /* |
---|
583 | * ---------------------------------------------------------------------- |
---|
584 | * |
---|
585 | * ImageChangedProc -- |
---|
586 | * |
---|
587 | * This procedure is invoked by the image code whenever the manager |
---|
588 | * for an image does something that affects the image's size or |
---|
589 | * how it is displayed. |
---|
590 | * |
---|
591 | * Results: |
---|
592 | * None. |
---|
593 | * |
---|
594 | * Side effects: |
---|
595 | * Arranges for the canvas to get redisplayed. |
---|
596 | * |
---|
597 | * ---------------------------------------------------------------------- |
---|
598 | */ |
---|
599 | static void |
---|
600 | ImageChangedProc(clientData, x, y, width, height, imgWidth, imgHeight) |
---|
601 | ClientData clientData; /* Pointer to canvas item for image. */ |
---|
602 | int x, y; /* Upper left pixel (within image) |
---|
603 | * that must be redisplayed. */ |
---|
604 | int width, height; /* Dimensions of area to redisplay |
---|
605 | * (may be <= 0). */ |
---|
606 | int imgWidth, imgHeight; /* New dimensions of image. */ |
---|
607 | { |
---|
608 | HotspotItem *itemPtr = (HotspotItem *) clientData; |
---|
609 | |
---|
610 | itemPtr->flags |= LAYOUT_PENDING; |
---|
611 | EventuallyRedraw(itemPtr); |
---|
612 | return; |
---|
613 | } |
---|
614 | |
---|
615 | static void |
---|
616 | AddTextSegment(HotspotItem *itemPtr, const char *text, int length) |
---|
617 | { |
---|
618 | ItemSegment *segPtr; |
---|
619 | |
---|
620 | segPtr = (ItemSegment *)Tcl_Alloc(sizeof(ItemSegment) + length + 1); |
---|
621 | memset(segPtr, 0, sizeof(ItemSegment) + length + 1); |
---|
622 | segPtr->itemPtr = itemPtr; |
---|
623 | segPtr->lineNum = itemPtr->numLines; |
---|
624 | segPtr->type = SEGMENT_TEXT; |
---|
625 | strncpy(segPtr->text, text, length); |
---|
626 | segPtr->text[length] = '\0'; |
---|
627 | segPtr->length = length; |
---|
628 | segPtr->x = segPtr->y = 0; |
---|
629 | segPtr->nextPtr = NULL; |
---|
630 | if (itemPtr->firstPtr == NULL) { |
---|
631 | itemPtr->firstPtr = segPtr; |
---|
632 | } |
---|
633 | if (itemPtr->lastPtr == NULL) { |
---|
634 | itemPtr->lastPtr = segPtr; |
---|
635 | } else { |
---|
636 | itemPtr->lastPtr->nextPtr = segPtr; |
---|
637 | itemPtr->lastPtr = segPtr; |
---|
638 | } |
---|
639 | } |
---|
640 | |
---|
641 | static void |
---|
642 | AddValueSegment(HotspotItem *itemPtr, const char *varName, int length) |
---|
643 | { |
---|
644 | ItemSegment *segPtr; |
---|
645 | Tcl_Interp *interp; |
---|
646 | |
---|
647 | segPtr = (ItemSegment *)Tcl_Alloc(sizeof(ItemSegment) + length + 1); |
---|
648 | memset(segPtr, 0, sizeof(ItemSegment) + length + 1); |
---|
649 | segPtr->itemPtr = itemPtr; |
---|
650 | segPtr->lineNum = itemPtr->numLines; |
---|
651 | segPtr->type = SEGMENT_VALUE; |
---|
652 | strncpy(segPtr->text, varName, length); |
---|
653 | segPtr->text[length] = '\0'; |
---|
654 | segPtr->length = length; |
---|
655 | interp = itemPtr->valueInterp; |
---|
656 | if (interp == NULL) { |
---|
657 | interp = itemPtr->interp; |
---|
658 | } |
---|
659 | Tcl_TraceVar(interp, varName, VAR_FLAGS, TraceVarProc, itemPtr); |
---|
660 | segPtr->value = NULL; |
---|
661 | segPtr->x = segPtr->y = 0; |
---|
662 | segPtr->nextPtr = NULL; |
---|
663 | if (itemPtr->firstPtr == NULL) { |
---|
664 | itemPtr->firstPtr = segPtr; |
---|
665 | } |
---|
666 | if (itemPtr->lastPtr == NULL) { |
---|
667 | itemPtr->lastPtr = segPtr; |
---|
668 | } else { |
---|
669 | itemPtr->lastPtr->nextPtr = segPtr; |
---|
670 | itemPtr->lastPtr = segPtr; |
---|
671 | } |
---|
672 | } |
---|
673 | |
---|
674 | static void |
---|
675 | DestroySegment(ItemSegment *segPtr) |
---|
676 | { |
---|
677 | HotspotItem *itemPtr; |
---|
678 | |
---|
679 | itemPtr = segPtr->itemPtr; |
---|
680 | if (segPtr->layout != NULL) { |
---|
681 | Tk_FreeTextLayout(segPtr->layout); |
---|
682 | } |
---|
683 | if (segPtr->type == SEGMENT_VALUE) { |
---|
684 | Tcl_UntraceVar(itemPtr->interp, segPtr->text, VAR_FLAGS, TraceVarProc, |
---|
685 | itemPtr); |
---|
686 | } |
---|
687 | if (segPtr->value != NULL) { |
---|
688 | Tcl_Free((char *)segPtr->value); |
---|
689 | } |
---|
690 | Tcl_Free((char *)segPtr); |
---|
691 | } |
---|
692 | |
---|
693 | static void |
---|
694 | DestroySegments(HotspotItem *itemPtr) |
---|
695 | { |
---|
696 | ItemSegment *segPtr, *nextPtr; |
---|
697 | |
---|
698 | for (segPtr = itemPtr->firstPtr; segPtr != NULL; segPtr = nextPtr) { |
---|
699 | nextPtr = segPtr->nextPtr; |
---|
700 | DestroySegment(segPtr); |
---|
701 | } |
---|
702 | itemPtr->firstPtr = itemPtr->lastPtr = NULL; |
---|
703 | } |
---|
704 | |
---|
705 | static void |
---|
706 | DoSubstitutions(HotspotItem *itemPtr) |
---|
707 | { |
---|
708 | ItemSegment *segPtr; |
---|
709 | Tcl_Interp *interp; |
---|
710 | |
---|
711 | itemPtr->flags &= ~SUBSTITUTIONS_PENDING; |
---|
712 | interp = itemPtr->valueInterp; |
---|
713 | if (interp == NULL) { |
---|
714 | interp = itemPtr->interp; |
---|
715 | } |
---|
716 | for (segPtr = itemPtr->firstPtr; segPtr != NULL; segPtr = segPtr->nextPtr) { |
---|
717 | const char *value; |
---|
718 | |
---|
719 | if (segPtr->type != SEGMENT_VALUE) { |
---|
720 | continue; |
---|
721 | } |
---|
722 | value = Tcl_GetVar(interp, segPtr->text, TCL_GLOBAL_ONLY); |
---|
723 | if (value == NULL) { |
---|
724 | value = "???"; |
---|
725 | } |
---|
726 | if (segPtr->value != NULL) { |
---|
727 | Tcl_Free((char *)segPtr->value); |
---|
728 | } |
---|
729 | segPtr->value = Tcl_Alloc(strlen(value) + 1); |
---|
730 | strcpy(segPtr->value, value); |
---|
731 | } |
---|
732 | itemPtr->flags |= LAYOUT_PENDING; |
---|
733 | } |
---|
734 | |
---|
735 | static void |
---|
736 | ComputeGeometry(HotspotItem *itemPtr) |
---|
737 | { |
---|
738 | int x, y, w, h; |
---|
739 | ItemSegment *segPtr; |
---|
740 | int imgWidth, imgHeight; |
---|
741 | int maxWidth, maxHeight; |
---|
742 | int justify; |
---|
743 | int wrapLength; |
---|
744 | int flags; |
---|
745 | |
---|
746 | justify = TK_JUSTIFY_LEFT; |
---|
747 | wrapLength = 0; |
---|
748 | flags = 0; |
---|
749 | /* Collect the individual segment sizes. */ |
---|
750 | imgWidth = itemPtr->maxImageWidth + IMAGE_PAD; |
---|
751 | imgHeight = itemPtr->maxImageHeight + 2 * IMAGE_PAD; |
---|
752 | for (segPtr = itemPtr->firstPtr; segPtr != NULL; segPtr = segPtr->nextPtr) { |
---|
753 | if (segPtr->layout != NULL) { |
---|
754 | Tk_FreeTextLayout(segPtr->layout); |
---|
755 | } |
---|
756 | w = h = 0; |
---|
757 | switch (segPtr->type) { |
---|
758 | case SEGMENT_VALUE: |
---|
759 | segPtr->layout = Tk_ComputeTextLayout(itemPtr->valueFont, |
---|
760 | segPtr->value, -1, wrapLength, justify, flags, &w, &h); |
---|
761 | w += TEXT_PADX * 2; |
---|
762 | h += TEXT_PADY * 2; |
---|
763 | if (itemPtr->showIcons) { |
---|
764 | w += imgWidth, h = MAX(h, imgHeight); |
---|
765 | } |
---|
766 | break; |
---|
767 | case SEGMENT_TEXT: |
---|
768 | segPtr->layout = Tk_ComputeTextLayout(itemPtr->font, segPtr->text, |
---|
769 | -1, wrapLength, justify, flags, &w, &h); |
---|
770 | w += TEXT_PADX * 2; |
---|
771 | h += TEXT_PADY * 2; |
---|
772 | break; |
---|
773 | default: |
---|
774 | Tcl_Panic("unknown hotspot segment type"); |
---|
775 | } |
---|
776 | segPtr->width = w; |
---|
777 | segPtr->height = h; |
---|
778 | #ifdef notdef |
---|
779 | fprintf(stderr, "ComputeGeometry: item=(%s) segment=%x (%s) type=%d w=%d h=%d lineNum=%d nextPtr=%x\n", |
---|
780 | itemPtr->text, segPtr, segPtr->text, segPtr->type, segPtr->width, |
---|
781 | segPtr->height, segPtr->lineNum, segPtr->nextPtr); |
---|
782 | #endif |
---|
783 | } |
---|
784 | /* Compute the relative positions of the segments. */ |
---|
785 | x = y = 0; |
---|
786 | maxWidth = maxHeight = 0; |
---|
787 | for (segPtr = itemPtr->firstPtr; segPtr != NULL; /*empty*/) { |
---|
788 | int maxHeight, lineNum; |
---|
789 | ItemSegment *startPtr; |
---|
790 | |
---|
791 | maxHeight = 0; |
---|
792 | lineNum = segPtr->lineNum; |
---|
793 | startPtr = segPtr; |
---|
794 | x = 0; |
---|
795 | |
---|
796 | while ((segPtr != NULL) && (segPtr->lineNum == lineNum)) { |
---|
797 | if (segPtr->height > maxHeight) { |
---|
798 | maxHeight = segPtr->height; |
---|
799 | } |
---|
800 | segPtr->x = x; |
---|
801 | x += segPtr->width; |
---|
802 | segPtr = segPtr->nextPtr; |
---|
803 | } |
---|
804 | if (x > maxWidth) { |
---|
805 | maxWidth = x; |
---|
806 | } |
---|
807 | segPtr = startPtr; |
---|
808 | while ((segPtr != NULL) && (segPtr->lineNum == lineNum)) { |
---|
809 | segPtr->y = y + (maxHeight - segPtr->height) / 2; |
---|
810 | segPtr = segPtr->nextPtr; |
---|
811 | } |
---|
812 | y += maxHeight; |
---|
813 | } |
---|
814 | itemPtr->width = maxWidth; |
---|
815 | itemPtr->height = y; |
---|
816 | ComputeBbox(itemPtr); |
---|
817 | } |
---|
818 | |
---|
819 | static int |
---|
820 | ParseDescription(Tcl_Interp *interp, HotspotItem *itemPtr, |
---|
821 | const char *description) |
---|
822 | { |
---|
823 | const char *p, *start; |
---|
824 | |
---|
825 | if (itemPtr->firstPtr != NULL) { |
---|
826 | DestroySegments(itemPtr); |
---|
827 | } |
---|
828 | start = description; |
---|
829 | itemPtr->numLines = 1; |
---|
830 | for (p = description; *p != '\0'; p++) { |
---|
831 | if ((p[0] == '$') && (p[1] == '{')) { |
---|
832 | const char *varName; |
---|
833 | const char *q; |
---|
834 | |
---|
835 | if (p > start) { |
---|
836 | /* Save the leading text. */ |
---|
837 | AddTextSegment(itemPtr, start, p - start); |
---|
838 | start = p; |
---|
839 | } |
---|
840 | /* Start of substitution variable. */ |
---|
841 | varName = p + 2; |
---|
842 | for (q = varName; q != '\0'; q++) { |
---|
843 | if (*q == '}') { |
---|
844 | AddValueSegment(itemPtr, varName, q - varName); |
---|
845 | p = q; |
---|
846 | start = p + 1; |
---|
847 | goto next; |
---|
848 | } |
---|
849 | } |
---|
850 | Tcl_AppendResult(interp, "back variable specification in \"", |
---|
851 | description, "\"", (char *)NULL); |
---|
852 | return TCL_ERROR; |
---|
853 | } else if (p[0] == '\n') { |
---|
854 | if (p > start) { |
---|
855 | AddTextSegment(itemPtr, start, p - start); |
---|
856 | start = p + 1; |
---|
857 | itemPtr->numLines++; |
---|
858 | } |
---|
859 | } |
---|
860 | next: |
---|
861 | ; |
---|
862 | } |
---|
863 | if (p > start) { |
---|
864 | /* Save the trailing text. */ |
---|
865 | AddTextSegment(itemPtr, start, p - start); |
---|
866 | } |
---|
867 | itemPtr->flags |= SUBSTITUTIONS_PENDING; |
---|
868 | return TCL_OK; |
---|
869 | } |
---|
870 | |
---|
871 | /* |
---|
872 | * ------------------------------------------------------------------------ |
---|
873 | * |
---|
874 | * CreateProc -- |
---|
875 | * |
---|
876 | * This procedure is invoked to create a new hotspot item |
---|
877 | * in a canvas. A hotspot is a text item with a box behind it. |
---|
878 | * |
---|
879 | * Results: |
---|
880 | * A standard Tcl return name. If an error occurred in creating |
---|
881 | * the item then an error message is left in the interp's result. |
---|
882 | * In this case itemPtr is left uninitialized so it can be safely |
---|
883 | * freed by the caller. |
---|
884 | * |
---|
885 | * Side effects: |
---|
886 | * A new item is created. |
---|
887 | * |
---|
888 | * ------------------------------------------------------------------------ |
---|
889 | */ |
---|
890 | static int |
---|
891 | CreateProc(Tcl_Interp *interp, Tk_Canvas canvas, Tk_Item *canvItemPtr, |
---|
892 | int objc, Tcl_Obj *const *objv) |
---|
893 | { |
---|
894 | HotspotItem *itemPtr = (HotspotItem *)canvItemPtr; |
---|
895 | double x, y; |
---|
896 | Tk_Window tkwin; |
---|
897 | |
---|
898 | tkwin = Tk_CanvasTkwin(canvas); |
---|
899 | if (objc < 2) { |
---|
900 | Tcl_AppendResult(interp, "wrong # args: should be \"", |
---|
901 | Tk_PathName(tkwin), " create ", canvItemPtr->typePtr->name, |
---|
902 | " x y ?options?\"", (char *)NULL); |
---|
903 | return TCL_ERROR; |
---|
904 | } |
---|
905 | |
---|
906 | /* Initialize all HotspotItem fields, expect for the the base. */ |
---|
907 | memset((char *)itemPtr + sizeof(Tk_Item), 0, |
---|
908 | sizeof(HotspotItem) - sizeof(Tk_Item)); |
---|
909 | /* |
---|
910 | * Carry out initialization that is needed in order to clean up after |
---|
911 | * errors during the the remainder of this procedure. |
---|
912 | */ |
---|
913 | itemPtr->anchor = TK_ANCHOR_CENTER; |
---|
914 | itemPtr->canvas = canvas; |
---|
915 | itemPtr->display = Tk_Display(tkwin); |
---|
916 | itemPtr->interp = interp; |
---|
917 | itemPtr->relief = TK_RELIEF_FLAT; |
---|
918 | itemPtr->tkwin = tkwin; |
---|
919 | itemPtr->valueInterp = interp; |
---|
920 | itemPtr->showIcons = FALSE; |
---|
921 | |
---|
922 | /* Process the arguments to fill in the item record. */ |
---|
923 | if ((GetCoordFromObj(interp, itemPtr, objv[0], &x) != TCL_OK) || |
---|
924 | (GetCoordFromObj(interp, itemPtr, objv[1], &y) != TCL_OK)) { |
---|
925 | DeleteProc(canvas, canvItemPtr, itemPtr->display); |
---|
926 | return TCL_ERROR; |
---|
927 | } |
---|
928 | itemPtr->x = x; |
---|
929 | itemPtr->y = y; |
---|
930 | ComputeBbox(itemPtr); |
---|
931 | if (ConfigureProc(interp, canvas, canvItemPtr, objc - 2, objv + 2, 0) |
---|
932 | != TCL_OK) { |
---|
933 | DeleteProc(canvas, canvItemPtr, itemPtr->display); |
---|
934 | return TCL_ERROR; |
---|
935 | } |
---|
936 | return TCL_OK; |
---|
937 | } |
---|
938 | |
---|
939 | /* |
---|
940 | * ------------------------------------------------------------------------ |
---|
941 | * |
---|
942 | * CoordsProc -- |
---|
943 | * |
---|
944 | * This procedure is invoked to process the "coords" widget command on |
---|
945 | * hotspot items. See the user documentation for details on what |
---|
946 | * it does. |
---|
947 | * |
---|
948 | * Results: |
---|
949 | * Returns TCL_OK or TCL_ERROR, and sets the interp's result. |
---|
950 | * |
---|
951 | * Side effects: |
---|
952 | * The coordinates for the given item may be changed. |
---|
953 | * |
---|
954 | * ------------------------------------------------------------------------ |
---|
955 | */ |
---|
956 | static int |
---|
957 | CoordsProc(Tcl_Interp *interp, Tk_Canvas canvas, Tk_Item *canvItemPtr, |
---|
958 | int objc, Tcl_Obj *const *objv) |
---|
959 | { |
---|
960 | HotspotItem *itemPtr = (HotspotItem *) canvItemPtr; |
---|
961 | double x, y; |
---|
962 | |
---|
963 | if (objc == 0) { |
---|
964 | Tcl_Obj *listObjPtr; |
---|
965 | |
---|
966 | listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); |
---|
967 | Tcl_ListObjAppendElement(interp, listObjPtr, |
---|
968 | Tcl_NewDoubleObj(itemPtr->x)); |
---|
969 | Tcl_ListObjAppendElement(interp, listObjPtr, |
---|
970 | Tcl_NewDoubleObj(itemPtr->y)); |
---|
971 | Tcl_SetObjResult(interp, listObjPtr); |
---|
972 | return TCL_OK; |
---|
973 | } |
---|
974 | if (objc == 1) { |
---|
975 | int elc; |
---|
976 | Tcl_Obj **elv; |
---|
977 | |
---|
978 | if (Tcl_ListObjGetElements(interp, objv[0], &elc, &elv) != TCL_OK) { |
---|
979 | return TCL_ERROR; |
---|
980 | } |
---|
981 | if ((GetCoordFromObj(interp, itemPtr, elv[0], &x) != TCL_OK) || |
---|
982 | (GetCoordFromObj(interp, itemPtr, elv[1], &y) != TCL_OK)) { |
---|
983 | return TCL_ERROR; |
---|
984 | } |
---|
985 | } else if (objc == 3) { |
---|
986 | if ((GetCoordFromObj(interp, itemPtr, objv[0], &x) != TCL_OK) || |
---|
987 | (GetCoordFromObj(interp, itemPtr, objv[1], &y) != TCL_OK)) { |
---|
988 | return TCL_ERROR; |
---|
989 | } |
---|
990 | } else { |
---|
991 | char buf[64 + TCL_INTEGER_SPACE]; |
---|
992 | |
---|
993 | sprintf(buf, "wrong # coordinates: expected 0 or 2, got %d", objc); |
---|
994 | Tcl_AppendResult(interp, buf, (char *)NULL); |
---|
995 | return TCL_ERROR; |
---|
996 | } |
---|
997 | itemPtr->x = x; |
---|
998 | itemPtr->y = y; |
---|
999 | ComputeBbox(itemPtr); |
---|
1000 | return TCL_OK; |
---|
1001 | } |
---|
1002 | |
---|
1003 | /* |
---|
1004 | * ------------------------------------------------------------------------ |
---|
1005 | * ConfigureProc -- |
---|
1006 | * |
---|
1007 | * This procedure is invoked to configure various aspects of a |
---|
1008 | * hotspot item, such as its color and font. |
---|
1009 | * |
---|
1010 | * Results: |
---|
1011 | * A standard Tcl result code. If an error occurs, then an error |
---|
1012 | * message is left in the interp's result. |
---|
1013 | * |
---|
1014 | * Side effects: |
---|
1015 | * Configuration information, such as colors and GCs, may be set |
---|
1016 | * for itemPtr. |
---|
1017 | * ------------------------------------------------------------------------ |
---|
1018 | */ |
---|
1019 | static int |
---|
1020 | ConfigureProc(Tcl_Interp *interp, Tk_Canvas canvas, Tk_Item *canvItemPtr, |
---|
1021 | int objc, Tcl_Obj *CONST *objv, int flags) |
---|
1022 | { |
---|
1023 | HotspotItem *itemPtr = (HotspotItem *)canvItemPtr; |
---|
1024 | XGCValues gcValues; |
---|
1025 | GC newGC; |
---|
1026 | unsigned long mask; |
---|
1027 | Tk_Window tkwin; |
---|
1028 | Tk_Image tkImage; |
---|
1029 | |
---|
1030 | tkwin = Tk_CanvasTkwin(canvas); |
---|
1031 | if (Tk_ConfigureWidget(interp, tkwin, configSpecs, objc, |
---|
1032 | (CONST char **)objv, (char*)itemPtr, flags|TK_CONFIG_OBJS) != TCL_OK) { |
---|
1033 | return TCL_ERROR; |
---|
1034 | } |
---|
1035 | |
---|
1036 | /* Normal text GC. */ |
---|
1037 | mask = GCFont | GCForeground; |
---|
1038 | gcValues.font = Tk_FontId(itemPtr->font); |
---|
1039 | gcValues.foreground = itemPtr->textColor->pixel; |
---|
1040 | newGC = Tk_GetGC(tkwin, mask, &gcValues); |
---|
1041 | if (itemPtr->normalGC != None) { |
---|
1042 | Tk_FreeGC(Tk_Display(tkwin), itemPtr->normalGC); |
---|
1043 | } |
---|
1044 | itemPtr->normalGC = newGC; |
---|
1045 | |
---|
1046 | /* Outline GC */ |
---|
1047 | if (itemPtr->outlineColor != NULL) { |
---|
1048 | mask = GCLineWidth | GCForeground; |
---|
1049 | gcValues.foreground = itemPtr->outlineColor->pixel; |
---|
1050 | gcValues.line_width = 1; |
---|
1051 | newGC = Tk_GetGC(tkwin, mask, &gcValues); |
---|
1052 | if (itemPtr->outlineGC != None) { |
---|
1053 | Tk_FreeGC(Tk_Display(tkwin), itemPtr->outlineGC); |
---|
1054 | } |
---|
1055 | itemPtr->outlineGC = newGC; |
---|
1056 | } |
---|
1057 | |
---|
1058 | /* Substituted value GC. */ |
---|
1059 | mask = GCFont | GCForeground; |
---|
1060 | gcValues.font = Tk_FontId(itemPtr->valueFont); |
---|
1061 | gcValues.foreground = itemPtr->valueColor->pixel; |
---|
1062 | newGC = Tk_GetGC(tkwin, mask, &gcValues); |
---|
1063 | if (itemPtr->valueGC != None) { |
---|
1064 | Tk_FreeGC(Tk_Display(tkwin), itemPtr->valueGC); |
---|
1065 | } |
---|
1066 | itemPtr->valueGC = newGC; |
---|
1067 | |
---|
1068 | /* Active substituted value GC. */ |
---|
1069 | mask = GCFont | GCForeground; |
---|
1070 | gcValues.font = Tk_FontId(itemPtr->valueFont); |
---|
1071 | gcValues.foreground = itemPtr->activeColor->pixel; |
---|
1072 | newGC = Tk_GetGC(tkwin, mask, &gcValues); |
---|
1073 | if (itemPtr->activeGC != None) { |
---|
1074 | Tk_FreeGC(Tk_Display(tkwin), itemPtr->activeGC); |
---|
1075 | } |
---|
1076 | itemPtr->activeGC = newGC; |
---|
1077 | |
---|
1078 | itemPtr->maxImageWidth = itemPtr->maxImageHeight = 0; |
---|
1079 | /* Normal hotspot image. */ |
---|
1080 | if (itemPtr->imageName != NULL) { |
---|
1081 | int iw, ih; |
---|
1082 | |
---|
1083 | tkImage = Tk_GetImage(interp, tkwin, itemPtr->imageName, |
---|
1084 | ImageChangedProc, (ClientData)itemPtr); |
---|
1085 | if (tkImage == NULL) { |
---|
1086 | return TCL_ERROR; |
---|
1087 | } |
---|
1088 | Tk_SizeOfImage(tkImage, &iw, &ih); |
---|
1089 | if (iw > itemPtr->maxImageWidth) { |
---|
1090 | itemPtr->maxImageWidth = iw; |
---|
1091 | } |
---|
1092 | if (ih > itemPtr->maxImageHeight) { |
---|
1093 | itemPtr->maxImageHeight = ih; |
---|
1094 | } |
---|
1095 | } else { |
---|
1096 | tkImage = NULL; |
---|
1097 | } |
---|
1098 | |
---|
1099 | if (itemPtr->image != NULL) { |
---|
1100 | Tk_FreeImage(itemPtr->image); |
---|
1101 | } |
---|
1102 | itemPtr->image = tkImage; |
---|
1103 | |
---|
1104 | /* Active hotspot image. */ |
---|
1105 | if (itemPtr->activeImageName != NULL) { |
---|
1106 | int iw, ih; |
---|
1107 | |
---|
1108 | tkImage = Tk_GetImage(interp, tkwin, itemPtr->activeImageName, |
---|
1109 | ImageChangedProc, (ClientData)itemPtr); |
---|
1110 | if (tkImage == NULL) { |
---|
1111 | return TCL_ERROR; |
---|
1112 | } |
---|
1113 | Tk_SizeOfImage(tkImage, &iw, &ih); |
---|
1114 | if (iw > itemPtr->maxImageWidth) { |
---|
1115 | itemPtr->maxImageWidth = iw; |
---|
1116 | } |
---|
1117 | if (ih > itemPtr->maxImageHeight) { |
---|
1118 | itemPtr->maxImageHeight = ih; |
---|
1119 | } |
---|
1120 | } else { |
---|
1121 | tkImage = NULL; |
---|
1122 | } |
---|
1123 | if (itemPtr->activeImage != NULL) { |
---|
1124 | Tk_FreeImage(itemPtr->activeImage); |
---|
1125 | } |
---|
1126 | itemPtr->activeImage = tkImage; |
---|
1127 | |
---|
1128 | if (itemPtr->text != NULL) { |
---|
1129 | if (ParseDescription(interp, itemPtr, itemPtr->text) != TCL_OK) { |
---|
1130 | return TCL_ERROR; |
---|
1131 | } |
---|
1132 | } |
---|
1133 | ComputeBbox(itemPtr); |
---|
1134 | return TCL_OK; |
---|
1135 | } |
---|
1136 | |
---|
1137 | /* |
---|
1138 | * ------------------------------------------------------------------------ |
---|
1139 | * DeleteHotspot -- |
---|
1140 | * |
---|
1141 | * This procedure is called to clean up the data structure |
---|
1142 | * associated with a hotspot item. |
---|
1143 | * |
---|
1144 | * Results: |
---|
1145 | * None. |
---|
1146 | * |
---|
1147 | * Side effects: |
---|
1148 | * Resources associated with itemPtr are released. |
---|
1149 | * ------------------------------------------------------------------------ |
---|
1150 | */ |
---|
1151 | static void |
---|
1152 | DeleteProc(Tk_Canvas canvas, Tk_Item *canvItemPtr, Display *display) |
---|
1153 | { |
---|
1154 | HotspotItem *itemPtr = (HotspotItem *)canvItemPtr; |
---|
1155 | |
---|
1156 | if (itemPtr->textColor != NULL) { |
---|
1157 | Tk_FreeColor(itemPtr->textColor); |
---|
1158 | } |
---|
1159 | if (itemPtr->valueColor != NULL) { |
---|
1160 | Tk_FreeColor(itemPtr->valueColor); |
---|
1161 | } |
---|
1162 | if (itemPtr->activeColor != NULL) { |
---|
1163 | Tk_FreeColor(itemPtr->activeColor); |
---|
1164 | } |
---|
1165 | if (itemPtr->outlineColor != NULL) { |
---|
1166 | Tk_FreeColor(itemPtr->outlineColor); |
---|
1167 | } |
---|
1168 | if (itemPtr->border != NULL) { |
---|
1169 | Tk_Free3DBorder(itemPtr->border); |
---|
1170 | } |
---|
1171 | if (itemPtr->activeBorder != NULL) { |
---|
1172 | Tk_Free3DBorder(itemPtr->activeBorder); |
---|
1173 | } |
---|
1174 | if (itemPtr->text != NULL) { |
---|
1175 | Tcl_Free((char *)itemPtr->text); |
---|
1176 | } |
---|
1177 | if (itemPtr->imageName != NULL) { |
---|
1178 | Tcl_Free((char *)itemPtr->imageName); |
---|
1179 | } |
---|
1180 | if (itemPtr->activeImageName != NULL) { |
---|
1181 | Tcl_Free((char *)itemPtr->activeImageName); |
---|
1182 | } |
---|
1183 | if (itemPtr->image != NULL ) { |
---|
1184 | Tk_FreeImage(itemPtr->image); |
---|
1185 | } |
---|
1186 | if (itemPtr->activeImage != NULL ) { |
---|
1187 | Tk_FreeImage(itemPtr->activeImage); |
---|
1188 | } |
---|
1189 | if (itemPtr->font != NULL) { |
---|
1190 | Tk_FreeFont(itemPtr->font); |
---|
1191 | } |
---|
1192 | if (itemPtr->valueFont != NULL) { |
---|
1193 | Tk_FreeFont(itemPtr->valueFont); |
---|
1194 | } |
---|
1195 | if (itemPtr->normalGC != None) { |
---|
1196 | Tk_FreeGC(display, itemPtr->normalGC); |
---|
1197 | } |
---|
1198 | if (itemPtr->activeGC != None) { |
---|
1199 | Tk_FreeGC(display, itemPtr->activeGC); |
---|
1200 | } |
---|
1201 | if (itemPtr->outlineGC != None) { |
---|
1202 | Tk_FreeGC(display, itemPtr->outlineGC); |
---|
1203 | } |
---|
1204 | if (itemPtr->valueGC != None) { |
---|
1205 | Tk_FreeGC(display, itemPtr->valueGC); |
---|
1206 | } |
---|
1207 | DestroySegments(itemPtr); |
---|
1208 | } |
---|
1209 | |
---|
1210 | |
---|
1211 | /* |
---|
1212 | *--------------------------------------------------------------------------- |
---|
1213 | * |
---|
1214 | * TranslateAnchor -- |
---|
1215 | * |
---|
1216 | * Translate the coordinates of a given bounding box based upon the |
---|
1217 | * anchor specified. The anchor indicates where the given xy position |
---|
1218 | * is in relation to the bounding box. |
---|
1219 | * |
---|
1220 | * nw --- n --- ne |
---|
1221 | * | | x,y ---+ |
---|
1222 | * w center e | | |
---|
1223 | * | | +-----+ |
---|
1224 | * sw --- s --- se |
---|
1225 | * |
---|
1226 | * Results: |
---|
1227 | * The translated coordinates of the bounding box are returned. |
---|
1228 | * |
---|
1229 | *--------------------------------------------------------------------------- |
---|
1230 | */ |
---|
1231 | static void |
---|
1232 | TranslateAnchor(HotspotItem *itemPtr, double *xPtr, double *yPtr) |
---|
1233 | { |
---|
1234 | double x, y; |
---|
1235 | |
---|
1236 | x = *xPtr, y = *yPtr; |
---|
1237 | switch (itemPtr->anchor) { |
---|
1238 | case TK_ANCHOR_NW: /* Upper left corner */ |
---|
1239 | break; |
---|
1240 | case TK_ANCHOR_W: /* Left center */ |
---|
1241 | y -= (itemPtr->height * 0.5); |
---|
1242 | break; |
---|
1243 | case TK_ANCHOR_SW: /* Lower left corner */ |
---|
1244 | y -= itemPtr->height; |
---|
1245 | break; |
---|
1246 | case TK_ANCHOR_N: /* Top center */ |
---|
1247 | x -= (itemPtr->width * 0.5); |
---|
1248 | break; |
---|
1249 | case TK_ANCHOR_CENTER: /* Centered */ |
---|
1250 | x -= (itemPtr->width * 0.5); |
---|
1251 | y -= (itemPtr->height * 0.5); |
---|
1252 | break; |
---|
1253 | case TK_ANCHOR_S: /* Bottom center */ |
---|
1254 | x -= (itemPtr->width * 0.5); |
---|
1255 | y -= itemPtr->height; |
---|
1256 | break; |
---|
1257 | case TK_ANCHOR_NE: /* Upper right corner */ |
---|
1258 | x -= itemPtr->width; |
---|
1259 | break; |
---|
1260 | case TK_ANCHOR_E: /* Right center */ |
---|
1261 | x -= itemPtr->width; |
---|
1262 | y -= (itemPtr->height * 0.5); |
---|
1263 | break; |
---|
1264 | case TK_ANCHOR_SE: /* Lower right corner */ |
---|
1265 | x -= itemPtr->width; |
---|
1266 | y -= itemPtr->height; |
---|
1267 | break; |
---|
1268 | } |
---|
1269 | *xPtr = x; |
---|
1270 | *yPtr = y; |
---|
1271 | } |
---|
1272 | |
---|
1273 | /* |
---|
1274 | * ------------------------------------------------------------------------ |
---|
1275 | * |
---|
1276 | * ComputeBbox -- |
---|
1277 | * |
---|
1278 | * This procedure is invoked to compute the bounding box of all the pixels |
---|
1279 | * that may be drawn as part of a text item. In addition, it recomputes |
---|
1280 | * all of the geometry information used to display a text item or check for |
---|
1281 | * mouse hits. |
---|
1282 | * |
---|
1283 | * Results: |
---|
1284 | * None. |
---|
1285 | * |
---|
1286 | * Side effects: |
---|
1287 | * The fields x1, y1, x2, and y2 are updated in the base structure |
---|
1288 | * for itemPtr, and the linePtr structure is regenerated |
---|
1289 | * for itemPtr. |
---|
1290 | * |
---|
1291 | * ------------------------------------------------------------------------ |
---|
1292 | */ |
---|
1293 | static void |
---|
1294 | ComputeBbox(HotspotItem *itemPtr) |
---|
1295 | { |
---|
1296 | double x, y; |
---|
1297 | |
---|
1298 | x = itemPtr->x, y = itemPtr->y; |
---|
1299 | TranslateAnchor(itemPtr, &x, &y); |
---|
1300 | itemPtr->x1 = x; |
---|
1301 | itemPtr->x2 = x + itemPtr->width; |
---|
1302 | itemPtr->y1 = y; |
---|
1303 | itemPtr->y2 = y + itemPtr->height; |
---|
1304 | itemPtr->base.x1 = ROUND(itemPtr->x1); |
---|
1305 | itemPtr->base.y1 = ROUND(itemPtr->y1); |
---|
1306 | itemPtr->base.x2 = ROUND(itemPtr->x2); |
---|
1307 | itemPtr->base.y2 = ROUND(itemPtr->y2); |
---|
1308 | } |
---|
1309 | |
---|
1310 | static void |
---|
1311 | DrawSegment(ItemSegment *segPtr, Drawable drawable, int x, int y) |
---|
1312 | { |
---|
1313 | HotspotItem *itemPtr; |
---|
1314 | GC gc; |
---|
1315 | int isActive; |
---|
1316 | |
---|
1317 | itemPtr = segPtr->itemPtr; |
---|
1318 | if (segPtr->type == SEGMENT_TEXT) { |
---|
1319 | Tk_DrawTextLayout(Tk_Display(itemPtr->tkwin), drawable, |
---|
1320 | itemPtr->normalGC, segPtr->layout, x + segPtr->x + TEXT_PADX, |
---|
1321 | y + segPtr->y + TEXT_PADY, 0, -1); |
---|
1322 | return; |
---|
1323 | } |
---|
1324 | isActive = ((itemPtr->activeValue != NULL) && |
---|
1325 | (strcmp(itemPtr->activeValue, segPtr->text) == 0)); |
---|
1326 | |
---|
1327 | if ((itemPtr->activeBorder != NULL) && (isActive)) { |
---|
1328 | Tk_Fill3DRectangle(itemPtr->tkwin, drawable, itemPtr->activeBorder, |
---|
1329 | x + segPtr->x, y + segPtr->y, segPtr->width, segPtr->height, |
---|
1330 | 1, TK_RELIEF_SOLID); |
---|
1331 | } |
---|
1332 | if (itemPtr->showIcons) { |
---|
1333 | Tk_Image tkImage; |
---|
1334 | int w, h; |
---|
1335 | int ix, iy, ih, iw; |
---|
1336 | |
---|
1337 | ih = itemPtr->maxImageHeight * 2 * IMAGE_PAD; |
---|
1338 | iw = itemPtr->maxImageWidth + IMAGE_PAD; |
---|
1339 | ix = x + segPtr->x + IMAGE_PAD; |
---|
1340 | iy = y + segPtr->y + IMAGE_PAD; |
---|
1341 | if (segPtr->height > ih) { |
---|
1342 | iy += (segPtr->height - ih) / 2; |
---|
1343 | } |
---|
1344 | tkImage = (isActive) ? itemPtr->activeImage : itemPtr->image; |
---|
1345 | Tk_SizeOfImage(tkImage, &w, &h); |
---|
1346 | Tk_RedrawImage(tkImage, 0, 0, w, h, drawable, ix, iy); |
---|
1347 | x += iw; |
---|
1348 | } |
---|
1349 | gc = (isActive) ? itemPtr->activeGC : itemPtr->valueGC; |
---|
1350 | Tk_DrawTextLayout(Tk_Display(itemPtr->tkwin), drawable, gc, segPtr->layout, |
---|
1351 | x + segPtr->x + TEXT_PADX, y + segPtr->y + TEXT_PADY, 0, -1); |
---|
1352 | } |
---|
1353 | |
---|
1354 | /* |
---|
1355 | * ------------------------------------------------------------------------ |
---|
1356 | * |
---|
1357 | * DisplayProc -- |
---|
1358 | * |
---|
1359 | * This procedure is invoked to draw a text item in a given drawable. |
---|
1360 | * |
---|
1361 | * Results: |
---|
1362 | * None. |
---|
1363 | * |
---|
1364 | * Side effects: |
---|
1365 | * ItemPtr is drawn in drawable using the transformation information |
---|
1366 | * in canvas. |
---|
1367 | * |
---|
1368 | * ------------------------------------------------------------------------ |
---|
1369 | */ |
---|
1370 | static void |
---|
1371 | DisplayProc(Tk_Canvas canvas, Tk_Item *canvItemPtr, Display *display, |
---|
1372 | Drawable drawable, int x, int y, int width, int height) |
---|
1373 | { |
---|
1374 | HotspotItem *itemPtr = (HotspotItem *) canvItemPtr; |
---|
1375 | ItemSegment *segPtr; |
---|
1376 | short int drawX, drawY; |
---|
1377 | double tx, ty; |
---|
1378 | |
---|
1379 | itemPtr->flags &= ~REDRAW_PENDING; |
---|
1380 | if (itemPtr->flags & SUBSTITUTIONS_PENDING) { |
---|
1381 | DoSubstitutions(itemPtr); |
---|
1382 | } |
---|
1383 | if (itemPtr->flags & LAYOUT_PENDING) { |
---|
1384 | ComputeGeometry(itemPtr); |
---|
1385 | } |
---|
1386 | tx = itemPtr->x, ty = itemPtr->y; |
---|
1387 | TranslateAnchor(itemPtr, &tx, &ty); |
---|
1388 | |
---|
1389 | /* Convert canvas coordinates to drawable coordinates. */ |
---|
1390 | Tk_CanvasDrawableCoords(canvas, tx, ty, &drawX, &drawY); |
---|
1391 | x = drawX, y = drawY; |
---|
1392 | |
---|
1393 | if (itemPtr->border != NULL) { |
---|
1394 | Tk_Fill3DRectangle(itemPtr->tkwin, drawable, itemPtr->border, x, y, |
---|
1395 | itemPtr->width, itemPtr->height, itemPtr->borderWidth, |
---|
1396 | itemPtr->relief); |
---|
1397 | } |
---|
1398 | if (itemPtr->outlineColor != NULL) { |
---|
1399 | XDrawRectangle(display, drawable, itemPtr->outlineGC, x, y, |
---|
1400 | itemPtr->width, itemPtr->height); |
---|
1401 | } |
---|
1402 | for (segPtr = itemPtr->firstPtr; segPtr != NULL; segPtr = segPtr->nextPtr) { |
---|
1403 | DrawSegment(segPtr, drawable, x, y); |
---|
1404 | } |
---|
1405 | } |
---|
1406 | |
---|
1407 | /* |
---|
1408 | * ------------------------------------------------------------------------ |
---|
1409 | * |
---|
1410 | * PointProc -- |
---|
1411 | * |
---|
1412 | * Computes the distance from a given point to a given hotspot item, in |
---|
1413 | * canvas units. |
---|
1414 | * |
---|
1415 | * Results: |
---|
1416 | * The return value is 0 if the point whose x and y coordinates |
---|
1417 | * are pointPtr[0] and pointPtr[1] is inside the text item. If |
---|
1418 | * the point isn't inside the text item then the return value |
---|
1419 | * is the distance from the point to the text item. |
---|
1420 | * |
---|
1421 | * Side effects: |
---|
1422 | * None. |
---|
1423 | * |
---|
1424 | * ------------------------------------------------------------------------ |
---|
1425 | */ |
---|
1426 | static double |
---|
1427 | PointProc(Tk_Canvas canvas, Tk_Item *canvItemPtr, double *pointPtr) |
---|
1428 | { |
---|
1429 | HotspotItem *itemPtr = (HotspotItem *)canvItemPtr; |
---|
1430 | double dx, dy; |
---|
1431 | |
---|
1432 | if (pointPtr[0] < itemPtr->x1) { |
---|
1433 | dx = itemPtr->x1 - pointPtr[0]; |
---|
1434 | } else if (pointPtr[0] >= itemPtr->x2) { |
---|
1435 | dx = pointPtr[0] + 1 - itemPtr->x2; |
---|
1436 | } else { |
---|
1437 | dx = 0; |
---|
1438 | } |
---|
1439 | if (pointPtr[1] < itemPtr->y1) { |
---|
1440 | dy = itemPtr->y1 - pointPtr[1]; |
---|
1441 | } else if (pointPtr[1] >= itemPtr->y2) { |
---|
1442 | dy = pointPtr[1] + 1 - itemPtr->y2; |
---|
1443 | } else { |
---|
1444 | dy = 0; |
---|
1445 | } |
---|
1446 | return hypot(dx, dy); |
---|
1447 | } |
---|
1448 | |
---|
1449 | /* |
---|
1450 | * ------------------------------------------------------------------------ |
---|
1451 | * |
---|
1452 | * AreaProc -- |
---|
1453 | * |
---|
1454 | * This procedure is called to determine whether an item lies |
---|
1455 | * entirely inside, entirely outside, or overlapping a given |
---|
1456 | * rectangle. |
---|
1457 | * |
---|
1458 | * Results: |
---|
1459 | * -1 is returned if the item is entirely outside the area given |
---|
1460 | * by rectPtr, 0 if it overlaps, and 1 if it is entirely inside |
---|
1461 | * the given area. |
---|
1462 | * |
---|
1463 | * Side effects: |
---|
1464 | * None. |
---|
1465 | * |
---|
1466 | * ------------------------------------------------------------------------ |
---|
1467 | */ |
---|
1468 | static int |
---|
1469 | AreaProc(Tk_Canvas canvas, Tk_Item *canvItemPtr, double *rectPtr) |
---|
1470 | { |
---|
1471 | HotspotItem *itemPtr = (HotspotItem *)canvItemPtr; |
---|
1472 | |
---|
1473 | if ((rectPtr[2] <= itemPtr->x1) || (rectPtr[0] >= itemPtr->x2) || |
---|
1474 | (rectPtr[3] <= itemPtr->y1) || (rectPtr[1] >= itemPtr->y2)) { |
---|
1475 | return -1; |
---|
1476 | } |
---|
1477 | if ((rectPtr[0] <= itemPtr->x1) && (rectPtr[1] <= itemPtr->y1) && |
---|
1478 | (rectPtr[2] >= itemPtr->x2) && (rectPtr[3] >= itemPtr->y2)) { |
---|
1479 | return 1; |
---|
1480 | } |
---|
1481 | return 0; |
---|
1482 | } |
---|
1483 | |
---|
1484 | /* |
---|
1485 | * ------------------------------------------------------------------------ |
---|
1486 | * |
---|
1487 | * ScaleProc -- |
---|
1488 | * |
---|
1489 | * This procedure is invoked to rescale a text item. |
---|
1490 | * |
---|
1491 | * Results: |
---|
1492 | * None. |
---|
1493 | * |
---|
1494 | * Side effects: |
---|
1495 | * Scales the position of the text, but not the size of the font |
---|
1496 | * for the text. |
---|
1497 | * |
---|
1498 | * ------------------------------------------------------------------------ |
---|
1499 | */ |
---|
1500 | /* ARGSUSED */ |
---|
1501 | static void |
---|
1502 | ScaleProc(Tk_Canvas canvas, Tk_Item *canvItemPtr, double x, double y, |
---|
1503 | double scaleX, double scaleY) |
---|
1504 | { |
---|
1505 | HotspotItem *itemPtr = (HotspotItem *)canvItemPtr; |
---|
1506 | |
---|
1507 | itemPtr->x = x + scaleX * (itemPtr->x - x); |
---|
1508 | itemPtr->y = y + scaleY * (itemPtr->y - y); |
---|
1509 | ComputeBbox(itemPtr); |
---|
1510 | return; |
---|
1511 | } |
---|
1512 | |
---|
1513 | /* |
---|
1514 | * ------------------------------------------------------------------------ |
---|
1515 | * |
---|
1516 | * TranslateProc -- |
---|
1517 | * |
---|
1518 | * This procedure is called to move a text item by a given amount. |
---|
1519 | * |
---|
1520 | * Results: |
---|
1521 | * None. |
---|
1522 | * |
---|
1523 | * Side effects: |
---|
1524 | * The position of the text item is offset by (xDelta, yDelta), |
---|
1525 | * and the bounding box is updated in the generic part of the |
---|
1526 | * item structure. |
---|
1527 | * |
---|
1528 | * ------------------------------------------------------------------------ |
---|
1529 | */ |
---|
1530 | static void |
---|
1531 | TranslateProc(Tk_Canvas canvas, Tk_Item *canvItemPtr, double dx, double dy) |
---|
1532 | { |
---|
1533 | HotspotItem *itemPtr = (HotspotItem *)canvItemPtr; |
---|
1534 | |
---|
1535 | itemPtr->x += dx; |
---|
1536 | itemPtr->y += dy; |
---|
1537 | ComputeBbox(itemPtr); |
---|
1538 | } |
---|
1539 | |
---|
1540 | /* |
---|
1541 | * ------------------------------------------------------------------------ |
---|
1542 | * |
---|
1543 | * PostscriptProc -- |
---|
1544 | * |
---|
1545 | * This procedure is called to generate Postscript for hotspot items. |
---|
1546 | * |
---|
1547 | * Results: |
---|
1548 | * The return value is a standard Tcl result. If an error occurs in |
---|
1549 | * generating Postscript then an error message is left in the interp's |
---|
1550 | * result, replacing whatever used to be there. If no error occurs, then |
---|
1551 | * Postscript for the item is appended to the result. |
---|
1552 | * |
---|
1553 | * Side effects: |
---|
1554 | * None. |
---|
1555 | * |
---|
1556 | * ------------------------------------------------------------------------ |
---|
1557 | */ |
---|
1558 | static int |
---|
1559 | PostscriptProc(Tcl_Interp *interp, Tk_Canvas canvas, Tk_Item *canvItemPtr, |
---|
1560 | int prepass) |
---|
1561 | { |
---|
1562 | #ifdef notdef |
---|
1563 | HotspotItem *itemPtr = (HotspotItem *)canvItemPtr; |
---|
1564 | Tk_Window canvasWin = Tk_CanvasTkwin(canvas); |
---|
1565 | |
---|
1566 | int x, y; |
---|
1567 | double xpos; |
---|
1568 | Tk_FontMetrics fm; |
---|
1569 | char *justify; |
---|
1570 | char buffer[500]; |
---|
1571 | int delta; |
---|
1572 | |
---|
1573 | if (itemPtr->textColor == NULL || itemPtr->text == NULL |
---|
1574 | || *itemPtr->text == 0) { |
---|
1575 | return TCL_OK; |
---|
1576 | } |
---|
1577 | |
---|
1578 | if (Tk_CanvasPsFont(interp, canvas, itemPtr->font) != TCL_OK) { |
---|
1579 | return TCL_ERROR; |
---|
1580 | } |
---|
1581 | |
---|
1582 | |
---|
1583 | if (!prepass && |
---|
1584 | Tk_CanvasPsColor(interp, canvas, itemPtr->textColor) != TCL_OK) { |
---|
1585 | return TCL_ERROR; |
---|
1586 | } |
---|
1587 | xpos = itemPtr->x; |
---|
1588 | |
---|
1589 | /* |
---|
1590 | * Check for an image to print. |
---|
1591 | */ |
---|
1592 | |
---|
1593 | if (itemPtr->imageLeft) { |
---|
1594 | if(!prepass) { |
---|
1595 | delta = (itemPtr->base.y2 - itemPtr->base.y1)/2 - itemPtr->imageLeftH/2; |
---|
1596 | sprintf(buffer, "%.15g %.15g", xpos, |
---|
1597 | Tk_CanvasPsY(canvas, itemPtr->y+delta+2)); |
---|
1598 | Tcl_AppendResult(interp, buffer, " translate\n", (char *) NULL); |
---|
1599 | } |
---|
1600 | |
---|
1601 | Tk_PostscriptImage(itemPtr->imageLeft, interp, canvasWin, |
---|
1602 | ((TkCanvas*)canvas)->psInfo, 0, 0, |
---|
1603 | itemPtr->imageLeftW, itemPtr->imageLeftH, prepass); |
---|
1604 | |
---|
1605 | if ( !prepass ) { |
---|
1606 | /* |
---|
1607 | * This PS code is needed in order for the label to display |
---|
1608 | * correctly |
---|
1609 | */ |
---|
1610 | |
---|
1611 | Tcl_AppendResult(interp,"grestore\ngsave\n",(char *)NULL); |
---|
1612 | } |
---|
1613 | |
---|
1614 | xpos += itemPtr->imageLeftW + itemPtr->padding; |
---|
1615 | } |
---|
1616 | |
---|
1617 | |
---|
1618 | if (prepass != 0 ) { |
---|
1619 | return TCL_OK; |
---|
1620 | } |
---|
1621 | |
---|
1622 | /* |
---|
1623 | * Before drawing the text, reset the font and color information. |
---|
1624 | * Otherwise the text won't appear. |
---|
1625 | */ |
---|
1626 | |
---|
1627 | if ( itemPtr->imageLeft ){ |
---|
1628 | if (Tk_CanvasPsFont(interp, canvas, itemPtr->font) != TCL_OK) { |
---|
1629 | return TCL_ERROR; |
---|
1630 | } |
---|
1631 | |
---|
1632 | if (Tk_CanvasPsColor(interp, canvas, itemPtr->textColor) != TCL_OK) { |
---|
1633 | return TCL_ERROR; |
---|
1634 | } |
---|
1635 | } |
---|
1636 | |
---|
1637 | |
---|
1638 | /* |
---|
1639 | * Draw the text for the hotspot. |
---|
1640 | */ |
---|
1641 | sprintf(buffer, "%.15g %.15g [\n", xpos, |
---|
1642 | Tk_CanvasPsY(canvas, itemPtr->y)); |
---|
1643 | Tcl_AppendResult(interp, buffer, (char *) NULL); |
---|
1644 | |
---|
1645 | Tk_TextLayoutToPostscript(interp, itemPtr->textLayout); |
---|
1646 | |
---|
1647 | x = 0; y = 0; justify = NULL; |
---|
1648 | switch (itemPtr->anchor) { |
---|
1649 | case TK_ANCHOR_NW: x = 0; y = 0; break; |
---|
1650 | case TK_ANCHOR_N: x = 1; y = 0; break; |
---|
1651 | case TK_ANCHOR_NE: x = 2; y = 0; break; |
---|
1652 | case TK_ANCHOR_E: x = 2; y = 1; break; |
---|
1653 | case TK_ANCHOR_SE: x = 2; y = 2; break; |
---|
1654 | case TK_ANCHOR_S: x = 1; y = 2; break; |
---|
1655 | case TK_ANCHOR_SW: x = 0; y = 2; break; |
---|
1656 | case TK_ANCHOR_W: x = 0; y = 1; break; |
---|
1657 | case TK_ANCHOR_CENTER: x = 1; y = 1; break; |
---|
1658 | } |
---|
1659 | justify = "0"; /* TK_JUSTIFY_LEFT */ |
---|
1660 | |
---|
1661 | Tk_GetFontMetrics(itemPtr->font, &fm); |
---|
1662 | sprintf(buffer, "] %d %g %g %s %s DrawText\n", |
---|
1663 | fm.linespace, x / -2.0, y / 2.0, justify, |
---|
1664 | "false" /* stipple */); |
---|
1665 | Tcl_AppendResult(interp, buffer, (char *) NULL); |
---|
1666 | #endif |
---|
1667 | return TCL_OK; |
---|
1668 | } |
---|
1669 | |
---|
1670 | static HotspotItem * |
---|
1671 | GetHotspotItem(Tcl_Interp *interp, Tcl_Obj *canvasObjPtr, Tcl_Obj *itemObjPtr) |
---|
1672 | { |
---|
1673 | const char *string; |
---|
1674 | Tk_Item *canvItemPtr; |
---|
1675 | HotspotItem *itemPtr; |
---|
1676 | Tcl_CmdInfo cmdInfo; |
---|
1677 | TkCanvas *canvasPtr; |
---|
1678 | int id; |
---|
1679 | long lid; |
---|
1680 | Tcl_HashEntry *hPtr; |
---|
1681 | Tk_Window tkwin; |
---|
1682 | |
---|
1683 | /* See if we can find the canvas window associated by the name. */ |
---|
1684 | string = Tcl_GetString(canvasObjPtr); |
---|
1685 | tkwin = Tk_NameToWindow(interp, string, Tk_MainWindow(interp)); |
---|
1686 | if (tkwin == NULL) { |
---|
1687 | Tcl_AppendResult(interp, "can't find canvas \"", string, "\"", |
---|
1688 | (char *)NULL); |
---|
1689 | return NULL; |
---|
1690 | } |
---|
1691 | if (strcmp(Tk_Class(tkwin), "Canvas") != 0) { |
---|
1692 | Tcl_AppendResult(interp, "window \"", string, "\" isn't a canvas.", |
---|
1693 | (char *)NULL); |
---|
1694 | return NULL; |
---|
1695 | } |
---|
1696 | /* If we're this far, then we can try to get the clientData. If the widget |
---|
1697 | * command was renamed, we're out of luck. */ |
---|
1698 | if (!Tcl_GetCommandInfo(interp, string, &cmdInfo)) { |
---|
1699 | Tcl_AppendResult(interp, "can't find canvas \"", string, "\"", |
---|
1700 | (char *)NULL); |
---|
1701 | return NULL; |
---|
1702 | } |
---|
1703 | canvasPtr = cmdInfo.objClientData; |
---|
1704 | if (Tcl_GetIntFromObj(interp, itemObjPtr, &id) != TCL_OK) { |
---|
1705 | return NULL; |
---|
1706 | } |
---|
1707 | string = Tcl_GetString(itemObjPtr); |
---|
1708 | |
---|
1709 | lid = id; |
---|
1710 | hPtr = Tcl_FindHashEntry(&canvasPtr->idTable, (char *)lid); |
---|
1711 | if (hPtr == NULL) { |
---|
1712 | Tcl_AppendResult(interp, "can't find canvas item \"", string, |
---|
1713 | "\"", (char *)NULL); |
---|
1714 | return NULL; |
---|
1715 | |
---|
1716 | } |
---|
1717 | canvItemPtr = Tcl_GetHashValue(hPtr); |
---|
1718 | /* The canvas item we find has to be a hotspot. */ |
---|
1719 | if (canvItemPtr->typePtr != &hotspotType) { |
---|
1720 | Tcl_AppendResult(interp, "bad canvas item \"", string, |
---|
1721 | "\": must be a hotspot", (char *)NULL); |
---|
1722 | return NULL; |
---|
1723 | |
---|
1724 | } |
---|
1725 | return itemPtr = (HotspotItem *)canvItemPtr; |
---|
1726 | } |
---|
1727 | |
---|
1728 | static Tcl_Obj * |
---|
1729 | Identify(Tcl_Interp *interp, HotspotItem *itemPtr, double x, double y) |
---|
1730 | { |
---|
1731 | Tcl_Obj* resultPtr = NULL; |
---|
1732 | ItemSegment *segPtr; |
---|
1733 | Tcl_Obj* objPtr; |
---|
1734 | |
---|
1735 | x -= itemPtr->x1; |
---|
1736 | y -= itemPtr->y1; |
---|
1737 | for (segPtr = itemPtr->firstPtr; segPtr != NULL; segPtr = segPtr->nextPtr) { |
---|
1738 | if (segPtr->type == SEGMENT_TEXT) { |
---|
1739 | continue; /* Ignore text segments. */ |
---|
1740 | } |
---|
1741 | if ((x >= segPtr->x) && (x < (segPtr->x + segPtr->width)) && |
---|
1742 | (y >= segPtr->y) && (y < (segPtr->y + segPtr->height))) { |
---|
1743 | |
---|
1744 | /* build return list: {string x0 y0 x1 y1} */ |
---|
1745 | resultPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); |
---|
1746 | |
---|
1747 | objPtr = Tcl_NewStringObj(segPtr->text, -1); |
---|
1748 | Tcl_ListObjAppendElement(interp, resultPtr, objPtr); |
---|
1749 | |
---|
1750 | objPtr = Tcl_NewIntObj(itemPtr->x1 + segPtr->x); |
---|
1751 | Tcl_ListObjAppendElement(interp, resultPtr, objPtr); |
---|
1752 | objPtr = Tcl_NewIntObj(itemPtr->y1 + segPtr->y); |
---|
1753 | Tcl_ListObjAppendElement(interp, resultPtr, objPtr); |
---|
1754 | |
---|
1755 | objPtr = Tcl_NewIntObj(itemPtr->x1 + segPtr->x + segPtr->width); |
---|
1756 | Tcl_ListObjAppendElement(interp, resultPtr, objPtr); |
---|
1757 | objPtr = Tcl_NewIntObj(itemPtr->y1 + segPtr->y + segPtr->height); |
---|
1758 | Tcl_ListObjAppendElement(interp, resultPtr, objPtr); |
---|
1759 | |
---|
1760 | return resultPtr; |
---|
1761 | } |
---|
1762 | } |
---|
1763 | return NULL; |
---|
1764 | } |
---|
1765 | |
---|
1766 | static int |
---|
1767 | HotspotCmd(ClientData clientData, Tcl_Interp *interp, int objc, |
---|
1768 | Tcl_Obj *const *objv) |
---|
1769 | { |
---|
1770 | const char *string; |
---|
1771 | HotspotItem *itemPtr; |
---|
1772 | int length; |
---|
1773 | char c; |
---|
1774 | |
---|
1775 | if (objc < 3) { |
---|
1776 | return TCL_ERROR; |
---|
1777 | } |
---|
1778 | itemPtr = GetHotspotItem(interp, objv[2], objv[3]); |
---|
1779 | if (itemPtr == NULL) { |
---|
1780 | return TCL_ERROR; |
---|
1781 | } |
---|
1782 | string = Tcl_GetStringFromObj(objv[1], &length); |
---|
1783 | c = string[0]; |
---|
1784 | if ((c == 'a') && (strncmp(string, "activate", length) == 0)) { |
---|
1785 | /* hotspot activate .c 0 varName */ |
---|
1786 | } else if ((c == 'd') && (strncmp(string, "deactivate", length) == 0)) { |
---|
1787 | /* hotspot deactivate .c 0 */ |
---|
1788 | itemPtr->activeValue = NULL; |
---|
1789 | } else if ((c == 'i') && (strncmp(string, "identify", length) == 0)) { |
---|
1790 | double x, y; |
---|
1791 | Tcl_Obj *objPtr; |
---|
1792 | |
---|
1793 | /* hotspot identify .c 0 x y */ |
---|
1794 | if ((Tcl_GetDoubleFromObj(interp, objv[4], &x) != TCL_OK) || |
---|
1795 | (Tcl_GetDoubleFromObj(interp, objv[5], &y) != TCL_OK)) { |
---|
1796 | return TCL_ERROR; |
---|
1797 | } |
---|
1798 | objPtr = Identify(interp, itemPtr, x, y); |
---|
1799 | if (objPtr != NULL) { |
---|
1800 | Tcl_SetObjResult(interp, objPtr); |
---|
1801 | } |
---|
1802 | } else if ((c == 'v') && (strncmp(string, "variables", length) == 0)) { |
---|
1803 | Tcl_Obj *listObjPtr; |
---|
1804 | ItemSegment *segPtr; |
---|
1805 | |
---|
1806 | /* hotspot variables .c 0 */ |
---|
1807 | listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); |
---|
1808 | for (segPtr = itemPtr->firstPtr; segPtr != NULL; |
---|
1809 | segPtr = segPtr->nextPtr) { |
---|
1810 | if (segPtr->type == SEGMENT_VALUE) { |
---|
1811 | Tcl_Obj *objPtr; |
---|
1812 | |
---|
1813 | objPtr = Tcl_NewStringObj(segPtr->text, -1); |
---|
1814 | Tcl_ListObjAppendElement(interp, listObjPtr, objPtr); |
---|
1815 | } |
---|
1816 | } |
---|
1817 | Tcl_SetObjResult(interp, listObjPtr); |
---|
1818 | } else { |
---|
1819 | Tcl_AppendResult(interp, "unknown hotspot option \"", string, |
---|
1820 | "\": should be either activate, deactivate, identity, ", |
---|
1821 | "or variables", (char *)NULL); |
---|
1822 | return TCL_ERROR; |
---|
1823 | } |
---|
1824 | return TCL_OK; |
---|
1825 | } |
---|
1826 | |
---|
1827 | /* |
---|
1828 | * ------------------------------------------------------------------------ |
---|
1829 | * RpCanvHotspot_Init -- |
---|
1830 | * |
---|
1831 | * Invoked when the Rappture GUI library is being initialized |
---|
1832 | * to install the "hotspot" item on the Tk canvas widget. |
---|
1833 | * |
---|
1834 | * Returns TCL_OK if successful, or TCL_ERROR (along with an error |
---|
1835 | * message in the interp) if anything goes wrong. |
---|
1836 | * ------------------------------------------------------------------------ |
---|
1837 | */ |
---|
1838 | int |
---|
1839 | RpCanvHotspot_Init(interp) |
---|
1840 | Tcl_Interp *interp; /* interpreter being initialized */ |
---|
1841 | { |
---|
1842 | Tk_CreateItemType(&hotspotType); |
---|
1843 | Tcl_CreateObjCommand(interp, "Rappture::hotspot", HotspotCmd, NULL, NULL); |
---|
1844 | return TCL_OK; |
---|
1845 | } |
---|
1846 | |
---|