*** empty log message ***
[bpt/emacs.git] / src / xmenu.c
index bdfe046..4724b2e 100644 (file)
@@ -1,5 +1,6 @@
 /* X Communication module for terminals which understand the X protocol.
-   Copyright (C) 1986, 1988, 1993, 1994, 1996 Free Software Foundation, Inc.
+   Copyright (C) 1986, 88, 93, 94, 96, 99, 2000, 2001
+   Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -18,7 +19,7 @@ along with GNU Emacs; see the file COPYING.  If not, write to
 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 Boston, MA 02111-1307, USA.  */
 
-/* X pop-up deck-of-cards menu facility for gnuemacs.
+/* X pop-up deck-of-cards menu facility for GNU Emacs.
  *
  * Written by Jon Arnold and Roman Budzianowski
  * Mods and rewrite by Robert Krawitz
@@ -30,19 +31,23 @@ Boston, MA 02111-1307, USA.  */
 
 /* Rewritten for clarity and GC protection by rms in Feb 94.  */
 
+#include <config.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 "keyboard.h"
+#include "keymap.h"
 #include "frame.h"
 #include "window.h"
-#include "keyboard.h"
 #include "blockinput.h"
-#include "puresize.h"
 #include "buffer.h"
+#include "charset.h"
+#include "coding.h"
 
 #ifdef MSDOS
 #include "msdos.h"
@@ -63,7 +68,9 @@ Boston, MA 02111-1307, USA.  */
 #include "dispextern.h"
 
 #ifdef HAVE_X_WINDOWS
+#undef HAVE_MULTILINGUAL_MENU
 #ifdef USE_X_TOOLKIT
+#include "widget.h"
 #include <X11/Xlib.h>
 #include <X11/IntrinsicP.h>
 #include <X11/CoreP.h>
@@ -78,9 +85,6 @@ Boston, MA 02111-1307, USA.  */
 #endif /* not USE_X_TOOLKIT */
 #endif /* HAVE_X_WINDOWS */
 
-#define min(x,y) (((x) < (y)) ? (x) : (y))
-#define max(x,y) (((x) > (y)) ? (x) : (y))
-
 #ifndef TRUE
 #define TRUE 1
 #define FALSE 0
@@ -90,13 +94,10 @@ Lisp_Object Vmenu_updating_frame;
 
 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 QCtoggle, QCradio;
 
 extern Lisp_Object Voverriding_local_map;
 extern Lisp_Object Voverriding_local_map_menu_flag;
@@ -111,14 +112,28 @@ extern void process_expose_from_menu ();
 extern XtAppContext Xt_app_con;
 
 static Lisp_Object xdialog_show ();
-void popup_get_selection ();
+static void popup_get_selection ();
+
+/* Define HAVE_BOXES if menus can handle radio and toggle buttons.  */
+
+#define HAVE_BOXES 1
 #endif
 
-static Lisp_Object xmenu_show ();
-static void keymap_panes ();
-static void single_keymap_panes ();
-static void list_of_panes ();
-static void list_of_items ();
+static void push_menu_item P_ ((Lisp_Object, Lisp_Object, Lisp_Object,
+                               Lisp_Object, Lisp_Object, Lisp_Object,
+                               Lisp_Object, Lisp_Object));
+static int update_frame_menubar P_ ((struct frame *));
+static Lisp_Object xmenu_show P_ ((struct frame *, int, int, int, int,
+                                  Lisp_Object, char **));
+static void keymap_panes P_ ((Lisp_Object *, int, int));
+static void single_keymap_panes P_ ((Lisp_Object, Lisp_Object, Lisp_Object,
+                                    int, int));
+static void single_menu_item P_ ((Lisp_Object, Lisp_Object, Lisp_Object *,
+                                 int, int, int *));
+static void list_of_panes P_ ((Lisp_Object));
+static void list_of_items P_ ((Lisp_Object));
+
+extern EMACS_TIME timer_check P_ ((int));
 \f
 /* This holds a Lisp vector that holds the results of decoding
    the keymaps or alist-of-alists that specify a menu.
@@ -146,15 +161,25 @@ static void list_of_items ();
 #define MENU_ITEMS_PANE_PREFIX 2
 #define MENU_ITEMS_PANE_LENGTH 3
 
-#define MENU_ITEMS_ITEM_NAME 0
-#define MENU_ITEMS_ITEM_ENABLE 1
-#define MENU_ITEMS_ITEM_VALUE 2
-#define MENU_ITEMS_ITEM_EQUIV_KEY 3
-#define MENU_ITEMS_ITEM_DEFINITION 4
-#define MENU_ITEMS_ITEM_LENGTH 5
+enum menu_item_idx
+{
+  MENU_ITEMS_ITEM_NAME = 0,
+  MENU_ITEMS_ITEM_ENABLE,
+  MENU_ITEMS_ITEM_VALUE,
+  MENU_ITEMS_ITEM_EQUIV_KEY,
+  MENU_ITEMS_ITEM_DEFINITION,
+  MENU_ITEMS_ITEM_TYPE,
+  MENU_ITEMS_ITEM_SELECTED,
+  MENU_ITEMS_ITEM_HELP,
+  MENU_ITEMS_ITEM_LENGTH
+};
 
 static Lisp_Object menu_items;
 
+/* If non-nil, means that the global vars defined here are already in use.
+   Used to detect cases where we try to re-enter this non-reentrant code.  */
+static Lisp_Object menu_items_inuse;
+
 /* Number of slots currently allocated in menu_items.  */
 static int menu_items_allocated;
 
@@ -170,7 +195,7 @@ static int menu_items_submenu_depth;
 
 /* Flag which when set indicates a dialog or menu has been posted by
    Xt on behalf of one of the widget sets.  */
-static int popup_activated_flag;
+int popup_activated_flag;
 
 static int next_menubar_widget_id;
 
@@ -194,13 +219,13 @@ menubar_id_to_frame (id)
   Lisp_Object tail, frame;
   FRAME_PTR f;
 
-  for (tail = Vframe_list; GC_CONSP (tail); tail = XCONS (tail)->cdr)
+  for (tail = Vframe_list; GC_CONSP (tail); tail = XCDR (tail))
     {
-      frame = XCONS (tail)->car;
+      frame = XCAR (tail);
       if (!GC_FRAMEP (frame))
         continue;
       f = XFRAME (frame);
-      if (f->output_data.nothing == 1)
+      if (!FRAME_WINDOW_P (f))
        continue;
       if (f->output_data.x->id == id)
        return f;
@@ -222,19 +247,28 @@ init_menu_items ()
       menu_items = Fmake_vector (make_number (menu_items_allocated), Qnil);
     }
 
+  if (!NILP (menu_items_inuse))
+    error ("Trying to use a menu from within a menu-entry");
+  menu_items_inuse = Qt;
   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.
-   This fills in the number of items in the last pane.  */
+/* Call at the end of generating the data in menu_items.  */
 
 static void
 finish_menu_items ()
 {
 }
 
+static Lisp_Object
+unuse_menu_items (dummy)
+     int dummy;
+{
+  return menu_items_inuse = Qnil;
+}
+
 /* Call when finished using the data for the current menu
    in menu_items.  */
 
@@ -248,6 +282,7 @@ discard_menu_items ()
       menu_items = Qnil;
       menu_items_allocated = 0;
     }
+  xassert (NILP (menu_items_inuse));
 }
 
 /* Make the menu_items vector twice as large.  */
@@ -300,7 +335,7 @@ push_left_right_boundary ()
   XVECTOR (menu_items)->contents[menu_items_used++] = Qquote;
 }
 
-/* Start a new menu pane in menu_items..
+/* Start a new menu pane in menu_items.
    NAME is the pane name.  PREFIX_VEC is a prefix key for this pane.  */
 
 static void
@@ -317,17 +352,17 @@ push_menu_pane (name, prefix_vec)
   XVECTOR (menu_items)->contents[menu_items_used++] = prefix_vec;
 }
 
-/* Push one menu item into the current pane.
-   NAME is the string to display.  ENABLE if non-nil means
-   this item can be selected.  KEY is the key generated by
-   choosing this item, or nil if this item doesn't really have a definition.
-   DEF is the definition of this item.
-   EQUIV is the textual description of the keyboard equivalent for
-   this item (or nil if none).  */
+/* Push one menu item into the current pane.  NAME is the string to
+   display.  ENABLE if non-nil means this item can be selected.  KEY
+   is the key generated by choosing this item, or nil if this item
+   doesn't really have a definition.  DEF is the definition of this
+   item.  EQUIV is the textual description of the keyboard equivalent
+   for this item (or nil if none).  TYPE is the type of this menu
+   item, one of nil, `toggle' or `radio'. */
 
 static void
