X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/16ceacc222529b8cdfd6fa7b2031940a926717f0..364c38d3af67e2b56a90a6547ec9f773967bf1df:/src/macmenu.c diff --git a/src/macmenu.c b/src/macmenu.c index 88012b01f7..ee83a5f609 100644 --- a/src/macmenu.c +++ b/src/macmenu.c @@ -21,7 +21,6 @@ Boston, MA 02111-1307, 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. */ @@ -603,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 @@ -648,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)) @@ -807,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 */ @@ -824,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. @@ -839,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; @@ -928,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 (); @@ -1322,7 +1333,7 @@ update_submenu_strings (first_wv) for (wv = first_wv; wv; wv = wv->next) { - if (wv->lname && ! NILP (wv->lname)) + if (STRINGP (wv->lname)) { wv->name = SDATA (wv->lname); @@ -1336,7 +1347,7 @@ update_submenu_strings (first_wv) } } - if (wv->lkey && ! NILP (wv->lkey)) + if (STRINGP (wv->lkey)) wv->key = SDATA (wv->lkey); if (wv->contents) @@ -1345,6 +1356,68 @@ update_submenu_strings (first_wv) } +/* 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. */ @@ -1402,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); @@ -1564,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; @@ -1595,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. */ @@ -1633,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; @@ -1806,7 +1918,7 @@ mac_menu_show (f, x, y, for_click, keymaps, title, error) 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; @@ -1819,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; @@ -1833,11 +1949,18 @@ 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) { MenuHandle sel_menu = GetMenuHandle (HiWord (menu_item_choice)); @@ -1845,35 +1968,10 @@ mac_menu_show (f, x, y, for_click, keymaps, title, error) 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 - - /* Must reset this manually because the button release event is not - passed to Emacs event loop. */ - FRAME_MAC_DISPLAY_INFO (f)->grabbed = 0; - - /* 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 (submenu != NULL) - { - DeleteMenu (i); - DisposeMenu (submenu); - submenu = 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. */ @@ -1929,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; } @@ -2047,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; @@ -2153,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'; @@ -2240,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-"); @@ -2264,8 +2373,7 @@ add_menu_item (MenuHandle menu, widget_value *wv, int submenu, item_name[255] = 0; #if TARGET_API_MAC_CARBON { - CFStringRef string = - CFStringCreateWithCString (NULL, item_name, kCFStringEncodingUTF8); + CFStringRef string = cfstring_create_with_utf8_cstring (item_name); SetMenuItemTextWithCFString (menu, pos, string); CFRelease (string); @@ -2296,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);