XEmacs 21.2.12
[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, (XtPointer) "-*-helvetica-bold-r-*-*-*-120-*-*-*-*-iso8859-1"},
87 #else
88   {XtNfont,  XtCFont, XtRFontStruct, sizeof(XFontStruct *),
89      offset(menu.font), XtRString, (XtPointer) "XtDefaultFont"},
90 # ifdef USE_XFONTSET
91   {XtNfontSet,  XtCFontSet, XtRFontSet, sizeof(XFontSet),
92      offset(menu.font_set), XtRString, (XtPointer) "XtDefaultFontSet"},
93 # endif
94 #endif
95   {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
96      offset(menu.foreground), XtRString, (XtPointer) "XtDefaultForeground"},
97   {XtNbuttonForeground, XtCButtonForeground, XtRPixel, sizeof(Pixel),
98      offset(menu.button_foreground), XtRString, (XtPointer) "XtDefaultForeground"},
99   {XtNhighlightForeground, XtCHighlightForeground, XtRPixel, sizeof(Pixel),
100      offset(menu.highlight_foreground), XtRString, (XtPointer) "XtDefaultForeground"},
101   {XtNtitleForeground, XtCTitleForeground, XtRPixel, sizeof(Pixel),
102      offset(menu.title_foreground), XtRString, (XtPointer) "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           int int_ch = (int) (unsigned char) ch;
487           *out++ = firstp ? tolower (int_ch) : toupper (int_ch);
488           firstp = False;
489           while ((ch = massaged_resource_char[(unsigned char) *in++]) != '\0')
490             *out++ = ch;
491           if (!*(in-1))         /* Overshot the NULL byte? */
492             break;
493         }
494     }
495   *out = 0;
496
497 #ifdef PRINT_XLWMENU_RESOURCE_CONVERSIONS
498   printf ("! Emacs*XlwMenu.%s.labelString:\t%s\n", save_out, save_in);
499   printf (  "Emacs*XlwMenu.%s.labelString:\n",     save_out);
500 #endif
501 }
502
503 static XtResource
504 nameResource[] =
505 {
506   { "labelString", "LabelString", XtRString, sizeof(String),
507     0, XtRImmediate, 0 }
508 };
509
510 /*
511  *    This function looks through string searching for parameter
512  *    inserts of the form:
513  *    %[padding]1
514  *    padding is space (' ') or dash ('-') characters meaning
515  *    padding to the left or right of the inserted parameter.
516  *    In essence all %1 strings are replaced by value in the return
517  *    value (which the caller is expected to free).
518  *    %% means insert one % (like printf).
519  *    %1 means insert value.
520  *    %-1 means insert value followed by one space. The latter is
521  *    not inserted if value is a zero length string.
522  */
523 static char*
524 parameterize_string (CONST char *string, CONST char *value)
525 {
526   char *percent;
527   char *result;
528   unsigned int done = 0;
529   unsigned int ntimes;
530
531   if (!string)
532     {
533       result = XtMalloc(1);
534       result[0] = '\0';
535       return (result);
536     }
537
538   if (!value)
539     value = "";
540
541   for (ntimes = 1, result = (char *) string; (percent = strchr(result, '%'));
542        ntimes++)
543     result = &percent[1];
544
545   result = XtMalloc ((ntimes * strlen(value)) + strlen(string) + 4);
546   result[0] = '\0';
547
548   while ((percent = strchr(string, '%')))
549     {
550       unsigned int left_pad;
551       unsigned int right_pad;
552       char *p;
553
554       if (percent[1] == '%')
555         {       /* it's a real % */
556           strncat (result, string, 1 + percent - string); /* incl % */
557           string = &percent[2]; /* after the second '%' */
558           continue;             /* with the while() loop */
559         }
560
561       left_pad = 0;
562       right_pad = 0;
563
564       for (p = &percent[1]; /* test *p inside the loop */ ; p++)
565         {
566           if (*p == ' ')
567             {                   /* left pad */
568               left_pad++;
569             }
570           else if (*p == '-')
571             {                   /* right pad */
572               right_pad++;
573             }
574           else if (*p == '1')
575             {                   /* param and terminator */
576               strncat (result, string, percent - string);
577               if (value[0] != '\0')
578                 {
579                   unsigned int i;
580                   for (i = 0; i < left_pad; i++)
581                     strcat (result, " ");
582                   strcat (result, value);
583                   for (i = 0; i < right_pad; i++)
584                     strcat (result, " ");
585                 }
586               string = &p[1];   /* after the '1' */
587               done++;           /* no need to do old way */
588               break;            /* out of for() loop */
589             }
590           else
591             {                   /* bogus, copy the format as is */
592                                 /* out of for() loop */
593               strncat (result, string, 1 + p - string);
594               string = (*p ? &p[1] : p);
595               break;
596             }
597         }
598     }
599
600   /* Copy the tail of the string */
601   strcat (result, string);
602
603   /* If we have not processed a % string, and we have a value, tail it. */
604   if (!done && value[0] != '\0')
605     {
606       strcat (result, " ");
607       strcat (result, value);
608     }
609
610   return result;
611 }
612
613 #ifdef NEED_MOTIF
614
615 static XmString
616 resource_widget_value (XlwMenuWidget mw, widget_value *val)
617 {
618   if (!val->toolkit_data)
619     {
620       char *resourced_name = NULL;
621       char *converted_name, *str;
622       XmString complete_name;
623       char massaged_name [1024];
624
625       if (mw->menu.lookup_labels)
626         {
627           /* Convert value style name into resource style name.
628              eg: "Free Willy" becomes "freeWilly" */
629           massage_resource_name (val->name, massaged_name);
630
631           /* If we have a value (parameter) see if we can find a "Named"
632              resource. */
633           if (val->value)
634             {
635               char named_name[1024];
636               sprintf (named_name, "%sNamed", massaged_name);
637               XtGetSubresources ((Widget) mw,
638                                  (XtPointer) &resourced_name,
639                                  named_name, named_name,
640                                  nameResource, 1, NULL, 0);
641             }
642
643           /* If nothing yet, try to load from the massaged name. */
644           if (!resourced_name)
645             {
646               XtGetSubresources ((Widget) mw,
647                                  (XtPointer) &resourced_name,
648                                  massaged_name, massaged_name,
649                                  nameResource, 1, NULL, 0);
650             }
651         } /* if (mw->menu.lookup_labels) */
652
653       /* Still nothing yet, use the name as the value. */
654       if (!resourced_name)
655         resourced_name = val->name;
656
657       /* Parameterize the string. */
658       converted_name = parameterize_string (resourced_name, val->value);
659
660       /* nuke newline characters to prevent menubar screwups */
661       for ( str = converted_name ; *str ; str++ )
662         {
663           if (str[0] == '\n') str[0] = ' ';
664         }
665
666       /* Improve OSF's bottom line. */
667 #if (XmVersion >= 1002)
668       complete_name = XmStringCreateLocalized (converted_name);
669 #else
670       complete_name = XmStringCreateLtoR (converted_name,
671                                           XmSTRING_DEFAULT_CHARSET);
672 #endif
673       XtFree (converted_name);
674
675       val->toolkit_data = complete_name;
676       val->free_toolkit_data = True;
677     }
678   return (XmString) val->toolkit_data;
679 }
680
681 /* Unused */
682 #if 0
683 /* These two routines should be a seperate file..djw */
684 static char *
685 xlw_create_localized_string (Widget w,
686                              char *name,
687                              char **args,
688                              unsigned int nargs)
689 {
690   char *string = NULL;
691   char *arg = NULL;
692
693   if (nargs > 0)
694     arg = args[0];
695
696   XtGetSubresources (w,
697                      (XtPointer)&string,
698                      name,
699                      name,
700                      nameResource, 1,
701                      NULL, 0);
702
703   if (!string)
704     string = name;
705
706   return parameterize_string (string, arg);
707 }
708
709 static XmString
710 xlw_create_localized_xmstring (Widget w,
711                                char *name,
712                                char **args,
713                                unsigned int nargs)
714 {
715   char *   string = xlw_create_localized_string (w, name, args, nargs);
716   XmString xm_string = XmStringCreateLtoR (string, XmSTRING_DEFAULT_CHARSET);
717   XtFree (string);
718   return xm_string;
719 }
720 #endif /* 0 */
721
722 #else /* !Motif */
723
724 static char*
725 resource_widget_value (XlwMenuWidget mw, widget_value *val)
726 {
727   if (!val->toolkit_data)
728     {
729       char *resourced_name = NULL;
730       char *complete_name;
731       char massaged_name [1024];
732
733       if (mw->menu.lookup_labels)
734         {
735           massage_resource_name (val->name, massaged_name);
736
737           XtGetSubresources ((Widget) mw,
738                              (XtPointer) &resourced_name,
739                              massaged_name, massaged_name,
740                              nameResource, 1, NULL, 0);
741         }
742       if (!resourced_name)
743         resourced_name = val->name;
744
745       complete_name = parameterize_string (resourced_name, val->value);
746
747       val->toolkit_data = complete_name;
748       /* nuke newline characters to prevent menubar screwups */
749       for ( ; *complete_name ; complete_name++ )
750         {
751           if (complete_name[0] == '\n')
752             complete_name[0] = ' ';
753         }
754       val->free_toolkit_data = True;
755     }
756   return (char *) val->toolkit_data;
757 }
758
759 #endif /* !Motif */
760
761 /* Code for drawing strings. */
762 static void
763 string_draw (XlwMenuWidget mw,
764              Window window,
765              int x, int y,
766              GC gc,
767 #ifdef NEED_MOTIF
768              XmString string
769 #else
770              char *string
771 #endif
772 )
773 {
774 #ifdef NEED_MOTIF
775   XmStringDraw (XtDisplay (mw), window,
776                 mw->menu.font_list,
777                 string, gc,
778                 x, y,
779                 1000,   /* ???? width */
780                 XmALIGNMENT_BEGINNING,
781                 0, /* ???? layout_direction */
782                 0);
783 #else
784 # ifdef USE_XFONTSET
785   XmbDrawString (XtDisplay (mw), window, mw->menu.font_set, gc,
786                x, y + mw->menu.font_ascent, string, strlen (string));
787 # else
788   XDrawString (XtDisplay (mw), window, gc,
789                x, y + mw->menu.font_ascent, string, strlen (string));
790 # endif /* USE_XFONTSET */
791
792 #endif
793 }
794
795 static int
796 string_draw_range (
797         XlwMenuWidget mw,
798         Window window,
799         int x, int y,
800         GC gc,
801         char *string,
802         int start,
803         int end
804 )
805 {
806 #ifdef NEED_MOTIF
807         Dimension width, height;
808         XmString newstring;
809         int c;
810
811         if (end <= start)
812                 return 0;
813         c = string[end];
814         string[end] = '\0';
815         newstring = XmStringLtoRCreate (&string[start], XmFONTLIST_DEFAULT_TAG);
816         XmStringDraw (
817                 XtDisplay (mw), window,
818                 mw->menu.font_list,
819                 newstring, gc,
820                 x, y,
821                 1000,   /* ???? width */
822                 XmALIGNMENT_BEGINNING,
823                 0, /* ???? layout_direction */
824                 0
825         );
826         XmStringExtent (mw->menu.font_list, newstring, &width, &height);
827         XmStringFree (newstring);
828         string[end] = c;
829         return width;
830 #else
831 # ifdef USE_XFONTSET
832         XRectangle ri, rl;
833
834         if (end <= start)
835                 return 0;
836         XmbDrawString (
837                 XtDisplay (mw), window, mw->menu.font_set, gc,
838                 x, y + mw->menu.font_ascent, &string[start], end - start);
839         XmbTextExtents (
840                 mw->menu.font_set, &string[start], end - start, &ri, &rl);
841         return rl.width;
842 # else
843         XCharStruct xcs;
844         int drop;
845
846         if (end <= start)
847                 return 0;
848         XDrawString (
849                 XtDisplay (mw), window, gc,
850                 x, y + mw->menu.font_ascent, &string[start], end - start);
851         XTextExtents (
852                 mw->menu.font, &string[start], end - start,
853                 &drop, &drop, &drop, &xcs);
854         return xcs.width;
855 # endif
856 #endif
857 }
858
859 static void
860 string_draw_u (XlwMenuWidget mw,
861                Window window,
862                int x, int y,
863                GC gc,
864 #ifdef NEED_MOTIF
865                XmString string
866 #else
867                char *string
868 #endif
869 )
870 {
871   int i, s = 0;
872   char *chars;
873
874 #ifdef NEED_MOTIF
875   XmStringGetLtoR (string, XmFONTLIST_DEFAULT_TAG, &chars);
876 #else
877   chars = string;
878 #endif
879   for (i=0; chars[i]; ++i) {
880       if (chars[i] == '%' && chars[i+1] == '_') {
881           int w;
882
883           x += string_draw_range (mw, window, x, y, gc, chars, s, i);
884           w = string_draw_range (mw, window, x, y, gc, chars, i+2, i+3);
885
886           /* underline next character */
887           XDrawLine (XtDisplay (mw), window, gc, x - 1,
888                      y + mw->menu.font_ascent + 1,
889                      x + w - 1, y + mw->menu.font_ascent + 1 );
890           x += w;
891           s = i + 3;
892           i += 2;
893       }
894   }
895   x += string_draw_range (mw, window, x, y, gc, chars, s, i);
896 #ifdef NEED_MOTIF
897   XtFree (chars);
898 #endif
899 }
900
901 static void
902 binding_draw (XlwMenuWidget mw, Window w, int x, int y, GC gc, char *value)
903 {
904 #ifdef NEED_MOTIF
905   XmString xm_value = XmStringCreateLtoR(value, XmSTRING_DEFAULT_CHARSET);
906   string_draw (mw, w, x, y, gc, xm_value);
907   XmStringFree (xm_value);
908 #else
909   string_draw (mw, w, x, y, gc, value);
910 #endif
911 }
912
913 /* Low level code for drawing 3-D edges. */
914 static void
915 shadow_rectangle_draw (Display *dpy,
916                        Window window,
917                        GC top_gc,
918                        GC bottom_gc,
919                        int x, int y,
920                        unsigned int width,
921                        unsigned int height,
922                        unsigned int thickness)
923 {
924   XPoint points [4];
925
926   if (!thickness)
927     return;
928
929   points [0].x = x;
930   points [0].y = y;
931   points [1].x = x + width;
932   points [1].y = y;
933   points [2].x = x + width - thickness;
934   points [2].y = y + thickness;
935   points [3].x = x;
936   points [3].y = y + thickness;
937   XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
938   points [0].x = x;
939   points [0].y = y + thickness;
940   points [1].x = x;
941   points [1].y = y + height;
942   points [2].x = x + thickness;
943   points [2].y = y + height - thickness;
944   points [3].x = x + thickness;
945   points [3].y = y + thickness;
946   XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
947   points [0].x = x + width;
948   points [0].y = y;
949   points [1].x = x + width - thickness;
950   points [1].y = y + thickness;
951   points [2].x = x + width - thickness;
952   points [2].y = y + height - thickness;
953   points [3].x = x + width;
954   points [3].y = y + height - thickness;
955   XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
956   points [0].x = x;
957   points [0].y = y + height;
958   points [1].x = x + width;
959   points [1].y = y + height;
960   points [2].x = x + width;
961   points [2].y = y + height - thickness;
962   points [3].x = x + thickness;
963   points [3].y = y + height - thickness;
964   XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
965 }
966
967 typedef enum e_shadow_type
968 {
969   /* these are Motif compliant */
970   SHADOW_BACKGROUND,
971   SHADOW_OUT,
972   SHADOW_IN,
973   SHADOW_ETCHED_OUT,
974   SHADOW_ETCHED_IN,
975   SHADOW_ETCHED_OUT_DASH,
976   SHADOW_ETCHED_IN_DASH,
977   SHADOW_SINGLE_LINE,
978   SHADOW_DOUBLE_LINE,
979   SHADOW_SINGLE_DASHED_LINE,
980   SHADOW_DOUBLE_DASHED_LINE,
981   SHADOW_NO_LINE,
982   /* these are all non-Motif */
983   SHADOW_DOUBLE_ETCHED_OUT,
984   SHADOW_DOUBLE_ETCHED_IN,
985   SHADOW_DOUBLE_ETCHED_OUT_DASH,
986   SHADOW_DOUBLE_ETCHED_IN_DASH
987 } shadow_type;
988
989 static void
990 shadow_draw (XlwMenuWidget mw,
991              Window window,
992              int x, int y,
993              unsigned int width,
994              unsigned int height,
995              shadow_type type)
996 {
997   Display *dpy = XtDisplay (mw);
998   GC top_gc;
999   GC bottom_gc;
1000   int thickness = mw->menu.shadow_thickness;
1001 #if 0
1002   XPoint points [4];
1003 #endif /* 0 */
1004   Boolean etched = False;
1005
1006   switch (type)
1007     {
1008     case SHADOW_BACKGROUND:
1009       top_gc = bottom_gc = mw->menu.background_gc;
1010       break;
1011     case SHADOW_ETCHED_IN:
1012       top_gc = mw->menu.shadow_bottom_gc;
1013       bottom_gc = mw->menu.shadow_top_gc;
1014       etched = True;
1015       break;
1016     case SHADOW_ETCHED_OUT:
1017       top_gc = mw->menu.shadow_top_gc;
1018       bottom_gc = mw->menu.shadow_bottom_gc;
1019       etched = True;
1020       break;
1021     case SHADOW_IN:
1022       top_gc = mw->menu.shadow_bottom_gc;
1023       bottom_gc = mw->menu.shadow_top_gc;
1024       break;
1025     case SHADOW_OUT:
1026     default:
1027       top_gc = mw->menu.shadow_top_gc;
1028       bottom_gc = mw->menu.shadow_bottom_gc;
1029       break;
1030     }
1031
1032   if (etched)
1033     {
1034       unsigned int half = thickness/2;
1035       shadow_rectangle_draw (dpy,
1036                              window,
1037                              top_gc,
1038                              top_gc,
1039                              x, y,
1040                              width - half, height - half,
1041                              thickness - half);
1042       shadow_rectangle_draw (dpy,
1043                              window,
1044                              bottom_gc,
1045                              bottom_gc,
1046                              x + half, y + half,
1047                              width - half , height - half,
1048                              half);
1049     }
1050   else
1051     {
1052       shadow_rectangle_draw (dpy,
1053                              window,
1054                              top_gc,
1055                              bottom_gc,
1056                              x, y,
1057                              width, height,
1058                              thickness);
1059     }
1060 }
1061
1062 static void
1063 arrow_decoration_draw (XlwMenuWidget mw,
1064                        Window window,
1065                        int x, int y,
1066                        unsigned int width,
1067                        Boolean raised)
1068 {
1069   Display *dpy = XtDisplay (mw);
1070   GC top_gc;
1071   GC bottom_gc;
1072   GC select_gc;
1073   int thickness = mw->menu.shadow_thickness;
1074   XPoint points [4];
1075   int half_width;
1076   int length = (int)((double)width * 0.87);
1077   int thick_med = (int)((double)thickness * 1.73);
1078
1079   if (width & 0x1)
1080     half_width = width/2 + 1;
1081   else
1082     half_width = width/2;
1083
1084   select_gc = mw->menu.background_gc;
1085
1086   if (raised)
1087     {
1088       top_gc    = mw->menu.shadow_bottom_gc;
1089       bottom_gc = mw->menu.shadow_top_gc;
1090     }
1091   else
1092     {
1093       top_gc    = mw->menu.shadow_top_gc;
1094       bottom_gc = mw->menu.shadow_bottom_gc;
1095     }
1096
1097   /* Fill internal area.  We do this first so that the borders have a
1098      nice sharp edge.  */
1099   points [0].x = x + thickness;
1100   points [0].y = y + thickness;
1101   points [1].x = x + length - thickness;
1102   points [1].y = y + half_width;
1103   points [2].x = x + length - thickness;
1104   points [2].y = y + half_width + thickness;
1105   points [3].x = x + thickness;
1106   points [3].y = y + width - thickness;
1107
1108   XFillPolygon (dpy,
1109                 window,
1110                 select_gc,
1111                 points,
1112                 4,
1113                 Convex,
1114                 CoordModeOrigin);
1115
1116   /* left border */
1117   points [0].x = x;
1118   points [0].y = y;
1119   points [1].x = x + thickness;
1120   points [1].y = y + thick_med;
1121   points [2].x = x + thickness;
1122   points [2].y = y + width - thick_med;
1123   points [3].x = x;
1124   points [3].y = y + width;
1125
1126   XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
1127
1128   /* top border */
1129   points [0].x = x;
1130   points [0].y = y + width;
1131   points [1].x = x + length;
1132   points [1].y = y + half_width;
1133   points [2].x = x + length - (thickness + thickness);
1134   points [2].y = y + half_width;
1135   points [3].x = x + thickness;
1136   points [3].y = y + width - thick_med;
1137
1138   XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
1139
1140   /* bottom shadow */
1141   points [0].x = x;
1142   points [0].y = y;
1143   points [1].x = x + length;
1144   points [1].y = y + half_width;
1145   points [2].x = x + length - (thickness + thickness);
1146   points [2].y = y + half_width;
1147   points [3].x = x + thickness;
1148   points [3].y = y + thick_med;
1149
1150   XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
1151 }
1152
1153 static void
1154 toggle_decoration_draw (XlwMenuWidget mw,
1155                         Window window,
1156                         int x, int y,
1157                         unsigned int width,
1158                         Boolean set)
1159 {
1160   Display *dpy = XtDisplay (mw);
1161   int thickness = mw->menu.shadow_thickness;
1162   shadow_type type;
1163   GC select_gc = mw->menu.select_gc;
1164
1165   if (set)
1166     type = SHADOW_IN;
1167   else
1168     type = SHADOW_OUT;
1169
1170   /* Fill internal area. */
1171   if (set)
1172     XFillRectangle (dpy,
1173                     window,
1174                     select_gc,
1175                     x + thickness,
1176                     y + thickness,
1177                     width - (2*thickness),
1178                     width - (2*thickness));
1179
1180   shadow_draw (mw, window, x, y, width, width, type);
1181 }
1182
1183 static void
1184 radio_decoration_draw (XlwMenuWidget mw,
1185                        Window window,
1186                        int x, int y,
1187                        unsigned int width,
1188                        Boolean enabled)
1189 {
1190   Display *dpy = XtDisplay (mw);
1191   GC top_gc;
1192   GC bottom_gc;
1193   GC select_gc = mw->menu.select_gc;
1194   int thickness = mw->menu.shadow_thickness;
1195   XPoint points[6];
1196   int half_width;
1197 #if 0
1198   int npoints;
1199 #endif /* 0 */
1200
1201   if (width & 0x1)
1202     width++;
1203
1204   half_width = width/2;
1205
1206   if (enabled)
1207     {
1208       top_gc    = mw->menu.shadow_bottom_gc;
1209       bottom_gc = mw->menu.shadow_top_gc;
1210     }
1211   else
1212     {
1213       top_gc    = mw->menu.shadow_top_gc;
1214       bottom_gc = mw->menu.shadow_bottom_gc;
1215     }
1216
1217 #if 1
1218   /*  Draw the bottom first, just in case the regions overlap.
1219       The top should cast the longer shadow. */
1220   points [0].x = x; /* left corner */
1221   points [0].y = y + half_width;
1222   points [1].x = x + half_width; /* bottom corner */
1223   points [1].y = y + width;
1224   points [2].x = x + half_width; /* bottom inside corner */
1225   points [2].y = y + width - thickness;
1226   points [3].x = x + thickness; /* left inside corner */
1227   points [3].y = y + half_width;
1228
1229   XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
1230
1231   points [0].x = x + half_width; /* bottom corner */
1232   points [0].y = y + width;
1233   points [1].x = x + width; /* right corner */
1234   points [1].y = y + half_width;
1235   points [2].x = x + width - thickness; /* right inside corner */
1236   points [2].y = y + half_width;
1237   points [3].x = x + half_width; /* bottom inside corner */
1238   points [3].y = y + width - thickness;
1239
1240   XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
1241
1242   points [0].x = x; /* left corner */
1243   points [0].y = y + half_width;
1244   points [1].x = x + half_width; /* top corner */
1245   points [1].y = y;
1246   points [2].x = x + half_width; /* top inside corner */
1247   points [2].y = y + thickness;
1248   points [3].x = x + thickness; /* left inside corner */
1249   points [3].y = y + half_width;
1250
1251   XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
1252
1253   points [0].x = x + half_width; /* top corner */
1254   points [0].y = y;
1255   points [1].x = x + width; /* right corner */
1256   points [1].y = y + half_width;
1257   points [2].x = x + width - thickness; /* right inside corner */
1258   points [2].y = y + half_width;
1259   points [3].x = x + half_width; /* top inside corner */
1260   points [3].y = y + thickness;
1261
1262   XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
1263 #else
1264   /* Draw the bottom first, just in case the regions overlap.
1265      The top should cast the longer shadow. */
1266   npoints = 0;
1267   points [npoints].x = x; /* left corner */
1268   points [npoints++].y = y + half_width;
1269   points [npoints].x = x + half_width; /* bottom corner */
1270   points [npoints++].y = y + width;
1271   points [npoints].x = x + width; /* right corner */
1272   points [npoints++].y = y + half_width;
1273   points [npoints].x = x + width - thickness; /* right inside corner */
1274   points [npoints++].y = y + half_width;
1275   points [npoints].x = x + half_width; /* bottom inside corner */
1276   points [npoints++].y = y + width - thickness;
1277   points [npoints].x = x + thickness; /* left inside corner */
1278   points [npoints++].y = y + half_width;
1279
1280   XFillPolygon (dpy, window, bottom_gc,
1281                 points, npoints, Nonconvex, CoordModeOrigin);
1282
1283   npoints = 0;
1284
1285   points [npoints].x = x; /* left corner */
1286   points [npoints++].y = y + half_width;
1287   points [npoints].x = x + half_width; /* top corner */
1288   points [npoints++].y = y;
1289   points [npoints].x = x + width; /* right corner */
1290   points [npoints++].y = y + half_width;
1291   points [npoints].x = x + width - thickness; /* right inside corner */
1292   points [npoints++].y = y + half_width;
1293   points [npoints].x = x + half_width; /* top inside corner */
1294   points [npoints++].y = y + thickness;
1295   points [npoints].x = x + thickness; /* left inside corner */
1296   points [npoints++].y = y + half_width;
1297
1298   XFillPolygon (dpy, window, top_gc, points, npoints, Nonconvex,
1299                 CoordModeOrigin);
1300 #endif
1301
1302
1303   /* Fill internal area. */
1304   if (enabled)
1305     {
1306       points [0].x = x + thickness;
1307       points [0].y = y + half_width;
1308       points [1].x = x + half_width;
1309       points [1].y = y + thickness;
1310       points [2].x = x + width - thickness;
1311       points [2].y = y + half_width;
1312       points [3].x = x + half_width;
1313       points [3].y = y + width - thickness;
1314       XFillPolygon (dpy,
1315                     window,
1316                     select_gc,
1317                     points,
1318                     4,
1319                     Convex,
1320                     CoordModeOrigin);
1321     }
1322 }
1323
1324 static void
1325 separator_decoration_draw (XlwMenuWidget mw,
1326                            Window window,
1327                            int x, int y,
1328                            unsigned int width,
1329                            Boolean vertical,
1330                            shadow_type type)
1331 {
1332   Display *dpy = XtDisplay (mw);
1333   GC top_gc;
1334   GC bottom_gc;
1335   unsigned int offset = 0;
1336   unsigned int num_separators = 1;
1337   unsigned int top_line_thickness = 0;
1338   unsigned int bottom_line_thickness = 0;
1339   Boolean dashed = False;
1340
1341   switch (type)
1342     {
1343     case SHADOW_NO_LINE: /* nothing to do */
1344       return;
1345     case SHADOW_DOUBLE_LINE:
1346       num_separators = 2;
1347     case SHADOW_SINGLE_LINE:
1348       top_gc = bottom_gc = mw->menu.foreground_gc;
1349       top_line_thickness = 1;
1350       break;
1351     case SHADOW_DOUBLE_DASHED_LINE:
1352       num_separators = 2;
1353     case SHADOW_SINGLE_DASHED_LINE:
1354       top_gc = bottom_gc = mw->menu.foreground_gc;
1355       top_line_thickness = 1;
1356       dashed = True;
1357       break;
1358     case SHADOW_DOUBLE_ETCHED_OUT_DASH:
1359       num_separators = 2;
1360     case SHADOW_ETCHED_OUT_DASH:
1361       top_gc = mw->menu.shadow_top_gc;
1362       bottom_gc = mw->menu.shadow_bottom_gc;
1363       top_line_thickness = mw->menu.shadow_thickness/2;
1364       bottom_line_thickness = mw->menu.shadow_thickness - top_line_thickness;
1365       dashed = True;
1366       break;
1367     case SHADOW_DOUBLE_ETCHED_IN_DASH:
1368       num_separators = 2;
1369     case SHADOW_ETCHED_IN_DASH:
1370       top_gc = mw->menu.shadow_bottom_gc;
1371       bottom_gc = mw->menu.shadow_top_gc;
1372       top_line_thickness = mw->menu.shadow_thickness/2;
1373       bottom_line_thickness = mw->menu.shadow_thickness - top_line_thickness;
1374       dashed = True;
1375       break;
1376     case SHADOW_DOUBLE_ETCHED_OUT:
1377       num_separators = 2;
1378     case SHADOW_ETCHED_OUT:
1379       top_gc = mw->menu.shadow_top_gc;
1380       bottom_gc = mw->menu.shadow_bottom_gc;
1381       top_line_thickness = mw->menu.shadow_thickness/2;
1382       bottom_line_thickness = mw->menu.shadow_thickness - top_line_thickness;
1383       break;
1384     case SHADOW_DOUBLE_ETCHED_IN:
1385       num_separators = 2;
1386     case SHADOW_ETCHED_IN:
1387     default:
1388       top_gc = mw->menu.shadow_bottom_gc;
1389       bottom_gc = mw->menu.shadow_top_gc;
1390       top_line_thickness = mw->menu.shadow_thickness/2;
1391       bottom_line_thickness = mw->menu.shadow_thickness - top_line_thickness;
1392       break;
1393     }
1394
1395   if (dashed)
1396     {
1397       XGCValues values;
1398       values.line_style = LineOnOffDash;
1399       if (top_line_thickness > 0)
1400         XChangeGC (dpy, top_gc, GCLineStyle, &values);
1401       if (bottom_line_thickness > 0 && bottom_gc != top_gc)
1402         XChangeGC (dpy, bottom_gc, GCLineStyle, &values);
1403     }
1404
1405   while (num_separators--)
1406     {
1407       unsigned int i;
1408       for (i = 0; i < top_line_thickness; i++)
1409         XDrawLine (dpy, window, top_gc, x, y + i, x + width, y + i);
1410
1411       for (i = 0; i < bottom_line_thickness; i++)
1412         XDrawLine (dpy, window, bottom_gc,
1413                    x, y + top_line_thickness + offset + i,
1414                    x + width, y + top_line_thickness + offset + i);
1415       y += (top_line_thickness + offset + bottom_line_thickness + 1);
1416     }
1417
1418   if (dashed)
1419     {
1420       XGCValues values;
1421       values.line_style = LineSolid;
1422       if (top_line_thickness > 0)
1423         XChangeGC (dpy, top_gc, GCLineStyle, &values);
1424       if (bottom_line_thickness > 0 && bottom_gc != top_gc)
1425         XChangeGC (dpy, bottom_gc, GCLineStyle, &values);
1426     }
1427 }
1428
1429 #define SLOPPY_TYPES 0          /* 0=off, 1=error check, 2=easy to please */
1430 #if SLOPPY_TYPES
1431 #if SLOPPY_TYPES < 2
1432
1433 static char *wv_types[] =
1434 {
1435   "UNSPECIFIED",
1436   "BUTTON",
1437   "TOGGLE",
1438   "RADIO",
1439   "TEXT",
1440   "SEPARATOR",
1441   "CASCADE",
1442   "PUSHRIGHT",
1443   "INCREMENTAL"
1444 };
1445
1446 static void
1447 print_widget_value (widget_value *wv, int just_one, int depth)
1448 {
1449   char d [200];
1450   int i;
1451   for (i = 0; i < depth; i++)
1452     d[i] = ' ';
1453   d[depth]=0;
1454   if (!wv)
1455     {
1456       printf ("%s(null widget value pointer)\n", d);
1457       return;
1458     }
1459   printf ("%stype:    %s\n", d, wv_types [wv->type]);
1460 #if 0
1461   printf ("%sname:    %s\n", d, (wv->name ? wv->name : "(null)"));
1462 #else
1463   if (wv->name)  printf ("%sname:    %s\n", d, wv->name);
1464 #endif
1465   if (wv->value) printf ("%svalue:   %s\n", d, wv->value);
1466   if (wv->key)   printf ("%skey:     %s\n", d, wv->key);
1467   printf ("%senabled: %d\n", d, wv->enabled);
1468   if (wv->contents)
1469     {
1470       printf ("\n%scontents: \n", d);
1471       print_widget_value (wv->contents, 0, depth + 5);
1472     }
1473   if (!just_one && wv->next)
1474     {
1475       printf ("\n");
1476       print_widget_value (wv->next, 0, depth);
1477     }
1478 }
1479 #endif /* SLOPPY_TYPES < 2 */
1480
1481 static Boolean
1482 all_dashes_p (char *s)
1483 {
1484   char *p;
1485   if (!s || s[0] == '\0')
1486     return False;
1487   for (p = s; *p == '-'; p++);
1488
1489   if (*p == '!' || *p == '\0')
1490     return True;
1491   return False;
1492 }
1493 #endif /* SLOPPY_TYPES */
1494
1495 static widget_value_type
1496 menu_item_type (widget_value *val)
1497 {
1498   if (val->type != UNSPECIFIED_TYPE)
1499     return val->type;
1500 #if SLOPPY_TYPES
1501   else if (all_dashes_p (val->name))
1502     return SEPARATOR_TYPE;
1503   else if (val->name && val->name[0] == '\0') /* push right */
1504     return PUSHRIGHT_TYPE;
1505   else if (val->contents) /* cascade */
1506     return CASCADE_TYPE;
1507   else if (val->call_data) /* push button */
1508     return BUTTON_TYPE;
1509   else
1510     return TEXT_TYPE;
1511 #else
1512   else 
1513     abort();
1514   return UNSPECIFIED_TYPE; /* Not reached */
1515 #endif
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