use dynwind_begin and dynwind_end
[bpt/emacs.git] / src / nsmenu.m
index 22ff4dd..7600b60 100644 (file)
@@ -1,5 +1,5 @@
 /* NeXT/Open/GNUstep and MacOSX Cocoa menu and toolbar module.
-   Copyright (C) 2007-2013 Free Software Foundation, Inc.
+   Copyright (C) 2007-2014 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -45,8 +45,6 @@ Carbon version by Yamamoto Mitsuharu. */
 #include <sys/types.h>
 #endif
 
-#define MenuStagger 10.0
-
 #if 0
 int menu_trace_num = 0;
 #define NSTRACE(x)        fprintf (stderr, "%s:%d: [%d] " #x "\n",        \
@@ -88,14 +86,6 @@ static int trackingMenu;
    ========================================================================== */
 
 
-/* FIXME: not currently used, but should normalize with other terms. */
-void
-x_activate_menubar (struct frame *f)
-{
-    fprintf (stderr, "XXX: Received x_activate_menubar event.\n");
-}
-
-
 /* Supposed to discard menubar and free storage.  Since we share the
    menubar among frames and update its context for the focused window,
    there is nothing to do here. */
@@ -120,14 +110,13 @@ popup_activated (void)
     2) deep_p, submenu = nil: Recompute all submenus.
     3) deep_p, submenu = non-nil: Update contents of a single submenu.
    -------------------------------------------------------------------------- */
