(xmenu_show) [USE_X_TOOLKIT]: Remove the #if 0 directives and reverse one to
[bpt/emacs.git] / src / xmenu.c
index 972c433..a926ad1 100644 (file)
@@ -1,5 +1,5 @@
 /* X Communication module for terminals which understand the X protocol.
-   Copyright (C) 1986, 1988, 1992 Free Software Foundation, Inc.
+   Copyright (C) 1986, 1988, 1993 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -24,17 +24,20 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
  *
  */
 
-#ifdef XDEBUG
+/* Modified by Fred Pierresteguy on December 93
+   to make the popup menus and menubar use the Xt.  */
+
 #include <stdio.h>
-#endif
 
 /* On 4.3 this loses if it comes after xterm.h.  */
 #include <signal.h>
-#include "config.h"
+#include <config.h>
 #include "lisp.h"
+#include "termhooks.h"
 #include "frame.h"
 #include "window.h"
 #include "keyboard.h"
+#include "blockinput.h"
 
 /* This may include sys/types.h, and that somehow loses
    if this is not done before the other system files.  */
@@ -54,6 +57,16 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #include <X/XMenu.h>
 #endif
 
+#ifdef USE_X_TOOLKIT
+#include <X11/Xlib.h>
+#include <X11/IntrinsicP.h>
+#include <X11/CoreP.h>
+#include <X11/StringDefs.h>
+#include <X11/Xaw/Paned.h>
+#include "../lwlib/lwlib.h"
+#include "../lwlib/xlwmenuP.h"
+#endif /* USE_X_TOOLKIT */
+
 #define min(x,y) (((x) < (y)) ? (x) : (y))
 #define max(x,y) (((x) > (y)) ? (x) : (y))
 
@@ -62,7 +75,7 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #ifndef TRUE
 #define TRUE 1
 #define FALSE 0
-#endif TRUE
+#endif /* TRUE */
 
 #ifdef HAVE_X11
 extern Display *x_current_display;
@@ -70,8 +83,18 @@ extern Display *x_current_display;
 #define        ButtonReleaseMask ButtonReleased
 #endif /* not HAVE_X11 */
 
+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 ();
+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;
 
 /*************************************************************/
 
@@ -83,12 +106,13 @@ xmenu_quit ()
 }
 #endif
 
+\f
 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\
 where XOFFSET and YOFFSET are positions in characters from the top left\n\
-corner of WINDOW's frame.  A mouse-event list will serve for this.\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\
 \n\
@@ -98,28 +122,37 @@ 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\
 You can also use a list of keymaps as MENU.\n\
-  Then each keymap makes a separate pane.\n\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\
 Alternatively, you can specify a menu of multiple panes\n\
-  with a list of the form\n\
-\(TITLE PANE1 PANE2...), where each pane is a list of form\n\
-\(TITLE (LINE ITEM)...).  Each line should be a string, and item should\n\
-be the return value for that line (i.e. if it is selected).")
+  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.")
   (position, menu)
      Lisp_Object position, menu;
 {
-  int number_of_panes;
+  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;
   char *error_name;
   Lisp_Object ltitle, selection;
-  int i, j;
+  int i, j, menubarp = 0;
   FRAME_PTR f;
   Lisp_Object x, y, window;
+#ifdef USE_X_TOOLKIT
+  widget_value *val, *vw = 0;
+#endif /* USE_X_TOOLKIT */
 
   /* Decode the first argument: find the window and the coordinates.  */
   tem = Fcar (position);
@@ -131,25 +164,133 @@ be the return value for that line (i.e. if it is selected).")
     }
   else
     {
-      tem = EVENT_START (position);
-      window = POSN_WINDOW (tem);
-      tem = POSN_WINDOW_POSN (tem);
+      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_LIVE_WINDOW (window, 0);
   CHECK_NUMBER (x, 0);
   CHECK_NUMBER (y, 0);
 
-  f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
+  if (XTYPE (window) == Lisp_Frame)
+    {
+      f = XFRAME (window);
+      
+      XMenu_xpos = 0;
+      XMenu_ypos = 0;
+    }
+  else if (XTYPE (window) == Lisp_Window)
+    {
+      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);
+
+#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)
+       {
+         w2 = w1;
+         w1 += string_width (mw, vw->name) 
+           + 2 * (mw->menu.horizontal_spacing + 
+                  mw->menu.shadow_thickness);
+         if (XINT (x) < w1)
+           {
+             XMenu_xpos = w2 + 4;
+             XMenu_ypos = 0;
+             break;
+           }
+       }
+    }
+  else
+    {
+      XMenu_xpos += FONT_WIDTH (f->display.x->font) * XINT (x);
+      XMenu_ypos += FONT_HEIGHT (f->display.x->font) * XINT (y);
+    }
+
+  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;
+
+  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);
+
+  free_menu_items (names, enables, menus, items, number_of_panes, obj_list, 
+                  title, error_name);
+
+  if (selection != NUL)
+    {                          /* selected something */
+      XMenu_return = selection;
+    }
+  else
+    {                          /* nothing selected */
+      XMenu_return = Qnil;
+    }
+
+  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,
+
+                              /* 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
-    = FONT_WIDTH (f->display.x->font) * (XINT (x) + XWINDOW (window)->left);
-  XMenu_ypos
-    = FONT_HEIGHT (f->display.x->font) * (XINT (y) + XWINDOW (window)->top);
   XMenu_xpos += f->display.x->left_pos;
   XMenu_ypos += f->display.x->top_pos;
 
+
   keymap = Fkeymapp (menu);
   tem = Qnil;
   if (XTYPE (menu) == Lisp_Cons)
@@ -167,8 +308,8 @@ be the return value for that line (i.e. if it is selected).")
        title = (char *) XSTRING (prompt)->data;
 
       /* Extract the detailed info to make one pane.  */
