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