-push_menu_item (name, enable, key, def, equiv)
-     Lisp_Object name, enable, key, def, equiv;
+push_menu_item (name, enable, key, def, equiv, type, selected, help)
+     Lisp_Object name, enable, key, def, equiv, type, selected, help;
 {
   if (menu_items_used + MENU_ITEMS_ITEM_LENGTH > menu_items_allocated)
     grow_menu_items ();
@@ -337,152 +372,9 @@ push_menu_item (name, enable, key, def, equiv)
   XVECTOR (menu_items)->contents[menu_items_used++] = key;
   XVECTOR (menu_items)->contents[menu_items_used++] = equiv;
   XVECTOR (menu_items)->contents[menu_items_used++] = def;
-}
-\f
-/* Figure out the current keyboard equivalent of a menu item ITEM1.
-   The item string for menu display should be ITEM_STRING.
-   Store the equivalent keyboard key sequence's
-   textual description into *DESCRIP_PTR.
-   Also cache them in the item itself.
-   Return the real definition to execute.  */
-
-static Lisp_Object
-menu_item_equiv_key (item_string, item1, descrip_ptr)
-     Lisp_Object item_string;
-     Lisp_Object item1;
-     Lisp_Object *descrip_ptr;
-{
-  /* This is the real definition--the function to run.  */
-  Lisp_Object def;
-  /* This is the sublist that records cached equiv key data
-     so we can save time.  */
-  Lisp_Object cachelist;
-  /* These are the saved equivalent keyboard key sequence
-     and its key-description.  */
-  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)
-      && STRINGP (XCONS (XCONS (item1)->cdr)->car))
-    item1 = XCONS (item1)->cdr;
-
-  def = Fcdr (item1);
-
-  /* Get out the saved equivalent-keyboard-key info.  */
-  cachelist = savedkey = descrip = Qnil;
-  if (CONSP (def) && CONSP (XCONS (def)->car)
-      && (NILP (XCONS (XCONS (def)->car)->car)
-         || VECTORP (XCONS (XCONS (def)->car)->car)))
-    {
-      cachelist = XCONS (def)->car;
-      def = XCONS (def)->cdr;
-      savedkey = XCONS (cachelist)->car;
-      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
-        because that is too slow--except if we have a list of rebound
-        commands in Vdefine_key_rebound_commands, do recheck any command
-        that appears in that list.  */
-      && (NILP (cachelist) || !NILP (savedkey)
-         || (! EQ (Qt, Vdefine_key_rebound_commands)
-             && !NILP (Fmemq (def, Vdefine_key_rebound_commands)))))
-    {
-      changed = 1;
-      descrip = 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)
-         && ! NILP (Fget (def, Qmenu_alias)))
-       savedkey = Fwhere_is_internal (XSYMBOL (def)->function,
-                                      Qnil, Qt, Qnil);
-      else
-       /* Otherwise look up the specified command itself.
-          We don't try both, because that makes easymenu menus slow.  */
-       savedkey = Fwhere_is_internal (def, Qnil, Qt, Qnil);
-
-      if (!NILP (savedkey))
-       {
-         descrip = Fkey_description (savedkey);
-         descrip = concat2 (make_string ("  (", 3), descrip);
-         descrip = concat2 (descrip, make_string (")", 1));
-       }
-    }
-
-  /* Cache the data we just got in a sublist of the menu binding.  */
-  if (NILP (cachelist))
-    {
-      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;
-}
-
-/* This is used as the handler when calling internal_condition_case_1.  */
-
-static Lisp_Object
-menu_item_enabled_p_1 (arg)
-     Lisp_Object arg;
-{
-  /* If we got a quit from within the menu computation,
-     quit all the way out of it.  This takes care of C-] in the debugger.  */
-  if (CONSP (arg) && EQ (XCONS (arg)->car, Qquit))
-    Fsignal (Qquit, Qnil);
-
-  return Qnil;
-}
-
-/* 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.
-   If NOTREAL is set, don't bother really computing this.  */
-
-static Lisp_Object
-menu_item_enabled_p (def, notreal)
-     Lisp_Object def;
-     int notreal;
-{
-  Lisp_Object enabled, tem;
-
-  enabled = Qt;
-  if (notreal)
-    return enabled;
-  if (SYMBOLP (def))
-    {
-      /* No property, or nil, means enable.
-        Otherwise, enable if value is not nil.  */
-      tem = Fget (def, Qmenu_enable);
-      if (!NILP (tem))
-       /* (condition-case nil (eval tem)
-          (error nil))  */
-       enabled = internal_condition_case_1 (Feval, tem, Qerror,
-                                            menu_item_enabled_p_1);
-    }
-  return enabled;
+  XVECTOR (menu_items)->contents[menu_items_used++] = type;
+  XVECTOR (menu_items)->contents[menu_items_used++] = selected;
+  XVECTOR (menu_items)->contents[menu_items_used++] = help;
 }
 \f
 /* Look through KEYMAPS, a vector of keymaps that is NMAPS long,
@@ -504,7 +396,8 @@ keymap_panes (keymaps, nmaps, notreal)
      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, notreal, 10);
+    single_keymap_panes (keymaps[mapno],
+                        Fkeymap_prompt (keymaps[mapno]), Qnil, notreal, 10);
 
   finish_menu_items ();
 }
@@ -513,8 +406,8 @@ keymap_panes (keymaps, nmaps, notreal)
    It handles one keymap, KEYMAP.
    The other arguments are passed along
    or point to local variables of the previous function.
-   If NOTREAL is nonzero,
-   don't bother really computing whether an item is enabled.
+   If NOTREAL is nonzero, only check for equivalent key bindings, don't
+   evaluate expressions in menu items and don't make any menu.
 
    If we encounter submenus deeper than MAXDEPTH levels, ignore them.  */
 
@@ -526,88 +419,33 @@ single_keymap_panes (keymap, pane_name, prefix, notreal, maxdepth)
      int notreal;
      int maxdepth;
 {
-  Lisp_Object pending_maps;
-  Lisp_Object tail, item, item1, item_string, table;
-  struct gcpro gcpro1, gcpro2, gcpro3, gcpro4;
+  Lisp_Object pending_maps = Qnil;
+  Lisp_Object tail, item;
+  struct gcpro gcpro1, gcpro2;
+  int notbuttons = 0;
 
   if (maxdepth <= 0)
     return;
 
-  pending_maps = Qnil;
-
   push_menu_pane (pane_name, prefix);
 
-  for (tail = keymap; CONSP (tail); tail = XCONS (tail)->cdr)
+#ifndef HAVE_BOXES
+  /* Remember index for first item in this pane so we can go back and
+     add a prefix when (if) we see the first button.  After that, notbuttons
+     is set to 0, to mark that we have seen a button and all non button
+     items need a prefix.  */
+  notbuttons = menu_items_used;
+#endif
+
+  for (tail = keymap; CONSP (tail); tail = XCDR (tail))
     {
-      /* Look at each key binding, and if it has a menu string,
-        make a menu item from it.  */
-      item = XCONS (tail)->car;
+      GCPRO2 (keymap, pending_maps);
+      /* Look at each key binding, and if it is a menu item add it
+        to this menu.  */
+      item = XCAR (tail);
       if (CONSP (item))
-       {
-         item1 = XCONS (item)->cdr;
-         if (CONSP (item1))
-           {
-             item_string = XCONS (item1)->car;
-             if (STRINGP (item_string))
-               {
-                 /* This is the real definition--the function to run.  */
-                 Lisp_Object def;
-                 /* These are the saved equivalent keyboard key sequence
-                    and its key-description.  */
-                 Lisp_Object descrip;
-                 Lisp_Object tem, enabled;
-
-                 /* 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);
-
-                 def = menu_item_equiv_key (item_string, item1, &descrip);
-                 enabled = menu_item_enabled_p (def, notreal);
-
-                 UNGCPRO;
-
-                 item_string = XCONS (item1)->car;
-
-                 tem = Fkeymapp (def);
-                 if (XSTRING (item_string)->data[0] == '@' && !NILP (tem))
-                   pending_maps = Fcons (Fcons (def, Fcons (item_string, XCONS (item)->car)),
-                                         pending_maps);
-                 else
-                   {
-                     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
-                     /* If definition is nil, pass nil as the key.  */
-                     push_menu_item (item_string, enabled,
-                                     XCONS (item)->car, def,
-                                     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,
-                                              maxdepth - 1);
-                         push_submenu_end ();
-                       }
-#endif
-                   }
-               }
-           }
-       }
+       single_menu_item (XCAR (item), XCDR (item),
+                         &pending_maps, notreal, maxdepth, &notbuttons);
       else if (VECTORP (item))
        {
          /* Loop over the char values represented in the vector.  */
@@ -617,68 +455,11 @@ single_keymap_panes (keymap, pane_name, prefix, notreal, maxdepth)
            {
              Lisp_Object character;
              XSETFASTINT (character, c);
-             item1 = XVECTOR (item)->contents[c];
-             if (CONSP (item1))
-               {
-                 item_string = XCONS (item1)->car;
-                 if (STRINGP (item_string))
-                   {
-                     Lisp_Object def;
-
-                     /* These are the saved equivalent keyboard key sequence
-                        and its key-description.  */
-                     Lisp_Object descrip;
-                     Lisp_Object tem, enabled;
-
-                     /* 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);
-                     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;
-
-                     tem = Fkeymapp (def);
-                     if (XSTRING (item_string)->data[0] == '@' && !NILP (tem))
-                       pending_maps = Fcons (Fcons (def, Fcons (item_string, character)),
-                                             pending_maps);
-                     else
-                       {
-                         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
-                         /* If definition is nil, pass nil as the key.  */
-                         push_menu_item (item_string, enabled, character,
-                                         def, descrip);
-#ifdef USE_X_TOOLKIT
-                         if (! NILP (submap))
-                           {
-                             push_submenu_start ();
-                             single_keymap_panes (submap, Qnil,
-                                                  character, notreal,
-                                                  maxdepth - 1);
-                             push_submenu_end ();
-                           }
-#endif
-                       }
-                   }
-               }
+             single_menu_item (character, XVECTOR (item)->contents[c],
+                               &pending_maps, notreal, maxdepth, &notbuttons);
            }
        }
+      UNGCPRO;
     }
 
   /* Process now any submenus which want to be panes at this level.  */
