(xdialog_show): Don't allow 9 buttons.
[bpt/emacs.git] / src / xmenu.c
index 6ed239d..c4c6b97 100644 (file)
@@ -1,5 +1,5 @@
 /* X Communication module for terminals which understand the X protocol.
-   Copyright (C) 1986, 1988, 1993, 1994 Free Software Foundation, Inc.
+   Copyright (C) 1986, 1988, 1993, 1994, 1996 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -15,7 +15,8 @@ GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with GNU Emacs; see the file COPYING.  If not, write to
-the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
 
 /* X pop-up deck-of-cards menu facility for gnuemacs.
  *
@@ -41,6 +42,7 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #include "keyboard.h"
 #include "blockinput.h"
 #include "puresize.h"
+#include "buffer.h"
 
 #ifdef MSDOS
 #include "msdos.h"
@@ -67,7 +69,9 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #include <X11/CoreP.h>
 #include <X11/StringDefs.h>
 #include <X11/Shell.h>
+#ifdef USE_LUCID
 #include <X11/Xaw/Paned.h>
+#endif /* USE_LUCID */
 #include "../lwlib/lwlib.h"
 #else /* not USE_X_TOOLKIT */
 #include "../oldXMenu/XMenu.h"
@@ -84,13 +88,23 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 
 Lisp_Object Qdebug_on_next_call;
 
+Lisp_Object Qmenu_alias;
+
 extern Lisp_Object Qmenu_enable;
 extern Lisp_Object Qmenu_bar;
 extern Lisp_Object Qmouse_click, Qevent_kind;
 
 extern Lisp_Object Vdefine_key_rebound_commands;
 
+extern Lisp_Object Voverriding_local_map;
+extern Lisp_Object Voverriding_local_map_menu_flag;
+
+extern Lisp_Object Qoverriding_local_map, Qoverriding_terminal_local_map;
+
+extern Lisp_Object Qmenu_bar_update_hook;
+
 #ifdef USE_X_TOOLKIT
+extern void set_frame_menubar ();
 extern void process_expose_from_menu ();
 extern XtAppContext Xt_app_con;
 
@@ -156,48 +170,43 @@ static int menu_items_submenu_depth;
    Xt on behalf of one of the widget sets.  */
 static int popup_activated_flag;
 
-/* This holds a Lisp vector
-   which contains frames that have menu bars.
-   Each frame that has a menu bar is found at some index in this vector
-   and the menu bar widget refers to the frame through that index.  */
-static Lisp_Object frame_vector;
-\f
-/* Return the index of FRAME in frame_vector.
-   If FRAME isn't in frame_vector yet, put it in,
-   lengthening the vector if necessary.  */
+static int next_menubar_widget_id;
 
-static int
-frame_vector_add_frame (f)
-     FRAME_PTR *f;
-{
-  int length = XVECTOR (frame_vector)->size;
-  int i, empty = -1;
-  Lisp_Object new, frame;
+/* 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.
 
-  XSETFRAME (frame, f);
+   I don't understand why this is needed, but it does seem to be
+   needed on Motif, according to Marcus Daniels <marcus@sysc.pdx.edu>.  */
 
-  for (i = 0; i < length; i++)
-    {
-      if (EQ (frame, XVECTOR (frame_vector)->contents[i]))
-       return i;
-      if (NILP (XVECTOR (frame_vector)->contents[i]))
-       empty = i;
-    }
+int pending_menu_activation;
+\f
+#ifdef USE_X_TOOLKIT
+
+/* Return the frame whose ->output_data.x->id equals ID, or 0 if none.  */
+
+static struct frame *
+menubar_id_to_frame (id)
+     LWLIB_ID id;
+{
+  Lisp_Object tail, frame;
+  FRAME_PTR f;
 
-  if (empty >= 0)
+  for (tail = Vframe_list; GC_CONSP (tail); tail = XCONS (tail)->cdr)
     {
-      XVECTOR (frame_vector)->contents[empty] = frame;
-      return empty;
+      frame = XCONS (tail)->car;
+      if (!GC_FRAMEP (frame))
+        continue;
+      f = XFRAME (frame);
+      if (f->output_data.nothing == 1)
+       continue;
+      if (f->output_data.x->id == id)
+       return f;
     }
-
-  new = Fmake_vector (make_number (length * 2), Qnil);
-  bcopy (XVECTOR (frame_vector)->contents,
-        XVECTOR (new)->contents, sizeof (Lisp_Object) * length);
-  
-  frame_vector = new;
-  XVECTOR (frame_vector)->contents[length] = frame;
-  return length;
+  return 0;
 }
+
+#endif
 \f
 /* Initialize the menu_items structure if we haven't already done so.
    Also mark it as currently empty.  */
@@ -398,7 +407,8 @@ menu_item_equiv_key (item_string, item1, descrip_ptr)
       /* If the command is an alias for another
         (such as easymenu.el and lmenu.el set it up),
         see if the original command name has equivalent keys.  */
-      if (SYMBOLP (def) && SYMBOLP (XSYMBOL (def)->function))
+      if (SYMBOLP (def) && SYMBOLP (XSYMBOL (def)->function)
+         && ! NILP (Fget (def, Qmenu_alias)))
        savedkey = Fwhere_is_internal (XSYMBOL (def)->function,
                                       Qnil, Qt, Qnil);
       else
@@ -676,7 +686,7 @@ single_keymap_panes (keymap, pane_name, prefix, notreal)
     }
 }
 \f
