home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / tkisrc04.zip / tk / os2 / tkButton.c < prev    next >
C/C++ Source or Header  |  1998-08-07  |  61KB  |  1,872 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-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.128 96/03/01 17:34:49
  16.  */
  17.  
  18. #include "default.h"
  19. #include "tkPort.h"
  20. #include "tkInt.h"
  21.  
  22. /*
  23.  * A data structure of the following type is kept for each
  24.  * widget managed by this file:
  25.  */
  26.  
  27. typedef struct {
  28.     Tk_Window tkwin;        /* Window that embodies the button.  NULL
  29.                  * means that the window has been destroyed. */
  30.     Display *display;        /* Display containing widget.  Needed to
  31.                  * free up resources after tkwin is gone. */
  32.     Tcl_Interp *interp;        /* Interpreter associated with button. */
  33.     Tcl_Command widgetCmd;    /* Token for button's widget command. */
  34.     int type;            /* Type of widget:  restricts operations
  35.                  * that may be performed on widget.  See
  36.                  * below for possible values. */
  37.  
  38.     /*
  39.      * Information about what's in the button.
  40.      */
  41.  
  42.     char *text;            /* Text to display in button (malloc'ed)
  43.                  * or NULL. */
  44.     int numChars;        /* # of characters in text. */
  45.     int underline;        /* Index of character to underline.  < 0 means
  46.                  * don't underline anything. */
  47.     char *textVarName;        /* Name of variable (malloc'ed) or NULL.
  48.                  * If non-NULL, button displays the contents
  49.                  * of this variable. */
  50.     Pixmap bitmap;        /* Bitmap to display or None.  If not None
  51.                  * then text and textVar are ignored. */
  52.     char *imageString;        /* Name of image to display (malloc'ed), or
  53.                  * NULL.  If non-NULL, bitmap, text, and
  54.                  * textVarName are ignored. */
  55.     Tk_Image image;        /* Image to display in window, or NULL if
  56.                  * none. */
  57.     char *selectImageString;    /* Name of image to display when selected
  58.                  * (malloc'ed), or NULL. */
  59.     Tk_Image selectImage;    /* Image to display in window when selected,
  60.                  * or NULL if none.  Ignored if image is
  61.                  * NULL. */
  62.  
  63.     /*
  64.      * Information used when displaying widget:
  65.      */
  66.  
  67.     Tk_Uid state;        /* State of button for display purposes:
  68.                  * normal, active, or disabled. */
  69.     Tk_3DBorder normalBorder;    /* Structure used to draw 3-D
  70.                  * border and background when window
  71.                  * isn't active.  NULL means no such
  72.                  * border exists. */
  73.     Tk_3DBorder activeBorder;    /* Structure used to draw 3-D
  74.                  * border and background when window
  75.                  * is active.  NULL means no such
  76.                  * border exists. */
  77.     int borderWidth;        /* Width of border. */
  78.     int relief;            /* 3-d effect: TK_RELIEF_RAISED, etc. */
  79.     int highlightWidth;        /* Width in pixels of highlight to draw
  80.                  * around widget when it has the focus.
  81.                  * <= 0 means don't draw a highlight. */
  82.     XColor *highlightBgColorPtr;
  83.                 /* Color for drawing traversal highlight
  84.                  * area when highlight is off. */
  85.     XColor *highlightColorPtr;    /* Color for drawing traversal highlight. */
  86.     int inset;            /* Total width of all borders, including
  87.                  * traversal highlight and 3-D border.
  88.                  * Indicates how much interior stuff must
  89.                  * be offset from outside edges to leave
  90.                  * room for borders. */
  91.     XFontStruct *fontPtr;    /* Information about text font, or NULL. */
  92.     XColor *normalFg;        /* Foreground color in normal mode. */
  93.     XColor *activeFg;        /* Foreground color in active mode.  NULL
  94.                  * means use normalFg instead. */
  95.     XColor *disabledFg;        /* Foreground color when disabled.  NULL
  96.                  * means use normalFg with a 50% stipple
  97.                  * instead. */
  98.     GC normalTextGC;        /* GC for drawing text in normal mode.  Also
  99.                  * used to copy from off-screen pixmap onto
  100.                  * screen. */
  101.     GC activeTextGC;        /* GC for drawing text in active mode (NULL
  102.                  * means use normalTextGC). */
  103.     Pixmap gray;        /* Pixmap for displaying disabled text if
  104.                  * disabledFg is NULL. */
  105.     GC disabledGC;        /* Used to produce disabled effect.  If
  106.                  * disabledFg isn't NULL, this GC is used to
  107.                  * draw button text or icon.  Otherwise
  108.                  * text or icon is drawn with normalGC and
  109.                  * this GC is used to stipple background
  110.                  * across it.  For labels this is None. */
  111.     GC copyGC;            /* Used for copying information from an
  112.                  * off-screen pixmap to the screen. */
  113.     char *widthString;        /* Value of -width option.  Malloc'ed. */
  114.     char *heightString;        /* Value of -height option.  Malloc'ed. */
  115.     int width, height;        /* If > 0, these specify dimensions to request
  116.                  * for window, in characters for text and in
  117.                  * pixels for bitmaps.  In this case the actual
  118.                  * size of the text string or bitmap is
  119.                  * ignored in computing desired window size. */
  120.     int wrapLength;        /* Line length (in pixels) at which to wrap
  121.                  * onto next line.  <= 0 means don't wrap
  122.                  * except at newlines. */
  123.     int padX, padY;        /* Extra space around text (pixels to leave
  124.                  * on each side).  Ignored for bitmaps and
  125.                  * images. */
  126.     Tk_Anchor anchor;        /* Where text/bitmap should be displayed
  127.                  * inside button region. */
  128.     Tk_Justify justify;        /* Justification to use for multi-line text. */
  129.     int indicatorOn;        /* True means draw indicator, false means
  130.                  * don't draw it. */
  131.     Tk_3DBorder selectBorder;    /* For drawing indicator background, or perhaps
  132.                  * widget background, when selected. */
  133.     int textWidth;        /* Width needed to display text as requested,
  134.                  * in pixels. */
  135.     int textHeight;        /* Height needed to display text as requested,
  136.                  * in pixels. */
  137.     int indicatorSpace;        /* Horizontal space (in pixels) allocated for
  138.                  * display of indicator. */
  139.     int indicatorDiameter;    /* Diameter of indicator, in pixels. */
  140.  
  141.     /*
  142.      * For check and radio buttons, the fields below are used
  143.      * to manage the variable indicating the button's state.
  144.      */
  145.  
  146.     char *selVarName;        /* Name of variable used to control selected
  147.                  * state of button.  Malloc'ed (if
  148.                  * not NULL). */
  149.     char *onValue;        /* Value to store in variable when
  150.                  * this button is selected.  Malloc'ed (if
  151.                  * not NULL). */
  152.     char *offValue;        /* Value to store in variable when this
  153.                  * button isn't selected.  Malloc'ed
  154.                  * (if not NULL).  Valid only for check
  155.                  * buttons. */
  156.  
  157.     /*
  158.      * Miscellaneous information:
  159.      */
  160.  
  161.     Tk_Cursor cursor;        /* Current cursor for window, or None. */
  162.     char *takeFocus;        /* Value of -takefocus option;  not used in
  163.                  * the C code, but used by keyboard traversal
  164.                  * scripts.  Malloc'ed, but may be NULL. */
  165.     char *command;        /* Command to execute when button is
  166.                  * invoked; valid for buttons only.
  167.                  * If not NULL, it's malloc-ed. */
  168.     int flags;            /* Various flags;  see below for
  169.                  * definitions. */
  170. } Button;
  171.  
  172. /*
  173.  * Possible "type" values for buttons.  These are the kinds of
  174.  * widgets supported by this file.  The ordering of the type
  175.  * numbers is significant:  greater means more features and is
  176.  * used in the code.
  177.  */
  178.  
  179. #define TYPE_LABEL        0
  180. #define TYPE_BUTTON        1
  181. #define TYPE_CHECK_BUTTON    2
  182. #define TYPE_RADIO_BUTTON    3
  183.  
  184. /*
  185.  * Class names for buttons, indexed by one of the type values above.
  186.  */
  187.  
  188. static char *classNames[] = {"Label", "Button", "Checkbutton", "Radiobutton"};
  189.  
  190. /*
  191.  * Flag bits for buttons:
  192.  *
  193.  * REDRAW_PENDING:        Non-zero means a DoWhenIdle handler
  194.  *                has already been queued to redraw
  195.  *                this window.
  196.  * SELECTED:            Non-zero means this button is selected,
  197.  *                so special highlight should be drawn.
  198.  * GOT_FOCUS:            Non-zero means this button currently
  199.  *                has the input focus.
  200.  */
  201.  
  202. #define REDRAW_PENDING        1
  203. #define SELECTED        2
  204. #define GOT_FOCUS        4
  205.  
  206. /*
  207.  * Mask values used to selectively enable entries in the
  208.  * configuration specs:
  209.  */
  210.  
  211. #define LABEL_MASK        TK_CONFIG_USER_BIT
  212. #define BUTTON_MASK        TK_CONFIG_USER_BIT << 1
  213. #define CHECK_BUTTON_MASK    TK_CONFIG_USER_BIT << 2
  214. #define RADIO_BUTTON_MASK    TK_CONFIG_USER_BIT << 3
  215. #define ALL_MASK        (LABEL_MASK | BUTTON_MASK \
  216.     | CHECK_BUTTON_MASK | RADIO_BUTTON_MASK)
  217.  
  218. static int configFlags[] = {LABEL_MASK, BUTTON_MASK,
  219.     CHECK_BUTTON_MASK, RADIO_BUTTON_MASK};
  220.  
  221. /*
  222.  * Information used for parsing configuration specs:
  223.  */
  224.  
  225. static Tk_ConfigSpec configSpecs[] = {
  226.     {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
  227.     DEF_BUTTON_ACTIVE_BG_COLOR, Tk_Offset(Button, activeBorder),
  228.     BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK
  229.     |TK_CONFIG_COLOR_ONLY},
  230.     {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
  231.     DEF_BUTTON_ACTIVE_BG_MONO, Tk_Offset(Button, activeBorder),
  232.     BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK
  233.     |TK_CONFIG_MONO_ONLY},
  234.     {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
  235.     DEF_BUTTON_ACTIVE_FG_COLOR, Tk_Offset(Button, activeFg), 
  236.     BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK
  237.     |TK_CONFIG_COLOR_ONLY},
  238.     {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
  239.     DEF_BUTTON_ACTIVE_FG_MONO, Tk_Offset(Button, activeFg), 
  240.     BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK
  241.     |TK_CONFIG_MONO_ONLY},
  242.     {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor",
  243.     DEF_BUTTON_ANCHOR, Tk_Offset(Button, anchor), ALL_MASK},
  244.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  245.     DEF_BUTTON_BG_COLOR, Tk_Offset(Button, normalBorder),
  246.     ALL_MASK | TK_CONFIG_COLOR_ONLY},
  247.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  248.     DEF_BUTTON_BG_MONO, Tk_Offset(Button, normalBorder),
  249.     ALL_MASK | TK_CONFIG_MONO_ONLY},
  250.     {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
  251.     (char *) NULL, 0, ALL_MASK},
  252.     {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
  253.     (char *) NULL, 0, ALL_MASK},
  254.     {TK_CONFIG_BITMAP, "-bitmap", "bitmap", "Bitmap",
  255.     DEF_BUTTON_BITMAP, Tk_Offset(Button, bitmap),
  256.     ALL_MASK|TK_CONFIG_NULL_OK},
  257.     {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
  258.     DEF_BUTTON_BORDER_WIDTH, Tk_Offset(Button, borderWidth), ALL_MASK},
  259.     {TK_CONFIG_STRING, "-command", "command", "Command",
  260.     DEF_BUTTON_COMMAND, Tk_Offset(Button, command),
  261.     BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|TK_CONFIG_NULL_OK},
  262.     {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
  263.     DEF_BUTTON_CURSOR, Tk_Offset(Button, cursor),
  264.     ALL_MASK|TK_CONFIG_NULL_OK},
  265.     {TK_CONFIG_COLOR, "-disabledforeground", "disabledForeground",
  266.     "DisabledForeground", DEF_BUTTON_DISABLED_FG_COLOR,
  267.     Tk_Offset(Button, disabledFg), BUTTON_MASK|CHECK_BUTTON_MASK
  268.     |RADIO_BUTTON_MASK|TK_CONFIG_COLOR_ONLY|TK_CONFIG_NULL_OK},
  269.     {TK_CONFIG_COLOR, "-disabledforeground", "disabledForeground",
  270.     "DisabledForeground", DEF_BUTTON_DISABLED_FG_MONO,
  271.     Tk_Offset(Button, disabledFg), BUTTON_MASK|CHECK_BUTTON_MASK
  272.     |RADIO_BUTTON_MASK|TK_CONFIG_MONO_ONLY|TK_CONFIG_NULL_OK},
  273.     {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
  274.     (char *) NULL, 0, ALL_MASK},
  275.     {TK_CONFIG_FONT, "-font", "font", "Font",
  276.     DEF_BUTTON_FONT, Tk_Offset(Button, fontPtr),
  277.     ALL_MASK},
  278.     {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
  279.     DEF_BUTTON_FG, Tk_Offset(Button, normalFg), ALL_MASK},
  280.     {TK_CONFIG_STRING, "-height", "height", "Height",
  281.     DEF_BUTTON_HEIGHT, Tk_Offset(Button, heightString), ALL_MASK},
  282.     {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
  283.     "HighlightBackground", DEF_BUTTON_HIGHLIGHT_BG,
  284.     Tk_Offset(Button, highlightBgColorPtr), ALL_MASK},
  285.     {TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
  286.     DEF_BUTTON_HIGHLIGHT, Tk_Offset(Button, highlightColorPtr),
  287.     ALL_MASK},
  288.     {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
  289.     "HighlightThickness",
  290.     DEF_LABEL_HIGHLIGHT_WIDTH, Tk_Offset(Button, highlightWidth),
  291.     LABEL_MASK},
  292.     {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
  293.     "HighlightThickness",
  294.     DEF_BUTTON_HIGHLIGHT_WIDTH, Tk_Offset(Button, highlightWidth),
  295.     BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
  296.     {TK_CONFIG_STRING, "-image", "image", "Image",
  297.     DEF_BUTTON_IMAGE, Tk_Offset(Button, imageString),
  298.     ALL_MASK|TK_CONFIG_NULL_OK},
  299.     {TK_CONFIG_BOOLEAN, "-indicatoron", "indicatorOn", "IndicatorOn",
  300.     DEF_BUTTON_INDICATOR, Tk_Offset(Button, indicatorOn),
  301.     CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
  302.     {TK_CONFIG_JUSTIFY, "-justify", "justify", "Justify",
  303.     DEF_BUTTON_JUSTIFY, Tk_Offset(Button, justify), ALL_MASK},
  304.     {TK_CONFIG_STRING, "-offvalue", "offValue", "Value",
  305.     DEF_BUTTON_OFF_VALUE, Tk_Offset(Button, offValue),
  306.     CHECK_BUTTON_MASK},
  307.     {TK_CONFIG_STRING, "-onvalue", "onValue", "Value",
  308.     DEF_BUTTON_ON_VALUE, Tk_Offset(Button, onValue),
  309.     CHECK_BUTTON_MASK},
  310.     {TK_CONFIG_PIXELS, "-padx", "padX", "Pad",
  311.     DEF_BUTTON_PADX, Tk_Offset(Button, padX), BUTTON_MASK},
  312.     {TK_CONFIG_PIXELS, "-padx", "padX", "Pad",
  313.     DEF_LABCHKRAD_PADX, Tk_Offset(Button, padX),
  314.     LABEL_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
  315.     {TK_CONFIG_PIXELS, "-pady", "padY", "Pad",
  316.     DEF_BUTTON_PADY, Tk_Offset(Button, padY), BUTTON_MASK},
  317.     {TK_CONFIG_PIXELS, "-pady", "padY", "Pad",
  318.     DEF_LABCHKRAD_PADY, Tk_Offset(Button, padY),
  319.     LABEL_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
  320.     {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
  321.     DEF_BUTTON_RELIEF, Tk_Offset(Button, relief), BUTTON_MASK},
  322.     {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
  323.     DEF_LABCHKRAD_RELIEF, Tk_Offset(Button, relief),
  324.     LABEL_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
  325.     {TK_CONFIG_BORDER, "-selectcolor", "selectColor", "Background",
  326.     DEF_BUTTON_SELECT_COLOR, Tk_Offset(Button, selectBorder),
  327.     CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|TK_CONFIG_COLOR_ONLY
  328.     |TK_CONFIG_NULL_OK},
  329.     {TK_CONFIG_BORDER, "-selectcolor", "selectColor", "Background",
  330.     DEF_BUTTON_SELECT_MONO, Tk_Offset(Button, selectBorder),
  331.     CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|TK_CONFIG_MONO_ONLY
  332.     |TK_CONFIG_NULL_OK},
  333.     {TK_CONFIG_STRING, "-selectimage", "selectImage", "SelectImage",
  334.     DEF_BUTTON_SELECT_IMAGE, Tk_Offset(Button, selectImageString),
  335.     CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|TK_CONFIG_NULL_OK},
  336.     {TK_CONFIG_UID, "-state", "state", "State",
  337.     DEF_BUTTON_STATE, Tk_Offset(Button, state),
  338.     BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
  339.     {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
  340.     DEF_LABEL_TAKE_FOCUS, Tk_Offset(Button, takeFocus),
  341.     LABEL_MASK|TK_CONFIG_NULL_OK},
  342.     {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
  343.     DEF_BUTTON_TAKE_FOCUS, Tk_Offset(Button, takeFocus),
  344.     BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|TK_CONFIG_NULL_OK},
  345.     {TK_CONFIG_STRING, "-text", "text", "Text",
  346.     DEF_BUTTON_TEXT, Tk_Offset(Button, text), ALL_MASK},
  347.     {TK_CONFIG_STRING, "-textvariable", "textVariable", "Variable",
  348.     DEF_BUTTON_TEXT_VARIABLE, Tk_Offset(Button, textVarName),
  349.     ALL_MASK|TK_CONFIG_NULL_OK},
  350.     {TK_CONFIG_INT, "-underline", "underline", "Underline",
  351.     DEF_BUTTON_UNDERLINE, Tk_Offset(Button, underline), ALL_MASK},
  352.     {TK_CONFIG_STRING, "-value", "value", "Value",
  353.     DEF_BUTTON_VALUE, Tk_Offset(Button, onValue),
  354.     RADIO_BUTTON_MASK},
  355.     {TK_CONFIG_STRING, "-variable", "variable", "Variable",
  356.     DEF_RADIOBUTTON_VARIABLE, Tk_Offset(Button, selVarName),
  357.     RADIO_BUTTON_MASK},
  358.     {TK_CONFIG_STRING, "-variable", "variable", "Variable",
  359.     DEF_CHECKBUTTON_VARIABLE, Tk_Offset(Button, selVarName),
  360.     CHECK_BUTTON_MASK|TK_CONFIG_NULL_OK},
  361.     {TK_CONFIG_STRING, "-width", "width", "Width",
  362.     DEF_BUTTON_WIDTH, Tk_Offset(Button, widthString), ALL_MASK},
  363.     {TK_CONFIG_PIXELS, "-wraplength", "wrapLength", "WrapLength",
  364.     DEF_BUTTON_WRAP_LENGTH, Tk_Offset(Button, wrapLength), ALL_MASK},
  365.     {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
  366.     (char *) NULL, 0, 0}
  367. };
  368.  
  369. /*
  370.  * String to print out in error messages, identifying options for
  371.  * widget commands for different types of labels or buttons:
  372.  */
  373.  
  374. static char *optionStrings[] = {
  375.     "cget or configure",
  376.     "cget, configure, flash, or invoke",
  377.     "cget, configure, deselect, flash, invoke, select, or toggle",
  378.     "cget, configure, deselect, flash, invoke, or select"
  379. };
  380.  
  381. /*
  382.  * Forward declarations for procedures defined later in this file:
  383.  */
  384.  
  385. static void        ButtonCmdDeletedProc _ANSI_ARGS_((
  386.                 ClientData clientData));
  387. static int        ButtonCreate _ANSI_ARGS_((ClientData clientData,
  388.                 Tcl_Interp *interp, int argc, char **argv,
  389.                 int type));
  390. static void        ButtonEventProc _ANSI_ARGS_((ClientData clientData,
  391.                 XEvent *eventPtr));
  392. static void        ButtonImageProc _ANSI_ARGS_((ClientData clientData,
  393.                 int x, int y, int width, int height,
  394.                 int imgWidth, int imgHeight));
  395. static void        ButtonSelectImageProc _ANSI_ARGS_((
  396.                 ClientData clientData, int x, int y, int width,
  397.                 int height, int imgWidth, int imgHeight));
  398. static char *        ButtonTextVarProc _ANSI_ARGS_((ClientData clientData,
  399.                 Tcl_Interp *interp, char *name1, char *name2,
  400.                 int flags));
  401. static char *        ButtonVarProc _ANSI_ARGS_((ClientData clientData,
  402.                 Tcl_Interp *interp, char *name1, char *name2,
  403.                 int flags));
  404. static int        ButtonWidgetCmd _ANSI_ARGS_((ClientData clientData,
  405.                 Tcl_Interp *interp, int argc, char **argv));
  406. static void        ComputeButtonGeometry _ANSI_ARGS_((Button *butPtr));
  407. static int        ConfigureButton _ANSI_ARGS_((Tcl_Interp *interp,
  408.                 Button *butPtr, int argc, char **argv,
  409.                 int flags));
  410. static void        DestroyButton _ANSI_ARGS_((Button *butPtr));
  411. static void        DisplayButton _ANSI_ARGS_((ClientData clientData));
  412. static int        InvokeButton  _ANSI_ARGS_((Button *butPtr));
  413.  
  414. /*
  415.  *--------------------------------------------------------------
  416.  *
  417.  * Tk_ButtonCmd, Tk_CheckbuttonCmd, Tk_LabelCmd, Tk_RadiobuttonCmd --
  418.  *
  419.  *    These procedures are invoked to process the "button", "label",
  420.  *    "radiobutton", and "checkbutton" Tcl commands.  See the
  421.  *    user documentation for details on what they do.
  422.  *
  423.  * Results:
  424.  *    A standard Tcl result.
  425.  *
  426.  * Side effects:
  427.  *    See the user documentation.  These procedures are just wrappers;
  428.  *    they call ButtonCreate to do all of the real work.
  429.  *
  430.  *--------------------------------------------------------------
  431.  */
  432.  
  433. int
  434. Tk_ButtonCmd(clientData, interp, argc, argv)
  435.     ClientData clientData;    /* Main window associated with
  436.                  * interpreter. */
  437.     Tcl_Interp *interp;        /* Current interpreter. */
  438.     int argc;            /* Number of arguments. */
  439.     char **argv;        /* Argument strings. */
  440. {
  441.     return ButtonCreate(clientData, interp, argc, argv, TYPE_BUTTON);
  442. }
  443.  
  444. int
  445. Tk_CheckbuttonCmd(clientData, interp, argc, argv)
  446.     ClientData clientData;    /* Main window associated with
  447.                  * interpreter. */
  448.     Tcl_Interp *interp;        /* Current interpreter. */
  449.     int argc;            /* Number of arguments. */
  450.     char **argv;        /* Argument strings. */
  451. {
  452.     return ButtonCreate(clientData, interp, argc, argv, TYPE_CHECK_BUTTON);
  453. }
  454.  
  455. int
  456. Tk_LabelCmd(clientData, interp, argc, argv)
  457.     ClientData clientData;    /* Main window associated with
  458.                  * interpreter. */
  459.     Tcl_Interp *interp;        /* Current interpreter. */
  460.     int argc;            /* Number of arguments. */
  461.     char **argv;        /* Argument strings. */
  462. {
  463.     return ButtonCreate(clientData, interp, argc, argv, TYPE_LABEL);
  464. }
  465.  
  466. int
  467. Tk_RadiobuttonCmd(clientData, interp, argc, argv)
  468.     ClientData clientData;    /* Main window associated with
  469.                  * interpreter. */
  470.     Tcl_Interp *interp;        /* Current interpreter. */
  471.     int argc;            /* Number of arguments. */
  472.     char **argv;        /* Argument strings. */
  473. {
  474.     return ButtonCreate(clientData, interp, argc, argv, TYPE_RADIO_BUTTON);
  475. }
  476.  
  477. /*
  478.  *--------------------------------------------------------------
  479.  *
  480.  * ButtonCreate --
  481.  *
  482.  *    This procedure does all the real work of implementing the
  483.  *    "button", "label", "radiobutton", and "checkbutton" Tcl
  484.  *    commands.  See the user documentation for details on what it does.
  485.  *
  486.  * Results:
  487.  *    A standard Tcl result.
  488.  *
  489.  * Side effects:
  490.  *    See the user documentation.
  491.  *
  492.  *--------------------------------------------------------------
  493.  */
  494.  
  495. static int
  496. ButtonCreate(clientData, interp, argc, argv, type)
  497.     ClientData clientData;    /* Main window associated with
  498.                  * interpreter. */
  499.     Tcl_Interp *interp;        /* Current interpreter. */
  500.     int argc;            /* Number of arguments. */
  501.     char **argv;        /* Argument strings. */
  502.     int type;            /* Type of button to create: TYPE_LABEL,
  503.                  * TYPE_BUTTON, TYPE_CHECK_BUTTON, or
  504.                  * TYPE_RADIO_BUTTON. */
  505. {
  506.     register Button *butPtr;
  507.     Tk_Window tkwin = (Tk_Window) clientData;
  508.     Tk_Window new;
  509.  
  510.     if (argc < 2) {
  511.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  512.         argv[0], " pathName ?options?\"", (char *) NULL);
  513.     return TCL_ERROR;
  514.     }
  515.  
  516.     /*
  517.      * Create the new window.
  518.      */
  519.  
  520.     new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
  521.     if (new == NULL) {
  522.     return TCL_ERROR;
  523.     }
  524.  
  525.     /*
  526.      * Initialize the data structure for the button.
  527.      */
  528.  
  529.     butPtr = (Button *) ckalloc(sizeof(Button));
  530.     butPtr->tkwin = new;
  531.     butPtr->display = Tk_Display(new);
  532.     butPtr->widgetCmd = Tcl_CreateCommand(interp, Tk_PathName(butPtr->tkwin),
  533.         ButtonWidgetCmd, (ClientData) butPtr, ButtonCmdDeletedProc);
  534.     butPtr->interp = interp;
  535.     butPtr->type = type;
  536.     butPtr->text = NULL;
  537.     butPtr->numChars = 0;
  538.     butPtr->underline = -1;
  539.     butPtr->textVarName = NULL;
  540.     butPtr->bitmap = None;
  541.     butPtr->imageString = NULL;
  542.     butPtr->image = NULL;
  543.     butPtr->selectImageString = NULL;
  544.     butPtr->selectImage = NULL;
  545.     butPtr->state = tkNormalUid;
  546.     butPtr->normalBorder = NULL;
  547.     butPtr->activeBorder = NULL;
  548.     butPtr->borderWidth = 0;
  549.     butPtr->relief = TK_RELIEF_FLAT;
  550.     butPtr->highlightWidth = 0;
  551.     butPtr->highlightBgColorPtr = NULL;
  552.     butPtr->highlightColorPtr = NULL;
  553.     butPtr->inset = 0;
  554.     butPtr->fontPtr = NULL;
  555.     butPtr->normalFg = NULL;
  556.     butPtr->activeFg = NULL;
  557.     butPtr->disabledFg = NULL;
  558.     butPtr->normalTextGC = None;
  559.     butPtr->activeTextGC = None;
  560.     butPtr->gray = None;
  561.     butPtr->disabledGC = None;
  562.     butPtr->copyGC = None;
  563.     butPtr->widthString = NULL;
  564.     butPtr->heightString = NULL;
  565.     butPtr->width = 0;
  566.     butPtr->height = 0;
  567.     butPtr->wrapLength = 0;
  568.     butPtr->padX = 0;
  569.     butPtr->padY = 0;
  570.     butPtr->anchor = TK_ANCHOR_CENTER;
  571.     butPtr->justify = TK_JUSTIFY_CENTER;
  572.     butPtr->indicatorOn = 0;
  573.     butPtr->selectBorder = NULL;
  574.     butPtr->indicatorSpace = 0;
  575.     butPtr->indicatorDiameter = 0;
  576.     butPtr->selVarName = NULL;
  577.     butPtr->onValue = NULL;
  578.     butPtr->offValue = NULL;
  579.     butPtr->cursor = None;
  580.     butPtr->command = NULL;
  581.     butPtr->takeFocus = NULL;
  582.     butPtr->flags = 0;
  583.  
  584.     Tk_SetClass(new, classNames[type]);
  585.     Tk_CreateEventHandler(butPtr->tkwin,
  586.         ExposureMask|StructureNotifyMask|FocusChangeMask,
  587.         ButtonEventProc, (ClientData) butPtr);
  588.     if (ConfigureButton(interp, butPtr, argc-2, argv+2,
  589.         configFlags[type]) != TCL_OK) {
  590.     Tk_DestroyWindow(butPtr->tkwin);
  591.     return TCL_ERROR;
  592.     }
  593.  
  594.     interp->result = Tk_PathName(butPtr->tkwin);
  595.     return TCL_OK;
  596. }
  597.  
  598. /*
  599.  *--------------------------------------------------------------
  600.  *
  601.  * ButtonWidgetCmd --
  602.  *
  603.  *    This procedure is invoked to process the Tcl command
  604.  *    that corresponds to a widget managed by this module.
  605.  *    See the user documentation for details on what it does.
  606.  *
  607.  * Results:
  608.  *    A standard Tcl result.
  609.  *
  610.  * Side effects:
  611.  *    See the user documentation.
  612.  *
  613.  *--------------------------------------------------------------
  614.  */
  615.  
  616. static int
  617. ButtonWidgetCmd(clientData, interp, argc, argv)
  618.     ClientData clientData;    /* Information about button widget. */
  619.     Tcl_Interp *interp;        /* Current interpreter. */
  620.     int argc;            /* Number of arguments. */
  621.     char **argv;        /* Argument strings. */
  622. {
  623.     register Button *butPtr = (Button *) clientData;
  624.     int result = TCL_OK;
  625.     size_t length;
  626.     int c;
  627.  
  628.     if (argc < 2) {
  629.     sprintf(interp->result,
  630.         "wrong # args: should be \"%.50s option ?arg arg ...?\"",
  631.         argv[0]);
  632.     return TCL_ERROR;
  633.     }
  634.     Tcl_Preserve((ClientData) butPtr);
  635.     c = argv[1][0];
  636.     length = strlen(argv[1]);
  637.     if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
  638.         && (length >= 2)) {
  639.     if (argc != 3) {
  640.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  641.             argv[0], " cget option\"",
  642.             (char *) NULL);
  643.         goto error;
  644.     }
  645.     result = Tk_ConfigureValue(interp, butPtr->tkwin, configSpecs,
  646.         (char *) butPtr, argv[2], configFlags[butPtr->type]);
  647.     } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
  648.         && (length >= 2)) {
  649.     if (argc == 2) {
  650.         result = Tk_ConfigureInfo(interp, butPtr->tkwin, configSpecs,
  651.             (char *) butPtr, (char *) NULL, configFlags[butPtr->type]);
  652.     } else if (argc == 3) {
  653.         result = Tk_ConfigureInfo(interp, butPtr->tkwin, configSpecs,
  654.             (char *) butPtr, argv[2],
  655.             configFlags[butPtr->type]);
  656.     } else {
  657.         result = ConfigureButton(interp, butPtr, argc-2, argv+2,
  658.             configFlags[butPtr->type] | TK_CONFIG_ARGV_ONLY);
  659.     }
  660.     } else if ((c == 'd') && (strncmp(argv[1], "deselect", length) == 0)
  661.         && (butPtr->type >= TYPE_CHECK_BUTTON)) {
  662.     if (argc > 2) {
  663.         sprintf(interp->result,
  664.             "wrong # args: should be \"%.50s deselect\"",
  665.             argv[0]);
  666.         goto error;
  667.     }
  668.     if (butPtr->type == TYPE_CHECK_BUTTON) {
  669.         if (Tcl_SetVar(interp, butPtr->selVarName, butPtr->offValue,
  670.             TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
  671.         result = TCL_ERROR;
  672.         }
  673.     } else if (butPtr->flags & SELECTED) {
  674.         if (Tcl_SetVar(interp, butPtr->selVarName, "",
  675.             TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
  676.         result = TCL_ERROR;
  677.         };
  678.     }
  679.     } else if ((c == 'f') && (strncmp(argv[1], "flash", length) == 0)
  680.         && (butPtr->type != TYPE_LABEL)) {
  681.     int i;
  682.  
  683.     if (argc > 2) {
  684.         sprintf(interp->result,
  685.             "wrong # args: should be \"%.50s flash\"",
  686.             argv[0]);
  687.         goto error;
  688.     }
  689.     if (butPtr->state != tkDisabledUid) {
  690.         for (i = 0; i < 4; i++) {
  691.         butPtr->state = (butPtr->state == tkNormalUid)
  692.             ? tkActiveUid : tkNormalUid;
  693.         Tk_SetBackgroundFromBorder(butPtr->tkwin,
  694.             (butPtr->state == tkActiveUid) ? butPtr->activeBorder
  695.             : butPtr->normalBorder);
  696.         DisplayButton((ClientData) butPtr);
  697.  
  698.         /*
  699.          * Special note: must cancel any existing idle handler
  700.          * for DisplayButton;  it's no longer needed, and DisplayButton
  701.          * cleared the REDRAW_PENDING flag.
  702.          */
  703.  
  704.         Tcl_CancelIdleCall(DisplayButton, (ClientData) butPtr);
  705.         XFlush(butPtr->display);
  706.         Tcl_Sleep(50);
  707.         }
  708.     }
  709.     } else if ((c == 'i') && (strncmp(argv[1], "invoke", length) == 0)
  710.         && (butPtr->type > TYPE_LABEL)) {
  711.     if (argc > 2) {
  712.         sprintf(interp->result,
  713.             "wrong # args: should be \"%.50s invoke\"",
  714.             argv[0]);
  715.         goto error;
  716.     }
  717.     if (butPtr->state != tkDisabledUid) {
  718.         result = InvokeButton(butPtr);
  719.     }
  720.     } else if ((c == 's') && (strncmp(argv[1], "select", length) == 0)
  721.         && (butPtr->type >= TYPE_CHECK_BUTTON)) {
  722.     if (argc > 2) {
  723.         sprintf(interp->result,
  724.             "wrong # args: should be \"%.50s select\"",
  725.             argv[0]);
  726.         goto error;
  727.     }
  728.     if (Tcl_SetVar(interp, butPtr->selVarName, butPtr->onValue,
  729.         TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
  730.         result = TCL_ERROR;
  731.     }
  732.     } else if ((c == 't') && (strncmp(argv[1], "toggle", length) == 0)
  733.         && (length >= 2) && (butPtr->type == TYPE_CHECK_BUTTON)) {
  734.     if (argc > 2) {
  735.         sprintf(interp->result,
  736.             "wrong # args: should be \"%.50s toggle\"",
  737.             argv[0]);
  738.         goto error;
  739.     }
  740.     if (butPtr->flags & SELECTED) {
  741.         if (Tcl_SetVar(interp, butPtr->selVarName, butPtr->offValue,
  742.             TCL_GLOBAL_ONLY) == NULL) {
  743.         result = TCL_ERROR;
  744.         }
  745.     } else {
  746.         if (Tcl_SetVar(interp, butPtr->selVarName, butPtr->onValue,
  747.             TCL_GLOBAL_ONLY) == NULL) {
  748.         result = TCL_ERROR;
  749.         }
  750.     }
  751.     } else {
  752.     sprintf(interp->result,
  753.         "bad option \"%.50s\": must be %s", argv[1],
  754.         optionStrings[butPtr->type]);
  755.     goto error;
  756.     }
  757.     Tcl_Release((ClientData) butPtr);
  758.     return result;
  759.  
  760.     error:
  761.     Tcl_Release((ClientData) butPtr);
  762.     return TCL_ERROR;
  763. }
  764.  
  765. /*
  766.  *----------------------------------------------------------------------
  767.  *
  768.  * DestroyButton --
  769.  *
  770.  *    This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
  771.  *    to clean up the internal structure of a button at a safe time
  772.  *    (when no-one is using it anymore).
  773.  *
  774.  * Results:
  775.  *    None.
  776.  *
  777.  * Side effects:
  778.  *    Everything associated with the widget is freed up.
  779.  *
  780.  *----------------------------------------------------------------------
  781.  */
  782.  
  783. static void
  784. DestroyButton(butPtr)
  785.     Button *butPtr;        /* Info about button widget. */
  786. {
  787.     /*
  788.      * Free up all the stuff that requires special handling, then
  789.      * let Tk_FreeOptions handle all the standard option-related
  790.      * stuff.
  791.      */
  792.  
  793.     if (butPtr->textVarName != NULL) {
  794.     Tcl_UntraceVar(butPtr->interp, butPtr->textVarName,
  795.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  796.         ButtonTextVarProc, (ClientData) butPtr);
  797.     }
  798.     if (butPtr->image != NULL) {
  799.     Tk_FreeImage(butPtr->image);
  800.     }
  801.     if (butPtr->selectImage != NULL) {
  802.     Tk_FreeImage(butPtr->selectImage);
  803.     }
  804.     if (butPtr->normalTextGC != None) {
  805.     Tk_FreeGC(butPtr->display, butPtr->normalTextGC);
  806.     }
  807.     if (butPtr->activeTextGC != None) {
  808.     Tk_FreeGC(butPtr->display, butPtr->activeTextGC);
  809.     }
  810.     if (butPtr->gray != None) {
  811.     Tk_FreeBitmap(butPtr->display, butPtr->gray);
  812.     }
  813.     if (butPtr->disabledGC != None) {
  814.     Tk_FreeGC(butPtr->display, butPtr->disabledGC);
  815.     }
  816.     if (butPtr->copyGC != None) {
  817.     Tk_FreeGC(butPtr->display, butPtr->copyGC);
  818.     }
  819.     if (butPtr->selVarName != NULL) {
  820.     Tcl_UntraceVar(butPtr->interp, butPtr->selVarName,
  821.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  822.         ButtonVarProc, (ClientData) butPtr);
  823.     }
  824.     Tk_FreeOptions(configSpecs, (char *) butPtr, butPtr->display,
  825.         configFlags[butPtr->type]);
  826.     Tcl_EventuallyFree((ClientData)butPtr, TCL_DYNAMIC);
  827. }
  828.  
  829. /*
  830.  *----------------------------------------------------------------------
  831.  *
  832.  * ConfigureButton --
  833.  *
  834.  *    This procedure is called to process an argv/argc list, plus
  835.  *    the Tk option database, in order to configure (or
  836.  *    reconfigure) a button widget.
  837.  *
  838.  * Results:
  839.  *    The return value is a standard Tcl result.  If TCL_ERROR is
  840.  *    returned, then interp->result contains an error message.
  841.  *
  842.  * Side effects:
  843.  *    Configuration information, such as text string, colors, font,
  844.  *    etc. get set for butPtr;  old resources get freed, if there
  845.  *    were any.  The button is redisplayed.
  846.  *
  847.  *----------------------------------------------------------------------
  848.  */
  849.  
  850. static int
  851. ConfigureButton(interp, butPtr, argc, argv, flags)
  852.     Tcl_Interp *interp;        /* Used for error reporting. */
  853.     register Button *butPtr;    /* Information about widget;  may or may
  854.                  * not already have values for some fields. */
  855.     int argc;            /* Number of valid entries in argv. */
  856.     char **argv;        /* Arguments. */
  857.     int flags;            /* Flags to pass to Tk_ConfigureWidget. */
  858. {
  859.     XGCValues gcValues;
  860.     GC newGC;
  861.     unsigned long mask;
  862.     Tk_Image image;
  863.  
  864.     /*
  865.      * Eliminate any existing trace on variables monitored by the button.
  866.      */
  867.  
  868.     if (butPtr->textVarName != NULL) {
  869.     Tcl_UntraceVar(interp, butPtr->textVarName, 
  870.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  871.         ButtonTextVarProc, (ClientData) butPtr);
  872.     }
  873.     if (butPtr->selVarName != NULL) {
  874.     Tcl_UntraceVar(interp, butPtr->selVarName, 
  875.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  876.         ButtonVarProc, (ClientData) butPtr);
  877.     }
  878.  
  879.     if (Tk_ConfigureWidget(interp, butPtr->tkwin, configSpecs,
  880.         argc, argv, (char *) butPtr, flags) != TCL_OK) {
  881.     return TCL_ERROR;
  882.     }
  883.  
  884.     /*
  885.      * A few options need special processing, such as setting the
  886.      * background from a 3-D border, or filling in complicated
  887.      * defaults that couldn't be specified to Tk_ConfigureWidget.
  888.      */
  889.  
  890.     if ((butPtr->state == tkActiveUid) && !Tk_StrictMotif(butPtr->tkwin)) {
  891.     Tk_SetBackgroundFromBorder(butPtr->tkwin, butPtr->activeBorder);
  892.     } else {
  893.     Tk_SetBackgroundFromBorder(butPtr->tkwin, butPtr->normalBorder);
  894.     if ((butPtr->state != tkNormalUid) && (butPtr->state != tkActiveUid)
  895.         && (butPtr->state != tkDisabledUid)) {
  896.         Tcl_AppendResult(interp, "bad state value \"", butPtr->state,
  897.             "\": must be normal, active, or disabled", (char *) NULL);
  898.         butPtr->state = tkNormalUid;
  899.         return TCL_ERROR;
  900.     }
  901.     }
  902.  
  903.     if (butPtr->highlightWidth < 0) {
  904.     butPtr->highlightWidth = 0;
  905.     }
  906.  
  907.     gcValues.font = butPtr->fontPtr->fid;
  908.     gcValues.foreground = butPtr->normalFg->pixel;
  909.     gcValues.background = Tk_3DBorderColor(butPtr->normalBorder)->pixel;
  910.     
  911.     /*
  912.      * Note: GraphicsExpose events are disabled in normalTextGC because it's
  913.      * used to copy stuff from an off-screen pixmap onto the screen (we know
  914.      * that there's no problem with obscured areas).
  915.      */
  916.  
  917.     gcValues.graphics_exposures = False;
  918.     newGC = Tk_GetGC(butPtr->tkwin,
  919.         GCForeground|GCBackground|GCFont|GCGraphicsExposures,
  920.         &gcValues);
  921.     if (butPtr->normalTextGC != None) {
  922.     Tk_FreeGC(butPtr->display, butPtr->normalTextGC);
  923.     }
  924.     butPtr->normalTextGC = newGC;
  925.  
  926.     if (butPtr->activeFg != NULL) {
  927.     gcValues.font = butPtr->fontPtr->fid;
  928.     gcValues.foreground = butPtr->activeFg->pixel;
  929.     gcValues.background = Tk_3DBorderColor(butPtr->activeBorder)->pixel;
  930.     newGC = Tk_GetGC(butPtr->tkwin,
  931.         GCForeground|GCBackground|GCFont, &gcValues);
  932.     if (butPtr->activeTextGC != None) {
  933.         Tk_FreeGC(butPtr->display, butPtr->activeTextGC);
  934.     }
  935.     butPtr->activeTextGC = newGC;
  936.     }
  937.  
  938.     if (butPtr->type != TYPE_LABEL) {
  939.     gcValues.font = butPtr->fontPtr-> fid;
  940.     gcValues.background = Tk_3DBorderColor(butPtr->normalBorder)->pixel;
  941.     if ((butPtr->disabledFg != NULL) && (butPtr->imageString == NULL)) {
  942.         gcValues.foreground = butPtr->disabledFg->pixel;
  943.         mask = GCForeground|GCBackground|GCFont;
  944.     } else {
  945.         gcValues.foreground = gcValues.background;
  946.         if (butPtr->gray == None) {
  947.         butPtr->gray = Tk_GetBitmap(interp, butPtr->tkwin,
  948.             Tk_GetUid("gray50"));
  949.         if (butPtr->gray == None) {
  950.             return TCL_ERROR;
  951.         }
  952.         }
  953.         gcValues.fill_style = FillStippled;
  954.         gcValues.stipple = butPtr->gray;
  955.         mask = GCForeground|GCFillStyle|GCStipple;
  956.     }
  957.     newGC = Tk_GetGC(butPtr->tkwin, mask, &gcValues);
  958.     if (butPtr->disabledGC != None) {
  959.         Tk_FreeGC(butPtr->display, butPtr->disabledGC);
  960.     }
  961.     butPtr->disabledGC = newGC;
  962.     }
  963.  
  964.     if (butPtr->copyGC == None) {
  965.     butPtr->copyGC = Tk_GetGC(butPtr->tkwin, 0, &gcValues);
  966.     }
  967.  
  968.     if (butPtr->padX < 0) {
  969.     butPtr->padX = 0;
  970.     }
  971.     if (butPtr->padY < 0) {
  972.     butPtr->padY = 0;
  973.     }
  974.  
  975.     if (butPtr->type >= TYPE_CHECK_BUTTON) {
  976.     char *value;
  977.  
  978.     if (butPtr->selVarName == NULL) {
  979.         butPtr->selVarName = (char *) ckalloc((unsigned)
  980.             (strlen(Tk_Name(butPtr->tkwin)) + 1));
  981.         strcpy(butPtr->selVarName, Tk_Name(butPtr->tkwin));
  982.     }
  983.  
  984.     /*
  985.      * Select the button if the associated variable has the
  986.      * appropriate value, initialize the variable if it doesn't
  987.      * exist, then set a trace on the variable to monitor future
  988.      * changes to its value.
  989.      */
  990.  
  991.     value = Tcl_GetVar(interp, butPtr->selVarName, TCL_GLOBAL_ONLY);
  992.     butPtr->flags &= ~SELECTED;
  993.     if (value != NULL) {
  994.         if (strcmp(value, butPtr->onValue) == 0) {
  995.         butPtr->flags |= SELECTED;
  996.         }
  997.     } else {
  998.         if (Tcl_SetVar(interp, butPtr->selVarName,
  999.             (butPtr->type == TYPE_CHECK_BUTTON) ? butPtr->offValue : "",
  1000.             TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
  1001.         return TCL_ERROR;
  1002.         }
  1003.     }
  1004.     Tcl_TraceVar(interp, butPtr->selVarName,
  1005.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  1006.         ButtonVarProc, (ClientData) butPtr);
  1007.     }
  1008.  
  1009.     /*
  1010.      * Get the images for the widget, if there are any.  Allocate the
  1011.      * new images before freeing the old ones, so that the reference
  1012.      * counts don't go to zero and cause image data to be discarded.
  1013.      */
  1014.  
  1015.     if (butPtr->imageString != NULL) {
  1016.     image = Tk_GetImage(butPtr->interp, butPtr->tkwin,
  1017.         butPtr->imageString, ButtonImageProc, (ClientData) butPtr);
  1018.     if (image == NULL) {
  1019.         return TCL_ERROR;
  1020.     }
  1021.     } else {
  1022.     image = NULL;
  1023.     }
  1024.     if (butPtr->image != NULL) {
  1025.     Tk_FreeImage(butPtr->image);
  1026.     }
  1027.     butPtr->image = image;
  1028.     if (butPtr->selectImageString != NULL) {
  1029.     image = Tk_GetImage(butPtr->interp, butPtr->tkwin,
  1030.         butPtr->selectImageString, ButtonSelectImageProc,
  1031.         (ClientData) butPtr);
  1032.     if (image == NULL) {
  1033.         return TCL_ERROR;
  1034.     }
  1035.     } else {
  1036.     image = NULL;
  1037.     }
  1038.     if (butPtr->selectImage != NULL) {
  1039.     Tk_FreeImage(butPtr->selectImage);
  1040.     }
  1041.     butPtr->selectImage = image;
  1042.  
  1043.     if ((butPtr->image == NULL) && (butPtr->bitmap == None)
  1044.         && (butPtr->textVarName != NULL)) {
  1045.     /*
  1046.      * The button must display the value of a variable: set up a trace
  1047.      * on the variable's value, create the variable if it doesn't
  1048.      * exist, and fetch its current value.
  1049.      */
  1050.  
  1051.     char *value;
  1052.  
  1053.     value = Tcl_GetVar(interp, butPtr->textVarName, TCL_GLOBAL_ONLY);
  1054.     if (value == NULL) {
  1055.         if (Tcl_SetVar(interp, butPtr->textVarName, butPtr->text,
  1056.             TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
  1057.         return TCL_ERROR;
  1058.         }
  1059.     } else {
  1060.         if (butPtr->text != NULL) {
  1061.         ckfree(butPtr->text);
  1062.         }
  1063.         butPtr->text = (char *) ckalloc((unsigned) (strlen(value) + 1));
  1064.         strcpy(butPtr->text, value);
  1065.     }
  1066.     Tcl_TraceVar(interp, butPtr->textVarName,
  1067.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  1068.         ButtonTextVarProc, (ClientData) butPtr);
  1069.     }
  1070.  
  1071.     if ((butPtr->bitmap != None) || (butPtr->image != NULL)) {
  1072.     if (Tk_GetPixels(interp, butPtr->tkwin, butPtr->widthString,
  1073.         &butPtr->width) != TCL_OK) {
  1074.         widthError:
  1075.         Tcl_AddErrorInfo(interp, "\n    (processing -width option)");
  1076.         return TCL_ERROR;
  1077.     }
  1078.     if (Tk_GetPixels(interp, butPtr->tkwin, butPtr->heightString,
  1079.         &butPtr->height) != TCL_OK) {
  1080.         heightError:
  1081.         Tcl_AddErrorInfo(interp, "\n    (processing -height option)");
  1082.         return TCL_ERROR;
  1083.     }
  1084.     } else {
  1085.     if (Tcl_GetInt(interp, butPtr->widthString, &butPtr->width)
  1086.         != TCL_OK) {
  1087.         goto widthError;
  1088.     }
  1089.     if (Tcl_GetInt(interp, butPtr->heightString, &butPtr->height)
  1090.         != TCL_OK) {
  1091.         goto heightError;
  1092.     }
  1093.     }
  1094.     ComputeButtonGeometry(butPtr);
  1095.  
  1096.     /*
  1097.      * Lastly, arrange for the button to be redisplayed.
  1098.      */
  1099.  
  1100.     if (Tk_IsMapped(butPtr->tkwin) && !(butPtr->flags & REDRAW_PENDING)) {
  1101.     Tcl_DoWhenIdle(DisplayButton, (ClientData) butPtr);
  1102.     butPtr->flags |= REDRAW_PENDING;
  1103.     }
  1104.  
  1105.     return TCL_OK;
  1106. }
  1107.  
  1108. /*
  1109.  *----------------------------------------------------------------------
  1110.  *
  1111.  * DisplayButton --
  1112.  *
  1113.  *    This procedure is invoked to display a button widget.  It is
  1114.  *    normally invoked as an idle handler.
  1115.  *
  1116.  * Results:
  1117.  *    None.
  1118.  *
  1119.  * Side effects:
  1120.  *    Commands are output to X to display the button in its
  1121.  *    current mode.  The REDRAW_PENDING flag is cleared.
  1122.  *
  1123.  *----------------------------------------------------------------------
  1124.  */
  1125.  
  1126. static void
  1127. DisplayButton(clientData)
  1128.     ClientData clientData;    /* Information about widget. */
  1129. {
  1130.     register Button *butPtr = (Button *) clientData;
  1131.     GC gc;
  1132.     Tk_3DBorder border;
  1133.     Pixmap pixmap;
  1134.     int x = 0;            /* Initialization only needed to stop
  1135.                  * compiler warning. */
  1136.     int y, relief;
  1137.     register Tk_Window tkwin = butPtr->tkwin;
  1138.     int width, height;
  1139.     int offset;            /* 0 means this is a label widget.  1 means
  1140.                  * it is a flavor of button, so we offset
  1141.                  * the text to make the button appear to
  1142.                  * move up and down as the relief changes. */
  1143.  
  1144.     butPtr->flags &= ~REDRAW_PENDING;
  1145.     if ((butPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
  1146.     return;
  1147.     }
  1148.  
  1149.     border = butPtr->normalBorder;
  1150.     if ((butPtr->state == tkDisabledUid) && (butPtr->disabledFg != NULL)) {
  1151.     gc = butPtr->disabledGC;
  1152.     } else if ((butPtr->state == tkActiveUid)
  1153.         && !Tk_StrictMotif(butPtr->tkwin)) {
  1154.     gc = butPtr->activeTextGC;
  1155.     border = butPtr->activeBorder;
  1156.     } else {
  1157.     gc = butPtr->normalTextGC;
  1158.     }
  1159.     if ((butPtr->flags & SELECTED) && (butPtr->state != tkActiveUid)
  1160.         && (butPtr->selectBorder != NULL) && !butPtr->indicatorOn) {
  1161.     border = butPtr->selectBorder;
  1162.     }
  1163.  
  1164.     /*
  1165.      * Override the relief specified for the button if this is a
  1166.      * checkbutton or radiobutton and there's no indicator.
  1167.      */
  1168.  
  1169.     relief = butPtr->relief;
  1170.     if ((butPtr->type >= TYPE_CHECK_BUTTON) && !butPtr->indicatorOn) {
  1171.     relief = (butPtr->flags & SELECTED) ? TK_RELIEF_SUNKEN
  1172.         : TK_RELIEF_RAISED;
  1173.     }
  1174.  
  1175.     offset = (butPtr->type == TYPE_BUTTON) && !Tk_StrictMotif(butPtr->tkwin);
  1176.  
  1177.     /*
  1178.      * In order to avoid screen flashes, this procedure redraws
  1179.      * the button in a pixmap, then copies the pixmap to the
  1180.      * screen in a single operation.  This means that there's no
  1181.      * point in time where the on-sreen image has been cleared.
  1182.      */
  1183.  
  1184.     pixmap = Tk_GetPixmap(butPtr->display, Tk_WindowId(tkwin),
  1185.         Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin));
  1186.     Tk_Fill3DRectangle(tkwin, pixmap, border, 0, 0, Tk_Width(tkwin),
  1187.         Tk_Height(tkwin), 0, TK_RELIEF_FLAT);
  1188.  
  1189.     /*
  1190.      * Display image or bitmap or text for button.
  1191.      */
  1192.  
  1193.     if (butPtr->image != None) {
  1194.     Tk_SizeOfImage(butPtr->image, &width, &height);
  1195.  
  1196.     imageOrBitmap:
  1197.     switch (butPtr->anchor) {
  1198.         case TK_ANCHOR_NW: case TK_ANCHOR_W: case TK_ANCHOR_SW:
  1199.         x = butPtr->inset + butPtr->indicatorSpace + offset;
  1200.         break;
  1201.         case TK_ANCHOR_N: case TK_ANCHOR_CENTER: case TK_ANCHOR_S:
  1202.         x = ((int) (Tk_Width(tkwin) + butPtr->indicatorSpace
  1203.             - width))/2;
  1204.         break;
  1205.         default:
  1206.         x = Tk_Width(tkwin) - butPtr->inset - width - offset;
  1207.         break;
  1208.     }
  1209.     switch (butPtr->anchor) {
  1210.         case TK_ANCHOR_NW: case TK_ANCHOR_N: case TK_ANCHOR_NE:
  1211.         y = butPtr->inset + offset;
  1212.         break;
  1213.         case TK_ANCHOR_W: case TK_ANCHOR_CENTER: case TK_ANCHOR_E:
  1214.         y = ((int) (Tk_Height(tkwin) - height))/2;
  1215.         break;
  1216.         default:
  1217.         y = Tk_Height(tkwin) - butPtr->inset - height - offset;
  1218.         break;
  1219.     }
  1220.     if (relief == TK_RELIEF_RAISED) {
  1221.         x -= offset;
  1222.         y -= offset;
  1223.     } else if (relief == TK_RELIEF_SUNKEN) {
  1224.         x += offset;
  1225.         y += offset;
  1226.     }
  1227.     if (butPtr->image != NULL) {
  1228.         if ((butPtr->selectImage != NULL) && (butPtr->flags & SELECTED)) {
  1229.         Tk_RedrawImage(butPtr->selectImage, 0, 0, width, height, pixmap,
  1230.             x, y);
  1231.         } else {
  1232.         Tk_RedrawImage(butPtr->image, 0, 0, width, height, pixmap,
  1233.             x, y);
  1234.         }
  1235.     } else {
  1236.         XSetClipOrigin(butPtr->display, gc, x, y);
  1237.         XCopyPlane(butPtr->display, butPtr->bitmap, pixmap, gc, 0, 0,
  1238.             (unsigned int) width, (unsigned int) height, x, y, 1);
  1239.         XSetClipOrigin(butPtr->display, gc, 0, 0);
  1240.     }
  1241.     y += height/2;
  1242.     } else if (butPtr->bitmap != None) {
  1243.     Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height);
  1244.     goto imageOrBitmap;
  1245.     } else {
  1246.     switch (butPtr->anchor) {
  1247.         case TK_ANCHOR_NW: case TK_ANCHOR_W: case TK_ANCHOR_SW:
  1248.         x = butPtr->inset + butPtr->padX + butPtr->indicatorSpace
  1249.             + offset;
  1250.         break;
  1251.         case TK_ANCHOR_N: case TK_ANCHOR_CENTER: case TK_ANCHOR_S:
  1252.         x = ((int) (Tk_Width(tkwin) + butPtr->indicatorSpace
  1253.             - butPtr->textWidth))/2;
  1254.         break;
  1255.         default:
  1256.         x = Tk_Width(tkwin) - butPtr->inset - butPtr->padX
  1257.             - butPtr->textWidth - offset;
  1258.         break;
  1259.     }
  1260.     switch (butPtr->anchor) {
  1261.         case TK_ANCHOR_NW: case TK_ANCHOR_N: case TK_ANCHOR_NE:
  1262.         y = butPtr->inset + butPtr->padY + offset;
  1263.         break;
  1264.         case TK_ANCHOR_W: case TK_ANCHOR_CENTER: case TK_ANCHOR_E:
  1265.         y = ((int) (Tk_Height(tkwin) - butPtr->textHeight))/2;
  1266.         break;
  1267.         default:
  1268.         y = Tk_Height(tkwin) - butPtr->inset - butPtr->padY
  1269.             - butPtr->textHeight - offset;
  1270.         break;
  1271.     }
  1272.     if (relief == TK_RELIEF_RAISED) {
  1273.         x -= offset;
  1274.         y -= offset;
  1275.     } else if (relief == TK_RELIEF_SUNKEN) {
  1276.         x += offset;
  1277.         y += offset;
  1278.     }
  1279.     TkDisplayText(butPtr->display, pixmap, butPtr->fontPtr,
  1280.         butPtr->text, butPtr->numChars, x, y, butPtr->textWidth,
  1281.         butPtr->justify, butPtr->underline, gc);
  1282.     y += butPtr->textHeight/2;
  1283.     }
  1284.  
  1285.     /*
  1286.      * Draw the indicator for check buttons and radio buttons.  At this
  1287.      * point x and y refer to the top-left corner of the text or image
  1288.      * or bitmap.
  1289.      */
  1290.  
  1291.     if ((butPtr->type == TYPE_CHECK_BUTTON) && butPtr->indicatorOn) {
  1292.     int dim;
  1293.  
  1294.     dim = butPtr->indicatorDiameter;
  1295.     x -= butPtr->indicatorSpace;
  1296.     y -= dim/2;
  1297.     if (dim > 2*butPtr->borderWidth) {
  1298.         Tk_Draw3DRectangle(tkwin, pixmap, border, x, y, dim, dim,
  1299.             butPtr->borderWidth, 
  1300.             (butPtr->flags & SELECTED) ? TK_RELIEF_SUNKEN :
  1301.             TK_RELIEF_RAISED);
  1302.         x += butPtr->borderWidth;
  1303.         y += butPtr->borderWidth;
  1304.         dim -= 2*butPtr->borderWidth;
  1305.         if (butPtr->flags & SELECTED) {
  1306.         GC gc;
  1307.  
  1308.         gc = Tk_3DBorderGC(tkwin,(butPtr->selectBorder != NULL)
  1309.             ? butPtr->selectBorder : butPtr->normalBorder,
  1310.             TK_3D_FLAT_GC);
  1311.         XFillRectangle(butPtr->display, pixmap, gc, x, y,
  1312.             (unsigned int) dim, (unsigned int) dim);
  1313.         } else {
  1314.         Tk_Fill3DRectangle(tkwin, pixmap, butPtr->normalBorder, x, y,
  1315.             dim, dim, butPtr->borderWidth, TK_RELIEF_FLAT);
  1316.         }
  1317.     }
  1318.     } else if ((butPtr->type == TYPE_RADIO_BUTTON) && butPtr->indicatorOn) {
  1319.     XPoint points[4];
  1320.     int radius;
  1321.  
  1322.     radius = butPtr->indicatorDiameter/2;
  1323.     points[0].x = x - butPtr->indicatorSpace;
  1324.     points[0].y = y;
  1325.     points[1].x = points[0].x + radius;
  1326.     points[1].y = points[0].y + radius;
  1327.     points[2].x = points[1].x + radius;
  1328.     points[2].y = points[0].y;
  1329.     points[3].x = points[1].x;
  1330.     points[3].y = points[0].y - radius;
  1331.     if (butPtr->flags & SELECTED) {
  1332.         GC gc;
  1333.  
  1334.         gc = Tk_3DBorderGC(tkwin, (butPtr->selectBorder != NULL)
  1335.             ? butPtr->selectBorder : butPtr->normalBorder,
  1336.             TK_3D_FLAT_GC);
  1337.         XFillPolygon(butPtr->display, pixmap, gc, points, 4, Convex,
  1338.             CoordModeOrigin);
  1339.     } else {
  1340.         Tk_Fill3DPolygon(tkwin, pixmap, butPtr->normalBorder, points,
  1341.             4, butPtr->borderWidth, TK_RELIEF_FLAT);
  1342.     }
  1343.     Tk_Draw3DPolygon(tkwin, pixmap, border, points, 4, butPtr->borderWidth,
  1344.         (butPtr->flags & SELECTED) ? TK_RELIEF_SUNKEN :
  1345.         TK_RELIEF_RAISED);
  1346.     }
  1347.  
  1348.     /*
  1349.      * If the button is disabled with a stipple rather than a special
  1350.      * foreground color, generate the stippled effect.  If the widget
  1351.      * is selected and we use a different background color when selected,
  1352.      * must temporarily modify the GC.
  1353.      */
  1354.  
  1355.     if ((butPtr->state == tkDisabledUid)
  1356.         && ((butPtr->disabledFg == NULL) || (butPtr->image != NULL))) {
  1357.     if ((butPtr->flags & SELECTED) && !butPtr->indicatorOn
  1358.         && (butPtr->selectBorder != NULL)) {
  1359.         XSetForeground(butPtr->display, butPtr->disabledGC,
  1360.             Tk_3DBorderColor(butPtr->selectBorder)->pixel);
  1361.     }
  1362.     XFillRectangle(butPtr->display, pixmap, butPtr->disabledGC,
  1363.         butPtr->inset, butPtr->inset,
  1364.         (unsigned) (Tk_Width(tkwin) - 2*butPtr->inset),
  1365.         (unsigned) (Tk_Height(tkwin) - 2*butPtr->inset));
  1366.     if ((butPtr->flags & SELECTED) && !butPtr->indicatorOn
  1367.         && (butPtr->selectBorder != NULL)) {
  1368.         XSetForeground(butPtr->display, butPtr->disabledGC,
  1369.             Tk_3DBorderColor(butPtr->normalBorder)->pixel);
  1370.     }
  1371.     }
  1372.  
  1373.     /*
  1374.      * Draw the border and traversal highlight last.  This way, if the
  1375.      * button's contents overflow they'll be covered up by the border.
  1376.      */
  1377.  
  1378.     if (relief != TK_RELIEF_FLAT) {
  1379.     Tk_Draw3DRectangle(tkwin, pixmap, border,
  1380.         butPtr->highlightWidth, butPtr->highlightWidth,
  1381.         Tk_Width(tkwin) - 2*butPtr->highlightWidth,
  1382.         Tk_Height(tkwin) - 2*butPtr->highlightWidth,
  1383.         butPtr->borderWidth, relief);
  1384.     }
  1385.     if (butPtr->highlightWidth != 0) {
  1386.     GC gc;
  1387.  
  1388.     if (butPtr->flags & GOT_FOCUS) {
  1389.         gc = Tk_GCForColor(butPtr->highlightColorPtr, pixmap);
  1390.     } else {
  1391.         gc = Tk_GCForColor(butPtr->highlightBgColorPtr, pixmap);
  1392.     }
  1393.     Tk_DrawFocusHighlight(tkwin, gc, butPtr->highlightWidth, pixmap);
  1394.     }
  1395.  
  1396.     /*
  1397.      * Copy the information from the off-screen pixmap onto the screen,
  1398.      * then delete the pixmap.
  1399.      */
  1400.  
  1401.     XCopyArea(butPtr->display, pixmap, Tk_WindowId(tkwin),
  1402.         butPtr->copyGC, 0, 0, (unsigned) Tk_Width(tkwin),
  1403.         (unsigned) Tk_Height(tkwin), 0, 0);
  1404.     Tk_FreePixmap(butPtr->display, pixmap);
  1405. }
  1406.  
  1407. /*
  1408.  *--------------------------------------------------------------
  1409.  *
  1410.  * ButtonEventProc --
  1411.  *
  1412.  *    This procedure is invoked by the Tk dispatcher for various
  1413.  *    events on buttons.
  1414.  *
  1415.  * Results:
  1416.  *    None.
  1417.  *
  1418.  * Side effects:
  1419.  *    When the window gets deleted, internal structures get
  1420.  *    cleaned up.  When it gets exposed, it is redisplayed.
  1421.  *
  1422.  *--------------------------------------------------------------
  1423.  */
  1424.  
  1425. static void
  1426. ButtonEventProc(clientData, eventPtr)
  1427.     ClientData clientData;    /* Information about window. */
  1428.     XEvent *eventPtr;        /* Information about event. */
  1429. {
  1430.     Button *butPtr = (Button *) clientData;
  1431.     if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
  1432.     goto redraw;
  1433.     } else if (eventPtr->type == ConfigureNotify) {
  1434.     /*
  1435.      * Must redraw after size changes, since layout could have changed
  1436.      * and borders will need to be redrawn.
  1437.      */
  1438.  
  1439.     goto redraw;
  1440.     } else if (eventPtr->type == DestroyNotify) {
  1441.     if (butPtr->tkwin != NULL) {
  1442.         butPtr->tkwin = NULL;
  1443.         Tcl_DeleteCommand(butPtr->interp,
  1444.             Tcl_GetCommandName(butPtr->interp, butPtr->widgetCmd));
  1445.     }
  1446.     if (butPtr->flags & REDRAW_PENDING) {
  1447.         Tcl_CancelIdleCall(DisplayButton, (ClientData) butPtr);
  1448.     }
  1449.     DestroyButton(butPtr);
  1450.     } else if (eventPtr->type == FocusIn) {
  1451.     if (eventPtr->xfocus.detail != NotifyInferior) {
  1452.         butPtr->flags |= GOT_FOCUS;
  1453.         if (butPtr->highlightWidth > 0) {
  1454.         goto redraw;
  1455.         }
  1456.     }
  1457.     } else if (eventPtr->type == FocusOut) {
  1458.     if (eventPtr->xfocus.detail != NotifyInferior) {
  1459.         butPtr->flags &= ~GOT_FOCUS;
  1460.         if (butPtr->highlightWidth > 0) {
  1461.         goto redraw;
  1462.         }
  1463.     }
  1464.     }
  1465.     return;
  1466.  
  1467.     redraw:
  1468.     if ((butPtr->tkwin != NULL) && !(butPtr->flags & REDRAW_PENDING)) {
  1469.     Tcl_DoWhenIdle(DisplayButton, (ClientData) butPtr);
  1470.     butPtr->flags |= REDRAW_PENDING;
  1471.     }
  1472. }
  1473.  
  1474. /*
  1475.  *----------------------------------------------------------------------
  1476.  *
  1477.  * ButtonCmdDeletedProc --
  1478.  *
  1479.  *    This procedure is invoked when a widget command is deleted.  If
  1480.  *    the widget isn't already in the process of being destroyed,
  1481.  *    this command destroys it.
  1482.  *
  1483.  * Results:
  1484.  *    None.
  1485.  *
  1486.  * Side effects:
  1487.  *    The widget is destroyed.
  1488.  *
  1489.  *----------------------------------------------------------------------
  1490.  */
  1491.  
  1492. static void
  1493. ButtonCmdDeletedProc(clientData)
  1494.     ClientData clientData;    /* Pointer to widget record for widget. */
  1495. {
  1496.     Button *butPtr = (Button *) clientData;
  1497.     Tk_Window tkwin = butPtr->tkwin;
  1498.  
  1499.     /*
  1500.      * This procedure could be invoked either because the window was
  1501.      * destroyed and the command was then deleted (in which case tkwin
  1502.      * is NULL) or because the command was deleted, and then this procedure
  1503.      * destroys the widget.
  1504.      */
  1505.  
  1506.     if (tkwin != NULL) {
  1507.     butPtr->tkwin = NULL;
  1508.     Tk_DestroyWindow(tkwin);
  1509.     }
  1510. }
  1511.  
  1512. /*
  1513.  *----------------------------------------------------------------------
  1514.  *
  1515.  * ComputeButtonGeometry --
  1516.  *
  1517.  *    After changes in a button's text or bitmap, this procedure
  1518.  *    recomputes the button's geometry and passes this information
  1519.  *    along to the geometry manager for the window.
  1520.  *
  1521.  * Results:
  1522.  *    None.
  1523.  *
  1524.  * Side effects:
  1525.  *    The button's window may change size.
  1526.  *
  1527.  *----------------------------------------------------------------------
  1528.  */
  1529.  
  1530. static void
  1531. ComputeButtonGeometry(butPtr)
  1532.     register Button *butPtr;    /* Button whose geometry may have changed. */
  1533. {
  1534.     int width, height;
  1535.  
  1536.     if (butPtr->highlightWidth < 0) {
  1537.     butPtr->highlightWidth = 0;
  1538.     }
  1539.     butPtr->inset = butPtr->highlightWidth + butPtr->borderWidth;
  1540.     butPtr->indicatorSpace = 0;
  1541.     if (butPtr->image != NULL) {
  1542.     Tk_SizeOfImage(butPtr->image, &width, &height);
  1543.     imageOrBitmap:
  1544.     if (butPtr->width > 0) {
  1545.         width = butPtr->width;
  1546.     }
  1547.     if (butPtr->height > 0) {
  1548.         height = butPtr->height;
  1549.     }
  1550.     if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn) {
  1551.         butPtr->indicatorSpace = height;
  1552.         if (butPtr->type == TYPE_CHECK_BUTTON) {
  1553.         butPtr->indicatorDiameter = (65*height)/100;
  1554.         } else {
  1555.         butPtr->indicatorDiameter = (75*height)/100;
  1556.         }
  1557.     }
  1558.     } else if (butPtr->bitmap != None) {
  1559.     Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height);
  1560.     goto imageOrBitmap;
  1561.     } else {
  1562.     butPtr->numChars = strlen(butPtr->text);
  1563.     TkComputeTextGeometry(butPtr->fontPtr, butPtr->text,
  1564.         butPtr->numChars, butPtr->wrapLength, &butPtr->textWidth,
  1565.         &butPtr->textHeight);
  1566.     width = butPtr->textWidth;
  1567.     height = butPtr->textHeight;
  1568.     if (butPtr->width > 0) {
  1569.         width = butPtr->width * XTextWidth(butPtr->fontPtr, "0", 1);
  1570.     }
  1571.     if (butPtr->height > 0) {
  1572.         height = butPtr->height * (butPtr->fontPtr->ascent
  1573.             + butPtr->fontPtr->descent);
  1574.     }
  1575.     if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn) {
  1576.         butPtr->indicatorDiameter = butPtr->fontPtr->ascent
  1577.             + butPtr->fontPtr->descent;
  1578.         if (butPtr->type == TYPE_CHECK_BUTTON) {
  1579.         butPtr->indicatorDiameter = (80*butPtr->indicatorDiameter)/100;
  1580.         }
  1581.         butPtr->indicatorSpace = butPtr->indicatorDiameter
  1582.         + XTextWidth(butPtr->fontPtr, "0", 1);
  1583.     }
  1584.     }
  1585.  
  1586.     /*
  1587.      * When issuing the geometry request, add extra space for the indicator,
  1588.      * if any, and for the border and padding, plus two extra pixels so the
  1589.      * display can be offset by 1 pixel in either direction for the raised
  1590.      * or lowered effect.
  1591.      */
  1592.  
  1593.     if ((butPtr->image == NULL) && (butPtr->bitmap == None)) {
  1594.     width += 2*butPtr->padX;
  1595.     height += 2*butPtr->padY;
  1596.     }
  1597.     if ((butPtr->type == TYPE_BUTTON) && !Tk_StrictMotif(butPtr->tkwin)) {
  1598.     width += 2;
  1599.     height += 2;
  1600.     }
  1601.     Tk_GeometryRequest(butPtr->tkwin, (int) (width + butPtr->indicatorSpace
  1602.         + 2*butPtr->inset), (int) (height + 2*butPtr->inset));
  1603.     Tk_SetInternalBorder(butPtr->tkwin, butPtr->inset);
  1604. }
  1605.  
  1606. /*
  1607.  *----------------------------------------------------------------------
  1608.  *
  1609.  * InvokeButton --
  1610.  *
  1611.  *    This procedure is called to carry out the actions associated
  1612.  *    with a button, such as invoking a Tcl command or setting a
  1613.  *    variable.  This procedure is invoked, for example, when the
  1614.  *    button is invoked via the mouse.
  1615.  *
  1616.  * Results:
  1617.  *    A standard Tcl return value.  Information is also left in
  1618.  *    interp->result.
  1619.  *
  1620.  * Side effects:
  1621.  *    Depends on the button and its associated command.
  1622.  *
  1623.  *----------------------------------------------------------------------
  1624.  */
  1625.  
  1626. static int
  1627. InvokeButton(butPtr)
  1628.     register Button *butPtr;        /* Information about button. */
  1629. {
  1630.     if (butPtr->type == TYPE_CHECK_BUTTON) {
  1631.     if (butPtr->flags & SELECTED) {
  1632.         if (Tcl_SetVar(butPtr->interp, butPtr->selVarName, butPtr->offValue,
  1633.             TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
  1634.         return TCL_ERROR;
  1635.         }
  1636.     } else {
  1637.         if (Tcl_SetVar(butPtr->interp, butPtr->selVarName, butPtr->onValue,
  1638.             TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
  1639.         return TCL_ERROR;
  1640.         }
  1641.     }
  1642.     } else if (butPtr->type == TYPE_RADIO_BUTTON) {
  1643.     if (Tcl_SetVar(butPtr->interp, butPtr->selVarName, butPtr->onValue,
  1644.         TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
  1645.         return TCL_ERROR;
  1646.     }
  1647.     }
  1648.     if ((butPtr->type != TYPE_LABEL) && (butPtr->command != NULL)) {
  1649.     return TkCopyAndGlobalEval(butPtr->interp, butPtr->command);
  1650.     }
  1651.     return TCL_OK;
  1652. }
  1653.  
  1654. /*
  1655.  *--------------------------------------------------------------
  1656.  *
  1657.  * ButtonVarProc --
  1658.  *
  1659.  *    This procedure is invoked when someone changes the
  1660.  *    state variable associated with a radio button.  Depending
  1661.  *    on the new value of the button's variable, the button
  1662.  *    may be selected or deselected.
  1663.  *
  1664.  * Results:
  1665.  *    NULL is always returned.
  1666.  *
  1667.  * Side effects:
  1668.  *    The button may become selected or deselected.
  1669.  *
  1670.  *--------------------------------------------------------------
  1671.  */
  1672.  
  1673.     /* ARGSUSED */
  1674. static char *
  1675. ButtonVarProc(clientData, interp, name1, name2, flags)
  1676.     ClientData clientData;    /* Information about button. */
  1677.     Tcl_Interp *interp;        /* Interpreter containing variable. */
  1678.     char *name1;        /* Name of variable. */
  1679.     char *name2;        /* Second part of variable name. */
  1680.     int flags;            /* Information about what happened. */
  1681. {
  1682.     register Button *butPtr = (Button *) clientData;
  1683.     char *value;
  1684.  
  1685.     /*
  1686.      * If the variable is being unset, then just re-establish the
  1687.      * trace unless the whole interpreter is going away.
  1688.      */
  1689.  
  1690.     if (flags & TCL_TRACE_UNSETS) {
  1691.     butPtr->flags &= ~SELECTED;
  1692.     if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
  1693.         Tcl_TraceVar(interp, butPtr->selVarName,
  1694.             TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  1695.             ButtonVarProc, clientData);
  1696.     }
  1697.     goto redisplay;
  1698.     }
  1699.  
  1700.     /*
  1701.      * Use the value of the variable to update the selected status of
  1702.      * the button.
  1703.      */
  1704.  
  1705.     value = Tcl_GetVar(interp, butPtr->selVarName, TCL_GLOBAL_ONLY);
  1706.     if (value == NULL) {
  1707.     value = "";
  1708.     }
  1709.     if (strcmp(value, butPtr->onValue) == 0) {
  1710.     if (butPtr->flags & SELECTED) {
  1711.         return (char *) NULL;
  1712.     }
  1713.     butPtr->flags |= SELECTED;
  1714.     } else if (butPtr->flags & SELECTED) {
  1715.     butPtr->flags &= ~SELECTED;
  1716.     } else {
  1717.     return (char *) NULL;
  1718.     }
  1719.  
  1720.     redisplay:
  1721.     if ((butPtr->tkwin != NULL) && Tk_IsMapped(butPtr->tkwin)
  1722.         && !(butPtr->flags & REDRAW_PENDING)) {
  1723.     Tcl_DoWhenIdle(DisplayButton, (ClientData) butPtr);
  1724.     butPtr->flags |= REDRAW_PENDING;
  1725.     }
  1726.     return (char *) NULL;
  1727. }
  1728.  
  1729. /*
  1730.  *--------------------------------------------------------------
  1731.  *
  1732.  * ButtonTextVarProc --
  1733.  *
  1734.  *    This procedure is invoked when someone changes the variable
  1735.  *    whose contents are to be displayed in a button.
  1736.  *
  1737.  * Results:
  1738.  *    NULL is always returned.
  1739.  *
  1740.  * Side effects:
  1741.  *    The text displayed in the button will change to match the
  1742.  *    variable.
  1743.  *
  1744.  *--------------------------------------------------------------
  1745.  */
  1746.  
  1747.     /* ARGSUSED */
  1748. static char *
  1749. ButtonTextVarProc(clientData, interp, name1, name2, flags)
  1750.     ClientData clientData;    /* Information about button. */
  1751.     Tcl_Interp *interp;        /* Interpreter containing variable. */
  1752.     char *name1;        /* Not used. */
  1753.     char *name2;        /* Not used. */
  1754.     int flags;            /* Information about what happened. */
  1755. {
  1756.     register Button *butPtr = (Button *) clientData;
  1757.     char *value;
  1758.  
  1759.     /*
  1760.      * If the variable is unset, then immediately recreate it unless
  1761.      * the whole interpreter is going away.
  1762.      */
  1763.  
  1764.     if (flags & TCL_TRACE_UNSETS) {
  1765.     if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
  1766.         Tcl_SetVar(interp, butPtr->textVarName, butPtr->text,
  1767.             TCL_GLOBAL_ONLY);
  1768.         Tcl_TraceVar(interp, butPtr->textVarName,
  1769.             TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  1770.             ButtonTextVarProc, clientData);
  1771.     }
  1772.     return (char *) NULL;
  1773.     }
  1774.  
  1775.     value = Tcl_GetVar(interp, butPtr->textVarName, TCL_GLOBAL_ONLY);
  1776.     if (value == NULL) {
  1777.     value = "";
  1778.     }
  1779.     if (butPtr->text != NULL) {
  1780.     ckfree(butPtr->text);
  1781.     }
  1782.     butPtr->text = (char *) ckalloc((unsigned) (strlen(value) + 1));
  1783.     strcpy(butPtr->text, value);
  1784.     ComputeButtonGeometry(butPtr);
  1785.  
  1786.     if ((butPtr->tkwin != NULL) && Tk_IsMapped(butPtr->tkwin)
  1787.         && !(butPtr->flags & REDRAW_PENDING)) {
  1788.     Tcl_DoWhenIdle(DisplayButton, (ClientData) butPtr);
  1789.     butPtr->flags |= REDRAW_PENDING;
  1790.     }
  1791.     return (char *) NULL;
  1792. }
  1793.  
  1794. /*
  1795.  *----------------------------------------------------------------------
  1796.  *
  1797.  * ButtonImageProc --
  1798.  *
  1799.  *    This procedure is invoked by the image code whenever the manager
  1800.  *    for an image does something that affects the size of contents
  1801.  *    of an image displayed in a button.
  1802.  *
  1803.  * Results:
  1804.  *    None.
  1805.  *
  1806.  * Side effects:
  1807.  *    Arranges for the button to get redisplayed.
  1808.  *
  1809.  *----------------------------------------------------------------------
  1810.  */
  1811.  
  1812. static void
  1813. ButtonImageProc(clientData, x, y, width, height, imgWidth, imgHeight)
  1814.     ClientData clientData;        /* Pointer to widget record. */
  1815.     int x, y;                /* Upper left pixel (within image)
  1816.                      * that must be redisplayed. */
  1817.     int width, height;            /* Dimensions of area to redisplay
  1818.                      * (may be <= 0). */
  1819.     int imgWidth, imgHeight;        /* New dimensions of image. */
  1820. {
  1821.     register Button *butPtr = (Button *) clientData;
  1822.  
  1823.     if (butPtr->tkwin != NULL) {
  1824.     ComputeButtonGeometry(butPtr);
  1825.     if (Tk_IsMapped(butPtr->tkwin) && !(butPtr->flags & REDRAW_PENDING)) {
  1826.         Tcl_DoWhenIdle(DisplayButton, (ClientData) butPtr);
  1827.         butPtr->flags |= REDRAW_PENDING;
  1828.     }
  1829.     }
  1830. }
  1831.  
  1832. /*
  1833.  *----------------------------------------------------------------------
  1834.  *
  1835.  * ButtonSelectImageProc --
  1836.  *
  1837.  *    This procedure is invoked by the image code whenever the manager
  1838.  *    for an image does something that affects the size of contents
  1839.  *    of the image displayed in a button when it is selected.
  1840.  *
  1841.  * Results:
  1842.  *    None.
  1843.  *
  1844.  * Side effects:
  1845.  *    May arrange for the button to get redisplayed.
  1846.  *
  1847.  *----------------------------------------------------------------------
  1848.  */
  1849.  
  1850. static void
  1851. ButtonSelectImageProc(clientData, x, y, width, height, imgWidth, imgHeight)
  1852.     ClientData clientData;        /* Pointer to widget record. */
  1853.     int x, y;                /* Upper left pixel (within image)
  1854.                      * that must be redisplayed. */
  1855.     int width, height;            /* Dimensions of area to redisplay
  1856.                      * (may be <= 0). */
  1857.     int imgWidth, imgHeight;        /* New dimensions of image. */
  1858. {
  1859.     register Button *butPtr = (Button *) clientData;
  1860.  
  1861.     /*
  1862.      * Don't recompute geometry:  it's controlled by the primary image.
  1863.      */
  1864.  
  1865.     if ((butPtr->flags & SELECTED) && (butPtr->tkwin != NULL)
  1866.         && Tk_IsMapped(butPtr->tkwin)
  1867.         && !(butPtr->flags & REDRAW_PENDING)) {
  1868.     Tcl_DoWhenIdle(DisplayButton, (ClientData) butPtr);
  1869.     butPtr->flags |= REDRAW_PENDING;
  1870.     }
  1871. }
  1872.