]> code.delx.au - gnu-emacs/blobdiff - src/macmenu.c
Add 2008 to copyright years.
[gnu-emacs] / src / macmenu.c
index 8cce41114de709f1012f7aebdb156dd18bfe1c25..e65d3863207a794aeef49e131d851a2569bb6d3c 100644 (file)
@@ -1,12 +1,12 @@
 /* Menu support for GNU Emacs on Mac OS.
    Copyright (C) 2000, 2001, 2002, 2003, 2004,
-                 2005, 2006 Free Software Foundation, Inc.
+                 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,
@@ -62,17 +62,26 @@ Boston, MA 02110-1301, 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 */
@@ -151,10 +160,10 @@ typedef struct _widget_value
 #define FALSE 0
 #endif /* no TRUE */
 
-Lisp_Object Vmenu_updating_frame;
-
 Lisp_Object Qdebug_on_next_call;
 
+extern Lisp_Object Vmenu_updating_frame;
+
 extern Lisp_Object Qmenu_bar, Qmac_apple_event;
 
 extern Lisp_Object QCtoggle, QCradio;
@@ -189,9 +198,11 @@ static void single_keymap_panes P_ ((Lisp_Object, Lisp_Object, Lisp_Object,
 static void list_of_panes P_ ((Lisp_Object));
 static void list_of_items P_ ((Lisp_Object));
 
-static int fill_menu P_ ((MenuHandle, widget_value *, int));
+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_ ((int));
+static void dispose_menus P_ ((enum mac_menu_kind, int));
 
 \f
 /* This holds a Lisp vector that holds the results of decoding
@@ -248,6 +259,9 @@ static int menu_items_n_panes;
 /* Current depth within submenus.  */
 static int menu_items_submenu_depth;
 
+/* Nonzero means a menu is currently active.  */
+static int popup_activated_flag;
+
 /* 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.
@@ -296,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
@@ -306,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));
@@ -595,6 +642,7 @@ 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,
@@ -832,6 +880,32 @@ no quit occurs and `x-popup-menu' returns nil.  */)
 
 #ifdef HAVE_MENUS
 
+/* 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.
@@ -917,6 +991,101 @@ for instance using the window manager, then this produces a quit and
        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.  */
@@ -974,39 +1143,65 @@ 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))
        {
@@ -1064,13 +1259,11 @@ menubar_selection_callback (FRAME_PTR f, int client_data)
              buf.arg = entry;
              kbd_buffer_store_event (&buf);
 
-             f->output_data.mac->menubar_active = 0;
              return;
            }
          i += MENU_ITEMS_ITEM_LENGTH;
        }
     }
-  f->output_data.mac->menubar_active = 0;
 }
 
 /* Allocate a widget_value, blocking input.  */
@@ -1178,6 +1371,7 @@ digest_single_submenu (start, end, top_level_items)
   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 *));
@@ -1224,6 +1418,8 @@ digest_single_submenu (start, end, top_level_items)
          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];
 
@@ -1271,6 +1467,10 @@ digest_single_submenu (start, end, top_level_items)
          Lisp_Object item_name, enable, descrip, def, type, selected;
          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);
          descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY);
@@ -1377,30 +1577,103 @@ update_submenu_strings (first_wv)
 }
 
 \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).  */
 
-#ifdef HAVE_CANCELMENUTRACKING
+#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;
-  extern int mac_quit_char_modifiers;
-  extern int mac_quit_char_keycode;
 
-  GetEventParameter (theEvent, kEventParamKeyCode,
-                     typeUInt32, NULL, sizeof(UInt32), NULL, &keyCode);
+  err = GetEventParameter (theEvent, kEventParamKeyCode,
+                          typeUInt32, NULL, sizeof(UInt32), NULL, &keyCode);
 
-  GetEventParameter (theEvent, kEventParamKeyModifiers,
-                     typeUInt32, NULL, sizeof(UInt32),
-                     NULL, &keyModifiers);
+  if (err == noErr)
+    err = GetEventParameter (theEvent, kEventParamKeyModifiers,
+                            typeUInt32, NULL, sizeof(UInt32),
+                            NULL, &keyModifiers);
 
-  if (keyCode == mac_quit_char_keycode
-      && keyModifiers == mac_quit_char_modifiers)
+  if (err == noErr && mac_quit_char_key_p (keyModifiers, keyCode))
     {
       MenuRef menu = userData != 0
         ? (MenuRef)userData : AcquireRootMenu ();
@@ -1412,39 +1685,38 @@ menu_quit_handler (nextHandler, theEvent, userData)
 
   return CallNextEventHandler (nextHandler, theEvent);
 }
-#endif /* HAVE_CANCELMENUTRACKING */
+#endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 1030 */
 
