Add 2008 to copyright years.
[bpt/emacs.git] / lwlib / xlwmenu.c
index 3a1a490..2320dbc 100644 (file)
@@ -1,22 +1,24 @@
-/* Implements a lightweight menubar widget.  
-   Copyright (C) 1992 Lucid, Inc.
+/* Implements a lightweight menubar widget.
+Copyright (C) 1992 Lucid, Inc.
+Copyright (C) 1994, 1995, 1997, 1999, 2000, 2001, 2002, 2003, 2004,
+  2005, 2006, 2007, 2008  Free Software Foundation, Inc.
 
 This file is part of the Lucid Widget Library.
 
 
 This file is part of the Lucid Widget Library.
 
-The Lucid Widget Library is free software; you can redistribute it and/or 
+The Lucid Widget Library is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2, or (at your option)
 any later version.
 
 The Lucid Widget Library is distributed in the hope that it will be useful,
 modify it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2, or (at your option)
 any later version.
 
 The Lucid Widget Library is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of 
+but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with GNU Emacs; see the file COPYING.  If not, write to the
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with GNU Emacs; see the file COPYING.  If not, write to the
-Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA.  */
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA.  */
 
 /* Created by devin@lucid.com */
 
 
 /* Created by devin@lucid.com */
 
@@ -24,39 +26,52 @@ Boston, MA 02111-1307, USA.  */
 #include <config.h>
 #endif
 
 #include <config.h>
 #endif
 
+#include "lisp.h"
+
 #include <stdio.h>
 
 #include <sys/types.h>
 #include <stdio.h>
 
 #include <sys/types.h>
+#if (defined __sun) && !(defined SUNOS41)
+#define SUNOS41
+#include <X11/Xos.h>
+#undef SUNOS41
+#else
 #include <X11/Xos.h>
 #include <X11/Xos.h>
+#endif
 #include <X11/IntrinsicP.h>
 #include <X11/ObjectP.h>
 #include <X11/StringDefs.h>
 #include <X11/cursorfont.h>
 #include <X11/IntrinsicP.h>
 #include <X11/ObjectP.h>
 #include <X11/StringDefs.h>
 #include <X11/cursorfont.h>
-#include <X11/bitmaps/gray>
 #include "xlwmenuP.h"
 
 #ifdef emacs
 #include "xlwmenuP.h"
 
 #ifdef emacs
-/* Defined in xterm.c.  */
-extern int x_alloc_nearest_color_for_widget __P ((Widget, Colormap, XColor*));
-extern int x_catch_errors __P ((Display*));
-extern int x_uncatch_errors __P ((Display*, int));
-extern int x_had_errors_p __P ((Display*));
-extern int x_clear_errors __P ((Display*));
-extern unsigned long x_copy_dpy_color __P ((Display *, Colormap,
-                                           unsigned long));
-
-/* Defined in xfaces.c.  */
-extern void x_free_dpy_colors __P ((Display *, Screen *, Colormap,
-                                   unsigned long *pixels, int npixels));
-#endif
+
+/* Defined in xfns.c.  When config.h defines `static' as empty, we get
+   redefinition errors when gray_bitmap is included more than once, so
+   we're referring to the one include in xfns.c here.  */
+
+extern int gray_bitmap_width;
+extern int gray_bitmap_height;
+extern char *gray_bitmap_bits;
+
+#include "xterm.h"
+
+#else /* not emacs */
+
+#include <X11/bitmaps/gray>
+#define gray_bitmap_width      gray_width
+#define gray_bitmap_height     gray_height
+#define gray_bitmap_bits       gray_bits
+
+#endif /* not emacs */
 
 static int pointer_grabbed;
 static XEvent menu_post_event;
 
 XFontStruct *xlwmenu_default_font;
 
 
 static int pointer_grabbed;
 static XEvent menu_post_event;
 
 XFontStruct *xlwmenu_default_font;
 
-static char 
-xlwMenuTranslations [] = 
+static char
+xlwMenuTranslations [] =
 "<BtnDown>:      start()\n\
 <Motion>:        drag()\n\
 <BtnUp>:         select()\n\
 "<BtnDown>:      start()\n\
 <Motion>:        drag()\n\
 <BtnUp>:         select()\n\
@@ -88,32 +103,50 @@ xlwMenuTranslations [] =
 <KeyUp>Alt_R:     nothing()\n\
 <KeyUp>Caps_Lock: nothing()\n\
 <KeyUp>Shift_Lock:nothing()\n\
 <KeyUp>Alt_R:     nothing()\n\
 <KeyUp>Caps_Lock: nothing()\n\
 <KeyUp>Shift_Lock:nothing()\n\
+<Key>Return:      select()\n\
+<Key>Down:        down()\n\
+<Key>Up:          up()\n\
+<Key>Left:        left()\n\
+<Key>Right:       right()\n\
 <Key>:            key()\n\
 <KeyUp>:          key()\n\
 ";
 
 <Key>:            key()\n\
 <KeyUp>:          key()\n\
 ";
 
+/* FIXME: Space should toggle toggleable menu item but not remove the menu
+   so you can toggle the next one without entering the menu again.  */
+
+/* FIXME: Should ESC close one level of menu structure or the complete menu?  */
+
+/* FIXME: F10 should enter the menu, the first one in the menu-bar.  */
+
 #define offset(field) XtOffset(XlwMenuWidget, field)
 #define offset(field) XtOffset(XlwMenuWidget, field)
-static XtResource 
+static XtResource
 xlwMenuResources[] =
 xlwMenuResources[] =
