home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / emacs-19.28-src.tgz / tar.out / fsf / emacs / lwlib / xlwmenu.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  40KB  |  1,456 lines

  1. /* Implements a lightweight menubar widget.  
  2.    Copyright (C) 1992 Lucid, Inc.
  3.  
  4. This file is part of the Lucid Widget Library.
  5.  
  6. The Lucid Widget Library is free software; you can redistribute it and/or 
  7. modify it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 2, or (at your option)
  9. any later version.
  10.  
  11. The Lucid Widget Library is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of 
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. GNU General Public License for more details.
  15.  
  16. You should have received a copy of the GNU General Public License
  17. along with GNU Emacs; see the file COPYING.  If not, write to
  18. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
  19.  
  20. /* Created by devin@lucid.com */
  21.  
  22. #include <stdio.h>
  23.  
  24. #include <sys/types.h>
  25. #include <X11/Xos.h>
  26. #include <X11/IntrinsicP.h>
  27. #include <X11/StringDefs.h>
  28. #include <X11/cursorfont.h>
  29. #include <X11/bitmaps/gray>
  30. #include "xlwmenuP.h"
  31.  
  32. static int pointer_grabbed;
  33. static XEvent menu_post_event;
  34.  
  35. static char 
  36. xlwMenuTranslations [] = 
  37. "<BtnDown>:    start()\n\
  38. <Motion>:    drag()\n\
  39. <BtnUp>:    select()\n\
  40. ";
  41.  
  42. #define offset(field) XtOffset(XlwMenuWidget, field)
  43. static XtResource 
  44. xlwMenuResources[] =
  45.   {XtNfont,  XtCFont, XtRFontStruct, sizeof(XFontStruct *),
  46.      offset(menu.font),XtRString, "XtDefaultFont"},
  47.   {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
  48.      offset(menu.foreground), XtRString, "XtDefaultForeground"},
  49.   {XtNbuttonForeground, XtCButtonForeground, XtRPixel, sizeof(Pixel),
  50.      offset(menu.button_foreground), XtRString, "XtDefaultForeground"},
  51.   {XtNmargin, XtCMargin, XtRDimension,  sizeof(Dimension),
  52.      offset(menu.margin), XtRImmediate, (XtPointer)0},
  53.   {XtNhorizontalSpacing, XtCMargin, XtRDimension,  sizeof(Dimension),
  54.      offset(menu.horizontal_spacing), XtRImmediate, (XtPointer)3},
  55.   {XtNverticalSpacing, XtCMargin, XtRDimension,  sizeof(Dimension),
  56.      offset(menu.vertical_spacing), XtRImmediate, (XtPointer)1},
  57.   {XtNarrowSpacing, XtCMargin, XtRDimension,  sizeof(Dimension),
  58.      offset(menu.arrow_spacing), XtRImmediate, (XtPointer)10},
  59.  
  60.   {XmNshadowThickness, XmCShadowThickness, XtRDimension,
  61.      sizeof (Dimension), offset (menu.shadow_thickness),
  62.      XtRImmediate, (XtPointer) 2},
  63.   {XmNtopShadowColor, XmCTopShadowColor, XtRPixel, sizeof (Pixel),
  64.      offset (menu.top_shadow_color), XtRImmediate, (XtPointer)-1},
  65.   {XmNbottomShadowColor, XmCBottomShadowColor, XtRPixel, sizeof (Pixel),
  66.      offset (menu.bottom_shadow_color), XtRImmediate, (XtPointer)-1},
  67.   {XmNtopShadowPixmap, XmCTopShadowPixmap, XtRPixmap, sizeof (Pixmap),
  68.      offset (menu.top_shadow_pixmap), XtRImmediate, (XtPointer)None},
  69.   {XmNbottomShadowPixmap, XmCBottomShadowPixmap, XtRPixmap, sizeof (Pixmap),
  70.      offset (menu.bottom_shadow_pixmap), XtRImmediate, (XtPointer)None},
  71.  
  72.   {XtNopen, XtCCallback, XtRCallback, sizeof(XtPointer), 
  73.      offset(menu.open), XtRCallback, (XtPointer)NULL},
  74.   {XtNselect, XtCCallback, XtRCallback, sizeof(XtPointer), 
  75.      offset(menu.select), XtRCallback, (XtPointer)NULL},
  76.   {XtNmenu, XtCMenu, XtRPointer, sizeof(XtPointer),
  77.      offset(menu.contents), XtRImmediate, (XtPointer)NULL},
  78.   {XtNcursor, XtCCursor, XtRCursor, sizeof(Cursor),
  79.      offset(menu.cursor_shape), XtRString, (XtPointer)"right_ptr"},
  80.   {XtNhorizontal, XtCHorizontal, XtRInt, sizeof(int),
  81.      offset(menu.horizontal), XtRImmediate, (XtPointer)True},
  82. };
  83. #undef offset
  84.  
  85. static Boolean XlwMenuSetValues();
  86. static void XlwMenuRealize();
  87. static void XlwMenuRedisplay();
  88. static void XlwMenuResize();
  89. static void XlwMenuInitialize();
  90. static void XlwMenuRedisplay();
  91. static void XlwMenuDestroy();
  92. static void XlwMenuClassInitialize();
  93. static void Start();
  94. static void Drag();
  95. static void Select();
  96.  
  97. static XtActionsRec 
  98. xlwMenuActionsList [] =
  99. {
  100.   {"start",        Start},
  101.   {"drag",        Drag},
  102.   {"select",        Select},
  103. };
  104.  
  105. #define SuperClass ((CoreWidgetClass)&coreClassRec)
  106.  
  107. XlwMenuClassRec xlwMenuClassRec =
  108. {
  109.   {  /* CoreClass fields initialization */
  110.     (WidgetClass) SuperClass,        /* superclass          */    
  111.     "XlwMenu",                /* class_name          */
  112.     sizeof(XlwMenuRec),            /* size              */
  113.     XlwMenuClassInitialize,        /* class_initialize      */
  114.     NULL,                /* class_part_initialize  */
  115.     FALSE,                /* class_inited          */
  116.     XlwMenuInitialize,            /* initialize          */
  117.     NULL,                /* initialize_hook      */
  118.     XlwMenuRealize,            /* realize          */
  119.     xlwMenuActionsList,            /* actions          */
  120.     XtNumber(xlwMenuActionsList),    /* num_actions          */
  121.     xlwMenuResources,            /* resources          */
  122.     XtNumber(xlwMenuResources),        /* resource_count      */
  123.     NULLQUARK,                /* xrm_class          */
  124.     TRUE,                /* compress_motion      */
  125.     TRUE,                /* compress_exposure      */
  126.     TRUE,                /* compress_enterleave    */
  127.     FALSE,                /* visible_interest      */
  128.     XlwMenuDestroy,            /* destroy          */
  129.     XlwMenuResize,            /* resize          */
  130.     XlwMenuRedisplay,            /* expose          */
  131.     XlwMenuSetValues,            /* set_values          */
  132.     NULL,                /* set_values_hook      */
  133.     XtInheritSetValuesAlmost,        /* set_values_almost      */
  134.     NULL,                /* get_values_hook      */
  135.     NULL,                /* accept_focus          */
  136.     XtVersion,                /* version          */
  137.     NULL,                /* callback_private      */
  138.     xlwMenuTranslations,        /* tm_table          */
  139.     XtInheritQueryGeometry,        /* query_geometry      */
  140.     XtInheritDisplayAccelerator,    /* display_accelerator      */
  141.     NULL                /* extension          */
  142.   },  /* XlwMenuClass fields initialization */
  143.   {
  144.     0                    /* dummy */
  145.   },
  146. };
  147.  
  148. WidgetClass xlwMenuWidgetClass = (WidgetClass) &xlwMenuClassRec;
  149.  
  150. int submenu_destroyed;
  151.  
  152. static int next_release_must_exit;
  153.  
  154. /* Utilities */
  155. static void
  156. push_new_stack (mw, val)
  157.      XlwMenuWidget mw;
  158.      widget_value* val;
  159. {
  160.   if (!mw->menu.new_stack)
  161.     {
  162.       mw->menu.new_stack_length = 10;
  163.       mw->menu.new_stack =
  164.     (widget_value**)XtCalloc (mw->menu.new_stack_length,
  165.                   sizeof (widget_value*));
  166.     }
  167.   else if (mw->menu.new_depth == mw->menu.new_stack_length)
  168.     {
  169.       mw->menu.new_stack_length *= 2;
  170.       mw->menu.new_stack =
  171.     (widget_value**)XtRealloc ((char*)mw->menu.new_stack,
  172.                    mw->menu.new_stack_length * sizeof (widget_value*));
  173.     }
  174.   mw->menu.new_stack [mw->menu.new_depth++] = val;
  175. }
  176.  
  177. static void
  178. pop_new_stack_if_no_contents (mw)
  179.      XlwMenuWidget mw;
  180. {
  181.   if (mw->menu.new_depth)
  182.     {
  183.       if (!mw->menu.new_stack [mw->menu.new_depth - 1]->contents)
  184.     mw->menu.new_depth -= 1;
  185.     }
  186. }
  187.  
  188. static void
  189. make_old_stack_space (mw, n)
  190.      XlwMenuWidget mw;
  191.      int n;
  192. {
  193.   if (!mw->menu.old_stack)
  194.     {
  195.       mw->menu.old_stack_length = 10;
  196.       mw->menu.old_stack =
  197.     (widget_value**)XtCalloc (mw->menu.old_stack_length,
  198.                   sizeof (widget_value*));
  199.     }
  200.   else if (mw->menu.old_stack_length < n)
  201.     {
  202.       mw->menu.old_stack_length *= 2;
  203.       mw->menu.old_stack =
  204.     (widget_value**)XtRealloc ((char*)mw->menu.old_stack,
  205.                    mw->menu.old_stack_length * sizeof (widget_value*));
  206.     }
  207. }
  208.  
  209. /* Size code */
  210. static Boolean
  211. all_dashes_p (s)
  212.      char *s;
  213. {
  214.   char* p;
  215.   for (p = s; *p == '-'; p++);
  216.   return !*p;
  217. }
  218.  
  219. int
  220. string_width (mw, s)
  221.      XlwMenuWidget mw;
  222.      char *s;
  223. {
  224.   XCharStruct xcs;
  225.   int drop;
  226.   
  227.   XTextExtents (mw->menu.font, s, strlen (s), &drop, &drop, &drop, &xcs);
  228.   return xcs.width;
  229. }
  230.  
  231. static int
  232. arrow_width (mw)
  233.      XlwMenuWidget mw;
  234. {
  235.   return mw->menu.font->ascent / 2 | 1;
  236. }
  237.  
  238. static XtResource
  239. nameResource[] =
  240.   {"labelString",  "LabelString", XtRString, sizeof(String),
  241.      0, XtRImmediate, 0},
  242. };
  243.  
  244. static char*
  245. resource_widget_value (mw, val)
  246.      XlwMenuWidget mw;
  247.      widget_value *val;
  248. {
  249.   if (!val->toolkit_data)
  250.     {
  251.       char* resourced_name = NULL;
  252.       char* complete_name;
  253.       XtGetSubresources ((Widget) mw,
  254.              (XtPointer) &resourced_name,
  255.              val->name, val->name,
  256.              nameResource, 1, NULL, 0);
  257.       if (!resourced_name)
  258.     resourced_name = val->name;
  259.       if (!val->value)
  260.     {
  261.       complete_name = (char *) XtMalloc (strlen (resourced_name) + 1);
  262.       strcpy (complete_name, resourced_name);
  263.     }
  264.       else
  265.     {
  266.       int complete_length =
  267.         strlen (resourced_name) + strlen (val->value) + 2;
  268.       complete_name = XtMalloc (complete_length);
  269.       *complete_name = 0;
  270.       strcat (complete_name, resourced_name);
  271.       strcat (complete_name, " ");
  272.       strcat (complete_name, val->value);
  273.     }
  274.  
  275.       val->toolkit_data = complete_name;
  276.       val->free_toolkit_data = True;
  277.     }
  278.   return (char*)val->toolkit_data;
  279. }
  280.  
  281. /* Returns the sizes of an item */
  282. static void
  283. size_menu_item (mw, val, horizontal_p, label_width, rest_width, height)
  284.      XlwMenuWidget mw;
  285.      widget_value* val;
  286.      int horizontal_p;
  287.      int* label_width;
  288.      int* rest_width;
  289.      int* height;
  290. {
  291.   if (all_dashes_p (val->name))
  292.     {
  293.       *height = 2;
  294.       *label_width = 1;
  295.       *rest_width = 0;
  296.     }
  297.   else
  298.     {
  299.       *height =
  300.     mw->menu.font->ascent + mw->menu.font->descent
  301.       + 2 * mw->menu.vertical_spacing + 2 * mw->menu.shadow_thickness;
  302.       
  303.       *label_width =
  304.     string_width (mw, resource_widget_value (mw, val))
  305.       + mw->menu.horizontal_spacing + mw->menu.shadow_thickness;
  306.       
  307.       *rest_width =  mw->menu.horizontal_spacing + mw->menu.shadow_thickness;
  308.       if (!horizontal_p)
  309.     {
  310.       if (val->contents)
  311.         *rest_width += arrow_width (mw) + mw->menu.arrow_spacing;
  312.       else if (val->key)
  313.         *rest_width +=
  314.           string_width (mw, val->key) + mw->menu.arrow_spacing;
  315.     }
  316.     }
  317. }
  318.  
  319. static void
  320. size_menu (mw, level)
  321.      XlwMenuWidget mw;
  322.      int level;
  323. {
  324.   int        label_width = 0;
  325.   int        rest_width = 0;
  326.   int        max_rest_width = 0;
  327.   int        height = 0;
  328.   int        horizontal_p = mw->menu.horizontal && (level == 0);
  329.   widget_value*    val;
  330.   window_state*    ws;
  331.  
  332.   if (level >= mw->menu.old_depth)
  333.     abort ();
  334.  
  335.   ws = &mw->menu.windows [level];  
  336.   ws->width = 0;
  337.   ws->height = 0;
  338.   ws->label_width = 0;
  339.  
  340.   for (val = mw->menu.old_stack [level]->contents; val; val = val->next)
  341.     {
  342.       size_menu_item (mw, val, horizontal_p, &label_width, &rest_width,
  343.               &height);
  344.       if (horizontal_p)
  345.     {
  346.       ws->width += label_width + rest_width;
  347.       if (height > ws->height)
  348.         ws->height = height;
  349.     }
  350.       else
  351.     {
  352.       if (label_width > ws->label_width)
  353.         ws->label_width = label_width;
  354.       if (rest_width > max_rest_width)
  355.         max_rest_width = rest_width;
  356.       ws->height += height;
  357.     }
  358.     }
  359.   
  360.   if (horizontal_p)
  361.     ws->label_width = 0;
  362.   else
  363.     ws->width = ws->label_width + max_rest_width;
  364.  
  365.   ws->width += 2 * mw->menu.shadow_thickness;
  366.   ws->height += 2 * mw->menu.shadow_thickness;
  367. }
  368.  
  369.  
  370. /* Display code */
  371. static void
  372. draw_arrow (mw, window, gc, x, y, width)
  373.      XlwMenuWidget mw;
  374.      Window window;
  375.      GC gc;
  376.      int x;
  377.      int y;
  378.      int width;
  379. {
  380.   XPoint points [3];
  381.   points [0].x = x;
  382.   points [0].y = y + mw->menu.font->ascent;
  383.   points [1].x = x;
  384.   points [1].y = y;
  385.   points [2].x = x + width;
  386.   points [2].y = y + mw->menu.font->ascent / 2;
  387.   
  388.   XFillPolygon (XtDisplay (mw), window, gc, points, 3, Convex,
  389.         CoordModeOrigin);
  390. }
  391.  
  392. static void
  393. draw_shadow_rectangle (mw, window, x, y, width, height, erase_p)
  394.      XlwMenuWidget mw;
  395.      Window window;
  396.      int x;
  397.      int y;
  398.      int width;
  399.      int height;
  400.      int erase_p;
  401. {
  402.   Display *dpy = XtDisplay (mw);
  403.   GC top_gc = !erase_p ? mw->menu.shadow_top_gc : mw->menu.background_gc;
  404.   GC bottom_gc = !erase_p ? mw->menu.shadow_bottom_gc : mw->menu.background_gc;
  405.   int thickness = mw->menu.shadow_thickness;
  406.   XPoint points [4];
  407.   points [0].x = x;
  408.   points [0].y = y;
  409.   points [1].x = x + width;
  410.   points [1].y = y;
  411.   points [2].x = x + width - thickness;
  412.   points [2].y = y + thickness;
  413.   points [3].x = x;
  414.   points [3].y = y + thickness;
  415.   XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
  416.   points [0].x = x;
  417.   points [0].y = y + thickness;
  418.   points [1].x = x;
  419.   points [1].y = y + height;
  420.   points [2].x = x + thickness;
  421.   points [2].y = y + height - thickness;
  422.   points [3].x = x + thickness;
  423.   points [3].y = y + thickness;
  424.   XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
  425.   points [0].x = x + width;
  426.   points [0].y = y;
  427.   points [1].x = x + width - thickness;
  428.   points [1].y = y + thickness;
  429.   points [2].x = x + width - thickness;
  430.   points [2].y = y + height - thickness;
  431.   points [3].x = x + width;
  432.   points [3].y = y + height - thickness;
  433.   XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
  434.   points [0].x = x;
  435.   points [0].y = y + height;
  436.   points [1].x = x + width;
  437.   points [1].y = y + height;
  438.   points [2].x = x + width;
  439.   points [2].y = y + height - thickness;
  440.   points [3].x = x + thickness;
  441.   points [3].y = y + height - thickness;
  442.   XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
  443. }
  444.  
  445.  
  446. /* Display the menu item and increment where.x and where.y to show how large
  447. ** the menu item was. 
  448. */
  449. static void
  450. display_menu_item (mw, val, ws, where, highlighted_p, horizontal_p, just_compute_p)
  451.      XlwMenuWidget mw;
  452.      widget_value* val;
  453.      window_state* ws;
  454.      XPoint* where;
  455.      Boolean highlighted_p;
  456.      Boolean horizontal_p;
  457.      Boolean just_compute_p;
  458. {
  459.   GC deco_gc;
  460.   GC text_gc;
  461.   int font_ascent = mw->menu.font->ascent;
  462.   int font_descent = mw->menu.font->descent;
  463.   int shadow = mw->menu.shadow_thickness;
  464.   int separator_p = all_dashes_p (val->name);
  465.   int h_spacing = mw->menu.horizontal_spacing;
  466.   int v_spacing = mw->menu.vertical_spacing;
  467.   int label_width;
  468.   int rest_width;
  469.   int height;
  470.   int width;
  471.   int button_p;
  472.  
  473.   /* compute the sizes of the item */
  474.   size_menu_item (mw, val, horizontal_p, &label_width, &rest_width, &height);
  475.  
  476.   if (horizontal_p)
  477.     width = label_width + rest_width;
  478.   else
  479.     {
  480.       label_width = ws->label_width;
  481.       width = ws->width - 2 * shadow;
  482.     }
  483.  
  484.   /* see if it should be a button in the menubar */
  485.   button_p = horizontal_p && val->call_data;
  486.  
  487.   /* Only highlight an enabled item that has a callback. */
  488.   if (highlighted_p)
  489.     if (!val->enabled || !(val->call_data || val->contents))
  490.       highlighted_p = 0;
  491.  
  492.   /* do the drawing. */
  493.   if (!just_compute_p)
  494.     {
  495.       /* Add the shadow border of the containing menu */
  496.       int x = where->x + shadow;
  497.       int y = where->y + shadow;
  498.  
  499.       /* pick the foreground and background GC. */
  500.       if (val->enabled)
  501.     text_gc = button_p ? mw->menu.button_gc : mw->menu.foreground_gc;
  502.       else
  503.     text_gc =
  504.       button_p ? mw->menu.inactive_button_gc : mw->menu.inactive_gc;
  505.       deco_gc = mw->menu.foreground_gc;
  506.  
  507.       if (separator_p)
  508.     {
  509.       XDrawLine (XtDisplay (mw), ws->window, mw->menu.shadow_bottom_gc,
  510.              x, y, x + width, y);
  511.       XDrawLine (XtDisplay (mw), ws->window, mw->menu.shadow_top_gc,
  512.              x, y + 1, x + width, y + 1);
  513.     }
  514.       else 
  515.     {
  516.       char* display_string = resource_widget_value (mw, val);
  517.       draw_shadow_rectangle (mw, ws->window, x, y, width, height, True);
  518.       XDrawString (XtDisplay (mw), ws->window, text_gc,
  519.                x + h_spacing + shadow,
  520.                y + v_spacing + shadow + font_ascent,
  521.                display_string, strlen (display_string));
  522.       
  523.       if (!horizontal_p)
  524.         {
  525.           if (val->contents)
  526.         {
  527.           int a_w = arrow_width (mw);
  528.           draw_arrow (mw, ws->window, deco_gc,
  529.                   x + width - arrow_width (mw)
  530.                   - mw->menu.horizontal_spacing 
  531.                   - mw->menu.shadow_thickness,
  532.                   y + v_spacing + shadow, a_w);
  533.         }
  534.           else if (val->key)
  535.         {
  536.           XDrawString (XtDisplay (mw), ws->window, text_gc,
  537.                    x + label_width + mw->menu.arrow_spacing,
  538.                    y + v_spacing + shadow + font_ascent,
  539.                    val->key, strlen (val->key));
  540.         }
  541.         }
  542.  
  543.       else if (button_p)
  544.         {
  545. #if 1
  546.           XDrawRectangle (XtDisplay (mw), ws->window, deco_gc,
  547.                   x + shadow, y + shadow,
  548.                   label_width + h_spacing - 1,
  549.                   font_ascent + font_descent + 2 * v_spacing - 1);
  550.           draw_shadow_rectangle (mw, ws->window, x, y, width, height,
  551.                      False);
  552. #else
  553.           highlighted_p = True;
  554. #endif
  555.         }
  556.       else
  557.         {
  558.           XDrawRectangle (XtDisplay (mw), ws->window, 
  559.                   mw->menu.background_gc,
  560.                   x + shadow, y + shadow,
  561.                   label_width + h_spacing - 1,
  562.                   font_ascent + font_descent + 2 * v_spacing - 1);
  563.           draw_shadow_rectangle (mw, ws->window, x, y, width, height,
  564.                      True);
  565.         }
  566.  
  567.       if (highlighted_p)
  568.         draw_shadow_rectangle (mw, ws->window, x, y, width, height, False);
  569.     }
  570.     }
  571.   
  572.   where->x += width;
  573.   where->y += height;
  574. }
  575.  
  576. static void
  577. display_menu (mw, level, just_compute_p, highlighted_pos, hit, hit_return,
  578.           this, that)
  579.      XlwMenuWidget mw;
  580.      int level;
  581.      Boolean just_compute_p;
  582.      XPoint* highlighted_pos;
  583.      XPoint* hit;
  584.      widget_value** hit_return;
  585.      widget_value* this;
  586.      widget_value* that;
  587. {
  588.   widget_value*    val;
  589.   widget_value* following_item;
  590.   window_state* ws;
  591.   XPoint    where;
  592.   int horizontal_p = mw->menu.horizontal && (level == 0);
  593.   int highlighted_p;
  594.   int just_compute_this_one_p;
  595.  
  596.   if (level >= mw->menu.old_depth)
  597.     abort ();
  598.  
  599.   if (level < mw->menu.old_depth - 1)
  600.     following_item = mw->menu.old_stack [level + 1];
  601.   else 
  602.     following_item = NULL;
  603.  
  604.   if (hit)
  605.     *hit_return = NULL;
  606.  
  607.   where.x = 0;
  608.   where.y = 0;
  609.  
  610.   ws = &mw->menu.windows [level];
  611.   for (val = mw->menu.old_stack [level]->contents; val; val = val->next)
  612.     {
  613.       highlighted_p = val == following_item;
  614.       if (highlighted_p && highlighted_pos)
  615.     {
  616.       if (horizontal_p)
  617.         highlighted_pos->x = where.x;
  618.       else
  619.         highlighted_pos->y = where.y;
  620.     }
  621.       
  622.       just_compute_this_one_p =
  623.     just_compute_p || ((this || that) && val != this &&  val != that);
  624.  
  625.       display_menu_item (mw, val, ws, &where, highlighted_p, horizontal_p,
  626.              just_compute_this_one_p);
  627.  
  628.       if (highlighted_p && highlighted_pos)
  629.     {
  630.       if (horizontal_p)
  631.         highlighted_pos->y = where.y;
  632.       else
  633.         highlighted_pos->x = where.x;
  634.     }
  635.  
  636.       if (hit
  637.       && !*hit_return
  638.       && (horizontal_p ? hit->x < where.x : hit->y < where.y)
  639.       && !all_dashes_p (val->name))
  640.     *hit_return = val;
  641.  
  642.       if (horizontal_p)
  643.     where.y = 0;
  644.       else
  645.     where.x = 0;
  646.     }
  647.   
  648.   if (!just_compute_p)
  649.     draw_shadow_rectangle (mw, ws->window, 0, 0, ws->width, ws->height, False);
  650. }
  651.  
  652. /* Motion code */
  653. static void
  654. set_new_state (mw, val, level)
  655.      XlwMenuWidget mw;
  656.      widget_value* val;
  657.      int level;
  658. {
  659.   int i;
  660.   
  661.   mw->menu.new_depth = 0;
  662.   for (i = 0; i < level; i++)
  663.     push_new_stack (mw, mw->menu.old_stack [i]);
  664.   push_new_stack (mw, val);
  665. }
  666.  
  667. static void
  668. make_windows_if_needed (mw, n)
  669.      XlwMenuWidget mw;
  670.      int n;
  671. {
  672.   int i;
  673.   int start_at;
  674.   XSetWindowAttributes xswa;
  675.   int mask;
  676.   Window root = RootWindowOfScreen (DefaultScreenOfDisplay (XtDisplay (mw)));
  677.   window_state* windows;
  678.   
  679.   if (mw->menu.windows_length >= n)
  680.     return;
  681.  
  682.   xswa.save_under = True;
  683.   xswa.override_redirect = True;
  684.   xswa.background_pixel = mw->core.background_pixel;
  685.   xswa.border_pixel = mw->core.border_pixel;
  686.   xswa.event_mask =
  687.     ExposureMask | PointerMotionMask | PointerMotionHintMask
  688.       | ButtonReleaseMask | ButtonPressMask;
  689.   xswa.cursor = mw->menu.cursor_shape;
  690.   mask = CWSaveUnder | CWOverrideRedirect | CWBackPixel | CWBorderPixel
  691.     | CWEventMask | CWCursor;
  692.   
  693.   if (!mw->menu.windows)
  694.     {
  695.       mw->menu.windows =
  696.     (window_state*)XtMalloc (n * sizeof (window_state));
  697.       start_at = 0;
  698.     }
  699.   else
  700.     {
  701.       mw->menu.windows =
  702.     (window_state*)XtRealloc ((char*)mw->menu.windows,
  703.                   n * sizeof (window_state));
  704.       start_at = mw->menu.windows_length;
  705.     }
  706.   mw->menu.windows_length = n;
  707.  
  708.   windows = mw->menu.windows;
  709.  
  710.   for (i = start_at; i < n; i++)
  711.    {
  712.      windows [i].x = 0;
  713.      windows [i].y = 0;
  714.      windows [i].width = 1;
  715.      windows [i].height = 1;
  716.      windows [i].window =
  717.        XCreateWindow (XtDisplay (mw), root, 0, 0, 1, 1,
  718.               0, 0, CopyFromParent, CopyFromParent, mask, &xswa);
  719.   }
  720. }
  721.  
  722. /* Make the window fit in the screen */
  723. static void
  724. fit_to_screen (mw, ws, previous_ws, horizontal_p)
  725.      XlwMenuWidget mw;
  726.      window_state* ws;
  727.      window_state* previous_ws;
  728.      Boolean horizontal_p;
  729. {
  730.   int screen_width = WidthOfScreen (XtScreen (mw));
  731.   int screen_height = HeightOfScreen (XtScreen (mw));
  732.  
  733.   if (ws->x < 0)
  734.     ws->x = 0;
  735.   else if (ws->x + ws->width > screen_width)
  736.     {
  737.       if (!horizontal_p)
  738.     ws->x = previous_ws->x - ws->width;
  739.       else
  740.     ws->x = screen_width - ws->width;
  741.     }
  742.   if (ws->y < 0)
  743.     ws->y = 0;
  744.   else if (ws->y + ws->height > screen_height)
  745.     {
  746.       if (horizontal_p)
  747.     ws->y = previous_ws->y - ws->height;
  748.       else
  749.     ws->y = screen_height - ws->height;
  750.     }
  751. }
  752.  
  753. /* Updates old_stack from new_stack and redisplays. */
  754. static void
  755. remap_menubar (mw)
  756.      XlwMenuWidget mw;
  757. {
  758.   int i;
  759.   int last_same;
  760.   XPoint selection_position;
  761.   int old_depth = mw->menu.old_depth;
  762.   int new_depth = mw->menu.new_depth;
  763.   widget_value** old_stack;
  764.   widget_value** new_stack;
  765.   window_state* windows;
  766.   widget_value* old_selection;
  767.   widget_value* new_selection;
  768.  
  769.   /* Check that enough windows and old_stack are ready. */
  770.   make_windows_if_needed (mw, new_depth);
  771.   make_old_stack_space (mw, new_depth);
  772.   windows = mw->menu.windows;
  773.   old_stack = mw->menu.old_stack;
  774.   new_stack = mw->menu.new_stack;
  775.  
  776.   /* compute the last identical different entry */
  777.   for (i = 1; i < old_depth && i < new_depth; i++)
  778.     if (old_stack [i] != new_stack [i])
  779.       break;
  780.   last_same = i - 1;
  781.  
  782.   /* Memorize the previously selected item to be able to refresh it */
  783.   old_selection = last_same + 1 < old_depth ? old_stack [last_same + 1] : NULL;
  784.   if (old_selection && !old_selection->enabled)
  785.     old_selection = NULL;
  786.   new_selection = last_same + 1 < new_depth ? new_stack [last_same + 1] : NULL;
  787.   if (new_selection && !new_selection->enabled)
  788.     new_selection = NULL;
  789.  
  790.   /* updates old_state from new_state.  It has to be done now because
  791.      display_menu (called below) uses the old_stack to know what to display. */
  792.   for (i = last_same + 1; i < new_depth; i++)
  793.     old_stack [i] = new_stack [i];
  794.   mw->menu.old_depth = new_depth;
  795.  
  796.   /* refresh the last seletion */
  797.   selection_position.x = 0;
  798.   selection_position.y = 0;
  799.   display_menu (mw, last_same, new_selection == old_selection,
  800.         &selection_position, NULL, NULL, old_selection, new_selection);
  801.  
  802.   /* Now popup the new menus */
  803.   for (i = last_same + 1; i < new_depth && new_stack [i]->contents; i++)
  804.     {
  805.       window_state* previous_ws = &windows [i - 1];
  806.       window_state* ws = &windows [i];
  807.  
  808.       ws->x =
  809.     previous_ws->x + selection_position.x + mw->menu.shadow_thickness;
  810.       if (!mw->menu.horizontal || i > 1)
  811.     ws->x += mw->menu.shadow_thickness;
  812.       ws->y =
  813.     previous_ws->y + selection_position.y + mw->menu.shadow_thickness;
  814.  
  815.       size_menu (mw, i);
  816.  
  817.       fit_to_screen (mw, ws, previous_ws, mw->menu.horizontal && i == 1);
  818.  
  819.       XClearWindow (XtDisplay (mw), ws->window);
  820.       XMoveResizeWindow (XtDisplay (mw), ws->window, ws->x, ws->y,
  821.              ws->width, ws->height);
  822.       XMapRaised (XtDisplay (mw), ws->window);
  823.       display_menu (mw, i, False, &selection_position, NULL, NULL, NULL, NULL);
  824.     }
  825.  
  826.   /* unmap the menus that popped down */
  827.   for (i = new_depth - 1; i < old_depth; i++)
  828.     if (i >= new_depth || !new_stack [i]->contents)
  829.       XUnmapWindow (XtDisplay (mw), windows [i].window);
  830. }
  831.  
  832. static Boolean
  833. motion_event_is_in_menu (mw, ev, level, relative_pos)
  834.      XlwMenuWidget mw;
  835.      XMotionEvent* ev;
  836.      int level;
  837.      XPoint* relative_pos;
  838. {
  839.   window_state* ws = &mw->menu.windows [level];
  840.   int x = level == 0 ? ws->x : ws->x + mw->menu.shadow_thickness;
  841.   int y = level == 0 ? ws->y : ws->y + mw->menu.shadow_thickness;
  842.   relative_pos->x = ev->x_root - x;
  843.   relative_pos->y = ev->y_root - y;
  844.   return (x < ev->x_root && ev->x_root < x + ws->width
  845.       && y < ev->y_root && ev->y_root < y + ws->height);
  846. }
  847.  
  848. static Boolean
  849. map_event_to_widget_value (mw, ev, val, level)
  850.      XlwMenuWidget mw;
  851.      XMotionEvent* ev;
  852.      widget_value** val;
  853.      int* level;
  854. {
  855.   int         i;
  856.   XPoint    relative_pos;
  857.   window_state*    ws;
  858.  
  859.   *val = NULL;
  860.   
  861.   /* Find the window */
  862.   for (i = mw->menu.old_depth - 1; i >= 0; i--)
  863.     {
  864.       ws = &mw->menu.windows [i];
  865.       if (ws && motion_event_is_in_menu (mw, ev, i, &relative_pos))
  866.     {
  867.       display_menu (mw, i, True, NULL, &relative_pos, val, NULL, NULL);
  868.  
  869.       if (*val)
  870.         {
  871.           *level = i + 1;
  872.           return True;
  873.         }
  874.     }
  875.     }
  876.   return False;
  877. }
  878.  
  879. /* Procedures */
  880. static void
  881. make_drawing_gcs (mw)
  882.      XlwMenuWidget mw;
  883. {
  884.   XGCValues xgcv;
  885.  
  886.   xgcv.font = mw->menu.font->fid;
  887.   xgcv.foreground = mw->menu.foreground;
  888.   xgcv.background = mw->core.background_pixel;
  889.   mw->menu.foreground_gc = XtGetGC ((Widget)mw,
  890.                     GCFont | GCForeground | GCBackground,
  891.                     &xgcv);
  892.   
  893.   xgcv.font = mw->menu.font->fid;
  894.   xgcv.foreground = mw->menu.button_foreground;
  895.   xgcv.background = mw->core.background_pixel;
  896.   mw->menu.button_gc = XtGetGC ((Widget)mw,
  897.                 GCFont | GCForeground | GCBackground,
  898.                 &xgcv);
  899.   
  900.   xgcv.font = mw->menu.font->fid;
  901.   xgcv.foreground = mw->menu.foreground;
  902.   xgcv.background = mw->core.background_pixel;
  903.   xgcv.fill_style = FillStippled;
  904.   xgcv.stipple = mw->menu.gray_pixmap;
  905.   mw->menu.inactive_gc = XtGetGC ((Widget)mw,
  906.                   (GCFont | GCForeground | GCBackground
  907.                    | GCFillStyle | GCStipple), &xgcv);
  908.   
  909.   xgcv.font = mw->menu.font->fid;
  910.   xgcv.foreground = mw->menu.button_foreground;
  911.   xgcv.background = mw->core.background_pixel;
  912.   xgcv.fill_style = FillStippled;
  913.   xgcv.stipple = mw->menu.gray_pixmap;
  914.   mw->menu.inactive_button_gc = XtGetGC ((Widget)mw,
  915.                   (GCFont | GCForeground | GCBackground
  916.                    | GCFillStyle | GCStipple), &xgcv);
  917.   
  918.   xgcv.font = mw->menu.font->fid;
  919.   xgcv.foreground = mw->core.background_pixel;
  920.   xgcv.background = mw->menu.foreground;
  921.   mw->menu.background_gc = XtGetGC ((Widget)mw,
  922.                     GCFont | GCForeground | GCBackground,
  923.                     &xgcv);
  924. }
  925.  
  926. static void
  927. release_drawing_gcs (mw)
  928.      XlwMenuWidget mw;
  929. {
  930.   XtReleaseGC ((Widget) mw, mw->menu.foreground_gc);
  931.   XtReleaseGC ((Widget) mw, mw->menu.button_gc);
  932.   XtReleaseGC ((Widget) mw, mw->menu.inactive_gc);
  933.   XtReleaseGC ((Widget) mw, mw->menu.inactive_button_gc);
  934.   XtReleaseGC ((Widget) mw, mw->menu.background_gc);
  935.   /* let's get some segvs if we try to use these... */
  936.   mw->menu.foreground_gc = (GC) -1;
  937.   mw->menu.button_gc = (GC) -1;
  938.   mw->menu.inactive_gc = (GC) -1;
  939.   mw->menu.inactive_button_gc = (GC) -1;
  940.   mw->menu.background_gc = (GC) -1;
  941. }
  942.  
  943. #define MINL(x,y) ((((unsigned long) (x)) < ((unsigned long) (y))) \
  944.            ? ((unsigned long) (x)) : ((unsigned long) (y)))
  945.  
  946. static void
  947. make_shadow_gcs (mw)
  948.      XlwMenuWidget mw;
  949. {
  950.   XGCValues xgcv;
  951.   unsigned long pm = 0;
  952.   Display *dpy = XtDisplay ((Widget) mw);
  953.   Colormap cmap = DefaultColormapOfScreen (XtScreen ((Widget) mw));
  954.   XColor topc, botc;
  955.   int top_frobbed = 0, bottom_frobbed = 0;
  956.  
  957.   if (mw->menu.top_shadow_color == -1)
  958.     mw->menu.top_shadow_color = mw->core.background_pixel;
  959.   if (mw->menu.bottom_shadow_color == -1)
  960.     mw->menu.bottom_shadow_color = mw->menu.foreground;
  961.  
  962.   if (mw->menu.top_shadow_color == mw->core.background_pixel ||
  963.       mw->menu.top_shadow_color == mw->menu.foreground)
  964.     {
  965.       topc.pixel = mw->core.background_pixel;
  966.       XQueryColor (dpy, cmap, &topc);
  967.       /* don't overflow/wrap! */
  968.       topc.red   = MINL (65535, topc.red   * 1.2);
  969.       topc.green = MINL (65535, topc.green * 1.2);
  970.       topc.blue  = MINL (65535, topc.blue  * 1.2);
  971.       if (XAllocColor (dpy, cmap, &topc))
  972.     {
  973.       mw->menu.top_shadow_color = topc.pixel;
  974.       top_frobbed = 1;
  975.     }
  976.     }
  977.   if (mw->menu.bottom_shadow_color == mw->menu.foreground ||
  978.       mw->menu.bottom_shadow_color == mw->core.background_pixel)
  979.     {
  980.       botc.pixel = mw->core.background_pixel;
  981.       XQueryColor (dpy, cmap, &botc);
  982.       botc.red   *= 0.6;
  983.       botc.green *= 0.6;
  984.       botc.blue  *= 0.6;
  985.       if (XAllocColor (dpy, cmap, &botc))
  986.     {
  987.       mw->menu.bottom_shadow_color = botc.pixel;
  988.       bottom_frobbed = 1;
  989.     }
  990.     }
  991.  
  992.   if (top_frobbed && bottom_frobbed)
  993.     {
  994.       int top_avg = ((topc.red / 3) + (topc.green / 3) + (topc.blue / 3));
  995.       int bot_avg = ((botc.red / 3) + (botc.green / 3) + (botc.blue / 3));
  996.       if (bot_avg > top_avg)
  997.     {
  998.       Pixel tmp = mw->menu.top_shadow_color;
  999.       mw->menu.top_shadow_color = mw->menu.bottom_shadow_color;
  1000.       mw->menu.bottom_shadow_color = tmp;
  1001.     }
  1002.       else if (topc.pixel == botc.pixel)
  1003.     {
  1004.       if (botc.pixel == mw->menu.foreground)
  1005.         mw->menu.top_shadow_color = mw->core.background_pixel;
  1006.       else
  1007.         mw->menu.bottom_shadow_color = mw->menu.foreground;
  1008.     }
  1009.     }
  1010.  
  1011.   if (!mw->menu.top_shadow_pixmap &&
  1012.       mw->menu.top_shadow_color == mw->core.background_pixel)
  1013.     {
  1014.       mw->menu.top_shadow_pixmap = mw->menu.gray_pixmap;
  1015.       mw->menu.top_shadow_color = mw->menu.foreground;
  1016.     }
  1017.   if (!mw->menu.bottom_shadow_pixmap &&
  1018.       mw->menu.bottom_shadow_color == mw->core.background_pixel)
  1019.     {
  1020.       mw->menu.bottom_shadow_pixmap = mw->menu.gray_pixmap;
  1021.       mw->menu.bottom_shadow_color = mw->menu.foreground;
  1022.     }
  1023.  
  1024.   xgcv.fill_style = FillStippled;
  1025.   xgcv.foreground = mw->menu.top_shadow_color;
  1026.   xgcv.stipple = mw->menu.top_shadow_pixmap;
  1027.   pm = (xgcv.stipple ? GCStipple|GCFillStyle : 0);
  1028.   mw->menu.shadow_top_gc = XtGetGC ((Widget)mw, GCForeground | pm, &xgcv);
  1029.  
  1030.   xgcv.foreground = mw->menu.bottom_shadow_color;
  1031.   xgcv.stipple = mw->menu.bottom_shadow_pixmap;
  1032.   pm = (xgcv.stipple ? GCStipple|GCFillStyle : 0);
  1033.   mw->menu.shadow_bottom_gc = XtGetGC ((Widget)mw, GCForeground | pm, &xgcv);
  1034. }
  1035.  
  1036.  
  1037. static void
  1038. release_shadow_gcs (mw)
  1039.      XlwMenuWidget mw;
  1040. {
  1041.   XtReleaseGC ((Widget) mw, mw->menu.shadow_top_gc);
  1042.   XtReleaseGC ((Widget) mw, mw->menu.shadow_bottom_gc);
  1043. }
  1044.  
  1045. static void
  1046. XlwMenuInitialize (request, new, args, num_args)
  1047.      Widget request;
  1048.      Widget new;
  1049.      ArgList args;
  1050.      Cardinal *num_args;
  1051. {
  1052.   /* Get the GCs and the widget size */
  1053.   XlwMenuWidget mw = (XlwMenuWidget)new;
  1054.   
  1055.   XSetWindowAttributes xswa;
  1056.   int mask;
  1057.   
  1058.   Window window = RootWindowOfScreen (DefaultScreenOfDisplay (XtDisplay (mw)));
  1059.   Display* display = XtDisplay (mw);
  1060.   
  1061. #if 0
  1062.   widget_value *tem = (widget_value *) XtMalloc (sizeof (widget_value));
  1063.  
  1064.   /* _XtCreate is freeing the object that was passed to us,
  1065.      so make a copy that we will actually keep.  */
  1066.   lwlib_bcopy (mw->menu.contents, tem, sizeof (widget_value));
  1067.   mw->menu.contents = tem;
  1068. #endif
  1069.  
  1070. /*  mw->menu.cursor = XCreateFontCursor (display, mw->menu.cursor_shape); */
  1071.   mw->menu.cursor = mw->menu.cursor_shape;
  1072.   
  1073.   mw->menu.gray_pixmap = XCreatePixmapFromBitmapData (display, window,
  1074.                               gray_bits, gray_width,
  1075.                               gray_height, 1, 0, 1);
  1076.   
  1077.   make_drawing_gcs (mw);
  1078.   make_shadow_gcs (mw);
  1079.   
  1080.   xswa.background_pixel = mw->core.background_pixel;
  1081.   xswa.border_pixel = mw->core.border_pixel;
  1082.   mask = CWBackPixel | CWBorderPixel;
  1083.   
  1084.   mw->menu.popped_up = False;
  1085.   
  1086.   mw->menu.old_depth = 1;
  1087.   mw->menu.old_stack = (widget_value**)XtMalloc (sizeof (widget_value*));
  1088.   mw->menu.old_stack_length = 1;
  1089.   mw->menu.old_stack [0] = mw->menu.contents;
  1090.   
  1091.   mw->menu.new_depth = 0;
  1092.   mw->menu.new_stack = 0;
  1093.   mw->menu.new_stack_length = 0;
  1094.   push_new_stack (mw, mw->menu.contents);
  1095.   
  1096.   mw->menu.windows = (window_state*)XtMalloc (sizeof (window_state));
  1097.   mw->menu.windows_length = 1;
  1098.   mw->menu.windows [0].x = 0;
  1099.   mw->menu.windows [0].y = 0;
  1100.   mw->menu.windows [0].width = 0;
  1101.   mw->menu.windows [0].height = 0;
  1102.   size_menu (mw, 0);
  1103.   
  1104.   mw->core.width = mw->menu.windows [0].width;
  1105.   mw->core.height = mw->menu.windows [0].height;
  1106. }
  1107.  
  1108. static void
  1109. XlwMenuClassInitialize ()
  1110. {
  1111. }
  1112.  
  1113. static void
  1114. XlwMenuRealize (w, valueMask, attributes)
  1115.      Widget w;
  1116.      Mask *valueMask;
  1117.      XSetWindowAttributes *attributes;
  1118. {
  1119.   XlwMenuWidget mw = (XlwMenuWidget)w;
  1120.   XSetWindowAttributes xswa;
  1121.   int mask;
  1122.  
  1123.   (*xlwMenuWidgetClass->core_class.superclass->core_class.realize)
  1124.     (w, valueMask, attributes);
  1125.  
  1126.   xswa.save_under = True;
  1127.   xswa.cursor = mw->menu.cursor_shape;
  1128.   mask = CWSaveUnder | CWCursor;
  1129.   XChangeWindowAttributes (XtDisplay (w), XtWindow (w), mask, &xswa);
  1130.  
  1131.   mw->menu.windows [0].window = XtWindow (w);
  1132.   mw->menu.windows [0].x = w->core.x;
  1133.   mw->menu.windows [0].y = w->core.y;
  1134.   mw->menu.windows [0].width = w->core.width;
  1135.   mw->menu.windows [0].height = w->core.height;
  1136. }
  1137.  
  1138. /* Only the toplevel menubar/popup is a widget so it's the only one that
  1139.    receives expose events through Xt.  So we repaint all the other panes
  1140.    when receiving an Expose event. */
  1141. static void 
  1142. XlwMenuRedisplay (w, ev, region)
  1143.      Widget w;
  1144.      XEvent* ev;
  1145.      Region region;
  1146. {
  1147.   XlwMenuWidget mw = (XlwMenuWidget)w;
  1148.   int i;
  1149.  
  1150.   /* If we have a depth beyond 1, it's because a submenu was displayed.
  1151.      If the submenu has been destroyed, set the depth back to 1.  */
  1152.   if (submenu_destroyed)
  1153.     {
  1154.       mw->menu.old_depth = 1;
  1155.       submenu_destroyed = 0;
  1156.     }
  1157.  
  1158.   for (i = 0; i < mw->menu.old_depth; i++)
  1159.     display_menu (mw, i, False, NULL, NULL, NULL, NULL, NULL);
  1160. }
  1161.  
  1162. static void 
  1163. XlwMenuDestroy (w)
  1164.      Widget w;
  1165. {
  1166.   int i;
  1167.   XlwMenuWidget mw = (XlwMenuWidget) w;
  1168.  
  1169.   if (pointer_grabbed)
  1170.     XtUngrabPointer ((Widget)w, CurrentTime);
  1171.   pointer_grabbed = 0;
  1172.  
  1173.   submenu_destroyed = 1;
  1174.  
  1175.   release_drawing_gcs (mw);
  1176.   release_shadow_gcs (mw);
  1177.  
  1178.   /* this doesn't come from the resource db but is created explicitly
  1179.      so we must free it ourselves. */
  1180.   XFreePixmap (XtDisplay (mw), mw->menu.gray_pixmap);
  1181.   mw->menu.gray_pixmap = (Pixmap) -1;
  1182.  
  1183. #if 0
  1184.   /* Do free mw->menu.contents because nowadays we copy it
  1185.      during initialization.  */
  1186.   XtFree (mw->menu.contents);
  1187. #endif
  1188.  
  1189.   /* Don't free mw->menu.contents because that comes from our creator.
  1190.      The `*_stack' elements are just pointers into `contents' so leave
  1191.      that alone too.  But free the stacks themselves. */
  1192.   if (mw->menu.old_stack) XtFree ((char *) mw->menu.old_stack);
  1193.   if (mw->menu.new_stack) XtFree ((char *) mw->menu.new_stack);
  1194.  
  1195.   /* Remember, you can't free anything that came from the resource
  1196.      database.  This includes:
  1197.          mw->menu.cursor
  1198.          mw->menu.top_shadow_pixmap
  1199.          mw->menu.bottom_shadow_pixmap
  1200.          mw->menu.font
  1201.      Also the color cells of top_shadow_color, bottom_shadow_color,
  1202.      foreground, and button_foreground will never be freed until this
  1203.      client exits.  Nice, eh?
  1204.    */
  1205.  
  1206.   /* start from 1 because the one in slot 0 is w->core.window */
  1207.   for (i = 1; i < mw->menu.windows_length; i++)
  1208.     XDestroyWindow (XtDisplay (mw), mw->menu.windows [i].window);
  1209.   if (mw->menu.windows)
  1210.     XtFree ((char *) mw->menu.windows);
  1211. }
  1212.  
  1213. static Boolean 
  1214. XlwMenuSetValues (current, request, new)
  1215.      Widget current;
  1216.      Widget request;
  1217.      Widget new;
  1218. {
  1219.   XlwMenuWidget oldmw = (XlwMenuWidget)current;
  1220.   XlwMenuWidget newmw = (XlwMenuWidget)new;
  1221.   Boolean redisplay = False;
  1222.   int i;
  1223.  
  1224.   if (newmw->menu.contents
  1225.       && newmw->menu.contents->contents
  1226.       && newmw->menu.contents->contents->change >= VISIBLE_CHANGE)
  1227.     redisplay = True;
  1228.  
  1229.   if (newmw->core.background_pixel != oldmw->core.background_pixel
  1230.       || newmw->menu.foreground != oldmw->menu.foreground
  1231.       || newmw->menu.font != oldmw->menu.font)
  1232.     {
  1233.       release_drawing_gcs (newmw);
  1234.       make_drawing_gcs (newmw);
  1235.       redisplay = True;
  1236.       
  1237.       for (i = 0; i < oldmw->menu.windows_length; i++)
  1238.     {
  1239.       XSetWindowBackground (XtDisplay (oldmw),
  1240.                 oldmw->menu.windows [i].window,
  1241.                 newmw->core.background_pixel);
  1242.       /* clear windows and generate expose events */
  1243.       XClearArea (XtDisplay (oldmw), oldmw->menu.windows[i].window,
  1244.               0, 0, 0, 0, True);
  1245.     }
  1246.     }
  1247.  
  1248.   return redisplay;
  1249. }
  1250.  
  1251. static void 
  1252. XlwMenuResize (w)
  1253.      Widget w;
  1254. {
  1255.   XlwMenuWidget mw = (XlwMenuWidget)w;
  1256.  
  1257.   if (mw->menu.popped_up)
  1258.     {
  1259.       /* Don't allow the popup menu to resize itself.  */
  1260.       mw->core.width = mw->menu.windows [0].width;
  1261.       mw->core.height = mw->menu.windows [0].height;
  1262.       mw->core.parent->core.width = mw->core.width ;
  1263.       mw->core.parent->core.height = mw->core.height ;
  1264.     }
  1265.   else
  1266.     {
  1267.       mw->menu.windows [0].width = mw->core.width;
  1268.       mw->menu.windows [0].height = mw->core.height;
  1269.     }
  1270. }
  1271.  
  1272. /* Action procedures */
  1273. static void
  1274. handle_single_motion_event (mw, ev)
  1275.      XlwMenuWidget mw;
  1276.      XMotionEvent* ev;
  1277. {
  1278.   widget_value*    val;
  1279.   int         level;
  1280.  
  1281.   if (!map_event_to_widget_value (mw, ev, &val, &level))
  1282.     pop_new_stack_if_no_contents (mw);
  1283.   else
  1284.     set_new_state (mw, val, level);
  1285.   remap_menubar (mw);
  1286.   
  1287. #if 0
  1288.   /* Sync with the display.  Makes it feel better on X terms. */
  1289.   XSync (XtDisplay (mw), False);
  1290. #endif
  1291. }
  1292.  
  1293. static void
  1294. handle_motion_event (mw, ev)
  1295.      XlwMenuWidget mw;
  1296.      XMotionEvent* ev;
  1297. {
  1298.   int x = ev->x_root;
  1299.   int y = ev->y_root;
  1300.   int state = ev->state;
  1301.  
  1302.   handle_single_motion_event (mw, ev);
  1303.  
  1304.   /* allow motion events to be generated again */
  1305.   if (ev->is_hint
  1306.       && XQueryPointer (XtDisplay (mw), ev->window,
  1307.             &ev->root, &ev->subwindow,
  1308.             &ev->x_root, &ev->y_root,
  1309.             &ev->x, &ev->y,
  1310.             &ev->state)
  1311.       && ev->state == state
  1312.       && (ev->x_root != x || ev->y_root != y))
  1313.     handle_single_motion_event (mw, ev);
  1314. }
  1315.  
  1316. static void 
  1317. Start (w, ev, params, num_params)
  1318.      Widget w;
  1319.      XEvent *ev;
  1320.      String *params;
  1321.      Cardinal *num_params;
  1322. {
  1323.   XlwMenuWidget mw = (XlwMenuWidget)w;
  1324.  
  1325.   if (!mw->menu.popped_up)
  1326.     {
  1327.       menu_post_event = *ev;
  1328.       next_release_must_exit = 0;
  1329.     }
  1330.   else
  1331.     /* If we push a button while the menu is posted semipermanently,
  1332.        releasing the button should always pop the menu down.  */
  1333.     next_release_must_exit = 1;
  1334.  
  1335.   XtCallCallbackList ((Widget)mw, mw->menu.open, NULL);
  1336.   
  1337.   /* notes the absolute position of the menubar window */
  1338.   mw->menu.windows [0].x = ev->xmotion.x_root - ev->xmotion.x;
  1339.   mw->menu.windows [0].y = ev->xmotion.y_root - ev->xmotion.y;
  1340.  
  1341.   /* handles the down like a move, slots are compatible */
  1342.   handle_motion_event (mw, &ev->xmotion);
  1343. }
  1344.  
  1345. static void 
  1346. Drag (w, ev, params, num_params)
  1347.      Widget w;
  1348.      XEvent *ev;
  1349.      String *params;
  1350.      Cardinal *num_params;
  1351. {
  1352.   XlwMenuWidget mw = (XlwMenuWidget)w;
  1353.   handle_motion_event (mw, &ev->xmotion);
  1354. }
  1355.  
  1356. static void 
  1357. Select (w, ev, params, num_params)
  1358.      Widget w;
  1359.      XEvent *ev;
  1360.      String *params;
  1361.      Cardinal *num_params;
  1362. {
  1363.   XlwMenuWidget mw = (XlwMenuWidget)w;
  1364.   widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
  1365.   
  1366.   /* If user releases the button quickly, without selecting anything,
  1367.      after the initial down-click that brought the menu up,
  1368.      do nothing.  */
  1369.   if ((selected_item == 0
  1370.        || ((widget_value *) selected_item)->call_data == 0)
  1371.       && !next_release_must_exit
  1372.       && (ev->xbutton.time - menu_post_event.xbutton.time
  1373.       < XtGetMultiClickTime (XtDisplay (w))))
  1374.     return;
  1375.  
  1376.   /* pop down everything.  */
  1377.   mw->menu.new_depth = 1;
  1378.   remap_menubar (mw);
  1379.  
  1380.   if (mw->menu.popped_up)
  1381.     {
  1382.       mw->menu.popped_up = False;
  1383.       XtUngrabPointer ((Widget)mw, ev->xmotion.time);
  1384.       XtPopdown (XtParent (mw));
  1385.     }
  1386.  
  1387.   /* callback */
  1388.   XtCallCallbackList ((Widget)mw, mw->menu.select, (XtPointer)selected_item);
  1389.   
  1390. }
  1391.  
  1392.  
  1393. /* Special code to pop-up a menu */
  1394. void
  1395. pop_up_menu (mw, event)
  1396.      XlwMenuWidget mw;
  1397.      XButtonPressedEvent* event;
  1398. {
  1399.   int        x = event->x_root;
  1400.   int        y = event->y_root;
  1401.   int        w;
  1402.   int        h;
  1403.   int        borderwidth = mw->menu.shadow_thickness;
  1404.   Screen*    screen = XtScreen (mw);
  1405.  
  1406.   next_release_must_exit = 0;
  1407.  
  1408.   XtCallCallbackList ((Widget)mw, mw->menu.open, NULL);
  1409.  
  1410.   size_menu (mw, 0);
  1411.  
  1412.   w = mw->menu.windows [0].width;
  1413.   h = mw->menu.windows [0].height;
  1414.  
  1415.   x -= borderwidth;
  1416.   y -= borderwidth;
  1417.   if (x < borderwidth)
  1418.     x = borderwidth;
  1419.   if (x + w + 2 * borderwidth > WidthOfScreen (screen))
  1420.     x = WidthOfScreen (screen) - w - 2 * borderwidth;
  1421.   if (y < borderwidth)
  1422.     y = borderwidth;
  1423.   if (y + h + 2 * borderwidth> HeightOfScreen (screen))
  1424.     y = HeightOfScreen (screen) - h - 2 * borderwidth;
  1425.  
  1426.   mw->menu.popped_up = True;
  1427.   XtConfigureWidget (XtParent (mw), x, y, w, h,
  1428.              XtParent (mw)->core.border_width);
  1429.   XtPopup (XtParent (mw), XtGrabExclusive);
  1430.   display_menu (mw, 0, False, NULL, NULL, NULL, NULL, NULL);
  1431. #ifdef emacs
  1432.   x_catch_errors ();
  1433. #endif
  1434.   XtGrabPointer ((Widget)mw, False,
  1435.          (PointerMotionMask | PointerMotionHintMask | ButtonReleaseMask
  1436.           | ButtonPressMask),
  1437.          GrabModeAsync, GrabModeAsync, None, mw->menu.cursor_shape,
  1438.          event->time);
  1439.   pointer_grabbed = 1;
  1440. #ifdef emacs
  1441.   if (x_had_errors_p ())
  1442.     {
  1443.       pointer_grabbed = 0;
  1444.       XtUngrabPointer ((Widget)mw, event->time);
  1445.     }
  1446.   x_uncatch_errors ();
  1447. #endif
  1448.  
  1449.   mw->menu.windows [0].x = x + borderwidth;
  1450.   mw->menu.windows [0].y = y + borderwidth;
  1451.  
  1452.   handle_motion_event (mw, (XMotionEvent*)event);
  1453. }
  1454.