]> code.delx.au - gnu-emacs/blobdiff - src/nsmenu.m
Allow sending mail in "emacs -Q" by not calling customize-save-variable
[gnu-emacs] / src / nsmenu.m
index 825568f6557759e834481b33d6a74e7eb69b321e..2a2f952e751b3ca2bc989b41f167365784645f7c 100644 (file)
@@ -1,5 +1,5 @@
 /* NeXT/Open/GNUstep and MacOSX Cocoa menu and toolbar module.
-   Copyright (C) 2007, 2008 Free Software Foundation, Inc.
+   Copyright (C) 2007-2011 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -21,7 +21,11 @@ By Adrian Robert, based on code from original nsmenu.m (Carl Edman,
 Christian Limpach, Scott Bender, Christophe de Dinechin) and code in the
 Carbon version by Yamamoto Mitsuharu. */
 
-#include "config.h"
+/* This should be the first include, as it may set up #defines affecting
+   interpretation of even the system includes. */
+#include <config.h>
+#include <setjmp.h>
+
 #include "lisp.h"
 #include "window.h"
 #include "buffer.h"
@@ -32,10 +36,14 @@ Carbon version by Yamamoto Mitsuharu. */
 #include "nsterm.h"
 #include "termhooks.h"
 #include "keyboard.h"
+#include "menu.h"
+
+#define NSMENUPROFILE 0
 
-/* for profiling */
+#if NSMENUPROFILE
 #include <sys/timeb.h>
 #include <sys/types.h>
+#endif
 
 #define MenuStagger 10.0
 
@@ -56,17 +64,15 @@ int menu_trace_num = 0;
 extern Lisp_Object Qundefined, Qmenu_enable, Qmenu_bar_update_hook;
 extern Lisp_Object QCtoggle, QCradio;
 
-extern Lisp_Object Vmenu_updating_frame;
-
 Lisp_Object Qdebug_on_next_call;
-extern Lisp_Object Voverriding_local_map, Voverriding_local_map_menu_flag,
-                  Qoverriding_local_map, Qoverriding_terminal_local_map;
+extern Lisp_Object Qoverriding_local_map, Qoverriding_terminal_local_map;
 
 extern long context_menu_value;
 EmacsMenu *mainMenu, *svcsMenu, *dockMenu;
 
 /* Nonzero means a menu is currently active.  */
 static int popup_activated_flag;
+static NSModalSession popupSession;
 
 /* NOTE: toolbar implementation is at end,
   following complete menu implementation. */
@@ -79,7 +85,7 @@ static int popup_activated_flag;
    ========================================================================== */
 
 
-/*23: FIXME: not currently used, but should normalize with other terms. */
+/* FIXME: not currently used, but should normalize with other terms. */
 void
 x_activate_menubar (struct frame *f)
 {
@@ -98,7 +104,7 @@ free_frame_menubar (struct frame *f)
 
 
 int
-popup_activated ()
+popup_activated (void)
 {
   return popup_activated_flag;
 }
@@ -111,7 +117,6 @@ popup_activated ()
     2) deep_p = 1, submenu = nil: Recompute all submenus.
     3) deep_p = 1, submenu = non-nil: Update contents of a single submenu.
    -------------------------------------------------------------------------- */
-/*#define NSMENUPROFILE 1 */
 void
 ns_update_menubar (struct frame *f, int deep_p, EmacsMenu *submenu)
 {
@@ -126,7 +131,7 @@ ns_update_menubar (struct frame *f, int deep_p, EmacsMenu *submenu)
   widget_value *wv, *first_wv, *prev_wv = 0;
   int i;
 
-#ifdef NSMENUPROFILE
+#if NSMENUPROFILE
   struct timeb tb;
   long t;
 #endif
@@ -150,7 +155,7 @@ ns_update_menubar (struct frame *f, int deep_p, EmacsMenu *submenu)
 
   if (menu == nil)
     {
-      menu = [[EmacsMenu alloc] initWithTitle: @"Emacs"];
+      menu = [[EmacsMenu alloc] initWithTitle: ns_app_name];
       needsSet = YES;
     }
   else
@@ -160,21 +165,11 @@ ns_update_menubar (struct frame *f, int deep_p, EmacsMenu *submenu)
         [attMenu close];
     }
 
-#ifdef NSMENUPROFILE
+#if NSMENUPROFILE
   ftime (&tb);
   t = -(1000*tb.time+tb.millitm);
 #endif
 
-  /* widget_value is a straightforward object translation of emacs's
-     Byzantine lisp menu structures */
-  wv = xmalloc_widget_value ();
-  wv->name = "Emacs";
-  wv->value = 0;
-  wv->enabled = 1;
-  wv->button_type = BUTTON_TYPE_NONE;
-  wv->help = Qnil;
-  first_wv = wv;
-
 #ifdef NS_IMPL_GNUSTEP
   deep_p = 1; /* until GNUstep NSMenu implements the Panther delegation model */
 #endif
@@ -223,27 +218,27 @@ ns_update_menubar (struct frame *f, int deep_p, EmacsMenu *submenu)
 
       /* Save the frame's previous menu bar contents data */
       if (previous_menu_items_used)
-       bcopy (XVECTOR (f->menu_bar_vector)->contents, previous_items,
-              previous_menu_items_used * sizeof (Lisp_Object));
+       memcpy (previous_items, &AREF (f->menu_bar_vector, 0),
+               previous_menu_items_used * sizeof (Lisp_Object));
 
       /* parse stage 1: extract from lisp */
       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_start = (int *) alloca (ASIZE (items) * sizeof (int *));
+      submenu_end = (int *) alloca (ASIZE (items) * sizeof (int *));
+      submenu_n_panes = (int *) alloca (ASIZE (items) * sizeof (int));
       submenu_top_level_items
