]> code.delx.au - gnu-emacs/blobdiff - src/xmenu.c
* xfns.c (xpm_load) [!ALLOC_XPM_COLORS]: Declare local variable I in inner block.
[gnu-emacs] / src / xmenu.c
index 1e6b0b13f1f5e323e7f7ed9732c456d9126ab77f..f3e27be2225888aaa63b4769a49b6ac596758b74 100644 (file)
@@ -1,5 +1,5 @@
 /* X Communication module for terminals which understand the X protocol.
-   Copyright (C) 1986, 1988, 1993, 1994, 1996 Free Software Foundation, Inc.
+   Copyright (C) 1986, 88, 93, 94, 96, 99, 2000 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -30,16 +30,17 @@ Boston, MA 02111-1307, USA.  */
 
 /* Rewritten for clarity and GC protection by rms in Feb 94.  */
 
+#include <config.h>
+
 /* On 4.3 this loses if it comes after xterm.h.  */
 #include <signal.h>
-#include <config.h>
 
 #include <stdio.h>
 #include "lisp.h"
 #include "termhooks.h"
+#include "keyboard.h"
 #include "frame.h"
 #include "window.h"
-#include "keyboard.h"
 #include "blockinput.h"
 #include "buffer.h"
 
@@ -62,6 +63,7 @@ Boston, MA 02111-1307, USA.  */
 #include "dispextern.h"
 
 #ifdef HAVE_X_WINDOWS
+#undef HAVE_MULTILINGUAL_MENU
 #ifdef USE_X_TOOLKIT
 #include <X11/Xlib.h>
 #include <X11/IntrinsicP.h>
@@ -77,6 +79,10 @@ Boston, MA 02111-1307, USA.  */
 #endif /* not USE_X_TOOLKIT */
 #endif /* HAVE_X_WINDOWS */
 
+#ifdef USE_MOTIF
+#include <Xm/Xm.h>             /* for LESSTIF_VERSION */
+#endif
+
 #define min(x,y) (((x) < (y)) ? (x) : (y))
 #define max(x,y) (((x) > (y)) ? (x) : (y))
 
@@ -110,6 +116,16 @@ static Lisp_Object xdialog_show ();
 void popup_get_selection ();
 #endif
 
+#ifdef USE_X_TOOLKIT
+
+/* Define HAVE_BOXES if meus can handle radio and toggle buttons.  */
+
+#define HAVE_BOXES 1
+#endif
+
+static void push_menu_item P_ ((Lisp_Object, Lisp_Object, Lisp_Object,
+                               Lisp_Object, Lisp_Object, Lisp_Object,
+                               Lisp_Object, Lisp_Object));
 static Lisp_Object xmenu_show ();
 static void keymap_panes ();
 static void single_keymap_panes ();
@@ -143,12 +159,18 @@ static void list_of_items ();
 #define MENU_ITEMS_PANE_PREFIX 2
 #define MENU_ITEMS_PANE_LENGTH 3
 
-#define MENU_ITEMS_ITEM_NAME 0
-#define MENU_ITEMS_ITEM_ENABLE 1
-#define MENU_ITEMS_ITEM_VALUE 2
-#define MENU_ITEMS_ITEM_EQUIV_KEY 3
-#define MENU_ITEMS_ITEM_DEFINITION 4
-#define MENU_ITEMS_ITEM_LENGTH 5
+enum menu_item_idx
+{
+  MENU_ITEMS_ITEM_NAME = 0,
+  MENU_ITEMS_ITEM_ENABLE,
+  MENU_ITEMS_ITEM_VALUE,
+  MENU_ITEMS_ITEM_EQUIV_KEY,
+  MENU_ITEMS_ITEM_DEFINITION,
+  MENU_ITEMS_ITEM_TYPE,
+  MENU_ITEMS_ITEM_SELECTED,
+  MENU_ITEMS_ITEM_HELP,
+  MENU_ITEMS_ITEM_LENGTH
+};
 
 static Lisp_Object menu_items;
 
@@ -167,7 +189,7 @@ static int menu_items_submenu_depth;
 
 /* Flag which when set indicates a dialog or menu has been posted by
    Xt on behalf of one of the widget sets.  */
-static int popup_activated_flag;
+int popup_activated_flag;
 
 static int next_menubar_widget_id;
 
@@ -191,13 +213,13 @@ menubar_id_to_frame (id)
   Lisp_Object tail, frame;
   FRAME_PTR f;
 
-  for (tail = Vframe_list; GC_CONSP (tail); tail = XCONS (tail)->cdr)
+  for (tail = Vframe_list; GC_CONSP (tail); tail = XCDR (tail))
     {
-      frame = XCONS (tail)->car;
+      frame = XCAR (tail);
       if (!GC_FRAMEP (frame))
         continue;
       f = XFRAME (frame);
-      if (f->output_data.nothing == 1)
+      if (!FRAME_WINDOW_P (f))
        continue;
       if (f->output_data.x->id == id)
        return f;
@@ -314,17 +336,17 @@ push_menu_pane (name, prefix_vec)
   XVECTOR (menu_items)->contents[menu_items_used++] = prefix_vec;
 }
 
