* nsfns.m (ns-read-file-name): BLOCK_INPUT while showing dialog. \n \n* nsmenu.m...
[bpt/emacs.git] / src / nsmenu.m
index 825568f..636b71c 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, 2008, 2009 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -21,7 +21,10 @@ 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. */
 
+/* This should be the first include, as it may set up #defines affecting
+   interpretation of even the system includes. */
 #include "config.h"
+
 #include "lisp.h"
 #include "window.h"
 #include "buffer.h"
@@ -33,9 +36,12 @@ Carbon version by Yamamoto Mitsuharu. */
 #include "termhooks.h"
 #include "keyboard.h"
 
-/* for profiling */
+#define NSMENUPROFILE 0
+
+#if NSMENUPROFILE
 #include <sys/timeb.h>
 #include <sys/types.h>
+#endif
 
 #define MenuStagger 10.0
 
@@ -67,6 +73,7 @@ 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 +86,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)
 {
@@ -111,7 +118,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 +132,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
@@ -160,7 +166,7 @@ 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
@@ -326,7 +332,7 @@ ns_update_menubar (struct frame *f, int deep_p, EmacsMenu *submenu)
             {
               /* 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);
@@ -477,7 +483,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);
@@ -568,7 +574,10 @@ name_is_separator (name)
      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,21 +593,27 @@ 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 */
+/* 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: (char *)key
 {
   char *tpos = key;
-  keyEquivModMask = 0;
-  /* currently we just parse 'super' combinations;
-     later we'll set keyEquivModMask */
+  keyEquivModMask = NSCommandKeyMask;
+
   if (!key || !strlen (key))
     return @"";
   
   while (*tpos == ' ' || *tpos == '(')
     tpos++;
-  if (*tpos != 's'/* || tpos[3] != ')'*/)
-    return @"";
+  if (*tpos != 's') {
+    keyEquivModMask = 0; /* signal */
+    return [NSString stringWithUTF8String: tpos];
+  }
   return [NSString stringWithFormat: @"%c", tpos[2]];
 }
 
@@ -621,12 +636,13 @@ name_is_separator (name)
         title = @"< ? >";  /* (get out in the open so we know about it) */
 
       keyEq = [self parseKeyEquiv: wv->key];
+      if (keyEquivModMask == 0)
+        title = [title stringByAppendingFormat: @" (%@)", keyEq];
 
       item = [self addItemWithTitle: (NSString *)title
                              action: @selector (menuDown:)
                       keyEquivalent: keyEq];
-      if (keyEquivModMask)
-        [item setKeyEquivalentModifierMask: keyEquivModMask];
+      [item setKeyEquivalentModifierMask: keyEquivModMask];
 
       [item setEnabled: wv->enabled];
 
@@ -676,7 +692,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];
@@ -1473,6 +1489,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)
 {
@@ -1488,7 +1522,9 @@ ns_popup_dialog (Lisp_Object position, Lisp_Object contents, Lisp_Object header)
 
   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 +1540,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;
 }
 
@@ -1836,25 +1876,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];
-
-  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 (1);
+      [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 +1900,6 @@ void process_dialog (id window, Lisp_Object list)
 @end
 
 
-
 /* ==========================================================================
 
     Lisp definitions