@@ -686,16 +467,157 @@ single_keymap_panes (keymap, pane_name, prefix, notreal, maxdepth)
     {
       Lisp_Object elt, eltcdr, string;
       elt = Fcar (pending_maps);
-      eltcdr = XCONS (elt)->cdr;
-      string = XCONS (eltcdr)->car;
+      eltcdr = XCDR (elt);
+      string = XCAR (eltcdr);
       /* 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, maxdepth - 1);
+                          XCDR (eltcdr), notreal, maxdepth - 1);
       pending_maps = Fcdr (pending_maps);
     }
 }
 \f
+/* This is a subroutine of single_keymap_panes that handles one
+   keymap entry.
+   KEY is a key in a keymap and ITEM is its binding. 
+   PENDING_MAPS_PTR points to a list of keymaps waiting to be made into
+   separate panes.
+   If NOTREAL is nonzero, only check for equivalent key bindings, don't
+   evaluate expressions in menu items and don't make any menu.
+   If we encounter submenus deeper than MAXDEPTH levels, ignore them.
+   NOTBUTTONS_PTR is only used when simulating toggle boxes and radio
+   buttons.  It points to variable notbuttons in single_keymap_panes,
+   which keeps track of if we have seen a button in this menu or not.  */
+
+static void
+single_menu_item (key, item, pending_maps_ptr, notreal, maxdepth,
+                 notbuttons_ptr)
+     Lisp_Object key, item;
+     Lisp_Object *pending_maps_ptr;
+     int maxdepth, notreal;
+     int *notbuttons_ptr;
+{
+  Lisp_Object map, item_string, enabled;
+  struct gcpro gcpro1, gcpro2;
+  int res;
+  
+  /* Parse the menu item and leave the result in item_properties.  */
+  GCPRO2 (key, item);
+  res = parse_menu_item (item, notreal, 0);
+  UNGCPRO;
+  if (!res)
+    return;                    /* Not a menu item.  */
+
+  map = XVECTOR (item_properties)->contents[ITEM_PROPERTY_MAP];
+  
+  if (notreal)
+    {
+      /* We don't want to make a menu, just traverse the keymaps to
+        precompute equivalent key bindings.  */
+      if (!NILP (map))
+       single_keymap_panes (map, Qnil, key, 1, maxdepth - 1);
+      return;
+    }
+
+  enabled = XVECTOR (item_properties)->contents[ITEM_PROPERTY_ENABLE];
+  item_string = XVECTOR (item_properties)->contents[ITEM_PROPERTY_NAME]; 
+
+  if (!NILP (map) && SREF (item_string, 0) == '@')
+    {
+      if (!NILP (enabled))
+       /* An enabled separate pane. Remember this to handle it later.  */
+       *pending_maps_ptr = Fcons (Fcons (map, Fcons (item_string, key)),
+                                  *pending_maps_ptr);
+      return;
+    }
+
+#ifndef HAVE_BOXES
+  /* Simulate radio buttons and toggle boxes by putting a prefix in
+     front of them.  */
+  {
+    Lisp_Object prefix = Qnil;
+    Lisp_Object type = XVECTOR (item_properties)->contents[ITEM_PROPERTY_TYPE];
+    if (!NILP (type))
+      {
+       Lisp_Object selected
+         = XVECTOR (item_properties)->contents[ITEM_PROPERTY_SELECTED];
+
+       if (*notbuttons_ptr)
+         /* The first button. Line up previous items in this menu.  */
+         {
+           int index = *notbuttons_ptr; /* Index for first item this menu.  */
+           int submenu = 0;
+           Lisp_Object tem;
+           while (index < menu_items_used)
+             {
+               tem
+                 = XVECTOR (menu_items)->contents[index + MENU_ITEMS_ITEM_NAME];
+               if (NILP (tem))
+                 {
+                   index++;
+                   submenu++;          /* Skip sub menu.  */
+                 }
+               else if (EQ (tem, Qlambda))
+                 {
+                   index++;
+                   submenu--;          /* End sub menu.  */
+                 }
+               else if (EQ (tem, Qt))
+                 index += 3;           /* Skip new pane marker. */
+               else if (EQ (tem, Qquote))
+                 index++;              /* Skip a left, right divider. */
+               else
+                 {
+                   if (!submenu && SREF (tem, 0) != '\0'
+                       && SREF (tem, 0) != '-')
+                     XVECTOR (menu_items)->contents[index + MENU_ITEMS_ITEM_NAME]
+                       = concat2 (build_string ("    "), tem);
+                   index += MENU_ITEMS_ITEM_LENGTH;
+                 }
+             }
+           *notbuttons_ptr = 0;
+         }
+
+       /* Calculate prefix, if any, for this item.  */
+       if (EQ (type, QCtoggle))
+         prefix = build_string (NILP (selected) ? "[ ] " : "[X] ");
+       else if (EQ (type, QCradio))
+         prefix = build_string (NILP (selected) ? "( ) " : "(*) ");
+      }
+    /* Not a button. If we have earlier buttons, then we need a prefix.  */
+    else if (!*notbuttons_ptr && SREF (item_string, 0) != '\0'
+            && SREF (item_string, 0) != '-')
+      prefix = build_string ("    ");
+
+    if (!NILP (prefix))
+      item_string = concat2 (prefix, item_string);
+  }
+#endif /* not HAVE_BOXES */
+#ifndef USE_X_TOOLKIT
+  if (!NILP(map))
+    /* Indicate visually that this is a submenu.  */
+    item_string = concat2 (item_string, build_string (" >"));
+#endif
+
+  push_menu_item (item_string, enabled, key,
+                 XVECTOR (item_properties)->contents[ITEM_PROPERTY_DEF],
+                 XVECTOR (item_properties)->contents[ITEM_PROPERTY_KEYEQ],
+                 XVECTOR (item_properties)->contents[ITEM_PROPERTY_TYPE],
+                 XVECTOR (item_properties)->contents[ITEM_PROPERTY_SELECTED],
+                 XVECTOR (item_properties)->contents[ITEM_PROPERTY_HELP]);
+
+#ifdef USE_X_TOOLKIT
+  /* Display a submenu using the toolkit.  */
+  if (! (NILP (map) || NILP (enabled)))
+    {
+      push_submenu_start ();
+      single_keymap_panes (map, Qnil, key, 0, maxdepth - 1);
+      push_submenu_end ();
+    }
+#endif
+}
+\f
 /* 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.  */
@@ -713,10 +635,10 @@ list_of_panes (menu)
       Lisp_Object elt, pane_name, pane_data;
       elt = Fcar (tail);
       pane_name = Fcar (elt);
-      CHECK_STRING (pane_name, 0);
+      CHECK_STRING (pane_name);
       push_menu_pane (pane_name, Qnil);
       pane_data = Fcdr (elt);
-      CHECK_CONS (pane_data, 0);
+      CHECK_CONS (pane_data);
       list_of_items (pane_data);
     }
 
@@ -735,66 +657,66 @@ list_of_items (pane)
     {
       item = Fcar (tail);
       if (STRINGP (item))
-       push_menu_item (item, Qnil, Qnil, Qt, Qnil);
+       push_menu_item (item, Qnil, Qnil, Qt, Qnil, Qnil, Qnil, Qnil);
       else if (NILP (item))
        push_left_right_boundary ();
       else
        {
-         CHECK_CONS (item, 0);
+         CHECK_CONS (item);
          item1 = Fcar (item);
-         CHECK_STRING (item1, 1);
-         push_menu_item (item1, Qt, Fcdr (item), Qt, Qnil);
+         CHECK_STRING (item1);
+         push_menu_item (item1, Qt, Fcdr (item), Qt, Qnil, Qnil, Qnil, Qnil);
        }
     }
 }
 \f
 DEFUN ("x-popup-menu", Fx_popup_menu, Sx_popup_menu, 2, 2, 0,
-  "Pop up a deck-of-cards menu and return user's selection.\n\
-POSITION is a position specification.  This is either a mouse button event\n\
-or a list ((XOFFSET YOFFSET) WINDOW)\n\
-where XOFFSET and YOFFSET are positions in pixels from the top left\n\
-corner of WINDOW's frame.  (WINDOW may be a frame object instead of a window.)\n\
-This controls the position of the center of the first line\n\
-in the first pane of the menu, not the top left of the menu as a whole.\n\
-If POSITION is t, it means to use the current mouse position.\n\
-\n\
-MENU is a specifier for a menu.  For the simplest case, MENU is a keymap.\n\
-The menu items come from key bindings that have a menu string as well as\n\
-a definition; actually, the \"definition\" in such a key binding looks like\n\
-\(STRING . REAL-DEFINITION).  To give the menu a title, put a string into\n\
-the keymap as a top-level element.\n\n\
-If REAL-DEFINITION is nil, that puts a nonselectable string in the menu.\n\
-Otherwise, REAL-DEFINITION should be a valid key binding definition.\n\
-\n\
-You can also use a list of keymaps as MENU.\n\
-  Then each keymap makes a separate pane.\n\
-When MENU is a keymap or a list of keymaps, the return value\n\
-is a list of events.\n\n\
-\n\
-Alternatively, you can specify a menu of multiple panes\n\
-  with a list of the form (TITLE PANE1 PANE2...),\n\
-where each pane is a list of form (TITLE ITEM1 ITEM2...).\n\
-Each ITEM is normally a cons cell (STRING . VALUE);\n\
-but a string can appear as an item--that makes a nonselectable line\n\
-in the menu.\n\
-With this form of menu, the return value is VALUE from the chosen item.\n\
-\n\
-If POSITION is nil, don't display the menu at all, just precalculate the\n\
-cached information about equivalent key sequences.")
-  (position, menu)
+       doc: /* Pop up a deck-of-cards menu and return user's selection.
+POSITION is a position specification.  This is either a mouse button event
+or a list ((XOFFSET YOFFSET) WINDOW)
+where XOFFSET and YOFFSET are positions in pixels from the top left
+corner of WINDOW's frame.  (WINDOW may be a frame object instead of a window.)
+This controls the position of the center of the first line
+in the first pane of the menu, not the top left of the menu as a whole.
+If POSITION is t, it means to use the current mouse position.
+
+MENU is a specifier for a menu.  For the simplest case, MENU is a keymap.
+The menu items come from key bindings that have a menu string as well as
+a definition; actually, the "definition" in such a key binding looks like
+\(STRING . REAL-DEFINITION).  To give the menu a title, put a string into
+the keymap as a top-level element.
+
+If REAL-DEFINITION is nil, that puts a nonselectable string in the menu.
+Otherwise, REAL-DEFINITION should be a valid key binding definition.
+
+You can also use a list of keymaps as MENU.
+  Then each keymap makes a separate pane.
+When MENU is a keymap or a list of keymaps, the return value
+is a list of events.
+
+Alternatively, you can specify a menu of multiple panes
+  with a list of the form (TITLE PANE1 PANE2...),
+where each pane is a list of form (TITLE ITEM1 ITEM2...).
+Each ITEM is normally a cons cell (STRING . VALUE);
+but a string can appear as an item--that makes a nonselectable line
+in the menu.
+With this form of menu, the return value is VALUE from the chosen item.
+
+If POSITION is nil, don't display the menu at all, just precalculate the
+cached information about equivalent key sequences.  */)
+     (position, menu)
      Lisp_Object position, menu;
 {
-  int number_of_panes, panes;
   Lisp_Object keymap, tem;
-  int xpos, ypos;
+  int xpos = 0, ypos = 0;
   Lisp_Object title;
   char *error_name;
   Lisp_Object selection;
-  int i, j;
-  FRAME_PTR f;
+  FRAME_PTR f = NULL;
   Lisp_Object x, y, window;
   int keymaps = 0;
   int for_click = 0;
+  int specpdl_count = SPECPDL_INDEX ();
   struct gcpro gcpro1;
 
 #ifdef HAVE_MENUS
@@ -804,12 +726,13 @@ cached information about equivalent key sequences.")
 
       /* Decode the first argument: find the window and the coordinates.  */
       if (EQ (position, Qt)
-         || (CONSP (position) && EQ (XCONS (position)->car, Qmenu_bar)))
+         || (CONSP (position) && (EQ (XCAR (position), Qmenu_bar)
+                                  || EQ (XCAR (position), Qtool_bar))))
        {
          /* Use the mouse's current position.  */
-         FRAME_PTR new_f = selected_frame;
+         FRAME_PTR new_f = SELECTED_FRAME ();
          Lisp_Object bar_window;
-         int part;
+         enum scroll_bar_part part;
          unsigned long time;
 
          if (mouse_position_hook)
@@ -844,8 +767,8 @@ cached information about equivalent key sequences.")
            }
        }
 
