home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 2: Applications / Linux Cubed Series 2 - Applications.iso / editors / emacs / xemacs / xemacs-1.004 / xemacs-1 / xemacs-19.13 / lwlib / xlwmenu.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-07-28  |  83.2 KB  |  3,172 lines

  1. /* Implements a lightweight menubar widget.  
  2.    Copyright (C) 1992, 1993, 1994 Lucid, Inc.
  3.    Copyright (C) 1995 Tinker Systems and INS Engineering Corp.
  4.  
  5. This file is part of the Lucid Widget Library.
  6.  
  7. The Lucid Widget Library is free software; you can redistribute it and/or 
  8. modify it under the terms of the GNU General Public License as published by
  9. the Free Software Foundation; either version 2, or (at your option)
  10. any later version.
  11.  
  12. The Lucid Widget Library is distributed in the hope that it will be useful,
  13. but WITHOUT ANY WARRANTY; without even the implied warranty of 
  14. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15. GNU General Public License for more details.
  16.  
  17. You should have received a copy of the GNU General Public License
  18. along with GNU Emacs; see the file COPYING.  If not, write to
  19. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
  20.  
  21. /* Created by devin@lucid.com */
  22.  
  23. #include <stdlib.h>
  24. #include <unistd.h>
  25. #include <string.h>
  26. #include <ctype.h>
  27. #include <stdio.h>
  28.  
  29. #include <sys/types.h>
  30. #include <X11/Xos.h>
  31. #include <X11/IntrinsicP.h>
  32. #include <X11/StringDefs.h>
  33. #include <X11/cursorfont.h>
  34. #include <X11/bitmaps/gray>
  35.  
  36. #ifdef NEED_MOTIF
  37. #include <Xm/Xm.h>
  38. #endif
  39. #include "xlwmenuP.h"
  40.  
  41. static char 
  42. xlwMenuTranslations [] = 
  43. "<BtnDown>:    start()\n\
  44. <BtnMotion>:    drag()\n\
  45. <BtnUp>:    select()\n\
  46. ";
  47.  
  48. #define offset(field) XtOffset(XlwMenuWidget, field)
  49. static XtResource 
  50. xlwMenuResources[] =
  51. #ifdef NEED_MOTIF
  52.   /* There are three font list resources, so that we can accept either of
  53.      the resources *fontList: or *font:, and so that we can tell the
  54.      difference between them being specified, and being defaulted to a
  55.      font from the XtRString specified here.
  56.    */
  57.   {XmNfontList,  XmCFontList, XmRFontList, sizeof(XmFontList),
  58.      offset(menu.font_list),  XtRImmediate, (XtPointer)0},
  59.   {XtNfont,      XtCFont,     XmRFontList, sizeof(XmFontList),
  60.      offset(menu.font_list_2),XtRImmediate, (XtPointer)0},
  61.   {XmNfontList,  XmCFontList, XmRFontList, sizeof(XmFontList),
  62.      offset(menu.fallback_font_list),
  63.      /* We must use an iso8859-1 font here, or people without $LANG set lose.
  64.     It's fair to assume that those who do have $LANG set also have the
  65.     *fontList resource set, or at least know how to deal with this.
  66.       */
  67.      XtRString, "-*-helvetica-bold-r-*-*-*-120-*-*-*-*-iso8859-1"},
  68. #else
  69.   {XtNfont,  XtCFont, XtRFontStruct, sizeof(XFontStruct *),
  70.      offset(menu.font),XtRString, "XtDefaultFont"},
  71. #endif
  72.   {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
  73.      offset(menu.foreground), XtRString, "XtDefaultForeground"},
  74.   {XtNbuttonForeground, XtCButtonForeground, XtRPixel, sizeof(Pixel),
  75.      offset(menu.button_foreground), XtRString, "XtDefaultForeground"},
  76.   {XtNmargin, XtCMargin, XtRDimension,  sizeof(Dimension),
  77.      offset(menu.margin), XtRImmediate, (XtPointer)2},
  78.   {XmNmarginWidth, XmCMarginWidth, XmRHorizontalDimension, sizeof(Dimension),
  79.      offset(menu.horizontal_margin), XtRImmediate, (XtPointer)2},
  80.   {XmNmarginHeight, XmCMarginHeight, XmRVerticalDimension, sizeof(Dimension),
  81.      offset(menu.vertical_margin), XtRImmediate, (XtPointer)1},
  82.   {XmNspacing, XmCSpacing, XmRHorizontalDimension,  sizeof(Dimension),
  83.      offset(menu.column_spacing), XtRImmediate, (XtPointer)4},
  84.   {XmNindicatorSize, XmCIndicatorSize, XtRDimension,  sizeof(Dimension),
  85.      offset(menu.indicator_size), XtRImmediate, (XtPointer)0},
  86.   {XmNshadowThickness, XmCShadowThickness, XmRHorizontalDimension,
  87.      sizeof (Dimension), offset (menu.shadow_thickness),
  88.      XtRImmediate, (XtPointer) 2},
  89.   {XmNselectColor, XmCSelectColor, XtRPixel, sizeof (Pixel),
  90.      offset (menu.select_color), XtRImmediate, (XtPointer)-1},
  91.   {XmNtopShadowColor, XmCTopShadowColor, XtRPixel, sizeof (Pixel),
  92.      offset (menu.top_shadow_color), XtRImmediate, (XtPointer)-1},
  93.   {XmNbottomShadowColor, XmCBottomShadowColor, XtRPixel, sizeof (Pixel),
  94.      offset (menu.bottom_shadow_color), XtRImmediate, (XtPointer)-1},
  95.   {XmNtopShadowPixmap, XmCTopShadowPixmap, XtRPixmap, sizeof (Pixmap),
  96.      offset (menu.top_shadow_pixmap), XtRImmediate, (XtPointer)None},
  97.   {XmNbottomShadowPixmap, XmCBottomShadowPixmap, XtRPixmap, sizeof (Pixmap),
  98.      offset (menu.bottom_shadow_pixmap), XtRImmediate, (XtPointer)None},
  99.  
  100.   {XtNopen, XtCCallback, XtRCallback, sizeof(XtPointer), 
  101.      offset(menu.open), XtRCallback, (XtPointer)NULL},
  102.   {XtNselect, XtCCallback, XtRCallback, sizeof(XtPointer), 
  103.      offset(menu.select), XtRCallback, (XtPointer)NULL},
  104.   {XtNmenu, XtCMenu, XtRPointer, sizeof(XtPointer),
  105.      offset(menu.contents), XtRImmediate, (XtPointer)NULL},
  106.   {XtNcursor, XtCCursor, XtRCursor, sizeof(Cursor),
  107.      offset(menu.cursor_shape), XtRString, (XtPointer)"right_ptr"},
  108.   {XtNhorizontal, XtCHorizontal, XtRInt, sizeof(int),
  109.      offset(menu.horizontal), XtRImmediate, (XtPointer)True},
  110.   {XtNuseBackingStore, XtCUseBackingStore, XtRBoolean, sizeof (Boolean),
  111.      offset (menu.use_backing_store), XtRImmediate, (XtPointer)False},
  112.   {XtNbounceDown, XtCBounceDown, XtRBoolean, sizeof (Boolean),
  113.      offset (menu.bounce_down), XtRImmediate, (XtPointer)True},
  114.   {XtNresourceLabels, XtCResourceLabels, XtRBoolean, sizeof (Boolean),
  115.      offset (menu.lookup_labels), XtRImmediate, (XtPointer)False},
  116. };
  117. #undef offset
  118.  
  119. static Boolean XlwMenuSetValues (Widget current, Widget request, Widget new,
  120.                  ArgList args, Cardinal *num_args);
  121. static void XlwMenuRealize (Widget w, Mask *valueMask,
  122.                 XSetWindowAttributes *attributes);
  123. static void XlwMenuRedisplay (Widget w, XEvent *ev, Region region);
  124. static void XlwMenuResize (Widget w);
  125. static void XlwMenuInitialize (Widget request, Widget new, ArgList args,
  126.                    Cardinal *num_args);
  127. static void XlwMenuDestroy (Widget w);
  128. static void XlwMenuClassInitialize (void);
  129. static void Start (Widget w, XEvent *ev, String *params, Cardinal *num_params);
  130. static void Drag (Widget w, XEvent *ev, String *params, Cardinal *num_params);
  131. static void Select (Widget w, XEvent *ev, String *params,
  132.  Cardinal *num_params);
  133.  
  134. #ifdef NEED_MOTIF
  135. static XFontStruct *default_font_of_font_list (XmFontList);
  136. #endif
  137.  
  138. static XtActionsRec 
  139. xlwMenuActionsList [] =
  140. {
  141.   {"start",        Start},
  142.   {"drag",        Drag},
  143.   {"select",        Select},
  144. };
  145.  
  146. #define SuperClass ((CoreWidgetClass)&coreClassRec)
  147.  
  148. XlwMenuClassRec xlwMenuClassRec =
  149. {
  150.   {  /* CoreClass fields initialization */
  151.     (WidgetClass) SuperClass,        /* superclass          */    
  152.     "XlwMenu",                /* class_name          */
  153.     sizeof(XlwMenuRec),            /* size              */
  154.     XlwMenuClassInitialize,        /* class_initialize      */
  155.     NULL,                /* class_part_initialize  */
  156.     FALSE,                /* class_inited          */
  157.     XlwMenuInitialize,            /* initialize          */
  158.     NULL,                /* initialize_hook      */
  159.     XlwMenuRealize,            /* realize          */
  160.     xlwMenuActionsList,            /* actions          */
  161.     XtNumber(xlwMenuActionsList),    /* num_actions          */
  162.     xlwMenuResources,            /* resources          */
  163.     XtNumber(xlwMenuResources),        /* resource_count      */
  164.     NULLQUARK,                /* xrm_class          */
  165.     TRUE,                /* compress_motion      */
  166.     TRUE,                /* compress_exposure      */
  167.     TRUE,                /* compress_enterleave    */
  168.     FALSE,                /* visible_interest      */
  169.     XlwMenuDestroy,            /* destroy          */
  170.     XlwMenuResize,            /* resize          */
  171.     XlwMenuRedisplay,            /* expose          */
  172.     XlwMenuSetValues,            /* set_values          */
  173.     NULL,                /* set_values_hook      */
  174.     XtInheritSetValuesAlmost,        /* set_values_almost      */
  175.     NULL,                /* get_values_hook      */
  176.     NULL,            /* #### - should this be set for grabs? accept_focus          */
  177.     XtVersion,                /* version          */
  178.     NULL,                /* callback_private      */
  179.     xlwMenuTranslations,        /* tm_table          */
  180.     XtInheritQueryGeometry,        /* query_geometry      */
  181.     XtInheritDisplayAccelerator,    /* display_accelerator      */
  182.     NULL                /* extension          */
  183.   },  /* XlwMenuClass fields initialization */
  184.   {
  185.     0                    /* dummy */
  186.   },
  187. };
  188.  
  189. WidgetClass xlwMenuWidgetClass = (WidgetClass) &xlwMenuClassRec;
  190.  
  191. /* Utilities */
  192. #if 0 /* Apparently not used anywhere */
  193.  
  194. static char *
  195. safe_strdup (char *s)
  196. {
  197.   char *result;
  198.   if (! s) return 0;
  199.   result = (char *) malloc (strlen (s) + 1);
  200.   if (! result)
  201.     return 0;
  202.   strcpy (result, s);
  203.   return result;
  204. }
  205.  
  206. #endif /* 0 */
  207.  
  208. static void
  209. push_new_stack (XlwMenuWidget mw, widget_value *val)
  210. {
  211.   if (!mw->menu.new_stack)
  212.     {
  213.       mw->menu.new_stack_length = 10;
  214.       mw->menu.new_stack =
  215.     (widget_value**)XtCalloc (mw->menu.new_stack_length,
  216.                   sizeof (widget_value*));
  217.     }
  218.   else if (mw->menu.new_depth == mw->menu.new_stack_length)
  219.     {
  220.       mw->menu.new_stack_length *= 2;
  221.       mw->menu.new_stack =
  222.     (widget_value**)XtRealloc ((char*)mw->menu.new_stack,
  223.                    mw->menu.new_stack_length *
  224.                    sizeof (widget_value*));
  225.     }
  226.   mw->menu.new_stack [mw->menu.new_depth++] = val;
  227. }
  228.  
  229. static void
  230. pop_new_stack_if_no_contents (XlwMenuWidget mw)
  231. {
  232.   if (mw->menu.new_depth)
  233.     {
  234.       if (!mw->menu.new_stack [mw->menu.new_depth - 1]->contents)
  235.     {
  236.       mw->menu.new_depth -= 1;
  237.     }
  238.     }
  239. }
  240.  
  241. static void
  242. make_old_stack_space (XlwMenuWidget mw, int n)
  243. {
  244.   if (!mw->menu.old_stack)
  245.     {
  246.       mw->menu.old_stack_length = 10;
  247.       mw->menu.old_stack =
  248.     (widget_value**)XtCalloc (mw->menu.old_stack_length,
  249.                   sizeof (widget_value*));
  250.     }
  251.   else if (mw->menu.old_stack_length < n)
  252.     {
  253.       mw->menu.old_stack_length *= 2;
  254.       mw->menu.old_stack =
  255.     (widget_value**)XtRealloc ((char*)mw->menu.old_stack,
  256.                    mw->menu.old_stack_length *
  257.                    sizeof (widget_value*));
  258.     }
  259. }
  260.  
  261. static Boolean
  262. close_to_reference_time(Widget w, Time reference_time, XEvent *ev)
  263. {
  264.   return (reference_time &&
  265.       (ev->xbutton.time - reference_time
  266.           < XtGetMultiClickTime (XtDisplay (w))));
  267. }
  268.  
  269. /* Size code */
  270. static int
  271. string_width (XlwMenuWidget mw,
  272. #ifdef NEED_MOTIF
  273.           XmString s
  274. #else
  275.           char *s
  276. #endif
  277.           )
  278. {
  279. #ifdef NEED_MOTIF
  280.   Dimension width, height;
  281.   XmStringExtent (mw->menu.font_list, s, &width, &height);
  282.   return width;
  283. #else
  284.   XCharStruct xcs;
  285.   int drop;
  286.   XTextExtents (mw->menu.font, s, strlen (s), &drop, &drop, &drop, &xcs);
  287.   return xcs.width;
  288. #endif
  289. }
  290.  
  291. static void
  292. massage_resource_name (CONST char *in, char *out)
  293. {
  294.   /* Turn a random string into something suitable for using as a resource.
  295.      For example:
  296.  
  297.      "Kill Buffer"        ->    "killBuffer"
  298.      "Find File..."        ->    "findFile"
  299.      "Search and Replace..."    ->    "searchAndReplace"
  300.    */
  301.  
  302. # define GOOD_CHAR(c) (((c) >= 'a' && (c) <= 'z') || \
  303.                ((c) >= 'A' && (c) <= 'Z') || \
  304.                ((c) >= '0' && (c) <= '9') || \
  305.                ((c) == '_') || \
  306.                ((c) > 0240))
  307.   int firstp = 1;
  308.   while (*in)
  309.     {
  310.       if (GOOD_CHAR ((unsigned char) *in))
  311.     {
  312.       if (firstp)
  313.         *out = tolower (*in);
  314.       else
  315.         *out = toupper (*in);
  316.       firstp = 0;
  317.       in++;
  318.       out++;
  319.       while (GOOD_CHAR ((unsigned char) *in))
  320.         {
  321.           *out = *in;
  322.           in++;
  323.           out++;
  324.         }
  325.     }
  326.       else
  327.     {
  328.       /* A bogus char between words; skip it. */
  329.       in++;
  330.     }
  331.     }
  332.   *out = 0;
  333. #undef GOOD_CHAR
  334. }
  335.  
  336. static XtResource
  337. nameResource[] =
  338.   { "labelString",  "LabelString", XtRString, sizeof(String),
  339.     0, XtRImmediate, 0 }
  340. };
  341.  
  342. /*
  343.  *    This function looks through string searching for parameter
  344.  *    inserts of the form:
  345.  *    %[padding]1
  346.  *    padding is space (' ') or dash ('-') characters meaning
  347.  *    padding to the left or right of the inserted parameter.
  348.  *    In essence all %1 strings are replaced by value in the return
  349.  *    value (which the caller is expected to free).
  350.  *    %% means insert one % (like printf).
  351.  *    %1 means insert value.
  352.  *    %-1 means insert value followed by one space. The latter is
  353.  *    not inserted if value is a zero length string.
  354.  */
  355. static char*
  356. parameterize_string (char *string, char *value)
  357. {
  358.   char *percent;
  359.   char *result;
  360.   unsigned done = 0;
  361.   unsigned ntimes;
  362.  
  363.   if (!string)
  364.     string = "";
  365.  
  366.   if (!value)
  367.     value = "";
  368.  
  369.   for (ntimes = 1, result = string; (percent = strchr(result, '%')); ntimes++)
  370.     result = &percent[1];
  371.   
  372.   result = XtMalloc((ntimes * strlen(value)) + strlen(string) + 4);
  373.   result[0] = '\0';
  374.  
  375.   while ((percent = strchr(string, '%')))
  376.     {
  377.       unsigned left_pad;
  378.       unsigned right_pad;
  379.       char *p;
  380.       
  381.       if (percent[1] == '%')
  382.     {    /* it's a real % */
  383.       strncat(result, string, 1 + percent - string); /* incl % */
  384.       string = &percent[2];    /* after the second '%' */
  385.       continue;        /* with the while() loop */
  386.     }
  387.       
  388.       left_pad = 0;
  389.       right_pad = 0;
  390.  
  391.       for (p = &percent[1]; /* test *p inside the loop */ ; p++)
  392.     {
  393.       if (*p == ' ')
  394.         {            /* left pad */
  395.           left_pad++;
  396.         }
  397.       else if (*p == '-')
  398.         {            /* right pad */
  399.           right_pad++;
  400.         }
  401.       else if (*p == '1')
  402.         {            /* param and terminator */
  403.           strncat(result, string, percent - string);
  404.           if (value[0] != '\0')
  405.         {
  406.           unsigned i;
  407.           for (i = 0; i < left_pad; i++)
  408.             strcat(result, " ");
  409.           strcat(result, value);
  410.           for (i = 0; i < right_pad; i++)
  411.             strcat(result, " ");
  412.         }
  413.           string = &p[1];    /* after the '1' */
  414.           done++;        /* no need to do old way */
  415.           break;        /* out of for() loop */
  416.         }
  417.       else
  418.         {            /* bogus, copy the format as is */
  419.                 /* out of for() loop */
  420.           strncat(result, string, 1 + p - string);
  421.           string= (*p ? &p[1] : p);
  422.           break;        
  423.         }
  424.     }
  425.     }
  426.  
  427.   /*
  428.    *    Copy the tail of the string
  429.    */
  430.   strcat(result, string);
  431.  
  432.   /*
  433.    *    If we have not processed a % string, and we have a value, tail it.
  434.    */
  435.   if (!done && value[0] != '\0')
  436.     {
  437.       strcat(result, " ");
  438.       strcat(result, value);
  439.     }
  440.  
  441.   return result;
  442. }
  443.  
  444. #ifdef NEED_MOTIF
  445.  
  446. static XmString
  447. resource_widget_value (XlwMenuWidget mw, widget_value *val)
  448. {
  449.   if (!val->toolkit_data)
  450.     {
  451.       char *resourced_name = NULL;
  452.       char *converted_name, *str;
  453.       XmString complete_name;
  454.       char massaged_name [1024];
  455.  
  456.       if (mw->menu.lookup_labels)
  457.     {
  458.       /* Convert value style name into resource style name.
  459.          eg: "Free Willy" becomes "freeWilly" */
  460.       massage_resource_name (val->name, massaged_name);
  461.       
  462.       /* If we have a value (parameter) see if we can find a "Named"
  463.          resource. */
  464.       if (val->value)
  465.         {
  466.           char named_name[1024];
  467.           sprintf(named_name, "%sNamed", massaged_name);
  468.           XtGetSubresources ((Widget) mw,
  469.                  (XtPointer) &resourced_name,
  470.                  named_name, named_name,
  471.                  nameResource, 1, NULL, 0);
  472.         }
  473.  
  474.       /* If nothing yet, try to load from the massaged name. */
  475.       if (!resourced_name)
  476.         {
  477.           XtGetSubresources ((Widget) mw,
  478.                  (XtPointer) &resourced_name,
  479.                  massaged_name, massaged_name,
  480.                  nameResource, 1, NULL, 0);
  481.         }
  482.     } /* if (mw->menu.lookup_labels) */
  483.  
  484.       /* Still nothing yet, use the name as the value. */
  485.       if (!resourced_name)
  486.     resourced_name = val->name;
  487.  
  488.       /* Parameterize the string. */
  489.       converted_name = parameterize_string(resourced_name, val->value);
  490.  
  491.       /* nuke newline characters to prevent menubar screwups */
  492.       for ( str = converted_name ; *str ; str++ )
  493.     {
  494.       if (str[0] == '\n') str[0] = ' ';
  495.     }
  496.  
  497.       /* Improve OSF's bottom line. */
  498.       complete_name = XmStringCreateLtoR (converted_name,
  499.                       XmSTRING_DEFAULT_CHARSET);
  500.       XtFree (converted_name);
  501.  
  502.       val->toolkit_data = complete_name;
  503.       val->free_toolkit_data = True;
  504.     }
  505.   return ((XmString) val->toolkit_data);
  506. }
  507.  
  508. /* Unused */
  509. #if 0
  510. /*
  511.  *    These two routines should be a seperate file..djw
  512.  */
  513. static char *
  514. xlw_create_localized_string (Widget w,
  515.                  char *name,
  516.                  char **args,
  517.                  unsigned nargs)
  518. {
  519.   char *string = NULL;
  520.   char *arg = NULL;
  521.  
  522.   if (nargs > 0)
  523.     arg = args[0];
  524.  
  525.   XtGetSubresources (w,
  526.              (XtPointer)&string,
  527.              name,
  528.              name,
  529.              nameResource, 1,
  530.              NULL, 0
  531.              );
  532.  
  533.   if (!string)
  534.     string = name;
  535.  
  536.   return parameterize_string (string, arg);
  537. }
  538.  
  539. static XmString
  540. xlw_create_localized_xmstring (Widget w,
  541.                    char *name,
  542.                    char **args,
  543.                    unsigned nargs)
  544. {
  545.   char *   string = xlw_create_localized_string (w, name, args, nargs);
  546.   XmString xm_string = XmStringCreateLtoR (string, XmSTRING_DEFAULT_CHARSET);
  547.   XtFree(string);
  548.   return xm_string;
  549. }
  550. #endif /* 0 */
  551.  
  552. #else /* !Motif */
  553.  
  554. static char*
  555. resource_widget_value (XlwMenuWidget mw, widget_value *val)
  556. {
  557.   if (!val->toolkit_data)
  558.     {
  559.       char *resourced_name = NULL;
  560.       char *complete_name;
  561.       char massaged_name [1024];
  562.  
  563.       if (mw->menu.lookup_labels)
  564.     {
  565.       massage_resource_name (val->name, massaged_name);
  566.       
  567.       XtGetSubresources ((Widget) mw,
  568.                  (XtPointer) &resourced_name,
  569.                  massaged_name, massaged_name,
  570.                  nameResource, 1, NULL, 0);
  571.     }
  572.       if (!resourced_name)
  573.     resourced_name = val->name;
  574.  
  575.       complete_name = parameterize_string(resourced_name, val->value);
  576.  
  577.       val->toolkit_data = complete_name;
  578.       /* nuke newline characters to prevent menubar screwups */
  579.       for ( ; *complete_name ; complete_name++ )
  580.     {
  581.       if (complete_name[0] == '\n')
  582.         complete_name[0] = ' ';
  583.     }
  584.       val->free_toolkit_data = True;
  585.     }
  586.   return (char*)val->toolkit_data;
  587. }
  588.  
  589. #endif /* !Motif */
  590.  
  591. /*
  592.  *    Code for drawing strings.
  593.  */
  594. static void
  595. string_draw(
  596.         XlwMenuWidget mw,
  597.         Window window,
  598.         int x, int y,
  599.         GC gc,
  600. #ifdef NEED_MOTIF
  601.         XmString string
  602. #else
  603.         char *string
  604. #endif
  605. ) {
  606. #ifdef NEED_MOTIF
  607.   XmStringDraw (XtDisplay (mw), window,
  608.         mw->menu.font_list,
  609.         string, gc,
  610.         x, y,
  611.         1000,    /* ???? width */
  612.         XmALIGNMENT_BEGINNING,
  613.         0, /* ???? layout_direction */
  614.         0);
  615. #else
  616.   XDrawString (XtDisplay (mw), window, gc,
  617.            x, y + mw->menu.font_ascent, string, strlen (string));
  618.  
  619. #endif
  620. }
  621.  
  622. static void 
  623. binding_draw (XlwMenuWidget mw, Window w, int x, int y, GC gc, char *value)
  624. {
  625. #ifdef NEED_MOTIF
  626.   XmString xm_value = XmStringCreateLtoR(value, XmSTRING_DEFAULT_CHARSET);
  627.   string_draw (mw, w, x, y, gc, xm_value);
  628.   XmStringFree (xm_value);
  629. #else
  630.   string_draw (mw, w, x, y, gc, value);
  631. #endif
  632. }
  633.  
  634. /*
  635.  *    Low level code for drawing 3-D edges.
  636.  */
  637. static void
  638. shadow_rectangle_draw (
  639.                Display *dpy,
  640.                Window window,
  641.                GC top_gc,
  642.                GC bottom_gc,
  643.                int x, int y, unsigned width, unsigned height,
  644.                unsigned thickness
  645.                )
  646. {
  647.   XPoint points [4];
  648.  
  649.   if (!thickness)
  650.     return;
  651.  
  652.   points [0].x = x;
  653.   points [0].y = y;
  654.   points [1].x = x + width;
  655.   points [1].y = y;
  656.   points [2].x = x + width - thickness;
  657.   points [2].y = y + thickness;
  658.   points [3].x = x;
  659.   points [3].y = y + thickness;
  660.   XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
  661.   points [0].x = x;
  662.   points [0].y = y + thickness;
  663.   points [1].x = x;
  664.   points [1].y = y + height;
  665.   points [2].x = x + thickness;
  666.   points [2].y = y + height - thickness;
  667.   points [3].x = x + thickness;
  668.   points [3].y = y + thickness;
  669.   XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
  670.   points [0].x = x + width;
  671.   points [0].y = y;
  672.   points [1].x = x + width - thickness;
  673.   points [1].y = y + thickness;
  674.   points [2].x = x + width - thickness;
  675.   points [2].y = y + height - thickness;
  676.   points [3].x = x + width;
  677.   points [3].y = y + height - thickness;
  678.   XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
  679.   points [0].x = x;
  680.   points [0].y = y + height;
  681.   points [1].x = x + width;
  682.   points [1].y = y + height;
  683.   points [2].x = x + width;
  684.   points [2].y = y + height - thickness;
  685.   points [3].x = x + thickness;
  686.   points [3].y = y + height - thickness;
  687.   XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
  688. }
  689.  
  690. typedef enum e_shadow_type
  691. {
  692.   /* these are Motif compliant */
  693.   SHADOW_BACKGROUND,
  694.   SHADOW_OUT,
  695.   SHADOW_IN,
  696.   SHADOW_ETCHED_OUT,
  697.   SHADOW_ETCHED_IN,
  698.   SHADOW_ETCHED_OUT_DASH,
  699.   SHADOW_ETCHED_IN_DASH,
  700.   SHADOW_SINGLE_LINE,
  701.   SHADOW_DOUBLE_LINE,
  702.   SHADOW_SINGLE_DASHED_LINE,
  703.   SHADOW_DOUBLE_DASHED_LINE,
  704.   SHADOW_NO_LINE,
  705.   /* these are all non-Motif */
  706.   SHADOW_DOUBLE_ETCHED_OUT,
  707.   SHADOW_DOUBLE_ETCHED_IN,
  708.   SHADOW_DOUBLE_ETCHED_OUT_DASH,
  709.   SHADOW_DOUBLE_ETCHED_IN_DASH
  710. } shadow_type;
  711.  
  712. static void
  713. shadow_draw (XlwMenuWidget mw,
  714.          Window window,
  715.          int x, int y, unsigned width, unsigned height,
  716.          shadow_type type
  717.          )
  718. {
  719.   Display *dpy = XtDisplay (mw);
  720.   GC top_gc;
  721.   GC bottom_gc;
  722.   int thickness = mw->menu.shadow_thickness;
  723. #if 0
  724.   XPoint points [4];
  725. #endif /* 0 */
  726.   Boolean etched = False;
  727.  
  728.   switch (type)
  729.     {
  730.     case SHADOW_BACKGROUND:
  731.       top_gc = bottom_gc = mw->menu.background_gc;
  732.       break;
  733.     case SHADOW_ETCHED_IN:
  734.       top_gc = mw->menu.shadow_bottom_gc;
  735.       bottom_gc = mw->menu.shadow_top_gc;
  736.       etched = True;
  737.       break;
  738.     case SHADOW_ETCHED_OUT:
  739.       top_gc = mw->menu.shadow_top_gc;
  740.       bottom_gc = mw->menu.shadow_bottom_gc;
  741.       etched = True;
  742.       break;
  743.     case SHADOW_IN:
  744.       top_gc = mw->menu.shadow_bottom_gc;
  745.       bottom_gc = mw->menu.shadow_top_gc;
  746.       break;
  747.     case SHADOW_OUT:
  748.     default:
  749.       top_gc = mw->menu.shadow_top_gc;
  750.       bottom_gc = mw->menu.shadow_bottom_gc;
  751.       break;
  752.     }
  753.  
  754.   if (etched)
  755.     {
  756.       unsigned half = thickness/2;
  757.       shadow_rectangle_draw (
  758.                  dpy,
  759.                  window,
  760.                  top_gc,
  761.                  top_gc,
  762.                  x, y,
  763.                  width - half, height - half,
  764.                  thickness - half
  765.                  );
  766.       shadow_rectangle_draw (
  767.                  dpy,
  768.                  window,
  769.                  bottom_gc,
  770.                  bottom_gc,
  771.                  x + half, y + half,
  772.                  width - half , height - half,
  773.                  half
  774.                  );
  775.     }
  776.   else
  777.     {
  778.       shadow_rectangle_draw (
  779.                  dpy,
  780.                  window,
  781.                  top_gc,
  782.                  bottom_gc,
  783.                  x, y,
  784.                  width, height,
  785.                  thickness
  786.                  );
  787.     }
  788. }
  789.  
  790. static void 
  791. arrow_decoration_draw (
  792.                XlwMenuWidget mw,
  793.                Window window,
  794.                int x, int y,
  795.                unsigned width,
  796.                Boolean raised
  797.                )
  798. {
  799.   Display *dpy = XtDisplay (mw);
  800.   GC top_gc;
  801.   GC bottom_gc;
  802.   GC select_gc;
  803.   int thickness = mw->menu.shadow_thickness;
  804.   XPoint points [4];
  805.   int half_width;
  806.   int length = (int)((double)width * 0.87);
  807.   int thick_med = (int)((double)thickness * 1.73);
  808.  
  809.   if (width & 0x1)
  810.     half_width = width/2 + 1;
  811.   else
  812.     half_width = width/2;
  813.   
  814.   select_gc = mw->menu.background_gc;
  815.   
  816.   if (raised)
  817.     {
  818.       top_gc = mw->menu.shadow_bottom_gc;
  819.       bottom_gc = mw->menu.shadow_top_gc;
  820.     }
  821.   else
  822.     {
  823.       top_gc = mw->menu.shadow_top_gc;
  824.       bottom_gc = mw->menu.shadow_bottom_gc;
  825.     }
  826.  
  827.   /*
  828.    *    Fill internal area, we do this first so that the borders
  829.    *    have a nice sharp edge.
  830.    */
  831.   points [0].x = x + thickness;
  832.   points [0].y = y + thickness;
  833.   points [1].x = x + length - thickness;
  834.   points [1].y = y + half_width;
  835.   points [2].x = x + length - thickness;
  836.   points [2].y = y + half_width + thickness;
  837.   points [3].x = x + thickness;
  838.   points [3].y = y + width - thickness;
  839.     
  840.   XFillPolygon (
  841.         dpy,
  842.         window,
  843.         select_gc,
  844.         points,
  845.         4, 
  846.         Convex,
  847.         CoordModeOrigin
  848.         );
  849.  
  850.   /* left border */
  851.   points [0].x = x;
  852.   points [0].y = y;
  853.   points [1].x = x + thickness;
  854.   points [1].y = y + thick_med;
  855.   points [2].x = x + thickness;
  856.   points [2].y = y + width - thick_med;
  857.   points [3].x = x;
  858.   points [3].y = y + width;
  859.   
  860.   XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
  861.  
  862.   /* top border */
  863.   points [0].x = x;
  864.   points [0].y = y + width;
  865.   points [1].x = x + length;
  866.   points [1].y = y + half_width;
  867.   points [2].x = x + length - (thickness + thickness);
  868.   points [2].y = y + half_width;
  869.   points [3].x = x + thickness;
  870.   points [3].y = y + width - thick_med;
  871.   
  872.   XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
  873.  
  874.   /* bottom shadow */
  875.   points [0].x = x;
  876.   points [0].y = y;
  877.   points [1].x = x + length;
  878.   points [1].y = y + half_width;
  879.   points [2].x = x + length - (thickness + thickness);
  880.   points [2].y = y + half_width;
  881.   points [3].x = x + thickness;
  882.   points [3].y = y + thick_med;
  883.   
  884.   XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
  885. }
  886.  
  887. static void
  888. toggle_decoration_draw (
  889.             XlwMenuWidget mw,
  890.             Window window,
  891.             int x, int y,
  892.             unsigned width,
  893.             Boolean set
  894.             )
  895. {
  896.   Display *dpy = XtDisplay (mw);
  897.   int thickness = mw->menu.shadow_thickness;
  898.   shadow_type type;
  899.   GC select_gc = mw->menu.select_gc;
  900.  
  901.   if (set)
  902.     type = SHADOW_IN;
  903.   else
  904.     type = SHADOW_OUT;
  905.  
  906.   /*
  907.    *    Fill internal area.
  908.    */
  909.   if (set) 
  910.     XFillRectangle (
  911.             dpy,
  912.             window,
  913.             select_gc,
  914.             x + thickness,
  915.             y + thickness,
  916.             width - (2*thickness),
  917.             width - (2*thickness)
  918.             );
  919.   
  920.   shadow_draw(mw, window, x, y, width, width, type);
  921. }
  922.  
  923. static void
  924. radio_decoration_draw (
  925.                XlwMenuWidget mw,
  926.                Window window,
  927.                int x, int y,
  928.                unsigned width,
  929.                Boolean enabled
  930.                )
  931. {
  932.   Display *dpy = XtDisplay (mw);
  933.   GC top_gc;
  934.   GC bottom_gc;
  935.   GC select_gc = mw->menu.select_gc;
  936.   int thickness = mw->menu.shadow_thickness;
  937.   XPoint points[6];
  938.   int half_width;
  939. #if 0
  940.   int npoints;
  941. #endif /* 0 */
  942.  
  943.   if (width & 0x1)
  944.     width++;
  945.  
  946.   half_width = width/2;
  947.  
  948.   if (enabled)
  949.     {
  950.       top_gc = mw->menu.shadow_bottom_gc;
  951.       bottom_gc = mw->menu.shadow_top_gc;
  952.     }
  953.   else
  954.     {
  955.       top_gc = mw->menu.shadow_top_gc;
  956.       bottom_gc = mw->menu.shadow_bottom_gc;
  957.     }
  958.  
  959. #if 1
  960.   /*
  961.    *    Draw the bottom first, just incase the regions overlap.
  962.    *    The top should cast the longer shadow.
  963.    */
  964.   points [0].x = x; /* left corner */
  965.   points [0].y = y + half_width;
  966.   points [1].x = x + half_width; /* bottom corner */
  967.   points [1].y = y + width;
  968.   points [2].x = x + half_width; /* bottom inside corner */
  969.   points [2].y = y + width - thickness;
  970.   points [3].x = x + thickness; /* left inside corner */
  971.   points [3].y = y + half_width;
  972.  
  973.   XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
  974.  
  975.   points [0].x = x + half_width; /* bottom corner */
  976.   points [0].y = y + width;
  977.   points [1].x = x + width; /* right corner */
  978.   points [1].y = y + half_width;
  979.   points [2].x = x + width - thickness; /* right inside corner */
  980.   points [2].y = y + half_width;
  981.   points [3].x = x + half_width; /* bottom inside corner */
  982.   points [3].y = y + width - thickness;
  983.  
  984.   XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
  985.  
  986.   points [0].x = x; /* left corner */
  987.   points [0].y = y + half_width;
  988.   points [1].x = x + half_width; /* top corner */
  989.   points [1].y = y;
  990.   points [2].x = x + half_width; /* top inside corner */
  991.   points [2].y = y + thickness;
  992.   points [3].x = x + thickness; /* left inside corner */
  993.   points [3].y = y + half_width;
  994.  
  995.   XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
  996.  
  997.   points [0].x = x + half_width; /* top corner */
  998.   points [0].y = y;
  999.   points [1].x = x + width; /* right corner */
  1000.   points [1].y = y + half_width;
  1001.   points [2].x = x + width - thickness; /* right inside corner */
  1002.   points [2].y = y + half_width;
  1003.   points [3].x = x + half_width; /* top inside corner */
  1004.   points [3].y = y + thickness;
  1005.  
  1006.   XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
  1007. #else
  1008.   /*
  1009.    *    Draw the bottom first, just incase the regions overlap.
  1010.    *    The top should cast the longer shadow.
  1011.    */
  1012.   npoints = 0;
  1013.   points [npoints].x = x; /* left corner */
  1014.   points [npoints++].y = y + half_width;
  1015.   points [npoints].x = x + half_width; /* bottom corner */
  1016.   points [npoints++].y = y + width;
  1017.   points [npoints].x = x + width; /* right corner */
  1018.   points [npoints++].y = y + half_width;
  1019.   points [npoints].x = x + width - thickness; /* right inside corner */
  1020.   points [npoints++].y = y + half_width;
  1021.   points [npoints].x = x + half_width; /* bottom inside corner */
  1022.   points [npoints++].y = y + width - thickness;
  1023.   points [npoints].x = x + thickness; /* left inside corner */
  1024.   points [npoints++].y = y + half_width;
  1025.  
  1026.   XFillPolygon (dpy, window, bottom_gc, 
  1027.         points, npoints, Nonconvex, CoordModeOrigin);
  1028.  
  1029.   npoints = 0;
  1030.  
  1031.   points [npoints].x = x; /* left corner */
  1032.   points [npoints++].y = y + half_width;
  1033.   points [npoints].x = x + half_width; /* top corner */
  1034.   points [npoints++].y = y;
  1035.   points [npoints].x = x + width; /* right corner */
  1036.   points [npoints++].y = y + half_width;
  1037.   points [npoints].x = x + width - thickness; /* right inside corner */
  1038.   points [npoints++].y = y + half_width;
  1039.   points [npoints].x = x + half_width; /* top inside corner */
  1040.   points [npoints++].y = y + thickness;
  1041.   points [npoints].x = x + thickness; /* left inside corner */
  1042.   points [npoints++].y = y + half_width;
  1043.  
  1044.   XFillPolygon (dpy, window, top_gc, points, npoints, Nonconvex,
  1045.         CoordModeOrigin);
  1046. #endif
  1047.  
  1048.  
  1049.   /*
  1050.    *    Fill internal area.
  1051.    */
  1052.   if (enabled)
  1053.     {
  1054.       points [0].x = x + thickness;
  1055.       points [0].y = y + half_width;
  1056.       points [1].x = x + half_width;
  1057.       points [1].y = y + thickness;
  1058.       points [2].x = x + width - thickness;
  1059.       points [2].y = y + half_width;
  1060.       points [3].x = x + half_width;
  1061.       points [3].y = y + width - thickness;
  1062.       XFillPolygon (dpy,
  1063.             window,
  1064.             select_gc,
  1065.             points,
  1066.             4, 
  1067.             Convex,
  1068.             CoordModeOrigin
  1069.             );
  1070.     }
  1071. }
  1072.  
  1073. static void
  1074. separator_decoration_draw (
  1075.                XlwMenuWidget mw,
  1076.                Window window,
  1077.                int x, int y,
  1078.                unsigned width,
  1079.                Boolean vertical,
  1080.                shadow_type type
  1081.                )
  1082. {
  1083.   Display *dpy = XtDisplay (mw);
  1084.   GC top_gc;
  1085.   GC bottom_gc;
  1086.   unsigned offset = 0;
  1087.   unsigned num_separators = 1;
  1088.   unsigned top_line_thickness = 0;
  1089.   unsigned bottom_line_thickness = 0;
  1090.   Boolean dashed = False;
  1091.   int i;
  1092.  
  1093.   switch (type)
  1094.     {
  1095.     case SHADOW_NO_LINE: /* nothing to do */
  1096.       return; 
  1097.     case SHADOW_DOUBLE_LINE:
  1098.       num_separators = 2;
  1099.     case SHADOW_SINGLE_LINE:
  1100.       top_gc = bottom_gc = mw->menu.foreground_gc;
  1101.       top_line_thickness = 1;
  1102.       break;
  1103.     case SHADOW_DOUBLE_DASHED_LINE:
  1104.       num_separators = 2;
  1105.     case SHADOW_SINGLE_DASHED_LINE:
  1106.       top_gc = bottom_gc = mw->menu.foreground_gc;
  1107.       top_line_thickness = 1;
  1108.       dashed = True;
  1109.       break;
  1110.     case SHADOW_DOUBLE_ETCHED_OUT_DASH:
  1111.       num_separators = 2;
  1112.     case SHADOW_ETCHED_OUT_DASH:
  1113.       top_gc = mw->menu.shadow_top_gc;
  1114.       bottom_gc = mw->menu.shadow_bottom_gc;
  1115.       top_line_thickness = mw->menu.shadow_thickness/2;
  1116.       bottom_line_thickness = mw->menu.shadow_thickness - top_line_thickness;
  1117.       dashed = True;
  1118.       break;
  1119.     case SHADOW_DOUBLE_ETCHED_IN_DASH:
  1120.       num_separators = 2;
  1121.     case SHADOW_ETCHED_IN_DASH:
  1122.       top_gc = mw->menu.shadow_bottom_gc;
  1123.       bottom_gc = mw->menu.shadow_top_gc;
  1124.       top_line_thickness = mw->menu.shadow_thickness/2;
  1125.       bottom_line_thickness = mw->menu.shadow_thickness - top_line_thickness;
  1126.       dashed = True;
  1127.       break;
  1128.     case SHADOW_DOUBLE_ETCHED_OUT:
  1129.       num_separators = 2;
  1130.     case SHADOW_ETCHED_OUT:
  1131.       top_gc = mw->menu.shadow_top_gc;
  1132.       bottom_gc = mw->menu.shadow_bottom_gc;
  1133.       top_line_thickness = mw->menu.shadow_thickness/2;
  1134.       bottom_line_thickness = mw->menu.shadow_thickness - top_line_thickness;
  1135.       break;
  1136.     case SHADOW_DOUBLE_ETCHED_IN:
  1137.       num_separators = 2;
  1138.     case SHADOW_ETCHED_IN:
  1139.     default:
  1140.       top_gc = mw->menu.shadow_bottom_gc;
  1141.       bottom_gc = mw->menu.shadow_top_gc;
  1142.       top_line_thickness = mw->menu.shadow_thickness/2;
  1143.       bottom_line_thickness = mw->menu.shadow_thickness - top_line_thickness;
  1144.       break;
  1145.     }
  1146.  
  1147.   if (dashed)
  1148.     {
  1149.       XGCValues values;
  1150.       values.line_style = LineOnOffDash;
  1151.       if (top_line_thickness > 0)
  1152.     XChangeGC (dpy, top_gc, GCLineStyle, &values);
  1153.       if (bottom_line_thickness > 0 && bottom_gc != top_gc)
  1154.     XChangeGC (dpy, bottom_gc, GCLineStyle, &values);
  1155.     }
  1156.   
  1157.   while (num_separators--)
  1158.     {
  1159.       for (i = 0; i < top_line_thickness; i++)
  1160.     XDrawLine (dpy, window, top_gc, x, y + i, x + width, y + i);
  1161.       
  1162.       for (i = 0; i < bottom_line_thickness; i++)
  1163.     XDrawLine (
  1164.            dpy, window, bottom_gc, 
  1165.            x, y + top_line_thickness + offset + i,
  1166.            x + width, y + top_line_thickness + offset + i
  1167.            );
  1168.       y += (top_line_thickness + offset + bottom_line_thickness + 1);
  1169.     }
  1170.  
  1171.   if (dashed)
  1172.     {
  1173.       XGCValues values;
  1174.       values.line_style = LineSolid;
  1175.       if (top_line_thickness > 0)
  1176.     XChangeGC (dpy, top_gc, GCLineStyle, &values);
  1177.       if (bottom_line_thickness > 0 && bottom_gc != top_gc)
  1178.     XChangeGC (dpy, bottom_gc, GCLineStyle, &values);
  1179.     }
  1180. }
  1181.  
  1182. #define SLOPPY_TYPES 0        /* 0=off, 1=error check, 2=easy to please */
  1183. #if SLOPPY_TYPES
  1184. #if SLOPPY_TYPES < 2
  1185.  
  1186. static char *wv_types[] =
  1187. {
  1188.   "UNSPECIFIED",
  1189.   "BUTTON",
  1190.   "TOGGLE",
  1191.   "RADIO",
  1192.   "TEXT",
  1193.   "SEPARATOR",
  1194.   "CASCADE",
  1195.   "PUSHRIGHT",
  1196.   "INCREMENTAL"
  1197. };
  1198.  
  1199. static void
  1200. print_widget_value (widget_value *wv, int just_one, int depth)
  1201. {
  1202.   char d [200];
  1203.   int i;
  1204.   for (i = 0; i < depth; i++) d[i] = ' ';
  1205.   d[depth]=0;
  1206.   if (!wv)
  1207.     {
  1208.       printf ("%s(null widget value pointer)\n", d);
  1209.       return;
  1210.     }
  1211.   printf ("%stype:    %s\n", d, wv_types [wv->type]);
  1212. #if 0
  1213.   printf ("%sname:    %s\n", d, (wv->name ? wv->name : "(null)"));
  1214. #else
  1215.   if (wv->name)  printf ("%sname:    %s\n", d, wv->name);
  1216. #endif
  1217.   if (wv->value) printf ("%svalue:   %s\n", d, wv->value);
  1218.   if (wv->key)   printf ("%skey:     %s\n", d, wv->key);
  1219.   printf ("%senabled: %d\n", d, wv->enabled);
  1220.   if (wv->contents)
  1221.     {
  1222.       printf ("\n%scontents: \n", d);
  1223.       print_widget_value (wv->contents, 0, depth + 5);
  1224.     }
  1225.   if (!just_one && wv->next)
  1226.     {
  1227.       printf ("\n");
  1228.       print_widget_value (wv->next, 0, depth);
  1229.     }
  1230. }
  1231. #endif
  1232.  
  1233. static Boolean
  1234. all_dashes_p (char *s)
  1235. {
  1236.   char *p;
  1237.   if (!s || s[0] == '\0')
  1238.     return False;
  1239.   for (p = s; *p == '-'; p++);
  1240.  
  1241.   if (*p == '!' || *p == '\0')
  1242.     return True;
  1243.   return False;
  1244. }
  1245. #endif
  1246.  
  1247. static widget_value_type
  1248. menu_item_type (widget_value *val)
  1249. {
  1250.   if (val->type != UNSPECIFIED_TYPE)
  1251.     return val->type;
  1252.   else
  1253.     {
  1254. #if SLOPPY_TYPES
  1255.       if (all_dashes_p(val->name))
  1256.     return SEPARATOR_TYPE;
  1257.       else if (val->name && val->name[0] == '\0') /* push right */
  1258.     return PUSHRIGHT_TYPE;
  1259.       else if (val->contents) /* cascade */
  1260.     return CASCADE_TYPE;
  1261.       else if (val->call_data) /* push button */
  1262.     return BUTTON_TYPE;
  1263.       else
  1264.     return TEXT_TYPE;
  1265. #else 
  1266.     abort();
  1267. #endif 
  1268.     }
  1269. }
  1270.  
  1271. static void
  1272. label_button_size (
  1273.            XlwMenuWidget mw,
  1274.            widget_value *val,
  1275.            Boolean in_menubar,
  1276.            unsigned *toggle_width,
  1277.            unsigned *label_width,
  1278.            unsigned *bindings_width,
  1279.            unsigned *height
  1280.            )
  1281. {
  1282.   *height = (mw->menu.font_ascent + mw->menu.font_descent +
  1283.          2 * mw->menu.vertical_margin +
  1284.          2 * mw->menu.shadow_thickness);
  1285.   /* no left column decoration */
  1286.   *toggle_width = mw->menu.horizontal_margin + mw->menu.shadow_thickness;;
  1287.   
  1288.   *label_width  = string_width (mw, resource_widget_value (mw, val));
  1289.   *bindings_width =  mw->menu.horizontal_margin + mw->menu.shadow_thickness;
  1290. }
  1291.  
  1292. static void
  1293. label_button_draw (
  1294.            XlwMenuWidget mw, 
  1295.            widget_value *val,
  1296.            Boolean       in_menubar,
  1297.            Boolean       highlighted,
  1298.            Window        window, 
  1299.            int x, int y, 
  1300.            unsigned width,
  1301.            unsigned height,
  1302.            unsigned label_offset,
  1303.            unsigned binding_tab
  1304.            )
  1305. {
  1306.   int y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin;
  1307.   
  1308.   if (!label_offset)
  1309.     label_offset = mw->menu.shadow_thickness + mw->menu.horizontal_margin;
  1310.  
  1311.   /*
  1312.    *    Draw the label string.
  1313.    */
  1314.   string_draw (
  1315.            mw,
  1316.            window,
  1317.            x + label_offset, y + y_offset, 
  1318.            mw->menu.foreground_gc,
  1319.            resource_widget_value (mw, val)
  1320.            );
  1321. }
  1322.  
  1323. static void
  1324. push_button_size (
  1325.           XlwMenuWidget mw,
  1326.           widget_value *val,
  1327.           Boolean in_menubar,
  1328.           unsigned *toggle_width,
  1329.           unsigned *label_width,
  1330.           unsigned *bindings_width,
  1331.           unsigned *height
  1332.           )
  1333. {
  1334.   /* inherit */
  1335.   label_button_size (
  1336.              mw, val, in_menubar,
  1337.              toggle_width, label_width, bindings_width,
  1338.              height
  1339.              );
  1340.   
  1341.   /* key bindings to display? */
  1342.   if (!in_menubar && val->key)
  1343.     {
  1344.       int w;
  1345. #ifdef NEED_MOTIF
  1346.       XmString key = XmStringCreateLtoR (val->key, XmSTRING_DEFAULT_CHARSET);
  1347.       w = string_width(mw, key);
  1348.       XmStringFree (key);
  1349. #else
  1350.       char *key = val->key;
  1351.       w = string_width (mw, key);
  1352. #endif
  1353.       *bindings_width += w + mw->menu.column_spacing;
  1354.     }
  1355. }
  1356.  
  1357. static void
  1358. push_button_draw (
  1359.           XlwMenuWidget mw, 
  1360.           widget_value *val,
  1361.           Boolean       in_menubar,
  1362.           Boolean       highlighted,
  1363.           Window        window, 
  1364.           int x, int y, 
  1365.           unsigned width, unsigned height,
  1366.           unsigned      label_offset,
  1367.           unsigned      binding_offset
  1368.           )
  1369. {
  1370.   int y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin;
  1371.   GC gc;
  1372.   shadow_type type;
  1373.   Boolean menu_pb = in_menubar && (menu_item_type (val) == BUTTON_TYPE);
  1374.   
  1375.   /*
  1376.    *    Draw the label string.
  1377.    */
  1378.   if (!label_offset)
  1379.     label_offset = mw->menu.shadow_thickness + mw->menu.horizontal_margin;
  1380.   
  1381.   if (menu_pb)
  1382.     {
  1383.       if (val->enabled)
  1384.     gc = mw->menu.button_gc;
  1385.       else
  1386.     gc = mw->menu.inactive_button_gc;
  1387.     }
  1388.   else
  1389.     {
  1390.       if (val->enabled)
  1391.     gc = mw->menu.foreground_gc;
  1392.       else
  1393.     gc = mw->menu.inactive_gc;
  1394.     }
  1395.  
  1396.   string_draw (
  1397.            mw,
  1398.            window,
  1399.            x + label_offset, y + y_offset, 
  1400.            gc,
  1401.            resource_widget_value(mw, val)
  1402.            );
  1403.   
  1404.   /*
  1405.    *    Draw the keybindings
  1406.    */
  1407.   if (val->key)
  1408.     {
  1409.       if (!binding_offset)
  1410.     {
  1411.       unsigned s_width = string_width (mw, resource_widget_value(mw, val));
  1412.       binding_offset = label_offset + s_width +  mw->menu.shadow_thickness;
  1413.     }
  1414.       binding_draw (mw, window,
  1415.             x + binding_offset + mw->menu.column_spacing,
  1416.             y + y_offset, gc, val->key);
  1417.     }
  1418.   
  1419.   /*
  1420.    *    Draw the shadow
  1421.    */
  1422.   if (menu_pb)
  1423.     {
  1424.       if (highlighted)
  1425.     type = SHADOW_OUT;
  1426.       else 
  1427.     type = (val->selected ? SHADOW_ETCHED_OUT : SHADOW_ETCHED_IN);
  1428.     }
  1429.   else
  1430.     {
  1431.       if (highlighted)
  1432.     type = SHADOW_OUT;
  1433.       else 
  1434.     type = SHADOW_BACKGROUND;
  1435.     }
  1436.  
  1437.   shadow_draw (mw, window, x, y, width, height, type);
  1438. }
  1439.  
  1440. static unsigned int
  1441. arrow_decoration_height (XlwMenuWidget mw)
  1442. {
  1443.   unsigned int result =
  1444.     (mw->menu.font_ascent + mw->menu.font_descent) / (unsigned int)2;
  1445.   
  1446.   result += 2 * mw->menu.shadow_thickness;
  1447.  
  1448.   if (result > (mw->menu.font_ascent + mw->menu.font_descent))
  1449.     result = mw->menu.font_ascent + mw->menu.font_descent;
  1450.  
  1451.   return result;
  1452. }
  1453.  
  1454. static void
  1455. cascade_button_size (
  1456.              XlwMenuWidget mw,
  1457.              widget_value *val,
  1458.              Boolean in_menubar,
  1459.              unsigned *toggle_width,
  1460.              unsigned *label_width,
  1461.              unsigned *arrow_width,
  1462.              unsigned *height
  1463.              )
  1464. {
  1465.   /* inherit */
  1466.   label_button_size (
  1467.              mw, val, in_menubar,
  1468.              toggle_width, label_width, arrow_width,
  1469.              height
  1470.              );
  1471.   /* we have a pull aside arrow */
  1472.   if (!in_menubar)
  1473.     {
  1474.       *arrow_width += arrow_decoration_height(mw) + mw->menu.column_spacing;
  1475.     }
  1476. }
  1477.  
  1478. static void
  1479. cascade_button_draw (
  1480.              XlwMenuWidget mw, 
  1481.              widget_value *val,
  1482.              Boolean       in_menubar,
  1483.              Boolean       highlighted,
  1484.              Window        window, 
  1485.              int x, int y, 
  1486.              unsigned width, unsigned height,
  1487.              unsigned      label_offset,
  1488.              unsigned      binding_offset
  1489.              )
  1490. {
  1491.   shadow_type type;
  1492.  
  1493.   /*
  1494.    *    Draw the label string.
  1495.    */
  1496.   label_button_draw (mw, val, in_menubar, highlighted,
  1497.              window, x, y, width, height, label_offset,
  1498.              binding_offset);
  1499.  
  1500.   /*
  1501.    *    Draw the pull aside arrow
  1502.    */
  1503.   if (!in_menubar && val->contents)
  1504.     {
  1505.       int y_offset;
  1506.       unsigned arrow_height = arrow_decoration_height (mw);
  1507.       
  1508.       y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin +
  1509.     (mw->menu.font_ascent+mw->menu.font_descent - arrow_height)/2;
  1510.       
  1511.       if (!binding_offset)
  1512.     {
  1513.       unsigned s_width = string_width(mw, resource_widget_value (mw, val));
  1514.       
  1515.       if (!label_offset)
  1516.         label_offset = mw->menu.shadow_thickness +
  1517.           mw->menu.horizontal_margin;
  1518.       
  1519.       binding_offset = label_offset + s_width +  mw->menu.shadow_thickness;
  1520.     }
  1521.       
  1522.       arrow_decoration_draw (
  1523.                  mw,
  1524.                  window,
  1525.                  x + binding_offset + mw->menu.column_spacing,
  1526.                  y + y_offset,
  1527.                  arrow_height,
  1528.                  highlighted
  1529.                  );
  1530.     }
  1531.   
  1532.   /*
  1533.    *    Draw the shadow
  1534.    */
  1535.   if (highlighted)
  1536.     type = SHADOW_OUT;
  1537.   else
  1538.     type = SHADOW_BACKGROUND;
  1539.   
  1540.   shadow_draw(mw, window, x, y, width, height, type);
  1541. }
  1542.  
  1543. static unsigned
  1544. toggle_decoration_height(XlwMenuWidget mw)
  1545. {
  1546.   unsigned rv;
  1547.   if (mw->menu.indicator_size > 0)
  1548.     rv = mw->menu.indicator_size;
  1549.   else
  1550.     rv = mw->menu.font_ascent;
  1551.   
  1552.   if (rv > (mw->menu.font_ascent+mw->menu.font_descent))
  1553.     rv = mw->menu.font_ascent+mw->menu.font_descent;
  1554.   
  1555.   return rv;
  1556. }
  1557.  
  1558. static void
  1559. toggle_button_size (
  1560.             XlwMenuWidget mw,
  1561.             widget_value *val,
  1562.             Boolean in_menubar,
  1563.             unsigned *toggle_width,
  1564.             unsigned *label_width,
  1565.             unsigned *bindings_width,
  1566.             unsigned *height
  1567.             )
  1568. {
  1569.   /* inherit */
  1570.   push_button_size (
  1571.             mw, val, in_menubar,
  1572.             toggle_width, label_width, bindings_width,
  1573.             height
  1574.             );
  1575.   /* we have a toggle */
  1576.   *toggle_width += toggle_decoration_height(mw) + mw->menu.column_spacing;
  1577. }
  1578.  
  1579. static void
  1580. toggle_button_draw (
  1581.             XlwMenuWidget mw, 
  1582.             widget_value *val,
  1583.             Boolean       in_menubar,
  1584.             Boolean highlighted,
  1585.             Window        window, 
  1586.             int x, int y, 
  1587.             unsigned width, unsigned height,
  1588.             unsigned      label_tab,
  1589.             unsigned      binding_tab
  1590.             )
  1591. {
  1592.   int x_offset;
  1593.   int y_offset;
  1594.   unsigned t_height = toggle_decoration_height(mw);
  1595.   
  1596.   /*
  1597.    *    Draw a toggle.
  1598.    */
  1599.   x_offset = mw->menu.shadow_thickness + mw->menu.horizontal_margin;
  1600.   y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin;
  1601.   y_offset += (mw->menu.font_ascent + mw->menu.font_descent - t_height)/2;
  1602.   
  1603.   toggle_decoration_draw (mw, window, x + x_offset, y + y_offset,
  1604.               t_height, val->selected);
  1605.  
  1606.   /*
  1607.    *    Draw the pushbutton parts.
  1608.    */
  1609.   push_button_draw (mw, val, in_menubar, highlighted, window, x, y, width,
  1610.             height, label_tab, binding_tab);
  1611. }
  1612.  
  1613. static unsigned
  1614. radio_decoration_height(XlwMenuWidget mw)
  1615. {
  1616.   return toggle_decoration_height(mw);
  1617. }
  1618.  
  1619. static void
  1620. radio_button_draw (
  1621.            XlwMenuWidget mw, 
  1622.            widget_value *val,
  1623.            Boolean       in_menubar,
  1624.            Boolean       highlighted,
  1625.            Window        window, 
  1626.            int x, int y, 
  1627.            unsigned width, unsigned height,
  1628.            unsigned      label_tab,
  1629.            unsigned      binding_tab
  1630.            )
  1631. {
  1632.   int x_offset;
  1633.   int y_offset;
  1634.   unsigned r_height = radio_decoration_height(mw);
  1635.   
  1636.   /*
  1637.    *    Draw a toggle.
  1638.    */
  1639.   x_offset = mw->menu.shadow_thickness + mw->menu.horizontal_margin;
  1640.   y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin;
  1641.   y_offset += (mw->menu.font_ascent + mw->menu.font_descent - r_height)/2;
  1642.  
  1643.   radio_decoration_draw (mw, window, x + x_offset, y + y_offset, r_height,
  1644.              val->selected);
  1645.  
  1646.   /*
  1647.    *    Draw the pushbutton parts.
  1648.    */
  1649.   push_button_draw (mw, val, in_menubar, highlighted, window, x, y, width,
  1650.             height, label_tab, binding_tab);
  1651. }
  1652.  
  1653. static struct _shadow_names
  1654. {
  1655.   char *      name;
  1656.   shadow_type type;
  1657. } shadow_names[] =
  1658. {
  1659.   /* Motif */
  1660.   { "singleLine", SHADOW_SINGLE_LINE },
  1661.   { "doubleLine", SHADOW_DOUBLE_LINE },
  1662.   { "singleDashedLine", SHADOW_SINGLE_DASHED_LINE },
  1663.   { "doubleDashedLine", SHADOW_DOUBLE_DASHED_LINE },
  1664.   { "noLine", SHADOW_NO_LINE },
  1665.   { "shadowEtchedIn", SHADOW_ETCHED_IN },
  1666.   { "shadowEtchedOut", SHADOW_ETCHED_OUT },
  1667.   { "shadowEtchedInDash", SHADOW_ETCHED_IN_DASH },
  1668.   { "shadowEtchedOutDash", SHADOW_ETCHED_OUT_DASH },
  1669.   /* non-Motif */
  1670.   { "shadowDoubleEtchedIn", SHADOW_DOUBLE_ETCHED_IN },
  1671.   { "shadowDoubleEtchedOut", SHADOW_DOUBLE_ETCHED_OUT },
  1672.   { "shadowDoubleEtchedInDash", SHADOW_DOUBLE_ETCHED_IN_DASH },
  1673.   { "shadowDoubleEtchedOutDash", SHADOW_DOUBLE_ETCHED_OUT_DASH }
  1674. };
  1675.  
  1676. static shadow_type
  1677. separator_type (char *name)
  1678. {
  1679.   int i;
  1680.  
  1681.   if (name)
  1682.     {
  1683.       for (i = 0; i < XtNumber(shadow_names); i++ )
  1684.     {
  1685.       if (strcmp (name, shadow_names[i].name) == 0)
  1686.         return shadow_names[i].type;
  1687.     }
  1688.     }
  1689.   return SHADOW_BACKGROUND;
  1690. }
  1691.  
  1692. static unsigned
  1693. separator_decoration_height (XlwMenuWidget mw, widget_value *val)
  1694. {
  1695.  
  1696.   switch (separator_type(val->value))
  1697.     {
  1698.     case SHADOW_NO_LINE:
  1699.     case SHADOW_SINGLE_LINE:
  1700.     case SHADOW_SINGLE_DASHED_LINE:
  1701.       return 1;
  1702.     case SHADOW_DOUBLE_LINE:
  1703.     case SHADOW_DOUBLE_DASHED_LINE:
  1704.       return 3;
  1705.     case SHADOW_DOUBLE_ETCHED_OUT:
  1706.     case SHADOW_DOUBLE_ETCHED_IN:
  1707.     case SHADOW_DOUBLE_ETCHED_OUT_DASH:
  1708.     case SHADOW_DOUBLE_ETCHED_IN_DASH:
  1709.       return (1 + 2 * mw->menu.shadow_thickness);
  1710.     case SHADOW_ETCHED_OUT:
  1711.     case SHADOW_ETCHED_IN:
  1712.     default:
  1713.       return mw->menu.shadow_thickness;
  1714.     }
  1715. }
  1716.  
  1717. static void
  1718. separator_size (XlwMenuWidget mw,
  1719.         widget_value *val,
  1720.         Boolean in_menubar,
  1721.         unsigned *toggle_width,
  1722.         unsigned *label_width,
  1723.         unsigned *rest_width,
  1724.         unsigned *height
  1725.         )
  1726. {
  1727.   *height = separator_decoration_height (mw, val);
  1728.   *label_width = 1;
  1729.   *toggle_width = *rest_width = 0;
  1730. }
  1731.  
  1732. static void
  1733. separator_draw (XlwMenuWidget mw, 
  1734.         widget_value *val,
  1735.         Boolean       in_menubar,
  1736.         Boolean       highlighted,
  1737.         Window        window, 
  1738.         int x, int y, 
  1739.         unsigned width, unsigned height,
  1740.         unsigned      label_tab,
  1741.         unsigned      binding_tab
  1742.         )
  1743. {
  1744.   unsigned sep_width;
  1745.  
  1746.   if (in_menubar)
  1747.     sep_width = height;
  1748.   else
  1749.     sep_width = width;
  1750.  
  1751.   separator_decoration_draw (mw,
  1752.                  window,
  1753.                  x,
  1754.                  y,
  1755.                  sep_width,
  1756.                  in_menubar,
  1757.                  separator_type(val->value)
  1758.                  );
  1759. }
  1760.  
  1761. static void
  1762. pushright_size (XlwMenuWidget mw,
  1763.         widget_value *val,
  1764.         Boolean in_menubar,
  1765.         unsigned *toggle_width,
  1766.         unsigned *label_width,
  1767.         unsigned *rest_width,
  1768.         unsigned *height
  1769. )
  1770. {
  1771.   *height = *label_width = *toggle_width = *rest_width = 0;
  1772. }
  1773.  
  1774. static void
  1775. size_menu_item (XlwMenuWidget mw,
  1776.         widget_value *val,
  1777.         int horizontal,
  1778.         unsigned *toggle_width,
  1779.         unsigned *label_width,
  1780.         unsigned *rest_width,
  1781.         unsigned *height
  1782. )
  1783. {
  1784.  
  1785.   void (*function_ptr) (
  1786.             XlwMenuWidget mw,
  1787.             widget_value *val,
  1788.             Boolean in_menubar,
  1789.             unsigned *toggle_width,
  1790.             unsigned *label_width,
  1791.             unsigned *rest_width,
  1792.             unsigned *height
  1793.             );
  1794.   switch (menu_item_type (val))
  1795.     {
  1796.     case TOGGLE_TYPE:
  1797.     case RADIO_TYPE:
  1798.       function_ptr = toggle_button_size;
  1799.       break;
  1800.     case SEPARATOR_TYPE:
  1801.       function_ptr = separator_size;
  1802.       break;
  1803.     case INCREMENTAL_TYPE:
  1804.     case CASCADE_TYPE:
  1805.       function_ptr = cascade_button_size;
  1806.       break;
  1807.     case BUTTON_TYPE:
  1808.       function_ptr = push_button_size;
  1809.       break;
  1810.     case PUSHRIGHT_TYPE:
  1811.       function_ptr = pushright_size;
  1812.       break;
  1813.     case TEXT_TYPE:
  1814.     default:
  1815.       function_ptr = label_button_size;
  1816.       break;
  1817.     }
  1818.  
  1819.   (*function_ptr) (
  1820.            mw,
  1821.            val,
  1822.            horizontal,
  1823.            toggle_width,
  1824.            label_width,
  1825.            rest_width,
  1826.            height
  1827.            );
  1828. }
  1829.  
  1830. static void
  1831. display_menu_item (
  1832.            XlwMenuWidget mw,
  1833.            widget_value *val,
  1834.            window_state *ws,
  1835.            XPoint *where,
  1836.            Boolean highlighted,
  1837.            Boolean horizontal,
  1838.            Boolean just_compute
  1839.            )
  1840. {
  1841.   
  1842.   int x = where->x /* + mw->menu.shadow_thickness */ ;
  1843.   int y = where->y /* + mw->menu.shadow_thickness */ ;
  1844.   unsigned toggle_width;
  1845.   unsigned label_width;
  1846.   unsigned binding_width;
  1847.   unsigned width;
  1848.   unsigned height;
  1849.   unsigned label_tab;
  1850.   unsigned binding_tab;
  1851.   void (*function_ptr) (
  1852.             XlwMenuWidget mw,
  1853.             widget_value *val,
  1854.             Boolean in_menubar,
  1855.             Boolean highlighted,
  1856.             Window        window, 
  1857.             int x, int y, 
  1858.             unsigned width, unsigned height,
  1859.             unsigned      label_tab,
  1860.             unsigned      binding_tab
  1861.             );
  1862.  
  1863.   size_menu_item (
  1864.           mw, val, horizontal,
  1865.           &toggle_width, &label_width, &binding_width, &height
  1866.           );
  1867.  
  1868.   if (horizontal)
  1869.     {
  1870.       width = toggle_width + label_width + binding_width;
  1871.       height = ws->height - 2 * mw->menu.shadow_thickness;
  1872.     }
  1873.   else
  1874.     {
  1875.       width = ws->width - 2 * mw->menu.shadow_thickness;
  1876.       toggle_width = ws->toggle_width;
  1877.       label_width = ws->label_width;
  1878.     }
  1879.   
  1880.   where->x += width;
  1881.   where->y += height;
  1882.   
  1883.   if (just_compute)
  1884.     return;
  1885.   
  1886.   label_tab = toggle_width;
  1887.   binding_tab = toggle_width + label_width;
  1888.  
  1889.   switch (menu_item_type (val))
  1890.     {
  1891.     case TOGGLE_TYPE:
  1892.       function_ptr = toggle_button_draw;
  1893.       break;
  1894.     case RADIO_TYPE:
  1895.       function_ptr = radio_button_draw;
  1896.       break;
  1897.     case SEPARATOR_TYPE:
  1898.       function_ptr = separator_draw;
  1899.       break;
  1900.     case INCREMENTAL_TYPE:
  1901.     case CASCADE_TYPE:
  1902.       function_ptr = cascade_button_draw;
  1903.       break;
  1904.     case BUTTON_TYPE:
  1905.       function_ptr = push_button_draw;
  1906.       break;
  1907.     case TEXT_TYPE:
  1908.       function_ptr = label_button_draw;
  1909.       break;
  1910.     default: /* do no drawing */
  1911.       return;
  1912.     }
  1913.  
  1914.   (*function_ptr) (
  1915.            mw,
  1916.            val,
  1917.            horizontal,
  1918.            highlighted,
  1919.            ws->window, 
  1920.            x, y, 
  1921.            width, height,
  1922.            label_tab,
  1923.            binding_tab
  1924.            );
  1925. }
  1926.  
  1927. static void
  1928. size_menu (XlwMenuWidget mw, int level)
  1929. {
  1930.   unsigned      toggle_width;
  1931.   unsigned    label_width;
  1932.   unsigned    rest_width;
  1933.   unsigned    height;
  1934.   unsigned    max_toggle_width = 0;
  1935.   unsigned    max_label_width = 0;
  1936.   unsigned    max_rest_width = 0;
  1937.   unsigned    max_height = 0;
  1938.   int        horizontal_p = mw->menu.horizontal && (level == 0);
  1939.   widget_value*    val;
  1940.   window_state*    ws;
  1941.  
  1942.   if (level >= mw->menu.old_depth)
  1943.     abort ();
  1944.  
  1945.   ws = &mw->menu.windows [level];  
  1946.  
  1947.   for (val = mw->menu.old_stack [level]->contents; val; val = val->next)
  1948.     {
  1949.       size_menu_item (
  1950.               mw,
  1951.               val,
  1952.               horizontal_p,
  1953.               &toggle_width,
  1954.               &label_width,
  1955.               &rest_width,
  1956.               &height
  1957.               );
  1958.       if (horizontal_p)
  1959.     {
  1960.       max_label_width += toggle_width + label_width + rest_width;
  1961.       if (height > max_height)
  1962.         max_height = height;
  1963.     }
  1964.       else
  1965.     {
  1966.       if (toggle_width > max_toggle_width)
  1967.         max_toggle_width = toggle_width;
  1968.       if (label_width > max_label_width)
  1969.         max_label_width = label_width;
  1970.       if (rest_width > max_rest_width)
  1971.         max_rest_width = rest_width;
  1972.       max_height += height;
  1973.     }
  1974.     }
  1975.   
  1976.   ws->height = max_height;
  1977.   ws->width = max_label_width + max_rest_width + max_toggle_width;
  1978.   ws->toggle_width = max_toggle_width;
  1979.   ws->label_width = max_label_width;
  1980.  
  1981.   ws->width += 2 * mw->menu.shadow_thickness;
  1982.   ws->height += 2 * mw->menu.shadow_thickness;
  1983. }
  1984.  
  1985. static void
  1986. display_menu (XlwMenuWidget mw, int level, Boolean just_compute_p,
  1987.           XPoint *highlighted_pos, XPoint *hit, widget_value **hit_return,
  1988.           widget_value *this, widget_value *that)
  1989. {
  1990.   widget_value *val;
  1991.   widget_value *following_item;
  1992.   window_state *ws;
  1993.   XPoint    where;
  1994.   int horizontal_p = mw->menu.horizontal && (level == 0);
  1995.   int highlighted_p;
  1996.   int just_compute_this_one_p;
  1997.  
  1998.   if (level >= mw->menu.old_depth)
  1999.     abort ();
  2000.  
  2001.   if (level < mw->menu.old_depth - 1)
  2002.     following_item = mw->menu.old_stack [level + 1];
  2003.   else 
  2004.     following_item = NULL;
  2005.  
  2006. #if SLOPPY_TYPES == 1
  2007.   puts("===================================================================");
  2008.   print_widget_value (following_item, 1, 0);
  2009. #endif 
  2010.   if (following_item
  2011.       && following_item->type == CASCADE_TYPE
  2012.       && following_item->contents
  2013.       && following_item->contents->type == INCREMENTAL_TYPE)
  2014.     {
  2015.       /* okay, we're now doing a lisp callback to incrementally generate
  2016.      more of the menu. */
  2017.       XtCallCallbackList ((Widget)mw,
  2018.               mw->menu.open,
  2019.               (XtPointer)following_item->contents);
  2020. #if SLOPPY_TYPES == 1
  2021.   puts("==== NEW ==== NEW ==== NEW ==== NEW ==== NEW ==== NEW ==== NEW ====");
  2022.   print_widget_value(following_item, 1, 0);
  2023. #endif 
  2024.     }
  2025.  
  2026.   if (hit)
  2027.     *hit_return = NULL;
  2028.  
  2029.   where.x = mw->menu.shadow_thickness;
  2030.   where.y = mw->menu.shadow_thickness;
  2031.  
  2032.   ws = &mw->menu.windows [level];
  2033.   for (val = mw->menu.old_stack [level]->contents; val; val = val->next)
  2034.     {
  2035.       XPoint start;
  2036.  
  2037.       highlighted_p = (val == following_item);
  2038.       /* If this is the partition (the dummy item which says that menus
  2039.      after this should be flushright) then figure out how big the
  2040.      following items are.  This means we walk down the tail of the
  2041.      list twice, but that's no big deal - it's short.
  2042.        */
  2043.       if (horizontal_p && (menu_item_type (val) == PUSHRIGHT_TYPE))
  2044.     {
  2045.       widget_value *rest;
  2046.       XPoint flushright_size;
  2047.       int new_x;
  2048.       flushright_size.x = 0;
  2049.       flushright_size.y = 0;
  2050.       for (rest = val; rest; rest = rest->next)
  2051.         display_menu_item (mw, rest, ws, &flushright_size,
  2052.                    highlighted_p, horizontal_p, True);
  2053.       new_x = ws->width - (flushright_size.x + mw->menu.shadow_thickness);
  2054.       if (new_x > where.x)
  2055.         where.x = new_x;
  2056.       /* We know what we need; don't draw this item. */
  2057.       continue;
  2058.     }
  2059.  
  2060.       if (highlighted_p && highlighted_pos)
  2061.     {
  2062.       if (horizontal_p)
  2063.         highlighted_pos->x = where.x;
  2064.       else
  2065.         highlighted_pos->y = where.y;
  2066.     }
  2067.       
  2068.       just_compute_this_one_p =
  2069.     just_compute_p || ((this || that) && val != this &&  val != that);
  2070.  
  2071.       start.x = where.x;
  2072.       start.y = where.y;
  2073.       display_menu_item (mw, val, ws, &where, highlighted_p, horizontal_p,
  2074.              just_compute_this_one_p);
  2075.  
  2076.       if (highlighted_p && highlighted_pos)
  2077.     {
  2078.       if (horizontal_p)
  2079.         highlighted_pos->y = ws->height;
  2080.       else
  2081.         highlighted_pos->x = ws->width;
  2082.     }
  2083.  
  2084.       if (hit && !*hit_return && (val->type != SEPARATOR_TYPE))
  2085.     {
  2086.       if (horizontal_p && hit->x > start.x && hit->x < where.x)
  2087.         *hit_return = val;
  2088.       else if (!horizontal_p && hit->y > start.y && hit->y < where.y)
  2089.         *hit_return = val;
  2090.     }
  2091.  
  2092.       if (horizontal_p)
  2093.     where.y = mw->menu.shadow_thickness;
  2094.       else
  2095.     where.x = mw->menu.shadow_thickness;
  2096.     }
  2097.  
  2098.   /* Draw slab edges around menu */
  2099.   if (!just_compute_p)
  2100.     shadow_draw(mw, ws->window, 0, 0, ws->width, ws->height, SHADOW_OUT);
  2101. }
  2102.  
  2103. /* Motion code */
  2104. static void
  2105. set_new_state (XlwMenuWidget mw, widget_value *val, int level)
  2106. {
  2107.   int i;
  2108.   
  2109.   mw->menu.new_depth = 0;
  2110.   for (i = 0; i < level; i++)
  2111.     push_new_stack (mw, mw->menu.old_stack [i]);
  2112.   if (val)
  2113.     push_new_stack (mw, val);
  2114. }
  2115.  
  2116. static void
  2117. make_windows_if_needed (XlwMenuWidget mw, int n)
  2118. {
  2119.   int i;
  2120.   int start_at;
  2121.   XSetWindowAttributes xswa;
  2122.   int mask;
  2123. #define ROOT_PARENT
  2124. #ifdef ROOT_PARENT
  2125.   Window root = RootWindowOfScreen (DefaultScreenOfDisplay (XtDisplay (mw)));
  2126. #endif 
  2127.   window_state *windows;
  2128.   
  2129.   if (mw->menu.windows_length >= n)
  2130.     return;
  2131.  
  2132.   xswa.save_under = True;
  2133.   xswa.override_redirect = True;
  2134.   xswa.background_pixel = mw->core.background_pixel;
  2135.   xswa.border_pixel = mw->core.border_pixel;
  2136.   xswa.event_mask = (ExposureMask | ButtonMotionMask
  2137.              | ButtonReleaseMask | ButtonPressMask);
  2138.   xswa.cursor = mw->menu.cursor_shape;
  2139.   mask = CWSaveUnder | CWOverrideRedirect | CWBackPixel | CWBorderPixel
  2140.     | CWEventMask | CWCursor;
  2141.  
  2142.   if (mw->menu.use_backing_store)
  2143.     {
  2144.       xswa.backing_store = Always;
  2145.       mask |= CWBackingStore;
  2146.     }
  2147.   
  2148.   if (!mw->menu.windows)
  2149.     {
  2150.       mw->menu.windows =
  2151.     (window_state *) XtMalloc (n * sizeof (window_state));
  2152.       start_at = 0;
  2153.     }
  2154.   else
  2155.     {
  2156.       mw->menu.windows =
  2157.     (window_state *) XtRealloc ((char*)mw->menu.windows,
  2158.                     n * sizeof (window_state));
  2159.       start_at = mw->menu.windows_length;
  2160.     }
  2161.   mw->menu.windows_length = n;
  2162.  
  2163.   windows = mw->menu.windows;
  2164.  
  2165.   for (i = start_at; i < n; i++)
  2166.    {
  2167.      windows [i].x = 0;
  2168.      windows [i].y = 0;
  2169.      windows [i].width = 1;
  2170.      windows [i].height = 1;
  2171.      windows [i].window =
  2172.        XCreateWindow (XtDisplay (mw),
  2173. #ifdef ROOT_PARENT
  2174.               root,
  2175. #else 
  2176.               ((i >  0)
  2177.                ? windows[0].window
  2178.                : XtWindow (XtParent (mw))),
  2179. #endif 
  2180.               0, 0, 1, 1,
  2181.               0, 0, CopyFromParent, CopyFromParent, mask, &xswa);
  2182.   }
  2183. }
  2184.  
  2185. /* Make the window fit in the screen */
  2186. static void
  2187. fit_to_screen (XlwMenuWidget mw, window_state *ws, window_state *previous_ws,
  2188.            Boolean horizontal_p)
  2189. {
  2190.   int screen_width = WidthOfScreen (XtScreen (mw));
  2191.   int screen_height = HeightOfScreen (XtScreen (mw));
  2192.  
  2193.   if (ws->x < 0)
  2194.     ws->x = 0;
  2195.   else if ((int) (ws->x + ws->width) > screen_width)
  2196.     {
  2197.       if (!horizontal_p)
  2198.     ws->x = previous_ws->x - ws->width;
  2199.       else
  2200.     {
  2201.       ws->x = screen_width - ws->width;
  2202.  
  2203.       /* This check is to make sure we cut off the right side
  2204.              instead of the left side if the menu is wider than the
  2205.              screen. */
  2206.       if (ws->x < 0)
  2207.         ws->x = 0;
  2208.     }
  2209.     }
  2210.   if (ws->y < 0)
  2211.     ws->y = 0;
  2212.   else if ((int) (ws->y + ws->height) > screen_height)
  2213.     {
  2214.       if (horizontal_p)
  2215.     {
  2216.       /* A pulldown must either be entirely above or below the menubar.
  2217.          If we're here, the pulldown doesn't fit below the menubar, so
  2218.              let's determine if it will fit above the menubar.
  2219.              Only put it above if there is more room above than below.
  2220.          Note shadow_thickness offset to allow for slab surround.
  2221.          */
  2222.       if (ws->y > (screen_height / 2))
  2223.         ws->y = previous_ws->y - ws->height + mw->menu.shadow_thickness;
  2224.     }
  2225.       else
  2226.     {
  2227.       ws->y = screen_height - ws->height;
  2228.        /* if it's taller than the screen, display the topmost part
  2229.           that will fit, beginning at the top of the screen. */
  2230.       if (ws->y < 0)
  2231.         ws->y = 0;
  2232.     }
  2233.     }
  2234. }
  2235.  
  2236. /* Updates old_stack from new_stack and redisplays. */
  2237. static void
  2238. remap_menubar (XlwMenuWidget mw)
  2239. {
  2240.   int i;
  2241.   int last_same;
  2242.   XPoint selection_position;
  2243.   int old_depth = mw->menu.old_depth;
  2244.   int new_depth = mw->menu.new_depth;
  2245.   widget_value **old_stack;
  2246.   widget_value **new_stack;
  2247.   window_state *windows;
  2248.   widget_value *old_selection;
  2249.   widget_value *new_selection;
  2250.  
  2251.   /* Check that enough windows and old_stack are ready. */
  2252.   make_windows_if_needed (mw, new_depth);
  2253.   make_old_stack_space (mw, new_depth);
  2254.   windows = mw->menu.windows;
  2255.   old_stack = mw->menu.old_stack;
  2256.   new_stack = mw->menu.new_stack;
  2257.  
  2258.   /* compute the last identical different entry */
  2259.   for (i = 1; i < old_depth && i < new_depth; i++)
  2260.     if (old_stack [i] != new_stack [i])
  2261.       break;
  2262.   last_same = i - 1;
  2263.  
  2264.   /* Memorize the previously selected item to be able to refresh it */
  2265.   old_selection = last_same + 1 < old_depth ? old_stack [last_same + 1] : NULL;
  2266.   if (old_selection && !old_selection->enabled)
  2267.     old_selection = NULL;
  2268.   new_selection = last_same + 1 < new_depth ? new_stack [last_same + 1] : NULL;
  2269.   if (new_selection && !new_selection->enabled)
  2270.     new_selection = NULL;
  2271.  
  2272.   /* updates old_state from new_state.  It has to be done now because
  2273.      display_menu (called below) uses the old_stack to know what to display. */
  2274.   for (i = last_same + 1; i < new_depth; i++)
  2275.     old_stack [i] = new_stack [i];
  2276.   mw->menu.old_depth = new_depth;
  2277.  
  2278.   /* refresh the last seletion */
  2279.   selection_position.x = 0;
  2280.   selection_position.y = 0;
  2281.   display_menu (mw, last_same, new_selection == old_selection,
  2282.         &selection_position, NULL, NULL, old_selection, new_selection);
  2283.  
  2284.   /* Now popup the new menus */
  2285.   for (i = last_same + 1; i < new_depth && new_stack [i]->contents; i++)
  2286.     {
  2287.       window_state *previous_ws = &windows [i - 1];
  2288.       window_state *ws = &windows [i];
  2289.  
  2290.       ws->x = previous_ws->x + selection_position.x;
  2291.       ws->y = previous_ws->y + selection_position.y;
  2292.  
  2293.       /* take into account the slab around the new menu */
  2294.       ws->y -= mw->menu.shadow_thickness;
  2295.  
  2296.       size_menu (mw, i);
  2297.  
  2298.       fit_to_screen (mw, ws, previous_ws, mw->menu.horizontal && i == 1);
  2299.  
  2300.       XClearWindow (XtDisplay (mw), ws->window);
  2301.       XMoveResizeWindow (XtDisplay (mw), ws->window, ws->x, ws->y,
  2302.              ws->width, ws->height);
  2303.       XMapRaised (XtDisplay (mw), ws->window);
  2304.       display_menu (mw, i, False, &selection_position, NULL, NULL, NULL, NULL);
  2305.     }
  2306.  
  2307.   /* unmap the menus that popped down */
  2308.   for (i = new_depth - 1; i < old_depth; i++)
  2309.     if (i >= new_depth || !new_stack [i]->contents)
  2310.       XUnmapWindow (XtDisplay (mw), windows [i].window);
  2311. }
  2312.  
  2313. static Boolean
  2314. motion_event_is_in_menu (XlwMenuWidget mw, XMotionEvent *ev, int level,
  2315.              XPoint *relative_pos)
  2316. {
  2317.   window_state *ws = &mw->menu.windows [level];
  2318.   int x = level == 0 ? ws->x : ws->x + mw->menu.shadow_thickness;
  2319.   int y = level == 0 ? ws->y : ws->y + mw->menu.shadow_thickness;
  2320.   relative_pos->x = ev->x_root - x;
  2321.   relative_pos->y = ev->y_root - y;
  2322.   return (x < ev->x_root && ev->x_root < (int) (x + ws->width)
  2323.       && y < ev->y_root && ev->y_root < (int) (y + ws->height));
  2324. }
  2325.  
  2326. static Boolean
  2327. map_event_to_widget_value (XlwMenuWidget mw, XMotionEvent *ev,
  2328.                widget_value **val_ptr, int *level,
  2329.                Boolean *inside_menu)
  2330. {
  2331.   int         i;
  2332.   XPoint    relative_pos;
  2333.   window_state*    ws;
  2334.  
  2335.   *val_ptr = NULL;
  2336.   *inside_menu = False;
  2337.   
  2338.   /* Find the window */
  2339.   for (i = mw->menu.old_depth - 1; i >= 0; i--)
  2340.     {
  2341.       ws = &mw->menu.windows [i];
  2342.       if (ws && motion_event_is_in_menu (mw, ev, i, &relative_pos))
  2343.     {
  2344.       *inside_menu = True;    /* special logic for menubar below... */
  2345.       if ((ev->type == ButtonPress) ||
  2346.           (ev->state != 0))
  2347.         {
  2348.           display_menu (mw, i, True, NULL, &relative_pos,
  2349.                 val_ptr, NULL, NULL);
  2350.           if (*val_ptr)
  2351.         {
  2352.           *level = i + 1;
  2353.           *inside_menu = True;
  2354.           return True;
  2355.         }
  2356.           else if (mw->menu.horizontal || i == 0)
  2357.         {
  2358.           /* if we're clicking on empty part of the menubar, then
  2359.              unpost the stay-up menu */
  2360.           *inside_menu = False;
  2361.         }
  2362.         }
  2363.     }
  2364.     }
  2365.   return False;
  2366. }
  2367.  
  2368. /* Procedures */
  2369. static void
  2370. make_drawing_gcs (XlwMenuWidget mw)
  2371. {
  2372.   XGCValues xgcv;
  2373.   unsigned long flags = (GCFont | GCForeground | GCBackground);
  2374.  
  2375. #ifdef NEED_MOTIF
  2376.   xgcv.font = default_font_of_font_list (mw->menu.font_list)->fid;
  2377. #else
  2378.   xgcv.font = mw->menu.font->fid;
  2379. #endif
  2380.  
  2381.   xgcv.foreground = mw->core.background_pixel;
  2382.   xgcv.background = mw->menu.foreground;
  2383.   mw->menu.background_gc = XtGetGC ((Widget)mw, flags, &xgcv);
  2384.  
  2385.   xgcv.foreground = mw->menu.foreground;
  2386.   xgcv.background = mw->core.background_pixel;
  2387.   mw->menu.foreground_gc = XtGetGC ((Widget)mw, flags, &xgcv);
  2388.  
  2389.   if (mw->menu.select_color != (Pixel)-1)
  2390.     {
  2391.       xgcv.foreground = mw->menu.select_color;
  2392.     }
  2393.   else
  2394.     {
  2395.       Display *dpy = XtDisplay(mw);
  2396.       if (CellsOfScreen(DefaultScreenOfDisplay(dpy)) <= 2)
  2397.     { /* mono */
  2398.       xgcv.foreground = mw->menu.foreground;
  2399.     }
  2400.       else
  2401.     { /* color */
  2402.       XColor xcolor;
  2403.       Display *dpy = XtDisplay ((Widget) mw);
  2404.       Colormap cmap = DefaultColormapOfScreen (XtScreen ((Widget) mw));
  2405.       xcolor.pixel = mw->core.background_pixel;
  2406.       XQueryColor (dpy, cmap, &xcolor);
  2407.       xcolor.red   *= 0.85;
  2408.       xcolor.green *= 0.85;
  2409.       xcolor.blue  *= 0.85;
  2410.       if (XAllocColor (dpy, cmap, &xcolor))
  2411.         xgcv.foreground = xcolor.pixel;
  2412.     }
  2413.     }
  2414.   xgcv.background = mw->core.background_pixel;
  2415.   mw->menu.select_gc = XtGetGC ((Widget)mw, flags, &xgcv);
  2416.   
  2417.   xgcv.foreground = mw->menu.foreground;
  2418.   xgcv.background = mw->core.background_pixel;
  2419.   xgcv.fill_style = FillStippled;
  2420.   xgcv.stipple = mw->menu.gray_pixmap;
  2421.   mw->menu.inactive_gc = XtGetGC ((Widget)mw,
  2422.                   (flags | GCFillStyle | GCStipple),
  2423.                   &xgcv);
  2424.  
  2425.   xgcv.foreground = mw->menu.button_foreground;
  2426.   xgcv.background = mw->core.background_pixel;
  2427.   mw->menu.button_gc = XtGetGC ((Widget)mw, flags, &xgcv);
  2428.   
  2429.   xgcv.fill_style = FillStippled;
  2430.   xgcv.stipple = mw->menu.gray_pixmap;
  2431.   mw->menu.inactive_button_gc = XtGetGC ((Widget)mw,
  2432.                   (flags | GCFillStyle | GCStipple),
  2433.                      &xgcv);
  2434. }
  2435.  
  2436. static void
  2437. release_drawing_gcs (XlwMenuWidget mw)
  2438. {
  2439.   XtReleaseGC ((Widget) mw, mw->menu.foreground_gc);
  2440.   XtReleaseGC ((Widget) mw, mw->menu.button_gc);
  2441.   XtReleaseGC ((Widget) mw, mw->menu.inactive_gc);
  2442.   XtReleaseGC ((Widget) mw, mw->menu.inactive_button_gc);
  2443.   XtReleaseGC ((Widget) mw, mw->menu.background_gc);
  2444.   XtReleaseGC ((Widget) mw, mw->menu.select_gc);
  2445.   /* let's get some segvs if we try to use these... */
  2446.   mw->menu.foreground_gc = (GC) -1;
  2447.   mw->menu.button_gc = (GC) -1;
  2448.   mw->menu.inactive_gc = (GC) -1;
  2449.   mw->menu.inactive_button_gc = (GC) -1;
  2450.   mw->menu.background_gc = (GC) -1;
  2451.   mw->menu.select_gc = (GC) -1;
  2452. }
  2453.  
  2454. #define MINL(x,y) ((((unsigned long) (x)) < ((unsigned long) (y))) \
  2455.            ? ((unsigned long) (x)) : ((unsigned long) (y)))
  2456.  
  2457. static void
  2458. make_shadow_gcs (XlwMenuWidget mw)
  2459. {
  2460.   XGCValues xgcv;
  2461.   unsigned long pm = 0;
  2462.   Display *dpy = XtDisplay ((Widget) mw);
  2463.   Colormap cmap = DefaultColormapOfScreen (XtScreen ((Widget) mw));
  2464.   XColor topc, botc;
  2465.   int top_frobbed = 0, bottom_frobbed = 0;
  2466.  
  2467.   if (mw->menu.top_shadow_color == -1)
  2468.     mw->menu.top_shadow_color = mw->core.background_pixel;
  2469.   if (mw->menu.bottom_shadow_color == -1)
  2470.     mw->menu.bottom_shadow_color = mw->menu.foreground;
  2471.  
  2472.   if (mw->menu.top_shadow_color == mw->core.background_pixel ||
  2473.       mw->menu.top_shadow_color == mw->menu.foreground)
  2474.     {
  2475.       topc.pixel = mw->core.background_pixel;
  2476.       XQueryColor (dpy, cmap, &topc);
  2477.       /* don't overflow/wrap! */
  2478.       topc.red   = MINL (65535, topc.red   * 1.2);
  2479.       topc.green = MINL (65535, topc.green * 1.2);
  2480.       topc.blue  = MINL (65535, topc.blue  * 1.2);
  2481.       if (XAllocColor (dpy, cmap, &topc))
  2482.     {
  2483.           if (topc.pixel == mw->core.background_pixel)
  2484.         {
  2485.           XFreeColors( dpy, cmap, &topc.pixel, 1, 0);
  2486.           topc.red   = MINL (65535, topc.red   + 0x8000);
  2487.           topc.green = MINL (65535, topc.green + 0x8000);
  2488.           topc.blue  = MINL (65535, topc.blue  + 0x8000);
  2489.           if (XAllocColor (dpy, cmap, &topc))
  2490.         {
  2491.           mw->menu.top_shadow_color = topc.pixel;
  2492.         }  
  2493.         }
  2494.       else
  2495.         {
  2496.           mw->menu.top_shadow_color = topc.pixel;
  2497.         }
  2498.  
  2499.       top_frobbed = 1;
  2500.     }
  2501.     }
  2502.   if (mw->menu.bottom_shadow_color == mw->menu.foreground ||
  2503.       mw->menu.bottom_shadow_color == mw->core.background_pixel)
  2504.     {
  2505.       botc.pixel = mw->core.background_pixel;
  2506.       XQueryColor (dpy, cmap, &botc);
  2507.       botc.red   *= 0.6;
  2508.       botc.green *= 0.6;
  2509.       botc.blue  *= 0.6;
  2510.       if (XAllocColor (dpy, cmap, &botc))
  2511.     {
  2512.       if (botc.pixel == mw->core.background_pixel)
  2513.         {
  2514.           XFreeColors (dpy, cmap, &botc.pixel, 1, 0);
  2515.           botc.red   = MINL (65535, botc.red   + 0x4000);
  2516.           botc.green = MINL (65535, botc.green + 0x4000);
  2517.           botc.blue  = MINL (65535, botc.blue  + 0x4000);
  2518.           if (XAllocColor (dpy, cmap, &botc))
  2519.         {
  2520.           mw->menu.bottom_shadow_color = botc.pixel;
  2521.         }  
  2522.         }
  2523.       else
  2524.         {
  2525.           mw->menu.bottom_shadow_color = botc.pixel;
  2526.         }
  2527.  
  2528.           bottom_frobbed = 1;
  2529.     }
  2530.     }
  2531.  
  2532.   if (top_frobbed && bottom_frobbed)
  2533.     {
  2534.       int top_avg = ((topc.red / 3) + (topc.green / 3) + (topc.blue / 3));
  2535.       int bot_avg = ((botc.red / 3) + (botc.green / 3) + (botc.blue / 3));
  2536.       if (bot_avg > top_avg)
  2537.     {
  2538.       Pixel tmp = mw->menu.top_shadow_color;
  2539.       mw->menu.top_shadow_color = mw->menu.bottom_shadow_color;
  2540.       mw->menu.bottom_shadow_color = tmp;
  2541.     }
  2542.       else if (topc.pixel == botc.pixel)
  2543.     {
  2544.       if (botc.pixel == mw->menu.foreground)
  2545.         mw->menu.top_shadow_color = mw->core.background_pixel;
  2546.       else
  2547.         mw->menu.bottom_shadow_color = mw->menu.foreground;
  2548.     }
  2549.     }
  2550.  
  2551.   if (!mw->menu.top_shadow_pixmap &&
  2552.       mw->menu.top_shadow_color == mw->core.background_pixel)
  2553.     {
  2554.       mw->menu.top_shadow_pixmap = mw->menu.gray_pixmap;
  2555.       mw->menu.top_shadow_color = mw->menu.foreground;
  2556.     }
  2557.   if (!mw->menu.bottom_shadow_pixmap &&
  2558.       mw->menu.bottom_shadow_color == mw->core.background_pixel)
  2559.     {
  2560.       mw->menu.bottom_shadow_pixmap = mw->menu.gray_pixmap;
  2561.       mw->menu.bottom_shadow_color = mw->menu.foreground;
  2562.     }
  2563.  
  2564.   xgcv.fill_style = FillOpaqueStippled;
  2565.   xgcv.foreground = mw->menu.top_shadow_color;
  2566.   xgcv.background = mw->core.background_pixel;
  2567.   xgcv.stipple = mw->menu.top_shadow_pixmap;
  2568.   pm = (xgcv.stipple ? GCStipple|GCFillStyle : 0);
  2569.   mw->menu.shadow_top_gc =
  2570.     XtGetGC((Widget)mw, GCForeground|GCBackground|pm, &xgcv);
  2571.  
  2572.   xgcv.foreground = mw->menu.bottom_shadow_color;
  2573.   xgcv.stipple = mw->menu.bottom_shadow_pixmap;
  2574.   pm = (xgcv.stipple ? GCStipple|GCFillStyle : 0);
  2575.   mw->menu.shadow_bottom_gc =
  2576.     XtGetGC ((Widget)mw, GCForeground|GCBackground|pm, &xgcv);
  2577. }
  2578.  
  2579.  
  2580. static void
  2581. release_shadow_gcs (XlwMenuWidget mw)
  2582. {
  2583.   XtReleaseGC ((Widget) mw, mw->menu.shadow_top_gc);
  2584.   XtReleaseGC ((Widget) mw, mw->menu.shadow_bottom_gc);
  2585. }
  2586.  
  2587. static void
  2588. extract_font_extents (XlwMenuWidget mw)
  2589. {
  2590. #ifdef NEED_MOTIF
  2591.   /* Find the maximal ascent/descent of the fonts in the font list
  2592.      so that all menu items can be the same height... */
  2593.   mw->menu.font_ascent  = 0;
  2594.   mw->menu.font_descent = 0;
  2595.   
  2596.   {
  2597.     XmFontContext context;
  2598. #if (XmVersion >= 1002)
  2599.     XmFontListEntry fontentry;
  2600. #else    
  2601.     XmStringCharSet charset;
  2602. #endif
  2603.     XFontStruct *font;
  2604.  
  2605.     if (! XmFontListInitFontContext (&context, mw->menu.font_list))
  2606.       abort ();
  2607. #if (XmVersion >= 1002)
  2608.     /* There is a BUG in the 1.2 version of XmFontListGetNextFont() (or more
  2609.        specifically, in _XmGetFirstFont()) that can cause a null pointer to be
  2610.        passed to XFontsOfFontSet.  Use XmFontListNextEntry(), which is the
  2611.        newer equivalent, instead.  Also, it supports font sets, and the
  2612.        older function doesn't. */
  2613.     while ((fontentry = XmFontListNextEntry (context)))
  2614.       {
  2615.     char *one_of_them;
  2616.     XmFontType rettype; 
  2617.     
  2618.     one_of_them = XmFontListEntryGetFont (fontentry, &rettype);
  2619.     if (rettype == XmFONT_IS_FONTSET)
  2620.       {
  2621.         XFontSet fontset = (XFontSet) one_of_them;
  2622.         XFontStruct **fontstruct_list;
  2623.         char **fontname_list;
  2624.         int fontcount = XFontsOfFontSet (fontset, &fontstruct_list,
  2625.                          &fontname_list);
  2626.         while (--fontcount >= 0)
  2627.           {
  2628.         font = fontstruct_list[fontcount];
  2629.         if (font->ascent > (int) mw->menu.font_ascent)
  2630.           mw->menu.font_ascent = font->ascent;
  2631.         if (font->descent > (int) mw->menu.font_descent)
  2632.           mw->menu.font_descent = font->descent;
  2633.           }
  2634.       }
  2635.     else /* XmFONT_IS_FONT */
  2636.       {
  2637.         font = (XFontStruct *) one_of_them;
  2638.         if (font->ascent > (int) mw->menu.font_ascent)
  2639.           mw->menu.font_ascent = font->ascent;
  2640.         if (font->descent > (int) mw->menu.font_descent)
  2641.           mw->menu.font_descent = font->descent;
  2642.       }
  2643.       }
  2644. #else /* motif 1.1 */
  2645.     while (XmFontListGetNextFont (context, &charset, &font))
  2646.       {
  2647.     if (font->ascent > (int) mw->menu.font_ascent)
  2648.       mw->menu.font_ascent = font->ascent;
  2649.     if (font->descent > (int) mw->menu.font_descent)
  2650.       mw->menu.font_descent = font->descent;
  2651.     XtFree (charset);
  2652.       }
  2653. #endif    
  2654.     XmFontListFreeFontContext (context);
  2655.   }
  2656. #else
  2657.   mw->menu.font_ascent  = mw->menu.font->ascent;
  2658.   mw->menu.font_descent = mw->menu.font->descent;
  2659. #endif
  2660. }
  2661.  
  2662. #ifdef NEED_MOTIF
  2663. static XFontStruct *
  2664. default_font_of_font_list (XmFontList font_list)
  2665. {
  2666.   XFontStruct *font = 0;
  2667. # if 0
  2668.   /* Xm/Label.c does this: */
  2669.   _XmFontListGetDefaultFont (font_list, &font);
  2670. # else  /* !0 */
  2671.   {
  2672.     XmFontContext context;
  2673. #if (XmVersion >= 1002)
  2674.     XmFontListEntry fontentry;
  2675.     XmFontType rettype;
  2676.     char *one_of_them;
  2677. #else    
  2678.     XmStringCharSet charset;
  2679. #endif
  2680.     
  2681.     if (! XmFontListInitFontContext (&context, font_list))
  2682.       abort ();
  2683. #if (XmVersion >= 1002)
  2684.     /* There is a BUG in the 1.2 version of XmFontListGetNextFont() (or more
  2685.        specifically, in _XmGetFirstFont()) that can cause a null pointer to be
  2686.        passed to XFontsOfFontSet.  Use XmFontListNextEntry(), which is the
  2687.        newer equivalent, instead. */
  2688.     fontentry = XmFontListNextEntry (context);
  2689.     one_of_them = XmFontListEntryGetFont (fontentry, &rettype);
  2690.     if (rettype == XmFONT_IS_FONTSET)
  2691.       {
  2692.     XFontSet fontset = (XFontSet) one_of_them;
  2693.     XFontStruct **fontstruct_list;
  2694.     char **fontname_list;
  2695.     (void) XFontsOfFontSet (fontset, &fontstruct_list, &fontname_list);
  2696.     font = fontstruct_list[0];
  2697.       }
  2698.     else /* XmFONT_IS_FONT */
  2699.       {
  2700.     font = (XFontStruct *) one_of_them;
  2701.       }
  2702. #else    
  2703.     if (! XmFontListGetNextFont (context, &charset, &font))
  2704.       abort ();
  2705.     XtFree (charset);
  2706. #endif    
  2707.     XmFontListFreeFontContext (context);
  2708.   }
  2709. # endif /* !0 */
  2710.  
  2711.   if (! font) abort ();
  2712.   return font;
  2713. }
  2714. #endif
  2715.  
  2716. static void
  2717. XlwMenuInitialize (Widget request, Widget new, ArgList args,
  2718.            Cardinal *num_args)
  2719. {
  2720.   /* Get the GCs and the widget size */
  2721.   XlwMenuWidget mw = (XlwMenuWidget)new;
  2722.   
  2723.   XSetWindowAttributes xswa;
  2724.   int mask;
  2725.   
  2726.   Window window = RootWindowOfScreen (DefaultScreenOfDisplay (XtDisplay (mw)));
  2727.   Display *display = XtDisplay (mw);
  2728.   
  2729. /*  mw->menu.cursor = XCreateFontCursor (display, mw->menu.cursor_shape); */
  2730.   mw->menu.cursor = mw->menu.cursor_shape;
  2731.   
  2732.   mw->menu.gray_pixmap = XCreatePixmapFromBitmapData (display, window,
  2733.                               (char *) gray_bits,
  2734.                               gray_width,
  2735.                               gray_height, 1, 0, 1);
  2736.   
  2737. #ifdef NEED_MOTIF
  2738.   /* The menu.font_list slot came from the *fontList resource (Motif standard.)
  2739.      The menu.font_list_2 slot came from the *font resource, for backward
  2740.      compatibility with older versions of this code, and consistency with the
  2741.      rest of emacs.  If both font and fontList are specified, we use font.
  2742.      If only one is specified, we use that.  If neither are specified, we
  2743.      use the "fallback" value.  What a kludge!!!
  2744.  
  2745.      Note that this has the bug that a more general wildcard like "*fontList:"
  2746.      will override a more specific resoure like "Emacs*menubar.font:".  But
  2747.      I can't think of a way around that.
  2748.    */
  2749.   if (mw->menu.font_list)      /* if *fontList is specified, use that */
  2750.     ;
  2751.   else if (mw->menu.font_list_2)  /* else if *font is specified, use that */
  2752.     mw->menu.font_list = mw->menu.font_list_2;
  2753.   else                  /* otherwise use default */
  2754.     mw->menu.font_list = mw->menu.fallback_font_list;
  2755. #endif
  2756.  
  2757.   make_drawing_gcs (mw);
  2758.   make_shadow_gcs (mw);
  2759.   extract_font_extents (mw);
  2760.  
  2761.   xswa.background_pixel = mw->core.background_pixel;
  2762.   xswa.border_pixel = mw->core.border_pixel;
  2763.   mask = CWBackPixel | CWBorderPixel;
  2764.   
  2765.   mw->menu.popped_up = False;
  2766.   mw->menu.pointer_grabbed = False;
  2767.   mw->menu.next_release_must_exit = False;
  2768.   
  2769.   mw->menu.old_depth = 1;
  2770.   mw->menu.old_stack = (widget_value**)XtMalloc (sizeof (widget_value*));
  2771.   mw->menu.old_stack_length = 1;
  2772.   mw->menu.old_stack [0] = mw->menu.contents;
  2773.   
  2774.   mw->menu.new_depth = 0;
  2775.   mw->menu.new_stack = 0;
  2776.   mw->menu.new_stack_length = 0;
  2777.   push_new_stack (mw, mw->menu.contents);
  2778.   
  2779.   mw->menu.windows = (window_state*)XtMalloc (sizeof (window_state));
  2780.   mw->menu.windows_length = 1;
  2781.   mw->menu.windows [0].x = 0;
  2782.   mw->menu.windows [0].y = 0;
  2783.   mw->menu.windows [0].width = 0;
  2784.   mw->menu.windows [0].height = 0;
  2785.   size_menu (mw, 0);
  2786.   
  2787.   mw->core.width = mw->menu.windows [0].width;
  2788.   mw->core.height = mw->menu.windows [0].height;
  2789. }
  2790.  
  2791. static void
  2792. XlwMenuClassInitialize (void)
  2793. {
  2794. }
  2795.  
  2796. static void
  2797. XlwMenuRealize (Widget w, Mask *valueMask, XSetWindowAttributes *attributes)
  2798. {
  2799.   XlwMenuWidget mw = (XlwMenuWidget)w;
  2800.   XSetWindowAttributes xswa;
  2801.   int mask;
  2802.  
  2803.   (*xlwMenuWidgetClass->core_class.superclass->core_class.realize)
  2804.     (w, valueMask, attributes);
  2805.  
  2806.   xswa.save_under = True;
  2807.   xswa.cursor = mw->menu.cursor_shape;
  2808.   mask = CWSaveUnder | CWCursor;
  2809.   if (mw->menu.use_backing_store)
  2810.     {
  2811.       xswa.backing_store = Always;
  2812.       mask |= CWBackingStore;
  2813.     }
  2814.   XChangeWindowAttributes (XtDisplay (w), XtWindow (w), mask, &xswa);
  2815.  
  2816.   mw->menu.windows [0].window = XtWindow (w);
  2817.   mw->menu.windows [0].x = w->core.x;
  2818.   mw->menu.windows [0].y = w->core.y;
  2819.   mw->menu.windows [0].width = w->core.width;
  2820.   mw->menu.windows [0].height = w->core.height;
  2821. }
  2822.  
  2823. /* Only the toplevel menubar/popup is a widget so it's the only one that
  2824.    receives expose events through Xt.  So we repaint all the other panes
  2825.    when receiving an Expose event. */
  2826. static void 
  2827. XlwMenuRedisplay (Widget w, XEvent *ev, Region region)
  2828. {
  2829.   XlwMenuWidget mw = (XlwMenuWidget)w;
  2830.   int i;
  2831.  
  2832.   if (mw->core.being_destroyed) return;
  2833.  
  2834.   for (i = 0; i < mw->menu.old_depth; i++)
  2835.     display_menu (mw, i, False, NULL, NULL, NULL, NULL, NULL);
  2836.   set_new_state (mw, NULL, mw->menu.old_depth); /* #### - ??? */
  2837.   remap_menubar (mw);        /* #### - do these two lines do anything? */
  2838. }
  2839.  
  2840. static void 
  2841. XlwMenuDestroy (Widget w)
  2842. {
  2843.   int i;
  2844.   XlwMenuWidget mw = (XlwMenuWidget) w;
  2845.  
  2846.   if (mw->menu.pointer_grabbed)
  2847.     {
  2848.       XtUngrabPointer (w, CurrentTime);
  2849.       mw->menu.pointer_grabbed = False;
  2850.     }
  2851.   
  2852.   release_drawing_gcs (mw);
  2853.   release_shadow_gcs (mw);
  2854.  
  2855.   /* this doesn't come from the resource db but is created explicitly
  2856.      so we must free it ourselves. */
  2857.   XFreePixmap (XtDisplay (mw), mw->menu.gray_pixmap);
  2858.   mw->menu.gray_pixmap = (Pixmap) -1;
  2859.  
  2860.   /* Don't free mw->menu.contents because that comes from our creator.
  2861.      The `*_stack' elements are just pointers into `contents' so leave
  2862.      that alone too.  But free the stacks themselves. */
  2863.   if (mw->menu.old_stack) XtFree ((char *) mw->menu.old_stack);
  2864.   if (mw->menu.new_stack) XtFree ((char *) mw->menu.new_stack);
  2865.  
  2866.   /* Remember, you can't free anything that came from the resource
  2867.      database.  This includes:
  2868.          mw->menu.cursor
  2869.          mw->menu.top_shadow_pixmap
  2870.          mw->menu.bottom_shadow_pixmap
  2871.          mw->menu.font
  2872.          mw->menu.font_set
  2873.      Also the color cells of top_shadow_color, bottom_shadow_color,
  2874.      foreground, and button_foreground will never be freed until this
  2875.      client exits.  Nice, eh?
  2876.    */
  2877.  
  2878.   /* start from 1 because the one in slot 0 is w->core.window */
  2879.   for (i = 1; i < mw->menu.windows_length; i++)
  2880.     XDestroyWindow (XtDisplay (mw), mw->menu.windows [i].window);
  2881.   if (mw->menu.windows)
  2882.     XtFree ((char *) mw->menu.windows);
  2883. }
  2884.  
  2885. static Boolean 
  2886. XlwMenuSetValues (Widget current, Widget request, Widget new, ArgList args,
  2887.           Cardinal *num_args)
  2888. {
  2889.   XlwMenuWidget oldmw = (XlwMenuWidget)current;
  2890.   XlwMenuWidget newmw = (XlwMenuWidget)new;
  2891.   Boolean redisplay = False;
  2892.   int i;
  2893.  
  2894.   if (newmw->menu.contents
  2895.       && newmw->menu.contents->contents
  2896.       && newmw->menu.contents->contents->change >= VISIBLE_CHANGE)
  2897.     redisplay = True;
  2898.  
  2899.   if (newmw->core.background_pixel != oldmw->core.background_pixel
  2900.       || newmw->menu.foreground != oldmw->menu.foreground
  2901.       /* For the XEditResource protocol, which may want to change the font. */
  2902. #ifdef NEED_MOTIF
  2903.       || newmw->menu.font_list != oldmw->menu.font_list
  2904.       || newmw->menu.font_list_2 != oldmw->menu.font_list_2
  2905.       || newmw->menu.fallback_font_list != oldmw->menu.fallback_font_list
  2906. #else
  2907.       || newmw->menu.font != oldmw->menu.font
  2908. #endif
  2909.       )
  2910.     {
  2911.       release_drawing_gcs (newmw);
  2912.       make_drawing_gcs (newmw);
  2913.       redisplay = True;
  2914.       
  2915.       for (i = 0; i < oldmw->menu.windows_length; i++)
  2916.     {
  2917.       XSetWindowBackground (XtDisplay (oldmw),
  2918.                 oldmw->menu.windows [i].window,
  2919.                 newmw->core.background_pixel);
  2920.       /* clear windows and generate expose events */
  2921.       XClearArea (XtDisplay (oldmw), oldmw->menu.windows[i].window,
  2922.               0, 0, 0, 0, True);
  2923.     }
  2924.     }
  2925.  
  2926.   return redisplay;
  2927. }
  2928.  
  2929. static void 
  2930. XlwMenuResize (Widget w)
  2931. {
  2932.   XlwMenuWidget mw = (XlwMenuWidget)w;
  2933.  
  2934.   mw->menu.windows [0].width = mw->core.width;
  2935.   mw->menu.windows [0].height = mw->core.height;
  2936. }
  2937.  
  2938. /* Action procedures */
  2939. static void
  2940. handle_single_motion_event (XlwMenuWidget mw, XMotionEvent *ev,
  2941.                 Boolean select_p)
  2942. {
  2943.   widget_value *val;
  2944.   Boolean      stay_up;
  2945.   int           level;
  2946.  
  2947.   if (!map_event_to_widget_value (mw, ev, &val, &level, &stay_up))
  2948.     {
  2949.       /* we wind up here when: (a) the event is in the menubar, (b) the
  2950.      event isn't in the menubar or any of the panes, (c) the event is on
  2951.      a disabled menu item */
  2952.       pop_new_stack_if_no_contents (mw);
  2953.       if (select_p && !stay_up) {
  2954.     /* pop down all menus and exit */
  2955.     mw->menu.next_release_must_exit = True;
  2956.     set_new_state(mw, (val = NULL), 1); 
  2957.       }
  2958.     }
  2959.   else
  2960.     {
  2961.       /* we wind up here when: (a) the event pops up a pull_right menu,
  2962.      (b) a menu item that is not disabled is highlighted */
  2963.       if (select_p && mw->menu.bounce_down
  2964.            && close_to_reference_time((Widget)mw,
  2965.                       mw->menu.menu_bounce_time,
  2966.                       (XEvent *)ev))
  2967.     {
  2968.       /* motion can cause more than one event.  Don't bounce right back
  2969.          up if we've just bounced down. */
  2970.       val = NULL;
  2971.     }
  2972.       else if (select_p && mw->menu.bounce_down &&
  2973.            mw->menu.last_selected_val &&
  2974.            (mw->menu.last_selected_val == val))
  2975.     {
  2976.       val = NULL;        /* assigned to mw->last_selected_val below */
  2977.       mw->menu.menu_bounce_time = ev->time;
  2978.       /* popdown last menu if we're selecting the same menu item as we did
  2979.          last time and the XlwMenu.bounceDown resource is set, if the
  2980.          item is on the menubar itself, then exit. */
  2981.       if (level == (mw->menu.popped_up ? 0 : 1))
  2982.         mw->menu.next_release_must_exit = True;
  2983.     }    
  2984.       else
  2985.     mw->menu.menu_bounce_time = 0;
  2986.       set_new_state (mw, val, level);
  2987.     }
  2988.   mw->menu.last_selected_val = val;
  2989.   remap_menubar (mw);
  2990.   
  2991.   /* Sync with the display.  Makes it feel better on X terms. */
  2992.   XFlush (XtDisplay (mw));
  2993. }
  2994.  
  2995. static void
  2996. handle_motion_event (XlwMenuWidget mw, XMotionEvent *ev,
  2997.              Boolean select_p)
  2998. {
  2999.   int x = ev->x_root;
  3000.   int y = ev->y_root;
  3001.   int state = ev->state;
  3002.   XMotionEvent *event= ev, dummy;
  3003.  
  3004.   /* allow motion events to be generated again */
  3005.   dummy.window = ev->window;
  3006.   if (ev->is_hint
  3007.       && XQueryPointer (XtDisplay (mw), dummy.window,
  3008.             &dummy.root, &dummy.subwindow,
  3009.             &dummy.x_root, &dummy.y_root,
  3010.             &dummy.x, &dummy.y,
  3011.             &dummy.state)
  3012.       && dummy.state == state
  3013.       && (dummy.x_root != x || dummy.y_root != y))
  3014.     {
  3015.       /* don't handle the event twice or that breaks bounce_down.  --Stig */
  3016.       dummy.type = ev->type;
  3017.       event = &dummy;
  3018.     }
  3019.  
  3020.   handle_single_motion_event (mw, event, select_p);
  3021. }
  3022.  
  3023. static void 
  3024. Start (Widget w, XEvent *ev, String *params, Cardinal *num_params)
  3025. {
  3026.   XlwMenuWidget mw = (XlwMenuWidget)w;
  3027.  
  3028.   if (!mw->menu.pointer_grabbed)
  3029.     {
  3030.       mw->menu.menu_post_time = ev->xbutton.time;
  3031.       mw->menu.menu_bounce_time = 0;
  3032.       mw->menu.next_release_must_exit = True;
  3033.       mw->menu.last_selected_val = NULL;
  3034.  
  3035.       XtCallCallbackList ((Widget)mw, mw->menu.open, NULL);
  3036.       
  3037.       /* notes the absolute position of the menubar window */
  3038.       mw->menu.windows [0].x = ev->xmotion.x_root - ev->xmotion.x;
  3039.       mw->menu.windows [0].y = ev->xmotion.y_root - ev->xmotion.y;
  3040.       
  3041.       XtGrabPointer ((Widget)mw, False,
  3042.              (ButtonMotionMask | ButtonReleaseMask | ButtonPressMask),
  3043.              GrabModeAsync, GrabModeAsync,
  3044.              None, mw->menu.cursor_shape,
  3045.              ((XButtonPressedEvent*)ev)->time);
  3046.       mw->menu.pointer_grabbed = True;
  3047.     }
  3048.  
  3049.   /* handles the down like a move, slots are mostly compatible */
  3050.   handle_motion_event (mw, &ev->xmotion, True);
  3051. }
  3052.  
  3053. static void 
  3054. Drag (Widget w, XEvent *ev, String *params, Cardinal *num_params)
  3055. {
  3056.   XlwMenuWidget mw = (XlwMenuWidget)w;
  3057.   handle_motion_event (mw, &ev->xmotion, False);
  3058. }
  3059.  
  3060. static void 
  3061. Select (Widget w, XEvent *ev, String *params, Cardinal *num_params)
  3062. {
  3063.   XlwMenuWidget mw = (XlwMenuWidget)w;
  3064.   widget_value *selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
  3065.   
  3066.   /* If user releases the button quickly, without selecting anything,
  3067.      after the initial down-click that brought the menu up,
  3068.      do nothing. */
  3069.   if ((selected_item == 0 || selected_item->call_data == 0)
  3070.       && (!mw->menu.next_release_must_exit
  3071.       || close_to_reference_time(w, mw->menu.menu_post_time, ev)))
  3072.     {
  3073.       mw->menu.next_release_must_exit = False;
  3074.       return;
  3075.     }
  3076.  
  3077.   /* pop down everything */
  3078.   mw->menu.new_depth = 1;
  3079.   remap_menubar (mw);
  3080.       
  3081.       /* Destroy() only gets called for popup menus.  Menubar widgets aren't
  3082.      destroyed when their menu panes get nuked. */
  3083.   if (mw->menu.pointer_grabbed)
  3084.     {
  3085.       XtUngrabPointer ((Widget)w, ev->xmotion.time);
  3086.       mw->menu.pointer_grabbed = False;
  3087.     }
  3088.   
  3089.   if (mw->menu.popped_up)
  3090.     {
  3091.       mw->menu.popped_up = False;
  3092.       XtPopdown (XtParent (mw));
  3093.     }
  3094.   
  3095.   /* callback */
  3096.   XtCallCallbackList ((Widget)mw, mw->menu.select, (XtPointer)selected_item);
  3097. }
  3098.  
  3099.  
  3100. /* Special code to pop-up a menu */
  3101. void
  3102. xlw_pop_up_menu (XlwMenuWidget mw, XButtonPressedEvent *event)
  3103. {
  3104.   int        x = event->x_root;
  3105.   int        y = event->y_root;
  3106.   int        w;
  3107.   int        h;
  3108.   int        borderwidth = mw->menu.shadow_thickness;
  3109.   Screen*    screen = XtScreen (mw);
  3110.  
  3111.   mw->menu.menu_post_time = event->time;
  3112.   mw->menu.menu_bounce_time = 0;
  3113.   mw->menu.next_release_must_exit = True;
  3114.   mw->menu.last_selected_val = NULL;
  3115.  
  3116.   XtCallCallbackList ((Widget)mw, mw->menu.open, NULL);
  3117.  
  3118.   size_menu (mw, 0);
  3119.  
  3120.   w = mw->menu.windows [0].width;
  3121.   h = mw->menu.windows [0].height;
  3122.  
  3123.   x -= borderwidth;
  3124.   y -= borderwidth;
  3125.   if (x < borderwidth)
  3126.     x = borderwidth;
  3127.   if (x + w + 2 * borderwidth > WidthOfScreen (screen))
  3128.     x = WidthOfScreen (screen) - w - 2 * borderwidth;
  3129.   if (y < borderwidth)
  3130.     y = borderwidth;
  3131.   if (y + h + 2 * borderwidth> HeightOfScreen (screen))
  3132.     y = HeightOfScreen (screen) - h - 2 * borderwidth;
  3133.  
  3134.   mw->menu.popped_up = True;
  3135.   XtConfigureWidget (XtParent (mw), x, y, w, h,
  3136.              XtParent (mw)->core.border_width);
  3137.   XtPopup (XtParent (mw), XtGrabExclusive);
  3138.   display_menu (mw, 0, False, NULL, NULL, NULL, NULL, NULL);
  3139.   if (!mw->menu.pointer_grabbed)
  3140.     {
  3141.       XtGrabPointer ((Widget)mw, False,
  3142.              (ButtonMotionMask | ButtonReleaseMask | ButtonPressMask),
  3143.              GrabModeAsync, GrabModeAsync,
  3144.              None, mw->menu.cursor_shape, event->time);
  3145.       mw->menu.pointer_grabbed = True;
  3146.     }
  3147.  
  3148.   mw->menu.windows [0].x = x + borderwidth;
  3149.   mw->menu.windows [0].y = y + borderwidth;
  3150.  
  3151.   handle_motion_event (mw, (XMotionEvent *) event, True);
  3152. }
  3153.  
  3154. /* #### unused */
  3155. #if 0
  3156. /*
  3157.  *    This is a horrible function which should not be needed.
  3158.  *    use it to put the resize method back the way the XlwMenu
  3159.  *    class initializer put it. Motif screws with this when
  3160.  *    the XlwMenu class gets instantiated.
  3161.  */
  3162. void
  3163. xlw_unmunge_class_resize (Widget w)
  3164. {
  3165.   if (w->core.widget_class->core_class.resize != XlwMenuResize)
  3166.     w->core.widget_class->core_class.resize = XlwMenuResize;
  3167. }
  3168. #endif /* 0 */
  3169.  
  3170.