-/* Push one menu item into the current pane.
-   NAME is the string to display.  ENABLE if non-nil means
-   this item can be selected.  KEY is the key generated by
-   choosing this item, or nil if this item doesn't really have a definition.
-   DEF is the definition of this item.
-   EQUIV is the textual description of the keyboard equivalent for
-   this item (or nil if none).  */
+/* Push one menu item into the current pane.  NAME is the string to
+   display.  ENABLE if non-nil means this item can be selected.  KEY
+   is the key generated by choosing this item, or nil if this item
+   doesn't really have a definition.  DEF is the definition of this
+   item.  EQUIV is the textual description of the keyboard equivalent
+   for this item (or nil if none).  TYPE is the type of this menu
+   item, one of nil, `toggle' or `radio'. */
 
 static void
-push_menu_item (name, enable, key, def, equiv)
-     Lisp_Object name, enable, key, def, equiv;
+push_menu_item (name, enable, key, def, equiv, type, selected, help)
+     Lisp_Object name, enable, key, def, equiv, type, selected, help;
 {
   if (menu_items_used + MENU_ITEMS_ITEM_LENGTH > menu_items_allocated)
     grow_menu_items ();
@@ -334,6 +356,9 @@ push_menu_item (name, enable, key, def, equiv)
   XVECTOR (menu_items)->contents[menu_items_used++] = key;
   XVECTOR (menu_items)->contents[menu_items_used++] = equiv;
   XVECTOR (menu_items)->contents[menu_items_used++] = def;
+  XVECTOR (menu_items)->contents[menu_items_used++] = type;
+  XVECTOR (menu_items)->contents[menu_items_used++] = selected;
+  XVECTOR (menu_items)->contents[menu_items_used++] = help;
 }
 \f
 /* Look through KEYMAPS, a vector of keymaps that is NMAPS long,
@@ -355,7 +380,8 @@ keymap_panes (keymaps, nmaps, notreal)
      But don't make a pane that is empty--ignore that map instead.
      P is the number of panes we have made so far.  */
   for (mapno = 0; mapno < nmaps; mapno++)
-    single_keymap_panes (keymaps[mapno], Qnil, Qnil, notreal, 10);
+    single_keymap_panes (keymaps[mapno],
+                        map_prompt (keymaps[mapno]), Qnil, notreal, 10);
 
   finish_menu_items ();
 }
@@ -395,14 +421,14 @@ single_keymap_panes (keymap, pane_name, prefix, notreal, maxdepth)
   notbuttons = menu_items_used;
 #endif
 
