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