]> code.delx.au - gnu-emacs/blobdiff - src/macmenu.c
Add 2008 to copyright years.
[gnu-emacs] / src / macmenu.c
index 9b09bfb332714a6c882ea2ba4526371416930611..e65d3863207a794aeef49e131d851a2569bb6d3c 100644 (file)
@@ -1,11 +1,12 @@
-/* Menu support for GNU Emacs on the for Mac OS.
-   Copyright (C) 2000, 2001, 2002, 2003  Free Software Foundation, Inc.
+/* Menu support for GNU Emacs on Mac OS.
+   Copyright (C) 2000, 2001, 2002, 2003, 2004,
+                 2005, 2006, 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 2, or (at your option)
+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,
@@ -15,15 +16,15 @@ 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, Inc., 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA.  */
+the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA.  */
 
 /* Contributed by Andrew Choi (akochoi@mac.com).  */
 
 #include <config.h>
-#include <signal.h>
 
 #include <stdio.h>
+
 #include "lisp.h"
 #include "termhooks.h"
 #include "keyboard.h"
@@ -35,7 +36,7 @@ Boston, MA 02111-1307, USA.  */
 #include "charset.h"
 #include "coding.h"
 
-#ifndef MAC_OSX
+#if !TARGET_API_MAC_CARBON
 #include <MacTypes.h>
 #include <Menus.h>
 #include <QuickDraw.h>
@@ -47,7 +48,7 @@ Boston, MA 02111-1307, USA.  */
 #if defined (__MRC__) || (__MSL__ >= 0x6000)
 #include <ControlDefinitions.h>
 #endif
-#endif /* not MAC_OSX */
+#endif /* not TARGET_API_MAC_CARBON */
 
 /* This may include sys/types.h, and that somehow loses
    if this is not done before the other system files.  */
@@ -61,17 +62,26 @@ Boston, MA 02111-1307, USA.  */
 
 #include "dispextern.h"
 
-#define POPUP_SUBMENU_ID 235
-#define MIN_POPUP_SUBMENU_ID 512
-#define MIN_MENU_ID 256
-#define MIN_SUBMENU_ID 1
+enum mac_menu_kind {           /* Menu ID range  */
+  MAC_MENU_APPLE,              /* 0 (Reserved by Apple) */
+  MAC_MENU_MENU_BAR,           /* 1 .. 233       */
+  MAC_MENU_M_APPLE,            /* 234      (== M_APPLE) */
+  MAC_MENU_POPUP,              /* 235            */
+  MAC_MENU_DRIVER,             /* 236 .. 255 (Reserved) */
+  MAC_MENU_MENU_BAR_SUB,       /* 256 .. 16383   */
+  MAC_MENU_POPUP_SUB,          /* 16384 .. 32767 */
+  MAC_MENU_END                 /* 32768          */
+};
+
+static const int min_menu_id[] = {0, 1, 234, 235, 236, 256, 16384, 32768};
 
 #define DIALOG_WINDOW_RESOURCE 130
 
+#if TARGET_API_MAC_CARBON
 #define HAVE_DIALOGS 1
+#endif
 
 #undef HAVE_MULTILINGUAL_MENU
-#undef HAVE_DIALOGS /* TODO: Implement native dialogs.  */
 
 /******************************************************************/
 /* Definitions copied from lwlib.h */
@@ -90,10 +100,12 @@ enum button_type
 typedef struct _widget_value
 {
   /* name of widget */
+  Lisp_Object   lname;
   char*                name;
   /* value (meaning depend on widget type) */
   char*                value;
   /* keyboard equivalent. no implications for XtTranslations */
+  Lisp_Object   lkey;
   char*                key;
   /* Help string or nil if none.
      GC finds this string through the frame's menu_bar_vector
@@ -148,11 +160,11 @@ typedef struct _widget_value
 #define FALSE 0
 #endif /* no TRUE */
 
-Lisp_Object Vmenu_updating_frame;
-
 Lisp_Object Qdebug_on_next_call;
 
-extern Lisp_Object Qmenu_bar;
+extern Lisp_Object Vmenu_updating_frame;
+
+extern Lisp_Object Qmenu_bar, Qmac_apple_event;
 
 extern Lisp_Object QCtoggle, QCradio;
 
@@ -163,30 +175,34 @@ extern Lisp_Object Qoverriding_local_map, Qoverriding_terminal_local_map;
 
 extern Lisp_Object Qmenu_bar_update_hook;
 
+void set_frame_menubar P_ ((FRAME_PTR, int, int));
+
 #if TARGET_API_MAC_CARBON
 #define ENCODE_MENU_STRING(str) ENCODE_UTF_8 (str)
 #else
 #define ENCODE_MENU_STRING(str) ENCODE_SYSTEM (str)
 #endif
 
-void set_frame_menubar ();
-
 static void push_menu_item P_ ((Lisp_Object, Lisp_Object, Lisp_Object,
                                Lisp_Object, Lisp_Object, Lisp_Object,
                                Lisp_Object, Lisp_Object));
 #ifdef HAVE_DIALOGS
-static Lisp_Object mac_dialog_show ();
+static Lisp_Object mac_dialog_show P_ ((FRAME_PTR, int, Lisp_Object,
+                                       Lisp_Object, char **));
 #endif
-static Lisp_Object mac_menu_show ();
-
-static void keymap_panes ();
-static void single_keymap_panes ();
-static void single_menu_item ();
-static void list_of_panes ();
-static void list_of_items ();
-
-static void fill_submenu (MenuHandle, widget_value *);
-static void fill_menubar (widget_value *);
+static Lisp_Object mac_menu_show P_ ((struct frame *, int, int, int, int,
+                                     Lisp_Object, char **));
+static void keymap_panes P_ ((Lisp_Object *, int, int));
+static void single_keymap_panes P_ ((Lisp_Object, Lisp_Object, Lisp_Object,
+                                    int, int));
+static void list_of_panes P_ ((Lisp_Object));
+static void list_of_items P_ ((Lisp_Object));
+
+static void find_and_call_menu_selection P_ ((FRAME_PTR, int, Lisp_Object,
+                                             void *));
+static int fill_menu P_ ((MenuHandle, widget_value *, enum mac_menu_kind, int));
+static void fill_menubar P_ ((widget_value *, int));
+static void dispose_menus P_ ((enum mac_menu_kind, int));
 
 \f
 /* This holds a Lisp vector that holds the results of decoding
@@ -243,15 +259,9 @@ static int menu_items_n_panes;
 /* Current depth within submenus.  */
 static int menu_items_submenu_depth;
 
-/* Flag which when set indicates a dialog or menu has been posted by
-   Xt on behalf of one of the widget sets.  */
+/* Nonzero means a menu is currently active.  */
 static int popup_activated_flag;
 
-/* Index of the next submenu */
-static int submenu_id;
-
-static int next_menubar_widget_id;
-
 /* This is set nonzero after the user activates the menu bar, and set
    to zero again after the menu bars are redisplayed by prepare_menu_bar.
    While it is nonzero, all calls to set_frame_menubar go deep.
@@ -278,8 +288,7 @@ init_menu_items ()
   menu_items_submenu_depth = 0;
 }
 
-/* Call at the end of generating the data in menu_items.
-   This fills in the number of items in the last pane.  */
+/* Call at the end of generating the data in menu_items.  */
 
 static void
 finish_menu_items ()
@@ -301,6 +310,38 @@ discard_menu_items ()
     }
 }
 
+/* This undoes save_menu_items, and it is called by the specpdl unwind
+   mechanism.  */
+
+static Lisp_Object
+restore_menu_items (saved)
+     Lisp_Object saved;
+{
+  menu_items = XCAR (saved);
+  menu_items_allocated = (VECTORP (menu_items) ? ASIZE (menu_items) : 0);
+  saved = XCDR (saved);
+  menu_items_used = XINT (XCAR (saved));
+  saved = XCDR (saved);
+  menu_items_n_panes = XINT (XCAR (saved));
+  saved = XCDR (saved);
+  menu_items_submenu_depth = XINT (XCAR (saved));
+  return Qnil;
+}
+
+/* Push the whole state of menu_items processing onto the specpdl.
+   It will be restored when the specpdl is unwound.  */
+
+static void
+save_menu_items ()
+{
+  Lisp_Object saved = list4 (menu_items,
+                            make_number (menu_items_used),
+                            make_number (menu_items_n_panes),
+                            make_number (menu_items_submenu_depth));
+  record_unwind_protect (restore_menu_items, saved);
+  menu_items = Qnil;
+}
+\f
 /* Make the menu_items vector twice as large.  */
 
 static void
@@ -311,6 +352,7 @@ grow_menu_items ()
   old = menu_items;
 
   menu_items_allocated *= 2;
+
   menu_items = Fmake_vector (make_number (menu_items_allocated), Qnil);
   bcopy (XVECTOR (old)->contents, XVECTOR (menu_items)->contents,
         old_size * sizeof (Lisp_Object));
@@ -413,11 +455,21 @@ keymap_panes (keymaps, nmaps, notreal)
      P is the number of panes we have made so far.  */
   for (mapno = 0; mapno < nmaps; mapno++)
     single_keymap_panes (keymaps[mapno],
-                         Fkeymap_prompt (keymaps[mapno]), Qnil, notreal, 10);
+                        Fkeymap_prompt (keymaps[mapno]), Qnil, notreal, 10);
 
   finish_menu_items ();
 }
 
+/* Args passed between single_keymap_panes and single_menu_item.  */
+struct skp
+  {
+     Lisp_Object pending_maps;
+     int maxdepth, notreal;
+  };
+
+static void single_menu_item P_ ((Lisp_Object, Lisp_Object, Lisp_Object,
+                                 void *));
+
 /* This is a recursive subroutine of keymap_panes.
    It handles one keymap, KEYMAP.
    The other arguments are passed along
@@ -435,89 +487,71 @@ single_keymap_panes (keymap, pane_name, prefix, notreal, maxdepth)
      int notreal;
      int maxdepth;
 {
-  Lisp_Object pending_maps = Qnil;
-  Lisp_Object tail, item;
-  struct gcpro gcpro1, gcpro2;
+  struct skp skp;
+  struct gcpro gcpro1;
+
+  skp.pending_maps = Qnil;
+  skp.maxdepth = maxdepth;
+  skp.notreal = notreal;
 
   if (maxdepth <= 0)
     return;
 
   push_menu_pane (pane_name, prefix);
 
-  for (tail = keymap; CONSP (tail); tail = XCDR (tail))
-    {
-      GCPRO2 (keymap, pending_maps);
-      /* Look at each key binding, and if it is a menu item add it
-        to this menu.  */
-      item = XCAR (tail);
-      if (CONSP (item))
-       single_menu_item (XCAR (item), XCDR (item),
-                         &pending_maps, notreal, maxdepth);
-      else if (VECTORP (item))
-       {
-         /* Loop over the char values represented in the vector.  */
-         int len = XVECTOR (item)->size;
-         int c;
-         for (c = 0; c < len; c++)
-           {
-             Lisp_Object character;
-             XSETFASTINT (character, c);
-             single_menu_item (character, XVECTOR (item)->contents[c],
-                               &pending_maps, notreal, maxdepth);
-           }
-       }
-      UNGCPRO;
-    }
+  GCPRO1 (skp.pending_maps);
+  map_keymap (keymap, single_menu_item, Qnil, &skp, 1);
+  UNGCPRO;
 
   /* Process now any submenus which want to be panes at this level.  */
