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