4 * This module implements button-like widgets that are used to invoke
7 * Copyright (c) 1990-1994 The Regents of the University of California.
8 * Copyright (c) 1994-1997 Sun Microsystems, Inc.
10 * See the file "license.terms" for information on usage and redistribution of
11 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
15 #include "tkMenubutton.h"
19 * The structure below defines menubutton class behavior by means of
20 * procedures that can be invoked from generic window code.
23 static const Tk_ClassProcs menubuttonClass = {
24 sizeof(Tk_ClassProcs), /* size */
25 TkMenuButtonWorldChanged, /* worldChangedProc */
26 NULL, /* createProc */
31 * The following table defines the legal values for the -direction option. It
32 * is used together with the "enum direction" declaration in tkMenubutton.h.
35 static const char *const directionStrings[] = {
36 "above", "below", "flush", "left", "right", NULL
40 * The following table defines the legal values for the -state option. It is
41 * used together with the "enum state" declaration in tkMenubutton.h.
44 static const char *const stateStrings[] = {
45 "active", "disabled", "normal", NULL
49 * The following table defines the legal values for the -compound option. It
50 * is used with the "enum compound" declaration in tkMenuButton.h
53 static const char *const compoundStrings[] = {
54 "bottom", "center", "left", "none", "right", "top", NULL
58 * Information used for parsing configuration specs:
61 static const Tk_OptionSpec optionSpecs[] = {
62 {TK_OPTION_BORDER, "-activebackground", "activeBackground", "Foreground",
63 DEF_MENUBUTTON_ACTIVE_BG_COLOR, -1,
64 Tk_Offset(TkMenuButton, activeBorder), 0,
65 (ClientData) DEF_MENUBUTTON_ACTIVE_BG_MONO, 0},
66 {TK_OPTION_COLOR, "-activeforeground", "activeForeground", "Background",
67 DEF_MENUBUTTON_ACTIVE_FG_COLOR, -1,
68 Tk_Offset(TkMenuButton, activeFg),
69 0, DEF_MENUBUTTON_ACTIVE_FG_MONO, 0},
70 {TK_OPTION_ANCHOR, "-anchor", "anchor", "Anchor",
71 DEF_MENUBUTTON_ANCHOR, -1,
72 Tk_Offset(TkMenuButton, anchor), 0, 0, 0},
73 {TK_OPTION_BORDER, "-background", "background", "Background",
74 DEF_MENUBUTTON_BG_COLOR, -1, Tk_Offset(TkMenuButton, normalBorder),
75 0, DEF_MENUBUTTON_BG_MONO, 0},
76 {TK_OPTION_SYNONYM, "-bd", NULL, NULL, NULL, 0, -1, 0,
77 (ClientData) "-borderwidth", 0},
78 {TK_OPTION_SYNONYM, "-bg", NULL, NULL, NULL, 0, -1, 0,
79 (ClientData) "-background", 0},
80 {TK_OPTION_BITMAP, "-bitmap", "bitmap", "Bitmap",
81 DEF_MENUBUTTON_BITMAP, -1, Tk_Offset(TkMenuButton, bitmap),
82 TK_OPTION_NULL_OK, 0, 0},
83 {TK_OPTION_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
84 DEF_MENUBUTTON_BORDER_WIDTH, -1,
85 Tk_Offset(TkMenuButton, borderWidth), 0, 0, 0},
86 {TK_OPTION_CURSOR, "-cursor", "cursor", "Cursor",
87 DEF_MENUBUTTON_CURSOR, -1, Tk_Offset(TkMenuButton, cursor),
88 TK_OPTION_NULL_OK, 0, 0},
89 {TK_OPTION_STRING_TABLE, "-direction", "direction", "Direction",
90 DEF_MENUBUTTON_DIRECTION, -1, Tk_Offset(TkMenuButton, direction),
91 0, directionStrings, 0},
92 {TK_OPTION_COLOR, "-disabledforeground", "disabledForeground",
93 "DisabledForeground", DEF_MENUBUTTON_DISABLED_FG_COLOR,
94 -1, Tk_Offset(TkMenuButton, disabledFg), TK_OPTION_NULL_OK,
95 (ClientData) DEF_MENUBUTTON_DISABLED_FG_MONO, 0},
96 {TK_OPTION_SYNONYM, "-fg", "foreground", NULL, NULL, 0, -1, 0,
97 (ClientData) "-foreground", 0},
98 {TK_OPTION_FONT, "-font", "font", "Font",
99 DEF_MENUBUTTON_FONT, -1, Tk_Offset(TkMenuButton, tkfont), 0, 0, 0},
100 {TK_OPTION_COLOR, "-foreground", "foreground", "Foreground",
101 DEF_MENUBUTTON_FG, -1, Tk_Offset(TkMenuButton, normalFg), 0, 0, 0},
102 {TK_OPTION_STRING, "-height", "height", "Height",
103 DEF_MENUBUTTON_HEIGHT, -1, Tk_Offset(TkMenuButton, heightString),
105 {TK_OPTION_COLOR, "-highlightbackground", "highlightBackground",
106 "HighlightBackground", DEF_MENUBUTTON_HIGHLIGHT_BG_COLOR,
107 -1, Tk_Offset(TkMenuButton, highlightBgColorPtr), 0, 0, 0},
108 {TK_OPTION_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
109 DEF_MENUBUTTON_HIGHLIGHT, -1,
110 Tk_Offset(TkMenuButton, highlightColorPtr), 0, 0, 0},
111 {TK_OPTION_PIXELS, "-highlightthickness", "highlightThickness",
112 "HighlightThickness", DEF_MENUBUTTON_HIGHLIGHT_WIDTH,
113 -1, Tk_Offset(TkMenuButton, highlightWidth), 0, 0, 0},
114 {TK_OPTION_STRING, "-image", "image", "Image",
115 DEF_MENUBUTTON_IMAGE, -1, Tk_Offset(TkMenuButton, imageString),
116 TK_OPTION_NULL_OK, 0, 0},
117 {TK_OPTION_BOOLEAN, "-indicatoron", "indicatorOn", "IndicatorOn",
118 DEF_MENUBUTTON_INDICATOR, -1, Tk_Offset(TkMenuButton, indicatorOn),
120 {TK_OPTION_JUSTIFY, "-justify", "justify", "Justify",
121 DEF_MENUBUTTON_JUSTIFY, -1, Tk_Offset(TkMenuButton, justify), 0, 0, 0},
122 {TK_OPTION_STRING, "-menu", "menu", "Menu",
123 DEF_MENUBUTTON_MENU, -1, Tk_Offset(TkMenuButton, menuName),
124 TK_OPTION_NULL_OK, 0, 0},
125 {TK_OPTION_PIXELS, "-padx", "padX", "Pad",
126 DEF_MENUBUTTON_PADX, -1, Tk_Offset(TkMenuButton, padX),
128 {TK_OPTION_PIXELS, "-pady", "padY", "Pad",
129 DEF_MENUBUTTON_PADY, -1, Tk_Offset(TkMenuButton, padY),
131 {TK_OPTION_RELIEF, "-relief", "relief", "Relief",
132 DEF_MENUBUTTON_RELIEF, -1, Tk_Offset(TkMenuButton, relief),
134 {TK_OPTION_STRING_TABLE, "-compound", "compound", "Compound",
135 DEF_BUTTON_COMPOUND, -1, Tk_Offset(TkMenuButton, compound), 0,
137 {TK_OPTION_STRING_TABLE, "-state", "state", "State",
138 DEF_MENUBUTTON_STATE, -1, Tk_Offset(TkMenuButton, state),
140 {TK_OPTION_STRING, "-takefocus", "takeFocus", "TakeFocus",
141 DEF_MENUBUTTON_TAKE_FOCUS, -1,
142 Tk_Offset(TkMenuButton, takeFocus), TK_OPTION_NULL_OK, 0, 0},
143 {TK_OPTION_STRING, "-text", "text", "Text",
144 DEF_MENUBUTTON_TEXT, -1, Tk_Offset(TkMenuButton, text), 0, 0, 0},
145 {TK_OPTION_STRING, "-textvariable", "textVariable", "Variable",
146 DEF_MENUBUTTON_TEXT_VARIABLE, -1,
147 Tk_Offset(TkMenuButton, textVarName), TK_OPTION_NULL_OK, 0, 0},
148 {TK_OPTION_INT, "-underline", "underline", "Underline",
149 DEF_MENUBUTTON_UNDERLINE, -1, Tk_Offset(TkMenuButton, underline),
151 {TK_OPTION_STRING, "-width", "width", "Width",
152 DEF_MENUBUTTON_WIDTH, -1, Tk_Offset(TkMenuButton, widthString),
154 {TK_OPTION_PIXELS, "-wraplength", "wrapLength", "WrapLength",
155 DEF_MENUBUTTON_WRAP_LENGTH, -1, Tk_Offset(TkMenuButton, wrapLength),
157 {TK_OPTION_END, NULL, NULL, NULL, NULL, 0, 0, 0, NULL, 0}
161 * The following tables define the menubutton widget commands and map the
162 * indexes into the string tables into a single enumerated type used to
163 * dispatch the scale widget command.
166 static const char *const commandNames[] = {
167 "cget", "configure", NULL
171 COMMAND_CGET, COMMAND_CONFIGURE
175 * Forward declarations for functions defined later in this file:
178 static void MenuButtonCmdDeletedProc(ClientData clientData);
179 static void MenuButtonEventProc(ClientData clientData,
181 static void MenuButtonImageProc(ClientData clientData,
182 int x, int y, int width, int height, int imgWidth,
184 static char * MenuButtonTextVarProc(ClientData clientData,
185 Tcl_Interp *interp, const char *name1,
186 const char *name2, int flags);
187 static int MenuButtonWidgetObjCmd(ClientData clientData,
188 Tcl_Interp *interp, int objc,
189 Tcl_Obj *const objv[]);
190 static int ConfigureMenuButton(Tcl_Interp *interp,
191 TkMenuButton *mbPtr, int objc,
192 Tcl_Obj *const objv[]);
193 static void DestroyMenuButton(char *memPtr);
196 *--------------------------------------------------------------
198 * Tk_MenubuttonObjCmd --
200 * This function is invoked to process the "button", "label",
201 * "radiobutton", and "checkbutton" Tcl commands. See the user
202 * documentation for details on what it does.
205 * A standard Tcl result.
208 * See the user documentation.
210 *--------------------------------------------------------------
215 ClientData clientData, /* NULL. */
216 Tcl_Interp *interp, /* Current interpreter. */
217 int objc, /* Number of arguments. */
218 Tcl_Obj *const objv[]) /* Argument objects. */
221 Tk_OptionTable optionTable;
225 Tcl_WrongNumArgs(interp, 1, objv, "pathName ?-option value ...?");
230 * Create the new window.
233 tkwin = Tk_CreateWindowFromPath(interp, Tk_MainWindow(interp),
234 Tcl_GetString(objv[1]), NULL);
240 * Create the option table for this widget class. If it has already been
241 * created, the cached pointer will be returned.
244 optionTable = Tk_CreateOptionTable(interp, optionSpecs);
246 Tk_SetClass(tkwin, "Menubutton");
247 mbPtr = TkpCreateMenuButton(tkwin);
249 Tk_SetClassProcs(tkwin, &menubuttonClass, mbPtr);
252 * Initialize the data structure for the button.
255 mbPtr->tkwin = tkwin;
256 mbPtr->display = Tk_Display(tkwin);
257 mbPtr->interp = interp;
258 mbPtr->widgetCmd = Tcl_CreateObjCommand(interp,
259 Tk_PathName(mbPtr->tkwin), MenuButtonWidgetObjCmd, mbPtr,
260 MenuButtonCmdDeletedProc);
261 mbPtr->optionTable = optionTable;
262 mbPtr->menuName = NULL;
264 mbPtr->underline = -1;
265 mbPtr->textVarName = NULL;
266 mbPtr->bitmap = None;
267 mbPtr->imageString = NULL;
269 mbPtr->state = STATE_NORMAL;
270 mbPtr->normalBorder = NULL;
271 mbPtr->activeBorder = NULL;
272 mbPtr->borderWidth = 0;
273 mbPtr->relief = TK_RELIEF_FLAT;
274 mbPtr->highlightWidth = 0;
275 mbPtr->highlightBgColorPtr = NULL;
276 mbPtr->highlightColorPtr = NULL;
278 mbPtr->tkfont = NULL;
279 mbPtr->normalFg = NULL;
280 mbPtr->activeFg = NULL;
281 mbPtr->disabledFg = NULL;
282 mbPtr->normalTextGC = NULL;
283 mbPtr->activeTextGC = NULL;
285 mbPtr->disabledGC = NULL;
286 mbPtr->stippleGC = NULL;
287 mbPtr->leftBearing = 0;
288 mbPtr->rightBearing = 0;
289 mbPtr->widthString = NULL;
290 mbPtr->heightString = NULL;
293 mbPtr->wrapLength = 0;
296 mbPtr->anchor = TK_ANCHOR_CENTER;
297 mbPtr->justify = TK_JUSTIFY_CENTER;
298 mbPtr->textLayout = NULL;
299 mbPtr->indicatorOn = 0;
300 mbPtr->indicatorWidth = 0;
301 mbPtr->indicatorHeight = 0;
302 mbPtr->direction = DIRECTION_FLUSH;
303 mbPtr->cursor = NULL;
304 mbPtr->takeFocus = NULL;
307 Tk_CreateEventHandler(mbPtr->tkwin,
308 ExposureMask|StructureNotifyMask|FocusChangeMask,
309 MenuButtonEventProc, mbPtr);
311 if (Tk_InitOptions(interp, (char *) mbPtr, optionTable, tkwin) != TCL_OK) {
312 Tk_DestroyWindow(mbPtr->tkwin);
316 if (ConfigureMenuButton(interp, mbPtr, objc-2, objv+2) != TCL_OK) {
317 Tk_DestroyWindow(mbPtr->tkwin);
321 Tcl_SetObjResult(interp, TkNewWindowObj(mbPtr->tkwin));
326 *--------------------------------------------------------------
328 * MenuButtonWidgetObjCmd --
330 * This function is invoked to process the Tcl command that corresponds
331 * to a widget managed by this module. See the user documentation for
332 * details on what it does.
335 * A standard Tcl result.
338 * See the user documentation.
340 *--------------------------------------------------------------
344 MenuButtonWidgetObjCmd(
345 ClientData clientData, /* Information about button widget. */
346 Tcl_Interp *interp, /* Current interpreter. */
347 int objc, /* Number of arguments. */
348 Tcl_Obj *const objv[]) /* Argument objects. */
350 TkMenuButton *mbPtr = clientData;
355 Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?");
358 result = Tcl_GetIndexFromObjStruct(interp, objv[1], commandNames,
359 sizeof(char *), "option", 0, &index);
360 if (result != TCL_OK) {
368 Tcl_WrongNumArgs(interp, 1, objv, "cget option");
372 objPtr = Tk_GetOptionValue(interp, (char *) mbPtr,
373 mbPtr->optionTable, objv[2], mbPtr->tkwin);
374 if (objPtr == NULL) {
377 Tcl_SetObjResult(interp, objPtr);
380 case COMMAND_CONFIGURE:
382 objPtr = Tk_GetOptionInfo(interp, (char *) mbPtr,
383 mbPtr->optionTable, (objc == 3) ? objv[2] : NULL,
385 if (objPtr == NULL) {
388 Tcl_SetObjResult(interp, objPtr);
390 result = ConfigureMenuButton(interp, mbPtr, objc-2, objv+2);
403 *----------------------------------------------------------------------
405 * DestroyMenuButton --
407 * This function is invoked to recycle all of the resources associated
408 * with a menubutton widget. It is invoked as a when-idle handler in
409 * order to make sure that there is no other use of the menubutton
410 * pending at the time of the deletion.
416 * Everything associated with the widget is freed up.
418 *----------------------------------------------------------------------
423 char *memPtr) /* Info about button widget. */
425 TkMenuButton *mbPtr = (TkMenuButton *) memPtr;
426 TkpDestroyMenuButton(mbPtr);
428 if (mbPtr->flags & REDRAW_PENDING) {
429 Tcl_CancelIdleCall(TkpDisplayMenuButton, mbPtr);
433 * Free up all the stuff that requires special handling, then let
434 * Tk_FreeOptions handle all the standard option-related stuff.
437 Tcl_DeleteCommandFromToken(mbPtr->interp, mbPtr->widgetCmd);
438 if (mbPtr->textVarName != NULL) {
439 Tcl_UntraceVar2(mbPtr->interp, mbPtr->textVarName, NULL,
440 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
441 MenuButtonTextVarProc, mbPtr);
443 if (mbPtr->image != NULL) {
444 Tk_FreeImage(mbPtr->image);
446 if (mbPtr->normalTextGC != NULL) {
447 Tk_FreeGC(mbPtr->display, mbPtr->normalTextGC);
449 if (mbPtr->activeTextGC != NULL) {
450 Tk_FreeGC(mbPtr->display, mbPtr->activeTextGC);
452 if (mbPtr->disabledGC != NULL) {
453 Tk_FreeGC(mbPtr->display, mbPtr->disabledGC);
455 if (mbPtr->stippleGC != NULL) {
456 Tk_FreeGC(mbPtr->display, mbPtr->stippleGC);
458 if (mbPtr->gray != None) {
459 Tk_FreeBitmap(mbPtr->display, mbPtr->gray);
461 if (mbPtr->textLayout != NULL) {
462 Tk_FreeTextLayout(mbPtr->textLayout);
464 Tk_FreeConfigOptions((char *) mbPtr, mbPtr->optionTable, mbPtr->tkwin);
466 Tcl_EventuallyFree(mbPtr, TCL_DYNAMIC);
470 *----------------------------------------------------------------------
472 * ConfigureMenuButton --
474 * This function is called to process an argv/argc list, plus the Tk
475 * option database, in order to configure (or reconfigure) a menubutton
479 * The return value is a standard Tcl result. If TCL_ERROR is returned,
480 * then the interp's result contains an error message.
483 * Configuration information, such as text string, colors, font, etc. get
484 * set for mbPtr; old resources get freed, if there were any. The
485 * menubutton is redisplayed.
487 *----------------------------------------------------------------------
492 Tcl_Interp *interp, /* Used for error reporting. */
494 /* Information about widget; may or may not
495 * already have values for some fields. */
496 int objc, /* Number of valid entries in objv. */
497 Tcl_Obj *const objv[]) /* Arguments. */
499 Tk_SavedOptions savedOptions;
500 Tcl_Obj *errorResult = NULL;
505 * Eliminate any existing trace on variables monitored by the menubutton.
508 if (mbPtr->textVarName != NULL) {
509 Tcl_UntraceVar2(interp, mbPtr->textVarName, NULL,
510 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
511 MenuButtonTextVarProc, mbPtr);
515 * The following loop is potentially executed twice. During the first pass
516 * configuration options get set to their new values. If there is an error
517 * in this pass, we execute a second pass to restore all the options to
518 * their previous values.
521 for (error = 0; error <= 1; error++) {
524 * First pass: set options to new values.
527 if (Tk_SetOptions(interp, (char *) mbPtr,
528 mbPtr->optionTable, objc, objv,
529 mbPtr->tkwin, &savedOptions, NULL) != TCL_OK) {
534 * Second pass: restore options to old values.
537 errorResult = Tcl_GetObjResult(interp);
538 Tcl_IncrRefCount(errorResult);
539 Tk_RestoreSavedOptions(&savedOptions);
543 * A few options need special processing, such as setting the
544 * background from a 3-D border, or filling in complicated defaults
545 * that couldn't be specified to Tk_SetOptions.
548 if ((mbPtr->state == STATE_ACTIVE)
549 && !Tk_StrictMotif(mbPtr->tkwin)) {
550 Tk_SetBackgroundFromBorder(mbPtr->tkwin, mbPtr->activeBorder);
552 Tk_SetBackgroundFromBorder(mbPtr->tkwin, mbPtr->normalBorder);
555 if (mbPtr->highlightWidth < 0) {
556 mbPtr->highlightWidth = 0;
559 if (mbPtr->padX < 0) {
562 if (mbPtr->padY < 0) {
567 * Get the image for the widget, if there is one. Allocate the new
568 * image before freeing the old one, so that the reference count
569 * doesn't go to zero and cause image data to be discarded.
572 if (mbPtr->imageString != NULL) {
573 image = Tk_GetImage(mbPtr->interp, mbPtr->tkwin,
574 mbPtr->imageString, MenuButtonImageProc, mbPtr);
581 if (mbPtr->image != NULL) {
582 Tk_FreeImage(mbPtr->image);
584 mbPtr->image = image;
587 * Recompute the geometry for the button.
590 if ((mbPtr->bitmap != None) || (mbPtr->image != NULL)) {
591 if (Tk_GetPixels(interp, mbPtr->tkwin, mbPtr->widthString,
592 &mbPtr->width) != TCL_OK) {
594 Tcl_AddErrorInfo(interp, "\n (processing -width option)");
597 if (Tk_GetPixels(interp, mbPtr->tkwin, mbPtr->heightString,
598 &mbPtr->height) != TCL_OK) {
600 Tcl_AddErrorInfo(interp, "\n (processing -height option)");
604 if (Tcl_GetInt(interp, mbPtr->widthString, &mbPtr->width)
608 if (Tcl_GetInt(interp, mbPtr->heightString, &mbPtr->height)
617 Tk_FreeSavedOptions(&savedOptions);
620 if (mbPtr->textVarName != NULL) {
622 * If no image or -compound is used, display the value of a variable.
623 * Set up a trace to watch for any changes in it, create the variable
624 * if it doesn't exist, and fetch its current value.
628 value = Tcl_GetVar2(interp, mbPtr->textVarName, NULL, TCL_GLOBAL_ONLY);
630 Tcl_SetVar2(interp, mbPtr->textVarName, NULL, mbPtr->text,
633 if (mbPtr->text != NULL) {
636 mbPtr->text = ckalloc(strlen(value) + 1);
637 strcpy(mbPtr->text, value);
639 Tcl_TraceVar2(interp, mbPtr->textVarName, NULL,
640 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
641 MenuButtonTextVarProc, mbPtr);
644 TkMenuButtonWorldChanged(mbPtr);
646 Tcl_SetObjResult(interp, errorResult);
647 Tcl_DecrRefCount(errorResult);
654 *---------------------------------------------------------------------------
656 * TkMenuButtonWorldChanged --
658 * This function is called when the world has changed in some way and the
659 * widget needs to recompute all its graphics contexts and determine its
666 * TkMenuButton will be relayed out and redisplayed.
668 *---------------------------------------------------------------------------
672 TkMenuButtonWorldChanged(
673 ClientData instanceData) /* Information about widget. */
678 TkMenuButton *mbPtr = instanceData;
680 gcValues.font = Tk_FontId(mbPtr->tkfont);
681 gcValues.foreground = mbPtr->normalFg->pixel;
682 gcValues.background = Tk_3DBorderColor(mbPtr->normalBorder)->pixel;
685 * Note: GraphicsExpose events are disabled in GC's because they're used
686 * to copy stuff from an off-screen pixmap onto the screen (we know that
687 * there's no problem with obscured areas).
690 gcValues.graphics_exposures = False;
691 mask = GCForeground | GCBackground | GCFont | GCGraphicsExposures;
692 gc = Tk_GetGC(mbPtr->tkwin, mask, &gcValues);
693 if (mbPtr->normalTextGC != NULL) {
694 Tk_FreeGC(mbPtr->display, mbPtr->normalTextGC);
696 mbPtr->normalTextGC = gc;
698 gcValues.foreground = mbPtr->activeFg->pixel;
699 gcValues.background = Tk_3DBorderColor(mbPtr->activeBorder)->pixel;
700 mask = GCForeground | GCBackground | GCFont;
701 gc = Tk_GetGC(mbPtr->tkwin, mask, &gcValues);
702 if (mbPtr->activeTextGC != NULL) {
703 Tk_FreeGC(mbPtr->display, mbPtr->activeTextGC);
705 mbPtr->activeTextGC = gc;
707 gcValues.background = Tk_3DBorderColor(mbPtr->normalBorder)->pixel;
710 * Create the GC that can be used for stippling
713 if (mbPtr->stippleGC == NULL) {
714 gcValues.foreground = gcValues.background;
716 if (mbPtr->gray == None) {
717 mbPtr->gray = Tk_GetBitmap(NULL, mbPtr->tkwin, "gray50");
719 if (mbPtr->gray != None) {
720 gcValues.fill_style = FillStippled;
721 gcValues.stipple = mbPtr->gray;
722 mask |= GCFillStyle | GCStipple;
724 mbPtr->stippleGC = Tk_GetGC(mbPtr->tkwin, mask, &gcValues);
728 * Allocate the disabled graphics context, for drawing text in its
732 mask = GCForeground | GCBackground | GCFont;
733 if (mbPtr->disabledFg != NULL) {
734 gcValues.foreground = mbPtr->disabledFg->pixel;
736 gcValues.foreground = gcValues.background;
738 gc = Tk_GetGC(mbPtr->tkwin, mask, &gcValues);
739 if (mbPtr->disabledGC != NULL) {
740 Tk_FreeGC(mbPtr->display, mbPtr->disabledGC);
742 mbPtr->disabledGC = gc;
744 TkpComputeMenuButtonGeometry(mbPtr);
747 * Lastly, arrange for the button to be redisplayed.
750 if (Tk_IsMapped(mbPtr->tkwin) && !(mbPtr->flags & REDRAW_PENDING)) {
751 Tcl_DoWhenIdle(TkpDisplayMenuButton, mbPtr);
752 mbPtr->flags |= REDRAW_PENDING;
757 *--------------------------------------------------------------
759 * MenuButtonEventProc --
761 * This function is invoked by the Tk dispatcher for various events on
768 * When the window gets deleted, internal structures get cleaned up.
769 * When it gets exposed, it is redisplayed.
771 *--------------------------------------------------------------
776 ClientData clientData, /* Information about window. */
777 XEvent *eventPtr) /* Information about event. */
779 TkMenuButton *mbPtr = clientData;
781 if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
783 } else if (eventPtr->type == ConfigureNotify) {
785 * Must redraw after size changes, since layout could have changed and
786 * borders will need to be redrawn.
790 } else if (eventPtr->type == DestroyNotify) {
791 DestroyMenuButton((char *) mbPtr);
792 } else if (eventPtr->type == FocusIn) {
793 if (eventPtr->xfocus.detail != NotifyInferior) {
794 mbPtr->flags |= GOT_FOCUS;
795 if (mbPtr->highlightWidth > 0) {
799 } else if (eventPtr->type == FocusOut) {
800 if (eventPtr->xfocus.detail != NotifyInferior) {
801 mbPtr->flags &= ~GOT_FOCUS;
802 if (mbPtr->highlightWidth > 0) {
810 if ((mbPtr->tkwin != NULL) && !(mbPtr->flags & REDRAW_PENDING)) {
811 Tcl_DoWhenIdle(TkpDisplayMenuButton, mbPtr);
812 mbPtr->flags |= REDRAW_PENDING;
817 *----------------------------------------------------------------------
819 * MenuButtonCmdDeletedProc --
821 * This function is invoked when a widget command is deleted. If the
822 * widget isn't already in the process of being destroyed, this command
829 * The widget is destroyed.
831 *----------------------------------------------------------------------
835 MenuButtonCmdDeletedProc(
836 ClientData clientData) /* Pointer to widget record for widget. */
838 TkMenuButton *mbPtr = clientData;
839 Tk_Window tkwin = mbPtr->tkwin;
842 * This function could be invoked either because the window was destroyed
843 * and the command was then deleted (in which case tkwin is NULL) or
844 * because the command was deleted, and then this function destroys the
849 Tk_DestroyWindow(tkwin);
854 *--------------------------------------------------------------
856 * MenuButtonTextVarProc --
858 * This function is invoked when someone changes the variable whose
859 * contents are to be displayed in a menu button.
862 * NULL is always returned.
865 * The text displayed in the menu button will change to match the
868 *--------------------------------------------------------------
873 MenuButtonTextVarProc(
874 ClientData clientData, /* Information about button. */
875 Tcl_Interp *interp, /* Interpreter containing variable. */
876 const char *name1, /* Name of variable. */
877 const char *name2, /* Second part of variable name. */
878 int flags) /* Information about what happened. */
880 TkMenuButton *mbPtr = clientData;
885 * If the variable is unset, then immediately recreate it unless the whole
886 * interpreter is going away.
889 if (flags & TCL_TRACE_UNSETS) {
890 if (!Tcl_InterpDeleted(interp) && mbPtr->textVarName) {
891 ClientData probe = NULL;
894 probe = Tcl_VarTraceInfo(interp,
896 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
897 MenuButtonTextVarProc, probe);
898 if (probe == (ClientData)mbPtr) {
904 * We were able to fetch the unset trace for our
905 * textVarName, which means it is not unset and not
906 * the cause of this unset trace. Instead some outdated
907 * former variable must be, and we should ignore it.
911 Tcl_SetVar2(interp, mbPtr->textVarName, NULL, mbPtr->text,
913 Tcl_TraceVar2(interp, mbPtr->textVarName, NULL,
914 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
915 MenuButtonTextVarProc, clientData);
920 value = Tcl_GetVar2(interp, mbPtr->textVarName, NULL, TCL_GLOBAL_ONLY);
924 if (mbPtr->text != NULL) {
927 len = 1 + (unsigned) strlen(value);
928 mbPtr->text = ckalloc(len);
929 memcpy(mbPtr->text, value, len);
930 TkpComputeMenuButtonGeometry(mbPtr);
932 if ((mbPtr->tkwin != NULL) && Tk_IsMapped(mbPtr->tkwin)
933 && !(mbPtr->flags & REDRAW_PENDING)) {
934 Tcl_DoWhenIdle(TkpDisplayMenuButton, mbPtr);
935 mbPtr->flags |= REDRAW_PENDING;
941 *----------------------------------------------------------------------
943 * MenuButtonImageProc --
945 * This function is invoked by the image code whenever the manager for an
946 * image does something that affects the size of contents of an image
947 * displayed in a button.
953 * Arranges for the button to get redisplayed.
955 *----------------------------------------------------------------------
960 ClientData clientData, /* Pointer to widget record. */
961 int x, int y, /* Upper left pixel (within image) that must
963 int width, int height, /* Dimensions of area to redisplay (may be <=
965 int imgWidth, int imgHeight)/* New dimensions of image. */
967 TkMenuButton *mbPtr = clientData;
969 if (mbPtr->tkwin != NULL) {
970 TkpComputeMenuButtonGeometry(mbPtr);
971 if (Tk_IsMapped(mbPtr->tkwin) && !(mbPtr->flags & REDRAW_PENDING)) {
972 Tcl_DoWhenIdle(TkpDisplayMenuButton, mbPtr);
973 mbPtr->flags |= REDRAW_PENDING;