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 *--------------------------------------------------------------
385 ClientData clientData
, /* Main window associated with
387 Tcl_Interp
*interp
, /* Current interpreter. */
388 int argc
, /* Number of arguments. */
389 char **argv
/* Argument strings. */
392 Tk_Window tkwin
= (Tk_Window
) clientData
;
394 register Menu
*menuPtr
;
395 XSetWindowAttributes atts
;
398 Tcl_AppendResult(interp
, "wrong # args: should be \"",
399 argv
[0], " pathName ?options?\"", (char *) NULL
);
404 * Create the new window. Set override-redirect so the window
405 * manager won't add a border or argue about placement, and set
406 * save-under so that the window can pop up and down without a
410 new = Tk_CreateWindowFromPath(interp
, tkwin
, argv
[1], "");
414 atts
.override_redirect
= True
;
415 atts
.save_under
= True
;
416 Tk_ChangeWindowAttributes(new, CWOverrideRedirect
|CWSaveUnder
, &atts
);
419 * Initialize the data structure for the menu.
422 menuPtr
= (Menu
*) ckalloc(sizeof(Menu
));
423 menuPtr
->tkwin
= new;
424 menuPtr
->interp
= interp
;
425 menuPtr
->entries
= NULL
;
426 menuPtr
->numEntries
= 0;
427 menuPtr
->active
= -1;
428 menuPtr
->group
= NULL
;
429 menuPtr
->border
= NULL
;
430 menuPtr
->activeBorder
= NULL
;
431 menuPtr
->fontPtr
= NULL
;
433 menuPtr
->textGC
= None
;
434 menuPtr
->disabledFg
= NULL
;
435 menuPtr
->gray
= None
;
436 menuPtr
->disabledGC
= None
;
437 menuPtr
->activeFg
= NULL
;
438 menuPtr
->activeGC
= None
;
439 menuPtr
->selectorFg
= NULL
;
440 menuPtr
->selectorGC
= None
;
441 menuPtr
->cursor
= None
;
442 menuPtr
->postedCascade
= NULL
;
445 Tk_SetClass(new, "Menu");
446 Tk_CreateEventHandler(menuPtr
->tkwin
, ExposureMask
|StructureNotifyMask
,
447 MenuEventProc
, (ClientData
) menuPtr
);
448 Tcl_CreateCommand(interp
, Tk_PathName(menuPtr
->tkwin
), MenuWidgetCmd
,
449 (ClientData
) menuPtr
, (void (*)(int *)) NULL
);
450 if (ConfigureMenu(interp
, menuPtr
, argc
-2, argv
+2, 0) != TCL_OK
) {
454 interp
->result
= Tk_PathName(menuPtr
->tkwin
);
458 Tk_DestroyWindow(menuPtr
->tkwin
);
463 *--------------------------------------------------------------
467 * This procedure is invoked to process the Tcl command
468 * that corresponds to a widget managed by this module.
469 * See the user documentation for details on what it does.
472 * A standard Tcl result.
475 * See the user documentation.
477 *--------------------------------------------------------------
482 ClientData clientData
, /* Information about menu widget. */
483 Tcl_Interp
*interp
, /* Current interpreter. */
484 int argc
, /* Number of arguments. */
485 char **argv
/* Argument strings. */
488 register Menu
*menuPtr
= (Menu
*) clientData
;
489 register MenuEntry
*mePtr
;
495 Tcl_AppendResult(interp
, "wrong # args: should be \"",
496 argv
[0], " option ?arg arg ...?\"", (char *) NULL
);
499 Tk_Preserve((ClientData
) menuPtr
);
501 length
= strlen(argv
[1]);
502 if ((c
== 'a') && (strncmp(argv
[1], "activate", length
) == 0)
507 Tcl_AppendResult(interp
, "wrong # args: should be \"",
508 argv
[0], " activate index\"", (char *) NULL
);
511 if (GetMenuIndex(interp
, menuPtr
, argv
[2], &index
) != TCL_OK
) {
514 if (menuPtr
->active
== index
) {
518 if ((menuPtr
->entries
[index
]->type
== SEPARATOR_ENTRY
)
519 || (menuPtr
->entries
[index
]->state
== tkDisabledUid
)) {
523 result
= ActivateMenuEntry(menuPtr
, index
);
524 } else if ((c
== 'a') && (strncmp(argv
[1], "add", length
) == 0)
526 MenuEntry
**newEntries
;
529 Tcl_AppendResult(interp
, "wrong # args: should be \"",
530 argv
[0], " add type ?options?\"", (char *) NULL
);
535 * Figure out the type of the new entry.
539 length
= strlen(argv
[2]);
540 if ((c
== 'c') && (strncmp(argv
[2], "cascade", length
) == 0)
542 type
= CASCADE_ENTRY
;
543 } else if ((c
== 'c') && (strncmp(argv
[2], "checkbutton", length
) == 0)
545 type
= CHECK_BUTTON_ENTRY
;
546 } else if ((c
== 'c') && (strncmp(argv
[2], "command", length
) == 0)
548 type
= COMMAND_ENTRY
;
549 } else if ((c
== 'r')
550 && (strncmp(argv
[2], "radiobutton", length
) == 0)) {
551 type
= RADIO_BUTTON_ENTRY
;
552 } else if ((c
== 's')
553 && (strncmp(argv
[2], "separator", length
) == 0)) {
554 type
= SEPARATOR_ENTRY
;
556 Tcl_AppendResult(interp
, "bad menu entry type \"",
557 argv
[2], "\": must be cascade, checkbutton, ",
558 "command, radiobutton, or separator", (char *) NULL
);
563 * Add a new entry to the end of the menu's array of entries,
564 * and process options for it.
567 mePtr
= (MenuEntry
*) ckalloc(sizeof(MenuEntry
));
568 newEntries
= (MenuEntry
**) ckalloc((unsigned)
569 ((menuPtr
->numEntries
+1)*sizeof(MenuEntry
*)));
570 if (menuPtr
->numEntries
!= 0) {
571 memcpy((VOID
*) newEntries
, (VOID
*) menuPtr
->entries
,
572 menuPtr
->numEntries
*sizeof(MenuEntry
*));
573 ckfree((char *) menuPtr
->entries
);
575 menuPtr
->entries
= newEntries
;
576 menuPtr
->entries
[menuPtr
->numEntries
] = mePtr
;
577 menuPtr
->numEntries
++;
579 mePtr
->menuPtr
= menuPtr
;
581 mePtr
->underline
= -1;
582 mePtr
->bitmap
= None
;
584 mePtr
->state
= tkNormalUid
;
585 mePtr
->border
= NULL
;
586 mePtr
->activeBorder
= NULL
;
587 mePtr
->fontPtr
= NULL
;
588 mePtr
->textGC
= None
;
589 mePtr
->activeGC
= None
;
590 mePtr
->disabledGC
= None
;
591 mePtr
->command
= NULL
;
593 mePtr
->onValue
= NULL
;
594 mePtr
->offValue
= NULL
;
596 if (ConfigureMenuEntry(interp
, menuPtr
, mePtr
, menuPtr
->numEntries
-1,
597 argc
-3, argv
+3, 0) != TCL_OK
) {
598 DestroyMenuEntry((ClientData
) mePtr
);
599 menuPtr
->numEntries
--;
602 } else if ((c
== 'c') && (strncmp(argv
[1], "configure", length
) == 0)) {
604 result
= Tk_ConfigureInfo(interp
, menuPtr
->tkwin
, configSpecs
,
605 (char *) menuPtr
, (char *) NULL
, 0);
606 } else if (argc
== 3) {
607 result
= Tk_ConfigureInfo(interp
, menuPtr
->tkwin
, configSpecs
,
608 (char *) menuPtr
, argv
[2], 0);
610 result
= ConfigureMenu(interp
, menuPtr
, argc
-2, argv
+2,
611 TK_CONFIG_ARGV_ONLY
);
613 } else if ((c
== 'd') && (strncmp(argv
[1], "delete", length
) == 0)
618 Tcl_AppendResult(interp
, "wrong # args: should be \"",
619 argv
[0], " delete index\"", (char *) NULL
);
622 if (GetMenuIndex(interp
, menuPtr
, argv
[2], &index
) != TCL_OK
) {
628 Tk_EventuallyFree((ClientData
) menuPtr
->entries
[index
],
630 for (i
= index
; i
< menuPtr
->numEntries
-1; i
++) {
631 menuPtr
->entries
[i
] = menuPtr
->entries
[i
+1];
633 menuPtr
->numEntries
-= 1;
634 if (menuPtr
->active
== index
) {
635 menuPtr
->active
= -1;
636 } else if (menuPtr
->active
> index
) {
637 menuPtr
->active
-= 1;
639 if (!(menuPtr
->flags
& RESIZE_PENDING
)) {
640 menuPtr
->flags
|= RESIZE_PENDING
;
641 Tk_DoWhenIdle(ComputeMenuGeometry
, (ClientData
) menuPtr
);
643 } else if ((c
== 'd') && (strncmp(argv
[1], "disable", length
) == 0)
648 Tcl_AppendResult(interp
, "wrong # args: should be \"",
649 argv
[0], " disable index\"", (char *) NULL
);
652 if (GetMenuIndex(interp
, menuPtr
, argv
[2], &index
) != TCL_OK
) {
658 menuPtr
->entries
[index
]->state
= tkDisabledUid
;
659 if (menuPtr
->active
== index
) {
660 menuPtr
->active
= -1;
662 EventuallyRedrawMenu(menuPtr
, index
);
663 } else if ((c
== 'e') && (length
>= 3)
664 && (strncmp(argv
[1], "enable", length
) == 0)) {
668 Tcl_AppendResult(interp
, "wrong # args: should be \"",
669 argv
[0], " enable index\"", (char *) NULL
);
672 if (GetMenuIndex(interp
, menuPtr
, argv
[2], &index
) != TCL_OK
) {
678 menuPtr
->entries
[index
]->state
= tkNormalUid
;
679 EventuallyRedrawMenu(menuPtr
, index
);
680 } else if ((c
== 'e') && (length
>= 3)
681 && (strncmp(argv
[1], "entryconfigure", length
) == 0)) {
685 Tcl_AppendResult(interp
, "wrong # args: should be \"",
686 argv
[0], " entryconfigure index ?option value ...?\"",
690 if (GetMenuIndex(interp
, menuPtr
, argv
[2], &index
) != TCL_OK
) {
696 mePtr
= menuPtr
->entries
[index
];
697 Tk_Preserve((ClientData
) mePtr
);
699 result
= Tk_ConfigureInfo(interp
, menuPtr
->tkwin
, entryConfigSpecs
,
700 (char *) mePtr
, (char *) NULL
,
701 COMMAND_MASK
<< mePtr
->type
);
702 } else if (argc
== 4) {
703 result
= Tk_ConfigureInfo(interp
, menuPtr
->tkwin
, entryConfigSpecs
,
704 (char *) mePtr
, argv
[3], COMMAND_MASK
<< mePtr
->type
);
706 result
= ConfigureMenuEntry(interp
, menuPtr
, mePtr
, index
, argc
-3,
707 argv
+3, TK_CONFIG_ARGV_ONLY
| COMMAND_MASK
<< mePtr
->type
);
709 Tk_Release((ClientData
) mePtr
);
710 } else if ((c
== 'i') && (strncmp(argv
[1], "index", length
) == 0)
715 Tcl_AppendResult(interp
, "wrong # args: should be \"",
716 argv
[0], " index string\"", (char *) NULL
);
719 if (GetMenuIndex(interp
, menuPtr
, argv
[2], &index
) != TCL_OK
) {
723 interp
->result
= "none";
725 sprintf(interp
->result
, "%d", index
);
727 } else if ((c
== 'i') && (strncmp(argv
[1], "invoke", length
) == 0)
732 Tcl_AppendResult(interp
, "wrong # args: should be \"",
733 argv
[0], " invoke index\"", (char *) NULL
);
736 if (GetMenuIndex(interp
, menuPtr
, argv
[2], &index
) != TCL_OK
) {
742 mePtr
= menuPtr
->entries
[index
];
743 if (mePtr
->state
== tkDisabledUid
) {
746 Tk_Preserve((ClientData
) mePtr
);
747 if (mePtr
->type
== CHECK_BUTTON_ENTRY
) {
748 if (mePtr
->flags
& ENTRY_SELECTED
) {
749 Tcl_SetVar(interp
, mePtr
->name
, mePtr
->offValue
,
752 Tcl_SetVar(interp
, mePtr
->name
, mePtr
->onValue
,
755 } else if (mePtr
->type
== RADIO_BUTTON_ENTRY
) {
756 Tcl_SetVar(interp
, mePtr
->name
, mePtr
->onValue
, TCL_GLOBAL_ONLY
);
758 if (mePtr
->command
!= NULL
) {
759 result
= Tcl_GlobalEval(interp
, mePtr
->command
);
761 Tk_Release((ClientData
) mePtr
);
762 } else if ((c
== 'p') && (strncmp(argv
[1], "post", length
) == 0)) {
766 if ((argc
!= 4) && (argc
!= 5)) {
767 Tcl_AppendResult(interp
, "wrong # args: should be \"",
768 argv
[0], " post x y ?group?\"", (char *) NULL
);
771 if ((Tcl_GetInt(interp
, argv
[2], &x
) != TCL_OK
)
772 || (Tcl_GetInt(interp
, argv
[3], &y
) != TCL_OK
)) {
776 group
= Tk_GetUid(argv
[4]);
778 group
= Tk_GetUid("default");
782 * Adjust the position of the menu if necessary to keep it
786 tmp
= WidthOfScreen(Tk_Screen(menuPtr
->tkwin
))
787 - Tk_Width(menuPtr
->tkwin
);
794 tmp
= HeightOfScreen(Tk_Screen(menuPtr
->tkwin
))
795 - Tk_Height(menuPtr
->tkwin
);
802 if ((x
!= Tk_X(menuPtr
->tkwin
)) || (y
!= Tk_Y(menuPtr
->tkwin
))) {
803 Tk_MoveWindow(menuPtr
->tkwin
, x
, y
);
805 if (Tk_IsMapped(menuPtr
->tkwin
)) {
806 if (group
!= menuPtr
->group
) {
807 Tk_UnshareEvents(menuPtr
->tkwin
, menuPtr
->group
);
808 Tk_ShareEvents(menuPtr
->tkwin
, group
);
811 Tk_ShareEvents(menuPtr
->tkwin
, group
);
812 Tk_MapWindow(menuPtr
->tkwin
);
813 result
= ActivateMenuEntry(menuPtr
, -1);
815 XRaiseWindow(Tk_Display(menuPtr
->tkwin
), Tk_WindowId(menuPtr
->tkwin
));
816 menuPtr
->group
= group
;
817 } else if ((c
== 'u') && (strncmp(argv
[1], "unpost", length
) == 0)) {
819 Tcl_AppendResult(interp
, "wrong # args: should be \"",
820 argv
[0], " unpost\"", (char *) NULL
);
823 Tk_UnshareEvents(menuPtr
->tkwin
, menuPtr
->group
);
824 Tk_UnmapWindow(menuPtr
->tkwin
);
825 result
= ActivateMenuEntry(menuPtr
, -1);
826 if (result
== TCL_OK
) {
827 result
= PostSubmenu(interp
, menuPtr
, (MenuEntry
*) NULL
);
830 Tcl_AppendResult(interp
, "bad option \"", argv
[1],
831 "\": must be activate, add, configure, delete, disable, ",
832 "enable, entryconfigure, index, invoke, post, ",
833 "or unpost", (char *) NULL
);
837 Tk_Release((ClientData
) menuPtr
);
841 Tk_Release((ClientData
) menuPtr
);
846 *----------------------------------------------------------------------
850 * This procedure is invoked by Tk_EventuallyFree or Tk_Release
851 * to clean up the internal structure of a menu at a safe time
852 * (when no-one is using it anymore).
858 * Everything associated with the menu is freed up.
860 *----------------------------------------------------------------------
865 ClientData clientData
/* Info about menu widget. */
868 register Menu
*menuPtr
= (Menu
*) clientData
;
871 for (i
= 0; i
< menuPtr
->numEntries
; i
++) {
872 DestroyMenuEntry((ClientData
) menuPtr
->entries
[i
]);
874 if (menuPtr
->entries
!= NULL
) {
875 ckfree((char *) menuPtr
->entries
);
877 if (menuPtr
->border
!= NULL
) {
878 Tk_Free3DBorder(menuPtr
->border
);
880 if (menuPtr
->activeBorder
!= NULL
) {
881 Tk_Free3DBorder(menuPtr
->activeBorder
);
883 if (menuPtr
->fontPtr
!= NULL
) {
884 Tk_FreeFontStruct(menuPtr
->fontPtr
);
886 if (menuPtr
->fg
!= NULL
) {
887 Tk_FreeColor(menuPtr
->fg
);
889 if (menuPtr
->textGC
!= None
) {
890 Tk_FreeGC(menuPtr
->textGC
);
892 if (menuPtr
->disabledFg
!= NULL
) {
893 Tk_FreeColor(menuPtr
->disabledFg
);
895 if (menuPtr
->gray
!= None
) {
896 Tk_FreeBitmap(menuPtr
->gray
);
898 if (menuPtr
->disabledGC
!= None
) {
899 Tk_FreeGC(menuPtr
->disabledGC
);
901 if (menuPtr
->activeFg
!= NULL
) {
902 Tk_FreeColor(menuPtr
->activeFg
);
904 if (menuPtr
->activeGC
!= None
) {
905 Tk_FreeGC(menuPtr
->activeGC
);
907 if (menuPtr
->selectorFg
!= NULL
) {
908 Tk_FreeColor(menuPtr
->selectorFg
);
910 if (menuPtr
->selectorGC
!= None
) {
911 Tk_FreeGC(menuPtr
->selectorGC
);
913 if (menuPtr
->cursor
!= None
) {
914 Tk_FreeCursor(menuPtr
->cursor
);
916 ckfree((char *) menuPtr
);
920 *----------------------------------------------------------------------
922 * DestroyMenuEntry --
924 * This procedure is invoked by Tk_EventuallyFree or Tk_Release
925 * to clean up the internal structure of a menu entry at a safe time
926 * (when no-one is using it anymore).
932 * Everything associated with the menu entry is freed up.
934 *----------------------------------------------------------------------
939 ClientData clientData
/* Pointer to entry to be freed. */
942 register MenuEntry
*mePtr
= (MenuEntry
*) clientData
;
943 Menu
*menuPtr
= mePtr
->menuPtr
;
945 if (mePtr
->name
!= NULL
) {
946 Tcl_UntraceVar(menuPtr
->interp
, mePtr
->name
,
947 TCL_GLOBAL_ONLY
|TCL_TRACE_WRITES
|TCL_TRACE_UNSETS
,
948 MenuVarProc
, (ClientData
) mePtr
);
950 if (menuPtr
->postedCascade
== mePtr
) {
951 if (PostSubmenu(menuPtr
->interp
, menuPtr
, (MenuEntry
*) NULL
)
953 TkBindError(menuPtr
->interp
);
956 if (mePtr
->label
!= NULL
) {
957 ckfree(mePtr
->label
);
959 if (mePtr
->bitmap
!= None
) {
960 #if defined(USE_XPM3)
961 Tk_FreePixmap(mePtr
->bitmap
);
963 Tk_FreeBitmap(mePtr
->bitmap
);
966 if (mePtr
->accel
!= NULL
) {
967 ckfree(mePtr
->accel
);
969 if (mePtr
->border
!= NULL
) {
970 Tk_Free3DBorder(mePtr
->border
);
972 if (mePtr
->activeBorder
!= NULL
) {
973 Tk_Free3DBorder(mePtr
->activeBorder
);
975 if (mePtr
->fontPtr
!= NULL
) {
976 Tk_FreeFontStruct(mePtr
->fontPtr
);
978 if (mePtr
->textGC
!= NULL
) {
979 Tk_FreeGC(mePtr
->textGC
);
981 if (mePtr
->activeGC
!= NULL
) {
982 Tk_FreeGC(mePtr
->activeGC
);
984 if (mePtr
->disabledGC
!= NULL
) {
985 Tk_FreeGC(mePtr
->disabledGC
);
987 if (mePtr
->command
!= NULL
) {
988 ckfree(mePtr
->command
);
990 if (mePtr
->name
!= NULL
) {
993 if (mePtr
->onValue
!= NULL
) {
994 ckfree(mePtr
->onValue
);
996 if (mePtr
->offValue
!= NULL
) {
997 ckfree(mePtr
->offValue
);
999 ckfree((char *) mePtr
);
1003 *----------------------------------------------------------------------
1007 * This procedure is called to process an argv/argc list, plus
1008 * the Tk option database, in order to configure (or
1009 * reconfigure) a menu widget.
1012 * The return value is a standard Tcl result. If TCL_ERROR is
1013 * returned, then interp->result contains an error message.
1016 * Configuration information, such as colors, font, etc. get set
1017 * for menuPtr; old resources get freed, if there were any.
1019 *----------------------------------------------------------------------
1024 Tcl_Interp
*interp
, /* Used for error reporting. */
1025 register Menu
*menuPtr
, /* Information about widget; may or may
1026 * not already have values for some fields. */
1027 int argc
, /* Number of valid entries in argv. */
1028 char **argv
, /* Arguments. */
1029 int flags
/* Flags to pass to Tk_ConfigureWidget. */
1037 if (Tk_ConfigureWidget(interp
, menuPtr
->tkwin
, configSpecs
,
1038 argc
, argv
, (char *) menuPtr
, flags
) != TCL_OK
) {
1043 * A few options need special processing, such as setting the
1044 * background from a 3-D border, or filling in complicated
1045 * defaults that couldn't be specified to Tk_ConfigureWidget.
1048 Tk_SetBackgroundFromBorder(menuPtr
->tkwin
, menuPtr
->border
);
1050 gcValues
.font
= menuPtr
->fontPtr
->fid
;
1051 gcValues
.foreground
= menuPtr
->fg
->pixel
;
1052 gcValues
.background
= Tk_3DBorderColor(menuPtr
->border
)->pixel
;
1053 newGC
= Tk_GetGC(menuPtr
->tkwin
, GCForeground
|GCBackground
|GCFont
,
1055 if (menuPtr
->textGC
!= None
) {
1056 Tk_FreeGC(menuPtr
->textGC
);
1058 menuPtr
->textGC
= newGC
;
1060 if (menuPtr
->disabledFg
!= NULL
) {
1061 gcValues
.foreground
= menuPtr
->disabledFg
->pixel
;
1062 mask
= GCForeground
|GCBackground
|GCFont
;
1064 gcValues
.foreground
= gcValues
.background
;
1065 if (menuPtr
->gray
== None
) {
1066 menuPtr
->gray
= Tk_GetBitmap(interp
, menuPtr
->tkwin
,
1067 Tk_GetUid("gray50"));
1068 if (menuPtr
->gray
== None
) {
1072 gcValues
.fill_style
= FillStippled
;
1073 gcValues
.stipple
= menuPtr
->gray
;
1074 mask
= GCForeground
|GCFillStyle
|GCStipple
;
1076 newGC
= Tk_GetGC(menuPtr
->tkwin
, mask
, &gcValues
);
1077 if (menuPtr
->disabledGC
!= None
) {
1078 Tk_FreeGC(menuPtr
->disabledGC
);
1080 menuPtr
->disabledGC
= newGC
;
1082 gcValues
.font
= menuPtr
->fontPtr
->fid
;
1083 gcValues
.foreground
= menuPtr
->activeFg
->pixel
;
1084 gcValues
.background
= Tk_3DBorderColor(menuPtr
->activeBorder
)->pixel
;
1085 newGC
= Tk_GetGC(menuPtr
->tkwin
, GCForeground
|GCBackground
|GCFont
,
1087 if (menuPtr
->activeGC
!= None
) {
1088 Tk_FreeGC(menuPtr
->activeGC
);
1090 menuPtr
->activeGC
= newGC
;
1092 gcValues
.foreground
= menuPtr
->selectorFg
->pixel
;
1093 newGC
= Tk_GetGC(menuPtr
->tkwin
, GCForeground
|GCFont
, &gcValues
);
1094 if (menuPtr
->selectorGC
!= None
) {
1095 Tk_FreeGC(menuPtr
->selectorGC
);
1097 menuPtr
->selectorGC
= newGC
;
1100 * After reconfiguring a menu, we need to reconfigure all of the
1101 * entries in the menu, since some of the things in the children
1102 * (such as graphics contexts) may have to change to reflect changes
1106 for (i
= 0; i
< menuPtr
->numEntries
; i
++) {
1109 mePtr
= menuPtr
->entries
[i
];
1110 ConfigureMenuEntry(interp
, menuPtr
, mePtr
, i
, 0, (char **) NULL
,
1111 TK_CONFIG_ARGV_ONLY
| COMMAND_MASK
<< mePtr
->type
);
1114 if (!(menuPtr
->flags
& RESIZE_PENDING
)) {
1115 menuPtr
->flags
|= RESIZE_PENDING
;
1116 Tk_DoWhenIdle(ComputeMenuGeometry
, (ClientData
) menuPtr
);
1123 *----------------------------------------------------------------------
1125 * ConfigureMenuEntry --
1127 * This procedure is called to process an argv/argc list, plus
1128 * the Tk option database, in order to configure (or
1129 * reconfigure) one entry in a menu.
1132 * The return value is a standard Tcl result. If TCL_ERROR is
1133 * returned, then interp->result contains an error message.
1136 * Configuration information such as label and accelerator get
1137 * set for mePtr; old resources get freed, if there were any.
1139 *----------------------------------------------------------------------
1143 ConfigureMenuEntry (
1144 Tcl_Interp
*interp
, /* Used for error reporting. */
1145 Menu
*menuPtr
, /* Information about whole menu. */
1146 register MenuEntry
*mePtr
, /* Information about menu entry; may
1147 * or may not already have values for
1149 int index
, /* Index of mePtr within menuPtr's
1151 int argc
, /* Number of valid entries in argv. */
1152 char **argv
, /* Arguments. */
1153 int flags
/* Additional flags to pass to
1154 * Tk_ConfigureWidget. */
1158 GC newGC
, newActiveGC
, newDisabledGC
;
1162 * If this entry is a cascade and the cascade is posted, then unpost
1163 * it before reconfiguring the entry (otherwise the reconfigure might
1164 * change the name of the cascaded entry, leaving a posted menu
1168 if (menuPtr
->postedCascade
== mePtr
) {
1169 if (PostSubmenu(menuPtr
->interp
, menuPtr
, (MenuEntry
*) NULL
)
1171 TkBindError(menuPtr
->interp
);
1176 * If this entry is a check button or radio button, then remove
1177 * its old trace procedure.
1180 if ((mePtr
->name
!= NULL
) &&
1181 ((mePtr
->type
== CHECK_BUTTON_ENTRY
)
1182 || (mePtr
->type
== RADIO_BUTTON_ENTRY
))) {
1183 Tcl_UntraceVar(menuPtr
->interp
, mePtr
->name
,
1184 TCL_GLOBAL_ONLY
|TCL_TRACE_WRITES
|TCL_TRACE_UNSETS
,
1185 MenuVarProc
, (ClientData
) mePtr
);
1188 if (Tk_ConfigureWidget(interp
, menuPtr
->tkwin
, entryConfigSpecs
,
1189 argc
, argv
, (char *) mePtr
,
1190 flags
| (COMMAND_MASK
<< mePtr
->type
)) != TCL_OK
) {
1195 * The code below handles special configuration stuff not taken
1196 * care of by Tk_ConfigureWidget, such as special processing for
1197 * defaults, sizing strings, graphics contexts, etc.
1200 if (mePtr
->label
== NULL
) {
1201 mePtr
->labelLength
= 0;
1203 mePtr
->labelLength
= strlen(mePtr
->label
);
1205 if (mePtr
->accel
== NULL
) {
1206 mePtr
->accelLength
= 0;
1208 mePtr
->accelLength
= strlen(mePtr
->accel
);
1211 if (mePtr
->state
== tkActiveUid
) {
1212 if (index
!= menuPtr
->active
) {
1213 ActivateMenuEntry(menuPtr
, index
);
1216 if (index
== menuPtr
->active
) {
1217 ActivateMenuEntry(menuPtr
, -1);
1219 if ((mePtr
->state
!= tkNormalUid
) && (mePtr
->state
!= tkDisabledUid
)) {
1220 Tcl_AppendResult(interp
, "bad state value \"", mePtr
->state
,
1221 "\": must be normal, active, or disabled", (char *) NULL
);
1222 mePtr
->state
= tkNormalUid
;
1227 if (mePtr
->fontPtr
!= NULL
) {
1228 gcValues
.foreground
= menuPtr
->fg
->pixel
;
1229 gcValues
.background
= Tk_3DBorderColor(
1230 (mePtr
->border
!= NULL
) ? mePtr
->border
: menuPtr
->border
)
1232 gcValues
.font
= mePtr
->fontPtr
->fid
;
1235 * Note: disable GraphicsExpose events; we know there won't be
1236 * obscured areas when copying from an off-screen pixmap to the
1237 * screen and this gets rid of unnecessary events.
1240 gcValues
.graphics_exposures
= False
;
1241 newGC
= Tk_GetGC(menuPtr
->tkwin
,
1242 GCForeground
|GCBackground
|GCFont
|GCGraphicsExposures
,
1245 if (menuPtr
->disabledFg
!= NULL
) {
1246 gcValues
.foreground
= menuPtr
->disabledFg
->pixel
;
1247 mask
= GCForeground
|GCBackground
|GCFont
|GCGraphicsExposures
;
1249 gcValues
.foreground
= gcValues
.background
;
1250 gcValues
.fill_style
= FillStippled
;
1251 gcValues
.stipple
= menuPtr
->gray
;
1252 mask
= GCForeground
|GCFillStyle
|GCStipple
;
1254 newDisabledGC
= Tk_GetGC(menuPtr
->tkwin
, mask
, &gcValues
);
1256 gcValues
.foreground
= menuPtr
->activeFg
->pixel
;
1257 gcValues
.background
= Tk_3DBorderColor(
1258 (mePtr
->activeBorder
!= NULL
) ? mePtr
->activeBorder
1259 : menuPtr
->activeBorder
)->pixel
;
1260 newActiveGC
= Tk_GetGC(menuPtr
->tkwin
,
1261 GCForeground
|GCBackground
|GCFont
|GCGraphicsExposures
,
1266 newDisabledGC
= NULL
;
1268 if (mePtr
->textGC
!= NULL
) {
1269 Tk_FreeGC(mePtr
->textGC
);
1271 mePtr
->textGC
= newGC
;
1272 if (mePtr
->activeGC
!= NULL
) {
1273 Tk_FreeGC(mePtr
->activeGC
);
1275 mePtr
->activeGC
= newActiveGC
;
1276 if (mePtr
->disabledGC
!= NULL
) {
1277 Tk_FreeGC(mePtr
->disabledGC
);
1279 mePtr
->disabledGC
= newDisabledGC
;
1281 if ((mePtr
->type
== CHECK_BUTTON_ENTRY
)
1282 || (mePtr
->type
== RADIO_BUTTON_ENTRY
)) {
1285 if (mePtr
->name
== NULL
) {
1286 mePtr
->name
= ckalloc((unsigned) (strlen(mePtr
->label
) + 1));
1287 strcpy(mePtr
->name
, mePtr
->label
);
1289 if (mePtr
->onValue
== NULL
) {
1290 mePtr
->onValue
= ckalloc((unsigned) (strlen(mePtr
->label
) + 1));
1291 strcpy(mePtr
->onValue
, mePtr
->label
);
1295 * Select the entry if the associated variable has the
1296 * appropriate value, initialize the variable if it doesn't
1297 * exist, then set a trace on the variable to monitor future
1298 * changes to its value.
1301 value
= Tcl_GetVar(interp
, mePtr
->name
, TCL_GLOBAL_ONLY
);
1302 mePtr
->flags
&= ENTRY_SELECTED
;
1303 if (value
!= NULL
) {
1304 if (strcmp(value
, mePtr
->onValue
) == 0) {
1305 mePtr
->flags
|= ENTRY_SELECTED
;
1308 Tcl_SetVar(interp
, mePtr
->name
,
1309 (mePtr
->type
== CHECK_BUTTON_ENTRY
) ? mePtr
->offValue
: "",
1312 Tcl_TraceVar(interp
, mePtr
->name
,
1313 TCL_GLOBAL_ONLY
|TCL_TRACE_WRITES
|TCL_TRACE_UNSETS
,
1314 MenuVarProc
, (ClientData
) mePtr
);
1317 if (!(menuPtr
->flags
& RESIZE_PENDING
)) {
1318 menuPtr
->flags
|= RESIZE_PENDING
;
1319 Tk_DoWhenIdle(ComputeMenuGeometry
, (ClientData
) menuPtr
);
1325 *--------------------------------------------------------------
1327 * ComputeMenuGeometry --
1329 * This procedure is invoked to recompute the size and
1330 * layout of a menu. It is called as a when-idle handler so
1331 * that it only gets done once, even if a group of changes is
1338 * Fields of menu entries are changed to reflect their
1339 * current positions, and the size of the menu window
1340 * itself may be changed.
1342 *--------------------------------------------------------------
1346 ComputeMenuGeometry (
1347 ClientData clientData
/* Structure describing menu. */
1350 Menu
*menuPtr
= (Menu
*) clientData
;
1351 register MenuEntry
*mePtr
;
1352 XFontStruct
*fontPtr
;
1353 int maxLabelWidth
, maxSelectorWidth
, maxAccelWidth
;
1354 int width
, height
, selectorSpace
, horizMargin
;
1357 if (menuPtr
->tkwin
== NULL
) {
1361 maxLabelWidth
= maxSelectorWidth
= maxAccelWidth
= 0;
1362 y
= menuPtr
->borderWidth
;
1364 for (i
= 0; i
< menuPtr
->numEntries
; i
++) {
1365 mePtr
= menuPtr
->entries
[i
];
1367 fontPtr
= mePtr
->fontPtr
;
1368 if (fontPtr
== NULL
) {
1369 fontPtr
= menuPtr
->fontPtr
;
1373 * For each entry, compute the height required by that
1374 * particular entry, plus three widths: the width of the
1375 * label, the width to allow for a selector to be displayed
1376 * to the left of the label (if any), and the width of the
1377 * accelerator to be displayed to the right of the label
1378 * (if any). These sizes depend, of course, on the type
1382 if (mePtr
->bitmap
!= None
) {
1383 unsigned int bitmapWidth
, bitmapHeight
;
1385 #if defined(USE_XPM3)
1386 Tk_SizeOfPixmap(mePtr
->bitmap
, &bitmapWidth
, &bitmapHeight
);
1388 Tk_SizeOfBitmap(mePtr
->bitmap
, &bitmapWidth
, &bitmapHeight
);
1390 mePtr
->height
= bitmapHeight
;
1391 width
= bitmapWidth
;
1392 if (mePtr
->type
== CHECK_BUTTON_ENTRY
) {
1393 selectorSpace
= (14*mePtr
->height
)/10;
1394 mePtr
->selectorDiameter
= (65*mePtr
->height
)/100;
1395 } else if (mePtr
->type
== RADIO_BUTTON_ENTRY
) {
1396 selectorSpace
= (14*mePtr
->height
)/10;
1397 mePtr
->selectorDiameter
= (75*mePtr
->height
)/100;
1400 mePtr
->height
= fontPtr
->ascent
+ fontPtr
->descent
;
1401 if (mePtr
->label
!= NULL
) {
1402 (void) TkMeasureChars(fontPtr
, mePtr
->label
,
1403 mePtr
->labelLength
, 0, (int) 100000,
1404 TK_NEWLINES_NOT_SPECIAL
, &width
);
1408 if (mePtr
->type
== CHECK_BUTTON_ENTRY
) {
1409 selectorSpace
= mePtr
->height
;
1410 mePtr
->selectorDiameter
= (80*mePtr
->height
)/100;
1411 } else if (mePtr
->type
== RADIO_BUTTON_ENTRY
) {
1412 selectorSpace
= mePtr
->height
;
1413 mePtr
->selectorDiameter
= mePtr
->height
;
1416 mePtr
->height
+= 2*menuPtr
->activeBorderWidth
+ 2;
1417 if (width
> maxLabelWidth
) {
1418 maxLabelWidth
= width
;
1420 if (mePtr
->accel
!= NULL
) {
1421 (void) TkMeasureChars(fontPtr
, mePtr
->accel
, mePtr
->accelLength
,
1422 0, (int) 100000, TK_NEWLINES_NOT_SPECIAL
, &width
);
1423 if (width
> maxAccelWidth
) {
1424 maxAccelWidth
= width
;
1427 if (mePtr
->type
== SEPARATOR_ENTRY
) {
1428 mePtr
->height
= 4*menuPtr
->borderWidth
;
1430 if (selectorSpace
> maxSelectorWidth
) {
1431 maxSelectorWidth
= selectorSpace
;
1438 * Got all the sizes. Update fields in the menu structure, then
1439 * resize the window if necessary. Leave margins on either side
1440 * of the selector (or just one margin if there is no selector).
1441 * Leave another margin on the right side of the label, plus yet
1442 * another margin to the right of the accelerator (if there is one).
1446 menuPtr
->selectorSpace
= maxSelectorWidth
+ horizMargin
;
1447 if (maxSelectorWidth
!= 0) {
1448 menuPtr
->selectorSpace
+= horizMargin
;
1450 menuPtr
->labelWidth
= maxLabelWidth
+ horizMargin
;
1451 width
= menuPtr
->selectorSpace
+ menuPtr
->labelWidth
+ maxAccelWidth
1452 + 2*menuPtr
->borderWidth
+ 2*menuPtr
->activeBorderWidth
+ 2;
1453 if (maxAccelWidth
!= 0) {
1454 width
+= horizMargin
;
1456 height
= y
+ menuPtr
->borderWidth
;
1459 * The X server doesn't like zero dimensions, so round up to at least
1460 * 1 (a zero-sized menu should never really occur, anyway).
1469 if ((width
!= Tk_ReqWidth(menuPtr
->tkwin
)) ||
1470 (height
!= Tk_ReqHeight(menuPtr
->tkwin
))) {
1471 Tk_GeometryRequest(menuPtr
->tkwin
, width
, height
);
1474 * Must always force a redisplay here if the window is mapped
1475 * (even if the size didn't change, something else might have
1476 * changed in the menu, such as a label or accelerator). The
1477 * resize will force a redisplay above.
1480 EventuallyRedrawMenu(menuPtr
, -1);
1483 menuPtr
->flags
&= ~RESIZE_PENDING
;
1487 *----------------------------------------------------------------------
1491 * This procedure is invoked to display a menu widget.
1497 * Commands are output to X to display the menu in its
1500 *----------------------------------------------------------------------
1505 ClientData clientData
/* Information about widget. */
1508 register Menu
*menuPtr
= (Menu
*) clientData
;
1509 register MenuEntry
*mePtr
;
1510 register Tk_Window tkwin
= menuPtr
->tkwin
;
1511 XFontStruct
*fontPtr
;
1512 int index
, baseline
;
1515 menuPtr
->flags
&= ~REDRAW_PENDING
;
1516 if ((menuPtr
->tkwin
== NULL
) || !Tk_IsMapped(tkwin
)) {
1521 * Loop through all of the entries, drawing them one at a time.
1524 for (index
= 0; index
< menuPtr
->numEntries
; index
++) {
1525 mePtr
= menuPtr
->entries
[index
];
1526 if (!(mePtr
->flags
& ENTRY_NEEDS_REDISPLAY
)) {
1529 mePtr
->flags
&= ~ENTRY_NEEDS_REDISPLAY
;
1535 if (mePtr
->state
== tkActiveUid
) {
1536 Tk_Fill3DRectangle(Tk_Display(tkwin
), Tk_WindowId(tkwin
),
1537 (mePtr
->activeBorder
!= NULL
) ? mePtr
->activeBorder
1538 : menuPtr
->activeBorder
, menuPtr
->borderWidth
, mePtr
->y
,
1539 Tk_Width(tkwin
) - 2*menuPtr
->borderWidth
, mePtr
->height
,
1540 menuPtr
->activeBorderWidth
, TK_RELIEF_RAISED
);
1541 gc
= mePtr
->activeGC
;
1543 gc
= menuPtr
->activeGC
;
1546 Tk_Fill3DRectangle(Tk_Display(tkwin
), Tk_WindowId(tkwin
),
1547 (mePtr
->border
!= NULL
) ? mePtr
->border
1548 : menuPtr
->border
, menuPtr
->borderWidth
, mePtr
->y
,
1549 Tk_Width(tkwin
) - 2*menuPtr
->borderWidth
, mePtr
->height
,
1551 if ((mePtr
->state
== tkDisabledUid
)
1552 && (menuPtr
->disabledFg
!= NULL
)) {
1553 gc
= mePtr
->disabledGC
;
1555 gc
= menuPtr
->disabledGC
;
1560 gc
= menuPtr
->textGC
;
1566 * Draw label or bitmap for entry.
1569 fontPtr
= mePtr
->fontPtr
;
1570 if (fontPtr
== NULL
) {
1571 fontPtr
= menuPtr
->fontPtr
;
1573 baseline
= mePtr
->y
+ (mePtr
->height
+ fontPtr
->ascent
1574 - fontPtr
->descent
)/2;
1575 if (mePtr
->bitmap
!= None
) {
1576 unsigned int width
, height
;
1578 #if defined(USE_XPM3)
1579 Tk_SizeOfPixmap(mePtr
->bitmap
, &width
, &height
);
1580 XCopyArea(Tk_Display(tkwin
), mePtr
->bitmap
, Tk_WindowId(tkwin
),
1581 gc
, 0, 0, width
, height
,
1582 menuPtr
->borderWidth
+ menuPtr
->selectorSpace
,
1583 (int) (mePtr
->y
+ (mePtr
->height
- height
)/2));
1585 Tk_SizeOfBitmap(mePtr
->bitmap
, &width
, &height
);
1586 XCopyPlane(Tk_Display(tkwin
), mePtr
->bitmap
, Tk_WindowId(tkwin
),
1587 gc
, 0, 0, width
, height
,
1588 menuPtr
->borderWidth
+ menuPtr
->selectorSpace
,
1589 (int) (mePtr
->y
+ (mePtr
->height
- height
)/2), 1);
1592 baseline
= mePtr
->y
+ (mePtr
->height
+ fontPtr
->ascent
1593 - fontPtr
->descent
)/2;
1594 if (mePtr
->label
!= NULL
) {
1595 TkDisplayChars(Tk_Display(tkwin
), Tk_WindowId(tkwin
), gc
,
1596 fontPtr
, mePtr
->label
, mePtr
->labelLength
,
1597 menuPtr
->borderWidth
+ menuPtr
->selectorSpace
,
1598 baseline
, TK_NEWLINES_NOT_SPECIAL
);
1599 if (mePtr
->underline
>= 0) {
1600 TkUnderlineChars(Tk_Display(tkwin
), Tk_WindowId(tkwin
), gc
,
1601 fontPtr
, mePtr
->label
,
1602 menuPtr
->borderWidth
+ menuPtr
->selectorSpace
,
1603 baseline
, TK_NEWLINES_NOT_SPECIAL
,
1604 mePtr
->underline
, mePtr
->underline
);
1613 if (mePtr
->accel
!= NULL
) {
1614 TkDisplayChars(Tk_Display(tkwin
), Tk_WindowId(tkwin
), gc
,
1615 fontPtr
, mePtr
->accel
, mePtr
->accelLength
,
1616 menuPtr
->borderWidth
+ menuPtr
->selectorSpace
1617 + menuPtr
->labelWidth
, baseline
, TK_NEWLINES_NOT_SPECIAL
);
1621 * Draw check-button selector.
1624 if (mePtr
->type
== CHECK_BUTTON_ENTRY
) {
1627 dim
= mePtr
->selectorDiameter
;
1628 x
= menuPtr
->borderWidth
+ (menuPtr
->selectorSpace
- dim
)/2;
1629 y
= mePtr
->y
+ (mePtr
->height
- dim
)/2;
1630 Tk_Fill3DRectangle(Tk_Display(tkwin
), Tk_WindowId(tkwin
),
1631 menuPtr
->border
, x
, y
, dim
, dim
,
1632 menuPtr
->activeBorderWidth
, TK_RELIEF_SUNKEN
);
1633 x
+= menuPtr
->activeBorderWidth
;
1634 y
+= menuPtr
->activeBorderWidth
;
1635 dim
-= 2*menuPtr
->activeBorderWidth
;
1636 if ((dim
> 0) && (mePtr
->flags
& ENTRY_SELECTED
)) {
1637 XFillRectangle(Tk_Display(tkwin
), Tk_WindowId(tkwin
),
1638 menuPtr
->selectorGC
, x
, y
, (unsigned int) dim
,
1639 (unsigned int) dim
);
1644 * Draw radio-button selector.
1647 if (mePtr
->type
== RADIO_BUTTON_ENTRY
) {
1651 radius
= mePtr
->selectorDiameter
/2;
1652 points
[0].x
= menuPtr
->borderWidth
1653 + (menuPtr
->selectorSpace
- mePtr
->selectorDiameter
)/2;
1654 points
[0].y
= mePtr
->y
+ (mePtr
->height
)/2;
1655 points
[1].x
= points
[0].x
+ radius
;
1656 points
[1].y
= points
[0].y
+ radius
;
1657 points
[2].x
= points
[1].x
+ radius
;
1658 points
[2].y
= points
[0].y
;
1659 points
[3].x
= points
[1].x
;
1660 points
[3].y
= points
[0].y
- radius
;
1661 if (mePtr
->flags
& ENTRY_SELECTED
) {
1662 XFillPolygon(Tk_Display(tkwin
), Tk_WindowId(tkwin
),
1663 menuPtr
->selectorGC
, points
, 4, Convex
,
1666 Tk_Fill3DPolygon(Tk_Display(tkwin
), Tk_WindowId(tkwin
),
1667 menuPtr
->border
, points
, 4, menuPtr
->activeBorderWidth
,
1670 Tk_Draw3DPolygon(Tk_Display(tkwin
), Tk_WindowId(tkwin
),
1671 menuPtr
->border
, points
, 4, menuPtr
->activeBorderWidth
,
1679 if (mePtr
->type
== SEPARATOR_ENTRY
) {
1683 margin
= (fontPtr
->ascent
+ fontPtr
->descent
)/2;
1684 points
[0].x
= 2*menuPtr
->borderWidth
+ margin
;
1685 points
[0].y
= mePtr
->y
+ mePtr
->height
/2;
1686 points
[1].x
= Tk_Width(tkwin
) - 2*menuPtr
->borderWidth
- margin
;
1687 points
[1].y
= points
[0].y
;
1688 Tk_Draw3DPolygon(Tk_Display(tkwin
), Tk_WindowId(tkwin
),
1689 menuPtr
->border
, points
, 2, 1, TK_RELIEF_RAISED
);
1693 * If the entry is disabled with a stipple rather than a special
1694 * foreground color, generate the stippled effect.
1697 if ((mePtr
->state
== tkDisabledUid
) && (menuPtr
->disabledFg
== NULL
)) {
1698 XFillRectangle(Tk_Display(tkwin
), Tk_WindowId(tkwin
),
1699 menuPtr
->disabledGC
, menuPtr
->borderWidth
,
1701 (unsigned) (Tk_Width(tkwin
) - 2*menuPtr
->borderWidth
),
1702 (unsigned) mePtr
->height
);
1706 Tk_Draw3DRectangle(Tk_Display(tkwin
), Tk_WindowId(tkwin
),
1707 menuPtr
->border
, 0, 0, Tk_Width(tkwin
), Tk_Height(tkwin
),
1708 menuPtr
->borderWidth
, TK_RELIEF_RAISED
);
1712 *--------------------------------------------------------------
1716 * Parse a textual index into a menu and return the numerical
1717 * index of the indicated entry.
1720 * A standard Tcl result. If all went well, then *indexPtr is
1721 * filled in with the entry index corresponding to string
1722 * (ranges from -1 to the number of entries in the menu minus
1723 * one). Otherwise an error message is left in interp->result.
1728 *--------------------------------------------------------------
1733 Tcl_Interp
*interp
, /* For error messages. */
1734 Menu
*menuPtr
, /* Menu for which the index is being
1736 char *string
, /* Specification of an entry in menu. See
1737 * manual entry for valid .*/
1738 int *indexPtr
/* Where to store converted relief. */
1743 if ((string
[0] == 'a') && (strcmp(string
, "active") == 0)) {
1744 *indexPtr
= menuPtr
->active
;
1748 if ((string
[0] == 'l') && (strcmp(string
, "last") == 0)) {
1749 *indexPtr
= menuPtr
->numEntries
-1;
1753 if ((string
[0] == 'n') && (strcmp(string
, "none") == 0)) {
1758 if (string
[0] == '@') {
1759 if (Tcl_GetInt(interp
, string
+1, &y
) == TCL_OK
) {
1764 for (i
= 0; i
< menuPtr
->numEntries
; i
++) {
1765 y
-= menuPtr
->entries
[i
]->height
;
1770 if (i
>= menuPtr
->numEntries
) {
1776 Tcl_SetResult(interp
, (char *) NULL
, TCL_STATIC
);
1780 if (isdigit(string
[0])) {
1781 if (Tcl_GetInt(interp
, string
, &i
) == TCL_OK
) {
1782 if ((i
< menuPtr
->numEntries
) && (i
>= 0)) {
1787 Tcl_SetResult(interp
, (char *) NULL
, TCL_STATIC
);
1791 for (i
= 0; i
< menuPtr
->numEntries
; i
++) {
1794 label
= menuPtr
->entries
[i
]->label
;
1796 && (Tcl_StringMatch(menuPtr
->entries
[i
]->label
, string
))) {
1802 Tcl_AppendResult(interp
, "bad menu entry index \"",
1803 string
, "\"", (char *) NULL
);
1808 *--------------------------------------------------------------
1812 * This procedure is invoked by the Tk dispatcher for various
1819 * When the window gets deleted, internal structures get
1820 * cleaned up. When it gets exposed, it is redisplayed.
1822 *--------------------------------------------------------------
1827 ClientData clientData
, /* Information about window. */
1828 XEvent
*eventPtr
/* Information about event. */
1831 Menu
*menuPtr
= (Menu
*) clientData
;
1832 if ((eventPtr
->type
== Expose
) && (eventPtr
->xexpose
.count
== 0)) {
1833 EventuallyRedrawMenu(menuPtr
, -1);
1834 } else if (eventPtr
->type
== DestroyNotify
) {
1835 Tcl_DeleteCommand(menuPtr
->interp
, Tk_PathName(menuPtr
->tkwin
));
1838 * Careful! Must delete the event-sharing information here
1839 * rather than in DestroyMenu. By the time that procedure
1840 * is called the tkwin may have been reused, resulting in some
1841 * other window accidentally being cut off from shared events.
1844 Tk_UnshareEvents(menuPtr
->tkwin
, menuPtr
->group
);
1845 menuPtr
->tkwin
= NULL
;
1846 if (menuPtr
->flags
& REDRAW_PENDING
) {
1847 Tk_CancelIdleCall(DisplayMenu
, (ClientData
) menuPtr
);
1849 if (menuPtr
->flags
& RESIZE_PENDING
) {
1850 Tk_CancelIdleCall(ComputeMenuGeometry
, (ClientData
) menuPtr
);
1852 Tk_EventuallyFree((ClientData
) menuPtr
, DestroyMenu
);
1857 *--------------------------------------------------------------
1861 * This procedure is invoked when someone changes the
1862 * state variable associated with a radiobutton or checkbutton
1863 * menu entry. The entry's selected state is set to match
1864 * the value of the variable.
1867 * NULL is always returned.
1870 * The menu entry may become selected or deselected.
1872 *--------------------------------------------------------------
1878 ClientData clientData
, /* Information about menu entry. */
1879 Tcl_Interp
*interp
, /* Interpreter containing variable. */
1880 char *name1
, /* First part of variable's name. */
1881 char *name2
, /* Second part of variable's name. */
1882 int flags
/* Describes what just happened. */
1885 MenuEntry
*mePtr
= (MenuEntry
*) clientData
;
1889 menuPtr
= mePtr
->menuPtr
;
1892 * If the variable is being unset, then re-establish the
1893 * trace unless the whole interpreter is going away.
1896 if (flags
& TCL_TRACE_UNSETS
) {
1897 mePtr
->flags
&= ~ENTRY_SELECTED
;
1898 if ((flags
& TCL_TRACE_DESTROYED
) && !(flags
& TCL_INTERP_DESTROYED
)) {
1899 Tcl_TraceVar2(interp
, name1
, name2
,
1900 TCL_GLOBAL_ONLY
|TCL_TRACE_WRITES
|TCL_TRACE_UNSETS
,
1901 MenuVarProc
, clientData
);
1903 EventuallyRedrawMenu(menuPtr
, -1);
1904 return (char *) NULL
;
1908 * Use the value of the variable to update the selected status of
1912 value
= Tcl_GetVar2(interp
, name1
, name2
, flags
& TCL_GLOBAL_ONLY
);
1913 if (strcmp(value
, mePtr
->onValue
) == 0) {
1914 if (mePtr
->flags
& ENTRY_SELECTED
) {
1915 return (char *) NULL
;
1917 mePtr
->flags
|= ENTRY_SELECTED
;
1918 } else if (mePtr
->flags
& ENTRY_SELECTED
) {
1919 mePtr
->flags
&= ~ENTRY_SELECTED
;
1921 return (char *) NULL
;
1923 EventuallyRedrawMenu(menuPtr
, -1);
1924 return (char *) NULL
;
1928 *----------------------------------------------------------------------
1930 * EventuallyRedrawMenu --
1932 * Arrange for an entry of a menu, or the whole menu, to be
1933 * redisplayed at some point in the future.
1939 * A when-idle hander is scheduled to do the redisplay, if there
1940 * isn't one already scheduled.
1942 *----------------------------------------------------------------------
1946 EventuallyRedrawMenu (
1947 register Menu
*menuPtr
, /* Information about menu to redraw. */
1948 int index
/* Which entry to redraw. If -1, then
1949 * all the entries in the menu are redrawn. */
1952 if (menuPtr
->tkwin
== NULL
) {
1956 menuPtr
->entries
[index
]->flags
|= ENTRY_NEEDS_REDISPLAY
;
1958 for (index
= 0; index
< menuPtr
->numEntries
; index
++) {
1959 menuPtr
->entries
[index
]->flags
|= ENTRY_NEEDS_REDISPLAY
;
1962 if ((menuPtr
->tkwin
== NULL
) || !Tk_IsMapped(menuPtr
->tkwin
)
1963 || (menuPtr
->flags
& REDRAW_PENDING
)) {
1966 Tk_DoWhenIdle(DisplayMenu
, (ClientData
) menuPtr
);
1967 menuPtr
->flags
|= REDRAW_PENDING
;
1971 *--------------------------------------------------------------
1975 * This procedure arranges for a particular submenu (i.e. the
1976 * menu corresponding to a given cascade entry) to be
1980 * A standard Tcl return result. Errors may occur in the
1981 * Tcl commands generated to post and unpost submenus.
1984 * If there is already a submenu posted, it is unposted.
1985 * The new submenu is then posted.
1987 *--------------------------------------------------------------
1992 Tcl_Interp
*interp
, /* Used for invoking sub-commands and
1993 * reporting errors. */
1994 register Menu
*menuPtr
, /* Information about menu as a whole. */
1995 register MenuEntry
*mePtr
/* Info about submenu that is to be
1996 * posted. NULL means make sure that
1997 * no submenu is posted. */
2003 if (mePtr
== menuPtr
->postedCascade
) {
2007 if (menuPtr
->postedCascade
!= NULL
) {
2008 result
= Tcl_VarEval(interp
, menuPtr
->postedCascade
->name
,
2009 " unpost", (char *) NULL
);
2010 menuPtr
->postedCascade
= NULL
;
2011 if (result
!= TCL_OK
) {
2016 if ((mePtr
!= NULL
) && (mePtr
->name
!= NULL
)) {
2017 Tk_GetRootCoords(menuPtr
->tkwin
, &x
, &y
);
2018 x
+= Tk_Width(menuPtr
->tkwin
);
2020 sprintf(string
, "%d %d ", x
, y
);
2021 result
= Tcl_VarEval(interp
, mePtr
->name
, " post ", string
,
2022 menuPtr
->group
, (char *) NULL
);
2023 if (result
!= TCL_OK
) {
2026 menuPtr
->postedCascade
= mePtr
;
2032 *----------------------------------------------------------------------
2034 * ActivateMenuEntry --
2036 * This procedure is invoked to make a particular menu entry
2037 * the active one, deactivating any other entry that might
2038 * currently be active.
2041 * The return value is a standard Tcl result (errors can occur
2042 * while posting and unposting submenus).
2045 * Menu entries get redisplayed, and the active entry changes.
2046 * Submenus may get posted and unposted.
2048 *----------------------------------------------------------------------
2053 register Menu
*menuPtr
, /* Menu in which to activate. */
2054 int index
/* Index of entry to activate, or
2055 * -1 to deactivate all entries. */
2058 register MenuEntry
*mePtr
;
2059 int result
= TCL_OK
;
2061 if (menuPtr
->active
>= 0) {
2062 mePtr
= menuPtr
->entries
[menuPtr
->active
];
2065 * Don't change the state unless it's currently active (state
2066 * might already have been changed to disabled).
2069 if (mePtr
->state
== tkActiveUid
) {
2070 mePtr
->state
= tkNormalUid
;
2072 EventuallyRedrawMenu(menuPtr
, menuPtr
->active
);
2074 menuPtr
->active
= index
;
2076 mePtr
= menuPtr
->entries
[index
];
2077 mePtr
->state
= tkActiveUid
;
2078 EventuallyRedrawMenu(menuPtr
, index
);
2079 Tk_Preserve((ClientData
) mePtr
);
2080 if (mePtr
->type
== CASCADE_ENTRY
) {
2081 result
= PostSubmenu(menuPtr
->interp
, menuPtr
, mePtr
);
2083 result
= PostSubmenu(menuPtr
->interp
, menuPtr
, (MenuEntry
*) NULL
);
2085 Tk_Release((ClientData
) mePtr
);