-  while (!NILP (pending_maps))
+  while (CONSP (skp.pending_maps))
     {
       Lisp_Object elt, eltcdr, string;
-      elt = Fcar (pending_maps);
+      elt = XCAR (skp.pending_maps);
       eltcdr = XCDR (elt);
       string = XCAR (eltcdr);
       /* We no longer discard the @ from the beginning of the string here.
         Instead, we do this in mac_menu_show.  */
       single_keymap_panes (Fcar (elt), string,
                           XCDR (eltcdr), notreal, maxdepth - 1);
-      pending_maps = Fcdr (pending_maps);
+      skp.pending_maps = XCDR (skp.pending_maps);
     }
 }
 \f
 /* This is a subroutine of single_keymap_panes that handles one
    keymap entry.
    KEY is a key in a keymap and ITEM is its binding.
-   PENDING_MAPS_PTR points to a list of keymaps waiting to be made into
+   SKP->PENDING_MAPS_PTR is a list of keymaps waiting to be made into
    separate panes.
-   If NOTREAL is nonzero, only check for equivalent key bindings, don't
+   If SKP->NOTREAL is nonzero, only check for equivalent key bindings, don't
    evaluate expressions in menu items and don't make any menu.
-   If we encounter submenus deeper than MAXDEPTH levels, ignore them.  */
+   If we encounter submenus deeper than SKP->MAXDEPTH levels, ignore them.  */
 
 static void
-single_menu_item (key, item, pending_maps_ptr, notreal, maxdepth)
-     Lisp_Object key, item;
-     Lisp_Object *pending_maps_ptr;
-     int maxdepth, notreal;
+single_menu_item (key, item, dummy, skp_v)
+     Lisp_Object key, item, dummy;
+     void *skp_v;
 {
   Lisp_Object map, item_string, enabled;
   struct gcpro gcpro1, gcpro2;
   int res;
+  struct skp *skp = skp_v;
 
   /* Parse the menu item and leave the result in item_properties.  */
   GCPRO2 (key, item);
-  res = parse_menu_item (item, notreal, 0);
+  res = parse_menu_item (item, skp->notreal, 0);
   UNGCPRO;
   if (!res)
     return;                    /* Not a menu item.  */
 
   map = XVECTOR (item_properties)->contents[ITEM_PROPERTY_MAP];
 
-  if (notreal)
+  if (skp->notreal)
     {
       /* We don't want to make a menu, just traverse the keymaps to
         precompute equivalent key bindings.  */
       if (!NILP (map))
-       single_keymap_panes (map, Qnil, key, 1, maxdepth - 1);
+       single_keymap_panes (map, Qnil, key, 1, skp->maxdepth - 1);
       return;
     }
 
@@ -528,8 +562,8 @@ single_menu_item (key, item, pending_maps_ptr, notreal, maxdepth)
     {
       if (!NILP (enabled))
        /* An enabled separate pane. Remember this to handle it later.  */
-       *pending_maps_ptr = Fcons (Fcons (map, Fcons (item_string, key)),
-                                  *pending_maps_ptr);
+       skp->pending_maps = Fcons (Fcons (map, Fcons (item_string, key)),
+                                  skp->pending_maps);
       return;
     }
 
@@ -537,14 +571,14 @@ single_menu_item (key, item, pending_maps_ptr, notreal, maxdepth)
                  XVECTOR (item_properties)->contents[ITEM_PROPERTY_DEF],
                  XVECTOR (item_properties)->contents[ITEM_PROPERTY_KEYEQ],
                  XVECTOR (item_properties)->contents[ITEM_PROPERTY_TYPE],
-                  XVECTOR (item_properties)->contents[ITEM_PROPERTY_SELECTED],
-                  XVECTOR (item_properties)->contents[ITEM_PROPERTY_HELP]);
+                 XVECTOR (item_properties)->contents[ITEM_PROPERTY_SELECTED],
+                 XVECTOR (item_properties)->contents[ITEM_PROPERTY_HELP]);
 
   /* Display a submenu using the toolkit.  */
   if (! (NILP (map) || NILP (enabled)))
     {
       push_submenu_start ();
-      single_keymap_panes (map, Qnil, key, 0, maxdepth - 1);
+      single_keymap_panes (map, Qnil, key, 0, skp->maxdepth - 1);
       push_submenu_end ();
     }
 }
@@ -561,13 +595,13 @@ list_of_panes (menu)
 
   init_menu_items ();
 
-  for (tail = menu; !NILP (tail); tail = Fcdr (tail))
+  for (tail = menu; CONSP (tail); tail = XCDR (tail))
     {
       Lisp_Object elt, pane_name, pane_data;
-      elt = Fcar (tail);
+      elt = XCAR (tail);
       pane_name = Fcar (elt);
       CHECK_STRING (pane_name);
-      push_menu_pane (pane_name, Qnil);
+      push_menu_pane (ENCODE_MENU_STRING (pane_name), Qnil);
       pane_data = Fcdr (elt);
       CHECK_CONS (pane_data);
       list_of_items (pane_data);
@@ -584,67 +618,89 @@ list_of_items (pane)
 {
   Lisp_Object tail, item, item1;
 
-  for (tail = pane; !NILP (tail); tail = Fcdr (tail))
+  for (tail = pane; CONSP (tail); tail = XCDR (tail))
     {
-      item = Fcar (tail);
+      item = XCAR (tail);
       if (STRINGP (item))
-       push_menu_item (item, Qnil, Qnil, Qt, Qnil, Qnil, Qnil, Qnil);
-      else if (NILP (item))
-       push_left_right_boundary ();
-      else
+       push_menu_item (ENCODE_MENU_STRING (item), Qnil, Qnil, Qt,
+                       Qnil, Qnil, Qnil, Qnil);
+      else if (CONSP (item))
        {
-         CHECK_CONS (item);
-         item1 = Fcar (item);
+         item1 = XCAR (item);
          CHECK_STRING (item1);
-         push_menu_item (item1, Qt, Fcdr (item), Qt, Qnil, Qnil, Qnil, Qnil);
+         push_menu_item (ENCODE_MENU_STRING (item1), Qt, XCDR (item),
+                         Qt, Qnil, Qnil, Qnil, Qnil);
        }
+      else
+       push_left_right_boundary ();
+
     }
 }
 \f
+static Lisp_Object
+cleanup_popup_menu (arg)
+     Lisp_Object arg;
+{
+  discard_menu_items ();
+  return Qnil;
+}
+
 DEFUN ("x-popup-menu", Fx_popup_menu, Sx_popup_menu, 2, 2, 0,
        doc: /* Pop up a deck-of-cards menu and return user's selection.
-POSITION is a position specification.  This is either a mouse button
-event or a list ((XOFFSET YOFFSET) WINDOW) where XOFFSET and YOFFSET
-are positions in pixels from the top left corner of WINDOW's frame
-\(WINDOW may be a frame object instead of a window).  This controls the
-position of the center of the first line in the first pane of the
-menu, not the top left of the menu as a whole.  If POSITION is t, it
-means to use the current mouse position.
+POSITION is a position specification.  This is either a mouse button event
+or a list ((XOFFSET YOFFSET) WINDOW)
+where XOFFSET and YOFFSET are positions in pixels from the top left
+corner of WINDOW.  (WINDOW may be a window or a frame object.)
+This controls the position of the top left of the menu as a whole.
+If POSITION is t, it means to use the current mouse position.
 
 MENU is a specifier for a menu.  For the simplest case, MENU is a keymap.
 The menu items come from key bindings that have a menu string as well as
-a definition; actually, the \"definition\" in such a key binding looks like
+a definition; actually, the "definition" in such a key binding looks like
 \(STRING . REAL-DEFINITION).  To give the menu a title, put a string into
 the keymap as a top-level element.
 
 If REAL-DEFINITION is nil, that puts a nonselectable string in the menu.
 Otherwise, REAL-DEFINITION should be a valid key binding definition.
 
-You can also use a list of keymaps as MENU.  Then each keymap makes a
-separate pane.  When MENU is a keymap or a list of keymaps, the return
-value is a list of events.
+You can also use a list of keymaps as MENU.
+  Then each keymap makes a separate pane.
+
+When MENU is a keymap or a list of keymaps, the return value is the
+list of events corresponding to the user's choice. Note that
+`x-popup-menu' does not actually execute the command bound to that
+sequence of events.
 
-Alternatively, you can specify a menu of multiple panes with a list of
-the form (TITLE PANE1 PANE2...), where each pane is a list of
-form (TITLE ITEM1 ITEM2...).
-Each ITEM is normally a cons cell (STRING . VALUE); but a string can
-appear as an item--that makes a nonselectable line in the menu.
+Alternatively, you can specify a menu of multiple panes
+  with a list of the form (TITLE PANE1 PANE2...),
+where each pane is a list of form (TITLE ITEM1 ITEM2...).
+Each ITEM is normally a cons cell (STRING . VALUE);
+but a string can appear as an item--that makes a nonselectable line
+in the menu.
 With this form of menu, the return value is VALUE from the chosen item.
 
 If POSITION is nil, don't display the menu at all, just precalculate the
-cached information about equivalent key sequences.  */)
-  (position, menu)
+cached information about equivalent key sequences.
+
+If the user gets rid of the menu without making a valid choice, for
+instance by clicking the mouse away from a valid choice or by typing
+keyboard input, then this normally results in a quit and
+`x-popup-menu' does not return.  But if POSITION is a mouse button
+event (indicating that the user invoked the menu with the mouse) then
+no quit occurs and `x-popup-menu' returns nil.  */)
+     (position, menu)
      Lisp_Object position, menu;
 {
   Lisp_Object keymap, tem;
   int xpos = 0, ypos = 0;
   Lisp_Object title;
-  char *error_name;
+  char *error_name = NULL;
   Lisp_Object selection;
   FRAME_PTR f = NULL;
   Lisp_Object x, y, window;
   int keymaps = 0;
   int for_click = 0;
+  int specpdl_count = SPECPDL_INDEX ();
   struct gcpro gcpro1;
 
 #ifdef HAVE_MENUS
@@ -655,7 +711,8 @@ cached information about equivalent key sequences.  */)
       /* Decode the first argument: find the window and the coordinates.  */
       if (EQ (position, Qt)
          || (CONSP (position) && (EQ (XCAR (position), Qmenu_bar)
-                                   || EQ (XCAR (position), Qtool_bar))))
+                                  || EQ (XCAR (position), Qtool_bar)
+                                  || EQ (XCAR (position), Qmac_apple_event))))
        {
          /* Use the mouse's current position.  */
          FRAME_PTR new_f = SELECTED_FRAME ();
@@ -681,8 +738,8 @@ cached information about equivalent key sequences.  */)
          if (CONSP (tem))
            {
              window = Fcar (Fcdr (position));
-             x = Fcar (tem);
-             y = Fcar (Fcdr (tem));
+             x = XCAR (tem);
+             y = Fcar (XCDR (tem));
            }
          else
            {
@@ -724,7 +781,8 @@ cached information about equivalent key sequences.  */)
 
       XSETFRAME (Vmenu_updating_frame, f);
     }
-  Vmenu_updating_frame = Qnil;
+  else
+    Vmenu_updating_frame = Qnil;
 #endif /* HAVE_MENUS */
 
   title = Qnil;
@@ -765,11 +823,11 @@ cached information about equivalent key sequences.  */)
 
       /* The first keymap that has a prompt string
         supplies the menu title.  */
