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