Drop FRAME_PTR typedef.
[bpt/emacs.git] / src / nsmenu.m
index 657b930..464be89 100644 (file)
@@ -1,5 +1,5 @@
 /* NeXT/Open/GNUstep and MacOSX Cocoa menu and toolbar module.
-   Copyright (C) 2007-2012 Free Software Foundation, Inc.
+   Copyright (C) 2007-2013 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -24,7 +24,6 @@ Carbon version by Yamamoto Mitsuharu. */
 /* 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"
@@ -46,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",        \
@@ -89,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. */
@@ -116,21 +105,19 @@ popup_activated (void)
 
 /* --------------------------------------------------------------------------
     Update menubar.  Three cases:
-    1) deep_p = 0, submenu = nil: Fresh switch onto a frame -- either set up
+    1) ! deep_p, submenu = nil: Fresh switch onto a frame -- either set up
        just top-level menu strings (OS X), or goto case (2) (GNUstep).
-    2) deep_p = 1, submenu = nil: Recompute all submenus.
-    3) deep_p = 1, submenu = non-nil: Update contents of a single submenu.
+    2) deep_p, submenu = nil: Recompute all submenus.
+    3) deep_p, submenu = non-nil: Update contents of a single submenu.
    -------------------------------------------------------------------------- */
-void
-ns_update_menubar (struct frame *f, int deep_p, EmacsMenu *submenu)
+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];
-  extern int waiting_for_input;
-  int owfi;
+  bool owfi;
   Lisp_Object items;
   widget_value *wv, *first_wv, *prev_wv = 0;
   int i;
@@ -140,14 +127,14 @@ ns_update_menubar (struct frame *f, int deep_p, EmacsMenu *submenu)
   long t;
 #endif
 
-  NSTRACE (set_frame_menubar);
+  NSTRACE (ns_update_menubar);
 
   if (f != SELECTED_FRAME ())
       return;
   XSETFRAME (Vmenu_updating_frame, f);
 /*fprintf (stderr, "ns_update_menubar: frame: %p\tdeep: %d\tsub: %p\n", f, deep_p, submenu); */
 
-  BLOCK_INPUT;
+  block_input ();
   pool = [[NSAutoreleasePool alloc] init];
 
   /* Menu may have been created automatically; if so, discard it. */
@@ -183,7 +170,8 @@ ns_update_menubar (struct frame *f, int deep_p, EmacsMenu *submenu)
       /* Fully parse one or more of the submenus. */
       int n = 0;
       int *submenu_start, *submenu_end;
-      int *submenu_top_level_items, *submenu_n_panes;
+      bool *submenu_top_level_items;
+      int *submenu_n_panes;
       struct buffer *prev = current_buffer;
       Lisp_Object buffer;
       ptrdiff_t specpdl_count = SPECPDL_INDEX ();
@@ -192,7 +180,7 @@ ns_update_menubar (struct frame *f, int deep_p, EmacsMenu *submenu)
        = alloca (previous_menu_items_used * sizeof *previous_items);
 
       /* 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 ();
@@ -214,7 +202,7 @@ ns_update_menubar (struct frame *f, int deep_p, EmacsMenu *submenu)
       if (! NILP (Vlucid_menu_bar_dirty_flag))
        call0 (Qrecompute_lucid_menubar);
       safe_run_hooks (Qmenu_bar_update_hook);
-      FSET (f, menu_bar_items, menu_bar_items (FRAME_MENU_BAR_ITEMS (f)));
+      fset_menu_bar_items (f, menu_bar_items (FRAME_MENU_BAR_ITEMS (f)));
 
       /* Now ready to go */
       items = FRAME_MENU_BAR_ITEMS (f);
@@ -248,7 +236,7 @@ ns_update_menubar (struct frame *f, int 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;
@@ -268,11 +256,11 @@ ns_update_menubar (struct frame *f, int 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);
           [pool release];
-          UNBLOCK_INPUT;
+          unblock_input ();
          return;
         }
 