-      for (tem = menu, i = 0; CONSP (tem); tem = Fcdr (tem))
+      for (tem = menu, i = 0; CONSP (tem); tem = XCDR (tem))
        {
          Lisp_Object prompt;
 
-         maps[i++] = keymap = get_keymap (Fcar (tem), 1, 0);
+         maps[i++] = keymap = get_keymap (XCAR (tem), 1, 0);
 
          prompt = Fkeymap_prompt (keymap);
          if (NILP (title) && !NILP (prompt))
@@ -805,13 +863,13 @@ cached information about equivalent key sequences.  */)
 
 #ifdef HAVE_MENUS
   /* Display them in a menu.  */
+  record_unwind_protect (cleanup_popup_menu, Qnil);
   BLOCK_INPUT;
 
   selection = mac_menu_show (f, xpos, ypos, for_click,
                             keymaps, title, &error_name);
   UNBLOCK_INPUT;
-
-  discard_menu_items ();
+  unbind_to (specpdl_count, Qnil);
 
   UNGCPRO;
 #endif /* HAVE_MENUS */
@@ -822,7 +880,33 @@ cached information about equivalent key sequences.  */)
 
 #ifdef HAVE_MENUS
 
-DEFUN ("x-popup-dialog", Fx_popup_dialog, Sx_popup_dialog, 2, 2, 0,
+/* Regard ESC and C-g as Cancel even without the Cancel button.  */
+
+#ifdef MAC_OSX
+static Boolean
+mac_dialog_modal_filter (dialog, event, item_hit)
+     DialogRef dialog;
+     EventRecord *event;
+     DialogItemIndex *item_hit;
+{
+  Boolean result;
+
+  result = StdFilterProc (dialog, event, item_hit);
+  if (result == false
+      && (event->what == keyDown || event->what == autoKey)
+      && ((event->message & charCodeMask) == kEscapeCharCode
+         || mac_quit_char_key_p (event->modifiers,
+                                 (event->message & keyCodeMask) >> 8)))
+    {
+      *item_hit = kStdCancelItemIndex;
+      return true;
+    }
+
+  return result;
+}
+#endif
+
+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.
@@ -830,16 +914,23 @@ 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 (TITLE ITEM1 ITEM2...).
+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.)  */)
-  (position, contents)
-     Lisp_Object position, contents;
+\(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;
 {
   FRAME_PTR f = NULL;
   Lisp_Object window;
@@ -849,7 +940,8 @@ on the left of the dialog box and all following items on the right.
   /* Decode the first argument: find the window or frame to use.  */
   if (EQ (position, Qt)
       || (CONSP (position) && (EQ (XCAR (position), Qmenu_bar)
-                               || EQ (XCAR (position), Qtool_bar))))
+                              || EQ (XCAR (position), Qtool_bar)
+                              || EQ (XCAR (position), Qmac_apple_event))))
     {
 #if 0 /* Using the frame the mouse is on may not be right.  */
       /* Use the mouse's current position.  */
@@ -899,6 +991,101 @@ on the left of the dialog box and all following items on the right.
        but I don't want to make one now.  */
     CHECK_WINDOW (window);
 
+#ifdef MAC_OSX
+  /* Special treatment for Fmessage_box, Fyes_or_no_p, and Fy_or_n_p.  */
+  if (EQ (position, Qt)
+      && STRINGP (Fcar (contents))
+      && ((!NILP (Fequal (XCDR (contents),
+                         Fcons (Fcons (build_string ("OK"), Qt), Qnil)))
+          && EQ (header, Qt))
+         || (!NILP (Fequal (XCDR (contents),
+                            Fcons (Fcons (build_string ("Yes"), Qt),
+                                   Fcons (Fcons (build_string ("No"), Qnil),
+                                          Qnil))))
+             && NILP (header))))
+    {
+      OSStatus err = noErr;
+      AlertStdCFStringAlertParamRec param;
+      CFStringRef error_string, explanation_string;
+      DialogRef alert;
+      DialogItemIndex item_hit;
+      Lisp_Object tem;
+
+      /* Force a redisplay before showing the dialog.  If a frame is
+        created just before showing the dialog, its contents may not
+        have been fully drawn.  */
+      Fredisplay (Qt);
+
+      tem = Fstring_match (concat3 (build_string ("\\("),
+                                   call0 (intern ("sentence-end")),
+                                   build_string ("\\)\n")),
+                          XCAR (contents), Qnil);
+      BLOCK_INPUT;
+      if (NILP (tem))
+       {
+         error_string = cfstring_create_with_string (XCAR (contents));
+         if (error_string == NULL)
+           err = memFullErr;
+         explanation_string = NULL;
+       }
+      else
+       {
+         tem = Fmatch_end (make_number (1));
+         error_string =
+           cfstring_create_with_string (Fsubstring (XCAR (contents),
+                                                    make_number (0), tem));
+         if (error_string == NULL)
+           err = memFullErr;
+         else
+           {
+             XSETINT (tem, XINT (tem) + 1);
+             explanation_string =
+               cfstring_create_with_string (Fsubstring (XCAR (contents),
+                                                        tem, Qnil));
+             if (explanation_string == NULL)
+               {
+                 CFRelease (error_string);
+                 err = memFullErr;
+               }
+           }
+       }
+      if (err == noErr)
+       err = GetStandardAlertDefaultParams (&param,
+                                            kStdCFStringAlertVersionOne);
+      if (err == noErr)
+       {
+         param.movable = true;
+         param.position = kWindowAlertPositionParentWindow;
+         if (NILP (header))
+           {
+             param.defaultText = CFSTR ("Yes");
+             param.otherText = CFSTR ("No");
+#if 0
+             param.cancelText = CFSTR ("Cancel");
+             param.cancelButton = kAlertStdAlertCancelButton;
+#endif
+           }
+         err = CreateStandardAlert (kAlertNoteAlert, error_string,
+                                    explanation_string, &param, &alert);
+         CFRelease (error_string);
+         if (explanation_string)
+           CFRelease (explanation_string);
+       }
+      if (err == noErr)
+       err = RunStandardAlert (alert, mac_dialog_modal_filter, &item_hit);
+      UNBLOCK_INPUT;
+
+      if (err == noErr)
+       {
+         if (item_hit == kStdCancelItemIndex)
+           Fsignal (Qquit, Qnil);
+         else if (item_hit == kStdOkItemIndex)
+           return Qt;
+         else
+           return Qnil;
+       }
+    }
+#endif
 #ifndef HAVE_DIALOGS
   /* Display a menu with these alternatives
      in the middle of frame F.  */
@@ -917,6 +1104,7 @@ on the left of the dialog box and all following items on the right.
     Lisp_Object title;
     char *error_name;
     Lisp_Object selection;
+    int specpdl_count = SPECPDL_INDEX ();
 
     /* Decode the dialog items from what was specified.  */
     title = Fcar (contents);
@@ -925,11 +1113,11 @@ on the left of the dialog box and all following items on the right.
     list_of_panes (Fcons (contents, Qnil));
 
     /* Display them in a dialog box.  */
+    record_unwind_protect (cleanup_popup_menu, Qnil);
     BLOCK_INPUT;
-    selection = mac_dialog_show (f, 0, title, &error_name);
+    selection = mac_dialog_show (f, 0, title, header, &error_name);
     UNBLOCK_INPUT;
-
-    discard_menu_items ();
+    unbind_to (specpdl_count, Qnil);
 
     if (error_name) error (error_name);
     return selection;
@@ -941,52 +1129,79 @@ on the left of the dialog box and all following items on the right.
    This is called from keyboard.c when it gets the
    MENU_BAR_ACTIVATE_EVENT out of the Emacs event queue.
 
-   To activate the menu bar, we signal to the input thread that it can
-   return from the WM_INITMENU message, allowing the normal Windows
-   processing of the menus.
+   To activate the menu bar, we use the button-press event location
+   that was saved in saved_menu_event_location.
 
    But first we recompute the menu bar contents (the whole tree).
 
-   This way we can safely execute Lisp code.  */
+   The reason for saving the button event until here, instead of
+   passing it to the toolkit right away, is that we can safely
+   execute Lisp code.  */
 
 void
 x_activate_menubar (f)
      FRAME_PTR f;
 {
   SInt32 menu_choice;
+  SInt16 menu_id, menu_item;
   extern Point saved_menu_event_location;
 
   set_frame_menubar (f, 0, 1);
   BLOCK_INPUT;
 
+  popup_activated_flag = 1;
   menu_choice = MenuSelect (saved_menu_event_location);
-  do_menu_choice (menu_choice);
+  popup_activated_flag = 0;
+  menu_id = HiWord (menu_choice);
+  menu_item = LoWord (menu_choice);
+
+#if !TARGET_API_MAC_CARBON
+  if (menu_id == min_menu_id[MAC_MENU_M_APPLE])
+    do_apple_menu (menu_item);
+  else
+#endif
+    if (menu_id)
+      {
+        MenuHandle menu = GetMenuHandle (menu_id);
+
+        if (menu)
+          {
+            UInt32 refcon;
+
+            GetMenuItemRefCon (menu, menu_item, &refcon);
+            find_and_call_menu_selection (f, f->menu_bar_items_used,
+                                         f->menu_bar_vector, (void *) refcon);
+          }
+      }
+
+  HiliteMenu (0);
 
   UNBLOCK_INPUT;
 }
 
-/* This callback is called from the menu bar pulldown menu
-   when the user makes a selection.
-   Figure out what the user chose
-   and put the appropriate events into the keyboard buffer.  */
+/* Find the menu selection and store it in the keyboard buffer.
+   F is the frame the menu is on.
+   MENU_BAR_ITEMS_USED is the length of VECTOR.
+   VECTOR is an array of menu events for the whole menu.  */
 
-void
-menubar_selection_callback (FRAME_PTR f, int client_data)
+static void
+find_and_call_menu_selection (f, menu_bar_items_used, vector, client_data)
+     FRAME_PTR f;
+     int menu_bar_items_used;
+     Lisp_Object vector;
+     void *client_data;
 {
   Lisp_Object prefix, entry;
-  Lisp_Object vector;
   Lisp_Object *subprefix_stack;
   int submenu_depth = 0;
   int i;
 
-  if (!f)
-    return;
   entry = Qnil;
-  subprefix_stack = (Lisp_Object *) alloca (f->menu_bar_items_used * sizeof (Lisp_Object));
-  vector = f->menu_bar_vector;
+  subprefix_stack = (Lisp_Object *) alloca (menu_bar_items_used * sizeof (Lisp_Object));
   prefix = Qnil;
   i = 0;
-  while (i < f->menu_bar_items_used)
+
+  while (i < menu_bar_items_used)
     {
       if (EQ (XVECTOR (vector)->contents[i], Qnil))
        {
@@ -1044,15 +1259,11 @@ menubar_selection_callback (FRAME_PTR f, int client_data)
              buf.arg = entry;
              kbd_buffer_store_event (&buf);
 
-             f->output_data.mac->menu_command_in_progress = 0;
-             f->output_data.mac->menubar_active = 0;
              return;
            }
          i += MENU_ITEMS_ITEM_LENGTH;
        }
     }
-  f->output_data.mac->menu_command_in_progress = 0;
-  f->output_data.mac->menubar_active = 0;
 }
 
 /* Allocate a widget_value, blocking input.  */
@@ -1097,22 +1308,18 @@ free_menubar_widget_value_tree (wv)
   UNBLOCK_INPUT;
 }
 \f
-/* Return a tree of widget_value structures for a menu bar item
+/* Set up data in menu_items for a menu bar item
    whose event type is ITEM_KEY (with string ITEM_NAME)
    and whose contents come from the list of keymaps MAPS.  */
 