-  for (tail = keymap; CONSP (tail); tail = XCONS (tail)->cdr)
+  for (tail = keymap; CONSP (tail); tail = XCDR (tail))
     {
       GCPRO2 (keymap, pending_maps);
       /* Look at each key binding, and if it is a menu item add it
         to this menu.  */
-      item = XCONS (tail)->car;
+      item = XCAR (tail);
       if (CONSP (item))
-       single_menu_item (XCONS (item)->car, XCONS (item)->cdr,
+       single_menu_item (XCAR (item), XCDR (item),
                          &pending_maps, notreal, maxdepth, &notbuttons);
       else if (VECTORP (item))
        {
@@ -425,12 +451,12 @@ single_keymap_panes (keymap, pane_name, prefix, notreal, maxdepth)
     {
       Lisp_Object elt, eltcdr, string;
       elt = Fcar (pending_maps);
-      eltcdr = XCONS (elt)->cdr;
-      string = XCONS (eltcdr)->car;
+      eltcdr = XCDR (elt);
+      string = XCAR (eltcdr);
       /* We no longer discard the @ from the beginning of the string here.
         Instead, we do this in xmenu_show.  */
       single_keymap_panes (Fcar (elt), string,
-                          XCONS (eltcdr)->cdr, notreal, maxdepth - 1);
+                          XCDR (eltcdr), notreal, maxdepth - 1);
       pending_maps = Fcdr (pending_maps);
     }
 }
@@ -455,7 +481,7 @@ single_menu_item (key, item, pending_maps_ptr, notreal, maxdepth,
      int maxdepth, notreal;
      int *notbuttons_ptr;
 {
-  Lisp_Object def, map, item_string, enabled;
+  Lisp_Object map, item_string, enabled;
   struct gcpro gcpro1, gcpro2;
   int res;
   
@@ -560,7 +586,10 @@ single_menu_item (key, item, pending_maps_ptr, notreal, maxdepth,
 
   push_menu_item (item_string, enabled, key,
                  XVECTOR (item_properties)->contents[ITEM_PROPERTY_DEF],
-                 XVECTOR (item_properties)->contents[ITEM_PROPERTY_KEYEQ]);
+                 XVECTOR (item_properties)->contents[ITEM_PROPERTY_KEYEQ],
+                 XVECTOR (item_properties)->contents[ITEM_PROPERTY_TYPE],
+                 XVECTOR (item_properties)->contents[ITEM_PROPERTY_SELECTED],
+                 XVECTOR (item_properties)->contents[ITEM_PROPERTY_HELP]);
 
 #ifdef USE_X_TOOLKIT
   /* Display a submenu using the toolkit.  */
@@ -612,7 +641,7 @@ list_of_items (pane)
     {
       item = Fcar (tail);
       if (STRINGP (item))
-       push_menu_item (item, Qnil, Qnil, Qt, Qnil);
+       push_menu_item (item, Qnil, Qnil, Qt, Qnil, Qnil, Qnil, Qnil);
       else if (NILP (item))
        push_left_right_boundary ();
       else
@@ -620,7 +649,7 @@ list_of_items (pane)
          CHECK_CONS (item, 0);
          item1 = Fcar (item);
          CHECK_STRING (item1, 1);
-         push_menu_item (item1, Qt, Fcdr (item), Qt, Qnil);
+         push_menu_item (item1, Qt, Fcdr (item), Qt, Qnil, Qnil, Qnil, Qnil);
        }
     }
 }
@@ -661,14 +690,12 @@ cached information about equivalent key sequences.")
   (position, menu)
      Lisp_Object position, menu;
 {
-  int number_of_panes, panes;
   Lisp_Object keymap, tem;
-  int xpos, ypos;
+  int xpos = 0, ypos = 0;
   Lisp_Object title;
   char *error_name;
   Lisp_Object selection;
-  int i, j;
-  FRAME_PTR f;
+  struct frame *f = NULL;
   Lisp_Object x, y, window;
   int keymaps = 0;
   int for_click = 0;
@@ -681,12 +708,13 @@ cached information about equivalent key sequences.")
 
       /* Decode the first argument: find the window and the coordinates.  */
       if (EQ (position, Qt)
-         || (CONSP (position) && EQ (XCONS (position)->car, Qmenu_bar)))
+         || (CONSP (position) && (EQ (XCAR (position), Qmenu_bar)
+                                  || EQ (XCAR (position), Qtool_bar))))
        {
          /* Use the mouse's current position.  */
-         FRAME_PTR new_f = selected_frame;
+         FRAME_PTR new_f = SELECTED_FRAME ();
          Lisp_Object bar_window;
-         int part;
+         enum scroll_bar_part part;
          unsigned long time;
 
          if (mouse_position_hook)
@@ -760,15 +788,11 @@ cached information about equivalent key sequences.")
 
   /* Decode the menu items from what was specified.  */
 
-  keymap = Fkeymapp (menu);
-  tem = Qnil;
-  if (CONSP (menu))
-    tem = Fkeymapp (Fcar (menu));
-  if (!NILP (keymap))
+  keymap = get_keymap (menu, 0, 0);
+  if (CONSP (keymap))
     {
       /* We were given a keymap.  Extract menu info from the keymap.  */
       Lisp_Object prompt;
-      keymap = get_keymap (menu);
 
       /* Extract the detailed info to make one pane.  */
       keymap_panes (&menu, 1, NILP (position));
@@ -785,7 +809,7 @@ cached information about equivalent key sequences.")
 
       keymaps = 1;
     }
-  else if (!NILP (tem))
+  else if (CONSP (menu) && KEYMAPP (XCAR (menu)))
     {
       /* We were given a list of keymaps.  */
       int nmaps = XFASTINT (Flength (menu));
@@ -801,7 +825,7 @@ cached information about equivalent key sequences.")
        {
          Lisp_Object prompt;
 
-         maps[i++] = keymap = get_keymap (Fcar (tem));
+         maps[i++] = keymap = get_keymap (Fcar (tem), 1, 0);
 
          prompt = map_prompt (keymap);
          if (NILP (title) && !NILP (prompt))
@@ -872,18 +896,19 @@ on the left of the dialog box and all following items on the right.\n\
   (position, contents)
      Lisp_Object position, contents;
 {
-  FRAME_PTR f;
+  struct frame * f = NULL;
   Lisp_Object window;
 
   check_x ();
 
   /* Decode the first argument: find the window or frame to use.  */
   if (EQ (position, Qt)
-      || (CONSP (position) && EQ (XCONS (position)->car, Qmenu_bar)))
+      || (CONSP (position) && (EQ (XCAR (position), Qmenu_bar)
+                              || EQ (XCAR (position), Qtool_bar))))
     {
 #if 0 /* Using the frame the mouse is on may not be right.  */
       /* Use the mouse's current position.  */
-      FRAME_PTR new_f = selected_frame;
+      FRAME_PTR new_f = SELECTED_FRAME ();
       Lisp_Object bar_window;
       int part;
       unsigned long time;
@@ -1025,8 +1050,12 @@ popup_get_selection (initial_event, dpyinfo, id)
       else if (event.type == KeyPress
               && dpyinfo->display == event.xbutton.display)
        {
-         popup_activated_flag = 0;
-         break;
+         KeySym keysym = XLookupKeysym (&event.xkey, 0);
+         if (!IsModifierKey (keysym))
+           {
+             popup_activated_flag = 0;
+             break;
+           }
        }
       /* Button presses outside the menu also pop it down.  */
       else if (event.type == ButtonPress
@@ -1048,14 +1077,10 @@ popup_get_selection (initial_event, dpyinfo, id)
          && (event.xany.display != dpyinfo->display
              || x_non_menubar_window_to_frame (dpyinfo, event.xany.window)))
        {
-         queue_tmp = (struct event_queue *) malloc (sizeof (struct event_queue));
-
-         if (queue_tmp != NULL) 
-           {
-             queue_tmp->event = event;
-             queue_tmp->next = queue;
-             queue = queue_tmp;
-           }
+         queue_tmp = (struct event_queue *) xmalloc (sizeof *queue_tmp);
+         queue_tmp->event = event;
+         queue_tmp->next = queue;
+         queue = queue_tmp;
        }
       else
        XtDispatchEvent (&event);
@@ -1071,7 +1096,7 @@ popup_get_selection (initial_event, dpyinfo, id)
       queue_tmp = queue;
       XPutBackEvent (queue_tmp->event.xany.display, &queue_tmp->event);
       queue = queue_tmp->next;
-      free ((char *)queue_tmp);
+      xfree ((char *)queue_tmp);
       /* Cause these events to get read as soon as we UNBLOCK_INPUT.  */
       interrupt_input_pending = 1;
     }
@@ -1091,6 +1116,7 @@ popup_get_selection (initial_event, dpyinfo, id)
    passing it to the toolkit right away, is that we can safely
    execute Lisp code.  */
    
+void
 x_activate_menubar (f)
      FRAME_PTR f;
 {
@@ -1118,7 +1144,6 @@ popup_activated ()
   return popup_activated_flag;
 }
 
-
 /* This callback is invoked when the user selects a menubar cascade
    pushbutton, but before the pulldown menu is posted.  */
 
@@ -1128,7 +1153,72 @@ popup_activate_callback (widget, id, client_data)
      LWLIB_ID id;
      XtPointer client_data;
 {
+#ifdef USE_MOTIF
+  ++popup_activated_flag;
+#else
   popup_activated_flag = 1;
+#endif
+}
+
+/* This callback is invoked when a dialog or menu is finished being
+   used and has been unposted.  */
+
+static void
+popup_deactivate_callback (widget, id, client_data)
+     Widget widget;
+     LWLIB_ID id;
+     XtPointer client_data;
+{
+#ifdef USE_MOTIF
+  --popup_activated_flag;
+#else
+  popup_activated_flag = 0;
+#endif
+}
+
+/* Lwlib callback called when menu items are highlighted/unhighlighted
+   while moving the mouse over them.  WIDGET is the menu bar or menu
+   popup widget.  ID is its LWLIB_ID.  CALL_DATA contains a pointer to
+   the widget_value structure for the menu item, or null in case of
+   unhighlighting.  */
+
+void
+menu_highlight_callback (widget, id, call_data)
+     Widget widget;
+     LWLIB_ID id;
+     void *call_data;
+{
+  widget_value *wv = (widget_value *) call_data;
+  struct frame *f;
+  Lisp_Object frame, help;
+
+  help = wv && wv->help ? build_string (wv->help) : Qnil;
+  
+  /* Determine the frame for the help event.  */
+  f = menubar_id_to_frame (id);
+  if (f)
+    {
+      XSETFRAME (frame, f);
+      kbd_buffer_store_help_event (frame, help);
+    }
+  else
+    {
+      /* WIDGET is the popup menu.  It's parent is the frame's 
+        widget.  See which frame that is.  */
+      Widget frame_widget = XtParent (widget);
+      Lisp_Object tail;
+
+      for (tail = Vframe_list; GC_CONSP (tail); tail = XCDR (tail))
+       {
+         frame = XCAR (tail);
+         if (GC_FRAMEP (frame)
+             && (f = XFRAME (frame),
+                 FRAME_X_P (f) && f->output_data.x->widget == frame_widget))
+           break;
+       }
+
+      show_help_echo (help, Qnil, Qnil, Qnil, 1);
+    }
 }
 
 /* This callback is called from the menu bar pulldown menu
@@ -1151,6 +1241,7 @@ menubar_selection_callback (widget, id, client_data)
 
   if (!f)
     return;
+  entry = Qnil;
   subprefix_stack = (Lisp_Object *) alloca (f->menu_bar_items_used * sizeof (Lisp_Object));
   vector = f->menu_bar_vector;
   prefix = Qnil;
@@ -1185,27 +1276,31 @@ menubar_selection_callback (widget, id, client_data)
              Lisp_Object frame;
 
              XSETFRAME (frame, f);
-             buf.kind = menu_bar_event;
-             buf.frame_or_window = Fcons (frame, Fcons (Qmenu_bar, Qnil));
+             buf.kind = MENU_BAR_EVENT;
+             buf.frame_or_window = frame;
+             buf.arg = frame;
              kbd_buffer_store_event (&buf);
 
              for (j = 0; j < submenu_depth; j++)
                if (!NILP (subprefix_stack[j]))
                  {
-                   buf.kind = menu_bar_event;
-                   buf.frame_or_window = Fcons (frame, subprefix_stack[j]);
+                   buf.kind = MENU_BAR_EVENT;
+                   buf.frame_or_window = frame;
+                   buf.arg = subprefix_stack[j];
                    kbd_buffer_store_event (&buf);
                  }
 
              if (!NILP (prefix))
                {
-                 buf.kind = menu_bar_event;
-                 buf.frame_or_window = Fcons (frame, prefix);
+                 buf.kind = MENU_BAR_EVENT;
+                 buf.frame_or_window = frame;
+                 buf.arg = prefix;
                  kbd_buffer_store_event (&buf);
                }
 
-             buf.kind = menu_bar_event;
-             buf.frame_or_window = Fcons (frame, entry);
+             buf.kind = MENU_BAR_EVENT;
+             buf.frame_or_window = frame;
+             buf.arg = entry;
              kbd_buffer_store_event (&buf);
 
              return;
@@ -1215,18 +1310,6 @@ menubar_selection_callback (widget, id, client_data)
     }
 }
 
-/* This callback is invoked when a dialog or menu is finished being
-   used and has been unposted.  */
-
-static void
-popup_deactivate_callback (widget, id, client_data)
-     Widget widget;
-     LWLIB_ID id;
-     XtPointer client_data;
-{
-  popup_activated_flag = 0;
-}
-
 /* Allocate a widget_value, blocking input.  */
 
 widget_value *
@@ -1284,7 +1367,6 @@ single_submenu (item_key, item_name, maps)
   int len;
   Lisp_Object *mapvec;
   widget_value **submenu_stack;
-  int mapno;
   int previous_items = menu_items_used;
   int top_level_items = 0;
 
@@ -1306,14 +1388,14 @@ single_submenu (item_key, item_name, maps)
   for (i = 0; i < len; i++)
     {
       if (SYMBOLP (mapvec[i])
-         || (CONSP (mapvec[i])
-             && NILP (Fkeymapp (mapvec[i]))))
+         || (CONSP (mapvec[i]) && !KEYMAPP (mapvec[i])))
        {
          /* Here we have a command at top level in the menu bar
             as opposed to a submenu.  */
          top_level_items = 1;
          push_menu_pane (Qnil, Qnil);
-         push_menu_item (item_name, Qt, item_key, mapvec[i], Qnil);
+         push_menu_item (item_name, Qt, item_key, mapvec[i],
+                         Qnil, Qnil, Qnil, Qnil);
        }
       else
        single_keymap_panes (mapvec[i], item_name, item_key, 0, 10);
@@ -1328,6 +1410,7 @@ single_submenu (item_key, item_name, maps)
   wv->name = "menu";
   wv->value = 0;
   wv->enabled = 1;
+  wv->button_type = BUTTON_TYPE_NONE;
   first_wv = wv;
   save_wv = 0;
   prev_wv = 0;
@@ -1366,6 +1449,10 @@ single_submenu (item_key, item_name, maps)
          char *pane_string;
          pane_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_NAME];
          prefix = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
+#ifndef HAVE_MULTILINGUAL_MENU
+         if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name))
+           pane_name = string_make_unibyte (pane_name);
+#endif
          pane_string = (NILP (pane_name)
                         ? "" : (char *) XSTRING (pane_name)->data);
          /* If there is just one top-level pane, put all its items directly
@@ -1390,6 +1477,7 @@ single_submenu (item_key, item_name, maps)
                wv->name++;
              wv->value = 0;
              wv->enabled = 1;
+             wv->button_type = BUTTON_TYPE_NONE;
            }
          save_wv = wv;
          prev_wv = 0;
@@ -1398,12 +1486,24 @@ single_submenu (item_key, item_name, maps)
       else
        {
          /* Create a new item within current pane.  */
-         Lisp_Object item_name, enable, descrip, def;
+         Lisp_Object item_name, enable, descrip, def, type, selected;
+         Lisp_Object help;
+         
          item_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_NAME];
          enable = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_ENABLE];
          descrip
            = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_EQUIV_KEY];
          def = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_DEFINITION];
