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