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

Contents of /projs/trunk/shared_source/c_tk_base_7_5_w_mods/tkmenu.c

Parent Directory Parent Directory | Revision Log Revision Log


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

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25