+         type = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_TYPE];
+         selected = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_SELECTED];
+         help = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_HELP];
+
+#ifndef HAVE_MULTILINGUAL_MENU
+          if (STRING_MULTIBYTE (item_name))
+            item_name = string_make_unibyte (item_name);
+          if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
+            descrip = string_make_unibyte (descrip);
+#endif
 
          wv = xmalloc_widget_value ();
          if (prev_wv) 
@@ -1419,6 +1519,20 @@ single_submenu (item_key, item_name, maps)
             as long as pointers have enough bits to hold small integers.  */
          wv->call_data = (!NILP (def) ? (void *) (EMACS_INT) i : 0);
          wv->enabled = !NILP (enable);
+         
+         if (NILP (type))
+           wv->button_type = BUTTON_TYPE_NONE;
+         else if (EQ (type, QCradio))
+           wv->button_type = BUTTON_TYPE_RADIO;
+         else if (EQ (type, QCtoggle))
+           wv->button_type = BUTTON_TYPE_TOGGLE;
+         else
+           abort ();
+
+         wv->selected = !NILP (selected);
+         if (STRINGP (help))
+           wv->help = XSTRING (help)->data;
+                  
          prev_wv = wv;
 
          i += MENU_ITEMS_ITEM_LENGTH;
@@ -1450,8 +1564,6 @@ update_frame_menubar (f)
   int columns, rows;
   int menubar_changed;
   
