diff src/xmenu.c @ 6058:662b9cd767fe

(Fx_popup_menu): Allow t as position, meaning use mouse pos. Allow nil as position, meaning just precompute equiv-key data. Mouse events have coords in pixel units. (menu_item_equiv_key): Cached equiv-key data is a sublist. Most of file rewritten. (menu_items, menu_items_*): New variables. (MENU_ITEMS_*): New macros. (init_menu_items, discard_menu_items, push_menu_pane, push_menu_item) (finish_menu_items): New functions. (menu_item_enabled_p): New function. (keymap_panes, single_keymap_panes): Major rewrite; most args changed. (list_of_panes, list_of_items): Major rewrite; most args changed. (Fx_popup_menu): Major rewrite. Now independent of display mechanism. No more conditionals here. (set_menu_items, free_menu_items): Functions deleted. (xmenu_show): Both versions rewritten to work from menu_items and to do all the conditionalized things that were in Fx_popup_menu. (unread_menu_bar_button, other_menu_bar_item_p): New functions. (check_mouse_other_menu_bar): New function.
author Richard M. Stallman <rms@gnu.org>
date Thu, 24 Feb 1994 08:07:16 +0000
parents 51d9a0c72a29
children e3d6c30a3906
line wrap: on
line diff
--- a/src/xmenu.c	Thu Feb 24 08:02:10 1994 +0000
+++ b/src/xmenu.c	Thu Feb 24 08:07:16 1994 +0000
@@ -27,6 +27,8 @@
 /* Modified by Fred Pierresteguy on December 93
    to make the popup menus and menubar use the Xt.  */
 
+/* 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.  */
@@ -70,12 +72,10 @@
 #define min(x,y) (((x) < (y)) ? (x) : (y))
 #define max(x,y) (((x) > (y)) ? (x) : (y))
 
-#define NUL 0
-
 #ifndef TRUE
 #define TRUE 1
 #define FALSE 0
-#endif /* TRUE */
+#endif /* no TRUE */
 
 #ifdef HAVE_X11
 extern Display *x_current_display;
@@ -83,31 +83,460 @@
 #define	ButtonReleaseMask ButtonReleased
 #endif /* not HAVE_X11 */
 
+/* We need a unique id for each popup menu and dialog box.  */
+static unsigned int popup_id_tick;
+
 extern Lisp_Object Qmenu_enable;
 extern Lisp_Object Qmenu_bar;
-Lisp_Object xmenu_show ();
-extern int x_error_handler ();
+
 #ifdef USE_X_TOOLKIT
-static widget_value *set_menu_items ();
+extern void process_expose_from_menu ();
+extern XtAppContext Xt_app_con;
+
 static int string_width ();
-static void free_menu_items ();
 #endif
 
-/* we need a unique id for each popup menu and dialog box */
-unsigned int popup_id_tick;
+static Lisp_Object xmenu_show ();
+static void keymap_panes ();
+static void single_keymap_panes ();
+static void list_of_panes ();
+static void list_of_items ();
+
+/* This holds a Lisp vector that holds the results of decoding
+   the keymaps or alist-of-alists that specify a menu.
+
+   It describes the panes and items within the panes.
+
+   Each pane is described by 3 elements in the vector:
+   t, the pane name, the pane's prefix key.
+   Then follow the pane's items, with 4 elements per item:
+   the item string, the enable flag, the item's value,
+   and the equivalent keyboard key's description string.
+
+   Using a Lisp vector to hold this information while we decode it
+   takes care of protecting all the data from GC.  */
+
+#define MENU_ITEMS_PANE_NAME 1
+#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_LENGTH 4
+
+static Lisp_Object menu_items;
+
+/* Number of slots currently allocated in menu_items.  */
+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.  */
+static int menu_items_n_panes;
+
+/* Initialize the menu_items structure if we haven't already done so.
+   Also mark it as currently empty.  */
+
+static void
+init_menu_items ()
+{
+  if (NILP (menu_items))
+    {
+      menu_items_allocated = 60;
+      menu_items = Fmake_vector (make_number (menu_items_allocated), Qnil);
+    }
+
+  menu_items_used = 0;
+  menu_items_n_panes = 0;
+}
+
+/* Call at the end of generating the data in menu_items.
+   This fills in the number of items in the last pane.  */
+
+static void
+finish_menu_items ()
+{
+}
+
+/* Call when finished using the data for the current menu
+   in menu_items.  */
+
+static void
+discard_menu_items ()
+{
+  /* Free the structure if it is especially large.
+     Otherwise, hold on to it, to save time.  */
+  if (menu_items_allocated > 200)
+    {
+      menu_items = Qnil;
+      menu_items_allocated = 0;
+    }
+}
+
+/* Start a new menu pane in menu_items..
+   NAME is the pane name.  PREFIX_VEC is a prefix key for this pane.  */
+
+static void
+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;
+
+      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++;
+  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;
+}
 
