1 /* Implements a lightweight menubar widget.
2 Copyright (C) 1992, 1993, 1994 Lucid, Inc.
3 Copyright (C) 1995 Tinker Systems and INS Engineering Corp.
5 This file is part of the Lucid Widget Library.
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)
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.
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. */
22 /* Created by devin@lucid.com */
29 #include <sys/types.h>
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>
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 */
50 #ifdef USE_DEBUG_MALLOC
54 /* simple, naieve integer maximum */
56 #define max(a,b) ((a)>(b)?(a):(b))
60 xlwMenuTranslations [] =
61 "<BtnDown>: start()\n\
62 <BtnMotion>: drag()\n\
66 extern Widget lw_menubar_widget;
68 #define offset(field) XtOffset(XlwMenuWidget, field)
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"},
88 {XtNfont, XtCFont, XtRFontStruct, sizeof(XFontStruct *),
89 offset(menu.font), XtRString, (XtPointer) "XtDefaultFont"},
91 {XtNfontSet, XtCFontSet, XtRFontSet, sizeof(XFontSet),
92 offset(menu.font_set), XtRString, (XtPointer) "XtDefaultFontSet"},
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},
114 {XmNshadowThickness, XmCShadowThickness, XmRHorizontalDimension,
115 sizeof (Dimension), offset (menu.shadow_thickness),
116 XtRImmediate, (XtPointer) 2},
118 {XmNshadowThickness, XmCShadowThickness, XtRDimension,
119 sizeof (Dimension), offset (menu.shadow_thickness),
120 XtRImmediate, (XtPointer) 2},
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},
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},
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,
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);
167 static XFontStruct *default_font_of_font_list (XmFontList);
171 xlwMenuActionsList [] =
178 #define SuperClass ((CoreWidgetClass)&coreClassRec)
180 XlwMenuClassRec xlwMenuClassRec =
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 */
215 }, /* XlwMenuClass fields initialization */
221 WidgetClass xlwMenuWidgetClass = (WidgetClass) &xlwMenuClassRec;
223 extern int lw_menu_accelerate;
226 #if 0 /* Apparently not used anywhere */
229 safe_strdup (char *s)
233 result = (char *) malloc (strlen (s) + 1);
242 /* Replacement for XAllocColor() that tries to return the nearest
243 available color if the colormap is full. From FSF Emacs. */
246 allocate_nearest_color (Display *display, Colormap screen_colormap,
249 int status = XAllocColor (display, screen_colormap, color_def);
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. */
260 unsigned long nearest_delta = ULONG_MAX;
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);
267 for (x = 0; x < no_cells; x++)
270 XQueryColors (display, screen_colormap, cells, no_cells);
272 for (nearest = 0, x = 0; x < no_cells; x++)
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;
279 if (delta < nearest_delta)
282 nearest_delta = delta;
285 color_def->red = cells[nearest].red;
286 color_def->green = cells[nearest].green;
287 color_def->blue = cells[nearest].blue;
289 return XAllocColor (display, screen_colormap, color_def);
294 push_new_stack (XlwMenuWidget mw, widget_value *val)
296 if (!mw->menu.new_stack)
298 mw->menu.new_stack_length = 10;
300 (widget_value**)XtCalloc (mw->menu.new_stack_length,
301 sizeof (widget_value*));
303 else if (mw->menu.new_depth == mw->menu.new_stack_length)
305 mw->menu.new_stack_length *= 2;
307 (widget_value**)XtRealloc ((char *)mw->menu.new_stack,
308 mw->menu.new_stack_length *
309 sizeof (widget_value*));
311 mw->menu.new_stack [mw->menu.new_depth++] = val;
315 pop_new_stack_if_no_contents (XlwMenuWidget mw)
317 if (mw->menu.new_depth &&
318 !mw->menu.new_stack [mw->menu.new_depth - 1]->contents)
319 mw->menu.new_depth -= 1;
323 make_old_stack_space (XlwMenuWidget mw, int n)
325 if (!mw->menu.old_stack)
327 mw->menu.old_stack_length = max (10, n);
329 (widget_value**)XtCalloc (mw->menu.old_stack_length,
330 sizeof (widget_value*));
332 else if (mw->menu.old_stack_length < n)
334 while (mw->menu.old_stack_length < n)
335 mw->menu.old_stack_length *= 2;
338 (widget_value**)XtRealloc ((char *)mw->menu.old_stack,
339 mw->menu.old_stack_length *
340 sizeof (widget_value*));
345 close_to_reference_time (Widget w, Time reference_time, XEvent *ev)
349 (ev->xbutton.time - reference_time < XtGetMultiClickTime (XtDisplay (w)));
354 string_width (XlwMenuWidget mw,
363 Dimension width, height;
364 XmStringExtent (mw->menu.font_list, s, &width, &height);
369 XmbTextExtents (mw->menu.font_set, s, strlen (s), &ri, &rl);
374 XTextExtents (mw->menu.font, s, strlen (s), &drop, &drop, &drop, &xcs);
376 # endif /* USE_XFONTSET */
380 static char massaged_resource_char[256];
383 initialize_massaged_resource_char (void)
386 for (j = 0; j < (int) sizeof (massaged_resource_char); j++)
388 if ((j >= 'a' && j <= 'z') ||
389 (j >= 'A' && j <= 'Z') ||
390 (j >= '0' && j <= '9') ||
393 massaged_resource_char[j] = (char) j;
395 massaged_resource_char ['_'] = '_';
396 massaged_resource_char ['+'] = 'P'; /* Convert C++ to cPP */
397 massaged_resource_char ['.'] = '_'; /* Convert Buffers... to buffers___ */
401 string_width_u (XlwMenuWidget mw,
410 Dimension width, height;
415 # else /* ! USE_XFONTSET */
427 if (!XmStringGetLtoR (string, XmFONTLIST_DEFAULT_TAG, &chars))
432 charslength = strlen (chars);
433 newchars = (char *) alloca (charslength + 1);
435 for (i = j = 0; chars[i] && (j < charslength); i++)
436 if (chars[i]=='%'&&chars[i+1]=='_')
439 newchars[j++] = chars[i];
443 newstring = XmStringLtoRCreate (newchars, XmFONTLIST_DEFAULT_TAG);
444 XmStringExtent (mw->menu.font_list, newstring, &width, &height);
445 XmStringFree (newstring);
450 XmbTextExtents (mw->menu.font_set, newchars, j, &ri, &rl);
452 # else /* ! USE_XFONTSET */
453 XTextExtents (mw->menu.font, newchars, j, &drop, &drop, &drop, &xcs);
455 # endif /* USE_XFONTSET */
460 massage_resource_name (const char *in, char *out)
462 /* Turn a random string into something suitable for using as a resource.
465 "Kill Buffer" -> "killBuffer"
466 "Find File..." -> "findFile___"
467 "Search and Replace..." -> "searchAndReplace___"
468 "C++ Mode Commands" -> "cppModeCommands"
470 Valid characters in a resource NAME component are: a-zA-Z0-9_
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;
479 Boolean firstp = True;
482 if (*in == '%' && *(in + 1) == '_')
488 if (*in == '%' && *(in + 1) == '%')
490 ch = massaged_resource_char[(unsigned char) *in++];
493 int int_ch = (int) (unsigned char) ch;
494 *out++ = firstp ? tolower (int_ch) : toupper (int_ch);
496 while ((ch = massaged_resource_char[(unsigned char) *in++])
499 if (!*(in-1)) /* Overshot the NULL byte? */
506 #ifdef PRINT_XLWMENU_RESOURCE_CONVERSIONS
507 printf ("! Emacs*XlwMenu.%s.labelString:\t%s\n", save_out, save_in);
508 printf ( "Emacs*XlwMenu.%s.labelString:\n", save_out);
515 { "labelString", "LabelString", XtRString, sizeof(String),
520 * This function looks through string searching for parameter
521 * inserts of the form:
523 * padding is space (' ') or dash ('-') characters meaning
524 * padding to the left or right of the inserted parameter.
525 * In essence all %1 strings are replaced by value in the return
526 * value (which the caller is expected to free).
527 * %% means insert one % (like printf).
528 * %1 means insert value.
529 * %-1 means insert value followed by one space. The latter is
530 * not inserted if value is a zero length string.
533 parameterize_string (const char *string, const char *value)
537 unsigned int done = 0;
542 result = XtMalloc(1);
550 for (ntimes = 1, result = (char *) string; (percent = strchr(result, '%'));
552 result = &percent[1];
554 result = XtMalloc ((ntimes * strlen(value)) + strlen(string) + 4);
557 while ((percent = strchr(string, '%')))
559 unsigned int left_pad;
560 unsigned int right_pad;
563 if (percent[1] == '%')
564 { /* it's a real % */
565 strncat (result, string, 1 + percent - string); /* incl % */
566 string = &percent[2]; /* after the second '%' */
567 continue; /* with the while() loop */
573 for (p = &percent[1]; /* test *p inside the loop */ ; p++)
584 { /* param and terminator */
585 strncat (result, string, percent - string);
586 if (value[0] != '\0')
589 for (i = 0; i < left_pad; i++)
590 strcat (result, " ");
591 strcat (result, value);
592 for (i = 0; i < right_pad; i++)
593 strcat (result, " ");
595 string = &p[1]; /* after the '1' */
596 done++; /* no need to do old way */
597 break; /* out of for() loop */
600 { /* bogus, copy the format as is */
601 /* out of for() loop */
602 strncat (result, string, 1 + p - string);
603 string = (*p ? &p[1] : p);
609 /* Copy the tail of the string */
610 strcat (result, string);
612 /* If we have not processed a % string, and we have a value, tail it. */
613 if (!done && value[0] != '\0')
615 strcat (result, " ");
616 strcat (result, value);
625 resource_widget_value (XlwMenuWidget mw, widget_value *val)
627 if (!val->toolkit_data)
629 char *resourced_name = NULL;
630 char *converted_name, *str;
631 XmString complete_name;
632 char massaged_name [1024];
634 if (mw->menu.lookup_labels)
636 /* Convert value style name into resource style name.
637 eg: "Free Willy" becomes "freeWilly" */
638 massage_resource_name (val->name, massaged_name);
640 /* If we have a value (parameter) see if we can find a "Named"
644 char named_name[1024];
645 sprintf (named_name, "%sNamed", massaged_name);
646 XtGetSubresources ((Widget) mw,
647 (XtPointer) &resourced_name,
648 named_name, named_name,
649 nameResource, 1, NULL, 0);
652 /* If nothing yet, try to load from the massaged name. */
655 XtGetSubresources ((Widget) mw,
656 (XtPointer) &resourced_name,
657 massaged_name, massaged_name,
658 nameResource, 1, NULL, 0);
660 } /* if (mw->menu.lookup_labels) */
662 /* Still nothing yet, use the name as the value. */
664 resourced_name = val->name;
666 /* Parameterize the string. */
667 converted_name = parameterize_string (resourced_name, val->value);
669 /* nuke newline characters to prevent menubar screwups */
670 for ( str = converted_name ; *str ; str++ )
672 if (str[0] == '\n') str[0] = ' ';
675 /* Improve OSF's bottom line. */
676 #if (XmVersion >= 1002)
677 complete_name = XmStringCreateLocalized (converted_name);
679 complete_name = XmStringCreateLtoR (converted_name,
680 XmSTRING_DEFAULT_CHARSET);
682 XtFree (converted_name);
684 val->toolkit_data = complete_name;
685 val->free_toolkit_data = True;
687 return (XmString) val->toolkit_data;
692 /* These two routines should be a seperate file..djw */
694 xlw_create_localized_string (Widget w,
705 XtGetSubresources (w,
715 return parameterize_string (string, arg);
719 xlw_create_localized_xmstring (Widget w,
724 char * string = xlw_create_localized_string (w, name, args, nargs);
725 XmString xm_string = XmStringCreateLtoR (string, XmSTRING_DEFAULT_CHARSET);
734 resource_widget_value (XlwMenuWidget mw, widget_value *val)
736 if (!val->toolkit_data)
738 char *resourced_name = NULL;
740 char massaged_name [1024];
742 if (mw->menu.lookup_labels)
744 massage_resource_name (val->name, massaged_name);
746 XtGetSubresources ((Widget) mw,
747 (XtPointer) &resourced_name,
748 massaged_name, massaged_name,
749 nameResource, 1, NULL, 0);
752 resourced_name = val->name;
754 complete_name = parameterize_string (resourced_name, val->value);
756 val->toolkit_data = complete_name;
757 /* nuke newline characters to prevent menubar screwups */
758 for ( ; *complete_name ; complete_name++ )
760 if (complete_name[0] == '\n')
761 complete_name[0] = ' ';
763 val->free_toolkit_data = True;
765 return (char *) val->toolkit_data;
770 /* Code for drawing strings. */
772 string_draw (XlwMenuWidget mw,
784 XmStringDraw (XtDisplay (mw), window,
788 1000, /* ???? width */
789 XmALIGNMENT_BEGINNING,
790 0, /* ???? layout_direction */
794 XmbDrawString (XtDisplay (mw), window, mw->menu.font_set, gc,
795 x, y + mw->menu.font_ascent, string, strlen (string));
797 XDrawString (XtDisplay (mw), window, gc,
798 x, y + mw->menu.font_ascent, string, strlen (string));
799 # endif /* USE_XFONTSET */
816 Dimension width, height;
824 newstring = XmStringLtoRCreate (&string[start], XmFONTLIST_DEFAULT_TAG);
826 XtDisplay (mw), window,
830 1000, /* ???? width */
831 XmALIGNMENT_BEGINNING,
832 0, /* ???? layout_direction */
835 XmStringExtent (mw->menu.font_list, newstring, &width, &height);
836 XmStringFree (newstring);
846 XtDisplay (mw), window, mw->menu.font_set, gc,
847 x, y + mw->menu.font_ascent, &string[start], end - start);
849 mw->menu.font_set, &string[start], end - start, &ri, &rl);
858 XtDisplay (mw), window, gc,
859 x, y + mw->menu.font_ascent, &string[start], end - start);
861 mw->menu.font, &string[start], end - start,
862 &drop, &drop, &drop, &xcs);
869 string_draw_u (XlwMenuWidget mw,
885 if (!XmStringGetLtoR (string, XmFONTLIST_DEFAULT_TAG, &chars))
890 for (i=0; chars[i]; ++i) {
891 if (chars[i] == '%' && chars[i+1] == '_') {
894 x += string_draw_range (mw, window, x, y, gc, chars, s, i);
895 w = string_draw_range (mw, window, x, y, gc, chars, i+2, i+3);
897 /* underline next character */
898 XDrawLine (XtDisplay (mw), window, gc, x - 1,
899 y + mw->menu.font_ascent + 1,
900 x + w - 1, y + mw->menu.font_ascent + 1 );
906 x += string_draw_range (mw, window, x, y, gc, chars, s, i);
913 binding_draw (XlwMenuWidget mw, Window w, int x, int y, GC gc, char *value)
916 XmString xm_value = XmStringCreateLtoR(value, XmSTRING_DEFAULT_CHARSET);
917 string_draw (mw, w, x, y, gc, xm_value);
918 XmStringFree (xm_value);
920 string_draw (mw, w, x, y, gc, value);
924 /* Low level code for drawing 3-D edges. */
926 shadow_rectangle_draw (Display *dpy,
933 unsigned int thickness)
942 points [1].x = x + width;
944 points [2].x = x + width - thickness;
945 points [2].y = y + thickness;
947 points [3].y = y + thickness;
948 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
950 points [0].y = y + thickness;
952 points [1].y = y + height;
953 points [2].x = x + thickness;
954 points [2].y = y + height - thickness;
955 points [3].x = x + thickness;
956 points [3].y = y + thickness;
957 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
958 points [0].x = x + width;
960 points [1].x = x + width - thickness;
961 points [1].y = y + thickness;
962 points [2].x = x + width - thickness;
963 points [2].y = y + height - thickness;
964 points [3].x = x + width;
965 points [3].y = y + height - thickness;
966 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
968 points [0].y = y + height;
969 points [1].x = x + width;
970 points [1].y = y + height;
971 points [2].x = x + width;
972 points [2].y = y + height - thickness;
973 points [3].x = x + thickness;
974 points [3].y = y + height - thickness;
975 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
978 typedef enum e_shadow_type
980 /* these are Motif compliant */
986 SHADOW_ETCHED_OUT_DASH,
987 SHADOW_ETCHED_IN_DASH,
990 SHADOW_SINGLE_DASHED_LINE,
991 SHADOW_DOUBLE_DASHED_LINE,
993 /* these are all non-Motif */
994 SHADOW_DOUBLE_ETCHED_OUT,
995 SHADOW_DOUBLE_ETCHED_IN,
996 SHADOW_DOUBLE_ETCHED_OUT_DASH,
997 SHADOW_DOUBLE_ETCHED_IN_DASH
1001 shadow_draw (XlwMenuWidget mw,
1005 unsigned int height,
1008 Display *dpy = XtDisplay (mw);
1011 int thickness = mw->menu.shadow_thickness;
1015 Boolean etched = False;
1019 case SHADOW_BACKGROUND:
1020 top_gc = bottom_gc = mw->menu.background_gc;
1022 case SHADOW_ETCHED_IN:
1023 top_gc = mw->menu.shadow_bottom_gc;
1024 bottom_gc = mw->menu.shadow_top_gc;
1027 case SHADOW_ETCHED_OUT:
1028 top_gc = mw->menu.shadow_top_gc;
1029 bottom_gc = mw->menu.shadow_bottom_gc;
1033 top_gc = mw->menu.shadow_bottom_gc;
1034 bottom_gc = mw->menu.shadow_top_gc;
1038 top_gc = mw->menu.shadow_top_gc;
1039 bottom_gc = mw->menu.shadow_bottom_gc;
1045 unsigned int half = thickness/2;
1046 shadow_rectangle_draw (dpy,
1051 width - half, height - half,
1053 shadow_rectangle_draw (dpy,
1058 width - half , height - half,
1063 shadow_rectangle_draw (dpy,
1074 arrow_decoration_draw (XlwMenuWidget mw,
1080 Display *dpy = XtDisplay (mw);
1084 int thickness = mw->menu.shadow_thickness;
1087 int length = (int)((double)width * 0.87);
1088 int thick_med = (int)((double)thickness * 1.73);
1091 half_width = width/2 + 1;
1093 half_width = width/2;
1095 select_gc = mw->menu.background_gc;
1099 top_gc = mw->menu.shadow_bottom_gc;
1100 bottom_gc = mw->menu.shadow_top_gc;
1104 top_gc = mw->menu.shadow_top_gc;
1105 bottom_gc = mw->menu.shadow_bottom_gc;
1108 /* Fill internal area. We do this first so that the borders have a
1110 points [0].x = x + thickness;
1111 points [0].y = y + thickness;
1112 points [1].x = x + length - thickness;
1113 points [1].y = y + half_width;
1114 points [2].x = x + length - thickness;
1115 points [2].y = y + half_width + thickness;
1116 points [3].x = x + thickness;
1117 points [3].y = y + width - thickness;
1130 points [1].x = x + thickness;
1131 points [1].y = y + thick_med;
1132 points [2].x = x + thickness;
1133 points [2].y = y + width - thick_med;
1135 points [3].y = y + width;
1137 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
1141 points [0].y = y + width;
1142 points [1].x = x + length;
1143 points [1].y = y + half_width;
1144 points [2].x = x + length - (thickness + thickness);
1145 points [2].y = y + half_width;
1146 points [3].x = x + thickness;
1147 points [3].y = y + width - thick_med;
1149 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
1154 points [1].x = x + length;
1155 points [1].y = y + half_width;
1156 points [2].x = x + length - (thickness + thickness);
1157 points [2].y = y + half_width;
1158 points [3].x = x + thickness;
1159 points [3].y = y + thick_med;
1161 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
1165 toggle_decoration_draw (XlwMenuWidget mw,
1171 Display *dpy = XtDisplay (mw);
1172 int thickness = mw->menu.shadow_thickness;
1174 GC select_gc = mw->menu.select_gc;
1181 /* Fill internal area. */
1183 XFillRectangle (dpy,
1188 width - (2*thickness),
1189 width - (2*thickness));
1191 shadow_draw (mw, window, x, y, width, width, type);
1195 radio_decoration_draw (XlwMenuWidget mw,
1201 Display *dpy = XtDisplay (mw);
1204 GC select_gc = mw->menu.select_gc;
1205 int thickness = mw->menu.shadow_thickness;
1215 half_width = width/2;
1219 top_gc = mw->menu.shadow_bottom_gc;
1220 bottom_gc = mw->menu.shadow_top_gc;
1224 top_gc = mw->menu.shadow_top_gc;
1225 bottom_gc = mw->menu.shadow_bottom_gc;
1229 /* Draw the bottom first, just in case the regions overlap.
1230 The top should cast the longer shadow. */
1231 points [0].x = x; /* left corner */
1232 points [0].y = y + half_width;
1233 points [1].x = x + half_width; /* bottom corner */
1234 points [1].y = y + width;
1235 points [2].x = x + half_width; /* bottom inside corner */
1236 points [2].y = y + width - thickness;
1237 points [3].x = x + thickness; /* left inside corner */
1238 points [3].y = y + half_width;
1240 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
1242 points [0].x = x + half_width; /* bottom corner */
1243 points [0].y = y + width;
1244 points [1].x = x + width; /* right corner */
1245 points [1].y = y + half_width;
1246 points [2].x = x + width - thickness; /* right inside corner */
1247 points [2].y = y + half_width;
1248 points [3].x = x + half_width; /* bottom inside corner */
1249 points [3].y = y + width - thickness;
1251 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
1253 points [0].x = x; /* left corner */
1254 points [0].y = y + half_width;
1255 points [1].x = x + half_width; /* top corner */
1257 points [2].x = x + half_width; /* top inside corner */
1258 points [2].y = y + thickness;
1259 points [3].x = x + thickness; /* left inside corner */
1260 points [3].y = y + half_width;
1262 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
1264 points [0].x = x + half_width; /* top corner */
1266 points [1].x = x + width; /* right corner */
1267 points [1].y = y + half_width;
1268 points [2].x = x + width - thickness; /* right inside corner */
1269 points [2].y = y + half_width;
1270 points [3].x = x + half_width; /* top inside corner */
1271 points [3].y = y + thickness;
1273 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
1275 /* Draw the bottom first, just in case the regions overlap.
1276 The top should cast the longer shadow. */
1278 points [npoints].x = x; /* left corner */
1279 points [npoints++].y = y + half_width;
1280 points [npoints].x = x + half_width; /* bottom corner */
1281 points [npoints++].y = y + width;
1282 points [npoints].x = x + width; /* right corner */
1283 points [npoints++].y = y + half_width;
1284 points [npoints].x = x + width - thickness; /* right inside corner */
1285 points [npoints++].y = y + half_width;
1286 points [npoints].x = x + half_width; /* bottom inside corner */
1287 points [npoints++].y = y + width - thickness;
1288 points [npoints].x = x + thickness; /* left inside corner */
1289 points [npoints++].y = y + half_width;
1291 XFillPolygon (dpy, window, bottom_gc,
1292 points, npoints, Nonconvex, CoordModeOrigin);
1296 points [npoints].x = x; /* left corner */
1297 points [npoints++].y = y + half_width;
1298 points [npoints].x = x + half_width; /* top corner */
1299 points [npoints++].y = y;
1300 points [npoints].x = x + width; /* right corner */
1301 points [npoints++].y = y + half_width;
1302 points [npoints].x = x + width - thickness; /* right inside corner */
1303 points [npoints++].y = y + half_width;
1304 points [npoints].x = x + half_width; /* top inside corner */
1305 points [npoints++].y = y + thickness;
1306 points [npoints].x = x + thickness; /* left inside corner */
1307 points [npoints++].y = y + half_width;
1309 XFillPolygon (dpy, window, top_gc, points, npoints, Nonconvex,
1314 /* Fill internal area. */
1317 points [0].x = x + thickness;
1318 points [0].y = y + half_width;
1319 points [1].x = x + half_width;
1320 points [1].y = y + thickness;
1321 points [2].x = x + width - thickness;
1322 points [2].y = y + half_width;
1323 points [3].x = x + half_width;
1324 points [3].y = y + width - thickness;
1336 separator_decoration_draw (XlwMenuWidget mw,
1343 Display *dpy = XtDisplay (mw);
1346 unsigned int offset = 0;
1347 unsigned int num_separators = 1;
1348 unsigned int top_line_thickness = 0;
1349 unsigned int bottom_line_thickness = 0;
1350 Boolean dashed = False;
1354 case SHADOW_NO_LINE: /* nothing to do */
1356 case SHADOW_DOUBLE_LINE:
1358 case SHADOW_SINGLE_LINE:
1359 top_gc = bottom_gc = mw->menu.foreground_gc;
1360 top_line_thickness = 1;
1362 case SHADOW_DOUBLE_DASHED_LINE:
1364 case SHADOW_SINGLE_DASHED_LINE:
1365 top_gc = bottom_gc = mw->menu.foreground_gc;
1366 top_line_thickness = 1;
1369 case SHADOW_DOUBLE_ETCHED_OUT_DASH:
1371 case SHADOW_ETCHED_OUT_DASH:
1372 top_gc = mw->menu.shadow_top_gc;
1373 bottom_gc = mw->menu.shadow_bottom_gc;
1374 top_line_thickness = mw->menu.shadow_thickness/2;
1375 bottom_line_thickness = mw->menu.shadow_thickness - top_line_thickness;
1378 case SHADOW_DOUBLE_ETCHED_IN_DASH:
1380 case SHADOW_ETCHED_IN_DASH:
1381 top_gc = mw->menu.shadow_bottom_gc;
1382 bottom_gc = mw->menu.shadow_top_gc;
1383 top_line_thickness = mw->menu.shadow_thickness/2;
1384 bottom_line_thickness = mw->menu.shadow_thickness - top_line_thickness;
1387 case SHADOW_DOUBLE_ETCHED_OUT:
1389 case SHADOW_ETCHED_OUT:
1390 top_gc = mw->menu.shadow_top_gc;
1391 bottom_gc = mw->menu.shadow_bottom_gc;
1392 top_line_thickness = mw->menu.shadow_thickness/2;
1393 bottom_line_thickness = mw->menu.shadow_thickness - top_line_thickness;
1395 case SHADOW_DOUBLE_ETCHED_IN:
1397 case SHADOW_ETCHED_IN:
1399 top_gc = mw->menu.shadow_bottom_gc;
1400 bottom_gc = mw->menu.shadow_top_gc;
1401 top_line_thickness = mw->menu.shadow_thickness/2;
1402 bottom_line_thickness = mw->menu.shadow_thickness - top_line_thickness;
1409 values.line_style = LineOnOffDash;
1410 if (top_line_thickness > 0)
1411 XChangeGC (dpy, top_gc, GCLineStyle, &values);
1412 if (bottom_line_thickness > 0 && bottom_gc != top_gc)
1413 XChangeGC (dpy, bottom_gc, GCLineStyle, &values);
1416 while (num_separators--)
1419 for (i = 0; i < top_line_thickness; i++)
1420 XDrawLine (dpy, window, top_gc, x, y + i, x + width, y + i);
1422 for (i = 0; i < bottom_line_thickness; i++)
1423 XDrawLine (dpy, window, bottom_gc,
1424 x, y + top_line_thickness + offset + i,
1425 x + width, y + top_line_thickness + offset + i);
1426 y += (top_line_thickness + offset + bottom_line_thickness + 1);
1432 values.line_style = LineSolid;
1433 if (top_line_thickness > 0)
1434 XChangeGC (dpy, top_gc, GCLineStyle, &values);
1435 if (bottom_line_thickness > 0 && bottom_gc != top_gc)
1436 XChangeGC (dpy, bottom_gc, GCLineStyle, &values);
1440 #define SLOPPY_TYPES 0 /* 0=off, 1=error check, 2=easy to please */
1442 #if SLOPPY_TYPES < 2
1444 static char *wv_types[] =
1458 print_widget_value (widget_value *wv, int just_one, int depth)
1462 for (i = 0; i < depth; i++)
1467 printf ("%s(null widget value pointer)\n", d);
1470 printf ("%stype: %s\n", d, wv_types [wv->type]);
1472 printf ("%sname: %s\n", d, (wv->name ? wv->name : "(null)"));
1474 if (wv->name) printf ("%sname: %s\n", d, wv->name);
1476 if (wv->value) printf ("%svalue: %s\n", d, wv->value);
1477 if (wv->key) printf ("%skey: %s\n", d, wv->key);
1478 printf ("%senabled: %d\n", d, wv->enabled);
1481 printf ("\n%scontents: \n", d);
1482 print_widget_value (wv->contents, 0, depth + 5);
1484 if (!just_one && wv->next)
1487 print_widget_value (wv->next, 0, depth);
1490 #endif /* SLOPPY_TYPES < 2 */
1493 all_dashes_p (char *s)
1496 if (!s || s[0] == '\0')
1498 for (p = s; *p == '-'; p++);
1500 if (*p == '!' || *p == '\0')
1504 #endif /* SLOPPY_TYPES */
1506 static widget_value_type
1507 menu_item_type (widget_value *val)
1509 if (val->type != UNSPECIFIED_TYPE)
1512 else if (all_dashes_p (val->name))
1513 return SEPARATOR_TYPE;
1514 else if (val->name && val->name[0] == '\0') /* push right */
1515 return PUSHRIGHT_TYPE;
1516 else if (val->contents) /* cascade */
1517 return CASCADE_TYPE;
1518 else if (val->call_data) /* push button */
1525 return UNSPECIFIED_TYPE; /* Not reached */
1530 label_button_size (XlwMenuWidget mw,
1533 unsigned int *toggle_width,
1534 unsigned int *label_width,
1535 unsigned int *bindings_width,
1536 unsigned int *height)
1538 *height = (mw->menu.font_ascent + mw->menu.font_descent +
1539 2 * mw->menu.vertical_margin +
1540 2 * mw->menu.shadow_thickness);
1541 /* no left column decoration */
1542 *toggle_width = mw->menu.horizontal_margin + mw->menu.shadow_thickness;
1544 *label_width = string_width_u (mw, resource_widget_value (mw, val));
1545 *bindings_width = mw->menu.horizontal_margin + mw->menu.shadow_thickness;
1549 label_button_draw (XlwMenuWidget mw,
1552 Boolean highlighted,
1556 unsigned int height,
1557 unsigned int label_offset,
1558 unsigned int binding_tab)
1560 int y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin;
1564 label_offset = mw->menu.shadow_thickness + mw->menu.horizontal_margin;
1566 if (highlighted && (in_menubar || val->contents))
1567 gc = mw->menu.highlight_gc;
1568 else if (in_menubar || val->contents)
1569 gc = mw->menu.foreground_gc;
1571 gc = mw->menu.title_gc;
1573 /* Draw the label string. */
1576 x + label_offset, y + y_offset,
1578 resource_widget_value (mw, val));
1582 push_button_size (XlwMenuWidget mw,
1585 unsigned int *toggle_width,
1586 unsigned int *label_width,
1587 unsigned int *bindings_width,
1588 unsigned int *height)
1591 label_button_size (mw, val, in_menubar,
1592 toggle_width, label_width, bindings_width,
1595 /* key bindings to display? */
1596 if (!in_menubar && val->key)
1600 XmString key = XmStringCreateLtoR (val->key, XmSTRING_DEFAULT_CHARSET);
1601 w = string_width (mw, key);
1604 char *key = val->key;
1605 w = string_width (mw, key);
1607 *bindings_width += w + mw->menu.column_spacing;
1612 push_button_draw (XlwMenuWidget mw,
1615 Boolean highlighted,
1619 unsigned int height,
1620 unsigned int label_offset,
1621 unsigned int binding_offset)
1623 int y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin;
1626 Boolean menu_pb = in_menubar && (menu_item_type (val) == BUTTON_TYPE);
1628 /* Draw the label string. */
1630 label_offset = mw->menu.shadow_thickness + mw->menu.horizontal_margin;
1635 gc = mw->menu.highlight_gc;
1637 gc = mw->menu.inactive_gc;
1642 gc = mw->menu.button_gc;
1644 gc = mw->menu.inactive_button_gc;
1649 gc = mw->menu.foreground_gc;
1651 gc = mw->menu.inactive_gc;
1656 x + label_offset, y + y_offset,
1658 resource_widget_value (mw, val));
1660 /* Draw the keybindings */
1663 if (!binding_offset)
1665 unsigned int s_width =
1666 string_width (mw, resource_widget_value (mw, val));
1667 binding_offset = label_offset + s_width + mw->menu.shadow_thickness;
1669 binding_draw (mw, window,
1670 x + binding_offset + mw->menu.column_spacing,
1671 y + y_offset, gc, val->key);
1674 /* Draw the shadow */
1680 type = (val->selected ? SHADOW_ETCHED_OUT : SHADOW_ETCHED_IN);
1687 type = SHADOW_BACKGROUND;
1690 shadow_draw (mw, window, x, y, width, height, type);
1694 arrow_decoration_height (XlwMenuWidget mw)
1696 int result = (mw->menu.font_ascent + mw->menu.font_descent) / 2;
1698 result += 2 * mw->menu.shadow_thickness;
1700 if (result > (mw->menu.font_ascent + mw->menu.font_descent))
1701 result = mw->menu.font_ascent + mw->menu.font_descent;
1707 cascade_button_size (XlwMenuWidget mw,
1710 unsigned int *toggle_width,
1711 unsigned int *label_width,
1712 unsigned int *arrow_width,
1713 unsigned int *height)
1716 label_button_size (mw, val, in_menubar,
1717 toggle_width, label_width, arrow_width,
1719 /* we have a pull aside arrow */
1722 *arrow_width += arrow_decoration_height (mw) + mw->menu.column_spacing;
1727 cascade_button_draw (XlwMenuWidget mw,
1730 Boolean highlighted,
1734 unsigned int height,
1735 unsigned int label_offset,
1736 unsigned int binding_offset)
1740 /* Draw the label string. */
1741 label_button_draw (mw, val, in_menubar, highlighted,
1742 window, x, y, width, height, label_offset,
1745 /* Draw the pull aside arrow */
1746 if (!in_menubar && val->contents)
1749 unsigned int arrow_height = arrow_decoration_height (mw);
1751 y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin +
1752 (mw->menu.font_ascent+mw->menu.font_descent - arrow_height)/2;
1754 if (!binding_offset)
1756 unsigned int s_width =
1757 string_width (mw, resource_widget_value (mw, val));
1760 label_offset = mw->menu.shadow_thickness +
1761 mw->menu.horizontal_margin;
1763 binding_offset = label_offset + s_width + mw->menu.shadow_thickness;
1766 arrow_decoration_draw (mw,
1768 x + binding_offset + mw->menu.column_spacing,
1774 /* Draw the shadow */
1778 type = SHADOW_BACKGROUND;
1780 shadow_draw (mw, window, x, y, width, height, type);
1784 toggle_decoration_height (XlwMenuWidget mw)
1787 if (mw->menu.indicator_size > 0)
1788 rv = mw->menu.indicator_size;
1790 rv = mw->menu.font_ascent;
1792 if (rv > (mw->menu.font_ascent + mw->menu.font_descent))
1793 rv = mw->menu.font_ascent + mw->menu.font_descent;
1795 /* radio button can't be smaller than its border or a filling
1796 error will occur. */
1797 if (rv < 2 * mw->menu.shadow_thickness)
1798 rv = 2 * mw->menu.shadow_thickness;
1804 toggle_button_size (XlwMenuWidget mw,
1807 unsigned int *toggle_width,
1808 unsigned int *label_width,
1809 unsigned int *bindings_width,
1810 unsigned int *height)
1813 push_button_size (mw, val, in_menubar,
1814 toggle_width, label_width, bindings_width,
1816 /* we have a toggle */
1817 *toggle_width += toggle_decoration_height (mw) + mw->menu.column_spacing;
1821 toggle_button_draw (XlwMenuWidget mw,
1824 Boolean highlighted,
1828 unsigned int height,
1829 unsigned int label_tab,
1830 unsigned int binding_tab)
1834 unsigned int t_height = toggle_decoration_height (mw);
1836 /* Draw a toggle. */
1837 x_offset = mw->menu.shadow_thickness + mw->menu.horizontal_margin;
1838 y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin;
1839 y_offset += (mw->menu.font_ascent + mw->menu.font_descent - t_height)/2;
1841 toggle_decoration_draw (mw, window, x + x_offset, y + y_offset,
1842 t_height, val->selected);
1844 /* Draw the pushbutton parts. */
1845 push_button_draw (mw, val, in_menubar, highlighted, window, x, y, width,
1846 height, label_tab, binding_tab);
1850 radio_decoration_height (XlwMenuWidget mw)
1852 return toggle_decoration_height (mw);
1856 radio_button_draw (XlwMenuWidget mw,
1859 Boolean highlighted,
1863 unsigned int height,
1864 unsigned int label_tab,
1865 unsigned int binding_tab)
1869 unsigned int r_height = radio_decoration_height (mw);
1871 /* Draw a toggle. */
1872 x_offset = mw->menu.shadow_thickness + mw->menu.horizontal_margin;
1873 y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin;
1874 y_offset += (mw->menu.font_ascent + mw->menu.font_descent - r_height)/2;
1876 radio_decoration_draw (mw, window, x + x_offset, y + y_offset, r_height,
1879 /* Draw the pushbutton parts. */
1880 push_button_draw (mw, val, in_menubar, highlighted, window, x, y, width,
1881 height, label_tab, binding_tab);
1884 static struct _shadow_names
1891 { "singleLine", SHADOW_SINGLE_LINE },
1892 { "doubleLine", SHADOW_DOUBLE_LINE },
1893 { "singleDashedLine", SHADOW_SINGLE_DASHED_LINE },
1894 { "doubleDashedLine", SHADOW_DOUBLE_DASHED_LINE },
1895 { "noLine", SHADOW_NO_LINE },
1896 { "shadowEtchedIn", SHADOW_ETCHED_IN },
1897 { "shadowEtchedOut", SHADOW_ETCHED_OUT },
1898 { "shadowEtchedInDash", SHADOW_ETCHED_IN_DASH },
1899 { "shadowEtchedOutDash", SHADOW_ETCHED_OUT_DASH },
1901 { "shadowDoubleEtchedIn", SHADOW_DOUBLE_ETCHED_IN },
1902 { "shadowDoubleEtchedOut", SHADOW_DOUBLE_ETCHED_OUT },
1903 { "shadowDoubleEtchedInDash", SHADOW_DOUBLE_ETCHED_IN_DASH },
1904 { "shadowDoubleEtchedOutDash", SHADOW_DOUBLE_ETCHED_OUT_DASH }
1908 separator_type (char *name)
1913 for (i = 0; i < (int) (XtNumber (shadow_names)); i++ )
1915 if (strcmp (name, shadow_names[i].name) == 0)
1916 return shadow_names[i].type;
1919 return SHADOW_BACKGROUND;
1923 separator_decoration_height (XlwMenuWidget mw, widget_value *val)
1926 switch (separator_type (val->value))
1928 case SHADOW_NO_LINE:
1929 case SHADOW_SINGLE_LINE:
1930 case SHADOW_SINGLE_DASHED_LINE:
1932 case SHADOW_DOUBLE_LINE:
1933 case SHADOW_DOUBLE_DASHED_LINE:
1935 case SHADOW_DOUBLE_ETCHED_OUT:
1936 case SHADOW_DOUBLE_ETCHED_IN:
1937 case SHADOW_DOUBLE_ETCHED_OUT_DASH:
1938 case SHADOW_DOUBLE_ETCHED_IN_DASH:
1939 return (1 + 2 * mw->menu.shadow_thickness);
1940 case SHADOW_ETCHED_OUT:
1941 case SHADOW_ETCHED_IN:
1943 return mw->menu.shadow_thickness;
1948 separator_size (XlwMenuWidget mw,
1951 unsigned int *toggle_width,
1952 unsigned int *label_width,
1953 unsigned int *rest_width,
1954 unsigned int *height)
1956 *height = separator_decoration_height (mw, val);
1958 *toggle_width = *rest_width = 0;
1962 separator_draw (XlwMenuWidget mw,
1965 Boolean highlighted,
1969 unsigned int height,
1970 unsigned int label_tab,
1971 unsigned int binding_tab)
1973 unsigned int sep_width;
1980 separator_decoration_draw (mw,
1986 separator_type(val->value));
1990 pushright_size (XlwMenuWidget mw,
1993 unsigned int *toggle_width,
1994 unsigned int *label_width,
1995 unsigned int *rest_width,
1996 unsigned int *height)
1998 *height = *label_width = *toggle_width = *rest_width = 0;
2002 size_menu_item (XlwMenuWidget mw,
2005 unsigned int *toggle_width,
2006 unsigned int *label_width,
2007 unsigned int *rest_width,
2008 unsigned int *height)
2010 void (*function_ptr) (XlwMenuWidget _mw,
2012 Boolean _in_menubar,
2013 unsigned int *_toggle_width,
2014 unsigned int *_label_width,
2015 unsigned int *_rest_width,
2016 unsigned int *_height);
2018 switch (menu_item_type (val))
2022 function_ptr = toggle_button_size;
2024 case SEPARATOR_TYPE:
2025 function_ptr = separator_size;
2027 case INCREMENTAL_TYPE:
2029 function_ptr = cascade_button_size;
2032 function_ptr = push_button_size;
2034 case PUSHRIGHT_TYPE:
2035 function_ptr = pushright_size;
2039 function_ptr = label_button_size;
2043 (*function_ptr) (mw,
2053 display_menu_item (XlwMenuWidget mw,
2057 Boolean highlighted,
2059 Boolean just_compute)
2062 int x = where->x /* + mw->menu.shadow_thickness */ ;
2063 int y = where->y /* + mw->menu.shadow_thickness */ ;
2064 unsigned int toggle_width;
2065 unsigned int label_width;
2066 unsigned int binding_width;
2068 unsigned int height;
2069 unsigned int label_tab;
2070 unsigned int binding_tab;
2071 void (*function_ptr) (XlwMenuWidget _mw,
2073 Boolean _in_menubar,
2074 Boolean _highlighted,
2077 unsigned int _width,
2078 unsigned int _height,
2079 unsigned int _label_tab,
2080 unsigned int _binding_tab);
2082 size_menu_item (mw, val, horizontal,
2083 &toggle_width, &label_width, &binding_width, &height);
2087 width = toggle_width + label_width + binding_width;
2088 height = ws->height - 2 * mw->menu.shadow_thickness;
2092 width = ws->width - 2 * mw->menu.shadow_thickness;
2093 toggle_width = ws->toggle_width;
2094 label_width = ws->label_width;
2103 label_tab = toggle_width;
2104 binding_tab = toggle_width + label_width;
2106 switch (menu_item_type (val))
2109 function_ptr = toggle_button_draw;
2112 function_ptr = radio_button_draw;
2114 case SEPARATOR_TYPE:
2115 function_ptr = separator_draw;
2117 case INCREMENTAL_TYPE:
2119 function_ptr = cascade_button_draw;
2122 function_ptr = push_button_draw;
2125 function_ptr = label_button_draw;
2127 default: /* do no drawing */
2131 (*function_ptr) (mw,
2143 size_menu (XlwMenuWidget mw, int level)
2145 unsigned int toggle_width;
2146 unsigned int label_width;
2147 unsigned int rest_width;
2148 unsigned int height;
2149 unsigned int max_toggle_width = 0;
2150 unsigned int max_label_width = 0;
2151 unsigned int max_rest_width = 0;
2152 unsigned int max_height = 0;
2153 int horizontal_p = mw->menu.horizontal && (level == 0);
2157 if (level >= mw->menu.old_depth)
2160 ws = &mw->menu.windows [level];
2162 for (val = mw->menu.old_stack [level]->contents; val; val = val->next)
2173 max_label_width += toggle_width + label_width + rest_width;
2174 if (height > max_height)
2175 max_height = height;
2179 if (max_toggle_width < toggle_width)
2180 max_toggle_width = toggle_width;
2181 if (max_label_width < label_width)
2182 max_label_width = label_width;
2183 if (max_rest_width < rest_width)
2184 max_rest_width = rest_width;
2185 max_height += height;
2189 ws->height = max_height;
2190 ws->width = max_label_width + max_rest_width + max_toggle_width;
2191 ws->toggle_width = max_toggle_width;
2192 ws->label_width = max_label_width;
2194 ws->width += 2 * mw->menu.shadow_thickness;
2195 ws->height += 2 * mw->menu.shadow_thickness;
2199 display_menu (XlwMenuWidget mw, int level, Boolean just_compute_p,
2200 XPoint *highlighted_pos, XPoint *hit, widget_value **hit_return,
2201 widget_value *this, widget_value *that)
2204 widget_value *following_item;
2207 int horizontal_p = mw->menu.horizontal && (level == 0);
2209 int just_compute_this_one_p;
2211 if (level >= mw->menu.old_depth)
2214 if (level < mw->menu.old_depth - 1)
2215 following_item = mw->menu.old_stack [level + 1];
2218 if (lw_menu_accelerate
2219 && level == mw->menu.old_depth - 1
2220 && mw->menu.old_stack [level]->type == CASCADE_TYPE)
2221 just_compute_p = True;
2222 following_item = NULL;
2225 #if SLOPPY_TYPES == 1
2226 puts("===================================================================");
2227 print_widget_value (following_item, 1, 0);
2233 where.x = mw->menu.shadow_thickness;
2234 where.y = mw->menu.shadow_thickness;
2236 ws = &mw->menu.windows [level];
2237 for (val = mw->menu.old_stack [level]->contents; val; val = val->next)
2241 highlighted_p = (val == following_item);
2242 /* If this is the partition (the dummy item which says that menus
2243 after this should be flushright) then figure out how big the
2244 following items are. This means we walk down the tail of the
2245 list twice, but that's no big deal - it's short.
2247 if (horizontal_p && (menu_item_type (val) == PUSHRIGHT_TYPE))
2250 XPoint flushright_size;
2252 flushright_size.x = 0;
2253 flushright_size.y = 0;
2254 for (rest = val; rest; rest = rest->next)
2255 display_menu_item (mw, rest, ws, &flushright_size,
2256 highlighted_p, horizontal_p, True);
2257 new_x = ws->width - (flushright_size.x + mw->menu.shadow_thickness);
2258 if (new_x > where.x)
2260 /* We know what we need; don't draw this item. */
2264 if (highlighted_p && highlighted_pos)
2267 highlighted_pos->x = where.x;
2269 highlighted_pos->y = where.y;
2272 just_compute_this_one_p =
2273 just_compute_p || ((this || that) && val != this && val != that);
2277 display_menu_item (mw, val, ws, &where, highlighted_p, horizontal_p,
2278 just_compute_this_one_p);
2280 if (highlighted_p && highlighted_pos)
2283 highlighted_pos->y = ws->height;
2285 highlighted_pos->x = ws->width;
2288 if (hit && !*hit_return)
2290 if (horizontal_p && hit->x > start.x && hit->x <= where.x)
2292 else if (!horizontal_p && hit->y > start.y && hit->y <= where.y)
2297 where.y = mw->menu.shadow_thickness;
2299 where.x = mw->menu.shadow_thickness;
2302 /* Draw slab edges around menu */
2303 if (!just_compute_p)
2304 shadow_draw(mw, ws->window, 0, 0, ws->width, ws->height, SHADOW_OUT);
2309 set_new_state (XlwMenuWidget mw, widget_value *val, int level)
2313 mw->menu.new_depth = 0;
2314 for (i = 0; i < level; i++)
2315 push_new_stack (mw, mw->menu.old_stack [i]);
2317 push_new_stack (mw, val);
2321 make_windows_if_needed (XlwMenuWidget mw, int n)
2325 XSetWindowAttributes xswa;
2330 window_state *windows;
2333 if (mw->menu.windows_length >= n)
2336 root = RootWindowOfScreen (XtScreen(mw));
2337 /* grab the visual and depth from the nearest shell ancestor */
2338 visual = CopyFromParent;
2339 depth = CopyFromParent;
2341 while (visual == CopyFromParent && p)
2345 visual = ((ShellWidget)p)->shell.visual;
2346 depth = p->core.depth;
2351 xswa.save_under = True;
2352 xswa.override_redirect = True;
2353 xswa.background_pixel = mw->core.background_pixel;
2354 xswa.border_pixel = mw->core.border_pixel;
2355 xswa.event_mask = (ExposureMask | ButtonMotionMask
2356 | ButtonReleaseMask | ButtonPressMask);
2357 xswa.cursor = mw->menu.cursor_shape;
2358 xswa.colormap = mw->core.colormap;
2359 mask = CWSaveUnder | CWOverrideRedirect | CWBackPixel | CWBorderPixel
2360 | CWEventMask | CWCursor | CWColormap;
2362 if (mw->menu.use_backing_store)
2364 xswa.backing_store = Always;
2365 mask |= CWBackingStore;
2368 if (!mw->menu.windows)
2371 (window_state *) XtMalloc (n * sizeof (window_state));
2377 (window_state *) XtRealloc ((char *) mw->menu.windows,
2378 n * sizeof (window_state));
2379 start_at = mw->menu.windows_length;
2381 mw->menu.windows_length = n;
2383 windows = mw->menu.windows;
2385 for (i = start_at; i < n; i++)
2389 windows [i].width = 1;
2390 windows [i].height = 1;
2391 windows [i].window =
2392 XCreateWindow (XtDisplay (mw),
2395 0, depth, CopyFromParent, visual, mask, &xswa);
2399 /* Make the window fit in the screen */
2401 fit_to_screen (XlwMenuWidget mw, window_state *ws, window_state *previous_ws,
2402 Boolean horizontal_p)
2404 int screen_width = WidthOfScreen (XtScreen (mw));
2405 int screen_height = HeightOfScreen (XtScreen (mw));
2409 else if ((int) (ws->x + ws->width) > screen_width)
2412 ws->x = previous_ws->x - ws->width;
2415 ws->x = screen_width - ws->width;
2417 /* This check is to make sure we cut off the right side
2418 instead of the left side if the menu is wider than the
2426 else if ((int) (ws->y + ws->height) > screen_height)
2430 /* A pulldown must either be entirely above or below the menubar.
2431 If we're here, the pulldown doesn't fit below the menubar, so
2432 let's determine if it will fit above the menubar.
2433 Only put it above if there is more room above than below.
2434 Note shadow_thickness offset to allow for slab surround.
2436 if (ws->y > (screen_height / 2))
2437 ws->y = previous_ws->y - ws->height + mw->menu.shadow_thickness;
2441 ws->y = screen_height - ws->height;
2442 /* if it's taller than the screen, display the topmost part
2443 that will fit, beginning at the top of the screen. */
2450 /* Updates old_stack from new_stack and redisplays. */
2452 remap_menubar (XlwMenuWidget mw)
2456 XPoint selection_position;
2457 int old_depth = mw->menu.old_depth;
2458 int new_depth = mw->menu.new_depth;
2459 widget_value **old_stack;
2460 widget_value **new_stack;
2461 window_state *windows;
2462 widget_value *old_selection;
2463 widget_value *new_selection;
2465 /* Check that enough windows and old_stack are ready. */
2466 make_windows_if_needed (mw, new_depth);
2467 make_old_stack_space (mw, new_depth);
2468 windows = mw->menu.windows;
2469 old_stack = mw->menu.old_stack;
2470 new_stack = mw->menu.new_stack;
2472 /* compute the last identical different entry */
2473 for (i = 1; i < old_depth && i < new_depth; i++)
2474 if (old_stack [i] != new_stack [i])
2478 if (lw_menu_accelerate
2480 && last_same == old_depth - 1
2481 && old_stack [last_same]->contents)
2484 /* Memorize the previously selected item to be able to refresh it */
2485 old_selection = last_same + 1 < old_depth ? old_stack [last_same + 1] : NULL;
2486 new_selection = last_same + 1 < new_depth ? new_stack [last_same + 1] : NULL;
2488 /* updates old_state from new_state. It has to be done now because
2489 display_menu (called below) uses the old_stack to know what to display. */
2490 for (i = last_same + 1; i < new_depth; i++)
2491 old_stack [i] = new_stack [i];
2493 mw->menu.old_depth = new_depth;
2495 /* refresh the last seletion */
2496 selection_position.x = 0;
2497 selection_position.y = 0;
2498 display_menu (mw, last_same, new_selection == old_selection,
2499 &selection_position, NULL, NULL, old_selection, new_selection);
2501 /* Now popup the new menus */
2502 for (i = last_same + 1; i < new_depth && new_stack [i]->contents; i++)
2504 window_state *previous_ws = &windows [i - 1];
2505 window_state *ws = &windows [i];
2507 if (lw_menu_accelerate && i == new_depth - 1)
2510 ws->x = previous_ws->x + selection_position.x;
2511 ws->y = previous_ws->y + selection_position.y;
2513 /* take into account the slab around the new menu */
2514 ws->y -= mw->menu.shadow_thickness;
2517 widget_value *val = mw->menu.old_stack [i];
2518 if (val->contents->type == INCREMENTAL_TYPE)
2520 /* okay, we're now doing a lisp callback to incrementally generate
2521 more of the menu. */
2522 XtCallCallbackList ((Widget)mw,
2524 (XtPointer)val->contents);
2530 fit_to_screen (mw, ws, previous_ws, mw->menu.horizontal && i == 1);
2532 XClearWindow (XtDisplay (mw), ws->window);
2533 XMoveResizeWindow (XtDisplay (mw), ws->window, ws->x, ws->y,
2534 ws->width, ws->height);
2535 XMapRaised (XtDisplay (mw), ws->window);
2536 display_menu (mw, i, False, &selection_position, NULL, NULL, NULL, NULL);
2539 /* unmap the menus that popped down */
2541 last_same = new_depth;
2542 if (lw_menu_accelerate
2544 && new_stack [last_same - 1]->contents)
2547 for (i = last_same - 1; i < old_depth; i++)
2548 if (i >= last_same || !new_stack [i]->contents)
2549 XUnmapWindow (XtDisplay (mw), windows [i].window);
2553 motion_event_is_in_menu (XlwMenuWidget mw, XMotionEvent *ev, int level,
2554 XPoint *relative_pos)
2556 window_state *ws = &mw->menu.windows [level];
2557 int x = level == 0 ? ws->x : ws->x + mw->menu.shadow_thickness;
2558 int y = level == 0 ? ws->y : ws->y + mw->menu.shadow_thickness;
2559 relative_pos->x = ev->x_root - x;
2560 relative_pos->y = ev->y_root - y;
2561 return (x < ev->x_root && ev->x_root < (int) (x + ws->width) &&
2562 y < ev->y_root && ev->y_root < (int) (y + ws->height));
2566 map_event_to_widget_value (XlwMenuWidget mw, XMotionEvent *ev,
2567 widget_value **val_ptr, int *level,
2568 Boolean *inside_menu)
2571 XPoint relative_pos;
2575 *inside_menu = False;
2577 /* Find the window */
2579 for (i = mw->menu.old_depth - 1; i >= 0; i--)
2581 for (i = 0; i <= mw->menu.old_depth - 1; i++)
2584 ws = &mw->menu.windows [i];
2585 if (ws && motion_event_is_in_menu (mw, ev, i, &relative_pos))
2587 *inside_menu = True; /* special logic for menubar below... */
2588 if ((ev->type == ButtonPress) ||
2591 display_menu (mw, i, True, NULL, &relative_pos,
2592 val_ptr, NULL, NULL);
2596 *inside_menu = True;
2599 else if (mw->menu.horizontal || i == 0)
2601 /* if we're clicking on empty part of the menubar, then
2602 unpost the stay-up menu */
2603 *inside_menu = False;
2613 make_drawing_gcs (XlwMenuWidget mw)
2616 unsigned long flags = (GCFont | GCForeground | GCBackground);
2619 xgcv.font = default_font_of_font_list (mw->menu.font_list)->fid;
2621 xgcv.font = mw->menu.font->fid;
2624 xgcv.foreground = mw->core.background_pixel;
2625 xgcv.background = mw->menu.foreground;
2626 mw->menu.background_gc = XtGetGC ((Widget) mw, flags, &xgcv);
2628 xgcv.foreground = mw->menu.foreground;
2629 xgcv.background = mw->core.background_pixel;
2630 mw->menu.foreground_gc = XtGetGC ((Widget) mw, flags, &xgcv);
2632 if (mw->menu.select_color != (Pixel)-1)
2634 xgcv.foreground = mw->menu.select_color;
2638 Display *dpy = XtDisplay(mw);
2639 if (CellsOfScreen(DefaultScreenOfDisplay(dpy)) <= 2)
2641 xgcv.foreground = mw->menu.foreground;
2646 Colormap cmap = mw->core.colormap;
2647 xcolor.pixel = mw->core.background_pixel;
2648 XQueryColor (dpy, cmap, &xcolor);
2649 xcolor.red = (xcolor.red * 17) / 20;
2650 xcolor.green = (xcolor.green * 17) / 20;
2651 xcolor.blue = (xcolor.blue * 17) / 20;
2652 if (allocate_nearest_color (dpy, cmap, &xcolor))
2653 xgcv.foreground = xcolor.pixel;
2656 xgcv.background = mw->core.background_pixel;
2657 mw->menu.select_gc = XtGetGC ((Widget)mw, flags, &xgcv);
2659 xgcv.foreground = mw->menu.foreground;
2660 xgcv.background = mw->core.background_pixel;
2661 xgcv.fill_style = FillStippled;
2662 xgcv.stipple = mw->menu.gray_pixmap;
2663 mw->menu.inactive_gc = XtGetGC ((Widget)mw,
2664 (flags | GCFillStyle | GCStipple),
2667 xgcv.foreground = mw->menu.highlight_foreground;
2668 xgcv.background = mw->core.background_pixel;
2669 mw->menu.highlight_gc = XtGetGC ((Widget)mw, flags, &xgcv);
2671 xgcv.foreground = mw->menu.title_foreground;
2672 xgcv.background = mw->core.background_pixel;
2673 mw->menu.title_gc = XtGetGC ((Widget)mw, flags, &xgcv);
2675 xgcv.foreground = mw->menu.button_foreground;
2676 xgcv.background = mw->core.background_pixel;
2677 mw->menu.button_gc = XtGetGC ((Widget)mw, flags, &xgcv);
2679 xgcv.fill_style = FillStippled;
2680 xgcv.stipple = mw->menu.gray_pixmap;
2681 mw->menu.inactive_button_gc = XtGetGC ((Widget)mw,
2682 (flags | GCFillStyle | GCStipple),
2687 release_drawing_gcs (XlwMenuWidget mw)
2689 XtReleaseGC ((Widget) mw, mw->menu.foreground_gc);
2690 XtReleaseGC ((Widget) mw, mw->menu.button_gc);
2691 XtReleaseGC ((Widget) mw, mw->menu.highlight_gc);
2692 XtReleaseGC ((Widget) mw, mw->menu.title_gc);
2693 XtReleaseGC ((Widget) mw, mw->menu.inactive_gc);
2694 XtReleaseGC ((Widget) mw, mw->menu.inactive_button_gc);
2695 XtReleaseGC ((Widget) mw, mw->menu.background_gc);
2696 XtReleaseGC ((Widget) mw, mw->menu.select_gc);
2697 /* let's get some segvs if we try to use these... */
2698 mw->menu.foreground_gc = (GC) -1;
2699 mw->menu.button_gc = (GC) -1;
2700 mw->menu.highlight_gc = (GC) -1;
2701 mw->menu.title_gc = (GC) -1;
2702 mw->menu.inactive_gc = (GC) -1;
2703 mw->menu.inactive_button_gc = (GC) -1;
2704 mw->menu.background_gc = (GC) -1;
2705 mw->menu.select_gc = (GC) -1;
2708 #define MINL(x,y) ((((unsigned long) (x)) < ((unsigned long) (y))) \
2709 ? ((unsigned long) (x)) : ((unsigned long) (y)))
2712 make_shadow_gcs (XlwMenuWidget mw)
2715 unsigned long pm = 0;
2716 Display *dpy = XtDisplay ((Widget) mw);
2717 Colormap cmap = mw->core.colormap;
2719 int top_frobbed = 0, bottom_frobbed = 0;
2721 if (mw->menu.top_shadow_color == (Pixel) (-1))
2722 mw->menu.top_shadow_color = mw->core.background_pixel;
2723 if (mw->menu.bottom_shadow_color == (Pixel) (-1))
2724 mw->menu.bottom_shadow_color = mw->menu.foreground;
2726 if (mw->menu.top_shadow_color == mw->core.background_pixel ||
2727 mw->menu.top_shadow_color == mw->menu.foreground)
2729 topc.pixel = mw->core.background_pixel;
2730 XQueryColor (dpy, cmap, &topc);
2731 /* don't overflow/wrap! */
2732 topc.red = MINL (65535, topc.red * 1.2);
2733 topc.green = MINL (65535, topc.green * 1.2);
2734 topc.blue = MINL (65535, topc.blue * 1.2);
2735 if (allocate_nearest_color (dpy, cmap, &topc))
2737 if (topc.pixel == mw->core.background_pixel)
2739 XFreeColors( dpy, cmap, &topc.pixel, 1, 0);
2740 topc.red = MINL (65535, topc.red + 0x8000);
2741 topc.green = MINL (65535, topc.green + 0x8000);
2742 topc.blue = MINL (65535, topc.blue + 0x8000);
2743 if (allocate_nearest_color (dpy, cmap, &topc))
2745 mw->menu.top_shadow_color = topc.pixel;
2750 mw->menu.top_shadow_color = topc.pixel;
2756 if (mw->menu.bottom_shadow_color == mw->menu.foreground ||
2757 mw->menu.bottom_shadow_color == mw->core.background_pixel)
2759 botc.pixel = mw->core.background_pixel;
2760 XQueryColor (dpy, cmap, &botc);
2761 botc.red = (botc.red * 3) / 5;
2762 botc.green = (botc.green * 3) / 5;
2763 botc.blue = (botc.blue * 3) / 5;
2764 if (allocate_nearest_color (dpy, cmap, &botc))
2766 if (botc.pixel == mw->core.background_pixel)
2768 XFreeColors (dpy, cmap, &botc.pixel, 1, 0);
2769 botc.red = MINL (65535, botc.red + 0x4000);
2770 botc.green = MINL (65535, botc.green + 0x4000);
2771 botc.blue = MINL (65535, botc.blue + 0x4000);
2772 if (allocate_nearest_color (dpy, cmap, &botc))
2774 mw->menu.bottom_shadow_color = botc.pixel;
2779 mw->menu.bottom_shadow_color = botc.pixel;
2786 if (top_frobbed && bottom_frobbed)
2788 int top_avg = ((topc.red / 3) + (topc.green / 3) + (topc.blue / 3));
2789 int bot_avg = ((botc.red / 3) + (botc.green / 3) + (botc.blue / 3));
2790 if (bot_avg > top_avg)
2792 Pixel tmp = mw->menu.top_shadow_color;
2793 mw->menu.top_shadow_color = mw->menu.bottom_shadow_color;
2794 mw->menu.bottom_shadow_color = tmp;
2796 else if (topc.pixel == botc.pixel)
2798 if (botc.pixel == mw->menu.foreground)
2799 mw->menu.top_shadow_color = mw->core.background_pixel;
2801 mw->menu.bottom_shadow_color = mw->menu.foreground;
2805 if (!mw->menu.top_shadow_pixmap &&
2806 mw->menu.top_shadow_color == mw->core.background_pixel)
2808 mw->menu.top_shadow_pixmap = mw->menu.gray_pixmap;
2809 mw->menu.top_shadow_color = mw->menu.foreground;
2811 if (!mw->menu.bottom_shadow_pixmap &&
2812 mw->menu.bottom_shadow_color == mw->core.background_pixel)
2814 mw->menu.bottom_shadow_pixmap = mw->menu.gray_pixmap;
2815 mw->menu.bottom_shadow_color = mw->menu.foreground;
2818 xgcv.fill_style = FillOpaqueStippled;
2819 xgcv.foreground = mw->menu.top_shadow_color;
2820 xgcv.background = mw->core.background_pixel;
2821 /* xgcv.stipple = mw->menu.top_shadow_pixmap; gtb */
2822 if (mw->menu.top_shadow_pixmap &&
2823 mw->menu.top_shadow_pixmap != XmUNSPECIFIED_PIXMAP)
2824 xgcv.stipple = mw->menu.top_shadow_pixmap;
2827 pm = (xgcv.stipple ? GCStipple|GCFillStyle : 0);
2828 mw->menu.shadow_top_gc =
2829 XtGetGC((Widget)mw, GCForeground|GCBackground|pm, &xgcv);
2831 xgcv.foreground = mw->menu.bottom_shadow_color;
2832 /* xgcv.stipple = mw->menu.bottom_shadow_pixmap; gtb */
2833 if (mw->menu.bottom_shadow_pixmap &&
2834 mw->menu.bottom_shadow_pixmap != XmUNSPECIFIED_PIXMAP)
2835 xgcv.stipple = mw->menu.bottom_shadow_pixmap;
2838 pm = (xgcv.stipple ? GCStipple|GCFillStyle : 0);
2839 mw->menu.shadow_bottom_gc =
2840 XtGetGC ((Widget)mw, GCForeground|GCBackground|pm, &xgcv);
2845 release_shadow_gcs (XlwMenuWidget mw)
2847 XtReleaseGC ((Widget) mw, mw->menu.shadow_top_gc);
2848 XtReleaseGC ((Widget) mw, mw->menu.shadow_bottom_gc);
2853 extract_font_extents (XlwMenuWidget mw)
2856 /* Find the maximal ascent/descent of the fonts in the font list
2857 so that all menu items can be the same height... */
2858 mw->menu.font_ascent = 0;
2859 mw->menu.font_descent = 0;
2862 XmFontContext context;
2863 #if (XmVersion >= 1002)
2864 XmFontListEntry fontentry;
2866 XmStringCharSet charset;
2870 if (! XmFontListInitFontContext (&context, mw->menu.font_list))
2872 #if (XmVersion >= 1002)
2873 /* There is a BUG in the 1.2 version of XmFontListGetNextFont() (or more
2874 specifically, in _XmGetFirstFont()) that can cause a null pointer to be
2875 passed to XFontsOfFontSet. Use XmFontListNextEntry(), which is the
2876 newer equivalent, instead. Also, it supports font sets, and the
2877 older function doesn't. */
2878 while ((fontentry = XmFontListNextEntry (context)))
2882 XtPointer one_of_them = XmFontListEntryGetFont (fontentry, &rettype);
2883 if (rettype == XmFONT_IS_FONTSET)
2885 XFontSet fontset = (XFontSet) one_of_them;
2886 XFontStruct **fontstruct_list;
2887 char **fontname_list;
2888 int fontcount = XFontsOfFontSet (fontset, &fontstruct_list,
2890 while (--fontcount >= 0)
2892 font = fontstruct_list[fontcount];
2893 if (font->ascent > (int) mw->menu.font_ascent)
2894 mw->menu.font_ascent = font->ascent;
2895 if (font->descent > (int) mw->menu.font_descent)
2896 mw->menu.font_descent = font->descent;
2899 else /* XmFONT_IS_FONT */
2901 font = (XFontStruct *) one_of_them;
2902 if (font->ascent > (int) mw->menu.font_ascent)
2903 mw->menu.font_ascent = font->ascent;
2904 if (font->descent > (int) mw->menu.font_descent)
2905 mw->menu.font_descent = font->descent;
2908 #else /* motif 1.1 */
2909 while (XmFontListGetNextFont (context, &charset, &font))
2911 if (font->ascent > (int) mw->menu.font_ascent)
2912 mw->menu.font_ascent = font->ascent;
2913 if (font->descent > (int) mw->menu.font_descent)
2914 mw->menu.font_descent = font->descent;
2917 #endif /* Motif version */
2918 XmFontListFreeFontContext (context);
2920 #else /* Not Motif */
2921 # ifdef USE_XFONTSET
2922 XFontStruct **fontstruct_list;
2923 char **fontname_list;
2925 int fontcount = XFontsOfFontSet(mw->menu.font_set, &fontstruct_list,
2927 mw->menu.font_ascent = 0;
2928 mw->menu.font_descent = 0;
2929 # if 0 /* nasty, personal debug, Kazz */
2930 fprintf(stderr, "fontSet count is %d\n", fontcount);
2932 while (--fontcount >= 0) {
2933 font = fontstruct_list[fontcount];
2934 if (font->ascent > (int) mw->menu.font_ascent)
2935 mw->menu.font_ascent = font->ascent;
2936 if (font->descent > (int) mw->menu.font_descent)
2937 mw->menu.font_descent = font->descent;
2939 # else /* ! USE_XFONTSET */
2940 mw->menu.font_ascent = mw->menu.font->ascent;
2941 mw->menu.font_descent = mw->menu.font->descent;
2943 #endif /* NEED_MOTIF */
2947 static XFontStruct *
2948 default_font_of_font_list (XmFontList font_list)
2950 XFontStruct *font = 0;
2952 /* Xm/Label.c does this: */
2953 _XmFontListGetDefaultFont (font_list, &font);
2956 XmFontContext context;
2957 #if (XmVersion >= 1002)
2958 XmFontListEntry fontentry;
2960 XtPointer one_of_them;
2962 XmStringCharSet charset;
2965 if (! XmFontListInitFontContext (&context, font_list))
2967 #if (XmVersion >= 1002)
2968 /* There is a BUG in the 1.2 version of XmFontListGetNextFont() (or more
2969 specifically, in _XmGetFirstFont()) that can cause a null pointer to be
2970 passed to XFontsOfFontSet. Use XmFontListNextEntry(), which is the
2971 newer equivalent, instead. */
2972 fontentry = XmFontListNextEntry (context);
2973 one_of_them = XmFontListEntryGetFont (fontentry, &rettype);
2974 if (rettype == XmFONT_IS_FONTSET)
2976 XFontSet fontset = (XFontSet) one_of_them;
2977 XFontStruct **fontstruct_list;
2978 char **fontname_list;
2979 (void) XFontsOfFontSet (fontset, &fontstruct_list, &fontname_list);
2980 font = fontstruct_list[0];
2982 else /* XmFONT_IS_FONT */
2984 font = (XFontStruct *) one_of_them;
2987 if (! XmFontListGetNextFont (context, &charset, &font))
2991 XmFontListFreeFontContext (context);
2995 if (! font) abort ();
2998 #endif /* NEED_MOTIF */
3001 XlwMenuInitialize (Widget request, Widget new, ArgList args,
3004 /* Get the GCs and the widget size */
3005 XlwMenuWidget mw = (XlwMenuWidget)new;
3007 XSetWindowAttributes xswa;
3010 Window window = RootWindowOfScreen (DefaultScreenOfDisplay (XtDisplay (mw)));
3011 Display *display = XtDisplay (mw);
3013 /* mw->menu.cursor = XCreateFontCursor (display, mw->menu.cursor_shape); */
3014 mw->menu.cursor = mw->menu.cursor_shape;
3016 mw->menu.gray_pixmap =
3017 XCreatePixmapFromBitmapData (display, window, (char *) gray_bits,
3018 gray_width, gray_height, 1, 0, 1);
3021 /* The menu.font_list slot came from the *fontList resource (Motif standard.)
3022 The menu.font_list_2 slot came from the *font resource, for backward
3023 compatibility with older versions of this code, and consistency with the
3024 rest of emacs. If both font and fontList are specified, we use font.
3025 If only one is specified, we use that. If neither are specified, we
3026 use the "fallback" value. What a kludge!!!
3028 Note that this has the bug that a more general wildcard like "*fontList:"
3029 will override a more specific resource like "Emacs*menubar.font:". But
3030 I can't think of a way around that.
3032 if (mw->menu.font_list) /* if *fontList is specified, use that */
3034 else if (mw->menu.font_list_2) /* else if *font is specified, use that */
3035 mw->menu.font_list = mw->menu.font_list_2;
3036 else /* otherwise use default */
3037 mw->menu.font_list = mw->menu.fallback_font_list;
3040 make_drawing_gcs (mw);
3041 make_shadow_gcs (mw);
3042 extract_font_extents (mw);
3044 xswa.background_pixel = mw->core.background_pixel;
3045 xswa.border_pixel = mw->core.border_pixel;
3046 mask = CWBackPixel | CWBorderPixel;
3048 mw->menu.popped_up = False;
3049 mw->menu.pointer_grabbed = False;
3050 mw->menu.next_release_must_exit = False;
3052 mw->menu.old_depth = 1;
3053 mw->menu.old_stack = XtNew (widget_value*);
3054 mw->menu.old_stack_length = 1;
3055 mw->menu.old_stack [0] = mw->menu.contents;
3057 mw->menu.new_depth = 0;
3058 mw->menu.new_stack = 0;
3059 mw->menu.new_stack_length = 0;
3060 push_new_stack (mw, mw->menu.contents);
3062 mw->menu.windows = XtNew (window_state);
3063 mw->menu.windows_length = 1;
3064 mw->menu.windows [0].x = 0;
3065 mw->menu.windows [0].y = 0;
3066 mw->menu.windows [0].width = 0;
3067 mw->menu.windows [0].height = 0;
3070 mw->core.width = mw->menu.windows [0].width;
3071 mw->core.height = mw->menu.windows [0].height;
3075 XlwMenuClassInitialize (void)
3077 initialize_massaged_resource_char();
3081 XlwMenuRealize (Widget w, Mask *valueMask, XSetWindowAttributes *attributes)
3083 XlwMenuWidget mw = (XlwMenuWidget)w;
3084 XSetWindowAttributes xswa;
3087 (*xlwMenuWidgetClass->core_class.superclass->core_class.realize)
3088 (w, valueMask, attributes);
3090 xswa.save_under = True;
3091 xswa.cursor = mw->menu.cursor_shape;
3092 mask = CWSaveUnder | CWCursor;
3093 if (mw->menu.use_backing_store)
3095 xswa.backing_store = Always;
3096 mask |= CWBackingStore;
3098 XChangeWindowAttributes (XtDisplay (w), XtWindow (w), mask, &xswa);
3100 mw->menu.windows [0].window = XtWindow (w);
3101 mw->menu.windows [0].x = w->core.x;
3102 mw->menu.windows [0].y = w->core.y;
3103 mw->menu.windows [0].width = w->core.width;
3104 mw->menu.windows [0].height = w->core.height;
3107 /* Only the toplevel menubar/popup is a widget so it's the only one that
3108 receives expose events through Xt. So we repaint all the other panes
3109 when receiving an Expose event. */
3111 XlwMenuRedisplay (Widget w, XEvent *ev, Region region)
3113 XlwMenuWidget mw = (XlwMenuWidget)w;
3116 if (mw->core.being_destroyed) return;
3118 for (i = 0; i < mw->menu.old_depth; i++)
3119 display_menu (mw, i, False, NULL, NULL, NULL, NULL, NULL);
3120 set_new_state (mw, NULL, mw->menu.old_depth); /* #### - ??? */
3121 remap_menubar (mw); /* #### - do these two lines do anything? */
3125 XlwMenuDestroy (Widget w)
3128 XlwMenuWidget mw = (XlwMenuWidget) w;
3130 if (mw->menu.pointer_grabbed)
3132 XtUngrabPointer (w, CurrentTime);
3133 mw->menu.pointer_grabbed = False;
3136 release_drawing_gcs (mw);
3137 release_shadow_gcs (mw);
3139 /* this doesn't come from the resource db but is created explicitly
3140 so we must free it ourselves. */
3141 XFreePixmap (XtDisplay (mw), mw->menu.gray_pixmap);
3142 mw->menu.gray_pixmap = (Pixmap) -1;
3144 /* Don't free mw->menu.contents because that comes from our creator.
3145 The `*_stack' elements are just pointers into `contents' so leave
3146 that alone too. But free the stacks themselves. */
3147 if (mw->menu.old_stack) XtFree ((char *) mw->menu.old_stack);
3148 if (mw->menu.new_stack) XtFree ((char *) mw->menu.new_stack);
3150 /* Remember, you can't free anything that came from the resource
3151 database. This includes:
3153 mw->menu.top_shadow_pixmap
3154 mw->menu.bottom_shadow_pixmap
3157 Also the color cells of top_shadow_color, bottom_shadow_color,
3158 foreground, and button_foreground will never be freed until this
3159 client exits. Nice, eh?
3162 /* start from 1 because the one in slot 0 is w->core.window */
3163 for (i = 1; i < mw->menu.windows_length; i++)
3164 XDestroyWindow (XtDisplay (mw), mw->menu.windows [i].window);
3165 if (mw->menu.windows)
3166 XtFree ((char *) mw->menu.windows);
3170 XlwMenuSetValues (Widget current, Widget request, Widget new, ArgList args,
3173 XlwMenuWidget oldmw = (XlwMenuWidget)current;
3174 XlwMenuWidget newmw = (XlwMenuWidget)new;
3175 Boolean redisplay = False;
3178 if (newmw->menu.contents
3179 && newmw->menu.contents->contents
3180 && newmw->menu.contents->contents->change >= VISIBLE_CHANGE)
3183 if (newmw->core.background_pixel != oldmw->core.background_pixel
3184 || newmw->menu.foreground != oldmw->menu.foreground
3185 /* For the XEditResource protocol, which may want to change the font. */
3187 || newmw->menu.font_list != oldmw->menu.font_list
3188 || newmw->menu.font_list_2 != oldmw->menu.font_list_2
3189 || newmw->menu.fallback_font_list != oldmw->menu.fallback_font_list
3191 || newmw->menu.font != oldmw->menu.font
3195 release_drawing_gcs (newmw);
3196 make_drawing_gcs (newmw);
3199 for (i = 0; i < oldmw->menu.windows_length; i++)
3201 XSetWindowBackground (XtDisplay (oldmw),
3202 oldmw->menu.windows [i].window,
3203 newmw->core.background_pixel);
3204 /* clear windows and generate expose events */
3205 XClearArea (XtDisplay (oldmw), oldmw->menu.windows[i].window,
3214 XlwMenuResize (Widget w)
3216 XlwMenuWidget mw = (XlwMenuWidget)w;
3218 mw->menu.windows [0].width = mw->core.width;
3219 mw->menu.windows [0].height = mw->core.height;
3222 \f/* Action procedures */
3224 handle_single_motion_event (XlwMenuWidget mw, XMotionEvent *ev,
3231 if (!map_event_to_widget_value (mw, ev, &val, &level, &stay_up))
3233 /* we wind up here when: (a) the event is in the menubar, (b) the
3234 event isn't in the menubar or any of the panes, (c) the event is on
3235 a disabled menu item */
3236 pop_new_stack_if_no_contents (mw);
3237 if (select_p && !stay_up) {
3238 /* pop down all menus and exit */
3239 mw->menu.next_release_must_exit = True;
3240 set_new_state(mw, (val = NULL), 1);
3245 /* we wind up here when: (a) the event pops up a pull_right menu,
3246 (b) a menu item that is not disabled is highlighted */
3247 if (select_p && mw->menu.bounce_down
3248 && close_to_reference_time((Widget)mw,
3249 mw->menu.menu_bounce_time,
3252 /* motion can cause more than one event. Don't bounce right back
3253 up if we've just bounced down. */
3256 else if (select_p && mw->menu.bounce_down &&
3257 mw->menu.last_selected_val &&
3258 (mw->menu.last_selected_val == val))
3260 val = NULL; /* assigned to mw->last_selected_val below */
3261 mw->menu.menu_bounce_time = ev->time;
3262 /* popdown last menu if we're selecting the same menu item as we did
3263 last time and the XlwMenu.bounceDown resource is set, if the
3264 item is on the menubar itself, then exit. */
3265 if (level == (mw->menu.popped_up ? 0 : 1))
3266 mw->menu.next_release_must_exit = True;
3269 mw->menu.menu_bounce_time = 0;
3270 set_new_state (mw, val, level);
3272 mw->menu.last_selected_val = val;
3275 /* Sync with the display. Makes it feel better on X terms. */
3276 XFlush (XtDisplay (mw));
3280 handle_motion_event (XlwMenuWidget mw, XMotionEvent *ev,
3285 unsigned int state = ev->state;
3286 XMotionEvent *event= ev, dummy;
3288 /* allow motion events to be generated again */
3289 dummy.window = ev->window;
3291 && XQueryPointer (XtDisplay (mw), dummy.window,
3292 &dummy.root, &dummy.subwindow,
3293 &dummy.x_root, &dummy.y_root,
3296 && dummy.state == state
3297 && (dummy.x_root != x || dummy.y_root != y))
3299 /* don't handle the event twice or that breaks bounce_down. --Stig */
3300 dummy.type = ev->type;
3304 lw_menu_accelerate = False;
3305 handle_single_motion_event (mw, event, select_p);
3308 Time x_focus_timestamp_really_sucks_fix_me_better;
3311 Start (Widget w, XEvent *ev, String *params, Cardinal *num_params)
3313 XlwMenuWidget mw = (XlwMenuWidget)w;
3315 lw_menubar_widget = w;
3317 lw_menu_active = True;
3319 if (!mw->menu.pointer_grabbed)
3321 mw->menu.menu_post_time = ev->xbutton.time;
3322 mw->menu.menu_bounce_time = 0;
3323 mw->menu.next_release_must_exit = True;
3324 mw->menu.last_selected_val = NULL;
3325 x_focus_timestamp_really_sucks_fix_me_better =
3326 ((XButtonPressedEvent*)ev)->time;
3327 XtCallCallbackList ((Widget)mw, mw->menu.open, NULL);
3329 /* notes the absolute position of the menubar window */
3330 mw->menu.windows [0].x = ev->xmotion.x_root - ev->xmotion.x;
3331 mw->menu.windows [0].y = ev->xmotion.y_root - ev->xmotion.y;
3333 XtGrabPointer ((Widget)mw, False,
3334 (ButtonMotionMask | ButtonReleaseMask | ButtonPressMask),
3335 GrabModeAsync, GrabModeAsync,
3336 None, mw->menu.cursor_shape,
3337 ((XButtonPressedEvent*)ev)->time);
3338 mw->menu.pointer_grabbed = True;
3341 /* handles the down like a move, slots are mostly compatible */
3342 handle_motion_event (mw, &ev->xmotion, True);
3346 Drag (Widget w, XEvent *ev, String *params, Cardinal *num_params)
3348 XlwMenuWidget mw = (XlwMenuWidget)w;
3349 handle_motion_event (mw, &ev->xmotion, False);
3353 Select (Widget w, XEvent *ev, String *params, Cardinal *num_params)
3355 XlwMenuWidget mw = (XlwMenuWidget)w;
3356 widget_value *selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
3358 lw_menu_accelerate = False;
3360 /* If user releases the button quickly, without selecting anything,
3361 after the initial down-click that brought the menu up,
3363 if ((selected_item == 0 || selected_item->call_data == 0)
3364 && (!mw->menu.next_release_must_exit
3365 || close_to_reference_time(w, mw->menu.menu_post_time, ev)))
3367 mw->menu.next_release_must_exit = False;
3371 /* pop down everything */
3372 mw->menu.new_depth = 1;
3375 /* Destroy() only gets called for popup menus. Menubar widgets aren't
3376 destroyed when their menu panes get nuked. */
3377 if (mw->menu.pointer_grabbed)
3379 XtUngrabPointer ((Widget)w, ev->xmotion.time);
3380 mw->menu.pointer_grabbed = False;
3383 if (mw->menu.popped_up)
3385 mw->menu.popped_up = False;
3386 XtPopdown (XtParent (mw));
3389 lw_menu_active = False;
3391 x_focus_timestamp_really_sucks_fix_me_better =
3392 ((XButtonPressedEvent*)ev)->time;
3395 XtCallCallbackList ((Widget) mw, mw->menu.select, (XtPointer) selected_item);
3398 \f/* Action procedures for keyboard accelerators */
3402 xlw_set_menu (Widget w, widget_value *val)
3404 lw_menubar_widget = w;
3405 set_new_state ((XlwMenuWidget)w, val, 1);
3408 /* prepare the menu structure via the call-backs */
3410 xlw_map_menu (Time t)
3412 XlwMenuWidget mw = (XlwMenuWidget)lw_menubar_widget;
3414 lw_menu_accelerate = True;
3416 if (!mw->menu.pointer_grabbed)
3418 XWindowAttributes ret;
3421 unsigned int num_waste;
3423 lw_menu_active = True;
3425 mw->menu.menu_post_time = t;
3426 mw->menu.menu_bounce_time = 0;
3428 mw->menu.next_release_must_exit = True;
3429 mw->menu.last_selected_val = NULL;
3431 XtCallCallbackList ((Widget)mw, mw->menu.open, NULL);
3433 /* do this for keyboards too! */
3434 /* notes the absolute position of the menubar window */
3436 mw->menu.windows [0].x = ev->xmotion.x_root - ev->xmotion.x;
3437 mw->menu.windows [0].y = ev->xmotion.y_root - ev->xmotion.y;
3440 /* get the geometry of the menubar */
3442 /* there has to be a better way than this. */
3444 mw->menu.windows [0].x = 0;
3445 mw->menu.windows [0].y = 0;
3447 parent = XtWindow (lw_menubar_widget);
3450 XGetWindowAttributes (XtDisplay (lw_menubar_widget), parent, &ret);
3451 mw->menu.windows [0].x += ret.x;
3452 mw->menu.windows [0].y += ret.y;
3455 XQueryTree (XtDisplay (lw_menubar_widget), parent, &root, &parent, &waste,
3462 while (parent != root);
3464 XtGrabPointer ((Widget)mw, False,
3465 (ButtonMotionMask | ButtonReleaseMask | ButtonPressMask),
3466 GrabModeAsync, GrabModeAsync,
3467 None, mw->menu.cursor_shape, t);
3468 mw->menu.pointer_grabbed = True;
3472 /* display the stupid menu already */
3474 xlw_display_menu (Time t)
3476 XlwMenuWidget mw = (XlwMenuWidget)lw_menubar_widget;
3478 lw_menu_accelerate = True;
3482 /* Sync with the display. Makes it feel better on X terms. */
3483 XFlush (XtDisplay (mw));
3486 /* push a sub menu */
3488 xlw_push_menu (widget_value *val)
3490 push_new_stack ((XlwMenuWidget)lw_menubar_widget, val);
3493 /* pop a sub menu */
3497 if (((XlwMenuWidget)lw_menubar_widget)->menu.new_depth > 0)
3498 ((XlwMenuWidget)lw_menubar_widget)->menu.new_depth --;
3505 xlw_kill_menus (widget_value *val)
3507 XlwMenuWidget mw = (XlwMenuWidget)lw_menubar_widget;
3509 lw_menu_accelerate = False;
3511 mw->menu.new_depth = 1;
3514 if (mw->menu.pointer_grabbed)
3516 XtUngrabPointer (lw_menubar_widget, CurrentTime);
3517 mw->menu.pointer_grabbed = False;
3520 lw_menu_active = False;
3521 XtCallCallbackList (lw_menubar_widget, mw->menu.select, (XtPointer)val);
3524 /* set the menu item */
3526 xlw_set_item (widget_value *val)
3528 if (((XlwMenuWidget)lw_menubar_widget)->menu.new_depth > 0)
3529 ((XlwMenuWidget) lw_menubar_widget)->menu.new_depth --;
3530 push_new_stack ((XlwMenuWidget) lw_menubar_widget, val);
3533 /* get either the current entry or a list of all entries in the current submenu */
3535 xlw_get_entries (int allp)
3537 XlwMenuWidget mw = (XlwMenuWidget)lw_menubar_widget;
3540 if (mw->menu.new_depth >= 2)
3541 return mw->menu.new_stack [mw->menu.new_depth - 2]->contents;
3543 return mw->menu.new_stack[0];
3546 if (mw->menu.new_depth >= 1)
3547 return mw->menu.new_stack [mw->menu.new_depth - 1];
3553 xlw_menu_level (void)
3555 return ((XlwMenuWidget)lw_menubar_widget)->menu.new_depth;
3559 /* Special code to pop-up a menu */
3561 xlw_pop_up_menu (XlwMenuWidget mw, XButtonPressedEvent *event)
3563 int x = event->x_root;
3564 int y = event->y_root;
3567 int borderwidth = mw->menu.shadow_thickness;
3568 Screen* screen = XtScreen (mw);
3570 mw->menu.menu_post_time = event->time;
3571 mw->menu.menu_bounce_time = 0;
3572 mw->menu.next_release_must_exit = True;
3573 mw->menu.last_selected_val = NULL;
3575 XtCallCallbackList ((Widget) mw, mw->menu.open, NULL);
3579 w = mw->menu.windows [0].width;
3580 h = mw->menu.windows [0].height;
3585 if (x < borderwidth)
3588 if (x > WidthOfScreen (screen) - w - 2 * borderwidth)
3589 x = WidthOfScreen (screen) - w - 2 * borderwidth;
3591 if (y < borderwidth)
3594 if (y > HeightOfScreen (screen) - h - 2 * borderwidth)
3595 y = HeightOfScreen (screen) - h - 2 * borderwidth;
3597 mw->menu.popped_up = True;
3598 XtConfigureWidget (XtParent (mw), x, y, w, h,
3599 XtParent (mw)->core.border_width);
3600 XtPopup (XtParent (mw), XtGrabExclusive);
3601 display_menu (mw, 0, False, NULL, NULL, NULL, NULL, NULL);
3602 if (!mw->menu.pointer_grabbed)
3604 XtGrabPointer ((Widget)mw, False,
3605 (ButtonMotionMask | ButtonReleaseMask | ButtonPressMask),
3606 GrabModeAsync, GrabModeAsync,
3607 None, mw->menu.cursor_shape, event->time);
3608 mw->menu.pointer_grabbed = True;
3611 mw->menu.windows [0].x = x + borderwidth;
3612 mw->menu.windows [0].y = y + borderwidth;
3614 handle_motion_event (mw, (XMotionEvent *) event, True);
3620 * This is a horrible function which should not be needed.
3621 * use it to put the resize method back the way the XlwMenu
3622 * class initializer put it. Motif screws with this when
3623 * the XlwMenu class gets instantiated.
3626 xlw_unmunge_class_resize (Widget w)
3628 if (w->core.widget_class->core_class.resize != XlwMenuResize)
3629 w->core.widget_class->core_class.resize = XlwMenuResize;