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