]>
Commit | Line | Data |
---|---|---|
6a5fa4e0 MG |
1 | /* |
2 | * tkMenubutton.c -- | |
3 | * | |
4 | * This module implements button-like widgets that are used | |
5 | * to invoke pull-down menus. | |
6 | * | |
7 | * Copyright 1990 Regents of the University of California. | |
8 | * Permission to use, copy, modify, and distribute this | |
9 | * software and its documentation for any purpose and without | |
10 | * fee is hereby granted, provided that the above copyright | |
11 | * notice appear in all copies. The University of California | |
12 | * makes no representations about the suitability of this | |
13 | * software for any purpose. It is provided "as is" without | |
14 | * express or implied warranty. | |
15 | */ | |
16 | ||
17 | #ifndef lint | |
18 | static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkMenubutton.c,v 1.33 92/08/21 16:21:47 ouster Exp $ SPRITE (Berkeley)"; | |
19 | #endif | |
20 | ||
21 | #include "tkconfig.h" | |
22 | #include "default.h" | |
23 | #include "tkint.h" | |
24 | ||
25 | /* | |
26 | * A data structure of the following type is kept for each | |
27 | * widget managed by this file: | |
28 | */ | |
29 | ||
30 | typedef struct { | |
31 | Tk_Window tkwin; /* Window that embodies the widget. NULL | |
32 | * means that the window has been destroyed | |
33 | * but the data structures haven't yet been | |
34 | * cleaned up.*/ | |
35 | Tcl_Interp *interp; /* Interpreter associated with menu button. */ | |
36 | char *menuName; /* Name of menu associated with widget (used | |
37 | * to generate post and unpost commands). | |
38 | * Malloc-ed. */ | |
39 | Tk_Uid varName; /* Name of variable associated with collection | |
40 | * of menu bars: used to allow scanning of | |
41 | * menus. Also used as identifier for | |
42 | * menu group. */ | |
43 | ||
44 | /* | |
45 | * Information about what's displayed in the menu button: | |
46 | */ | |
47 | ||
48 | char *text; /* Text to display in button (malloc'ed) | |
49 | * or NULL. */ | |
50 | int textLength; /* # of characters in text. */ | |
51 | int underline; /* Index of character to underline. */ | |
52 | char *textVarName; /* Name of variable (malloc'ed) or NULL. | |
53 | * If non-NULL, button displays the contents | |
54 | * of this variable. */ | |
55 | Pixmap bitmap; /* Bitmap to display or None. If not None | |
56 | * then text and textVar and underline | |
57 | * are ignored. */ | |
58 | ||
59 | /* | |
60 | * Information used when displaying widget: | |
61 | */ | |
62 | ||
63 | Tk_Uid state; /* State of button for display purposes: | |
64 | * normal, active, or disabled. */ | |
65 | Tk_3DBorder normalBorder; /* Structure used to draw 3-D | |
66 | * border and background when window | |
67 | * isn't active. NULL means no such | |
68 | * border exists. */ | |
69 | Tk_3DBorder activeBorder; /* Structure used to draw 3-D | |
70 | * border and background when window | |
71 | * is active. NULL means no such | |
72 | * border exists. */ | |
73 | int borderWidth; /* Width of border. */ | |
74 | int relief; /* 3-d effect: TK_RELIEF_RAISED, etc. */ | |
75 | XFontStruct *fontPtr; /* Information about text font, or NULL. */ | |
76 | XColor *normalFg; /* Foreground color in normal mode. */ | |
77 | XColor *activeFg; /* Foreground color in active mode. NULL | |
78 | * means use normalFg instead. */ | |
79 | XColor *disabledFg; /* Foreground color when disabled. NULL | |
80 | * means use normalFg with a 50% stipple | |
81 | * instead. */ | |
82 | GC normalTextGC; /* GC for drawing text in normal mode. */ | |
83 | GC activeTextGC; /* GC for drawing text in active mode (NULL | |
84 | * means use normalTextGC). */ | |
85 | Pixmap gray; /* Pixmap for displaying disabled text/icon if | |
86 | * disabledFg is NULL. */ | |
87 | GC disabledGC; /* Used to produce disabled effect. If | |
88 | * disabledFg isn't NULL, this GC is used to | |
89 | * draw button text or icon. Otherwise | |
90 | * text or icon is drawn with normalGC and | |
91 | * this GC is used to stipple background | |
92 | * across it. */ | |
93 | int leftBearing; /* Amount text sticks left from its origin, | |
94 | * in pixels. */ | |
95 | int rightBearing; /* Amount text sticks right from its origin. */ | |
96 | int width, height; /* If > 0, these specify dimensions to request | |
97 | * for window, in characters for text and in | |
98 | * pixels for bitmaps. In this case the actual | |
99 | * size of the text string or bitmap is | |
100 | * ignored in computing desired window size. */ | |
101 | int padX, padY; /* Extra space around text or bitmap (pixels | |
102 | * on each side). */ | |
103 | Tk_Anchor anchor; /* Where text/bitmap should be displayed | |
104 | * inside window region. */ | |
105 | ||
106 | /* | |
107 | * Miscellaneous information: | |
108 | */ | |
109 | ||
110 | Cursor cursor; /* Current cursor for window, or None. */ | |
111 | int flags; /* Various flags; see below for | |
112 | * definitions. */ | |
113 | } MenuButton; | |
114 | ||
115 | /* | |
116 | * Flag bits for buttons: | |
117 | * | |
118 | * REDRAW_PENDING: Non-zero means a DoWhenIdle handler | |
119 | * has already been queued to redraw | |
120 | * this window. | |
121 | * POSTED: Non-zero means that the menu associated | |
122 | * with this button has been posted (typically | |
123 | * because of an active button press). | |
124 | */ | |
125 | ||
126 | #define REDRAW_PENDING 1 | |
127 | #define POSTED 2 | |
128 | ||
129 | /* | |
130 | * Information used for parsing configuration specs: | |
131 | */ | |
132 | ||
133 | static Tk_ConfigSpec configSpecs[] = { | |
134 | {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground", | |
135 | DEF_MENUBUTTON_ACTIVE_BG_COLOR, Tk_Offset(MenuButton, activeBorder), | |
136 | TK_CONFIG_COLOR_ONLY}, | |
137 | {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground", | |
138 | DEF_MENUBUTTON_ACTIVE_BG_MONO, Tk_Offset(MenuButton, activeBorder), | |
139 | TK_CONFIG_MONO_ONLY}, | |
140 | {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background", | |
141 | DEF_MENUBUTTON_ACTIVE_FG_COLOR, Tk_Offset(MenuButton, activeFg), | |
142 | TK_CONFIG_COLOR_ONLY}, | |
143 | {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background", | |
144 | DEF_MENUBUTTON_ACTIVE_FG_MONO, Tk_Offset(MenuButton, activeFg), | |
145 | TK_CONFIG_MONO_ONLY}, | |
146 | {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor", | |
147 | DEF_MENUBUTTON_ANCHOR, Tk_Offset(MenuButton, anchor), 0}, | |
148 | {TK_CONFIG_BORDER, "-background", "background", "Background", | |
149 | DEF_MENUBUTTON_BG_COLOR, Tk_Offset(MenuButton, normalBorder), | |
150 | TK_CONFIG_COLOR_ONLY}, | |
151 | {TK_CONFIG_BORDER, "-background", "background", "Background", | |
152 | DEF_MENUBUTTON_BG_MONO, Tk_Offset(MenuButton, normalBorder), | |
153 | TK_CONFIG_MONO_ONLY}, | |
154 | {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL, | |
155 | (char *) NULL, 0, 0}, | |
156 | {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL, | |
157 | (char *) NULL, 0, 0}, | |
158 | #if defined(USE_XPM3) | |
159 | {TK_CONFIG_PIXMAP, "-bitmap", "bitmap", "Bitmap", | |
160 | DEF_MENUBUTTON_BITMAP, Tk_Offset(MenuButton, bitmap), | |
161 | TK_CONFIG_NULL_OK}, | |
162 | #else | |
163 | {TK_CONFIG_BITMAP, "-bitmap", "bitmap", "Bitmap", | |
164 | DEF_MENUBUTTON_BITMAP, Tk_Offset(MenuButton, bitmap), | |
165 | TK_CONFIG_NULL_OK}, | |
166 | #endif | |
167 | {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth", | |
168 | DEF_MENUBUTTON_BORDER_WIDTH, Tk_Offset(MenuButton, borderWidth), 0}, | |
169 | {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor", | |
170 | DEF_MENUBUTTON_CURSOR, Tk_Offset(MenuButton, cursor), | |
171 | TK_CONFIG_NULL_OK}, | |
172 | {TK_CONFIG_COLOR, "-disabledforeground", "disabledForeground", | |
173 | "DisabledForeground", DEF_MENUBUTTON_DISABLED_FG_COLOR, | |
174 | Tk_Offset(MenuButton, disabledFg), | |
175 | TK_CONFIG_COLOR_ONLY|TK_CONFIG_NULL_OK}, | |
176 | {TK_CONFIG_COLOR, "-disabledforeground", "disabledForeground", | |
177 | "DisabledForeground", DEF_MENUBUTTON_DISABLED_FG_MONO, | |
178 | Tk_Offset(MenuButton, disabledFg), | |
179 | TK_CONFIG_MONO_ONLY|TK_CONFIG_NULL_OK}, | |
180 | {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL, | |
181 | (char *) NULL, 0, 0}, | |
182 | {TK_CONFIG_FONT, "-font", "font", "Font", | |
183 | DEF_MENUBUTTON_FONT, Tk_Offset(MenuButton, fontPtr), 0}, | |
184 | {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground", | |
185 | DEF_MENUBUTTON_FG, Tk_Offset(MenuButton, normalFg), 0}, | |
186 | {TK_CONFIG_INT, "-height", "height", "Height", | |
187 | DEF_MENUBUTTON_HEIGHT, Tk_Offset(MenuButton, height), 0}, | |
188 | {TK_CONFIG_STRING, "-menu", "menu", "Menu", | |
189 | DEF_MENUBUTTON_MENU, Tk_Offset(MenuButton, menuName), 0}, | |
190 | {TK_CONFIG_PIXELS, "-padx", "padX", "Pad", | |
191 | DEF_MENUBUTTON_PADX, Tk_Offset(MenuButton, padX), 0}, | |
192 | {TK_CONFIG_PIXELS, "-pady", "padY", "Pad", | |
193 | DEF_MENUBUTTON_PADY, Tk_Offset(MenuButton, padY), 0}, | |
194 | {TK_CONFIG_RELIEF, "-relief", "relief", "Relief", | |
195 | DEF_MENUBUTTON_RELIEF, Tk_Offset(MenuButton, relief), 0}, | |
196 | {TK_CONFIG_UID, "-state", "state", "State", | |
197 | DEF_MENUBUTTON_STATE, Tk_Offset(MenuButton, state), 0}, | |
198 | {TK_CONFIG_STRING, "-text", "text", "Text", | |
199 | DEF_MENUBUTTON_TEXT, Tk_Offset(MenuButton, text), 0}, | |
200 | {TK_CONFIG_STRING, "-textvariable", "textVariable", "Variable", | |
201 | DEF_MENUBUTTON_TEXT_VARIABLE, Tk_Offset(MenuButton, textVarName), | |
202 | TK_CONFIG_NULL_OK}, | |
203 | {TK_CONFIG_INT, "-underline", "underline", "Underline", | |
204 | DEF_MENUBUTTON_UNDERLINE, Tk_Offset(MenuButton, underline), 0}, | |
205 | {TK_CONFIG_UID, "-variable", "variable", "Variable", | |
206 | DEF_MENUBUTTON_VARIABLE, Tk_Offset(MenuButton, varName), 0}, | |
207 | {TK_CONFIG_INT, "-width", "width", "Width", | |
208 | DEF_MENUBUTTON_WIDTH, Tk_Offset(MenuButton, width), 0}, | |
209 | {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL, | |
210 | (char *) NULL, 0, 0} | |
211 | }; | |
212 | ||
213 | /* | |
214 | * Forward declarations for procedures defined later in this file: | |
215 | */ | |
216 | ||
217 | static void ComputeMenuButtonGeometry _ANSI_ARGS_(( | |
218 | MenuButton *mbPtr)); | |
219 | static void MenuButtonEventProc _ANSI_ARGS_((ClientData clientData, | |
220 | XEvent *eventPtr)); | |
221 | static char * MenuButtonTextVarProc _ANSI_ARGS_(( | |
222 | ClientData clientData, Tcl_Interp *interp, | |
223 | char *name1, char *name2, int flags)); | |
224 | static char * MenuButtonVarProc _ANSI_ARGS_((ClientData clientData, | |
225 | Tcl_Interp *interp, char *name1, char *name2, | |
226 | int flags)); | |
227 | static int MenuButtonWidgetCmd _ANSI_ARGS_((ClientData clientData, | |
228 | Tcl_Interp *interp, int argc, char **argv)); | |
229 | static int ConfigureMenuButton _ANSI_ARGS_((Tcl_Interp *interp, | |
230 | MenuButton *mbPtr, int argc, char **argv, | |
231 | int flags)); | |
232 | static void DestroyMenuButton _ANSI_ARGS_((ClientData clientData)); | |
233 | static void DisplayMenuButton _ANSI_ARGS_((ClientData clientData)); | |
234 | \f | |
235 | /* | |
236 | *-------------------------------------------------------------- | |
237 | * | |
238 | * Tk_MenubuttonCmd -- | |
239 | * | |
240 | * This procedure is invoked to process the "button", "label", | |
241 | * "radiobutton", and "checkbutton" Tcl commands. See the | |
242 | * user documentation for details on what it does. | |
243 | * | |
244 | * Results: | |
245 | * A standard Tcl result. | |
246 | * | |
247 | * Side effects: | |
248 | * See the user documentation. | |
249 | * | |
250 | *-------------------------------------------------------------- | |
251 | */ | |
252 | ||
253 | int | |
254 | Tk_MenubuttonCmd(clientData, interp, argc, argv) | |
255 | ClientData clientData; /* Main window associated with | |
256 | * interpreter. */ | |
257 | Tcl_Interp *interp; /* Current interpreter. */ | |
258 | int argc; /* Number of arguments. */ | |
259 | char **argv; /* Argument strings. */ | |
260 | { | |
261 | register MenuButton *mbPtr; | |
262 | Tk_Window tkwin = (Tk_Window) clientData; | |
263 | Tk_Window new; | |
264 | ||
265 | if (argc < 2) { | |
266 | Tcl_AppendResult(interp, "wrong # args: should be \"", | |
267 | argv[0], " pathName ?options?\"", (char *) NULL); | |
268 | return TCL_ERROR; | |
269 | } | |
270 | ||
271 | /* | |
272 | * Create the new window. | |
273 | */ | |
274 | ||
275 | new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL); | |
276 | if (new == NULL) { | |
277 | return TCL_ERROR; | |
278 | } | |
279 | ||
280 | /* | |
281 | * Initialize the data structure for the button. | |
282 | */ | |
283 | ||
284 | mbPtr = (MenuButton *) ckalloc(sizeof(MenuButton)); | |
285 | mbPtr->tkwin = new; | |
286 | mbPtr->interp = interp; | |
287 | mbPtr->menuName = NULL; | |
288 | mbPtr->varName = NULL; | |
289 | mbPtr->text = NULL; | |
290 | mbPtr->underline = -1; | |
291 | mbPtr->textVarName = NULL; | |
292 | mbPtr->bitmap = None; | |
293 | mbPtr->state = tkNormalUid; | |
294 | mbPtr->normalBorder = NULL; | |
295 | mbPtr->activeBorder = NULL; | |
296 | mbPtr->borderWidth = 0; | |
297 | mbPtr->relief = TK_RELIEF_FLAT; | |
298 | mbPtr->fontPtr = NULL; | |
299 | mbPtr->normalFg = NULL; | |
300 | mbPtr->activeFg = NULL; | |
301 | mbPtr->disabledFg = NULL; | |
302 | mbPtr->normalTextGC = NULL; | |
303 | mbPtr->activeTextGC = NULL; | |
304 | mbPtr->gray = None; | |
305 | mbPtr->disabledGC = NULL; | |
306 | mbPtr->cursor = None; | |
307 | mbPtr->flags = 0; | |
308 | ||
309 | Tk_SetClass(mbPtr->tkwin, "Menubutton"); | |
310 | Tk_CreateEventHandler(mbPtr->tkwin, ExposureMask|StructureNotifyMask, | |
311 | MenuButtonEventProc, (ClientData) mbPtr); | |
312 | Tcl_CreateCommand(interp, Tk_PathName(mbPtr->tkwin), MenuButtonWidgetCmd, | |
313 | (ClientData) mbPtr, (void (*)()) NULL); | |
314 | if (ConfigureMenuButton(interp, mbPtr, argc-2, argv+2, 0) != TCL_OK) { | |
315 | Tk_DestroyWindow(mbPtr->tkwin); | |
316 | return TCL_ERROR; | |
317 | } | |
318 | ||
319 | interp->result = Tk_PathName(mbPtr->tkwin); | |
320 | return TCL_OK; | |
321 | } | |
322 | \f | |
323 | /* | |
324 | *-------------------------------------------------------------- | |
325 | * | |
326 | * MenuButtonWidgetCmd -- | |
327 | * | |
328 | * This procedure is invoked to process the Tcl command | |
329 | * that corresponds to a widget managed by this module. | |
330 | * See the user documentation for details on what it does. | |
331 | * | |
332 | * Results: | |
333 | * A standard Tcl result. | |
334 | * | |
335 | * Side effects: | |
336 | * See the user documentation. | |
337 | * | |
338 | *-------------------------------------------------------------- | |
339 | */ | |
340 | ||
341 | static int | |
342 | MenuButtonWidgetCmd(clientData, interp, argc, argv) | |
343 | ClientData clientData; /* Information about button widget. */ | |
344 | Tcl_Interp *interp; /* Current interpreter. */ | |
345 | int argc; /* Number of arguments. */ | |
346 | char **argv; /* Argument strings. */ | |
347 | { | |
348 | register MenuButton *mbPtr = (MenuButton *) clientData; | |
349 | int result = TCL_OK; | |
350 | int length; | |
351 | char c; | |
352 | ||
353 | if (argc < 2) { | |
354 | Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], | |
355 | " option ?arg arg ...?\"", (char *) NULL); | |
356 | return TCL_ERROR; | |
357 | } | |
358 | Tk_Preserve((ClientData) mbPtr); | |
359 | c = argv[1][0]; | |
360 | length = strlen(argv[1]); | |
361 | if ((c == 'a') && (strncmp(argv[1], "activate", length) == 0)) { | |
362 | if (argc > 2) { | |
363 | Tcl_AppendResult(interp, "wrong # args: should be \"", | |
364 | argv[0], " activate\"", (char *) NULL); | |
365 | goto error; | |
366 | } | |
367 | if (mbPtr->state != tkDisabledUid) { | |
368 | mbPtr->state = tkActiveUid; | |
369 | Tk_SetBackgroundFromBorder(mbPtr->tkwin, mbPtr->activeBorder); | |
370 | goto redisplay; | |
371 | } | |
372 | } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) { | |
373 | if (argc == 2) { | |
374 | result = Tk_ConfigureInfo(interp, mbPtr->tkwin, configSpecs, | |
375 | (char *) mbPtr, (char *) NULL, 0); | |
376 | } else if (argc == 3) { | |
377 | result = Tk_ConfigureInfo(interp, mbPtr->tkwin, configSpecs, | |
378 | (char *) mbPtr, argv[2], 0); | |
379 | } else { | |
380 | result = ConfigureMenuButton(interp, mbPtr, argc-2, argv+2, | |
381 | TK_CONFIG_ARGV_ONLY); | |
382 | } | |
383 | } else if ((c == 'd') && (strncmp(argv[1], "deactivate", length) == 0)) { | |
384 | if (argc > 2) { | |
385 | Tcl_AppendResult(interp, "wrong # args: should be \"", | |
386 | argv[0], " deactivate\"", (char *) NULL); | |
387 | goto error; | |
388 | } | |
389 | if (mbPtr->state != tkDisabledUid) { | |
390 | mbPtr->state = tkNormalUid; | |
391 | Tk_SetBackgroundFromBorder(mbPtr->tkwin, mbPtr->normalBorder); | |
392 | goto redisplay; | |
393 | } | |
394 | } else if ((c == 'p') && (strncmp(argv[1], "post", length) == 0)) { | |
395 | if (argc > 2) { | |
396 | Tcl_AppendResult(interp, "wrong # args: should be \"", | |
397 | argv[0], " post\"", (char *) NULL); | |
398 | goto error; | |
399 | } | |
400 | if ((mbPtr->flags & POSTED) || (mbPtr->menuName == NULL) | |
401 | || (mbPtr->state == tkDisabledUid)) { | |
402 | goto done; | |
403 | } | |
404 | ||
405 | /* | |
406 | * Store the name of the posted menu into the associated variable. | |
407 | * This will cause any other menu posted via that variable to | |
408 | * unpost itself and will cause this menu to post itself. | |
409 | */ | |
410 | ||
411 | Tcl_SetVar(interp, mbPtr->varName, Tk_PathName(mbPtr->tkwin), | |
412 | TCL_GLOBAL_ONLY); | |
413 | } else if ((c == 'u') && (strncmp(argv[1], "unpost", length) == 0)) { | |
414 | if (argc > 2) { | |
415 | Tcl_AppendResult(interp, "wrong # args: should be \"", | |
416 | argv[0], " unpost\"", (char *) NULL); | |
417 | goto error; | |
418 | } | |
419 | ||
420 | /* | |
421 | * The one-liner below looks simple, but it isn't. This code | |
422 | * does the right thing even if this menu isn't posted anymore, | |
423 | * but some other variable associated with the same variable | |
424 | * is posted instead: it unposts whatever is posted. This | |
425 | * approach is necessary because at present ButtonRelease | |
426 | * events go to the menu button where the mouse button was | |
427 | * first pressed; this may not be the same menu button that's | |
428 | * currently active. | |
429 | */ | |
430 | Tcl_SetVar(interp, mbPtr->varName, "", TCL_GLOBAL_ONLY); | |
431 | } else { | |
432 | Tcl_AppendResult(interp, "bad option \"", argv[1], | |
433 | "\": must be activate, configure, deactivate, ", | |
434 | "post, or unpost", (char *) NULL); | |
435 | goto error; | |
436 | } | |
437 | done: | |
438 | Tk_Release((ClientData) mbPtr); | |
439 | return result; | |
440 | ||
441 | redisplay: | |
442 | if (Tk_IsMapped(mbPtr->tkwin) && !(mbPtr->flags & REDRAW_PENDING)) { | |
443 | Tk_DoWhenIdle(DisplayMenuButton, (ClientData) mbPtr); | |
444 | mbPtr->flags |= REDRAW_PENDING; | |
445 | } | |
446 | goto done; | |
447 | ||
448 | error: | |
449 | Tk_Release((ClientData) mbPtr); | |
450 | return TCL_ERROR; | |
451 | } | |
452 | \f | |
453 | /* | |
454 | *---------------------------------------------------------------------- | |
455 | * | |
456 | * DestroyMenuButton -- | |
457 | * | |
458 | * This procedure is invoked to recycle all of the resources | |
459 | * associated with a button widget. It is invoked as a | |
460 | * when-idle handler in order to make sure that there is no | |
461 | * other use of the button pending at the time of the deletion. | |
462 | * | |
463 | * Results: | |
464 | * None. | |
465 | * | |
466 | * Side effects: | |
467 | * Everything associated with the widget is freed up. | |
468 | * | |
469 | *---------------------------------------------------------------------- | |
470 | */ | |
471 | ||
472 | static void | |
473 | DestroyMenuButton(clientData) | |
474 | ClientData clientData; /* Info about button widget. */ | |
475 | { | |
476 | register MenuButton *mbPtr = (MenuButton *) clientData; | |
477 | if (mbPtr->menuName != NULL) { | |
478 | ckfree(mbPtr->menuName); | |
479 | } | |
480 | if (mbPtr->varName != NULL) { | |
481 | Tcl_UntraceVar(mbPtr->interp, mbPtr->varName, | |
482 | TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, | |
483 | MenuButtonVarProc, (ClientData) mbPtr); | |
484 | } | |
485 | if (mbPtr->text != NULL) { | |
486 | ckfree(mbPtr->text); | |
487 | } | |
488 | if (mbPtr->textVarName != NULL) { | |
489 | Tcl_UntraceVar(mbPtr->interp, mbPtr->textVarName, | |
490 | TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, | |
491 | MenuButtonTextVarProc, (ClientData) mbPtr); | |
492 | ckfree(mbPtr->textVarName); | |
493 | } | |
494 | if (mbPtr->bitmap != None) { | |
495 | #if defined(USE_XPM3) | |
496 | Tk_FreePixmap(mbPtr->bitmap); | |
497 | #else | |
498 | Tk_FreeBitmap(mbPtr->bitmap); | |
499 | #endif | |
500 | } | |
501 | if (mbPtr->normalBorder != NULL) { | |
502 | Tk_Free3DBorder(mbPtr->normalBorder); | |
503 | } | |
504 | if (mbPtr->activeBorder != NULL) { | |
505 | Tk_Free3DBorder(mbPtr->activeBorder); | |
506 | } | |
507 | if (mbPtr->fontPtr != NULL) { | |
508 | Tk_FreeFontStruct(mbPtr->fontPtr); | |
509 | } | |
510 | if (mbPtr->normalFg != NULL) { | |
511 | Tk_FreeColor(mbPtr->normalFg); | |
512 | } | |
513 | if (mbPtr->activeFg != NULL) { | |
514 | Tk_FreeColor(mbPtr->activeFg); | |
515 | } | |
516 | if (mbPtr->disabledFg != NULL) { | |
517 | Tk_FreeColor(mbPtr->disabledFg); | |
518 | } | |
519 | if (mbPtr->normalTextGC != None) { | |
520 | Tk_FreeGC(mbPtr->normalTextGC); | |
521 | } | |
522 | if (mbPtr->activeTextGC != None) { | |
523 | Tk_FreeGC(mbPtr->activeTextGC); | |
524 | } | |
525 | if (mbPtr->gray != None) { | |
526 | Tk_FreeBitmap(mbPtr->gray); | |
527 | } | |
528 | if (mbPtr->disabledGC != None) { | |
529 | Tk_FreeGC(mbPtr->disabledGC); | |
530 | } | |
531 | if (mbPtr->cursor != None) { | |
532 | Tk_FreeCursor(mbPtr->cursor); | |
533 | } | |
534 | ckfree((char *) mbPtr); | |
535 | } | |
536 | \f | |
537 | /* | |
538 | *---------------------------------------------------------------------- | |
539 | * | |
540 | * ConfigureMenuButton -- | |
541 | * | |
542 | * This procedure is called to process an argv/argc list, plus | |
543 | * the Tk option database, in order to configure (or | |
544 | * reconfigure) a menubutton widget. | |
545 | * | |
546 | * Results: | |
547 | * The return value is a standard Tcl result. If TCL_ERROR is | |
548 | * returned, then interp->result contains an error message. | |
549 | * | |
550 | * Side effects: | |
551 | * Configuration information, such as text string, colors, font, | |
552 | * etc. get set for mbPtr; old resources get freed, if there | |
553 | * were any. The menubutton is redisplayed. | |
554 | * | |
555 | *---------------------------------------------------------------------- | |
556 | */ | |
557 | ||
558 | static int | |
559 | ConfigureMenuButton(interp, mbPtr, argc, argv, flags) | |
560 | Tcl_Interp *interp; /* Used for error reporting. */ | |
561 | register MenuButton *mbPtr; /* Information about widget; may or may | |
562 | * not already have values for some fields. */ | |
563 | int argc; /* Number of valid entries in argv. */ | |
564 | char **argv; /* Arguments. */ | |
565 | int flags; /* Flags to pass to Tk_ConfigureWidget. */ | |
566 | { | |
567 | XGCValues gcValues; | |
568 | GC newGC; | |
569 | unsigned long mask; | |
570 | int result; | |
571 | Tk_Uid oldGroup; | |
572 | char *value; | |
573 | ||
574 | /* | |
575 | * Eliminate any existing traces on variables monitored by the button. | |
576 | */ | |
577 | ||
578 | if (mbPtr->varName != NULL) { | |
579 | Tcl_UntraceVar(interp, mbPtr->varName, | |
580 | TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, | |
581 | MenuButtonVarProc, (ClientData) mbPtr); | |
582 | } | |
583 | if (mbPtr->textVarName != NULL) { | |
584 | Tcl_UntraceVar(interp, mbPtr->textVarName, | |
585 | TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, | |
586 | MenuButtonTextVarProc, (ClientData) mbPtr); | |
587 | } | |
588 | ||
589 | oldGroup = mbPtr->varName; | |
590 | result = Tk_ConfigureWidget(interp, mbPtr->tkwin, configSpecs, | |
591 | argc, argv, (char *) mbPtr, flags); | |
592 | if (oldGroup != mbPtr->varName) { | |
593 | Tk_UnshareEvents(mbPtr->tkwin, oldGroup); | |
594 | Tk_ShareEvents(mbPtr->tkwin, mbPtr->varName); | |
595 | } | |
596 | if (result != TCL_OK) { | |
597 | return TCL_ERROR; | |
598 | } | |
599 | ||
600 | /* | |
601 | * A few options need special processing, such as setting the | |
602 | * background from a 3-D border, or filling in complicated | |
603 | * defaults that couldn't be specified to Tk_ConfigureWidget. | |
604 | */ | |
605 | ||
606 | if (mbPtr->state == tkActiveUid) { | |
607 | Tk_SetBackgroundFromBorder(mbPtr->tkwin, mbPtr->activeBorder); | |
608 | } else { | |
609 | Tk_SetBackgroundFromBorder(mbPtr->tkwin, mbPtr->normalBorder); | |
610 | if ((mbPtr->state != tkNormalUid) && (mbPtr->state != tkDisabledUid)) { | |
611 | Tcl_AppendResult(interp, "bad state value \"", mbPtr->state, | |
612 | "\": must be normal, active, or disabled", (char *) NULL); | |
613 | mbPtr->state = tkNormalUid; | |
614 | return TCL_ERROR; | |
615 | } | |
616 | } | |
617 | ||
618 | gcValues.font = mbPtr->fontPtr->fid; | |
619 | gcValues.foreground = mbPtr->normalFg->pixel; | |
620 | gcValues.background = Tk_3DBorderColor(mbPtr->normalBorder)->pixel; | |
621 | ||
622 | /* | |
623 | * Note: GraphicsExpose events are disabled in GC's because they're | |
624 | * used to copy stuff from an off-screen pixmap onto the screen (we know | |
625 | * that there's no problem with obscured areas). | |
626 | */ | |
627 | ||
628 | gcValues.graphics_exposures = False; | |
629 | newGC = Tk_GetGC(mbPtr->tkwin, | |
630 | GCForeground|GCBackground|GCFont|GCGraphicsExposures, &gcValues); | |
631 | if (mbPtr->normalTextGC != None) { | |
632 | Tk_FreeGC(mbPtr->normalTextGC); | |
633 | } | |
634 | mbPtr->normalTextGC = newGC; | |
635 | ||
636 | gcValues.font = mbPtr->fontPtr->fid; | |
637 | gcValues.foreground = mbPtr->activeFg->pixel; | |
638 | gcValues.background = Tk_3DBorderColor(mbPtr->activeBorder)->pixel; | |
639 | newGC = Tk_GetGC(mbPtr->tkwin, GCForeground|GCBackground|GCFont, | |
640 | &gcValues); | |
641 | if (mbPtr->activeTextGC != None) { | |
642 | Tk_FreeGC(mbPtr->activeTextGC); | |
643 | } | |
644 | mbPtr->activeTextGC = newGC; | |
645 | ||
646 | gcValues.font = mbPtr->fontPtr->fid; | |
647 | gcValues.background = Tk_3DBorderColor(mbPtr->normalBorder)->pixel; | |
648 | if (mbPtr->disabledFg != NULL) { | |
649 | gcValues.foreground = mbPtr->disabledFg->pixel; | |
650 | mask = GCForeground|GCBackground|GCFont; | |
651 | } else { | |
652 | gcValues.foreground = gcValues.background; | |
653 | if (mbPtr->gray == None) { | |
654 | mbPtr->gray = Tk_GetBitmap(interp, mbPtr->tkwin, | |
655 | Tk_GetUid("gray50")); | |
656 | if (mbPtr->gray == None) { | |
657 | return TCL_ERROR; | |
658 | } | |
659 | } | |
660 | gcValues.fill_style = FillStippled; | |
661 | gcValues.stipple = mbPtr->gray; | |
662 | mask = GCForeground|GCFillStyle|GCStipple; | |
663 | } | |
664 | newGC = Tk_GetGC(mbPtr->tkwin, mask, &gcValues); | |
665 | if (mbPtr->disabledGC != None) { | |
666 | Tk_FreeGC(mbPtr->disabledGC); | |
667 | } | |
668 | mbPtr->disabledGC = newGC; | |
669 | ||
670 | if (mbPtr->padX < 0) { | |
671 | mbPtr->padX = 0; | |
672 | } | |
673 | if (mbPtr->padY < 0) { | |
674 | mbPtr->padY = 0; | |
675 | } | |
676 | ||
677 | /* | |
678 | * Set up a trace on the menu button's variable, then initialize | |
679 | * the variable if it doesn't already exist, so that it can be | |
680 | * accessed immediately from Tcl code without fear of | |
681 | * "nonexistent variable" errors. | |
682 | */ | |
683 | ||
684 | value = Tcl_GetVar(interp, mbPtr->varName, TCL_GLOBAL_ONLY); | |
685 | if (value == NULL) { | |
686 | Tcl_SetVar(interp, mbPtr->varName, "", TCL_GLOBAL_ONLY); | |
687 | } | |
688 | Tcl_TraceVar(interp, mbPtr->varName, | |
689 | TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, | |
690 | MenuButtonVarProc, (ClientData) mbPtr); | |
691 | ||
692 | /* | |
693 | * Set up a trace on the variable that determines what's displayed | |
694 | * in the menu button, if such a trace has been requested. | |
695 | */ | |
696 | ||
697 | if ((mbPtr->bitmap == None) && (mbPtr->textVarName != NULL)) { | |
698 | char *value; | |
699 | ||
700 | value = Tcl_GetVar(interp, mbPtr->textVarName, TCL_GLOBAL_ONLY); | |
701 | if (value == NULL) { | |
702 | Tcl_SetVar(interp, mbPtr->textVarName, mbPtr->text, | |
703 | TCL_GLOBAL_ONLY); | |
704 | } else { | |
705 | if (mbPtr->text != NULL) { | |
706 | ckfree(mbPtr->text); | |
707 | } | |
708 | mbPtr->text = ckalloc((unsigned) (strlen(value) + 1)); | |
709 | strcpy(mbPtr->text, value); | |
710 | } | |
711 | Tcl_TraceVar(interp, mbPtr->textVarName, | |
712 | TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, | |
713 | MenuButtonTextVarProc, (ClientData) mbPtr); | |
714 | } | |
715 | ||
716 | /* | |
717 | * Recompute the geometry for the button. | |
718 | */ | |
719 | ||
720 | ComputeMenuButtonGeometry(mbPtr); | |
721 | ||
722 | /* | |
723 | * Lastly, arrange for the button to be redisplayed. | |
724 | */ | |
725 | ||
726 | if (Tk_IsMapped(mbPtr->tkwin) && !(mbPtr->flags & REDRAW_PENDING)) { | |
727 | Tk_DoWhenIdle(DisplayMenuButton, (ClientData) mbPtr); | |
728 | mbPtr->flags |= REDRAW_PENDING; | |
729 | } | |
730 | ||
731 | return TCL_OK; | |
732 | } | |
733 | \f | |
734 | /* | |
735 | *---------------------------------------------------------------------- | |
736 | * | |
737 | * DisplayMenuButton -- | |
738 | * | |
739 | * This procedure is invoked to display a menubutton widget. | |
740 | * | |
741 | * Results: | |
742 | * None. | |
743 | * | |
744 | * Side effects: | |
745 | * Commands are output to X to display the menubutton in its | |
746 | * current mode. | |
747 | * | |
748 | *---------------------------------------------------------------------- | |
749 | */ | |
750 | ||
751 | static void | |
752 | DisplayMenuButton(clientData) | |
753 | ClientData clientData; /* Information about widget. */ | |
754 | { | |
755 | register MenuButton *mbPtr = (MenuButton *) clientData; | |
756 | GC gc; | |
757 | Tk_3DBorder border; | |
758 | Pixmap pixmap; | |
759 | int x = 0; /* Initialization needed only to stop | |
760 | * compiler warning. */ | |
761 | int y; | |
762 | register Tk_Window tkwin = mbPtr->tkwin; | |
763 | ||
764 | mbPtr->flags &= ~REDRAW_PENDING; | |
765 | if ((mbPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) { | |
766 | return; | |
767 | } | |
768 | ||
769 | if ((mbPtr->state == tkDisabledUid) && (mbPtr->disabledFg != NULL)) { | |
770 | gc = mbPtr->disabledGC; | |
771 | border = mbPtr->normalBorder; | |
772 | } else if (mbPtr->state == tkActiveUid) { | |
773 | gc = mbPtr->activeTextGC; | |
774 | border = mbPtr->activeBorder; | |
775 | } else { | |
776 | gc = mbPtr->normalTextGC; | |
777 | border = mbPtr->normalBorder; | |
778 | } | |
779 | ||
780 | /* | |
781 | * In order to avoid screen flashes, this procedure redraws | |
782 | * the menu button in a pixmap, then copies the pixmap to the | |
783 | * screen in a single operation. This means that there's no | |
784 | * point in time where the on-sreen image has been cleared. | |
785 | */ | |
786 | ||
787 | pixmap = XCreatePixmap(Tk_Display(tkwin), Tk_WindowId(tkwin), | |
788 | Tk_Width(tkwin), Tk_Height(tkwin), | |
789 | Tk_DefaultDepth(Tk_Screen(tkwin))); | |
790 | Tk_Fill3DRectangle(Tk_Display(tkwin), pixmap, border, | |
791 | 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 0, TK_RELIEF_FLAT); | |
792 | ||
793 | /* | |
794 | * Display bitmap or text for button. | |
795 | */ | |
796 | ||
797 | if (mbPtr->bitmap != None) { | |
798 | unsigned int width, height; | |
799 | ||
800 | #if defined(USE_XPM3) | |
801 | Tk_SizeOfPixmap(mbPtr->bitmap, &width, &height); | |
802 | #else | |
803 | Tk_SizeOfBitmap(mbPtr->bitmap, &width, &height); | |
804 | #endif | |
805 | switch (mbPtr->anchor) { | |
806 | case TK_ANCHOR_NW: case TK_ANCHOR_W: case TK_ANCHOR_SW: | |
807 | x += mbPtr->borderWidth + mbPtr->padX; | |
808 | break; | |
809 | case TK_ANCHOR_N: case TK_ANCHOR_CENTER: case TK_ANCHOR_S: | |
810 | x += (Tk_Width(tkwin) - width)/2; | |
811 | break; | |
812 | default: | |
813 | x += Tk_Width(tkwin) - mbPtr->borderWidth - mbPtr->padX | |
814 | - width; | |
815 | break; | |
816 | } | |
817 | switch (mbPtr->anchor) { | |
818 | case TK_ANCHOR_NW: case TK_ANCHOR_N: case TK_ANCHOR_NE: | |
819 | y = mbPtr->borderWidth + mbPtr->padY; | |
820 | break; | |
821 | case TK_ANCHOR_W: case TK_ANCHOR_CENTER: case TK_ANCHOR_E: | |
822 | y = (Tk_Height(tkwin) - height)/2; | |
823 | break; | |
824 | default: | |
825 | y = Tk_Height(tkwin) - mbPtr->borderWidth - mbPtr->padY | |
826 | - height; | |
827 | break; | |
828 | } | |
829 | #if defined(USE_XPM3) | |
830 | XCopyArea(Tk_Display(tkwin), mbPtr->bitmap, pixmap, | |
831 | gc, 0, 0, width, height, x, y); | |
832 | #else | |
833 | XCopyPlane(Tk_Display(tkwin), mbPtr->bitmap, pixmap, | |
834 | gc, 0, 0, width, height, x, y, 1); | |
835 | #endif | |
836 | } else { | |
837 | switch (mbPtr->anchor) { | |
838 | case TK_ANCHOR_NW: case TK_ANCHOR_W: case TK_ANCHOR_SW: | |
839 | x = mbPtr->borderWidth + mbPtr->padX + mbPtr->leftBearing; | |
840 | break; | |
841 | case TK_ANCHOR_N: case TK_ANCHOR_CENTER: case TK_ANCHOR_S: | |
842 | x = (Tk_Width(tkwin) + mbPtr->leftBearing | |
843 | - mbPtr->rightBearing)/2; | |
844 | break; | |
845 | default: | |
846 | x = Tk_Width(tkwin) - mbPtr->borderWidth - mbPtr->padX | |
847 | - mbPtr->rightBearing; | |
848 | break; | |
849 | } | |
850 | switch (mbPtr->anchor) { | |
851 | case TK_ANCHOR_NW: case TK_ANCHOR_N: case TK_ANCHOR_NE: | |
852 | y = mbPtr->borderWidth + mbPtr->fontPtr->ascent | |
853 | + mbPtr->padY; | |
854 | break; | |
855 | case TK_ANCHOR_W: case TK_ANCHOR_CENTER: case TK_ANCHOR_E: | |
856 | y = (Tk_Height(tkwin) + mbPtr->fontPtr->ascent | |
857 | - mbPtr->fontPtr->descent)/2; | |
858 | break; | |
859 | default: | |
860 | y = Tk_Height(tkwin) - mbPtr->borderWidth - mbPtr->padY | |
861 | - mbPtr->fontPtr->descent; | |
862 | break; | |
863 | } | |
864 | XDrawString(Tk_Display(tkwin), pixmap, gc, x, y, mbPtr->text, | |
865 | mbPtr->textLength); | |
866 | if (mbPtr->underline >= 0) { | |
867 | TkUnderlineChars(Tk_Display(tkwin), pixmap, gc, mbPtr->fontPtr, | |
868 | mbPtr->text, x, y, TK_NEWLINES_NOT_SPECIAL, | |
869 | mbPtr->underline, mbPtr->underline); | |
870 | } | |
871 | } | |
872 | ||
873 | /* | |
874 | * If the menu button is disabled with a stipple rather than a special | |
875 | * foreground color, generate the stippled effect. | |
876 | */ | |
877 | ||
878 | if ((mbPtr->state == tkDisabledUid) && (mbPtr->disabledFg == NULL)) { | |
879 | XFillRectangle(Tk_Display(tkwin), pixmap, mbPtr->disabledGC, | |
880 | mbPtr->borderWidth, mbPtr->borderWidth, | |
881 | (unsigned) (Tk_Width(tkwin) - 2*mbPtr->borderWidth), | |
882 | (unsigned) (Tk_Height(tkwin) - 2*mbPtr->borderWidth)); | |
883 | } | |
884 | ||
885 | /* | |
886 | * Draw the border last. This way, if the menu button's contents | |
887 | * overflow onto the border they'll be covered up by the border. | |
888 | */ | |
889 | ||
890 | if (mbPtr->relief != TK_RELIEF_FLAT) { | |
891 | Tk_Draw3DRectangle(Tk_Display(tkwin), pixmap, border, | |
892 | 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), | |
893 | mbPtr->borderWidth, mbPtr->relief); | |
894 | } | |
895 | ||
896 | /* | |
897 | * Copy the information from the off-screen pixmap onto the screen, | |
898 | * then delete the pixmap. | |
899 | */ | |
900 | ||
901 | XCopyArea(Tk_Display(tkwin), pixmap, Tk_WindowId(tkwin), | |
902 | mbPtr->normalTextGC, 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 0, 0); | |
903 | XFreePixmap(Tk_Display(tkwin), pixmap); | |
904 | } | |
905 | \f | |
906 | /* | |
907 | *-------------------------------------------------------------- | |
908 | * | |
909 | * MenuButtonEventProc -- | |
910 | * | |
911 | * This procedure is invoked by the Tk dispatcher for various | |
912 | * events on buttons. | |
913 | * | |
914 | * Results: | |
915 | * None. | |
916 | * | |
917 | * Side effects: | |
918 | * When the window gets deleted, internal structures get | |
919 | * cleaned up. When it gets exposed, it is redisplayed. | |
920 | * | |
921 | *-------------------------------------------------------------- | |
922 | */ | |
923 | ||
924 | static void | |
925 | MenuButtonEventProc(clientData, eventPtr) | |
926 | ClientData clientData; /* Information about window. */ | |
927 | XEvent *eventPtr; /* Information about event. */ | |
928 | { | |
929 | MenuButton *mbPtr = (MenuButton *) clientData; | |
930 | if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) { | |
931 | if ((mbPtr->tkwin != NULL) && !(mbPtr->flags & REDRAW_PENDING)) { | |
932 | Tk_DoWhenIdle(DisplayMenuButton, (ClientData) mbPtr); | |
933 | mbPtr->flags |= REDRAW_PENDING; | |
934 | } | |
935 | } else if (eventPtr->type == DestroyNotify) { | |
936 | Tcl_DeleteCommand(mbPtr->interp, Tk_PathName(mbPtr->tkwin)); | |
937 | ||
938 | /* | |
939 | * Careful! Must delete the event-sharing information here | |
940 | * rather than in DestroyMenuButton. By the time that procedure | |
941 | * is called the tkwin may have been reused, resulting in some | |
942 | * other window accidentally being cut off from shared events. | |
943 | */ | |
944 | ||
945 | Tk_UnshareEvents(mbPtr->tkwin, mbPtr->varName); | |
946 | mbPtr->tkwin = NULL; | |
947 | if (mbPtr->flags & REDRAW_PENDING) { | |
948 | Tk_CancelIdleCall(DisplayMenuButton, (ClientData) mbPtr); | |
949 | } | |
950 | Tk_EventuallyFree((ClientData) mbPtr, DestroyMenuButton); | |
951 | } | |
952 | } | |
953 | \f | |
954 | /* | |
955 | *---------------------------------------------------------------------- | |
956 | * | |
957 | * ComputeMenuButtonGeometry -- | |
958 | * | |
959 | * After changes in a menu button's text or bitmap, this procedure | |
960 | * recomputes the menu button's geometry and passes this information | |
961 | * along to the geometry manager for the window. | |
962 | * | |
963 | * Results: | |
964 | * None. | |
965 | * | |
966 | * Side effects: | |
967 | * The menu button's window may change size. | |
968 | * | |
969 | *---------------------------------------------------------------------- | |
970 | */ | |
971 | ||
972 | static void | |
973 | ComputeMenuButtonGeometry(mbPtr) | |
974 | register MenuButton *mbPtr; /* Widget record for menu button. */ | |
975 | { | |
976 | XCharStruct bbox; | |
977 | int dummy; | |
978 | unsigned int width, height; | |
979 | ||
980 | if (mbPtr->bitmap != None) { | |
981 | #if defined(USE_XPM3) | |
982 | Tk_SizeOfPixmap(mbPtr->bitmap, &width, &height); | |
983 | #else | |
984 | Tk_SizeOfBitmap(mbPtr->bitmap, &width, &height); | |
985 | #endif | |
986 | if (mbPtr->width > 0) { | |
987 | width = mbPtr->width; | |
988 | } | |
989 | if (mbPtr->height > 0) { | |
990 | height = mbPtr->height; | |
991 | } | |
992 | } else { | |
993 | mbPtr->textLength = strlen(mbPtr->text); | |
994 | XTextExtents(mbPtr->fontPtr, mbPtr->text, mbPtr->textLength, | |
995 | &dummy, &dummy, &dummy, &bbox); | |
996 | mbPtr->leftBearing = bbox.lbearing; | |
997 | mbPtr->rightBearing = bbox.rbearing; | |
998 | width = bbox.lbearing + bbox.rbearing; | |
999 | height = mbPtr->fontPtr->ascent + mbPtr->fontPtr->descent; | |
1000 | if (mbPtr->width > 0) { | |
1001 | width = mbPtr->width * XTextWidth(mbPtr->fontPtr, "0", 1); | |
1002 | } | |
1003 | if (mbPtr->height > 0) { | |
1004 | height *= mbPtr->height; | |
1005 | } | |
1006 | } | |
1007 | ||
1008 | width += 2*mbPtr->padX; | |
1009 | height += 2*mbPtr->padY; | |
1010 | Tk_GeometryRequest(mbPtr->tkwin, (int) (width + 2*mbPtr->borderWidth), | |
1011 | (int) (height + 2*mbPtr->borderWidth)); | |
1012 | Tk_SetInternalBorder(mbPtr->tkwin, mbPtr->borderWidth); | |
1013 | } | |
1014 | \f | |
1015 | /* | |
1016 | *-------------------------------------------------------------- | |
1017 | * | |
1018 | * MenuButtonVarProc -- | |
1019 | * | |
1020 | * This procedure is invoked when someone changes the | |
1021 | * state variable associated with a menubutton. This causes | |
1022 | * the posted/unposted state of the menu to change if needed | |
1023 | * to match the variable's new value. | |
1024 | * | |
1025 | * Results: | |
1026 | * NULL is always returned. | |
1027 | * | |
1028 | * Side effects: | |
1029 | * The menu may be posted or unposted. | |
1030 | * | |
1031 | *-------------------------------------------------------------- | |
1032 | */ | |
1033 | ||
1034 | /* ARGSUSED */ | |
1035 | static char * | |
1036 | MenuButtonVarProc(clientData, interp, name1, name2, flags) | |
1037 | ClientData clientData; /* Information about button. */ | |
1038 | Tcl_Interp *interp; /* Interpreter containing variable. */ | |
1039 | char *name1; /* First part of variable's name. */ | |
1040 | char *name2; /* Second part of variable's name. */ | |
1041 | int flags; /* Describes what's happening to variable. */ | |
1042 | { | |
1043 | register MenuButton *mbPtr = (MenuButton *) clientData; | |
1044 | char *value; | |
1045 | int newFlags; | |
1046 | ||
1047 | /* | |
1048 | * If the variable is being unset, then just re-establish the | |
1049 | * trace unless the whole interpreter is going away. Also unpost | |
1050 | * the menu. | |
1051 | */ | |
1052 | ||
1053 | newFlags = mbPtr->flags; | |
1054 | if (flags & TCL_TRACE_UNSETS) { | |
1055 | newFlags &= ~POSTED; | |
1056 | if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) { | |
1057 | Tcl_TraceVar2(interp, name1, name2, | |
1058 | TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, | |
1059 | MenuButtonVarProc, clientData); | |
1060 | } | |
1061 | } else { | |
1062 | ||
1063 | /* | |
1064 | * Use the value of the variable to update the posted status of | |
1065 | * the menu. | |
1066 | */ | |
1067 | ||
1068 | value = Tcl_GetVar2(interp, name1, name2, flags & TCL_GLOBAL_ONLY); | |
1069 | if (strcmp(value, Tk_PathName(mbPtr->tkwin)) == 0) { | |
1070 | newFlags |= POSTED; | |
1071 | } else { | |
1072 | newFlags &= ~POSTED; | |
1073 | } | |
1074 | } | |
1075 | ||
1076 | if ((mbPtr->menuName != NULL) && (newFlags != mbPtr->flags)) { | |
1077 | mbPtr->flags = newFlags; | |
1078 | if (newFlags & POSTED) { | |
1079 | int x, y; | |
1080 | char string[50]; | |
1081 | ||
1082 | /* | |
1083 | * Post the menu just below the menu button. | |
1084 | */ | |
1085 | ||
1086 | Tk_GetRootCoords(mbPtr->tkwin, &x, &y); | |
1087 | y += Tk_Height(mbPtr->tkwin); | |
1088 | sprintf(string, "%d %d ", x, y); | |
1089 | if (Tcl_VarEval(interp, mbPtr->menuName, " post ", string, | |
1090 | mbPtr->varName, (char *) NULL) != TCL_OK) { | |
1091 | TkBindError(interp); | |
1092 | } | |
1093 | } else { | |
1094 | if (Tcl_VarEval(interp, mbPtr->menuName, " unpost", | |
1095 | (char *) NULL) != TCL_OK) { | |
1096 | TkBindError(interp); | |
1097 | } | |
1098 | } | |
1099 | } | |
1100 | return (char *) NULL; | |
1101 | } | |
1102 | \f | |
1103 | /* | |
1104 | *-------------------------------------------------------------- | |
1105 | * | |
1106 | * MenuButtonTextVarProc -- | |
1107 | * | |
1108 | * This procedure is invoked when someone changes the variable | |
1109 | * whose contents are to be displayed in a menu button. | |
1110 | * | |
1111 | * Results: | |
1112 | * NULL is always returned. | |
1113 | * | |
1114 | * Side effects: | |
1115 | * The text displayed in the menu button will change to match the | |
1116 | * variable. | |
1117 | * | |
1118 | *-------------------------------------------------------------- | |
1119 | */ | |
1120 | ||
1121 | /* ARGSUSED */ | |
1122 | static char * | |
1123 | MenuButtonTextVarProc(clientData, interp, name1, name2, flags) | |
1124 | ClientData clientData; /* Information about button. */ | |
1125 | Tcl_Interp *interp; /* Interpreter containing variable. */ | |
1126 | char *name1; /* Name of variable. */ | |
1127 | char *name2; /* Second part of variable name. */ | |
1128 | int flags; /* Information about what happened. */ | |
1129 | { | |
1130 | register MenuButton *mbPtr = (MenuButton *) clientData; | |
1131 | char *value; | |
1132 | ||
1133 | /* | |
1134 | * If the variable is unset, then immediately recreate it unless | |
1135 | * the whole interpreter is going away. | |
1136 | */ | |
1137 | ||
1138 | if (flags & TCL_TRACE_UNSETS) { | |
1139 | if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) { | |
1140 | Tcl_SetVar2(interp, name1, name2, mbPtr->text, | |
1141 | flags & TCL_GLOBAL_ONLY); | |
1142 | Tcl_TraceVar2(interp, name1, name2, | |
1143 | TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, | |
1144 | MenuButtonTextVarProc, clientData); | |
1145 | } | |
1146 | return (char *) NULL; | |
1147 | } | |
1148 | ||
1149 | value = Tcl_GetVar2(interp, name1, name2, flags & TCL_GLOBAL_ONLY); | |
1150 | if (value == NULL) { | |
1151 | value = ""; | |
1152 | } | |
1153 | if (mbPtr->text != NULL) { | |
1154 | ckfree(mbPtr->text); | |
1155 | } | |
1156 | mbPtr->text = ckalloc((unsigned) (strlen(value) + 1)); | |
1157 | strcpy(mbPtr->text, value); | |
1158 | ComputeMenuButtonGeometry(mbPtr); | |
1159 | ||
1160 | if ((mbPtr->tkwin != NULL) && Tk_IsMapped(mbPtr->tkwin) | |
1161 | && !(mbPtr->flags & REDRAW_PENDING)) { | |
1162 | Tk_DoWhenIdle(DisplayMenuButton, (ClientData) mbPtr); | |
1163 | mbPtr->flags |= REDRAW_PENDING; | |
1164 | } | |
1165 | return (char *) NULL; | |
1166 | } |