Annotation of micropolis/src/tk/tkmenu.c, revision 1.1.1.1

1.1       root        1: /* 
                      2:  * tkMenu.c --
                      3:  *
                      4:  *     This module implements menus for the Tk toolkit.  The menus
                      5:  *     support normal button entries, plus check buttons, radio
                      6:  *     buttons, iconic forms of all of the above, and separator
                      7:  *     entries.
                      8:  *
                      9:  * Copyright 1990-1992 Regents of the University of California.
                     10:  * Permission to use, copy, modify, and distribute this
                     11:  * software and its documentation for any purpose and without
                     12:  * fee is hereby granted, provided that the above copyright
                     13:  * notice appear in all copies.  The University of California
                     14:  * makes no representations about the suitability of this
                     15:  * software for any purpose.  It is provided "as is" without
                     16:  * express or implied warranty.
                     17:  */
                     18: 
                     19: #ifndef lint
                     20: static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkMenu.c,v 1.37 92/08/24 09:24:04 ouster Exp $ SPRITE (Berkeley)";
                     21: #endif
                     22: 
                     23: #include "tkconfig.h"
                     24: #include "default.h"
                     25: #include "tkint.h"
                     26: 
                     27: /*
                     28:  * One of the following data structures is kept for each entry of each
                     29:  * menu managed by this file:
                     30:  */
                     31: 
                     32: typedef struct MenuEntry {
                     33:     int type;                  /* Type of menu entry;  see below for
                     34:                                 * valid types. */
                     35:     struct Menu *menuPtr;      /* Menu with which this entry is associated. */
                     36:     char *label;               /* Main text label displayed in entry (NULL
                     37:                                 * if no label).  Malloc'ed. */
                     38:     int labelLength;           /* Number of non-NULL characters in label. */
                     39:     int underline;             /* Index of character to underline. */
                     40:     Pixmap bitmap;             /* Bitmap to display in menu entry, or None.
                     41:                                 * If not None then label is ignored. */
                     42:     char *accel;               /* Accelerator string displayed at right
                     43:                                 * of menu entry.  NULL means no such
                     44:                                 * accelerator.  Malloc'ed. */
                     45:     int accelLength;           /* Number of non-NULL characters in
                     46:                                 * accelerator. */
                     47: 
                     48:     /*
                     49:      * Information related to displaying entry:
                     50:      */
                     51: 
                     52:     Tk_Uid state;              /* State of button for display purposes:
                     53:                                 * normal, active, or disabled. */
                     54:     int height;                        /* Number of pixels occupied by entry in
                     55:                                 * vertical dimension. */
                     56:     int y;                     /* Y-coordinate of topmost pixel in entry. */
                     57:     int selectorDiameter;      /* Size of selector display, in pixels. */
                     58:     Tk_3DBorder border;                /* Structure used to draw background for
                     59:                                 * entry.  NULL means use overall border
                     60:                                 * for menu. */
                     61:     Tk_3DBorder activeBorder;  /* Used to draw background and border when
                     62:                                 * element is active.  NULL means use
                     63:                                 * activeBorder from menu. */
                     64:     XFontStruct *fontPtr;      /* Text font for menu entries.  NULL means
                     65:                                 * use overall font for menu. */
                     66:     GC textGC;                 /* GC for drawing text in entry.  NULL means
                     67:                                 * use overall textGC for menu. */
                     68:     GC activeGC;               /* GC for drawing text in entry when active.
                     69:                                 * NULL means use overall activeGC for
                     70:                                 * menu. */
                     71:     GC disabledGC;             /* Used to produce disabled effect for entry.
                     72:                                 * NULL means use overall disabledGC from
                     73:                                 * menu structure.  See comments for
                     74:                                 * disabledFg in menu structure for more
                     75:                                 * information. */
                     76: 
                     77:     /*
                     78:      * Information used to implement this entry's action:
                     79:      */
                     80: 
                     81:     char *command;             /* Command to invoke when entry is invoked.
                     82:                                 * Malloc'ed. */
                     83:     char *name;                        /* Name of variable (for check buttons and
                     84:                                 * radio buttons) or menu (for cascade
                     85:                                 * entries).  Malloc'ed.*/
                     86:     char *onValue;             /* Value to store in variable when selected
                     87:                                 * (only for radio and check buttons).
                     88:                                 * Malloc'ed. */
                     89:     char *offValue;            /* Value to store in variable when not
                     90:                                 * selected (only for check buttons).
                     91:                                 * Malloc'ed. */
                     92: 
                     93:     /*
                     94:      * Miscellaneous information:
                     95:      */
                     96: 
                     97:     int flags;                 /* Various flags.  See below for definitions. */
                     98: } MenuEntry;
                     99: 
                    100: /*
                    101:  * Flag values defined for menu entries:
                    102:  *
                    103:  * ENTRY_SELECTED:             Non-zero means this is a radio or check
                    104:  *                             button and that it should be drawn in
                    105:  *                             the "selected" state.
                    106:  * ENTRY_NEEDS_REDISPLAY:      Non-zero means the entry should be redisplayed.
                    107:  */
                    108: 
                    109: #define ENTRY_SELECTED         1
                    110: #define ENTRY_NEEDS_REDISPLAY  4
                    111: 
                    112: /*
                    113:  * Types defined for MenuEntries:
                    114:  */
                    115: 
                    116: #define COMMAND_ENTRY          0
                    117: #define SEPARATOR_ENTRY                1
                    118: #define CHECK_BUTTON_ENTRY     2
                    119: #define RADIO_BUTTON_ENTRY     3
                    120: #define CASCADE_ENTRY          4
                    121: 
                    122: /*
                    123:  * Mask bits for above types:
                    124:  */
                    125: 
                    126: #define COMMAND_MASK           TK_CONFIG_USER_BIT
                    127: #define SEPARATOR_MASK         (TK_CONFIG_USER_BIT << 1)
                    128: #define CHECK_BUTTON_MASK      (TK_CONFIG_USER_BIT << 2)
                    129: #define RADIO_BUTTON_MASK      (TK_CONFIG_USER_BIT << 3)
                    130: #define CASCADE_MASK           (TK_CONFIG_USER_BIT << 4)
                    131: #define ALL_MASK               (COMMAND_MASK | SEPARATOR_MASK \
                    132:        | CHECK_BUTTON_MASK | RADIO_BUTTON_MASK | CASCADE_MASK)
                    133: 
                    134: /*
                    135:  * Configuration specs for individual menu entries:
                    136:  */
                    137: 
                    138: static Tk_ConfigSpec entryConfigSpecs[] = {
                    139:     {TK_CONFIG_BORDER, "-activebackground", (char *) NULL, (char *) NULL,
                    140:        DEF_MENU_ENTRY_ACTIVE_BG, Tk_Offset(MenuEntry, activeBorder),
                    141:        COMMAND_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CASCADE_MASK
                    142:        |TK_CONFIG_NULL_OK},
                    143:     {TK_CONFIG_STRING, "-accelerator", (char *) NULL, (char *) NULL,
                    144:        DEF_MENU_ENTRY_ACCELERATOR, Tk_Offset(MenuEntry, accel),
                    145:        COMMAND_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CASCADE_MASK},
                    146:     {TK_CONFIG_BORDER, "-background", (char *) NULL, (char *) NULL,
                    147:        DEF_MENU_ENTRY_BG, Tk_Offset(MenuEntry, border),
                    148:        COMMAND_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CASCADE_MASK
                    149:        |TK_CONFIG_NULL_OK},
                    150: #if defined(USE_XPM3)
                    151:     {TK_CONFIG_PIXMAP, "-bitmap", (char *) NULL, (char *) NULL,
                    152:        DEF_MENU_ENTRY_BITMAP, Tk_Offset(MenuEntry, bitmap),
                    153:        COMMAND_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CASCADE_MASK
                    154:        |TK_CONFIG_NULL_OK},
                    155: #else
                    156:     {TK_CONFIG_BITMAP, "-bitmap", (char *) NULL, (char *) NULL,
                    157:        DEF_MENU_ENTRY_BITMAP, Tk_Offset(MenuEntry, bitmap),
                    158:        COMMAND_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CASCADE_MASK
                    159:        |TK_CONFIG_NULL_OK},
                    160: #endif
                    161:     {TK_CONFIG_STRING, "-command", (char *) NULL, (char *) NULL,
                    162:        DEF_MENU_ENTRY_COMMAND, Tk_Offset(MenuEntry, command),
                    163:        COMMAND_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CASCADE_MASK},
                    164:     {TK_CONFIG_FONT, "-font", (char *) NULL, (char *) NULL,
                    165:        DEF_MENU_ENTRY_FONT, Tk_Offset(MenuEntry, fontPtr),
                    166:        COMMAND_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CASCADE_MASK
                    167:        |TK_CONFIG_NULL_OK},
                    168:     {TK_CONFIG_STRING, "-label", (char *) NULL, (char *) NULL,
                    169:        DEF_MENU_ENTRY_LABEL, Tk_Offset(MenuEntry, label),
                    170:        COMMAND_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CASCADE_MASK},
                    171:     {TK_CONFIG_STRING, "-menu", (char *) NULL, (char *) NULL,
                    172:        DEF_MENU_ENTRY_MENU, Tk_Offset(MenuEntry, name), CASCADE_MASK},
                    173:     {TK_CONFIG_STRING, "-offvalue", (char *) NULL, (char *) NULL,
                    174:        DEF_MENU_ENTRY_OFF_VALUE, Tk_Offset(MenuEntry, offValue),
                    175:        CHECK_BUTTON_MASK},
                    176:     {TK_CONFIG_UID, "-state", (char *) NULL, (char *) NULL,
                    177:        DEF_MENU_ENTRY_STATE, Tk_Offset(MenuEntry, state),
                    178:        COMMAND_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CASCADE_MASK
                    179:        |TK_CONFIG_DONT_SET_DEFAULT},
                    180:     {TK_CONFIG_STRING, "-onvalue", (char *) NULL, (char *) NULL,
                    181:        DEF_MENU_ENTRY_ON_VALUE, Tk_Offset(MenuEntry, onValue),
                    182:        CHECK_BUTTON_MASK},
                    183:     {TK_CONFIG_STRING, "-value", (char *) NULL, (char *) NULL,
                    184:        DEF_MENU_ENTRY_VALUE, Tk_Offset(MenuEntry, onValue),
                    185:        RADIO_BUTTON_MASK},
                    186:     {TK_CONFIG_STRING, "-variable", (char *) NULL, (char *) NULL,
                    187:        DEF_MENU_ENTRY_CHECK_VARIABLE, Tk_Offset(MenuEntry, name),
                    188:        CHECK_BUTTON_MASK},
                    189:     {TK_CONFIG_STRING, "-variable", (char *) NULL, (char *) NULL,
                    190:        DEF_MENU_ENTRY_RADIO_VARIABLE, Tk_Offset(MenuEntry, name),
                    191:        RADIO_BUTTON_MASK},
                    192:     {TK_CONFIG_INT, "-underline", (char *) NULL, (char *) NULL,
                    193:        DEF_MENU_ENTRY_UNDERLINE, Tk_Offset(MenuEntry, underline),
                    194:        COMMAND_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CASCADE_MASK
                    195:        |TK_CONFIG_DONT_SET_DEFAULT},
                    196:     {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
                    197:        (char *) NULL, 0, 0}
                    198: };
                    199: 
                    200: /*
                    201:  * A data structure of the following type is kept for each
                    202:  * menu managed by this file:
                    203:  */
                    204: 
                    205: typedef struct Menu {
                    206:     Tk_Window tkwin;           /* Window that embodies the pane.  NULL
                    207:                                 * means that the window has been destroyed
                    208:                                 * but the data structures haven't yet been
                    209:                                 * cleaned up.*/
                    210:     Tcl_Interp *interp;                /* Interpreter associated with menu. */
                    211:     MenuEntry **entries;       /* Array of pointers to all the entries
                    212:                                 * in the menu.  NULL means no entries. */
                    213:     int numEntries;            /* Number of elements in entries. */
                    214:     int active;                        /* Index of active entry.  -1 means
                    215:                                 * nothing active. */
                    216:     Tk_Uid group;              /* Used to allow event sharing between
                    217:                                 * related menus and menu buttons. */
                    218: 
                    219:     /*
                    220:      * Information used when displaying widget:
                    221:      */
                    222: 
                    223:     Tk_3DBorder border;                /* Structure used to draw 3-D
                    224:                                 * border and background for menu. */
                    225:     int borderWidth;           /* Width of border around whole menu. */
                    226:     Tk_3DBorder activeBorder;  /* Used to draw background and border for
                    227:                                 * active element (if any). */
                    228:     int activeBorderWidth;     /* Width of border around active element. */
                    229:     XFontStruct *fontPtr;      /* Text font for menu entries. */
                    230:     XColor *fg;                        /* Foreground color for entries. */
                    231:     GC textGC;                 /* GC for drawing text and other features
                    232:                                 * of menu entries. */
                    233:     XColor *disabledFg;                /* Foreground color when disabled.  NULL
                    234:                                 * means use normalFg with a 50% stipple
                    235:                                 * instead. */
                    236:     Pixmap gray;               /* Bitmap for drawing disabled entries in
                    237:                                 * a stippled fashion.  None means not
                    238:                                 * allocated yet. */
                    239:     GC disabledGC;             /* Used to produce disabled effect.  If
                    240:                                 * disabledFg isn't NULL, this GC is used to
                    241:                                 * draw text and icons for disabled entries.
                    242:                                 * Otherwise text and icons are drawn with
                    243:                                 * normalGC and this GC is used to stipple
                    244:                                 * background across them. */
                    245:     XColor *activeFg;          /* Foreground color for active entry. */
                    246:     GC activeGC;               /* GC for drawing active entry. */
                    247:     XColor *selectorFg;                /* Color for selectors in radio and check
                    248:                                 * button entries. */
                    249:     GC selectorGC;             /* For drawing selectors. */
                    250:     int selectorSpace;         /* Number of pixels to allow for displaying
                    251:                                 * selectors in menu entries (includes extra
                    252:                                 * space around selector). */
                    253:     int labelWidth;            /* Number of pixels to allow for displaying
                    254:                                 * labels in menu entries. */
                    255: 
                    256:     /*
                    257:      * Miscellaneous information:
                    258:      */
                    259: 
                    260:     Cursor cursor;             /* Current cursor for window, or None. */
                    261:     MenuEntry *postedCascade;  /* Points to menu entry for cascaded
                    262:                                 * submenu that is currently posted, or
                    263:                                 * NULL if no submenu posted. */
                    264:     int flags;                 /* Various flags;  see below for
                    265:                                 * definitions. */
                    266: } Menu;
                    267: 
                    268: /*
                    269:  * Flag bits for menus:
                    270:  *
                    271:  * REDRAW_PENDING:             Non-zero means a DoWhenIdle handler
                    272:  *                             has already been queued to redraw
                    273:  *                             this window.
                    274:  * RESIZE_PENDING:             Non-zero means a call to ComputeMenuGeometry
                    275:  *                             has already been scheduled.
                    276:  */
                    277: 
                    278: #define REDRAW_PENDING         1
                    279: #define RESIZE_PENDING         2
                    280: 
                    281: /*
                    282:  * Configuration specs valid for the menu as a whole:
                    283:  */
                    284: 
                    285: static Tk_ConfigSpec configSpecs[] = {
                    286:     {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
                    287:        DEF_MENU_ACTIVE_BG_COLOR, Tk_Offset(Menu, activeBorder),
                    288:        TK_CONFIG_COLOR_ONLY},
                    289:     {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
                    290:        DEF_MENU_ACTIVE_BG_MONO, Tk_Offset(Menu, activeBorder),
                    291:        TK_CONFIG_MONO_ONLY},
                    292:     {TK_CONFIG_PIXELS, "-activeborderwidth", "activeBorderWidth", "BorderWidth",
                    293:        DEF_MENU_ACTIVE_BORDER_WIDTH, Tk_Offset(Menu, activeBorderWidth), 0},
                    294:     {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
                    295:        DEF_MENU_ACTIVE_FG_COLOR, Tk_Offset(Menu, activeFg),
                    296:        TK_CONFIG_COLOR_ONLY},
                    297:     {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
                    298:        DEF_MENU_ACTIVE_FG_MONO, Tk_Offset(Menu, activeFg),
                    299:        TK_CONFIG_MONO_ONLY},
                    300:     {TK_CONFIG_BORDER, "-background", "background", "Background",
                    301:        DEF_MENU_BG_COLOR, Tk_Offset(Menu, border), TK_CONFIG_COLOR_ONLY},
                    302:     {TK_CONFIG_BORDER, "-background", "background", "Background",
                    303:        DEF_MENU_BG_MONO, Tk_Offset(Menu, border), TK_CONFIG_MONO_ONLY},
                    304:     {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
                    305:        (char *) NULL, 0, 0},
                    306:     {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
                    307:        (char *) NULL, 0, 0},
                    308:     {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
                    309:        DEF_MENU_BORDER_WIDTH, Tk_Offset(Menu, borderWidth), 0},
                    310:     {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
                    311:        DEF_MENU_CURSOR, Tk_Offset(Menu, cursor), TK_CONFIG_NULL_OK},
                    312:     {TK_CONFIG_COLOR, "-disabledforeground", "disabledForeground",
                    313:        "DisabledForeground", DEF_MENU_DISABLED_FG_COLOR,
                    314:        Tk_Offset(Menu, disabledFg), TK_CONFIG_COLOR_ONLY|TK_CONFIG_NULL_OK},
                    315:     {TK_CONFIG_COLOR, "-disabledforeground", "disabledForeground",
                    316:        "DisabledForeground", DEF_MENU_DISABLED_FG_MONO,
                    317:        Tk_Offset(Menu, disabledFg), TK_CONFIG_MONO_ONLY|TK_CONFIG_NULL_OK},
                    318:     {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
                    319:        (char *) NULL, 0, 0},
                    320:     {TK_CONFIG_FONT, "-font", "font", "Font",
                    321:        DEF_MENU_FONT, Tk_Offset(Menu, fontPtr), 0},
                    322:     {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
                    323:        DEF_MENU_FG, Tk_Offset(Menu, fg), 0},
                    324:     {TK_CONFIG_COLOR, "-selector", "selector", "Foreground",
                    325:        DEF_MENU_SELECTOR_COLOR, Tk_Offset(Menu, selectorFg),
                    326:        TK_CONFIG_COLOR_ONLY},
                    327:     {TK_CONFIG_COLOR, "-selector", "selector", "Foreground",
                    328:        DEF_MENU_SELECTOR_MONO, Tk_Offset(Menu, selectorFg),
                    329:        TK_CONFIG_MONO_ONLY},
                    330:     {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
                    331:        (char *) NULL, 0, 0}
                    332: };
                    333: 
                    334: /*
                    335:  * Forward declarations for procedures defined later in this file:
                    336:  */
                    337: 
                    338: static int             ActivateMenuEntry _ANSI_ARGS_((Menu *menuPtr,
                    339:                            int index));
                    340: static void            ComputeMenuGeometry _ANSI_ARGS_((
                    341:                            ClientData clientData));
                    342: static int             ConfigureMenu _ANSI_ARGS_((Tcl_Interp *interp,
                    343:                            Menu *menuPtr, int argc, char **argv,
                    344:                            int flags));
                    345: static int             ConfigureMenuEntry _ANSI_ARGS_((Tcl_Interp *interp,
                    346:                            Menu *menuPtr, MenuEntry *mePtr, int index,
                    347:                            int argc, char **argv, int flags));
                    348: static void            DestroyMenu _ANSI_ARGS_((ClientData clientData));
                    349: static void            DestroyMenuEntry _ANSI_ARGS_((ClientData clientData));
                    350: static void            DisplayMenu _ANSI_ARGS_((ClientData clientData));
                    351: static void            EventuallyRedrawMenu _ANSI_ARGS_((Menu *menuPtr,
                    352:                            int index));
                    353: static int             GetMenuIndex _ANSI_ARGS_((Tcl_Interp *interp,
                    354:                            Menu *menuPtr, char *string, int *indexPtr));
                    355: static void            MenuEventProc _ANSI_ARGS_((ClientData clientData,
                    356:                            XEvent *eventPtr));
                    357: static char *          MenuVarProc _ANSI_ARGS_((ClientData clientData,
                    358:                            Tcl_Interp *interp, char *name1, char *name2,
                    359:                            int flags));
                    360: static int             MenuWidgetCmd _ANSI_ARGS_((ClientData clientData,
                    361:                            Tcl_Interp *interp, int argc, char **argv));
                    362: static int             PostSubmenu _ANSI_ARGS_((Tcl_Interp *interp,
                    363:                            Menu *menuPtr, MenuEntry *mePtr));
                    364: 
                    365: /*
                    366:  *--------------------------------------------------------------
                    367:  *
                    368:  * Tk_MenuCmd --
                    369:  *
                    370:  *     This procedure is invoked to process the "menu" Tcl
                    371:  *     command.  See the user documentation for details on
                    372:  *     what it does.
                    373:  *
                    374:  * Results:
                    375:  *     A standard Tcl result.
                    376:  *
                    377:  * Side effects:
                    378:  *     See the user documentation.
                    379:  *
                    380:  *--------------------------------------------------------------
                    381:  */
                    382: 
                    383: int
                    384: Tk_MenuCmd(clientData, interp, argc, argv)
                    385:     ClientData clientData;     /* Main window associated with
                    386:                                 * interpreter. */
                    387:     Tcl_Interp *interp;                /* Current interpreter. */
                    388:     int argc;                  /* Number of arguments. */
                    389:     char **argv;               /* Argument strings. */
                    390: {
                    391:     Tk_Window tkwin = (Tk_Window) clientData;
                    392:     Tk_Window new;
                    393:     register Menu *menuPtr;
                    394:     XSetWindowAttributes atts;
                    395: 
                    396:     if (argc < 2) {
                    397:        Tcl_AppendResult(interp, "wrong # args: should be \"",
                    398:                argv[0], " pathName ?options?\"", (char *) NULL);
                    399:        return TCL_ERROR;
                    400:     }
                    401: 
                    402:     /*
                    403:      * Create the new window.  Set override-redirect so the window
                    404:      * manager won't add a border or argue about placement, and set
                    405:      * save-under so that the window can pop up and down without a
                    406:      * lot of re-drawing.
                    407:      */
                    408: 
                    409:     new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], "");
                    410:     if (new == NULL) {
                    411:        return TCL_ERROR;
                    412:     }
                    413:     atts.override_redirect = True;
                    414:     atts.save_under = True;
                    415:     Tk_ChangeWindowAttributes(new, CWOverrideRedirect|CWSaveUnder, &atts);
                    416: 
                    417:     /*
                    418:      * Initialize the data structure for the menu.
                    419:      */
                    420: 
                    421:     menuPtr = (Menu *) ckalloc(sizeof(Menu));
                    422:     menuPtr->tkwin = new;
                    423:     menuPtr->interp = interp;
                    424:     menuPtr->entries = NULL;
                    425:     menuPtr->numEntries = 0;
                    426:     menuPtr->active = -1;
                    427:     menuPtr->group = NULL; 
                    428:     menuPtr->border = NULL;
                    429:     menuPtr->activeBorder = NULL;
                    430:     menuPtr->fontPtr = NULL;
                    431:     menuPtr->fg = NULL;
                    432:     menuPtr->textGC = None;
                    433:     menuPtr->disabledFg = NULL;
                    434:     menuPtr->gray = None;
                    435:     menuPtr->disabledGC = None;
                    436:     menuPtr->activeFg = NULL;
                    437:     menuPtr->activeGC = None;
                    438:     menuPtr->selectorFg = NULL;
                    439:     menuPtr->selectorGC = None;
                    440:     menuPtr->cursor = None;
                    441:     menuPtr->postedCascade = NULL;
                    442:     menuPtr->flags = 0;
                    443: 
                    444:     Tk_SetClass(new, "Menu");
                    445:     Tk_CreateEventHandler(menuPtr->tkwin, ExposureMask|StructureNotifyMask,
                    446:            MenuEventProc, (ClientData) menuPtr);
                    447:     Tcl_CreateCommand(interp, Tk_PathName(menuPtr->tkwin), MenuWidgetCmd,
                    448:            (ClientData) menuPtr, (void (*)()) NULL);
                    449:     if (ConfigureMenu(interp, menuPtr, argc-2, argv+2, 0) != TCL_OK) {
                    450:        goto error;
                    451:     }
                    452: 
                    453:     interp->result = Tk_PathName(menuPtr->tkwin);
                    454:     return TCL_OK;
                    455: 
                    456:     error:
                    457:     Tk_DestroyWindow(menuPtr->tkwin);
                    458:     return TCL_ERROR;
                    459: }
                    460: 
                    461: /*
                    462:  *--------------------------------------------------------------
                    463:  *
                    464:  * MenuWidgetCmd --
                    465:  *
                    466:  *     This procedure is invoked to process the Tcl command
                    467:  *     that corresponds to a widget managed by this module.
                    468:  *     See the user documentation for details on what it does.
                    469:  *
                    470:  * Results:
                    471:  *     A standard Tcl result.
                    472:  *
                    473:  * Side effects:
                    474:  *     See the user documentation.
                    475:  *
                    476:  *--------------------------------------------------------------
                    477:  */
                    478: 
                    479: static int
                    480: MenuWidgetCmd(clientData, interp, argc, argv)
                    481:     ClientData clientData;     /* Information about menu widget. */
                    482:     Tcl_Interp *interp;                /* Current interpreter. */
                    483:     int argc;                  /* Number of arguments. */
                    484:     char **argv;               /* Argument strings. */
                    485: {
                    486:     register Menu *menuPtr = (Menu *) clientData;
                    487:     register MenuEntry *mePtr;
                    488:     int result = TCL_OK;
                    489:     int length, type;
                    490:     char c;
                    491: 
                    492:     if (argc < 2) {
                    493:        Tcl_AppendResult(interp, "wrong # args: should be \"",
                    494:                argv[0], " option ?arg arg ...?\"", (char *) NULL);
                    495:        return TCL_ERROR;
                    496:     }
                    497:     Tk_Preserve((ClientData) menuPtr);
                    498:     c = argv[1][0];
                    499:     length = strlen(argv[1]);
                    500:     if ((c == 'a') && (strncmp(argv[1], "activate", length) == 0)
                    501:            && (length >= 2)) {
                    502:        int index;
                    503: 
                    504:        if (argc != 3) {
                    505:            Tcl_AppendResult(interp, "wrong # args: should be \"",
                    506:                    argv[0], " activate index\"", (char *) NULL);
                    507:            goto error;
                    508:        }
                    509:        if (GetMenuIndex(interp, menuPtr, argv[2], &index) != TCL_OK) {
                    510:            goto error;
                    511:        }
                    512:        if (menuPtr->active == index) {
                    513:            goto done;
                    514:        }
                    515:        if (index >= 0) {
                    516:            if ((menuPtr->entries[index]->type == SEPARATOR_ENTRY)
                    517:                    || (menuPtr->entries[index]->state == tkDisabledUid)) {
                    518:                index = -1;
                    519:            }
                    520:        }
                    521:        result = ActivateMenuEntry(menuPtr, index);
                    522:     } else if ((c == 'a') && (strncmp(argv[1], "add", length) == 0)
                    523:            && (length >= 2)) {
                    524:        MenuEntry **newEntries;
                    525: 
                    526:        if (argc < 3) {
                    527:            Tcl_AppendResult(interp, "wrong # args: should be \"",
                    528:                    argv[0], " add type ?options?\"", (char *) NULL);
                    529:            goto error;
                    530:        }
                    531: 
                    532:        /*
                    533:         * Figure out the type of the new entry.
                    534:         */
                    535: 
                    536:        c = argv[2][0];
                    537:        length = strlen(argv[2]);
                    538:        if ((c == 'c') && (strncmp(argv[2], "cascade", length) == 0)
                    539:                && (length >= 2)) {
                    540:            type = CASCADE_ENTRY;
                    541:        } else if ((c == 'c') && (strncmp(argv[2], "checkbutton", length) == 0)
                    542:                && (length >= 2)) {
                    543:            type = CHECK_BUTTON_ENTRY;
                    544:        } else if ((c == 'c') && (strncmp(argv[2], "command", length) == 0)
                    545:                && (length >= 2)) {
                    546:            type = COMMAND_ENTRY;
                    547:        } else if ((c == 'r')
                    548:                && (strncmp(argv[2], "radiobutton", length) == 0)) {
                    549:            type = RADIO_BUTTON_ENTRY;
                    550:        } else if ((c == 's')
                    551:                && (strncmp(argv[2], "separator", length) == 0)) {
                    552:            type = SEPARATOR_ENTRY;
                    553:        } else {
                    554:            Tcl_AppendResult(interp, "bad menu entry type \"",
                    555:                    argv[2], "\":  must be cascade, checkbutton, ",
                    556:                    "command, radiobutton, or separator", (char *) NULL);
                    557:            goto error;
                    558:        }
                    559: 
                    560:        /*
                    561:         * Add a new entry to the end of the menu's array of entries,
                    562:         * and process options for it.
                    563:         */
                    564: 
                    565:        mePtr = (MenuEntry *) ckalloc(sizeof(MenuEntry));
                    566:        newEntries = (MenuEntry **) ckalloc((unsigned)
                    567:                ((menuPtr->numEntries+1)*sizeof(MenuEntry *)));
                    568:        if (menuPtr->numEntries != 0) {
                    569:            memcpy((VOID *) newEntries, (VOID *) menuPtr->entries,
                    570:                    menuPtr->numEntries*sizeof(MenuEntry *));
                    571:            ckfree((char *) menuPtr->entries);
                    572:        }
                    573:        menuPtr->entries = newEntries;
                    574:        menuPtr->entries[menuPtr->numEntries] = mePtr;
                    575:        menuPtr->numEntries++;
                    576:        mePtr->type = type;
                    577:        mePtr->menuPtr = menuPtr;
                    578:        mePtr->label = NULL;
                    579:        mePtr->underline = -1;
                    580:        mePtr->bitmap = None;
                    581:        mePtr->accel = NULL;
                    582:        mePtr->state = tkNormalUid;
                    583:        mePtr->border = NULL;
                    584:        mePtr->activeBorder = NULL;
                    585:        mePtr->fontPtr = NULL;
                    586:        mePtr->textGC = None;
                    587:        mePtr->activeGC = None;
                    588:        mePtr->disabledGC = None;
                    589:        mePtr->command = NULL;
                    590:        mePtr->name = NULL;
                    591:        mePtr->onValue = NULL;
                    592:        mePtr->offValue = NULL;
                    593:        mePtr->flags = 0;
                    594:        if (ConfigureMenuEntry(interp, menuPtr, mePtr, menuPtr->numEntries-1,
                    595:                argc-3, argv+3, 0) != TCL_OK) {
                    596:            DestroyMenuEntry((ClientData) mePtr);
                    597:            menuPtr->numEntries--;
                    598:            goto error;
                    599:        }
                    600:     } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) {
                    601:        if (argc == 2) {
                    602:            result = Tk_ConfigureInfo(interp, menuPtr->tkwin, configSpecs,
                    603:                    (char *) menuPtr, (char *) NULL, 0);
                    604:        } else if (argc == 3) {
                    605:            result = Tk_ConfigureInfo(interp, menuPtr->tkwin, configSpecs,
                    606:                    (char *) menuPtr, argv[2], 0);
                    607:        } else {
                    608:            result = ConfigureMenu(interp, menuPtr, argc-2, argv+2,
                    609:                    TK_CONFIG_ARGV_ONLY);
                    610:        }
                    611:     } else if ((c == 'd') && (strncmp(argv[1], "delete", length) == 0)
                    612:            && (length >= 2)) {
                    613:        int index, i;
                    614: 
                    615:        if (argc != 3) {
                    616:            Tcl_AppendResult(interp, "wrong # args: should be \"",
                    617:                    argv[0], " delete index\"", (char *) NULL);
                    618:            goto error;
                    619:        }
                    620:        if (GetMenuIndex(interp, menuPtr, argv[2], &index) != TCL_OK) {
                    621:            goto error;
                    622:        }
                    623:        if (index < 0) {
                    624:            goto done;
                    625:        }
                    626:        Tk_EventuallyFree((ClientData) menuPtr->entries[index],
                    627:                DestroyMenuEntry);
                    628:        for (i = index; i < menuPtr->numEntries-1; i++) {
                    629:            menuPtr->entries[i] = menuPtr->entries[i+1];
                    630:        }
                    631:        menuPtr->numEntries -= 1;
                    632:        if (menuPtr->active == index) {
                    633:            menuPtr->active = -1;
                    634:        } else if (menuPtr->active > index) {
                    635:            menuPtr->active -= 1;
                    636:        }
                    637:        if (!(menuPtr->flags & RESIZE_PENDING)) {
                    638:            menuPtr->flags |= RESIZE_PENDING;
                    639:            Tk_DoWhenIdle(ComputeMenuGeometry, (ClientData) menuPtr);
                    640:        }
                    641:     } else if ((c == 'd') && (strncmp(argv[1], "disable", length) == 0)
                    642:            && (length >= 2)) {
                    643:        int index;
                    644: 
                    645:        if (argc != 3) {
                    646:            Tcl_AppendResult(interp, "wrong # args: should be \"",
                    647:                    argv[0], " disable index\"", (char *) NULL);
                    648:            goto error;
                    649:        }
                    650:        if (GetMenuIndex(interp, menuPtr, argv[2], &index) != TCL_OK) {
                    651:            goto error;
                    652:        }
                    653:        if (index < 0) {
                    654:            goto done;
                    655:        }
                    656:        menuPtr->entries[index]->state = tkDisabledUid;
                    657:        if (menuPtr->active == index) {
                    658:            menuPtr->active = -1;
                    659:        }
                    660:        EventuallyRedrawMenu(menuPtr, index);
                    661:     } else if ((c == 'e') && (length >= 3)
                    662:            && (strncmp(argv[1], "enable", length) == 0)) {
                    663:        int index;
                    664: 
                    665:        if (argc != 3) {
                    666:            Tcl_AppendResult(interp, "wrong # args: should be \"",
                    667:                    argv[0], " enable index\"", (char *) NULL);
                    668:            goto error;
                    669:        }
                    670:        if (GetMenuIndex(interp, menuPtr, argv[2], &index) != TCL_OK) {
                    671:            goto error;
                    672:        }
                    673:        if (index < 0) {
                    674:            goto done;
                    675:        }
                    676:        menuPtr->entries[index]->state = tkNormalUid;
                    677:        EventuallyRedrawMenu(menuPtr, index);
                    678:     } else if ((c == 'e') && (length >= 3)
                    679:            && (strncmp(argv[1], "entryconfigure", length) == 0)) {
                    680:        int index;
                    681: 
                    682:        if (argc < 3) {
                    683:            Tcl_AppendResult(interp, "wrong # args: should be \"",
                    684:                    argv[0], " entryconfigure index ?option value ...?\"",
                    685:                    (char *) NULL);
                    686:            goto error;
                    687:        }
                    688:        if (GetMenuIndex(interp, menuPtr, argv[2], &index) != TCL_OK) {
                    689:            goto error;
                    690:        }
                    691:        if (index < 0) {
                    692:            goto done;
                    693:        }
                    694:        mePtr = menuPtr->entries[index];
                    695:        Tk_Preserve((ClientData) mePtr);
                    696:        if (argc == 3) {
                    697:            result = Tk_ConfigureInfo(interp, menuPtr->tkwin, entryConfigSpecs,
                    698:                    (char *) mePtr, (char *) NULL,
                    699:                    COMMAND_MASK << mePtr->type);
                    700:        } else if (argc == 4) {
                    701:            result = Tk_ConfigureInfo(interp, menuPtr->tkwin, entryConfigSpecs,
                    702:                    (char *) mePtr, argv[3], COMMAND_MASK << mePtr->type);
                    703:        } else {
                    704:            result = ConfigureMenuEntry(interp, menuPtr, mePtr, index, argc-3,
                    705:                    argv+3, TK_CONFIG_ARGV_ONLY | COMMAND_MASK << mePtr->type);
                    706:        }
                    707:        Tk_Release((ClientData) mePtr);
                    708:     } else if ((c == 'i') && (strncmp(argv[1], "index", length) == 0)
                    709:            && (length >= 3)) {
                    710:        int index;
                    711: 
                    712:        if (argc != 3) {
                    713:            Tcl_AppendResult(interp, "wrong # args: should be \"",
                    714:                    argv[0], " index string\"", (char *) NULL);
                    715:            goto error;
                    716:        }
                    717:        if (GetMenuIndex(interp, menuPtr, argv[2], &index) != TCL_OK) {
                    718:            goto error;
                    719:        }
                    720:        if (index < 0) {
                    721:            interp->result = "none";
                    722:        } else {
                    723:            sprintf(interp->result, "%d", index);
                    724:        }
                    725:     } else if ((c == 'i') && (strncmp(argv[1], "invoke", length) == 0)
                    726:            && (length >= 3)) {
                    727:        int index;
                    728: 
                    729:        if (argc != 3) {
                    730:            Tcl_AppendResult(interp, "wrong # args: should be \"",
                    731:                    argv[0], " invoke index\"", (char *) NULL);
                    732:            goto error;
                    733:        }
                    734:        if (GetMenuIndex(interp, menuPtr, argv[2], &index) != TCL_OK) {
                    735:            goto error;
                    736:        }
                    737:        if (index < 0) {
                    738:            goto done;
                    739:        }
                    740:        mePtr = menuPtr->entries[index];
                    741:        if (mePtr->state == tkDisabledUid) {
                    742:            goto done;
                    743:        }
                    744:        Tk_Preserve((ClientData) mePtr);
                    745:        if (mePtr->type == CHECK_BUTTON_ENTRY) {
                    746:            if (mePtr->flags & ENTRY_SELECTED) {
                    747:                Tcl_SetVar(interp, mePtr->name, mePtr->offValue,
                    748:                        TCL_GLOBAL_ONLY);
                    749:            } else {
                    750:                Tcl_SetVar(interp, mePtr->name, mePtr->onValue,
                    751:                        TCL_GLOBAL_ONLY);
                    752:            }
                    753:        } else if (mePtr->type == RADIO_BUTTON_ENTRY) {
                    754:            Tcl_SetVar(interp, mePtr->name, mePtr->onValue, TCL_GLOBAL_ONLY);
                    755:        }
                    756:        if (mePtr->command != NULL) {
                    757:            result = Tcl_GlobalEval(interp, mePtr->command);
                    758:        }
                    759:        Tk_Release((ClientData) mePtr);
                    760:     } else if ((c == 'p') && (strncmp(argv[1], "post", length) == 0)) {
                    761:        int x, y, tmp;
                    762:        Tk_Uid group;
                    763: 
                    764:        if ((argc != 4) && (argc != 5)) {
                    765:            Tcl_AppendResult(interp, "wrong # args: should be \"",
                    766:                    argv[0], " post x y ?group?\"", (char *) NULL);
                    767:            goto error;
                    768:        }
                    769:        if ((Tcl_GetInt(interp, argv[2], &x) != TCL_OK)
                    770:                || (Tcl_GetInt(interp, argv[3], &y) != TCL_OK)) {
                    771:            goto error;
                    772:        }
                    773:        if (argc == 5) {
                    774:            group = Tk_GetUid(argv[4]);
                    775:        } else {
                    776:            group = Tk_GetUid("default");
                    777:        }
                    778: 
                    779:        /*
                    780:         * Adjust the position of the menu if necessary to keep it
                    781:         * on-screen.
                    782:         */
                    783: 
                    784:        tmp = WidthOfScreen(Tk_Screen(menuPtr->tkwin))
                    785:                - Tk_Width(menuPtr->tkwin);
                    786:        if (x > tmp) {
                    787:            x = tmp;
                    788:        }
                    789:        if (x < 0) {
                    790:            x = 0;
                    791:        }
                    792:        tmp = HeightOfScreen(Tk_Screen(menuPtr->tkwin))
                    793:                - Tk_Height(menuPtr->tkwin);
                    794:        if (y > tmp) {
                    795:            y = tmp;
                    796:        }
                    797:        if (y < 0) {
                    798:            y = 0;
                    799:        }
                    800:        if ((x != Tk_X(menuPtr->tkwin)) || (y != Tk_Y(menuPtr->tkwin))) {
                    801:            Tk_MoveWindow(menuPtr->tkwin, x, y);
                    802:        }
                    803:        if (Tk_IsMapped(menuPtr->tkwin)) {
                    804:            if (group != menuPtr->group) {
                    805:                Tk_UnshareEvents(menuPtr->tkwin, menuPtr->group);
                    806:                Tk_ShareEvents(menuPtr->tkwin, group);
                    807:            }
                    808:        } else {
                    809:            Tk_ShareEvents(menuPtr->tkwin, group);
                    810:            Tk_MapWindow(menuPtr->tkwin);
                    811:            result = ActivateMenuEntry(menuPtr, -1);
                    812:        }
                    813:        XRaiseWindow(Tk_Display(menuPtr->tkwin), Tk_WindowId(menuPtr->tkwin));
                    814:        menuPtr->group = group;
                    815:     } else if ((c == 'u') && (strncmp(argv[1], "unpost", length) == 0)) {
                    816:        if (argc != 2) {
                    817:            Tcl_AppendResult(interp, "wrong # args: should be \"",
                    818:                    argv[0], " unpost\"", (char *) NULL);
                    819:            goto error;
                    820:        }
                    821:        Tk_UnshareEvents(menuPtr->tkwin, menuPtr->group);
                    822:        Tk_UnmapWindow(menuPtr->tkwin);
                    823:        result = ActivateMenuEntry(menuPtr, -1);
                    824:        if (result == TCL_OK) {
                    825:            result = PostSubmenu(interp, menuPtr, (MenuEntry *) NULL);
                    826:        }
                    827:     } else {
                    828:        Tcl_AppendResult(interp, "bad option \"", argv[1],
                    829:                "\": must be activate, add, configure, delete, disable, ",
                    830:                "enable, entryconfigure, index, invoke, post, ",
                    831:                "or unpost", (char *) NULL);
                    832:        goto error;
                    833:     }
                    834:     done:
                    835:     Tk_Release((ClientData) menuPtr);
                    836:     return result;
                    837: 
                    838:     error:
                    839:     Tk_Release((ClientData) menuPtr);
                    840:     return TCL_ERROR;
                    841: }
                    842: 
                    843: /*
                    844:  *----------------------------------------------------------------------
                    845:  *
                    846:  * DestroyMenu --
                    847:  *
                    848:  *     This procedure is invoked by Tk_EventuallyFree or Tk_Release
                    849:  *     to clean up the internal structure of a menu at a safe time
                    850:  *     (when no-one is using it anymore).
                    851:  *
                    852:  * Results:
                    853:  *     None.
                    854:  *
                    855:  * Side effects:
                    856:  *     Everything associated with the menu is freed up.
                    857:  *
                    858:  *----------------------------------------------------------------------
                    859:  */
                    860: 
                    861: static void
                    862: DestroyMenu(clientData)
                    863:     ClientData clientData;     /* Info about menu widget. */
                    864: {
                    865:     register Menu *menuPtr = (Menu *) clientData;
                    866:     int i;
                    867: 
                    868:     for (i = 0; i < menuPtr->numEntries; i++) {
                    869:        DestroyMenuEntry((ClientData) menuPtr->entries[i]);
                    870:     }
                    871:     if (menuPtr->entries != NULL) {
                    872:        ckfree((char *) menuPtr->entries);
                    873:     }
                    874:     if (menuPtr->border != NULL) {
                    875:        Tk_Free3DBorder(menuPtr->border);
                    876:     }
                    877:     if (menuPtr->activeBorder != NULL) {
                    878:        Tk_Free3DBorder(menuPtr->activeBorder);
                    879:     }
                    880:     if (menuPtr->fontPtr != NULL) {
                    881:        Tk_FreeFontStruct(menuPtr->fontPtr);
                    882:     }
                    883:     if (menuPtr->fg != NULL) {
                    884:        Tk_FreeColor(menuPtr->fg);
                    885:     }
                    886:     if (menuPtr->textGC != None) {
                    887:        Tk_FreeGC(menuPtr->textGC);
                    888:     }
                    889:     if (menuPtr->disabledFg != NULL) {
                    890:        Tk_FreeColor(menuPtr->disabledFg);
                    891:     }
                    892:     if (menuPtr->gray != None) {
                    893:        Tk_FreeBitmap(menuPtr->gray);
                    894:     }
                    895:     if (menuPtr->disabledGC != None) {
                    896:        Tk_FreeGC(menuPtr->disabledGC);
                    897:     }
                    898:     if (menuPtr->activeFg != NULL) {
                    899:        Tk_FreeColor(menuPtr->activeFg);
                    900:     }
                    901:     if (menuPtr->activeGC != None) {
                    902:        Tk_FreeGC(menuPtr->activeGC);
                    903:     }
                    904:     if (menuPtr->selectorFg != NULL) {
                    905:        Tk_FreeColor(menuPtr->selectorFg);
                    906:     }
                    907:     if (menuPtr->selectorGC != None) {
                    908:        Tk_FreeGC(menuPtr->selectorGC);
                    909:     }
                    910:     if (menuPtr->cursor != None) {
                    911:        Tk_FreeCursor(menuPtr->cursor);
                    912:     }
                    913:     ckfree((char *) menuPtr);
                    914: }
                    915: 
                    916: /*
                    917:  *----------------------------------------------------------------------
                    918:  *
                    919:  * DestroyMenuEntry --
                    920:  *
                    921:  *     This procedure is invoked by Tk_EventuallyFree or Tk_Release
                    922:  *     to clean up the internal structure of a menu entry at a safe time
                    923:  *     (when no-one is using it anymore).
                    924:  *
                    925:  * Results:
                    926:  *     None.
                    927:  *
                    928:  * Side effects:
                    929:  *     Everything associated with the menu entry is freed up.
                    930:  *
                    931:  *----------------------------------------------------------------------
                    932:  */
                    933: 
                    934: static void
                    935: DestroyMenuEntry(clientData)
                    936:     ClientData clientData;             /* Pointer to entry to be freed. */
                    937: {
                    938:     register MenuEntry *mePtr = (MenuEntry *) clientData;
                    939:     Menu *menuPtr = mePtr->menuPtr;
                    940: 
                    941:     if (mePtr->name != NULL) {
                    942:        Tcl_UntraceVar(menuPtr->interp, mePtr->name,
                    943:                TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
                    944:                MenuVarProc, (ClientData) mePtr);
                    945:     }
                    946:     if (menuPtr->postedCascade == mePtr) {
                    947:        if (PostSubmenu(menuPtr->interp, menuPtr, (MenuEntry *) NULL)
                    948:                != TCL_OK) {
                    949:            TkBindError(menuPtr->interp);
                    950:        }
                    951:     }
                    952:     if (mePtr->label != NULL) {
                    953:        ckfree(mePtr->label);
                    954:     }
                    955:     if (mePtr->bitmap != None) {
                    956: #if defined(USE_XPM3)
                    957:        Tk_FreePixmap(mePtr->bitmap);
                    958: #else
                    959:        Tk_FreeBitmap(mePtr->bitmap);
                    960: #endif
                    961:     }
                    962:     if (mePtr->accel != NULL) {
                    963:        ckfree(mePtr->accel);
                    964:     }
                    965:     if (mePtr->border != NULL) {
                    966:        Tk_Free3DBorder(mePtr->border);
                    967:     }
                    968:     if (mePtr->activeBorder != NULL) {
                    969:        Tk_Free3DBorder(mePtr->activeBorder);
                    970:     }
                    971:     if (mePtr->fontPtr != NULL) {
                    972:        Tk_FreeFontStruct(mePtr->fontPtr);
                    973:     }
                    974:     if (mePtr->textGC != NULL) {
                    975:        Tk_FreeGC(mePtr->textGC);
                    976:     }
                    977:     if (mePtr->activeGC != NULL) {
                    978:        Tk_FreeGC(mePtr->activeGC);
                    979:     }
                    980:     if (mePtr->disabledGC != NULL) {
                    981:        Tk_FreeGC(mePtr->disabledGC);
                    982:     }
                    983:     if (mePtr->command != NULL) {
                    984:        ckfree(mePtr->command);
                    985:     }
                    986:     if (mePtr->name != NULL) {
                    987:        ckfree(mePtr->name);
                    988:     }
                    989:     if (mePtr->onValue != NULL) {
                    990:        ckfree(mePtr->onValue);
                    991:     }
                    992:     if (mePtr->offValue != NULL) {
                    993:        ckfree(mePtr->offValue);
                    994:     }
                    995:     ckfree((char *) mePtr);
                    996: }
                    997: 
                    998: /*
                    999:  *----------------------------------------------------------------------
                   1000:  *
                   1001:  * ConfigureMenu --
                   1002:  *
                   1003:  *     This procedure is called to process an argv/argc list, plus
                   1004:  *     the Tk option database, in order to configure (or
                   1005:  *     reconfigure) a menu widget.
                   1006:  *
                   1007:  * Results:
                   1008:  *     The return value is a standard Tcl result.  If TCL_ERROR is
                   1009:  *     returned, then interp->result contains an error message.
                   1010:  *
                   1011:  * Side effects:
                   1012:  *     Configuration information, such as colors, font, etc. get set
                   1013:  *     for menuPtr;  old resources get freed, if there were any.
                   1014:  *
                   1015:  *----------------------------------------------------------------------
                   1016:  */
                   1017: 
                   1018: static int
                   1019: ConfigureMenu(interp, menuPtr, argc, argv, flags)
                   1020:     Tcl_Interp *interp;                /* Used for error reporting. */
                   1021:     register Menu *menuPtr;    /* Information about widget;  may or may
                   1022:                                 * not already have values for some fields. */
                   1023:     int argc;                  /* Number of valid entries in argv. */
                   1024:     char **argv;               /* Arguments. */
                   1025:     int flags;                 /* Flags to pass to Tk_ConfigureWidget. */
                   1026: {
                   1027:     XGCValues gcValues;
                   1028:     GC newGC;
                   1029:     unsigned long mask;
                   1030:     int i;
                   1031: 
                   1032:     if (Tk_ConfigureWidget(interp, menuPtr->tkwin, configSpecs,
                   1033:            argc, argv, (char *) menuPtr, flags) != TCL_OK) {
                   1034:        return TCL_ERROR;
                   1035:     }
                   1036: 
                   1037:     /*
                   1038:      * A few options need special processing, such as setting the
                   1039:      * background from a 3-D border, or filling in complicated
                   1040:      * defaults that couldn't be specified to Tk_ConfigureWidget.
                   1041:      */
                   1042: 
                   1043:     Tk_SetBackgroundFromBorder(menuPtr->tkwin, menuPtr->border);
                   1044: 
                   1045:     gcValues.font = menuPtr->fontPtr->fid;
                   1046:     gcValues.foreground = menuPtr->fg->pixel;
                   1047:     gcValues.background = Tk_3DBorderColor(menuPtr->border)->pixel;
                   1048:     newGC = Tk_GetGC(menuPtr->tkwin, GCForeground|GCBackground|GCFont,
                   1049:            &gcValues);
                   1050:     if (menuPtr->textGC != None) {
                   1051:        Tk_FreeGC(menuPtr->textGC);
                   1052:     }
                   1053:     menuPtr->textGC = newGC;
                   1054: 
                   1055:     if (menuPtr->disabledFg != NULL) {
                   1056:        gcValues.foreground = menuPtr->disabledFg->pixel;
                   1057:        mask = GCForeground|GCBackground|GCFont;
                   1058:     } else {
                   1059:        gcValues.foreground = gcValues.background;
                   1060:        if (menuPtr->gray == None) {
                   1061:            menuPtr->gray = Tk_GetBitmap(interp, menuPtr->tkwin,
                   1062:                    Tk_GetUid("gray50"));
                   1063:            if (menuPtr->gray == None) {
                   1064:                return TCL_ERROR;
                   1065:            }
                   1066:        }
                   1067:        gcValues.fill_style = FillStippled;
                   1068:        gcValues.stipple = menuPtr->gray;
                   1069:        mask = GCForeground|GCFillStyle|GCStipple;
                   1070:     }
                   1071:     newGC = Tk_GetGC(menuPtr->tkwin, mask, &gcValues);
                   1072:     if (menuPtr->disabledGC != None) {
                   1073:        Tk_FreeGC(menuPtr->disabledGC);
                   1074:     }
                   1075:     menuPtr->disabledGC = newGC;
                   1076: 
                   1077:     gcValues.font = menuPtr->fontPtr->fid;
                   1078:     gcValues.foreground = menuPtr->activeFg->pixel;
                   1079:     gcValues.background = Tk_3DBorderColor(menuPtr->activeBorder)->pixel;
                   1080:     newGC = Tk_GetGC(menuPtr->tkwin, GCForeground|GCBackground|GCFont,
                   1081:            &gcValues);
                   1082:     if (menuPtr->activeGC != None) {
                   1083:        Tk_FreeGC(menuPtr->activeGC);
                   1084:     }
                   1085:     menuPtr->activeGC = newGC;
                   1086: 
                   1087:     gcValues.foreground = menuPtr->selectorFg->pixel;
                   1088:     newGC = Tk_GetGC(menuPtr->tkwin, GCForeground|GCFont, &gcValues);
                   1089:     if (menuPtr->selectorGC != None) {
                   1090:        Tk_FreeGC(menuPtr->selectorGC);
                   1091:     }
                   1092:     menuPtr->selectorGC = newGC;
                   1093: 
                   1094:     /*
                   1095:      * After reconfiguring a menu, we need to reconfigure all of the
                   1096:      * entries in the menu, since some of the things in the children
                   1097:      * (such as graphics contexts) may have to change to reflect changes
                   1098:      * in the parent.
                   1099:      */
                   1100: 
                   1101:     for (i = 0; i < menuPtr->numEntries; i++) {
                   1102:        MenuEntry *mePtr;
                   1103: 
                   1104:        mePtr = menuPtr->entries[i];
                   1105:        ConfigureMenuEntry(interp, menuPtr, mePtr, i, 0, (char **) NULL,
                   1106:                TK_CONFIG_ARGV_ONLY | COMMAND_MASK << mePtr->type);
                   1107:     }
                   1108: 
                   1109:     if (!(menuPtr->flags & RESIZE_PENDING)) {
                   1110:        menuPtr->flags |= RESIZE_PENDING;
                   1111:        Tk_DoWhenIdle(ComputeMenuGeometry, (ClientData) menuPtr);
                   1112:     }
                   1113: 
                   1114:     return TCL_OK;
                   1115: }
                   1116: 
                   1117: /*
                   1118:  *----------------------------------------------------------------------
                   1119:  *
                   1120:  * ConfigureMenuEntry --
                   1121:  *
                   1122:  *     This procedure is called to process an argv/argc list, plus
                   1123:  *     the Tk option database, in order to configure (or
                   1124:  *     reconfigure) one entry in a menu.
                   1125:  *
                   1126:  * Results:
                   1127:  *     The return value is a standard Tcl result.  If TCL_ERROR is
                   1128:  *     returned, then interp->result contains an error message.
                   1129:  *
                   1130:  * Side effects:
                   1131:  *     Configuration information such as label and accelerator get
                   1132:  *     set for mePtr;  old resources get freed, if there were any.
                   1133:  *
                   1134:  *----------------------------------------------------------------------
                   1135:  */
                   1136: 
                   1137: static int
                   1138: ConfigureMenuEntry(interp, menuPtr, mePtr, index, argc, argv, flags)
                   1139:     Tcl_Interp *interp;                        /* Used for error reporting. */
                   1140:     Menu *menuPtr;                     /* Information about whole menu. */
                   1141:     register MenuEntry *mePtr;         /* Information about menu entry;  may
                   1142:                                         * or may not already have values for
                   1143:                                         * some fields. */
                   1144:     int index;                         /* Index of mePtr within menuPtr's
                   1145:                                         * entries. */
                   1146:     int argc;                          /* Number of valid entries in argv. */
                   1147:     char **argv;                       /* Arguments. */
                   1148:     int flags;                         /* Additional flags to pass to
                   1149:                                         * Tk_ConfigureWidget. */
                   1150: {
                   1151:     XGCValues gcValues;
                   1152:     GC newGC, newActiveGC, newDisabledGC;
                   1153:     unsigned long mask;
                   1154: 
                   1155:     /*
                   1156:      * If this entry is a cascade and the cascade is posted, then unpost
                   1157:      * it before reconfiguring the entry (otherwise the reconfigure might
                   1158:      * change the name of the cascaded entry, leaving a posted menu
                   1159:      * high and dry).
                   1160:      */
                   1161: 
                   1162:     if (menuPtr->postedCascade == mePtr) {
                   1163:        if (PostSubmenu(menuPtr->interp, menuPtr, (MenuEntry *) NULL)
                   1164:                != TCL_OK) {
                   1165:            TkBindError(menuPtr->interp);
                   1166:        }
                   1167:     }
                   1168: 
                   1169:     /*
                   1170:      * If this entry is a check button or radio button, then remove
                   1171:      * its old trace procedure.
                   1172:      */
                   1173: 
                   1174:     if ((mePtr->name != NULL) &&
                   1175:            ((mePtr->type == CHECK_BUTTON_ENTRY)
                   1176:            || (mePtr->type == RADIO_BUTTON_ENTRY))) {
                   1177:        Tcl_UntraceVar(menuPtr->interp, mePtr->name,
                   1178:                TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
                   1179:                MenuVarProc, (ClientData) mePtr);
                   1180:     }
                   1181: 
                   1182:     if (Tk_ConfigureWidget(interp, menuPtr->tkwin, entryConfigSpecs,
                   1183:            argc, argv, (char *) mePtr,
                   1184:            flags | (COMMAND_MASK << mePtr->type)) != TCL_OK) {
                   1185:        return TCL_ERROR;
                   1186:     }
                   1187: 
                   1188:     /*
                   1189:      * The code below handles special configuration stuff not taken
                   1190:      * care of by Tk_ConfigureWidget, such as special processing for
                   1191:      * defaults, sizing strings, graphics contexts, etc.
                   1192:      */
                   1193: 
                   1194:     if (mePtr->label == NULL) {
                   1195:        mePtr->labelLength = 0;
                   1196:     } else {
                   1197:        mePtr->labelLength = strlen(mePtr->label);
                   1198:     }
                   1199:     if (mePtr->accel == NULL) {
                   1200:        mePtr->accelLength = 0;
                   1201:     } else {
                   1202:        mePtr->accelLength = strlen(mePtr->accel);
                   1203:     }
                   1204: 
                   1205:     if (mePtr->state == tkActiveUid) {
                   1206:        if (index != menuPtr->active) {
                   1207:            ActivateMenuEntry(menuPtr, index);
                   1208:        }
                   1209:     } else {
                   1210:        if (index == menuPtr->active) {
                   1211:            ActivateMenuEntry(menuPtr, -1);
                   1212:        }
                   1213:        if ((mePtr->state != tkNormalUid) && (mePtr->state != tkDisabledUid)) {
                   1214:            Tcl_AppendResult(interp, "bad state value \"", mePtr->state,
                   1215:                    "\":  must be normal, active, or disabled", (char *) NULL);
                   1216:            mePtr->state = tkNormalUid;
                   1217:            return TCL_ERROR;
                   1218:        }
                   1219:     }
                   1220: 
                   1221:     if (mePtr->fontPtr != NULL) {
                   1222:        gcValues.foreground = menuPtr->fg->pixel;
                   1223:        gcValues.background = Tk_3DBorderColor(
                   1224:                (mePtr->border != NULL) ? mePtr->border : menuPtr->border)
                   1225:                ->pixel;
                   1226:        gcValues.font = mePtr->fontPtr->fid;
                   1227: 
                   1228:        /*
                   1229:         * Note: disable GraphicsExpose events;  we know there won't be
                   1230:         * obscured areas when copying from an off-screen pixmap to the
                   1231:         * screen and this gets rid of unnecessary events.
                   1232:         */
                   1233: 
                   1234:        gcValues.graphics_exposures = False;
                   1235:        newGC = Tk_GetGC(menuPtr->tkwin,
                   1236:                GCForeground|GCBackground|GCFont|GCGraphicsExposures,
                   1237:                &gcValues);
                   1238: 
                   1239:        if (menuPtr->disabledFg != NULL) {
                   1240:            gcValues.foreground = menuPtr->disabledFg->pixel;
                   1241:            mask = GCForeground|GCBackground|GCFont|GCGraphicsExposures;
                   1242:        } else {
                   1243:            gcValues.foreground = gcValues.background;
                   1244:            gcValues.fill_style = FillStippled;
                   1245:            gcValues.stipple = menuPtr->gray;
                   1246:            mask = GCForeground|GCFillStyle|GCStipple;
                   1247:        }
                   1248:        newDisabledGC = Tk_GetGC(menuPtr->tkwin, mask, &gcValues);
                   1249: 
                   1250:        gcValues.foreground = menuPtr->activeFg->pixel;
                   1251:        gcValues.background = Tk_3DBorderColor(
                   1252:                (mePtr->activeBorder != NULL) ? mePtr->activeBorder
                   1253:                : menuPtr->activeBorder)->pixel;
                   1254:        newActiveGC = Tk_GetGC(menuPtr->tkwin,
                   1255:                GCForeground|GCBackground|GCFont|GCGraphicsExposures,
                   1256:                &gcValues);
                   1257:     } else {
                   1258:        newGC = NULL;
                   1259:        newActiveGC = NULL;
                   1260:        newDisabledGC = NULL;
                   1261:     }
                   1262:     if (mePtr->textGC != NULL) {
                   1263:            Tk_FreeGC(mePtr->textGC);
                   1264:     }
                   1265:     mePtr->textGC = newGC;
                   1266:     if (mePtr->activeGC != NULL) {
                   1267:            Tk_FreeGC(mePtr->activeGC);
                   1268:     }
                   1269:     mePtr->activeGC = newActiveGC;
                   1270:     if (mePtr->disabledGC != NULL) {
                   1271:            Tk_FreeGC(mePtr->disabledGC);
                   1272:     }
                   1273:     mePtr->disabledGC = newDisabledGC;
                   1274: 
                   1275:     if ((mePtr->type == CHECK_BUTTON_ENTRY)
                   1276:            || (mePtr->type == RADIO_BUTTON_ENTRY)) {
                   1277:        char *value;
                   1278: 
                   1279:        if (mePtr->name == NULL) {
                   1280:            mePtr->name = ckalloc((unsigned) (strlen(mePtr->label) + 1));
                   1281:            strcpy(mePtr->name, mePtr->label);
                   1282:        }
                   1283:        if (mePtr->onValue == NULL) {
                   1284:            mePtr->onValue = ckalloc((unsigned) (strlen(mePtr->label) + 1));
                   1285:            strcpy(mePtr->onValue, mePtr->label);
                   1286:        }
                   1287: 
                   1288:        /*
                   1289:         * Select the entry if the associated variable has the
                   1290:         * appropriate value, initialize the variable if it doesn't
                   1291:         * exist, then set a trace on the variable to monitor future
                   1292:         * changes to its value.
                   1293:         */
                   1294: 
                   1295:        value = Tcl_GetVar(interp, mePtr->name, TCL_GLOBAL_ONLY);
                   1296:        mePtr->flags &= ENTRY_SELECTED;
                   1297:        if (value != NULL) {
                   1298:            if (strcmp(value, mePtr->onValue) == 0) {
                   1299:                mePtr->flags |= ENTRY_SELECTED;
                   1300:            }
                   1301:        } else {
                   1302:            Tcl_SetVar(interp, mePtr->name,
                   1303:                    (mePtr->type == CHECK_BUTTON_ENTRY) ? mePtr->offValue : "",
                   1304:                    TCL_GLOBAL_ONLY);
                   1305:        }
                   1306:        Tcl_TraceVar(interp, mePtr->name,
                   1307:                TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
                   1308:                MenuVarProc, (ClientData) mePtr);
                   1309:     }
                   1310: 
                   1311:     if (!(menuPtr->flags & RESIZE_PENDING)) {
                   1312:        menuPtr->flags |= RESIZE_PENDING;
                   1313:        Tk_DoWhenIdle(ComputeMenuGeometry, (ClientData) menuPtr);
                   1314:     }
                   1315:     return TCL_OK;
                   1316: }
                   1317: 
                   1318: /*
                   1319:  *--------------------------------------------------------------
                   1320:  *
                   1321:  * ComputeMenuGeometry --
                   1322:  *
                   1323:  *     This procedure is invoked to recompute the size and
                   1324:  *     layout of a menu.  It is called as a when-idle handler so
                   1325:  *     that it only gets done once, even if a group of changes is
                   1326:  *     made to the menu.
                   1327:  *
                   1328:  * Results:
                   1329:  *     None.
                   1330:  *
                   1331:  * Side effects:
                   1332:  *     Fields of menu entries are changed to reflect their
                   1333:  *     current positions, and the size of the menu window
                   1334:  *     itself may be changed.
                   1335:  *
                   1336:  *--------------------------------------------------------------
                   1337:  */
                   1338: 
                   1339: static void
                   1340: ComputeMenuGeometry(clientData)
                   1341:     ClientData clientData;             /* Structure describing menu. */
                   1342: {
                   1343:     Menu *menuPtr = (Menu *) clientData;
                   1344:     register MenuEntry *mePtr;
                   1345:     XFontStruct *fontPtr;
                   1346:     int maxLabelWidth, maxSelectorWidth, maxAccelWidth;
                   1347:     int width, height, selectorSpace, horizMargin;
                   1348:     int i, y;
                   1349: 
                   1350:     if (menuPtr->tkwin == NULL) {
                   1351:        return;
                   1352:     }
                   1353: 
                   1354:     maxLabelWidth = maxSelectorWidth = maxAccelWidth = 0;
                   1355:     y = menuPtr->borderWidth;
                   1356: 
                   1357:     for (i = 0; i < menuPtr->numEntries; i++) {
                   1358:        mePtr = menuPtr->entries[i];
                   1359:        selectorSpace = 0;
                   1360:        fontPtr = mePtr->fontPtr;
                   1361:        if (fontPtr == NULL) {
                   1362:            fontPtr = menuPtr->fontPtr;
                   1363:        }
                   1364: 
                   1365:        /*
                   1366:         * For each entry, compute the height required by that
                   1367:         * particular entry, plus three widths:  the width of the
                   1368:         * label, the width to allow for a selector to be displayed
                   1369:         * to the left of the label (if any), and the width of the
                   1370:         * accelerator to be displayed to the right of the label
                   1371:         * (if any).  These sizes depend, of course, on the type
                   1372:         * of the entry.
                   1373:         */
                   1374: 
                   1375:        if (mePtr->bitmap != None) {
                   1376:            unsigned int bitmapWidth, bitmapHeight;
                   1377: 
                   1378: #if defined(USE_XPM3)
                   1379:            Tk_SizeOfPixmap(mePtr->bitmap, &bitmapWidth, &bitmapHeight);
                   1380: #else
                   1381:            Tk_SizeOfBitmap(mePtr->bitmap, &bitmapWidth, &bitmapHeight);
                   1382: #endif
                   1383:            mePtr->height = bitmapHeight;
                   1384:            width = bitmapWidth;
                   1385:            if (mePtr->type == CHECK_BUTTON_ENTRY) {
                   1386:                selectorSpace = (14*mePtr->height)/10;
                   1387:                mePtr->selectorDiameter = (65*mePtr->height)/100;
                   1388:            } else if (mePtr->type == RADIO_BUTTON_ENTRY) {
                   1389:                selectorSpace = (14*mePtr->height)/10;
                   1390:                mePtr->selectorDiameter = (75*mePtr->height)/100;
                   1391:            }
                   1392:        } else {
                   1393:            mePtr->height = fontPtr->ascent + fontPtr->descent;
                   1394:            if (mePtr->label != NULL) {
                   1395:                (void) TkMeasureChars(fontPtr, mePtr->label,
                   1396:                        mePtr->labelLength, 0, (int) 100000,
                   1397:                        TK_NEWLINES_NOT_SPECIAL, &width);
                   1398:            } else {
                   1399:                width = 0;
                   1400:            }
                   1401:            if (mePtr->type == CHECK_BUTTON_ENTRY) {
                   1402:                selectorSpace = mePtr->height;
                   1403:                mePtr->selectorDiameter = (80*mePtr->height)/100;
                   1404:            } else if (mePtr->type == RADIO_BUTTON_ENTRY) {
                   1405:                selectorSpace = mePtr->height;
                   1406:                mePtr->selectorDiameter = mePtr->height;
                   1407:            }
                   1408:        }
                   1409:        mePtr->height += 2*menuPtr->activeBorderWidth + 2;
                   1410:        if (width > maxLabelWidth) {
                   1411:            maxLabelWidth = width;
                   1412:        }
                   1413:        if (mePtr->accel != NULL) {
                   1414:            (void) TkMeasureChars(fontPtr, mePtr->accel, mePtr->accelLength,
                   1415:                    0, (int) 100000, TK_NEWLINES_NOT_SPECIAL, &width);
                   1416:            if (width > maxAccelWidth) {
                   1417:                maxAccelWidth = width;
                   1418:            }
                   1419:        }
                   1420:        if (mePtr->type == SEPARATOR_ENTRY) {
                   1421:            mePtr->height = 4*menuPtr->borderWidth;
                   1422:        }
                   1423:        if (selectorSpace > maxSelectorWidth) {
                   1424:            maxSelectorWidth = selectorSpace;
                   1425:        }
                   1426:        mePtr->y = y;
                   1427:        y += mePtr->height;
                   1428:     }
                   1429: 
                   1430:     /*
                   1431:      * Got all the sizes.  Update fields in the menu structure, then
                   1432:      * resize the window if necessary.  Leave margins on either side
                   1433:      * of the selector (or just one margin if there is no selector).
                   1434:      * Leave another margin on the right side of the label, plus yet
                   1435:      * another margin to the right of the accelerator (if there is one).
                   1436:      */
                   1437: 
                   1438:     horizMargin = 2;
                   1439:     menuPtr->selectorSpace = maxSelectorWidth + horizMargin;
                   1440:     if (maxSelectorWidth != 0) {
                   1441:        menuPtr->selectorSpace += horizMargin;
                   1442:     }
                   1443:     menuPtr->labelWidth = maxLabelWidth + horizMargin;
                   1444:     width = menuPtr->selectorSpace + menuPtr->labelWidth + maxAccelWidth
                   1445:            + 2*menuPtr->borderWidth + 2*menuPtr->activeBorderWidth + 2;
                   1446:     if (maxAccelWidth != 0) {
                   1447:        width += horizMargin;
                   1448:     }
                   1449:     height = y + menuPtr->borderWidth;
                   1450: 
                   1451:     /*
                   1452:      * The X server doesn't like zero dimensions, so round up to at least
                   1453:      * 1 (a zero-sized menu should never really occur, anyway).
                   1454:      */
                   1455: 
                   1456:     if (width <= 0) {
                   1457:        width = 1;
                   1458:     }
                   1459:     if (height <= 0) {
                   1460:        height = 1;
                   1461:     }
                   1462:     if ((width != Tk_ReqWidth(menuPtr->tkwin)) ||
                   1463:            (height != Tk_ReqHeight(menuPtr->tkwin))) {
                   1464:        Tk_GeometryRequest(menuPtr->tkwin, width, height);
                   1465:     } else {
                   1466:        /*
                   1467:         * Must always force a redisplay here if the window is mapped
                   1468:         * (even if the size didn't change, something else might have
                   1469:         * changed in the menu, such as a label or accelerator).  The
                   1470:         * resize will force a redisplay above.
                   1471:         */
                   1472: 
                   1473:        EventuallyRedrawMenu(menuPtr, -1);
                   1474:     }
                   1475: 
                   1476:     menuPtr->flags &= ~RESIZE_PENDING;
                   1477: }
                   1478: 
                   1479: /*
                   1480:  *----------------------------------------------------------------------
                   1481:  *
                   1482:  * DisplayMenu --
                   1483:  *
                   1484:  *     This procedure is invoked to display a menu widget.
                   1485:  *
                   1486:  * Results:
                   1487:  *     None.
                   1488:  *
                   1489:  * Side effects:
                   1490:  *     Commands are output to X to display the menu in its
                   1491:  *     current mode.
                   1492:  *
                   1493:  *----------------------------------------------------------------------
                   1494:  */
                   1495: 
                   1496: static void
                   1497: DisplayMenu(clientData)
                   1498:     ClientData clientData;     /* Information about widget. */
                   1499: {
                   1500:     register Menu *menuPtr = (Menu *) clientData;
                   1501:     register MenuEntry *mePtr;
                   1502:     register Tk_Window tkwin = menuPtr->tkwin;
                   1503:     XFontStruct *fontPtr;
                   1504:     int index, baseline;
                   1505:     GC gc;
                   1506: 
                   1507:     menuPtr->flags &= ~REDRAW_PENDING;
                   1508:     if ((menuPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
                   1509:        return;
                   1510:     }
                   1511: 
                   1512:     /*
                   1513:      * Loop through all of the entries, drawing them one at a time.
                   1514:      */
                   1515: 
                   1516:     for (index = 0; index < menuPtr->numEntries; index++) {
                   1517:        mePtr = menuPtr->entries[index];
                   1518:        if (!(mePtr->flags & ENTRY_NEEDS_REDISPLAY)) {
                   1519:            continue;
                   1520:        }
                   1521:        mePtr->flags &= ~ENTRY_NEEDS_REDISPLAY;
                   1522: 
                   1523:        /*
                   1524:         * Background.
                   1525:         */
                   1526: 
                   1527:        if (mePtr->state == tkActiveUid) {
                   1528:            Tk_Fill3DRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin),
                   1529:                    (mePtr->activeBorder != NULL) ? mePtr->activeBorder
                   1530:                    : menuPtr->activeBorder, menuPtr->borderWidth, mePtr->y,
                   1531:                    Tk_Width(tkwin) - 2*menuPtr->borderWidth, mePtr->height,
                   1532:                    menuPtr->activeBorderWidth, TK_RELIEF_RAISED);
                   1533:            gc = mePtr->activeGC;
                   1534:            if (gc == NULL) {
                   1535:                gc = menuPtr->activeGC;
                   1536:            }
                   1537:        } else {
                   1538:            Tk_Fill3DRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin),
                   1539:                    (mePtr->border != NULL) ? mePtr->border
                   1540:                    : menuPtr->border, menuPtr->borderWidth, mePtr->y,
                   1541:                    Tk_Width(tkwin) - 2*menuPtr->borderWidth, mePtr->height,
                   1542:                    0, TK_RELIEF_FLAT);
                   1543:            if ((mePtr->state == tkDisabledUid)
                   1544:                    && (menuPtr->disabledFg != NULL)) {
                   1545:                gc = mePtr->disabledGC;
                   1546:                if (gc == NULL) {
                   1547:                    gc = menuPtr->disabledGC;
                   1548:                }
                   1549:            } else {
                   1550:                gc = mePtr->textGC;
                   1551:                if (gc == NULL) {
                   1552:                    gc = menuPtr->textGC;
                   1553:                }
                   1554:            }
                   1555:        }
                   1556: 
                   1557:        /*
                   1558:         * Draw label or bitmap for entry.
                   1559:         */
                   1560: 
                   1561:        fontPtr = mePtr->fontPtr;
                   1562:        if (fontPtr == NULL) {
                   1563:            fontPtr = menuPtr->fontPtr;
                   1564:        }
                   1565:        baseline = mePtr->y + (mePtr->height + fontPtr->ascent
                   1566:                - fontPtr->descent)/2;
                   1567:        if (mePtr->bitmap != None) {
                   1568:            unsigned int width, height;
                   1569: 
                   1570: #if defined(USE_XPM3)        
                   1571:            Tk_SizeOfPixmap(mePtr->bitmap, &width, &height);
                   1572:             XCopyArea(Tk_Display(tkwin), mePtr->bitmap, Tk_WindowId(tkwin),
                   1573:                    gc, 0, 0, width, height,
                   1574:                    menuPtr->borderWidth + menuPtr->selectorSpace,
                   1575:                    (int) (mePtr->y + (mePtr->height - height)/2));
                   1576: #else
                   1577:            Tk_SizeOfBitmap(mePtr->bitmap, &width, &height);
                   1578:            XCopyPlane(Tk_Display(tkwin), mePtr->bitmap, Tk_WindowId(tkwin),
                   1579:                    gc, 0, 0, width, height,
                   1580:                    menuPtr->borderWidth + menuPtr->selectorSpace,
                   1581:                    (int) (mePtr->y + (mePtr->height - height)/2), 1);
                   1582: #endif
                   1583:        } else {
                   1584:            baseline = mePtr->y + (mePtr->height + fontPtr->ascent
                   1585:                    - fontPtr->descent)/2;
                   1586:            if (mePtr->label != NULL) {
                   1587:                TkDisplayChars(Tk_Display(tkwin), Tk_WindowId(tkwin), gc,
                   1588:                        fontPtr, mePtr->label, mePtr->labelLength,
                   1589:                        menuPtr->borderWidth + menuPtr->selectorSpace,
                   1590:                        baseline, TK_NEWLINES_NOT_SPECIAL);
                   1591:                if (mePtr->underline >= 0) {
                   1592:                    TkUnderlineChars(Tk_Display(tkwin), Tk_WindowId(tkwin), gc,
                   1593:                            fontPtr, mePtr->label,
                   1594:                            menuPtr->borderWidth + menuPtr->selectorSpace,
                   1595:                            baseline, TK_NEWLINES_NOT_SPECIAL,
                   1596:                            mePtr->underline, mePtr->underline);
                   1597:                }
                   1598:            }
                   1599:        }
                   1600: 
                   1601:        /*
                   1602:         * Draw accelerator.
                   1603:         */
                   1604: 
                   1605:        if (mePtr->accel != NULL) {
                   1606:            TkDisplayChars(Tk_Display(tkwin), Tk_WindowId(tkwin), gc,
                   1607:                    fontPtr, mePtr->accel, mePtr->accelLength,
                   1608:                    menuPtr->borderWidth + menuPtr->selectorSpace
                   1609:                    + menuPtr->labelWidth, baseline, TK_NEWLINES_NOT_SPECIAL);
                   1610:        }
                   1611: 
                   1612:        /*
                   1613:         * Draw check-button selector.
                   1614:         */
                   1615: 
                   1616:        if (mePtr->type == CHECK_BUTTON_ENTRY) {
                   1617:            int dim, x, y;
                   1618: 
                   1619:            dim = mePtr->selectorDiameter;
                   1620:            x = menuPtr->borderWidth + (menuPtr->selectorSpace - dim)/2;
                   1621:            y = mePtr->y + (mePtr->height - dim)/2;
                   1622:            Tk_Fill3DRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin),
                   1623:                    menuPtr->border, x, y, dim, dim,
                   1624:                    menuPtr->activeBorderWidth, TK_RELIEF_SUNKEN);
                   1625:            x += menuPtr->activeBorderWidth;
                   1626:            y += menuPtr->activeBorderWidth;
                   1627:            dim -= 2*menuPtr->activeBorderWidth;
                   1628:            if ((dim > 0) && (mePtr->flags & ENTRY_SELECTED)) {
                   1629:                XFillRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin),
                   1630:                        menuPtr->selectorGC, x, y, (unsigned int) dim,
                   1631:                        (unsigned int) dim);
                   1632:            }
                   1633:        }
                   1634: 
                   1635:        /*
                   1636:         * Draw radio-button selector.
                   1637:         */
                   1638: 
                   1639:        if (mePtr->type == RADIO_BUTTON_ENTRY) {
                   1640:            XPoint points[4];
                   1641:            int radius;
                   1642: 
                   1643:            radius = mePtr->selectorDiameter/2;
                   1644:            points[0].x = menuPtr->borderWidth
                   1645:                    + (menuPtr->selectorSpace - mePtr->selectorDiameter)/2;
                   1646:            points[0].y = mePtr->y + (mePtr->height)/2;
                   1647:            points[1].x = points[0].x + radius;
                   1648:            points[1].y = points[0].y + radius;
                   1649:            points[2].x = points[1].x + radius;
                   1650:            points[2].y = points[0].y;
                   1651:            points[3].x = points[1].x;
                   1652:            points[3].y = points[0].y - radius;
                   1653:            if (mePtr->flags & ENTRY_SELECTED) {
                   1654:                XFillPolygon(Tk_Display(tkwin), Tk_WindowId(tkwin),
                   1655:                        menuPtr->selectorGC, points, 4, Convex,
                   1656:                        CoordModeOrigin);
                   1657:            } else {
                   1658:                Tk_Fill3DPolygon(Tk_Display(tkwin), Tk_WindowId(tkwin),
                   1659:                        menuPtr->border, points, 4, menuPtr->activeBorderWidth,
                   1660:                        TK_RELIEF_FLAT);
                   1661:            }
                   1662:            Tk_Draw3DPolygon(Tk_Display(tkwin), Tk_WindowId(tkwin),
                   1663:                    menuPtr->border, points, 4, menuPtr->activeBorderWidth,
                   1664:                    TK_RELIEF_SUNKEN);
                   1665:        }
                   1666: 
                   1667:        /*
                   1668:         * Draw separator.
                   1669:         */
                   1670: 
                   1671:        if (mePtr->type == SEPARATOR_ENTRY) {
                   1672:            XPoint points[2];
                   1673:            int margin;
                   1674: 
                   1675:            margin = (fontPtr->ascent + fontPtr->descent)/2;
                   1676:            points[0].x = 2*menuPtr->borderWidth + margin;
                   1677:            points[0].y = mePtr->y + mePtr->height/2;
                   1678:            points[1].x = Tk_Width(tkwin) - 2*menuPtr->borderWidth - margin;
                   1679:            points[1].y = points[0].y;
                   1680:            Tk_Draw3DPolygon(Tk_Display(tkwin), Tk_WindowId(tkwin),
                   1681:                    menuPtr->border, points, 2, 1, TK_RELIEF_RAISED);
                   1682:        }
                   1683: 
                   1684:        /*
                   1685:         * If the entry is disabled with a stipple rather than a special
                   1686:         * foreground color, generate the stippled effect.
                   1687:         */
                   1688: 
                   1689:        if ((mePtr->state == tkDisabledUid) && (menuPtr->disabledFg == NULL)) {
                   1690:            XFillRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin),
                   1691:                    menuPtr->disabledGC, menuPtr->borderWidth,
                   1692:                    mePtr->y,
                   1693:                    (unsigned) (Tk_Width(tkwin) - 2*menuPtr->borderWidth),
                   1694:                    (unsigned) mePtr->height);
                   1695:        }
                   1696:     }
                   1697: 
                   1698:     Tk_Draw3DRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin),
                   1699:            menuPtr->border, 0, 0, Tk_Width(tkwin), Tk_Height(tkwin),
                   1700:            menuPtr->borderWidth, TK_RELIEF_RAISED);
                   1701: }
                   1702: 
                   1703: /*
                   1704:  *--------------------------------------------------------------
                   1705:  *
                   1706:  * GetMenuIndex --
                   1707:  *
                   1708:  *     Parse a textual index into a menu and return the numerical
                   1709:  *     index of the indicated entry.
                   1710:  *
                   1711:  * Results:
                   1712:  *     A standard Tcl result.  If all went well, then *indexPtr is
                   1713:  *     filled in with the entry index corresponding to string
                   1714:  *     (ranges from -1 to the number of entries in the menu minus
                   1715:  *     one).  Otherwise an error message is left in interp->result.
                   1716:  *
                   1717:  * Side effects:
                   1718:  *     None.
                   1719:  *
                   1720:  *--------------------------------------------------------------
                   1721:  */
                   1722: 
                   1723: static int
                   1724: GetMenuIndex(interp, menuPtr, string, indexPtr)
                   1725:     Tcl_Interp *interp;                /* For error messages. */
                   1726:     Menu *menuPtr;             /* Menu for which the index is being
                   1727:                                 * specified. */
                   1728:     char *string;              /* Specification of an entry in menu.  See
                   1729:                                 * manual entry for valid .*/
                   1730:     int *indexPtr;             /* Where to store converted relief. */
                   1731: {
                   1732:     int i, y;
                   1733: 
                   1734:     if ((string[0] == 'a') && (strcmp(string, "active") == 0)) {
                   1735:        *indexPtr = menuPtr->active;
                   1736:        return TCL_OK;
                   1737:     }
                   1738: 
                   1739:     if ((string[0] == 'l') && (strcmp(string, "last") == 0)) {
                   1740:        *indexPtr = menuPtr->numEntries-1;
                   1741:        return TCL_OK;
                   1742:     }
                   1743: 
                   1744:     if ((string[0] == 'n') && (strcmp(string, "none") == 0)) {
                   1745:        *indexPtr = -1;
                   1746:        return TCL_OK;
                   1747:     }
                   1748: 
                   1749:     if (string[0] == '@') {
                   1750:        if (Tcl_GetInt(interp, string+1,  &y) == TCL_OK) {
                   1751:            if (y < 0) {
                   1752:                *indexPtr = -1;
                   1753:                return TCL_OK;
                   1754:            }
                   1755:            for (i = 0; i < menuPtr->numEntries; i++) {
                   1756:                y -= menuPtr->entries[i]->height;
                   1757:                if (y < 0) {
                   1758:                    break;
                   1759:                }
                   1760:            }
                   1761:            if (i >= menuPtr->numEntries) {
                   1762:                i = -1;
                   1763:            }
                   1764:            *indexPtr = i;
                   1765:            return TCL_OK;
                   1766:        } else {
                   1767:            Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);
                   1768:        }
                   1769:     }
                   1770: 
                   1771:     if (isdigit(string[0])) {
                   1772:        if (Tcl_GetInt(interp, string,  &i) == TCL_OK) {
                   1773:            if ((i < menuPtr->numEntries) && (i >= 0)) {
                   1774:                *indexPtr = i;
                   1775:                return TCL_OK;
                   1776:            }
                   1777:        } else {
                   1778:            Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);
                   1779:        }
                   1780:     }
                   1781: 
                   1782:     for (i = 0; i < menuPtr->numEntries; i++) {
                   1783:        char *label;
                   1784: 
                   1785:        label = menuPtr->entries[i]->label;
                   1786:        if ((label != NULL)
                   1787:                && (Tcl_StringMatch(menuPtr->entries[i]->label, string))) {
                   1788:            *indexPtr = i;
                   1789:            return TCL_OK;
                   1790:        }
                   1791:     }
                   1792: 
                   1793:     Tcl_AppendResult(interp, "bad menu entry index \"",
                   1794:            string, "\"", (char *) NULL);
                   1795:     return TCL_ERROR;
                   1796: }
                   1797: 
                   1798: /*
                   1799:  *--------------------------------------------------------------
                   1800:  *
                   1801:  * MenuEventProc --
                   1802:  *
                   1803:  *     This procedure is invoked by the Tk dispatcher for various
                   1804:  *     events on menus.
                   1805:  *
                   1806:  * Results:
                   1807:  *     None.
                   1808:  *
                   1809:  * Side effects:
                   1810:  *     When the window gets deleted, internal structures get
                   1811:  *     cleaned up.  When it gets exposed, it is redisplayed.
                   1812:  *
                   1813:  *--------------------------------------------------------------
                   1814:  */
                   1815: 
                   1816: static void
                   1817: MenuEventProc(clientData, eventPtr)
                   1818:     ClientData clientData;     /* Information about window. */
                   1819:     XEvent *eventPtr;          /* Information about event. */
                   1820: {
                   1821:     Menu *menuPtr = (Menu *) clientData;
                   1822:     if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
                   1823:        EventuallyRedrawMenu(menuPtr, -1);
                   1824:     } else if (eventPtr->type == DestroyNotify) {
                   1825:        Tcl_DeleteCommand(menuPtr->interp, Tk_PathName(menuPtr->tkwin));
                   1826: 
                   1827:        /*
                   1828:         * Careful!  Must delete the event-sharing information here
                   1829:         * rather than in DestroyMenu.  By the time that procedure
                   1830:         * is called the tkwin may have been reused, resulting in some
                   1831:         * other window accidentally being cut off from shared events.
                   1832:         */
                   1833: 
                   1834:        Tk_UnshareEvents(menuPtr->tkwin, menuPtr->group);
                   1835:        menuPtr->tkwin = NULL;
                   1836:        if (menuPtr->flags & REDRAW_PENDING) {
                   1837:            Tk_CancelIdleCall(DisplayMenu, (ClientData) menuPtr);
                   1838:        }
                   1839:        if (menuPtr->flags & RESIZE_PENDING) {
                   1840:            Tk_CancelIdleCall(ComputeMenuGeometry, (ClientData) menuPtr);
                   1841:        }
                   1842:        Tk_EventuallyFree((ClientData) menuPtr, DestroyMenu);
                   1843:     }
                   1844: }
                   1845: 
                   1846: /*
                   1847:  *--------------------------------------------------------------
                   1848:  *
                   1849:  * MenuVarProc --
                   1850:  *
                   1851:  *     This procedure is invoked when someone changes the
                   1852:  *     state variable associated with a radiobutton or checkbutton
                   1853:  *     menu entry.  The entry's selected state is set to match
                   1854:  *     the value of the variable.
                   1855:  *
                   1856:  * Results:
                   1857:  *     NULL is always returned.
                   1858:  *
                   1859:  * Side effects:
                   1860:  *     The menu entry may become selected or deselected.
                   1861:  *
                   1862:  *--------------------------------------------------------------
                   1863:  */
                   1864: 
                   1865:        /* ARGSUSED */
                   1866: static char *
                   1867: MenuVarProc(clientData, interp, name1, name2, flags)
                   1868:     ClientData clientData;     /* Information about menu entry. */
                   1869:     Tcl_Interp *interp;                /* Interpreter containing variable. */
                   1870:     char *name1;               /* First part of variable's name. */
                   1871:     char *name2;               /* Second part of variable's name. */
                   1872:     int flags;                 /* Describes what just happened. */
                   1873: {
                   1874:     MenuEntry *mePtr = (MenuEntry *) clientData;
                   1875:     Menu *menuPtr;
                   1876:     char *value;
                   1877: 
                   1878:     menuPtr = mePtr->menuPtr;
                   1879: 
                   1880:     /*
                   1881:      * If the variable is being unset, then re-establish the
                   1882:      * trace unless the whole interpreter is going away.
                   1883:      */
                   1884: 
                   1885:     if (flags & TCL_TRACE_UNSETS) {
                   1886:        mePtr->flags &= ~ENTRY_SELECTED;
                   1887:        if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
                   1888:            Tcl_TraceVar2(interp, name1, name2,
                   1889:                    TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
                   1890:                    MenuVarProc, clientData);
                   1891:        }
                   1892:        EventuallyRedrawMenu(menuPtr, -1);
                   1893:        return (char *) NULL;
                   1894:     }
                   1895: 
                   1896:     /*
                   1897:      * Use the value of the variable to update the selected status of
                   1898:      * the menu entry.
                   1899:      */
                   1900: 
                   1901:     value = Tcl_GetVar2(interp, name1, name2, flags & TCL_GLOBAL_ONLY);
                   1902:     if (strcmp(value, mePtr->onValue) == 0) {
                   1903:        if (mePtr->flags & ENTRY_SELECTED) {
                   1904:            return (char *) NULL;
                   1905:        }
                   1906:        mePtr->flags |= ENTRY_SELECTED;
                   1907:     } else if (mePtr->flags & ENTRY_SELECTED) {
                   1908:        mePtr->flags &= ~ENTRY_SELECTED;
                   1909:     } else {
                   1910:        return (char *) NULL;
                   1911:     }
                   1912:     EventuallyRedrawMenu(menuPtr, -1);
                   1913:     return (char *) NULL;
                   1914: }
                   1915: 
                   1916: /*
                   1917:  *----------------------------------------------------------------------
                   1918:  *
                   1919:  * EventuallyRedrawMenu --
                   1920:  *
                   1921:  *     Arrange for an entry of a menu, or the whole menu, to be
                   1922:  *     redisplayed at some point in the future.
                   1923:  *
                   1924:  * Results:
                   1925:  *     None.
                   1926:  *
                   1927:  * Side effects:
                   1928:  *     A when-idle hander is scheduled to do the redisplay, if there
                   1929:  *     isn't one already scheduled.
                   1930:  *
                   1931:  *----------------------------------------------------------------------
                   1932:  */
                   1933: 
                   1934: static void
                   1935: EventuallyRedrawMenu(menuPtr, index)
                   1936:     register Menu *menuPtr;    /* Information about menu to redraw. */
                   1937:     int index;                 /* Which entry to redraw.  If -1, then
                   1938:                                 * all the entries in the menu are redrawn. */
                   1939: {
                   1940:     if (menuPtr->tkwin == NULL) {
                   1941:        return;
                   1942:     }
                   1943:     if (index != -1) {
                   1944:        menuPtr->entries[index]->flags |= ENTRY_NEEDS_REDISPLAY;
                   1945:     } else {
                   1946:        for (index = 0; index < menuPtr->numEntries; index++) {
                   1947:            menuPtr->entries[index]->flags |= ENTRY_NEEDS_REDISPLAY;
                   1948:        }
                   1949:     }
                   1950:     if ((menuPtr->tkwin == NULL) || !Tk_IsMapped(menuPtr->tkwin)
                   1951:            || (menuPtr->flags & REDRAW_PENDING)) {
                   1952:        return;
                   1953:     }
                   1954:     Tk_DoWhenIdle(DisplayMenu, (ClientData) menuPtr);
                   1955:     menuPtr->flags |= REDRAW_PENDING;
                   1956: }
                   1957: 
                   1958: /*
                   1959:  *--------------------------------------------------------------
                   1960:  *
                   1961:  * PostSubmenu --
                   1962:  *
                   1963:  *     This procedure arranges for a particular submenu (i.e. the
                   1964:  *     menu corresponding to a given cascade entry) to be
                   1965:  *     posted.
                   1966:  *
                   1967:  * Results:
                   1968:  *     A standard Tcl return result.  Errors may occur in the
                   1969:  *     Tcl commands generated to post and unpost submenus.
                   1970:  *
                   1971:  * Side effects:
                   1972:  *     If there is already a submenu posted, it is unposted.
                   1973:  *     The new submenu is then posted.
                   1974:  *
                   1975:  *--------------------------------------------------------------
                   1976:  */
                   1977: 
                   1978: static int
                   1979: PostSubmenu(interp, menuPtr, mePtr)
                   1980:     Tcl_Interp *interp;                /* Used for invoking sub-commands and
                   1981:                                 * reporting errors. */
                   1982:     register Menu *menuPtr;    /* Information about menu as a whole. */
                   1983:     register MenuEntry *mePtr; /* Info about submenu that is to be
                   1984:                                 * posted.  NULL means make sure that
                   1985:                                 * no submenu is posted. */
                   1986: {
                   1987:     char string[30];
                   1988:     int result, x, y;
                   1989: 
                   1990:     if (mePtr == menuPtr->postedCascade) {
                   1991:        return TCL_OK;
                   1992:     }
                   1993: 
                   1994:     if (menuPtr->postedCascade != NULL) {
                   1995:        result = Tcl_VarEval(interp, menuPtr->postedCascade->name,
                   1996:                " unpost", (char *) NULL);
                   1997:        menuPtr->postedCascade = NULL;
                   1998:        if (result != TCL_OK) {
                   1999:            return result;
                   2000:        }
                   2001:     }
                   2002: 
                   2003:     if ((mePtr != NULL) && (mePtr->name != NULL)) {
                   2004:        Tk_GetRootCoords(menuPtr->tkwin, &x, &y);
                   2005:        x += Tk_Width(menuPtr->tkwin);
                   2006:        y += mePtr->y;
                   2007:        sprintf(string, "%d %d ", x, y);
                   2008:        result = Tcl_VarEval(interp, mePtr->name, " post ", string,
                   2009:                menuPtr->group, (char *) NULL);
                   2010:        if (result != TCL_OK) {
                   2011:            return result;
                   2012:        }
                   2013:        menuPtr->postedCascade = mePtr;
                   2014:     }
                   2015:     return TCL_OK;
                   2016: }
                   2017: 
                   2018: /*
                   2019:  *----------------------------------------------------------------------
                   2020:  *
                   2021:  * ActivateMenuEntry --
                   2022:  *
                   2023:  *     This procedure is invoked to make a particular menu entry
                   2024:  *     the active one, deactivating any other entry that might
                   2025:  *     currently be active.
                   2026:  *
                   2027:  * Results:
                   2028:  *     The return value is a standard Tcl result (errors can occur
                   2029:  *     while posting and unposting submenus).
                   2030:  *
                   2031:  * Side effects:
                   2032:  *     Menu entries get redisplayed, and the active entry changes.
                   2033:  *     Submenus may get posted and unposted.
                   2034:  *
                   2035:  *----------------------------------------------------------------------
                   2036:  */
                   2037: 
                   2038: static int
                   2039: ActivateMenuEntry(menuPtr, index)
                   2040:     register Menu *menuPtr;            /* Menu in which to activate. */
                   2041:     int index;                         /* Index of entry to activate, or
                   2042:                                         * -1 to deactivate all entries. */
                   2043: {
                   2044:     register MenuEntry *mePtr;
                   2045:     int result = TCL_OK;
                   2046: 
                   2047:     if (menuPtr->active >= 0) {
                   2048:        mePtr = menuPtr->entries[menuPtr->active];
                   2049: 
                   2050:        /*
                   2051:         * Don't change the state unless it's currently active (state
                   2052:         * might already have been changed to disabled).
                   2053:         */
                   2054: 
                   2055:        if (mePtr->state == tkActiveUid) {
                   2056:            mePtr->state = tkNormalUid;
                   2057:        }
                   2058:        EventuallyRedrawMenu(menuPtr, menuPtr->active);
                   2059:     }
                   2060:     menuPtr->active = index;
                   2061:     if (index >= 0) {
                   2062:        mePtr = menuPtr->entries[index];
                   2063:        mePtr->state = tkActiveUid;
                   2064:        EventuallyRedrawMenu(menuPtr, index);
                   2065:        Tk_Preserve((ClientData) mePtr);
                   2066:        if (mePtr->type == CASCADE_ENTRY) {
                   2067:            result = PostSubmenu(menuPtr->interp, menuPtr, mePtr);
                   2068:        } else {
                   2069:            result = PostSubmenu(menuPtr->interp, menuPtr, (MenuEntry *) NULL);
                   2070:        }
                   2071:        Tk_Release((ClientData) mePtr);
                   2072:     }
                   2073:     return result;
                   2074: }

unix.superglobalmegacorp.com

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