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