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
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.
20 static char rcsid
[] = "$Header: /user6/ouster/wish/RCS/tkMenu.c,v 1.37 92/08/24 09:24:04 ouster Exp $ SPRITE (Berkeley)";
28 * One of the following data structures is kept for each entry of each
29 * menu managed by this file:
32 typedef struct MenuEntry
{
33 int type
; /* Type of menu entry; see below for
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
49 * Information related to displaying entry:
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
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
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
78 * Information used to implement this entry's action:
81 char *command
; /* Command to invoke when entry is invoked.
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).
89 char *offValue
; /* Value to store in variable when not
90 * selected (only for check buttons).
94 * Miscellaneous information:
97 int flags
; /* Various flags. See below for definitions. */
101 * Flag values defined for menu entries:
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.
109 #define ENTRY_SELECTED 1
110 #define ENTRY_NEEDS_REDISPLAY 4
113 * Types defined for MenuEntries:
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
123 * Mask bits for above types:
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)
135 * Configuration specs for individual menu entries:
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
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
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
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
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
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
),
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
),
183 {TK_CONFIG_STRING
, "-value", (char *) NULL
, (char *) NULL
,
184 DEF_MENU_ENTRY_VALUE
, Tk_Offset(MenuEntry
, onValue
),
186 {TK_CONFIG_STRING
, "-variable", (char *) NULL
, (char *) NULL
,
187 DEF_MENU_ENTRY_CHECK_VARIABLE
, Tk_Offset(MenuEntry
, name
),
189 {TK_CONFIG_STRING
, "-variable", (char *) NULL
, (char *) NULL
,
190 DEF_MENU_ENTRY_RADIO_VARIABLE
, Tk_Offset(MenuEntry
, name
),
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
,
201 * A data structure of the following type is kept for each
202 * menu managed by this file:
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
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
216 Tk_Uid group
; /* Used to allow event sharing between
217 * related menus and menu buttons. */
220 * Information used when displaying widget:
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
236 Pixmap gray
; /* Bitmap for drawing disabled entries in
237 * a stippled fashion. None means not
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
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. */
257 * Miscellaneous information:
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
269 * Flag bits for menus:
271 * REDRAW_PENDING: Non-zero means a DoWhenIdle handler
272 * has already been queued to redraw
274 * RESIZE_PENDING: Non-zero means a call to ComputeMenuGeometry
275 * has already been scheduled.
278 #define REDRAW_PENDING 1
279 #define RESIZE_PENDING 2
282 * Configuration specs valid for the menu as a whole:
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
,
335 * Forward declarations for procedures defined later in this file:
338 static int ActivateMenuEntry
_ANSI_ARGS_((Menu
*menuPtr
,
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
,
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
,
353 static int GetMenuIndex
_ANSI_ARGS_((Tcl_Interp
*interp
,
354 Menu
*menuPtr
, char *string
, int *indexPtr
));
355 static void MenuEventProc
_ANSI_ARGS_((ClientData clientData
,
357 static char * MenuVarProc
_ANSI_ARGS_((ClientData clientData
,
358 Tcl_Interp
*interp
, char *name1
, char *name2
,
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
));
366 *--------------------------------------------------------------
370 * This procedure is invoked to process the "menu" Tcl
371 * command. See the user documentation for details on
375 * A standard Tcl result.
378 * See the user documentation.
380 *--------------------------------------------------------------
384 Tk_MenuCmd(clientData
, interp
, argc
, argv
)
385 ClientData clientData
; /* Main window associated with
387 Tcl_Interp
*interp
; /* Current interpreter. */
388 int argc
; /* Number of arguments. */
389 char **argv
; /* Argument strings. */
391 Tk_Window tkwin
= (Tk_Window
) clientData
;
393 register Menu
*menuPtr
;
394 XSetWindowAttributes atts
;
397 Tcl_AppendResult(interp
, "wrong # args: should be \"",
398 argv
[0], " pathName ?options?\"", (char *) NULL
);
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
409 new = Tk_CreateWindowFromPath(interp
, tkwin
, argv
[1], "");
413 atts
.override_redirect
= True
;
414 atts
.save_under
= True
;
415 Tk_ChangeWindowAttributes(new, CWOverrideRedirect
|CWSaveUnder
, &atts
);
418 * Initialize the data structure for the menu.
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
;
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
;
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
) {
453 interp
->result
= Tk_PathName(menuPtr
->tkwin
);
457 Tk_DestroyWindow(menuPtr
->tkwin
);
462 *--------------------------------------------------------------
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.
471 * A standard Tcl result.
474 * See the user documentation.
476 *--------------------------------------------------------------
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. */
486 register Menu
*menuPtr
= (Menu
*) clientData
;
487 register MenuEntry
*mePtr
;
493 Tcl_AppendResult(interp
, "wrong # args: should be \"",
494 argv
[0], " option ?arg arg ...?\"", (char *) NULL
);
497 Tk_Preserve((ClientData
) menuPtr
);
499 length
= strlen(argv
[1]);
500 if ((c
== 'a') && (strncmp(argv
[1], "activate", length
) == 0)
505 Tcl_AppendResult(interp
, "wrong # args: should be \"",
506 argv
[0], " activate index\"", (char *) NULL
);
509 if (GetMenuIndex(interp
, menuPtr
, argv
[2], &index
) != TCL_OK
) {
512 if (menuPtr
->active
== index
) {
516 if ((menuPtr
->entries
[index
]->type
== SEPARATOR_ENTRY
)
517 || (menuPtr
->entries
[index
]->state
== tkDisabledUid
)) {
521 result
= ActivateMenuEntry(menuPtr
, index
);
522 } else if ((c
== 'a') && (strncmp(argv
[1], "add", length
) == 0)
524 MenuEntry
**newEntries
;
527 Tcl_AppendResult(interp
, "wrong # args: should be \"",
528 argv
[0], " add type ?options?\"", (char *) NULL
);
533 * Figure out the type of the new entry.
537 length
= strlen(argv
[2]);
538 if ((c
== 'c') && (strncmp(argv
[2], "cascade", length
) == 0)
540 type
= CASCADE_ENTRY
;
541 } else if ((c
== 'c') && (strncmp(argv
[2], "checkbutton", length
) == 0)
543 type
= CHECK_BUTTON_ENTRY
;
544 } else if ((c
== 'c') && (strncmp(argv
[2], "command", length
) == 0)
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
;
554 Tcl_AppendResult(interp
, "bad menu entry type \"",
555 argv
[2], "\": must be cascade, checkbutton, ",
556 "command, radiobutton, or separator", (char *) NULL
);
561 * Add a new entry to the end of the menu's array of entries,
562 * and process options for it.
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
);
573 menuPtr
->entries
= newEntries
;
574 menuPtr
->entries
[menuPtr
->numEntries
] = mePtr
;
575 menuPtr
->numEntries
++;
577 mePtr
->menuPtr
= menuPtr
;
579 mePtr
->underline
= -1;
580 mePtr
->bitmap
= None
;
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
;
591 mePtr
->onValue
= NULL
;
592 mePtr
->offValue
= NULL
;
594 if (ConfigureMenuEntry(interp
, menuPtr
, mePtr
, menuPtr
->numEntries
-1,
595 argc
-3, argv
+3, 0) != TCL_OK
) {
596 DestroyMenuEntry((ClientData
) mePtr
);
597 menuPtr
->numEntries
--;
600 } else if ((c
== 'c') && (strncmp(argv
[1], "configure", length
) == 0)) {
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);
608 result
= ConfigureMenu(interp
, menuPtr
, argc
-2, argv
+2,
609 TK_CONFIG_ARGV_ONLY
);
611 } else if ((c
== 'd') && (strncmp(argv
[1], "delete", length
) == 0)
616 Tcl_AppendResult(interp
, "wrong # args: should be \"",
617 argv
[0], " delete index\"", (char *) NULL
);
620 if (GetMenuIndex(interp
, menuPtr
, argv
[2], &index
) != TCL_OK
) {
626 Tk_EventuallyFree((ClientData
) menuPtr
->entries
[index
],
628 for (i
= index
; i
< menuPtr
->numEntries
-1; i
++) {
629 menuPtr
->entries
[i
] = menuPtr
->entries
[i
+1];
631 menuPtr
->numEntries
-= 1;
632 if (menuPtr
->active
== index
) {
633 menuPtr
->active
= -1;
634 } else if (menuPtr
->active
> index
) {
635 menuPtr
->active
-= 1;
637 if (!(menuPtr
->flags
& RESIZE_PENDING
)) {
638 menuPtr
->flags
|= RESIZE_PENDING
;
639 Tk_DoWhenIdle(ComputeMenuGeometry
, (ClientData
) menuPtr
);
641 } else if ((c
== 'd') && (strncmp(argv
[1], "disable", length
) == 0)
646 Tcl_AppendResult(interp
, "wrong # args: should be \"",
647 argv
[0], " disable index\"", (char *) NULL
);
650 if (GetMenuIndex(interp
, menuPtr
, argv
[2], &index
) != TCL_OK
) {
656 menuPtr
->entries
[index
]->state
= tkDisabledUid
;
657 if (menuPtr
->active
== index
) {
658 menuPtr
->active
= -1;
660 EventuallyRedrawMenu(menuPtr
, index
);
661 } else if ((c
== 'e') && (length
>= 3)
662 && (strncmp(argv
[1], "enable", length
) == 0)) {
666 Tcl_AppendResult(interp
, "wrong # args: should be \"",
667 argv
[0], " enable index\"", (char *) NULL
);
670 if (GetMenuIndex(interp
, menuPtr
, argv
[2], &index
) != TCL_OK
) {
676 menuPtr
->entries
[index
]->state
= tkNormalUid
;
677 EventuallyRedrawMenu(menuPtr
, index
);
678 } else if ((c
== 'e') && (length
>= 3)
679 && (strncmp(argv
[1], "entryconfigure", length
) == 0)) {
683 Tcl_AppendResult(interp
, "wrong # args: should be \"",
684 argv
[0], " entryconfigure index ?option value ...?\"",
688 if (GetMenuIndex(interp
, menuPtr
, argv
[2], &index
) != TCL_OK
) {
694 mePtr
= menuPtr
->entries
[index
];
695 Tk_Preserve((ClientData
) mePtr
);
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
);
704 result
= ConfigureMenuEntry(interp
, menuPtr
, mePtr
, index
, argc
-3,
705 argv
+3, TK_CONFIG_ARGV_ONLY
| COMMAND_MASK
<< mePtr
->type
);
707 Tk_Release((ClientData
) mePtr
);
708 } else if ((c
== 'i') && (strncmp(argv
[1], "index", length
) == 0)
713 Tcl_AppendResult(interp
, "wrong # args: should be \"",
714 argv
[0], " index string\"", (char *) NULL
);
717 if (GetMenuIndex(interp
, menuPtr
, argv
[2], &index
) != TCL_OK
) {
721 interp
->result
= "none";
723 sprintf(interp
->result
, "%d", index
);
725 } else if ((c
== 'i') && (strncmp(argv
[1], "invoke", length
) == 0)
730 Tcl_AppendResult(interp
, "wrong # args: should be \"",
731 argv
[0], " invoke index\"", (char *) NULL
);
734 if (GetMenuIndex(interp
, menuPtr
, argv
[2], &index
) != TCL_OK
) {
740 mePtr
= menuPtr
->entries
[index
];
741 if (mePtr
->state
== tkDisabledUid
) {
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
,
750 Tcl_SetVar(interp
, mePtr
->name
, mePtr
->onValue
,
753 } else if (mePtr
->type
== RADIO_BUTTON_ENTRY
) {
754 Tcl_SetVar(interp
, mePtr
->name
, mePtr
->onValue
, TCL_GLOBAL_ONLY
);
756 if (mePtr
->command
!= NULL
) {
757 result
= Tcl_GlobalEval(interp
, mePtr
->command
);
759 Tk_Release((ClientData
) mePtr
);
760 } else if ((c
== 'p') && (strncmp(argv
[1], "post", length
) == 0)) {
764 if ((argc
!= 4) && (argc
!= 5)) {
765 Tcl_AppendResult(interp
, "wrong # args: should be \"",
766 argv
[0], " post x y ?group?\"", (char *) NULL
);
769 if ((Tcl_GetInt(interp
, argv
[2], &x
) != TCL_OK
)
770 || (Tcl_GetInt(interp
, argv
[3], &y
) != TCL_OK
)) {
774 group
= Tk_GetUid(argv
[4]);
776 group
= Tk_GetUid("default");
780 * Adjust the position of the menu if necessary to keep it
784 tmp
= WidthOfScreen(Tk_Screen(menuPtr
->tkwin
))
785 - Tk_Width(menuPtr
->tkwin
);
792 tmp
= HeightOfScreen(Tk_Screen(menuPtr
->tkwin
))
793 - Tk_Height(menuPtr
->tkwin
);
800 if ((x
!= Tk_X(menuPtr
->tkwin
)) || (y
!= Tk_Y(menuPtr
->tkwin
))) {
801 Tk_MoveWindow(menuPtr
->tkwin
, x
, y
);
803 if (Tk_IsMapped(menuPtr
->tkwin
)) {
804 if (group
!= menuPtr
->group
) {
805 Tk_UnshareEvents(menuPtr
->tkwin
, menuPtr
->group
);
806 Tk_ShareEvents(menuPtr
->tkwin
, group
);
809 Tk_ShareEvents(menuPtr
->tkwin
, group
);
810 Tk_MapWindow(menuPtr
->tkwin
);
811 result
= ActivateMenuEntry(menuPtr
, -1);
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)) {
817 Tcl_AppendResult(interp
, "wrong # args: should be \"",
818 argv
[0], " unpost\"", (char *) NULL
);
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
);
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
);
835 Tk_Release((ClientData
) menuPtr
);
839 Tk_Release((ClientData
) menuPtr
);
844 *----------------------------------------------------------------------
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).
856 * Everything associated with the menu is freed up.
858 *----------------------------------------------------------------------
862 DestroyMenu(clientData
)
863 ClientData clientData
; /* Info about menu widget. */
865 register Menu
*menuPtr
= (Menu
*) clientData
;
868 for (i
= 0; i
< menuPtr
->numEntries
; i
++) {
869 DestroyMenuEntry((ClientData
) menuPtr
->entries
[i
]);
871 if (menuPtr
->entries
!= NULL
) {
872 ckfree((char *) menuPtr
->entries
);
874 if (menuPtr
->border
!= NULL
) {
875 Tk_Free3DBorder(menuPtr
->border
);
877 if (menuPtr
->activeBorder
!= NULL
) {
878 Tk_Free3DBorder(menuPtr
->activeBorder
);
880 if (menuPtr
->fontPtr
!= NULL
) {
881 Tk_FreeFontStruct(menuPtr
->fontPtr
);
883 if (menuPtr
->fg
!= NULL
) {
884 Tk_FreeColor(menuPtr
->fg
);
886 if (menuPtr
->textGC
!= None
) {
887 Tk_FreeGC(menuPtr
->textGC
);
889 if (menuPtr
->disabledFg
!= NULL
) {
890 Tk_FreeColor(menuPtr
->disabledFg
);
892 if (menuPtr
->gray
!= None
) {
893 Tk_FreeBitmap(menuPtr
->gray
);
895 if (menuPtr
->disabledGC
!= None
) {
896 Tk_FreeGC(menuPtr
->disabledGC
);
898 if (menuPtr
->activeFg
!= NULL
) {
899 Tk_FreeColor(menuPtr
->activeFg
);
901 if (menuPtr
->activeGC
!= None
) {
902 Tk_FreeGC(menuPtr
->activeGC
);
904 if (menuPtr
->selectorFg
!= NULL
) {
905 Tk_FreeColor(menuPtr
->selectorFg
);
907 if (menuPtr
->selectorGC
!= None
) {
908 Tk_FreeGC(menuPtr
->selectorGC
);
910 if (menuPtr
->cursor
!= None
) {
911 Tk_FreeCursor(menuPtr
->cursor
);
913 ckfree((char *) menuPtr
);
917 *----------------------------------------------------------------------
919 * DestroyMenuEntry --
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).
929 * Everything associated with the menu entry is freed up.
931 *----------------------------------------------------------------------
935 DestroyMenuEntry(clientData
)
936 ClientData clientData
; /* Pointer to entry to be freed. */
938 register MenuEntry
*mePtr
= (MenuEntry
*) clientData
;
939 Menu
*menuPtr
= mePtr
->menuPtr
;
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
);
946 if (menuPtr
->postedCascade
== mePtr
) {
947 if (PostSubmenu(menuPtr
->interp
, menuPtr
, (MenuEntry
*) NULL
)
949 TkBindError(menuPtr
->interp
);
952 if (mePtr
->label
!= NULL
) {
953 ckfree(mePtr
->label
);
955 if (mePtr
->bitmap
!= None
) {
956 #if defined(USE_XPM3)
957 Tk_FreePixmap(mePtr
->bitmap
);
959 Tk_FreeBitmap(mePtr
->bitmap
);
962 if (mePtr
->accel
!= NULL
) {
963 ckfree(mePtr
->accel
);
965 if (mePtr
->border
!= NULL
) {
966 Tk_Free3DBorder(mePtr
->border
);
968 if (mePtr
->activeBorder
!= NULL
) {
969 Tk_Free3DBorder(mePtr
->activeBorder
);
971 if (mePtr
->fontPtr
!= NULL
) {
972 Tk_FreeFontStruct(mePtr
->fontPtr
);
974 if (mePtr
->textGC
!= NULL
) {
975 Tk_FreeGC(mePtr
->textGC
);
977 if (mePtr
->activeGC
!= NULL
) {
978 Tk_FreeGC(mePtr
->activeGC
);
980 if (mePtr
->disabledGC
!= NULL
) {
981 Tk_FreeGC(mePtr
->disabledGC
);
983 if (mePtr
->command
!= NULL
) {
984 ckfree(mePtr
->command
);
986 if (mePtr
->name
!= NULL
) {
989 if (mePtr
->onValue
!= NULL
) {
990 ckfree(mePtr
->onValue
);
992 if (mePtr
->offValue
!= NULL
) {
993 ckfree(mePtr
->offValue
);
995 ckfree((char *) mePtr
);
999 *----------------------------------------------------------------------
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.
1008 * The return value is a standard Tcl result. If TCL_ERROR is
1009 * returned, then interp->result contains an error message.
1012 * Configuration information, such as colors, font, etc. get set
1013 * for menuPtr; old resources get freed, if there were any.
1015 *----------------------------------------------------------------------
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. */
1032 if (Tk_ConfigureWidget(interp
, menuPtr
->tkwin
, configSpecs
,
1033 argc
, argv
, (char *) menuPtr
, flags
) != TCL_OK
) {
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.
1043 Tk_SetBackgroundFromBorder(menuPtr
->tkwin
, menuPtr
->border
);
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
,
1050 if (menuPtr
->textGC
!= None
) {
1051 Tk_FreeGC(menuPtr
->textGC
);
1053 menuPtr
->textGC
= newGC
;
1055 if (menuPtr
->disabledFg
!= NULL
) {
1056 gcValues
.foreground
= menuPtr
->disabledFg
->pixel
;
1057 mask
= GCForeground
|GCBackground
|GCFont
;
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
) {
1067 gcValues
.fill_style
= FillStippled
;
1068 gcValues
.stipple
= menuPtr
->gray
;
1069 mask
= GCForeground
|GCFillStyle
|GCStipple
;
1071 newGC
= Tk_GetGC(menuPtr
->tkwin
, mask
, &gcValues
);
1072 if (menuPtr
->disabledGC
!= None
) {
1073 Tk_FreeGC(menuPtr
->disabledGC
);
1075 menuPtr
->disabledGC
= newGC
;
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
,
1082 if (menuPtr
->activeGC
!= None
) {
1083 Tk_FreeGC(menuPtr
->activeGC
);
1085 menuPtr
->activeGC
= newGC
;
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
);
1092 menuPtr
->selectorGC
= newGC
;
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
1101 for (i
= 0; i
< menuPtr
->numEntries
; i
++) {
1104 mePtr
= menuPtr
->entries
[i
];
1105 ConfigureMenuEntry(interp
, menuPtr
, mePtr
, i
, 0, (char **) NULL
,
1106 TK_CONFIG_ARGV_ONLY
| COMMAND_MASK
<< mePtr
->type
);
1109 if (!(menuPtr
->flags
& RESIZE_PENDING
)) {
1110 menuPtr
->flags
|= RESIZE_PENDING
;
1111 Tk_DoWhenIdle(ComputeMenuGeometry
, (ClientData
) menuPtr
);
1118 *----------------------------------------------------------------------
1120 * ConfigureMenuEntry --
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.
1127 * The return value is a standard Tcl result. If TCL_ERROR is
1128 * returned, then interp->result contains an error message.
1131 * Configuration information such as label and accelerator get
1132 * set for mePtr; old resources get freed, if there were any.
1134 *----------------------------------------------------------------------
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
1144 int index
; /* Index of mePtr within menuPtr's
1146 int argc
; /* Number of valid entries in argv. */
1147 char **argv
; /* Arguments. */
1148 int flags
; /* Additional flags to pass to
1149 * Tk_ConfigureWidget. */
1152 GC newGC
, newActiveGC
, newDisabledGC
;
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
1162 if (menuPtr
->postedCascade
== mePtr
) {
1163 if (PostSubmenu(menuPtr
->interp
, menuPtr
, (MenuEntry
*) NULL
)
1165 TkBindError(menuPtr
->interp
);
1170 * If this entry is a check button or radio button, then remove
1171 * its old trace procedure.
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
);
1182 if (Tk_ConfigureWidget(interp
, menuPtr
->tkwin
, entryConfigSpecs
,
1183 argc
, argv
, (char *) mePtr
,
1184 flags
| (COMMAND_MASK
<< mePtr
->type
)) != TCL_OK
) {
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.
1194 if (mePtr
->label
== NULL
) {
1195 mePtr
->labelLength
= 0;
1197 mePtr
->labelLength
= strlen(mePtr
->label
);
1199 if (mePtr
->accel
== NULL
) {
1200 mePtr
->accelLength
= 0;
1202 mePtr
->accelLength
= strlen(mePtr
->accel
);
1205 if (mePtr
->state
== tkActiveUid
) {
1206 if (index
!= menuPtr
->active
) {
1207 ActivateMenuEntry(menuPtr
, index
);
1210 if (index
== menuPtr
->active
) {
1211 ActivateMenuEntry(menuPtr
, -1);
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
;
1221 if (mePtr
->fontPtr
!= NULL
) {
1222 gcValues
.foreground
= menuPtr
->fg
->pixel
;
1223 gcValues
.background
= Tk_3DBorderColor(
1224 (mePtr
->border
!= NULL
) ? mePtr
->border
: menuPtr
->border
)
1226 gcValues
.font
= mePtr
->fontPtr
->fid
;
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.
1234 gcValues
.graphics_exposures
= False
;
1235 newGC
= Tk_GetGC(menuPtr
->tkwin
,
1236 GCForeground
|GCBackground
|GCFont
|GCGraphicsExposures
,
1239 if (menuPtr
->disabledFg
!= NULL
) {
1240 gcValues
.foreground
= menuPtr
->disabledFg
->pixel
;
1241 mask
= GCForeground
|GCBackground
|GCFont
|GCGraphicsExposures
;
1243 gcValues
.foreground
= gcValues
.background
;
1244 gcValues
.fill_style
= FillStippled
;
1245 gcValues
.stipple
= menuPtr
->gray
;
1246 mask
= GCForeground
|GCFillStyle
|GCStipple
;
1248 newDisabledGC
= Tk_GetGC(menuPtr
->tkwin
, mask
, &gcValues
);
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
,
1260 newDisabledGC
= NULL
;
1262 if (mePtr
->textGC
!= NULL
) {
1263 Tk_FreeGC(mePtr
->textGC
);
1265 mePtr
->textGC
= newGC
;
1266 if (mePtr
->activeGC
!= NULL
) {
1267 Tk_FreeGC(mePtr
->activeGC
);
1269 mePtr
->activeGC
= newActiveGC
;
1270 if (mePtr
->disabledGC
!= NULL
) {
1271 Tk_FreeGC(mePtr
->disabledGC
);
1273 mePtr
->disabledGC
= newDisabledGC
;
1275 if ((mePtr
->type
== CHECK_BUTTON_ENTRY
)
1276 || (mePtr
->type
== RADIO_BUTTON_ENTRY
)) {
1279 if (mePtr
->name
== NULL
) {
1280 mePtr
->name
= ckalloc((unsigned) (strlen(mePtr
->label
) + 1));
1281 strcpy(mePtr
->name
, mePtr
->label
);
1283 if (mePtr
->onValue
== NULL
) {
1284 mePtr
->onValue
= ckalloc((unsigned) (strlen(mePtr
->label
) + 1));
1285 strcpy(mePtr
->onValue
, mePtr
->label
);
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.
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
;
1302 Tcl_SetVar(interp
, mePtr
->name
,
1303 (mePtr
->type
== CHECK_BUTTON_ENTRY
) ? mePtr
->offValue
: "",
1306 Tcl_TraceVar(interp
, mePtr
->name
,
1307 TCL_GLOBAL_ONLY
|TCL_TRACE_WRITES
|TCL_TRACE_UNSETS
,
1308 MenuVarProc
, (ClientData
) mePtr
);
1311 if (!(menuPtr
->flags
& RESIZE_PENDING
)) {
1312 menuPtr
->flags
|= RESIZE_PENDING
;
1313 Tk_DoWhenIdle(ComputeMenuGeometry
, (ClientData
) menuPtr
);
1319 *--------------------------------------------------------------
1321 * ComputeMenuGeometry --
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
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.
1336 *--------------------------------------------------------------
1340 ComputeMenuGeometry(clientData
)
1341 ClientData clientData
; /* Structure describing menu. */
1343 Menu
*menuPtr
= (Menu
*) clientData
;
1344 register MenuEntry
*mePtr
;
1345 XFontStruct
*fontPtr
;
1346 int maxLabelWidth
, maxSelectorWidth
, maxAccelWidth
;
1347 int width
, height
, selectorSpace
, horizMargin
;
1350 if (menuPtr
->tkwin
== NULL
) {
1354 maxLabelWidth
= maxSelectorWidth
= maxAccelWidth
= 0;
1355 y
= menuPtr
->borderWidth
;
1357 for (i
= 0; i
< menuPtr
->numEntries
; i
++) {
1358 mePtr
= menuPtr
->entries
[i
];
1360 fontPtr
= mePtr
->fontPtr
;
1361 if (fontPtr
== NULL
) {
1362 fontPtr
= menuPtr
->fontPtr
;
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
1375 if (mePtr
->bitmap
!= None
) {
1376 unsigned int bitmapWidth
, bitmapHeight
;
1378 #if defined(USE_XPM3)
1379 Tk_SizeOfPixmap(mePtr
->bitmap
, &bitmapWidth
, &bitmapHeight
);
1381 Tk_SizeOfBitmap(mePtr
->bitmap
, &bitmapWidth
, &bitmapHeight
);
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;
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
);
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
;
1409 mePtr
->height
+= 2*menuPtr
->activeBorderWidth
+ 2;
1410 if (width
> maxLabelWidth
) {
1411 maxLabelWidth
= width
;
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
;
1420 if (mePtr
->type
== SEPARATOR_ENTRY
) {
1421 mePtr
->height
= 4*menuPtr
->borderWidth
;
1423 if (selectorSpace
> maxSelectorWidth
) {
1424 maxSelectorWidth
= selectorSpace
;
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).
1439 menuPtr
->selectorSpace
= maxSelectorWidth
+ horizMargin
;
1440 if (maxSelectorWidth
!= 0) {
1441 menuPtr
->selectorSpace
+= horizMargin
;
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
;
1449 height
= y
+ menuPtr
->borderWidth
;
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).
1462 if ((width
!= Tk_ReqWidth(menuPtr
->tkwin
)) ||
1463 (height
!= Tk_ReqHeight(menuPtr
->tkwin
))) {
1464 Tk_GeometryRequest(menuPtr
->tkwin
, width
, height
);
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.
1473 EventuallyRedrawMenu(menuPtr
, -1);
1476 menuPtr
->flags
&= ~RESIZE_PENDING
;
1480 *----------------------------------------------------------------------
1484 * This procedure is invoked to display a menu widget.
1490 * Commands are output to X to display the menu in its
1493 *----------------------------------------------------------------------
1497 DisplayMenu(clientData
)
1498 ClientData clientData
; /* Information about widget. */
1500 register Menu
*menuPtr
= (Menu
*) clientData
;
1501 register MenuEntry
*mePtr
;
1502 register Tk_Window tkwin
= menuPtr
->tkwin
;
1503 XFontStruct
*fontPtr
;
1504 int index
, baseline
;
1507 menuPtr
->flags
&= ~REDRAW_PENDING
;
1508 if ((menuPtr
->tkwin
== NULL
) || !Tk_IsMapped(tkwin
)) {
1513 * Loop through all of the entries, drawing them one at a time.
1516 for (index
= 0; index
< menuPtr
->numEntries
; index
++) {
1517 mePtr
= menuPtr
->entries
[index
];
1518 if (!(mePtr
->flags
& ENTRY_NEEDS_REDISPLAY
)) {
1521 mePtr
->flags
&= ~ENTRY_NEEDS_REDISPLAY
;
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
;
1535 gc
= menuPtr
->activeGC
;
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
,
1543 if ((mePtr
->state
== tkDisabledUid
)
1544 && (menuPtr
->disabledFg
!= NULL
)) {
1545 gc
= mePtr
->disabledGC
;
1547 gc
= menuPtr
->disabledGC
;
1552 gc
= menuPtr
->textGC
;
1558 * Draw label or bitmap for entry.
1561 fontPtr
= mePtr
->fontPtr
;
1562 if (fontPtr
== NULL
) {
1563 fontPtr
= menuPtr
->fontPtr
;
1565 baseline
= mePtr
->y
+ (mePtr
->height
+ fontPtr
->ascent
1566 - fontPtr
->descent
)/2;
1567 if (mePtr
->bitmap
!= None
) {
1568 unsigned int width
, height
;
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));
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);
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
);
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
);
1613 * Draw check-button selector.
1616 if (mePtr
->type
== CHECK_BUTTON_ENTRY
) {
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
);
1636 * Draw radio-button selector.
1639 if (mePtr
->type
== RADIO_BUTTON_ENTRY
) {
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
,
1658 Tk_Fill3DPolygon(Tk_Display(tkwin
), Tk_WindowId(tkwin
),
1659 menuPtr
->border
, points
, 4, menuPtr
->activeBorderWidth
,
1662 Tk_Draw3DPolygon(Tk_Display(tkwin
), Tk_WindowId(tkwin
),
1663 menuPtr
->border
, points
, 4, menuPtr
->activeBorderWidth
,
1671 if (mePtr
->type
== SEPARATOR_ENTRY
) {
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
);
1685 * If the entry is disabled with a stipple rather than a special
1686 * foreground color, generate the stippled effect.
1689 if ((mePtr
->state
== tkDisabledUid
) && (menuPtr
->disabledFg
== NULL
)) {
1690 XFillRectangle(Tk_Display(tkwin
), Tk_WindowId(tkwin
),
1691 menuPtr
->disabledGC
, menuPtr
->borderWidth
,
1693 (unsigned) (Tk_Width(tkwin
) - 2*menuPtr
->borderWidth
),
1694 (unsigned) mePtr
->height
);
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
);
1704 *--------------------------------------------------------------
1708 * Parse a textual index into a menu and return the numerical
1709 * index of the indicated entry.
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.
1720 *--------------------------------------------------------------
1724 GetMenuIndex(interp
, menuPtr
, string
, indexPtr
)
1725 Tcl_Interp
*interp
; /* For error messages. */
1726 Menu
*menuPtr
; /* Menu for which the index is being
1728 char *string
; /* Specification of an entry in menu. See
1729 * manual entry for valid .*/
1730 int *indexPtr
; /* Where to store converted relief. */
1734 if ((string
[0] == 'a') && (strcmp(string
, "active") == 0)) {
1735 *indexPtr
= menuPtr
->active
;
1739 if ((string
[0] == 'l') && (strcmp(string
, "last") == 0)) {
1740 *indexPtr
= menuPtr
->numEntries
-1;
1744 if ((string
[0] == 'n') && (strcmp(string
, "none") == 0)) {
1749 if (string
[0] == '@') {
1750 if (Tcl_GetInt(interp
, string
+1, &y
) == TCL_OK
) {
1755 for (i
= 0; i
< menuPtr
->numEntries
; i
++) {
1756 y
-= menuPtr
->entries
[i
]->height
;
1761 if (i
>= menuPtr
->numEntries
) {
1767 Tcl_SetResult(interp
, (char *) NULL
, TCL_STATIC
);
1771 if (isdigit(string
[0])) {
1772 if (Tcl_GetInt(interp
, string
, &i
) == TCL_OK
) {
1773 if ((i
< menuPtr
->numEntries
) && (i
>= 0)) {
1778 Tcl_SetResult(interp
, (char *) NULL
, TCL_STATIC
);
1782 for (i
= 0; i
< menuPtr
->numEntries
; i
++) {
1785 label
= menuPtr
->entries
[i
]->label
;
1787 && (Tcl_StringMatch(menuPtr
->entries
[i
]->label
, string
))) {
1793 Tcl_AppendResult(interp
, "bad menu entry index \"",
1794 string
, "\"", (char *) NULL
);
1799 *--------------------------------------------------------------
1803 * This procedure is invoked by the Tk dispatcher for various
1810 * When the window gets deleted, internal structures get
1811 * cleaned up. When it gets exposed, it is redisplayed.
1813 *--------------------------------------------------------------
1817 MenuEventProc(clientData
, eventPtr
)
1818 ClientData clientData
; /* Information about window. */
1819 XEvent
*eventPtr
; /* Information about event. */
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
));
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.
1834 Tk_UnshareEvents(menuPtr
->tkwin
, menuPtr
->group
);
1835 menuPtr
->tkwin
= NULL
;
1836 if (menuPtr
->flags
& REDRAW_PENDING
) {
1837 Tk_CancelIdleCall(DisplayMenu
, (ClientData
) menuPtr
);
1839 if (menuPtr
->flags
& RESIZE_PENDING
) {
1840 Tk_CancelIdleCall(ComputeMenuGeometry
, (ClientData
) menuPtr
);
1842 Tk_EventuallyFree((ClientData
) menuPtr
, DestroyMenu
);
1847 *--------------------------------------------------------------
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.
1857 * NULL is always returned.
1860 * The menu entry may become selected or deselected.
1862 *--------------------------------------------------------------
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. */
1874 MenuEntry
*mePtr
= (MenuEntry
*) clientData
;
1878 menuPtr
= mePtr
->menuPtr
;
1881 * If the variable is being unset, then re-establish the
1882 * trace unless the whole interpreter is going away.
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
);
1892 EventuallyRedrawMenu(menuPtr
, -1);
1893 return (char *) NULL
;
1897 * Use the value of the variable to update the selected status of
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
;
1906 mePtr
->flags
|= ENTRY_SELECTED
;
1907 } else if (mePtr
->flags
& ENTRY_SELECTED
) {
1908 mePtr
->flags
&= ~ENTRY_SELECTED
;
1910 return (char *) NULL
;
1912 EventuallyRedrawMenu(menuPtr
, -1);
1913 return (char *) NULL
;
1917 *----------------------------------------------------------------------
1919 * EventuallyRedrawMenu --
1921 * Arrange for an entry of a menu, or the whole menu, to be
1922 * redisplayed at some point in the future.
1928 * A when-idle hander is scheduled to do the redisplay, if there
1929 * isn't one already scheduled.
1931 *----------------------------------------------------------------------
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. */
1940 if (menuPtr
->tkwin
== NULL
) {
1944 menuPtr
->entries
[index
]->flags
|= ENTRY_NEEDS_REDISPLAY
;
1946 for (index
= 0; index
< menuPtr
->numEntries
; index
++) {
1947 menuPtr
->entries
[index
]->flags
|= ENTRY_NEEDS_REDISPLAY
;
1950 if ((menuPtr
->tkwin
== NULL
) || !Tk_IsMapped(menuPtr
->tkwin
)
1951 || (menuPtr
->flags
& REDRAW_PENDING
)) {
1954 Tk_DoWhenIdle(DisplayMenu
, (ClientData
) menuPtr
);
1955 menuPtr
->flags
|= REDRAW_PENDING
;
1959 *--------------------------------------------------------------
1963 * This procedure arranges for a particular submenu (i.e. the
1964 * menu corresponding to a given cascade entry) to be
1968 * A standard Tcl return result. Errors may occur in the
1969 * Tcl commands generated to post and unpost submenus.
1972 * If there is already a submenu posted, it is unposted.
1973 * The new submenu is then posted.
1975 *--------------------------------------------------------------
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. */
1990 if (mePtr
== menuPtr
->postedCascade
) {
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
) {
2003 if ((mePtr
!= NULL
) && (mePtr
->name
!= NULL
)) {
2004 Tk_GetRootCoords(menuPtr
->tkwin
, &x
, &y
);
2005 x
+= Tk_Width(menuPtr
->tkwin
);
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
) {
2013 menuPtr
->postedCascade
= mePtr
;
2019 *----------------------------------------------------------------------
2021 * ActivateMenuEntry --
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.
2028 * The return value is a standard Tcl result (errors can occur
2029 * while posting and unposting submenus).
2032 * Menu entries get redisplayed, and the active entry changes.
2033 * Submenus may get posted and unposted.
2035 *----------------------------------------------------------------------
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. */
2044 register MenuEntry
*mePtr
;
2045 int result
= TCL_OK
;
2047 if (menuPtr
->active
>= 0) {
2048 mePtr
= menuPtr
->entries
[menuPtr
->active
];
2051 * Don't change the state unless it's currently active (state
2052 * might already have been changed to disabled).
2055 if (mePtr
->state
== tkActiveUid
) {
2056 mePtr
->state
= tkNormalUid
;
2058 EventuallyRedrawMenu(menuPtr
, menuPtr
->active
);
2060 menuPtr
->active
= index
;
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
);
2069 result
= PostSubmenu(menuPtr
->interp
, menuPtr
, (MenuEntry
*) NULL
);
2071 Tk_Release((ClientData
) mePtr
);