home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / devel / lang / lisp / stk-3.002 / stk-3 / STk-3.1 / Tk / generic / tkMenubutton.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-05-31  |  39.2 KB  |  1,246 lines

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