(menu_item_enabled_p): Fix declaration.
[bpt/emacs.git] / src / xmenu.c
index 5258888..ec0695a 100644 (file)
@@ -29,17 +29,18 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 
 /* Rewritten for clarity and GC protection by rms in Feb 94.  */
 
-#include <stdio.h>
-
 /* On 4.3 this loses if it comes after xterm.h.  */
 #include <signal.h>
 #include <config.h>
+
+#include <stdio.h>
 #include "lisp.h"
 #include "termhooks.h"
 #include "frame.h"
 #include "window.h"
 #include "keyboard.h"
 #include "blockinput.h"
+#include "puresize.h"
 
 /* This may include sys/types.h, and that somehow loses
    if this is not done before the other system files.  */
@@ -64,6 +65,7 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #include <X11/IntrinsicP.h>
 #include <X11/CoreP.h>
 #include <X11/StringDefs.h>
+#include <X11/Shell.h>
 #include <X11/Xaw/Paned.h>
 #include "../lwlib/lwlib.h"
 #include "../lwlib/xlwmenuP.h"
@@ -93,7 +95,7 @@ extern Lisp_Object Qmenu_bar;
 extern void process_expose_from_menu ();
 extern XtAppContext Xt_app_con;
 
-static int string_width ();
+static Lisp_Object xdialog_show ();
 #endif
 
 static Lisp_Object xmenu_show ();
@@ -113,6 +115,14 @@ static void list_of_items ();
    the item string, the enable flag, the item's value,
    and the equivalent keyboard key's description string.
 
+   In some cases, multiple levels of menus may be described.
+   A single vector slot containing nil indicates the start of a submenu.
+   A single vector slot containing lambda indicates the end of a submenu.
+   The submenu follows a menu item which is the way to reach the submenu.
+
+   A single vector slot containing quote indicates that the
+   following items should appear on the right of a dialog box.
+
    Using a Lisp vector to hold this information while we decode it
    takes care of protecting all the data from GC.  */
 
@@ -134,9 +144,13 @@ static int menu_items_allocated;
 /* This is the index in menu_items of the first empty slot.  */
 static int menu_items_used;
 
-/* The number of panes currently recorded in menu_items.  */
+/* The number of panes currently recorded in menu_items,
+   excluding those within submenus.  */
 static int menu_items_n_panes;
 
+/* Current depth within submenus.  */
+static int menu_items_submenu_depth;
+
 /* Initialize the menu_items structure if we haven't already done so.
    Also mark it as currently empty.  */
 
@@ -151,6 +165,7 @@ init_menu_items ()
 
   menu_items_used = 0;
   menu_items_n_panes = 0;
+  menu_items_submenu_depth = 0;
 }
 
 /* Call at the end of generating the data in menu_items.
@@ -176,6 +191,56 @@ discard_menu_items ()
     }
 }
 
+/* Make the menu_items vector twice as large.  */
+
+static void
+grow_menu_items ()
+{
+  Lisp_Object old;
+  int old_size = menu_items_allocated;
+  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));
+}
+
+/* Begin a submenu.  */
+
+static void
+push_submenu_start ()
+{
+  if (menu_items_used + 1 > menu_items_allocated)
+    grow_menu_items ();
+
+  XVECTOR (menu_items)->contents[menu_items_used++] = Qnil;
+  menu_items_submenu_depth++;
+}
+
+/* End a submenu.  */
+
+static void
+push_submenu_end ()
+{
+  if (menu_items_used + 1 > menu_items_allocated)
+    grow_menu_items ();
+
+  XVECTOR (menu_items)->contents[menu_items_used++] = Qlambda;
+  menu_items_submenu_depth--;
+}
+
+/* Indicate boundary between left and right.  */
+
+static void
+push_left_right_boundary ()
+{
+  if (menu_items_used + 1 > menu_items_allocated)
+    grow_menu_items ();
+
+  XVECTOR (menu_items)->contents[menu_items_used++] = Qquote;
+}
+
 /* Start a new menu pane in menu_items..
    NAME is the pane name.  PREFIX_VEC is a prefix key for this pane.  */
 
@@ -184,18 +249,10 @@ push_menu_pane (name, prefix_vec)
      Lisp_Object name, prefix_vec;
 {
   if (menu_items_used + MENU_ITEMS_PANE_LENGTH > menu_items_allocated)
-    {
-      Lisp_Object old;
-      int old_size = menu_items_allocated;
-      old = menu_items;
+    grow_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));
-    }
-
-  menu_items_n_panes++;
+  if (menu_items_submenu_depth == 0)
+    menu_items_n_panes++;
   XVECTOR (menu_items)->contents[menu_items_used++] = Qt;
   XVECTOR (menu_items)->contents[menu_items_used++] = name;
   XVECTOR (menu_items)->contents[menu_items_used++] = prefix_vec;
@@ -212,16 +269,7 @@ push_menu_item (name, enable, key, equiv)
      Lisp_Object name, enable, key, equiv;
 {
   if (menu_items_used + MENU_ITEMS_ITEM_LENGTH > menu_items_allocated)
-    {
-      Lisp_Object old;
-      int old_size = menu_items_allocated;
-      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));
-    }
+    grow_menu_items ();
 
   XVECTOR (menu_items)->contents[menu_items_used++] = name;
   XVECTOR (menu_items)->contents[menu_items_used++] = enable;
@@ -252,6 +300,7 @@ menu_item_equiv_key (item_string, item1, descrip_ptr)
   Lisp_Object savedkey, descrip;
   Lisp_Object def1;
   int changed = 0;
+  struct gcpro gcpro1, gcpro2, gcpro3, gcpro4;
 
   /* If a help string follows the item string, skip it.  */
   if (CONSP (XCONS (item1)->cdr)
@@ -272,12 +321,19 @@ menu_item_equiv_key (item_string, item1, descrip_ptr)
       descrip = XCONS (cachelist)->cdr;
     }
 
+  GCPRO4 (def, def1, savedkey, descrip);
+
   /* Is it still valid?  */
   def1 = Qnil;
   if (!NILP (savedkey))
     def1 = Fkey_binding (savedkey, Qnil);
   /* If not, update it.  */
   if (! EQ (def1, def)
+      /* If the command is an alias for another
+         (such as easymenu.el and lmenu.el set it up),
+         check if the original command matches the cached command. */
+      && !(SYMBOLP (def) && SYMBOLP (XSYMBOL (def)->function)
+           && EQ (def1, XSYMBOL (def)->function))
       /* If something had no key binding before, don't recheck it--
         doing that takes too much time and makes menus too slow.  */
       && !(!NILP (cachelist) && NILP (savedkey)))
@@ -285,6 +341,13 @@ menu_item_equiv_key (item_string, item1, descrip_ptr)
       changed = 1;
       descrip = Qnil;
       savedkey = Fwhere_is_internal (def, Qnil, Qt, Qnil);