-/* Add event handler for MENU_HANDLE so we can detect C-g.
-   If MENU_HANDLE is NULL, install handler for all menus in the menu bar.
+/* 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 (MenuHandle menu_handle)
+install_menu_quit_handler (kind, menu_handle)
+     enum mac_menu_kind kind;
+     MenuHandle menu_handle;
 {
-#ifdef HAVE_CANCELMENUTRACKING
-  EventTypeSpec typesList[] = { { kEventClassKeyboard, kEventRawKeyDown } };
-  int i = MIN_MENU_ID;
-  MenuHandle menu = menu_handle ? menu_handle : GetMenuHandle (i);
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1030
+  static const EventTypeSpec typesList[] =
+    {{kEventClassKeyboard, kEventRawKeyDown}};
+  int id;
 
-  while (menu != NULL)
+#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++)
     {
-      InstallMenuEventHandler (menu, menu_quit_handler,
-                              GetEventTypeCount (typesList),
-                               typesList, menu_handle, NULL);
-      if (menu_handle) break;
-      menu = GetMenuHandle (++i);
-    }
+      MenuHandle menu = GetMenuHandle (id);
 
-  i = menu_handle ? MIN_POPUP_SUBMENU_ID : MIN_SUBMENU_ID;
-  menu = GetMenuHandle (i);
-  while (menu != NULL)
-    {
+      if (menu == NULL)
+       break;
       InstallMenuEventHandler (menu, menu_quit_handler,
                               GetEventTypeCount (typesList),
-                               typesList, menu_handle, NULL);
-      menu = GetMenuHandle (++i);
+                              typesList, menu_handle, NULL);
     }
-#endif /* HAVE_CANCELMENUTRACKING */
+#endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 1030 */
 }
 
 /* Set the contents of the menubar widgets of frame F.
@@ -1464,10 +1736,6 @@ set_frame_menubar (f, first_time, deep_p)
   int *submenu_start, *submenu_end;
   int *submenu_top_level_items, *submenu_n_panes;
 
-  /* We must not change the menubar when actually in use.  */
-  if (f->output_data.mac->menubar_active)
-    return;
-
   XSETFRAME (Vmenu_updating_frame, f);
 
   if (! menubar_widget)
@@ -1526,6 +1794,8 @@ set_frame_menubar (f, first_time, deep_p)
 
       /* 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 *));
@@ -1585,23 +1855,33 @@ set_frame_menubar (f, first_time, deep_p)
        }
 
       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
            || (!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);
          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.  */
       wv = first_wv->contents;
@@ -1616,9 +1896,6 @@ set_frame_menubar (f, first_time, deep_p)
          wv = wv->next;
        }
 
-      f->menu_bar_vector = menu_items;
-      f->menu_bar_items_used = menu_items_used;
-      discard_menu_items ();
     }
   else
     {
@@ -1677,7 +1954,8 @@ set_frame_menubar (f, first_time, deep_p)
   fill_menubar (first_wv->contents, deep_p);
 
   /* Add event handler so we can detect C-g. */
-  install_menu_quit_handler (NULL);
+  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;
@@ -1700,7 +1978,7 @@ pop_down_menu (arg)
 {
   struct Lisp_Save_Value *p = XSAVE_VALUE (arg);
   FRAME_PTR f = p->pointer;
-  MenuHandle menu = GetMenuHandle (POPUP_SUBMENU_ID);
+  MenuHandle menu = GetMenuHandle (min_menu_id[MAC_MENU_POPUP]);
 
   BLOCK_INPUT;
 
@@ -1709,8 +1987,8 @@ pop_down_menu (arg)
   FRAME_MAC_DISPLAY_INFO (f)->grabbed = 0;
 
   /* delete all menus */
-  dispose_menus (MIN_POPUP_SUBMENU_ID);
-  DeleteMenu (POPUP_SUBMENU_ID);
+  dispose_menus (MAC_MENU_POPUP_SUB, 0);
+  DeleteMenu (min_menu_id[MAC_MENU_POPUP]);
   DisposeMenu (menu);
 
   UNBLOCK_INPUT;
@@ -1744,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;
@@ -1944,9 +2221,10 @@ mac_menu_show (f, x, y, for_click, keymaps, title, error)
     }
 
   /* Actually create the menu.  */
-  menu = NewMenu (POPUP_SUBMENU_ID, "\p");
+  menu = NewMenu (min_menu_id[MAC_MENU_POPUP], "\p");
   InsertMenu (menu, -1);
-  fill_menu (menu, first_wv->contents, MIN_POPUP_SUBMENU_ID);
+  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.  */
@@ -1960,30 +2238,30 @@ mac_menu_show (f, x, y, for_click, keymaps, title, error)
   LocalToGlobal (&pos);
 
   /* No selection has been chosen yet.  */
-  menu_item_choice = 0;
   menu_item_selection = 0;
 
   record_unwind_protect (pop_down_menu, make_save_value (f, 0));
 
   /* Add event handler so we can detect C-g. */
-  install_menu_quit_handler (menu);
+  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)
+  if (menu_item_choice)
     {
       MenuHandle sel_menu = GetMenuHandle (HiWord (menu_item_choice));
-      if (sel_menu) {
-       GetMenuItemRefCon (sel_menu, menu_item_selection, &refcon);
-      }
+
+      if (sel_menu)
+       GetMenuItemRefCon (sel_menu, LoWord (menu_item_choice),
+                          &menu_item_selection);
     }
