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 */
2812 if (mw->menu.top_shadow_pixmap &&
2813 mw->menu.top_shadow_pixmap != XmUNSPECIFIED_PIXMAP)
2814 xgcv.stipple = mw->menu.top_shadow_pixmap;
2817 pm = (xgcv.stipple ? GCStipple|GCFillStyle : 0);
2818 mw->menu.shadow_top_gc =
2819 XtGetGC((Widget)mw, GCForeground|GCBackground|pm, &xgcv);
2821 xgcv.foreground = mw->menu.bottom_shadow_color;
2822 /* xgcv.stipple = mw->menu.bottom_shadow_pixmap; gtb */
2823 if (mw->menu.bottom_shadow_pixmap &&
2824 mw->menu.bottom_shadow_pixmap != XmUNSPECIFIED_PIXMAP)
2825 xgcv.stipple = mw->menu.bottom_shadow_pixmap;
2828 pm = (xgcv.stipple ? GCStipple|GCFillStyle : 0);
2829 mw->menu.shadow_bottom_gc =
2830 XtGetGC ((Widget)mw, GCForeground|GCBackground|pm, &xgcv);
2835 release_shadow_gcs (XlwMenuWidget mw)
2837 XtReleaseGC ((Widget) mw, mw->menu.shadow_top_gc);
2838 XtReleaseGC ((Widget) mw, mw->menu.shadow_bottom_gc);
2843 extract_font_extents (XlwMenuWidget mw)
2846 /* Find the maximal ascent/descent of the fonts in the font list
2847 so that all menu items can be the same height... */
2848 mw->menu.font_ascent = 0;
2849 mw->menu.font_descent = 0;
2852 XmFontContext context;
2853 #if (XmVersion >= 1002)
2854 XmFontListEntry fontentry;
2856 XmStringCharSet charset;
2860 if (! XmFontListInitFontContext (&context, mw->menu.font_list))
2862 #if (XmVersion >= 1002)
2863 /* There is a BUG in the 1.2 version of XmFontListGetNextFont() (or more
2864 specifically, in _XmGetFirstFont()) that can cause a null pointer to be
2865 passed to XFontsOfFontSet. Use XmFontListNextEntry(), which is the
2866 newer equivalent, instead. Also, it supports font sets, and the
2867 older function doesn't. */
2868 while ((fontentry = XmFontListNextEntry (context)))
2872 XtPointer one_of_them = XmFontListEntryGetFont (fontentry, &rettype);
2873 if (rettype == XmFONT_IS_FONTSET)
2875 XFontSet fontset = (XFontSet) one_of_them;
2876 XFontStruct **fontstruct_list;
2877 char **fontname_list;
2878 int fontcount = XFontsOfFontSet (fontset, &fontstruct_list,
2880 while (--fontcount >= 0)
2882 font = fontstruct_list[fontcount];
2883 if (font->ascent > (int) mw->menu.font_ascent)
2884 mw->menu.font_ascent = font->ascent;
2885 if (font->descent > (int) mw->menu.font_descent)
2886 mw->menu.font_descent = font->descent;
2889 else /* XmFONT_IS_FONT */
2891 font = (XFontStruct *) one_of_them;
2892 if (font->ascent > (int) mw->menu.font_ascent)
2893 mw->menu.font_ascent = font->ascent;
2894 if (font->descent > (int) mw->menu.font_descent)
2895 mw->menu.font_descent = font->descent;
2898 #else /* motif 1.1 */
2899 while (XmFontListGetNextFont (context, &charset, &font))
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 #endif /* Motif version */
2908 XmFontListFreeFontContext (context);
2910 #else /* Not Motif */
2911 # ifdef USE_XFONTSET
2912 XFontStruct **fontstruct_list;
2913 char **fontname_list;
2915 int fontcount = XFontsOfFontSet(mw->menu.font_set, &fontstruct_list,
2917 mw->menu.font_ascent = 0;
2918 mw->menu.font_descent = 0;
2919 # if 0 /* nasty, personal debug, Kazz */
2920 fprintf(stderr, "fontSet count is %d\n", fontcount);
2922 while (--fontcount >= 0) {
2923 font = fontstruct_list[fontcount];
2924 if (font->ascent > (int) mw->menu.font_ascent)
2925 mw->menu.font_ascent = font->ascent;
2926 if (font->descent > (int) mw->menu.font_descent)
2927 mw->menu.font_descent = font->descent;
2929 # else /* ! USE_XFONTSET */
2930 mw->menu.font_ascent = mw->menu.font->ascent;
2931 mw->menu.font_descent = mw->menu.font->descent;
2933 #endif /* NEED_MOTIF */
2937 static XFontStruct *
2938 default_font_of_font_list (XmFontList font_list)
2940 XFontStruct *font = 0;
2942 /* Xm/Label.c does this: */
2943 _XmFontListGetDefaultFont (font_list, &font);
2946 XmFontContext context;
2947 #if (XmVersion >= 1002)
2948 XmFontListEntry fontentry;
2950 XtPointer one_of_them;
2952 XmStringCharSet charset;
2955 if (! XmFontListInitFontContext (&context, font_list))
2957 #if (XmVersion >= 1002)
2958 /* There is a BUG in the 1.2 version of XmFontListGetNextFont() (or more
2959 specifically, in _XmGetFirstFont()) that can cause a null pointer to be
2960 passed to XFontsOfFontSet. Use XmFontListNextEntry(), which is the
2961 newer equivalent, instead. */
2962 fontentry = XmFontListNextEntry (context);
2963 one_of_them = XmFontListEntryGetFont (fontentry, &rettype);
2964 if (rettype == XmFONT_IS_FONTSET)
2966 XFontSet fontset = (XFontSet) one_of_them;
2967 XFontStruct **fontstruct_list;
2968 char **fontname_list;
2969 (void) XFontsOfFontSet (fontset, &fontstruct_list, &fontname_list);
2970 font = fontstruct_list[0];
2972 else /* XmFONT_IS_FONT */
2974 font = (XFontStruct *) one_of_them;
2977 if (! XmFontListGetNextFont (context, &charset, &font))
2981 XmFontListFreeFontContext (context);
2985 if (! font) abort ();
2988 #endif /* NEED_MOTIF */
2991 XlwMenuInitialize (Widget request, Widget new, ArgList args,
2994 /* Get the GCs and the widget size */
2995 XlwMenuWidget mw = (XlwMenuWidget)new;
2997 XSetWindowAttributes xswa;
3000 Window window = RootWindowOfScreen (DefaultScreenOfDisplay (XtDisplay (mw)));
3001 Display *display = XtDisplay (mw);
3003 /* mw->menu.cursor = XCreateFontCursor (display, mw->menu.cursor_shape); */
3004 mw->menu.cursor = mw->menu.cursor_shape;
3006 mw->menu.gray_pixmap =
3007 XCreatePixmapFromBitmapData (display, window, (char *) gray_bits,
3008 gray_width, gray_height, 1, 0, 1);
3011 /* The menu.font_list slot came from the *fontList resource (Motif standard.)
3012 The menu.font_list_2 slot came from the *font resource, for backward
3013 compatibility with older versions of this code, and consistency with the
3014 rest of emacs. If both font and fontList are specified, we use font.
3015 If only one is specified, we use that. If neither are specified, we
3016 use the "fallback" value. What a kludge!!!
3018 Note that this has the bug that a more general wildcard like "*fontList:"
3019 will override a more specific resource like "Emacs*menubar.font:". But
3020 I can't think of a way around that.
3022 if (mw->menu.font_list) /* if *fontList is specified, use that */
3024 else if (mw->menu.font_list_2) /* else if *font is specified, use that */
3025 mw->menu.font_list = mw->menu.font_list_2;
3026 else /* otherwise use default */
3027 mw->menu.font_list = mw->menu.fallback_font_list;
3030 make_drawing_gcs (mw);
3031 make_shadow_gcs (mw);
3032 extract_font_extents (mw);
3034 xswa.background_pixel = mw->core.background_pixel;
3035 xswa.border_pixel = mw->core.border_pixel;
3036 mask = CWBackPixel | CWBorderPixel;
3038 mw->menu.popped_up = False;
3039 mw->menu.pointer_grabbed = False;
3040 mw->menu.next_release_must_exit = False;
3042 mw->menu.old_depth = 1;
3043 mw->menu.old_stack = XtNew (widget_value*);
3044 mw->menu.old_stack_length = 1;
3045 mw->menu.old_stack [0] = mw->menu.contents;
3047 mw->menu.new_depth = 0;
3048 mw->menu.new_stack = 0;
3049 mw->menu.new_stack_length = 0;
3050 push_new_stack (mw, mw->menu.contents);
3052 mw->menu.windows = XtNew (window_state);
3053 mw->menu.windows_length = 1;
3054 mw->menu.windows [0].x = 0;
3055 mw->menu.windows [0].y = 0;
3056 mw->menu.windows [0].width = 0;
3057 mw->menu.windows [0].height = 0;
3060 mw->core.width = mw->menu.windows [0].width;
3061 mw->core.height = mw->menu.windows [0].height;
3065 XlwMenuClassInitialize (void)
3067 initialize_massaged_resource_char();
3071 XlwMenuRealize (Widget w, Mask *valueMask, XSetWindowAttributes *attributes)
3073 XlwMenuWidget mw = (XlwMenuWidget)w;
3074 XSetWindowAttributes xswa;
3077 (*xlwMenuWidgetClass->core_class.superclass->core_class.realize)
3078 (w, valueMask, attributes);
3080 xswa.save_under = True;
3081 xswa.cursor = mw->menu.cursor_shape;
3082 mask = CWSaveUnder | CWCursor;
3083 if (mw->menu.use_backing_store)
3085 xswa.backing_store = Always;
3086 mask |= CWBackingStore;
3088 XChangeWindowAttributes (XtDisplay (w), XtWindow (w), mask, &xswa);
3090 mw->menu.windows [0].window = XtWindow (w);
3091 mw->menu.windows [0].x = w->core.x;
3092 mw->menu.windows [0].y = w->core.y;
3093 mw->menu.windows [0].width = w->core.width;
3094 mw->menu.windows [0].height = w->core.height;
3097 /* Only the toplevel menubar/popup is a widget so it's the only one that
3098 receives expose events through Xt. So we repaint all the other panes
3099 when receiving an Expose event. */
3101 XlwMenuRedisplay (Widget w, XEvent *ev, Region region)
3103 XlwMenuWidget mw = (XlwMenuWidget)w;
3106 if (mw->core.being_destroyed) return;
3108 for (i = 0; i < mw->menu.old_depth; i++)
3109 display_menu (mw, i, False, NULL, NULL, NULL, NULL, NULL);
3110 set_new_state (mw, NULL, mw->menu.old_depth); /* #### - ??? */
3111 remap_menubar (mw); /* #### - do these two lines do anything? */
3115 XlwMenuDestroy (Widget w)
3118 XlwMenuWidget mw = (XlwMenuWidget) w;
3120 if (mw->menu.pointer_grabbed)
3122 XtUngrabPointer (w, CurrentTime);
3123 mw->menu.pointer_grabbed = False;
3126 release_drawing_gcs (mw);
3127 release_shadow_gcs (mw);
3129 /* this doesn't come from the resource db but is created explicitly
3130 so we must free it ourselves. */
3131 XFreePixmap (XtDisplay (mw), mw->menu.gray_pixmap);
3132 mw->menu.gray_pixmap = (Pixmap) -1;
3134 /* Don't free mw->menu.contents because that comes from our creator.
3135 The `*_stack' elements are just pointers into `contents' so leave
3136 that alone too. But free the stacks themselves. */
3137 if (mw->menu.old_stack) XtFree ((char *) mw->menu.old_stack);
3138 if (mw->menu.new_stack) XtFree ((char *) mw->menu.new_stack);
3140 /* Remember, you can't free anything that came from the resource
3141 database. This includes:
3143 mw->menu.top_shadow_pixmap
3144 mw->menu.bottom_shadow_pixmap
3147 Also the color cells of top_shadow_color, bottom_shadow_color,
3148 foreground, and button_foreground will never be freed until this
3149 client exits. Nice, eh?
3152 /* start from 1 because the one in slot 0 is w->core.window */
3153 for (i = 1; i < mw->menu.windows_length; i++)
3154 XDestroyWindow (XtDisplay (mw), mw->menu.windows [i].window);
3155 if (mw->menu.windows)
3156 XtFree ((char *) mw->menu.windows);
3160 XlwMenuSetValues (Widget current, Widget request, Widget new, ArgList args,
3163 XlwMenuWidget oldmw = (XlwMenuWidget)current;
3164 XlwMenuWidget newmw = (XlwMenuWidget)new;
3165 Boolean redisplay = False;
3168 if (newmw->menu.contents
3169 && newmw->menu.contents->contents
3170 && newmw->menu.contents->contents->change >= VISIBLE_CHANGE)
3173 if (newmw->core.background_pixel != oldmw->core.background_pixel
3174 || newmw->menu.foreground != oldmw->menu.foreground
3175 /* For the XEditResource protocol, which may want to change the font. */
3177 || newmw->menu.font_list != oldmw->menu.font_list
3178 || newmw->menu.font_list_2 != oldmw->menu.font_list_2
3179 || newmw->menu.fallback_font_list != oldmw->menu.fallback_font_list
3181 || newmw->menu.font != oldmw->menu.font
3185 release_drawing_gcs (newmw);
3186 make_drawing_gcs (newmw);
3189 for (i = 0; i < oldmw->menu.windows_length; i++)
3191 XSetWindowBackground (XtDisplay (oldmw),
3192 oldmw->menu.windows [i].window,
3193 newmw->core.background_pixel);
3194 /* clear windows and generate expose events */
3195 XClearArea (XtDisplay (oldmw), oldmw->menu.windows[i].window,
3204 XlwMenuResize (Widget w)
3206 XlwMenuWidget mw = (XlwMenuWidget)w;
3208 mw->menu.windows [0].width = mw->core.width;
3209 mw->menu.windows [0].height = mw->core.height;
3212 \f/* Action procedures */
3214 handle_single_motion_event (XlwMenuWidget mw, XMotionEvent *ev,
3221 if (!map_event_to_widget_value (mw, ev, &val, &level, &stay_up))
3223 /* we wind up here when: (a) the event is in the menubar, (b) the
3224 event isn't in the menubar or any of the panes, (c) the event is on
3225 a disabled menu item */
3226 pop_new_stack_if_no_contents (mw);
3227 if (select_p && !stay_up) {
3228 /* pop down all menus and exit */
3229 mw->menu.next_release_must_exit = True;
3230 set_new_state(mw, (val = NULL), 1);
3235 /* we wind up here when: (a) the event pops up a pull_right menu,
3236 (b) a menu item that is not disabled is highlighted */
3237 if (select_p && mw->menu.bounce_down
3238 && close_to_reference_time((Widget)mw,
3239 mw->menu.menu_bounce_time,
3242 /* motion can cause more than one event. Don't bounce right back
3243 up if we've just bounced down. */
3246 else if (select_p && mw->menu.bounce_down &&
3247 mw->menu.last_selected_val &&
3248 (mw->menu.last_selected_val == val))
3250 val = NULL; /* assigned to mw->last_selected_val below */
3251 mw->menu.menu_bounce_time = ev->time;
3252 /* popdown last menu if we're selecting the same menu item as we did
3253 last time and the XlwMenu.bounceDown resource is set, if the
3254 item is on the menubar itself, then exit. */
3255 if (level == (mw->menu.popped_up ? 0 : 1))
3256 mw->menu.next_release_must_exit = True;
3259 mw->menu.menu_bounce_time = 0;
3260 set_new_state (mw, val, level);
3262 mw->menu.last_selected_val = val;
3265 /* Sync with the display. Makes it feel better on X terms. */
3266 XFlush (XtDisplay (mw));
3270 handle_motion_event (XlwMenuWidget mw, XMotionEvent *ev,
3275 unsigned int state = ev->state;
3276 XMotionEvent *event= ev, dummy;
3278 /* allow motion events to be generated again */
3279 dummy.window = ev->window;
3281 && XQueryPointer (XtDisplay (mw), dummy.window,
3282 &dummy.root, &dummy.subwindow,
3283 &dummy.x_root, &dummy.y_root,
3286 && dummy.state == state
3287 && (dummy.x_root != x || dummy.y_root != y))
3289 /* don't handle the event twice or that breaks bounce_down. --Stig */
3290 dummy.type = ev->type;
3294 lw_menu_accelerate = False;
3295 handle_single_motion_event (mw, event, select_p);
3298 Time x_focus_timestamp_really_sucks_fix_me_better;
3301 Start (Widget w, XEvent *ev, String *params, Cardinal *num_params)
3303 XlwMenuWidget mw = (XlwMenuWidget)w;
3305 lw_menubar_widget = w;
3307 lw_menu_active = True;
3309 if (!mw->menu.pointer_grabbed)
3311 mw->menu.menu_post_time = ev->xbutton.time;
3312 mw->menu.menu_bounce_time = 0;
3313 mw->menu.next_release_must_exit = True;
3314 mw->menu.last_selected_val = NULL;
3315 x_focus_timestamp_really_sucks_fix_me_better =
3316 ((XButtonPressedEvent*)ev)->time;
3317 XtCallCallbackList ((Widget)mw, mw->menu.open, NULL);
3319 /* notes the absolute position of the menubar window */
3320 mw->menu.windows [0].x = ev->xmotion.x_root - ev->xmotion.x;
3321 mw->menu.windows [0].y = ev->xmotion.y_root - ev->xmotion.y;
3323 XtGrabPointer ((Widget)mw, False,
3324 (ButtonMotionMask | ButtonReleaseMask | ButtonPressMask),
3325 GrabModeAsync, GrabModeAsync,
3326 None, mw->menu.cursor_shape,
3327 ((XButtonPressedEvent*)ev)->time);
3328 mw->menu.pointer_grabbed = True;
3331 /* handles the down like a move, slots are mostly compatible */
3332 handle_motion_event (mw, &ev->xmotion, True);
3336 Drag (Widget w, XEvent *ev, String *params, Cardinal *num_params)
3338 XlwMenuWidget mw = (XlwMenuWidget)w;
3339 handle_motion_event (mw, &ev->xmotion, False);
3343 Select (Widget w, XEvent *ev, String *params, Cardinal *num_params)
3345 XlwMenuWidget mw = (XlwMenuWidget)w;
3346 widget_value *selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
3348 lw_menu_accelerate = False;
3350 /* If user releases the button quickly, without selecting anything,
3351 after the initial down-click that brought the menu up,
3353 if ((selected_item == 0 || selected_item->call_data == 0)
3354 && (!mw->menu.next_release_must_exit
3355 || close_to_reference_time(w, mw->menu.menu_post_time, ev)))
3357 mw->menu.next_release_must_exit = False;
3361 /* pop down everything */
3362 mw->menu.new_depth = 1;
3365 /* Destroy() only gets called for popup menus. Menubar widgets aren't
3366 destroyed when their menu panes get nuked. */
3367 if (mw->menu.pointer_grabbed)
3369 XtUngrabPointer ((Widget)w, ev->xmotion.time);
3370 mw->menu.pointer_grabbed = False;
3373 if (mw->menu.popped_up)
3375 mw->menu.popped_up = False;
3376 XtPopdown (XtParent (mw));
3379 lw_menu_active = False;
3381 x_focus_timestamp_really_sucks_fix_me_better =
3382 ((XButtonPressedEvent*)ev)->time;
3385 XtCallCallbackList ((Widget) mw, mw->menu.select, (XtPointer) selected_item);
3388 \f/* Action procedures for keyboard accelerators */
3392 xlw_set_menu (Widget w, widget_value *val)
3394 lw_menubar_widget = w;
3395 set_new_state ((XlwMenuWidget)w, val, 1);
3398 /* prepare the menu structure via the call-backs */
3400 xlw_map_menu (Time t)
3402 XlwMenuWidget mw = (XlwMenuWidget)lw_menubar_widget;
3404 lw_menu_accelerate = True;
3406 if (!mw->menu.pointer_grabbed)
3408 XWindowAttributes ret;
3411 unsigned int num_waste;
3413 lw_menu_active = True;
3415 mw->menu.menu_post_time = t;
3416 mw->menu.menu_bounce_time = 0;
3418 mw->menu.next_release_must_exit = True;
3419 mw->menu.last_selected_val = NULL;
3421 XtCallCallbackList ((Widget)mw, mw->menu.open, NULL);
3423 /* do this for keyboards too! */
3424 /* notes the absolute position of the menubar window */
3426 mw->menu.windows [0].x = ev->xmotion.x_root - ev->xmotion.x;
3427 mw->menu.windows [0].y = ev->xmotion.y_root - ev->xmotion.y;
3430 /* get the geometry of the menubar */
3432 /* there has to be a better way than this. */
3434 mw->menu.windows [0].x = 0;
3435 mw->menu.windows [0].y = 0;
3437 parent = XtWindow (lw_menubar_widget);
3440 XGetWindowAttributes (XtDisplay (lw_menubar_widget), parent, &ret);
3441 mw->menu.windows [0].x += ret.x;
3442 mw->menu.windows [0].y += ret.y;
3445 XQueryTree (XtDisplay (lw_menubar_widget), parent, &root, &parent, &waste,
3452 while (parent != root);
3454 XtGrabPointer ((Widget)mw, False,
3455 (ButtonMotionMask | ButtonReleaseMask | ButtonPressMask),
3456 GrabModeAsync, GrabModeAsync,
3457 None, mw->menu.cursor_shape, t);
3458 mw->menu.pointer_grabbed = True;
3462 /* display the stupid menu already */
3464 xlw_display_menu (Time t)
3466 XlwMenuWidget mw = (XlwMenuWidget)lw_menubar_widget;
3468 lw_menu_accelerate = True;
3472 /* Sync with the display. Makes it feel better on X terms. */
3473 XFlush (XtDisplay (mw));
3476 /* push a sub menu */
3478 xlw_push_menu (widget_value *val)
3480 push_new_stack ((XlwMenuWidget)lw_menubar_widget, val);
3483 /* pop a sub menu */
3487 if (((XlwMenuWidget)lw_menubar_widget)->menu.new_depth > 0)
3488 ((XlwMenuWidget)lw_menubar_widget)->menu.new_depth --;
3495 xlw_kill_menus (widget_value *val)
3497 XlwMenuWidget mw = (XlwMenuWidget)lw_menubar_widget;
3499 lw_menu_accelerate = False;
3501 mw->menu.new_depth = 1;
3504 if (mw->menu.pointer_grabbed)
3506 XtUngrabPointer (lw_menubar_widget, CurrentTime);
3507 mw->menu.pointer_grabbed = False;
3510 lw_menu_active = False;
3511 XtCallCallbackList (lw_menubar_widget, mw->menu.select, (XtPointer)val);
3514 /* set the menu item */
3516 xlw_set_item (widget_value *val)
3518 if (((XlwMenuWidget)lw_menubar_widget)->menu.new_depth > 0)
3519 ((XlwMenuWidget) lw_menubar_widget)->menu.new_depth --;
3520 push_new_stack ((XlwMenuWidget) lw_menubar_widget, val);
3523 /* get either the current entry or a list of all entries in the current submenu */
3525 xlw_get_entries (int allp)
3527 XlwMenuWidget mw = (XlwMenuWidget)lw_menubar_widget;
3530 if (mw->menu.new_depth >= 2)
3531 return mw->menu.new_stack [mw->menu.new_depth - 2]->contents;
3533 return mw->menu.new_stack[0];
3536 if (mw->menu.new_depth >= 1)
3537 return mw->menu.new_stack [mw->menu.new_depth - 1];
3543 xlw_menu_level (void)
3545 return ((XlwMenuWidget)lw_menubar_widget)->menu.new_depth;
3549 /* Special code to pop-up a menu */
3551 xlw_pop_up_menu (XlwMenuWidget mw, XButtonPressedEvent *event)
3553 int x = event->x_root;
3554 int y = event->y_root;
3557 int borderwidth = mw->menu.shadow_thickness;
3558 Screen* screen = XtScreen (mw);
3560 mw->menu.menu_post_time = event->time;
3561 mw->menu.menu_bounce_time = 0;
3562 mw->menu.next_release_must_exit = True;
3563 mw->menu.last_selected_val = NULL;
3565 XtCallCallbackList ((Widget) mw, mw->menu.open, NULL);
3569 w = mw->menu.windows [0].width;
3570 h = mw->menu.windows [0].height;
3575 if (x < borderwidth)
3578 if (x > WidthOfScreen (screen) - w - 2 * borderwidth)
3579 x = WidthOfScreen (screen) - w - 2 * borderwidth;
3581 if (y < borderwidth)
3584 if (y > HeightOfScreen (screen) - h - 2 * borderwidth)
3585 y = HeightOfScreen (screen) - h - 2 * borderwidth;
3587 mw->menu.popped_up = True;
3588 XtConfigureWidget (XtParent (mw), x, y, w, h,
3589 XtParent (mw)->core.border_width);
3590 XtPopup (XtParent (mw), XtGrabExclusive);
3591 display_menu (mw, 0, False, NULL, NULL, NULL, NULL, NULL);
3592 if (!mw->menu.pointer_grabbed)
3594 XtGrabPointer ((Widget)mw, False,
3595 (ButtonMotionMask | ButtonReleaseMask | ButtonPressMask),
3596 GrabModeAsync, GrabModeAsync,
3597 None, mw->menu.cursor_shape, event->time);
3598 mw->menu.pointer_grabbed = True;
3601 mw->menu.windows [0].x = x + borderwidth;
3602 mw->menu.windows [0].y = y + borderwidth;
3604 handle_motion_event (mw, (XMotionEvent *) event, True);
3610 * This is a horrible function which should not be needed.
3611 * use it to put the resize method back the way the XlwMenu
3612 * class initializer put it. Motif screws with this when
3613 * the XlwMenu class gets instantiated.
3616 xlw_unmunge_class_resize (Widget w)
3618 if (w->core.widget_class->core_class.resize != XlwMenuResize)
3619 w->core.widget_class->core_class.resize = XlwMenuResize;