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