-static widget_value *
-single_submenu (item_key, item_name, maps)
+static int
+parse_single_submenu (item_key, item_name, maps)
      Lisp_Object item_key, item_name, maps;
 {
-  widget_value *wv, *prev_wv, *save_wv, *first_wv;
-  int i;
-  int submenu_depth = 0;
   Lisp_Object length;
   int len;
   Lisp_Object *mapvec;
-  widget_value **submenu_stack;
-  int previous_items = menu_items_used;
+  int i;
   int top_level_items = 0;
 
   length = Flength (maps);
@@ -1126,28 +1333,45 @@ single_submenu (item_key, item_name, maps)
       maps = Fcdr (maps);
     }
 
-  menu_items_n_panes = 0;
-
   /* Loop over the given keymaps, making a pane for each map.
      But don't make a pane that is empty--ignore that map instead.  */
   for (i = 0; i < len; i++)
     {
-      if (SYMBOLP (mapvec[i])
-         || (CONSP (mapvec[i]) && !KEYMAPP (mapvec[i])))
+      if (!KEYMAPP (mapvec[i]))
        {
          /* Here we have a command at top level in the menu bar
             as opposed to a submenu.  */
          top_level_items = 1;
          push_menu_pane (Qnil, Qnil);
          push_menu_item (item_name, Qt, item_key, mapvec[i],
-                          Qnil, Qnil, Qnil, Qnil);
+                         Qnil, Qnil, Qnil, Qnil);
        }
       else
-       single_keymap_panes (mapvec[i], item_name, item_key, 0, 10);
+       {
+         Lisp_Object prompt;
+         prompt = Fkeymap_prompt (mapvec[i]);
+         single_keymap_panes (mapvec[i],
+                              !NILP (prompt) ? prompt : item_name,
+                              item_key, 0, 10);
+       }
     }
 
-  /* Create a tree of widget_value objects
-     representing the panes and their items.  */
+  return top_level_items;
+}
+
+/* Create a tree of widget_value objects
+   representing the panes and items
+   in menu_items starting at index START, up to index END.  */
+
+static widget_value *
+digest_single_submenu (start, end, top_level_items)
+     int start, end, top_level_items;
+{
+  widget_value *wv, *prev_wv, *save_wv, *first_wv;
+  int i;
+  int submenu_depth = 0;
+  widget_value **submenu_stack;
+  int panes_seen = 0;
 
   submenu_stack
     = (widget_value **) alloca (menu_items_used * sizeof (widget_value *));
@@ -1161,12 +1385,12 @@ single_submenu (item_key, item_name, maps)
   save_wv = 0;
   prev_wv = 0;
 
-  /* Loop over all panes and items made during this call
-     and construct a tree of widget_value objects.
-     Ignore the panes and items made by previous calls to
-     single_submenu, even though those are also in menu_items.  */
-  i = previous_items;
-  while (i < menu_items_used)
+  /* Loop over all panes and items made by the preceding call
+     to parse_single_submenu and construct a tree of widget_value objects.
+     Ignore the panes and items used by previous calls to
+     digest_single_submenu, even though those are also in menu_items.  */
+  i = start;
+  while (i < end)
     {
       if (EQ (XVECTOR (menu_items)->contents[i], Qnil))
        {
@@ -1194,13 +1418,15 @@ single_submenu (item_key, item_name, maps)
          Lisp_Object pane_name, prefix;
          char *pane_string;
 
+         panes_seen++;
+
          pane_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_NAME];
          prefix = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
 
 #ifndef HAVE_MULTILINGUAL_MENU
          if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name))
            {
-             pane_name = ENCODE_SYSTEM (pane_name);
+             pane_name = ENCODE_MENU_STRING (pane_name);
              AREF (menu_items, i + MENU_ITEMS_PANE_NAME) = pane_name;
            }
 #endif
@@ -1221,17 +1447,17 @@ single_submenu (item_key, item_name, maps)
                save_wv->next = wv;
              else
                first_wv->contents = wv;
-             wv->name = pane_string;
-             /* Ignore the @ that means "separate pane".
-                This is a kludge, but this isn't worth more time.  */
-             if (!NILP (prefix) && wv->name[0] == '@')
-               wv->name++;
-             wv->value = 0;
+             wv->lname = pane_name;
+              /* Set value to 1 so update_submenu_strings can handle '@'  */
+             wv->value = (char *)1;
              wv->enabled = 1;
              wv->button_type = BUTTON_TYPE_NONE;
              wv->help = Qnil;
+             save_wv = wv;
            }
-         save_wv = wv;
+         else
+           save_wv = first_wv;
+
          prev_wv = 0;
          i += MENU_ITEMS_PANE_LENGTH;
        }
@@ -1239,7 +1465,11 @@ single_submenu (item_key, item_name, maps)
        {
          /* Create a new item within current pane.  */
          Lisp_Object item_name, enable, descrip, def, type, selected;
-          Lisp_Object help;
+         Lisp_Object help;
+
+         /* All items should be contained in panes.  */
+         if (panes_seen == 0)
+           abort ();
 
          item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
          enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
@@ -1250,13 +1480,13 @@ single_submenu (item_key, item_name, maps)
          help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP);
 
 #ifndef HAVE_MULTILINGUAL_MENU
-         if (STRING_MULTIBYTE (item_name))
+          if (STRING_MULTIBYTE (item_name))
            {
              item_name = ENCODE_MENU_STRING (item_name);
              AREF (menu_items, i + MENU_ITEMS_ITEM_NAME) = item_name;
            }
 
-         if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
+          if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
            {
              descrip = ENCODE_MENU_STRING (descrip);
              AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY) = descrip;
@@ -1269,9 +1499,9 @@ single_submenu (item_key, item_name, maps)
          else
            save_wv->contents = wv;
 
-         wv->name = (char *) SDATA (item_name);
+         wv->lname = item_name;
          if (!NILP (descrip))
-           wv->key = (char *) SDATA (descrip);
+           wv->lkey = descrip;
          wv->value = 0;
          /* The EMACS_INT cast avoids a warning.  There's no problem
             as long as pointers have enough bits to hold small integers.  */
@@ -1288,7 +1518,7 @@ single_submenu (item_key, item_name, maps)
            abort ();
 
          wv->selected = !NILP (selected);
-         if (!STRINGP (help))
+         if (! STRINGP (help))
            help = Qnil;
 
          wv->help = help;
@@ -1310,7 +1540,185 @@ single_submenu (item_key, item_name, maps)
 
   return first_wv;
 }
+
+/* Walk through the widget_value tree starting at FIRST_WV and update
+   the char * pointers from the corresponding lisp values.
+   We do this after building the whole tree, since GC may happen while the
+   tree is constructed, and small strings are relocated.  So we must wait
+   until no GC can happen before storing pointers into lisp values.  */
+static void
+update_submenu_strings (first_wv)
+     widget_value *first_wv;
+{
+  widget_value *wv;
+
+  for (wv = first_wv; wv; wv = wv->next)
+    {
+      if (STRINGP (wv->lname))
+        {
+          wv->name = SDATA (wv->lname);
+
+          /* Ignore the @ that means "separate pane".
+             This is a kludge, but this isn't worth more time.  */
+          if (wv->value == (char *)1)
+            {
+              if (wv->name[0] == '@')
+               wv->name++;
+              wv->value = 0;
+            }
+        }
+
+      if (STRINGP (wv->lkey))
+        wv->key = SDATA (wv->lkey);
+
+      if (wv->contents)
+        update_submenu_strings (wv->contents);
+    }
+}
+
 \f
+#if TARGET_API_MAC_CARBON
+extern Lisp_Object Vshow_help_function;
+
+static Lisp_Object
+restore_show_help_function (old_show_help_function)
+     Lisp_Object old_show_help_function;
+{
+  Vshow_help_function = old_show_help_function;
+
+  return Qnil;
+}
+
+static pascal OSStatus
+menu_target_item_handler (next_handler, event, data)
+     EventHandlerCallRef next_handler;
+     EventRef event;
+     void *data;
+{
+  OSStatus err, result;
+  MenuRef menu;
+  MenuItemIndex menu_item;
+  Lisp_Object help;
+  GrafPtr port;
+  int specpdl_count = SPECPDL_INDEX ();
+
+  result = CallNextEventHandler (next_handler, event);
+
+  err = GetEventParameter (event, kEventParamDirectObject, typeMenuRef,
+                          NULL, sizeof (MenuRef), NULL, &menu);
+  if (err == noErr)
+    err = GetEventParameter (event, kEventParamMenuItemIndex,
+                            typeMenuItemIndex, NULL,
+                            sizeof (MenuItemIndex), NULL, &menu_item);
+  if (err == noErr)
+    err = GetMenuItemProperty (menu, menu_item,
+                              MAC_EMACS_CREATOR_CODE, 'help',
+                              sizeof (Lisp_Object), NULL, &help);
+  if (err != noErr)
+    help = Qnil;
+
+  /* Temporarily bind Vshow_help_function to Qnil because we don't
+     want tooltips during menu tracking.  */
+  record_unwind_protect (restore_show_help_function, Vshow_help_function);
+  Vshow_help_function = Qnil;
+  GetPort (&port);
+  show_help_echo (help, Qnil, Qnil, Qnil, 1);
+  SetPort (port);
+  unbind_to (specpdl_count, Qnil);
+
+  return err == noErr ? noErr : result;
+}
+#endif
+
+OSStatus
+install_menu_target_item_handler (window)
+     WindowPtr window;
+{
+  OSStatus err = noErr;
+#if TARGET_API_MAC_CARBON
+  static const EventTypeSpec specs[] =
+    {{kEventClassMenu, kEventMenuTargetItem}};
+  static EventHandlerUPP menu_target_item_handlerUPP = NULL;
+
+  if (menu_target_item_handlerUPP == NULL)
+    menu_target_item_handlerUPP =
+      NewEventHandlerUPP (menu_target_item_handler);
+
+  err = InstallWindowEventHandler (window, menu_target_item_handlerUPP,
+                                  GetEventTypeCount (specs), specs,
+                                  NULL, NULL);
+#endif
+  return err;
+}
+
+/* Event handler function that pops down a menu on C-g.  We can only pop
+   down menus if CancelMenuTracking is present (OSX 10.3 or later).  */
+
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1030
+static pascal OSStatus
+menu_quit_handler (nextHandler, theEvent, userData)
+     EventHandlerCallRef nextHandler;
+     EventRef theEvent;
+     void* userData;
+{
+  OSStatus err;
+  UInt32 keyCode;
+  UInt32 keyModifiers;
+
+  err = GetEventParameter (theEvent, kEventParamKeyCode,
+                          typeUInt32, NULL, sizeof(UInt32), NULL, &keyCode);
+
+  if (err == noErr)
+    err = GetEventParameter (theEvent, kEventParamKeyModifiers,
+                            typeUInt32, NULL, sizeof(UInt32),
+                            NULL, &keyModifiers);
+
+  if (err == noErr && mac_quit_char_key_p (keyModifiers, keyCode))
+    {
+      MenuRef menu = userData != 0
+        ? (MenuRef)userData : AcquireRootMenu ();
+
+      CancelMenuTracking (menu, true, 0);
+      if (!userData) ReleaseMenu (menu);
+      return noErr;
+    }
+
+  return CallNextEventHandler (nextHandler, theEvent);
+}
+#endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 1030 */
+
+/* Add event handler to all menus that belong to KIND so we can detect C-g.
+   MENU_HANDLE is the root menu of the tracking session to dismiss
+   when C-g is detected.  NULL means the menu bar.
+   If CancelMenuTracking isn't available, do nothing.  */
+
+static void
+install_menu_quit_handler (kind, menu_handle)
+     enum mac_menu_kind kind;
+     MenuHandle menu_handle;
+{
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1030
+  static const EventTypeSpec typesList[] =
+    {{kEventClassKeyboard, kEventRawKeyDown}};
+  int id;
+
+#if MAC_OS_X_VERSION_MIN_REQUIRED == 1020
+  if (CancelMenuTracking == NULL)
+    return;
+#endif
+  for (id = min_menu_id[kind]; id < min_menu_id[kind + 1]; id++)
+    {
+      MenuHandle menu = GetMenuHandle (id);
+
+      if (menu == NULL)
+       break;
+      InstallMenuEventHandler (menu, menu_quit_handler,
+                              GetEventTypeCount (typesList),
+                              typesList, menu_handle, NULL);
+    }
+#endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 1030 */
+}
+
 /* Set the contents of the menubar widgets of frame F.
    The argument FIRST_TIME is currently ignored;
    it is set the first time this is called, from initialize_frame_menubar.  */
