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