-void
+static void
 ns_update_menubar (struct frame *f, bool deep_p, EmacsMenu *submenu)
 {
   NSAutoreleasePool *pool;
   id menu = [NSApp mainMenu];
   static EmacsMenu *last_submenu = nil;
   BOOL needsSet = NO;
-  const char *submenuTitle = [[submenu title] UTF8String];
   bool owfi;
   Lisp_Object items;
   widget_value *wv, *first_wv, *prev_wv = 0;
@@ -138,7 +127,7 @@ ns_update_menubar (struct frame *f, bool deep_p, EmacsMenu *submenu)
   long t;
 #endif
 
-  NSTRACE (set_frame_menubar);
+  NSTRACE (ns_update_menubar);
 
   if (f != SELECTED_FRAME ())
       return;
@@ -185,13 +174,14 @@ ns_update_menubar (struct frame *f, bool deep_p, EmacsMenu *submenu)
       int *submenu_n_panes;
       struct buffer *prev = current_buffer;
       Lisp_Object buffer;
-      ptrdiff_t specpdl_count = SPECPDL_INDEX ();
       int previous_menu_items_used = f->menu_bar_items_used;
       Lisp_Object *previous_items
        = alloca (previous_menu_items_used * sizeof *previous_items);
 
+      dynwind_begin ();
+
       /* lisp preliminaries */
-      buffer = XWINDOW (FRAME_SELECTED_WINDOW (f))->buffer;
+      buffer = XWINDOW (FRAME_SELECTED_WINDOW (f))->contents;
       specbind (Qinhibit_quit, Qt);
       specbind (Qdebug_on_next_call, Qnil);
       record_unwind_save_match_data ();
@@ -247,7 +237,7 @@ ns_update_menubar (struct frame *f, bool deep_p, EmacsMenu *submenu)
           /* FIXME: we'd like to only parse the needed submenu, but this
                was causing crashes in the _common parsing code.. need to make
                sure proper initialization done.. */
-/*        if (submenu && strcmp (submenuTitle, SSDATA (string)))
+/*        if (submenu && strcmp ([[submenu title] UTF8String], SSDATA (string)))
              continue; */
 
          submenu_start[i] = menu_items_used;
@@ -267,9 +257,9 @@ ns_update_menubar (struct frame *f, bool deep_p, EmacsMenu *submenu)
         {
           /* should have found a menu for this one but didn't */
           fprintf (stderr, "ERROR: did not find lisp menu for submenu '%s'.\n",
-                  submenuTitle);
+                  [[submenu title] UTF8String]);
          discard_menu_items ();
-         unbind_to (specpdl_count, Qnil);
+          dynwind_end ();
           [pool release];
           unblock_input ();
          return;
@@ -277,12 +267,8 @@ ns_update_menubar (struct frame *f, bool deep_p, EmacsMenu *submenu)
 
       /* parse stage 2: insert into lucid 'widget_value' structures
          [comments in other terms say not to evaluate lisp code here] */
-      wv = xmalloc_widget_value ();
-      wv->name = "menubar";
-      wv->value = 0;
-      wv->enabled = 1;
+      wv = make_widget_value ("menubar", NULL, true, Qnil);
       wv->button_type = BUTTON_TYPE_NONE;
-      wv->help = Qnil;
       first_wv = wv;
 
       for (i = 0; i < 4*n; i += 4)
@@ -331,7 +317,7 @@ ns_update_menubar (struct frame *f, bool deep_p, EmacsMenu *submenu)
 
               free_menubar_widget_value_tree (first_wv);
               discard_menu_items ();
-              unbind_to (specpdl_count, Qnil);
+              dynwind_end ();
               [pool release];
               unblock_input ();
               return;
@@ -343,7 +329,7 @@ ns_update_menubar (struct frame *f, bool deep_p, EmacsMenu *submenu)
       f->menu_bar_items_used = menu_items_used;
 
       /* Calls restore_menu_items, etc., as they were outside */
-      unbind_to (specpdl_count, Qnil);
+      dynwind_end ();
 
       /* 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 */
@@ -354,8 +340,6 @@ ns_update_menubar (struct frame *f, bool deep_p, EmacsMenu *submenu)
          string = AREF (items, i + 1);
          if (NILP (string))
            break;
-/*           if (submenu && strcmp (submenuTitle, SSDATA (string)))
-               continue; */
 
          wv->name = SSDATA (string);
           update_submenu_strings (wv->contents);
@@ -366,6 +350,7 @@ ns_update_menubar (struct frame *f, bool deep_p, EmacsMenu *submenu)
          create a new menu for each sub and fill it. */
       if (submenu)
         {
+          const char *submenuTitle = [[submenu title] UTF8String];
           for (wv = first_wv->contents; wv; wv = wv->next)
             {
               if (!strcmp (submenuTitle, wv->name))
@@ -378,7 +363,7 @@ ns_update_menubar (struct frame *f, bool deep_p, EmacsMenu *submenu)
         }
       else
         {
-          [menu fillWithWidgetValue: first_wv->contents];
+          [menu fillWithWidgetValue: first_wv->contents frame: f];
         }
 
     }
@@ -390,12 +375,8 @@ ns_update_menubar (struct frame *f, bool deep_p, EmacsMenu *submenu)
       int n;
       Lisp_Object string;
 
-      wv = xmalloc_widget_value ();
-      wv->name = "menubar";
-      wv->value = 0;
-      wv->enabled = 1;
+      wv = make_widget_value ("menubar", NULL, true, Qnil);
       wv->button_type = BUTTON_TYPE_NONE;
-      wv->help = Qnil;
       first_wv = wv;
 
       /* Make widget-value tree w/ just the top level menu bar strings */
@@ -451,12 +432,8 @@ ns_update_menubar (struct frame *f, bool deep_p, EmacsMenu *submenu)
            memcpy (previous_strings[i/4], SDATA (string),
                     min (10, SBYTES (string) + 1));
 
-         wv = xmalloc_widget_value ();
-         wv->name = SSDATA (string);
-         wv->value = 0;
-         wv->enabled = 1;
+         wv = make_widget_value (SSDATA (string), NULL, true, Qnil);
          wv->button_type = BUTTON_TYPE_NONE;
-         wv->help = Qnil;
          wv->call_data = (void *) (intptr_t) (-1);
 
 #ifdef NS_IMPL_COCOA
@@ -512,6 +489,19 @@ set_frame_menubar (struct frame *f, bool first_time, bool deep_p)
   ns_update_menubar (f, deep_p, nil);
 }
 