@@ -334,13 +322,13 @@ ns_update_menubar (struct frame *f, int deep_p, EmacsMenu *submenu)
               discard_menu_items ();
               unbind_to (specpdl_count, Qnil);
               [pool release];
-              UNBLOCK_INPUT;
+              unblock_input ();
               return;
             }
         }
       /* The menu items are different, so store them in the frame */
       /* FIXME: this is not correct for single-submenu case */
-      FSET (f, menu_bar_vector, menu_items);
+      fset_menu_bar_vector (f, menu_items);
       f->menu_bar_items_used = menu_items_used;
 
       /* Calls restore_menu_items, etc., as they were outside */
@@ -355,8 +343,6 @@ ns_update_menubar (struct frame *f, int 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);
@@ -367,6 +353,7 @@ ns_update_menubar (struct frame *f, int 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))
@@ -405,7 +392,7 @@ ns_update_menubar (struct frame *f, int deep_p, EmacsMenu *submenu)
         {
           free_menubar_widget_value_tree (first_wv);
           [pool release];
-          UNBLOCK_INPUT;
+          unblock_input ();
           return;
         }
 
@@ -436,7 +423,7 @@ ns_update_menubar (struct frame *f, int deep_p, EmacsMenu *submenu)
             {
               free_menubar_widget_value_tree (first_wv);
               [pool release];
-              UNBLOCK_INPUT;
+              unblock_input ();
               return;
             }
         }
@@ -499,7 +486,7 @@ ns_update_menubar (struct frame *f, int deep_p, EmacsMenu *submenu)
     [NSApp setMainMenu: menu];
 
   [pool release];
-  UNBLOCK_INPUT;
+  unblock_input ();
 
 }
 
@@ -508,11 +495,36 @@ ns_update_menubar (struct frame *f, int deep_p, EmacsMenu *submenu)
    frame's menus have changed, and the *step representation should be updated
    from Lisp. */
 void
-set_frame_menubar (struct frame *f, int first_time, int deep_p)
+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
+  NSArray *a = [[NSApp mainMenu] itemArray];
+  /* Update each submenu separately so ns_update_menubar doesn't reset
+     the delegate.  */
+  int i = 0;
+  while (i < [a count])
+    {
+      EmacsMenu *menu = (EmacsMenu *)[[a objectAtIndex:i] submenu];
+      const char *title = [[menu title] UTF8String];
+      if (strcmp (title, ns_get_pending_menu_title ()) == 0)
+        {
+          ns_update_menubar (f, true, menu);
+          break;
+        }
+      ++i;
+    }
+  ns_check_pending_open_menu ();
+#endif
+}
+
+
+
 
 /* ==========================================================================
 
@@ -565,6 +577,14 @@ extern NSString *NSMenuDidBeginTrackingNotification;
   trackingMenu = ([notification name] == NSMenuDidBeginTrackingNotification
                   ? 1 : 0);
 }
+
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
+- (void)menuWillOpen:(NSMenu *)menu
+{
+  ns_check_menu_open (menu);
+}
+#endif
+
 #endif
 
 /* delegate method called when a submenu is being opened: run a 'deep' call
@@ -589,13 +609,15 @@ extern NSString *NSMenuDidBeginTrackingNotification;
      From 10.6 on, we could also use -[NSMenu propertiesToUpdate]: In the
      key press case, NSMenuPropertyItemImage (e.g.) won't be set.
   */