-/*************************************************************/
+/* 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.  EQUIV is the textual description
+   of the keyboard equivalent for this item (or nil if none).  */
+
+static void
+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));
+    }
+
+  XVECTOR (menu_items)->contents[menu_items_used++] = name;
+  XVECTOR (menu_items)->contents[menu_items_used++] = enable;
+  XVECTOR (menu_items)->contents[menu_items_used++] = key;
+  XVECTOR (menu_items)->contents[menu_items_used++] = equiv;
+}
+
+/* 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;
+
+  /* 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;
+    }
+
+  /* Is it still valid?  */
+  def1 = Qnil;
+  if (!NILP (savedkey))
+    def1 = Fkey_binding (savedkey, Qnil);
+  /* If not, update it.  */
+  if (! EQ (def1, def)
+      /* 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)))
+    {
+      changed = 1;
+      descrip = Qnil;
+      savedkey = Fwhere_is_internal (def, Qnil, Qt, Qnil);
+      if (VECTORP (savedkey)
+	  && EQ (XVECTOR (savedkey)->contents[0], Qmenu_bar))
+	savedkey = 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))
+    XCONS (item1)->cdr = Fcons (Fcons (savedkey, descrip), def);
+  else if (changed)
+    {
+      XCONS (cachelist)->car = savedkey;
+      XCONS (cachelist)->cdr = descrip;
+    }
+
+  *descrip_ptr = descrip;
+  return def;
+}
+
+/* This is used as the handler when calling internal_condition_case_1.  */
 
-#if 0
-/* Ignoring the args is easiest.  */
-xmenu_quit ()
+static Lisp_Object
+menu_item_enabled_p_1 (arg)
+     Lisp_Object arg;
+{
+  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.  */
+
+static Lisp_Object
+menu_item_enabled_p (def)
+     Lisp_Object def;
+{
+  Lisp_Object enabled, tem;
+
+  enabled = Qt;
+  if (XTYPE (def) == Lisp_Symbol)
+    {
+      /* 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;
+}
+
+/* Look through KEYMAPS, a vector of keymaps that is NMAPS long,
+   and generate menu panes for them in menu_items.  */
+
+static void
+keymap_panes (keymaps, nmaps)
+     Lisp_Object *keymaps;
+     int nmaps;
+{
+  int mapno;
+
+  init_menu_items ();
+
+  /* Loop over the given keymaps, making a pane for each map.
+     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);
+
+  finish_menu_items ();
+}
+
+/* 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.  */
+
+static void
+single_keymap_panes (keymap, pane_name, prefix)
+     Lisp_Object keymap;
+     Lisp_Object pane_name;
+     Lisp_Object prefix;
 {
-  error ("Unknown XMenu error");
+  Lisp_Object pending_maps;
+  Lisp_Object tail, item, item1, item_string, table;
+  struct gcpro gcpro1, gcpro2, gcpro3, gcpro4;
+
+  pending_maps = Qnil;
+
+  push_menu_pane (pane_name, prefix);
+
+  for (tail = keymap; XTYPE (tail) == Lisp_Cons; tail = XCONS (tail)->cdr)
+    {
+      /* Look at each key binding, and if it has a menu string,
+	 make a menu item from it.  */
+      item = XCONS (tail)->car;
+      if (XTYPE (item) == Lisp_Cons)
+	{
+	  item1 = XCONS (item)->cdr;
+	  if (XTYPE (item1) == Lisp_Cons)
+	    {
+	      item_string = XCONS (item1)->car;
+	      if (XTYPE (item_string) == Lisp_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;
+
+		  def = menu_item_equiv_key (item_string, item1, &descrip);
+
+		  /* GCPRO because we will call eval.
+		     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);
+		  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
+		    push_menu_item (item_string, enabled, XCONS (item)->car,
+				    descrip);
+		}
+	    }
+	}
+      else if (XTYPE (item) == Lisp_Vector)
+	{
+	  /* Loop over the char values represented in the vector.  */
+	  int len = XVECTOR (item)->size;
+	  int c;
+	  for (c = 0; c < len; c++)
+	    {
+	      Lisp_Object character;
+	      XFASTINT (character) = c;
+	      item1 = XVECTOR (item)->contents[c];
+	      if (XTYPE (item1) == Lisp_Cons)
+		{
+		  item_string = XCONS (item1)->car;
+		  if (XTYPE (item_string) == Lisp_String)
+		    {
+		      Lisp_Object def;
+
+		      /* These are the saved equivalent keyboard key sequence
+			 and its key-description.  */
+		      Lisp_Object descrip;
+		      Lisp_Object tem, enabled;
+
+		      def = menu_item_equiv_key (item_string, item1, &descrip);
+
+		      /* GCPRO because we will call eval.
+			 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);
+		      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
+			push_menu_item (item_string, enabled,
+					character, descrip);
+		    }
+		}
+	    }
+	}
+    }
+
+  /* Process now any submenus which want to be panes at this level.  */
+  while (!NILP (pending_maps))
+    {
+      Lisp_Object elt, eltcdr;
+      elt = Fcar (pending_maps);
+      eltcdr = XCONS (elt)->cdr;
+      single_keymap_panes (Fcar (elt),
+			   /* Fails to discard the @.  */
+			   XCONS (eltcdr)->car, XCONS (eltcdr)->cdr);
+      pending_maps = Fcdr (pending_maps);
+    }
 }
-#endif
+
+/* Push all the panes and items of a menu decsribed by the
+   alist-of-alists MENU.
+   This handles old-fashioned calls to x-popup-menu.  */
+
+static void
+list_of_panes (menu)
+     Lisp_Object menu;
+{
+  Lisp_Object tail;
+
+  init_menu_items ();
+
+  for (tail = menu; !NILP (tail); tail = Fcdr (tail))
+    {
+      Lisp_Object elt, pane_name, pane_data;
+      elt = Fcar (tail);
+      pane_name = Fcar (elt);
+      CHECK_STRING (pane_name, 0);
+      push_menu_pane (pane_name, Qnil);
+      pane_data = Fcdr (elt);
+      CHECK_CONS (pane_data, 0);
+      list_of_items (pane_data);
+    }
 
+  finish_menu_items ();
+}
+
+/* Push the items in a single pane defined by the alist PANE.  */
+
+static void
+list_of_items (pane)
+     Lisp_Object pane;
+{
+  Lisp_Object tail, item, item1;
+
+  for (tail = pane; !NILP (tail); tail = Fcdr (tail))
+    {
+      item = Fcar (tail);
+      if (STRINGP (item))
+	push_menu_item (item, Qnil, Qnil);
+      else
+	{
+	  CHECK_CONS (item, 0);
+	  item1 = Fcar (item);
+	  CHECK_STRING (item1, 1);
+	  push_menu_item (item1, Qt, Fcdr (item), Qnil);
+	}
+    }
+}
 
-DEFUN ("x-popup-menu",Fx_popup_menu, Sx_popup_menu, 1, 2, 0,
+DEFUN ("x-popup-menu", Fx_popup_menu, Sx_popup_menu, 1, 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\
@@ -115,6 +544,7 @@
 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\
@@ -131,166 +561,101 @@
 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.")
+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)
      Lisp_Object position, menu;
 {
   int number_of_panes, panes;
-  Lisp_Object XMenu_return, keymap, tem;
-  int XMenu_xpos, XMenu_ypos;
-  char **menus;
-  char ***names;
-  int **enables;
-  Lisp_Object **obj_list;
-  Lisp_Object *prefixes;
-  int *items;
-  char *title;
+  Lisp_Object keymap, tem;
+  int xpos, ypos;
+  Lisp_Object title;
   char *error_name;
-  Lisp_Object ltitle, selection;
-  int i, j, menubarp = 0;
+  Lisp_Object selection;
+  int i, j;
   FRAME_PTR f;
   Lisp_Object x, y, window;
-#ifdef USE_X_TOOLKIT
-  widget_value *val, *vw = 0;
-#endif /* USE_X_TOOLKIT */
+  int keymaps = 0;
+  int menubarp = 0;
+  struct gcpro gcpro1;
 
   check_x ();
-  /* Decode the first argument: find the window and the coordinates.  */
-  tem = Fcar (position);
-  if (XTYPE (tem) == Lisp_Cons)
-    {
-      window = Fcar (Fcdr (position));
-      x = Fcar (tem);
-      y = Fcar (Fcdr (tem));
-    }
-  else
-    {
-      tem = Fcar (Fcdr (position));  /* EVENT_START (position) */
-      window = Fcar (tem);	     /* POSN_WINDOW (tem) */
-      tem = Fcar (Fcdr (Fcdr (tem))); /* POSN_WINDOW_POSN (tem) */
-      x = Fcar (tem);
-      y = Fcdr (tem);
-    }
-  CHECK_NUMBER (x, 0);
-  CHECK_NUMBER (y, 0);
 
-  if (XTYPE (window) == Lisp_Frame)
-    {
-      f = XFRAME (window);
-      
-      XMenu_xpos = 0;
-      XMenu_ypos = 0;
-    }
-  else if (XTYPE (window) == Lisp_Window)
+  if (! NILP (position))
     {
-      CHECK_LIVE_WINDOW (window, 0);
-      f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
-      
-      XMenu_xpos = FONT_WIDTH (f->display.x->font) 
-	* XWINDOW (window)->left;
-      XMenu_ypos = FONT_HEIGHT (f->display.x->font) 
-	* 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);
+      /* Decode the first argument: find the window and the coordinates.  */
+      if (EQ (position, Qt))
+	{
+	  /* Use the mouse's current position.  */
+	  FRAME_PTR new_f;
+	  Lisp_Object bar_window;
+	  int part;
+	  unsigned long time;
 
-#ifdef USE_X_TOOLKIT
-  tem = Fcar (Fcdr (Fcar (Fcdr (position))));
-  if (XTYPE (Fcar (position)) != Lisp_Cons
-      && CONSP (tem)
-      && EQ (Fcar (tem), Qmenu_bar))
-    {
-      /* We are in the menubar */
-      XlwMenuWidget mw;
-      int w1 = 0, w2;
-    
-      mw = (XlwMenuWidget)f->display.x->menubar_widget;
-      menubarp = 1;
-      for (vw = mw->menu.old_stack [0]->contents; vw; vw = vw->next)
+	  (*mouse_position_hook) (&new_f, &bar_window, &part, &x, &y, &time);
+	  XSET (window, Lisp_Frame, new_f);
+	}
+      else
 	{
-	  w2 = w1;
-	  w1 += string_width (mw, vw->name) 
-	    + 2 * (mw->menu.horizontal_spacing + 
-		   mw->menu.shadow_thickness);
-	  if (XINT (x) < w1)
+	  tem = Fcar (position);
+	  if (XTYPE (tem) == Lisp_Cons)
+	    {
+	      window = Fcar (Fcdr (position));
+	      x = Fcar (tem);
+	      y = Fcar (Fcdr (tem));
+	    }
+	  else
 	    {
-	      XMenu_xpos = w2 + 4;
-	      XMenu_ypos = 0;
-	      break;
+	      tem = Fcar (Fcdr (position));  /* EVENT_START (position) */
+	      window = Fcar (tem);	     /* POSN_WINDOW (tem) */
+	      tem = Fcar (Fcdr (Fcdr (tem))); /* POSN_WINDOW_POSN (tem) */
+	      x = Fcar (tem);
+	      y = Fcdr (tem);
+
+	      /* 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))
+		menubarp = 1;
 	    }
 	}
-    }
-  else
-    {
-      XMenu_xpos += FONT_WIDTH (f->display.x->font) * XINT (x);
-      XMenu_ypos += FONT_HEIGHT (f->display.x->font) * XINT (y);
-    }
+
+      CHECK_NUMBER (x, 0);
+      CHECK_NUMBER (y, 0);
 
-  BLOCK_INPUT;
-  XMenu_xpos += (f->display.x->widget->core.x
-		 + f->display.x->widget->core.border_width);
-  XMenu_ypos += (f->display.x->widget->core.y 
-		 + f->display.x->widget->core.border_width
-		 + f->display.x->menubar_widget->core.height);
-  UNBLOCK_INPUT;
+      /* Decode where to put the menu.  */
+
+      if (XTYPE (window) == Lisp_Frame)
+	{
+	  f = XFRAME (window);
 
-  val = set_menu_items (menu, &prefixes, &panes, &names, 
-			&enables, &menus, &items, &number_of_panes, &obj_list, 
-			&title, &error_name);
-  selection = xmenu_show (f, val, XMenu_xpos, XMenu_ypos,
-			  menubarp, vw);
+	  xpos = 0;
+	  ypos = 0;
+	}
+      else if (XTYPE (window) == Lisp_Window)
+	{
+	  CHECK_LIVE_WINDOW (window, 0);
+	  f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
 
-  free_menu_items (names, enables, menus, items, number_of_panes, obj_list, 
-		   title, error_name);
+	  xpos = (FONT_WIDTH (f->display.x->font) * XWINDOW (window)->left);
+	  ypos = (FONT_HEIGHT (f->display.x->font) * 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);
 
-  if (selection != NUL)
-    {				/* selected something */
-      XMenu_return = selection;
-    }
-  else
-    {				/* nothing selected */
-      XMenu_return = Qnil;
+      xpos += XINT (x);
+      ypos += XINT (y);
     }
 
-  return XMenu_return;
-
-#else /* not USE_X_TOOLKIT */
-#ifdef HAVE_X11
-  {
-    Window child;
-    int win_x = 0, win_y = 0;
-
-    /* Find the position of the outside upper-left corner of
-       the inner window, with respect to the outer window.  */
-    if (f->display.x->parent_desc != ROOT_WINDOW)
-      {
-	BLOCK_INPUT;
-	XTranslateCoordinates (x_current_display,
-
-			       /* From-window, to-window.  */
-			       f->display.x->window_desc,
-			       f->display.x->parent_desc,
+  title = Qnil;
+  GCPRO1 (title);
 
-			       /* From-position, to-position.  */
-			       0, 0, &win_x, &win_y,
-
-			       /* Child of window.  */
-			       &child);
-	UNBLOCK_INPUT;
-	XMenu_xpos += win_x;
-	XMenu_ypos += win_y;
-      }
-  }
-#endif /* HAVE_X11 */
-
-  XMenu_xpos += FONT_WIDTH (f->display.x->font) * XINT (x);
-  XMenu_ypos += FONT_HEIGHT (f->display.x->font) * XINT (y);
-
-  XMenu_xpos += f->display.x->left_pos;
-  XMenu_ypos += f->display.x->top_pos;
-
+  /* Decode the menu items from what was specified.  */
 
   keymap = Fkeymapp (menu);
   tem = Qnil;
@@ -302,121 +667,82 @@
       Lisp_Object prompt;
       keymap = get_keymap (menu);
 
+      /* Extract the detailed info to make one pane.  */
+      keymap_panes (&menu, 1);
+
       /* Search for a string appearing directly as an element of the keymap.
 	 That string is the title of the menu.  */
       prompt = map_prompt (keymap);
-      if (!NILP (prompt))
-	title = (char *) XSTRING (prompt)->data;
 
-      /* Extract the detailed info to make one pane.  */
-      number_of_panes = keymap_panes (&obj_list, &menus, &names, &enables,
-				      &items, &prefixes, &menu, 1);
-      /* The menu title seems to be ignored,
-	 so put it in the pane title.  */
-      if (menus[0] == 0)
-	menus[0] = title;
+      /* Make that be the pane title of the first pane.  */
+      if (!NILP (prompt) && menu_items_n_panes >= 0)
+	XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME] = prompt;
+
+      keymaps = 1;
     }
   else if (!NILP (tem))
     {
       /* We were given a list of keymaps.  */
-      Lisp_Object prompt;
       int nmaps = XFASTINT (Flength (menu));
       Lisp_Object *maps
 	= (Lisp_Object *) alloca (nmaps * sizeof (Lisp_Object));
       int i;
-      title = 0;
+
+      title = Qnil;
 
       /* The first keymap that has a prompt string
 	 supplies the menu title.  */
       for (tem = menu, i = 0; XTYPE (tem) == Lisp_Cons; tem = Fcdr (tem))
 	{
+	  Lisp_Object prompt;
+
 	  maps[i++] = keymap = get_keymap (Fcar (tem));
 
 	  prompt = map_prompt (keymap);
-	  if (title == 0 && !NILP (prompt))
-	    title = (char *) XSTRING (prompt)->data;
+	  if (NILP (title) && !NILP (prompt))
+	    title = prompt;
 	}
 
       /* Extract the detailed info to make one pane.  */
-      number_of_panes = keymap_panes (&obj_list, &menus, &names, &enables,
-				      &items, &prefixes, maps, nmaps);
-      /* The menu title seems to be ignored,
-	 so put it in the pane title.  */
-      if (menus[0] == 0)
-	menus[0] = title;
+      keymap_panes (maps, nmaps);
+
+      /* Make the title be the pane title of the first pane.  */
+      if (!NILP (title) && menu_items_n_panes >= 0)
+	XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME] = title;
+
+      keymaps = 1;
     }
   else
     {
       /* We were given an old-fashioned menu.  */
-      ltitle = Fcar (menu);
-      CHECK_STRING (ltitle, 1);
-      title = (char *) XSTRING (ltitle)->data;
-      prefixes = 0;
-      number_of_panes = list_of_panes (&obj_list, &menus, &names, &enables,
-				       &items, Fcdr (menu));
-    }
-#ifdef XDEBUG
-  fprintf (stderr, "Panes = %d\n", number_of_panes);
-  for (i = 0; i < number_of_panes; i++)
-    {
-      fprintf (stderr, "Pane %d has lines %d title %s\n",
-	       i, items[i], menus[i]);
-      for (j = 0; j < items[i]; j++)
-	fprintf (stderr, "    Item %d %s\n", j, names[i][j]);
-    }
-#endif
+      title = Fcar (menu);
+      CHECK_STRING (title, 1);
 
-  BLOCK_INPUT;
-  {
-    Window root;
-    int root_x, root_y;
-    int dummy_int;
-    unsigned int dummy_uint;
-    Window dummy_window;
-
-    /* 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,
-		  &dummy_uint, &dummy_uint);
+      list_of_panes (Fcdr (menu));
 
-    /* Translate the menu co-ordinates within f to menu co-ordinates
-       on that root window.  */
-    if (! XTranslateCoordinates (x_current_display,
-				 FRAME_X_WINDOW (f), root,
-				 XMenu_xpos, XMenu_ypos, &root_x, &root_y,
-				 &dummy_window))
-      /* But XGetGeometry said root was the root window of f's screen!  */ 
-      abort ();
-    selection = xmenu_show (root, XMenu_xpos, XMenu_ypos, names, enables,
-			    menus, prefixes, items, number_of_panes, obj_list,
-			    title, &error_name);
-  }
-  UNBLOCK_INPUT;
-  /* fprintf (stderr, "selection = %x\n", selection);  */
-  if (selection != NUL)
-    {				/* selected something */
-      XMenu_return = selection;
+      keymaps = 0;
+    }
+  
+  if (NILP (position))
+    {
+      discard_menu_items ();
+      UNGCPRO;
+      return Qnil;
     }
-  else
-    {				/* nothing selected */
-      XMenu_return = Qnil;
-    }
-  /* now free up the strings */
-  for (i = 0; i < number_of_panes; i++)
-    {
-      xfree (names[i]);
-      xfree (enables[i]);
-      xfree (obj_list[i]);
-    }
-  xfree (menus);
-  xfree (obj_list);
-  xfree (names);
-  xfree (enables);
-  xfree (items);
-  /* free (title); */
+
+  /* Display them in a menu.  */
+  BLOCK_INPUT;
+
+  selection = xmenu_show (f, xpos, ypos, menubarp,
+			  keymaps, title, &error_name);
+  UNBLOCK_INPUT;
+
+  discard_menu_items ();
+
+  UNGCPRO;
+
   if (error_name) error (error_name);
-  return XMenu_return;
-#endif /* not USE_X_TOOLKIT */
+  return selection;
 }
 
 #ifdef USE_X_TOOLKIT
@@ -460,9 +786,10 @@
      char *name;
      int *string_w;
 {
-  *string_w += string_width (mw, name) 
-    + 2 * (mw->menu.horizontal_spacing + mw->menu.shadow_thickness);
-  return (XINT (event->x) < *string_w);
+  *string_w += (string_width (mw, name) 
+		+ 2 * (mw->menu.horizontal_spacing
+		       + mw->menu.shadow_thickness));
+  return XINT (event->x) < *string_w;
 }
 
 
@@ -495,185 +822,7 @@
   return Qnil;
 }
 
-static widget_value *
-set_menu_items (menu, prefixes, panes, names, enables, menus,
-		items, number_of_panes, obj_list, title, error_name)
-     Lisp_Object menu;
-     Lisp_Object **prefixes;
-     int *panes;
-     char ***names[];
-     int ***enables;
-     char ***menus;
-     int **items;
-     int *number_of_panes;
-     Lisp_Object ***obj_list;
-     char **title;
-     char **error_name;
-{
-  Lisp_Object keymap, tem;
-  Lisp_Object ltitle, selection;
-  int i, j;
-  widget_value *wv, *save_wv = 0, *first_wv = 0, *prev_wv = 0;
-  int last, selidx, lpane, status;
-  int lines, sofar;
-
-  keymap = Fkeymapp (menu);
-  tem = Qnil;
-
-  if (XTYPE (menu) == Lisp_Cons)
-    tem = Fkeymapp (Fcar (menu));
-  if (!NILP (keymap))
-    {
-      /* We were given a keymap.  Extract menu info from the keymap.  */
-      Lisp_Object prompt;
-      keymap = get_keymap (menu);
-
-      /* Search for a string appearing directly as an element of the keymap.
-	 That string is the title of the menu.  */
-      prompt = map_prompt (keymap);
-      if (!NILP (prompt))
-	*title = (char *) XSTRING (prompt)->data;
-
-      /* Extract the detailed info to make one pane.  */
-      *number_of_panes = keymap_panes (obj_list, menus, names, enables,
-				      items, prefixes, menu, 1);
-      /* The menu title seems to be ignored,
-	 so put it in the pane title.  */
-      if ((*menus)[0] == 0)
-	(*menus)[0] = *title;
-    }
-  else if (!NILP (tem))
-    {
-      /* We were given a list of keymaps.  */
-      Lisp_Object prompt;
-      int nmaps = XFASTINT (Flength (menu));
-      Lisp_Object *maps
-	= (Lisp_Object *) alloca (nmaps * sizeof (Lisp_Object));
-      int i;
-      *title = 0;
-
-      /* The first keymap that has a prompt string
-	 supplies the menu title.  */
-      for (tem = menu, i = 0; XTYPE (tem) == Lisp_Cons; tem = Fcdr (tem))
-	{
-	  maps[i++] = keymap = get_keymap (Fcar (tem));
-
-	  prompt = map_prompt (keymap);
-	  if (*title == 0 && !NILP (prompt))
-	    *title = (char *) XSTRING (prompt)->data;
-	}
-
-      /* Extract the detailed info to make one pane.  */
-      *number_of_panes = keymap_panes (obj_list, menus, names, enables,
-				      items, prefixes, maps, nmaps);
-      /* The menu title seems to be ignored,
-	 so put it in the pane title.  */
-      if ((*menus)[0] == 0)
-	(*menus)[0] = *title;
-    }
-  else
-    {
-      /* We were given an old-fashioned menu.  */
-      ltitle = Fcar (menu);
-      CHECK_STRING (ltitle, 1);
-      *title = (char *) XSTRING (ltitle)->data;
-      *prefixes = 0;
-      *number_of_panes = list_of_panes (obj_list, menus, names, enables,
-				       items, Fcdr (menu));
-    }
-
-  *error_name = 0;
-  if (*number_of_panes == 0)
-    return 0;
-
-  *error_name = (char *) 0;		/* Initialize error pointer to null */
-
-  wv = malloc_widget_value ();
-  wv->name = "menu";
-  wv->value = 0;
-  wv->enabled = 1;
-  first_wv = wv;
- 
-  for (*panes = 0, lines = 0; *panes < *number_of_panes;
-       lines += (*items)[*panes], (*panes)++)
-    ;
-  /* datap = (struct indices *) xmalloc (lines * sizeof (struct indices)); */
-  /* datap = (char *) xmalloc (lines * sizeof (char));
-    datap_save = datap;*/
-  
-  for (*panes = 0, sofar = 0; *panes < *number_of_panes;
-       sofar += (*items)[*panes], (*panes)++)
-    {
-      if (strcmp((*menus)[*panes], ""))
-	{
-	  wv = malloc_widget_value ();
-	  if (save_wv)
-	    save_wv->next = wv;
-	  else
-	    first_wv->contents = wv;
-	  wv->name = (*menus)[*panes];
-	  wv->value = 0;
-	  wv->enabled = 1;
-	}
-      prev_wv = 0;
-      save_wv = wv;
-
-      for (selidx = 0; selidx < (*items)[*panes]; selidx++)
-	{
-	  wv = malloc_widget_value ();
-	  if (prev_wv) 
-	    prev_wv->next = wv;
-	  else 
-	    save_wv->contents = wv;
-	  wv->name = (*names)[*panes][selidx];
-	  wv->value = 0;
-	  selection = (*obj_list)[*panes][selidx];
-	  if (*prefixes != 0)
-	    {
-	      selection = Fcons (selection, Qnil);
-	      if (!NILP ((*prefixes)[*panes]))
-		selection = Fcons ((*prefixes)[*panes], selection);
-	    }
-	  wv->call_data = LISP_TO_VOID(selection);
-	  wv->enabled = (*enables)[*panes][selidx];
-	  prev_wv = wv;
-	}
-    }
-
-  return (first_wv);
-}
-
-static void
-free_menu_items (names, enables, menus, items, number_of_panes, 
-		 obj_list, title, error_name)
-     char **names[];
-     int *enables[];
-     char **menus;
-     int *items;
-     int number_of_panes;
-     Lisp_Object **obj_list;
-     char *title;
-     char *error_name;
-{
-  int i;
-  /* now free up the strings */
-  for (i = 0; i < number_of_panes; i++)
-    {
-      xfree (names[i]);
-      xfree (enables[i]);
-      xfree (obj_list[i]);
-    }
-  xfree (menus);
-  xfree (obj_list);
-  xfree (names);
-  xfree (enables);
-  xfree (items);
-  /* free (title); */
-  if (error_name) error (error_name);
-
-}
-
-static Lisp_Object menu_item_selection;
+static Lisp_Object *menu_item_selection;
 
 static void
 popup_selection_callback (widget, id, client_data)
@@ -681,7 +830,7 @@
      LWLIB_ID id;
      XtPointer client_data;
 {
-  VOID_TO_LISP (menu_item_selection, client_data);
+  menu_item_selection = (Lisp_Object *) client_data;
 }
 
 static void
@@ -697,16 +846,14 @@
 
 /* This recursively calls free_widget_value() on the tree of widgets.
    It must free all data that was malloc'ed for these widget_values.
-   Currently, emacs only allocates new storage for the `key' slot.
-   All other slots are pointers into the data of Lisp_Strings, and
-   must be left alone.
- */
+   In Emacs, many slots are pointers into the data of Lisp_Strings, and
+   must be left alone.  */
+
 void
 free_menubar_widget_value_tree (wv)
      widget_value *wv;
 {
   if (! wv) return;
-  if (wv->key) xfree (wv->key);
 
   wv->name = wv->value = wv->key = (char *) 0xDEADBEEF;
 
@@ -843,64 +990,268 @@
       UNBLOCK_INPUT;
     }
 }
+
+/* Nonzero if position X, Y relative to inside of frame F
+   is in some other menu bar item.  */
+
+static int this_menu_bar_item_beg;
+static int this_menu_bar_item_end;
+
+static int
+other_menu_bar_item_p (f, x, y)
+     FRAME_PTR f;
+     int x, y;
+{
+  return (y >= 0
+	  && y < f->display.x->menubar_widget->core.height
+	  && x >= 0
+	  && x < f->display.x->menubar_widget->core.width
+	  && (x >= this_menu_bar_item_end
+	      || x < this_menu_bar_item_beg));
+}
+
+/* Unread a button-press event in the menu bar of frame F
+   at x position XPOS relative to the inside of the frame.  */
+
+static void
+unread_menu_bar_button (f, xpos)
+     FRAME_PTR f;
+     int xpos;
+{
+  XEvent event;
+
+  event.type = ButtonPress;
+  event.xbutton.display = x_current_display;
+  event.xbutton.serial = 0;
+  event.xbutton.send_event = 0;
+  event.xbutton.time = CurrentTime;
+  event.xbutton.button = Button1;
+  event.xbutton.window = XtWindow (f->display.x->menubar_widget);
+  event.xbutton.x = xpos;
+  XPutBackEvent (XDISPLAY &event);
+}
+
+/* If the mouse has moved to another menu bar item,
+   return 1 and unread a button press event for that item.
+   Otherwise return 0.  */
+
+static int
+check_mouse_other_menu_bar (f)
+     FRAME_PTR f;
+{
+  FRAME_PTR new_f;
+  Lisp_Object bar_window;
+  int part;
+  Lisp_Object x, y;
+  unsigned long time;
+
+  (*mouse_position_hook) (&new_f, &bar_window, &part, &x, &y, &time);
+
+  if (f == new_f && other_menu_bar_item_p (f, x, y))
+    {
+      unread_menu_bar_button (f, x);
+      return 1;
+    }
+
+  return 0;
+}
 #endif /* USE_X_TOOLKIT */
 
-struct indices {
-  int pane;
-  int line;
-};
+/* xmenu_show actually displays a menu using the panes and items in menu_items
+   and returns the value selected from it.
+   There are two versions of xmenu_show, one for Xt and one for Xlib.
+   Both assume input is blocked by the caller.  */
 
-extern void process_expose_from_menu ();
+/* F is the frame the menu is for.
+   X and Y are the frame-relative specified position,
+   relative to the inside upper left corner of the frame F.
+   MENUBARP is 1 if the click that asked for this menu came from the menu bar.
+   KEYMAPS is 1 if this menu was specified with keymaps;
+    in that case, we return a list containing the chosen item's value
+    and perhaps also the pane's prefix.
+   TITLE is the specified menu title.
+   ERROR is a place to store an error message string in case of failure.
+   (We return nil on failure, but the value doesn't actually matter.)  */
 
 #ifdef USE_X_TOOLKIT
-extern XtAppContext Xt_app_con;
 
-Lisp_Object
-xmenu_show (f, val, x, y, menubarp, vw)
+static Lisp_Object
+xmenu_show (f, x, y, menubarp, keymaps, title, error)
      FRAME_PTR f;
-     widget_value *val;
      int x;
      int y;
      int menubarp;
-     widget_value *vw;
+     int keymaps;
+     Lisp_Object title;
+     char **error;
 {
-  int menu_id, item_length;
-  Lisp_Object selection;
+  int i;
+  int menu_id;
   Widget menu;
-  XlwMenuWidget menuw = (XlwMenuWidget) f->display.x->menubar_widget;
+  XlwMenuWidget menubar = (XlwMenuWidget) f->display.x->menubar_widget;
 
-  /*
-   * Define and allocate a foreign event queue to hold events
-   * that don't belong to XMenu.  These events are later restored
-   * to the X event queue.
-   */
-  typedef struct _xmeventque 
+  /* 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 _xmeventque *next;
-    } XMEventQue;
-  
-  XMEventQue *feq = NULL;    		/* Foreign event queue. */
-  XMEventQue *feq_tmp;		/* Foreign event queue temporary. */
+      struct event_queue *next;
+    };
   
-  BLOCK_INPUT;
-  if (val == 0) return Qnil;
+  struct event_queue *queue = NULL;
+  struct event_queue *queue_tmp;
+
+  *error = NULL;
+
+  this_menu_bar_item_beg = -1;
+  this_menu_bar_item_end = -1;
+
+  /* Figure out which menu bar item, if any, this menu is for.  */
+  if (menubarp)
+    {
+      int xbeg;
+      int xend = 0;
+
+      for (menubar_item = menubar->menu.old_stack[0]->contents;
+	   menubar_item;
+	   menubar_item = menubar_item->next)
+	{
+	  xbeg = xend;
+	  xend += (string_width (menubar, menubar_item->name) 
+		   + 2 * (menubar->menu.horizontal_spacing
+			  + menubar->menu.shadow_thickness));
+	  if (x < xend)
+	    {
+	      x = xbeg + 4;
+	      y = 0;
+	      /* 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;
+	    }
+	}
+    }
+  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);
 
+  /* Create a tree of widget_value objects
+     representing the panes and their items.  */
+  wv = malloc_widget_value ();
+  wv->name = "menu";
+  wv->value = 0;
+  wv->enabled = 1;
+  first_wv = wv;
+ 
+  /* 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))
+	{
+	  /* 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_string = (NILP (pane_name)
+			 ? "" : (char *) XSTRING (pane_name)->data);
+	  /* If there is just one pane, put all its items directly
+	     under the top-level menu.  */
+	  if (menu_items_n_panes == 1)
+	    pane_string = "";
+
+	  /* 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, ""))
+	    {
+	      wv = malloc_widget_value ();
+	      if (save_wv)
+		save_wv->next = wv;
+	      else
+		first_wv->contents = wv;
+	      wv->name = pane_string;
+	      if (keymaps && !NILP (prefix))
+		wv->name++;
+	      wv->value = 0;
+	      wv->enabled = 1;
+	    }
+	  save_wv = wv;
+	  prev_wv = 0;
+	  i += MENU_ITEMS_PANE_LENGTH;
+	}
+      else
+	{
+	  /* 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];
+
+	  wv = malloc_widget_value ();
+	  if (prev_wv) 
+	    prev_wv->next = wv;
+	  else 
+	    save_wv->contents = wv;
+	  wv->name = XSTRING (item_name)->data;
+	  if (!NILP (descrip))
+	    wv->key = XSTRING (descrip)->data;
+	  wv->value = 0;
+	  wv->call_data = (void *) &XVECTOR (menu_items)->contents[i];
+	  wv->enabled = !NILP (enable);
+	  prev_wv = wv;
+
+	  i += MENU_ITEMS_ITEM_LENGTH;
+	}
+    }
+
+  /* Actually create the menu.  */
   menu_id = ++popup_id_tick;
-  menu = lw_create_widget ("popup", val->name, menu_id, val, 
+  menu = lw_create_widget ("popup", first_wv->name, menu_id, first_wv,
 			   f->display.x->widget, 1, 0,
 			   popup_selection_callback, popup_down_callback);
-  free_menubar_widget_value_tree (val);
+  /* 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;
 
-  /* reset the selection */
-  menu_item_selection = Qnil;
+  /* If the mouse moves out of the menu before we show the menu,
+     don't show it at all.  */
+  if (check_mouse_other_menu_bar (f))
+    {
+      lw_destroy_all_widgets (menu_id); 
+      return Qnil;
+    }
+
 
+  /* Highlight the menu bar item (if any) that led to this menu.  */
+  if (menubarp)
+    {
+      menubar_item->call_data = (XtPointer) 1;
+      dispatch_dummy_expose (f->display.x->menubar_widget, x, y);
+    }
+
+  /* Display the menu.  */
   {
     XButtonPressedEvent dummy;
     XlwMenuWidget mw;
     
-    mw = ((XlwMenuWidget)
-	  ((CompositeWidget)menu)->composite.children[0]);
+    mw = (XlwMenuWidget) ((CompositeWidget)menu)->composite.children[0];
 
     dummy.type = ButtonPress;
     dummy.serial = 0;
@@ -912,731 +1263,347 @@
     dummy.x_root = x;
     dummy.y_root = y;
 
-    if (menubarp)
-      {
-	vw->call_data = (XtPointer) 1;
-	dispatch_dummy_expose (f->display.x->menubar_widget, x, y);
-      }
-
-
-    /* We activate directly the lucid implementation */
+    /* We activate directly the lucid implementation.  */
     pop_up_menu (mw, &dummy);
   }
 
-  if (menubarp)
+  /* Check again whether the mouse has moved to another menu bar item.  */
+  if (check_mouse_other_menu_bar (f))
     {
-      item_length = (x + string_width (menuw, vw->name) 
-		     + (2 * (menuw->menu.horizontal_spacing 
-			     + menuw->menu.shadow_thickness))
-		     - 4);
+      /* The mouse moved into a different menu bar item. 
+	 We should bring up that item's menu instead.
+	 First pop down this menu.  */
+      XtUngrabPointer ((Widget)
+		       ((XlwMenuWidget)
+			((CompositeWidget)menu)->composite.children[0]),
+		       CurrentTime);
+      lw_destroy_all_widgets (menu_id); 
+      goto pop_down;
     }
 
-  /* Enters XEvent loop */
+  /* Process events that apply to the menu.  */
   while (1)
     {
+      XEvent event;
 
-      XEvent event;
       XtAppNextEvent (Xt_app_con, &event);
       if (event.type == ButtonRelease)
 	{
 	  XtDispatchEvent (&event);
 	  break;
 	}
-      else
-	if (event.type == Expose)
-	  process_expose_from_menu (event);
-      else 
-	if (event.type == MotionNotify 
-	    && menubarp
-	    && ((event.xmotion.y_root 
-		 >= (f->display.x->widget->core.y 
-		     + f->display.x->widget->core.border_width))
-		&& (event.xmotion.y_root
-		    < (f->display.x->widget->core.y
-		       + f->display.x->widget->core.border_width
-		       + f->display.x->menubar_widget->core.height)))
-	    && ((event.xmotion.x_root
-		 >= (f->display.x->widget->core.x
-		     + f->display.x->widget->core.border_width))
-		&& (event.xmotion.x_root
-		    < (f->display.x->widget->core.x
-		       + f->display.x->widget->core.border_width
-		       + f->display.x->widget->core.width)))
-	    && (event.xmotion.x_root >= item_length
-		|| event.xmotion.x_root < (x - 4)))
-	  {
-	    BLOCK_INPUT;
-	    XtUngrabPointer ((Widget)
-			     ((XlwMenuWidget)
-			      ((CompositeWidget)menu)->composite.children[0]),
-			     event.xbutton.time);
-	    lw_destroy_all_widgets (menu_id); 
-	    UNBLOCK_INPUT;
+      else if (event.type == Expose)
+	process_expose_from_menu (event);
+      else if (event.type == MotionNotify)
+	{
+	  int event_x = (event.xmotion.x_root
+			 - (f->display.x->widget->core.x
+			    + f->display.x->widget->core.border_width));
+	  int event_y = (event.xmotion.y_root
+			 - (f->display.x->widget->core.y
+			    + f->display.x->widget->core.border_width));
 
-	    event.type = ButtonPress;
-	        event.xbutton.time = CurrentTime;
-	        event.xbutton.button = Button1;
-	    event.xbutton.window = XtWindow (f->display.x->menubar_widget);
-	    event.xbutton.x = (event.xbutton.x_root 
-			       - (f->display.x->widget->core.x
-				  + f->display.x->widget->core.border_width));
-	    XPutBackEvent (XDISPLAY &event);
-	    break;
-	  }
+	  if (other_menu_bar_item_p (f, event_x, event_y))
+	    {
+	      /* The mouse moved into a different menu bar item. 
+		 We should bring up that item's menu instead.
+		 First pop down this menu.  */
+	      XtUngrabPointer ((Widget)
+			       ((XlwMenuWidget)
+				((CompositeWidget)menu)->composite.children[0]),
+			       event.xbutton.time);
+	      lw_destroy_all_widgets (menu_id); 
+
+	      /* Put back an event that will bring up the other item's menu.  */
+	      unread_menu_bar_button (f, event_x);
+	      /* Don't let us select anything in this case.  */
+	      menu_item_selection = 0;
+	      break;
+	    }
+	}
 
       XtDispatchEvent (&event);
-      feq_tmp = (XMEventQue *) malloc (sizeof (XMEventQue));
-
-      if (feq_tmp == NULL) 
-	return(Qnil);
+      queue_tmp = (struct event_queue *) malloc (sizeof (struct event_queue));
 
-      feq_tmp->event = event;
-      feq_tmp->next = feq;
-      feq = feq_tmp;
+      if (queue_tmp != NULL) 
+	{
+	  queue_tmp->event = event;
+	  queue_tmp->next = queue;
+	  queue = queue_tmp;
+	}
     }
-      
+
+ pop_down:
+  /* Unhighlight the menu bar item (if any) that led to this menu.  */
   if (menubarp)
     {
-      vw->call_data = (XtPointer) 0;
+      menubar_item->call_data = (XtPointer) 0;
       dispatch_dummy_expose (f->display.x->menubar_widget, x, y);
     }
 
-  /* Return any foreign events that were queued to the X event queue.  */
-  while (feq != NULL) 
+  /* Make sure the menu disappears.  */
+  lw_destroy_all_widgets (menu_id); 
+
+  /* Unread any events that we got but did not handle.  */
+  while (queue != NULL) 
     {
-      feq_tmp = feq;
-      XPutBackEvent (XDISPLAY &feq_tmp->event);
-      feq = feq_tmp->next;
-      free ((char *)feq_tmp);
+      queue_tmp = queue;
+      XPutBackEvent (XDISPLAY &queue_tmp->event);
+      queue = queue_tmp->next;
+      free ((char *)queue_tmp);
     }
 
-  UNBLOCK_INPUT;
+  /* 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;
 
-  return menu_item_selection;
+	  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 */
-xmenu_show (parent, startx, starty, line_list, enable_list, pane_list,
-	    prefixes, line_cnt, pane_cnt, item_list, title, error)
-     Window parent;		
-     int startx, starty;	/* upper left corner position BROKEN */
-     char **line_list[];   	/* list of strings for items */
-     int *enable_list[];   	/* enable flags of lines */
-     char *pane_list[];		/* list of pane titles */
-     Lisp_Object *prefixes;	/* Prefix key for each pane */
-     char *title;
-     int pane_cnt;		/* total number of panes */
-     Lisp_Object *item_list[];	/* All items */
-     int line_cnt[];		/* Lines in each pane */
-     char **error;		/* Error returned */
+
+static Lisp_Object
+xmenu_show (f, x, y, menubarp, keymaps, title, error)
+     FRAME_PTR f;
+     int x, y;
+     int keymaps;
+     int menubarp;
+     Lisp_Object title;
+     char **error;
 {
-  XMenu *GXMenu;
-  int last, panes, selidx, lpane, status;
-  int lines, sofar;
-  Lisp_Object entry;
-  /* struct indices *datap, *datap_save; */
+  Window root;		
+  XMenu *menu;
+  int pane, selidx, lpane, status;
+  Lisp_Object entry, pane_prefix;
   char *datap;
   int ulx, uly, width, height;
   int dispwidth, dispheight;
+  int i;
+  int dummy_int;
+  unsigned int dummy_uint;
 
   *error = 0;
-  if (pane_cnt == 0)
-    return 0;
+  if (menu_items_n_panes == 0)
+    return Qnil;
 
-  BLOCK_INPUT;
-  *error = (char *) 0;		/* Initialize error pointer to null */
+  /* 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,
+		&dummy_uint, &dummy_uint);
 
-  GXMenu = XMenuCreate (XDISPLAY parent, "emacs");
-  if (GXMenu == NUL)
+  /* Make the menu on that window.  */
+  menu = XMenuCreate (XDISPLAY root, "emacs");
+  if (menu == NULL)
     {
       *error = "Can't create menu";
-      UNBLOCK_INPUT;
-      return (0);
+      return Qnil;
     }
+
+  /* Adjust coordinates to relative to the outer (window manager) window.  */
+#ifdef HAVE_X11
+  {
+    Window child;
+    int win_x = 0, win_y = 0;
+
+    /* Find the position of the outside upper-left corner of
+       the inner window, with respect to the outer window.  */
+    if (f->display.x->parent_desc != ROOT_WINDOW)
+      {
+	BLOCK_INPUT;
+	XTranslateCoordinates (x_current_display,
+
+			       /* From-window, to-window.  */
+			       f->display.x->window_desc,
+			       f->display.x->parent_desc,
+
+			       /* From-position, to-position.  */
+			       0, 0, &win_x, &win_y,
+
+			       /* Child of window.  */
+			       &child);
+	UNBLOCK_INPUT;
+	x += win_x;
+	y += win_y;
+      }
+  }
+#endif /* HAVE_X11 */
+
+  /* Adjust coordinates to be root-window-relative.  */
+  x += f->display.x->left_pos;
+  y += f->display.x->top_pos;
  
-  for (panes = 0, lines = 0; panes < pane_cnt;
-       lines += line_cnt[panes], panes++)
-    ;
-  /* datap = (struct indices *) xmalloc (lines * sizeof (struct indices)); */
-  /* datap = (char *) xmalloc (lines * sizeof (char));
-    datap_save = datap;*/
-  
-  for (panes = 0, sofar = 0; panes < pane_cnt;
-       sofar += line_cnt[panes], panes++)
+  /* Create all the necessary panes and their items.  */
+  i = 0;
+  while (i < menu_items_used)
     {
-      /* create all the necessary panes */
-      lpane = XMenuAddPane (XDISPLAY GXMenu, pane_list[panes], TRUE);
-      if (lpane == XM_FAILURE)
+      if (EQ (XVECTOR (menu_items)->contents[i], Qt))
 	{
-	  XMenuDestroy (XDISPLAY GXMenu);
-	  *error = "Can't create pane";
-	  UNBLOCK_INPUT;
-	  return (0);
+	  /* 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_string = (NILP (pane_name)
+			 ? "" : (char *) XSTRING (pane_name)->data);
+	  if (keymaps && !NILP (prefix))
+	    pane_string++;
+
+	  lpane = XMenuAddPane (XDISPLAY menu, pane_string, TRUE);
+	  if (lpane == XM_FAILURE)
+	    {
+	      XMenuDestroy (XDISPLAY menu);
+	      *error = "Can't create pane";
+	      return Qnil;
+	    }
+	  i += MENU_ITEMS_PANE_LENGTH;
 	}
-
-      for (selidx = 0; selidx < line_cnt[panes]; selidx++)
+      else
 	{
-	  /* add the selection stuff to the menus */
-	  /* datap[selidx+sofar].pane = panes;
-	     datap[selidx+sofar].line = selidx; */
-	  if (XMenuAddSelection (XDISPLAY GXMenu, lpane, 0,
-				 line_list[panes][selidx],
-				 enable_list[panes][selidx])
+	  /* 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 (descrip))
+	    item_name = concat2 (item_name, descrip);
+
+	  if (XMenuAddSelection (XDISPLAY menu, lpane, 0,
+				 XSTRING (item_name)->data,
+				 !NILP (enable))
 	      == XM_FAILURE)
 	    {
-	      XMenuDestroy (XDISPLAY GXMenu);
-	      /* free (datap); */
+	      XMenuDestroy (XDISPLAY menu);
 	      *error = "Can't add selection to menu";
-	      /* error ("Can't add selection to menu"); */
-	      UNBLOCK_INPUT;
-	      return (0);
+	      return Qnil;
 	    }
+	  i += MENU_ITEMS_ITEM_LENGTH;
 	}
     }
-  /* all set and ready to fly */
-  XMenuRecompute (XDISPLAY GXMenu);
+  
+  /* All set and ready to fly.  */
+  XMenuRecompute (XDISPLAY menu);
   dispwidth = DisplayWidth (x_current_display, XDefaultScreen (x_current_display));
   dispheight = DisplayHeight (x_current_display, XDefaultScreen (x_current_display));
-  startx = min (startx, dispwidth);
-  starty = min (starty, dispheight);
-  startx = max (startx, 1);
-  starty = max (starty, 1);
-  XMenuLocate (XDISPLAY GXMenu, 0, 0, startx, starty,
+  x = min (x, dispwidth);
+  y = min (y, dispheight);
+  x = max (x, 1);
+  y = max (y, 1);
+  XMenuLocate (XDISPLAY menu, 0, 0, x, y,
 	       &ulx, &uly, &width, &height);
   if (ulx+width > dispwidth)
     {
-      startx -= (ulx + width) - dispwidth;
+      x -= (ulx + width) - dispwidth;
       ulx = dispwidth - width;
     }
   if (uly+height > dispheight)
     {
-      starty -= (uly + height) - dispheight;
+      y -= (uly + height) - dispheight;
       uly = dispheight - height;
     }
-  if (ulx < 0) startx -= ulx;
-  if (uly < 0) starty -= uly;
+  if (ulx < 0) x -= ulx;
+  if (uly < 0) y -= uly;
     
-  XMenuSetFreeze (GXMenu, TRUE);
-  panes = selidx = 0;
+  XMenuSetFreeze (menu, TRUE);
+  pane = selidx = 0;
   
-  status = XMenuActivate (XDISPLAY GXMenu, &panes, &selidx,
-			  startx, starty, ButtonReleaseMask, &datap);
+  status = XMenuActivate (XDISPLAY menu, &pane, &selidx,
+			  x, y, ButtonReleaseMask, &datap);
   switch (status)
     {
     case XM_SUCCESS:
 #ifdef XDEBUG
       fprintf (stderr, "pane= %d line = %d\n", panes, selidx);
 #endif
-      entry = item_list[panes][selidx];
-      if (prefixes != 0)
+
+      /* Find the item number SELIDX in pane number PANE.  */
+      i = 0;
+      while (i < menu_items_used)
 	{
-	  entry = Fcons (entry, Qnil);
-	  if (!NILP (prefixes[panes]))
-	    entry = Fcons (prefixes[panes], entry);
+	  if (EQ (XVECTOR (menu_items)->contents[i], Qt))
+	    {
+	      if (pane == 0)
+		pane_prefix
+		  = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
+	      pane--;
+	      i += MENU_ITEMS_PANE_LENGTH;
+	    }
+	  else
+	    {
+	      if (pane == -1)
+		{
+		  if (selidx == 0)
+		    {
+		      entry
+			= XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_VALUE];
+		      if (keymaps != 0)
+			{
+			  entry = Fcons (entry, Qnil);
+			  if (!NILP (pane_prefix))
+			    entry = Fcons (pane_prefix, entry);
+			}
+		      break;
+		    }
+		  selidx--;
+		}
+	      i += MENU_ITEMS_ITEM_LENGTH;
+	    }
 	}
       break;
+
     case XM_FAILURE:
-      /* free (datap_save); */
-      XMenuDestroy (XDISPLAY GXMenu);
+      XMenuDestroy (XDISPLAY menu);
       *error = "Can't activate menu";
-      /* error ("Can't activate menu"); */
     case XM_IA_SELECT:
     case XM_NO_SELECT:
       entry = Qnil;
       break;
     }
-  XMenuDestroy (XDISPLAY GXMenu);
-  UNBLOCK_INPUT;
-  /* free (datap_save);*/
-  return (entry);
+  XMenuDestroy (XDISPLAY menu);
+  return entry;
 }
 #endif /* not USE_X_TOOLKIT */
-
+
 syms_of_xmenu ()
 {
+  staticpro (&menu_items);
+  menu_items = Qnil;
+
   popup_id_tick = (1<<16);	
   defsubr (&Sx_popup_menu);
 }
-
-/* Figure out the current keyboard equivalent of a menu item ITEM1.
-   Store the equivalent key sequence in *SAVEDKEY_PTR
-   and the textual description (to use in the menu display) in *DESCRIP_PTR.
-   Also cache them in the item itself.
-   Return the real definition to execute.  */
-
-static Lisp_Object
-menu_item_equiv_key (item1, savedkey_ptr, descrip_ptr)
-     Lisp_Object item1;
-     Lisp_Object *savedkey_ptr, *descrip_ptr;
-{
-  /* This is what is left after the menu item name.  */
-  Lisp_Object overdef;
-  /* 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 savedkey, descrip;
-  Lisp_Object def1;
-  int changed = 0;
-
-  overdef = def = Fcdr (item1);
-
-  /* Get out the saved equivalent-keyboard-key info.  */
-  savedkey = descrip = Qnil;
-  if (CONSP (overdef) && VECTORP (XCONS (overdef)->car))
-    {
-      savedkey = XCONS (overdef)->car;
-      def = XCONS (def)->cdr;
-      if (CONSP (def) && STRINGP (XCONS (def)->car))
-	{
-	  descrip = XCONS (def)->car;
-	  def = XCONS (def)->cdr;
-	}
-    }
-
-  /* Is it still valid?  */
-  def1 = Qnil;
-  if (!NILP (savedkey))
-    def1 = Fkey_binding (savedkey, Qnil);
-  /* If not, update it.  */
-  if (! EQ (def1, def))
-    {
-      changed = 1;
-      descrip = Qnil;
-      savedkey = Fwhere_is_internal (def, Qnil, Qt, Qnil);
-      if (VECTORP (savedkey)
-	  && EQ (XVECTOR (savedkey)->contents[0], Qmenu_bar))
-	savedkey = Qnil;
-      if (!NILP (savedkey))
-	{
-	  descrip = Fkey_description (savedkey);
-	  descrip = concat2 (make_string ("  (", 3), descrip);
-	  descrip = concat2 (descrip, make_string (")", 1));
-	}
-    }
-
-  /* Store back the recorded keyboard key sequence
-     if we changed it.  */
-  if (!NILP (savedkey)
-      && CONSP (overdef) && VECTORP (XCONS (overdef)->car))
-    {
-      if (changed)
-	{
-	  XCONS (overdef)->car = savedkey;
-	  def1 = XCONS (overdef)->cdr;
-	  if (CONSP (def1) && STRINGP (XCONS (def1)->car))
-	    XCONS (def1)->car = descrip;
-	}
-    }
-  /* If we had none but need one now, add it.  */
-  else if (!NILP (savedkey))
-    XCONS (item1)->cdr
-      = overdef = Fcons (savedkey, Fcons (descrip, def));
-  /* If we had one but no longer should have one, delete it.  */
-  else if (CONSP (overdef) && VECTORP (XCONS (overdef)->car))
-    {
-      XCONS (item1)->cdr = overdef = XCONS (overdef)->cdr;
-      if (CONSP (overdef) && STRINGP (XCONS (overdef)->car))
-	XCONS (item1)->cdr = overdef = XCONS (overdef)->cdr;
-    }
-
-  *savedkey_ptr = savedkey;
-  *descrip_ptr = descrip;
-  return def;
-}
-
-/* Construct the vectors that describe a menu
-   and store them in *VECTOR, *PANES, *NAMES, *ENABLES and *ITEMS.
-   Each of those four values is a vector indexed by pane number.
-   Return the number of panes.
-
-   KEYMAPS is a vector of keymaps.  NMAPS gives the length of KEYMAPS.  */
-
-int
-keymap_panes (vector, panes, names, enables, items, prefixes, keymaps, nmaps)
-     Lisp_Object ***vector;	/* RETURN all menu objects */
-     char ***panes;		/* RETURN pane names */
-     char ****names;		/* RETURN all line names */
-     int ***enables;		/* RETURN enable-flags of lines */
-     int **items;		/* RETURN number of items per pane */
-     Lisp_Object **prefixes;	/* RETURN vector of prefix keys, per pane */
-     Lisp_Object *keymaps;
-     int nmaps;
-{
-  /* Number of panes we have made.  */
-  int p = 0;
-  /* Number of panes we have space for.  */
-  int npanes_allocated = nmaps;
-  int mapno;
-
-  if (npanes_allocated < 4)
-    npanes_allocated = 4;
-
-  /* Make space for an estimated number of panes.  */
-  *vector = (Lisp_Object **) xmalloc (npanes_allocated * sizeof (Lisp_Object *));
-  *panes = (char **) xmalloc (npanes_allocated * sizeof (char *));
-  *items = (int *) xmalloc (npanes_allocated * sizeof (int));
-  *names = (char ***) xmalloc (npanes_allocated * sizeof (char **));
-  *enables = (int **) xmalloc (npanes_allocated * sizeof (int *));
-  *prefixes = (Lisp_Object *) xmalloc (npanes_allocated * sizeof (Lisp_Object));
-
-  /* Loop over the given keymaps, making a pane for each map.
-     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], panes, vector, names, enables, items,
-			 prefixes, &p, &npanes_allocated, "");
-
-  /* Return the number of panes.  */
-  return p;
-}
-
-/* This is used as the handler when calling internal_condition_case_1.  */
-
-static Lisp_Object
-single_keymap_panes_1 (arg)
-     Lisp_Object arg;
-{
-  return Qnil;
-}
-
-/* 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.  */
-
-single_keymap_panes (keymap, panes, vector, names, enables, items, prefixes,
-		     p_ptr, npanes_allocated_ptr, pane_name)
-     Lisp_Object keymap;
-     Lisp_Object ***vector;	/* RETURN all menu objects */
-     char ***panes;		/* RETURN pane names */
-     char ****names;		/* RETURN all line names */
-     int ***enables;		/* RETURN enable flags of lines */
-     int **items;		/* RETURN number of items per pane */
-     Lisp_Object **prefixes;	/* RETURN vector of prefix keys, per pane */
-     int *p_ptr;
-     int *npanes_allocated_ptr;
-     char *pane_name;
-{
-  int i;
-  Lisp_Object pending_maps;
-  Lisp_Object tail, item, item1, item_string, table;
-  struct gcpro gcpro1, gcpro2, gcpro3, gcpro4;
-
-  pending_maps = Qnil;
-
-  /* Make sure we have room for another pane.  */
-  if (*p_ptr == *npanes_allocated_ptr)
-    {
-      *npanes_allocated_ptr *= 2;
-
-      *vector
-	= (Lisp_Object **) xrealloc (*vector,
-				     *npanes_allocated_ptr * sizeof (Lisp_Object *));
-      *panes
-	= (char **) xrealloc (*panes,
-			      *npanes_allocated_ptr * sizeof (char *));
-      *items
-	= (int *) xrealloc (*items,
-			    *npanes_allocated_ptr * sizeof (int));
-      *prefixes
-	= (Lisp_Object *) xrealloc (*prefixes,
-				    (*npanes_allocated_ptr
-				     * sizeof (Lisp_Object)));
-      *names
-	= (char ***) xrealloc (*names,
-			       *npanes_allocated_ptr * sizeof (char **));
-      *enables
-	= (int **) xrealloc (*enables,
-			     *npanes_allocated_ptr * sizeof (int *));
-    }
-
-  /* When a menu comes from keymaps, don't give names to the panes.  */
-  (*panes)[*p_ptr] = pane_name;
-
-  /* Normally put nil as pane's prefix key.
-     Caller will override this if appropriate.  */
-  (*prefixes)[*p_ptr] = Qnil;
-
-  /* Get the length of the list level of the keymap.  */
-  i = XFASTINT (Flength (keymap));
-
-  /* Add in lengths of any arrays.  */
-  for (tail = keymap; XTYPE (tail) == Lisp_Cons; tail = XCONS (tail)->cdr)
-    if (XTYPE (XCONS (tail)->car) == Lisp_Vector)
-      i += XVECTOR (XCONS (tail)->car)->size;
-
-  /* Create vectors for the names and values of the items in the pane.
-     I is an upper bound for the number of items.  */
-  (*vector)[*p_ptr] = (Lisp_Object *) xmalloc (i * sizeof (Lisp_Object));
-  (*names)[*p_ptr] = (char **) xmalloc (i * sizeof (char *));
-  (*enables)[*p_ptr] = (int *) xmalloc (i * sizeof (int));
-
-  /* I is now the index of the next unused slots.  */
-  i = 0;
-  for (tail = keymap; XTYPE (tail) == Lisp_Cons; tail = XCONS (tail)->cdr)
-    {
-      /* Look at each key binding, and if it has a menu string,
-	 make a menu item from it.  */
-      item = XCONS (tail)->car;
-      if (XTYPE (item) == Lisp_Cons)
-	{
-	  item1 = XCONS (item)->cdr;
-	  if (XTYPE (item1) == Lisp_Cons)
-	    {
-	      item_string = XCONS (item1)->car;
-	      if (XTYPE (item_string) == Lisp_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 savedkey, descrip;
-		  Lisp_Object tem, enabled;
-
-		  /* 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 = menu_item_equiv_key (item1, &savedkey, &descrip);
-
-		  enabled = Qt;
-		  if (XTYPE (def) == Lisp_Symbol)
-		    {
-		      /* No property, or nil, means enable.
-			 Otherwise, enable if value is not nil.  */
-		      tem = Fget (def, Qmenu_enable);
-		      /* GCPRO because we will call eval.
-			 Protecting KEYMAP preserves everything we use;
-			 aside from that, must protect whatever might be
-			 a string.  */
-		      GCPRO3 (keymap, def, descrip, item_string);
-		      if (!NILP (tem))
-			/* (condition-case nil (eval tem)
-			   (error nil))  */
-			enabled = internal_condition_case_1 (Feval, tem,
-							     Qerror,
-							     single_keymap_panes_1);
-		      UNGCPRO;
-		    }
-		  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 concat;
-		      if (!NILP (descrip))
-			concat = concat2 (item_string, descrip);
-		      else
-			concat = item_string;
-		      (*names)[*p_ptr][i] = (char *) XSTRING (concat)->data;
-		      /* The menu item "value" is the key bound here.  */
-		      (*vector)[*p_ptr][i] = XCONS (item)->car;
-		      (*enables)[*p_ptr][i]
-			= (NILP (def) ? -1 : !NILP (enabled) ? 1 : 0);
-		      i++;
-		    }
-		}
-	    }
-	}
-      else if (XTYPE (item) == Lisp_Vector)
-	{
-	  /* Loop over the char values represented in the vector.  */
-	  int len = XVECTOR (item)->size;
-	  int c;
-	  for (c = 0; c < len; c++)
-	    {
-	      Lisp_Object character;
-	      XFASTINT (character) = c;
-	      item1 = XVECTOR (item)->contents[c];
-	      if (XTYPE (item1) == Lisp_Cons)
-		{
-		  item_string = XCONS (item1)->car;
-		  if (XTYPE (item_string) == Lisp_String)
-		    {
-		      Lisp_Object def;
-
-		      /* These are the saved equivalent keyboard key sequence
-			 and its key-description.  */
-		      Lisp_Object savedkey, descrip;
-		      Lisp_Object tem, enabled;
-
-		      /* 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 = menu_item_equiv_key (item1, &savedkey, &descrip);
-
-		      enabled = Qt;
-		      if (XTYPE (def) == Lisp_Symbol)
-			{
-			  tem = Fget (def, Qmenu_enable);
-			  /* GCPRO because we will call eval.
-			     Protecting KEYMAP preserves everything we use;
-			     aside from that, must protect whatever might be
-			     a string.  */
-			  GCPRO3 (keymap, def, descrip, item_string);
-			  /* No property, or nil, means enable.
-			     Otherwise, enable if value is not nil.  */
-			  if (!NILP (tem))
-			    /* (condition-case nil (eval tem)
-			       (error nil))  */
-			    enabled = internal_condition_case_1 (Feval, tem,
-								 Qerror,
-								 single_keymap_panes_1);
-			  UNGCPRO;
-			}
-
-		      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 concat;
-			  if (!NILP (descrip))
-			    concat = concat2 (item_string, descrip);
-			  else
-			    concat = item_string;
-			  (*names)[*p_ptr][i]
-			    = (char *) XSTRING (concat)->data;
-			  /* The menu item "value" is the key bound here.  */
-			  (*vector)[*p_ptr][i] = character;
-			  (*enables)[*p_ptr][i]
-			    = (NILP (def) ? -1 : !NILP (enabled) ? 1 : 0);
-			  i++;
-			}
-		    }
-		}
-	    }
-	}
-    }
-  /* Record the number of items in the pane.  */
-  (*items)[*p_ptr] = i;
-
-  /* If we just made an empty pane, get rid of it.  */
-  if (i == 0)
-    {
-      xfree ((*vector)[*p_ptr]);
-      xfree ((*names)[*p_ptr]);
-      xfree ((*enables)[*p_ptr]);
-    }
-  /* Otherwise, advance past it.  */
-  else
-    (*p_ptr)++;
-
-  /* Process now any submenus which want to be panes at this level.  */
-  while (!NILP (pending_maps))
-    {
-      Lisp_Object elt, eltcdr;
-      int panenum = *p_ptr;
-      elt = Fcar (pending_maps);
-      eltcdr = XCONS (elt)->cdr;
-      single_keymap_panes (Fcar (elt), panes, vector, names, enables, items,
-			   prefixes, p_ptr, npanes_allocated_ptr,
-			   /* Add 1 to discard the @.  */
-			   (char *) XSTRING (XCONS (eltcdr)->car)->data + 1);
-      (*prefixes)[panenum] = XCONS (eltcdr)->cdr;
-      pending_maps = Fcdr (pending_maps);
-    }
-}
-
-/* Construct the vectors that describe a menu
-   and store them in *VECTOR, *PANES, *NAMES, *ENABLES and *ITEMS.
-   Each of those four values is a vector indexed by pane number.
-   Return the number of panes.
-
-   MENU is the argument that was given to Fx_popup_menu.  */
-
-int
-list_of_panes (vector, panes, names, enables, items, menu)
-     Lisp_Object ***vector;	/* RETURN all menu objects */
-     char ***panes;		/* RETURN pane names */
-     char ****names;		/* RETURN all line names */
-     int ***enables;		/* RETURN enable flags of lines */
-     int **items;		/* RETURN number of items per pane */
-     Lisp_Object menu;
-{
-  Lisp_Object tail, item, item1;
-  int i;
-  
-  if (XTYPE (menu) != Lisp_Cons) menu = wrong_type_argument (Qlistp, menu);
-
-  i = XFASTINT (Flength (menu));
-
-  *vector = (Lisp_Object **) xmalloc (i * sizeof (Lisp_Object *));
-  *panes = (char **) xmalloc (i * sizeof (char *));
-  *items = (int *) xmalloc (i * sizeof (int));
-  *names = (char ***) xmalloc (i * sizeof (char **));
-  *enables = (int **) xmalloc (i * sizeof (int *));
-
-  for (i = 0, tail = menu; !NILP (tail); tail = Fcdr (tail), i++)
-    {
-      item = Fcdr (Fcar (tail));
-      if (XTYPE (item) != Lisp_Cons) (void) wrong_type_argument (Qlistp, item);
-#ifdef XDEBUG
-      fprintf (stderr, "list_of_panes check tail, i=%d\n", i);
-#endif
-      item1 = Fcar (Fcar (tail));
-      CHECK_STRING (item1, 1);
-#ifdef XDEBUG
-      fprintf (stderr, "list_of_panes check pane, i=%d%s\n", i,
-	       XSTRING (item1)->data);
-#endif
-      (*panes)[i] = (char *) XSTRING (item1)->data;
-      (*items)[i] = list_of_items ((*vector)+i, (*names)+i, (*enables)+i, item);
-      /* (*panes)[i] = (char *) xmalloc ((XSTRING (item1)->size)+1);
-	 bcopy (XSTRING (item1)->data, (*panes)[i], XSTRING (item1)->size + 1)
-	 ; */
-    }
-  return i;
-}
-
-/* Construct the lists of values and names for a single pane, from the
-   alist PANE.  Put them in *VECTOR and *NAMES.  Put the enable flags
-   int *ENABLES.   Return the number of items.  */
-
-int
-list_of_items (vector, names, enables, pane)
-     Lisp_Object **vector;	/* RETURN menu "objects" */
-     char ***names;		/* RETURN line names */
-     int **enables;		/* RETURN enable flags of lines */
-     Lisp_Object pane;
-{
-  Lisp_Object tail, item, item1;
-  int i;
-
-  if (XTYPE (pane) != Lisp_Cons) pane = wrong_type_argument (Qlistp, pane);
-
-  i = XFASTINT (Flength (pane));
-
-  *vector = (Lisp_Object *) xmalloc (i * sizeof (Lisp_Object));
-  *names = (char **) xmalloc (i * sizeof (char *));
-  *enables = (int *) xmalloc (i * sizeof (int));
-
-  for (i = 0, tail = pane; !NILP (tail); tail = Fcdr (tail), i++)
-    {
-      item = Fcar (tail);
-      if (STRINGP (item))
-	{
-	  (*vector)[i] = Qnil;
-	  (*names)[i] = (char *) XSTRING (item)->data;
-	  (*enables)[i] = -1;
-	}
-      else
-	{
-	  CHECK_CONS (item, 0);
-	  (*vector)[i] = Fcdr (item);
-	  item1 = Fcar (item);
-	  CHECK_STRING (item1, 1);
-	  (*names)[i] = (char *) XSTRING (item1)->data;
-	  (*enables)[i] = 1;
-	}
-    }
-  return i;
-}