+void
+x_activate_menubar (struct frame *f)
+{
+#ifdef NS_IMPL_COCOA
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
+  ns_update_menubar (f, true, nil);
+  ns_check_pending_open_menu ();
+#endif
+#endif
+}
+
+
+
 
 /* ==========================================================================
 
@@ -528,6 +518,7 @@ set_frame_menubar (struct frame *f, bool first_time, bool deep_p)
 /* override designated initializer */
 - initWithTitle: (NSString *)title
 {
+  frame = 0;
   if ((self = [super initWithTitle: title]))
     [self setAutoenablesItems: NO];
   return self;
@@ -563,9 +554,36 @@ extern NSString *NSMenuDidBeginTrackingNotification;
   /* Update menu in menuNeedsUpdate only while tracking menus.  */
   trackingMenu = ([notification name] == NSMenuDidBeginTrackingNotification
                   ? 1 : 0);
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
+  if (! trackingMenu) ns_check_menu_open (nil);
+#endif
 }
+
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
+- (void)menuWillOpen:(NSMenu *)menu
+{
+  ++trackingMenu;
+
+#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
+  // On 10.6 we get repeated calls, only the one for NSSystemDefined is "real".
+  if ([[NSApp currentEvent] type] != NSSystemDefined) return;
 #endif
 
+  /* When dragging from one menu to another, we get willOpen followed by didClose,
+     i.e. trackingMenu == 3 in willOpen and then 2 after didClose.
+     We have updated all menus, so avoid doing it when trackingMenu == 3.  */
+  if (trackingMenu == 2)
+    ns_check_menu_open (menu);
+}
+
+- (void)menuDidClose:(NSMenu *)menu
+{
+  --trackingMenu;
+}
+#endif /* OSX >= 10.5 */
+
+#endif /* NS_IMPL_COCOA */
+
 /* delegate method called when a submenu is being opened: run a 'deep' call
    to set_frame_menubar */
 - (void)menuNeedsUpdate: (NSMenu *)menu
@@ -591,7 +609,12 @@ extern NSString *NSMenuDidBeginTrackingNotification;
   if (trackingMenu == 0)
     return;
 /*fprintf (stderr, "Updating menu '%s'\n", [[self title] UTF8String]); NSLog (@"%@\n", event); */
-  ns_update_menubar (frame, 1, self);
+#if (! defined (NS_IMPL_COCOA) \
+     || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5)
+  /* Don't know how to do this for anything other than OSX >= 10.5
+     This is wrong, as it might run Lisp code in the event loop.  */
+  ns_update_menubar (frame, true, self);
+#endif
 }
 
 
@@ -695,6 +718,11 @@ extern NSString *NSMenuDidBeginTrackingNotification;
 
 
 - (void)fillWithWidgetValue: (void *)wvptr
+{
+  [self fillWithWidgetValue: wvptr frame: (struct frame *)nil];
+}
+
+- (void)fillWithWidgetValue: (void *)wvptr frame: (struct frame *)f
 {
   widget_value *wv = (widget_value *)wvptr;
 
@@ -709,12 +737,17 @@ extern NSString *NSMenuDidBeginTrackingNotification;
 
       if (wv->contents)
         {
-          EmacsMenu *submenu = [[EmacsMenu alloc] initWithTitle: [item title]];
+          EmacsMenu *submenu;
+
+          if (f)
+            submenu = [[EmacsMenu alloc] initWithTitle: [item title] frame:f];
+          else
+            submenu = [[EmacsMenu alloc] initWithTitle: [item title]];
 
           [self setSubmenu: submenu forItem: item];
           [submenu fillWithWidgetValue: wv->contents];
           [submenu release];
-          [item setAction: nil];
+          [item setAction: (SEL)nil];
         }
     }
 