-  if (trackingMenu == 0
-      /* 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)
+  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
 }
 
 
@@ -718,7 +740,7 @@ extern NSString *NSMenuDidBeginTrackingNotification;
           [self setSubmenu: submenu forItem: item];
           [submenu fillWithWidgetValue: wv->contents];
           [submenu release];
-          [item setAction: nil];
+          [item setAction: (SEL)nil];
         }
     }
 
@@ -726,11 +748,6 @@ extern NSString *NSMenuDidBeginTrackingNotification;
 #ifdef NS_IMPL_GNUSTEP
   if ([[self window] isVisible])
     [self sizeToFit];
-#else
-#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_2
-  if ([self supermenu] == nil)
-    [self sizeToFit];
-#endif
 #endif
 }
 
@@ -740,7 +757,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];
@@ -750,7 +767,7 @@ extern NSString *NSMenuDidBeginTrackingNotification;
 
 /* run a menu in popup mode */
 - (Lisp_Object)runMenuAt: (NSPoint)p forFrame: (struct frame *)f
-                 keymaps: (int)keymaps
+                 keymaps: (bool)keymaps
 {
   EmacsView *view = FRAME_NS_VIEW (f);
   NSEvent *e, *event;
@@ -789,7 +806,7 @@ extern NSString *NSMenuDidBeginTrackingNotification;
    ========================================================================== */
 
 Lisp_Object
-ns_menu_show (FRAME_PTR f, int x, int y, int for_click, int keymaps,
+ns_menu_show (struct frame *f, int x, int y, bool for_click, bool keymaps,
              Lisp_Object title, const char **error)
 {
   EmacsMenu *pmenu;
@@ -811,7 +828,7 @@ ns_menu_show (FRAME_PTR f, int x, int y, int for_click, int keymaps,
 
 #if 0
   /* FIXME: a couple of one-line differences prevent reuse */
-  wv = digest_single_submenu (0, menu_items_used, Qnil);
+  wv = digest_single_submenu (0, menu_items_used, 0);
 #else
   {
   widget_value *save_wv = 0, *prev_wv = 0;
@@ -948,7 +965,7 @@ ns_menu_show (FRAME_PTR f, int x, int y, int for_click, int keymaps,
          else if (EQ (type, QCradio))
            wv->button_type = BUTTON_TYPE_RADIO;
          else
-           abort ();
+           emacs_abort ();
 
          wv->selected = !NILP (selected);
 
@@ -1011,30 +1028,35 @@ ns_menu_show (FRAME_PTR f, int x, int y, int for_click, int 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.
    -------------------------------------------------------------------------- */
 {
-  BLOCK_INPUT;
+  block_input ();
   [[FRAME_NS_VIEW (f) toolbar] setVisible: NO];
   FRAME_TOOLBAR_HEIGHT (f) = 0;
-  UNBLOCK_INPUT;
+  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];
 
-  BLOCK_INPUT;
+  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)
@@ -1050,6 +1072,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);
@@ -1086,7 +1117,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
     }
@@ -1094,6 +1128,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 */
@@ -1115,11 +1150,14 @@ 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);
-  UNBLOCK_INPUT;
+    if (FRAME_TOOLBAR_HEIGHT (f) < 0) // happens if frame is fullscreen.
+      FRAME_TOOLBAR_HEIGHT (f) = 0;
+    unblock_input ();
 }
 
 
@@ -1140,6 +1178,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;
 }
@@ -1161,18 +1200,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]];
+  [activeIdentifiers addObject: identifier];
 
   /* 2) create / reuse item */
   NSToolbarItem *item = [identifierToItem objectForKey: identifier];
@@ -1184,20 +1234,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 */
@@ -1220,7 +1275,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 */
@@ -1307,6 +1363,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
@@ -1353,13 +1410,12 @@ struct Popdown_data
   EmacsDialogPanel *dialog;
 };
 
-static Lisp_Object
-pop_down_menu (Lisp_Object arg)
+static void
+pop_down_menu (void *arg)
 {
-  struct Lisp_Save_Value *p = XSAVE_VALUE (arg);
-  struct Popdown_data *unwind_data = (struct Popdown_data *) p->pointer;
+  struct Popdown_data *unwind_data = arg;
 
-  BLOCK_INPUT;
+  block_input ();
   if (popup_activated_flag)
     {
       EmacsDialogPanel *panel = unwind_data->dialog;
@@ -1370,9 +1426,7 @@ pop_down_menu (Lisp_Object arg)
     }
 
   xfree (unwind_data);
-  UNBLOCK_INPUT;
-
-  return Qnil;
+  unblock_input ();
 }
 
 
@@ -1388,8 +1442,6 @@ ns_popup_dialog (Lisp_Object position, Lisp_Object contents, Lisp_Object header)
 
   NSTRACE (x-popup-dialog);
 
