]> code.delx.au - gnu-emacs/blobdiff - src/macmenu.c
Update FSF's address.
[gnu-emacs] / src / macmenu.c
index 88012b01f73861bcefc61dd166b8ea92244aa499..ee83a5f609f8798d78b77cbf115f2126522c1b5c 100644 (file)
@@ -21,7 +21,6 @@ Boston, MA 02111-1307, USA.  */
 /* Contributed by Andrew Choi (akochoi@mac.com).  */
 
 #include <config.h>
-#include <signal.h>
 
 #include <stdio.h>
 #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 <MacTypes.h>
 #include <Menus.h>
 #include <QuickDraw.h>
@@ -47,7 +46,7 @@ Boston, MA 02111-1307, USA.  */
 #if defined (__MRC__) || (__MSL__ >= 0x6000)
 #include <ControlDefinitions.h>
 #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)
     }
 }
 \f
+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)
 }
 
 \f
+/* 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)
 }
 
 \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);