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

1.1     ! root        1: /* 
        !             2:  * tkMenubutton.c --
        !             3:  *
        !             4:  *     This module implements button-like widgets that are used
        !             5:  *     to invoke pull-down menus.
        !             6:  *
        !             7:  * Copyright 1990 Regents of the University of California.
        !             8:  * Permission to use, copy, modify, and distribute this
        !             9:  * software and its documentation for any purpose and without
        !            10:  * fee is hereby granted, provided that the above copyright
        !            11:  * notice appear in all copies.  The University of California
        !            12:  * makes no representations about the suitability of this
        !            13:  * software for any purpose.  It is provided "as is" without
        !            14:  * express or implied warranty.
        !            15:  */
        !            16: 
        !            17: #ifndef lint
        !            18: static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkMenubutton.c,v 1.33 92/08/21 16:21:47 ouster Exp $ SPRITE (Berkeley)";
        !            19: #endif
        !            20: 
        !            21: #include "tkconfig.h"
        !            22: #include "default.h"
        !            23: #include "tkint.h"
        !            24: 
        !            25: /*
        !            26:  * A data structure of the following type is kept for each
        !            27:  * widget managed by this file:
        !            28:  */
        !            29: 
        !            30: typedef struct {
        !            31:     Tk_Window tkwin;           /* Window that embodies the widget.  NULL
        !            32:                                 * means that the window has been destroyed
        !            33:                                 * but the data structures haven't yet been
        !            34:                                 * cleaned up.*/
        !            35:     Tcl_Interp *interp;                /* Interpreter associated with menu button. */
        !            36:     char *menuName;            /* Name of menu associated with widget (used
        !            37:                                 * to generate post and unpost commands).
        !            38:                                 * Malloc-ed. */
        !            39:     Tk_Uid varName;            /* Name of variable associated with collection
        !            40:                                 * of menu bars:  used to allow scanning of
        !            41:                                 * menus.   Also used as identifier for
        !            42:                                 * menu group. */
        !            43: 
        !            44:     /*
        !            45:      * Information about what's displayed in the menu button:
        !            46:      */
        !            47: 
        !            48:     char *text;                        /* Text to display in button (malloc'ed)
        !            49:                                 * or NULL. */
        !            50:     int textLength;            /* # of characters in text. */
        !            51:     int underline;             /* Index of character to underline. */
        !            52:     char *textVarName;         /* Name of variable (malloc'ed) or NULL.
        !            53:                                 * If non-NULL, button displays the contents
        !            54:                                 * of this variable. */
        !            55:     Pixmap bitmap;             /* Bitmap to display or None.  If not None
        !            56:                                 * then text and textVar and underline
        !            57:                                 * are ignored. */
        !            58: 
        !            59:     /*
        !            60:      * Information used when displaying widget:
        !            61:      */
        !            62: 
        !            63:     Tk_Uid state;              /* State of button for display purposes:
        !            64:                                 * normal, active, or disabled. */
        !            65:     Tk_3DBorder normalBorder;  /* Structure used to draw 3-D
        !            66:                                 * border and background when window
        !            67:                                 * isn't active.  NULL means no such
        !            68:                                 * border exists. */
        !            69:     Tk_3DBorder activeBorder;  /* Structure used to draw 3-D
        !            70:                                 * border and background when window
        !            71:                                 * is active.  NULL means no such
        !            72:                                 * border exists. */
        !            73:     int borderWidth;           /* Width of border. */
        !            74:     int relief;                        /* 3-d effect: TK_RELIEF_RAISED, etc. */
        !            75:     XFontStruct *fontPtr;      /* Information about text font, or NULL. */
        !            76:     XColor *normalFg;          /* Foreground color in normal mode. */
        !            77:     XColor *activeFg;          /* Foreground color in active mode.  NULL
        !            78:                                 * means use normalFg instead. */
        !            79:     XColor *disabledFg;                /* Foreground color when disabled.  NULL
        !            80:                                 * means use normalFg with a 50% stipple
        !            81:                                 * instead. */
        !            82:     GC normalTextGC;           /* GC for drawing text in normal mode. */
        !            83:     GC activeTextGC;           /* GC for drawing text in active mode (NULL
        !            84:                                 * means use normalTextGC). */
        !            85:     Pixmap gray;               /* Pixmap for displaying disabled text/icon if
        !            86:                                 * disabledFg is NULL. */
        !            87:     GC disabledGC;             /* Used to produce disabled effect.  If
        !            88:                                 * disabledFg isn't NULL, this GC is used to
        !            89:                                 * draw button text or icon.  Otherwise
        !            90:                                 * text or icon is drawn with normalGC and
        !            91:                                 * this GC is used to stipple background
        !            92:                                 * across it. */
        !            93:     int leftBearing;           /* Amount text sticks left from its origin,
        !            94:                                 * in pixels. */
        !            95:     int rightBearing;          /* Amount text sticks right from its origin. */
        !            96:     int width, height;         /* If > 0, these specify dimensions to request
        !            97:                                 * for window, in characters for text and in
        !            98:                                 * pixels for bitmaps.  In this case the actual
        !            99:                                 * size of the text string or bitmap is
        !           100:                                 * ignored in computing desired window size. */
        !           101:     int padX, padY;            /* Extra space around text or bitmap (pixels
        !           102:                                 * on each side). */
        !           103:     Tk_Anchor anchor;          /* Where text/bitmap should be displayed
        !           104:                                 * inside window region. */
        !           105: 
        !           106:     /*
        !           107:      * Miscellaneous information:
        !           108:      */
        !           109: 
        !           110:     Cursor cursor;             /* Current cursor for window, or None. */
        !           111:     int flags;                 /* Various flags;  see below for
        !           112:                                 * definitions. */
        !           113: } MenuButton;
        !           114: 
        !           115: /*
        !           116:  * Flag bits for buttons:
        !           117:  *
        !           118:  * REDRAW_PENDING:             Non-zero means a DoWhenIdle handler
        !           119:  *                             has already been queued to redraw
        !           120:  *                             this window.
        !           121:  * POSTED:                     Non-zero means that the menu associated
        !           122:  *                             with this button has been posted (typically
        !           123:  *                             because of an active button press).
        !           124:  */
        !           125: 
        !           126: #define REDRAW_PENDING         1
        !           127: #define POSTED                 2
        !           128: 
        !           129: /*
        !           130:  * Information used for parsing configuration specs:
        !           131:  */
        !           132: 
        !           133: static Tk_ConfigSpec configSpecs[] = {
        !           134:     {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
        !           135:        DEF_MENUBUTTON_ACTIVE_BG_COLOR, Tk_Offset(MenuButton, activeBorder),
        !           136:        TK_CONFIG_COLOR_ONLY},
        !           137:     {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
        !           138:        DEF_MENUBUTTON_ACTIVE_BG_MONO, Tk_Offset(MenuButton, activeBorder),
        !           139:        TK_CONFIG_MONO_ONLY},
        !           140:     {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
        !           141:        DEF_MENUBUTTON_ACTIVE_FG_COLOR, Tk_Offset(MenuButton, activeFg),
        !           142:        TK_CONFIG_COLOR_ONLY},
        !           143:     {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
        !           144:        DEF_MENUBUTTON_ACTIVE_FG_MONO, Tk_Offset(MenuButton, activeFg),
        !           145:        TK_CONFIG_MONO_ONLY},
        !           146:     {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor",
        !           147:        DEF_MENUBUTTON_ANCHOR, Tk_Offset(MenuButton, anchor), 0},
        !           148:     {TK_CONFIG_BORDER, "-background", "background", "Background",
        !           149:        DEF_MENUBUTTON_BG_COLOR, Tk_Offset(MenuButton, normalBorder),
        !           150:        TK_CONFIG_COLOR_ONLY},
        !           151:     {TK_CONFIG_BORDER, "-background", "background", "Background",
        !           152:        DEF_MENUBUTTON_BG_MONO, Tk_Offset(MenuButton, normalBorder),
        !           153:        TK_CONFIG_MONO_ONLY},
        !           154:     {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
        !           155:        (char *) NULL, 0, 0},
        !           156:     {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
        !           157:        (char *) NULL, 0, 0},
        !           158: #if defined(USE_XPM3)
        !           159:     {TK_CONFIG_PIXMAP, "-bitmap", "bitmap", "Bitmap",
        !           160:        DEF_MENUBUTTON_BITMAP, Tk_Offset(MenuButton, bitmap),
        !           161:        TK_CONFIG_NULL_OK},
        !           162: #else
        !           163:     {TK_CONFIG_BITMAP, "-bitmap", "bitmap", "Bitmap",
        !           164:        DEF_MENUBUTTON_BITMAP, Tk_Offset(MenuButton, bitmap),
        !           165:        TK_CONFIG_NULL_OK},
        !           166: #endif
        !           167:     {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
        !           168:        DEF_MENUBUTTON_BORDER_WIDTH, Tk_Offset(MenuButton, borderWidth), 0},
        !           169:     {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
        !           170:        DEF_MENUBUTTON_CURSOR, Tk_Offset(MenuButton, cursor),
        !           171:        TK_CONFIG_NULL_OK},
        !           172:     {TK_CONFIG_COLOR, "-disabledforeground", "disabledForeground",
        !           173:        "DisabledForeground", DEF_MENUBUTTON_DISABLED_FG_COLOR,
        !           174:        Tk_Offset(MenuButton, disabledFg),
        !           175:        TK_CONFIG_COLOR_ONLY|TK_CONFIG_NULL_OK},
        !           176:     {TK_CONFIG_COLOR, "-disabledforeground", "disabledForeground",
        !           177:        "DisabledForeground", DEF_MENUBUTTON_DISABLED_FG_MONO,
        !           178:        Tk_Offset(MenuButton, disabledFg),
        !           179:        TK_CONFIG_MONO_ONLY|TK_CONFIG_NULL_OK},
        !           180:     {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
        !           181:        (char *) NULL, 0, 0},
        !           182:     {TK_CONFIG_FONT, "-font", "font", "Font",
        !           183:        DEF_MENUBUTTON_FONT, Tk_Offset(MenuButton, fontPtr), 0},
        !           184:     {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
        !           185:        DEF_MENUBUTTON_FG, Tk_Offset(MenuButton, normalFg), 0},
        !           186:     {TK_CONFIG_INT, "-height", "height", "Height",
        !           187:        DEF_MENUBUTTON_HEIGHT, Tk_Offset(MenuButton, height), 0},
        !           188:     {TK_CONFIG_STRING, "-menu", "menu", "Menu",
        !           189:        DEF_MENUBUTTON_MENU, Tk_Offset(MenuButton, menuName), 0},
        !           190:     {TK_CONFIG_PIXELS, "-padx", "padX", "Pad",
        !           191:        DEF_MENUBUTTON_PADX, Tk_Offset(MenuButton, padX), 0},
        !           192:     {TK_CONFIG_PIXELS, "-pady", "padY", "Pad",
        !           193:        DEF_MENUBUTTON_PADY, Tk_Offset(MenuButton, padY), 0},
        !           194:     {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
        !           195:        DEF_MENUBUTTON_RELIEF, Tk_Offset(MenuButton, relief), 0},
        !           196:     {TK_CONFIG_UID, "-state", "state", "State",
        !           197:        DEF_MENUBUTTON_STATE, Tk_Offset(MenuButton, state), 0},
        !           198:     {TK_CONFIG_STRING, "-text", "text", "Text",
        !           199:        DEF_MENUBUTTON_TEXT, Tk_Offset(MenuButton, text), 0},
        !           200:     {TK_CONFIG_STRING, "-textvariable", "textVariable", "Variable",
        !           201:        DEF_MENUBUTTON_TEXT_VARIABLE, Tk_Offset(MenuButton, textVarName),
        !           202:        TK_CONFIG_NULL_OK},
        !           203:     {TK_CONFIG_INT, "-underline", "underline", "Underline",
        !           204:        DEF_MENUBUTTON_UNDERLINE, Tk_Offset(MenuButton, underline), 0},
        !           205:     {TK_CONFIG_UID, "-variable", "variable", "Variable",
        !           206:        DEF_MENUBUTTON_VARIABLE, Tk_Offset(MenuButton, varName), 0},
        !           207:     {TK_CONFIG_INT, "-width", "width", "Width",
        !           208:        DEF_MENUBUTTON_WIDTH, Tk_Offset(MenuButton, width), 0},
        !           209:     {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
        !           210:        (char *) NULL, 0, 0}
        !           211: };
        !           212: 
        !           213: /*
        !           214:  * Forward declarations for procedures defined later in this file:
        !           215:  */
        !           216: 
        !           217: static void            ComputeMenuButtonGeometry _ANSI_ARGS_((
        !           218:                            MenuButton *mbPtr));
        !           219: static void            MenuButtonEventProc _ANSI_ARGS_((ClientData clientData,
        !           220:                            XEvent *eventPtr));
        !           221: static char *          MenuButtonTextVarProc _ANSI_ARGS_((
        !           222:                            ClientData clientData, Tcl_Interp *interp,
        !           223:                            char *name1, char *name2, int flags));
        !           224: static char *          MenuButtonVarProc _ANSI_ARGS_((ClientData clientData,
        !           225:                            Tcl_Interp *interp, char *name1, char *name2,
        !           226:                            int flags));
        !           227: static int             MenuButtonWidgetCmd _ANSI_ARGS_((ClientData clientData,
        !           228:                            Tcl_Interp *interp, int argc, char **argv));
        !           229: static int             ConfigureMenuButton _ANSI_ARGS_((Tcl_Interp *interp,
        !           230:                            MenuButton *mbPtr, int argc, char **argv,
        !           231:                            int flags));
        !           232: static void            DestroyMenuButton _ANSI_ARGS_((ClientData clientData));
        !           233: static void            DisplayMenuButton _ANSI_ARGS_((ClientData clientData));
        !           234: 
        !           235: /*
        !           236:  *--------------------------------------------------------------
        !           237:  *
        !           238:  * Tk_MenubuttonCmd --
        !           239:  *
        !           240:  *     This procedure is invoked to process the "button", "label",
        !           241:  *     "radiobutton", and "checkbutton" Tcl commands.  See the
        !           242:  *     user documentation for details on what it does.
        !           243:  *
        !           244:  * Results:
        !           245:  *     A standard Tcl result.
        !           246:  *
        !           247:  * Side effects:
        !           248:  *     See the user documentation.
        !           249:  *
        !           250:  *--------------------------------------------------------------
        !           251:  */
        !           252: 
        !           253: int
        !           254: Tk_MenubuttonCmd(clientData, interp, argc, argv)
        !           255:     ClientData clientData;     /* Main window associated with
        !           256:                                 * interpreter. */
        !           257:     Tcl_Interp *interp;                /* Current interpreter. */
        !           258:     int argc;                  /* Number of arguments. */
        !           259:     char **argv;               /* Argument strings. */
        !           260: {
        !           261:     register MenuButton *mbPtr;
        !           262:     Tk_Window tkwin = (Tk_Window) clientData;
        !           263:     Tk_Window new;
        !           264: 
        !           265:     if (argc < 2) {
        !           266:        Tcl_AppendResult(interp, "wrong # args:  should be \"",
        !           267:                argv[0], " pathName ?options?\"", (char *) NULL);
        !           268:        return TCL_ERROR;
        !           269:     }
        !           270: 
        !           271:     /*
        !           272:      * Create the new window.
        !           273:      */
        !           274: 
        !           275:     new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
        !           276:     if (new == NULL) {
        !           277:        return TCL_ERROR;
        !           278:     }
        !           279: 
        !           280:     /*
        !           281:      * Initialize the data structure for the button.
        !           282:      */
        !           283: 
        !           284:     mbPtr = (MenuButton *) ckalloc(sizeof(MenuButton));
        !           285:     mbPtr->tkwin = new;
        !           286:     mbPtr->interp = interp;
        !           287:     mbPtr->menuName = NULL;
        !           288:     mbPtr->varName = NULL;
        !           289:     mbPtr->text = NULL;
        !           290:     mbPtr->underline = -1;
        !           291:     mbPtr->textVarName = NULL;
        !           292:     mbPtr->bitmap = None;
        !           293:     mbPtr->state = tkNormalUid;
        !           294:     mbPtr->normalBorder = NULL;
        !           295:     mbPtr->activeBorder = NULL;
        !           296:     mbPtr->borderWidth = 0;
        !           297:     mbPtr->relief = TK_RELIEF_FLAT;
        !           298:     mbPtr->fontPtr = NULL;
        !           299:     mbPtr->normalFg = NULL;
        !           300:     mbPtr->activeFg = NULL;
        !           301:     mbPtr->disabledFg = NULL;
        !           302:     mbPtr->normalTextGC = NULL;
        !           303:     mbPtr->activeTextGC = NULL;
        !           304:     mbPtr->gray = None;
        !           305:     mbPtr->disabledGC = NULL;
        !           306:     mbPtr->cursor = None;
        !           307:     mbPtr->flags = 0;
        !           308: 
        !           309:     Tk_SetClass(mbPtr->tkwin, "Menubutton");
        !           310:     Tk_CreateEventHandler(mbPtr->tkwin, ExposureMask|StructureNotifyMask,
        !           311:            MenuButtonEventProc, (ClientData) mbPtr);
        !           312:     Tcl_CreateCommand(interp, Tk_PathName(mbPtr->tkwin), MenuButtonWidgetCmd,
        !           313:            (ClientData) mbPtr, (void (*)()) NULL);
        !           314:     if (ConfigureMenuButton(interp, mbPtr, argc-2, argv+2, 0) != TCL_OK) {
        !           315:        Tk_DestroyWindow(mbPtr->tkwin);
        !           316:        return TCL_ERROR;
        !           317:     }
        !           318: 
        !           319:     interp->result = Tk_PathName(mbPtr->tkwin);
        !           320:     return TCL_OK;
        !           321: }
        !           322: 
        !           323: /*
        !           324:  *--------------------------------------------------------------
        !           325:  *
        !           326:  * MenuButtonWidgetCmd --
        !           327:  *
        !           328:  *     This procedure is invoked to process the Tcl command
        !           329:  *     that corresponds to a widget managed by this module.
        !           330:  *     See the user documentation for details on what it does.
        !           331:  *
        !           332:  * Results:
        !           333:  *     A standard Tcl result.
        !           334:  *
        !           335:  * Side effects:
        !           336:  *     See the user documentation.
        !           337:  *
        !           338:  *--------------------------------------------------------------
        !           339:  */
        !           340: 
        !           341: static int
        !           342: MenuButtonWidgetCmd(clientData, interp, argc, argv)
        !           343:     ClientData clientData;     /* Information about button widget. */
        !           344:     Tcl_Interp *interp;                /* Current interpreter. */
        !           345:     int argc;                  /* Number of arguments. */
        !           346:     char **argv;               /* Argument strings. */
        !           347: {
        !           348:     register MenuButton *mbPtr = (MenuButton *) clientData;
        !           349:     int result = TCL_OK;
        !           350:     int length;
        !           351:     char c;
        !           352: 
        !           353:     if (argc < 2) {
        !           354:        Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
        !           355:                " option ?arg arg ...?\"", (char *) NULL);
        !           356:        return TCL_ERROR;
        !           357:     }
        !           358:     Tk_Preserve((ClientData) mbPtr);
        !           359:     c = argv[1][0];
        !           360:     length = strlen(argv[1]);
        !           361:     if ((c == 'a') && (strncmp(argv[1], "activate", length) == 0)) {
        !           362:        if (argc > 2) {
        !           363:            Tcl_AppendResult(interp, "wrong # args: should be \"",
        !           364:                    argv[0], " activate\"", (char *) NULL);
        !           365:            goto error;
        !           366:        }
        !           367:        if (mbPtr->state != tkDisabledUid) {
        !           368:            mbPtr->state = tkActiveUid;
        !           369:            Tk_SetBackgroundFromBorder(mbPtr->tkwin, mbPtr->activeBorder);
        !           370:            goto redisplay;
        !           371:        }
        !           372:     } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) {
        !           373:        if (argc == 2) {
        !           374:            result = Tk_ConfigureInfo(interp, mbPtr->tkwin, configSpecs,
        !           375:                    (char *) mbPtr, (char *) NULL, 0);
        !           376:        } else if (argc == 3) {
        !           377:            result = Tk_ConfigureInfo(interp, mbPtr->tkwin, configSpecs,
        !           378:                    (char *) mbPtr, argv[2], 0);
        !           379:        } else {
        !           380:            result = ConfigureMenuButton(interp, mbPtr, argc-2, argv+2,
        !           381:                    TK_CONFIG_ARGV_ONLY);
        !           382:        }
        !           383:     } else if ((c == 'd') && (strncmp(argv[1], "deactivate", length) == 0)) {
        !           384:        if (argc > 2) {
        !           385:            Tcl_AppendResult(interp, "wrong # args: should be \"",
        !           386:                    argv[0], " deactivate\"", (char *) NULL);
        !           387:            goto error;
        !           388:        }
        !           389:        if (mbPtr->state != tkDisabledUid) {
        !           390:            mbPtr->state = tkNormalUid;
        !           391:            Tk_SetBackgroundFromBorder(mbPtr->tkwin, mbPtr->normalBorder);
        !           392:            goto redisplay;
        !           393:        }
        !           394:     } else if ((c == 'p') && (strncmp(argv[1], "post", length) == 0)) {
        !           395:        if (argc > 2) {
        !           396:            Tcl_AppendResult(interp, "wrong # args: should be \"",
        !           397:                    argv[0], " post\"", (char *) NULL);
        !           398:            goto error;
        !           399:        }
        !           400:        if ((mbPtr->flags & POSTED) || (mbPtr->menuName == NULL)
        !           401:                || (mbPtr->state == tkDisabledUid)) {
        !           402:            goto done;
        !           403:        }
        !           404: 
        !           405:        /*
        !           406:         * Store the name of the posted menu into the associated variable.
        !           407:         * This will cause any other menu posted via that variable to
        !           408:         * unpost itself and will cause this menu to post itself.
        !           409:         */
        !           410: 
        !           411:        Tcl_SetVar(interp, mbPtr->varName, Tk_PathName(mbPtr->tkwin),
        !           412:                TCL_GLOBAL_ONLY);
        !           413:     } else if ((c == 'u') && (strncmp(argv[1], "unpost", length) == 0)) {
        !           414:        if (argc > 2) {
        !           415:            Tcl_AppendResult(interp, "wrong # args: should be \"",
        !           416:                    argv[0], " unpost\"", (char *) NULL);
        !           417:            goto error;
        !           418:        }
        !           419: 
        !           420:        /*
        !           421:         * The one-liner below looks simple, but it isn't.  This code
        !           422:         * does the right thing even if this menu isn't posted anymore,
        !           423:         * but some other variable associated with the same variable
        !           424:         * is posted instead:  it unposts whatever is posted.  This
        !           425:         * approach is necessary because at present ButtonRelease
        !           426:         * events go to the menu button where the mouse button was
        !           427:         * first pressed;  this may not be the same menu button that's
        !           428:         * currently active.
        !           429:         */
        !           430:        Tcl_SetVar(interp, mbPtr->varName, "", TCL_GLOBAL_ONLY);
        !           431:     } else {
        !           432:        Tcl_AppendResult(interp, "bad option \"", argv[1],
        !           433:                "\":  must be activate, configure, deactivate, ",
        !           434:                "post, or unpost", (char *) NULL);
        !           435:        goto error;
        !           436:     }
        !           437:     done:
        !           438:     Tk_Release((ClientData) mbPtr);
        !           439:     return result;
        !           440: 
        !           441:     redisplay:
        !           442:     if (Tk_IsMapped(mbPtr->tkwin) && !(mbPtr->flags & REDRAW_PENDING)) {
        !           443:        Tk_DoWhenIdle(DisplayMenuButton, (ClientData) mbPtr);
        !           444:        mbPtr->flags |= REDRAW_PENDING;
        !           445:     }
        !           446:     goto done;
        !           447: 
        !           448:     error:
        !           449:     Tk_Release((ClientData) mbPtr);
        !           450:     return TCL_ERROR;
        !           451: }
        !           452: 
        !           453: /*
        !           454:  *----------------------------------------------------------------------
        !           455:  *
        !           456:  * DestroyMenuButton --
        !           457:  *
        !           458:  *     This procedure is invoked to recycle all of the resources
        !           459:  *     associated with a button widget.  It is invoked as a
        !           460:  *     when-idle handler in order to make sure that there is no
        !           461:  *     other use of the button pending at the time of the deletion.
        !           462:  *
        !           463:  * Results:
        !           464:  *     None.
        !           465:  *
        !           466:  * Side effects:
        !           467:  *     Everything associated with the widget is freed up.
        !           468:  *
        !           469:  *----------------------------------------------------------------------
        !           470:  */
        !           471: 
        !           472: static void
        !           473: DestroyMenuButton(clientData)
        !           474:     ClientData clientData;     /* Info about button widget. */
        !           475: {
        !           476:     register MenuButton *mbPtr = (MenuButton *) clientData;
        !           477:     if (mbPtr->menuName != NULL) {
        !           478:         ckfree(mbPtr->menuName);
        !           479:     }
        !           480:     if (mbPtr->varName != NULL) {
        !           481:        Tcl_UntraceVar(mbPtr->interp, mbPtr->varName,
        !           482:                TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
        !           483:                MenuButtonVarProc, (ClientData) mbPtr);
        !           484:     }
        !           485:     if (mbPtr->text != NULL) {
        !           486:        ckfree(mbPtr->text);
        !           487:     }
        !           488:     if (mbPtr->textVarName != NULL) {
        !           489:        Tcl_UntraceVar(mbPtr->interp, mbPtr->textVarName,
        !           490:                TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
        !           491:                MenuButtonTextVarProc, (ClientData) mbPtr);
        !           492:        ckfree(mbPtr->textVarName);
        !           493:     }
        !           494:     if (mbPtr->bitmap != None) {
        !           495: #if defined(USE_XPM3)
        !           496:         Tk_FreePixmap(mbPtr->bitmap);
        !           497: #else
        !           498:         Tk_FreeBitmap(mbPtr->bitmap);
        !           499: #endif
        !           500:     }
        !           501:     if (mbPtr->normalBorder != NULL) {
        !           502:        Tk_Free3DBorder(mbPtr->normalBorder);
        !           503:     }
        !           504:     if (mbPtr->activeBorder != NULL) {
        !           505:        Tk_Free3DBorder(mbPtr->activeBorder);
        !           506:     }
        !           507:     if (mbPtr->fontPtr != NULL) {
        !           508:        Tk_FreeFontStruct(mbPtr->fontPtr);
        !           509:     }
        !           510:     if (mbPtr->normalFg != NULL) {
        !           511:        Tk_FreeColor(mbPtr->normalFg);
        !           512:     }
        !           513:     if (mbPtr->activeFg != NULL) {
        !           514:        Tk_FreeColor(mbPtr->activeFg);
        !           515:     }
        !           516:     if (mbPtr->disabledFg != NULL) {
        !           517:        Tk_FreeColor(mbPtr->disabledFg);
        !           518:     }
        !           519:     if (mbPtr->normalTextGC != None) {
        !           520:        Tk_FreeGC(mbPtr->normalTextGC);
        !           521:     }
        !           522:     if (mbPtr->activeTextGC != None) {
        !           523:        Tk_FreeGC(mbPtr->activeTextGC);
        !           524:     }
        !           525:     if (mbPtr->gray != None) {
        !           526:        Tk_FreeBitmap(mbPtr->gray);
        !           527:     }
        !           528:     if (mbPtr->disabledGC != None) {
        !           529:        Tk_FreeGC(mbPtr->disabledGC);
        !           530:     }
        !           531:     if (mbPtr->cursor != None) {
        !           532:        Tk_FreeCursor(mbPtr->cursor);
        !           533:     }
        !           534:     ckfree((char *) mbPtr);
        !           535: }
        !           536: 
        !           537: /*
        !           538:  *----------------------------------------------------------------------
        !           539:  *
        !           540:  * ConfigureMenuButton --
        !           541:  *
        !           542:  *     This procedure is called to process an argv/argc list, plus
        !           543:  *     the Tk option database, in order to configure (or
        !           544:  *     reconfigure) a menubutton widget.
        !           545:  *
        !           546:  * Results:
        !           547:  *     The return value is a standard Tcl result.  If TCL_ERROR is
        !           548:  *     returned, then interp->result contains an error message.
        !           549:  *
        !           550:  * Side effects:
        !           551:  *     Configuration information, such as text string, colors, font,
        !           552:  *     etc. get set for mbPtr;  old resources get freed, if there
        !           553:  *     were any.  The menubutton is redisplayed.
        !           554:  *
        !           555:  *----------------------------------------------------------------------
        !           556:  */
        !           557: 
        !           558: static int
        !           559: ConfigureMenuButton(interp, mbPtr, argc, argv, flags)
        !           560:     Tcl_Interp *interp;                /* Used for error reporting. */
        !           561:     register MenuButton *mbPtr;        /* Information about widget;  may or may
        !           562:                                 * not already have values for some fields. */
        !           563:     int argc;                  /* Number of valid entries in argv. */
        !           564:     char **argv;               /* Arguments. */
        !           565:     int flags;                 /* Flags to pass to Tk_ConfigureWidget. */
        !           566: {
        !           567:     XGCValues gcValues;
        !           568:     GC newGC;
        !           569:     unsigned long mask;
        !           570:     int result;
        !           571:     Tk_Uid oldGroup;
        !           572:     char *value;
        !           573: 
        !           574:     /*
        !           575:      * Eliminate any existing traces on variables monitored by the button.
        !           576:      */
        !           577: 
        !           578:     if (mbPtr->varName != NULL) {
        !           579:        Tcl_UntraceVar(interp, mbPtr->varName,
        !           580:                TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
        !           581:                MenuButtonVarProc, (ClientData) mbPtr);
        !           582:     }
        !           583:     if (mbPtr->textVarName != NULL) {
        !           584:        Tcl_UntraceVar(interp, mbPtr->textVarName,
        !           585:                TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
        !           586:                MenuButtonTextVarProc, (ClientData) mbPtr);
        !           587:     }
        !           588: 
        !           589:     oldGroup = mbPtr->varName;
        !           590:     result = Tk_ConfigureWidget(interp, mbPtr->tkwin, configSpecs,
        !           591:            argc, argv, (char *) mbPtr, flags);
        !           592:     if (oldGroup != mbPtr->varName) {
        !           593:        Tk_UnshareEvents(mbPtr->tkwin, oldGroup);
        !           594:        Tk_ShareEvents(mbPtr->tkwin, mbPtr->varName);
        !           595:     }
        !           596:     if (result != TCL_OK) {
        !           597:        return TCL_ERROR;
        !           598:     }
        !           599: 
        !           600:     /*
        !           601:      * A few options need special processing, such as setting the
        !           602:      * background from a 3-D border, or filling in complicated
        !           603:      * defaults that couldn't be specified to Tk_ConfigureWidget.
        !           604:      */
        !           605: 
        !           606:     if (mbPtr->state == tkActiveUid) {
        !           607:        Tk_SetBackgroundFromBorder(mbPtr->tkwin, mbPtr->activeBorder);
        !           608:     } else {
        !           609:        Tk_SetBackgroundFromBorder(mbPtr->tkwin, mbPtr->normalBorder);
        !           610:        if ((mbPtr->state != tkNormalUid) && (mbPtr->state != tkDisabledUid)) {
        !           611:            Tcl_AppendResult(interp, "bad state value \"", mbPtr->state,
        !           612:                    "\":  must be normal, active, or disabled", (char *) NULL);
        !           613:            mbPtr->state = tkNormalUid;
        !           614:            return TCL_ERROR;
        !           615:        }
        !           616:     }
        !           617: 
        !           618:     gcValues.font = mbPtr->fontPtr->fid;
        !           619:     gcValues.foreground = mbPtr->normalFg->pixel;
        !           620:     gcValues.background = Tk_3DBorderColor(mbPtr->normalBorder)->pixel;
        !           621: 
        !           622:     /*
        !           623:      * Note: GraphicsExpose events are disabled in GC's because they're
        !           624:      * used to copy stuff from an off-screen pixmap onto the screen (we know
        !           625:      * that there's no problem with obscured areas).
        !           626:      */
        !           627: 
        !           628:     gcValues.graphics_exposures = False;
        !           629:     newGC = Tk_GetGC(mbPtr->tkwin,
        !           630:            GCForeground|GCBackground|GCFont|GCGraphicsExposures, &gcValues);
        !           631:     if (mbPtr->normalTextGC != None) {
        !           632:        Tk_FreeGC(mbPtr->normalTextGC);
        !           633:     }
        !           634:     mbPtr->normalTextGC = newGC;
        !           635: 
        !           636:     gcValues.font = mbPtr->fontPtr->fid;
        !           637:     gcValues.foreground = mbPtr->activeFg->pixel;
        !           638:     gcValues.background = Tk_3DBorderColor(mbPtr->activeBorder)->pixel;
        !           639:     newGC = Tk_GetGC(mbPtr->tkwin, GCForeground|GCBackground|GCFont,
        !           640:            &gcValues);
        !           641:     if (mbPtr->activeTextGC != None) {
        !           642:        Tk_FreeGC(mbPtr->activeTextGC);
        !           643:     }
        !           644:     mbPtr->activeTextGC = newGC;
        !           645: 
        !           646:     gcValues.font = mbPtr->fontPtr->fid;
        !           647:     gcValues.background = Tk_3DBorderColor(mbPtr->normalBorder)->pixel;
        !           648:     if (mbPtr->disabledFg != NULL) {
        !           649:        gcValues.foreground = mbPtr->disabledFg->pixel;
        !           650:        mask = GCForeground|GCBackground|GCFont;
        !           651:     } else {
        !           652:        gcValues.foreground = gcValues.background;
        !           653:        if (mbPtr->gray == None) {
        !           654:            mbPtr->gray = Tk_GetBitmap(interp, mbPtr->tkwin,
        !           655:                    Tk_GetUid("gray50"));
        !           656:            if (mbPtr->gray == None) {
        !           657:                return TCL_ERROR;
        !           658:            }
        !           659:        }
        !           660:        gcValues.fill_style = FillStippled;
        !           661:        gcValues.stipple = mbPtr->gray;
        !           662:        mask = GCForeground|GCFillStyle|GCStipple;
        !           663:     }
        !           664:     newGC = Tk_GetGC(mbPtr->tkwin, mask, &gcValues);
        !           665:     if (mbPtr->disabledGC != None) {
        !           666:        Tk_FreeGC(mbPtr->disabledGC);
        !           667:     }
        !           668:     mbPtr->disabledGC = newGC;
        !           669: 
        !           670:     if (mbPtr->padX < 0) {
        !           671:        mbPtr->padX = 0;
        !           672:     }
        !           673:     if (mbPtr->padY < 0) {
        !           674:        mbPtr->padY = 0;
        !           675:     }
        !           676: 
        !           677:     /*
        !           678:      * Set up a trace on the menu button's variable, then initialize
        !           679:      * the variable if it doesn't already exist, so that it can be
        !           680:      * accessed immediately from Tcl code without fear of
        !           681:      * "nonexistent variable" errors.
        !           682:      */
        !           683: 
        !           684:     value = Tcl_GetVar(interp, mbPtr->varName, TCL_GLOBAL_ONLY);
        !           685:     if (value == NULL) {
        !           686:        Tcl_SetVar(interp, mbPtr->varName, "", TCL_GLOBAL_ONLY);
        !           687:     }
        !           688:     Tcl_TraceVar(interp, mbPtr->varName,
        !           689:            TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
        !           690:            MenuButtonVarProc, (ClientData) mbPtr);
        !           691: 
        !           692:     /*
        !           693:      * Set up a trace on the variable that determines what's displayed
        !           694:      * in the menu button, if such a trace has been requested.
        !           695:      */
        !           696: 
        !           697:     if ((mbPtr->bitmap == None) && (mbPtr->textVarName != NULL)) {
        !           698:        char *value;
        !           699: 
        !           700:        value = Tcl_GetVar(interp, mbPtr->textVarName, TCL_GLOBAL_ONLY);
        !           701:        if (value == NULL) {
        !           702:            Tcl_SetVar(interp, mbPtr->textVarName, mbPtr->text,
        !           703:                    TCL_GLOBAL_ONLY);
        !           704:        } else {
        !           705:            if (mbPtr->text != NULL) {
        !           706:                ckfree(mbPtr->text);
        !           707:            }
        !           708:            mbPtr->text = ckalloc((unsigned) (strlen(value) + 1));
        !           709:            strcpy(mbPtr->text, value);
        !           710:        }
        !           711:        Tcl_TraceVar(interp, mbPtr->textVarName,
        !           712:                TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
        !           713:                MenuButtonTextVarProc, (ClientData) mbPtr);
        !           714:     }
        !           715: 
        !           716:     /*
        !           717:      * Recompute the geometry for the button.
        !           718:      */
        !           719: 
        !           720:     ComputeMenuButtonGeometry(mbPtr);
        !           721: 
        !           722:     /*
        !           723:      * Lastly, arrange for the button to be redisplayed.
        !           724:      */
        !           725: 
        !           726:     if (Tk_IsMapped(mbPtr->tkwin) && !(mbPtr->flags & REDRAW_PENDING)) {
        !           727:        Tk_DoWhenIdle(DisplayMenuButton, (ClientData) mbPtr);
        !           728:        mbPtr->flags |= REDRAW_PENDING;
        !           729:     }
        !           730: 
        !           731:     return TCL_OK;
        !           732: }
        !           733: 
        !           734: /*
        !           735:  *----------------------------------------------------------------------
        !           736:  *
        !           737:  * DisplayMenuButton --
        !           738:  *
        !           739:  *     This procedure is invoked to display a menubutton widget.
        !           740:  *
        !           741:  * Results:
        !           742:  *     None.
        !           743:  *
        !           744:  * Side effects:
        !           745:  *     Commands are output to X to display the menubutton in its
        !           746:  *     current mode.
        !           747:  *
        !           748:  *----------------------------------------------------------------------
        !           749:  */
        !           750: 
        !           751: static void
        !           752: DisplayMenuButton(clientData)
        !           753:     ClientData clientData;     /* Information about widget. */
        !           754: {
        !           755:     register MenuButton *mbPtr = (MenuButton *) clientData;
        !           756:     GC gc;
        !           757:     Tk_3DBorder border;
        !           758:     Pixmap pixmap;
        !           759:     int x = 0;                 /* Initialization needed only to stop
        !           760:                                 * compiler warning. */
        !           761:     int y;
        !           762:     register Tk_Window tkwin = mbPtr->tkwin;
        !           763: 
        !           764:     mbPtr->flags &= ~REDRAW_PENDING;
        !           765:     if ((mbPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
        !           766:        return;
        !           767:     }
        !           768: 
        !           769:     if ((mbPtr->state == tkDisabledUid) && (mbPtr->disabledFg != NULL)) {
        !           770:        gc = mbPtr->disabledGC;
        !           771:        border = mbPtr->normalBorder;
        !           772:     } else if (mbPtr->state == tkActiveUid) {
        !           773:        gc = mbPtr->activeTextGC;
        !           774:        border = mbPtr->activeBorder;
        !           775:     } else {
        !           776:        gc = mbPtr->normalTextGC;
        !           777:        border = mbPtr->normalBorder;
        !           778:     }
        !           779: 
        !           780:     /*
        !           781:      * In order to avoid screen flashes, this procedure redraws
        !           782:      * the menu button in a pixmap, then copies the pixmap to the
        !           783:      * screen in a single operation.  This means that there's no
        !           784:      * point in time where the on-sreen image has been cleared.
        !           785:      */
        !           786: 
        !           787:     pixmap = XCreatePixmap(Tk_Display(tkwin), Tk_WindowId(tkwin),
        !           788:            Tk_Width(tkwin), Tk_Height(tkwin),
        !           789:            Tk_DefaultDepth(Tk_Screen(tkwin)));
        !           790:     Tk_Fill3DRectangle(Tk_Display(tkwin), pixmap, border,
        !           791:            0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 0, TK_RELIEF_FLAT);
        !           792: 
        !           793:     /*
        !           794:      * Display bitmap or text for button.
        !           795:      */
        !           796: 
        !           797:     if (mbPtr->bitmap != None) {
        !           798:        unsigned int width, height;
        !           799: 
        !           800: #if defined(USE_XPM3)
        !           801:        Tk_SizeOfPixmap(mbPtr->bitmap, &width, &height);
        !           802: #else
        !           803:        Tk_SizeOfBitmap(mbPtr->bitmap, &width, &height);
        !           804: #endif
        !           805:        switch (mbPtr->anchor) {
        !           806:            case TK_ANCHOR_NW: case TK_ANCHOR_W: case TK_ANCHOR_SW:
        !           807:                x += mbPtr->borderWidth + mbPtr->padX;
        !           808:                break;
        !           809:            case TK_ANCHOR_N: case TK_ANCHOR_CENTER: case TK_ANCHOR_S:
        !           810:                x += (Tk_Width(tkwin) - width)/2;
        !           811:                break;
        !           812:            default:
        !           813:                x += Tk_Width(tkwin) - mbPtr->borderWidth - mbPtr->padX
        !           814:                        - width;
        !           815:                break;
        !           816:        }
        !           817:        switch (mbPtr->anchor) {
        !           818:            case TK_ANCHOR_NW: case TK_ANCHOR_N: case TK_ANCHOR_NE:
        !           819:                y = mbPtr->borderWidth + mbPtr->padY;
        !           820:                break;
        !           821:            case TK_ANCHOR_W: case TK_ANCHOR_CENTER: case TK_ANCHOR_E:
        !           822:                y = (Tk_Height(tkwin) - height)/2;
        !           823:                break;
        !           824:            default:
        !           825:                y = Tk_Height(tkwin) - mbPtr->borderWidth - mbPtr->padY
        !           826:                        - height;
        !           827:                break;
        !           828:        }
        !           829: #if defined(USE_XPM3)        
        !           830:        XCopyArea(Tk_Display(tkwin), mbPtr->bitmap, pixmap,
        !           831:                gc, 0, 0, width, height, x, y);
        !           832: #else
        !           833:        XCopyPlane(Tk_Display(tkwin), mbPtr->bitmap, pixmap,
        !           834:                gc, 0, 0, width, height, x, y, 1);
        !           835: #endif
        !           836:     } else {
        !           837:        switch (mbPtr->anchor) {
        !           838:            case TK_ANCHOR_NW: case TK_ANCHOR_W: case TK_ANCHOR_SW:
        !           839:                x = mbPtr->borderWidth + mbPtr->padX + mbPtr->leftBearing;
        !           840:                break;
        !           841:            case TK_ANCHOR_N: case TK_ANCHOR_CENTER: case TK_ANCHOR_S:
        !           842:                x = (Tk_Width(tkwin) + mbPtr->leftBearing
        !           843:                        - mbPtr->rightBearing)/2;
        !           844:                break;
        !           845:            default:
        !           846:                x = Tk_Width(tkwin) - mbPtr->borderWidth - mbPtr->padX
        !           847:                        - mbPtr->rightBearing;
        !           848:                break;
        !           849:        }
        !           850:        switch (mbPtr->anchor) {
        !           851:            case TK_ANCHOR_NW: case TK_ANCHOR_N: case TK_ANCHOR_NE:
        !           852:                y = mbPtr->borderWidth + mbPtr->fontPtr->ascent
        !           853:                        + mbPtr->padY;
        !           854:                break;
        !           855:            case TK_ANCHOR_W: case TK_ANCHOR_CENTER: case TK_ANCHOR_E:
        !           856:                y = (Tk_Height(tkwin) + mbPtr->fontPtr->ascent
        !           857:                        - mbPtr->fontPtr->descent)/2;
        !           858:                break;
        !           859:            default:
        !           860:                y = Tk_Height(tkwin) - mbPtr->borderWidth - mbPtr->padY
        !           861:                        - mbPtr->fontPtr->descent;
        !           862:                break;
        !           863:        }
        !           864:        XDrawString(Tk_Display(tkwin), pixmap, gc, x, y, mbPtr->text,
        !           865:                mbPtr->textLength);
        !           866:        if (mbPtr->underline >= 0) {
        !           867:            TkUnderlineChars(Tk_Display(tkwin), pixmap, gc, mbPtr->fontPtr,
        !           868:                    mbPtr->text, x, y, TK_NEWLINES_NOT_SPECIAL,
        !           869:                    mbPtr->underline, mbPtr->underline);
        !           870:        }
        !           871:     }
        !           872: 
        !           873:     /*
        !           874:      * If the menu button is disabled with a stipple rather than a special
        !           875:      * foreground color, generate the stippled effect.
        !           876:      */
        !           877: 
        !           878:     if ((mbPtr->state == tkDisabledUid) && (mbPtr->disabledFg == NULL)) {
        !           879:        XFillRectangle(Tk_Display(tkwin), pixmap, mbPtr->disabledGC,
        !           880:                mbPtr->borderWidth, mbPtr->borderWidth,
        !           881:                (unsigned) (Tk_Width(tkwin) - 2*mbPtr->borderWidth),
        !           882:                (unsigned) (Tk_Height(tkwin) - 2*mbPtr->borderWidth));
        !           883:     }
        !           884: 
        !           885:     /*
        !           886:      * Draw the border last.  This way, if the menu button's contents
        !           887:      * overflow onto the border they'll be covered up by the border.
        !           888:      */
        !           889: 
        !           890:     if (mbPtr->relief != TK_RELIEF_FLAT) {
        !           891:        Tk_Draw3DRectangle(Tk_Display(tkwin), pixmap, border,
        !           892:                0, 0, Tk_Width(tkwin), Tk_Height(tkwin),
        !           893:                mbPtr->borderWidth, mbPtr->relief);
        !           894:     }
        !           895: 
        !           896:     /*
        !           897:      * Copy the information from the off-screen pixmap onto the screen,
        !           898:      * then delete the pixmap.
        !           899:      */
        !           900: 
        !           901:     XCopyArea(Tk_Display(tkwin), pixmap, Tk_WindowId(tkwin),
        !           902:        mbPtr->normalTextGC, 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 0, 0);
        !           903:     XFreePixmap(Tk_Display(tkwin), pixmap);
        !           904: }
        !           905: 
        !           906: /*
        !           907:  *--------------------------------------------------------------
        !           908:  *
        !           909:  * MenuButtonEventProc --
        !           910:  *
        !           911:  *     This procedure is invoked by the Tk dispatcher for various
        !           912:  *     events on buttons.
        !           913:  *
        !           914:  * Results:
        !           915:  *     None.
        !           916:  *
        !           917:  * Side effects:
        !           918:  *     When the window gets deleted, internal structures get
        !           919:  *     cleaned up.  When it gets exposed, it is redisplayed.
        !           920:  *
        !           921:  *--------------------------------------------------------------
        !           922:  */
        !           923: 
        !           924: static void
        !           925: MenuButtonEventProc(clientData, eventPtr)
        !           926:     ClientData clientData;     /* Information about window. */
        !           927:     XEvent *eventPtr;          /* Information about event. */
        !           928: {
        !           929:     MenuButton *mbPtr = (MenuButton *) clientData;
        !           930:     if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
        !           931:        if ((mbPtr->tkwin != NULL) && !(mbPtr->flags & REDRAW_PENDING)) {
        !           932:            Tk_DoWhenIdle(DisplayMenuButton, (ClientData) mbPtr);
        !           933:            mbPtr->flags |= REDRAW_PENDING;
        !           934:        }
        !           935:     } else if (eventPtr->type == DestroyNotify) {
        !           936:        Tcl_DeleteCommand(mbPtr->interp, Tk_PathName(mbPtr->tkwin));
        !           937: 
        !           938:        /*
        !           939:         * Careful!  Must delete the event-sharing information here
        !           940:         * rather than in DestroyMenuButton.  By the time that procedure
        !           941:         * is called the tkwin may have been reused, resulting in some
        !           942:         * other window accidentally being cut off from shared events.
        !           943:         */
        !           944: 
        !           945:        Tk_UnshareEvents(mbPtr->tkwin, mbPtr->varName);
        !           946:        mbPtr->tkwin = NULL;
        !           947:        if (mbPtr->flags & REDRAW_PENDING) {
        !           948:            Tk_CancelIdleCall(DisplayMenuButton, (ClientData) mbPtr);
        !           949:        }
        !           950:        Tk_EventuallyFree((ClientData) mbPtr, DestroyMenuButton);
        !           951:     }
        !           952: }
        !           953: 
        !           954: /*
        !           955:  *----------------------------------------------------------------------
        !           956:  *
        !           957:  * ComputeMenuButtonGeometry --
        !           958:  *
        !           959:  *     After changes in a menu button's text or bitmap, this procedure
        !           960:  *     recomputes the menu button's geometry and passes this information
        !           961:  *     along to the geometry manager for the window.
        !           962:  *
        !           963:  * Results:
        !           964:  *     None.
        !           965:  *
        !           966:  * Side effects:
        !           967:  *     The menu button's window may change size.
        !           968:  *
        !           969:  *----------------------------------------------------------------------
        !           970:  */
        !           971: 
        !           972: static void
        !           973: ComputeMenuButtonGeometry(mbPtr)
        !           974:     register MenuButton *mbPtr;                /* Widget record for menu button. */
        !           975: {
        !           976:     XCharStruct bbox;
        !           977:     int dummy;
        !           978:     unsigned int width, height;
        !           979: 
        !           980:     if (mbPtr->bitmap != None) {
        !           981: #if defined(USE_XPM3)
        !           982:         Tk_SizeOfPixmap(mbPtr->bitmap, &width, &height);
        !           983: #else
        !           984:         Tk_SizeOfBitmap(mbPtr->bitmap, &width, &height);
        !           985: #endif
        !           986:        if (mbPtr->width > 0) {
        !           987:            width = mbPtr->width;
        !           988:        }
        !           989:        if (mbPtr->height > 0) {
        !           990:            height = mbPtr->height;
        !           991:        }
        !           992:     } else {
        !           993:        mbPtr->textLength = strlen(mbPtr->text);
        !           994:        XTextExtents(mbPtr->fontPtr, mbPtr->text, mbPtr->textLength,
        !           995:                &dummy, &dummy, &dummy, &bbox);
        !           996:        mbPtr->leftBearing = bbox.lbearing;
        !           997:        mbPtr->rightBearing = bbox.rbearing;
        !           998:        width = bbox.lbearing + bbox.rbearing;
        !           999:        height = mbPtr->fontPtr->ascent + mbPtr->fontPtr->descent;
        !          1000:        if (mbPtr->width > 0) {
        !          1001:            width = mbPtr->width * XTextWidth(mbPtr->fontPtr, "0", 1);
        !          1002:        }
        !          1003:        if (mbPtr->height > 0) {
        !          1004:            height *= mbPtr->height;
        !          1005:        }
        !          1006:     }
        !          1007: 
        !          1008:     width += 2*mbPtr->padX;
        !          1009:     height += 2*mbPtr->padY;
        !          1010:     Tk_GeometryRequest(mbPtr->tkwin, (int) (width + 2*mbPtr->borderWidth),
        !          1011:            (int) (height + 2*mbPtr->borderWidth));
        !          1012:     Tk_SetInternalBorder(mbPtr->tkwin, mbPtr->borderWidth);
        !          1013: }
        !          1014: 
        !          1015: /*
        !          1016:  *--------------------------------------------------------------
        !          1017:  *
        !          1018:  * MenuButtonVarProc --
        !          1019:  *
        !          1020:  *     This procedure is invoked when someone changes the
        !          1021:  *     state variable associated with a menubutton.  This causes
        !          1022:  *     the posted/unposted state of the menu to change if needed
        !          1023:  *     to match the variable's new value.
        !          1024:  *
        !          1025:  * Results:
        !          1026:  *     NULL is always returned.
        !          1027:  *
        !          1028:  * Side effects:
        !          1029:  *     The menu may be posted or unposted.
        !          1030:  *
        !          1031:  *--------------------------------------------------------------
        !          1032:  */
        !          1033: 
        !          1034:        /* ARGSUSED */
        !          1035: static char *
        !          1036: MenuButtonVarProc(clientData, interp, name1, name2, flags)
        !          1037:     ClientData clientData;     /* Information about button. */
        !          1038:     Tcl_Interp *interp;                /* Interpreter containing variable. */
        !          1039:     char *name1;               /* First part of variable's name. */
        !          1040:     char *name2;               /* Second part of variable's name. */
        !          1041:     int flags;                 /* Describes what's happening to variable. */
        !          1042: {
        !          1043:     register MenuButton *mbPtr = (MenuButton *) clientData;
        !          1044:     char *value;
        !          1045:     int newFlags;
        !          1046: 
        !          1047:     /*
        !          1048:      * If the variable is being unset, then just re-establish the
        !          1049:      * trace unless the whole interpreter is going away.  Also unpost
        !          1050:      * the menu.
        !          1051:      */
        !          1052: 
        !          1053:     newFlags = mbPtr->flags;
        !          1054:     if (flags & TCL_TRACE_UNSETS) {
        !          1055:        newFlags &= ~POSTED;
        !          1056:        if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
        !          1057:            Tcl_TraceVar2(interp, name1, name2,
        !          1058:                    TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
        !          1059:                    MenuButtonVarProc, clientData);
        !          1060:        }
        !          1061:     } else {
        !          1062: 
        !          1063:        /*
        !          1064:         * Use the value of the variable to update the posted status of
        !          1065:         * the menu.
        !          1066:         */
        !          1067:     
        !          1068:        value = Tcl_GetVar2(interp, name1, name2, flags & TCL_GLOBAL_ONLY);
        !          1069:        if (strcmp(value, Tk_PathName(mbPtr->tkwin)) == 0) {
        !          1070:            newFlags |= POSTED;
        !          1071:        } else {
        !          1072:            newFlags &= ~POSTED;
        !          1073:        }
        !          1074:     }
        !          1075: 
        !          1076:     if ((mbPtr->menuName != NULL) && (newFlags != mbPtr->flags)) {
        !          1077:        mbPtr->flags = newFlags;
        !          1078:        if (newFlags & POSTED) {
        !          1079:            int x, y;
        !          1080:            char string[50];
        !          1081: 
        !          1082:            /*
        !          1083:             * Post the menu just below the menu button.
        !          1084:             */
        !          1085: 
        !          1086:            Tk_GetRootCoords(mbPtr->tkwin, &x, &y);
        !          1087:            y += Tk_Height(mbPtr->tkwin);
        !          1088:            sprintf(string, "%d %d ", x, y);
        !          1089:            if (Tcl_VarEval(interp, mbPtr->menuName, " post ", string,
        !          1090:                    mbPtr->varName, (char *) NULL) != TCL_OK) {
        !          1091:                TkBindError(interp);
        !          1092:            }
        !          1093:        } else {
        !          1094:            if (Tcl_VarEval(interp, mbPtr->menuName, " unpost",
        !          1095:                    (char *) NULL) != TCL_OK) {
        !          1096:                TkBindError(interp);
        !          1097:            }
        !          1098:        }
        !          1099:     }
        !          1100:     return (char *) NULL;
        !          1101: }
        !          1102: 
        !          1103: /*
        !          1104:  *--------------------------------------------------------------
        !          1105:  *
        !          1106:  * MenuButtonTextVarProc --
        !          1107:  *
        !          1108:  *     This procedure is invoked when someone changes the variable
        !          1109:  *     whose contents are to be displayed in a menu button.
        !          1110:  *
        !          1111:  * Results:
        !          1112:  *     NULL is always returned.
        !          1113:  *
        !          1114:  * Side effects:
        !          1115:  *     The text displayed in the menu button will change to match the
        !          1116:  *     variable.
        !          1117:  *
        !          1118:  *--------------------------------------------------------------
        !          1119:  */
        !          1120: 
        !          1121:        /* ARGSUSED */
        !          1122: static char *
        !          1123: MenuButtonTextVarProc(clientData, interp, name1, name2, flags)
        !          1124:     ClientData clientData;     /* Information about button. */
        !          1125:     Tcl_Interp *interp;                /* Interpreter containing variable. */
        !          1126:     char *name1;               /* Name of variable. */
        !          1127:     char *name2;               /* Second part of variable name. */
        !          1128:     int flags;                 /* Information about what happened. */
        !          1129: {
        !          1130:     register MenuButton *mbPtr = (MenuButton *) clientData;
        !          1131:     char *value;
        !          1132: 
        !          1133:     /*
        !          1134:      * If the variable is unset, then immediately recreate it unless
        !          1135:      * the whole interpreter is going away.
        !          1136:      */
        !          1137: 
        !          1138:     if (flags & TCL_TRACE_UNSETS) {
        !          1139:        if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
        !          1140:            Tcl_SetVar2(interp, name1, name2, mbPtr->text,
        !          1141:                    flags & TCL_GLOBAL_ONLY);
        !          1142:            Tcl_TraceVar2(interp, name1, name2,
        !          1143:                    TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
        !          1144:                    MenuButtonTextVarProc, clientData);
        !          1145:        }
        !          1146:        return (char *) NULL;
        !          1147:     }
        !          1148: 
        !          1149:     value = Tcl_GetVar2(interp, name1, name2, flags & TCL_GLOBAL_ONLY);
        !          1150:     if (value == NULL) {
        !          1151:        value = "";
        !          1152:     }
        !          1153:     if (mbPtr->text != NULL) {
        !          1154:        ckfree(mbPtr->text);
        !          1155:     }
        !          1156:     mbPtr->text = ckalloc((unsigned) (strlen(value) + 1));
        !          1157:     strcpy(mbPtr->text, value);
        !          1158:     ComputeMenuButtonGeometry(mbPtr);
        !          1159: 
        !          1160:     if ((mbPtr->tkwin != NULL) && Tk_IsMapped(mbPtr->tkwin)
        !          1161:            && !(mbPtr->flags & REDRAW_PENDING)) {
        !          1162:        Tk_DoWhenIdle(DisplayMenuButton, (ClientData) mbPtr);
        !          1163:        mbPtr->flags |= REDRAW_PENDING;
        !          1164:     }
        !          1165:     return (char *) NULL;
        !          1166: }

unix.superglobalmegacorp.com

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