-/* Push all the panes and items of a menu decsribed by the
+/* Push all the panes and items of a menu described by the
    alist-of-alists MENU.
    This handles old-fashioned calls to x-popup-menu.  */
 
@@ -773,6 +783,7 @@ cached information about equivalent key sequences.")
   int for_click = 0;
   struct gcpro gcpro1;
 
+#ifdef HAVE_MENUS
   if (! NILP (position))
     {
       check_x ();
@@ -835,8 +846,8 @@ cached information about equivalent key sequences.")
          CHECK_LIVE_WINDOW (window, 0);
          f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
 
-         xpos = (FONT_WIDTH (f->display.x->font) * XWINDOW (window)->left);
-         ypos = (f->display.x->line_height * XWINDOW (window)->top);
+         xpos = (FONT_WIDTH (f->output_data.x->font) * XWINDOW (window)->left);
+         ypos = (f->output_data.x->line_height * XWINDOW (window)->top);
        }
       else
        /* ??? Not really clean; should be CHECK_WINDOW_OR_FRAME,
@@ -846,6 +857,7 @@ cached information about equivalent key sequences.")
       xpos += XINT (x);
       ypos += XINT (y);
     }
+#endif /* HAVE_MENUS */
 
   title = Qnil;
   GCPRO1 (title);
@@ -925,6 +937,7 @@ cached information about equivalent key sequences.")
       return Qnil;
     }
 
+#ifdef HAVE_MENUS
   /* Display them in a menu.  */
   BLOCK_INPUT;
 
@@ -935,11 +948,14 @@ cached information about equivalent key sequences.")
   discard_menu_items ();
 
   UNGCPRO;
+#endif /* HAVE_MENUS */
 
   if (error_name) error (error_name);
   return selection;
 }
 