-      CHECK_NUMBER (x, 0);
-      CHECK_NUMBER (y, 0);
+      CHECK_NUMBER (x);
+      CHECK_NUMBER (y);
 
       /* Decode where to put the menu.  */
 
@@ -857,48 +780,43 @@ cached information about equivalent key sequences.")
        }
       else if (WINDOWP (window))
        {
-         CHECK_LIVE_WINDOW (window, 0);
+         CHECK_LIVE_WINDOW (window);
          f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
 
-         xpos = (FONT_WIDTH (f->output_data.x->font)
+         xpos = (FONT_WIDTH (FRAME_FONT (f))
                  * XFASTINT (XWINDOW (window)->left));
-         ypos = (f->output_data.x->line_height
+         ypos = (FRAME_LINE_HEIGHT (f)
                  * XFASTINT (XWINDOW (window)->top));
        }
       else
        /* ??? Not really clean; should be CHECK_WINDOW_OR_FRAME,
           but I don't want to make one now.  */
-       CHECK_WINDOW (window, 0);
+       CHECK_WINDOW (window);
 
       xpos += XINT (x);
       ypos += XINT (y);
-
-      XSETFRAME (Vmenu_updating_frame, f);
     }
   Vmenu_updating_frame = Qnil;
 #endif /* HAVE_MENUS */
 
+  record_unwind_protect (unuse_menu_items, Qnil);
   title = Qnil;
   GCPRO1 (title);
 
   /* Decode the menu items from what was specified.  */
 
-  keymap = Fkeymapp (menu);
-  tem = Qnil;
-  if (CONSP (menu))
-    tem = Fkeymapp (Fcar (menu));
-  if (!NILP (keymap))
+  keymap = get_keymap (menu, 0, 0);
+  if (CONSP (keymap))
     {
       /* We were given a keymap.  Extract menu info from the keymap.  */
       Lisp_Object prompt;
-      keymap = get_keymap (menu);
 
       /* Extract the detailed info to make one pane.  */
       keymap_panes (&menu, 1, NILP (position));
 
       /* Search for a string appearing directly as an element of the keymap.
         That string is the title of the menu.  */
-      prompt = map_prompt (keymap);
+      prompt = Fkeymap_prompt (keymap);
       if (NILP (title) && !NILP (prompt))
        title = prompt;
 
@@ -908,7 +826,7 @@ cached information about equivalent key sequences.")
 
       keymaps = 1;
     }
-  else if (!NILP (tem))
+  else if (CONSP (menu) && KEYMAPP (XCAR (menu)))
     {
       /* We were given a list of keymaps.  */
       int nmaps = XFASTINT (Flength (menu));
@@ -924,9 +842,9 @@ cached information about equivalent key sequences.")
        {
          Lisp_Object prompt;
 
-         maps[i++] = keymap = get_keymap (Fcar (tem));
+         maps[i++] = keymap = get_keymap (Fcar (tem), 1, 0);
 
-         prompt = map_prompt (keymap);
+         prompt = Fkeymap_prompt (keymap);
          if (NILP (title) && !NILP (prompt))
            title = prompt;
        }
@@ -944,13 +862,15 @@ cached information about equivalent key sequences.")
     {
       /* We were given an old-fashioned menu.  */
       title = Fcar (menu);
-      CHECK_STRING (title, 1);
+      CHECK_STRING (title);
 
       list_of_panes (Fcdr (menu));
 
       keymaps = 0;
     }
   
+  unbind_to (specpdl_count, Qnil);
+
   if (NILP (position))
     {
       discard_menu_items ();
@@ -978,37 +898,39 @@ cached information about equivalent key sequences.")
 #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\
-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)
+       doc: /* Pop up a dialog box and return user's selection.
+POSITION specifies which frame to use.
+This is normally a mouse button event or a window or frame.
+If POSITION is t, it means to use the frame the mouse is on.
+The dialog box appears in the middle of the specified frame.
+
+CONTENTS specifies the alternatives to display in the dialog box.
+It is a list of the form (TITLE ITEM1 ITEM2...).
+Each ITEM is a cons cell (STRING . VALUE).
+The return value is VALUE from the chosen item.
+
+An ITEM may also be just a string--that makes a nonselectable item.
+An ITEM may also be nil--that means to put all preceding items
+on the left of the dialog box and all following items on the right.
+\(By default, approximately half appear on each side.)  */)
+     (position, contents)
      Lisp_Object position, contents;
 {
-  FRAME_PTR f;
+  FRAME_PTR f = NULL;
   Lisp_Object window;
 
   check_x ();
 
   /* Decode the first argument: find the window or frame to use.  */
   if (EQ (position, Qt)
-      || (CONSP (position) && EQ (XCONS (position)->car, Qmenu_bar)))
+      || (CONSP (position) && (EQ (XCAR (position), Qmenu_bar)
+                              || EQ (XCAR (position), Qtool_bar))))
     {
 #if 0 /* Using the frame the mouse is on may not be right.  */
       /* Use the mouse's current position.  */
-      FRAME_PTR new_f = selected_frame;
+      FRAME_PTR new_f = SELECTED_FRAME ();
       Lisp_Object bar_window;
-      int part;
+      enum scroll_bar_part part;
       unsigned long time;
       Lisp_Object x, y;
 
@@ -1044,13 +966,13 @@ on the left of the dialog box and all following items on the right.\n\
     f = XFRAME (window);
   else if (WINDOWP (window))
     {
-      CHECK_LIVE_WINDOW (window, 0);
+      CHECK_LIVE_WINDOW (window);
       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);
+    CHECK_WINDOW (window);
 
 #ifndef USE_X_TOOLKIT
   /* Display a menu with these alternatives
@@ -1070,10 +992,12 @@ on the left of the dialog box and all following items on the right.\n\
     Lisp_Object title;
     char *error_name;
     Lisp_Object selection;
+    int specpdl_count = SPECPDL_INDEX ();
 
     /* Decode the dialog items from what was specified.  */
     title = Fcar (contents);
-    CHECK_STRING (title, 1);
+    CHECK_STRING (title);
+    record_unwind_protect (unuse_menu_items, Qnil);
 
     list_of_panes (Fcons (contents, Qnil));
 
@@ -1082,6 +1006,7 @@ on the left of the dialog box and all following items on the right.\n\
     selection = xdialog_show (f, 0, title, &error_name);
     UNBLOCK_INPUT;
 
+    unbind_to (specpdl_count, Qnil);
     discard_menu_items ();
 
     if (error_name) error (error_name);
@@ -1092,31 +1017,44 @@ on the left of the dialog box and all following items on the right.\n\
 \f
 #ifdef USE_X_TOOLKIT
 
+/* 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;
+  };
+
+/* It is ok that this queue is a static variable,
+   because init_menu_items won't allow the menu mechanism
+   to be entered recursively.  */
+static struct event_queue *popup_get_selection_queue;
+
+static Lisp_Object popup_get_selection_unwind ();
+
 /* Loop in Xt until the menu pulldown or dialog popup has been
    popped down (deactivated).  This is used for x-popup-menu
-   and x-popup-dialog; it is not used for the menu bar any more.
+   and x-popup-dialog; it is not used for the menu bar.
+
+   If DO_TIMERS is nonzero, run timers.
 
    NOTE: All calls to popup_get_selection should be protected
    with BLOCK_INPUT, UNBLOCK_INPUT wrappers.  */
 
-void
-popup_get_selection (initial_event, dpyinfo, id)
+static void
+popup_get_selection (initial_event, dpyinfo, id, do_timers)
      XEvent *initial_event;
      struct x_display_info *dpyinfo;
      LWLIB_ID id;
+     int do_timers;
 {
   XEvent event;
-
-  /* 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;
+  int count = SPECPDL_INDEX ();
+
+  popup_get_selection_queue = NULL;
+
+  record_unwind_protect (popup_get_selection_unwind, Qnil);
 
   if (initial_event)
     event = *initial_event;
@@ -1148,8 +1086,12 @@ popup_get_selection (initial_event, dpyinfo, id)
       else if (event.type == KeyPress
               && dpyinfo->display == event.xbutton.display)
        {
-         popup_activated_flag = 0;
-         break;
+         KeySym keysym = XLookupKeysym (&event.xkey, 0);
+         if (!IsModifierKey (keysym))
+           {
+             popup_activated_flag = 0;
+             break;
+           }
        }
       /* Button presses outside the menu also pop it down.  */
       else if (event.type == ButtonPress
@@ -1171,30 +1113,41 @@ popup_get_selection (initial_event, dpyinfo, id)
          && (event.xany.display != dpyinfo->display
              || x_non_menubar_window_to_frame (dpyinfo, event.xany.window)))
        {
-         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;
-           }
+         queue_tmp = (struct event_queue *) xmalloc (sizeof *queue_tmp);
+         queue_tmp->event = event;
+         queue_tmp->next = popup_get_selection_queue;
+         popup_get_selection_queue = queue_tmp;
        }
       else
        XtDispatchEvent (&event);
 
-      if (!popup_activated ())
+      /* If the event deactivated the menu, we are finished.  */
+      if (!popup_activated_flag)
        break;
+
+      /* If we have no events to run, consider timers.  */
+      if (do_timers && !XtAppPending (Xt_app_con))
+       timer_check (1);
+
       XtAppNextEvent (Xt_app_con, &event);
     }
 
-  /* Unread any events that we got but did not handle.  */
-  while (queue != NULL) 
+  unbind_to (count, Qnil);
+}
+
+/* Unread any events that popup_get_selection read but did not handle.  */
+
+static Lisp_Object
+popup_get_selection_unwind (ignore)
+     Lisp_Object ignore;
+{
+  while (popup_get_selection_queue != NULL) 
     {
-      queue_tmp = queue;
+      struct event_queue *queue_tmp;
+      queue_tmp = popup_get_selection_queue;
       XPutBackEvent (queue_tmp->event.xany.display, &queue_tmp->event);
-      queue = queue_tmp->next;
-      free ((char *)queue_tmp);
+      popup_get_selection_queue = queue_tmp->next;
+      xfree ((char *)queue_tmp);
       /* Cause these events to get read as soon as we UNBLOCK_INPUT.  */
       interrupt_input_pending = 1;
     }
@@ -1202,7 +1155,7 @@ 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.
+   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.
@@ -1214,6 +1167,7 @@ popup_get_selection (initial_event, dpyinfo, id)
    passing it to the toolkit right away, is that we can safely
    execute Lisp code.  */
    
+void
 x_activate_menubar (f)
      FRAME_PTR f;
 {
@@ -1222,7 +1176,7 @@ x_activate_menubar (f)
 
   set_frame_menubar (f, 0, 1);
   BLOCK_INPUT;
-  XtDispatchEvent ((XEvent *) f->output_data.x->saved_menu_event);
+  XtDispatchEvent (f->output_data.x->saved_menu_event);
   UNBLOCK_INPUT;
 #ifdef USE_MOTIF
   if (f->output_data.x->saved_menu_event->type == ButtonRelease)
@@ -1241,7 +1195,6 @@ popup_activated ()
   return popup_activated_flag;
 }
 
-
 /* This callback is invoked when the user selects a menubar cascade
    pushbutton, but before the pulldown menu is posted.  */
 
@@ -1254,6 +1207,63 @@ popup_activate_callback (widget, id, client_data)
   popup_activated_flag = 1;
 }
 