+      /* 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))
+       savedkey = Fwhere_is_internal (XSYMBOL (def)->function,
+                                      Qnil, Qt, Qnil);
+
       if (VECTORP (savedkey)
          && EQ (XVECTOR (savedkey)->contents[0], Qmenu_bar))
        savedkey = Qnil;
@@ -298,13 +361,17 @@ menu_item_equiv_key (item_string, item1, descrip_ptr)
 
   /* Cache the data we just got in a sublist of the menu binding.  */
   if (NILP (cachelist))
-    XCONS (item1)->cdr = Fcons (Fcons (savedkey, descrip), def);
+    {
+      CHECK_IMPURE (item1);
+      XCONS (item1)->cdr = Fcons (Fcons (savedkey, descrip), def);
+    }
   else if (changed)
     {
       XCONS (cachelist)->car = savedkey;
       XCONS (cachelist)->cdr = descrip;
     }
 
+  UNGCPRO;
   *descrip_ptr = descrip;
   return def;
 }
@@ -319,15 +386,19 @@ menu_item_enabled_p_1 (arg)
 }
 
 /* Return non-nil if the command DEF is enabled when used as a menu item.
-   This is based on looking for a menu-enable property.  */
+   This is based on looking for a menu-enable property.
+   If NOTREAL is set, don't bother really computing this.  */
 
 static Lisp_Object
-menu_item_enabled_p (def)
+menu_item_enabled_p (def, notreal)
      Lisp_Object def;
+     int notreal;
 {
   Lisp_Object enabled, tem;
 
   enabled = Qt;
+  if (notreal)
+    return enabled;
   if (XTYPE (def) == Lisp_Symbol)
     {
       /* No property, or nil, means enable.
@@ -343,12 +414,15 @@ menu_item_enabled_p (def)
 }
 \f
 /* Look through KEYMAPS, a vector of keymaps that is NMAPS long,
-   and generate menu panes for them in menu_items.  */
+   and generate menu panes for them in menu_items.
+   If NOTREAL is nonzero,
+   don't bother really computing whether an item is enabled.  */
 
 static void
-keymap_panes (keymaps, nmaps)
+keymap_panes (keymaps, nmaps, notreal)
      Lisp_Object *keymaps;
      int nmaps;
+     int notreal;
 {
   int mapno;
 
@@ -358,7 +432,7 @@ keymap_panes (keymaps, nmaps)
      But don't make a pane that is empty--ignore that map instead.
      P is the number of panes we have made so far.  */
   for (mapno = 0; mapno < nmaps; mapno++)
-    single_keymap_panes (keymaps[mapno], Qnil, Qnil);
+    single_keymap_panes (keymaps[mapno], Qnil, Qnil, notreal);
 
   finish_menu_items ();
 }
@@ -366,13 +440,16 @@ keymap_panes (keymaps, nmaps)
 /* This is a recursive subroutine of keymap_panes.
    It handles one keymap, KEYMAP.
    The other arguments are passed along
-   or point to local variables of the previous function.  */
+   or point to local variables of the previous function.
+   If NOTREAL is nonzero,
+   don't bother really computing whether an item is enabled.  */
 
 static void
-single_keymap_panes (keymap, pane_name, prefix)
+single_keymap_panes (keymap, pane_name, prefix, notreal)
      Lisp_Object keymap;
      Lisp_Object pane_name;
      Lisp_Object prefix;
+     int notreal;
 {
   Lisp_Object pending_maps;
   Lisp_Object tail, item, item1, item_string, table;
@@ -402,15 +479,18 @@ single_keymap_panes (keymap, pane_name, prefix)
                  Lisp_Object descrip;
                  Lisp_Object tem, enabled;
 
-                 def = menu_item_equiv_key (item_string, item1, &descrip);
-
-                 /* GCPRO because we will call eval.
+                 /* GCPRO because ...enabled_p will call eval
+                    and ..._equiv_key may autoload something.
                     Protecting KEYMAP preserves everything we use;
                     aside from that, must protect whatever might be
                     a string.  Since there's no GCPRO5, we refetch
                     item_string instead of protecting it.  */
+                 descrip = def = Qnil;
                  GCPRO4 (keymap, pending_maps, def, descrip);
-                 enabled = menu_item_enabled_p (def);
+
+                 def = menu_item_equiv_key (item_string, item1, &descrip);
+                 enabled = menu_item_enabled_p (def, notreal);
+
                  UNGCPRO;
 
                  item_string = XCONS (item1)->car;
@@ -420,8 +500,30 @@ single_keymap_panes (keymap, pane_name, prefix)
                    pending_maps = Fcons (Fcons (def, Fcons (item_string, XCONS (item)->car)),
                                          pending_maps);
                  else
-                   push_menu_item (item_string, enabled, XCONS (item)->car,
-                                   descrip);
+                   {
+                     Lisp_Object submap;
+                     GCPRO4 (keymap, pending_maps, descrip, item_string);
+                     submap = get_keymap_1 (def, 0, 1);
+                     UNGCPRO;
+#ifndef USE_X_TOOLKIT
+                     /* Indicate visually that this is a submenu.  */
+                     if (!NILP (submap))
+                       item_string = concat2 (item_string,
+                                              build_string (" >"));
+#endif
+                     push_menu_item (item_string, enabled, XCONS (item)->car,
+                                     descrip);
+#ifdef USE_X_TOOLKIT
+                     /* Display a submenu using the toolkit.  */
+                     if (! NILP (submap))
+                       {
+                         push_submenu_start ();
+                         single_keymap_panes (submap, Qnil,
+                                              XCONS (item)->car, notreal);
+                         push_submenu_end ();
+                       }
+#endif
+                   }
                }
            }
        }
@@ -447,15 +549,18 @@ single_keymap_panes (keymap, pane_name, prefix)
                      Lisp_Object descrip;
                      Lisp_Object tem, enabled;
 
-                     def = menu_item_equiv_key (item_string, item1, &descrip);
-
-                     /* GCPRO because we will call eval.
+                     /* GCPRO because ...enabled_p will call eval
+                        and ..._equiv_key may autoload something.
                         Protecting KEYMAP preserves everything we use;
                         aside from that, must protect whatever might be
                         a string.  Since there's no GCPRO5, we refetch
                         item_string instead of protecting it.  */
                      GCPRO4 (keymap, pending_maps, def, descrip);
-                     enabled = menu_item_enabled_p (def);
+                     descrip = def = Qnil;
+
+                     def = menu_item_equiv_key (item_string, item1, &descrip);
+                     enabled = menu_item_enabled_p (def, notreal);
+
                      UNGCPRO;
 
                      item_string = XCONS (item1)->car;
