tkMenubutton.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:29k
源码类别:

通讯编程

开发平台:

Visual C++

  1. /* 
  2.  * tkMenubutton.c --
  3.  *
  4.  * This module implements button-like widgets that are used
  5.  * to invoke pull-down menus.
  6.  *
  7.  * Copyright (c) 1990-1994 The Regents of the University of California.
  8.  * Copyright (c) 1994-1997 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.  * RCS: @(#) $Id: tkMenubutton.c,v 1.12.2.2 2007/12/13 23:13:52 hobbs Exp $
  14.  */
  15. #include "tkMenubutton.h"
  16. #include "tkPort.h"
  17. #include "default.h"
  18. /*
  19.  * The following table defines the legal values for the -direction 
  20.  * option.  It is used together with the "enum direction" declaration 
  21.  * in tkMenubutton.h.
  22.  */
  23. static char *directionStrings[] = {
  24.     "above", "below", "flush", "left", "right", (char *) NULL
  25. };
  26. /*
  27.  * The following table defines the legal values for the -state option.
  28.  * It is used together with the "enum state" declaration in tkMenubutton.h.
  29.  */
  30. static char *stateStrings[] = {
  31.     "active", "disabled", "normal", (char *) NULL
  32. };
  33. /*
  34.  * The following table defines the legal values for the -compound option.
  35.  * It is used with the "enum compound" declaration in tkMenuButton.h
  36.  */
  37. static char *compoundStrings[] = {
  38.     "bottom", "center", "left", "none", "right", "top", (char *) NULL
  39. };
  40. /*
  41.  * Information used for parsing configuration specs:
  42.  */
  43. static Tk_OptionSpec optionSpecs[] = {
  44.     {TK_OPTION_BORDER, "-activebackground", "activeBackground", "Foreground",
  45.         DEF_MENUBUTTON_ACTIVE_BG_COLOR, -1, 
  46.         Tk_Offset(TkMenuButton, activeBorder), 0, 
  47.         (ClientData) DEF_MENUBUTTON_ACTIVE_BG_MONO, 0},
  48.     {TK_OPTION_COLOR, "-activeforeground", "activeForeground", "Background",
  49. DEF_MENUBUTTON_ACTIVE_FG_COLOR, -1, 
  50.          Tk_Offset(TkMenuButton, activeFg),
  51.          0, (ClientData) DEF_MENUBUTTON_ACTIVE_FG_MONO, 0},
  52.     {TK_OPTION_ANCHOR, "-anchor", "anchor", "Anchor",
  53. DEF_MENUBUTTON_ANCHOR, -1, 
  54.         Tk_Offset(TkMenuButton, anchor), 0, 0, 0},
  55.     {TK_OPTION_BORDER, "-background", "background", "Background",
  56. DEF_MENUBUTTON_BG_COLOR, -1, Tk_Offset(TkMenuButton, normalBorder),
  57.         0, (ClientData) DEF_MENUBUTTON_BG_MONO, 0},
  58.     {TK_OPTION_SYNONYM, "-bd", (char *) NULL, (char *) NULL,
  59. (char *) NULL, 0, -1, 0, (ClientData) "-borderwidth", 0},
  60.     {TK_OPTION_SYNONYM, "-bg", (char *) NULL, (char *) NULL,
  61. (char *) NULL, 0, -1, 0, (ClientData) "-background", 0},
  62.     {TK_OPTION_BITMAP, "-bitmap", "bitmap", "Bitmap",
  63. DEF_MENUBUTTON_BITMAP, -1, Tk_Offset(TkMenuButton, bitmap),
  64. TK_OPTION_NULL_OK, 0, 0},
  65.     {TK_OPTION_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
  66. DEF_MENUBUTTON_BORDER_WIDTH, -1, 
  67.         Tk_Offset(TkMenuButton, borderWidth), 0, 0, 0},
  68.     {TK_OPTION_CURSOR, "-cursor", "cursor", "Cursor",
  69. DEF_MENUBUTTON_CURSOR, -1, Tk_Offset(TkMenuButton, cursor),
  70. TK_OPTION_NULL_OK, 0, 0},
  71.     {TK_OPTION_STRING_TABLE, "-direction", "direction", "Direction",
  72.      DEF_MENUBUTTON_DIRECTION, -1, Tk_Offset(TkMenuButton, direction), 
  73. 0, (ClientData) directionStrings, 0},
  74.     {TK_OPTION_COLOR, "-disabledforeground", "disabledForeground",
  75. "DisabledForeground", DEF_MENUBUTTON_DISABLED_FG_COLOR,
  76. -1, Tk_Offset(TkMenuButton, disabledFg), TK_OPTION_NULL_OK,
  77. (ClientData) DEF_MENUBUTTON_DISABLED_FG_MONO, 0},
  78.     {TK_OPTION_SYNONYM, "-fg", "foreground", (char *) NULL,
  79. (char *) NULL, 0, -1, 0, (ClientData) "-foreground", 0},
  80.     {TK_OPTION_FONT, "-font", "font", "Font",
  81. DEF_MENUBUTTON_FONT, -1, Tk_Offset(TkMenuButton, tkfont), 0, 0, 0},
  82.     {TK_OPTION_COLOR, "-foreground", "foreground", "Foreground",
  83. DEF_MENUBUTTON_FG, -1, Tk_Offset(TkMenuButton, normalFg), 0, 0, 0},
  84.     {TK_OPTION_STRING, "-height", "height", "Height",
  85. DEF_MENUBUTTON_HEIGHT, -1, Tk_Offset(TkMenuButton, heightString), 
  86.         0, 0, 0},
  87.     {TK_OPTION_COLOR, "-highlightbackground", "highlightBackground",
  88. "HighlightBackground", DEF_MENUBUTTON_HIGHLIGHT_BG_COLOR,
  89. -1, Tk_Offset(TkMenuButton, highlightBgColorPtr), 0, 0, 0},
  90.     {TK_OPTION_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
  91. DEF_MENUBUTTON_HIGHLIGHT, -1, 
  92.         Tk_Offset(TkMenuButton, highlightColorPtr), 0, 0, 0},
  93.     {TK_OPTION_PIXELS, "-highlightthickness", "highlightThickness",
  94. "HighlightThickness", DEF_MENUBUTTON_HIGHLIGHT_WIDTH,
  95. -1, Tk_Offset(TkMenuButton, highlightWidth), 0, 0, 0},
  96.     {TK_OPTION_STRING, "-image", "image", "Image",
  97. DEF_MENUBUTTON_IMAGE, -1, Tk_Offset(TkMenuButton, imageString), 
  98. TK_OPTION_NULL_OK, 0, 0},
  99.     {TK_OPTION_BOOLEAN, "-indicatoron", "indicatorOn", "IndicatorOn",
  100. DEF_MENUBUTTON_INDICATOR, -1, Tk_Offset(TkMenuButton, indicatorOn),
  101.         0, 0, 0},
  102.     {TK_OPTION_JUSTIFY, "-justify", "justify", "Justify",
  103. DEF_BUTTON_JUSTIFY, -1, Tk_Offset(TkMenuButton, justify), 0, 0, 0},
  104.     {TK_OPTION_STRING, "-menu", "menu", "Menu",
  105. DEF_MENUBUTTON_MENU, -1, Tk_Offset(TkMenuButton, menuName), 
  106. TK_OPTION_NULL_OK, 0, 0},
  107.     {TK_OPTION_PIXELS, "-padx", "padX", "Pad",
  108. DEF_MENUBUTTON_PADX, -1, Tk_Offset(TkMenuButton, padX),
  109. 0, 0, 0},
  110.     {TK_OPTION_PIXELS, "-pady", "padY", "Pad",
  111. DEF_MENUBUTTON_PADY, -1, Tk_Offset(TkMenuButton, padY),
  112. 0, 0, 0},
  113.     {TK_OPTION_RELIEF, "-relief", "relief", "Relief",
  114. DEF_MENUBUTTON_RELIEF, -1, Tk_Offset(TkMenuButton, relief), 
  115.         0, 0, 0},
  116.     {TK_OPTION_STRING_TABLE, "-compound", "compound", "Compound",
  117.          DEF_BUTTON_COMPOUND, -1, Tk_Offset(TkMenuButton, compound), 0,
  118.          (ClientData) compoundStrings, 0},
  119.     {TK_OPTION_STRING_TABLE, "-state", "state", "State",
  120. DEF_MENUBUTTON_STATE, -1, Tk_Offset(TkMenuButton, state),
  121. 0, (ClientData) stateStrings, 0},
  122.     {TK_OPTION_STRING, "-takefocus", "takeFocus", "TakeFocus",
  123. DEF_MENUBUTTON_TAKE_FOCUS, -1, 
  124.         Tk_Offset(TkMenuButton, takeFocus), TK_OPTION_NULL_OK, 0, 0},
  125.     {TK_OPTION_STRING, "-text", "text", "Text",
  126. DEF_MENUBUTTON_TEXT, -1, Tk_Offset(TkMenuButton, text), 0, 0, 0},
  127.     {TK_OPTION_STRING, "-textvariable", "textVariable", "Variable",
  128. DEF_MENUBUTTON_TEXT_VARIABLE, -1, 
  129.         Tk_Offset(TkMenuButton, textVarName), TK_OPTION_NULL_OK, 0, 0},
  130.     {TK_OPTION_INT, "-underline", "underline", "Underline",
  131. DEF_MENUBUTTON_UNDERLINE, -1, Tk_Offset(TkMenuButton, underline),
  132.          0, 0, 0},
  133.     {TK_OPTION_STRING, "-width", "width", "Width",
  134. DEF_MENUBUTTON_WIDTH, -1, Tk_Offset(TkMenuButton, widthString), 
  135.         0, 0, 0},
  136.     {TK_OPTION_PIXELS, "-wraplength", "wrapLength", "WrapLength",
  137. DEF_MENUBUTTON_WRAP_LENGTH, -1, Tk_Offset(TkMenuButton, wrapLength),
  138.         0, 0, 0},
  139.     {TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL,
  140. (char *) NULL, 0, 0}
  141. };
  142. /*
  143.  * The following tables define the menubutton widget commands and map the 
  144.  * indexes into the string tables into a single enumerated type used 
  145.  * to dispatch the scale widget command.
  146.  */
  147. static CONST char *commandNames[] = {
  148.     "cget", "configure", (char *) NULL
  149. };
  150. enum command {
  151.     COMMAND_CGET, COMMAND_CONFIGURE
  152. };
  153. /*
  154.  * Forward declarations for procedures defined later in this file:
  155.  */
  156. static void MenuButtonCmdDeletedProc _ANSI_ARGS_((
  157.     ClientData clientData));
  158. static void MenuButtonEventProc _ANSI_ARGS_((ClientData clientData,
  159.     XEvent *eventPtr));
  160. static void MenuButtonImageProc _ANSI_ARGS_((ClientData clientData,
  161.     int x, int y, int width, int height, int imgWidth,
  162.     int imgHeight));
  163. static char * MenuButtonTextVarProc _ANSI_ARGS_((
  164.     ClientData clientData, Tcl_Interp *interp,
  165.     CONST char *name1, CONST char *name2, int flags));
  166. static int MenuButtonWidgetObjCmd _ANSI_ARGS_((
  167.                             ClientData clientData, Tcl_Interp *interp, 
  168.     int objc, Tcl_Obj *CONST objv[]));
  169. static int ConfigureMenuButton _ANSI_ARGS_((Tcl_Interp *interp,
  170.     TkMenuButton *mbPtr, int objc, 
  171.                             Tcl_Obj *CONST objv[]));
  172. static void DestroyMenuButton _ANSI_ARGS_((char *memPtr));
  173. /*
  174.  *--------------------------------------------------------------
  175.  *
  176.  * Tk_MenubuttonObjCmd --
  177.  *
  178.  * This procedure is invoked to process the "button", "label",
  179.  * "radiobutton", and "checkbutton" Tcl commands.  See the
  180.  * user documentation for details on what it does.
  181.  *
  182.  * Results:
  183.  * A standard Tcl result.
  184.  *
  185.  * Side effects:
  186.  * See the user documentation.
  187.  *
  188.  *--------------------------------------------------------------
  189.  */
  190. int
  191. Tk_MenubuttonObjCmd(clientData, interp, objc, objv)
  192.     ClientData clientData; /* NULL. */
  193.     Tcl_Interp *interp; /* Current interpreter. */
  194.     int objc; /* Number of arguments. */
  195.     Tcl_Obj *CONST objv[]; /* Argument objects. */
  196. {
  197.     register TkMenuButton *mbPtr;
  198.     Tk_OptionTable optionTable;
  199.     Tk_Window tkwin;
  200.     if (objc < 2) {
  201. Tcl_WrongNumArgs(interp, 1, objv, "pathName ?options?");
  202. return TCL_ERROR;
  203.     }
  204.     /*
  205.      * Create the new window.
  206.      */
  207.     tkwin = Tk_CreateWindowFromPath(interp, Tk_MainWindow(interp),
  208.     Tcl_GetString(objv[1]), (char *) NULL);
  209.     if (tkwin == NULL) {
  210. return TCL_ERROR;
  211.     }
  212.     /*
  213.      * Create the option table for this widget class.  If it has already
  214.      * been created, the cached pointer will be returned.
  215.      */
  216.     optionTable = Tk_CreateOptionTable(interp, optionSpecs);
  217.     Tk_SetClass(tkwin, "Menubutton");
  218.     mbPtr = TkpCreateMenuButton(tkwin);
  219.     Tk_SetClassProcs(tkwin, &tkpMenubuttonClass, (ClientData) mbPtr);
  220.     /*
  221.      * Initialize the data structure for the button.
  222.      */
  223.     mbPtr->tkwin = tkwin;
  224.     mbPtr->display = Tk_Display (tkwin);
  225.     mbPtr->interp = interp;
  226.     mbPtr->widgetCmd = Tcl_CreateObjCommand(interp, 
  227.             Tk_PathName(mbPtr->tkwin), MenuButtonWidgetObjCmd, 
  228.             (ClientData) mbPtr, MenuButtonCmdDeletedProc);
  229.     mbPtr->optionTable = optionTable;
  230.     mbPtr->menuName = NULL;
  231.     mbPtr->text = NULL;
  232.     mbPtr->underline = -1;
  233.     mbPtr->textVarName = NULL;
  234.     mbPtr->bitmap = None;
  235.     mbPtr->imageString = NULL;
  236.     mbPtr->image = NULL;
  237.     mbPtr->state = STATE_NORMAL;
  238.     mbPtr->normalBorder = NULL;
  239.     mbPtr->activeBorder = NULL;
  240.     mbPtr->borderWidth = 0;
  241.     mbPtr->relief = TK_RELIEF_FLAT;
  242.     mbPtr->highlightWidth = 0;
  243.     mbPtr->highlightBgColorPtr = NULL;
  244.     mbPtr->highlightColorPtr = NULL;
  245.     mbPtr->inset = 0;
  246.     mbPtr->tkfont = NULL;
  247.     mbPtr->normalFg = NULL;
  248.     mbPtr->activeFg = NULL;
  249.     mbPtr->disabledFg = NULL;
  250.     mbPtr->normalTextGC = None;
  251.     mbPtr->activeTextGC = None;
  252.     mbPtr->gray = None;
  253.     mbPtr->disabledGC = None;
  254.     mbPtr->stippleGC = None;
  255.     mbPtr->leftBearing = 0;
  256.     mbPtr->rightBearing = 0;
  257.     mbPtr->widthString = NULL;
  258.     mbPtr->heightString = NULL;
  259.     mbPtr->width = 0;
  260.     mbPtr->width = 0;
  261.     mbPtr->wrapLength = 0;
  262.     mbPtr->padX = 0;
  263.     mbPtr->padY = 0;
  264.     mbPtr->anchor = TK_ANCHOR_CENTER;
  265.     mbPtr->justify = TK_JUSTIFY_CENTER;
  266.     mbPtr->textLayout = NULL;
  267.     mbPtr->indicatorOn = 0;
  268.     mbPtr->indicatorWidth = 0;
  269.     mbPtr->indicatorHeight = 0;
  270.     mbPtr->direction = DIRECTION_FLUSH;
  271.     mbPtr->cursor = None;
  272.     mbPtr->takeFocus = NULL;
  273.     mbPtr->flags = 0;
  274.     Tk_CreateEventHandler(mbPtr->tkwin,
  275.     ExposureMask|StructureNotifyMask|FocusChangeMask,
  276.     MenuButtonEventProc, (ClientData) mbPtr);
  277.     if (Tk_InitOptions(interp, (char *) mbPtr, optionTable, tkwin) != TCL_OK) {
  278. Tk_DestroyWindow(mbPtr->tkwin);
  279. return TCL_ERROR;
  280.     }
  281.     if (ConfigureMenuButton(interp, mbPtr, objc-2, objv+2) != TCL_OK) {
  282. Tk_DestroyWindow(mbPtr->tkwin);
  283. return TCL_ERROR;
  284.     }
  285.     Tcl_SetStringObj(Tcl_GetObjResult(interp), Tk_PathName(mbPtr->tkwin), -1);
  286.     return TCL_OK;
  287. }
  288. /*
  289.  *--------------------------------------------------------------
  290.  *
  291.  * MenuButtonWidgetObjCmd --
  292.  *
  293.  * This procedure is invoked to process the Tcl command
  294.  * that corresponds to a widget managed by this module.
  295.  * See the user documentation for details on what it does.
  296.  *
  297.  * Results:
  298.  * A standard Tcl result.
  299.  *
  300.  * Side effects:
  301.  * See the user documentation.
  302.  *
  303.  *--------------------------------------------------------------
  304.  */
  305. static int
  306. MenuButtonWidgetObjCmd(clientData, interp, objc, objv)
  307.     ClientData clientData; /* Information about button widget. */
  308.     Tcl_Interp *interp; /* Current interpreter. */
  309.     int objc; /* Number of arguments. */
  310.     Tcl_Obj *CONST objv[]; /* Argument objects. */
  311. {
  312.     register TkMenuButton *mbPtr = (TkMenuButton *) clientData;
  313.     int result, index;
  314.     Tcl_Obj *objPtr;
  315.     if (objc < 2) {
  316.         Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?");
  317. return TCL_ERROR;
  318.     }
  319.     result = Tcl_GetIndexFromObj(interp, objv[1], 
  320.             commandNames, "option", 0, &index);
  321.     if (result != TCL_OK) {
  322.         return result;
  323.     }
  324.     Tcl_Preserve((ClientData) mbPtr);
  325.     switch (index) {
  326.         case COMMAND_CGET: {
  327.     if (objc != 3) {
  328.         Tcl_WrongNumArgs(interp, 1, objv, "cget option");
  329.     goto error;
  330.     }
  331.     objPtr = Tk_GetOptionValue(interp, (char *) mbPtr,
  332.                     mbPtr->optionTable, objv[2], mbPtr->tkwin);
  333.     if (objPtr == NULL) {
  334.         goto error;
  335.     } else {
  336.         Tcl_SetObjResult(interp, objPtr);
  337.     }
  338.     break;
  339. }
  340.         case COMMAND_CONFIGURE: {
  341.     if (objc <= 3) {
  342. objPtr = Tk_GetOptionInfo(interp, (char *) mbPtr,
  343. mbPtr->optionTable,
  344. (objc == 3) ? objv[2] : (Tcl_Obj *) NULL,
  345. mbPtr->tkwin);
  346. if (objPtr == NULL) {
  347.     goto error;
  348. } else {
  349.     Tcl_SetObjResult(interp, objPtr);
  350. }
  351.     } else {
  352. result = ConfigureMenuButton(interp, mbPtr, objc-2, 
  353.         objv+2);
  354.     }
  355.     break;
  356. }
  357.     }
  358.     Tcl_Release((ClientData) mbPtr);
  359.     return result;
  360.     error:
  361.     Tcl_Release((ClientData) mbPtr);
  362.     return TCL_ERROR;
  363. }
  364. /*
  365.  *----------------------------------------------------------------------
  366.  *
  367.  * DestroyMenuButton --
  368.  *
  369.  * This procedure is invoked to recycle all of the resources
  370.  * associated with a menubutton widget.  It is invoked as a
  371.  * when-idle handler in order to make sure that there is no
  372.  * other use of the menubutton pending at the time of the deletion.
  373.  *
  374.  * Results:
  375.  * None.
  376.  *
  377.  * Side effects:
  378.  * Everything associated with the widget is freed up.
  379.  *
  380.  *----------------------------------------------------------------------
  381.  */
  382. static void
  383. DestroyMenuButton(memPtr)
  384.     char *memPtr; /* Info about button widget. */
  385. {
  386.     register TkMenuButton *mbPtr = (TkMenuButton *) memPtr;
  387.     TkpDestroyMenuButton(mbPtr);
  388.     if (mbPtr->flags & REDRAW_PENDING) {
  389.         Tcl_CancelIdleCall(TkpDisplayMenuButton, (ClientData) mbPtr);
  390.     }
  391.     /*
  392.      * Free up all the stuff that requires special handling, then
  393.      * let Tk_FreeOptions handle all the standard option-related
  394.      * stuff.
  395.      */
  396.     Tcl_DeleteCommandFromToken(mbPtr->interp, mbPtr->widgetCmd);
  397.     if (mbPtr->textVarName != NULL) {
  398. Tcl_UntraceVar(mbPtr->interp, mbPtr->textVarName,
  399. TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  400. MenuButtonTextVarProc, (ClientData) mbPtr);
  401.     }
  402.     if (mbPtr->image != NULL) {
  403. Tk_FreeImage(mbPtr->image);
  404.     }
  405.     if (mbPtr->normalTextGC != None) {
  406. Tk_FreeGC(mbPtr->display, mbPtr->normalTextGC);
  407.     }
  408.     if (mbPtr->activeTextGC != None) {
  409. Tk_FreeGC(mbPtr->display, mbPtr->activeTextGC);
  410.     }
  411.     if (mbPtr->disabledGC != None) {
  412. Tk_FreeGC(mbPtr->display, mbPtr->disabledGC);
  413.     }
  414.     if (mbPtr->stippleGC != None) {
  415. Tk_FreeGC(mbPtr->display, mbPtr->stippleGC);
  416.     }
  417.     if (mbPtr->gray != None) {
  418. Tk_FreeBitmap(mbPtr->display, mbPtr->gray);
  419.     }
  420.     if (mbPtr->textLayout != NULL) {
  421.         Tk_FreeTextLayout(mbPtr->textLayout);
  422.     }
  423.     Tk_FreeConfigOptions((char *) mbPtr, mbPtr->optionTable, mbPtr->tkwin);
  424.     mbPtr->tkwin = NULL;
  425.     Tcl_EventuallyFree((ClientData) mbPtr, TCL_DYNAMIC);
  426. }
  427. /*
  428.  *----------------------------------------------------------------------
  429.  *
  430.  * ConfigureMenuButton --
  431.  *
  432.  * This procedure is called to process an argv/argc list, plus
  433.  * the Tk option database, in order to configure (or
  434.  * reconfigure) a menubutton widget.
  435.  *
  436.  * Results:
  437.  * The return value is a standard Tcl result.  If TCL_ERROR is
  438.  * returned, then the interp's result contains an error message.
  439.  *
  440.  * Side effects:
  441.  * Configuration information, such as text string, colors, font,
  442.  * etc. get set for mbPtr;  old resources get freed, if there
  443.  * were any.  The menubutton is redisplayed.
  444.  *
  445.  *----------------------------------------------------------------------
  446.  */
  447. static int
  448. ConfigureMenuButton(interp, mbPtr, objc, objv)
  449.     Tcl_Interp *interp; /* Used for error reporting. */
  450.     register TkMenuButton *mbPtr;
  451.                                 /* Information about widget;  may or may
  452.  * not already have values for some 
  453.  * fields. */
  454.     int objc; /* Number of valid entries in objv. */
  455.     Tcl_Obj *CONST objv[]; /* Arguments. */
  456. {
  457.     Tk_SavedOptions savedOptions;
  458.     Tcl_Obj *errorResult = NULL;
  459.     int error;
  460.     Tk_Image image;
  461.     /*
  462.      * Eliminate any existing trace on variables monitored by the 
  463.      * menubutton.
  464.      */
  465.     if (mbPtr->textVarName != NULL) {
  466. Tcl_UntraceVar(interp, mbPtr->textVarName, 
  467. TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  468. MenuButtonTextVarProc, (ClientData) mbPtr);
  469.     }
  470.     /*
  471.      * The following loop is potentially executed twice.  During the
  472.      * first pass configuration options get set to their new values.
  473.      * If there is an error in this pass, we execute a second pass
  474.      * to restore all the options to their previous values.
  475.      */
  476.     for (error = 0; error <= 1; error++) {
  477. if (!error) {
  478.     /*
  479.      * First pass: set options to new values.
  480.      */
  481.     if (Tk_SetOptions(interp, (char *) mbPtr,
  482.     mbPtr->optionTable, objc, objv,
  483.     mbPtr->tkwin, &savedOptions, (int *) NULL) != TCL_OK) {
  484. continue;
  485.     }
  486. } else {
  487.     /*
  488.      * Second pass: restore options to old values.
  489.      */
  490.     errorResult = Tcl_GetObjResult(interp);
  491.     Tcl_IncrRefCount(errorResult);
  492.     Tk_RestoreSavedOptions(&savedOptions);
  493. }
  494. /*
  495.  * A few options need special processing, such as setting the
  496.  * background from a 3-D border, or filling in complicated
  497.  * defaults that couldn't be specified to Tk_SetOptions.
  498.  */
  499. if ((mbPtr->state == STATE_ACTIVE)
  500. && !Tk_StrictMotif(mbPtr->tkwin)) {
  501.     Tk_SetBackgroundFromBorder(mbPtr->tkwin, mbPtr->activeBorder);
  502. } else {
  503.     Tk_SetBackgroundFromBorder(mbPtr->tkwin, mbPtr->normalBorder);
  504. }
  505. if (mbPtr->highlightWidth < 0) {
  506.     mbPtr->highlightWidth = 0;
  507. }
  508. if (mbPtr->padX < 0) {
  509.     mbPtr->padX = 0;
  510. }
  511. if (mbPtr->padY < 0) {
  512.     mbPtr->padY = 0;
  513. }
  514. /*
  515.  * Get the image for the widget, if there is one.  Allocate the
  516.  * new image before freeing the old one, so that the reference
  517.  * count doesn't go to zero and cause image data to be discarded.
  518.  */
  519. if (mbPtr->imageString != NULL) {
  520.     image = Tk_GetImage(mbPtr->interp, mbPtr->tkwin,
  521.     mbPtr->imageString, MenuButtonImageProc, 
  522.     (ClientData) mbPtr);
  523.     if (image == NULL) {
  524.         return TCL_ERROR;
  525.     }
  526. } else {
  527.     image = NULL;
  528. }
  529. if (mbPtr->image != NULL) {
  530.     Tk_FreeImage(mbPtr->image);
  531. }
  532. mbPtr->image = image;
  533. /*
  534.  * Recompute the geometry for the button.
  535.  */
  536. if ((mbPtr->bitmap != None) || (mbPtr->image != NULL)) {
  537.     if (Tk_GetPixels(interp, mbPtr->tkwin, mbPtr->widthString,
  538.                     &mbPtr->width) != TCL_OK) {
  539.                 widthError:
  540.         Tcl_AddErrorInfo(interp, "n    (processing -width option)");
  541. continue;
  542.     }
  543.     if (Tk_GetPixels(interp, mbPtr->tkwin, mbPtr->heightString,
  544.     &mbPtr->height) != TCL_OK) {
  545.         heightError:
  546.         Tcl_AddErrorInfo(interp, "n    (processing -height option)");
  547. continue;
  548.     }
  549. } else {
  550.     if (Tcl_GetInt(interp, mbPtr->widthString, &mbPtr->width)
  551.     != TCL_OK) {
  552.         goto widthError;
  553.     }
  554.     if (Tcl_GetInt(interp, mbPtr->heightString, &mbPtr->height)
  555.     != TCL_OK) {
  556.         goto heightError;
  557.     }
  558. }
  559. break;
  560.     }
  561.     if (!error) {
  562.       Tk_FreeSavedOptions(&savedOptions);
  563.     }
  564.     if (mbPtr->textVarName != NULL) {
  565. /*
  566.  * If no image or -compound is used, display the value of a variable.
  567.  * Set up a trace to watch for any changes in it, create the variable
  568.  * if it doesn't exist, and fetch its current value.
  569.  */
  570. CONST char *value;
  571. value = Tcl_GetVar(interp, mbPtr->textVarName, TCL_GLOBAL_ONLY);
  572. if (value == NULL) {
  573.     Tcl_SetVar(interp, mbPtr->textVarName, mbPtr->text,
  574.     TCL_GLOBAL_ONLY);
  575. } else {
  576.     if (mbPtr->text != NULL) {
  577. ckfree(mbPtr->text);
  578.     }
  579.     mbPtr->text = (char *) ckalloc((unsigned) (strlen(value) + 1));
  580.     strcpy(mbPtr->text, value);
  581. }
  582. Tcl_TraceVar(interp, mbPtr->textVarName,
  583. TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  584. MenuButtonTextVarProc, (ClientData) mbPtr);
  585.     }
  586.     TkMenuButtonWorldChanged((ClientData) mbPtr);
  587.     if (error) {
  588. Tcl_SetObjResult(interp, errorResult);
  589. Tcl_DecrRefCount(errorResult);
  590.         return TCL_ERROR;
  591.     } else {
  592.         return TCL_OK;
  593.     }
  594. }
  595. /*
  596.  *---------------------------------------------------------------------------
  597.  *
  598.  * TkMenuButtonWorldChanged --
  599.  *
  600.  *      This procedure is called when the world has changed in some
  601.  *      way and the widget needs to recompute all its graphics contexts
  602.  * and determine its new geometry.
  603.  *
  604.  * Results:
  605.  *      None.
  606.  *
  607.  * Side effects:
  608.  *      TkMenuButton will be relayed out and redisplayed.
  609.  *
  610.  *---------------------------------------------------------------------------
  611.  */
  612.  
  613. void
  614. TkMenuButtonWorldChanged(instanceData)
  615.     ClientData instanceData; /* Information about widget. */
  616. {
  617.     XGCValues gcValues;
  618.     GC gc;
  619.     unsigned long mask;
  620.     TkMenuButton *mbPtr;
  621.     mbPtr = (TkMenuButton *) instanceData;
  622.     gcValues.font = Tk_FontId(mbPtr->tkfont);
  623.     gcValues.foreground = mbPtr->normalFg->pixel;
  624.     gcValues.background = Tk_3DBorderColor(mbPtr->normalBorder)->pixel;
  625.     /*
  626.      * Note: GraphicsExpose events are disabled in GC's because they're
  627.      * used to copy stuff from an off-screen pixmap onto the screen (we know
  628.      * that there's no problem with obscured areas).
  629.      */
  630.     gcValues.graphics_exposures = False;
  631.     mask = GCForeground | GCBackground | GCFont | GCGraphicsExposures;
  632.     gc = Tk_GetGC(mbPtr->tkwin, mask, &gcValues);
  633.     if (mbPtr->normalTextGC != None) {
  634. Tk_FreeGC(mbPtr->display, mbPtr->normalTextGC);
  635.     }
  636.     mbPtr->normalTextGC = gc;
  637.     gcValues.foreground = mbPtr->activeFg->pixel;
  638.     gcValues.background = Tk_3DBorderColor(mbPtr->activeBorder)->pixel;
  639.     mask = GCForeground | GCBackground | GCFont;
  640.     gc = Tk_GetGC(mbPtr->tkwin, mask, &gcValues);
  641.     if (mbPtr->activeTextGC != None) {
  642. Tk_FreeGC(mbPtr->display, mbPtr->activeTextGC);
  643.     }
  644.     mbPtr->activeTextGC = gc;
  645.     gcValues.background = Tk_3DBorderColor(mbPtr->normalBorder)->pixel;
  646.     /*
  647.      * Create the GC that can be used for stippling
  648.      */
  649.     if (mbPtr->stippleGC == None) {
  650. gcValues.foreground = gcValues.background;
  651. mask = GCForeground;
  652. if (mbPtr->gray == None) {
  653.     mbPtr->gray = Tk_GetBitmap(NULL, mbPtr->tkwin, "gray50");
  654. }
  655. if (mbPtr->gray != None) {
  656.     gcValues.fill_style = FillStippled;
  657.     gcValues.stipple = mbPtr->gray;
  658.     mask |= GCFillStyle | GCStipple;
  659. }
  660. mbPtr->stippleGC = Tk_GetGC(mbPtr->tkwin, mask, &gcValues);
  661.     }
  662.     /*
  663.      * Allocate the disabled graphics context, for drawing text in
  664.      * its disabled state.
  665.      */
  666.     mask = GCForeground | GCBackground | GCFont;
  667.     if (mbPtr->disabledFg != NULL) {
  668. gcValues.foreground = mbPtr->disabledFg->pixel;
  669.     } else {
  670. gcValues.foreground = gcValues.background;
  671.     }
  672.     gc = Tk_GetGC(mbPtr->tkwin, mask, &gcValues);
  673.     if (mbPtr->disabledGC != None) {
  674. Tk_FreeGC(mbPtr->display, mbPtr->disabledGC);
  675.     }
  676.     mbPtr->disabledGC = gc;
  677.     TkpComputeMenuButtonGeometry(mbPtr);
  678.     /*
  679.      * Lastly, arrange for the button to be redisplayed.
  680.      */
  681.     if (Tk_IsMapped(mbPtr->tkwin) && !(mbPtr->flags & REDRAW_PENDING)) {
  682. Tcl_DoWhenIdle(TkpDisplayMenuButton, (ClientData) mbPtr);
  683. mbPtr->flags |= REDRAW_PENDING;
  684.     }
  685. }
  686. /*
  687.  *--------------------------------------------------------------
  688.  *
  689.  * MenuButtonEventProc --
  690.  *
  691.  * This procedure is invoked by the Tk dispatcher for various
  692.  * events on buttons.
  693.  *
  694.  * Results:
  695.  * None.
  696.  *
  697.  * Side effects:
  698.  * When the window gets deleted, internal structures get
  699.  * cleaned up.  When it gets exposed, it is redisplayed.
  700.  *
  701.  *--------------------------------------------------------------
  702.  */
  703. static void
  704. MenuButtonEventProc(clientData, eventPtr)
  705.     ClientData clientData; /* Information about window. */
  706.     XEvent *eventPtr; /* Information about event. */
  707. {
  708.     TkMenuButton *mbPtr = (TkMenuButton *) clientData;
  709.     if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
  710. goto redraw;
  711.     } else if (eventPtr->type == ConfigureNotify) {
  712. /*
  713.  * Must redraw after size changes, since layout could have changed
  714.  * and borders will need to be redrawn.
  715.  */
  716. goto redraw;
  717.     } else if (eventPtr->type == DestroyNotify) {
  718.         DestroyMenuButton((char *) mbPtr);
  719.     } else if (eventPtr->type == FocusIn) {
  720. if (eventPtr->xfocus.detail != NotifyInferior) {
  721.     mbPtr->flags |= GOT_FOCUS;
  722.     if (mbPtr->highlightWidth > 0) {
  723. goto redraw;
  724.     }
  725. }
  726.     } else if (eventPtr->type == FocusOut) {
  727. if (eventPtr->xfocus.detail != NotifyInferior) {
  728.     mbPtr->flags &= ~GOT_FOCUS;
  729.     if (mbPtr->highlightWidth > 0) {
  730. goto redraw;
  731.     }
  732. }
  733.     }
  734.     return;
  735.     redraw:
  736.     if ((mbPtr->tkwin != NULL) && !(mbPtr->flags & REDRAW_PENDING)) {
  737. Tcl_DoWhenIdle(TkpDisplayMenuButton, (ClientData) mbPtr);
  738. mbPtr->flags |= REDRAW_PENDING;
  739.     }
  740. }
  741. /*
  742.  *----------------------------------------------------------------------
  743.  *
  744.  * MenuButtonCmdDeletedProc --
  745.  *
  746.  * This procedure is invoked when a widget command is deleted.  If
  747.  * the widget isn't already in the process of being destroyed,
  748.  * this command destroys it.
  749.  *
  750.  * Results:
  751.  * None.
  752.  *
  753.  * Side effects:
  754.  * The widget is destroyed.
  755.  *
  756.  *----------------------------------------------------------------------
  757.  */
  758. static void
  759. MenuButtonCmdDeletedProc(clientData)
  760.     ClientData clientData; /* Pointer to widget record for widget. */
  761. {
  762.     TkMenuButton *mbPtr = (TkMenuButton *) clientData;
  763.     Tk_Window tkwin = mbPtr->tkwin;
  764.     /*
  765.      * This procedure could be invoked either because the window was
  766.      * destroyed and the command was then deleted (in which case tkwin
  767.      * is NULL) or because the command was deleted, and then this procedure
  768.      * destroys the widget.
  769.      */
  770.     if (tkwin != NULL) {
  771. Tk_DestroyWindow(tkwin);
  772.     }
  773. }
  774. /*
  775.  *--------------------------------------------------------------
  776.  *
  777.  * MenuButtonTextVarProc --
  778.  *
  779.  * This procedure is invoked when someone changes the variable
  780.  * whose contents are to be displayed in a menu button.
  781.  *
  782.  * Results:
  783.  * NULL is always returned.
  784.  *
  785.  * Side effects:
  786.  * The text displayed in the menu button will change to match the
  787.  * variable.
  788.  *
  789.  *--------------------------------------------------------------
  790.  */
  791. /* ARGSUSED */
  792. static char *
  793. MenuButtonTextVarProc(clientData, interp, name1, name2, flags)
  794.     ClientData clientData; /* Information about button. */
  795.     Tcl_Interp *interp; /* Interpreter containing variable. */
  796.     CONST char *name1; /* Name of variable. */
  797.     CONST char *name2; /* Second part of variable name. */
  798.     int flags; /* Information about what happened. */
  799. {
  800.     register TkMenuButton *mbPtr = (TkMenuButton *) clientData;
  801.     CONST char *value;
  802.     /*
  803.      * If the variable is unset, then immediately recreate it unless
  804.      * the whole interpreter is going away.
  805.      */
  806.     if (flags & TCL_TRACE_UNSETS) {
  807. if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
  808.     Tcl_SetVar(interp, mbPtr->textVarName, mbPtr->text,
  809.     TCL_GLOBAL_ONLY);
  810.     Tcl_TraceVar(interp, mbPtr->textVarName,
  811.     TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  812.     MenuButtonTextVarProc, clientData);
  813. }
  814. return (char *) NULL;
  815.     }
  816.     value = Tcl_GetVar(interp, mbPtr->textVarName, TCL_GLOBAL_ONLY);
  817.     if (value == NULL) {
  818. value = "";
  819.     }
  820.     if (mbPtr->text != NULL) {
  821. ckfree(mbPtr->text);
  822.     }
  823.     mbPtr->text = (char *) ckalloc((unsigned) (strlen(value) + 1));
  824.     strcpy(mbPtr->text, value);
  825.     TkpComputeMenuButtonGeometry(mbPtr);
  826.     if ((mbPtr->tkwin != NULL) && Tk_IsMapped(mbPtr->tkwin)
  827.     && !(mbPtr->flags & REDRAW_PENDING)) {
  828. Tcl_DoWhenIdle(TkpDisplayMenuButton, (ClientData) mbPtr);
  829. mbPtr->flags |= REDRAW_PENDING;
  830.     }
  831.     return (char *) NULL;
  832. }
  833. /*
  834.  *----------------------------------------------------------------------
  835.  *
  836.  * MenuButtonImageProc --
  837.  *
  838.  * This procedure is invoked by the image code whenever the manager
  839.  * for an image does something that affects the size of contents
  840.  * of an image displayed in a button.
  841.  *
  842.  * Results:
  843.  * None.
  844.  *
  845.  * Side effects:
  846.  * Arranges for the button to get redisplayed.
  847.  *
  848.  *----------------------------------------------------------------------
  849.  */
  850. static void
  851. MenuButtonImageProc(clientData, x, y, width, height, imgWidth, imgHeight)
  852.     ClientData clientData; /* Pointer to widget record. */
  853.     int x, y; /* Upper left pixel (within image)
  854.  * that must be redisplayed. */
  855.     int width, height; /* Dimensions of area to redisplay
  856.  * (may be <= 0). */
  857.     int imgWidth, imgHeight; /* New dimensions of image. */
  858. {
  859.     register TkMenuButton *mbPtr = (TkMenuButton *) clientData;
  860.     if (mbPtr->tkwin != NULL) {
  861. TkpComputeMenuButtonGeometry(mbPtr);
  862. if (Tk_IsMapped(mbPtr->tkwin) && !(mbPtr->flags & REDRAW_PENDING)) {
  863.     Tcl_DoWhenIdle(TkpDisplayMenuButton, (ClientData) mbPtr);
  864.     mbPtr->flags |= REDRAW_PENDING;
  865. }
  866.     }
  867. }