+/* This callback is invoked when a dialog or menu is finished being
+   used and has been unposted.  */
+
+static void
+popup_deactivate_callback (widget, id, client_data)
+     Widget widget;
+     LWLIB_ID id;
+     XtPointer client_data;
+{
+  popup_activated_flag = 0;
+}
+
+/* Lwlib callback called when menu items are highlighted/unhighlighted
+   while moving the mouse over them.  WIDGET is the menu bar or menu
+   popup widget.  ID is its LWLIB_ID.  CALL_DATA contains a pointer to
+   the widget_value structure for the menu item, or null in case of
+   unhighlighting.  */
+
+void
+menu_highlight_callback (widget, id, call_data)
+     Widget widget;
+     LWLIB_ID id;
+     void *call_data;
+{
+  widget_value *wv = (widget_value *) call_data;
+  struct frame *f;
+  Lisp_Object frame, help;
+
+  help = wv ? wv->help : Qnil;
+  
+  /* Determine the frame for the help event.  */
+  f = menubar_id_to_frame (id);
+  if (f)
+    {
+      XSETFRAME (frame, f);
+      kbd_buffer_store_help_event (frame, help);
+    }
+  else
+    {
+      /* WIDGET is the popup menu.  It's parent is the frame's 
+        widget.  See which frame that is.  */
+      Widget frame_widget = XtParent (widget);
+      Lisp_Object tail;
+
+      for (tail = Vframe_list; GC_CONSP (tail); tail = XCDR (tail))
+       {
+         frame = XCAR (tail);
+         if (GC_FRAMEP (frame)
+             && (f = XFRAME (frame),
+                 FRAME_X_P (f) && f->output_data.x->widget == frame_widget))
+           break;
+       }
+
+      show_help_echo (help, Qnil, Qnil, Qnil, 1);
+    }
+}
+
 /* This callback is called from the menu bar pulldown menu
    when the user makes a selection.
    Figure out what the user chose
@@ -1274,6 +1284,7 @@ menubar_selection_callback (widget, id, client_data)
 
   if (!f)
     return;
+  entry = Qnil;
   subprefix_stack = (Lisp_Object *) alloca (f->menu_bar_items_used * sizeof (Lisp_Object));
   vector = f->menu_bar_vector;
   prefix = Qnil;
@@ -1308,27 +1319,31 @@ menubar_selection_callback (widget, id, client_data)
              Lisp_Object frame;
 
              XSETFRAME (frame, f);
-             buf.kind = menu_bar_event;
-             buf.frame_or_window = Fcons (frame, Fcons (Qmenu_bar, Qnil));
+             buf.kind = MENU_BAR_EVENT;
+             buf.frame_or_window = frame;
+             buf.arg = frame;
              kbd_buffer_store_event (&buf);
 
              for (j = 0; j < submenu_depth; j++)
                if (!NILP (subprefix_stack[j]))
                  {
-                   buf.kind = menu_bar_event;
-                   buf.frame_or_window = Fcons (frame, subprefix_stack[j]);
+                   buf.kind = MENU_BAR_EVENT;
+                   buf.frame_or_window = frame;
+                   buf.arg = subprefix_stack[j];
                    kbd_buffer_store_event (&buf);
                  }
 
              if (!NILP (prefix))
                {
-                 buf.kind = menu_bar_event;
-                 buf.frame_or_window = Fcons (frame, prefix);
+                 buf.kind = MENU_BAR_EVENT;
+                 buf.frame_or_window = frame;
+                 buf.arg = prefix;
                  kbd_buffer_store_event (&buf);
                }
 
-             buf.kind = menu_bar_event;
-             buf.frame_or_window = Fcons (frame, entry);
+             buf.kind = MENU_BAR_EVENT;
+             buf.frame_or_window = frame;
+             buf.arg = entry;
              kbd_buffer_store_event (&buf);
 
              return;
@@ -1338,18 +1353,6 @@ menubar_selection_callback (widget, id, client_data)
     }
 }
 
-/* This callback is invoked when a dialog or menu is finished being
-   used and has been unposted.  */
-
-static void
-popup_deactivate_callback (widget, id, client_data)
-     Widget widget;
-     LWLIB_ID id;
-     XtPointer client_data;
-{
-  popup_activated_flag = 0;
-}
-
 /* Allocate a widget_value, blocking input.  */
 
 widget_value *
@@ -1392,23 +1395,18 @@ free_menubar_widget_value_tree (wv)
   UNBLOCK_INPUT;
 }
 \f
-/* Return a tree of widget_value structures for a menu bar item
+/* Set up data in menu_items for a menu bar item
    whose event type is ITEM_KEY (with string ITEM_NAME)
    and whose contents come from the list of keymaps MAPS.  */
 
-static widget_value *
-single_submenu (item_key, item_name, maps)
+static int
+parse_single_submenu (item_key, item_name, maps)
      Lisp_Object item_key, item_name, maps;
 {
-  widget_value *wv, *prev_wv, *save_wv, *first_wv;
-  int i;
-  int submenu_depth = 0;
   Lisp_Object length;
   int len;
   Lisp_Object *mapvec;
-  widget_value **submenu_stack;
-  int mapno;
-  int previous_items = menu_items_used;
+  int i;
   int top_level_items = 0;
 
   length = Flength (maps);
@@ -1422,28 +1420,44 @@ single_submenu (item_key, item_name, maps)
       maps = Fcdr (maps);
     }
 
-  menu_items_n_panes = 0;
-
   /* Loop over the given keymaps, making a pane for each map.
      But don't make a pane that is empty--ignore that map instead.  */
   for (i = 0; i < len; i++)
     {
-      if (SYMBOLP (mapvec[i])
-         || (CONSP (mapvec[i])
-             && NILP (Fkeymapp (mapvec[i]))))
+      if (!KEYMAPP (mapvec[i]))
        {
          /* Here we have a command at top level in the menu bar
             as opposed to a submenu.  */
          top_level_items = 1;
          push_menu_pane (Qnil, Qnil);
-         push_menu_item (item_name, Qt, item_key, mapvec[i], Qnil);
+         push_menu_item (item_name, Qt, item_key, mapvec[i],
+                         Qnil, Qnil, Qnil, Qnil);
        }
       else
-       single_keymap_panes (mapvec[i], item_name, item_key, 0, 10);
+       {
+         Lisp_Object prompt;
+         prompt = Fkeymap_prompt (mapvec[i]);
+         single_keymap_panes (mapvec[i],
+                              !NILP (prompt) ? prompt : item_name,
+                              item_key, 0, 10);
+       }
     }
 
-  /* Create a tree of widget_value objects
-     representing the panes and their items.  */
+  return top_level_items;
+}
+
+/* Create a tree of widget_value objects
+   representing the panes and items
+   in menu_items starting at index START, up to index END.  */
+
+static widget_value *
+digest_single_submenu (start, end, top_level_items)
+     int start, end, top_level_items;
+{
+  widget_value *wv, *prev_wv, *save_wv, *first_wv;
+  int i;
+  int submenu_depth = 0;
+  widget_value **submenu_stack;
 
   submenu_stack
     = (widget_value **) alloca (menu_items_used * sizeof (widget_value *));
@@ -1451,16 +1465,18 @@ single_submenu (item_key, item_name, maps)
   wv->name = "menu";
   wv->value = 0;
   wv->enabled = 1;
+  wv->button_type = BUTTON_TYPE_NONE;
+  wv->help = Qnil;
   first_wv = wv;
   save_wv = 0;
   prev_wv = 0;
  
-  /* Loop over all panes and items made during this call
-     and construct a tree of widget_value objects.
-     Ignore the panes and items made by previous calls to
-     single_submenu, even though those are also in menu_items.  */
-  i = previous_items;
-  while (i < menu_items_used)
+  /* Loop over all panes and items made by the preceding call
+     to parse_single_submenu and construct a tree of widget_value objects.
+     Ignore the panes and items used by previous calls to
+     digest_single_submenu, even though those are also in menu_items.  */
+  i = start;
+  while (i < end)
     {
       if (EQ (XVECTOR (menu_items)->contents[i], Qnil))
        {
@@ -1487,10 +1503,19 @@ single_submenu (item_key, item_name, maps)
          /* Create a new pane.  */
          Lisp_Object pane_name, prefix;
          char *pane_string;
+         
          pane_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_NAME];
          prefix = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
+         
+#ifndef HAVE_MULTILINGUAL_MENU
+         if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name))
+           {
+             pane_name = ENCODE_SYSTEM (pane_name);
+             AREF (menu_items, i + MENU_ITEMS_PANE_NAME) = pane_name;
+           }
+#endif
          pane_string = (NILP (pane_name)
-                        ? "" : (char *) XSTRING (pane_name)->data);
+                        ? "" : (char *) SDATA (pane_name));
          /* If there is just one top-level pane, put all its items directly
             under the top-level menu.  */
          if (menu_items_n_panes == 1)
@@ -1513,6 +1538,8 @@ single_submenu (item_key, item_name, maps)
                wv->name++;
              wv->value = 0;
              wv->enabled = 1;
+             wv->button_type = BUTTON_TYPE_NONE;
+             wv->help = Qnil;
            }
          save_wv = wv;
          prev_wv = 0;