@@ -465,8 +570,28 @@ single_keymap_panes (keymap, pane_name, prefix)
                        pending_maps = Fcons (Fcons (def, Fcons (item_string, character)),
                                              pending_maps);
                      else
-                       push_menu_item (item_string, enabled,
-                                       character, descrip);
+                       {
+                         Lisp_Object submap;
+                         GCPRO4 (keymap, pending_maps, descrip, item_string);
+                         submap = get_keymap_1 (def, 0, 1);
+                         UNGCPRO;
+#ifndef USE_X_TOOLKIT
+                         if (!NILP (submap))
+                           item_string = concat2 (item_string,
+                                                  build_string (" >"));
+#endif
+                         push_menu_item (item_string, enabled, character,
+                                         descrip);
+#ifdef USE_X_TOOLKIT
+                         if (! NILP (submap))
+                           {
+                             push_submenu_start ();
+                             single_keymap_panes (submap, Qnil,
+                                                  character, notreal);
+                             push_submenu_end ();
+                           }
+#endif
+                       }
                    }
                }
            }
@@ -476,12 +601,14 @@ single_keymap_panes (keymap, pane_name, prefix)
   /* Process now any submenus which want to be panes at this level.  */
   while (!NILP (pending_maps))
     {
-      Lisp_Object elt, eltcdr;
+      Lisp_Object elt, eltcdr, string;
       elt = Fcar (pending_maps);
       eltcdr = XCONS (elt)->cdr;
-      single_keymap_panes (Fcar (elt),
-                          /* Fails to discard the @.  */
-                          XCONS (eltcdr)->car, XCONS (eltcdr)->cdr);
+      string = XCONS (eltcdr)->car;
+      /* We no longer discard the @ from the beginning of the string here.
+        Instead, we do this in xmenu_show.  */
+      single_keymap_panes (Fcar (elt), string,
+                          XCONS (eltcdr)->cdr, notreal);
       pending_maps = Fcdr (pending_maps);
     }
 }
@@ -525,7 +652,9 @@ list_of_items (pane)
     {
       item = Fcar (tail);
       if (STRINGP (item))
-       push_menu_item (item, Qnil, Qnil);
+       push_menu_item (item, Qnil, Qnil, Qnil);
+      else if (NILP (item))
+       push_left_right_boundary ();
       else
        {
          CHECK_CONS (item, 0);
@@ -536,11 +665,11 @@ list_of_items (pane)
     }
 }
 \f
-DEFUN ("x-popup-menu", Fx_popup_menu, Sx_popup_menu, 1, 2, 0,
+DEFUN ("x-popup-menu", Fx_popup_menu, Sx_popup_menu, 2, 2, 0,
   "Pop up a deck-of-cards menu and return user's selection.\n\
 POSITION is a position specification.  This is either a mouse button event\n\
 or a list ((XOFFSET YOFFSET) WINDOW)\n\
-where XOFFSET and YOFFSET are positions in characters from the top left\n\
+where XOFFSET and YOFFSET are positions in pixels from the top left\n\
 corner of WINDOW's frame.  (WINDOW may be a frame object instead of a window.)\n\
 This controls the position of the center of the first line\n\
 in the first pane of the menu, not the top left of the menu as a whole.\n\
@@ -581,21 +710,29 @@ cached information about equivalent key sequences.")
   int menubarp = 0;
   struct gcpro gcpro1;
 
-  check_x ();
-
   if (! NILP (position))
     {
+      check_x ();
+
       /* Decode the first argument: find the window and the coordinates.  */
       if (EQ (position, Qt))
        {
          /* Use the mouse's current position.  */
-         FRAME_PTR new_f;
+         FRAME_PTR new_f = 0;
          Lisp_Object bar_window;
          int part;
          unsigned long time;
 
-         (*mouse_position_hook) (&new_f, &bar_window, &part, &x, &y, &time);
-         XSET (window, Lisp_Frame, new_f);
+         if (mouse_position_hook)
+           (*mouse_position_hook) (&new_f, &bar_window, &part, &x, &y, &time);
+         if (new_f != 0)
+           XSET (window, Lisp_Frame, new_f);
+         else
+           {
+             window = selected_window;
+             XFASTINT (x) = 0;
+             XFASTINT (y) = 0;
+           }
        }
       else
        {
@@ -616,9 +753,7 @@ cached information about equivalent key sequences.")
 
              /* Determine whether this menu is handling a menu bar click.  */
              tem = Fcar (Fcdr (Fcar (Fcdr (position))));
-             if (XTYPE (Fcar (position)) != Lisp_Cons
-                 && CONSP (tem)
-                 && EQ (Fcar (tem), Qmenu_bar))
+             if (CONSP (tem) && EQ (Fcar (tem), Qmenu_bar))
                menubarp = 1;
            }
        }
@@ -641,7 +776,7 @@ cached information about equivalent key sequences.")
          f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
 
          xpos = (FONT_WIDTH (f->display.x->font) * XWINDOW (window)->left);
-         ypos = (FONT_HEIGHT (f->display.x->font) * XWINDOW (window)->top);
+         ypos = (f->display.x->line_height * XWINDOW (window)->top);
        }
       else
        /* ??? Not really clean; should be CHECK_WINDOW_OR_FRAME,
@@ -668,7 +803,7 @@ cached information about equivalent key sequences.")
       keymap = get_keymap (menu);
 
       /* Extract the detailed info to make one pane.  */
-      keymap_panes (&menu, 1);
+      keymap_panes (&menu, 1, NILP (position));
 
       /* Search for a string appearing directly as an element of the keymap.
         That string is the title of the menu.  */
@@ -704,7 +839,7 @@ cached information about equivalent key sequences.")
        }
 
       /* Extract the detailed info to make one pane.  */
-      keymap_panes (maps, nmaps);
+      keymap_panes (maps, nmaps, NILP (position));
 
       /* Make the title be the pane title of the first pane.  */
       if (!NILP (title) && menu_items_n_panes >= 0)
@@ -744,6 +879,118 @@ cached information about equivalent key sequences.")
   if (error_name) error (error_name);
   return selection;
 }