-      number_of_panes = keymap_panes (&obj_list, &menus, &names, &items,
-                                     &menu, 1);
+      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)
@@ -196,8 +337,8 @@ be the return value for that line (i.e. if it is selected).")
        }
 
       /* Extract the detailed info to make one pane.  */
-      number_of_panes = keymap_panes (&obj_list, &menus, &names, &items,
-                                     maps, nmaps);
+      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)
@@ -209,8 +350,9 @@ be the return value for that line (i.e. if it is selected).")
       ltitle = Fcar (menu);
       CHECK_STRING (ltitle, 1);
       title = (char *) XSTRING (ltitle)->data;
-      number_of_panes = list_of_panes (&obj_list, &menus, &names, &items,
-                                      Fcdr (menu));
+      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);
@@ -222,6 +364,7 @@ be the return value for that line (i.e. if it is selected).")
        fprintf (stderr, "    Item %d %s\n", j, names[i][j]);
     }
 #endif
+
   BLOCK_INPUT;
   {
     Window root;
@@ -243,10 +386,9 @@ be the return value for that line (i.e. if it is selected).")
                                 &dummy_window))
       /* But XGetGeometry said root was the root window of f's screen!  */ 
       abort ();
-
-    selection = xmenu_show (root, XMenu_xpos, XMenu_ypos, names, menus,
-                           items, number_of_panes, obj_list, title,
-                           &error_name);
+    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);  */
@@ -261,30 +403,572 @@ be the return value for that line (i.e. if it is selected).")
   /* now free up the strings */
   for (i = 0; i < number_of_panes; i++)
     {
-      free (names[i]);
-      free (obj_list[i]);
+      xfree (names[i]);
+      xfree (enables[i]);
+      xfree (obj_list[i]);
     }
-  free (menus);
-  free (obj_list);
-  free (names);
-  free (items);
+  xfree (menus);
+  xfree (obj_list);
+  xfree (names);
+  xfree (enables);
+  xfree (items);
   /* free (title); */
   if (error_name) error (error_name);
   return XMenu_return;