@@ -731,7 +764,7 @@ extern NSString *NSMenuDidBeginTrackingNotification;
 {
   NSString *titleStr = [NSString stringWithUTF8String: title];
   NSMenuItem *item = [self addItemWithTitle: titleStr
-                                     action: nil /*@selector (menuDown:) */
+                                     action: (SEL)nil /*@selector (menuDown:) */
                               keyEquivalent: @""];
   EmacsMenu *submenu = [[EmacsMenu alloc] initWithTitle: titleStr frame: f];
   [self setSubmenu: submenu forItem: item];
@@ -780,24 +813,24 @@ extern NSString *NSMenuDidBeginTrackingNotification;
    ========================================================================== */
 
 Lisp_Object
-ns_menu_show (FRAME_PTR f, int x, int y, bool for_click, bool keymaps,
+ns_menu_show (struct frame *f, int x, int y, int menuflags,
              Lisp_Object title, const char **error)
 {
   EmacsMenu *pmenu;
   NSPoint p;
   Lisp_Object tem;
-  ptrdiff_t specpdl_count = SPECPDL_INDEX ();
   widget_value *wv, *first_wv = 0;
+  bool keymaps = (menuflags & MENU_KEYMAPS);
+
+  block_input ();
+
+  dynwind_begin ();
 
   p.x = x; p.y = y;
 
   /* now parse stage 2 as in ns_update_menubar */
-  wv = xmalloc_widget_value ();
-  wv->name = "contextmenu";
-  wv->value = 0;
-  wv->enabled = 1;
+  wv = make_widget_value ("contextmenu", NULL, true, Qnil);
   wv->button_type = BUTTON_TYPE_NONE;
-  wv->help = Qnil;
   first_wv = wv;
 
 #if 0
@@ -868,18 +901,14 @@ ns_menu_show (FRAME_PTR f, int x, int y, bool for_click, bool keymaps,
             with its items as a submenu beneath it.  */
          if (!keymaps && strcmp (pane_string, ""))
            {
-             wv = xmalloc_widget_value ();
+             wv = make_widget_value (pane_string, NULL, true, Qnil);
              if (save_wv)
                save_wv->next = wv;
              else
                first_wv->contents = wv;
-             wv->name = pane_string;
              if (keymaps && !NILP (prefix))
                wv->name++;
-             wv->value = 0;
-             wv->enabled = 1;
              wv->button_type = BUTTON_TYPE_NONE;
-             wv->help = Qnil;
              save_wv = wv;
              prev_wv = 0;
            }
@@ -917,20 +946,18 @@ ns_menu_show (FRAME_PTR f, int x, int y, bool for_click, bool keymaps,
            }
 #endif /* not HAVE_MULTILINGUAL_MENU */
 
-         wv = xmalloc_widget_value ();
+         wv = make_widget_value (SSDATA (item_name), NULL, !NILP (enable),
+                                 STRINGP (help) ? help : Qnil);
          if (prev_wv)
            prev_wv->next = wv;
          else
            save_wv->contents = wv;
-         wv->name = SSDATA (item_name);
          if (!NILP (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) ? aref_addr (menu_items, i) : 0;
-         wv->enabled = !NILP (enable);
 
          if (NILP (type))
            wv->button_type = BUTTON_TYPE_NONE;
@@ -943,11 +970,6 @@ ns_menu_show (FRAME_PTR f, int x, int y, bool for_click, bool keymaps,
 
          wv->selected = !NILP (selected);
 
-          if (! STRINGP (help))
-           help = Qnil;
-
-         wv->help = help;
-
          prev_wv = wv;
 
          i += MENU_ITEMS_ITEM_LENGTH;
@@ -958,24 +980,19 @@ ns_menu_show (FRAME_PTR f, int x, int y, bool for_click, bool keymaps,
 
   if (!NILP (title))
     {
-      widget_value *wv_title = xmalloc_widget_value ();
-      widget_value *wv_sep = xmalloc_widget_value ();
+      widget_value *wv_title;
+      widget_value *wv_sep = make_widget_value ("--", NULL, false, Qnil);
 
       /* Maybe replace this separator with a bitmap or owner-draw item
         so that it looks better.  Having two separators looks odd.  */
-      wv_sep->name = "--";
       wv_sep->next = first_wv->contents;
-      wv_sep->help = Qnil;
 
 #ifndef HAVE_MULTILINGUAL_MENU
       if (STRING_MULTIBYTE (title))
        title = ENCODE_MENU_STRING (title);
 #endif
-
-      wv_title->name = SSDATA (title);
-      wv_title->enabled = NO;
+      wv_title = make_widget_value (SSDATA (title), NULL, false, Qnil);
       wv_title->button_type = BUTTON_TYPE_NONE;
-      wv_title->help = Qnil;
       wv_title->next = wv_sep;
       first_wv->contents = wv_title;
     }
@@ -984,13 +1001,14 @@ ns_menu_show (FRAME_PTR f, int x, int y, bool for_click, bool keymaps,
                                [NSString stringWithUTF8String: SSDATA (title)]];
   [pmenu fillWithWidgetValue: first_wv->contents];
   free_menubar_widget_value_tree (first_wv);
-  unbind_to (specpdl_count, Qnil);
+  dynwind_end ();
 
   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 ();
   return tem;
 }
 
@@ -1002,30 +1020,38 @@ ns_menu_show (FRAME_PTR f, int x, int y, bool for_click, bool keymaps,
    ========================================================================== */
 
 void
-free_frame_tool_bar (FRAME_PTR f)
+free_frame_tool_bar (struct frame *f)
 /* --------------------------------------------------------------------------
     Under NS we just hide the toolbar until it might be needed again.
    -------------------------------------------------------------------------- */
 {
+  EmacsView *view = FRAME_NS_VIEW (f);
   block_input ();
-  [[FRAME_NS_VIEW (f) toolbar] setVisible: NO];
+  view->wait_for_tool_bar = NO;
+  [[view toolbar] setVisible: NO];
   FRAME_TOOLBAR_HEIGHT (f) = 0;
   unblock_input ();
 }
 
 void
-update_frame_tool_bar (FRAME_PTR f)
+update_frame_tool_bar (struct frame *f)
 /* --------------------------------------------------------------------------
     Update toolbar contents
    -------------------------------------------------------------------------- */
 {
-  int i;
+  int i, k = 0;
   EmacsView *view = FRAME_NS_VIEW (f);
   NSWindow *window = [view window];
   EmacsToolbar *toolbar = [view toolbar];
 
+  if (view == nil || toolbar == nil) return;
   block_input ();
+
+#ifdef NS_IMPL_COCOA
   [toolbar clearActive];
+#else
+  [toolbar clearAll];
+#endif
 
   /* update EmacsToolbar as in GtkUtils, build items list */
   for (i = 0; i < f->n_tool_bar_items; ++i)
@@ -1041,6 +1067,15 @@ update_frame_tool_bar (FRAME_PTR f)
       Lisp_Object helpObj;
       const char *helpText;
 
+      /* Check if this is a separator.  */
+      if (EQ (TOOLPROP (TOOL_BAR_ITEM_TYPE), Qt))
+        {
+          /* Skip separators.  Newer OSX don't show them, and on GNUstep they
+             are wide as a button, thus overflowing the toolbar most of
+             the time.  */
+          continue;
+        }
+
       /* If image is a vector, choose the image according to the
         button state.  */
       image = TOOLPROP (TOOL_BAR_ITEM_IMAGES);
@@ -1077,7 +1112,10 @@ update_frame_tool_bar (FRAME_PTR f)
           continue;
         }
 
-      [toolbar addDisplayItemWithImage: img->pixmap idx: i helpText: helpText
+      [toolbar addDisplayItemWithImage: img->pixmap
+                                   idx: k++
+                                   tag: i
+                              helpText: helpText
                                enabled: enabled_p];
 #undef TOOLPROP
     }
@@ -1085,6 +1123,7 @@ update_frame_tool_bar (FRAME_PTR f)
   if (![toolbar isVisible])
       [toolbar setVisible: YES];
 
+#ifdef NS_IMPL_COCOA
   if ([toolbar changed])
     {
       /* inform app that toolbar has changed */
@@ -1106,13 +1145,18 @@ update_frame_tool_bar (FRAME_PTR f)
       [toolbar setConfigurationFromDictionary: newDict];
       [newDict release];
     }
+#endif
 
   FRAME_TOOLBAR_HEIGHT (f) =
     NSHeight ([window frameRectForContentRect: NSMakeRect (0, 0, 0, 0)])
     - FRAME_NS_TITLEBAR_HEIGHT (f);
-    if (FRAME_TOOLBAR_HEIGHT (f) < 0) // happens if frame is fullscreen.
-      FRAME_TOOLBAR_HEIGHT (f) = 0;
-    unblock_input ();
+  if (FRAME_TOOLBAR_HEIGHT (f) < 0) // happens if frame is fullscreen.
+    FRAME_TOOLBAR_HEIGHT (f) = 0;
+
+  if (view->wait_for_tool_bar && FRAME_TOOLBAR_HEIGHT (f) > 0)
+      [view setNeedsDisplay: YES];
+
+  unblock_input ();
 }
 
 
@@ -1133,6 +1177,7 @@ update_frame_tool_bar (FRAME_PTR f)
   [self setDelegate: self];
   identifierToItem = [[NSMutableDictionary alloc] initWithCapacity: 10];
   activeIdentifiers = [[NSMutableArray alloc] initWithCapacity: 8];
+  prevIdentifiers = nil;
   prevEnablement = enablement = 0L;
   return self;
 }
@@ -1154,18 +1199,29 @@ update_frame_tool_bar (FRAME_PTR f)
   enablement = 0L;
 }
 
+- (void) clearAll
+{
+  [self clearActive];
+  while ([[self items] count] > 0)
+    [self removeItemAtIndex: 0];
+}
+
 - (BOOL) changed
 {
   return [activeIdentifiers isEqualToArray: prevIdentifiers] &&
     enablement == prevEnablement ? NO : YES;
 }
 
-- (void) addDisplayItemWithImage: (EmacsImage *)img idx: (int)idx
-                        helpText: (const char *)help enabled: (BOOL)enabled
+- (void) addDisplayItemWithImage: (EmacsImage *)img
+                             idx: (int)idx
+                             tag: (int)tag
+                        helpText: (const char *)help
+                         enabled: (BOOL)enabled
 {
   /* 1) come up w/identifier */
   NSString *identifier
-      = [NSString stringWithFormat: @"%u", [img hash]];
+    = [NSString stringWithFormat: @"%lu", (unsigned long)[img hash]];
+  [activeIdentifiers addObject: identifier];
 
   /* 2) create / reuse item */
   NSToolbarItem *item = [identifierToItem objectForKey: identifier];
@@ -1177,20 +1233,25 @@ update_frame_tool_bar (FRAME_PTR f)
       [item setToolTip: [NSString stringWithUTF8String: help]];
       [item setTarget: emacsView];
       [item setAction: @selector (toolbarClicked:)];
+      [identifierToItem setObject: item forKey: identifier];
     }
 
-  [item setTag: idx];
+#ifdef NS_IMPL_GNUSTEP
+  [self insertItemWithItemIdentifier: identifier atIndex: idx];
+#endif
+
+  [item setTag: tag];
   [item setEnabled: enabled];
 
   /* 3) update state */
-  [identifierToItem setObject: item forKey: identifier];
-  [activeIdentifiers addObject: identifier];
   enablement = (enablement << 1) | (enabled == YES);
 }
 
 /* This overrides super's implementation, which automatically sets
    all items to enabled state (for some reason). */
-- (void)validateVisibleItems { }
+- (void)validateVisibleItems
+{
+}
 
 
 /* delegate methods */
@@ -1213,7 +1274,8 @@ update_frame_tool_bar (FRAME_PTR f)
 - (NSArray *)toolbarAllowedItemIdentifiers: (NSToolbar *)toolbar
 {
   /* return entire set... */
-  return [identifierToItem allKeys];
+  return activeIdentifiers;
+  //return [identifierToItem allKeys];
 }
 
 /* optional and unneeded */
@@ -1300,6 +1362,7 @@ update_frame_tool_bar (FRAME_PTR f)
   wr.size = [textField frame].size;
 
   [win setFrame: wr display: YES];
+  [win setLevel: NSPopUpMenuWindowLevel];
   [win orderFront: self];
   [win display];
   timer = [NSTimer scheduledTimerWithTimeInterval: (float)seconds target: self
@@ -1346,10 +1409,10 @@ struct Popdown_data
   EmacsDialogPanel *dialog;
 };
 
-static Lisp_Object
-pop_down_menu (Lisp_Object arg)
+static void
+pop_down_menu (void *arg)
 {
-  struct Popdown_data *unwind_data = XSAVE_POINTER (arg, 0);
+  struct Popdown_data *unwind_data = arg;
 
   block_input ();
   if (popup_activated_flag)
@@ -1363,61 +1426,23 @@ pop_down_menu (Lisp_Object arg)
 
   xfree (unwind_data);
   unblock_input ();
-
-  return Qnil;
 }
 
 
 Lisp_Object
-ns_popup_dialog (Lisp_Object position, Lisp_Object contents, Lisp_Object header)
+ns_popup_dialog (struct frame *f, Lisp_Object header, Lisp_Object contents)
 {
   id dialog;
   Lisp_Object window, tem, title;
-  struct frame *f;
   NSPoint p;
   BOOL isQ;
   NSAutoreleasePool *pool;
 
   NSTRACE (x-popup-dialog);
 
-  check_ns ();
-
   isQ = NILP (header);
 
-  if (EQ (position, Qt)
-      || (CONSP (position) && (EQ (XCAR (position), Qmenu_bar)
-                               || EQ (XCAR (position), Qtool_bar))))
-    {
-      window = selected_window;
-    }
-  else if (CONSP (position))
-    {
-      Lisp_Object tem;
-      tem = Fcar (position);
-      if (XTYPE (tem) == Lisp_Cons)
-        window = Fcar (Fcdr (position));
-      else
-        {
-          tem = Fcar (Fcdr (position));  /* EVENT_START (position) */
-          window = Fcar (tem);      /* POSN_WINDOW (tem) */
-        }
-    }
-  else if (WINDOWP (position) || FRAMEP (position))
-    {
-      window = position;
-    }
-  else
-    window = Qnil;
-
-  if (FRAMEP (window))
-    f = XFRAME (window);
-  else if (WINDOWP (window))
-    {
-      CHECK_LIVE_WINDOW (window);
-      f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
-    }
-  else
-    CHECK_WINDOW (window);
+  check_window_system (f);
 
   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;
@@ -1428,7 +1453,7 @@ ns_popup_dialog (Lisp_Object position, Lisp_Object contents, Lisp_Object header)
   if (NILP (Fcar (Fcdr (contents))))
     /* No buttons specified, add an "Ok" button so users can pop down
        the dialog.  */
-    contents = Fcons (title, Fcons (Fcons (build_string ("Ok"), Qt), Qnil));
+    contents = list2 (title, Fcons (build_string ("Ok"), Qt));
 
   block_input ();
   pool = [[NSAutoreleasePool alloc] init];
@@ -1436,16 +1461,16 @@ ns_popup_dialog (Lisp_Object position, Lisp_Object contents, Lisp_Object header)
                                            isQuestion: isQ];
 
   {
-    ptrdiff_t specpdl_count = SPECPDL_INDEX ();
     struct Popdown_data *unwind_data = xmalloc (sizeof (*unwind_data));
 
     unwind_data->pool = pool;
     unwind_data->dialog = dialog;
 
-    record_unwind_protect (pop_down_menu, make_save_pointer (unwind_data));
+    dynwind_begin ();
+    record_unwind_protect_ptr (pop_down_menu, unwind_data);
     popup_activated_flag = 1;
     tem = [dialog runDialogAt: p];
-    unbind_to (specpdl_count, Qnil);  /* calls pop_down_menu */
+    dynwind_end ();
   }
 
   unblock_input ();
@@ -1504,7 +1529,7 @@ ns_popup_dialog (Lisp_Object position, Lisp_Object contents, Lisp_Object header)
   [img autorelease];
   [imgView autorelease];
 
-  aStyle = NSTitledWindowMask;
+  aStyle = NSTitledWindowMask|NSClosableWindowMask|NSUtilityWindowMask;
   flag = YES;
   rows = 0;
   cols = 1;
@@ -1572,9 +1597,6 @@ ns_popup_dialog (Lisp_Object position, Lisp_Object contents, Lisp_Object header)
   [self setOneShot: YES];
   [self setReleasedWhenClosed: YES];
   [self setHidesOnDeactivate: YES];
-  [self setStyleMask:
-          NSTitledWindowMask|NSClosableWindowMask|NSUtilityWindowMask];
-
   return self;
 }
 
@@ -1598,24 +1620,24 @@ ns_popup_dialog (Lisp_Object position, Lisp_Object contents, Lisp_Object header)
   int row = 0;
   int buttons = 0, btnnr = 0;
 
-  for (; XTYPE (lst) == Lisp_Cons; lst = XCDR (lst))
+  for (; CONSP (lst); lst = XCDR (lst))
     {
       item = XCAR (list);
-      if (XTYPE (item) == Lisp_Cons)
+      if (CONSP (item))
         ++buttons;
     }
 
   if (buttons > 0)
-    button_values = (Lisp_Object *) xmalloc (buttons * sizeof (*button_values));
+    button_values = xmalloc (buttons * sizeof *button_values);
 
-  for (; XTYPE (list) == Lisp_Cons; list = XCDR (list))
+  for (; CONSP (list); list = XCDR (list))
     {
       item = XCAR (list);
-      if (XTYPE (item) == Lisp_String)
+      if (STRINGP (item))
         {
           [self addString: SSDATA (item) row: row++];
         }
-      else if (XTYPE (item) == Lisp_Cons)
+      else if (CONSP (item))
         {
           button_values[btnnr] = XCDR (item);
           [self addButton: SSDATA (XCAR (item)) value: btnnr row: row++];
@@ -1692,7 +1714,7 @@ ns_popup_dialog (Lisp_Object position, Lisp_Object contents, Lisp_Object header)
   Lisp_Object head;
   [super init];
 
-  if (XTYPE (contents) == Lisp_Cons)
+  if (CONSP (contents))
     {
       head = Fcar (contents);
       [self process_dialog: Fcdr (contents)];
@@ -1700,7 +1722,7 @@ ns_popup_dialog (Lisp_Object position, Lisp_Object contents, Lisp_Object header)
   else
     head = contents;
 
-  if (XTYPE (head) == Lisp_String)
+  if (STRINGP (head))
       [title setStringValue:
                  [NSString stringWithUTF8String: SSDATA (head)]];
   else if (isQ == YES)
@@ -1790,11 +1812,11 @@ ns_popup_dialog (Lisp_Object position, Lisp_Object contents, Lisp_Object header)
   while (popup_activated_flag)
     {
       NSTimer *tmo = nil;
-      EMACS_TIME next_time = timer_check ();
+      struct timespec next_time = timer_check ();
 
-      if (EMACS_TIME_VALID_P (next_time))
+      if (timespec_valid_p (next_time))
         {
-          double time = EMACS_TIME_TO_DOUBLE (next_time);
+          double time = timespectod (next_time);
           tmo = [NSTimer timerWithTimeInterval: time
                                         target: self
                                       selector: @selector (timeout_handler:)
@@ -1839,34 +1861,6 @@ DEFUN ("ns-reset-menu", Fns_reset_menu, Sns_reset_menu, 0, 0, 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.
-If POSITION is t, it means to use the frame the mouse is on.
-The dialog box appears in the middle of the specified frame.
-
-CONTENTS specifies the alternatives to display in the dialog box.
-It is a list of the form (DIALOG ITEM1 ITEM2...).
-Each ITEM is a cons cell (STRING . VALUE).
-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.)
-
-If HEADER is non-nil, the frame title for the box is "Information",
-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.  */)
-     (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)
@@ -1883,14 +1877,13 @@ DEFUN ("menu-or-popup-active-p", Fmenu_or_popup_active_p, Smenu_or_popup_active_
 void
 syms_of_nsmenu (void)
 {
+#include "nsmenu.x"
+
 #ifndef NS_IMPL_COCOA
-  /* Don't know how to keep track of this in Next/Open/Gnustep.  Always
+  /* Don't know how to keep track of this in Next/Open/GNUstep.  Always
      update menus there.  */
   trackingMenu = 1;
 #endif
-  defsubr (&Sx_popup_dialog);
-  defsubr (&Sns_reset_menu);
-  defsubr (&Smenu_or_popup_active_p);
 
   Qdebug_on_next_call = intern_c_string ("debug-on-next-call");
   staticpro (&Qdebug_on_next_call);