+
+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\
+This is normally a mouse button event or a window or frame.\n\
+If POSITION is t, it means to use the frame the mouse is on.\n\
+The dialog box appears in the middle of the specified frame.\n\
+\n\
+CONTENTS specifies the alternatives to display in the dialog box.\n\
+It is a list of the form (TITLE ITEM1 ITEM2...).\n\
+Each ITEM is a cons cell (STRING . VALUE).\n\
+The return value is VALUE from the chosen item.\n\n\
+An ITEM may also be just a string--that makes a nonselectable item.\n\
+An ITEM may also be nil--that means to put all preceding items\n\
+on the left of the dialog box and all following items on the right.\n\
+\(By default, approximately half appear on each side.)")
+  (position, contents)
+     Lisp_Object position, contents;
+{
+  FRAME_PTR f;
+  Lisp_Object window;
+
+  check_x ();
+
+  /* Decode the first argument: find the window or frame to use.  */
+  if (EQ (position, Qt))
+    {
+#if 0 /* Using the frame the mouse is on may not be right.  */
+      /* Use the mouse's current position.  */
+      FRAME_PTR new_f = 0;
+      Lisp_Object bar_window;
+      int part;
+      unsigned long time;
+      Lisp_Object x, y;
+
+      (*mouse_position_hook) (&new_f, &bar_window, &part, &x, &y, &time);
+
+      if (new_f != 0)
+       XSET (window, Lisp_Frame, new_f);
+      else
+       window = selected_window;
+#endif
+      /* Decode the first argument: find the window and the coordinates.  */
+      if (EQ (position, Qt))
+       window = selected_window;
+    }
+  else if (CONSP (position))
+    {
+      Lisp_Object tem;
+      tem = Fcar (position);
+      if (XTYPE (tem) == Lisp_Cons)
+       window = Fcar (Fcdr (position));
+      else
+       {
+         tem = Fcar (Fcdr (position));  /* EVENT_START (position) */
+         window = Fcar (tem);       /* POSN_WINDOW (tem) */
+       }
+    }
+  else if (WINDOWP (position) || FRAMEP (position))
+    window = position;
+
+  /* Decode where to put the menu.  */
+
+  if (XTYPE (window) == Lisp_Frame)
+    f = XFRAME (window);
+  else if (XTYPE (window) == Lisp_Window)
+    {
+      CHECK_LIVE_WINDOW (window, 0);
+      f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
+    }
+  else
+    /* ??? Not really clean; should be CHECK_WINDOW_OR_FRAME,
+       but I don't want to make one now.  */
+    CHECK_WINDOW (window, 0);
+
+#ifndef USE_X_TOOLKIT
+  /* Display a menu with these alternatives
+     in the middle of frame F.  */
+  {
+    Lisp_Object x, y, frame, newpos;
+    XSET (frame, Lisp_Frame, f);
+    XSET (x, Lisp_Int, x_pixel_width (f) / 2);
+    XSET (y, Lisp_Int, x_pixel_height (f) / 2);
+    newpos = Fcons (Fcons (x, Fcons (y, Qnil)), Fcons (frame, Qnil));
+
+    return Fx_popup_menu (newpos,
+                         Fcons (Fcar (contents), Fcons (contents, Qnil)));
+  }
+#else
+  {
+    Lisp_Object title;
+    char *error_name;
+    Lisp_Object selection;
+
+    /* Decode the dialog items from what was specified.  */
+    title = Fcar (contents);
+    CHECK_STRING (title, 1);
+
+    list_of_panes (Fcons (contents, Qnil));
+
+    /* Display them in a dialog box.  */
+    BLOCK_INPUT;
+    selection = xdialog_show (f, 0, 0, title, &error_name);
+    UNBLOCK_INPUT;
+
+    discard_menu_items ();
+
+    if (error_name) error (error_name);
+    return selection;
+  }
+#endif
+}
 \f
 #ifdef USE_X_TOOLKIT
 
@@ -764,19 +1011,7 @@ dispatch_dummy_expose (w, x, y)
   dummy.x = x;
   dummy.y = y;
 
-  XtDispatchEvent (&dummy);
-}
-
-static int
-string_width (mw, s)
-     XlwMenuWidget mw;
-     char* s;
-{
-  XCharStruct xcs;
-  int drop;
-  
-  XTextExtents (mw->menu.font, s, strlen (s), &drop, &drop, &drop, &xcs);
-  return xcs.width;
+  XtDispatchEvent ((XEvent *) &dummy);
 }
 
 static int
@@ -835,7 +1070,11 @@ map_event_to_object (event, f)
   return Qnil;
 }
 
+#ifdef __STDC__
+static Lisp_Object *volatile menu_item_selection;
+#else
 static Lisp_Object *menu_item_selection;
+#endif
 
 static void
 popup_selection_callback (widget, id, client_data)
@@ -857,6 +1096,19 @@ popup_down_callback (widget, id, client_data)
   UNBLOCK_INPUT;
 }
 
+static void
+dialog_selection_callback (widget, id, client_data)
+     Widget widget;
+     LWLIB_ID id;
+     XtPointer client_data;
+{
+  if ((int)client_data != -1)
+    menu_item_selection = (Lisp_Object *) client_data;
+  BLOCK_INPUT;
+  lw_destroy_all_widgets (id);
+  UNBLOCK_INPUT;
+}
+
 /* This recursively calls free_widget_value() on the tree of widgets.
    It must free all data that was malloc'ed for these widget_values.
    In Emacs, many slots are pointers into the data of Lisp_Strings, and
@@ -885,12 +1137,14 @@ free_menubar_widget_value_tree (wv)
   UNBLOCK_INPUT;
 }
 
+extern void EmacsFrameSetCharSize ();
+
 static void
-update_one_frame_psheets (f)
+update_frame_menubar (f)
      FRAME_PTR f;
 {
   struct x_display *x = f->display.x;
-  
+  int columns, rows;
   int menubar_changed;
   
   menubar_changed = (x->menubar_widget
@@ -900,6 +1154,12 @@ update_one_frame_psheets (f)
     return;
 
   BLOCK_INPUT;
+  /* Save the size of the frame because the pane widget doesn't accept to
+     resize itself. So force it.  */
+  columns = f->width;
+  rows = f->height;
+
+
   XawPanedSetRefigureMode (x->column_widget, 0);
   
   /* the order in which children are managed is the top to
@@ -924,12 +1184,17 @@ update_one_frame_psheets (f)
 
   /* and now thrash the sizes */
   XawPanedSetRefigureMode (x->column_widget, 1);
+
+  /* Force the pane widget to resize itself with the right values.  */
+  EmacsFrameSetCharSize (x->edit_widget, columns, rows);
+
   UNBLOCK_INPUT;
 }
 
 void
-set_frame_menubar (f)
+set_frame_menubar (f, first_time)
      FRAME_PTR f;
