4 * This module implements button-like widgets that are used
5 * to invoke pull-down menus.
7 * Copyright (c) 1990-1994 The Regents of the University of California.
8 * Copyright (c) 1994-1995 Sun Microsystems, Inc.
10 * See the file "license.terms" for information on usage and redistribution
11 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
13 * SCCS: @(#) tkMenubutton.c 1.77 96/02/15 18:52:22
21 * A data structure of the following type is kept for each
22 * widget managed by this file:
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
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.
39 * Information about what's displayed in the menu button:
42 char *text; /* Text to display in button (malloc'ed)
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
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
59 * Information used when displaying widget:
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
68 Tk_3DBorder activeBorder; /* Structure used to draw 3-D
69 * border and background when window
70 * is active. NULL means no such
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
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
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
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,
124 int textHeight; /* Height needed to display text as requested,
126 int indicatorOn; /* Non-zero means display indicator; 0 means
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. */
136 * Miscellaneous information:
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
148 * Flag bits for buttons:
150 * REDRAW_PENDING: Non-zero means a DoWhenIdle handler
151 * has already been queued to redraw
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.
160 #define REDRAW_PENDING 1
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.
170 #define INDICATOR_WIDTH 40
171 #define INDICATOR_HEIGHT 17
174 * Information used for parsing configuration specs:
177 static Tk_ConfigSpec configSpecs[] =
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,
201 {TK_CONFIG_SYNONYM, "-bg", "background", (char *)NULL,
203 {TK_CONFIG_BITMAP, "-bitmap", "bitmap", "Bitmap",
204 DEF_MENUBUTTON_BITMAP, Tk_Offset(MenuButton, bitmap),
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),
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,
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),
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),
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),
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),
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,
272 * Forward declarations for procedures defined later in this file:
275 static void ComputeMenuButtonGeometry _ANSI_ARGS_((
277 static void MenuButtonCmdDeletedProc _ANSI_ARGS_((
278 ClientData clientData));
279 static void MenuButtonEventProc _ANSI_ARGS_((ClientData clientData,
281 static void MenuButtonImageProc _ANSI_ARGS_((ClientData clientData,
282 int x, int y, int width, int height, int imgWidth,
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,
292 static void DestroyMenuButton _ANSI_ARGS_((char *memPtr));
293 static void DisplayMenuButton _ANSI_ARGS_((ClientData clientData));
296 *--------------------------------------------------------------
298 * Tk_MenubuttonCmd --
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.
305 * A standard Tcl result.
308 * See the user documentation.
310 *--------------------------------------------------------------
314 Tk_MenubuttonCmd(clientData, interp, argc, argv)
315 ClientData clientData; /* Main window associated with
317 Tcl_Interp *interp; /* Current interpreter. */
318 int argc; /* Number of arguments. */
319 char **argv; /* Argument strings. */
321 register MenuButton *mbPtr;
325 Tcl_AppendResult(interp, "wrong # args: should be \"",
326 argv[0], " pathName ?options?\"", (char *)NULL);
330 * Create the new window.
333 tkwin = Tk_CreateWindowFromPath(interp, Tk_MainWindow(interp), argv[1],
339 * Initialize the data structure for the button.
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;
351 mbPtr->underline = -1;
352 mbPtr->textVarName = NULL;
353 mbPtr->bitmap = None;
354 mbPtr->imageString = 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;
365 mbPtr->fontPtr = NULL;
366 mbPtr->normalFg = NULL;
367 mbPtr->activeFg = NULL;
368 mbPtr->disabledFg = NULL;
369 mbPtr->normalTextGC = None;
370 mbPtr->activeTextGC = None;
372 mbPtr->disabledGC = None;
373 mbPtr->leftBearing = 0;
374 mbPtr->rightBearing = 0;
375 mbPtr->widthString = NULL;
376 mbPtr->heightString = NULL;
379 mbPtr->wrapLength = 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;
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);
399 Tcl_SetResult(interp, Tk_PathName(mbPtr->tkwin), TCL_VOLATILE);
404 *--------------------------------------------------------------
406 * MenuButtonWidgetCmd --
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.
413 * A standard Tcl result.
416 * See the user documentation.
418 *--------------------------------------------------------------
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. */
428 register MenuButton *mbPtr = clientData;
434 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
435 " option ?arg arg ...?\"", (char *)NULL);
438 Tcl_Preserve((ClientData)mbPtr);
440 length = strlen(argv[1]);
441 if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
444 Tcl_AppendResult(interp, "wrong # args: should be \"",
445 argv[0], " cget option\"",
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)
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);
460 result = ConfigureMenuButton(interp, mbPtr, argc - 2, argv + 2,
461 TK_CONFIG_ARGV_ONLY);
464 Tcl_AppendResult(interp, "bad option \"", argv[1],
465 "\": must be cget or configure",
469 Tcl_Release((ClientData)mbPtr);
473 Tcl_Release((ClientData)mbPtr);
478 *----------------------------------------------------------------------
480 * DestroyMenuButton --
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.
491 * Everything associated with the widget is freed up.
493 *----------------------------------------------------------------------
497 DestroyMenuButton(memPtr)
498 char *memPtr; /* Info about button widget. */
500 register MenuButton *mbPtr = (MenuButton *)memPtr;
503 * Free up all the stuff that requires special handling, then
504 * let Tk_FreeOptions handle all the standard option-related
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);
513 if (mbPtr->image != NULL) {
514 Tk_FreeImage(mbPtr->image);
516 if (mbPtr->normalTextGC != None) {
517 Tk_FreeGC(mbPtr->display, mbPtr->normalTextGC);
519 if (mbPtr->activeTextGC != None) {
520 Tk_FreeGC(mbPtr->display, mbPtr->activeTextGC);
522 if (mbPtr->gray != None) {
523 Tk_FreeBitmap(mbPtr->display, mbPtr->gray);
525 if (mbPtr->disabledGC != None) {
526 Tk_FreeGC(mbPtr->display, mbPtr->disabledGC);
528 Tk_FreeOptions(configSpecs, (char *)mbPtr, mbPtr->display, 0);
533 *----------------------------------------------------------------------
535 * ConfigureMenuButton --
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.
542 * The return value is a standard Tcl result. If TCL_ERROR is
543 * returned, then interp->result contains an error message.
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.
550 *----------------------------------------------------------------------
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. */
569 * Eliminate any existing trace on variables monitored by the menubutton.
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);
577 result = Tk_ConfigureWidget(interp, mbPtr->tkwin, configSpecs,
578 argc, argv, (char *)mbPtr, flags);
579 if (result != TCL_OK) {
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.
588 if ((mbPtr->state == tkActiveUid) && !Tk_StrictMotif(mbPtr->tkwin)) {
589 Tk_SetBackgroundFromBorder(mbPtr->tkwin, mbPtr->activeBorder);
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;
601 if (mbPtr->highlightWidth < 0) {
602 mbPtr->highlightWidth = 0;
604 gcValues.font = mbPtr->fontPtr->fid;
605 gcValues.foreground = mbPtr->normalFg->pixel;
606 gcValues.background = Tk_3DBorderColor(mbPtr->normalBorder)->pixel;
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).
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);
620 mbPtr->normalTextGC = newGC;
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,
627 if (mbPtr->activeTextGC != None) {
628 Tk_FreeGC(mbPtr->display, mbPtr->activeTextGC);
630 mbPtr->activeTextGC = newGC;
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;
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) {
646 gcValues.fill_style = FillStippled;
647 gcValues.stipple = mbPtr->gray;
648 mask = GCForeground | GCFillStyle | GCStipple;
650 newGC = Tk_GetGC(mbPtr->tkwin, mask, &gcValues);
651 if (mbPtr->disabledGC != None) {
652 Tk_FreeGC(mbPtr->display, mbPtr->disabledGC);
654 mbPtr->disabledGC = newGC;
656 if (mbPtr->padX < 0) {
659 if (mbPtr->padY < 0) {
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.
668 if (mbPtr->imageString != NULL) {
669 image = Tk_GetImage(mbPtr->interp, mbPtr->tkwin,
670 mbPtr->imageString, MenuButtonImageProc, (ClientData)mbPtr);
677 if (mbPtr->image != NULL) {
678 Tk_FreeImage(mbPtr->image);
680 mbPtr->image = image;
682 if ((mbPtr->image == NULL) && (mbPtr->bitmap == None)
683 && (mbPtr->textVarName != NULL)) {
685 * The menubutton displays a variable. Set up a trace to watch
686 * for any changes in it.
691 value = Tcl_GetVar(interp, mbPtr->textVarName, TCL_GLOBAL_ONLY);
693 Tcl_SetVar(interp, mbPtr->textVarName, mbPtr->text,
696 if (mbPtr->text != NULL) {
697 ckBlt_Free(mbPtr->text);
699 mbPtr->text = Blt_Malloc(strlen(value) + 1);
700 strcpy(mbPtr->text, value);
702 Tcl_TraceVar(interp, mbPtr->textVarName,
703 TCL_GLOBAL_ONLY | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
704 MenuButtonTextVarProc, (ClientData)mbPtr);
707 * Recompute the geometry for the button.
710 if ((mbPtr->bitmap != None) || (mbPtr->image != NULL)) {
711 if (Tk_GetPixels(interp, mbPtr->tkwin, mbPtr->widthString,
712 &mbPtr->width) != TCL_OK) {
714 Tcl_AddErrorInfo(interp, "\n (processing -width option)");
717 if (Tk_GetPixels(interp, mbPtr->tkwin, mbPtr->heightString,
718 &mbPtr->height) != TCL_OK) {
720 Tcl_AddErrorInfo(interp, "\n (processing -height option)");
724 if (Tcl_GetInt(interp, mbPtr->widthString, &mbPtr->width)
728 if (Tcl_GetInt(interp, mbPtr->heightString, &mbPtr->height)
733 ComputeMenuButtonGeometry(mbPtr);
736 * Lastly, arrange for the button to be redisplayed.
739 if (Tk_IsMapped(mbPtr->tkwin) && !(mbPtr->flags & REDRAW_PENDING)) {
740 Tcl_DoWhenIdle(DisplayMenuButton, (ClientData)mbPtr);
741 mbPtr->flags |= REDRAW_PENDING;
747 *----------------------------------------------------------------------
749 * DisplayMenuButton --
751 * This procedure is invoked to display a menubutton widget.
757 * Commands are output to X to display the menubutton in its
760 *----------------------------------------------------------------------
764 DisplayMenuButton(clientData)
765 ClientData clientData; /* Information about widget. */
767 register MenuButton *mbPtr = clientData;
771 int x = 0; /* Initialization needed only to stop
772 * compiler warning. */
774 register Tk_Window tkwin = mbPtr->tkwin;
777 mbPtr->flags &= ~REDRAW_PENDING;
778 if ((mbPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
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;
788 gc = mbPtr->normalTextGC;
789 border = mbPtr->normalBorder;
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.
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);
805 * Display image or bitmap or text for button.
808 if (mbPtr->image != None) {
809 Tk_SizeOfImage(mbPtr->image, &width, &height);
812 switch (mbPtr->anchor) {
819 case TK_ANCHOR_CENTER:
821 x += ((int)(Tk_Width(tkwin) - width
822 - mbPtr->indicatorWidth)) / 2;
825 x += Tk_Width(tkwin) - mbPtr->inset - width
826 - mbPtr->indicatorWidth;
829 switch (mbPtr->anchor) {
836 case TK_ANCHOR_CENTER:
838 y = ((int)(Tk_Height(tkwin) - height)) / 2;
841 y = Tk_Height(tkwin) - mbPtr->inset - height;
844 if (mbPtr->image != NULL) {
845 Tk_RedrawImage(mbPtr->image, 0, 0, width, height, pixmap,
848 XCopyPlane(mbPtr->display, mbPtr->bitmap, pixmap,
849 gc, 0, 0, (unsigned)width, (unsigned)height, x, y, 1);
851 } else if (mbPtr->bitmap != None) {
852 Tk_SizeOfBitmap(mbPtr->display, mbPtr->bitmap, &width, &height);
855 width = mbPtr->textWidth;
856 height = mbPtr->textHeight;
857 switch (mbPtr->anchor) {
861 x = mbPtr->inset + mbPtr->padX;
864 case TK_ANCHOR_CENTER:
866 x = ((int)(Tk_Width(tkwin) - width
867 - mbPtr->indicatorWidth)) / 2;
870 x = Tk_Width(tkwin) - width - mbPtr->padX - mbPtr->inset
871 - mbPtr->indicatorWidth;
874 switch (mbPtr->anchor) {
878 y = mbPtr->inset + mbPtr->padY;
881 case TK_ANCHOR_CENTER:
883 y = ((int)(Tk_Height(tkwin) - height)) / 2;
886 y = Tk_Height(tkwin) - mbPtr->inset - mbPtr->padY - height;
889 TkDisplayText(mbPtr->display, pixmap, mbPtr->fontPtr,
890 mbPtr->text, mbPtr->numChars, x, y, mbPtr->textWidth,
891 mbPtr->justify, mbPtr->underline, gc);
895 * If the menu button is disabled with a stipple rather than a special
896 * foreground color, generate the stippled effect.
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));
907 * Draw the cascade indicator for the menu button on the
908 * right side of the window, if desired.
911 if (mbPtr->indicatorOn) {
914 borderWidth = (mbPtr->indicatorHeight + 1) / 3;
915 if (borderWidth < 1) {
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);
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
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);
938 if (mbPtr->highlightWidth != 0) {
941 if (mbPtr->flags & GOT_FOCUS) {
942 gc = Tk_GCForColor(mbPtr->highlightColorPtr, pixmap);
944 gc = Tk_GCForColor(mbPtr->highlightBgColorPtr, pixmap);
946 Tk_DrawFocusHighlight(tkwin, gc, mbPtr->highlightWidth, pixmap);
949 * Copy the information from the off-screen pixmap onto the screen,
950 * then delete the pixmap.
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);
960 *--------------------------------------------------------------
962 * MenuButtonEventProc --
964 * This procedure is invoked by the Tk dispatcher for various
971 * When the window gets deleted, internal structures get
972 * cleaned up. When it gets exposed, it is redisplayed.
974 *--------------------------------------------------------------
978 MenuButtonEventProc(clientData, eventPtr)
979 ClientData clientData; /* Information about window. */
980 XEvent *eventPtr; /* Information about event. */
982 MenuButton *mbPtr = clientData;
983 if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
985 } else if (eventPtr->type == ConfigureNotify) {
987 * Must redraw after size changes, since layout could have changed
988 * and borders will need to be redrawn.
992 } else if (eventPtr->type == DestroyNotify) {
993 if (mbPtr->tkwin != NULL) {
995 Tcl_DeleteCommandFromToken(mbPtr->interp, mbPtr->widgetCmd);
997 if (mbPtr->flags & REDRAW_PENDING) {
998 Tcl_CancelIdleCall(DisplayMenuButton, (ClientData)mbPtr);
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) {
1008 } else if (eventPtr->type == FocusOut) {
1009 if (eventPtr->xfocus.detail != NotifyInferior) {
1010 mbPtr->flags &= ~GOT_FOCUS;
1011 if (mbPtr->highlightWidth > 0) {
1019 if ((mbPtr->tkwin != NULL) && !(mbPtr->flags & REDRAW_PENDING)) {
1020 Tcl_DoWhenIdle(DisplayMenuButton, (ClientData)mbPtr);
1021 mbPtr->flags |= REDRAW_PENDING;
1026 *----------------------------------------------------------------------
1028 * MenuButtonCmdDeletedProc --
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.
1038 * The widget is destroyed.
1040 *----------------------------------------------------------------------
1044 MenuButtonCmdDeletedProc(clientData)
1045 ClientData clientData; /* Pointer to widget record for widget. */
1047 MenuButton *mbPtr = clientData;
1048 Tk_Window tkwin = mbPtr->tkwin;
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.
1057 if (tkwin != NULL) {
1058 mbPtr->tkwin = NULL;
1059 Tk_DestroyWindow(tkwin);
1064 *----------------------------------------------------------------------
1066 * ComputeMenuButtonGeometry --
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.
1076 * The menu button's window may change size.
1078 *----------------------------------------------------------------------
1082 ComputeMenuButtonGeometry(mbPtr)
1083 register MenuButton *mbPtr; /* Widget record for menu button. */
1085 int width, height, mm, pixels;
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;
1093 if (mbPtr->height > 0) {
1094 height = mbPtr->height;
1096 } else if (mbPtr->bitmap != None) {
1097 Tk_SizeOfBitmap(mbPtr->display, mbPtr->bitmap, &width, &height);
1098 if (mbPtr->width > 0) {
1099 width = mbPtr->width;
1101 if (mbPtr->height > 0) {
1102 height = mbPtr->height;
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);
1114 if (mbPtr->height > 0) {
1115 height = mbPtr->height * (mbPtr->fontPtr->ascent
1116 + mbPtr->fontPtr->descent);
1118 width += 2 * mbPtr->padX;
1119 height += 2 * mbPtr->padY;
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;
1130 mbPtr->indicatorHeight = 0;
1131 mbPtr->indicatorWidth = 0;
1134 Tk_GeometryRequest(mbPtr->tkwin, (int)(width + 2 * mbPtr->inset),
1135 (int)(height + 2 * mbPtr->inset));
1136 Tk_SetInternalBorder(mbPtr->tkwin, mbPtr->inset);
1140 *--------------------------------------------------------------
1142 * MenuButtonTextVarProc --
1144 * This procedure is invoked when someone changes the variable
1145 * whose contents are to be displayed in a menu button.
1148 * NULL is always returned.
1151 * The text displayed in the menu button will change to match the
1154 *--------------------------------------------------------------
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. */
1166 register MenuButton *mbPtr = clientData;
1170 * If the variable is unset, then immediately recreate it unless
1171 * the whole interpreter is going away.
1174 if (flags & TCL_TRACE_UNSETS) {
1175 if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
1176 Tcl_SetVar(interp, mbPtr->textVarName, mbPtr->text,
1178 Tcl_TraceVar(interp, mbPtr->textVarName,
1179 TCL_GLOBAL_ONLY | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
1180 MenuButtonTextVarProc, clientData);
1182 return (char *) NULL;
1184 value = Tcl_GetVar(interp, mbPtr->textVarName, TCL_GLOBAL_ONLY);
1185 if (value == NULL) {
1188 if (mbPtr->text != NULL) {
1189 ckBlt_Free(mbPtr->text);
1191 mbPtr->text = Blt_Malloc(strlen(value) + 1);
1192 strcpy(mbPtr->text, value);
1193 ComputeMenuButtonGeometry(mbPtr);
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;
1200 return (char *) NULL;
1204 *----------------------------------------------------------------------
1206 * MenuButtonImageProc --
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.
1216 * Arranges for the button to get redisplayed.
1218 *----------------------------------------------------------------------
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
1228 int imgWidth, imgHeight; /* New dimensions of image. */
1230 register MenuButton *mbPtr = clientData;
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;