Annotation of micropolis/src/tk/tkmnbut.c, revision 1.1.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.