+     int first_time;
 {
   Widget menubar_widget = f->display.x->menubar_widget;
   int id = (int) f;
@@ -945,7 +1210,8 @@ set_frame_menubar (f)
   wv->enabled = 1;
   save_wv = first_wv = wv;
 
-  items = FRAME_MENU_BAR_ITEMS (f);
+  if (NILP (items = FRAME_MENU_BAR_ITEMS (f)))
+    items = FRAME_MENU_BAR_ITEMS (f) = menu_bar_items (FRAME_MENU_BAR_ITEMS (f));
 
   for (i = 0; i < XVECTOR (items)->size; i += 3)
     {
@@ -960,7 +1226,7 @@ set_frame_menubar (f)
        prev_wv->next = wv;
       else 
        save_wv->contents = wv;
-      wv->name = XSTRING (string)->data;
+      wv->name = (char *) XSTRING (string)->data;
       wv->value = 0;
       wv->enabled = 1;
       prev_wv = wv;
@@ -985,7 +1251,9 @@ set_frame_menubar (f)
   
   free_menubar_widget_value_tree (first_wv);
 
-  update_one_frame_psheets (f);
+  /* Don't update the menubar the first time it is created via x_window.  */
+  if (!first_time)
+    update_frame_menubar (f);
 
   UNBLOCK_INPUT;
 }
@@ -1007,22 +1275,39 @@ free_frame_menubar (f)
       UNBLOCK_INPUT;
     }
 }
+/* Called from Fx_create_frame to create the inital 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.  */
+void
+initialize_frame_menubar (f)
+     FRAME_PTR f;
+{
+  set_frame_menubar (f, 1);
+}
 \f
-/* Nonzero if position X, Y relative to inside of frame F
-   is in some other menu bar item.  */
+/* Horizontal bounds of the current menu bar item.  */
 
 static int this_menu_bar_item_beg;
 static int this_menu_bar_item_end;
 
+/* Horizontal position of the end of the last menu bar item.  */
+
+static int last_menu_bar_item_end;
+
+/* Nonzero if position X, Y is in the menu bar and in some menu bar item
+   but not in the current menu bar item.  */
+
 static int
 other_menu_bar_item_p (f, x, y)
      FRAME_PTR f;
      int x, y;
 {
   return (y >= 0
+         && f->display.x->menubar_widget != 0
          && y < f->display.x->menubar_widget->core.height
          && x >= 0
-         && x < f->display.x->menubar_widget->core.width
+         && x < last_menu_bar_item_end
          && (x >= this_menu_bar_item_end
              || x < this_menu_bar_item_beg));
 }
@@ -1092,6 +1377,12 @@ check_mouse_other_menu_bar (f)
 
 #ifdef USE_X_TOOLKIT
 
+extern unsigned last_event_timestamp;
+extern Lisp_Object Vdouble_click_time;
+
+extern unsigned int x_mouse_grabbed;
+extern Lisp_Object Vmouse_depressed;
+
 static Lisp_Object
 xmenu_show (f, x, y, menubarp, keymaps, title, error)
      FRAME_PTR f;
@@ -1106,11 +1397,18 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
   int menu_id;
   Widget menu;
   XlwMenuWidget menubar = (XlwMenuWidget) f->display.x->menubar_widget;
+  Arg av [2];
+  int ac = 0;
 
   /* This is the menu bar item (if any) that led to this menu.  */
   widget_value *menubar_item = 0;
 
   widget_value *wv, *save_wv = 0, *first_wv = 0, *prev_wv = 0;
+  widget_value **submenu_stack
+    = (widget_value **) alloca (menu_items_used * sizeof (widget_value *));
+  Lisp_Object *subprefix_stack
+    = (Lisp_Object *) alloca (menu_items_used * sizeof (Lisp_Object));
+  int submenu_depth = 0;
 
   /* Define a queue to save up for later unreading
      all X events that don't pertain to the menu.  */
@@ -1123,46 +1421,60 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
   struct event_queue *queue = NULL;
   struct event_queue *queue_tmp;
 
+  Position root_x, root_y;
+
+  int first_pane;
+  int next_release_must_exit = 0;
+
   *error = NULL;
 
+  if (menu_items_used <= MENU_ITEMS_PANE_LENGTH)
+    {
+      *error = "Empty menu";
+      return Qnil;
+    }
   this_menu_bar_item_beg = -1;
   this_menu_bar_item_end = -1;
