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