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