+  last_menu_bar_item_end = -1;
 
   /* Figure out which menu bar item, if any, this menu is for.  */
   if (menubarp)
     {
       int xbeg;
       int xend = 0;
+      widget_value *mb_item = 0;
 
-      for (menubar_item = menubar->menu.old_stack[0]->contents;
-          menubar_item;
-          menubar_item = menubar_item->next)
+      for (mb_item = menubar->menu.old_stack[0]->contents;
+          mb_item;
+          mb_item = mb_item->next)
        {
          xbeg = xend;
-         xend += (string_width (menubar, menubar_item->name) 
+         xend += (string_width (menubar, mb_item->name) 
                   + 2 * (menubar->menu.horizontal_spacing
                          + menubar->menu.shadow_thickness));
-         if (x < xend)
+         if (x >= xbeg && x < xend)
            {
              x = xbeg + 4;
              y = 0;
+             menubar_item = mb_item;
              /* Arrange to show a different menu if we move in the menu bar
                 to a different item.  */
              this_menu_bar_item_beg = xbeg;
              this_menu_bar_item_end = xend;
-             break;
            }
        }
+      last_menu_bar_item_end = xend;
     }
   if (menubar_item == 0)
     menubarp = 0;
 
   /* Offset the coordinates to root-relative.  */
-  x += (f->display.x->widget->core.x
-       + f->display.x->widget->core.border_width);
-  y += (f->display.x->widget->core.y 
-       + f->display.x->widget->core.border_width
-                + f->display.x->menubar_widget->core.height);
+  if (f->display.x->menubar_widget != 0)
+    y += f->display.x->menubar_widget->core.height;
+  XtTranslateCoords (f->display.x->widget,
+                    x, y, &root_x, &root_y);
+  x = root_x;
+  y = root_y;
 
   /* Create a tree of widget_value objects
      representing the panes and their items.  */
@@ -1171,12 +1483,35 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
   wv->value = 0;
   wv->enabled = 1;
   first_wv = wv;
+  first_pane = 1;
  
   /* Loop over all panes and items, filling in the tree.  */
   i = 0;
   while (i < menu_items_used)
     {
-      if (EQ (XVECTOR (menu_items)->contents[i], Qt))
+      if (EQ (XVECTOR (menu_items)->contents[i], Qnil))
+       {
+         submenu_stack[submenu_depth++] = save_wv;
+         save_wv = prev_wv;
+         prev_wv = 0;
+         first_pane = 1;
+         i++;
+       }
+      else if (EQ (XVECTOR (menu_items)->contents[i], Qlambda))
+       {
+         prev_wv = save_wv;
+         save_wv = submenu_stack[--submenu_depth];
+         first_pane = 0;
+         i++;
+       }
+      else if (EQ (XVECTOR (menu_items)->contents[i], Qt)
+              && submenu_depth != 0)
+       i += MENU_ITEMS_PANE_LENGTH;
+      /* Ignore a nil in the item list.
+        It's meaningful only for dialog boxes.  */
+      else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
+       i += 1;
+      else if (EQ (XVECTOR (menu_items)->contents[i], Qt))
        {
          /* Create a new pane.  */
          Lisp_Object pane_name, prefix;
@@ -1185,7 +1520,7 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
          prefix = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
          pane_string = (NILP (pane_name)
                         ? "" : (char *) XSTRING (pane_name)->data);
-         /* If there is just one pane, put all its items directly
+         /* If there is just one top-level pane, put all its items directly
             under the top-level menu.  */
          if (menu_items_n_panes == 1)
            pane_string = "";
@@ -1193,7 +1528,7 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
          /* If the pane has a meaningful name,
             make the pane a top-level menu item
             with its items as a submenu beneath it.  */
-         if (strcmp (pane_string, ""))
+         if (!keymaps && strcmp (pane_string, ""))
            {
              wv = malloc_widget_value ();
              if (save_wv)
@@ -1205,9 +1540,15 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
                wv->name++;
              wv->value = 0;
              wv->enabled = 1;
+             save_wv = wv;
+             prev_wv = 0;
            }
-         save_wv = wv;
-         prev_wv = 0;
+         else if (first_pane)
+           {
+             save_wv = wv;
+             prev_wv = 0;
+           }
+         first_pane = 0;
          i += MENU_ITEMS_PANE_LENGTH;
        }
       else
@@ -1224,9 +1565,9 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
            prev_wv->next = wv;
          else 
            save_wv->contents = wv;
-         wv->name = XSTRING (item_name)->data;
+         wv->name = (char *) XSTRING (item_name)->data;
          if (!NILP (descrip))
-           wv->key = XSTRING (descrip)->data;
+           wv->key = (char *) XSTRING (descrip)->data;
          wv->value = 0;
          wv->call_data = (void *) &XVECTOR (menu_items)->contents[i];
          wv->enabled = !NILP (enable);
@@ -1241,6 +1582,11 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
   menu = lw_create_widget ("popup", first_wv->name, menu_id, first_wv,
                           f->display.x->widget, 1, 0,
                           popup_selection_callback, popup_down_callback);
+
+  /* Don't allow any geometry request from the user.  */
+  XtSetArg (av[ac], XtNgeometry, 0); ac++;
+  XtSetValues (menu, av, ac);
+
   /* Free the widget_value objects we used to specify the contents.  */
   free_menubar_widget_value_tree (first_wv);
 
@@ -1284,34 +1630,90 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
     pop_up_menu (mw, &dummy);
   }
 
+  /* No need to check a second time since this is done in the XEvent loop.
+     This slows done the execution.  */
+#ifdef XMENU_FOO
   /* Check again whether the mouse has moved to another menu bar item.  */
   if (check_mouse_other_menu_bar (f))
     {
       /* The mouse moved into a different menu bar item. 
         We should bring up that item's menu instead.
         First pop down this menu.  */
+#if 0 /* xlwmenu.c now does this.  */
       XtUngrabPointer ((Widget)
                       ((XlwMenuWidget)
                        ((CompositeWidget)menu)->composite.children[0]),
                       CurrentTime);
+#endif
       lw_destroy_all_widgets (menu_id); 
       goto pop_down;
     }
+#endif
 
   /* Process events that apply to the menu.  */
   while (1)
     {
       XEvent event;
+      int queue_and_exit = 0;
+      int in_this_menu = 0, in_menu_bar = 0;
+      Widget widget;
 
       XtAppNextEvent (Xt_app_con, &event);
+
+      /* Check whether the event happened in the menu
+        or any child of it.  */
+      widget = XtWindowToWidget (XDISPLAY event.xany.window);
+
+      while (widget)
+       {
+         if (widget == menu)
+           {
+             in_this_menu = 1;
+             break;
+           }
+         if (widget == f->display.x->menubar_widget)
+           {
+             in_menu_bar = 1;
+             break;
+           }
+         widget = XtParent (widget);
+       }
+
       if (event.type == ButtonRelease)
        {
-         XtDispatchEvent (&event);
-         break;
+         /* Do the work of construct_mouse_click since it can't
+            be called. Initially, the popup menu has been called
+            from a ButtonPress in the edit_widget. Then the mouse
+            has been set to grabbed. Reset it now.  */
+         x_mouse_grabbed &= ~(1 << event.xbutton.button);
+         if (!x_mouse_grabbed)
+           Vmouse_depressed = Qnil;
+
+         /* If we release the button soon without selecting anything,
+            stay in the loop--that is, leave the menu posted.
+            Otherwise, exit this loop and thus pop down the menu.  */
+         if (! in_this_menu
+             && (next_release_must_exit
+                 || !(((XButtonEvent *) (&event))->time - last_event_timestamp
+                      < XINT (Vdouble_click_time))))
+           break;
+       }
+      /* A button press outside the menu => pop it down.  */
+      else if (event.type == ButtonPress && !in_this_menu)
+       break;
+      else if (event.type == ButtonPress)
+       next_release_must_exit = 1;
+      else if (event.type == KeyPress)
+       {
+         /* Exit the loop, but first queue this event for reuse.  */
+         queue_and_exit = 1;
        }
       else if (event.type == Expose)
        process_expose_from_menu (event);
-      else if (event.type == MotionNotify)
+      /* If the mouse moves to a different menu bar item, switch to
+        that item's menu.  But only if the button is still held down.  */
+      else if (event.type == MotionNotify
+              && x_mouse_grabbed)
        {
          int event_x = (event.xmotion.x_root
                         - (f->display.x->widget->core.x
@@ -1325,10 +1727,12 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
              /* The mouse moved into a different menu bar item. 
                 We should bring up that item's menu instead.
                 First pop down this menu.  */
+#if 0 /* xlwmenu.c now does this.  */
              XtUngrabPointer ((Widget)
                               ((XlwMenuWidget)
                                ((CompositeWidget)menu)->composite.children[0]),
                               event.xbutton.time);
+#endif
              lw_destroy_all_widgets (menu_id); 
 
              /* Put back an event that will bring up the other item's menu.  */
@@ -1338,9 +1742,17 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
              break;
            }
        }
+      else if (event.type == UnmapNotify)
+       {
+         /* If the menu disappears, there is no need to stay in the
+             loop.  */
+         if (event.xunmap.window == menu->core.window)
+           break;
+       }
 
       XtDispatchEvent (&event);
-      if (XtWindowToWidget(XDISPLAY event.xany.window) != menu)
+
+      if (queue_and_exit || (!in_this_menu && !in_menu_bar))
        {
          queue_tmp
            = (struct event_queue *) malloc (sizeof (struct event_queue));
@@ -1352,6 +1764,8 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
              queue = queue_tmp;
            }
        }
+      if (queue_and_exit)
+       break;
     }
 
  pop_down:
@@ -1362,6 +1776,9 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
       dispatch_dummy_expose (f->display.x->menubar_widget, x, y);
     }
 