+#endif /* not USE_X_TOOLKIT */
+}
+\f
+#ifdef USE_X_TOOLKIT
+
+static void
+dispatch_dummy_expose (w, x, y)
+     Widget w;
+     int x;
+     int y;
+{
+  XExposeEvent dummy;
+       
+  dummy.type = Expose;
+  dummy.window = XtWindow (w);
+  dummy.count = 0;
+  dummy.serial = 0;
+  dummy.send_event = 0;
+  dummy.display = XtDisplay (w);
+  dummy.x = x;
+  dummy.y = y;
+
+  XtDispatchEvent (&dummy);
+}
+
+static int
+string_width (mw, s)
+     XlwMenuWidget mw;
+     char* s;
+{
+  XCharStruct xcs;
+  int drop;
+  
+  XTextExtents (mw->menu.font, s, strlen (s), &drop, &drop, &drop, &xcs);
+  return xcs.width;
+}
+
+static int
+event_is_in_menu_item (mw, event, name, string_w)
+     XlwMenuWidget mw;
+     struct input_event *event;
+     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);
+}
+
+
+Lisp_Object
+map_event_to_object (event, f)
+     struct input_event *event;
+     FRAME_PTR f;
+{
+  int i,j, string_w;
+  window_state*        ws;
+  XlwMenuWidget mw = (XlwMenuWidget) f->display.x->menubar_widget;
+  widget_value *val;
+
+
+  string_w = 0;
+  /* Find the window */
+  for (val = mw->menu.old_stack [0]->contents; val; val = val->next)
+    {
+      ws = &mw->menu.windows [0];
+      if (ws && event_is_in_menu_item (mw, event, val->name, &string_w))
+       {
+         Lisp_Object items;
+         items = FRAME_MENU_BAR_ITEMS (f);
+         for (; CONSP (items); items = XCONS (items)->cdr)
+           if (!strcmp (val->name,
+                       XSTRING (Fcar (Fcdr (Fcar (items))))->data))
+             return items;
+       }
+    }
+  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 void
+popup_selection_callback (widget, id, client_data)
+     Widget widget;
+     LWLIB_ID id;
+     XtPointer client_data;
+{
+#if 0
+  last_popup_selection_callback_id = id;
+  menubar_selection_callback (widget, id, client_data);
+  /* lw_destroy_all_widgets() will be called from popup_down_callback() */
+#endif
+  VOID_TO_LISP (menu_item_selection, client_data);
+}
+
+static void
+popup_down_callback (widget, id, client_data)
+     Widget widget;
+     LWLIB_ID id;
+     XtPointer client_data;
+{
+#if 0
+  if (popup_menu_up_p == 0) abort ();
+  popup_menu_up_p--;
+  /* if this isn't called immediately after the selection callback, then
+     there wasn't a menu selection. */
+  if (id != last_popup_selection_callback_id)
+    menubar_selection_callback (widget, id, (XtPointer) -1);
+#endif
+  BLOCK_INPUT;
+  lw_destroy_all_widgets (id);
+  UNBLOCK_INPUT;
+/*  ungcpro_popup_callbacks (make_number (id)); */
+}
+
+/* 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.
+ */
+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;
+
+  if (wv->contents && (wv->contents != (widget_value*)1))
+    {
+      free_menubar_widget_value_tree (wv->contents);
+      wv->contents = (widget_value *) 0xDEADBEEF;
+    }
+  if (wv->next)
+    {
+      free_menubar_widget_value_tree (wv->next);
+      wv->next = (widget_value *) 0xDEADBEEF;
+    }
+  BLOCK_INPUT;
+  free_widget_value (wv);
+  UNBLOCK_INPUT;
+}
+
+static void
+update_one_frame_psheets (f)
+     FRAME_PTR f;
+{
+  struct x_display *x = f->display.x;
+  
+  int menubar_changed;
+  
+  menubar_changed = (x->menubar_widget
+                    && !XtIsManaged (x->menubar_widget));
+
+  if (! (menubar_changed))
+    return;
+
+  BLOCK_INPUT;
+  XawPanedSetRefigureMode (x->column_widget, 0);
+  
+  /* 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);
+    }
+
+
+  /* Re-manage the text-area widget */
+  XtManageChild (x->edit_widget);
+
+  /* and now thrash the sizes */
+  XawPanedSetRefigureMode (x->column_widget, 1);
+  UNBLOCK_INPUT;
+}
+
+void
+set_frame_menubar (f)
+     FRAME_PTR f;
+{
+  Widget menubar_widget = f->display.x->menubar_widget;
+  int id = (int) f;
+  Lisp_Object tail;
+  widget_value *wv, *save_wv, *first_wv, *prev_wv = 0;
+
+  BLOCK_INPUT;
+
+  wv = malloc_widget_value ();
+  wv->name = "menubar";
+  wv->value = 0;
+  wv->enabled = 1;
+  save_wv = first_wv = wv;
+
+
+  for (tail = FRAME_MENU_BAR_ITEMS (f); CONSP (tail); tail = XCONS (tail)->cdr)
+    {
+      Lisp_Object string;
+
+      string = Fcar (Fcdr (Fcar (tail)));
+
+      wv = malloc_widget_value ();
+      if (prev_wv) 
+       prev_wv->next = wv;
+      else 
+       save_wv->contents = wv;
+      wv->name = XSTRING (string)->data;
+      wv->value = 0;
+      wv->enabled = 1;
+      prev_wv = wv;
+    }
+
+  if (menubar_widget)
+    lw_modify_all_widgets (id, first_wv, False);
+  else
+    {
+      menubar_widget = lw_create_widget ("menubar", "menubar", 
+                                        id, first_wv, 
+                                        f->display.x->column_widget, 
+                                        0, 0,
+                                        0, 0);
+      f->display.x->menubar_widget = menubar_widget;
+      XtVaSetValues (menubar_widget,
+                    XtNshowGrip, 0,
+                    XtNresizeToPreferred, 1,
+                    XtNallowResize, 1,
+                    0);
+    }
+  
+  free_menubar_widget_value_tree (first_wv);
+
+  update_one_frame_psheets (f);
+
+  UNBLOCK_INPUT;
+}
+#endif /* USE_X_TOOLKIT */
+\f
 struct indices {
   int pane;
   int line;
 };
 