@@ -1521,12 +1548,30 @@ single_submenu (item_key, item_name, maps)
       else
        {
          /* Create a new item within current pane.  */
-         Lisp_Object item_name, enable, descrip, def;
-         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];
-         def = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_DEFINITION];
+         Lisp_Object item_name, enable, descrip, def, type, selected;
+         Lisp_Object help;
+         
+         item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
+         enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
+         descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY);
+         def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION);
+         type = AREF (menu_items, i + MENU_ITEMS_ITEM_TYPE);
+         selected = AREF (menu_items, i + MENU_ITEMS_ITEM_SELECTED);
+         help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP);
+
+#ifndef HAVE_MULTILINGUAL_MENU
+          if (STRING_MULTIBYTE (item_name))
+           {
+             item_name = ENCODE_SYSTEM (item_name);
+             AREF (menu_items, i + MENU_ITEMS_ITEM_NAME) = item_name;
+           }
+         
+          if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
+           {
+             descrip = ENCODE_SYSTEM (descrip);
+             AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY) = descrip;
+           }
+#endif /* not HAVE_MULTILINGUAL_MENU */
 
          wv = xmalloc_widget_value ();
          if (prev_wv) 
@@ -1534,14 +1579,30 @@ single_submenu (item_key, item_name, maps)
          else
            save_wv->contents = wv;
 
-         wv->name = (char *) XSTRING (item_name)->data;
+         wv->name = (char *) SDATA (item_name);
          if (!NILP (descrip))
-           wv->key = (char *) XSTRING (descrip)->data;
+           wv->key = (char *) SDATA (descrip);
          wv->value = 0;
          /* The EMACS_INT cast avoids a warning.  There's no problem
             as long as pointers have enough bits to hold small integers.  */
          wv->call_data = (!NILP (def) ? (void *) (EMACS_INT) i : 0);
          wv->enabled = !NILP (enable);
+         
+         if (NILP (type))
+           wv->button_type = BUTTON_TYPE_NONE;
+         else if (EQ (type, QCradio))
+           wv->button_type = BUTTON_TYPE_RADIO;
+         else if (EQ (type, QCtoggle))
+           wv->button_type = BUTTON_TYPE_TOGGLE;
+         else
+           abort ();
+
+         wv->selected = !NILP (selected);
+         if (! STRINGP (help))
+           help = Qnil;
+
+         wv->help = help;
+
          prev_wv = wv;
 
          i += MENU_ITEMS_ITEM_LENGTH;
@@ -1560,56 +1621,39 @@ single_submenu (item_key, item_name, maps)
   return first_wv;
 }
 \f
-extern void EmacsFrameSetCharSize ();
-
-/* Recompute all the widgets of frame F, when the menu bar
-   has been changed.  */
+/* Recompute all the widgets of frame F, when the menu bar has been
+   changed.  Value is non-zero if widgets were updated.  */
 
-static void
+static int
 update_frame_menubar (f)
      FRAME_PTR f;
 {
   struct x_output *x = f->output_data.x;
   int columns, rows;
-  int menubar_changed;
   
-  Dimension shell_height;
-
-  /* We assume the menubar contents has changed if the global flag is set,
-     or if the current buffer has changed, or if the menubar has never
-     been updated before.
-   */
-  menubar_changed = (x->menubar_widget
-                    && !XtIsManaged (x->menubar_widget));
-
-  if (! (menubar_changed))
-    return;
+  if (!x->menubar_widget || XtIsManaged (x->menubar_widget))
+    return 0;
 
   BLOCK_INPUT;
-  /* Save the size of the frame because the pane widget doesn't accept to
-     resize itself. So force it.  */
+  /* 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;
 
-  /* Do the voodoo which means "I'm changing lots of things, don't try to
-     refigure sizes until I'm done." */
+  /* Do the voodoo which means "I'm changing lots of things, don't try
+     to refigure sizes until I'm done."  */
   lw_refigure_widget (x->column_widget, False);
 
-  /* the order in which children are managed is the top to
-     bottom order in which they are displayed in the paned window.
-     First, remove the text-area widget.
-   */
+  /* The order in which children are managed is the top to bottom
+     order in which they are displayed in the paned window.  First,
+     remove the text-area widget.  */
   XtUnmanageChild (x->edit_widget);
 
-  /* remove the menubar that is there now, and put up the menubar that
-     should be there.
-   */
-  if (menubar_changed)
-    {
-      XtManageChild (x->menubar_widget);
-      XtMapWidget (x->menubar_widget);
-      XtVaSetValues (x->menubar_widget, XtNmappedWhenManaged, 1, 0);
-    }
+  /* Remove the menubar that is there now, and put up the menubar that
+     should be there.  */
+  XtManageChild (x->menubar_widget);
+  XtMapWidget (x->menubar_widget);
+  XtVaSetValues (x->menubar_widget, XtNmappedWhenManaged, 1, NULL);
 
   /* Re-manage the text-area widget, and then thrash the sizes.  */
   XtManageChild (x->edit_widget);
@@ -1617,8 +1661,8 @@ update_frame_menubar (f)
 
   /* Force the pane widget to resize itself with the right values.  */
   EmacsFrameSetCharSize (x->edit_widget, columns, rows);
-
   UNBLOCK_INPUT;
+  return 1;
 }
 
 /* Set the contents of the menubar widgets of frame F.
@@ -1632,9 +1676,12 @@ set_frame_menubar (f, first_time, deep_p)
      int deep_p;
 {
   Widget menubar_widget = f->output_data.x->menubar_widget;
-  Lisp_Object tail, items, frame;
+  Lisp_Object items;
   widget_value *wv, *first_wv, *prev_wv = 0;
-  int i;
+  int i, last_i;
+  int *submenu_start, *submenu_end;
+  int *submenu_top_level_items, *submenu_n_panes;
+
   LWLIB_ID id;
 
   XSETFRAME (Vmenu_updating_frame, f);
@@ -1655,19 +1702,13 @@ set_frame_menubar (f, first_time, deep_p)
       f->output_data.x->saved_menu_event->type = 0;
     }
 
-  wv = xmalloc_widget_value ();
-  wv->name = "menubar";
-  wv->value = 0;
-  wv->enabled = 1;
-  first_wv = wv;
-
   if (deep_p)
     {
       /* 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 specpdl_count = SPECPDL_INDEX ();
       int previous_menu_items_used = f->menu_bar_items_used;
       Lisp_Object *previous_items
        = (Lisp_Object *) alloca (previous_menu_items_used
@@ -1684,7 +1725,8 @@ set_frame_menubar (f, first_time, deep_p)
         because it is not reentrant.  */
       specbind (Qdebug_on_next_call, Qnil);
 
-      record_unwind_protect (Fstore_match_data, Fmatch_data (Qnil, Qnil));
+      record_unwind_protect (Fset_match_data, Fmatch_data (Qnil, Qnil));
+      record_unwind_protect (unuse_menu_items, Qnil);
       if (NILP (Voverriding_local_map_menu_flag))
        {
          specbind (Qoverriding_terminal_local_map, Qnil);
@@ -1694,7 +1736,8 @@ set_frame_menubar (f, first_time, deep_p)
       set_buffer_internal_1 (XBUFFER (buffer));
 
       /* Run the Lucid hook.  */
-      call1 (Vrun_hooks, Qactivate_menubar_hook);
+      safe_run_hooks (Qactivate_menubar_hook);
+      
       /* If it has changed current-menubar from previous value,
         really recompute the menubar from the value.  */
       if (! NILP (Vlucid_menu_bar_dirty_flag))
@@ -1704,38 +1747,71 @@ set_frame_menubar (f, first_time, deep_p)
 
       items = FRAME_MENU_BAR_ITEMS (f);
 
-      inhibit_garbage_collection ();
-
       /* Save the frame's previous menu bar contents data.  */
-      bcopy (XVECTOR (f->menu_bar_vector)->contents, previous_items,
-            previous_menu_items_used * sizeof (Lisp_Object));
+      if (previous_menu_items_used)
+       bcopy (XVECTOR (f->menu_bar_vector)->contents, previous_items,
+              previous_menu_items_used * sizeof (Lisp_Object));
 
-      /* Fill in the current menu bar contents.  */
+      /* Fill in menu_items with the current menu bar contents.
+        This can evaluate Lisp code.  */
       menu_items = f->menu_bar_vector;
-      menu_items_allocated = XVECTOR (menu_items)->size;
+      menu_items_allocated = VECTORP (menu_items) ? ASIZE (menu_items) : 0;
+      submenu_start = (int *) alloca (XVECTOR (items)->size * sizeof (int *));
+      submenu_end = (int *) alloca (XVECTOR (items)->size * sizeof (int *));
+      submenu_n_panes = (int *) alloca (XVECTOR (items)->size * sizeof (int));
+      submenu_top_level_items
+       = (int *) alloca (XVECTOR (items)->size * sizeof (int *));
       init_menu_items ();
       for (i = 0; i < XVECTOR (items)->size; i += 4)
        {
          Lisp_Object key, string, maps;
 
+         last_i = i;
+
          key = XVECTOR (items)->contents[i];
          string = XVECTOR (items)->contents[i + 1];
          maps = XVECTOR (items)->contents[i + 2];
          if (NILP (string))
            break;
 
-         wv = single_submenu (key, string, maps);
+         submenu_start[i] = menu_items_used;
+
+         menu_items_n_panes = 0;
+         submenu_top_level_items[i]
+           = parse_single_submenu (key, string, maps);
+         submenu_n_panes[i] = menu_items_n_panes;
+
+         submenu_end[i] = menu_items_used;
+       }
+
+      finish_menu_items ();
+
+      /* Convert menu_items into widget_value trees
+        to display the menu.  This cannot evaluate Lisp code.  */
+
+      wv = xmalloc_widget_value ();
+      wv->name = "menubar";
+      wv->value = 0;
+      wv->enabled = 1;
+      wv->button_type = BUTTON_TYPE_NONE;
+      wv->help = Qnil;
+      first_wv = wv;
+
+      for (i = 0; i < last_i; i += 4)
+       {
+         menu_items_n_panes = submenu_n_panes[i];
+         wv = digest_single_submenu (submenu_start[i], submenu_end[i],
+                                     submenu_top_level_items[i]);
          if (prev_wv) 
            prev_wv->next = wv;
          else
            first_wv->contents = wv;
          /* Don't set wv->name here; GC during the loop might relocate it.  */
          wv->enabled = 1;
+         wv->button_type = BUTTON_TYPE_NONE;
          prev_wv = wv;
        }
 
-      finish_menu_items ();
-
       set_buffer_internal_1 (prev);
       unbind_to (specpdl_count, Qnil);
 
@@ -1749,7 +1825,7 @@ set_frame_menubar (f, first_time, deep_p)
       if (i == menu_items_used && i == previous_menu_items_used && i != 0)
        {
          free_menubar_widget_value_tree (first_wv);
-         menu_items = Qnil;
+         discard_menu_items ();
 
          return;
        }
@@ -1763,19 +1839,27 @@ set_frame_menubar (f, first_time, deep_p)
          string = XVECTOR (items)->contents[i + 1];
          if (NILP (string))
            break;
-         wv->name = (char *) XSTRING (string)->data;
+         wv->name = (char *) SDATA (string);
          wv = wv->next;
        }
 
       f->menu_bar_vector = menu_items;
       f->menu_bar_items_used = menu_items_used;