@@ -1324,11 +1732,9 @@ set_frame_menubar (f, first_time, deep_p)
   int menubar_widget = f->output_data.mac->menubar_widget;
   Lisp_Object items;
   widget_value *wv, *first_wv, *prev_wv = 0;
-  int i;
-
-  /* We must not change the menubar when actually in use.  */
-  if (f->output_data.mac->menubar_active)
-    return;
+  int i, last_i = 0;
+  int *submenu_start, *submenu_end;
+  int *submenu_top_level_items, *submenu_n_panes;
 
   XSETFRAME (Vmenu_updating_frame, f);
 
@@ -1337,14 +1743,6 @@ set_frame_menubar (f, first_time, deep_p)
   else if (pending_menu_activation && !deep_p)
     deep_p = 1;
 
-  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;
-
   if (deep_p)
     {
       /* Make a widget-value tree representing the entire menu trees.  */
@@ -1368,7 +1766,7 @@ set_frame_menubar (f, first_time, deep_p)
         because it is not reentrant.  */
       specbind (Qdebug_on_next_call, Qnil);
 
-      record_unwind_protect (Fset_match_data, Fmatch_data (Qnil, Qnil));
+      record_unwind_save_match_data ();
       if (NILP (Voverriding_local_map_menu_flag))
        {
          specbind (Qoverriding_terminal_local_map, Qnil);
@@ -1379,6 +1777,7 @@ set_frame_menubar (f, first_time, deep_p)
 
       /* Run the Lucid hook.  */
       safe_run_hooks (Qactivate_menubar_hook);
+
       /* If it has changed current-menubar from previous value,
         really recompute the menubar from the value.  */
       if (! NILP (Vlucid_menu_bar_dirty_flag))
@@ -1388,28 +1787,63 @@ set_frame_menubar (f, first_time, deep_p)
 
       items = FRAME_MENU_BAR_ITEMS (f);
 
-      inhibit_garbage_collection ();
-
       /* 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));
 
-      /* Fill in the current menu bar contents.  */
+      /* Fill in menu_items with the current menu bar contents.
+        This can evaluate Lisp code.  */
+      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;
 
+         last_i = i;
+
          key = XVECTOR (items)->contents[i];
          string = XVECTOR (items)->contents[i + 1];
          maps = XVECTOR (items)->contents[i + 2];
          if (NILP (string))
            break;
 
-         wv = single_submenu (key, string, maps);
+         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;
+       }
+
+      finish_menu_items ();
+
+      /* Convert menu_items into widget_value trees
+        to display the menu.  This cannot evaluate Lisp code.  */
+
+      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 < last_i; 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
@@ -1420,32 +1854,36 @@ set_frame_menubar (f, first_time, deep_p)
          prev_wv = wv;
        }
 
-      finish_menu_items ();
-
       set_buffer_internal_1 (prev);
-      unbind_to (specpdl_count, Qnil);
 
       /* If there has been no change in the Lisp-level contents
         of the menu bar, skip redisplaying it.  Just exit.  */
 
+      /* Compare the new menu items with the ones computed last time.  */
       for (i = 0; i < previous_menu_items_used; i++)
        if (menu_items_used == i
-           || (NILP (Fequal (previous_items[i],
-                             XVECTOR (menu_items)->contents[i]))))
+           || (!EQ (previous_items[i], XVECTOR (menu_items)->contents[i])))
          break;
       if (i == menu_items_used && i == previous_menu_items_used && i != 0)
        {
+         /* The menu items have not changed.  Don't bother updating
+            the menus in any form, since it would be a no-op.  */
          free_menubar_widget_value_tree (first_wv);
-         menu_items = Qnil;
-
+         discard_menu_items ();
+         unbind_to (specpdl_count, Qnil);
          return;
        }
 
+      /* The menu items are different, so store them in the frame.  */
+      f->menu_bar_vector = menu_items;
+      f->menu_bar_items_used = menu_items_used;
+
+      /* This calls restore_menu_items to restore menu_items, etc.,
+        as they were outside.  */
+      unbind_to (specpdl_count, Qnil);
+
       /* Now GC cannot happen during the lifetime of the widget_value,
-        so it's safe to store data from a Lisp_String, as long as
-        local copies are made when the actual menu is created.
-        Windows takes care of this for normal string items, but
-        not for owner-drawn items or additional item-info.  */
+        so it's safe to store data from a Lisp_String.  */
       wv = first_wv->contents;
       for (i = 0; i < XVECTOR (items)->size; i += 4)
        {
@@ -1454,18 +1892,24 @@ set_frame_menubar (f, first_time, deep_p)
          if (NILP (string))
            break;
          wv->name = (char *) SDATA (string);
+          update_submenu_strings (wv->contents);
          wv = wv->next;
        }
 
-      f->menu_bar_vector = menu_items;
-      f->menu_bar_items_used = menu_items_used;
-      menu_items = Qnil;
     }
   else
     {
       /* Make a widget-value tree containing
         just the top level menu bar strings.  */
 
+      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;
+
       items = FRAME_MENU_BAR_ITEMS (f);
       for (i = 0; i < XVECTOR (items)->size; i += 4)
        {
@@ -1507,62 +1951,52 @@ set_frame_menubar (f, first_time, deep_p)
   /* Non-null value to indicate menubar has already been "created".  */
   f->output_data.mac->menubar_widget = 1;
 
-  {
-    int i = MIN_MENU_ID;
-    MenuHandle menu = GetMenuHandle (i);
-    while (menu != NULL)
-      {
-       DeleteMenu (i);
-       DisposeMenu (menu);
-       menu = GetMenuHandle (++i);
-      }
-
-    i = MIN_SUBMENU_ID;
-    menu = GetMenuHandle (i);
-    while (menu != NULL)
-      {
-       DeleteMenu (i);
-       DisposeMenu (menu);
-       menu = GetMenuHandle (++i);
-      }
-  }
-
-  fill_menubar (first_wv->contents);
-
-  DrawMenuBar ();
+  fill_menubar (first_wv->contents, deep_p);
 
+  /* Add event handler so we can detect C-g. */
+  install_menu_quit_handler (MAC_MENU_MENU_BAR, NULL);
+  install_menu_quit_handler (MAC_MENU_MENU_BAR_SUB, NULL);
   free_menubar_widget_value_tree (first_wv);
 
   UNBLOCK_INPUT;
 }
 
-/* Called from Fx_create_frame to create the initial menubar of a frame
-   before it is mapped, so that the window is mapped with the menubar already
-   there instead of us tacking it on later and thrashing the window after it
-   is visible.  */
+/* Get rid of the menu bar of frame F, and free its storage.
+   This is used when deleting a frame, and when turning off the menu bar.  */
 
 void
-initialize_frame_menubar (f)
+free_frame_menubar (f)
      FRAME_PTR f;
 {
-  /* This function is called before the first chance to redisplay
-     the frame.  It has to be, so the frame will have the right size.  */
-  FRAME_MENU_BAR_ITEMS (f) = menu_bar_items (FRAME_MENU_BAR_ITEMS (f));
-  set_frame_menubar (f, 1, 1);
+  f->output_data.mac->menubar_widget = 0;
 }
 
-/* Get rid of the menu bar of frame F, and free its storage.
-   This is used when deleting a frame, and when turning off the menu bar.  */
-
-void
-free_frame_menubar (f)
-     FRAME_PTR f;
+\f
+static Lisp_Object
+pop_down_menu (arg)
+     Lisp_Object arg;
 {
-  f->output_data.mac->menubar_widget = NULL;
+  struct Lisp_Save_Value *p = XSAVE_VALUE (arg);
+  FRAME_PTR f = p->pointer;
+  MenuHandle menu = GetMenuHandle (min_menu_id[MAC_MENU_POPUP]);
+
+  BLOCK_INPUT;
+
+  /* Must reset this manually because the button release event is not
+     passed to Emacs event loop. */
+  FRAME_MAC_DISPLAY_INFO (f)->grabbed = 0;
+
+  /* delete all menus */
+  dispose_menus (MAC_MENU_POPUP_SUB, 0);
+  DeleteMenu (min_menu_id[MAC_MENU_POPUP]);
+  DisposeMenu (menu);
+
+  UNBLOCK_INPUT;
+
+  return Qnil;
 }
 
-\f
-/* mac_menu_show actually displays a menu using the panes and items in
+/* Mac_menu_show actually displays a menu using the panes and items in
    menu_items and returns the value selected from it; we assume input
    is blocked by the caller.  */
 
@@ -1588,9 +2022,8 @@ mac_menu_show (f, x, y, for_click, keymaps, title, error)
      char **error;
 {
   int i;
-  UInt32 refcon;
   int menu_item_choice;
-  int menu_item_selection;
+  UInt32 menu_item_selection;
   MenuHandle menu;
   Point pos;
   widget_value *wv, *save_wv = 0, *first_wv = 0, *prev_wv = 0;
@@ -1599,7 +2032,9 @@ mac_menu_show (f, x, y, for_click, keymaps, title, error)
   Lisp_Object *subprefix_stack
     = (Lisp_Object *) alloca (menu_items_used * sizeof (Lisp_Object));
   int submenu_depth = 0;
+
   int first_pane;
+  int specpdl_count = SPECPDL_INDEX ();
 
   *error = NULL;
 
@@ -1651,12 +2086,14 @@ mac_menu_show (f, x, y, for_click, keymaps, title, error)
          /* 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_SYSTEM (pane_name);
+             pane_name = ENCODE_MENU_STRING (pane_name);
              AREF (menu_items, i + MENU_ITEMS_PANE_NAME) = pane_name;
            }
 #endif
@@ -1699,14 +2136,13 @@ mac_menu_show (f, x, y, for_click, keymaps, title, error)
        {
          /* 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);
+         help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP);
 
 #ifndef HAVE_MULTILINGUAL_MENU
           if (STRINGP (item_name) && STRING_MULTIBYTE (item_name))
@@ -1714,8 +2150,9 @@ mac_menu_show (f, x, y, for_click, keymaps, title, error)
              item_name = ENCODE_MENU_STRING (item_name);
              AREF (menu_items, i + MENU_ITEMS_ITEM_NAME) = item_name;
            }
+
           if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
-            {
+           {
              descrip = ENCODE_MENU_STRING (descrip);
              AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY) = descrip;
            }
@@ -1745,7 +2182,8 @@ mac_menu_show (f, x, y, for_click, keymaps, title, error)
            abort ();
 
          wv->selected = !NILP (selected);
-          if (!STRINGP (help))
+
+          if (! STRINGP (help))
            help = Qnil;
 
          wv->help = help;
@@ -1772,8 +2210,9 @@ mac_menu_show (f, x, y, for_click, keymaps, title, error)
       if (STRING_MULTIBYTE (title))
        title = ENCODE_MENU_STRING (title);
 #endif
+
       wv_title->name = (char *) SDATA (title);
-      wv_title->enabled = TRUE;
+      wv_title->enabled = FALSE;
       wv_title->title = TRUE;
       wv_title->button_type = BUTTON_TYPE_NONE;
       wv_title->help = Qnil;
@@ -1782,65 +2221,47 @@ mac_menu_show (f, x, y, for_click, keymaps, title, error)
     }
 
   /* Actually create the menu.  */
-  menu = NewMenu (POPUP_SUBMENU_ID, "\p");
-  submenu_id = MIN_POPUP_SUBMENU_ID;
-  fill_submenu (menu, first_wv->contents);
+  menu = NewMenu (min_menu_id[MAC_MENU_POPUP], "\p");
+  InsertMenu (menu, -1);
+  fill_menu (menu, first_wv->contents, MAC_MENU_POPUP_SUB,
+            min_menu_id[MAC_MENU_POPUP_SUB]);
+
+  /* Free the widget_value objects we used to specify the
+     contents.  */
+  free_menubar_widget_value_tree (first_wv);
 
   /* Adjust coordinates to be root-window-relative.  */
   pos.h = x;
   pos.v = y;
 
   SetPortWindowPort (FRAME_MAC_WINDOW (f));
-
   LocalToGlobal (&pos);
 
   /* No selection has been chosen yet.  */
-  menu_item_choice = 0;
   menu_item_selection = 0;
 
-  InsertMenu (menu, -1);
+  record_unwind_protect (pop_down_menu, make_save_value (f, 0));
+
+  /* Add event handler so we can detect C-g. */
+  install_menu_quit_handler (MAC_MENU_POPUP, menu);
+  install_menu_quit_handler (MAC_MENU_POPUP_SUB, menu);
 
   /* Display the menu.  */
+  popup_activated_flag = 1;
   menu_item_choice = PopUpMenuSelect (menu, pos.v, pos.h, 0);
-  menu_item_selection = LoWord (menu_item_choice);
+  popup_activated_flag = 0;
 
-  /* Get the refcon to find the correct item*/
-  if (menu_item_selection)
+  /* Get the refcon to find the correct item */
+  if (menu_item_choice)
     {
       MenuHandle sel_menu = GetMenuHandle (HiWord (menu_item_choice));
-      if (sel_menu) {
-       GetMenuItemRefCon (sel_menu, menu_item_selection, &refcon);
-      }
-    }
 
-#if 0
-  /* Clean up extraneous mouse events which might have been generated
-     during the call.  */
-  discard_mouse_events ();
-#endif
-
-  /* Must reset this manually because the button release event is not
-     passed to Emacs event loop. */
-  FRAME_MAC_DISPLAY_INFO (f)->grabbed = 0;
-
-  /* Free the widget_value objects we used to specify the
-     contents.  */
-  free_menubar_widget_value_tree (first_wv);
-
-  /* delete all menus */
-  {
-    int i = MIN_POPUP_SUBMENU_ID;
-    MenuHandle submenu = GetMenuHandle (i);
-    while (submenu != NULL)
-      {
-       DeleteMenu (i);
-       DisposeMenu (submenu);
-       submenu = GetMenuHandle (++i);
-      }
-  }
+      if (sel_menu)
+       GetMenuItemRefCon (sel_menu, LoWord (menu_item_choice),
+                          &menu_item_selection);
+    }
 
-  DeleteMenu (POPUP_SUBMENU_ID);
-  DisposeMenu (menu);
+  unbind_to (specpdl_count, Qnil);
 
   /* Find the selected item, and its pane, to return
      the proper value.  */
@@ -1877,7 +2298,7 @@ mac_menu_show (f, x, y, for_click, keymaps, title, error)
            {
              entry
                = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_VALUE];
-             if ((int) (EMACS_INT) refcon == i)
+             if (menu_item_selection == i)
                {
                  if (keymaps != 0)
                    {
@@ -1896,14 +2317,401 @@ mac_menu_show (f, x, y, for_click, keymaps, title, error)
            }
        }
     }
