* buffer.h (FETCH_MULTIBYTE_CHAR): Define as inline.
[bpt/emacs.git] / src / nsmenu.m
index 6a9ee7d..1f86417 100644 (file)
@@ -1,5 +1,5 @@
 /* NeXT/Open/GNUstep and MacOSX Cocoa menu and toolbar module.
-   Copyright (C) 2007-2011 Free Software Foundation, Inc.
+   Copyright (C) 2007-2012 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -28,6 +28,7 @@ Carbon version by Yamamoto Mitsuharu. */
 
 #include "lisp.h"
 #include "window.h"
+#include "character.h"
 #include "buffer.h"
 #include "keymap.h"
 #include "coding.h"
@@ -74,6 +75,10 @@ EmacsMenu *mainMenu, *svcsMenu, *dockMenu;
 static int popup_activated_flag;
 static NSModalSession popupSession;
 
+/* Nonzero means we are tracking and updating menus.  */
+static int trackingMenu;
+
+
 /* NOTE: toolbar implementation is at end,
   following complete menu implementation. */
 
@@ -182,7 +187,7 @@ ns_update_menubar (struct frame *f, int deep_p, EmacsMenu *submenu)
       int *submenu_top_level_items, *submenu_n_panes;
       struct buffer *prev = current_buffer;
       Lisp_Object buffer;