-  Dimension shell_height;
-
   /* We assume the menubar contents has changed if the global flag is set,
      or if the current buffer has changed, or if the menubar has never
      been updated before.
@@ -1485,16 +1597,16 @@ update_frame_menubar (f)
     {
       XtManageChild (x->menubar_widget);
       XtMapWidget (x->menubar_widget);
-      XtVaSetValues (x->menubar_widget, XtNmappedWhenManaged, 1, 0);
+      XtVaSetValues (x->menubar_widget, XtNmappedWhenManaged, 1, NULL);
     }
 
   /* Re-manage the text-area widget, and then thrash the sizes.  */
   XtManageChild (x->edit_widget);
+  x_set_menu_resources_from_menu_face (f, x->menubar_widget);
   lw_refigure_widget (x->column_widget, True);
 
   /* Force the pane widget to resize itself with the right values.  */
   EmacsFrameSetCharSize (x->edit_widget, columns, rows);
-
   UNBLOCK_INPUT;
 }
 
@@ -1509,7 +1621,7 @@ set_frame_menubar (f, first_time, deep_p)
      int deep_p;
 {
   Widget menubar_widget = f->output_data.x->menubar_widget;
-  Lisp_Object tail, items, frame;
+  Lisp_Object items;
   widget_value *wv, *first_wv, *prev_wv = 0;
   int i;
   LWLIB_ID id;
@@ -1536,6 +1648,7 @@ set_frame_menubar (f, first_time, deep_p)
   wv->name = "menubar";
   wv->value = 0;
   wv->enabled = 1;
+  wv->button_type = BUTTON_TYPE_NONE;
   first_wv = wv;
 
   if (deep_p)
@@ -1608,6 +1721,7 @@ set_frame_menubar (f, first_time, deep_p)
            first_wv->contents = wv;
          /* Don't set wv->name here; GC during the loop might relocate it.  */
          wv->enabled = 1;
+         wv->button_type = BUTTON_TYPE_NONE;
          prev_wv = wv;
        }
 
