emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Emacs-diffs] Changes to src/nsmenu.m


From: Adrian Robert
Subject: [Emacs-diffs] Changes to src/nsmenu.m
Date: Tue, 15 Jul 2008 18:16:07 +0000

CVSROOT:        /sources/emacs
Module name:    emacs
Changes by:     Adrian Robert <arobert> 08/07/15 18:15:19

Index: src/nsmenu.m
===================================================================
RCS file: src/nsmenu.m
diff -N src/nsmenu.m
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ src/nsmenu.m        15 Jul 2008 18:15:15 -0000      1.1
@@ -0,0 +1,1948 @@
+/* NeXT/Open/GNUstep and MacOSX Cocoa menu and toolbar module.
+   Copyright (C) 2007, 2008 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs; see the file COPYING.  If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  
+
+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"
+#include "lisp.h"
+#include "window.h"
+#include "buffer.h"
+#include "keymap.h"
+#include "coding.h"
+#include "commands.h"
+#include "blockinput.h"
+#include "nsterm.h"
+#include "termhooks.h"
+#include "keyboard.h"
+
+/* for profiling */
+#include <sys/timeb.h>
+#include <sys/types.h>
+
+#define MenuStagger 10.0
+
+#if 0
+int menu_trace_num = 0;
+#define NSTRACE(x)        fprintf (stderr, "%s:%d: [%d] " #x "\n",        \
+                                __FILE__, __LINE__, ++menu_trace_num)
+#else
+#define NSTRACE(x)
+#endif
+
+#if 0
+/* Include lisp -> C common menu parsing code */
+#define ENCODE_MENU_STRING(str) ENCODE_UTF_8 (str)
+#include "nsmenu_common.c"
+#endif
+
+extern struct widget_value;
+
+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 long context_menu_value;
+EmacsMenu *mainMenu, *svcsMenu;
+
+/* NOTE: toolbar implementation is at end,
+  following complete menu implementation. */
+
+
+/* ==========================================================================
+
+    Menu: Externally-called functions
+
+   ========================================================================== 
*/
+
+
+/*23: PENDING: 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. */
+void
+free_frame_menubar (struct frame *f)
+{
+  return;
+}
+
+
+/* --------------------------------------------------------------------------
+    Update menubar.  Three cases:
+    1) deep_p = 0, 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.
+   -------------------------------------------------------------------------- 
*/
+/*#define NSMENUPROFILE 1 */
+void
+ns_update_menubar (struct frame *f, int 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;
+  Lisp_Object items;
+  widget_value *wv, *first_wv, *prev_wv = 0;
+  int i;
+
+#ifdef NSMENUPROFILE
+  struct timeb tb;
+  long t;
+#endif
+
+  NSTRACE (set_frame_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;
+  pool = [[NSAutoreleasePool alloc] init];
+
+  /* Menu may have been created automatically; if so, discard it. */
+  if ([menu isKindOfClass: [EmacsMenu class]] == NO)
+    {
+      [menu release];
+      menu = nil;
+    }
+
+  if (menu == nil)
+    {
+      menu = [[EmacsMenu alloc] initWithTitle: @"Emacs"];
+      needsSet = YES;
+    }
+  else
+    {  /* close up anything on there */
+      id attMenu = [menu attachedMenu];
+      if (attMenu != nil)
+        [attMenu close];
+    }
+
+#ifdef 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
+
+  if (deep_p)
+    {
+      /* Fully parse one or more of the submenus. */
+      int n = 0;
+      int *submenu_start, *submenu_end;
+      int *submenu_top_level_items, *submenu_n_panes;
+      struct buffer *prev = current_buffer;
+      Lisp_Object buffer;
+      int 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
+                                 * sizeof (Lisp_Object));
+
+      /* lisp preliminaries */
+      buffer = XWINDOW (FRAME_SELECTED_WINDOW (f))->buffer;
+      specbind (Qinhibit_quit, Qt);
+      specbind (Qdebug_on_next_call, Qnil);
+      record_unwind_save_match_data ();
+      if (NILP (Voverriding_local_map_menu_flag))
+       {
+         specbind (Qoverriding_terminal_local_map, Qnil);
+         specbind (Qoverriding_local_map, Qnil);
+       }
+      set_buffer_internal_1 (XBUFFER (buffer));
+
+      /* PENDING: for some reason this is not needed in other terms,
+         but some menu updates call Info-extract-pointer which causes
+         abort-on-error if waiting-for-input.  Needs further investigation. */
+      owfi = waiting_for_input;
+      waiting_for_input = 0;
+
+      /* lucid hook and possible reset */
+      safe_run_hooks (Qactivate_menubar_hook);
+      if (! NILP (Vlucid_menu_bar_dirty_flag))
+       call0 (Qrecompute_lucid_menubar);
+      safe_run_hooks (Qmenu_bar_update_hook);
+      FRAME_MENU_BAR_ITEMS (f) = menu_bar_items (FRAME_MENU_BAR_ITEMS (f));
+
+      /* Now ready to go */
+      items = FRAME_MENU_BAR_ITEMS (f);
+
+      /* 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));
+
+      /* 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_top_level_items
+       = (int *) alloca (XVECTOR (items)->size * sizeof (int *));
+      init_menu_items ();
+      for (i = 0; i < XVECTOR (items)->size; i += 4)
+       {
+         Lisp_Object key, string, maps;
+
+         key = XVECTOR (items)->contents[i];
+         string = XVECTOR (items)->contents[i + 1];
+         maps = XVECTOR (items)->contents[i + 2];
+         if (NILP (string))
+           break;
+
+          /* PENDING: 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, SDATA (string)))
+             continue; */
+
+         submenu_start[i] = menu_items_used;
+
+         menu_items_n_panes = 0;
+         submenu_top_level_items[i] = parse_single_submenu (key, string, maps);
+         submenu_n_panes[i] = menu_items_n_panes;
+         submenu_end[i] = menu_items_used;
+          n++;
+       }
+
+      finish_menu_items ();
+      waiting_for_input = owfi;
+
+
+      if (submenu && n == 0)
+        {
+          /* should have found a menu for this one but didn't */
+          fprintf (stderr, "ERROR: did not find lisp menu for submenu '%s'.\n",
+                  submenuTitle);
+         discard_menu_items ();
+         unbind_to (specpdl_count, Qnil);
+          [pool release];
+          UNBLOCK_INPUT;
+         return;
+        }
+
+      /* 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->button_type = BUTTON_TYPE_NONE;
+      wv->help = Qnil;
+      first_wv = wv;
+
+      for (i = 0; i < 4*n; i += 4)
+       {
+         menu_items_n_panes = submenu_n_panes[i];
+         wv = digest_single_submenu (submenu_start[i], submenu_end[i],
+                                     submenu_top_level_items[i]);
+         if (prev_wv)
+           prev_wv->next = wv;
+         else
+           first_wv->contents = wv;
+         /* Don't set wv->name here; GC during the loop might relocate it.  */
+         wv->enabled = 1;
+         wv->button_type = BUTTON_TYPE_NONE;
+         prev_wv = wv;
+       }
+
+      set_buffer_internal_1 (prev);
+
+      /* Compare the new menu items with previous, and leave off if no change 
*/
+      /* PENDING: following other terms here, but seems like this should be
+         done before parse stage 2 above, since its results aren't used */
+      if (previous_menu_items_used
+          && (!submenu || (submenu && submenu == last_submenu))
+          && menu_items_used == previous_menu_items_used)
+        {
+          for (i = 0; i < previous_menu_items_used; i++)
+            /* PENDING: 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 (!(STRINGP (previous_items[i])
+                    && STRINGP (XVECTOR (menu_items)->contents[i])
+                    && !strcmp (SDATA (previous_items[i]),
+                               SDATA (XVECTOR (menu_items)->contents[i]))))
+                  break;
+          if (i == previous_menu_items_used)
+            {
+              /* No change.. */
+
+#ifdef NSMENUPROFILE
+              ftime (&tb);
+              t += 1000*tb.time+tb.millitm;
+              fprintf (stderr, "NO CHANGE!  CUTTING OUT after %ld msec.\n", t);
+#endif
+
+              free_menubar_widget_value_tree (first_wv);
+              discard_menu_items ();
+              unbind_to (specpdl_count, Qnil);
+              [pool release];
+              UNBLOCK_INPUT;
+              return;
+            }
+        }
+      /* The menu items are different, so store them in the frame */
+      /* PENDING: this is not correct for single-submenu case */
+      f->menu_bar_vector = menu_items;
+      f->menu_bar_items_used = menu_items_used;
+
+      /* Calls restore_menu_items, etc., as they were outside */
+      unbind_to (specpdl_count, Qnil);
+
+      /* 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)
+       {
+         Lisp_Object string;
+         string = XVECTOR (items)->contents[i + 1];
+         if (NILP (string))
+           break;
+/*           if (submenu && strcmp (submenuTitle, SDATA (string)))
+               continue; */
+
+         wv->name = (char *) SDATA (string);
+          update_submenu_strings (wv->contents);
+         wv = wv->next;
+       }
+
+      /* Now, update the NS menu; if we have a submenu, use that, otherwise
+         create a new menu for each sub and fill it. */
+      if (submenu)
+        {
+          for (wv = first_wv->contents; wv; wv = wv->next)
+            {
+              if (!strcmp (submenuTitle, wv->name))
+                {
+                  [submenu fillWithWidgetValue: wv->contents];
+                  last_submenu = submenu;
+                  break;
+                }
+            }
+        }
+      else
+        {
+          [menu fillWithWidgetValue: first_wv->contents];
+        }
+
+    }
+  else
+    {
+      static int n_previous_strings = 0;
+      static char previous_strings[100][10];
+      static struct frame *last_f = NULL;
+      int n;
+      Lisp_Object string;
+
+      /* Make widget-value tree w/ just the top level menu bar strings */
+      items = FRAME_MENU_BAR_ITEMS (f);
+      if (NILP (items))
+        {
+          [pool release];
+          UNBLOCK_INPUT;
+          return;
+        }
+
+
+      /* check if no change.. this mechanism is a bit rough, but ready */
+      n = XVECTOR (items)->size / 4;
+      if (f == last_f && n_previous_strings == n)
+        {
+          for (i = 0; i<n; i++)
+            {
+              string = XVECTOR (items)->contents[4*i+1];
+
+              if (!string)
+                continue;
+              if (NILP (string))
+                if (previous_strings[i][0])
+                  break;
+              else
+                continue;
+              if (strncmp (previous_strings[i], SDATA (string), 10))
+                break;
+            }
+
+          if (i == n)
+            {
+              [pool release];
+              UNBLOCK_INPUT;
+              return;
+            }
+        }
+
+      [menu clear];
+      for (i = 0; i < XVECTOR (items)->size; i += 4)
+       {
+         string = XVECTOR (items)->contents[i + 1];
+         if (NILP (string))
+           break;
+
+          if (n < 100)
+            strncpy (previous_strings[i/4], SDATA (string), 10);
+
+         wv = xmalloc_widget_value ();
+         wv->name = (char *) SDATA (string);
+         wv->value = 0;
+         wv->enabled = 1;
+         wv->button_type = BUTTON_TYPE_NONE;
+         wv->help = Qnil;
+         wv->call_data = (void *) (EMACS_INT) (-1);
+
+#ifdef NS_IMPL_COCOA
+          /* we'll update the real copy under app menu when time comes */
+          if (!strcmp ("Services", wv->name))
+            {
+              /* but we need to make sure it will update on demand */
+              [svcsMenu setFrame: f];
+              [svcsMenu setDelegate: svcsMenu];
+            }
+          else
+#endif
+          [menu addSubmenuWithTitle: wv->name forFrame: f];
+
+         if (prev_wv)
+           prev_wv->next = wv;
+         else
+           first_wv->contents = wv;
+         prev_wv = wv;
+       }
+
+      last_f = f;
+      if (n < 100)
+        n_previous_strings = n;
+      else
+        n_previous_strings = 0;
+
+    }
+  free_menubar_widget_value_tree (first_wv);
+
+
+#ifdef NSMENUPROFILE
+  ftime (&tb);
+  t += 1000*tb.time+tb.millitm;
+  fprintf (stderr, "Menu update took %ld msec.\n", t);
+#endif
+
+  /* set main menu */
+  if (needsSet)
+    [NSApp setMainMenu: menu];
+
+  [pool release];
+  UNBLOCK_INPUT;
+
+}
+
+
+/* Main emacs core entry point for menubar menus: called to indicate that the
+   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)
+{
+  ns_update_menubar (f, deep_p, nil);
+}
+
+
+/* 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
+
+   ========================================================================== 
*/
+
+
+/* Menu that can define itself from Emacs "widget_value"s and will lazily
+   update itself when user clicked.  Based on Carbon/AppKit implementation
+   by Yamamoto Mitsuharu. */
address@hidden EmacsMenu
+
+/* override designated initializer */
+- initWithTitle: (NSString *)title
+{
+  if (self = [super initWithTitle: title])
+    [self setAutoenablesItems: NO];
+  return self;
+}
+
+
+/* used for top-level */
+- initWithTitle: (NSString *)title frame: (struct frame *)f
+{
+  [self initWithTitle: title];
+  frame = f;
+#ifdef NS_IMPL_COCOA
+  [self setDelegate: self];
+#endif
+  return self;
+}
+
+
+- (void)setFrame: (struct frame *)f
+{
+  frame = f;
+}
+
+
+/* delegate method called when a submenu is being opened: run a 'deep' call
+   to set_frame_menubar */
+- (void)menuNeedsUpdate: (NSMenu *)menu
+{
+  NSEvent *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)
+    return;
+/*fprintf (stderr, "Updating menu '%s'\n", [[self title] UTF8String]); NSLog 
(@"address@hidden", event); */
+  ns_update_menubar (frame, 1, self);
+}
+
+
+- (BOOL)performKeyEquivalent: (NSEvent *)theEvent
+{
+  if (SELECTED_FRAME () && FRAME_NS_P (SELECTED_FRAME ())
+      && FRAME_NS_VIEW (SELECTED_FRAME ()))
+    [FRAME_NS_VIEW (SELECTED_FRAME ()) keyDown: theEvent];
+  return YES;
+}
+
+
+/* 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
+{
+  char *tpos = key;
+  keyEquivModMask = 0;
+  /* currently we just parse 'super' combinations;
+     later we'll set keyEquivModMask */
+  if (!key || !strlen (key))
+    return @"";
+  
+  while (*tpos == ' ' || *tpos == '(')
+    tpos++;
+  if (*tpos != 's'/* || tpos[3] != ')'*/)
+    return @"";
+  return [NSString stringWithFormat: @"%c", tpos[2]];
+}
+
+- (id <NSMenuItem>)addItemWithWidgetValue: (void *)wvptr
+{
+  id <NSMenuItem> item;
+  widget_value *wv = (widget_value *)wvptr;
+
+  if (name_is_separator (wv->name))
+    {
+      item = [NSMenuItem separatorItem];
+      [self addItem: item];
+    }
+  else
+    {
+      NSString *title, *keyEq;
+      title = [NSString stringWithUTF8String: wv->name];
+      if (title == nil)
+        title = @"< ? >";  /* (get out in the open so we know about it) */
+
+      keyEq = [self parseKeyEquiv: wv->key];
+
+      item = [self addItemWithTitle: (NSString *)title
+                             action: @selector (menuDown:)
+                      keyEquivalent: keyEq];
+      if (keyEquivModMask)
+        [item setKeyEquivalentModifierMask: keyEquivModMask];
+
+      [item setEnabled: wv->enabled];
+
+      /* Draw radio buttons and tickboxes */
+      if (wv->selected && (wv->button_type == BUTTON_TYPE_TOGGLE ||
+                           wv->button_type == BUTTON_TYPE_RADIO))
+        [item setState: NSOnState];
+      else
+        [item setState: NSOffState];
+
+      [item setTag: (int)wv->call_data];
+    }
+
+  return item;
+}
+
+
+/* convenience */
+-(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])
+          && ![item isSeparatorItem])
+        continue;
+      [self removeItemAtIndex: n];
+    }
+}
+
+
+- (void)fillWithWidgetValue: (void *)wvptr
+{
+  widget_value *wv = (widget_value *)wvptr;
+
+  /* clear existing contents */
+  [self setMenuChangedMessagesEnabled: NO];
+  [self clear];
+
+  /* add new contents */
+  for (; wv != NULL; wv = wv->next)
+    {
+      id <NSMenuItem> item = [self addItemWithWidgetValue: wv];
+
+      if (wv->contents)
+        {
+          EmacsMenu *submenu = [[EmacsMenu alloc] initWithTitle: @"Submenu"];
+
+          [self setSubmenu: submenu forItem: item];
+          [submenu fillWithWidgetValue: wv->contents];
+          [submenu release];
+          [item setAction: nil];
+        }
+    }
+
+  [self setMenuChangedMessagesEnabled: YES];
+#ifdef NS_IMPL_GNUSTEP
+  if ([[self window] isVisible])
+    [self sizeToFit];
+#else
+  if ([self supermenu] == nil)
+    [self sizeToFit];
+#endif
+}
+
+
+/* adds an empty submenu and returns it */
+- (EmacsMenu *)addSubmenuWithTitle: (char *)title forFrame: (struct frame *)f
+{
+  NSString *titleStr = [NSString stringWithUTF8String: title];
+  id <NSMenuItem> item =
+    [self addItemWithTitle: titleStr
+                    action: nil /address@hidden (menuDown:) */
+             keyEquivalent: @""];
+  EmacsMenu *submenu = [[EmacsMenu alloc] initWithTitle: titleStr frame: f];
+  [self setSubmenu: submenu forItem: item];
+  [submenu release];
+  return submenu;
+}
+
+/* run a menu in popup mode */
+- (Lisp_Object)runMenuAt: (NSPoint)p forFrame: (struct frame *)f
+                 keymaps: (int)keymaps
+{
+  EmacsView *view = FRAME_NS_VIEW (f);
+/*   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;
+
+  context_menu_value = -1;
+  [NSMenu popUpContextMenu: self withEvent: event forView: view];
+  retVal = context_menu_value;
+  context_menu_value = 0;
+  return retVal > 0 ?
+    find_and_return_menu_selection (f, keymaps, (void *)retVal) : Qnil;
+}
+
address@hidden  /* EmacsMenu */
+
+
+
+/* ==========================================================================
+
+    Context Menu: implementing functions
+
+   ========================================================================== 
*/
+
+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)
+{
+  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;
+  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 */
+      /* PENDING: 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;
+
+  /* now parse stage 2 as in ns_update_menubar */
+  wv = xmalloc_widget_value ();
+  wv->name = "contextmenu";
+  wv->value = 0;
+  wv->enabled = 1;
+  wv->button_type = BUTTON_TYPE_NONE;
+  wv->help = Qnil;
+  first_wv = wv;
+
+  specpdl_count2 = SPECPDL_INDEX ();
+
+#if 0
+  /*PENDING: a couple of one-line differences prevent reuse */
+  wv = digest_single_submenu (0, menu_items_used, Qnil);
+#else
+  {
+  widget_value *save_wv = 0, *prev_wv = 0;
+  widget_value **submenu_stack
+    = (widget_value **) alloca (menu_items_used * sizeof (widget_value *));
+/*   Lisp_Object *subprefix_stack
+       = (Lisp_Object *) alloca (menu_items_used * sizeof (Lisp_Object)); */
+  int submenu_depth = 0;
+  int first_pane = 1;
+  int i;
+
+  /* Loop over all panes and items, filling in the tree.  */
+  i = 0;
+  while (i < menu_items_used)
+    {
+      if (EQ (XVECTOR (menu_items)->contents[i], Qnil))
+       {
+         submenu_stack[submenu_depth++] = save_wv;
+         save_wv = prev_wv;
+         prev_wv = 0;
+         first_pane = 1;
+         i++;
+       }
+      else if (EQ (XVECTOR (menu_items)->contents[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)
+              && 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))
+       i += 1;
+      else if (EQ (XVECTOR (menu_items)->contents[i], Qt))
+       {
+         /* Create a new pane.  */
+         Lisp_Object pane_name, prefix;
+         char *pane_string;
+
+         pane_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME);
+         prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
+
+#ifndef HAVE_MULTILINGUAL_MENU
+         if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name))
+           {
+             pane_name = ENCODE_MENU_STRING (pane_name);
+             ASET (menu_items, i + MENU_ITEMS_PANE_NAME, pane_name);
+           }
+#endif
+         pane_string = (NILP (pane_name)
+                        ? "" : (char *) SDATA (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)
+           pane_string = "";
+
+         /* If the pane has a meaningful name,
+            make the pane a top-level menu item
+            with its items as a submenu beneath it.  */
+         if (!keymaps && strcmp (pane_string, ""))
+           {
+             wv = xmalloc_widget_value ();
+             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;
+           }
+         else if (first_pane)
+           {
+             save_wv = wv;
+             prev_wv = 0;
+           }
+         first_pane = 0;
+         i += MENU_ITEMS_PANE_LENGTH;
+       }
+      else
+       {
+         /* Create a new item within current pane.  */
+         Lisp_Object item_name, enable, descrip, def, type, selected, help;
+         item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
+         enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
+         descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY);
+         def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION);
+         type = AREF (menu_items, i + MENU_ITEMS_ITEM_TYPE);
+         selected = AREF (menu_items, i + MENU_ITEMS_ITEM_SELECTED);
+         help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP);
+
+#ifndef HAVE_MULTILINGUAL_MENU
+          if (STRINGP (item_name) && STRING_MULTIBYTE (item_name))
+           {
+             item_name = ENCODE_MENU_STRING (item_name);
+             ASET (menu_items, i + MENU_ITEMS_ITEM_NAME, item_name);
+           }
+
+          if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
+           {
+             descrip = ENCODE_MENU_STRING (descrip);
+             ASET (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY, descrip);
+           }
+#endif /* not HAVE_MULTILINGUAL_MENU */
+
+         wv = xmalloc_widget_value ();
+         if (prev_wv)
+           prev_wv->next = wv;
+         else
+           save_wv->contents = wv;
+         wv->name = (char *) SDATA (item_name);
+         if (!NILP (descrip))
+           wv->key = (char *) SDATA (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;
+         wv->enabled = !NILP (enable);
+
+         if (NILP (type))
+           wv->button_type = BUTTON_TYPE_NONE;
+         else if (EQ (type, QCtoggle))
+           wv->button_type = BUTTON_TYPE_TOGGLE;
+         else if (EQ (type, QCradio))
+           wv->button_type = BUTTON_TYPE_RADIO;
+         else
+           abort ();
+
+         wv->selected = !NILP (selected);
+
+          if (! STRINGP (help))
+           help = Qnil;
+
+         wv->help = help;
+
+         prev_wv = wv;
+
+         i += MENU_ITEMS_ITEM_LENGTH;
+       }
+    }
+  }
+#endif
+
+  if (!NILP (title))
+    {
+      widget_value *wv_title = xmalloc_widget_value ();
+      widget_value *wv_sep = xmalloc_widget_value ();
+
+      /* 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 = (char *) SDATA (title);
+      wv_title->enabled = NULL;
+      wv_title->button_type = BUTTON_TYPE_NONE;
+      wv_title->help = Qnil;
+      wv_title->next = wv_sep;
+      first_wv->contents = wv_title;
+    }
+
+  pmenu = [[EmacsMenu alloc] initWithTitle:
+                               [NSString stringWithUTF8String: SDATA (title)]];
+  [pmenu fillWithWidgetValue: first_wv->contents];
+  free_menubar_widget_value_tree (first_wv);
+  unbind_to (specpdl_count2, Qnil);
+
+  tem = [pmenu runMenuAt: p forFrame: f keymaps: keymaps];
+  [[FRAME_NS_VIEW (SELECTED_FRAME ()) window] makeKeyWindow];
+
+  UNBLOCK_INPUT;
+  unbind_to (specpdl_count, Qnil);
+  UNGCPRO;
+
+  if (error_name) error (error_name);
+  return tem;
+}
+
+
+
+
+/* ==========================================================================
+
+    Toolbar: externally-called functions
+
+   ========================================================================== 
*/
+
+void
+free_frame_tool_bar (FRAME_PTR f)
+/* --------------------------------------------------------------------------
+    Under NS we just hide the toolbar until it might be needed again.
+   -------------------------------------------------------------------------- 
*/
+{
+  [[FRAME_NS_VIEW (f) toolbar] setVisible: NO];
+}
+
+void
+update_frame_tool_bar (FRAME_PTR f)
+/* --------------------------------------------------------------------------
+    Update toolbar contents
+   -------------------------------------------------------------------------- 
*/
+{
+  int i;
+  EmacsToolbar *toolbar = [FRAME_NS_VIEW (f) toolbar];
+
+  if (NILP (f->tool_bar_lines) || !INTEGERP (f->tool_bar_lines))
+    return;
+
+  [toolbar clearActive];
+
+  /* update EmacsToolbar as in GtkUtils, build items list */
+  for (i = 0; i < f->n_tool_bar_items; ++i)
+    {
+#define TOOLPROP(IDX) AREF (f->tool_bar_items, \
+                            i * TOOL_BAR_ITEM_NSLOTS + (IDX))
+
+      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;
+      struct image *img;
+      Lisp_Object image;
+      Lisp_Object helpObj;
+      char *helpText;
+
+      /* If image is a vector, choose the image according to the
+        button state.  */
+      image = TOOLPROP (TOOL_BAR_ITEM_IMAGES);
+      if (VECTORP (image))
+       {
+          /* NS toolbar auto-computes disabled and selected images */
+          idx = TOOL_BAR_IMAGE_ENABLED_SELECTED;
+         xassert (ASIZE (image) >= idx);
+         image = AREF (image, idx);
+       }
+      else
+        {
+          idx = -1;
+        }
+      /* Ignore invalid image specifications.  */
+      if (!valid_image_p (image))
+        {
+          NSLog (@"Invalid image for toolbar item");
+          continue;
+        }
+
+      img_id = lookup_image (f, image);
+      img = IMAGE_FROM_ID (f, img_id);
+      prepare_image_for_display (f, img);
+
+      if (img->load_failed_p || img->pixmap == nil)
+        {
+          NSLog (@"Could not prepare toolbar image for display.");
+          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
+    }
+
+  if (![toolbar isVisible])
+      [toolbar setVisible: YES];
+
+  if ([toolbar changed])
+    {
+      /* inform app that toolbar has changed */
+      NSDictionary *dict = [toolbar configurationDictionary];
+      NSMutableDictionary *newDict = [dict mutableCopy];
+      NSEnumerator *keys = [[dict allKeys] objectEnumerator];
+      NSObject *key;
+      while ((key = [keys nextObject]) != nil)
+        {
+          NSObject *val = [dict objectForKey: key];
+          if ([val isKindOfClass: [NSArray class]])
+            {
+              [newDict setObject:
+                         [toolbar toolbarDefaultItemIdentifiers: toolbar]
+                          forKey: key];
+              break;
+            }
+        }
+      [toolbar setConfigurationFromDictionary: newDict];
+      [newDict release];
+    }
+
+}
+
+
+/* ==========================================================================
+
+    Toolbar: class implementation
+
+   ========================================================================== 
*/
+
address@hidden EmacsToolbar
+
+- initForView: (EmacsView *)view withIdentifier: (NSString *)identifier
+{
+  self = [super initWithIdentifier: identifier];
+  emacsView = view;
+  [self setDisplayMode: NSToolbarDisplayModeIconOnly];
+  [self setSizeMode: NSToolbarSizeModeSmall];
+  [self setDelegate: self];
+  identifierToItem = [[NSMutableDictionary alloc] initWithCapacity: 10];
+  activeIdentifiers = [[NSMutableArray alloc] initWithCapacity: 8];
+  prevEnablement = enablement = 0L;
+  return self;
+}
+
+- (void)dealloc
+{
+  [prevIdentifiers release];
+  [activeIdentifiers release];
+  [identifierToItem release];
+  [super dealloc];
+}
+
+- (void) clearActive
+{
+  [prevIdentifiers release];
+  prevIdentifiers = [activeIdentifiers copy];
+  [activeIdentifiers removeAllObjects];
+  prevEnablement = enablement;
+  enablement = 0L;
+}
+
+- (BOOL) changed
+{
+  return [activeIdentifiers isEqualToArray: prevIdentifiers] &&
+    enablement == prevEnablement ? NO : YES;
+}
+
+- (void) addDisplayItemWithImage: (EmacsImage *)img idx: (int)idx
+                        helpText: (char *)help enabled: (BOOL)enabled
+{
+  /* 1) come up w/identifier */
+  NSString *identifier =
+    [NSString stringWithFormat: @"%u", [img hash]];
+
+  /* 2) create / reuse item */
+  NSToolbarItem *item = [identifierToItem objectForKey: identifier];
+  if (item == nil)
+    {
+      item = [[[NSToolbarItem alloc] initWithItemIdentifier: identifier]
+               autorelease];
+      [item setImage: img];
+      [item setToolTip: [NSString stringWithCString: help]];
+      [item setTarget: emacsView];
+      [item setAction: @selector (toolbarClicked:)];
+    }
+
+  [item setTag: idx];
+  [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 { }
+
+
+/* delegate methods */
+
+- (NSToolbarItem *)toolbar: (NSToolbar *)toolbar
+      itemForItemIdentifier: (NSString *)itemIdentifier
+  willBeInsertedIntoToolbar: (BOOL)flag
+{
+  /* look up NSToolbarItem by identifier and return... */
+  return [identifierToItem objectForKey: itemIdentifier];
+}
+
+- (NSArray *)toolbarDefaultItemIdentifiers: (NSToolbar *)toolbar
+{
+  /* return entire set.. */
+  return activeIdentifiers;
+}
+
+/* for configuration palette (not yet supported) */
+- (NSArray *)toolbarAllowedItemIdentifiers: (NSToolbar *)toolbar
+{
+  /* return entire set... */
+  return [identifierToItem allKeys];
+}
+
+/* optional and unneeded */
+/* - toolbarWillAddItem: (NSNotification *)notification { } */
+/* - toolbarDidRemoveItem: (NSNotification *)notification { } */
+/* - (NSArray *)toolbarSelectableItemIdentifiers: (NSToolbar *)toolbar */
+
address@hidden  /* EmacsToolbar */
+
+
+
+/* ==========================================================================
+
+    Tooltip: class implementation
+
+   ========================================================================== 
*/
+
+/* Needed because NeXTstep does not provide enough control over tooltip
+   display. */
address@hidden EmacsTooltip
+
+- init
+{
+  NSColor *col = [NSColor colorWithCalibratedRed: 1.0 green: 1.0
+                                            blue: 0.792 alpha: 0.95];
+  NSFont *font = [NSFont toolTipsFontOfSize: 0];
+  NSFont *sfont = [font screenFont];
+  int height = [sfont ascender] - [sfont descender];
+/*[font boundingRectForFont].size.height; */
+  NSRect r = NSMakeRect (0, 0, 100, height+6);
+
+  textField = [[NSTextField alloc] initWithFrame: r];
+  [textField setFont: font];
+  [textField setBackgroundColor: col];
+
+  [textField setEditable: NO];
+  [textField setSelectable: NO];
+  [textField setBordered: YES];
+  [textField setBezeled: YES];
+  [textField setDrawsBackground: YES];
+
+  win = [[NSWindow alloc]
+            initWithContentRect: [textField frame]
+                      styleMask: 0
+                        backing: NSBackingStoreBuffered
+                          defer: YES];
+  [win setReleasedWhenClosed: NO];
+  [win setDelegate: self];
+  [[win contentView] addSubview: textField];
+/*  [win setBackgroundColor: col]; */
+  [win setOpaque: NO];
+
+  return self;
+}
+
+- (void) dealloc
+{
+  [win close];
+  [win release];
+  [textField release];
+  [super dealloc];
+}
+
+- (void) setText: (char *)text
+{
+  NSString *str = [NSString stringWithUTF8String: text];
+  NSRect r = [textField frame];
+  r.size.width = [[[textField font] screenFont] widthOfString: str] + 8;
+  [textField setFrame: r];
+  [textField setStringValue: str];
+}
+
+- (void) showAtX: (int)x Y: (int)y for: (int)seconds
+{
+  NSRect wr = [win frame];
+
+  wr.origin = NSMakePoint (x, y);
+  wr.size = [textField frame].size;
+
+  [win setFrame: wr display: YES];
+  [win orderFront: self];
+  [win display];
+  timer = [NSTimer scheduledTimerWithTimeInterval: (float)seconds target: self
+                                         selector: @selector (hide)
+                                         userInfo: nil repeats: NO];
+  [timer retain];
+}
+
+- (void) hide
+{
+  [win close];
+  if (timer != nil)
+    {
+      if ([timer isValid])
+        [timer invalidate];
+      [timer release];
+      timer = nil;
+    }
+}
+
+- (BOOL) isActive
+{
+  return timer != nil;
+}
+
+- (NSRect) frame
+{
+  return [textField frame];
+}
+
address@hidden  /* EmacsTooltip */
+
+
+
+/* ==========================================================================
+
+    Popup Dialog: implementing functions
+
+   ========================================================================== 
*/
+
+Lisp_Object
+ns_popup_dialog (Lisp_Object position, Lisp_Object contents, Lisp_Object 
header)
+{
+  id dialog;
+  Lisp_Object window, tem;
+  struct frame *f;
+  NSPoint p;
+  BOOL isQ;
+
+  NSTRACE (x-popup-dialog);
+  
+  check_ns ();
+
+  isQ = NILP (header);
+
+  if (EQ (position, Qt))
+    {
+      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 (FRAMEP (position))
+    {
+      window = position;
+    }
+  else
+    {
+      CHECK_LIVE_WINDOW (position);
+      window = position;
+    }
+  
+  if (FRAMEP (window))
+    f = XFRAME (window);
+  else
+    {
+      CHECK_LIVE_WINDOW (window);
+      f = XFRAME (WINDOW_FRAME (XWINDOW (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;
+  dialog = [[EmacsDialogPanel alloc] initFromContents: contents
+                                           isQuestion: isQ];
+
+  tem = [dialog runDialogAt: p];
+
+  [dialog close];
+
+  [[FRAME_NS_VIEW (SELECTED_FRAME ()) window] makeKeyWindow];
+  return tem;
+}
+
+
+/* ==========================================================================
+
+    Popup Dialog: class implementation
+
+   ========================================================================== 
*/
+
address@hidden FlippedView : NSView
+{
+}
address@hidden
+
address@hidden FlippedView
+- (BOOL)isFlipped
+{
+  return YES;
+}
address@hidden
+
address@hidden EmacsDialogPanel
+
+#define SPACER         8.0
+#define ICONSIZE       64.0
+#define TEXTHEIGHT     20.0
+#define MINCELLWIDTH   90.0
+
+- initWithContentRect: (NSRect)contentRect styleMask: (unsigned int)aStyle
+              backing: (NSBackingStoreType)backingType defer: (BOOL)flag
+{
+  NSSize spacing = {SPACER, SPACER};
+  NSRect area;
+  char this_cmd_name[80];
+  id cell, tem;
+  static NSImageView *imgView;
+  static FlippedView *contentView;
+
+  if (imgView == nil)
+    {
+      NSImage *img;
+      area.origin.x   = 3*SPACER;
+      area.origin.y   = 2*SPACER;
+      area.size.width = ICONSIZE;
+      area.size.height= ICONSIZE;
+      img = [[NSImage imageNamed: @"NSApplicationIcon"] copy];
+      [img setScalesWhenResized: YES];
+      [img setSize: NSMakeSize (ICONSIZE, ICONSIZE)];
+      imgView = [[NSImageView alloc] initWithFrame: area];
+      [imgView setImage: img];
+      [imgView setEditable: NO];
+      [img release];
+    }
+
+  aStyle = NSTitledWindowMask;
+  flag = YES;
+  rows = 0;
+  cols = 1;
+  [super initWithContentRect: contentRect styleMask: aStyle
+                     backing: backingType defer: flag];
+  contentView = [[FlippedView alloc] initWithFrame: [[self contentView] 
frame]];
+  [self setContentView: contentView];
+
+  [[self contentView] setAutoresizesSubviews: YES];
+
+  [[self contentView] addSubview: imgView];
+  [self setTitle: @""];
+
+  area.origin.x   += ICONSIZE+2*SPACER;
+/*  area.origin.y   = TEXTHEIGHT; ICONSIZE/2-10+SPACER; */
+  area.size.width = 400;
+  area.size.height= TEXTHEIGHT;
+  command = [[[NSTextField alloc] initWithFrame: area] autorelease];
+  [[self contentView] addSubview: command];
+  [command setStringValue: @"Emacs"];
+  [command setDrawsBackground: NO];
+  [command setBezeled: NO];
+  [command setSelectable: NO];
+  [command setFont: [NSFont boldSystemFontOfSize: 13.0]];
+
+/*  area.origin.x   = ICONSIZE+2*SPACER;
+  area.origin.y   = TEXTHEIGHT + 2*SPACER;
+  area.size.width = 400;
+  area.size.height= 2;
+  tem = [[[NSBox alloc] initWithFrame: area] autorelease];
+  [[self contentView] addSubview: tem];
+  [tem setTitlePosition: NSNoTitle];
+  [tem setAutoresizingMask: NSViewWidthSizable];*/
+
+/*  area.origin.x = ICONSIZE+2*SPACER; */
+  area.origin.y += TEXTHEIGHT+SPACER;
+  area.size.width = 400;
+  area.size.height= TEXTHEIGHT;
+  title = [[[NSTextField alloc] initWithFrame: area] autorelease];
+  [[self contentView] addSubview: title];
+  [title setDrawsBackground: NO];
+  [title setBezeled: NO];
+  [title setSelectable: NO];
+  [title setFont: [NSFont systemFontOfSize: 11.0]];
+
+  cell = [[[NSButtonCell alloc] initTextCell: @""] autorelease];
+  [cell setBordered: NO];
+  [cell setEnabled: NO];
+  [cell setCellAttribute: NSCellIsInsetButton to: 8];
+  [cell setBezelStyle: NSRoundedBezelStyle];
+
+  matrix = [[NSMatrix alloc] initWithFrame: contentRect 
+                                      mode: NSHighlightModeMatrix 
+                                 prototype: cell 
+                              numberOfRows: 0 
+                           numberOfColumns: 1];
+  [[self contentView] addSubview: matrix];
+  [matrix release];
+  [matrix setFrameOrigin: NSMakePoint (area.origin.x,
+                                      area.origin.y + (TEXTHEIGHT+3*SPACER))];
+  [matrix setIntercellSpacing: spacing];
+
+  [self setOneShot: YES];
+  [self setReleasedWhenClosed: YES];
+  [self setHidesOnDeactivate: YES];
+  return self;
+}
+
+
+- (BOOL)windowShouldClose: (id)sender
+{
+  [NSApp stopModalWithCode: Qnil];
+  return NO;
+}
+
+
+void process_dialog (id window, Lisp_Object list)
+{
+  Lisp_Object item;
+  int row = 0;
+
+  for (; XTYPE (list) == Lisp_Cons; list = XCDR (list))
+    {
+      item = XCAR (list);
+      if (XTYPE (item) == Lisp_String)
+        {
+          [window addString: XSTRING (item)->data row: row++];
+        }
+      else if (XTYPE (item) == Lisp_Cons)
+        {
+          [window addButton: XSTRING (XCAR (item))->data
+                      value: XCDR (item) row: row++];
+        }
+      else if (NILP (item))
+        {
+          [window addSplit];
+          row = 0;
+        }
+    }
+}
+
+
+- addButton: (char *)str value: (Lisp_Object)val row: (int)row
+{
+  id cell;
+       
+  if (row >= rows)
+    {
+      [matrix addRow];
+      rows++;
+    }
+  cell = [matrix cellAtRow: row column: cols-1];
+  [cell setTarget: self];
+  [cell setAction: @selector (clicked: )];
+  [cell setTitle: [NSString stringWithUTF8String: str]];
+  [cell setTag: (int)val];
+  [cell setBordered: YES];
+  [cell setEnabled: YES];
+
+  return self;
+}
+
+
+- addString: (char *)str row: (int)row
+{
+  id cell;
+       
+  if (row >= rows)
+    {
+      [matrix addRow];
+      rows++;
+    }
+  cell = [matrix cellAtRow: row column: cols-1];
+  [cell setTitle: [NSString stringWithUTF8String: str]];
+  [cell setBordered: YES];
+  [cell setEnabled: NO];
+
+  return self;
+}
+
+
+- addSplit
+{
+  [matrix addColumn];
+  cols++;
+  return self;
+}
+
+
+- clicked: sender
+{
+  NSArray *sellist = nil;
+  Lisp_Object seltag;
+
+  sellist = [sender selectedCells];
+  if ([sellist count]<1) 
+    return self;
+
+  seltag = (Lisp_Object)[[sellist objectAtIndex: 0] tag];
+  if (! EQ (seltag, Qundefined))
+    [NSApp stopModalWithCode: seltag];
+  return self;
+}
+
+
+- initFromContents: (Lisp_Object)contents isQuestion: (BOOL)isQ
+{
+  Lisp_Object head;
+  [super init];
+
+  if (XTYPE (contents) == Lisp_Cons)
+    {
+      head = Fcar (contents);
+      process_dialog (self, Fcdr (contents));
+    }
+  else
+    head = contents;
+
+  if (XTYPE (head) == Lisp_String)
+      [title setStringValue:
+                 [NSString stringWithUTF8String: XSTRING (head)->data]];
+  else if (isQ == YES)
+      [title setStringValue: @"Question"];
+  else
+      [title setStringValue: @"Information"];
+
+  {
+    int i;
+    NSRect r, s, t;
+
+    if (cols == 1 && rows > 1) /* Never told where to split */
+      {
+        [matrix addColumn];
+        for (i = 0; i<rows/2; i++)
+          {
+            [matrix putCell: [matrix cellAtRow: (rows+1)/2 column: 0]
+                      atRow: i column: 1];
+            [matrix removeRow: (rows+1)/2];
+          }
+      }
+
+    [matrix sizeToFit];
+    {
+      NSSize csize = [matrix cellSize];
+      if (csize.width < MINCELLWIDTH)
+        {
+          csize.width = MINCELLWIDTH;
+          [matrix setCellSize: csize];
+          [matrix sizeToCells];
+        }
+    }
+
+    [title sizeToFit];
+    [command sizeToFit];
+
+    t = [matrix frame];
+    r = [title frame];
+    if (r.size.width+r.origin.x > t.size.width+t.origin.x)
+      {
+        t.origin.x   = r.origin.x;
+        t.size.width = r.size.width;
+      }
+    r = [command frame];
+    if (r.size.width+r.origin.x > t.size.width+t.origin.x)
+      {
+        t.origin.x   = r.origin.x;
+        t.size.width = r.size.width;
+      }
+
+    r = [self frame];
+    s = [(NSView *)[self contentView] frame];
+    r.size.width  += t.origin.x+t.size.width +2*SPACER-s.size.width;
+    r.size.height += t.origin.y+t.size.height+SPACER-s.size.height;
+    [self setFrame: r display: NO];
+  }
+
+  return self;
+}
+
+
+- (void)dealloc
+{
+  { [super dealloc]; return; };
+}
+
+
+- (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)
+    {
+    (e = [NSApp nextEventMatchingMask: NSAnyEventMask
+                            untilDate: [NSDate distantFuture]
+                               inMode: NSEventTrackingRunLoopMode
+                              dequeue: NO]);
+/*fprintf (stderr, "ret = %d\te = %p\n", ret, e); */
+    }
+  [NSApp endModalSession: session];
+
+  return (Lisp_Object)ret;
+}
+
address@hidden
+
+
+
+/* ==========================================================================
+
+    Lisp definitions
+
+   ========================================================================== 
*/
+
+DEFUN ("ns-reset-menu", Fns_reset_menu, Sns_reset_menu, 0, 0, 0,
+       "Cause the NS menu to be re-calculated.")
+     ()
+{
+  set_frame_menubar (SELECTED_FRAME (), 1, 0);
+  return Qnil;
+}
+
+
+DEFUN ("x-popup-menu", Fx_popup_menu, Sx_popup_menu, 2, 2, 0,
+       "Pop up a deck-of-cards menu and return user's selection.\n\
+POSITION is a position specification.  This is either a mouse button event\n\
+or a list ((XOFFSET YOFFSET) WINDOW)\n\
+where XOFFSET and YOFFSET are positions in pixels from the top left\n\
+corner of WINDOW's frame. (WINDOW may be a frame object instead of a 
window.)\n\
+This controls the position of the center of the first line\n\
+in the first pane of the menu, not the top left of the menu as a whole.\n\
+\n\
+MENU is a specifier for a menu.  For the simplest case, MENU is a keymap.\n\
+The menu items come from key bindings that have a menu string as well as\n\
+a definition; actually, the \"definition\" in such a key binding looks like\n\
+\(STRING . REAL-DEFINITION).  To give the menu a title, put a string into\n\
+the keymap as a top-level element.\n\n\
+You can also use a list of keymaps as MENU.\n\
+  Then each keymap makes a separate pane.\n\
+When MENU is a keymap or a list of keymaps, the return value\n\
+is a list of events.\n\n\
+Alternatively, you can specify a menu of multiple panes\n\
+  with a list of the form (TITLE PANE1 PANE2...),\n\
+where each pane is a list of form (TITLE ITEM1 ITEM2...).\n\
+Each ITEM is normally a cons cell (STRING . VALUE);\n\
+but a string can appear as an item--that makes a nonselectable line\n\
+in the menu.\n\
+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.
+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.  */)
+     (position, contents, header)
+     Lisp_Object position, contents, header;
+{
+  return ns_popup_dialog (position, contents, header);
+}
+
+
+/* ==========================================================================
+
+    Lisp interface declaration
+
+   ========================================================================== 
*/
+
+void
+syms_of_nsmenu ()
+{
+  defsubr (&Sx_popup_menu);
+  defsubr (&Sx_popup_dialog);
+  defsubr (&Sns_reset_menu);
+  staticpro (&menu_items);
+  menu_items = Qnil;
+
+  Qdebug_on_next_call = intern ("debug-on-next-call");
+  staticpro (&Qdebug_on_next_call);
+}




reply via email to

[Prev in Thread] Current Thread [Next in Thread]