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, naive 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 /* #### Consider using the same method as for Motif; see the comment in
92 XlwMenuInitialize(). */
93 {XtNfontSet, XtCFontSet, XtRFontSet, sizeof(XFontSet),
94 offset(menu.font_set), XtRString, (XtPointer) "XtDefaultFontSet"},
97 {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
98 offset(menu.foreground), XtRString, (XtPointer) "XtDefaultForeground"},
99 {XtNbuttonForeground, XtCButtonForeground, XtRPixel, sizeof(Pixel),
100 offset(menu.button_foreground), XtRString, (XtPointer) "XtDefaultForeground"},
101 {XtNhighlightForeground, XtCHighlightForeground, XtRPixel, sizeof(Pixel),
102 offset(menu.highlight_foreground), XtRString, (XtPointer) "XtDefaultForeground"},
103 {XtNtitleForeground, XtCTitleForeground, XtRPixel, sizeof(Pixel),
104 offset(menu.title_foreground), XtRString, (XtPointer) "XtDefaultForeground"},
105 {XtNmargin, XtCMargin, XtRDimension, sizeof(Dimension),
106 offset(menu.margin), XtRImmediate, (XtPointer)2},
107 {XmNmarginWidth, XmCMarginWidth, XmRHorizontalDimension, sizeof(Dimension),
108 offset(menu.horizontal_margin), XtRImmediate, (XtPointer)2},
109 {XmNmarginHeight, XmCMarginHeight, XmRVerticalDimension, sizeof(Dimension),
110 offset(menu.vertical_margin), XtRImmediate, (XtPointer)1},
111 {XmNspacing, XmCSpacing, XmRHorizontalDimension, sizeof(Dimension),
112 offset(menu.column_spacing), XtRImmediate, (XtPointer)4},
113 {XmNindicatorSize, XmCIndicatorSize, XtRDimension, sizeof(Dimension),
114 offset(menu.indicator_size), XtRImmediate, (XtPointer)0},
116 {XmNshadowThickness, XmCShadowThickness, XmRHorizontalDimension,
117 sizeof (Dimension), offset (menu.shadow_thickness),
118 XtRImmediate, (XtPointer) 2},
120 {XmNshadowThickness, XmCShadowThickness, XtRDimension,
121 sizeof (Dimension), offset (menu.shadow_thickness),
122 XtRImmediate, (XtPointer) 2},
124 {XmNselectColor, XmCSelectColor, XtRPixel, sizeof (Pixel),
125 offset (menu.select_color), XtRImmediate, (XtPointer)-1},
126 {XmNtopShadowColor, XmCTopShadowColor, XtRPixel, sizeof (Pixel),
127 offset (menu.top_shadow_color), XtRImmediate, (XtPointer)-1},
128 {XmNbottomShadowColor, XmCBottomShadowColor, XtRPixel, sizeof (Pixel),
129 offset (menu.bottom_shadow_color), XtRImmediate, (XtPointer)-1},
130 {XmNtopShadowPixmap, XmCTopShadowPixmap, XtRPixmap, sizeof (Pixmap),
131 offset (menu.top_shadow_pixmap), XtRImmediate, (XtPointer)None},
132 {XmNbottomShadowPixmap, XmCBottomShadowPixmap, XtRPixmap, sizeof (Pixmap),
133 offset (menu.bottom_shadow_pixmap), XtRImmediate, (XtPointer)None},
135 {XtNopen, XtCCallback, XtRCallback, sizeof(XtPointer),
136 offset(menu.open), XtRCallback, (XtPointer)NULL},
137 {XtNselect, XtCCallback, XtRCallback, sizeof(XtPointer),
138 offset(menu.select), XtRCallback, (XtPointer)NULL},
139 {XtNmenu, XtCMenu, XtRPointer, sizeof(XtPointer),
140 offset(menu.contents), XtRImmediate, (XtPointer)NULL},
141 {XtNcursor, XtCCursor, XtRCursor, sizeof(Cursor),
142 offset(menu.cursor_shape), XtRString, (XtPointer) "right_ptr"},
143 {XtNhorizontal, XtCHorizontal, XtRInt, sizeof(int),
144 offset(menu.horizontal), XtRImmediate, (XtPointer)True},
145 {XtNuseBackingStore, XtCUseBackingStore, XtRBoolean, sizeof (Boolean),
146 offset (menu.use_backing_store), XtRImmediate, (XtPointer)False},
147 {XtNbounceDown, XtCBounceDown, XtRBoolean, sizeof (Boolean),
148 offset (menu.bounce_down), XtRImmediate, (XtPointer)True},
149 {XtNresourceLabels, XtCResourceLabels, XtRBoolean, sizeof (Boolean),
150 offset (menu.lookup_labels), XtRImmediate, (XtPointer)False},
154 static Boolean XlwMenuSetValues (Widget current, Widget request, Widget new,
155 ArgList args, Cardinal *num_args);
156 static void XlwMenuRealize (Widget w, Mask *valueMask,
157 XSetWindowAttributes *attributes);
158 static void XlwMenuRedisplay (Widget w, XEvent *ev, Region region);
159 static void XlwMenuResize (Widget w);
160 static void XlwMenuInitialize (Widget request, Widget new, ArgList args,
162 static void XlwMenuDestroy (Widget w);
163 static void XlwMenuClassInitialize (void);
164 static void Start (Widget w, XEvent *ev, String *params, Cardinal *num_params);
165 static void Drag (Widget w, XEvent *ev, String *params, Cardinal *num_params);
166 static void Select(Widget w, XEvent *ev, String *params, Cardinal *num_params);
169 static XFontStruct *default_font_of_font_list (XmFontList);
173 xlwMenuActionsList [] =
180 #define SuperClass ((CoreWidgetClass)&coreClassRec)
182 XlwMenuClassRec xlwMenuClassRec =
184 { /* CoreClass fields initialization */
185 (WidgetClass) SuperClass, /* superclass */
186 "XlwMenu", /* class_name */
187 sizeof(XlwMenuRec), /* size */
188 XlwMenuClassInitialize, /* class_initialize */
189 NULL, /* class_part_initialize */
190 FALSE, /* class_inited */
191 XlwMenuInitialize, /* initialize */
192 NULL, /* initialize_hook */
193 XlwMenuRealize, /* realize */
194 xlwMenuActionsList, /* actions */
195 XtNumber(xlwMenuActionsList), /* num_actions */
196 xlwMenuResources, /* resources */
197 XtNumber(xlwMenuResources), /* resource_count */
198 NULLQUARK, /* xrm_class */
199 TRUE, /* compress_motion */
200 XtExposeCompressMaximal, /* compress_exposure */
201 TRUE, /* compress_enterleave */
202 FALSE, /* visible_interest */
203 XlwMenuDestroy, /* destroy */
204 XlwMenuResize, /* resize */
205 XlwMenuRedisplay, /* expose */
206 XlwMenuSetValues, /* set_values */
207 NULL, /* set_values_hook */
208 XtInheritSetValuesAlmost, /* set_values_almost */
209 NULL, /* get_values_hook */
210 NULL, /* #### - should this be set for grabs? accept_focus */
211 XtVersion, /* version */
212 NULL, /* callback_private */
213 xlwMenuTranslations, /* tm_table */
214 XtInheritQueryGeometry, /* query_geometry */
215 XtInheritDisplayAccelerator, /* display_accelerator */
217 }, /* XlwMenuClass fields initialization */
223 WidgetClass xlwMenuWidgetClass = (WidgetClass) &xlwMenuClassRec;
225 extern int lw_menu_accelerate;
228 #if 0 /* Apparently not used anywhere */
231 safe_strdup (char *s)
235 result = (char *) malloc (strlen (s) + 1);
244 /* Replacement for XAllocColor() that tries to return the nearest
245 available color if the colormap is full. From FSF Emacs. */
248 allocate_nearest_color (Display *display, Colormap screen_colormap,
251 int status = XAllocColor (display, screen_colormap, color_def);
256 /* If we got to this point, the colormap is full, so we're
257 going to try to get the next closest color.
258 The algorithm used is a least-squares matching, which is
259 what X uses for closest color matching with StaticColor visuals. */
262 unsigned long nearest_delta = ULONG_MAX;
264 int no_cells = XDisplayCells (display, XDefaultScreen (display));
265 /* Don't use alloca here because lwlib doesn't have the
266 necessary configuration information that src does. */
267 XColor *cells = (XColor *) malloc (sizeof (XColor) * no_cells);
269 for (x = 0; x < no_cells; x++)
272 XQueryColors (display, screen_colormap, cells, no_cells);
274 for (nearest = 0, x = 0; x < no_cells; x++)
276 long dred = (color_def->red >> 8) - (cells[x].red >> 8);
277 long dgreen = (color_def->green >> 8) - (cells[x].green >> 8);
278 long dblue = (color_def->blue >> 8) - (cells[x].blue >> 8);
279 unsigned long delta = dred * dred + dgreen * dgreen + dblue * dblue;
281 if (delta < nearest_delta)
284 nearest_delta = delta;
287 color_def->red = cells[nearest].red;
288 color_def->green = cells[nearest].green;
289 color_def->blue = cells[nearest].blue;
291 return XAllocColor (display, screen_colormap, color_def);
296 push_new_stack (XlwMenuWidget mw, widget_value *val)
298 if (!mw->menu.new_stack)
300 mw->menu.new_stack_length = 10;
302 (widget_value**)XtCalloc (mw->menu.new_stack_length,
303 sizeof (widget_value*));
305 else if (mw->menu.new_depth == mw->menu.new_stack_length)
307 mw->menu.new_stack_length *= 2;
309 (widget_value**)XtRealloc ((char *)mw->menu.new_stack,
310 mw->menu.new_stack_length *
311 sizeof (widget_value*));
313 mw->menu.new_stack [mw->menu.new_depth++] = val;
317 pop_new_stack_if_no_contents (XlwMenuWidget mw)
319 if (mw->menu.new_depth &&
320 !mw->menu.new_stack [mw->menu.new_depth - 1]->contents)
321 mw->menu.new_depth -= 1;
325 make_old_stack_space (XlwMenuWidget mw, int n)
327 if (!mw->menu.old_stack)
329 mw->menu.old_stack_length = max (10, n);
331 (widget_value**)XtCalloc (mw->menu.old_stack_length,
332 sizeof (widget_value*));
334 else if (mw->menu.old_stack_length < n)
336 while (mw->menu.old_stack_length < n)
337 mw->menu.old_stack_length *= 2;
340 (widget_value**)XtRealloc ((char *)mw->menu.old_stack,
341 mw->menu.old_stack_length *
342 sizeof (widget_value*));
347 close_to_reference_time (Widget w, Time reference_time, XEvent *ev)
351 (ev->xbutton.time - reference_time
352 < (Time) XtGetMultiClickTime (XtDisplay (w)));
357 string_width (XlwMenuWidget mw,
366 Dimension width, height;
367 XmStringExtent (mw->menu.font_list, s, &width, &height);
372 XmbTextExtents (mw->menu.font_set, s, strlen (s), &ri, &rl);
377 XTextExtents (mw->menu.font, s, strlen (s), &drop, &drop, &drop, &xcs);
379 # endif /* USE_XFONTSET */
383 static char massaged_resource_char[256];
386 initialize_massaged_resource_char (void)
389 for (j = 0; j < (int) sizeof (massaged_resource_char); j++)
391 if ((j >= 'a' && j <= 'z') ||
392 (j >= 'A' && j <= 'Z') ||
393 (j >= '0' && j <= '9') ||
396 massaged_resource_char[j] = (char) j;
398 massaged_resource_char ['_'] = '_';
399 massaged_resource_char ['+'] = 'P'; /* Convert C++ to cPP */
400 massaged_resource_char ['.'] = '_'; /* Convert Buffers... to buffers___ */
404 string_width_u (XlwMenuWidget mw,
413 Dimension width, height;
418 # else /* ! USE_XFONTSET */
430 if (!XmStringGetLtoR (string, XmFONTLIST_DEFAULT_TAG, &chars))
435 charslength = strlen (chars);
436 newchars = (char *) alloca (charslength + 1);
438 for (i = j = 0; chars[i] && (j < charslength); i++)
439 if (chars[i]=='%'&&chars[i+1]=='_')
442 newchars[j++] = chars[i];
446 newstring = XmStringLtoRCreate (newchars, XmFONTLIST_DEFAULT_TAG);
447 XmStringExtent (mw->menu.font_list, newstring, &width, &height);
448 XmStringFree (newstring);
453 XmbTextExtents (mw->menu.font_set, newchars, j, &ri, &rl);
455 # else /* ! USE_XFONTSET */
456 XTextExtents (mw->menu.font, newchars, j, &drop, &drop, &drop, &xcs);
458 # endif /* USE_XFONTSET */
463 massage_resource_name (const char *in, char *out)
465 /* Turn a random string into something suitable for using as a resource.
468 "Kill Buffer" -> "killBuffer"
469 "Find File..." -> "findFile___"
470 "Search and Replace..." -> "searchAndReplace___"
471 "C++ Mode Commands" -> "cppModeCommands"
473 Valid characters in a resource NAME component are: a-zA-Z0-9_
476 #ifdef PRINT_XLWMENU_RESOURCE_CONVERSIONS
477 /* Compile with -DPRINT_XLWMENU_RESOURCE_CONVERSIONS to generate a
478 translation file for menu localizations. */
479 char *save_in = in, *save_out = out;
482 Boolean firstp = True;
485 if (*in == '%' && *(in + 1) == '_')
491 if (*in == '%' && *(in + 1) == '%')
493 ch = massaged_resource_char[(unsigned char) *in++];
496 int int_ch = (int) (unsigned char) ch;
497 *out++ = firstp ? tolower (int_ch) : toupper (int_ch);
499 while ((ch = massaged_resource_char[(unsigned char) *in++])
502 if (!*(in-1)) /* Overshot the NULL byte? */
509 #ifdef PRINT_XLWMENU_RESOURCE_CONVERSIONS
510 printf ("! Emacs*XlwMenu.%s.labelString:\t%s\n", save_out, save_in);
511 printf ( "Emacs*XlwMenu.%s.labelString:\n", save_out);
518 { "labelString", "LabelString", XtRString, sizeof(String),
522 /* This function searches STRING for parameter inserts of the form:
524 padding is either space (' ') or dash ('-') meaning
525 padding to the left or right of the inserted parameter.
526 In essence, all %1 strings are replaced by VALUE in the return value.
527 The caller is expected to free the return value using XtFree().
528 %% means insert one % (like printf).
529 %1 means insert VALUE.
530 %-1 means insert VALUE followed by one space. The latter is
531 not inserted if VALUE is a zero length string.
534 parameterize_string (const char *string, const char *value)
538 unsigned int done = 0;
543 result = XtMalloc(1);
551 for (ntimes = 1, percent = string;
552 (percent = strchr (percent, '%'));
556 result = XtMalloc ((ntimes * strlen(value)) + strlen(string) + 4);
559 while ((percent = strchr (string, '%')))
561 unsigned int left_pad;
562 unsigned int right_pad;
565 if (percent[1] == '%')
566 { /* it's a real % */
567 strncat (result, string, 1 + percent - string); /* incl % */
568 string = &percent[2]; /* after the second '%' */
569 continue; /* with the while() loop */
575 for (p = &percent[1]; /* test *p inside the loop */ ; p++)
586 { /* param and terminator */
587 strncat (result, string, percent - string);
588 if (value[0] != '\0')
591 for (i = 0; i < left_pad; i++)
592 strcat (result, " ");
593 strcat (result, value);
594 for (i = 0; i < right_pad; i++)
595 strcat (result, " ");
597 string = &p[1]; /* after the '1' */
598 done++; /* no need to do old way */
599 break; /* out of for() loop */
602 { /* bogus, copy the format as is */
603 /* out of for() loop */
604 strncat (result, string, 1 + p - string);
605 string = (*p ? &p[1] : p);
611 /* Copy the tail of the string */
612 strcat (result, string);
614 /* If we have not processed a % string, and we have a value, tail it. */
615 if (!done && value[0] != '\0')
617 strcat (result, " ");
618 strcat (result, value);
627 resource_widget_value (XlwMenuWidget mw, widget_value *val)
629 if (!val->toolkit_data)
631 char *resourced_name = NULL;
632 char *converted_name, *str;
633 XmString complete_name;
634 char massaged_name [1024];
636 if (mw->menu.lookup_labels)
638 /* Convert value style name into resource style name.
639 eg: "Free Willy" becomes "freeWilly" */
640 massage_resource_name (val->name, massaged_name);
642 /* If we have a value (parameter) see if we can find a "Named"
646 char named_name[1024];
647 sprintf (named_name, "%sNamed", massaged_name);
648 XtGetSubresources ((Widget) mw,
649 (XtPointer) &resourced_name,
650 named_name, named_name,
651 nameResource, 1, NULL, 0);
654 /* If nothing yet, try to load from the massaged name. */
657 XtGetSubresources ((Widget) mw,
658 (XtPointer) &resourced_name,
659 massaged_name, massaged_name,
660 nameResource, 1, NULL, 0);
662 } /* if (mw->menu.lookup_labels) */
664 /* Still nothing yet, use the name as the value. */
666 resourced_name = val->name;
668 /* Parameterize the string. */
669 converted_name = parameterize_string (resourced_name, val->value);
671 /* nuke newline characters to prevent menubar screwups */
672 for ( str = converted_name ; *str ; str++ )
674 if (str[0] == '\n') str[0] = ' ';
677 /* Improve OSF's bottom line. */
678 #if (XmVersion >= 1002)
679 complete_name = XmStringCreateLocalized (converted_name);
681 complete_name = XmStringCreateLtoR (converted_name,
682 XmSTRING_DEFAULT_CHARSET);
684 XtFree (converted_name);
686 val->toolkit_data = complete_name;
687 val->free_toolkit_data = True;
689 return (XmString) val->toolkit_data;
694 /* These two routines should be a separate file..djw */
696 xlw_create_localized_string (Widget w,
707 XtGetSubresources (w,
717 return parameterize_string (string, arg);
721 xlw_create_localized_xmstring (Widget w,
726 char * string = xlw_create_localized_string (w, name, args, nargs);
727 XmString xm_string = XmStringCreateLtoR (string, XmSTRING_DEFAULT_CHARSET);
736 resource_widget_value (XlwMenuWidget mw, widget_value *val)
738 if (!val->toolkit_data)
740 char *resourced_name = NULL;
742 char massaged_name [1024];
744 if (mw->menu.lookup_labels)
746 massage_resource_name (val->name, massaged_name);
748 XtGetSubresources ((Widget) mw,
749 (XtPointer) &resourced_name,
750 massaged_name, massaged_name,
751 nameResource, 1, NULL, 0);
754 resourced_name = val->name;
756 complete_name = parameterize_string (resourced_name, val->value);
758 val->toolkit_data = complete_name;
759 /* nuke newline characters to prevent menubar screwups */
760 for ( ; *complete_name ; complete_name++ )
762 if (complete_name[0] == '\n')
763 complete_name[0] = ' ';
765 val->free_toolkit_data = True;
767 return (char *) val->toolkit_data;
772 /* Code for drawing strings. */
774 string_draw (XlwMenuWidget mw,
786 XmStringDraw (XtDisplay (mw), window,
790 1000, /* ???? width */
791 XmALIGNMENT_BEGINNING,
792 0, /* ???? layout_direction */
796 XmbDrawString (XtDisplay (mw), window, mw->menu.font_set, gc,
797 x, y + mw->menu.font_ascent, string, strlen (string));
799 XDrawString (XtDisplay (mw), window, gc,
800 x, y + mw->menu.font_ascent, string, strlen (string));
801 # endif /* USE_XFONTSET */
818 Dimension width, height;
826 newstring = XmStringLtoRCreate (&string[start], XmFONTLIST_DEFAULT_TAG);
828 XtDisplay (mw), window,
832 1000, /* ???? width */
833 XmALIGNMENT_BEGINNING,
834 0, /* ???? layout_direction */
837 XmStringExtent (mw->menu.font_list, newstring, &width, &height);
838 XmStringFree (newstring);
848 XtDisplay (mw), window, mw->menu.font_set, gc,
849 x, y + mw->menu.font_ascent, &string[start], end - start);
851 mw->menu.font_set, &string[start], end - start, &ri, &rl);
860 XtDisplay (mw), window, gc,
861 x, y + mw->menu.font_ascent, &string[start], end - start);
863 mw->menu.font, &string[start], end - start,
864 &drop, &drop, &drop, &xcs);
871 string_draw_u (XlwMenuWidget mw,
887 if (!XmStringGetLtoR (string, XmFONTLIST_DEFAULT_TAG, &chars))
892 for (i=0; chars[i]; ++i) {
893 if (chars[i] == '%' && chars[i+1] == '_') {
896 x += string_draw_range (mw, window, x, y, gc, chars, s, i);
897 w = string_draw_range (mw, window, x, y, gc, chars, i+2, i+3);
899 /* underline next character */
900 XDrawLine (XtDisplay (mw), window, gc, x - 1,
901 y + mw->menu.font_ascent + 1,
902 x + w - 1, y + mw->menu.font_ascent + 1 );
908 x += string_draw_range (mw, window, x, y, gc, chars, s, i);
915 binding_draw (XlwMenuWidget mw, Window w, int x, int y, GC gc, char *value)
918 XmString xm_value = XmStringCreateLtoR(value, XmSTRING_DEFAULT_CHARSET);
919 string_draw (mw, w, x, y, gc, xm_value);
920 XmStringFree (xm_value);
922 string_draw (mw, w, x, y, gc, value);
926 /* Low level code for drawing 3-D edges. */
928 shadow_rectangle_draw (Display *dpy,
935 unsigned int thickness)
944 points [1].x = x + width;
946 points [2].x = x + width - thickness;
947 points [2].y = y + thickness;
949 points [3].y = y + thickness;
950 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
952 points [0].y = y + thickness;
954 points [1].y = y + height;
955 points [2].x = x + thickness;
956 points [2].y = y + height - thickness;
957 points [3].x = x + thickness;
958 points [3].y = y + thickness;
959 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
960 points [0].x = x + width;
962 points [1].x = x + width - thickness;
963 points [1].y = y + thickness;
964 points [2].x = x + width - thickness;
965 points [2].y = y + height - thickness;
966 points [3].x = x + width;
967 points [3].y = y + height - thickness;
968 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
970 points [0].y = y + height;
971 points [1].x = x + width;
972 points [1].y = y + height;
973 points [2].x = x + width;
974 points [2].y = y + height - thickness;
975 points [3].x = x + thickness;
976 points [3].y = y + height - thickness;
977 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
980 typedef enum e_shadow_type
982 /* these are Motif compliant */
988 SHADOW_ETCHED_OUT_DASH,
989 SHADOW_ETCHED_IN_DASH,
992 SHADOW_SINGLE_DASHED_LINE,
993 SHADOW_DOUBLE_DASHED_LINE,
995 /* these are all non-Motif */
996 SHADOW_DOUBLE_ETCHED_OUT,
997 SHADOW_DOUBLE_ETCHED_IN,
998 SHADOW_DOUBLE_ETCHED_OUT_DASH,
999 SHADOW_DOUBLE_ETCHED_IN_DASH
1003 shadow_draw (XlwMenuWidget mw,
1007 unsigned int height,
1010 Display *dpy = XtDisplay (mw);
1013 int thickness = mw->menu.shadow_thickness;
1017 Boolean etched = False;
1021 case SHADOW_BACKGROUND:
1022 top_gc = bottom_gc = mw->menu.background_gc;
1024 case SHADOW_ETCHED_IN:
1025 top_gc = mw->menu.shadow_bottom_gc;
1026 bottom_gc = mw->menu.shadow_top_gc;
1029 case SHADOW_ETCHED_OUT:
1030 top_gc = mw->menu.shadow_top_gc;
1031 bottom_gc = mw->menu.shadow_bottom_gc;
1035 top_gc = mw->menu.shadow_bottom_gc;
1036 bottom_gc = mw->menu.shadow_top_gc;
1040 top_gc = mw->menu.shadow_top_gc;
1041 bottom_gc = mw->menu.shadow_bottom_gc;
1047 unsigned int half = thickness/2;
1048 shadow_rectangle_draw (dpy,
1053 width - half, height - half,
1055 shadow_rectangle_draw (dpy,
1060 width - half , height - half,
1065 shadow_rectangle_draw (dpy,
1076 arrow_decoration_draw (XlwMenuWidget mw,
1082 Display *dpy = XtDisplay (mw);
1086 int thickness = mw->menu.shadow_thickness;
1089 int length = (int)((double)width * 0.87);
1090 int thick_med = (int)((double)thickness * 1.73);
1093 half_width = width/2 + 1;
1095 half_width = width/2;
1097 select_gc = mw->menu.background_gc;
1101 top_gc = mw->menu.shadow_bottom_gc;
1102 bottom_gc = mw->menu.shadow_top_gc;
1106 top_gc = mw->menu.shadow_top_gc;
1107 bottom_gc = mw->menu.shadow_bottom_gc;
1110 /* Fill internal area. We do this first so that the borders have a
1112 points [0].x = x + thickness;
1113 points [0].y = y + thickness;
1114 points [1].x = x + length - thickness;
1115 points [1].y = y + half_width;
1116 points [2].x = x + length - thickness;
1117 points [2].y = y + half_width + thickness;
1118 points [3].x = x + thickness;
1119 points [3].y = y + width - thickness;
1132 points [1].x = x + thickness;
1133 points [1].y = y + thick_med;
1134 points [2].x = x + thickness;
1135 points [2].y = y + width - thick_med;
1137 points [3].y = y + width;
1139 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
1143 points [0].y = y + width;
1144 points [1].x = x + length;
1145 points [1].y = y + half_width;
1146 points [2].x = x + length - (thickness + thickness);
1147 points [2].y = y + half_width;
1148 points [3].x = x + thickness;
1149 points [3].y = y + width - thick_med;
1151 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
1156 points [1].x = x + length;
1157 points [1].y = y + half_width;
1158 points [2].x = x + length - (thickness + thickness);
1159 points [2].y = y + half_width;
1160 points [3].x = x + thickness;
1161 points [3].y = y + thick_med;
1163 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
1167 toggle_decoration_draw (XlwMenuWidget mw,
1173 Display *dpy = XtDisplay (mw);
1174 int thickness = mw->menu.shadow_thickness;
1176 GC select_gc = mw->menu.select_gc;
1183 /* Fill internal area. */
1185 XFillRectangle (dpy,
1190 width - (2*thickness),
1191 width - (2*thickness));
1193 shadow_draw (mw, window, x, y, width, width, type);
1197 radio_decoration_draw (XlwMenuWidget mw,
1203 Display *dpy = XtDisplay (mw);
1206 GC select_gc = mw->menu.select_gc;
1207 int thickness = mw->menu.shadow_thickness;
1217 half_width = width/2;
1221 top_gc = mw->menu.shadow_bottom_gc;
1222 bottom_gc = mw->menu.shadow_top_gc;
1226 top_gc = mw->menu.shadow_top_gc;
1227 bottom_gc = mw->menu.shadow_bottom_gc;
1231 /* Draw the bottom first, just in case the regions overlap.
1232 The top should cast the longer shadow. */
1233 points [0].x = x; /* left corner */
1234 points [0].y = y + half_width;
1235 points [1].x = x + half_width; /* bottom corner */
1236 points [1].y = y + width;
1237 points [2].x = x + half_width; /* bottom inside corner */
1238 points [2].y = y + width - thickness;
1239 points [3].x = x + thickness; /* left inside corner */
1240 points [3].y = y + half_width;
1242 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
1244 points [0].x = x + half_width; /* bottom corner */
1245 points [0].y = y + width;
1246 points [1].x = x + width; /* right corner */
1247 points [1].y = y + half_width;
1248 points [2].x = x + width - thickness; /* right inside corner */
1249 points [2].y = y + half_width;
1250 points [3].x = x + half_width; /* bottom inside corner */
1251 points [3].y = y + width - thickness;
1253 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
1255 points [0].x = x; /* left corner */
1256 points [0].y = y + half_width;
1257 points [1].x = x + half_width; /* top corner */
1259 points [2].x = x + half_width; /* top inside corner */
1260 points [2].y = y + thickness;
1261 points [3].x = x + thickness; /* left inside corner */
1262 points [3].y = y + half_width;
1264 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
1266 points [0].x = x + half_width; /* top corner */
1268 points [1].x = x + width; /* right corner */
1269 points [1].y = y + half_width;
1270 points [2].x = x + width - thickness; /* right inside corner */
1271 points [2].y = y + half_width;
1272 points [3].x = x + half_width; /* top inside corner */
1273 points [3].y = y + thickness;
1275 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
1277 /* Draw the bottom first, just in case the regions overlap.
1278 The top should cast the longer shadow. */
1280 points [npoints].x = x; /* left corner */
1281 points [npoints++].y = y + half_width;
1282 points [npoints].x = x + half_width; /* bottom corner */
1283 points [npoints++].y = y + width;
1284 points [npoints].x = x + width; /* right corner */
1285 points [npoints++].y = y + half_width;
1286 points [npoints].x = x + width - thickness; /* right inside corner */
1287 points [npoints++].y = y + half_width;
1288 points [npoints].x = x + half_width; /* bottom inside corner */
1289 points [npoints++].y = y + width - thickness;
1290 points [npoints].x = x + thickness; /* left inside corner */
1291 points [npoints++].y = y + half_width;
1293 XFillPolygon (dpy, window, bottom_gc,
1294 points, npoints, Nonconvex, CoordModeOrigin);
1298 points [npoints].x = x; /* left corner */
1299 points [npoints++].y = y + half_width;
1300 points [npoints].x = x + half_width; /* top corner */
1301 points [npoints++].y = y;
1302 points [npoints].x = x + width; /* right corner */
1303 points [npoints++].y = y + half_width;
1304 points [npoints].x = x + width - thickness; /* right inside corner */
1305 points [npoints++].y = y + half_width;
1306 points [npoints].x = x + half_width; /* top inside corner */
1307 points [npoints++].y = y + thickness;
1308 points [npoints].x = x + thickness; /* left inside corner */
1309 points [npoints++].y = y + half_width;
1311 XFillPolygon (dpy, window, top_gc, points, npoints, Nonconvex,
1316 /* Fill internal area. */
1319 points [0].x = x + thickness;
1320 points [0].y = y + half_width;
1321 points [1].x = x + half_width;
1322 points [1].y = y + thickness;
1323 points [2].x = x + width - thickness;
1324 points [2].y = y + half_width;
1325 points [3].x = x + half_width;
1326 points [3].y = y + width - thickness;
1338 separator_decoration_draw (XlwMenuWidget mw,
1345 Display *dpy = XtDisplay (mw);
1348 unsigned int offset = 0;
1349 unsigned int num_separators = 1;
1350 unsigned int top_line_thickness = 0;
1351 unsigned int bottom_line_thickness = 0;
1352 Boolean dashed = False;
1356 case SHADOW_NO_LINE: /* nothing to do */
1358 case SHADOW_DOUBLE_LINE:
1360 case SHADOW_SINGLE_LINE:
1361 top_gc = bottom_gc = mw->menu.foreground_gc;
1362 top_line_thickness = 1;
1364 case SHADOW_DOUBLE_DASHED_LINE:
1366 case SHADOW_SINGLE_DASHED_LINE:
1367 top_gc = bottom_gc = mw->menu.foreground_gc;
1368 top_line_thickness = 1;
1371 case SHADOW_DOUBLE_ETCHED_OUT_DASH:
1373 case SHADOW_ETCHED_OUT_DASH:
1374 top_gc = mw->menu.shadow_top_gc;
1375 bottom_gc = mw->menu.shadow_bottom_gc;
1376 top_line_thickness = mw->menu.shadow_thickness/2;
1377 bottom_line_thickness = mw->menu.shadow_thickness - top_line_thickness;
1380 case SHADOW_DOUBLE_ETCHED_IN_DASH:
1382 case SHADOW_ETCHED_IN_DASH:
1383 top_gc = mw->menu.shadow_bottom_gc;
1384 bottom_gc = mw->menu.shadow_top_gc;
1385 top_line_thickness = mw->menu.shadow_thickness/2;
1386 bottom_line_thickness = mw->menu.shadow_thickness - top_line_thickness;
1389 case SHADOW_DOUBLE_ETCHED_OUT:
1391 case SHADOW_ETCHED_OUT:
1392 top_gc = mw->menu.shadow_top_gc;
1393 bottom_gc = mw->menu.shadow_bottom_gc;
1394 top_line_thickness = mw->menu.shadow_thickness/2;
1395 bottom_line_thickness = mw->menu.shadow_thickness - top_line_thickness;
1397 case SHADOW_DOUBLE_ETCHED_IN:
1399 case SHADOW_ETCHED_IN:
1401 top_gc = mw->menu.shadow_bottom_gc;
1402 bottom_gc = mw->menu.shadow_top_gc;
1403 top_line_thickness = mw->menu.shadow_thickness/2;
1404 bottom_line_thickness = mw->menu.shadow_thickness - top_line_thickness;
1411 values.line_style = LineOnOffDash;
1412 if (top_line_thickness > 0)
1413 XChangeGC (dpy, top_gc, GCLineStyle, &values);
1414 if (bottom_line_thickness > 0 && bottom_gc != top_gc)
1415 XChangeGC (dpy, bottom_gc, GCLineStyle, &values);
1418 while (num_separators--)
1421 for (i = 0; i < top_line_thickness; i++)
1422 XDrawLine (dpy, window, top_gc, x, y + i, x + width, y + i);
1424 for (i = 0; i < bottom_line_thickness; i++)
1425 XDrawLine (dpy, window, bottom_gc,
1426 x, y + top_line_thickness + offset + i,
1427 x + width, y + top_line_thickness + offset + i);
1428 y += (top_line_thickness + offset + bottom_line_thickness + 1);
1434 values.line_style = LineSolid;
1435 if (top_line_thickness > 0)
1436 XChangeGC (dpy, top_gc, GCLineStyle, &values);
1437 if (bottom_line_thickness > 0 && bottom_gc != top_gc)
1438 XChangeGC (dpy, bottom_gc, GCLineStyle, &values);
1442 #define SLOPPY_TYPES 0 /* 0=off, 1=error check, 2=easy to please */
1444 #if SLOPPY_TYPES < 2
1446 static char *wv_types[] =
1460 print_widget_value (widget_value *wv, int just_one, int depth)
1464 for (i = 0; i < depth; i++)
1469 printf ("%s(null widget value pointer)\n", d);
1472 printf ("%stype: %s\n", d, wv_types [wv->type]);
1474 printf ("%sname: %s\n", d, (wv->name ? wv->name : "(null)"));
1476 if (wv->name) printf ("%sname: %s\n", d, wv->name);
1478 if (wv->value) printf ("%svalue: %s\n", d, wv->value);
1479 if (wv->key) printf ("%skey: %s\n", d, wv->key);
1480 printf ("%senabled: %d\n", d, wv->enabled);
1483 printf ("\n%scontents: \n", d);
1484 print_widget_value (wv->contents, 0, depth + 5);
1486 if (!just_one && wv->next)
1489 print_widget_value (wv->next, 0, depth);
1492 #endif /* SLOPPY_TYPES < 2 */
1495 all_dashes_p (char *s)
1498 if (!s || s[0] == '\0')
1500 for (p = s; *p == '-'; p++);
1502 if (*p == '!' || *p == '\0')
1506 #endif /* SLOPPY_TYPES */
1508 static widget_value_type
1509 menu_item_type (widget_value *val)
1511 if (val->type != UNSPECIFIED_TYPE)
1514 else if (all_dashes_p (val->name))
1515 return SEPARATOR_TYPE;
1516 else if (val->name && val->name[0] == '\0') /* push right */
1517 return PUSHRIGHT_TYPE;
1518 else if (val->contents) /* cascade */
1519 return CASCADE_TYPE;
1520 else if (val->call_data) /* push button */
1527 return UNSPECIFIED_TYPE; /* Not reached */
1532 label_button_size (XlwMenuWidget mw,
1535 unsigned int *toggle_width,
1536 unsigned int *label_width,
1537 unsigned int *bindings_width,
1538 unsigned int *height)
1540 *height = (mw->menu.font_ascent + mw->menu.font_descent +
1541 2 * mw->menu.vertical_margin +
1542 2 * mw->menu.shadow_thickness);
1543 /* no left column decoration */
1544 *toggle_width = mw->menu.horizontal_margin + mw->menu.shadow_thickness;
1546 *label_width = string_width_u (mw, resource_widget_value (mw, val));
1547 *bindings_width = mw->menu.horizontal_margin + mw->menu.shadow_thickness;
1551 label_button_draw (XlwMenuWidget mw,
1554 Boolean highlighted,
1558 unsigned int height,
1559 unsigned int label_offset,
1560 unsigned int binding_tab)
1562 int y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin;
1566 label_offset = mw->menu.shadow_thickness + mw->menu.horizontal_margin;
1568 if (highlighted && (in_menubar || val->contents))
1569 gc = mw->menu.highlight_gc;
1570 else if (in_menubar || val->contents)
1571 gc = mw->menu.foreground_gc;
1573 gc = mw->menu.title_gc;
1575 /* Draw the label string. */
1578 x + label_offset, y + y_offset,
1580 resource_widget_value (mw, val));
1584 push_button_size (XlwMenuWidget mw,
1587 unsigned int *toggle_width,
1588 unsigned int *label_width,
1589 unsigned int *bindings_width,
1590 unsigned int *height)
1593 label_button_size (mw, val, in_menubar,
1594 toggle_width, label_width, bindings_width,
1597 /* key bindings to display? */
1598 if (!in_menubar && val->key)
1602 XmString key = XmStringCreateLtoR (val->key, XmSTRING_DEFAULT_CHARSET);
1603 w = string_width (mw, key);
1606 char *key = val->key;
1607 w = string_width (mw, key);
1609 *bindings_width += w + mw->menu.column_spacing;
1614 push_button_draw (XlwMenuWidget mw,
1617 Boolean highlighted,
1621 unsigned int height,
1622 unsigned int label_offset,
1623 unsigned int binding_offset)
1625 int y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin;
1628 Boolean menu_pb = in_menubar && (menu_item_type (val) == BUTTON_TYPE);
1630 /* Draw the label string. */
1632 label_offset = mw->menu.shadow_thickness + mw->menu.horizontal_margin;
1637 gc = mw->menu.highlight_gc;
1639 gc = mw->menu.inactive_gc;
1644 gc = mw->menu.button_gc;
1646 gc = mw->menu.inactive_button_gc;
1651 gc = mw->menu.foreground_gc;
1653 gc = mw->menu.inactive_gc;
1658 x + label_offset, y + y_offset,
1660 resource_widget_value (mw, val));
1662 /* Draw the keybindings */
1665 if (!binding_offset)
1667 unsigned int s_width =
1668 string_width (mw, resource_widget_value (mw, val));
1669 binding_offset = label_offset + s_width + mw->menu.shadow_thickness;
1671 binding_draw (mw, window,
1672 x + binding_offset + mw->menu.column_spacing,
1673 y + y_offset, gc, val->key);
1676 /* Draw the shadow */
1682 type = (val->selected ? SHADOW_ETCHED_OUT : SHADOW_ETCHED_IN);
1689 type = SHADOW_BACKGROUND;
1692 shadow_draw (mw, window, x, y, width, height, type);
1696 arrow_decoration_height (XlwMenuWidget mw)
1698 int result = (mw->menu.font_ascent + mw->menu.font_descent) / 2;
1700 result += 2 * mw->menu.shadow_thickness;
1702 if (result > (mw->menu.font_ascent + mw->menu.font_descent))
1703 result = mw->menu.font_ascent + mw->menu.font_descent;
1709 cascade_button_size (XlwMenuWidget mw,
1712 unsigned int *toggle_width,
1713 unsigned int *label_width,
1714 unsigned int *arrow_width,
1715 unsigned int *height)
1718 label_button_size (mw, val, in_menubar,
1719 toggle_width, label_width, arrow_width,
1721 /* we have a pull aside arrow */
1724 *arrow_width += arrow_decoration_height (mw) + mw->menu.column_spacing;
1729 cascade_button_draw (XlwMenuWidget mw,
1732 Boolean highlighted,
1736 unsigned int height,
1737 unsigned int label_offset,
1738 unsigned int binding_offset)
1742 /* Draw the label string. */
1743 label_button_draw (mw, val, in_menubar, highlighted,
1744 window, x, y, width, height, label_offset,
1747 /* Draw the pull aside arrow */
1748 if (!in_menubar && val->contents)
1751 unsigned int arrow_height = arrow_decoration_height (mw);
1753 y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin +
1754 (mw->menu.font_ascent+mw->menu.font_descent - arrow_height)/2;
1756 if (!binding_offset)
1758 unsigned int s_width =
1759 string_width (mw, resource_widget_value (mw, val));
1762 label_offset = mw->menu.shadow_thickness +
1763 mw->menu.horizontal_margin;
1765 binding_offset = label_offset + s_width + mw->menu.shadow_thickness;
1768 arrow_decoration_draw (mw,
1770 x + binding_offset + mw->menu.column_spacing,
1776 /* Draw the shadow */
1780 type = SHADOW_BACKGROUND;
1782 shadow_draw (mw, window, x, y, width, height, type);
1786 toggle_decoration_height (XlwMenuWidget mw)
1789 if (mw->menu.indicator_size > 0)
1790 rv = mw->menu.indicator_size;
1792 rv = mw->menu.font_ascent;
1794 if (rv > (mw->menu.font_ascent + mw->menu.font_descent))
1795 rv = mw->menu.font_ascent + mw->menu.font_descent;
1797 /* radio button can't be smaller than its border or a filling
1798 error will occur. */
1799 if (rv < 2 * mw->menu.shadow_thickness)
1800 rv = 2 * mw->menu.shadow_thickness;
1806 toggle_button_size (XlwMenuWidget mw,
1809 unsigned int *toggle_width,
1810 unsigned int *label_width,
1811 unsigned int *bindings_width,
1812 unsigned int *height)
1815 push_button_size (mw, val, in_menubar,
1816 toggle_width, label_width, bindings_width,
1818 /* we have a toggle */
1819 *toggle_width += toggle_decoration_height (mw) + mw->menu.column_spacing;
1823 toggle_button_draw (XlwMenuWidget mw,
1826 Boolean highlighted,
1830 unsigned int height,
1831 unsigned int label_tab,
1832 unsigned int binding_tab)
1836 unsigned int t_height = toggle_decoration_height (mw);
1838 /* Draw a toggle. */
1839 x_offset = mw->menu.shadow_thickness + mw->menu.horizontal_margin;
1840 y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin;
1841 y_offset += (mw->menu.font_ascent + mw->menu.font_descent - t_height)/2;
1843 toggle_decoration_draw (mw, window, x + x_offset, y + y_offset,
1844 t_height, val->selected);
1846 /* Draw the pushbutton parts. */
1847 push_button_draw (mw, val, in_menubar, highlighted, window, x, y, width,
1848 height, label_tab, binding_tab);
1852 radio_decoration_height (XlwMenuWidget mw)
1854 return toggle_decoration_height (mw);
1858 radio_button_draw (XlwMenuWidget mw,
1861 Boolean highlighted,
1865 unsigned int height,
1866 unsigned int label_tab,
1867 unsigned int binding_tab)
1871 unsigned int r_height = radio_decoration_height (mw);
1873 /* Draw a toggle. */
1874 x_offset = mw->menu.shadow_thickness + mw->menu.horizontal_margin;
1875 y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin;
1876 y_offset += (mw->menu.font_ascent + mw->menu.font_descent - r_height)/2;
1878 radio_decoration_draw (mw, window, x + x_offset, y + y_offset, r_height,
1881 /* Draw the pushbutton parts. */
1882 push_button_draw (mw, val, in_menubar, highlighted, window, x, y, width,
1883 height, label_tab, binding_tab);
1886 static struct _shadow_names
1893 { "singleLine", SHADOW_SINGLE_LINE },
1894 { "doubleLine", SHADOW_DOUBLE_LINE },
1895 { "singleDashedLine", SHADOW_SINGLE_DASHED_LINE },
1896 { "doubleDashedLine", SHADOW_DOUBLE_DASHED_LINE },
1897 { "noLine", SHADOW_NO_LINE },
1898 { "shadowEtchedIn", SHADOW_ETCHED_IN },
1899 { "shadowEtchedOut", SHADOW_ETCHED_OUT },
1900 { "shadowEtchedInDash", SHADOW_ETCHED_IN_DASH },
1901 { "shadowEtchedOutDash", SHADOW_ETCHED_OUT_DASH },
1903 { "shadowDoubleEtchedIn", SHADOW_DOUBLE_ETCHED_IN },
1904 { "shadowDoubleEtchedOut", SHADOW_DOUBLE_ETCHED_OUT },
1905 { "shadowDoubleEtchedInDash", SHADOW_DOUBLE_ETCHED_IN_DASH },
1906 { "shadowDoubleEtchedOutDash", SHADOW_DOUBLE_ETCHED_OUT_DASH }
1910 separator_type (char *name)
1915 for (i = 0; i < (int) (XtNumber (shadow_names)); i++ )
1917 if (strcmp (name, shadow_names[i].name) == 0)
1918 return shadow_names[i].type;
1921 return SHADOW_BACKGROUND;
1925 separator_decoration_height (XlwMenuWidget mw, widget_value *val)
1928 switch (separator_type (val->value))
1930 case SHADOW_NO_LINE:
1931 case SHADOW_SINGLE_LINE:
1932 case SHADOW_SINGLE_DASHED_LINE:
1934 case SHADOW_DOUBLE_LINE:
1935 case SHADOW_DOUBLE_DASHED_LINE:
1937 case SHADOW_DOUBLE_ETCHED_OUT:
1938 case SHADOW_DOUBLE_ETCHED_IN:
1939 case SHADOW_DOUBLE_ETCHED_OUT_DASH:
1940 case SHADOW_DOUBLE_ETCHED_IN_DASH:
1941 return (1 + 2 * mw->menu.shadow_thickness);
1942 case SHADOW_ETCHED_OUT:
1943 case SHADOW_ETCHED_IN:
1945 return mw->menu.shadow_thickness;
1950 separator_size (XlwMenuWidget mw,
1953 unsigned int *toggle_width,
1954 unsigned int *label_width,
1955 unsigned int *rest_width,
1956 unsigned int *height)
1958 *height = separator_decoration_height (mw, val);
1960 *toggle_width = *rest_width = 0;
1964 separator_draw (XlwMenuWidget mw,
1967 Boolean highlighted,
1971 unsigned int height,
1972 unsigned int label_tab,
1973 unsigned int binding_tab)
1975 unsigned int sep_width;
1982 separator_decoration_draw (mw,
1988 separator_type(val->value));
1992 pushright_size (XlwMenuWidget mw,
1995 unsigned int *toggle_width,
1996 unsigned int *label_width,
1997 unsigned int *rest_width,
1998 unsigned int *height)
2000 *height = *label_width = *toggle_width = *rest_width = 0;
2004 size_menu_item (XlwMenuWidget mw,
2007 unsigned int *toggle_width,
2008 unsigned int *label_width,
2009 unsigned int *rest_width,
2010 unsigned int *height)
2012 void (*function_ptr) (XlwMenuWidget _mw,
2014 Boolean _in_menubar,
2015 unsigned int *_toggle_width,
2016 unsigned int *_label_width,
2017 unsigned int *_rest_width,
2018 unsigned int *_height);
2020 switch (menu_item_type (val))
2024 function_ptr = toggle_button_size;
2026 case SEPARATOR_TYPE:
2027 function_ptr = separator_size;
2029 case INCREMENTAL_TYPE:
2031 function_ptr = cascade_button_size;
2034 function_ptr = push_button_size;
2036 case PUSHRIGHT_TYPE:
2037 function_ptr = pushright_size;
2041 function_ptr = label_button_size;
2045 (*function_ptr) (mw,
2055 display_menu_item (XlwMenuWidget mw,
2059 Boolean highlighted,
2061 Boolean just_compute)
2064 int x = where->x /* + mw->menu.shadow_thickness */ ;
2065 int y = where->y /* + mw->menu.shadow_thickness */ ;
2066 unsigned int toggle_width;
2067 unsigned int label_width;
2068 unsigned int binding_width;
2070 unsigned int height;
2071 unsigned int label_tab;
2072 unsigned int binding_tab;
2073 void (*function_ptr) (XlwMenuWidget _mw,
2075 Boolean _in_menubar,
2076 Boolean _highlighted,
2079 unsigned int _width,
2080 unsigned int _height,
2081 unsigned int _label_tab,
2082 unsigned int _binding_tab);
2084 size_menu_item (mw, val, horizontal,
2085 &toggle_width, &label_width, &binding_width, &height);
2089 width = toggle_width + label_width + binding_width;
2090 height = ws->height - 2 * mw->menu.shadow_thickness;
2094 width = ws->width - 2 * mw->menu.shadow_thickness;
2095 toggle_width = ws->toggle_width;
2096 label_width = ws->label_width;
2105 label_tab = toggle_width;
2106 binding_tab = toggle_width + label_width;
2108 switch (menu_item_type (val))
2111 function_ptr = toggle_button_draw;
2114 function_ptr = radio_button_draw;
2116 case SEPARATOR_TYPE:
2117 function_ptr = separator_draw;
2119 case INCREMENTAL_TYPE:
2121 function_ptr = cascade_button_draw;
2124 function_ptr = push_button_draw;
2127 function_ptr = label_button_draw;
2129 default: /* do no drawing */
2133 (*function_ptr) (mw,
2145 size_menu (XlwMenuWidget mw, int level)
2147 unsigned int toggle_width;
2148 unsigned int label_width;
2149 unsigned int rest_width;
2150 unsigned int height;
2151 unsigned int max_toggle_width = 0;
2152 unsigned int max_label_width = 0;
2153 unsigned int max_rest_width = 0;
2154 unsigned int max_height = 0;
2155 int horizontal_p = mw->menu.horizontal && (level == 0);
2159 if (level >= mw->menu.old_depth)
2162 ws = &mw->menu.windows [level];
2164 for (val = mw->menu.old_stack [level]->contents; val; val = val->next)
2175 max_label_width += toggle_width + label_width + rest_width;
2176 if (height > max_height)
2177 max_height = height;
2181 if (max_toggle_width < toggle_width)
2182 max_toggle_width = toggle_width;
2183 if (max_label_width < label_width)
2184 max_label_width = label_width;
2185 if (max_rest_width < rest_width)
2186 max_rest_width = rest_width;
2187 max_height += height;
2191 ws->height = max_height;
2192 ws->width = max_label_width + max_rest_width + max_toggle_width;
2193 ws->toggle_width = max_toggle_width;
2194 ws->label_width = max_label_width;
2196 ws->width += 2 * mw->menu.shadow_thickness;
2197 ws->height += 2 * mw->menu.shadow_thickness;
2201 display_menu (XlwMenuWidget mw, int level, Boolean just_compute_p,
2202 XPoint *highlighted_pos, XPoint *hit, widget_value **hit_return,
2203 widget_value *this, widget_value *that)
2206 widget_value *following_item;
2209 int horizontal_p = mw->menu.horizontal && (level == 0);
2211 int just_compute_this_one_p;
2213 if (level >= mw->menu.old_depth)
2216 if (level < mw->menu.old_depth - 1)
2217 following_item = mw->menu.old_stack [level + 1];
2220 if (lw_menu_accelerate
2221 && level == mw->menu.old_depth - 1
2222 && mw->menu.old_stack [level]->type == CASCADE_TYPE)
2223 just_compute_p = True;
2224 following_item = NULL;
2227 #if SLOPPY_TYPES == 1
2228 puts("===================================================================");
2229 print_widget_value (following_item, 1, 0);
2235 where.x = mw->menu.shadow_thickness;
2236 where.y = mw->menu.shadow_thickness;
2238 ws = &mw->menu.windows [level];
2239 for (val = mw->menu.old_stack [level]->contents; val; val = val->next)
2243 highlighted_p = (val == following_item);
2244 /* If this is the partition (the dummy item which says that menus
2245 after this should be flushright) then figure out how big the
2246 following items are. This means we walk down the tail of the
2247 list twice, but that's no big deal - it's short.
2249 if (horizontal_p && (menu_item_type (val) == PUSHRIGHT_TYPE))
2252 XPoint flushright_size;
2254 flushright_size.x = 0;
2255 flushright_size.y = 0;
2256 for (rest = val; rest; rest = rest->next)
2257 display_menu_item (mw, rest, ws, &flushright_size,
2258 highlighted_p, horizontal_p, True);
2259 new_x = ws->width - (flushright_size.x + mw->menu.shadow_thickness);
2260 if (new_x > where.x)
2262 /* We know what we need; don't draw this item. */
2266 if (highlighted_p && highlighted_pos)
2269 highlighted_pos->x = where.x;
2271 highlighted_pos->y = where.y;
2274 just_compute_this_one_p =
2275 just_compute_p || ((this || that) && val != this && val != that);
2279 display_menu_item (mw, val, ws, &where, highlighted_p, horizontal_p,
2280 just_compute_this_one_p);
2282 if (highlighted_p && highlighted_pos)
2285 highlighted_pos->y = ws->height;
2287 highlighted_pos->x = ws->width;
2290 if (hit && !*hit_return)
2292 if (horizontal_p && hit->x > start.x && hit->x <= where.x)
2294 else if (!horizontal_p && hit->y > start.y && hit->y <= where.y)
2299 where.y = mw->menu.shadow_thickness;
2301 where.x = mw->menu.shadow_thickness;
2304 /* Draw slab edges around menu */
2305 if (!just_compute_p)
2306 shadow_draw(mw, ws->window, 0, 0, ws->width, ws->height, SHADOW_OUT);
2311 set_new_state (XlwMenuWidget mw, widget_value *val, int level)
2315 mw->menu.new_depth = 0;
2316 for (i = 0; i < level; i++)
2317 push_new_stack (mw, mw->menu.old_stack [i]);
2319 push_new_stack (mw, val);
2323 make_windows_if_needed (XlwMenuWidget mw, int n)
2327 XSetWindowAttributes xswa;
2332 window_state *windows;
2335 if (mw->menu.windows_length >= n)
2338 root = RootWindowOfScreen (XtScreen(mw));
2339 /* grab the visual and depth from the nearest shell ancestor */
2340 visual = CopyFromParent;
2341 depth = CopyFromParent;
2343 while (visual == CopyFromParent && p)
2347 visual = ((ShellWidget)p)->shell.visual;
2348 depth = p->core.depth;
2353 xswa.save_under = True;
2354 xswa.override_redirect = True;
2355 xswa.background_pixel = mw->core.background_pixel;
2356 xswa.border_pixel = mw->core.border_pixel;
2357 xswa.event_mask = (ExposureMask | ButtonMotionMask
2358 | ButtonReleaseMask | ButtonPressMask);
2359 xswa.cursor = mw->menu.cursor_shape;
2360 xswa.colormap = mw->core.colormap;
2361 mask = CWSaveUnder | CWOverrideRedirect | CWBackPixel | CWBorderPixel
2362 | CWEventMask | CWCursor | CWColormap;
2364 if (mw->menu.use_backing_store)
2366 xswa.backing_store = Always;
2367 mask |= CWBackingStore;
2370 if (!mw->menu.windows)
2373 (window_state *) XtMalloc (n * sizeof (window_state));
2379 (window_state *) XtRealloc ((char *) mw->menu.windows,
2380 n * sizeof (window_state));
2381 start_at = mw->menu.windows_length;
2383 mw->menu.windows_length = n;
2385 windows = mw->menu.windows;
2387 for (i = start_at; i < n; i++)
2391 windows [i].width = 1;
2392 windows [i].height = 1;
2393 windows [i].window =
2394 XCreateWindow (XtDisplay (mw),
2397 0, depth, CopyFromParent, visual, mask, &xswa);
2401 /* Make the window fit in the screen */
2403 fit_to_screen (XlwMenuWidget mw, window_state *ws, window_state *previous_ws,
2404 Boolean horizontal_p)
2406 int screen_width = WidthOfScreen (XtScreen (mw));
2407 int screen_height = HeightOfScreen (XtScreen (mw));
2411 else if ((int) (ws->x + ws->width) > screen_width)
2414 ws->x = previous_ws->x - ws->width;
2417 ws->x = screen_width - ws->width;
2419 /* This check is to make sure we cut off the right side
2420 instead of the left side if the menu is wider than the
2428 else if ((int) (ws->y + ws->height) > screen_height)
2432 /* A pulldown must either be entirely above or below the menubar.
2433 If we're here, the pulldown doesn't fit below the menubar, so
2434 let's determine if it will fit above the menubar.
2435 Only put it above if there is more room above than below.
2436 Note shadow_thickness offset to allow for slab surround.
2438 if (ws->y > (screen_height / 2))
2439 ws->y = previous_ws->y - ws->height + mw->menu.shadow_thickness;
2443 ws->y = screen_height - ws->height;
2444 /* if it's taller than the screen, display the topmost part
2445 that will fit, beginning at the top of the screen. */
2452 /* Updates old_stack from new_stack and redisplays. */
2454 remap_menubar (XlwMenuWidget mw)
2458 XPoint selection_position;
2459 int old_depth = mw->menu.old_depth;
2460 int new_depth = mw->menu.new_depth;
2461 widget_value **old_stack;
2462 widget_value **new_stack;
2463 window_state *windows;
2464 widget_value *old_selection;
2465 widget_value *new_selection;
2467 /* Check that enough windows and old_stack are ready. */
2468 make_windows_if_needed (mw, new_depth);
2469 make_old_stack_space (mw, new_depth);
2470 windows = mw->menu.windows;
2471 old_stack = mw->menu.old_stack;
2472 new_stack = mw->menu.new_stack;
2474 /* compute the last identical different entry */
2475 for (i = 1; i < old_depth && i < new_depth; i++)
2476 if (old_stack [i] != new_stack [i])
2480 if (lw_menu_accelerate
2482 && last_same == old_depth - 1
2483 && old_stack [last_same]->contents)
2486 /* Memorize the previously selected item to be able to refresh it */
2487 old_selection = last_same + 1 < old_depth ? old_stack [last_same + 1] : NULL;
2488 new_selection = last_same + 1 < new_depth ? new_stack [last_same + 1] : NULL;
2490 /* updates old_state from new_state. It has to be done now because
2491 display_menu (called below) uses the old_stack to know what to display. */
2492 for (i = last_same + 1; i < new_depth; i++)
2493 old_stack [i] = new_stack [i];
2495 mw->menu.old_depth = new_depth;
2497 /* refresh the last selection */
2498 selection_position.x = 0;
2499 selection_position.y = 0;
2500 display_menu (mw, last_same, new_selection == old_selection,
2501 &selection_position, NULL, NULL, old_selection, new_selection);
2503 /* Now popup the new menus */
2504 for (i = last_same + 1; i < new_depth && new_stack [i]->contents; i++)
2506 window_state *previous_ws = &windows [i - 1];
2507 window_state *ws = &windows [i];
2509 if (lw_menu_accelerate && i == new_depth - 1)
2512 ws->x = previous_ws->x + selection_position.x;
2513 ws->y = previous_ws->y + selection_position.y;
2515 /* take into account the slab around the new menu */
2516 ws->y -= mw->menu.shadow_thickness;
2519 widget_value *val = mw->menu.old_stack [i];
2520 if (val->contents->type == INCREMENTAL_TYPE)
2522 /* okay, we're now doing a lisp callback to incrementally generate
2523 more of the menu. */
2524 XtCallCallbackList ((Widget)mw,
2526 (XtPointer)val->contents);
2532 fit_to_screen (mw, ws, previous_ws, mw->menu.horizontal && i == 1);
2534 XClearWindow (XtDisplay (mw), ws->window);
2535 XMoveResizeWindow (XtDisplay (mw), ws->window, ws->x, ws->y,
2536 ws->width, ws->height);
2537 XMapRaised (XtDisplay (mw), ws->window);
2538 display_menu (mw, i, False, &selection_position, NULL, NULL, NULL, NULL);
2541 /* unmap the menus that popped down */
2543 last_same = new_depth;
2544 if (lw_menu_accelerate
2546 && new_stack [last_same - 1]->contents)
2549 for (i = last_same - 1; i < old_depth; i++)
2550 if (i >= last_same || !new_stack [i]->contents)
2551 XUnmapWindow (XtDisplay (mw), windows [i].window);
2555 motion_event_is_in_menu (XlwMenuWidget mw, XMotionEvent *ev, int level,
2556 XPoint *relative_pos)
2558 window_state *ws = &mw->menu.windows [level];
2559 int x = level == 0 ? ws->x : ws->x + mw->menu.shadow_thickness;
2560 int y = level == 0 ? ws->y : ws->y + mw->menu.shadow_thickness;
2561 relative_pos->x = ev->x_root - x;
2562 relative_pos->y = ev->y_root - y;
2563 return (x < ev->x_root && ev->x_root < (int) (x + ws->width) &&
2564 y < ev->y_root && ev->y_root < (int) (y + ws->height));
2568 map_event_to_widget_value (XlwMenuWidget mw, XMotionEvent *ev,
2569 widget_value **val_ptr, int *level,
2570 Boolean *inside_menu)
2573 XPoint relative_pos;
2577 *inside_menu = False;
2579 /* Find the window */
2581 for (i = mw->menu.old_depth - 1; i >= 0; i--)
2583 for (i = 0; i <= mw->menu.old_depth - 1; i++)
2586 ws = &mw->menu.windows [i];
2587 if (ws && motion_event_is_in_menu (mw, ev, i, &relative_pos))
2589 *inside_menu = True; /* special logic for menubar below... */
2590 if ((ev->type == ButtonPress) ||
2593 display_menu (mw, i, True, NULL, &relative_pos,
2594 val_ptr, NULL, NULL);
2598 *inside_menu = True;
2601 else if (mw->menu.horizontal || i == 0)
2603 /* if we're clicking on empty part of the menubar, then
2604 unpost the stay-up menu */
2605 *inside_menu = False;
2615 make_drawing_gcs (XlwMenuWidget mw)
2618 unsigned long flags = (GCFont | GCForeground | GCBackground);
2621 xgcv.font = default_font_of_font_list (mw->menu.font_list)->fid;
2623 xgcv.font = mw->menu.font->fid;
2626 xgcv.foreground = mw->core.background_pixel;
2627 xgcv.background = mw->menu.foreground;
2628 mw->menu.background_gc = XtGetGC ((Widget) mw, flags, &xgcv);
2630 xgcv.foreground = mw->menu.foreground;
2631 xgcv.background = mw->core.background_pixel;
2632 mw->menu.foreground_gc = XtGetGC ((Widget) mw, flags, &xgcv);
2634 if (mw->menu.select_color != (Pixel)-1)
2636 xgcv.foreground = mw->menu.select_color;
2640 Display *dpy = XtDisplay(mw);
2641 if (CellsOfScreen(DefaultScreenOfDisplay(dpy)) <= 2)
2643 xgcv.foreground = mw->menu.foreground;
2648 Colormap cmap = mw->core.colormap;
2649 xcolor.pixel = mw->core.background_pixel;
2650 XQueryColor (dpy, cmap, &xcolor);
2651 xcolor.red = (xcolor.red * 17) / 20;
2652 xcolor.green = (xcolor.green * 17) / 20;
2653 xcolor.blue = (xcolor.blue * 17) / 20;
2654 if (allocate_nearest_color (dpy, cmap, &xcolor))
2655 xgcv.foreground = xcolor.pixel;
2658 xgcv.background = mw->core.background_pixel;
2659 mw->menu.select_gc = XtGetGC ((Widget)mw, flags, &xgcv);
2661 xgcv.foreground = mw->menu.foreground;
2662 xgcv.background = mw->core.background_pixel;
2663 xgcv.fill_style = FillStippled;
2664 xgcv.stipple = mw->menu.gray_pixmap;
2665 mw->menu.inactive_gc = XtGetGC ((Widget)mw,
2666 (flags | GCFillStyle | GCStipple),
2669 xgcv.foreground = mw->menu.highlight_foreground;
2670 xgcv.background = mw->core.background_pixel;
2671 mw->menu.highlight_gc = XtGetGC ((Widget)mw, flags, &xgcv);
2673 xgcv.foreground = mw->menu.title_foreground;
2674 xgcv.background = mw->core.background_pixel;
2675 mw->menu.title_gc = XtGetGC ((Widget)mw, flags, &xgcv);
2677 xgcv.foreground = mw->menu.button_foreground;
2678 xgcv.background = mw->core.background_pixel;
2679 mw->menu.button_gc = XtGetGC ((Widget)mw, flags, &xgcv);
2681 xgcv.fill_style = FillStippled;
2682 xgcv.stipple = mw->menu.gray_pixmap;
2683 mw->menu.inactive_button_gc = XtGetGC ((Widget)mw,
2684 (flags | GCFillStyle | GCStipple),
2689 release_drawing_gcs (XlwMenuWidget mw)
2691 XtReleaseGC ((Widget) mw, mw->menu.foreground_gc);
2692 XtReleaseGC ((Widget) mw, mw->menu.button_gc);
2693 XtReleaseGC ((Widget) mw, mw->menu.highlight_gc);
2694 XtReleaseGC ((Widget) mw, mw->menu.title_gc);
2695 XtReleaseGC ((Widget) mw, mw->menu.inactive_gc);
2696 XtReleaseGC ((Widget) mw, mw->menu.inactive_button_gc);
2697 XtReleaseGC ((Widget) mw, mw->menu.background_gc);
2698 XtReleaseGC ((Widget) mw, mw->menu.select_gc);
2699 /* let's get some segvs if we try to use these... */
2700 mw->menu.foreground_gc = (GC) -1;
2701 mw->menu.button_gc = (GC) -1;
2702 mw->menu.highlight_gc = (GC) -1;
2703 mw->menu.title_gc = (GC) -1;
2704 mw->menu.inactive_gc = (GC) -1;
2705 mw->menu.inactive_button_gc = (GC) -1;
2706 mw->menu.background_gc = (GC) -1;
2707 mw->menu.select_gc = (GC) -1;
2710 #define MINL(x,y) ((((unsigned long) (x)) < ((unsigned long) (y))) \
2711 ? ((unsigned long) (x)) : ((unsigned long) (y)))
2714 make_shadow_gcs (XlwMenuWidget mw)
2717 unsigned long pm = 0;
2718 Display *dpy = XtDisplay ((Widget) mw);
2719 Colormap cmap = mw->core.colormap;
2721 int top_frobbed = 0, bottom_frobbed = 0;
2723 if (mw->menu.top_shadow_color == (Pixel) (-1))
2724 mw->menu.top_shadow_color = mw->core.background_pixel;
2725 if (mw->menu.bottom_shadow_color == (Pixel) (-1))
2726 mw->menu.bottom_shadow_color = mw->menu.foreground;
2728 if (mw->menu.top_shadow_color == mw->core.background_pixel ||
2729 mw->menu.top_shadow_color == mw->menu.foreground)
2731 topc.pixel = mw->core.background_pixel;
2732 XQueryColor (dpy, cmap, &topc);
2733 /* don't overflow/wrap! */
2734 topc.red = MINL (65535, topc.red * 1.2);
2735 topc.green = MINL (65535, topc.green * 1.2);
2736 topc.blue = MINL (65535, topc.blue * 1.2);
2737 if (allocate_nearest_color (dpy, cmap, &topc))
2739 if (topc.pixel == mw->core.background_pixel)
2741 XFreeColors( dpy, cmap, &topc.pixel, 1, 0);
2742 topc.red = MINL (65535, topc.red + 0x8000);
2743 topc.green = MINL (65535, topc.green + 0x8000);
2744 topc.blue = MINL (65535, topc.blue + 0x8000);
2745 if (allocate_nearest_color (dpy, cmap, &topc))
2747 mw->menu.top_shadow_color = topc.pixel;
2752 mw->menu.top_shadow_color = topc.pixel;
2758 if (mw->menu.bottom_shadow_color == mw->menu.foreground ||
2759 mw->menu.bottom_shadow_color == mw->core.background_pixel)
2761 botc.pixel = mw->core.background_pixel;
2762 XQueryColor (dpy, cmap, &botc);
2763 botc.red = (botc.red * 3) / 5;
2764 botc.green = (botc.green * 3) / 5;
2765 botc.blue = (botc.blue * 3) / 5;
2766 if (allocate_nearest_color (dpy, cmap, &botc))
2768 if (botc.pixel == mw->core.background_pixel)
2770 XFreeColors (dpy, cmap, &botc.pixel, 1, 0);
2771 botc.red = MINL (65535, botc.red + 0x4000);
2772 botc.green = MINL (65535, botc.green + 0x4000);
2773 botc.blue = MINL (65535, botc.blue + 0x4000);
2774 if (allocate_nearest_color (dpy, cmap, &botc))
2776 mw->menu.bottom_shadow_color = botc.pixel;
2781 mw->menu.bottom_shadow_color = botc.pixel;
2788 if (top_frobbed && bottom_frobbed)
2790 int top_avg = ((topc.red / 3) + (topc.green / 3) + (topc.blue / 3));
2791 int bot_avg = ((botc.red / 3) + (botc.green / 3) + (botc.blue / 3));
2792 if (bot_avg > top_avg)
2794 Pixel tmp = mw->menu.top_shadow_color;
2795 mw->menu.top_shadow_color = mw->menu.bottom_shadow_color;
2796 mw->menu.bottom_shadow_color = tmp;
2798 else if (topc.pixel == botc.pixel)
2800 if (botc.pixel == mw->menu.foreground)
2801 mw->menu.top_shadow_color = mw->core.background_pixel;
2803 mw->menu.bottom_shadow_color = mw->menu.foreground;
2807 if (!mw->menu.top_shadow_pixmap &&
2808 mw->menu.top_shadow_color == mw->core.background_pixel)
2810 mw->menu.top_shadow_pixmap = mw->menu.gray_pixmap;
2811 mw->menu.top_shadow_color = mw->menu.foreground;
2813 if (!mw->menu.bottom_shadow_pixmap &&
2814 mw->menu.bottom_shadow_color == mw->core.background_pixel)
2816 mw->menu.bottom_shadow_pixmap = mw->menu.gray_pixmap;
2817 mw->menu.bottom_shadow_color = mw->menu.foreground;
2820 xgcv.fill_style = FillOpaqueStippled;
2821 xgcv.foreground = mw->menu.top_shadow_color;
2822 xgcv.background = mw->core.background_pixel;
2823 /* xgcv.stipple = mw->menu.top_shadow_pixmap; gtb */
2824 if (mw->menu.top_shadow_pixmap &&
2825 mw->menu.top_shadow_pixmap != XmUNSPECIFIED_PIXMAP)
2826 xgcv.stipple = mw->menu.top_shadow_pixmap;
2829 pm = (xgcv.stipple ? GCStipple|GCFillStyle : 0);
2830 mw->menu.shadow_top_gc =
2831 XtGetGC((Widget)mw, GCForeground|GCBackground|pm, &xgcv);
2833 xgcv.foreground = mw->menu.bottom_shadow_color;
2834 /* xgcv.stipple = mw->menu.bottom_shadow_pixmap; gtb */
2835 if (mw->menu.bottom_shadow_pixmap &&
2836 mw->menu.bottom_shadow_pixmap != XmUNSPECIFIED_PIXMAP)
2837 xgcv.stipple = mw->menu.bottom_shadow_pixmap;
2840 pm = (xgcv.stipple ? GCStipple|GCFillStyle : 0);
2841 mw->menu.shadow_bottom_gc =
2842 XtGetGC ((Widget)mw, GCForeground|GCBackground|pm, &xgcv);
2847 release_shadow_gcs (XlwMenuWidget mw)
2849 XtReleaseGC ((Widget) mw, mw->menu.shadow_top_gc);
2850 XtReleaseGC ((Widget) mw, mw->menu.shadow_bottom_gc);
2855 extract_font_extents (XlwMenuWidget mw)
2858 /* Find the maximal ascent/descent of the fonts in the font list
2859 so that all menu items can be the same height... */
2860 mw->menu.font_ascent = 0;
2861 mw->menu.font_descent = 0;
2864 XmFontContext context;
2865 #if (XmVersion >= 1002)
2866 XmFontListEntry fontentry;
2868 XmStringCharSet charset;
2872 if (! XmFontListInitFontContext (&context, mw->menu.font_list))
2874 #if (XmVersion >= 1002)
2875 /* There is a BUG in the 1.2 version of XmFontListGetNextFont() (or more
2876 specifically, in _XmGetFirstFont()) that can cause a null pointer to be
2877 passed to XFontsOfFontSet. Use XmFontListNextEntry(), which is the
2878 newer equivalent, instead. Also, it supports font sets, and the
2879 older function doesn't. */
2880 while ((fontentry = XmFontListNextEntry (context)))
2884 XtPointer one_of_them = XmFontListEntryGetFont (fontentry, &rettype);
2885 if (rettype == XmFONT_IS_FONTSET)
2887 XFontSet fontset = (XFontSet) one_of_them;
2888 XFontStruct **fontstruct_list;
2889 char **fontname_list;
2890 int fontcount = XFontsOfFontSet (fontset, &fontstruct_list,
2892 while (--fontcount >= 0)
2894 font = fontstruct_list[fontcount];
2895 if (font->ascent > (int) mw->menu.font_ascent)
2896 mw->menu.font_ascent = font->ascent;
2897 if (font->descent > (int) mw->menu.font_descent)
2898 mw->menu.font_descent = font->descent;
2901 else /* XmFONT_IS_FONT */
2903 font = (XFontStruct *) one_of_them;
2904 if (font->ascent > (int) mw->menu.font_ascent)
2905 mw->menu.font_ascent = font->ascent;
2906 if (font->descent > (int) mw->menu.font_descent)
2907 mw->menu.font_descent = font->descent;
2910 #else /* motif 1.1 */
2911 while (XmFontListGetNextFont (context, &charset, &font))
2913 if (font->ascent > (int) mw->menu.font_ascent)
2914 mw->menu.font_ascent = font->ascent;
2915 if (font->descent > (int) mw->menu.font_descent)
2916 mw->menu.font_descent = font->descent;
2919 #endif /* Motif version */
2920 XmFontListFreeFontContext (context);
2922 #else /* Not Motif */
2923 # ifdef USE_XFONTSET
2924 XFontStruct **fontstruct_list;
2925 char **fontname_list;
2927 int fontcount = XFontsOfFontSet(mw->menu.font_set, &fontstruct_list,
2929 mw->menu.font_ascent = 0;
2930 mw->menu.font_descent = 0;
2931 # if 0 /* nasty, personal debug, Kazz */
2932 fprintf(stderr, "fontSet count is %d\n", fontcount);
2934 while (--fontcount >= 0) {
2935 font = fontstruct_list[fontcount];
2936 if (font->ascent > (int) mw->menu.font_ascent)
2937 mw->menu.font_ascent = font->ascent;
2938 if (font->descent > (int) mw->menu.font_descent)
2939 mw->menu.font_descent = font->descent;
2941 # else /* ! USE_XFONTSET */
2942 mw->menu.font_ascent = mw->menu.font->ascent;
2943 mw->menu.font_descent = mw->menu.font->descent;
2945 #endif /* NEED_MOTIF */
2949 static XFontStruct *
2950 default_font_of_font_list (XmFontList font_list)
2952 XFontStruct *font = 0;
2954 /* Xm/Label.c does this: */
2955 _XmFontListGetDefaultFont (font_list, &font);
2958 XmFontContext context;
2959 #if (XmVersion >= 1002)
2960 XmFontListEntry fontentry;
2962 XtPointer one_of_them;
2964 XmStringCharSet charset;
2967 if (! XmFontListInitFontContext (&context, font_list))
2969 #if (XmVersion >= 1002)
2970 /* There is a BUG in the 1.2 version of XmFontListGetNextFont() (or more
2971 specifically, in _XmGetFirstFont()) that can cause a null pointer to be
2972 passed to XFontsOfFontSet. Use XmFontListNextEntry(), which is the
2973 newer equivalent, instead. */
2974 fontentry = XmFontListNextEntry (context);
2975 one_of_them = XmFontListEntryGetFont (fontentry, &rettype);
2976 if (rettype == XmFONT_IS_FONTSET)
2978 XFontSet fontset = (XFontSet) one_of_them;
2979 XFontStruct **fontstruct_list;
2980 char **fontname_list;
2981 (void) XFontsOfFontSet (fontset, &fontstruct_list, &fontname_list);
2982 font = fontstruct_list[0];
2984 else /* XmFONT_IS_FONT */
2986 font = (XFontStruct *) one_of_them;
2989 if (! XmFontListGetNextFont (context, &charset, &font))
2993 XmFontListFreeFontContext (context);
2997 if (! font) abort ();
3000 #endif /* NEED_MOTIF */
3003 XlwMenuInitialize (Widget request, Widget new, ArgList args,
3006 /* Get the GCs and the widget size */
3007 XlwMenuWidget mw = (XlwMenuWidget)new;
3008 Window window = RootWindowOfScreen (DefaultScreenOfDisplay (XtDisplay (mw)));
3009 Display *display = XtDisplay (mw);
3011 /* mw->menu.cursor = XCreateFontCursor (display, mw->menu.cursor_shape); */
3012 mw->menu.cursor = mw->menu.cursor_shape;
3014 mw->menu.gray_pixmap =
3015 XCreatePixmapFromBitmapData (display, window, (char *) gray_bits,
3016 gray_width, gray_height, 1, 0, 1);
3019 /* #### Even if it's a kludge!!!, we should consider doing the same for
3021 /* The menu.font_list slot came from the *fontList resource (Motif standard.)
3022 The menu.font_list_2 slot came from the *font resource, for backward
3023 compatibility with older versions of this code, and consistency with the
3024 rest of emacs. If both font and fontList are specified, we use fontList.
3025 If only one is specified, we use that. If neither are specified, we
3026 use the "fallback" value. What a kludge!!!
3028 Note that this has the bug that a more general wildcard like "*fontList:"
3029 will override a more specific resource like "Emacs*menubar.font:". But
3030 I can't think of a way around that.
3032 if (mw->menu.font_list) /* if *fontList is specified, use that */
3034 else if (mw->menu.font_list_2) /* else if *font is specified, use that */
3035 mw->menu.font_list = mw->menu.font_list_2;
3036 else /* otherwise use default */
3037 mw->menu.font_list = mw->menu.fallback_font_list;
3040 make_drawing_gcs (mw);
3041 make_shadow_gcs (mw);
3042 extract_font_extents (mw);
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;
3415 Window parent,root = 0UL;
3416 Window *waste = NULL;
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;