+extern void process_expose_from_menu ();
+
+#ifdef USE_X_TOOLKIT
+extern XtAppContext Xt_app_con;
+
 Lisp_Object
-xmenu_show (parent, startx, starty, line_list, pane_list, line_cnt,
-                     pane_cnt, item_list, title, error)
+xmenu_show (f, val, x, y, menubarp, vw)
+     FRAME_PTR f;
+     widget_value *val;
+     int x;
+     int y;
+     int menubarp;
+     widget_value *vw;
+{
+  int menu_id, id = (int)f;
+  Lisp_Object selection;
+  Widget menu;
+
+  /*
+   * 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 
+    {
+      XEvent event;
+      struct _xmeventque *next;
+    } XMEventQue;
+  
+  XMEventQue *feq = NULL;              /* Foreign event queue. */
+  XMEventQue *feq_tmp;         /* Foreign event queue temporary. */
+  
+  BLOCK_INPUT;
+  menu_id = ++popup_id_tick;
+  menu = lw_create_widget ("popup", val->name, menu_id, val, 
+                          f->display.x->widget, 1, 0,
+                          popup_selection_callback, popup_down_callback);
+  free_menubar_widget_value_tree (val);
+
+  /* reset the selection */
+  menu_item_selection = Qnil;
+
+  {
+    XButtonPressedEvent dummy;
+    XlwMenuWidget mw;
+    
+    mw = ((XlwMenuWidget)
+         ((CompositeWidget)menu)->composite.children[0]);
+
+    dummy.type = ButtonPress;
+    dummy.serial = 0;
+    dummy.send_event = 0;
+    dummy.display = XtDisplay (menu);
+    dummy.window = XtWindow (XtParent (menu));
+    dummy.time = CurrentTime;
+    dummy.button = 0;
+    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 */
+    pop_up_menu (mw, &dummy);
+  }
+
+  /* Enters XEvent loop */
+  while (1)
+    {
+
+      XEvent event;
+      XtAppNextEvent (Xt_app_con, &event);
+      if (event.type == ButtonRelease)
+       {
+         XtDispatchEvent (&event);
+         break;
+       }
+      else
+       if (event.type == Expose)
+         process_expose_from_menu (event);
+      XtDispatchEvent (&event);
+      feq_tmp = (XMEventQue *) malloc (sizeof (XMEventQue));
+
+      if (feq_tmp == NULL) 
+       return(Qnil);
+
+      feq_tmp->event = event;
+      feq_tmp->next = feq;
+      feq = feq_tmp;
+    }
+      
+  if (menubarp)
+    {
+      vw->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) 
+    {
+      feq_tmp = feq;
+      XPutBackEvent (XDISPLAY &feq_tmp->event);
+      feq = feq_tmp->next;
+      free ((char *)feq_tmp);
+    }
+
+  UNBLOCK_INPUT;
+
+  return menu_item_selection;
+}
+
+#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 */
@@ -300,17 +984,21 @@ xmenu_show (parent, startx, starty, line_list, pane_list, line_cnt,
   int ulx, uly, width, height;
   int dispwidth, dispheight;
 
+  *error = 0;
   if (pane_cnt == 0)
     return 0;
 
+  BLOCK_INPUT;
   *error = (char *) 0;         /* Initialize error pointer to null */
+
   GXMenu = XMenuCreate (XDISPLAY parent, "emacs");
   if (GXMenu == NUL)
     {
       *error = "Can't create menu";
+      UNBLOCK_INPUT;
       return (0);
     }
-  
   for (panes = 0, lines = 0; panes < pane_cnt;
        lines += line_cnt[panes], panes++)
     ;
@@ -327,21 +1015,25 @@ xmenu_show (parent, startx, starty, line_list, pane_list, line_cnt,
        {
          XMenuDestroy (XDISPLAY GXMenu);
          *error = "Can't create pane";
+         UNBLOCK_INPUT;
          return (0);
        }
+
       for (selidx = 0; selidx < line_cnt[panes]; selidx++)
        {
          /* 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], TRUE)
+                                line_list[panes][selidx],
+                                enable_list[panes][selidx])
              == XM_FAILURE)
            {
              XMenuDestroy (XDISPLAY GXMenu);
              /* free (datap); */
              *error = "Can't add selection to menu";
              /* error ("Can't add selection to menu"); */
+             UNBLOCK_INPUT;
              return (0);
            }
        }
@@ -381,6 +1073,12 @@ xmenu_show (parent, startx, starty, line_list, pane_list, line_cnt,
       fprintf (stderr, "pane= %d line = %d\n", panes, selidx);
 #endif
       entry = item_list[panes][selidx];
+      if (prefixes != 0)
+       {
+         entry = Fcons (entry, Qnil);
+         if (!NILP (prefixes[panes]))
+           entry = Fcons (prefixes[panes], entry);
+       }
       break;
     case XM_FAILURE:
       /* free (datap_save); */
@@ -393,28 +1091,33 @@ xmenu_show (parent, startx, starty, line_list, pane_list, line_cnt,
       break;
     }
   XMenuDestroy (XDISPLAY GXMenu);
+  UNBLOCK_INPUT;
   /* free (datap_save);*/
   return (entry);
 }
+#endif /* not USE_X_TOOLKIT */
 
 syms_of_xmenu ()
 {
+  popup_id_tick = (1<<16);     
   defsubr (&Sx_popup_menu);
 }
 \f
 /* Construct the vectors that describe a menu
-   and store them in *VECTOR, *PANES, *NAMES and *ITEMS.
+   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, items, keymaps, nmaps)
+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;
 {
@@ -432,13 +1135,15 @@ keymap_panes (vector, panes, names, items, keymaps, nmaps)
   *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, items,
-                        &p, &npanes_allocated, "");
+    single_keymap_panes (keymaps[mapno], panes, vector, names, enables, items,
+                        prefixes, &p, &npanes_allocated, "");
 
   /* Return the number of panes.  */
   return p;
@@ -449,13 +1154,15 @@ keymap_panes (vector, panes, names, items, keymaps, nmaps)
    The other arguments are passed along
    or point to local variables of the previous function.  */
 
-single_keymap_panes (keymap, panes, vector, names, items,
+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;
@@ -480,14 +1187,25 @@ single_keymap_panes (keymap, panes, vector, names, items,
       *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));
 
@@ -500,6 +1218,7 @@ single_keymap_panes (keymap, panes, vector, names, items,
      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;
@@ -516,16 +1235,30 @@ single_keymap_panes (keymap, panes, vector, names, items,
              item2 = XCONS (item1)->car;
              if (XTYPE (item2) == Lisp_String)
                {
-                 Lisp_Object tem;
-                 tem = Fkeymapp (Fcdr (item1));
+                 Lisp_Object def, tem;
+                 Lisp_Object enabled;
+
+                 def = Fcdr (item1);
+                 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))
+                       enabled = Feval (tem);
+                   }
+                 tem = Fkeymapp (def);
                  if (XSTRING (item2)->data[0] == '@' && !NILP (tem))
-                   pending_maps = Fcons (Fcons (Fcdr (item1), item2),
+                   pending_maps = Fcons (Fcons (def, Fcons (item2, XCONS (item)->car)),
                                          pending_maps);
                  else
                    {
                      (*names)[*p_ptr][i] = (char *) XSTRING (item2)->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++;
                    }
                }
@@ -547,15 +1280,31 @@ single_keymap_panes (keymap, panes, vector, names, items,
                  if (XTYPE (item2) == Lisp_String)
                    {
                      Lisp_Object tem;
-                     tem = Fkeymapp (Fcdr (item1));
+                     Lisp_Object def;
+                     Lisp_Object enabled;
+
+                     def = Fcdr (item1);
+                     enabled = Qt;
+                     if (XTYPE (def) == Lisp_Symbol)
+                       {
+                         tem = Fget (def, Qmenu_enable);
+                         /* No property, or nil, means enable.
+                            Otherwise, enable if value is not nil.  */
+                         if (!NILP (tem))
+                           enabled = Feval (tem);
+                       }
+
+                     tem = Fkeymapp (def);
                      if (XSTRING (item2)->data[0] == '@' && !NILP (tem))
-                       pending_maps = Fcons (Fcons (Fcdr (item1), item2),
+                       pending_maps = Fcons (Fcons (def, Fcons (item2, character)),
                                              pending_maps);
                      else
                        {
                          (*names)[*p_ptr][i] = (char *) XSTRING (item2)->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++;
                        }
                    }
@@ -569,8 +1318,9 @@ single_keymap_panes (keymap, panes, vector, names, items,
   /* If we just made an empty pane, get rid of it.  */
   if (i == 0)
     {
-      free ((*vector)[*p_ptr]);
-      free ((*names)[*p_ptr]);
+      xfree ((*vector)[*p_ptr]);
+      xfree ((*names)[*p_ptr]);
+      xfree ((*enables)[*p_ptr]);
     }
   /* Otherwise, advance past it.  */
   else
@@ -579,28 +1329,32 @@ single_keymap_panes (keymap, panes, vector, names, items,
   /* Process now any submenus which want to be panes at this level.  */
   while (!NILP (pending_maps))
     {
-      Lisp_Object elt;
+      Lisp_Object elt, eltcdr;
+      int panenum = *p_ptr;
       elt = Fcar (pending_maps);
-      single_keymap_panes (Fcar (elt), panes, vector, names, items,
-                          p_ptr, npanes_allocated_ptr,
+      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 (elt)->cdr)->data + 1);
+                          (char *) XSTRING (XCONS (eltcdr)->car)->data + 1);
+      (*prefixes)[panenum] = XCONS (eltcdr)->cdr;
       pending_maps = Fcdr (pending_maps);
     }
 }
 \f
 /* Construct the vectors that describe a menu
-   and store them in *VECTOR, *PANES, *NAMES and *ITEMS.
+   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, items, menu)
+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;
 {
@@ -615,6 +1369,7 @@ list_of_panes (vector, panes, names, items, menu)
   *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++)
     {
@@ -630,7 +1385,7 @@ list_of_panes (vector, panes, names, items, menu)
               XSTRING (item1)->data);
 #endif
       (*panes)[i] = (char *) XSTRING (item1)->data;
-      (*items)[i] = list_of_items ((*vector)+i, (*names)+i, item);
+      (*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)
         ; */
@@ -639,13 +1394,14 @@ list_of_panes (vector, panes, names, items, menu)
 }
 \f
 /* Construct the lists of values and names for a single pane, from the
-   alist PANE.  Put them in *VECTOR and *NAMES.
-   Return the number of items.  */
+   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, pane)  /* get list from emacs and put to vector */
+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;
@@ -657,22 +1413,26 @@ list_of_items (vector, names, pane)  /* get list from emacs and put to vector */
 
   *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 (XTYPE (item) != Lisp_Cons) (void) wrong_type_argument (Qlistp, item);
-#ifdef XDEBUG
-      fprintf (stderr, "list_of_items check tail, i=%d\n", i);
-#endif
-      (*vector)[i] =  Fcdr (item);
-      item1 = Fcar (item);
-      CHECK_STRING (item1, 1);
-#ifdef XDEBUG
-      fprintf (stderr, "list_of_items check item, i=%d%s\n", i,
-              XSTRING (item1)->data);
-#endif
-      (*names)[i] = (char *) XSTRING (item1)->data;
+      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;
 }