-  check_ns ();
-
   isQ = NILP (header);
 
   if (EQ (position, Qt)
@@ -1427,6 +1479,8 @@ ns_popup_dialog (Lisp_Object position, Lisp_Object contents, Lisp_Object header)
   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;
 
@@ -1436,9 +1490,9 @@ 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;
+  block_input ();
   pool = [[NSAutoreleasePool alloc] init];
   dialog = [[EmacsDialogPanel alloc] initFromContents: contents
                                            isQuestion: isQ];
@@ -1450,13 +1504,13 @@ ns_popup_dialog (Lisp_Object position, Lisp_Object contents, Lisp_Object header)
     unwind_data->pool = pool;
     unwind_data->dialog = dialog;
 
-    record_unwind_protect (pop_down_menu, make_save_value (unwind_data, 0));
+    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 */
   }
 
-  UNBLOCK_INPUT;
+  unblock_input ();
 
   return tem;
 }
@@ -1497,6 +1551,8 @@ ns_popup_dialog (Lisp_Object position, Lisp_Object contents, Lisp_Object header)
   FlippedView *contentView;
   NSImage *img;
 
+  dialog_return   = Qundefined;
+  button_values   = NULL;
   area.origin.x   = 3*SPACER;
   area.origin.y   = 2*SPACER;
   area.size.width = ICONSIZE;
@@ -1510,7 +1566,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;
@@ -1584,38 +1640,56 @@ ns_popup_dialog (Lisp_Object position, Lisp_Object contents, Lisp_Object header)
 
 - (BOOL)windowShouldClose: (id)sender
 {
-  [NSApp stopModalWithCode: XHASH (Qnil)]; // FIXME: BIG UGLY HACK!!
+  window_closed = YES;
+  [NSApp stop:self];
   return NO;
 }
 
+- (void)dealloc
+{
+  xfree (button_values);
+  [super dealloc];
+}
 
-void process_dialog (id window, Lisp_Object list)
+- (void)process_dialog: (Lisp_Object) list
 {
-  Lisp_Object item;
+  Lisp_Object item, lst = list;
   int row = 0;
+  int buttons = 0, btnnr = 0;
+
+  for (; XTYPE (lst) == Lisp_Cons; lst = XCDR (lst))
+    {
+      item = XCAR (list);
+      if (XTYPE (item) == Lisp_Cons)
+        ++buttons;
+    }
+
+  if (buttons > 0)
+    button_values = (Lisp_Object *) xmalloc (buttons * sizeof (*button_values));
 
   for (; XTYPE (list) == Lisp_Cons; list = XCDR (list))
     {
       item = XCAR (list);
       if (XTYPE (item) == Lisp_String)
         {
-          [window addString: SSDATA (item) row: row++];
+          [self addString: SSDATA (item) row: row++];
         }
       else if (XTYPE (item) == Lisp_Cons)
         {
-          [window addButton: SSDATA (XCAR (item))
-                      value: XCDR (item) row: row++];
+          button_values[btnnr] = XCDR (item);
+          [self addButton: SSDATA (XCAR (item)) value: btnnr row: row++];
+          ++btnnr;
         }
       else if (NILP (item))
         {
-          [window addSplit];
+          [self addSplit];
           row = 0;
         }
     }
 }
 
 
-- addButton: (char *)str value: (Lisp_Object)val row: (int)row
+- (void)addButton: (char *)str value: (int)tag row: (int)row
 {
   id cell;
 
@@ -1628,15 +1702,13 @@ void process_dialog (id window, Lisp_Object list)
   [cell setTarget: self];
   [cell setAction: @selector (clicked: )];
   [cell setTitle: [NSString stringWithUTF8String: str]];
-  [cell setTag: XHASH (val)];  // FIXME: BIG UGLY HACK!!
+  [cell setTag: tag];
   [cell setBordered: YES];
   [cell setEnabled: YES];
-
-  return self;
 }
 
 