+#ifdef HAVE_MENUS
+
 DEFUN ("x-popup-dialog", Fx_popup_dialog, Sx_popup_dialog, 2, 2, 0,
   "Pop up a dialog box and return user's selection.\n\
 POSITION specifies which frame to use.\n\
@@ -998,6 +1014,8 @@ on the left of the dialog box and all following items on the right.\n\
     }
   else if (WINDOWP (position) || FRAMEP (position))
     window = position;
+  else
+    window = Qnil;
 
   /* Decode where to put the menu.  */
 
@@ -1054,7 +1072,8 @@ on the left of the dialog box and all following items on the right.\n\
 #ifdef USE_X_TOOLKIT
 
 /* Loop in Xt until the menu pulldown or dialog popup has been
-   popped down (deactivated).
+   popped down (deactivated).  This is used for x-popup-menu
+   and x-popup-dialog; it is not used for the menu bar any more.
 
    NOTE: All calls to popup_get_selection should be protected
    with BLOCK_INPUT, UNBLOCK_INPUT wrappers.  */
@@ -1088,10 +1107,21 @@ popup_get_selection (initial_event, dpyinfo, id)
       /* Handle expose events for editor frames right away.  */
       if (event.type == Expose)
        process_expose_from_menu (event);
-      /* Make sure we don't consider buttons grabbed after menu goes.  */
+      /* Make sure we don't consider buttons grabbed after menu goes.
+        And make sure to deactivate for any ButtonRelease,
+        even if XtDispatchEvent doesn't do that.  */
       else if (event.type == ButtonRelease
               && dpyinfo->display == event.xbutton.display)
-       dpyinfo->grabbed &= ~(1 << event.xbutton.button);
+        {
+          dpyinfo->grabbed &= ~(1 << event.xbutton.button);
+          popup_activated_flag = 0;
+#ifdef USE_MOTIF /* Pretending that the event came from a 
+                   Btn1Down seems the only way to convince Motif to
+                   activate its callbacks; setting the XmNmenuPost
+                   isn't working. --marcus@sysc.pdx.edu.  */
+         event.xbutton.button = 1;
+#endif
+        }
       /* If the user presses a key, deactivate the menu.
         The user is likely to do that if we get wedged.  */
       else if (event.type == KeyPress
@@ -1100,13 +1130,23 @@ popup_get_selection (initial_event, dpyinfo, id)
          popup_activated_flag = 0;
          break;
        }
+      /* Button presses outside the menu also pop it down.  */
+      else if (event.type == ButtonPress
+              && event.xany.display == dpyinfo->display
+              && x_any_window_to_frame (dpyinfo, event.xany.window))
+       {
+         popup_activated_flag = 0;
+         break;
+       }
 
       /* Queue all events not for this popup,
-        except for Expose, which we've already handled.
+        except for Expose, which we've already handled, and ButtonRelease.
         Note that the X window is associated with the frame if this
         is a menu bar popup, but not if it's a dialog box.  So we use
         x_non_menubar_window_to_frame, not x_any_window_to_frame.  */
       if (event.type != Expose
+          && !(event.type == ButtonRelease
+               && dpyinfo->display == event.xbutton.display)
          && (event.xany.display != dpyinfo->display
              || x_non_menubar_window_to_frame (dpyinfo, event.xany.window)))
        {
@@ -1139,6 +1179,39 @@ popup_get_selection (initial_event, dpyinfo, id)
     }
 }
 
+/* Activate the menu bar of frame F.
+   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 use the X button-press event
+   that was saved in saved_menu_event.
+   That makes the toolkit do its thing.
+
+   But first we recompute the menu bar contents (the whole tree).
+
+   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.  */
+   
+x_activate_menubar (f)
+     FRAME_PTR f;
+{
+  if (!f->output_data.x->saved_menu_event->type)
+    return;
+
+  set_frame_menubar (f, 0, 1);
+  BLOCK_INPUT;
+  XtDispatchEvent ((XEvent *) f->output_data.x->saved_menu_event);
+  UNBLOCK_INPUT;
+#ifdef USE_MOTIF
+  if (f->output_data.x->saved_menu_event->type == ButtonRelease)
+    pending_menu_activation = 1;
+#endif
+  
+  /* Ignore this if we get it a second time.  */
+  f->output_data.x->saved_menu_event->type = 0;
+}
+
 /* Detect if a dialog or menu has been posted.  */
 
 int
@@ -1172,7 +1245,7 @@ menubar_selection_callback (widget, id, client_data)
      XtPointer client_data;
 {
   Lisp_Object prefix, entry;
-  FRAME_PTR f = XFRAME (XVECTOR (frame_vector)->contents[id]);
+  FRAME_PTR f = menubar_id_to_frame (id);
   Lisp_Object vector;
   Lisp_Object *subprefix_stack;
   int submenu_depth = 0;
@@ -1256,6 +1329,19 @@ popup_deactivate_callback (widget, id, client_data)
   popup_activated_flag = 0;
 }
 
