root/lwlib/xlwmenu.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ungrab_all
  2. abort_gracefully
  3. push_new_stack
  4. pop_new_stack_if_no_contents
  5. make_old_stack_space
  6. string_width
  7. arrow_width
  8. toggle_button_width
  9. radio_button_width
  10. resource_widget_value
  11. size_menu_item
  12. size_menu
  13. draw_arrow
  14. draw_shadow_rectangle
  15. draw_shadow_rhombus
  16. draw_toggle
  17. draw_radio
  18. draw_separator
  19. separator_height
  20. draw_highlight
  21. display_menu_item
  22. display_menu
  23. set_new_state
  24. expose_cb
  25. set_window_type
  26. make_windows_if_needed
  27. xlwmenu_window_p
  28. fit_to_screen
  29. create_pixmap_for_menu
  30. remap_menubar
  31. motion_event_is_in_menu
  32. map_event_to_widget_value
  33. make_drawing_gcs
  34. release_drawing_gcs
  35. compute_shadow_colors
  36. make_shadow_gcs
  37. release_shadow_gcs
  38. getDefaultXftFont
  39. openXftFont
  40. update_xft_colors
  41. XlwMenuInitialize
  42. XlwMenuClassInitialize
  43. XlwMenuRealize
  44. XlwMenuRedisplay
  45. xlwmenu_redisplay
  46. XlwMenuDestroy
  47. fontname_changed
  48. XlwMenuSetValues
  49. XlwMenuResize
  50. handle_single_motion_event
  51. handle_motion_event
  52. Start
  53. Drag
  54. Nothing
  55. find_first_selectable
  56. find_next_selectable
  57. find_prev_selectable
  58. Down
  59. Up
  60. Left
  61. Right
  62. Key
  63. Select
  64. pop_up_menu

     1 /* Implements a lightweight menubar widget.
     2 
     3 Copyright (C) 1992 Lucid, Inc.
     4 Copyright (C) 1994-1995, 1997, 1999-2023 Free Software Foundation, Inc.
     5 
     6 This file is part of the Lucid Widget Library.
     7 
     8 The Lucid Widget Library is free software; you can redistribute it and/or
     9 modify it under the terms of the GNU General Public License as published by
    10 the Free Software Foundation; either version 2, or (at your option)
    11 any later version.
    12 
    13 The Lucid Widget Library is distributed in the hope that it will be useful,
    14 but WITHOUT ANY WARRANTY; without even the implied warranty of
    15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    16 GNU General Public License for more details.
    17 
    18 You should have received a copy of the GNU General Public License
    19 along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
    20 
    21 /* Created by devin@lucid.com */
    22 
    23 #include <config.h>
    24 
    25 #include <setjmp.h>
    26 #include <lisp.h>
    27 
    28 #include <stdio.h>
    29 
    30 #include <sys/types.h>
    31 #include <X11/Xos.h>
    32 #include <X11/IntrinsicP.h>
    33 #include <X11/ObjectP.h>
    34 #include <X11/StringDefs.h>
    35 #include <X11/cursorfont.h>
    36 #include <X11/Shell.h>
    37 #include "xlwmenuP.h"
    38 
    39 #ifdef emacs
    40 
    41 #include <xterm.h>
    42 #include "bitmaps/gray.xbm"
    43 
    44 #else /* not emacs */
    45 
    46 #include <X11/bitmaps/gray>
    47 
    48 #endif /* not emacs */
    49 
    50 static int pointer_grabbed;
    51 static int keyboard_grabbed;
    52 static XEvent menu_post_event;
    53 
    54 static char
    55 xlwMenuTranslations [] =
    56 "<BtnDown>:       start()\n\
    57 <Motion>:         drag()\n\
    58 <BtnUp>:          select()\n\
    59 <Key>Shift_L:     nothing()\n\
    60 <Key>Shift_R:     nothing()\n\
    61 <Key>Meta_L:      nothing()\n\
    62 <Key>Meta_R:      nothing()\n\
    63 <Key>Control_L:   nothing()\n\
    64 <Key>Control_R:   nothing()\n\
    65 <Key>Hyper_L:     nothing()\n\
    66 <Key>Hyper_R:     nothing()\n\
    67 <Key>Super_L:     nothing()\n\
    68 <Key>Super_R:     nothing()\n\
    69 <Key>Alt_L:       nothing()\n\
    70 <Key>Alt_R:       nothing()\n\
    71 <Key>Caps_Lock:   nothing()\n\
    72 <Key>Shift_Lock:  nothing()\n\
    73 <KeyUp>Shift_L:   nothing()\n\
    74 <KeyUp>Shift_R:   nothing()\n\
    75 <KeyUp>Meta_L:    nothing()\n\
    76 <KeyUp>Meta_R:    nothing()\n\
    77 <KeyUp>Control_L: nothing()\n\
    78 <KeyUp>Control_R: nothing()\n\
    79 <KeyUp>Hyper_L:   nothing()\n\
    80 <KeyUp>Hyper_R:   nothing()\n\
    81 <KeyUp>Super_L:   nothing()\n\
    82 <KeyUp>Super_R:   nothing()\n\
    83 <KeyUp>Alt_L:     nothing()\n\
    84 <KeyUp>Alt_R:     nothing()\n\
    85 <KeyUp>Caps_Lock: nothing()\n\
    86 <KeyUp>Shift_Lock:nothing()\n\
    87 <Key>Return:      select()\n\
    88 <Key>Down:        down()\n\
    89 <Key>Up:          up()\n\
    90 <Key>Left:        left()\n\
    91 <Key>Right:       right()\n\
    92 <Key>:            key()\n\
    93 <KeyUp>:          key()\n\
    94 ";
    95 
    96 /* FIXME: Space should toggle togglable menu item but not remove the menu
    97    so you can toggle the next one without entering the menu again.  */
    98 
    99 /* FIXME: Should ESC close one level of menu structure or the complete menu?  */
   100 
   101 /* FIXME: F10 should enter the menu, the first one in the menu-bar.  */
   102 
   103 #define offset(field) offsetof (XlwMenuRec, field)
   104 static XtResource
   105 xlwMenuResources[] =
   106 {
   107 #ifdef HAVE_X_I18N
   108   {XtNfontSet,  XtCFontSet, XtRFontSet, sizeof(XFontSet),
   109      offset(menu.fontSet), XtRFontSet, NULL},
   110 #endif
   111 #if defined USE_CAIRO || defined HAVE_XFT
   112 #define DEFAULT_FONTNAME "Sans-10"
   113 #else
   114 #define DEFAULT_FONTNAME "XtDefaultFont"
   115 #endif
   116   {XtNfont,  XtCFont, XtRString, sizeof(String),
   117      offset(menu.fontName), XtRString, DEFAULT_FONTNAME },
   118   {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
   119      offset(menu.foreground), XtRString, "XtDefaultForeground"},
   120   {XtNdisabledForeground, XtCDisabledForeground, XtRPixel, sizeof(Pixel),
   121    offset(menu.disabled_foreground), XtRString, (XtPointer)NULL},
   122   {XtNbuttonForeground, XtCButtonForeground, XtRPixel, sizeof(Pixel),
   123      offset(menu.button_foreground), XtRString, "XtDefaultForeground"},
   124   {XtNhighlightForeground, XtCHighlightForeground, XtRPixel, sizeof(Pixel),
   125      offset(menu.highlight_foreground), XtRImmediate, (XtPointer) -1},
   126   {XtNhighlightBackground, XtCHighlightBackground, XtRPixel, sizeof(Pixel),
   127      offset(menu.highlight_background), XtRImmediate, (XtPointer)-1},
   128   {XtNmargin, XtCMargin, XtRDimension,  sizeof(Dimension),
   129      offset(menu.margin), XtRImmediate, (XtPointer)1},
   130   {XtNhorizontalSpacing, XtCMargin, XtRDimension,  sizeof(Dimension),
   131      offset(menu.horizontal_spacing), XtRImmediate, (XtPointer)3},
   132   {XtNverticalSpacing, XtCMargin, XtRDimension,  sizeof(Dimension),
   133      offset(menu.vertical_spacing), XtRImmediate, (XtPointer)2},
   134   {XtNarrowSpacing, XtCMargin, XtRDimension,  sizeof(Dimension),
   135      offset(menu.arrow_spacing), XtRImmediate, (XtPointer)10},
   136 
   137   {XmNshadowThickness, XmCShadowThickness, XtRDimension,
   138      sizeof (Dimension), offset (menu.shadow_thickness),
   139      XtRImmediate, (XtPointer)1},
   140   {XmNtopShadowColor, XmCTopShadowColor, XtRPixel, sizeof (Pixel),
   141      offset (menu.top_shadow_color), XtRImmediate, (XtPointer)-1},
   142   {XmNbottomShadowColor, XmCBottomShadowColor, XtRPixel, sizeof (Pixel),
   143      offset (menu.bottom_shadow_color), XtRImmediate, (XtPointer)-1},
   144   {XmNtopShadowPixmap, XmCTopShadowPixmap, XtRPixmap, sizeof (Pixmap),
   145      offset (menu.top_shadow_pixmap), XtRImmediate, (XtPointer)None},
   146   {XmNbottomShadowPixmap, XmCBottomShadowPixmap, XtRPixmap, sizeof (Pixmap),
   147      offset (menu.bottom_shadow_pixmap), XtRImmediate, (XtPointer)None},
   148 
   149   {XtNopen, XtCCallback, XtRCallback, sizeof(XtPointer),
   150      offset(menu.open), XtRCallback, (XtPointer)NULL},
   151   {XtNselect, XtCCallback, XtRCallback, sizeof(XtPointer),
   152      offset(menu.select), XtRCallback, (XtPointer)NULL},
   153   {XtNhighlightCallback, XtCCallback, XtRCallback, sizeof(XtPointer),
   154      offset(menu.highlight), XtRCallback, (XtPointer)NULL},
   155   {XtNenterCallback, XtCCallback, XtRCallback, sizeof(XtPointer),
   156      offset(menu.enter), XtRCallback, (XtPointer)NULL},
   157   {XtNleaveCallback, XtCCallback, XtRCallback, sizeof(XtPointer),
   158      offset(menu.leave), XtRCallback, (XtPointer)NULL},
   159   {XtNmenu, XtCMenu, XtRPointer, sizeof(XtPointer),
   160      offset(menu.contents), XtRImmediate, (XtPointer)NULL},
   161   {XtNcursor, XtCCursor, XtRCursor, sizeof(Cursor),
   162      offset(menu.cursor_shape), XtRString, (XtPointer)"right_ptr"},
   163   {XtNhorizontal, XtCHorizontal, XtRInt, sizeof(int),
   164      offset(menu.horizontal), XtRImmediate, (XtPointer)True},
   165   {XtNborderThickness, XtCBorderThickness, XtRDimension,
   166      sizeof (Dimension), offset (menu.border_thickness),
   167      XtRImmediate, (XtPointer)1}
   168 };
   169 #undef offset
   170 
   171 static Boolean XlwMenuSetValues(Widget current, Widget request, Widget new,
   172                                 ArgList args, Cardinal *num_args);
   173 static void XlwMenuRealize(Widget, Mask *, XSetWindowAttributes *);
   174 static void XlwMenuResize(Widget w);
   175 static void XlwMenuInitialize(Widget, Widget, ArgList, Cardinal *);
   176 static void XlwMenuRedisplay(Widget w, XEvent *ev, Region region);
   177 static void XlwMenuDestroy(Widget w);
   178 static void XlwMenuClassInitialize(void);
   179 static void Start(Widget w, XEvent *ev, String *params, Cardinal *num_params);
   180 static void Drag(Widget w, XEvent *ev, String *params, Cardinal *num_params);
   181 static void Down(Widget w, XEvent *ev, String *params, Cardinal *num_params);
   182 static void Up(Widget w, XEvent *ev, String *params, Cardinal *num_params);
   183 static void Left(Widget w, XEvent *ev, String *params, Cardinal *num_params);
   184 static void Right(Widget w, XEvent *ev, String *params, Cardinal *num_params);
   185 static void Select(Widget w, XEvent *ev, String *params, Cardinal *num_params);
   186 static void Key(Widget w, XEvent *ev, String *params, Cardinal *num_params);
   187 static void Nothing(Widget w, XEvent *ev, String *params, Cardinal *num_params);
   188 static int separator_height (enum menu_separator);
   189 static void pop_up_menu (XlwMenuWidget, XButtonPressedEvent *);
   190 
   191 static XtActionsRec
   192 xlwMenuActionsList [] =
   193 {
   194   {"start",             Start},
   195   {"drag",              Drag},
   196   {"down",              Down},
   197   {"up",                Up},
   198   {"left",              Left},
   199   {"right",             Right},
   200   {"select",            Select},
   201   {"key",               Key},
   202   {"MenuGadgetEscape",  Key},   /* Compatibility with Lesstif/Motif.  */
   203   {"nothing",           Nothing},
   204 };
   205 
   206 #define SuperClass ((CoreWidgetClass)&coreClassRec)
   207 
   208 XlwMenuClassRec xlwMenuClassRec =
   209 {
   210   {  /* CoreClass fields initialization */
   211     (WidgetClass) SuperClass,           /* superclass             */
   212     "XlwMenu",                          /* class_name             */
   213     sizeof(XlwMenuRec),                 /* size                   */
   214     XlwMenuClassInitialize,             /* class_initialize       */
   215     NULL,                               /* class_part_initialize  */
   216     FALSE,                              /* class_inited           */
   217     XlwMenuInitialize,                  /* initialize             */
   218     NULL,                               /* initialize_hook        */
   219     XlwMenuRealize,                     /* realize                */
   220     xlwMenuActionsList,                 /* actions                */
   221     XtNumber(xlwMenuActionsList),       /* num_actions            */
   222     xlwMenuResources,                   /* resources              */
   223     XtNumber(xlwMenuResources),         /* resource_count         */
   224     NULLQUARK,                          /* xrm_class              */
   225     TRUE,                               /* compress_motion        */
   226     XtExposeCompressMaximal,            /* compress_exposure      */
   227     TRUE,                               /* compress_enterleave    */
   228     FALSE,                              /* visible_interest       */
   229     XlwMenuDestroy,                     /* destroy                */
   230     XlwMenuResize,                      /* resize                 */
   231     XlwMenuRedisplay,                   /* expose                 */
   232     XlwMenuSetValues,                   /* set_values             */
   233     NULL,                               /* set_values_hook        */
   234     XtInheritSetValuesAlmost,           /* set_values_almost      */
   235     NULL,                               /* get_values_hook        */
   236     NULL,                               /* accept_focus           */
   237     XtVersion,                          /* version                */
   238     NULL,                               /* callback_private       */
   239     xlwMenuTranslations,                /* tm_table               */
   240     XtInheritQueryGeometry,             /* query_geometry         */
   241     XtInheritDisplayAccelerator,        /* display_accelerator    */
   242     NULL                                /* extension              */
   243   },  /* XlwMenuClass fields initialization */
   244   {
   245     0                                   /* dummy */
   246   },
   247 };
   248 
   249 WidgetClass xlwMenuWidgetClass = (WidgetClass) &xlwMenuClassRec;
   250 
   251 int submenu_destroyed;
   252 
   253 static int next_release_must_exit;
   254 
   255 /* Utilities */
   256 
   257 /* Ungrab pointer and keyboard */
   258 static void
   259 ungrab_all (Widget w, Time ungrabtime)
   260 {
   261   XtUngrabPointer (w, ungrabtime);
   262 
   263   if (keyboard_grabbed)
   264     XtUngrabKeyboard (w, ungrabtime);
   265 }
   266 
   267 /* Like abort, but remove grabs from widget W before.  */
   268 
   269 static _Noreturn void
   270 abort_gracefully (Widget w)
   271 {
   272   if (XtIsShell (XtParent (w)))
   273     XtRemoveGrab (w);
   274   ungrab_all (w, CurrentTime);
   275   emacs_abort ();
   276 }
   277 
   278 static void
   279 push_new_stack (XlwMenuWidget mw, widget_value *val)
   280 {
   281   if (!mw->menu.new_stack)
   282     {
   283       mw->menu.new_stack_length = 10;
   284       mw->menu.new_stack =
   285         (widget_value**)XtCalloc (mw->menu.new_stack_length,
   286                                   sizeof (widget_value*));
   287     }
   288   else if (mw->menu.new_depth == mw->menu.new_stack_length)
   289     {
   290       mw->menu.new_stack_length *= 2;
   291       mw->menu.new_stack =
   292         (widget_value**)XtRealloc ((char*)mw->menu.new_stack,
   293                                    mw->menu.new_stack_length * sizeof (widget_value*));
   294     }
   295   mw->menu.new_stack [mw->menu.new_depth++] = val;
   296 }
   297 
   298 static void
   299 pop_new_stack_if_no_contents (XlwMenuWidget mw)
   300 {
   301   if (mw->menu.new_depth > 1)
   302     {
   303       if (!mw->menu.new_stack [mw->menu.new_depth - 1]->contents)
   304         mw->menu.new_depth -= 1;
   305     }
   306 }
   307 
   308 static void
   309 make_old_stack_space (XlwMenuWidget mw, int n)
   310 {
   311   if (!mw->menu.old_stack)
   312     {
   313       mw->menu.old_stack_length = 10;
   314       mw->menu.old_stack =
   315         (widget_value**)XtCalloc (mw->menu.old_stack_length,
   316                                   sizeof (widget_value*));
   317     }
   318   else if (mw->menu.old_stack_length < n)
   319     {
   320       mw->menu.old_stack_length *= 2;
   321       mw->menu.old_stack =
   322         (widget_value**)XtRealloc ((char*)mw->menu.old_stack,
   323                                    mw->menu.old_stack_length * sizeof (widget_value*));
   324     }
   325 }
   326 
   327 /* Size code */
   328 static int
   329 string_width (XlwMenuWidget mw, char *s)
   330 {
   331   XCharStruct xcs;
   332   int drop;
   333 #if defined USE_CAIRO || defined HAVE_XFT
   334   if (mw->menu.xft_font)
   335     {
   336       XGlyphInfo gi;
   337       XftTextExtentsUtf8 (XtDisplay (mw), mw->menu.xft_font,
   338                           (FcChar8 *) s,
   339                           strlen (s), &gi);
   340       return gi.xOff;
   341     }
   342 #endif
   343 #ifdef HAVE_X_I18N
   344   if (mw->menu.fontSet)
   345     {
   346       XRectangle ink, logical;
   347       XmbTextExtents (mw->menu.fontSet, s, strlen (s), &ink, &logical);
   348       return logical.width;
   349     }
   350 #endif
   351 
   352   XTextExtents (mw->menu.font, s, strlen (s), &drop, &drop, &drop, &xcs);
   353   return xcs.width;
   354 
   355 }
   356 
   357 #if defined USE_CAIRO || defined HAVE_XFT
   358 #define MENU_FONT_HEIGHT(mw)                                    \
   359   ((mw)->menu.xft_font != NULL                                  \
   360    ? (mw)->menu.xft_font->height                                \
   361    : ((mw)->menu.fontSet != NULL                                \
   362       ? (mw)->menu.font_extents->max_logical_extent.height      \
   363       : (mw)->menu.font->ascent + (mw)->menu.font->descent))
   364 #define MENU_FONT_ASCENT(mw)                                    \
   365   ((mw)->menu.xft_font != NULL                                  \
   366     ? (mw)->menu.xft_font->ascent                               \
   367     : ((mw)->menu.fontSet != NULL                               \
   368        ? - (mw)->menu.font_extents->max_logical_extent.y        \
   369        : (mw)->menu.font->ascent))
   370 #else
   371 #ifdef HAVE_X_I18N
   372 #define MENU_FONT_HEIGHT(mw) \
   373   ((mw)->menu.fontSet != NULL \
   374    ? (mw)->menu.font_extents->max_logical_extent.height   \
   375    : (mw)->menu.font->ascent + (mw)->menu.font->descent)
   376 #define MENU_FONT_ASCENT(mw) \
   377   ((mw)->menu.fontSet != NULL \
   378    ? - (mw)->menu.font_extents->max_logical_extent.y \
   379    : (mw)->menu.font->ascent)
   380 #else
   381 #define MENU_FONT_HEIGHT(mw) \
   382   ((mw)->menu.font->ascent + (mw)->menu.font->descent)
   383 #define MENU_FONT_ASCENT(mw) ((mw)->menu.font->ascent)
   384 #endif
   385 #endif
   386 
   387 static int
   388 arrow_width (XlwMenuWidget mw)
   389 {
   390   return (MENU_FONT_ASCENT (mw) * 3/4) | 1;
   391 }
   392 
   393 /* Return the width of toggle buttons of widget MW.  */
   394 
   395 static int
   396 toggle_button_width (XlwMenuWidget mw)
   397 {
   398   return (MENU_FONT_HEIGHT (mw) * 2 / 3) | 1;
   399 }
   400 
   401 
   402 /* Return the width of radio buttons of widget MW.  */
   403 
   404 static int
   405 radio_button_width (XlwMenuWidget mw)
   406 {
   407   return toggle_button_width (mw) * 1.41;
   408 }
   409 
   410 
   411 static XtResource
   412 nameResource[] =
   413 {
   414   {"labelString",  "LabelString", XtRString, sizeof(String),
   415      0, XtRImmediate, 0},
   416 };
   417 
   418 static char*
   419 resource_widget_value (XlwMenuWidget mw, widget_value *val)
   420 {
   421   if (!val->toolkit_data)
   422     {
   423       char* resourced_name = NULL;
   424       char* complete_name;
   425       XtGetSubresources ((Widget) mw,
   426                          (XtPointer) &resourced_name,
   427                          val->name, val->name,
   428                          nameResource, 1, NULL, 0);
   429       if (!resourced_name)
   430         resourced_name = val->name;
   431       if (!val->value)
   432         {
   433           complete_name = (char *) XtMalloc (strlen (resourced_name) + 1);
   434           strcpy (complete_name, resourced_name);
   435         }
   436       else
   437         {
   438           int complete_length =
   439             strlen (resourced_name) + strlen (val->value) + 2;
   440           complete_name = XtMalloc (complete_length);
   441           char *z = stpcpy (complete_name, resourced_name);
   442           *z++ = ' ';
   443           strcpy (z, val->value);
   444         }
   445 
   446       val->toolkit_data = complete_name;
   447       val->free_toolkit_data = True;
   448     }
   449   return (char*)val->toolkit_data;
   450 }
   451 
   452 /* Returns the sizes of an item */
   453 static void
   454 size_menu_item (XlwMenuWidget mw,
   455                 widget_value* val,
   456                 int horizontal_p,
   457                 int* label_width,
   458                 int* rest_width,
   459                 int* button_width,
   460                 int* height)
   461 {
   462   enum menu_separator separator;
   463 
   464   if (lw_separator_p (val->name, &separator, 0))
   465     {
   466       *height = separator_height (separator);
   467       *label_width = 1;
   468       *rest_width = 0;
   469       *button_width = 0;
   470     }
   471   else
   472     {
   473       *height = MENU_FONT_HEIGHT (mw)
   474         + 2 * mw->menu.vertical_spacing + 2 * mw->menu.shadow_thickness;
   475 
   476       *label_width =
   477         string_width (mw, resource_widget_value (mw, val))
   478           + mw->menu.horizontal_spacing + mw->menu.shadow_thickness;
   479 
   480       *rest_width =  mw->menu.horizontal_spacing + mw->menu.shadow_thickness;
   481       if (!horizontal_p)
   482         {
   483           if (val->contents)
   484             /* Add width of the arrow displayed for submenus.  */
   485             *rest_width += arrow_width (mw) + mw->menu.arrow_spacing;
   486           else if (val->key)
   487             /* Add width of key equivalent string.  */
   488             *rest_width += (string_width (mw, val->key)
   489                             + mw->menu.arrow_spacing);
   490 
   491           if (val->button_type == BUTTON_TYPE_TOGGLE)
   492             *button_width = (toggle_button_width (mw)
   493                              + mw->menu.horizontal_spacing);
   494           else if (val->button_type == BUTTON_TYPE_RADIO)
   495             *button_width = (radio_button_width (mw)
   496                              + mw->menu.horizontal_spacing);
   497         }
   498     }
   499 }
   500 
   501 static void
   502 size_menu (XlwMenuWidget mw, int level)
   503 {
   504   int           label_width = 0;
   505   int           rest_width = 0;
   506   int           button_width = 0;
   507   int           max_rest_width = 0;
   508   int           max_button_width = 0;
   509   int           height = 0;
   510   int           horizontal_p = mw->menu.horizontal && (level == 0);
   511   widget_value* val;
   512   window_state* ws;
   513 
   514   if (level >= mw->menu.old_depth)
   515     abort_gracefully ((Widget) mw);
   516 
   517   ws = &mw->menu.windows [level];
   518   ws->width = 0;
   519   ws->height = 0;
   520   ws->label_width = 0;
   521   ws->button_width = 0;
   522 
   523   for (val = mw->menu.old_stack [level]->contents; val; val = val->next)
   524     {
   525       size_menu_item (mw, val, horizontal_p, &label_width, &rest_width,
   526                       &button_width, &height);
   527       if (horizontal_p)
   528         {
   529           ws->width += label_width + rest_width;
   530           if (height > ws->height)
   531             ws->height = height;
   532         }
   533       else
   534         {
   535           if (label_width > ws->label_width)
   536             ws->label_width = label_width;
   537           if (rest_width > max_rest_width)
   538             max_rest_width = rest_width;
   539           if (button_width > max_button_width)
   540             max_button_width = button_width;
   541           ws->height += height;
   542         }
   543     }
   544 
   545   if (horizontal_p)
   546     ws->label_width = ws->button_width = 0;
   547   else
   548     {
   549       ws->width = ws->label_width + max_rest_width + max_button_width;
   550       ws->button_width = max_button_width;
   551     }
   552 
   553   ws->width += 2 * mw->menu.shadow_thickness;
   554   ws->height += 2 * mw->menu.shadow_thickness;
   555   ws->max_rest_width = max_rest_width;
   556 
   557   if (horizontal_p)
   558     {
   559       ws->width += 2 * mw->menu.margin;
   560       ws->height += 2 * mw->menu.margin;
   561     }
   562 }
   563 
   564 
   565 /* Display code */
   566 
   567 static void
   568 draw_arrow (XlwMenuWidget mw,
   569             Window window,
   570             GC gc,
   571             int x,
   572             int y,
   573             int width,
   574             int down_p)
   575 {
   576   Display *dpy = XtDisplay (mw);
   577   GC top_gc, bottom_gc;
   578   int thickness = mw->menu.shadow_thickness;
   579   int height = width;
   580   XPoint pt[10];
   581   /* alpha = atan (0.5)
   582      factor = (1 + sin (alpha)) / cos (alpha) */
   583   double factor = 1.62;
   584   int thickness2 = thickness * factor;
   585 
   586   y += (MENU_FONT_HEIGHT (mw) - height) / 2;
   587 
   588   if (down_p)
   589     {
   590       top_gc = mw->menu.highlight_shadow_bottom_gc;
   591       bottom_gc = mw->menu.highlight_shadow_top_gc;
   592     }
   593   else
   594     {
   595       top_gc = mw->menu.shadow_top_gc;
   596       bottom_gc = mw->menu.shadow_bottom_gc;
   597     }
   598 
   599   pt[0].x = x;
   600   pt[0].y = y + height;
   601   pt[1].x = x + thickness;
   602   pt[1].y = y + height - thickness2;
   603   pt[2].x = x + thickness2;
   604   pt[2].y = y + thickness2;
   605   pt[3].x = x;
   606   pt[3].y = y;
   607   XFillPolygon (dpy, window, top_gc, pt, 4, Convex, CoordModeOrigin);
   608 
   609   pt[0].x = x;
   610   pt[0].y = y;
   611   pt[1].x = x + thickness;
   612   pt[1].y = y + thickness2;
   613   pt[2].x = x + width - thickness2;
   614   pt[2].y = y + height / 2;
   615   pt[3].x = x + width;
   616   pt[3].y = y + height / 2;
   617   XFillPolygon (dpy, window, top_gc, pt, 4, Convex, CoordModeOrigin);
   618 
   619   pt[0].x = x;
   620   pt[0].y = y + height;
   621   pt[1].x = x + thickness;
   622   pt[1].y = y + height - thickness2;
   623   pt[2].x = x + width - thickness2;
   624   pt[2].y = y + height / 2;
   625   pt[3].x = x + width;
   626   pt[3].y = y + height / 2;
   627   XFillPolygon (dpy, window, bottom_gc, pt, 4, Convex, CoordModeOrigin);
   628 }
   629 
   630 /* Generic draw shadow rectangle function.  It is used to draw shadows
   631    on menus, menu items and also toggle buttons.  When ERASE_P is
   632    true, it clears shadows.  DOWN_P is true when a menu item is pushed
   633    or a button toggled.  TOP_GC and BOTTOM_GC are the graphic contexts
   634    used to draw the top and bottom shadow respectively.  */
   635 static void
   636 draw_shadow_rectangle (XlwMenuWidget mw, Window window, int x, int y,
   637                        int width, int height, int erase_p, int down_p,
   638                        GC top_gc, GC bottom_gc)
   639 {
   640   Display *dpy = XtDisplay (mw);
   641   int thickness = !x && !y ? mw->menu.border_thickness : mw->menu.shadow_thickness;
   642   XPoint points [4];
   643 
   644   /* Choose correct GC with a standard default if NULL.  */
   645   if (erase_p)
   646     {
   647       top_gc = mw->menu.background_gc;
   648       bottom_gc = mw->menu.background_gc;
   649     }
   650   else
   651     {
   652       if (top_gc == NULL)
   653         top_gc = mw->menu.shadow_top_gc;
   654       if (bottom_gc == NULL)
   655         bottom_gc = mw->menu.shadow_bottom_gc;
   656     }
   657 
   658   if (!erase_p && width == height && width == toggle_button_width (mw))
   659     {
   660       points [0].x = x;
   661       points [0].y = y;
   662       points [1].x = x + width;
   663       points [1].y = y;
   664       points [2].x = x + width;
   665       points [2].y = y + height;
   666       points [3].x = x;
   667       points [3].y = y + height;
   668       XFillPolygon (dpy, window,
   669                     down_p ? mw->menu.button_gc : mw->menu.inactive_button_gc,
   670                     points, 4, Convex, CoordModeOrigin);
   671     }
   672 
   673   if (!erase_p && down_p)
   674     {
   675       GC temp;
   676       temp = top_gc;
   677       top_gc = bottom_gc;
   678       bottom_gc = temp;
   679     }
   680 
   681   /* Do draw (or erase) shadows */
   682   points [0].x = x;
   683   points [0].y = y;
   684   points [1].x = x + width;
   685   points [1].y = y;
   686   points [2].x = x + width - thickness;
   687   points [2].y = y + thickness;
   688   points [3].x = x;
   689   points [3].y = y + thickness;
   690   XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
   691   points [0].x = x;
   692   points [0].y = y + thickness;
   693   points [1].x = x;
   694   points [1].y = y + height;
   695   points [2].x = x + thickness;
   696   points [2].y = y + height - thickness;
   697   points [3].x = x + thickness;
   698   points [3].y = y + thickness;
   699   XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
   700   points [0].x = x + width;
   701   points [0].y = y;
   702   points [1].x = x + width - thickness;
   703   points [1].y = y + thickness;
   704   points [2].x = x + width - thickness;
   705   points [2].y = y + height - thickness;
   706   points [3].x = x + width;
   707   points [3].y = y + height - thickness;
   708   XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
   709   points [0].x = x;
   710   points [0].y = y + height;
   711   points [1].x = x + width;
   712   points [1].y = y + height;
   713   points [2].x = x + width;
   714   points [2].y = y + height - thickness;
   715   points [3].x = x + thickness;
   716   points [3].y = y + height - thickness;
   717   XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
   718 }
   719 
   720 
   721 static void
   722 draw_shadow_rhombus (XlwMenuWidget mw, Window window, int x, int y,
   723                      int width, int height, int erase_p, int down_p,
   724                      GC top_gc, GC bottom_gc)
   725 {
   726   Display *dpy = XtDisplay (mw);
   727   int thickness = mw->menu.shadow_thickness;
   728   XPoint points [4];
   729 
   730   /* Choose correct GC with a standard default if NULL */
   731   if (erase_p)
   732     {
   733       top_gc = mw->menu.background_gc;
   734       bottom_gc = mw->menu.background_gc;
   735     }
   736   else
   737     {
   738       if (top_gc == NULL)
   739         top_gc = mw->menu.shadow_top_gc;
   740       if (bottom_gc == NULL)
   741         top_gc = mw->menu.shadow_bottom_gc;
   742     }
   743 
   744   if (!erase_p && width == height && width == radio_button_width (mw))
   745     {
   746       points [0].x = x;
   747       points [0].y = y + width / 2;
   748       points [1].x = x + height / 2;
   749       points [1].y = y + width;
   750       points [2].x = x + height;
   751       points [2].y = y + width / 2;
   752       points [3].x = x + height / 2;
   753       points [3].y = y;
   754       XFillPolygon (dpy, window,
   755                     down_p ? mw->menu.button_gc : mw->menu.inactive_button_gc,
   756                     points, 4, Convex, CoordModeOrigin);
   757     }
   758 
   759   if (!erase_p && down_p)
   760     {
   761       GC temp;
   762       temp = top_gc;
   763       top_gc = bottom_gc;
   764       bottom_gc = temp;
   765     }
   766 
   767   points [0].x = x;
   768   points [0].y = y + height / 2;
   769   points [1].x = x + thickness;
   770   points [1].y = y + height / 2;
   771   points [2].x = x + width / 2;
   772   points [2].y = y + thickness;
   773   points [3].x = x + width / 2;
   774   points [3].y = y;
   775   XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
   776   points [0].x = x + width / 2;
   777   points [0].y = y;
   778   points [1].x = x + width / 2;
   779   points [1].y = y + thickness;
   780   points [2].x = x + width - thickness;
   781   points [2].y = y + height / 2;
   782   points [3].x = x + width;
   783   points [3].y = y + height / 2;
   784   XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
   785   points [0].x = x;
   786   points [0].y = y + height / 2;
   787   points [1].x = x + thickness;
   788   points [1].y = y + height / 2;
   789   points [2].x = x + width / 2;
   790   points [2].y = y + height - thickness;
   791   points [3].x = x + width / 2;
   792   points [3].y = y + height;
   793   XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
   794   points [0].x = x + width / 2;
   795   points [0].y = y + height;
   796   points [1].x = x + width / 2;
   797   points [1].y = y + height - thickness;
   798   points [2].x = x + width - thickness;
   799   points [2].y = y + height / 2;
   800   points [3].x = x + width;
   801   points [3].y = y + height / 2;
   802   XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
   803 }
   804 
   805 
   806 /* Draw a toggle button on widget MW, X window WINDOW.  X/Y is the
   807    top-left corner of the menu item.  SELECTED_P non-zero means the
   808    toggle button is selected.  */
   809 
   810 static void
   811 draw_toggle (XlwMenuWidget mw, Window window, int x, int y, int selected_p,
   812              int highlighted_p)
   813 {
   814   int width, height;
   815   GC top_gc, bottom_gc;
   816 
   817   if (highlighted_p)
   818     {
   819       top_gc = mw->menu.highlight_shadow_top_gc;
   820       bottom_gc = mw->menu.highlight_shadow_bottom_gc;
   821     }
   822   else
   823     {
   824       top_gc = mw->menu.shadow_top_gc;
   825       bottom_gc = mw->menu.shadow_bottom_gc;
   826     }
   827 
   828   width = toggle_button_width (mw);
   829   height = width;
   830   x += mw->menu.horizontal_spacing;
   831   y += (MENU_FONT_ASCENT (mw) - height) / 2;
   832   draw_shadow_rectangle (mw, window, x, y, width, height, False,
   833                          selected_p, top_gc, bottom_gc);
   834 }
   835 
   836 
   837 /* Draw a radio button on widget MW, X window WINDOW.  X/Y is the
   838    top-left corner of the menu item.  SELECTED_P non-zero means the
   839    toggle button is selected.  */
   840 
   841 static void
   842 draw_radio (XlwMenuWidget mw, Window window, int x, int y, int selected_p,
   843             int highlighted_p)
   844 {
   845   int width, height;
   846   GC top_gc, bottom_gc;
   847 
   848   if (highlighted_p)
   849     {
   850       top_gc = mw->menu.highlight_shadow_top_gc;
   851       bottom_gc = mw->menu.highlight_shadow_bottom_gc;
   852     }
   853   else
   854     {
   855       top_gc = mw->menu.shadow_top_gc;
   856       bottom_gc = mw->menu.shadow_bottom_gc;
   857     }
   858 
   859   width = radio_button_width (mw);
   860   height = width;
   861   x += mw->menu.horizontal_spacing;
   862   y += (MENU_FONT_ASCENT (mw) - height) / 2;
   863   draw_shadow_rhombus (mw, window, x, y, width, height, False, selected_p,
   864                        top_gc, bottom_gc);
   865 }
   866 
   867 
   868 /* Draw a menu separator on widget MW, X window WINDOW.  X/Y is the
   869    top-left corner of the menu item.  WIDTH is the width of the
   870    separator to draw.  TYPE is the separator type.  */
   871 
   872 static void
   873 draw_separator (XlwMenuWidget mw,
   874                 Window window,
   875                 int x,
   876                 int y,
   877                 int width,
   878                 enum menu_separator type)
   879 {
   880   Display *dpy = XtDisplay (mw);
   881   XGCValues xgcv;
   882 
   883   switch (type)
   884     {
   885     case SEPARATOR_NO_LINE:
   886       break;
   887 
   888     case SEPARATOR_SINGLE_LINE:
   889       XDrawLine (dpy, window, mw->menu.foreground_gc,
   890                  x, y, x + width, y);
   891       break;
   892 
   893     case SEPARATOR_DOUBLE_LINE:
   894       draw_separator (mw, window, x, y, width, SEPARATOR_SINGLE_LINE);
   895       draw_separator (mw, window, x, y + 2, width, SEPARATOR_SINGLE_LINE);
   896       break;
   897 
   898     case SEPARATOR_SINGLE_DASHED_LINE:
   899       xgcv.line_style = LineOnOffDash;
   900       XChangeGC (dpy, mw->menu.foreground_gc, GCLineStyle, &xgcv);
   901       XDrawLine (dpy, window, mw->menu.foreground_gc,
   902                  x, y, x + width, y);
   903       xgcv.line_style = LineSolid;
   904       XChangeGC (dpy, mw->menu.foreground_gc, GCLineStyle, &xgcv);
   905       break;
   906 
   907     case SEPARATOR_DOUBLE_DASHED_LINE:
   908       draw_separator (mw, window, x, y, width,
   909                       SEPARATOR_SINGLE_DASHED_LINE);
   910       draw_separator (mw, window, x, y + 2, width,
   911                       SEPARATOR_SINGLE_DASHED_LINE);
   912       break;
   913 
   914     case SEPARATOR_SHADOW_ETCHED_IN:
   915       XDrawLine (dpy, window, mw->menu.shadow_bottom_gc,
   916                  x, y, x + width, y);
   917       XDrawLine (dpy, window, mw->menu.shadow_top_gc,
   918                  x, y + 1, x + width, y + 1);
   919       break;
   920 
   921     case SEPARATOR_SHADOW_ETCHED_OUT:
   922       XDrawLine (dpy, window, mw->menu.shadow_top_gc,
   923                  x, y, x + width, y);
   924       XDrawLine (dpy, window, mw->menu.shadow_bottom_gc,
   925                  x, y + 1, x + width, y + 1);
   926       break;
   927 
   928     case SEPARATOR_SHADOW_ETCHED_IN_DASH:
   929       xgcv.line_style = LineOnOffDash;
   930       XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
   931       XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
   932       draw_separator (mw, window, x, y, width, SEPARATOR_SHADOW_ETCHED_IN);
   933       xgcv.line_style = LineSolid;
   934       XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
   935       XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
   936       break;
   937 
   938     case SEPARATOR_SHADOW_ETCHED_OUT_DASH:
   939       xgcv.line_style = LineOnOffDash;
   940       XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
   941       XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
   942       draw_separator (mw, window, x, y, width, SEPARATOR_SHADOW_ETCHED_OUT);
   943       xgcv.line_style = LineSolid;
   944       XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
   945       XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
   946       break;
   947 
   948     case SEPARATOR_SHADOW_DOUBLE_ETCHED_IN:
   949       draw_separator (mw, window, x, y, width, SEPARATOR_SHADOW_ETCHED_IN);
   950       draw_separator (mw, window, x, y + 3, width, SEPARATOR_SHADOW_ETCHED_IN);
   951       break;
   952 
   953     case SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT:
   954       draw_separator (mw, window, x, y, width,
   955                       SEPARATOR_SHADOW_ETCHED_OUT);
   956       draw_separator (mw, window, x, y + 3, width,
   957                       SEPARATOR_SHADOW_ETCHED_OUT);
   958       break;
   959 
   960     case SEPARATOR_SHADOW_DOUBLE_ETCHED_IN_DASH:
   961       xgcv.line_style = LineOnOffDash;
   962       XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
   963       XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
   964       draw_separator (mw, window, x, y, width,
   965                       SEPARATOR_SHADOW_DOUBLE_ETCHED_IN);
   966       xgcv.line_style = LineSolid;
   967       XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
   968       XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
   969       break;
   970 
   971     case SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT_DASH:
   972       xgcv.line_style = LineOnOffDash;
   973       XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
   974       XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
   975       draw_separator (mw, window, x, y, width,
   976                       SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT);
   977       xgcv.line_style = LineSolid;
   978       XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
   979       XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
   980       break;
   981 
   982     default:
   983       emacs_abort ();
   984     }
   985 }
   986 
   987 
   988 /* Return the pixel height of menu separator SEPARATOR.  */
   989 
   990 static int
   991 separator_height (enum menu_separator separator)
   992 {
   993   switch (separator)
   994     {
   995     case SEPARATOR_NO_LINE:
   996       return 2;
   997 
   998     case SEPARATOR_SINGLE_LINE:
   999     case SEPARATOR_SINGLE_DASHED_LINE:
  1000       return 1;
  1001 
  1002     case SEPARATOR_DOUBLE_LINE:
  1003     case SEPARATOR_DOUBLE_DASHED_LINE:
  1004       return 3;
  1005 
  1006     case SEPARATOR_SHADOW_ETCHED_IN:
  1007     case SEPARATOR_SHADOW_ETCHED_OUT:
  1008     case SEPARATOR_SHADOW_ETCHED_IN_DASH:
  1009     case SEPARATOR_SHADOW_ETCHED_OUT_DASH:
  1010       return 2;
  1011 
  1012     case SEPARATOR_SHADOW_DOUBLE_ETCHED_IN:
  1013     case SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT:
  1014     case SEPARATOR_SHADOW_DOUBLE_ETCHED_IN_DASH:
  1015     case SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT_DASH:
  1016       return 5;
  1017 
  1018     default:
  1019       emacs_abort ();
  1020     }
  1021 }
  1022 
  1023 /* Draw the highlighted background and shadows.  */
  1024 
  1025 static void
  1026 draw_highlight (XlwMenuWidget mw, Window window, int x, int y, int width,
  1027                 int height)
  1028 {
  1029   Display *dpy = XtDisplay (mw);
  1030   XPoint points [4];
  1031 
  1032   points [0].x = x;
  1033   points [0].y = y;
  1034   points [1].x = x + width;
  1035   points [1].y = y;
  1036   points [2].x = x + width;
  1037   points [2].y = y + height;
  1038   points [3].x = x;
  1039   points [3].y = y + height;
  1040   XFillPolygon (dpy, window,
  1041                 mw->menu.highlight_background_gc,
  1042                 points, 4, Convex, CoordModeOrigin);
  1043 
  1044   draw_shadow_rectangle(mw, window, x, y, width, height, False, False,
  1045                         mw->menu.highlight_shadow_top_gc,
  1046                         mw->menu.highlight_shadow_bottom_gc);
  1047 }
  1048 
  1049 /* Display the menu item and increment where.x and where.y to show how large
  1050    the menu item was.  */
  1051 
  1052 static void
  1053 display_menu_item (XlwMenuWidget mw,
  1054                    widget_value* val,
  1055                    window_state* ws,
  1056                    XPoint* where,
  1057                    Boolean highlighted_p,
  1058                    Boolean horizontal_p,
  1059                    Boolean just_compute_p)
  1060 {
  1061   GC deco_gc;
  1062   GC text_gc;
  1063   int font_ascent = MENU_FONT_ASCENT (mw);
  1064   int shadow = mw->menu.shadow_thickness;
  1065   int margin = mw->menu.margin;
  1066   int h_spacing = mw->menu.horizontal_spacing;
  1067   int v_spacing = mw->menu.vertical_spacing;
  1068   int label_width;
  1069   int rest_width;
  1070   int button_width;
  1071   int height;
  1072   int width;
  1073   enum menu_separator separator;
  1074   int separator_p = lw_separator_p (val->name, &separator, 0);
  1075 #if defined USE_CAIRO || defined HAVE_XFT
  1076   XftColor *xftfg;
  1077 #endif
  1078 
  1079   /* compute the sizes of the item */
  1080   size_menu_item (mw, val, horizontal_p, &label_width, &rest_width,
  1081                   &button_width, &height);
  1082 
  1083   if (horizontal_p)
  1084     width = label_width + rest_width;
  1085   else
  1086     {
  1087       label_width = ws->label_width;
  1088       width = ws->width - 2 * shadow;
  1089     }
  1090 
  1091   /* Only highlight an enabled item that has a callback. */
  1092   if (highlighted_p)
  1093     if (!val->enabled || !(val->call_data || val->contents))
  1094       highlighted_p = 0;
  1095 
  1096   /* do the drawing. */
  1097   if (!just_compute_p)
  1098     {
  1099       /* Add the shadow border of the containing menu */
  1100       int x = where->x + shadow;
  1101       int y = where->y + shadow;
  1102 
  1103       if (horizontal_p)
  1104         {
  1105           x += margin;
  1106           y += margin;
  1107         }
  1108 
  1109       /* pick the foreground and background GC. */
  1110       if (val->enabled)
  1111         if (highlighted_p)
  1112           text_gc = mw->menu.highlight_foreground_gc;
  1113         else
  1114           text_gc = mw->menu.foreground_gc;
  1115       else
  1116         text_gc = mw->menu.disabled_gc;
  1117       deco_gc = mw->menu.foreground_gc;
  1118 #if defined USE_CAIRO || defined HAVE_XFT
  1119       if (val->enabled)
  1120         if (highlighted_p)
  1121           xftfg = &mw->menu.xft_highlight_fg;
  1122         else
  1123           xftfg = &mw->menu.xft_fg;
  1124       else
  1125         xftfg = &mw->menu.xft_disabled_fg;
  1126 #endif
  1127 
  1128       if (separator_p)
  1129         draw_separator (mw, ws->pixmap, x, y, width, separator);
  1130       else
  1131         {
  1132           int x_offset = x + h_spacing + shadow;
  1133           char* display_string = resource_widget_value (mw, val);
  1134           /* Clears shadows and maybe highlight */
  1135           draw_shadow_rectangle (mw, ws->pixmap, x, y, width, height,
  1136                                  True, False, NULL, NULL);
  1137           if (highlighted_p)
  1138             draw_highlight (mw, ws->pixmap, x, y, width, height);
  1139 
  1140           /* Deal with centering a menu title. */
  1141           if (!horizontal_p && !val->contents && !val->call_data)
  1142             {
  1143               int l = string_width (mw, display_string);
  1144 
  1145               if (width > l)
  1146                 x_offset = (width - l) >> 1;
  1147             }
  1148           else if (!horizontal_p && ws->button_width)
  1149             x_offset += ws->button_width;
  1150 
  1151 
  1152 #if defined USE_CAIRO || defined HAVE_XFT
  1153           if (ws->xft_draw)
  1154             {
  1155               int draw_y = y + v_spacing + shadow;
  1156 #ifdef USE_CAIRO
  1157               cairo_surface_mark_dirty (cairo_get_target (ws->xft_draw));
  1158 #endif
  1159               XftDrawStringUtf8 (ws->xft_draw, xftfg,
  1160                                  mw->menu.xft_font,
  1161                                  x_offset, draw_y + font_ascent,
  1162                                  (unsigned char *) display_string,
  1163                                  strlen (display_string));
  1164             }
  1165           else
  1166 #endif
  1167 #ifdef HAVE_X_I18N
  1168           if (mw->menu.fontSet)
  1169             XmbDrawString (XtDisplay (mw), ws->pixmap, mw->menu.fontSet,
  1170                            text_gc, x_offset,
  1171                            y + v_spacing + shadow + font_ascent,
  1172                            display_string, strlen (display_string));
  1173           else
  1174 #endif
  1175           XDrawString (XtDisplay (mw), ws->pixmap,
  1176                        text_gc, x_offset,
  1177                        y + v_spacing + shadow + font_ascent,
  1178                        display_string, strlen (display_string));
  1179 
  1180           if (!horizontal_p)
  1181             {
  1182               if (val->button_type == BUTTON_TYPE_TOGGLE)
  1183                 draw_toggle (mw, ws->pixmap, x, y + v_spacing + shadow,
  1184                              val->selected, highlighted_p);
  1185               else if (val->button_type == BUTTON_TYPE_RADIO)
  1186                 draw_radio (mw, ws->pixmap, x, y + v_spacing + shadow,
  1187                             val->selected, highlighted_p);
  1188 
  1189               if (val->contents)
  1190                 {
  1191                   int a_w = arrow_width (mw);
  1192                   draw_arrow (mw, ws->pixmap, deco_gc,
  1193                               x + width - a_w
  1194                               - mw->menu.horizontal_spacing
  1195                               - mw->menu.shadow_thickness,
  1196                               y + v_spacing + shadow, a_w,
  1197                               highlighted_p);
  1198                 }
  1199               else if (val->key)
  1200                 {
  1201 #if defined USE_CAIRO || defined HAVE_XFT
  1202                   if (ws->xft_draw)
  1203                     {
  1204                       int draw_x = ws->width - ws->max_rest_width
  1205                         + mw->menu.arrow_spacing;
  1206                       int draw_y = y + v_spacing + shadow + font_ascent;
  1207                       XftDrawStringUtf8 (ws->xft_draw, xftfg,
  1208                                          mw->menu.xft_font,
  1209                                          draw_x, draw_y,
  1210                                          (unsigned char *) val->key,
  1211                                          strlen (val->key));
  1212                     }
  1213                   else
  1214 #endif
  1215 #ifdef HAVE_X_I18N
  1216                   if (mw->menu.fontSet)
  1217                     XmbDrawString (XtDisplay (mw), ws->pixmap,
  1218                                    mw->menu.fontSet,
  1219                                    text_gc,
  1220                                    x + label_width + mw->menu.arrow_spacing,
  1221                                    y + v_spacing + shadow + font_ascent,
  1222                                    val->key, strlen (val->key));
  1223                   else
  1224 #endif
  1225                   XDrawString (XtDisplay (mw), ws->pixmap,
  1226                                text_gc,
  1227                                x + label_width + mw->menu.arrow_spacing,
  1228                                y + v_spacing + shadow + font_ascent,
  1229                                val->key, strlen (val->key));
  1230                 }
  1231             }
  1232           else
  1233             {
  1234               /* If not highlighted, clears shadows for horizontal
  1235                  menu item */
  1236               if (!highlighted_p)
  1237                 draw_shadow_rectangle (mw, ws->pixmap, x, y, width, height,
  1238                                        True, False, NULL, NULL);
  1239             }
  1240 #ifdef USE_CAIRO
  1241           if (ws->xft_draw)
  1242             cairo_surface_flush (cairo_get_target (ws->xft_draw));
  1243 #endif
  1244         }
  1245     }
  1246   where->x += width;
  1247   where->y += height;
  1248 }
  1249 
  1250 static void
  1251 display_menu (XlwMenuWidget mw,
  1252               int level,
  1253               Boolean just_compute_p,
  1254               XPoint *highlighted_pos,
  1255               XPoint *hit,
  1256               widget_value **hit_return)
  1257 {
  1258   widget_value* val;
  1259   widget_value* following_item;
  1260   window_state* ws;
  1261   XPoint        where;
  1262   int horizontal_p = mw->menu.horizontal && (level == 0);
  1263   int highlighted_p;
  1264   int no_return = 0;
  1265   enum menu_separator separator;
  1266 
  1267   if (level >= mw->menu.old_depth)
  1268     abort_gracefully ((Widget) mw);
  1269 
  1270   if (level < mw->menu.old_depth - 1)
  1271     following_item = mw->menu.old_stack [level + 1];
  1272   else
  1273     following_item = NULL;
  1274 
  1275   if (hit)
  1276     *hit_return = NULL;
  1277 
  1278   where.x = 0;
  1279   where.y = 0;
  1280 
  1281   ws = &mw->menu.windows [level];
  1282 
  1283   if (!just_compute_p)
  1284     XFillRectangle (XtDisplay (mw), ws->pixmap, mw->menu.background_gc,
  1285                     0, 0, ws->width, ws->height);
  1286 
  1287   for (val = mw->menu.old_stack [level]->contents; val; val = val->next)
  1288     {
  1289       highlighted_p = val == following_item;
  1290       if (highlighted_p && highlighted_pos)
  1291         {
  1292           if (horizontal_p)
  1293             highlighted_pos->x = where.x;
  1294           else
  1295             highlighted_pos->y = where.y;
  1296         }
  1297 
  1298       display_menu_item (mw, val, ws, &where, highlighted_p, horizontal_p,
  1299                          just_compute_p);
  1300 
  1301       if (highlighted_p && highlighted_pos)
  1302         {
  1303           if (horizontal_p)
  1304             highlighted_pos->y = where.y;
  1305           else
  1306             highlighted_pos->x = where.x;
  1307         }
  1308 
  1309       if (hit
  1310           && !*hit_return
  1311           && (horizontal_p ? hit->x < where.x : hit->y < where.y)
  1312           && !lw_separator_p (val->name, &separator, 0)
  1313           && !no_return)
  1314         {
  1315           if (val->enabled)
  1316             *hit_return = val;
  1317           else
  1318             no_return = 1;
  1319           if (mw->menu.inside_entry != val)
  1320             {
  1321               if (mw->menu.inside_entry)
  1322                 XtCallCallbackList ((Widget)mw, mw->menu.leave,
  1323                                     (XtPointer) mw->menu.inside_entry);
  1324               mw->menu.inside_entry = val;
  1325               XtCallCallbackList ((Widget)mw, mw->menu.enter,
  1326                                   (XtPointer) mw->menu.inside_entry);
  1327             }
  1328         }
  1329 
  1330       if (horizontal_p)
  1331         where.y = 0;
  1332       else
  1333         where.x = 0;
  1334     }
  1335 
  1336   if (!just_compute_p)
  1337     {
  1338       draw_shadow_rectangle (mw, ws->pixmap, 0, 0, ws->width, ws->height,
  1339                              False, False, NULL, NULL);
  1340       XCopyArea (XtDisplay (mw), ws->pixmap, ws->window,
  1341                  mw->menu.foreground_gc, 0, 0, ws->width, ws->height, 0, 0);
  1342     }
  1343 }
  1344 
  1345 /* Motion code */
  1346 static void
  1347 set_new_state (XlwMenuWidget mw, widget_value *val, int level)
  1348 {
  1349   int i;
  1350 
  1351   mw->menu.new_depth = 0;
  1352   for (i = 0; i < level; i++)
  1353     push_new_stack (mw, mw->menu.old_stack [i]);
  1354   push_new_stack (mw, val);
  1355 }
  1356 
  1357 static void
  1358 expose_cb (Widget widget,
  1359            XtPointer closure,
  1360            XEvent* event,
  1361            Boolean* continue_to_dispatch)
  1362 {
  1363   XlwMenuWidget mw = (XlwMenuWidget) closure;
  1364   int i;
  1365 
  1366   *continue_to_dispatch = False;
  1367   for (i = 0; i < mw->menu.windows_length; ++i)
  1368     if (mw->menu.windows [i].w == widget) break;
  1369   if (i < mw->menu.windows_length && i < mw->menu.old_depth)
  1370     display_menu (mw, i, False, NULL, NULL, NULL);
  1371 }
  1372 
  1373 static void
  1374 set_window_type (Widget w, XlwMenuWidget mw)
  1375 {
  1376   int popup_menu_p = mw->menu.top_depth == 1;
  1377   Atom type = XInternAtom (XtDisplay (w),
  1378                            popup_menu_p
  1379                            ? "_NET_WM_WINDOW_TYPE_POPUP_MENU"
  1380                            : "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU",
  1381                            False);
  1382 
  1383   XChangeProperty (XtDisplay (w), XtWindow (w),
  1384                    XInternAtom (XtDisplay (w), "_NET_WM_WINDOW_TYPE", False),
  1385                    XA_ATOM, 32, PropModeReplace,
  1386                    (unsigned char *)&type, 1);
  1387 }
  1388 
  1389 
  1390 static void
  1391 make_windows_if_needed (XlwMenuWidget mw, int n)
  1392 {
  1393   int i;
  1394   int start_at;
  1395   window_state* windows;
  1396 
  1397   if (mw->menu.windows_length >= n)
  1398     return;
  1399 
  1400   if (!mw->menu.windows)
  1401     {
  1402       mw->menu.windows =
  1403         (window_state*)XtMalloc (n * sizeof (window_state));
  1404       start_at = 0;
  1405     }
  1406   else
  1407     {
  1408       mw->menu.windows =
  1409         (window_state*)XtRealloc ((char*)mw->menu.windows,
  1410                                   n * sizeof (window_state));
  1411       start_at = mw->menu.windows_length;
  1412     }
  1413   mw->menu.windows_length = n;
  1414 
  1415   windows = mw->menu.windows;
  1416 
  1417   for (i = start_at; i < n; i++)
  1418    {
  1419      Arg av[10];
  1420      int ac = 0;
  1421      windows [i].x = 0;
  1422      windows [i].y = 0;
  1423      windows [i].width = 1;
  1424      windows [i].height = 1;
  1425      windows [i].max_rest_width = 0;
  1426      XtSetArg (av[ac], XtNwidth, 1); ++ac;
  1427      XtSetArg (av[ac], XtNheight, 1); ++ac;
  1428      XtSetArg (av[ac], XtNsaveUnder, True); ++ac;
  1429      XtSetArg (av[ac], XtNbackground, mw->core.background_pixel); ++ac;
  1430      XtSetArg (av[ac], XtNborderColor, mw->core.border_pixel); ++ac;
  1431      XtSetArg (av[ac], XtNborderWidth, mw->core.border_width); ++ac;
  1432      XtSetArg (av[ac], XtNcursor, mw->menu.cursor_shape); ++ac;
  1433      windows [i].w =
  1434        XtCreatePopupShell ("sub", overrideShellWidgetClass,
  1435                            (Widget) mw, av, ac);
  1436      XtRealizeWidget (windows [i].w);
  1437      XtAddEventHandler (windows [i].w, ExposureMask, False, expose_cb, mw);
  1438      windows [i].window = XtWindow (windows [i].w);
  1439      windows [i].pixmap = None;
  1440 #if defined USE_CAIRO || defined HAVE_XFT
  1441      windows [i].xft_draw = 0;
  1442 #endif
  1443      set_window_type (windows [i].w, mw);
  1444    }
  1445   XFlush (XtDisplay (mw));
  1446 }
  1447 
  1448 /* Value is non-zero if WINDOW is part of menu bar widget W.  */
  1449 
  1450 int
  1451 xlwmenu_window_p (Widget w, Window window)
  1452 {
  1453   XlwMenuWidget mw = (XlwMenuWidget) w;
  1454   int i;
  1455 
  1456   for (i = 0; i < mw->menu.windows_length; ++i)
  1457     if (window == mw->menu.windows[i].window)
  1458       break;
  1459 
  1460   return i < mw->menu.windows_length;
  1461 }
  1462 
  1463 /* Make the window fit in the screen */
  1464 static void
  1465 fit_to_screen (XlwMenuWidget mw,
  1466                window_state *ws,
  1467                window_state *previous_ws,
  1468                Boolean horizontal_p)
  1469 {
  1470   int screen_width, screen_height;
  1471   int screen_x, screen_y;
  1472   int prev_screen_x, prev_screen_y;
  1473 
  1474 #ifdef emacs
  1475   xlw_monitor_dimensions_at_pos (XtDisplay (mw), XtScreen (mw),
  1476                                  previous_ws->x, previous_ws->y,
  1477                                  &prev_screen_x, &prev_screen_y,
  1478                                  &screen_width, &screen_height);
  1479   xlw_monitor_dimensions_at_pos (XtDisplay (mw), XtScreen (mw),
  1480                                  ws->x, ws->y, &screen_x, &screen_y,
  1481                                  &screen_width, &screen_height);
  1482 #else
  1483   screen_width = WidthOfScreen (XtScreen (mw));
  1484   screen_height = HeightOfScreen (XtScreen (mw));
  1485   prev_screen_x = screen_x = 0;
  1486   prev_screen_y = screen_y = 0;
  1487 #endif
  1488   /* 1 if we are unable to avoid an overlap between
  1489      this menu and the parent menu in the X dimension.  */
  1490   int horizontal_overlap = 0;
  1491 
  1492   if (ws->x < screen_x)
  1493     ws->x = screen_x;
  1494   else if (ws->x + ws->width > screen_x + screen_width)
  1495     {
  1496       if (!horizontal_p)
  1497         /* The addition of shadow-thickness for a sub-menu's position is
  1498            to reflect a similar adjustment when the menu is displayed to
  1499            the right of the invoking menu-item; it makes the sub-menu
  1500            look more `attached' to the menu-item.  */
  1501         ws->x = screen_x + (previous_ws->x
  1502                             - prev_screen_x
  1503                             - ws->width
  1504                             + mw->menu.shadow_thickness);
  1505       else
  1506         ws->x = screen_x + (screen_width - ws->width);
  1507       if (ws->x < screen_x)
  1508         {
  1509           ws->x = screen_x;
  1510           horizontal_overlap = 1;
  1511         }
  1512     }
  1513   /* If we overlap in X, try to avoid overlap in Y.  */
  1514   if (horizontal_overlap
  1515       && ws->y < previous_ws->y + previous_ws->height
  1516       && previous_ws->y < ws->y + ws->height)
  1517     {
  1518       /* Put this menu right below or right above PREVIOUS_WS
  1519          if there's room.  */
  1520       if (previous_ws->y + previous_ws->height + ws->height < screen_height)
  1521         ws->y = previous_ws->y + previous_ws->height;
  1522       else if (previous_ws->y - ws->height > 0)
  1523         ws->y = previous_ws->y - ws->height;
  1524     }
  1525 
  1526   if (ws->y < screen_y)
  1527     ws->y = screen_y;
  1528   else if (ws->y + ws->height > screen_y + screen_height)
  1529     {
  1530       if (horizontal_p)
  1531         ws->y = screen_y + (previous_ws->y
  1532                             - prev_screen_y
  1533                             - ws->height);
  1534       else
  1535         ws->y = screen_y + (screen_height - ws->height);
  1536       if (ws->y < screen_y)
  1537         ws->y = screen_y;
  1538     }
  1539 }
  1540 
  1541 static void
  1542 create_pixmap_for_menu (window_state* ws, XlwMenuWidget mw)
  1543 {
  1544   if (ws->pixmap != None)
  1545     {
  1546       XFreePixmap (XtDisplay (ws->w), ws->pixmap);
  1547       ws->pixmap = None;
  1548     }
  1549   ws->pixmap = XCreatePixmap (XtDisplay (ws->w), ws->window,
  1550                               ws->width, ws->height,
  1551                               DefaultDepthOfScreen (XtScreen (ws->w)));
  1552 #if defined USE_CAIRO || defined HAVE_XFT
  1553   if (ws->xft_draw)
  1554     XftDrawDestroy (ws->xft_draw);
  1555   if (mw->menu.xft_font)
  1556     {
  1557       int screen = XScreenNumberOfScreen (mw->core.screen);
  1558       ws->xft_draw = XftDrawCreate (XtDisplay (ws->w),
  1559                                     ws->pixmap,
  1560                                     DefaultVisual (XtDisplay (ws->w), screen),
  1561                                     mw->core.colormap);
  1562     }
  1563   else
  1564     ws->xft_draw = 0;
  1565 #endif
  1566 }
  1567 
  1568 /* Updates old_stack from new_stack and redisplays. */
  1569 static void
  1570 remap_menubar (XlwMenuWidget mw)
  1571 {
  1572   int i;
  1573   int last_same;
  1574   XPoint selection_position;
  1575   int old_depth = mw->menu.old_depth;
  1576   int new_depth = mw->menu.new_depth;
  1577   widget_value** old_stack;
  1578   widget_value** new_stack;
  1579   window_state* windows;
  1580   widget_value* old_selection;
  1581   widget_value* new_selection;
  1582 
  1583   /* Check that enough windows and old_stack are ready. */
  1584   make_windows_if_needed (mw, new_depth);
  1585   make_old_stack_space (mw, new_depth);
  1586   windows = mw->menu.windows;
  1587   old_stack = mw->menu.old_stack;
  1588   new_stack = mw->menu.new_stack;
  1589 
  1590   /* compute the last identical different entry */
  1591   for (i = 1; i < old_depth && i < new_depth; i++)
  1592     if (old_stack [i] != new_stack [i])
  1593       break;
  1594   last_same = i - 1;
  1595 
  1596   /* Memorize the previously selected item to be able to refresh it */
  1597   old_selection = last_same + 1 < old_depth ? old_stack [last_same + 1] : NULL;
  1598   if (old_selection && !old_selection->enabled)
  1599     old_selection = NULL;
  1600   new_selection = last_same + 1 < new_depth ? new_stack [last_same + 1] : NULL;
  1601   if (new_selection && !new_selection->enabled)
  1602     new_selection = NULL;
  1603 
  1604   /* Call callback when the highlighted item changes.  */
  1605   if (old_selection || new_selection)
  1606     XtCallCallbackList ((Widget)mw, mw->menu.highlight,
  1607                         (XtPointer) new_selection);
  1608 
  1609   /* updates old_state from new_state.  It has to be done now because
  1610      display_menu (called below) uses the old_stack to know what to display. */
  1611   for (i = last_same + 1; i < new_depth; i++)
  1612     {
  1613       XtPopdown (mw->menu.windows [i].w);
  1614       old_stack [i] = new_stack [i];
  1615     }
  1616   mw->menu.old_depth = new_depth;
  1617 
  1618   /* refresh the last selection */
  1619   selection_position.x = 0;
  1620   selection_position.y = 0;
  1621   display_menu (mw, last_same, new_selection == old_selection,
  1622                 &selection_position, NULL, NULL);
  1623 
  1624   /* Now place the new menus.  */
  1625   for (i = last_same + 1; i < new_depth && new_stack[i]->contents; i++)
  1626     {
  1627       window_state *previous_ws = &windows[i - 1];
  1628       window_state *ws = &windows[i];
  1629 
  1630       ws->x = (previous_ws->x + selection_position.x
  1631                + mw->menu.shadow_thickness);
  1632       if (mw->menu.horizontal && i == 1)
  1633         ws->x += mw->menu.margin;
  1634 
  1635 #if 0
  1636       if (!mw->menu.horizontal || i > 1)
  1637         ws->x += mw->menu.shadow_thickness;
  1638 #endif
  1639 
  1640       ws->y = (previous_ws->y + selection_position.y
  1641                + mw->menu.shadow_thickness);
  1642       if (mw->menu.horizontal && i == 1)
  1643         ws->y += mw->menu.margin;
  1644 
  1645       /* WMs like Gnome 3 ignores requests to move windows.  So we
  1646          must destroy the current one and create a new to get it to move.  */
  1647       XtUnrealizeWidget (ws->w);
  1648       XtRealizeWidget (ws->w);
  1649       ws->window = XtWindow (ws->w);
  1650 
  1651       size_menu (mw, i);
  1652 
  1653       fit_to_screen (mw, ws, previous_ws, mw->menu.horizontal && i == 1);
  1654 
  1655       create_pixmap_for_menu (ws, mw);
  1656       XtConfigureWidget (ws->w, ws->x, ws->y, ws->width, ws->height,
  1657                          ws->w->core.border_width);
  1658       display_menu (mw, i, False, &selection_position, NULL, NULL);
  1659       XtPopup (ws->w, XtGrabNone);
  1660     }
  1661 
  1662   /* unmap the menus that popped down */
  1663   for (i = new_depth - 1; i < old_depth; i++)
  1664     if (i >= new_depth || (i > 0 && !new_stack[i]->contents))
  1665       XtPopdown (windows[i].w);
  1666 }
  1667 
  1668 static Boolean
  1669 motion_event_is_in_menu (XlwMenuWidget mw,
  1670                          XMotionEvent *ev,
  1671                          int level,
  1672                          XPoint *relative_pos)
  1673 {
  1674   window_state* ws = &mw->menu.windows [level];
  1675   int shadow = level == 0 ? 0 : mw->menu.shadow_thickness;
  1676   int x = ws->x + shadow;
  1677   int y = ws->y + shadow;
  1678   relative_pos->x = ev->x_root - x;
  1679   relative_pos->y = ev->y_root - y;
  1680   return (x - shadow < ev->x_root && ev->x_root < x + ws->width
  1681           && y - shadow < ev->y_root && ev->y_root < y + ws->height);
  1682 }
  1683 
  1684 static Boolean
  1685 map_event_to_widget_value (XlwMenuWidget mw,
  1686                            XMotionEvent *ev,
  1687                            widget_value **val,
  1688                            int *level)
  1689 {
  1690   int           i;
  1691   XPoint        relative_pos;
  1692   window_state* ws;
  1693   int inside = 0;
  1694 
  1695   *val = NULL;
  1696 
  1697   /* Find the window */
  1698   for (i = mw->menu.old_depth - 1; i >= 0; i--)
  1699     {
  1700       ws = &mw->menu.windows [i];
  1701       if (ws && motion_event_is_in_menu (mw, ev, i, &relative_pos))
  1702         {
  1703           inside = 1;
  1704           display_menu (mw, i, True, NULL, &relative_pos, val);
  1705 
  1706           if (*val)
  1707             {
  1708               *level = i + 1;
  1709               return True;
  1710             }
  1711         }
  1712     }
  1713 
  1714   if (!inside)
  1715     {
  1716       if (mw->menu.inside_entry != NULL)
  1717         XtCallCallbackList ((Widget)mw, mw->menu.leave,
  1718                             (XtPointer) mw->menu.inside_entry);
  1719       mw->menu.inside_entry = NULL;
  1720     }
  1721 
  1722   return False;
  1723 }
  1724 
  1725 
  1726 static void
  1727 make_drawing_gcs (XlwMenuWidget mw)
  1728 {
  1729   XGCValues xgcv;
  1730   float scale;
  1731   XtGCMask mask = GCForeground | GCBackground;
  1732 
  1733 #ifdef HAVE_X_I18N
  1734   if (!mw->menu.fontSet && mw->menu.font)
  1735     {
  1736       xgcv.font = mw->menu.font->fid;
  1737       mask |= GCFont;
  1738     }
  1739 #else
  1740   if (mw->menu.font)
  1741     {
  1742       xgcv.font = mw->menu.font->fid;
  1743       mask |= GCFont;
  1744     }
  1745 #endif
  1746   xgcv.foreground = mw->menu.foreground;
  1747   xgcv.background = mw->core.background_pixel;
  1748   mw->menu.foreground_gc = XtGetGC ((Widget)mw, mask, &xgcv);
  1749 
  1750   xgcv.foreground = mw->menu.button_foreground;
  1751   mw->menu.button_gc = XtGetGC ((Widget)mw, mask, &xgcv);
  1752 
  1753   xgcv.background = mw->core.background_pixel;
  1754 
  1755 #define BRIGHTNESS(color) (((color) & 0xff) + (((color) >> 8) & 0xff) + (((color) >> 16) & 0xff))
  1756 
  1757   /* Allocate color for disabled menu-items.  */
  1758   if (BRIGHTNESS(mw->menu.foreground) < BRIGHTNESS(mw->core.background_pixel))
  1759     scale = 2.3;
  1760   else
  1761     scale = 0.55;
  1762 
  1763   x_alloc_lighter_color_for_widget ((Widget) mw, XtDisplay ((Widget) mw),
  1764                                     mw->core.colormap,
  1765                                     &mw->menu.disabled_foreground,
  1766                                     scale,
  1767                                     0x8000);
  1768 
  1769   if (mw->menu.foreground == mw->menu.disabled_foreground
  1770       || mw->core.background_pixel == mw->menu.disabled_foreground)
  1771     {
  1772       /* Too few colors, use stipple.  */
  1773       xgcv.foreground = mw->menu.foreground;
  1774       xgcv.fill_style = FillStippled;
  1775       xgcv.stipple = mw->menu.gray_pixmap;
  1776       mw->menu.disabled_gc = XtGetGC ((Widget)mw, mask
  1777                                       | GCFillStyle | GCStipple, &xgcv);
  1778     }
  1779   else
  1780     {
  1781       /* Many colors available, use disabled pixel.  */
  1782       xgcv.foreground = mw->menu.disabled_foreground;
  1783       mw->menu.disabled_gc = XtGetGC ((Widget)mw, mask, &xgcv);
  1784     }
  1785 
  1786   xgcv.foreground = mw->menu.button_foreground;
  1787   xgcv.background = mw->core.background_pixel;
  1788   xgcv.fill_style = FillStippled;
  1789   xgcv.stipple = mw->menu.gray_pixmap;
  1790   mw->menu.inactive_button_gc = XtGetGC ((Widget)mw, mask
  1791                                          | GCFillStyle | GCStipple, &xgcv);
  1792 
  1793   xgcv.foreground = mw->core.background_pixel;
  1794   xgcv.background = mw->menu.foreground;
  1795   mw->menu.background_gc = XtGetGC ((Widget)mw, mask, &xgcv);
  1796 
  1797   xgcv.foreground = ((mw->menu.highlight_foreground == -1)
  1798                      ? mw->menu.foreground
  1799                      : mw->menu.highlight_foreground);
  1800   xgcv.background = ((mw->menu.highlight_background == -1)
  1801                      ? mw->core.background_pixel
  1802                      : mw->menu.highlight_background);
  1803   mw->menu.highlight_foreground_gc = XtGetGC ((Widget)mw, mask, &xgcv);
  1804 
  1805   xgcv.foreground = ((mw->menu.highlight_background == -1)
  1806                      ? mw->core.background_pixel
  1807                      : mw->menu.highlight_background);
  1808   xgcv.background = mw->menu.foreground;
  1809   mw->menu.highlight_background_gc = XtGetGC ((Widget)mw, mask, &xgcv);
  1810 }
  1811 
  1812 static void
  1813 release_drawing_gcs (XlwMenuWidget mw)
  1814 {
  1815   XtReleaseGC ((Widget) mw, mw->menu.foreground_gc);
  1816   XtReleaseGC ((Widget) mw, mw->menu.button_gc);
  1817   XtReleaseGC ((Widget) mw, mw->menu.disabled_gc);
  1818   XtReleaseGC ((Widget) mw, mw->menu.inactive_button_gc);
  1819   XtReleaseGC ((Widget) mw, mw->menu.background_gc);
  1820   XtReleaseGC ((Widget) mw, mw->menu.highlight_foreground_gc);
  1821   XtReleaseGC ((Widget) mw, mw->menu.highlight_background_gc);
  1822   /* let's get some segvs if we try to use these... */
  1823   mw->menu.foreground_gc = (GC) -1;
  1824   mw->menu.button_gc = (GC) -1;
  1825   mw->menu.disabled_gc = (GC) -1;
  1826   mw->menu.inactive_button_gc = (GC) -1;
  1827   mw->menu.background_gc = (GC) -1;
  1828   mw->menu.highlight_foreground_gc = (GC) -1;
  1829   mw->menu.highlight_background_gc = (GC) -1;
  1830 }
  1831 
  1832 #ifndef emacs
  1833 #define MINL(x,y) ((((unsigned long) (x)) < ((unsigned long) (y))) \
  1834                    ? ((unsigned long) (x)) : ((unsigned long) (y)))
  1835 #endif
  1836 
  1837 static void
  1838 compute_shadow_colors (XlwMenuWidget mw, Pixel *top_color, Pixel *bottom_color,
  1839                        Boolean *free_top_p, Boolean *free_bottom_p,
  1840                        Pixmap *top_pixmap, Pixmap *bottom_pixmap,
  1841                        Pixel fore_color, Pixel back_color)
  1842 {
  1843   Display *dpy = XtDisplay ((Widget) mw);
  1844   Screen *screen = XtScreen ((Widget) mw);
  1845   Colormap cmap = mw->core.colormap;
  1846   XColor topc, botc;
  1847   int top_frobbed = 0, bottom_frobbed = 0;
  1848 
  1849   *free_top_p = False;
  1850   *free_bottom_p = False;
  1851 
  1852   if (*top_color == -1)
  1853     *top_color = back_color;
  1854 
  1855   if (*bottom_color == -1)
  1856     *bottom_color = fore_color;
  1857 
  1858   if (*top_color == back_color || *top_color == fore_color)
  1859     {
  1860       topc.pixel = back_color;
  1861 #ifdef emacs
  1862       if (x_alloc_lighter_color_for_widget ((Widget) mw, dpy, cmap,
  1863                                             &topc.pixel,
  1864                                             1.2, 0x8000))
  1865 #else
  1866       XQueryColor (dpy, cmap, &topc);
  1867       /* Don't overflow/wrap!  */
  1868       topc.red   = MINL (65535, topc.red   * 1.2);
  1869       topc.green = MINL (65535, topc.green * 1.2);
  1870       topc.blue  = MINL (65535, topc.blue  * 1.2);
  1871       if (XAllocColor (dpy, cmap, &topc))
  1872 #endif
  1873         {
  1874           *top_color = topc.pixel;
  1875           *free_top_p = True;
  1876           top_frobbed = 1;
  1877         }
  1878     }
  1879   if (*bottom_color == fore_color || *bottom_color == back_color)
  1880     {
  1881       botc.pixel = back_color;
  1882 #ifdef emacs
  1883       if (x_alloc_lighter_color_for_widget ((Widget) mw, dpy, cmap,
  1884                                             &botc.pixel,
  1885                                             0.6, 0x4000))
  1886 #else
  1887       XQueryColor (dpy, cmap, &botc);
  1888       botc.red   *= 0.6;
  1889       botc.green *= 0.6;
  1890       botc.blue  *= 0.6;
  1891       if (XAllocColor (dpy, cmap, &botc))
  1892 #endif
  1893         {
  1894           *bottom_color = botc.pixel;
  1895           *free_bottom_p = True;
  1896           bottom_frobbed = 1;
  1897         }
  1898     }
  1899 
  1900   if (top_frobbed && bottom_frobbed)
  1901     {
  1902       if (topc.pixel == botc.pixel)
  1903         {
  1904           if (botc.pixel == fore_color)
  1905             {
  1906               if (*free_top_p)
  1907                 {
  1908                   x_free_dpy_colors (dpy, screen, cmap, top_color, 1);
  1909                   *free_top_p = False;
  1910                 }
  1911               *top_color = back_color;
  1912             }
  1913           else
  1914             {
  1915               if (*free_bottom_p)
  1916                 {
  1917                   x_free_dpy_colors (dpy, screen, cmap, bottom_color, 1);
  1918                   *free_bottom_p = False;
  1919                 }
  1920               *bottom_color = fore_color;
  1921             }
  1922         }
  1923     }
  1924 
  1925   if (!*top_pixmap && *top_color == back_color)
  1926     {
  1927       *top_pixmap = mw->menu.gray_pixmap;
  1928       if (*free_top_p)
  1929         {
  1930           x_free_dpy_colors (dpy, screen, cmap, top_color, 1);
  1931           *free_top_p = False;
  1932         }
  1933       *top_color = fore_color;
  1934     }
  1935   if (!*bottom_pixmap && *bottom_color == back_color)
  1936     {
  1937       *bottom_pixmap = mw->menu.gray_pixmap;
  1938       if (*free_bottom_p)
  1939         {
  1940           x_free_dpy_colors (dpy, screen, cmap, bottom_color, 1);
  1941           *free_bottom_p = False;
  1942         }
  1943       *bottom_color = fore_color;
  1944     }
  1945 }
  1946 
  1947 static void
  1948 make_shadow_gcs (XlwMenuWidget mw)
  1949 {
  1950   XGCValues xgcv;
  1951   unsigned long pm = 0;
  1952   Pixel highlight_fg;
  1953 
  1954   highlight_fg = mw->menu.highlight_foreground;
  1955 
  1956   if (highlight_fg == -1)
  1957     highlight_fg = mw->menu.foreground;
  1958 
  1959   /* Normal shadows */
  1960   compute_shadow_colors (mw, &(mw->menu.top_shadow_color),
  1961                          &(mw->menu.bottom_shadow_color),
  1962                          &(mw->menu.free_top_shadow_color_p),
  1963                          &(mw->menu.free_bottom_shadow_color_p),
  1964                          &(mw->menu.top_shadow_pixmap),
  1965                          &(mw->menu.bottom_shadow_pixmap),
  1966                          mw->menu.foreground, mw->core.background_pixel);
  1967 
  1968   /* Highlight shadows */
  1969   compute_shadow_colors (mw, &(mw->menu.top_highlight_shadow_color),
  1970                          &(mw->menu.bottom_highlight_shadow_color),
  1971                          &(mw->menu.free_top_highlight_shadow_color_p),
  1972                          &(mw->menu.free_bottom_highlight_shadow_color_p),
  1973                          &(mw->menu.top_highlight_shadow_pixmap),
  1974                          &(mw->menu.bottom_highlight_shadow_pixmap),
  1975                          highlight_fg, mw->menu.highlight_background);
  1976 
  1977   xgcv.fill_style = FillStippled;
  1978   xgcv.foreground = mw->menu.top_shadow_color;
  1979   xgcv.stipple = mw->menu.top_shadow_pixmap;
  1980   pm = (xgcv.stipple ? GCStipple | GCFillStyle : 0);
  1981   mw->menu.shadow_top_gc = XtGetGC ((Widget) mw, GCForeground | pm, &xgcv);
  1982 
  1983   xgcv.foreground = mw->menu.bottom_shadow_color;
  1984   xgcv.stipple = mw->menu.bottom_shadow_pixmap;
  1985   pm = (xgcv.stipple ? GCStipple | GCFillStyle : 0);
  1986   mw->menu.shadow_bottom_gc = XtGetGC ((Widget) mw, GCForeground | pm, &xgcv);
  1987 
  1988   xgcv.foreground = mw->menu.top_highlight_shadow_color;
  1989   xgcv.stipple = mw->menu.top_highlight_shadow_pixmap;
  1990   pm = (xgcv.stipple ? GCStipple | GCFillStyle : 0);
  1991   mw->menu.highlight_shadow_top_gc = XtGetGC ((Widget) mw, GCForeground | pm, &xgcv);
  1992 
  1993   xgcv.foreground = mw->menu.bottom_highlight_shadow_color;
  1994   xgcv.stipple = mw->menu.bottom_highlight_shadow_pixmap;
  1995   pm = (xgcv.stipple ? GCStipple | GCFillStyle : 0);
  1996   mw->menu.highlight_shadow_bottom_gc = XtGetGC ((Widget) mw, GCForeground | pm, &xgcv);
  1997 }
  1998 
  1999 static void
  2000 release_shadow_gcs (XlwMenuWidget mw)
  2001 {
  2002   Display *dpy = XtDisplay ((Widget) mw);
  2003   Screen *screen = XtScreen ((Widget) mw);
  2004   Colormap cmap = mw->core.colormap;
  2005   Pixel px[4];
  2006   int i = 0;
  2007 
  2008   if (mw->menu.free_top_shadow_color_p)
  2009     px[i++] = mw->menu.top_shadow_color;
  2010   if (mw->menu.free_bottom_shadow_color_p)
  2011     px[i++] = mw->menu.bottom_shadow_color;
  2012   if (mw->menu.free_top_highlight_shadow_color_p)
  2013     px[i++] = mw->menu.top_highlight_shadow_color;
  2014   if (mw->menu.free_bottom_highlight_shadow_color_p)
  2015     px[i++] = mw->menu.bottom_highlight_shadow_color;
  2016   if (i > 0)
  2017     x_free_dpy_colors (dpy, screen, cmap, px, i);
  2018 
  2019   XtReleaseGC ((Widget) mw, mw->menu.shadow_top_gc);
  2020   XtReleaseGC ((Widget) mw, mw->menu.shadow_bottom_gc);
  2021   XtReleaseGC ((Widget) mw, mw->menu.highlight_shadow_top_gc);
  2022   XtReleaseGC ((Widget) mw, mw->menu.highlight_shadow_bottom_gc);
  2023 }
  2024 
  2025 #if defined USE_CAIRO || defined HAVE_XFT
  2026 static XftFont *
  2027 getDefaultXftFont (XlwMenuWidget mw)
  2028 {
  2029   int screen = XScreenNumberOfScreen (mw->core.screen);
  2030   return XftFontOpenName (XtDisplay (mw), screen, DEFAULT_FONTNAME);
  2031 }
  2032 
  2033 static int
  2034 openXftFont (XlwMenuWidget mw)
  2035 {
  2036   char *fname = mw->menu.fontName;
  2037 
  2038   mw->menu.xft_font = 0;
  2039   mw->menu.default_face = fname && strcmp (fname, DEFAULT_FONTNAME) == 0;
  2040 
  2041   if (fname && strcmp (fname, "none") != 0)
  2042     {
  2043       int screen = XScreenNumberOfScreen (mw->core.screen);
  2044       int len = strlen (fname), i = len - 1;
  2045       /* Try to convert Gtk-syntax (Sans 9) to Xft syntax Sans-9.  */
  2046       while (i > 0 && '0' <= fname[i] && fname[i] <= '9')
  2047         --i;
  2048       if (fname[i] == ' ')
  2049         {
  2050           fname = xstrdup (mw->menu.fontName);
  2051           fname[i] = '-';
  2052         }
  2053 
  2054       mw->menu.xft_font = XftFontOpenName (XtDisplay (mw), screen, fname);
  2055       if (!mw->menu.xft_font)
  2056         mw->menu.xft_font = getDefaultXftFont (mw);
  2057     }
  2058 
  2059   if (fname != mw->menu.fontName) xfree (fname);
  2060 
  2061   return mw->menu.xft_font != 0;
  2062 }
  2063 
  2064 static void
  2065 update_xft_colors (Widget w)
  2066 {
  2067   XlwMenuWidget mw;
  2068   XColor colors[4];
  2069 
  2070   mw = (XlwMenuWidget) w;
  2071 
  2072   colors[0].pixel = mw->menu.xft_fg.pixel
  2073     = mw->menu.foreground;
  2074   colors[1].pixel = mw->menu.xft_bg.pixel
  2075     = mw->core.background_pixel;
  2076   colors[2].pixel = mw->menu.xft_disabled_fg.pixel
  2077     = mw->menu.disabled_foreground;
  2078   colors[3].pixel = mw->menu.xft_highlight_fg.pixel
  2079     = (mw->menu.highlight_foreground != -1
  2080        ? mw->menu.highlight_foreground
  2081        : mw->menu.foreground);
  2082 
  2083   XQueryColors (XtDisplay (mw), mw->core.colormap,
  2084                 colors, 4);
  2085 
  2086   mw->menu.xft_fg.color.alpha = 0xFFFF;
  2087   mw->menu.xft_fg.color.red = colors[0].red;
  2088   mw->menu.xft_fg.color.green = colors[0].green;
  2089   mw->menu.xft_fg.color.blue = colors[0].blue;
  2090   mw->menu.xft_bg.color.alpha = 0xFFFF;
  2091   mw->menu.xft_bg.color.red = colors[1].red;
  2092   mw->menu.xft_bg.color.green = colors[1].green;
  2093   mw->menu.xft_bg.color.blue = colors[1].blue;
  2094   mw->menu.xft_disabled_fg.color.alpha = 0xFFFF;
  2095   mw->menu.xft_disabled_fg.color.red = colors[2].red;
  2096   mw->menu.xft_disabled_fg.color.green = colors[2].green;
  2097   mw->menu.xft_disabled_fg.color.blue = colors[2].blue;
  2098   mw->menu.xft_highlight_fg.color.alpha = 0xFFFF;
  2099   mw->menu.xft_highlight_fg.color.red = colors[3].red;
  2100   mw->menu.xft_highlight_fg.color.green = colors[3].green;
  2101   mw->menu.xft_highlight_fg.color.blue = colors[3].blue;
  2102 }
  2103 #endif
  2104 
  2105 static void
  2106 XlwMenuInitialize (Widget request, Widget w, ArgList args, Cardinal *num_args)
  2107 {
  2108   /* Get the GCs and the widget size.  */
  2109   XlwMenuWidget mw = (XlwMenuWidget) w;
  2110   Window window = RootWindowOfScreen (DefaultScreenOfDisplay (XtDisplay (mw)));
  2111   Display* display = XtDisplay (mw);
  2112 
  2113   /*  mw->menu.cursor = XCreateFontCursor (display, mw->menu.cursor_shape); */
  2114   mw->menu.cursor = mw->menu.cursor_shape;
  2115 
  2116   mw->menu.gray_pixmap
  2117     = XCreatePixmapFromBitmapData (display, window, gray_bits,
  2118                                    gray_width, gray_height,
  2119                                    (unsigned long)1, (unsigned long)0, 1);
  2120 
  2121 #if defined USE_CAIRO || defined HAVE_XFT
  2122   if (openXftFont (mw))
  2123     ;
  2124   else
  2125 #endif
  2126     {
  2127       mw->menu.font = XLoadQueryFont (display, mw->menu.fontName);
  2128       if (!mw->menu.font)
  2129         {
  2130           mw->menu.font = XLoadQueryFont (display, "fixed");
  2131           if (!mw->menu.font)
  2132             {
  2133               fprintf (stderr, "Menu font fixed not found, can't continue.\n");
  2134               emacs_abort ();
  2135             }
  2136         }
  2137     }
  2138 
  2139 #ifdef HAVE_X_I18N
  2140   if (mw->menu.fontSet)
  2141     mw->menu.font_extents = XExtentsOfFontSet (mw->menu.fontSet);
  2142 #endif
  2143 
  2144   mw->menu.top_highlight_shadow_color = -1;
  2145   mw->menu.bottom_highlight_shadow_color = -1;
  2146   mw->menu.top_highlight_shadow_pixmap = None;
  2147   mw->menu.bottom_highlight_shadow_pixmap = None;
  2148 
  2149   make_drawing_gcs (mw);
  2150   make_shadow_gcs (mw);
  2151 
  2152   mw->menu.popped_up = False;
  2153 
  2154   mw->menu.old_depth = 1;
  2155   mw->menu.old_stack = (widget_value**)XtMalloc (sizeof (widget_value*));
  2156   mw->menu.old_stack_length = 1;
  2157   mw->menu.old_stack [0] = mw->menu.contents;
  2158 
  2159   mw->menu.new_depth = 0;
  2160   mw->menu.new_stack = 0;
  2161   mw->menu.new_stack_length = 0;
  2162   push_new_stack (mw, mw->menu.contents);
  2163 
  2164   mw->menu.windows = (window_state*)XtMalloc (sizeof (window_state));
  2165   mw->menu.windows_length = 1;
  2166   mw->menu.windows [0].x = 0;
  2167   mw->menu.windows [0].y = 0;
  2168   mw->menu.windows [0].width = 0;
  2169   mw->menu.windows [0].height = 0;
  2170   mw->menu.windows [0].max_rest_width = 0;
  2171   mw->menu.windows [0].pixmap = None;
  2172 #if defined USE_CAIRO || defined HAVE_XFT
  2173   mw->menu.windows [0].xft_draw = 0;
  2174 #endif
  2175   size_menu (mw, 0);
  2176 
  2177   mw->core.width = mw->menu.windows [0].width;
  2178   mw->core.height = mw->menu.windows [0].height;
  2179 }
  2180 
  2181 static void
  2182 XlwMenuClassInitialize (void)
  2183 {
  2184 }
  2185 
  2186 static void
  2187 XlwMenuRealize (Widget w, Mask *valueMask, XSetWindowAttributes *attributes)
  2188 {
  2189   XlwMenuWidget mw = (XlwMenuWidget)w;
  2190   XSetWindowAttributes xswa;
  2191   int mask;
  2192 
  2193   (*xlwMenuWidgetClass->core_class.superclass->core_class.realize)
  2194     (w, valueMask, attributes);
  2195 
  2196   xswa.save_under = True;
  2197   xswa.cursor = mw->menu.cursor_shape;
  2198   mask = CWSaveUnder | CWCursor;
  2199   /* I sometimes get random BadCursor errors while creating the first
  2200      frame on a display.  I can not find their reason, but they are
  2201      annoying so for now let's ignore any errors here.  -- lorentey  */
  2202 #ifdef emacs
  2203   x_catch_errors (XtDisplay (w));
  2204 #endif
  2205   XChangeWindowAttributes (XtDisplay (w), XtWindow (w), mask, &xswa);
  2206 #ifdef emacs
  2207   x_uncatch_errors ();
  2208 #endif
  2209 
  2210   mw->menu.windows [0].w = w;
  2211   mw->menu.windows [0].window = XtWindow (w);
  2212   mw->menu.windows [0].x = w->core.x;
  2213   mw->menu.windows [0].y = w->core.y;
  2214   mw->menu.windows [0].width = w->core.width;
  2215   mw->menu.windows [0].height = w->core.height;
  2216 
  2217   set_window_type (mw->menu.windows [0].w, mw);
  2218   create_pixmap_for_menu (&mw->menu.windows [0], mw);
  2219 
  2220 #if defined USE_CAIRO || defined HAVE_XFT
  2221   if (mw->menu.xft_font)
  2222     update_xft_colors (w);
  2223 #endif
  2224 }
  2225 
  2226 /* Only the toplevel menubar/popup is a widget so it's the only one that
  2227    receives expose events through Xt.  So we repaint all the other panes
  2228    when receiving an Expose event.  */
  2229 static void
  2230 XlwMenuRedisplay (Widget w, XEvent *ev, Region region)
  2231 {
  2232   XlwMenuWidget mw = (XlwMenuWidget)w;
  2233 
  2234   /* If we have a depth beyond 1, it's because a submenu was displayed.
  2235      If the submenu has been destroyed, set the depth back to 1.  */
  2236   if (submenu_destroyed)
  2237     {
  2238       mw->menu.old_depth = 1;
  2239       submenu_destroyed = 0;
  2240     }
  2241 
  2242   display_menu (mw, 0, False, NULL, NULL, NULL);
  2243 }
  2244 
  2245 
  2246 /* Part of a hack to make the menu redisplay when a tooltip frame
  2247    over a menu item is unmapped.  */
  2248 
  2249 void
  2250 xlwmenu_redisplay (Widget w)
  2251 {
  2252   XlwMenuRedisplay (w, NULL, None);
  2253 }
  2254 
  2255 static void
  2256 XlwMenuDestroy (Widget w)
  2257 {
  2258   int i;
  2259   XlwMenuWidget mw = (XlwMenuWidget) w;
  2260 
  2261   if (pointer_grabbed)
  2262     ungrab_all ((Widget)w, CurrentTime);
  2263   pointer_grabbed = 0;
  2264   keyboard_grabbed = 0;
  2265 
  2266   if (!XtIsShell (XtParent (w)))
  2267     submenu_destroyed = 1;
  2268 
  2269   release_drawing_gcs (mw);
  2270   release_shadow_gcs (mw);
  2271 
  2272   /* This doesn't come from the resource db but is created explicitly
  2273      so we must free it ourselves.  */
  2274   XFreePixmap (XtDisplay (mw), mw->menu.gray_pixmap);
  2275   mw->menu.gray_pixmap = (Pixmap) -1;
  2276 
  2277   /* Don't free mw->menu.contents because that comes from our creator.
  2278      The `*_stack' elements are just pointers into `contents' so leave
  2279      that alone too.  But free the stacks themselves.  */
  2280   if (mw->menu.old_stack) XtFree ((char *) mw->menu.old_stack);
  2281   if (mw->menu.new_stack) XtFree ((char *) mw->menu.new_stack);
  2282 
  2283   /* Original comment was:
  2284 
  2285      Remember, you can't free anything that came from the resource
  2286      database.  This includes:
  2287          mw->menu.cursor
  2288          mw->menu.top_shadow_pixmap
  2289          mw->menu.bottom_shadow_pixmap
  2290          mw->menu.font
  2291      Also the color cells of top_shadow_color, bottom_shadow_color,
  2292      foreground, and button_foreground will never be freed until this
  2293      client exits.  Nice, eh?
  2294 
  2295      But now I can free font without any visible glitches.  */
  2296 
  2297   if (mw->menu.font)
  2298     XFreeFont (XtDisplay (mw), mw->menu.font);
  2299 
  2300 #if defined USE_CAIRO || defined HAVE_XFT
  2301   if (mw->menu.windows [0].xft_draw)
  2302     XftDrawDestroy (mw->menu.windows [0].xft_draw);
  2303   if (mw->menu.xft_font)
  2304     XftFontClose (XtDisplay (mw), mw->menu.xft_font);
  2305 #endif
  2306 
  2307   if (mw->menu.windows [0].pixmap != None)
  2308     XFreePixmap (XtDisplay (mw), mw->menu.windows [0].pixmap);
  2309   /* Start from 1 because the one in slot 0 is w->core.window.  */
  2310   for (i = 1; i < mw->menu.windows_length; i++)
  2311     {
  2312       if (mw->menu.windows [i].pixmap != None)
  2313         XFreePixmap (XtDisplay (mw), mw->menu.windows [i].pixmap);
  2314 #if defined USE_CAIRO || defined HAVE_XFT
  2315       if (mw->menu.windows [i].xft_draw)
  2316         XftDrawDestroy (mw->menu.windows [i].xft_draw);
  2317 #endif
  2318     }
  2319 
  2320   if (mw->menu.windows)
  2321     XtFree ((char *) mw->menu.windows);
  2322 }
  2323 
  2324 #if defined USE_CAIRO || defined HAVE_XFT
  2325 static int
  2326 fontname_changed (XlwMenuWidget newmw,
  2327                   XlwMenuWidget oldmw)
  2328 {
  2329   /* This will force a new XftFont even if the same string is set.
  2330      This is good, as rendering parameters may have changed and
  2331      we just want to do a redisplay.  */
  2332   return newmw->menu.fontName != oldmw->menu.fontName;
  2333 }
  2334 #endif
  2335 
  2336 static Boolean
  2337 XlwMenuSetValues (Widget current, Widget request, Widget new,
  2338                   ArgList args, Cardinal *num_args)
  2339 {
  2340   XlwMenuWidget oldmw = (XlwMenuWidget)current;
  2341   XlwMenuWidget newmw = (XlwMenuWidget)new;
  2342   Boolean do_redisplay = False;
  2343 
  2344   if (newmw->menu.contents
  2345       && newmw->menu.contents->contents
  2346       && newmw->menu.contents->contents->change >= VISIBLE_CHANGE)
  2347     do_redisplay = True;
  2348   /* Do redisplay if the contents are entirely eliminated.  */
  2349   if (newmw->menu.contents
  2350       && newmw->menu.contents->contents == 0
  2351       && newmw->menu.contents->change >= VISIBLE_CHANGE)
  2352     do_redisplay = True;
  2353 
  2354   if (newmw->core.background_pixel != oldmw->core.background_pixel
  2355       || newmw->menu.foreground != oldmw->menu.foreground
  2356 #if defined USE_CAIRO || defined HAVE_XFT
  2357       || fontname_changed (newmw, oldmw)
  2358 #endif
  2359 #ifdef HAVE_X_I18N
  2360       || newmw->menu.fontSet != oldmw->menu.fontSet
  2361       || (newmw->menu.fontSet == NULL && newmw->menu.font != oldmw->menu.font)
  2362 #else
  2363       || newmw->menu.font != oldmw->menu.font
  2364 #endif
  2365       )
  2366     {
  2367       int i;
  2368       release_drawing_gcs (newmw);
  2369       make_drawing_gcs (newmw);
  2370 
  2371       release_shadow_gcs (newmw);
  2372       /* Cause the shadow colors to be recalculated.  */
  2373       newmw->menu.top_shadow_color = -1;
  2374       newmw->menu.bottom_shadow_color = -1;
  2375       make_shadow_gcs (newmw);
  2376 
  2377       do_redisplay = True;
  2378 
  2379       if (XtIsRealized (current))
  2380         /* If the menu is currently displayed, change the display.  */
  2381         for (i = 0; i < oldmw->menu.windows_length; i++)
  2382           {
  2383             XSetWindowBackground (XtDisplay (oldmw),
  2384                                   oldmw->menu.windows [i].window,
  2385                                   newmw->core.background_pixel);
  2386             /* Clear windows and generate expose events.  */
  2387             XClearArea (XtDisplay (oldmw), oldmw->menu.windows[i].window,
  2388                         0, 0, 0, 0, True);
  2389           }
  2390 
  2391       /* Colors changed.  Update the Xft colors as well.  */
  2392 #if defined USE_CAIRO || defined HAVE_XFT
  2393       if (oldmw->menu.xft_font)
  2394         update_xft_colors (new);
  2395 #endif
  2396     }
  2397 
  2398 #if defined USE_CAIRO || defined HAVE_XFT
  2399   if (fontname_changed (newmw, oldmw))
  2400     {
  2401       int i;
  2402       int screen = XScreenNumberOfScreen (newmw->core.screen);
  2403       if (newmw->menu.xft_font)
  2404         XftFontClose (XtDisplay (newmw), newmw->menu.xft_font);
  2405       openXftFont (newmw);
  2406       for (i = 0; i < newmw->menu.windows_length; i++)
  2407         {
  2408           if (newmw->menu.windows [i].xft_draw)
  2409             XftDrawDestroy (newmw->menu.windows [i].xft_draw);
  2410           newmw->menu.windows [i].xft_draw = 0;
  2411         }
  2412       if (newmw->menu.xft_font)
  2413       for (i = 0; i < newmw->menu.windows_length; i++)
  2414           newmw->menu.windows [i].xft_draw
  2415             = XftDrawCreate (XtDisplay (newmw),
  2416                              newmw->menu.windows [i].window,
  2417                              DefaultVisual (XtDisplay (newmw), screen),
  2418                              newmw->core.colormap);
  2419     }
  2420 #endif
  2421 #ifdef HAVE_X_I18N
  2422   if (newmw->menu.fontSet != oldmw->menu.fontSet && newmw->menu.fontSet != NULL)
  2423     {
  2424       do_redisplay = True;
  2425       newmw->menu.font_extents = XExtentsOfFontSet (newmw->menu.fontSet);
  2426     }
  2427 #endif
  2428 
  2429   return do_redisplay;
  2430 }
  2431 
  2432 static void
  2433 XlwMenuResize (Widget w)
  2434 {
  2435   XlwMenuWidget mw = (XlwMenuWidget)w;
  2436 
  2437   if (mw->menu.popped_up)
  2438     {
  2439       /* Don't allow the popup menu to resize itself.  */
  2440       mw->core.width = mw->menu.windows [0].width;
  2441       mw->core.height = mw->menu.windows [0].height;
  2442       mw->core.parent->core.width = mw->core.width;
  2443       mw->core.parent->core.height = mw->core.height;
  2444     }
  2445   else
  2446     {
  2447       mw->menu.windows [0].width = mw->core.width;
  2448       mw->menu.windows [0].height = mw->core.height;
  2449       create_pixmap_for_menu (&mw->menu.windows [0], mw);
  2450     }
  2451 }
  2452 
  2453 /* Action procedures */
  2454 static void
  2455 handle_single_motion_event (XlwMenuWidget mw, XMotionEvent *ev)
  2456 {
  2457   widget_value* val;
  2458   int           level;
  2459 
  2460   if (!map_event_to_widget_value (mw, ev, &val, &level))
  2461     pop_new_stack_if_no_contents (mw);
  2462   else
  2463     set_new_state (mw, val, level);
  2464   remap_menubar (mw);
  2465 
  2466   /* Sync with the display.  Makes it feel better on X terms.  */
  2467   XSync (XtDisplay (mw), False);
  2468 }
  2469 
  2470 static void
  2471 handle_motion_event (XlwMenuWidget mw, XMotionEvent *ev)
  2472 {
  2473   int x = ev->x_root;
  2474   int y = ev->y_root;
  2475   int state = ev->state;
  2476   XMotionEvent oldev = *ev;
  2477 
  2478   /* Allow motion events to be generated again.  */
  2479   if (ev->is_hint
  2480       && XQueryPointer (XtDisplay (mw), ev->window,
  2481                         &ev->root, &ev->subwindow,
  2482                         &ev->x_root, &ev->y_root,
  2483                         &ev->x, &ev->y,
  2484                         &ev->state)
  2485       && ev->state == state
  2486       && (ev->x_root != x || ev->y_root != y))
  2487     handle_single_motion_event (mw, ev);
  2488   else
  2489     handle_single_motion_event (mw, &oldev);
  2490 }
  2491 
  2492 static void
  2493 Start (Widget w, XEvent *ev, String *params, Cardinal *num_params)
  2494 {
  2495   XlwMenuWidget mw = (XlwMenuWidget)w;
  2496 
  2497   if (!mw->menu.popped_up)
  2498     {
  2499       menu_post_event = *ev;
  2500       /* If event is set to CurrentTime, get the last known time stamp.
  2501          This is for calculating if (popup) menus should stay up after
  2502          a fast click.  */
  2503       if (menu_post_event.xbutton.time == CurrentTime)
  2504         menu_post_event.xbutton.time
  2505           = XtLastTimestampProcessed (XtDisplay (w));
  2506 
  2507       pop_up_menu (mw, (XButtonPressedEvent*) ev);
  2508     }
  2509   else
  2510     {
  2511       /* If we push a button while the menu is posted semipermanently,
  2512          releasing the button should always pop the menu down.  */
  2513       next_release_must_exit = 1;
  2514 
  2515       /* Notes the absolute position of the menubar window.  */
  2516       mw->menu.windows [0].x = ev->xmotion.x_root - ev->xmotion.x;
  2517       mw->menu.windows [0].y = ev->xmotion.y_root - ev->xmotion.y;
  2518 
  2519       /* Handles the down like a move, slots are compatible.  */
  2520       ev->xmotion.is_hint = 0;
  2521       handle_motion_event (mw, &ev->xmotion);
  2522     }
  2523 }
  2524 
  2525 static void
  2526 Drag (Widget w, XEvent *ev, String *params, Cardinal *num_params)
  2527 {
  2528   XlwMenuWidget mw = (XlwMenuWidget)w;
  2529   if (mw->menu.popped_up)
  2530     handle_motion_event (mw, &ev->xmotion);
  2531 }
  2532 
  2533 /* Do nothing.
  2534    This is how we handle presses and releases of modifier keys.  */
  2535 static void
  2536 Nothing (Widget w, XEvent *ev, String *params, Cardinal *num_params)
  2537 {
  2538 }
  2539 
  2540 static widget_value *
  2541 find_first_selectable (XlwMenuWidget mw, widget_value *item, int skip_titles)
  2542 {
  2543   widget_value *current = item;
  2544   enum menu_separator separator;
  2545 
  2546   while (lw_separator_p (current->name, &separator, 0) || !current->enabled
  2547          || (skip_titles && !current->call_data && !current->contents))
  2548     if (current->next)
  2549       current = current->next;
  2550     else
  2551       return NULL;
  2552 
  2553   return current;
  2554 }
  2555 
  2556 static widget_value *
  2557 find_next_selectable (XlwMenuWidget mw, widget_value *item, int skip_titles)
  2558 {
  2559   widget_value *current = item;
  2560   enum menu_separator separator;
  2561 
  2562   while (current->next && (current = current->next)
  2563          && (lw_separator_p (current->name, &separator, 0) || !current->enabled
  2564              || (skip_titles && !current->call_data && !current->contents)))
  2565     ;
  2566 
  2567   if (current == item)
  2568     {
  2569       if (mw->menu.old_depth < 2)
  2570         return current;
  2571       current = mw->menu.old_stack [mw->menu.old_depth - 2]->contents;
  2572 
  2573       while (lw_separator_p (current->name, &separator, 0)
  2574              || !current->enabled
  2575              || (skip_titles && !current->call_data
  2576                  && !current->contents))
  2577         {
  2578           if (current->next)
  2579             current = current->next;
  2580 
  2581           if (current == item)
  2582             break;
  2583         }
  2584 
  2585     }
  2586 
  2587   return current;
  2588 }
  2589 
  2590 static widget_value *
  2591 find_prev_selectable (XlwMenuWidget mw, widget_value *item, int skip_titles)
  2592 {
  2593   widget_value *current = item;
  2594   widget_value *prev = item;
  2595 
  2596   while ((current = find_next_selectable (mw, current, skip_titles))
  2597          != item)
  2598     {
  2599       if (prev == current)
  2600         break;
  2601       prev = current;
  2602     }
  2603 
  2604   return prev;
  2605 }
  2606 
  2607 static void
  2608 Down (Widget w, XEvent *ev, String *params, Cardinal *num_params)
  2609 {
  2610   XlwMenuWidget mw = (XlwMenuWidget) w;
  2611   widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
  2612   int popup_menu_p = mw->menu.top_depth == 1;
  2613 
  2614   /* Inside top-level menu-bar?  */
  2615   if (mw->menu.old_depth == mw->menu.top_depth)
  2616     /* When <down> in the menu-bar is pressed, display the corresponding
  2617        sub-menu and select the first selectable menu item there.
  2618        If this is a popup menu, skip title item of the popup.  */
  2619     set_new_state (mw,
  2620                    find_first_selectable (mw,
  2621                                           selected_item->contents,
  2622                                           popup_menu_p),
  2623                    mw->menu.old_depth);
  2624   else
  2625     /* Highlight next possible (enabled and not separator) menu item.  */
  2626     set_new_state (mw, find_next_selectable (mw, selected_item, popup_menu_p),
  2627                    mw->menu.old_depth - 1);
  2628 
  2629   remap_menubar (mw);
  2630 }
  2631 
  2632 static void
  2633 Up (Widget w, XEvent *ev, String *params, Cardinal *num_params)
  2634 {
  2635   XlwMenuWidget mw = (XlwMenuWidget) w;
  2636   widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
  2637   int popup_menu_p = mw->menu.top_depth == 1;
  2638 
  2639   /* Inside top-level menu-bar?  */
  2640   if (mw->menu.old_depth == mw->menu.top_depth)
  2641     {
  2642       /* FIXME: this is tricky.  <up> in the menu-bar should select the
  2643          last selectable item in the list.  So we select the first
  2644          selectable one and find the previous selectable item.  Is there
  2645          a better way?  */
  2646       /* If this is a popup menu, skip title item of the popup.  */
  2647       set_new_state (mw,
  2648                      find_first_selectable (mw,
  2649                                             selected_item->contents,
  2650                                             popup_menu_p),
  2651                      mw->menu.old_depth);
  2652       remap_menubar (mw);
  2653       selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
  2654       set_new_state (mw,
  2655                      find_prev_selectable (mw,
  2656                                            selected_item,
  2657                                            popup_menu_p),
  2658                      mw->menu.old_depth - 1);
  2659     }
  2660   else
  2661     /* Highlight previous (enabled and not separator) menu item.  */
  2662     set_new_state (mw, find_prev_selectable (mw, selected_item, popup_menu_p),
  2663                    mw->menu.old_depth - 1);
  2664 
  2665   remap_menubar (mw);
  2666 }
  2667 
  2668 void
  2669 Left (Widget w, XEvent *ev, String *params, Cardinal *num_params)
  2670 {
  2671   XlwMenuWidget mw = (XlwMenuWidget) w;
  2672   widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
  2673 
  2674   /* Inside top-level menu-bar?  */
  2675   if (mw->menu.old_depth == mw->menu.top_depth)
  2676     /* When <left> in the menu-bar is pressed, display the previous item on
  2677        the menu-bar. If the current item is the first one, highlight the
  2678        last item in the menubar (probably Help).  */
  2679     set_new_state (mw, find_prev_selectable (mw, selected_item, 0),
  2680                    mw->menu.old_depth - 1);
  2681   else if (mw->menu.old_depth == 1
  2682            && selected_item->contents)     /* Is this menu item expandable?  */
  2683     {
  2684       set_new_state (mw, selected_item->contents, mw->menu.old_depth);
  2685       remap_menubar (mw);
  2686       selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
  2687       if (!selected_item->enabled && find_first_selectable (mw,
  2688                                                             selected_item,
  2689                                                             0))
  2690         set_new_state (mw, find_first_selectable (mw, selected_item, 0),
  2691                        mw->menu.old_depth - 1);
  2692     }
  2693 
  2694   else
  2695     {
  2696       pop_new_stack_if_no_contents (mw);
  2697       set_new_state (mw, mw->menu.old_stack [mw->menu.old_depth - 2],
  2698                      mw->menu.old_depth - 2);
  2699     }
  2700 
  2701   remap_menubar (mw);
  2702 }
  2703 
  2704 void
  2705 Right (Widget w, XEvent *ev, String *params, Cardinal *num_params)
  2706 {
  2707   XlwMenuWidget mw = (XlwMenuWidget) w;
  2708   widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
  2709 
  2710   /* Inside top-level menu-bar?  */
  2711   if (mw->menu.old_depth == mw->menu.top_depth)
  2712     /* When <right> in the menu-bar is pressed, display the next item on
  2713        the menu-bar. If the current item is the last one, highlight the
  2714        first item (probably File).  */
  2715     set_new_state (mw, find_next_selectable (mw, selected_item, 0),
  2716                    mw->menu.old_depth - 1);
  2717   else if (selected_item->contents)     /* Is this menu item expandable?  */
  2718     {
  2719       set_new_state (mw, selected_item->contents, mw->menu.old_depth);
  2720       remap_menubar (mw);
  2721       selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
  2722       if (!selected_item->enabled && find_first_selectable (mw,
  2723                                                             selected_item,
  2724                                                             0))
  2725         set_new_state (mw, find_first_selectable (mw, selected_item, 0),
  2726                        mw->menu.old_depth - 1);
  2727     }
  2728   else
  2729     {
  2730       pop_new_stack_if_no_contents (mw);
  2731       set_new_state (mw, mw->menu.old_stack [mw->menu.old_depth - 2],
  2732                      mw->menu.old_depth - 2);
  2733     }
  2734 
  2735   remap_menubar (mw);
  2736 }
  2737 
  2738 /* Handle key press and release events while menu is popped up.
  2739    Our action is to get rid of the menu.  */
  2740 static void
  2741 Key (Widget w, XEvent *ev, String *params, Cardinal *num_params)
  2742 {
  2743   XlwMenuWidget mw = (XlwMenuWidget)w;
  2744 
  2745   /* Pop down everything.  */
  2746   mw->menu.new_depth = 1;
  2747   remap_menubar (mw);
  2748 
  2749   if (mw->menu.popped_up)
  2750     {
  2751       mw->menu.popped_up = False;
  2752       ungrab_all ((Widget)mw, ev->xmotion.time);
  2753       if (XtIsShell (XtParent ((Widget) mw)))
  2754         XtPopdown (XtParent ((Widget) mw));
  2755       else
  2756         {
  2757           XtRemoveGrab ((Widget) mw);
  2758           display_menu (mw, 0, False, NULL, NULL, NULL);
  2759         }
  2760     }
  2761 
  2762   /* callback */
  2763   XtCallCallbackList ((Widget)mw, mw->menu.select, (XtPointer)0);
  2764 }
  2765 
  2766 static void
  2767 Select (Widget w, XEvent *ev, String *params, Cardinal *num_params)
  2768 {
  2769   XlwMenuWidget mw = (XlwMenuWidget)w;
  2770   widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
  2771 
  2772   /* If user releases the button quickly, without selecting anything,
  2773      after the initial down-click that brought the menu up,
  2774      do nothing.  */
  2775   if ((selected_item == 0
  2776        || ((widget_value *) selected_item)->call_data == 0)
  2777       && !next_release_must_exit
  2778       && (ev->xbutton.time - menu_post_event.xbutton.time
  2779           < XtGetMultiClickTime (XtDisplay (w))))
  2780     return;
  2781 
  2782   /* Pop down everything.  */
  2783   mw->menu.new_depth = 1;
  2784   remap_menubar (mw);
  2785 
  2786   if (mw->menu.popped_up)
  2787     {
  2788       mw->menu.popped_up = False;
  2789       ungrab_all ((Widget)mw, ev->xmotion.time);
  2790       if (XtIsShell (XtParent ((Widget) mw)))
  2791         XtPopdown (XtParent ((Widget) mw));
  2792       else
  2793         {
  2794           XtRemoveGrab ((Widget) mw);
  2795           display_menu (mw, 0, False, NULL, NULL, NULL);
  2796         }
  2797     }
  2798 
  2799   /* callback */
  2800   XtCallCallbackList ((Widget)mw, mw->menu.select, (XtPointer)selected_item);
  2801 }
  2802 
  2803 
  2804 /* Special code to pop-up a menu.  */
  2805 static void
  2806 pop_up_menu (XlwMenuWidget mw, XButtonPressedEvent *event)
  2807 {
  2808   int           x = event->x_root;
  2809   int           y = event->y_root;
  2810   int           w;
  2811   int           h;
  2812   int           borderwidth = mw->menu.shadow_thickness;
  2813   Screen*       screen = XtScreen (mw);
  2814   Display       *display = XtDisplay (mw);
  2815   int           screen_x;
  2816   int           screen_y;
  2817   int           screen_w;
  2818   int           screen_h;
  2819 
  2820 #ifdef emacs
  2821   xlw_monitor_dimensions_at_pos (display, screen, x, y,
  2822                                  &screen_x, &screen_y,
  2823                                  &screen_w, &screen_h);
  2824 #else
  2825   screen_x = 0;
  2826   screen_y = 0;
  2827   screen_w = WidthOfScreen (screen);
  2828   screen_h = HeightOfScreen (screen);
  2829 #endif
  2830   next_release_must_exit = 0;
  2831 
  2832   mw->menu.inside_entry = NULL;
  2833   XtCallCallbackList ((Widget)mw, mw->menu.open, NULL);
  2834 
  2835   if (XtIsShell (XtParent ((Widget)mw)))
  2836     size_menu (mw, 0);
  2837 
  2838   w = mw->menu.windows [0].width;
  2839   h = mw->menu.windows [0].height;
  2840 
  2841   x -= borderwidth;
  2842   y -= borderwidth;
  2843   if (x < screen_x + borderwidth)
  2844     x = screen_x + borderwidth;
  2845   if (x + w + 2 * borderwidth > screen_x + screen_w)
  2846     x = (screen_x + screen_w) - w - 2 * borderwidth;
  2847   if (y < screen_y + borderwidth)
  2848     y = screen_y + borderwidth;
  2849   if (y + h + 2 * borderwidth > screen_y + screen_h)
  2850     y = (screen_y + screen_h) - h - 2 * borderwidth;
  2851 
  2852   mw->menu.popped_up = True;
  2853   if (XtIsShell (XtParent ((Widget)mw)))
  2854     {
  2855       /* fprintf (stderr, "Config %d %d\n", x, y); */
  2856       XtConfigureWidget (XtParent ((Widget)mw), x, y, w, h,
  2857                          XtParent ((Widget)mw)->core.border_width);
  2858       XtPopup (XtParent ((Widget)mw), XtGrabExclusive);
  2859       display_menu (mw, 0, False, NULL, NULL, NULL);
  2860       mw->menu.windows [0].x = x + borderwidth;
  2861       mw->menu.windows [0].y = y + borderwidth;
  2862       mw->menu.top_depth = 1;  /* Popup menus don't have a bar so top is 1.  */
  2863     }
  2864   else
  2865     {
  2866       XEvent *ev = (XEvent *) event;
  2867 
  2868       XtAddGrab ((Widget) mw, True, True);
  2869 
  2870       /* Notes the absolute position of the menubar window.  */
  2871       mw->menu.windows [0].x = ev->xmotion.x_root - ev->xmotion.x;
  2872       mw->menu.windows [0].y = ev->xmotion.y_root - ev->xmotion.y;
  2873       mw->menu.top_depth = 2;
  2874     }
  2875 
  2876 #ifdef emacs
  2877   x_catch_errors (display);
  2878 #endif
  2879   if (XtGrabPointer ((Widget)mw, False,
  2880                      (PointerMotionMask
  2881                       | PointerMotionHintMask
  2882                       | ButtonReleaseMask
  2883                       | ButtonPressMask),
  2884                      GrabModeAsync, GrabModeAsync, None,
  2885                      mw->menu.cursor_shape,
  2886                      event->time) == Success)
  2887     {
  2888       if (true
  2889 #ifdef emacs
  2890           && lucid__menu_grab_keyboard
  2891 #endif
  2892           && XtGrabKeyboard ((Widget) mw, False, GrabModeAsync,
  2893                              GrabModeAsync, event->time) == Success)
  2894         {
  2895           XtSetKeyboardFocus ((Widget) mw, None);
  2896           pointer_grabbed = 1;
  2897           keyboard_grabbed = 1;
  2898         }
  2899       else
  2900         {
  2901           XtUngrabPointer ((Widget) mw, event->time);
  2902           keyboard_grabbed = 0;
  2903         }
  2904     }
  2905 
  2906 #ifdef emacs
  2907   if (x_had_errors_p (display))
  2908     {
  2909       pointer_grabbed = 0;
  2910       XtUngrabPointer ((Widget)mw, event->time);
  2911     }
  2912   x_uncatch_errors ();
  2913 #endif
  2914 
  2915   ((XMotionEvent*)event)->is_hint = 0;
  2916   handle_motion_event (mw, (XMotionEvent*)event);
  2917 
  2918   XlwMenuRedisplay ((Widget) mw, NULL, None);
  2919 }

/* [<][>][^][v][top][bottom][index][help] */