-      int specpdl_count = SPECPDL_INDEX ();
+      ptrdiff_t specpdl_count = SPECPDL_INDEX ();
       int previous_menu_items_used = f->menu_bar_items_used;
       Lisp_Object *previous_items
        = (Lisp_Object *) alloca (previous_menu_items_used
@@ -400,6 +405,7 @@ ns_update_menubar (struct frame *f, int deep_p, EmacsMenu *submenu)
       items = FRAME_MENU_BAR_ITEMS (f);
       if (NILP (items))
         {
+          free_menubar_widget_value_tree (first_wv);
           [pool release];
           UNBLOCK_INPUT;
           return;
@@ -427,6 +433,7 @@ ns_update_menubar (struct frame *f, int deep_p, EmacsMenu *submenu)
 
           if (i == n)
             {
+              free_menubar_widget_value_tree (first_wv);
               [pool release];
               UNBLOCK_INPUT;
               return;
@@ -449,7 +456,7 @@ ns_update_menubar (struct frame *f, int deep_p, EmacsMenu *submenu)
          wv->enabled = 1;
          wv->button_type = BUTTON_TYPE_NONE;
          wv->help = Qnil;
-         wv->call_data = (void *) (EMACS_INT) (-1);
+         wv->call_data = (void *) (intptr_t) (-1);
 
 #ifdef NS_IMPL_COCOA
           /* we'll update the real copy under app menu when time comes */
@@ -543,21 +550,44 @@ set_frame_menubar (struct frame *f, int first_time, int deep_p)
   frame = f;
 }
 
+#ifdef NS_IMPL_COCOA
+#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5
+extern NSString *NSMenuDidBeginTrackingNotification;
+#endif
+#endif
+
+#ifdef NS_IMPL_COCOA
+-(void)trackingNotification:(NSNotification *)notification
+{
+  /* Update menu in menuNeedsUpdate only while tracking menus.  */
+  trackingMenu = ([notification name] == NSMenuDidBeginTrackingNotification
+                  ? 1 : 0);
+}
+#endif
 
 /* delegate method called when a submenu is being opened: run a 'deep' call
    to set_frame_menubar */
 - (void)menuNeedsUpdate: (NSMenu *)menu
 {
-  NSEvent *event;
   if (!FRAME_LIVE_P (frame))
     return;
-  event = [[FRAME_NS_VIEW (frame) window] currentEvent];
-  /* HACK: Cocoa/Carbon will request update on every keystroke
+
+  /* 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
+     On Leopard, even keystroke events generate SystemDefined event.
+     Third-party applications that enhance mouse / trackpad
+     interaction, or also VNC/Remote Desktop will send events
+     of type AppDefined rather than SysDefined.
+     Menus will fail to show up if they haven't been initialized.
+     AppDefined events may lack timing data.
+
+     Thus, we rely on the didBeginTrackingNotification notification
+     as above to indicate the need for updates.
+     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)
@@ -763,7 +793,7 @@ ns_menu_show (FRAME_PTR f, int x, int y, int for_click, int keymaps,
   EmacsMenu *pmenu;
   NSPoint p;
   Lisp_Object window, tem, keymap;
-  int specpdl_count = SPECPDL_INDEX ();
+  ptrdiff_t specpdl_count = SPECPDL_INDEX ();
   widget_value *wv, *first_wv = 0;
 
   p.x = x; p.y = y;
@@ -1014,7 +1044,7 @@ update_frame_tool_bar (FRAME_PTR f)
       BOOL enabled_p = !NILP (TOOLPROP (TOOL_BAR_ITEM_ENABLED_P));
       BOOL selected_p = !NILP (TOOLPROP (TOOL_BAR_ITEM_SELECTED_P));
       int idx;
-      int img_id;
+      ptrdiff_t img_id;
       struct image *img;
       Lisp_Object image;
       Lisp_Object helpObj;
@@ -1228,8 +1258,8 @@ update_frame_tool_bar (FRAME_PTR f)
 
   [textField setEditable: NO];
   [textField setSelectable: NO];
-  [textField setBordered: YES];
-  [textField setBezeled: YES];
+  [textField setBordered: NO];
+  [textField setBezeled: NO];
   [textField setDrawsBackground: YES];
 
   win = [[NSWindow alloc]
@@ -1237,6 +1267,7 @@ update_frame_tool_bar (FRAME_PTR f)
                       styleMask: 0
                         backing: NSBackingStoreBuffered
                           defer: YES];
+  [win setHasShadow: YES];
   [win setReleasedWhenClosed: NO];
   [win setDelegate: self];
   [[win contentView] addSubview: textField];
@@ -1257,17 +1288,15 @@ update_frame_tool_bar (FRAME_PTR f)
 - (void) setText: (char *)text
 {
   NSString *str = [NSString stringWithUTF8String: text];
-  NSRect r = [textField frame];
-  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];
+  NSRect r  = [textField frame];
+  NSSize tooltipDims;
+
   [textField setStringValue: str];
+  tooltipDims = [[textField cell] cellSize];
+
+  r.size.width = tooltipDims.width;
+  r.size.height = tooltipDims.height;
+  [textField setFrame: r];
 }
 
 - (void) showAtX: (int)x Y: (int)y for: (int)seconds
@@ -1340,7 +1369,7 @@ Lisp_Object
 ns_popup_dialog (Lisp_Object position, Lisp_Object contents, Lisp_Object header)
 {
   id dialog;
-  Lisp_Object window, tem;
+  Lisp_Object window, tem, title;
   struct frame *f;
   NSPoint p;
   BOOL isQ;
@@ -1389,11 +1418,19 @@ ns_popup_dialog (Lisp_Object position, Lisp_Object contents, Lisp_Object header)
   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;
 
+  title = Fcar (contents);
+  CHECK_STRING (title);
+
+  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));
+
   BLOCK_INPUT;
   dialog = [[EmacsDialogPanel alloc] initFromContents: contents
                                            isQuestion: isQ];
   {
-    int specpdl_count = SPECPDL_INDEX ();
+    ptrdiff_t specpdl_count = SPECPDL_INDEX ();
     record_unwind_protect (pop_down_menu, make_save_value (dialog, 0));
     popup_activated_flag = 1;
     tem = [dialog runDialogAt: p];
@@ -1788,6 +1825,11 @@ DEFUN ("menu-or-popup-active-p", Fmenu_or_popup_active_p, Smenu_or_popup_active_
 void
 syms_of_nsmenu (void)
 {
+#ifndef NS_IMPL_COCOA
+  /* 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);