OSDN Git Service

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