-{ 
+{
+#ifdef HAVE_X_I18N
+  {XtNfontSet,  XtCFontSet, XtRFontSet, sizeof(XFontSet),
+     offset(menu.fontSet), XtRFontSet, NULL},
+#endif
   {XtNfont,  XtCFont, XtRFontStruct, sizeof(XFontStruct *),
   {XtNfont,  XtCFont, XtRFontStruct, sizeof(XFontStruct *),
-     offset(menu.font),XtRString, "XtDefaultFont"},
+     offset(menu.font), XtRString, "XtDefaultFont"},
   {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
      offset(menu.foreground), XtRString, "XtDefaultForeground"},
   {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
      offset(menu.foreground), XtRString, "XtDefaultForeground"},
+  {XtNdisabledForeground, XtCDisabledForeground, XtRPixel, sizeof(Pixel),
+   offset(menu.disabled_foreground), XtRString, (XtPointer)NULL},
   {XtNbuttonForeground, XtCButtonForeground, XtRPixel, sizeof(Pixel),
      offset(menu.button_foreground), XtRString, "XtDefaultForeground"},
   {XtNmargin, XtCMargin, XtRDimension,  sizeof(Dimension),
   {XtNbuttonForeground, XtCButtonForeground, XtRPixel, sizeof(Pixel),
      offset(menu.button_foreground), XtRString, "XtDefaultForeground"},
   {XtNmargin, XtCMargin, XtRDimension,  sizeof(Dimension),
-     offset(menu.margin), XtRImmediate, (XtPointer) 4},
+     offset(menu.margin), XtRImmediate, (XtPointer)1},
   {XtNhorizontalSpacing, XtCMargin, XtRDimension,  sizeof(Dimension),
      offset(menu.horizontal_spacing), XtRImmediate, (XtPointer)3},
   {XtNverticalSpacing, XtCMargin, XtRDimension,  sizeof(Dimension),
   {XtNhorizontalSpacing, XtCMargin, XtRDimension,  sizeof(Dimension),
      offset(menu.horizontal_spacing), XtRImmediate, (XtPointer)3},
   {XtNverticalSpacing, XtCMargin, XtRDimension,  sizeof(Dimension),
-     offset(menu.vertical_spacing), XtRImmediate, (XtPointer)1},
+     offset(menu.vertical_spacing), XtRImmediate, (XtPointer)2},
   {XtNarrowSpacing, XtCMargin, XtRDimension,  sizeof(Dimension),
      offset(menu.arrow_spacing), XtRImmediate, (XtPointer)10},
 
   {XmNshadowThickness, XmCShadowThickness, XtRDimension,
      sizeof (Dimension), offset (menu.shadow_thickness),
   {XtNarrowSpacing, XtCMargin, XtRDimension,  sizeof(Dimension),
      offset(menu.arrow_spacing), XtRImmediate, (XtPointer)10},
 
   {XmNshadowThickness, XmCShadowThickness, XtRDimension,
      sizeof (Dimension), offset (menu.shadow_thickness),
-     XtRImmediate, (XtPointer) 2},
+     XtRImmediate, (XtPointer)1},
   {XmNtopShadowColor, XmCTopShadowColor, XtRPixel, sizeof (Pixel),
      offset (menu.top_shadow_color), XtRImmediate, (XtPointer)-1},
   {XmNbottomShadowColor, XmCBottomShadowColor, XtRPixel, sizeof (Pixel),
   {XmNtopShadowColor, XmCTopShadowColor, XtRPixel, sizeof (Pixel),
      offset (menu.top_shadow_color), XtRImmediate, (XtPointer)-1},
   {XmNbottomShadowColor, XmCBottomShadowColor, XtRPixel, sizeof (Pixel),
@@ -123,12 +156,16 @@ xlwMenuResources[] =
   {XmNbottomShadowPixmap, XmCBottomShadowPixmap, XtRPixmap, sizeof (Pixmap),
      offset (menu.bottom_shadow_pixmap), XtRImmediate, (XtPointer)None},
 
   {XmNbottomShadowPixmap, XmCBottomShadowPixmap, XtRPixmap, sizeof (Pixmap),
      offset (menu.bottom_shadow_pixmap), XtRImmediate, (XtPointer)None},
 
-  {XtNopen, XtCCallback, XtRCallback, sizeof(XtPointer), 
+  {XtNopen, XtCCallback, XtRCallback, sizeof(XtPointer),
      offset(menu.open), XtRCallback, (XtPointer)NULL},
      offset(menu.open), XtRCallback, (XtPointer)NULL},
-  {XtNselect, XtCCallback, XtRCallback, sizeof(XtPointer), 
+  {XtNselect, XtCCallback, XtRCallback, sizeof(XtPointer),
      offset(menu.select), XtRCallback, (XtPointer)NULL},
      offset(menu.select), XtRCallback, (XtPointer)NULL},
-  {XtNhighlightCallback, XtCCallback, XtRCallback, sizeof(XtPointer), 
+  {XtNhighlightCallback, XtCCallback, XtRCallback, sizeof(XtPointer),
      offset(menu.highlight), XtRCallback, (XtPointer)NULL},
      offset(menu.highlight), XtRCallback, (XtPointer)NULL},
+  {XtNenterCallback, XtCCallback, XtRCallback, sizeof(XtPointer),
+     offset(menu.enter), XtRCallback, (XtPointer)NULL},
+  {XtNleaveCallback, XtCCallback, XtRCallback, sizeof(XtPointer),
+     offset(menu.leave), XtRCallback, (XtPointer)NULL},
   {XtNmenu, XtCMenu, XtRPointer, sizeof(XtPointer),
      offset(menu.contents), XtRImmediate, (XtPointer)NULL},
   {XtNcursor, XtCCursor, XtRCursor, sizeof(Cursor),
   {XtNmenu, XtCMenu, XtRPointer, sizeof(XtPointer),
      offset(menu.contents), XtRImmediate, (XtPointer)NULL},
   {XtNcursor, XtCCursor, XtRCursor, sizeof(Cursor),
@@ -148,18 +185,29 @@ static void XlwMenuDestroy();
 static void XlwMenuClassInitialize();
 static void Start();
 static void Drag();
 static void XlwMenuClassInitialize();
 static void Start();
 static void Drag();
+static void Down();
+static void Up();
+static void Left();
+static void Right();
 static void Select();
 static void Key();
 static void Nothing();
 static void Select();
 static void Key();
 static void Nothing();
-static int separator_height ();
+static int separator_height __P ((enum menu_separator));
+static void pop_up_menu __P ((XlwMenuWidget, XButtonPressedEvent *));
 
 
-static XtActionsRec 
+
+static XtActionsRec
 xlwMenuActionsList [] =
 {
   {"start",            Start},
   {"drag",             Drag},
 xlwMenuActionsList [] =
 {
   {"start",            Start},
   {"drag",             Drag},
+  {"down",             Down},
+  {"up",               Up},
+  {"left",             Left},
+  {"right",            Right},
   {"select",           Select},
   {"key",              Key},
   {"select",           Select},
   {"key",              Key},
+  {"MenuGadgetEscape",  Key},   /* Compatibility with Lesstif/Motif.  */
   {"nothing",          Nothing},
 };
 
   {"nothing",          Nothing},
 };
 
@@ -168,7 +216,7 @@ xlwMenuActionsList [] =
 XlwMenuClassRec xlwMenuClassRec =
 {
   {  /* CoreClass fields initialization */
 XlwMenuClassRec xlwMenuClassRec =
 {
   {  /* CoreClass fields initialization */
-    (WidgetClass) SuperClass,          /* superclass             */    
+    (WidgetClass) SuperClass,          /* superclass             */
     "XlwMenu",                         /* class_name             */
     sizeof(XlwMenuRec),                        /* size                   */
     XlwMenuClassInitialize,            /* class_initialize       */
     "XlwMenu",                         /* class_name             */
     sizeof(XlwMenuRec),                        /* size                   */
     XlwMenuClassInitialize,            /* class_initialize       */
@@ -183,7 +231,7 @@ XlwMenuClassRec xlwMenuClassRec =
     XtNumber(xlwMenuResources),                /* resource_count         */
     NULLQUARK,                         /* xrm_class              */
     TRUE,                              /* compress_motion        */
     XtNumber(xlwMenuResources),                /* resource_count         */
     NULLQUARK,                         /* xrm_class              */
     TRUE,                              /* compress_motion        */
-    TRUE,                              /* compress_exposure      */
+    XtExposeCompressMaximal,           /* compress_exposure      */
     TRUE,                              /* compress_enterleave    */
     FALSE,                             /* visible_interest       */
     XlwMenuDestroy,                    /* destroy                */
     TRUE,                              /* compress_enterleave    */
     FALSE,                             /* visible_interest       */
     XlwMenuDestroy,                    /* destroy                */
@@ -210,10 +258,24 @@ WidgetClass xlwMenuWidgetClass = (WidgetClass) &xlwMenuClassRec;
 
 int submenu_destroyed;
 
 
 int submenu_destroyed;
 
+/* For debug, if installation-directory is non-nil this is not an installed
+   Emacs.   In that case we do not grab the keyboard to make it easier to
+   debug. */
+#define GRAB_KEYBOARD  (EQ (Vinstallation_directory, Qnil))
+
 static int next_release_must_exit;
 
 \f/* Utilities */
 
 static int next_release_must_exit;
 
 \f/* Utilities */
 
+/* Ungrab pointer and keyboard */
+static void
+ungrab_all (w, ungrabtime)
+     Widget w;
+     Time ungrabtime;
+{
+  XtUngrabPointer (w, ungrabtime);
+  if (GRAB_KEYBOARD) XtUngrabKeyboard (w, ungrabtime);
+}
 
 /* Like abort, but remove grabs from widget W before.  */
 
 
 /* Like abort, but remove grabs from widget W before.  */
 
@@ -223,7 +285,7 @@ abort_gracefully (w)
 {
   if (XtIsShell (XtParent (w)))
     XtRemoveGrab (w);
 {
   if (XtIsShell (XtParent (w)))
     XtRemoveGrab (w);
-  XtUngrabPointer (w, CurrentTime);
+  ungrab_all (w, CurrentTime);
   abort ();
 }
 
   abort ();
 }
 
@@ -253,7 +315,7 @@ static void
 pop_new_stack_if_no_contents (mw)
      XlwMenuWidget mw;
 {
 pop_new_stack_if_no_contents (mw)
      XlwMenuWidget mw;
 {
-  if (mw->menu.new_depth)
+  if (mw->menu.new_depth > 1)
     {
       if (!mw->menu.new_stack [mw->menu.new_depth - 1]->contents)
        mw->menu.new_depth -= 1;
     {
       if (!mw->menu.new_stack [mw->menu.new_depth - 1]->contents)
        mw->menu.new_depth -= 1;
@@ -282,23 +344,47 @@ make_old_stack_space (mw, n)
 }
 
 \f/* Size code */
 }
 
 \f/* Size code */
-int
+static int
 string_width (mw, s)
      XlwMenuWidget mw;
      char *s;
 {
   XCharStruct xcs;
   int drop;
 string_width (mw, s)
      XlwMenuWidget mw;
      char *s;
 {
   XCharStruct xcs;
   int drop;
-  
+#ifdef HAVE_X_I18N
+  XRectangle ink, logical;
+  if (mw->menu.fontSet)
+    {
+      XmbTextExtents (mw->menu.fontSet, s, strlen (s), &ink, &logical);
+      return logical.width;
+    }
+#endif
+
   XTextExtents (mw->menu.font, s, strlen (s), &drop, &drop, &drop, &xcs);
   return xcs.width;
   XTextExtents (mw->menu.font, s, strlen (s), &drop, &drop, &drop, &xcs);
   return xcs.width;
+
 }
 
 }
 
+#ifdef HAVE_X_I18N
+#define MENU_FONT_HEIGHT(mw) \
+  ((mw)->menu.fontSet != NULL \
+   ? (mw)->menu.font_extents->max_logical_extent.height   \
+   : (mw)->menu.font->ascent + (mw)->menu.font->descent)
+#define MENU_FONT_ASCENT(mw) \
+  ((mw)->menu.fontSet != NULL \
+   ? - (mw)->menu.font_extents->max_logical_extent.y \
+   : (mw)->menu.font->ascent)
+#else
+#define MENU_FONT_HEIGHT(mw) \
+  ((mw)->menu.font->ascent + (mw)->menu.font->descent)
+#define MENU_FONT_ASCENT(mw) ((mw)->menu.font->ascent)
+#endif
+
 static int
 arrow_width (mw)
      XlwMenuWidget mw;
 {
 static int
 arrow_width (mw)
      XlwMenuWidget mw;
 {
-  return (mw->menu.font->ascent * 3/4) | 1;
+  return (MENU_FONT_ASCENT (mw) * 3/4) | 1;
 }
 
 /* Return the width of toggle buttons of widget MW.  */
 }
 
 /* Return the width of toggle buttons of widget MW.  */
@@ -307,7 +393,7 @@ static int
 toggle_button_width (mw)
      XlwMenuWidget mw;
 {
 toggle_button_width (mw)
      XlwMenuWidget mw;
 {
-  return ((mw->menu.font->ascent + mw->menu.font->descent) * 2 / 3) | 1;
+  return (MENU_FONT_HEIGHT (mw) * 2 / 3) | 1;
 }
 
 
 }
 
 
@@ -323,7 +409,7 @@ radio_button_width (mw)
 
 static XtResource
 nameResource[] =
 
 static XtResource
 nameResource[] =
-{ 
+{
   {"labelString",  "LabelString", XtRString, sizeof(String),
      0, XtRImmediate, 0},
 };
   {"labelString",  "LabelString", XtRString, sizeof(String),
      0, XtRImmediate, 0},
 };
@@ -378,7 +464,7 @@ size_menu_item (mw, val, horizontal_p, label_width, rest_width, button_width,
      int* height;
 {
   enum menu_separator separator;
      int* height;
 {
   enum menu_separator separator;
-  
+
   if (lw_separator_p (val->name, &separator, 0))
     {
       *height = separator_height (separator);
   if (lw_separator_p (val->name, &separator, 0))
     {
       *height = separator_height (separator);
@@ -388,14 +474,13 @@ size_menu_item (mw, val, horizontal_p, label_width, rest_width, button_width,
     }
   else
     {
     }
   else
     {
-      *height =
-       mw->menu.font->ascent + mw->menu.font->descent
-         + 2 * mw->menu.vertical_spacing + 2 * mw->menu.shadow_thickness;
-      
+      *height = MENU_FONT_HEIGHT (mw)
+       + 2 * mw->menu.vertical_spacing + 2 * mw->menu.shadow_thickness;
+
       *label_width =
        string_width (mw, resource_widget_value (mw, val))
          + mw->menu.horizontal_spacing + mw->menu.shadow_thickness;
       *label_width =
        string_width (mw, resource_widget_value (mw, val))
          + mw->menu.horizontal_spacing + mw->menu.shadow_thickness;
-      
+
       *rest_width =  mw->menu.horizontal_spacing + mw->menu.shadow_thickness;
       if (!horizontal_p)
        {
       *rest_width =  mw->menu.horizontal_spacing + mw->menu.shadow_thickness;
       if (!horizontal_p)
        {
@@ -435,7 +520,7 @@ size_menu (mw, level)
   if (level >= mw->menu.old_depth)
     abort_gracefully ((Widget) mw);
 
   if (level >= mw->menu.old_depth)
     abort_gracefully ((Widget) mw);
 
-  ws = &mw->menu.windows [level];  
+  ws = &mw->menu.windows [level];
   ws->width = 0;
   ws->height = 0;
   ws->label_width = 0;
   ws->width = 0;
   ws->height = 0;
   ws->label_width = 0;
@@ -462,7 +547,7 @@ size_menu (mw, level)
          ws->height += height;
        }
     }
          ws->height += height;
        }
     }
-  
+
   if (horizontal_p)
     ws->label_width = ws->button_width = 0;
   else
   if (horizontal_p)
     ws->label_width = ws->button_width = 0;
   else
@@ -504,9 +589,9 @@ draw_arrow (mw, window, gc, x, y, width, down_p)
      factor = (1 + sin (alpha)) / cos (alpha) */
   double factor = 1.62;
   int thickness2 = thickness * factor;
      factor = (1 + sin (alpha)) / cos (alpha) */
   double factor = 1.62;
   int thickness2 = thickness * factor;
-  
-  y += (mw->menu.font->ascent + mw->menu.font->descent - height) / 2;
-  
+
+  y += (MENU_FONT_HEIGHT (mw) - height) / 2;
+
   if (down_p)
     {
       GC temp;
   if (down_p)
     {
       GC temp;
@@ -524,7 +609,7 @@ draw_arrow (mw, window, gc, x, y, width, down_p)
   pt[3].x = x;
   pt[3].y = y;
   XFillPolygon (dpy, window, top_gc, pt, 4, Convex, CoordModeOrigin);
   pt[3].x = x;
   pt[3].y = y;
   XFillPolygon (dpy, window, top_gc, pt, 4, Convex, CoordModeOrigin);
-  
+
   pt[0].x = x;
   pt[0].y = y;
   pt[1].x = x + thickness;
   pt[0].x = x;
   pt[0].y = y;
   pt[1].x = x + thickness;
@@ -534,7 +619,7 @@ draw_arrow (mw, window, gc, x, y, width, down_p)
   pt[3].x = x + width;
   pt[3].y = y + height / 2;
   XFillPolygon (dpy, window, top_gc, pt, 4, Convex, CoordModeOrigin);
   pt[3].x = x + width;
   pt[3].y = y + height / 2;
   XFillPolygon (dpy, window, top_gc, pt, 4, Convex, CoordModeOrigin);
-  
+
   pt[0].x = x;
   pt[0].y = y + height;
   pt[1].x = x + thickness;
   pt[0].x = x;
   pt[0].y = y + height;
   pt[1].x = x + thickness;
@@ -572,7 +657,7 @@ draw_shadow_rectangle (mw, window, x, y, width, height, erase_p, down_p)
       top_gc = bottom_gc;
       bottom_gc = temp;
     }
       top_gc = bottom_gc;
       bottom_gc = temp;
     }
-  
+
   points [0].x = x;
   points [0].y = y;
   points [1].x = x + width;
   points [0].x = x;
   points [0].y = y;
   points [1].x = x + width;
@@ -691,7 +776,7 @@ draw_toggle (mw, window, x, y, selected_p)
   width = toggle_button_width (mw);
   height = width;
   x += mw->menu.horizontal_spacing;
   width = toggle_button_width (mw);
   height = width;
   x += mw->menu.horizontal_spacing;
-  y += (mw->menu.font->ascent - height) / 2;
+  y += (MENU_FONT_ASCENT (mw) - height) / 2;
   draw_shadow_rectangle (mw, window, x, y, width, height, False, selected_p);
 }
 
   draw_shadow_rectangle (mw, window, x, y, width, height, False, selected_p);
 }
 
@@ -711,7 +796,7 @@ draw_radio (mw, window, x, y, selected_p)
   width = radio_button_width (mw);
   height = width;
   x += mw->menu.horizontal_spacing;
   width = radio_button_width (mw);
   height = width;
   x += mw->menu.horizontal_spacing;
-  y += (mw->menu.font->ascent - height) / 2;
+  y += (MENU_FONT_ASCENT (mw) - height) / 2;
   draw_shadow_rhombus (mw, window, x, y, width, height, False, selected_p);
 }
 
   draw_shadow_rhombus (mw, window, x, y, width, height, False, selected_p);
 }
 
@@ -734,17 +819,17 @@ draw_separator (mw, window, x, y, width, type)
     {
     case SEPARATOR_NO_LINE:
       break;
     {
     case SEPARATOR_NO_LINE:
       break;
-      
+
     case SEPARATOR_SINGLE_LINE:
       XDrawLine (dpy, window, mw->menu.foreground_gc,
                 x, y, x + width, y);
       break;
     case SEPARATOR_SINGLE_LINE:
       XDrawLine (dpy, window, mw->menu.foreground_gc,
                 x, y, x + width, y);
       break;
-      
+
     case SEPARATOR_DOUBLE_LINE:
       draw_separator (mw, window, x, y, width, SEPARATOR_SINGLE_LINE);
       draw_separator (mw, window, x, y + 2, width, SEPARATOR_SINGLE_LINE);
       break;
     case SEPARATOR_DOUBLE_LINE:
       draw_separator (mw, window, x, y, width, SEPARATOR_SINGLE_LINE);
       draw_separator (mw, window, x, y + 2, width, SEPARATOR_SINGLE_LINE);
       break;
-      
+
     case SEPARATOR_SINGLE_DASHED_LINE:
       xgcv.line_style = LineOnOffDash;
       XChangeGC (dpy, mw->menu.foreground_gc, GCLineStyle, &xgcv);
     case SEPARATOR_SINGLE_DASHED_LINE:
       xgcv.line_style = LineOnOffDash;
       XChangeGC (dpy, mw->menu.foreground_gc, GCLineStyle, &xgcv);
@@ -753,14 +838,14 @@ draw_separator (mw, window, x, y, width, type)
       xgcv.line_style = LineSolid;
       XChangeGC (dpy, mw->menu.foreground_gc, GCLineStyle, &xgcv);
       break;
       xgcv.line_style = LineSolid;
       XChangeGC (dpy, mw->menu.foreground_gc, GCLineStyle, &xgcv);
       break;
-      
+
     case SEPARATOR_DOUBLE_DASHED_LINE:
       draw_separator (mw, window, x, y, width,
                      SEPARATOR_SINGLE_DASHED_LINE);
       draw_separator (mw, window, x, y + 2, width,
                      SEPARATOR_SINGLE_DASHED_LINE);
       break;
     case SEPARATOR_DOUBLE_DASHED_LINE:
       draw_separator (mw, window, x, y, width,
                      SEPARATOR_SINGLE_DASHED_LINE);
       draw_separator (mw, window, x, y + 2, width,
                      SEPARATOR_SINGLE_DASHED_LINE);
       break;
-      
+
     case SEPARATOR_SHADOW_ETCHED_IN:
       XDrawLine (dpy, window, mw->menu.shadow_bottom_gc,
                 x, y, x + width, y);
     case SEPARATOR_SHADOW_ETCHED_IN:
       XDrawLine (dpy, window, mw->menu.shadow_bottom_gc,
                 x, y, x + width, y);
@@ -774,22 +859,22 @@ draw_separator (mw, window, x, y, width, type)
       XDrawLine (dpy, window, mw->menu.shadow_bottom_gc,
                 x, y + 1, x + width, y + 1);
       break;
       XDrawLine (dpy, window, mw->menu.shadow_bottom_gc,
                 x, y + 1, x + width, y + 1);
       break;
-      
+
     case SEPARATOR_SHADOW_ETCHED_IN_DASH:
       xgcv.line_style = LineOnOffDash;
       XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
       XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
     case SEPARATOR_SHADOW_ETCHED_IN_DASH:
       xgcv.line_style = LineOnOffDash;
       XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
       XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
-      draw_separator (mw, window, x, y, SEPARATOR_SHADOW_ETCHED_IN);
+      draw_separator (mw, window, x, y, width, SEPARATOR_SHADOW_ETCHED_IN);
       xgcv.line_style = LineSolid;
       XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
       XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
       break;
       xgcv.line_style = LineSolid;
       XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
       XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
       break;
-      
+
     case SEPARATOR_SHADOW_ETCHED_OUT_DASH:
       xgcv.line_style = LineOnOffDash;
       XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
       XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
     case SEPARATOR_SHADOW_ETCHED_OUT_DASH:
       xgcv.line_style = LineOnOffDash;
       XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
       XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
-      draw_separator (mw, window, x, y, SEPARATOR_SHADOW_ETCHED_OUT);
+      draw_separator (mw, window, x, y, width, SEPARATOR_SHADOW_ETCHED_OUT);
       xgcv.line_style = LineSolid;
       XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
       XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
       xgcv.line_style = LineSolid;
       XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
       XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
@@ -799,14 +884,14 @@ draw_separator (mw, window, x, y, width, type)
       draw_separator (mw, window, x, y, width, SEPARATOR_SHADOW_ETCHED_IN);
       draw_separator (mw, window, x, y + 3, width, SEPARATOR_SHADOW_ETCHED_IN);
       break;
       draw_separator (mw, window, x, y, width, SEPARATOR_SHADOW_ETCHED_IN);
       draw_separator (mw, window, x, y + 3, width, SEPARATOR_SHADOW_ETCHED_IN);
       break;
-      
+
     case SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT:
       draw_separator (mw, window, x, y, width,
                      SEPARATOR_SHADOW_ETCHED_OUT);
       draw_separator (mw, window, x, y + 3, width,
                      SEPARATOR_SHADOW_ETCHED_OUT);
       break;
     case SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT:
       draw_separator (mw, window, x, y, width,
                      SEPARATOR_SHADOW_ETCHED_OUT);
       draw_separator (mw, window, x, y + 3, width,
                      SEPARATOR_SHADOW_ETCHED_OUT);
       break;
-      
+
     case SEPARATOR_SHADOW_DOUBLE_ETCHED_IN_DASH:
       xgcv.line_style = LineOnOffDash;
       XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
     case SEPARATOR_SHADOW_DOUBLE_ETCHED_IN_DASH:
       xgcv.line_style = LineOnOffDash;
       XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
@@ -845,27 +930,27 @@ separator_height (separator)
     {
     case SEPARATOR_NO_LINE:
       return 2;
     {
     case SEPARATOR_NO_LINE:
       return 2;
-      
+
     case SEPARATOR_SINGLE_LINE:
     case SEPARATOR_SINGLE_DASHED_LINE:
       return 1;
     case SEPARATOR_SINGLE_LINE:
     case SEPARATOR_SINGLE_DASHED_LINE:
       return 1;
-      
+
     case SEPARATOR_DOUBLE_LINE:
     case SEPARATOR_DOUBLE_DASHED_LINE:
       return 3;
     case SEPARATOR_DOUBLE_LINE:
     case SEPARATOR_DOUBLE_DASHED_LINE:
       return 3;
-      
+
     case SEPARATOR_SHADOW_ETCHED_IN:
     case SEPARATOR_SHADOW_ETCHED_OUT:
     case SEPARATOR_SHADOW_ETCHED_IN_DASH:
     case SEPARATOR_SHADOW_ETCHED_OUT_DASH:
       return 2;
     case SEPARATOR_SHADOW_ETCHED_IN:
     case SEPARATOR_SHADOW_ETCHED_OUT:
     case SEPARATOR_SHADOW_ETCHED_IN_DASH:
     case SEPARATOR_SHADOW_ETCHED_OUT_DASH:
       return 2;
-      
+
     case SEPARATOR_SHADOW_DOUBLE_ETCHED_IN:
     case SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT:
     case SEPARATOR_SHADOW_DOUBLE_ETCHED_IN_DASH:
     case SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT_DASH:
       return 5;
     case SEPARATOR_SHADOW_DOUBLE_ETCHED_IN:
     case SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT:
     case SEPARATOR_SHADOW_DOUBLE_ETCHED_IN_DASH:
     case SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT_DASH:
       return 5;
-      
+
     default:
       abort ();
     }
     default:
       abort ();
     }
@@ -888,8 +973,8 @@ display_menu_item (mw, val, ws, where, highlighted_p, horizontal_p,
 {
   GC deco_gc;
   GC text_gc;
 {
   GC deco_gc;
   GC text_gc;
-  int font_ascent = mw->menu.font->ascent;
-  int font_descent = mw->menu.font->descent;
+  int font_height = MENU_FONT_HEIGHT (mw);
+  int font_ascent = MENU_FONT_ASCENT (mw);
   int shadow = mw->menu.shadow_thickness;
   int margin = mw->menu.margin;
   int h_spacing = mw->menu.horizontal_spacing;
   int shadow = mw->menu.shadow_thickness;
   int margin = mw->menu.margin;
   int h_spacing = mw->menu.horizontal_spacing;
@@ -936,14 +1021,14 @@ display_menu_item (mw, val, ws, where, highlighted_p, horizontal_p,
       if (val->enabled)
        text_gc = mw->menu.foreground_gc;
       else
       if (val->enabled)
        text_gc = mw->menu.foreground_gc;
       else
-       text_gc = mw->menu.inactive_gc;
+       text_gc = mw->menu.disabled_gc;
       deco_gc = mw->menu.foreground_gc;
 
       if (separator_p)
        {
          draw_separator (mw, ws->window, x, y, width, separator);
        }
       deco_gc = mw->menu.foreground_gc;
 
       if (separator_p)
        {
          draw_separator (mw, ws->window, x, y, width, separator);
        }
-      else 
+      else
        {
          int x_offset = x + h_spacing + shadow;
          char* display_string = resource_widget_value (mw, val);
        {
          int x_offset = x + h_spacing + shadow;
          char* display_string = resource_widget_value (mw, val);
@@ -960,12 +1045,21 @@ display_menu_item (mw, val, ws, where, highlighted_p, horizontal_p,
            }
          else if (!horizontal_p && ws->button_width)
            x_offset += ws->button_width;
            }
          else if (!horizontal_p && ws->button_width)
            x_offset += ws->button_width;
-         
-         
-          XDrawString (XtDisplay (mw), ws->window, text_gc, x_offset,
+
+
+#ifdef HAVE_X_I18N
+          if (mw->menu.fontSet)
+            XmbDrawString (XtDisplay (mw), ws->window, mw->menu.fontSet,
+                           text_gc, x_offset,
+                           y + v_spacing + shadow + font_ascent,
+                           display_string, strlen (display_string));
+          else
+#endif
+          XDrawString (XtDisplay (mw), ws->window,
+                      text_gc, x_offset,
                       y + v_spacing + shadow + font_ascent,
                       display_string, strlen (display_string));
                       y + v_spacing + shadow + font_ascent,
                       display_string, strlen (display_string));
-         
+
          if (!horizontal_p)
            {
              if (val->button_type == BUTTON_TYPE_TOGGLE)
          if (!horizontal_p)
            {
              if (val->button_type == BUTTON_TYPE_TOGGLE)
@@ -974,20 +1068,31 @@ display_menu_item (mw, val, ws, where, highlighted_p, horizontal_p,
              else if (val->button_type == BUTTON_TYPE_RADIO)
                draw_radio (mw, ws->window, x, y + v_spacing + shadow,
                            val->selected);
              else if (val->button_type == BUTTON_TYPE_RADIO)
                draw_radio (mw, ws->window, x, y + v_spacing + shadow,
                            val->selected);
-             
+
              if (val->contents)
                {
                  int a_w = arrow_width (mw);
                  draw_arrow (mw, ws->window, deco_gc,
                              x + width - a_w
              if (val->contents)
                {
                  int a_w = arrow_width (mw);
                  draw_arrow (mw, ws->window, deco_gc,
                              x + width - a_w
-                             - mw->menu.horizontal_spacing 
+                             - mw->menu.horizontal_spacing
                              - mw->menu.shadow_thickness,
                              y + v_spacing + shadow, a_w,
                              highlighted_p);
                }
              else if (val->key)
                {
                              - mw->menu.shadow_thickness,
                              y + v_spacing + shadow, a_w,
                              highlighted_p);
                }
              else if (val->key)
                {
-                 XDrawString (XtDisplay (mw), ws->window, text_gc,
+#ifdef HAVE_X_I18N
+                  if (mw->menu.fontSet)
+                    XmbDrawString (XtDisplay (mw), ws->window,
+                                   mw->menu.fontSet,
+                                   text_gc,
+                                   x + label_width + mw->menu.arrow_spacing,
+                                   y + v_spacing + shadow + font_ascent,
+                                   val->key, strlen (val->key));
+                  else
+#endif
+                 XDrawString (XtDisplay (mw), ws->window,
+                              text_gc,
                               x + label_width + mw->menu.arrow_spacing,
                               y + v_spacing + shadow + font_ascent,
                               val->key, strlen (val->key));
                               x + label_width + mw->menu.arrow_spacing,
                               y + v_spacing + shadow + font_ascent,
                               val->key, strlen (val->key));
@@ -995,11 +1100,11 @@ display_menu_item (mw, val, ws, where, highlighted_p, horizontal_p,
            }
          else
            {
            }
          else
            {
-             XDrawRectangle (XtDisplay (mw), ws->window, 
+             XDrawRectangle (XtDisplay (mw), ws->window,
                              mw->menu.background_gc,
                              x + shadow, y + shadow,
                              label_width + h_spacing - 1,
                              mw->menu.background_gc,
                              x + shadow, y + shadow,
                              label_width + h_spacing - 1,
-                             font_ascent + font_descent + 2 * v_spacing - 1);
+                             font_height + 2 * v_spacing - 1);
              draw_shadow_rectangle (mw, ws->window, x, y, width, height,
                                     True, False);
            }
              draw_shadow_rectangle (mw, ws->window, x, y, width, height,
                                     True, False);
            }
@@ -1009,7 +1114,7 @@ display_menu_item (mw, val, ws, where, highlighted_p, horizontal_p,
                                   False);
        }
     }
                                   False);
        }
     }
-  
+
   where->x += width;
   where->y += height;
 }
   where->x += width;
   where->y += height;
 }
@@ -1043,7 +1148,7 @@ display_menu (mw, level, just_compute_p, highlighted_pos, hit, hit_return,
 
   if (level < mw->menu.old_depth - 1)
     following_item = mw->menu.old_stack [level + 1];
 
   if (level < mw->menu.old_depth - 1)
     following_item = mw->menu.old_stack [level + 1];
-  else 
+  else
     following_item = NULL;
 
   if (hit)
     following_item = NULL;
 
   if (hit)
@@ -1063,7 +1168,7 @@ display_menu (mw, level, just_compute_p, highlighted_pos, hit, hit_return,
          else
            highlighted_pos->y = where.y;
        }
          else
            highlighted_pos->y = where.y;
        }
-      
+
       just_compute_this_one_p =
        just_compute_p || ((this || that) && val != this &&  val != that);
 
       just_compute_this_one_p =
        just_compute_p || ((this || that) && val != this &&  val != that);
 
@@ -1086,8 +1191,17 @@ display_menu (mw, level, just_compute_p, highlighted_pos, hit, hit_return,
        {
          if (val->enabled)
            *hit_return = val;
        {
          if (val->enabled)
            *hit_return = val;
-         else
-           no_return = 1;
+         else 
+            no_return = 1;
+          if (mw->menu.inside_entry != val) 
+            {
+              if (mw->menu.inside_entry)
+                XtCallCallbackList ((Widget)mw, mw->menu.leave,
+                                    (XtPointer) mw->menu.inside_entry);
+              mw->menu.inside_entry = val;
+              XtCallCallbackList ((Widget)mw, mw->menu.enter,
+                                  (XtPointer) mw->menu.inside_entry);
+            }
        }
 
       if (horizontal_p)
        }
 
       if (horizontal_p)
@@ -1095,7 +1209,7 @@ display_menu (mw, level, just_compute_p, highlighted_pos, hit, hit_return,
       else
        where.x = 0;
     }
       else
        where.x = 0;
     }
-  
+
   if (!just_compute_p)
     draw_shadow_rectangle (mw, ws->window, 0, 0, ws->width, ws->height,
                           False, False);
   if (!just_compute_p)
     draw_shadow_rectangle (mw, ws->window, 0, 0, ws->width, ws->height,
                           False, False);
@@ -1109,7 +1223,7 @@ set_new_state (mw, val, level)
      int level;
 {
   int i;
      int level;
 {
   int i;
-  
+
   mw->menu.new_depth = 0;
   for (i = 0; i < level; i++)
     push_new_stack (mw, mw->menu.old_stack [i]);
   mw->menu.new_depth = 0;
   for (i = 0; i < level; i++)
     push_new_stack (mw, mw->menu.old_stack [i]);
@@ -1127,7 +1241,7 @@ make_windows_if_needed (mw, n)
   int mask;
   Window root = RootWindowOfScreen (DefaultScreenOfDisplay (XtDisplay (mw)));
   window_state* windows;
   int mask;
   Window root = RootWindowOfScreen (DefaultScreenOfDisplay (XtDisplay (mw)));
   window_state* windows;
-  
+
   if (mw->menu.windows_length >= n)
     return;
 
   if (mw->menu.windows_length >= n)
     return;
 
@@ -1141,7 +1255,7 @@ make_windows_if_needed (mw, n)
   xswa.cursor = mw->menu.cursor_shape;
   mask = CWSaveUnder | CWOverrideRedirect | CWBackPixel | CWBorderPixel
     | CWEventMask | CWCursor;
   xswa.cursor = mw->menu.cursor_shape;
   mask = CWSaveUnder | CWOverrideRedirect | CWBackPixel | CWBorderPixel
     | CWEventMask | CWCursor;
-  
+
   if (!mw->menu.windows)
     {
       mw->menu.windows =
   if (!mw->menu.windows)
     {
       mw->menu.windows =
@@ -1171,6 +1285,23 @@ make_windows_if_needed (mw, n)
   }
 }
 
   }
 }
 
+/* Value is non-zero if WINDOW is part of menu bar widget W.  */
+
+int
+xlwmenu_window_p (w, window)
+     Widget w;
+     Window window;
+{
+  XlwMenuWidget mw = (XlwMenuWidget) w;
+  int i;
+
+  for (i = 0; i < mw->menu.windows_length; ++i)
+    if (window == mw->menu.windows[i].window)
+      break;
+
+  return i < mw->menu.windows_length;
+}
+
 /* Make the window fit in the screen */
 static void
 fit_to_screen (mw, ws, previous_ws, horizontal_p)
 /* Make the window fit in the screen */
 static void
 fit_to_screen (mw, ws, previous_ws, horizontal_p)
@@ -1190,7 +1321,11 @@ fit_to_screen (mw, ws, previous_ws, horizontal_p)
   else if (ws->x + ws->width > screen_width)
     {
       if (!horizontal_p)
   else if (ws->x + ws->width > screen_width)
     {
       if (!horizontal_p)
-       ws->x = previous_ws->x - ws->width;
+       /* The addition of shadow-thickness for a sub-menu's position is
+          to reflect a similar adjustment when the menu is displayed to
+          the right of the invoking menu-item; it makes the sub-menu
+          look more `attached' to the menu-item.  */
+       ws->x = previous_ws->x - ws->width + mw->menu.shadow_thickness;
       else
        ws->x = screen_width - ws->width;
       if (ws->x < 0)
       else
        ws->x = screen_width - ws->width;
       if (ws->x < 0)
@@ -1220,7 +1355,7 @@ fit_to_screen (mw, ws, previous_ws, horizontal_p)
        ws->y = previous_ws->y - ws->height;
       else
        ws->y = screen_height - ws->height;
        ws->y = previous_ws->y - ws->height;
       else
        ws->y = screen_height - ws->height;
-      if (ws->y < 0) 
+      if (ws->y < 0)
         ws->y = 0;
     }
 }
         ws->y = 0;
     }
 }
@@ -1287,17 +1422,17 @@ remap_menubar (mw)
 
       ws->x = (previous_ws->x + selection_position.x
               + mw->menu.shadow_thickness);
 
       ws->x = (previous_ws->x + selection_position.x
               + mw->menu.shadow_thickness);
-      if (i == 1)
+      if (mw->menu.horizontal && i == 1)
        ws->x += mw->menu.margin;
 
 #if 0
       if (!mw->menu.horizontal || i > 1)
        ws->x += mw->menu.shadow_thickness;
 #endif
        ws->x += mw->menu.margin;
 
 #if 0
       if (!mw->menu.horizontal || i > 1)
        ws->x += mw->menu.shadow_thickness;
 #endif
-      
+
       ws->y = (previous_ws->y + selection_position.y
               + mw->menu.shadow_thickness);
       ws->y = (previous_ws->y + selection_position.y
               + mw->menu.shadow_thickness);
-      if (i == 1)
+      if (mw->menu.horizontal && i == 1)
        ws->y += mw->menu.margin;
 
       size_menu (mw, i);
        ws->y += mw->menu.margin;
 
       size_menu (mw, i);
@@ -1313,7 +1448,7 @@ remap_menubar (mw)
 
   /* unmap the menus that popped down */
   for (i = new_depth - 1; i < old_depth; i++)
 
   /* unmap the menus that popped down */
   for (i = new_depth - 1; i < old_depth; i++)
-    if (i >= new_depth || !new_stack[i]->contents)
+    if (i >= new_depth || (i > 0 && !new_stack[i]->contents))
       XUnmapWindow (XtDisplay (mw), windows[i].window);
 }
 
       XUnmapWindow (XtDisplay (mw), windows[i].window);
 }
 
@@ -1331,7 +1466,7 @@ motion_event_is_in_menu (mw, ev, level, relative_pos)
   relative_pos->x = ev->x_root - x;
   relative_pos->y = ev->y_root - y;
   return (x - shadow < ev->x_root && ev->x_root < x + ws->width
   relative_pos->x = ev->x_root - x;
   relative_pos->y = ev->y_root - y;
   return (x - shadow < ev->x_root && ev->x_root < x + ws->width
-         && y - shadow < ev->y_root && ev->y_root < y + ws->height);
+          && y - shadow < ev->y_root && ev->y_root < y + ws->height);
 }
 
 static Boolean
 }
 
 static Boolean
@@ -1344,15 +1479,17 @@ map_event_to_widget_value (mw, ev, val, level)
   int          i;
   XPoint       relative_pos;
   window_state*        ws;
   int          i;
   XPoint       relative_pos;
   window_state*        ws;
+  int inside = 0;
 
   *val = NULL;
 
   *val = NULL;
-  
+
   /* Find the window */
   for (i = mw->menu.old_depth - 1; i >= 0; i--)
     {
       ws = &mw->menu.windows [i];
       if (ws && motion_event_is_in_menu (mw, ev, i, &relative_pos))
        {
   /* Find the window */
   for (i = mw->menu.old_depth - 1; i >= 0; i--)
     {
       ws = &mw->menu.windows [i];
       if (ws && motion_event_is_in_menu (mw, ev, i, &relative_pos))
        {
+          inside = 1;
          display_menu (mw, i, True, NULL, &relative_pos, val, NULL, NULL);
 
          if (*val)
          display_menu (mw, i, True, NULL, &relative_pos, val, NULL, NULL);
 
          if (*val)
@@ -1362,6 +1499,15 @@ map_event_to_widget_value (mw, ev, val, level)
            }
        }
     }
            }
        }
     }
+
+  if (!inside) 
+    {
+      if (mw->menu.inside_entry != NULL) 
+        XtCallCallbackList ((Widget)mw, mw->menu.leave,
+                            (XtPointer) mw->menu.inside_entry);
+      mw->menu.inside_entry = NULL;
+    }
+
   return False;
 }
 
   return False;
 }
 
@@ -1371,45 +1517,70 @@ make_drawing_gcs (mw)
      XlwMenuWidget mw;
 {
   XGCValues xgcv;
      XlwMenuWidget mw;
 {
   XGCValues xgcv;
+  float scale;
+  XtGCMask mask = GCForeground | GCBackground;
 
 
+#ifdef HAVE_X_I18N
+  if (!mw->menu.fontSet)
+    {
+      xgcv.font = mw->menu.font->fid;
+      mask |= GCFont;
+    }
+#else
   xgcv.font = mw->menu.font->fid;
   xgcv.font = mw->menu.font->fid;
+  mask |= GCFont;
+#endif
   xgcv.foreground = mw->menu.foreground;
   xgcv.background = mw->core.background_pixel;
   xgcv.foreground = mw->menu.foreground;
   xgcv.background = mw->core.background_pixel;
-  mw->menu.foreground_gc = XtGetGC ((Widget)mw,
-                                   GCFont | GCForeground | GCBackground,
-                                   &xgcv);
-  
-  xgcv.font = mw->menu.font->fid;
+  mw->menu.foreground_gc = XtGetGC ((Widget)mw, mask, &xgcv);
+
   xgcv.foreground = mw->menu.button_foreground;
   xgcv.foreground = mw->menu.button_foreground;
+  mw->menu.button_gc = XtGetGC ((Widget)mw, mask, &xgcv);
+
   xgcv.background = mw->core.background_pixel;
   xgcv.background = mw->core.background_pixel;
-  mw->menu.button_gc = XtGetGC ((Widget)mw,
-                               GCFont | GCForeground | GCBackground,
-                               &xgcv);
-  
-  xgcv.font = mw->menu.font->fid;
-  xgcv.foreground = mw->menu.foreground;
-  xgcv.background = mw->core.background_pixel;
-  xgcv.fill_style = FillStippled;
-  xgcv.stipple = mw->menu.gray_pixmap;
-  mw->menu.inactive_gc = XtGetGC ((Widget)mw,
-                                 (GCFont | GCForeground | GCBackground
-                                  | GCFillStyle | GCStipple), &xgcv);
-  
-  xgcv.font = mw->menu.font->fid;
+
+#define BRIGHTNESS(color) (((color) & 0xff) + (((color) >> 8) & 0xff) + (((color) >> 16) & 0xff))
+
+  /* Allocate color for disabled menu-items.  */
+  mw->menu.disabled_foreground = mw->menu.foreground;
+  if (BRIGHTNESS(mw->menu.foreground) < BRIGHTNESS(mw->core.background_pixel))
+    scale = 2.3;
+  else
+    scale = 0.55;
+
+  x_alloc_lighter_color_for_widget ((Widget) mw, XtDisplay ((Widget) mw),
+                                   mw->core.colormap,
+                                   &mw->menu.disabled_foreground,
+                                   scale,
+                                   0x8000);
+
+  if (mw->menu.foreground == mw->menu.disabled_foreground
+      || mw->core.background_pixel == mw->menu.disabled_foreground)
+    {
+      /* Too few colors, use stipple.  */
+      xgcv.foreground = mw->menu.foreground;
+      xgcv.fill_style = FillStippled;
+      xgcv.stipple = mw->menu.gray_pixmap;
+      mw->menu.disabled_gc = XtGetGC ((Widget)mw, mask
+                                     | GCFillStyle | GCStipple, &xgcv);
+    }
+  else
+    {
+      /* Many colors available, use disabled pixel.  */
+      xgcv.foreground = mw->menu.disabled_foreground;
+      mw->menu.disabled_gc = XtGetGC ((Widget)mw, mask, &xgcv);
+    }
+
   xgcv.foreground = mw->menu.button_foreground;
   xgcv.background = mw->core.background_pixel;
   xgcv.fill_style = FillStippled;
   xgcv.stipple = mw->menu.gray_pixmap;
   xgcv.foreground = mw->menu.button_foreground;
   xgcv.background = mw->core.background_pixel;
   xgcv.fill_style = FillStippled;
   xgcv.stipple = mw->menu.gray_pixmap;
-  mw->menu.inactive_button_gc = XtGetGC ((Widget)mw,
-                                 (GCFont | GCForeground | GCBackground
-                                  | GCFillStyle | GCStipple), &xgcv);
-  
-  xgcv.font = mw->menu.font->fid;
+  mw->menu.inactive_button_gc = XtGetGC ((Widget)mw, mask
+                                        | GCFillStyle | GCStipple, &xgcv);
+
   xgcv.foreground = mw->core.background_pixel;
   xgcv.background = mw->menu.foreground;
   xgcv.foreground = mw->core.background_pixel;
   xgcv.background = mw->menu.foreground;
-  mw->menu.background_gc = XtGetGC ((Widget)mw,
-                                   GCFont | GCForeground | GCBackground,
-                                   &xgcv);
+  mw->menu.background_gc = XtGetGC ((Widget)mw, mask, &xgcv);
 }
 
 static void
 }
 
 static void
@@ -1418,13 +1589,13 @@ release_drawing_gcs (mw)
 {
   XtReleaseGC ((Widget) mw, mw->menu.foreground_gc);
   XtReleaseGC ((Widget) mw, mw->menu.button_gc);
 {
   XtReleaseGC ((Widget) mw, mw->menu.foreground_gc);
   XtReleaseGC ((Widget) mw, mw->menu.button_gc);
-  XtReleaseGC ((Widget) mw, mw->menu.inactive_gc);
+  XtReleaseGC ((Widget) mw, mw->menu.disabled_gc);
   XtReleaseGC ((Widget) mw, mw->menu.inactive_button_gc);
   XtReleaseGC ((Widget) mw, mw->menu.background_gc);
   /* let's get some segvs if we try to use these... */
   mw->menu.foreground_gc = (GC) -1;
   mw->menu.button_gc = (GC) -1;
   XtReleaseGC ((Widget) mw, mw->menu.inactive_button_gc);
   XtReleaseGC ((Widget) mw, mw->menu.background_gc);
   /* let's get some segvs if we try to use these... */
   mw->menu.foreground_gc = (GC) -1;
   mw->menu.button_gc = (GC) -1;
-  mw->menu.inactive_gc = (GC) -1;
+  mw->menu.disabled_gc = (GC) -1;
   mw->menu.inactive_button_gc = (GC) -1;
   mw->menu.background_gc = (GC) -1;
 }
   mw->menu.inactive_button_gc = (GC) -1;
   mw->menu.background_gc = (GC) -1;
 }
@@ -1451,7 +1622,7 @@ make_shadow_gcs (mw)
     mw->menu.top_shadow_color = mw->core.background_pixel;
   else
     mw->menu.top_shadow_color = mw->menu.top_shadow_color;
     mw->menu.top_shadow_color = mw->core.background_pixel;
   else
     mw->menu.top_shadow_color = mw->menu.top_shadow_color;
-    
+
   if (mw->menu.bottom_shadow_color == -1)
     mw->menu.bottom_shadow_color = mw->menu.foreground;
   else
   if (mw->menu.bottom_shadow_color == -1)
     mw->menu.bottom_shadow_color = mw->menu.foreground;
   else
@@ -1461,14 +1632,16 @@ make_shadow_gcs (mw)
       mw->menu.top_shadow_color == mw->menu.foreground)
     {
       topc.pixel = mw->core.background_pixel;
       mw->menu.top_shadow_color == mw->menu.foreground)
     {
       topc.pixel = mw->core.background_pixel;
+#ifdef emacs
+      if (x_alloc_lighter_color_for_widget ((Widget) mw, dpy, cmap,
+                                           &topc.pixel,
+                                           1.2, 0x8000))
+#else
       XQueryColor (dpy, cmap, &topc);
       /* don't overflow/wrap! */
       topc.red   = MINL (65535, topc.red   * 1.2);
       topc.green = MINL (65535, topc.green * 1.2);
       topc.blue  = MINL (65535, topc.blue  * 1.2);
       XQueryColor (dpy, cmap, &topc);
       /* don't overflow/wrap! */
       topc.red   = MINL (65535, topc.red   * 1.2);
       topc.green = MINL (65535, topc.green * 1.2);
       topc.blue  = MINL (65535, topc.blue  * 1.2);
-#ifdef emacs
-      if (x_alloc_nearest_color_for_widget ((Widget) mw, cmap, &topc))
-#else
       if (XAllocColor (dpy, cmap, &topc))
 #endif
        {
       if (XAllocColor (dpy, cmap, &topc))
 #endif
        {
@@ -1481,13 +1654,15 @@ make_shadow_gcs (mw)
       mw->menu.bottom_shadow_color == mw->core.background_pixel)
     {
       botc.pixel = mw->core.background_pixel;
       mw->menu.bottom_shadow_color == mw->core.background_pixel)
     {
       botc.pixel = mw->core.background_pixel;
+#ifdef emacs
+      if (x_alloc_lighter_color_for_widget ((Widget) mw, dpy, cmap,
+                                           &botc.pixel,
+                                           0.6, 0x4000))
+#else
       XQueryColor (dpy, cmap, &botc);
       botc.red   *= 0.6;
       botc.green *= 0.6;
       botc.blue  *= 0.6;
       XQueryColor (dpy, cmap, &botc);
       botc.red   *= 0.6;
       botc.green *= 0.6;
       botc.blue  *= 0.6;
-#ifdef emacs
-      if (x_alloc_nearest_color_for_widget ((Widget) mw, cmap, &botc))
-#else
       if (XAllocColor (dpy, cmap, &botc))
 #endif
        {
       if (XAllocColor (dpy, cmap, &botc))
 #endif
        {
@@ -1499,15 +1674,7 @@ make_shadow_gcs (mw)
 
   if (top_frobbed && bottom_frobbed)
     {
 
   if (top_frobbed && bottom_frobbed)
     {
-      int top_avg = ((topc.red / 3) + (topc.green / 3) + (topc.blue / 3));
-      int bot_avg = ((botc.red / 3) + (botc.green / 3) + (botc.blue / 3));
-      if (bot_avg > top_avg)
-       {
-         Pixel tmp = mw->menu.top_shadow_color;
-         mw->menu.top_shadow_color = mw->menu.bottom_shadow_color;
-         mw->menu.bottom_shadow_color = tmp;
-       }
-      else if (topc.pixel == botc.pixel)
+      if (topc.pixel == botc.pixel)
        {
          if (botc.pixel == mw->menu.foreground)
            {
        {
          if (botc.pixel == mw->menu.foreground)
            {
@@ -1585,7 +1752,7 @@ release_shadow_gcs (mw)
     px[i++] = mw->menu.bottom_shadow_color;
   if (i > 0)
     x_free_dpy_colors (dpy, screen, cmap, px, i);
     px[i++] = mw->menu.bottom_shadow_color;
   if (i > 0)
     x_free_dpy_colors (dpy, screen, cmap, px, i);
-  
+
   XtReleaseGC ((Widget) mw, mw->menu.shadow_top_gc);
   XtReleaseGC ((Widget) mw, mw->menu.shadow_bottom_gc);
 }
   XtReleaseGC ((Widget) mw, mw->menu.shadow_top_gc);
   XtReleaseGC ((Widget) mw, mw->menu.shadow_bottom_gc);
 }
@@ -1598,12 +1765,10 @@ XlwMenuInitialize (request, mw, args, num_args)
      Cardinal *num_args;
 {
   /* Get the GCs and the widget size */
      Cardinal *num_args;
 {
   /* Get the GCs and the widget size */
-  XSetWindowAttributes xswa;
-  int mask;
-  
+
   Window window = RootWindowOfScreen (DefaultScreenOfDisplay (XtDisplay (mw)));
   Display* display = XtDisplay (mw);
   Window window = RootWindowOfScreen (DefaultScreenOfDisplay (XtDisplay (mw)));
   Display* display = XtDisplay (mw);
-  
+
 #if 0
   widget_value *tem = (widget_value *) XtMalloc (sizeof (widget_value));
 
 #if 0
   widget_value *tem = (widget_value *) XtMalloc (sizeof (widget_value));
 
@@ -1615,37 +1780,37 @@ XlwMenuInitialize (request, mw, args, num_args)
 
 /*  mw->menu.cursor = XCreateFontCursor (display, mw->menu.cursor_shape); */
   mw->menu.cursor = mw->menu.cursor_shape;
 
 /*  mw->menu.cursor = XCreateFontCursor (display, mw->menu.cursor_shape); */
   mw->menu.cursor = mw->menu.cursor_shape;
-  
+
   mw->menu.gray_pixmap
   mw->menu.gray_pixmap
-    = XCreatePixmapFromBitmapData (display, window, gray_bits,
-                                  gray_width, gray_height,
+    = XCreatePixmapFromBitmapData (display, window, gray_bitmap_bits,
+                                  gray_bitmap_width, gray_bitmap_height,
                                   (unsigned long)1, (unsigned long)0, 1);
                                   (unsigned long)1, (unsigned long)0, 1);
-  
+
   /* I don't understand why this ends up 0 sometimes,
      but it does.  This kludge works around it.
      Can anyone find a real fix?   -- rms.  */
   if (mw->menu.font == 0)
     mw->menu.font = xlwmenu_default_font;
   /* I don't understand why this ends up 0 sometimes,
      but it does.  This kludge works around it.
      Can anyone find a real fix?   -- rms.  */
   if (mw->menu.font == 0)
     mw->menu.font = xlwmenu_default_font;
-
+#ifdef HAVE_X_I18N
+  if (mw->menu.fontSet)
+    mw->menu.font_extents = XExtentsOfFontSet (mw->menu.fontSet);
+#endif
+      
   make_drawing_gcs (mw);
   make_shadow_gcs (mw);
   make_drawing_gcs (mw);
   make_shadow_gcs (mw);
-  
-  xswa.background_pixel = mw->core.background_pixel;
-  xswa.border_pixel = mw->core.border_pixel;
-  mask = CWBackPixel | CWBorderPixel;
-  
+
   mw->menu.popped_up = False;
   mw->menu.popped_up = False;
-  
+
   mw->menu.old_depth = 1;
   mw->menu.old_stack = (widget_value**)XtMalloc (sizeof (widget_value*));
   mw->menu.old_stack_length = 1;
   mw->menu.old_stack [0] = mw->menu.contents;
   mw->menu.old_depth = 1;
   mw->menu.old_stack = (widget_value**)XtMalloc (sizeof (widget_value*));
   mw->menu.old_stack_length = 1;
   mw->menu.old_stack [0] = mw->menu.contents;
-  
+
   mw->menu.new_depth = 0;
   mw->menu.new_stack = 0;
   mw->menu.new_stack_length = 0;
   push_new_stack (mw, mw->menu.contents);
   mw->menu.new_depth = 0;
   mw->menu.new_stack = 0;
   mw->menu.new_stack_length = 0;
   push_new_stack (mw, mw->menu.contents);
-  
+
   mw->menu.windows = (window_state*)XtMalloc (sizeof (window_state));
   mw->menu.windows_length = 1;
   mw->menu.windows [0].x = 0;
   mw->menu.windows = (window_state*)XtMalloc (sizeof (window_state));
   mw->menu.windows_length = 1;
   mw->menu.windows [0].x = 0;
@@ -1653,7 +1818,7 @@ XlwMenuInitialize (request, mw, args, num_args)
   mw->menu.windows [0].width = 0;
   mw->menu.windows [0].height = 0;
   size_menu (mw, 0);
   mw->menu.windows [0].width = 0;
   mw->menu.windows [0].height = 0;
   size_menu (mw, 0);
-  
+
   mw->core.width = mw->menu.windows [0].width;
   mw->core.height = mw->menu.windows [0].height;
 }
   mw->core.width = mw->menu.windows [0].width;
   mw->core.height = mw->menu.windows [0].height;
 }
@@ -1691,7 +1856,7 @@ XlwMenuRealize (w, valueMask, attributes)
 /* Only the toplevel menubar/popup is a widget so it's the only one that
    receives expose events through Xt.  So we repaint all the other panes
    when receiving an Expose event. */
 /* Only the toplevel menubar/popup is a widget so it's the only one that
    receives expose events through Xt.  So we repaint all the other panes
    when receiving an Expose event. */
-static void 
+static void
 XlwMenuRedisplay (w, ev, region)
      Widget w;
      XEvent* ev;
 XlwMenuRedisplay (w, ev, region)
      Widget w;
      XEvent* ev;
@@ -1712,7 +1877,18 @@ XlwMenuRedisplay (w, ev, region)
     display_menu (mw, i, False, NULL, NULL, NULL, NULL, NULL);
 }
 
     display_menu (mw, i, False, NULL, NULL, NULL, NULL, NULL);
 }
 
-static void 
+
+/* Part of a hack to make the menu redisplay when a tooltip frame
+   over a menu item is unmapped.  */
+
+void
+xlwmenu_redisplay (w)
+     Widget w;
+{
+  XlwMenuRedisplay (w, NULL, None);
+}
+
+static void
 XlwMenuDestroy (w)
      Widget w;
 {
 XlwMenuDestroy (w)
      Widget w;
 {
@@ -1720,7 +1896,7 @@ XlwMenuDestroy (w)
   XlwMenuWidget mw = (XlwMenuWidget) w;
 
   if (pointer_grabbed)
   XlwMenuWidget mw = (XlwMenuWidget) w;
 
   if (pointer_grabbed)
-    XtUngrabPointer ((Widget)w, CurrentTime);
+    ungrab_all ((Widget)w, CurrentTime);
   pointer_grabbed = 0;
 
   submenu_destroyed = 1;
   pointer_grabbed = 0;
 
   submenu_destroyed = 1;
@@ -1763,7 +1939,7 @@ XlwMenuDestroy (w)
     XtFree ((char *) mw->menu.windows);
 }
 
     XtFree ((char *) mw->menu.windows);
 }
 
-static Boolean 
+static Boolean
 XlwMenuSetValues (current, request, new)
      Widget current;
      Widget request;
 XlwMenuSetValues (current, request, new)
      Widget current;
      Widget request;
@@ -1786,27 +1962,50 @@ XlwMenuSetValues (current, request, new)
 
   if (newmw->core.background_pixel != oldmw->core.background_pixel
       || newmw->menu.foreground != oldmw->menu.foreground
 
   if (newmw->core.background_pixel != oldmw->core.background_pixel
       || newmw->menu.foreground != oldmw->menu.foreground
-      || newmw->menu.font != oldmw->menu.font)
+#ifdef HAVE_X_I18N
+      || newmw->menu.fontSet != oldmw->menu.fontSet
+      || (newmw->menu.fontSet == NULL && newmw->menu.font != oldmw->menu.font)
+#else
+      || newmw->menu.font != oldmw->menu.font
+#endif
+      )
     {
       release_drawing_gcs (newmw);
       make_drawing_gcs (newmw);
     {
       release_drawing_gcs (newmw);
       make_drawing_gcs (newmw);
+
+      release_shadow_gcs (newmw);
+      /* Cause the shadow colors to be recalculated.  */
+      newmw->menu.top_shadow_color = -1;
+      newmw->menu.bottom_shadow_color = -1;
+      make_shadow_gcs (newmw);
+
       redisplay = True;
       redisplay = True;
-      
-      for (i = 0; i < oldmw->menu.windows_length; i++)
-       {
-         XSetWindowBackground (XtDisplay (oldmw),
-                               oldmw->menu.windows [i].window,
-                               newmw->core.background_pixel);
-         /* clear windows and generate expose events */
-         XClearArea (XtDisplay (oldmw), oldmw->menu.windows[i].window,
-                     0, 0, 0, 0, True);
-       }
+
+      if (XtIsRealized (current))
+       /* If the menu is currently displayed, change the display.  */
+       for (i = 0; i < oldmw->menu.windows_length; i++)
+         {
+           XSetWindowBackground (XtDisplay (oldmw),
+                                 oldmw->menu.windows [i].window,
+                                 newmw->core.background_pixel);
+           /* clear windows and generate expose events */
+           XClearArea (XtDisplay (oldmw), oldmw->menu.windows[i].window,
+                       0, 0, 0, 0, True);
+         }
+    }
+
+#ifdef HAVE_X_I18N
+  if (newmw->menu.fontSet != oldmw->menu.fontSet && newmw->menu.fontSet != NULL)
+    {
+      redisplay = True;
+      newmw->menu.font_extents = XExtentsOfFontSet (newmw->menu.fontSet);
     }
     }
+#endif
 
   return redisplay;
 }
 
 
   return redisplay;
 }
 
-static void 
+static void
 XlwMenuResize (w)
      Widget w;
 {
 XlwMenuResize (w)
      Widget w;
 {
@@ -1841,7 +2040,7 @@ handle_single_motion_event (mw, ev)
   else
     set_new_state (mw, val, level);
   remap_menubar (mw);
   else
     set_new_state (mw, val, level);
   remap_menubar (mw);
-  
+
   /* Sync with the display.  Makes it feel better on X terms. */
   XSync (XtDisplay (mw), False);
 }
   /* Sync with the display.  Makes it feel better on X terms. */
   XSync (XtDisplay (mw), False);
 }
@@ -1869,7 +2068,7 @@ handle_motion_event (mw, ev)
     handle_single_motion_event (mw, ev);
 }
 
     handle_single_motion_event (mw, ev);
 }
 
-static void 
+static void
 Start (w, ev, params, num_params)
      Widget w;
      XEvent *ev;
 Start (w, ev, params, num_params)
      Widget w;
      XEvent *ev;
@@ -1881,6 +2080,13 @@ Start (w, ev, params, num_params)
   if (!mw->menu.popped_up)
     {
       menu_post_event = *ev;
   if (!mw->menu.popped_up)
     {
       menu_post_event = *ev;
+      /* If event is set to CurrentTime, get the last known time stamp.
+         This is for calculating if (popup) menus should stay up after
+         a fast click.  */
+      if (menu_post_event.xbutton.time == CurrentTime)
+        menu_post_event.xbutton.time
+          = XtLastTimestampProcessed (XtDisplay (w));
+
       pop_up_menu (mw, (XButtonPressedEvent*) ev);
     }
   else
       pop_up_menu (mw, (XButtonPressedEvent*) ev);
     }
   else
@@ -1894,11 +2100,12 @@ Start (w, ev, params, num_params)
       mw->menu.windows [0].y = ev->xmotion.y_root - ev->xmotion.y;
 
       /* handles the down like a move, slots are compatible */
       mw->menu.windows [0].y = ev->xmotion.y_root - ev->xmotion.y;
 
       /* handles the down like a move, slots are compatible */
+      ev->xmotion.is_hint = 0;
       handle_motion_event (mw, &ev->xmotion);
     }
 }
 
       handle_motion_event (mw, &ev->xmotion);
     }
 }
 
-static void 
+static void
 Drag (w, ev, params, num_params)
      Widget w;
      XEvent *ev;
 Drag (w, ev, params, num_params)
      Widget w;
      XEvent *ev;
@@ -1921,6 +2128,229 @@ Nothing (w, ev, params, num_params)
 {
 }
 
 {
 }
 
+static widget_value *
+find_first_selectable (mw, item, skip_titles)
+     XlwMenuWidget mw;
+     widget_value *item;
+     int skip_titles;
+{
+  widget_value *current = item;
+  enum menu_separator separator;
+
+  while (lw_separator_p (current->name, &separator, 0) || !current->enabled
+         || (skip_titles && !current->call_data && !current->contents))
+    if (current->next)
+      current=current->next;
+    else
+      return NULL;
+
+  return current;
+}
+
+static widget_value *
+find_next_selectable (mw, item, skip_titles)
+     XlwMenuWidget mw;
+     widget_value *item;
+     int skip_titles;
+{
+  widget_value *current = item;
+  enum menu_separator separator;
+
+  while (current->next && (current=current->next) &&
+        (lw_separator_p (current->name, &separator, 0) || !current->enabled
+          || (skip_titles && !current->call_data && !current->contents)))
+    ;
+
+  if (current == item)
+    {
+      if (mw->menu.old_depth < 2)
+       return current;
+      current = mw->menu.old_stack [mw->menu.old_depth - 2]->contents;
+
+      while (lw_separator_p (current->name, &separator, 0)
+             || !current->enabled
+             || (skip_titles && !current->call_data
+                 && !current->contents))
+       {
+         if (current->next)
+           current=current->next;
+
+         if (current == item)
+           break;
+       }
+
+    }
+
+  return current;
+}
+
+static widget_value *
+find_prev_selectable (mw, item, skip_titles)
+     XlwMenuWidget mw;
+     widget_value *item;
+     int skip_titles;
+{
+  widget_value *current = item;
+  widget_value *prev = item;
+
+  while ((current=find_next_selectable (mw, current, skip_titles))
+         != item)
+    {
+      if (prev == current)
+       break;
+      prev=current;
+    }
+
+  return prev;
+}
+
+static void
+Down (w, ev, params, num_params)
+     Widget w;
+     XEvent *ev;
+     String *params;
+     Cardinal *num_params;
+{
+  XlwMenuWidget mw = (XlwMenuWidget) w;
+  widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
+  int popup_menu_p = mw->menu.top_depth == 1;
+
+  /* Inside top-level menu-bar?  */
+  if (mw->menu.old_depth == mw->menu.top_depth)
+    /* When <down> in the menu-bar is pressed, display the corresponding
+       sub-menu and select the first selectable menu item there.
+       If this is a popup menu, skip title item of the popup.  */
+    set_new_state (mw,
+                   find_first_selectable (mw,
+                                          selected_item->contents,
+                                          popup_menu_p),
+                   mw->menu.old_depth);
+  else
+    /* Highlight next possible (enabled and not separator) menu item.  */
+    set_new_state (mw, find_next_selectable (mw, selected_item, popup_menu_p),
+                   mw->menu.old_depth - 1);
+
+  remap_menubar (mw);
+}
+
+static void
+Up (w, ev, params, num_params)
+     Widget w;
+     XEvent *ev;
+     String *params;
+     Cardinal *num_params;
+{
+  XlwMenuWidget mw = (XlwMenuWidget) w;
+  widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
+  int popup_menu_p = mw->menu.top_depth == 1;
+
+  /* Inside top-level menu-bar?  */
+  if (mw->menu.old_depth == mw->menu.top_depth)
+    {
+      /* FIXME: this is tricky.  <up> in the menu-bar should select the
+        last selectable item in the list.  So we select the first
+        selectable one and find the previous selectable item.  Is there
+        a better way?  */
+      /* If this is a popup menu, skip title item of the popup.  */
+      set_new_state (mw,
+                     find_first_selectable (mw,
+                                            selected_item->contents,
+                                            popup_menu_p),
+                     mw->menu.old_depth);
+      remap_menubar (mw);
+      selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
+      set_new_state (mw,
+                     find_prev_selectable (mw,
+                                           selected_item,
+                                           popup_menu_p),
+                     mw->menu.old_depth - 1);
+    }
+  else
+    /* Highlight previous (enabled and not separator) menu item.  */
+    set_new_state (mw, find_prev_selectable (mw, selected_item, popup_menu_p),
+                   mw->menu.old_depth - 1);
+
+  remap_menubar (mw);
+}
+
+void
+Left (w, ev, params, num_params)
+     Widget w;
+     XEvent *ev;
+     String *params;
+     Cardinal *num_params;
+{
+  XlwMenuWidget mw = (XlwMenuWidget) w;
+  widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
+
+  /* Inside top-level menu-bar?  */
+  if (mw->menu.old_depth == mw->menu.top_depth)
+    /* When <left> in the menu-bar is pressed, display the previous item on
+       the menu-bar. If the current item is the first one, highlight the
+       last item in the menubar (probably Help).  */
+    set_new_state (mw, find_prev_selectable (mw, selected_item, 0),
+                   mw->menu.old_depth - 1);
+  else if (mw->menu.old_depth == 1
+          && selected_item->contents)     /* Is this menu item expandable?  */
+    {
+      set_new_state (mw, selected_item->contents, mw->menu.old_depth);
+      remap_menubar (mw);
+      selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
+      if (!selected_item->enabled && find_first_selectable (mw,
+                                                            selected_item,
+                                                            0))
+       set_new_state (mw, find_first_selectable (mw, selected_item, 0),
+                       mw->menu.old_depth - 1);
+    }
+
+  else
+    {
+      pop_new_stack_if_no_contents (mw);
+      set_new_state (mw, mw->menu.old_stack [mw->menu.old_depth - 2],
+                     mw->menu.old_depth - 2);
+    }
+
+  remap_menubar (mw);
+}
+
+void
+Right (w, ev, params, num_params)
+     Widget w;
+     XEvent *ev;
+     String *params;
+     Cardinal *num_params;
+{
+  XlwMenuWidget mw = (XlwMenuWidget) w;
+  widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
+
+  /* Inside top-level menu-bar?  */
+  if (mw->menu.old_depth == mw->menu.top_depth)
+    /* When <right> in the menu-bar is pressed, display the next item on
+       the menu-bar. If the current item is the last one, highlight the
+       first item (probably File).  */
+    set_new_state (mw, find_next_selectable (mw, selected_item, 0),
+                   mw->menu.old_depth - 1);
+  else if (selected_item->contents)     /* Is this menu item expandable?  */
+    {
+      set_new_state (mw, selected_item->contents, mw->menu.old_depth);
+      remap_menubar (mw);
+      selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
+      if (!selected_item->enabled && find_first_selectable (mw,
+                                                            selected_item,
+                                                            0))
+       set_new_state (mw, find_first_selectable (mw, selected_item, 0),
+                       mw->menu.old_depth - 1);
+    }
+  else
+    {
+      pop_new_stack_if_no_contents (mw);
+      set_new_state (mw, mw->menu.old_stack [mw->menu.old_depth - 2],
+                     mw->menu.old_depth - 2);
+    }
+
+  remap_menubar (mw);
+}
+
 /* Handle key press and release events while menu is popped up.
    Our action is to get rid of the menu.  */
 static void
 /* Handle key press and release events while menu is popped up.
    Our action is to get rid of the menu.  */
 static void
@@ -1939,7 +2369,7 @@ Key (w, ev, params, num_params)
   if (mw->menu.popped_up)
     {
       mw->menu.popped_up = False;
   if (mw->menu.popped_up)
     {
       mw->menu.popped_up = False;
-      XtUngrabPointer ((Widget)mw, ev->xmotion.time);
+      ungrab_all ((Widget)mw, ev->xmotion.time);
       if (XtIsShell (XtParent ((Widget) mw)))
        XtPopdown (XtParent ((Widget) mw));
       else
       if (XtIsShell (XtParent ((Widget) mw)))
        XtPopdown (XtParent ((Widget) mw));
       else
@@ -1962,7 +2392,7 @@ Select (w, ev, params, num_params)
 {
   XlwMenuWidget mw = (XlwMenuWidget)w;
   widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
 {
   XlwMenuWidget mw = (XlwMenuWidget)w;
   widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
-  
+
   /* If user releases the button quickly, without selecting anything,
      after the initial down-click that brought the menu up,
      do nothing.  */
   /* If user releases the button quickly, without selecting anything,
      after the initial down-click that brought the menu up,
      do nothing.  */
@@ -1980,7 +2410,7 @@ Select (w, ev, params, num_params)
   if (mw->menu.popped_up)
     {
       mw->menu.popped_up = False;
   if (mw->menu.popped_up)
     {
       mw->menu.popped_up = False;
-      XtUngrabPointer ((Widget)mw, ev->xmotion.time);
+      ungrab_all ((Widget)mw, ev->xmotion.time);
       if (XtIsShell (XtParent ((Widget) mw)))
        XtPopdown (XtParent ((Widget) mw));
       else
       if (XtIsShell (XtParent ((Widget) mw)))
        XtPopdown (XtParent ((Widget) mw));
       else
@@ -1996,7 +2426,7 @@ Select (w, ev, params, num_params)
 
 
 \f/* Special code to pop-up a menu */
 
 
 \f/* Special code to pop-up a menu */
-void
+static void
 pop_up_menu (mw, event)
      XlwMenuWidget mw;
      XButtonPressedEvent* event;
 pop_up_menu (mw, event)
      XlwMenuWidget mw;
      XButtonPressedEvent* event;
@@ -2008,10 +2438,10 @@ pop_up_menu (mw, event)
   int          borderwidth = mw->menu.shadow_thickness;
   Screen*      screen = XtScreen (mw);
   Display       *display = XtDisplay (mw);
   int          borderwidth = mw->menu.shadow_thickness;
   Screen*      screen = XtScreen (mw);
   Display       *display = XtDisplay (mw);
-  int count;
 
   next_release_must_exit = 0;
 
 
   next_release_must_exit = 0;
 
+  mw->menu.inside_entry = NULL;
   XtCallCallbackList ((Widget)mw, mw->menu.open, NULL);
 
   if (XtIsShell (XtParent ((Widget)mw)))
   XtCallCallbackList ((Widget)mw, mw->menu.open, NULL);
 
   if (XtIsShell (XtParent ((Widget)mw)))
@@ -2040,6 +2470,7 @@ pop_up_menu (mw, event)
       display_menu (mw, 0, False, NULL, NULL, NULL, NULL, NULL);
       mw->menu.windows [0].x = x + borderwidth;
       mw->menu.windows [0].y = y + borderwidth;
       display_menu (mw, 0, False, NULL, NULL, NULL, NULL, NULL);
       mw->menu.windows [0].x = x + borderwidth;
       mw->menu.windows [0].y = y + borderwidth;
+      mw->menu.top_depth = 1;  /* Popup menus don't have a bar so top is 1  */
     }
   else
     {
     }
   else
     {
@@ -2050,28 +2481,44 @@ pop_up_menu (mw, event)
       /* notes the absolute position of the menubar window */
       mw->menu.windows [0].x = ev->xmotion.x_root - ev->xmotion.x;
       mw->menu.windows [0].y = ev->xmotion.y_root - ev->xmotion.y;
       /* notes the absolute position of the menubar window */
       mw->menu.windows [0].x = ev->xmotion.x_root - ev->xmotion.x;
       mw->menu.windows [0].y = ev->xmotion.y_root - ev->xmotion.y;
+      mw->menu.top_depth = 2;
     }
 
 #ifdef emacs
     }
 
 #ifdef emacs
-  count = x_catch_errors (display);
+  x_catch_errors (display);
 #endif
 #endif
-  XtGrabPointer ((Widget)mw, False,
-                (PointerMotionMask
-                 | PointerMotionHintMask
-                 | ButtonReleaseMask
-                 | ButtonPressMask),
-                GrabModeAsync, GrabModeAsync, None,
-                mw->menu.cursor_shape,
-                event->time);
-  pointer_grabbed = 1;
+  if (XtGrabPointer ((Widget)mw, False,
+                     (PointerMotionMask
+                      | PointerMotionHintMask
+                      | ButtonReleaseMask
+                      | ButtonPressMask),
+                     GrabModeAsync, GrabModeAsync, None,
+                     mw->menu.cursor_shape,
+                     event->time) == Success)
+    {
+      if (! GRAB_KEYBOARD
+          || XtGrabKeyboard ((Widget)mw, False, GrabModeAsync,
+                             GrabModeAsync, event->time) == Success)
+        {
+          XtSetKeyboardFocus((Widget)mw, None);
+          pointer_grabbed = 1;
+        }
+      else
+        XtUngrabPointer ((Widget)mw, event->time);
+    }
+
 #ifdef emacs
   if (x_had_errors_p (display))
     {
       pointer_grabbed = 0;
       XtUngrabPointer ((Widget)mw, event->time);
     }
 #ifdef emacs
   if (x_had_errors_p (display))
     {
       pointer_grabbed = 0;
       XtUngrabPointer ((Widget)mw, event->time);
     }
-  x_uncatch_errors (display, count);
+  x_uncatch_errors ();
 #endif
 
 #endif
 
+  ((XMotionEvent*)event)->is_hint = 0;
   handle_motion_event (mw, (XMotionEvent*)event);
 }
   handle_motion_event (mw, (XMotionEvent*)event);
 }
+
+/* arch-tag: 657f43dd-dfd0-4cc9-910c-52935f01176e
+   (do not change this comment) */