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 */
426 if (!XmStringGetLtoR (string, XmFONTLIST_DEFAULT_TAG, &chars))
433 charslength = strlen (chars);
434 newchars = (char *) alloca (charslength + 1);
436 for (i = j = 0; chars[i] && (j < charslength); i++)
437 if (chars[i]=='%'&&chars[i+1]=='_')
440 newchars[j++] = chars[i];
444 newstring = XmStringLtoRCreate (newchars, XmFONTLIST_DEFAULT_TAG);
445 XmStringExtent (mw->menu.font_list, newstring, &width, &height);
446 XmStringFree (newstring);
451 XmbTextExtents (mw->menu.font_set, newchars, j, &ri, &rl);
453 # else /* ! USE_XFONTSET */
454 XTextExtents (mw->menu.font, newchars, j, &drop, &drop, &drop, &xcs);
456 # endif /* USE_XFONTSET */
461 massage_resource_name (CONST char *in, char *out)
463 /* Turn a random string into something suitable for using as a resource.
466 "Kill Buffer" -> "killBuffer"
467 "Find File..." -> "findFile___"
468 "Search and Replace..." -> "searchAndReplace___"
469 "C++ Mode Commands" -> "cppModeCommands"
471 Valid characters in a resource NAME component are: a-zA-Z0-9_
474 #ifdef PRINT_XLWMENU_RESOURCE_CONVERSIONS
475 /* Compile with -DPRINT_XLWMENU_RESOURCE_CONVERSIONS to generate a
476 translation file for menu localizations. */
477 char *save_in = in, *save_out = out;
480 Boolean firstp = True;
483 char ch = massaged_resource_char[(unsigned char) *in++];
486 *out++ = firstp ? tolower (ch) : toupper (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,
874 XmStringGetLtoR (string, XmFONTLIST_DEFAULT_TAG, &chars);
878 for (i=0; chars[i]; ++i) {
879 if (chars[i] == '%' && chars[i+1] == '_') {
882 x += string_draw_range (mw, window, x, y, gc, chars, s, i);
883 w = string_draw_range (mw, window, x, y, gc, chars, i+2, i+3);
885 /* underline next character */
886 XDrawLine (XtDisplay (mw), window, gc, x - 1,
887 y + mw->menu.font_ascent + 1,
888 x + w - 1, y + mw->menu.font_ascent + 1 );
894 x += string_draw_range (mw, window, x, y, gc, chars, s, i);
901 binding_draw (XlwMenuWidget mw, Window w, int x, int y, GC gc, char *value)
904 XmString xm_value = XmStringCreateLtoR(value, XmSTRING_DEFAULT_CHARSET);
905 string_draw (mw, w, x, y, gc, xm_value);
906 XmStringFree (xm_value);
908 string_draw (mw, w, x, y, gc, value);
912 /* Low level code for drawing 3-D edges. */
914 shadow_rectangle_draw (Display *dpy,
921 unsigned int thickness)
930 points [1].x = x + width;
932 points [2].x = x + width - thickness;
933 points [2].y = y + thickness;
935 points [3].y = y + thickness;
936 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
938 points [0].y = y + thickness;
940 points [1].y = y + height;
941 points [2].x = x + thickness;
942 points [2].y = y + height - thickness;
943 points [3].x = x + thickness;
944 points [3].y = y + thickness;
945 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
946 points [0].x = x + width;
948 points [1].x = x + width - thickness;
949 points [1].y = y + thickness;
950 points [2].x = x + width - thickness;
951 points [2].y = y + height - thickness;
952 points [3].x = x + width;
953 points [3].y = y + height - thickness;
954 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
956 points [0].y = y + height;
957 points [1].x = x + width;
958 points [1].y = y + height;
959 points [2].x = x + width;
960 points [2].y = y + height - thickness;
961 points [3].x = x + thickness;
962 points [3].y = y + height - thickness;
963 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
966 typedef enum e_shadow_type
968 /* these are Motif compliant */
974 SHADOW_ETCHED_OUT_DASH,
975 SHADOW_ETCHED_IN_DASH,
978 SHADOW_SINGLE_DASHED_LINE,
979 SHADOW_DOUBLE_DASHED_LINE,
981 /* these are all non-Motif */
982 SHADOW_DOUBLE_ETCHED_OUT,
983 SHADOW_DOUBLE_ETCHED_IN,
984 SHADOW_DOUBLE_ETCHED_OUT_DASH,
985 SHADOW_DOUBLE_ETCHED_IN_DASH
989 shadow_draw (XlwMenuWidget mw,
996 Display *dpy = XtDisplay (mw);
999 int thickness = mw->menu.shadow_thickness;
1003 Boolean etched = False;
1007 case SHADOW_BACKGROUND:
1008 top_gc = bottom_gc = mw->menu.background_gc;
1010 case SHADOW_ETCHED_IN:
1011 top_gc = mw->menu.shadow_bottom_gc;
1012 bottom_gc = mw->menu.shadow_top_gc;
1015 case SHADOW_ETCHED_OUT:
1016 top_gc = mw->menu.shadow_top_gc;
1017 bottom_gc = mw->menu.shadow_bottom_gc;
1021 top_gc = mw->menu.shadow_bottom_gc;
1022 bottom_gc = mw->menu.shadow_top_gc;
1026 top_gc = mw->menu.shadow_top_gc;
1027 bottom_gc = mw->menu.shadow_bottom_gc;
1033 unsigned int half = thickness/2;
1034 shadow_rectangle_draw (dpy,
1039 width - half, height - half,
1041 shadow_rectangle_draw (dpy,
1046 width - half , height - half,
1051 shadow_rectangle_draw (dpy,
1062 arrow_decoration_draw (XlwMenuWidget mw,
1068 Display *dpy = XtDisplay (mw);
1072 int thickness = mw->menu.shadow_thickness;
1075 int length = (int)((double)width * 0.87);
1076 int thick_med = (int)((double)thickness * 1.73);
1079 half_width = width/2 + 1;
1081 half_width = width/2;
1083 select_gc = mw->menu.background_gc;
1087 top_gc = mw->menu.shadow_bottom_gc;
1088 bottom_gc = mw->menu.shadow_top_gc;
1092 top_gc = mw->menu.shadow_top_gc;
1093 bottom_gc = mw->menu.shadow_bottom_gc;
1096 /* Fill internal area. We do this first so that the borders have a
1098 points [0].x = x + thickness;
1099 points [0].y = y + thickness;
1100 points [1].x = x + length - thickness;
1101 points [1].y = y + half_width;
1102 points [2].x = x + length - thickness;
1103 points [2].y = y + half_width + thickness;
1104 points [3].x = x + thickness;
1105 points [3].y = y + width - thickness;
1118 points [1].x = x + thickness;
1119 points [1].y = y + thick_med;
1120 points [2].x = x + thickness;
1121 points [2].y = y + width - thick_med;
1123 points [3].y = y + width;
1125 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
1129 points [0].y = y + width;
1130 points [1].x = x + length;
1131 points [1].y = y + half_width;
1132 points [2].x = x + length - (thickness + thickness);
1133 points [2].y = y + half_width;
1134 points [3].x = x + thickness;
1135 points [3].y = y + width - thick_med;
1137 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
1142 points [1].x = x + length;
1143 points [1].y = y + half_width;
1144 points [2].x = x + length - (thickness + thickness);
1145 points [2].y = y + half_width;
1146 points [3].x = x + thickness;
1147 points [3].y = y + thick_med;
1149 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
1153 toggle_decoration_draw (XlwMenuWidget mw,
1159 Display *dpy = XtDisplay (mw);
1160 int thickness = mw->menu.shadow_thickness;
1162 GC select_gc = mw->menu.select_gc;
1169 /* Fill internal area. */
1171 XFillRectangle (dpy,
1176 width - (2*thickness),
1177 width - (2*thickness));
1179 shadow_draw (mw, window, x, y, width, width, type);
1183 radio_decoration_draw (XlwMenuWidget mw,
1189 Display *dpy = XtDisplay (mw);
1192 GC select_gc = mw->menu.select_gc;
1193 int thickness = mw->menu.shadow_thickness;
1203 half_width = width/2;
1207 top_gc = mw->menu.shadow_bottom_gc;
1208 bottom_gc = mw->menu.shadow_top_gc;
1212 top_gc = mw->menu.shadow_top_gc;
1213 bottom_gc = mw->menu.shadow_bottom_gc;
1217 /* Draw the bottom first, just in case the regions overlap.
1218 The top should cast the longer shadow. */
1219 points [0].x = x; /* left corner */
1220 points [0].y = y + half_width;
1221 points [1].x = x + half_width; /* bottom corner */
1222 points [1].y = y + width;
1223 points [2].x = x + half_width; /* bottom inside corner */
1224 points [2].y = y + width - thickness;
1225 points [3].x = x + thickness; /* left inside corner */
1226 points [3].y = y + half_width;
1228 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
1230 points [0].x = x + half_width; /* bottom corner */
1231 points [0].y = y + width;
1232 points [1].x = x + width; /* right corner */
1233 points [1].y = y + half_width;
1234 points [2].x = x + width - thickness; /* right inside corner */
1235 points [2].y = y + half_width;
1236 points [3].x = x + half_width; /* bottom inside corner */
1237 points [3].y = y + width - thickness;
1239 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
1241 points [0].x = x; /* left corner */
1242 points [0].y = y + half_width;
1243 points [1].x = x + half_width; /* top corner */
1245 points [2].x = x + half_width; /* top inside corner */
1246 points [2].y = y + thickness;
1247 points [3].x = x + thickness; /* left inside corner */
1248 points [3].y = y + half_width;
1250 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
1252 points [0].x = x + half_width; /* top corner */
1254 points [1].x = x + width; /* right corner */
1255 points [1].y = y + half_width;
1256 points [2].x = x + width - thickness; /* right inside corner */
1257 points [2].y = y + half_width;
1258 points [3].x = x + half_width; /* top inside corner */
1259 points [3].y = y + thickness;
1261 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
1263 /* Draw the bottom first, just in case the regions overlap.
1264 The top should cast the longer shadow. */
1266 points [npoints].x = x; /* left corner */
1267 points [npoints++].y = y + half_width;
1268 points [npoints].x = x + half_width; /* bottom corner */
1269 points [npoints++].y = y + width;
1270 points [npoints].x = x + width; /* right corner */
1271 points [npoints++].y = y + half_width;
1272 points [npoints].x = x + width - thickness; /* right inside corner */
1273 points [npoints++].y = y + half_width;
1274 points [npoints].x = x + half_width; /* bottom inside corner */
1275 points [npoints++].y = y + width - thickness;
1276 points [npoints].x = x + thickness; /* left inside corner */
1277 points [npoints++].y = y + half_width;
1279 XFillPolygon (dpy, window, bottom_gc,
1280 points, npoints, Nonconvex, CoordModeOrigin);
1284 points [npoints].x = x; /* left corner */
1285 points [npoints++].y = y + half_width;
1286 points [npoints].x = x + half_width; /* top corner */
1287 points [npoints++].y = y;
1288 points [npoints].x = x + width; /* right corner */
1289 points [npoints++].y = y + half_width;
1290 points [npoints].x = x + width - thickness; /* right inside corner */
1291 points [npoints++].y = y + half_width;
1292 points [npoints].x = x + half_width; /* top inside corner */
1293 points [npoints++].y = y + thickness;
1294 points [npoints].x = x + thickness; /* left inside corner */
1295 points [npoints++].y = y + half_width;
1297 XFillPolygon (dpy, window, top_gc, points, npoints, Nonconvex,
1302 /* Fill internal area. */
1305 points [0].x = x + thickness;
1306 points [0].y = y + half_width;
1307 points [1].x = x + half_width;
1308 points [1].y = y + thickness;
1309 points [2].x = x + width - thickness;
1310 points [2].y = y + half_width;
1311 points [3].x = x + half_width;
1312 points [3].y = y + width - thickness;
1324 separator_decoration_draw (XlwMenuWidget mw,
1331 Display *dpy = XtDisplay (mw);
1334 unsigned int offset = 0;
1335 unsigned int num_separators = 1;
1336 unsigned int top_line_thickness = 0;
1337 unsigned int bottom_line_thickness = 0;
1338 Boolean dashed = False;
1342 case SHADOW_NO_LINE: /* nothing to do */
1344 case SHADOW_DOUBLE_LINE:
1346 case SHADOW_SINGLE_LINE:
1347 top_gc = bottom_gc = mw->menu.foreground_gc;
1348 top_line_thickness = 1;
1350 case SHADOW_DOUBLE_DASHED_LINE:
1352 case SHADOW_SINGLE_DASHED_LINE:
1353 top_gc = bottom_gc = mw->menu.foreground_gc;
1354 top_line_thickness = 1;
1357 case SHADOW_DOUBLE_ETCHED_OUT_DASH:
1359 case SHADOW_ETCHED_OUT_DASH:
1360 top_gc = mw->menu.shadow_top_gc;
1361 bottom_gc = mw->menu.shadow_bottom_gc;
1362 top_line_thickness = mw->menu.shadow_thickness/2;
1363 bottom_line_thickness = mw->menu.shadow_thickness - top_line_thickness;
1366 case SHADOW_DOUBLE_ETCHED_IN_DASH:
1368 case SHADOW_ETCHED_IN_DASH:
1369 top_gc = mw->menu.shadow_bottom_gc;
1370 bottom_gc = mw->menu.shadow_top_gc;
1371 top_line_thickness = mw->menu.shadow_thickness/2;
1372 bottom_line_thickness = mw->menu.shadow_thickness - top_line_thickness;
1375 case SHADOW_DOUBLE_ETCHED_OUT:
1377 case SHADOW_ETCHED_OUT:
1378 top_gc = mw->menu.shadow_top_gc;
1379 bottom_gc = mw->menu.shadow_bottom_gc;
1380 top_line_thickness = mw->menu.shadow_thickness/2;
1381 bottom_line_thickness = mw->menu.shadow_thickness - top_line_thickness;
1383 case SHADOW_DOUBLE_ETCHED_IN:
1385 case SHADOW_ETCHED_IN:
1387 top_gc = mw->menu.shadow_bottom_gc;
1388 bottom_gc = mw->menu.shadow_top_gc;
1389 top_line_thickness = mw->menu.shadow_thickness/2;
1390 bottom_line_thickness = mw->menu.shadow_thickness - top_line_thickness;
1397 values.line_style = LineOnOffDash;
1398 if (top_line_thickness > 0)
1399 XChangeGC (dpy, top_gc, GCLineStyle, &values);
1400 if (bottom_line_thickness > 0 && bottom_gc != top_gc)
1401 XChangeGC (dpy, bottom_gc, GCLineStyle, &values);
1404 while (num_separators--)
1407 for (i = 0; i < top_line_thickness; i++)
1408 XDrawLine (dpy, window, top_gc, x, y + i, x + width, y + i);
1410 for (i = 0; i < bottom_line_thickness; i++)
1411 XDrawLine (dpy, window, bottom_gc,
1412 x, y + top_line_thickness + offset + i,
1413 x + width, y + top_line_thickness + offset + i);
1414 y += (top_line_thickness + offset + bottom_line_thickness + 1);
1420 values.line_style = LineSolid;
1421 if (top_line_thickness > 0)
1422 XChangeGC (dpy, top_gc, GCLineStyle, &values);
1423 if (bottom_line_thickness > 0 && bottom_gc != top_gc)
1424 XChangeGC (dpy, bottom_gc, GCLineStyle, &values);
1428 #define SLOPPY_TYPES 0 /* 0=off, 1=error check, 2=easy to please */
1430 #if SLOPPY_TYPES < 2
1432 static char *wv_types[] =
1446 print_widget_value (widget_value *wv, int just_one, int depth)
1450 for (i = 0; i < depth; i++)
1455 printf ("%s(null widget value pointer)\n", d);
1458 printf ("%stype: %s\n", d, wv_types [wv->type]);
1460 printf ("%sname: %s\n", d, (wv->name ? wv->name : "(null)"));
1462 if (wv->name) printf ("%sname: %s\n", d, wv->name);
1464 if (wv->value) printf ("%svalue: %s\n", d, wv->value);
1465 if (wv->key) printf ("%skey: %s\n", d, wv->key);
1466 printf ("%senabled: %d\n", d, wv->enabled);
1469 printf ("\n%scontents: \n", d);
1470 print_widget_value (wv->contents, 0, depth + 5);
1472 if (!just_one && wv->next)
1475 print_widget_value (wv->next, 0, depth);
1478 #endif /* SLOPPY_TYPES < 2 */
1481 all_dashes_p (char *s)
1484 if (!s || s[0] == '\0')
1486 for (p = s; *p == '-'; p++);
1488 if (*p == '!' || *p == '\0')
1492 #endif /* SLOPPY_TYPES */
1494 static widget_value_type
1495 menu_item_type (widget_value *val)
1497 if (val->type != UNSPECIFIED_TYPE)
1500 else if (all_dashes_p (val->name))
1501 return SEPARATOR_TYPE;
1502 else if (val->name && val->name[0] == '\0') /* push right */
1503 return PUSHRIGHT_TYPE;
1504 else if (val->contents) /* cascade */
1505 return CASCADE_TYPE;
1506 else if (val->call_data) /* push button */
1513 return UNSPECIFIED_TYPE; /* Not reached */
1518 label_button_size (XlwMenuWidget mw,
1521 unsigned int *toggle_width,
1522 unsigned int *label_width,
1523 unsigned int *bindings_width,
1524 unsigned int *height)
1526 *height = (mw->menu.font_ascent + mw->menu.font_descent +
1527 2 * mw->menu.vertical_margin +
1528 2 * mw->menu.shadow_thickness);
1529 /* no left column decoration */
1530 *toggle_width = mw->menu.horizontal_margin + mw->menu.shadow_thickness;;
1532 *label_width = string_width_u (mw, resource_widget_value (mw, val));
1533 *bindings_width = mw->menu.horizontal_margin + mw->menu.shadow_thickness;
1537 label_button_draw (XlwMenuWidget mw,
1540 Boolean highlighted,
1544 unsigned int height,
1545 unsigned int label_offset,
1546 unsigned int binding_tab)
1548 int y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin;
1552 label_offset = mw->menu.shadow_thickness + mw->menu.horizontal_margin;
1554 if (highlighted && (in_menubar || val->contents))
1555 gc = mw->menu.highlight_gc;
1556 else if (in_menubar || val->contents)
1557 gc = mw->menu.foreground_gc;
1559 gc = mw->menu.title_gc;
1561 /* Draw the label string. */
1564 x + label_offset, y + y_offset,
1566 resource_widget_value (mw, val));
1570 push_button_size (XlwMenuWidget mw,
1573 unsigned int *toggle_width,
1574 unsigned int *label_width,
1575 unsigned int *bindings_width,
1576 unsigned int *height)
1579 label_button_size (mw, val, in_menubar,
1580 toggle_width, label_width, bindings_width,
1583 /* key bindings to display? */
1584 if (!in_menubar && val->key)
1588 XmString key = XmStringCreateLtoR (val->key, XmSTRING_DEFAULT_CHARSET);
1589 w = string_width (mw, key);
1592 char *key = val->key;
1593 w = string_width (mw, key);
1595 *bindings_width += w + mw->menu.column_spacing;
1600 push_button_draw (XlwMenuWidget mw,
1603 Boolean highlighted,
1607 unsigned int height,
1608 unsigned int label_offset,
1609 unsigned int binding_offset)
1611 int y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin;
1614 Boolean menu_pb = in_menubar && (menu_item_type (val) == BUTTON_TYPE);
1616 /* Draw the label string. */
1618 label_offset = mw->menu.shadow_thickness + mw->menu.horizontal_margin;
1623 gc = mw->menu.highlight_gc;
1625 gc = mw->menu.inactive_gc;
1630 gc = mw->menu.button_gc;
1632 gc = mw->menu.inactive_button_gc;
1637 gc = mw->menu.foreground_gc;
1639 gc = mw->menu.inactive_gc;
1644 x + label_offset, y + y_offset,
1646 resource_widget_value (mw, val));
1648 /* Draw the keybindings */
1651 if (!binding_offset)
1653 unsigned int s_width =
1654 string_width (mw, resource_widget_value (mw, val));
1655 binding_offset = label_offset + s_width + mw->menu.shadow_thickness;
1657 binding_draw (mw, window,
1658 x + binding_offset + mw->menu.column_spacing,
1659 y + y_offset, gc, val->key);
1662 /* Draw the shadow */
1668 type = (val->selected ? SHADOW_ETCHED_OUT : SHADOW_ETCHED_IN);
1675 type = SHADOW_BACKGROUND;
1678 shadow_draw (mw, window, x, y, width, height, type);
1682 arrow_decoration_height (XlwMenuWidget mw)
1684 int result = (mw->menu.font_ascent + mw->menu.font_descent) / 2;
1686 result += 2 * mw->menu.shadow_thickness;
1688 if (result > (mw->menu.font_ascent + mw->menu.font_descent))
1689 result = mw->menu.font_ascent + mw->menu.font_descent;
1695 cascade_button_size (XlwMenuWidget mw,
1698 unsigned int *toggle_width,
1699 unsigned int *label_width,
1700 unsigned int *arrow_width,
1701 unsigned int *height)
1704 label_button_size (mw, val, in_menubar,
1705 toggle_width, label_width, arrow_width,
1707 /* we have a pull aside arrow */
1710 *arrow_width += arrow_decoration_height (mw) + mw->menu.column_spacing;
1715 cascade_button_draw (XlwMenuWidget mw,
1718 Boolean highlighted,
1722 unsigned int height,
1723 unsigned int label_offset,
1724 unsigned int binding_offset)
1728 /* Draw the label string. */
1729 label_button_draw (mw, val, in_menubar, highlighted,
1730 window, x, y, width, height, label_offset,
1733 /* Draw the pull aside arrow */
1734 if (!in_menubar && val->contents)
1737 unsigned int arrow_height = arrow_decoration_height (mw);
1739 y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin +
1740 (mw->menu.font_ascent+mw->menu.font_descent - arrow_height)/2;
1742 if (!binding_offset)
1744 unsigned int s_width =
1745 string_width (mw, resource_widget_value (mw, val));
1748 label_offset = mw->menu.shadow_thickness +
1749 mw->menu.horizontal_margin;
1751 binding_offset = label_offset + s_width + mw->menu.shadow_thickness;
1754 arrow_decoration_draw (mw,
1756 x + binding_offset + mw->menu.column_spacing,
1762 /* Draw the shadow */
1766 type = SHADOW_BACKGROUND;
1768 shadow_draw (mw, window, x, y, width, height, type);
1772 toggle_decoration_height (XlwMenuWidget mw)
1775 if (mw->menu.indicator_size > 0)
1776 rv = mw->menu.indicator_size;
1778 rv = mw->menu.font_ascent;
1780 if (rv > (mw->menu.font_ascent + mw->menu.font_descent))
1781 rv = mw->menu.font_ascent + mw->menu.font_descent;
1783 /* radio button can't be smaller than its border or a filling
1784 error will occur. */
1785 if (rv < 2 * mw->menu.shadow_thickness)
1786 rv = 2 * mw->menu.shadow_thickness;
1792 toggle_button_size (XlwMenuWidget mw,
1795 unsigned int *toggle_width,
1796 unsigned int *label_width,
1797 unsigned int *bindings_width,
1798 unsigned int *height)
1801 push_button_size (mw, val, in_menubar,
1802 toggle_width, label_width, bindings_width,
1804 /* we have a toggle */
1805 *toggle_width += toggle_decoration_height (mw) + mw->menu.column_spacing;
1809 toggle_button_draw (XlwMenuWidget mw,
1812 Boolean highlighted,
1816 unsigned int height,
1817 unsigned int label_tab,
1818 unsigned int binding_tab)
1822 unsigned int t_height = toggle_decoration_height (mw);
1824 /* Draw a toggle. */
1825 x_offset = mw->menu.shadow_thickness + mw->menu.horizontal_margin;
1826 y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin;
1827 y_offset += (mw->menu.font_ascent + mw->menu.font_descent - t_height)/2;
1829 toggle_decoration_draw (mw, window, x + x_offset, y + y_offset,
1830 t_height, val->selected);
1832 /* Draw the pushbutton parts. */
1833 push_button_draw (mw, val, in_menubar, highlighted, window, x, y, width,
1834 height, label_tab, binding_tab);
1838 radio_decoration_height (XlwMenuWidget mw)
1840 return toggle_decoration_height (mw);
1844 radio_button_draw (XlwMenuWidget mw,
1847 Boolean highlighted,
1851 unsigned int height,
1852 unsigned int label_tab,
1853 unsigned int binding_tab)
1857 unsigned int r_height = radio_decoration_height (mw);
1859 /* Draw a toggle. */
1860 x_offset = mw->menu.shadow_thickness + mw->menu.horizontal_margin;
1861 y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin;
1862 y_offset += (mw->menu.font_ascent + mw->menu.font_descent - r_height)/2;
1864 radio_decoration_draw (mw, window, x + x_offset, y + y_offset, r_height,
1867 /* Draw the pushbutton parts. */
1868 push_button_draw (mw, val, in_menubar, highlighted, window, x, y, width,
1869 height, label_tab, binding_tab);
1872 static struct _shadow_names
1879 { "singleLine", SHADOW_SINGLE_LINE },
1880 { "doubleLine", SHADOW_DOUBLE_LINE },
1881 { "singleDashedLine", SHADOW_SINGLE_DASHED_LINE },
1882 { "doubleDashedLine", SHADOW_DOUBLE_DASHED_LINE },
1883 { "noLine", SHADOW_NO_LINE },
1884 { "shadowEtchedIn", SHADOW_ETCHED_IN },
1885 { "shadowEtchedOut", SHADOW_ETCHED_OUT },
1886 { "shadowEtchedInDash", SHADOW_ETCHED_IN_DASH },
1887 { "shadowEtchedOutDash", SHADOW_ETCHED_OUT_DASH },
1889 { "shadowDoubleEtchedIn", SHADOW_DOUBLE_ETCHED_IN },
1890 { "shadowDoubleEtchedOut", SHADOW_DOUBLE_ETCHED_OUT },
1891 { "shadowDoubleEtchedInDash", SHADOW_DOUBLE_ETCHED_IN_DASH },
1892 { "shadowDoubleEtchedOutDash", SHADOW_DOUBLE_ETCHED_OUT_DASH }
1896 separator_type (char *name)
1901 for (i = 0; i < (int) (XtNumber (shadow_names)); i++ )
1903 if (strcmp (name, shadow_names[i].name) == 0)
1904 return shadow_names[i].type;
1907 return SHADOW_BACKGROUND;
1911 separator_decoration_height (XlwMenuWidget mw, widget_value *val)
1914 switch (separator_type (val->value))
1916 case SHADOW_NO_LINE:
1917 case SHADOW_SINGLE_LINE:
1918 case SHADOW_SINGLE_DASHED_LINE:
1920 case SHADOW_DOUBLE_LINE:
1921 case SHADOW_DOUBLE_DASHED_LINE:
1923 case SHADOW_DOUBLE_ETCHED_OUT:
1924 case SHADOW_DOUBLE_ETCHED_IN:
1925 case SHADOW_DOUBLE_ETCHED_OUT_DASH:
1926 case SHADOW_DOUBLE_ETCHED_IN_DASH:
1927 return (1 + 2 * mw->menu.shadow_thickness);
1928 case SHADOW_ETCHED_OUT:
1929 case SHADOW_ETCHED_IN:
1931 return mw->menu.shadow_thickness;
1936 separator_size (XlwMenuWidget mw,
1939 unsigned int *toggle_width,
1940 unsigned int *label_width,
1941 unsigned int *rest_width,
1942 unsigned int *height)
1944 *height = separator_decoration_height (mw, val);
1946 *toggle_width = *rest_width = 0;
1950 separator_draw (XlwMenuWidget mw,
1953 Boolean highlighted,
1957 unsigned int height,
1958 unsigned int label_tab,
1959 unsigned int binding_tab)
1961 unsigned int sep_width;
1968 separator_decoration_draw (mw,
1974 separator_type(val->value));
1978 pushright_size (XlwMenuWidget mw,
1981 unsigned int *toggle_width,
1982 unsigned int *label_width,
1983 unsigned int *rest_width,
1984 unsigned int *height)
1986 *height = *label_width = *toggle_width = *rest_width = 0;
1990 size_menu_item (XlwMenuWidget mw,
1993 unsigned int *toggle_width,
1994 unsigned int *label_width,
1995 unsigned int *rest_width,
1996 unsigned int *height)
1998 void (*function_ptr) (XlwMenuWidget _mw,
2000 Boolean _in_menubar,
2001 unsigned int *_toggle_width,
2002 unsigned int *_label_width,
2003 unsigned int *_rest_width,
2004 unsigned int *_height);
2006 switch (menu_item_type (val))
2010 function_ptr = toggle_button_size;
2012 case SEPARATOR_TYPE:
2013 function_ptr = separator_size;
2015 case INCREMENTAL_TYPE:
2017 function_ptr = cascade_button_size;
2020 function_ptr = push_button_size;
2022 case PUSHRIGHT_TYPE:
2023 function_ptr = pushright_size;
2027 function_ptr = label_button_size;
2031 (*function_ptr) (mw,
2041 display_menu_item (XlwMenuWidget mw,
2045 Boolean highlighted,
2047 Boolean just_compute)
2050 int x = where->x /* + mw->menu.shadow_thickness */ ;
2051 int y = where->y /* + mw->menu.shadow_thickness */ ;
2052 unsigned int toggle_width;
2053 unsigned int label_width;
2054 unsigned int binding_width;
2056 unsigned int height;
2057 unsigned int label_tab;
2058 unsigned int binding_tab;
2059 void (*function_ptr) (XlwMenuWidget _mw,
2061 Boolean _in_menubar,
2062 Boolean _highlighted,
2065 unsigned int _width,
2066 unsigned int _height,
2067 unsigned int _label_tab,
2068 unsigned int _binding_tab);
2070 size_menu_item (mw, val, horizontal,
2071 &toggle_width, &label_width, &binding_width, &height);
2075 width = toggle_width + label_width + binding_width;
2076 height = ws->height - 2 * mw->menu.shadow_thickness;
2080 width = ws->width - 2 * mw->menu.shadow_thickness;
2081 toggle_width = ws->toggle_width;
2082 label_width = ws->label_width;
2091 label_tab = toggle_width;
2092 binding_tab = toggle_width + label_width;
2094 switch (menu_item_type (val))
2097 function_ptr = toggle_button_draw;
2100 function_ptr = radio_button_draw;
2102 case SEPARATOR_TYPE:
2103 function_ptr = separator_draw;
2105 case INCREMENTAL_TYPE:
2107 function_ptr = cascade_button_draw;
2110 function_ptr = push_button_draw;
2113 function_ptr = label_button_draw;
2115 default: /* do no drawing */
2119 (*function_ptr) (mw,
2131 size_menu (XlwMenuWidget mw, int level)
2133 unsigned int toggle_width;
2134 unsigned int label_width;
2135 unsigned int rest_width;
2136 unsigned int height;
2137 unsigned int max_toggle_width = 0;
2138 unsigned int max_label_width = 0;
2139 unsigned int max_rest_width = 0;
2140 unsigned int max_height = 0;
2141 int horizontal_p = mw->menu.horizontal && (level == 0);
2145 if (level >= mw->menu.old_depth)
2148 ws = &mw->menu.windows [level];
2150 for (val = mw->menu.old_stack [level]->contents; val; val = val->next)
2161 max_label_width += toggle_width + label_width + rest_width;
2162 if (height > max_height)
2163 max_height = height;
2167 if (max_toggle_width < toggle_width)
2168 max_toggle_width = toggle_width;
2169 if (max_label_width < label_width)
2170 max_label_width = label_width;
2171 if (max_rest_width < rest_width)
2172 max_rest_width = rest_width;
2173 max_height += height;
2177 ws->height = max_height;
2178 ws->width = max_label_width + max_rest_width + max_toggle_width;
2179 ws->toggle_width = max_toggle_width;
2180 ws->label_width = max_label_width;
2182 ws->width += 2 * mw->menu.shadow_thickness;
2183 ws->height += 2 * mw->menu.shadow_thickness;
2187 display_menu (XlwMenuWidget mw, int level, Boolean just_compute_p,
2188 XPoint *highlighted_pos, XPoint *hit, widget_value **hit_return,
2189 widget_value *this, widget_value *that)
2192 widget_value *following_item;
2195 int horizontal_p = mw->menu.horizontal && (level == 0);
2197 int just_compute_this_one_p;
2199 if (level >= mw->menu.old_depth)
2202 if (level < mw->menu.old_depth - 1)
2203 following_item = mw->menu.old_stack [level + 1];
2206 if (lw_menu_accelerate
2207 && level == mw->menu.old_depth - 1
2208 && mw->menu.old_stack [level]->type == CASCADE_TYPE)
2209 just_compute_p = True;
2210 following_item = NULL;
2213 #if SLOPPY_TYPES == 1
2214 puts("===================================================================");
2215 print_widget_value (following_item, 1, 0);
2221 where.x = mw->menu.shadow_thickness;
2222 where.y = mw->menu.shadow_thickness;
2224 ws = &mw->menu.windows [level];
2225 for (val = mw->menu.old_stack [level]->contents; val; val = val->next)
2229 highlighted_p = (val == following_item);
2230 /* If this is the partition (the dummy item which says that menus
2231 after this should be flushright) then figure out how big the
2232 following items are. This means we walk down the tail of the
2233 list twice, but that's no big deal - it's short.
2235 if (horizontal_p && (menu_item_type (val) == PUSHRIGHT_TYPE))
2238 XPoint flushright_size;
2240 flushright_size.x = 0;
2241 flushright_size.y = 0;
2242 for (rest = val; rest; rest = rest->next)
2243 display_menu_item (mw, rest, ws, &flushright_size,
2244 highlighted_p, horizontal_p, True);
2245 new_x = ws->width - (flushright_size.x + mw->menu.shadow_thickness);
2246 if (new_x > where.x)
2248 /* We know what we need; don't draw this item. */
2252 if (highlighted_p && highlighted_pos)
2255 highlighted_pos->x = where.x;
2257 highlighted_pos->y = where.y;
2260 just_compute_this_one_p =
2261 just_compute_p || ((this || that) && val != this && val != that);
2265 display_menu_item (mw, val, ws, &where, highlighted_p, horizontal_p,
2266 just_compute_this_one_p);
2268 if (highlighted_p && highlighted_pos)
2271 highlighted_pos->y = ws->height;
2273 highlighted_pos->x = ws->width;
2276 if (hit && !*hit_return)
2278 if (horizontal_p && hit->x > start.x && hit->x <= where.x)
2280 else if (!horizontal_p && hit->y > start.y && hit->y <= where.y)
2285 where.y = mw->menu.shadow_thickness;
2287 where.x = mw->menu.shadow_thickness;
2290 /* Draw slab edges around menu */
2291 if (!just_compute_p)
2292 shadow_draw(mw, ws->window, 0, 0, ws->width, ws->height, SHADOW_OUT);
2297 set_new_state (XlwMenuWidget mw, widget_value *val, int level)
2301 mw->menu.new_depth = 0;
2302 for (i = 0; i < level; i++)
2303 push_new_stack (mw, mw->menu.old_stack [i]);
2305 push_new_stack (mw, val);
2309 make_windows_if_needed (XlwMenuWidget mw, int n)
2313 XSetWindowAttributes xswa;
2318 window_state *windows;
2321 if (mw->menu.windows_length >= n)
2324 root = RootWindowOfScreen (XtScreen(mw));
2325 /* grab the visual and depth from the nearest shell ancestor */
2326 visual = CopyFromParent;
2327 depth = CopyFromParent;
2329 while (visual == CopyFromParent && p)
2333 visual = ((ShellWidget)p)->shell.visual;
2334 depth = p->core.depth;
2339 xswa.save_under = True;
2340 xswa.override_redirect = True;
2341 xswa.background_pixel = mw->core.background_pixel;
2342 xswa.border_pixel = mw->core.border_pixel;
2343 xswa.event_mask = (ExposureMask | ButtonMotionMask
2344 | ButtonReleaseMask | ButtonPressMask);
2345 xswa.cursor = mw->menu.cursor_shape;
2346 xswa.colormap = mw->core.colormap;
2347 mask = CWSaveUnder | CWOverrideRedirect | CWBackPixel | CWBorderPixel
2348 | CWEventMask | CWCursor | CWColormap;
2350 if (mw->menu.use_backing_store)
2352 xswa.backing_store = Always;
2353 mask |= CWBackingStore;
2356 if (!mw->menu.windows)
2359 (window_state *) XtMalloc (n * sizeof (window_state));
2365 (window_state *) XtRealloc ((char *) mw->menu.windows,
2366 n * sizeof (window_state));
2367 start_at = mw->menu.windows_length;
2369 mw->menu.windows_length = n;
2371 windows = mw->menu.windows;
2373 for (i = start_at; i < n; i++)
2377 windows [i].width = 1;
2378 windows [i].height = 1;
2379 windows [i].window =
2380 XCreateWindow (XtDisplay (mw),
2383 0, depth, CopyFromParent, visual, mask, &xswa);
2387 /* Make the window fit in the screen */
2389 fit_to_screen (XlwMenuWidget mw, window_state *ws, window_state *previous_ws,
2390 Boolean horizontal_p)
2392 int screen_width = WidthOfScreen (XtScreen (mw));
2393 int screen_height = HeightOfScreen (XtScreen (mw));
2397 else if ((int) (ws->x + ws->width) > screen_width)
2400 ws->x = previous_ws->x - ws->width;
2403 ws->x = screen_width - ws->width;
2405 /* This check is to make sure we cut off the right side
2406 instead of the left side if the menu is wider than the
2414 else if ((int) (ws->y + ws->height) > screen_height)
2418 /* A pulldown must either be entirely above or below the menubar.
2419 If we're here, the pulldown doesn't fit below the menubar, so
2420 let's determine if it will fit above the menubar.
2421 Only put it above if there is more room above than below.
2422 Note shadow_thickness offset to allow for slab surround.
2424 if (ws->y > (screen_height / 2))
2425 ws->y = previous_ws->y - ws->height + mw->menu.shadow_thickness;
2429 ws->y = screen_height - ws->height;
2430 /* if it's taller than the screen, display the topmost part
2431 that will fit, beginning at the top of the screen. */
2438 /* Updates old_stack from new_stack and redisplays. */
2440 remap_menubar (XlwMenuWidget mw)
2444 XPoint selection_position;
2445 int old_depth = mw->menu.old_depth;
2446 int new_depth = mw->menu.new_depth;
2447 widget_value **old_stack;
2448 widget_value **new_stack;
2449 window_state *windows;
2450 widget_value *old_selection;
2451 widget_value *new_selection;
2453 /* Check that enough windows and old_stack are ready. */
2454 make_windows_if_needed (mw, new_depth);
2455 make_old_stack_space (mw, new_depth);
2456 windows = mw->menu.windows;
2457 old_stack = mw->menu.old_stack;
2458 new_stack = mw->menu.new_stack;
2460 /* compute the last identical different entry */
2461 for (i = 1; i < old_depth && i < new_depth; i++)
2462 if (old_stack [i] != new_stack [i])
2466 if (lw_menu_accelerate
2468 && last_same == old_depth - 1
2469 && old_stack [last_same]->contents)
2472 /* Memorize the previously selected item to be able to refresh it */
2473 old_selection = last_same + 1 < old_depth ? old_stack [last_same + 1] : NULL;
2474 new_selection = last_same + 1 < new_depth ? new_stack [last_same + 1] : NULL;
2476 /* updates old_state from new_state. It has to be done now because
2477 display_menu (called below) uses the old_stack to know what to display. */
2478 for (i = last_same + 1; i < new_depth; i++)
2479 old_stack [i] = new_stack [i];
2481 mw->menu.old_depth = new_depth;
2483 /* refresh the last seletion */
2484 selection_position.x = 0;
2485 selection_position.y = 0;
2486 display_menu (mw, last_same, new_selection == old_selection,
2487 &selection_position, NULL, NULL, old_selection, new_selection);
2489 /* Now popup the new menus */
2490 for (i = last_same + 1; i < new_depth && new_stack [i]->contents; i++)
2492 window_state *previous_ws = &windows [i - 1];
2493 window_state *ws = &windows [i];
2495 if (lw_menu_accelerate && i == new_depth - 1)
2498 ws->x = previous_ws->x + selection_position.x;
2499 ws->y = previous_ws->y + selection_position.y;
2501 /* take into account the slab around the new menu */
2502 ws->y -= mw->menu.shadow_thickness;
2505 widget_value *val = mw->menu.old_stack [i];
2506 if (val->contents->type == INCREMENTAL_TYPE)
2508 /* okay, we're now doing a lisp callback to incrementally generate
2509 more of the menu. */
2510 XtCallCallbackList ((Widget)mw,
2512 (XtPointer)val->contents);
2518 fit_to_screen (mw, ws, previous_ws, mw->menu.horizontal && i == 1);
2520 XClearWindow (XtDisplay (mw), ws->window);
2521 XMoveResizeWindow (XtDisplay (mw), ws->window, ws->x, ws->y,
2522 ws->width, ws->height);
2523 XMapRaised (XtDisplay (mw), ws->window);
2524 display_menu (mw, i, False, &selection_position, NULL, NULL, NULL, NULL);
2527 /* unmap the menus that popped down */
2529 last_same = new_depth;
2530 if (lw_menu_accelerate
2532 && new_stack [last_same - 1]->contents)
2535 for (i = last_same - 1; i < old_depth; i++)
2536 if (i >= last_same || !new_stack [i]->contents)
2537 XUnmapWindow (XtDisplay (mw), windows [i].window);
2541 motion_event_is_in_menu (XlwMenuWidget mw, XMotionEvent *ev, int level,
2542 XPoint *relative_pos)
2544 window_state *ws = &mw->menu.windows [level];
2545 int x = level == 0 ? ws->x : ws->x + mw->menu.shadow_thickness;
2546 int y = level == 0 ? ws->y : ws->y + mw->menu.shadow_thickness;
2547 relative_pos->x = ev->x_root - x;
2548 relative_pos->y = ev->y_root - y;
2549 return (x < ev->x_root && ev->x_root < (int) (x + ws->width) &&
2550 y < ev->y_root && ev->y_root < (int) (y + ws->height));
2554 map_event_to_widget_value (XlwMenuWidget mw, XMotionEvent *ev,
2555 widget_value **val_ptr, int *level,
2556 Boolean *inside_menu)
2559 XPoint relative_pos;
2563 *inside_menu = False;
2565 /* Find the window */
2567 for (i = mw->menu.old_depth - 1; i >= 0; i--)
2569 for (i = 0; i <= mw->menu.old_depth - 1; i++)
2572 ws = &mw->menu.windows [i];
2573 if (ws && motion_event_is_in_menu (mw, ev, i, &relative_pos))
2575 *inside_menu = True; /* special logic for menubar below... */
2576 if ((ev->type == ButtonPress) ||
2579 display_menu (mw, i, True, NULL, &relative_pos,
2580 val_ptr, NULL, NULL);
2584 *inside_menu = True;
2587 else if (mw->menu.horizontal || i == 0)
2589 /* if we're clicking on empty part of the menubar, then
2590 unpost the stay-up menu */
2591 *inside_menu = False;
2601 make_drawing_gcs (XlwMenuWidget mw)
2604 unsigned long flags = (GCFont | GCForeground | GCBackground);
2607 xgcv.font = default_font_of_font_list (mw->menu.font_list)->fid;
2609 xgcv.font = mw->menu.font->fid;
2612 xgcv.foreground = mw->core.background_pixel;
2613 xgcv.background = mw->menu.foreground;
2614 mw->menu.background_gc = XtGetGC ((Widget) mw, flags, &xgcv);
2616 xgcv.foreground = mw->menu.foreground;
2617 xgcv.background = mw->core.background_pixel;
2618 mw->menu.foreground_gc = XtGetGC ((Widget) mw, flags, &xgcv);
2620 if (mw->menu.select_color != (Pixel)-1)
2622 xgcv.foreground = mw->menu.select_color;
2626 Display *dpy = XtDisplay(mw);
2627 if (CellsOfScreen(DefaultScreenOfDisplay(dpy)) <= 2)
2629 xgcv.foreground = mw->menu.foreground;
2634 Colormap cmap = mw->core.colormap;
2635 xcolor.pixel = mw->core.background_pixel;
2636 XQueryColor (dpy, cmap, &xcolor);
2637 xcolor.red = (xcolor.red * 17) / 20;
2638 xcolor.green = (xcolor.green * 17) / 20;
2639 xcolor.blue = (xcolor.blue * 17) / 20;
2640 if (allocate_nearest_color (dpy, cmap, &xcolor))
2641 xgcv.foreground = xcolor.pixel;
2644 xgcv.background = mw->core.background_pixel;
2645 mw->menu.select_gc = XtGetGC ((Widget)mw, flags, &xgcv);
2647 xgcv.foreground = mw->menu.foreground;
2648 xgcv.background = mw->core.background_pixel;
2649 xgcv.fill_style = FillStippled;
2650 xgcv.stipple = mw->menu.gray_pixmap;
2651 mw->menu.inactive_gc = XtGetGC ((Widget)mw,
2652 (flags | GCFillStyle | GCStipple),
2655 xgcv.foreground = mw->menu.highlight_foreground;
2656 xgcv.background = mw->core.background_pixel;
2657 mw->menu.highlight_gc = XtGetGC ((Widget)mw, flags, &xgcv);
2659 xgcv.foreground = mw->menu.title_foreground;
2660 xgcv.background = mw->core.background_pixel;
2661 mw->menu.title_gc = XtGetGC ((Widget)mw, flags, &xgcv);
2663 xgcv.foreground = mw->menu.button_foreground;
2664 xgcv.background = mw->core.background_pixel;
2665 mw->menu.button_gc = XtGetGC ((Widget)mw, flags, &xgcv);
2667 xgcv.fill_style = FillStippled;
2668 xgcv.stipple = mw->menu.gray_pixmap;
2669 mw->menu.inactive_button_gc = XtGetGC ((Widget)mw,
2670 (flags | GCFillStyle | GCStipple),
2675 release_drawing_gcs (XlwMenuWidget mw)
2677 XtReleaseGC ((Widget) mw, mw->menu.foreground_gc);
2678 XtReleaseGC ((Widget) mw, mw->menu.button_gc);
2679 XtReleaseGC ((Widget) mw, mw->menu.highlight_gc);
2680 XtReleaseGC ((Widget) mw, mw->menu.title_gc);
2681 XtReleaseGC ((Widget) mw, mw->menu.inactive_gc);
2682 XtReleaseGC ((Widget) mw, mw->menu.inactive_button_gc);
2683 XtReleaseGC ((Widget) mw, mw->menu.background_gc);
2684 XtReleaseGC ((Widget) mw, mw->menu.select_gc);
2685 /* let's get some segvs if we try to use these... */
2686 mw->menu.foreground_gc = (GC) -1;
2687 mw->menu.button_gc = (GC) -1;
2688 mw->menu.highlight_gc = (GC) -1;
2689 mw->menu.title_gc = (GC) -1;
2690 mw->menu.inactive_gc = (GC) -1;
2691 mw->menu.inactive_button_gc = (GC) -1;
2692 mw->menu.background_gc = (GC) -1;
2693 mw->menu.select_gc = (GC) -1;
2696 #define MINL(x,y) ((((unsigned long) (x)) < ((unsigned long) (y))) \
2697 ? ((unsigned long) (x)) : ((unsigned long) (y)))
2700 make_shadow_gcs (XlwMenuWidget mw)
2703 unsigned long pm = 0;
2704 Display *dpy = XtDisplay ((Widget) mw);
2705 Colormap cmap = mw->core.colormap;
2707 int top_frobbed = 0, bottom_frobbed = 0;
2709 if (mw->menu.top_shadow_color == (Pixel) (-1))
2710 mw->menu.top_shadow_color = mw->core.background_pixel;
2711 if (mw->menu.bottom_shadow_color == (Pixel) (-1))
2712 mw->menu.bottom_shadow_color = mw->menu.foreground;
2714 if (mw->menu.top_shadow_color == mw->core.background_pixel ||
2715 mw->menu.top_shadow_color == mw->menu.foreground)
2717 topc.pixel = mw->core.background_pixel;
2718 XQueryColor (dpy, cmap, &topc);
2719 /* don't overflow/wrap! */
2720 topc.red = MINL (65535, topc.red * 1.2);
2721 topc.green = MINL (65535, topc.green * 1.2);
2722 topc.blue = MINL (65535, topc.blue * 1.2);
2723 if (allocate_nearest_color (dpy, cmap, &topc))
2725 if (topc.pixel == mw->core.background_pixel)
2727 XFreeColors( dpy, cmap, &topc.pixel, 1, 0);
2728 topc.red = MINL (65535, topc.red + 0x8000);
2729 topc.green = MINL (65535, topc.green + 0x8000);
2730 topc.blue = MINL (65535, topc.blue + 0x8000);
2731 if (allocate_nearest_color (dpy, cmap, &topc))
2733 mw->menu.top_shadow_color = topc.pixel;
2738 mw->menu.top_shadow_color = topc.pixel;
2744 if (mw->menu.bottom_shadow_color == mw->menu.foreground ||
2745 mw->menu.bottom_shadow_color == mw->core.background_pixel)
2747 botc.pixel = mw->core.background_pixel;
2748 XQueryColor (dpy, cmap, &botc);
2749 botc.red = (botc.red * 3) / 5;
2750 botc.green = (botc.green * 3) / 5;
2751 botc.blue = (botc.blue * 3) / 5;
2752 if (allocate_nearest_color (dpy, cmap, &botc))
2754 if (botc.pixel == mw->core.background_pixel)
2756 XFreeColors (dpy, cmap, &botc.pixel, 1, 0);
2757 botc.red = MINL (65535, botc.red + 0x4000);
2758 botc.green = MINL (65535, botc.green + 0x4000);
2759 botc.blue = MINL (65535, botc.blue + 0x4000);
2760 if (allocate_nearest_color (dpy, cmap, &botc))
2762 mw->menu.bottom_shadow_color = botc.pixel;
2767 mw->menu.bottom_shadow_color = botc.pixel;
2774 if (top_frobbed && bottom_frobbed)
2776 int top_avg = ((topc.red / 3) + (topc.green / 3) + (topc.blue / 3));
2777 int bot_avg = ((botc.red / 3) + (botc.green / 3) + (botc.blue / 3));
2778 if (bot_avg > top_avg)
2780 Pixel tmp = mw->menu.top_shadow_color;
2781 mw->menu.top_shadow_color = mw->menu.bottom_shadow_color;
2782 mw->menu.bottom_shadow_color = tmp;
2784 else if (topc.pixel == botc.pixel)
2786 if (botc.pixel == mw->menu.foreground)
2787 mw->menu.top_shadow_color = mw->core.background_pixel;
2789 mw->menu.bottom_shadow_color = mw->menu.foreground;
2793 if (!mw->menu.top_shadow_pixmap &&
2794 mw->menu.top_shadow_color == mw->core.background_pixel)
2796 mw->menu.top_shadow_pixmap = mw->menu.gray_pixmap;
2797 mw->menu.top_shadow_color = mw->menu.foreground;
2799 if (!mw->menu.bottom_shadow_pixmap &&
2800 mw->menu.bottom_shadow_color == mw->core.background_pixel)
2802 mw->menu.bottom_shadow_pixmap = mw->menu.gray_pixmap;
2803 mw->menu.bottom_shadow_color = mw->menu.foreground;
2806 xgcv.fill_style = FillOpaqueStippled;
2807 xgcv.foreground = mw->menu.top_shadow_color;
2808 xgcv.background = mw->core.background_pixel;
2809 /* xgcv.stipple = mw->menu.top_shadow_pixmap; gtb */
2811 if (mw->menu.top_shadow_pixmap &&
2812 mw->menu.top_shadow_pixmap != XmUNSPECIFIED_PIXMAP)
2813 xgcv.stipple = mw->menu.top_shadow_pixmap;
2817 xgcv.stipple = mw->menu.top_shadow_pixmap;
2818 #endif /* NEED_MOTIF */
2819 pm = (xgcv.stipple ? GCStipple|GCFillStyle : 0);
2820 mw->menu.shadow_top_gc =
2821 XtGetGC((Widget)mw, GCForeground|GCBackground|pm, &xgcv);
2823 xgcv.foreground = mw->menu.bottom_shadow_color;
2824 /* xgcv.stipple = mw->menu.bottom_shadow_pixmap; gtb */
2826 if (mw->menu.top_shadow_pixmap &&
2827 mw->menu.top_shadow_pixmap != XmUNSPECIFIED_PIXMAP)
2828 xgcv.stipple = mw->menu.bottom_shadow_pixmap;
2832 xgcv.stipple = mw->menu.bottom_shadow_pixmap;
2833 #endif /* NEED_MOTIF */
2834 pm = (xgcv.stipple ? GCStipple|GCFillStyle : 0);
2835 mw->menu.shadow_bottom_gc =
2836 XtGetGC ((Widget)mw, GCForeground|GCBackground|pm, &xgcv);
2841 release_shadow_gcs (XlwMenuWidget mw)
2843 XtReleaseGC ((Widget) mw, mw->menu.shadow_top_gc);
2844 XtReleaseGC ((Widget) mw, mw->menu.shadow_bottom_gc);
2849 extract_font_extents (XlwMenuWidget mw)
2852 /* Find the maximal ascent/descent of the fonts in the font list
2853 so that all menu items can be the same height... */
2854 mw->menu.font_ascent = 0;
2855 mw->menu.font_descent = 0;
2858 XmFontContext context;
2859 #if (XmVersion >= 1002)
2860 XmFontListEntry fontentry;
2862 XmStringCharSet charset;
2866 if (! XmFontListInitFontContext (&context, mw->menu.font_list))
2868 #if (XmVersion >= 1002)
2869 /* There is a BUG in the 1.2 version of XmFontListGetNextFont() (or more
2870 specifically, in _XmGetFirstFont()) that can cause a null pointer to be
2871 passed to XFontsOfFontSet. Use XmFontListNextEntry(), which is the
2872 newer equivalent, instead. Also, it supports font sets, and the
2873 older function doesn't. */
2874 while ((fontentry = XmFontListNextEntry (context)))
2878 XtPointer one_of_them = XmFontListEntryGetFont (fontentry, &rettype);
2879 if (rettype == XmFONT_IS_FONTSET)
2881 XFontSet fontset = (XFontSet) one_of_them;
2882 XFontStruct **fontstruct_list;
2883 char **fontname_list;
2884 int fontcount = XFontsOfFontSet (fontset, &fontstruct_list,
2886 while (--fontcount >= 0)
2888 font = fontstruct_list[fontcount];
2889 if (font->ascent > (int) mw->menu.font_ascent)
2890 mw->menu.font_ascent = font->ascent;
2891 if (font->descent > (int) mw->menu.font_descent)
2892 mw->menu.font_descent = font->descent;
2895 else /* XmFONT_IS_FONT */
2897 font = (XFontStruct *) one_of_them;
2898 if (font->ascent > (int) mw->menu.font_ascent)
2899 mw->menu.font_ascent = font->ascent;
2900 if (font->descent > (int) mw->menu.font_descent)
2901 mw->menu.font_descent = font->descent;
2904 #else /* motif 1.1 */
2905 while (XmFontListGetNextFont (context, &charset, &font))
2907 if (font->ascent > (int) mw->menu.font_ascent)
2908 mw->menu.font_ascent = font->ascent;
2909 if (font->descent > (int) mw->menu.font_descent)
2910 mw->menu.font_descent = font->descent;
2913 #endif /* Motif version */
2914 XmFontListFreeFontContext (context);
2916 #else /* Not Motif */
2917 # ifdef USE_XFONTSET
2918 XFontStruct **fontstruct_list;
2919 char **fontname_list;
2921 int fontcount = XFontsOfFontSet(mw->menu.font_set, &fontstruct_list,
2923 mw->menu.font_ascent = 0;
2924 mw->menu.font_descent = 0;
2925 # if 0 /* nasty, personal debug, Kazz */
2926 fprintf(stderr, "fontSet count is %d\n", fontcount);
2928 while (--fontcount >= 0) {
2929 font = fontstruct_list[fontcount];
2930 if (font->ascent > (int) mw->menu.font_ascent)
2931 mw->menu.font_ascent = font->ascent;
2932 if (font->descent > (int) mw->menu.font_descent)
2933 mw->menu.font_descent = font->descent;
2935 # else /* ! USE_XFONTSET */
2936 mw->menu.font_ascent = mw->menu.font->ascent;
2937 mw->menu.font_descent = mw->menu.font->descent;
2939 #endif /* NEED_MOTIF */
2943 static XFontStruct *
2944 default_font_of_font_list (XmFontList font_list)
2946 XFontStruct *font = 0;
2948 /* Xm/Label.c does this: */
2949 _XmFontListGetDefaultFont (font_list, &font);
2952 XmFontContext context;
2953 #if (XmVersion >= 1002)
2954 XmFontListEntry fontentry;
2956 XtPointer one_of_them;
2958 XmStringCharSet charset;
2961 if (! XmFontListInitFontContext (&context, font_list))
2963 #if (XmVersion >= 1002)
2964 /* There is a BUG in the 1.2 version of XmFontListGetNextFont() (or more
2965 specifically, in _XmGetFirstFont()) that can cause a null pointer to be
2966 passed to XFontsOfFontSet. Use XmFontListNextEntry(), which is the
2967 newer equivalent, instead. */
2968 fontentry = XmFontListNextEntry (context);
2969 one_of_them = XmFontListEntryGetFont (fontentry, &rettype);
2970 if (rettype == XmFONT_IS_FONTSET)
2972 XFontSet fontset = (XFontSet) one_of_them;
2973 XFontStruct **fontstruct_list;
2974 char **fontname_list;
2975 (void) XFontsOfFontSet (fontset, &fontstruct_list, &fontname_list);
2976 font = fontstruct_list[0];
2978 else /* XmFONT_IS_FONT */
2980 font = (XFontStruct *) one_of_them;
2983 if (! XmFontListGetNextFont (context, &charset, &font))
2987 XmFontListFreeFontContext (context);
2991 if (! font) abort ();
2994 #endif /* NEED_MOTIF */
2997 XlwMenuInitialize (Widget request, Widget new, ArgList args,
3000 /* Get the GCs and the widget size */
3001 XlwMenuWidget mw = (XlwMenuWidget)new;
3003 XSetWindowAttributes xswa;
3006 Window window = RootWindowOfScreen (DefaultScreenOfDisplay (XtDisplay (mw)));
3007 Display *display = XtDisplay (mw);
3009 /* mw->menu.cursor = XCreateFontCursor (display, mw->menu.cursor_shape); */
3010 mw->menu.cursor = mw->menu.cursor_shape;
3012 mw->menu.gray_pixmap =
3013 XCreatePixmapFromBitmapData (display, window, (char *) gray_bits,
3014 gray_width, gray_height, 1, 0, 1);
3017 /* The menu.font_list slot came from the *fontList resource (Motif standard.)
3018 The menu.font_list_2 slot came from the *font resource, for backward
3019 compatibility with older versions of this code, and consistency with the
3020 rest of emacs. If both font and fontList are specified, we use font.
3021 If only one is specified, we use that. If neither are specified, we
3022 use the "fallback" value. What a kludge!!!
3024 Note that this has the bug that a more general wildcard like "*fontList:"
3025 will override a more specific resource like "Emacs*menubar.font:". But
3026 I can't think of a way around that.
3028 if (mw->menu.font_list) /* if *fontList is specified, use that */
3030 else if (mw->menu.font_list_2) /* else if *font is specified, use that */
3031 mw->menu.font_list = mw->menu.font_list_2;
3032 else /* otherwise use default */
3033 mw->menu.font_list = mw->menu.fallback_font_list;
3036 make_drawing_gcs (mw);
3037 make_shadow_gcs (mw);
3038 extract_font_extents (mw);
3040 xswa.background_pixel = mw->core.background_pixel;
3041 xswa.border_pixel = mw->core.border_pixel;
3042 mask = CWBackPixel | CWBorderPixel;
3044 mw->menu.popped_up = False;
3045 mw->menu.pointer_grabbed = False;
3046 mw->menu.next_release_must_exit = False;
3048 mw->menu.old_depth = 1;
3049 mw->menu.old_stack = XtNew (widget_value*);
3050 mw->menu.old_stack_length = 1;
3051 mw->menu.old_stack [0] = mw->menu.contents;
3053 mw->menu.new_depth = 0;
3054 mw->menu.new_stack = 0;
3055 mw->menu.new_stack_length = 0;
3056 push_new_stack (mw, mw->menu.contents);
3058 mw->menu.windows = XtNew (window_state);
3059 mw->menu.windows_length = 1;
3060 mw->menu.windows [0].x = 0;
3061 mw->menu.windows [0].y = 0;
3062 mw->menu.windows [0].width = 0;
3063 mw->menu.windows [0].height = 0;
3066 mw->core.width = mw->menu.windows [0].width;
3067 mw->core.height = mw->menu.windows [0].height;
3071 XlwMenuClassInitialize (void)
3073 initialize_massaged_resource_char();
3077 XlwMenuRealize (Widget w, Mask *valueMask, XSetWindowAttributes *attributes)
3079 XlwMenuWidget mw = (XlwMenuWidget)w;
3080 XSetWindowAttributes xswa;
3083 (*xlwMenuWidgetClass->core_class.superclass->core_class.realize)
3084 (w, valueMask, attributes);
3086 xswa.save_under = True;
3087 xswa.cursor = mw->menu.cursor_shape;
3088 mask = CWSaveUnder | CWCursor;
3089 if (mw->menu.use_backing_store)
3091 xswa.backing_store = Always;
3092 mask |= CWBackingStore;
3094 XChangeWindowAttributes (XtDisplay (w), XtWindow (w), mask, &xswa);
3096 mw->menu.windows [0].window = XtWindow (w);
3097 mw->menu.windows [0].x = w->core.x;
3098 mw->menu.windows [0].y = w->core.y;
3099 mw->menu.windows [0].width = w->core.width;
3100 mw->menu.windows [0].height = w->core.height;
3103 /* Only the toplevel menubar/popup is a widget so it's the only one that
3104 receives expose events through Xt. So we repaint all the other panes
3105 when receiving an Expose event. */
3107 XlwMenuRedisplay (Widget w, XEvent *ev, Region region)
3109 XlwMenuWidget mw = (XlwMenuWidget)w;
3112 if (mw->core.being_destroyed) return;
3114 for (i = 0; i < mw->menu.old_depth; i++)
3115 display_menu (mw, i, False, NULL, NULL, NULL, NULL, NULL);
3116 set_new_state (mw, NULL, mw->menu.old_depth); /* #### - ??? */
3117 remap_menubar (mw); /* #### - do these two lines do anything? */
3121 XlwMenuDestroy (Widget w)
3124 XlwMenuWidget mw = (XlwMenuWidget) w;
3126 if (mw->menu.pointer_grabbed)
3128 XtUngrabPointer (w, CurrentTime);
3129 mw->menu.pointer_grabbed = False;
3132 release_drawing_gcs (mw);
3133 release_shadow_gcs (mw);
3135 /* this doesn't come from the resource db but is created explicitly
3136 so we must free it ourselves. */
3137 XFreePixmap (XtDisplay (mw), mw->menu.gray_pixmap);
3138 mw->menu.gray_pixmap = (Pixmap) -1;
3140 /* Don't free mw->menu.contents because that comes from our creator.
3141 The `*_stack' elements are just pointers into `contents' so leave
3142 that alone too. But free the stacks themselves. */
3143 if (mw->menu.old_stack) XtFree ((char *) mw->menu.old_stack);
3144 if (mw->menu.new_stack) XtFree ((char *) mw->menu.new_stack);
3146 /* Remember, you can't free anything that came from the resource
3147 database. This includes:
3149 mw->menu.top_shadow_pixmap
3150 mw->menu.bottom_shadow_pixmap
3153 Also the color cells of top_shadow_color, bottom_shadow_color,
3154 foreground, and button_foreground will never be freed until this
3155 client exits. Nice, eh?
3158 /* start from 1 because the one in slot 0 is w->core.window */
3159 for (i = 1; i < mw->menu.windows_length; i++)
3160 XDestroyWindow (XtDisplay (mw), mw->menu.windows [i].window);
3161 if (mw->menu.windows)
3162 XtFree ((char *) mw->menu.windows);
3166 XlwMenuSetValues (Widget current, Widget request, Widget new, ArgList args,
3169 XlwMenuWidget oldmw = (XlwMenuWidget)current;
3170 XlwMenuWidget newmw = (XlwMenuWidget)new;
3171 Boolean redisplay = False;
3174 if (newmw->menu.contents
3175 && newmw->menu.contents->contents
3176 && newmw->menu.contents->contents->change >= VISIBLE_CHANGE)
3179 if (newmw->core.background_pixel != oldmw->core.background_pixel
3180 || newmw->menu.foreground != oldmw->menu.foreground
3181 /* For the XEditResource protocol, which may want to change the font. */
3183 || newmw->menu.font_list != oldmw->menu.font_list
3184 || newmw->menu.font_list_2 != oldmw->menu.font_list_2
3185 || newmw->menu.fallback_font_list != oldmw->menu.fallback_font_list
3187 || newmw->menu.font != oldmw->menu.font
3191 release_drawing_gcs (newmw);
3192 make_drawing_gcs (newmw);
3195 for (i = 0; i < oldmw->menu.windows_length; i++)
3197 XSetWindowBackground (XtDisplay (oldmw),
3198 oldmw->menu.windows [i].window,
3199 newmw->core.background_pixel);
3200 /* clear windows and generate expose events */
3201 XClearArea (XtDisplay (oldmw), oldmw->menu.windows[i].window,
3210 XlwMenuResize (Widget w)
3212 XlwMenuWidget mw = (XlwMenuWidget)w;
3214 mw->menu.windows [0].width = mw->core.width;
3215 mw->menu.windows [0].height = mw->core.height;
3218 \f/* Action procedures */
3220 handle_single_motion_event (XlwMenuWidget mw, XMotionEvent *ev,
3227 if (!map_event_to_widget_value (mw, ev, &val, &level, &stay_up))
3229 /* we wind up here when: (a) the event is in the menubar, (b) the
3230 event isn't in the menubar or any of the panes, (c) the event is on
3231 a disabled menu item */
3232 pop_new_stack_if_no_contents (mw);
3233 if (select_p && !stay_up) {
3234 /* pop down all menus and exit */
3235 mw->menu.next_release_must_exit = True;
3236 set_new_state(mw, (val = NULL), 1);
3241 /* we wind up here when: (a) the event pops up a pull_right menu,
3242 (b) a menu item that is not disabled is highlighted */
3243 if (select_p && mw->menu.bounce_down
3244 && close_to_reference_time((Widget)mw,
3245 mw->menu.menu_bounce_time,
3248 /* motion can cause more than one event. Don't bounce right back
3249 up if we've just bounced down. */
3252 else if (select_p && mw->menu.bounce_down &&
3253 mw->menu.last_selected_val &&
3254 (mw->menu.last_selected_val == val))
3256 val = NULL; /* assigned to mw->last_selected_val below */
3257 mw->menu.menu_bounce_time = ev->time;
3258 /* popdown last menu if we're selecting the same menu item as we did
3259 last time and the XlwMenu.bounceDown resource is set, if the
3260 item is on the menubar itself, then exit. */
3261 if (level == (mw->menu.popped_up ? 0 : 1))
3262 mw->menu.next_release_must_exit = True;
3265 mw->menu.menu_bounce_time = 0;
3266 set_new_state (mw, val, level);
3268 mw->menu.last_selected_val = val;
3271 /* Sync with the display. Makes it feel better on X terms. */
3272 XFlush (XtDisplay (mw));
3276 handle_motion_event (XlwMenuWidget mw, XMotionEvent *ev,
3281 unsigned int state = ev->state;
3282 XMotionEvent *event= ev, dummy;
3284 /* allow motion events to be generated again */
3285 dummy.window = ev->window;
3287 && XQueryPointer (XtDisplay (mw), dummy.window,
3288 &dummy.root, &dummy.subwindow,
3289 &dummy.x_root, &dummy.y_root,
3292 && dummy.state == state
3293 && (dummy.x_root != x || dummy.y_root != y))
3295 /* don't handle the event twice or that breaks bounce_down. --Stig */
3296 dummy.type = ev->type;
3300 lw_menu_accelerate = False;
3301 handle_single_motion_event (mw, event, select_p);
3304 Time x_focus_timestamp_really_sucks_fix_me_better;
3307 Start (Widget w, XEvent *ev, String *params, Cardinal *num_params)
3309 XlwMenuWidget mw = (XlwMenuWidget)w;
3311 lw_menubar_widget = w;
3313 lw_menu_active = True;
3315 if (!mw->menu.pointer_grabbed)
3317 mw->menu.menu_post_time = ev->xbutton.time;
3318 mw->menu.menu_bounce_time = 0;
3319 mw->menu.next_release_must_exit = True;
3320 mw->menu.last_selected_val = NULL;
3321 x_focus_timestamp_really_sucks_fix_me_better =
3322 ((XButtonPressedEvent*)ev)->time;
3323 XtCallCallbackList ((Widget)mw, mw->menu.open, NULL);
3325 /* notes the absolute position of the menubar window */
3326 mw->menu.windows [0].x = ev->xmotion.x_root - ev->xmotion.x;
3327 mw->menu.windows [0].y = ev->xmotion.y_root - ev->xmotion.y;
3329 XtGrabPointer ((Widget)mw, False,
3330 (ButtonMotionMask | ButtonReleaseMask | ButtonPressMask),
3331 GrabModeAsync, GrabModeAsync,
3332 None, mw->menu.cursor_shape,
3333 ((XButtonPressedEvent*)ev)->time);
3334 mw->menu.pointer_grabbed = True;
3337 /* handles the down like a move, slots are mostly compatible */
3338 handle_motion_event (mw, &ev->xmotion, True);
3342 Drag (Widget w, XEvent *ev, String *params, Cardinal *num_params)
3344 XlwMenuWidget mw = (XlwMenuWidget)w;
3345 handle_motion_event (mw, &ev->xmotion, False);
3349 Select (Widget w, XEvent *ev, String *params, Cardinal *num_params)
3351 XlwMenuWidget mw = (XlwMenuWidget)w;
3352 widget_value *selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
3354 lw_menu_accelerate = False;
3356 /* If user releases the button quickly, without selecting anything,
3357 after the initial down-click that brought the menu up,
3359 if ((selected_item == 0 || selected_item->call_data == 0)
3360 && (!mw->menu.next_release_must_exit
3361 || close_to_reference_time(w, mw->menu.menu_post_time, ev)))
3363 mw->menu.next_release_must_exit = False;
3367 /* pop down everything */
3368 mw->menu.new_depth = 1;
3371 /* Destroy() only gets called for popup menus. Menubar widgets aren't
3372 destroyed when their menu panes get nuked. */
3373 if (mw->menu.pointer_grabbed)
3375 XtUngrabPointer ((Widget)w, ev->xmotion.time);
3376 mw->menu.pointer_grabbed = False;
3379 if (mw->menu.popped_up)
3381 mw->menu.popped_up = False;
3382 XtPopdown (XtParent (mw));
3385 lw_menu_active = False;
3387 x_focus_timestamp_really_sucks_fix_me_better =
3388 ((XButtonPressedEvent*)ev)->time;
3391 XtCallCallbackList ((Widget) mw, mw->menu.select, (XtPointer) selected_item);
3394 \f/* Action procedures for keyboard accelerators */
3398 xlw_set_menu (Widget w, widget_value *val)
3400 lw_menubar_widget = w;
3401 set_new_state ((XlwMenuWidget)w, val, 1);
3404 /* prepare the menu structure via the call-backs */
3406 xlw_map_menu (Time t)
3408 XlwMenuWidget mw = (XlwMenuWidget)lw_menubar_widget;
3410 lw_menu_accelerate = True;
3412 if (!mw->menu.pointer_grabbed)
3414 XWindowAttributes ret;
3417 unsigned int num_waste;
3419 lw_menu_active = True;
3421 mw->menu.menu_post_time = t;
3422 mw->menu.menu_bounce_time = 0;
3424 mw->menu.next_release_must_exit = True;
3425 mw->menu.last_selected_val = NULL;
3427 XtCallCallbackList ((Widget)mw, mw->menu.open, NULL);
3429 /* do this for keyboards too! */
3430 /* notes the absolute position of the menubar window */
3432 mw->menu.windows [0].x = ev->xmotion.x_root - ev->xmotion.x;
3433 mw->menu.windows [0].y = ev->xmotion.y_root - ev->xmotion.y;
3436 /* get the geometry of the menubar */
3438 /* there has to be a better way than this. */
3440 mw->menu.windows [0].x = 0;
3441 mw->menu.windows [0].y = 0;
3443 parent = XtWindow (lw_menubar_widget);
3446 XGetWindowAttributes (XtDisplay (lw_menubar_widget), parent, &ret);
3447 mw->menu.windows [0].x += ret.x;
3448 mw->menu.windows [0].y += ret.y;
3451 XQueryTree (XtDisplay (lw_menubar_widget), parent, &root, &parent, &waste,
3458 while (parent != root);
3460 XtGrabPointer ((Widget)mw, False,
3461 (ButtonMotionMask | ButtonReleaseMask | ButtonPressMask),
3462 GrabModeAsync, GrabModeAsync,
3463 None, mw->menu.cursor_shape, t);
3464 mw->menu.pointer_grabbed = True;
3468 /* display the stupid menu already */
3470 xlw_display_menu (Time t)
3472 XlwMenuWidget mw = (XlwMenuWidget)lw_menubar_widget;
3474 lw_menu_accelerate = True;
3478 /* Sync with the display. Makes it feel better on X terms. */
3479 XFlush (XtDisplay (mw));
3482 /* push a sub menu */
3484 xlw_push_menu (widget_value *val)
3486 push_new_stack ((XlwMenuWidget)lw_menubar_widget, val);
3489 /* pop a sub menu */
3493 if (((XlwMenuWidget)lw_menubar_widget)->menu.new_depth > 0)
3494 ((XlwMenuWidget)lw_menubar_widget)->menu.new_depth --;
3501 xlw_kill_menus (widget_value *val)
3503 XlwMenuWidget mw = (XlwMenuWidget)lw_menubar_widget;
3505 lw_menu_accelerate = False;
3507 mw->menu.new_depth = 1;
3510 if (mw->menu.pointer_grabbed)
3512 XtUngrabPointer (lw_menubar_widget, CurrentTime);
3513 mw->menu.pointer_grabbed = False;
3516 lw_menu_active = False;
3517 XtCallCallbackList (lw_menubar_widget, mw->menu.select, (XtPointer)val);
3520 /* set the menu item */
3522 xlw_set_item (widget_value *val)
3524 if (((XlwMenuWidget)lw_menubar_widget)->menu.new_depth > 0)
3525 ((XlwMenuWidget) lw_menubar_widget)->menu.new_depth --;
3526 push_new_stack ((XlwMenuWidget) lw_menubar_widget, val);
3529 /* get either the current entry or a list of all entries in the current submenu */
3531 xlw_get_entries (int allp)
3533 XlwMenuWidget mw = (XlwMenuWidget)lw_menubar_widget;
3536 if (mw->menu.new_depth >= 2)
3537 return mw->menu.new_stack [mw->menu.new_depth - 2]->contents;
3539 return mw->menu.new_stack[0];
3542 if (mw->menu.new_depth >= 1)
3543 return mw->menu.new_stack [mw->menu.new_depth - 1];
3549 xlw_menu_level (void)
3551 return ((XlwMenuWidget)lw_menubar_widget)->menu.new_depth;
3555 /* Special code to pop-up a menu */
3557 xlw_pop_up_menu (XlwMenuWidget mw, XButtonPressedEvent *event)
3559 int x = event->x_root;
3560 int y = event->y_root;
3563 int borderwidth = mw->menu.shadow_thickness;
3564 Screen* screen = XtScreen (mw);
3566 mw->menu.menu_post_time = event->time;
3567 mw->menu.menu_bounce_time = 0;
3568 mw->menu.next_release_must_exit = True;
3569 mw->menu.last_selected_val = NULL;
3571 XtCallCallbackList ((Widget) mw, mw->menu.open, NULL);
3575 w = mw->menu.windows [0].width;
3576 h = mw->menu.windows [0].height;
3581 if (x < borderwidth)
3584 if (x > WidthOfScreen (screen) - w - 2 * borderwidth)
3585 x = WidthOfScreen (screen) - w - 2 * borderwidth;
3587 if (y < borderwidth)
3590 if (y > HeightOfScreen (screen) - h - 2 * borderwidth)
3591 y = HeightOfScreen (screen) - h - 2 * borderwidth;
3593 mw->menu.popped_up = True;
3594 XtConfigureWidget (XtParent (mw), x, y, w, h,
3595 XtParent (mw)->core.border_width);
3596 XtPopup (XtParent (mw), XtGrabExclusive);
3597 display_menu (mw, 0, False, NULL, NULL, NULL, NULL, NULL);
3598 if (!mw->menu.pointer_grabbed)
3600 XtGrabPointer ((Widget)mw, False,
3601 (ButtonMotionMask | ButtonReleaseMask | ButtonPressMask),
3602 GrabModeAsync, GrabModeAsync,
3603 None, mw->menu.cursor_shape, event->time);
3604 mw->menu.pointer_grabbed = True;
3607 mw->menu.windows [0].x = x + borderwidth;
3608 mw->menu.windows [0].y = y + borderwidth;
3610 handle_motion_event (mw, (XMotionEvent *) event, True);
3616 * This is a horrible function which should not be needed.
3617 * use it to put the resize method back the way the XlwMenu
3618 * class initializer put it. Motif screws with this when
3619 * the XlwMenu class gets instantiated.
3622 xlw_unmunge_class_resize (Widget w)
3624 if (w->core.widget_class->core_class.resize != XlwMenuResize)
3625 w->core.widget_class->core_class.resize = XlwMenuResize;