+  else if (!for_click)
+    /* Make "Cancel" equivalent to C-g.  */
+    Fsignal (Qquit, Qnil);
 
   return Qnil;
 }
 \f
 
 #ifdef HAVE_DIALOGS
-/* Construct native Mac OS menubar based on widget_value tree.  */
+/* Construct native Mac OS dialog based on widget_value tree.  */
+
+#if TARGET_API_MAC_CARBON
+
+static pascal OSStatus
+mac_handle_dialog_event (next_handler, event, data)
+     EventHandlerCallRef next_handler;
+     EventRef event;
+     void *data;
+{
+  OSStatus err;
+  WindowRef window = (WindowRef) data;
+
+  switch (GetEventClass (event))
+    {
+    case kEventClassCommand:
+      {
+       HICommand command;
+
+       err = GetEventParameter (event, kEventParamDirectObject,
+                                typeHICommand, NULL, sizeof (HICommand),
+                                NULL, &command);
+       if (err == noErr)
+         if ((command.commandID & ~0xffff) == 'Bt\0\0')
+           {
+             SetWRefCon (window, command.commandID);
+             err = QuitAppModalLoopForWindow (window);
+
+             return err == noErr ? noErr : eventNotHandledErr;
+           }
+
+       return CallNextEventHandler (next_handler, event);
+      }
+      break;
+
+    case kEventClassKeyboard:
+      {
+       OSStatus result;
+       char char_code;
+
+       result = CallNextEventHandler (next_handler, event);
+       if (result == noErr)
+         return noErr;
+
+       err = GetEventParameter (event, kEventParamKeyMacCharCodes,
+                                typeChar, NULL, sizeof (char),
+                                NULL, &char_code);
+       if (err == noErr)
+         switch (char_code)
+           {
+           case kEscapeCharCode:
+             err = QuitAppModalLoopForWindow (window);
+             break;
+
+           default:
+             {
+               UInt32 modifiers, key_code;
+
+               err = GetEventParameter (event, kEventParamKeyModifiers,
+                                        typeUInt32, NULL, sizeof (UInt32),
+                                        NULL, &modifiers);
+               if (err == noErr)
+                 err = GetEventParameter (event, kEventParamKeyCode,
+                                          typeUInt32, NULL, sizeof (UInt32),
+                                          NULL, &key_code);
+               if (err == noErr)
+                 {
+                   if (mac_quit_char_key_p (modifiers, key_code))
+                     err = QuitAppModalLoopForWindow (window);
+                   else
+                     err = eventNotHandledErr;
+                 }
+             }
+             break;
+           }
+
+       return err == noErr ? noErr : result;
+      }
+      break;
+
+    default:
+      abort ();
+    }
+}
+
+static OSStatus
+install_dialog_event_handler (window)
+     WindowRef window;
+{
+  static const EventTypeSpec specs[] =
+    {{kEventClassCommand, kEventCommandProcess},
+     {kEventClassKeyboard, kEventRawKeyDown}};
+  static EventHandlerUPP handle_dialog_eventUPP = NULL;
+
+  if (handle_dialog_eventUPP == NULL)
+    handle_dialog_eventUPP = NewEventHandlerUPP (mac_handle_dialog_event);
+  return InstallWindowEventHandler (window, handle_dialog_eventUPP,
+                                   GetEventTypeCount (specs), specs,
+                                   window, NULL);
+}
+
+#define DIALOG_LEFT_MARGIN (112)
+#define DIALOG_TOP_MARGIN (24)
+#define DIALOG_RIGHT_MARGIN (24)
+#define DIALOG_BOTTOM_MARGIN (20)
+#define DIALOG_MIN_INNER_WIDTH (338)
+#define DIALOG_MAX_INNER_WIDTH (564)
+#define DIALOG_BUTTON_BUTTON_HORIZONTAL_SPACE (12)
+#define DIALOG_BUTTON_BUTTON_VERTICAL_SPACE (12)
+#define DIALOG_BUTTON_MIN_WIDTH (68)
+#define DIALOG_TEXT_MIN_HEIGHT (50)
+#define DIALOG_TEXT_BUTTONS_VERTICAL_SPACE (10)
+#define DIALOG_ICON_WIDTH (64)
+#define DIALOG_ICON_HEIGHT (64)
+#define DIALOG_ICON_LEFT_MARGIN (24)
+#define DIALOG_ICON_TOP_MARGIN (15)
+
+static int
+create_and_show_dialog (f, first_wv)
+     FRAME_PTR f;
+     widget_value *first_wv;
+{
+  OSStatus err;
+  char *dialog_name, *message;
+  int nb_buttons, first_group_count, i, result = 0;
+  widget_value *wv;
+  short buttons_height, text_height, inner_width, inner_height;
+  Rect empty_rect, *rects;
+  WindowRef window = NULL;
+  ControlRef *buttons, default_button = NULL, text;
+
+  dialog_name = first_wv->name;
+  nb_buttons = dialog_name[1] - '0';
+  first_group_count = nb_buttons - (dialog_name[4] - '0');
+
+  wv = first_wv->contents;
+  message = wv->value;
+
+  wv = wv->next;
+  SetRect (&empty_rect, 0, 0, 0, 0);
+
+  /* Create dialog window.  */
+  err = CreateNewWindow (kMovableModalWindowClass,
+                        kWindowStandardHandlerAttribute,
+                        &empty_rect, &window);
+  if (err == noErr)
+    err = SetThemeWindowBackground (window, kThemeBrushMovableModalBackground,
+                                   true);
+  if (err == noErr)
+    err = SetWindowTitleWithCFString (window, (dialog_name[0] == 'Q'
+                                              ? CFSTR ("Question")
+                                              : CFSTR ("Information")));
+
+  /* Create button controls and measure their optimal bounds.  */
+  if (err == noErr)
+    {
+      buttons = alloca (sizeof (ControlRef) * nb_buttons);
+      rects = alloca (sizeof (Rect) * nb_buttons);
+      for (i = 0; i < nb_buttons; i++)
+       {
+         CFStringRef label = cfstring_create_with_utf8_cstring (wv->value);
+
+         if (label == NULL)
+           err = memFullErr;
+         else
+           {
+             err = CreatePushButtonControl (window, &empty_rect,
+                                            label, &buttons[i]);
+             CFRelease (label);
+           }
+         if (err == noErr)
+           {
+             if (!wv->enabled)
+               {
+#ifdef MAC_OSX
+                 err = DisableControl (buttons[i]);
+#else
+                 err = DeactivateControl (buttons[i]);
+#endif
+               }
+             else if (default_button == NULL)
+               default_button = buttons[i];
+           }
+         if (err == noErr)
+           {
+             SInt16 unused;
+
+             rects[i] = empty_rect;
+             err = GetBestControlRect (buttons[i], &rects[i], &unused);
+           }
+         if (err == noErr)
+           {
+             OffsetRect (&rects[i], -rects[i].left, -rects[i].top);
+             if (rects[i].right < DIALOG_BUTTON_MIN_WIDTH)
+               rects[i].right = DIALOG_BUTTON_MIN_WIDTH;
+             else if (rects[i].right > DIALOG_MAX_INNER_WIDTH)
+               rects[i].right = DIALOG_MAX_INNER_WIDTH;
+
+             err = SetControlCommandID (buttons[i],
+                                        'Bt\0\0' + (int) wv->call_data);
+           }
+         if (err != noErr)
+           break;
+         wv = wv->next;
+       }
+    }
+
+  /* Layout buttons.  rects[i] is set relative to the bottom-right
+     corner of the inner box.  */
+  if (err == noErr)
+    {
+      short bottom, right, max_height, left_align_shift;
+
+      inner_width = DIALOG_MIN_INNER_WIDTH;
+      bottom = right = max_height = 0;
+      for (i = 0; i < nb_buttons; i++)
+       {
+         if (right - rects[i].right < - inner_width)
+           {
+             if (i != first_group_count
+                 && right - rects[i].right >= - DIALOG_MAX_INNER_WIDTH)
+               inner_width = - (right - rects[i].right);
+             else
+               {
+                 bottom -= max_height + DIALOG_BUTTON_BUTTON_VERTICAL_SPACE;
+                 right = max_height = 0;
+               }
+           }
+         if (max_height < rects[i].bottom)
+           max_height = rects[i].bottom;
+         OffsetRect (&rects[i], right - rects[i].right,
+                     bottom - rects[i].bottom);
+         right = rects[i].left - DIALOG_BUTTON_BUTTON_HORIZONTAL_SPACE;
+         if (i == first_group_count - 1)
+           right -= DIALOG_BUTTON_BUTTON_HORIZONTAL_SPACE;
+       }
+      buttons_height = - (bottom - max_height);
+
+      left_align_shift = - (inner_width + rects[nb_buttons - 1].left);
+      for (i = nb_buttons - 1; i >= first_group_count; i--)
+       {
+         if (bottom != rects[i].bottom)
+           {
+             left_align_shift = - (inner_width + rects[i].left);
+             bottom = rects[i].bottom;
+           }
+         OffsetRect (&rects[i], left_align_shift, 0);
+       }
+    }
+
+  /* Create a static text control and measure its bounds.  */
+  if (err == noErr)
+    {
+      CFStringRef message_string;
+      Rect bounds;
+
+      message_string = cfstring_create_with_utf8_cstring (message);
+      if (message_string == NULL)
+       err = memFullErr;
+      else
+       {
+         ControlFontStyleRec text_style;
+
+         text_style.flags = 0;
+         SetRect (&bounds, 0, 0, inner_width, 0);
+         err = CreateStaticTextControl (window, &bounds, message_string,
+                                        &text_style, &text);
+         CFRelease (message_string);
+       }
+      if (err == noErr)
+       {
+         SInt16 unused;
+
+         bounds = empty_rect;
+         err = GetBestControlRect (text, &bounds, &unused);
+       }
+      if (err == noErr)
+       {
+         text_height = bounds.bottom - bounds.top;
+         if (text_height < DIALOG_TEXT_MIN_HEIGHT)
+           text_height = DIALOG_TEXT_MIN_HEIGHT;
+       }
+    }
 
+  /* Place buttons. */
+  if (err == noErr)
+    {
+      inner_height = (text_height + DIALOG_TEXT_BUTTONS_VERTICAL_SPACE
+                     + buttons_height);
+
+      for (i = 0; i < nb_buttons; i++)
+       {
+         OffsetRect (&rects[i], DIALOG_LEFT_MARGIN + inner_width,
+                     DIALOG_TOP_MARGIN + inner_height);
+         SetControlBounds (buttons[i], &rects[i]);
+       }
+    }
+
+  /* Place text.  */
+  if (err == noErr)
+    {
+      Rect bounds;
+
+      SetRect (&bounds, DIALOG_LEFT_MARGIN, DIALOG_TOP_MARGIN,
+              DIALOG_LEFT_MARGIN + inner_width,
+              DIALOG_TOP_MARGIN + text_height);
+      SetControlBounds (text, &bounds);
+    }
+
+  /* Create the application icon at the upper-left corner.  */
+  if (err == noErr)
+    {
+      ControlButtonContentInfo content;
+      ControlRef icon;
+      static const ProcessSerialNumber psn = {0, kCurrentProcess};
+#ifdef MAC_OSX
+      FSRef app_location;
+#else
+      ProcessInfoRec pinfo;
+      FSSpec app_spec;
+#endif
+      SInt16 unused;
+
+      content.contentType = kControlContentIconRef;
+#ifdef MAC_OSX
+      err = GetProcessBundleLocation (&psn, &app_location);
+      if (err == noErr)
+       err = GetIconRefFromFileInfo (&app_location, 0, NULL, 0, NULL,
+                                     kIconServicesNormalUsageFlag,
+                                     &content.u.iconRef, &unused);
+#else
+      bzero (&pinfo, sizeof (ProcessInfoRec));
+      pinfo.processInfoLength = sizeof (ProcessInfoRec);
+      pinfo.processAppSpec = &app_spec;
+      err = GetProcessInformation (&psn, &pinfo);
+      if (err == noErr)
+       err = GetIconRefFromFile (&app_spec, &content.u.iconRef, &unused);
+#endif
+      if (err == noErr)
+       {
+         Rect bounds;
+
+         SetRect (&bounds, DIALOG_ICON_LEFT_MARGIN, DIALOG_ICON_TOP_MARGIN,
+                  DIALOG_ICON_LEFT_MARGIN + DIALOG_ICON_WIDTH,
+                  DIALOG_ICON_TOP_MARGIN + DIALOG_ICON_HEIGHT);
+         err = CreateIconControl (window, &bounds, &content, true, &icon);
+         ReleaseIconRef (content.u.iconRef);
+       }
+    }
+
+  /* Show the dialog window and run event loop.  */
+  if (err == noErr)
+    if (default_button)
+      err = SetWindowDefaultButton (window, default_button);
+  if (err == noErr)
+    err = install_dialog_event_handler (window);
+  if (err == noErr)
+    {
+      SizeWindow (window,
+                 DIALOG_LEFT_MARGIN + inner_width + DIALOG_RIGHT_MARGIN,
+                 DIALOG_TOP_MARGIN + inner_height + DIALOG_BOTTOM_MARGIN,
+                 true);
+      err = RepositionWindow (window, FRAME_MAC_WINDOW (f),
+                             kWindowAlertPositionOnParentWindow);
+    }
+  if (err == noErr)
+    {
+      SetWRefCon (window, 0);
+      ShowWindow (window);
+      BringToFront (window);
+      err = RunAppModalLoopForWindow (window);
+    }
+  if (err == noErr)
+    {
+      UInt32 command_id = GetWRefCon (window);
+
+      if ((command_id & ~0xffff) == 'Bt\0\0')
+       result = command_id - 'Bt\0\0';
+    }
+
+  if (window)
+    DisposeWindow (window);
+
+  return result;
+}
+#else  /* not TARGET_API_MAC_CARBON */
 static int
 mac_dialog (widget_value *wv)
 {
@@ -2008,17 +2816,18 @@ mac_dialog (widget_value *wv)
 
   return i;
 }
