tkMenu.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:106k
源码类别:

通讯编程

开发平台:

Visual C++

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