@@ -1666,6 +1780,7 @@ set_frame_menubar (f, first_time, deep_p)
          wv->name = (char *) XSTRING (string)->data;
          wv->value = 0;
          wv->enabled = 1;
+         wv->button_type = BUTTON_TYPE_NONE;
          /* This prevents lwlib from assuming this
             menu item is really supposed to be empty.  */
          /* The EMACS_INT cast avoids a warning.
@@ -1708,7 +1823,8 @@ set_frame_menubar (f, first_time, deep_p)
                                         0,
                                         popup_activate_callback,
                                         menubar_selection_callback,
-                                        popup_deactivate_callback);
+                                        popup_deactivate_callback,
+                                        menu_highlight_callback);
       f->output_data.x->menubar_widget = menubar_widget;
     }
 
@@ -1736,7 +1852,6 @@ set_frame_menubar (f, first_time, deep_p)
   }
   
   free_menubar_widget_value_tree (first_wv);
-
   update_frame_menubar (f);
 
   UNBLOCK_INPUT;
@@ -1765,7 +1880,6 @@ free_frame_menubar (f)
      FRAME_PTR f;
 {
   Widget menubar_widget;
-  int id;
 
   menubar_widget = f->output_data.x->menubar_widget;
 
@@ -1809,11 +1923,7 @@ free_frame_menubar (f)
    next_menubar_widget_id.  */
 LWLIB_ID widget_id_tick;
 
-#ifdef __STDC__
 static Lisp_Object *volatile menu_item_selection;
-#else
-static Lisp_Object *menu_item_selection;
-#endif
 
 static void
 popup_selection_callback (widget, id, client_data)
@@ -1848,7 +1958,6 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
   XButtonPressedEvent dummy;
 
   int first_pane;
-  int next_release_must_exit = 0;
 
   *error = NULL;
 
@@ -1864,6 +1973,7 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
   wv->name = "menu";
   wv->value = 0;
   wv->enabled = 1;