+#endif  /* not TARGET_API_MAC_CARBON */
 
 static char * button_names [] = {
   "button1", "button2", "button3", "button4", "button5",
   "button6", "button7", "button8", "button9", "button10" };
 
 static Lisp_Object
-mac_dialog_show (f, keymaps, title, error)
+mac_dialog_show (f, keymaps, title, header, error_name)
      FRAME_PTR f;
      int keymaps;
-     Lisp_Object title;
-     char **error;
+     Lisp_Object title, header;
+     char **error_name;
 {
   int i, nb_buttons=0;
   char dialog_name[6];
@@ -2031,11 +2840,11 @@ mac_dialog_show (f, keymaps, title, error)
   /* 1 means we've seen the boundary between left-hand elts and right-hand.  */
   int boundary_seen = 0;
 
-  *error = NULL;
+  *error_name = NULL;
 
   if (menu_items_n_panes > 1)
     {
-      *error = "Multiple panes in dialog box";
+      *error_name = "Multiple panes in dialog box";
       return Qnil;
     }
 
@@ -2063,18 +2872,16 @@ mac_dialog_show (f, keymaps, title, error)
       {
 
        /* Create a new item within current pane.  */
-       Lisp_Object item_name, enable, descrip, help;
-
+       Lisp_Object item_name, enable, descrip;
        item_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_NAME];
        enable = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_ENABLE];
        descrip
          = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_EQUIV_KEY];
-        help = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_HELP];
 
        if (NILP (item_name))
          {
            free_menubar_widget_value_tree (first_wv);
-           *error = "Submenu in dialog items";
+           *error_name = "Submenu in dialog items";
            return Qnil;
          }
        if (EQ (item_name, Qquote))
@@ -2088,7 +2895,7 @@ mac_dialog_show (f, keymaps, title, error)
        if (nb_buttons >= 9)
          {
            free_menubar_widget_value_tree (first_wv);
-           *error = "Too many dialog items";
+           *error_name = "Too many dialog items";
            return Qnil;
          }
 
@@ -2120,11 +2927,17 @@ mac_dialog_show (f, keymaps, title, error)
     wv->name = dialog_name;
     wv->help = Qnil;
 
+    /*  Frame title: 'Q' = Question, 'I' = Information.
+        Can also have 'E' = Error if, one day, we want
+        a popup for errors. */
+    if (NILP(header))
+      dialog_name[0] = 'Q';
+    else
+      dialog_name[0] = 'I';
+
     /* Dialog boxes use a really stupid name encoding
        which specifies how many buttons to use
-       and how many buttons are on the right.
-       The Q means something also.  */
-    dialog_name[0] = 'Q';
+       and how many buttons are on the right. */
     dialog_name[1] = '0' + nb_buttons;
     dialog_name[2] = 'B';
     dialog_name[3] = 'R';
@@ -2135,18 +2948,23 @@ mac_dialog_show (f, keymaps, title, error)
     first_wv = wv;
   }
 
+  /* Force a redisplay before showing the dialog.  If a frame is created
+     just before showing the dialog, its contents may not have been fully
+     drawn.  */
+  Fredisplay (Qt);
+
   /* Actually create the dialog.  */
-#ifdef HAVE_DIALOGS
-  menu_item_selection = mac_dialog (first_wv);
+#if TARGET_API_MAC_CARBON
+  menu_item_selection = create_and_show_dialog (f, first_wv);
 #else
-  menu_item_selection = 0;
+  menu_item_selection = mac_dialog (first_wv);
 #endif
 
   /* Free the widget_value objects we used to specify the contents.  */
   free_menubar_widget_value_tree (first_wv);
 
-  /* Find the selected item, and its pane, to return the proper
-     value.  */
+  /* Find the selected item, and its pane, to return
+     the proper value.  */
   if (menu_item_selection != 0)
     {
       Lisp_Object prefix;
@@ -2163,6 +2981,12 @@ mac_dialog_show (f, keymaps, title, error)
                = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
              i += MENU_ITEMS_PANE_LENGTH;
            }
+         else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
+           {
+             /* This is the boundary between left-side elts and
+                right-side elts.  */
+             ++i;
+           }
          else
            {
              entry
@@ -2181,6 +3005,9 @@ mac_dialog_show (f, keymaps, title, error)
            }
        }
     }
+  else
+    /* Make "Cancel" equivalent to C-g.  */
+    Fsignal (Qquit, Qnil);
 
   return Qnil;
 }
@@ -2190,9 +3017,9 @@ mac_dialog_show (f, keymaps, title, error)
 /* Is this item a separator? */
 static int
 name_is_separator (name)
-     char *name;
+     const char *name;
 {
-  char *start = name;
+  const char *start = name;
 
   /* Check if name string consists of only dashes ('-').  */
   while (*name == '-') name++;
@@ -2203,11 +3030,16 @@ name_is_separator (name)
 }
 
 static void
