home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / languages / tcl / tk3.3b1 / tkButton.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-06-16  |  45.1 KB  |  1,428 lines

  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-1993 The Regents of the University of California.
  10.  * All rights reserved.
  11.  *
  12.  * Permission is hereby granted, without written agreement and without
  13.  * license or royalty fees, to use, copy, modify, and distribute this
  14.  * software and its documentation for any purpose, provided that the
  15.  * above copyright notice and the following two paragraphs appear in
  16.  * all copies of this software.
  17.  * 
  18.  * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
  19.  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
  20.  * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
  21.  * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  22.  *
  23.  * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
  24.  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
  25.  * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
  26.  * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
  27.  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  28.  */
  29.  
  30. #ifndef lint
  31. static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkButton.c,v 1.77 93/06/16 17:15:20 ouster Exp $ SPRITE (Berkeley)";
  32. #endif
  33.  
  34. #include "default.h"
  35. #include "tkConfig.h"
  36. #include "tkInt.h"
  37.  
  38. /*
  39.  * A data structure of the following type is kept for each
  40.  * widget managed by this file:
  41.  */
  42.  
  43. typedef struct {
  44.     Tk_Window tkwin;        /* Window that embodies the button.  NULL
  45.                  * means that the window has been destroyed. */
  46.     Display *display;        /* Display containing widget.  Needed to
  47.                  * free up resources after tkwin is gone. */
  48.     Tcl_Interp *interp;        /* Interpreter associated with button. */
  49.     int type;            /* Type of widget:  restricts operations
  50.                  * that may be performed on widget.  See
  51.                  * below for possible values. */
  52.  
  53.     /*
  54.      * Information about what's in the button.
  55.      */
  56.  
  57.     char *text;            /* Text to display in button (malloc'ed)
  58.                  * or NULL. */
  59.     int textLength;        /* # of characters in text. */
  60.     char *textVarName;        /* Name of variable (malloc'ed) or NULL.
  61.                  * If non-NULL, button displays the contents
  62.                  * of this variable. */
  63.     Pixmap bitmap;        /* Bitmap to display or None.  If not None
  64.                  * then text and textVar are ignored. */
  65.  
  66.     /*
  67.      * Information used when displaying widget:
  68.      */
  69.  
  70.     Tk_Uid state;        /* State of button for display purposes:
  71.                  * normal, active, or disabled. */
  72.     Tk_3DBorder normalBorder;    /* Structure used to draw 3-D
  73.                  * border and background when window
  74.                  * isn't active.  NULL means no such
  75.                  * border exists. */
  76.     Tk_3DBorder activeBorder;    /* Structure used to draw 3-D
  77.                  * border and background when window
  78.                  * is active.  NULL means no such
  79.                  * border exists. */
  80.     int borderWidth;        /* Width of border. */
  81.     int relief;            /* 3-d effect: TK_RELIEF_RAISED, etc. */
  82.     XFontStruct *fontPtr;    /* Information about text font, or NULL. */
  83.     XColor *normalFg;        /* Foreground color in normal mode. */
  84.     XColor *activeFg;        /* Foreground color in active mode.  NULL
  85.                  * means use normalFg instead. */
  86.     XColor *disabledFg;        /* Foreground color when disabled.  NULL
  87.                  * means use normalFg with a 50% stipple
  88.                  * instead. */
  89.     GC normalTextGC;        /* GC for drawing text in normal mode.  Also
  90.                  * used to copy from off-screen pixmap onto
  91.                  * screen. */
  92.     GC activeTextGC;        /* GC for drawing text in active mode (NULL
  93.                  * means use normalTextGC). */
  94.     Pixmap gray;        /* Pixmap for displaying disabled text if
  95.                  * disabledFg is NULL. */
  96.     GC disabledGC;        /* Used to produce disabled effect.  If
  97.                  * disabledFg isn't NULL, this GC is used to
  98.                  * draw button text or icon.  Otherwise
  99.                  * text or icon is drawn with normalGC and
  100.                  * this GC is used to stipple background
  101.                  * across it. */
  102.     int leftBearing;        /* Distance from origin of text to its leftmost
  103.                  * drawn pixel, in pixels (positive measures
  104.                  * to the right). */
  105.     int rightBearing;        /* Amount text sticks right from its origin. */
  106.     int width, height;        /* If > 0, these specify dimensions to request
  107.                  * for window, in characters for text and in
  108.                  * pixels for bitmaps.  In this case the actual
  109.                  * size of the text string or bitmap is
  110.                  * ignored in computing desired window size. */
  111.     int padX, padY;        /* Extra space around text or bitmap (pixels
  112.                  * on each side). */
  113.     Tk_Anchor anchor;        /* Where text/bitmap should be displayed
  114.                  * inside button region. */
  115.     XColor *selectorFg;        /* Color for selector. */
  116.     GC selectorGC;        /* For drawing highlight when this button
  117.                  * is in selected state. */
  118.     int selectorSpace;        /* Horizontal space (in pixels) allocated for
  119.                  * display of selector. */
  120.     int selectorDiameter;    /* Diameter of selector, in pixels. */
  121.  
  122.     /*
  123.      * For check and radio buttons, the fields below are used
  124.      * to manage the variable indicating the button's state.
  125.      */
  126.  
  127.     char *selVarName;        /* Name of variable used to control selected
  128.                  * state of button.  Malloc'ed (if
  129.                  * not NULL). */
  130.     char *onValue;        /* Value to store in variable when
  131.                  * this button is selected.  Malloc'ed (if
  132.                  * not NULL). */
  133.     char *offValue;        /* Value to store in variable when this
  134.                  * button isn't selected.  Malloc'ed
  135.                  * (if not NULL).  Valid only for check
  136.                  * buttons. */
  137.  
  138.     /*
  139.      * Miscellaneous information:
  140.      */
  141.  
  142.     Cursor cursor;        /* Current cursor for window, or None. */
  143.     char *command;        /* Command to execute when button is
  144.                  * invoked; valid for buttons only.
  145.                  * If not NULL, it's malloc-ed. */
  146.     int flags;            /* Various flags;  see below for
  147.                  * definitions. */
  148. } Button;
  149.  
  150. /*
  151.  * Possible "type" values for buttons.  These are the kinds of
  152.  * widgets supported by this file.  The ordering of the type
  153.  * numbers is significant:  greater means more features and is
  154.  * used in the code.
  155.  */
  156.  
  157. #define TYPE_LABEL        0
  158. #define TYPE_BUTTON        1
  159. #define TYPE_CHECK_BUTTON    2
  160. #define TYPE_RADIO_BUTTON    3
  161.  
  162. /*
  163.  * Class names for buttons, indexed by one of the type values above.
  164.  */
  165.  
  166. static char *classNames[] = {"Label", "Button", "Checkbutton", "Radiobutton"};
  167.  
  168. /*
  169.  * Flag bits for buttons:
  170.  *
  171.  * REDRAW_PENDING:        Non-zero means a DoWhenIdle handler
  172.  *                has already been queued to redraw
  173.  *                this window.
  174.  * SELECTED:            Non-zero means this button is selected,
  175.  *                so special highlight should be drawn.
  176.  */
  177.  
  178. #define REDRAW_PENDING        1
  179. #define SELECTED        2
  180.  
  181. /*
  182.  * Mask values used to selectively enable entries in the
  183.  * configuration specs:
  184.  */
  185.  
  186. #define LABEL_MASK        TK_CONFIG_USER_BIT
  187. #define BUTTON_MASK        TK_CONFIG_USER_BIT << 1
  188. #define CHECK_BUTTON_MASK    TK_CONFIG_USER_BIT << 2
  189. #define RADIO_BUTTON_MASK    TK_CONFIG_USER_BIT << 3
  190. #define ALL_MASK        (LABEL_MASK | BUTTON_MASK \
  191.     | CHECK_BUTTON_MASK | RADIO_BUTTON_MASK)
  192.  
  193. static int configFlags[] = {LABEL_MASK, BUTTON_MASK,
  194.     CHECK_BUTTON_MASK, RADIO_BUTTON_MASK};
  195. /*
  196.  * Information used for parsing configuration specs:
  197.  */
  198.  
  199. static Tk_ConfigSpec configSpecs[] = {
  200.     {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
  201.     DEF_BUTTON_ACTIVE_BG_COLOR, Tk_Offset(Button, activeBorder),
  202.     BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK
  203.     |TK_CONFIG_COLOR_ONLY},
  204.     {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
  205.     DEF_BUTTON_ACTIVE_BG_MONO, Tk_Offset(Button, activeBorder),
  206.     BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK
  207.     |TK_CONFIG_MONO_ONLY},
  208.     {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
  209.     DEF_BUTTON_ACTIVE_FG_COLOR, Tk_Offset(Button, activeFg), 
  210.     BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK
  211.     |TK_CONFIG_COLOR_ONLY},
  212.     {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
  213.     DEF_BUTTON_ACTIVE_FG_MONO, Tk_Offset(Button, activeFg), 
  214.     BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK
  215.     |TK_CONFIG_MONO_ONLY},
  216.     {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor",
  217.     DEF_BUTTON_ANCHOR, Tk_Offset(Button, anchor), ALL_MASK},
  218.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  219.     DEF_BUTTON_BG_COLOR, Tk_Offset(Button, normalBorder),
  220.     ALL_MASK | TK_CONFIG_COLOR_ONLY},
  221.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  222.     DEF_BUTTON_BG_MONO, Tk_Offset(Button, normalBorder),
  223.     ALL_MASK | TK_CONFIG_MONO_ONLY},
  224.     {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
  225.     (char *) NULL, 0, ALL_MASK},
  226.     {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
  227.     (char *) NULL, 0, ALL_MASK},
  228.     {TK_CONFIG_BITMAP, "-bitmap", "bitmap", "Bitmap",
  229.     DEF_BUTTON_BITMAP, Tk_Offset(Button, bitmap),
  230.     ALL_MASK|TK_CONFIG_NULL_OK},
  231.     {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
  232.     DEF_BUTTON_BORDER_WIDTH, Tk_Offset(Button, borderWidth), ALL_MASK},
  233.     {TK_CONFIG_STRING, "-command", "command", "Command",
  234.     DEF_BUTTON_COMMAND, Tk_Offset(Button, command),
  235.     BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|TK_CONFIG_NULL_OK},
  236.     {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
  237.     DEF_BUTTON_CURSOR, Tk_Offset(Button, cursor),
  238.     ALL_MASK|TK_CONFIG_NULL_OK},
  239.     {TK_CONFIG_COLOR, "-disabledforeground", "disabledForeground",
  240.     "DisabledForeground", DEF_BUTTON_DISABLED_FG_COLOR,
  241.     Tk_Offset(Button, disabledFg), BUTTON_MASK|CHECK_BUTTON_MASK
  242.     |RADIO_BUTTON_MASK|TK_CONFIG_COLOR_ONLY|TK_CONFIG_NULL_OK},
  243.     {TK_CONFIG_COLOR, "-disabledforeground", "disabledForeground",
  244.     "DisabledForeground", DEF_BUTTON_DISABLED_FG_MONO,
  245.     Tk_Offset(Button, disabledFg), BUTTON_MASK|CHECK_BUTTON_MASK
  246.     |RADIO_BUTTON_MASK|TK_CONFIG_MONO_ONLY|TK_CONFIG_NULL_OK},
  247.     {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
  248.     (char *) NULL, 0, ALL_MASK},
  249.     {TK_CONFIG_FONT, "-font", "font", "Font",
  250.     DEF_BUTTON_FONT, Tk_Offset(Button, fontPtr),
  251.     ALL_MASK},
  252.     {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
  253.     DEF_BUTTON_FG, Tk_Offset(Button, normalFg), ALL_MASK},
  254.     {TK_CONFIG_INT, "-height", "height", "Height",
  255.     DEF_BUTTON_HEIGHT, Tk_Offset(Button, height), ALL_MASK},
  256.     {TK_CONFIG_STRING, "-offvalue", "offValue", "Value",
  257.     DEF_BUTTON_OFF_VALUE, Tk_Offset(Button, offValue),
  258.     CHECK_BUTTON_MASK},
  259.     {TK_CONFIG_STRING, "-onvalue", "onValue", "Value",
  260.     DEF_BUTTON_ON_VALUE, Tk_Offset(Button, onValue),
  261.     CHECK_BUTTON_MASK},
  262.     {TK_CONFIG_PIXELS, "-padx", "padX", "Pad",
  263.     DEF_BUTTON_PADX, Tk_Offset(Button, padX), ALL_MASK},
  264.     {TK_CONFIG_PIXELS, "-pady", "padY", "Pad",
  265.     DEF_BUTTON_PADY, Tk_Offset(Button, padY), ALL_MASK},
  266.     {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
  267.     DEF_BUTTON_RELIEF, Tk_Offset(Button, relief), 
  268.     BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
  269.     {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
  270.     DEF_LABEL_RELIEF, Tk_Offset(Button, relief), LABEL_MASK},
  271.     {TK_CONFIG_COLOR, "-selector", "selector", "Foreground",
  272.     DEF_BUTTON_SELECTOR_COLOR, Tk_Offset(Button, selectorFg),
  273.     CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|TK_CONFIG_COLOR_ONLY
  274.     |TK_CONFIG_NULL_OK},
  275.     {TK_CONFIG_COLOR, "-selector", "selector", "Foreground",
  276.     DEF_BUTTON_SELECTOR_MONO, Tk_Offset(Button, selectorFg),
  277.     CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|TK_CONFIG_MONO_ONLY
  278.     |TK_CONFIG_NULL_OK},
  279.     {TK_CONFIG_UID, "-state", "state", "State",
  280.     DEF_BUTTON_STATE, Tk_Offset(Button, state),
  281.     BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
  282.     {TK_CONFIG_STRING, "-text", "text", "Text",
  283.     DEF_BUTTON_TEXT, Tk_Offset(Button, text), ALL_MASK},
  284.     {TK_CONFIG_STRING, "-textvariable", "textVariable", "Variable",
  285.     DEF_BUTTON_TEXT_VARIABLE, Tk_Offset(Button, textVarName),
  286.     ALL_MASK|TK_CONFIG_NULL_OK},
  287.     {TK_CONFIG_STRING, "-value", "value", "Value",
  288.     DEF_BUTTON_VALUE, Tk_Offset(Button, onValue),
  289.     RADIO_BUTTON_MASK|TK_CONFIG_NULL_OK},
  290.     {TK_CONFIG_STRING, "-variable", "variable", "Variable",
  291.     DEF_RADIOBUTTON_VARIABLE, Tk_Offset(Button, selVarName),
  292.     RADIO_BUTTON_MASK},
  293.     {TK_CONFIG_STRING, "-variable", "variable", "Variable",
  294.     DEF_CHECKBUTTON_VARIABLE, Tk_Offset(Button, selVarName),
  295.     CHECK_BUTTON_MASK|TK_CONFIG_NULL_OK},
  296.     {TK_CONFIG_INT, "-width", "width", "Width",
  297.     DEF_BUTTON_WIDTH, Tk_Offset(Button, width), ALL_MASK},
  298.     {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
  299.     (char *) NULL, 0, 0}
  300. };
  301.  
  302. /*
  303.  * String to print out in error messages, identifying options for
  304.  * widget commands for different types of labels or buttons:
  305.  */
  306.  
  307. static char *optionStrings[] = {
  308.     "configure",
  309.     "activate, configure, deactivate, flash, or invoke",
  310.     "activate, configure, deactivate, deselect, flash, invoke, select, or toggle",
  311.     "activate, configure, deactivate, deselect, flash, invoke, or select"
  312. };
  313.  
  314. /*
  315.  * Forward declarations for procedures defined later in this file:
  316.  */
  317.  
  318. static void        ButtonEventProc _ANSI_ARGS_((ClientData clientData,
  319.                 XEvent *eventPtr));
  320. static char *        ButtonTextVarProc _ANSI_ARGS_((ClientData clientData,
  321.                 Tcl_Interp *interp, char *name1, char *name2,
  322.                 int flags));
  323. static char *        ButtonVarProc _ANSI_ARGS_((ClientData clientData,
  324.                 Tcl_Interp *interp, char *name1, char *name2,
  325.                 int flags));
  326. static int        ButtonWidgetCmd _ANSI_ARGS_((ClientData clientData,
  327.                 Tcl_Interp *interp, int argc, char **argv));
  328. static void        ComputeButtonGeometry _ANSI_ARGS_((Button *butPtr));
  329. static int        ConfigureButton _ANSI_ARGS_((Tcl_Interp *interp,
  330.                 Button *butPtr, int argc, char **argv,
  331.                 int flags));
  332. static void        DestroyButton _ANSI_ARGS_((ClientData clientData));
  333. static void        DisplayButton _ANSI_ARGS_((ClientData clientData));
  334. static int        InvokeButton  _ANSI_ARGS_((Button *butPtr));
  335.  
  336. /*
  337.  *--------------------------------------------------------------
  338.  *
  339.  * Tk_ButtonCmd --
  340.  *
  341.  *    This procedure is invoked to process the "button", "label",
  342.  *    "radiobutton", and "checkbutton" Tcl commands.  See the
  343.  *    user documentation for details on what it does.
  344.  *
  345.  * Results:
  346.  *    A standard Tcl result.
  347.  *
  348.  * Side effects:
  349.  *    See the user documentation.
  350.  *
  351.  *--------------------------------------------------------------
  352.  */
  353.  
  354. int
  355. Tk_ButtonCmd(clientData, interp, argc, argv)
  356.     ClientData clientData;    /* Main window associated with
  357.                  * interpreter. */
  358.     Tcl_Interp *interp;        /* Current interpreter. */
  359.     int argc;            /* Number of arguments. */
  360.     char **argv;        /* Argument strings. */
  361. {
  362.     register Button *butPtr;
  363.     int type;
  364.     Tk_Window tkwin = (Tk_Window) clientData;
  365.     Tk_Window new;
  366.  
  367.     if (argc < 2) {
  368.     Tcl_AppendResult(interp, "wrong # args:  should be \"",
  369.         argv[0], " pathName ?options?\"", (char *) NULL);
  370.     return TCL_ERROR;
  371.     }
  372.  
  373.     switch (argv[0][0]) {
  374.     case 'l':
  375.         type = TYPE_LABEL;
  376.         break;
  377.     case 'b':
  378.         type = TYPE_BUTTON;
  379.         break;
  380.     case 'c':
  381.         type = TYPE_CHECK_BUTTON;
  382.         break;
  383.     case 'r':
  384.         type = TYPE_RADIO_BUTTON;
  385.         break;
  386.     default:
  387.         sprintf(interp->result,
  388.             "unknown button-creation command \"%.50s\"", argv[0]);
  389.         return TCL_ERROR;
  390.     }
  391.  
  392.     /*
  393.      * Create the new window.
  394.      */
  395.  
  396.     new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
  397.     if (new == NULL) {
  398.     return TCL_ERROR;
  399.     }
  400.  
  401.     /*
  402.      * Initialize the data structure for the button.
  403.      */
  404.  
  405.     butPtr = (Button *) ckalloc(sizeof(Button));
  406.     butPtr->tkwin = new;
  407.     butPtr->display = Tk_Display(new);
  408.     butPtr->interp = interp;
  409.     butPtr->type = type;
  410.     butPtr->text = NULL;
  411.     butPtr->textVarName = NULL;
  412.     butPtr->bitmap = None;
  413.     butPtr->state = tkNormalUid;
  414.     butPtr->normalBorder = NULL;
  415.     butPtr->activeBorder = NULL;
  416.     butPtr->borderWidth = 0;
  417.     butPtr->relief = TK_RELIEF_FLAT;
  418.     butPtr->fontPtr = NULL;
  419.     butPtr->normalFg = NULL;
  420.     butPtr->activeFg = NULL;
  421.     butPtr->disabledFg = NULL;
  422.     butPtr->normalTextGC = None;
  423.     butPtr->activeTextGC = None;
  424.     butPtr->gray = None;
  425.     butPtr->disabledGC = None;
  426.     butPtr->selectorFg = NULL;
  427.     butPtr->selectorGC = None;
  428.     butPtr->selVarName = NULL;
  429.     butPtr->onValue = NULL;
  430.     butPtr->offValue = NULL;
  431.     butPtr->cursor = None;
  432.     butPtr->command = NULL;
  433.     butPtr->flags = 0;
  434.  
  435.     Tk_SetClass(new, classNames[type]);
  436.     Tk_CreateEventHandler(butPtr->tkwin, ExposureMask|StructureNotifyMask,
  437.         ButtonEventProc, (ClientData) butPtr);
  438.     Tcl_CreateCommand(interp, Tk_PathName(butPtr->tkwin), ButtonWidgetCmd,
  439.         (ClientData) butPtr, (void (*)()) NULL);
  440.     if (ConfigureButton(interp, butPtr, argc-2, argv+2,
  441.         configFlags[type]) != TCL_OK) {
  442.     Tk_DestroyWindow(butPtr->tkwin);
  443.     return TCL_ERROR;
  444.     }
  445.  
  446.     interp->result = Tk_PathName(butPtr->tkwin);
  447.     return TCL_OK;
  448. }
  449.  
  450. /*
  451.  *--------------------------------------------------------------
  452.  *
  453.  * ButtonWidgetCmd --
  454.  *
  455.  *    This procedure is invoked to process the Tcl command
  456.  *    that corresponds to a widget managed by this module.
  457.  *    See the user documentation for details on what it does.
  458.  *
  459.  * Results:
  460.  *    A standard Tcl result.
  461.  *
  462.  * Side effects:
  463.  *    See the user documentation.
  464.  *
  465.  *--------------------------------------------------------------
  466.  */
  467.  
  468. static int
  469. ButtonWidgetCmd(clientData, interp, argc, argv)
  470.     ClientData clientData;    /* Information about button widget. */
  471.     Tcl_Interp *interp;        /* Current interpreter. */
  472.     int argc;            /* Number of arguments. */
  473.     char **argv;        /* Argument strings. */
  474. {
  475.     register Button *butPtr = (Button *) clientData;
  476.     int result = TCL_OK;
  477.     int length;
  478.     char c;
  479.  
  480.     if (argc < 2) {
  481.     sprintf(interp->result,
  482.         "wrong # args: should be \"%.50s option [arg arg ...]\"",
  483.         argv[0]);
  484.     return TCL_ERROR;
  485.     }
  486.     Tk_Preserve((ClientData) butPtr);
  487.     c = argv[1][0];
  488.     length = strlen(argv[1]);
  489.     if ((c == 'a') && (strncmp(argv[1], "activate", length) == 0)
  490.         && (butPtr->type != TYPE_LABEL)) {
  491.     if (argc > 2) {
  492.         sprintf(interp->result,
  493.             "wrong # args: should be \"%.50s activate\"",
  494.             argv[0]);
  495.         goto error;
  496.     }
  497.     if (butPtr->state != tkDisabledUid) {
  498.         butPtr->state = tkActiveUid;
  499.         Tk_SetBackgroundFromBorder(butPtr->tkwin, butPtr->activeBorder);
  500.         goto redisplay;
  501.     }
  502.     } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) {
  503.     if (argc == 2) {
  504.         result = Tk_ConfigureInfo(interp, butPtr->tkwin, configSpecs,
  505.             (char *) butPtr, (char *) NULL, configFlags[butPtr->type]);
  506.     } else if (argc == 3) {
  507.         result = Tk_ConfigureInfo(interp, butPtr->tkwin, configSpecs,
  508.             (char *) butPtr, argv[2],
  509.             configFlags[butPtr->type]);
  510.     } else {
  511.         result = ConfigureButton(interp, butPtr, argc-2, argv+2,
  512.             configFlags[butPtr->type] | TK_CONFIG_ARGV_ONLY);
  513.     }
  514.     } else if ((c == 'd') && (strncmp(argv[1], "deactivate", length) == 0)
  515.         && (length > 2) && (butPtr->type != TYPE_LABEL)) {
  516.     if (argc > 2) {
  517.         sprintf(interp->result,
  518.             "wrong # args: should be \"%.50s deactivate\"",
  519.             argv[0]);
  520.         goto error;
  521.     }
  522.     if (butPtr->state != tkDisabledUid) {
  523.         butPtr->state = tkNormalUid;
  524.         Tk_SetBackgroundFromBorder(butPtr->tkwin, butPtr->normalBorder);
  525.         goto redisplay;
  526.     }
  527.     } else if ((c == 'd') && (strncmp(argv[1], "deselect", length) == 0)
  528.         && (length > 2) && (butPtr->type >= TYPE_CHECK_BUTTON)) {
  529.     if (argc > 2) {
  530.         sprintf(interp->result,
  531.             "wrong # args: should be \"%.50s deselect\"",
  532.             argv[0]);
  533.         goto error;
  534.     }
  535.     if (butPtr->type == TYPE_CHECK_BUTTON) {
  536.         Tcl_SetVar(interp, butPtr->selVarName, butPtr->offValue,
  537.             TCL_GLOBAL_ONLY);
  538.     } else if (butPtr->flags & SELECTED) {
  539.         Tcl_SetVar(interp, butPtr->selVarName, "", TCL_GLOBAL_ONLY);
  540.     }
  541.     } else if ((c == 'f') && (strncmp(argv[1], "flash", length) == 0)
  542.     && (butPtr->type != TYPE_LABEL)) {
  543.     int i;
  544.  
  545.     if (argc > 2) {
  546.         sprintf(interp->result,
  547.             "wrong # args: should be \"%.50s flash\"",
  548.             argv[0]);
  549.         goto error;
  550.     }
  551.     if (butPtr->state != tkDisabledUid) {
  552.         for (i = 0; i < 4; i++) {
  553.         butPtr->state = (butPtr->state == tkNormalUid)
  554.             ? tkActiveUid : tkNormalUid;
  555.         Tk_SetBackgroundFromBorder(butPtr->tkwin,
  556.             (butPtr->state == tkActiveUid) ? butPtr->activeBorder
  557.             : butPtr->normalBorder);
  558.         DisplayButton((ClientData) butPtr);
  559.         XFlush(butPtr->display);
  560.         Tk_Sleep(50);
  561.         }
  562.     }
  563.     } else if ((c == 'i') && (strncmp(argv[1], "invoke", length) == 0)
  564.         && (butPtr->type > TYPE_LABEL)) {
  565.     if (argc > 2) {
  566.         sprintf(interp->result,
  567.             "wrong # args: should be \"%.50s invoke\"",
  568.             argv[0]);
  569.         goto error;
  570.     }
  571.     if (butPtr->state != tkDisabledUid) {
  572.         result = InvokeButton(butPtr);
  573.     }
  574.     } else if ((c == 's') && (strncmp(argv[1], "select", length) == 0)
  575.         && (butPtr->type >= TYPE_CHECK_BUTTON)) {
  576.     if (argc > 2) {
  577.         sprintf(interp->result,
  578.             "wrong # args: should be \"%.50s select\"",
  579.             argv[0]);
  580.         goto error;
  581.     }
  582.     Tcl_SetVar(interp, butPtr->selVarName, butPtr->onValue, TCL_GLOBAL_ONLY);
  583.     } else if ((c == 't') && (strncmp(argv[1], "toggle", length) == 0)
  584.         && (length >= 2) && (butPtr->type == TYPE_CHECK_BUTTON)) {
  585.     if (argc > 2) {
  586.         sprintf(interp->result,
  587.             "wrong # args: should be \"%.50s select\"",
  588.             argv[0]);
  589.         goto error;
  590.     }
  591.     if (butPtr->flags & SELECTED) {
  592.         Tcl_SetVar(interp, butPtr->selVarName, butPtr->offValue, TCL_GLOBAL_ONLY);
  593.     } else {
  594.         Tcl_SetVar(interp, butPtr->selVarName, butPtr->onValue, TCL_GLOBAL_ONLY);
  595.     }
  596.     } else {
  597.     sprintf(interp->result,
  598.         "bad option \"%.50s\":  must be %s", argv[1],
  599.         optionStrings[butPtr->type]);
  600.     goto error;
  601.     }
  602.     Tk_Release((ClientData) butPtr);
  603.     return result;
  604.  
  605.     redisplay:
  606.     if (Tk_IsMapped(butPtr->tkwin) && !(butPtr->flags & REDRAW_PENDING)) {
  607.     Tk_DoWhenIdle(DisplayButton, (ClientData) butPtr);
  608.     butPtr->flags |= REDRAW_PENDING;
  609.     }
  610.     Tk_Release((ClientData) butPtr);
  611.     return TCL_OK;
  612.  
  613.     error:
  614.     Tk_Release((ClientData) butPtr);
  615.     return TCL_ERROR;
  616. }
  617.  
  618. /*
  619.  *----------------------------------------------------------------------
  620.  *
  621.  * DestroyButton --
  622.  *
  623.  *    This procedure is invoked by Tk_EventuallyFree or Tk_Release
  624.  *    to clean up the internal structure of a button at a safe time
  625.  *    (when no-one is using it anymore).
  626.  *
  627.  * Results:
  628.  *    None.
  629.  *
  630.  * Side effects:
  631.  *    Everything associated with the widget is freed up.
  632.  *
  633.  *----------------------------------------------------------------------
  634.  */
  635.  
  636. static void
  637. DestroyButton(clientData)
  638.     ClientData clientData;        /* Info about entry widget. */
  639. {
  640.     register Button *butPtr = (Button *) clientData;
  641.  
  642.     /*
  643.      * Free up all the stuff that requires special handling, then
  644.      * let Tk_FreeOptions handle all the standard option-related
  645.      * stuff.
  646.      */
  647.  
  648.     if (butPtr->textVarName != NULL) {
  649.     Tcl_UntraceVar(butPtr->interp, butPtr->textVarName,
  650.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  651.         ButtonTextVarProc, (ClientData) butPtr);
  652.     }
  653.     if (butPtr->normalTextGC != None) {
  654.     Tk_FreeGC(butPtr->display, butPtr->normalTextGC);
  655.     }
  656.     if (butPtr->activeTextGC != None) {
  657.     Tk_FreeGC(butPtr->display, butPtr->activeTextGC);
  658.     }
  659.     if (butPtr->gray != None) {
  660.     Tk_FreeBitmap(butPtr->display, butPtr->gray);
  661.     }
  662.     if (butPtr->disabledGC != None) {
  663.     Tk_FreeGC(butPtr->display, butPtr->disabledGC);
  664.     }
  665.     if (butPtr->selectorGC != None) {
  666.     Tk_FreeGC(butPtr->display, butPtr->selectorGC);
  667.     }
  668.     if (butPtr->selVarName != NULL) {
  669.     Tcl_UntraceVar(butPtr->interp, butPtr->selVarName,
  670.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  671.         ButtonVarProc, (ClientData) butPtr);
  672.     }
  673.     Tk_FreeOptions(configSpecs, (char *) butPtr, butPtr->display,
  674.         configFlags[butPtr->type]);
  675.     ckfree((char *) butPtr);
  676. }
  677.  
  678. /*
  679.  *----------------------------------------------------------------------
  680.  *
  681.  * ConfigureButton --
  682.  *
  683.  *    This procedure is called to process an argv/argc list, plus
  684.  *    the Tk option database, in order to configure (or
  685.  *    reconfigure) a button widget.
  686.  *
  687.  * Results:
  688.  *    The return value is a standard Tcl result.  If TCL_ERROR is
  689.  *    returned, then interp->result contains an error message.
  690.  *
  691.  * Side effects:
  692.  *    Configuration information, such as text string, colors, font,
  693.  *    etc. get set for butPtr;  old resources get freed, if there
  694.  *    were any.  The button is redisplayed.
  695.  *
  696.  *----------------------------------------------------------------------
  697.  */
  698.  
  699. static int
  700. ConfigureButton(interp, butPtr, argc, argv, flags)
  701.     Tcl_Interp *interp;        /* Used for error reporting. */
  702.     register Button *butPtr;    /* Information about widget;  may or may
  703.                  * not already have values for some fields. */
  704.     int argc;            /* Number of valid entries in argv. */
  705.     char **argv;        /* Arguments. */
  706.     int flags;            /* Flags to pass to Tk_ConfigureWidget. */
  707. {
  708.     XGCValues gcValues;
  709.     GC newGC;
  710.     unsigned long mask;
  711.  
  712.     /*
  713.      * Eliminate any existing trace on variables monitored by the button.
  714.      */
  715.  
  716.     if (butPtr->textVarName != NULL) {
  717.     Tcl_UntraceVar(interp, butPtr->textVarName, 
  718.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  719.         ButtonTextVarProc, (ClientData) butPtr);
  720.     }
  721.     if (butPtr->selVarName != NULL) {
  722.     Tcl_UntraceVar(interp, butPtr->selVarName, 
  723.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  724.         ButtonVarProc, (ClientData) butPtr);
  725.     }
  726.  
  727.     if (Tk_ConfigureWidget(interp, butPtr->tkwin, configSpecs,
  728.         argc, argv, (char *) butPtr, flags) != TCL_OK) {
  729.     return TCL_ERROR;
  730.     }
  731.  
  732.     /*
  733.      * A few options need special processing, such as setting the
  734.      * background from a 3-D border, or filling in complicated
  735.      * defaults that couldn't be specified to Tk_ConfigureWidget.
  736.      */
  737.  
  738.     if (butPtr->state == tkActiveUid) {
  739.     Tk_SetBackgroundFromBorder(butPtr->tkwin, butPtr->activeBorder);
  740.     } else {
  741.     Tk_SetBackgroundFromBorder(butPtr->tkwin, butPtr->normalBorder);
  742.     if ((butPtr->state != tkNormalUid)
  743.         && (butPtr->state != tkDisabledUid)) {
  744.         Tcl_AppendResult(interp, "bad state value \"", butPtr->state,
  745.             "\":  must be normal, active, or disabled", (char *) NULL);
  746.         butPtr->state = tkNormalUid;
  747.         return TCL_ERROR;
  748.     }
  749.     }
  750.  
  751.     gcValues.font = butPtr->fontPtr->fid;
  752.     gcValues.foreground = butPtr->normalFg->pixel;
  753.     gcValues.background = Tk_3DBorderColor(butPtr->normalBorder)->pixel;
  754.  
  755.     /*
  756.      * Note: GraphicsExpose events are disabled in normalTextGC because it's
  757.      * used to copy stuff from an off-screen pixmap onto the screen (we know
  758.      * that there's no problem with obscured areas).
  759.      */
  760.  
  761.     gcValues.graphics_exposures = False;
  762.     newGC = Tk_GetGC(butPtr->tkwin,
  763.         GCForeground|GCBackground|GCFont|GCGraphicsExposures, &gcValues);
  764.     if (butPtr->normalTextGC != None) {
  765.     Tk_FreeGC(butPtr->display, butPtr->normalTextGC);
  766.     }
  767.     butPtr->normalTextGC = newGC;
  768.  
  769.     if (butPtr->activeFg != NULL) {
  770.     gcValues.font = butPtr->fontPtr->fid;
  771.     gcValues.foreground = butPtr->activeFg->pixel;
  772.     gcValues.background = Tk_3DBorderColor(butPtr->activeBorder)->pixel;
  773.     newGC = Tk_GetGC(butPtr->tkwin, GCForeground|GCBackground|GCFont,
  774.         &gcValues);
  775.     if (butPtr->activeTextGC != None) {
  776.         Tk_FreeGC(butPtr->display, butPtr->activeTextGC);
  777.     }
  778.     butPtr->activeTextGC = newGC;
  779.     }
  780.  
  781.     gcValues.font = butPtr->fontPtr->fid;
  782.     gcValues.background = Tk_3DBorderColor(butPtr->normalBorder)->pixel;
  783.     if (butPtr->disabledFg != NULL) {
  784.     gcValues.foreground = butPtr->disabledFg->pixel;
  785.     mask = GCForeground|GCBackground|GCFont;
  786.     } else {
  787.     gcValues.foreground = gcValues.background;
  788.     if (butPtr->gray == None) {
  789.         butPtr->gray = Tk_GetBitmap(interp, butPtr->tkwin,
  790.             Tk_GetUid("gray50"));
  791.         if (butPtr->gray == None) {
  792.         return TCL_ERROR;
  793.         }
  794.     }
  795.     gcValues.fill_style = FillStippled;
  796.     gcValues.stipple = butPtr->gray;
  797.     mask = GCForeground|GCFillStyle|GCStipple;
  798.     }
  799.     newGC = Tk_GetGC(butPtr->tkwin, mask, &gcValues);
  800.     if (butPtr->disabledGC != None) {
  801.     Tk_FreeGC(butPtr->display, butPtr->disabledGC);
  802.     }
  803.     butPtr->disabledGC = newGC;
  804.  
  805.     if (butPtr->padX < 0) {
  806.     butPtr->padX = 0;
  807.     }
  808.     if (butPtr->padY < 0) {
  809.     butPtr->padY = 0;
  810.     }
  811.  
  812.     if (butPtr->type >= TYPE_CHECK_BUTTON) {
  813.     char *value;
  814.  
  815.     if (butPtr->selectorFg != NULL) {
  816.         gcValues.foreground = butPtr->selectorFg->pixel;
  817.         newGC = Tk_GetGC(butPtr->tkwin, GCForeground, &gcValues);
  818.     } else {
  819.         newGC = None;
  820.     }
  821.     if (butPtr->selectorGC != None) {
  822.         Tk_FreeGC(butPtr->display, butPtr->selectorGC);
  823.     }
  824.     butPtr->selectorGC = newGC;
  825.  
  826.     if (butPtr->selVarName == NULL) {
  827.         butPtr->selVarName = (char *) ckalloc((unsigned)
  828.             (strlen(Tk_Name(butPtr->tkwin)) + 1));
  829.         strcpy(butPtr->selVarName, Tk_Name(butPtr->tkwin));
  830.     }
  831.     if (butPtr->onValue == NULL) {
  832.         butPtr->onValue = (char *) ckalloc((unsigned)
  833.             (strlen(Tk_Name(butPtr->tkwin)) + 1));
  834.         strcpy(butPtr->onValue, Tk_Name(butPtr->tkwin));
  835.     }
  836.  
  837.     /*
  838.      * Select the button if the associated variable has the
  839.      * appropriate value, initialize the variable if it doesn't
  840.      * exist, then set a trace on the variable to monitor future
  841.      * changes to its value.
  842.      */
  843.  
  844.     value = Tcl_GetVar(interp, butPtr->selVarName, TCL_GLOBAL_ONLY);
  845.     butPtr->flags &= ~SELECTED;
  846.     if (value != NULL) {
  847.         if (strcmp(value, butPtr->onValue) == 0) {
  848.         butPtr->flags |= SELECTED;
  849.         }
  850.     } else {
  851.         Tcl_SetVar(interp, butPtr->selVarName,
  852.             (butPtr->type == TYPE_CHECK_BUTTON) ? butPtr->offValue : "",
  853.             TCL_GLOBAL_ONLY);
  854.     }
  855.     Tcl_TraceVar(interp, butPtr->selVarName,
  856.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  857.         ButtonVarProc, (ClientData) butPtr);
  858.     }
  859.  
  860.     /*
  861.      * If the button is to display the value of a variable, then set up
  862.      * a trace on the variable's value, create the variable if it doesn't
  863.      * exist, and fetch its current value.
  864.      */
  865.  
  866.     if ((butPtr->bitmap == None) && (butPtr->textVarName != NULL)) {
  867.     char *value;
  868.  
  869.     value = Tcl_GetVar(interp, butPtr->textVarName, TCL_GLOBAL_ONLY);
  870.     if (value == NULL) {
  871.         Tcl_SetVar(interp, butPtr->textVarName, butPtr->text,
  872.             TCL_GLOBAL_ONLY);
  873.     } else {
  874.         if (butPtr->text != NULL) {
  875.         ckfree(butPtr->text);
  876.         }
  877.         butPtr->text = ckalloc((unsigned) (strlen(value) + 1));
  878.         strcpy(butPtr->text, value);
  879.     }
  880.     Tcl_TraceVar(interp, butPtr->textVarName,
  881.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  882.         ButtonTextVarProc, (ClientData) butPtr);
  883.     }
  884.  
  885.     ComputeButtonGeometry(butPtr);
  886.  
  887.     /*
  888.      * Lastly, arrange for the button to be redisplayed.
  889.      */
  890.  
  891.     if (Tk_IsMapped(butPtr->tkwin) && !(butPtr->flags & REDRAW_PENDING)) {
  892.     Tk_DoWhenIdle(DisplayButton, (ClientData) butPtr);
  893.     butPtr->flags |= REDRAW_PENDING;
  894.     }
  895.  
  896.     return TCL_OK;
  897. }
  898.  
  899. /*
  900.  *----------------------------------------------------------------------
  901.  *
  902.  * DisplayButton --
  903.  *
  904.  *    This procedure is invoked to display a button widget.
  905.  *
  906.  * Results:
  907.  *    None.
  908.  *
  909.  * Side effects:
  910.  *    Commands are output to X to display the button in its
  911.  *    current mode.
  912.  *
  913.  *----------------------------------------------------------------------
  914.  */
  915.  
  916. static void
  917. DisplayButton(clientData)
  918.     ClientData clientData;    /* Information about widget. */
  919. {
  920.     register Button *butPtr = (Button *) clientData;
  921.     GC gc;
  922.     Tk_3DBorder border;
  923.     Pixmap pixmap;
  924.     int x = 0;            /* Initialization only needed to stop
  925.                  * compiler warning. */
  926.     int y;
  927.     register Tk_Window tkwin = butPtr->tkwin;
  928.  
  929.     butPtr->flags &= ~REDRAW_PENDING;
  930.     if ((butPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
  931.     return;
  932.     }
  933.  
  934.     if ((butPtr->state == tkDisabledUid) && (butPtr->disabledFg != NULL)) {
  935.     gc = butPtr->disabledGC;
  936.     border = butPtr->normalBorder;
  937.     } else if (butPtr->state == tkActiveUid) {
  938.     gc = butPtr->activeTextGC;
  939.     border = butPtr->activeBorder;
  940.     } else {
  941.     gc = butPtr->normalTextGC;
  942.     border = butPtr->normalBorder;
  943.     }
  944.  
  945.     /*
  946.      * In order to avoid screen flashes, this procedure redraws
  947.      * the button in a pixmap, then copies the pixmap to the
  948.      * screen in a single operation.  This means that there's no
  949.      * point in time where the on-sreen image has been cleared.
  950.      */
  951.  
  952.     pixmap = XCreatePixmap(butPtr->display, Tk_WindowId(tkwin),
  953.         Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin));
  954.     Tk_Fill3DRectangle(butPtr->display, pixmap, border,
  955.         0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 0, TK_RELIEF_FLAT);
  956.  
  957.     /*
  958.      * Display bitmap or text for button.
  959.      */
  960.  
  961.     if (butPtr->bitmap != None) {
  962.     unsigned int width, height;
  963.  
  964.     Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height);
  965.     switch (butPtr->anchor) {
  966.         case TK_ANCHOR_NW: case TK_ANCHOR_W: case TK_ANCHOR_SW:
  967.         x = butPtr->borderWidth + butPtr->selectorSpace
  968.             + butPtr->padX + 1;
  969.         break;
  970.         case TK_ANCHOR_N: case TK_ANCHOR_CENTER: case TK_ANCHOR_S:
  971.         x = (Tk_Width(tkwin) + butPtr->selectorSpace - width)/2;
  972.         break;
  973.         default:
  974.         x = Tk_Width(tkwin) - butPtr->borderWidth - butPtr->padX
  975.             - width - 1;
  976.         break;
  977.     }
  978.     switch (butPtr->anchor) {
  979.         case TK_ANCHOR_NW: case TK_ANCHOR_N: case TK_ANCHOR_NE:
  980.         y = butPtr->borderWidth + butPtr->padY + 1;
  981.         break;
  982.         case TK_ANCHOR_W: case TK_ANCHOR_CENTER: case TK_ANCHOR_E:
  983.         y = (Tk_Height(tkwin) - height)/2;
  984.         break;
  985.         default:
  986.         y = Tk_Height(tkwin) - butPtr->borderWidth - butPtr->padY
  987.             - height - 1;
  988.         break;
  989.     }
  990.     if (butPtr->relief == TK_RELIEF_RAISED) {
  991.         x -= 1;
  992.         y -= 1;
  993.     } else if (butPtr->relief == TK_RELIEF_SUNKEN) {
  994.         x += 1;
  995.         y += 1;
  996.     }
  997.     XCopyPlane(butPtr->display, butPtr->bitmap, pixmap,
  998.         gc, 0, 0, width, height, x, y, 1);
  999.     y += height/2;
  1000.     } else {
  1001.     switch (butPtr->anchor) {
  1002.         case TK_ANCHOR_NW: case TK_ANCHOR_W: case TK_ANCHOR_SW:
  1003.         x = butPtr->borderWidth + butPtr->padX + butPtr->selectorSpace
  1004.             - butPtr->leftBearing + 1;
  1005.         break;
  1006.         case TK_ANCHOR_N: case TK_ANCHOR_CENTER: case TK_ANCHOR_S:
  1007.         x = (Tk_Width(tkwin) + butPtr->selectorSpace
  1008.             - butPtr->leftBearing - butPtr->rightBearing)/2;
  1009.         break;
  1010.         default:
  1011.         x = Tk_Width(tkwin) - butPtr->borderWidth - butPtr->padX
  1012.             - butPtr->rightBearing - 1;
  1013.         break;
  1014.     }
  1015.     switch (butPtr->anchor) {
  1016.         case TK_ANCHOR_NW: case TK_ANCHOR_N: case TK_ANCHOR_NE:
  1017.         y = butPtr->borderWidth + butPtr->fontPtr->ascent
  1018.             + butPtr->padY + 1;
  1019.         break;
  1020.         case TK_ANCHOR_W: case TK_ANCHOR_CENTER: case TK_ANCHOR_E:
  1021.         y = (Tk_Height(tkwin) + butPtr->fontPtr->ascent
  1022.             - butPtr->fontPtr->descent)/2;
  1023.         break;
  1024.         default:
  1025.         y = Tk_Height(tkwin) - butPtr->borderWidth - butPtr->padY
  1026.             - butPtr->fontPtr->descent - 1;
  1027.         break;
  1028.     }
  1029.     if (butPtr->relief == TK_RELIEF_RAISED) {
  1030.         x -= 1;
  1031.         y -= 1;
  1032.     } else if (butPtr->relief == TK_RELIEF_SUNKEN) {
  1033.         x += 1;
  1034.         y += 1;
  1035.     }
  1036.     XDrawString(butPtr->display, pixmap, gc, x, y,
  1037.         butPtr->text, butPtr->textLength);
  1038.     y -= (butPtr->fontPtr->ascent - butPtr->fontPtr->descent)/2;
  1039.     x += butPtr->leftBearing;
  1040.     }
  1041.  
  1042.     /*
  1043.      * Draw the selector for check buttons and radio buttons.  At this
  1044.      * point x and y refer to the top-left corner of the text or bitmap.
  1045.      */
  1046.  
  1047.     if ((butPtr->type == TYPE_CHECK_BUTTON) && (butPtr->selectorGC != None)) {
  1048.     int dim;
  1049.  
  1050.     dim = butPtr->selectorDiameter;
  1051.     x -= butPtr->selectorSpace;
  1052.     y -= dim/2;
  1053.     Tk_Draw3DRectangle(butPtr->display, pixmap, border, x, y,
  1054.         dim, dim, butPtr->borderWidth, TK_RELIEF_SUNKEN);
  1055.     x += butPtr->borderWidth;
  1056.     y += butPtr->borderWidth;
  1057.     dim -= 2*butPtr->borderWidth;
  1058.     if (dim > 0) {
  1059.         if (butPtr->flags & SELECTED) {
  1060.         XFillRectangle(butPtr->display, pixmap, butPtr->selectorGC,
  1061.             x, y, (unsigned int) dim, (unsigned int) dim);
  1062.         } else {
  1063.         Tk_Fill3DRectangle(butPtr->display, pixmap,
  1064.             butPtr->normalBorder, x, y, dim, dim,
  1065.             butPtr->borderWidth, TK_RELIEF_FLAT);
  1066.         }
  1067.     }
  1068.     } else if ((butPtr->type == TYPE_RADIO_BUTTON)
  1069.         && (butPtr->selectorGC != None)) {
  1070.     XPoint points[4];
  1071.     int radius;
  1072.  
  1073.     radius = butPtr->selectorDiameter/2;
  1074.     points[0].x = x - butPtr->selectorSpace;
  1075.     points[0].y = y;
  1076.     points[1].x = points[0].x + radius;
  1077.     points[1].y = points[0].y + radius;
  1078.     points[2].x = points[1].x + radius;
  1079.     points[2].y = points[0].y;
  1080.     points[3].x = points[1].x;
  1081.     points[3].y = points[0].y - radius;
  1082.     if (butPtr->flags & SELECTED) {
  1083.         XFillPolygon(butPtr->display, pixmap, butPtr->selectorGC,
  1084.             points, 4, Convex, CoordModeOrigin);
  1085.     } else {
  1086.         Tk_Fill3DPolygon(butPtr->display, pixmap, butPtr->normalBorder,
  1087.             points, 4, butPtr->borderWidth, TK_RELIEF_FLAT);
  1088.     }
  1089.     Tk_Draw3DPolygon(butPtr->display, pixmap, border,
  1090.         points, 4, butPtr->borderWidth, TK_RELIEF_RAISED);
  1091.     }
  1092.  
  1093.     /*
  1094.      * If the button is disabled with a stipple rather than a special
  1095.      * foreground color, generate the stippled effect.
  1096.      */
  1097.  
  1098.     if ((butPtr->state == tkDisabledUid) && (butPtr->disabledFg == NULL)) {
  1099.     XFillRectangle(butPtr->display, pixmap, butPtr->disabledGC,
  1100.         butPtr->borderWidth, butPtr->borderWidth,
  1101.         (unsigned) (Tk_Width(tkwin) - 2*butPtr->borderWidth),
  1102.         (unsigned) (Tk_Height(tkwin) - 2*butPtr->borderWidth));
  1103.     }
  1104.  
  1105.     /*
  1106.      * Draw the border last.  This way, if the button's contents
  1107.      * overflow onto the border they'll be covered up by the border.
  1108.      */
  1109.  
  1110.     if (butPtr->relief != TK_RELIEF_FLAT) {
  1111.     Tk_Draw3DRectangle(butPtr->display, pixmap, border,0, 0,
  1112.         Tk_Width(tkwin), Tk_Height(tkwin), butPtr->borderWidth,
  1113.         butPtr->relief);
  1114.     }
  1115.  
  1116.     /*
  1117.      * Copy the information from the off-screen pixmap onto the screen,
  1118.      * then delete the pixmap.
  1119.      */
  1120.  
  1121.     XCopyArea(butPtr->display, pixmap, Tk_WindowId(tkwin),
  1122.     butPtr->normalTextGC, 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 0, 0);
  1123.     XFreePixmap(butPtr->display, pixmap);
  1124. }
  1125.  
  1126. /*
  1127.  *--------------------------------------------------------------
  1128.  *
  1129.  * ButtonEventProc --
  1130.  *
  1131.  *    This procedure is invoked by the Tk dispatcher for various
  1132.  *    events on buttons.
  1133.  *
  1134.  * Results:
  1135.  *    None.
  1136.  *
  1137.  * Side effects:
  1138.  *    When the window gets deleted, internal structures get
  1139.  *    cleaned up.  When it gets exposed, it is redisplayed.
  1140.  *
  1141.  *--------------------------------------------------------------
  1142.  */
  1143.  
  1144. static void
  1145. ButtonEventProc(clientData, eventPtr)
  1146.     ClientData clientData;    /* Information about window. */
  1147.     XEvent *eventPtr;        /* Information about event. */
  1148. {
  1149.     Button *butPtr = (Button *) clientData;
  1150.     if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
  1151.     if ((butPtr->tkwin != NULL) && !(butPtr->flags & REDRAW_PENDING)) {
  1152.         Tk_DoWhenIdle(DisplayButton, (ClientData) butPtr);
  1153.         butPtr->flags |= REDRAW_PENDING;
  1154.     }
  1155.     } else if (eventPtr->type == DestroyNotify) {
  1156.     Tcl_DeleteCommand(butPtr->interp, Tk_PathName(butPtr->tkwin));
  1157.     butPtr->tkwin = NULL;
  1158.     if (butPtr->flags & REDRAW_PENDING) {
  1159.         Tk_CancelIdleCall(DisplayButton, (ClientData) butPtr);
  1160.     }
  1161.     Tk_EventuallyFree((ClientData) butPtr, DestroyButton);
  1162.     }
  1163. }
  1164.  
  1165. /*
  1166.  *----------------------------------------------------------------------
  1167.  *
  1168.  * ComputeButtonGeometry --
  1169.  *
  1170.  *    After changes in a button's text or bitmap, this procedure
  1171.  *    recomputes the button's geometry and passes this information
  1172.  *    along to the geometry manager for the window.
  1173.  *
  1174.  * Results:
  1175.  *    None.
  1176.  *
  1177.  * Side effects:
  1178.  *    The button's window may change size.
  1179.  *
  1180.  *----------------------------------------------------------------------
  1181.  */
  1182.  
  1183. static void
  1184. ComputeButtonGeometry(butPtr)
  1185.     register Button *butPtr;    /* Button whose geometry may have changed. */
  1186. {
  1187.     XCharStruct bbox;
  1188.     int dummy;
  1189.     unsigned int width, height;
  1190.  
  1191.     butPtr->selectorSpace = 0;
  1192.     if (butPtr->bitmap != None) {
  1193.     Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height);
  1194.     if (butPtr->width > 0) {
  1195.         width = butPtr->width;
  1196.     }
  1197.     if (butPtr->height > 0) {
  1198.         height = butPtr->height;
  1199.     }
  1200.     if ((butPtr->type >= TYPE_CHECK_BUTTON)
  1201.         && (butPtr->selectorGC != None)) {
  1202.         butPtr->selectorSpace = height;
  1203.         if (butPtr->type == TYPE_CHECK_BUTTON) {
  1204.         butPtr->selectorDiameter = (65*height)/100;
  1205.         } else {
  1206.         butPtr->selectorDiameter = (75*height)/100;
  1207.         }
  1208.     }
  1209.     } else {
  1210.     butPtr->textLength = strlen(butPtr->text);
  1211.     XTextExtents(butPtr->fontPtr, butPtr->text, butPtr->textLength,
  1212.         &dummy, &dummy, &dummy, &bbox);
  1213.     butPtr->leftBearing = bbox.lbearing;
  1214.     butPtr->rightBearing = bbox.rbearing;
  1215.     width = bbox.rbearing - bbox.lbearing;
  1216.     height = butPtr->fontPtr->ascent + butPtr->fontPtr->descent;
  1217.     if (butPtr->width > 0) {
  1218.         width = butPtr->width * XTextWidth(butPtr->fontPtr, "0", 1);
  1219.     }
  1220.     if (butPtr->height > 0) {
  1221.         height *= butPtr->height;
  1222.     }
  1223.     if ((butPtr->type >= TYPE_CHECK_BUTTON)
  1224.         && (butPtr->selectorGC != None)) {
  1225.         butPtr->selectorDiameter = butPtr->fontPtr->ascent
  1226.             + butPtr->fontPtr->descent;
  1227.         if (butPtr->type == TYPE_CHECK_BUTTON) {
  1228.         butPtr->selectorDiameter = (80*butPtr->selectorDiameter)/100;
  1229.         }
  1230.         butPtr->selectorSpace = butPtr->selectorDiameter
  1231.         + XTextWidth(butPtr->fontPtr, "0", 1);
  1232.     }
  1233.     }
  1234.  
  1235.     /*
  1236.      * When issuing the geometry request, add extra space for the selector,
  1237.      * if any, and for the border and padding, plus two extra pixels so the
  1238.      * display can be offset by 1 pixel in either direction for the raised
  1239.      * or lowered effect.
  1240.      */
  1241.  
  1242.     width += 2*butPtr->padX;
  1243.     height += 2*butPtr->padY;
  1244.     Tk_GeometryRequest(butPtr->tkwin, (int) (width + butPtr->selectorSpace
  1245.         + 2*butPtr->borderWidth + 2),
  1246.         (int) (height + 2*butPtr->borderWidth + 2));
  1247.     Tk_SetInternalBorder(butPtr->tkwin, butPtr->borderWidth);
  1248. }
  1249.  
  1250. /*
  1251.  *----------------------------------------------------------------------
  1252.  *
  1253.  * InvokeButton --
  1254.  *
  1255.  *    This procedure is called to carry out the actions associated
  1256.  *    with a button, such as invoking a Tcl command or setting a
  1257.  *    variable.  This procedure is invoked, for example, when the
  1258.  *    button is invoked via the mouse.
  1259.  *
  1260.  * Results:
  1261.  *    A standard Tcl return value.  Information is also left in
  1262.  *    interp->result.
  1263.  *
  1264.  * Side effects:
  1265.  *    Depends on the button and its associated command.
  1266.  *
  1267.  *----------------------------------------------------------------------
  1268.  */
  1269.  
  1270. static int
  1271. InvokeButton(butPtr)
  1272.     register Button *butPtr;        /* Information about button. */
  1273. {
  1274.     if (butPtr->type == TYPE_CHECK_BUTTON) {
  1275.     if (butPtr->flags & SELECTED) {
  1276.         Tcl_SetVar(butPtr->interp, butPtr->selVarName, butPtr->offValue,
  1277.             TCL_GLOBAL_ONLY);
  1278.     } else {
  1279.         Tcl_SetVar(butPtr->interp, butPtr->selVarName, butPtr->onValue,
  1280.             TCL_GLOBAL_ONLY);
  1281.     }
  1282.     } else if (butPtr->type == TYPE_RADIO_BUTTON) {
  1283.     Tcl_SetVar(butPtr->interp, butPtr->selVarName, butPtr->onValue,
  1284.         TCL_GLOBAL_ONLY);
  1285.     }
  1286.     if ((butPtr->type != TYPE_LABEL) && (butPtr->command != NULL)) {
  1287.     return TkCopyAndGlobalEval(butPtr->interp, butPtr->command);
  1288.     }
  1289.     return TCL_OK;
  1290. }
  1291.  
  1292. /*
  1293.  *--------------------------------------------------------------
  1294.  *
  1295.  * ButtonVarProc --
  1296.  *
  1297.  *    This procedure is invoked when someone changes the
  1298.  *    state variable associated with a radio button.  Depending
  1299.  *    on the new value of the button's variable, the button
  1300.  *    may be selected or deselected.
  1301.  *
  1302.  * Results:
  1303.  *    NULL is always returned.
  1304.  *
  1305.  * Side effects:
  1306.  *    The button may become selected or deselected.
  1307.  *
  1308.  *--------------------------------------------------------------
  1309.  */
  1310.  
  1311.     /* ARGSUSED */
  1312. static char *
  1313. ButtonVarProc(clientData, interp, name1, name2, flags)
  1314.     ClientData clientData;    /* Information about button. */
  1315.     Tcl_Interp *interp;        /* Interpreter containing variable. */
  1316.     char *name1;        /* Name of variable. */
  1317.     char *name2;        /* Second part of variable name. */
  1318.     int flags;            /* Information about what happened. */
  1319. {
  1320.     register Button *butPtr = (Button *) clientData;
  1321.     char *value;
  1322.  
  1323.     /*
  1324.      * If the variable is being unset, then just re-establish the
  1325.      * trace unless the whole interpreter is going away.
  1326.      */
  1327.  
  1328.     if (flags & TCL_TRACE_UNSETS) {
  1329.     butPtr->flags &= ~SELECTED;
  1330.     if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
  1331.         Tcl_TraceVar2(interp, name1, name2,
  1332.             TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  1333.             ButtonVarProc, clientData);
  1334.     }
  1335.     goto redisplay;
  1336.     }
  1337.  
  1338.     /*
  1339.      * Use the value of the variable to update the selected status of
  1340.      * the button.
  1341.      */
  1342.  
  1343.     value = Tcl_GetVar2(interp, name1, name2, flags & TCL_GLOBAL_ONLY);
  1344.     if (strcmp(value, butPtr->onValue) == 0) {
  1345.     if (butPtr->flags & SELECTED) {
  1346.         return (char *) NULL;
  1347.     }
  1348.     butPtr->flags |= SELECTED;
  1349.     } else if (butPtr->flags & SELECTED) {
  1350.     butPtr->flags &= ~SELECTED;
  1351.     } else {
  1352.     return (char *) NULL;
  1353.     }
  1354.  
  1355.     redisplay:
  1356.     if ((butPtr->tkwin != NULL) && Tk_IsMapped(butPtr->tkwin)
  1357.         && !(butPtr->flags & REDRAW_PENDING)) {
  1358.     Tk_DoWhenIdle(DisplayButton, (ClientData) butPtr);
  1359.     butPtr->flags |= REDRAW_PENDING;
  1360.     }
  1361.     return (char *) NULL;
  1362. }
  1363.  
  1364. /*
  1365.  *--------------------------------------------------------------
  1366.  *
  1367.  * ButtonTextVarProc --
  1368.  *
  1369.  *    This procedure is invoked when someone changes the variable
  1370.  *    whose contents are to be displayed in a button.
  1371.  *
  1372.  * Results:
  1373.  *    NULL is always returned.
  1374.  *
  1375.  * Side effects:
  1376.  *    The text displayed in the button will change to match the
  1377.  *    variable.
  1378.  *
  1379.  *--------------------------------------------------------------
  1380.  */
  1381.  
  1382.     /* ARGSUSED */
  1383. static char *
  1384. ButtonTextVarProc(clientData, interp, name1, name2, flags)
  1385.     ClientData clientData;    /* Information about button. */
  1386.     Tcl_Interp *interp;        /* Interpreter containing variable. */
  1387.     char *name1;        /* Name of variable. */
  1388.     char *name2;        /* Second part of variable name. */
  1389.     int flags;            /* Information about what happened. */
  1390. {
  1391.     register Button *butPtr = (Button *) clientData;
  1392.     char *value;
  1393.  
  1394.     /*
  1395.      * If the variable is unset, then immediately recreate it unless
  1396.      * the whole interpreter is going away.
  1397.      */
  1398.  
  1399.     if (flags & TCL_TRACE_UNSETS) {
  1400.     if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
  1401.         Tcl_SetVar2(interp, name1, name2, butPtr->text,
  1402.             flags & TCL_GLOBAL_ONLY);
  1403.         Tcl_TraceVar2(interp, name1, name2,
  1404.             TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  1405.             ButtonTextVarProc, clientData);
  1406.     }
  1407.     return (char *) NULL;
  1408.     }
  1409.  
  1410.     value = Tcl_GetVar2(interp, name1, name2, flags & TCL_GLOBAL_ONLY);
  1411.     if (value == NULL) {
  1412.     value = "";
  1413.     }
  1414.     if (butPtr->text != NULL) {
  1415.     ckfree(butPtr->text);
  1416.     }
  1417.     butPtr->text = ckalloc((unsigned) (strlen(value) + 1));
  1418.     strcpy(butPtr->text, value);
  1419.     ComputeButtonGeometry(butPtr);
  1420.  
  1421.     if ((butPtr->tkwin != NULL) && Tk_IsMapped(butPtr->tkwin)
  1422.         && !(butPtr->flags & REDRAW_PENDING)) {
  1423.     Tk_DoWhenIdle(DisplayButton, (ClientData) butPtr);
  1424.     butPtr->flags |= REDRAW_PENDING;
  1425.     }
  1426.     return (char *) NULL;
  1427. }
  1428.