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 char ch = massaged_resource_char[(unsigned char) *in++];
485 int int_ch = (int) (unsigned char) ch;
486 *out++ = firstp ? tolower (int_ch) : toupper (int_ch);
488 while ((ch = massaged_resource_char[(unsigned char) *in++]) != '\0')
490 if (!*(in-1)) /* Overshot the NULL byte? */
496 #ifdef PRINT_XLWMENU_RESOURCE_CONVERSIONS
497 printf ("! Emacs*XlwMenu.%s.labelString:\t%s\n", save_out, save_in);
498 printf ( "Emacs*XlwMenu.%s.labelString:\n", save_out);
505 { "labelString", "LabelString", XtRString, sizeof(String),
510 * This function looks through string searching for parameter
511 * inserts of the form:
513 * padding is space (' ') or dash ('-') characters meaning
514 * padding to the left or right of the inserted parameter.
515 * In essence all %1 strings are replaced by value in the return
516 * value (which the caller is expected to free).
517 * %% means insert one % (like printf).
518 * %1 means insert value.
519 * %-1 means insert value followed by one space. The latter is
520 * not inserted if value is a zero length string.
523 parameterize_string (CONST char *string, CONST char *value)
527 unsigned int done = 0;
532 result = XtMalloc(1);
540 for (ntimes = 1, result = (char *) string; (percent = strchr(result, '%'));
542 result = &percent[1];
544 result = XtMalloc ((ntimes * strlen(value)) + strlen(string) + 4);
547 while ((percent = strchr(string, '%')))
549 unsigned int left_pad;
550 unsigned int right_pad;
553 if (percent[1] == '%')
554 { /* it's a real % */
555 strncat (result, string, 1 + percent - string); /* incl % */
556 string = &percent[2]; /* after the second '%' */
557 continue; /* with the while() loop */
563 for (p = &percent[1]; /* test *p inside the loop */ ; p++)
574 { /* param and terminator */
575 strncat (result, string, percent - string);
576 if (value[0] != '\0')
579 for (i = 0; i < left_pad; i++)
580 strcat (result, " ");
581 strcat (result, value);
582 for (i = 0; i < right_pad; i++)
583 strcat (result, " ");
585 string = &p[1]; /* after the '1' */
586 done++; /* no need to do old way */
587 break; /* out of for() loop */
590 { /* bogus, copy the format as is */
591 /* out of for() loop */
592 strncat (result, string, 1 + p - string);
593 string = (*p ? &p[1] : p);
599 /* Copy the tail of the string */
600 strcat (result, string);
602 /* If we have not processed a % string, and we have a value, tail it. */
603 if (!done && value[0] != '\0')
605 strcat (result, " ");
606 strcat (result, value);
615 resource_widget_value (XlwMenuWidget mw, widget_value *val)
617 if (!val->toolkit_data)
619 char *resourced_name = NULL;
620 char *converted_name, *str;
621 XmString complete_name;
622 char massaged_name [1024];
624 if (mw->menu.lookup_labels)
626 /* Convert value style name into resource style name.
627 eg: "Free Willy" becomes "freeWilly" */
628 massage_resource_name (val->name, massaged_name);
630 /* If we have a value (parameter) see if we can find a "Named"
634 char named_name[1024];
635 sprintf (named_name, "%sNamed", massaged_name);
636 XtGetSubresources ((Widget) mw,
637 (XtPointer) &resourced_name,
638 named_name, named_name,
639 nameResource, 1, NULL, 0);
642 /* If nothing yet, try to load from the massaged name. */
645 XtGetSubresources ((Widget) mw,
646 (XtPointer) &resourced_name,
647 massaged_name, massaged_name,
648 nameResource, 1, NULL, 0);
650 } /* if (mw->menu.lookup_labels) */
652 /* Still nothing yet, use the name as the value. */
654 resourced_name = val->name;
656 /* Parameterize the string. */
657 converted_name = parameterize_string (resourced_name, val->value);
659 /* nuke newline characters to prevent menubar screwups */
660 for ( str = converted_name ; *str ; str++ )
662 if (str[0] == '\n') str[0] = ' ';
665 /* Improve OSF's bottom line. */
666 #if (XmVersion >= 1002)
667 complete_name = XmStringCreateLocalized (converted_name);
669 complete_name = XmStringCreateLtoR (converted_name,
670 XmSTRING_DEFAULT_CHARSET);
672 XtFree (converted_name);
674 val->toolkit_data = complete_name;
675 val->free_toolkit_data = True;
677 return (XmString) val->toolkit_data;
682 /* These two routines should be a seperate file..djw */
684 xlw_create_localized_string (Widget w,
695 XtGetSubresources (w,
705 return parameterize_string (string, arg);
709 xlw_create_localized_xmstring (Widget w,
714 char * string = xlw_create_localized_string (w, name, args, nargs);
715 XmString xm_string = XmStringCreateLtoR (string, XmSTRING_DEFAULT_CHARSET);
724 resource_widget_value (XlwMenuWidget mw, widget_value *val)
726 if (!val->toolkit_data)
728 char *resourced_name = NULL;
730 char massaged_name [1024];
732 if (mw->menu.lookup_labels)
734 massage_resource_name (val->name, massaged_name);
736 XtGetSubresources ((Widget) mw,
737 (XtPointer) &resourced_name,
738 massaged_name, massaged_name,
739 nameResource, 1, NULL, 0);
742 resourced_name = val->name;
744 complete_name = parameterize_string (resourced_name, val->value);
746 val->toolkit_data = complete_name;
747 /* nuke newline characters to prevent menubar screwups */
748 for ( ; *complete_name ; complete_name++ )
750 if (complete_name[0] == '\n')
751 complete_name[0] = ' ';
753 val->free_toolkit_data = True;
755 return (char *) val->toolkit_data;
760 /* Code for drawing strings. */
762 string_draw (XlwMenuWidget mw,
774 XmStringDraw (XtDisplay (mw), window,
778 1000, /* ???? width */
779 XmALIGNMENT_BEGINNING,
780 0, /* ???? layout_direction */
784 XmbDrawString (XtDisplay (mw), window, mw->menu.font_set, gc,
785 x, y + mw->menu.font_ascent, string, strlen (string));
787 XDrawString (XtDisplay (mw), window, gc,
788 x, y + mw->menu.font_ascent, string, strlen (string));
789 # endif /* USE_XFONTSET */
806 Dimension width, height;
814 newstring = XmStringLtoRCreate (&string[start], XmFONTLIST_DEFAULT_TAG);
816 XtDisplay (mw), window,
820 1000, /* ???? width */
821 XmALIGNMENT_BEGINNING,
822 0, /* ???? layout_direction */
825 XmStringExtent (mw->menu.font_list, newstring, &width, &height);
826 XmStringFree (newstring);
836 XtDisplay (mw), window, mw->menu.font_set, gc,
837 x, y + mw->menu.font_ascent, &string[start], end - start);
839 mw->menu.font_set, &string[start], end - start, &ri, &rl);
848 XtDisplay (mw), window, gc,
849 x, y + mw->menu.font_ascent, &string[start], end - start);
851 mw->menu.font, &string[start], end - start,
852 &drop, &drop, &drop, &xcs);
859 string_draw_u (XlwMenuWidget mw,
875 if (!XmStringGetLtoR (string, XmFONTLIST_DEFAULT_TAG, &chars))
880 for (i=0; chars[i]; ++i) {
881 if (chars[i] == '%' && chars[i+1] == '_') {
884 x += string_draw_range (mw, window, x, y, gc, chars, s, i);
885 w = string_draw_range (mw, window, x, y, gc, chars, i+2, i+3);
887 /* underline next character */
888 XDrawLine (XtDisplay (mw), window, gc, x - 1,
889 y + mw->menu.font_ascent + 1,
890 x + w - 1, y + mw->menu.font_ascent + 1 );
896 x += string_draw_range (mw, window, x, y, gc, chars, s, i);
903 binding_draw (XlwMenuWidget mw, Window w, int x, int y, GC gc, char *value)
906 XmString xm_value = XmStringCreateLtoR(value, XmSTRING_DEFAULT_CHARSET);
907 string_draw (mw, w, x, y, gc, xm_value);
908 XmStringFree (xm_value);
910 string_draw (mw, w, x, y, gc, value);
914 /* Low level code for drawing 3-D edges. */
916 shadow_rectangle_draw (Display *dpy,
923 unsigned int thickness)
932 points [1].x = x + width;
934 points [2].x = x + width - thickness;
935 points [2].y = y + thickness;
937 points [3].y = y + thickness;
938 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
940 points [0].y = y + thickness;
942 points [1].y = y + height;
943 points [2].x = x + thickness;
944 points [2].y = y + height - thickness;
945 points [3].x = x + thickness;
946 points [3].y = y + thickness;
947 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
948 points [0].x = x + width;
950 points [1].x = x + width - thickness;
951 points [1].y = y + thickness;
952 points [2].x = x + width - thickness;
953 points [2].y = y + height - thickness;
954 points [3].x = x + width;
955 points [3].y = y + height - thickness;
956 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
958 points [0].y = y + height;
959 points [1].x = x + width;
960 points [1].y = y + height;
961 points [2].x = x + width;
962 points [2].y = y + height - thickness;
963 points [3].x = x + thickness;
964 points [3].y = y + height - thickness;
965 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
968 typedef enum e_shadow_type
970 /* these are Motif compliant */
976 SHADOW_ETCHED_OUT_DASH,
977 SHADOW_ETCHED_IN_DASH,
980 SHADOW_SINGLE_DASHED_LINE,
981 SHADOW_DOUBLE_DASHED_LINE,
983 /* these are all non-Motif */
984 SHADOW_DOUBLE_ETCHED_OUT,
985 SHADOW_DOUBLE_ETCHED_IN,
986 SHADOW_DOUBLE_ETCHED_OUT_DASH,
987 SHADOW_DOUBLE_ETCHED_IN_DASH
991 shadow_draw (XlwMenuWidget mw,
998 Display *dpy = XtDisplay (mw);
1001 int thickness = mw->menu.shadow_thickness;
1005 Boolean etched = False;
1009 case SHADOW_BACKGROUND:
1010 top_gc = bottom_gc = mw->menu.background_gc;
1012 case SHADOW_ETCHED_IN:
1013 top_gc = mw->menu.shadow_bottom_gc;
1014 bottom_gc = mw->menu.shadow_top_gc;
1017 case SHADOW_ETCHED_OUT:
1018 top_gc = mw->menu.shadow_top_gc;
1019 bottom_gc = mw->menu.shadow_bottom_gc;
1023 top_gc = mw->menu.shadow_bottom_gc;
1024 bottom_gc = mw->menu.shadow_top_gc;
1028 top_gc = mw->menu.shadow_top_gc;
1029 bottom_gc = mw->menu.shadow_bottom_gc;
1035 unsigned int half = thickness/2;
1036 shadow_rectangle_draw (dpy,
1041 width - half, height - half,
1043 shadow_rectangle_draw (dpy,
1048 width - half , height - half,
1053 shadow_rectangle_draw (dpy,
1064 arrow_decoration_draw (XlwMenuWidget mw,
1070 Display *dpy = XtDisplay (mw);
1074 int thickness = mw->menu.shadow_thickness;
1077 int length = (int)((double)width * 0.87);
1078 int thick_med = (int)((double)thickness * 1.73);
1081 half_width = width/2 + 1;
1083 half_width = width/2;
1085 select_gc = mw->menu.background_gc;
1089 top_gc = mw->menu.shadow_bottom_gc;
1090 bottom_gc = mw->menu.shadow_top_gc;
1094 top_gc = mw->menu.shadow_top_gc;
1095 bottom_gc = mw->menu.shadow_bottom_gc;
1098 /* Fill internal area. We do this first so that the borders have a
1100 points [0].x = x + thickness;
1101 points [0].y = y + thickness;
1102 points [1].x = x + length - thickness;
1103 points [1].y = y + half_width;
1104 points [2].x = x + length - thickness;
1105 points [2].y = y + half_width + thickness;
1106 points [3].x = x + thickness;
1107 points [3].y = y + width - thickness;
1120 points [1].x = x + thickness;
1121 points [1].y = y + thick_med;
1122 points [2].x = x + thickness;
1123 points [2].y = y + width - thick_med;
1125 points [3].y = y + width;
1127 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
1131 points [0].y = y + width;
1132 points [1].x = x + length;
1133 points [1].y = y + half_width;
1134 points [2].x = x + length - (thickness + thickness);
1135 points [2].y = y + half_width;
1136 points [3].x = x + thickness;
1137 points [3].y = y + width - thick_med;
1139 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
1144 points [1].x = x + length;
1145 points [1].y = y + half_width;
1146 points [2].x = x + length - (thickness + thickness);
1147 points [2].y = y + half_width;
1148 points [3].x = x + thickness;
1149 points [3].y = y + thick_med;
1151 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
1155 toggle_decoration_draw (XlwMenuWidget mw,
1161 Display *dpy = XtDisplay (mw);
1162 int thickness = mw->menu.shadow_thickness;
1164 GC select_gc = mw->menu.select_gc;
1171 /* Fill internal area. */
1173 XFillRectangle (dpy,
1178 width - (2*thickness),
1179 width - (2*thickness));
1181 shadow_draw (mw, window, x, y, width, width, type);
1185 radio_decoration_draw (XlwMenuWidget mw,
1191 Display *dpy = XtDisplay (mw);
1194 GC select_gc = mw->menu.select_gc;
1195 int thickness = mw->menu.shadow_thickness;
1205 half_width = width/2;
1209 top_gc = mw->menu.shadow_bottom_gc;
1210 bottom_gc = mw->menu.shadow_top_gc;
1214 top_gc = mw->menu.shadow_top_gc;
1215 bottom_gc = mw->menu.shadow_bottom_gc;
1219 /* Draw the bottom first, just in case the regions overlap.
1220 The top should cast the longer shadow. */
1221 points [0].x = x; /* left corner */
1222 points [0].y = y + half_width;
1223 points [1].x = x + half_width; /* bottom corner */
1224 points [1].y = y + width;
1225 points [2].x = x + half_width; /* bottom inside corner */
1226 points [2].y = y + width - thickness;
1227 points [3].x = x + thickness; /* left inside corner */
1228 points [3].y = y + half_width;
1230 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
1232 points [0].x = x + half_width; /* bottom corner */
1233 points [0].y = y + width;
1234 points [1].x = x + width; /* right corner */
1235 points [1].y = y + half_width;
1236 points [2].x = x + width - thickness; /* right inside corner */
1237 points [2].y = y + half_width;
1238 points [3].x = x + half_width; /* bottom inside corner */
1239 points [3].y = y + width - thickness;
1241 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
1243 points [0].x = x; /* left corner */
1244 points [0].y = y + half_width;
1245 points [1].x = x + half_width; /* top corner */
1247 points [2].x = x + half_width; /* top inside corner */
1248 points [2].y = y + thickness;
1249 points [3].x = x + thickness; /* left inside corner */
1250 points [3].y = y + half_width;
1252 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
1254 points [0].x = x + half_width; /* top corner */
1256 points [1].x = x + width; /* right corner */
1257 points [1].y = y + half_width;
1258 points [2].x = x + width - thickness; /* right inside corner */
1259 points [2].y = y + half_width;
1260 points [3].x = x + half_width; /* top inside corner */
1261 points [3].y = y + thickness;
1263 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
1265 /* Draw the bottom first, just in case the regions overlap.
1266 The top should cast the longer shadow. */
1268 points [npoints].x = x; /* left corner */
1269 points [npoints++].y = y + half_width;
1270 points [npoints].x = x + half_width; /* bottom corner */
1271 points [npoints++].y = y + width;
1272 points [npoints].x = x + width; /* right corner */
1273 points [npoints++].y = y + half_width;
1274 points [npoints].x = x + width - thickness; /* right inside corner */
1275 points [npoints++].y = y + half_width;
1276 points [npoints].x = x + half_width; /* bottom inside corner */
1277 points [npoints++].y = y + width - thickness;
1278 points [npoints].x = x + thickness; /* left inside corner */
1279 points [npoints++].y = y + half_width;
1281 XFillPolygon (dpy, window, bottom_gc,
1282 points, npoints, Nonconvex, CoordModeOrigin);
1286 points [npoints].x = x; /* left corner */
1287 points [npoints++].y = y + half_width;
1288 points [npoints].x = x + half_width; /* top corner */
1289 points [npoints++].y = y;
1290 points [npoints].x = x + width; /* right corner */
1291 points [npoints++].y = y + half_width;
1292 points [npoints].x = x + width - thickness; /* right inside corner */
1293 points [npoints++].y = y + half_width;
1294 points [npoints].x = x + half_width; /* top inside corner */
1295 points [npoints++].y = y + thickness;
1296 points [npoints].x = x + thickness; /* left inside corner */
1297 points [npoints++].y = y + half_width;
1299 XFillPolygon (dpy, window, top_gc, points, npoints, Nonconvex,
1304 /* Fill internal area. */
1307 points [0].x = x + thickness;
1308 points [0].y = y + half_width;
1309 points [1].x = x + half_width;
1310 points [1].y = y + thickness;
1311 points [2].x = x + width - thickness;
1312 points [2].y = y + half_width;
1313 points [3].x = x + half_width;
1314 points [3].y = y + width - thickness;
1326 separator_decoration_draw (XlwMenuWidget mw,
1333 Display *dpy = XtDisplay (mw);
1336 unsigned int offset = 0;
1337 unsigned int num_separators = 1;
1338 unsigned int top_line_thickness = 0;
1339 unsigned int bottom_line_thickness = 0;
1340 Boolean dashed = False;
1344 case SHADOW_NO_LINE: /* nothing to do */
1346 case SHADOW_DOUBLE_LINE:
1348 case SHADOW_SINGLE_LINE:
1349 top_gc = bottom_gc = mw->menu.foreground_gc;
1350 top_line_thickness = 1;
1352 case SHADOW_DOUBLE_DASHED_LINE:
1354 case SHADOW_SINGLE_DASHED_LINE:
1355 top_gc = bottom_gc = mw->menu.foreground_gc;
1356 top_line_thickness = 1;
1359 case SHADOW_DOUBLE_ETCHED_OUT_DASH:
1361 case SHADOW_ETCHED_OUT_DASH:
1362 top_gc = mw->menu.shadow_top_gc;
1363 bottom_gc = mw->menu.shadow_bottom_gc;
1364 top_line_thickness = mw->menu.shadow_thickness/2;
1365 bottom_line_thickness = mw->menu.shadow_thickness - top_line_thickness;
1368 case SHADOW_DOUBLE_ETCHED_IN_DASH:
1370 case SHADOW_ETCHED_IN_DASH:
1371 top_gc = mw->menu.shadow_bottom_gc;
1372 bottom_gc = mw->menu.shadow_top_gc;
1373 top_line_thickness = mw->menu.shadow_thickness/2;
1374 bottom_line_thickness = mw->menu.shadow_thickness - top_line_thickness;
1377 case SHADOW_DOUBLE_ETCHED_OUT:
1379 case SHADOW_ETCHED_OUT:
1380 top_gc = mw->menu.shadow_top_gc;
1381 bottom_gc = mw->menu.shadow_bottom_gc;
1382 top_line_thickness = mw->menu.shadow_thickness/2;
1383 bottom_line_thickness = mw->menu.shadow_thickness - top_line_thickness;
1385 case SHADOW_DOUBLE_ETCHED_IN:
1387 case SHADOW_ETCHED_IN:
1389 top_gc = mw->menu.shadow_bottom_gc;
1390 bottom_gc = mw->menu.shadow_top_gc;
1391 top_line_thickness = mw->menu.shadow_thickness/2;
1392 bottom_line_thickness = mw->menu.shadow_thickness - top_line_thickness;
1399 values.line_style = LineOnOffDash;
1400 if (top_line_thickness > 0)
1401 XChangeGC (dpy, top_gc, GCLineStyle, &values);
1402 if (bottom_line_thickness > 0 && bottom_gc != top_gc)
1403 XChangeGC (dpy, bottom_gc, GCLineStyle, &values);
1406 while (num_separators--)
1409 for (i = 0; i < top_line_thickness; i++)
1410 XDrawLine (dpy, window, top_gc, x, y + i, x + width, y + i);
1412 for (i = 0; i < bottom_line_thickness; i++)
1413 XDrawLine (dpy, window, bottom_gc,
1414 x, y + top_line_thickness + offset + i,
1415 x + width, y + top_line_thickness + offset + i);
1416 y += (top_line_thickness + offset + bottom_line_thickness + 1);
1422 values.line_style = LineSolid;
1423 if (top_line_thickness > 0)
1424 XChangeGC (dpy, top_gc, GCLineStyle, &values);
1425 if (bottom_line_thickness > 0 && bottom_gc != top_gc)
1426 XChangeGC (dpy, bottom_gc, GCLineStyle, &values);
1430 #define SLOPPY_TYPES 0 /* 0=off, 1=error check, 2=easy to please */
1432 #if SLOPPY_TYPES < 2
1434 static char *wv_types[] =
1448 print_widget_value (widget_value *wv, int just_one, int depth)
1452 for (i = 0; i < depth; i++)
1457 printf ("%s(null widget value pointer)\n", d);
1460 printf ("%stype: %s\n", d, wv_types [wv->type]);
1462 printf ("%sname: %s\n", d, (wv->name ? wv->name : "(null)"));
1464 if (wv->name) printf ("%sname: %s\n", d, wv->name);
1466 if (wv->value) printf ("%svalue: %s\n", d, wv->value);
1467 if (wv->key) printf ("%skey: %s\n", d, wv->key);
1468 printf ("%senabled: %d\n", d, wv->enabled);
1471 printf ("\n%scontents: \n", d);
1472 print_widget_value (wv->contents, 0, depth + 5);
1474 if (!just_one && wv->next)
1477 print_widget_value (wv->next, 0, depth);
1480 #endif /* SLOPPY_TYPES < 2 */
1483 all_dashes_p (char *s)
1486 if (!s || s[0] == '\0')
1488 for (p = s; *p == '-'; p++);
1490 if (*p == '!' || *p == '\0')
1494 #endif /* SLOPPY_TYPES */
1496 static widget_value_type
1497 menu_item_type (widget_value *val)
1499 if (val->type != UNSPECIFIED_TYPE)
1502 else if (all_dashes_p (val->name))
1503 return SEPARATOR_TYPE;
1504 else if (val->name && val->name[0] == '\0') /* push right */
1505 return PUSHRIGHT_TYPE;
1506 else if (val->contents) /* cascade */
1507 return CASCADE_TYPE;
1508 else if (val->call_data) /* push button */
1515 return UNSPECIFIED_TYPE; /* Not reached */
1520 label_button_size (XlwMenuWidget mw,
1523 unsigned int *toggle_width,
1524 unsigned int *label_width,
1525 unsigned int *bindings_width,
1526 unsigned int *height)
1528 *height = (mw->menu.font_ascent + mw->menu.font_descent +
1529 2 * mw->menu.vertical_margin +
1530 2 * mw->menu.shadow_thickness);
1531 /* no left column decoration */
1532 *toggle_width = mw->menu.horizontal_margin + mw->menu.shadow_thickness;;
1534 *label_width = string_width_u (mw, resource_widget_value (mw, val));
1535 *bindings_width = mw->menu.horizontal_margin + mw->menu.shadow_thickness;
1539 label_button_draw (XlwMenuWidget mw,
1542 Boolean highlighted,
1546 unsigned int height,
1547 unsigned int label_offset,
1548 unsigned int binding_tab)
1550 int y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin;
1554 label_offset = mw->menu.shadow_thickness + mw->menu.horizontal_margin;
1556 if (highlighted && (in_menubar || val->contents))
1557 gc = mw->menu.highlight_gc;
1558 else if (in_menubar || val->contents)
1559 gc = mw->menu.foreground_gc;
1561 gc = mw->menu.title_gc;
1563 /* Draw the label string. */
1566 x + label_offset, y + y_offset,
1568 resource_widget_value (mw, val));
1572 push_button_size (XlwMenuWidget mw,
1575 unsigned int *toggle_width,
1576 unsigned int *label_width,
1577 unsigned int *bindings_width,
1578 unsigned int *height)
1581 label_button_size (mw, val, in_menubar,
1582 toggle_width, label_width, bindings_width,
1585 /* key bindings to display? */
1586 if (!in_menubar && val->key)
1590 XmString key = XmStringCreateLtoR (val->key, XmSTRING_DEFAULT_CHARSET);
1591 w = string_width (mw, key);
1594 char *key = val->key;
1595 w = string_width (mw, key);
1597 *bindings_width += w + mw->menu.column_spacing;
1602 push_button_draw (XlwMenuWidget mw,
1605 Boolean highlighted,
1609 unsigned int height,
1610 unsigned int label_offset,
1611 unsigned int binding_offset)
1613 int y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin;
1616 Boolean menu_pb = in_menubar && (menu_item_type (val) == BUTTON_TYPE);
1618 /* Draw the label string. */
1620 label_offset = mw->menu.shadow_thickness + mw->menu.horizontal_margin;
1625 gc = mw->menu.highlight_gc;
1627 gc = mw->menu.inactive_gc;
1632 gc = mw->menu.button_gc;
1634 gc = mw->menu.inactive_button_gc;
1639 gc = mw->menu.foreground_gc;
1641 gc = mw->menu.inactive_gc;
1646 x + label_offset, y + y_offset,
1648 resource_widget_value (mw, val));
1650 /* Draw the keybindings */
1653 if (!binding_offset)
1655 unsigned int s_width =
1656 string_width (mw, resource_widget_value (mw, val));
1657 binding_offset = label_offset + s_width + mw->menu.shadow_thickness;
1659 binding_draw (mw, window,
1660 x + binding_offset + mw->menu.column_spacing,
1661 y + y_offset, gc, val->key);
1664 /* Draw the shadow */
1670 type = (val->selected ? SHADOW_ETCHED_OUT : SHADOW_ETCHED_IN);
1677 type = SHADOW_BACKGROUND;
1680 shadow_draw (mw, window, x, y, width, height, type);
1684 arrow_decoration_height (XlwMenuWidget mw)
1686 int result = (mw->menu.font_ascent + mw->menu.font_descent) / 2;
1688 result += 2 * mw->menu.shadow_thickness;
1690 if (result > (mw->menu.font_ascent + mw->menu.font_descent))
1691 result = mw->menu.font_ascent + mw->menu.font_descent;
1697 cascade_button_size (XlwMenuWidget mw,
1700 unsigned int *toggle_width,
1701 unsigned int *label_width,
1702 unsigned int *arrow_width,
1703 unsigned int *height)
1706 label_button_size (mw, val, in_menubar,
1707 toggle_width, label_width, arrow_width,
1709 /* we have a pull aside arrow */
1712 *arrow_width += arrow_decoration_height (mw) + mw->menu.column_spacing;
1717 cascade_button_draw (XlwMenuWidget mw,
1720 Boolean highlighted,
1724 unsigned int height,
1725 unsigned int label_offset,
1726 unsigned int binding_offset)
1730 /* Draw the label string. */
1731 label_button_draw (mw, val, in_menubar, highlighted,
1732 window, x, y, width, height, label_offset,
1735 /* Draw the pull aside arrow */
1736 if (!in_menubar && val->contents)
1739 unsigned int arrow_height = arrow_decoration_height (mw);
1741 y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin +
1742 (mw->menu.font_ascent+mw->menu.font_descent - arrow_height)/2;
1744 if (!binding_offset)
1746 unsigned int s_width =
1747 string_width (mw, resource_widget_value (mw, val));
1750 label_offset = mw->menu.shadow_thickness +
1751 mw->menu.horizontal_margin;
1753 binding_offset = label_offset + s_width + mw->menu.shadow_thickness;
1756 arrow_decoration_draw (mw,
1758 x + binding_offset + mw->menu.column_spacing,
1764 /* Draw the shadow */
1768 type = SHADOW_BACKGROUND;
1770 shadow_draw (mw, window, x, y, width, height, type);
1774 toggle_decoration_height (XlwMenuWidget mw)
1777 if (mw->menu.indicator_size > 0)
1778 rv = mw->menu.indicator_size;
1780 rv = mw->menu.font_ascent;
1782 if (rv > (mw->menu.font_ascent + mw->menu.font_descent))
1783 rv = mw->menu.font_ascent + mw->menu.font_descent;
1785 /* radio button can't be smaller than its border or a filling
1786 error will occur. */
1787 if (rv < 2 * mw->menu.shadow_thickness)
1788 rv = 2 * mw->menu.shadow_thickness;
1794 toggle_button_size (XlwMenuWidget mw,
1797 unsigned int *toggle_width,
1798 unsigned int *label_width,
1799 unsigned int *bindings_width,
1800 unsigned int *height)
1803 push_button_size (mw, val, in_menubar,
1804 toggle_width, label_width, bindings_width,
1806 /* we have a toggle */
1807 *toggle_width += toggle_decoration_height (mw) + mw->menu.column_spacing;
1811 toggle_button_draw (XlwMenuWidget mw,
1814 Boolean highlighted,
1818 unsigned int height,
1819 unsigned int label_tab,
1820 unsigned int binding_tab)
1824 unsigned int t_height = toggle_decoration_height (mw);
1826 /* Draw a toggle. */
1827 x_offset = mw->menu.shadow_thickness + mw->menu.horizontal_margin;
1828 y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin;
1829 y_offset += (mw->menu.font_ascent + mw->menu.font_descent - t_height)/2;
1831 toggle_decoration_draw (mw, window, x + x_offset, y + y_offset,
1832 t_height, val->selected);
1834 /* Draw the pushbutton parts. */
1835 push_button_draw (mw, val, in_menubar, highlighted, window, x, y, width,
1836 height, label_tab, binding_tab);
1840 radio_decoration_height (XlwMenuWidget mw)
1842 return toggle_decoration_height (mw);
1846 radio_button_draw (XlwMenuWidget mw,
1849 Boolean highlighted,
1853 unsigned int height,
1854 unsigned int label_tab,
1855 unsigned int binding_tab)
1859 unsigned int r_height = radio_decoration_height (mw);
1861 /* Draw a toggle. */
1862 x_offset = mw->menu.shadow_thickness + mw->menu.horizontal_margin;
1863 y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin;
1864 y_offset += (mw->menu.font_ascent + mw->menu.font_descent - r_height)/2;
1866 radio_decoration_draw (mw, window, x + x_offset, y + y_offset, r_height,
1869 /* Draw the pushbutton parts. */
1870 push_button_draw (mw, val, in_menubar, highlighted, window, x, y, width,
1871 height, label_tab, binding_tab);
1874 static struct _shadow_names
1881 { "singleLine", SHADOW_SINGLE_LINE },
1882 { "doubleLine", SHADOW_DOUBLE_LINE },
1883 { "singleDashedLine", SHADOW_SINGLE_DASHED_LINE },
1884 { "doubleDashedLine", SHADOW_DOUBLE_DASHED_LINE },
1885 { "noLine", SHADOW_NO_LINE },
1886 { "shadowEtchedIn", SHADOW_ETCHED_IN },
1887 { "shadowEtchedOut", SHADOW_ETCHED_OUT },
1888 { "shadowEtchedInDash", SHADOW_ETCHED_IN_DASH },
1889 { "shadowEtchedOutDash", SHADOW_ETCHED_OUT_DASH },
1891 { "shadowDoubleEtchedIn", SHADOW_DOUBLE_ETCHED_IN },
1892 { "shadowDoubleEtchedOut", SHADOW_DOUBLE_ETCHED_OUT },
1893 { "shadowDoubleEtchedInDash", SHADOW_DOUBLE_ETCHED_IN_DASH },
1894 { "shadowDoubleEtchedOutDash", SHADOW_DOUBLE_ETCHED_OUT_DASH }
1898 separator_type (char *name)
1903 for (i = 0; i < (int) (XtNumber (shadow_names)); i++ )
1905 if (strcmp (name, shadow_names[i].name) == 0)
1906 return shadow_names[i].type;
1909 return SHADOW_BACKGROUND;
1913 separator_decoration_height (XlwMenuWidget mw, widget_value *val)
1916 switch (separator_type (val->value))
1918 case SHADOW_NO_LINE:
1919 case SHADOW_SINGLE_LINE:
1920 case SHADOW_SINGLE_DASHED_LINE:
1922 case SHADOW_DOUBLE_LINE:
1923 case SHADOW_DOUBLE_DASHED_LINE:
1925 case SHADOW_DOUBLE_ETCHED_OUT:
1926 case SHADOW_DOUBLE_ETCHED_IN:
1927 case SHADOW_DOUBLE_ETCHED_OUT_DASH:
1928 case SHADOW_DOUBLE_ETCHED_IN_DASH:
1929 return (1 + 2 * mw->menu.shadow_thickness);
1930 case SHADOW_ETCHED_OUT:
1931 case SHADOW_ETCHED_IN:
1933 return mw->menu.shadow_thickness;
1938 separator_size (XlwMenuWidget mw,
1941 unsigned int *toggle_width,
1942 unsigned int *label_width,
1943 unsigned int *rest_width,
1944 unsigned int *height)
1946 *height = separator_decoration_height (mw, val);
1948 *toggle_width = *rest_width = 0;
1952 separator_draw (XlwMenuWidget mw,
1955 Boolean highlighted,
1959 unsigned int height,
1960 unsigned int label_tab,
1961 unsigned int binding_tab)
1963 unsigned int sep_width;
1970 separator_decoration_draw (mw,
1976 separator_type(val->value));
1980 pushright_size (XlwMenuWidget mw,
1983 unsigned int *toggle_width,
1984 unsigned int *label_width,
1985 unsigned int *rest_width,
1986 unsigned int *height)
1988 *height = *label_width = *toggle_width = *rest_width = 0;
1992 size_menu_item (XlwMenuWidget mw,
1995 unsigned int *toggle_width,
1996 unsigned int *label_width,
1997 unsigned int *rest_width,
1998 unsigned int *height)
2000 void (*function_ptr) (XlwMenuWidget _mw,
2002 Boolean _in_menubar,
2003 unsigned int *_toggle_width,
2004 unsigned int *_label_width,
2005 unsigned int *_rest_width,
2006 unsigned int *_height);
2008 switch (menu_item_type (val))
2012 function_ptr = toggle_button_size;
2014 case SEPARATOR_TYPE:
2015 function_ptr = separator_size;
2017 case INCREMENTAL_TYPE:
2019 function_ptr = cascade_button_size;
2022 function_ptr = push_button_size;
2024 case PUSHRIGHT_TYPE:
2025 function_ptr = pushright_size;
2029 function_ptr = label_button_size;
2033 (*function_ptr) (mw,
2043 display_menu_item (XlwMenuWidget mw,
2047 Boolean highlighted,
2049 Boolean just_compute)
2052 int x = where->x /* + mw->menu.shadow_thickness */ ;
2053 int y = where->y /* + mw->menu.shadow_thickness */ ;
2054 unsigned int toggle_width;
2055 unsigned int label_width;
2056 unsigned int binding_width;
2058 unsigned int height;
2059 unsigned int label_tab;
2060 unsigned int binding_tab;
2061 void (*function_ptr) (XlwMenuWidget _mw,
2063 Boolean _in_menubar,
2064 Boolean _highlighted,
2067 unsigned int _width,
2068 unsigned int _height,
2069 unsigned int _label_tab,
2070 unsigned int _binding_tab);
2072 size_menu_item (mw, val, horizontal,
2073 &toggle_width, &label_width, &binding_width, &height);
2077 width = toggle_width + label_width + binding_width;
2078 height = ws->height - 2 * mw->menu.shadow_thickness;
2082 width = ws->width - 2 * mw->menu.shadow_thickness;
2083 toggle_width = ws->toggle_width;
2084 label_width = ws->label_width;
2093 label_tab = toggle_width;
2094 binding_tab = toggle_width + label_width;
2096 switch (menu_item_type (val))
2099 function_ptr = toggle_button_draw;
2102 function_ptr = radio_button_draw;
2104 case SEPARATOR_TYPE:
2105 function_ptr = separator_draw;
2107 case INCREMENTAL_TYPE:
2109 function_ptr = cascade_button_draw;
2112 function_ptr = push_button_draw;
2115 function_ptr = label_button_draw;
2117 default: /* do no drawing */
2121 (*function_ptr) (mw,
2133 size_menu (XlwMenuWidget mw, int level)
2135 unsigned int toggle_width;
2136 unsigned int label_width;
2137 unsigned int rest_width;
2138 unsigned int height;
2139 unsigned int max_toggle_width = 0;
2140 unsigned int max_label_width = 0;
2141 unsigned int max_rest_width = 0;
2142 unsigned int max_height = 0;
2143 int horizontal_p = mw->menu.horizontal && (level == 0);
2147 if (level >= mw->menu.old_depth)
2150 ws = &mw->menu.windows [level];
2152 for (val = mw->menu.old_stack [level]->contents; val; val = val->next)
2163 max_label_width += toggle_width + label_width + rest_width;
2164 if (height > max_height)
2165 max_height = height;
2169 if (max_toggle_width < toggle_width)
2170 max_toggle_width = toggle_width;
2171 if (max_label_width < label_width)
2172 max_label_width = label_width;
2173 if (max_rest_width < rest_width)
2174 max_rest_width = rest_width;
2175 max_height += height;
2179 ws->height = max_height;
2180 ws->width = max_label_width + max_rest_width + max_toggle_width;
2181 ws->toggle_width = max_toggle_width;
2182 ws->label_width = max_label_width;
2184 ws->width += 2 * mw->menu.shadow_thickness;
2185 ws->height += 2 * mw->menu.shadow_thickness;
2189 display_menu (XlwMenuWidget mw, int level, Boolean just_compute_p,
2190 XPoint *highlighted_pos, XPoint *hit, widget_value **hit_return,
2191 widget_value *this, widget_value *that)
2194 widget_value *following_item;
2197 int horizontal_p = mw->menu.horizontal && (level == 0);
2199 int just_compute_this_one_p;
2201 if (level >= mw->menu.old_depth)
2204 if (level < mw->menu.old_depth - 1)
2205 following_item = mw->menu.old_stack [level + 1];
2208 if (lw_menu_accelerate
2209 && level == mw->menu.old_depth - 1
2210 && mw->menu.old_stack [level]->type == CASCADE_TYPE)
2211 just_compute_p = True;
2212 following_item = NULL;
2215 #if SLOPPY_TYPES == 1
2216 puts("===================================================================");
2217 print_widget_value (following_item, 1, 0);
2223 where.x = mw->menu.shadow_thickness;
2224 where.y = mw->menu.shadow_thickness;
2226 ws = &mw->menu.windows [level];
2227 for (val = mw->menu.old_stack [level]->contents; val; val = val->next)
2231 highlighted_p = (val == following_item);
2232 /* If this is the partition (the dummy item which says that menus
2233 after this should be flushright) then figure out how big the
2234 following items are. This means we walk down the tail of the
2235 list twice, but that's no big deal - it's short.
2237 if (horizontal_p && (menu_item_type (val) == PUSHRIGHT_TYPE))
2240 XPoint flushright_size;
2242 flushright_size.x = 0;
2243 flushright_size.y = 0;
2244 for (rest = val; rest; rest = rest->next)
2245 display_menu_item (mw, rest, ws, &flushright_size,
2246 highlighted_p, horizontal_p, True);
2247 new_x = ws->width - (flushright_size.x + mw->menu.shadow_thickness);
2248 if (new_x > where.x)
2250 /* We know what we need; don't draw this item. */
2254 if (highlighted_p && highlighted_pos)
2257 highlighted_pos->x = where.x;
2259 highlighted_pos->y = where.y;
2262 just_compute_this_one_p =
2263 just_compute_p || ((this || that) && val != this && val != that);
2267 display_menu_item (mw, val, ws, &where, highlighted_p, horizontal_p,
2268 just_compute_this_one_p);
2270 if (highlighted_p && highlighted_pos)
2273 highlighted_pos->y = ws->height;
2275 highlighted_pos->x = ws->width;
2278 if (hit && !*hit_return)
2280 if (horizontal_p && hit->x > start.x && hit->x <= where.x)
2282 else if (!horizontal_p && hit->y > start.y && hit->y <= where.y)
2287 where.y = mw->menu.shadow_thickness;
2289 where.x = mw->menu.shadow_thickness;
2292 /* Draw slab edges around menu */
2293 if (!just_compute_p)
2294 shadow_draw(mw, ws->window, 0, 0, ws->width, ws->height, SHADOW_OUT);
2299 set_new_state (XlwMenuWidget mw, widget_value *val, int level)
2303 mw->menu.new_depth = 0;
2304 for (i = 0; i < level; i++)
2305 push_new_stack (mw, mw->menu.old_stack [i]);
2307 push_new_stack (mw, val);
2311 make_windows_if_needed (XlwMenuWidget mw, int n)
2315 XSetWindowAttributes xswa;
2320 window_state *windows;
2323 if (mw->menu.windows_length >= n)
2326 root = RootWindowOfScreen (XtScreen(mw));
2327 /* grab the visual and depth from the nearest shell ancestor */
2328 visual = CopyFromParent;
2329 depth = CopyFromParent;
2331 while (visual == CopyFromParent && p)
2335 visual = ((ShellWidget)p)->shell.visual;
2336 depth = p->core.depth;
2341 xswa.save_under = True;
2342 xswa.override_redirect = True;
2343 xswa.background_pixel = mw->core.background_pixel;
2344 xswa.border_pixel = mw->core.border_pixel;
2345 xswa.event_mask = (ExposureMask | ButtonMotionMask
2346 | ButtonReleaseMask | ButtonPressMask);
2347 xswa.cursor = mw->menu.cursor_shape;
2348 xswa.colormap = mw->core.colormap;
2349 mask = CWSaveUnder | CWOverrideRedirect | CWBackPixel | CWBorderPixel
2350 | CWEventMask | CWCursor | CWColormap;
2352 if (mw->menu.use_backing_store)
2354 xswa.backing_store = Always;
2355 mask |= CWBackingStore;
2358 if (!mw->menu.windows)
2361 (window_state *) XtMalloc (n * sizeof (window_state));
2367 (window_state *) XtRealloc ((char *) mw->menu.windows,
2368 n * sizeof (window_state));
2369 start_at = mw->menu.windows_length;
2371 mw->menu.windows_length = n;
2373 windows = mw->menu.windows;
2375 for (i = start_at; i < n; i++)
2379 windows [i].width = 1;
2380 windows [i].height = 1;
2381 windows [i].window =
2382 XCreateWindow (XtDisplay (mw),
2385 0, depth, CopyFromParent, visual, mask, &xswa);
2389 /* Make the window fit in the screen */
2391 fit_to_screen (XlwMenuWidget mw, window_state *ws, window_state *previous_ws,
2392 Boolean horizontal_p)
2394 int screen_width = WidthOfScreen (XtScreen (mw));
2395 int screen_height = HeightOfScreen (XtScreen (mw));
2399 else if ((int) (ws->x + ws->width) > screen_width)
2402 ws->x = previous_ws->x - ws->width;
2405 ws->x = screen_width - ws->width;
2407 /* This check is to make sure we cut off the right side
2408 instead of the left side if the menu is wider than the
2416 else if ((int) (ws->y + ws->height) > screen_height)
2420 /* A pulldown must either be entirely above or below the menubar.
2421 If we're here, the pulldown doesn't fit below the menubar, so
2422 let's determine if it will fit above the menubar.
2423 Only put it above if there is more room above than below.
2424 Note shadow_thickness offset to allow for slab surround.
2426 if (ws->y > (screen_height / 2))
2427 ws->y = previous_ws->y - ws->height + mw->menu.shadow_thickness;
2431 ws->y = screen_height - ws->height;
2432 /* if it's taller than the screen, display the topmost part
2433 that will fit, beginning at the top of the screen. */
2440 /* Updates old_stack from new_stack and redisplays. */
2442 remap_menubar (XlwMenuWidget mw)
2446 XPoint selection_position;
2447 int old_depth = mw->menu.old_depth;
2448 int new_depth = mw->menu.new_depth;
2449 widget_value **old_stack;
2450 widget_value **new_stack;
2451 window_state *windows;
2452 widget_value *old_selection;
2453 widget_value *new_selection;
2455 /* Check that enough windows and old_stack are ready. */
2456 make_windows_if_needed (mw, new_depth);
2457 make_old_stack_space (mw, new_depth);
2458 windows = mw->menu.windows;
2459 old_stack = mw->menu.old_stack;
2460 new_stack = mw->menu.new_stack;
2462 /* compute the last identical different entry */
2463 for (i = 1; i < old_depth && i < new_depth; i++)
2464 if (old_stack [i] != new_stack [i])
2468 if (lw_menu_accelerate
2470 && last_same == old_depth - 1
2471 && old_stack [last_same]->contents)
2474 /* Memorize the previously selected item to be able to refresh it */
2475 old_selection = last_same + 1 < old_depth ? old_stack [last_same + 1] : NULL;
2476 new_selection = last_same + 1 < new_depth ? new_stack [last_same + 1] : NULL;
2478 /* updates old_state from new_state. It has to be done now because
2479 display_menu (called below) uses the old_stack to know what to display. */
2480 for (i = last_same + 1; i < new_depth; i++)
2481 old_stack [i] = new_stack [i];
2483 mw->menu.old_depth = new_depth;
2485 /* refresh the last seletion */
2486 selection_position.x = 0;
2487 selection_position.y = 0;
2488 display_menu (mw, last_same, new_selection == old_selection,
2489 &selection_position, NULL, NULL, old_selection, new_selection);
2491 /* Now popup the new menus */
2492 for (i = last_same + 1; i < new_depth && new_stack [i]->contents; i++)
2494 window_state *previous_ws = &windows [i - 1];
2495 window_state *ws = &windows [i];
2497 if (lw_menu_accelerate && i == new_depth - 1)
2500 ws->x = previous_ws->x + selection_position.x;
2501 ws->y = previous_ws->y + selection_position.y;
2503 /* take into account the slab around the new menu */
2504 ws->y -= mw->menu.shadow_thickness;
2507 widget_value *val = mw->menu.old_stack [i];
2508 if (val->contents->type == INCREMENTAL_TYPE)
2510 /* okay, we're now doing a lisp callback to incrementally generate
2511 more of the menu. */
2512 XtCallCallbackList ((Widget)mw,
2514 (XtPointer)val->contents);
2520 fit_to_screen (mw, ws, previous_ws, mw->menu.horizontal && i == 1);
2522 XClearWindow (XtDisplay (mw), ws->window);
2523 XMoveResizeWindow (XtDisplay (mw), ws->window, ws->x, ws->y,
2524 ws->width, ws->height);
2525 XMapRaised (XtDisplay (mw), ws->window);
2526 display_menu (mw, i, False, &selection_position, NULL, NULL, NULL, NULL);
2529 /* unmap the menus that popped down */
2531 last_same = new_depth;
2532 if (lw_menu_accelerate
2534 && new_stack [last_same - 1]->contents)
2537 for (i = last_same - 1; i < old_depth; i++)
2538 if (i >= last_same || !new_stack [i]->contents)
2539 XUnmapWindow (XtDisplay (mw), windows [i].window);
2543 motion_event_is_in_menu (XlwMenuWidget mw, XMotionEvent *ev, int level,
2544 XPoint *relative_pos)
2546 window_state *ws = &mw->menu.windows [level];
2547 int x = level == 0 ? ws->x : ws->x + mw->menu.shadow_thickness;
2548 int y = level == 0 ? ws->y : ws->y + mw->menu.shadow_thickness;
2549 relative_pos->x = ev->x_root - x;
2550 relative_pos->y = ev->y_root - y;
2551 return (x < ev->x_root && ev->x_root < (int) (x + ws->width) &&
2552 y < ev->y_root && ev->y_root < (int) (y + ws->height));
2556 map_event_to_widget_value (XlwMenuWidget mw, XMotionEvent *ev,
2557 widget_value **val_ptr, int *level,
2558 Boolean *inside_menu)
2561 XPoint relative_pos;
2565 *inside_menu = False;
2567 /* Find the window */
2569 for (i = mw->menu.old_depth - 1; i >= 0; i--)
2571 for (i = 0; i <= mw->menu.old_depth - 1; i++)
2574 ws = &mw->menu.windows [i];
2575 if (ws && motion_event_is_in_menu (mw, ev, i, &relative_pos))
2577 *inside_menu = True; /* special logic for menubar below... */
2578 if ((ev->type == ButtonPress) ||
2581 display_menu (mw, i, True, NULL, &relative_pos,
2582 val_ptr, NULL, NULL);
2586 *inside_menu = True;
2589 else if (mw->menu.horizontal || i == 0)
2591 /* if we're clicking on empty part of the menubar, then
2592 unpost the stay-up menu */
2593 *inside_menu = False;
2603 make_drawing_gcs (XlwMenuWidget mw)
2606 unsigned long flags = (GCFont | GCForeground | GCBackground);
2609 xgcv.font = default_font_of_font_list (mw->menu.font_list)->fid;
2611 xgcv.font = mw->menu.font->fid;
2614 xgcv.foreground = mw->core.background_pixel;
2615 xgcv.background = mw->menu.foreground;
2616 mw->menu.background_gc = XtGetGC ((Widget) mw, flags, &xgcv);
2618 xgcv.foreground = mw->menu.foreground;
2619 xgcv.background = mw->core.background_pixel;
2620 mw->menu.foreground_gc = XtGetGC ((Widget) mw, flags, &xgcv);
2622 if (mw->menu.select_color != (Pixel)-1)
2624 xgcv.foreground = mw->menu.select_color;
2628 Display *dpy = XtDisplay(mw);
2629 if (CellsOfScreen(DefaultScreenOfDisplay(dpy)) <= 2)
2631 xgcv.foreground = mw->menu.foreground;
2636 Colormap cmap = mw->core.colormap;
2637 xcolor.pixel = mw->core.background_pixel;
2638 XQueryColor (dpy, cmap, &xcolor);
2639 xcolor.red = (xcolor.red * 17) / 20;
2640 xcolor.green = (xcolor.green * 17) / 20;
2641 xcolor.blue = (xcolor.blue * 17) / 20;
2642 if (allocate_nearest_color (dpy, cmap, &xcolor))
2643 xgcv.foreground = xcolor.pixel;
2646 xgcv.background = mw->core.background_pixel;
2647 mw->menu.select_gc = XtGetGC ((Widget)mw, flags, &xgcv);
2649 xgcv.foreground = mw->menu.foreground;
2650 xgcv.background = mw->core.background_pixel;
2651 xgcv.fill_style = FillStippled;
2652 xgcv.stipple = mw->menu.gray_pixmap;
2653 mw->menu.inactive_gc = XtGetGC ((Widget)mw,
2654 (flags | GCFillStyle | GCStipple),
2657 xgcv.foreground = mw->menu.highlight_foreground;
2658 xgcv.background = mw->core.background_pixel;
2659 mw->menu.highlight_gc = XtGetGC ((Widget)mw, flags, &xgcv);
2661 xgcv.foreground = mw->menu.title_foreground;
2662 xgcv.background = mw->core.background_pixel;
2663 mw->menu.title_gc = XtGetGC ((Widget)mw, flags, &xgcv);
2665 xgcv.foreground = mw->menu.button_foreground;
2666 xgcv.background = mw->core.background_pixel;
2667 mw->menu.button_gc = XtGetGC ((Widget)mw, flags, &xgcv);
2669 xgcv.fill_style = FillStippled;
2670 xgcv.stipple = mw->menu.gray_pixmap;
2671 mw->menu.inactive_button_gc = XtGetGC ((Widget)mw,
2672 (flags | GCFillStyle | GCStipple),
2677 release_drawing_gcs (XlwMenuWidget mw)
2679 XtReleaseGC ((Widget) mw, mw->menu.foreground_gc);
2680 XtReleaseGC ((Widget) mw, mw->menu.button_gc);
2681 XtReleaseGC ((Widget) mw, mw->menu.highlight_gc);
2682 XtReleaseGC ((Widget) mw, mw->menu.title_gc);
2683 XtReleaseGC ((Widget) mw, mw->menu.inactive_gc);
2684 XtReleaseGC ((Widget) mw, mw->menu.inactive_button_gc);
2685 XtReleaseGC ((Widget) mw, mw->menu.background_gc);
2686 XtReleaseGC ((Widget) mw, mw->menu.select_gc);
2687 /* let's get some segvs if we try to use these... */
2688 mw->menu.foreground_gc = (GC) -1;
2689 mw->menu.button_gc = (GC) -1;
2690 mw->menu.highlight_gc = (GC) -1;
2691 mw->menu.title_gc = (GC) -1;
2692 mw->menu.inactive_gc = (GC) -1;
2693 mw->menu.inactive_button_gc = (GC) -1;
2694 mw->menu.background_gc = (GC) -1;
2695 mw->menu.select_gc = (GC) -1;
2698 #define MINL(x,y) ((((unsigned long) (x)) < ((unsigned long) (y))) \
2699 ? ((unsigned long) (x)) : ((unsigned long) (y)))
2702 make_shadow_gcs (XlwMenuWidget mw)
2705 unsigned long pm = 0;
2706 Display *dpy = XtDisplay ((Widget) mw);
2707 Colormap cmap = mw->core.colormap;
2709 int top_frobbed = 0, bottom_frobbed = 0;
2711 if (mw->menu.top_shadow_color == (Pixel) (-1))
2712 mw->menu.top_shadow_color = mw->core.background_pixel;
2713 if (mw->menu.bottom_shadow_color == (Pixel) (-1))
2714 mw->menu.bottom_shadow_color = mw->menu.foreground;
2716 if (mw->menu.top_shadow_color == mw->core.background_pixel ||
2717 mw->menu.top_shadow_color == mw->menu.foreground)
2719 topc.pixel = mw->core.background_pixel;
2720 XQueryColor (dpy, cmap, &topc);
2721 /* don't overflow/wrap! */
2722 topc.red = MINL (65535, topc.red * 1.2);
2723 topc.green = MINL (65535, topc.green * 1.2);
2724 topc.blue = MINL (65535, topc.blue * 1.2);
2725 if (allocate_nearest_color (dpy, cmap, &topc))
2727 if (topc.pixel == mw->core.background_pixel)
2729 XFreeColors( dpy, cmap, &topc.pixel, 1, 0);
2730 topc.red = MINL (65535, topc.red + 0x8000);
2731 topc.green = MINL (65535, topc.green + 0x8000);
2732 topc.blue = MINL (65535, topc.blue + 0x8000);
2733 if (allocate_nearest_color (dpy, cmap, &topc))
2735 mw->menu.top_shadow_color = topc.pixel;
2740 mw->menu.top_shadow_color = topc.pixel;
2746 if (mw->menu.bottom_shadow_color == mw->menu.foreground ||
2747 mw->menu.bottom_shadow_color == mw->core.background_pixel)
2749 botc.pixel = mw->core.background_pixel;
2750 XQueryColor (dpy, cmap, &botc);
2751 botc.red = (botc.red * 3) / 5;
2752 botc.green = (botc.green * 3) / 5;
2753 botc.blue = (botc.blue * 3) / 5;
2754 if (allocate_nearest_color (dpy, cmap, &botc))
2756 if (botc.pixel == mw->core.background_pixel)
2758 XFreeColors (dpy, cmap, &botc.pixel, 1, 0);
2759 botc.red = MINL (65535, botc.red + 0x4000);
2760 botc.green = MINL (65535, botc.green + 0x4000);
2761 botc.blue = MINL (65535, botc.blue + 0x4000);
2762 if (allocate_nearest_color (dpy, cmap, &botc))
2764 mw->menu.bottom_shadow_color = botc.pixel;
2769 mw->menu.bottom_shadow_color = botc.pixel;
2776 if (top_frobbed && bottom_frobbed)
2778 int top_avg = ((topc.red / 3) + (topc.green / 3) + (topc.blue / 3));
2779 int bot_avg = ((botc.red / 3) + (botc.green / 3) + (botc.blue / 3));
2780 if (bot_avg > top_avg)
2782 Pixel tmp = mw->menu.top_shadow_color;
2783 mw->menu.top_shadow_color = mw->menu.bottom_shadow_color;
2784 mw->menu.bottom_shadow_color = tmp;
2786 else if (topc.pixel == botc.pixel)
2788 if (botc.pixel == mw->menu.foreground)
2789 mw->menu.top_shadow_color = mw->core.background_pixel;
2791 mw->menu.bottom_shadow_color = mw->menu.foreground;
2795 if (!mw->menu.top_shadow_pixmap &&
2796 mw->menu.top_shadow_color == mw->core.background_pixel)
2798 mw->menu.top_shadow_pixmap = mw->menu.gray_pixmap;
2799 mw->menu.top_shadow_color = mw->menu.foreground;
2801 if (!mw->menu.bottom_shadow_pixmap &&
2802 mw->menu.bottom_shadow_color == mw->core.background_pixel)
2804 mw->menu.bottom_shadow_pixmap = mw->menu.gray_pixmap;
2805 mw->menu.bottom_shadow_color = mw->menu.foreground;
2808 xgcv.fill_style = FillOpaqueStippled;
2809 xgcv.foreground = mw->menu.top_shadow_color;
2810 xgcv.background = mw->core.background_pixel;
2811 /* xgcv.stipple = mw->menu.top_shadow_pixmap; gtb */
2813 if (mw->menu.top_shadow_pixmap &&
2814 mw->menu.top_shadow_pixmap != XmUNSPECIFIED_PIXMAP)
2815 xgcv.stipple = mw->menu.top_shadow_pixmap;
2819 xgcv.stipple = mw->menu.top_shadow_pixmap;
2820 #endif /* NEED_MOTIF */
2821 pm = (xgcv.stipple ? GCStipple|GCFillStyle : 0);
2822 mw->menu.shadow_top_gc =
2823 XtGetGC((Widget)mw, GCForeground|GCBackground|pm, &xgcv);
2825 xgcv.foreground = mw->menu.bottom_shadow_color;
2826 /* xgcv.stipple = mw->menu.bottom_shadow_pixmap; gtb */
2828 if (mw->menu.bottom_shadow_pixmap &&
2829 mw->menu.bottom_shadow_pixmap != XmUNSPECIFIED_PIXMAP)
2830 xgcv.stipple = mw->menu.bottom_shadow_pixmap;
2834 xgcv.stipple = mw->menu.bottom_shadow_pixmap;
2835 #endif /* NEED_MOTIF */
2836 pm = (xgcv.stipple ? GCStipple|GCFillStyle : 0);
2837 mw->menu.shadow_bottom_gc =
2838 XtGetGC ((Widget)mw, GCForeground|GCBackground|pm, &xgcv);
2843 release_shadow_gcs (XlwMenuWidget mw)
2845 XtReleaseGC ((Widget) mw, mw->menu.shadow_top_gc);
2846 XtReleaseGC ((Widget) mw, mw->menu.shadow_bottom_gc);
2851 extract_font_extents (XlwMenuWidget mw)
2854 /* Find the maximal ascent/descent of the fonts in the font list
2855 so that all menu items can be the same height... */
2856 mw->menu.font_ascent = 0;
2857 mw->menu.font_descent = 0;
2860 XmFontContext context;
2861 #if (XmVersion >= 1002)
2862 XmFontListEntry fontentry;
2864 XmStringCharSet charset;
2868 if (! XmFontListInitFontContext (&context, mw->menu.font_list))
2870 #if (XmVersion >= 1002)
2871 /* There is a BUG in the 1.2 version of XmFontListGetNextFont() (or more
2872 specifically, in _XmGetFirstFont()) that can cause a null pointer to be
2873 passed to XFontsOfFontSet. Use XmFontListNextEntry(), which is the
2874 newer equivalent, instead. Also, it supports font sets, and the
2875 older function doesn't. */
2876 while ((fontentry = XmFontListNextEntry (context)))
2880 XtPointer one_of_them = XmFontListEntryGetFont (fontentry, &rettype);
2881 if (rettype == XmFONT_IS_FONTSET)
2883 XFontSet fontset = (XFontSet) one_of_them;
2884 XFontStruct **fontstruct_list;
2885 char **fontname_list;
2886 int fontcount = XFontsOfFontSet (fontset, &fontstruct_list,
2888 while (--fontcount >= 0)
2890 font = fontstruct_list[fontcount];
2891 if (font->ascent > (int) mw->menu.font_ascent)
2892 mw->menu.font_ascent = font->ascent;
2893 if (font->descent > (int) mw->menu.font_descent)
2894 mw->menu.font_descent = font->descent;
2897 else /* XmFONT_IS_FONT */
2899 font = (XFontStruct *) one_of_them;
2900 if (font->ascent > (int) mw->menu.font_ascent)
2901 mw->menu.font_ascent = font->ascent;
2902 if (font->descent > (int) mw->menu.font_descent)
2903 mw->menu.font_descent = font->descent;
2906 #else /* motif 1.1 */
2907 while (XmFontListGetNextFont (context, &charset, &font))
2909 if (font->ascent > (int) mw->menu.font_ascent)
2910 mw->menu.font_ascent = font->ascent;
2911 if (font->descent > (int) mw->menu.font_descent)
2912 mw->menu.font_descent = font->descent;
2915 #endif /* Motif version */
2916 XmFontListFreeFontContext (context);
2918 #else /* Not Motif */
2919 # ifdef USE_XFONTSET
2920 XFontStruct **fontstruct_list;
2921 char **fontname_list;
2923 int fontcount = XFontsOfFontSet(mw->menu.font_set, &fontstruct_list,
2925 mw->menu.font_ascent = 0;
2926 mw->menu.font_descent = 0;
2927 # if 0 /* nasty, personal debug, Kazz */
2928 fprintf(stderr, "fontSet count is %d\n", fontcount);
2930 while (--fontcount >= 0) {
2931 font = fontstruct_list[fontcount];
2932 if (font->ascent > (int) mw->menu.font_ascent)
2933 mw->menu.font_ascent = font->ascent;
2934 if (font->descent > (int) mw->menu.font_descent)
2935 mw->menu.font_descent = font->descent;
2937 # else /* ! USE_XFONTSET */
2938 mw->menu.font_ascent = mw->menu.font->ascent;
2939 mw->menu.font_descent = mw->menu.font->descent;
2941 #endif /* NEED_MOTIF */
2945 static XFontStruct *
2946 default_font_of_font_list (XmFontList font_list)
2948 XFontStruct *font = 0;
2950 /* Xm/Label.c does this: */
2951 _XmFontListGetDefaultFont (font_list, &font);
2954 XmFontContext context;
2955 #if (XmVersion >= 1002)
2956 XmFontListEntry fontentry;
2958 XtPointer one_of_them;
2960 XmStringCharSet charset;
2963 if (! XmFontListInitFontContext (&context, font_list))
2965 #if (XmVersion >= 1002)
2966 /* There is a BUG in the 1.2 version of XmFontListGetNextFont() (or more
2967 specifically, in _XmGetFirstFont()) that can cause a null pointer to be
2968 passed to XFontsOfFontSet. Use XmFontListNextEntry(), which is the
2969 newer equivalent, instead. */
2970 fontentry = XmFontListNextEntry (context);
2971 one_of_them = XmFontListEntryGetFont (fontentry, &rettype);
2972 if (rettype == XmFONT_IS_FONTSET)
2974 XFontSet fontset = (XFontSet) one_of_them;
2975 XFontStruct **fontstruct_list;
2976 char **fontname_list;
2977 (void) XFontsOfFontSet (fontset, &fontstruct_list, &fontname_list);
2978 font = fontstruct_list[0];
2980 else /* XmFONT_IS_FONT */
2982 font = (XFontStruct *) one_of_them;
2985 if (! XmFontListGetNextFont (context, &charset, &font))
2989 XmFontListFreeFontContext (context);
2993 if (! font) abort ();
2996 #endif /* NEED_MOTIF */
2999 XlwMenuInitialize (Widget request, Widget new, ArgList args,
3002 /* Get the GCs and the widget size */
3003 XlwMenuWidget mw = (XlwMenuWidget)new;
3005 XSetWindowAttributes xswa;
3008 Window window = RootWindowOfScreen (DefaultScreenOfDisplay (XtDisplay (mw)));
3009 Display *display = XtDisplay (mw);
3011 /* mw->menu.cursor = XCreateFontCursor (display, mw->menu.cursor_shape); */
3012 mw->menu.cursor = mw->menu.cursor_shape;
3014 mw->menu.gray_pixmap =
3015 XCreatePixmapFromBitmapData (display, window, (char *) gray_bits,
3016 gray_width, gray_height, 1, 0, 1);
3019 /* The menu.font_list slot came from the *fontList resource (Motif standard.)
3020 The menu.font_list_2 slot came from the *font resource, for backward
3021 compatibility with older versions of this code, and consistency with the
3022 rest of emacs. If both font and fontList are specified, we use font.
3023 If only one is specified, we use that. If neither are specified, we
3024 use the "fallback" value. What a kludge!!!
3026 Note that this has the bug that a more general wildcard like "*fontList:"
3027 will override a more specific resource like "Emacs*menubar.font:". But
3028 I can't think of a way around that.
3030 if (mw->menu.font_list) /* if *fontList is specified, use that */
3032 else if (mw->menu.font_list_2) /* else if *font is specified, use that */
3033 mw->menu.font_list = mw->menu.font_list_2;
3034 else /* otherwise use default */
3035 mw->menu.font_list = mw->menu.fallback_font_list;
3038 make_drawing_gcs (mw);
3039 make_shadow_gcs (mw);
3040 extract_font_extents (mw);
3042 xswa.background_pixel = mw->core.background_pixel;
3043 xswa.border_pixel = mw->core.border_pixel;
3044 mask = CWBackPixel | CWBorderPixel;
3046 mw->menu.popped_up = False;
3047 mw->menu.pointer_grabbed = False;
3048 mw->menu.next_release_must_exit = False;
3050 mw->menu.old_depth = 1;
3051 mw->menu.old_stack = XtNew (widget_value*);
3052 mw->menu.old_stack_length = 1;
3053 mw->menu.old_stack [0] = mw->menu.contents;
3055 mw->menu.new_depth = 0;
3056 mw->menu.new_stack = 0;
3057 mw->menu.new_stack_length = 0;
3058 push_new_stack (mw, mw->menu.contents);
3060 mw->menu.windows = XtNew (window_state);
3061 mw->menu.windows_length = 1;
3062 mw->menu.windows [0].x = 0;
3063 mw->menu.windows [0].y = 0;
3064 mw->menu.windows [0].width = 0;
3065 mw->menu.windows [0].height = 0;
3068 mw->core.width = mw->menu.windows [0].width;
3069 mw->core.height = mw->menu.windows [0].height;
3073 XlwMenuClassInitialize (void)
3075 initialize_massaged_resource_char();
3079 XlwMenuRealize (Widget w, Mask *valueMask, XSetWindowAttributes *attributes)
3081 XlwMenuWidget mw = (XlwMenuWidget)w;
3082 XSetWindowAttributes xswa;
3085 (*xlwMenuWidgetClass->core_class.superclass->core_class.realize)
3086 (w, valueMask, attributes);
3088 xswa.save_under = True;
3089 xswa.cursor = mw->menu.cursor_shape;
3090 mask = CWSaveUnder | CWCursor;
3091 if (mw->menu.use_backing_store)
3093 xswa.backing_store = Always;
3094 mask |= CWBackingStore;
3096 XChangeWindowAttributes (XtDisplay (w), XtWindow (w), mask, &xswa);
3098 mw->menu.windows [0].window = XtWindow (w);
3099 mw->menu.windows [0].x = w->core.x;
3100 mw->menu.windows [0].y = w->core.y;
3101 mw->menu.windows [0].width = w->core.width;
3102 mw->menu.windows [0].height = w->core.height;
3105 /* Only the toplevel menubar/popup is a widget so it's the only one that
3106 receives expose events through Xt. So we repaint all the other panes
3107 when receiving an Expose event. */
3109 XlwMenuRedisplay (Widget w, XEvent *ev, Region region)
3111 XlwMenuWidget mw = (XlwMenuWidget)w;
3114 if (mw->core.being_destroyed) return;
3116 for (i = 0; i < mw->menu.old_depth; i++)
3117 display_menu (mw, i, False, NULL, NULL, NULL, NULL, NULL);
3118 set_new_state (mw, NULL, mw->menu.old_depth); /* #### - ??? */
3119 remap_menubar (mw); /* #### - do these two lines do anything? */
3123 XlwMenuDestroy (Widget w)
3126 XlwMenuWidget mw = (XlwMenuWidget) w;
3128 if (mw->menu.pointer_grabbed)
3130 XtUngrabPointer (w, CurrentTime);
3131 mw->menu.pointer_grabbed = False;
3134 release_drawing_gcs (mw);
3135 release_shadow_gcs (mw);
3137 /* this doesn't come from the resource db but is created explicitly
3138 so we must free it ourselves. */
3139 XFreePixmap (XtDisplay (mw), mw->menu.gray_pixmap);
3140 mw->menu.gray_pixmap = (Pixmap) -1;
3142 /* Don't free mw->menu.contents because that comes from our creator.
3143 The `*_stack' elements are just pointers into `contents' so leave
3144 that alone too. But free the stacks themselves. */
3145 if (mw->menu.old_stack) XtFree ((char *) mw->menu.old_stack);
3146 if (mw->menu.new_stack) XtFree ((char *) mw->menu.new_stack);
3148 /* Remember, you can't free anything that came from the resource
3149 database. This includes:
3151 mw->menu.top_shadow_pixmap
3152 mw->menu.bottom_shadow_pixmap
3155 Also the color cells of top_shadow_color, bottom_shadow_color,
3156 foreground, and button_foreground will never be freed until this
3157 client exits. Nice, eh?
3160 /* start from 1 because the one in slot 0 is w->core.window */
3161 for (i = 1; i < mw->menu.windows_length; i++)
3162 XDestroyWindow (XtDisplay (mw), mw->menu.windows [i].window);
3163 if (mw->menu.windows)
3164 XtFree ((char *) mw->menu.windows);
3168 XlwMenuSetValues (Widget current, Widget request, Widget new, ArgList args,
3171 XlwMenuWidget oldmw = (XlwMenuWidget)current;
3172 XlwMenuWidget newmw = (XlwMenuWidget)new;
3173 Boolean redisplay = False;
3176 if (newmw->menu.contents
3177 && newmw->menu.contents->contents
3178 && newmw->menu.contents->contents->change >= VISIBLE_CHANGE)
3181 if (newmw->core.background_pixel != oldmw->core.background_pixel
3182 || newmw->menu.foreground != oldmw->menu.foreground
3183 /* For the XEditResource protocol, which may want to change the font. */
3185 || newmw->menu.font_list != oldmw->menu.font_list
3186 || newmw->menu.font_list_2 != oldmw->menu.font_list_2
3187 || newmw->menu.fallback_font_list != oldmw->menu.fallback_font_list
3189 || newmw->menu.font != oldmw->menu.font
3193 release_drawing_gcs (newmw);
3194 make_drawing_gcs (newmw);
3197 for (i = 0; i < oldmw->menu.windows_length; i++)
3199 XSetWindowBackground (XtDisplay (oldmw),
3200 oldmw->menu.windows [i].window,
3201 newmw->core.background_pixel);
3202 /* clear windows and generate expose events */
3203 XClearArea (XtDisplay (oldmw), oldmw->menu.windows[i].window,
3212 XlwMenuResize (Widget w)
3214 XlwMenuWidget mw = (XlwMenuWidget)w;
3216 mw->menu.windows [0].width = mw->core.width;
3217 mw->menu.windows [0].height = mw->core.height;
3220 \f/* Action procedures */
3222 handle_single_motion_event (XlwMenuWidget mw, XMotionEvent *ev,
3229 if (!map_event_to_widget_value (mw, ev, &val, &level, &stay_up))
3231 /* we wind up here when: (a) the event is in the menubar, (b) the
3232 event isn't in the menubar or any of the panes, (c) the event is on
3233 a disabled menu item */
3234 pop_new_stack_if_no_contents (mw);
3235 if (select_p && !stay_up) {
3236 /* pop down all menus and exit */
3237 mw->menu.next_release_must_exit = True;
3238 set_new_state(mw, (val = NULL), 1);
3243 /* we wind up here when: (a) the event pops up a pull_right menu,
3244 (b) a menu item that is not disabled is highlighted */
3245 if (select_p && mw->menu.bounce_down
3246 && close_to_reference_time((Widget)mw,
3247 mw->menu.menu_bounce_time,
3250 /* motion can cause more than one event. Don't bounce right back
3251 up if we've just bounced down. */
3254 else if (select_p && mw->menu.bounce_down &&
3255 mw->menu.last_selected_val &&
3256 (mw->menu.last_selected_val == val))
3258 val = NULL; /* assigned to mw->last_selected_val below */
3259 mw->menu.menu_bounce_time = ev->time;
3260 /* popdown last menu if we're selecting the same menu item as we did
3261 last time and the XlwMenu.bounceDown resource is set, if the
3262 item is on the menubar itself, then exit. */
3263 if (level == (mw->menu.popped_up ? 0 : 1))
3264 mw->menu.next_release_must_exit = True;
3267 mw->menu.menu_bounce_time = 0;
3268 set_new_state (mw, val, level);
3270 mw->menu.last_selected_val = val;
3273 /* Sync with the display. Makes it feel better on X terms. */
3274 XFlush (XtDisplay (mw));
3278 handle_motion_event (XlwMenuWidget mw, XMotionEvent *ev,
3283 unsigned int state = ev->state;
3284 XMotionEvent *event= ev, dummy;
3286 /* allow motion events to be generated again */
3287 dummy.window = ev->window;
3289 && XQueryPointer (XtDisplay (mw), dummy.window,
3290 &dummy.root, &dummy.subwindow,
3291 &dummy.x_root, &dummy.y_root,
3294 && dummy.state == state
3295 && (dummy.x_root != x || dummy.y_root != y))
3297 /* don't handle the event twice or that breaks bounce_down. --Stig */
3298 dummy.type = ev->type;
3302 lw_menu_accelerate = False;
3303 handle_single_motion_event (mw, event, select_p);
3306 Time x_focus_timestamp_really_sucks_fix_me_better;
3309 Start (Widget w, XEvent *ev, String *params, Cardinal *num_params)
3311 XlwMenuWidget mw = (XlwMenuWidget)w;
3313 lw_menubar_widget = w;
3315 lw_menu_active = True;
3317 if (!mw->menu.pointer_grabbed)
3319 mw->menu.menu_post_time = ev->xbutton.time;
3320 mw->menu.menu_bounce_time = 0;
3321 mw->menu.next_release_must_exit = True;
3322 mw->menu.last_selected_val = NULL;
3323 x_focus_timestamp_really_sucks_fix_me_better =
3324 ((XButtonPressedEvent*)ev)->time;
3325 XtCallCallbackList ((Widget)mw, mw->menu.open, NULL);
3327 /* notes the absolute position of the menubar window */
3328 mw->menu.windows [0].x = ev->xmotion.x_root - ev->xmotion.x;
3329 mw->menu.windows [0].y = ev->xmotion.y_root - ev->xmotion.y;
3331 XtGrabPointer ((Widget)mw, False,
3332 (ButtonMotionMask | ButtonReleaseMask | ButtonPressMask),
3333 GrabModeAsync, GrabModeAsync,
3334 None, mw->menu.cursor_shape,
3335 ((XButtonPressedEvent*)ev)->time);
3336 mw->menu.pointer_grabbed = True;
3339 /* handles the down like a move, slots are mostly compatible */
3340 handle_motion_event (mw, &ev->xmotion, True);
3344 Drag (Widget w, XEvent *ev, String *params, Cardinal *num_params)
3346 XlwMenuWidget mw = (XlwMenuWidget)w;
3347 handle_motion_event (mw, &ev->xmotion, False);
3351 Select (Widget w, XEvent *ev, String *params, Cardinal *num_params)
3353 XlwMenuWidget mw = (XlwMenuWidget)w;
3354 widget_value *selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
3356 lw_menu_accelerate = False;
3358 /* If user releases the button quickly, without selecting anything,
3359 after the initial down-click that brought the menu up,
3361 if ((selected_item == 0 || selected_item->call_data == 0)
3362 && (!mw->menu.next_release_must_exit
3363 || close_to_reference_time(w, mw->menu.menu_post_time, ev)))
3365 mw->menu.next_release_must_exit = False;
3369 /* pop down everything */
3370 mw->menu.new_depth = 1;
3373 /* Destroy() only gets called for popup menus. Menubar widgets aren't
3374 destroyed when their menu panes get nuked. */
3375 if (mw->menu.pointer_grabbed)
3377 XtUngrabPointer ((Widget)w, ev->xmotion.time);
3378 mw->menu.pointer_grabbed = False;
3381 if (mw->menu.popped_up)
3383 mw->menu.popped_up = False;
3384 XtPopdown (XtParent (mw));
3387 lw_menu_active = False;
3389 x_focus_timestamp_really_sucks_fix_me_better =
3390 ((XButtonPressedEvent*)ev)->time;
3393 XtCallCallbackList ((Widget) mw, mw->menu.select, (XtPointer) selected_item);
3396 \f/* Action procedures for keyboard accelerators */
3400 xlw_set_menu (Widget w, widget_value *val)
3402 lw_menubar_widget = w;
3403 set_new_state ((XlwMenuWidget)w, val, 1);
3406 /* prepare the menu structure via the call-backs */
3408 xlw_map_menu (Time t)
3410 XlwMenuWidget mw = (XlwMenuWidget)lw_menubar_widget;
3412 lw_menu_accelerate = True;
3414 if (!mw->menu.pointer_grabbed)
3416 XWindowAttributes ret;
3419 unsigned int num_waste;
3421 lw_menu_active = True;
3423 mw->menu.menu_post_time = t;
3424 mw->menu.menu_bounce_time = 0;
3426 mw->menu.next_release_must_exit = True;
3427 mw->menu.last_selected_val = NULL;
3429 XtCallCallbackList ((Widget)mw, mw->menu.open, NULL);
3431 /* do this for keyboards too! */
3432 /* notes the absolute position of the menubar window */
3434 mw->menu.windows [0].x = ev->xmotion.x_root - ev->xmotion.x;
3435 mw->menu.windows [0].y = ev->xmotion.y_root - ev->xmotion.y;
3438 /* get the geometry of the menubar */
3440 /* there has to be a better way than this. */
3442 mw->menu.windows [0].x = 0;
3443 mw->menu.windows [0].y = 0;
3445 parent = XtWindow (lw_menubar_widget);
3448 XGetWindowAttributes (XtDisplay (lw_menubar_widget), parent, &ret);
3449 mw->menu.windows [0].x += ret.x;
3450 mw->menu.windows [0].y += ret.y;
3453 XQueryTree (XtDisplay (lw_menubar_widget), parent, &root, &parent, &waste,
3460 while (parent != root);
3462 XtGrabPointer ((Widget)mw, False,
3463 (ButtonMotionMask | ButtonReleaseMask | ButtonPressMask),
3464 GrabModeAsync, GrabModeAsync,
3465 None, mw->menu.cursor_shape, t);
3466 mw->menu.pointer_grabbed = True;
3470 /* display the stupid menu already */
3472 xlw_display_menu (Time t)
3474 XlwMenuWidget mw = (XlwMenuWidget)lw_menubar_widget;
3476 lw_menu_accelerate = True;
3480 /* Sync with the display. Makes it feel better on X terms. */
3481 XFlush (XtDisplay (mw));
3484 /* push a sub menu */
3486 xlw_push_menu (widget_value *val)
3488 push_new_stack ((XlwMenuWidget)lw_menubar_widget, val);
3491 /* pop a sub menu */
3495 if (((XlwMenuWidget)lw_menubar_widget)->menu.new_depth > 0)
3496 ((XlwMenuWidget)lw_menubar_widget)->menu.new_depth --;
3503 xlw_kill_menus (widget_value *val)
3505 XlwMenuWidget mw = (XlwMenuWidget)lw_menubar_widget;
3507 lw_menu_accelerate = False;
3509 mw->menu.new_depth = 1;
3512 if (mw->menu.pointer_grabbed)
3514 XtUngrabPointer (lw_menubar_widget, CurrentTime);
3515 mw->menu.pointer_grabbed = False;
3518 lw_menu_active = False;
3519 XtCallCallbackList (lw_menubar_widget, mw->menu.select, (XtPointer)val);
3522 /* set the menu item */
3524 xlw_set_item (widget_value *val)
3526 if (((XlwMenuWidget)lw_menubar_widget)->menu.new_depth > 0)
3527 ((XlwMenuWidget) lw_menubar_widget)->menu.new_depth --;
3528 push_new_stack ((XlwMenuWidget) lw_menubar_widget, val);
3531 /* get either the current entry or a list of all entries in the current submenu */
3533 xlw_get_entries (int allp)
3535 XlwMenuWidget mw = (XlwMenuWidget)lw_menubar_widget;
3538 if (mw->menu.new_depth >= 2)
3539 return mw->menu.new_stack [mw->menu.new_depth - 2]->contents;
3541 return mw->menu.new_stack[0];
3544 if (mw->menu.new_depth >= 1)
3545 return mw->menu.new_stack [mw->menu.new_depth - 1];
3551 xlw_menu_level (void)
3553 return ((XlwMenuWidget)lw_menubar_widget)->menu.new_depth;
3557 /* Special code to pop-up a menu */
3559 xlw_pop_up_menu (XlwMenuWidget mw, XButtonPressedEvent *event)
3561 int x = event->x_root;
3562 int y = event->y_root;
3565 int borderwidth = mw->menu.shadow_thickness;
3566 Screen* screen = XtScreen (mw);
3568 mw->menu.menu_post_time = event->time;
3569 mw->menu.menu_bounce_time = 0;
3570 mw->menu.next_release_must_exit = True;
3571 mw->menu.last_selected_val = NULL;
3573 XtCallCallbackList ((Widget) mw, mw->menu.open, NULL);
3577 w = mw->menu.windows [0].width;
3578 h = mw->menu.windows [0].height;
3583 if (x < borderwidth)
3586 if (x > WidthOfScreen (screen) - w - 2 * borderwidth)
3587 x = WidthOfScreen (screen) - w - 2 * borderwidth;
3589 if (y < borderwidth)
3592 if (y > HeightOfScreen (screen) - h - 2 * borderwidth)
3593 y = HeightOfScreen (screen) - h - 2 * borderwidth;
3595 mw->menu.popped_up = True;
3596 XtConfigureWidget (XtParent (mw), x, y, w, h,
3597 XtParent (mw)->core.border_width);
3598 XtPopup (XtParent (mw), XtGrabExclusive);
3599 display_menu (mw, 0, False, NULL, NULL, NULL, NULL, NULL);
3600 if (!mw->menu.pointer_grabbed)
3602 XtGrabPointer ((Widget)mw, False,
3603 (ButtonMotionMask | ButtonReleaseMask | ButtonPressMask),
3604 GrabModeAsync, GrabModeAsync,
3605 None, mw->menu.cursor_shape, event->time);
3606 mw->menu.pointer_grabbed = True;
3609 mw->menu.windows [0].x = x + borderwidth;
3610 mw->menu.windows [0].y = y + borderwidth;
3612 handle_motion_event (mw, (XMotionEvent *) event, True);
3618 * This is a horrible function which should not be needed.
3619 * use it to put the resize method back the way the XlwMenu
3620 * class initializer put it. Motif screws with this when
3621 * the XlwMenu class gets instantiated.
3624 xlw_unmunge_class_resize (Widget w)
3626 if (w->core.widget_class->core_class.resize != XlwMenuResize)
3627 w->core.widget_class->core_class.resize = XlwMenuResize;