Parent Directory | Revision Log | Patch
revision 44 by dashley, Fri Oct 14 02:09:58 2016 UTC | revision 71 by dashley, Sat Nov 5 11:07:06 2016 UTC | |
---|---|---|
# | Line 1 | Line 1 |
/* $Header: /cvsroot/esrg/sfesrg/esrgpcpj/shared/tk_base/tkmenu.c,v 1.1.1.1 2001/06/13 05:05:37 dtashley Exp $ */ | ||
/* | ||
* tkMenu.c -- | ||
* | ||
* This file contains most of the code for implementing menus in Tk. It takes | ||
* care of all of the generic (platform-independent) parts of menus, and | ||
* is supplemented by platform-specific files. The geometry calculation | ||
* and drawing code for menus is in the file tkMenuDraw.c | ||
* | ||
* Copyright (c) 1990-1994 The Regents of the University of California. | ||
* Copyright (c) 1994-1998 Sun Microsystems, Inc. | ||
* | ||
* See the file "license.terms" for information on usage and redistribution | ||
* of this file, and for a DISCLAIMER OF ALL WARRANTIES. | ||
* | ||
* RCS: @(#) $Id: tkmenu.c,v 1.1.1.1 2001/06/13 05:05:37 dtashley Exp $ | ||
*/ | ||
/* | ||
* Notes on implementation of menus: | ||
* | ||
* Menus can be used in three ways: | ||
* - as a popup menu, either as part of a menubutton or standalone. | ||
* - as a menubar. The menu's cascade items are arranged according to | ||
* the specific platform to provide the user access to the menus at all | ||
* times | ||
* - as a tearoff palette. This is a window with the menu's items in it. | ||
* | ||
* The goal is to provide the Tk developer with a way to use a common | ||
* set of menus for all of these tasks. | ||
* | ||
* In order to make the bindings for cascade menus work properly under Unix, | ||
* the cascade menus' pathnames must be proper children of the menu that | ||
* they are cascade from. So if there is a menu .m, and it has two | ||
* cascades labelled "File" and "Edit", the cascade menus might have | ||
* the pathnames .m.file and .m.edit. Another constraint is that the menus | ||
* used for menubars must be children of the toplevel widget that they | ||
* are attached to. And on the Macintosh, the platform specific menu handle | ||
* for cascades attached to a menu bar must have a title that matches the | ||
* label for the cascade menu. | ||
* | ||
* To handle all of the constraints, Tk menubars and tearoff menus are | ||
* implemented using menu clones. Menu clones are full menus in their own | ||
* right; they have a Tk window and pathname associated with them; they have | ||
* a TkMenu structure and array of entries. However, they are linked with the | ||
* original menu that they were cloned from. The reflect the attributes of | ||
* the original, or "master", menu. So if an item is added to a menu, and | ||
* that menu has clones, then the item must be added to all of its clones | ||
* also. Menus are cloned when a menu is torn-off or when a menu is assigned | ||
* as a menubar using the "-menu" option of the toplevel's pathname configure | ||
* subcommand. When a clone is destroyed, only the clone is destroyed, but | ||
* when the master menu is destroyed, all clones are also destroyed. This | ||
* allows the developer to just deal with one set of menus when creating | ||
* and destroying. | ||
* | ||
* Clones are rather tricky when a menu with cascade entries is cloned (such | ||
* as a menubar). Not only does the menu have to be cloned, but each cascade | ||
* entry's corresponding menu must also be cloned. This maintains the pathname | ||
* parent-child hierarchy necessary for menubars and toplevels to work. | ||
* This leads to several special cases: | ||
* | ||
* 1. When a new menu is created, and it is pointed to by cascade entries in | ||
* cloned menus, the new menu has to be cloned to parallel the cascade | ||
* structure. | ||
* 2. When a cascade item is added to a menu that has been cloned, and the | ||
* menu that the cascade item points to exists, that menu has to be cloned. | ||
* 3. When the menu that a cascade entry points to is changed, the old | ||
* cloned cascade menu has to be discarded, and the new one has to be cloned. | ||
* | ||
*/ | ||
#if 0 | ||
/* | ||
* used only to test for old config code | ||
*/ | ||
#define __NO_OLD_CONFIG | ||
#endif | ||
#include "tkPort.h" | ||
#include "tkMenu.h" | ||
#define MENU_HASH_KEY "tkMenus" | ||
typedef struct ThreadSpecificData { | ||
int menusInitialized; /* Flag indicates whether thread-specific | ||
* elements of the Windows Menu module | ||
* have been initialized. */ | ||
} ThreadSpecificData; | ||
static Tcl_ThreadDataKey dataKey; | ||
/* | ||
* The following flag indicates whether the process-wide state for | ||
* the Menu module has been intialized. The Mutex protects access to | ||
* that flag. | ||
*/ | ||
static int menusInitialized; | ||
TCL_DECLARE_MUTEX(menuMutex) | ||
/* | ||
* Configuration specs for individual menu entries. If this changes, be sure | ||
* to update code in TkpMenuInit that changes the font string entry. | ||
*/ | ||
char *tkMenuStateStrings[] = {"active", "normal", "disabled", (char *) NULL}; | ||
static char *menuEntryTypeStrings[] = {"cascade", "checkbutton", "command", | ||
"radiobutton", "separator", (char *) NULL}; | ||
Tk_OptionSpec tkBasicMenuEntryConfigSpecs[] = { | ||
{TK_OPTION_BORDER, "-activebackground", (char *) NULL, (char *) NULL, | ||
DEF_MENU_ENTRY_ACTIVE_BG, Tk_Offset(TkMenuEntry, activeBorderPtr), -1, | ||
TK_OPTION_NULL_OK}, | ||
{TK_OPTION_COLOR, "-activeforeground", (char *) NULL, (char *) NULL, | ||
DEF_MENU_ENTRY_ACTIVE_FG, | ||
Tk_Offset(TkMenuEntry, activeFgPtr), -1, TK_OPTION_NULL_OK}, | ||
{TK_OPTION_STRING, "-accelerator", (char *) NULL, (char *) NULL, | ||
DEF_MENU_ENTRY_ACCELERATOR, | ||
Tk_Offset(TkMenuEntry, accelPtr), -1, TK_OPTION_NULL_OK}, | ||
{TK_OPTION_BORDER, "-background", (char *) NULL, (char *) NULL, | ||
DEF_MENU_ENTRY_BG, | ||
Tk_Offset(TkMenuEntry, borderPtr), -1, TK_OPTION_NULL_OK}, | ||
{TK_OPTION_BITMAP, "-bitmap", (char *) NULL, (char *) NULL, | ||
DEF_MENU_ENTRY_BITMAP, | ||
Tk_Offset(TkMenuEntry, bitmapPtr), -1, TK_OPTION_NULL_OK}, | ||
{TK_OPTION_BOOLEAN, "-columnbreak", (char *) NULL, (char *) NULL, | ||
DEF_MENU_ENTRY_COLUMN_BREAK, | ||
-1, Tk_Offset(TkMenuEntry, columnBreak)}, | ||
{TK_OPTION_STRING, "-command", (char *) NULL, (char *) NULL, | ||
DEF_MENU_ENTRY_COMMAND, | ||
Tk_Offset(TkMenuEntry, commandPtr), -1, TK_OPTION_NULL_OK}, | ||
{TK_OPTION_FONT, "-font", (char *) NULL, (char *) NULL, | ||
DEF_MENU_ENTRY_FONT, | ||
Tk_Offset(TkMenuEntry, fontPtr), -1, TK_OPTION_NULL_OK}, | ||
{TK_OPTION_COLOR, "-foreground", (char *) NULL, (char *) NULL, | ||
DEF_MENU_ENTRY_FG, | ||
Tk_Offset(TkMenuEntry, fgPtr), -1, TK_OPTION_NULL_OK}, | ||
{TK_OPTION_BOOLEAN, "-hidemargin", (char *) NULL, (char *) NULL, | ||
DEF_MENU_ENTRY_HIDE_MARGIN, | ||
-1, Tk_Offset(TkMenuEntry, hideMargin)}, | ||
{TK_OPTION_STRING, "-image", (char *) NULL, (char *) NULL, | ||
DEF_MENU_ENTRY_IMAGE, | ||
Tk_Offset(TkMenuEntry, imagePtr), -1, TK_OPTION_NULL_OK}, | ||
{TK_OPTION_STRING, "-label", (char *) NULL, (char *) NULL, | ||
DEF_MENU_ENTRY_LABEL, | ||
Tk_Offset(TkMenuEntry, labelPtr), -1, 0}, | ||
{TK_OPTION_STRING_TABLE, "-state", (char *) NULL, (char *) NULL, | ||
DEF_MENU_ENTRY_STATE, | ||
-1, Tk_Offset(TkMenuEntry, state), 0, | ||
(ClientData) tkMenuStateStrings}, | ||
{TK_OPTION_INT, "-underline", (char *) NULL, (char *) NULL, | ||
DEF_MENU_ENTRY_UNDERLINE, -1, Tk_Offset(TkMenuEntry, underline)}, | ||
{TK_OPTION_END} | ||
}; | ||
Tk_OptionSpec tkSeparatorEntryConfigSpecs[] = { | ||
{TK_OPTION_BORDER, "-background", (char *) NULL, (char *) NULL, | ||
DEF_MENU_ENTRY_BG, | ||
Tk_Offset(TkMenuEntry, borderPtr), -1, TK_OPTION_NULL_OK}, | ||
{TK_OPTION_END} | ||
}; | ||
Tk_OptionSpec tkCheckButtonEntryConfigSpecs[] = { | ||
{TK_OPTION_BOOLEAN, "-indicatoron", (char *) NULL, (char *) NULL, | ||
DEF_MENU_ENTRY_INDICATOR, | ||
-1, Tk_Offset(TkMenuEntry, indicatorOn)}, | ||
{TK_OPTION_STRING, "-offvalue", (char *) NULL, (char *) NULL, | ||
DEF_MENU_ENTRY_OFF_VALUE, | ||
Tk_Offset(TkMenuEntry, offValuePtr), -1}, | ||
{TK_OPTION_STRING, "-onvalue", (char *) NULL, (char *) NULL, | ||
DEF_MENU_ENTRY_ON_VALUE, | ||
Tk_Offset(TkMenuEntry, onValuePtr), -1}, | ||
{TK_OPTION_COLOR, "-selectcolor", (char *) NULL, (char *) NULL, | ||
DEF_MENU_ENTRY_SELECT, | ||
Tk_Offset(TkMenuEntry, indicatorFgPtr), -1, TK_OPTION_NULL_OK}, | ||
{TK_OPTION_STRING, "-selectimage", (char *) NULL, (char *) NULL, | ||
DEF_MENU_ENTRY_SELECT_IMAGE, | ||
Tk_Offset(TkMenuEntry, selectImagePtr), -1, TK_OPTION_NULL_OK}, | ||
{TK_OPTION_STRING, "-variable", (char *) NULL, (char *) NULL, | ||
DEF_MENU_ENTRY_CHECK_VARIABLE, | ||
Tk_Offset(TkMenuEntry, namePtr), -1, TK_OPTION_NULL_OK}, | ||
{TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL, | ||
(char *) NULL, 0, -1, 0, (ClientData) tkBasicMenuEntryConfigSpecs} | ||
}; | ||
Tk_OptionSpec tkRadioButtonEntryConfigSpecs[] = { | ||
{TK_OPTION_BOOLEAN, "-indicatoron", (char *) NULL, (char *) NULL, | ||
DEF_MENU_ENTRY_INDICATOR, | ||
-1, Tk_Offset(TkMenuEntry, indicatorOn)}, | ||
{TK_OPTION_COLOR, "-selectcolor", (char *) NULL, (char *) NULL, | ||
DEF_MENU_ENTRY_SELECT, | ||
Tk_Offset(TkMenuEntry, indicatorFgPtr), -1, TK_OPTION_NULL_OK}, | ||
{TK_OPTION_STRING, "-selectimage", (char *) NULL, (char *) NULL, | ||
DEF_MENU_ENTRY_SELECT_IMAGE, | ||
Tk_Offset(TkMenuEntry, selectImagePtr), -1, TK_OPTION_NULL_OK}, | ||
{TK_OPTION_STRING, "-value", (char *) NULL, (char *) NULL, | ||
DEF_MENU_ENTRY_VALUE, | ||
Tk_Offset(TkMenuEntry, onValuePtr), -1, TK_OPTION_NULL_OK}, | ||
{TK_OPTION_STRING, "-variable", (char *) NULL, (char *) NULL, | ||
DEF_MENU_ENTRY_RADIO_VARIABLE, | ||
Tk_Offset(TkMenuEntry, namePtr), -1, 0}, | ||
{TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL, | ||
(char *) NULL, 0, -1, 0, (ClientData) tkBasicMenuEntryConfigSpecs} | ||
}; | ||
Tk_OptionSpec tkCascadeEntryConfigSpecs[] = { | ||
{TK_OPTION_STRING, "-menu", (char *) NULL, (char *) NULL, | ||
DEF_MENU_ENTRY_MENU, | ||
Tk_Offset(TkMenuEntry, namePtr), -1, TK_OPTION_NULL_OK}, | ||
{TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL, | ||
(char *) NULL, 0, -1, 0, (ClientData) tkBasicMenuEntryConfigSpecs} | ||
}; | ||
Tk_OptionSpec tkTearoffEntryConfigSpecs[] = { | ||
{TK_OPTION_BORDER, "-background", (char *) NULL, (char *) NULL, | ||
DEF_MENU_ENTRY_BG, | ||
Tk_Offset(TkMenuEntry, borderPtr), -1, TK_OPTION_NULL_OK}, | ||
{TK_OPTION_STRING_TABLE, "-state", (char *) NULL, (char *) NULL, | ||
DEF_MENU_ENTRY_STATE, -1, Tk_Offset(TkMenuEntry, state), 0, | ||
(ClientData) tkMenuStateStrings}, | ||
{TK_OPTION_END} | ||
}; | ||
static Tk_OptionSpec *specsArray[] = { | ||
tkCascadeEntryConfigSpecs, tkCheckButtonEntryConfigSpecs, | ||
tkBasicMenuEntryConfigSpecs, tkRadioButtonEntryConfigSpecs, | ||
tkSeparatorEntryConfigSpecs, tkTearoffEntryConfigSpecs}; | ||
/* | ||
* Menu type strings for use with Tcl_GetIndexFromObj. | ||
*/ | ||
static char *menuTypeStrings[] = {"normal", "tearoff", "menubar", | ||
(char *) NULL}; | ||
Tk_OptionSpec tkMenuConfigSpecs[] = { | ||
{TK_OPTION_BORDER, "-activebackground", "activeBackground", | ||
"Foreground", DEF_MENU_ACTIVE_BG_COLOR, | ||
Tk_Offset(TkMenu, activeBorderPtr), -1, 0, | ||
(ClientData) DEF_MENU_ACTIVE_BG_MONO}, | ||
{TK_OPTION_PIXELS, "-activeborderwidth", "activeBorderWidth", | ||
"BorderWidth", DEF_MENU_ACTIVE_BORDER_WIDTH, | ||
Tk_Offset(TkMenu, activeBorderWidthPtr), -1}, | ||
{TK_OPTION_COLOR, "-activeforeground", "activeForeground", | ||
"Background", DEF_MENU_ACTIVE_FG_COLOR, | ||
Tk_Offset(TkMenu, activeFgPtr), -1, 0, | ||
(ClientData) DEF_MENU_ACTIVE_FG_MONO}, | ||
{TK_OPTION_BORDER, "-background", "background", "Background", | ||
DEF_MENU_BG_COLOR, Tk_Offset(TkMenu, borderPtr), -1, 0, | ||
(ClientData) DEF_MENU_BG_MONO}, | ||
{TK_OPTION_SYNONYM, "-bd", (char *) NULL, (char *) NULL, | ||
(char *) NULL, 0, -1, 0, (ClientData) "-borderwidth"}, | ||
{TK_OPTION_SYNONYM, "-bg", (char *) NULL, (char *) NULL, | ||
(char *) NULL, 0, -1, 0, (ClientData) "-background"}, | ||
{TK_OPTION_PIXELS, "-borderwidth", "borderWidth", "BorderWidth", | ||
DEF_MENU_BORDER_WIDTH, | ||
Tk_Offset(TkMenu, borderWidthPtr), -1, 0}, | ||
{TK_OPTION_CURSOR, "-cursor", "cursor", "Cursor", | ||
DEF_MENU_CURSOR, | ||
Tk_Offset(TkMenu, cursorPtr), -1, TK_OPTION_NULL_OK}, | ||
{TK_OPTION_COLOR, "-disabledforeground", "disabledForeground", | ||
"DisabledForeground", DEF_MENU_DISABLED_FG_COLOR, | ||
Tk_Offset(TkMenu, disabledFgPtr), -1, TK_OPTION_NULL_OK, | ||
(ClientData) DEF_MENU_DISABLED_FG_MONO}, | ||
{TK_OPTION_SYNONYM, "-fg", (char *) NULL, (char *) NULL, | ||
(char *) NULL, 0, -1, 0, (ClientData) "-foreground"}, | ||
{TK_OPTION_FONT, "-font", "font", "Font", | ||
DEF_MENU_FONT, Tk_Offset(TkMenu, fontPtr), -1}, | ||
{TK_OPTION_COLOR, "-foreground", "foreground", "Foreground", | ||
DEF_MENU_FG, Tk_Offset(TkMenu, fgPtr), -1}, | ||
{TK_OPTION_STRING, "-postcommand", "postCommand", "Command", | ||
DEF_MENU_POST_COMMAND, | ||
Tk_Offset(TkMenu, postCommandPtr), -1, TK_OPTION_NULL_OK}, | ||
{TK_OPTION_RELIEF, "-relief", "relief", "Relief", | ||
DEF_MENU_RELIEF, Tk_Offset(TkMenu, reliefPtr), -1}, | ||
{TK_OPTION_COLOR, "-selectcolor", "selectColor", "Background", | ||
DEF_MENU_SELECT_COLOR, Tk_Offset(TkMenu, indicatorFgPtr), -1, 0, | ||
(ClientData) DEF_MENU_SELECT_MONO}, | ||
{TK_OPTION_STRING, "-takefocus", "takeFocus", "TakeFocus", | ||
DEF_MENU_TAKE_FOCUS, | ||
Tk_Offset(TkMenu, takeFocusPtr), -1, TK_OPTION_NULL_OK}, | ||
{TK_OPTION_BOOLEAN, "-tearoff", "tearOff", "TearOff", | ||
DEF_MENU_TEAROFF, -1, Tk_Offset(TkMenu, tearoff)}, | ||
{TK_OPTION_STRING, "-tearoffcommand", "tearOffCommand", | ||
"TearOffCommand", DEF_MENU_TEAROFF_CMD, | ||
Tk_Offset(TkMenu, tearoffCommandPtr), -1, TK_OPTION_NULL_OK}, | ||
{TK_OPTION_STRING, "-title", "title", "Title", | ||
DEF_MENU_TITLE, Tk_Offset(TkMenu, titlePtr), -1, | ||
TK_OPTION_NULL_OK}, | ||
{TK_OPTION_STRING_TABLE, "-type", "type", "Type", | ||
DEF_MENU_TYPE, Tk_Offset(TkMenu, menuTypePtr), -1, TK_OPTION_NULL_OK, | ||
(ClientData) menuTypeStrings}, | ||
{TK_OPTION_END} | ||
}; | ||
/* | ||
* Command line options. Put here because MenuCmd has to look at them | ||
* along with MenuWidgetObjCmd. | ||
*/ | ||
static char *menuOptions[] = { | ||
"activate", "add", "cget", "clone", "configure", "delete", "entrycget", | ||
"entryconfigure", "index", "insert", "invoke", "post", "postcascade", | ||
"type", "unpost", "yposition", (char *) NULL | ||
}; | ||
enum options { | ||
MENU_ACTIVATE, MENU_ADD, MENU_CGET, MENU_CLONE, MENU_CONFIGURE, | ||
MENU_DELETE, MENU_ENTRYCGET, MENU_ENTRYCONFIGURE, MENU_INDEX, | ||
MENU_INSERT, MENU_INVOKE, MENU_POST, MENU_POSTCASCADE, MENU_TYPE, | ||
MENU_UNPOST, MENU_YPOSITION | ||
}; | ||
/* | ||
* Prototypes for static procedures in this file: | ||
*/ | ||
static int CloneMenu _ANSI_ARGS_((TkMenu *menuPtr, | ||
Tcl_Obj *newMenuName, Tcl_Obj *newMenuTypeString)); | ||
static int ConfigureMenu _ANSI_ARGS_((Tcl_Interp *interp, | ||
TkMenu *menuPtr, int objc, Tcl_Obj *CONST objv[])); | ||
static int ConfigureMenuCloneEntries _ANSI_ARGS_(( | ||
Tcl_Interp *interp, TkMenu *menuPtr, int index, | ||
int objc, Tcl_Obj *CONST objv[])); | ||
static int ConfigureMenuEntry _ANSI_ARGS_((TkMenuEntry *mePtr, | ||
int objc, Tcl_Obj *CONST objv[])); | ||
static void DeleteMenuCloneEntries _ANSI_ARGS_((TkMenu *menuPtr, | ||
int first, int last)); | ||
static void DestroyMenuHashTable _ANSI_ARGS_(( | ||
ClientData clientData, Tcl_Interp *interp)); | ||
static void DestroyMenuInstance _ANSI_ARGS_((TkMenu *menuPtr)); | ||
static void DestroyMenuEntry _ANSI_ARGS_((char *memPtr)); | ||
static int GetIndexFromCoords | ||
_ANSI_ARGS_((Tcl_Interp *interp, TkMenu *menuPtr, | ||
char *string, int *indexPtr)); | ||
static int MenuDoYPosition _ANSI_ARGS_((Tcl_Interp *interp, | ||
TkMenu *menuPtr, Tcl_Obj *objPtr)); | ||
static int MenuAddOrInsert _ANSI_ARGS_((Tcl_Interp *interp, | ||
TkMenu *menuPtr, Tcl_Obj *indexPtr, int objc, | ||
Tcl_Obj *CONST objv[])); | ||
static int MenuCmd _ANSI_ARGS_((ClientData clientData, | ||
Tcl_Interp *interp, int objc, | ||
Tcl_Obj *CONST objv[])); | ||
static void MenuCmdDeletedProc _ANSI_ARGS_(( | ||
ClientData clientData)); | ||
static TkMenuEntry * MenuNewEntry _ANSI_ARGS_((TkMenu *menuPtr, int index, | ||
int type)); | ||
static char * MenuVarProc _ANSI_ARGS_((ClientData clientData, | ||
Tcl_Interp *interp, char *name1, char *name2, | ||
int flags)); | ||
static int MenuWidgetObjCmd _ANSI_ARGS_((ClientData clientData, | ||
Tcl_Interp *interp, int objc, | ||
Tcl_Obj *CONST objv[])); | ||
static void MenuWorldChanged _ANSI_ARGS_(( | ||
ClientData instanceData)); | ||
static int PostProcessEntry _ANSI_ARGS_((TkMenuEntry *mePtr)); | ||
static void RecursivelyDeleteMenu _ANSI_ARGS_((TkMenu *menuPtr)); | ||
static void UnhookCascadeEntry _ANSI_ARGS_((TkMenuEntry *mePtr)); | ||
/* | ||
* The structure below is a list of procs that respond to certain window | ||
* manager events. One of these includes a font change, which forces | ||
* the geometry proc to be called. | ||
*/ | ||
static TkClassProcs menuClass = { | ||
NULL, /* createProc. */ | ||
MenuWorldChanged /* geometryProc. */ | ||
}; | ||
/* | ||
*-------------------------------------------------------------- | ||
* | ||
* Tk_CreateMenuCmd -- | ||
* | ||
* Called by Tk at initialization time to create the menu | ||
* command. | ||
* | ||
* Results: | ||
* A standard Tcl result. | ||
* | ||
* Side effects: | ||
* See the user documentation. | ||
* | ||
*-------------------------------------------------------------- | ||
*/ | ||
int | ||
TkCreateMenuCmd(interp) | ||
Tcl_Interp *interp; /* Interpreter we are creating the | ||
* command in. */ | ||
{ | ||
TkMenuOptionTables *optionTablesPtr = | ||
(TkMenuOptionTables *) ckalloc(sizeof(TkMenuOptionTables)); | ||
optionTablesPtr->menuOptionTable = | ||
Tk_CreateOptionTable(interp, tkMenuConfigSpecs); | ||
optionTablesPtr->entryOptionTables[TEAROFF_ENTRY] = | ||
Tk_CreateOptionTable(interp, specsArray[TEAROFF_ENTRY]); | ||
optionTablesPtr->entryOptionTables[COMMAND_ENTRY] = | ||
Tk_CreateOptionTable(interp, specsArray[COMMAND_ENTRY]); | ||
optionTablesPtr->entryOptionTables[CASCADE_ENTRY] = | ||
Tk_CreateOptionTable(interp, specsArray[CASCADE_ENTRY]); | ||
optionTablesPtr->entryOptionTables[SEPARATOR_ENTRY] = | ||
Tk_CreateOptionTable(interp, specsArray[SEPARATOR_ENTRY]); | ||
optionTablesPtr->entryOptionTables[RADIO_BUTTON_ENTRY] = | ||
Tk_CreateOptionTable(interp, specsArray[RADIO_BUTTON_ENTRY]); | ||
optionTablesPtr->entryOptionTables[CHECK_BUTTON_ENTRY] = | ||
Tk_CreateOptionTable(interp, specsArray[CHECK_BUTTON_ENTRY]); | ||
Tcl_CreateObjCommand(interp, "menu", MenuCmd, | ||
(ClientData) optionTablesPtr, NULL); | ||
if (Tcl_IsSafe(interp)) { | ||
Tcl_HideCommand(interp, "menu", "menu"); | ||
} | ||
return TCL_OK; | ||
} | ||
/* | ||
*-------------------------------------------------------------- | ||
* | ||
* MenuCmd -- | ||
* | ||
* This procedure is invoked to process the "menu" Tcl | ||
* command. See the user documentation for details on | ||
* what it does. | ||
* | ||
* Results: | ||
* A standard Tcl result. | ||
* | ||
* Side effects: | ||
* See the user documentation. | ||
* | ||
*-------------------------------------------------------------- | ||
*/ | ||
static int | ||
MenuCmd(clientData, interp, objc, objv) | ||
ClientData clientData; /* Main window associated with | ||
* interpreter. */ | ||
Tcl_Interp *interp; /* Current interpreter. */ | ||
int objc; /* Number of arguments. */ | ||
Tcl_Obj *CONST objv[]; /* Argument strings. */ | ||
{ | ||
Tk_Window tkwin = Tk_MainWindow(interp); | ||
Tk_Window new; | ||
register TkMenu *menuPtr; | ||
TkMenuReferences *menuRefPtr; | ||
int i, index; | ||
int toplevel; | ||
char *windowName; | ||
static char *typeStringList[] = {"-type", (char *) NULL}; | ||
TkMenuOptionTables *optionTablesPtr = (TkMenuOptionTables *) clientData; | ||
if (objc < 2) { | ||
Tcl_WrongNumArgs(interp, 1, objv, "pathName ?options?"); | ||
return TCL_ERROR; | ||
} | ||
TkMenuInit(); | ||
toplevel = 1; | ||
for (i = 2; i < (objc - 1); i++) { | ||
if (Tcl_GetIndexFromObj(NULL, objv[i], typeStringList, NULL, 0, &index) | ||
!= TCL_ERROR) { | ||
if ((Tcl_GetIndexFromObj(NULL, objv[i + 1], menuTypeStrings, NULL, | ||
0, &index) == TCL_OK) && (index == MENUBAR)) { | ||
toplevel = 0; | ||
} | ||
break; | ||
} | ||
} | ||
windowName = Tcl_GetStringFromObj(objv[1], NULL); | ||
new = Tk_CreateWindowFromPath(interp, tkwin, windowName, toplevel ? "" | ||
: NULL); | ||
if (new == NULL) { | ||
return TCL_ERROR; | ||
} | ||
/* | ||
* Initialize the data structure for the menu. | ||
*/ | ||
menuPtr = (TkMenu *) ckalloc(sizeof(TkMenu)); | ||
menuPtr->tkwin = new; | ||
menuPtr->display = Tk_Display(new); | ||
menuPtr->interp = interp; | ||
menuPtr->widgetCmd = Tcl_CreateObjCommand(interp, | ||
Tk_PathName(menuPtr->tkwin), MenuWidgetObjCmd, | ||
(ClientData) menuPtr, MenuCmdDeletedProc); | ||
menuPtr->entries = NULL; | ||
menuPtr->numEntries = 0; | ||
menuPtr->active = -1; | ||
menuPtr->borderPtr = NULL; | ||
menuPtr->borderWidthPtr = NULL; | ||
menuPtr->reliefPtr = NULL; | ||
menuPtr->activeBorderPtr = NULL; | ||
menuPtr->activeBorderWidthPtr = NULL; | ||
menuPtr->fontPtr = NULL; | ||
menuPtr->fgPtr = NULL; | ||
menuPtr->disabledFgPtr = NULL; | ||
menuPtr->activeFgPtr = NULL; | ||
menuPtr->indicatorFgPtr = NULL; | ||
menuPtr->tearoff = 0; | ||
menuPtr->tearoffCommandPtr = NULL; | ||
menuPtr->cursorPtr = None; | ||
menuPtr->takeFocusPtr = NULL; | ||
menuPtr->postCommandPtr = NULL; | ||
menuPtr->postCommandGeneration = 0; | ||
menuPtr->postedCascade = NULL; | ||
menuPtr->nextInstancePtr = NULL; | ||
menuPtr->masterMenuPtr = menuPtr; | ||
menuPtr->menuType = UNKNOWN_TYPE; | ||
menuPtr->menuFlags = 0; | ||
menuPtr->parentTopLevelPtr = NULL; | ||
menuPtr->menuTypePtr = NULL; | ||
menuPtr->titlePtr = NULL; | ||
menuPtr->errorStructPtr = NULL; | ||
menuPtr->optionTablesPtr = optionTablesPtr; | ||
TkMenuInitializeDrawingFields(menuPtr); | ||
Tk_SetClass(menuPtr->tkwin, "Menu"); | ||
TkSetClassProcs(menuPtr->tkwin, &menuClass, (ClientData) menuPtr); | ||
if (Tk_InitOptions(interp, (char *) menuPtr, | ||
menuPtr->optionTablesPtr->menuOptionTable, menuPtr->tkwin) | ||
!= TCL_OK) { | ||
Tk_DestroyWindow(menuPtr->tkwin); | ||
ckfree((char *) menuPtr); | ||
return TCL_ERROR; | ||
} | ||
menuRefPtr = TkCreateMenuReferences(menuPtr->interp, | ||
Tk_PathName(menuPtr->tkwin)); | ||
menuRefPtr->menuPtr = menuPtr; | ||
menuPtr->menuRefPtr = menuRefPtr; | ||
if (TCL_OK != TkpNewMenu(menuPtr)) { | ||
Tk_DestroyWindow(menuPtr->tkwin); | ||
ckfree((char *) menuPtr); | ||
return TCL_ERROR; | ||
} | ||
Tk_CreateEventHandler(new, ExposureMask|StructureNotifyMask|ActivateMask, | ||
TkMenuEventProc, (ClientData) menuPtr); | ||
if (ConfigureMenu(interp, menuPtr, objc - 2, objv + 2) != TCL_OK) { | ||
Tk_DestroyWindow(menuPtr->tkwin); | ||
return TCL_ERROR; | ||
} | ||
/* | ||
* If a menu has a parent menu pointing to it as a cascade entry, the | ||
* parent menu needs to be told that this menu now exists so that | ||
* the platform-part of the menu is correctly updated. | ||
* | ||
* If a menu has an instance and has cascade entries, then each cascade | ||
* menu must also have a parallel instance. This is especially true on | ||
* the Mac, where each menu has to have a separate title everytime it is in | ||
* a menubar. For instance, say you have a menu .m1 with a cascade entry | ||
* for .m2, where .m2 does not exist yet. You then put .m1 into a menubar. | ||
* This creates a menubar instance for .m1, but since .m2 is not there, | ||
* nothing else happens. When we go to create .m2, we hook it up properly | ||
* with .m1. However, we now need to clone .m2 and assign the clone of .m2 | ||
* to be the cascade entry for the clone of .m1. This is special case | ||
* #1 listed in the introductory comment. | ||
*/ | ||
if (menuRefPtr->parentEntryPtr != NULL) { | ||
TkMenuEntry *cascadeListPtr = menuRefPtr->parentEntryPtr; | ||
TkMenuEntry *nextCascadePtr; | ||
Tcl_Obj *newMenuName; | ||
Tcl_Obj *newObjv[2]; | ||
while (cascadeListPtr != NULL) { | ||
nextCascadePtr = cascadeListPtr->nextCascadePtr; | ||
/* | ||
* If we have a new master menu, and an existing cloned menu | ||
* points to this menu in a cascade entry, we have to clone | ||
* the new menu and point the entry to the clone instead | ||
* of the menu we are creating. Otherwise, ConfigureMenuEntry | ||
* will hook up the platform-specific cascade linkages now | ||
* that the menu we are creating exists. | ||
*/ | ||
if ((menuPtr->masterMenuPtr != menuPtr) | ||
|| ((menuPtr->masterMenuPtr == menuPtr) | ||
&& ((cascadeListPtr->menuPtr->masterMenuPtr | ||
== cascadeListPtr->menuPtr)))) { | ||
newObjv[0] = Tcl_NewStringObj("-menu", -1); | ||
newObjv[1] = Tcl_NewStringObj(Tk_PathName(menuPtr->tkwin), -1); | ||
Tcl_IncrRefCount(newObjv[0]); | ||
Tcl_IncrRefCount(newObjv[1]); | ||
ConfigureMenuEntry(cascadeListPtr, 2, newObjv); | ||
Tcl_DecrRefCount(newObjv[0]); | ||
Tcl_DecrRefCount(newObjv[1]); | ||
} else { | ||
Tcl_Obj *normalPtr = Tcl_NewStringObj("normal", -1); | ||
Tcl_Obj *windowNamePtr = Tcl_NewStringObj( | ||
Tk_PathName(cascadeListPtr->menuPtr->tkwin), -1); | ||
Tcl_IncrRefCount(normalPtr); | ||
Tcl_IncrRefCount(windowNamePtr); | ||
newMenuName = TkNewMenuName(menuPtr->interp, | ||
windowNamePtr, menuPtr); | ||
Tcl_IncrRefCount(newMenuName); | ||
CloneMenu(menuPtr, newMenuName, normalPtr); | ||
/* | ||
* Now we can set the new menu instance to be the cascade entry | ||
* of the parent's instance. | ||
*/ | ||
newObjv[0] = Tcl_NewStringObj("-menu", -1); | ||
newObjv[1] = newMenuName; | ||
Tcl_IncrRefCount(newObjv[0]); | ||
ConfigureMenuEntry(cascadeListPtr, 2, newObjv); | ||
Tcl_DecrRefCount(normalPtr); | ||
Tcl_DecrRefCount(newObjv[0]); | ||
Tcl_DecrRefCount(newObjv[1]); | ||
Tcl_DecrRefCount(windowNamePtr); | ||
} | ||
cascadeListPtr = nextCascadePtr; | ||
} | ||
} | ||
/* | ||
* If there already exist toplevel widgets that refer to this menu, | ||
* find them and notify them so that they can reconfigure their | ||
* geometry to reflect the menu. | ||
*/ | ||
if (menuRefPtr->topLevelListPtr != NULL) { | ||
TkMenuTopLevelList *topLevelListPtr = menuRefPtr->topLevelListPtr; | ||
TkMenuTopLevelList *nextPtr; | ||
Tk_Window listtkwin; | ||
while (topLevelListPtr != NULL) { | ||
/* | ||
* Need to get the next pointer first. TkSetWindowMenuBar | ||
* changes the list, so that the next pointer is different | ||
* after calling it. | ||
*/ | ||
nextPtr = topLevelListPtr->nextPtr; | ||
listtkwin = topLevelListPtr->tkwin; | ||
TkSetWindowMenuBar(menuPtr->interp, listtkwin, | ||
Tk_PathName(menuPtr->tkwin), Tk_PathName(menuPtr->tkwin)); | ||
topLevelListPtr = nextPtr; | ||
} | ||
} | ||
Tcl_SetResult(interp, Tk_PathName(menuPtr->tkwin), TCL_STATIC); | ||
return TCL_OK; | ||
} | ||
/* | ||
*-------------------------------------------------------------- | ||
* | ||
* MenuWidgetObjCmd -- | ||
* | ||
* This procedure is invoked to process the Tcl command | ||
* that corresponds to a widget managed by this module. | ||
* See the user documentation for details on what it does. | ||
* | ||
* Results: | ||
* A standard Tcl result. | ||
* | ||
* Side effects: | ||
* See the user documentation. | ||
* | ||
*-------------------------------------------------------------- | ||
*/ | ||
static int | ||
MenuWidgetObjCmd(clientData, interp, objc, objv) | ||
ClientData clientData; /* Information about menu widget. */ | ||
Tcl_Interp *interp; /* Current interpreter. */ | ||
int objc; /* Number of arguments. */ | ||
Tcl_Obj *CONST objv[]; /* Argument strings. */ | ||
{ | ||
register TkMenu *menuPtr = (TkMenu *) clientData; | ||
register TkMenuEntry *mePtr; | ||
int result = TCL_OK; | ||
int option; | ||
if (objc < 2) { | ||
Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?"); | ||
return TCL_ERROR; | ||
} | ||
if (Tcl_GetIndexFromObj(interp, objv[1], menuOptions, "option", 0, | ||
&option) != TCL_OK) { | ||
return TCL_ERROR; | ||
} | ||
Tcl_Preserve((ClientData) menuPtr); | ||
switch ((enum options) option) { | ||
case MENU_ACTIVATE: { | ||
int index; | ||
if (objc != 3) { | ||
Tcl_WrongNumArgs(interp, 1, objv, "activate index"); | ||
goto error; | ||
} | ||
if (TkGetMenuIndex(interp, menuPtr, objv[2], 0, &index) | ||
!= TCL_OK) { | ||
goto error; | ||
} | ||
if (menuPtr->active == index) { | ||
goto done; | ||
} | ||
if ((index >= 0) | ||
&& ((menuPtr->entries[index]->type == SEPARATOR_ENTRY) | ||
|| (menuPtr->entries[index]->state | ||
== ENTRY_DISABLED))) { | ||
index = -1; | ||
} | ||
result = TkActivateMenuEntry(menuPtr, index); | ||
break; | ||
} | ||
case MENU_ADD: | ||
if (objc < 3) { | ||
Tcl_WrongNumArgs(interp, 1, objv, "add type ?options?"); | ||
goto error; | ||
} | ||
if (MenuAddOrInsert(interp, menuPtr, (Tcl_Obj *) NULL, | ||
objc - 2, objv + 2) != TCL_OK) { | ||
goto error; | ||
} | ||
break; | ||
case MENU_CGET: { | ||
Tcl_Obj *resultPtr; | ||
if (objc != 3) { | ||
Tcl_WrongNumArgs(interp, 1, objv, "cget option"); | ||
goto error; | ||
} | ||
resultPtr = Tk_GetOptionValue(interp, (char *) menuPtr, | ||
menuPtr->optionTablesPtr->menuOptionTable, objv[2], | ||
menuPtr->tkwin); | ||
if (resultPtr == NULL) { | ||
goto error; | ||
} | ||
Tcl_SetObjResult(interp, resultPtr); | ||
break; | ||
} | ||
case MENU_CLONE: | ||
if ((objc < 3) || (objc > 4)) { | ||
Tcl_WrongNumArgs(interp, 1, objv, | ||
"clone newMenuName ?menuType?"); | ||
goto error; | ||
} | ||
result = CloneMenu(menuPtr, objv[2], (objc == 3) ? NULL : objv[3]); | ||
break; | ||
case MENU_CONFIGURE: { | ||
Tcl_Obj *resultPtr; | ||
if (objc == 2) { | ||
resultPtr = Tk_GetOptionInfo(interp, (char *) menuPtr, | ||
menuPtr->optionTablesPtr->menuOptionTable, | ||
(Tcl_Obj *) NULL, menuPtr->tkwin); | ||
if (resultPtr == NULL) { | ||
result = TCL_ERROR; | ||
} else { | ||
result = TCL_OK; | ||
Tcl_SetObjResult(interp, resultPtr); | ||
} | ||
} else if (objc == 3) { | ||
resultPtr = Tk_GetOptionInfo(interp, (char *) menuPtr, | ||
menuPtr->optionTablesPtr->menuOptionTable, | ||
objv[2], menuPtr->tkwin); | ||
if (resultPtr == NULL) { | ||
result = TCL_ERROR; | ||
} else { | ||
result = TCL_OK; | ||
Tcl_SetObjResult(interp, resultPtr); | ||
} | ||
} else { | ||
result = ConfigureMenu(interp, menuPtr, objc - 2, objv + 2); | ||
} | ||
if (result != TCL_OK) { | ||
goto error; | ||
} | ||
break; | ||
} | ||
case MENU_DELETE: { | ||
int first, last; | ||
if ((objc != 3) && (objc != 4)) { | ||
Tcl_WrongNumArgs(interp, 1, objv, "delete first ?last?"); | ||
goto error; | ||
} | ||
if (TkGetMenuIndex(interp, menuPtr, objv[2], 0, &first) | ||
!= TCL_OK) { | ||
goto error; | ||
} | ||
if (objc == 3) { | ||
last = first; | ||
} else { | ||
if (TkGetMenuIndex(interp, menuPtr, objv[3], 0, &last) | ||
!= TCL_OK) { | ||
goto error; | ||
} | ||
} | ||
if (menuPtr->tearoff && (first == 0)) { | ||
/* | ||
* Sorry, can't delete the tearoff entry; must reconfigure | ||
* the menu. | ||
*/ | ||
first = 1; | ||
} | ||
if ((first < 0) || (last < first)) { | ||
goto done; | ||
} | ||
DeleteMenuCloneEntries(menuPtr, first, last); | ||
break; | ||
} | ||
case MENU_ENTRYCGET: { | ||
int index; | ||
Tcl_Obj *resultPtr; | ||
if (objc != 4) { | ||
Tcl_WrongNumArgs(interp, 1, objv, "entrycget index option"); | ||
goto error; | ||
} | ||
if (TkGetMenuIndex(interp, menuPtr, objv[2], 0, &index) | ||
!= TCL_OK) { | ||
goto error; | ||
} | ||
if (index < 0) { | ||
goto done; | ||
} | ||
mePtr = menuPtr->entries[index]; | ||
Tcl_Preserve((ClientData) mePtr); | ||
resultPtr = Tk_GetOptionValue(interp, (char *) mePtr, | ||
mePtr->optionTable, objv[3], menuPtr->tkwin); | ||
Tcl_Release((ClientData) mePtr); | ||
if (resultPtr == NULL) { | ||
goto error; | ||
} | ||
Tcl_SetObjResult(interp, resultPtr); | ||
break; | ||
} | ||
case MENU_ENTRYCONFIGURE: { | ||
int index; | ||
Tcl_Obj *resultPtr; | ||
if (objc < 3) { | ||
Tcl_WrongNumArgs(interp, 1, objv, | ||
"entryconfigure index ?option value ...?"); | ||
goto error; | ||
} | ||
if (TkGetMenuIndex(interp, menuPtr, objv[2], 0, &index) | ||
!= TCL_OK) { | ||
goto error; | ||
} | ||
if (index < 0) { | ||
goto done; | ||
} | ||
mePtr = menuPtr->entries[index]; | ||
Tcl_Preserve((ClientData) mePtr); | ||
if (objc == 3) { | ||
resultPtr = Tk_GetOptionInfo(interp, (char *) mePtr, | ||
mePtr->optionTable, (Tcl_Obj *) NULL, menuPtr->tkwin); | ||
if (resultPtr == NULL) { | ||
result = TCL_ERROR; | ||
} else { | ||
result = TCL_OK; | ||
Tcl_SetObjResult(interp, resultPtr); | ||
} | ||
} else if (objc == 4) { | ||
resultPtr = Tk_GetOptionInfo(interp, (char *) mePtr, | ||
mePtr->optionTable, objv[3], menuPtr->tkwin); | ||
if (resultPtr == NULL) { | ||
result = TCL_ERROR; | ||
} else { | ||
result = TCL_OK; | ||
Tcl_SetObjResult(interp, resultPtr); | ||
} | ||
} else { | ||
result = ConfigureMenuCloneEntries(interp, menuPtr, index, | ||
objc - 3, objv + 3); | ||
} | ||
Tcl_Release((ClientData) mePtr); | ||
break; | ||
} | ||
case MENU_INDEX: { | ||
int index; | ||
if (objc != 3) { | ||
Tcl_WrongNumArgs(interp, 1, objv, "index string"); | ||
goto error; | ||
} | ||
if (TkGetMenuIndex(interp, menuPtr, objv[2], 0, &index) | ||
!= TCL_OK) { | ||
goto error; | ||
} | ||
if (index < 0) { | ||
Tcl_SetResult(interp, "none", TCL_STATIC); | ||
} else { | ||
Tcl_SetIntObj(Tcl_GetObjResult(interp), index); | ||
} | ||
break; | ||
} | ||
case MENU_INSERT: | ||
if (objc < 4) { | ||
Tcl_WrongNumArgs(interp, 1, objv, | ||
"insert index type ?options?"); | ||
goto error; | ||
} | ||
if (MenuAddOrInsert(interp, menuPtr, objv[2], objc - 3, | ||
objv + 3) != TCL_OK) { | ||
goto error; | ||
} | ||
break; | ||
case MENU_INVOKE: { | ||
int index; | ||
if (objc != 3) { | ||
Tcl_WrongNumArgs(interp, 1, objv, "invoke index"); | ||
goto error; | ||
} | ||
if (TkGetMenuIndex(interp, menuPtr, objv[2], 0, &index) | ||
!= TCL_OK) { | ||
goto error; | ||
} | ||
if (index < 0) { | ||
goto done; | ||
} | ||
result = TkInvokeMenu(interp, menuPtr, index); | ||
break; | ||
} | ||
case MENU_POST: { | ||
int x, y; | ||
if (objc != 4) { | ||
Tcl_WrongNumArgs(interp, 1, objv, "post x y"); | ||
goto error; | ||
} | ||
if ((Tcl_GetIntFromObj(interp, objv[2], &x) != TCL_OK) | ||
|| (Tcl_GetIntFromObj(interp, objv[3], &y) != TCL_OK)) { | ||
goto error; | ||
} | ||
/* | ||
* Tearoff menus are posted differently on Mac and Windows than | ||
* non-tearoffs. TkpPostMenu does not actually map the menu's | ||
* window on those platforms, and popup menus have to be | ||
* handled specially. | ||
*/ | ||
if (menuPtr->menuType != TEAROFF_MENU) { | ||
result = TkpPostMenu(interp, menuPtr, x, y); | ||
} else { | ||
result = TkPostTearoffMenu(interp, menuPtr, x, y); | ||
} | ||
break; | ||
} | ||
case MENU_POSTCASCADE: { | ||
int index; | ||
if (objc != 3) { | ||
Tcl_WrongNumArgs(interp, 1, objv, "postcascade index"); | ||
goto error; | ||
} | ||
if (TkGetMenuIndex(interp, menuPtr, objv[2], 0, &index) | ||
!= TCL_OK) { | ||
goto error; | ||
} | ||
if ((index < 0) || (menuPtr->entries[index]->type | ||
!= CASCADE_ENTRY)) { | ||
result = TkPostSubmenu(interp, menuPtr, (TkMenuEntry *) NULL); | ||
} else { | ||
result = TkPostSubmenu(interp, menuPtr, | ||
menuPtr->entries[index]); | ||
} | ||
break; | ||
} | ||
case MENU_TYPE: { | ||
int index; | ||
if (objc != 3) { | ||
Tcl_WrongNumArgs(interp, 1, objv, "type index"); | ||
goto error; | ||
} | ||
if (TkGetMenuIndex(interp, menuPtr, objv[2], 0, &index) | ||
!= TCL_OK) { | ||
goto error; | ||
} | ||
if (index < 0) { | ||
goto done; | ||
} | ||
if (menuPtr->entries[index]->type == TEAROFF_ENTRY) { | ||
Tcl_SetResult(interp, "tearoff", TCL_STATIC); | ||
} else { | ||
Tcl_SetResult(interp, | ||
menuEntryTypeStrings[menuPtr->entries[index]->type], | ||
TCL_STATIC); | ||
} | ||
break; | ||
} | ||
case MENU_UNPOST: | ||
if (objc != 2) { | ||
Tcl_WrongNumArgs(interp, 1, objv, "unpost"); | ||
goto error; | ||
} | ||
Tk_UnmapWindow(menuPtr->tkwin); | ||
result = TkPostSubmenu(interp, menuPtr, (TkMenuEntry *) NULL); | ||
break; | ||
case MENU_YPOSITION: | ||
if (objc != 3) { | ||
Tcl_WrongNumArgs(interp, 1, objv, "yposition index"); | ||
goto error; | ||
} | ||
result = MenuDoYPosition(interp, menuPtr, objv[2]); | ||
break; | ||
} | ||
done: | ||
Tcl_Release((ClientData) menuPtr); | ||
return result; | ||
error: | ||
Tcl_Release((ClientData) menuPtr); | ||
return TCL_ERROR; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* TkInvokeMenu -- | ||
* | ||
* Given a menu and an index, takes the appropriate action for the | ||
* entry associated with that index. | ||
* | ||
* Results: | ||
* Standard Tcl result. | ||
* | ||
* Side effects: | ||
* Commands may get excecuted; variables may get set; sub-menus may | ||
* get posted. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
int | ||
TkInvokeMenu(interp, menuPtr, index) | ||
Tcl_Interp *interp; /* The interp that the menu lives in. */ | ||
TkMenu *menuPtr; /* The menu we are invoking. */ | ||
int index; /* The zero based index of the item we | ||
* are invoking */ | ||
{ | ||
int result = TCL_OK; | ||
TkMenuEntry *mePtr; | ||
if (index < 0) { | ||
goto done; | ||
} | ||
mePtr = menuPtr->entries[index]; | ||
if (mePtr->state == ENTRY_DISABLED) { | ||
goto done; | ||
} | ||
Tcl_Preserve((ClientData) mePtr); | ||
if (mePtr->type == TEAROFF_ENTRY) { | ||
Tcl_DString ds; | ||
Tcl_DStringInit(&ds); | ||
Tcl_DStringAppend(&ds, "tkTearOffMenu ", -1); | ||
Tcl_DStringAppend(&ds, Tk_PathName(menuPtr->tkwin), -1); | ||
result = Tcl_Eval(interp, Tcl_DStringValue(&ds)); | ||
Tcl_DStringFree(&ds); | ||
} else if ((mePtr->type == CHECK_BUTTON_ENTRY) | ||
&& (mePtr->namePtr != NULL)) { | ||
Tcl_Obj *valuePtr; | ||
if (mePtr->entryFlags & ENTRY_SELECTED) { | ||
valuePtr = mePtr->offValuePtr; | ||
} else { | ||
valuePtr = mePtr->onValuePtr; | ||
} | ||
if (valuePtr == NULL) { | ||
valuePtr = Tcl_NewObj(); | ||
} | ||
Tcl_IncrRefCount(valuePtr); | ||
if (Tcl_ObjSetVar2(interp, mePtr->namePtr, NULL, valuePtr, | ||
TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) { | ||
result = TCL_ERROR; | ||
} | ||
Tcl_DecrRefCount(valuePtr); | ||
} else if ((mePtr->type == RADIO_BUTTON_ENTRY) | ||
&& (mePtr->namePtr != NULL)) { | ||
Tcl_Obj *valuePtr = mePtr->onValuePtr; | ||
if (valuePtr == NULL) { | ||
valuePtr = Tcl_NewObj(); | ||
} | ||
Tcl_IncrRefCount(valuePtr); | ||
if (Tcl_ObjSetVar2(interp, mePtr->namePtr, NULL, valuePtr, | ||
TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) { | ||
result = TCL_ERROR; | ||
} | ||
Tcl_DecrRefCount(valuePtr); | ||
} | ||
if ((result == TCL_OK) && (mePtr->commandPtr != NULL)) { | ||
Tcl_Obj *commandPtr = mePtr->commandPtr; | ||
Tcl_IncrRefCount(commandPtr); | ||
result = Tcl_EvalObjEx(interp, commandPtr, TCL_EVAL_GLOBAL); | ||
Tcl_DecrRefCount(commandPtr); | ||
} | ||
Tcl_Release((ClientData) mePtr); | ||
done: | ||
return result; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* DestroyMenuInstance -- | ||
* | ||
* This procedure is invoked by TkDestroyMenu | ||
* to clean up the internal structure of a menu at a safe time | ||
* (when no-one is using it anymore). Only takes care of one instance | ||
* of the menu. | ||
* | ||
* Results: | ||
* None. | ||
* | ||
* Side effects: | ||
* Everything associated with the menu is freed up. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
static void | ||
DestroyMenuInstance(menuPtr) | ||
TkMenu *menuPtr; /* Info about menu widget. */ | ||
{ | ||
int i; | ||
TkMenu *menuInstancePtr; | ||
TkMenuEntry *cascadePtr, *nextCascadePtr; | ||
Tcl_Obj *newObjv[2]; | ||
TkMenu *parentMasterMenuPtr; | ||
TkMenuEntry *parentMasterEntryPtr; | ||
/* | ||
* If the menu has any cascade menu entries pointing to it, the cascade | ||
* entries need to be told that the menu is going away. We need to clear | ||
* the menu ptr field in the menu reference at this point in the code | ||
* so that everything else can forget about this menu properly. We also | ||
* need to reset -menu field of all entries that are not master menus | ||
* back to this entry name if this is a master menu pointed to by another | ||
* master menu. If there is a clone menu that points to this menu, | ||
* then this menu is itself a clone, so when this menu goes away, | ||
* the -menu field of the pointing entry must be set back to this | ||
* menu's master menu name so that later if another menu is created | ||
* the cascade hierarchy can be maintained. | ||
*/ | ||
TkpDestroyMenu(menuPtr); | ||
cascadePtr = menuPtr->menuRefPtr->parentEntryPtr; | ||
menuPtr->menuRefPtr->menuPtr = NULL; | ||
TkFreeMenuReferences(menuPtr->menuRefPtr); | ||
for (; cascadePtr != NULL; cascadePtr = nextCascadePtr) { | ||
nextCascadePtr = cascadePtr->nextCascadePtr; | ||
if (menuPtr->masterMenuPtr != menuPtr) { | ||
Tcl_Obj *menuNamePtr = Tcl_NewStringObj("-menu", -1); | ||
parentMasterMenuPtr = cascadePtr->menuPtr->masterMenuPtr; | ||
parentMasterEntryPtr = | ||
parentMasterMenuPtr->entries[cascadePtr->index]; | ||
newObjv[0] = menuNamePtr; | ||
newObjv[1] = parentMasterEntryPtr->namePtr; | ||
/* | ||
* It is possible that the menu info is out of sync, and | ||
* these things point to NULL, so verify existence [Bug: 3402] | ||
*/ | ||
if (newObjv[0] && newObjv[1]) { | ||
Tcl_IncrRefCount(newObjv[0]); | ||
Tcl_IncrRefCount(newObjv[1]); | ||
ConfigureMenuEntry(cascadePtr, 2, newObjv); | ||
Tcl_DecrRefCount(newObjv[0]); | ||
Tcl_DecrRefCount(newObjv[1]); | ||
} | ||
} else { | ||
ConfigureMenuEntry(cascadePtr, 0, (Tcl_Obj **) NULL); | ||
} | ||
} | ||
if (menuPtr->masterMenuPtr != menuPtr) { | ||
for (menuInstancePtr = menuPtr->masterMenuPtr; | ||
menuInstancePtr != NULL; | ||
menuInstancePtr = menuInstancePtr->nextInstancePtr) { | ||
if (menuInstancePtr->nextInstancePtr == menuPtr) { | ||
menuInstancePtr->nextInstancePtr = | ||
menuInstancePtr->nextInstancePtr->nextInstancePtr; | ||
break; | ||
} | ||
} | ||
} else if (menuPtr->nextInstancePtr != NULL) { | ||
panic("Attempting to delete master menu when there are still clones."); | ||
} | ||
/* | ||
* Free up all the stuff that requires special handling, then | ||
* let Tk_FreeConfigOptions handle all the standard option-related | ||
* stuff. | ||
*/ | ||
for (i = menuPtr->numEntries; --i >= 0; ) { | ||
/* | ||
* As each menu entry is deleted from the end of the array of | ||
* entries, decrement menuPtr->numEntries. Otherwise, the act of | ||
* deleting menu entry i will dereference freed memory attempting | ||
* to queue a redraw for menu entries (i+1)...numEntries. | ||
*/ | ||
DestroyMenuEntry((char *) menuPtr->entries[i]); | ||
menuPtr->numEntries = i; | ||
} | ||
if (menuPtr->entries != NULL) { | ||
ckfree((char *) menuPtr->entries); | ||
} | ||
TkMenuFreeDrawOptions(menuPtr); | ||
Tk_FreeConfigOptions((char *) menuPtr, | ||
menuPtr->optionTablesPtr->menuOptionTable, menuPtr->tkwin); | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* TkDestroyMenu -- | ||
* | ||
* This procedure is invoked by Tcl_EventuallyFree or Tcl_Release | ||
* to clean up the internal structure of a menu at a safe time | ||
* (when no-one is using it anymore). If called on a master instance, | ||
* destroys all of the slave instances. If called on a non-master | ||
* instance, just destroys that instance. | ||
* | ||
* Results: | ||
* None. | ||
* | ||
* Side effects: | ||
* Everything associated with the menu is freed up. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
void | ||
TkDestroyMenu(menuPtr) | ||
TkMenu *menuPtr; /* Info about menu widget. */ | ||
{ | ||
TkMenu *menuInstancePtr; | ||
TkMenuTopLevelList *topLevelListPtr, *nextTopLevelPtr; | ||
if (menuPtr->menuFlags & MENU_DELETION_PENDING) { | ||
return; | ||
} | ||
/* | ||
* Now destroy all non-tearoff instances of this menu if this is a | ||
* parent menu. Is this loop safe enough? Are there going to be | ||
* destroy bindings on child menus which kill the parent? If not, | ||
* we have to do a slightly more complex scheme. | ||
*/ | ||
if (menuPtr->masterMenuPtr == menuPtr) { | ||
menuPtr->menuFlags |= MENU_DELETION_PENDING; | ||
while (menuPtr->nextInstancePtr != NULL) { | ||
menuInstancePtr = menuPtr->nextInstancePtr; | ||
menuPtr->nextInstancePtr = menuInstancePtr->nextInstancePtr; | ||
if (menuInstancePtr->tkwin != NULL) { | ||
Tk_DestroyWindow(menuInstancePtr->tkwin); | ||
} | ||
} | ||
menuPtr->menuFlags &= ~MENU_DELETION_PENDING; | ||
} | ||
/* | ||
* If any toplevel widgets have this menu as their menubar, | ||
* the geometry of the window may have to be recalculated. | ||
*/ | ||
topLevelListPtr = menuPtr->menuRefPtr->topLevelListPtr; | ||
while (topLevelListPtr != NULL) { | ||
nextTopLevelPtr = topLevelListPtr->nextPtr; | ||
TkpSetWindowMenuBar(topLevelListPtr->tkwin, NULL); | ||
topLevelListPtr = nextTopLevelPtr; | ||
} | ||
DestroyMenuInstance(menuPtr); | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* UnhookCascadeEntry -- | ||
* | ||
* This entry is removed from the list of entries that point to the | ||
* cascade menu. This is done in preparation for changing the menu | ||
* that this entry points to. | ||
* | ||
* Results: | ||
* None | ||
* | ||
* Side effects: | ||
* The appropriate lists are modified. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
static void | ||
UnhookCascadeEntry(mePtr) | ||
TkMenuEntry *mePtr; /* The cascade entry we are removing | ||
* from the cascade list. */ | ||
{ | ||
TkMenuEntry *cascadeEntryPtr; | ||
TkMenuEntry *prevCascadePtr; | ||
TkMenuReferences *menuRefPtr; | ||
menuRefPtr = mePtr->childMenuRefPtr; | ||
if (menuRefPtr == NULL) { | ||
return; | ||
} | ||
cascadeEntryPtr = menuRefPtr->parentEntryPtr; | ||
if (cascadeEntryPtr == NULL) { | ||
return; | ||
} | ||
/* | ||
* Singularly linked list deletion. The two special cases are | ||
* 1. one element; 2. The first element is the one we want. | ||
*/ | ||
if (cascadeEntryPtr == mePtr) { | ||
if (cascadeEntryPtr->nextCascadePtr == NULL) { | ||
/* | ||
* This is the last menu entry which points to this | ||
* menu, so we need to clear out the list pointer in the | ||
* cascade itself. | ||
*/ | ||
menuRefPtr->parentEntryPtr = NULL; | ||
TkFreeMenuReferences(menuRefPtr); | ||
} else { | ||
menuRefPtr->parentEntryPtr = cascadeEntryPtr->nextCascadePtr; | ||
} | ||
mePtr->nextCascadePtr = NULL; | ||
} else { | ||
for (prevCascadePtr = cascadeEntryPtr, | ||
cascadeEntryPtr = cascadeEntryPtr->nextCascadePtr; | ||
cascadeEntryPtr != NULL; | ||
prevCascadePtr = cascadeEntryPtr, | ||
cascadeEntryPtr = cascadeEntryPtr->nextCascadePtr) { | ||
if (cascadeEntryPtr == mePtr){ | ||
prevCascadePtr->nextCascadePtr = | ||
cascadeEntryPtr->nextCascadePtr; | ||
cascadeEntryPtr->nextCascadePtr = NULL; | ||
break; | ||
} | ||
} | ||
} | ||
mePtr->childMenuRefPtr = NULL; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* DestroyMenuEntry -- | ||
* | ||
* This procedure is invoked by Tcl_EventuallyFree or Tcl_Release | ||
* to clean up the internal structure of a menu entry at a safe time | ||
* (when no-one is using it anymore). | ||
* | ||
* Results: | ||
* None. | ||
* | ||
* Side effects: | ||
* Everything associated with the menu entry is freed. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
static void | ||
DestroyMenuEntry(memPtr) | ||
char *memPtr; /* Pointer to entry to be freed. */ | ||
{ | ||
register TkMenuEntry *mePtr = (TkMenuEntry *) memPtr; | ||
TkMenu *menuPtr = mePtr->menuPtr; | ||
if (menuPtr->postedCascade == mePtr) { | ||
/* | ||
* Ignore errors while unposting the menu, since it's possible | ||
* that the menu has already been deleted and the unpost will | ||
* generate an error. | ||
*/ | ||
TkPostSubmenu(menuPtr->interp, menuPtr, (TkMenuEntry *) NULL); | ||
} | ||
/* | ||
* Free up all the stuff that requires special handling, then | ||
* let Tk_FreeConfigOptions handle all the standard option-related | ||
* stuff. | ||
*/ | ||
if (mePtr->type == CASCADE_ENTRY) { | ||
UnhookCascadeEntry(mePtr); | ||
} | ||
if (mePtr->image != NULL) { | ||
Tk_FreeImage(mePtr->image); | ||
} | ||
if (mePtr->selectImage != NULL) { | ||
Tk_FreeImage(mePtr->selectImage); | ||
} | ||
if (((mePtr->type == CHECK_BUTTON_ENTRY) | ||
|| (mePtr->type == RADIO_BUTTON_ENTRY)) | ||
&& (mePtr->namePtr != NULL)) { | ||
char *varName = Tcl_GetStringFromObj(mePtr->namePtr, NULL); | ||
Tcl_UntraceVar(menuPtr->interp, varName, | ||
TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, | ||
MenuVarProc, (ClientData) mePtr); | ||
} | ||
TkpDestroyMenuEntry(mePtr); | ||
TkMenuEntryFreeDrawOptions(mePtr); | ||
Tk_FreeConfigOptions((char *) mePtr, mePtr->optionTable, menuPtr->tkwin); | ||
ckfree((char *) mePtr); | ||
} | ||
/* | ||
*--------------------------------------------------------------------------- | ||
* | ||
* MenuWorldChanged -- | ||
* | ||
* This procedure is called when the world has changed in some | ||
* way (such as the fonts in the system changing) and the widget needs | ||
* to recompute all its graphics contexts and determine its new geometry. | ||
* | ||
* Results: | ||
* None. | ||
* | ||
* Side effects: | ||
* Menu will be relayed out and redisplayed. | ||
* | ||
*--------------------------------------------------------------------------- | ||
*/ | ||
static void | ||
MenuWorldChanged(instanceData) | ||
ClientData instanceData; /* Information about widget. */ | ||
{ | ||
TkMenu *menuPtr = (TkMenu *) instanceData; | ||
int i; | ||
TkMenuConfigureDrawOptions(menuPtr); | ||
for (i = 0; i < menuPtr->numEntries; i++) { | ||
TkMenuConfigureEntryDrawOptions(menuPtr->entries[i], | ||
menuPtr->entries[i]->index); | ||
TkpConfigureMenuEntry(menuPtr->entries[i]); | ||
} | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* ConfigureMenu -- | ||
* | ||
* This procedure is called to process an argv/argc list, plus | ||
* the Tk option database, in order to configure (or | ||
* reconfigure) a menu widget. | ||
* | ||
* Results: | ||
* The return value is a standard Tcl result. If TCL_ERROR is | ||
* returned, then the interp's result contains an error message. | ||
* | ||
* Side effects: | ||
* Configuration information, such as colors, font, etc. get set | ||
* for menuPtr; old resources get freed, if there were any. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
static int | ||
ConfigureMenu(interp, menuPtr, objc, objv) | ||
Tcl_Interp *interp; /* Used for error reporting. */ | ||
register TkMenu *menuPtr; /* Information about widget; may or may | ||
* not already have values for some fields. */ | ||
int objc; /* Number of valid entries in argv. */ | ||
Tcl_Obj *CONST objv[]; /* Arguments. */ | ||
{ | ||
int i; | ||
TkMenu *menuListPtr, *cleanupPtr; | ||
int result; | ||
for (menuListPtr = menuPtr->masterMenuPtr; menuListPtr != NULL; | ||
menuListPtr = menuListPtr->nextInstancePtr) { | ||
menuListPtr->errorStructPtr = (Tk_SavedOptions *) | ||
ckalloc(sizeof(Tk_SavedOptions)); | ||
result = Tk_SetOptions(interp, (char *) menuListPtr, | ||
menuListPtr->optionTablesPtr->menuOptionTable, objc, objv, | ||
menuListPtr->tkwin, menuListPtr->errorStructPtr, (int *) NULL); | ||
if (result != TCL_OK) { | ||
for (cleanupPtr = menuPtr->masterMenuPtr; | ||
cleanupPtr != menuListPtr; | ||
cleanupPtr = cleanupPtr->nextInstancePtr) { | ||
Tk_RestoreSavedOptions(cleanupPtr->errorStructPtr); | ||
ckfree((char *) cleanupPtr->errorStructPtr); | ||
cleanupPtr->errorStructPtr = NULL; | ||
} | ||
return TCL_ERROR; | ||
} | ||
/* | ||
* When a menu is created, the type is in all of the arguments | ||
* to the menu command. Let Tk_ConfigureWidget take care of | ||
* parsing them, and then set the type after we can look at | ||
* the type string. Once set, a menu's type cannot be changed | ||
*/ | ||
if (menuListPtr->menuType == UNKNOWN_TYPE) { | ||
Tcl_GetIndexFromObj(NULL, menuListPtr->menuTypePtr, | ||
menuTypeStrings, NULL, 0, &menuListPtr->menuType); | ||
/* | ||
* Configure the new window to be either a pop-up menu | ||
* or a tear-off menu. | ||
* We don't do this for menubars since they are not toplevel | ||
* windows. Also, since this gets called before CloneMenu has | ||
* a chance to set the menuType field, we have to look at the | ||
* menuTypeName field to tell that this is a menu bar. | ||
*/ | ||
if (menuListPtr->menuType == MASTER_MENU) { | ||
TkpMakeMenuWindow(menuListPtr->tkwin, 1); | ||
} else if (menuListPtr->menuType == TEAROFF_MENU) { | ||
TkpMakeMenuWindow(menuListPtr->tkwin, 0); | ||
} | ||
} | ||
/* | ||
* Depending on the -tearOff option, make sure that there is or | ||
* isn't an initial tear-off entry at the beginning of the menu. | ||
*/ | ||
if (menuListPtr->tearoff) { | ||
if ((menuListPtr->numEntries == 0) | ||
|| (menuListPtr->entries[0]->type != TEAROFF_ENTRY)) { | ||
if (MenuNewEntry(menuListPtr, 0, TEAROFF_ENTRY) == NULL) { | ||
if (menuListPtr->errorStructPtr != NULL) { | ||
for (cleanupPtr = menuPtr->masterMenuPtr; | ||
cleanupPtr != menuListPtr; | ||
cleanupPtr = cleanupPtr->nextInstancePtr) { | ||
Tk_RestoreSavedOptions(cleanupPtr->errorStructPtr); | ||
ckfree((char *) cleanupPtr->errorStructPtr); | ||
cleanupPtr->errorStructPtr = NULL; | ||
} | ||
Tk_RestoreSavedOptions(cleanupPtr->errorStructPtr); | ||
ckfree((char *) cleanupPtr->errorStructPtr); | ||
cleanupPtr->errorStructPtr = NULL; | ||
} | ||
return TCL_ERROR; | ||
} | ||
} | ||
} else if ((menuListPtr->numEntries > 0) | ||
&& (menuListPtr->entries[0]->type == TEAROFF_ENTRY)) { | ||
int i; | ||
Tcl_EventuallyFree((ClientData) menuListPtr->entries[0], | ||
DestroyMenuEntry); | ||
for (i = 0; i < menuListPtr->numEntries - 1; i++) { | ||
menuListPtr->entries[i] = menuListPtr->entries[i + 1]; | ||
menuListPtr->entries[i]->index = i; | ||
} | ||
menuListPtr->numEntries--; | ||
if (menuListPtr->numEntries == 0) { | ||
ckfree((char *) menuListPtr->entries); | ||
menuListPtr->entries = NULL; | ||
} | ||
} | ||
TkMenuConfigureDrawOptions(menuListPtr); | ||
/* | ||
* After reconfiguring a menu, we need to reconfigure all of the | ||
* entries in the menu, since some of the things in the children | ||
* (such as graphics contexts) may have to change to reflect changes | ||
* in the parent. | ||
*/ | ||
for (i = 0; i < menuListPtr->numEntries; i++) { | ||
TkMenuEntry *mePtr; | ||
mePtr = menuListPtr->entries[i]; | ||
ConfigureMenuEntry(mePtr, 0, (Tcl_Obj **) NULL); | ||
} | ||
TkEventuallyRecomputeMenu(menuListPtr); | ||
} | ||
for (cleanupPtr = menuPtr->masterMenuPtr; cleanupPtr != NULL; | ||
cleanupPtr = cleanupPtr->nextInstancePtr) { | ||
Tk_FreeSavedOptions(cleanupPtr->errorStructPtr); | ||
ckfree((char *) cleanupPtr->errorStructPtr); | ||
cleanupPtr->errorStructPtr = NULL; | ||
} | ||
return TCL_OK; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* PostProcessEntry -- | ||
* | ||
* This is called by ConfigureMenuEntry to do all of the configuration | ||
* after Tk_SetOptions is called. This is separate | ||
* so that error handling is easier. | ||
* | ||
* Results: | ||
* The return value is a standard Tcl result. If TCL_ERROR is | ||
* returned, then the interp's result contains an error message. | ||
* | ||
* Side effects: | ||
* Configuration information such as label and accelerator get | ||
* set for mePtr; old resources get freed, if there were any. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
static int | ||
PostProcessEntry(mePtr) | ||
TkMenuEntry *mePtr; /* The entry we are configuring. */ | ||
{ | ||
TkMenu *menuPtr = mePtr->menuPtr; | ||
int index = mePtr->index; | ||
char *name; | ||
Tk_Image image; | ||
/* | ||
* The code below handles special configuration stuff not taken | ||
* care of by Tk_ConfigureWidget, such as special processing for | ||
* defaults, sizing strings, graphics contexts, etc. | ||
*/ | ||
if (mePtr->labelPtr == NULL) { | ||
mePtr->labelLength = 0; | ||
} else { | ||
Tcl_GetStringFromObj(mePtr->labelPtr, &mePtr->labelLength); | ||
} | ||
if (mePtr->accelPtr == NULL) { | ||
mePtr->accelLength = 0; | ||
} else { | ||
Tcl_GetStringFromObj(mePtr->accelPtr, &mePtr->accelLength); | ||
} | ||
/* | ||
* If this is a cascade entry, the platform-specific data of the child | ||
* menu has to be updated. Also, the links that point to parents and | ||
* cascades have to be updated. | ||
*/ | ||
if ((mePtr->type == CASCADE_ENTRY) && (mePtr->namePtr != NULL)) { | ||
TkMenuEntry *cascadeEntryPtr; | ||
int alreadyThere; | ||
TkMenuReferences *menuRefPtr; | ||
char *oldHashKey = NULL; /* Initialization only needed to | ||
* prevent compiler warning. */ | ||
/* | ||
* This is a cascade entry. If the menu that the cascade entry | ||
* is pointing to has changed, we need to remove this entry | ||
* from the list of entries pointing to the old menu, and add a | ||
* cascade reference to the list of entries pointing to the | ||
* new menu. | ||
* | ||
* BUG: We are not recloning for special case #3 yet. | ||
*/ | ||
name = Tcl_GetStringFromObj(mePtr->namePtr, NULL); | ||
if (mePtr->childMenuRefPtr != NULL) { | ||
oldHashKey = Tcl_GetHashKey(TkGetMenuHashTable(menuPtr->interp), | ||
mePtr->childMenuRefPtr->hashEntryPtr); | ||
if (strcmp(oldHashKey, name) != 0) { | ||
UnhookCascadeEntry(mePtr); | ||
} | ||
} | ||
if ((mePtr->childMenuRefPtr == NULL) | ||
|| (strcmp(oldHashKey, name) != 0)) { | ||
menuRefPtr = TkCreateMenuReferences(menuPtr->interp, name); | ||
mePtr->childMenuRefPtr = menuRefPtr; | ||
if (menuRefPtr->parentEntryPtr == NULL) { | ||
menuRefPtr->parentEntryPtr = mePtr; | ||
} else { | ||
alreadyThere = 0; | ||
for (cascadeEntryPtr = menuRefPtr->parentEntryPtr; | ||
cascadeEntryPtr != NULL; | ||
cascadeEntryPtr = | ||
cascadeEntryPtr->nextCascadePtr) { | ||
if (cascadeEntryPtr == mePtr) { | ||
alreadyThere = 1; | ||
break; | ||
} | ||
} | ||
/* | ||
* Put the item at the front of the list. | ||
*/ | ||
if (!alreadyThere) { | ||
mePtr->nextCascadePtr = menuRefPtr->parentEntryPtr; | ||
menuRefPtr->parentEntryPtr = mePtr; | ||
} | ||
} | ||
} | ||
} | ||
if (TkMenuConfigureEntryDrawOptions(mePtr, index) != TCL_OK) { | ||
return TCL_ERROR; | ||
} | ||
if (TkpConfigureMenuEntry(mePtr) != TCL_OK) { | ||
return TCL_ERROR; | ||
} | ||
/* | ||
* Get the images for the entry, if there are any. Allocate the | ||
* new images before freeing the old ones, so that the reference | ||
* counts don't go to zero and cause image data to be discarded. | ||
*/ | ||
if (mePtr->imagePtr != NULL) { | ||
char *imageString = Tcl_GetStringFromObj(mePtr->imagePtr, NULL); | ||
image = Tk_GetImage(menuPtr->interp, menuPtr->tkwin, imageString, | ||
TkMenuImageProc, (ClientData) mePtr); | ||
if (image == NULL) { | ||
return TCL_ERROR; | ||
} | ||
} else { | ||
image = NULL; | ||
} | ||
if (mePtr->image != NULL) { | ||
Tk_FreeImage(mePtr->image); | ||
} | ||
mePtr->image = image; | ||
if (mePtr->selectImagePtr != NULL) { | ||
char *selectImageString = Tcl_GetStringFromObj( | ||
mePtr->selectImagePtr, NULL); | ||
image = Tk_GetImage(menuPtr->interp, menuPtr->tkwin, selectImageString, | ||
TkMenuSelectImageProc, (ClientData) mePtr); | ||
if (image == NULL) { | ||
return TCL_ERROR; | ||
} | ||
} else { | ||
image = NULL; | ||
} | ||
if (mePtr->selectImage != NULL) { | ||
Tk_FreeImage(mePtr->selectImage); | ||
} | ||
mePtr->selectImage = image; | ||
if ((mePtr->type == CHECK_BUTTON_ENTRY) | ||
|| (mePtr->type == RADIO_BUTTON_ENTRY)) { | ||
Tcl_Obj *valuePtr; | ||
char *name; | ||
if (mePtr->namePtr == NULL) { | ||
if (mePtr->labelPtr == NULL) { | ||
mePtr->namePtr = NULL; | ||
} else { | ||
mePtr->namePtr = Tcl_DuplicateObj(mePtr->labelPtr); | ||
Tcl_IncrRefCount(mePtr->namePtr); | ||
} | ||
} | ||
if (mePtr->onValuePtr == NULL) { | ||
if (mePtr->labelPtr == NULL) { | ||
mePtr->onValuePtr = NULL; | ||
} else { | ||
mePtr->onValuePtr = Tcl_DuplicateObj(mePtr->labelPtr); | ||
Tcl_IncrRefCount(mePtr->onValuePtr); | ||
} | ||
} | ||
/* | ||
* Select the entry if the associated variable has the | ||
* appropriate value, initialize the variable if it doesn't | ||
* exist, then set a trace on the variable to monitor future | ||
* changes to its value. | ||
*/ | ||
if (mePtr->namePtr != NULL) { | ||
valuePtr = Tcl_ObjGetVar2(menuPtr->interp, mePtr->namePtr, NULL, | ||
TCL_GLOBAL_ONLY); | ||
} else { | ||
valuePtr = NULL; | ||
} | ||
mePtr->entryFlags &= ~ENTRY_SELECTED; | ||
if (valuePtr != NULL) { | ||
if (mePtr->onValuePtr != NULL) { | ||
char *value = Tcl_GetStringFromObj(valuePtr, NULL); | ||
char *onValue = Tcl_GetStringFromObj(mePtr->onValuePtr, | ||
NULL); | ||
if (strcmp(value, onValue) == 0) { | ||
mePtr->entryFlags |= ENTRY_SELECTED; | ||
} | ||
} | ||
} else { | ||
if (mePtr->namePtr != NULL) { | ||
Tcl_ObjSetVar2(menuPtr->interp, mePtr->namePtr, NULL, | ||
(mePtr->type == CHECK_BUTTON_ENTRY) | ||
? mePtr->offValuePtr | ||
: Tcl_NewObj(), | ||
TCL_GLOBAL_ONLY); | ||
} | ||
} | ||
if (mePtr->namePtr != NULL) { | ||
name = Tcl_GetStringFromObj(mePtr->namePtr, NULL); | ||
Tcl_TraceVar(menuPtr->interp, name, | ||
TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, | ||
MenuVarProc, (ClientData) mePtr); | ||
} | ||
} | ||
return TCL_OK; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* ConfigureMenuEntry -- | ||
* | ||
* This procedure is called to process an argv/argc list in order | ||
* to configure (or reconfigure) one entry in a menu. | ||
* | ||
* Results: | ||
* The return value is a standard Tcl result. If TCL_ERROR is | ||
* returned, then the interp's result contains an error message. | ||
* | ||
* Side effects: | ||
* Configuration information such as label and accelerator get | ||
* set for mePtr; old resources get freed, if there were any. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
static int | ||
ConfigureMenuEntry(mePtr, objc, objv) | ||
register TkMenuEntry *mePtr; /* Information about menu entry; may | ||
* or may not already have values for | ||
* some fields. */ | ||
int objc; /* Number of valid entries in argv. */ | ||
Tcl_Obj *CONST objv[]; /* Arguments. */ | ||
{ | ||
TkMenu *menuPtr = mePtr->menuPtr; | ||
Tk_SavedOptions errorStruct; | ||
int result; | ||
/* | ||
* If this entry is a check button or radio button, then remove | ||
* its old trace procedure. | ||
*/ | ||
if ((mePtr->namePtr != NULL) | ||
&& ((mePtr->type == CHECK_BUTTON_ENTRY) | ||
|| (mePtr->type == RADIO_BUTTON_ENTRY))) { | ||
char *name = Tcl_GetStringFromObj(mePtr->namePtr, NULL); | ||
Tcl_UntraceVar(menuPtr->interp, name, | ||
TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, | ||
MenuVarProc, (ClientData) mePtr); | ||
} | ||
result = TCL_OK; | ||
if (menuPtr->tkwin != NULL) { | ||
if (Tk_SetOptions(menuPtr->interp, (char *) mePtr, | ||
mePtr->optionTable, objc, objv, menuPtr->tkwin, | ||
&errorStruct, (int *) NULL) != TCL_OK) { | ||
return TCL_ERROR; | ||
} | ||
result = PostProcessEntry(mePtr); | ||
if (result != TCL_OK) { | ||
Tk_RestoreSavedOptions(&errorStruct); | ||
PostProcessEntry(mePtr); | ||
} | ||
Tk_FreeSavedOptions(&errorStruct); | ||
} | ||
TkEventuallyRecomputeMenu(menuPtr); | ||
return result; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* ConfigureMenuCloneEntries -- | ||
* | ||
* Calls ConfigureMenuEntry for each menu in the clone chain. | ||
* | ||
* Results: | ||
* The return value is a standard Tcl result. If TCL_ERROR is | ||
* returned, then the interp's result contains an error message. | ||
* | ||
* Side effects: | ||
* Configuration information such as label and accelerator get | ||
* set for mePtr; old resources get freed, if there were any. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
static int | ||
ConfigureMenuCloneEntries(interp, menuPtr, index, objc, objv) | ||
Tcl_Interp *interp; /* Used for error reporting. */ | ||
TkMenu *menuPtr; /* Information about whole menu. */ | ||
int index; /* Index of mePtr within menuPtr's | ||
* entries. */ | ||
int objc; /* Number of valid entries in argv. */ | ||
Tcl_Obj *CONST objv[]; /* Arguments. */ | ||
{ | ||
TkMenuEntry *mePtr; | ||
TkMenu *menuListPtr; | ||
int cascadeEntryChanged = 0; | ||
TkMenuReferences *oldCascadeMenuRefPtr, *cascadeMenuRefPtr = NULL; | ||
Tcl_Obj *oldCascadePtr = NULL; | ||
char *newCascadeName; | ||
/* | ||
* Cascades are kind of tricky here. This is special case #3 in the comment | ||
* at the top of this file. Basically, if a menu is the master menu of a | ||
* clone chain, and has an entry with a cascade menu, the clones of | ||
* the menu will point to clones of the cascade menu. We have | ||
* to destroy the clones of the cascades, clone the new cascade | ||
* menu, and configure the entry to point to the new clone. | ||
*/ | ||
mePtr = menuPtr->masterMenuPtr->entries[index]; | ||
if (mePtr->type == CASCADE_ENTRY) { | ||
oldCascadePtr = mePtr->namePtr; | ||
if (oldCascadePtr != NULL) { | ||
Tcl_IncrRefCount(oldCascadePtr); | ||
} | ||
} | ||
if (ConfigureMenuEntry(mePtr, objc, objv) != TCL_OK) { | ||
return TCL_ERROR; | ||
} | ||
if (mePtr->type == CASCADE_ENTRY) { | ||
char *oldCascadeName; | ||
if (mePtr->namePtr != NULL) { | ||
newCascadeName = Tcl_GetStringFromObj(mePtr->namePtr, NULL); | ||
} else { | ||
newCascadeName = NULL; | ||
} | ||
if ((oldCascadePtr == NULL) && (mePtr->namePtr == NULL)) { | ||
cascadeEntryChanged = 0; | ||
} else if (((oldCascadePtr == NULL) && (mePtr->namePtr != NULL)) | ||
|| ((oldCascadePtr != NULL) | ||
&& (mePtr->namePtr == NULL))) { | ||
cascadeEntryChanged = 1; | ||
} else { | ||
oldCascadeName = Tcl_GetStringFromObj(oldCascadePtr, | ||
NULL); | ||
cascadeEntryChanged = (strcmp(oldCascadeName, newCascadeName) | ||
== 0); | ||
} | ||
if (oldCascadePtr != NULL) { | ||
Tcl_DecrRefCount(oldCascadePtr); | ||
} | ||
} | ||
if (cascadeEntryChanged) { | ||
if (mePtr->namePtr != NULL) { | ||
newCascadeName = Tcl_GetStringFromObj(mePtr->namePtr, NULL); | ||
cascadeMenuRefPtr = TkFindMenuReferences(menuPtr->interp, | ||
newCascadeName); | ||
} | ||
} | ||
for (menuListPtr = menuPtr->masterMenuPtr->nextInstancePtr; | ||
menuListPtr != NULL; | ||
menuListPtr = menuListPtr->nextInstancePtr) { | ||
mePtr = menuListPtr->entries[index]; | ||
if (cascadeEntryChanged && (mePtr->namePtr != NULL)) { | ||
oldCascadeMenuRefPtr = TkFindMenuReferencesObj(menuPtr->interp, | ||
mePtr->namePtr); | ||
if ((oldCascadeMenuRefPtr != NULL) | ||
&& (oldCascadeMenuRefPtr->menuPtr != NULL)) { | ||
RecursivelyDeleteMenu(oldCascadeMenuRefPtr->menuPtr); | ||
} | ||
} | ||
if (ConfigureMenuEntry(mePtr, objc, objv) != TCL_OK) { | ||
return TCL_ERROR; | ||
} | ||
if (cascadeEntryChanged && (mePtr->namePtr != NULL)) { | ||
if (cascadeMenuRefPtr->menuPtr != NULL) { | ||
Tcl_Obj *newObjv[2]; | ||
Tcl_Obj *newCloneNamePtr; | ||
Tcl_Obj *pathNamePtr = Tcl_NewStringObj( | ||
Tk_PathName(menuListPtr->tkwin), -1); | ||
Tcl_Obj *normalPtr = Tcl_NewStringObj("normal", -1); | ||
Tcl_Obj *menuObjPtr = Tcl_NewStringObj("-menu", -1); | ||
Tcl_IncrRefCount(pathNamePtr); | ||
newCloneNamePtr = TkNewMenuName(menuPtr->interp, | ||
pathNamePtr, | ||
cascadeMenuRefPtr->menuPtr); | ||
Tcl_IncrRefCount(newCloneNamePtr); | ||
Tcl_IncrRefCount(normalPtr); | ||
CloneMenu(cascadeMenuRefPtr->menuPtr, newCloneNamePtr, | ||
normalPtr); | ||
newObjv[0] = menuObjPtr; | ||
newObjv[1] = newCloneNamePtr; | ||
Tcl_IncrRefCount(menuObjPtr); | ||
ConfigureMenuEntry(mePtr, 2, newObjv); | ||
Tcl_DecrRefCount(newCloneNamePtr); | ||
Tcl_DecrRefCount(pathNamePtr); | ||
Tcl_DecrRefCount(normalPtr); | ||
Tcl_DecrRefCount(menuObjPtr); | ||
} | ||
} | ||
} | ||
return TCL_OK; | ||
} | ||
/* | ||
*-------------------------------------------------------------- | ||
* | ||
* TkGetMenuIndex -- | ||
* | ||
* Parse a textual index into a menu and return the numerical | ||
* index of the indicated entry. | ||
* | ||
* Results: | ||
* A standard Tcl result. If all went well, then *indexPtr is | ||
* filled in with the entry index corresponding to string | ||
* (ranges from -1 to the number of entries in the menu minus | ||
* one). Otherwise an error message is left in the interp's result. | ||
* | ||
* Side effects: | ||
* None. | ||
* | ||
*-------------------------------------------------------------- | ||
*/ | ||
int | ||
TkGetMenuIndex(interp, menuPtr, objPtr, lastOK, indexPtr) | ||
Tcl_Interp *interp; /* For error messages. */ | ||
TkMenu *menuPtr; /* Menu for which the index is being | ||
* specified. */ | ||
Tcl_Obj *objPtr; /* Specification of an entry in menu. See | ||
* manual entry for valid .*/ | ||
int lastOK; /* Non-zero means its OK to return index | ||
* just *after* last entry. */ | ||
int *indexPtr; /* Where to store converted index. */ | ||
{ | ||
int i; | ||
char *string = Tcl_GetStringFromObj(objPtr, NULL); | ||
if ((string[0] == 'a') && (strcmp(string, "active") == 0)) { | ||
*indexPtr = menuPtr->active; | ||
goto success; | ||
} | ||
if (((string[0] == 'l') && (strcmp(string, "last") == 0)) | ||
|| ((string[0] == 'e') && (strcmp(string, "end") == 0))) { | ||
*indexPtr = menuPtr->numEntries - ((lastOK) ? 0 : 1); | ||
goto success; | ||
} | ||
if ((string[0] == 'n') && (strcmp(string, "none") == 0)) { | ||
*indexPtr = -1; | ||
goto success; | ||
} | ||
if (string[0] == '@') { | ||
if (GetIndexFromCoords(interp, menuPtr, string, indexPtr) | ||
== TCL_OK) { | ||
goto success; | ||
} | ||
} | ||
if (isdigit(UCHAR(string[0]))) { | ||
if (Tcl_GetInt(interp, string, &i) == TCL_OK) { | ||
if (i >= menuPtr->numEntries) { | ||
if (lastOK) { | ||
i = menuPtr->numEntries; | ||
} else { | ||
i = menuPtr->numEntries-1; | ||
} | ||
} else if (i < 0) { | ||
i = -1; | ||
} | ||
*indexPtr = i; | ||
goto success; | ||
} | ||
Tcl_SetResult(interp, (char *) NULL, TCL_STATIC); | ||
} | ||
for (i = 0; i < menuPtr->numEntries; i++) { | ||
Tcl_Obj *labelPtr = menuPtr->entries[i]->labelPtr; | ||
char *label = (labelPtr == NULL) ? NULL | ||
: Tcl_GetStringFromObj(labelPtr, NULL); | ||
if ((label != NULL) | ||
&& (Tcl_StringMatch(label, string))) { | ||
*indexPtr = i; | ||
goto success; | ||
} | ||
} | ||
Tcl_AppendResult(interp, "bad menu entry index \"", | ||
string, "\"", (char *) NULL); | ||
return TCL_ERROR; | ||
success: | ||
return TCL_OK; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* MenuCmdDeletedProc -- | ||
* | ||
* This procedure is invoked when a widget command is deleted. If | ||
* the widget isn't already in the process of being destroyed, | ||
* this command destroys it. | ||
* | ||
* Results: | ||
* None. | ||
* | ||
* Side effects: | ||
* The widget is destroyed. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
static void | ||
MenuCmdDeletedProc(clientData) | ||
ClientData clientData; /* Pointer to widget record for widget. */ | ||
{ | ||
TkMenu *menuPtr = (TkMenu *) clientData; | ||
Tk_Window tkwin = menuPtr->tkwin; | ||
/* | ||
* This procedure could be invoked either because the window was | ||
* destroyed and the command was then deleted (in which case tkwin | ||
* is NULL) or because the command was deleted, and then this procedure | ||
* destroys the widget. | ||
*/ | ||
if (tkwin != NULL) { | ||
Tk_DestroyWindow(tkwin); | ||
} | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* MenuNewEntry -- | ||
* | ||
* This procedure allocates and initializes a new menu entry. | ||
* | ||
* Results: | ||
* The return value is a pointer to a new menu entry structure, | ||
* which has been malloc-ed, initialized, and entered into the | ||
* entry array for the menu. | ||
* | ||
* Side effects: | ||
* Storage gets allocated. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
static TkMenuEntry * | ||
MenuNewEntry(menuPtr, index, type) | ||
TkMenu *menuPtr; /* Menu that will hold the new entry. */ | ||
int index; /* Where in the menu the new entry is to | ||
* go. */ | ||
int type; /* The type of the new entry. */ | ||
{ | ||
TkMenuEntry *mePtr; | ||
TkMenuEntry **newEntries; | ||
int i; | ||
/* | ||
* Create a new array of entries with an empty slot for the | ||
* new entry. | ||
*/ | ||
newEntries = (TkMenuEntry **) ckalloc((unsigned) | ||
((menuPtr->numEntries+1)*sizeof(TkMenuEntry *))); | ||
for (i = 0; i < index; i++) { | ||
newEntries[i] = menuPtr->entries[i]; | ||
} | ||
for ( ; i < menuPtr->numEntries; i++) { | ||
newEntries[i+1] = menuPtr->entries[i]; | ||
newEntries[i+1]->index = i + 1; | ||
} | ||
if (menuPtr->numEntries != 0) { | ||
ckfree((char *) menuPtr->entries); | ||
} | ||
menuPtr->entries = newEntries; | ||
menuPtr->numEntries++; | ||
mePtr = (TkMenuEntry *) ckalloc(sizeof(TkMenuEntry)); | ||
menuPtr->entries[index] = mePtr; | ||
mePtr->type = type; | ||
mePtr->optionTable = menuPtr->optionTablesPtr->entryOptionTables[type]; | ||
mePtr->menuPtr = menuPtr; | ||
mePtr->labelPtr = NULL; | ||
mePtr->labelLength = 0; | ||
mePtr->underline = -1; | ||
mePtr->bitmapPtr = NULL; | ||
mePtr->imagePtr = NULL; | ||
mePtr->image = NULL; | ||
mePtr->selectImagePtr = NULL; | ||
mePtr->selectImage = NULL; | ||
mePtr->accelPtr = NULL; | ||
mePtr->accelLength = 0; | ||
mePtr->state = ENTRY_DISABLED; | ||
mePtr->borderPtr = NULL; | ||
mePtr->fgPtr = NULL; | ||
mePtr->activeBorderPtr = NULL; | ||
mePtr->activeFgPtr = NULL; | ||
mePtr->fontPtr = NULL; | ||
mePtr->indicatorOn = 0; | ||
mePtr->indicatorFgPtr = NULL; | ||
mePtr->columnBreak = 0; | ||
mePtr->hideMargin = 0; | ||
mePtr->commandPtr = NULL; | ||
mePtr->namePtr = NULL; | ||
mePtr->childMenuRefPtr = NULL; | ||
mePtr->onValuePtr = NULL; | ||
mePtr->offValuePtr = NULL; | ||
mePtr->entryFlags = 0; | ||
mePtr->index = index; | ||
mePtr->nextCascadePtr = NULL; | ||
if (Tk_InitOptions(menuPtr->interp, (char *) mePtr, | ||
mePtr->optionTable, menuPtr->tkwin) != TCL_OK) { | ||
ckfree((char *) mePtr); | ||
return NULL; | ||
} | ||
TkMenuInitializeEntryDrawingFields(mePtr); | ||
if (TkpMenuNewEntry(mePtr) != TCL_OK) { | ||
Tk_FreeConfigOptions((char *) mePtr, mePtr->optionTable, | ||
menuPtr->tkwin); | ||
ckfree((char *) mePtr); | ||
return NULL; | ||
} | ||
return mePtr; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* MenuAddOrInsert -- | ||
* | ||
* This procedure does all of the work of the "add" and "insert" | ||
* widget commands, allowing the code for these to be shared. | ||
* | ||
* Results: | ||
* A standard Tcl return value. | ||
* | ||
* Side effects: | ||
* A new menu entry is created in menuPtr. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
static int | ||
MenuAddOrInsert(interp, menuPtr, indexPtr, objc, objv) | ||
Tcl_Interp *interp; /* Used for error reporting. */ | ||
TkMenu *menuPtr; /* Widget in which to create new | ||
* entry. */ | ||
Tcl_Obj *indexPtr; /* Object describing index at which | ||
* to insert. NULL means insert at | ||
* end. */ | ||
int objc; /* Number of elements in objv. */ | ||
Tcl_Obj *CONST objv[]; /* Arguments to command: first arg | ||
* is type of entry, others are | ||
* config options. */ | ||
{ | ||
int type, index; | ||
TkMenuEntry *mePtr; | ||
TkMenu *menuListPtr; | ||
if (indexPtr != NULL) { | ||
if (TkGetMenuIndex(interp, menuPtr, indexPtr, 1, &index) | ||
!= TCL_OK) { | ||
return TCL_ERROR; | ||
} | ||
} else { | ||
index = menuPtr->numEntries; | ||
} | ||
if (index < 0) { | ||
char *indexString = Tcl_GetStringFromObj(indexPtr, NULL); | ||
Tcl_AppendResult(interp, "bad index \"", indexString, "\"", | ||
(char *) NULL); | ||
return TCL_ERROR; | ||
} | ||
if (menuPtr->tearoff && (index == 0)) { | ||
index = 1; | ||
} | ||
/* | ||
* Figure out the type of the new entry. | ||
*/ | ||
if (Tcl_GetIndexFromObj(interp, objv[0], menuEntryTypeStrings, | ||
"menu entry type", 0, &type) != TCL_OK) { | ||
return TCL_ERROR; | ||
} | ||
/* | ||
* Now we have to add an entry for every instance related to this menu. | ||
*/ | ||
for (menuListPtr = menuPtr->masterMenuPtr; menuListPtr != NULL; | ||
menuListPtr = menuListPtr->nextInstancePtr) { | ||
mePtr = MenuNewEntry(menuListPtr, index, type); | ||
if (mePtr == NULL) { | ||
return TCL_ERROR; | ||
} | ||
if (ConfigureMenuEntry(mePtr, objc - 1, objv + 1) != TCL_OK) { | ||
TkMenu *errorMenuPtr; | ||
int i; | ||
for (errorMenuPtr = menuPtr->masterMenuPtr; | ||
errorMenuPtr != NULL; | ||
errorMenuPtr = errorMenuPtr->nextInstancePtr) { | ||
Tcl_EventuallyFree((ClientData) errorMenuPtr->entries[index], | ||
DestroyMenuEntry); | ||
for (i = index; i < errorMenuPtr->numEntries - 1; i++) { | ||
errorMenuPtr->entries[i] = errorMenuPtr->entries[i + 1]; | ||
errorMenuPtr->entries[i]->index = i; | ||
} | ||
errorMenuPtr->numEntries--; | ||
if (errorMenuPtr->numEntries == 0) { | ||
ckfree((char *) errorMenuPtr->entries); | ||
errorMenuPtr->entries = NULL; | ||
} | ||
if (errorMenuPtr == menuListPtr) { | ||
break; | ||
} | ||
} | ||
return TCL_ERROR; | ||
} | ||
/* | ||
* If a menu has cascades, then every instance of the menu has | ||
* to have its own parallel cascade structure. So adding an | ||
* entry to a menu with clones means that the menu that the | ||
* entry points to has to be cloned for every clone the | ||
* master menu has. This is special case #2 in the comment | ||
* at the top of this file. | ||
*/ | ||
if ((menuPtr != menuListPtr) && (type == CASCADE_ENTRY)) { | ||
if ((mePtr->namePtr != NULL) | ||
&& (mePtr->childMenuRefPtr != NULL) | ||
&& (mePtr->childMenuRefPtr->menuPtr != NULL)) { | ||
TkMenu *cascadeMenuPtr = | ||
mePtr->childMenuRefPtr->menuPtr->masterMenuPtr; | ||
Tcl_Obj *newCascadePtr; | ||
Tcl_Obj *menuNamePtr = Tcl_NewStringObj("-menu", -1); | ||
Tcl_Obj *windowNamePtr = | ||
Tcl_NewStringObj(Tk_PathName(menuListPtr->tkwin), -1); | ||
Tcl_Obj *normalPtr = Tcl_NewStringObj("normal", -1); | ||
Tcl_Obj *newObjv[2]; | ||
TkMenuReferences *menuRefPtr; | ||
Tcl_IncrRefCount(windowNamePtr); | ||
newCascadePtr = TkNewMenuName(menuListPtr->interp, | ||
windowNamePtr, cascadeMenuPtr); | ||
Tcl_IncrRefCount(newCascadePtr); | ||
Tcl_IncrRefCount(normalPtr); | ||
CloneMenu(cascadeMenuPtr, newCascadePtr, normalPtr); | ||
menuRefPtr = TkFindMenuReferencesObj(menuListPtr->interp, | ||
newCascadePtr); | ||
if (menuRefPtr == NULL) { | ||
panic("CloneMenu failed inside of MenuAddOrInsert."); | ||
} | ||
newObjv[0] = menuNamePtr; | ||
newObjv[1] = newCascadePtr; | ||
Tcl_IncrRefCount(menuNamePtr); | ||
Tcl_IncrRefCount(newCascadePtr); | ||
ConfigureMenuEntry(mePtr, 2, newObjv); | ||
Tcl_DecrRefCount(newCascadePtr); | ||
Tcl_DecrRefCount(menuNamePtr); | ||
Tcl_DecrRefCount(windowNamePtr); | ||
Tcl_DecrRefCount(normalPtr); | ||
} | ||
} | ||
} | ||
return TCL_OK; | ||
} | ||
/* | ||
*-------------------------------------------------------------- | ||
* | ||
* MenuVarProc -- | ||
* | ||
* This procedure is invoked when someone changes the | ||
* state variable associated with a radiobutton or checkbutton | ||
* menu entry. The entry's selected state is set to match | ||
* the value of the variable. | ||
* | ||
* Results: | ||
* NULL is always returned. | ||
* | ||
* Side effects: | ||
* The menu entry may become selected or deselected. | ||
* | ||
*-------------------------------------------------------------- | ||
*/ | ||
static char * | ||
MenuVarProc(clientData, interp, name1, name2, flags) | ||
ClientData clientData; /* Information about menu entry. */ | ||
Tcl_Interp *interp; /* Interpreter containing variable. */ | ||
char *name1; /* First part of variable's name. */ | ||
char *name2; /* Second part of variable's name. */ | ||
int flags; /* Describes what just happened. */ | ||
{ | ||
TkMenuEntry *mePtr = (TkMenuEntry *) clientData; | ||
TkMenu *menuPtr; | ||
char *value; | ||
char *name = Tcl_GetStringFromObj(mePtr->namePtr, NULL); | ||
char *onValue; | ||
menuPtr = mePtr->menuPtr; | ||
/* | ||
* If the variable is being unset, then re-establish the | ||
* trace unless the whole interpreter is going away. | ||
*/ | ||
if (flags & TCL_TRACE_UNSETS) { | ||
mePtr->entryFlags &= ~ENTRY_SELECTED; | ||
if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) { | ||
Tcl_TraceVar(interp, name, | ||
TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, | ||
MenuVarProc, clientData); | ||
} | ||
TkpConfigureMenuEntry(mePtr); | ||
TkEventuallyRedrawMenu(menuPtr, (TkMenuEntry *) NULL); | ||
return (char *) NULL; | ||
} | ||
/* | ||
* Use the value of the variable to update the selected status of | ||
* the menu entry. | ||
*/ | ||
value = Tcl_GetVar(interp, name, TCL_GLOBAL_ONLY); | ||
if (value == NULL) { | ||
value = ""; | ||
} | ||
if (mePtr->onValuePtr != NULL) { | ||
onValue = Tcl_GetStringFromObj(mePtr->onValuePtr, NULL); | ||
if (strcmp(value, onValue) == 0) { | ||
if (mePtr->entryFlags & ENTRY_SELECTED) { | ||
return (char *) NULL; | ||
} | ||
mePtr->entryFlags |= ENTRY_SELECTED; | ||
} else if (mePtr->entryFlags & ENTRY_SELECTED) { | ||
mePtr->entryFlags &= ~ENTRY_SELECTED; | ||
} else { | ||
return (char *) NULL; | ||
} | ||
} else { | ||
return (char *) NULL; | ||
} | ||
TkpConfigureMenuEntry(mePtr); | ||
TkEventuallyRedrawMenu(menuPtr, mePtr); | ||
return (char *) NULL; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* TkActivateMenuEntry -- | ||
* | ||
* This procedure is invoked to make a particular menu entry | ||
* the active one, deactivating any other entry that might | ||
* currently be active. | ||
* | ||
* Results: | ||
* The return value is a standard Tcl result (errors can occur | ||
* while posting and unposting submenus). | ||
* | ||
* Side effects: | ||
* Menu entries get redisplayed, and the active entry changes. | ||
* Submenus may get posted and unposted. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
int | ||
TkActivateMenuEntry(menuPtr, index) | ||
register TkMenu *menuPtr; /* Menu in which to activate. */ | ||
int index; /* Index of entry to activate, or | ||
* -1 to deactivate all entries. */ | ||
{ | ||
register TkMenuEntry *mePtr; | ||
int result = TCL_OK; | ||
if (menuPtr->active >= 0) { | ||
mePtr = menuPtr->entries[menuPtr->active]; | ||
/* | ||
* Don't change the state unless it's currently active (state | ||
* might already have been changed to disabled). | ||
*/ | ||
if (mePtr->state == ENTRY_ACTIVE) { | ||
mePtr->state = ENTRY_NORMAL; | ||
} | ||
TkEventuallyRedrawMenu(menuPtr, menuPtr->entries[menuPtr->active]); | ||
} | ||
menuPtr->active = index; | ||
if (index >= 0) { | ||
mePtr = menuPtr->entries[index]; | ||
mePtr->state = ENTRY_ACTIVE; | ||
TkEventuallyRedrawMenu(menuPtr, mePtr); | ||
} | ||
return result; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* TkPostCommand -- | ||
* | ||
* Execute the postcommand for the given menu. | ||
* | ||
* Results: | ||
* The return value is a standard Tcl result (errors can occur | ||
* while the postcommands are being processed). | ||
* | ||
* Side effects: | ||
* Since commands can get executed while this routine is being executed, | ||
* the entire world can change. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
int | ||
TkPostCommand(menuPtr) | ||
TkMenu *menuPtr; | ||
{ | ||
int result; | ||
/* | ||
* If there is a command for the menu, execute it. This | ||
* may change the size of the menu, so be sure to recompute | ||
* the menu's geometry if needed. | ||
*/ | ||
if (menuPtr->postCommandPtr != NULL) { | ||
Tcl_Obj *postCommandPtr = menuPtr->postCommandPtr; | ||
Tcl_IncrRefCount(postCommandPtr); | ||
result = Tcl_EvalObjEx(menuPtr->interp, postCommandPtr, | ||
TCL_EVAL_GLOBAL); | ||
Tcl_DecrRefCount(postCommandPtr); | ||
if (result != TCL_OK) { | ||
return result; | ||
} | ||
TkRecomputeMenu(menuPtr); | ||
} | ||
return TCL_OK; | ||
} | ||
/* | ||
*-------------------------------------------------------------- | ||
* | ||
* CloneMenu -- | ||
* | ||
* Creates a child copy of the menu. It will be inserted into | ||
* the menu's instance chain. All attributes and entry | ||
* attributes will be duplicated. | ||
* | ||
* Results: | ||
* A standard Tcl result. | ||
* | ||
* Side effects: | ||
* Allocates storage. After the menu is created, any | ||
* configuration done with this menu or any related one | ||
* will be reflected in all of them. | ||
* | ||
*-------------------------------------------------------------- | ||
*/ | ||
static int | ||
CloneMenu(menuPtr, newMenuNamePtr, newMenuTypePtr) | ||
TkMenu *menuPtr; /* The menu we are going to clone */ | ||
Tcl_Obj *newMenuNamePtr; /* The name to give the new menu */ | ||
Tcl_Obj *newMenuTypePtr; /* What kind of menu is this, a normal menu | ||
* a menubar, or a tearoff? */ | ||
{ | ||
int returnResult; | ||
int menuType, i; | ||
TkMenuReferences *menuRefPtr; | ||
Tcl_Obj *menuDupCommandArray[4]; | ||
if (newMenuTypePtr == NULL) { | ||
menuType = MASTER_MENU; | ||
} else { | ||
if (Tcl_GetIndexFromObj(menuPtr->interp, newMenuTypePtr, | ||
menuTypeStrings, "menu type", 0, &menuType) != TCL_OK) { | ||
return TCL_ERROR; | ||
} | ||
} | ||
menuDupCommandArray[0] = Tcl_NewStringObj("tkMenuDup", -1); | ||
menuDupCommandArray[1] = Tcl_NewStringObj(Tk_PathName(menuPtr->tkwin), -1); | ||
menuDupCommandArray[2] = newMenuNamePtr; | ||
if (newMenuTypePtr == NULL) { | ||
menuDupCommandArray[3] = Tcl_NewStringObj("normal", -1); | ||
} else { | ||
menuDupCommandArray[3] = newMenuTypePtr; | ||
} | ||
for (i = 0; i < 4; i++) { | ||
Tcl_IncrRefCount(menuDupCommandArray[i]); | ||
} | ||
Tcl_Preserve((ClientData) menuPtr); | ||
returnResult = Tcl_EvalObjv(menuPtr->interp, 4, menuDupCommandArray, 0); | ||
for (i = 0; i < 4; i++) { | ||
Tcl_DecrRefCount(menuDupCommandArray[i]); | ||
} | ||
/* | ||
* Make sure the tcl command actually created the clone. | ||
*/ | ||
if ((returnResult == TCL_OK) && | ||
((menuRefPtr = TkFindMenuReferencesObj(menuPtr->interp, | ||
newMenuNamePtr)) != (TkMenuReferences *) NULL) | ||
&& (menuPtr->numEntries == menuRefPtr->menuPtr->numEntries)) { | ||
TkMenu *newMenuPtr = menuRefPtr->menuPtr; | ||
Tcl_Obj *newObjv[3]; | ||
char *newArgv[3]; | ||
int i, numElements; | ||
/* | ||
* Now put this newly created menu into the parent menu's instance | ||
* chain. | ||
*/ | ||
if (menuPtr->nextInstancePtr == NULL) { | ||
menuPtr->nextInstancePtr = newMenuPtr; | ||
newMenuPtr->masterMenuPtr = menuPtr->masterMenuPtr; | ||
} else { | ||
TkMenu *masterMenuPtr; | ||
masterMenuPtr = menuPtr->masterMenuPtr; | ||
newMenuPtr->nextInstancePtr = masterMenuPtr->nextInstancePtr; | ||
masterMenuPtr->nextInstancePtr = newMenuPtr; | ||
newMenuPtr->masterMenuPtr = masterMenuPtr; | ||
} | ||
/* | ||
* Add the master menu's window to the bind tags for this window | ||
* after this window's tag. This is so the user can bind to either | ||
* this clone (which may not be easy to do) or the entire menu | ||
* clone structure. | ||
*/ | ||
newArgv[0] = "bindtags"; | ||
newArgv[1] = Tk_PathName(newMenuPtr->tkwin); | ||
if (Tk_BindtagsCmd((ClientData)newMenuPtr->tkwin, | ||
newMenuPtr->interp, 2, newArgv) == TCL_OK) { | ||
char *windowName; | ||
Tcl_Obj *bindingsPtr = | ||
Tcl_DuplicateObj(Tcl_GetObjResult(newMenuPtr->interp)); | ||
Tcl_Obj *elementPtr; | ||
Tcl_ListObjLength(newMenuPtr->interp, bindingsPtr, &numElements); | ||
for (i = 0; i < numElements; i++) { | ||
Tcl_ListObjIndex(newMenuPtr->interp, bindingsPtr, i, | ||
&elementPtr); | ||
windowName = Tcl_GetStringFromObj(elementPtr, NULL); | ||
if (strcmp(windowName, Tk_PathName(newMenuPtr->tkwin)) | ||
== 0) { | ||
Tcl_Obj *newElementPtr = Tcl_NewStringObj( | ||
Tk_PathName(newMenuPtr->masterMenuPtr->tkwin), -1); | ||
Tcl_IncrRefCount(newElementPtr); | ||
Tcl_ListObjReplace(menuPtr->interp, bindingsPtr, | ||
i + 1, 0, 1, &newElementPtr); | ||
newArgv[2] = Tcl_GetStringFromObj(bindingsPtr, NULL); | ||
Tk_BindtagsCmd((ClientData)newMenuPtr->tkwin, | ||
menuPtr->interp, 3, newArgv); | ||
break; | ||
} | ||
} | ||
Tcl_DecrRefCount(bindingsPtr); | ||
} | ||
Tcl_ResetResult(menuPtr->interp); | ||
/* | ||
* Clone all of the cascade menus that this menu points to. | ||
*/ | ||
for (i = 0; i < menuPtr->numEntries; i++) { | ||
TkMenuReferences *cascadeRefPtr; | ||
TkMenu *oldCascadePtr; | ||
if ((menuPtr->entries[i]->type == CASCADE_ENTRY) | ||
&& (menuPtr->entries[i]->namePtr != NULL)) { | ||
cascadeRefPtr = | ||
TkFindMenuReferencesObj(menuPtr->interp, | ||
menuPtr->entries[i]->namePtr); | ||
if ((cascadeRefPtr != NULL) && (cascadeRefPtr->menuPtr)) { | ||
Tcl_Obj *windowNamePtr = | ||
Tcl_NewStringObj(Tk_PathName(newMenuPtr->tkwin), | ||
-1); | ||
Tcl_Obj *newCascadePtr; | ||
oldCascadePtr = cascadeRefPtr->menuPtr; | ||
Tcl_IncrRefCount(windowNamePtr); | ||
newCascadePtr = TkNewMenuName(menuPtr->interp, | ||
windowNamePtr, oldCascadePtr); | ||
Tcl_IncrRefCount(newCascadePtr); | ||
CloneMenu(oldCascadePtr, newCascadePtr, NULL); | ||
newObjv[0] = Tcl_NewStringObj("-menu", -1); | ||
newObjv[1] = newCascadePtr; | ||
Tcl_IncrRefCount(newObjv[0]); | ||
ConfigureMenuEntry(newMenuPtr->entries[i], 2, newObjv); | ||
Tcl_DecrRefCount(newObjv[0]); | ||
Tcl_DecrRefCount(newCascadePtr); | ||
Tcl_DecrRefCount(windowNamePtr); | ||
} | ||
} | ||
} | ||
returnResult = TCL_OK; | ||
} else { | ||
returnResult = TCL_ERROR; | ||
} | ||
Tcl_Release((ClientData) menuPtr); | ||
return returnResult; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* MenuDoYPosition -- | ||
* | ||
* Given arguments from an option command line, returns the Y position. | ||
* | ||
* Results: | ||
* Returns TCL_OK or TCL_Error | ||
* | ||
* Side effects: | ||
* yPosition is set to the Y-position of the menu entry. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
static int | ||
MenuDoYPosition(interp, menuPtr, objPtr) | ||
Tcl_Interp *interp; | ||
TkMenu *menuPtr; | ||
Tcl_Obj *objPtr; | ||
{ | ||
int index; | ||
TkRecomputeMenu(menuPtr); | ||
if (TkGetMenuIndex(interp, menuPtr, objPtr, 0, &index) != TCL_OK) { | ||
goto error; | ||
} | ||
Tcl_ResetResult(interp); | ||
if (index < 0) { | ||
Tcl_SetObjResult(interp, Tcl_NewIntObj(0)); | ||
} else { | ||
Tcl_SetObjResult(interp, Tcl_NewIntObj(menuPtr->entries[index]->y)); | ||
} | ||
return TCL_OK; | ||
error: | ||
return TCL_ERROR; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* GetIndexFromCoords -- | ||
* | ||
* Given a string of the form "@int", return the menu item corresponding | ||
* to int. | ||
* | ||
* Results: | ||
* If int is a valid number, *indexPtr will be the number of the menuentry | ||
* that is the correct height. If int is invaled, *indexPtr will be | ||
* unchanged. Returns appropriate Tcl error number. | ||
* | ||
* Side effects: | ||
* If int is invalid, interp's result will set to NULL. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
static int | ||
GetIndexFromCoords(interp, menuPtr, string, indexPtr) | ||
Tcl_Interp *interp; /* interp of menu */ | ||
TkMenu *menuPtr; /* the menu we are searching */ | ||
char *string; /* The @string we are parsing */ | ||
int *indexPtr; /* The index of the item that matches */ | ||
{ | ||
int x, y, i; | ||
char *p, *end; | ||
TkRecomputeMenu(menuPtr); | ||
p = string + 1; | ||
y = strtol(p, &end, 0); | ||
if (end == p) { | ||
goto error; | ||
} | ||
if (*end == ',') { | ||
x = y; | ||
p = end + 1; | ||
y = strtol(p, &end, 0); | ||
if (end == p) { | ||
goto error; | ||
} | ||
} else { | ||
Tk_GetPixelsFromObj(interp, menuPtr->tkwin, | ||
menuPtr->borderWidthPtr, &x); | ||
} | ||
for (i = 0; i < menuPtr->numEntries; i++) { | ||
if ((x >= menuPtr->entries[i]->x) && (y >= menuPtr->entries[i]->y) | ||
&& (x < (menuPtr->entries[i]->x + menuPtr->entries[i]->width)) | ||
&& (y < (menuPtr->entries[i]->y | ||
+ menuPtr->entries[i]->height))) { | ||
break; | ||
} | ||
} | ||
if (i >= menuPtr->numEntries) { | ||
/* i = menuPtr->numEntries - 1; */ | ||
i = -1; | ||
} | ||
*indexPtr = i; | ||
return TCL_OK; | ||
error: | ||
Tcl_SetResult(interp, (char *) NULL, TCL_STATIC); | ||
return TCL_ERROR; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* RecursivelyDeleteMenu -- | ||
* | ||
* Deletes a menu and any cascades underneath it. Used for deleting | ||
* instances when a menu is no longer being used as a menubar, | ||
* for instance. | ||
* | ||
* Results: | ||
* None. | ||
* | ||
* Side effects: | ||
* Destroys the menu and all cascade menus underneath it. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
static void | ||
RecursivelyDeleteMenu(menuPtr) | ||
TkMenu *menuPtr; /* The menubar instance we are deleting */ | ||
{ | ||
int i; | ||
TkMenuEntry *mePtr; | ||
for (i = 0; i < menuPtr->numEntries; i++) { | ||
mePtr = menuPtr->entries[i]; | ||
if ((mePtr->type == CASCADE_ENTRY) | ||
&& (mePtr->childMenuRefPtr != NULL) | ||
&& (mePtr->childMenuRefPtr->menuPtr != NULL)) { | ||
RecursivelyDeleteMenu(mePtr->childMenuRefPtr->menuPtr); | ||
} | ||
} | ||
Tk_DestroyWindow(menuPtr->tkwin); | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* TkNewMenuName -- | ||
* | ||
* Makes a new unique name for a cloned menu. Will be a child | ||
* of oldName. | ||
* | ||
* Results: | ||
* Returns a char * which has been allocated; caller must free. | ||
* | ||
* Side effects: | ||
* Memory is allocated. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
Tcl_Obj * | ||
TkNewMenuName(interp, parentPtr, menuPtr) | ||
Tcl_Interp *interp; /* The interp the new name has to live in.*/ | ||
Tcl_Obj *parentPtr; /* The prefix path of the new name. */ | ||
TkMenu *menuPtr; /* The menu we are cloning. */ | ||
{ | ||
Tcl_Obj *resultPtr = NULL; /* Initialization needed only to prevent | ||
* compiler warning. */ | ||
Tcl_Obj *childPtr; | ||
char *destString; | ||
int i; | ||
int doDot; | ||
Tcl_CmdInfo cmdInfo; | ||
Tcl_HashTable *nameTablePtr = NULL; | ||
TkWindow *winPtr = (TkWindow *) menuPtr->tkwin; | ||
char *parentName = Tcl_GetStringFromObj(parentPtr, NULL); | ||
if (winPtr->mainPtr != NULL) { | ||
nameTablePtr = &(winPtr->mainPtr->nameTable); | ||
} | ||
doDot = parentName[strlen(parentName) - 1] != '.'; | ||
childPtr = Tcl_NewStringObj(Tk_PathName(menuPtr->tkwin), -1); | ||
for (destString = Tcl_GetStringFromObj(childPtr, NULL); | ||
*destString != '\0'; destString++) { | ||
if (*destString == '.') { | ||
*destString = '#'; | ||
} | ||
} | ||
for (i = 0; ; i++) { | ||
if (i == 0) { | ||
resultPtr = Tcl_DuplicateObj(parentPtr); | ||
if (doDot) { | ||
Tcl_AppendToObj(resultPtr, ".", -1); | ||
} | ||
Tcl_AppendObjToObj(resultPtr, childPtr); | ||
} else { | ||
Tcl_Obj *intPtr; | ||
Tcl_DecrRefCount(resultPtr); | ||
resultPtr = Tcl_DuplicateObj(parentPtr); | ||
if (doDot) { | ||
Tcl_AppendToObj(resultPtr, ".", -1); | ||
} | ||
Tcl_AppendObjToObj(resultPtr, childPtr); | ||
intPtr = Tcl_NewIntObj(i); | ||
Tcl_AppendObjToObj(resultPtr, intPtr); | ||
Tcl_DecrRefCount(intPtr); | ||
} | ||
destString = Tcl_GetStringFromObj(resultPtr, NULL); | ||
if ((Tcl_GetCommandInfo(interp, destString, &cmdInfo) == 0) | ||
&& ((nameTablePtr == NULL) | ||
|| (Tcl_FindHashEntry(nameTablePtr, destString) == NULL))) { | ||
break; | ||
} | ||
} | ||
Tcl_DecrRefCount(childPtr); | ||
return resultPtr; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* TkSetWindowMenuBar -- | ||
* | ||
* Associates a menu with a window. Called by ConfigureFrame in | ||
* in response to a "-menu .foo" configuration option for a top | ||
* level. | ||
* | ||
* Results: | ||
* None. | ||
* | ||
* Side effects: | ||
* The old menu clones for the menubar are thrown away, and a | ||
* handler is set up to allocate the new ones. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
void | ||
TkSetWindowMenuBar(interp, tkwin, oldMenuName, menuName) | ||
Tcl_Interp *interp; /* The interpreter the toplevel lives in. */ | ||
Tk_Window tkwin; /* The toplevel window */ | ||
char *oldMenuName; /* The name of the menubar previously set in | ||
* this toplevel. NULL means no menu was | ||
* set previously. */ | ||
char *menuName; /* The name of the new menubar that the | ||
* toplevel needs to be set to. NULL means | ||
* that their is no menu now. */ | ||
{ | ||
TkMenuTopLevelList *topLevelListPtr, *prevTopLevelPtr; | ||
TkMenu *menuPtr; | ||
TkMenuReferences *menuRefPtr; | ||
TkMenuInit(); | ||
/* | ||
* Destroy the menubar instances of the old menu. Take this window | ||
* out of the old menu's top level reference list. | ||
*/ | ||
if (oldMenuName != NULL) { | ||
menuRefPtr = TkFindMenuReferences(interp, oldMenuName); | ||
if (menuRefPtr != NULL) { | ||
/* | ||
* Find the menubar instance that is to be removed. Destroy | ||
* it and all of the cascades underneath it. | ||
*/ | ||
if (menuRefPtr->menuPtr != NULL) { | ||
TkMenu *instancePtr; | ||
menuPtr = menuRefPtr->menuPtr; | ||
for (instancePtr = menuPtr->masterMenuPtr; | ||
instancePtr != NULL; | ||
instancePtr = instancePtr->nextInstancePtr) { | ||
if (instancePtr->menuType == MENUBAR | ||
&& instancePtr->parentTopLevelPtr == tkwin) { | ||
RecursivelyDeleteMenu(instancePtr); | ||
break; | ||
} | ||
} | ||
} | ||
/* | ||
* Now we need to remove this toplevel from the list of toplevels | ||
* that reference this menu. | ||
*/ | ||
for (topLevelListPtr = menuRefPtr->topLevelListPtr, | ||
prevTopLevelPtr = NULL; | ||
(topLevelListPtr != NULL) | ||
&& (topLevelListPtr->tkwin != tkwin); | ||
prevTopLevelPtr = topLevelListPtr, | ||
topLevelListPtr = topLevelListPtr->nextPtr) { | ||
/* | ||
* Empty loop body. | ||
*/ | ||
} | ||
/* | ||
* Now we have found the toplevel reference that matches the | ||
* tkwin; remove this reference from the list. | ||
*/ | ||
if (topLevelListPtr != NULL) { | ||
if (prevTopLevelPtr == NULL) { | ||
menuRefPtr->topLevelListPtr = | ||
menuRefPtr->topLevelListPtr->nextPtr; | ||
} else { | ||
prevTopLevelPtr->nextPtr = topLevelListPtr->nextPtr; | ||
} | ||
ckfree((char *) topLevelListPtr); | ||
TkFreeMenuReferences(menuRefPtr); | ||
} | ||
} | ||
} | ||
/* | ||
* Now, add the clone references for the new menu. | ||
*/ | ||
if (menuName != NULL && menuName[0] != 0) { | ||
TkMenu *menuBarPtr = NULL; | ||
menuRefPtr = TkCreateMenuReferences(interp, menuName); | ||
menuPtr = menuRefPtr->menuPtr; | ||
if (menuPtr != NULL) { | ||
Tcl_Obj *cloneMenuPtr; | ||
TkMenuReferences *cloneMenuRefPtr; | ||
Tcl_Obj *newObjv[4]; | ||
Tcl_Obj *windowNamePtr = Tcl_NewStringObj(Tk_PathName(tkwin), | ||
-1); | ||
Tcl_Obj *menubarPtr = Tcl_NewStringObj("menubar", -1); | ||
/* | ||
* Clone the menu and all of the cascades underneath it. | ||
*/ | ||
Tcl_IncrRefCount(windowNamePtr); | ||
cloneMenuPtr = TkNewMenuName(interp, windowNamePtr, | ||
menuPtr); | ||
Tcl_IncrRefCount(cloneMenuPtr); | ||
Tcl_IncrRefCount(menubarPtr); | ||
CloneMenu(menuPtr, cloneMenuPtr, menubarPtr); | ||
cloneMenuRefPtr = TkFindMenuReferencesObj(interp, cloneMenuPtr); | ||
if ((cloneMenuRefPtr != NULL) | ||
&& (cloneMenuRefPtr->menuPtr != NULL)) { | ||
Tcl_Obj *cursorPtr = Tcl_NewStringObj("-cursor", -1); | ||
Tcl_Obj *nullPtr = Tcl_NewObj(); | ||
cloneMenuRefPtr->menuPtr->parentTopLevelPtr = tkwin; | ||
menuBarPtr = cloneMenuRefPtr->menuPtr; | ||
newObjv[0] = cursorPtr; | ||
newObjv[1] = nullPtr; | ||
Tcl_IncrRefCount(cursorPtr); | ||
Tcl_IncrRefCount(nullPtr); | ||
ConfigureMenu(menuPtr->interp, cloneMenuRefPtr->menuPtr, | ||
2, newObjv); | ||
Tcl_DecrRefCount(cursorPtr); | ||
Tcl_DecrRefCount(nullPtr); | ||
} | ||
TkpSetWindowMenuBar(tkwin, menuBarPtr); | ||
Tcl_DecrRefCount(cloneMenuPtr); | ||
Tcl_DecrRefCount(menubarPtr); | ||
Tcl_DecrRefCount(windowNamePtr); | ||
} else { | ||
TkpSetWindowMenuBar(tkwin, NULL); | ||
} | ||
/* | ||
* Add this window to the menu's list of windows that refer | ||
* to this menu. | ||
*/ | ||
topLevelListPtr = (TkMenuTopLevelList *) | ||
ckalloc(sizeof(TkMenuTopLevelList)); | ||
topLevelListPtr->tkwin = tkwin; | ||
topLevelListPtr->nextPtr = menuRefPtr->topLevelListPtr; | ||
menuRefPtr->topLevelListPtr = topLevelListPtr; | ||
} else { | ||
TkpSetWindowMenuBar(tkwin, NULL); | ||
} | ||
TkpSetMainMenubar(interp, tkwin, menuName); | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* DestroyMenuHashTable -- | ||
* | ||
* Called when an interp is deleted and a menu hash table has | ||
* been set in it. | ||
* | ||
* Results: | ||
* None. | ||
* | ||
* Side effects: | ||
* The hash table is destroyed. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
static void | ||
DestroyMenuHashTable(clientData, interp) | ||
ClientData clientData; /* The menu hash table we are destroying */ | ||
Tcl_Interp *interp; /* The interpreter we are destroying */ | ||
{ | ||
Tcl_DeleteHashTable((Tcl_HashTable *) clientData); | ||
ckfree((char *) clientData); | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* TkGetMenuHashTable -- | ||
* | ||
* For a given interp, give back the menu hash table that goes with | ||
* it. If the hash table does not exist, it is created. | ||
* | ||
* Results: | ||
* Returns a hash table pointer. | ||
* | ||
* Side effects: | ||
* A new hash table is created if there were no table in the interp | ||
* originally. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
Tcl_HashTable * | ||
TkGetMenuHashTable(interp) | ||
Tcl_Interp *interp; /* The interp we need the hash table in.*/ | ||
{ | ||
Tcl_HashTable *menuTablePtr; | ||
menuTablePtr = (Tcl_HashTable *) Tcl_GetAssocData(interp, MENU_HASH_KEY, | ||
NULL); | ||
if (menuTablePtr == NULL) { | ||
menuTablePtr = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable)); | ||
Tcl_InitHashTable(menuTablePtr, TCL_STRING_KEYS); | ||
Tcl_SetAssocData(interp, MENU_HASH_KEY, DestroyMenuHashTable, | ||
(ClientData) menuTablePtr); | ||
} | ||
return menuTablePtr; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* TkCreateMenuReferences -- | ||
* | ||
* Given a pathname, gives back a pointer to a TkMenuReferences structure. | ||
* If a reference is not already in the hash table, one is created. | ||
* | ||
* Results: | ||
* Returns a pointer to a menu reference structure. Should not | ||
* be freed by calller; when a field of the reference is cleared, | ||
* TkFreeMenuReferences should be called. | ||
* | ||
* Side effects: | ||
* A new hash table entry is created if there were no references | ||
* to the menu originally. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
TkMenuReferences * | ||
TkCreateMenuReferences(interp, pathName) | ||
Tcl_Interp *interp; | ||
char *pathName; /* The path of the menu widget */ | ||
{ | ||
Tcl_HashEntry *hashEntryPtr; | ||
TkMenuReferences *menuRefPtr; | ||
int newEntry; | ||
Tcl_HashTable *menuTablePtr = TkGetMenuHashTable(interp); | ||
hashEntryPtr = Tcl_CreateHashEntry(menuTablePtr, pathName, &newEntry); | ||
if (newEntry) { | ||
menuRefPtr = (TkMenuReferences *) ckalloc(sizeof(TkMenuReferences)); | ||
menuRefPtr->menuPtr = NULL; | ||
menuRefPtr->topLevelListPtr = NULL; | ||
menuRefPtr->parentEntryPtr = NULL; | ||
menuRefPtr->hashEntryPtr = hashEntryPtr; | ||
Tcl_SetHashValue(hashEntryPtr, (char *) menuRefPtr); | ||
} else { | ||
menuRefPtr = (TkMenuReferences *) Tcl_GetHashValue(hashEntryPtr); | ||
} | ||
return menuRefPtr; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* TkFindMenuReferences -- | ||
* | ||
* Given a pathname, gives back a pointer to the TkMenuReferences | ||
* structure. | ||
* | ||
* Results: | ||
* Returns a pointer to a menu reference structure. Should not | ||
* be freed by calller; when a field of the reference is cleared, | ||
* TkFreeMenuReferences should be called. Returns NULL if no reference | ||
* with this pathname exists. | ||
* | ||
* Side effects: | ||
* None. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
TkMenuReferences * | ||
TkFindMenuReferences(interp, pathName) | ||
Tcl_Interp *interp; /* The interp the menu is living in. */ | ||
char *pathName; /* The path of the menu widget */ | ||
{ | ||
Tcl_HashEntry *hashEntryPtr; | ||
TkMenuReferences *menuRefPtr = NULL; | ||
Tcl_HashTable *menuTablePtr; | ||
menuTablePtr = TkGetMenuHashTable(interp); | ||
hashEntryPtr = Tcl_FindHashEntry(menuTablePtr, pathName); | ||
if (hashEntryPtr != NULL) { | ||
menuRefPtr = (TkMenuReferences *) Tcl_GetHashValue(hashEntryPtr); | ||
} | ||
return menuRefPtr; | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* TkFindMenuReferencesObj -- | ||
* | ||
* Given a pathname, gives back a pointer to the TkMenuReferences | ||
* structure. | ||
* | ||
* Results: | ||
* Returns a pointer to a menu reference structure. Should not | ||
* be freed by calller; when a field of the reference is cleared, | ||
* TkFreeMenuReferences should be called. Returns NULL if no reference | ||
* with this pathname exists. | ||
* | ||
* Side effects: | ||
* None. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
TkMenuReferences * | ||
TkFindMenuReferencesObj(interp, objPtr) | ||
Tcl_Interp *interp; /* The interp the menu is living in. */ | ||
Tcl_Obj *objPtr; /* The path of the menu widget */ | ||
{ | ||
char *pathName = Tcl_GetStringFromObj(objPtr, NULL); | ||
return TkFindMenuReferences(interp, pathName); | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* TkFreeMenuReferences -- | ||
* | ||
* This is called after one of the fields in a menu reference | ||
* is cleared. It cleans up the ref if it is now empty. | ||
* | ||
* Results: | ||
* None. | ||
* | ||
* Side effects: | ||
* If this is the last field to be cleared, the menu ref is | ||
* taken out of the hash table. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
void | ||
TkFreeMenuReferences(menuRefPtr) | ||
TkMenuReferences *menuRefPtr; /* The menu reference to | ||
* free */ | ||
{ | ||
if ((menuRefPtr->menuPtr == NULL) | ||
&& (menuRefPtr->parentEntryPtr == NULL) | ||
&& (menuRefPtr->topLevelListPtr == NULL)) { | ||
Tcl_DeleteHashEntry(menuRefPtr->hashEntryPtr); | ||
ckfree((char *) menuRefPtr); | ||
} | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* DeleteMenuCloneEntries -- | ||
* | ||
* For every clone in this clone chain, delete the menu entries | ||
* given by the parameters. | ||
* | ||
* Results: | ||
* None. | ||
* | ||
* Side effects: | ||
* The appropriate entries are deleted from all clones of this menu. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
static void | ||
DeleteMenuCloneEntries(menuPtr, first, last) | ||
TkMenu *menuPtr; /* the menu the command was issued with */ | ||
int first; /* the zero-based first entry in the set | ||
* of entries to delete. */ | ||
int last; /* the zero-based last entry */ | ||
{ | ||
TkMenu *menuListPtr; | ||
int numDeleted, i; | ||
numDeleted = last + 1 - first; | ||
for (menuListPtr = menuPtr->masterMenuPtr; menuListPtr != NULL; | ||
menuListPtr = menuListPtr->nextInstancePtr) { | ||
for (i = last; i >= first; i--) { | ||
Tcl_EventuallyFree((ClientData) menuListPtr->entries[i], | ||
DestroyMenuEntry); | ||
} | ||
for (i = last + 1; i < menuListPtr->numEntries; i++) { | ||
menuListPtr->entries[i - numDeleted] = menuListPtr->entries[i]; | ||
menuListPtr->entries[i - numDeleted]->index = i; | ||
} | ||
menuListPtr->numEntries -= numDeleted; | ||
if (menuListPtr->numEntries == 0) { | ||
ckfree((char *) menuListPtr->entries); | ||
menuListPtr->entries = NULL; | ||
} | ||
if ((menuListPtr->active >= first) | ||
&& (menuListPtr->active <= last)) { | ||
menuListPtr->active = -1; | ||
} else if (menuListPtr->active > last) { | ||
menuListPtr->active -= numDeleted; | ||
} | ||
TkEventuallyRecomputeMenu(menuListPtr); | ||
} | ||
} | ||
/* | ||
*---------------------------------------------------------------------- | ||
* | ||
* TkMenuInit -- | ||
* | ||
* Sets up the hash tables and the variables used by the menu package. | ||
* | ||
* Results: | ||
* None. | ||
* | ||
* Side effects: | ||
* lastMenuID gets initialized, and the parent hash and the command hash | ||
* are allocated. | ||
* | ||
*---------------------------------------------------------------------- | ||
*/ | ||
void | ||
TkMenuInit() | ||
{ | ||
ThreadSpecificData *tsdPtr = (ThreadSpecificData *) | ||
Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); | ||
if (!menusInitialized) { | ||
Tcl_MutexLock(&menuMutex); | ||
if (!menusInitialized) { | ||
TkpMenuInit(); | ||
menusInitialized = 1; | ||
} | ||
Tcl_MutexUnlock(&menuMutex); | ||
} | ||
if (!tsdPtr->menusInitialized) { | ||
TkpMenuThreadInit(); | ||
tsdPtr->menusInitialized = 1; | ||
} | ||
} | ||
/* $History: tkMenu.c $ | ||
* | ||
* ***************** Version 1 ***************** | ||
* User: Dtashley Date: 1/02/01 Time: 2:58a | ||
* Created in $/IjuScripter, IjuConsole/Source/Tk Base | ||
* Initial check-in. | ||
*/ | ||
/* End of TKMENU.C */ | ||
1 | /* $Header$ */ | |
2 | ||
3 | /* | |
4 | * tkMenu.c -- | |
5 | * | |
6 | * This file contains most of the code for implementing menus in Tk. It takes | |
7 | * care of all of the generic (platform-independent) parts of menus, and | |
8 | * is supplemented by platform-specific files. The geometry calculation | |
9 | * and drawing code for menus is in the file tkMenuDraw.c | |
10 | * | |
11 | * Copyright (c) 1990-1994 The Regents of the University of California. | |
12 | * Copyright (c) 1994-1998 Sun Microsystems, Inc. | |
13 | * | |
14 | * See the file "license.terms" for information on usage and redistribution | |
15 | * of this file, and for a DISCLAIMER OF ALL WARRANTIES. | |
16 | * | |
17 | * RCS: @(#) $Id: tkmenu.c,v 1.1.1.1 2001/06/13 05:05:37 dtashley Exp $ | |
18 | */ | |
19 | ||
20 | /* | |
21 | * Notes on implementation of menus: | |
22 | * | |
23 | * Menus can be used in three ways: | |
24 | * - as a popup menu, either as part of a menubutton or standalone. | |
25 | * - as a menubar. The menu's cascade items are arranged according to | |
26 | * the specific platform to provide the user access to the menus at all | |
27 | * times | |
28 | * - as a tearoff palette. This is a window with the menu's items in it. | |
29 | * | |
30 | * The goal is to provide the Tk developer with a way to use a common | |
31 | * set of menus for all of these tasks. | |
32 | * | |
33 | * In order to make the bindings for cascade menus work properly under Unix, | |
34 | * the cascade menus' pathnames must be proper children of the menu that | |
35 | * they are cascade from. So if there is a menu .m, and it has two | |
36 | * cascades labelled "File" and "Edit", the cascade menus might have | |
37 | * the pathnames .m.file and .m.edit. Another constraint is that the menus | |
38 | * used for menubars must be children of the toplevel widget that they | |
39 | * are attached to. And on the Macintosh, the platform specific menu handle | |
40 | * for cascades attached to a menu bar must have a title that matches the | |
41 | * label for the cascade menu. | |
42 | * | |
43 | * To handle all of the constraints, Tk menubars and tearoff menus are | |
44 | * implemented using menu clones. Menu clones are full menus in their own | |
45 | * right; they have a Tk window and pathname associated with them; they have | |
46 | * a TkMenu structure and array of entries. However, they are linked with the | |
47 | * original menu that they were cloned from. The reflect the attributes of | |
48 | * the original, or "master", menu. So if an item is added to a menu, and | |
49 | * that menu has clones, then the item must be added to all of its clones | |
50 | * also. Menus are cloned when a menu is torn-off or when a menu is assigned | |
51 | * as a menubar using the "-menu" option of the toplevel's pathname configure | |
52 | * subcommand. When a clone is destroyed, only the clone is destroyed, but | |
53 | * when the master menu is destroyed, all clones are also destroyed. This | |
54 | * allows the developer to just deal with one set of menus when creating | |
55 | * and destroying. | |
56 | * | |
57 | * Clones are rather tricky when a menu with cascade entries is cloned (such | |
58 | * as a menubar). Not only does the menu have to be cloned, but each cascade | |
59 | * entry's corresponding menu must also be cloned. This maintains the pathname | |
60 | * parent-child hierarchy necessary for menubars and toplevels to work. | |
61 | * This leads to several special cases: | |
62 | * | |
63 | * 1. When a new menu is created, and it is pointed to by cascade entries in | |
64 | * cloned menus, the new menu has to be cloned to parallel the cascade | |
65 | * structure. | |
66 | * 2. When a cascade item is added to a menu that has been cloned, and the | |
67 | * menu that the cascade item points to exists, that menu has to be cloned. | |
68 | * 3. When the menu that a cascade entry points to is changed, the old | |
69 | * cloned cascade menu has to be discarded, and the new one has to be cloned. | |
70 | * | |
71 | */ | |
72 | ||
73 | #if 0 | |
74 | ||
75 | /* | |
76 | * used only to test for old config code | |
77 | */ | |
78 | ||
79 | #define __NO_OLD_CONFIG | |
80 | #endif | |
81 | ||
82 | #include "tkPort.h" | |
83 | #include "tkMenu.h" | |
84 | ||
85 | #define MENU_HASH_KEY "tkMenus" | |
86 | ||
87 | typedef struct ThreadSpecificData { | |
88 | int menusInitialized; /* Flag indicates whether thread-specific | |
89 | * elements of the Windows Menu module | |
90 | * have been initialized. */ | |
91 | } ThreadSpecificData; | |
92 | static Tcl_ThreadDataKey dataKey; | |
93 | ||
94 | /* | |
95 | * The following flag indicates whether the process-wide state for | |
96 | * the Menu module has been intialized. The Mutex protects access to | |
97 | * that flag. | |
98 | */ | |
99 | ||
100 | static int menusInitialized; | |
101 | TCL_DECLARE_MUTEX(menuMutex) | |
102 | ||
103 | /* | |
104 | * Configuration specs for individual menu entries. If this changes, be sure | |
105 | * to update code in TkpMenuInit that changes the font string entry. | |
106 | */ | |
107 | ||
108 | char *tkMenuStateStrings[] = {"active", "normal", "disabled", (char *) NULL}; | |
109 | ||
110 | static char *menuEntryTypeStrings[] = {"cascade", "checkbutton", "command", | |
111 | "radiobutton", "separator", (char *) NULL}; | |
112 | ||
113 | Tk_OptionSpec tkBasicMenuEntryConfigSpecs[] = { | |
114 | {TK_OPTION_BORDER, "-activebackground", (char *) NULL, (char *) NULL, | |
115 | DEF_MENU_ENTRY_ACTIVE_BG, Tk_Offset(TkMenuEntry, activeBorderPtr), -1, | |
116 | TK_OPTION_NULL_OK}, | |
117 | {TK_OPTION_COLOR, "-activeforeground", (char *) NULL, (char *) NULL, | |
118 | DEF_MENU_ENTRY_ACTIVE_FG, | |
119 | Tk_Offset(TkMenuEntry, activeFgPtr), -1, TK_OPTION_NULL_OK}, | |
120 | {TK_OPTION_STRING, "-accelerator", (char *) NULL, (char *) NULL, | |
121 | DEF_MENU_ENTRY_ACCELERATOR, | |
122 | Tk_Offset(TkMenuEntry, accelPtr), -1, TK_OPTION_NULL_OK}, | |
123 | {TK_OPTION_BORDER, "-background", (char *) NULL, (char *) NULL, | |
124 | DEF_MENU_ENTRY_BG, | |
125 | Tk_Offset(TkMenuEntry, borderPtr), -1, TK_OPTION_NULL_OK}, | |
126 | {TK_OPTION_BITMAP, "-bitmap", (char *) NULL, (char *) NULL, | |
127 | DEF_MENU_ENTRY_BITMAP, | |
128 | Tk_Offset(TkMenuEntry, bitmapPtr), -1, TK_OPTION_NULL_OK}, | |
129 | {TK_OPTION_BOOLEAN, "-columnbreak", (char *) NULL, (char *) NULL, | |
130 | DEF_MENU_ENTRY_COLUMN_BREAK, | |
131 | -1, Tk_Offset(TkMenuEntry, columnBreak)}, | |
132 | {TK_OPTION_STRING, "-command", (char *) NULL, (char *) NULL, | |
133 | DEF_MENU_ENTRY_COMMAND, | |
134 | Tk_Offset(TkMenuEntry, commandPtr), -1, TK_OPTION_NULL_OK}, | |
135 | {TK_OPTION_FONT, "-font", (char *) NULL, (char *) NULL, | |
136 | DEF_MENU_ENTRY_FONT, | |
137 | Tk_Offset(TkMenuEntry, fontPtr), -1, TK_OPTION_NULL_OK}, | |
138 | {TK_OPTION_COLOR, "-foreground", (char *) NULL, (char *) NULL, | |
139 | DEF_MENU_ENTRY_FG, | |
140 | Tk_Offset(TkMenuEntry, fgPtr), -1, TK_OPTION_NULL_OK}, | |
141 | {TK_OPTION_BOOLEAN, "-hidemargin", (char *) NULL, (char *) NULL, | |
142 | DEF_MENU_ENTRY_HIDE_MARGIN, | |
143 | -1, Tk_Offset(TkMenuEntry, hideMargin)}, | |
144 | {TK_OPTION_STRING, "-image", (char *) NULL, (char *) NULL, | |
145 | DEF_MENU_ENTRY_IMAGE, | |
146 | Tk_Offset(TkMenuEntry, imagePtr), -1, TK_OPTION_NULL_OK}, | |
147 | {TK_OPTION_STRING, "-label", (char *) NULL, (char *) NULL, | |
148 | DEF_MENU_ENTRY_LABEL, | |
149 | Tk_Offset(TkMenuEntry, labelPtr), -1, 0}, | |
150 | {TK_OPTION_STRING_TABLE, "-state", (char *) NULL, (char *) NULL, | |
151 | DEF_MENU_ENTRY_STATE, | |
152 | -1, Tk_Offset(TkMenuEntry, state), 0, | |
153 | (ClientData) tkMenuStateStrings}, | |
154 | {TK_OPTION_INT, "-underline", (char *) NULL, (char *) NULL, | |
155 | DEF_MENU_ENTRY_UNDERLINE, -1, Tk_Offset(TkMenuEntry, underline)}, | |
156 | {TK_OPTION_END} | |
157 | }; | |
158 | ||
159 | Tk_OptionSpec tkSeparatorEntryConfigSpecs[] = { | |
160 | {TK_OPTION_BORDER, "-background", (char *) NULL, (char *) NULL, | |
161 | DEF_MENU_ENTRY_BG, | |
162 | Tk_Offset(TkMenuEntry, borderPtr), -1, TK_OPTION_NULL_OK}, | |
163 | {TK_OPTION_END} | |
164 | }; | |
165 | ||
166 | Tk_OptionSpec tkCheckButtonEntryConfigSpecs[] = { | |
167 | {TK_OPTION_BOOLEAN, "-indicatoron", (char *) NULL, (char *) NULL, | |
168 | DEF_MENU_ENTRY_INDICATOR, | |
169 | -1, Tk_Offset(TkMenuEntry, indicatorOn)}, | |
170 | {TK_OPTION_STRING, "-offvalue", (char *) NULL, (char *) NULL, | |
171 | DEF_MENU_ENTRY_OFF_VALUE, | |
172 | Tk_Offset(TkMenuEntry, offValuePtr), -1}, | |
173 | {TK_OPTION_STRING, "-onvalue", (char *) NULL, (char *) NULL, | |
174 | DEF_MENU_ENTRY_ON_VALUE, | |
175 | Tk_Offset(TkMenuEntry, onValuePtr), -1}, | |
176 | {TK_OPTION_COLOR, "-selectcolor", (char *) NULL, (char *) NULL, | |
177 | DEF_MENU_ENTRY_SELECT, | |
178 | Tk_Offset(TkMenuEntry, indicatorFgPtr), -1, TK_OPTION_NULL_OK}, | |
179 | {TK_OPTION_STRING, "-selectimage", (char *) NULL, (char *) NULL, | |
180 | DEF_MENU_ENTRY_SELECT_IMAGE, | |
181 | Tk_Offset(TkMenuEntry, selectImagePtr), -1, TK_OPTION_NULL_OK}, | |
182 | {TK_OPTION_STRING, "-variable", (char *) NULL, (char *) NULL, | |
183 | DEF_MENU_ENTRY_CHECK_VARIABLE, | |
184 | Tk_Offset(TkMenuEntry, namePtr), -1, TK_OPTION_NULL_OK}, | |
185 | {TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL, | |
186 | (char *) NULL, 0, -1, 0, (ClientData) tkBasicMenuEntryConfigSpecs} | |
187 | }; | |
188 | ||
189 | Tk_OptionSpec tkRadioButtonEntryConfigSpecs[] = { | |
190 | {TK_OPTION_BOOLEAN, "-indicatoron", (char *) NULL, (char *) NULL, | |
191 | DEF_MENU_ENTRY_INDICATOR, | |
192 | -1, Tk_Offset(TkMenuEntry, indicatorOn)}, | |
193 | {TK_OPTION_COLOR, "-selectcolor", (char *) NULL, (char *) NULL, | |
194 | DEF_MENU_ENTRY_SELECT, | |
195 | Tk_Offset(TkMenuEntry, indicatorFgPtr), -1, TK_OPTION_NULL_OK}, | |
196 | {TK_OPTION_STRING, "-selectimage", (char *) NULL, (char *) NULL, | |
197 | DEF_MENU_ENTRY_SELECT_IMAGE, | |
198 | Tk_Offset(TkMenuEntry, selectImagePtr), -1, TK_OPTION_NULL_OK}, | |
199 | {TK_OPTION_STRING, "-value", (char *) NULL, (char *) NULL, | |
200 | DEF_MENU_ENTRY_VALUE, | |
201 | Tk_Offset(TkMenuEntry, onValuePtr), -1, TK_OPTION_NULL_OK}, | |
202 | {TK_OPTION_STRING, "-variable", (char *) NULL, (char *) NULL, | |
203 | DEF_MENU_ENTRY_RADIO_VARIABLE, | |
204 | Tk_Offset(TkMenuEntry, namePtr), -1, 0}, | |
205 | {TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL, | |
206 | (char *) NULL, 0, -1, 0, (ClientData) tkBasicMenuEntryConfigSpecs} | |
207 | }; | |
208 | ||
209 | Tk_OptionSpec tkCascadeEntryConfigSpecs[] = { | |
210 | {TK_OPTION_STRING, "-menu", (char *) NULL, (char *) NULL, | |
211 | DEF_MENU_ENTRY_MENU, | |
212 | Tk_Offset(TkMenuEntry, namePtr), -1, TK_OPTION_NULL_OK}, | |
213 | {TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL, | |
214 | (char *) NULL, 0, -1, 0, (ClientData) tkBasicMenuEntryConfigSpecs} | |
215 | }; | |
216 | ||
217 | Tk_OptionSpec tkTearoffEntryConfigSpecs[] = { | |
218 | {TK_OPTION_BORDER, "-background", (char *) NULL, (char *) NULL, | |
219 | DEF_MENU_ENTRY_BG, | |
220 | Tk_Offset(TkMenuEntry, borderPtr), -1, TK_OPTION_NULL_OK}, | |
221 | {TK_OPTION_STRING_TABLE, "-state", (char *) NULL, (char *) NULL, | |
222 | DEF_MENU_ENTRY_STATE, -1, Tk_Offset(TkMenuEntry, state), 0, | |
223 | (ClientData) tkMenuStateStrings}, | |
224 | {TK_OPTION_END} | |
225 | }; | |
226 | ||
227 | static Tk_OptionSpec *specsArray[] = { | |
228 | tkCascadeEntryConfigSpecs, tkCheckButtonEntryConfigSpecs, | |
229 | tkBasicMenuEntryConfigSpecs, tkRadioButtonEntryConfigSpecs, | |
230 | tkSeparatorEntryConfigSpecs, tkTearoffEntryConfigSpecs}; | |
231 | ||
232 | /* | |
233 | * Menu type strings for use with Tcl_GetIndexFromObj. | |
234 | */ | |
235 | ||
236 | static char *menuTypeStrings[] = {"normal", "tearoff", "menubar", | |
237 | (char *) NULL}; | |
238 | ||
239 | Tk_OptionSpec tkMenuConfigSpecs[] = { | |
240 | {TK_OPTION_BORDER, "-activebackground", "activeBackground", | |
241 | "Foreground", DEF_MENU_ACTIVE_BG_COLOR, | |
242 | Tk_Offset(TkMenu, activeBorderPtr), -1, 0, | |
243 | (ClientData) DEF_MENU_ACTIVE_BG_MONO}, | |
244 | {TK_OPTION_PIXELS, "-activeborderwidth", "activeBorderWidth", | |
245 | "BorderWidth", DEF_MENU_ACTIVE_BORDER_WIDTH, | |
246 | Tk_Offset(TkMenu, activeBorderWidthPtr), -1}, | |
247 | {TK_OPTION_COLOR, "-activeforeground", "activeForeground", | |
248 | "Background", DEF_MENU_ACTIVE_FG_COLOR, | |
249 | Tk_Offset(TkMenu, activeFgPtr), -1, 0, | |
250 | (ClientData) DEF_MENU_ACTIVE_FG_MONO}, | |
251 | {TK_OPTION_BORDER, "-background", "background", "Background", | |
252 | DEF_MENU_BG_COLOR, Tk_Offset(TkMenu, borderPtr), -1, 0, | |
253 | (ClientData) DEF_MENU_BG_MONO}, | |
254 | {TK_OPTION_SYNONYM, "-bd", (char *) NULL, (char *) NULL, | |
255 | (char *) NULL, 0, -1, 0, (ClientData) "-borderwidth"}, | |
256 | {TK_OPTION_SYNONYM, "-bg", (char *) NULL, (char *) NULL, | |
257 | (char *) NULL, 0, -1, 0, (ClientData) "-background"}, | |
258 | {TK_OPTION_PIXELS, "-borderwidth", "borderWidth", "BorderWidth", | |
259 | DEF_MENU_BORDER_WIDTH, | |
260 | Tk_Offset(TkMenu, borderWidthPtr), -1, 0}, | |
261 | {TK_OPTION_CURSOR, "-cursor", "cursor", "Cursor", | |
262 | DEF_MENU_CURSOR, | |
263 | Tk_Offset(TkMenu, cursorPtr), -1, TK_OPTION_NULL_OK}, | |
264 | {TK_OPTION_COLOR, "-disabledforeground", "disabledForeground", | |
265 | "DisabledForeground", DEF_MENU_DISABLED_FG_COLOR, | |
266 | Tk_Offset(TkMenu, disabledFgPtr), -1, TK_OPTION_NULL_OK, | |
267 | (ClientData) DEF_MENU_DISABLED_FG_MONO}, | |
268 | {TK_OPTION_SYNONYM, "-fg", (char *) NULL, (char *) NULL, | |
269 | (char *) NULL, 0, -1, 0, (ClientData) "-foreground"}, | |
270 | {TK_OPTION_FONT, "-font", "font", "Font", | |
271 | DEF_MENU_FONT, Tk_Offset(TkMenu, fontPtr), -1}, | |
272 | {TK_OPTION_COLOR, "-foreground", "foreground", "Foreground", | |
273 | DEF_MENU_FG, Tk_Offset(TkMenu, fgPtr), -1}, | |
274 | {TK_OPTION_STRING, "-postcommand", "postCommand", "Command", | |
275 | DEF_MENU_POST_COMMAND, | |
276 | Tk_Offset(TkMenu, postCommandPtr), -1, TK_OPTION_NULL_OK}, | |
277 | {TK_OPTION_RELIEF, "-relief", "relief", "Relief", | |
278 | DEF_MENU_RELIEF, Tk_Offset(TkMenu, reliefPtr), -1}, | |
279 | {TK_OPTION_COLOR, "-selectcolor", "selectColor", "Background", | |
280 | DEF_MENU_SELECT_COLOR, Tk_Offset(TkMenu, indicatorFgPtr), -1, 0, | |
281 | (ClientData) DEF_MENU_SELECT_MONO}, | |
282 | {TK_OPTION_STRING, "-takefocus", "takeFocus", "TakeFocus", | |
283 | DEF_MENU_TAKE_FOCUS, | |
284 | Tk_Offset(TkMenu, takeFocusPtr), -1, TK_OPTION_NULL_OK}, | |
285 | {TK_OPTION_BOOLEAN, "-tearoff", "tearOff", "TearOff", | |
286 | DEF_MENU_TEAROFF, -1, Tk_Offset(TkMenu, tearoff)}, | |
287 | {TK_OPTION_STRING, "-tearoffcommand", "tearOffCommand", | |
288 | "TearOffCommand", DEF_MENU_TEAROFF_CMD, | |
289 | Tk_Offset(TkMenu, tearoffCommandPtr), -1, TK_OPTION_NULL_OK}, | |
290 | {TK_OPTION_STRING, "-title", "title", "Title", | |
291 | DEF_MENU_TITLE, Tk_Offset(TkMenu, titlePtr), -1, | |
292 | TK_OPTION_NULL_OK}, | |
293 | {TK_OPTION_STRING_TABLE, "-type", "type", "Type", | |
294 | DEF_MENU_TYPE, Tk_Offset(TkMenu, menuTypePtr), -1, TK_OPTION_NULL_OK, | |
295 | (ClientData) menuTypeStrings}, | |
296 | {TK_OPTION_END} | |
297 | }; | |
298 | ||
299 | /* | |
300 | * Command line options. Put here because MenuCmd has to look at them | |
301 | * along with MenuWidgetObjCmd. | |
302 | */ | |
303 | ||
304 | static char *menuOptions[] = { | |
305 | "activate", "add", "cget", "clone", "configure", "delete", "entrycget", | |
306 | "entryconfigure", "index", "insert", "invoke", "post", "postcascade", | |
307 | "type", "unpost", "yposition", (char *) NULL | |
308 | }; | |
309 | enum options { | |
310 | MENU_ACTIVATE, MENU_ADD, MENU_CGET, MENU_CLONE, MENU_CONFIGURE, | |
311 | MENU_DELETE, MENU_ENTRYCGET, MENU_ENTRYCONFIGURE, MENU_INDEX, | |
312 | MENU_INSERT, MENU_INVOKE, MENU_POST, MENU_POSTCASCADE, MENU_TYPE, | |
313 | MENU_UNPOST, MENU_YPOSITION | |
314 | }; | |
315 | ||
316 | /* | |
317 | * Prototypes for static procedures in this file: | |
318 | */ | |
319 | ||
320 | static int CloneMenu _ANSI_ARGS_((TkMenu *menuPtr, | |
321 | Tcl_Obj *newMenuName, Tcl_Obj *newMenuTypeString)); | |
322 | static int ConfigureMenu _ANSI_ARGS_((Tcl_Interp *interp, | |
323 | TkMenu *menuPtr, int objc, Tcl_Obj *CONST objv[])); | |
324 | static int ConfigureMenuCloneEntries _ANSI_ARGS_(( | |
325 | Tcl_Interp *interp, TkMenu *menuPtr, int index, | |
326 | int objc, Tcl_Obj *CONST objv[])); | |
327 | static int ConfigureMenuEntry _ANSI_ARGS_((TkMenuEntry *mePtr, | |
328 | int objc, Tcl_Obj *CONST objv[])); | |
329 | static void DeleteMenuCloneEntries _ANSI_ARGS_((TkMenu *menuPtr, | |
330 | int first, int last)); | |
331 | static void DestroyMenuHashTable _ANSI_ARGS_(( | |
332 | ClientData clientData, Tcl_Interp *interp)); | |
333 | static void DestroyMenuInstance _ANSI_ARGS_((TkMenu *menuPtr)); | |
334 | static void DestroyMenuEntry _ANSI_ARGS_((char *memPtr)); | |
335 | static int GetIndexFromCoords | |
336 | _ANSI_ARGS_((Tcl_Interp *interp, TkMenu *menuPtr, | |
337 | char *string, int *indexPtr)); | |
338 | static int MenuDoYPosition _ANSI_ARGS_((Tcl_Interp *interp, | |
339 | TkMenu *menuPtr, Tcl_Obj *objPtr)); | |
340 | static int MenuAddOrInsert _ANSI_ARGS_((Tcl_Interp *interp, | |
341 | TkMenu *menuPtr, Tcl_Obj *indexPtr, int objc, | |
342 | Tcl_Obj *CONST objv[])); | |
343 | static int MenuCmd _ANSI_ARGS_((ClientData clientData, | |
344 | Tcl_Interp *interp, int objc, | |
345 | Tcl_Obj *CONST objv[])); | |
346 | static void MenuCmdDeletedProc _ANSI_ARGS_(( | |
347 | ClientData clientData)); | |
348 | static TkMenuEntry * MenuNewEntry _ANSI_ARGS_((TkMenu *menuPtr, int index, | |
349 | int type)); | |
350 | static char * MenuVarProc _ANSI_ARGS_((ClientData clientData, | |
351 | Tcl_Interp *interp, char *name1, char *name2, | |
352 | int flags)); | |
353 | static int MenuWidgetObjCmd _ANSI_ARGS_((ClientData clientData, | |
354 | Tcl_Interp *interp, int objc, | |
355 | Tcl_Obj *CONST objv[])); | |
356 | static void MenuWorldChanged _ANSI_ARGS_(( | |
357 | ClientData instanceData)); | |
358 | static int PostProcessEntry _ANSI_ARGS_((TkMenuEntry *mePtr)); | |
359 | static void RecursivelyDeleteMenu _ANSI_ARGS_((TkMenu *menuPtr)); | |
360 | static void UnhookCascadeEntry _ANSI_ARGS_((TkMenuEntry *mePtr)); | |
361 | ||
362 | /* | |
363 | * The structure below is a list of procs that respond to certain window | |
364 | * manager events. One of these includes a font change, which forces | |
365 | * the geometry proc to be called. | |
366 | */ | |
367 | ||
368 | static TkClassProcs menuClass = { | |
369 | NULL, /* createProc. */ | |
370 | MenuWorldChanged /* geometryProc. */ | |
371 | }; | |
372 | ||
373 | /* | |
374 | *-------------------------------------------------------------- | |
375 | * | |
376 | * Tk_CreateMenuCmd -- | |
377 | * | |
378 | * Called by Tk at initialization time to create the menu | |
379 | * command. | |
380 | * | |
381 | * Results: | |
382 | * A standard Tcl result. | |
383 | * | |
384 | * Side effects: | |
385 | * See the user documentation. | |
386 | * | |
387 | *-------------------------------------------------------------- | |
388 | */ | |
389 | ||
390 | int | |
391 | TkCreateMenuCmd(interp) | |
392 | Tcl_Interp *interp; /* Interpreter we are creating the | |
393 | * command in. */ | |
394 | { | |
395 | TkMenuOptionTables *optionTablesPtr = | |
396 | (TkMenuOptionTables *) ckalloc(sizeof(TkMenuOptionTables)); | |
397 | ||
398 | optionTablesPtr->menuOptionTable = | |
399 | Tk_CreateOptionTable(interp, tkMenuConfigSpecs); | |
400 | optionTablesPtr->entryOptionTables[TEAROFF_ENTRY] = | |
401 | Tk_CreateOptionTable(interp, specsArray[TEAROFF_ENTRY]); | |
402 | optionTablesPtr->entryOptionTables[COMMAND_ENTRY] = | |
403 | Tk_CreateOptionTable(interp, specsArray[COMMAND_ENTRY]); | |
404 | optionTablesPtr->entryOptionTables[CASCADE_ENTRY] = | |
405 | Tk_CreateOptionTable(interp, specsArray[CASCADE_ENTRY]); | |
406 | optionTablesPtr->entryOptionTables[SEPARATOR_ENTRY] = | |
407 | Tk_CreateOptionTable(interp, specsArray[SEPARATOR_ENTRY]); | |
408 | optionTablesPtr->entryOptionTables[RADIO_BUTTON_ENTRY] = | |
409 | Tk_CreateOptionTable(interp, specsArray[RADIO_BUTTON_ENTRY]); | |
410 | optionTablesPtr->entryOptionTables[CHECK_BUTTON_ENTRY] = | |
411 | Tk_CreateOptionTable(interp, specsArray[CHECK_BUTTON_ENTRY]); | |
412 | ||
413 | Tcl_CreateObjCommand(interp, "menu", MenuCmd, | |
414 | (ClientData) optionTablesPtr, NULL); | |
415 | ||
416 | if (Tcl_IsSafe(interp)) { | |
417 | Tcl_HideCommand(interp, "menu", "menu"); | |
418 | } | |
419 | ||
420 | return TCL_OK; | |
421 | } | |
422 | ||
423 | /* | |
424 | *-------------------------------------------------------------- | |
425 | * | |
426 | * MenuCmd -- | |
427 | * | |
428 | * This procedure is invoked to process the "menu" Tcl | |
429 | * command. See the user documentation for details on | |
430 | * what it does. | |
431 | * | |
432 | * Results: | |
433 | * A standard Tcl result. | |
434 | * | |
435 | * Side effects: | |
436 | * See the user documentation. | |
437 | * | |
438 | *-------------------------------------------------------------- | |
439 | */ | |
440 | ||
441 | static int | |
442 | MenuCmd(clientData, interp, objc, objv) | |
443 | ClientData clientData; /* Main window associated with | |
444 | * interpreter. */ | |
445 | Tcl_Interp *interp; /* Current interpreter. */ | |
446 | int objc; /* Number of arguments. */ | |
447 | Tcl_Obj *CONST objv[]; /* Argument strings. */ | |
448 | { | |
449 | Tk_Window tkwin = Tk_MainWindow(interp); | |
450 | Tk_Window new; | |
451 | register TkMenu *menuPtr; | |
452 | TkMenuReferences *menuRefPtr; | |
453 | int i, index; | |
454 | int toplevel; | |
455 | char *windowName; | |
456 | static char *typeStringList[] = {"-type", (char *) NULL}; | |
457 | TkMenuOptionTables *optionTablesPtr = (TkMenuOptionTables *) clientData; | |
458 | ||
459 | if (objc < 2) { | |
460 | Tcl_WrongNumArgs(interp, 1, objv, "pathName ?options?"); | |
461 | return TCL_ERROR; | |
462 | } | |
463 | ||
464 | TkMenuInit(); | |
465 | ||
466 | toplevel = 1; | |
467 | for (i = 2; i < (objc - 1); i++) { | |
468 | if (Tcl_GetIndexFromObj(NULL, objv[i], typeStringList, NULL, 0, &index) | |
469 | != TCL_ERROR) { | |
470 | if ((Tcl_GetIndexFromObj(NULL, objv[i + 1], menuTypeStrings, NULL, | |
471 | 0, &index) == TCL_OK) && (index == MENUBAR)) { | |
472 | toplevel = 0; | |
473 | } | |
474 | break; | |
475 | } | |
476 | } | |
477 | ||
478 | windowName = Tcl_GetStringFromObj(objv[1], NULL); | |
479 | new = Tk_CreateWindowFromPath(interp, tkwin, windowName, toplevel ? "" | |
480 | : NULL); | |
481 | if (new == NULL) { | |
482 | return TCL_ERROR; | |
483 | } | |
484 | ||
485 | /* | |
486 | * Initialize the data structure for the menu. | |
487 | */ | |
488 | ||
489 | menuPtr = (TkMenu *) ckalloc(sizeof(TkMenu)); | |
490 | menuPtr->tkwin = new; | |
491 | menuPtr->display = Tk_Display(new); | |
492 | menuPtr->interp = interp; | |
493 | menuPtr->widgetCmd = Tcl_CreateObjCommand(interp, | |
494 | Tk_PathName(menuPtr->tkwin), MenuWidgetObjCmd, | |
495 | (ClientData) menuPtr, MenuCmdDeletedProc); | |
496 | menuPtr->entries = NULL; | |
497 | menuPtr->numEntries = 0; | |
498 | menuPtr->active = -1; | |
499 | menuPtr->borderPtr = NULL; | |
500 | menuPtr->borderWidthPtr = NULL; | |
501 | menuPtr->reliefPtr = NULL; | |
502 | menuPtr->activeBorderPtr = NULL; | |
503 | menuPtr->activeBorderWidthPtr = NULL; | |
504 | menuPtr->fontPtr = NULL; | |
505 | menuPtr->fgPtr = NULL; | |
506 | menuPtr->disabledFgPtr = NULL; | |
507 | menuPtr->activeFgPtr = NULL; | |
508 | menuPtr->indicatorFgPtr = NULL; | |
509 | menuPtr->tearoff = 0; | |
510 | menuPtr->tearoffCommandPtr = NULL; | |
511 | menuPtr->cursorPtr = None; | |
512 | menuPtr->takeFocusPtr = NULL; | |
513 | menuPtr->postCommandPtr = NULL; | |
514 | menuPtr->postCommandGeneration = 0; | |
515 | menuPtr->postedCascade = NULL; | |
516 | menuPtr->nextInstancePtr = NULL; | |
517 | menuPtr->masterMenuPtr = menuPtr; | |
518 | menuPtr->menuType = UNKNOWN_TYPE; | |
519 | menuPtr->menuFlags = 0; | |
520 | menuPtr->parentTopLevelPtr = NULL; | |
521 | menuPtr->menuTypePtr = NULL; | |
522 | menuPtr->titlePtr = NULL; | |
523 | menuPtr->errorStructPtr = NULL; | |
524 | menuPtr->optionTablesPtr = optionTablesPtr; | |
525 | TkMenuInitializeDrawingFields(menuPtr); | |
526 | ||
527 | Tk_SetClass(menuPtr->tkwin, "Menu"); | |
528 | TkSetClassProcs(menuPtr->tkwin, &menuClass, (ClientData) menuPtr); | |
529 | if (Tk_InitOptions(interp, (char *) menuPtr, | |
530 | menuPtr->optionTablesPtr->menuOptionTable, menuPtr->tkwin) | |
531 | != TCL_OK) { | |
532 | Tk_DestroyWindow(menuPtr->tkwin); | |
533 | ckfree((char *) menuPtr); | |
534 | return TCL_ERROR; | |
535 | } | |
536 | ||
537 | ||
538 | menuRefPtr = TkCreateMenuReferences(menuPtr->interp, | |
539 | Tk_PathName(menuPtr->tkwin)); | |
540 | menuRefPtr->menuPtr = menuPtr; | |
541 | menuPtr->menuRefPtr = menuRefPtr; | |
542 | if (TCL_OK != TkpNewMenu(menuPtr)) { | |
543 | Tk_DestroyWindow(menuPtr->tkwin); | |
544 | ckfree((char *) menuPtr); | |
545 | return TCL_ERROR; | |
546 | } | |
547 | ||
548 | Tk_CreateEventHandler(new, ExposureMask|StructureNotifyMask|ActivateMask, | |
549 | TkMenuEventProc, (ClientData) menuPtr); | |
550 | if (ConfigureMenu(interp, menuPtr, objc - 2, objv + 2) != TCL_OK) { | |
551 | Tk_DestroyWindow(menuPtr->tkwin); | |
552 | return TCL_ERROR; | |
553 | } | |
554 | ||
555 | /* | |
556 | * If a menu has a parent menu pointing to it as a cascade entry, the | |
557 | * parent menu needs to be told that this menu now exists so that | |
558 | * the platform-part of the menu is correctly updated. | |
559 | * | |
560 | * If a menu has an instance and has cascade entries, then each cascade | |
561 | * menu must also have a parallel instance. This is especially true on | |
562 | * the Mac, where each menu has to have a separate title everytime it is in | |
563 | * a menubar. For instance, say you have a menu .m1 with a cascade entry | |
564 | * for .m2, where .m2 does not exist yet. You then put .m1 into a menubar. | |
565 | * This creates a menubar instance for .m1, but since .m2 is not there, | |
566 | * nothing else happens. When we go to create .m2, we hook it up properly | |
567 | * with .m1. However, we now need to clone .m2 and assign the clone of .m2 | |
568 | * to be the cascade entry for the clone of .m1. This is special case | |
569 | * #1 listed in the introductory comment. | |
570 | */ | |
571 | ||
572 | if (menuRefPtr->parentEntryPtr != NULL) { | |
573 | TkMenuEntry *cascadeListPtr = menuRefPtr->parentEntryPtr; | |
574 | TkMenuEntry *nextCascadePtr; | |
575 | Tcl_Obj *newMenuName; | |
576 | Tcl_Obj *newObjv[2]; | |
577 | ||
578 | while (cascadeListPtr != NULL) { | |
579 | ||
580 | nextCascadePtr = cascadeListPtr->nextCascadePtr; | |
581 | ||
582 | /* | |
583 | * If we have a new master menu, and an existing cloned menu | |
584 | * points to this menu in a cascade entry, we have to clone | |
585 | * the new menu and point the entry to the clone instead | |
586 | * of the menu we are creating. Otherwise, ConfigureMenuEntry | |
587 | * will hook up the platform-specific cascade linkages now | |
588 | * that the menu we are creating exists. | |
589 | */ | |
590 | ||
591 | if ((menuPtr->masterMenuPtr != menuPtr) | |
592 | || ((menuPtr->masterMenuPtr == menuPtr) | |
593 | && ((cascadeListPtr->menuPtr->masterMenuPtr | |
594 | == cascadeListPtr->menuPtr)))) { | |
595 | newObjv[0] = Tcl_NewStringObj("-menu", -1); | |
596 | newObjv[1] = Tcl_NewStringObj(Tk_PathName(menuPtr->tkwin), -1); | |
597 | Tcl_IncrRefCount(newObjv[0]); | |
598 | Tcl_IncrRefCount(newObjv[1]); | |
599 | ConfigureMenuEntry(cascadeListPtr, 2, newObjv); | |
600 | Tcl_DecrRefCount(newObjv[0]); | |
601 | Tcl_DecrRefCount(newObjv[1]); | |
602 | } else { | |
603 | Tcl_Obj *normalPtr = Tcl_NewStringObj("normal", -1); | |
604 | Tcl_Obj *windowNamePtr = Tcl_NewStringObj( | |
605 | Tk_PathName(cascadeListPtr->menuPtr->tkwin), -1); | |
606 | ||
607 | Tcl_IncrRefCount(normalPtr); | |
608 | Tcl_IncrRefCount(windowNamePtr); | |
609 | newMenuName = TkNewMenuName(menuPtr->interp, | |
610 | windowNamePtr, menuPtr); | |
611 | Tcl_IncrRefCount(newMenuName); | |
612 | CloneMenu(menuPtr, newMenuName, normalPtr); | |
613 | ||
614 | /* | |
615 | * Now we can set the new menu instance to be the cascade entry | |
616 | * of the parent's instance. | |
617 | */ | |
618 | ||
619 | newObjv[0] = Tcl_NewStringObj("-menu", -1); | |
620 | newObjv[1] = newMenuName; | |
621 | Tcl_IncrRefCount(newObjv[0]); | |
622 | ConfigureMenuEntry(cascadeListPtr, 2, newObjv); | |
623 | Tcl_DecrRefCount(normalPtr); | |
624 | Tcl_DecrRefCount(newObjv[0]); | |
625 | Tcl_DecrRefCount(newObjv[1]); | |
626 | Tcl_DecrRefCount(windowNamePtr); | |
627 | } | |
628 | cascadeListPtr = nextCascadePtr; | |
629 | } | |
630 | } | |
631 | ||
632 | /* | |
633 | * If there already exist toplevel widgets that refer to this menu, | |
634 | * find them and notify them so that they can reconfigure their | |
635 | * geometry to reflect the menu. | |
636 | */ | |
637 | ||
638 | if (menuRefPtr->topLevelListPtr != NULL) { | |
639 | TkMenuTopLevelList *topLevelListPtr = menuRefPtr->topLevelListPtr; | |
640 | TkMenuTopLevelList *nextPtr; | |
641 | Tk_Window listtkwin; | |
642 | while (topLevelListPtr != NULL) { | |
643 | ||
644 | /* | |
645 | * Need to get the next pointer first. TkSetWindowMenuBar | |
646 | * changes the list, so that the next pointer is different | |
647 | * after calling it. | |
648 | */ | |
649 | ||
650 | nextPtr = topLevelListPtr->nextPtr; | |
651 | listtkwin = topLevelListPtr->tkwin; | |
652 | TkSetWindowMenuBar(menuPtr->interp, listtkwin, | |
653 | Tk_PathName(menuPtr->tkwin), Tk_PathName(menuPtr->tkwin)); | |
654 | topLevelListPtr = nextPtr; | |
655 | } | |
656 | } | |
657 | ||
658 | Tcl_SetResult(interp, Tk_PathName(menuPtr->tkwin), TCL_STATIC); | |
659 | return TCL_OK; | |
660 | } | |
661 | ||
662 | /* | |
663 | *-------------------------------------------------------------- | |
664 | * | |
665 | * MenuWidgetObjCmd -- | |
666 | * | |
667 | * This procedure is invoked to process the Tcl command | |
668 | * that corresponds to a widget managed by this module. | |
669 | * See the user documentation for details on what it does. | |
670 | * | |
671 | * Results: | |
672 | * A standard Tcl result. | |
673 | * | |
674 | * Side effects: | |
675 | * See the user documentation. | |
676 | * | |
677 | *-------------------------------------------------------------- | |
678 | */ | |
679 | ||
680 | static int | |
681 | MenuWidgetObjCmd(clientData, interp, objc, objv) | |
682 | ClientData clientData; /* Information about menu widget. */ | |
683 | Tcl_Interp *interp; /* Current interpreter. */ | |
684 | int objc; /* Number of arguments. */ | |
685 | Tcl_Obj *CONST objv[]; /* Argument strings. */ | |
686 | { | |
687 | register TkMenu *menuPtr = (TkMenu *) clientData; | |
688 | register TkMenuEntry *mePtr; | |
689 | int result = TCL_OK; | |
690 | int option; | |
691 | ||
692 | if (objc < 2) { | |
693 | Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?"); | |
694 | return TCL_ERROR; | |
695 | } | |
696 | if (Tcl_GetIndexFromObj(interp, objv[1], menuOptions, "option", 0, | |
697 | &option) != TCL_OK) { | |
698 | return TCL_ERROR; | |
699 | } | |
700 | Tcl_Preserve((ClientData) menuPtr); | |
701 | ||
702 | switch ((enum options) option) { | |
703 | case MENU_ACTIVATE: { | |
704 | int index; | |
705 | ||
706 | if (objc != 3) { | |
707 | Tcl_WrongNumArgs(interp, 1, objv, "activate index"); | |
708 | goto error; | |
709 | } | |
710 | if (TkGetMenuIndex(interp, menuPtr, objv[2], 0, &index) | |
711 | != TCL_OK) { | |
712 | goto error; | |
713 | } | |
714 | if (menuPtr->active == index) { | |
715 | goto done; | |
716 | } | |
717 | if ((index >= 0) | |
718 | && ((menuPtr->entries[index]->type == SEPARATOR_ENTRY) | |
719 | || (menuPtr->entries[index]->state | |
720 | == ENTRY_DISABLED))) { | |
721 | index = -1; | |
722 | } | |
723 | result = TkActivateMenuEntry(menuPtr, index); | |
724 | break; | |
725 | } | |
726 | case MENU_ADD: | |
727 | if (objc < 3) { | |
728 | Tcl_WrongNumArgs(interp, 1, objv, "add type ?options?"); | |
729 | goto error; | |
730 | } | |
731 | ||
732 | if (MenuAddOrInsert(interp, menuPtr, (Tcl_Obj *) NULL, | |
733 | objc - 2, objv + 2) != TCL_OK) { | |
734 | goto error; | |
735 | } | |
736 | break; | |
737 | case MENU_CGET: { | |
738 | Tcl_Obj *resultPtr; | |
739 | ||
740 | if (objc != 3) { | |
741 | Tcl_WrongNumArgs(interp, 1, objv, "cget option"); | |
742 | goto error; | |
743 | } | |
744 | resultPtr = Tk_GetOptionValue(interp, (char *) menuPtr, | |
745 | menuPtr->optionTablesPtr->menuOptionTable, objv[2], | |
746 | menuPtr->tkwin); | |
747 | if (resultPtr == NULL) { | |
748 | goto error; | |
749 | } | |
750 | Tcl_SetObjResult(interp, resultPtr); | |
751 | break; | |
752 | } | |
753 | case MENU_CLONE: | |
754 | if ((objc < 3) || (objc > 4)) { | |
755 | Tcl_WrongNumArgs(interp, 1, objv, | |
756 | "clone newMenuName ?menuType?"); | |
757 | goto error; | |
758 | } | |
759 | result = CloneMenu(menuPtr, objv[2], (objc == 3) ? NULL : objv[3]); | |
760 | break; | |
761 | case MENU_CONFIGURE: { | |
762 | Tcl_Obj *resultPtr; | |
763 | ||
764 | if (objc == 2) { | |
765 | resultPtr = Tk_GetOptionInfo(interp, (char *) menuPtr, | |
766 | menuPtr->optionTablesPtr->menuOptionTable, | |
767 | (Tcl_Obj *) NULL, menuPtr->tkwin); | |
768 | if (resultPtr == NULL) { | |
769 | result = TCL_ERROR; | |
770 | } else { | |
771 | result = TCL_OK; | |
772 | Tcl_SetObjResult(interp, resultPtr); | |
773 | } | |
774 | } else if (objc == 3) { | |
775 | resultPtr = Tk_GetOptionInfo(interp, (char *) menuPtr, | |
776 | menuPtr->optionTablesPtr->menuOptionTable, | |
777 | objv[2], menuPtr->tkwin); | |
778 | if (resultPtr == NULL) { | |
779 | result = TCL_ERROR; | |
780 | } else { | |
781 | result = TCL_OK; | |
782 | Tcl_SetObjResult(interp, resultPtr); | |
783 | } | |
784 | } else { | |
785 | result = ConfigureMenu(interp, menuPtr, objc - 2, objv + 2); | |
786 | } | |
787 | if (result != TCL_OK) { | |
788 | goto error; | |
789 | } | |
790 | break; | |
791 | } | |
792 | case MENU_DELETE: { | |
793 | int first, last; | |
794 | ||
795 | if ((objc != 3) && (objc != 4)) { | |
796 | Tcl_WrongNumArgs(interp, 1, objv, "delete first ?last?"); | |
797 | goto error; | |
798 | } | |
799 | if (TkGetMenuIndex(interp, menuPtr, objv[2], 0, &first) | |
800 | != TCL_OK) { | |
801 | goto error; | |
802 | } | |
803 | if (objc == 3) { | |
804 | last = first; | |
805 | } else { | |
806 | if (TkGetMenuIndex(interp, menuPtr, objv[3], 0, &last) | |
807 | != TCL_OK) { | |
808 | goto error; | |
809 | } | |
810 | } | |
811 | if (menuPtr->tearoff && (first == 0)) { | |
812 | ||
813 | /* | |
814 | * Sorry, can't delete the tearoff entry; must reconfigure | |
815 | * the menu. | |
816 | */ | |
817 | ||
818 | first = 1; | |
819 | } | |
820 | if ((first < 0) || (last < first)) { | |
821 | goto done; | |
822 | } | |
823 | DeleteMenuCloneEntries(menuPtr, first, last); | |
824 | break; | |
825 | } | |
826 | case MENU_ENTRYCGET: { | |
827 | int index; | |
828 | Tcl_Obj *resultPtr; | |
829 | ||
830 | if (objc != 4) { | |
831 | Tcl_WrongNumArgs(interp, 1, objv, "entrycget index option"); | |
832 | goto error; | |
833 | } | |
834 | if (TkGetMenuIndex(interp, menuPtr, objv[2], 0, &index) | |
835 | != TCL_OK) { | |
836 | goto error; | |
837 | } | |
838 | if (index < 0) { | |
839 | goto done; | |
840 | } | |
841 | mePtr = menuPtr->entries[index]; | |
842 | Tcl_Preserve((ClientData) mePtr); | |
843 | resultPtr = Tk_GetOptionValue(interp, (char *) mePtr, | |
844 | mePtr->optionTable, objv[3], menuPtr->tkwin); | |
845 | Tcl_Release((ClientData) mePtr); | |
846 | if (resultPtr == NULL) { | |
847 | goto error; | |
848 | } | |
849 | Tcl_SetObjResult(interp, resultPtr); | |
850 | break; | |
851 | } | |
852 | case MENU_ENTRYCONFIGURE: { | |
853 | int index; | |
854 | Tcl_Obj *resultPtr; | |
855 | ||
856 | if (objc < 3) { | |
857 | Tcl_WrongNumArgs(interp, 1, objv, | |
858 | "entryconfigure index ?option value ...?"); | |
859 | goto error; | |
860 | } | |
861 | if (TkGetMenuIndex(interp, menuPtr, objv[2], 0, &index) | |
862 | != TCL_OK) { | |
863 | goto error; | |
864 | } | |
865 | if (index < 0) { | |
866 | goto done; | |
867 | } | |
868 | mePtr = menuPtr->entries[index]; | |
869 | Tcl_Preserve((ClientData) mePtr); | |
870 | if (objc == 3) { | |
871 | resultPtr = Tk_GetOptionInfo(interp, (char *) mePtr, | |
872 | mePtr->optionTable, (Tcl_Obj *) NULL, menuPtr->tkwin); | |
873 | if (resultPtr == NULL) { | |
874 | result = TCL_ERROR; | |
875 | } else { | |
876 | result = TCL_OK; | |
877 | Tcl_SetObjResult(interp, resultPtr); | |
878 | } | |
879 | } else if (objc == 4) { | |
880 | resultPtr = Tk_GetOptionInfo(interp, (char *) mePtr, | |
881 | mePtr->optionTable, objv[3], menuPtr->tkwin); | |
882 | if (resultPtr == NULL) { | |
883 | result = TCL_ERROR; | |
884 | } else { | |
885 | result = TCL_OK; | |
886 | Tcl_SetObjResult(interp, resultPtr); | |
887 | } | |
888 | } else { | |
889 | result = ConfigureMenuCloneEntries(interp, menuPtr, index, | |
890 | objc - 3, objv + 3); | |
891 | } | |
892 | Tcl_Release((ClientData) mePtr); | |
893 | break; | |
894 | } | |
895 | case MENU_INDEX: { | |
896 | int index; | |
897 | ||
898 | if (objc != 3) { | |
899 | Tcl_WrongNumArgs(interp, 1, objv, "index string"); | |
900 | goto error; | |
901 | } | |
902 | if (TkGetMenuIndex(interp, menuPtr, objv[2], 0, &index) | |
903 | != TCL_OK) { | |
904 | goto error; | |
905 | } | |
906 | if (index < 0) { | |
907 | Tcl_SetResult(interp, "none", TCL_STATIC); | |
908 | } else { | |
909 | Tcl_SetIntObj(Tcl_GetObjResult(interp), index); | |
910 | } | |
911 | break; | |
912 | } | |
913 | case MENU_INSERT: | |
914 | if (objc < 4) { | |
915 | Tcl_WrongNumArgs(interp, 1, objv, | |
916 | "insert index type ?options?"); | |
917 | goto error; | |
918 | } | |
919 | if (MenuAddOrInsert(interp, menuPtr, objv[2], objc - 3, | |
920 | objv + 3) != TCL_OK) { | |
921 | goto error; | |
922 | } | |
923 | break; | |
924 | case MENU_INVOKE: { | |
925 | int index; | |
926 | ||
927 | if (objc != 3) { | |
928 | Tcl_WrongNumArgs(interp, 1, objv, "invoke index"); | |
929 | goto error; | |
930 | } | |
931 | if (TkGetMenuIndex(interp, menuPtr, objv[2], 0, &index) | |
932 | != TCL_OK) { | |
933 | goto error; | |
934 | } | |
935 | if (index < 0) { | |
936 | goto done; | |
937 | } | |
938 | result = TkInvokeMenu(interp, menuPtr, index); | |
939 | break; | |
940 | } | |
941 | case MENU_POST: { | |
942 | int x, y; | |
943 | ||
944 | if (objc != 4) { | |
945 | Tcl_WrongNumArgs(interp, 1, objv, "post x y"); | |
946 | goto error; | |
947 | } | |
948 | if ((Tcl_GetIntFromObj(interp, objv[2], &x) != TCL_OK) | |
949 | || (Tcl_GetIntFromObj(interp, objv[3], &y) != TCL_OK)) { | |
950 | goto error; | |
951 | } | |
952 | ||
953 | /* | |
954 | * Tearoff menus are posted differently on Mac and Windows than | |
955 | * non-tearoffs. TkpPostMenu does not actually map the menu's | |
956 | * window on those platforms, and popup menus have to be | |
957 | * handled specially. | |
958 | */ | |
959 | ||
960 | if (menuPtr->menuType != TEAROFF_MENU) { | |
961 | result = TkpPostMenu(interp, menuPtr, x, y); | |
962 | } else { | |
963 | result = TkPostTearoffMenu(interp, menuPtr, x, y); | |
964 | } | |
965 | break; | |
966 | } | |
967 | case MENU_POSTCASCADE: { | |
968 | int index; | |
969 | ||
970 | if (objc != 3) { | |
971 | Tcl_WrongNumArgs(interp, 1, objv, "postcascade index"); | |
972 | goto error; | |
973 | } | |
974 | ||
975 | if (TkGetMenuIndex(interp, menuPtr, objv[2], 0, &index) | |
976 | != TCL_OK) { | |
977 | goto error; | |
978 | } | |
979 | if ((index < 0) || (menuPtr->entries[index]->type | |
980 | != CASCADE_ENTRY)) { | |
981 | result = TkPostSubmenu(interp, menuPtr, (TkMenuEntry *) NULL); | |
982 | } else { | |
983 | result = TkPostSubmenu(interp, menuPtr, | |
984 | menuPtr->entries[index]); | |
985 | } | |
986 | break; | |
987 | } | |
988 | case MENU_TYPE: { | |
989 | int index; | |
990 | ||
991 | if (objc != 3) { | |
992 | Tcl_WrongNumArgs(interp, 1, objv, "type index"); | |
993 | goto error; | |
994 | } | |
995 | if (TkGetMenuIndex(interp, menuPtr, objv[2], 0, &index) | |
996 | != TCL_OK) { | |
997 | goto error; | |
998 | } | |
999 | if (index < 0) { | |
1000 | goto done; | |
1001 | } | |
1002 | if (menuPtr->entries[index]->type == TEAROFF_ENTRY) { | |
1003 | Tcl_SetResult(interp, "tearoff", TCL_STATIC); | |
1004 | } else { | |
1005 | Tcl_SetResult(interp, | |
1006 | menuEntryTypeStrings[menuPtr->entries[index]->type], | |
1007 | TCL_STATIC); | |
1008 | } | |
1009 | break; | |
1010 | } | |
1011 | case MENU_UNPOST: | |
1012 | if (objc != 2) { | |
1013 | Tcl_WrongNumArgs(interp, 1, objv, "unpost"); | |
1014 | goto error; | |
1015 | } | |
1016 | Tk_UnmapWindow(menuPtr->tkwin); | |
1017 | result = TkPostSubmenu(interp, menuPtr, (TkMenuEntry *) NULL); | |
1018 | break; | |
1019 | case MENU_YPOSITION: | |
1020 | if (objc != 3) { | |
1021 | Tcl_WrongNumArgs(interp, 1, objv, "yposition index"); | |
1022 | goto error; | |
1023 | } | |
1024 | result = MenuDoYPosition(interp, menuPtr, objv[2]); | |
1025 | break; | |
1026 | } | |
1027 | done: | |
1028 | Tcl_Release((ClientData) menuPtr); | |
1029 | return result; | |
1030 | ||
1031 | error: | |
1032 | Tcl_Release((ClientData) menuPtr); | |
1033 | return TCL_ERROR; | |
1034 | } | |
1035 | ||
1036 | /* | |
1037 | *---------------------------------------------------------------------- | |
1038 | * | |
1039 | * TkInvokeMenu -- | |
1040 | * | |
1041 | * Given a menu and an index, takes the appropriate action for the | |
1042 | * entry associated with that index. | |
1043 | * | |
1044 | * Results: | |
1045 | * Standard Tcl result. | |
1046 | * | |
1047 | * Side effects: | |
1048 | * Commands may get excecuted; variables may get set; sub-menus may | |
1049 | * get posted. | |
1050 | * | |
1051 | *---------------------------------------------------------------------- | |
1052 | */ | |
1053 | ||
1054 | int | |
1055 | TkInvokeMenu(interp, menuPtr, index) | |
1056 | Tcl_Interp *interp; /* The interp that the menu lives in. */ | |
1057 | TkMenu *menuPtr; /* The menu we are invoking. */ | |
1058 | int index; /* The zero based index of the item we | |
1059 | * are invoking */ | |
1060 | { | |
1061 | int result = TCL_OK; | |
1062 | TkMenuEntry *mePtr; | |
1063 | ||
1064 | if (index < 0) { | |
1065 | goto done; | |
1066 | } | |
1067 | mePtr = menuPtr->entries[index]; | |
1068 | if (mePtr->state == ENTRY_DISABLED) { | |
1069 | goto done; | |
1070 | } | |
1071 | Tcl_Preserve((ClientData) mePtr); | |
1072 | if (mePtr->type == TEAROFF_ENTRY) { | |
1073 | Tcl_DString ds; | |
1074 | Tcl_DStringInit(&ds); | |
1075 | Tcl_DStringAppend(&ds, "tkTearOffMenu ", -1); | |
1076 | Tcl_DStringAppend(&ds, Tk_PathName(menuPtr->tkwin), -1); | |
1077 | result = Tcl_Eval(interp, Tcl_DStringValue(&ds)); | |
1078 | Tcl_DStringFree(&ds); | |
1079 | } else if ((mePtr->type == CHECK_BUTTON_ENTRY) | |
1080 | && (mePtr->namePtr != NULL)) { | |
1081 | Tcl_Obj *valuePtr; | |
1082 | ||
1083 | if (mePtr->entryFlags & ENTRY_SELECTED) { | |
1084 | valuePtr = mePtr->offValuePtr; | |
1085 | } else { | |
1086 | valuePtr = mePtr->onValuePtr; | |
1087 | } | |
1088 | if (valuePtr == NULL) { | |
1089 | valuePtr = Tcl_NewObj(); | |
1090 | } | |
1091 | Tcl_IncrRefCount(valuePtr); | |
1092 | if (Tcl_ObjSetVar2(interp, mePtr->namePtr, NULL, valuePtr, | |
1093 | TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) { | |
1094 | result = TCL_ERROR; | |
1095 | } | |
1096 | Tcl_DecrRefCount(valuePtr); | |
1097 | } else if ((mePtr->type == RADIO_BUTTON_ENTRY) | |
1098 | && (mePtr->namePtr != NULL)) { | |
1099 | Tcl_Obj *valuePtr = mePtr->onValuePtr; | |
1100 | ||
1101 | if (valuePtr == NULL) { | |
1102 | valuePtr = Tcl_NewObj(); | |
1103 | } | |
1104 | Tcl_IncrRefCount(valuePtr); | |
1105 | if (Tcl_ObjSetVar2(interp, mePtr->namePtr, NULL, valuePtr, | |
1106 | TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) { | |
1107 | result = TCL_ERROR; | |
1108 | } | |
1109 | Tcl_DecrRefCount(valuePtr); | |
1110 | } | |
1111 | if ((result == TCL_OK) && (mePtr->commandPtr != NULL)) { | |
1112 | Tcl_Obj *commandPtr = mePtr->commandPtr; | |
1113 | ||
1114 | Tcl_IncrRefCount(commandPtr); | |
1115 | result = Tcl_EvalObjEx(interp, commandPtr, TCL_EVAL_GLOBAL); | |
1116 | Tcl_DecrRefCount(commandPtr); | |
1117 | } | |
1118 | Tcl_Release((ClientData) mePtr); | |
1119 | done: | |
1120 | return result; | |
1121 | } | |
1122 | ||
1123 | /* | |
1124 | *---------------------------------------------------------------------- | |
1125 | * | |
1126 | * DestroyMenuInstance -- | |
1127 | * | |
1128 | * This procedure is invoked by TkDestroyMenu | |
1129 | * to clean up the internal structure of a menu at a safe time | |
1130 | * (when no-one is using it anymore). Only takes care of one instance | |
1131 | * of the menu. | |
1132 | * | |
1133 | * Results: | |
1134 | * None. | |
1135 | * | |
1136 | * Side effects: | |
1137 | * Everything associated with the menu is freed up. | |
1138 | * | |
1139 | *---------------------------------------------------------------------- | |
1140 | */ | |
1141 | ||
1142 | static void | |
1143 | DestroyMenuInstance(menuPtr) | |
1144 | TkMenu *menuPtr; /* Info about menu widget. */ | |
1145 | { | |
1146 | int i; | |
1147 | TkMenu *menuInstancePtr; | |
1148 | TkMenuEntry *cascadePtr, *nextCascadePtr; | |
1149 | Tcl_Obj *newObjv[2]; | |
1150 | TkMenu *parentMasterMenuPtr; | |
1151 | TkMenuEntry *parentMasterEntryPtr; | |
1152 | ||
1153 | /* | |
1154 | * If the menu has any cascade menu entries pointing to it, the cascade | |
1155 | * entries need to be told that the menu is going away. We need to clear | |
1156 | * the menu ptr field in the menu reference at this point in the code | |
1157 | * so that everything else can forget about this menu properly. We also | |
1158 | * need to reset -menu field of all entries that are not master menus | |
1159 | * back to this entry name if this is a master menu pointed to by another | |
1160 | * master menu. If there is a clone menu that points to this menu, | |
1161 | * then this menu is itself a clone, so when this menu goes away, | |
1162 | * the -menu field of the pointing entry must be set back to this | |
1163 | * menu's master menu name so that later if another menu is created | |
1164 | * the cascade hierarchy can be maintained. | |
1165 | */ | |
1166 | ||
1167 | TkpDestroyMenu(menuPtr); | |
1168 | cascadePtr = menuPtr->menuRefPtr->parentEntryPtr; | |
1169 | menuPtr->menuRefPtr->menuPtr = NULL; | |
1170 | TkFreeMenuReferences(menuPtr->menuRefPtr); | |
1171 | ||
1172 | for (; cascadePtr != NULL; cascadePtr = nextCascadePtr) { | |
1173 | nextCascadePtr = cascadePtr->nextCascadePtr; | |
1174 | ||
1175 | if (menuPtr->masterMenuPtr != menuPtr) { | |
1176 | Tcl_Obj *menuNamePtr = Tcl_NewStringObj("-menu", -1); | |
1177 | ||
1178 | parentMasterMenuPtr = cascadePtr->menuPtr->masterMenuPtr; | |
1179 | parentMasterEntryPtr = | |
1180 | parentMasterMenuPtr->entries[cascadePtr->index]; | |
1181 | newObjv[0] = menuNamePtr; | |
1182 | newObjv[1] = parentMasterEntryPtr->namePtr; | |
1183 | /* | |
1184 | * It is possible that the menu info is out of sync, and | |
1185 | * these things point to NULL, so verify existence [Bug: 3402] | |
1186 | */ | |
1187 | if (newObjv[0] && newObjv[1]) { | |
1188 | Tcl_IncrRefCount(newObjv[0]); | |
1189 | Tcl_IncrRefCount(newObjv[1]); | |
1190 | ConfigureMenuEntry(cascadePtr, 2, newObjv); | |
1191 | Tcl_DecrRefCount(newObjv[0]); | |
1192 | Tcl_DecrRefCount(newObjv[1]); | |
1193 | } | |
1194 | } else { | |
1195 | ConfigureMenuEntry(cascadePtr, 0, (Tcl_Obj **) NULL); | |
1196 | } | |
1197 | } | |
1198 | ||
1199 | if (menuPtr->masterMenuPtr != menuPtr) { | |
1200 | for (menuInstancePtr = menuPtr->masterMenuPtr; | |
1201 | menuInstancePtr != NULL; | |
1202 | menuInstancePtr = menuInstancePtr->nextInstancePtr) { | |
1203 | if (menuInstancePtr->nextInstancePtr == menuPtr) { | |
1204 | menuInstancePtr->nextInstancePtr = | |
1205 | menuInstancePtr->nextInstancePtr->nextInstancePtr; | |
1206 | break; | |
1207 | } | |
1208 | } | |
1209 | } else if (menuPtr->nextInstancePtr != NULL) { | |
1210 | panic("Attempting to delete master menu when there are still clones."); | |
1211 | } | |
1212 | ||
1213 | /* | |
1214 | * Free up all the stuff that requires special handling, then | |
1215 | * let Tk_FreeConfigOptions handle all the standard option-related | |
1216 | * stuff. | |
1217 | */ | |
1218 | ||
1219 | for (i = menuPtr->numEntries; --i >= 0; ) { | |
1220 | /* | |
1221 | * As each menu entry is deleted from the end of the array of | |
1222 | * entries, decrement menuPtr->numEntries. Otherwise, the act of | |
1223 | * deleting menu entry i will dereference freed memory attempting | |
1224 | * to queue a redraw for menu entries (i+1)...numEntries. | |
1225 | */ | |
1226 | ||
1227 | DestroyMenuEntry((char *) menuPtr->entries[i]); | |
1228 | menuPtr->numEntries = i; | |
1229 | } | |
1230 | if (menuPtr->entries != NULL) { | |
1231 | ckfree((char *) menuPtr->entries); | |
1232 | } | |
1233 | TkMenuFreeDrawOptions(menuPtr); | |
1234 | Tk_FreeConfigOptions((char *) menuPtr, | |
1235 | menuPtr->optionTablesPtr->menuOptionTable, menuPtr->tkwin); | |
1236 | } | |
1237 | ||
1238 | /* | |
1239 | *---------------------------------------------------------------------- | |
1240 | * | |
1241 | * TkDestroyMenu -- | |
1242 | * | |
1243 | * This procedure is invoked by Tcl_EventuallyFree or Tcl_Release | |
1244 | * to clean up the internal structure of a menu at a safe time | |
1245 | * (when no-one is using it anymore). If called on a master instance, | |
1246 | * destroys all of the slave instances. If called on a non-master | |
1247 | * instance, just destroys that instance. | |
1248 | * | |
1249 | * Results: | |
1250 | * None. | |
1251 | * | |
1252 | * Side effects: | |
1253 | * Everything associated with the menu is freed up. | |
1254 | * | |
1255 | *---------------------------------------------------------------------- | |
1256 | */ | |
1257 | ||
1258 | void | |
1259 | TkDestroyMenu(menuPtr) | |
1260 | TkMenu *menuPtr; /* Info about menu widget. */ | |
1261 | { | |
1262 | TkMenu *menuInstancePtr; | |
1263 | TkMenuTopLevelList *topLevelListPtr, *nextTopLevelPtr; | |
1264 | ||
1265 | if (menuPtr->menuFlags & MENU_DELETION_PENDING) { | |
1266 | return; | |
1267 | } | |
1268 | ||
1269 | /* | |
1270 | * Now destroy all non-tearoff instances of this menu if this is a | |
1271 | * parent menu. Is this loop safe enough? Are there going to be | |
1272 | * destroy bindings on child menus which kill the parent? If not, | |
1273 | * we have to do a slightly more complex scheme. | |
1274 | */ | |
1275 | ||
1276 | if (menuPtr->masterMenuPtr == menuPtr) { | |
1277 | menuPtr->menuFlags |= MENU_DELETION_PENDING; | |
1278 | while (menuPtr->nextInstancePtr != NULL) { | |
1279 | menuInstancePtr = menuPtr->nextInstancePtr; | |
1280 | menuPtr->nextInstancePtr = menuInstancePtr->nextInstancePtr; | |
1281 | if (menuInstancePtr->tkwin != NULL) { | |
1282 | Tk_DestroyWindow(menuInstancePtr->tkwin); | |
1283 | } | |
1284 | } | |
1285 | menuPtr->menuFlags &= ~MENU_DELETION_PENDING; | |
1286 | } | |
1287 | ||
1288 | /* | |
1289 | * If any toplevel widgets have this menu as their menubar, | |
1290 | * the geometry of the window may have to be recalculated. | |
1291 | */ | |
1292 | ||
1293 | topLevelListPtr = menuPtr->menuRefPtr->topLevelListPtr; | |
1294 | while (topLevelListPtr != NULL) { | |
1295 | nextTopLevelPtr = topLevelListPtr->nextPtr; | |
1296 | TkpSetWindowMenuBar(topLevelListPtr->tkwin, NULL); | |
1297 | topLevelListPtr = nextTopLevelPtr; | |
1298 | } | |
1299 | DestroyMenuInstance(menuPtr); | |
1300 | } | |
1301 | ||
1302 | /* | |
1303 | *---------------------------------------------------------------------- | |
1304 | * | |
1305 | * UnhookCascadeEntry -- | |
1306 | * | |
1307 | * This entry is removed from the list of entries that point to the | |
1308 | * cascade menu. This is done in preparation for changing the menu | |
1309 | * that this entry points to. | |
1310 | * | |
1311 | * Results: | |
1312 | * None | |
1313 | * | |
1314 | * Side effects: | |
1315 | * The appropriate lists are modified. | |
1316 | * | |
1317 | *---------------------------------------------------------------------- | |
1318 | */ | |
1319 | ||
1320 | static void | |
1321 | UnhookCascadeEntry(mePtr) | |
1322 | TkMenuEntry *mePtr; /* The cascade entry we are removing | |
1323 | * from the cascade list. */ | |
1324 | { | |
1325 | TkMenuEntry *cascadeEntryPtr; | |
1326 | TkMenuEntry *prevCascadePtr; | |
1327 | TkMenuReferences *menuRefPtr; | |
1328 | ||
1329 | menuRefPtr = mePtr->childMenuRefPtr; | |
1330 | if (menuRefPtr == NULL) { | |
1331 | return; | |
1332 | } | |
1333 | ||
1334 | cascadeEntryPtr = menuRefPtr->parentEntryPtr; | |
1335 | if (cascadeEntryPtr == NULL) { | |
1336 | return; | |
1337 | } | |
1338 | ||
1339 | /* | |
1340 | * Singularly linked list deletion. The two special cases are | |
1341 | * 1. one element; 2. The first element is the one we want. | |
1342 | */ | |
1343 | ||
1344 | if (cascadeEntryPtr == mePtr) { | |
1345 | if (cascadeEntryPtr->nextCascadePtr == NULL) { | |
1346 | ||
1347 | /* | |
1348 | * This is the last menu entry which points to this | |
1349 | * menu, so we need to clear out the list pointer in the | |
1350 | * cascade itself. | |
1351 | */ | |
1352 | ||
1353 | menuRefPtr->parentEntryPtr = NULL; | |
1354 | TkFreeMenuReferences(menuRefPtr); | |
1355 | } else { | |
1356 | menuRefPtr->parentEntryPtr = cascadeEntryPtr->nextCascadePtr; | |
1357 | } | |
1358 | mePtr->nextCascadePtr = NULL; | |
1359 | } else { | |
1360 | for (prevCascadePtr = cascadeEntryPtr, | |
1361 | cascadeEntryPtr = cascadeEntryPtr->nextCascadePtr; | |
1362 | cascadeEntryPtr != NULL; | |
1363 | prevCascadePtr = cascadeEntryPtr, | |
1364 | cascadeEntryPtr = cascadeEntryPtr->nextCascadePtr) { | |
1365 | if (cascadeEntryPtr == mePtr){ | |
1366 | prevCascadePtr->nextCascadePtr = | |
1367 | cascadeEntryPtr->nextCascadePtr; | |
1368 | cascadeEntryPtr->nextCascadePtr = NULL; | |
1369 | break; | |
1370 | } | |
1371 | } | |
1372 | } | |
1373 | mePtr->childMenuRefPtr = NULL; | |
1374 | } | |
1375 | ||
1376 | /* | |
1377 | *---------------------------------------------------------------------- | |
1378 | * | |
1379 | * DestroyMenuEntry -- | |
1380 | * | |
1381 | * This procedure is invoked by Tcl_EventuallyFree or Tcl_Release | |
1382 | * to clean up the internal structure of a menu entry at a safe time | |
1383 | * (when no-one is using it anymore). | |
1384 | * | |
1385 | * Results: | |
1386 | * None. | |
1387 | * | |
1388 | * Side effects: | |
1389 | * Everything associated with the menu entry is freed. | |
1390 | * | |
1391 | *---------------------------------------------------------------------- | |
1392 | */ | |
1393 | ||
1394 | static void | |
1395 | DestroyMenuEntry(memPtr) | |
1396 | char *memPtr; /* Pointer to entry to be freed. */ | |
1397 | { | |
1398 | register TkMenuEntry *mePtr = (TkMenuEntry *) memPtr; | |
1399 | TkMenu *menuPtr = mePtr->menuPtr; | |
1400 | ||
1401 | if (menuPtr->postedCascade == mePtr) { | |
1402 | ||
1403 | /* | |
1404 | * Ignore errors while unposting the menu, since it's possible | |
1405 | * that the menu has already been deleted and the unpost will | |
1406 | * generate an error. | |
1407 | */ | |
1408 | ||
1409 | TkPostSubmenu(menuPtr->interp, menuPtr, (TkMenuEntry *) NULL); | |
1410 | } | |
1411 | ||
1412 | /* | |
1413 | * Free up all the stuff that requires special handling, then | |
1414 | * let Tk_FreeConfigOptions handle all the standard option-related | |
1415 | * stuff. | |
1416 | */ | |
1417 | ||
1418 | if (mePtr->type == CASCADE_ENTRY) { | |
1419 | UnhookCascadeEntry(mePtr); | |
1420 | } | |
1421 | if (mePtr->image != NULL) { | |
1422 | Tk_FreeImage(mePtr->image); | |
1423 | } | |
1424 | if (mePtr->selectImage != NULL) { | |
1425 | Tk_FreeImage(mePtr->selectImage); | |
1426 | } | |
1427 | if (((mePtr->type == CHECK_BUTTON_ENTRY) | |
1428 | || (mePtr->type == RADIO_BUTTON_ENTRY)) | |
1429 | && (mePtr->namePtr != NULL)) { | |
1430 | char *varName = Tcl_GetStringFromObj(mePtr->namePtr, NULL); | |
1431 | Tcl_UntraceVar(menuPtr->interp, varName, | |
1432 | TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, | |
1433 | MenuVarProc, (ClientData) mePtr); | |
1434 | } | |
1435 | TkpDestroyMenuEntry(mePtr); | |
1436 | TkMenuEntryFreeDrawOptions(mePtr); | |
1437 | Tk_FreeConfigOptions((char *) mePtr, mePtr->optionTable, menuPtr->tkwin); | |
1438 | ckfree((char *) mePtr); | |
1439 | } | |
1440 | ||
1441 | /* | |
1442 | *--------------------------------------------------------------------------- | |
1443 | * | |
1444 | * MenuWorldChanged -- | |
1445 | * | |
1446 | * This procedure is called when the world has changed in some | |
1447 | * way (such as the fonts in the system changing) and the widget needs | |
1448 | * to recompute all its graphics contexts and determine its new geometry. | |
1449 | * | |
1450 | * Results: | |
1451 | * None. | |
1452 | * | |
1453 | * Side effects: | |
1454 | * Menu will be relayed out and redisplayed. | |