+  /* fp turned off the following statement and wrote a comment
+     that it is unnecessary--that the menu has already disappeared.
+     I observer that is not so. -- rms.  */
   /* Make sure the menu disappears.  */
   lw_destroy_all_widgets (menu_id); 
 
@@ -1372,6 +1789,8 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
       XPutBackEvent (XDISPLAY &queue_tmp->event);
       queue = queue_tmp->next;
       free ((char *)queue_tmp);
+      /* Cause these events to get read as soon as we UNBLOCK_INPUT.  */
+      interrupt_input_pending = 1;
     }
 
   /* Find the selected item, and its pane, to return
@@ -1386,7 +1805,18 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
        {
          Lisp_Object entry;
 
-         if (EQ (XVECTOR (menu_items)->contents[i], Qt))
+         if (EQ (XVECTOR (menu_items)->contents[i], Qnil))
+           {
+             subprefix_stack[submenu_depth++] = prefix;
+             prefix = entry;
+             i++;
+           }
+         else if (EQ (XVECTOR (menu_items)->contents[i], Qlambda))
+           {
+             prefix = subprefix_stack[--submenu_depth];
+             i++;
+           }
+         else if (EQ (XVECTOR (menu_items)->contents[i], Qt))
            {
              prefix
                = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
@@ -1400,9 +1830,14 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
                {
                  if (keymaps != 0)
                    {
+                     int j;
+
                      entry = Fcons (entry, Qnil);
                      if (!NILP (prefix))
                        entry = Fcons (prefix, entry);
+                     for (j = submenu_depth - 1; j >= 0; j--)
+                       if (!NILP (subprefix_stack[j]))
+                         entry = Fcons (subprefix_stack[j], entry);
                    }
                  return entry;
                }
@@ -1414,6 +1849,245 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
   return Qnil;
 }
 
+static char * button_names [] = {
+  "button1", "button2", "button3", "button4", "button5",
+  "button6", "button7", "button8", "button9", "button10" };
+
+static Lisp_Object
+xdialog_show (f, menubarp, keymaps, title, error)
+     FRAME_PTR f;
+     int menubarp;
+     int keymaps;
+     Lisp_Object title;
+     char **error;
+{
+  int i, nb_buttons=0;
+  int dialog_id;
+  Widget menu;
+  XlwMenuWidget menubar = (XlwMenuWidget) f->display.x->menubar_widget;
+  char dialog_name[6];
+
+  /* This is the menu bar item (if any) that led to this menu.  */
+  widget_value *menubar_item = 0;
+
+  widget_value *wv, *save_wv = 0, *first_wv = 0, *prev_wv = 0;
+
+  /* Define a queue to save up for later unreading
+     all X events that don't pertain to the menu.  */
+  struct event_queue
+    {
+      XEvent event;
+      struct event_queue *next;
+    };
+  
+  struct event_queue *queue = NULL;
+  struct event_queue *queue_tmp;
+
+  /* Number of elements seen so far, before boundary.  */
+  int left_count = 0;
+  /* 1 means we've seen the boundary between left-hand elts and right-hand.  */
+  int boundary_seen = 0;
+
+  *error = NULL;
+
+  if (menu_items_n_panes > 1)
+    {
+      *error = "Multiple panes in dialog box";
+      return Qnil;
+    }
+
+  /* Create a tree of widget_value objects
+     representing the text label and buttons.  */
+  {
+    Lisp_Object pane_name, prefix;
+    char *pane_string;
+    pane_name = XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME];
+    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->value = pane_string;
+    if (keymaps && !NILP (prefix))
+      prev_wv->name++;
+    prev_wv->enabled = 1;
+    prev_wv->name = "message";
+    first_wv = prev_wv;
+    /* Loop over all panes and items, filling in the tree.  */
+    i = MENU_ITEMS_PANE_LENGTH;
+    while (i < menu_items_used)
+      {
+       
+       /* Create a new item within current pane.  */
+       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];
+       
+       if (NILP (item_name))
+         {
+           free_menubar_widget_value_tree (first_wv);
+           *error = "Submenu in dialog items";
+           return Qnil;
+         }
+       if (EQ (item_name, Qquote))
+         {
+           /* This is the boundary between left-side elts
+              and right-side elts.  Stop incrementing right_count.  */
+           boundary_seen = 1;
+           i++;
+           continue;
+         }
+       if (nb_buttons >= 10)
+         {
+           free_menubar_widget_value_tree (first_wv);
+           *error = "Too many dialog items";
+           return Qnil;
+         }
+
+       wv = malloc_widget_value ();
+       prev_wv->next = wv;
+       wv->name = (char *) button_names[nb_buttons];
+       if (!NILP (descrip))
+         wv->key = (char *) XSTRING (descrip)->data;
+       wv->value = (char *) XSTRING (item_name)->data;
+       wv->call_data = (void *) &XVECTOR (menu_items)->contents[i];
+       wv->enabled = !NILP (enable);
+       prev_wv = wv;
+
+       if (! boundary_seen)
+         left_count++;
+
+       nb_buttons++;
+       i += MENU_ITEMS_ITEM_LENGTH;
+      }
+
+    /* If the boundary was not specified,
+       by default put half on the left and half on the right.  */
+    if (! boundary_seen)
+      left_count = nb_buttons - nb_buttons / 2;
+
+    wv = malloc_widget_value ();
+    wv->name = dialog_name;
+
+    /* 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';
+    dialog_name[1] = '0' + nb_buttons;
+    dialog_name[2] = 'B';
+    dialog_name[3] = 'R';
+    /* Number of buttons to put on the right.  */
+    dialog_name[4] = '0' + nb_buttons - left_count;
+    dialog_name[5] = 0;
+    wv->contents = first_wv;
+    first_wv = wv;
+  }
+
+  /* Actually create the dialog.  */
+  dialog_id = ++popup_id_tick;
+  menu = lw_create_widget (first_wv->name, "dialog", dialog_id, first_wv,
+                          f->display.x->widget, 1, 0,
+                          dialog_selection_callback, 0);
+#if 0 /* This causes crashes, and seems to be redundant -- rms.  */
+  lw_modify_all_widgets (dialog_id, first_wv, True);
+#endif
+  lw_modify_all_widgets (dialog_id, first_wv->contents, True);
+  /* Free the widget_value objects we used to specify the contents.  */
+  free_menubar_widget_value_tree (first_wv);
+
+  /* No selection has been chosen yet.  */
+  menu_item_selection = 0;
+
+  /* Display the menu.  */
+  lw_pop_up_all_widgets (dialog_id);
+
+  /* Process events that apply to the menu.  */
+  while (1)
+    {
+      XEvent event;
+
+      XtAppNextEvent (Xt_app_con, &event);
+      if (event.type == ButtonRelease)
+       {
+         XtDispatchEvent (&event);
+         break;
+       }
+      else if (event.type == Expose)
+       process_expose_from_menu (event);
+      XtDispatchEvent (&event);
+      if (XtWindowToWidget(XDISPLAY event.xany.window) != menu)
+       {
+         queue_tmp = (struct event_queue *) malloc (sizeof (struct event_queue));
+
+         if (queue_tmp != NULL) 
+           {
+             queue_tmp->event = event;
+             queue_tmp->next = queue;
+             queue = queue_tmp;
+           }
+       }
+    }
+ pop_down:
+
+  /* State that no mouse buttons are now held.
+     That is not necessarily true, but the fiction leads to reasonable
+     results, and it is a pain to ask which are actually held now
+     or track this in the loop above.  */
+  x_mouse_grabbed = 0;
+
+  /* Unread any events that we got but did not handle.  */
+  while (queue != NULL) 
+    {
+      queue_tmp = queue;
+      XPutBackEvent (XDISPLAY &queue_tmp->event);
+      queue = queue_tmp->next;
+      free ((char *)queue_tmp);
+      /* Cause these events to get read as soon as we UNBLOCK_INPUT.  */
+      interrupt_input_pending = 1;
+    }
+
+  /* Find the selected item, and its pane, to return
+     the proper value.  */
+  if (menu_item_selection != 0)
+    {
+      Lisp_Object prefix;
+
+      prefix = Qnil;
+      i = 0;
+      while (i < menu_items_used)
+       {
+         Lisp_Object entry;
+
+         if (EQ (XVECTOR (menu_items)->contents[i], Qt))
+           {
+             prefix
+               = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
+             i += MENU_ITEMS_PANE_LENGTH;
+           }
+         else
+           {
+             entry
+               = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_VALUE];
+             if (menu_item_selection == &XVECTOR (menu_items)->contents[i])
+               {
+                 if (keymaps != 0)
+                   {
+                     entry = Fcons (entry, Qnil);
+                     if (!NILP (prefix))
+                       entry = Fcons (prefix, entry);
+                   }
+                 return entry;
+               }
+             i += MENU_ITEMS_ITEM_LENGTH;
+           }
+       }
+    }
+
+  return Qnil;
+}
 #else /* not USE_X_TOOLKIT */
 
 static Lisp_Object