+  wv->button_type = BUTTON_TYPE_NONE;
   first_wv = wv;
   first_pane = 1;
  
@@ -1900,6 +2010,10 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
          char *pane_string;
          pane_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_NAME];
          prefix = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
+#ifndef HAVE_MULTILINGUAL_MENU
+         if (!NILP (pane_name) && STRING_MULTIBYTE (pane_name))
+           pane_name = string_make_unibyte (pane_name);
+#endif
          pane_string = (NILP (pane_name)
                         ? "" : (char *) XSTRING (pane_name)->data);
          /* If there is just one top-level pane, put all its items directly
@@ -1922,6 +2036,7 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
                wv->name++;
              wv->value = 0;
              wv->enabled = 1;
+             wv->button_type = BUTTON_TYPE_NONE;
              save_wv = wv;
              prev_wv = 0;
            }
@@ -1936,13 +2051,23 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
       else
        {
          /* Create a new item within current pane.  */
-         Lisp_Object item_name, enable, descrip, def;
+         Lisp_Object item_name, enable, descrip, def, type, selected, help;
          item_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_NAME];
          enable = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_ENABLE];
          descrip
            = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_EQUIV_KEY];
          def = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_DEFINITION];
-
+         type = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_TYPE];
+         selected = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_SELECTED];
+         help = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_HELP];
+
+#ifndef HAVE_MULTILINGUAL_MENU
+          if (STRINGP (item_name) && STRING_MULTIBYTE (item_name))
+            item_name = string_make_unibyte (item_name);
+          if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
+            item_name = string_make_unibyte (descrip);
+#endif
          wv = xmalloc_widget_value ();
          if (prev_wv) 
            prev_wv->next = wv;
@@ -1958,6 +2083,20 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
          wv->call_data
            = (!NILP (def) ? (void *) &XVECTOR (menu_items)->contents[i] : 0);
          wv->enabled = !NILP (enable);
+
+         if (NILP (type))
+           wv->button_type = BUTTON_TYPE_NONE;
+         else if (EQ (type, QCtoggle))
+           wv->button_type = BUTTON_TYPE_TOGGLE;
+         else if (EQ (type, QCradio))
+           wv->button_type = BUTTON_TYPE_RADIO;
+         else
+           abort ();
+
+         wv->selected = !NILP (selected);
+         if (STRINGP (help))
+           wv->help = XSTRING (help)->data;
+         
          prev_wv = wv;
 
          i += MENU_ITEMS_ITEM_LENGTH;
@@ -1977,8 +2116,13 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
       wv_sep1->name = "--";
       wv_sep1->next = wv_sep2;
 
+#ifndef HAVE_MULTILINGUAL_MENU
+      if (STRING_MULTIBYTE (title))
+       title = string_make_unibyte (title);
+#endif
       wv_title->name = (char *) XSTRING (title)->data;
       wv_title->enabled = True;
+      wv_title->button_type = BUTTON_TYPE_NONE;
       wv_title->next = wv_sep1;
       first_wv->contents = wv_title;
     }
@@ -1988,7 +2132,8 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
   menu = lw_create_widget ("popup", first_wv->name, menu_id, first_wv,
                           f->output_data.x->widget, 1, 0,
                           popup_selection_callback,
-                          popup_deactivate_callback);
+                          popup_deactivate_callback,
+                          menu_highlight_callback);
 
   /* Adjust coordinates to relative to the outer (window manager) window.  */
   {
@@ -2046,16 +2191,30 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
   /* Free the widget_value objects we used to specify the contents.  */
   free_menubar_widget_value_tree (first_wv);
 
+  /* Override any default settings with ones from the `menu' face.  */
+  x_set_menu_resources_from_menu_face (f, menu);
+
   /* No selection has been chosen yet.  */
   menu_item_selection = 0;
 
   /* Display the menu.  */
-  lw_popup_menu (menu, &dummy);
+  lw_popup_menu (menu, (XEvent *) &dummy);
   popup_activated_flag = 1;
 
   /* Process events that apply to the menu.  */
   popup_get_selection ((XEvent *) 0, FRAME_X_DISPLAY_INFO (f), menu_id);
 
+#ifdef LESSTIF_VERSION
+  /* Nov 1998: For an unknown reason a button grab remains active
+     after the popup menu has gone.  */
+  XUngrabButton (XtDisplay (f->output_data.x->widget),
+                AnyButton, AnyModifier,
+                XtWindow (f->output_data.x->widget));
+  XUngrabButton (XtDisplay (f->output_data.x->edit_widget),
+                AnyButton, AnyModifier,
+                XtWindow (f->output_data.x->edit_widget));
+#endif /* LESSTIF_VERSION */
+
   /* fp turned off the following statement and wrote a comment
      that it is unnecessary--that the menu has already disappeared.
      Nowadays the menu disappears ok, all right, but
@@ -2068,7 +2227,7 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
     {
       Lisp_Object prefix, entry;
 
-      prefix = Qnil;
+      prefix = entry = Qnil;
       i = 0;
       while (i < menu_items_used)
        {
@@ -2152,7 +2311,7 @@ xdialog_show (f, keymaps, title, error)
   Widget menu;
   char dialog_name[6];
 
-  widget_value *wv, *save_wv = 0, *first_wv = 0, *prev_wv = 0;
+  widget_value *wv, *first_wv = 0, *prev_wv = 0;
 
   /* Number of elements seen so far, before boundary.  */
   int left_count = 0;
@@ -2261,7 +2420,7 @@ xdialog_show (f, keymaps, title, error)
   dialog_id = widget_id_tick++;
   menu = lw_create_widget (first_wv->name, "dialog", dialog_id, first_wv,
                           f->output_data.x->widget, 1, 0,
-                          dialog_selection_callback, 0);
+                          dialog_selection_callback, 0, 0);
   lw_modify_all_widgets (dialog_id, first_wv->contents, True);
   /* Free the widget_value objects we used to specify the contents.  */
   free_menubar_widget_value_tree (first_wv);