-       = (int *) alloca (XVECTOR (items)->size * sizeof (int *));
+       = (int *) alloca (ASIZE (items) * sizeof (int *));
       init_menu_items ();
-      for (i = 0; i < XVECTOR (items)->size; i += 4)
+      for (i = 0; i < ASIZE (items); i += 4)
        {
          Lisp_Object key, string, maps;
 
-         key = XVECTOR (items)->contents[i];
-         string = XVECTOR (items)->contents[i + 1];
-         maps = XVECTOR (items)->contents[i + 2];
+         key = AREF (items, i);
+         string = AREF (items, i + 1);
+         maps = AREF (items, i + 2);
          if (NILP (string))
            break;
 
@@ -316,17 +311,17 @@ ns_update_menubar (struct frame *f, int deep_p, EmacsMenu *submenu)
             /* FIXME: this ALWAYS fails on Buffers menu items.. something
                  about their strings causes them to change every time, so we
                  double-check failures */
-            if (!EQ (previous_items[i], XVECTOR (menu_items)->contents[i]))
+            if (!EQ (previous_items[i], AREF (menu_items, i)))
               if (!(STRINGP (previous_items[i])
-                    && STRINGP (XVECTOR (menu_items)->contents[i])
+                    && STRINGP (AREF (menu_items, i))
                     && !strcmp (SDATA (previous_items[i]),
-                               SDATA (XVECTOR (menu_items)->contents[i]))))
+                               SDATA (AREF (menu_items, i)))))
                   break;
           if (i == previous_menu_items_used)
             {
               /* No change.. */
 
-#ifdef NSMENUPROFILE
+#if NSMENUPROFILE
               ftime (&tb);
               t += 1000*tb.time+tb.millitm;
               fprintf (stderr, "NO CHANGE!  CUTTING OUT after %ld msec.\n", t);
@@ -351,16 +346,16 @@ ns_update_menubar (struct frame *f, int deep_p, EmacsMenu *submenu)
       /* Parse stage 2a: now GC cannot happen during the lifetime of the
          widget_value, so it's safe to store data from a Lisp_String */
       wv = first_wv->contents;
-      for (i = 0; i < XVECTOR (items)->size; i += 4)
+      for (i = 0; i < ASIZE (items); i += 4)
        {
          Lisp_Object string;
-         string = XVECTOR (items)->contents[i + 1];
+         string = AREF (items, i + 1);
          if (NILP (string))
            break;
 /*           if (submenu && strcmp (submenuTitle, SDATA (string)))
                continue; */
 
-         wv->name = (char *) SDATA (string);
+         wv->name = SSDATA (string);
           update_submenu_strings (wv->contents);
          wv = wv->next;
        }
@@ -393,6 +388,14 @@ ns_update_menubar (struct frame *f, int deep_p, EmacsMenu *submenu)
       int n;
       Lisp_Object string;
 
+      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;
+
       /* Make widget-value tree w/ just the top level menu bar strings */
       items = FRAME_MENU_BAR_ITEMS (f);
       if (NILP (items))
@@ -404,7 +407,7 @@ ns_update_menubar (struct frame *f, int deep_p, EmacsMenu *submenu)
 
 
       /* check if no change.. this mechanism is a bit rough, but ready */
-      n = XVECTOR (items)->size / 4;
+      n = ASIZE (items) / 4;
       if (f == last_f && n_previous_strings == n)
         {
           for (i = 0; i<n; i++)
@@ -431,9 +434,9 @@ ns_update_menubar (struct frame *f, int deep_p, EmacsMenu *submenu)
         }
 
       [menu clear];
-      for (i = 0; i < XVECTOR (items)->size; i += 4)
+      for (i = 0; i < ASIZE (items); i += 4)
        {
-         string = XVECTOR (items)->contents[i + 1];
+         string = AREF (items, i + 1);
          if (NILP (string))
            break;
 
@@ -441,7 +444,7 @@ ns_update_menubar (struct frame *f, int deep_p, EmacsMenu *submenu)
             strncpy (previous_strings[i/4], SDATA (string), 10);
 
          wv = xmalloc_widget_value ();
-         wv->name = (char *) SDATA (string);
+         wv->name = SSDATA (string);
          wv->value = 0;
          wv->enabled = 1;
          wv->button_type = BUTTON_TYPE_NONE;
@@ -477,7 +480,7 @@ ns_update_menubar (struct frame *f, int deep_p, EmacsMenu *submenu)
   free_menubar_widget_value_tree (first_wv);
 
 
-#ifdef NSMENUPROFILE
+#if NSMENUPROFILE
   ftime (&tb);
   t += 1000*tb.time+tb.millitm;
   fprintf (stderr, "Menu update took %ld msec.\n", t);
@@ -503,22 +506,6 @@ set_frame_menubar (struct frame *f, int first_time, int deep_p)
 }
 
 
-/* Utility (from macmenu.c): is this item a separator? */
-static int
-name_is_separator (name)
-     const char *name;
-{
-  const char *start = name;
-
-  /* Check if name string consists of only dashes ('-').  */
-  while (*name == '-') name++;
-  /* Separators can also be of the form "--:TripleSuperMegaEtched"
-     or "--deep-shadow".  We don't implement them yet, se we just treat
-     them like normal separators.  */
-  return (*name == '\0' || start + 2 == name);
-}
-
-
 /* ==========================================================================
 
     Menu: class implementation
@@ -562,13 +549,19 @@ name_is_separator (name)
    to set_frame_menubar */
 - (void)menuNeedsUpdate: (NSMenu *)menu
 {
-  NSEvent *event = [[FRAME_NS_VIEW (frame) window] currentEvent];
+  NSEvent *event;
+  if (!FRAME_LIVE_P (frame))
+    return;
+  event = [[FRAME_NS_VIEW (frame) window] currentEvent];
   /* HACK: Cocoa/Carbon will request update on every keystroke
      via IsMenuKeyEvent -> CheckMenusForKeyEvent.  These are not needed
      since key equivalents are handled through emacs.
      On Leopard, even keystroke events generate SystemDefined events, but
      their subtype is 8. */
-  if ([event type] != NSSystemDefined || [event subtype] == 8)
+  if ([event type] != NSSystemDefined || [event subtype] == 8
+      /* Also, don't try this if from an event picked up asynchronously,
+         as lots of lisp evaluation happens in ns_update_menubar. */
+      || handling_signal != 0)
     return;
 /*fprintf (stderr, "Updating menu '%s'\n", [[self title] UTF8String]); NSLog (@"%@\n", event); */
   ns_update_menubar (frame, 1, self);
@@ -584,22 +577,29 @@ name_is_separator (name)
 }
 
 
-/* parse a wdiget_value's key rep (examples: 's-p', 's-S', '(C-x C-s)', '<f13>')
-   into an accelerator string */
--(NSString *)parseKeyEquiv: (char *)key
+/* Parse a widget_value's key rep (examples: 's-p', 's-S', '(C-x C-s)', '<f13>')
+   into an accelerator string.  We are only able to display a single character
+   for an accelerator, together with an optional modifier combination.  (Under
+   Carbon more control was possible, but in Cocoa multi-char strings passed to
+   NSMenuItem get ignored.  For now we try to display a super-single letter
+   combo, and return the others as strings to be appended to the item title.
+   (This is signaled by setting keyEquivModMask to 0 for now.) */
+-(NSString *)parseKeyEquiv: (const char *)key
 {
-  char *tpos = key;
-  keyEquivModMask = 0;
-  /* currently we just parse 'super' combinations;
-     later we'll set keyEquivModMask */
+  const char *tpos = key;
+  keyEquivModMask = NSCommandKeyMask;
+
   if (!key || !strlen (key))
     return @"";
-  
+
   while (*tpos == ' ' || *tpos == '(')
     tpos++;
-  if (*tpos != 's'/* || tpos[3] != ')'*/)
-    return @"";
-  return [NSString stringWithFormat: @"%c", tpos[2]];
+  if ((*tpos == 's') && (*(tpos+1) == '-'))
+    {
+      return [NSString stringWithFormat: @"%c", tpos[2]];
+    }
+  keyEquivModMask = 0; /* signal */
+  return [NSString stringWithUTF8String: tpos];
 }
 
 
@@ -608,7 +608,7 @@ name_is_separator (name)
   NSMenuItem *item;
   widget_value *wv = (widget_value *)wvptr;
 
-  if (name_is_separator (wv->name))
+  if (menu_separator_name_p (wv->name))
     {
       item = [NSMenuItem separatorItem];
       [self addItem: item];
@@ -621,12 +621,16 @@ name_is_separator (name)
         title = @"< ? >";  /* (get out in the open so we know about it) */
 
       keyEq = [self parseKeyEquiv: wv->key];
+#ifdef NS_IMPL_COCOA
+      /* OS X just ignores modifier strings longer than one character */
+      if (keyEquivModMask == 0)
+        title = [title stringByAppendingFormat: @" (%@)", keyEq];
+#endif
 
       item = [self addItemWithTitle: (NSString *)title
                              action: @selector (menuDown:)
                       keyEquivalent: keyEq];
-      if (keyEquivModMask)
-        [item setKeyEquivalentModifierMask: keyEquivModMask];
+      [item setKeyEquivalentModifierMask: keyEquivModMask];
 
       [item setEnabled: wv->enabled];
 
@@ -637,7 +641,7 @@ name_is_separator (name)
       else
         [item setState: NSOffState];
 
-      [item setTag: (int)wv->call_data];
+      [item setTag: (NSInteger)wv->call_data];
     }
 
   return item;
@@ -645,15 +649,17 @@ name_is_separator (name)
 
 
 /* convenience */
--(void) clear
+-(void)clear
 {
   int n;
-  
+
   for (n = [self numberOfItems]-1; n >= 0; n--)
     {
       NSMenuItem *item = [self itemAtIndex: n];
       NSString *title = [item title];
-      if (([title length] == 0 || [@"Apple" isEqualToString: title])
+      if (([title length] == 0  /* OSX 10.5 */
+          || [ns_app_name isEqualToString: title]  /* from 10.6 on */
+          || [@"Apple" isEqualToString: title]) /* older */
           && ![item isSeparatorItem])
         continue;
       [self removeItemAtIndex: n];
@@ -676,7 +682,7 @@ name_is_separator (name)
 
       if (wv->contents)
         {
-          EmacsMenu *submenu = [[EmacsMenu alloc] initWithTitle: @"Submenu"];
+          EmacsMenu *submenu = [[EmacsMenu alloc] initWithTitle: [item title]];
 
           [self setSubmenu: submenu forItem: item];
           [submenu fillWithWidgetValue: wv->contents];
@@ -697,7 +703,7 @@ name_is_separator (name)
 
 
 /* adds an empty submenu and returns it */
-- (EmacsMenu *)addSubmenuWithTitle: (char *)title forFrame: (struct frame *)f
+- (EmacsMenu *)addSubmenuWithTitle: (const char *)title forFrame: (struct frame *)f
 {
   NSString *titleStr = [NSString stringWithUTF8String: title];
   NSMenuItem *item = [self addItemWithTitle: titleStr
@@ -714,19 +720,21 @@ name_is_separator (name)
                  keymaps: (int)keymaps
 {
   EmacsView *view = FRAME_NS_VIEW (f);
+  NSEvent *e, *event;
+  long retVal;
+
 /*   p = [view convertPoint:p fromView: nil]; */
   p.y = NSHeight ([view frame]) - p.y;
-  NSEvent *e = [[view window] currentEvent];
-  NSEvent *event = [NSEvent mouseEventWithType: NSRightMouseDown
-                                      location: p
-                                 modifierFlags: 0
-                                     timestamp: [e timestamp]
-                                  windowNumber: [[view window] windowNumber]
-                                       context: [e context]
-                                   eventNumber: 0/*[e eventNumber] */
-                                    clickCount: 1
-                                      pressure: 0];
-  long retVal;
+  e = [[view window] currentEvent];
+   event = [NSEvent mouseEventWithType: NSRightMouseDown
+                              location: p
+                         modifierFlags: 0
+                             timestamp: [e timestamp]
+                          windowNumber: [[view window] windowNumber]
+                               context: [e context]
+                           eventNumber: 0/*[e eventNumber] */
+                            clickCount: 1
+                              pressure: 0];
 
   context_menu_value = -1;
   [NSMenu popUpContextMenu: self withEvent: event forView: view];
@@ -747,188 +755,17 @@ name_is_separator (name)
 
    ========================================================================== */
 
-static Lisp_Object
-cleanup_popup_menu (Lisp_Object arg)
-{
-  discard_menu_items ();
-  return Qnil;
-}
-
-
-static Lisp_Object
-ns_popup_menu (Lisp_Object position, Lisp_Object menu)
+Lisp_Object
+ns_menu_show (FRAME_PTR f, int x, int y, int for_click, int keymaps,
+             Lisp_Object title, const char **error)
 {
   EmacsMenu *pmenu;
-  struct frame *f = NULL;
   NSPoint p;
-  Lisp_Object window, x, y, tem, keymap, title;
-  struct gcpro gcpro1;
-  int specpdl_count = SPECPDL_INDEX (), specpdl_count2;
-  char *error_name = NULL;
-  int keymaps = 0;
+  Lisp_Object window, tem, keymap;
+  int specpdl_count = SPECPDL_INDEX ();
   widget_value *wv, *first_wv = 0;
 
-  NSTRACE (ns_popup_menu);
-
-  if (!NILP (position))
-    {
-      check_ns ();
-  
-      if (EQ (position, Qt)
-          || (CONSP (position) && (EQ (XCAR (position), Qmenu_bar)
-                                   || EQ (XCAR (position), Qtool_bar))))
-        {
-          /* Use the mouse's current position.  */
-          struct frame *new_f = SELECTED_FRAME ();
-
-          if (FRAME_TERMINAL (new_f)->mouse_position_hook)
-            (*FRAME_TERMINAL (new_f)->mouse_position_hook)
-              (&new_f, 0, 0, 0, &x, &y, 0);
-          if (new_f != 0)
-            XSETFRAME (window, new_f);
-          else
-            {
-              window = selected_window;
-              x = make_number (0);
-              y = make_number (0);
-            }
-        }
-      else
-        {
-          CHECK_CONS (position);
-          tem = Fcar (position);
-          if (XTYPE (tem) == Lisp_Cons)
-            {
-              window = Fcar (Fcdr (position));
-              x = Fcar (tem);
-              y = Fcar (Fcdr (tem));
-            }
-          else
-            {
-              tem = Fcar (Fcdr (position));
-              window = Fcar (tem);
-              tem = Fcar (Fcdr (Fcdr (tem)));
-              x = Fcar (tem);
-              y = Fcdr (tem);
-            }
-        }
-  
-      CHECK_NUMBER (x);
-      CHECK_NUMBER (y);
-
-      if (FRAMEP (window))
-        {
-          f = XFRAME (window);
-      
-          p.x = 0;
-          p.y = 0;
-        }
-      else
-        {
-          struct window *win = XWINDOW (window);
-          CHECK_LIVE_WINDOW (window);
-          f = XFRAME (WINDOW_FRAME (win));
-          p.x = FRAME_COLUMN_WIDTH (f) * WINDOW_LEFT_EDGE_COL (win);
-          p.y = FRAME_LINE_HEIGHT (f) * WINDOW_TOP_EDGE_LINE (win);
-        }
-
-      p.x += XINT (x); p.y += XINT (y);
-
-      XSETFRAME (Vmenu_updating_frame, f);
-    }
-  else
-    {      /* no position given */
-      /* FIXME: if called during dump, we need to stop precomputation of
-         key equivalents (see below) because the keydefs in ns-win.el have
-         not been loaded yet. */
-      if (noninteractive)
-        return Qnil;
-      Vmenu_updating_frame = Qnil;
-    }
-
-  /* now parse the lisp menus */
-  record_unwind_protect (unuse_menu_items, Qnil);
-  title = Qnil;
-  GCPRO1 (title);
-
-  /* Decode the menu items from what was specified.  */
-
-  keymap = get_keymap (menu, 0, 0);
-  if (CONSP (keymap))
-    {
-      /* We were given a keymap.  Extract menu info from the keymap.  */
-      Lisp_Object prompt;
-
-      /* Extract the detailed info to make one pane.  */
-      keymap_panes (&menu, 1, NILP (position));
-
-      /* Search for a string appearing directly as an element of the keymap.
-        That string is the title of the menu.  */
-      prompt = Fkeymap_prompt (keymap);
-      title = NILP (prompt) ? build_string ("Select") : prompt;
-
-      /* Make that be the pane title of the first pane.  */
-      if (!NILP (prompt) && menu_items_n_panes >= 0)
-       XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME] = prompt;
-
-      keymaps = 1;
-    }
-  else if (CONSP (menu) && KEYMAPP (XCAR (menu)))
-    {
-      /* We were given a list of keymaps.  */
-      int nmaps = XFASTINT (Flength (menu));
-      Lisp_Object *maps
-       = (Lisp_Object *) alloca (nmaps * sizeof (Lisp_Object));
-      int i;
-
-      title = Qnil;
-
-      /* The first keymap that has a prompt string
-        supplies the menu title.  */
-      for (tem = menu, i = 0; CONSP (tem); tem = XCDR (tem))
-       {
-         Lisp_Object prompt;
-
-         maps[i++] = keymap = get_keymap (XCAR (tem), 1, 0);
-
-         prompt = Fkeymap_prompt (keymap);
-         if (NILP (title) && !NILP (prompt))
-           title = prompt;
-       }
-
-      /* Extract the detailed info to make one pane.  */
-      keymap_panes (maps, nmaps, NILP (position));
-
-      /* Make the title be the pane title of the first pane.  */
-      if (!NILP (title) && menu_items_n_panes >= 0)
-       XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME] = title;
-
-      keymaps = 1;
-    }
-  else
-    {
-      /* We were given an old-fashioned menu.  */
-      title = Fcar (menu);
-      CHECK_STRING (title);
-
-      list_of_panes (Fcdr (menu));
-
-      keymaps = 0;
-    }
-
-  unbind_to (specpdl_count, Qnil);
-
-  /* If no position given, that was a signal to just precompute and cache
-     key equivalents, which was a side-effect of what we just did. */
-  if (NILP (position))
-    {
-      discard_menu_items ();
-      UNGCPRO;
-      return Qnil;
-    }
-
-  record_unwind_protect (cleanup_popup_menu, Qnil);
-  BLOCK_INPUT;
+  p.x = x; p.y = y;
 
   /* now parse stage 2 as in ns_update_menubar */
   wv = xmalloc_widget_value ();
@@ -939,8 +776,6 @@ ns_popup_menu (Lisp_Object position, Lisp_Object menu)
   wv->help = Qnil;
   first_wv = wv;
 
-  specpdl_count2 = SPECPDL_INDEX ();
-
 #if 0
   /* FIXME: a couple of one-line differences prevent reuse */
   wv = digest_single_submenu (0, menu_items_used, Qnil);
@@ -959,7 +794,7 @@ ns_popup_menu (Lisp_Object position, Lisp_Object menu)
   i = 0;
   while (i < menu_items_used)
     {
-      if (EQ (XVECTOR (menu_items)->contents[i], Qnil))
+      if (EQ (AREF (menu_items, i), Qnil))
        {
          submenu_stack[submenu_depth++] = save_wv;
          save_wv = prev_wv;
@@ -967,25 +802,25 @@ ns_popup_menu (Lisp_Object position, Lisp_Object menu)
          first_pane = 1;
          i++;
        }
-      else if (EQ (XVECTOR (menu_items)->contents[i], Qlambda))
+      else if (EQ (AREF (menu_items, i), Qlambda))
        {
          prev_wv = save_wv;
          save_wv = submenu_stack[--submenu_depth];
          first_pane = 0;
          i++;
        }
-      else if (EQ (XVECTOR (menu_items)->contents[i], Qt)
+      else if (EQ (AREF (menu_items, i), Qt)
               && submenu_depth != 0)
        i += MENU_ITEMS_PANE_LENGTH;
       /* Ignore a nil in the item list.
         It's meaningful only for dialog boxes.  */
-      else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
+      else if (EQ (AREF (menu_items, i), Qquote))
        i += 1;
-      else if (EQ (XVECTOR (menu_items)->contents[i], Qt))
+      else if (EQ (AREF (menu_items, i), Qt))
        {
          /* Create a new pane.  */
          Lisp_Object pane_name, prefix;
-         char *pane_string;
+         const char *pane_string;
 
          pane_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME);
          prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
@@ -998,7 +833,7 @@ ns_popup_menu (Lisp_Object position, Lisp_Object menu)
            }
 #endif
          pane_string = (NILP (pane_name)
-                        ? "" : (char *) SDATA (pane_name));
+                        ? "" : SSDATA (pane_name));
          /* If there is just one top-level pane, put all its items directly
             under the top-level menu.  */
          if (menu_items_n_panes == 1)
@@ -1063,15 +898,15 @@ ns_popup_menu (Lisp_Object position, Lisp_Object menu)
            prev_wv->next = wv;
          else
            save_wv->contents = wv;
-         wv->name = (char *) SDATA (item_name);
+         wv->name = SSDATA (item_name);
          if (!NILP (descrip))
-           wv->key = (char *) SDATA (descrip);
+           wv->key = SSDATA (descrip);
          wv->value = 0;
          /* If this item has a null value,
             make the call_data null so that it won't display a box
             when the mouse is on it.  */
          wv->call_data
-             = !NILP (def) ? (void *) &XVECTOR (menu_items)->contents[i] : 0;
+             = !NILP (def) ? (void *) &AREF (menu_items, i) : 0;
          wv->enabled = !NILP (enable);
 
          if (NILP (type))
@@ -1114,7 +949,7 @@ ns_popup_menu (Lisp_Object position, Lisp_Object menu)
        title = ENCODE_MENU_STRING (title);
 #endif
 
-      wv_title->name = (char *) SDATA (title);
+      wv_title->name = SSDATA (title);
       wv_title->enabled = NO;
       wv_title->button_type = BUTTON_TYPE_NONE;
       wv_title->help = Qnil;
@@ -1126,25 +961,17 @@ ns_popup_menu (Lisp_Object position, Lisp_Object menu)
                                [NSString stringWithUTF8String: SDATA (title)]];
   [pmenu fillWithWidgetValue: first_wv->contents];
   free_menubar_widget_value_tree (first_wv);
-  unbind_to (specpdl_count2, Qnil);
+  unbind_to (specpdl_count, Qnil);
 
   popup_activated_flag = 1;
   tem = [pmenu runMenuAt: p forFrame: f keymaps: keymaps];
   popup_activated_flag = 0;
   [[FRAME_NS_VIEW (SELECTED_FRAME ()) window] makeKeyWindow];
 
-  UNBLOCK_INPUT;
-  discard_menu_items ();
-  unbind_to (specpdl_count, Qnil);
-  UNGCPRO;
-
-  if (error_name) error (error_name);
   return tem;
 }
 
 
-
-
 /* ==========================================================================
 
     Toolbar: externally-called functions
@@ -1157,7 +984,10 @@ free_frame_tool_bar (FRAME_PTR f)
     Under NS we just hide the toolbar until it might be needed again.
    -------------------------------------------------------------------------- */
 {
+  BLOCK_INPUT;
   [[FRAME_NS_VIEW (f) toolbar] setVisible: NO];
+  FRAME_TOOLBAR_HEIGHT (f) = 0;
+  UNBLOCK_INPUT;
 }
 
 void
@@ -1167,8 +997,11 @@ update_frame_tool_bar (FRAME_PTR f)
    -------------------------------------------------------------------------- */
 {
   int i;
-  EmacsToolbar *toolbar = [FRAME_NS_VIEW (f) toolbar];
+  EmacsView *view = FRAME_NS_VIEW (f);
+  NSWindow *window = [view window];
+  EmacsToolbar *toolbar = [view toolbar];
 
+  BLOCK_INPUT;
   [toolbar clearActive];
 
   /* update EmacsToolbar as in GtkUtils, build items list */
@@ -1184,7 +1017,7 @@ update_frame_tool_bar (FRAME_PTR f)
       struct image *img;
       Lisp_Object image;
       Lisp_Object helpObj;
-      char *helpText;
+      const char *helpText;
 
       /* If image is a vector, choose the image according to the
         button state.  */
@@ -1200,10 +1033,15 @@ update_frame_tool_bar (FRAME_PTR f)
         {
           idx = -1;
         }
+      helpObj = TOOLPROP (TOOL_BAR_ITEM_HELP);
+      if (NILP (helpObj))
+        helpObj = TOOLPROP (TOOL_BAR_ITEM_CAPTION);
+      helpText = NILP (helpObj) ? "" : SSDATA (helpObj);
+
       /* Ignore invalid image specifications.  */
       if (!valid_image_p (image))
         {
-          NSLog (@"Invalid image for toolbar item");
+          /* Don't log anything, GNUS makes invalid images all the time.  */
           continue;
         }
 
@@ -1217,11 +1055,6 @@ update_frame_tool_bar (FRAME_PTR f)
           continue;
         }
 
-      helpObj = TOOLPROP (TOOL_BAR_ITEM_HELP);
-      if (NILP (helpObj))
-        helpObj = TOOLPROP (TOOL_BAR_ITEM_CAPTION);
-      helpText = NILP (helpObj) ? "" : (char *)SDATA (helpObj);
-
       [toolbar addDisplayItemWithImage: img->pixmap idx: i helpText: helpText
                                enabled: enabled_p];
 #undef TOOLPROP
@@ -1252,6 +1085,10 @@ update_frame_tool_bar (FRAME_PTR f)
       [newDict release];
     }
 
+  FRAME_TOOLBAR_HEIGHT (f) =
+    NSHeight ([window frameRectForContentRect: NSMakeRect (0, 0, 0, 0)])
+    - FRAME_NS_TITLEBAR_HEIGHT (f);
+  UNBLOCK_INPUT;
 }
 
 
@@ -1300,7 +1137,7 @@ update_frame_tool_bar (FRAME_PTR f)
 }
 
 - (void) addDisplayItemWithImage: (EmacsImage *)img idx: (int)idx
-                        helpText: (char *)help enabled: (BOOL)enabled
+                        helpText: (const char *)help enabled: (BOOL)enabled
 {
   /* 1) come up w/identifier */
   NSString *identifier
@@ -1313,7 +1150,7 @@ update_frame_tool_bar (FRAME_PTR f)
       item = [[[NSToolbarItem alloc] initWithItemIdentifier: identifier]
                autorelease];
       [item setImage: img];
-      [item setToolTip: [NSString stringWithCString: help]];
+      [item setToolTip: [NSString stringWithUTF8String: help]];
       [item setTarget: emacsView];
       [item setAction: @selector (toolbarClicked:)];
     }
@@ -1420,7 +1257,14 @@ update_frame_tool_bar (FRAME_PTR f)
 {
   NSString *str = [NSString stringWithUTF8String: text];
   NSRect r = [textField frame];
-  r.size.width = [[[textField font] screenFont] widthOfString: str] + 8;
+  NSSize textSize = [str sizeWithAttributes:
+     [NSDictionary dictionaryWithObject: [[textField font] screenFont]
+                                forKey: NSFontAttributeName]];
+  NSSize padSize = [[[textField font] screenFont]
+                    boundingRectForFont].size;
+
+  r.size.width = textSize.width + padSize.width/2;
+  r.size.height = textSize.height + padSize.height/2;
   [textField setFrame: r];
   [textField setStringValue: str];
 }
@@ -1473,6 +1317,24 @@ update_frame_tool_bar (FRAME_PTR f)
 
    ========================================================================== */
 
+
+static Lisp_Object
+pop_down_menu (Lisp_Object arg)
+{
+  struct Lisp_Save_Value *p = XSAVE_VALUE (arg);
+  if (popup_activated_flag)
+    {
+      popup_activated_flag = 0;
+      BLOCK_INPUT;
+      [NSApp endModalSession: popupSession];
+      [((EmacsDialogPanel *) (p->pointer)) close];
+      [[FRAME_NS_VIEW (SELECTED_FRAME ()) window] makeKeyWindow];
+      UNBLOCK_INPUT;
+    }
+  return Qnil;
+}
+
+
 Lisp_Object
 ns_popup_dialog (Lisp_Object position, Lisp_Object contents, Lisp_Object header)
 {
@@ -1483,12 +1345,14 @@ ns_popup_dialog (Lisp_Object position, Lisp_Object contents, Lisp_Object header)
   BOOL isQ;
 
   NSTRACE (x-popup-dialog);
-  
+
   check_ns ();
 
   isQ = NILP (header);
 
-  if (EQ (position, Qt))
+  if (EQ (position, Qt)
+      || (CONSP (position) && (EQ (XCAR (position), Qmenu_bar)
+                               || EQ (XCAR (position), Qtool_bar))))
     {
       window = selected_window;
     }
@@ -1504,34 +1368,38 @@ ns_popup_dialog (Lisp_Object position, Lisp_Object contents, Lisp_Object header)
           window = Fcar (tem);      /* POSN_WINDOW (tem) */
         }
     }
-  else if (FRAMEP (position))
+  else if (WINDOWP (position) || FRAMEP (position))
     {
       window = position;
     }
   else
-    {
-      CHECK_LIVE_WINDOW (position);
-      window = position;
-    }
-  
+    window = Qnil;
+
   if (FRAMEP (window))
     f = XFRAME (window);
-  else
+  else if (WINDOWP (window))
     {
       CHECK_LIVE_WINDOW (window);
       f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
     }
+  else
+    CHECK_WINDOW (window);
+
   p.x = (int)f->left_pos + ((int)FRAME_COLUMN_WIDTH (f) * f->text_cols)/2;
   p.y = (int)f->top_pos + (FRAME_LINE_HEIGHT (f) * f->text_lines)/2;
+
+  BLOCK_INPUT;
   dialog = [[EmacsDialogPanel alloc] initFromContents: contents
                                            isQuestion: isQ];
-  popup_activated_flag = 1;
-  tem = [dialog runDialogAt: p];
-  popup_activated_flag = 0;
-
-  [dialog close];
+  {
+    int specpdl_count = SPECPDL_INDEX ();
+    record_unwind_protect (pop_down_menu, make_save_value (dialog, 0));
+    popup_activated_flag = 1;
+    tem = [dialog runDialogAt: p];
+    unbind_to (specpdl_count, Qnil);  /* calls pop_down_menu */
+  }
+  UNBLOCK_INPUT;
 
-  [[FRAME_NS_VIEW (SELECTED_FRAME ()) window] makeKeyWindow];
   return tem;
 }
 
