X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/50bf76732ba3cd84d56185685e58604dd139ba94..b8dc613f4501331628cc292c65ba0768d6537b2e:/src/macmenu.c diff --git a/src/macmenu.c b/src/macmenu.c index d205ee3b87..ab266f6f0b 100644 --- a/src/macmenu.c +++ b/src/macmenu.c @@ -15,13 +15,12 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Emacs; see the file COPYING. If not, write to -the Free Software Foundation, Inc., 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. */ +the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. */ /* Contributed by Andrew Choi (akochoi@mac.com). */ #include -#include #include #include "lisp.h" @@ -35,7 +34,7 @@ Boston, MA 02111-1307, USA. */ #include "charset.h" #include "coding.h" -#ifndef MAC_OSX +#if !TARGET_API_MAC_CARBON #include #include #include @@ -47,7 +46,7 @@ Boston, MA 02111-1307, USA. */ #if defined (__MRC__) || (__MSL__ >= 0x6000) #include #endif -#endif /* not MAC_OSX */ +#endif /* not TARGET_API_MAC_CARBON */ /* This may include sys/types.h, and that somehow loses if this is not done before the other system files. */ @@ -90,10 +89,12 @@ enum button_type typedef struct _widget_value { /* name of widget */ + Lisp_Object lname; char* name; /* value (meaning depend on widget type) */ char* value; /* keyboard equivalent. no implications for XtTranslations */ + Lisp_Object lkey; char* key; /* Help string or nil if none. GC finds this string through the frame's menu_bar_vector @@ -163,6 +164,12 @@ extern Lisp_Object Qoverriding_local_map, Qoverriding_terminal_local_map; extern Lisp_Object Qmenu_bar_update_hook; +#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, @@ -595,6 +602,13 @@ list_of_items (pane) } } +static Lisp_Object +cleanup_popup_menu (arg) + Lisp_Object arg; +{ + discard_menu_items (); +} + DEFUN ("x-popup-menu", Fx_popup_menu, Sx_popup_menu, 2, 2, 0, doc: /* Pop up a deck-of-cards menu and return user's selection. POSITION is a position specification. This is either a mouse button @@ -640,6 +654,8 @@ cached information about equivalent key sequences. */) int keymaps = 0; int for_click = 0; struct gcpro gcpro1; + int specpdl_count = SPECPDL_INDEX (); + #ifdef HAVE_MENUS if (! NILP (position)) @@ -799,13 +815,13 @@ cached information about equivalent key sequences. */) #ifdef HAVE_MENUS /* Display them in a menu. */ + record_unwind_protect (cleanup_popup_menu, Qnil); BLOCK_INPUT; selection = mac_menu_show (f, xpos, ypos, for_click, keymaps, title, &error_name); UNBLOCK_INPUT; - - discard_menu_items (); + unbind_to (specpdl_count, Qnil); UNGCPRO; #endif /* HAVE_MENUS */ @@ -816,7 +832,7 @@ cached information about equivalent key sequences. */) #ifdef HAVE_MENUS -DEFUN ("x-popup-dialog", Fx_popup_dialog, Sx_popup_dialog, 2, 2, 0, +DEFUN ("x-popup-dialog", Fx_popup_dialog, Sx_popup_dialog, 2, 3, 0, doc: /* Pop up a dialog box and return user's selection. POSITION specifies which frame to use. This is normally a mouse button event or a window or frame. @@ -831,9 +847,12 @@ The return value is VALUE from the chosen item. An ITEM may also be just a string--that makes a nonselectable item. An ITEM may also be nil--that means to put all preceding items on the left of the dialog box and all following items on the right. -\(By default, approximately half appear on each side.) */) - (position, contents) - Lisp_Object position, contents; +\(By default, approximately half appear on each side.) + +If HEADER is non-nil, the frame title for the box is "Information", +otherwise it is "Question". */) + (position, contents, header) + Lisp_Object position, contents, header; { FRAME_PTR f = NULL; Lisp_Object window; @@ -920,7 +939,7 @@ on the left of the dialog box and all following items on the right. /* Display them in a dialog box. */ BLOCK_INPUT; - selection = mac_dialog_show (f, 0, title, &error_name); + selection = mac_dialog_show (f, 0, title, header, &error_name); UNBLOCK_INPUT; discard_menu_items (); @@ -1215,12 +1234,9 @@ single_submenu (item_key, item_name, maps) save_wv->next = wv; else first_wv->contents = wv; - wv->name = pane_string; - /* Ignore the @ that means "separate pane". - This is a kludge, but this isn't worth more time. */ - if (!NILP (prefix) && wv->name[0] == '@') - wv->name++; - wv->value = 0; + wv->lname = pane_name; + /* Set value to 1 so update_submenu_strings can handle '@' */ + wv->value = (char *)1; wv->enabled = 1; wv->button_type = BUTTON_TYPE_NONE; wv->help = Qnil; @@ -1246,13 +1262,13 @@ single_submenu (item_key, item_name, maps) #ifndef HAVE_MULTILINGUAL_MENU if (STRING_MULTIBYTE (item_name)) { - item_name = ENCODE_SYSTEM (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)) { - descrip = ENCODE_SYSTEM (descrip); + descrip = ENCODE_MENU_STRING (descrip); AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY) = descrip; } #endif /* not HAVE_MULTILINGUAL_MENU */ @@ -1263,9 +1279,9 @@ single_submenu (item_key, item_name, maps) else save_wv->contents = wv; - wv->name = (char *) SDATA (item_name); + wv->lname = item_name; if (!NILP (descrip)) - wv->key = (char *) SDATA (descrip); + wv->lkey = descrip; wv->value = 0; /* The EMACS_INT cast avoids a warning. There's no problem as long as pointers have enough bits to hold small integers. */ @@ -1304,7 +1320,104 @@ 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 + tree is constructed, and small strings are relocated. So we must wait + until no GC can happen before storing pointers into lisp values. */ +static void +update_submenu_strings (first_wv) + widget_value *first_wv; +{ + widget_value *wv; + + for (wv = first_wv; wv; wv = wv->next) + { + if (STRINGP (wv->lname)) + { + wv->name = SDATA (wv->lname); + + /* Ignore the @ that means "separate pane". + This is a kludge, but this isn't worth more time. */ + if (wv->value == (char *)1) + { + if (wv->name[0] == '@') + wv->name++; + wv->value = 0; + } + } + + if (STRINGP (wv->lkey)) + wv->key = SDATA (wv->lkey); + + if (wv->contents) + update_submenu_strings (wv->contents); + } +} + +/* Event handler function that pops down a menu on C-g. We can only pop + down menus if CancelMenuTracking is present (OSX 10.3 or later). */ + +#ifdef HAVE_CANCELMENUTRACKING +static pascal OSStatus +menu_quit_handler (nextHandler, theEvent, userData) + EventHandlerCallRef nextHandler; + EventRef theEvent; + void* userData; +{ + 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); + + GetEventParameter (theEvent, kEventParamKeyModifiers, + typeUInt32, NULL, sizeof(UInt32), + NULL, &keyModifiers); + + if (keyCode == mac_quit_char_keycode + && keyModifiers == mac_quit_char_modifiers) + { + MenuRef menu = userData != 0 + ? (MenuRef)userData : AcquireRootMenu (); + + CancelMenuTracking (menu, true, 0); + if (!userData) ReleaseMenu (menu); + return noErr; + } + + return CallNextEventHandler (nextHandler, theEvent); +} +#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. + If CancelMenuTracking isn't available, do nothing. */ + +static void +install_menu_quit_handler (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); + + while (menu != NULL) + { + InstallMenuEventHandler (menu, handler, GetEventTypeCount (typesList), + typesList, menu_handle, NULL); + if (menu_handle) break; + menu = GetMenuHandle (++i); + } + DisposeEventHandlerUPP (handler); +#endif /* HAVE_CANCELMENUTRACKING */ +} + /* Set the contents of the menubar widgets of frame F. The argument FIRST_TIME is currently ignored; it is set the first time this is called, from initialize_frame_menubar. */ @@ -1362,7 +1475,7 @@ set_frame_menubar (f, first_time, deep_p) because it is not reentrant. */ specbind (Qdebug_on_next_call, Qnil); - record_unwind_protect (Fset_match_data, Fmatch_data (Qnil, Qnil)); + record_unwind_save_match_data (); if (NILP (Voverriding_local_map_menu_flag)) { specbind (Qoverriding_terminal_local_map, Qnil); @@ -1382,8 +1495,6 @@ set_frame_menubar (f, first_time, deep_p) items = FRAME_MENU_BAR_ITEMS (f); - inhibit_garbage_collection (); - /* Save the frame's previous menu bar contents data. */ if (previous_menu_items_used) bcopy (XVECTOR (f->menu_bar_vector)->contents, previous_items, @@ -1448,6 +1559,7 @@ set_frame_menubar (f, first_time, deep_p) if (NILP (string)) break; wv->name = (char *) SDATA (string); + update_submenu_strings (wv->contents); wv = wv->next; } @@ -1525,6 +1637,8 @@ set_frame_menubar (f, first_time, deep_p) DrawMenuBar (); + /* Add event handler so we can detect C-g. */ + install_menu_quit_handler (NULL); free_menubar_widget_value_tree (first_wv); UNBLOCK_INPUT; @@ -1556,7 +1670,43 @@ free_frame_menubar (f) } -/* mac_menu_show actually displays a menu using the panes and items in +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; + + BLOCK_INPUT; + + /* Must reset this manually because the button release event is not + passed to Emacs event loop. */ + 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); + + UNBLOCK_INPUT; + + return Qnil; +} + +/* Mac_menu_show actually displays a menu using the panes and items in menu_items and returns the value selected from it; we assume input is blocked by the caller. */ @@ -1594,6 +1744,7 @@ mac_menu_show (f, x, y, for_click, keymaps, title, error) = (Lisp_Object *) alloca (menu_items_used * sizeof (Lisp_Object)); int submenu_depth = 0; int first_pane; + int specpdl_count = SPECPDL_INDEX (); *error = NULL; @@ -1705,12 +1856,12 @@ mac_menu_show (f, x, y, for_click, keymaps, title, error) #ifndef HAVE_MULTILINGUAL_MENU if (STRINGP (item_name) && STRING_MULTIBYTE (item_name)) { - item_name = ENCODE_SYSTEM (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)) { - descrip = ENCODE_SYSTEM (descrip); + descrip = ENCODE_MENU_STRING (descrip); AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY) = descrip; } #endif /* not HAVE_MULTILINGUAL_MENU */ @@ -1764,10 +1915,10 @@ mac_menu_show (f, x, y, for_click, keymaps, title, error) #ifndef HAVE_MULTILINGUAL_MENU if (STRING_MULTIBYTE (title)) - title = ENCODE_SYSTEM (title); + title = ENCODE_MENU_STRING (title); #endif wv_title->name = (char *) SDATA (title); - wv_title->enabled = TRUE; + wv_title->enabled = FALSE; wv_title->title = TRUE; wv_title->button_type = BUTTON_TYPE_NONE; wv_title->help = Qnil; @@ -1780,6 +1931,10 @@ mac_menu_show (f, x, y, for_click, keymaps, title, error) submenu_id = MIN_POPUP_SUBMENU_ID; fill_submenu (menu, first_wv->contents); + /* Free the widget_value objects we used to specify the + contents. */ + free_menubar_widget_value_tree (first_wv); + /* Adjust coordinates to be root-window-relative. */ pos.h = x; pos.v = y; @@ -1794,43 +1949,29 @@ mac_menu_show (f, x, y, for_click, keymaps, title, error) InsertMenu (menu, -1); + record_unwind_protect (pop_down_menu, + Fcons (make_save_value (f, 0), + make_save_value (&menu, 0))); + + /* Add event handler so we can detect C-g. */ + install_menu_quit_handler (menu); + /* Display the menu. */ menu_item_choice = PopUpMenuSelect (menu, pos.v, pos.h, 0); menu_item_selection = LoWord (menu_item_choice); - /* Get the refcon to find the correct item*/ + /* Get the refcon to find the correct item */ if (menu_item_selection) { - menu = GetMenuHandle (HiWord (menu_item_choice)); - if (menu) { - GetMenuItemRefCon (menu, menu_item_selection, &refcon); + MenuHandle sel_menu = GetMenuHandle (HiWord (menu_item_choice)); + if (sel_menu) { + GetMenuItemRefCon (sel_menu, menu_item_selection, &refcon); } } - -#if 0 - /* Clean up extraneous mouse events which might have been generated - during the call. */ - discard_mouse_events (); -#endif - - /* Free the widget_value objects we used to specify the - contents. */ - free_menubar_widget_value_tree (first_wv); - - /* delete all menus */ - { - int i = MIN_POPUP_SUBMENU_ID; - MenuHandle submenu = GetMenuHandle (i); - while (menu != NULL) - { - DeleteMenu (i); - DisposeMenu (menu); - menu = GetMenuHandle (++i); - } - } - - DeleteMenu (POPUP_SUBMENU_ID); - DisposeMenu (menu); + else if (! for_click) + /* Make "Cancel" equivalent to C-g unless this menu was popped up by + a mouse press. */ + Fsignal (Qquit, Qnil); /* Find the selected item, and its pane, to return the proper value. */ @@ -1886,6 +2027,11 @@ mac_menu_show (f, x, y, for_click, keymaps, title, error) } } } + else if (!for_click) + /* Make "Cancel" equivalent to C-g. */ + Fsignal (Qquit, Qnil); + + unbind_to (specpdl_count, Qnil); return Qnil; } @@ -2004,10 +2150,10 @@ static char * button_names [] = { "button6", "button7", "button8", "button9", "button10" }; static Lisp_Object -mac_dialog_show (f, keymaps, title, error) +mac_dialog_show (f, keymaps, title, header, error) FRAME_PTR f; int keymaps; - Lisp_Object title; + Lisp_Object title, header; char **error; { int i, nb_buttons=0; @@ -2110,11 +2256,17 @@ mac_dialog_show (f, keymaps, title, error) wv->name = dialog_name; wv->help = Qnil; + /* Frame title: 'Q' = Question, 'I' = Information. + Can also have 'E' = Error if, one day, we want + a popup for errors. */ + if (NILP(header)) + dialog_name[0] = 'Q'; + else + dialog_name[0] = 'I'; + /* Dialog boxes use a really stupid name encoding which specifies how many buttons to use - and how many buttons are on the right. - The Q means something also. */ - dialog_name[0] = 'Q'; + and how many buttons are on the right. */ dialog_name[1] = '0' + nb_buttons; dialog_name[2] = 'B'; dialog_name[3] = 'R'; @@ -2197,7 +2349,7 @@ add_menu_item (MenuHandle menu, widget_value *wv, int submenu, int force_disable) { Str255 item_name; - int pos, i; + int pos; if (name_is_separator (wv->name)) AppendMenu (menu, "\p-"); @@ -2219,8 +2371,17 @@ add_menu_item (MenuHandle menu, widget_value *wv, int submenu, strncat (item_name, wv->key, 255); } 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 c2pstr (item_name); SetMenuItemText (menu, pos, item_name); +#endif if (wv->enabled && !force_disable) #if TARGET_API_MAC_CARBON @@ -2243,9 +2404,9 @@ add_menu_item (MenuHandle menu, widget_value *wv, int submenu, else SetItemMark (menu, pos, noMark); } - } - SetMenuItemRefCon (menu, pos, (UInt32) wv->call_data); + SetMenuItemRefCon (menu, pos, (UInt32) wv->call_data); + } if (submenu != NULL) SetMenuItemHierarchicalID (menu, pos, submenu);