X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/50971a123a9ccca43497c008305de55cb2203fa1..bbdb68b67a9e467c4bd5ae8610665747b72a17ac:/src/macmenu.c diff --git a/src/macmenu.c b/src/macmenu.c index c7ee0dfaf2..e7d69d5657 100644 --- a/src/macmenu.c +++ b/src/macmenu.c @@ -1,5 +1,6 @@ -/* Menu support for GNU Emacs on the for Mac OS. - Copyright (C) 2000, 2001, 2002, 2003 Free Software Foundation, Inc. +/* Menu support for GNU Emacs on Mac OS. + Copyright (C) 2000, 2001, 2002, 2003, 2004, + 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Emacs. @@ -23,6 +24,7 @@ Boston, MA 02110-1301, USA. */ #include #include + #include "lisp.h" #include "termhooks.h" #include "keyboard.h" @@ -60,10 +62,17 @@ Boston, MA 02110-1301, USA. */ #include "dispextern.h" -#define POPUP_SUBMENU_ID 235 -#define MIN_POPUP_SUBMENU_ID 512 -#define MIN_MENU_ID 256 -#define MIN_SUBMENU_ID 1 +enum mac_menu_kind { /* Menu ID range */ + MAC_MENU_APPLE, /* 0 (Reserved by Apple) */ + MAC_MENU_MENU_BAR, /* 1 .. 234 */ + MAC_MENU_POPUP, /* 235 */ + MAC_MENU_DRIVER, /* 236 .. 255 (Reserved) */ + MAC_MENU_MENU_BAR_SUB, /* 256 .. 16383 */ + MAC_MENU_POPUP_SUB, /* 16384 .. 32767 */ + MAC_MENU_END /* 32768 */ +}; + +static const int min_menu_id[] = {0, 1, 235, 236, 256, 16384, 32768}; #define DIALOG_WINDOW_RESOURCE 130 @@ -153,7 +162,7 @@ Lisp_Object Vmenu_updating_frame; Lisp_Object Qdebug_on_next_call; -extern Lisp_Object Qmenu_bar; +extern Lisp_Object Qmenu_bar, Qmac_apple_event; extern Lisp_Object QCtoggle, QCradio; @@ -164,30 +173,32 @@ extern Lisp_Object Qoverriding_local_map, Qoverriding_terminal_local_map; extern Lisp_Object Qmenu_bar_update_hook; +void set_frame_menubar P_ ((FRAME_PTR, int, int)); + #if TARGET_API_MAC_CARBON #define ENCODE_MENU_STRING(str) ENCODE_UTF_8 (str) #else #define ENCODE_MENU_STRING(str) ENCODE_SYSTEM (str) #endif -void set_frame_menubar (); - static void push_menu_item P_ ((Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object)); #ifdef HAVE_DIALOGS -static Lisp_Object mac_dialog_show (); +static Lisp_Object mac_dialog_show P_ ((FRAME_PTR, int, Lisp_Object, + Lisp_Object, char **)); #endif -static Lisp_Object mac_menu_show (); - -static void keymap_panes (); -static void single_keymap_panes (); -static void single_menu_item (); -static void list_of_panes (); -static void list_of_items (); - -static void fill_submenu (MenuHandle, widget_value *); -static void fill_menubar (widget_value *); +static Lisp_Object mac_menu_show P_ ((struct frame *, int, int, int, int, + Lisp_Object, char **)); +static void keymap_panes P_ ((Lisp_Object *, int, int)); +static void single_keymap_panes P_ ((Lisp_Object, Lisp_Object, Lisp_Object, + int, int)); +static void list_of_panes P_ ((Lisp_Object)); +static void list_of_items P_ ((Lisp_Object)); + +static int fill_menu P_ ((MenuHandle, widget_value *, enum mac_menu_kind, int)); +static void fill_menubar P_ ((widget_value *, int)); +static void dispose_menus P_ ((enum mac_menu_kind, int)); /* This holds a Lisp vector that holds the results of decoding @@ -244,15 +255,6 @@ static int menu_items_n_panes; /* Current depth within submenus. */ static int menu_items_submenu_depth; -/* Flag which when set indicates a dialog or menu has been posted by - Xt on behalf of one of the widget sets. */ -static int popup_activated_flag; - -/* Index of the next submenu */ -static int submenu_id; - -static int next_menubar_widget_id; - /* This is set nonzero after the user activates the menu bar, and set to zero again after the menu bars are redisplayed by prepare_menu_bar. While it is nonzero, all calls to set_frame_menubar go deep. @@ -279,8 +281,7 @@ init_menu_items () menu_items_submenu_depth = 0; } -/* Call at the end of generating the data in menu_items. - This fills in the number of items in the last pane. */ +/* Call at the end of generating the data in menu_items. */ static void finish_menu_items () @@ -302,6 +303,38 @@ discard_menu_items () } } +/* This undoes save_menu_items, and it is called by the specpdl unwind + mechanism. */ + +static Lisp_Object +restore_menu_items (saved) + Lisp_Object saved; +{ + menu_items = XCAR (saved); + menu_items_allocated = (VECTORP (menu_items) ? ASIZE (menu_items) : 0); + saved = XCDR (saved); + menu_items_used = XINT (XCAR (saved)); + saved = XCDR (saved); + menu_items_n_panes = XINT (XCAR (saved)); + saved = XCDR (saved); + menu_items_submenu_depth = XINT (XCAR (saved)); + return Qnil; +} + +/* Push the whole state of menu_items processing onto the specpdl. + It will be restored when the specpdl is unwound. */ + +static void +save_menu_items () +{ + Lisp_Object saved = list4 (menu_items, + make_number (menu_items_used), + make_number (menu_items_n_panes), + make_number (menu_items_submenu_depth)); + record_unwind_protect (restore_menu_items, saved); + menu_items = Qnil; +} + /* Make the menu_items vector twice as large. */ static void @@ -312,6 +345,7 @@ grow_menu_items () old = menu_items; menu_items_allocated *= 2; + menu_items = Fmake_vector (make_number (menu_items_allocated), Qnil); bcopy (XVECTOR (old)->contents, XVECTOR (menu_items)->contents, old_size * sizeof (Lisp_Object)); @@ -414,11 +448,21 @@ keymap_panes (keymaps, nmaps, notreal) P is the number of panes we have made so far. */ for (mapno = 0; mapno < nmaps; mapno++) single_keymap_panes (keymaps[mapno], - Fkeymap_prompt (keymaps[mapno]), Qnil, notreal, 10); + Fkeymap_prompt (keymaps[mapno]), Qnil, notreal, 10); finish_menu_items (); } +/* Args passed between single_keymap_panes and single_menu_item. */ +struct skp + { + Lisp_Object pending_maps; + int maxdepth, notreal; + }; + +static void single_menu_item P_ ((Lisp_Object, Lisp_Object, Lisp_Object, + void *)); + /* This is a recursive subroutine of keymap_panes. It handles one keymap, KEYMAP. The other arguments are passed along @@ -436,89 +480,71 @@ single_keymap_panes (keymap, pane_name, prefix, notreal, maxdepth) int notreal; int maxdepth; { - Lisp_Object pending_maps = Qnil; - Lisp_Object tail, item; - struct gcpro gcpro1, gcpro2; + struct skp skp; + struct gcpro gcpro1; + + skp.pending_maps = Qnil; + skp.maxdepth = maxdepth; + skp.notreal = notreal; if (maxdepth <= 0) return; push_menu_pane (pane_name, prefix); - for (tail = keymap; CONSP (tail); tail = XCDR (tail)) - { - GCPRO2 (keymap, pending_maps); - /* Look at each key binding, and if it is a menu item add it - to this menu. */ - item = XCAR (tail); - if (CONSP (item)) - single_menu_item (XCAR (item), XCDR (item), - &pending_maps, notreal, maxdepth); - else if (VECTORP (item)) - { - /* Loop over the char values represented in the vector. */ - int len = XVECTOR (item)->size; - int c; - for (c = 0; c < len; c++) - { - Lisp_Object character; - XSETFASTINT (character, c); - single_menu_item (character, XVECTOR (item)->contents[c], - &pending_maps, notreal, maxdepth); - } - } - UNGCPRO; - } + GCPRO1 (skp.pending_maps); + map_keymap (keymap, single_menu_item, Qnil, &skp, 1); + UNGCPRO; /* Process now any submenus which want to be panes at this level. */ - while (!NILP (pending_maps)) + while (CONSP (skp.pending_maps)) { Lisp_Object elt, eltcdr, string; - elt = Fcar (pending_maps); + elt = XCAR (skp.pending_maps); eltcdr = XCDR (elt); string = XCAR (eltcdr); /* We no longer discard the @ from the beginning of the string here. Instead, we do this in mac_menu_show. */ single_keymap_panes (Fcar (elt), string, XCDR (eltcdr), notreal, maxdepth - 1); - pending_maps = Fcdr (pending_maps); + skp.pending_maps = XCDR (skp.pending_maps); } } /* This is a subroutine of single_keymap_panes that handles one keymap entry. KEY is a key in a keymap and ITEM is its binding. - PENDING_MAPS_PTR points to a list of keymaps waiting to be made into + SKP->PENDING_MAPS_PTR is a list of keymaps waiting to be made into separate panes. - If NOTREAL is nonzero, only check for equivalent key bindings, don't + If SKP->NOTREAL is nonzero, only check for equivalent key bindings, don't evaluate expressions in menu items and don't make any menu. - If we encounter submenus deeper than MAXDEPTH levels, ignore them. */ + If we encounter submenus deeper than SKP->MAXDEPTH levels, ignore them. */ static void -single_menu_item (key, item, pending_maps_ptr, notreal, maxdepth) - Lisp_Object key, item; - Lisp_Object *pending_maps_ptr; - int maxdepth, notreal; +single_menu_item (key, item, dummy, skp_v) + Lisp_Object key, item, dummy; + void *skp_v; { Lisp_Object map, item_string, enabled; struct gcpro gcpro1, gcpro2; int res; + struct skp *skp = skp_v; /* Parse the menu item and leave the result in item_properties. */ GCPRO2 (key, item); - res = parse_menu_item (item, notreal, 0); + res = parse_menu_item (item, skp->notreal, 0); UNGCPRO; if (!res) return; /* Not a menu item. */ map = XVECTOR (item_properties)->contents[ITEM_PROPERTY_MAP]; - if (notreal) + if (skp->notreal) { /* We don't want to make a menu, just traverse the keymaps to precompute equivalent key bindings. */ if (!NILP (map)) - single_keymap_panes (map, Qnil, key, 1, maxdepth - 1); + single_keymap_panes (map, Qnil, key, 1, skp->maxdepth - 1); return; } @@ -529,8 +555,8 @@ single_menu_item (key, item, pending_maps_ptr, notreal, maxdepth) { if (!NILP (enabled)) /* An enabled separate pane. Remember this to handle it later. */ - *pending_maps_ptr = Fcons (Fcons (map, Fcons (item_string, key)), - *pending_maps_ptr); + skp->pending_maps = Fcons (Fcons (map, Fcons (item_string, key)), + skp->pending_maps); return; } @@ -538,14 +564,14 @@ single_menu_item (key, item, pending_maps_ptr, notreal, maxdepth) XVECTOR (item_properties)->contents[ITEM_PROPERTY_DEF], XVECTOR (item_properties)->contents[ITEM_PROPERTY_KEYEQ], XVECTOR (item_properties)->contents[ITEM_PROPERTY_TYPE], - XVECTOR (item_properties)->contents[ITEM_PROPERTY_SELECTED], - XVECTOR (item_properties)->contents[ITEM_PROPERTY_HELP]); + XVECTOR (item_properties)->contents[ITEM_PROPERTY_SELECTED], + XVECTOR (item_properties)->contents[ITEM_PROPERTY_HELP]); /* Display a submenu using the toolkit. */ if (! (NILP (map) || NILP (enabled))) { push_submenu_start (); - single_keymap_panes (map, Qnil, key, 0, maxdepth - 1); + single_keymap_panes (map, Qnil, key, 0, skp->maxdepth - 1); push_submenu_end (); } } @@ -562,13 +588,13 @@ list_of_panes (menu) init_menu_items (); - for (tail = menu; !NILP (tail); tail = Fcdr (tail)) + for (tail = menu; CONSP (tail); tail = XCDR (tail)) { Lisp_Object elt, pane_name, pane_data; - elt = Fcar (tail); + elt = XCAR (tail); pane_name = Fcar (elt); CHECK_STRING (pane_name); - push_menu_pane (pane_name, Qnil); + push_menu_pane (ENCODE_MENU_STRING (pane_name), Qnil); pane_data = Fcdr (elt); CHECK_CONS (pane_data); list_of_items (pane_data); @@ -585,20 +611,22 @@ list_of_items (pane) { Lisp_Object tail, item, item1; - for (tail = pane; !NILP (tail); tail = Fcdr (tail)) + for (tail = pane; CONSP (tail); tail = XCDR (tail)) { - item = Fcar (tail); + item = XCAR (tail); if (STRINGP (item)) - push_menu_item (item, Qnil, Qnil, Qt, Qnil, Qnil, Qnil, Qnil); - else if (NILP (item)) - push_left_right_boundary (); - else + push_menu_item (ENCODE_MENU_STRING (item), Qnil, Qnil, Qt, + Qnil, Qnil, Qnil, Qnil); + else if (CONSP (item)) { - CHECK_CONS (item); - item1 = Fcar (item); + item1 = XCAR (item); CHECK_STRING (item1); - push_menu_item (item1, Qt, Fcdr (item), Qt, Qnil, Qnil, Qnil, Qnil); + push_menu_item (ENCODE_MENU_STRING (item1), Qt, XCDR (item), + Qt, Qnil, Qnil, Qnil, Qnil); } + else + push_left_right_boundary (); + } } @@ -607,6 +635,7 @@ cleanup_popup_menu (arg) Lisp_Object arg; { discard_menu_items (); + return Qnil; } DEFUN ("x-popup-menu", Fx_popup_menu, Sx_popup_menu, 2, 2, 0, @@ -658,15 +687,14 @@ no quit occurs and `x-popup-menu' returns nil. */) Lisp_Object keymap, tem; int xpos = 0, ypos = 0; Lisp_Object title; - char *error_name; + char *error_name = NULL; Lisp_Object selection; FRAME_PTR f = NULL; Lisp_Object x, y, window; int keymaps = 0; int for_click = 0; - struct gcpro gcpro1; int specpdl_count = SPECPDL_INDEX (); - + struct gcpro gcpro1; #ifdef HAVE_MENUS if (! NILP (position)) @@ -676,7 +704,8 @@ no quit occurs and `x-popup-menu' returns nil. */) /* Decode the first argument: find the window and the coordinates. */ if (EQ (position, Qt) || (CONSP (position) && (EQ (XCAR (position), Qmenu_bar) - || EQ (XCAR (position), Qtool_bar)))) + || EQ (XCAR (position), Qtool_bar) + || EQ (XCAR (position), Qmac_apple_event)))) { /* Use the mouse's current position. */ FRAME_PTR new_f = SELECTED_FRAME (); @@ -702,8 +731,8 @@ no quit occurs and `x-popup-menu' returns nil. */) if (CONSP (tem)) { window = Fcar (Fcdr (position)); - x = Fcar (tem); - y = Fcar (Fcdr (tem)); + x = XCAR (tem); + y = Fcar (XCDR (tem)); } else { @@ -745,7 +774,8 @@ no quit occurs and `x-popup-menu' returns nil. */) XSETFRAME (Vmenu_updating_frame, f); } - Vmenu_updating_frame = Qnil; + else + Vmenu_updating_frame = Qnil; #endif /* HAVE_MENUS */ title = Qnil; @@ -786,11 +816,11 @@ no quit occurs and `x-popup-menu' returns nil. */) /* The first keymap that has a prompt string supplies the menu title. */ - for (tem = menu, i = 0; CONSP (tem); tem = Fcdr (tem)) + for (tem = menu, i = 0; CONSP (tem); tem = XCDR (tem)) { Lisp_Object prompt; - maps[i++] = keymap = get_keymap (Fcar (tem), 1, 0); + maps[i++] = keymap = get_keymap (XCAR (tem), 1, 0); prompt = Fkeymap_prompt (keymap); if (NILP (title) && !NILP (prompt)) @@ -877,7 +907,8 @@ for instance using the window manager, then this produces a quit and /* Decode the first argument: find the window or frame to use. */ if (EQ (position, Qt) || (CONSP (position) && (EQ (XCAR (position), Qmenu_bar) - || EQ (XCAR (position), Qtool_bar)))) + || EQ (XCAR (position), Qtool_bar) + || EQ (XCAR (position), Qmac_apple_event)))) { #if 0 /* Using the frame the mouse is on may not be right. */ /* Use the mouse's current position. */ @@ -945,6 +976,7 @@ for instance using the window manager, then this produces a quit and Lisp_Object title; char *error_name; Lisp_Object selection; + int specpdl_count = SPECPDL_INDEX (); /* Decode the dialog items from what was specified. */ title = Fcar (contents); @@ -953,11 +985,11 @@ for instance using the window manager, then this produces a quit and list_of_panes (Fcons (contents, Qnil)); /* Display them in a dialog box. */ + record_unwind_protect (cleanup_popup_menu, Qnil); BLOCK_INPUT; selection = mac_dialog_show (f, 0, title, header, &error_name); UNBLOCK_INPUT; - - discard_menu_items (); + unbind_to (specpdl_count, Qnil); if (error_name) error (error_name); return selection; @@ -969,13 +1001,14 @@ for instance using the window manager, then this produces a quit and This is called from keyboard.c when it gets the MENU_BAR_ACTIVATE_EVENT out of the Emacs event queue. - To activate the menu bar, we signal to the input thread that it can - return from the WM_INITMENU message, allowing the normal Windows - processing of the menus. + To activate the menu bar, we use the button-press event location + that was saved in saved_menu_event_location. But first we recompute the menu bar contents (the whole tree). - This way we can safely execute Lisp code. */ + The reason for saving the button event until here, instead of + passing it to the toolkit right away, is that we can safely + execute Lisp code. */ void x_activate_menubar (f) @@ -1072,14 +1105,12 @@ menubar_selection_callback (FRAME_PTR f, int client_data) buf.arg = entry; kbd_buffer_store_event (&buf); - f->output_data.mac->menu_command_in_progress = 0; f->output_data.mac->menubar_active = 0; return; } i += MENU_ITEMS_ITEM_LENGTH; } } - f->output_data.mac->menu_command_in_progress = 0; f->output_data.mac->menubar_active = 0; } @@ -1125,22 +1156,18 @@ free_menubar_widget_value_tree (wv) UNBLOCK_INPUT; } -/* Return a tree of widget_value structures for a menu bar item +/* Set up data in menu_items for a menu bar item whose event type is ITEM_KEY (with string ITEM_NAME) and whose contents come from the list of keymaps MAPS. */ -static widget_value * -single_submenu (item_key, item_name, maps) +static int +parse_single_submenu (item_key, item_name, maps) Lisp_Object item_key, item_name, maps; { - widget_value *wv, *prev_wv, *save_wv, *first_wv; - int i; - int submenu_depth = 0; Lisp_Object length; int len; Lisp_Object *mapvec; - widget_value **submenu_stack; - int previous_items = menu_items_used; + int i; int top_level_items = 0; length = Flength (maps); @@ -1154,28 +1181,45 @@ single_submenu (item_key, item_name, maps) maps = Fcdr (maps); } - menu_items_n_panes = 0; - /* Loop over the given keymaps, making a pane for each map. But don't make a pane that is empty--ignore that map instead. */ for (i = 0; i < len; i++) { - if (SYMBOLP (mapvec[i]) - || (CONSP (mapvec[i]) && !KEYMAPP (mapvec[i]))) + if (!KEYMAPP (mapvec[i])) { /* Here we have a command at top level in the menu bar as opposed to a submenu. */ top_level_items = 1; push_menu_pane (Qnil, Qnil); push_menu_item (item_name, Qt, item_key, mapvec[i], - Qnil, Qnil, Qnil, Qnil); + Qnil, Qnil, Qnil, Qnil); } else - single_keymap_panes (mapvec[i], item_name, item_key, 0, 10); + { + Lisp_Object prompt; + prompt = Fkeymap_prompt (mapvec[i]); + single_keymap_panes (mapvec[i], + !NILP (prompt) ? prompt : item_name, + item_key, 0, 10); + } } - /* Create a tree of widget_value objects - representing the panes and their items. */ + return top_level_items; +} + +/* Create a tree of widget_value objects + representing the panes and items + in menu_items starting at index START, up to index END. */ + +static widget_value * +digest_single_submenu (start, end, top_level_items) + int start, end, top_level_items; +{ + widget_value *wv, *prev_wv, *save_wv, *first_wv; + int i; + int submenu_depth = 0; + widget_value **submenu_stack; + int panes_seen = 0; submenu_stack = (widget_value **) alloca (menu_items_used * sizeof (widget_value *)); @@ -1189,12 +1233,12 @@ single_submenu (item_key, item_name, maps) save_wv = 0; prev_wv = 0; - /* Loop over all panes and items made during this call - and construct a tree of widget_value objects. - Ignore the panes and items made by previous calls to - single_submenu, even though those are also in menu_items. */ - i = previous_items; - while (i < menu_items_used) + /* Loop over all panes and items made by the preceding call + to parse_single_submenu and construct a tree of widget_value objects. + Ignore the panes and items used by previous calls to + digest_single_submenu, even though those are also in menu_items. */ + i = start; + while (i < end) { if (EQ (XVECTOR (menu_items)->contents[i], Qnil)) { @@ -1222,13 +1266,15 @@ single_submenu (item_key, item_name, maps) Lisp_Object pane_name, prefix; char *pane_string; + panes_seen++; + pane_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_NAME]; prefix = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX]; #ifndef HAVE_MULTILINGUAL_MENU if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name)) { - pane_name = ENCODE_SYSTEM (pane_name); + pane_name = ENCODE_MENU_STRING (pane_name); AREF (menu_items, i + MENU_ITEMS_PANE_NAME) = pane_name; } #endif @@ -1255,8 +1301,11 @@ single_submenu (item_key, item_name, maps) wv->enabled = 1; wv->button_type = BUTTON_TYPE_NONE; wv->help = Qnil; + save_wv = wv; } - save_wv = wv; + else + save_wv = first_wv; + prev_wv = 0; i += MENU_ITEMS_PANE_LENGTH; } @@ -1264,7 +1313,11 @@ single_submenu (item_key, item_name, maps) { /* Create a new item within current pane. */ Lisp_Object item_name, enable, descrip, def, type, selected; - Lisp_Object help; + Lisp_Object help; + + /* All items should be contained in panes. */ + if (panes_seen == 0) + abort (); item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME); enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE); @@ -1275,13 +1328,13 @@ single_submenu (item_key, item_name, maps) help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP); #ifndef HAVE_MULTILINGUAL_MENU - if (STRING_MULTIBYTE (item_name)) + if (STRING_MULTIBYTE (item_name)) { item_name = ENCODE_MENU_STRING (item_name); AREF (menu_items, i + MENU_ITEMS_ITEM_NAME) = item_name; } - if (STRINGP (descrip) && STRING_MULTIBYTE (descrip)) + if (STRINGP (descrip) && STRING_MULTIBYTE (descrip)) { descrip = ENCODE_MENU_STRING (descrip); AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY) = descrip; @@ -1313,7 +1366,7 @@ single_submenu (item_key, item_name, maps) abort (); wv->selected = !NILP (selected); - if (!STRINGP (help)) + if (! STRINGP (help)) help = Qnil; wv->help = help; @@ -1335,6 +1388,7 @@ single_submenu (item_key, item_name, maps) return first_wv; } + /* Walk through the widget_value tree starting at FIRST_WV and update the char * pointers from the corresponding lisp values. We do this after building the whole tree, since GC may happen while the @@ -1381,19 +1435,21 @@ menu_quit_handler (nextHandler, theEvent, userData) EventRef theEvent; void* userData; { + OSStatus err; UInt32 keyCode; UInt32 keyModifiers; extern int mac_quit_char_modifiers; extern int mac_quit_char_keycode; - GetEventParameter (theEvent, kEventParamKeyCode, - typeUInt32, NULL, sizeof(UInt32), NULL, &keyCode); + err = GetEventParameter (theEvent, kEventParamKeyCode, + typeUInt32, NULL, sizeof(UInt32), NULL, &keyCode); - GetEventParameter (theEvent, kEventParamKeyModifiers, - typeUInt32, NULL, sizeof(UInt32), - NULL, &keyModifiers); + if (err == noErr) + err = GetEventParameter (theEvent, kEventParamKeyModifiers, + typeUInt32, NULL, sizeof(UInt32), + NULL, &keyModifiers); - if (keyCode == mac_quit_char_keycode + if (err == noErr && keyCode == mac_quit_char_keycode && keyModifiers == mac_quit_char_modifiers) { MenuRef menu = userData != 0 @@ -1408,28 +1464,30 @@ menu_quit_handler (nextHandler, theEvent, userData) } #endif /* HAVE_CANCELMENUTRACKING */ -/* Add event handler for MENU_HANDLE so we can detect C-g. - If MENU_HANDLE is NULL, install handler for all menus in the menu bar. +/* Add event handler to all menus that belong to KIND so we can detect C-g. + MENU_HANDLE is the root menu of the tracking session to dismiss + when C-g is detected. NULL means the menu bar. If CancelMenuTracking isn't available, do nothing. */ static void -install_menu_quit_handler (MenuHandle menu_handle) +install_menu_quit_handler (kind, menu_handle) + enum mac_menu_kind kind; + MenuHandle menu_handle; { #ifdef HAVE_CANCELMENUTRACKING - EventHandlerUPP handler = NewEventHandlerUPP(menu_quit_handler); - UInt32 numTypes = 1; EventTypeSpec typesList[] = { { kEventClassKeyboard, kEventRawKeyDown } }; - int i = MIN_MENU_ID; - MenuHandle menu = menu_handle ? menu_handle : GetMenuHandle (i); + int id; - while (menu != NULL) + for (id = min_menu_id[kind]; id < min_menu_id[kind + 1]; id++) { - InstallMenuEventHandler (menu, handler, GetEventTypeCount (typesList), + MenuHandle menu = GetMenuHandle (id); + + if (menu == NULL) + break; + InstallMenuEventHandler (menu, menu_quit_handler, + GetEventTypeCount (typesList), typesList, menu_handle, NULL); - if (menu_handle) break; - menu = GetMenuHandle (++i); } - DisposeEventHandlerUPP (handler); #endif /* HAVE_CANCELMENUTRACKING */ } @@ -1446,7 +1504,9 @@ set_frame_menubar (f, first_time, deep_p) int menubar_widget = f->output_data.mac->menubar_widget; Lisp_Object items; widget_value *wv, *first_wv, *prev_wv = 0; - int i; + int i, last_i = 0; + int *submenu_start, *submenu_end; + int *submenu_top_level_items, *submenu_n_panes; /* We must not change the menubar when actually in use. */ if (f->output_data.mac->menubar_active) @@ -1459,14 +1519,6 @@ set_frame_menubar (f, first_time, deep_p) else if (pending_menu_activation && !deep_p) deep_p = 1; - wv = xmalloc_widget_value (); - wv->name = "menubar"; - wv->value = 0; - wv->enabled = 1; - wv->button_type = BUTTON_TYPE_NONE; - wv->help = Qnil; - first_wv = wv; - if (deep_p) { /* Make a widget-value tree representing the entire menu trees. */ @@ -1501,6 +1553,7 @@ set_frame_menubar (f, first_time, deep_p) /* Run the Lucid hook. */ safe_run_hooks (Qactivate_menubar_hook); + /* If it has changed current-menubar from previous value, really recompute the menubar from the value. */ if (! NILP (Vlucid_menu_bar_dirty_flag)) @@ -1515,21 +1568,58 @@ set_frame_menubar (f, first_time, deep_p) bcopy (XVECTOR (f->menu_bar_vector)->contents, previous_items, previous_menu_items_used * sizeof (Lisp_Object)); - /* Fill in the current menu bar contents. */ + /* Fill in menu_items with the current menu bar contents. + This can evaluate Lisp code. */ + save_menu_items (); + menu_items = f->menu_bar_vector; menu_items_allocated = VECTORP (menu_items) ? ASIZE (menu_items) : 0; + submenu_start = (int *) alloca (XVECTOR (items)->size * sizeof (int *)); + submenu_end = (int *) alloca (XVECTOR (items)->size * sizeof (int *)); + submenu_n_panes = (int *) alloca (XVECTOR (items)->size * sizeof (int)); + submenu_top_level_items + = (int *) alloca (XVECTOR (items)->size * sizeof (int *)); init_menu_items (); for (i = 0; i < XVECTOR (items)->size; i += 4) { Lisp_Object key, string, maps; + last_i = i; + key = XVECTOR (items)->contents[i]; string = XVECTOR (items)->contents[i + 1]; maps = XVECTOR (items)->contents[i + 2]; if (NILP (string)) break; - wv = single_submenu (key, string, maps); + submenu_start[i] = menu_items_used; + + menu_items_n_panes = 0; + submenu_top_level_items[i] + = parse_single_submenu (key, string, maps); + submenu_n_panes[i] = menu_items_n_panes; + + submenu_end[i] = menu_items_used; + } + + finish_menu_items (); + + /* Convert menu_items into widget_value trees + to display the menu. This cannot evaluate Lisp code. */ + + wv = xmalloc_widget_value (); + wv->name = "menubar"; + wv->value = 0; + wv->enabled = 1; + wv->button_type = BUTTON_TYPE_NONE; + wv->help = Qnil; + first_wv = wv; + + for (i = 0; i < last_i; i += 4) + { + menu_items_n_panes = submenu_n_panes[i]; + wv = digest_single_submenu (submenu_start[i], submenu_end[i], + submenu_top_level_items[i]); if (prev_wv) prev_wv->next = wv; else @@ -1540,32 +1630,36 @@ set_frame_menubar (f, first_time, deep_p) prev_wv = wv; } - finish_menu_items (); - set_buffer_internal_1 (prev); - unbind_to (specpdl_count, Qnil); /* If there has been no change in the Lisp-level contents of the menu bar, skip redisplaying it. Just exit. */ + /* Compare the new menu items with the ones computed last time. */ for (i = 0; i < previous_menu_items_used; i++) if (menu_items_used == i - || (NILP (Fequal (previous_items[i], - XVECTOR (menu_items)->contents[i])))) + || (!EQ (previous_items[i], XVECTOR (menu_items)->contents[i]))) break; if (i == menu_items_used && i == previous_menu_items_used && i != 0) { + /* The menu items have not changed. Don't bother updating + the menus in any form, since it would be a no-op. */ free_menubar_widget_value_tree (first_wv); - menu_items = Qnil; - + discard_menu_items (); + unbind_to (specpdl_count, Qnil); return; } + /* The menu items are different, so store them in the frame. */ + f->menu_bar_vector = menu_items; + f->menu_bar_items_used = menu_items_used; + + /* This calls restore_menu_items to restore menu_items, etc., + as they were outside. */ + unbind_to (specpdl_count, Qnil); + /* Now GC cannot happen during the lifetime of the widget_value, - so it's safe to store data from a Lisp_String, as long as - local copies are made when the actual menu is created. - Windows takes care of this for normal string items, but - not for owner-drawn items or additional item-info. */ + so it's safe to store data from a Lisp_String. */ wv = first_wv->contents; for (i = 0; i < XVECTOR (items)->size; i += 4) { @@ -1578,15 +1672,20 @@ set_frame_menubar (f, first_time, deep_p) wv = wv->next; } - f->menu_bar_vector = menu_items; - f->menu_bar_items_used = menu_items_used; - menu_items = Qnil; } else { /* Make a widget-value tree containing just the top level menu bar strings. */ + wv = xmalloc_widget_value (); + wv->name = "menubar"; + wv->value = 0; + wv->enabled = 1; + wv->button_type = BUTTON_TYPE_NONE; + wv->help = Qnil; + first_wv = wv; + items = FRAME_MENU_BAR_ITEMS (f); for (i = 0; i < XVECTOR (items)->size; i += 4) { @@ -1628,52 +1727,16 @@ set_frame_menubar (f, first_time, deep_p) /* Non-null value to indicate menubar has already been "created". */ f->output_data.mac->menubar_widget = 1; - { - int i = MIN_MENU_ID; - MenuHandle menu = GetMenuHandle (i); - while (menu != NULL) - { - DeleteMenu (i); - DisposeMenu (menu); - menu = GetMenuHandle (++i); - } - - i = MIN_SUBMENU_ID; - menu = GetMenuHandle (i); - while (menu != NULL) - { - DeleteMenu (i); - DisposeMenu (menu); - menu = GetMenuHandle (++i); - } - } - - fill_menubar (first_wv->contents); - - DrawMenuBar (); + fill_menubar (first_wv->contents, deep_p); /* Add event handler so we can detect C-g. */ - install_menu_quit_handler (NULL); + install_menu_quit_handler (MAC_MENU_MENU_BAR, NULL); + install_menu_quit_handler (MAC_MENU_MENU_BAR_SUB, NULL); free_menubar_widget_value_tree (first_wv); UNBLOCK_INPUT; } -/* Called from Fx_create_frame to create the initial menubar of a frame - before it is mapped, so that the window is mapped with the menubar already - there instead of us tacking it on later and thrashing the window after it - is visible. */ - -void -initialize_frame_menubar (f) - FRAME_PTR f; -{ - /* This function is called before the first chance to redisplay - the frame. It has to be, so the frame will have the right size. */ - FRAME_MENU_BAR_ITEMS (f) = menu_bar_items (FRAME_MENU_BAR_ITEMS (f)); - set_frame_menubar (f, 1, 1); -} - /* Get rid of the menu bar of frame F, and free its storage. This is used when deleting a frame, and when turning off the menu bar. */ @@ -1681,7 +1744,7 @@ void free_frame_menubar (f) FRAME_PTR f; { - f->output_data.mac->menubar_widget = NULL; + f->output_data.mac->menubar_widget = 0; } @@ -1689,11 +1752,9 @@ static Lisp_Object pop_down_menu (arg) Lisp_Object arg; { - struct Lisp_Save_Value *p1 = XSAVE_VALUE (Fcar (arg)); - struct Lisp_Save_Value *p2 = XSAVE_VALUE (Fcdr (arg)); - - FRAME_PTR f = p1->pointer; - MenuHandle *menu = p2->pointer; + struct Lisp_Save_Value *p = XSAVE_VALUE (arg); + FRAME_PTR f = p->pointer; + MenuHandle menu = GetMenuHandle (min_menu_id[MAC_MENU_POPUP]); BLOCK_INPUT; @@ -1702,19 +1763,9 @@ pop_down_menu (arg) FRAME_MAC_DISPLAY_INFO (f)->grabbed = 0; /* delete all menus */ - { - int i = MIN_POPUP_SUBMENU_ID; - MenuHandle submenu = GetMenuHandle (i); - while (submenu != NULL) - { - DeleteMenu (i); - DisposeMenu (submenu); - submenu = GetMenuHandle (++i); - } - } - - DeleteMenu (POPUP_SUBMENU_ID); - DisposeMenu (*menu); + dispose_menus (MAC_MENU_POPUP_SUB, 0); + DeleteMenu (min_menu_id[MAC_MENU_POPUP]); + DisposeMenu (menu); UNBLOCK_INPUT; @@ -1758,6 +1809,7 @@ mac_menu_show (f, x, y, for_click, keymaps, title, error) Lisp_Object *subprefix_stack = (Lisp_Object *) alloca (menu_items_used * sizeof (Lisp_Object)); int submenu_depth = 0; + int first_pane; int specpdl_count = SPECPDL_INDEX (); @@ -1811,12 +1863,14 @@ mac_menu_show (f, x, y, for_click, keymaps, title, error) /* Create a new pane. */ Lisp_Object pane_name, prefix; char *pane_string; + pane_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME); prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX); + #ifndef HAVE_MULTILINGUAL_MENU if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name)) { - pane_name = ENCODE_SYSTEM (pane_name); + pane_name = ENCODE_MENU_STRING (pane_name); AREF (menu_items, i + MENU_ITEMS_PANE_NAME) = pane_name; } #endif @@ -1859,14 +1913,13 @@ mac_menu_show (f, x, y, for_click, keymaps, title, error) { /* Create a new item within current pane. */ Lisp_Object item_name, enable, descrip, def, type, selected, help; - item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME); enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE); descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY); def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION); type = AREF (menu_items, i + MENU_ITEMS_ITEM_TYPE); selected = AREF (menu_items, i + MENU_ITEMS_ITEM_SELECTED); - help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP); + help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP); #ifndef HAVE_MULTILINGUAL_MENU if (STRINGP (item_name) && STRING_MULTIBYTE (item_name)) @@ -1874,8 +1927,9 @@ mac_menu_show (f, x, y, for_click, keymaps, title, error) item_name = ENCODE_MENU_STRING (item_name); AREF (menu_items, i + MENU_ITEMS_ITEM_NAME) = item_name; } + if (STRINGP (descrip) && STRING_MULTIBYTE (descrip)) - { + { descrip = ENCODE_MENU_STRING (descrip); AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY) = descrip; } @@ -1905,7 +1959,8 @@ mac_menu_show (f, x, y, for_click, keymaps, title, error) abort (); wv->selected = !NILP (selected); - if (!STRINGP (help)) + + if (! STRINGP (help)) help = Qnil; wv->help = help; @@ -1932,6 +1987,7 @@ mac_menu_show (f, x, y, for_click, keymaps, title, error) if (STRING_MULTIBYTE (title)) title = ENCODE_MENU_STRING (title); #endif + wv_title->name = (char *) SDATA (title); wv_title->enabled = FALSE; wv_title->title = TRUE; @@ -1942,9 +1998,10 @@ mac_menu_show (f, x, y, for_click, keymaps, title, error) } /* Actually create the menu. */ - menu = NewMenu (POPUP_SUBMENU_ID, "\p"); - submenu_id = MIN_POPUP_SUBMENU_ID; - fill_submenu (menu, first_wv->contents); + menu = NewMenu (min_menu_id[MAC_MENU_POPUP], "\p"); + InsertMenu (menu, -1); + fill_menu (menu, first_wv->contents, MAC_MENU_POPUP_SUB, + min_menu_id[MAC_MENU_POPUP_SUB]); /* Free the widget_value objects we used to specify the contents. */ @@ -1955,21 +2012,17 @@ mac_menu_show (f, x, y, for_click, keymaps, title, error) pos.v = y; SetPortWindowPort (FRAME_MAC_WINDOW (f)); - LocalToGlobal (&pos); /* No selection has been chosen yet. */ menu_item_choice = 0; menu_item_selection = 0; - InsertMenu (menu, -1); - - record_unwind_protect (pop_down_menu, - Fcons (make_save_value (f, 0), - make_save_value (&menu, 0))); + record_unwind_protect (pop_down_menu, make_save_value (f, 0)); /* Add event handler so we can detect C-g. */ - install_menu_quit_handler (menu); + install_menu_quit_handler (MAC_MENU_POPUP, menu); + install_menu_quit_handler (MAC_MENU_POPUP_SUB, menu); /* Display the menu. */ menu_item_choice = PopUpMenuSelect (menu, pos.v, pos.h, 0); @@ -2165,11 +2218,11 @@ static char * button_names [] = { "button6", "button7", "button8", "button9", "button10" }; static Lisp_Object -mac_dialog_show (f, keymaps, title, header, error) +mac_dialog_show (f, keymaps, title, header, error_name) FRAME_PTR f; int keymaps; Lisp_Object title, header; - char **error; + char **error_name; { int i, nb_buttons=0; char dialog_name[6]; @@ -2182,11 +2235,11 @@ mac_dialog_show (f, keymaps, title, header, error) /* 1 means we've seen the boundary between left-hand elts and right-hand. */ int boundary_seen = 0; - *error = NULL; + *error_name = NULL; if (menu_items_n_panes > 1) { - *error = "Multiple panes in dialog box"; + *error_name = "Multiple panes in dialog box"; return Qnil; } @@ -2214,18 +2267,16 @@ mac_dialog_show (f, keymaps, title, header, error) { /* Create a new item within current pane. */ - Lisp_Object item_name, enable, descrip, help; - + Lisp_Object item_name, enable, descrip; item_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_NAME]; enable = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_ENABLE]; descrip = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_EQUIV_KEY]; - help = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_HELP]; if (NILP (item_name)) { free_menubar_widget_value_tree (first_wv); - *error = "Submenu in dialog items"; + *error_name = "Submenu in dialog items"; return Qnil; } if (EQ (item_name, Qquote)) @@ -2239,7 +2290,7 @@ mac_dialog_show (f, keymaps, title, header, error) if (nb_buttons >= 9) { free_menubar_widget_value_tree (first_wv); - *error = "Too many dialog items"; + *error_name = "Too many dialog items"; return Qnil; } @@ -2302,8 +2353,8 @@ mac_dialog_show (f, keymaps, title, header, error) /* Free the widget_value objects we used to specify the contents. */ free_menubar_widget_value_tree (first_wv); - /* Find the selected item, and its pane, to return the proper - value. */ + /* Find the selected item, and its pane, to return + the proper value. */ if (menu_item_selection != 0) { Lisp_Object prefix; @@ -2320,6 +2371,12 @@ mac_dialog_show (f, keymaps, title, header, error) = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX]; i += MENU_ITEMS_PANE_LENGTH; } + else if (EQ (XVECTOR (menu_items)->contents[i], Qquote)) + { + /* This is the boundary between left-side elts and + right-side elts. */ + ++i; + } else { entry @@ -2338,6 +2395,9 @@ mac_dialog_show (f, keymaps, title, header, error) } } } + else + /* Make "Cancel" equivalent to C-g. */ + Fsignal (Qquit, Qnil); return Qnil; } @@ -2360,11 +2420,16 @@ name_is_separator (name) } static void -add_menu_item (MenuHandle menu, widget_value *wv, int submenu, - int force_disable) +add_menu_item (menu, pos, wv) + MenuHandle menu; + int pos; + widget_value *wv; { +#if TARGET_API_MAC_CARBON + CFStringRef item_name; +#else Str255 item_name; - int pos; +#endif if (name_is_separator (wv->name)) AppendMenu (menu, "\p-"); @@ -2373,125 +2438,188 @@ add_menu_item (MenuHandle menu, widget_value *wv, int submenu, AppendMenu (menu, "\pX"); #if TARGET_API_MAC_CARBON - pos = CountMenuItems (menu); -#else - pos = CountMItems (menu); -#endif + item_name = cfstring_create_with_utf8_cstring (wv->name); - strcpy (item_name, ""); - strncat (item_name, wv->name, 255); if (wv->key != NULL) { - strncat (item_name, " ", 255); - strncat (item_name, wv->key, 255); + CFStringRef name, key; + + name = item_name; + key = cfstring_create_with_utf8_cstring (wv->key); + item_name = CFStringCreateWithFormat (NULL, NULL, CFSTR ("%@ %@"), + name, key); + CFRelease (name); + CFRelease (key); } - item_name[255] = 0; -#if TARGET_API_MAC_CARBON - { - CFStringRef string = cfstring_create_with_utf8_cstring (item_name); - SetMenuItemTextWithCFString (menu, pos, string); - CFRelease (string); - } -#else + SetMenuItemTextWithCFString (menu, pos, item_name); + CFRelease (item_name); + + if (wv->enabled) + EnableMenuItem (menu, pos); + else + DisableMenuItem (menu, pos); +#else /* ! TARGET_API_MAC_CARBON */ + item_name[sizeof (item_name) - 1] = '\0'; + strncpy (item_name, wv->name, sizeof (item_name) - 1); + if (wv->key != NULL) + { + int len = strlen (item_name); + + strncpy (item_name + len, " ", sizeof (item_name) - 1 - len); + len = strlen (item_name); + strncpy (item_name + len, wv->key, sizeof (item_name) - 1 - len); + } c2pstr (item_name); SetMenuItemText (menu, pos, item_name); -#endif - if (wv->enabled && !force_disable) -#if TARGET_API_MAC_CARBON - EnableMenuItem (menu, pos); -#else + if (wv->enabled) EnableItem (menu, pos); -#endif else -#if TARGET_API_MAC_CARBON - DisableMenuItem (menu, pos); -#else DisableItem (menu, pos); -#endif +#endif /* ! TARGET_API_MAC_CARBON */ /* Draw radio buttons and tickboxes. */ - { if (wv->selected && (wv->button_type == BUTTON_TYPE_TOGGLE || wv->button_type == BUTTON_TYPE_RADIO)) SetItemMark (menu, pos, checkMark); else SetItemMark (menu, pos, noMark); - } SetMenuItemRefCon (menu, pos, (UInt32) wv->call_data); } - - if (submenu != NULL) - SetMenuItemHierarchicalID (menu, pos, submenu); } -/* Construct native Mac OS menubar based on widget_value tree. */ +/* Construct native Mac OS menu based on widget_value tree. */ -static void -fill_submenu (MenuHandle menu, widget_value *wv) +static int +fill_menu (menu, wv, kind, submenu_id) + MenuHandle menu; + widget_value *wv; + enum mac_menu_kind kind; + int submenu_id; { - for ( ; wv != NULL; wv = wv->next) - if (wv->contents) - { - int cur_submenu = submenu_id++; - MenuHandle submenu = NewMenu (cur_submenu, "\pX"); - fill_submenu (submenu, wv->contents); - InsertMenu (submenu, -1); - add_menu_item (menu, wv, cur_submenu, 0); - } - else - add_menu_item (menu, wv, NULL, 0); -} + int pos; + for (pos = 1; wv != NULL; wv = wv->next, pos++) + { + add_menu_item (menu, pos, wv); + if (wv->contents && submenu_id < min_menu_id[kind + 1]) + { + MenuHandle submenu = NewMenu (submenu_id, "\pX"); -/* Construct native Mac OS menu based on widget_value tree. */ + InsertMenu (submenu, -1); + SetMenuItemHierarchicalID (menu, pos, submenu_id); + submenu_id = fill_menu (submenu, wv->contents, kind, submenu_id + 1); + } + } -static void -fill_menu (MenuHandle menu, widget_value *wv) -{ - for ( ; wv != NULL; wv = wv->next) - if (wv->contents) - { - int cur_submenu = submenu_id++; - MenuHandle submenu = NewMenu (cur_submenu, "\pX"); - fill_submenu (submenu, wv->contents); - InsertMenu (submenu, -1); - add_menu_item (menu, wv, cur_submenu, 0); - } - else - add_menu_item (menu, wv, NULL, 0); + return submenu_id; } /* Construct native Mac OS menubar based on widget_value tree. */ static void -fill_menubar (widget_value *wv) +fill_menubar (wv, deep_p) + widget_value *wv; + int deep_p; { - int id; - - submenu_id = MIN_SUBMENU_ID; + int id, submenu_id; + MenuHandle menu; + Str255 title; +#if !TARGET_API_MAC_CARBON + int title_changed_p = 0; +#endif - for (id = MIN_MENU_ID; wv != NULL; wv = wv->next, id++) + /* Clean up the menu bar when filled by the entire menu trees. */ + if (deep_p) { - MenuHandle menu; - Str255 title; + dispose_menus (MAC_MENU_MENU_BAR, 0); + dispose_menus (MAC_MENU_MENU_BAR_SUB, 0); +#if !TARGET_API_MAC_CARBON + title_changed_p = 1; +#endif + } + /* Fill menu bar titles and submenus. Reuse the existing menu bar + titles as much as possible to minimize redraw (if !deep_p). */ + submenu_id = min_menu_id[MAC_MENU_MENU_BAR_SUB]; + for (id = min_menu_id[MAC_MENU_MENU_BAR]; + wv != NULL && id < min_menu_id[MAC_MENU_MENU_BAR + 1]; + wv = wv->next, id++) + { strncpy (title, wv->name, 255); - title[255] = 0; + title[255] = '\0'; c2pstr (title); - menu = NewMenu (id, title); + + menu = GetMenuHandle (id); + if (menu) + { +#if TARGET_API_MAC_CARBON + Str255 old_title; + + GetMenuTitle (menu, old_title); + if (!EqualString (title, old_title, false, false)) + SetMenuTitle (menu, title); +#else /* !TARGET_API_MAC_CARBON */ + if (!EqualString (title, (*menu)->menuData, false, false)) + { + DeleteMenu (id); + DisposeMenu (menu); + menu = NewMenu (id, title); + InsertMenu (menu, GetMenuHandle (id + 1) ? id + 1 : 0); + title_changed_p = 1; + } +#endif /* !TARGET_API_MAC_CARBON */ + } + else + { + menu = NewMenu (id, title); + InsertMenu (menu, 0); +#if !TARGET_API_MAC_CARBON + title_changed_p = 1; +#endif + } if (wv->contents) - fill_menu (menu, wv->contents); + submenu_id = fill_menu (menu, wv->contents, MAC_MENU_MENU_BAR_SUB, + submenu_id); + } - InsertMenu (menu, 0); + if (id < min_menu_id[MAC_MENU_MENU_BAR + 1] && GetMenuHandle (id)) + { + dispose_menus (MAC_MENU_MENU_BAR, id); +#if !TARGET_API_MAC_CARBON + title_changed_p = 1; +#endif } + +#if !TARGET_API_MAC_CARBON + if (title_changed_p) + InvalMenuBar (); +#endif } -#endif /* HAVE_MENUS */ +/* Dispose of menus that belong to KIND, and remove them from the menu + list. ID is the lower bound of menu IDs that will be processed. */ +static void +dispose_menus (kind, id) + enum mac_menu_kind kind; + int id; +{ + for (id = max (id, min_menu_id[kind]); id < min_menu_id[kind + 1]; id++) + { + MenuHandle menu = GetMenuHandle (id); + + if (menu == NULL) + break; + DeleteMenu (id); + DisposeMenu (menu); + } +} + +#endif /* HAVE_MENUS */ void syms_of_macmenu ()