@@ -1432,7 +2106,8 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
   char *datap;
   int ulx, uly, width, height;
   int dispwidth, dispheight;
-  int i;
+  int i, j;
+  int maxwidth;
   int dummy_int;
   unsigned int dummy_uint;
 
@@ -1440,6 +2115,12 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
   if (menu_items_n_panes == 0)
     return Qnil;
 
+  if (menu_items_used <= MENU_ITEMS_PANE_LENGTH)
+    {
+      *error = "Empty menu";
+      return Qnil;
+    }
+
   /* Figure out which root window F is on.  */
   XGetGeometry (x_current_display, FRAME_X_WINDOW (f), &root,
                &dummy_int, &dummy_int, &dummy_uint, &dummy_uint,
@@ -1511,21 +2192,70 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
              return Qnil;
            }
          i += MENU_ITEMS_PANE_LENGTH;
+
+         /* Find the width of the widest item in this pane.  */
+         maxwidth = 0;
+         j = i;
+         while (j < menu_items_used)
+           {
+             Lisp_Object item;
+             item = XVECTOR (menu_items)->contents[j];
+             if (EQ (item, Qt))
+               break;
+             if (NILP (item))
+               {
+                 j++;
+                 continue;
+               }
+             width = XSTRING (item)->size;
+             if (width > maxwidth)
+               maxwidth = width;
+
+             j += MENU_ITEMS_ITEM_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
        {
          /* Create a new item within current pane.  */
          Lisp_Object item_name, enable, descrip;
+         unsigned char *item_data;
 
          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];
          if (!NILP (descrip))
-           item_name = concat2 (item_name, descrip);
+           {
+             int gap = maxwidth - XSTRING (item_name)->size;
+#ifdef C_ALLOCA
+             Lisp_Object spacer;
+             spacer = Fmake_string (make_number (gap), make_number (' '));
+             item_name = concat2 (item_name, spacer);
+             item_name = concat2 (item_name, descrip);
+             item_data = XSTRING (item_name)->data;
+#else
+             /* if alloca is fast, use that to make the space,
+                to reduce gc needs.  */
+             item_data
+               = (unsigned char *) alloca (maxwidth
+                                           + XSTRING (descrip)->size + 1);
+             bcopy (XSTRING (item_name)->data, item_data,
+                    XSTRING (item_name)->size);
+             for (j = XSTRING (item_name)->size; j < maxwidth; j++)
+               item_data[j] = ' ';
+             bcopy (XSTRING (descrip)->data, item_data + j,
+                    XSTRING (descrip)->size);
+             item_data[j + XSTRING (descrip)->size] = 0;
+#endif
+           }
+         else
+           item_data = XSTRING (item_name)->data;
 
-         if (XMenuAddSelection (XDISPLAY menu, lpane, 0,
-                                XSTRING (item_name)->data,
+         if (XMenuAddSelection (XDISPLAY menu, lpane, 0, item_data,
                                 !NILP (enable))
              == XM_FAILURE)
            {
@@ -1536,7 +2266,7 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
          i += MENU_ITEMS_ITEM_LENGTH;
        }
     }
-  
+
   /* All set and ready to fly.  */
   XMenuRecompute (XDISPLAY menu);
   dispwidth = DisplayWidth (x_current_display, XDefaultScreen (x_current_display));
@@ -1559,7 +2289,8 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
     }
   if (ulx < 0) x -= ulx;
   if (uly < 0) y -= uly;
-    
+
+  XMenuSetAEQ (menu, TRUE);
   XMenuSetFreeze (menu, TRUE);
   pane = selidx = 0;
   
@@ -1608,7 +2339,6 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
       break;
 
     case XM_FAILURE:
-      XMenuDestroy (XDISPLAY menu);
       *error = "Can't activate menu";
     case XM_IA_SELECT:
     case XM_NO_SELECT:
@@ -1616,6 +2346,13 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
       break;
     }
   XMenuDestroy (XDISPLAY menu);
+
+  /* State that no mouse buttons are now held.
+     (The oldXMenu code doesn't track this info for us.)
+     That is not necessarily true, but the fiction leads to reasonable
+     results, and it is a pain to ask which are actually held now.  */
+  x_mouse_grabbed = 0;
+
   return entry;
 }
 #endif /* not USE_X_TOOLKIT */
@@ -1627,4 +2364,5 @@ syms_of_xmenu ()
 
   popup_id_tick = (1<<16);     
   defsubr (&Sx_popup_menu);
+  defsubr (&Sx_popup_dialog);
 }