home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Applications / Tcl-Tk 8.0 / Pre-installed version / tk8.0 / generic / tkButton.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-08-15  |  42.1 KB  |  1,348 lines  |  [TEXT/CWIE]

  1. /* 
  2.  * tkButton.c --
  3.  *
  4.  *    This module implements a collection of button-like
  5.  *    widgets for the Tk toolkit.  The widgets implemented
  6.  *    include labels, buttons, check buttons, and radio
  7.  *    buttons.
  8.  *
  9.  * Copyright (c) 1990-1994 The Regents of the University of California.
  10.  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
  11.  *
  12.  * See the file "license.terms" for information on usage and redistribution
  13.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  14.  *
  15.  * SCCS: @(#) tkButton.c 1.144 97/07/31 09:04:57
  16.  */
  17.  
  18. #include "tkButton.h"
  19. #include "default.h"
  20.  
  21. /*
  22.  * Class names for buttons, indexed by one of the type values above.
  23.  */
  24.  
  25. static char *classNames[] = {"Label", "Button", "Checkbutton", "Radiobutton"};
  26.  
  27. /*
  28.  * The class procedure table for the button widget.
  29.  */
  30.  
  31. static int configFlags[] = {LABEL_MASK, BUTTON_MASK,
  32.     CHECK_BUTTON_MASK, RADIO_BUTTON_MASK};
  33.  
  34. /*
  35.  * Information used for parsing configuration specs:
  36.  */
  37.  
  38. Tk_ConfigSpec tkpButtonConfigSpecs[] = {
  39.     {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
  40.     DEF_BUTTON_ACTIVE_BG_COLOR, Tk_Offset(TkButton, activeBorder),
  41.     BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK
  42.     |TK_CONFIG_COLOR_ONLY},
  43.     {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
  44.     DEF_BUTTON_ACTIVE_BG_MONO, Tk_Offset(TkButton, activeBorder),
  45.     BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK
  46.     |TK_CONFIG_MONO_ONLY},
  47.     {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
  48.     DEF_BUTTON_ACTIVE_FG_COLOR, Tk_Offset(TkButton, activeFg), 
  49.     BUTTON_MASK|TK_CONFIG_COLOR_ONLY},
  50.     {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
  51.     DEF_CHKRAD_ACTIVE_FG_COLOR, Tk_Offset(TkButton, activeFg), 
  52.     CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|TK_CONFIG_COLOR_ONLY},
  53.     {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
  54.     DEF_BUTTON_ACTIVE_FG_MONO, Tk_Offset(TkButton, activeFg), 
  55.     BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK
  56.     |TK_CONFIG_MONO_ONLY},
  57.     {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor",
  58.     DEF_BUTTON_ANCHOR, Tk_Offset(TkButton, anchor), ALL_MASK},
  59.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  60.     DEF_BUTTON_BG_COLOR, Tk_Offset(TkButton, normalBorder),
  61.     ALL_MASK | TK_CONFIG_COLOR_ONLY},
  62.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  63.     DEF_BUTTON_BG_MONO, Tk_Offset(TkButton, normalBorder),
  64.     ALL_MASK | TK_CONFIG_MONO_ONLY},
  65.     {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
  66.     (char *) NULL, 0, ALL_MASK},
  67.     {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
  68.     (char *) NULL, 0, ALL_MASK},
  69.     {TK_CONFIG_BITMAP, "-bitmap", "bitmap", "Bitmap",
  70.     DEF_BUTTON_BITMAP, Tk_Offset(TkButton, bitmap),
  71.     ALL_MASK|TK_CONFIG_NULL_OK},
  72.     {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
  73.     DEF_BUTTON_BORDER_WIDTH, Tk_Offset(TkButton, borderWidth), ALL_MASK},
  74.     {TK_CONFIG_STRING, "-command", "command", "Command",
  75.     DEF_BUTTON_COMMAND, Tk_Offset(TkButton, command),
  76.     BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|TK_CONFIG_NULL_OK},
  77.     {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
  78.     DEF_BUTTON_CURSOR, Tk_Offset(TkButton, cursor),
  79.     ALL_MASK|TK_CONFIG_NULL_OK},
  80.     {TK_CONFIG_UID, "-default", "default", "Default",
  81.         DEF_BUTTON_DEFAULT, Tk_Offset(TkButton, defaultState), BUTTON_MASK},
  82.     {TK_CONFIG_COLOR, "-disabledforeground", "disabledForeground",
  83.     "DisabledForeground", DEF_BUTTON_DISABLED_FG_COLOR,
  84.     Tk_Offset(TkButton, disabledFg), BUTTON_MASK|CHECK_BUTTON_MASK
  85.     |RADIO_BUTTON_MASK|TK_CONFIG_COLOR_ONLY|TK_CONFIG_NULL_OK},
  86.     {TK_CONFIG_COLOR, "-disabledforeground", "disabledForeground",
  87.     "DisabledForeground", DEF_BUTTON_DISABLED_FG_MONO,
  88.     Tk_Offset(TkButton, disabledFg), BUTTON_MASK|CHECK_BUTTON_MASK
  89.     |RADIO_BUTTON_MASK|TK_CONFIG_MONO_ONLY|TK_CONFIG_NULL_OK},
  90.     {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
  91.     (char *) NULL, 0, ALL_MASK},
  92.     {TK_CONFIG_FONT, "-font", "font", "Font",
  93.     DEF_BUTTON_FONT, Tk_Offset(TkButton, tkfont),
  94.     ALL_MASK},
  95.     {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
  96.     DEF_BUTTON_FG, Tk_Offset(TkButton, normalFg), LABEL_MASK|BUTTON_MASK},
  97.     {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
  98.     DEF_CHKRAD_FG, Tk_Offset(TkButton, normalFg), CHECK_BUTTON_MASK
  99.     |RADIO_BUTTON_MASK},
  100.     {TK_CONFIG_STRING, "-height", "height", "Height",
  101.     DEF_BUTTON_HEIGHT, Tk_Offset(TkButton, heightString), ALL_MASK},
  102.     {TK_CONFIG_BORDER, "-highlightbackground", "highlightBackground",
  103.     "HighlightBackground", DEF_BUTTON_HIGHLIGHT_BG,
  104.     Tk_Offset(TkButton, highlightBorder), ALL_MASK},
  105.     {TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
  106.     DEF_BUTTON_HIGHLIGHT, Tk_Offset(TkButton, highlightColorPtr),
  107.     ALL_MASK},
  108.     {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
  109.     "HighlightThickness",
  110.     DEF_LABEL_HIGHLIGHT_WIDTH, Tk_Offset(TkButton, highlightWidth),
  111.     LABEL_MASK},
  112.     {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
  113.     "HighlightThickness",
  114.     DEF_BUTTON_HIGHLIGHT_WIDTH, Tk_Offset(TkButton, highlightWidth),
  115.     BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
  116.     {TK_CONFIG_STRING, "-image", "image", "Image",
  117.     DEF_BUTTON_IMAGE, Tk_Offset(TkButton, imageString),
  118.     ALL_MASK|TK_CONFIG_NULL_OK},
  119.     {TK_CONFIG_BOOLEAN, "-indicatoron", "indicatorOn", "IndicatorOn",
  120.     DEF_BUTTON_INDICATOR, Tk_Offset(TkButton, indicatorOn),
  121.     CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
  122.     {TK_CONFIG_JUSTIFY, "-justify", "justify", "Justify",
  123.     DEF_BUTTON_JUSTIFY, Tk_Offset(TkButton, justify), ALL_MASK},
  124.     {TK_CONFIG_STRING, "-offvalue", "offValue", "Value",
  125.     DEF_BUTTON_OFF_VALUE, Tk_Offset(TkButton, offValue),
  126.     CHECK_BUTTON_MASK},
  127.     {TK_CONFIG_STRING, "-onvalue", "onValue", "Value",
  128.     DEF_BUTTON_ON_VALUE, Tk_Offset(TkButton, onValue),
  129.     CHECK_BUTTON_MASK},
  130.     {TK_CONFIG_PIXELS, "-padx", "padX", "Pad",
  131.     DEF_BUTTON_PADX, Tk_Offset(TkButton, padX), BUTTON_MASK},
  132.     {TK_CONFIG_PIXELS, "-padx", "padX", "Pad",
  133.     DEF_LABCHKRAD_PADX, Tk_Offset(TkButton, padX),
  134.     LABEL_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
  135.     {TK_CONFIG_PIXELS, "-pady", "padY", "Pad",
  136.     DEF_BUTTON_PADY, Tk_Offset(TkButton, padY), BUTTON_MASK},
  137.     {TK_CONFIG_PIXELS, "-pady", "padY", "Pad",
  138.     DEF_LABCHKRAD_PADY, Tk_Offset(TkButton, padY),
  139.     LABEL_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
  140.     {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
  141.     DEF_BUTTON_RELIEF, Tk_Offset(TkButton, relief), BUTTON_MASK},
  142.     {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
  143.     DEF_LABCHKRAD_RELIEF, Tk_Offset(TkButton, relief),
  144.     LABEL_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
  145.     {TK_CONFIG_BORDER, "-selectcolor", "selectColor", "Background",
  146.     DEF_BUTTON_SELECT_COLOR, Tk_Offset(TkButton, selectBorder),
  147.     CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|TK_CONFIG_COLOR_ONLY
  148.     |TK_CONFIG_NULL_OK},
  149.     {TK_CONFIG_BORDER, "-selectcolor", "selectColor", "Background",
  150.     DEF_BUTTON_SELECT_MONO, Tk_Offset(TkButton, selectBorder),
  151.     CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|TK_CONFIG_MONO_ONLY
  152.     |TK_CONFIG_NULL_OK},
  153.     {TK_CONFIG_STRING, "-selectimage", "selectImage", "SelectImage",
  154.     DEF_BUTTON_SELECT_IMAGE, Tk_Offset(TkButton, selectImageString),
  155.     CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|TK_CONFIG_NULL_OK},
  156.     {TK_CONFIG_UID, "-state", "state", "State",
  157.     DEF_BUTTON_STATE, Tk_Offset(TkButton, state),
  158.     BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
  159.     {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
  160.     DEF_LABEL_TAKE_FOCUS, Tk_Offset(TkButton, takeFocus),
  161.     LABEL_MASK|TK_CONFIG_NULL_OK},
  162.     {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
  163.     DEF_BUTTON_TAKE_FOCUS, Tk_Offset(TkButton, takeFocus),
  164.     BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|TK_CONFIG_NULL_OK},
  165.     {TK_CONFIG_STRING, "-text", "text", "Text",
  166.     DEF_BUTTON_TEXT, Tk_Offset(TkButton, text), ALL_MASK},
  167.     {TK_CONFIG_STRING, "-textvariable", "textVariable", "Variable",
  168.     DEF_BUTTON_TEXT_VARIABLE, Tk_Offset(TkButton, textVarName),
  169.     ALL_MASK|TK_CONFIG_NULL_OK},
  170.     {TK_CONFIG_INT, "-underline", "underline", "Underline",
  171.     DEF_BUTTON_UNDERLINE, Tk_Offset(TkButton, underline), ALL_MASK},
  172.     {TK_CONFIG_STRING, "-value", "value", "Value",
  173.     DEF_BUTTON_VALUE, Tk_Offset(TkButton, onValue),
  174.     RADIO_BUTTON_MASK},
  175.     {TK_CONFIG_STRING, "-variable", "variable", "Variable",
  176.     DEF_RADIOBUTTON_VARIABLE, Tk_Offset(TkButton, selVarName),
  177.     RADIO_BUTTON_MASK},
  178.     {TK_CONFIG_STRING, "-variable", "variable", "Variable",
  179.     DEF_CHECKBUTTON_VARIABLE, Tk_Offset(TkButton, selVarName),
  180.     CHECK_BUTTON_MASK|TK_CONFIG_NULL_OK},
  181.     {TK_CONFIG_STRING, "-width", "width", "Width",
  182.     DEF_BUTTON_WIDTH, Tk_Offset(TkButton, widthString), ALL_MASK},
  183.     {TK_CONFIG_PIXELS, "-wraplength", "wrapLength", "WrapLength",
  184.     DEF_BUTTON_WRAP_LENGTH, Tk_Offset(TkButton, wrapLength), ALL_MASK},
  185.     {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
  186.     (char *) NULL, 0, 0}
  187. };
  188.  
  189. /*
  190.  * String to print out in error messages, identifying options for
  191.  * widget commands for different types of labels or buttons:
  192.  */
  193.  
  194. static char *optionStrings[] = {
  195.     "cget or configure",
  196.     "cget, configure, flash, or invoke",
  197.     "cget, configure, deselect, flash, invoke, select, or toggle",
  198.     "cget, configure, deselect, flash, invoke, or select"
  199. };
  200.  
  201. /*
  202.  * Forward declarations for procedures defined later in this file:
  203.  */
  204.  
  205. static void        ButtonCmdDeletedProc _ANSI_ARGS_((
  206.                 ClientData clientData));
  207. static int        ButtonCreate _ANSI_ARGS_((ClientData clientData,
  208.                 Tcl_Interp *interp, int argc, char **argv,
  209.                 int type));
  210. static void        ButtonEventProc _ANSI_ARGS_((ClientData clientData,
  211.                 XEvent *eventPtr));
  212. static void        ButtonImageProc _ANSI_ARGS_((ClientData clientData,
  213.                 int x, int y, int width, int height,
  214.                 int imgWidth, int imgHeight));
  215. static void        ButtonSelectImageProc _ANSI_ARGS_((
  216.                 ClientData clientData, int x, int y, int width,
  217.                 int height, int imgWidth, int imgHeight));
  218. static char *        ButtonTextVarProc _ANSI_ARGS_((ClientData clientData,
  219.                 Tcl_Interp *interp, char *name1, char *name2,
  220.                 int flags));
  221. static char *        ButtonVarProc _ANSI_ARGS_((ClientData clientData,
  222.                 Tcl_Interp *interp, char *name1, char *name2,
  223.                 int flags));
  224. static int        ButtonWidgetCmd _ANSI_ARGS_((ClientData clientData,
  225.                 Tcl_Interp *interp, int argc, char **argv));
  226. static int        ConfigureButton _ANSI_ARGS_((Tcl_Interp *interp,
  227.                 TkButton *butPtr, int argc, char **argv,
  228.                 int flags));
  229. static void        DestroyButton _ANSI_ARGS_((TkButton *butPtr));
  230.  
  231.  
  232. /*
  233.  *--------------------------------------------------------------
  234.  *
  235.  * Tk_ButtonCmd, Tk_CheckbuttonCmd, Tk_LabelCmd, Tk_RadiobuttonCmd --
  236.  *
  237.  *    These procedures are invoked to process the "button", "label",
  238.  *    "radiobutton", and "checkbutton" Tcl commands.  See the
  239.  *    user documentation for details on what they do.
  240.  *
  241.  * Results:
  242.  *    A standard Tcl result.
  243.  *
  244.  * Side effects:
  245.  *    See the user documentation.  These procedures are just wrappers;
  246.  *    they call ButtonCreate to do all of the real work.
  247.  *
  248.  *--------------------------------------------------------------
  249.  */
  250.  
  251. int
  252. Tk_ButtonCmd(clientData, interp, argc, argv)
  253.     ClientData clientData;    /* Main window associated with
  254.                  * interpreter. */
  255.     Tcl_Interp *interp;        /* Current interpreter. */
  256.     int argc;            /* Number of arguments. */
  257.     char **argv;        /* Argument strings. */
  258. {
  259.     return ButtonCreate(clientData, interp, argc, argv, TYPE_BUTTON);
  260. }
  261.  
  262. int
  263. Tk_CheckbuttonCmd(clientData, interp, argc, argv)
  264.     ClientData clientData;    /* Main window associated with
  265.                  * interpreter. */
  266.     Tcl_Interp *interp;        /* Current interpreter. */
  267.     int argc;            /* Number of arguments. */
  268.     char **argv;        /* Argument strings. */
  269. {
  270.     return ButtonCreate(clientData, interp, argc, argv, TYPE_CHECK_BUTTON);
  271. }
  272.  
  273. int
  274. Tk_LabelCmd(clientData, interp, argc, argv)
  275.     ClientData clientData;    /* Main window associated with
  276.                  * interpreter. */
  277.     Tcl_Interp *interp;        /* Current interpreter. */
  278.     int argc;            /* Number of arguments. */
  279.     char **argv;        /* Argument strings. */
  280. {
  281.     return ButtonCreate(clientData, interp, argc, argv, TYPE_LABEL);
  282. }
  283.  
  284. int
  285. Tk_RadiobuttonCmd(clientData, interp, argc, argv)
  286.     ClientData clientData;    /* Main window associated with
  287.                  * interpreter. */
  288.     Tcl_Interp *interp;        /* Current interpreter. */
  289.     int argc;            /* Number of arguments. */
  290.     char **argv;        /* Argument strings. */
  291. {
  292.     return ButtonCreate(clientData, interp, argc, argv, TYPE_RADIO_BUTTON);
  293. }
  294.  
  295. /*
  296.  *--------------------------------------------------------------
  297.  *
  298.  * ButtonCreate --
  299.  *
  300.  *    This procedure does all the real work of implementing the
  301.  *    "button", "label", "radiobutton", and "checkbutton" Tcl
  302.  *    commands.  See the user documentation for details on what it does.
  303.  *
  304.  * Results:
  305.  *    A standard Tcl result.
  306.  *
  307.  * Side effects:
  308.  *    See the user documentation.
  309.  *
  310.  *--------------------------------------------------------------
  311.  */
  312.  
  313. static int
  314. ButtonCreate(clientData, interp, argc, argv, type)
  315.     ClientData clientData;    /* Main window associated with
  316.                  * interpreter. */
  317.     Tcl_Interp *interp;        /* Current interpreter. */
  318.     int argc;            /* Number of arguments. */
  319.     char **argv;        /* Argument strings. */
  320.     int type;            /* Type of button to create: TYPE_LABEL,
  321.                  * TYPE_BUTTON, TYPE_CHECK_BUTTON, or
  322.                  * TYPE_RADIO_BUTTON. */
  323. {
  324.     register TkButton *butPtr;
  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.     Tk_SetClass(new, classNames[type]);
  344.     butPtr = TkpCreateButton(new);
  345.  
  346.     TkSetClassProcs(new, &tkpButtonProcs, (ClientData) butPtr);
  347.  
  348.     /*
  349.      * Initialize the data structure for the button.
  350.      */
  351.  
  352.     butPtr->tkwin = new;
  353.     butPtr->display = Tk_Display(new);
  354.     butPtr->widgetCmd = Tcl_CreateCommand(interp, Tk_PathName(butPtr->tkwin),
  355.         ButtonWidgetCmd, (ClientData) butPtr, ButtonCmdDeletedProc);
  356.     butPtr->interp = interp;
  357.     butPtr->type = type;
  358.     butPtr->text = NULL;
  359.     butPtr->underline = -1;
  360.     butPtr->textVarName = NULL;
  361.     butPtr->bitmap = None;
  362.     butPtr->imageString = NULL;
  363.     butPtr->image = NULL;
  364.     butPtr->selectImageString = NULL;
  365.     butPtr->selectImage = NULL;
  366.     butPtr->state = tkNormalUid;
  367.     butPtr->normalBorder = NULL;
  368.     butPtr->activeBorder = NULL;
  369.     butPtr->borderWidth = 0;
  370.     butPtr->relief = TK_RELIEF_FLAT;
  371.     butPtr->highlightWidth = 0;
  372.     butPtr->highlightBorder = NULL;
  373.     butPtr->highlightColorPtr = NULL;
  374.     butPtr->inset = 0;
  375.     butPtr->tkfont = NULL;
  376.     butPtr->normalFg = NULL;
  377.     butPtr->activeFg = NULL;
  378.     butPtr->disabledFg = NULL;
  379.     butPtr->normalTextGC = None;
  380.     butPtr->activeTextGC = None;
  381.     butPtr->gray = None;
  382.     butPtr->disabledGC = None;
  383.     butPtr->copyGC = None;
  384.     butPtr->widthString = NULL;
  385.     butPtr->heightString = NULL;
  386.     butPtr->width = 0;
  387.     butPtr->height = 0;
  388.     butPtr->wrapLength = 0;
  389.     butPtr->padX = 0;
  390.     butPtr->padY = 0;
  391.     butPtr->anchor = TK_ANCHOR_CENTER;
  392.     butPtr->justify = TK_JUSTIFY_CENTER;
  393.     butPtr->textLayout = NULL;
  394.     butPtr->indicatorOn = 0;
  395.     butPtr->selectBorder = NULL;
  396.     butPtr->indicatorSpace = 0;
  397.     butPtr->indicatorDiameter = 0;
  398.     butPtr->defaultState = tkDisabledUid;
  399.     butPtr->selVarName = NULL;
  400.     butPtr->onValue = NULL;
  401.     butPtr->offValue = NULL;
  402.     butPtr->cursor = None;
  403.     butPtr->command = NULL;
  404.     butPtr->takeFocus = NULL;
  405.     butPtr->flags = 0;
  406.  
  407.     Tk_CreateEventHandler(butPtr->tkwin,
  408.         ExposureMask|StructureNotifyMask|FocusChangeMask,
  409.         ButtonEventProc, (ClientData) butPtr);
  410.  
  411.     if (ConfigureButton(interp, butPtr, argc - 2, argv + 2,
  412.         configFlags[type]) != TCL_OK) {
  413.     Tk_DestroyWindow(butPtr->tkwin);
  414.     return TCL_ERROR;
  415.     }
  416.  
  417.     interp->result = Tk_PathName(butPtr->tkwin);
  418.     return TCL_OK;
  419. }
  420.  
  421. /*
  422.  *--------------------------------------------------------------
  423.  *
  424.  * ButtonWidgetCmd --
  425.  *
  426.  *    This procedure is invoked to process the Tcl command
  427.  *    that corresponds to a widget managed by this module.
  428.  *    See the user documentation for details on what it does.
  429.  *
  430.  * Results:
  431.  *    A standard Tcl result.
  432.  *
  433.  * Side effects:
  434.  *    See the user documentation.
  435.  *
  436.  *--------------------------------------------------------------
  437.  */
  438.  
  439. static int
  440. ButtonWidgetCmd(clientData, interp, argc, argv)
  441.     ClientData clientData;    /* Information about button widget. */
  442.     Tcl_Interp *interp;        /* Current interpreter. */
  443.     int argc;            /* Number of arguments. */
  444.     char **argv;        /* Argument strings. */
  445. {
  446.     register TkButton *butPtr = (TkButton *) clientData;
  447.     int result = TCL_OK;
  448.     size_t length;
  449.     int c;
  450.  
  451.     if (argc < 2) {
  452.     sprintf(interp->result,
  453.         "wrong # args: should be \"%.50s option ?arg arg ...?\"",
  454.         argv[0]);
  455.     return TCL_ERROR;
  456.     }
  457.     Tcl_Preserve((ClientData) butPtr);
  458.     c = argv[1][0];
  459.     length = strlen(argv[1]);
  460.  
  461.     if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
  462.         && (length >= 2)) {
  463.     if (argc != 3) {
  464.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  465.             argv[0], " cget option\"",
  466.             (char *) NULL);
  467.         goto error;
  468.     }
  469.     result = Tk_ConfigureValue(interp, butPtr->tkwin, tkpButtonConfigSpecs,
  470.         (char *) butPtr, argv[2], configFlags[butPtr->type]);
  471.     } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
  472.         && (length >= 2)) {
  473.     if (argc == 2) {
  474.         result = Tk_ConfigureInfo(interp, butPtr->tkwin,
  475.             tkpButtonConfigSpecs, (char *) butPtr, (char *) NULL,
  476.             configFlags[butPtr->type]);
  477.     } else if (argc == 3) {
  478.         result = Tk_ConfigureInfo(interp, butPtr->tkwin,
  479.             tkpButtonConfigSpecs, (char *) butPtr, argv[2],
  480.             configFlags[butPtr->type]);
  481.     } else {
  482.         result = ConfigureButton(interp, butPtr, argc-2, argv+2,
  483.             configFlags[butPtr->type] | TK_CONFIG_ARGV_ONLY);
  484.     }
  485.     } else if ((c == 'd') && (strncmp(argv[1], "deselect", length) == 0)
  486.         && (butPtr->type >= TYPE_CHECK_BUTTON)) {
  487.     if (argc > 2) {
  488.         sprintf(interp->result,
  489.             "wrong # args: should be \"%.50s deselect\"",
  490.             argv[0]);
  491.         goto error;
  492.     }
  493.     if (butPtr->type == TYPE_CHECK_BUTTON) {
  494.         if (Tcl_SetVar(interp, butPtr->selVarName, butPtr->offValue,
  495.             TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
  496.         result = TCL_ERROR;
  497.         }
  498.     } else if (butPtr->flags & SELECTED) {
  499.         if (Tcl_SetVar(interp, butPtr->selVarName, "",
  500.             TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
  501.         result = TCL_ERROR;
  502.         };
  503.     }
  504.     } else if ((c == 'f') && (strncmp(argv[1], "flash", length) == 0)
  505.         && (butPtr->type != TYPE_LABEL)) {
  506.     int i;
  507.  
  508.     if (argc > 2) {
  509.         sprintf(interp->result,
  510.             "wrong # args: should be \"%.50s flash\"",
  511.             argv[0]);
  512.         goto error;
  513.     }
  514.     if (butPtr->state != tkDisabledUid) {
  515.         for (i = 0; i < 4; i++) {
  516.         butPtr->state = (butPtr->state == tkNormalUid)
  517.             ? tkActiveUid : tkNormalUid;
  518.         Tk_SetBackgroundFromBorder(butPtr->tkwin,
  519.             (butPtr->state == tkActiveUid) ? butPtr->activeBorder
  520.             : butPtr->normalBorder);
  521.         TkpDisplayButton((ClientData) butPtr);
  522.  
  523.         /*
  524.          * Special note: must cancel any existing idle handler
  525.          * for TkpDisplayButton;  it's no longer needed, and TkpDisplayButton
  526.          * cleared the REDRAW_PENDING flag.
  527.          */
  528.  
  529.         Tcl_CancelIdleCall(TkpDisplayButton, (ClientData) butPtr);
  530.         XFlush(butPtr->display);
  531.         Tcl_Sleep(50);
  532.         }
  533.     }
  534.     } else if ((c == 'i') && (strncmp(argv[1], "invoke", length) == 0)
  535.         && (butPtr->type > TYPE_LABEL)) {
  536.     if (argc > 2) {
  537.         sprintf(interp->result,
  538.             "wrong # args: should be \"%.50s invoke\"",
  539.             argv[0]);
  540.         goto error;
  541.     }
  542.     if (butPtr->state != tkDisabledUid) {
  543.         result = TkInvokeButton(butPtr);
  544.     }
  545.     } else if ((c == 's') && (strncmp(argv[1], "select", length) == 0)
  546.         && (butPtr->type >= TYPE_CHECK_BUTTON)) {
  547.     if (argc > 2) {
  548.         sprintf(interp->result,
  549.             "wrong # args: should be \"%.50s select\"",
  550.             argv[0]);
  551.         goto error;
  552.     }
  553.     if (Tcl_SetVar(interp, butPtr->selVarName, butPtr->onValue,
  554.         TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
  555.         result = TCL_ERROR;
  556.     }
  557.     } else if ((c == 't') && (strncmp(argv[1], "toggle", length) == 0)
  558.         && (length >= 2) && (butPtr->type == TYPE_CHECK_BUTTON)) {
  559.     if (argc > 2) {
  560.         sprintf(interp->result,
  561.             "wrong # args: should be \"%.50s toggle\"",
  562.             argv[0]);
  563.         goto error;
  564.     }
  565.     if (butPtr->flags & SELECTED) {
  566.         if (Tcl_SetVar(interp, butPtr->selVarName, butPtr->offValue,
  567.             TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
  568.         result = TCL_ERROR;
  569.         }
  570.     } else {
  571.         if (Tcl_SetVar(interp, butPtr->selVarName, butPtr->onValue,
  572.             TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
  573.         result = TCL_ERROR;
  574.         }
  575.     }
  576.     } else {
  577.     sprintf(interp->result,
  578.         "bad option \"%.50s\": must be %s", argv[1],
  579.         optionStrings[butPtr->type]);
  580.     goto error;
  581.     }
  582.     Tcl_Release((ClientData) butPtr);
  583.     return result;
  584.  
  585.     error:
  586.     Tcl_Release((ClientData) butPtr);
  587.     return TCL_ERROR;
  588. }
  589.  
  590. /*
  591.  *----------------------------------------------------------------------
  592.  *
  593.  * DestroyButton --
  594.  *
  595.  *    This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
  596.  *    to clean up the internal structure of a button at a safe time
  597.  *    (when no-one is using it anymore).
  598.  *
  599.  * Results:
  600.  *    None.
  601.  *
  602.  * Side effects:
  603.  *    Everything associated with the widget is freed up.
  604.  *
  605.  *----------------------------------------------------------------------
  606.  */
  607.  
  608. static void
  609. DestroyButton(butPtr)
  610.     TkButton *butPtr;        /* Info about button widget. */
  611. {
  612.     /*
  613.      * Free up all the stuff that requires special handling, then
  614.      * let Tk_FreeOptions handle all the standard option-related
  615.      * stuff.
  616.      */
  617.  
  618.     if (butPtr->textVarName != NULL) {
  619.     Tcl_UntraceVar(butPtr->interp, butPtr->textVarName,
  620.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  621.         ButtonTextVarProc, (ClientData) butPtr);
  622.     }
  623.     if (butPtr->image != NULL) {
  624.     Tk_FreeImage(butPtr->image);
  625.     }
  626.     if (butPtr->selectImage != NULL) {
  627.     Tk_FreeImage(butPtr->selectImage);
  628.     }
  629.     if (butPtr->normalTextGC != None) {
  630.     Tk_FreeGC(butPtr->display, butPtr->normalTextGC);
  631.     }
  632.     if (butPtr->activeTextGC != None) {
  633.     Tk_FreeGC(butPtr->display, butPtr->activeTextGC);
  634.     }
  635.     if (butPtr->gray != None) {
  636.     Tk_FreeBitmap(butPtr->display, butPtr->gray);
  637.     }
  638.     if (butPtr->disabledGC != None) {
  639.     Tk_FreeGC(butPtr->display, butPtr->disabledGC);
  640.     }
  641.     if (butPtr->copyGC != None) {
  642.     Tk_FreeGC(butPtr->display, butPtr->copyGC);
  643.     }
  644.     if (butPtr->selVarName != NULL) {
  645.     Tcl_UntraceVar(butPtr->interp, butPtr->selVarName,
  646.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  647.         ButtonVarProc, (ClientData) butPtr);
  648.     }
  649.     Tk_FreeTextLayout(butPtr->textLayout);
  650.     Tk_FreeOptions(tkpButtonConfigSpecs, (char *) butPtr, butPtr->display,
  651.         configFlags[butPtr->type]);
  652.     Tcl_EventuallyFree((ClientData)butPtr, TCL_DYNAMIC);
  653. }
  654.  
  655. /*
  656.  *----------------------------------------------------------------------
  657.  *
  658.  * ConfigureButton --
  659.  *
  660.  *    This procedure is called to process an argv/argc list, plus
  661.  *    the Tk option database, in order to configure (or
  662.  *    reconfigure) a button widget.
  663.  *
  664.  * Results:
  665.  *    The return value is a standard Tcl result.  If TCL_ERROR is
  666.  *    returned, then interp->result contains an error message.
  667.  *
  668.  * Side effects:
  669.  *    Configuration information, such as text string, colors, font,
  670.  *    etc. get set for butPtr;  old resources get freed, if there
  671.  *    were any.  The button is redisplayed.
  672.  *
  673.  *----------------------------------------------------------------------
  674.  */
  675.  
  676. static int
  677. ConfigureButton(interp, butPtr, argc, argv, flags)
  678.     Tcl_Interp *interp;        /* Used for error reporting. */
  679.     register TkButton *butPtr;    /* Information about widget;  may or may
  680.                  * not already have values for some fields. */
  681.     int argc;            /* Number of valid entries in argv. */
  682.     char **argv;        /* Arguments. */
  683.     int flags;            /* Flags to pass to Tk_ConfigureWidget. */
  684. {
  685.     Tk_Image image;
  686.  
  687.     /*
  688.      * Eliminate any existing trace on variables monitored by the button.
  689.      */
  690.  
  691.     if (butPtr->textVarName != NULL) {
  692.     Tcl_UntraceVar(interp, butPtr->textVarName, 
  693.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  694.         ButtonTextVarProc, (ClientData) butPtr);
  695.     }
  696.     if (butPtr->selVarName != NULL) {
  697.     Tcl_UntraceVar(interp, butPtr->selVarName, 
  698.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  699.         ButtonVarProc, (ClientData) butPtr);
  700.     }
  701.  
  702.     
  703.  
  704.     if (Tk_ConfigureWidget(interp, butPtr->tkwin, tkpButtonConfigSpecs,
  705.         argc, argv, (char *) butPtr, flags) != TCL_OK) {
  706.     return TCL_ERROR;
  707.     }
  708.  
  709.     /*
  710.      * A few options need special processing, such as setting the
  711.      * background from a 3-D border, or filling in complicated
  712.      * defaults that couldn't be specified to Tk_ConfigureWidget.
  713.      */
  714.  
  715.     if ((butPtr->state == tkActiveUid) && !Tk_StrictMotif(butPtr->tkwin)) {
  716.     Tk_SetBackgroundFromBorder(butPtr->tkwin, butPtr->activeBorder);
  717.     } else {
  718.     Tk_SetBackgroundFromBorder(butPtr->tkwin, butPtr->normalBorder);
  719.     if ((butPtr->state != tkNormalUid) && (butPtr->state != tkActiveUid)
  720.         && (butPtr->state != tkDisabledUid)) {
  721.         Tcl_AppendResult(interp, "bad state value \"", butPtr->state,
  722.             "\": must be normal, active, or disabled", (char *) NULL);
  723.         butPtr->state = tkNormalUid;
  724.         return TCL_ERROR;
  725.     }
  726.     }
  727.  
  728.     if ((butPtr->defaultState != tkActiveUid)
  729.         && (butPtr->defaultState != tkDisabledUid)
  730.         && (butPtr->defaultState != tkNormalUid)) {
  731.     Tcl_AppendResult(interp, "bad -default value \"", butPtr->defaultState,
  732.         "\": must be normal, active, or disabled", (char *) NULL);
  733.     butPtr->defaultState = tkDisabledUid;
  734.     return TCL_ERROR;
  735.     }
  736.  
  737.     if (butPtr->highlightWidth < 0) {
  738.     butPtr->highlightWidth = 0;
  739.     }
  740.  
  741.     if (butPtr->padX < 0) {
  742.     butPtr->padX = 0;
  743.     }
  744.     if (butPtr->padY < 0) {
  745.     butPtr->padY = 0;
  746.     }
  747.  
  748.     if (butPtr->type >= TYPE_CHECK_BUTTON) {
  749.     char *value;
  750.  
  751.     if (butPtr->selVarName == NULL) {
  752.         butPtr->selVarName = (char *) ckalloc((unsigned)
  753.             (strlen(Tk_Name(butPtr->tkwin)) + 1));
  754.         strcpy(butPtr->selVarName, Tk_Name(butPtr->tkwin));
  755.     }
  756.  
  757.     /*
  758.      * Select the button if the associated variable has the
  759.      * appropriate value, initialize the variable if it doesn't
  760.      * exist, then set a trace on the variable to monitor future
  761.      * changes to its value.
  762.      */
  763.  
  764.     value = Tcl_GetVar(interp, butPtr->selVarName, TCL_GLOBAL_ONLY);
  765.     butPtr->flags &= ~SELECTED;
  766.     if (value != NULL) {
  767.         if (strcmp(value, butPtr->onValue) == 0) {
  768.         butPtr->flags |= SELECTED;
  769.         }
  770.     } else {
  771.         if (Tcl_SetVar(interp, butPtr->selVarName,
  772.             (butPtr->type == TYPE_CHECK_BUTTON) ? butPtr->offValue : "",
  773.             TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
  774.         return TCL_ERROR;
  775.         }
  776.     }
  777.     Tcl_TraceVar(interp, butPtr->selVarName,
  778.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  779.         ButtonVarProc, (ClientData) butPtr);
  780.     }
  781.  
  782.     /*
  783.      * Get the images for the widget, if there are any.  Allocate the
  784.      * new images before freeing the old ones, so that the reference
  785.      * counts don't go to zero and cause image data to be discarded.
  786.      */
  787.  
  788.     if (butPtr->imageString != NULL) {
  789.     image = Tk_GetImage(butPtr->interp, butPtr->tkwin,
  790.         butPtr->imageString, ButtonImageProc, (ClientData) butPtr);
  791.     if (image == NULL) {
  792.         return TCL_ERROR;
  793.     }
  794.     } else {
  795.     image = NULL;
  796.     }
  797.     if (butPtr->image != NULL) {
  798.     Tk_FreeImage(butPtr->image);
  799.     }
  800.     butPtr->image = image;
  801.     if (butPtr->selectImageString != NULL) {
  802.     image = Tk_GetImage(butPtr->interp, butPtr->tkwin,
  803.         butPtr->selectImageString, ButtonSelectImageProc,
  804.         (ClientData) butPtr);
  805.     if (image == NULL) {
  806.         return TCL_ERROR;
  807.     }
  808.     } else {
  809.     image = NULL;
  810.     }
  811.     if (butPtr->selectImage != NULL) {
  812.     Tk_FreeImage(butPtr->selectImage);
  813.     }
  814.     butPtr->selectImage = image;
  815.  
  816.     if ((butPtr->image == NULL) && (butPtr->bitmap == None)
  817.         && (butPtr->textVarName != NULL)) {
  818.     /*
  819.      * The button must display the value of a variable: set up a trace
  820.      * on the variable's value, create the variable if it doesn't
  821.      * exist, and fetch its current value.
  822.      */
  823.  
  824.     char *value;
  825.  
  826.     value = Tcl_GetVar(interp, butPtr->textVarName, TCL_GLOBAL_ONLY);
  827.     if (value == NULL) {
  828.         if (Tcl_SetVar(interp, butPtr->textVarName, butPtr->text,
  829.             TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
  830.         return TCL_ERROR;
  831.         }
  832.     } else {
  833.         if (butPtr->text != NULL) {
  834.         ckfree(butPtr->text);
  835.         }
  836.         butPtr->text = (char *) ckalloc((unsigned) (strlen(value) + 1));
  837.         strcpy(butPtr->text, value);
  838.     }
  839.     Tcl_TraceVar(interp, butPtr->textVarName,
  840.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  841.         ButtonTextVarProc, (ClientData) butPtr);
  842.     }
  843.  
  844.     if ((butPtr->bitmap != None) || (butPtr->image != NULL)) {
  845.     if (Tk_GetPixels(interp, butPtr->tkwin, butPtr->widthString,
  846.         &butPtr->width) != TCL_OK) {
  847.         widthError:
  848.         Tcl_AddErrorInfo(interp, "\n    (processing -width option)");
  849.         return TCL_ERROR;
  850.     }
  851.     if (Tk_GetPixels(interp, butPtr->tkwin, butPtr->heightString,
  852.         &butPtr->height) != TCL_OK) {
  853.         heightError:
  854.         Tcl_AddErrorInfo(interp, "\n    (processing -height option)");
  855.         return TCL_ERROR;
  856.     }
  857.     } else {
  858.     if (Tcl_GetInt(interp, butPtr->widthString, &butPtr->width)
  859.         != TCL_OK) {
  860.         goto widthError;
  861.     }
  862.     if (Tcl_GetInt(interp, butPtr->heightString, &butPtr->height)
  863.         != TCL_OK) {
  864.         goto heightError;
  865.     }
  866.     }
  867.     
  868.     TkButtonWorldChanged((ClientData) butPtr);
  869.     return TCL_OK;
  870. }
  871.  
  872. /*
  873.  *---------------------------------------------------------------------------
  874.  *
  875.  * TkButtonWorldChanged --
  876.  *
  877.  *      This procedure is called when the world has changed in some
  878.  *      way and the widget needs to recompute all its graphics contexts
  879.  *    and determine its new geometry.
  880.  *
  881.  * Results:
  882.  *      None.
  883.  *
  884.  * Side effects:
  885.  *      Button will be relayed out and redisplayed.
  886.  *
  887.  *---------------------------------------------------------------------------
  888.  */
  889.  
  890. void
  891. TkButtonWorldChanged(instanceData)
  892.     ClientData instanceData;    /* Information about widget. */
  893. {
  894.     XGCValues gcValues;
  895.     GC newGC;
  896.     unsigned long mask;
  897.     TkButton *butPtr;
  898.  
  899.     butPtr = (TkButton *) instanceData;
  900.  
  901.     /*
  902.      * Recompute GCs.
  903.      */
  904.  
  905.     gcValues.font = Tk_FontId(butPtr->tkfont);
  906.     gcValues.foreground = butPtr->normalFg->pixel;
  907.     gcValues.background = Tk_3DBorderColor(butPtr->normalBorder)->pixel;
  908.     
  909.     /*
  910.      * Note: GraphicsExpose events are disabled in normalTextGC because it's
  911.      * used to copy stuff from an off-screen pixmap onto the screen (we know
  912.      * that there's no problem with obscured areas).
  913.      */
  914.  
  915.     gcValues.graphics_exposures = False;
  916.     mask = GCForeground | GCBackground | GCFont | GCGraphicsExposures;
  917.     newGC = Tk_GetGC(butPtr->tkwin, mask, &gcValues);
  918.     if (butPtr->normalTextGC != None) {
  919.     Tk_FreeGC(butPtr->display, butPtr->normalTextGC);
  920.     }
  921.     butPtr->normalTextGC = newGC;
  922.  
  923.     if (butPtr->activeFg != NULL) {
  924.     gcValues.font = Tk_FontId(butPtr->tkfont);
  925.     gcValues.foreground = butPtr->activeFg->pixel;
  926.     gcValues.background = Tk_3DBorderColor(butPtr->activeBorder)->pixel;
  927.     mask = GCForeground | GCBackground | GCFont;
  928.     newGC = Tk_GetGC(butPtr->tkwin, mask, &gcValues);
  929.     if (butPtr->activeTextGC != None) {
  930.         Tk_FreeGC(butPtr->display, butPtr->activeTextGC);
  931.     }
  932.     butPtr->activeTextGC = newGC;
  933.     }
  934.  
  935.     if (butPtr->type != TYPE_LABEL) {
  936.     gcValues.font = Tk_FontId(butPtr->tkfont);
  937.     gcValues.background = Tk_3DBorderColor(butPtr->normalBorder)->pixel;
  938.     if ((butPtr->disabledFg != NULL) && (butPtr->imageString == NULL)) {
  939.         gcValues.foreground = butPtr->disabledFg->pixel;
  940.         mask = GCForeground | GCBackground | GCFont;
  941.     } else {
  942.         gcValues.foreground = gcValues.background;
  943.         mask = GCForeground;
  944.         if (butPtr->gray == None) {
  945.         butPtr->gray = Tk_GetBitmap(NULL, butPtr->tkwin, 
  946.             Tk_GetUid("gray50"));
  947.         }
  948.         if (butPtr->gray != None) {
  949.         gcValues.fill_style = FillStippled;
  950.         gcValues.stipple = butPtr->gray;
  951.         mask |= GCFillStyle | GCStipple;
  952.         }
  953.     }
  954.     newGC = Tk_GetGC(butPtr->tkwin, mask, &gcValues);
  955.     if (butPtr->disabledGC != None) {
  956.         Tk_FreeGC(butPtr->display, butPtr->disabledGC);
  957.     }
  958.     butPtr->disabledGC = newGC;
  959.     }
  960.  
  961.     if (butPtr->copyGC == None) {
  962.     butPtr->copyGC = Tk_GetGC(butPtr->tkwin, 0, &gcValues);
  963.     }
  964.  
  965.     TkpComputeButtonGeometry(butPtr);
  966.  
  967.     /*
  968.      * Lastly, arrange for the button to be redisplayed.
  969.      */
  970.  
  971.     if (Tk_IsMapped(butPtr->tkwin) && !(butPtr->flags & REDRAW_PENDING)) {
  972.     Tcl_DoWhenIdle(TkpDisplayButton, (ClientData) butPtr);
  973.     butPtr->flags |= REDRAW_PENDING;
  974.     }
  975. }
  976.  
  977. /*
  978.  *--------------------------------------------------------------
  979.  *
  980.  * ButtonEventProc --
  981.  *
  982.  *    This procedure is invoked by the Tk dispatcher for various
  983.  *    events on buttons.
  984.  *
  985.  * Results:
  986.  *    None.
  987.  *
  988.  * Side effects:
  989.  *    When the window gets deleted, internal structures get
  990.  *    cleaned up.  When it gets exposed, it is redisplayed.
  991.  *
  992.  *--------------------------------------------------------------
  993.  */
  994.  
  995. static void
  996. ButtonEventProc(clientData, eventPtr)
  997.     ClientData clientData;    /* Information about window. */
  998.     XEvent *eventPtr;        /* Information about event. */
  999. {
  1000.     TkButton *butPtr = (TkButton *) clientData;
  1001.     if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
  1002.     goto redraw;
  1003.     } else if (eventPtr->type == ConfigureNotify) {
  1004.     /*
  1005.      * Must redraw after size changes, since layout could have changed
  1006.      * and borders will need to be redrawn.
  1007.      */
  1008.  
  1009.     goto redraw;
  1010.     } else if (eventPtr->type == DestroyNotify) {
  1011.     TkpDestroyButton(butPtr);
  1012.     if (butPtr->tkwin != NULL) {
  1013.         butPtr->tkwin = NULL;
  1014.             Tcl_DeleteCommandFromToken(butPtr->interp, butPtr->widgetCmd);
  1015.     }
  1016.     if (butPtr->flags & REDRAW_PENDING) {
  1017.         Tcl_CancelIdleCall(TkpDisplayButton, (ClientData) butPtr);
  1018.     }
  1019.     DestroyButton(butPtr);
  1020.     } else if (eventPtr->type == FocusIn) {
  1021.     if (eventPtr->xfocus.detail != NotifyInferior) {
  1022.         butPtr->flags |= GOT_FOCUS;
  1023.         if (butPtr->highlightWidth > 0) {
  1024.         goto redraw;
  1025.         }
  1026.     }
  1027.     } else if (eventPtr->type == FocusOut) {
  1028.     if (eventPtr->xfocus.detail != NotifyInferior) {
  1029.         butPtr->flags &= ~GOT_FOCUS;
  1030.         if (butPtr->highlightWidth > 0) {
  1031.         goto redraw;
  1032.         }
  1033.     }
  1034.     }
  1035.     return;
  1036.  
  1037.     redraw:
  1038.     if ((butPtr->tkwin != NULL) && !(butPtr->flags & REDRAW_PENDING)) {
  1039.     Tcl_DoWhenIdle(TkpDisplayButton, (ClientData) butPtr);
  1040.     butPtr->flags |= REDRAW_PENDING;
  1041.     }
  1042. }
  1043.  
  1044. /*
  1045.  *----------------------------------------------------------------------
  1046.  *
  1047.  * ButtonCmdDeletedProc --
  1048.  *
  1049.  *    This procedure is invoked when a widget command is deleted.  If
  1050.  *    the widget isn't already in the process of being destroyed,
  1051.  *    this command destroys it.
  1052.  *
  1053.  * Results:
  1054.  *    None.
  1055.  *
  1056.  * Side effects:
  1057.  *    The widget is destroyed.
  1058.  *
  1059.  *----------------------------------------------------------------------
  1060.  */
  1061.  
  1062. static void
  1063. ButtonCmdDeletedProc(clientData)
  1064.     ClientData clientData;    /* Pointer to widget record for widget. */
  1065. {
  1066.     TkButton *butPtr = (TkButton *) clientData;
  1067.     Tk_Window tkwin = butPtr->tkwin;
  1068.  
  1069.     /*
  1070.      * This procedure could be invoked either because the window was
  1071.      * destroyed and the command was then deleted (in which case tkwin
  1072.      * is NULL) or because the command was deleted, and then this procedure
  1073.      * destroys the widget.
  1074.      */
  1075.  
  1076.     if (tkwin != NULL) {
  1077.     butPtr->tkwin = NULL;
  1078.     Tk_DestroyWindow(tkwin);
  1079.     }
  1080. }
  1081.  
  1082. /*
  1083.  *----------------------------------------------------------------------
  1084.  *
  1085.  * TkInvokeButton --
  1086.  *
  1087.  *    This procedure is called to carry out the actions associated
  1088.  *    with a button, such as invoking a Tcl command or setting a
  1089.  *    variable.  This procedure is invoked, for example, when the
  1090.  *    button is invoked via the mouse.
  1091.  *
  1092.  * Results:
  1093.  *    A standard Tcl return value.  Information is also left in
  1094.  *    interp->result.
  1095.  *
  1096.  * Side effects:
  1097.  *    Depends on the button and its associated command.
  1098.  *
  1099.  *----------------------------------------------------------------------
  1100.  */
  1101.  
  1102. int
  1103. TkInvokeButton(butPtr)
  1104.     register TkButton *butPtr;        /* Information about button. */
  1105. {
  1106.     if (butPtr->type == TYPE_CHECK_BUTTON) {
  1107.     if (butPtr->flags & SELECTED) {
  1108.         if (Tcl_SetVar(butPtr->interp, butPtr->selVarName, butPtr->offValue,
  1109.             TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
  1110.         return TCL_ERROR;
  1111.         }
  1112.     } else {
  1113.         if (Tcl_SetVar(butPtr->interp, butPtr->selVarName, butPtr->onValue,
  1114.             TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
  1115.         return TCL_ERROR;
  1116.         }
  1117.     }
  1118.     } else if (butPtr->type == TYPE_RADIO_BUTTON) {
  1119.     if (Tcl_SetVar(butPtr->interp, butPtr->selVarName, butPtr->onValue,
  1120.         TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
  1121.         return TCL_ERROR;
  1122.     }
  1123.     }
  1124.     if ((butPtr->type != TYPE_LABEL) && (butPtr->command != NULL)) {
  1125.     return TkCopyAndGlobalEval(butPtr->interp, butPtr->command);
  1126.     }
  1127.     return TCL_OK;
  1128. }
  1129.  
  1130. /*
  1131.  *--------------------------------------------------------------
  1132.  *
  1133.  * ButtonVarProc --
  1134.  *
  1135.  *    This procedure is invoked when someone changes the
  1136.  *    state variable associated with a radio button.  Depending
  1137.  *    on the new value of the button's variable, the button
  1138.  *    may be selected or deselected.
  1139.  *
  1140.  * Results:
  1141.  *    NULL is always returned.
  1142.  *
  1143.  * Side effects:
  1144.  *    The button may become selected or deselected.
  1145.  *
  1146.  *--------------------------------------------------------------
  1147.  */
  1148.  
  1149.     /* ARGSUSED */
  1150. static char *
  1151. ButtonVarProc(clientData, interp, name1, name2, flags)
  1152.     ClientData clientData;    /* Information about button. */
  1153.     Tcl_Interp *interp;        /* Interpreter containing variable. */
  1154.     char *name1;        /* Name of variable. */
  1155.     char *name2;        /* Second part of variable name. */
  1156.     int flags;            /* Information about what happened. */
  1157. {
  1158.     register TkButton *butPtr = (TkButton *) clientData;
  1159.     char *value;
  1160.  
  1161.     /*
  1162.      * If the variable is being unset, then just re-establish the
  1163.      * trace unless the whole interpreter is going away.
  1164.      */
  1165.  
  1166.     if (flags & TCL_TRACE_UNSETS) {
  1167.     butPtr->flags &= ~SELECTED;
  1168.     if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
  1169.         Tcl_TraceVar(interp, butPtr->selVarName,
  1170.             TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  1171.             ButtonVarProc, clientData);
  1172.     }
  1173.     goto redisplay;
  1174.     }
  1175.  
  1176.     /*
  1177.      * Use the value of the variable to update the selected status of
  1178.      * the button.
  1179.      */
  1180.  
  1181.     value = Tcl_GetVar(interp, butPtr->selVarName, TCL_GLOBAL_ONLY);
  1182.     if (value == NULL) {
  1183.     value = "";
  1184.     }
  1185.     if (strcmp(value, butPtr->onValue) == 0) {
  1186.     if (butPtr->flags & SELECTED) {
  1187.         return (char *) NULL;
  1188.     }
  1189.     butPtr->flags |= SELECTED;
  1190.     } else if (butPtr->flags & SELECTED) {
  1191.     butPtr->flags &= ~SELECTED;
  1192.     } else {
  1193.     return (char *) NULL;
  1194.     }
  1195.  
  1196.     redisplay:
  1197.     if ((butPtr->tkwin != NULL) && Tk_IsMapped(butPtr->tkwin)
  1198.         && !(butPtr->flags & REDRAW_PENDING)) {
  1199.     Tcl_DoWhenIdle(TkpDisplayButton, (ClientData) butPtr);
  1200.     butPtr->flags |= REDRAW_PENDING;
  1201.     }
  1202.     return (char *) NULL;
  1203. }
  1204.  
  1205. /*
  1206.  *--------------------------------------------------------------
  1207.  *
  1208.  * ButtonTextVarProc --
  1209.  *
  1210.  *    This procedure is invoked when someone changes the variable
  1211.  *    whose contents are to be displayed in a button.
  1212.  *
  1213.  * Results:
  1214.  *    NULL is always returned.
  1215.  *
  1216.  * Side effects:
  1217.  *    The text displayed in the button will change to match the
  1218.  *    variable.
  1219.  *
  1220.  *--------------------------------------------------------------
  1221.  */
  1222.  
  1223.     /* ARGSUSED */
  1224. static char *
  1225. ButtonTextVarProc(clientData, interp, name1, name2, flags)
  1226.     ClientData clientData;    /* Information about button. */
  1227.     Tcl_Interp *interp;        /* Interpreter containing variable. */
  1228.     char *name1;        /* Not used. */
  1229.     char *name2;        /* Not used. */
  1230.     int flags;            /* Information about what happened. */
  1231. {
  1232.     register TkButton *butPtr = (TkButton *) clientData;
  1233.     char *value;
  1234.  
  1235.     /*
  1236.      * If the variable is unset, then immediately recreate it unless
  1237.      * the whole interpreter is going away.
  1238.      */
  1239.  
  1240.     if (flags & TCL_TRACE_UNSETS) {
  1241.     if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
  1242.         Tcl_SetVar(interp, butPtr->textVarName, butPtr->text,
  1243.             TCL_GLOBAL_ONLY);
  1244.         Tcl_TraceVar(interp, butPtr->textVarName,
  1245.             TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  1246.             ButtonTextVarProc, clientData);
  1247.     }
  1248.     return (char *) NULL;
  1249.     }
  1250.  
  1251.     value = Tcl_GetVar(interp, butPtr->textVarName, TCL_GLOBAL_ONLY);
  1252.     if (value == NULL) {
  1253.     value = "";
  1254.     }
  1255.     if (butPtr->text != NULL) {
  1256.     ckfree(butPtr->text);
  1257.     }
  1258.     butPtr->text = (char *) ckalloc((unsigned) (strlen(value) + 1));
  1259.     strcpy(butPtr->text, value);
  1260.     TkpComputeButtonGeometry(butPtr);
  1261.  
  1262.     if ((butPtr->tkwin != NULL) && Tk_IsMapped(butPtr->tkwin)
  1263.         && !(butPtr->flags & REDRAW_PENDING)) {
  1264.     Tcl_DoWhenIdle(TkpDisplayButton, (ClientData) butPtr);
  1265.     butPtr->flags |= REDRAW_PENDING;
  1266.     }
  1267.     return (char *) NULL;
  1268. }
  1269.  
  1270. /*
  1271.  *----------------------------------------------------------------------
  1272.  *
  1273.  * ButtonImageProc --
  1274.  *
  1275.  *    This procedure is invoked by the image code whenever the manager
  1276.  *    for an image does something that affects the size of contents
  1277.  *    of an image displayed in a button.
  1278.  *
  1279.  * Results:
  1280.  *    None.
  1281.  *
  1282.  * Side effects:
  1283.  *    Arranges for the button to get redisplayed.
  1284.  *
  1285.  *----------------------------------------------------------------------
  1286.  */
  1287.  
  1288. static void
  1289. ButtonImageProc(clientData, x, y, width, height, imgWidth, imgHeight)
  1290.     ClientData clientData;        /* Pointer to widget record. */
  1291.     int x, y;                /* Upper left pixel (within image)
  1292.                      * that must be redisplayed. */
  1293.     int width, height;            /* Dimensions of area to redisplay
  1294.                      * (may be <= 0). */
  1295.     int imgWidth, imgHeight;        /* New dimensions of image. */
  1296. {
  1297.     register TkButton *butPtr = (TkButton *) clientData;
  1298.  
  1299.     if (butPtr->tkwin != NULL) {
  1300.     TkpComputeButtonGeometry(butPtr);
  1301.     if (Tk_IsMapped(butPtr->tkwin) && !(butPtr->flags & REDRAW_PENDING)) {
  1302.         Tcl_DoWhenIdle(TkpDisplayButton, (ClientData) butPtr);
  1303.         butPtr->flags |= REDRAW_PENDING;
  1304.     }
  1305.     }
  1306. }
  1307.  
  1308. /*
  1309.  *----------------------------------------------------------------------
  1310.  *
  1311.  * ButtonSelectImageProc --
  1312.  *
  1313.  *    This procedure is invoked by the image code whenever the manager
  1314.  *    for an image does something that affects the size of contents
  1315.  *    of the image displayed in a button when it is selected.
  1316.  *
  1317.  * Results:
  1318.  *    None.
  1319.  *
  1320.  * Side effects:
  1321.  *    May arrange for the button to get redisplayed.
  1322.  *
  1323.  *----------------------------------------------------------------------
  1324.  */
  1325.  
  1326. static void
  1327. ButtonSelectImageProc(clientData, x, y, width, height, imgWidth, imgHeight)
  1328.     ClientData clientData;        /* Pointer to widget record. */
  1329.     int x, y;                /* Upper left pixel (within image)
  1330.                      * that must be redisplayed. */
  1331.     int width, height;            /* Dimensions of area to redisplay
  1332.                      * (may be <= 0). */
  1333.     int imgWidth, imgHeight;        /* New dimensions of image. */
  1334. {
  1335.     register TkButton *butPtr = (TkButton *) clientData;
  1336.  
  1337.     /*
  1338.      * Don't recompute geometry:  it's controlled by the primary image.
  1339.      */
  1340.  
  1341.     if ((butPtr->flags & SELECTED) && (butPtr->tkwin != NULL)
  1342.         && Tk_IsMapped(butPtr->tkwin)
  1343.         && !(butPtr->flags & REDRAW_PENDING)) {
  1344.     Tcl_DoWhenIdle(TkpDisplayButton, (ClientData) butPtr);
  1345.     butPtr->flags |= REDRAW_PENDING;
  1346.     }
  1347. }
  1348.