+/* Allocate a widget_value, blocking input.  */
+
+widget_value *
+xmalloc_widget_value ()
+{
+  widget_value *value;
+
+  BLOCK_INPUT;
+  value = malloc_widget_value ();
+  UNBLOCK_INPUT;
+
+  return value;
+}
 
 /* This recursively calls free_widget_value on the tree of widgets.
    It must free all data that was malloc'ed for these widget_values.
@@ -1321,8 +1407,12 @@ single_submenu (item_key, item_name, maps)
      But don't make a pane that is empty--ignore that map instead.  */
   for (i = 0; i < len; i++)
     {
-      if (SYMBOLP (mapvec[i]))
+      if (SYMBOLP (mapvec[i])
+         || (CONSP (mapvec[i])
+             && NILP (Fkeymapp (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);
@@ -1336,7 +1426,7 @@ single_submenu (item_key, item_name, maps)
 
   submenu_stack
     = (widget_value **) alloca (menu_items_used * sizeof (widget_value *));
-  wv = malloc_widget_value ();
+  wv = xmalloc_widget_value ();
   wv->name = "menu";
   wv->value = 0;
   wv->enabled = 1;
@@ -1390,13 +1480,15 @@ single_submenu (item_key, item_name, maps)
             with its items as a submenu beneath it.  */
          if (strcmp (pane_string, ""))
            {
-             wv = malloc_widget_value ();
+             wv = xmalloc_widget_value ();
              if (save_wv)
                save_wv->next = wv;
              else
                first_wv->contents = wv;
              wv->name = pane_string;
-             if (!NILP (prefix))
+             /* 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->enabled = 1;
@@ -1415,7 +1507,7 @@ single_submenu (item_key, item_name, maps)
            = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_EQUIV_KEY];
          def = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_DEFINITION];
 
-         wv = malloc_widget_value ();
+         wv = xmalloc_widget_value ();
          if (prev_wv) 
            prev_wv->next = wv;
          else
@@ -1456,7 +1548,7 @@ static void
 update_frame_menubar (f)
      FRAME_PTR f;
 {
-  struct x_display *x = f->display.x;
+  struct x_output *x = f->output_data.x;
   int columns, rows;
   int menubar_changed;
   
@@ -1513,132 +1605,236 @@ update_frame_menubar (f)
    it is set the first time this is called, from initialize_frame_menubar.  */
 
 void
-set_frame_menubar (f, first_time)
+set_frame_menubar (f, first_time, deep_p)
      FRAME_PTR f;
      int first_time;
+     int deep_p;
 {
-  Widget menubar_widget = f->display.x->menubar_widget;
+  Widget menubar_widget = f->output_data.x->menubar_widget;
   Lisp_Object tail, items, frame;
   widget_value *wv, *first_wv, *prev_wv = 0;
   int i;
-  int id;
-  int count;
-  int specpdl_count = specpdl_ptr - specpdl;
-
-  count = inhibit_garbage_collection ();
-
-  specbind (Qinhibit_quit, Qt);
-  /* Don't let the debugger step into this code
-     because it is not reentrant.  */
-  specbind (Qdebug_on_next_call, Qnil);
-
-  id = frame_vector_add_frame (f);
+  LWLIB_ID id;
+
+  if (f->output_data.x->id == 0)
+    f->output_data.x->id = next_menubar_widget_id++;
+  id = f->output_data.x->id;
+
+  if (! menubar_widget)
+    deep_p = 1;
+  else if (pending_menu_activation && !deep_p)
+    deep_p = 1;
+  /* Make the first call for any given frame always go deep.  */
+  else if (!f->output_data.x->saved_menu_event && !deep_p)
+    {
+      deep_p = 1;
+      f->output_data.x->saved_menu_event = (XEvent*)xmalloc (sizeof (XEvent));
+      f->output_data.x->saved_menu_event->type = 0;
+    }
 
-  wv = malloc_widget_value ();
+  wv = xmalloc_widget_value ();
   wv->name = "menubar";
   wv->value = 0;
   wv->enabled = 1;
   first_wv = wv;
-  items = FRAME_MENU_BAR_ITEMS (f);
-  menu_items = f->menu_bar_vector;
-  menu_items_allocated = XVECTOR (menu_items)->size;
-  init_menu_items ();
 
-  for (i = 0; i < XVECTOR (items)->size; i += 3)
+  if (deep_p)
     {
-      Lisp_Object key, string, maps;
+      /* Make a widget-value tree representing the entire menu trees.  */
+
+      struct buffer *prev = current_buffer;
+      Lisp_Object buffer;
+      int specpdl_count = specpdl_ptr - specpdl;
+      int previous_menu_items_used = f->menu_bar_items_used;
+      Lisp_Object *previous_items
+       = (Lisp_Object *) alloca (previous_menu_items_used
+                                 * sizeof (Lisp_Object));
+
+      buffer = XWINDOW (FRAME_SELECTED_WINDOW (f))->buffer;
+      specbind (Qinhibit_quit, Qt);
+      /* Don't let the debugger step into this code
+        because it is not reentrant.  */
+      specbind (Qdebug_on_next_call, Qnil);
+
+      record_unwind_protect (Fstore_match_data, Fmatch_data ());
+      if (NILP (Voverriding_local_map_menu_flag))
+       {
+         specbind (Qoverriding_terminal_local_map, Qnil);
+         specbind (Qoverriding_local_map, Qnil);
+       }
 
-      key = XVECTOR (items)->contents[i];
-      string = XVECTOR (items)->contents[i + 1];
-      maps = XVECTOR (items)->contents[i + 2];
-      if (NILP (string))
-       break;
+      set_buffer_internal_1 (XBUFFER (buffer));
 
-      wv = single_submenu (key, string, maps);
-      if (prev_wv) 
-       prev_wv->next = wv;
-      else
-       first_wv->contents = wv;
-      /* Don't set wv->name here; GC during the loop might relocate it.  */
-      wv->enabled = 1;
-      prev_wv = wv;
-    }
+      /* Run the Lucid hook.  */
+      call1 (Vrun_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))
+       call0 (Qrecompute_lucid_menubar);
+      safe_run_hooks (Qmenu_bar_update_hook);
+      FRAME_MENU_BAR_ITEMS (f) = menu_bar_items (FRAME_MENU_BAR_ITEMS (f));
 
-  /* Now GC cannot happen during the lifetime of the widget_value,
-     so it's safe to store data from a Lisp_String.  */
-  wv = first_wv->contents;
-  for (i = 0; i < XVECTOR (items)->size; i += 3)
-    {
-      Lisp_Object string;
-      string = XVECTOR (items)->contents[i + 1];
-      if (NILP (string))
-       break;
-      wv->name = (char *) XSTRING (string)->data;
-      wv = wv->next;
+      items = FRAME_MENU_BAR_ITEMS (f);
+
+      inhibit_garbage_collection ();
+
+      /* Save the frame's previous menu bar contents data.  */
+      bcopy (XVECTOR (f->menu_bar_vector)->contents, previous_items,
+            previous_menu_items_used * sizeof (Lisp_Object));
+
+      /* Fill in the current menu bar contents.  */
+      menu_items = f->menu_bar_vector;
+      menu_items_allocated = XVECTOR (menu_items)->size;
+      init_menu_items ();
+      for (i = 0; i < XVECTOR (items)->size; i += 4)
+       {
+         Lisp_Object key, string, maps;
+
+         key = XVECTOR (items)->contents[i];
+         string = XVECTOR (items)->contents[i + 1];
+         maps = XVECTOR (items)->contents[i + 2];
+         if (NILP (string))
+           break;
+
+         wv = single_submenu (key, string, maps);
+         if (prev_wv) 
+           prev_wv->next = wv;
+         else
+           first_wv->contents = wv;
+         /* Don't set wv->name here; GC during the loop might relocate it.  */
+         wv->enabled = 1;
+         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.  */
+
+      for (i = 0; i < previous_menu_items_used; i++)
+       if (menu_items_used == i
+           || (previous_items[i] != XVECTOR (menu_items)->contents[i]))
+         break;
+      if (i == menu_items_used && i == previous_menu_items_used && i != 0)
+       {
+         free_menubar_widget_value_tree (first_wv);
+         menu_items = Qnil;
+
+         return;
+       }
+
+      /* Now GC cannot happen during the lifetime of the widget_value,
+        so it's safe to store data from a Lisp_String.  */
+      wv = first_wv->contents;
+      for (i = 0; i < XVECTOR (items)->size; i += 4)
+       {
+         Lisp_Object string;
+         string = XVECTOR (items)->contents[i + 1];
+         if (NILP (string))
+           break;
+         wv->name = (char *) XSTRING (string)->data;
+         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.  */
 
-  finish_menu_items ();
+      items = FRAME_MENU_BAR_ITEMS (f);
+      for (i = 0; i < XVECTOR (items)->size; i += 4)
+       {
+         Lisp_Object string;
 
-  f->menu_bar_vector = menu_items;
-  f->menu_bar_items_used = menu_items_used;
-  menu_items = Qnil;
+         string = XVECTOR (items)->contents[i + 1];
+         if (NILP (string))
+           break;
 
-  unbind_to (count, Qnil);
+         wv = xmalloc_widget_value ();
+         wv->name = (char *) XSTRING (string)->data;
+         wv->value = 0;
+         wv->enabled = 1;
+         /* This prevents lwlib from assuming this
+            menu item is really supposed to be empty.  */
+         /* The EMACS_INT cast avoids a warning.
+            This value just has to be different from small integers.  */
+         wv->call_data = (void *) (EMACS_INT) (-1);
+
+         if (prev_wv) 
+           prev_wv->next = wv;
+         else
+           first_wv->contents = wv;
+         prev_wv = wv;
+       }
+
+      /* Forget what we thought we knew about what is in the
+        detailed contents of the menu bar menus.
+        Changing the top level always destroys the contents.  */
+      f->menu_bar_items_used = 0;
+    }
+
+  /* Create or update the menu bar widget.  */
 
   BLOCK_INPUT;
 
   if (menubar_widget)
     {
       /* Disable resizing (done for Motif!) */
-      lw_allow_resizing (f->display.x->widget, False);
+      lw_allow_resizing (f->output_data.x->widget, False);
 
       /* The third arg is DEEP_P, which says to consider the entire
         menu trees we supply, rather than just the menu bar item names.  */
-      lw_modify_all_widgets ((LWLIB_ID) id, first_wv, 1);
+      lw_modify_all_widgets (id, first_wv, deep_p);
 
       /* Re-enable the edit widget to resize.  */
-      lw_allow_resizing (f->display.x->widget, True);
+      lw_allow_resizing (f->output_data.x->widget, True);
     }
   else
     {
-      menubar_widget = lw_create_widget ("menubar", "menubar", 
-                                        (LWLIB_ID) id, first_wv, 
-                                        f->display.x->column_widget,
+      menubar_widget = lw_create_widget ("menubar", "menubar", id, first_wv, 
+                                        f->output_data.x->column_widget,
                                         0,
                                         popup_activate_callback,
                                         menubar_selection_callback,
                                         popup_deactivate_callback);
-      f->display.x->menubar_widget = menubar_widget;
+      f->output_data.x->menubar_widget = menubar_widget;
     }
 
   {
     int menubar_size 
-      = (f->display.x->menubar_widget
-        ? (f->display.x->menubar_widget->core.height
-           + f->display.x->menubar_widget->core.border_width)
+      = (f->output_data.x->menubar_widget
+        ? (f->output_data.x->menubar_widget->core.height
+           + f->output_data.x->menubar_widget->core.border_width)
         : 0);
 
+#ifdef USE_LUCID
     if (FRAME_EXTERNAL_MENU_BAR (f))
       {
         Dimension ibw = 0;
-        XtVaGetValues (f->display.x->column_widget,
+        XtVaGetValues (f->output_data.x->column_widget,
                       XtNinternalBorderWidth, &ibw, NULL);
         menubar_size += ibw;
       }
+#endif /* USE_LUCID */
 
-    f->display.x->menubar_height = menubar_size;
+    f->output_data.x->menubar_height = menubar_size;
   }
   
   free_menubar_widget_value_tree (first_wv);
 
   update_frame_menubar (f);
 
-  unbind_to (specpdl_count, Qnil);
-
   UNBLOCK_INPUT;
 }
 
-/* Called from Fx_create_frame to create the inital menubar of a frame
+/* 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.  */
@@ -1650,7 +1846,7 @@ initialize_frame_menubar (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);
+  set_frame_menubar (f, 1, 1);
 }
 
 /* Get rid of the menu bar of frame F, and free its storage.
@@ -1663,14 +1859,12 @@ free_frame_menubar (f)
   Widget menubar_widget;
   int id;
 
-  menubar_widget = f->display.x->menubar_widget;
+  menubar_widget = f->output_data.x->menubar_widget;
   
   if (menubar_widget)
     {
-      id = frame_vector_add_frame (f);
       BLOCK_INPUT;
-      lw_destroy_all_widgets ((LWLIB_ID) id);
-      XVECTOR (frame_vector)->contents[id] = Qnil;
+      lw_destroy_all_widgets ((LWLIB_ID) f->output_data.x->id);
       UNBLOCK_INPUT;
     }
 }
@@ -1685,7 +1879,7 @@ free_frame_menubar (f)
 /* F is the frame the menu is for.
    X and Y are the frame-relative specified position,
    relative to the inside upper left corner of the frame F.
-   FOR_CLICK if this menu was invoked for a mouse click.
+   FOR_CLICK is nonzero if this menu was invoked for a mouse click.
    KEYMAPS is 1 if this menu was specified with keymaps;
     in that case, we return a list containing the chosen item's value
     and perhaps also the pane's prefix.
@@ -1699,10 +1893,10 @@ free_frame_menubar (f)
    library.
 
    For the main windows, and popup menus, we use this counter,
-   which we increment each time after use.
+   which we increment each time after use.  This starts from 1<<16.
 
-   For menu bars, we use the index of the frame in frame_vector
-   as the id.  */
+   For menu bars, we use numbers starting at 0, counted in
+   next_menubar_widget_id.  */
 LWLIB_ID widget_id_tick;
 
 #ifdef __STDC__
@@ -1733,7 +1927,7 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
   int i;
   LWLIB_ID menu_id;
   Widget menu;
-  Arg av [2];
+  Arg av[2];
   int ac = 0;
   widget_value *wv, *save_wv = 0, *first_wv = 0, *prev_wv = 0;
   widget_value **submenu_stack
@@ -1741,8 +1935,7 @@ xmenu_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;
-
-  Position root_x, root_y;
+  XButtonPressedEvent dummy;
 
   int first_pane;
   int next_release_must_exit = 0;
@@ -1757,7 +1950,7 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
 
   /* Create a tree of widget_value objects
      representing the panes and their items.  */
-  wv = malloc_widget_value ();
+  wv = xmalloc_widget_value ();
   wv->name = "menu";
   wv->value = 0;
   wv->enabled = 1;
@@ -1809,7 +2002,7 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
             with its items as a submenu beneath it.  */
          if (!keymaps && strcmp (pane_string, ""))
            {
-             wv = malloc_widget_value ();
+             wv = xmalloc_widget_value ();
              if (save_wv)
                save_wv->next = wv;
              else
@@ -1840,7 +2033,7 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
            = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_EQUIV_KEY];
          def = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_DEFINITION];
 
-         wv = malloc_widget_value ();
+         wv = xmalloc_widget_value ();
          if (prev_wv) 
            prev_wv->next = wv;
          else 
@@ -1864,9 +2057,9 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
   /* Deal with the title, if it is non-nil.  */
   if (!NILP (title))
     {
-      widget_value *wv_title = malloc_widget_value ();
-      widget_value *wv_sep1 = malloc_widget_value ();
-      widget_value *wv_sep2 = malloc_widget_value ();
+      widget_value *wv_title = xmalloc_widget_value ();
+      widget_value *wv_sep1 = xmalloc_widget_value ();
+      widget_value *wv_sep2 = xmalloc_widget_value ();
 
       wv_sep2->name = "--";
       wv_sep2->next = first_wv->contents;
@@ -1883,10 +2076,59 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
   /* Actually create the menu.  */
   menu_id = widget_id_tick++;
   menu = lw_create_widget ("popup", first_wv->name, menu_id, first_wv,
-                          f->display.x->widget, 1, 0,
+                          f->output_data.x->widget, 1, 0,
                           popup_selection_callback,
                           popup_deactivate_callback);
 
+  /* Adjust coordinates to relative to the outer (window manager) window.  */
+  {
+    Window child;
+    int win_x = 0, win_y = 0;
+
+    /* Find the position of the outside upper-left corner of
+       the inner window, with respect to the outer window.  */
+    if (f->output_data.x->parent_desc != FRAME_X_DISPLAY_INFO (f)->root_window)
+      {
+       BLOCK_INPUT;
+       XTranslateCoordinates (FRAME_X_DISPLAY (f),
+
+                              /* From-window, to-window.  */
+                              f->output_data.x->window_desc,
+                              f->output_data.x->parent_desc,
+
+                              /* From-position, to-position.  */
+                              0, 0, &win_x, &win_y,
+
+                              /* Child of window.  */
+                              &child);
+       UNBLOCK_INPUT;
+       x += win_x;
+       y += win_y;
+      }
+  }
+
+  /* Adjust coordinates to be root-window-relative.  */
+  x += f->output_data.x->left_pos;
+  y += f->output_data.x->top_pos;
+
+  dummy.type = ButtonPress;
+  dummy.serial = 0;
+  dummy.send_event = 0;
+  dummy.display = FRAME_X_DISPLAY (f);
+  dummy.time = CurrentTime;
+  dummy.root = FRAME_X_DISPLAY_INFO (f)->root_window;
+  dummy.window = dummy.root;
+  dummy.subwindow = dummy.root;
+  dummy.x_root = x;
+  dummy.y_root = y;
+  dummy.x = x;
+  dummy.y = y;
+  dummy.state = (FRAME_X_DISPLAY_INFO (f)->grabbed >> 1) * Button1Mask;
+  dummy.button = 0;
+  for (i = 0; i < 5; i++)
+    if (FRAME_X_DISPLAY_INFO (f)->grabbed & (1 << i))
+      dummy.button = i;
+
   /* Don't allow any geometry request from the user.  */
   XtSetArg (av[ac], XtNgeometry, 0); ac++;
   XtSetValues (menu, av, ac);
@@ -1898,7 +2140,7 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
   menu_item_selection = 0;
 
   /* Display the menu.  */
-  lw_popup_menu (menu);
+  lw_popup_menu (menu, &dummy);
   popup_activated_flag = 1;
 
   /* Process events that apply to the menu.  */
@@ -1937,6 +2179,10 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
                = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
              i += MENU_ITEMS_PANE_LENGTH;
            }
+         /* Ignore a nil in the item list.
+            It's meaningful only for dialog boxes.  */
+         else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
+           i += 1;
          else
            {
              entry
@@ -2020,7 +2266,7 @@ xdialog_show (f, keymaps, title, error)
     prefix = XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_PREFIX];
     pane_string = (NILP (pane_name)
                   ? "" : (char *) XSTRING (pane_name)->data);  
-    prev_wv = malloc_widget_value ();
+    prev_wv = xmalloc_widget_value ();
     prev_wv->value = pane_string;
     if (keymaps && !NILP (prefix))
       prev_wv->name++;
@@ -2054,14 +2300,14 @@ xdialog_show (f, keymaps, title, error)
            i++;
            continue;
          }
-       if (nb_buttons >= 10)
+       if (nb_buttons >= 9)
          {
            free_menubar_widget_value_tree (first_wv);
            *error = "Too many dialog items";
            return Qnil;
          }
 
-       wv = malloc_widget_value ();
+       wv = xmalloc_widget_value ();
        prev_wv->next = wv;
        wv->name = (char *) button_names[nb_buttons];
        if (!NILP (descrip))
@@ -2083,7 +2329,7 @@ xdialog_show (f, keymaps, title, error)
     if (! boundary_seen)
       left_count = nb_buttons - nb_buttons / 2;
 
-    wv = malloc_widget_value ();
+    wv = xmalloc_widget_value ();
     wv->name = dialog_name;
 
     /* Dialog boxes use a really stupid name encoding
@@ -2104,7 +2350,7 @@ xdialog_show (f, keymaps, title, error)
   /* Actually create the dialog.  */
   dialog_id = widget_id_tick++;
   menu = lw_create_widget (first_wv->name, "dialog", dialog_id, first_wv,
-                          f->display.x->widget, 1, 0,
+                          f->output_data.x->widget, 1, 0,
                           dialog_selection_callback, 0);
   lw_modify_all_widgets (dialog_id, first_wv->contents, True);
   /* Free the widget_value objects we used to specify the contents.  */
@@ -2215,14 +2461,14 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
 
     /* Find the position of the outside upper-left corner of
        the inner window, with respect to the outer window.  */
-    if (f->display.x->parent_desc != FRAME_X_DISPLAY_INFO (f)->root_window)
+    if (f->output_data.x->parent_desc != FRAME_X_DISPLAY_INFO (f)->root_window)
       {
        BLOCK_INPUT;
        XTranslateCoordinates (FRAME_X_DISPLAY (f),
 
                               /* From-window, to-window.  */
-                              f->display.x->window_desc,
-                              f->display.x->parent_desc,
+                              f->output_data.x->window_desc,
+                              f->output_data.x->parent_desc,
 
                               /* From-position, to-position.  */
                               0, 0, &win_x, &win_y,
@@ -2237,8 +2483,8 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
 #endif /* HAVE_X_WINDOWS */
 
   /* Adjust coordinates to be root-window-relative.  */
-  x += f->display.x->left_pos;
-  y += f->display.x->top_pos;
+  x += f->output_data.x->left_pos;
+  y += f->output_data.x->top_pos;
  
   /* Create all the necessary panes and their items.  */
   i = 0;
@@ -2443,22 +2689,27 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
 }
 
 #endif /* not USE_X_TOOLKIT */
+
+#endif /* HAVE_MENUS */
 \f
 syms_of_xmenu ()
 {
   staticpro (&menu_items);
   menu_items = Qnil;
 
+  Qmenu_alias = intern ("menu-alias");
+  staticpro (&Qmenu_alias);
+
   Qdebug_on_next_call = intern ("debug-on-next-call");
   staticpro (&Qdebug_on_next_call);
 
 #ifdef USE_X_TOOLKIT
   widget_id_tick = (1<<16);    
+  next_menubar_widget_id = 1;
 #endif
 
-  staticpro (&frame_vector);
-  frame_vector = Fmake_vector (make_number (10), Qnil);
-
   defsubr (&Sx_popup_menu);
+#ifdef HAVE_MENUS
   defsubr (&Sx_popup_dialog);
+#endif
 }