-  else if (! for_click)
-    /* Make "Cancel" equivalent to C-g unless this menu was popped up by
-       a mouse press.  */
-    Fsignal (Qquit, Qnil);
+
+  unbind_to (specpdl_count, Qnil);
 
   /* Find the selected item, and its pane, to return
      the proper value.  */
@@ -2020,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)
                    {
@@ -2043,15 +2321,397 @@ mac_menu_show (f, x, y, for_click, keymaps, title, error)
     /* Make "Cancel" equivalent to C-g.  */
     Fsignal (Qquit, Qnil);
 
-  unbind_to (specpdl_count, 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)
 {
@@ -2156,6 +2816,7 @@ mac_dialog (widget_value *wv)
 
   return i;
 }
+#endif  /* not TARGET_API_MAC_CARBON */
 
 static char * button_names [] = {
   "button1", "button2", "button3", "button4", "button5",
@@ -2287,11 +2948,16 @@ mac_dialog_show (f, keymaps, title, header, error_name)
     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.  */
@@ -2351,9 +3017,9 @@ mac_dialog_show (f, keymaps, title, header, error_name)
 /* 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++;
@@ -2403,6 +3069,10 @@ add_menu_item (menu, pos, wv)
         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);
@@ -2437,9 +3107,10 @@ add_menu_item (menu, pos, wv)
 /* Construct native Mac OS menu based on widget_value tree.  */
 
 static int
-fill_menu (menu, wv, submenu_id)
+fill_menu (menu, wv, kind, submenu_id)
      MenuHandle menu;
      widget_value *wv;
+     enum mac_menu_kind kind;
      int submenu_id;
 {
   int pos;
@@ -2447,13 +3118,13 @@ fill_menu (menu, wv, submenu_id)
   for (pos = 1; wv != NULL; wv = wv->next, pos++)
     {
       add_menu_item (menu, pos, wv);
-      if (wv->contents)
+      if (wv->contents && submenu_id < min_menu_id[kind + 1])
        {
          MenuHandle submenu = NewMenu (submenu_id, "\pX");
 
          InsertMenu (submenu, -1);
          SetMenuItemHierarchicalID (menu, pos, submenu_id);
-         submenu_id = fill_menu (submenu, wv->contents, submenu_id + 1);
+         submenu_id = fill_menu (submenu, wv->contents, kind, submenu_id + 1);
        }
     }
 
@@ -2477,8 +3148,8 @@ fill_menubar (wv, deep_p)
   /* Clean up the menu bar when filled by the entire menu trees.  */
   if (deep_p)
     {
-      dispose_menus (MIN_MENU_ID);
-      dispose_menus (MIN_SUBMENU_ID);
+      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
@@ -2486,8 +3157,10 @@ fill_menubar (wv, deep_p)
 
   /* 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_SUBMENU_ID;
-  for (id = MIN_MENU_ID; wv != NULL; wv = wv->next, id++)
+  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';
@@ -2501,7 +3174,22 @@ fill_menubar (wv, deep_p)
 
          GetMenuTitle (menu, old_title);
          if (!EqualString (title, old_title, false, false))
-           SetMenuTitle (menu, title);
+           {
+#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))
            {
@@ -2513,7 +3201,8 @@ fill_menubar (wv, deep_p)
            }
 #endif  /* !TARGET_API_MAC_CARBON */
        }
-      else
+
+      if (!menu)
        {
          menu = NewMenu (id, title);
          InsertMenu (menu, 0);
@@ -2523,12 +3212,13 @@ fill_menubar (wv, deep_p)
        }
 
       if (wv->contents)
-        submenu_id = fill_menu (menu, wv->contents, submenu_id);
+        submenu_id = fill_menu (menu, wv->contents, MAC_MENU_MENU_BAR_SUB,
+                               submenu_id);
     }
 
-  if (GetMenuHandle (id))
+  if (id < min_menu_id[MAC_MENU_MENU_BAR + 1] && GetMenuHandle (id))
     {
-      dispose_menus (id);
+      dispose_menus (MAC_MENU_MENU_BAR, id);
 #if !TARGET_API_MAC_CARBON
       title_changed_p = 1;
 #endif
@@ -2540,21 +3230,45 @@ fill_menubar (wv, deep_p)
 #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 (id)
+dispose_menus (kind, id)
+     enum mac_menu_kind kind;
      int id;
 {
-  MenuHandle menu;
-
-  while ((menu = GetMenuHandle (id)) != NULL)
+  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);
-      id++;
     }
 }
 
 #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 ()
@@ -2565,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