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