@@ -2296,6 +2455,12 @@ xdialog_show (f, keymaps, title, error)
                = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
              i += MENU_ITEMS_PANE_LENGTH;
            }
+         else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
+           {
+             /* This is the boundary between left-side elts and
+                right-side elts.  */
+             ++i;
+           }
          else
            {
              entry
@@ -2317,8 +2482,52 @@ xdialog_show (f, keymaps, title, error)
 
   return Qnil;
 }
+
 #else /* not USE_X_TOOLKIT */
 
+/* The frame of the last activated non-toolkit menu bar.
+   Used to generate menu help events.  */
+
+static struct frame *menu_help_frame;
+
+
+/* Show help HELP_STRING, or clear help if HELP_STRING is null.
+
+   PANE is the pane number, and ITEM is the menu item number in
+   the menu (currently not used).
+   
+   This cannot be done with generating a HELP_EVENT because
+   XMenuActivate contains a loop that doesn't let Emacs process
+   keyboard events.  */
+
+static void
+menu_help_callback (help_string, pane, item)
+     char *help_string;
+     int pane, item;
+{
+  extern Lisp_Object Qmenu_item;
+  Lisp_Object *first_item;
+  Lisp_Object pane_name;
+  Lisp_Object menu_object;
+  first_item = XVECTOR (menu_items)->contents;
+  if (EQ (first_item[0], Qt))
+    pane_name = first_item[MENU_ITEMS_PANE_NAME];
+  else if (EQ (first_item[0], Qquote))
+    /* This shouldn't happen, see xmenu_show.  */
+    pane_name = build_string ("");
+  else
+    pane_name = first_item[MENU_ITEMS_ITEM_NAME];
+  /* (menu-item MENU-NAME PANE-NUMBER)  */
+  menu_object = Fcons (Qmenu_item,
+                      Fcons (pane_name,
+                             Fcons (make_number (pane), Qnil)));
+  show_help_echo (help_string ? build_string (help_string) : Qnil,
+                 Qnil, menu_object, make_number (item), 1);
+}
+  
+
 static Lisp_Object
 xmenu_show (f, x, y, for_click, keymaps, title, error)
      FRAME_PTR f;
@@ -2450,13 +2659,17 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
       else
        {
          /* Create a new item within current pane.  */
-         Lisp_Object item_name, enable, descrip;
+         Lisp_Object item_name, enable, descrip, help;
          unsigned char *item_data;
+         char *help_string;
 
          item_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_NAME];
          enable = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_ENABLE];
          descrip
            = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_EQUIV_KEY];
+         help = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_HELP];
+         help_string = STRINGP (help) ? XSTRING (help)->data : NULL;
+         
          if (!NILP (descrip))
            {
              int gap = maxwidth - STRING_BYTES (XSTRING (item_name));
@@ -2486,7 +2699,7 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
 
          if (XMenuAddSelection (FRAME_X_DISPLAY (f),
                                 menu, lpane, 0, item_data,
-                                !NILP (enable))
+                                !NILP (enable), help_string)
              == XM_FAILURE)
            {
              XMenuDestroy (FRAME_X_DISPLAY (f), menu);
@@ -2525,9 +2738,13 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
   XMenuSetAEQ (menu, TRUE);
   XMenuSetFreeze (menu, TRUE);
   pane = selidx = 0;
-  
+
+  /* Help display under X won't work because XMenuActivate contains
+     a loop that doesn't give Emacs a chance to process it.  */
+  menu_help_frame = f;
   status = XMenuActivate (FRAME_X_DISPLAY (f), menu, &pane, &selidx,
-                         x, y, ButtonReleaseMask, &datap);
+                         x, y, ButtonReleaseMask, &datap,
+                         menu_help_callback);
 
 
 #ifdef HAVE_X_WINDOWS
@@ -2602,6 +2819,7 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
 
 #endif /* HAVE_MENUS */
 \f
+void
 syms_of_xmenu ()
 {
   staticpro (&menu_items);