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