]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * tkMenu.c -- | |
3 | * | |
4 | * This module implements menus for the Tk toolkit. The menus | |
5 | * support normal button entries, plus check buttons, radio | |
6 | * buttons, iconic forms of all of the above, and separator | |
7 | * entries. | |
8 | * | |
9 | * Copyright 1990-1992 Regents of the University of California. | |
10 | * Permission to use, copy, modify, and distribute this | |
11 | * software and its documentation for any purpose and without | |
12 | * fee is hereby granted, provided that the above copyright | |
13 | * notice appear in all copies. The University of California | |
14 | * makes no representations about the suitability of this | |
15 | * software for any purpose. It is provided "as is" without | |
16 | * express or implied warranty. | |
17 | */ | |
18 | ||
19 | #ifndef lint | |
20 | static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkMenu.c,v 1.37 92/08/24 09:24:04 ouster Exp $ SPRITE (Berkeley)"; | |
21 | #endif | |
22 | ||
23 | #include "tkconfig.h" | |
24 | #include "default.h" | |
25 | #include "tkint.h" | |
26 | ||
27 | /* | |
28 | * One of the following data structures is kept for each entry of each | |
29 | * menu managed by this file: | |
30 | */ | |
31 | ||
32 | typedef struct MenuEntry { | |
33 | int type; /* Type of menu entry; see below for | |
34 | * valid types. */ | |
35 | struct Menu *menuPtr; /* Menu with which this entry is associated. */ | |
36 | char *label; /* Main text label displayed in entry (NULL | |
37 | * if no label). Malloc'ed. */ | |
38 | int labelLength; /* Number of non-NULL characters in label. */ | |
39 | int underline; /* Index of character to underline. */ | |
40 | Pixmap bitmap; /* Bitmap to display in menu entry, or None. | |
41 | * If not None then label is ignored. */ | |
42 | char *accel; /* Accelerator string displayed at right | |
43 | * of menu entry. NULL means no such | |
44 | * accelerator. Malloc'ed. */ | |
45 | int accelLength; /* Number of non-NULL characters in | |
46 | * accelerator. */ | |
47 | ||
48 | /* | |
49 | * Information related to displaying entry: | |
50 | */ | |
51 | ||
52 | Tk_Uid state; /* State of button for display purposes: | |
53 | * normal, active, or disabled. */ | |
54 | int height; /* Number of pixels occupied by entry in | |
55 | * vertical dimension. */ | |
56 | int y; /* Y-coordinate of topmost pixel in entry. */ | |
57 | int selectorDiameter; /* Size of selector display, in pixels. */ | |
58 | Tk_3DBorder border; /* Structure used to draw background for | |
59 | * entry. NULL means use overall border | |
60 | * for menu. */ | |
61 | Tk_3DBorder activeBorder; /* Used to draw background and border when | |
62 | * element is active. NULL means use | |
63 | * activeBorder from menu. */ | |
64 | XFontStruct *fontPtr; /* Text font for menu entries. NULL means | |
65 | * use overall font for menu. */ | |
66 | GC textGC; /* GC for drawing text in entry. NULL means | |
67 | * use overall textGC for menu. */ | |
68 | GC activeGC; /* GC for drawing text in entry when active. | |
69 | * NULL means use overall activeGC for | |
70 | * menu. */ | |
71 | GC disabledGC; /* Used to produce disabled effect for entry. | |
72 | * NULL means use overall disabledGC from | |
73 | * menu structure. See comments for | |
74 | * disabledFg in menu structure for more | |
75 | * information. */ | |
76 | ||
77 | /* | |
78 | * Information used to implement this entry's action: | |
79 | */ | |
80 | ||
81 | char *command; /* Command to invoke when entry is invoked. | |
82 | * Malloc'ed. */ | |
83 | char *name; /* Name of variable (for check buttons and | |
84 | * radio buttons) or menu (for cascade | |
85 | * entries). Malloc'ed.*/ | |
86 | char *onValue; /* Value to store in variable when selected | |
87 | * (only for radio and check buttons). | |
88 | * Malloc'ed. */ | |
89 | char *offValue; /* Value to store in variable when not | |
90 | * selected (only for check buttons). | |
91 | * Malloc'ed. */ | |
92 | ||
93 | /* | |
94 | * Miscellaneous information: | |
95 | */ | |
96 | ||
97 | int flags; /* Various flags. See below for definitions. */ | |
98 | } MenuEntry; | |
99 | ||
100 | /* | |
101 | * Flag values defined for menu entries: | |
102 | * | |
103 | * ENTRY_SELECTED: Non-zero means this is a radio or check | |
104 | * button and that it should be drawn in | |
105 | * the "selected" state. | |
106 | * ENTRY_NEEDS_REDISPLAY: Non-zero means the entry should be redisplayed. | |
107 | */ | |
108 | ||
109 | #define ENTRY_SELECTED 1 | |
110 | #define ENTRY_NEEDS_REDISPLAY 4 | |
111 | ||
112 | /* | |
113 | * Types defined for MenuEntries: | |
114 | */ | |
115 | ||
116 | #define COMMAND_ENTRY 0 | |
117 | #define SEPARATOR_ENTRY 1 | |
118 | #define CHECK_BUTTON_ENTRY 2 | |
119 | #define RADIO_BUTTON_ENTRY 3 | |
120 | #define CASCADE_ENTRY 4 | |
121 | ||
122 | /* | |
123 | * Mask bits for above types: | |
124 | */ | |
125 | ||
126 | #define COMMAND_MASK TK_CONFIG_USER_BIT | |
127 | #define SEPARATOR_MASK (TK_CONFIG_USER_BIT << 1) | |
128 | #define CHECK_BUTTON_MASK (TK_CONFIG_USER_BIT << 2) | |
129 | #define RADIO_BUTTON_MASK (TK_CONFIG_USER_BIT << 3) | |
130 | #define CASCADE_MASK (TK_CONFIG_USER_BIT << 4) | |
131 | #define ALL_MASK (COMMAND_MASK | SEPARATOR_MASK \ | |
132 | | CHECK_BUTTON_MASK | RADIO_BUTTON_MASK | CASCADE_MASK) | |
133 | ||
134 | /* | |
135 | * Configuration specs for individual menu entries: | |
136 | */ | |
137 | ||
138 | static Tk_ConfigSpec entryConfigSpecs[] = { | |
139 | {TK_CONFIG_BORDER, "-activebackground", (char *) NULL, (char *) NULL, | |
140 | DEF_MENU_ENTRY_ACTIVE_BG, Tk_Offset(MenuEntry, activeBorder), | |
141 | COMMAND_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CASCADE_MASK | |
142 | |TK_CONFIG_NULL_OK}, | |
143 | {TK_CONFIG_STRING, "-accelerator", (char *) NULL, (char *) NULL, | |
144 | DEF_MENU_ENTRY_ACCELERATOR, Tk_Offset(MenuEntry, accel), | |
145 | COMMAND_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CASCADE_MASK}, | |
146 | {TK_CONFIG_BORDER, "-background", (char *) NULL, (char *) NULL, | |
147 | DEF_MENU_ENTRY_BG, Tk_Offset(MenuEntry, border), | |
148 | COMMAND_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CASCADE_MASK | |
149 | |TK_CONFIG_NULL_OK}, | |
150 | #if defined(USE_XPM3) | |
151 | {TK_CONFIG_PIXMAP, "-bitmap", (char *) NULL, (char *) NULL, | |
152 | DEF_MENU_ENTRY_BITMAP, Tk_Offset(MenuEntry, bitmap), | |
153 | COMMAND_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CASCADE_MASK | |
154 | |TK_CONFIG_NULL_OK}, | |
155 | #else | |
156 | {TK_CONFIG_BITMAP, "-bitmap", (char *) NULL, (char *) NULL, | |
157 | DEF_MENU_ENTRY_BITMAP, Tk_Offset(MenuEntry, bitmap), | |
158 | COMMAND_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CASCADE_MASK | |
159 | |TK_CONFIG_NULL_OK}, | |
160 | #endif | |
161 | {TK_CONFIG_STRING, "-command", (char *) NULL, (char *) NULL, | |
162 | DEF_MENU_ENTRY_COMMAND, Tk_Offset(MenuEntry, command), | |
163 | COMMAND_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CASCADE_MASK}, | |
164 | {TK_CONFIG_FONT, "-font", (char *) NULL, (char *) NULL, | |
165 | DEF_MENU_ENTRY_FONT, Tk_Offset(MenuEntry, fontPtr), | |
166 | COMMAND_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CASCADE_MASK | |
167 | |TK_CONFIG_NULL_OK}, | |
168 | {TK_CONFIG_STRING, "-label", (char *) NULL, (char *) NULL, | |
169 | DEF_MENU_ENTRY_LABEL, Tk_Offset(MenuEntry, label), | |
170 | COMMAND_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CASCADE_MASK}, | |
171 | {TK_CONFIG_STRING, "-menu", (char *) NULL, (char *) NULL, | |
172 | DEF_MENU_ENTRY_MENU, Tk_Offset(MenuEntry, name), CASCADE_MASK}, | |
173 | {TK_CONFIG_STRING, "-offvalue", (char *) NULL, (char *) NULL, | |
174 | DEF_MENU_ENTRY_OFF_VALUE, Tk_Offset(MenuEntry, offValue), | |
175 | CHECK_BUTTON_MASK}, | |
176 | {TK_CONFIG_UID, "-state", (char *) NULL, (char *) NULL, | |
177 | DEF_MENU_ENTRY_STATE, Tk_Offset(MenuEntry, state), | |
178 | COMMAND_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CASCADE_MASK | |
179 | |TK_CONFIG_DONT_SET_DEFAULT}, | |
180 | {TK_CONFIG_STRING, "-onvalue", (char *) NULL, (char *) NULL, | |
181 | DEF_MENU_ENTRY_ON_VALUE, Tk_Offset(MenuEntry, onValue), | |
182 | CHECK_BUTTON_MASK}, | |
183 | {TK_CONFIG_STRING, "-value", (char *) NULL, (char *) NULL, | |
184 | DEF_MENU_ENTRY_VALUE, Tk_Offset(MenuEntry, onValue), | |
185 | RADIO_BUTTON_MASK}, | |
186 | {TK_CONFIG_STRING, "-variable", (char *) NULL, (char *) NULL, | |
187 | DEF_MENU_ENTRY_CHECK_VARIABLE, Tk_Offset(MenuEntry, name), | |
188 | CHECK_BUTTON_MASK}, | |
189 | {TK_CONFIG_STRING, "-variable", (char *) NULL, (char *) NULL, | |
190 | DEF_MENU_ENTRY_RADIO_VARIABLE, Tk_Offset(MenuEntry, name), | |
191 | RADIO_BUTTON_MASK}, | |
192 | {TK_CONFIG_INT, "-underline", (char *) NULL, (char *) NULL, | |
193 | DEF_MENU_ENTRY_UNDERLINE, Tk_Offset(MenuEntry, underline), | |
194 | COMMAND_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CASCADE_MASK | |
195 | |TK_CONFIG_DONT_SET_DEFAULT}, | |
196 | {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL, | |
197 | (char *) NULL, 0, 0} | |
198 | }; | |
199 | ||
200 | /* | |
201 | * A data structure of the following type is kept for each | |
202 | * menu managed by this file: | |
203 | */ | |
204 | ||
205 | typedef struct Menu { | |
206 | Tk_Window tkwin; /* Window that embodies the pane. NULL | |
207 | * means that the window has been destroyed | |
208 | * but the data structures haven't yet been | |
209 | * cleaned up.*/ | |
210 | Tcl_Interp *interp; /* Interpreter associated with menu. */ | |
211 | MenuEntry **entries; /* Array of pointers to all the entries | |
212 | * in the menu. NULL means no entries. */ | |
213 | int numEntries; /* Number of elements in entries. */ | |
214 | int active; /* Index of active entry. -1 means | |
215 | * nothing active. */ | |
216 | Tk_Uid group; /* Used to allow event sharing between | |
217 | * related menus and menu buttons. */ | |
218 | ||
219 | /* | |
220 | * Information used when displaying widget: | |
221 | */ | |
222 | ||
223 | Tk_3DBorder border; /* Structure used to draw 3-D | |
224 | * border and background for menu. */ | |
225 | int borderWidth; /* Width of border around whole menu. */ | |
226 | Tk_3DBorder activeBorder; /* Used to draw background and border for | |
227 | * active element (if any). */ | |
228 | int activeBorderWidth; /* Width of border around active element. */ | |
229 | XFontStruct *fontPtr; /* Text font for menu entries. */ | |
230 | XColor *fg; /* Foreground color for entries. */ | |
231 | GC textGC; /* GC for drawing text and other features | |
232 | * of menu entries. */ | |
233 | XColor *disabledFg; /* Foreground color when disabled. NULL | |
234 | * means use normalFg with a 50% stipple | |
235 | * instead. */ | |
236 | Pixmap gray; /* Bitmap for drawing disabled entries in | |
237 | * a stippled fashion. None means not | |
238 | * allocated yet. */ | |
239 | GC disabledGC; /* Used to produce disabled effect. If | |
240 | * disabledFg isn't NULL, this GC is used to | |
241 | * draw text and icons for disabled entries. | |
242 | * Otherwise text and icons are drawn with | |
243 | * normalGC and this GC is used to stipple | |
244 | * background across them. */ | |
245 | XColor *activeFg; /* Foreground color for active entry. */ | |
246 | GC activeGC; /* GC for drawing active entry. */ | |
247 | XColor *selectorFg; /* Color for selectors in radio and check | |
248 | * button entries. */ | |
249 | GC selectorGC; /* For drawing selectors. */ | |
250 | int selectorSpace; /* Number of pixels to allow for displaying | |
251 | * selectors in menu entries (includes extra | |
252 | * space around selector). */ | |
253 | int labelWidth; /* Number of pixels to allow for displaying | |
254 | * labels in menu entries. */ | |
255 | ||
256 | /* | |
257 | * Miscellaneous information: | |
258 | */ | |
259 | ||
260 | Cursor cursor; /* Current cursor for window, or None. */ | |
261 | MenuEntry *postedCascade; /* Points to menu entry for cascaded | |
262 | * submenu that is currently posted, or | |
263 | * NULL if no submenu posted. */ | |
264 | int flags; /* Various flags; see below for | |
265 | * definitions. */ | |
266 | } Menu; | |
267 | ||
268 | /* | |
269 | * Flag bits for menus: | |
270 | * | |
271 | * REDRAW_PENDING: Non-zero means a DoWhenIdle handler | |
272 | * has already been queued to redraw | |
273 | * this window. | |
274 | * RESIZE_PENDING: Non-zero means a call to ComputeMenuGeometry | |
275 | * has already been scheduled. | |
276 | */ | |
277 | ||
278 | #define REDRAW_PENDING 1 | |
279 | #define RESIZE_PENDING 2 | |
280 | ||
281 | /* | |
282 | * Configuration specs valid for the menu as a whole: | |
283 | */ | |
284 | ||
285 | static Tk_ConfigSpec configSpecs[] = { | |
286 | {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground", | |
287 | DEF_MENU_ACTIVE_BG_COLOR, Tk_Offset(Menu, activeBorder), | |
288 | TK_CONFIG_COLOR_ONLY}, | |
289 | {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground", | |
290 | DEF_MENU_ACTIVE_BG_MONO, Tk_Offset(Menu, activeBorder), | |
291 | TK_CONFIG_MONO_ONLY}, | |
292 | {TK_CONFIG_PIXELS, "-activeborderwidth", "activeBorderWidth", "BorderWidth", | |
293 | DEF_MENU_ACTIVE_BORDER_WIDTH, Tk_Offset(Menu, activeBorderWidth), 0}, | |
294 | {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background", | |
295 | DEF_MENU_ACTIVE_FG_COLOR, Tk_Offset(Menu, activeFg), | |
296 | TK_CONFIG_COLOR_ONLY}, | |
297 | {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background", | |
298 | DEF_MENU_ACTIVE_FG_MONO, Tk_Offset(Menu, activeFg), | |
299 | TK_CONFIG_MONO_ONLY}, | |
300 | {TK_CONFIG_BORDER, "-background", "background", "Background", | |
301 | DEF_MENU_BG_COLOR, Tk_Offset(Menu, border), TK_CONFIG_COLOR_ONLY}, | |
302 | {TK_CONFIG_BORDER, "-background", "background", "Background", | |
303 | DEF_MENU_BG_MONO, Tk_Offset(Menu, border), TK_CONFIG_MONO_ONLY}, | |
304 | {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL, | |
305 | (char *) NULL, 0, 0}, | |
306 | {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL, | |
307 | (char *) NULL, 0, 0}, | |
308 | {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth", | |
309 | DEF_MENU_BORDER_WIDTH, Tk_Offset(Menu, borderWidth), 0}, | |
310 | {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor", | |
311 | DEF_MENU_CURSOR, Tk_Offset(Menu, cursor), TK_CONFIG_NULL_OK}, | |
312 | {TK_CONFIG_COLOR, "-disabledforeground", "disabledForeground", | |
313 | "DisabledForeground", DEF_MENU_DISABLED_FG_COLOR, | |
314 | Tk_Offset(Menu, disabledFg), TK_CONFIG_COLOR_ONLY|TK_CONFIG_NULL_OK}, | |
315 | {TK_CONFIG_COLOR, "-disabledforeground", "disabledForeground", | |
316 | "DisabledForeground", DEF_MENU_DISABLED_FG_MONO, | |
317 | Tk_Offset(Menu, disabledFg), TK_CONFIG_MONO_ONLY|TK_CONFIG_NULL_OK}, | |
318 | {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL, | |
319 | (char *) NULL, 0, 0}, | |
320 | {TK_CONFIG_FONT, "-font", "font", "Font", | |
321 | DEF_MENU_FONT, Tk_Offset(Menu, fontPtr), 0}, | |
322 | {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground", | |
323 | DEF_MENU_FG, Tk_Offset(Menu, fg), 0}, | |
324 | {TK_CONFIG_COLOR, "-selector", "selector", "Foreground", | |
325 | DEF_MENU_SELECTOR_COLOR, Tk_Offset(Menu, selectorFg), | |
326 | TK_CONFIG_COLOR_ONLY}, | |
327 | {TK_CONFIG_COLOR, "-selector", "selector", "Foreground", | |
328 | DEF_MENU_SELECTOR_MONO, Tk_Offset(Menu, selectorFg), | |
329 | TK_CONFIG_MONO_ONLY}, | |
330 | {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL, | |
331 | (char *) NULL, 0, 0} | |
332 | }; | |
333 | ||
334 | /* | |
335 | * Forward declarations for procedures defined later in this file: | |
336 | */ | |
337 | ||
338 | static int ActivateMenuEntry _ANSI_ARGS_((Menu *menuPtr, | |
339 | int index)); | |
340 | static void ComputeMenuGeometry _ANSI_ARGS_(( | |
341 | ClientData clientData)); | |
342 | static int ConfigureMenu _ANSI_ARGS_((Tcl_Interp *interp, | |
343 | Menu *menuPtr, int argc, char **argv, | |
344 | int flags)); | |
345 | static int ConfigureMenuEntry _ANSI_ARGS_((Tcl_Interp *interp, | |
346 | Menu *menuPtr, MenuEntry *mePtr, int index, | |
347 | int argc, char **argv, int flags)); | |
348 | static void DestroyMenu _ANSI_ARGS_((ClientData clientData)); | |
349 | static void DestroyMenuEntry _ANSI_ARGS_((ClientData clientData)); | |
350 | static void DisplayMenu _ANSI_ARGS_((ClientData clientData)); | |
351 | static void EventuallyRedrawMenu _ANSI_ARGS_((Menu *menuPtr, | |
352 | int index)); | |
353 | static int GetMenuIndex _ANSI_ARGS_((Tcl_Interp *interp, | |
354 | Menu *menuPtr, char *string, int *indexPtr)); | |
355 | static void MenuEventProc _ANSI_ARGS_((ClientData clientData, | |
356 | XEvent *eventPtr)); | |
357 | static char * MenuVarProc _ANSI_ARGS_((ClientData clientData, | |
358 | Tcl_Interp *interp, char *name1, char *name2, | |
359 | int flags)); | |
360 | static int MenuWidgetCmd _ANSI_ARGS_((ClientData clientData, | |
361 | Tcl_Interp *interp, int argc, char **argv)); | |
362 | static int PostSubmenu _ANSI_ARGS_((Tcl_Interp *interp, | |
363 | Menu *menuPtr, MenuEntry *mePtr)); | |
364 | \f | |
365 | /* | |
366 | *-------------------------------------------------------------- | |
367 | * | |
368 | * Tk_MenuCmd -- | |
369 | * | |
370 | * This procedure is invoked to process the "menu" Tcl | |
371 | * command. See the user documentation for details on | |
372 | * what it does. | |
373 | * | |
374 | * Results: | |
375 | * A standard Tcl result. | |
376 | * | |
377 | * Side effects: | |
378 | * See the user documentation. | |
379 | * | |
380 | *-------------------------------------------------------------- | |
381 | */ | |
382 | ||
383 | int | |
384 | Tk_MenuCmd(clientData, interp, argc, argv) | |
385 | ClientData clientData; /* Main window associated with | |
386 | * interpreter. */ | |
387 | Tcl_Interp *interp; /* Current interpreter. */ | |
388 | int argc; /* Number of arguments. */ | |
389 | char **argv; /* Argument strings. */ | |
390 | { | |
391 | Tk_Window tkwin = (Tk_Window) clientData; | |
392 | Tk_Window new; | |
393 | register Menu *menuPtr; | |
394 | XSetWindowAttributes atts; | |
395 | ||
396 | if (argc < 2) { | |
397 | Tcl_AppendResult(interp, "wrong # args: should be \"", | |
398 | argv[0], " pathName ?options?\"", (char *) NULL); | |
399 | return TCL_ERROR; | |
400 | } | |
401 | ||
402 | /* | |
403 | * Create the new window. Set override-redirect so the window | |
404 | * manager won't add a border or argue about placement, and set | |
405 | * save-under so that the window can pop up and down without a | |
406 | * lot of re-drawing. | |
407 | */ | |
408 | ||
409 | new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], ""); | |
410 | if (new == NULL) { | |
411 | return TCL_ERROR; | |
412 | } | |
413 | atts.override_redirect = True; | |
414 | atts.save_under = True; | |
415 | Tk_ChangeWindowAttributes(new, CWOverrideRedirect|CWSaveUnder, &atts); | |
416 | ||
417 | /* | |
418 | * Initialize the data structure for the menu. | |
419 | */ | |
420 | ||
421 | menuPtr = (Menu *) ckalloc(sizeof(Menu)); | |
422 | menuPtr->tkwin = new; | |
423 | menuPtr->interp = interp; | |
424 | menuPtr->entries = NULL; | |
425 | menuPtr->numEntries = 0; | |
426 | menuPtr->active = -1; | |
427 | menuPtr->group = NULL; | |
428 | menuPtr->border = NULL; | |
429 | menuPtr->activeBorder = NULL; | |
430 | menuPtr->fontPtr = NULL; | |
431 | menuPtr->fg = NULL; | |
432 | menuPtr->textGC = None; | |
433 | menuPtr->disabledFg = NULL; | |
434 | menuPtr->gray = None; | |
435 | menuPtr->disabledGC = None; | |
436 | menuPtr->activeFg = NULL; | |
437 | menuPtr->activeGC = None; | |
438 | menuPtr->selectorFg = NULL; | |
439 | menuPtr->selectorGC = None; | |
440 | menuPtr->cursor = None; | |
441 | menuPtr->postedCascade = NULL; | |
442 | menuPtr->flags = 0; | |
443 | ||
444 | Tk_SetClass(new, "Menu"); | |
445 | Tk_CreateEventHandler(menuPtr->tkwin, ExposureMask|StructureNotifyMask, | |
446 | MenuEventProc, (ClientData) menuPtr); | |
447 | Tcl_CreateCommand(interp, Tk_PathName(menuPtr->tkwin), MenuWidgetCmd, | |
448 | (ClientData) menuPtr, (void (*)()) NULL); | |
449 | if (ConfigureMenu(interp, menuPtr, argc-2, argv+2, 0) != TCL_OK) { | |
450 | goto error; | |
451 | } | |
452 | ||
453 | interp->result = Tk_PathName(menuPtr->tkwin); | |
454 | return TCL_OK; | |
455 | ||
456 | error: | |
457 | Tk_DestroyWindow(menuPtr->tkwin); | |
458 | return TCL_ERROR; | |
459 | } | |
460 | \f | |
461 | /* | |
462 | *-------------------------------------------------------------- | |
463 | * | |
464 | * MenuWidgetCmd -- | |
465 | * | |
466 | * This procedure is invoked to process the Tcl command | |
467 | * that corresponds to a widget managed by this module. | |
468 | * See the user documentation for details on what it does. | |
469 | * | |
470 | * Results: | |
471 | * A standard Tcl result. | |
472 | * | |
473 | * Side effects: | |
474 | * See the user documentation. | |
475 | * | |
476 | *-------------------------------------------------------------- | |
477 | */ | |
478 | ||
479 | static int | |
480 | MenuWidgetCmd(clientData, interp, argc, argv) | |
481 | ClientData clientData; /* Information about menu widget. */ | |
482 | Tcl_Interp *interp; /* Current interpreter. */ | |
483 | int argc; /* Number of arguments. */ | |
484 | char **argv; /* Argument strings. */ | |
485 | { | |
486 | register Menu *menuPtr = (Menu *) clientData; | |
487 | register MenuEntry *mePtr; | |
488 | int result = TCL_OK; | |
489 | int length, type; | |
490 | char c; | |
491 | ||
492 | if (argc < 2) { | |
493 | Tcl_AppendResult(interp, "wrong # args: should be \"", | |
494 | argv[0], " option ?arg arg ...?\"", (char *) NULL); | |
495 | return TCL_ERROR; | |
496 | } | |
497 | Tk_Preserve((ClientData) menuPtr); | |
498 | c = argv[1][0]; | |
499 | length = strlen(argv[1]); | |
500 | if ((c == 'a') && (strncmp(argv[1], "activate", length) == 0) | |
501 | && (length >= 2)) { | |
502 | int index; | |
503 | ||
504 | if (argc != 3) { | |
505 | Tcl_AppendResult(interp, "wrong # args: should be \"", | |
506 | argv[0], " activate index\"", (char *) NULL); | |
507 | goto error; | |
508 | } | |
509 | if (GetMenuIndex(interp, menuPtr, argv[2], &index) != TCL_OK) { | |
510 | goto error; | |
511 | } | |
512 | if (menuPtr->active == index) { | |
513 | goto done; | |
514 | } | |
515 | if (index >= 0) { | |
516 | if ((menuPtr->entries[index]->type == SEPARATOR_ENTRY) | |
517 | || (menuPtr->entries[index]->state == tkDisabledUid)) { | |
518 | index = -1; | |
519 | } | |
520 | } | |
521 | result = ActivateMenuEntry(menuPtr, index); | |
522 | } else if ((c == 'a') && (strncmp(argv[1], "add", length) == 0) | |
523 | && (length >= 2)) { | |
524 | MenuEntry **newEntries; | |
525 | ||
526 | if (argc < 3) { | |
527 | Tcl_AppendResult(interp, "wrong # args: should be \"", | |
528 | argv[0], " add type ?options?\"", (char *) NULL); | |
529 | goto error; | |
530 | } | |
531 | ||
532 | /* | |
533 | * Figure out the type of the new entry. | |
534 | */ | |
535 | ||
536 | c = argv[2][0]; | |
537 | length = strlen(argv[2]); | |
538 | if ((c == 'c') && (strncmp(argv[2], "cascade", length) == 0) | |
539 | && (length >= 2)) { | |
540 | type = CASCADE_ENTRY; | |
541 | } else if ((c == 'c') && (strncmp(argv[2], "checkbutton", length) == 0) | |
542 | && (length >= 2)) { | |
543 | type = CHECK_BUTTON_ENTRY; | |
544 | } else if ((c == 'c') && (strncmp(argv[2], "command", length) == 0) | |
545 | && (length >= 2)) { | |
546 | type = COMMAND_ENTRY; | |
547 | } else if ((c == 'r') | |
548 | && (strncmp(argv[2], "radiobutton", length) == 0)) { | |
549 | type = RADIO_BUTTON_ENTRY; | |
550 | } else if ((c == 's') | |
551 | && (strncmp(argv[2], "separator", length) == 0)) { | |
552 | type = SEPARATOR_ENTRY; | |
553 | } else { | |
554 | Tcl_AppendResult(interp, "bad menu entry type \"", | |
555 | argv[2], "\": must be cascade, checkbutton, ", | |
556 | "command, radiobutton, or separator", (char *) NULL); | |
557 | goto error; | |
558 | } | |
559 | ||
560 | /* | |
561 | * Add a new entry to the end of the menu's array of entries, | |
562 | * and process options for it. | |
563 | */ | |
564 | ||
565 | mePtr = (MenuEntry *) ckalloc(sizeof(MenuEntry)); | |
566 | newEntries = (MenuEntry **) ckalloc((unsigned) | |
567 | ((menuPtr->numEntries+1)*sizeof(MenuEntry *))); | |
568 | if (menuPtr->numEntries != 0) { | |
569 | memcpy((VOID *) newEntries, (VOID *) menuPtr->entries, | |
570 | menuPtr->numEntries*sizeof(MenuEntry *)); | |
571 | ckfree((char *) menuPtr->entries); | |
572 | } | |
573 | menuPtr->entries = newEntries; | |
574 | menuPtr->entries[menuPtr->numEntries] = mePtr; | |
575 | menuPtr->numEntries++; | |
576 | mePtr->type = type; | |
577 | mePtr->menuPtr = menuPtr; | |
578 | mePtr->label = NULL; | |
579 | mePtr->underline = -1; | |
580 | mePtr->bitmap = None; | |
581 | mePtr->accel = NULL; | |
582 | mePtr->state = tkNormalUid; | |
583 | mePtr->border = NULL; | |
584 | mePtr->activeBorder = NULL; | |
585 | mePtr->fontPtr = NULL; | |
586 | mePtr->textGC = None; | |
587 | mePtr->activeGC = None; | |
588 | mePtr->disabledGC = None; | |
589 | mePtr->command = NULL; | |
590 | mePtr->name = NULL; | |
591 | mePtr->onValue = NULL; | |
592 | mePtr->offValue = NULL; | |
593 | mePtr->flags = 0; | |
594 | if (ConfigureMenuEntry(interp, menuPtr, mePtr, menuPtr->numEntries-1, | |
595 | argc-3, argv+3, 0) != TCL_OK) { | |
596 | DestroyMenuEntry((ClientData) mePtr); | |
597 | menuPtr->numEntries--; | |
598 | goto error; | |
599 | } | |
600 | } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) { | |
601 | if (argc == 2) { | |
602 | result = Tk_ConfigureInfo(interp, menuPtr->tkwin, configSpecs, | |
603 | (char *) menuPtr, (char *) NULL, 0); | |
604 | } else if (argc == 3) { | |
605 | result = Tk_ConfigureInfo(interp, menuPtr->tkwin, configSpecs, | |
606 | (char *) menuPtr, argv[2], 0); | |
607 | } else { | |
608 | result = ConfigureMenu(interp, menuPtr, argc-2, argv+2, | |
609 | TK_CONFIG_ARGV_ONLY); | |
610 | } | |
611 | } else if ((c == 'd') && (strncmp(argv[1], "delete", length) == 0) | |
612 | && (length >= 2)) { | |
613 | int index, i; | |
614 | ||
615 | if (argc != 3) { | |
616 | Tcl_AppendResult(interp, "wrong # args: should be \"", | |
617 | argv[0], " delete index\"", (char *) NULL); | |
618 | goto error; | |
619 | } | |
620 | if (GetMenuIndex(interp, menuPtr, argv[2], &index) != TCL_OK) { | |
621 | goto error; | |
622 | } | |
623 | if (index < 0) { | |
624 | goto done; | |
625 | } | |
626 | Tk_EventuallyFree((ClientData) menuPtr->entries[index], | |
627 | DestroyMenuEntry); | |
628 | for (i = index; i < menuPtr->numEntries-1; i++) { | |
629 | menuPtr->entries[i] = menuPtr->entries[i+1]; | |
630 | } | |
631 | menuPtr->numEntries -= 1; | |
632 | if (menuPtr->active == index) { | |
633 | menuPtr->active = -1; | |
634 | } else if (menuPtr->active > index) { | |
635 | menuPtr->active -= 1; | |
636 | } | |
637 | if (!(menuPtr->flags & RESIZE_PENDING)) { | |
638 | menuPtr->flags |= RESIZE_PENDING; | |
639 | Tk_DoWhenIdle(ComputeMenuGeometry, (ClientData) menuPtr); | |
640 | } | |
641 | } else if ((c == 'd') && (strncmp(argv[1], "disable", length) == 0) | |
642 | && (length >= 2)) { | |
643 | int index; | |
644 | ||
645 | if (argc != 3) { | |
646 | Tcl_AppendResult(interp, "wrong # args: should be \"", | |
647 | argv[0], " disable index\"", (char *) NULL); | |
648 | goto error; | |
649 | } | |
650 | if (GetMenuIndex(interp, menuPtr, argv[2], &index) != TCL_OK) { | |
651 | goto error; | |
652 | } | |
653 | if (index < 0) { | |
654 | goto done; | |
655 | } | |
656 | menuPtr->entries[index]->state = tkDisabledUid; | |
657 | if (menuPtr->active == index) { | |
658 | menuPtr->active = -1; | |
659 | } | |
660 | EventuallyRedrawMenu(menuPtr, index); | |
661 | } else if ((c == 'e') && (length >= 3) | |
662 | && (strncmp(argv[1], "enable", length) == 0)) { | |
663 | int index; | |
664 | ||
665 | if (argc != 3) { | |
666 | Tcl_AppendResult(interp, "wrong # args: should be \"", | |
667 | argv[0], " enable index\"", (char *) NULL); | |
668 | goto error; | |
669 | } | |
670 | if (GetMenuIndex(interp, menuPtr, argv[2], &index) != TCL_OK) { | |
671 | goto error; | |
672 | } | |
673 | if (index < 0) { | |
674 | goto done; | |
675 | } | |
676 | menuPtr->entries[index]->state = tkNormalUid; | |
677 | EventuallyRedrawMenu(menuPtr, index); | |
678 | } else if ((c == 'e') && (length >= 3) | |
679 | && (strncmp(argv[1], "entryconfigure", length) == 0)) { | |
680 | int index; | |
681 | ||
682 | if (argc < 3) { | |
683 | Tcl_AppendResult(interp, "wrong # args: should be \"", | |
684 | argv[0], " entryconfigure index ?option value ...?\"", | |
685 | (char *) NULL); | |
686 | goto error; | |
687 | } | |
688 | if (GetMenuIndex(interp, menuPtr, argv[2], &index) != TCL_OK) { | |
689 | goto error; | |
690 | } | |
691 | if (index < 0) { | |
692 | goto done; | |
693 | } | |
694 | mePtr = menuPtr->entries[index]; | |
695 | Tk_Preserve((ClientData) mePtr); | |
696 | if (argc == 3) { | |
697 | result = Tk_ConfigureInfo(interp, menuPtr->tkwin, entryConfigSpecs, | |
698 | (char *) mePtr, (char *) NULL, | |
699 | COMMAND_MASK << mePtr->type); | |
700 | } else if (argc == 4) { | |
701 | result = Tk_ConfigureInfo(interp, menuPtr->tkwin, entryConfigSpecs, | |
702 | (char *) mePtr, argv[3], COMMAND_MASK << mePtr->type); | |
703 | } else { | |
704 | result = ConfigureMenuEntry(interp, menuPtr, mePtr, index, argc-3, | |
705 | argv+3, TK_CONFIG_ARGV_ONLY | COMMAND_MASK << mePtr->type); | |
706 | } | |
707 | Tk_Release((ClientData) mePtr); | |
708 | } else if ((c == 'i') && (strncmp(argv[1], "index", length) == 0) | |
709 | && (length >= 3)) { | |
710 | int index; | |
711 | ||
712 | if (argc != 3) { | |
713 | Tcl_AppendResult(interp, "wrong # args: should be \"", | |
714 | argv[0], " index string\"", (char *) NULL); | |
715 | goto error; | |
716 | } | |
717 | if (GetMenuIndex(interp, menuPtr, argv[2], &index) != TCL_OK) { | |
718 | goto error; | |
719 | } | |
720 | if (index < 0) { | |
721 | interp->result = "none"; | |
722 | } else { | |
723 | sprintf(interp->result, "%d", index); | |
724 | } | |
725 | } else if ((c == 'i') && (strncmp(argv[1], "invoke", length) == 0) | |
726 | && (length >= 3)) { | |
727 | int index; | |
728 | ||
729 | if (argc != 3) { | |
730 | Tcl_AppendResult(interp, "wrong # args: should be \"", | |
731 | argv[0], " invoke index\"", (char *) NULL); | |
732 | goto error; | |
733 | } | |
734 | if (GetMenuIndex(interp, menuPtr, argv[2], &index) != TCL_OK) { | |
735 | goto error; | |
736 | } | |
737 | if (index < 0) { | |
738 | goto done; | |
739 | } | |
740 | mePtr = menuPtr->entries[index]; | |
741 | if (mePtr->state == tkDisabledUid) { | |
742 | goto done; | |
743 | } | |
744 | Tk_Preserve((ClientData) mePtr); | |
745 | if (mePtr->type == CHECK_BUTTON_ENTRY) { | |
746 | if (mePtr->flags & ENTRY_SELECTED) { | |
747 | Tcl_SetVar(interp, mePtr->name, mePtr->offValue, | |
748 | TCL_GLOBAL_ONLY); | |
749 | } else { | |
750 | Tcl_SetVar(interp, mePtr->name, mePtr->onValue, | |
751 | TCL_GLOBAL_ONLY); | |
752 | } | |
753 | } else if (mePtr->type == RADIO_BUTTON_ENTRY) { | |
754 | Tcl_SetVar(interp, mePtr->name, mePtr->onValue, TCL_GLOBAL_ONLY); | |
755 | } | |
756 | if (mePtr->command != NULL) { | |
757 | result = Tcl_GlobalEval(interp, mePtr->command); | |
758 | } | |
759 | Tk_Release((ClientData) mePtr); | |
760 | } else if ((c == 'p') && (strncmp(argv[1], "post", length) == 0)) { | |
761 | int x, y, tmp; | |
762 | Tk_Uid group; | |
763 | ||
764 | if ((argc != 4) && (argc != 5)) { | |
765 | Tcl_AppendResult(interp, "wrong # args: should be \"", | |
766 | argv[0], " post x y ?group?\"", (char *) NULL); | |
767 | goto error; | |
768 | } | |
769 | if ((Tcl_GetInt(interp, argv[2], &x) != TCL_OK) | |
770 | || (Tcl_GetInt(interp, argv[3], &y) != TCL_OK)) { | |
771 | goto error; | |
772 | } | |
773 | if (argc == 5) { | |
774 | group = Tk_GetUid(argv[4]); | |
775 | } else { | |
776 | group = Tk_GetUid("default"); | |
777 | } | |
778 | ||
779 | /* | |
780 | * Adjust the position of the menu if necessary to keep it | |
781 | * on-screen. | |
782 | */ | |
783 | ||
784 | tmp = WidthOfScreen(Tk_Screen(menuPtr->tkwin)) | |
785 | - Tk_Width(menuPtr->tkwin); | |
786 | if (x > tmp) { | |
787 | x = tmp; | |
788 | } | |
789 | if (x < 0) { | |
790 | x = 0; | |
791 | } | |
792 | tmp = HeightOfScreen(Tk_Screen(menuPtr->tkwin)) | |
793 | - Tk_Height(menuPtr->tkwin); | |
794 | if (y > tmp) { | |
795 | y = tmp; | |
796 | } | |
797 | if (y < 0) { | |
798 | y = 0; | |
799 | } | |
800 | if ((x != Tk_X(menuPtr->tkwin)) || (y != Tk_Y(menuPtr->tkwin))) { | |
801 | Tk_MoveWindow(menuPtr->tkwin, x, y); | |
802 | } | |
803 | if (Tk_IsMapped(menuPtr->tkwin)) { | |
804 | if (group != menuPtr->group) { | |
805 | Tk_UnshareEvents(menuPtr->tkwin, menuPtr->group); | |
806 | Tk_ShareEvents(menuPtr->tkwin, group); | |
807 | } | |
808 | } else { | |
809 | Tk_ShareEvents(menuPtr->tkwin, group); | |
810 | Tk_MapWindow(menuPtr->tkwin); | |
811 | result = ActivateMenuEntry(menuPtr, -1); | |
812 | } | |
813 | XRaiseWindow(Tk_Display(menuPtr->tkwin), Tk_WindowId(menuPtr->tkwin)); | |
814 | menuPtr->group = group; | |
815 | } else if ((c == 'u') && (strncmp(argv[1], "unpost", length) == 0)) { | |
816 | if (argc != 2) { | |
817 | Tcl_AppendResult(interp, "wrong # args: should be \"", | |
818 | argv[0], " unpost\"", (char *) NULL); | |
819 | goto error; | |
820 | } | |
821 | Tk_UnshareEvents(menuPtr->tkwin, menuPtr->group); | |
822 | Tk_UnmapWindow(menuPtr->tkwin); | |
823 | result = ActivateMenuEntry(menuPtr, -1); | |
824 | if (result == TCL_OK) { | |
825 | result = PostSubmenu(interp, menuPtr, (MenuEntry *) NULL); | |
826 | } | |
827 | } else { | |
828 | Tcl_AppendResult(interp, "bad option \"", argv[1], | |
829 | "\": must be activate, add, configure, delete, disable, ", | |
830 | "enable, entryconfigure, index, invoke, post, ", | |
831 | "or unpost", (char *) NULL); | |
832 | goto error; | |
833 | } | |
834 | done: | |
835 | Tk_Release((ClientData) menuPtr); | |
836 | return result; | |
837 | ||
838 | error: | |
839 | Tk_Release((ClientData) menuPtr); | |
840 | return TCL_ERROR; | |
841 | } | |
842 | \f | |
843 | /* | |
844 | *---------------------------------------------------------------------- | |
845 | * | |
846 | * DestroyMenu -- | |
847 | * | |
848 | * This procedure is invoked by Tk_EventuallyFree or Tk_Release | |
849 | * to clean up the internal structure of a menu at a safe time | |
850 | * (when no-one is using it anymore). | |
851 | * | |
852 | * Results: | |
853 | * None. | |
854 | * | |
855 | * Side effects: | |
856 | * Everything associated with the menu is freed up. | |
857 | * | |
858 | *---------------------------------------------------------------------- | |
859 | */ | |
860 | ||
861 | static void | |
862 | DestroyMenu(clientData) | |
863 | ClientData clientData; /* Info about menu widget. */ | |
864 | { | |
865 | register Menu *menuPtr = (Menu *) clientData; | |
866 | int i; | |
867 | ||
868 | for (i = 0; i < menuPtr->numEntries; i++) { | |
869 | DestroyMenuEntry((ClientData) menuPtr->entries[i]); | |
870 | } | |
871 | if (menuPtr->entries != NULL) { | |
872 | ckfree((char *) menuPtr->entries); | |
873 | } | |
874 | if (menuPtr->border != NULL) { | |
875 | Tk_Free3DBorder(menuPtr->border); | |
876 | } | |
877 | if (menuPtr->activeBorder != NULL) { | |
878 | Tk_Free3DBorder(menuPtr->activeBorder); | |
879 | } | |
880 | if (menuPtr->fontPtr != NULL) { | |
881 | Tk_FreeFontStruct(menuPtr->fontPtr); | |
882 | } | |
883 | if (menuPtr->fg != NULL) { | |
884 | Tk_FreeColor(menuPtr->fg); | |
885 | } | |
886 | if (menuPtr->textGC != None) { | |
887 | Tk_FreeGC(menuPtr->textGC); | |
888 | } | |
889 | if (menuPtr->disabledFg != NULL) { | |
890 | Tk_FreeColor(menuPtr->disabledFg); | |
891 | } | |
892 | if (menuPtr->gray != None) { | |
893 | Tk_FreeBitmap(menuPtr->gray); | |
894 | } | |
895 | if (menuPtr->disabledGC != None) { | |
896 | Tk_FreeGC(menuPtr->disabledGC); | |
897 | } | |
898 | if (menuPtr->activeFg != NULL) { | |
899 | Tk_FreeColor(menuPtr->activeFg); | |
900 | } | |
901 | if (menuPtr->activeGC != None) { | |
902 | Tk_FreeGC(menuPtr->activeGC); | |
903 | } | |
904 | if (menuPtr->selectorFg != NULL) { | |
905 | Tk_FreeColor(menuPtr->selectorFg); | |
906 | } | |
907 | if (menuPtr->selectorGC != None) { | |
908 | Tk_FreeGC(menuPtr->selectorGC); | |
909 | } | |
910 | if (menuPtr->cursor != None) { | |
911 | Tk_FreeCursor(menuPtr->cursor); | |
912 | } | |
913 | ckfree((char *) menuPtr); | |
914 | } | |
915 | \f | |
916 | /* | |
917 | *---------------------------------------------------------------------- | |
918 | * | |
919 | * DestroyMenuEntry -- | |
920 | * | |
921 | * This procedure is invoked by Tk_EventuallyFree or Tk_Release | |
922 | * to clean up the internal structure of a menu entry at a safe time | |
923 | * (when no-one is using it anymore). | |
924 | * | |
925 | * Results: | |
926 | * None. | |
927 | * | |
928 | * Side effects: | |
929 | * Everything associated with the menu entry is freed up. | |
930 | * | |
931 | *---------------------------------------------------------------------- | |
932 | */ | |
933 | ||
934 | static void | |
935 | DestroyMenuEntry(clientData) | |
936 | ClientData clientData; /* Pointer to entry to be freed. */ | |
937 | { | |
938 | register MenuEntry *mePtr = (MenuEntry *) clientData; | |
939 | Menu *menuPtr = mePtr->menuPtr; | |
940 | ||
941 | if (mePtr->name != NULL) { | |
942 | Tcl_UntraceVar(menuPtr->interp, mePtr->name, | |
943 | TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, | |
944 | MenuVarProc, (ClientData) mePtr); | |
945 | } | |
946 | if (menuPtr->postedCascade == mePtr) { | |
947 | if (PostSubmenu(menuPtr->interp, menuPtr, (MenuEntry *) NULL) | |
948 | != TCL_OK) { | |
949 | TkBindError(menuPtr->interp); | |
950 | } | |
951 | } | |
952 | if (mePtr->label != NULL) { | |
953 | ckfree(mePtr->label); | |
954 | } | |
955 | if (mePtr->bitmap != None) { | |
956 | #if defined(USE_XPM3) | |
957 | Tk_FreePixmap(mePtr->bitmap); | |
958 | #else | |
959 | Tk_FreeBitmap(mePtr->bitmap); | |
960 | #endif | |
961 | } | |
962 | if (mePtr->accel != NULL) { | |
963 | ckfree(mePtr->accel); | |
964 | } | |
965 | if (mePtr->border != NULL) { | |
966 | Tk_Free3DBorder(mePtr->border); | |
967 | } | |
968 | if (mePtr->activeBorder != NULL) { | |
969 | Tk_Free3DBorder(mePtr->activeBorder); | |
970 | } | |
971 | if (mePtr->fontPtr != NULL) { | |
972 | Tk_FreeFontStruct(mePtr->fontPtr); | |
973 | } | |
974 | if (mePtr->textGC != NULL) { | |
975 | Tk_FreeGC(mePtr->textGC); | |
976 | } | |
977 | if (mePtr->activeGC != NULL) { | |
978 | Tk_FreeGC(mePtr->activeGC); | |
979 | } | |
980 | if (mePtr->disabledGC != NULL) { | |
981 | Tk_FreeGC(mePtr->disabledGC); | |
982 | } | |
983 | if (mePtr->command != NULL) { | |
984 | ckfree(mePtr->command); | |
985 | } | |
986 | if (mePtr->name != NULL) { | |
987 | ckfree(mePtr->name); | |
988 | } | |
989 | if (mePtr->onValue != NULL) { | |
990 | ckfree(mePtr->onValue); | |
991 | } | |
992 | if (mePtr->offValue != NULL) { | |
993 | ckfree(mePtr->offValue); | |
994 | } | |
995 | ckfree((char *) mePtr); | |
996 | } | |
997 | \f | |
998 | /* | |
999 | *---------------------------------------------------------------------- | |
1000 | * | |
1001 | * ConfigureMenu -- | |
1002 | * | |
1003 | * This procedure is called to process an argv/argc list, plus | |
1004 | * the Tk option database, in order to configure (or | |
1005 | * reconfigure) a menu widget. | |
1006 | * | |
1007 | * Results: | |
1008 | * The return value is a standard Tcl result. If TCL_ERROR is | |
1009 | * returned, then interp->result contains an error message. | |
1010 | * | |
1011 | * Side effects: | |
1012 | * Configuration information, such as colors, font, etc. get set | |
1013 | * for menuPtr; old resources get freed, if there were any. | |
1014 | * | |
1015 | *---------------------------------------------------------------------- | |
1016 | */ | |
1017 | ||
1018 | static int | |
1019 | ConfigureMenu(interp, menuPtr, argc, argv, flags) | |
1020 | Tcl_Interp *interp; /* Used for error reporting. */ | |
1021 | register Menu *menuPtr; /* Information about widget; may or may | |
1022 | * not already have values for some fields. */ | |
1023 | int argc; /* Number of valid entries in argv. */ | |
1024 | char **argv; /* Arguments. */ | |
1025 | int flags; /* Flags to pass to Tk_ConfigureWidget. */ | |
1026 | { | |
1027 | XGCValues gcValues; | |
1028 | GC newGC; | |
1029 | unsigned long mask; | |
1030 | int i; | |
1031 | ||
1032 | if (Tk_ConfigureWidget(interp, menuPtr->tkwin, configSpecs, | |
1033 | argc, argv, (char *) menuPtr, flags) != TCL_OK) { | |
1034 | return TCL_ERROR; | |
1035 | } | |
1036 | ||
1037 | /* | |
1038 | * A few options need special processing, such as setting the | |
1039 | * background from a 3-D border, or filling in complicated | |
1040 | * defaults that couldn't be specified to Tk_ConfigureWidget. | |
1041 | */ | |
1042 | ||
1043 | Tk_SetBackgroundFromBorder(menuPtr->tkwin, menuPtr->border); | |
1044 | ||
1045 | gcValues.font = menuPtr->fontPtr->fid; | |
1046 | gcValues.foreground = menuPtr->fg->pixel; | |
1047 | gcValues.background = Tk_3DBorderColor(menuPtr->border)->pixel; | |
1048 | newGC = Tk_GetGC(menuPtr->tkwin, GCForeground|GCBackground|GCFont, | |
1049 | &gcValues); | |
1050 | if (menuPtr->textGC != None) { | |
1051 | Tk_FreeGC(menuPtr->textGC); | |
1052 | } | |
1053 | menuPtr->textGC = newGC; | |
1054 | ||
1055 | if (menuPtr->disabledFg != NULL) { | |
1056 | gcValues.foreground = menuPtr->disabledFg->pixel; | |
1057 | mask = GCForeground|GCBackground|GCFont; | |
1058 | } else { | |
1059 | gcValues.foreground = gcValues.background; | |
1060 | if (menuPtr->gray == None) { | |
1061 | menuPtr->gray = Tk_GetBitmap(interp, menuPtr->tkwin, | |
1062 | Tk_GetUid("gray50")); | |
1063 | if (menuPtr->gray == None) { | |
1064 | return TCL_ERROR; | |
1065 | } | |
1066 | } | |
1067 | gcValues.fill_style = FillStippled; | |
1068 | gcValues.stipple = menuPtr->gray; | |
1069 | mask = GCForeground|GCFillStyle|GCStipple; | |
1070 | } | |
1071 | newGC = Tk_GetGC(menuPtr->tkwin, mask, &gcValues); | |
1072 | if (menuPtr->disabledGC != None) { | |
1073 | Tk_FreeGC(menuPtr->disabledGC); | |
1074 | } | |
1075 | menuPtr->disabledGC = newGC; | |
1076 | ||
1077 | gcValues.font = menuPtr->fontPtr->fid; | |
1078 | gcValues.foreground = menuPtr->activeFg->pixel; | |
1079 | gcValues.background = Tk_3DBorderColor(menuPtr->activeBorder)->pixel; | |
1080 | newGC = Tk_GetGC(menuPtr->tkwin, GCForeground|GCBackground|GCFont, | |
1081 | &gcValues); | |
1082 | if (menuPtr->activeGC != None) { | |
1083 | Tk_FreeGC(menuPtr->activeGC); | |
1084 | } | |
1085 | menuPtr->activeGC = newGC; | |
1086 | ||
1087 | gcValues.foreground = menuPtr->selectorFg->pixel; | |
1088 | newGC = Tk_GetGC(menuPtr->tkwin, GCForeground|GCFont, &gcValues); | |
1089 | if (menuPtr->selectorGC != None) { | |
1090 | Tk_FreeGC(menuPtr->selectorGC); | |
1091 | } | |
1092 | menuPtr->selectorGC = newGC; | |
1093 | ||
1094 | /* | |
1095 | * After reconfiguring a menu, we need to reconfigure all of the | |
1096 | * entries in the menu, since some of the things in the children | |
1097 | * (such as graphics contexts) may have to change to reflect changes | |
1098 | * in the parent. | |
1099 | */ | |
1100 | ||
1101 | for (i = 0; i < menuPtr->numEntries; i++) { | |
1102 | MenuEntry *mePtr; | |
1103 | ||
1104 | mePtr = menuPtr->entries[i]; | |
1105 | ConfigureMenuEntry(interp, menuPtr, mePtr, i, 0, (char **) NULL, | |
1106 | TK_CONFIG_ARGV_ONLY | COMMAND_MASK << mePtr->type); | |
1107 | } | |
1108 | ||
1109 | if (!(menuPtr->flags & RESIZE_PENDING)) { | |
1110 | menuPtr->flags |= RESIZE_PENDING; | |
1111 | Tk_DoWhenIdle(ComputeMenuGeometry, (ClientData) menuPtr); | |
1112 | } | |
1113 | ||
1114 | return TCL_OK; | |
1115 | } | |
1116 | \f | |
1117 | /* | |
1118 | *---------------------------------------------------------------------- | |
1119 | * | |
1120 | * ConfigureMenuEntry -- | |
1121 | * | |
1122 | * This procedure is called to process an argv/argc list, plus | |
1123 | * the Tk option database, in order to configure (or | |
1124 | * reconfigure) one entry in a menu. | |
1125 | * | |
1126 | * Results: | |
1127 | * The return value is a standard Tcl result. If TCL_ERROR is | |
1128 | * returned, then interp->result contains an error message. | |
1129 | * | |
1130 | * Side effects: | |
1131 | * Configuration information such as label and accelerator get | |
1132 | * set for mePtr; old resources get freed, if there were any. | |
1133 | * | |
1134 | *---------------------------------------------------------------------- | |
1135 | */ | |
1136 | ||
1137 | static int | |
1138 | ConfigureMenuEntry(interp, menuPtr, mePtr, index, argc, argv, flags) | |
1139 | Tcl_Interp *interp; /* Used for error reporting. */ | |
1140 | Menu *menuPtr; /* Information about whole menu. */ | |
1141 | register MenuEntry *mePtr; /* Information about menu entry; may | |
1142 | * or may not already have values for | |
1143 | * some fields. */ | |
1144 | int index; /* Index of mePtr within menuPtr's | |
1145 | * entries. */ | |
1146 | int argc; /* Number of valid entries in argv. */ | |
1147 | char **argv; /* Arguments. */ | |
1148 | int flags; /* Additional flags to pass to | |
1149 | * Tk_ConfigureWidget. */ | |
1150 | { | |
1151 | XGCValues gcValues; | |
1152 | GC newGC, newActiveGC, newDisabledGC; | |
1153 | unsigned long mask; | |
1154 | ||
1155 | /* | |
1156 | * If this entry is a cascade and the cascade is posted, then unpost | |
1157 | * it before reconfiguring the entry (otherwise the reconfigure might | |
1158 | * change the name of the cascaded entry, leaving a posted menu | |
1159 | * high and dry). | |
1160 | */ | |
1161 | ||
1162 | if (menuPtr->postedCascade == mePtr) { | |
1163 | if (PostSubmenu(menuPtr->interp, menuPtr, (MenuEntry *) NULL) | |
1164 | != TCL_OK) { | |
1165 | TkBindError(menuPtr->interp); | |
1166 | } | |
1167 | } | |
1168 | ||
1169 | /* | |
1170 | * If this entry is a check button or radio button, then remove | |
1171 | * its old trace procedure. | |
1172 | */ | |
1173 | ||
1174 | if ((mePtr->name != NULL) && | |
1175 | ((mePtr->type == CHECK_BUTTON_ENTRY) | |
1176 | || (mePtr->type == RADIO_BUTTON_ENTRY))) { | |
1177 | Tcl_UntraceVar(menuPtr->interp, mePtr->name, | |
1178 | TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, | |
1179 | MenuVarProc, (ClientData) mePtr); | |
1180 | } | |
1181 | ||
1182 | if (Tk_ConfigureWidget(interp, menuPtr->tkwin, entryConfigSpecs, | |
1183 | argc, argv, (char *) mePtr, | |
1184 | flags | (COMMAND_MASK << mePtr->type)) != TCL_OK) { | |
1185 | return TCL_ERROR; | |
1186 | } | |
1187 | ||
1188 | /* | |
1189 | * The code below handles special configuration stuff not taken | |
1190 | * care of by Tk_ConfigureWidget, such as special processing for | |
1191 | * defaults, sizing strings, graphics contexts, etc. | |
1192 | */ | |
1193 | ||
1194 | if (mePtr->label == NULL) { | |
1195 | mePtr->labelLength = 0; | |
1196 | } else { | |
1197 | mePtr->labelLength = strlen(mePtr->label); | |
1198 | } | |
1199 | if (mePtr->accel == NULL) { | |
1200 | mePtr->accelLength = 0; | |
1201 | } else { | |
1202 | mePtr->accelLength = strlen(mePtr->accel); | |
1203 | } | |
1204 | ||
1205 | if (mePtr->state == tkActiveUid) { | |
1206 | if (index != menuPtr->active) { | |
1207 | ActivateMenuEntry(menuPtr, index); | |
1208 | } | |
1209 | } else { | |
1210 | if (index == menuPtr->active) { | |
1211 | ActivateMenuEntry(menuPtr, -1); | |
1212 | } | |
1213 | if ((mePtr->state != tkNormalUid) && (mePtr->state != tkDisabledUid)) { | |
1214 | Tcl_AppendResult(interp, "bad state value \"", mePtr->state, | |
1215 | "\": must be normal, active, or disabled", (char *) NULL); | |
1216 | mePtr->state = tkNormalUid; | |
1217 | return TCL_ERROR; | |
1218 | } | |
1219 | } | |
1220 | ||
1221 | if (mePtr->fontPtr != NULL) { | |
1222 | gcValues.foreground = menuPtr->fg->pixel; | |
1223 | gcValues.background = Tk_3DBorderColor( | |
1224 | (mePtr->border != NULL) ? mePtr->border : menuPtr->border) | |
1225 | ->pixel; | |
1226 | gcValues.font = mePtr->fontPtr->fid; | |
1227 | ||
1228 | /* | |
1229 | * Note: disable GraphicsExpose events; we know there won't be | |
1230 | * obscured areas when copying from an off-screen pixmap to the | |
1231 | * screen and this gets rid of unnecessary events. | |
1232 | */ | |
1233 | ||
1234 | gcValues.graphics_exposures = False; | |
1235 | newGC = Tk_GetGC(menuPtr->tkwin, | |
1236 | GCForeground|GCBackground|GCFont|GCGraphicsExposures, | |
1237 | &gcValues); | |
1238 | ||
1239 | if (menuPtr->disabledFg != NULL) { | |
1240 | gcValues.foreground = menuPtr->disabledFg->pixel; | |
1241 | mask = GCForeground|GCBackground|GCFont|GCGraphicsExposures; | |
1242 | } else { | |
1243 | gcValues.foreground = gcValues.background; | |
1244 | gcValues.fill_style = FillStippled; | |
1245 | gcValues.stipple = menuPtr->gray; | |
1246 | mask = GCForeground|GCFillStyle|GCStipple; | |
1247 | } | |
1248 | newDisabledGC = Tk_GetGC(menuPtr->tkwin, mask, &gcValues); | |
1249 | ||
1250 | gcValues.foreground = menuPtr->activeFg->pixel; | |
1251 | gcValues.background = Tk_3DBorderColor( | |
1252 | (mePtr->activeBorder != NULL) ? mePtr->activeBorder | |
1253 | : menuPtr->activeBorder)->pixel; | |
1254 | newActiveGC = Tk_GetGC(menuPtr->tkwin, | |
1255 | GCForeground|GCBackground|GCFont|GCGraphicsExposures, | |
1256 | &gcValues); | |
1257 | } else { | |
1258 | newGC = NULL; | |
1259 | newActiveGC = NULL; | |
1260 | newDisabledGC = NULL; | |
1261 | } | |
1262 | if (mePtr->textGC != NULL) { | |
1263 | Tk_FreeGC(mePtr->textGC); | |
1264 | } | |
1265 | mePtr->textGC = newGC; | |
1266 | if (mePtr->activeGC != NULL) { | |
1267 | Tk_FreeGC(mePtr->activeGC); | |
1268 | } | |
1269 | mePtr->activeGC = newActiveGC; | |
1270 | if (mePtr->disabledGC != NULL) { | |
1271 | Tk_FreeGC(mePtr->disabledGC); | |
1272 | } | |
1273 | mePtr->disabledGC = newDisabledGC; | |
1274 | ||
1275 | if ((mePtr->type == CHECK_BUTTON_ENTRY) | |
1276 | || (mePtr->type == RADIO_BUTTON_ENTRY)) { | |
1277 | char *value; | |
1278 | ||
1279 | if (mePtr->name == NULL) { | |
1280 | mePtr->name = ckalloc((unsigned) (strlen(mePtr->label) + 1)); | |
1281 | strcpy(mePtr->name, mePtr->label); | |
1282 | } | |
1283 | if (mePtr->onValue == NULL) { | |
1284 | mePtr->onValue = ckalloc((unsigned) (strlen(mePtr->label) + 1)); | |
1285 | strcpy(mePtr->onValue, mePtr->label); | |
1286 | } | |
1287 | ||
1288 | /* | |
1289 | * Select the entry if the associated variable has the | |
1290 | * appropriate value, initialize the variable if it doesn't | |
1291 | * exist, then set a trace on the variable to monitor future | |
1292 | * changes to its value. | |
1293 | */ | |
1294 | ||
1295 | value = Tcl_GetVar(interp, mePtr->name, TCL_GLOBAL_ONLY); | |
1296 | mePtr->flags &= ENTRY_SELECTED; | |
1297 | if (value != NULL) { | |
1298 | if (strcmp(value, mePtr->onValue) == 0) { | |
1299 | mePtr->flags |= ENTRY_SELECTED; | |
1300 | } | |
1301 | } else { | |
1302 | Tcl_SetVar(interp, mePtr->name, | |
1303 | (mePtr->type == CHECK_BUTTON_ENTRY) ? mePtr->offValue : "", | |
1304 | TCL_GLOBAL_ONLY); | |
1305 | } | |
1306 | Tcl_TraceVar(interp, mePtr->name, | |
1307 | TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, | |
1308 | MenuVarProc, (ClientData) mePtr); | |
1309 | } | |
1310 | ||
1311 | if (!(menuPtr->flags & RESIZE_PENDING)) { | |
1312 | menuPtr->flags |= RESIZE_PENDING; | |
1313 | Tk_DoWhenIdle(ComputeMenuGeometry, (ClientData) menuPtr); | |
1314 | } | |
1315 | return TCL_OK; | |
1316 | } | |
1317 | \f | |
1318 | /* | |
1319 | *-------------------------------------------------------------- | |
1320 | * | |
1321 | * ComputeMenuGeometry -- | |
1322 | * | |
1323 | * This procedure is invoked to recompute the size and | |
1324 | * layout of a menu. It is called as a when-idle handler so | |
1325 | * that it only gets done once, even if a group of changes is | |
1326 | * made to the menu. | |
1327 | * | |
1328 | * Results: | |
1329 | * None. | |
1330 | * | |
1331 | * Side effects: | |
1332 | * Fields of menu entries are changed to reflect their | |
1333 | * current positions, and the size of the menu window | |
1334 | * itself may be changed. | |
1335 | * | |
1336 | *-------------------------------------------------------------- | |
1337 | */ | |
1338 | ||
1339 | static void | |
1340 | ComputeMenuGeometry(clientData) | |
1341 | ClientData clientData; /* Structure describing menu. */ | |
1342 | { | |
1343 | Menu *menuPtr = (Menu *) clientData; | |
1344 | register MenuEntry *mePtr; | |
1345 | XFontStruct *fontPtr; | |
1346 | int maxLabelWidth, maxSelectorWidth, maxAccelWidth; | |
1347 | int width, height, selectorSpace, horizMargin; | |
1348 | int i, y; | |
1349 | ||
1350 | if (menuPtr->tkwin == NULL) { | |
1351 | return; | |
1352 | } | |
1353 | ||
1354 | maxLabelWidth = maxSelectorWidth = maxAccelWidth = 0; | |
1355 | y = menuPtr->borderWidth; | |
1356 | ||
1357 | for (i = 0; i < menuPtr->numEntries; i++) { | |
1358 | mePtr = menuPtr->entries[i]; | |
1359 | selectorSpace = 0; | |
1360 | fontPtr = mePtr->fontPtr; | |
1361 | if (fontPtr == NULL) { | |
1362 | fontPtr = menuPtr->fontPtr; | |
1363 | } | |
1364 | ||
1365 | /* | |
1366 | * For each entry, compute the height required by that | |
1367 | * particular entry, plus three widths: the width of the | |
1368 | * label, the width to allow for a selector to be displayed | |
1369 | * to the left of the label (if any), and the width of the | |
1370 | * accelerator to be displayed to the right of the label | |
1371 | * (if any). These sizes depend, of course, on the type | |
1372 | * of the entry. | |
1373 | */ | |
1374 | ||
1375 | if (mePtr->bitmap != None) { | |
1376 | unsigned int bitmapWidth, bitmapHeight; | |
1377 | ||
1378 | #if defined(USE_XPM3) | |
1379 | Tk_SizeOfPixmap(mePtr->bitmap, &bitmapWidth, &bitmapHeight); | |
1380 | #else | |
1381 | Tk_SizeOfBitmap(mePtr->bitmap, &bitmapWidth, &bitmapHeight); | |
1382 | #endif | |
1383 | mePtr->height = bitmapHeight; | |
1384 | width = bitmapWidth; | |
1385 | if (mePtr->type == CHECK_BUTTON_ENTRY) { | |
1386 | selectorSpace = (14*mePtr->height)/10; | |
1387 | mePtr->selectorDiameter = (65*mePtr->height)/100; | |
1388 | } else if (mePtr->type == RADIO_BUTTON_ENTRY) { | |
1389 | selectorSpace = (14*mePtr->height)/10; | |
1390 | mePtr->selectorDiameter = (75*mePtr->height)/100; | |
1391 | } | |
1392 | } else { | |
1393 | mePtr->height = fontPtr->ascent + fontPtr->descent; | |
1394 | if (mePtr->label != NULL) { | |
1395 | (void) TkMeasureChars(fontPtr, mePtr->label, | |
1396 | mePtr->labelLength, 0, (int) 100000, | |
1397 | TK_NEWLINES_NOT_SPECIAL, &width); | |
1398 | } else { | |
1399 | width = 0; | |
1400 | } | |
1401 | if (mePtr->type == CHECK_BUTTON_ENTRY) { | |
1402 | selectorSpace = mePtr->height; | |
1403 | mePtr->selectorDiameter = (80*mePtr->height)/100; | |
1404 | } else if (mePtr->type == RADIO_BUTTON_ENTRY) { | |
1405 | selectorSpace = mePtr->height; | |
1406 | mePtr->selectorDiameter = mePtr->height; | |
1407 | } | |
1408 | } | |
1409 | mePtr->height += 2*menuPtr->activeBorderWidth + 2; | |
1410 | if (width > maxLabelWidth) { | |
1411 | maxLabelWidth = width; | |
1412 | } | |
1413 | if (mePtr->accel != NULL) { | |
1414 | (void) TkMeasureChars(fontPtr, mePtr->accel, mePtr->accelLength, | |
1415 | 0, (int) 100000, TK_NEWLINES_NOT_SPECIAL, &width); | |
1416 | if (width > maxAccelWidth) { | |
1417 | maxAccelWidth = width; | |
1418 | } | |
1419 | } | |
1420 | if (mePtr->type == SEPARATOR_ENTRY) { | |
1421 | mePtr->height = 4*menuPtr->borderWidth; | |
1422 | } | |
1423 | if (selectorSpace > maxSelectorWidth) { | |
1424 | maxSelectorWidth = selectorSpace; | |
1425 | } | |
1426 | mePtr->y = y; | |
1427 | y += mePtr->height; | |
1428 | } | |
1429 | ||
1430 | /* | |
1431 | * Got all the sizes. Update fields in the menu structure, then | |
1432 | * resize the window if necessary. Leave margins on either side | |
1433 | * of the selector (or just one margin if there is no selector). | |
1434 | * Leave another margin on the right side of the label, plus yet | |
1435 | * another margin to the right of the accelerator (if there is one). | |
1436 | */ | |
1437 | ||
1438 | horizMargin = 2; | |
1439 | menuPtr->selectorSpace = maxSelectorWidth + horizMargin; | |
1440 | if (maxSelectorWidth != 0) { | |
1441 | menuPtr->selectorSpace += horizMargin; | |
1442 | } | |
1443 | menuPtr->labelWidth = maxLabelWidth + horizMargin; | |
1444 | width = menuPtr->selectorSpace + menuPtr->labelWidth + maxAccelWidth | |
1445 | + 2*menuPtr->borderWidth + 2*menuPtr->activeBorderWidth + 2; | |
1446 | if (maxAccelWidth != 0) { | |
1447 | width += horizMargin; | |
1448 | } | |
1449 | height = y + menuPtr->borderWidth; | |
1450 | ||
1451 | /* | |
1452 | * The X server doesn't like zero dimensions, so round up to at least | |
1453 | * 1 (a zero-sized menu should never really occur, anyway). | |
1454 | */ | |
1455 | ||
1456 | if (width <= 0) { | |
1457 | width = 1; | |
1458 | } | |
1459 | if (height <= 0) { | |
1460 | height = 1; | |
1461 | } | |
1462 | if ((width != Tk_ReqWidth(menuPtr->tkwin)) || | |
1463 | (height != Tk_ReqHeight(menuPtr->tkwin))) { | |
1464 | Tk_GeometryRequest(menuPtr->tkwin, width, height); | |
1465 | } else { | |
1466 | /* | |
1467 | * Must always force a redisplay here if the window is mapped | |
1468 | * (even if the size didn't change, something else might have | |
1469 | * changed in the menu, such as a label or accelerator). The | |
1470 | * resize will force a redisplay above. | |
1471 | */ | |
1472 | ||
1473 | EventuallyRedrawMenu(menuPtr, -1); | |
1474 | } | |
1475 | ||
1476 | menuPtr->flags &= ~RESIZE_PENDING; | |
1477 | } | |
1478 | \f | |
1479 | /* | |
1480 | *---------------------------------------------------------------------- | |
1481 | * | |
1482 | * DisplayMenu -- | |
1483 | * | |
1484 | * This procedure is invoked to display a menu widget. | |
1485 | * | |
1486 | * Results: | |
1487 | * None. | |
1488 | * | |
1489 | * Side effects: | |
1490 | * Commands are output to X to display the menu in its | |
1491 | * current mode. | |
1492 | * | |
1493 | *---------------------------------------------------------------------- | |
1494 | */ | |
1495 | ||
1496 | static void | |
1497 | DisplayMenu(clientData) | |
1498 | ClientData clientData; /* Information about widget. */ | |
1499 | { | |
1500 | register Menu *menuPtr = (Menu *) clientData; | |
1501 | register MenuEntry *mePtr; | |
1502 | register Tk_Window tkwin = menuPtr->tkwin; | |
1503 | XFontStruct *fontPtr; | |
1504 | int index, baseline; | |
1505 | GC gc; | |
1506 | ||
1507 | menuPtr->flags &= ~REDRAW_PENDING; | |
1508 | if ((menuPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) { | |
1509 | return; | |
1510 | } | |
1511 | ||
1512 | /* | |
1513 | * Loop through all of the entries, drawing them one at a time. | |
1514 | */ | |
1515 | ||
1516 | for (index = 0; index < menuPtr->numEntries; index++) { | |
1517 | mePtr = menuPtr->entries[index]; | |
1518 | if (!(mePtr->flags & ENTRY_NEEDS_REDISPLAY)) { | |
1519 | continue; | |
1520 | } | |
1521 | mePtr->flags &= ~ENTRY_NEEDS_REDISPLAY; | |
1522 | ||
1523 | /* | |
1524 | * Background. | |
1525 | */ | |
1526 | ||
1527 | if (mePtr->state == tkActiveUid) { | |
1528 | Tk_Fill3DRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin), | |
1529 | (mePtr->activeBorder != NULL) ? mePtr->activeBorder | |
1530 | : menuPtr->activeBorder, menuPtr->borderWidth, mePtr->y, | |
1531 | Tk_Width(tkwin) - 2*menuPtr->borderWidth, mePtr->height, | |
1532 | menuPtr->activeBorderWidth, TK_RELIEF_RAISED); | |
1533 | gc = mePtr->activeGC; | |
1534 | if (gc == NULL) { | |
1535 | gc = menuPtr->activeGC; | |
1536 | } | |
1537 | } else { | |
1538 | Tk_Fill3DRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin), | |
1539 | (mePtr->border != NULL) ? mePtr->border | |
1540 | : menuPtr->border, menuPtr->borderWidth, mePtr->y, | |
1541 | Tk_Width(tkwin) - 2*menuPtr->borderWidth, mePtr->height, | |
1542 | 0, TK_RELIEF_FLAT); | |
1543 | if ((mePtr->state == tkDisabledUid) | |
1544 | && (menuPtr->disabledFg != NULL)) { | |
1545 | gc = mePtr->disabledGC; | |
1546 | if (gc == NULL) { | |
1547 | gc = menuPtr->disabledGC; | |
1548 | } | |
1549 | } else { | |
1550 | gc = mePtr->textGC; | |
1551 | if (gc == NULL) { | |
1552 | gc = menuPtr->textGC; | |
1553 | } | |
1554 | } | |
1555 | } | |
1556 | ||
1557 | /* | |
1558 | * Draw label or bitmap for entry. | |
1559 | */ | |
1560 | ||
1561 | fontPtr = mePtr->fontPtr; | |
1562 | if (fontPtr == NULL) { | |
1563 | fontPtr = menuPtr->fontPtr; | |
1564 | } | |
1565 | baseline = mePtr->y + (mePtr->height + fontPtr->ascent | |
1566 | - fontPtr->descent)/2; | |
1567 | if (mePtr->bitmap != None) { | |
1568 | unsigned int width, height; | |
1569 | ||
1570 | #if defined(USE_XPM3) | |
1571 | Tk_SizeOfPixmap(mePtr->bitmap, &width, &height); | |
1572 | XCopyArea(Tk_Display(tkwin), mePtr->bitmap, Tk_WindowId(tkwin), | |
1573 | gc, 0, 0, width, height, | |
1574 | menuPtr->borderWidth + menuPtr->selectorSpace, | |
1575 | (int) (mePtr->y + (mePtr->height - height)/2)); | |
1576 | #else | |
1577 | Tk_SizeOfBitmap(mePtr->bitmap, &width, &height); | |
1578 | XCopyPlane(Tk_Display(tkwin), mePtr->bitmap, Tk_WindowId(tkwin), | |
1579 | gc, 0, 0, width, height, | |
1580 | menuPtr->borderWidth + menuPtr->selectorSpace, | |
1581 | (int) (mePtr->y + (mePtr->height - height)/2), 1); | |
1582 | #endif | |
1583 | } else { | |
1584 | baseline = mePtr->y + (mePtr->height + fontPtr->ascent | |
1585 | - fontPtr->descent)/2; | |
1586 | if (mePtr->label != NULL) { | |
1587 | TkDisplayChars(Tk_Display(tkwin), Tk_WindowId(tkwin), gc, | |
1588 | fontPtr, mePtr->label, mePtr->labelLength, | |
1589 | menuPtr->borderWidth + menuPtr->selectorSpace, | |
1590 | baseline, TK_NEWLINES_NOT_SPECIAL); | |
1591 | if (mePtr->underline >= 0) { | |
1592 | TkUnderlineChars(Tk_Display(tkwin), Tk_WindowId(tkwin), gc, | |
1593 | fontPtr, mePtr->label, | |
1594 | menuPtr->borderWidth + menuPtr->selectorSpace, | |
1595 | baseline, TK_NEWLINES_NOT_SPECIAL, | |
1596 | mePtr->underline, mePtr->underline); | |
1597 | } | |
1598 | } | |
1599 | } | |
1600 | ||
1601 | /* | |
1602 | * Draw accelerator. | |
1603 | */ | |
1604 | ||
1605 | if (mePtr->accel != NULL) { | |
1606 | TkDisplayChars(Tk_Display(tkwin), Tk_WindowId(tkwin), gc, | |
1607 | fontPtr, mePtr->accel, mePtr->accelLength, | |
1608 | menuPtr->borderWidth + menuPtr->selectorSpace | |
1609 | + menuPtr->labelWidth, baseline, TK_NEWLINES_NOT_SPECIAL); | |
1610 | } | |
1611 | ||
1612 | /* | |
1613 | * Draw check-button selector. | |
1614 | */ | |
1615 | ||
1616 | if (mePtr->type == CHECK_BUTTON_ENTRY) { | |
1617 | int dim, x, y; | |
1618 | ||
1619 | dim = mePtr->selectorDiameter; | |
1620 | x = menuPtr->borderWidth + (menuPtr->selectorSpace - dim)/2; | |
1621 | y = mePtr->y + (mePtr->height - dim)/2; | |
1622 | Tk_Fill3DRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin), | |
1623 | menuPtr->border, x, y, dim, dim, | |
1624 | menuPtr->activeBorderWidth, TK_RELIEF_SUNKEN); | |
1625 | x += menuPtr->activeBorderWidth; | |
1626 | y += menuPtr->activeBorderWidth; | |
1627 | dim -= 2*menuPtr->activeBorderWidth; | |
1628 | if ((dim > 0) && (mePtr->flags & ENTRY_SELECTED)) { | |
1629 | XFillRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin), | |
1630 | menuPtr->selectorGC, x, y, (unsigned int) dim, | |
1631 | (unsigned int) dim); | |
1632 | } | |
1633 | } | |
1634 | ||
1635 | /* | |
1636 | * Draw radio-button selector. | |
1637 | */ | |
1638 | ||
1639 | if (mePtr->type == RADIO_BUTTON_ENTRY) { | |
1640 | XPoint points[4]; | |
1641 | int radius; | |
1642 | ||
1643 | radius = mePtr->selectorDiameter/2; | |
1644 | points[0].x = menuPtr->borderWidth | |
1645 | + (menuPtr->selectorSpace - mePtr->selectorDiameter)/2; | |
1646 | points[0].y = mePtr->y + (mePtr->height)/2; | |
1647 | points[1].x = points[0].x + radius; | |
1648 | points[1].y = points[0].y + radius; | |
1649 | points[2].x = points[1].x + radius; | |
1650 | points[2].y = points[0].y; | |
1651 | points[3].x = points[1].x; | |
1652 | points[3].y = points[0].y - radius; | |
1653 | if (mePtr->flags & ENTRY_SELECTED) { | |
1654 | XFillPolygon(Tk_Display(tkwin), Tk_WindowId(tkwin), | |
1655 | menuPtr->selectorGC, points, 4, Convex, | |
1656 | CoordModeOrigin); | |
1657 | } else { | |
1658 | Tk_Fill3DPolygon(Tk_Display(tkwin), Tk_WindowId(tkwin), | |
1659 | menuPtr->border, points, 4, menuPtr->activeBorderWidth, | |
1660 | TK_RELIEF_FLAT); | |
1661 | } | |
1662 | Tk_Draw3DPolygon(Tk_Display(tkwin), Tk_WindowId(tkwin), | |
1663 | menuPtr->border, points, 4, menuPtr->activeBorderWidth, | |
1664 | TK_RELIEF_SUNKEN); | |
1665 | } | |
1666 | ||
1667 | /* | |
1668 | * Draw separator. | |
1669 | */ | |
1670 | ||
1671 | if (mePtr->type == SEPARATOR_ENTRY) { | |
1672 | XPoint points[2]; | |
1673 | int margin; | |
1674 | ||
1675 | margin = (fontPtr->ascent + fontPtr->descent)/2; | |
1676 | points[0].x = 2*menuPtr->borderWidth + margin; | |
1677 | points[0].y = mePtr->y + mePtr->height/2; | |
1678 | points[1].x = Tk_Width(tkwin) - 2*menuPtr->borderWidth - margin; | |
1679 | points[1].y = points[0].y; | |
1680 | Tk_Draw3DPolygon(Tk_Display(tkwin), Tk_WindowId(tkwin), | |
1681 | menuPtr->border, points, 2, 1, TK_RELIEF_RAISED); | |
1682 | } | |
1683 | ||
1684 | /* | |
1685 | * If the entry is disabled with a stipple rather than a special | |
1686 | * foreground color, generate the stippled effect. | |
1687 | */ | |
1688 | ||
1689 | if ((mePtr->state == tkDisabledUid) && (menuPtr->disabledFg == NULL)) { | |
1690 | XFillRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin), | |
1691 | menuPtr->disabledGC, menuPtr->borderWidth, | |
1692 | mePtr->y, | |
1693 | (unsigned) (Tk_Width(tkwin) - 2*menuPtr->borderWidth), | |
1694 | (unsigned) mePtr->height); | |
1695 | } | |
1696 | } | |
1697 | ||
1698 | Tk_Draw3DRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin), | |
1699 | menuPtr->border, 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), | |
1700 | menuPtr->borderWidth, TK_RELIEF_RAISED); | |
1701 | } | |
1702 | \f | |
1703 | /* | |
1704 | *-------------------------------------------------------------- | |
1705 | * | |
1706 | * GetMenuIndex -- | |
1707 | * | |
1708 | * Parse a textual index into a menu and return the numerical | |
1709 | * index of the indicated entry. | |
1710 | * | |
1711 | * Results: | |
1712 | * A standard Tcl result. If all went well, then *indexPtr is | |
1713 | * filled in with the entry index corresponding to string | |
1714 | * (ranges from -1 to the number of entries in the menu minus | |
1715 | * one). Otherwise an error message is left in interp->result. | |
1716 | * | |
1717 | * Side effects: | |
1718 | * None. | |
1719 | * | |
1720 | *-------------------------------------------------------------- | |
1721 | */ | |
1722 | ||
1723 | static int | |
1724 | GetMenuIndex(interp, menuPtr, string, indexPtr) | |
1725 | Tcl_Interp *interp; /* For error messages. */ | |
1726 | Menu *menuPtr; /* Menu for which the index is being | |
1727 | * specified. */ | |
1728 | char *string; /* Specification of an entry in menu. See | |
1729 | * manual entry for valid .*/ | |
1730 | int *indexPtr; /* Where to store converted relief. */ | |
1731 | { | |
1732 | int i, y; | |
1733 | ||
1734 | if ((string[0] == 'a') && (strcmp(string, "active") == 0)) { | |
1735 | *indexPtr = menuPtr->active; | |
1736 | return TCL_OK; | |
1737 | } | |
1738 | ||
1739 | if ((string[0] == 'l') && (strcmp(string, "last") == 0)) { | |
1740 | *indexPtr = menuPtr->numEntries-1; | |
1741 | return TCL_OK; | |
1742 | } | |
1743 | ||
1744 | if ((string[0] == 'n') && (strcmp(string, "none") == 0)) { | |
1745 | *indexPtr = -1; | |
1746 | return TCL_OK; | |
1747 | } | |
1748 | ||
1749 | if (string[0] == '@') { | |
1750 | if (Tcl_GetInt(interp, string+1, &y) == TCL_OK) { | |
1751 | if (y < 0) { | |
1752 | *indexPtr = -1; | |
1753 | return TCL_OK; | |
1754 | } | |
1755 | for (i = 0; i < menuPtr->numEntries; i++) { | |
1756 | y -= menuPtr->entries[i]->height; | |
1757 | if (y < 0) { | |
1758 | break; | |
1759 | } | |
1760 | } | |
1761 | if (i >= menuPtr->numEntries) { | |
1762 | i = -1; | |
1763 | } | |
1764 | *indexPtr = i; | |
1765 | return TCL_OK; | |
1766 | } else { | |
1767 | Tcl_SetResult(interp, (char *) NULL, TCL_STATIC); | |
1768 | } | |
1769 | } | |
1770 | ||
1771 | if (isdigit(string[0])) { | |
1772 | if (Tcl_GetInt(interp, string, &i) == TCL_OK) { | |
1773 | if ((i < menuPtr->numEntries) && (i >= 0)) { | |
1774 | *indexPtr = i; | |
1775 | return TCL_OK; | |
1776 | } | |
1777 | } else { | |
1778 | Tcl_SetResult(interp, (char *) NULL, TCL_STATIC); | |
1779 | } | |
1780 | } | |
1781 | ||
1782 | for (i = 0; i < menuPtr->numEntries; i++) { | |
1783 | char *label; | |
1784 | ||
1785 | label = menuPtr->entries[i]->label; | |
1786 | if ((label != NULL) | |
1787 | && (Tcl_StringMatch(menuPtr->entries[i]->label, string))) { | |
1788 | *indexPtr = i; | |
1789 | return TCL_OK; | |
1790 | } | |
1791 | } | |
1792 | ||
1793 | Tcl_AppendResult(interp, "bad menu entry index \"", | |
1794 | string, "\"", (char *) NULL); | |
1795 | return TCL_ERROR; | |
1796 | } | |
1797 | \f | |
1798 | /* | |
1799 | *-------------------------------------------------------------- | |
1800 | * | |
1801 | * MenuEventProc -- | |
1802 | * | |
1803 | * This procedure is invoked by the Tk dispatcher for various | |
1804 | * events on menus. | |
1805 | * | |
1806 | * Results: | |
1807 | * None. | |
1808 | * | |
1809 | * Side effects: | |
1810 | * When the window gets deleted, internal structures get | |
1811 | * cleaned up. When it gets exposed, it is redisplayed. | |
1812 | * | |
1813 | *-------------------------------------------------------------- | |
1814 | */ | |
1815 | ||
1816 | static void | |
1817 | MenuEventProc(clientData, eventPtr) | |
1818 | ClientData clientData; /* Information about window. */ | |
1819 | XEvent *eventPtr; /* Information about event. */ | |
1820 | { | |
1821 | Menu *menuPtr = (Menu *) clientData; | |
1822 | if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) { | |
1823 | EventuallyRedrawMenu(menuPtr, -1); | |
1824 | } else if (eventPtr->type == DestroyNotify) { | |
1825 | Tcl_DeleteCommand(menuPtr->interp, Tk_PathName(menuPtr->tkwin)); | |
1826 | ||
1827 | /* | |
1828 | * Careful! Must delete the event-sharing information here | |
1829 | * rather than in DestroyMenu. By the time that procedure | |
1830 | * is called the tkwin may have been reused, resulting in some | |
1831 | * other window accidentally being cut off from shared events. | |
1832 | */ | |
1833 | ||
1834 | Tk_UnshareEvents(menuPtr->tkwin, menuPtr->group); | |
1835 | menuPtr->tkwin = NULL; | |
1836 | if (menuPtr->flags & REDRAW_PENDING) { | |
1837 | Tk_CancelIdleCall(DisplayMenu, (ClientData) menuPtr); | |
1838 | } | |
1839 | if (menuPtr->flags & RESIZE_PENDING) { | |
1840 | Tk_CancelIdleCall(ComputeMenuGeometry, (ClientData) menuPtr); | |
1841 | } | |
1842 | Tk_EventuallyFree((ClientData) menuPtr, DestroyMenu); | |
1843 | } | |
1844 | } | |
1845 | \f | |
1846 | /* | |
1847 | *-------------------------------------------------------------- | |
1848 | * | |
1849 | * MenuVarProc -- | |
1850 | * | |
1851 | * This procedure is invoked when someone changes the | |
1852 | * state variable associated with a radiobutton or checkbutton | |
1853 | * menu entry. The entry's selected state is set to match | |
1854 | * the value of the variable. | |
1855 | * | |
1856 | * Results: | |
1857 | * NULL is always returned. | |
1858 | * | |
1859 | * Side effects: | |
1860 | * The menu entry may become selected or deselected. | |
1861 | * | |
1862 | *-------------------------------------------------------------- | |
1863 | */ | |
1864 | ||
1865 | /* ARGSUSED */ | |
1866 | static char * | |
1867 | MenuVarProc(clientData, interp, name1, name2, flags) | |
1868 | ClientData clientData; /* Information about menu entry. */ | |
1869 | Tcl_Interp *interp; /* Interpreter containing variable. */ | |
1870 | char *name1; /* First part of variable's name. */ | |
1871 | char *name2; /* Second part of variable's name. */ | |
1872 | int flags; /* Describes what just happened. */ | |
1873 | { | |
1874 | MenuEntry *mePtr = (MenuEntry *) clientData; | |
1875 | Menu *menuPtr; | |
1876 | char *value; | |
1877 | ||
1878 | menuPtr = mePtr->menuPtr; | |
1879 | ||
1880 | /* | |
1881 | * If the variable is being unset, then re-establish the | |
1882 | * trace unless the whole interpreter is going away. | |
1883 | */ | |
1884 | ||
1885 | if (flags & TCL_TRACE_UNSETS) { | |
1886 | mePtr->flags &= ~ENTRY_SELECTED; | |
1887 | if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) { | |
1888 | Tcl_TraceVar2(interp, name1, name2, | |
1889 | TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, | |
1890 | MenuVarProc, clientData); | |
1891 | } | |
1892 | EventuallyRedrawMenu(menuPtr, -1); | |
1893 | return (char *) NULL; | |
1894 | } | |
1895 | ||
1896 | /* | |
1897 | * Use the value of the variable to update the selected status of | |
1898 | * the menu entry. | |
1899 | */ | |
1900 | ||
1901 | value = Tcl_GetVar2(interp, name1, name2, flags & TCL_GLOBAL_ONLY); | |
1902 | if (strcmp(value, mePtr->onValue) == 0) { | |
1903 | if (mePtr->flags & ENTRY_SELECTED) { | |
1904 | return (char *) NULL; | |
1905 | } | |
1906 | mePtr->flags |= ENTRY_SELECTED; | |
1907 | } else if (mePtr->flags & ENTRY_SELECTED) { | |
1908 | mePtr->flags &= ~ENTRY_SELECTED; | |
1909 | } else { | |
1910 | return (char *) NULL; | |
1911 | } | |
1912 | EventuallyRedrawMenu(menuPtr, -1); | |
1913 | return (char *) NULL; | |
1914 | } | |
1915 | \f | |
1916 | /* | |
1917 | *---------------------------------------------------------------------- | |
1918 | * | |
1919 | * EventuallyRedrawMenu -- | |
1920 | * | |
1921 | * Arrange for an entry of a menu, or the whole menu, to be | |
1922 | * redisplayed at some point in the future. | |
1923 | * | |
1924 | * Results: | |
1925 | * None. | |
1926 | * | |
1927 | * Side effects: | |
1928 | * A when-idle hander is scheduled to do the redisplay, if there | |
1929 | * isn't one already scheduled. | |
1930 | * | |
1931 | *---------------------------------------------------------------------- | |
1932 | */ | |
1933 | ||
1934 | static void | |
1935 | EventuallyRedrawMenu(menuPtr, index) | |
1936 | register Menu *menuPtr; /* Information about menu to redraw. */ | |
1937 | int index; /* Which entry to redraw. If -1, then | |
1938 | * all the entries in the menu are redrawn. */ | |
1939 | { | |
1940 | if (menuPtr->tkwin == NULL) { | |
1941 | return; | |
1942 | } | |
1943 | if (index != -1) { | |
1944 | menuPtr->entries[index]->flags |= ENTRY_NEEDS_REDISPLAY; | |
1945 | } else { | |
1946 | for (index = 0; index < menuPtr->numEntries; index++) { | |
1947 | menuPtr->entries[index]->flags |= ENTRY_NEEDS_REDISPLAY; | |
1948 | } | |
1949 | } | |
1950 | if ((menuPtr->tkwin == NULL) || !Tk_IsMapped(menuPtr->tkwin) | |
1951 | || (menuPtr->flags & REDRAW_PENDING)) { | |
1952 | return; | |
1953 | } | |
1954 | Tk_DoWhenIdle(DisplayMenu, (ClientData) menuPtr); | |
1955 | menuPtr->flags |= REDRAW_PENDING; | |
1956 | } | |
1957 | \f | |
1958 | /* | |
1959 | *-------------------------------------------------------------- | |
1960 | * | |
1961 | * PostSubmenu -- | |
1962 | * | |
1963 | * This procedure arranges for a particular submenu (i.e. the | |
1964 | * menu corresponding to a given cascade entry) to be | |
1965 | * posted. | |
1966 | * | |
1967 | * Results: | |
1968 | * A standard Tcl return result. Errors may occur in the | |
1969 | * Tcl commands generated to post and unpost submenus. | |
1970 | * | |
1971 | * Side effects: | |
1972 | * If there is already a submenu posted, it is unposted. | |
1973 | * The new submenu is then posted. | |
1974 | * | |
1975 | *-------------------------------------------------------------- | |
1976 | */ | |
1977 | ||
1978 | static int | |
1979 | PostSubmenu(interp, menuPtr, mePtr) | |
1980 | Tcl_Interp *interp; /* Used for invoking sub-commands and | |
1981 | * reporting errors. */ | |
1982 | register Menu *menuPtr; /* Information about menu as a whole. */ | |
1983 | register MenuEntry *mePtr; /* Info about submenu that is to be | |
1984 | * posted. NULL means make sure that | |
1985 | * no submenu is posted. */ | |
1986 | { | |
1987 | char string[30]; | |
1988 | int result, x, y; | |
1989 | ||
1990 | if (mePtr == menuPtr->postedCascade) { | |
1991 | return TCL_OK; | |
1992 | } | |
1993 | ||
1994 | if (menuPtr->postedCascade != NULL) { | |
1995 | result = Tcl_VarEval(interp, menuPtr->postedCascade->name, | |
1996 | " unpost", (char *) NULL); | |
1997 | menuPtr->postedCascade = NULL; | |
1998 | if (result != TCL_OK) { | |
1999 | return result; | |
2000 | } | |
2001 | } | |
2002 | ||
2003 | if ((mePtr != NULL) && (mePtr->name != NULL)) { | |
2004 | Tk_GetRootCoords(menuPtr->tkwin, &x, &y); | |
2005 | x += Tk_Width(menuPtr->tkwin); | |
2006 | y += mePtr->y; | |
2007 | sprintf(string, "%d %d ", x, y); | |
2008 | result = Tcl_VarEval(interp, mePtr->name, " post ", string, | |
2009 | menuPtr->group, (char *) NULL); | |
2010 | if (result != TCL_OK) { | |
2011 | return result; | |
2012 | } | |
2013 | menuPtr->postedCascade = mePtr; | |
2014 | } | |
2015 | return TCL_OK; | |
2016 | } | |
2017 | \f | |
2018 | /* | |
2019 | *---------------------------------------------------------------------- | |
2020 | * | |
2021 | * ActivateMenuEntry -- | |
2022 | * | |
2023 | * This procedure is invoked to make a particular menu entry | |
2024 | * the active one, deactivating any other entry that might | |
2025 | * currently be active. | |
2026 | * | |
2027 | * Results: | |
2028 | * The return value is a standard Tcl result (errors can occur | |
2029 | * while posting and unposting submenus). | |
2030 | * | |
2031 | * Side effects: | |
2032 | * Menu entries get redisplayed, and the active entry changes. | |
2033 | * Submenus may get posted and unposted. | |
2034 | * | |
2035 | *---------------------------------------------------------------------- | |
2036 | */ | |
2037 | ||
2038 | static int | |
2039 | ActivateMenuEntry(menuPtr, index) | |
2040 | register Menu *menuPtr; /* Menu in which to activate. */ | |
2041 | int index; /* Index of entry to activate, or | |
2042 | * -1 to deactivate all entries. */ | |
2043 | { | |
2044 | register MenuEntry *mePtr; | |
2045 | int result = TCL_OK; | |
2046 | ||
2047 | if (menuPtr->active >= 0) { | |
2048 | mePtr = menuPtr->entries[menuPtr->active]; | |
2049 | ||
2050 | /* | |
2051 | * Don't change the state unless it's currently active (state | |
2052 | * might already have been changed to disabled). | |
2053 | */ | |
2054 | ||
2055 | if (mePtr->state == tkActiveUid) { | |
2056 | mePtr->state = tkNormalUid; | |
2057 | } | |
2058 | EventuallyRedrawMenu(menuPtr, menuPtr->active); | |
2059 | } | |
2060 | menuPtr->active = index; | |
2061 | if (index >= 0) { | |
2062 | mePtr = menuPtr->entries[index]; | |
2063 | mePtr->state = tkActiveUid; | |
2064 | EventuallyRedrawMenu(menuPtr, index); | |
2065 | Tk_Preserve((ClientData) mePtr); | |
2066 | if (mePtr->type == CASCADE_ENTRY) { | |
2067 | result = PostSubmenu(menuPtr->interp, menuPtr, mePtr); | |
2068 | } else { | |
2069 | result = PostSubmenu(menuPtr->interp, menuPtr, (MenuEntry *) NULL); | |
2070 | } | |
2071 | Tk_Release((ClientData) mePtr); | |
2072 | } | |
2073 | return result; | |
2074 | } |