-      menu_items = Qnil;
+      discard_menu_items ();
     }
   else
     {
       /* Make a widget-value tree containing
         just the top level menu bar strings.  */
 
+      wv = xmalloc_widget_value ();
+      wv->name = "menubar";
+      wv->value = 0;
+      wv->enabled = 1;
+      wv->button_type = BUTTON_TYPE_NONE;
+      wv->help = Qnil;
+      first_wv = wv;
+
       items = FRAME_MENU_BAR_ITEMS (f);
       for (i = 0; i < XVECTOR (items)->size; i += 4)
        {
@@ -1786,9 +1870,11 @@ set_frame_menubar (f, first_time, deep_p)
            break;
 
          wv = xmalloc_widget_value ();
-         wv->name = (char *) XSTRING (string)->data;
+         wv->name = (char *) SDATA (string);
          wv->value = 0;
          wv->enabled = 1;
+         wv->button_type = BUTTON_TYPE_NONE;
+         wv->help = Qnil;
          /* This prevents lwlib from assuming this
             menu item is really supposed to be empty.  */
          /* The EMACS_INT cast avoids a warning.
@@ -1831,7 +1917,8 @@ set_frame_menubar (f, first_time, deep_p)
                                         0,
                                         popup_activate_callback,
                                         menubar_selection_callback,
-                                        popup_deactivate_callback);
+                                        popup_deactivate_callback,
+                                        menu_highlight_callback);
       f->output_data.x->menubar_widget = menubar_widget;
     }
 
@@ -1859,7 +1946,6 @@ set_frame_menubar (f, first_time, deep_p)
   }
   
   free_menubar_widget_value_tree (first_wv);
-
   update_frame_menubar (f);
 
   UNBLOCK_INPUT;
@@ -1880,6 +1966,7 @@ initialize_frame_menubar (f)
   set_frame_menubar (f, 1, 1);
 }
 
+
 /* Get rid of the menu bar of frame F, and free its storage.
    This is used when deleting a frame, and when turning off the menu bar.  */
 
@@ -1888,14 +1975,44 @@ free_frame_menubar (f)
      FRAME_PTR f;
 {
   Widget menubar_widget;
-  int id;
 
   menubar_widget = f->output_data.x->menubar_widget;
+
+  f->output_data.x->menubar_height = 0;
   
   if (menubar_widget)
     {
+#ifdef USE_MOTIF
+      /* Removing the menu bar magically changes the shell widget's x
+        and y position of (0, 0) which, when the menu bar is turned
+        on again, leads to pull-down menuss appearing in strange
+        positions near the upper-left corner of the display.  This
+        happens only with some window managers like twm and ctwm,
+        but not with other like Motif's mwm or kwm, because the
+        latter generate ConfigureNotify events when the menu bar
+        is switched off, which fixes the shell position.  */
+      Position x0, y0, x1, y1;
+#endif
+      
       BLOCK_INPUT;
+
+#ifdef USE_MOTIF
+      if (f->output_data.x->widget)
+       XtVaGetValues (f->output_data.x->widget, XtNx, &x0, XtNy, &y0, NULL);
+#endif
+      
       lw_destroy_all_widgets ((LWLIB_ID) f->output_data.x->id);
+      f->output_data.x->menubar_widget = NULL;
+
+#ifdef USE_MOTIF
+      if (f->output_data.x->widget)
+       {
+         XtVaGetValues (f->output_data.x->widget, XtNx, &x1, XtNy, &y1, NULL);
+         if (x1 == 0 && y1 == 0)
+           XtVaSetValues (f->output_data.x->widget, XtNx, x0, XtNy, y0, NULL);
+       }
+#endif
+      
       UNBLOCK_INPUT;
     }
 }
@@ -1930,11 +2047,7 @@ free_frame_menubar (f)
    next_menubar_widget_id.  */
 LWLIB_ID widget_id_tick;
 
-#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)
@@ -1969,7 +2082,6 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
   XButtonPressedEvent dummy;
 
   int first_pane;
-  int next_release_must_exit = 0;
 
   *error = NULL;
 
@@ -1985,6 +2097,8 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
   wv->name = "menu";
   wv->value = 0;
   wv->enabled = 1;
+  wv->button_type = BUTTON_TYPE_NONE;
+  wv->help =Qnil;
   first_wv = wv;
   first_pane = 1;
  
@@ -2019,10 +2133,19 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
          /* Create a new pane.  */
          Lisp_Object pane_name, prefix;
          char *pane_string;
-         pane_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_NAME];
-         prefix = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
+         
+         pane_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME);
+         prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
+         
+#ifndef HAVE_MULTILINGUAL_MENU
+         if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name))
+           {
+             pane_name = ENCODE_SYSTEM (pane_name);
+             AREF (menu_items, i + MENU_ITEMS_PANE_NAME) = pane_name;
+           }
+#endif
          pane_string = (NILP (pane_name)
-                        ? "" : (char *) XSTRING (pane_name)->data);
+                        ? "" : (char *) SDATA (pane_name));
          /* If there is just one top-level pane, put all its items directly
             under the top-level menu.  */
          if (menu_items_n_panes == 1)
@@ -2043,6 +2166,8 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
                wv->name++;
              wv->value = 0;
              wv->enabled = 1;
+             wv->button_type = BUTTON_TYPE_NONE;
+             wv->help = Qnil;
              save_wv = wv;
              prev_wv = 0;
            }
@@ -2057,21 +2182,37 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
       else
        {
          /* Create a new item within current pane.  */
-         Lisp_Object item_name, enable, descrip, def;
-         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];
-         def = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_DEFINITION];
-
+         Lisp_Object item_name, enable, descrip, def, type, selected, help;
+         item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
+         enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
+         descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY);
+         def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION);
+         type = AREF (menu_items, i + MENU_ITEMS_ITEM_TYPE);
+         selected = AREF (menu_items, i + MENU_ITEMS_ITEM_SELECTED);
+         help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP);
+
+#ifndef HAVE_MULTILINGUAL_MENU
+          if (STRINGP (item_name) && STRING_MULTIBYTE (item_name))
+           {
+             item_name = ENCODE_SYSTEM (item_name);
+             AREF (menu_items, i + MENU_ITEMS_ITEM_NAME) = item_name;
+           }
+         
+          if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
+           {
+             descrip = ENCODE_SYSTEM (descrip);
+             AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY) = descrip;
+           }
+#endif /* not HAVE_MULTILINGUAL_MENU */
          wv = xmalloc_widget_value ();
          if (prev_wv) 
            prev_wv->next = wv;
          else 
            save_wv->contents = wv;
-         wv->name = (char *) XSTRING (item_name)->data;
+         wv->name = (char *) SDATA (item_name);
          if (!NILP (descrip))
-           wv->key = (char *) XSTRING (descrip)->data;
+           wv->key = (char *) SDATA (descrip);
          wv->value = 0;
          /* If this item has a null value,
             make the call_data null so that it won't display a box
@@ -2079,6 +2220,23 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
          wv->call_data
            = (!NILP (def) ? (void *) &XVECTOR (menu_items)->contents[i] : 0);
          wv->enabled = !NILP (enable);
+
+         if (NILP (type))
+           wv->button_type = BUTTON_TYPE_NONE;
+         else if (EQ (type, QCtoggle))
+           wv->button_type = BUTTON_TYPE_TOGGLE;
+         else if (EQ (type, QCradio))
+           wv->button_type = BUTTON_TYPE_RADIO;
+         else
+           abort ();
+
+         wv->selected = !NILP (selected);
+
+          if (! STRINGP (help))
+           help = Qnil;
+
+         wv->help = help;
+
          prev_wv = wv;
 
          i += MENU_ITEMS_ITEM_LENGTH;
@@ -2094,13 +2252,22 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
 
       wv_sep2->name = "--";
       wv_sep2->next = first_wv->contents;
+      wv_sep2->help = Qnil;
 
       wv_sep1->name = "--";
       wv_sep1->next = wv_sep2;
+      wv_sep1->help = Qnil;
 
-      wv_title->name = (char *) XSTRING (title)->data;
-      wv_title->enabled = True;
+#ifndef HAVE_MULTILINGUAL_MENU
+      if (STRING_MULTIBYTE (title))
+       title = ENCODE_SYSTEM (title);
+#endif
+      
+      wv_title->name = (char *) SDATA (title);
+      wv_title->enabled = TRUE;
+      wv_title->button_type = BUTTON_TYPE_NONE;
       wv_title->next = wv_sep1;
+      wv_title->help = Qnil;
       first_wv->contents = wv_title;
     }
 
@@ -2109,7 +2276,8 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
   menu = lw_create_widget ("popup", first_wv->name, menu_id, first_wv,
                           f->output_data.x->widget, 1, 0,
                           popup_selection_callback,
-                          popup_deactivate_callback);
+                          popup_deactivate_callback,
+                          menu_highlight_callback);
 
   /* Adjust coordinates to relative to the outer (window manager) window.  */
   {
@@ -2171,11 +2339,11 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
   menu_item_selection = 0;
 
   /* Display the menu.  */
-  lw_popup_menu (menu, &dummy);
+  lw_popup_menu (menu, (XEvent *) &dummy);
   popup_activated_flag = 1;
 
   /* Process events that apply to the menu.  */
-  popup_get_selection ((XEvent *) 0, FRAME_X_DISPLAY_INFO (f), menu_id);
+  popup_get_selection ((XEvent *) 0, FRAME_X_DISPLAY_INFO (f), menu_id, 0);
 
   /* fp turned off the following statement and wrote a comment
      that it is unnecessary--that the menu has already disappeared.
@@ -2189,7 +2357,7 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
     {
       Lisp_Object prefix, entry;
 
-      prefix = Qnil;
+      prefix = entry = Qnil;
       i = 0;
       while (i < menu_items_used)
        {
@@ -2257,6 +2425,22 @@ dialog_selection_callback (widget, id, client_data)
   popup_activated_flag = 0;
 }
 
+/* ARG is the LWLIB ID of the dialog box, represented
+   as a Lisp object as (HIGHPART . LOWPART).  */
+
+Lisp_Object
+xdialog_show_unwind (arg)
+     Lisp_Object arg;
+{
+  LWLIB_ID id = (XINT (XCAR (arg)) << 4 * sizeof (LWLIB_ID)
+                | XINT (XCDR (arg)));
+  BLOCK_INPUT;
+  lw_destroy_all_widgets (id);
+  UNBLOCK_INPUT;
+  popup_activated_flag = 0;
+  return Qnil;
+}
+
 static char * button_names [] = {
   "button1", "button2", "button3", "button4", "button5",
   "button6", "button7", "button8", "button9", "button10" };
@@ -2270,10 +2454,9 @@ xdialog_show (f, keymaps, title, error)
 {
   int i, nb_buttons=0;
   LWLIB_ID dialog_id;
-  Widget menu;
   char dialog_name[6];
 
-  widget_value *wv, *save_wv = 0, *first_wv = 0, *prev_wv = 0;
+  widget_value *wv, *first_wv = 0, *prev_wv = 0;
 
   /* Number of elements seen so far, before boundary.  */
   int left_count = 0;
@@ -2296,13 +2479,14 @@ xdialog_show (f, keymaps, title, error)
     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);  
+                  ? "" : (char *) SDATA (pane_name));  
     prev_wv = xmalloc_widget_value ();
     prev_wv->value = pane_string;
     if (keymaps && !NILP (prefix))
       prev_wv->name++;
     prev_wv->enabled = 1;
     prev_wv->name = "message";
