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),
519 /* This function searches STRING for parameter inserts of the form:
521 padding is either space (' ') or dash ('-') meaning
522 padding to the left or right of the inserted parameter.
523 In essence, all %1 strings are replaced by VALUE in the return value.
524 The caller is expected to free the return value using XtFree().
525 %% means insert one % (like printf).
526 %1 means insert VALUE.
527 %-1 means insert VALUE followed by one space. The latter is
528 not inserted if VALUE is a zero length string.
531 parameterize_string (const char *string, const char *value)
535 unsigned int done = 0;
540 result = XtMalloc(1);
548 for (ntimes = 1, percent = string;
549 (percent = strchr (percent, '%'));
553 result = XtMalloc ((ntimes * strlen(value)) + strlen(string) + 4);
556 while ((percent = strchr (string, '%')))
558 unsigned int left_pad;
559 unsigned int right_pad;
562 if (percent[1] == '%')
563 { /* it's a real % */
564 strncat (result, string, 1 + percent - string); /* incl % */
565 string = &percent[2]; /* after the second '%' */
566 continue; /* with the while() loop */
572 for (p = &percent[1]; /* test *p inside the loop */ ; p++)
583 { /* param and terminator */
584 strncat (result, string, percent - string);
585 if (value[0] != '\0')
588 for (i = 0; i < left_pad; i++)
589 strcat (result, " ");
590 strcat (result, value);
591 for (i = 0; i < right_pad; i++)
592 strcat (result, " ");
594 string = &p[1]; /* after the '1' */
595 done++; /* no need to do old way */
596 break; /* out of for() loop */
599 { /* bogus, copy the format as is */
600 /* out of for() loop */
601 strncat (result, string, 1 + p - string);
602 string = (*p ? &p[1] : p);
608 /* Copy the tail of the string */
609 strcat (result, string);
611 /* If we have not processed a % string, and we have a value, tail it. */
612 if (!done && value[0] != '\0')
614 strcat (result, " ");
615 strcat (result, value);
624 resource_widget_value (XlwMenuWidget mw, widget_value *val)
626 if (!val->toolkit_data)
628 char *resourced_name = NULL;
629 char *converted_name, *str;
630 XmString complete_name;
631 char massaged_name [1024];
633 if (mw->menu.lookup_labels)
635 /* Convert value style name into resource style name.
636 eg: "Free Willy" becomes "freeWilly" */
637 massage_resource_name (val->name, massaged_name);
639 /* If we have a value (parameter) see if we can find a "Named"
643 char named_name[1024];
644 sprintf (named_name, "%sNamed", massaged_name);
645 XtGetSubresources ((Widget) mw,
646 (XtPointer) &resourced_name,
647 named_name, named_name,
648 nameResource, 1, NULL, 0);
651 /* If nothing yet, try to load from the massaged name. */
654 XtGetSubresources ((Widget) mw,
655 (XtPointer) &resourced_name,
656 massaged_name, massaged_name,
657 nameResource, 1, NULL, 0);
659 } /* if (mw->menu.lookup_labels) */
661 /* Still nothing yet, use the name as the value. */
663 resourced_name = val->name;
665 /* Parameterize the string. */
666 converted_name = parameterize_string (resourced_name, val->value);
668 /* nuke newline characters to prevent menubar screwups */
669 for ( str = converted_name ; *str ; str++ )
671 if (str[0] == '\n') str[0] = ' ';
674 /* Improve OSF's bottom line. */
675 #if (XmVersion >= 1002)
676 complete_name = XmStringCreateLocalized (converted_name);
678 complete_name = XmStringCreateLtoR (converted_name,
679 XmSTRING_DEFAULT_CHARSET);
681 XtFree (converted_name);
683 val->toolkit_data = complete_name;
684 val->free_toolkit_data = True;
686 return (XmString) val->toolkit_data;
691 /* These two routines should be a seperate file..djw */
693 xlw_create_localized_string (Widget w,
704 XtGetSubresources (w,
714 return parameterize_string (string, arg);
718 xlw_create_localized_xmstring (Widget w,
723 char * string = xlw_create_localized_string (w, name, args, nargs);
724 XmString xm_string = XmStringCreateLtoR (string, XmSTRING_DEFAULT_CHARSET);
733 resource_widget_value (XlwMenuWidget mw, widget_value *val)
735 if (!val->toolkit_data)
737 char *resourced_name = NULL;
739 char massaged_name [1024];
741 if (mw->menu.lookup_labels)
743 massage_resource_name (val->name, massaged_name);
745 XtGetSubresources ((Widget) mw,
746 (XtPointer) &resourced_name,
747 massaged_name, massaged_name,
748 nameResource, 1, NULL, 0);
751 resourced_name = val->name;
753 complete_name = parameterize_string (resourced_name, val->value);
755 val->toolkit_data = complete_name;
756 /* nuke newline characters to prevent menubar screwups */
757 for ( ; *complete_name ; complete_name++ )
759 if (complete_name[0] == '\n')
760 complete_name[0] = ' ';
762 val->free_toolkit_data = True;
764 return (char *) val->toolkit_data;
769 /* Code for drawing strings. */
771 string_draw (XlwMenuWidget mw,
783 XmStringDraw (XtDisplay (mw), window,
787 1000, /* ???? width */
788 XmALIGNMENT_BEGINNING,
789 0, /* ???? layout_direction */
793 XmbDrawString (XtDisplay (mw), window, mw->menu.font_set, gc,
794 x, y + mw->menu.font_ascent, string, strlen (string));
796 XDrawString (XtDisplay (mw), window, gc,
797 x, y + mw->menu.font_ascent, string, strlen (string));
798 # endif /* USE_XFONTSET */
815 Dimension width, height;
823 newstring = XmStringLtoRCreate (&string[start], XmFONTLIST_DEFAULT_TAG);
825 XtDisplay (mw), window,
829 1000, /* ???? width */
830 XmALIGNMENT_BEGINNING,
831 0, /* ???? layout_direction */
834 XmStringExtent (mw->menu.font_list, newstring, &width, &height);
835 XmStringFree (newstring);
845 XtDisplay (mw), window, mw->menu.font_set, gc,
846 x, y + mw->menu.font_ascent, &string[start], end - start);
848 mw->menu.font_set, &string[start], end - start, &ri, &rl);
857 XtDisplay (mw), window, gc,
858 x, y + mw->menu.font_ascent, &string[start], end - start);
860 mw->menu.font, &string[start], end - start,
861 &drop, &drop, &drop, &xcs);
868 string_draw_u (XlwMenuWidget mw,
884 if (!XmStringGetLtoR (string, XmFONTLIST_DEFAULT_TAG, &chars))
889 for (i=0; chars[i]; ++i) {
890 if (chars[i] == '%' && chars[i+1] == '_') {
893 x += string_draw_range (mw, window, x, y, gc, chars, s, i);
894 w = string_draw_range (mw, window, x, y, gc, chars, i+2, i+3);
896 /* underline next character */
897 XDrawLine (XtDisplay (mw), window, gc, x - 1,
898 y + mw->menu.font_ascent + 1,
899 x + w - 1, y + mw->menu.font_ascent + 1 );
905 x += string_draw_range (mw, window, x, y, gc, chars, s, i);
912 binding_draw (XlwMenuWidget mw, Window w, int x, int y, GC gc, char *value)
915 XmString xm_value = XmStringCreateLtoR(value, XmSTRING_DEFAULT_CHARSET);
916 string_draw (mw, w, x, y, gc, xm_value);
917 XmStringFree (xm_value);
919 string_draw (mw, w, x, y, gc, value);
923 /* Low level code for drawing 3-D edges. */
925 shadow_rectangle_draw (Display *dpy,
932 unsigned int thickness)
941 points [1].x = x + width;
943 points [2].x = x + width - thickness;
944 points [2].y = y + thickness;
946 points [3].y = y + thickness;
947 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
949 points [0].y = y + thickness;
951 points [1].y = y + height;
952 points [2].x = x + thickness;
953 points [2].y = y + height - thickness;
954 points [3].x = x + thickness;
955 points [3].y = y + thickness;
956 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
957 points [0].x = x + width;
959 points [1].x = x + width - thickness;
960 points [1].y = y + thickness;
961 points [2].x = x + width - thickness;
962 points [2].y = y + height - thickness;
963 points [3].x = x + width;
964 points [3].y = y + height - thickness;
965 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
967 points [0].y = y + height;
968 points [1].x = x + width;
969 points [1].y = y + height;
970 points [2].x = x + width;
971 points [2].y = y + height - thickness;
972 points [3].x = x + thickness;
973 points [3].y = y + height - thickness;
974 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
977 typedef enum e_shadow_type
979 /* these are Motif compliant */
985 SHADOW_ETCHED_OUT_DASH,
986 SHADOW_ETCHED_IN_DASH,
989 SHADOW_SINGLE_DASHED_LINE,
990 SHADOW_DOUBLE_DASHED_LINE,
992 /* these are all non-Motif */
993 SHADOW_DOUBLE_ETCHED_OUT,
994 SHADOW_DOUBLE_ETCHED_IN,
995 SHADOW_DOUBLE_ETCHED_OUT_DASH,
996 SHADOW_DOUBLE_ETCHED_IN_DASH
1000 shadow_draw (XlwMenuWidget mw,
1004 unsigned int height,
1007 Display *dpy = XtDisplay (mw);
1010 int thickness = mw->menu.shadow_thickness;
1014 Boolean etched = False;
1018 case SHADOW_BACKGROUND:
1019 top_gc = bottom_gc = mw->menu.background_gc;
1021 case SHADOW_ETCHED_IN:
1022 top_gc = mw->menu.shadow_bottom_gc;
1023 bottom_gc = mw->menu.shadow_top_gc;
1026 case SHADOW_ETCHED_OUT:
1027 top_gc = mw->menu.shadow_top_gc;
1028 bottom_gc = mw->menu.shadow_bottom_gc;
1032 top_gc = mw->menu.shadow_bottom_gc;
1033 bottom_gc = mw->menu.shadow_top_gc;
1037 top_gc = mw->menu.shadow_top_gc;
1038 bottom_gc = mw->menu.shadow_bottom_gc;
1044 unsigned int half = thickness/2;
1045 shadow_rectangle_draw (dpy,
1050 width - half, height - half,
1052 shadow_rectangle_draw (dpy,
1057 width - half , height - half,
1062 shadow_rectangle_draw (dpy,
1073 arrow_decoration_draw (XlwMenuWidget mw,
1079 Display *dpy = XtDisplay (mw);
1083 int thickness = mw->menu.shadow_thickness;
1086 int length = (int)((double)width * 0.87);
1087 int thick_med = (int)((double)thickness * 1.73);
1090 half_width = width/2 + 1;
1092 half_width = width/2;
1094 select_gc = mw->menu.background_gc;
1098 top_gc = mw->menu.shadow_bottom_gc;
1099 bottom_gc = mw->menu.shadow_top_gc;
1103 top_gc = mw->menu.shadow_top_gc;
1104 bottom_gc = mw->menu.shadow_bottom_gc;
1107 /* Fill internal area. We do this first so that the borders have a
1109 points [0].x = x + thickness;
1110 points [0].y = y + thickness;
1111 points [1].x = x + length - thickness;
1112 points [1].y = y + half_width;
1113 points [2].x = x + length - thickness;
1114 points [2].y = y + half_width + thickness;
1115 points [3].x = x + thickness;
1116 points [3].y = y + width - thickness;
1129 points [1].x = x + thickness;
1130 points [1].y = y + thick_med;
1131 points [2].x = x + thickness;
1132 points [2].y = y + width - thick_med;
1134 points [3].y = y + width;
1136 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
1140 points [0].y = y + width;
1141 points [1].x = x + length;
1142 points [1].y = y + half_width;
1143 points [2].x = x + length - (thickness + thickness);
1144 points [2].y = y + half_width;
1145 points [3].x = x + thickness;
1146 points [3].y = y + width - thick_med;
1148 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
1153 points [1].x = x + length;
1154 points [1].y = y + half_width;
1155 points [2].x = x + length - (thickness + thickness);
1156 points [2].y = y + half_width;
1157 points [3].x = x + thickness;
1158 points [3].y = y + thick_med;
1160 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
1164 toggle_decoration_draw (XlwMenuWidget mw,
1170 Display *dpy = XtDisplay (mw);
1171 int thickness = mw->menu.shadow_thickness;
1173 GC select_gc = mw->menu.select_gc;
1180 /* Fill internal area. */
1182 XFillRectangle (dpy,
1187 width - (2*thickness),
1188 width - (2*thickness));
1190 shadow_draw (mw, window, x, y, width, width, type);
1194 radio_decoration_draw (XlwMenuWidget mw,
1200 Display *dpy = XtDisplay (mw);
1203 GC select_gc = mw->menu.select_gc;
1204 int thickness = mw->menu.shadow_thickness;
1214 half_width = width/2;
1218 top_gc = mw->menu.shadow_bottom_gc;
1219 bottom_gc = mw->menu.shadow_top_gc;
1223 top_gc = mw->menu.shadow_top_gc;
1224 bottom_gc = mw->menu.shadow_bottom_gc;
1228 /* Draw the bottom first, just in case the regions overlap.
1229 The top should cast the longer shadow. */
1230 points [0].x = x; /* left corner */
1231 points [0].y = y + half_width;
1232 points [1].x = x + half_width; /* bottom corner */
1233 points [1].y = y + width;
1234 points [2].x = x + half_width; /* bottom inside corner */
1235 points [2].y = y + width - thickness;
1236 points [3].x = x + thickness; /* left inside corner */
1237 points [3].y = y + half_width;
1239 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
1241 points [0].x = x + half_width; /* bottom corner */
1242 points [0].y = y + width;
1243 points [1].x = x + width; /* right corner */
1244 points [1].y = y + half_width;
1245 points [2].x = x + width - thickness; /* right inside corner */
1246 points [2].y = y + half_width;
1247 points [3].x = x + half_width; /* bottom inside corner */
1248 points [3].y = y + width - thickness;
1250 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
1252 points [0].x = x; /* left corner */
1253 points [0].y = y + half_width;
1254 points [1].x = x + half_width; /* top corner */
1256 points [2].x = x + half_width; /* top inside corner */
1257 points [2].y = y + thickness;
1258 points [3].x = x + thickness; /* left inside corner */
1259 points [3].y = y + half_width;
1261 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
1263 points [0].x = x + half_width; /* top corner */
1265 points [1].x = x + width; /* right corner */
1266 points [1].y = y + half_width;
1267 points [2].x = x + width - thickness; /* right inside corner */
1268 points [2].y = y + half_width;
1269 points [3].x = x + half_width; /* top inside corner */
1270 points [3].y = y + thickness;
1272 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
1274 /* Draw the bottom first, just in case the regions overlap.
1275 The top should cast the longer shadow. */
1277 points [npoints].x = x; /* left corner */
1278 points [npoints++].y = y + half_width;
1279 points [npoints].x = x + half_width; /* bottom corner */
1280 points [npoints++].y = y + width;
1281 points [npoints].x = x + width; /* right corner */
1282 points [npoints++].y = y + half_width;
1283 points [npoints].x = x + width - thickness; /* right inside corner */
1284 points [npoints++].y = y + half_width;
1285 points [npoints].x = x + half_width; /* bottom inside corner */
1286 points [npoints++].y = y + width - thickness;
1287 points [npoints].x = x + thickness; /* left inside corner */
1288 points [npoints++].y = y + half_width;
1290 XFillPolygon (dpy, window, bottom_gc,
1291 points, npoints, Nonconvex, CoordModeOrigin);
1295 points [npoints].x = x; /* left corner */
1296 points [npoints++].y = y + half_width;
1297 points [npoints].x = x + half_width; /* top corner */
1298 points [npoints++].y = y;
1299 points [npoints].x = x + width; /* right corner */
1300 points [npoints++].y = y + half_width;
1301 points [npoints].x = x + width - thickness; /* right inside corner */
1302 points [npoints++].y = y + half_width;
1303 points [npoints].x = x + half_width; /* top inside corner */
1304 points [npoints++].y = y + thickness;
1305 points [npoints].x = x + thickness; /* left inside corner */
1306 points [npoints++].y = y + half_width;
1308 XFillPolygon (dpy, window, top_gc, points, npoints, Nonconvex,
1313 /* Fill internal area. */
1316 points [0].x = x + thickness;
1317 points [0].y = y + half_width;
1318 points [1].x = x + half_width;
1319 points [1].y = y + thickness;
1320 points [2].x = x + width - thickness;
1321 points [2].y = y + half_width;
1322 points [3].x = x + half_width;
1323 points [3].y = y + width - thickness;
1335 separator_decoration_draw (XlwMenuWidget mw,
1342 Display *dpy = XtDisplay (mw);
1345 unsigned int offset = 0;
1346 unsigned int num_separators = 1;
1347 unsigned int top_line_thickness = 0;
1348 unsigned int bottom_line_thickness = 0;
1349 Boolean dashed = False;
1353 case SHADOW_NO_LINE: /* nothing to do */
1355 case SHADOW_DOUBLE_LINE:
1357 case SHADOW_SINGLE_LINE:
1358 top_gc = bottom_gc = mw->menu.foreground_gc;
1359 top_line_thickness = 1;
1361 case SHADOW_DOUBLE_DASHED_LINE:
1363 case SHADOW_SINGLE_DASHED_LINE:
1364 top_gc = bottom_gc = mw->menu.foreground_gc;
1365 top_line_thickness = 1;
1368 case SHADOW_DOUBLE_ETCHED_OUT_DASH:
1370 case SHADOW_ETCHED_OUT_DASH:
1371 top_gc = mw->menu.shadow_top_gc;
1372 bottom_gc = mw->menu.shadow_bottom_gc;
1373 top_line_thickness = mw->menu.shadow_thickness/2;
1374 bottom_line_thickness = mw->menu.shadow_thickness - top_line_thickness;
1377 case SHADOW_DOUBLE_ETCHED_IN_DASH:
1379 case SHADOW_ETCHED_IN_DASH:
1380 top_gc = mw->menu.shadow_bottom_gc;
1381 bottom_gc = mw->menu.shadow_top_gc;
1382 top_line_thickness = mw->menu.shadow_thickness/2;
1383 bottom_line_thickness = mw->menu.shadow_thickness - top_line_thickness;
1386 case SHADOW_DOUBLE_ETCHED_OUT:
1388 case SHADOW_ETCHED_OUT:
1389 top_gc = mw->menu.shadow_top_gc;
1390 bottom_gc = mw->menu.shadow_bottom_gc;
1391 top_line_thickness = mw->menu.shadow_thickness/2;
1392 bottom_line_thickness = mw->menu.shadow_thickness - top_line_thickness;
1394 case SHADOW_DOUBLE_ETCHED_IN:
1396 case SHADOW_ETCHED_IN:
1398 top_gc = mw->menu.shadow_bottom_gc;
1399 bottom_gc = mw->menu.shadow_top_gc;
1400 top_line_thickness = mw->menu.shadow_thickness/2;
1401 bottom_line_thickness = mw->menu.shadow_thickness - top_line_thickness;
1408 values.line_style = LineOnOffDash;
1409 if (top_line_thickness > 0)
1410 XChangeGC (dpy, top_gc, GCLineStyle, &values);
1411 if (bottom_line_thickness > 0 && bottom_gc != top_gc)
1412 XChangeGC (dpy, bottom_gc, GCLineStyle, &values);
1415 while (num_separators--)
1418 for (i = 0; i < top_line_thickness; i++)
1419 XDrawLine (dpy, window, top_gc, x, y + i, x + width, y + i);
1421 for (i = 0; i < bottom_line_thickness; i++)
1422 XDrawLine (dpy, window, bottom_gc,
1423 x, y + top_line_thickness + offset + i,
1424 x + width, y + top_line_thickness + offset + i);
1425 y += (top_line_thickness + offset + bottom_line_thickness + 1);
1431 values.line_style = LineSolid;
1432 if (top_line_thickness > 0)
1433 XChangeGC (dpy, top_gc, GCLineStyle, &values);
1434 if (bottom_line_thickness > 0 && bottom_gc != top_gc)
1435 XChangeGC (dpy, bottom_gc, GCLineStyle, &values);
1439 #define SLOPPY_TYPES 0 /* 0=off, 1=error check, 2=easy to please */
1441 #if SLOPPY_TYPES < 2
1443 static char *wv_types[] =
1457 print_widget_value (widget_value *wv, int just_one, int depth)
1461 for (i = 0; i < depth; i++)
1466 printf ("%s(null widget value pointer)\n", d);
1469 printf ("%stype: %s\n", d, wv_types [wv->type]);
1471 printf ("%sname: %s\n", d, (wv->name ? wv->name : "(null)"));
1473 if (wv->name) printf ("%sname: %s\n", d, wv->name);
1475 if (wv->value) printf ("%svalue: %s\n", d, wv->value);
1476 if (wv->key) printf ("%skey: %s\n", d, wv->key);
1477 printf ("%senabled: %d\n", d, wv->enabled);
1480 printf ("\n%scontents: \n", d);
1481 print_widget_value (wv->contents, 0, depth + 5);
1483 if (!just_one && wv->next)
1486 print_widget_value (wv->next, 0, depth);
1489 #endif /* SLOPPY_TYPES < 2 */
1492 all_dashes_p (char *s)
1495 if (!s || s[0] == '\0')
1497 for (p = s; *p == '-'; p++);
1499 if (*p == '!' || *p == '\0')
1503 #endif /* SLOPPY_TYPES */
1505 static widget_value_type
1506 menu_item_type (widget_value *val)
1508 if (val->type != UNSPECIFIED_TYPE)
1511 else if (all_dashes_p (val->name))
1512 return SEPARATOR_TYPE;
1513 else if (val->name && val->name[0] == '\0') /* push right */
1514 return PUSHRIGHT_TYPE;
1515 else if (val->contents) /* cascade */
1516 return CASCADE_TYPE;
1517 else if (val->call_data) /* push button */
1524 return UNSPECIFIED_TYPE; /* Not reached */
1529 label_button_size (XlwMenuWidget mw,
1532 unsigned int *toggle_width,
1533 unsigned int *label_width,
1534 unsigned int *bindings_width,
1535 unsigned int *height)
1537 *height = (mw->menu.font_ascent + mw->menu.font_descent +
1538 2 * mw->menu.vertical_margin +
1539 2 * mw->menu.shadow_thickness);
1540 /* no left column decoration */
1541 *toggle_width = mw->menu.horizontal_margin + mw->menu.shadow_thickness;
1543 *label_width = string_width_u (mw, resource_widget_value (mw, val));
1544 *bindings_width = mw->menu.horizontal_margin + mw->menu.shadow_thickness;
1548 label_button_draw (XlwMenuWidget mw,
1551 Boolean highlighted,
1555 unsigned int height,
1556 unsigned int label_offset,
1557 unsigned int binding_tab)
1559 int y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin;
1563 label_offset = mw->menu.shadow_thickness + mw->menu.horizontal_margin;
1565 if (highlighted && (in_menubar || val->contents))
1566 gc = mw->menu.highlight_gc;
1567 else if (in_menubar || val->contents)
1568 gc = mw->menu.foreground_gc;
1570 gc = mw->menu.title_gc;
1572 /* Draw the label string. */
1575 x + label_offset, y + y_offset,
1577 resource_widget_value (mw, val));
1581 push_button_size (XlwMenuWidget mw,
1584 unsigned int *toggle_width,
1585 unsigned int *label_width,
1586 unsigned int *bindings_width,
1587 unsigned int *height)
1590 label_button_size (mw, val, in_menubar,
1591 toggle_width, label_width, bindings_width,
1594 /* key bindings to display? */
1595 if (!in_menubar && val->key)
1599 XmString key = XmStringCreateLtoR (val->key, XmSTRING_DEFAULT_CHARSET);
1600 w = string_width (mw, key);
1603 char *key = val->key;
1604 w = string_width (mw, key);
1606 *bindings_width += w + mw->menu.column_spacing;
1611 push_button_draw (XlwMenuWidget mw,
1614 Boolean highlighted,
1618 unsigned int height,
1619 unsigned int label_offset,
1620 unsigned int binding_offset)
1622 int y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin;
1625 Boolean menu_pb = in_menubar && (menu_item_type (val) == BUTTON_TYPE);
1627 /* Draw the label string. */
1629 label_offset = mw->menu.shadow_thickness + mw->menu.horizontal_margin;
1634 gc = mw->menu.highlight_gc;
1636 gc = mw->menu.inactive_gc;
1641 gc = mw->menu.button_gc;
1643 gc = mw->menu.inactive_button_gc;
1648 gc = mw->menu.foreground_gc;
1650 gc = mw->menu.inactive_gc;
1655 x + label_offset, y + y_offset,
1657 resource_widget_value (mw, val));
1659 /* Draw the keybindings */
1662 if (!binding_offset)
1664 unsigned int s_width =
1665 string_width (mw, resource_widget_value (mw, val));
1666 binding_offset = label_offset + s_width + mw->menu.shadow_thickness;
1668 binding_draw (mw, window,
1669 x + binding_offset + mw->menu.column_spacing,
1670 y + y_offset, gc, val->key);
1673 /* Draw the shadow */
1679 type = (val->selected ? SHADOW_ETCHED_OUT : SHADOW_ETCHED_IN);
1686 type = SHADOW_BACKGROUND;
1689 shadow_draw (mw, window, x, y, width, height, type);
1693 arrow_decoration_height (XlwMenuWidget mw)
1695 int result = (mw->menu.font_ascent + mw->menu.font_descent) / 2;
1697 result += 2 * mw->menu.shadow_thickness;
1699 if (result > (mw->menu.font_ascent + mw->menu.font_descent))
1700 result = mw->menu.font_ascent + mw->menu.font_descent;
1706 cascade_button_size (XlwMenuWidget mw,
1709 unsigned int *toggle_width,
1710 unsigned int *label_width,
1711 unsigned int *arrow_width,
1712 unsigned int *height)
1715 label_button_size (mw, val, in_menubar,
1716 toggle_width, label_width, arrow_width,
1718 /* we have a pull aside arrow */
1721 *arrow_width += arrow_decoration_height (mw) + mw->menu.column_spacing;
1726 cascade_button_draw (XlwMenuWidget mw,
1729 Boolean highlighted,
1733 unsigned int height,
1734 unsigned int label_offset,
1735 unsigned int binding_offset)
1739 /* Draw the label string. */
1740 label_button_draw (mw, val, in_menubar, highlighted,
1741 window, x, y, width, height, label_offset,
1744 /* Draw the pull aside arrow */
1745 if (!in_menubar && val->contents)
1748 unsigned int arrow_height = arrow_decoration_height (mw);
1750 y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin +
1751 (mw->menu.font_ascent+mw->menu.font_descent - arrow_height)/2;
1753 if (!binding_offset)
1755 unsigned int s_width =
1756 string_width (mw, resource_widget_value (mw, val));
1759 label_offset = mw->menu.shadow_thickness +
1760 mw->menu.horizontal_margin;
1762 binding_offset = label_offset + s_width + mw->menu.shadow_thickness;
1765 arrow_decoration_draw (mw,
1767 x + binding_offset + mw->menu.column_spacing,
1773 /* Draw the shadow */
1777 type = SHADOW_BACKGROUND;
1779 shadow_draw (mw, window, x, y, width, height, type);
1783 toggle_decoration_height (XlwMenuWidget mw)
1786 if (mw->menu.indicator_size > 0)
1787 rv = mw->menu.indicator_size;
1789 rv = mw->menu.font_ascent;
1791 if (rv > (mw->menu.font_ascent + mw->menu.font_descent))
1792 rv = mw->menu.font_ascent + mw->menu.font_descent;
1794 /* radio button can't be smaller than its border or a filling
1795 error will occur. */
1796 if (rv < 2 * mw->menu.shadow_thickness)
1797 rv = 2 * mw->menu.shadow_thickness;
1803 toggle_button_size (XlwMenuWidget mw,
1806 unsigned int *toggle_width,
1807 unsigned int *label_width,
1808 unsigned int *bindings_width,
1809 unsigned int *height)
1812 push_button_size (mw, val, in_menubar,
1813 toggle_width, label_width, bindings_width,
1815 /* we have a toggle */
1816 *toggle_width += toggle_decoration_height (mw) + mw->menu.column_spacing;
1820 toggle_button_draw (XlwMenuWidget mw,
1823 Boolean highlighted,
1827 unsigned int height,
1828 unsigned int label_tab,
1829 unsigned int binding_tab)
1833 unsigned int t_height = toggle_decoration_height (mw);
1835 /* Draw a toggle. */
1836 x_offset = mw->menu.shadow_thickness + mw->menu.horizontal_margin;
1837 y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin;
1838 y_offset += (mw->menu.font_ascent + mw->menu.font_descent - t_height)/2;
1840 toggle_decoration_draw (mw, window, x + x_offset, y + y_offset,
1841 t_height, val->selected);
1843 /* Draw the pushbutton parts. */
1844 push_button_draw (mw, val, in_menubar, highlighted, window, x, y, width,
1845 height, label_tab, binding_tab);
1849 radio_decoration_height (XlwMenuWidget mw)
1851 return toggle_decoration_height (mw);
1855 radio_button_draw (XlwMenuWidget mw,
1858 Boolean highlighted,
1862 unsigned int height,
1863 unsigned int label_tab,
1864 unsigned int binding_tab)
1868 unsigned int r_height = radio_decoration_height (mw);
1870 /* Draw a toggle. */
1871 x_offset = mw->menu.shadow_thickness + mw->menu.horizontal_margin;
1872 y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin;
1873 y_offset += (mw->menu.font_ascent + mw->menu.font_descent - r_height)/2;
1875 radio_decoration_draw (mw, window, x + x_offset, y + y_offset, r_height,
1878 /* Draw the pushbutton parts. */
1879 push_button_draw (mw, val, in_menubar, highlighted, window, x, y, width,
1880 height, label_tab, binding_tab);
1883 static struct _shadow_names
1890 { "singleLine", SHADOW_SINGLE_LINE },
1891 { "doubleLine", SHADOW_DOUBLE_LINE },
1892 { "singleDashedLine", SHADOW_SINGLE_DASHED_LINE },
1893 { "doubleDashedLine", SHADOW_DOUBLE_DASHED_LINE },
1894 { "noLine", SHADOW_NO_LINE },
1895 { "shadowEtchedIn", SHADOW_ETCHED_IN },
1896 { "shadowEtchedOut", SHADOW_ETCHED_OUT },
1897 { "shadowEtchedInDash", SHADOW_ETCHED_IN_DASH },
1898 { "shadowEtchedOutDash", SHADOW_ETCHED_OUT_DASH },
1900 { "shadowDoubleEtchedIn", SHADOW_DOUBLE_ETCHED_IN },
1901 { "shadowDoubleEtchedOut", SHADOW_DOUBLE_ETCHED_OUT },
1902 { "shadowDoubleEtchedInDash", SHADOW_DOUBLE_ETCHED_IN_DASH },
1903 { "shadowDoubleEtchedOutDash", SHADOW_DOUBLE_ETCHED_OUT_DASH }
1907 separator_type (char *name)
1912 for (i = 0; i < (int) (XtNumber (shadow_names)); i++ )
1914 if (strcmp (name, shadow_names[i].name) == 0)
1915 return shadow_names[i].type;
1918 return SHADOW_BACKGROUND;
1922 separator_decoration_height (XlwMenuWidget mw, widget_value *val)
1925 switch (separator_type (val->value))
1927 case SHADOW_NO_LINE:
1928 case SHADOW_SINGLE_LINE:
1929 case SHADOW_SINGLE_DASHED_LINE:
1931 case SHADOW_DOUBLE_LINE:
1932 case SHADOW_DOUBLE_DASHED_LINE:
1934 case SHADOW_DOUBLE_ETCHED_OUT:
1935 case SHADOW_DOUBLE_ETCHED_IN:
1936 case SHADOW_DOUBLE_ETCHED_OUT_DASH:
1937 case SHADOW_DOUBLE_ETCHED_IN_DASH:
1938 return (1 + 2 * mw->menu.shadow_thickness);
1939 case SHADOW_ETCHED_OUT:
1940 case SHADOW_ETCHED_IN:
1942 return mw->menu.shadow_thickness;
1947 separator_size (XlwMenuWidget mw,
1950 unsigned int *toggle_width,
1951 unsigned int *label_width,
1952 unsigned int *rest_width,
1953 unsigned int *height)
1955 *height = separator_decoration_height (mw, val);
1957 *toggle_width = *rest_width = 0;
1961 separator_draw (XlwMenuWidget mw,
1964 Boolean highlighted,
1968 unsigned int height,
1969 unsigned int label_tab,
1970 unsigned int binding_tab)
1972 unsigned int sep_width;
1979 separator_decoration_draw (mw,
1985 separator_type(val->value));
1989 pushright_size (XlwMenuWidget mw,
1992 unsigned int *toggle_width,
1993 unsigned int *label_width,
1994 unsigned int *rest_width,
1995 unsigned int *height)
1997 *height = *label_width = *toggle_width = *rest_width = 0;
2001 size_menu_item (XlwMenuWidget mw,
2004 unsigned int *toggle_width,
2005 unsigned int *label_width,
2006 unsigned int *rest_width,
2007 unsigned int *height)
2009 void (*function_ptr) (XlwMenuWidget _mw,
2011 Boolean _in_menubar,
2012 unsigned int *_toggle_width,
2013 unsigned int *_label_width,
2014 unsigned int *_rest_width,
2015 unsigned int *_height);
2017 switch (menu_item_type (val))
2021 function_ptr = toggle_button_size;
2023 case SEPARATOR_TYPE:
2024 function_ptr = separator_size;
2026 case INCREMENTAL_TYPE:
2028 function_ptr = cascade_button_size;
2031 function_ptr = push_button_size;
2033 case PUSHRIGHT_TYPE:
2034 function_ptr = pushright_size;
2038 function_ptr = label_button_size;
2042 (*function_ptr) (mw,
2052 display_menu_item (XlwMenuWidget mw,
2056 Boolean highlighted,
2058 Boolean just_compute)
2061 int x = where->x /* + mw->menu.shadow_thickness */ ;
2062 int y = where->y /* + mw->menu.shadow_thickness */ ;
2063 unsigned int toggle_width;
2064 unsigned int label_width;
2065 unsigned int binding_width;
2067 unsigned int height;
2068 unsigned int label_tab;
2069 unsigned int binding_tab;
2070 void (*function_ptr) (XlwMenuWidget _mw,
2072 Boolean _in_menubar,
2073 Boolean _highlighted,
2076 unsigned int _width,
2077 unsigned int _height,
2078 unsigned int _label_tab,
2079 unsigned int _binding_tab);
2081 size_menu_item (mw, val, horizontal,
2082 &toggle_width, &label_width, &binding_width, &height);
2086 width = toggle_width + label_width + binding_width;
2087 height = ws->height - 2 * mw->menu.shadow_thickness;
2091 width = ws->width - 2 * mw->menu.shadow_thickness;
2092 toggle_width = ws->toggle_width;
2093 label_width = ws->label_width;
2102 label_tab = toggle_width;
2103 binding_tab = toggle_width + label_width;
2105 switch (menu_item_type (val))
2108 function_ptr = toggle_button_draw;
2111 function_ptr = radio_button_draw;
2113 case SEPARATOR_TYPE:
2114 function_ptr = separator_draw;
2116 case INCREMENTAL_TYPE:
2118 function_ptr = cascade_button_draw;
2121 function_ptr = push_button_draw;
2124 function_ptr = label_button_draw;
2126 default: /* do no drawing */
2130 (*function_ptr) (mw,
2142 size_menu (XlwMenuWidget mw, int level)
2144 unsigned int toggle_width;
2145 unsigned int label_width;
2146 unsigned int rest_width;
2147 unsigned int height;
2148 unsigned int max_toggle_width = 0;
2149 unsigned int max_label_width = 0;
2150 unsigned int max_rest_width = 0;
2151 unsigned int max_height = 0;
2152 int horizontal_p = mw->menu.horizontal && (level == 0);
2156 if (level >= mw->menu.old_depth)
2159 ws = &mw->menu.windows [level];
2161 for (val = mw->menu.old_stack [level]->contents; val; val = val->next)
2172 max_label_width += toggle_width + label_width + rest_width;
2173 if (height > max_height)
2174 max_height = height;
2178 if (max_toggle_width < toggle_width)
2179 max_toggle_width = toggle_width;
2180 if (max_label_width < label_width)
2181 max_label_width = label_width;
2182 if (max_rest_width < rest_width)
2183 max_rest_width = rest_width;
2184 max_height += height;
2188 ws->height = max_height;
2189 ws->width = max_label_width + max_rest_width + max_toggle_width;
2190 ws->toggle_width = max_toggle_width;
2191 ws->label_width = max_label_width;
2193 ws->width += 2 * mw->menu.shadow_thickness;
2194 ws->height += 2 * mw->menu.shadow_thickness;
2198 display_menu (XlwMenuWidget mw, int level, Boolean just_compute_p,
2199 XPoint *highlighted_pos, XPoint *hit, widget_value **hit_return,
2200 widget_value *this, widget_value *that)
2203 widget_value *following_item;
2206 int horizontal_p = mw->menu.horizontal && (level == 0);
2208 int just_compute_this_one_p;
2210 if (level >= mw->menu.old_depth)
2213 if (level < mw->menu.old_depth - 1)
2214 following_item = mw->menu.old_stack [level + 1];
2217 if (lw_menu_accelerate
2218 && level == mw->menu.old_depth - 1
2219 && mw->menu.old_stack [level]->type == CASCADE_TYPE)
2220 just_compute_p = True;
2221 following_item = NULL;
2224 #if SLOPPY_TYPES == 1
2225 puts("===================================================================");
2226 print_widget_value (following_item, 1, 0);
2232 where.x = mw->menu.shadow_thickness;
2233 where.y = mw->menu.shadow_thickness;
2235 ws = &mw->menu.windows [level];
2236 for (val = mw->menu.old_stack [level]->contents; val; val = val->next)
2240 highlighted_p = (val == following_item);
2241 /* If this is the partition (the dummy item which says that menus
2242 after this should be flushright) then figure out how big the
2243 following items are. This means we walk down the tail of the
2244 list twice, but that's no big deal - it's short.
2246 if (horizontal_p && (menu_item_type (val) == PUSHRIGHT_TYPE))
2249 XPoint flushright_size;
2251 flushright_size.x = 0;
2252 flushright_size.y = 0;
2253 for (rest = val; rest; rest = rest->next)
2254 display_menu_item (mw, rest, ws, &flushright_size,
2255 highlighted_p, horizontal_p, True);
2256 new_x = ws->width - (flushright_size.x + mw->menu.shadow_thickness);
2257 if (new_x > where.x)
2259 /* We know what we need; don't draw this item. */
2263 if (highlighted_p && highlighted_pos)
2266 highlighted_pos->x = where.x;
2268 highlighted_pos->y = where.y;
2271 just_compute_this_one_p =
2272 just_compute_p || ((this || that) && val != this && val != that);
2276 display_menu_item (mw, val, ws, &where, highlighted_p, horizontal_p,
2277 just_compute_this_one_p);
2279 if (highlighted_p && highlighted_pos)
2282 highlighted_pos->y = ws->height;
2284 highlighted_pos->x = ws->width;
2287 if (hit && !*hit_return)
2289 if (horizontal_p && hit->x > start.x && hit->x <= where.x)
2291 else if (!horizontal_p && hit->y > start.y && hit->y <= where.y)
2296 where.y = mw->menu.shadow_thickness;
2298 where.x = mw->menu.shadow_thickness;
2301 /* Draw slab edges around menu */
2302 if (!just_compute_p)
2303 shadow_draw(mw, ws->window, 0, 0, ws->width, ws->height, SHADOW_OUT);
2308 set_new_state (XlwMenuWidget mw, widget_value *val, int level)
2312 mw->menu.new_depth = 0;
2313 for (i = 0; i < level; i++)
2314 push_new_stack (mw, mw->menu.old_stack [i]);
2316 push_new_stack (mw, val);
2320 make_windows_if_needed (XlwMenuWidget mw, int n)
2324 XSetWindowAttributes xswa;
2329 window_state *windows;
2332 if (mw->menu.windows_length >= n)
2335 root = RootWindowOfScreen (XtScreen(mw));
2336 /* grab the visual and depth from the nearest shell ancestor */
2337 visual = CopyFromParent;
2338 depth = CopyFromParent;
2340 while (visual == CopyFromParent && p)
2344 visual = ((ShellWidget)p)->shell.visual;
2345 depth = p->core.depth;
2350 xswa.save_under = True;
2351 xswa.override_redirect = True;
2352 xswa.background_pixel = mw->core.background_pixel;
2353 xswa.border_pixel = mw->core.border_pixel;
2354 xswa.event_mask = (ExposureMask | ButtonMotionMask
2355 | ButtonReleaseMask | ButtonPressMask);
2356 xswa.cursor = mw->menu.cursor_shape;
2357 xswa.colormap = mw->core.colormap;
2358 mask = CWSaveUnder | CWOverrideRedirect | CWBackPixel | CWBorderPixel
2359 | CWEventMask | CWCursor | CWColormap;
2361 if (mw->menu.use_backing_store)
2363 xswa.backing_store = Always;
2364 mask |= CWBackingStore;
2367 if (!mw->menu.windows)
2370 (window_state *) XtMalloc (n * sizeof (window_state));
2376 (window_state *) XtRealloc ((char *) mw->menu.windows,
2377 n * sizeof (window_state));
2378 start_at = mw->menu.windows_length;
2380 mw->menu.windows_length = n;
2382 windows = mw->menu.windows;
2384 for (i = start_at; i < n; i++)
2388 windows [i].width = 1;
2389 windows [i].height = 1;
2390 windows [i].window =
2391 XCreateWindow (XtDisplay (mw),
2394 0, depth, CopyFromParent, visual, mask, &xswa);
2398 /* Make the window fit in the screen */
2400 fit_to_screen (XlwMenuWidget mw, window_state *ws, window_state *previous_ws,
2401 Boolean horizontal_p)
2403 int screen_width = WidthOfScreen (XtScreen (mw));
2404 int screen_height = HeightOfScreen (XtScreen (mw));
2408 else if ((int) (ws->x + ws->width) > screen_width)
2411 ws->x = previous_ws->x - ws->width;
2414 ws->x = screen_width - ws->width;
2416 /* This check is to make sure we cut off the right side
2417 instead of the left side if the menu is wider than the
2425 else if ((int) (ws->y + ws->height) > screen_height)
2429 /* A pulldown must either be entirely above or below the menubar.
2430 If we're here, the pulldown doesn't fit below the menubar, so
2431 let's determine if it will fit above the menubar.
2432 Only put it above if there is more room above than below.
2433 Note shadow_thickness offset to allow for slab surround.
2435 if (ws->y > (screen_height / 2))
2436 ws->y = previous_ws->y - ws->height + mw->menu.shadow_thickness;
2440 ws->y = screen_height - ws->height;
2441 /* if it's taller than the screen, display the topmost part
2442 that will fit, beginning at the top of the screen. */
2449 /* Updates old_stack from new_stack and redisplays. */
2451 remap_menubar (XlwMenuWidget mw)
2455 XPoint selection_position;
2456 int old_depth = mw->menu.old_depth;
2457 int new_depth = mw->menu.new_depth;
2458 widget_value **old_stack;
2459 widget_value **new_stack;
2460 window_state *windows;
2461 widget_value *old_selection;
2462 widget_value *new_selection;
2464 /* Check that enough windows and old_stack are ready. */
2465 make_windows_if_needed (mw, new_depth);
2466 make_old_stack_space (mw, new_depth);
2467 windows = mw->menu.windows;
2468 old_stack = mw->menu.old_stack;
2469 new_stack = mw->menu.new_stack;
2471 /* compute the last identical different entry */
2472 for (i = 1; i < old_depth && i < new_depth; i++)
2473 if (old_stack [i] != new_stack [i])
2477 if (lw_menu_accelerate
2479 && last_same == old_depth - 1
2480 && old_stack [last_same]->contents)
2483 /* Memorize the previously selected item to be able to refresh it */
2484 old_selection = last_same + 1 < old_depth ? old_stack [last_same + 1] : NULL;
2485 new_selection = last_same + 1 < new_depth ? new_stack [last_same + 1] : NULL;
2487 /* updates old_state from new_state. It has to be done now because
2488 display_menu (called below) uses the old_stack to know what to display. */
2489 for (i = last_same + 1; i < new_depth; i++)
2490 old_stack [i] = new_stack [i];
2492 mw->menu.old_depth = new_depth;
2494 /* refresh the last seletion */
2495 selection_position.x = 0;
2496 selection_position.y = 0;
2497 display_menu (mw, last_same, new_selection == old_selection,
2498 &selection_position, NULL, NULL, old_selection, new_selection);
2500 /* Now popup the new menus */
2501 for (i = last_same + 1; i < new_depth && new_stack [i]->contents; i++)
2503 window_state *previous_ws = &windows [i - 1];
2504 window_state *ws = &windows [i];
2506 if (lw_menu_accelerate && i == new_depth - 1)
2509 ws->x = previous_ws->x + selection_position.x;
2510 ws->y = previous_ws->y + selection_position.y;
2512 /* take into account the slab around the new menu */
2513 ws->y -= mw->menu.shadow_thickness;
2516 widget_value *val = mw->menu.old_stack [i];
2517 if (val->contents->type == INCREMENTAL_TYPE)
2519 /* okay, we're now doing a lisp callback to incrementally generate
2520 more of the menu. */
2521 XtCallCallbackList ((Widget)mw,
2523 (XtPointer)val->contents);
2529 fit_to_screen (mw, ws, previous_ws, mw->menu.horizontal && i == 1);
2531 XClearWindow (XtDisplay (mw), ws->window);
2532 XMoveResizeWindow (XtDisplay (mw), ws->window, ws->x, ws->y,
2533 ws->width, ws->height);
2534 XMapRaised (XtDisplay (mw), ws->window);
2535 display_menu (mw, i, False, &selection_position, NULL, NULL, NULL, NULL);
2538 /* unmap the menus that popped down */
2540 last_same = new_depth;
2541 if (lw_menu_accelerate
2543 && new_stack [last_same - 1]->contents)
2546 for (i = last_same - 1; i < old_depth; i++)
2547 if (i >= last_same || !new_stack [i]->contents)
2548 XUnmapWindow (XtDisplay (mw), windows [i].window);
2552 motion_event_is_in_menu (XlwMenuWidget mw, XMotionEvent *ev, int level,
2553 XPoint *relative_pos)
2555 window_state *ws = &mw->menu.windows [level];
2556 int x = level == 0 ? ws->x : ws->x + mw->menu.shadow_thickness;
2557 int y = level == 0 ? ws->y : ws->y + mw->menu.shadow_thickness;
2558 relative_pos->x = ev->x_root - x;
2559 relative_pos->y = ev->y_root - y;
2560 return (x < ev->x_root && ev->x_root < (int) (x + ws->width) &&
2561 y < ev->y_root && ev->y_root < (int) (y + ws->height));
2565 map_event_to_widget_value (XlwMenuWidget mw, XMotionEvent *ev,
2566 widget_value **val_ptr, int *level,
2567 Boolean *inside_menu)
2570 XPoint relative_pos;
2574 *inside_menu = False;
2576 /* Find the window */
2578 for (i = mw->menu.old_depth - 1; i >= 0; i--)
2580 for (i = 0; i <= mw->menu.old_depth - 1; i++)
2583 ws = &mw->menu.windows [i];
2584 if (ws && motion_event_is_in_menu (mw, ev, i, &relative_pos))
2586 *inside_menu = True; /* special logic for menubar below... */
2587 if ((ev->type == ButtonPress) ||
2590 display_menu (mw, i, True, NULL, &relative_pos,
2591 val_ptr, NULL, NULL);
2595 *inside_menu = True;
2598 else if (mw->menu.horizontal || i == 0)
2600 /* if we're clicking on empty part of the menubar, then
2601 unpost the stay-up menu */
2602 *inside_menu = False;
2612 make_drawing_gcs (XlwMenuWidget mw)
2615 unsigned long flags = (GCFont | GCForeground | GCBackground);
2618 xgcv.font = default_font_of_font_list (mw->menu.font_list)->fid;
2620 xgcv.font = mw->menu.font->fid;
2623 xgcv.foreground = mw->core.background_pixel;
2624 xgcv.background = mw->menu.foreground;
2625 mw->menu.background_gc = XtGetGC ((Widget) mw, flags, &xgcv);
2627 xgcv.foreground = mw->menu.foreground;
2628 xgcv.background = mw->core.background_pixel;
2629 mw->menu.foreground_gc = XtGetGC ((Widget) mw, flags, &xgcv);
2631 if (mw->menu.select_color != (Pixel)-1)
2633 xgcv.foreground = mw->menu.select_color;
2637 Display *dpy = XtDisplay(mw);
2638 if (CellsOfScreen(DefaultScreenOfDisplay(dpy)) <= 2)
2640 xgcv.foreground = mw->menu.foreground;
2645 Colormap cmap = mw->core.colormap;
2646 xcolor.pixel = mw->core.background_pixel;
2647 XQueryColor (dpy, cmap, &xcolor);
2648 xcolor.red = (xcolor.red * 17) / 20;
2649 xcolor.green = (xcolor.green * 17) / 20;
2650 xcolor.blue = (xcolor.blue * 17) / 20;
2651 if (allocate_nearest_color (dpy, cmap, &xcolor))
2652 xgcv.foreground = xcolor.pixel;
2655 xgcv.background = mw->core.background_pixel;
2656 mw->menu.select_gc = XtGetGC ((Widget)mw, flags, &xgcv);
2658 xgcv.foreground = mw->menu.foreground;
2659 xgcv.background = mw->core.background_pixel;
2660 xgcv.fill_style = FillStippled;
2661 xgcv.stipple = mw->menu.gray_pixmap;
2662 mw->menu.inactive_gc = XtGetGC ((Widget)mw,
2663 (flags | GCFillStyle | GCStipple),
2666 xgcv.foreground = mw->menu.highlight_foreground;
2667 xgcv.background = mw->core.background_pixel;
2668 mw->menu.highlight_gc = XtGetGC ((Widget)mw, flags, &xgcv);
2670 xgcv.foreground = mw->menu.title_foreground;
2671 xgcv.background = mw->core.background_pixel;
2672 mw->menu.title_gc = XtGetGC ((Widget)mw, flags, &xgcv);
2674 xgcv.foreground = mw->menu.button_foreground;
2675 xgcv.background = mw->core.background_pixel;
2676 mw->menu.button_gc = XtGetGC ((Widget)mw, flags, &xgcv);
2678 xgcv.fill_style = FillStippled;
2679 xgcv.stipple = mw->menu.gray_pixmap;
2680 mw->menu.inactive_button_gc = XtGetGC ((Widget)mw,
2681 (flags | GCFillStyle | GCStipple),
2686 release_drawing_gcs (XlwMenuWidget mw)
2688 XtReleaseGC ((Widget) mw, mw->menu.foreground_gc);
2689 XtReleaseGC ((Widget) mw, mw->menu.button_gc);
2690 XtReleaseGC ((Widget) mw, mw->menu.highlight_gc);
2691 XtReleaseGC ((Widget) mw, mw->menu.title_gc);
2692 XtReleaseGC ((Widget) mw, mw->menu.inactive_gc);
2693 XtReleaseGC ((Widget) mw, mw->menu.inactive_button_gc);
2694 XtReleaseGC ((Widget) mw, mw->menu.background_gc);
2695 XtReleaseGC ((Widget) mw, mw->menu.select_gc);
2696 /* let's get some segvs if we try to use these... */
2697 mw->menu.foreground_gc = (GC) -1;
2698 mw->menu.button_gc = (GC) -1;
2699 mw->menu.highlight_gc = (GC) -1;
2700 mw->menu.title_gc = (GC) -1;
2701 mw->menu.inactive_gc = (GC) -1;
2702 mw->menu.inactive_button_gc = (GC) -1;
2703 mw->menu.background_gc = (GC) -1;
2704 mw->menu.select_gc = (GC) -1;
2707 #define MINL(x,y) ((((unsigned long) (x)) < ((unsigned long) (y))) \
2708 ? ((unsigned long) (x)) : ((unsigned long) (y)))
2711 make_shadow_gcs (XlwMenuWidget mw)
2714 unsigned long pm = 0;
2715 Display *dpy = XtDisplay ((Widget) mw);
2716 Colormap cmap = mw->core.colormap;
2718 int top_frobbed = 0, bottom_frobbed = 0;
2720 if (mw->menu.top_shadow_color == (Pixel) (-1))
2721 mw->menu.top_shadow_color = mw->core.background_pixel;
2722 if (mw->menu.bottom_shadow_color == (Pixel) (-1))
2723 mw->menu.bottom_shadow_color = mw->menu.foreground;
2725 if (mw->menu.top_shadow_color == mw->core.background_pixel ||
2726 mw->menu.top_shadow_color == mw->menu.foreground)
2728 topc.pixel = mw->core.background_pixel;
2729 XQueryColor (dpy, cmap, &topc);
2730 /* don't overflow/wrap! */
2731 topc.red = MINL (65535, topc.red * 1.2);
2732 topc.green = MINL (65535, topc.green * 1.2);
2733 topc.blue = MINL (65535, topc.blue * 1.2);
2734 if (allocate_nearest_color (dpy, cmap, &topc))
2736 if (topc.pixel == mw->core.background_pixel)
2738 XFreeColors( dpy, cmap, &topc.pixel, 1, 0);
2739 topc.red = MINL (65535, topc.red + 0x8000);
2740 topc.green = MINL (65535, topc.green + 0x8000);
2741 topc.blue = MINL (65535, topc.blue + 0x8000);
2742 if (allocate_nearest_color (dpy, cmap, &topc))
2744 mw->menu.top_shadow_color = topc.pixel;
2749 mw->menu.top_shadow_color = topc.pixel;
2755 if (mw->menu.bottom_shadow_color == mw->menu.foreground ||
2756 mw->menu.bottom_shadow_color == mw->core.background_pixel)
2758 botc.pixel = mw->core.background_pixel;
2759 XQueryColor (dpy, cmap, &botc);
2760 botc.red = (botc.red * 3) / 5;
2761 botc.green = (botc.green * 3) / 5;
2762 botc.blue = (botc.blue * 3) / 5;
2763 if (allocate_nearest_color (dpy, cmap, &botc))
2765 if (botc.pixel == mw->core.background_pixel)
2767 XFreeColors (dpy, cmap, &botc.pixel, 1, 0);
2768 botc.red = MINL (65535, botc.red + 0x4000);
2769 botc.green = MINL (65535, botc.green + 0x4000);
2770 botc.blue = MINL (65535, botc.blue + 0x4000);
2771 if (allocate_nearest_color (dpy, cmap, &botc))
2773 mw->menu.bottom_shadow_color = botc.pixel;
2778 mw->menu.bottom_shadow_color = botc.pixel;
2785 if (top_frobbed && bottom_frobbed)
2787 int top_avg = ((topc.red / 3) + (topc.green / 3) + (topc.blue / 3));
2788 int bot_avg = ((botc.red / 3) + (botc.green / 3) + (botc.blue / 3));
2789 if (bot_avg > top_avg)
2791 Pixel tmp = mw->menu.top_shadow_color;
2792 mw->menu.top_shadow_color = mw->menu.bottom_shadow_color;
2793 mw->menu.bottom_shadow_color = tmp;
2795 else if (topc.pixel == botc.pixel)
2797 if (botc.pixel == mw->menu.foreground)
2798 mw->menu.top_shadow_color = mw->core.background_pixel;
2800 mw->menu.bottom_shadow_color = mw->menu.foreground;
2804 if (!mw->menu.top_shadow_pixmap &&
2805 mw->menu.top_shadow_color == mw->core.background_pixel)
2807 mw->menu.top_shadow_pixmap = mw->menu.gray_pixmap;
2808 mw->menu.top_shadow_color = mw->menu.foreground;
2810 if (!mw->menu.bottom_shadow_pixmap &&
2811 mw->menu.bottom_shadow_color == mw->core.background_pixel)
2813 mw->menu.bottom_shadow_pixmap = mw->menu.gray_pixmap;
2814 mw->menu.bottom_shadow_color = mw->menu.foreground;
2817 xgcv.fill_style = FillOpaqueStippled;
2818 xgcv.foreground = mw->menu.top_shadow_color;
2819 xgcv.background = mw->core.background_pixel;
2820 /* xgcv.stipple = mw->menu.top_shadow_pixmap; gtb */
2821 if (mw->menu.top_shadow_pixmap &&
2822 mw->menu.top_shadow_pixmap != XmUNSPECIFIED_PIXMAP)
2823 xgcv.stipple = mw->menu.top_shadow_pixmap;
2826 pm = (xgcv.stipple ? GCStipple|GCFillStyle : 0);
2827 mw->menu.shadow_top_gc =
2828 XtGetGC((Widget)mw, GCForeground|GCBackground|pm, &xgcv);
2830 xgcv.foreground = mw->menu.bottom_shadow_color;
2831 /* xgcv.stipple = mw->menu.bottom_shadow_pixmap; gtb */
2832 if (mw->menu.bottom_shadow_pixmap &&
2833 mw->menu.bottom_shadow_pixmap != XmUNSPECIFIED_PIXMAP)
2834 xgcv.stipple = mw->menu.bottom_shadow_pixmap;
2837 pm = (xgcv.stipple ? GCStipple|GCFillStyle : 0);
2838 mw->menu.shadow_bottom_gc =
2839 XtGetGC ((Widget)mw, GCForeground|GCBackground|pm, &xgcv);
2844 release_shadow_gcs (XlwMenuWidget mw)
2846 XtReleaseGC ((Widget) mw, mw->menu.shadow_top_gc);
2847 XtReleaseGC ((Widget) mw, mw->menu.shadow_bottom_gc);
2852 extract_font_extents (XlwMenuWidget mw)
2855 /* Find the maximal ascent/descent of the fonts in the font list
2856 so that all menu items can be the same height... */
2857 mw->menu.font_ascent = 0;
2858 mw->menu.font_descent = 0;
2861 XmFontContext context;
2862 #if (XmVersion >= 1002)
2863 XmFontListEntry fontentry;
2865 XmStringCharSet charset;
2869 if (! XmFontListInitFontContext (&context, mw->menu.font_list))
2871 #if (XmVersion >= 1002)
2872 /* There is a BUG in the 1.2 version of XmFontListGetNextFont() (or more
2873 specifically, in _XmGetFirstFont()) that can cause a null pointer to be
2874 passed to XFontsOfFontSet. Use XmFontListNextEntry(), which is the
2875 newer equivalent, instead. Also, it supports font sets, and the
2876 older function doesn't. */
2877 while ((fontentry = XmFontListNextEntry (context)))
2881 XtPointer one_of_them = XmFontListEntryGetFont (fontentry, &rettype);
2882 if (rettype == XmFONT_IS_FONTSET)
2884 XFontSet fontset = (XFontSet) one_of_them;
2885 XFontStruct **fontstruct_list;
2886 char **fontname_list;
2887 int fontcount = XFontsOfFontSet (fontset, &fontstruct_list,
2889 while (--fontcount >= 0)
2891 font = fontstruct_list[fontcount];
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;
2898 else /* XmFONT_IS_FONT */
2900 font = (XFontStruct *) one_of_them;
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;
2907 #else /* motif 1.1 */
2908 while (XmFontListGetNextFont (context, &charset, &font))
2910 if (font->ascent > (int) mw->menu.font_ascent)
2911 mw->menu.font_ascent = font->ascent;
2912 if (font->descent > (int) mw->menu.font_descent)
2913 mw->menu.font_descent = font->descent;
2916 #endif /* Motif version */
2917 XmFontListFreeFontContext (context);
2919 #else /* Not Motif */
2920 # ifdef USE_XFONTSET
2921 XFontStruct **fontstruct_list;
2922 char **fontname_list;
2924 int fontcount = XFontsOfFontSet(mw->menu.font_set, &fontstruct_list,
2926 mw->menu.font_ascent = 0;
2927 mw->menu.font_descent = 0;
2928 # if 0 /* nasty, personal debug, Kazz */
2929 fprintf(stderr, "fontSet count is %d\n", fontcount);
2931 while (--fontcount >= 0) {
2932 font = fontstruct_list[fontcount];
2933 if (font->ascent > (int) mw->menu.font_ascent)
2934 mw->menu.font_ascent = font->ascent;
2935 if (font->descent > (int) mw->menu.font_descent)
2936 mw->menu.font_descent = font->descent;
2938 # else /* ! USE_XFONTSET */
2939 mw->menu.font_ascent = mw->menu.font->ascent;
2940 mw->menu.font_descent = mw->menu.font->descent;
2942 #endif /* NEED_MOTIF */
2946 static XFontStruct *
2947 default_font_of_font_list (XmFontList font_list)
2949 XFontStruct *font = 0;
2951 /* Xm/Label.c does this: */
2952 _XmFontListGetDefaultFont (font_list, &font);
2955 XmFontContext context;
2956 #if (XmVersion >= 1002)
2957 XmFontListEntry fontentry;
2959 XtPointer one_of_them;
2961 XmStringCharSet charset;
2964 if (! XmFontListInitFontContext (&context, font_list))
2966 #if (XmVersion >= 1002)
2967 /* There is a BUG in the 1.2 version of XmFontListGetNextFont() (or more
2968 specifically, in _XmGetFirstFont()) that can cause a null pointer to be
2969 passed to XFontsOfFontSet. Use XmFontListNextEntry(), which is the
2970 newer equivalent, instead. */
2971 fontentry = XmFontListNextEntry (context);
2972 one_of_them = XmFontListEntryGetFont (fontentry, &rettype);
2973 if (rettype == XmFONT_IS_FONTSET)
2975 XFontSet fontset = (XFontSet) one_of_them;
2976 XFontStruct **fontstruct_list;
2977 char **fontname_list;
2978 (void) XFontsOfFontSet (fontset, &fontstruct_list, &fontname_list);
2979 font = fontstruct_list[0];
2981 else /* XmFONT_IS_FONT */
2983 font = (XFontStruct *) one_of_them;
2986 if (! XmFontListGetNextFont (context, &charset, &font))
2990 XmFontListFreeFontContext (context);
2994 if (! font) abort ();
2997 #endif /* NEED_MOTIF */
3000 XlwMenuInitialize (Widget request, Widget new, ArgList args,
3003 /* Get the GCs and the widget size */
3004 XlwMenuWidget mw = (XlwMenuWidget)new;
3006 XSetWindowAttributes xswa;
3009 Window window = RootWindowOfScreen (DefaultScreenOfDisplay (XtDisplay (mw)));
3010 Display *display = XtDisplay (mw);
3012 /* mw->menu.cursor = XCreateFontCursor (display, mw->menu.cursor_shape); */
3013 mw->menu.cursor = mw->menu.cursor_shape;
3015 mw->menu.gray_pixmap =
3016 XCreatePixmapFromBitmapData (display, window, (char *) gray_bits,
3017 gray_width, gray_height, 1, 0, 1);
3020 /* The menu.font_list slot came from the *fontList resource (Motif standard.)
3021 The menu.font_list_2 slot came from the *font resource, for backward
3022 compatibility with older versions of this code, and consistency with the
3023 rest of emacs. If both font and fontList are specified, we use font.
3024 If only one is specified, we use that. If neither are specified, we
3025 use the "fallback" value. What a kludge!!!
3027 Note that this has the bug that a more general wildcard like "*fontList:"
3028 will override a more specific resource like "Emacs*menubar.font:". But
3029 I can't think of a way around that.
3031 if (mw->menu.font_list) /* if *fontList is specified, use that */
3033 else if (mw->menu.font_list_2) /* else if *font is specified, use that */
3034 mw->menu.font_list = mw->menu.font_list_2;
3035 else /* otherwise use default */
3036 mw->menu.font_list = mw->menu.fallback_font_list;
3039 make_drawing_gcs (mw);
3040 make_shadow_gcs (mw);
3041 extract_font_extents (mw);
3043 xswa.background_pixel = mw->core.background_pixel;
3044 xswa.border_pixel = mw->core.border_pixel;
3045 mask = CWBackPixel | CWBorderPixel;
3047 mw->menu.popped_up = False;
3048 mw->menu.pointer_grabbed = False;
3049 mw->menu.next_release_must_exit = False;
3051 mw->menu.old_depth = 1;
3052 mw->menu.old_stack = XtNew (widget_value*);
3053 mw->menu.old_stack_length = 1;
3054 mw->menu.old_stack [0] = mw->menu.contents;
3056 mw->menu.new_depth = 0;
3057 mw->menu.new_stack = 0;
3058 mw->menu.new_stack_length = 0;
3059 push_new_stack (mw, mw->menu.contents);
3061 mw->menu.windows = XtNew (window_state);
3062 mw->menu.windows_length = 1;
3063 mw->menu.windows [0].x = 0;
3064 mw->menu.windows [0].y = 0;
3065 mw->menu.windows [0].width = 0;
3066 mw->menu.windows [0].height = 0;
3069 mw->core.width = mw->menu.windows [0].width;
3070 mw->core.height = mw->menu.windows [0].height;
3074 XlwMenuClassInitialize (void)
3076 initialize_massaged_resource_char();
3080 XlwMenuRealize (Widget w, Mask *valueMask, XSetWindowAttributes *attributes)
3082 XlwMenuWidget mw = (XlwMenuWidget)w;
3083 XSetWindowAttributes xswa;
3086 (*xlwMenuWidgetClass->core_class.superclass->core_class.realize)
3087 (w, valueMask, attributes);
3089 xswa.save_under = True;
3090 xswa.cursor = mw->menu.cursor_shape;
3091 mask = CWSaveUnder | CWCursor;
3092 if (mw->menu.use_backing_store)
3094 xswa.backing_store = Always;
3095 mask |= CWBackingStore;
3097 XChangeWindowAttributes (XtDisplay (w), XtWindow (w), mask, &xswa);
3099 mw->menu.windows [0].window = XtWindow (w);
3100 mw->menu.windows [0].x = w->core.x;
3101 mw->menu.windows [0].y = w->core.y;
3102 mw->menu.windows [0].width = w->core.width;
3103 mw->menu.windows [0].height = w->core.height;
3106 /* Only the toplevel menubar/popup is a widget so it's the only one that
3107 receives expose events through Xt. So we repaint all the other panes
3108 when receiving an Expose event. */
3110 XlwMenuRedisplay (Widget w, XEvent *ev, Region region)
3112 XlwMenuWidget mw = (XlwMenuWidget)w;
3115 if (mw->core.being_destroyed) return;
3117 for (i = 0; i < mw->menu.old_depth; i++)
3118 display_menu (mw, i, False, NULL, NULL, NULL, NULL, NULL);
3119 set_new_state (mw, NULL, mw->menu.old_depth); /* #### - ??? */
3120 remap_menubar (mw); /* #### - do these two lines do anything? */
3124 XlwMenuDestroy (Widget w)
3127 XlwMenuWidget mw = (XlwMenuWidget) w;
3129 if (mw->menu.pointer_grabbed)
3131 XtUngrabPointer (w, CurrentTime);
3132 mw->menu.pointer_grabbed = False;
3135 release_drawing_gcs (mw);
3136 release_shadow_gcs (mw);
3138 /* this doesn't come from the resource db but is created explicitly
3139 so we must free it ourselves. */
3140 XFreePixmap (XtDisplay (mw), mw->menu.gray_pixmap);
3141 mw->menu.gray_pixmap = (Pixmap) -1;
3143 /* Don't free mw->menu.contents because that comes from our creator.
3144 The `*_stack' elements are just pointers into `contents' so leave
3145 that alone too. But free the stacks themselves. */
3146 if (mw->menu.old_stack) XtFree ((char *) mw->menu.old_stack);
3147 if (mw->menu.new_stack) XtFree ((char *) mw->menu.new_stack);
3149 /* Remember, you can't free anything that came from the resource
3150 database. This includes:
3152 mw->menu.top_shadow_pixmap
3153 mw->menu.bottom_shadow_pixmap
3156 Also the color cells of top_shadow_color, bottom_shadow_color,
3157 foreground, and button_foreground will never be freed until this
3158 client exits. Nice, eh?
3161 /* start from 1 because the one in slot 0 is w->core.window */
3162 for (i = 1; i < mw->menu.windows_length; i++)
3163 XDestroyWindow (XtDisplay (mw), mw->menu.windows [i].window);
3164 if (mw->menu.windows)
3165 XtFree ((char *) mw->menu.windows);
3169 XlwMenuSetValues (Widget current, Widget request, Widget new, ArgList args,
3172 XlwMenuWidget oldmw = (XlwMenuWidget)current;
3173 XlwMenuWidget newmw = (XlwMenuWidget)new;
3174 Boolean redisplay = False;
3177 if (newmw->menu.contents
3178 && newmw->menu.contents->contents
3179 && newmw->menu.contents->contents->change >= VISIBLE_CHANGE)
3182 if (newmw->core.background_pixel != oldmw->core.background_pixel
3183 || newmw->menu.foreground != oldmw->menu.foreground
3184 /* For the XEditResource protocol, which may want to change the font. */
3186 || newmw->menu.font_list != oldmw->menu.font_list
3187 || newmw->menu.font_list_2 != oldmw->menu.font_list_2
3188 || newmw->menu.fallback_font_list != oldmw->menu.fallback_font_list
3190 || newmw->menu.font != oldmw->menu.font
3194 release_drawing_gcs (newmw);
3195 make_drawing_gcs (newmw);
3198 for (i = 0; i < oldmw->menu.windows_length; i++)
3200 XSetWindowBackground (XtDisplay (oldmw),
3201 oldmw->menu.windows [i].window,
3202 newmw->core.background_pixel);
3203 /* clear windows and generate expose events */
3204 XClearArea (XtDisplay (oldmw), oldmw->menu.windows[i].window,
3213 XlwMenuResize (Widget w)
3215 XlwMenuWidget mw = (XlwMenuWidget)w;
3217 mw->menu.windows [0].width = mw->core.width;
3218 mw->menu.windows [0].height = mw->core.height;
3221 \f/* Action procedures */
3223 handle_single_motion_event (XlwMenuWidget mw, XMotionEvent *ev,
3230 if (!map_event_to_widget_value (mw, ev, &val, &level, &stay_up))
3232 /* we wind up here when: (a) the event is in the menubar, (b) the
3233 event isn't in the menubar or any of the panes, (c) the event is on
3234 a disabled menu item */
3235 pop_new_stack_if_no_contents (mw);
3236 if (select_p && !stay_up) {
3237 /* pop down all menus and exit */
3238 mw->menu.next_release_must_exit = True;
3239 set_new_state(mw, (val = NULL), 1);
3244 /* we wind up here when: (a) the event pops up a pull_right menu,
3245 (b) a menu item that is not disabled is highlighted */
3246 if (select_p && mw->menu.bounce_down
3247 && close_to_reference_time((Widget)mw,
3248 mw->menu.menu_bounce_time,
3251 /* motion can cause more than one event. Don't bounce right back
3252 up if we've just bounced down. */
3255 else if (select_p && mw->menu.bounce_down &&
3256 mw->menu.last_selected_val &&
3257 (mw->menu.last_selected_val == val))
3259 val = NULL; /* assigned to mw->last_selected_val below */
3260 mw->menu.menu_bounce_time = ev->time;
3261 /* popdown last menu if we're selecting the same menu item as we did
3262 last time and the XlwMenu.bounceDown resource is set, if the
3263 item is on the menubar itself, then exit. */
3264 if (level == (mw->menu.popped_up ? 0 : 1))
3265 mw->menu.next_release_must_exit = True;
3268 mw->menu.menu_bounce_time = 0;
3269 set_new_state (mw, val, level);
3271 mw->menu.last_selected_val = val;
3274 /* Sync with the display. Makes it feel better on X terms. */
3275 XFlush (XtDisplay (mw));
3279 handle_motion_event (XlwMenuWidget mw, XMotionEvent *ev,
3284 unsigned int state = ev->state;
3285 XMotionEvent *event= ev, dummy;
3287 /* allow motion events to be generated again */
3288 dummy.window = ev->window;
3290 && XQueryPointer (XtDisplay (mw), dummy.window,
3291 &dummy.root, &dummy.subwindow,
3292 &dummy.x_root, &dummy.y_root,
3295 && dummy.state == state
3296 && (dummy.x_root != x || dummy.y_root != y))
3298 /* don't handle the event twice or that breaks bounce_down. --Stig */
3299 dummy.type = ev->type;
3303 lw_menu_accelerate = False;
3304 handle_single_motion_event (mw, event, select_p);
3307 Time x_focus_timestamp_really_sucks_fix_me_better;
3310 Start (Widget w, XEvent *ev, String *params, Cardinal *num_params)
3312 XlwMenuWidget mw = (XlwMenuWidget)w;
3314 lw_menubar_widget = w;
3316 lw_menu_active = True;
3318 if (!mw->menu.pointer_grabbed)
3320 mw->menu.menu_post_time = ev->xbutton.time;
3321 mw->menu.menu_bounce_time = 0;
3322 mw->menu.next_release_must_exit = True;
3323 mw->menu.last_selected_val = NULL;
3324 x_focus_timestamp_really_sucks_fix_me_better =
3325 ((XButtonPressedEvent*)ev)->time;
3326 XtCallCallbackList ((Widget)mw, mw->menu.open, NULL);
3328 /* notes the absolute position of the menubar window */
3329 mw->menu.windows [0].x = ev->xmotion.x_root - ev->xmotion.x;
3330 mw->menu.windows [0].y = ev->xmotion.y_root - ev->xmotion.y;
3332 XtGrabPointer ((Widget)mw, False,
3333 (ButtonMotionMask | ButtonReleaseMask | ButtonPressMask),
3334 GrabModeAsync, GrabModeAsync,
3335 None, mw->menu.cursor_shape,
3336 ((XButtonPressedEvent*)ev)->time);
3337 mw->menu.pointer_grabbed = True;
3340 /* handles the down like a move, slots are mostly compatible */
3341 handle_motion_event (mw, &ev->xmotion, True);
3345 Drag (Widget w, XEvent *ev, String *params, Cardinal *num_params)
3347 XlwMenuWidget mw = (XlwMenuWidget)w;
3348 handle_motion_event (mw, &ev->xmotion, False);
3352 Select (Widget w, XEvent *ev, String *params, Cardinal *num_params)
3354 XlwMenuWidget mw = (XlwMenuWidget)w;
3355 widget_value *selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
3357 lw_menu_accelerate = False;
3359 /* If user releases the button quickly, without selecting anything,
3360 after the initial down-click that brought the menu up,
3362 if ((selected_item == 0 || selected_item->call_data == 0)
3363 && (!mw->menu.next_release_must_exit
3364 || close_to_reference_time(w, mw->menu.menu_post_time, ev)))
3366 mw->menu.next_release_must_exit = False;
3370 /* pop down everything */
3371 mw->menu.new_depth = 1;
3374 /* Destroy() only gets called for popup menus. Menubar widgets aren't
3375 destroyed when their menu panes get nuked. */
3376 if (mw->menu.pointer_grabbed)
3378 XtUngrabPointer ((Widget)w, ev->xmotion.time);
3379 mw->menu.pointer_grabbed = False;
3382 if (mw->menu.popped_up)
3384 mw->menu.popped_up = False;
3385 XtPopdown (XtParent (mw));
3388 lw_menu_active = False;
3390 x_focus_timestamp_really_sucks_fix_me_better =
3391 ((XButtonPressedEvent*)ev)->time;
3394 XtCallCallbackList ((Widget) mw, mw->menu.select, (XtPointer) selected_item);
3397 \f/* Action procedures for keyboard accelerators */
3401 xlw_set_menu (Widget w, widget_value *val)
3403 lw_menubar_widget = w;
3404 set_new_state ((XlwMenuWidget)w, val, 1);
3407 /* prepare the menu structure via the call-backs */
3409 xlw_map_menu (Time t)
3411 XlwMenuWidget mw = (XlwMenuWidget)lw_menubar_widget;
3413 lw_menu_accelerate = True;
3415 if (!mw->menu.pointer_grabbed)
3417 XWindowAttributes ret;
3420 unsigned int num_waste;
3422 lw_menu_active = True;
3424 mw->menu.menu_post_time = t;
3425 mw->menu.menu_bounce_time = 0;
3427 mw->menu.next_release_must_exit = True;
3428 mw->menu.last_selected_val = NULL;
3430 XtCallCallbackList ((Widget)mw, mw->menu.open, NULL);
3432 /* do this for keyboards too! */
3433 /* notes the absolute position of the menubar window */
3435 mw->menu.windows [0].x = ev->xmotion.x_root - ev->xmotion.x;
3436 mw->menu.windows [0].y = ev->xmotion.y_root - ev->xmotion.y;
3439 /* get the geometry of the menubar */
3441 /* there has to be a better way than this. */
3443 mw->menu.windows [0].x = 0;
3444 mw->menu.windows [0].y = 0;
3446 parent = XtWindow (lw_menubar_widget);
3449 XGetWindowAttributes (XtDisplay (lw_menubar_widget), parent, &ret);
3450 mw->menu.windows [0].x += ret.x;
3451 mw->menu.windows [0].y += ret.y;
3454 XQueryTree (XtDisplay (lw_menubar_widget), parent, &root, &parent, &waste,
3461 while (parent != root);
3463 XtGrabPointer ((Widget)mw, False,
3464 (ButtonMotionMask | ButtonReleaseMask | ButtonPressMask),
3465 GrabModeAsync, GrabModeAsync,
3466 None, mw->menu.cursor_shape, t);
3467 mw->menu.pointer_grabbed = True;
3471 /* display the stupid menu already */
3473 xlw_display_menu (Time t)
3475 XlwMenuWidget mw = (XlwMenuWidget)lw_menubar_widget;
3477 lw_menu_accelerate = True;
3481 /* Sync with the display. Makes it feel better on X terms. */
3482 XFlush (XtDisplay (mw));
3485 /* push a sub menu */
3487 xlw_push_menu (widget_value *val)
3489 push_new_stack ((XlwMenuWidget)lw_menubar_widget, val);
3492 /* pop a sub menu */
3496 if (((XlwMenuWidget)lw_menubar_widget)->menu.new_depth > 0)
3497 ((XlwMenuWidget)lw_menubar_widget)->menu.new_depth --;
3504 xlw_kill_menus (widget_value *val)
3506 XlwMenuWidget mw = (XlwMenuWidget)lw_menubar_widget;
3508 lw_menu_accelerate = False;
3510 mw->menu.new_depth = 1;
3513 if (mw->menu.pointer_grabbed)
3515 XtUngrabPointer (lw_menubar_widget, CurrentTime);
3516 mw->menu.pointer_grabbed = False;
3519 lw_menu_active = False;
3520 XtCallCallbackList (lw_menubar_widget, mw->menu.select, (XtPointer)val);
3523 /* set the menu item */
3525 xlw_set_item (widget_value *val)
3527 if (((XlwMenuWidget)lw_menubar_widget)->menu.new_depth > 0)
3528 ((XlwMenuWidget) lw_menubar_widget)->menu.new_depth --;
3529 push_new_stack ((XlwMenuWidget) lw_menubar_widget, val);
3532 /* get either the current entry or a list of all entries in the current submenu */
3534 xlw_get_entries (int allp)
3536 XlwMenuWidget mw = (XlwMenuWidget)lw_menubar_widget;
3539 if (mw->menu.new_depth >= 2)
3540 return mw->menu.new_stack [mw->menu.new_depth - 2]->contents;
3542 return mw->menu.new_stack[0];
3545 if (mw->menu.new_depth >= 1)
3546 return mw->menu.new_stack [mw->menu.new_depth - 1];
3552 xlw_menu_level (void)
3554 return ((XlwMenuWidget)lw_menubar_widget)->menu.new_depth;
3558 /* Special code to pop-up a menu */
3560 xlw_pop_up_menu (XlwMenuWidget mw, XButtonPressedEvent *event)
3562 int x = event->x_root;
3563 int y = event->y_root;
3566 int borderwidth = mw->menu.shadow_thickness;
3567 Screen* screen = XtScreen (mw);
3569 mw->menu.menu_post_time = event->time;
3570 mw->menu.menu_bounce_time = 0;
3571 mw->menu.next_release_must_exit = True;
3572 mw->menu.last_selected_val = NULL;
3574 XtCallCallbackList ((Widget) mw, mw->menu.open, NULL);
3578 w = mw->menu.windows [0].width;
3579 h = mw->menu.windows [0].height;
3584 if (x < borderwidth)
3587 if (x > WidthOfScreen (screen) - w - 2 * borderwidth)
3588 x = WidthOfScreen (screen) - w - 2 * borderwidth;
3590 if (y < borderwidth)
3593 if (y > HeightOfScreen (screen) - h - 2 * borderwidth)
3594 y = HeightOfScreen (screen) - h - 2 * borderwidth;
3596 mw->menu.popped_up = True;
3597 XtConfigureWidget (XtParent (mw), x, y, w, h,
3598 XtParent (mw)->core.border_width);
3599 XtPopup (XtParent (mw), XtGrabExclusive);
3600 display_menu (mw, 0, False, NULL, NULL, NULL, NULL, NULL);
3601 if (!mw->menu.pointer_grabbed)
3603 XtGrabPointer ((Widget)mw, False,
3604 (ButtonMotionMask | ButtonReleaseMask | ButtonPressMask),
3605 GrabModeAsync, GrabModeAsync,
3606 None, mw->menu.cursor_shape, event->time);
3607 mw->menu.pointer_grabbed = True;
3610 mw->menu.windows [0].x = x + borderwidth;
3611 mw->menu.windows [0].y = y + borderwidth;
3613 handle_motion_event (mw, (XMotionEvent *) event, True);
3619 * This is a horrible function which should not be needed.
3620 * use it to put the resize method back the way the XlwMenu
3621 * class initializer put it. Motif screws with this when
3622 * the XlwMenu class gets instantiated.
3625 xlw_unmunge_class_resize (Widget w)
3627 if (w->core.widget_class->core_class.resize != XlwMenuResize)
3628 w->core.widget_class->core_class.resize = XlwMenuResize;