Annotation of micropolis/src/tk/tkbutton.c, revision 1.1

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

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.