-add_menu_item (MenuHandle menu, widget_value *wv, int submenu,
-              int force_disable)
+add_menu_item (menu, pos, wv)
+     MenuHandle menu;
+     int pos;
+     widget_value *wv;
 {
+#if TARGET_API_MAC_CARBON
+  CFStringRef item_name;
+#else
   Str255 item_name;
-  int pos, i;
+#endif
 
   if (name_is_separator (wv->name))
     AppendMenu (menu, "\p-");
@@ -2216,126 +3048,227 @@ add_menu_item (MenuHandle menu, widget_value *wv, int submenu,
       AppendMenu (menu, "\pX");
 
 #if TARGET_API_MAC_CARBON
-      pos = CountMenuItems (menu);
-#else
-      pos = CountMItems (menu);
-#endif
+      item_name = cfstring_create_with_utf8_cstring (wv->name);
 
-      strcpy (item_name, "");
-      strncat (item_name, wv->name, 255);
       if (wv->key != NULL)
        {
-         strncat (item_name, " ", 255);
-         strncat (item_name, wv->key, 255);
+         CFStringRef name, key;
+
+         name = item_name;
+         key = cfstring_create_with_utf8_cstring (wv->key);
+         item_name = CFStringCreateWithFormat (NULL, NULL, CFSTR ("%@ %@"),
+                                               name, key);
+         CFRelease (name);
+         CFRelease (key);
        }
-      item_name[255] = 0;
-#if TARGET_API_MAC_CARBON
-      {
-       CFStringRef string =
-         CFStringCreateWithCString (NULL, item_name, kCFStringEncodingUTF8);
 
-       SetMenuItemTextWithCFString (menu, pos, string);
-       CFRelease (string);
-      }
-#else
+      SetMenuItemTextWithCFString (menu, pos, item_name);
+      CFRelease (item_name);
+
+      if (wv->enabled)
+        EnableMenuItem (menu, pos);
+      else
+        DisableMenuItem (menu, pos);
+
+      if (STRINGP (wv->help))
+       SetMenuItemProperty (menu, pos, MAC_EMACS_CREATOR_CODE, 'help',
+                            sizeof (Lisp_Object), &wv->help);
+#else  /* ! TARGET_API_MAC_CARBON */
+      item_name[sizeof (item_name) - 1] = '\0';
+      strncpy (item_name, wv->name, sizeof (item_name) - 1);
+      if (wv->key != NULL)
+       {
+         int len = strlen (item_name);
+
+         strncpy (item_name + len, " ", sizeof (item_name) - 1 - len);
+         len = strlen (item_name);
+         strncpy (item_name + len, wv->key, sizeof (item_name) - 1 - len);
+       }
       c2pstr (item_name);
       SetMenuItemText (menu, pos, item_name);
-#endif
 
-      if (wv->enabled && !force_disable)
-#if TARGET_API_MAC_CARBON
-        EnableMenuItem (menu, pos);
-#else
+      if (wv->enabled)
         EnableItem (menu, pos);
-#endif
       else
-#if TARGET_API_MAC_CARBON
-        DisableMenuItem (menu, pos);
-#else
         DisableItem (menu, pos);
-#endif
+#endif  /* ! TARGET_API_MAC_CARBON */
 
       /* Draw radio buttons and tickboxes. */
-      {
       if (wv->selected && (wv->button_type == BUTTON_TYPE_TOGGLE ||
                            wv->button_type == BUTTON_TYPE_RADIO))
        SetItemMark (menu, pos, checkMark);
       else
        SetItemMark (menu, pos, noMark);
-      }
-    }
 
-  SetMenuItemRefCon (menu, pos, (UInt32) wv->call_data);
-
-  if (submenu != NULL)
-    SetMenuItemHierarchicalID (menu, pos, submenu);
+      SetMenuItemRefCon (menu, pos, (UInt32) wv->call_data);
+    }
 }
 
-/* Construct native Mac OS menubar based on widget_value tree.  */
+/* Construct native Mac OS menu based on widget_value tree.  */
 
-static void
-fill_submenu (MenuHandle menu, widget_value *wv)
+static int
+fill_menu (menu, wv, kind, submenu_id)
+     MenuHandle menu;
+     widget_value *wv;
+     enum mac_menu_kind kind;
+     int submenu_id;
 {
-  for ( ; wv != NULL; wv = wv->next)
-    if (wv->contents)
-      {
-       int cur_submenu = submenu_id++;
-        MenuHandle submenu = NewMenu (cur_submenu, "\pX");
-        fill_submenu (submenu, wv->contents);
-        InsertMenu (submenu, -1);
-        add_menu_item (menu, wv, cur_submenu, 0);
-      }
-    else
-      add_menu_item (menu, wv, NULL, 0);
-}
+  int pos;
 
+  for (pos = 1; wv != NULL; wv = wv->next, pos++)
+    {
+      add_menu_item (menu, pos, wv);
+      if (wv->contents && submenu_id < min_menu_id[kind + 1])
+       {
+         MenuHandle submenu = NewMenu (submenu_id, "\pX");
 
-/* Construct native Mac OS menu based on widget_value tree.  */
+         InsertMenu (submenu, -1);
+         SetMenuItemHierarchicalID (menu, pos, submenu_id);
+         submenu_id = fill_menu (submenu, wv->contents, kind, submenu_id + 1);
+       }
+    }
 
-static void
-fill_menu (MenuHandle menu, widget_value *wv)
-{
-  for ( ; wv != NULL; wv = wv->next)
-    if (wv->contents)
-      {
-       int cur_submenu = submenu_id++;
-        MenuHandle submenu = NewMenu (cur_submenu, "\pX");
-        fill_submenu (submenu, wv->contents);
-        InsertMenu (submenu, -1);
-        add_menu_item (menu, wv, cur_submenu, 0);
-      }
-    else
-      add_menu_item (menu, wv, NULL, 0);
+  return submenu_id;
 }
 
 /* Construct native Mac OS menubar based on widget_value tree.  */
 
 static void
-fill_menubar (widget_value *wv)
+fill_menubar (wv, deep_p)
+     widget_value *wv;
+     int deep_p;
 {
-  int id;
-
-  submenu_id  = MIN_SUBMENU_ID;
+  int id, submenu_id;
+  MenuHandle menu;
+  Str255 title;
+#if !TARGET_API_MAC_CARBON
+  int title_changed_p = 0;
+#endif
 
-  for (id = MIN_MENU_ID; wv != NULL; wv = wv->next, id++)
+  /* Clean up the menu bar when filled by the entire menu trees.  */
+  if (deep_p)
     {
-      MenuHandle menu;
-      Str255 title;
+      dispose_menus (MAC_MENU_MENU_BAR, 0);
+      dispose_menus (MAC_MENU_MENU_BAR_SUB, 0);
+#if !TARGET_API_MAC_CARBON
+      title_changed_p = 1;
+#endif
+    }
 
+  /* Fill menu bar titles and submenus.  Reuse the existing menu bar
+     titles as much as possible to minimize redraw (if !deep_p).  */
+  submenu_id = min_menu_id[MAC_MENU_MENU_BAR_SUB];
+  for (id = min_menu_id[MAC_MENU_MENU_BAR];
+       wv != NULL && id < min_menu_id[MAC_MENU_MENU_BAR + 1];
+       wv = wv->next, id++)
+    {
       strncpy (title, wv->name, 255);
-      title[255] = 0;
+      title[255] = '\0';
       c2pstr (title);
-      menu = NewMenu (id, title);
+
+      menu = GetMenuHandle (id);
+      if (menu)
+       {
+#if TARGET_API_MAC_CARBON
+         Str255 old_title;
+
+         GetMenuTitle (menu, old_title);
+         if (!EqualString (title, old_title, false, false))
+           {
+#ifdef MAC_OSX
+             if (id + 1 == min_menu_id[MAC_MENU_MENU_BAR + 1]
+                 || GetMenuRef (id + 1) == NULL)
+               {
+                 /* This is a workaround for Mac OS X 10.5 where just
+                    calling SetMenuTitle fails to change the title of
+                    the last (Help) menu in the menu bar.  */
+                 DeleteMenu (id);
+                 DisposeMenu (menu);
+                 menu = NULL;
+               }
+             else
+#endif /* MAC_OSX */
+               SetMenuTitle (menu, title);
+           }
+#else  /* !TARGET_API_MAC_CARBON */
+         if (!EqualString (title, (*menu)->menuData, false, false))
+           {
+             DeleteMenu (id);
+             DisposeMenu (menu);
+             menu = NewMenu (id, title);
+             InsertMenu (menu, GetMenuHandle (id + 1) ? id + 1 : 0);
+             title_changed_p = 1;
+           }
+#endif  /* !TARGET_API_MAC_CARBON */
+       }
+
+      if (!menu)
+       {
+         menu = NewMenu (id, title);
+         InsertMenu (menu, 0);
+#if !TARGET_API_MAC_CARBON
+         title_changed_p = 1;
+#endif
+       }
 
       if (wv->contents)
-        fill_menu (menu, wv->contents);
+        submenu_id = fill_menu (menu, wv->contents, MAC_MENU_MENU_BAR_SUB,
+                               submenu_id);
+    }
 
-      InsertMenu (menu, 0);
+  if (id < min_menu_id[MAC_MENU_MENU_BAR + 1] && GetMenuHandle (id))
+    {
+      dispose_menus (MAC_MENU_MENU_BAR, id);
+#if !TARGET_API_MAC_CARBON
+      title_changed_p = 1;
+#endif
+    }
+
+#if !TARGET_API_MAC_CARBON
+  if (title_changed_p)
+    InvalMenuBar ();
+#endif
+}
+
+/* Dispose of menus that belong to KIND, and remove them from the menu
+   list.  ID is the lower bound of menu IDs that will be processed.  */
+
+static void
+dispose_menus (kind, id)
+     enum mac_menu_kind kind;
+     int id;
+{
+  for (id = max (id, min_menu_id[kind]); id < min_menu_id[kind + 1]; id++)
+    {
+      MenuHandle menu = GetMenuHandle (id);
+
+      if (menu == NULL)
+       break;
+      DeleteMenu (id);
+      DisposeMenu (menu);
     }
 }
 
 #endif /* HAVE_MENUS */
 
+/* Detect if a menu is currently active.  */
+
+int
+popup_activated ()
+{
+  return popup_activated_flag;
+}
+
+/* The following is used by delayed window autoselection.  */
+
+DEFUN ("menu-or-popup-active-p", Fmenu_or_popup_active_p, Smenu_or_popup_active_p, 0, 0, 0,
+       doc: /* Return t if a menu or popup dialog is active.  */)
+     ()
+{
+  /* Always return Qnil since menu selection functions do not return
+     until a selection has been made or cancelled.  */
+  return Qnil;
+}
 \f
 void
 syms_of_macmenu ()
@@ -2346,12 +3279,8 @@ syms_of_macmenu ()
   Qdebug_on_next_call = intern ("debug-on-next-call");
   staticpro (&Qdebug_on_next_call);
 
-  DEFVAR_LISP ("menu-updating-frame", &Vmenu_updating_frame,
-              doc: /* Frame for which we are updating a menu.
-The enable predicate for a menu command should check this variable.  */);
-  Vmenu_updating_frame = Qnil;
-
   defsubr (&Sx_popup_menu);
+  defsubr (&Smenu_or_popup_active_p);
 #ifdef HAVE_MENUS
   defsubr (&Sx_popup_dialog);
 #endif