-- addString: (char *)str row: (int)row
+- (void)addString: (char *)str row: (int)row
 {
   id cell;
 
@@ -1649,32 +1721,28 @@ void process_dialog (id window, Lisp_Object list)
   [cell setTitle: [NSString stringWithUTF8String: str]];
   [cell setBordered: YES];
   [cell setEnabled: NO];
-
-  return self;
 }
 
 
-- addSplit
+- (void)addSplit
 {
   [matrix addColumn];
   cols++;
-  return self;
 }
 
 
-- clicked: sender
+- (void)clicked: sender
 {
   NSArray *sellist = nil;
   EMACS_INT seltag;
 
   sellist = [sender selectedCells];
-  if ([sellist count]<1)
-    return self;
+  if ([sellist count] < 1)
+    return;
 
   seltag = [[sellist objectAtIndex: 0] tag];
-  if (seltag != XHASH (Qundefined)) // FIXME: BIG UGLY HACK!!
-    [NSApp stopModalWithCode: seltag];
-  return self;
+  dialog_return = button_values[seltag];
+  [NSApp stop:self];
 }
 
 
@@ -1686,7 +1754,7 @@ void process_dialog (id window, Lisp_Object list)
   if (XTYPE (contents) == Lisp_Cons)
     {
       head = Fcar (contents);
-      process_dialog (self, Fcdr (contents));
+      [self process_dialog: Fcdr (contents)];
     }
   else
     head = contents;
@@ -1706,7 +1774,7 @@ void process_dialog (id window, Lisp_Object list)
     if (cols == 1 && rows > 1) /* Never told where to split */
       {
         [matrix addColumn];
-        for (i = 0; i<rows/2; i++)
+        for (i = 0; i < rows/2; i++)
           {
             [matrix putCell: [matrix cellAtRow: (rows+1)/2 column: 0]
                       atRow: i column: 1];
@@ -1753,16 +1821,30 @@ void process_dialog (id window, Lisp_Object list)
 }
 
 
+
 - (void)timeout_handler: (NSTimer *)timedEntry
 {
-  timer_fired = 1;
-  [NSApp abortModal];
+  NSEvent *nxev = [NSEvent otherEventWithType: NSApplicationDefined
+                            location: NSMakePoint (0, 0)
+                       modifierFlags: 0
+                           timestamp: 0
+                        windowNumber: [[NSApp mainWindow] windowNumber]
+                             context: [NSApp context]
+                             subtype: 0
+                               data1: 0
+                               data2: 0];
+
+  timer_fired = YES;
+  /* We use sto because stopModal/abortModal out of the main loop does not
+     seem to work in 10.6.  But as we use stop we must send a real event so
+     the stop is seen and acted upon.  */
+  [NSApp stop:self];
+  [NSApp postEvent: nxev atStart: NO];
 }
 
 - (Lisp_Object)runDialogAt: (NSPoint)p
 {
-  NSInteger ret = 0;
+  Lisp_Object ret = Qundefined;
 
   while (popup_activated_flag)
     {
@@ -1780,8 +1862,10 @@ void process_dialog (id window, Lisp_Object list)
           [[NSRunLoop currentRunLoop] addTimer: tmo
                                        forMode: NSModalPanelRunLoopMode];
         }
-      timer_fired = 0;
-      ret = [NSApp runModalForWindow: self];
+      timer_fired = NO;
+      dialog_return = Qundefined;
+      [NSApp runModalForWindow: self];
+      ret = dialog_return;
       if (! timer_fired)
         {
           if (tmo != nil) [tmo invalidate]; /* Cancels timer */
@@ -1789,11 +1873,11 @@ void process_dialog (id window, Lisp_Object list)
         }
     }
 
-  {                            /* FIXME: BIG UGLY HACK!!! */
-      Lisp_Object tmp;
-      *(EMACS_INT*)(&tmp) = ret;
-      return tmp;
-  }
+  if (EQ (ret, Qundefined) && window_closed)
+    /* Make close button pressed equivalent to C-g.  */
+    Fsignal (Qquit, Qnil);
+
+  return ret;
 }
 
 @end