+    prev_wv->help = Qnil;
     first_wv = prev_wv;
  
     /* Loop over all panes and items, filling in the tree.  */
@@ -2342,10 +2526,11 @@ xdialog_show (f, keymaps, title, error)
        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->key = (char *) SDATA (descrip);
+       wv->value = (char *) SDATA (item_name);
        wv->call_data = (void *) &XVECTOR (menu_items)->contents[i];
        wv->enabled = !NILP (enable);
+       wv->help = Qnil;
        prev_wv = wv;
 
        if (! boundary_seen)
@@ -2362,7 +2547,7 @@ xdialog_show (f, keymaps, title, error)
 
     wv = xmalloc_widget_value ();
     wv->name = dialog_name;
-
+    wv->help = Qnil;
     /* Dialog boxes use a really stupid name encoding
        which specifies how many buttons to use
        and how many buttons are on the right.
@@ -2380,9 +2565,9 @@ 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->output_data.x->widget, 1, 0,
-                          dialog_selection_callback, 0);
+  lw_create_widget (first_wv->name, "dialog", dialog_id, first_wv,
+                   f->output_data.x->widget, 1, 0,
+                   dialog_selection_callback, 0, 0);
   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);
@@ -2390,17 +2575,26 @@ xdialog_show (f, keymaps, title, error)
   /* No selection has been chosen yet.  */
   menu_item_selection = 0;
 
-  /* Display the menu.  */
+  /* Display the dialog box.  */
   lw_pop_up_all_widgets (dialog_id);
   popup_activated_flag = 1;
 
-  /* Process events that apply to the menu.  */
-  popup_get_selection ((XEvent *) 0, FRAME_X_DISPLAY_INFO (f), dialog_id);
+  /* Process events that apply to the dialog box.
+     Also handle timers.  */
+  {
+    int count = SPECPDL_INDEX ();
 
-  lw_destroy_all_widgets (dialog_id); 
+    /* xdialog_show_unwind is responsible for popping the dialog box down.  */
+    record_unwind_protect (xdialog_show_unwind,
+                          Fcons (make_number (dialog_id >> (4 * sizeof (LWLIB_ID))),
+                                 make_number (dialog_id & ~(-1 << (4 * sizeof (LWLIB_ID))))));
 
-  /* Find the selected item, and its pane, to return
-     the proper value.  */
+    popup_get_selection ((XEvent *) 0, FRAME_X_DISPLAY_INFO (f), dialog_id, 1);
+
+    unbind_to (count, Qnil);
+  }
+
+  /* Find the selected item and pane, and return the corresponding value.  */
   if (menu_item_selection != 0)
     {
       Lisp_Object prefix;
@@ -2417,6 +2611,12 @@ xdialog_show (f, keymaps, title, error)
                = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
              i += MENU_ITEMS_PANE_LENGTH;
            }
+         else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
+           {
+             /* This is the boundary between left-side elts and
+                right-side elts.  */
+             ++i;
+           }
          else
            {
              entry
@@ -2438,8 +2638,52 @@ xdialog_show (f, keymaps, title, error)
 
   return Qnil;
 }
+
 #else /* not USE_X_TOOLKIT */
 
+/* The frame of the last activated non-toolkit menu bar.
+   Used to generate menu help events.  */
+
+static struct frame *menu_help_frame;
+
+
+/* Show help HELP_STRING, or clear help if HELP_STRING is null.
+
+   PANE is the pane number, and ITEM is the menu item number in
+   the menu (currently not used).
+   
+   This cannot be done with generating a HELP_EVENT because
+   XMenuActivate contains a loop that doesn't let Emacs process
+   keyboard events.  */
+
+static void
+menu_help_callback (help_string, pane, item)
+     char *help_string;
+     int pane, item;
+{
+  extern Lisp_Object Qmenu_item;
+  Lisp_Object *first_item;
+  Lisp_Object pane_name;
+  Lisp_Object menu_object;
+  first_item = XVECTOR (menu_items)->contents;
+  if (EQ (first_item[0], Qt))
+    pane_name = first_item[MENU_ITEMS_PANE_NAME];
+  else if (EQ (first_item[0], Qquote))
+    /* This shouldn't happen, see xmenu_show.  */
+    pane_name = empty_string;
+  else
+    pane_name = first_item[MENU_ITEMS_ITEM_NAME];
+  /* (menu-item MENU-NAME PANE-NUMBER)  */
+  menu_object = Fcons (Qmenu_item,
+                      Fcons (pane_name,
+                             Fcons (make_number (pane), Qnil)));
+  show_help_echo (help_string ? build_string (help_string) : Qnil,
+                 Qnil, menu_object, make_number (item), 1);
+}
+  
+
 static Lisp_Object
 xmenu_show (f, x, y, for_click, keymaps, title, error)
      FRAME_PTR f;
@@ -2530,7 +2774,7 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
          pane_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_NAME];
          prefix = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
          pane_string = (NILP (pane_name)
-                        ? "" : (char *) XSTRING (pane_name)->data);
+                        ? "" : (char *) SDATA (pane_name));
          if (keymaps && !NILP (prefix))
            pane_string++;
 
@@ -2557,7 +2801,7 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
                  j++;
                  continue;
                }
-             width = XSTRING (item)->size;
+             width = SBYTES (item);
              if (width > maxwidth)
                maxwidth = width;
 
@@ -2571,43 +2815,47 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
       else
        {
          /* Create a new item within current pane.  */
-         Lisp_Object item_name, enable, descrip;
+         Lisp_Object item_name, enable, descrip, help;
          unsigned char *item_data;
+         char *help_string;
 
          item_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_NAME];
          enable = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_ENABLE];
          descrip
            = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_EQUIV_KEY];
+         help = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_HELP];
+         help_string = STRINGP (help) ? SDATA (help) : NULL;
+         
          if (!NILP (descrip))
            {
-             int gap = maxwidth - XSTRING (item_name)->size;
+             int gap = maxwidth - SBYTES (item_name);
 #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;
+             item_data = SDATA (item_name);
 #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++)
+                                           + SBYTES (descrip) + 1);
+             bcopy (SDATA (item_name), item_data,
+                    SBYTES (item_name));
+             for (j = SCHARS (item_name); j < maxwidth; j++)
                item_data[j] = ' ';
-             bcopy (XSTRING (descrip)->data, item_data + j,
-                    XSTRING (descrip)->size);
-             item_data[j + XSTRING (descrip)->size] = 0;
+             bcopy (SDATA (descrip), item_data + j,
+                    SBYTES (descrip));
+             item_data[j + SBYTES (descrip)] = 0;
 #endif
            }
          else
-           item_data = XSTRING (item_name)->data;
+           item_data = SDATA (item_name);
 
          if (XMenuAddSelection (FRAME_X_DISPLAY (f),
                                 menu, lpane, 0, item_data,
-                                !NILP (enable))
+                                !NILP (enable), help_string)
              == XM_FAILURE)
            {
              XMenuDestroy (FRAME_X_DISPLAY (f), menu);
@@ -2620,10 +2868,8 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
 
   /* All set and ready to fly.  */
   XMenuRecompute (FRAME_X_DISPLAY (f), menu);
-  dispwidth = DisplayWidth (FRAME_X_DISPLAY (f),
-                           XScreenNumberOfScreen (FRAME_X_SCREEN (f)));
-  dispheight = DisplayHeight (FRAME_X_DISPLAY (f),
-                             XScreenNumberOfScreen (FRAME_X_SCREEN (f)));
+  dispwidth = DisplayWidth (FRAME_X_DISPLAY (f), FRAME_X_SCREEN_NUMBER (f));
+  dispheight = DisplayHeight (FRAME_X_DISPLAY (f), FRAME_X_SCREEN_NUMBER (f));
   x = min (x, dispwidth);
   y = min (y, dispheight);
   x = max (x, 1);
@@ -2646,9 +2892,13 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
   XMenuSetAEQ (menu, TRUE);
   XMenuSetFreeze (menu, TRUE);
   pane = selidx = 0;
-  
+
+  /* Help display under X won't work because XMenuActivate contains
+     a loop that doesn't give Emacs a chance to process it.  */
+  menu_help_frame = f;
   status = XMenuActivate (FRAME_X_DISPLAY (f), menu, &pane, &selidx,
-                         x, y, ButtonReleaseMask, &datap);
+                         x, y, ButtonReleaseMask, &datap,
+                         menu_help_callback);
 
 
 #ifdef HAVE_X_WINDOWS
@@ -2723,20 +2973,19 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
 
 #endif /* HAVE_MENUS */
 \f
+void
 syms_of_xmenu ()
 {
   staticpro (&menu_items);
   menu_items = Qnil;
-
-  Qmenu_alias = intern ("menu-alias");
-  staticpro (&Qmenu_alias);
+  menu_items_inuse = Qnil;
 
   Qdebug_on_next_call = intern ("debug-on-next-call");
   staticpro (&Qdebug_on_next_call);
 
   DEFVAR_LISP ("menu-updating-frame", &Vmenu_updating_frame,
-    "Frame for which we are updating a menu.\n\
-The enable predicate for a menu command should check this variable.");
+              doc: /* Frame for which we are updating a menu.
+The enable predicate for a menu command should check this variable.  */);
   Vmenu_updating_frame = Qnil;
 
 #ifdef USE_X_TOOLKIT