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 int int_ch = (int) (unsigned char) ch;
487 *out++ = firstp ? tolower (int_ch) : toupper (int_ch);
489 while ((ch = massaged_resource_char[(unsigned char) *in++]) != '\0')
491 if (!*(in-1)) /* Overshot the NULL byte? */
497 #ifdef PRINT_XLWMENU_RESOURCE_CONVERSIONS
498 printf ("! Emacs*XlwMenu.%s.labelString:\t%s\n", save_out, save_in);
499 printf ( "Emacs*XlwMenu.%s.labelString:\n", save_out);
506 { "labelString", "LabelString", XtRString, sizeof(String),
511 * This function looks through string searching for parameter
512 * inserts of the form:
514 * padding is space (' ') or dash ('-') characters meaning
515 * padding to the left or right of the inserted parameter.
516 * In essence all %1 strings are replaced by value in the return
517 * value (which the caller is expected to free).
518 * %% means insert one % (like printf).
519 * %1 means insert value.
520 * %-1 means insert value followed by one space. The latter is
521 * not inserted if value is a zero length string.
524 parameterize_string (CONST char *string, CONST char *value)
528 unsigned int done = 0;
533 result = XtMalloc(1);
541 for (ntimes = 1, result = (char *) string; (percent = strchr(result, '%'));
543 result = &percent[1];
545 result = XtMalloc ((ntimes * strlen(value)) + strlen(string) + 4);
548 while ((percent = strchr(string, '%')))
550 unsigned int left_pad;
551 unsigned int right_pad;
554 if (percent[1] == '%')
555 { /* it's a real % */
556 strncat (result, string, 1 + percent - string); /* incl % */
557 string = &percent[2]; /* after the second '%' */
558 continue; /* with the while() loop */
564 for (p = &percent[1]; /* test *p inside the loop */ ; p++)
575 { /* param and terminator */
576 strncat (result, string, percent - string);
577 if (value[0] != '\0')
580 for (i = 0; i < left_pad; i++)
581 strcat (result, " ");
582 strcat (result, value);
583 for (i = 0; i < right_pad; i++)
584 strcat (result, " ");
586 string = &p[1]; /* after the '1' */
587 done++; /* no need to do old way */
588 break; /* out of for() loop */
591 { /* bogus, copy the format as is */
592 /* out of for() loop */
593 strncat (result, string, 1 + p - string);
594 string = (*p ? &p[1] : p);
600 /* Copy the tail of the string */
601 strcat (result, string);
603 /* If we have not processed a % string, and we have a value, tail it. */
604 if (!done && value[0] != '\0')
606 strcat (result, " ");
607 strcat (result, value);
616 resource_widget_value (XlwMenuWidget mw, widget_value *val)
618 if (!val->toolkit_data)
620 char *resourced_name = NULL;
621 char *converted_name, *str;
622 XmString complete_name;
623 char massaged_name [1024];
625 if (mw->menu.lookup_labels)
627 /* Convert value style name into resource style name.
628 eg: "Free Willy" becomes "freeWilly" */
629 massage_resource_name (val->name, massaged_name);
631 /* If we have a value (parameter) see if we can find a "Named"
635 char named_name[1024];
636 sprintf (named_name, "%sNamed", massaged_name);
637 XtGetSubresources ((Widget) mw,
638 (XtPointer) &resourced_name,
639 named_name, named_name,
640 nameResource, 1, NULL, 0);
643 /* If nothing yet, try to load from the massaged name. */
646 XtGetSubresources ((Widget) mw,
647 (XtPointer) &resourced_name,
648 massaged_name, massaged_name,
649 nameResource, 1, NULL, 0);
651 } /* if (mw->menu.lookup_labels) */
653 /* Still nothing yet, use the name as the value. */
655 resourced_name = val->name;
657 /* Parameterize the string. */
658 converted_name = parameterize_string (resourced_name, val->value);
660 /* nuke newline characters to prevent menubar screwups */
661 for ( str = converted_name ; *str ; str++ )
663 if (str[0] == '\n') str[0] = ' ';
666 /* Improve OSF's bottom line. */
667 #if (XmVersion >= 1002)
668 complete_name = XmStringCreateLocalized (converted_name);
670 complete_name = XmStringCreateLtoR (converted_name,
671 XmSTRING_DEFAULT_CHARSET);
673 XtFree (converted_name);
675 val->toolkit_data = complete_name;
676 val->free_toolkit_data = True;
678 return (XmString) val->toolkit_data;
683 /* These two routines should be a seperate file..djw */
685 xlw_create_localized_string (Widget w,
696 XtGetSubresources (w,
706 return parameterize_string (string, arg);
710 xlw_create_localized_xmstring (Widget w,
715 char * string = xlw_create_localized_string (w, name, args, nargs);
716 XmString xm_string = XmStringCreateLtoR (string, XmSTRING_DEFAULT_CHARSET);
725 resource_widget_value (XlwMenuWidget mw, widget_value *val)
727 if (!val->toolkit_data)
729 char *resourced_name = NULL;
731 char massaged_name [1024];
733 if (mw->menu.lookup_labels)
735 massage_resource_name (val->name, massaged_name);
737 XtGetSubresources ((Widget) mw,
738 (XtPointer) &resourced_name,
739 massaged_name, massaged_name,
740 nameResource, 1, NULL, 0);
743 resourced_name = val->name;
745 complete_name = parameterize_string (resourced_name, val->value);
747 val->toolkit_data = complete_name;
748 /* nuke newline characters to prevent menubar screwups */
749 for ( ; *complete_name ; complete_name++ )
751 if (complete_name[0] == '\n')
752 complete_name[0] = ' ';
754 val->free_toolkit_data = True;
756 return (char *) val->toolkit_data;
761 /* Code for drawing strings. */
763 string_draw (XlwMenuWidget mw,
775 XmStringDraw (XtDisplay (mw), window,
779 1000, /* ???? width */
780 XmALIGNMENT_BEGINNING,
781 0, /* ???? layout_direction */
785 XmbDrawString (XtDisplay (mw), window, mw->menu.font_set, gc,
786 x, y + mw->menu.font_ascent, string, strlen (string));
788 XDrawString (XtDisplay (mw), window, gc,
789 x, y + mw->menu.font_ascent, string, strlen (string));
790 # endif /* USE_XFONTSET */
807 Dimension width, height;
815 newstring = XmStringLtoRCreate (&string[start], XmFONTLIST_DEFAULT_TAG);
817 XtDisplay (mw), window,
821 1000, /* ???? width */
822 XmALIGNMENT_BEGINNING,
823 0, /* ???? layout_direction */
826 XmStringExtent (mw->menu.font_list, newstring, &width, &height);
827 XmStringFree (newstring);
837 XtDisplay (mw), window, mw->menu.font_set, gc,
838 x, y + mw->menu.font_ascent, &string[start], end - start);
840 mw->menu.font_set, &string[start], end - start, &ri, &rl);
849 XtDisplay (mw), window, gc,
850 x, y + mw->menu.font_ascent, &string[start], end - start);
852 mw->menu.font, &string[start], end - start,
853 &drop, &drop, &drop, &xcs);
860 string_draw_u (XlwMenuWidget mw,
875 XmStringGetLtoR (string, XmFONTLIST_DEFAULT_TAG, &chars);
879 for (i=0; chars[i]; ++i) {
880 if (chars[i] == '%' && chars[i+1] == '_') {
883 x += string_draw_range (mw, window, x, y, gc, chars, s, i);
884 w = string_draw_range (mw, window, x, y, gc, chars, i+2, i+3);
886 /* underline next character */
887 XDrawLine (XtDisplay (mw), window, gc, x - 1,
888 y + mw->menu.font_ascent + 1,
889 x + w - 1, y + mw->menu.font_ascent + 1 );
895 x += string_draw_range (mw, window, x, y, gc, chars, s, i);
902 binding_draw (XlwMenuWidget mw, Window w, int x, int y, GC gc, char *value)
905 XmString xm_value = XmStringCreateLtoR(value, XmSTRING_DEFAULT_CHARSET);
906 string_draw (mw, w, x, y, gc, xm_value);
907 XmStringFree (xm_value);
909 string_draw (mw, w, x, y, gc, value);
913 /* Low level code for drawing 3-D edges. */
915 shadow_rectangle_draw (Display *dpy,
922 unsigned int thickness)
931 points [1].x = x + width;
933 points [2].x = x + width - thickness;
934 points [2].y = y + thickness;
936 points [3].y = y + thickness;
937 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
939 points [0].y = y + thickness;
941 points [1].y = y + height;
942 points [2].x = x + thickness;
943 points [2].y = y + height - thickness;
944 points [3].x = x + thickness;
945 points [3].y = y + thickness;
946 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
947 points [0].x = x + width;
949 points [1].x = x + width - thickness;
950 points [1].y = y + thickness;
951 points [2].x = x + width - thickness;
952 points [2].y = y + height - thickness;
953 points [3].x = x + width;
954 points [3].y = y + height - thickness;
955 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
957 points [0].y = y + height;
958 points [1].x = x + width;
959 points [1].y = y + height;
960 points [2].x = x + width;
961 points [2].y = y + height - thickness;
962 points [3].x = x + thickness;
963 points [3].y = y + height - thickness;
964 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
967 typedef enum e_shadow_type
969 /* these are Motif compliant */
975 SHADOW_ETCHED_OUT_DASH,
976 SHADOW_ETCHED_IN_DASH,
979 SHADOW_SINGLE_DASHED_LINE,
980 SHADOW_DOUBLE_DASHED_LINE,
982 /* these are all non-Motif */
983 SHADOW_DOUBLE_ETCHED_OUT,
984 SHADOW_DOUBLE_ETCHED_IN,
985 SHADOW_DOUBLE_ETCHED_OUT_DASH,
986 SHADOW_DOUBLE_ETCHED_IN_DASH
990 shadow_draw (XlwMenuWidget mw,
997 Display *dpy = XtDisplay (mw);
1000 int thickness = mw->menu.shadow_thickness;
1004 Boolean etched = False;
1008 case SHADOW_BACKGROUND:
1009 top_gc = bottom_gc = mw->menu.background_gc;
1011 case SHADOW_ETCHED_IN:
1012 top_gc = mw->menu.shadow_bottom_gc;
1013 bottom_gc = mw->menu.shadow_top_gc;
1016 case SHADOW_ETCHED_OUT:
1017 top_gc = mw->menu.shadow_top_gc;
1018 bottom_gc = mw->menu.shadow_bottom_gc;
1022 top_gc = mw->menu.shadow_bottom_gc;
1023 bottom_gc = mw->menu.shadow_top_gc;
1027 top_gc = mw->menu.shadow_top_gc;
1028 bottom_gc = mw->menu.shadow_bottom_gc;
1034 unsigned int half = thickness/2;
1035 shadow_rectangle_draw (dpy,
1040 width - half, height - half,
1042 shadow_rectangle_draw (dpy,
1047 width - half , height - half,
1052 shadow_rectangle_draw (dpy,
1063 arrow_decoration_draw (XlwMenuWidget mw,
1069 Display *dpy = XtDisplay (mw);
1073 int thickness = mw->menu.shadow_thickness;
1076 int length = (int)((double)width * 0.87);
1077 int thick_med = (int)((double)thickness * 1.73);
1080 half_width = width/2 + 1;
1082 half_width = width/2;
1084 select_gc = mw->menu.background_gc;
1088 top_gc = mw->menu.shadow_bottom_gc;
1089 bottom_gc = mw->menu.shadow_top_gc;
1093 top_gc = mw->menu.shadow_top_gc;
1094 bottom_gc = mw->menu.shadow_bottom_gc;
1097 /* Fill internal area. We do this first so that the borders have a
1099 points [0].x = x + thickness;
1100 points [0].y = y + thickness;
1101 points [1].x = x + length - thickness;
1102 points [1].y = y + half_width;
1103 points [2].x = x + length - thickness;
1104 points [2].y = y + half_width + thickness;
1105 points [3].x = x + thickness;
1106 points [3].y = y + width - thickness;
1119 points [1].x = x + thickness;
1120 points [1].y = y + thick_med;
1121 points [2].x = x + thickness;
1122 points [2].y = y + width - thick_med;
1124 points [3].y = y + width;
1126 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
1130 points [0].y = y + width;
1131 points [1].x = x + length;
1132 points [1].y = y + half_width;
1133 points [2].x = x + length - (thickness + thickness);
1134 points [2].y = y + half_width;
1135 points [3].x = x + thickness;
1136 points [3].y = y + width - thick_med;
1138 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
1143 points [1].x = x + length;
1144 points [1].y = y + half_width;
1145 points [2].x = x + length - (thickness + thickness);
1146 points [2].y = y + half_width;
1147 points [3].x = x + thickness;
1148 points [3].y = y + thick_med;
1150 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
1154 toggle_decoration_draw (XlwMenuWidget mw,
1160 Display *dpy = XtDisplay (mw);
1161 int thickness = mw->menu.shadow_thickness;
1163 GC select_gc = mw->menu.select_gc;
1170 /* Fill internal area. */
1172 XFillRectangle (dpy,
1177 width - (2*thickness),
1178 width - (2*thickness));
1180 shadow_draw (mw, window, x, y, width, width, type);
1184 radio_decoration_draw (XlwMenuWidget mw,
1190 Display *dpy = XtDisplay (mw);
1193 GC select_gc = mw->menu.select_gc;
1194 int thickness = mw->menu.shadow_thickness;
1204 half_width = width/2;
1208 top_gc = mw->menu.shadow_bottom_gc;
1209 bottom_gc = mw->menu.shadow_top_gc;
1213 top_gc = mw->menu.shadow_top_gc;
1214 bottom_gc = mw->menu.shadow_bottom_gc;
1218 /* Draw the bottom first, just in case the regions overlap.
1219 The top should cast the longer shadow. */
1220 points [0].x = x; /* left corner */
1221 points [0].y = y + half_width;
1222 points [1].x = x + half_width; /* bottom corner */
1223 points [1].y = y + width;
1224 points [2].x = x + half_width; /* bottom inside corner */
1225 points [2].y = y + width - thickness;
1226 points [3].x = x + thickness; /* left inside corner */
1227 points [3].y = y + half_width;
1229 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
1231 points [0].x = x + half_width; /* bottom corner */
1232 points [0].y = y + width;
1233 points [1].x = x + width; /* right corner */
1234 points [1].y = y + half_width;
1235 points [2].x = x + width - thickness; /* right inside corner */
1236 points [2].y = y + half_width;
1237 points [3].x = x + half_width; /* bottom inside corner */
1238 points [3].y = y + width - thickness;
1240 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
1242 points [0].x = x; /* left corner */
1243 points [0].y = y + half_width;
1244 points [1].x = x + half_width; /* top corner */
1246 points [2].x = x + half_width; /* top inside corner */
1247 points [2].y = y + thickness;
1248 points [3].x = x + thickness; /* left inside corner */
1249 points [3].y = y + half_width;
1251 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
1253 points [0].x = x + half_width; /* top corner */
1255 points [1].x = x + width; /* right corner */
1256 points [1].y = y + half_width;
1257 points [2].x = x + width - thickness; /* right inside corner */
1258 points [2].y = y + half_width;
1259 points [3].x = x + half_width; /* top inside corner */
1260 points [3].y = y + thickness;
1262 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
1264 /* Draw the bottom first, just in case the regions overlap.
1265 The top should cast the longer shadow. */
1267 points [npoints].x = x; /* left corner */
1268 points [npoints++].y = y + half_width;
1269 points [npoints].x = x + half_width; /* bottom corner */
1270 points [npoints++].y = y + width;
1271 points [npoints].x = x + width; /* right corner */
1272 points [npoints++].y = y + half_width;
1273 points [npoints].x = x + width - thickness; /* right inside corner */
1274 points [npoints++].y = y + half_width;
1275 points [npoints].x = x + half_width; /* bottom inside corner */
1276 points [npoints++].y = y + width - thickness;
1277 points [npoints].x = x + thickness; /* left inside corner */
1278 points [npoints++].y = y + half_width;
1280 XFillPolygon (dpy, window, bottom_gc,
1281 points, npoints, Nonconvex, CoordModeOrigin);
1285 points [npoints].x = x; /* left corner */
1286 points [npoints++].y = y + half_width;
1287 points [npoints].x = x + half_width; /* top corner */
1288 points [npoints++].y = y;
1289 points [npoints].x = x + width; /* right corner */
1290 points [npoints++].y = y + half_width;
1291 points [npoints].x = x + width - thickness; /* right inside corner */
1292 points [npoints++].y = y + half_width;
1293 points [npoints].x = x + half_width; /* top inside corner */
1294 points [npoints++].y = y + thickness;
1295 points [npoints].x = x + thickness; /* left inside corner */
1296 points [npoints++].y = y + half_width;
1298 XFillPolygon (dpy, window, top_gc, points, npoints, Nonconvex,
1303 /* Fill internal area. */
1306 points [0].x = x + thickness;
1307 points [0].y = y + half_width;
1308 points [1].x = x + half_width;
1309 points [1].y = y + thickness;
1310 points [2].x = x + width - thickness;
1311 points [2].y = y + half_width;
1312 points [3].x = x + half_width;
1313 points [3].y = y + width - thickness;
1325 separator_decoration_draw (XlwMenuWidget mw,
1332 Display *dpy = XtDisplay (mw);
1335 unsigned int offset = 0;
1336 unsigned int num_separators = 1;
1337 unsigned int top_line_thickness = 0;
1338 unsigned int bottom_line_thickness = 0;
1339 Boolean dashed = False;
1343 case SHADOW_NO_LINE: /* nothing to do */
1345 case SHADOW_DOUBLE_LINE:
1347 case SHADOW_SINGLE_LINE:
1348 top_gc = bottom_gc = mw->menu.foreground_gc;
1349 top_line_thickness = 1;
1351 case SHADOW_DOUBLE_DASHED_LINE:
1353 case SHADOW_SINGLE_DASHED_LINE:
1354 top_gc = bottom_gc = mw->menu.foreground_gc;
1355 top_line_thickness = 1;
1358 case SHADOW_DOUBLE_ETCHED_OUT_DASH:
1360 case SHADOW_ETCHED_OUT_DASH:
1361 top_gc = mw->menu.shadow_top_gc;
1362 bottom_gc = mw->menu.shadow_bottom_gc;
1363 top_line_thickness = mw->menu.shadow_thickness/2;
1364 bottom_line_thickness = mw->menu.shadow_thickness - top_line_thickness;
1367 case SHADOW_DOUBLE_ETCHED_IN_DASH:
1369 case SHADOW_ETCHED_IN_DASH:
1370 top_gc = mw->menu.shadow_bottom_gc;
1371 bottom_gc = mw->menu.shadow_top_gc;
1372 top_line_thickness = mw->menu.shadow_thickness/2;
1373 bottom_line_thickness = mw->menu.shadow_thickness - top_line_thickness;
1376 case SHADOW_DOUBLE_ETCHED_OUT:
1378 case SHADOW_ETCHED_OUT:
1379 top_gc = mw->menu.shadow_top_gc;
1380 bottom_gc = mw->menu.shadow_bottom_gc;
1381 top_line_thickness = mw->menu.shadow_thickness/2;
1382 bottom_line_thickness = mw->menu.shadow_thickness - top_line_thickness;
1384 case SHADOW_DOUBLE_ETCHED_IN:
1386 case SHADOW_ETCHED_IN:
1388 top_gc = mw->menu.shadow_bottom_gc;
1389 bottom_gc = mw->menu.shadow_top_gc;
1390 top_line_thickness = mw->menu.shadow_thickness/2;
1391 bottom_line_thickness = mw->menu.shadow_thickness - top_line_thickness;
1398 values.line_style = LineOnOffDash;
1399 if (top_line_thickness > 0)
1400 XChangeGC (dpy, top_gc, GCLineStyle, &values);
1401 if (bottom_line_thickness > 0 && bottom_gc != top_gc)
1402 XChangeGC (dpy, bottom_gc, GCLineStyle, &values);
1405 while (num_separators--)
1408 for (i = 0; i < top_line_thickness; i++)
1409 XDrawLine (dpy, window, top_gc, x, y + i, x + width, y + i);
1411 for (i = 0; i < bottom_line_thickness; i++)
1412 XDrawLine (dpy, window, bottom_gc,
1413 x, y + top_line_thickness + offset + i,
1414 x + width, y + top_line_thickness + offset + i);
1415 y += (top_line_thickness + offset + bottom_line_thickness + 1);
1421 values.line_style = LineSolid;
1422 if (top_line_thickness > 0)
1423 XChangeGC (dpy, top_gc, GCLineStyle, &values);
1424 if (bottom_line_thickness > 0 && bottom_gc != top_gc)
1425 XChangeGC (dpy, bottom_gc, GCLineStyle, &values);
1429 #define SLOPPY_TYPES 0 /* 0=off, 1=error check, 2=easy to please */
1431 #if SLOPPY_TYPES < 2
1433 static char *wv_types[] =
1447 print_widget_value (widget_value *wv, int just_one, int depth)
1451 for (i = 0; i < depth; i++)
1456 printf ("%s(null widget value pointer)\n", d);
1459 printf ("%stype: %s\n", d, wv_types [wv->type]);
1461 printf ("%sname: %s\n", d, (wv->name ? wv->name : "(null)"));
1463 if (wv->name) printf ("%sname: %s\n", d, wv->name);
1465 if (wv->value) printf ("%svalue: %s\n", d, wv->value);
1466 if (wv->key) printf ("%skey: %s\n", d, wv->key);
1467 printf ("%senabled: %d\n", d, wv->enabled);
1470 printf ("\n%scontents: \n", d);
1471 print_widget_value (wv->contents, 0, depth + 5);
1473 if (!just_one && wv->next)
1476 print_widget_value (wv->next, 0, depth);
1479 #endif /* SLOPPY_TYPES < 2 */
1482 all_dashes_p (char *s)
1485 if (!s || s[0] == '\0')
1487 for (p = s; *p == '-'; p++);
1489 if (*p == '!' || *p == '\0')
1493 #endif /* SLOPPY_TYPES */
1495 static widget_value_type
1496 menu_item_type (widget_value *val)
1498 if (val->type != UNSPECIFIED_TYPE)
1501 else if (all_dashes_p (val->name))
1502 return SEPARATOR_TYPE;
1503 else if (val->name && val->name[0] == '\0') /* push right */
1504 return PUSHRIGHT_TYPE;
1505 else if (val->contents) /* cascade */
1506 return CASCADE_TYPE;
1507 else if (val->call_data) /* push button */
1514 return UNSPECIFIED_TYPE; /* Not reached */
1519 label_button_size (XlwMenuWidget mw,
1522 unsigned int *toggle_width,
1523 unsigned int *label_width,
1524 unsigned int *bindings_width,
1525 unsigned int *height)
1527 *height = (mw->menu.font_ascent + mw->menu.font_descent +
1528 2 * mw->menu.vertical_margin +
1529 2 * mw->menu.shadow_thickness);
1530 /* no left column decoration */
1531 *toggle_width = mw->menu.horizontal_margin + mw->menu.shadow_thickness;;
1533 *label_width = string_width_u (mw, resource_widget_value (mw, val));
1534 *bindings_width = mw->menu.horizontal_margin + mw->menu.shadow_thickness;
1538 label_button_draw (XlwMenuWidget mw,
1541 Boolean highlighted,
1545 unsigned int height,
1546 unsigned int label_offset,
1547 unsigned int binding_tab)
1549 int y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin;
1553 label_offset = mw->menu.shadow_thickness + mw->menu.horizontal_margin;
1555 if (highlighted && (in_menubar || val->contents))
1556 gc = mw->menu.highlight_gc;
1557 else if (in_menubar || val->contents)
1558 gc = mw->menu.foreground_gc;
1560 gc = mw->menu.title_gc;
1562 /* Draw the label string. */
1565 x + label_offset, y + y_offset,
1567 resource_widget_value (mw, val));
1571 push_button_size (XlwMenuWidget mw,
1574 unsigned int *toggle_width,
1575 unsigned int *label_width,
1576 unsigned int *bindings_width,
1577 unsigned int *height)
1580 label_button_size (mw, val, in_menubar,
1581 toggle_width, label_width, bindings_width,
1584 /* key bindings to display? */
1585 if (!in_menubar && val->key)
1589 XmString key = XmStringCreateLtoR (val->key, XmSTRING_DEFAULT_CHARSET);
1590 w = string_width (mw, key);
1593 char *key = val->key;
1594 w = string_width (mw, key);
1596 *bindings_width += w + mw->menu.column_spacing;
1601 push_button_draw (XlwMenuWidget mw,
1604 Boolean highlighted,
1608 unsigned int height,
1609 unsigned int label_offset,
1610 unsigned int binding_offset)
1612 int y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin;
1615 Boolean menu_pb = in_menubar && (menu_item_type (val) == BUTTON_TYPE);
1617 /* Draw the label string. */
1619 label_offset = mw->menu.shadow_thickness + mw->menu.horizontal_margin;
1624 gc = mw->menu.highlight_gc;
1626 gc = mw->menu.inactive_gc;
1631 gc = mw->menu.button_gc;
1633 gc = mw->menu.inactive_button_gc;
1638 gc = mw->menu.foreground_gc;
1640 gc = mw->menu.inactive_gc;
1645 x + label_offset, y + y_offset,
1647 resource_widget_value (mw, val));
1649 /* Draw the keybindings */
1652 if (!binding_offset)
1654 unsigned int s_width =
1655 string_width (mw, resource_widget_value (mw, val));
1656 binding_offset = label_offset + s_width + mw->menu.shadow_thickness;
1658 binding_draw (mw, window,
1659 x + binding_offset + mw->menu.column_spacing,
1660 y + y_offset, gc, val->key);
1663 /* Draw the shadow */
1669 type = (val->selected ? SHADOW_ETCHED_OUT : SHADOW_ETCHED_IN);
1676 type = SHADOW_BACKGROUND;
1679 shadow_draw (mw, window, x, y, width, height, type);
1683 arrow_decoration_height (XlwMenuWidget mw)
1685 int result = (mw->menu.font_ascent + mw->menu.font_descent) / 2;
1687 result += 2 * mw->menu.shadow_thickness;
1689 if (result > (mw->menu.font_ascent + mw->menu.font_descent))
1690 result = mw->menu.font_ascent + mw->menu.font_descent;
1696 cascade_button_size (XlwMenuWidget mw,
1699 unsigned int *toggle_width,
1700 unsigned int *label_width,
1701 unsigned int *arrow_width,
1702 unsigned int *height)
1705 label_button_size (mw, val, in_menubar,
1706 toggle_width, label_width, arrow_width,
1708 /* we have a pull aside arrow */
1711 *arrow_width += arrow_decoration_height (mw) + mw->menu.column_spacing;
1716 cascade_button_draw (XlwMenuWidget mw,
1719 Boolean highlighted,
1723 unsigned int height,
1724 unsigned int label_offset,
1725 unsigned int binding_offset)
1729 /* Draw the label string. */
1730 label_button_draw (mw, val, in_menubar, highlighted,
1731 window, x, y, width, height, label_offset,
1734 /* Draw the pull aside arrow */
1735 if (!in_menubar && val->contents)
1738 unsigned int arrow_height = arrow_decoration_height (mw);
1740 y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin +
1741 (mw->menu.font_ascent+mw->menu.font_descent - arrow_height)/2;
1743 if (!binding_offset)
1745 unsigned int s_width =
1746 string_width (mw, resource_widget_value (mw, val));
1749 label_offset = mw->menu.shadow_thickness +
1750 mw->menu.horizontal_margin;
1752 binding_offset = label_offset + s_width + mw->menu.shadow_thickness;
1755 arrow_decoration_draw (mw,
1757 x + binding_offset + mw->menu.column_spacing,
1763 /* Draw the shadow */
1767 type = SHADOW_BACKGROUND;
1769 shadow_draw (mw, window, x, y, width, height, type);
1773 toggle_decoration_height (XlwMenuWidget mw)
1776 if (mw->menu.indicator_size > 0)
1777 rv = mw->menu.indicator_size;
1779 rv = mw->menu.font_ascent;
1781 if (rv > (mw->menu.font_ascent + mw->menu.font_descent))
1782 rv = mw->menu.font_ascent + mw->menu.font_descent;
1784 /* radio button can't be smaller than its border or a filling
1785 error will occur. */
1786 if (rv < 2 * mw->menu.shadow_thickness)
1787 rv = 2 * mw->menu.shadow_thickness;
1793 toggle_button_size (XlwMenuWidget mw,
1796 unsigned int *toggle_width,
1797 unsigned int *label_width,
1798 unsigned int *bindings_width,
1799 unsigned int *height)
1802 push_button_size (mw, val, in_menubar,
1803 toggle_width, label_width, bindings_width,
1805 /* we have a toggle */
1806 *toggle_width += toggle_decoration_height (mw) + mw->menu.column_spacing;
1810 toggle_button_draw (XlwMenuWidget mw,
1813 Boolean highlighted,
1817 unsigned int height,
1818 unsigned int label_tab,
1819 unsigned int binding_tab)
1823 unsigned int t_height = toggle_decoration_height (mw);
1825 /* Draw a toggle. */
1826 x_offset = mw->menu.shadow_thickness + mw->menu.horizontal_margin;
1827 y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin;
1828 y_offset += (mw->menu.font_ascent + mw->menu.font_descent - t_height)/2;
1830 toggle_decoration_draw (mw, window, x + x_offset, y + y_offset,
1831 t_height, val->selected);
1833 /* Draw the pushbutton parts. */
1834 push_button_draw (mw, val, in_menubar, highlighted, window, x, y, width,
1835 height, label_tab, binding_tab);
1839 radio_decoration_height (XlwMenuWidget mw)
1841 return toggle_decoration_height (mw);
1845 radio_button_draw (XlwMenuWidget mw,
1848 Boolean highlighted,
1852 unsigned int height,
1853 unsigned int label_tab,
1854 unsigned int binding_tab)
1858 unsigned int r_height = radio_decoration_height (mw);
1860 /* Draw a toggle. */
1861 x_offset = mw->menu.shadow_thickness + mw->menu.horizontal_margin;
1862 y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin;
1863 y_offset += (mw->menu.font_ascent + mw->menu.font_descent - r_height)/2;
1865 radio_decoration_draw (mw, window, x + x_offset, y + y_offset, r_height,
1868 /* Draw the pushbutton parts. */
1869 push_button_draw (mw, val, in_menubar, highlighted, window, x, y, width,
1870 height, label_tab, binding_tab);
1873 static struct _shadow_names
1880 { "singleLine", SHADOW_SINGLE_LINE },
1881 { "doubleLine", SHADOW_DOUBLE_LINE },
1882 { "singleDashedLine", SHADOW_SINGLE_DASHED_LINE },
1883 { "doubleDashedLine", SHADOW_DOUBLE_DASHED_LINE },
1884 { "noLine", SHADOW_NO_LINE },
1885 { "shadowEtchedIn", SHADOW_ETCHED_IN },
1886 { "shadowEtchedOut", SHADOW_ETCHED_OUT },
1887 { "shadowEtchedInDash", SHADOW_ETCHED_IN_DASH },
1888 { "shadowEtchedOutDash", SHADOW_ETCHED_OUT_DASH },
1890 { "shadowDoubleEtchedIn", SHADOW_DOUBLE_ETCHED_IN },
1891 { "shadowDoubleEtchedOut", SHADOW_DOUBLE_ETCHED_OUT },
1892 { "shadowDoubleEtchedInDash", SHADOW_DOUBLE_ETCHED_IN_DASH },
1893 { "shadowDoubleEtchedOutDash", SHADOW_DOUBLE_ETCHED_OUT_DASH }
1897 separator_type (char *name)
1902 for (i = 0; i < (int) (XtNumber (shadow_names)); i++ )
1904 if (strcmp (name, shadow_names[i].name) == 0)
1905 return shadow_names[i].type;
1908 return SHADOW_BACKGROUND;
1912 separator_decoration_height (XlwMenuWidget mw, widget_value *val)
1915 switch (separator_type (val->value))
1917 case SHADOW_NO_LINE:
1918 case SHADOW_SINGLE_LINE:
1919 case SHADOW_SINGLE_DASHED_LINE:
1921 case SHADOW_DOUBLE_LINE:
1922 case SHADOW_DOUBLE_DASHED_LINE:
1924 case SHADOW_DOUBLE_ETCHED_OUT:
1925 case SHADOW_DOUBLE_ETCHED_IN:
1926 case SHADOW_DOUBLE_ETCHED_OUT_DASH:
1927 case SHADOW_DOUBLE_ETCHED_IN_DASH:
1928 return (1 + 2 * mw->menu.shadow_thickness);
1929 case SHADOW_ETCHED_OUT:
1930 case SHADOW_ETCHED_IN:
1932 return mw->menu.shadow_thickness;
1937 separator_size (XlwMenuWidget mw,
1940 unsigned int *toggle_width,
1941 unsigned int *label_width,
1942 unsigned int *rest_width,
1943 unsigned int *height)
1945 *height = separator_decoration_height (mw, val);
1947 *toggle_width = *rest_width = 0;
1951 separator_draw (XlwMenuWidget mw,
1954 Boolean highlighted,
1958 unsigned int height,
1959 unsigned int label_tab,
1960 unsigned int binding_tab)
1962 unsigned int sep_width;
1969 separator_decoration_draw (mw,
1975 separator_type(val->value));
1979 pushright_size (XlwMenuWidget mw,
1982 unsigned int *toggle_width,
1983 unsigned int *label_width,
1984 unsigned int *rest_width,
1985 unsigned int *height)
1987 *height = *label_width = *toggle_width = *rest_width = 0;
1991 size_menu_item (XlwMenuWidget mw,
1994 unsigned int *toggle_width,
1995 unsigned int *label_width,
1996 unsigned int *rest_width,
1997 unsigned int *height)
1999 void (*function_ptr) (XlwMenuWidget _mw,
2001 Boolean _in_menubar,
2002 unsigned int *_toggle_width,
2003 unsigned int *_label_width,
2004 unsigned int *_rest_width,
2005 unsigned int *_height);
2007 switch (menu_item_type (val))
2011 function_ptr = toggle_button_size;
2013 case SEPARATOR_TYPE:
2014 function_ptr = separator_size;
2016 case INCREMENTAL_TYPE:
2018 function_ptr = cascade_button_size;
2021 function_ptr = push_button_size;
2023 case PUSHRIGHT_TYPE:
2024 function_ptr = pushright_size;
2028 function_ptr = label_button_size;
2032 (*function_ptr) (mw,
2042 display_menu_item (XlwMenuWidget mw,
2046 Boolean highlighted,
2048 Boolean just_compute)
2051 int x = where->x /* + mw->menu.shadow_thickness */ ;
2052 int y = where->y /* + mw->menu.shadow_thickness */ ;
2053 unsigned int toggle_width;
2054 unsigned int label_width;
2055 unsigned int binding_width;
2057 unsigned int height;
2058 unsigned int label_tab;
2059 unsigned int binding_tab;
2060 void (*function_ptr) (XlwMenuWidget _mw,
2062 Boolean _in_menubar,
2063 Boolean _highlighted,
2066 unsigned int _width,
2067 unsigned int _height,
2068 unsigned int _label_tab,
2069 unsigned int _binding_tab);
2071 size_menu_item (mw, val, horizontal,
2072 &toggle_width, &label_width, &binding_width, &height);
2076 width = toggle_width + label_width + binding_width;
2077 height = ws->height - 2 * mw->menu.shadow_thickness;
2081 width = ws->width - 2 * mw->menu.shadow_thickness;
2082 toggle_width = ws->toggle_width;
2083 label_width = ws->label_width;
2092 label_tab = toggle_width;
2093 binding_tab = toggle_width + label_width;
2095 switch (menu_item_type (val))
2098 function_ptr = toggle_button_draw;
2101 function_ptr = radio_button_draw;
2103 case SEPARATOR_TYPE:
2104 function_ptr = separator_draw;
2106 case INCREMENTAL_TYPE:
2108 function_ptr = cascade_button_draw;
2111 function_ptr = push_button_draw;
2114 function_ptr = label_button_draw;
2116 default: /* do no drawing */
2120 (*function_ptr) (mw,
2132 size_menu (XlwMenuWidget mw, int level)
2134 unsigned int toggle_width;
2135 unsigned int label_width;
2136 unsigned int rest_width;
2137 unsigned int height;
2138 unsigned int max_toggle_width = 0;
2139 unsigned int max_label_width = 0;
2140 unsigned int max_rest_width = 0;
2141 unsigned int max_height = 0;
2142 int horizontal_p = mw->menu.horizontal && (level == 0);
2146 if (level >= mw->menu.old_depth)
2149 ws = &mw->menu.windows [level];
2151 for (val = mw->menu.old_stack [level]->contents; val; val = val->next)
2162 max_label_width += toggle_width + label_width + rest_width;
2163 if (height > max_height)
2164 max_height = height;
2168 if (max_toggle_width < toggle_width)
2169 max_toggle_width = toggle_width;
2170 if (max_label_width < label_width)
2171 max_label_width = label_width;
2172 if (max_rest_width < rest_width)
2173 max_rest_width = rest_width;
2174 max_height += height;
2178 ws->height = max_height;
2179 ws->width = max_label_width + max_rest_width + max_toggle_width;
2180 ws->toggle_width = max_toggle_width;
2181 ws->label_width = max_label_width;
2183 ws->width += 2 * mw->menu.shadow_thickness;
2184 ws->height += 2 * mw->menu.shadow_thickness;
2188 display_menu (XlwMenuWidget mw, int level, Boolean just_compute_p,
2189 XPoint *highlighted_pos, XPoint *hit, widget_value **hit_return,
2190 widget_value *this, widget_value *that)
2193 widget_value *following_item;
2196 int horizontal_p = mw->menu.horizontal && (level == 0);
2198 int just_compute_this_one_p;
2200 if (level >= mw->menu.old_depth)
2203 if (level < mw->menu.old_depth - 1)
2204 following_item = mw->menu.old_stack [level + 1];
2207 if (lw_menu_accelerate
2208 && level == mw->menu.old_depth - 1
2209 && mw->menu.old_stack [level]->type == CASCADE_TYPE)
2210 just_compute_p = True;
2211 following_item = NULL;
2214 #if SLOPPY_TYPES == 1
2215 puts("===================================================================");
2216 print_widget_value (following_item, 1, 0);
2222 where.x = mw->menu.shadow_thickness;
2223 where.y = mw->menu.shadow_thickness;
2225 ws = &mw->menu.windows [level];
2226 for (val = mw->menu.old_stack [level]->contents; val; val = val->next)
2230 highlighted_p = (val == following_item);
2231 /* If this is the partition (the dummy item which says that menus
2232 after this should be flushright) then figure out how big the
2233 following items are. This means we walk down the tail of the
2234 list twice, but that's no big deal - it's short.
2236 if (horizontal_p && (menu_item_type (val) == PUSHRIGHT_TYPE))
2239 XPoint flushright_size;
2241 flushright_size.x = 0;
2242 flushright_size.y = 0;
2243 for (rest = val; rest; rest = rest->next)
2244 display_menu_item (mw, rest, ws, &flushright_size,
2245 highlighted_p, horizontal_p, True);
2246 new_x = ws->width - (flushright_size.x + mw->menu.shadow_thickness);
2247 if (new_x > where.x)
2249 /* We know what we need; don't draw this item. */
2253 if (highlighted_p && highlighted_pos)
2256 highlighted_pos->x = where.x;
2258 highlighted_pos->y = where.y;
2261 just_compute_this_one_p =
2262 just_compute_p || ((this || that) && val != this && val != that);
2266 display_menu_item (mw, val, ws, &where, highlighted_p, horizontal_p,
2267 just_compute_this_one_p);
2269 if (highlighted_p && highlighted_pos)
2272 highlighted_pos->y = ws->height;
2274 highlighted_pos->x = ws->width;
2277 if (hit && !*hit_return)
2279 if (horizontal_p && hit->x > start.x && hit->x <= where.x)
2281 else if (!horizontal_p && hit->y > start.y && hit->y <= where.y)
2286 where.y = mw->menu.shadow_thickness;
2288 where.x = mw->menu.shadow_thickness;
2291 /* Draw slab edges around menu */
2292 if (!just_compute_p)
2293 shadow_draw(mw, ws->window, 0, 0, ws->width, ws->height, SHADOW_OUT);
2298 set_new_state (XlwMenuWidget mw, widget_value *val, int level)
2302 mw->menu.new_depth = 0;
2303 for (i = 0; i < level; i++)
2304 push_new_stack (mw, mw->menu.old_stack [i]);
2306 push_new_stack (mw, val);
2310 make_windows_if_needed (XlwMenuWidget mw, int n)
2314 XSetWindowAttributes xswa;
2319 window_state *windows;
2322 if (mw->menu.windows_length >= n)
2325 root = RootWindowOfScreen (XtScreen(mw));
2326 /* grab the visual and depth from the nearest shell ancestor */
2327 visual = CopyFromParent;
2328 depth = CopyFromParent;
2330 while (visual == CopyFromParent && p)
2334 visual = ((ShellWidget)p)->shell.visual;
2335 depth = p->core.depth;
2340 xswa.save_under = True;
2341 xswa.override_redirect = True;
2342 xswa.background_pixel = mw->core.background_pixel;
2343 xswa.border_pixel = mw->core.border_pixel;
2344 xswa.event_mask = (ExposureMask | ButtonMotionMask
2345 | ButtonReleaseMask | ButtonPressMask);
2346 xswa.cursor = mw->menu.cursor_shape;
2347 xswa.colormap = mw->core.colormap;
2348 mask = CWSaveUnder | CWOverrideRedirect | CWBackPixel | CWBorderPixel
2349 | CWEventMask | CWCursor | CWColormap;
2351 if (mw->menu.use_backing_store)
2353 xswa.backing_store = Always;
2354 mask |= CWBackingStore;
2357 if (!mw->menu.windows)
2360 (window_state *) XtMalloc (n * sizeof (window_state));
2366 (window_state *) XtRealloc ((char *) mw->menu.windows,
2367 n * sizeof (window_state));
2368 start_at = mw->menu.windows_length;
2370 mw->menu.windows_length = n;
2372 windows = mw->menu.windows;
2374 for (i = start_at; i < n; i++)
2378 windows [i].width = 1;
2379 windows [i].height = 1;
2380 windows [i].window =
2381 XCreateWindow (XtDisplay (mw),
2384 0, depth, CopyFromParent, visual, mask, &xswa);
2388 /* Make the window fit in the screen */
2390 fit_to_screen (XlwMenuWidget mw, window_state *ws, window_state *previous_ws,
2391 Boolean horizontal_p)
2393 int screen_width = WidthOfScreen (XtScreen (mw));
2394 int screen_height = HeightOfScreen (XtScreen (mw));
2398 else if ((int) (ws->x + ws->width) > screen_width)
2401 ws->x = previous_ws->x - ws->width;
2404 ws->x = screen_width - ws->width;
2406 /* This check is to make sure we cut off the right side
2407 instead of the left side if the menu is wider than the
2415 else if ((int) (ws->y + ws->height) > screen_height)
2419 /* A pulldown must either be entirely above or below the menubar.
2420 If we're here, the pulldown doesn't fit below the menubar, so
2421 let's determine if it will fit above the menubar.
2422 Only put it above if there is more room above than below.
2423 Note shadow_thickness offset to allow for slab surround.
2425 if (ws->y > (screen_height / 2))
2426 ws->y = previous_ws->y - ws->height + mw->menu.shadow_thickness;
2430 ws->y = screen_height - ws->height;
2431 /* if it's taller than the screen, display the topmost part
2432 that will fit, beginning at the top of the screen. */
2439 /* Updates old_stack from new_stack and redisplays. */
2441 remap_menubar (XlwMenuWidget mw)
2445 XPoint selection_position;
2446 int old_depth = mw->menu.old_depth;
2447 int new_depth = mw->menu.new_depth;
2448 widget_value **old_stack;
2449 widget_value **new_stack;
2450 window_state *windows;
2451 widget_value *old_selection;
2452 widget_value *new_selection;
2454 /* Check that enough windows and old_stack are ready. */
2455 make_windows_if_needed (mw, new_depth);
2456 make_old_stack_space (mw, new_depth);
2457 windows = mw->menu.windows;
2458 old_stack = mw->menu.old_stack;
2459 new_stack = mw->menu.new_stack;
2461 /* compute the last identical different entry */
2462 for (i = 1; i < old_depth && i < new_depth; i++)
2463 if (old_stack [i] != new_stack [i])
2467 if (lw_menu_accelerate
2469 && last_same == old_depth - 1
2470 && old_stack [last_same]->contents)
2473 /* Memorize the previously selected item to be able to refresh it */
2474 old_selection = last_same + 1 < old_depth ? old_stack [last_same + 1] : NULL;
2475 new_selection = last_same + 1 < new_depth ? new_stack [last_same + 1] : NULL;
2477 /* updates old_state from new_state. It has to be done now because
2478 display_menu (called below) uses the old_stack to know what to display. */
2479 for (i = last_same + 1; i < new_depth; i++)
2480 old_stack [i] = new_stack [i];
2482 mw->menu.old_depth = new_depth;
2484 /* refresh the last seletion */
2485 selection_position.x = 0;
2486 selection_position.y = 0;
2487 display_menu (mw, last_same, new_selection == old_selection,
2488 &selection_position, NULL, NULL, old_selection, new_selection);
2490 /* Now popup the new menus */
2491 for (i = last_same + 1; i < new_depth && new_stack [i]->contents; i++)
2493 window_state *previous_ws = &windows [i - 1];
2494 window_state *ws = &windows [i];
2496 if (lw_menu_accelerate && i == new_depth - 1)
2499 ws->x = previous_ws->x + selection_position.x;
2500 ws->y = previous_ws->y + selection_position.y;
2502 /* take into account the slab around the new menu */
2503 ws->y -= mw->menu.shadow_thickness;
2506 widget_value *val = mw->menu.old_stack [i];
2507 if (val->contents->type == INCREMENTAL_TYPE)
2509 /* okay, we're now doing a lisp callback to incrementally generate
2510 more of the menu. */
2511 XtCallCallbackList ((Widget)mw,
2513 (XtPointer)val->contents);
2519 fit_to_screen (mw, ws, previous_ws, mw->menu.horizontal && i == 1);
2521 XClearWindow (XtDisplay (mw), ws->window);
2522 XMoveResizeWindow (XtDisplay (mw), ws->window, ws->x, ws->y,
2523 ws->width, ws->height);
2524 XMapRaised (XtDisplay (mw), ws->window);
2525 display_menu (mw, i, False, &selection_position, NULL, NULL, NULL, NULL);
2528 /* unmap the menus that popped down */
2530 last_same = new_depth;
2531 if (lw_menu_accelerate
2533 && new_stack [last_same - 1]->contents)
2536 for (i = last_same - 1; i < old_depth; i++)
2537 if (i >= last_same || !new_stack [i]->contents)
2538 XUnmapWindow (XtDisplay (mw), windows [i].window);
2542 motion_event_is_in_menu (XlwMenuWidget mw, XMotionEvent *ev, int level,
2543 XPoint *relative_pos)
2545 window_state *ws = &mw->menu.windows [level];
2546 int x = level == 0 ? ws->x : ws->x + mw->menu.shadow_thickness;
2547 int y = level == 0 ? ws->y : ws->y + mw->menu.shadow_thickness;
2548 relative_pos->x = ev->x_root - x;
2549 relative_pos->y = ev->y_root - y;
2550 return (x < ev->x_root && ev->x_root < (int) (x + ws->width) &&
2551 y < ev->y_root && ev->y_root < (int) (y + ws->height));
2555 map_event_to_widget_value (XlwMenuWidget mw, XMotionEvent *ev,
2556 widget_value **val_ptr, int *level,
2557 Boolean *inside_menu)
2560 XPoint relative_pos;
2564 *inside_menu = False;
2566 /* Find the window */
2568 for (i = mw->menu.old_depth - 1; i >= 0; i--)
2570 for (i = 0; i <= mw->menu.old_depth - 1; i++)
2573 ws = &mw->menu.windows [i];
2574 if (ws && motion_event_is_in_menu (mw, ev, i, &relative_pos))
2576 *inside_menu = True; /* special logic for menubar below... */
2577 if ((ev->type == ButtonPress) ||
2580 display_menu (mw, i, True, NULL, &relative_pos,
2581 val_ptr, NULL, NULL);
2585 *inside_menu = True;
2588 else if (mw->menu.horizontal || i == 0)
2590 /* if we're clicking on empty part of the menubar, then
2591 unpost the stay-up menu */
2592 *inside_menu = False;
2602 make_drawing_gcs (XlwMenuWidget mw)
2605 unsigned long flags = (GCFont | GCForeground | GCBackground);
2608 xgcv.font = default_font_of_font_list (mw->menu.font_list)->fid;
2610 xgcv.font = mw->menu.font->fid;
2613 xgcv.foreground = mw->core.background_pixel;
2614 xgcv.background = mw->menu.foreground;
2615 mw->menu.background_gc = XtGetGC ((Widget) mw, flags, &xgcv);
2617 xgcv.foreground = mw->menu.foreground;
2618 xgcv.background = mw->core.background_pixel;
2619 mw->menu.foreground_gc = XtGetGC ((Widget) mw, flags, &xgcv);
2621 if (mw->menu.select_color != (Pixel)-1)
2623 xgcv.foreground = mw->menu.select_color;
2627 Display *dpy = XtDisplay(mw);
2628 if (CellsOfScreen(DefaultScreenOfDisplay(dpy)) <= 2)
2630 xgcv.foreground = mw->menu.foreground;
2635 Colormap cmap = mw->core.colormap;
2636 xcolor.pixel = mw->core.background_pixel;
2637 XQueryColor (dpy, cmap, &xcolor);
2638 xcolor.red = (xcolor.red * 17) / 20;
2639 xcolor.green = (xcolor.green * 17) / 20;
2640 xcolor.blue = (xcolor.blue * 17) / 20;
2641 if (allocate_nearest_color (dpy, cmap, &xcolor))
2642 xgcv.foreground = xcolor.pixel;
2645 xgcv.background = mw->core.background_pixel;
2646 mw->menu.select_gc = XtGetGC ((Widget)mw, flags, &xgcv);
2648 xgcv.foreground = mw->menu.foreground;
2649 xgcv.background = mw->core.background_pixel;
2650 xgcv.fill_style = FillStippled;
2651 xgcv.stipple = mw->menu.gray_pixmap;
2652 mw->menu.inactive_gc = XtGetGC ((Widget)mw,
2653 (flags | GCFillStyle | GCStipple),
2656 xgcv.foreground = mw->menu.highlight_foreground;
2657 xgcv.background = mw->core.background_pixel;
2658 mw->menu.highlight_gc = XtGetGC ((Widget)mw, flags, &xgcv);
2660 xgcv.foreground = mw->menu.title_foreground;
2661 xgcv.background = mw->core.background_pixel;
2662 mw->menu.title_gc = XtGetGC ((Widget)mw, flags, &xgcv);
2664 xgcv.foreground = mw->menu.button_foreground;
2665 xgcv.background = mw->core.background_pixel;
2666 mw->menu.button_gc = XtGetGC ((Widget)mw, flags, &xgcv);
2668 xgcv.fill_style = FillStippled;
2669 xgcv.stipple = mw->menu.gray_pixmap;
2670 mw->menu.inactive_button_gc = XtGetGC ((Widget)mw,
2671 (flags | GCFillStyle | GCStipple),
2676 release_drawing_gcs (XlwMenuWidget mw)
2678 XtReleaseGC ((Widget) mw, mw->menu.foreground_gc);
2679 XtReleaseGC ((Widget) mw, mw->menu.button_gc);
2680 XtReleaseGC ((Widget) mw, mw->menu.highlight_gc);
2681 XtReleaseGC ((Widget) mw, mw->menu.title_gc);
2682 XtReleaseGC ((Widget) mw, mw->menu.inactive_gc);
2683 XtReleaseGC ((Widget) mw, mw->menu.inactive_button_gc);
2684 XtReleaseGC ((Widget) mw, mw->menu.background_gc);
2685 XtReleaseGC ((Widget) mw, mw->menu.select_gc);
2686 /* let's get some segvs if we try to use these... */
2687 mw->menu.foreground_gc = (GC) -1;
2688 mw->menu.button_gc = (GC) -1;
2689 mw->menu.highlight_gc = (GC) -1;
2690 mw->menu.title_gc = (GC) -1;
2691 mw->menu.inactive_gc = (GC) -1;
2692 mw->menu.inactive_button_gc = (GC) -1;
2693 mw->menu.background_gc = (GC) -1;
2694 mw->menu.select_gc = (GC) -1;
2697 #define MINL(x,y) ((((unsigned long) (x)) < ((unsigned long) (y))) \
2698 ? ((unsigned long) (x)) : ((unsigned long) (y)))
2701 make_shadow_gcs (XlwMenuWidget mw)
2704 unsigned long pm = 0;
2705 Display *dpy = XtDisplay ((Widget) mw);
2706 Colormap cmap = mw->core.colormap;
2708 int top_frobbed = 0, bottom_frobbed = 0;
2710 if (mw->menu.top_shadow_color == (Pixel) (-1))
2711 mw->menu.top_shadow_color = mw->core.background_pixel;
2712 if (mw->menu.bottom_shadow_color == (Pixel) (-1))
2713 mw->menu.bottom_shadow_color = mw->menu.foreground;
2715 if (mw->menu.top_shadow_color == mw->core.background_pixel ||
2716 mw->menu.top_shadow_color == mw->menu.foreground)
2718 topc.pixel = mw->core.background_pixel;
2719 XQueryColor (dpy, cmap, &topc);
2720 /* don't overflow/wrap! */
2721 topc.red = MINL (65535, topc.red * 1.2);
2722 topc.green = MINL (65535, topc.green * 1.2);
2723 topc.blue = MINL (65535, topc.blue * 1.2);
2724 if (allocate_nearest_color (dpy, cmap, &topc))
2726 if (topc.pixel == mw->core.background_pixel)
2728 XFreeColors( dpy, cmap, &topc.pixel, 1, 0);
2729 topc.red = MINL (65535, topc.red + 0x8000);
2730 topc.green = MINL (65535, topc.green + 0x8000);
2731 topc.blue = MINL (65535, topc.blue + 0x8000);
2732 if (allocate_nearest_color (dpy, cmap, &topc))
2734 mw->menu.top_shadow_color = topc.pixel;
2739 mw->menu.top_shadow_color = topc.pixel;
2745 if (mw->menu.bottom_shadow_color == mw->menu.foreground ||
2746 mw->menu.bottom_shadow_color == mw->core.background_pixel)
2748 botc.pixel = mw->core.background_pixel;
2749 XQueryColor (dpy, cmap, &botc);
2750 botc.red = (botc.red * 3) / 5;
2751 botc.green = (botc.green * 3) / 5;
2752 botc.blue = (botc.blue * 3) / 5;
2753 if (allocate_nearest_color (dpy, cmap, &botc))
2755 if (botc.pixel == mw->core.background_pixel)
2757 XFreeColors (dpy, cmap, &botc.pixel, 1, 0);
2758 botc.red = MINL (65535, botc.red + 0x4000);
2759 botc.green = MINL (65535, botc.green + 0x4000);
2760 botc.blue = MINL (65535, botc.blue + 0x4000);
2761 if (allocate_nearest_color (dpy, cmap, &botc))
2763 mw->menu.bottom_shadow_color = botc.pixel;
2768 mw->menu.bottom_shadow_color = botc.pixel;
2775 if (top_frobbed && bottom_frobbed)
2777 int top_avg = ((topc.red / 3) + (topc.green / 3) + (topc.blue / 3));
2778 int bot_avg = ((botc.red / 3) + (botc.green / 3) + (botc.blue / 3));
2779 if (bot_avg > top_avg)
2781 Pixel tmp = mw->menu.top_shadow_color;
2782 mw->menu.top_shadow_color = mw->menu.bottom_shadow_color;
2783 mw->menu.bottom_shadow_color = tmp;
2785 else if (topc.pixel == botc.pixel)
2787 if (botc.pixel == mw->menu.foreground)
2788 mw->menu.top_shadow_color = mw->core.background_pixel;
2790 mw->menu.bottom_shadow_color = mw->menu.foreground;
2794 if (!mw->menu.top_shadow_pixmap &&
2795 mw->menu.top_shadow_color == mw->core.background_pixel)
2797 mw->menu.top_shadow_pixmap = mw->menu.gray_pixmap;
2798 mw->menu.top_shadow_color = mw->menu.foreground;
2800 if (!mw->menu.bottom_shadow_pixmap &&
2801 mw->menu.bottom_shadow_color == mw->core.background_pixel)
2803 mw->menu.bottom_shadow_pixmap = mw->menu.gray_pixmap;
2804 mw->menu.bottom_shadow_color = mw->menu.foreground;
2807 xgcv.fill_style = FillOpaqueStippled;
2808 xgcv.foreground = mw->menu.top_shadow_color;
2809 xgcv.background = mw->core.background_pixel;
2810 /* 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;
2818 xgcv.stipple = mw->menu.top_shadow_pixmap;
2819 #endif /* NEED_MOTIF */
2820 pm = (xgcv.stipple ? GCStipple|GCFillStyle : 0);
2821 mw->menu.shadow_top_gc =
2822 XtGetGC((Widget)mw, GCForeground|GCBackground|pm, &xgcv);
2824 xgcv.foreground = mw->menu.bottom_shadow_color;
2825 /* xgcv.stipple = mw->menu.bottom_shadow_pixmap; gtb */
2827 if (mw->menu.top_shadow_pixmap &&
2828 mw->menu.top_shadow_pixmap != XmUNSPECIFIED_PIXMAP)
2829 xgcv.stipple = mw->menu.bottom_shadow_pixmap;
2833 xgcv.stipple = mw->menu.bottom_shadow_pixmap;
2834 #endif /* NEED_MOTIF */
2835 pm = (xgcv.stipple ? GCStipple|GCFillStyle : 0);
2836 mw->menu.shadow_bottom_gc =
2837 XtGetGC ((Widget)mw, GCForeground|GCBackground|pm, &xgcv);
2842 release_shadow_gcs (XlwMenuWidget mw)
2844 XtReleaseGC ((Widget) mw, mw->menu.shadow_top_gc);
2845 XtReleaseGC ((Widget) mw, mw->menu.shadow_bottom_gc);
2850 extract_font_extents (XlwMenuWidget mw)
2853 /* Find the maximal ascent/descent of the fonts in the font list
2854 so that all menu items can be the same height... */
2855 mw->menu.font_ascent = 0;
2856 mw->menu.font_descent = 0;
2859 XmFontContext context;
2860 #if (XmVersion >= 1002)
2861 XmFontListEntry fontentry;
2863 XmStringCharSet charset;
2867 if (! XmFontListInitFontContext (&context, mw->menu.font_list))
2869 #if (XmVersion >= 1002)
2870 /* There is a BUG in the 1.2 version of XmFontListGetNextFont() (or more
2871 specifically, in _XmGetFirstFont()) that can cause a null pointer to be
2872 passed to XFontsOfFontSet. Use XmFontListNextEntry(), which is the
2873 newer equivalent, instead. Also, it supports font sets, and the
2874 older function doesn't. */
2875 while ((fontentry = XmFontListNextEntry (context)))
2879 XtPointer one_of_them = XmFontListEntryGetFont (fontentry, &rettype);
2880 if (rettype == XmFONT_IS_FONTSET)
2882 XFontSet fontset = (XFontSet) one_of_them;
2883 XFontStruct **fontstruct_list;
2884 char **fontname_list;
2885 int fontcount = XFontsOfFontSet (fontset, &fontstruct_list,
2887 while (--fontcount >= 0)
2889 font = fontstruct_list[fontcount];
2890 if (font->ascent > (int) mw->menu.font_ascent)
2891 mw->menu.font_ascent = font->ascent;
2892 if (font->descent > (int) mw->menu.font_descent)
2893 mw->menu.font_descent = font->descent;
2896 else /* XmFONT_IS_FONT */
2898 font = (XFontStruct *) one_of_them;
2899 if (font->ascent > (int) mw->menu.font_ascent)
2900 mw->menu.font_ascent = font->ascent;
2901 if (font->descent > (int) mw->menu.font_descent)
2902 mw->menu.font_descent = font->descent;
2905 #else /* motif 1.1 */
2906 while (XmFontListGetNextFont (context, &charset, &font))
2908 if (font->ascent > (int) mw->menu.font_ascent)
2909 mw->menu.font_ascent = font->ascent;
2910 if (font->descent > (int) mw->menu.font_descent)
2911 mw->menu.font_descent = font->descent;
2914 #endif /* Motif version */
2915 XmFontListFreeFontContext (context);
2917 #else /* Not Motif */
2918 # ifdef USE_XFONTSET
2919 XFontStruct **fontstruct_list;
2920 char **fontname_list;
2922 int fontcount = XFontsOfFontSet(mw->menu.font_set, &fontstruct_list,
2924 mw->menu.font_ascent = 0;
2925 mw->menu.font_descent = 0;
2926 # if 0 /* nasty, personal debug, Kazz */
2927 fprintf(stderr, "fontSet count is %d\n", fontcount);
2929 while (--fontcount >= 0) {
2930 font = fontstruct_list[fontcount];
2931 if (font->ascent > (int) mw->menu.font_ascent)
2932 mw->menu.font_ascent = font->ascent;
2933 if (font->descent > (int) mw->menu.font_descent)
2934 mw->menu.font_descent = font->descent;
2936 # else /* ! USE_XFONTSET */
2937 mw->menu.font_ascent = mw->menu.font->ascent;
2938 mw->menu.font_descent = mw->menu.font->descent;
2940 #endif /* NEED_MOTIF */
2944 static XFontStruct *
2945 default_font_of_font_list (XmFontList font_list)
2947 XFontStruct *font = 0;
2949 /* Xm/Label.c does this: */
2950 _XmFontListGetDefaultFont (font_list, &font);
2953 XmFontContext context;
2954 #if (XmVersion >= 1002)
2955 XmFontListEntry fontentry;
2957 XtPointer one_of_them;
2959 XmStringCharSet charset;
2962 if (! XmFontListInitFontContext (&context, font_list))
2964 #if (XmVersion >= 1002)
2965 /* There is a BUG in the 1.2 version of XmFontListGetNextFont() (or more
2966 specifically, in _XmGetFirstFont()) that can cause a null pointer to be
2967 passed to XFontsOfFontSet. Use XmFontListNextEntry(), which is the
2968 newer equivalent, instead. */
2969 fontentry = XmFontListNextEntry (context);
2970 one_of_them = XmFontListEntryGetFont (fontentry, &rettype);
2971 if (rettype == XmFONT_IS_FONTSET)
2973 XFontSet fontset = (XFontSet) one_of_them;
2974 XFontStruct **fontstruct_list;
2975 char **fontname_list;
2976 (void) XFontsOfFontSet (fontset, &fontstruct_list, &fontname_list);
2977 font = fontstruct_list[0];
2979 else /* XmFONT_IS_FONT */
2981 font = (XFontStruct *) one_of_them;
2984 if (! XmFontListGetNextFont (context, &charset, &font))
2988 XmFontListFreeFontContext (context);
2992 if (! font) abort ();
2995 #endif /* NEED_MOTIF */
2998 XlwMenuInitialize (Widget request, Widget new, ArgList args,
3001 /* Get the GCs and the widget size */
3002 XlwMenuWidget mw = (XlwMenuWidget)new;
3004 XSetWindowAttributes xswa;
3007 Window window = RootWindowOfScreen (DefaultScreenOfDisplay (XtDisplay (mw)));
3008 Display *display = XtDisplay (mw);
3010 /* mw->menu.cursor = XCreateFontCursor (display, mw->menu.cursor_shape); */
3011 mw->menu.cursor = mw->menu.cursor_shape;
3013 mw->menu.gray_pixmap =
3014 XCreatePixmapFromBitmapData (display, window, (char *) gray_bits,
3015 gray_width, gray_height, 1, 0, 1);
3018 /* The menu.font_list slot came from the *fontList resource (Motif standard.)
3019 The menu.font_list_2 slot came from the *font resource, for backward
3020 compatibility with older versions of this code, and consistency with the
3021 rest of emacs. If both font and fontList are specified, we use font.
3022 If only one is specified, we use that. If neither are specified, we
3023 use the "fallback" value. What a kludge!!!
3025 Note that this has the bug that a more general wildcard like "*fontList:"
3026 will override a more specific resource like "Emacs*menubar.font:". But
3027 I can't think of a way around that.
3029 if (mw->menu.font_list) /* if *fontList is specified, use that */
3031 else if (mw->menu.font_list_2) /* else if *font is specified, use that */
3032 mw->menu.font_list = mw->menu.font_list_2;
3033 else /* otherwise use default */
3034 mw->menu.font_list = mw->menu.fallback_font_list;
3037 make_drawing_gcs (mw);
3038 make_shadow_gcs (mw);
3039 extract_font_extents (mw);
3041 xswa.background_pixel = mw->core.background_pixel;
3042 xswa.border_pixel = mw->core.border_pixel;
3043 mask = CWBackPixel | CWBorderPixel;
3045 mw->menu.popped_up = False;
3046 mw->menu.pointer_grabbed = False;
3047 mw->menu.next_release_must_exit = False;
3049 mw->menu.old_depth = 1;
3050 mw->menu.old_stack = XtNew (widget_value*);
3051 mw->menu.old_stack_length = 1;
3052 mw->menu.old_stack [0] = mw->menu.contents;
3054 mw->menu.new_depth = 0;
3055 mw->menu.new_stack = 0;
3056 mw->menu.new_stack_length = 0;
3057 push_new_stack (mw, mw->menu.contents);
3059 mw->menu.windows = XtNew (window_state);
3060 mw->menu.windows_length = 1;
3061 mw->menu.windows [0].x = 0;
3062 mw->menu.windows [0].y = 0;
3063 mw->menu.windows [0].width = 0;
3064 mw->menu.windows [0].height = 0;
3067 mw->core.width = mw->menu.windows [0].width;
3068 mw->core.height = mw->menu.windows [0].height;
3072 XlwMenuClassInitialize (void)
3074 initialize_massaged_resource_char();
3078 XlwMenuRealize (Widget w, Mask *valueMask, XSetWindowAttributes *attributes)
3080 XlwMenuWidget mw = (XlwMenuWidget)w;
3081 XSetWindowAttributes xswa;
3084 (*xlwMenuWidgetClass->core_class.superclass->core_class.realize)
3085 (w, valueMask, attributes);
3087 xswa.save_under = True;
3088 xswa.cursor = mw->menu.cursor_shape;
3089 mask = CWSaveUnder | CWCursor;
3090 if (mw->menu.use_backing_store)
3092 xswa.backing_store = Always;
3093 mask |= CWBackingStore;
3095 XChangeWindowAttributes (XtDisplay (w), XtWindow (w), mask, &xswa);
3097 mw->menu.windows [0].window = XtWindow (w);
3098 mw->menu.windows [0].x = w->core.x;
3099 mw->menu.windows [0].y = w->core.y;
3100 mw->menu.windows [0].width = w->core.width;
3101 mw->menu.windows [0].height = w->core.height;
3104 /* Only the toplevel menubar/popup is a widget so it's the only one that
3105 receives expose events through Xt. So we repaint all the other panes
3106 when receiving an Expose event. */
3108 XlwMenuRedisplay (Widget w, XEvent *ev, Region region)
3110 XlwMenuWidget mw = (XlwMenuWidget)w;
3113 if (mw->core.being_destroyed) return;
3115 for (i = 0; i < mw->menu.old_depth; i++)
3116 display_menu (mw, i, False, NULL, NULL, NULL, NULL, NULL);
3117 set_new_state (mw, NULL, mw->menu.old_depth); /* #### - ??? */
3118 remap_menubar (mw); /* #### - do these two lines do anything? */
3122 XlwMenuDestroy (Widget w)
3125 XlwMenuWidget mw = (XlwMenuWidget) w;
3127 if (mw->menu.pointer_grabbed)
3129 XtUngrabPointer (w, CurrentTime);
3130 mw->menu.pointer_grabbed = False;
3133 release_drawing_gcs (mw);
3134 release_shadow_gcs (mw);
3136 /* this doesn't come from the resource db but is created explicitly
3137 so we must free it ourselves. */
3138 XFreePixmap (XtDisplay (mw), mw->menu.gray_pixmap);
3139 mw->menu.gray_pixmap = (Pixmap) -1;
3141 /* Don't free mw->menu.contents because that comes from our creator.
3142 The `*_stack' elements are just pointers into `contents' so leave
3143 that alone too. But free the stacks themselves. */
3144 if (mw->menu.old_stack) XtFree ((char *) mw->menu.old_stack);
3145 if (mw->menu.new_stack) XtFree ((char *) mw->menu.new_stack);
3147 /* Remember, you can't free anything that came from the resource
3148 database. This includes:
3150 mw->menu.top_shadow_pixmap
3151 mw->menu.bottom_shadow_pixmap
3154 Also the color cells of top_shadow_color, bottom_shadow_color,
3155 foreground, and button_foreground will never be freed until this
3156 client exits. Nice, eh?
3159 /* start from 1 because the one in slot 0 is w->core.window */
3160 for (i = 1; i < mw->menu.windows_length; i++)
3161 XDestroyWindow (XtDisplay (mw), mw->menu.windows [i].window);
3162 if (mw->menu.windows)
3163 XtFree ((char *) mw->menu.windows);
3167 XlwMenuSetValues (Widget current, Widget request, Widget new, ArgList args,
3170 XlwMenuWidget oldmw = (XlwMenuWidget)current;
3171 XlwMenuWidget newmw = (XlwMenuWidget)new;
3172 Boolean redisplay = False;
3175 if (newmw->menu.contents
3176 && newmw->menu.contents->contents
3177 && newmw->menu.contents->contents->change >= VISIBLE_CHANGE)
3180 if (newmw->core.background_pixel != oldmw->core.background_pixel
3181 || newmw->menu.foreground != oldmw->menu.foreground
3182 /* For the XEditResource protocol, which may want to change the font. */
3184 || newmw->menu.font_list != oldmw->menu.font_list
3185 || newmw->menu.font_list_2 != oldmw->menu.font_list_2
3186 || newmw->menu.fallback_font_list != oldmw->menu.fallback_font_list
3188 || newmw->menu.font != oldmw->menu.font
3192 release_drawing_gcs (newmw);
3193 make_drawing_gcs (newmw);
3196 for (i = 0; i < oldmw->menu.windows_length; i++)
3198 XSetWindowBackground (XtDisplay (oldmw),
3199 oldmw->menu.windows [i].window,
3200 newmw->core.background_pixel);
3201 /* clear windows and generate expose events */
3202 XClearArea (XtDisplay (oldmw), oldmw->menu.windows[i].window,
3211 XlwMenuResize (Widget w)
3213 XlwMenuWidget mw = (XlwMenuWidget)w;
3215 mw->menu.windows [0].width = mw->core.width;
3216 mw->menu.windows [0].height = mw->core.height;
3219 \f/* Action procedures */
3221 handle_single_motion_event (XlwMenuWidget mw, XMotionEvent *ev,
3228 if (!map_event_to_widget_value (mw, ev, &val, &level, &stay_up))
3230 /* we wind up here when: (a) the event is in the menubar, (b) the
3231 event isn't in the menubar or any of the panes, (c) the event is on
3232 a disabled menu item */
3233 pop_new_stack_if_no_contents (mw);
3234 if (select_p && !stay_up) {
3235 /* pop down all menus and exit */
3236 mw->menu.next_release_must_exit = True;
3237 set_new_state(mw, (val = NULL), 1);
3242 /* we wind up here when: (a) the event pops up a pull_right menu,
3243 (b) a menu item that is not disabled is highlighted */
3244 if (select_p && mw->menu.bounce_down
3245 && close_to_reference_time((Widget)mw,
3246 mw->menu.menu_bounce_time,
3249 /* motion can cause more than one event. Don't bounce right back
3250 up if we've just bounced down. */
3253 else if (select_p && mw->menu.bounce_down &&
3254 mw->menu.last_selected_val &&
3255 (mw->menu.last_selected_val == val))
3257 val = NULL; /* assigned to mw->last_selected_val below */
3258 mw->menu.menu_bounce_time = ev->time;
3259 /* popdown last menu if we're selecting the same menu item as we did
3260 last time and the XlwMenu.bounceDown resource is set, if the
3261 item is on the menubar itself, then exit. */
3262 if (level == (mw->menu.popped_up ? 0 : 1))
3263 mw->menu.next_release_must_exit = True;
3266 mw->menu.menu_bounce_time = 0;
3267 set_new_state (mw, val, level);
3269 mw->menu.last_selected_val = val;
3272 /* Sync with the display. Makes it feel better on X terms. */
3273 XFlush (XtDisplay (mw));
3277 handle_motion_event (XlwMenuWidget mw, XMotionEvent *ev,
3282 unsigned int state = ev->state;
3283 XMotionEvent *event= ev, dummy;
3285 /* allow motion events to be generated again */
3286 dummy.window = ev->window;
3288 && XQueryPointer (XtDisplay (mw), dummy.window,
3289 &dummy.root, &dummy.subwindow,
3290 &dummy.x_root, &dummy.y_root,
3293 && dummy.state == state
3294 && (dummy.x_root != x || dummy.y_root != y))
3296 /* don't handle the event twice or that breaks bounce_down. --Stig */
3297 dummy.type = ev->type;
3301 lw_menu_accelerate = False;
3302 handle_single_motion_event (mw, event, select_p);
3305 Time x_focus_timestamp_really_sucks_fix_me_better;
3308 Start (Widget w, XEvent *ev, String *params, Cardinal *num_params)
3310 XlwMenuWidget mw = (XlwMenuWidget)w;
3312 lw_menubar_widget = w;
3314 lw_menu_active = True;
3316 if (!mw->menu.pointer_grabbed)
3318 mw->menu.menu_post_time = ev->xbutton.time;
3319 mw->menu.menu_bounce_time = 0;
3320 mw->menu.next_release_must_exit = True;
3321 mw->menu.last_selected_val = NULL;
3322 x_focus_timestamp_really_sucks_fix_me_better =
3323 ((XButtonPressedEvent*)ev)->time;
3324 XtCallCallbackList ((Widget)mw, mw->menu.open, NULL);
3326 /* notes the absolute position of the menubar window */
3327 mw->menu.windows [0].x = ev->xmotion.x_root - ev->xmotion.x;
3328 mw->menu.windows [0].y = ev->xmotion.y_root - ev->xmotion.y;
3330 XtGrabPointer ((Widget)mw, False,
3331 (ButtonMotionMask | ButtonReleaseMask | ButtonPressMask),
3332 GrabModeAsync, GrabModeAsync,
3333 None, mw->menu.cursor_shape,
3334 ((XButtonPressedEvent*)ev)->time);
3335 mw->menu.pointer_grabbed = True;
3338 /* handles the down like a move, slots are mostly compatible */
3339 handle_motion_event (mw, &ev->xmotion, True);
3343 Drag (Widget w, XEvent *ev, String *params, Cardinal *num_params)
3345 XlwMenuWidget mw = (XlwMenuWidget)w;
3346 handle_motion_event (mw, &ev->xmotion, False);
3350 Select (Widget w, XEvent *ev, String *params, Cardinal *num_params)
3352 XlwMenuWidget mw = (XlwMenuWidget)w;
3353 widget_value *selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
3355 lw_menu_accelerate = False;
3357 /* If user releases the button quickly, without selecting anything,
3358 after the initial down-click that brought the menu up,
3360 if ((selected_item == 0 || selected_item->call_data == 0)
3361 && (!mw->menu.next_release_must_exit
3362 || close_to_reference_time(w, mw->menu.menu_post_time, ev)))
3364 mw->menu.next_release_must_exit = False;
3368 /* pop down everything */
3369 mw->menu.new_depth = 1;
3372 /* Destroy() only gets called for popup menus. Menubar widgets aren't
3373 destroyed when their menu panes get nuked. */
3374 if (mw->menu.pointer_grabbed)
3376 XtUngrabPointer ((Widget)w, ev->xmotion.time);
3377 mw->menu.pointer_grabbed = False;
3380 if (mw->menu.popped_up)
3382 mw->menu.popped_up = False;
3383 XtPopdown (XtParent (mw));
3386 lw_menu_active = False;
3388 x_focus_timestamp_really_sucks_fix_me_better =
3389 ((XButtonPressedEvent*)ev)->time;
3392 XtCallCallbackList ((Widget) mw, mw->menu.select, (XtPointer) selected_item);
3395 \f/* Action procedures for keyboard accelerators */
3399 xlw_set_menu (Widget w, widget_value *val)
3401 lw_menubar_widget = w;
3402 set_new_state ((XlwMenuWidget)w, val, 1);
3405 /* prepare the menu structure via the call-backs */
3407 xlw_map_menu (Time t)
3409 XlwMenuWidget mw = (XlwMenuWidget)lw_menubar_widget;
3411 lw_menu_accelerate = True;
3413 if (!mw->menu.pointer_grabbed)
3415 XWindowAttributes ret;
3418 unsigned int num_waste;
3420 lw_menu_active = True;
3422 mw->menu.menu_post_time = t;
3423 mw->menu.menu_bounce_time = 0;
3425 mw->menu.next_release_must_exit = True;
3426 mw->menu.last_selected_val = NULL;
3428 XtCallCallbackList ((Widget)mw, mw->menu.open, NULL);
3430 /* do this for keyboards too! */
3431 /* notes the absolute position of the menubar window */
3433 mw->menu.windows [0].x = ev->xmotion.x_root - ev->xmotion.x;
3434 mw->menu.windows [0].y = ev->xmotion.y_root - ev->xmotion.y;
3437 /* get the geometry of the menubar */
3439 /* there has to be a better way than this. */
3441 mw->menu.windows [0].x = 0;
3442 mw->menu.windows [0].y = 0;
3444 parent = XtWindow (lw_menubar_widget);
3447 XGetWindowAttributes (XtDisplay (lw_menubar_widget), parent, &ret);
3448 mw->menu.windows [0].x += ret.x;
3449 mw->menu.windows [0].y += ret.y;
3452 XQueryTree (XtDisplay (lw_menubar_widget), parent, &root, &parent, &waste,
3459 while (parent != root);
3461 XtGrabPointer ((Widget)mw, False,
3462 (ButtonMotionMask | ButtonReleaseMask | ButtonPressMask),
3463 GrabModeAsync, GrabModeAsync,
3464 None, mw->menu.cursor_shape, t);
3465 mw->menu.pointer_grabbed = True;
3469 /* display the stupid menu already */
3471 xlw_display_menu (Time t)
3473 XlwMenuWidget mw = (XlwMenuWidget)lw_menubar_widget;
3475 lw_menu_accelerate = True;
3479 /* Sync with the display. Makes it feel better on X terms. */
3480 XFlush (XtDisplay (mw));
3483 /* push a sub menu */
3485 xlw_push_menu (widget_value *val)
3487 push_new_stack ((XlwMenuWidget)lw_menubar_widget, val);
3490 /* pop a sub menu */
3494 if (((XlwMenuWidget)lw_menubar_widget)->menu.new_depth > 0)
3495 ((XlwMenuWidget)lw_menubar_widget)->menu.new_depth --;
3502 xlw_kill_menus (widget_value *val)
3504 XlwMenuWidget mw = (XlwMenuWidget)lw_menubar_widget;
3506 lw_menu_accelerate = False;
3508 mw->menu.new_depth = 1;
3511 if (mw->menu.pointer_grabbed)
3513 XtUngrabPointer (lw_menubar_widget, CurrentTime);
3514 mw->menu.pointer_grabbed = False;
3517 lw_menu_active = False;
3518 XtCallCallbackList (lw_menubar_widget, mw->menu.select, (XtPointer)val);
3521 /* set the menu item */
3523 xlw_set_item (widget_value *val)
3525 if (((XlwMenuWidget)lw_menubar_widget)->menu.new_depth > 0)
3526 ((XlwMenuWidget) lw_menubar_widget)->menu.new_depth --;
3527 push_new_stack ((XlwMenuWidget) lw_menubar_widget, val);
3530 /* get either the current entry or a list of all entries in the current submenu */
3532 xlw_get_entries (int allp)
3534 XlwMenuWidget mw = (XlwMenuWidget)lw_menubar_widget;
3537 if (mw->menu.new_depth >= 2)
3538 return mw->menu.new_stack [mw->menu.new_depth - 2]->contents;
3540 return mw->menu.new_stack[0];
3543 if (mw->menu.new_depth >= 1)
3544 return mw->menu.new_stack [mw->menu.new_depth - 1];
3550 xlw_menu_level (void)
3552 return ((XlwMenuWidget)lw_menubar_widget)->menu.new_depth;
3556 /* Special code to pop-up a menu */
3558 xlw_pop_up_menu (XlwMenuWidget mw, XButtonPressedEvent *event)
3560 int x = event->x_root;
3561 int y = event->y_root;
3564 int borderwidth = mw->menu.shadow_thickness;
3565 Screen* screen = XtScreen (mw);
3567 mw->menu.menu_post_time = event->time;
3568 mw->menu.menu_bounce_time = 0;
3569 mw->menu.next_release_must_exit = True;
3570 mw->menu.last_selected_val = NULL;
3572 XtCallCallbackList ((Widget) mw, mw->menu.open, NULL);
3576 w = mw->menu.windows [0].width;
3577 h = mw->menu.windows [0].height;
3582 if (x < borderwidth)
3585 if (x > WidthOfScreen (screen) - w - 2 * borderwidth)
3586 x = WidthOfScreen (screen) - w - 2 * borderwidth;
3588 if (y < borderwidth)
3591 if (y > HeightOfScreen (screen) - h - 2 * borderwidth)
3592 y = HeightOfScreen (screen) - h - 2 * borderwidth;
3594 mw->menu.popped_up = True;
3595 XtConfigureWidget (XtParent (mw), x, y, w, h,
3596 XtParent (mw)->core.border_width);
3597 XtPopup (XtParent (mw), XtGrabExclusive);
3598 display_menu (mw, 0, False, NULL, NULL, NULL, NULL, NULL);
3599 if (!mw->menu.pointer_grabbed)
3601 XtGrabPointer ((Widget)mw, False,
3602 (ButtonMotionMask | ButtonReleaseMask | ButtonPressMask),
3603 GrabModeAsync, GrabModeAsync,
3604 None, mw->menu.cursor_shape, event->time);
3605 mw->menu.pointer_grabbed = True;
3608 mw->menu.windows [0].x = x + borderwidth;
3609 mw->menu.windows [0].y = y + borderwidth;
3611 handle_motion_event (mw, (XMotionEvent *) event, True);
3617 * This is a horrible function which should not be needed.
3618 * use it to put the resize method back the way the XlwMenu
3619 * class initializer put it. Motif screws with this when
3620 * the XlwMenu class gets instantiated.
3623 xlw_unmunge_class_resize (Widget w)
3625 if (w->core.widget_class->core_class.resize != XlwMenuResize)
3626 w->core.widget_class->core_class.resize = XlwMenuResize;