@@ -1561,7 +1429,7 @@ ns_popup_dialog (Lisp_Object position, Lisp_Object contents, Lisp_Object header)
 #define TEXTHEIGHT     20.0
 #define MINCELLWIDTH   90.0
 
-- initWithContentRect: (NSRect)contentRect styleMask: (unsigned int)aStyle
+- initWithContentRect: (NSRect)contentRect styleMask: (NSUInteger)aStyle
               backing: (NSBackingStoreType)backingType defer: (BOOL)flag
 {
   NSSize spacing = {SPACER, SPACER};
@@ -1607,7 +1475,7 @@ ns_popup_dialog (Lisp_Object position, Lisp_Object contents, Lisp_Object header)
   area.size.height= TEXTHEIGHT;
   command = [[[NSTextField alloc] initWithFrame: area] autorelease];
   [[self contentView] addSubview: command];
-  [command setStringValue: @"Emacs"];
+  [command setStringValue: ns_app_name];
   [command setDrawsBackground: NO];
   [command setBezeled: NO];
   [command setSelectable: NO];
@@ -1639,10 +1507,10 @@ ns_popup_dialog (Lisp_Object position, Lisp_Object contents, Lisp_Object header)
   [cell setCellAttribute: NSCellIsInsetButton to: 8];
   [cell setBezelStyle: NSRoundedBezelStyle];
 
-  matrix = [[NSMatrix alloc] initWithFrame: contentRect 
-                                      mode: NSHighlightModeMatrix 
-                                 prototype: cell 
-                              numberOfRows: 0 
+  matrix = [[NSMatrix alloc] initWithFrame: contentRect
+                                      mode: NSHighlightModeMatrix
+                                 prototype: cell
+                              numberOfRows: 0
                            numberOfColumns: 1];
   [[self contentView] addSubview: matrix];
   [matrix release];
@@ -1674,11 +1542,11 @@ void process_dialog (id window, Lisp_Object list)
       item = XCAR (list);
       if (XTYPE (item) == Lisp_String)
         {
-          [window addString: XSTRING (item)->data row: row++];
+          [window addString: SDATA (item) row: row++];
         }
       else if (XTYPE (item) == Lisp_Cons)
         {
-          [window addButton: XSTRING (XCAR (item))->data
+          [window addButton: SDATA (XCAR (item))
                       value: XCDR (item) row: row++];
         }
       else if (NILP (item))
@@ -1693,7 +1561,7 @@ void process_dialog (id window, Lisp_Object list)
 - addButton: (char *)str value: (Lisp_Object)val row: (int)row
 {
   id cell;
-       
+
   if (row >= rows)
     {
       [matrix addRow];
@@ -1714,7 +1582,7 @@ void process_dialog (id window, Lisp_Object list)
 - addString: (char *)str row: (int)row
 {
   id cell;
-       
+
   if (row >= rows)
     {
       [matrix addRow];
@@ -1743,7 +1611,7 @@ void process_dialog (id window, Lisp_Object list)
   EMACS_INT seltag;
 
   sellist = [sender selectedCells];
-  if ([sellist count]<1) 
+  if ([sellist count]<1)
     return self;
 
   seltag = [[sellist objectAtIndex: 0] tag];
@@ -1768,7 +1636,7 @@ void process_dialog (id window, Lisp_Object list)
 
   if (XTYPE (head) == Lisp_String)
       [title setStringValue:
-                 [NSString stringWithUTF8String: XSTRING (head)->data]];
+                 [NSString stringWithUTF8String: SDATA (head)]];
   else if (isQ == YES)
       [title setStringValue: @"Question"];
   else
@@ -1836,25 +1704,21 @@ void process_dialog (id window, Lisp_Object list)
 
 - (Lisp_Object)runDialogAt: (NSPoint)p
 {
-  NSEvent *e;
-  NSModalSession session;
-  int ret;
-
-  [self center];  /*XXX p ignored? */
-  [self orderFront: NSApp];
+  NSInteger ret;
 
-  session = [NSApp beginModalSessionForWindow: self];
-  while ((ret = [NSApp runModalSession: session]) == NSRunContinuesResponse)
+  /* initiate a session that will be ended by pop_down_menu */
+  popupSession = [NSApp beginModalSessionForWindow: self];
+  while (popup_activated_flag
+         && (ret = [NSApp runModalSession: popupSession])
+              == NSRunContinuesResponse)
     {
-    (e = [NSApp nextEventMatchingMask: NSAnyEventMask
-                            untilDate: [NSDate distantFuture]
-                               inMode: NSEventTrackingRunLoopMode
-                              dequeue: NO]);
-/*fprintf (stderr, "ret = %d\te = %p\n", ret, e); */
+      /* Run this for timers.el, indep of atimers; might not return.
+         TODO: use return value to avoid calling every iteration. */
+      timer_check ();
+      [NSThread sleepUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.1]];
     }
-  [NSApp endModalSession: session];
 
-  {                            // FIXME: BIG UGLY HACK!!!
+  {                            /* FIXME: BIG UGLY HACK!!! */
       Lisp_Object tmp;
       *(EMACS_INT*)(&tmp) = ret;
       return tmp;
@@ -1864,7 +1728,6 @@ void process_dialog (id window, Lisp_Object list)
 @end
 
 
-
 /* ==========================================================================
 
     Lisp definitions
@@ -1873,53 +1736,13 @@ void process_dialog (id window, Lisp_Object list)
 
 DEFUN ("ns-reset-menu", Fns_reset_menu, Sns_reset_menu, 0, 0, 0,
        doc: /* Cause the NS menu to be re-calculated.  */)
-     ()
+     (void)
 {
   set_frame_menubar (SELECTED_FRAME (), 1, 0);
   return Qnil;
 }
 
 
-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 event
-or a list ((XOFFSET YOFFSET) WINDOW)
-where XOFFSET and YOFFSET are positions in pixels from the top left
-corner of WINDOW.  (WINDOW may be a window or a frame object.)
-This controls the position of the top left of the menu as a whole.
-If POSITION is t, it means to use the current mouse position.
-
-MENU is a specifier for a menu.  For the simplest case, MENU is a keymap.
-The menu items come from key bindings that have a menu string as well as
-a definition; actually, the \"definition\" in such a key binding looks like
-\(STRING . REAL-DEFINITION).  To give the menu a title, put a string into
-the keymap as a top-level element.
-
-If REAL-DEFINITION is nil, that puts a nonselectable string in the menu.
-Otherwise, REAL-DEFINITION should be a valid key binding definition.
-
-You can also use a list of keymaps as MENU.
-  Then each keymap makes a separate pane.
-
-When MENU is a keymap or a list of keymaps, the return value is the
-list of events corresponding to the user's choice. Note that
-`x-popup-menu' does not actually execute the command bound to that
-sequence of events.
-
-Alternatively, you can specify a menu of multiple panes
-  with a list of the form (TITLE PANE1 PANE2...),
-where each pane is a list of form (TITLE ITEM1 ITEM2...).
-Each ITEM is normally a cons cell (STRING . VALUE);
-but a string can appear as an item--that makes a nonselectable line
-in the menu.
-With this form of menu, the return value is VALUE from the chosen item.  */)
-     (position, menu)
-     Lisp_Object position, menu;
-{
-  return ns_popup_menu (position, menu);
-}
-
-
 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.
@@ -1943,15 +1766,14 @@ otherwise it is "Question".
 If the user gets rid of the dialog box without making a valid choice,
 for instance using the window manager, then this produces a quit and
 `x-popup-dialog' does not return.  */)
-     (position, contents, header)
-     Lisp_Object position, contents, header;
+     (Lisp_Object position, Lisp_Object contents, Lisp_Object header)
 {
   return ns_popup_dialog (position, contents, header);
 }
 
 DEFUN ("menu-or-popup-active-p", Fmenu_or_popup_active_p, Smenu_or_popup_active_p, 0, 0, 0,
        doc: /* Return t if a menu or popup dialog is active.  */)
-     ()
+     (void)
 {
   return popup_activated () ? Qt : Qnil;
 }
@@ -1963,17 +1785,12 @@ DEFUN ("menu-or-popup-active-p", Fmenu_or_popup_active_p, Smenu_or_popup_active_
    ========================================================================== */
 
 void
-syms_of_nsmenu ()
+syms_of_nsmenu (void)
 {
-  defsubr (&Sx_popup_menu);
   defsubr (&Sx_popup_dialog);
   defsubr (&Sns_reset_menu);
   defsubr (&Smenu_or_popup_active_p);
-  staticpro (&menu_items);
-  menu_items = Qnil;
 
-  Qdebug_on_next_call = intern ("debug-on-next-call");
+  Qdebug_on_next_call = intern_c_string ("debug-on-next-call");
   staticpro (&Qdebug_on_next_call);
 }
-
-// arch-tag: 75773656-52e5-4c44-a398-47bd87b32619