/[dtapublic]/projs/emts/trunk/src/c_tk_base_7_5_w_mods/tkmenu.c
ViewVC logotype

Diff of /projs/emts/trunk/src/c_tk_base_7_5_w_mods/tkmenu.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

projs/trunk/shared_source/tk_base/tkmenu.c revision 42 by dashley, Fri Oct 14 01:50:00 2016 UTC projs/emts/trunk/src/c_tk_base_7_5_w_mods/tkmenu.c revision 269 by dashley, Sat Jun 1 21:29:58 2019 UTC
# Line 1  Line 1 
 /* $Header: /cvsroot/esrg/sfesrg/esrgpcpj/shared/tk_base/tkmenu.c,v 1.1.1.1 2001/06/13 05:05:37 dtashley Exp $ */  
   
 /*  
  * tkMenu.c --  
  *  
  * This file contains most of the code for implementing menus in Tk. It takes  
  * care of all of the generic (platform-independent) parts of menus, and  
  * is supplemented by platform-specific files. The geometry calculation  
  * and drawing code for menus is in the file tkMenuDraw.c  
  *  
  * Copyright (c) 1990-1994 The Regents of the University of California.  
  * Copyright (c) 1994-1998 Sun Microsystems, Inc.  
  *  
  * See the file "license.terms" for information on usage and redistribution  
  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.  
  *  
  * RCS: @(#) $Id: tkmenu.c,v 1.1.1.1 2001/06/13 05:05:37 dtashley Exp $  
  */  
   
 /*  
  * Notes on implementation of menus:  
  *  
  * Menus can be used in three ways:  
  * - as a popup menu, either as part of a menubutton or standalone.  
  * - as a menubar. The menu's cascade items are arranged according to  
  * the specific platform to provide the user access to the menus at all  
  * times  
  * - as a tearoff palette. This is a window with the menu's items in it.  
  *  
  * The goal is to provide the Tk developer with a way to use a common  
  * set of menus for all of these tasks.  
  *  
  * In order to make the bindings for cascade menus work properly under Unix,  
  * the cascade menus' pathnames must be proper children of the menu that  
  * they are cascade from. So if there is a menu .m, and it has two  
  * cascades labelled "File" and "Edit", the cascade menus might have  
  * the pathnames .m.file and .m.edit. Another constraint is that the menus  
  * used for menubars must be children of the toplevel widget that they  
  * are attached to. And on the Macintosh, the platform specific menu handle  
  * for cascades attached to a menu bar must have a title that matches the  
  * label for the cascade menu.  
  *  
  * To handle all of the constraints, Tk menubars and tearoff menus are  
  * implemented using menu clones. Menu clones are full menus in their own  
  * right; they have a Tk window and pathname associated with them; they have  
  * a TkMenu structure and array of entries. However, they are linked with the  
  * original menu that they were cloned from. The reflect the attributes of  
  * the original, or "master", menu. So if an item is added to a menu, and  
  * that menu has clones, then the item must be added to all of its clones  
  * also. Menus are cloned when a menu is torn-off or when a menu is assigned  
  * as a menubar using the "-menu" option of the toplevel's pathname configure  
  * subcommand. When a clone is destroyed, only the clone is destroyed, but  
  * when the master menu is destroyed, all clones are also destroyed. This  
  * allows the developer to just deal with one set of menus when creating  
  * and destroying.  
  *  
  * Clones are rather tricky when a menu with cascade entries is cloned (such  
  * as a menubar). Not only does the menu have to be cloned, but each cascade  
  * entry's corresponding menu must also be cloned. This maintains the pathname  
  * parent-child hierarchy necessary for menubars and toplevels to work.  
  * This leads to several special cases:  
  *  
  * 1. When a new menu is created, and it is pointed to by cascade entries in  
  * cloned menus, the new menu has to be cloned to parallel the cascade  
  * structure.  
  * 2. When a cascade item is added to a menu that has been cloned, and the  
  * menu that the cascade item points to exists, that menu has to be cloned.  
  * 3. When the menu that a cascade entry points to is changed, the old  
  * cloned cascade menu has to be discarded, and the new one has to be cloned.  
  *  
  */  
   
 #if 0  
   
 /*  
  * used only to test for old config code  
  */  
   
 #define __NO_OLD_CONFIG  
 #endif  
   
 #include "tkPort.h"  
 #include "tkMenu.h"  
   
 #define MENU_HASH_KEY "tkMenus"  
   
 typedef struct ThreadSpecificData {  
     int menusInitialized;       /* Flag indicates whether thread-specific  
                                  * elements of the Windows Menu module  
                                  * have been initialized. */  
 } ThreadSpecificData;  
 static Tcl_ThreadDataKey dataKey;  
   
 /*  
  * The following flag indicates whether the process-wide state for  
  * the Menu module has been intialized.  The Mutex protects access to  
  * that flag.  
  */  
   
 static int menusInitialized;  
 TCL_DECLARE_MUTEX(menuMutex)  
   
 /*  
  * Configuration specs for individual menu entries. If this changes, be sure  
  * to update code in TkpMenuInit that changes the font string entry.  
  */  
   
 char *tkMenuStateStrings[] = {"active", "normal", "disabled", (char *) NULL};  
   
 static char *menuEntryTypeStrings[] = {"cascade", "checkbutton", "command",  
         "radiobutton", "separator", (char *) NULL};  
   
 Tk_OptionSpec tkBasicMenuEntryConfigSpecs[] = {  
     {TK_OPTION_BORDER, "-activebackground", (char *) NULL, (char *) NULL,  
         DEF_MENU_ENTRY_ACTIVE_BG, Tk_Offset(TkMenuEntry, activeBorderPtr), -1,  
         TK_OPTION_NULL_OK},  
     {TK_OPTION_COLOR, "-activeforeground", (char *) NULL, (char *) NULL,  
         DEF_MENU_ENTRY_ACTIVE_FG,  
         Tk_Offset(TkMenuEntry, activeFgPtr), -1, TK_OPTION_NULL_OK},  
     {TK_OPTION_STRING, "-accelerator", (char *) NULL, (char *) NULL,  
         DEF_MENU_ENTRY_ACCELERATOR,  
         Tk_Offset(TkMenuEntry, accelPtr), -1, TK_OPTION_NULL_OK},  
     {TK_OPTION_BORDER, "-background", (char *) NULL, (char *) NULL,  
         DEF_MENU_ENTRY_BG,  
         Tk_Offset(TkMenuEntry, borderPtr), -1, TK_OPTION_NULL_OK},  
     {TK_OPTION_BITMAP, "-bitmap", (char *) NULL, (char *) NULL,  
         DEF_MENU_ENTRY_BITMAP,  
         Tk_Offset(TkMenuEntry, bitmapPtr), -1, TK_OPTION_NULL_OK},  
     {TK_OPTION_BOOLEAN, "-columnbreak", (char *) NULL, (char *) NULL,  
         DEF_MENU_ENTRY_COLUMN_BREAK,  
         -1, Tk_Offset(TkMenuEntry, columnBreak)},  
     {TK_OPTION_STRING, "-command", (char *) NULL, (char *) NULL,  
         DEF_MENU_ENTRY_COMMAND,  
         Tk_Offset(TkMenuEntry, commandPtr), -1, TK_OPTION_NULL_OK},  
     {TK_OPTION_FONT, "-font", (char *) NULL, (char *) NULL,  
         DEF_MENU_ENTRY_FONT,  
         Tk_Offset(TkMenuEntry, fontPtr), -1, TK_OPTION_NULL_OK},  
     {TK_OPTION_COLOR, "-foreground", (char *) NULL, (char *) NULL,  
         DEF_MENU_ENTRY_FG,  
         Tk_Offset(TkMenuEntry, fgPtr), -1, TK_OPTION_NULL_OK},  
     {TK_OPTION_BOOLEAN, "-hidemargin", (char *) NULL, (char *) NULL,  
         DEF_MENU_ENTRY_HIDE_MARGIN,  
         -1, Tk_Offset(TkMenuEntry, hideMargin)},  
     {TK_OPTION_STRING, "-image", (char *) NULL, (char *) NULL,  
         DEF_MENU_ENTRY_IMAGE,  
         Tk_Offset(TkMenuEntry, imagePtr), -1, TK_OPTION_NULL_OK},  
     {TK_OPTION_STRING, "-label", (char *) NULL, (char *) NULL,  
         DEF_MENU_ENTRY_LABEL,  
         Tk_Offset(TkMenuEntry, labelPtr), -1, 0},  
     {TK_OPTION_STRING_TABLE, "-state", (char *) NULL, (char *) NULL,  
         DEF_MENU_ENTRY_STATE,  
         -1, Tk_Offset(TkMenuEntry, state), 0,  
         (ClientData) tkMenuStateStrings},  
     {TK_OPTION_INT, "-underline", (char *) NULL, (char *) NULL,  
         DEF_MENU_ENTRY_UNDERLINE, -1, Tk_Offset(TkMenuEntry, underline)},  
     {TK_OPTION_END}  
 };  
   
 Tk_OptionSpec tkSeparatorEntryConfigSpecs[] = {  
     {TK_OPTION_BORDER, "-background", (char *) NULL, (char *) NULL,  
         DEF_MENU_ENTRY_BG,  
         Tk_Offset(TkMenuEntry, borderPtr), -1, TK_OPTION_NULL_OK},  
     {TK_OPTION_END}  
 };  
   
 Tk_OptionSpec tkCheckButtonEntryConfigSpecs[] = {  
     {TK_OPTION_BOOLEAN, "-indicatoron", (char *) NULL, (char *) NULL,  
         DEF_MENU_ENTRY_INDICATOR,  
         -1, Tk_Offset(TkMenuEntry, indicatorOn)},  
     {TK_OPTION_STRING, "-offvalue", (char *) NULL, (char *) NULL,  
         DEF_MENU_ENTRY_OFF_VALUE,  
         Tk_Offset(TkMenuEntry, offValuePtr), -1},  
     {TK_OPTION_STRING, "-onvalue", (char *) NULL, (char *) NULL,  
         DEF_MENU_ENTRY_ON_VALUE,  
         Tk_Offset(TkMenuEntry, onValuePtr), -1},  
     {TK_OPTION_COLOR, "-selectcolor", (char *) NULL, (char *) NULL,  
         DEF_MENU_ENTRY_SELECT,  
         Tk_Offset(TkMenuEntry, indicatorFgPtr), -1, TK_OPTION_NULL_OK},  
     {TK_OPTION_STRING, "-selectimage", (char *) NULL, (char *) NULL,  
         DEF_MENU_ENTRY_SELECT_IMAGE,  
         Tk_Offset(TkMenuEntry, selectImagePtr), -1, TK_OPTION_NULL_OK},  
     {TK_OPTION_STRING, "-variable", (char *) NULL, (char *) NULL,  
         DEF_MENU_ENTRY_CHECK_VARIABLE,  
         Tk_Offset(TkMenuEntry, namePtr), -1, TK_OPTION_NULL_OK},  
     {TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL,  
         (char *) NULL, 0, -1, 0, (ClientData) tkBasicMenuEntryConfigSpecs}  
 };  
   
 Tk_OptionSpec tkRadioButtonEntryConfigSpecs[] = {  
     {TK_OPTION_BOOLEAN, "-indicatoron", (char *) NULL, (char *) NULL,  
         DEF_MENU_ENTRY_INDICATOR,  
         -1, Tk_Offset(TkMenuEntry, indicatorOn)},  
     {TK_OPTION_COLOR, "-selectcolor", (char *) NULL, (char *) NULL,  
         DEF_MENU_ENTRY_SELECT,  
         Tk_Offset(TkMenuEntry, indicatorFgPtr), -1, TK_OPTION_NULL_OK},  
     {TK_OPTION_STRING, "-selectimage", (char *) NULL, (char *) NULL,  
         DEF_MENU_ENTRY_SELECT_IMAGE,  
         Tk_Offset(TkMenuEntry, selectImagePtr), -1, TK_OPTION_NULL_OK},  
     {TK_OPTION_STRING, "-value", (char *) NULL, (char *) NULL,  
         DEF_MENU_ENTRY_VALUE,  
         Tk_Offset(TkMenuEntry, onValuePtr), -1, TK_OPTION_NULL_OK},  
     {TK_OPTION_STRING, "-variable", (char *) NULL, (char *) NULL,  
         DEF_MENU_ENTRY_RADIO_VARIABLE,  
         Tk_Offset(TkMenuEntry, namePtr), -1, 0},  
     {TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL,  
         (char *) NULL, 0, -1, 0, (ClientData) tkBasicMenuEntryConfigSpecs}  
 };  
   
 Tk_OptionSpec tkCascadeEntryConfigSpecs[] = {  
     {TK_OPTION_STRING, "-menu", (char *) NULL, (char *) NULL,  
         DEF_MENU_ENTRY_MENU,  
         Tk_Offset(TkMenuEntry, namePtr), -1, TK_OPTION_NULL_OK},  
     {TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL,  
         (char *) NULL, 0, -1, 0, (ClientData) tkBasicMenuEntryConfigSpecs}  
 };  
   
 Tk_OptionSpec tkTearoffEntryConfigSpecs[] = {  
     {TK_OPTION_BORDER, "-background", (char *) NULL, (char *) NULL,  
         DEF_MENU_ENTRY_BG,  
         Tk_Offset(TkMenuEntry, borderPtr), -1, TK_OPTION_NULL_OK},  
     {TK_OPTION_STRING_TABLE, "-state", (char *) NULL, (char *) NULL,  
         DEF_MENU_ENTRY_STATE, -1, Tk_Offset(TkMenuEntry, state), 0,  
         (ClientData) tkMenuStateStrings},  
     {TK_OPTION_END}  
 };  
   
 static Tk_OptionSpec *specsArray[] = {  
     tkCascadeEntryConfigSpecs, tkCheckButtonEntryConfigSpecs,  
     tkBasicMenuEntryConfigSpecs, tkRadioButtonEntryConfigSpecs,  
     tkSeparatorEntryConfigSpecs, tkTearoffEntryConfigSpecs};  
       
 /*  
  * Menu type strings for use with Tcl_GetIndexFromObj.  
  */  
   
 static char *menuTypeStrings[] = {"normal", "tearoff", "menubar",  
         (char *) NULL};  
   
 Tk_OptionSpec tkMenuConfigSpecs[] = {  
     {TK_OPTION_BORDER, "-activebackground", "activeBackground",  
         "Foreground", DEF_MENU_ACTIVE_BG_COLOR,  
         Tk_Offset(TkMenu, activeBorderPtr), -1, 0,  
         (ClientData) DEF_MENU_ACTIVE_BG_MONO},  
     {TK_OPTION_PIXELS, "-activeborderwidth", "activeBorderWidth",  
         "BorderWidth", DEF_MENU_ACTIVE_BORDER_WIDTH,  
         Tk_Offset(TkMenu, activeBorderWidthPtr), -1},  
     {TK_OPTION_COLOR, "-activeforeground", "activeForeground",  
         "Background", DEF_MENU_ACTIVE_FG_COLOR,  
         Tk_Offset(TkMenu, activeFgPtr), -1, 0,  
         (ClientData) DEF_MENU_ACTIVE_FG_MONO},  
     {TK_OPTION_BORDER, "-background", "background", "Background",  
         DEF_MENU_BG_COLOR, Tk_Offset(TkMenu, borderPtr), -1, 0,  
         (ClientData) DEF_MENU_BG_MONO},  
     {TK_OPTION_SYNONYM, "-bd", (char *) NULL, (char *) NULL,  
         (char *) NULL, 0, -1, 0, (ClientData) "-borderwidth"},  
     {TK_OPTION_SYNONYM, "-bg", (char *) NULL, (char *) NULL,  
         (char *) NULL, 0, -1, 0, (ClientData) "-background"},  
     {TK_OPTION_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",  
         DEF_MENU_BORDER_WIDTH,  
         Tk_Offset(TkMenu, borderWidthPtr), -1, 0},  
     {TK_OPTION_CURSOR, "-cursor", "cursor", "Cursor",  
         DEF_MENU_CURSOR,  
         Tk_Offset(TkMenu, cursorPtr), -1, TK_OPTION_NULL_OK},  
     {TK_OPTION_COLOR, "-disabledforeground", "disabledForeground",  
         "DisabledForeground", DEF_MENU_DISABLED_FG_COLOR,  
         Tk_Offset(TkMenu, disabledFgPtr), -1, TK_OPTION_NULL_OK,  
         (ClientData) DEF_MENU_DISABLED_FG_MONO},  
     {TK_OPTION_SYNONYM, "-fg", (char *) NULL, (char *) NULL,  
         (char *) NULL, 0, -1, 0, (ClientData) "-foreground"},  
     {TK_OPTION_FONT, "-font", "font", "Font",  
         DEF_MENU_FONT, Tk_Offset(TkMenu, fontPtr), -1},  
     {TK_OPTION_COLOR, "-foreground", "foreground", "Foreground",  
         DEF_MENU_FG, Tk_Offset(TkMenu, fgPtr), -1},  
     {TK_OPTION_STRING, "-postcommand", "postCommand", "Command",  
         DEF_MENU_POST_COMMAND,  
         Tk_Offset(TkMenu, postCommandPtr), -1, TK_OPTION_NULL_OK},  
     {TK_OPTION_RELIEF, "-relief", "relief", "Relief",  
         DEF_MENU_RELIEF, Tk_Offset(TkMenu, reliefPtr), -1},  
     {TK_OPTION_COLOR, "-selectcolor", "selectColor", "Background",  
         DEF_MENU_SELECT_COLOR, Tk_Offset(TkMenu, indicatorFgPtr), -1, 0,  
         (ClientData) DEF_MENU_SELECT_MONO},  
     {TK_OPTION_STRING, "-takefocus", "takeFocus", "TakeFocus",  
         DEF_MENU_TAKE_FOCUS,  
         Tk_Offset(TkMenu, takeFocusPtr), -1, TK_OPTION_NULL_OK},  
     {TK_OPTION_BOOLEAN, "-tearoff", "tearOff", "TearOff",  
         DEF_MENU_TEAROFF, -1, Tk_Offset(TkMenu, tearoff)},  
     {TK_OPTION_STRING, "-tearoffcommand", "tearOffCommand",  
         "TearOffCommand", DEF_MENU_TEAROFF_CMD,  
         Tk_Offset(TkMenu, tearoffCommandPtr), -1, TK_OPTION_NULL_OK},  
     {TK_OPTION_STRING, "-title", "title", "Title",  
         DEF_MENU_TITLE,  Tk_Offset(TkMenu, titlePtr), -1,  
         TK_OPTION_NULL_OK},  
     {TK_OPTION_STRING_TABLE, "-type", "type", "Type",  
         DEF_MENU_TYPE, Tk_Offset(TkMenu, menuTypePtr), -1, TK_OPTION_NULL_OK,  
         (ClientData) menuTypeStrings},  
     {TK_OPTION_END}  
 };  
   
 /*  
  * Command line options. Put here because MenuCmd has to look at them  
  * along with MenuWidgetObjCmd.  
  */  
   
 static char *menuOptions[] = {  
     "activate", "add", "cget", "clone", "configure", "delete", "entrycget",  
     "entryconfigure", "index", "insert", "invoke", "post", "postcascade",  
     "type", "unpost", "yposition", (char *) NULL  
 };  
 enum options {  
     MENU_ACTIVATE, MENU_ADD, MENU_CGET, MENU_CLONE, MENU_CONFIGURE,  
     MENU_DELETE, MENU_ENTRYCGET, MENU_ENTRYCONFIGURE, MENU_INDEX,  
     MENU_INSERT, MENU_INVOKE, MENU_POST, MENU_POSTCASCADE, MENU_TYPE,  
     MENU_UNPOST, MENU_YPOSITION  
 };  
   
 /*  
  * Prototypes for static procedures in this file:  
  */  
   
 static int              CloneMenu _ANSI_ARGS_((TkMenu *menuPtr,  
                             Tcl_Obj *newMenuName, Tcl_Obj *newMenuTypeString));  
 static int              ConfigureMenu _ANSI_ARGS_((Tcl_Interp *interp,  
                             TkMenu *menuPtr, int objc, Tcl_Obj *CONST objv[]));  
 static int              ConfigureMenuCloneEntries _ANSI_ARGS_((  
                             Tcl_Interp *interp, TkMenu *menuPtr, int index,  
                             int objc, Tcl_Obj *CONST objv[]));  
 static int              ConfigureMenuEntry _ANSI_ARGS_((TkMenuEntry *mePtr,  
                             int objc, Tcl_Obj *CONST objv[]));  
 static void             DeleteMenuCloneEntries _ANSI_ARGS_((TkMenu *menuPtr,  
                             int first, int last));  
 static void             DestroyMenuHashTable _ANSI_ARGS_((  
                             ClientData clientData, Tcl_Interp *interp));  
 static void             DestroyMenuInstance _ANSI_ARGS_((TkMenu *menuPtr));  
 static void             DestroyMenuEntry _ANSI_ARGS_((char *memPtr));  
 static int              GetIndexFromCoords  
                             _ANSI_ARGS_((Tcl_Interp *interp, TkMenu *menuPtr,  
                             char *string, int *indexPtr));  
 static int              MenuDoYPosition _ANSI_ARGS_((Tcl_Interp *interp,  
                             TkMenu *menuPtr, Tcl_Obj *objPtr));  
 static int              MenuAddOrInsert _ANSI_ARGS_((Tcl_Interp *interp,  
                             TkMenu *menuPtr, Tcl_Obj *indexPtr, int objc,  
                             Tcl_Obj *CONST objv[]));  
 static int              MenuCmd _ANSI_ARGS_((ClientData clientData,  
                             Tcl_Interp *interp, int objc,  
                             Tcl_Obj *CONST objv[]));  
 static void             MenuCmdDeletedProc _ANSI_ARGS_((  
                             ClientData clientData));  
 static TkMenuEntry *    MenuNewEntry _ANSI_ARGS_((TkMenu *menuPtr, int index,  
                             int type));  
 static char *           MenuVarProc _ANSI_ARGS_((ClientData clientData,  
                             Tcl_Interp *interp, char *name1, char *name2,  
                             int flags));  
 static int              MenuWidgetObjCmd _ANSI_ARGS_((ClientData clientData,  
                             Tcl_Interp *interp, int objc,  
                             Tcl_Obj *CONST objv[]));  
 static void             MenuWorldChanged _ANSI_ARGS_((  
                             ClientData instanceData));  
 static int              PostProcessEntry _ANSI_ARGS_((TkMenuEntry *mePtr));  
 static void             RecursivelyDeleteMenu _ANSI_ARGS_((TkMenu *menuPtr));  
 static void             UnhookCascadeEntry _ANSI_ARGS_((TkMenuEntry *mePtr));  
   
 /*  
  * The structure below is a list of procs that respond to certain window  
  * manager events. One of these includes a font change, which forces  
  * the geometry proc to be called.  
  */  
   
 static TkClassProcs menuClass = {  
     NULL,                       /* createProc. */  
     MenuWorldChanged            /* geometryProc. */  
 };  
   
 /*  
  *--------------------------------------------------------------  
  *  
  * Tk_CreateMenuCmd --  
  *  
  *      Called by Tk at initialization time to create the menu  
  *      command.  
  *  
  * Results:  
  *      A standard Tcl result.  
  *  
  * Side effects:  
  *      See the user documentation.  
  *  
  *--------------------------------------------------------------  
  */  
   
 int  
 TkCreateMenuCmd(interp)  
     Tcl_Interp *interp;         /* Interpreter we are creating the  
                                  * command in. */  
 {  
     TkMenuOptionTables *optionTablesPtr =  
             (TkMenuOptionTables *) ckalloc(sizeof(TkMenuOptionTables));  
   
     optionTablesPtr->menuOptionTable =  
             Tk_CreateOptionTable(interp, tkMenuConfigSpecs);  
     optionTablesPtr->entryOptionTables[TEAROFF_ENTRY] =  
             Tk_CreateOptionTable(interp, specsArray[TEAROFF_ENTRY]);  
     optionTablesPtr->entryOptionTables[COMMAND_ENTRY] =  
             Tk_CreateOptionTable(interp, specsArray[COMMAND_ENTRY]);  
     optionTablesPtr->entryOptionTables[CASCADE_ENTRY] =  
             Tk_CreateOptionTable(interp, specsArray[CASCADE_ENTRY]);  
     optionTablesPtr->entryOptionTables[SEPARATOR_ENTRY] =  
             Tk_CreateOptionTable(interp, specsArray[SEPARATOR_ENTRY]);  
     optionTablesPtr->entryOptionTables[RADIO_BUTTON_ENTRY] =  
             Tk_CreateOptionTable(interp, specsArray[RADIO_BUTTON_ENTRY]);  
     optionTablesPtr->entryOptionTables[CHECK_BUTTON_ENTRY] =  
             Tk_CreateOptionTable(interp, specsArray[CHECK_BUTTON_ENTRY]);  
   
     Tcl_CreateObjCommand(interp, "menu", MenuCmd,  
             (ClientData) optionTablesPtr, NULL);  
   
     if (Tcl_IsSafe(interp)) {  
         Tcl_HideCommand(interp, "menu", "menu");  
     }  
   
     return TCL_OK;  
 }  
   
 /*  
  *--------------------------------------------------------------  
  *  
  * MenuCmd --  
  *  
  *      This procedure is invoked to process the "menu" Tcl  
  *      command.  See the user documentation for details on  
  *      what it does.  
  *  
  * Results:  
  *      A standard Tcl result.  
  *  
  * Side effects:  
  *      See the user documentation.  
  *  
  *--------------------------------------------------------------  
  */  
   
 static int  
 MenuCmd(clientData, interp, objc, objv)  
     ClientData clientData;      /* Main window associated with  
                                  * interpreter. */  
     Tcl_Interp *interp;         /* Current interpreter. */  
     int objc;                   /* Number of arguments. */  
     Tcl_Obj *CONST objv[];      /* Argument strings. */  
 {  
     Tk_Window tkwin = Tk_MainWindow(interp);  
     Tk_Window new;  
     register TkMenu *menuPtr;  
     TkMenuReferences *menuRefPtr;  
     int i, index;  
     int toplevel;  
     char *windowName;  
     static char *typeStringList[] = {"-type", (char *) NULL};  
     TkMenuOptionTables *optionTablesPtr = (TkMenuOptionTables *) clientData;  
   
     if (objc < 2) {  
         Tcl_WrongNumArgs(interp, 1, objv, "pathName ?options?");  
         return TCL_ERROR;  
     }  
   
     TkMenuInit();  
   
     toplevel = 1;  
     for (i = 2; i < (objc - 1); i++) {  
         if (Tcl_GetIndexFromObj(NULL, objv[i], typeStringList, NULL, 0, &index)  
                 != TCL_ERROR) {  
             if ((Tcl_GetIndexFromObj(NULL, objv[i + 1], menuTypeStrings, NULL,  
                     0, &index) == TCL_OK) && (index == MENUBAR)) {  
                 toplevel = 0;  
             }  
             break;  
         }  
     }  
   
     windowName = Tcl_GetStringFromObj(objv[1], NULL);  
     new = Tk_CreateWindowFromPath(interp, tkwin, windowName, toplevel ? ""  
             : NULL);  
     if (new == NULL) {  
         return TCL_ERROR;  
     }  
   
     /*  
      * Initialize the data structure for the menu.  
      */  
   
     menuPtr = (TkMenu *) ckalloc(sizeof(TkMenu));  
     menuPtr->tkwin = new;  
     menuPtr->display = Tk_Display(new);  
     menuPtr->interp = interp;  
     menuPtr->widgetCmd = Tcl_CreateObjCommand(interp,  
             Tk_PathName(menuPtr->tkwin), MenuWidgetObjCmd,  
             (ClientData) menuPtr, MenuCmdDeletedProc);  
     menuPtr->entries = NULL;  
     menuPtr->numEntries = 0;  
     menuPtr->active = -1;  
     menuPtr->borderPtr = NULL;  
     menuPtr->borderWidthPtr = NULL;  
     menuPtr->reliefPtr = NULL;  
     menuPtr->activeBorderPtr = NULL;  
     menuPtr->activeBorderWidthPtr = NULL;  
     menuPtr->fontPtr = NULL;  
     menuPtr->fgPtr = NULL;  
     menuPtr->disabledFgPtr = NULL;  
     menuPtr->activeFgPtr = NULL;  
     menuPtr->indicatorFgPtr = NULL;  
     menuPtr->tearoff = 0;  
     menuPtr->tearoffCommandPtr = NULL;  
     menuPtr->cursorPtr = None;  
     menuPtr->takeFocusPtr = NULL;  
     menuPtr->postCommandPtr = NULL;  
     menuPtr->postCommandGeneration = 0;  
     menuPtr->postedCascade = NULL;  
     menuPtr->nextInstancePtr = NULL;  
     menuPtr->masterMenuPtr = menuPtr;  
     menuPtr->menuType = UNKNOWN_TYPE;  
     menuPtr->menuFlags = 0;  
     menuPtr->parentTopLevelPtr = NULL;  
     menuPtr->menuTypePtr = NULL;  
     menuPtr->titlePtr = NULL;  
     menuPtr->errorStructPtr = NULL;  
     menuPtr->optionTablesPtr = optionTablesPtr;  
     TkMenuInitializeDrawingFields(menuPtr);  
   
     Tk_SetClass(menuPtr->tkwin, "Menu");  
     TkSetClassProcs(menuPtr->tkwin, &menuClass, (ClientData) menuPtr);  
     if (Tk_InitOptions(interp, (char *) menuPtr,  
             menuPtr->optionTablesPtr->menuOptionTable, menuPtr->tkwin)  
             != TCL_OK) {  
         Tk_DestroyWindow(menuPtr->tkwin);  
         ckfree((char *) menuPtr);  
         return TCL_ERROR;  
     }  
   
   
     menuRefPtr = TkCreateMenuReferences(menuPtr->interp,  
             Tk_PathName(menuPtr->tkwin));  
     menuRefPtr->menuPtr = menuPtr;  
     menuPtr->menuRefPtr = menuRefPtr;  
     if (TCL_OK != TkpNewMenu(menuPtr)) {  
         Tk_DestroyWindow(menuPtr->tkwin);  
         ckfree((char *) menuPtr);  
         return TCL_ERROR;  
     }  
   
     Tk_CreateEventHandler(new, ExposureMask|StructureNotifyMask|ActivateMask,  
             TkMenuEventProc, (ClientData) menuPtr);  
     if (ConfigureMenu(interp, menuPtr, objc - 2, objv + 2) != TCL_OK) {  
         Tk_DestroyWindow(menuPtr->tkwin);  
         return TCL_ERROR;  
     }  
   
     /*  
      * If a menu has a parent menu pointing to it as a cascade entry, the  
      * parent menu needs to be told that this menu now exists so that  
      * the platform-part of the menu is correctly updated.  
      *  
      * If a menu has an instance and has cascade entries, then each cascade  
      * menu must also have a parallel instance. This is especially true on  
      * the Mac, where each menu has to have a separate title everytime it is in  
      * a menubar. For instance, say you have a menu .m1 with a cascade entry  
      * for .m2, where .m2 does not exist yet. You then put .m1 into a menubar.  
      * This creates a menubar instance for .m1, but since .m2 is not there,  
      * nothing else happens. When we go to create .m2, we hook it up properly  
      * with .m1. However, we now need to clone .m2 and assign the clone of .m2  
      * to be the cascade entry for the clone of .m1. This is special case  
      * #1 listed in the introductory comment.  
      */  
       
     if (menuRefPtr->parentEntryPtr != NULL) {  
         TkMenuEntry *cascadeListPtr = menuRefPtr->parentEntryPtr;  
         TkMenuEntry *nextCascadePtr;  
         Tcl_Obj *newMenuName;  
         Tcl_Obj *newObjv[2];  
   
         while (cascadeListPtr != NULL) {  
   
             nextCascadePtr = cascadeListPtr->nextCascadePtr;  
       
             /*  
              * If we have a new master menu, and an existing cloned menu  
              * points to this menu in a cascade entry, we have to clone  
              * the new menu and point the entry to the clone instead  
              * of the menu we are creating. Otherwise, ConfigureMenuEntry  
              * will hook up the platform-specific cascade linkages now  
              * that the menu we are creating exists.  
              */  
               
             if ((menuPtr->masterMenuPtr != menuPtr)  
                     || ((menuPtr->masterMenuPtr == menuPtr)  
                     && ((cascadeListPtr->menuPtr->masterMenuPtr  
                     == cascadeListPtr->menuPtr)))) {  
                 newObjv[0] = Tcl_NewStringObj("-menu", -1);  
                 newObjv[1] = Tcl_NewStringObj(Tk_PathName(menuPtr->tkwin), -1);  
                 Tcl_IncrRefCount(newObjv[0]);  
                 Tcl_IncrRefCount(newObjv[1]);  
                 ConfigureMenuEntry(cascadeListPtr, 2, newObjv);  
                 Tcl_DecrRefCount(newObjv[0]);  
                 Tcl_DecrRefCount(newObjv[1]);  
             } else {  
                 Tcl_Obj *normalPtr = Tcl_NewStringObj("normal", -1);  
                 Tcl_Obj *windowNamePtr = Tcl_NewStringObj(  
                         Tk_PathName(cascadeListPtr->menuPtr->tkwin), -1);  
   
                 Tcl_IncrRefCount(normalPtr);  
                 Tcl_IncrRefCount(windowNamePtr);  
                 newMenuName = TkNewMenuName(menuPtr->interp,  
                         windowNamePtr, menuPtr);  
                 Tcl_IncrRefCount(newMenuName);  
                 CloneMenu(menuPtr, newMenuName, normalPtr);  
                       
                 /*  
                  * Now we can set the new menu instance to be the cascade entry  
                  * of the parent's instance.  
                  */  
   
                 newObjv[0] = Tcl_NewStringObj("-menu", -1);  
                 newObjv[1] = newMenuName;  
                 Tcl_IncrRefCount(newObjv[0]);  
                 ConfigureMenuEntry(cascadeListPtr, 2, newObjv);  
                 Tcl_DecrRefCount(normalPtr);  
                 Tcl_DecrRefCount(newObjv[0]);  
                 Tcl_DecrRefCount(newObjv[1]);  
                 Tcl_DecrRefCount(windowNamePtr);  
             }  
             cascadeListPtr = nextCascadePtr;  
         }  
     }  
       
     /*  
      * If there already exist toplevel widgets that refer to this menu,  
      * find them and notify them so that they can reconfigure their  
      * geometry to reflect the menu.  
      */  
   
     if (menuRefPtr->topLevelListPtr != NULL) {  
         TkMenuTopLevelList *topLevelListPtr = menuRefPtr->topLevelListPtr;  
         TkMenuTopLevelList *nextPtr;  
         Tk_Window listtkwin;  
         while (topLevelListPtr != NULL) {  
           
             /*  
              * Need to get the next pointer first. TkSetWindowMenuBar  
              * changes the list, so that the next pointer is different  
              * after calling it.  
              */  
           
             nextPtr = topLevelListPtr->nextPtr;  
             listtkwin = topLevelListPtr->tkwin;  
             TkSetWindowMenuBar(menuPtr->interp, listtkwin,  
                     Tk_PathName(menuPtr->tkwin), Tk_PathName(menuPtr->tkwin));  
             topLevelListPtr = nextPtr;  
         }  
     }  
   
     Tcl_SetResult(interp, Tk_PathName(menuPtr->tkwin), TCL_STATIC);  
     return TCL_OK;  
 }  
   
 /*  
  *--------------------------------------------------------------  
  *  
  * MenuWidgetObjCmd --  
  *  
  *      This procedure is invoked to process the Tcl command  
  *      that corresponds to a widget managed by this module.  
  *      See the user documentation for details on what it does.  
  *  
  * Results:  
  *      A standard Tcl result.  
  *  
  * Side effects:  
  *      See the user documentation.  
  *  
  *--------------------------------------------------------------  
  */  
   
 static int  
 MenuWidgetObjCmd(clientData, interp, objc, objv)  
     ClientData clientData;      /* Information about menu widget. */  
     Tcl_Interp *interp;         /* Current interpreter. */  
     int objc;                   /* Number of arguments. */  
     Tcl_Obj *CONST objv[];      /* Argument strings. */  
 {  
     register TkMenu *menuPtr = (TkMenu *) clientData;  
     register TkMenuEntry *mePtr;  
     int result = TCL_OK;  
     int option;  
   
     if (objc < 2) {  
         Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?");  
         return TCL_ERROR;  
     }  
     if (Tcl_GetIndexFromObj(interp, objv[1], menuOptions, "option", 0,  
             &option) != TCL_OK) {  
         return TCL_ERROR;  
     }  
     Tcl_Preserve((ClientData) menuPtr);  
   
     switch ((enum options) option) {  
         case MENU_ACTIVATE: {  
             int index;  
   
             if (objc != 3) {  
                 Tcl_WrongNumArgs(interp, 1, objv, "activate index");  
                 goto error;  
             }  
             if (TkGetMenuIndex(interp, menuPtr, objv[2], 0, &index)  
                     != TCL_OK) {  
                 goto error;  
             }  
             if (menuPtr->active == index) {  
                 goto done;  
             }  
             if ((index >= 0)  
                     && ((menuPtr->entries[index]->type == SEPARATOR_ENTRY)  
                             || (menuPtr->entries[index]->state  
                                     == ENTRY_DISABLED))) {  
                 index = -1;  
             }  
             result = TkActivateMenuEntry(menuPtr, index);  
             break;  
         }  
         case MENU_ADD:  
             if (objc < 3) {  
                 Tcl_WrongNumArgs(interp, 1, objv, "add type ?options?");  
                 goto error;  
             }  
   
             if (MenuAddOrInsert(interp, menuPtr, (Tcl_Obj *) NULL,  
                     objc - 2, objv + 2) != TCL_OK) {  
                 goto error;  
             }  
             break;  
         case MENU_CGET: {  
             Tcl_Obj *resultPtr;  
   
             if (objc != 3) {  
                 Tcl_WrongNumArgs(interp, 1, objv, "cget option");  
                 goto error;  
             }  
             resultPtr = Tk_GetOptionValue(interp, (char *) menuPtr,  
                     menuPtr->optionTablesPtr->menuOptionTable, objv[2],  
                     menuPtr->tkwin);  
             if (resultPtr == NULL) {  
                 goto error;  
             }  
             Tcl_SetObjResult(interp, resultPtr);  
             break;  
         }  
         case MENU_CLONE:  
             if ((objc < 3) || (objc > 4)) {  
                 Tcl_WrongNumArgs(interp, 1, objv,  
                         "clone newMenuName ?menuType?");  
                 goto error;  
             }  
             result = CloneMenu(menuPtr, objv[2], (objc == 3) ? NULL : objv[3]);  
             break;  
         case MENU_CONFIGURE: {  
             Tcl_Obj *resultPtr;  
   
             if (objc == 2) {  
                 resultPtr = Tk_GetOptionInfo(interp, (char *) menuPtr,  
                         menuPtr->optionTablesPtr->menuOptionTable,  
                         (Tcl_Obj *) NULL, menuPtr->tkwin);  
                 if (resultPtr == NULL) {  
                     result = TCL_ERROR;  
                 } else {  
                     result = TCL_OK;  
                     Tcl_SetObjResult(interp, resultPtr);  
                 }  
             } else if (objc == 3) {  
                 resultPtr = Tk_GetOptionInfo(interp, (char *) menuPtr,  
                         menuPtr->optionTablesPtr->menuOptionTable,  
                         objv[2], menuPtr->tkwin);  
                 if (resultPtr == NULL) {  
                     result = TCL_ERROR;  
                 } else {  
                     result = TCL_OK;  
                     Tcl_SetObjResult(interp, resultPtr);  
                 }  
             } else {  
                 result = ConfigureMenu(interp, menuPtr, objc - 2, objv + 2);  
             }  
             if (result != TCL_OK) {  
                 goto error;  
             }  
             break;  
         }  
         case MENU_DELETE: {  
             int first, last;  
               
             if ((objc != 3) && (objc != 4)) {  
                 Tcl_WrongNumArgs(interp, 1, objv, "delete first ?last?");  
                 goto error;  
             }  
             if (TkGetMenuIndex(interp, menuPtr, objv[2], 0, &first)  
                     != TCL_OK) {  
                 goto error;  
             }  
             if (objc == 3) {  
                 last = first;  
             } else {  
                 if (TkGetMenuIndex(interp, menuPtr, objv[3], 0, &last)  
                         != TCL_OK) {  
                     goto error;  
                 }  
             }  
             if (menuPtr->tearoff && (first == 0)) {  
   
                 /*  
                  * Sorry, can't delete the tearoff entry;  must reconfigure  
                  * the menu.  
                  */  
                   
                 first = 1;  
             }  
             if ((first < 0) || (last < first)) {  
                 goto done;  
             }  
             DeleteMenuCloneEntries(menuPtr, first, last);  
             break;  
         }  
         case MENU_ENTRYCGET: {  
             int index;  
             Tcl_Obj *resultPtr;  
   
             if (objc != 4) {  
                 Tcl_WrongNumArgs(interp, 1, objv, "entrycget index option");  
                 goto error;  
             }  
             if (TkGetMenuIndex(interp, menuPtr, objv[2], 0, &index)  
                     != TCL_OK) {  
                 goto error;  
             }  
             if (index < 0) {  
                 goto done;  
             }  
             mePtr = menuPtr->entries[index];  
             Tcl_Preserve((ClientData) mePtr);  
             resultPtr = Tk_GetOptionValue(interp, (char *) mePtr,  
                     mePtr->optionTable, objv[3], menuPtr->tkwin);  
             Tcl_Release((ClientData) mePtr);  
             if (resultPtr == NULL) {  
                 goto error;  
             }  
             Tcl_SetObjResult(interp, resultPtr);  
             break;  
         }  
         case MENU_ENTRYCONFIGURE: {  
             int index;  
             Tcl_Obj *resultPtr;  
   
             if (objc < 3) {  
                 Tcl_WrongNumArgs(interp, 1, objv,  
                         "entryconfigure index ?option value ...?");  
                 goto error;  
             }  
             if (TkGetMenuIndex(interp, menuPtr, objv[2], 0, &index)  
                     != TCL_OK) {  
                 goto error;  
             }  
             if (index < 0) {  
                 goto done;  
             }  
             mePtr = menuPtr->entries[index];  
             Tcl_Preserve((ClientData) mePtr);  
             if (objc == 3) {  
                 resultPtr = Tk_GetOptionInfo(interp, (char *) mePtr,  
                         mePtr->optionTable, (Tcl_Obj *) NULL, menuPtr->tkwin);  
                 if (resultPtr == NULL) {  
                     result = TCL_ERROR;  
                 } else {  
                     result = TCL_OK;  
                     Tcl_SetObjResult(interp, resultPtr);  
                 }  
             } else if (objc == 4) {  
                 resultPtr = Tk_GetOptionInfo(interp, (char *) mePtr,  
                         mePtr->optionTable, objv[3], menuPtr->tkwin);  
                 if (resultPtr == NULL) {  
                     result = TCL_ERROR;  
                 } else {  
                     result = TCL_OK;  
                     Tcl_SetObjResult(interp, resultPtr);  
                 }  
             } else {  
                 result = ConfigureMenuCloneEntries(interp, menuPtr, index,  
                         objc - 3, objv + 3);  
             }  
             Tcl_Release((ClientData) mePtr);  
             break;  
         }  
         case MENU_INDEX: {  
             int index;  
   
             if (objc != 3) {  
                 Tcl_WrongNumArgs(interp, 1, objv, "index string");  
                 goto error;  
             }  
             if (TkGetMenuIndex(interp, menuPtr, objv[2], 0, &index)  
                     != TCL_OK) {  
                 goto error;  
             }  
             if (index < 0) {  
                 Tcl_SetResult(interp, "none", TCL_STATIC);  
             } else {  
                 Tcl_SetIntObj(Tcl_GetObjResult(interp), index);  
             }  
             break;  
         }  
         case MENU_INSERT:  
             if (objc < 4) {  
                 Tcl_WrongNumArgs(interp, 1, objv,  
                         "insert index type ?options?");  
                 goto error;  
             }  
             if (MenuAddOrInsert(interp, menuPtr, objv[2], objc - 3,  
                     objv + 3) != TCL_OK) {  
                 goto error;  
             }  
             break;  
         case MENU_INVOKE: {  
             int index;  
   
             if (objc != 3) {  
                 Tcl_WrongNumArgs(interp, 1, objv, "invoke index");  
                 goto error;  
             }  
             if (TkGetMenuIndex(interp, menuPtr, objv[2], 0, &index)  
                     != TCL_OK) {  
                 goto error;  
             }  
             if (index < 0) {  
                 goto done;  
             }  
             result = TkInvokeMenu(interp, menuPtr, index);  
             break;  
         }  
         case MENU_POST: {  
             int x, y;  
   
             if (objc != 4) {  
                 Tcl_WrongNumArgs(interp, 1, objv, "post x y");  
                 goto error;  
             }  
             if ((Tcl_GetIntFromObj(interp, objv[2], &x) != TCL_OK)  
                     || (Tcl_GetIntFromObj(interp, objv[3], &y) != TCL_OK)) {  
                 goto error;  
             }  
   
             /*  
              * Tearoff menus are posted differently on Mac and Windows than  
              * non-tearoffs. TkpPostMenu does not actually map the menu's  
              * window on those platforms, and popup menus have to be  
              * handled specially.  
              */  
               
             if (menuPtr->menuType != TEAROFF_MENU) {  
                 result = TkpPostMenu(interp, menuPtr, x, y);  
             } else {  
                 result = TkPostTearoffMenu(interp, menuPtr, x, y);  
             }  
             break;  
         }  
         case MENU_POSTCASCADE: {  
             int index;  
   
             if (objc != 3) {  
                 Tcl_WrongNumArgs(interp, 1, objv, "postcascade index");  
                 goto error;  
             }  
   
             if (TkGetMenuIndex(interp, menuPtr, objv[2], 0, &index)  
                     != TCL_OK) {  
                 goto error;  
             }  
             if ((index < 0) || (menuPtr->entries[index]->type  
                     != CASCADE_ENTRY)) {  
                 result = TkPostSubmenu(interp, menuPtr, (TkMenuEntry *) NULL);  
             } else {  
                 result = TkPostSubmenu(interp, menuPtr,  
                         menuPtr->entries[index]);  
             }  
             break;  
         }  
         case MENU_TYPE: {  
             int index;  
   
             if (objc != 3) {  
                 Tcl_WrongNumArgs(interp, 1, objv, "type index");  
                 goto error;  
             }  
             if (TkGetMenuIndex(interp, menuPtr, objv[2], 0, &index)  
                     != TCL_OK) {  
                 goto error;  
             }  
             if (index < 0) {  
                 goto done;  
             }  
             if (menuPtr->entries[index]->type == TEAROFF_ENTRY) {  
                 Tcl_SetResult(interp, "tearoff", TCL_STATIC);  
             } else {  
                 Tcl_SetResult(interp,  
                         menuEntryTypeStrings[menuPtr->entries[index]->type],  
                         TCL_STATIC);  
             }  
             break;  
         }  
         case MENU_UNPOST:  
             if (objc != 2) {  
                 Tcl_WrongNumArgs(interp, 1, objv, "unpost");  
                 goto error;  
             }  
             Tk_UnmapWindow(menuPtr->tkwin);  
             result = TkPostSubmenu(interp, menuPtr, (TkMenuEntry *) NULL);  
             break;  
         case MENU_YPOSITION:  
             if (objc != 3) {  
                 Tcl_WrongNumArgs(interp, 1, objv, "yposition index");  
                 goto error;  
             }  
             result = MenuDoYPosition(interp, menuPtr, objv[2]);  
             break;  
     }  
     done:  
     Tcl_Release((ClientData) menuPtr);  
     return result;  
   
     error:  
     Tcl_Release((ClientData) menuPtr);  
     return TCL_ERROR;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * TkInvokeMenu --  
  *  
  *      Given a menu and an index, takes the appropriate action for the  
  *      entry associated with that index.  
  *  
  * Results:  
  *      Standard Tcl result.  
  *  
  * Side effects:  
  *      Commands may get excecuted; variables may get set; sub-menus may  
  *      get posted.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 int  
 TkInvokeMenu(interp, menuPtr, index)  
     Tcl_Interp *interp;         /* The interp that the menu lives in. */  
     TkMenu *menuPtr;            /* The menu we are invoking. */  
     int index;                  /* The zero based index of the item we  
                                  * are invoking */  
 {  
     int result = TCL_OK;  
     TkMenuEntry *mePtr;  
       
     if (index < 0) {  
         goto done;  
     }  
     mePtr = menuPtr->entries[index];  
     if (mePtr->state == ENTRY_DISABLED) {  
         goto done;  
     }  
     Tcl_Preserve((ClientData) mePtr);  
     if (mePtr->type == TEAROFF_ENTRY) {  
         Tcl_DString ds;  
         Tcl_DStringInit(&ds);  
         Tcl_DStringAppend(&ds, "tkTearOffMenu ", -1);  
         Tcl_DStringAppend(&ds, Tk_PathName(menuPtr->tkwin), -1);  
         result = Tcl_Eval(interp, Tcl_DStringValue(&ds));  
         Tcl_DStringFree(&ds);  
     } else if ((mePtr->type == CHECK_BUTTON_ENTRY)  
             && (mePtr->namePtr != NULL)) {  
         Tcl_Obj *valuePtr;  
   
         if (mePtr->entryFlags & ENTRY_SELECTED) {  
             valuePtr = mePtr->offValuePtr;  
         } else {  
             valuePtr = mePtr->onValuePtr;  
         }  
         if (valuePtr == NULL) {  
             valuePtr = Tcl_NewObj();  
         }  
         Tcl_IncrRefCount(valuePtr);  
         if (Tcl_ObjSetVar2(interp, mePtr->namePtr, NULL, valuePtr,  
                 TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {  
             result = TCL_ERROR;  
         }  
         Tcl_DecrRefCount(valuePtr);  
     } else if ((mePtr->type == RADIO_BUTTON_ENTRY)  
             && (mePtr->namePtr != NULL)) {  
         Tcl_Obj *valuePtr = mePtr->onValuePtr;  
   
         if (valuePtr == NULL) {  
             valuePtr = Tcl_NewObj();  
         }  
         Tcl_IncrRefCount(valuePtr);  
         if (Tcl_ObjSetVar2(interp, mePtr->namePtr, NULL, valuePtr,  
                 TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {  
             result = TCL_ERROR;  
         }  
         Tcl_DecrRefCount(valuePtr);  
     }  
     if ((result == TCL_OK) && (mePtr->commandPtr != NULL)) {  
         Tcl_Obj *commandPtr = mePtr->commandPtr;  
   
         Tcl_IncrRefCount(commandPtr);  
         result = Tcl_EvalObjEx(interp, commandPtr, TCL_EVAL_GLOBAL);  
         Tcl_DecrRefCount(commandPtr);  
     }  
     Tcl_Release((ClientData) mePtr);  
     done:  
     return result;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * DestroyMenuInstance --  
  *  
  *      This procedure is invoked by TkDestroyMenu  
  *      to clean up the internal structure of a menu at a safe time  
  *      (when no-one is using it anymore). Only takes care of one instance  
  *      of the menu.  
  *  
  * Results:  
  *      None.  
  *  
  * Side effects:  
  *      Everything associated with the menu is freed up.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 static void  
 DestroyMenuInstance(menuPtr)  
     TkMenu *menuPtr;    /* Info about menu widget. */  
 {  
     int i;  
     TkMenu *menuInstancePtr;  
     TkMenuEntry *cascadePtr, *nextCascadePtr;  
     Tcl_Obj *newObjv[2];  
     TkMenu *parentMasterMenuPtr;  
     TkMenuEntry *parentMasterEntryPtr;  
       
     /*  
      * If the menu has any cascade menu entries pointing to it, the cascade  
      * entries need to be told that the menu is going away. We need to clear  
      * the menu ptr field in the menu reference at this point in the code  
      * so that everything else can forget about this menu properly. We also  
      * need to reset -menu field of all entries that are not master menus  
      * back to this entry name if this is a master menu pointed to by another  
      * master menu. If there is a clone menu that points to this menu,  
      * then this menu is itself a clone, so when this menu goes away,  
      * the -menu field of the pointing entry must be set back to this  
      * menu's master menu name so that later if another menu is created  
      * the cascade hierarchy can be maintained.  
      */  
   
     TkpDestroyMenu(menuPtr);  
     cascadePtr = menuPtr->menuRefPtr->parentEntryPtr;  
     menuPtr->menuRefPtr->menuPtr = NULL;  
     TkFreeMenuReferences(menuPtr->menuRefPtr);  
   
     for (; cascadePtr != NULL; cascadePtr = nextCascadePtr) {  
         nextCascadePtr = cascadePtr->nextCascadePtr;  
           
         if (menuPtr->masterMenuPtr != menuPtr) {  
             Tcl_Obj *menuNamePtr = Tcl_NewStringObj("-menu", -1);  
   
             parentMasterMenuPtr = cascadePtr->menuPtr->masterMenuPtr;  
             parentMasterEntryPtr =  
                     parentMasterMenuPtr->entries[cascadePtr->index];  
             newObjv[0] = menuNamePtr;  
             newObjv[1] = parentMasterEntryPtr->namePtr;  
             /*  
              * It is possible that the menu info is out of sync, and  
              * these things point to NULL, so verify existence [Bug: 3402]  
              */  
             if (newObjv[0] && newObjv[1]) {  
                 Tcl_IncrRefCount(newObjv[0]);  
                 Tcl_IncrRefCount(newObjv[1]);  
                 ConfigureMenuEntry(cascadePtr, 2, newObjv);  
                 Tcl_DecrRefCount(newObjv[0]);  
                 Tcl_DecrRefCount(newObjv[1]);  
             }  
         } else {  
             ConfigureMenuEntry(cascadePtr, 0, (Tcl_Obj **) NULL);  
         }  
     }  
       
     if (menuPtr->masterMenuPtr != menuPtr) {  
         for (menuInstancePtr = menuPtr->masterMenuPtr;  
                 menuInstancePtr != NULL;  
                 menuInstancePtr = menuInstancePtr->nextInstancePtr) {  
             if (menuInstancePtr->nextInstancePtr == menuPtr) {  
                 menuInstancePtr->nextInstancePtr =  
                         menuInstancePtr->nextInstancePtr->nextInstancePtr;  
                 break;  
             }  
         }  
    } else if (menuPtr->nextInstancePtr != NULL) {  
        panic("Attempting to delete master menu when there are still clones.");  
    }  
   
     /*  
      * Free up all the stuff that requires special handling, then  
      * let Tk_FreeConfigOptions handle all the standard option-related  
      * stuff.  
      */  
   
     for (i = menuPtr->numEntries; --i >= 0; ) {  
         /*  
          * As each menu entry is deleted from the end of the array of  
          * entries, decrement menuPtr->numEntries.  Otherwise, the act of  
          * deleting menu entry i will dereference freed memory attempting  
          * to queue a redraw for menu entries (i+1)...numEntries.  
          */  
           
         DestroyMenuEntry((char *) menuPtr->entries[i]);  
         menuPtr->numEntries = i;  
     }  
     if (menuPtr->entries != NULL) {  
         ckfree((char *) menuPtr->entries);  
     }  
     TkMenuFreeDrawOptions(menuPtr);  
     Tk_FreeConfigOptions((char *) menuPtr,  
             menuPtr->optionTablesPtr->menuOptionTable, menuPtr->tkwin);  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * TkDestroyMenu --  
  *  
  *      This procedure is invoked by Tcl_EventuallyFree or Tcl_Release  
  *      to clean up the internal structure of a menu at a safe time  
  *      (when no-one is using it anymore).  If called on a master instance,  
  *      destroys all of the slave instances. If called on a non-master  
  *      instance, just destroys that instance.  
  *  
  * Results:  
  *      None.  
  *  
  * Side effects:  
  *      Everything associated with the menu is freed up.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 void  
 TkDestroyMenu(menuPtr)  
     TkMenu *menuPtr;    /* Info about menu widget. */  
 {  
     TkMenu *menuInstancePtr;  
     TkMenuTopLevelList *topLevelListPtr, *nextTopLevelPtr;  
   
     if (menuPtr->menuFlags & MENU_DELETION_PENDING) {  
         return;  
     }  
       
     /*  
      * Now destroy all non-tearoff instances of this menu if this is a  
      * parent menu. Is this loop safe enough? Are there going to be  
      * destroy bindings on child menus which kill the parent? If not,  
      * we have to do a slightly more complex scheme.  
      */  
       
     if (menuPtr->masterMenuPtr == menuPtr) {  
         menuPtr->menuFlags |= MENU_DELETION_PENDING;  
         while (menuPtr->nextInstancePtr != NULL) {  
             menuInstancePtr = menuPtr->nextInstancePtr;  
             menuPtr->nextInstancePtr = menuInstancePtr->nextInstancePtr;  
             if (menuInstancePtr->tkwin != NULL) {  
                 Tk_DestroyWindow(menuInstancePtr->tkwin);  
             }  
         }  
         menuPtr->menuFlags &= ~MENU_DELETION_PENDING;  
     }  
   
     /*  
      * If any toplevel widgets have this menu as their menubar,  
      * the geometry of the window may have to be recalculated.  
      */  
       
     topLevelListPtr = menuPtr->menuRefPtr->topLevelListPtr;  
     while (topLevelListPtr != NULL) {  
          nextTopLevelPtr = topLevelListPtr->nextPtr;  
          TkpSetWindowMenuBar(topLevelListPtr->tkwin, NULL);  
          topLevelListPtr = nextTopLevelPtr;  
     }    
     DestroyMenuInstance(menuPtr);  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * UnhookCascadeEntry --  
  *  
  *      This entry is removed from the list of entries that point to the  
  *      cascade menu. This is done in preparation for changing the menu  
  *      that this entry points to.  
  *  
  * Results:  
  *      None  
  *  
  * Side effects:  
  *      The appropriate lists are modified.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 static void  
 UnhookCascadeEntry(mePtr)  
     TkMenuEntry *mePtr;                 /* The cascade entry we are removing  
                                          * from the cascade list. */  
 {  
     TkMenuEntry *cascadeEntryPtr;  
     TkMenuEntry *prevCascadePtr;  
     TkMenuReferences *menuRefPtr;  
   
     menuRefPtr = mePtr->childMenuRefPtr;  
     if (menuRefPtr == NULL) {  
         return;  
     }  
       
     cascadeEntryPtr = menuRefPtr->parentEntryPtr;  
     if (cascadeEntryPtr == NULL) {  
         return;  
     }  
       
     /*  
      * Singularly linked list deletion. The two special cases are  
      * 1. one element; 2. The first element is the one we want.  
      */  
   
     if (cascadeEntryPtr == mePtr) {  
         if (cascadeEntryPtr->nextCascadePtr == NULL) {  
   
             /*  
              * This is the last menu entry which points to this  
              * menu, so we need to clear out the list pointer in the  
              * cascade itself.  
              */  
           
             menuRefPtr->parentEntryPtr = NULL;  
             TkFreeMenuReferences(menuRefPtr);  
         } else {  
             menuRefPtr->parentEntryPtr = cascadeEntryPtr->nextCascadePtr;  
         }  
         mePtr->nextCascadePtr = NULL;  
     } else {  
         for (prevCascadePtr = cascadeEntryPtr,  
                 cascadeEntryPtr = cascadeEntryPtr->nextCascadePtr;  
                 cascadeEntryPtr != NULL;  
                 prevCascadePtr = cascadeEntryPtr,  
                 cascadeEntryPtr = cascadeEntryPtr->nextCascadePtr) {  
             if (cascadeEntryPtr == mePtr){  
                 prevCascadePtr->nextCascadePtr =  
                         cascadeEntryPtr->nextCascadePtr;  
                 cascadeEntryPtr->nextCascadePtr = NULL;  
                 break;  
             }  
         }  
     }  
     mePtr->childMenuRefPtr = NULL;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * DestroyMenuEntry --  
  *  
  *      This procedure is invoked by Tcl_EventuallyFree or Tcl_Release  
  *      to clean up the internal structure of a menu entry at a safe time  
  *      (when no-one is using it anymore).  
  *  
  * Results:  
  *      None.  
  *  
  * Side effects:  
  *      Everything associated with the menu entry is freed.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 static void  
 DestroyMenuEntry(memPtr)  
     char *memPtr;               /* Pointer to entry to be freed. */  
 {  
     register TkMenuEntry *mePtr = (TkMenuEntry *) memPtr;  
     TkMenu *menuPtr = mePtr->menuPtr;  
   
     if (menuPtr->postedCascade == mePtr) {  
           
         /*  
          * Ignore errors while unposting the menu, since it's possible  
          * that the menu has already been deleted and the unpost will  
          * generate an error.  
          */  
   
         TkPostSubmenu(menuPtr->interp, menuPtr, (TkMenuEntry *) NULL);  
     }  
   
     /*  
      * Free up all the stuff that requires special handling, then  
      * let Tk_FreeConfigOptions handle all the standard option-related  
      * stuff.  
      */  
   
     if (mePtr->type == CASCADE_ENTRY) {  
         UnhookCascadeEntry(mePtr);  
     }  
     if (mePtr->image != NULL) {  
         Tk_FreeImage(mePtr->image);  
     }  
     if (mePtr->selectImage != NULL) {  
         Tk_FreeImage(mePtr->selectImage);  
     }  
     if (((mePtr->type == CHECK_BUTTON_ENTRY)  
             || (mePtr->type == RADIO_BUTTON_ENTRY))  
             && (mePtr->namePtr != NULL)) {  
         char *varName = Tcl_GetStringFromObj(mePtr->namePtr, NULL);  
         Tcl_UntraceVar(menuPtr->interp, varName,  
                 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,  
                 MenuVarProc, (ClientData) mePtr);  
     }  
     TkpDestroyMenuEntry(mePtr);  
     TkMenuEntryFreeDrawOptions(mePtr);  
     Tk_FreeConfigOptions((char *) mePtr, mePtr->optionTable, menuPtr->tkwin);  
     ckfree((char *) mePtr);  
 }  
   
 /*  
  *---------------------------------------------------------------------------  
  *  
  * MenuWorldChanged --  
  *  
  *      This procedure is called when the world has changed in some  
  *      way (such as the fonts in the system changing) and the widget needs  
  *      to recompute all its graphics contexts and determine its new geometry.  
  *  
  * Results:  
  *      None.  
  *  
  * Side effects:  
  *      Menu will be relayed out and redisplayed.  
  *  
  *---------------------------------------------------------------------------  
  */  
   
 static void  
 MenuWorldChanged(instanceData)  
     ClientData instanceData;    /* Information about widget. */  
 {  
     TkMenu *menuPtr = (TkMenu *) instanceData;  
     int i;  
       
     TkMenuConfigureDrawOptions(menuPtr);  
     for (i = 0; i < menuPtr->numEntries; i++) {  
         TkMenuConfigureEntryDrawOptions(menuPtr->entries[i],  
                 menuPtr->entries[i]->index);  
         TkpConfigureMenuEntry(menuPtr->entries[i]);      
     }  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * ConfigureMenu --  
  *  
  *      This procedure is called to process an argv/argc list, plus  
  *      the Tk option database, in order to configure (or  
  *      reconfigure) a menu widget.  
  *  
  * Results:  
  *      The return value is a standard Tcl result.  If TCL_ERROR is  
  *      returned, then the interp's result contains an error message.  
  *  
  * Side effects:  
  *      Configuration information, such as colors, font, etc. get set  
  *      for menuPtr;  old resources get freed, if there were any.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 static int  
 ConfigureMenu(interp, menuPtr, objc, objv)  
     Tcl_Interp *interp;         /* Used for error reporting. */  
     register TkMenu *menuPtr;   /* Information about widget;  may or may  
                                  * not already have values for some fields. */  
     int objc;                   /* Number of valid entries in argv. */  
     Tcl_Obj *CONST objv[];      /* Arguments. */  
 {  
     int i;  
     TkMenu *menuListPtr, *cleanupPtr;  
     int result;  
       
     for (menuListPtr = menuPtr->masterMenuPtr; menuListPtr != NULL;  
             menuListPtr = menuListPtr->nextInstancePtr) {  
         menuListPtr->errorStructPtr = (Tk_SavedOptions *)  
                 ckalloc(sizeof(Tk_SavedOptions));  
         result = Tk_SetOptions(interp, (char *) menuListPtr,  
                 menuListPtr->optionTablesPtr->menuOptionTable, objc, objv,  
                 menuListPtr->tkwin, menuListPtr->errorStructPtr, (int *) NULL);  
         if (result != TCL_OK) {  
             for (cleanupPtr = menuPtr->masterMenuPtr;  
                     cleanupPtr != menuListPtr;  
                     cleanupPtr = cleanupPtr->nextInstancePtr) {  
                 Tk_RestoreSavedOptions(cleanupPtr->errorStructPtr);  
                 ckfree((char *) cleanupPtr->errorStructPtr);  
                 cleanupPtr->errorStructPtr = NULL;  
             }  
             return TCL_ERROR;  
         }  
   
         /*  
          * When a menu is created, the type is in all of the arguments  
          * to the menu command. Let Tk_ConfigureWidget take care of  
          * parsing them, and then set the type after we can look at  
          * the type string. Once set, a menu's type cannot be changed  
          */  
           
         if (menuListPtr->menuType == UNKNOWN_TYPE) {  
             Tcl_GetIndexFromObj(NULL, menuListPtr->menuTypePtr,  
                     menuTypeStrings, NULL, 0, &menuListPtr->menuType);  
   
             /*  
              * Configure the new window to be either a pop-up menu  
              * or a tear-off menu.  
              * We don't do this for menubars since they are not toplevel  
              * windows. Also, since this gets called before CloneMenu has  
              * a chance to set the menuType field, we have to look at the  
              * menuTypeName field to tell that this is a menu bar.  
              */  
               
             if (menuListPtr->menuType == MASTER_MENU) {  
                 TkpMakeMenuWindow(menuListPtr->tkwin, 1);  
             } else if (menuListPtr->menuType == TEAROFF_MENU) {  
                 TkpMakeMenuWindow(menuListPtr->tkwin, 0);  
             }  
         }  
   
   
         /*  
          * Depending on the -tearOff option, make sure that there is or  
          * isn't an initial tear-off entry at the beginning of the menu.  
          */  
           
         if (menuListPtr->tearoff) {  
             if ((menuListPtr->numEntries == 0)  
                     || (menuListPtr->entries[0]->type != TEAROFF_ENTRY)) {  
                 if (MenuNewEntry(menuListPtr, 0, TEAROFF_ENTRY) == NULL) {  
                     if (menuListPtr->errorStructPtr != NULL) {  
                         for (cleanupPtr = menuPtr->masterMenuPtr;  
                                 cleanupPtr != menuListPtr;  
                                 cleanupPtr = cleanupPtr->nextInstancePtr) {  
                             Tk_RestoreSavedOptions(cleanupPtr->errorStructPtr);  
                             ckfree((char *) cleanupPtr->errorStructPtr);  
                             cleanupPtr->errorStructPtr = NULL;  
                         }  
                         Tk_RestoreSavedOptions(cleanupPtr->errorStructPtr);  
                         ckfree((char *) cleanupPtr->errorStructPtr);  
                         cleanupPtr->errorStructPtr = NULL;  
                     }  
                     return TCL_ERROR;  
                 }  
             }  
         } else if ((menuListPtr->numEntries > 0)  
                 && (menuListPtr->entries[0]->type == TEAROFF_ENTRY)) {  
             int i;  
               
             Tcl_EventuallyFree((ClientData) menuListPtr->entries[0],  
                     DestroyMenuEntry);  
   
             for (i = 0; i < menuListPtr->numEntries - 1; i++) {  
                 menuListPtr->entries[i] = menuListPtr->entries[i + 1];  
                 menuListPtr->entries[i]->index = i;  
             }  
             menuListPtr->numEntries--;  
             if (menuListPtr->numEntries == 0) {  
                 ckfree((char *) menuListPtr->entries);  
                 menuListPtr->entries = NULL;  
             }  
         }  
   
         TkMenuConfigureDrawOptions(menuListPtr);  
           
         /*  
          * After reconfiguring a menu, we need to reconfigure all of the  
          * entries in the menu, since some of the things in the children  
          * (such as graphics contexts) may have to change to reflect changes  
          * in the parent.  
          */  
           
         for (i = 0; i < menuListPtr->numEntries; i++) {  
             TkMenuEntry *mePtr;  
           
             mePtr = menuListPtr->entries[i];  
             ConfigureMenuEntry(mePtr, 0, (Tcl_Obj **) NULL);  
         }  
           
         TkEventuallyRecomputeMenu(menuListPtr);  
     }  
   
     for (cleanupPtr = menuPtr->masterMenuPtr; cleanupPtr != NULL;  
             cleanupPtr = cleanupPtr->nextInstancePtr) {  
         Tk_FreeSavedOptions(cleanupPtr->errorStructPtr);  
         ckfree((char *) cleanupPtr->errorStructPtr);  
         cleanupPtr->errorStructPtr = NULL;  
     }  
   
     return TCL_OK;  
 }  
   
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * PostProcessEntry --  
  *  
  *      This is called by ConfigureMenuEntry to do all of the configuration  
  *      after Tk_SetOptions is called. This is separate  
  *      so that error handling is easier.  
  *  
  * Results:  
  *      The return value is a standard Tcl result.  If TCL_ERROR is  
  *      returned, then the interp's result contains an error message.  
  *  
  * Side effects:  
  *      Configuration information such as label and accelerator get  
  *      set for mePtr;  old resources get freed, if there were any.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 static int  
 PostProcessEntry(mePtr)  
     TkMenuEntry *mePtr;                 /* The entry we are configuring. */  
 {  
     TkMenu *menuPtr = mePtr->menuPtr;  
     int index = mePtr->index;  
     char *name;  
     Tk_Image image;  
   
     /*  
      * The code below handles special configuration stuff not taken  
      * care of by Tk_ConfigureWidget, such as special processing for  
      * defaults, sizing strings, graphics contexts, etc.  
      */  
   
     if (mePtr->labelPtr == NULL) {  
         mePtr->labelLength = 0;  
     } else {  
         Tcl_GetStringFromObj(mePtr->labelPtr, &mePtr->labelLength);  
     }  
     if (mePtr->accelPtr == NULL) {  
         mePtr->accelLength = 0;  
     } else {  
         Tcl_GetStringFromObj(mePtr->accelPtr, &mePtr->accelLength);  
     }  
   
     /*  
      * If this is a cascade entry, the platform-specific data of the child  
      * menu has to be updated. Also, the links that point to parents and  
      * cascades have to be updated.  
      */  
   
     if ((mePtr->type == CASCADE_ENTRY) && (mePtr->namePtr != NULL)) {  
         TkMenuEntry *cascadeEntryPtr;  
         int alreadyThere;  
         TkMenuReferences *menuRefPtr;  
         char *oldHashKey = NULL;        /* Initialization only needed to  
                                          * prevent compiler warning. */  
   
         /*  
          * This is a cascade entry. If the menu that the cascade entry  
          * is pointing to has changed, we need to remove this entry  
          * from the list of entries pointing to the old menu, and add a  
          * cascade reference to the list of entries pointing to the  
          * new menu.  
          *  
          * BUG: We are not recloning for special case #3 yet.  
          */  
           
         name = Tcl_GetStringFromObj(mePtr->namePtr, NULL);  
         if (mePtr->childMenuRefPtr != NULL) {  
             oldHashKey = Tcl_GetHashKey(TkGetMenuHashTable(menuPtr->interp),  
                     mePtr->childMenuRefPtr->hashEntryPtr);  
             if (strcmp(oldHashKey, name) != 0) {  
                 UnhookCascadeEntry(mePtr);  
             }  
         }  
   
         if ((mePtr->childMenuRefPtr == NULL)  
                 || (strcmp(oldHashKey, name) != 0)) {  
             menuRefPtr = TkCreateMenuReferences(menuPtr->interp, name);  
             mePtr->childMenuRefPtr = menuRefPtr;  
   
             if (menuRefPtr->parentEntryPtr == NULL) {  
                 menuRefPtr->parentEntryPtr = mePtr;  
             } else {  
                 alreadyThere = 0;  
                 for (cascadeEntryPtr = menuRefPtr->parentEntryPtr;  
                         cascadeEntryPtr != NULL;  
                         cascadeEntryPtr =  
                         cascadeEntryPtr->nextCascadePtr) {  
                     if (cascadeEntryPtr == mePtr) {  
                         alreadyThere = 1;  
                         break;  
                     }  
                 }  
       
                 /*  
                  * Put the item at the front of the list.  
                  */  
               
                 if (!alreadyThere) {  
                     mePtr->nextCascadePtr = menuRefPtr->parentEntryPtr;  
                     menuRefPtr->parentEntryPtr = mePtr;  
                 }  
             }  
         }  
     }  
       
     if (TkMenuConfigureEntryDrawOptions(mePtr, index) != TCL_OK) {  
         return TCL_ERROR;  
     }  
   
     if (TkpConfigureMenuEntry(mePtr) != TCL_OK) {  
         return TCL_ERROR;  
     }  
       
     /*  
      * Get the images for the entry, if there are any.  Allocate the  
      * new images before freeing the old ones, so that the reference  
      * counts don't go to zero and cause image data to be discarded.  
      */  
   
     if (mePtr->imagePtr != NULL) {  
         char *imageString = Tcl_GetStringFromObj(mePtr->imagePtr, NULL);  
         image = Tk_GetImage(menuPtr->interp, menuPtr->tkwin, imageString,  
                 TkMenuImageProc, (ClientData) mePtr);  
         if (image == NULL) {  
             return TCL_ERROR;  
         }  
     } else {  
         image = NULL;  
     }  
     if (mePtr->image != NULL) {  
         Tk_FreeImage(mePtr->image);  
     }  
     mePtr->image = image;  
     if (mePtr->selectImagePtr != NULL) {  
         char *selectImageString = Tcl_GetStringFromObj(  
                 mePtr->selectImagePtr, NULL);  
         image = Tk_GetImage(menuPtr->interp, menuPtr->tkwin, selectImageString,  
                 TkMenuSelectImageProc, (ClientData) mePtr);  
         if (image == NULL) {  
             return TCL_ERROR;  
         }  
     } else {  
         image = NULL;  
     }  
     if (mePtr->selectImage != NULL) {  
         Tk_FreeImage(mePtr->selectImage);  
     }  
     mePtr->selectImage = image;  
   
     if ((mePtr->type == CHECK_BUTTON_ENTRY)  
             || (mePtr->type == RADIO_BUTTON_ENTRY)) {  
         Tcl_Obj *valuePtr;  
         char *name;  
   
         if (mePtr->namePtr == NULL) {  
             if (mePtr->labelPtr == NULL) {  
                 mePtr->namePtr = NULL;  
             } else {  
                 mePtr->namePtr = Tcl_DuplicateObj(mePtr->labelPtr);  
                 Tcl_IncrRefCount(mePtr->namePtr);  
             }  
         }  
         if (mePtr->onValuePtr == NULL) {  
             if (mePtr->labelPtr == NULL) {  
                 mePtr->onValuePtr = NULL;  
             } else {  
                 mePtr->onValuePtr = Tcl_DuplicateObj(mePtr->labelPtr);  
                 Tcl_IncrRefCount(mePtr->onValuePtr);  
             }  
         }  
   
         /*  
          * Select the entry if the associated variable has the  
          * appropriate value, initialize the variable if it doesn't  
          * exist, then set a trace on the variable to monitor future  
          * changes to its value.  
          */  
           
         if (mePtr->namePtr != NULL) {  
             valuePtr = Tcl_ObjGetVar2(menuPtr->interp, mePtr->namePtr, NULL,  
                     TCL_GLOBAL_ONLY);  
         } else {  
             valuePtr = NULL;  
         }  
         mePtr->entryFlags &= ~ENTRY_SELECTED;  
         if (valuePtr != NULL) {  
             if (mePtr->onValuePtr != NULL) {  
                 char *value = Tcl_GetStringFromObj(valuePtr, NULL);  
                 char *onValue = Tcl_GetStringFromObj(mePtr->onValuePtr,  
                         NULL);  
   
   
                 if (strcmp(value, onValue) == 0) {  
                     mePtr->entryFlags |= ENTRY_SELECTED;  
                 }  
             }  
         } else {  
             if (mePtr->namePtr != NULL) {  
                 Tcl_ObjSetVar2(menuPtr->interp, mePtr->namePtr, NULL,  
                         (mePtr->type == CHECK_BUTTON_ENTRY)  
                         ? mePtr->offValuePtr  
                         : Tcl_NewObj(),  
                         TCL_GLOBAL_ONLY);  
             }  
         }  
         if (mePtr->namePtr != NULL) {  
             name = Tcl_GetStringFromObj(mePtr->namePtr, NULL);  
             Tcl_TraceVar(menuPtr->interp, name,  
                     TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,  
                     MenuVarProc, (ClientData) mePtr);  
         }  
     }  
       
     return TCL_OK;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * ConfigureMenuEntry --  
  *  
  *      This procedure is called to process an argv/argc list in order  
  *      to configure (or reconfigure) one entry in a menu.  
  *  
  * Results:  
  *      The return value is a standard Tcl result.  If TCL_ERROR is  
  *      returned, then the interp's result contains an error message.  
  *  
  * Side effects:  
  *      Configuration information such as label and accelerator get  
  *      set for mePtr;  old resources get freed, if there were any.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 static int  
 ConfigureMenuEntry(mePtr, objc, objv)  
     register TkMenuEntry *mePtr;        /* Information about menu entry;  may  
                                          * or may not already have values for  
                                          * some fields. */  
     int objc;                           /* Number of valid entries in argv. */  
     Tcl_Obj *CONST objv[];              /* Arguments. */  
 {  
     TkMenu *menuPtr = mePtr->menuPtr;  
     Tk_SavedOptions errorStruct;  
     int result;  
   
     /*  
      * If this entry is a check button or radio button, then remove  
      * its old trace procedure.  
      */  
   
     if ((mePtr->namePtr != NULL)  
             && ((mePtr->type == CHECK_BUTTON_ENTRY)  
             || (mePtr->type == RADIO_BUTTON_ENTRY))) {  
         char *name = Tcl_GetStringFromObj(mePtr->namePtr, NULL);  
         Tcl_UntraceVar(menuPtr->interp, name,  
                 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,  
                 MenuVarProc, (ClientData) mePtr);  
     }  
   
     result = TCL_OK;  
     if (menuPtr->tkwin != NULL) {  
         if (Tk_SetOptions(menuPtr->interp, (char *) mePtr,  
                 mePtr->optionTable, objc, objv, menuPtr->tkwin,  
                 &errorStruct, (int *) NULL) != TCL_OK) {  
             return TCL_ERROR;  
         }  
         result = PostProcessEntry(mePtr);  
         if (result != TCL_OK) {  
             Tk_RestoreSavedOptions(&errorStruct);  
             PostProcessEntry(mePtr);  
         }  
         Tk_FreeSavedOptions(&errorStruct);  
     }  
   
     TkEventuallyRecomputeMenu(menuPtr);  
       
     return result;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * ConfigureMenuCloneEntries --  
  *  
  *      Calls ConfigureMenuEntry for each menu in the clone chain.  
  *  
  * Results:  
  *      The return value is a standard Tcl result.  If TCL_ERROR is  
  *      returned, then the interp's result contains an error message.  
  *  
  * Side effects:  
  *      Configuration information such as label and accelerator get  
  *      set for mePtr;  old resources get freed, if there were any.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 static int  
 ConfigureMenuCloneEntries(interp, menuPtr, index, objc, objv)  
     Tcl_Interp *interp;                 /* Used for error reporting. */  
     TkMenu *menuPtr;                    /* Information about whole menu. */  
     int index;                          /* Index of mePtr within menuPtr's  
                                          * entries. */  
     int objc;                           /* Number of valid entries in argv. */  
     Tcl_Obj *CONST objv[];              /* Arguments. */  
 {  
     TkMenuEntry *mePtr;  
     TkMenu *menuListPtr;  
     int cascadeEntryChanged = 0;  
     TkMenuReferences *oldCascadeMenuRefPtr, *cascadeMenuRefPtr = NULL;  
     Tcl_Obj *oldCascadePtr = NULL;  
     char *newCascadeName;  
   
     /*  
      * Cascades are kind of tricky here. This is special case #3 in the comment  
      * at the top of this file. Basically, if a menu is the master menu of a  
      * clone chain, and has an entry with a cascade menu, the clones of  
      * the menu will point to clones of the cascade menu. We have  
      * to destroy the clones of the cascades, clone the new cascade  
      * menu, and configure the entry to point to the new clone.  
      */  
   
     mePtr = menuPtr->masterMenuPtr->entries[index];  
     if (mePtr->type == CASCADE_ENTRY) {  
         oldCascadePtr = mePtr->namePtr;  
         if (oldCascadePtr != NULL) {  
             Tcl_IncrRefCount(oldCascadePtr);  
         }  
     }  
   
     if (ConfigureMenuEntry(mePtr, objc, objv) != TCL_OK) {  
         return TCL_ERROR;  
     }  
   
     if (mePtr->type == CASCADE_ENTRY) {  
         char *oldCascadeName;  
   
         if (mePtr->namePtr != NULL) {  
             newCascadeName = Tcl_GetStringFromObj(mePtr->namePtr, NULL);  
         } else {  
             newCascadeName = NULL;  
         }  
   
         if ((oldCascadePtr == NULL) && (mePtr->namePtr == NULL)) {  
             cascadeEntryChanged = 0;  
         } else if (((oldCascadePtr == NULL) && (mePtr->namePtr != NULL))  
                 || ((oldCascadePtr != NULL)  
                 && (mePtr->namePtr == NULL))) {  
             cascadeEntryChanged = 1;  
         } else {  
             oldCascadeName = Tcl_GetStringFromObj(oldCascadePtr,  
                     NULL);  
             cascadeEntryChanged = (strcmp(oldCascadeName, newCascadeName)  
                     == 0);  
         }  
         if (oldCascadePtr != NULL) {  
             Tcl_DecrRefCount(oldCascadePtr);  
         }  
     }  
   
     if (cascadeEntryChanged) {  
         if (mePtr->namePtr != NULL) {  
             newCascadeName = Tcl_GetStringFromObj(mePtr->namePtr, NULL);  
             cascadeMenuRefPtr = TkFindMenuReferences(menuPtr->interp,  
                     newCascadeName);  
         }  
     }  
   
     for (menuListPtr = menuPtr->masterMenuPtr->nextInstancePtr;  
             menuListPtr != NULL;  
             menuListPtr = menuListPtr->nextInstancePtr) {  
           
         mePtr = menuListPtr->entries[index];  
   
         if (cascadeEntryChanged && (mePtr->namePtr != NULL)) {  
             oldCascadeMenuRefPtr = TkFindMenuReferencesObj(menuPtr->interp,  
                     mePtr->namePtr);  
   
             if ((oldCascadeMenuRefPtr != NULL)  
                     && (oldCascadeMenuRefPtr->menuPtr != NULL)) {  
                 RecursivelyDeleteMenu(oldCascadeMenuRefPtr->menuPtr);  
             }  
         }  
   
         if (ConfigureMenuEntry(mePtr, objc, objv) != TCL_OK) {  
             return TCL_ERROR;  
         }  
           
         if (cascadeEntryChanged && (mePtr->namePtr != NULL)) {  
             if (cascadeMenuRefPtr->menuPtr != NULL) {  
                 Tcl_Obj *newObjv[2];  
                 Tcl_Obj *newCloneNamePtr;  
                 Tcl_Obj *pathNamePtr = Tcl_NewStringObj(  
                         Tk_PathName(menuListPtr->tkwin), -1);  
                 Tcl_Obj *normalPtr = Tcl_NewStringObj("normal", -1);  
                 Tcl_Obj *menuObjPtr = Tcl_NewStringObj("-menu", -1);  
   
                 Tcl_IncrRefCount(pathNamePtr);  
                 newCloneNamePtr = TkNewMenuName(menuPtr->interp,  
                         pathNamePtr,  
                         cascadeMenuRefPtr->menuPtr);  
                 Tcl_IncrRefCount(newCloneNamePtr);  
                 Tcl_IncrRefCount(normalPtr);  
                 CloneMenu(cascadeMenuRefPtr->menuPtr, newCloneNamePtr,  
                         normalPtr);  
   
                 newObjv[0] = menuObjPtr;  
                 newObjv[1] = newCloneNamePtr;  
                 Tcl_IncrRefCount(menuObjPtr);  
                 ConfigureMenuEntry(mePtr, 2, newObjv);  
                 Tcl_DecrRefCount(newCloneNamePtr);  
                 Tcl_DecrRefCount(pathNamePtr);  
                 Tcl_DecrRefCount(normalPtr);  
                 Tcl_DecrRefCount(menuObjPtr);  
             }  
         }  
     }  
     return TCL_OK;  
 }  
   
 /*  
  *--------------------------------------------------------------  
  *  
  * TkGetMenuIndex --  
  *  
  *      Parse a textual index into a menu and return the numerical  
  *      index of the indicated entry.  
  *  
  * Results:  
  *      A standard Tcl result.  If all went well, then *indexPtr is  
  *      filled in with the entry index corresponding to string  
  *      (ranges from -1 to the number of entries in the menu minus  
  *      one).  Otherwise an error message is left in the interp's result.  
  *  
  * Side effects:  
  *      None.  
  *  
  *--------------------------------------------------------------  
  */  
   
 int  
 TkGetMenuIndex(interp, menuPtr, objPtr, lastOK, indexPtr)  
     Tcl_Interp *interp;         /* For error messages. */  
     TkMenu *menuPtr;            /* Menu for which the index is being  
                                  * specified. */  
     Tcl_Obj *objPtr;            /* Specification of an entry in menu.  See  
                                  * manual entry for valid .*/  
     int lastOK;                 /* Non-zero means its OK to return index  
                                  * just *after* last entry. */  
     int *indexPtr;              /* Where to store converted index. */  
 {  
     int i;  
     char *string = Tcl_GetStringFromObj(objPtr, NULL);  
   
     if ((string[0] == 'a') && (strcmp(string, "active") == 0)) {  
         *indexPtr = menuPtr->active;  
         goto success;  
     }  
   
     if (((string[0] == 'l') && (strcmp(string, "last") == 0))  
             || ((string[0] == 'e') && (strcmp(string, "end") == 0))) {  
         *indexPtr = menuPtr->numEntries - ((lastOK) ? 0 : 1);  
         goto success;  
     }  
   
     if ((string[0] == 'n') && (strcmp(string, "none") == 0)) {  
         *indexPtr = -1;  
         goto success;  
     }  
   
     if (string[0] == '@') {  
         if (GetIndexFromCoords(interp, menuPtr, string, indexPtr)  
                 == TCL_OK) {  
             goto success;  
         }  
     }  
   
     if (isdigit(UCHAR(string[0]))) {  
         if (Tcl_GetInt(interp, string,  &i) == TCL_OK) {  
             if (i >= menuPtr->numEntries) {  
                 if (lastOK) {  
                     i = menuPtr->numEntries;  
                 } else {  
                     i = menuPtr->numEntries-1;  
                 }  
             } else if (i < 0) {  
                 i = -1;  
             }  
             *indexPtr = i;  
             goto success;  
         }  
         Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);  
     }  
   
     for (i = 0; i < menuPtr->numEntries; i++) {  
         Tcl_Obj *labelPtr = menuPtr->entries[i]->labelPtr;  
         char *label = (labelPtr == NULL) ? NULL  
                 : Tcl_GetStringFromObj(labelPtr, NULL);  
           
         if ((label != NULL)  
                 && (Tcl_StringMatch(label, string))) {  
             *indexPtr = i;  
             goto success;  
         }  
     }  
   
     Tcl_AppendResult(interp, "bad menu entry index \"",  
             string, "\"", (char *) NULL);  
     return TCL_ERROR;  
   
 success:  
     return TCL_OK;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * MenuCmdDeletedProc --  
  *  
  *      This procedure is invoked when a widget command is deleted.  If  
  *      the widget isn't already in the process of being destroyed,  
  *      this command destroys it.  
  *  
  * Results:  
  *      None.  
  *  
  * Side effects:  
  *      The widget is destroyed.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 static void  
 MenuCmdDeletedProc(clientData)  
     ClientData clientData;      /* Pointer to widget record for widget. */  
 {  
     TkMenu *menuPtr = (TkMenu *) clientData;  
     Tk_Window tkwin = menuPtr->tkwin;  
   
     /*  
      * This procedure could be invoked either because the window was  
      * destroyed and the command was then deleted (in which case tkwin  
      * is NULL) or because the command was deleted, and then this procedure  
      * destroys the widget.  
      */  
   
     if (tkwin != NULL) {  
         Tk_DestroyWindow(tkwin);  
     }  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * MenuNewEntry --  
  *  
  *      This procedure allocates and initializes a new menu entry.  
  *  
  * Results:  
  *      The return value is a pointer to a new menu entry structure,  
  *      which has been malloc-ed, initialized, and entered into the  
  *      entry array for the  menu.  
  *  
  * Side effects:  
  *      Storage gets allocated.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 static TkMenuEntry *  
 MenuNewEntry(menuPtr, index, type)  
     TkMenu *menuPtr;            /* Menu that will hold the new entry. */  
     int index;                  /* Where in the menu the new entry is to  
                                  * go. */  
     int type;                   /* The type of the new entry. */  
 {  
     TkMenuEntry *mePtr;  
     TkMenuEntry **newEntries;  
     int i;  
   
     /*  
      * Create a new array of entries with an empty slot for the  
      * new entry.  
      */  
   
     newEntries = (TkMenuEntry **) ckalloc((unsigned)  
             ((menuPtr->numEntries+1)*sizeof(TkMenuEntry *)));  
     for (i = 0; i < index; i++) {  
         newEntries[i] = menuPtr->entries[i];  
     }  
     for (  ; i < menuPtr->numEntries; i++) {  
         newEntries[i+1] = menuPtr->entries[i];  
         newEntries[i+1]->index = i + 1;  
     }  
     if (menuPtr->numEntries != 0) {  
         ckfree((char *) menuPtr->entries);  
     }  
     menuPtr->entries = newEntries;  
     menuPtr->numEntries++;  
     mePtr = (TkMenuEntry *) ckalloc(sizeof(TkMenuEntry));  
     menuPtr->entries[index] = mePtr;  
     mePtr->type = type;  
     mePtr->optionTable = menuPtr->optionTablesPtr->entryOptionTables[type];  
     mePtr->menuPtr = menuPtr;  
     mePtr->labelPtr = NULL;  
     mePtr->labelLength = 0;  
     mePtr->underline = -1;  
     mePtr->bitmapPtr = NULL;  
     mePtr->imagePtr = NULL;  
     mePtr->image = NULL;  
     mePtr->selectImagePtr = NULL;  
     mePtr->selectImage = NULL;  
     mePtr->accelPtr = NULL;  
     mePtr->accelLength = 0;  
     mePtr->state = ENTRY_DISABLED;  
     mePtr->borderPtr = NULL;  
     mePtr->fgPtr = NULL;  
     mePtr->activeBorderPtr = NULL;  
     mePtr->activeFgPtr = NULL;  
     mePtr->fontPtr = NULL;  
     mePtr->indicatorOn = 0;  
     mePtr->indicatorFgPtr = NULL;  
     mePtr->columnBreak = 0;  
     mePtr->hideMargin = 0;  
     mePtr->commandPtr = NULL;  
     mePtr->namePtr = NULL;  
     mePtr->childMenuRefPtr = NULL;  
     mePtr->onValuePtr = NULL;  
     mePtr->offValuePtr = NULL;  
     mePtr->entryFlags = 0;  
     mePtr->index = index;  
     mePtr->nextCascadePtr = NULL;  
     if (Tk_InitOptions(menuPtr->interp, (char *) mePtr,  
             mePtr->optionTable, menuPtr->tkwin) != TCL_OK) {  
         ckfree((char *) mePtr);  
         return NULL;  
     }  
     TkMenuInitializeEntryDrawingFields(mePtr);  
     if (TkpMenuNewEntry(mePtr) != TCL_OK) {  
         Tk_FreeConfigOptions((char *) mePtr, mePtr->optionTable,  
                 menuPtr->tkwin);  
         ckfree((char *) mePtr);  
         return NULL;  
     }  
   
     return mePtr;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * MenuAddOrInsert --  
  *  
  *      This procedure does all of the work of the "add" and "insert"  
  *      widget commands, allowing the code for these to be shared.  
  *  
  * Results:  
  *      A standard Tcl return value.  
  *  
  * Side effects:  
  *      A new menu entry is created in menuPtr.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 static int  
 MenuAddOrInsert(interp, menuPtr, indexPtr, objc, objv)  
     Tcl_Interp *interp;                 /* Used for error reporting. */  
     TkMenu *menuPtr;                    /* Widget in which to create new  
                                          * entry. */  
     Tcl_Obj *indexPtr;                  /* Object describing index at which  
                                          * to insert.  NULL means insert at  
                                          * end. */  
     int objc;                           /* Number of elements in objv. */  
     Tcl_Obj *CONST objv[];              /* Arguments to command:  first arg  
                                          * is type of entry, others are  
                                          * config options. */  
 {  
     int type, index;  
     TkMenuEntry *mePtr;  
     TkMenu *menuListPtr;  
   
     if (indexPtr != NULL) {  
         if (TkGetMenuIndex(interp, menuPtr, indexPtr, 1, &index)  
                 != TCL_OK) {  
             return TCL_ERROR;  
         }  
     } else {  
         index = menuPtr->numEntries;  
     }  
     if (index < 0) {  
         char *indexString = Tcl_GetStringFromObj(indexPtr, NULL);  
         Tcl_AppendResult(interp, "bad index \"", indexString, "\"",  
                  (char *) NULL);  
         return TCL_ERROR;  
     }  
     if (menuPtr->tearoff && (index == 0)) {  
         index = 1;  
     }  
   
     /*  
      * Figure out the type of the new entry.  
      */  
   
     if (Tcl_GetIndexFromObj(interp, objv[0], menuEntryTypeStrings,  
             "menu entry type", 0, &type) != TCL_OK) {  
         return TCL_ERROR;  
     }  
   
     /*  
      * Now we have to add an entry for every instance related to this menu.  
      */  
   
     for (menuListPtr = menuPtr->masterMenuPtr; menuListPtr != NULL;  
             menuListPtr = menuListPtr->nextInstancePtr) {  
           
         mePtr = MenuNewEntry(menuListPtr, index, type);  
         if (mePtr == NULL) {  
             return TCL_ERROR;  
         }  
         if (ConfigureMenuEntry(mePtr, objc - 1, objv + 1) != TCL_OK) {  
             TkMenu *errorMenuPtr;  
             int i;  
   
             for (errorMenuPtr = menuPtr->masterMenuPtr;  
                     errorMenuPtr != NULL;  
                     errorMenuPtr = errorMenuPtr->nextInstancePtr) {  
                 Tcl_EventuallyFree((ClientData) errorMenuPtr->entries[index],  
                         DestroyMenuEntry);  
                 for (i = index; i < errorMenuPtr->numEntries - 1; i++) {  
                     errorMenuPtr->entries[i] = errorMenuPtr->entries[i + 1];  
                     errorMenuPtr->entries[i]->index = i;  
                 }  
                 errorMenuPtr->numEntries--;  
                 if (errorMenuPtr->numEntries == 0) {  
                     ckfree((char *) errorMenuPtr->entries);  
                     errorMenuPtr->entries = NULL;  
                 }  
                 if (errorMenuPtr == menuListPtr) {  
                     break;  
                 }  
             }  
             return TCL_ERROR;  
         }  
           
         /*  
          * If a menu has cascades, then every instance of the menu has  
          * to have its own parallel cascade structure. So adding an  
          * entry to a menu with clones means that the menu that the  
          * entry points to has to be cloned for every clone the  
          * master menu has. This is special case #2 in the comment  
          * at the top of this file.  
          */  
   
         if ((menuPtr != menuListPtr) && (type == CASCADE_ENTRY)) {            
             if ((mePtr->namePtr != NULL)  
                     && (mePtr->childMenuRefPtr != NULL)  
                     && (mePtr->childMenuRefPtr->menuPtr != NULL)) {  
                 TkMenu *cascadeMenuPtr =  
                         mePtr->childMenuRefPtr->menuPtr->masterMenuPtr;  
                 Tcl_Obj *newCascadePtr;  
                 Tcl_Obj *menuNamePtr = Tcl_NewStringObj("-menu", -1);  
                 Tcl_Obj *windowNamePtr =  
                         Tcl_NewStringObj(Tk_PathName(menuListPtr->tkwin), -1);  
                 Tcl_Obj *normalPtr = Tcl_NewStringObj("normal", -1);  
                 Tcl_Obj *newObjv[2];  
                 TkMenuReferences *menuRefPtr;  
                     
                 Tcl_IncrRefCount(windowNamePtr);  
                 newCascadePtr = TkNewMenuName(menuListPtr->interp,  
                         windowNamePtr, cascadeMenuPtr);  
                 Tcl_IncrRefCount(newCascadePtr);  
                 Tcl_IncrRefCount(normalPtr);  
                 CloneMenu(cascadeMenuPtr, newCascadePtr, normalPtr);  
                   
                 menuRefPtr = TkFindMenuReferencesObj(menuListPtr->interp,  
                         newCascadePtr);  
                 if (menuRefPtr == NULL) {  
                     panic("CloneMenu failed inside of MenuAddOrInsert.");  
                 }  
                 newObjv[0] = menuNamePtr;  
                 newObjv[1] = newCascadePtr;  
                 Tcl_IncrRefCount(menuNamePtr);  
                 Tcl_IncrRefCount(newCascadePtr);  
                 ConfigureMenuEntry(mePtr, 2, newObjv);  
                 Tcl_DecrRefCount(newCascadePtr);  
                 Tcl_DecrRefCount(menuNamePtr);  
                 Tcl_DecrRefCount(windowNamePtr);  
                 Tcl_DecrRefCount(normalPtr);  
             }  
         }  
     }  
     return TCL_OK;  
 }  
   
 /*  
  *--------------------------------------------------------------  
  *  
  * MenuVarProc --  
  *  
  *      This procedure is invoked when someone changes the  
  *      state variable associated with a radiobutton or checkbutton  
  *      menu entry.  The entry's selected state is set to match  
  *      the value of the variable.  
  *  
  * Results:  
  *      NULL is always returned.  
  *  
  * Side effects:  
  *      The menu entry may become selected or deselected.  
  *  
  *--------------------------------------------------------------  
  */  
   
 static char *  
 MenuVarProc(clientData, interp, name1, name2, flags)  
     ClientData clientData;      /* Information about menu entry. */  
     Tcl_Interp *interp;         /* Interpreter containing variable. */  
     char *name1;                /* First part of variable's name. */  
     char *name2;                /* Second part of variable's name. */  
     int flags;                  /* Describes what just happened. */  
 {  
     TkMenuEntry *mePtr = (TkMenuEntry *) clientData;  
     TkMenu *menuPtr;  
     char *value;  
     char *name = Tcl_GetStringFromObj(mePtr->namePtr, NULL);  
     char *onValue;  
   
     menuPtr = mePtr->menuPtr;  
   
     /*  
      * If the variable is being unset, then re-establish the  
      * trace unless the whole interpreter is going away.  
      */  
   
     if (flags & TCL_TRACE_UNSETS) {  
         mePtr->entryFlags &= ~ENTRY_SELECTED;  
         if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {  
             Tcl_TraceVar(interp, name,  
                     TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,  
                     MenuVarProc, clientData);  
         }  
         TkpConfigureMenuEntry(mePtr);  
         TkEventuallyRedrawMenu(menuPtr, (TkMenuEntry *) NULL);  
         return (char *) NULL;  
     }  
   
     /*  
      * Use the value of the variable to update the selected status of  
      * the menu entry.  
      */  
   
     value = Tcl_GetVar(interp, name, TCL_GLOBAL_ONLY);  
     if (value == NULL) {  
         value = "";  
     }  
     if (mePtr->onValuePtr != NULL) {  
         onValue = Tcl_GetStringFromObj(mePtr->onValuePtr, NULL);  
         if (strcmp(value, onValue) == 0) {  
             if (mePtr->entryFlags & ENTRY_SELECTED) {  
                 return (char *) NULL;  
             }  
             mePtr->entryFlags |= ENTRY_SELECTED;  
         } else if (mePtr->entryFlags & ENTRY_SELECTED) {  
             mePtr->entryFlags &= ~ENTRY_SELECTED;  
         } else {  
             return (char *) NULL;  
         }  
     } else {  
         return (char *) NULL;  
     }  
     TkpConfigureMenuEntry(mePtr);  
     TkEventuallyRedrawMenu(menuPtr, mePtr);  
     return (char *) NULL;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * TkActivateMenuEntry --  
  *  
  *      This procedure is invoked to make a particular menu entry  
  *      the active one, deactivating any other entry that might  
  *      currently be active.  
  *  
  * Results:  
  *      The return value is a standard Tcl result (errors can occur  
  *      while posting and unposting submenus).  
  *  
  * Side effects:  
  *      Menu entries get redisplayed, and the active entry changes.  
  *      Submenus may get posted and unposted.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 int  
 TkActivateMenuEntry(menuPtr, index)  
     register TkMenu *menuPtr;           /* Menu in which to activate. */  
     int index;                          /* Index of entry to activate, or  
                                          * -1 to deactivate all entries. */  
 {  
     register TkMenuEntry *mePtr;  
     int result = TCL_OK;  
   
     if (menuPtr->active >= 0) {  
         mePtr = menuPtr->entries[menuPtr->active];  
   
         /*  
          * Don't change the state unless it's currently active (state  
          * might already have been changed to disabled).  
          */  
   
         if (mePtr->state == ENTRY_ACTIVE) {  
             mePtr->state = ENTRY_NORMAL;  
         }  
         TkEventuallyRedrawMenu(menuPtr, menuPtr->entries[menuPtr->active]);  
     }  
     menuPtr->active = index;  
     if (index >= 0) {  
         mePtr = menuPtr->entries[index];  
         mePtr->state = ENTRY_ACTIVE;  
         TkEventuallyRedrawMenu(menuPtr, mePtr);  
     }  
     return result;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * TkPostCommand --  
  *  
  *      Execute the postcommand for the given menu.  
  *  
  * Results:  
  *      The return value is a standard Tcl result (errors can occur  
  *      while the postcommands are being processed).  
  *  
  * Side effects:  
  *      Since commands can get executed while this routine is being executed,  
  *      the entire world can change.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 int  
 TkPostCommand(menuPtr)  
     TkMenu *menuPtr;  
 {  
     int result;  
   
     /*  
      * If there is a command for the menu, execute it.  This  
      * may change the size of the menu, so be sure to recompute  
      * the menu's geometry if needed.  
      */  
   
     if (menuPtr->postCommandPtr != NULL) {  
         Tcl_Obj *postCommandPtr = menuPtr->postCommandPtr;  
   
         Tcl_IncrRefCount(postCommandPtr);  
         result = Tcl_EvalObjEx(menuPtr->interp, postCommandPtr,  
                 TCL_EVAL_GLOBAL);  
         Tcl_DecrRefCount(postCommandPtr);  
         if (result != TCL_OK) {  
             return result;  
         }  
         TkRecomputeMenu(menuPtr);  
     }  
     return TCL_OK;  
 }  
   
 /*  
  *--------------------------------------------------------------  
  *  
  * CloneMenu --  
  *  
  *      Creates a child copy of the menu. It will be inserted into  
  *      the menu's instance chain. All attributes and entry  
  *      attributes will be duplicated.  
  *  
  * Results:  
  *      A standard Tcl result.  
  *  
  * Side effects:  
  *      Allocates storage. After the menu is created, any  
  *      configuration done with this menu or any related one  
  *      will be reflected in all of them.  
  *  
  *--------------------------------------------------------------  
  */  
   
 static int  
 CloneMenu(menuPtr, newMenuNamePtr, newMenuTypePtr)  
     TkMenu *menuPtr;            /* The menu we are going to clone */  
     Tcl_Obj *newMenuNamePtr;    /* The name to give the new menu */  
     Tcl_Obj *newMenuTypePtr;    /* What kind of menu is this, a normal menu  
                                  * a menubar, or a tearoff? */  
 {  
     int returnResult;  
     int menuType, i;  
     TkMenuReferences *menuRefPtr;  
     Tcl_Obj *menuDupCommandArray[4];  
       
     if (newMenuTypePtr == NULL) {  
         menuType = MASTER_MENU;  
     } else {  
         if (Tcl_GetIndexFromObj(menuPtr->interp, newMenuTypePtr,  
                 menuTypeStrings, "menu type", 0, &menuType) != TCL_OK) {  
             return TCL_ERROR;  
         }  
     }  
   
     menuDupCommandArray[0] = Tcl_NewStringObj("tkMenuDup", -1);  
     menuDupCommandArray[1] = Tcl_NewStringObj(Tk_PathName(menuPtr->tkwin), -1);  
     menuDupCommandArray[2] = newMenuNamePtr;  
     if (newMenuTypePtr == NULL) {  
         menuDupCommandArray[3] = Tcl_NewStringObj("normal", -1);  
     } else {  
         menuDupCommandArray[3] = newMenuTypePtr;  
     }  
     for (i = 0; i < 4; i++) {  
         Tcl_IncrRefCount(menuDupCommandArray[i]);  
     }  
     Tcl_Preserve((ClientData) menuPtr);  
     returnResult = Tcl_EvalObjv(menuPtr->interp, 4, menuDupCommandArray, 0);  
     for (i = 0; i < 4; i++) {  
         Tcl_DecrRefCount(menuDupCommandArray[i]);  
     }  
   
     /*  
      * Make sure the tcl command actually created the clone.  
      */  
       
     if ((returnResult == TCL_OK) &&  
             ((menuRefPtr = TkFindMenuReferencesObj(menuPtr->interp,  
             newMenuNamePtr)) != (TkMenuReferences *) NULL)  
             && (menuPtr->numEntries == menuRefPtr->menuPtr->numEntries)) {  
         TkMenu *newMenuPtr = menuRefPtr->menuPtr;  
         Tcl_Obj *newObjv[3];  
         char *newArgv[3];  
         int i, numElements;  
   
         /*  
          * Now put this newly created menu into the parent menu's instance  
          * chain.  
          */  
   
         if (menuPtr->nextInstancePtr == NULL) {  
             menuPtr->nextInstancePtr = newMenuPtr;  
             newMenuPtr->masterMenuPtr = menuPtr->masterMenuPtr;  
         } else {  
             TkMenu *masterMenuPtr;  
               
             masterMenuPtr = menuPtr->masterMenuPtr;  
             newMenuPtr->nextInstancePtr = masterMenuPtr->nextInstancePtr;  
             masterMenuPtr->nextInstancePtr = newMenuPtr;  
             newMenuPtr->masterMenuPtr = masterMenuPtr;  
         }  
           
         /*  
          * Add the master menu's window to the bind tags for this window  
          * after this window's tag. This is so the user can bind to either  
          * this clone (which may not be easy to do) or the entire menu  
          * clone structure.  
          */  
           
         newArgv[0] = "bindtags";  
         newArgv[1] = Tk_PathName(newMenuPtr->tkwin);  
         if (Tk_BindtagsCmd((ClientData)newMenuPtr->tkwin,  
                 newMenuPtr->interp, 2, newArgv) == TCL_OK) {  
             char *windowName;  
             Tcl_Obj *bindingsPtr =  
                     Tcl_DuplicateObj(Tcl_GetObjResult(newMenuPtr->interp));  
             Tcl_Obj *elementPtr;  
       
             Tcl_ListObjLength(newMenuPtr->interp, bindingsPtr, &numElements);  
             for (i = 0; i < numElements; i++) {  
                 Tcl_ListObjIndex(newMenuPtr->interp, bindingsPtr, i,  
                         &elementPtr);  
                 windowName = Tcl_GetStringFromObj(elementPtr, NULL);  
                 if (strcmp(windowName, Tk_PathName(newMenuPtr->tkwin))  
                         == 0) {  
                     Tcl_Obj *newElementPtr = Tcl_NewStringObj(  
                             Tk_PathName(newMenuPtr->masterMenuPtr->tkwin), -1);  
                     Tcl_IncrRefCount(newElementPtr);  
                     Tcl_ListObjReplace(menuPtr->interp, bindingsPtr,  
                             i + 1, 0, 1, &newElementPtr);  
                     newArgv[2] = Tcl_GetStringFromObj(bindingsPtr, NULL);  
                     Tk_BindtagsCmd((ClientData)newMenuPtr->tkwin,  
                             menuPtr->interp, 3, newArgv);  
                     break;  
                 }  
             }  
             Tcl_DecrRefCount(bindingsPtr);            
         }  
         Tcl_ResetResult(menuPtr->interp);  
           
         /*  
          * Clone all of the cascade menus that this menu points to.  
          */  
           
         for (i = 0; i < menuPtr->numEntries; i++) {  
             TkMenuReferences *cascadeRefPtr;  
             TkMenu *oldCascadePtr;  
               
             if ((menuPtr->entries[i]->type == CASCADE_ENTRY)  
                 && (menuPtr->entries[i]->namePtr != NULL)) {  
                 cascadeRefPtr =  
                         TkFindMenuReferencesObj(menuPtr->interp,  
                         menuPtr->entries[i]->namePtr);  
                 if ((cascadeRefPtr != NULL) && (cascadeRefPtr->menuPtr)) {  
                     Tcl_Obj *windowNamePtr =  
                             Tcl_NewStringObj(Tk_PathName(newMenuPtr->tkwin),  
                             -1);  
                     Tcl_Obj *newCascadePtr;  
                       
                     oldCascadePtr = cascadeRefPtr->menuPtr;  
   
                     Tcl_IncrRefCount(windowNamePtr);  
                     newCascadePtr = TkNewMenuName(menuPtr->interp,  
                             windowNamePtr, oldCascadePtr);  
                     Tcl_IncrRefCount(newCascadePtr);  
                     CloneMenu(oldCascadePtr, newCascadePtr, NULL);  
   
                     newObjv[0] = Tcl_NewStringObj("-menu", -1);  
                     newObjv[1] = newCascadePtr;  
                     Tcl_IncrRefCount(newObjv[0]);  
                     ConfigureMenuEntry(newMenuPtr->entries[i], 2, newObjv);  
                     Tcl_DecrRefCount(newObjv[0]);  
                     Tcl_DecrRefCount(newCascadePtr);  
                     Tcl_DecrRefCount(windowNamePtr);  
                 }  
             }  
         }  
           
         returnResult = TCL_OK;  
     } else {  
         returnResult = TCL_ERROR;  
     }  
     Tcl_Release((ClientData) menuPtr);  
     return returnResult;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * MenuDoYPosition --  
  *  
  *      Given arguments from an option command line, returns the Y position.  
  *  
  * Results:  
  *      Returns TCL_OK or TCL_Error  
  *  
  * Side effects:  
  *      yPosition is set to the Y-position of the menu entry.  
  *  
  *----------------------------------------------------------------------  
  */  
       
 static int  
 MenuDoYPosition(interp, menuPtr, objPtr)  
     Tcl_Interp *interp;  
     TkMenu *menuPtr;  
     Tcl_Obj *objPtr;  
 {  
     int index;  
       
     TkRecomputeMenu(menuPtr);  
     if (TkGetMenuIndex(interp, menuPtr, objPtr, 0, &index) != TCL_OK) {  
         goto error;  
     }  
     Tcl_ResetResult(interp);  
     if (index < 0) {  
         Tcl_SetObjResult(interp, Tcl_NewIntObj(0));  
     } else {  
         Tcl_SetObjResult(interp, Tcl_NewIntObj(menuPtr->entries[index]->y));  
     }  
   
     return TCL_OK;  
       
 error:  
     return TCL_ERROR;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * GetIndexFromCoords --  
  *  
  *      Given a string of the form "@int", return the menu item corresponding  
  *      to int.  
  *  
  * Results:  
  *      If int is a valid number, *indexPtr will be the number of the menuentry  
  *      that is the correct height. If int is invaled, *indexPtr will be  
  *      unchanged. Returns appropriate Tcl error number.  
  *  
  * Side effects:  
  *      If int is invalid, interp's result will set to NULL.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 static int  
 GetIndexFromCoords(interp, menuPtr, string, indexPtr)  
     Tcl_Interp *interp;         /* interp of menu */  
     TkMenu *menuPtr;            /* the menu we are searching */  
     char *string;               /* The @string we are parsing */  
     int *indexPtr;              /* The index of the item that matches */  
 {  
     int x, y, i;  
     char *p, *end;  
       
     TkRecomputeMenu(menuPtr);  
     p = string + 1;  
     y = strtol(p, &end, 0);  
     if (end == p) {  
         goto error;  
     }  
     if (*end == ',') {  
         x = y;  
         p = end + 1;  
         y = strtol(p, &end, 0);  
         if (end == p) {  
             goto error;  
         }  
     } else {  
         Tk_GetPixelsFromObj(interp, menuPtr->tkwin,  
                 menuPtr->borderWidthPtr, &x);  
     }  
       
     for (i = 0; i < menuPtr->numEntries; i++) {  
         if ((x >= menuPtr->entries[i]->x) && (y >= menuPtr->entries[i]->y)  
                 && (x < (menuPtr->entries[i]->x + menuPtr->entries[i]->width))  
                 && (y < (menuPtr->entries[i]->y  
                 + menuPtr->entries[i]->height))) {  
             break;  
         }  
     }  
     if (i >= menuPtr->numEntries) {  
         /* i = menuPtr->numEntries - 1; */  
         i = -1;  
     }  
     *indexPtr = i;  
     return TCL_OK;  
   
     error:  
     Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);  
     return TCL_ERROR;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * RecursivelyDeleteMenu --  
  *  
  *      Deletes a menu and any cascades underneath it. Used for deleting  
  *      instances when a menu is no longer being used as a menubar,  
  *      for instance.  
  *  
  * Results:  
  *      None.  
  *  
  * Side effects:  
  *      Destroys the menu and all cascade menus underneath it.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 static void  
 RecursivelyDeleteMenu(menuPtr)  
     TkMenu *menuPtr;            /* The menubar instance we are deleting */  
 {  
     int i;  
     TkMenuEntry *mePtr;  
       
     for (i = 0; i < menuPtr->numEntries; i++) {  
         mePtr = menuPtr->entries[i];  
         if ((mePtr->type == CASCADE_ENTRY)  
                 && (mePtr->childMenuRefPtr != NULL)  
                 && (mePtr->childMenuRefPtr->menuPtr != NULL)) {  
             RecursivelyDeleteMenu(mePtr->childMenuRefPtr->menuPtr);  
         }  
     }  
     Tk_DestroyWindow(menuPtr->tkwin);  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * TkNewMenuName --  
  *  
  *      Makes a new unique name for a cloned menu. Will be a child  
  *      of oldName.  
  *  
  * Results:  
  *      Returns a char * which has been allocated; caller must free.  
  *  
  * Side effects:  
  *      Memory is allocated.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 Tcl_Obj *  
 TkNewMenuName(interp, parentPtr, menuPtr)  
     Tcl_Interp *interp;         /* The interp the new name has to live in.*/  
     Tcl_Obj *parentPtr;         /* The prefix path of the new name. */  
     TkMenu *menuPtr;            /* The menu we are cloning. */  
 {  
     Tcl_Obj *resultPtr = NULL;  /* Initialization needed only to prevent  
                                  * compiler warning. */  
     Tcl_Obj *childPtr;  
     char *destString;  
     int i;  
     int doDot;  
     Tcl_CmdInfo cmdInfo;  
     Tcl_HashTable *nameTablePtr = NULL;  
     TkWindow *winPtr = (TkWindow *) menuPtr->tkwin;  
     char *parentName = Tcl_GetStringFromObj(parentPtr, NULL);  
   
     if (winPtr->mainPtr != NULL) {  
         nameTablePtr = &(winPtr->mainPtr->nameTable);  
     }  
   
     doDot = parentName[strlen(parentName) - 1] != '.';  
   
     childPtr = Tcl_NewStringObj(Tk_PathName(menuPtr->tkwin), -1);  
     for (destString = Tcl_GetStringFromObj(childPtr, NULL);  
             *destString != '\0'; destString++) {  
         if (*destString == '.') {  
             *destString = '#';  
         }  
     }  
       
     for (i = 0; ; i++) {  
         if (i == 0) {  
             resultPtr = Tcl_DuplicateObj(parentPtr);  
             if (doDot) {  
                 Tcl_AppendToObj(resultPtr, ".", -1);  
             }  
             Tcl_AppendObjToObj(resultPtr, childPtr);  
         } else {  
             Tcl_Obj *intPtr;  
   
             Tcl_DecrRefCount(resultPtr);  
             resultPtr = Tcl_DuplicateObj(parentPtr);  
             if (doDot) {  
                 Tcl_AppendToObj(resultPtr, ".", -1);  
             }  
             Tcl_AppendObjToObj(resultPtr, childPtr);  
             intPtr = Tcl_NewIntObj(i);  
             Tcl_AppendObjToObj(resultPtr, intPtr);  
             Tcl_DecrRefCount(intPtr);  
         }  
         destString = Tcl_GetStringFromObj(resultPtr, NULL);  
         if ((Tcl_GetCommandInfo(interp, destString, &cmdInfo) == 0)  
                 && ((nameTablePtr == NULL)  
                 || (Tcl_FindHashEntry(nameTablePtr, destString) == NULL))) {  
             break;  
         }  
     }  
     Tcl_DecrRefCount(childPtr);  
     return resultPtr;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * TkSetWindowMenuBar --  
  *  
  *      Associates a menu with a window. Called by ConfigureFrame in  
  *      in response to a "-menu .foo" configuration option for a top  
  *      level.  
  *  
  * Results:  
  *      None.  
  *  
  * Side effects:  
  *      The old menu clones for the menubar are thrown away, and a  
  *      handler is set up to allocate the new ones.  
  *  
  *----------------------------------------------------------------------  
  */  
 void  
 TkSetWindowMenuBar(interp, tkwin, oldMenuName, menuName)  
     Tcl_Interp *interp;         /* The interpreter the toplevel lives in. */  
     Tk_Window tkwin;            /* The toplevel window */  
     char *oldMenuName;          /* The name of the menubar previously set in  
                                  * this toplevel. NULL means no menu was  
                                  * set previously. */  
     char *menuName;             /* The name of the new menubar that the  
                                  * toplevel needs to be set to. NULL means  
                                  * that their is no menu now. */  
 {  
     TkMenuTopLevelList *topLevelListPtr, *prevTopLevelPtr;  
     TkMenu *menuPtr;  
     TkMenuReferences *menuRefPtr;  
       
     TkMenuInit();  
   
     /*  
      * Destroy the menubar instances of the old menu. Take this window  
      * out of the old menu's top level reference list.  
      */  
       
     if (oldMenuName != NULL) {  
         menuRefPtr = TkFindMenuReferences(interp, oldMenuName);  
         if (menuRefPtr != NULL) {  
   
             /*  
              * Find the menubar instance that is to be removed. Destroy  
              * it and all of the cascades underneath it.  
              */  
   
             if (menuRefPtr->menuPtr != NULL) {                
                 TkMenu *instancePtr;  
   
                 menuPtr = menuRefPtr->menuPtr;  
                               
                 for (instancePtr = menuPtr->masterMenuPtr;  
                         instancePtr != NULL;  
                         instancePtr = instancePtr->nextInstancePtr) {  
                     if (instancePtr->menuType == MENUBAR  
                             && instancePtr->parentTopLevelPtr == tkwin) {  
                         RecursivelyDeleteMenu(instancePtr);  
                         break;  
                     }  
                 }  
             }  
   
             /*  
              * Now we need to remove this toplevel from the list of toplevels  
              * that reference this menu.  
              */  
   
             for (topLevelListPtr = menuRefPtr->topLevelListPtr,  
                     prevTopLevelPtr = NULL;  
                     (topLevelListPtr != NULL)  
                     && (topLevelListPtr->tkwin != tkwin);  
                     prevTopLevelPtr = topLevelListPtr,  
                     topLevelListPtr = topLevelListPtr->nextPtr) {  
   
                 /*  
                  * Empty loop body.  
                  */  
                   
             }  
   
             /*  
              * Now we have found the toplevel reference that matches the  
              * tkwin; remove this reference from the list.  
              */  
   
             if (topLevelListPtr != NULL) {  
                 if (prevTopLevelPtr == NULL) {  
                     menuRefPtr->topLevelListPtr =  
                             menuRefPtr->topLevelListPtr->nextPtr;  
                 } else {  
                     prevTopLevelPtr->nextPtr = topLevelListPtr->nextPtr;  
                 }  
                 ckfree((char *) topLevelListPtr);  
                 TkFreeMenuReferences(menuRefPtr);  
             }  
         }  
     }  
   
     /*  
      * Now, add the clone references for the new menu.  
      */  
       
     if (menuName != NULL && menuName[0] != 0) {  
         TkMenu *menuBarPtr = NULL;  
   
         menuRefPtr = TkCreateMenuReferences(interp, menuName);            
           
         menuPtr = menuRefPtr->menuPtr;  
         if (menuPtr != NULL) {  
             Tcl_Obj *cloneMenuPtr;  
             TkMenuReferences *cloneMenuRefPtr;  
             Tcl_Obj *newObjv[4];  
             Tcl_Obj *windowNamePtr = Tcl_NewStringObj(Tk_PathName(tkwin),  
                     -1);  
             Tcl_Obj *menubarPtr = Tcl_NewStringObj("menubar", -1);  
           
             /*  
              * Clone the menu and all of the cascades underneath it.  
              */  
   
             Tcl_IncrRefCount(windowNamePtr);  
             cloneMenuPtr = TkNewMenuName(interp, windowNamePtr,  
                     menuPtr);  
             Tcl_IncrRefCount(cloneMenuPtr);  
             Tcl_IncrRefCount(menubarPtr);  
             CloneMenu(menuPtr, cloneMenuPtr, menubarPtr);  
               
             cloneMenuRefPtr = TkFindMenuReferencesObj(interp, cloneMenuPtr);  
             if ((cloneMenuRefPtr != NULL)  
                     && (cloneMenuRefPtr->menuPtr != NULL)) {  
                 Tcl_Obj *cursorPtr = Tcl_NewStringObj("-cursor", -1);  
                 Tcl_Obj *nullPtr = Tcl_NewObj();  
                 cloneMenuRefPtr->menuPtr->parentTopLevelPtr = tkwin;  
                 menuBarPtr = cloneMenuRefPtr->menuPtr;  
                 newObjv[0] = cursorPtr;  
                 newObjv[1] = nullPtr;  
                 Tcl_IncrRefCount(cursorPtr);  
                 Tcl_IncrRefCount(nullPtr);  
                 ConfigureMenu(menuPtr->interp, cloneMenuRefPtr->menuPtr,  
                         2, newObjv);  
                 Tcl_DecrRefCount(cursorPtr);  
                 Tcl_DecrRefCount(nullPtr);  
             }  
   
             TkpSetWindowMenuBar(tkwin, menuBarPtr);  
             Tcl_DecrRefCount(cloneMenuPtr);  
             Tcl_DecrRefCount(menubarPtr);  
             Tcl_DecrRefCount(windowNamePtr);  
         } else {  
             TkpSetWindowMenuBar(tkwin, NULL);  
         }  
   
           
         /*  
          * Add this window to the menu's list of windows that refer  
          * to this menu.  
          */  
   
         topLevelListPtr = (TkMenuTopLevelList *)  
                 ckalloc(sizeof(TkMenuTopLevelList));  
         topLevelListPtr->tkwin = tkwin;  
         topLevelListPtr->nextPtr = menuRefPtr->topLevelListPtr;  
         menuRefPtr->topLevelListPtr = topLevelListPtr;  
     } else {  
         TkpSetWindowMenuBar(tkwin, NULL);  
     }  
     TkpSetMainMenubar(interp, tkwin, menuName);  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * DestroyMenuHashTable --  
  *  
  *      Called when an interp is deleted and a menu hash table has  
  *      been set in it.  
  *  
  * Results:  
  *      None.  
  *  
  * Side effects:  
  *      The hash table is destroyed.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 static void  
 DestroyMenuHashTable(clientData, interp)  
     ClientData clientData;      /* The menu hash table we are destroying */  
     Tcl_Interp *interp;         /* The interpreter we are destroying */  
 {  
     Tcl_DeleteHashTable((Tcl_HashTable *) clientData);  
     ckfree((char *) clientData);  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * TkGetMenuHashTable --  
  *  
  *      For a given interp, give back the menu hash table that goes with  
  *      it. If the hash table does not exist, it is created.  
  *  
  * Results:  
  *      Returns a hash table pointer.  
  *  
  * Side effects:  
  *      A new hash table is created if there were no table in the interp  
  *      originally.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 Tcl_HashTable *  
 TkGetMenuHashTable(interp)  
     Tcl_Interp *interp;         /* The interp we need the hash table in.*/  
 {  
     Tcl_HashTable *menuTablePtr;  
   
     menuTablePtr = (Tcl_HashTable *) Tcl_GetAssocData(interp, MENU_HASH_KEY,  
             NULL);  
     if (menuTablePtr == NULL) {  
         menuTablePtr = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));  
         Tcl_InitHashTable(menuTablePtr, TCL_STRING_KEYS);  
         Tcl_SetAssocData(interp, MENU_HASH_KEY, DestroyMenuHashTable,  
                 (ClientData) menuTablePtr);  
     }  
     return menuTablePtr;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * TkCreateMenuReferences --  
  *  
  *      Given a pathname, gives back a pointer to a TkMenuReferences structure.  
  *      If a reference is not already in the hash table, one is created.  
  *  
  * Results:  
  *      Returns a pointer to a menu reference structure. Should not  
  *      be freed by calller; when a field of the reference is cleared,  
  *      TkFreeMenuReferences should be called.  
  *  
  * Side effects:  
  *      A new hash table entry is created if there were no references  
  *      to the menu originally.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 TkMenuReferences *  
 TkCreateMenuReferences(interp, pathName)  
     Tcl_Interp *interp;  
     char *pathName;             /* The path of the menu widget */  
 {  
     Tcl_HashEntry *hashEntryPtr;  
     TkMenuReferences *menuRefPtr;  
     int newEntry;  
     Tcl_HashTable *menuTablePtr = TkGetMenuHashTable(interp);  
   
     hashEntryPtr = Tcl_CreateHashEntry(menuTablePtr, pathName, &newEntry);  
     if (newEntry) {  
         menuRefPtr = (TkMenuReferences *) ckalloc(sizeof(TkMenuReferences));  
         menuRefPtr->menuPtr = NULL;  
         menuRefPtr->topLevelListPtr = NULL;  
         menuRefPtr->parentEntryPtr = NULL;  
         menuRefPtr->hashEntryPtr = hashEntryPtr;  
         Tcl_SetHashValue(hashEntryPtr, (char *) menuRefPtr);  
     } else {  
         menuRefPtr = (TkMenuReferences *) Tcl_GetHashValue(hashEntryPtr);  
     }  
     return menuRefPtr;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * TkFindMenuReferences --  
  *  
  *      Given a pathname, gives back a pointer to the TkMenuReferences  
  *      structure.  
  *  
  * Results:  
  *      Returns a pointer to a menu reference structure. Should not  
  *      be freed by calller; when a field of the reference is cleared,  
  *      TkFreeMenuReferences should be called. Returns NULL if no reference  
  *      with this pathname exists.  
  *  
  * Side effects:  
  *      None.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 TkMenuReferences *  
 TkFindMenuReferences(interp, pathName)  
     Tcl_Interp *interp;         /* The interp the menu is living in. */  
     char *pathName;             /* The path of the menu widget */  
 {  
     Tcl_HashEntry *hashEntryPtr;  
     TkMenuReferences *menuRefPtr = NULL;  
     Tcl_HashTable *menuTablePtr;  
   
     menuTablePtr = TkGetMenuHashTable(interp);  
     hashEntryPtr = Tcl_FindHashEntry(menuTablePtr, pathName);  
     if (hashEntryPtr != NULL) {  
         menuRefPtr = (TkMenuReferences *) Tcl_GetHashValue(hashEntryPtr);  
     }  
     return menuRefPtr;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * TkFindMenuReferencesObj --  
  *  
  *      Given a pathname, gives back a pointer to the TkMenuReferences  
  *      structure.  
  *  
  * Results:  
  *      Returns a pointer to a menu reference structure. Should not  
  *      be freed by calller; when a field of the reference is cleared,  
  *      TkFreeMenuReferences should be called. Returns NULL if no reference  
  *      with this pathname exists.  
  *  
  * Side effects:  
  *      None.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 TkMenuReferences *  
 TkFindMenuReferencesObj(interp, objPtr)  
     Tcl_Interp *interp;         /* The interp the menu is living in. */  
     Tcl_Obj *objPtr;            /* The path of the menu widget */  
 {  
     char *pathName = Tcl_GetStringFromObj(objPtr, NULL);  
     return TkFindMenuReferences(interp, pathName);  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * TkFreeMenuReferences --  
  *  
  *      This is called after one of the fields in a menu reference  
  *      is cleared. It cleans up the ref if it is now empty.  
  *  
  * Results:  
  *      None.  
  *  
  * Side effects:  
  *      If this is the last field to be cleared, the menu ref is  
  *      taken out of the hash table.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 void  
 TkFreeMenuReferences(menuRefPtr)  
     TkMenuReferences *menuRefPtr;               /* The menu reference to  
                                                  * free */  
 {  
     if ((menuRefPtr->menuPtr == NULL)  
             && (menuRefPtr->parentEntryPtr == NULL)  
             && (menuRefPtr->topLevelListPtr == NULL)) {  
         Tcl_DeleteHashEntry(menuRefPtr->hashEntryPtr);  
         ckfree((char *) menuRefPtr);  
     }  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * DeleteMenuCloneEntries --  
  *  
  *      For every clone in this clone chain, delete the menu entries  
  *      given by the parameters.  
  *  
  * Results:  
  *      None.  
  *  
  * Side effects:  
  *      The appropriate entries are deleted from all clones of this menu.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 static void  
 DeleteMenuCloneEntries(menuPtr, first, last)  
     TkMenu *menuPtr;                /* the menu the command was issued with */  
     int first;                      /* the zero-based first entry in the set  
                                      * of entries to delete. */  
     int last;                       /* the zero-based last entry */  
 {  
   
     TkMenu *menuListPtr;  
     int numDeleted, i;  
   
     numDeleted = last + 1 - first;  
     for (menuListPtr = menuPtr->masterMenuPtr; menuListPtr != NULL;  
             menuListPtr = menuListPtr->nextInstancePtr) {  
         for (i = last; i >= first; i--) {  
             Tcl_EventuallyFree((ClientData) menuListPtr->entries[i],  
                     DestroyMenuEntry);  
         }  
         for (i = last + 1; i < menuListPtr->numEntries; i++) {  
             menuListPtr->entries[i - numDeleted] = menuListPtr->entries[i];  
             menuListPtr->entries[i - numDeleted]->index = i;  
         }  
         menuListPtr->numEntries -= numDeleted;  
         if (menuListPtr->numEntries == 0) {  
             ckfree((char *) menuListPtr->entries);  
             menuListPtr->entries = NULL;  
         }  
         if ((menuListPtr->active >= first)  
                 && (menuListPtr->active <= last)) {  
             menuListPtr->active = -1;  
         } else if (menuListPtr->active > last) {  
             menuListPtr->active -= numDeleted;  
         }  
         TkEventuallyRecomputeMenu(menuListPtr);  
     }  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * TkMenuInit --  
  *  
  *      Sets up the hash tables and the variables used by the menu package.  
  *  
  * Results:  
  *      None.  
  *  
  * Side effects:  
  *      lastMenuID gets initialized, and the parent hash and the command hash  
  *      are allocated.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 void  
 TkMenuInit()  
 {  
     ThreadSpecificData *tsdPtr = (ThreadSpecificData *)  
             Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));  
       
     if (!menusInitialized) {  
         Tcl_MutexLock(&menuMutex);  
         if (!menusInitialized) {  
             TkpMenuInit();  
             menusInitialized = 1;  
         }  
         Tcl_MutexUnlock(&menuMutex);  
     }  
     if (!tsdPtr->menusInitialized) {  
         TkpMenuThreadInit();  
         tsdPtr->menusInitialized = 1;  
     }  
 }  
   
   
 /* $History: tkMenu.c $  
  *  
  * *****************  Version 1  *****************  
  * User: Dtashley     Date: 1/02/01    Time: 2:58a  
  * Created in $/IjuScripter, IjuConsole/Source/Tk Base  
  * Initial check-in.  
  */  
   
 /* End of TKMENU.C */  
1    /* $Header$ */
2    
3    /*
4     * tkMenu.c --
5     *
6     * This file contains most of the code for implementing menus in Tk. It takes
7     * care of all of the generic (platform-independent) parts of menus, and
8     * is supplemented by platform-specific files. The geometry calculation
9     * and drawing code for menus is in the file tkMenuDraw.c
10     *
11     * Copyright (c) 1990-1994 The Regents of the University of California.
12     * Copyright (c) 1994-1998 Sun Microsystems, Inc.
13     *
14     * See the file "license.terms" for information on usage and redistribution
15     * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
16     *
17     * RCS: @(#) $Id: tkmenu.c,v 1.1.1.1 2001/06/13 05:05:37 dtashley Exp $
18     */
19    
20    /*
21     * Notes on implementation of menus:
22     *
23     * Menus can be used in three ways:
24     * - as a popup menu, either as part of a menubutton or standalone.
25     * - as a menubar. The menu's cascade items are arranged according to
26     * the specific platform to provide the user access to the menus at all
27     * times
28     * - as a tearoff palette. This is a window with the menu's items in it.
29     *
30     * The goal is to provide the Tk developer with a way to use a common
31     * set of menus for all of these tasks.
32     *
33     * In order to make the bindings for cascade menus work properly under Unix,
34     * the cascade menus' pathnames must be proper children of the menu that
35     * they are cascade from. So if there is a menu .m, and it has two
36     * cascades labelled "File" and "Edit", the cascade menus might have
37     * the pathnames .m.file and .m.edit. Another constraint is that the menus
38     * used for menubars must be children of the toplevel widget that they
39     * are attached to. And on the Macintosh, the platform specific menu handle
40     * for cascades attached to a menu bar must have a title that matches the
41     * label for the cascade menu.
42     *
43     * To handle all of the constraints, Tk menubars and tearoff menus are
44     * implemented using menu clones. Menu clones are full menus in their own
45     * right; they have a Tk window and pathname associated with them; they have
46     * a TkMenu structure and array of entries. However, they are linked with the
47     * original menu that they were cloned from. The reflect the attributes of
48     * the original, or "master", menu. So if an item is added to a menu, and
49     * that menu has clones, then the item must be added to all of its clones
50     * also. Menus are cloned when a menu is torn-off or when a menu is assigned
51     * as a menubar using the "-menu" option of the toplevel's pathname configure
52     * subcommand. When a clone is destroyed, only the clone is destroyed, but
53     * when the master menu is destroyed, all clones are also destroyed. This
54     * allows the developer to just deal with one set of menus when creating
55     * and destroying.
56     *
57     * Clones are rather tricky when a menu with cascade entries is cloned (such
58     * as a menubar). Not only does the menu have to be cloned, but each cascade
59     * entry's corresponding menu must also be cloned. This maintains the pathname
60     * parent-child hierarchy necessary for menubars and toplevels to work.
61     * This leads to several special cases:
62     *
63     * 1. When a new menu is created, and it is pointed to by cascade entries in
64     * cloned menus, the new menu has to be cloned to parallel the cascade
65     * structure.
66     * 2. When a cascade item is added to a menu that has been cloned, and the
67     * menu that the cascade item points to exists, that menu has to be cloned.
68     * 3. When the menu that a cascade entry points to is changed, the old
69     * cloned cascade menu has to be discarded, and the new one has to be cloned.
70     *
71     */
72    
73    #if 0
74    
75    /*
76     * used only to test for old config code
77     */
78    
79    #define __NO_OLD_CONFIG
80    #endif
81    
82    #include "tkPort.h"
83    #include "tkMenu.h"
84    
85    #define MENU_HASH_KEY "tkMenus"
86    
87    typedef struct ThreadSpecificData {
88        int menusInitialized;       /* Flag indicates whether thread-specific
89                                     * elements of the Windows Menu module
90                                     * have been initialized. */
91    } ThreadSpecificData;
92    static Tcl_ThreadDataKey dataKey;
93    
94    /*
95     * The following flag indicates whether the process-wide state for
96     * the Menu module has been intialized.  The Mutex protects access to
97     * that flag.
98     */
99    
100    static int menusInitialized;
101    TCL_DECLARE_MUTEX(menuMutex)
102    
103    /*
104     * Configuration specs for individual menu entries. If this changes, be sure
105     * to update code in TkpMenuInit that changes the font string entry.
106     */
107    
108    char *tkMenuStateStrings[] = {"active", "normal", "disabled", (char *) NULL};
109    
110    static char *menuEntryTypeStrings[] = {"cascade", "checkbutton", "command",
111            "radiobutton", "separator", (char *) NULL};
112    
113    Tk_OptionSpec tkBasicMenuEntryConfigSpecs[] = {
114        {TK_OPTION_BORDER, "-activebackground", (char *) NULL, (char *) NULL,
115            DEF_MENU_ENTRY_ACTIVE_BG, Tk_Offset(TkMenuEntry, activeBorderPtr), -1,
116            TK_OPTION_NULL_OK},
117        {TK_OPTION_COLOR, "-activeforeground", (char *) NULL, (char *) NULL,
118            DEF_MENU_ENTRY_ACTIVE_FG,
119            Tk_Offset(TkMenuEntry, activeFgPtr), -1, TK_OPTION_NULL_OK},
120        {TK_OPTION_STRING, "-accelerator", (char *) NULL, (char *) NULL,
121            DEF_MENU_ENTRY_ACCELERATOR,
122            Tk_Offset(TkMenuEntry, accelPtr), -1, TK_OPTION_NULL_OK},
123        {TK_OPTION_BORDER, "-background", (char *) NULL, (char *) NULL,
124            DEF_MENU_ENTRY_BG,
125            Tk_Offset(TkMenuEntry, borderPtr), -1, TK_OPTION_NULL_OK},
126        {TK_OPTION_BITMAP, "-bitmap", (char *) NULL, (char *) NULL,
127            DEF_MENU_ENTRY_BITMAP,
128            Tk_Offset(TkMenuEntry, bitmapPtr), -1, TK_OPTION_NULL_OK},
129        {TK_OPTION_BOOLEAN, "-columnbreak", (char *) NULL, (char *) NULL,
130            DEF_MENU_ENTRY_COLUMN_BREAK,
131            -1, Tk_Offset(TkMenuEntry, columnBreak)},
132        {TK_OPTION_STRING, "-command", (char *) NULL, (char *) NULL,
133            DEF_MENU_ENTRY_COMMAND,
134            Tk_Offset(TkMenuEntry, commandPtr), -1, TK_OPTION_NULL_OK},
135        {TK_OPTION_FONT, "-font", (char *) NULL, (char *) NULL,
136            DEF_MENU_ENTRY_FONT,
137            Tk_Offset(TkMenuEntry, fontPtr), -1, TK_OPTION_NULL_OK},
138        {TK_OPTION_COLOR, "-foreground", (char *) NULL, (char *) NULL,
139            DEF_MENU_ENTRY_FG,
140            Tk_Offset(TkMenuEntry, fgPtr), -1, TK_OPTION_NULL_OK},
141        {TK_OPTION_BOOLEAN, "-hidemargin", (char *) NULL, (char *) NULL,
142            DEF_MENU_ENTRY_HIDE_MARGIN,
143            -1, Tk_Offset(TkMenuEntry, hideMargin)},
144        {TK_OPTION_STRING, "-image", (char *) NULL, (char *) NULL,
145            DEF_MENU_ENTRY_IMAGE,
146            Tk_Offset(TkMenuEntry, imagePtr), -1, TK_OPTION_NULL_OK},
147        {TK_OPTION_STRING, "-label", (char *) NULL, (char *) NULL,
148            DEF_MENU_ENTRY_LABEL,
149            Tk_Offset(TkMenuEntry, labelPtr), -1, 0},
150        {TK_OPTION_STRING_TABLE, "-state", (char *) NULL, (char *) NULL,
151            DEF_MENU_ENTRY_STATE,
152            -1, Tk_Offset(TkMenuEntry, state), 0,
153            (ClientData) tkMenuStateStrings},
154        {TK_OPTION_INT, "-underline", (char *) NULL, (char *) NULL,
155            DEF_MENU_ENTRY_UNDERLINE, -1, Tk_Offset(TkMenuEntry, underline)},
156        {TK_OPTION_END}
157    };
158    
159    Tk_OptionSpec tkSeparatorEntryConfigSpecs[] = {
160        {TK_OPTION_BORDER, "-background", (char *) NULL, (char *) NULL,
161            DEF_MENU_ENTRY_BG,
162            Tk_Offset(TkMenuEntry, borderPtr), -1, TK_OPTION_NULL_OK},
163        {TK_OPTION_END}
164    };
165    
166    Tk_OptionSpec tkCheckButtonEntryConfigSpecs[] = {
167        {TK_OPTION_BOOLEAN, "-indicatoron", (char *) NULL, (char *) NULL,
168            DEF_MENU_ENTRY_INDICATOR,
169            -1, Tk_Offset(TkMenuEntry, indicatorOn)},
170        {TK_OPTION_STRING, "-offvalue", (char *) NULL, (char *) NULL,
171            DEF_MENU_ENTRY_OFF_VALUE,
172            Tk_Offset(TkMenuEntry, offValuePtr), -1},
173        {TK_OPTION_STRING, "-onvalue", (char *) NULL, (char *) NULL,
174            DEF_MENU_ENTRY_ON_VALUE,
175            Tk_Offset(TkMenuEntry, onValuePtr), -1},
176        {TK_OPTION_COLOR, "-selectcolor", (char *) NULL, (char *) NULL,
177            DEF_MENU_ENTRY_SELECT,
178            Tk_Offset(TkMenuEntry, indicatorFgPtr), -1, TK_OPTION_NULL_OK},
179        {TK_OPTION_STRING, "-selectimage", (char *) NULL, (char *) NULL,
180            DEF_MENU_ENTRY_SELECT_IMAGE,
181            Tk_Offset(TkMenuEntry, selectImagePtr), -1, TK_OPTION_NULL_OK},
182        {TK_OPTION_STRING, "-variable", (char *) NULL, (char *) NULL,
183            DEF_MENU_ENTRY_CHECK_VARIABLE,
184            Tk_Offset(TkMenuEntry, namePtr), -1, TK_OPTION_NULL_OK},
185        {TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL,
186            (char *) NULL, 0, -1, 0, (ClientData) tkBasicMenuEntryConfigSpecs}
187    };
188    
189    Tk_OptionSpec tkRadioButtonEntryConfigSpecs[] = {
190        {TK_OPTION_BOOLEAN, "-indicatoron", (char *) NULL, (char *) NULL,
191            DEF_MENU_ENTRY_INDICATOR,
192            -1, Tk_Offset(TkMenuEntry, indicatorOn)},
193        {TK_OPTION_COLOR, "-selectcolor", (char *) NULL, (char *) NULL,
194            DEF_MENU_ENTRY_SELECT,
195            Tk_Offset(TkMenuEntry, indicatorFgPtr), -1, TK_OPTION_NULL_OK},
196        {TK_OPTION_STRING, "-selectimage", (char *) NULL, (char *) NULL,
197            DEF_MENU_ENTRY_SELECT_IMAGE,
198            Tk_Offset(TkMenuEntry, selectImagePtr), -1, TK_OPTION_NULL_OK},
199        {TK_OPTION_STRING, "-value", (char *) NULL, (char *) NULL,
200            DEF_MENU_ENTRY_VALUE,
201            Tk_Offset(TkMenuEntry, onValuePtr), -1, TK_OPTION_NULL_OK},
202        {TK_OPTION_STRING, "-variable", (char *) NULL, (char *) NULL,
203            DEF_MENU_ENTRY_RADIO_VARIABLE,
204            Tk_Offset(TkMenuEntry, namePtr), -1, 0},
205        {TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL,
206            (char *) NULL, 0, -1, 0, (ClientData) tkBasicMenuEntryConfigSpecs}
207    };
208    
209    Tk_OptionSpec tkCascadeEntryConfigSpecs[] = {
210        {TK_OPTION_STRING, "-menu", (char *) NULL, (char *) NULL,
211            DEF_MENU_ENTRY_MENU,
212            Tk_Offset(TkMenuEntry, namePtr), -1, TK_OPTION_NULL_OK},
213        {TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL,
214            (char *) NULL, 0, -1, 0, (ClientData) tkBasicMenuEntryConfigSpecs}
215    };
216    
217    Tk_OptionSpec tkTearoffEntryConfigSpecs[] = {
218        {TK_OPTION_BORDER, "-background", (char *) NULL, (char *) NULL,
219            DEF_MENU_ENTRY_BG,
220            Tk_Offset(TkMenuEntry, borderPtr), -1, TK_OPTION_NULL_OK},
221        {TK_OPTION_STRING_TABLE, "-state", (char *) NULL, (char *) NULL,
222            DEF_MENU_ENTRY_STATE, -1, Tk_Offset(TkMenuEntry, state), 0,
223            (ClientData) tkMenuStateStrings},
224        {TK_OPTION_END}
225    };
226    
227    static Tk_OptionSpec *specsArray[] = {
228        tkCascadeEntryConfigSpecs, tkCheckButtonEntryConfigSpecs,
229        tkBasicMenuEntryConfigSpecs, tkRadioButtonEntryConfigSpecs,
230        tkSeparatorEntryConfigSpecs, tkTearoffEntryConfigSpecs};
231        
232    /*
233     * Menu type strings for use with Tcl_GetIndexFromObj.
234     */
235    
236    static char *menuTypeStrings[] = {"normal", "tearoff", "menubar",
237            (char *) NULL};
238    
239    Tk_OptionSpec tkMenuConfigSpecs[] = {
240        {TK_OPTION_BORDER, "-activebackground", "activeBackground",
241            "Foreground", DEF_MENU_ACTIVE_BG_COLOR,
242            Tk_Offset(TkMenu, activeBorderPtr), -1, 0,
243            (ClientData) DEF_MENU_ACTIVE_BG_MONO},
244        {TK_OPTION_PIXELS, "-activeborderwidth", "activeBorderWidth",
245            "BorderWidth", DEF_MENU_ACTIVE_BORDER_WIDTH,
246            Tk_Offset(TkMenu, activeBorderWidthPtr), -1},
247        {TK_OPTION_COLOR, "-activeforeground", "activeForeground",
248            "Background", DEF_MENU_ACTIVE_FG_COLOR,
249            Tk_Offset(TkMenu, activeFgPtr), -1, 0,
250            (ClientData) DEF_MENU_ACTIVE_FG_MONO},
251        {TK_OPTION_BORDER, "-background", "background", "Background",
252            DEF_MENU_BG_COLOR, Tk_Offset(TkMenu, borderPtr), -1, 0,
253            (ClientData) DEF_MENU_BG_MONO},
254        {TK_OPTION_SYNONYM, "-bd", (char *) NULL, (char *) NULL,
255            (char *) NULL, 0, -1, 0, (ClientData) "-borderwidth"},
256        {TK_OPTION_SYNONYM, "-bg", (char *) NULL, (char *) NULL,
257            (char *) NULL, 0, -1, 0, (ClientData) "-background"},
258        {TK_OPTION_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
259            DEF_MENU_BORDER_WIDTH,
260            Tk_Offset(TkMenu, borderWidthPtr), -1, 0},
261        {TK_OPTION_CURSOR, "-cursor", "cursor", "Cursor",
262            DEF_MENU_CURSOR,
263            Tk_Offset(TkMenu, cursorPtr), -1, TK_OPTION_NULL_OK},
264        {TK_OPTION_COLOR, "-disabledforeground", "disabledForeground",
265            "DisabledForeground", DEF_MENU_DISABLED_FG_COLOR,
266            Tk_Offset(TkMenu, disabledFgPtr), -1, TK_OPTION_NULL_OK,
267            (ClientData) DEF_MENU_DISABLED_FG_MONO},
268        {TK_OPTION_SYNONYM, "-fg", (char *) NULL, (char *) NULL,
269            (char *) NULL, 0, -1, 0, (ClientData) "-foreground"},
270        {TK_OPTION_FONT, "-font", "font", "Font",
271            DEF_MENU_FONT, Tk_Offset(TkMenu, fontPtr), -1},
272        {TK_OPTION_COLOR, "-foreground", "foreground", "Foreground",
273            DEF_MENU_FG, Tk_Offset(TkMenu, fgPtr), -1},
274        {TK_OPTION_STRING, "-postcommand", "postCommand", "Command",
275            DEF_MENU_POST_COMMAND,
276            Tk_Offset(TkMenu, postCommandPtr), -1, TK_OPTION_NULL_OK},
277        {TK_OPTION_RELIEF, "-relief", "relief", "Relief",
278            DEF_MENU_RELIEF, Tk_Offset(TkMenu, reliefPtr), -1},
279        {TK_OPTION_COLOR, "-selectcolor", "selectColor", "Background",
280            DEF_MENU_SELECT_COLOR, Tk_Offset(TkMenu, indicatorFgPtr), -1, 0,
281            (ClientData) DEF_MENU_SELECT_MONO},
282        {TK_OPTION_STRING, "-takefocus", "takeFocus", "TakeFocus",
283            DEF_MENU_TAKE_FOCUS,
284            Tk_Offset(TkMenu, takeFocusPtr), -1, TK_OPTION_NULL_OK},
285        {TK_OPTION_BOOLEAN, "-tearoff", "tearOff", "TearOff",
286            DEF_MENU_TEAROFF, -1, Tk_Offset(TkMenu, tearoff)},
287        {TK_OPTION_STRING, "-tearoffcommand", "tearOffCommand",
288            "TearOffCommand", DEF_MENU_TEAROFF_CMD,
289            Tk_Offset(TkMenu, tearoffCommandPtr), -1, TK_OPTION_NULL_OK},
290        {TK_OPTION_STRING, "-title", "title", "Title",
291            DEF_MENU_TITLE,  Tk_Offset(TkMenu, titlePtr), -1,
292            TK_OPTION_NULL_OK},
293        {TK_OPTION_STRING_TABLE, "-type", "type", "Type",
294            DEF_MENU_TYPE, Tk_Offset(TkMenu, menuTypePtr), -1, TK_OPTION_NULL_OK,
295            (ClientData) menuTypeStrings},
296        {TK_OPTION_END}
297    };
298    
299    /*
300     * Command line options. Put here because MenuCmd has to look at them
301     * along with MenuWidgetObjCmd.
302     */
303    
304    static char *menuOptions[] = {
305        "activate", "add", "cget", "clone", "configure", "delete", "entrycget",
306        "entryconfigure", "index", "insert", "invoke", "post", "postcascade",
307        "type", "unpost", "yposition", (char *) NULL
308    };
309    enum options {
310        MENU_ACTIVATE, MENU_ADD, MENU_CGET, MENU_CLONE, MENU_CONFIGURE,
311        MENU_DELETE, MENU_ENTRYCGET, MENU_ENTRYCONFIGURE, MENU_INDEX,
312        MENU_INSERT, MENU_INVOKE, MENU_POST, MENU_POSTCASCADE, MENU_TYPE,
313        MENU_UNPOST, MENU_YPOSITION
314    };
315    
316    /*
317     * Prototypes for static procedures in this file:
318     */
319    
320    static int              CloneMenu _ANSI_ARGS_((TkMenu *menuPtr,
321                                Tcl_Obj *newMenuName, Tcl_Obj *newMenuTypeString));
322    static int              ConfigureMenu _ANSI_ARGS_((Tcl_Interp *interp,
323                                TkMenu *menuPtr, int objc, Tcl_Obj *CONST objv[]));
324    static int              ConfigureMenuCloneEntries _ANSI_ARGS_((
325                                Tcl_Interp *interp, TkMenu *menuPtr, int index,
326                                int objc, Tcl_Obj *CONST objv[]));
327    static int              ConfigureMenuEntry _ANSI_ARGS_((TkMenuEntry *mePtr,
328                                int objc, Tcl_Obj *CONST objv[]));
329    static void             DeleteMenuCloneEntries _ANSI_ARGS_((TkMenu *menuPtr,
330                                int first, int last));
331    static void             DestroyMenuHashTable _ANSI_ARGS_((
332                                ClientData clientData, Tcl_Interp *interp));
333    static void             DestroyMenuInstance _ANSI_ARGS_((TkMenu *menuPtr));
334    static void             DestroyMenuEntry _ANSI_ARGS_((char *memPtr));
335    static int              GetIndexFromCoords
336                                _ANSI_ARGS_((Tcl_Interp *interp, TkMenu *menuPtr,
337                                char *string, int *indexPtr));
338    static int              MenuDoYPosition _ANSI_ARGS_((Tcl_Interp *interp,
339                                TkMenu *menuPtr, Tcl_Obj *objPtr));
340    static int              MenuAddOrInsert _ANSI_ARGS_((Tcl_Interp *interp,
341                                TkMenu *menuPtr, Tcl_Obj *indexPtr, int objc,
342                                Tcl_Obj *CONST objv[]));
343    static int              MenuCmd _ANSI_ARGS_((ClientData clientData,
344                                Tcl_Interp *interp, int objc,
345                                Tcl_Obj *CONST objv[]));
346    static void             MenuCmdDeletedProc _ANSI_ARGS_((
347                                ClientData clientData));
348    static TkMenuEntry *    MenuNewEntry _ANSI_ARGS_((TkMenu *menuPtr, int index,
349                                int type));
350    static char *           MenuVarProc _ANSI_ARGS_((ClientData clientData,
351                                Tcl_Interp *interp, char *name1, char *name2,
352                                int flags));
353    static int              MenuWidgetObjCmd _ANSI_ARGS_((ClientData clientData,
354                                Tcl_Interp *interp, int objc,
355                                Tcl_Obj *CONST objv[]));
356    static void             MenuWorldChanged _ANSI_ARGS_((
357                                ClientData instanceData));
358    static int              PostProcessEntry _ANSI_ARGS_((TkMenuEntry *mePtr));
359    static void             RecursivelyDeleteMenu _ANSI_ARGS_((TkMenu *menuPtr));
360    static void             UnhookCascadeEntry _ANSI_ARGS_((TkMenuEntry *mePtr));
361    
362    /*
363     * The structure below is a list of procs that respond to certain window
364     * manager events. One of these includes a font change, which forces
365     * the geometry proc to be called.
366     */
367    
368    static TkClassProcs menuClass = {
369        NULL,                       /* createProc. */
370        MenuWorldChanged            /* geometryProc. */
371    };
372    
373    /*
374     *--------------------------------------------------------------
375     *
376     * Tk_CreateMenuCmd --
377     *
378     *      Called by Tk at initialization time to create the menu
379     *      command.
380     *
381     * Results:
382     *      A standard Tcl result.
383     *
384     * Side effects:
385     *      See the user documentation.
386     *
387     *--------------------------------------------------------------
388     */
389    
390    int
391    TkCreateMenuCmd(interp)
392        Tcl_Interp *interp;         /* Interpreter we are creating the
393                                     * command in. */
394    {
395        TkMenuOptionTables *optionTablesPtr =
396                (TkMenuOptionTables *) ckalloc(sizeof(TkMenuOptionTables));
397    
398        optionTablesPtr->menuOptionTable =
399                Tk_CreateOptionTable(interp, tkMenuConfigSpecs);
400        optionTablesPtr->entryOptionTables[TEAROFF_ENTRY] =
401                Tk_CreateOptionTable(interp, specsArray[TEAROFF_ENTRY]);
402        optionTablesPtr->entryOptionTables[COMMAND_ENTRY] =
403                Tk_CreateOptionTable(interp, specsArray[COMMAND_ENTRY]);
404        optionTablesPtr->entryOptionTables[CASCADE_ENTRY] =
405                Tk_CreateOptionTable(interp, specsArray[CASCADE_ENTRY]);
406        optionTablesPtr->entryOptionTables[SEPARATOR_ENTRY] =
407                Tk_CreateOptionTable(interp, specsArray[SEPARATOR_ENTRY]);
408        optionTablesPtr->entryOptionTables[RADIO_BUTTON_ENTRY] =
409                Tk_CreateOptionTable(interp, specsArray[RADIO_BUTTON_ENTRY]);
410        optionTablesPtr->entryOptionTables[CHECK_BUTTON_ENTRY] =
411                Tk_CreateOptionTable(interp, specsArray[CHECK_BUTTON_ENTRY]);
412    
413        Tcl_CreateObjCommand(interp, "menu", MenuCmd,
414                (ClientData) optionTablesPtr, NULL);
415    
416        if (Tcl_IsSafe(interp)) {
417            Tcl_HideCommand(interp, "menu", "menu");
418        }
419    
420        return TCL_OK;
421    }
422    
423    /*
424     *--------------------------------------------------------------
425     *
426     * MenuCmd --
427     *
428     *      This procedure is invoked to process the "menu" Tcl
429     *      command.  See the user documentation for details on
430     *      what it does.
431     *
432     * Results:
433     *      A standard Tcl result.
434     *
435     * Side effects:
436     *      See the user documentation.
437     *
438     *--------------------------------------------------------------
439     */
440    
441    static int
442    MenuCmd(clientData, interp, objc, objv)
443        ClientData clientData;      /* Main window associated with
444                                     * interpreter. */
445        Tcl_Interp *interp;         /* Current interpreter. */
446        int objc;                   /* Number of arguments. */
447        Tcl_Obj *CONST objv[];      /* Argument strings. */
448    {
449        Tk_Window tkwin = Tk_MainWindow(interp);
450        Tk_Window new;
451        register TkMenu *menuPtr;
452        TkMenuReferences *menuRefPtr;
453        int i, index;
454        int toplevel;
455        char *windowName;
456        static char *typeStringList[] = {"-type", (char *) NULL};
457        TkMenuOptionTables *optionTablesPtr = (TkMenuOptionTables *) clientData;
458    
459        if (objc < 2) {
460            Tcl_WrongNumArgs(interp, 1, objv, "pathName ?options?");
461            return TCL_ERROR;
462        }
463    
464        TkMenuInit();
465    
466        toplevel = 1;
467        for (i = 2; i < (objc - 1); i++) {
468            if (Tcl_GetIndexFromObj(NULL, objv[i], typeStringList, NULL, 0, &index)
469                    != TCL_ERROR) {
470                if ((Tcl_GetIndexFromObj(NULL, objv[i + 1], menuTypeStrings, NULL,
471                        0, &index) == TCL_OK) && (index == MENUBAR)) {
472                    toplevel = 0;
473                }
474                break;
475            }
476        }
477    
478        windowName = Tcl_GetStringFromObj(objv[1], NULL);
479        new = Tk_CreateWindowFromPath(interp, tkwin, windowName, toplevel ? ""
480                : NULL);
481        if (new == NULL) {
482            return TCL_ERROR;
483        }
484    
485        /*
486         * Initialize the data structure for the menu.
487         */
488    
489        menuPtr = (TkMenu *) ckalloc(sizeof(TkMenu));
490        menuPtr->tkwin = new;
491        menuPtr->display = Tk_Display(new);
492        menuPtr->interp = interp;
493        menuPtr->widgetCmd = Tcl_CreateObjCommand(interp,
494                Tk_PathName(menuPtr->tkwin), MenuWidgetObjCmd,
495                (ClientData) menuPtr, MenuCmdDeletedProc);
496        menuPtr->entries = NULL;
497        menuPtr->numEntries = 0;
498        menuPtr->active = -1;
499        menuPtr->borderPtr = NULL;
500        menuPtr->borderWidthPtr = NULL;
501        menuPtr->reliefPtr = NULL;
502        menuPtr->activeBorderPtr = NULL;
503        menuPtr->activeBorderWidthPtr = NULL;
504        menuPtr->fontPtr = NULL;
505        menuPtr->fgPtr = NULL;
506        menuPtr->disabledFgPtr = NULL;
507        menuPtr->activeFgPtr = NULL;
508        menuPtr->indicatorFgPtr = NULL;
509        menuPtr->tearoff = 0;
510        menuPtr->tearoffCommandPtr = NULL;
511        menuPtr->cursorPtr = None;
512        menuPtr->takeFocusPtr = NULL;
513        menuPtr->postCommandPtr = NULL;
514        menuPtr->postCommandGeneration = 0;
515        menuPtr->postedCascade = NULL;
516        menuPtr->nextInstancePtr = NULL;
517        menuPtr->masterMenuPtr = menuPtr;
518        menuPtr->menuType = UNKNOWN_TYPE;
519        menuPtr->menuFlags = 0;
520        menuPtr->parentTopLevelPtr = NULL;
521        menuPtr->menuTypePtr = NULL;
522        menuPtr->titlePtr = NULL;
523        menuPtr->errorStructPtr = NULL;
524        menuPtr->optionTablesPtr = optionTablesPtr;
525        TkMenuInitializeDrawingFields(menuPtr);
526    
527        Tk_SetClass(menuPtr->tkwin, "Menu");
528        TkSetClassProcs(menuPtr->tkwin, &menuClass, (ClientData) menuPtr);
529        if (Tk_InitOptions(interp, (char *) menuPtr,
530                menuPtr->optionTablesPtr->menuOptionTable, menuPtr->tkwin)
531                != TCL_OK) {
532            Tk_DestroyWindow(menuPtr->tkwin);
533            ckfree((char *) menuPtr);
534            return TCL_ERROR;
535        }
536    
537    
538        menuRefPtr = TkCreateMenuReferences(menuPtr->interp,
539                Tk_PathName(menuPtr->tkwin));
540        menuRefPtr->menuPtr = menuPtr;
541        menuPtr->menuRefPtr = menuRefPtr;
542        if (TCL_OK != TkpNewMenu(menuPtr)) {
543            Tk_DestroyWindow(menuPtr->tkwin);
544            ckfree((char *) menuPtr);
545            return TCL_ERROR;
546        }
547    
548        Tk_CreateEventHandler(new, ExposureMask|StructureNotifyMask|ActivateMask,
549                TkMenuEventProc, (ClientData) menuPtr);
550        if (ConfigureMenu(interp, menuPtr, objc - 2, objv + 2) != TCL_OK) {
551            Tk_DestroyWindow(menuPtr->tkwin);
552            return TCL_ERROR;
553        }
554    
555        /*
556         * If a menu has a parent menu pointing to it as a cascade entry, the
557         * parent menu needs to be told that this menu now exists so that
558         * the platform-part of the menu is correctly updated.
559         *
560         * If a menu has an instance and has cascade entries, then each cascade
561         * menu must also have a parallel instance. This is especially true on
562         * the Mac, where each menu has to have a separate title everytime it is in
563         * a menubar. For instance, say you have a menu .m1 with a cascade entry
564         * for .m2, where .m2 does not exist yet. You then put .m1 into a menubar.
565         * This creates a menubar instance for .m1, but since .m2 is not there,
566         * nothing else happens. When we go to create .m2, we hook it up properly
567         * with .m1. However, we now need to clone .m2 and assign the clone of .m2
568         * to be the cascade entry for the clone of .m1. This is special case
569         * #1 listed in the introductory comment.
570         */
571        
572        if (menuRefPtr->parentEntryPtr != NULL) {
573            TkMenuEntry *cascadeListPtr = menuRefPtr->parentEntryPtr;
574            TkMenuEntry *nextCascadePtr;
575            Tcl_Obj *newMenuName;
576            Tcl_Obj *newObjv[2];
577    
578            while (cascadeListPtr != NULL) {
579    
580                nextCascadePtr = cascadeListPtr->nextCascadePtr;
581        
582                /*
583                 * If we have a new master menu, and an existing cloned menu
584                 * points to this menu in a cascade entry, we have to clone
585                 * the new menu and point the entry to the clone instead
586                 * of the menu we are creating. Otherwise, ConfigureMenuEntry
587                 * will hook up the platform-specific cascade linkages now
588                 * that the menu we are creating exists.
589                 */
590                
591                if ((menuPtr->masterMenuPtr != menuPtr)
592                        || ((menuPtr->masterMenuPtr == menuPtr)
593                        && ((cascadeListPtr->menuPtr->masterMenuPtr
594                        == cascadeListPtr->menuPtr)))) {
595                    newObjv[0] = Tcl_NewStringObj("-menu", -1);
596                    newObjv[1] = Tcl_NewStringObj(Tk_PathName(menuPtr->tkwin), -1);
597                    Tcl_IncrRefCount(newObjv[0]);
598                    Tcl_IncrRefCount(newObjv[1]);
599                    ConfigureMenuEntry(cascadeListPtr, 2, newObjv);
600                    Tcl_DecrRefCount(newObjv[0]);
601                    Tcl_DecrRefCount(newObjv[1]);
602                } else {
603                    Tcl_Obj *normalPtr = Tcl_NewStringObj("normal", -1);
604                    Tcl_Obj *windowNamePtr = Tcl_NewStringObj(
605                            Tk_PathName(cascadeListPtr->menuPtr->tkwin), -1);
606    
607                    Tcl_IncrRefCount(normalPtr);
608                    Tcl_IncrRefCount(windowNamePtr);
609                    newMenuName = TkNewMenuName(menuPtr->interp,
610                            windowNamePtr, menuPtr);
611                    Tcl_IncrRefCount(newMenuName);
612                    CloneMenu(menuPtr, newMenuName, normalPtr);
613                        
614                    /*
615                     * Now we can set the new menu instance to be the cascade entry
616                     * of the parent's instance.
617                     */
618    
619                    newObjv[0] = Tcl_NewStringObj("-menu", -1);
620                    newObjv[1] = newMenuName;
621                    Tcl_IncrRefCount(newObjv[0]);
622                    ConfigureMenuEntry(cascadeListPtr, 2, newObjv);
623                    Tcl_DecrRefCount(normalPtr);
624                    Tcl_DecrRefCount(newObjv[0]);
625                    Tcl_DecrRefCount(newObjv[1]);
626                    Tcl_DecrRefCount(windowNamePtr);
627                }
628                cascadeListPtr = nextCascadePtr;
629            }
630        }
631        
632        /*
633         * If there already exist toplevel widgets that refer to this menu,
634         * find them and notify them so that they can reconfigure their
635         * geometry to reflect the menu.
636         */
637    
638        if (menuRefPtr->topLevelListPtr != NULL) {
639            TkMenuTopLevelList *topLevelListPtr = menuRefPtr->topLevelListPtr;
640            TkMenuTopLevelList *nextPtr;
641            Tk_Window listtkwin;
642            while (topLevelListPtr != NULL) {
643            
644                /*
645                 * Need to get the next pointer first. TkSetWindowMenuBar
646                 * changes the list, so that the next pointer is different
647                 * after calling it.
648                 */
649            
650                nextPtr = topLevelListPtr->nextPtr;
651                listtkwin = topLevelListPtr->tkwin;
652                TkSetWindowMenuBar(menuPtr->interp, listtkwin,
653                        Tk_PathName(menuPtr->tkwin), Tk_PathName(menuPtr->tkwin));
654                topLevelListPtr = nextPtr;
655            }
656        }
657    
658        Tcl_SetResult(interp, Tk_PathName(menuPtr->tkwin), TCL_STATIC);
659        return TCL_OK;
660    }
661    
662    /*
663     *--------------------------------------------------------------
664     *
665     * MenuWidgetObjCmd --
666     *
667     *      This procedure is invoked to process the Tcl command
668     *      that corresponds to a widget managed by this module.
669     *      See the user documentation for details on what it does.
670     *
671     * Results:
672     *      A standard Tcl result.
673     *
674     * Side effects:
675     *      See the user documentation.
676     *
677     *--------------------------------------------------------------
678     */
679    
680    static int
681    MenuWidgetObjCmd(clientData, interp, objc, objv)
682        ClientData clientData;      /* Information about menu widget. */
683        Tcl_Interp *interp;         /* Current interpreter. */
684        int objc;                   /* Number of arguments. */
685        Tcl_Obj *CONST objv[];      /* Argument strings. */
686    {
687        register TkMenu *menuPtr = (TkMenu *) clientData;
688        register TkMenuEntry *mePtr;
689        int result = TCL_OK;
690        int option;
691    
692        if (objc < 2) {
693            Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?");
694            return TCL_ERROR;
695        }
696        if (Tcl_GetIndexFromObj(interp, objv[1], menuOptions, "option", 0,
697                &option) != TCL_OK) {
698            return TCL_ERROR;
699        }
700        Tcl_Preserve((ClientData) menuPtr);
701    
702        switch ((enum options) option) {
703            case MENU_ACTIVATE: {
704                int index;
705    
706                if (objc != 3) {
707                    Tcl_WrongNumArgs(interp, 1, objv, "activate index");
708                    goto error;
709                }
710                if (TkGetMenuIndex(interp, menuPtr, objv[2], 0, &index)
711                        != TCL_OK) {
712                    goto error;
713                }
714                if (menuPtr->active == index) {
715                    goto done;
716                }
717                if ((index >= 0)
718                        && ((menuPtr->entries[index]->type == SEPARATOR_ENTRY)
719                                || (menuPtr->entries[index]->state
720                                        == ENTRY_DISABLED))) {
721                    index = -1;
722                }
723                result = TkActivateMenuEntry(menuPtr, index);
724                break;
725            }
726            case MENU_ADD:
727                if (objc < 3) {
728                    Tcl_WrongNumArgs(interp, 1, objv, "add type ?options?");
729                    goto error;
730                }
731    
732                if (MenuAddOrInsert(interp, menuPtr, (Tcl_Obj *) NULL,
733                        objc - 2, objv + 2) != TCL_OK) {
734                    goto error;
735                }
736                break;
737            case MENU_CGET: {
738                Tcl_Obj *resultPtr;
739    
740                if (objc != 3) {
741                    Tcl_WrongNumArgs(interp, 1, objv, "cget option");
742                    goto error;
743                }
744                resultPtr = Tk_GetOptionValue(interp, (char *) menuPtr,
745                        menuPtr->optionTablesPtr->menuOptionTable, objv[2],
746                        menuPtr->tkwin);
747                if (resultPtr == NULL) {
748                    goto error;
749                }
750                Tcl_SetObjResult(interp, resultPtr);
751                break;
752            }
753            case MENU_CLONE:
754                if ((objc < 3) || (objc > 4)) {
755                    Tcl_WrongNumArgs(interp, 1, objv,
756                            "clone newMenuName ?menuType?");
757                    goto error;
758                }
759                result = CloneMenu(menuPtr, objv[2], (objc == 3) ? NULL : objv[3]);
760                break;
761            case MENU_CONFIGURE: {
762                Tcl_Obj *resultPtr;
763    
764                if (objc == 2) {
765                    resultPtr = Tk_GetOptionInfo(interp, (char *) menuPtr,
766                            menuPtr->optionTablesPtr->menuOptionTable,
767                            (Tcl_Obj *) NULL, menuPtr->tkwin);
768                    if (resultPtr == NULL) {
769                        result = TCL_ERROR;
770                    } else {
771                        result = TCL_OK;
772                        Tcl_SetObjResult(interp, resultPtr);
773                    }
774                } else if (objc == 3) {
775                    resultPtr = Tk_GetOptionInfo(interp, (char *) menuPtr,
776                            menuPtr->optionTablesPtr->menuOptionTable,
777                            objv[2], menuPtr->tkwin);
778                    if (resultPtr == NULL) {
779                        result = TCL_ERROR;
780                    } else {
781                        result = TCL_OK;
782                        Tcl_SetObjResult(interp, resultPtr);
783                    }
784                } else {
785                    result = ConfigureMenu(interp, menuPtr, objc - 2, objv + 2);
786                }
787                if (result != TCL_OK) {
788                    goto error;
789                }
790                break;
791            }
792            case MENU_DELETE: {
793                int first, last;
794                
795                if ((objc != 3) && (objc != 4)) {
796                    Tcl_WrongNumArgs(interp, 1, objv, "delete first ?last?");
797                    goto error;
798                }
799                if (TkGetMenuIndex(interp, menuPtr, objv[2], 0, &first)
800                        != TCL_OK) {
801                    goto error;
802                }
803                if (objc == 3) {
804                    last = first;
805                } else {
806                    if (TkGetMenuIndex(interp, menuPtr, objv[3], 0, &last)
807                            != TCL_OK) {
808                        goto error;
809                    }
810                }
811                if (menuPtr->tearoff && (first == 0)) {
812    
813                    /*
814                     * Sorry, can't delete the tearoff entry;  must reconfigure
815                     * the menu.
816                     */
817                    
818                    first = 1;
819                }
820                if ((first < 0) || (last < first)) {
821                    goto done;
822                }
823                DeleteMenuCloneEntries(menuPtr, first, last);
824                break;
825            }
826            case MENU_ENTRYCGET: {
827                int index;
828                Tcl_Obj *resultPtr;
829    
830                if (objc != 4) {
831                    Tcl_WrongNumArgs(interp, 1, objv, "entrycget index option");
832                    goto error;
833                }
834                if (TkGetMenuIndex(interp, menuPtr, objv[2], 0, &index)
835                        != TCL_OK) {
836                    goto error;
837                }
838                if (index < 0) {
839                    goto done;
840                }
841                mePtr = menuPtr->entries[index];
842                Tcl_Preserve((ClientData) mePtr);
843                resultPtr = Tk_GetOptionValue(interp, (char *) mePtr,
844                        mePtr->optionTable, objv[3], menuPtr->tkwin);
845                Tcl_Release((ClientData) mePtr);
846                if (resultPtr == NULL) {
847                    goto error;
848                }
849                Tcl_SetObjResult(interp, resultPtr);
850                break;
851            }
852            case MENU_ENTRYCONFIGURE: {
853                int index;
854                Tcl_Obj *resultPtr;
855    
856                if (objc < 3) {
857                    Tcl_WrongNumArgs(interp, 1, objv,
858                            "entryconfigure index ?option value ...?");
859                    goto error;
860                }
861                if (TkGetMenuIndex(interp, menuPtr, objv[2], 0, &index)
862                        != TCL_OK) {
863                    goto error;
864                }
865                if (index < 0) {
866                    goto done;
867                }
868                mePtr = menuPtr->entries[index];
869                Tcl_Preserve((ClientData) mePtr);
870                if (objc == 3) {
871                    resultPtr = Tk_GetOptionInfo(interp, (char *) mePtr,
872                            mePtr->optionTable, (Tcl_Obj *) NULL, menuPtr->tkwin);
873                    if (resultPtr == NULL) {
874                        result = TCL_ERROR;
875                    } else {
876                        result = TCL_OK;
877                        Tcl_SetObjResult(interp, resultPtr);
878                    }
879                } else if (objc == 4) {
880                    resultPtr = Tk_GetOptionInfo(interp, (char *) mePtr,
881                            mePtr->optionTable, objv[3], menuPtr->tkwin);
882                    if (resultPtr == NULL) {
883                        result = TCL_ERROR;
884                    } else {
885                        result = TCL_OK;
886                        Tcl_SetObjResult(interp, resultPtr);
887                    }
888                } else {
889                    result = ConfigureMenuCloneEntries(interp, menuPtr, index,
890                            objc - 3, objv + 3);
891                }
892                Tcl_Release((ClientData) mePtr);
893                break;
894            }
895            case MENU_INDEX: {
896                int index;
897    
898                if (objc != 3) {
899                    Tcl_WrongNumArgs(interp, 1, objv, "index string");
900                    goto error;
901                }
902                if (TkGetMenuIndex(interp, menuPtr, objv[2], 0, &index)
903                        != TCL_OK) {
904                    goto error;
905                }
906                if (index < 0) {
907                    Tcl_SetResult(interp, "none", TCL_STATIC);
908                } else {
909                    Tcl_SetIntObj(Tcl_GetObjResult(interp), index);
910                }
911                break;
912            }
913            case MENU_INSERT:
914                if (objc < 4) {
915                    Tcl_WrongNumArgs(interp, 1, objv,
916                            "insert index type ?options?");
917                    goto error;
918                }
919                if (MenuAddOrInsert(interp, menuPtr, objv[2], objc - 3,
920                        objv + 3) != TCL_OK) {
921                    goto error;
922                }
923                break;
924            case MENU_INVOKE: {
925                int index;
926    
927                if (objc != 3) {
928                    Tcl_WrongNumArgs(interp, 1, objv, "invoke index");
929                    goto error;
930                }
931                if (TkGetMenuIndex(interp, menuPtr, objv[2], 0, &index)
932                        != TCL_OK) {
933                    goto error;
934                }
935                if (index < 0) {
936                    goto done;
937                }
938                result = TkInvokeMenu(interp, menuPtr, index);
939                break;
940            }
941            case MENU_POST: {
942                int x, y;
943    
944                if (objc != 4) {
945                    Tcl_WrongNumArgs(interp, 1, objv, "post x y");
946                    goto error;
947                }
948                if ((Tcl_GetIntFromObj(interp, objv[2], &x) != TCL_OK)
949                        || (Tcl_GetIntFromObj(interp, objv[3], &y) != TCL_OK)) {
950                    goto error;
951                }
952    
953                /*
954                 * Tearoff menus are posted differently on Mac and Windows than
955                 * non-tearoffs. TkpPostMenu does not actually map the menu's
956                 * window on those platforms, and popup menus have to be
957                 * handled specially.
958                 */
959                
960                if (menuPtr->menuType != TEAROFF_MENU) {
961                    result = TkpPostMenu(interp, menuPtr, x, y);
962                } else {
963                    result = TkPostTearoffMenu(interp, menuPtr, x, y);
964                }
965                break;
966            }
967            case MENU_POSTCASCADE: {
968                int index;
969    
970                if (objc != 3) {
971                    Tcl_WrongNumArgs(interp, 1, objv, "postcascade index");
972                    goto error;
973                }
974    
975                if (TkGetMenuIndex(interp, menuPtr, objv[2], 0, &index)
976                        != TCL_OK) {
977                    goto error;
978                }
979                if ((index < 0) || (menuPtr->entries[index]->type
980                        != CASCADE_ENTRY)) {
981                    result = TkPostSubmenu(interp, menuPtr, (TkMenuEntry *) NULL);
982                } else {
983                    result = TkPostSubmenu(interp, menuPtr,
984                            menuPtr->entries[index]);
985                }
986                break;
987            }
988            case MENU_TYPE: {
989                int index;
990    
991                if (objc != 3) {
992                    Tcl_WrongNumArgs(interp, 1, objv, "type index");
993                    goto error;
994                }
995                if (TkGetMenuIndex(interp, menuPtr, objv[2], 0, &index)
996                        != TCL_OK) {
997                    goto error;
998                }
999                if (index < 0) {
1000                    goto done;
1001                }
1002                if (menuPtr->entries[index]->type == TEAROFF_ENTRY) {
1003                    Tcl_SetResult(interp, "tearoff", TCL_STATIC);
1004                } else {
1005                    Tcl_SetResult(interp,
1006                            menuEntryTypeStrings[menuPtr->entries[index]->type],
1007                            TCL_STATIC);
1008                }
1009                break;
1010            }
1011            case MENU_UNPOST:
1012                if (objc != 2) {
1013                    Tcl_WrongNumArgs(interp, 1, objv, "unpost");
1014                    goto error;
1015                }
1016                Tk_UnmapWindow(menuPtr->tkwin);
1017                result = TkPostSubmenu(interp, menuPtr, (TkMenuEntry *) NULL);
1018                break;
1019            case MENU_YPOSITION:
1020                if (objc != 3) {
1021                    Tcl_WrongNumArgs(interp, 1, objv, "yposition index");
1022                    goto error;
1023                }
1024                result = MenuDoYPosition(interp, menuPtr, objv[2]);
1025                break;
1026        }
1027        done:
1028        Tcl_Release((ClientData) menuPtr);
1029        return result;
1030    
1031        error:
1032        Tcl_Release((ClientData) menuPtr);
1033        return TCL_ERROR;
1034    }
1035    
1036    /*
1037     *----------------------------------------------------------------------
1038     *
1039     * TkInvokeMenu --
1040     *
1041     *      Given a menu and an index, takes the appropriate action for the
1042     *      entry associated with that index.
1043     *
1044     * Results:
1045     *      Standard Tcl result.
1046     *
1047     * Side effects:
1048     *      Commands may get excecuted; variables may get set; sub-menus may
1049     *      get posted.
1050     *
1051     *----------------------------------------------------------------------
1052     */
1053    
1054    int
1055    TkInvokeMenu(interp, menuPtr, index)
1056        Tcl_Interp *interp;         /* The interp that the menu lives in. */
1057        TkMenu *menuPtr;            /* The menu we are invoking. */
1058        int index;                  /* The zero based index of the item we
1059                                     * are invoking */
1060    {
1061        int result = TCL_OK;
1062        TkMenuEntry *mePtr;
1063        
1064        if (index < 0) {
1065            goto done;
1066        }
1067        mePtr = menuPtr->entries[index];
1068        if (mePtr->state == ENTRY_DISABLED) {
1069            goto done;
1070        }
1071        Tcl_Preserve((ClientData) mePtr);
1072        if (mePtr->type == TEAROFF_ENTRY) {
1073            Tcl_DString ds;
1074            Tcl_DStringInit(&ds);
1075            Tcl_DStringAppend(&ds, "tkTearOffMenu ", -1);
1076            Tcl_DStringAppend(&ds, Tk_PathName(menuPtr->tkwin), -1);
1077            result = Tcl_Eval(interp, Tcl_DStringValue(&ds));
1078            Tcl_DStringFree(&ds);
1079        } else if ((mePtr->type == CHECK_BUTTON_ENTRY)
1080                && (mePtr->namePtr != NULL)) {
1081            Tcl_Obj *valuePtr;
1082    
1083            if (mePtr->entryFlags & ENTRY_SELECTED) {
1084                valuePtr = mePtr->offValuePtr;
1085            } else {
1086                valuePtr = mePtr->onValuePtr;
1087            }
1088            if (valuePtr == NULL) {
1089                valuePtr = Tcl_NewObj();
1090            }
1091            Tcl_IncrRefCount(valuePtr);
1092            if (Tcl_ObjSetVar2(interp, mePtr->namePtr, NULL, valuePtr,
1093                    TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
1094                result = TCL_ERROR;
1095            }
1096            Tcl_DecrRefCount(valuePtr);
1097        } else if ((mePtr->type == RADIO_BUTTON_ENTRY)
1098                && (mePtr->namePtr != NULL)) {
1099            Tcl_Obj *valuePtr = mePtr->onValuePtr;
1100    
1101            if (valuePtr == NULL) {
1102                valuePtr = Tcl_NewObj();
1103            }
1104            Tcl_IncrRefCount(valuePtr);
1105            if (Tcl_ObjSetVar2(interp, mePtr->namePtr, NULL, valuePtr,
1106                    TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
1107                result = TCL_ERROR;
1108            }
1109            Tcl_DecrRefCount(valuePtr);
1110        }
1111        if ((result == TCL_OK) && (mePtr->commandPtr != NULL)) {
1112            Tcl_Obj *commandPtr = mePtr->commandPtr;
1113    
1114            Tcl_IncrRefCount(commandPtr);
1115            result = Tcl_EvalObjEx(interp, commandPtr, TCL_EVAL_GLOBAL);
1116            Tcl_DecrRefCount(commandPtr);
1117        }
1118        Tcl_Release((ClientData) mePtr);
1119        done:
1120        return result;
1121    }
1122    
1123    /*
1124     *----------------------------------------------------------------------
1125     *
1126     * DestroyMenuInstance --
1127     *
1128     *      This procedure is invoked by TkDestroyMenu
1129     *      to clean up the internal structure of a menu at a safe time
1130     *      (when no-one is using it anymore). Only takes care of one instance
1131     *      of the menu.
1132     *
1133     * Results:
1134     *      None.
1135     *
1136     * Side effects:
1137     *      Everything associated with the menu is freed up.
1138     *
1139     *----------------------------------------------------------------------
1140     */
1141    
1142    static void
1143    DestroyMenuInstance(menuPtr)
1144        TkMenu *menuPtr;    /* Info about menu widget. */
1145    {
1146        int i;
1147        TkMenu *menuInstancePtr;
1148        TkMenuEntry *cascadePtr, *nextCascadePtr;
1149        Tcl_Obj *newObjv[2];
1150        TkMenu *parentMasterMenuPtr;
1151        TkMenuEntry *parentMasterEntryPtr;
1152        
1153        /*
1154         * If the menu has any cascade menu entries pointing to it, the cascade
1155         * entries need to be told that the menu is going away. We need to clear
1156         * the menu ptr field in the menu reference at this point in the code
1157         * so that everything else can forget about this menu properly. We also
1158         * need to reset -menu field of all entries that are not master menus
1159         * back to this entry name if this is a master menu pointed to by another
1160         * master menu. If there is a clone menu that points to this menu,
1161         * then this menu is itself a clone, so when this menu goes away,
1162         * the -menu field of the pointing entry must be set back to this
1163         * menu's master menu name so that later if another menu is created
1164         * the cascade hierarchy can be maintained.
1165         */
1166    
1167        TkpDestroyMenu(menuPtr);
1168        cascadePtr = menuPtr->menuRefPtr->parentEntryPtr;
1169        menuPtr->menuRefPtr->menuPtr = NULL;
1170        TkFreeMenuReferences(menuPtr->menuRefPtr);
1171    
1172        for (; cascadePtr != NULL; cascadePtr = nextCascadePtr) {
1173            nextCascadePtr = cascadePtr->nextCascadePtr;
1174            
1175            if (menuPtr->masterMenuPtr != menuPtr) {
1176                Tcl_Obj *menuNamePtr = Tcl_NewStringObj("-menu", -1);
1177    
1178                parentMasterMenuPtr = cascadePtr->menuPtr->masterMenuPtr;
1179                parentMasterEntryPtr =
1180                        parentMasterMenuPtr->entries[cascadePtr->index];
1181                newObjv[0] = menuNamePtr;
1182                newObjv[1] = parentMasterEntryPtr->namePtr;
1183                /*
1184                 * It is possible that the menu info is out of sync, and
1185                 * these things point to NULL, so verify existence [Bug: 3402]
1186                 */
1187                if (newObjv[0] && newObjv[1]) {
1188                    Tcl_IncrRefCount(newObjv[0]);
1189                    Tcl_IncrRefCount(newObjv[1]);
1190                    ConfigureMenuEntry(cascadePtr, 2, newObjv);
1191                    Tcl_DecrRefCount(newObjv[0]);
1192                    Tcl_DecrRefCount(newObjv[1]);
1193                }
1194            } else {
1195                ConfigureMenuEntry(cascadePtr, 0, (Tcl_Obj **) NULL);
1196            }
1197        }
1198        
1199        if (menuPtr->masterMenuPtr != menuPtr) {
1200            for (menuInstancePtr = menuPtr->masterMenuPtr;
1201                    menuInstancePtr != NULL;
1202                    menuInstancePtr = menuInstancePtr->nextInstancePtr) {
1203                if (menuInstancePtr->nextInstancePtr == menuPtr) {
1204                    menuInstancePtr->nextInstancePtr =
1205                            menuInstancePtr->nextInstancePtr->nextInstancePtr;
1206                    break;
1207                }
1208            }
1209       } else if (menuPtr->nextInstancePtr != NULL) {
1210           panic("Attempting to delete master menu when there are still clones.");
1211       }
1212    
1213        /*
1214         * Free up all the stuff that requires special handling, then
1215         * let Tk_FreeConfigOptions handle all the standard option-related
1216         * stuff.
1217         */
1218    
1219        for (i = menuPtr->numEntries; --i >= 0; ) {
1220            /*
1221             * As each menu entry is deleted from the end of the array of
1222             * entries, decrement menuPtr->numEntries.  Otherwise, the act of
1223             * deleting menu entry i will dereference freed memory attempting
1224             * to queue a redraw for menu entries (i+1)...numEntries.
1225             */
1226            
1227            DestroyMenuEntry((char *) menuPtr->entries[i]);
1228            menuPtr->numEntries = i;
1229        }
1230        if (menuPtr->entries != NULL) {
1231            ckfree((char *) menuPtr->entries);
1232        }
1233        TkMenuFreeDrawOptions(menuPtr);
1234        Tk_FreeConfigOptions((char *) menuPtr,
1235                menuPtr->optionTablesPtr->menuOptionTable, menuPtr->tkwin);
1236    }
1237    
1238    /*
1239     *----------------------------------------------------------------------
1240     *
1241     * TkDestroyMenu --
1242     *
1243     *      This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
1244     *      to clean up the internal structure of a menu at a safe time
1245     *      (when no-one is using it anymore).  If called on a master instance,
1246     *      destroys all of the slave instances. If called on a non-master
1247     *      instance, just destroys that instance.
1248     *
1249     * Results:
1250     *      None.
1251     *
1252     * Side effects:
1253     *      Everything associated with the menu is freed up.
1254     *
1255     *----------------------------------------------------------------------
1256     */
1257    
1258    void
1259    TkDestroyMenu(menuPtr)
1260        TkMenu *menuPtr;    /* Info about menu widget. */
1261    {
1262        TkMenu *menuInstancePtr;
1263        TkMenuTopLevelList *topLevelListPtr, *nextTopLevelPtr;
1264    
1265        if (menuPtr->menuFlags & MENU_DELETION_PENDING) {
1266            return;
1267        }
1268        
1269        /*
1270         * Now destroy all non-tearoff instances of this menu if this is a
1271         * parent menu. Is this loop safe enough? Are there going to be
1272         * destroy bindings on child menus which kill the parent? If not,
1273         * we have to do a slightly more complex scheme.
1274         */
1275        
1276        if (menuPtr->masterMenuPtr == menuPtr) {
1277            menuPtr->menuFlags |= MENU_DELETION_PENDING;
1278            while (menuPtr->nextInstancePtr != NULL) {
1279                menuInstancePtr = menuPtr->nextInstancePtr;
1280                menuPtr->nextInstancePtr = menuInstancePtr->nextInstancePtr;
1281                if (menuInstancePtr->tkwin != NULL) {
1282                    Tk_DestroyWindow(menuInstancePtr->tkwin);
1283                }
1284            }
1285            menuPtr->menuFlags &= ~MENU_DELETION_PENDING;
1286        }
1287    
1288        /*
1289         * If any toplevel widgets have this menu as their menubar,
1290         * the geometry of the window may have to be recalculated.
1291         */
1292        
1293        topLevelListPtr = menuPtr->menuRefPtr->topLevelListPtr;
1294        while (topLevelListPtr != NULL) {
1295             nextTopLevelPtr = topLevelListPtr->nextPtr;
1296             TkpSetWindowMenuBar(topLevelListPtr->tkwin, NULL);
1297             topLevelListPtr = nextTopLevelPtr;
1298        }  
1299        DestroyMenuInstance(menuPtr);
1300    }
1301    
1302    /*
1303     *----------------------------------------------------------------------
1304     *
1305     * UnhookCascadeEntry --
1306     *
1307     *      This entry is removed from the list of entries that point to the
1308     *      cascade menu. This is done in preparation for changing the menu
1309     *      that this entry points to.
1310     *
1311     * Results:
1312     *      None
1313     *
1314     * Side effects:
1315     *      The appropriate lists are modified.
1316     *
1317     *----------------------------------------------------------------------
1318     */
1319    
1320    static void
1321    UnhookCascadeEntry(mePtr)
1322        TkMenuEntry *mePtr;                 /* The cascade entry we are removing
1323                                             * from the cascade list. */
1324    {
1325        TkMenuEntry *cascadeEntryPtr;
1326        TkMenuEntry *prevCascadePtr;
1327        TkMenuReferences *menuRefPtr;
1328    
1329        menuRefPtr = mePtr->childMenuRefPtr;
1330        if (menuRefPtr == NULL) {
1331            return;
1332        }
1333        
1334        cascadeEntryPtr = menuRefPtr->parentEntryPtr;
1335        if (cascadeEntryPtr == NULL) {
1336            return;
1337        }
1338        
1339        /*
1340         * Singularly linked list deletion. The two special cases are
1341         * 1. one element; 2. The first element is the one we want.
1342         */
1343    
1344        if (cascadeEntryPtr == mePtr) {
1345            if (cascadeEntryPtr->nextCascadePtr == NULL) {
1346    
1347                /*
1348                 * This is the last menu entry which points to this
1349                 * menu, so we need to clear out the list pointer in the
1350                 * cascade itself.
1351                 */
1352            
1353                menuRefPtr->parentEntryPtr = NULL;
1354                TkFreeMenuReferences(menuRefPtr);
1355            } else {
1356                menuRefPtr->parentEntryPtr = cascadeEntryPtr->nextCascadePtr;
1357            }
1358            mePtr->nextCascadePtr = NULL;
1359        } else {
1360            for (prevCascadePtr = cascadeEntryPtr,
1361                    cascadeEntryPtr = cascadeEntryPtr->nextCascadePtr;
1362                    cascadeEntryPtr != NULL;
1363                    prevCascadePtr = cascadeEntryPtr,
1364                    cascadeEntryPtr = cascadeEntryPtr->nextCascadePtr) {
1365                if (cascadeEntryPtr == mePtr){
1366                    prevCascadePtr->nextCascadePtr =
1367                            cascadeEntryPtr->nextCascadePtr;
1368                    cascadeEntryPtr->nextCascadePtr = NULL;
1369                    break;
1370                }
1371            }
1372        }
1373        mePtr->childMenuRefPtr = NULL;
1374    }
1375    
1376    /*
1377     *----------------------------------------------------------------------
1378     *
1379     * DestroyMenuEntry --
1380     *
1381     *      This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
1382     *      to clean up the internal structure of a menu entry at a safe time
1383     *      (when no-one is using it anymore).
1384     *
1385     * Results:
1386     *      None.
1387     *
1388     * Side effects:
1389     *      Everything associated with the menu entry is freed.
1390     *
1391     *----------------------------------------------------------------------
1392     */
1393    
1394    static void
1395    DestroyMenuEntry(memPtr)
1396        char *memPtr;               /* Pointer to entry to be freed. */
1397    {
1398        register TkMenuEntry *mePtr = (TkMenuEntry *) memPtr;
1399        TkMenu *menuPtr = mePtr->menuPtr;
1400    
1401        if (menuPtr->postedCascade == mePtr) {
1402            
1403            /*
1404             * Ignore errors while unposting the menu, since it's possible
1405             * that the menu has already been deleted and the unpost will
1406             * generate an error.
1407             */
1408    
1409            TkPostSubmenu(menuPtr->interp, menuPtr, (TkMenuEntry *) NULL);
1410        }
1411    
1412        /*
1413         * Free up all the stuff that requires special handling, then
1414         * let Tk_FreeConfigOptions handle all the standard option-related
1415         * stuff.
1416         */
1417    
1418        if (mePtr->type == CASCADE_ENTRY) {
1419            UnhookCascadeEntry(mePtr);
1420        }
1421        if (mePtr->image != NULL) {
1422            Tk_FreeImage(mePtr->image);
1423        }
1424        if (mePtr->selectImage != NULL) {
1425            Tk_FreeImage(mePtr->selectImage);
1426        }
1427        if (((mePtr->type == CHECK_BUTTON_ENTRY)
1428                || (mePtr->type == RADIO_BUTTON_ENTRY))
1429                && (mePtr->namePtr != NULL)) {
1430            char *varName = Tcl_GetStringFromObj(mePtr->namePtr, NULL);
1431            Tcl_UntraceVar(menuPtr->interp, varName,
1432                    TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
1433                    MenuVarProc, (ClientData) mePtr);
1434        }
1435        TkpDestroyMenuEntry(mePtr);
1436        TkMenuEntryFreeDrawOptions(mePtr);
1437        Tk_FreeConfigOptions((char *) mePtr, mePtr->optionTable, menuPtr->tkwin);
1438        ckfree((char *) mePtr);
1439    }
1440    
1441    /*
1442     *---------------------------------------------------------------------------
1443     *
1444     * MenuWorldChanged --
1445     *
1446     *      This procedure is called when the world has changed in some
1447     *      way (such as the fonts in the system changing) and the widget needs
1448     *      to recompute all its graphics contexts and determine its new geometry.
1449     *
1450     * Results:
1451     *      None.
1452     *
1453     * Side effects:
1454     *      Menu will be relayed out and redisplayed.
1455     *
1456     *---------------------------------------------------------------------------
1457     */
1458    
1459    static void
1460    MenuWorldChanged(instanceData)
1461        ClientData instanceData;    /* Information about widget. */
1462    {
1463        TkMenu *menuPtr = (TkMenu *) instanceData;
1464        int i;
1465        
1466        TkMenuConfigureDrawOptions(menuPtr);
1467        for (i = 0; i < menuPtr->numEntries; i++) {
1468            TkMenuConfigureEntryDrawOptions(menuPtr->entries[i],
1469                    menuPtr->entries[i]->index);
1470            TkpConfigureMenuEntry(menuPtr->entries[i]);    
1471        }
1472    }
1473    
1474    /*
1475     *----------------------------------------------------------------------
1476     *
1477     * ConfigureMenu --
1478     *
1479     *      This procedure is called to process an argv/argc list, plus
1480     *      the Tk option database, in order to configure (or
1481     *      reconfigure) a menu widget.
1482     *
1483     * Results:
1484     *      The return value is a standard Tcl result.  If TCL_ERROR is
1485     *      returned, then the interp's result contains an error message.
1486     *
1487     * Side effects:
1488     *      Configuration information, such as colors, font, etc. get set
1489     *      for menuPtr;  old resources get freed, if there were any.
1490     *
1491     *----------------------------------------------------------------------
1492     */
1493    
1494    static int
1495    ConfigureMenu(interp, menuPtr, objc, objv)
1496        Tcl_Interp *interp;         /* Used for error reporting. */
1497        register TkMenu *menuPtr;   /* Information about widget;  may or may
1498                                     * not already have values for some fields. */
1499        int objc;                   /* Number of valid entries in argv. */
1500        Tcl_Obj *CONST objv[];      /* Arguments. */
1501    {
1502        int i;
1503        TkMenu *menuListPtr, *cleanupPtr;
1504        int result;
1505        
1506        for (menuListPtr = menuPtr->masterMenuPtr; menuListPtr != NULL;
1507                menuListPtr = menuListPtr->nextInstancePtr) {
1508            menuListPtr->errorStructPtr = (Tk_SavedOptions *)
1509                    ckalloc(sizeof(Tk_SavedOptions));
1510            result = Tk_SetOptions(interp, (char *) menuListPtr,
1511                    menuListPtr->optionTablesPtr->menuOptionTable, objc, objv,
1512                    menuListPtr->tkwin, menuListPtr->errorStructPtr, (int *) NULL);
1513            if (result != TCL_OK) {
1514                for (cleanupPtr = menuPtr->masterMenuPtr;
1515                        cleanupPtr != menuListPtr;
1516                        cleanupPtr = cleanupPtr->nextInstancePtr) {
1517                    Tk_RestoreSavedOptions(cleanupPtr->errorStructPtr);
1518                    ckfree((char *) cleanupPtr->errorStructPtr);
1519                    cleanupPtr->errorStructPtr = NULL;
1520                }
1521                return TCL_ERROR;
1522            }
1523    
1524            /*
1525             * When a menu is created, the type is in all of the arguments
1526             * to the menu command. Let Tk_ConfigureWidget take care of
1527             * parsing them, and then set the type after we can look at
1528             * the type string. Once set, a menu's type cannot be changed
1529             */
1530            
1531            if (menuListPtr->menuType == UNKNOWN_TYPE) {
1532                Tcl_GetIndexFromObj(NULL, menuListPtr->menuTypePtr,
1533                        menuTypeStrings, NULL, 0, &menuListPtr->menuType);
1534    
1535                /*
1536                 * Configure the new window to be either a pop-up menu
1537                 * or a tear-off menu.
1538                 * We don't do this for menubars since they are not toplevel
1539                 * windows. Also, since this gets called before CloneMenu has
1540                 * a chance to set the menuType field, we have to look at the
1541                 * menuTypeName field to tell that this is a menu bar.
1542                 */
1543                
1544                if (menuListPtr->menuType == MASTER_MENU) {
1545                    TkpMakeMenuWindow(menuListPtr->tkwin, 1);
1546                } else if (menuListPtr->menuType == TEAROFF_MENU) {
1547                    TkpMakeMenuWindow(menuListPtr->tkwin, 0);
1548                }
1549            }
1550    
1551    
1552            /*
1553             * Depending on the -tearOff option, make sure that there is or
1554             * isn't an initial tear-off entry at the beginning of the menu.
1555             */
1556            
1557            if (menuListPtr->tearoff) {
1558                if ((menuListPtr->numEntries == 0)
1559                        || (menuListPtr->entries[0]->type != TEAROFF_ENTRY)) {
1560                    if (MenuNewEntry(menuListPtr, 0, TEAROFF_ENTRY) == NULL) {
1561                        if (menuListPtr->errorStructPtr != NULL) {
1562                            for (cleanupPtr = menuPtr->masterMenuPtr;
1563                                    cleanupPtr != menuListPtr;
1564                                    cleanupPtr = cleanupPtr->nextInstancePtr) {
1565                                Tk_RestoreSavedOptions(cleanupPtr->errorStructPtr);
1566                                ckfree((char *) cleanupPtr->errorStructPtr);
1567                                cleanupPtr->errorStructPtr = NULL;
1568                            }
1569                            Tk_RestoreSavedOptions(cleanupPtr->errorStructPtr);
1570                            ckfree((char *) cleanupPtr->errorStructPtr);
1571                            cleanupPtr->errorStructPtr = NULL;
1572                        }
1573                        return TCL_ERROR;
1574                    }
1575                }
1576            } else if ((menuListPtr->numEntries > 0)
1577                    && (menuListPtr->entries[0]->type == TEAROFF_ENTRY)) {
1578                int i;
1579                
1580                Tcl_EventuallyFree((ClientData) menuListPtr->entries[0],
1581                        DestroyMenuEntry);
1582    
1583                for (i = 0; i < menuListPtr->numEntries - 1; i++) {
1584                    menuListPtr->entries[i] = menuListPtr->entries[i + 1];
1585                    menuListPtr->entries[i]->index = i;
1586                }
1587                menuListPtr->numEntries--;
1588                if (menuListPtr->numEntries == 0) {
1589                    ckfree((char *) menuListPtr->entries);
1590                    menuListPtr->entries = NULL;
1591                }
1592            }
1593    
1594            TkMenuConfigureDrawOptions(menuListPtr);
1595            
1596            /*
1597             * After reconfiguring a menu, we need to reconfigure all of the
1598             * entries in the menu, since some of the things in the children
1599             * (such as graphics contexts) may have to change to reflect changes
1600             * in the parent.
1601             */
1602            
1603            for (i = 0; i < menuListPtr->numEntries; i++) {
1604                TkMenuEntry *mePtr;
1605            
1606                mePtr = menuListPtr->entries[i];
1607                ConfigureMenuEntry(mePtr, 0, (Tcl_Obj **) NULL);
1608            }
1609            
1610            TkEventuallyRecomputeMenu(menuListPtr);
1611        }
1612    
1613        for (cleanupPtr = menuPtr->masterMenuPtr; cleanupPtr != NULL;
1614                cleanupPtr = cleanupPtr->nextInstancePtr) {
1615            Tk_FreeSavedOptions(cleanupPtr->errorStructPtr);
1616            ckfree((char *) cleanupPtr->errorStructPtr);
1617            cleanupPtr->errorStructPtr = NULL;
1618        }
1619    
1620        return TCL_OK;
1621    }
1622    
1623    
1624    /*
1625     *----------------------------------------------------------------------
1626     *
1627     * PostProcessEntry --
1628     *
1629     *      This is called by ConfigureMenuEntry to do all of the configuration
1630     *      after Tk_SetOptions is called. This is separate
1631     *      so that error handling is easier.
1632     *
1633     * Results:
1634     *      The return value is a standard Tcl result.  If TCL_ERROR is
1635     *      returned, then the interp's result contains an error message.
1636     *
1637     * Side effects:
1638     *      Configuration information such as label and accelerator get
1639     *      set for mePtr;  old resources get freed, if there were any.
1640     *
1641     *----------------------------------------------------------------------
1642     */
1643    
1644    static int
1645    PostProcessEntry(mePtr)
1646        TkMenuEntry *mePtr;                 /* The entry we are configuring. */
1647    {
1648        TkMenu *menuPtr = mePtr->menuPtr;
1649        int index = mePtr->index;
1650        char *name;
1651        Tk_Image image;
1652    
1653        /*
1654         * The code below handles special configuration stuff not taken
1655         * care of by Tk_ConfigureWidget, such as special processing for
1656         * defaults, sizing strings, graphics contexts, etc.
1657         */
1658    
1659        if (mePtr->labelPtr == NULL) {
1660            mePtr->labelLength = 0;
1661        } else {
1662            Tcl_GetStringFromObj(mePtr->labelPtr, &mePtr->labelLength);
1663        }
1664        if (mePtr->accelPtr == NULL) {
1665            mePtr->accelLength = 0;
1666        } else {
1667            Tcl_GetStringFromObj(mePtr->accelPtr, &mePtr->accelLength);
1668        }
1669    
1670        /*
1671         * If this is a cascade entry, the platform-specific data of the child
1672         * menu has to be updated. Also, the links that point to parents and
1673         * cascades have to be updated.
1674         */
1675    
1676        if ((mePtr->type == CASCADE_ENTRY) && (mePtr->namePtr != NULL)) {
1677            TkMenuEntry *cascadeEntryPtr;
1678            int alreadyThere;
1679            TkMenuReferences *menuRefPtr;
1680            char *oldHashKey = NULL;        /* Initialization only needed to
1681                                             * prevent compiler warning. */
1682    
1683            /*
1684             * This is a cascade entry. If the menu that the cascade entry
1685             * is pointing to has changed, we need to remove this entry
1686             * from the list of entries pointing to the old menu, and add a
1687             * cascade reference to the list of entries pointing to the
1688             * new menu.
1689             *
1690             * BUG: We are not recloning for special case #3 yet.
1691             */
1692            
1693            name = Tcl_GetStringFromObj(mePtr->namePtr, NULL);
1694            if (mePtr->childMenuRefPtr != NULL) {
1695                oldHashKey = Tcl_GetHashKey(TkGetMenuHashTable(menuPtr->interp),
1696                        mePtr->childMenuRefPtr->hashEntryPtr);
1697                if (strcmp(oldHashKey, name) != 0) {
1698                    UnhookCascadeEntry(mePtr);
1699                }
1700            }
1701    
1702            if ((mePtr->childMenuRefPtr == NULL)
1703                    || (strcmp(oldHashKey, name) != 0)) {
1704                menuRefPtr = TkCreateMenuReferences(menuPtr->interp, name);
1705                mePtr->childMenuRefPtr = menuRefPtr;
1706    
1707                if (menuRefPtr->parentEntryPtr == NULL) {
1708                    menuRefPtr->parentEntryPtr = mePtr;
1709                } else {
1710                    alreadyThere = 0;
1711                    for (cascadeEntryPtr = menuRefPtr->parentEntryPtr;
1712                            cascadeEntryPtr != NULL;
1713                            cascadeEntryPtr =
1714                            cascadeEntryPtr->nextCascadePtr) {
1715                        if (cascadeEntryPtr == mePtr) {
1716                            alreadyThere = 1;
1717                            break;
1718                        }
1719                    }
1720        
1721                    /*
1722                     * Put the item at the front of the list.
1723                     */
1724                
1725                    if (!alreadyThere) {
1726                        mePtr->nextCascadePtr = menuRefPtr->parentEntryPtr;
1727                        menuRefPtr->parentEntryPtr = mePtr;
1728                    }
1729                }
1730            }
1731        }
1732        
1733        if (TkMenuConfigureEntryDrawOptions(mePtr, index) != TCL_OK) {
1734            return TCL_ERROR;
1735        }
1736    
1737        if (TkpConfigureMenuEntry(mePtr) != TCL_OK) {
1738            return TCL_ERROR;
1739        }
1740        
1741        /*
1742         * Get the images for the entry, if there are any.  Allocate the
1743         * new images before freeing the old ones, so that the reference
1744         * counts don't go to zero and cause image data to be discarded.
1745         */
1746    
1747        if (mePtr->imagePtr != NULL) {
1748            char *imageString = Tcl_GetStringFromObj(mePtr->imagePtr, NULL);
1749            image = Tk_GetImage(menuPtr->interp, menuPtr->tkwin, imageString,
1750                    TkMenuImageProc, (ClientData) mePtr);
1751            if (image == NULL) {
1752                return TCL_ERROR;
1753            }
1754        } else {
1755            image = NULL;
1756        }
1757        if (mePtr->image != NULL) {
1758            Tk_FreeImage(mePtr->image);
1759        }
1760        mePtr->image = image;
1761        if (mePtr->selectImagePtr != NULL) {
1762            char *selectImageString = Tcl_GetStringFromObj(
1763                    mePtr->selectImagePtr, NULL);
1764            image = Tk_GetImage(menuPtr->interp, menuPtr->tkwin, selectImageString,
1765                    TkMenuSelectImageProc, (ClientData) mePtr);
1766            if (image == NULL) {
1767                return TCL_ERROR;
1768            }
1769        } else {
1770            image = NULL;
1771        }
1772        if (mePtr->selectImage != NULL) {
1773            Tk_FreeImage(mePtr->selectImage);
1774        }
1775        mePtr->selectImage = image;
1776    
1777        if ((mePtr->type == CHECK_BUTTON_ENTRY)
1778                || (mePtr->type == RADIO_BUTTON_ENTRY)) {
1779            Tcl_Obj *valuePtr;
1780            char *name;
1781    
1782            if (mePtr->namePtr == NULL) {
1783                if (mePtr->labelPtr == NULL) {
1784                    mePtr->namePtr = NULL;
1785                } else {
1786                    mePtr->namePtr = Tcl_DuplicateObj(mePtr->labelPtr);
1787                    Tcl_IncrRefCount(mePtr->namePtr);
1788                }
1789            }
1790            if (mePtr->onValuePtr == NULL) {
1791                if (mePtr->labelPtr == NULL) {
1792                    mePtr->onValuePtr = NULL;
1793                } else {
1794                    mePtr->onValuePtr = Tcl_DuplicateObj(mePtr->labelPtr);
1795                    Tcl_IncrRefCount(mePtr->onValuePtr);
1796                }
1797            }
1798    
1799            /*
1800             * Select the entry if the associated variable has the
1801             * appropriate value, initialize the variable if it doesn't
1802             * exist, then set a trace on the variable to monitor future
1803             * changes to its value.
1804             */
1805            
1806            if (mePtr->namePtr != NULL) {
1807                valuePtr = Tcl_ObjGetVar2(menuPtr->interp, mePtr->namePtr, NULL,
1808                        TCL_GLOBAL_ONLY);
1809            } else {
1810                valuePtr = NULL;
1811            }
1812            mePtr->entryFlags &= ~ENTRY_SELECTED;
1813            if (valuePtr != NULL) {
1814                if (mePtr->onValuePtr != NULL) {
1815                    char *value = Tcl_GetStringFromObj(valuePtr, NULL);
1816                    char *onValue = Tcl_GetStringFromObj(mePtr->onValuePtr,
1817                            NULL);
1818    
1819    
1820                    if (strcmp(value, onValue) == 0) {
1821                        mePtr->entryFlags |= ENTRY_SELECTED;
1822                    }
1823                }
1824            } else {
1825                if (mePtr->namePtr != NULL) {
1826                    Tcl_ObjSetVar2(menuPtr->interp, mePtr->namePtr, NULL,
1827                            (mePtr->type == CHECK_BUTTON_ENTRY)
1828                            ? mePtr->offValuePtr
1829                            : Tcl_NewObj(),
1830                            TCL_GLOBAL_ONLY);
1831                }
1832            }
1833            if (mePtr->namePtr != NULL) {
1834                name = Tcl_GetStringFromObj(mePtr->namePtr, NULL);
1835                Tcl_TraceVar(menuPtr->interp, name,
1836                        TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
1837                        MenuVarProc, (ClientData) mePtr);
1838            }
1839        }
1840        
1841        return TCL_OK;
1842    }
1843    
1844    /*
1845     *----------------------------------------------------------------------
1846     *
1847     * ConfigureMenuEntry --
1848     *
1849     *      This procedure is called to process an argv/argc list in order
1850     *      to configure (or reconfigure) one entry in a menu.
1851     *
1852     * Results:
1853     *      The return value is a standard Tcl result.  If TCL_ERROR is
1854     *      returned, then the interp's result contains an error message.
1855     *
1856     * Side effects:
1857     *      Configuration information such as label and accelerator get
1858     *      set for mePtr;  old resources get freed, if there were any.
1859     *
1860     *----------------------------------------------------------------------
1861     */
1862    
1863    static int
1864    ConfigureMenuEntry(mePtr, objc, objv)
1865        register TkMenuEntry *mePtr;        /* Information about menu entry;  may
1866                                             * or may not already have values for
1867                                             * some fields. */
1868        int objc;                           /* Number of valid entries in argv. */
1869        Tcl_Obj *CONST objv[];              /* Arguments. */
1870    {
1871        TkMenu *menuPtr = mePtr->menuPtr;
1872        Tk_SavedOptions errorStruct;
1873        int result;
1874    
1875        /*
1876         * If this entry is a check button or radio button, then remove
1877         * its old trace procedure.
1878         */
1879    
1880        if ((mePtr->namePtr != NULL)
1881                && ((mePtr->type == CHECK_BUTTON_ENTRY)
1882                || (mePtr->type == RADIO_BUTTON_ENTRY))) {
1883            char *name = Tcl_GetStringFromObj(mePtr->namePtr, NULL);
1884            Tcl_UntraceVar(menuPtr->interp, name,
1885                    TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
1886                    MenuVarProc, (ClientData) mePtr);
1887        }
1888    
1889        result = TCL_OK;
1890        if (menuPtr->tkwin != NULL) {
1891            if (Tk_SetOptions(menuPtr->interp, (char *) mePtr,
1892                    mePtr->optionTable, objc, objv, menuPtr->tkwin,
1893                    &errorStruct, (int *) NULL) != TCL_OK) {
1894                return TCL_ERROR;
1895            }
1896            result = PostProcessEntry(mePtr);
1897            if (result != TCL_OK) {
1898                Tk_RestoreSavedOptions(&errorStruct);
1899                PostProcessEntry(mePtr);
1900            }
1901            Tk_FreeSavedOptions(&errorStruct);
1902        }
1903    
1904        TkEventuallyRecomputeMenu(menuPtr);
1905        
1906        return result;
1907    }
1908    
1909    /*
1910     *----------------------------------------------------------------------
1911     *
1912     * ConfigureMenuCloneEntries --
1913     *
1914     *      Calls ConfigureMenuEntry for each menu in the clone chain.
1915     *
1916     * Results:
1917     *      The return value is a standard Tcl result.  If TCL_ERROR is
1918     *      returned, then the interp's result contains an error message.
1919     *
1920     * Side effects:
1921     *      Configuration information such as label and accelerator get
1922     *      set for mePtr;  old resources get freed, if there were any.
1923     *
1924     *----------------------------------------------------------------------
1925     */
1926    
1927    static int
1928    ConfigureMenuCloneEntries(interp, menuPtr, index, objc, objv)
1929        Tcl_Interp *interp;                 /* Used for error reporting. */
1930        TkMenu *menuPtr;                    /* Information about whole menu. */
1931        int index;                          /* Index of mePtr within menuPtr's
1932                                             * entries. */
1933        int objc;                           /* Number of valid entries in argv. */
1934        Tcl_Obj *CONST objv[];              /* Arguments. */
1935    {
1936        TkMenuEntry *mePtr;
1937        TkMenu *menuListPtr;
1938        int cascadeEntryChanged = 0;
1939        TkMenuReferences *oldCascadeMenuRefPtr, *cascadeMenuRefPtr = NULL;
1940        Tcl_Obj *oldCascadePtr = NULL;
1941        char *newCascadeName;
1942    
1943        /*
1944         * Cascades are kind of tricky here. This is special case #3 in the comment
1945         * at the top of this file. Basically, if a menu is the master menu of a
1946         * clone chain, and has an entry with a cascade menu, the clones of
1947         * the menu will point to clones of the cascade menu. We have
1948         * to destroy the clones of the cascades, clone the new cascade
1949         * menu, and configure the entry to point to the new clone.
1950         */
1951    
1952        mePtr = menuPtr->masterMenuPtr->entries[index];
1953        if (mePtr->type == CASCADE_ENTRY) {
1954            oldCascadePtr = mePtr->namePtr;
1955            if (oldCascadePtr != NULL) {
1956                Tcl_IncrRefCount(oldCascadePtr);
1957            }
1958        }
1959    
1960        if (ConfigureMenuEntry(mePtr, objc, objv) != TCL_OK) {
1961            return TCL_ERROR;
1962        }
1963    
1964        if (mePtr->type == CASCADE_ENTRY) {
1965            char *oldCascadeName;
1966    
1967            if (mePtr->namePtr != NULL) {
1968                newCascadeName = Tcl_GetStringFromObj(mePtr->namePtr, NULL);
1969            } else {
1970                newCascadeName = NULL;
1971            }
1972    
1973            if ((oldCascadePtr == NULL) && (mePtr->namePtr == NULL)) {
1974                cascadeEntryChanged = 0;
1975            } else if (((oldCascadePtr == NULL) && (mePtr->namePtr != NULL))
1976                    || ((oldCascadePtr != NULL)
1977                    && (mePtr->namePtr == NULL))) {
1978                cascadeEntryChanged = 1;
1979            } else {
1980                oldCascadeName = Tcl_GetStringFromObj(oldCascadePtr,
1981                        NULL);
1982                cascadeEntryChanged = (strcmp(oldCascadeName, newCascadeName)
1983                        == 0);
1984            }
1985            if (oldCascadePtr != NULL) {
1986                Tcl_DecrRefCount(oldCascadePtr);
1987            }
1988        }
1989    
1990        if (cascadeEntryChanged) {
1991            if (mePtr->namePtr != NULL) {
1992                newCascadeName = Tcl_GetStringFromObj(mePtr->namePtr, NULL);
1993                cascadeMenuRefPtr = TkFindMenuReferences(menuPtr->interp,
1994                        newCascadeName);
1995            }
1996        }
1997    
1998        for (menuListPtr = menuPtr->masterMenuPtr->nextInstancePtr;
1999                menuListPtr != NULL;
2000                menuListPtr = menuListPtr->nextInstancePtr) {
2001            
2002            mePtr = menuListPtr->entries[index];
2003    
2004            if (cascadeEntryChanged && (mePtr->namePtr != NULL)) {
2005                oldCascadeMenuRefPtr = TkFindMenuReferencesObj(menuPtr->interp,
2006                        mePtr->namePtr);
2007    
2008                if ((oldCascadeMenuRefPtr != NULL)
2009                        && (oldCascadeMenuRefPtr->menuPtr != NULL)) {
2010                    RecursivelyDeleteMenu(oldCascadeMenuRefPtr->menuPtr);
2011                }
2012            }
2013    
2014            if (ConfigureMenuEntry(mePtr, objc, objv) != TCL_OK) {
2015                return TCL_ERROR;
2016            }
2017            
2018            if (cascadeEntryChanged && (mePtr->namePtr != NULL)) {
2019                if (cascadeMenuRefPtr->menuPtr != NULL) {
2020                    Tcl_Obj *newObjv[2];
2021                    Tcl_Obj *newCloneNamePtr;
2022                    Tcl_Obj *pathNamePtr = Tcl_NewStringObj(
2023                            Tk_PathName(menuListPtr->tkwin), -1);
2024                    Tcl_Obj *normalPtr = Tcl_NewStringObj("normal", -1);
2025                    Tcl_Obj *menuObjPtr = Tcl_NewStringObj("-menu", -1);
2026    
2027                    Tcl_IncrRefCount(pathNamePtr);
2028                    newCloneNamePtr = TkNewMenuName(menuPtr->interp,
2029                            pathNamePtr,
2030                            cascadeMenuRefPtr->menuPtr);
2031                    Tcl_IncrRefCount(newCloneNamePtr);
2032                    Tcl_IncrRefCount(normalPtr);
2033                    CloneMenu(cascadeMenuRefPtr->menuPtr, newCloneNamePtr,
2034                            normalPtr);
2035    
2036                    newObjv[0] = menuObjPtr;
2037                    newObjv[1] = newCloneNamePtr;
2038                    Tcl_IncrRefCount(menuObjPtr);
2039                    ConfigureMenuEntry(mePtr, 2, newObjv);
2040                    Tcl_DecrRefCount(newCloneNamePtr);
2041                    Tcl_DecrRefCount(pathNamePtr);
2042                    Tcl_DecrRefCount(normalPtr);
2043                    Tcl_DecrRefCount(menuObjPtr);
2044                }
2045            }
2046        }
2047        return TCL_OK;
2048    }
2049    
2050    /*
2051     *--------------------------------------------------------------
2052     *
2053     * TkGetMenuIndex --
2054     *
2055     *      Parse a textual index into a menu and return the numerical
2056     *      index of the indicated entry.
2057     *
2058     * Results:
2059     *      A standard Tcl result.  If all went well, then *indexPtr is
2060     *      filled in with the entry index corresponding to string
2061     *      (ranges from -1 to the number of entries in the menu minus
2062     *      one).  Otherwise an error message is left in the interp's result.
2063     *
2064     * Side effects:
2065     *      None.
2066     *
2067     *--------------------------------------------------------------
2068     */
2069    
2070    int
2071    TkGetMenuIndex(interp, menuPtr, objPtr, lastOK, indexPtr)
2072        Tcl_Interp *interp;         /* For error messages. */
2073        TkMenu *menuPtr;            /* Menu for which the index is being
2074                                     * specified. */
2075        Tcl_Obj *objPtr;            /* Specification of an entry in menu.  See
2076                                     * manual entry for valid .*/
2077        int lastOK;                 /* Non-zero means its OK to return index
2078                                     * just *after* last entry. */
2079        int *indexPtr;              /* Where to store converted index. */
2080    {
2081        int i;
2082        char *string = Tcl_GetStringFromObj(objPtr, NULL);
2083    
2084        if ((string[0] == 'a') && (strcmp(string, "active") == 0)) {
2085            *indexPtr = menuPtr->active;
2086            goto success;
2087        }
2088    
2089        if (((string[0] == 'l') && (strcmp(string, "last") == 0))
2090                || ((string[0] == 'e') && (strcmp(string, "end") == 0))) {
2091            *indexPtr = menuPtr->numEntries - ((lastOK) ? 0 : 1);
2092            goto success;
2093        }
2094    
2095        if ((string[0] == 'n') && (strcmp(string, "none") == 0)) {
2096            *indexPtr = -1;
2097            goto success;
2098        }
2099    
2100        if (string[0] == '@') {
2101            if (GetIndexFromCoords(interp, menuPtr, string, indexPtr)
2102                    == TCL_OK) {
2103                goto success;
2104            }
2105        }
2106    
2107        if (isdigit(UCHAR(string[0]))) {
2108            if (Tcl_GetInt(interp, string,  &i) == TCL_OK) {
2109                if (i >= menuPtr->numEntries) {
2110                    if (lastOK) {
2111                        i = menuPtr->numEntries;
2112                    } else {
2113                        i = menuPtr->numEntries-1;
2114                    }
2115                } else if (i < 0) {
2116                    i = -1;
2117                }
2118                *indexPtr = i;
2119                goto success;
2120            }
2121            Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);
2122        }
2123    
2124        for (i = 0; i < menuPtr->numEntries; i++) {
2125            Tcl_Obj *labelPtr = menuPtr->entries[i]->labelPtr;
2126            char *label = (labelPtr == NULL) ? NULL
2127                    : Tcl_GetStringFromObj(labelPtr, NULL);
2128            
2129            if ((label != NULL)
2130                    && (Tcl_StringMatch(label, string))) {
2131                *indexPtr = i;
2132                goto success;
2133            }
2134        }
2135    
2136        Tcl_AppendResult(interp, "bad menu entry index \"",
2137                string, "\"", (char *) NULL);
2138        return TCL_ERROR;
2139    
2140    success:
2141        return TCL_OK;
2142    }
2143    
2144    /*
2145     *----------------------------------------------------------------------
2146     *
2147     * MenuCmdDeletedProc --
2148     *
2149     *      This procedure is invoked when a widget command is deleted.  If
2150     *      the widget isn't already in the process of being destroyed,
2151     *      this command destroys it.
2152     *
2153     * Results:
2154     *      None.
2155     *
2156     * Side effects:
2157     *      The widget is destroyed.
2158     *
2159     *----------------------------------------------------------------------
2160     */
2161    
2162    static void
2163    MenuCmdDeletedProc(clientData)
2164        ClientData clientData;      /* Pointer to widget record for widget. */
2165    {
2166        TkMenu *menuPtr = (TkMenu *) clientData;
2167        Tk_Window tkwin = menuPtr->tkwin;
2168    
2169        /*
2170         * This procedure could be invoked either because the window was
2171         * destroyed and the command was then deleted (in which case tkwin
2172         * is NULL) or because the command was deleted, and then this procedure
2173         * destroys the widget.
2174         */
2175    
2176        if (tkwin != NULL) {
2177            Tk_DestroyWindow(tkwin);
2178        }
2179    }
2180    
2181    /*
2182     *----------------------------------------------------------------------
2183     *
2184     * MenuNewEntry --
2185     *
2186     *      This procedure allocates and initializes a new menu entry.
2187     *
2188     * Results:
2189     *      The return value is a pointer to a new menu entry structure,
2190     *      which has been malloc-ed, initialized, and entered into the
2191     *      entry array for the  menu.
2192     *
2193     * Side effects:
2194     *      Storage gets allocated.
2195     *
2196     *----------------------------------------------------------------------
2197     */
2198    
2199    static TkMenuEntry *
2200    MenuNewEntry(menuPtr, index, type)
2201        TkMenu *menuPtr;            /* Menu that will hold the new entry. */
2202        int index;                  /* Where in the menu the new entry is to
2203                                     * go. */
2204        int type;                   /* The type of the new entry. */
2205    {
2206        TkMenuEntry *mePtr;
2207        TkMenuEntry **newEntries;
2208        int i;
2209    
2210        /*
2211         * Create a new array of entries with an empty slot for the
2212         * new entry.
2213         */
2214    
2215        newEntries = (TkMenuEntry **) ckalloc((unsigned)
2216                ((menuPtr->numEntries+1)*sizeof(TkMenuEntry *)));
2217        for (i = 0; i < index; i++) {
2218            newEntries[i] = menuPtr->entries[i];
2219        }
2220        for (  ; i < menuPtr->numEntries; i++) {
2221            newEntries[i+1] = menuPtr->entries[i];
2222            newEntries[i+1]->index = i + 1;
2223        }
2224        if (menuPtr->numEntries != 0) {
2225            ckfree((char *) menuPtr->entries);
2226        }
2227        menuPtr->entries = newEntries;
2228        menuPtr->numEntries++;
2229        mePtr = (TkMenuEntry *) ckalloc(sizeof(TkMenuEntry));
2230        menuPtr->entries[index] = mePtr;
2231        mePtr->type = type;
2232        mePtr->optionTable = menuPtr->optionTablesPtr->entryOptionTables[type];
2233        mePtr->menuPtr = menuPtr;
2234        mePtr->labelPtr = NULL;
2235        mePtr->labelLength = 0;
2236        mePtr->underline = -1;
2237        mePtr->bitmapPtr = NULL;
2238        mePtr->imagePtr = NULL;
2239        mePtr->image = NULL;
2240        mePtr->selectImagePtr = NULL;
2241        mePtr->selectImage = NULL;
2242        mePtr->accelPtr = NULL;
2243        mePtr->accelLength = 0;
2244        mePtr->state = ENTRY_DISABLED;
2245        mePtr->borderPtr = NULL;
2246        mePtr->fgPtr = NULL;
2247        mePtr->activeBorderPtr = NULL;
2248        mePtr->activeFgPtr = NULL;
2249        mePtr->fontPtr = NULL;
2250        mePtr->indicatorOn = 0;
2251        mePtr->indicatorFgPtr = NULL;
2252        mePtr->columnBreak = 0;
2253        mePtr->hideMargin = 0;
2254        mePtr->commandPtr = NULL;
2255        mePtr->namePtr = NULL;
2256        mePtr->childMenuRefPtr = NULL;
2257        mePtr->onValuePtr = NULL;
2258        mePtr->offValuePtr = NULL;
2259        mePtr->entryFlags = 0;
2260        mePtr->index = index;
2261        mePtr->nextCascadePtr = NULL;
2262        if (Tk_InitOptions(menuPtr->interp, (char *) mePtr,
2263                mePtr->optionTable, menuPtr->tkwin) != TCL_OK) {
2264            ckfree((char *) mePtr);
2265            return NULL;
2266        }
2267        TkMenuInitializeEntryDrawingFields(mePtr);
2268        if (TkpMenuNewEntry(mePtr) != TCL_OK) {
2269            Tk_FreeConfigOptions((char *) mePtr, mePtr->optionTable,
2270                    menuPtr->tkwin);
2271            ckfree((char *) mePtr);
2272            return NULL;
2273        }
2274    
2275        return mePtr;
2276    }
2277    
2278    /*
2279     *----------------------------------------------------------------------
2280     *
2281     * MenuAddOrInsert --
2282     *
2283     *      This procedure does all of the work of the "add" and "insert"
2284     *      widget commands, allowing the code for these to be shared.
2285     *
2286     * Results:
2287     *      A standard Tcl return value.
2288     *
2289     * Side effects:
2290     *      A new menu entry is created in menuPtr.
2291     *
2292     *----------------------------------------------------------------------
2293     */
2294    
2295    static int
2296    MenuAddOrInsert(interp, menuPtr, indexPtr, objc, objv)
2297        Tcl_Interp *interp;                 /* Used for error reporting. */
2298        TkMenu *menuPtr;                    /* Widget in which to create new
2299                                             * entry. */
2300        Tcl_Obj *indexPtr;                  /* Object describing index at which
2301                                             * to insert.  NULL means insert at
2302                                             * end. */
2303        int objc;                           /* Number of elements in objv. */
2304        Tcl_Obj *CONST objv[];              /* Arguments to command:  first arg
2305                                             * is type of entry, others are
2306                                             * config options. */
2307    {
2308        int type, index;
2309        TkMenuEntry *mePtr;
2310        TkMenu *menuListPtr;
2311    
2312        if (indexPtr != NULL) {
2313            if (TkGetMenuIndex(interp, menuPtr, indexPtr, 1, &index)
2314                    != TCL_OK) {
2315                return TCL_ERROR;
2316            }
2317        } else {
2318            index = menuPtr->numEntries;
2319        }
2320        if (index < 0) {
2321            char *indexString = Tcl_GetStringFromObj(indexPtr, NULL);
2322            Tcl_AppendResult(interp, "bad index \"", indexString, "\"",
2323                     (char *) NULL);
2324            return TCL_ERROR;
2325        }
2326        if (menuPtr->tearoff && (index == 0)) {
2327            index = 1;
2328        }
2329    
2330        /*
2331         * Figure out the type of the new entry.
2332         */
2333    
2334        if (Tcl_GetIndexFromObj(interp, objv[0], menuEntryTypeStrings,
2335                "menu entry type", 0, &type) != TCL_OK) {
2336            return TCL_ERROR;
2337        }
2338    
2339        /*
2340         * Now we have to add an entry for every instance related to this menu.
2341         */
2342    
2343        for (menuListPtr = menuPtr->masterMenuPtr; menuListPtr != NULL;
2344                menuListPtr = menuListPtr->nextInstancePtr) {
2345            
2346            mePtr = MenuNewEntry(menuListPtr, index, type);
2347            if (mePtr == NULL) {
2348                return TCL_ERROR;
2349            }
2350            if (ConfigureMenuEntry(mePtr, objc - 1, objv + 1) != TCL_OK) {
2351                TkMenu *errorMenuPtr;
2352                int i;
2353    
2354                for (errorMenuPtr = menuPtr->masterMenuPtr;
2355                        errorMenuPtr != NULL;
2356                        errorMenuPtr = errorMenuPtr->nextInstancePtr) {
2357                    Tcl_EventuallyFree((ClientData) errorMenuPtr->entries[index],
2358                            DestroyMenuEntry);
2359                    for (i = index; i < errorMenuPtr->numEntries - 1; i++) {
2360                        errorMenuPtr->entries[i] = errorMenuPtr->entries[i + 1];
2361                        errorMenuPtr->entries[i]->index = i;
2362                    }
2363                    errorMenuPtr->numEntries--;
2364                    if (errorMenuPtr->numEntries == 0) {
2365                        ckfree((char *) errorMenuPtr->entries);
2366                        errorMenuPtr->entries = NULL;
2367                    }
2368                    if (errorMenuPtr == menuListPtr) {
2369                        break;
2370                    }
2371                }
2372                return TCL_ERROR;
2373            }
2374            
2375            /*
2376             * If a menu has cascades, then every instance of the menu has
2377             * to have its own parallel cascade structure. So adding an
2378             * entry to a menu with clones means that the menu that the
2379             * entry points to has to be cloned for every clone the
2380             * master menu has. This is special case #2 in the comment
2381             * at the top of this file.
2382             */
2383    
2384            if ((menuPtr != menuListPtr) && (type == CASCADE_ENTRY)) {          
2385                if ((mePtr->namePtr != NULL)
2386                        && (mePtr->childMenuRefPtr != NULL)
2387                        && (mePtr->childMenuRefPtr->menuPtr != NULL)) {
2388                    TkMenu *cascadeMenuPtr =
2389                            mePtr->childMenuRefPtr->menuPtr->masterMenuPtr;
2390                    Tcl_Obj *newCascadePtr;
2391                    Tcl_Obj *menuNamePtr = Tcl_NewStringObj("-menu", -1);
2392                    Tcl_Obj *windowNamePtr =
2393                            Tcl_NewStringObj(Tk_PathName(menuListPtr->tkwin), -1);
2394                    Tcl_Obj *normalPtr = Tcl_NewStringObj("normal", -1);
2395                    Tcl_Obj *newObjv[2];
2396                    TkMenuReferences *menuRefPtr;
2397                      
2398                    Tcl_IncrRefCount(windowNamePtr);
2399                    newCascadePtr = TkNewMenuName(menuListPtr->interp,
2400                            windowNamePtr, cascadeMenuPtr);
2401                    Tcl_IncrRefCount(newCascadePtr);
2402                    Tcl_IncrRefCount(normalPtr);
2403                    CloneMenu(cascadeMenuPtr, newCascadePtr, normalPtr);
2404                    
2405                    menuRefPtr = TkFindMenuReferencesObj(menuListPtr->interp,
2406                            newCascadePtr);
2407                    if (menuRefPtr == NULL) {
2408                        panic("CloneMenu failed inside of MenuAddOrInsert.");
2409                    }
2410                    newObjv[0] = menuNamePtr;
2411                    newObjv[1] = newCascadePtr;
2412                    Tcl_IncrRefCount(menuNamePtr);
2413                    Tcl_IncrRefCount(newCascadePtr);
2414                    ConfigureMenuEntry(mePtr, 2, newObjv);
2415                    Tcl_DecrRefCount(newCascadePtr);
2416                    Tcl_DecrRefCount(menuNamePtr);
2417                    Tcl_DecrRefCount(windowNamePtr);
2418                    Tcl_DecrRefCount(normalPtr);
2419                }
2420            }
2421        }
2422        return TCL_OK;
2423    }
2424    
2425    /*
2426     *--------------------------------------------------------------
2427     *
2428     * MenuVarProc --
2429     *
2430     *      This procedure is invoked when someone changes the
2431     *      state variable associated with a radiobutton or checkbutton
2432     *      menu entry.  The entry's selected state is set to match
2433     *      the value of the variable.
2434     *
2435     * Results:
2436     *      NULL is always returned.
2437     *
2438     * Side effects:
2439     *      The menu entry may become selected or deselected.
2440     *
2441     *--------------------------------------------------------------
2442     */
2443    
2444    static char *
2445    MenuVarProc(clientData, interp, name1, name2, flags)
2446        ClientData clientData;      /* Information about menu entry. */
2447        Tcl_Interp *interp;         /* Interpreter containing variable. */
2448        char *name1;                /* First part of variable's name. */
2449        char *name2;                /* Second part of variable's name. */
2450        int flags;                  /* Describes what just happened. */
2451    {
2452        TkMenuEntry *mePtr = (TkMenuEntry *) clientData;
2453        TkMenu *menuPtr;
2454        char *value;
2455        char *name = Tcl_GetStringFromObj(mePtr->namePtr, NULL);
2456        char *onValue;
2457    
2458        menuPtr = mePtr->menuPtr;
2459    
2460        /*
2461         * If the variable is being unset, then re-establish the
2462         * trace unless the whole interpreter is going away.
2463         */
2464    
2465        if (flags & TCL_TRACE_UNSETS) {
2466            mePtr->entryFlags &= ~ENTRY_SELECTED;
2467            if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
2468                Tcl_TraceVar(interp, name,
2469                        TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
2470                        MenuVarProc, clientData);
2471            }
2472            TkpConfigureMenuEntry(mePtr);
2473            TkEventuallyRedrawMenu(menuPtr, (TkMenuEntry *) NULL);
2474            return (char *) NULL;
2475        }
2476    
2477        /*
2478         * Use the value of the variable to update the selected status of
2479         * the menu entry.
2480         */
2481    
2482        value = Tcl_GetVar(interp, name, TCL_GLOBAL_ONLY);
2483        if (value == NULL) {
2484            value = "";
2485        }
2486        if (mePtr->onValuePtr != NULL) {
2487            onValue = Tcl_GetStringFromObj(mePtr->onValuePtr, NULL);
2488            if (strcmp(value, onValue) == 0) {
2489                if (mePtr->entryFlags & ENTRY_SELECTED) {
2490                    return (char *) NULL;
2491                }
2492                mePtr->entryFlags |= ENTRY_SELECTED;
2493            } else if (mePtr->entryFlags & ENTRY_SELECTED) {
2494                mePtr->entryFlags &= ~ENTRY_SELECTED;
2495            } else {
2496                return (char *) NULL;
2497            }
2498        } else {
2499            return (char *) NULL;
2500        }
2501        TkpConfigureMenuEntry(mePtr);
2502        TkEventuallyRedrawMenu(menuPtr, mePtr);
2503        return (char *) NULL;
2504    }
2505    
2506    /*
2507     *----------------------------------------------------------------------
2508     *
2509     * TkActivateMenuEntry --
2510     *
2511     *      This procedure is invoked to make a particular menu entry
2512     *      the active one, deactivating any other entry that might
2513     *      currently be active.
2514     *
2515     * Results:
2516     *      The return value is a standard Tcl result (errors can occur
2517     *      while posting and unposting submenus).
2518     *
2519     * Side effects:
2520     *      Menu entries get redisplayed, and the active entry changes.
2521     *      Submenus may get posted and unposted.
2522     *
2523     *----------------------------------------------------------------------
2524     */
2525    
2526    int
2527    TkActivateMenuEntry(menuPtr, index)
2528        register TkMenu *menuPtr;           /* Menu in which to activate. */
2529        int index;                          /* Index of entry to activate, or
2530                                             * -1 to deactivate all entries. */
2531    {
2532        register TkMenuEntry *mePtr;
2533        int result = TCL_OK;
2534    
2535        if (menuPtr->active >= 0) {
2536            mePtr = menuPtr->entries[menuPtr->active];
2537    
2538            /*
2539             * Don't change the state unless it's currently active (state
2540             * might already have been changed to disabled).
2541             */
2542    
2543            if (mePtr->state == ENTRY_ACTIVE) {
2544                mePtr->state = ENTRY_NORMAL;
2545            }
2546            TkEventuallyRedrawMenu(menuPtr, menuPtr->entries[menuPtr->active]);
2547        }
2548        menuPtr->active = index;
2549        if (index >= 0) {
2550            mePtr = menuPtr->entries[index];
2551            mePtr->state = ENTRY_ACTIVE;
2552            TkEventuallyRedrawMenu(menuPtr, mePtr);
2553        }
2554        return result;
2555    }
2556    
2557    /*
2558     *----------------------------------------------------------------------
2559     *
2560     * TkPostCommand --
2561     *
2562     *      Execute the postcommand for the given menu.
2563     *
2564     * Results:
2565     *      The return value is a standard Tcl result (errors can occur
2566     *      while the postcommands are being processed).
2567     *
2568     * Side effects:
2569     *      Since commands can get executed while this routine is being executed,
2570     *      the entire world can change.
2571     *
2572     *----------------------------------------------------------------------
2573     */
2574    
2575    int
2576    TkPostCommand(menuPtr)
2577        TkMenu *menuPtr;
2578    {
2579        int result;
2580    
2581        /*
2582         * If there is a command for the menu, execute it.  This
2583         * may change the size of the menu, so be sure to recompute
2584         * the menu's geometry if needed.
2585         */
2586    
2587        if (menuPtr->postCommandPtr != NULL) {
2588            Tcl_Obj *postCommandPtr = menuPtr->postCommandPtr;
2589    
2590            Tcl_IncrRefCount(postCommandPtr);
2591            result = Tcl_EvalObjEx(menuPtr->interp, postCommandPtr,
2592                    TCL_EVAL_GLOBAL);
2593            Tcl_DecrRefCount(postCommandPtr);
2594            if (result != TCL_OK) {
2595                return result;
2596            }
2597            TkRecomputeMenu(menuPtr);
2598        }
2599        return TCL_OK;
2600    }
2601    
2602    /*
2603     *--------------------------------------------------------------
2604     *
2605     * CloneMenu --
2606     *
2607     *      Creates a child copy of the menu. It will be inserted into
2608     *      the menu's instance chain. All attributes and entry
2609     *      attributes will be duplicated.
2610     *
2611     * Results:
2612     *      A standard Tcl result.
2613     *
2614     * Side effects:
2615     *      Allocates storage. After the menu is created, any
2616     *      configuration done with this menu or any related one
2617     *      will be reflected in all of them.
2618     *
2619     *--------------------------------------------------------------
2620     */
2621    
2622    static int
2623    CloneMenu(menuPtr, newMenuNamePtr, newMenuTypePtr)
2624        TkMenu *menuPtr;            /* The menu we are going to clone */
2625        Tcl_Obj *newMenuNamePtr;    /* The name to give the new menu */
2626        Tcl_Obj *newMenuTypePtr;    /* What kind of menu is this, a normal menu
2627                                     * a menubar, or a tearoff? */
2628    {
2629        int returnResult;
2630        int menuType, i;
2631        TkMenuReferences *menuRefPtr;
2632        Tcl_Obj *menuDupCommandArray[4];
2633        
2634        if (newMenuTypePtr == NULL) {
2635            menuType = MASTER_MENU;
2636        } else {
2637            if (Tcl_GetIndexFromObj(menuPtr->interp, newMenuTypePtr,
2638                    menuTypeStrings, "menu type", 0, &menuType) != TCL_OK) {
2639                return TCL_ERROR;
2640            }
2641        }
2642    
2643        menuDupCommandArray[0] = Tcl_NewStringObj("tkMenuDup", -1);
2644        menuDupCommandArray[1] = Tcl_NewStringObj(Tk_PathName(menuPtr->tkwin), -1);
2645        menuDupCommandArray[2] = newMenuNamePtr;
2646        if (newMenuTypePtr == NULL) {
2647            menuDupCommandArray[3] = Tcl_NewStringObj("normal", -1);
2648        } else {
2649            menuDupCommandArray[3] = newMenuTypePtr;
2650        }
2651        for (i = 0; i < 4; i++) {
2652            Tcl_IncrRefCount(menuDupCommandArray[i]);
2653        }
2654        Tcl_Preserve((ClientData) menuPtr);
2655        returnResult = Tcl_EvalObjv(menuPtr->interp, 4, menuDupCommandArray, 0);
2656        for (i = 0; i < 4; i++) {
2657            Tcl_DecrRefCount(menuDupCommandArray[i]);
2658        }
2659    
2660        /*
2661         * Make sure the tcl command actually created the clone.
2662         */
2663        
2664        if ((returnResult == TCL_OK) &&
2665                ((menuRefPtr = TkFindMenuReferencesObj(menuPtr->interp,
2666                newMenuNamePtr)) != (TkMenuReferences *) NULL)
2667                && (menuPtr->numEntries == menuRefPtr->menuPtr->numEntries)) {
2668            TkMenu *newMenuPtr = menuRefPtr->menuPtr;
2669            Tcl_Obj *newObjv[3];
2670            char *newArgv[3];
2671            int i, numElements;
2672    
2673            /*
2674             * Now put this newly created menu into the parent menu's instance
2675             * chain.
2676             */
2677    
2678            if (menuPtr->nextInstancePtr == NULL) {
2679                menuPtr->nextInstancePtr = newMenuPtr;
2680                newMenuPtr->masterMenuPtr = menuPtr->masterMenuPtr;
2681            } else {
2682                TkMenu *masterMenuPtr;
2683                
2684                masterMenuPtr = menuPtr->masterMenuPtr;
2685                newMenuPtr->nextInstancePtr = masterMenuPtr->nextInstancePtr;
2686                masterMenuPtr->nextInstancePtr = newMenuPtr;
2687                newMenuPtr->masterMenuPtr = masterMenuPtr;
2688            }
2689            
2690            /*
2691             * Add the master menu's window to the bind tags for this window
2692             * after this window's tag. This is so the user can bind to either
2693             * this clone (which may not be easy to do) or the entire menu
2694             * clone structure.
2695             */
2696            
2697            newArgv[0] = "bindtags";
2698            newArgv[1] = Tk_PathName(newMenuPtr->tkwin);
2699            if (Tk_BindtagsCmd((ClientData)newMenuPtr->tkwin,
2700                    newMenuPtr->interp, 2, newArgv) == TCL_OK) {
2701                char *windowName;
2702                Tcl_Obj *bindingsPtr =
2703                        Tcl_DuplicateObj(Tcl_GetObjResult(newMenuPtr->interp));
2704                Tcl_Obj *elementPtr;
2705        
2706                Tcl_ListObjLength(newMenuPtr->interp, bindingsPtr, &numElements);
2707                for (i = 0; i < numElements; i++) {
2708                    Tcl_ListObjIndex(newMenuPtr->interp, bindingsPtr, i,
2709                            &elementPtr);
2710                    windowName = Tcl_GetStringFromObj(elementPtr, NULL);
2711                    if (strcmp(windowName, Tk_PathName(newMenuPtr->tkwin))
2712                            == 0) {
2713                        Tcl_Obj *newElementPtr = Tcl_NewStringObj(
2714                                Tk_PathName(newMenuPtr->masterMenuPtr->tkwin), -1);
2715                        Tcl_IncrRefCount(newElementPtr);
2716                        Tcl_ListObjReplace(menuPtr->interp, bindingsPtr,
2717                                i + 1, 0, 1, &newElementPtr);
2718                        newArgv[2] = Tcl_GetStringFromObj(bindingsPtr, NULL);
2719                        Tk_BindtagsCmd((ClientData)newMenuPtr->tkwin,
2720                                menuPtr->interp, 3, newArgv);
2721                        break;
2722                    }
2723                }
2724                Tcl_DecrRefCount(bindingsPtr);          
2725            }
2726            Tcl_ResetResult(menuPtr->interp);
2727            
2728            /*
2729             * Clone all of the cascade menus that this menu points to.
2730             */
2731            
2732            for (i = 0; i < menuPtr->numEntries; i++) {
2733                TkMenuReferences *cascadeRefPtr;
2734                TkMenu *oldCascadePtr;
2735                
2736                if ((menuPtr->entries[i]->type == CASCADE_ENTRY)
2737                    && (menuPtr->entries[i]->namePtr != NULL)) {
2738                    cascadeRefPtr =
2739                            TkFindMenuReferencesObj(menuPtr->interp,
2740                            menuPtr->entries[i]->namePtr);
2741                    if ((cascadeRefPtr != NULL) && (cascadeRefPtr->menuPtr)) {
2742                        Tcl_Obj *windowNamePtr =
2743                                Tcl_NewStringObj(Tk_PathName(newMenuPtr->tkwin),
2744                                -1);
2745                        Tcl_Obj *newCascadePtr;
2746                        
2747                        oldCascadePtr = cascadeRefPtr->menuPtr;
2748    
2749                        Tcl_IncrRefCount(windowNamePtr);
2750                        newCascadePtr = TkNewMenuName(menuPtr->interp,
2751                                windowNamePtr, oldCascadePtr);
2752                        Tcl_IncrRefCount(newCascadePtr);
2753                        CloneMenu(oldCascadePtr, newCascadePtr, NULL);
2754    
2755                        newObjv[0] = Tcl_NewStringObj("-menu", -1);
2756                        newObjv[1] = newCascadePtr;
2757                        Tcl_IncrRefCount(newObjv[0]);
2758                        ConfigureMenuEntry(newMenuPtr->entries[i], 2, newObjv);
2759                        Tcl_DecrRefCount(newObjv[0]);
2760                        Tcl_DecrRefCount(newCascadePtr);
2761                        Tcl_DecrRefCount(windowNamePtr);
2762                    }
2763                }
2764            }
2765            
2766            returnResult = TCL_OK;
2767        } else {
2768            returnResult = TCL_ERROR;
2769        }
2770        Tcl_Release((ClientData) menuPtr);
2771        return returnResult;
2772    }
2773    
2774    /*
2775     *----------------------------------------------------------------------
2776     *
2777     * MenuDoYPosition --
2778     *
2779     *      Given arguments from an option command line, returns the Y position.
2780     *
2781     * Results:
2782     *      Returns TCL_OK or TCL_Error
2783     *
2784     * Side effects:
2785     *      yPosition is set to the Y-position of the menu entry.
2786     *
2787     *----------------------------------------------------------------------
2788     */
2789        
2790    static int
2791    MenuDoYPosition(interp, menuPtr, objPtr)
2792        Tcl_Interp *interp;
2793        TkMenu *menuPtr;
2794        Tcl_Obj *objPtr;
2795    {
2796        int index;
2797        
2798        TkRecomputeMenu(menuPtr);
2799        if (TkGetMenuIndex(interp, menuPtr, objPtr, 0, &index) != TCL_OK) {
2800            goto error;
2801        }
2802        Tcl_ResetResult(interp);
2803        if (index < 0) {
2804            Tcl_SetObjResult(interp, Tcl_NewIntObj(0));
2805        } else {
2806            Tcl_SetObjResult(interp, Tcl_NewIntObj(menuPtr->entries[index]->y));
2807        }
2808    
2809        return TCL_OK;
2810        
2811    error:
2812        return TCL_ERROR;
2813    }
2814    
2815    /*
2816     *----------------------------------------------------------------------
2817     *
2818     * GetIndexFromCoords --
2819     *
2820     *      Given a string of the form "@int", return the menu item corresponding
2821     *      to int.
2822     *
2823     * Results:
2824     *      If int is a valid number, *indexPtr will be the number of the menuentry
2825     *      that is the correct height. If int is invaled, *indexPtr will be
2826     *      unchanged. Returns appropriate Tcl error number.
2827     *
2828     * Side effects:
2829     *      If int is invalid, interp's result will set to NULL.
2830     *
2831     *----------------------------------------------------------------------
2832     */
2833    
2834    static int
2835    GetIndexFromCoords(interp, menuPtr, string, indexPtr)
2836        Tcl_Interp *interp;         /* interp of menu */
2837        TkMenu *menuPtr;            /* the menu we are searching */
2838        char *string;               /* The @string we are parsing */
2839        int *indexPtr;              /* The index of the item that matches */
2840    {
2841        int x, y, i;
2842        char *p, *end;
2843        
2844        TkRecomputeMenu(menuPtr);
2845        p = string + 1;
2846        y = strtol(p, &end, 0);
2847        if (end == p) {
2848            goto error;
2849        }
2850        if (*end == ',') {
2851            x = y;
2852            p = end + 1;
2853            y = strtol(p, &end, 0);
2854            if (end == p) {
2855                goto error;
2856            }
2857        } else {
2858            Tk_GetPixelsFromObj(interp, menuPtr->tkwin,
2859                    menuPtr->borderWidthPtr, &x);
2860        }
2861        
2862        for (i = 0; i < menuPtr->numEntries; i++) {
2863            if ((x >= menuPtr->entries[i]->x) && (y >= menuPtr->entries[i]->y)
2864                    && (x < (menuPtr->entries[i]->x + menuPtr->entries[i]->width))
2865                    && (y < (menuPtr->entries[i]->y
2866                    + menuPtr->entries[i]->height))) {
2867                break;
2868            }
2869        }
2870        if (i >= menuPtr->numEntries) {
2871            /* i = menuPtr->numEntries - 1; */
2872            i = -1;
2873        }
2874        *indexPtr = i;
2875        return TCL_OK;
2876    
2877        error:
2878        Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);
2879        return TCL_ERROR;
2880    }
2881    
2882    /*
2883     *----------------------------------------------------------------------
2884     *
2885     * RecursivelyDeleteMenu --
2886     *
2887     *      Deletes a menu and any cascades underneath it. Used for deleting
2888     *      instances when a menu is no longer being used as a menubar,
2889     *      for instance.
2890     *
2891     * Results:
2892     *      None.
2893     *
2894     * Side effects:
2895     *      Destroys the menu and all cascade menus underneath it.
2896     *
2897     *----------------------------------------------------------------------
2898     */
2899    
2900    static void
2901    RecursivelyDeleteMenu(menuPtr)
2902        TkMenu *menuPtr;            /* The menubar instance we are deleting */
2903    {
2904        int i;
2905        TkMenuEntry *mePtr;
2906        
2907        for (i = 0; i < menuPtr->numEntries; i++) {
2908            mePtr = menuPtr->entries[i];
2909            if ((mePtr->type == CASCADE_ENTRY)
2910                    && (mePtr->childMenuRefPtr != NULL)
2911                    && (mePtr->childMenuRefPtr->menuPtr != NULL)) {
2912                RecursivelyDeleteMenu(mePtr->childMenuRefPtr->menuPtr);
2913            }
2914        }
2915        Tk_DestroyWindow(menuPtr->tkwin);
2916    }
2917    
2918    /*
2919     *----------------------------------------------------------------------
2920     *
2921     * TkNewMenuName --
2922     *
2923     *      Makes a new unique name for a cloned menu. Will be a child
2924     *      of oldName.
2925     *
2926     * Results:
2927     *      Returns a char * which has been allocated; caller must free.
2928     *
2929     * Side effects:
2930     *      Memory is allocated.
2931     *
2932     *----------------------------------------------------------------------
2933     */
2934    
2935    Tcl_Obj *
2936    TkNewMenuName(interp, parentPtr, menuPtr)
2937        Tcl_Interp *interp;         /* The interp the new name has to live in.*/
2938        Tcl_Obj *parentPtr;         /* The prefix path of the new name. */
2939        TkMenu *menuPtr;            /* The menu we are cloning. */
2940    {
2941        Tcl_Obj *resultPtr = NULL;  /* Initialization needed only to prevent
2942                                     * compiler warning. */
2943        Tcl_Obj *childPtr;
2944        char *destString;
2945        int i;
2946        int doDot;
2947        Tcl_CmdInfo cmdInfo;
2948        Tcl_HashTable *nameTablePtr = NULL;
2949        TkWindow *winPtr = (TkWindow *) menuPtr->tkwin;
2950        char *parentName = Tcl_GetStringFromObj(parentPtr, NULL);
2951    
2952        if (winPtr->mainPtr != NULL) {
2953            nameTablePtr = &(winPtr->mainPtr->nameTable);
2954        }
2955    
2956        doDot = parentName[strlen(parentName) - 1] != '.';
2957    
2958        childPtr = Tcl_NewStringObj(Tk_PathName(menuPtr->tkwin), -1);
2959        for (destString = Tcl_GetStringFromObj(childPtr, NULL);
2960                *destString != '\0'; destString++) {
2961            if (*destString == '.') {
2962                *destString = '#';
2963            }
2964        }
2965        
2966        for (i = 0; ; i++) {
2967            if (i == 0) {
2968                resultPtr = Tcl_DuplicateObj(parentPtr);
2969                if (doDot) {
2970                    Tcl_AppendToObj(resultPtr, ".", -1);
2971                }
2972                Tcl_AppendObjToObj(resultPtr, childPtr);
2973            } else {
2974                Tcl_Obj *intPtr;
2975    
2976                Tcl_DecrRefCount(resultPtr);
2977                resultPtr = Tcl_DuplicateObj(parentPtr);
2978                if (doDot) {
2979                    Tcl_AppendToObj(resultPtr, ".", -1);
2980                }
2981                Tcl_AppendObjToObj(resultPtr, childPtr);
2982                intPtr = Tcl_NewIntObj(i);
2983                Tcl_AppendObjToObj(resultPtr, intPtr);
2984                Tcl_DecrRefCount(intPtr);
2985            }
2986            destString = Tcl_GetStringFromObj(resultPtr, NULL);
2987            if ((Tcl_GetCommandInfo(interp, destString, &cmdInfo) == 0)
2988                    && ((nameTablePtr == NULL)
2989                    || (Tcl_FindHashEntry(nameTablePtr, destString) == NULL))) {
2990                break;
2991            }
2992        }
2993        Tcl_DecrRefCount(childPtr);
2994        return resultPtr;
2995    }
2996    
2997    /*
2998     *----------------------------------------------------------------------
2999     *
3000     * TkSetWindowMenuBar --
3001     *
3002     *      Associates a menu with a window. Called by ConfigureFrame in
3003     *      in response to a "-menu .foo" configuration option for a top
3004     *      level.
3005     *
3006     * Results:
3007     *      None.
3008     *
3009     * Side effects:
3010     *      The old menu clones for the menubar are thrown away, and a
3011     *      handler is set up to allocate the new ones.
3012     *
3013     *----------------------------------------------------------------------
3014     */
3015    void
3016    TkSetWindowMenuBar(interp, tkwin, oldMenuName, menuName)
3017        Tcl_Interp *interp;         /* The interpreter the toplevel lives in. */
3018        Tk_Window tkwin;            /* The toplevel window */
3019        char *oldMenuName;          /* The name of the menubar previously set in
3020                                     * this toplevel. NULL means no menu was
3021                                     * set previously. */
3022        char *menuName;             /* The name of the new menubar that the
3023                                     * toplevel needs to be set to. NULL means
3024                                     * that their is no menu now. */
3025    {
3026        TkMenuTopLevelList *topLevelListPtr, *prevTopLevelPtr;
3027        TkMenu *menuPtr;
3028        TkMenuReferences *menuRefPtr;
3029        
3030        TkMenuInit();
3031    
3032        /*
3033         * Destroy the menubar instances of the old menu. Take this window
3034         * out of the old menu's top level reference list.
3035         */
3036        
3037        if (oldMenuName != NULL) {
3038            menuRefPtr = TkFindMenuReferences(interp, oldMenuName);
3039            if (menuRefPtr != NULL) {
3040    
3041                /*
3042                 * Find the menubar instance that is to be removed. Destroy
3043                 * it and all of the cascades underneath it.
3044                 */
3045    
3046                if (menuRefPtr->menuPtr != NULL) {              
3047                    TkMenu *instancePtr;
3048    
3049                    menuPtr = menuRefPtr->menuPtr;
3050                                
3051                    for (instancePtr = menuPtr->masterMenuPtr;
3052                            instancePtr != NULL;
3053                            instancePtr = instancePtr->nextInstancePtr) {
3054                        if (instancePtr->menuType == MENUBAR
3055                                && instancePtr->parentTopLevelPtr == tkwin) {
3056                            RecursivelyDeleteMenu(instancePtr);
3057                            break;
3058                        }
3059                    }
3060                }
3061    
3062                /*
3063                 * Now we need to remove this toplevel from the list of toplevels
3064                 * that reference this menu.
3065                 */
3066    
3067                for (topLevelListPtr = menuRefPtr->topLevelListPtr,
3068                        prevTopLevelPtr = NULL;
3069                        (topLevelListPtr != NULL)
3070                        && (topLevelListPtr->tkwin != tkwin);
3071                        prevTopLevelPtr = topLevelListPtr,
3072                        topLevelListPtr = topLevelListPtr->nextPtr) {
3073    
3074                    /*
3075                     * Empty loop body.
3076                     */
3077                    
3078                }
3079    
3080                /*
3081                 * Now we have found the toplevel reference that matches the
3082                 * tkwin; remove this reference from the list.
3083                 */
3084    
3085                if (topLevelListPtr != NULL) {
3086                    if (prevTopLevelPtr == NULL) {
3087                        menuRefPtr->topLevelListPtr =
3088                                menuRefPtr->topLevelListPtr->nextPtr;
3089                    } else {
3090                        prevTopLevelPtr->nextPtr = topLevelListPtr->nextPtr;
3091                    }
3092                    ckfree((char *) topLevelListPtr);
3093                    TkFreeMenuReferences(menuRefPtr);
3094                }
3095            }
3096        }
3097    
3098        /*
3099         * Now, add the clone references for the new menu.
3100         */
3101        
3102        if (menuName != NULL && menuName[0] != 0) {
3103            TkMenu *menuBarPtr = NULL;
3104    
3105            menuRefPtr = TkCreateMenuReferences(interp, menuName);          
3106            
3107            menuPtr = menuRefPtr->menuPtr;
3108            if (menuPtr != NULL) {
3109                Tcl_Obj *cloneMenuPtr;
3110                TkMenuReferences *cloneMenuRefPtr;
3111                Tcl_Obj *newObjv[4];
3112                Tcl_Obj *windowNamePtr = Tcl_NewStringObj(Tk_PathName(tkwin),
3113                        -1);
3114                Tcl_Obj *menubarPtr = Tcl_NewStringObj("menubar", -1);
3115            
3116                /*
3117                 * Clone the menu and all of the cascades underneath it.
3118                 */
3119    
3120                Tcl_IncrRefCount(windowNamePtr);
3121                cloneMenuPtr = TkNewMenuName(interp, windowNamePtr,
3122                        menuPtr);
3123                Tcl_IncrRefCount(cloneMenuPtr);
3124                Tcl_IncrRefCount(menubarPtr);
3125                CloneMenu(menuPtr, cloneMenuPtr, menubarPtr);
3126                
3127                cloneMenuRefPtr = TkFindMenuReferencesObj(interp, cloneMenuPtr);
3128                if ((cloneMenuRefPtr != NULL)
3129                        && (cloneMenuRefPtr->menuPtr != NULL)) {
3130                    Tcl_Obj *cursorPtr = Tcl_NewStringObj("-cursor", -1);
3131                    Tcl_Obj *nullPtr = Tcl_NewObj();
3132                    cloneMenuRefPtr->menuPtr->parentTopLevelPtr = tkwin;
3133                    menuBarPtr = cloneMenuRefPtr->menuPtr;
3134                    newObjv[0] = cursorPtr;
3135                    newObjv[1] = nullPtr;
3136                    Tcl_IncrRefCount(cursorPtr);
3137                    Tcl_IncrRefCount(nullPtr);
3138                    ConfigureMenu(menuPtr->interp, cloneMenuRefPtr->menuPtr,
3139                            2, newObjv);
3140                    Tcl_DecrRefCount(cursorPtr);
3141                    Tcl_DecrRefCount(nullPtr);
3142                }
3143    
3144                TkpSetWindowMenuBar(tkwin, menuBarPtr);
3145                Tcl_DecrRefCount(cloneMenuPtr);
3146                Tcl_DecrRefCount(menubarPtr);
3147                Tcl_DecrRefCount(windowNamePtr);
3148            } else {
3149                TkpSetWindowMenuBar(tkwin, NULL);
3150            }
3151    
3152            
3153            /*
3154             * Add this window to the menu's list of windows that refer
3155             * to this menu.
3156             */
3157    
3158            topLevelListPtr = (TkMenuTopLevelList *)
3159                    ckalloc(sizeof(TkMenuTopLevelList));
3160            topLevelListPtr->tkwin = tkwin;
3161            topLevelListPtr->nextPtr = menuRefPtr->topLevelListPtr;
3162            menuRefPtr->topLevelListPtr = topLevelListPtr;
3163        } else {
3164            TkpSetWindowMenuBar(tkwin, NULL);
3165        }
3166        TkpSetMainMenubar(interp, tkwin, menuName);
3167    }
3168    
3169    /*
3170     *----------------------------------------------------------------------
3171     *
3172     * DestroyMenuHashTable --
3173     *
3174     *      Called when an interp is deleted and a menu hash table has
3175     *      been set in it.
3176     *
3177     * Results:
3178     *      None.
3179     *
3180     * Side effects:
3181     *      The hash table is destroyed.
3182     *
3183     *----------------------------------------------------------------------
3184     */
3185    
3186    static void
3187    DestroyMenuHashTable(clientData, interp)
3188        ClientData clientData;      /* The menu hash table we are destroying */
3189        Tcl_Interp *interp;         /* The interpreter we are destroying */
3190    {
3191        Tcl_DeleteHashTable((Tcl_HashTable *) clientData);
3192        ckfree((char *) clientData);
3193    }
3194    
3195    /*
3196     *----------------------------------------------------------------------
3197     *
3198     * TkGetMenuHashTable --
3199     *
3200     *      For a given interp, give back the menu hash table that goes with
3201     *      it. If the hash table does not exist, it is created.
3202     *
3203     * Results:
3204     *      Returns a hash table pointer.
3205     *
3206     * Side effects:
3207     *      A new hash table is created if there were no table in the interp
3208     *      originally.
3209     *
3210     *----------------------------------------------------------------------
3211     */
3212    
3213    Tcl_HashTable *
3214    TkGetMenuHashTable(interp)
3215        Tcl_Interp *interp;         /* The interp we need the hash table in.*/
3216    {
3217        Tcl_HashTable *menuTablePtr;
3218    
3219        menuTablePtr = (Tcl_HashTable *) Tcl_GetAssocData(interp, MENU_HASH_KEY,
3220                NULL);
3221        if (menuTablePtr == NULL) {
3222            menuTablePtr = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
3223            Tcl_InitHashTable(menuTablePtr, TCL_STRING_KEYS);
3224            Tcl_SetAssocData(interp, MENU_HASH_KEY, DestroyMenuHashTable,
3225                    (ClientData) menuTablePtr);
3226        }
3227        return menuTablePtr;
3228    }
3229    
3230    /*
3231     *----------------------------------------------------------------------
3232     *
3233     * TkCreateMenuReferences --
3234     *
3235     *      Given a pathname, gives back a pointer to a TkMenuReferences structure.
3236     *      If a reference is not already in the hash table, one is created.
3237     *
3238     * Results:
3239     *      Returns a pointer to a menu reference structure. Should not
3240     *      be freed by calller; when a field of the reference is cleared,
3241     *      TkFreeMenuReferences should be called.
3242     *
3243     * Side effects:
3244     *      A new hash table entry is created if there were no references
3245     *      to the menu originally.
3246     *
3247     *----------------------------------------------------------------------
3248     */
3249    
3250    TkMenuReferences *
3251    TkCreateMenuReferences(interp, pathName)
3252        Tcl_Interp *interp;
3253        char *pathName;             /* The path of the menu widget */
3254    {
3255        Tcl_HashEntry *hashEntryPtr;
3256        TkMenuReferences *menuRefPtr;
3257        int newEntry;
3258        Tcl_HashTable *menuTablePtr = TkGetMenuHashTable(interp);
3259    
3260        hashEntryPtr = Tcl_CreateHashEntry(menuTablePtr, pathName, &newEntry);
3261        if (newEntry) {
3262            menuRefPtr = (TkMenuReferences *) ckalloc(sizeof(TkMenuReferences));
3263            menuRefPtr->menuPtr = NULL;
3264            menuRefPtr->topLevelListPtr = NULL;
3265            menuRefPtr->parentEntryPtr = NULL;
3266            menuRefPtr->hashEntryPtr = hashEntryPtr;
3267            Tcl_SetHashValue(hashEntryPtr, (char *) menuRefPtr);
3268        } else {
3269            menuRefPtr = (TkMenuReferences *) Tcl_GetHashValue(hashEntryPtr);
3270        }
3271        return menuRefPtr;
3272    }
3273    
3274    /*
3275     *----------------------------------------------------------------------
3276     *
3277     * TkFindMenuReferences --
3278     *
3279     *      Given a pathname, gives back a pointer to the TkMenuReferences
3280     *      structure.
3281     *
3282     * Results:
3283     *      Returns a pointer to a menu reference structure. Should not
3284     *      be freed by calller; when a field of the reference is cleared,
3285     *      TkFreeMenuReferences should be called. Returns NULL if no reference
3286     *      with this pathname exists.
3287     *
3288     * Side effects:
3289     *      None.
3290     *
3291     *----------------------------------------------------------------------
3292     */
3293    
3294    TkMenuReferences *
3295    TkFindMenuReferences(interp, pathName)
3296        Tcl_Interp *interp;         /* The interp the menu is living in. */
3297        char *pathName;             /* The path of the menu widget */
3298    {
3299        Tcl_HashEntry *hashEntryPtr;
3300        TkMenuReferences *menuRefPtr = NULL;
3301        Tcl_HashTable *menuTablePtr;
3302    
3303        menuTablePtr = TkGetMenuHashTable(interp);
3304        hashEntryPtr = Tcl_FindHashEntry(menuTablePtr, pathName);
3305        if (hashEntryPtr != NULL) {
3306            menuRefPtr = (TkMenuReferences *) Tcl_GetHashValue(hashEntryPtr);
3307        }
3308        return menuRefPtr;
3309    }
3310    
3311    /*
3312     *----------------------------------------------------------------------
3313     *
3314     * TkFindMenuReferencesObj --
3315     *
3316     *      Given a pathname, gives back a pointer to the TkMenuReferences
3317     *      structure.
3318     *
3319     * Results:
3320     *      Returns a pointer to a menu reference structure. Should not
3321     *      be freed by calller; when a field of the reference is cleared,
3322     *      TkFreeMenuReferences should be called. Returns NULL if no reference
3323     *      with this pathname exists.
3324     *
3325     * Side effects:
3326     *      None.
3327     *
3328     *----------------------------------------------------------------------
3329     */
3330    
3331    TkMenuReferences *
3332    TkFindMenuReferencesObj(interp, objPtr)
3333        Tcl_Interp *interp;         /* The interp the menu is living in. */
3334        Tcl_Obj *objPtr;            /* The path of the menu widget */
3335    {
3336        char *pathName = Tcl_GetStringFromObj(objPtr, NULL);
3337        return TkFindMenuReferences(interp, pathName);
3338    }
3339    
3340    /*
3341     *----------------------------------------------------------------------
3342     *
3343     * TkFreeMenuReferences --
3344     *
3345     *      This is called after one of the fields in a menu reference
3346     *      is cleared. It cleans up the ref if it is now empty.
3347     *
3348     * Results:
3349     *      None.
3350     *
3351     * Side effects:
3352     *      If this is the last field to be cleared, the menu ref is
3353     *      taken out of the hash table.
3354     *
3355     *----------------------------------------------------------------------
3356     */
3357    
3358    void
3359    TkFreeMenuReferences(menuRefPtr)
3360        TkMenuReferences *menuRefPtr;               /* The menu reference to
3361                                                     * free */
3362    {
3363        if ((menuRefPtr->menuPtr == NULL)
3364                && (menuRefPtr->parentEntryPtr == NULL)
3365                && (menuRefPtr->topLevelListPtr == NULL)) {
3366            Tcl_DeleteHashEntry(menuRefPtr->hashEntryPtr);
3367            ckfree((char *) menuRefPtr);
3368        }
3369    }
3370    
3371    /*
3372     *----------------------------------------------------------------------
3373     *
3374     * DeleteMenuCloneEntries --
3375     *
3376     *      For every clone in this clone chain, delete the menu entries
3377     *      given by the parameters.
3378     *
3379     * Results:
3380     *      None.
3381     *
3382     * Side effects:
3383     *      The appropriate entries are deleted from all clones of this menu.
3384     *
3385     *----------------------------------------------------------------------
3386     */
3387    
3388    static void
3389    DeleteMenuCloneEntries(menuPtr, first, last)
3390        TkMenu *menuPtr;                /* the menu the command was issued with */
3391        int first;                      /* the zero-based first entry in the set
3392                                         * of entries to delete. */
3393        int last;                       /* the zero-based last entry */
3394    {
3395    
3396        TkMenu *menuListPtr;
3397        int numDeleted, i;
3398    
3399        numDeleted = last + 1 - first;
3400        for (menuListPtr = menuPtr->masterMenuPtr; menuListPtr != NULL;
3401                menuListPtr = menuListPtr->nextInstancePtr) {
3402            for (i = last; i >= first; i--) {
3403                Tcl_EventuallyFree((ClientData) menuListPtr->entries[i],
3404                        DestroyMenuEntry);
3405            }
3406            for (i = last + 1; i < menuListPtr->numEntries; i++) {
3407                menuListPtr->entries[i - numDeleted] = menuListPtr->entries[i];
3408                menuListPtr->entries[i - numDeleted]->index = i;
3409            }
3410            menuListPtr->numEntries -= numDeleted;
3411            if (menuListPtr->numEntries == 0) {
3412                ckfree((char *) menuListPtr->entries);
3413                menuListPtr->entries = NULL;
3414            }
3415            if ((menuListPtr->active >= first)
3416                    && (menuListPtr->active <= last)) {
3417                menuListPtr->active = -1;
3418            } else if (menuListPtr->active > last) {
3419                menuListPtr->active -= numDeleted;
3420            }
3421            TkEventuallyRecomputeMenu(menuListPtr);
3422        }
3423    }
3424    
3425    /*
3426     *----------------------------------------------------------------------
3427     *
3428     * TkMenuInit --
3429     *
3430     *      Sets up the hash tables and the variables used by the menu package.
3431     *
3432     * Results:
3433     *      None.
3434     *
3435     * Side effects:
3436     *      lastMenuID gets initialized, and the parent hash and the command hash
3437     *      are allocated.
3438     *
3439     *----------------------------------------------------------------------
3440     */
3441    
3442    void
3443    TkMenuInit()
3444    {
3445        ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
3446                Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
3447        
3448        if (!menusInitialized) {
3449            Tcl_MutexLock(&menuMutex);
3450            if (!menusInitialized) {
3451                TkpMenuInit();
3452                menusInitialized = 1;
3453            }
3454            Tcl_MutexUnlock(&menuMutex);
3455        }
3456        if (!tsdPtr->menusInitialized) {
3457            TkpMenuThreadInit();
3458            tsdPtr->menusInitialized = 1;
3459        }
3460    }
3461    
3462    /* End of tkmenu.c */

Legend:
Removed from v.42  
changed lines
  Added in v.269

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25