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 TRUE, /* 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 < XtGetMultiClickTime (XtDisplay (w)));
356 string_width (XlwMenuWidget mw,
365 Dimension width, height;
366 XmStringExtent (mw->menu.font_list, s, &width, &height);
371 XmbTextExtents (mw->menu.font_set, s, strlen (s), &ri, &rl);
376 XTextExtents (mw->menu.font, s, strlen (s), &drop, &drop, &drop, &xcs);
378 # endif /* USE_XFONTSET */
382 static char massaged_resource_char[256];
385 initialize_massaged_resource_char (void)
388 for (j = 0; j < (int) sizeof (massaged_resource_char); j++)
390 if ((j >= 'a' && j <= 'z') ||
391 (j >= 'A' && j <= 'Z') ||
392 (j >= '0' && j <= '9') ||
395 massaged_resource_char[j] = (char) j;
397 massaged_resource_char ['_'] = '_';
398 massaged_resource_char ['+'] = 'P'; /* Convert C++ to cPP */
399 massaged_resource_char ['.'] = '_'; /* Convert Buffers... to buffers___ */
403 string_width_u (XlwMenuWidget mw,
412 Dimension width, height;
417 # else /* ! USE_XFONTSET */
429 if (!XmStringGetLtoR (string, XmFONTLIST_DEFAULT_TAG, &chars))
434 charslength = strlen (chars);
435 newchars = (char *) alloca (charslength + 1);
437 for (i = j = 0; chars[i] && (j < charslength); i++)
438 if (chars[i]=='%'&&chars[i+1]=='_')
441 newchars[j++] = chars[i];
445 newstring = XmStringLtoRCreate (newchars, XmFONTLIST_DEFAULT_TAG);
446 XmStringExtent (mw->menu.font_list, newstring, &width, &height);
447 XmStringFree (newstring);
452 XmbTextExtents (mw->menu.font_set, newchars, j, &ri, &rl);
454 # else /* ! USE_XFONTSET */
455 XTextExtents (mw->menu.font, newchars, j, &drop, &drop, &drop, &xcs);
457 # endif /* USE_XFONTSET */
462 massage_resource_name (const char *in, char *out)
464 /* Turn a random string into something suitable for using as a resource.
467 "Kill Buffer" -> "killBuffer"
468 "Find File..." -> "findFile___"
469 "Search and Replace..." -> "searchAndReplace___"
470 "C++ Mode Commands" -> "cppModeCommands"
472 Valid characters in a resource NAME component are: a-zA-Z0-9_
475 #ifdef PRINT_XLWMENU_RESOURCE_CONVERSIONS
476 /* Compile with -DPRINT_XLWMENU_RESOURCE_CONVERSIONS to generate a
477 translation file for menu localizations. */
478 char *save_in = in, *save_out = out;
481 Boolean firstp = True;
484 if (*in == '%' && *(in + 1) == '_')
490 if (*in == '%' && *(in + 1) == '%')
492 ch = massaged_resource_char[(unsigned char) *in++];
495 int int_ch = (int) (unsigned char) ch;
496 *out++ = firstp ? tolower (int_ch) : toupper (int_ch);
498 while ((ch = massaged_resource_char[(unsigned char) *in++])
501 if (!*(in-1)) /* Overshot the NULL byte? */
508 #ifdef PRINT_XLWMENU_RESOURCE_CONVERSIONS
509 printf ("! Emacs*XlwMenu.%s.labelString:\t%s\n", save_out, save_in);
510 printf ( "Emacs*XlwMenu.%s.labelString:\n", save_out);
517 { "labelString", "LabelString", XtRString, sizeof(String),
521 /* This function searches STRING for parameter inserts of the form:
523 padding is either space (' ') or dash ('-') meaning
524 padding to the left or right of the inserted parameter.
525 In essence, all %1 strings are replaced by VALUE in the return value.
526 The caller is expected to free the return value using XtFree().
527 %% means insert one % (like printf).
528 %1 means insert VALUE.
529 %-1 means insert VALUE followed by one space. The latter is
530 not inserted if VALUE is a zero length string.
533 parameterize_string (const char *string, const char *value)
537 unsigned int done = 0;
542 result = XtMalloc(1);
550 for (ntimes = 1, percent = string;
551 (percent = strchr (percent, '%'));
555 result = XtMalloc ((ntimes * strlen(value)) + strlen(string) + 4);
558 while ((percent = strchr (string, '%')))
560 unsigned int left_pad;
561 unsigned int right_pad;
564 if (percent[1] == '%')
565 { /* it's a real % */
566 strncat (result, string, 1 + percent - string); /* incl % */
567 string = &percent[2]; /* after the second '%' */
568 continue; /* with the while() loop */
574 for (p = &percent[1]; /* test *p inside the loop */ ; p++)
585 { /* param and terminator */
586 strncat (result, string, percent - string);
587 if (value[0] != '\0')
590 for (i = 0; i < left_pad; i++)
591 strcat (result, " ");
592 strcat (result, value);
593 for (i = 0; i < right_pad; i++)
594 strcat (result, " ");
596 string = &p[1]; /* after the '1' */
597 done++; /* no need to do old way */
598 break; /* out of for() loop */
601 { /* bogus, copy the format as is */
602 /* out of for() loop */
603 strncat (result, string, 1 + p - string);
604 string = (*p ? &p[1] : p);
610 /* Copy the tail of the string */
611 strcat (result, string);
613 /* If we have not processed a % string, and we have a value, tail it. */
614 if (!done && value[0] != '\0')
616 strcat (result, " ");
617 strcat (result, value);
626 resource_widget_value (XlwMenuWidget mw, widget_value *val)
628 if (!val->toolkit_data)
630 char *resourced_name = NULL;
631 char *converted_name, *str;
632 XmString complete_name;
633 char massaged_name [1024];
635 if (mw->menu.lookup_labels)
637 /* Convert value style name into resource style name.
638 eg: "Free Willy" becomes "freeWilly" */
639 massage_resource_name (val->name, massaged_name);
641 /* If we have a value (parameter) see if we can find a "Named"
645 char named_name[1024];
646 sprintf (named_name, "%sNamed", massaged_name);
647 XtGetSubresources ((Widget) mw,
648 (XtPointer) &resourced_name,
649 named_name, named_name,
650 nameResource, 1, NULL, 0);
653 /* If nothing yet, try to load from the massaged name. */
656 XtGetSubresources ((Widget) mw,
657 (XtPointer) &resourced_name,
658 massaged_name, massaged_name,
659 nameResource, 1, NULL, 0);
661 } /* if (mw->menu.lookup_labels) */
663 /* Still nothing yet, use the name as the value. */
665 resourced_name = val->name;
667 /* Parameterize the string. */
668 converted_name = parameterize_string (resourced_name, val->value);
670 /* nuke newline characters to prevent menubar screwups */
671 for ( str = converted_name ; *str ; str++ )
673 if (str[0] == '\n') str[0] = ' ';
676 /* Improve OSF's bottom line. */
677 #if (XmVersion >= 1002)
678 complete_name = XmStringCreateLocalized (converted_name);
680 complete_name = XmStringCreateLtoR (converted_name,
681 XmSTRING_DEFAULT_CHARSET);
683 XtFree (converted_name);
685 val->toolkit_data = complete_name;
686 val->free_toolkit_data = True;
688 return (XmString) val->toolkit_data;
693 /* These two routines should be a separate file..djw */
695 xlw_create_localized_string (Widget w,
706 XtGetSubresources (w,
716 return parameterize_string (string, arg);
720 xlw_create_localized_xmstring (Widget w,
725 char * string = xlw_create_localized_string (w, name, args, nargs);
726 XmString xm_string = XmStringCreateLtoR (string, XmSTRING_DEFAULT_CHARSET);
735 resource_widget_value (XlwMenuWidget mw, widget_value *val)
737 if (!val->toolkit_data)
739 char *resourced_name = NULL;
741 char massaged_name [1024];
743 if (mw->menu.lookup_labels)
745 massage_resource_name (val->name, massaged_name);
747 XtGetSubresources ((Widget) mw,
748 (XtPointer) &resourced_name,
749 massaged_name, massaged_name,
750 nameResource, 1, NULL, 0);
753 resourced_name = val->name;
755 complete_name = parameterize_string (resourced_name, val->value);
757 val->toolkit_data = complete_name;
758 /* nuke newline characters to prevent menubar screwups */
759 for ( ; *complete_name ; complete_name++ )
761 if (complete_name[0] == '\n')
762 complete_name[0] = ' ';
764 val->free_toolkit_data = True;
766 return (char *) val->toolkit_data;
771 /* Code for drawing strings. */
773 string_draw (XlwMenuWidget mw,
785 XmStringDraw (XtDisplay (mw), window,
789 1000, /* ???? width */
790 XmALIGNMENT_BEGINNING,
791 0, /* ???? layout_direction */
795 XmbDrawString (XtDisplay (mw), window, mw->menu.font_set, gc,
796 x, y + mw->menu.font_ascent, string, strlen (string));
798 XDrawString (XtDisplay (mw), window, gc,
799 x, y + mw->menu.font_ascent, string, strlen (string));
800 # endif /* USE_XFONTSET */
817 Dimension width, height;
825 newstring = XmStringLtoRCreate (&string[start], XmFONTLIST_DEFAULT_TAG);
827 XtDisplay (mw), window,
831 1000, /* ???? width */
832 XmALIGNMENT_BEGINNING,
833 0, /* ???? layout_direction */
836 XmStringExtent (mw->menu.font_list, newstring, &width, &height);
837 XmStringFree (newstring);
847 XtDisplay (mw), window, mw->menu.font_set, gc,
848 x, y + mw->menu.font_ascent, &string[start], end - start);
850 mw->menu.font_set, &string[start], end - start, &ri, &rl);
859 XtDisplay (mw), window, gc,
860 x, y + mw->menu.font_ascent, &string[start], end - start);
862 mw->menu.font, &string[start], end - start,
863 &drop, &drop, &drop, &xcs);
870 string_draw_u (XlwMenuWidget mw,
886 if (!XmStringGetLtoR (string, XmFONTLIST_DEFAULT_TAG, &chars))
891 for (i=0; chars[i]; ++i) {
892 if (chars[i] == '%' && chars[i+1] == '_') {
895 x += string_draw_range (mw, window, x, y, gc, chars, s, i);
896 w = string_draw_range (mw, window, x, y, gc, chars, i+2, i+3);
898 /* underline next character */
899 XDrawLine (XtDisplay (mw), window, gc, x - 1,
900 y + mw->menu.font_ascent + 1,
901 x + w - 1, y + mw->menu.font_ascent + 1 );
907 x += string_draw_range (mw, window, x, y, gc, chars, s, i);
914 binding_draw (XlwMenuWidget mw, Window w, int x, int y, GC gc, char *value)
917 XmString xm_value = XmStringCreateLtoR(value, XmSTRING_DEFAULT_CHARSET);
918 string_draw (mw, w, x, y, gc, xm_value);
919 XmStringFree (xm_value);
921 string_draw (mw, w, x, y, gc, value);
925 /* Low level code for drawing 3-D edges. */
927 shadow_rectangle_draw (Display *dpy,
934 unsigned int thickness)
943 points [1].x = x + width;
945 points [2].x = x + width - thickness;
946 points [2].y = y + thickness;
948 points [3].y = y + thickness;
949 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
951 points [0].y = y + thickness;
953 points [1].y = y + height;
954 points [2].x = x + thickness;
955 points [2].y = y + height - thickness;
956 points [3].x = x + thickness;
957 points [3].y = y + thickness;
958 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
959 points [0].x = x + width;
961 points [1].x = x + width - thickness;
962 points [1].y = y + thickness;
963 points [2].x = x + width - thickness;
964 points [2].y = y + height - thickness;
965 points [3].x = x + width;
966 points [3].y = y + height - thickness;
967 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
969 points [0].y = y + height;
970 points [1].x = x + width;
971 points [1].y = y + height;
972 points [2].x = x + width;
973 points [2].y = y + height - thickness;
974 points [3].x = x + thickness;
975 points [3].y = y + height - thickness;
976 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
979 typedef enum e_shadow_type
981 /* these are Motif compliant */
987 SHADOW_ETCHED_OUT_DASH,
988 SHADOW_ETCHED_IN_DASH,
991 SHADOW_SINGLE_DASHED_LINE,
992 SHADOW_DOUBLE_DASHED_LINE,
994 /* these are all non-Motif */
995 SHADOW_DOUBLE_ETCHED_OUT,
996 SHADOW_DOUBLE_ETCHED_IN,
997 SHADOW_DOUBLE_ETCHED_OUT_DASH,
998 SHADOW_DOUBLE_ETCHED_IN_DASH
1002 shadow_draw (XlwMenuWidget mw,
1006 unsigned int height,
1009 Display *dpy = XtDisplay (mw);
1012 int thickness = mw->menu.shadow_thickness;
1016 Boolean etched = False;
1020 case SHADOW_BACKGROUND:
1021 top_gc = bottom_gc = mw->menu.background_gc;
1023 case SHADOW_ETCHED_IN:
1024 top_gc = mw->menu.shadow_bottom_gc;
1025 bottom_gc = mw->menu.shadow_top_gc;
1028 case SHADOW_ETCHED_OUT:
1029 top_gc = mw->menu.shadow_top_gc;
1030 bottom_gc = mw->menu.shadow_bottom_gc;
1034 top_gc = mw->menu.shadow_bottom_gc;
1035 bottom_gc = mw->menu.shadow_top_gc;
1039 top_gc = mw->menu.shadow_top_gc;
1040 bottom_gc = mw->menu.shadow_bottom_gc;
1046 unsigned int half = thickness/2;
1047 shadow_rectangle_draw (dpy,
1052 width - half, height - half,
1054 shadow_rectangle_draw (dpy,
1059 width - half , height - half,
1064 shadow_rectangle_draw (dpy,
1075 arrow_decoration_draw (XlwMenuWidget mw,
1081 Display *dpy = XtDisplay (mw);
1085 int thickness = mw->menu.shadow_thickness;
1088 int length = (int)((double)width * 0.87);
1089 int thick_med = (int)((double)thickness * 1.73);
1092 half_width = width/2 + 1;
1094 half_width = width/2;
1096 select_gc = mw->menu.background_gc;
1100 top_gc = mw->menu.shadow_bottom_gc;
1101 bottom_gc = mw->menu.shadow_top_gc;
1105 top_gc = mw->menu.shadow_top_gc;
1106 bottom_gc = mw->menu.shadow_bottom_gc;
1109 /* Fill internal area. We do this first so that the borders have a
1111 points [0].x = x + thickness;
1112 points [0].y = y + thickness;
1113 points [1].x = x + length - thickness;
1114 points [1].y = y + half_width;
1115 points [2].x = x + length - thickness;
1116 points [2].y = y + half_width + thickness;
1117 points [3].x = x + thickness;
1118 points [3].y = y + width - thickness;
1131 points [1].x = x + thickness;
1132 points [1].y = y + thick_med;
1133 points [2].x = x + thickness;
1134 points [2].y = y + width - thick_med;
1136 points [3].y = y + width;
1138 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
1142 points [0].y = y + width;
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 + width - thick_med;
1150 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
1155 points [1].x = x + length;
1156 points [1].y = y + half_width;
1157 points [2].x = x + length - (thickness + thickness);
1158 points [2].y = y + half_width;
1159 points [3].x = x + thickness;
1160 points [3].y = y + thick_med;
1162 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
1166 toggle_decoration_draw (XlwMenuWidget mw,
1172 Display *dpy = XtDisplay (mw);
1173 int thickness = mw->menu.shadow_thickness;
1175 GC select_gc = mw->menu.select_gc;
1182 /* Fill internal area. */
1184 XFillRectangle (dpy,
1189 width - (2*thickness),
1190 width - (2*thickness));
1192 shadow_draw (mw, window, x, y, width, width, type);
1196 radio_decoration_draw (XlwMenuWidget mw,
1202 Display *dpy = XtDisplay (mw);
1205 GC select_gc = mw->menu.select_gc;
1206 int thickness = mw->menu.shadow_thickness;
1216 half_width = width/2;
1220 top_gc = mw->menu.shadow_bottom_gc;
1221 bottom_gc = mw->menu.shadow_top_gc;
1225 top_gc = mw->menu.shadow_top_gc;
1226 bottom_gc = mw->menu.shadow_bottom_gc;
1230 /* Draw the bottom first, just in case the regions overlap.
1231 The top should cast the longer shadow. */
1232 points [0].x = x; /* left corner */
1233 points [0].y = y + half_width;
1234 points [1].x = x + half_width; /* bottom corner */
1235 points [1].y = y + width;
1236 points [2].x = x + half_width; /* bottom inside corner */
1237 points [2].y = y + width - thickness;
1238 points [3].x = x + thickness; /* left inside corner */
1239 points [3].y = y + half_width;
1241 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
1243 points [0].x = x + half_width; /* bottom corner */
1244 points [0].y = y + width;
1245 points [1].x = x + width; /* right corner */
1246 points [1].y = y + half_width;
1247 points [2].x = x + width - thickness; /* right inside corner */
1248 points [2].y = y + half_width;
1249 points [3].x = x + half_width; /* bottom inside corner */
1250 points [3].y = y + width - thickness;
1252 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
1254 points [0].x = x; /* left corner */
1255 points [0].y = y + half_width;
1256 points [1].x = x + half_width; /* top corner */
1258 points [2].x = x + half_width; /* top inside corner */
1259 points [2].y = y + thickness;
1260 points [3].x = x + thickness; /* left inside corner */
1261 points [3].y = y + half_width;
1263 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
1265 points [0].x = x + half_width; /* top corner */
1267 points [1].x = x + width; /* right corner */
1268 points [1].y = y + half_width;
1269 points [2].x = x + width - thickness; /* right inside corner */
1270 points [2].y = y + half_width;
1271 points [3].x = x + half_width; /* top inside corner */
1272 points [3].y = y + thickness;
1274 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
1276 /* Draw the bottom first, just in case the regions overlap.
1277 The top should cast the longer shadow. */
1279 points [npoints].x = x; /* left corner */
1280 points [npoints++].y = y + half_width;
1281 points [npoints].x = x + half_width; /* bottom corner */
1282 points [npoints++].y = y + width;
1283 points [npoints].x = x + width; /* right corner */
1284 points [npoints++].y = y + half_width;
1285 points [npoints].x = x + width - thickness; /* right inside corner */
1286 points [npoints++].y = y + half_width;
1287 points [npoints].x = x + half_width; /* bottom inside corner */
1288 points [npoints++].y = y + width - thickness;
1289 points [npoints].x = x + thickness; /* left inside corner */
1290 points [npoints++].y = y + half_width;
1292 XFillPolygon (dpy, window, bottom_gc,
1293 points, npoints, Nonconvex, CoordModeOrigin);
1297 points [npoints].x = x; /* left corner */
1298 points [npoints++].y = y + half_width;
1299 points [npoints].x = x + half_width; /* top corner */
1300 points [npoints++].y = y;
1301 points [npoints].x = x + width; /* right corner */
1302 points [npoints++].y = y + half_width;
1303 points [npoints].x = x + width - thickness; /* right inside corner */
1304 points [npoints++].y = y + half_width;
1305 points [npoints].x = x + half_width; /* top inside corner */
1306 points [npoints++].y = y + thickness;
1307 points [npoints].x = x + thickness; /* left inside corner */
1308 points [npoints++].y = y + half_width;
1310 XFillPolygon (dpy, window, top_gc, points, npoints, Nonconvex,
1315 /* Fill internal area. */
1318 points [0].x = x + thickness;
1319 points [0].y = y + half_width;
1320 points [1].x = x + half_width;
1321 points [1].y = y + thickness;
1322 points [2].x = x + width - thickness;
1323 points [2].y = y + half_width;
1324 points [3].x = x + half_width;
1325 points [3].y = y + width - thickness;
1337 separator_decoration_draw (XlwMenuWidget mw,
1344 Display *dpy = XtDisplay (mw);
1347 unsigned int offset = 0;
1348 unsigned int num_separators = 1;
1349 unsigned int top_line_thickness = 0;
1350 unsigned int bottom_line_thickness = 0;
1351 Boolean dashed = False;
1355 case SHADOW_NO_LINE: /* nothing to do */
1357 case SHADOW_DOUBLE_LINE:
1359 case SHADOW_SINGLE_LINE:
1360 top_gc = bottom_gc = mw->menu.foreground_gc;
1361 top_line_thickness = 1;
1363 case SHADOW_DOUBLE_DASHED_LINE:
1365 case SHADOW_SINGLE_DASHED_LINE:
1366 top_gc = bottom_gc = mw->menu.foreground_gc;
1367 top_line_thickness = 1;
1370 case SHADOW_DOUBLE_ETCHED_OUT_DASH:
1372 case SHADOW_ETCHED_OUT_DASH:
1373 top_gc = mw->menu.shadow_top_gc;
1374 bottom_gc = mw->menu.shadow_bottom_gc;
1375 top_line_thickness = mw->menu.shadow_thickness/2;
1376 bottom_line_thickness = mw->menu.shadow_thickness - top_line_thickness;
1379 case SHADOW_DOUBLE_ETCHED_IN_DASH:
1381 case SHADOW_ETCHED_IN_DASH:
1382 top_gc = mw->menu.shadow_bottom_gc;
1383 bottom_gc = mw->menu.shadow_top_gc;
1384 top_line_thickness = mw->menu.shadow_thickness/2;
1385 bottom_line_thickness = mw->menu.shadow_thickness - top_line_thickness;
1388 case SHADOW_DOUBLE_ETCHED_OUT:
1390 case SHADOW_ETCHED_OUT:
1391 top_gc = mw->menu.shadow_top_gc;
1392 bottom_gc = mw->menu.shadow_bottom_gc;
1393 top_line_thickness = mw->menu.shadow_thickness/2;
1394 bottom_line_thickness = mw->menu.shadow_thickness - top_line_thickness;
1396 case SHADOW_DOUBLE_ETCHED_IN:
1398 case SHADOW_ETCHED_IN:
1400 top_gc = mw->menu.shadow_bottom_gc;
1401 bottom_gc = mw->menu.shadow_top_gc;
1402 top_line_thickness = mw->menu.shadow_thickness/2;
1403 bottom_line_thickness = mw->menu.shadow_thickness - top_line_thickness;
1410 values.line_style = LineOnOffDash;
1411 if (top_line_thickness > 0)
1412 XChangeGC (dpy, top_gc, GCLineStyle, &values);
1413 if (bottom_line_thickness > 0 && bottom_gc != top_gc)
1414 XChangeGC (dpy, bottom_gc, GCLineStyle, &values);
1417 while (num_separators--)
1420 for (i = 0; i < top_line_thickness; i++)
1421 XDrawLine (dpy, window, top_gc, x, y + i, x + width, y + i);
1423 for (i = 0; i < bottom_line_thickness; i++)
1424 XDrawLine (dpy, window, bottom_gc,
1425 x, y + top_line_thickness + offset + i,
1426 x + width, y + top_line_thickness + offset + i);
1427 y += (top_line_thickness + offset + bottom_line_thickness + 1);
1433 values.line_style = LineSolid;
1434 if (top_line_thickness > 0)
1435 XChangeGC (dpy, top_gc, GCLineStyle, &values);
1436 if (bottom_line_thickness > 0 && bottom_gc != top_gc)
1437 XChangeGC (dpy, bottom_gc, GCLineStyle, &values);
1441 #define SLOPPY_TYPES 0 /* 0=off, 1=error check, 2=easy to please */
1443 #if SLOPPY_TYPES < 2
1445 static char *wv_types[] =
1459 print_widget_value (widget_value *wv, int just_one, int depth)
1463 for (i = 0; i < depth; i++)
1468 printf ("%s(null widget value pointer)\n", d);
1471 printf ("%stype: %s\n", d, wv_types [wv->type]);
1473 printf ("%sname: %s\n", d, (wv->name ? wv->name : "(null)"));
1475 if (wv->name) printf ("%sname: %s\n", d, wv->name);
1477 if (wv->value) printf ("%svalue: %s\n", d, wv->value);
1478 if (wv->key) printf ("%skey: %s\n", d, wv->key);
1479 printf ("%senabled: %d\n", d, wv->enabled);
1482 printf ("\n%scontents: \n", d);
1483 print_widget_value (wv->contents, 0, depth + 5);
1485 if (!just_one && wv->next)
1488 print_widget_value (wv->next, 0, depth);
1491 #endif /* SLOPPY_TYPES < 2 */
1494 all_dashes_p (char *s)
1497 if (!s || s[0] == '\0')
1499 for (p = s; *p == '-'; p++);
1501 if (*p == '!' || *p == '\0')
1505 #endif /* SLOPPY_TYPES */
1507 static widget_value_type
1508 menu_item_type (widget_value *val)
1510 if (val->type != UNSPECIFIED_TYPE)
1513 else if (all_dashes_p (val->name))
1514 return SEPARATOR_TYPE;
1515 else if (val->name && val->name[0] == '\0') /* push right */
1516 return PUSHRIGHT_TYPE;
1517 else if (val->contents) /* cascade */
1518 return CASCADE_TYPE;
1519 else if (val->call_data) /* push button */
1526 return UNSPECIFIED_TYPE; /* Not reached */
1531 label_button_size (XlwMenuWidget mw,
1534 unsigned int *toggle_width,
1535 unsigned int *label_width,
1536 unsigned int *bindings_width,
1537 unsigned int *height)
1539 *height = (mw->menu.font_ascent + mw->menu.font_descent +
1540 2 * mw->menu.vertical_margin +
1541 2 * mw->menu.shadow_thickness);
1542 /* no left column decoration */
1543 *toggle_width = mw->menu.horizontal_margin + mw->menu.shadow_thickness;
1545 *label_width = string_width_u (mw, resource_widget_value (mw, val));
1546 *bindings_width = mw->menu.horizontal_margin + mw->menu.shadow_thickness;
1550 label_button_draw (XlwMenuWidget mw,
1553 Boolean highlighted,
1557 unsigned int height,
1558 unsigned int label_offset,
1559 unsigned int binding_tab)
1561 int y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin;
1565 label_offset = mw->menu.shadow_thickness + mw->menu.horizontal_margin;
1567 if (highlighted && (in_menubar || val->contents))
1568 gc = mw->menu.highlight_gc;
1569 else if (in_menubar || val->contents)
1570 gc = mw->menu.foreground_gc;
1572 gc = mw->menu.title_gc;
1574 /* Draw the label string. */
1577 x + label_offset, y + y_offset,
1579 resource_widget_value (mw, val));
1583 push_button_size (XlwMenuWidget mw,
1586 unsigned int *toggle_width,
1587 unsigned int *label_width,
1588 unsigned int *bindings_width,
1589 unsigned int *height)
1592 label_button_size (mw, val, in_menubar,
1593 toggle_width, label_width, bindings_width,
1596 /* key bindings to display? */
1597 if (!in_menubar && val->key)
1601 XmString key = XmStringCreateLtoR (val->key, XmSTRING_DEFAULT_CHARSET);
1602 w = string_width (mw, key);
1605 char *key = val->key;
1606 w = string_width (mw, key);
1608 *bindings_width += w + mw->menu.column_spacing;
1613 push_button_draw (XlwMenuWidget mw,
1616 Boolean highlighted,
1620 unsigned int height,
1621 unsigned int label_offset,
1622 unsigned int binding_offset)
1624 int y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin;
1627 Boolean menu_pb = in_menubar && (menu_item_type (val) == BUTTON_TYPE);
1629 /* Draw the label string. */
1631 label_offset = mw->menu.shadow_thickness + mw->menu.horizontal_margin;
1636 gc = mw->menu.highlight_gc;
1638 gc = mw->menu.inactive_gc;
1643 gc = mw->menu.button_gc;
1645 gc = mw->menu.inactive_button_gc;
1650 gc = mw->menu.foreground_gc;
1652 gc = mw->menu.inactive_gc;
1657 x + label_offset, y + y_offset,
1659 resource_widget_value (mw, val));
1661 /* Draw the keybindings */
1664 if (!binding_offset)
1666 unsigned int s_width =
1667 string_width (mw, resource_widget_value (mw, val));
1668 binding_offset = label_offset + s_width + mw->menu.shadow_thickness;
1670 binding_draw (mw, window,
1671 x + binding_offset + mw->menu.column_spacing,
1672 y + y_offset, gc, val->key);
1675 /* Draw the shadow */
1681 type = (val->selected ? SHADOW_ETCHED_OUT : SHADOW_ETCHED_IN);
1688 type = SHADOW_BACKGROUND;
1691 shadow_draw (mw, window, x, y, width, height, type);
1695 arrow_decoration_height (XlwMenuWidget mw)
1697 int result = (mw->menu.font_ascent + mw->menu.font_descent) / 2;
1699 result += 2 * mw->menu.shadow_thickness;
1701 if (result > (mw->menu.font_ascent + mw->menu.font_descent))
1702 result = mw->menu.font_ascent + mw->menu.font_descent;
1708 cascade_button_size (XlwMenuWidget mw,
1711 unsigned int *toggle_width,
1712 unsigned int *label_width,
1713 unsigned int *arrow_width,
1714 unsigned int *height)
1717 label_button_size (mw, val, in_menubar,
1718 toggle_width, label_width, arrow_width,
1720 /* we have a pull aside arrow */
1723 *arrow_width += arrow_decoration_height (mw) + mw->menu.column_spacing;
1728 cascade_button_draw (XlwMenuWidget mw,
1731 Boolean highlighted,
1735 unsigned int height,
1736 unsigned int label_offset,
1737 unsigned int binding_offset)
1741 /* Draw the label string. */
1742 label_button_draw (mw, val, in_menubar, highlighted,
1743 window, x, y, width, height, label_offset,
1746 /* Draw the pull aside arrow */
1747 if (!in_menubar && val->contents)
1750 unsigned int arrow_height = arrow_decoration_height (mw);
1752 y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin +
1753 (mw->menu.font_ascent+mw->menu.font_descent - arrow_height)/2;
1755 if (!binding_offset)
1757 unsigned int s_width =
1758 string_width (mw, resource_widget_value (mw, val));
1761 label_offset = mw->menu.shadow_thickness +
1762 mw->menu.horizontal_margin;
1764 binding_offset = label_offset + s_width + mw->menu.shadow_thickness;
1767 arrow_decoration_draw (mw,
1769 x + binding_offset + mw->menu.column_spacing,
1775 /* Draw the shadow */
1779 type = SHADOW_BACKGROUND;
1781 shadow_draw (mw, window, x, y, width, height, type);
1785 toggle_decoration_height (XlwMenuWidget mw)
1788 if (mw->menu.indicator_size > 0)
1789 rv = mw->menu.indicator_size;
1791 rv = mw->menu.font_ascent;
1793 if (rv > (mw->menu.font_ascent + mw->menu.font_descent))
1794 rv = mw->menu.font_ascent + mw->menu.font_descent;
1796 /* radio button can't be smaller than its border or a filling
1797 error will occur. */
1798 if (rv < 2 * mw->menu.shadow_thickness)
1799 rv = 2 * mw->menu.shadow_thickness;
1805 toggle_button_size (XlwMenuWidget mw,
1808 unsigned int *toggle_width,
1809 unsigned int *label_width,
1810 unsigned int *bindings_width,
1811 unsigned int *height)
1814 push_button_size (mw, val, in_menubar,
1815 toggle_width, label_width, bindings_width,
1817 /* we have a toggle */
1818 *toggle_width += toggle_decoration_height (mw) + mw->menu.column_spacing;
1822 toggle_button_draw (XlwMenuWidget mw,
1825 Boolean highlighted,
1829 unsigned int height,
1830 unsigned int label_tab,
1831 unsigned int binding_tab)
1835 unsigned int t_height = toggle_decoration_height (mw);
1837 /* Draw a toggle. */
1838 x_offset = mw->menu.shadow_thickness + mw->menu.horizontal_margin;
1839 y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin;
1840 y_offset += (mw->menu.font_ascent + mw->menu.font_descent - t_height)/2;
1842 toggle_decoration_draw (mw, window, x + x_offset, y + y_offset,
1843 t_height, val->selected);
1845 /* Draw the pushbutton parts. */
1846 push_button_draw (mw, val, in_menubar, highlighted, window, x, y, width,
1847 height, label_tab, binding_tab);
1851 radio_decoration_height (XlwMenuWidget mw)
1853 return toggle_decoration_height (mw);
1857 radio_button_draw (XlwMenuWidget mw,
1860 Boolean highlighted,
1864 unsigned int height,
1865 unsigned int label_tab,
1866 unsigned int binding_tab)
1870 unsigned int r_height = radio_decoration_height (mw);
1872 /* Draw a toggle. */
1873 x_offset = mw->menu.shadow_thickness + mw->menu.horizontal_margin;
1874 y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin;
1875 y_offset += (mw->menu.font_ascent + mw->menu.font_descent - r_height)/2;
1877 radio_decoration_draw (mw, window, x + x_offset, y + y_offset, r_height,
1880 /* Draw the pushbutton parts. */
1881 push_button_draw (mw, val, in_menubar, highlighted, window, x, y, width,
1882 height, label_tab, binding_tab);
1885 static struct _shadow_names
1892 { "singleLine", SHADOW_SINGLE_LINE },
1893 { "doubleLine", SHADOW_DOUBLE_LINE },
1894 { "singleDashedLine", SHADOW_SINGLE_DASHED_LINE },
1895 { "doubleDashedLine", SHADOW_DOUBLE_DASHED_LINE },
1896 { "noLine", SHADOW_NO_LINE },
1897 { "shadowEtchedIn", SHADOW_ETCHED_IN },
1898 { "shadowEtchedOut", SHADOW_ETCHED_OUT },
1899 { "shadowEtchedInDash", SHADOW_ETCHED_IN_DASH },
1900 { "shadowEtchedOutDash", SHADOW_ETCHED_OUT_DASH },
1902 { "shadowDoubleEtchedIn", SHADOW_DOUBLE_ETCHED_IN },
1903 { "shadowDoubleEtchedOut", SHADOW_DOUBLE_ETCHED_OUT },
1904 { "shadowDoubleEtchedInDash", SHADOW_DOUBLE_ETCHED_IN_DASH },
1905 { "shadowDoubleEtchedOutDash", SHADOW_DOUBLE_ETCHED_OUT_DASH }
1909 separator_type (char *name)
1914 for (i = 0; i < (int) (XtNumber (shadow_names)); i++ )
1916 if (strcmp (name, shadow_names[i].name) == 0)
1917 return shadow_names[i].type;
1920 return SHADOW_BACKGROUND;
1924 separator_decoration_height (XlwMenuWidget mw, widget_value *val)
1927 switch (separator_type (val->value))
1929 case SHADOW_NO_LINE:
1930 case SHADOW_SINGLE_LINE:
1931 case SHADOW_SINGLE_DASHED_LINE:
1933 case SHADOW_DOUBLE_LINE:
1934 case SHADOW_DOUBLE_DASHED_LINE:
1936 case SHADOW_DOUBLE_ETCHED_OUT:
1937 case SHADOW_DOUBLE_ETCHED_IN:
1938 case SHADOW_DOUBLE_ETCHED_OUT_DASH:
1939 case SHADOW_DOUBLE_ETCHED_IN_DASH:
1940 return (1 + 2 * mw->menu.shadow_thickness);
1941 case SHADOW_ETCHED_OUT:
1942 case SHADOW_ETCHED_IN:
1944 return mw->menu.shadow_thickness;
1949 separator_size (XlwMenuWidget mw,
1952 unsigned int *toggle_width,
1953 unsigned int *label_width,
1954 unsigned int *rest_width,
1955 unsigned int *height)
1957 *height = separator_decoration_height (mw, val);
1959 *toggle_width = *rest_width = 0;
1963 separator_draw (XlwMenuWidget mw,
1966 Boolean highlighted,
1970 unsigned int height,
1971 unsigned int label_tab,
1972 unsigned int binding_tab)
1974 unsigned int sep_width;
1981 separator_decoration_draw (mw,
1987 separator_type(val->value));
1991 pushright_size (XlwMenuWidget mw,
1994 unsigned int *toggle_width,
1995 unsigned int *label_width,
1996 unsigned int *rest_width,
1997 unsigned int *height)
1999 *height = *label_width = *toggle_width = *rest_width = 0;
2003 size_menu_item (XlwMenuWidget mw,
2006 unsigned int *toggle_width,
2007 unsigned int *label_width,
2008 unsigned int *rest_width,
2009 unsigned int *height)
2011 void (*function_ptr) (XlwMenuWidget _mw,
2013 Boolean _in_menubar,
2014 unsigned int *_toggle_width,
2015 unsigned int *_label_width,
2016 unsigned int *_rest_width,
2017 unsigned int *_height);
2019 switch (menu_item_type (val))
2023 function_ptr = toggle_button_size;
2025 case SEPARATOR_TYPE:
2026 function_ptr = separator_size;
2028 case INCREMENTAL_TYPE:
2030 function_ptr = cascade_button_size;
2033 function_ptr = push_button_size;
2035 case PUSHRIGHT_TYPE:
2036 function_ptr = pushright_size;
2040 function_ptr = label_button_size;
2044 (*function_ptr) (mw,
2054 display_menu_item (XlwMenuWidget mw,
2058 Boolean highlighted,
2060 Boolean just_compute)
2063 int x = where->x /* + mw->menu.shadow_thickness */ ;
2064 int y = where->y /* + mw->menu.shadow_thickness */ ;
2065 unsigned int toggle_width;
2066 unsigned int label_width;
2067 unsigned int binding_width;
2069 unsigned int height;
2070 unsigned int label_tab;
2071 unsigned int binding_tab;
2072 void (*function_ptr) (XlwMenuWidget _mw,
2074 Boolean _in_menubar,
2075 Boolean _highlighted,
2078 unsigned int _width,
2079 unsigned int _height,
2080 unsigned int _label_tab,
2081 unsigned int _binding_tab);
2083 size_menu_item (mw, val, horizontal,
2084 &toggle_width, &label_width, &binding_width, &height);
2088 width = toggle_width + label_width + binding_width;
2089 height = ws->height - 2 * mw->menu.shadow_thickness;
2093 width = ws->width - 2 * mw->menu.shadow_thickness;
2094 toggle_width = ws->toggle_width;
2095 label_width = ws->label_width;
2104 label_tab = toggle_width;
2105 binding_tab = toggle_width + label_width;
2107 switch (menu_item_type (val))
2110 function_ptr = toggle_button_draw;
2113 function_ptr = radio_button_draw;
2115 case SEPARATOR_TYPE:
2116 function_ptr = separator_draw;
2118 case INCREMENTAL_TYPE:
2120 function_ptr = cascade_button_draw;
2123 function_ptr = push_button_draw;
2126 function_ptr = label_button_draw;
2128 default: /* do no drawing */
2132 (*function_ptr) (mw,
2144 size_menu (XlwMenuWidget mw, int level)
2146 unsigned int toggle_width;
2147 unsigned int label_width;
2148 unsigned int rest_width;
2149 unsigned int height;
2150 unsigned int max_toggle_width = 0;
2151 unsigned int max_label_width = 0;
2152 unsigned int max_rest_width = 0;
2153 unsigned int max_height = 0;
2154 int horizontal_p = mw->menu.horizontal && (level == 0);
2158 if (level >= mw->menu.old_depth)
2161 ws = &mw->menu.windows [level];
2163 for (val = mw->menu.old_stack [level]->contents; val; val = val->next)
2174 max_label_width += toggle_width + label_width + rest_width;
2175 if (height > max_height)
2176 max_height = height;
2180 if (max_toggle_width < toggle_width)
2181 max_toggle_width = toggle_width;
2182 if (max_label_width < label_width)
2183 max_label_width = label_width;
2184 if (max_rest_width < rest_width)
2185 max_rest_width = rest_width;
2186 max_height += height;
2190 ws->height = max_height;
2191 ws->width = max_label_width + max_rest_width + max_toggle_width;
2192 ws->toggle_width = max_toggle_width;
2193 ws->label_width = max_label_width;
2195 ws->width += 2 * mw->menu.shadow_thickness;
2196 ws->height += 2 * mw->menu.shadow_thickness;
2200 display_menu (XlwMenuWidget mw, int level, Boolean just_compute_p,
2201 XPoint *highlighted_pos, XPoint *hit, widget_value **hit_return,
2202 widget_value *this, widget_value *that)
2205 widget_value *following_item;
2208 int horizontal_p = mw->menu.horizontal && (level == 0);
2210 int just_compute_this_one_p;
2212 if (level >= mw->menu.old_depth)
2215 if (level < mw->menu.old_depth - 1)
2216 following_item = mw->menu.old_stack [level + 1];
2219 if (lw_menu_accelerate
2220 && level == mw->menu.old_depth - 1
2221 && mw->menu.old_stack [level]->type == CASCADE_TYPE)
2222 just_compute_p = True;
2223 following_item = NULL;
2226 #if SLOPPY_TYPES == 1
2227 puts("===================================================================");
2228 print_widget_value (following_item, 1, 0);
2234 where.x = mw->menu.shadow_thickness;
2235 where.y = mw->menu.shadow_thickness;
2237 ws = &mw->menu.windows [level];
2238 for (val = mw->menu.old_stack [level]->contents; val; val = val->next)
2242 highlighted_p = (val == following_item);
2243 /* If this is the partition (the dummy item which says that menus
2244 after this should be flushright) then figure out how big the
2245 following items are. This means we walk down the tail of the
2246 list twice, but that's no big deal - it's short.
2248 if (horizontal_p && (menu_item_type (val) == PUSHRIGHT_TYPE))
2251 XPoint flushright_size;
2253 flushright_size.x = 0;
2254 flushright_size.y = 0;
2255 for (rest = val; rest; rest = rest->next)
2256 display_menu_item (mw, rest, ws, &flushright_size,
2257 highlighted_p, horizontal_p, True);
2258 new_x = ws->width - (flushright_size.x + mw->menu.shadow_thickness);
2259 if (new_x > where.x)
2261 /* We know what we need; don't draw this item. */
2265 if (highlighted_p && highlighted_pos)
2268 highlighted_pos->x = where.x;
2270 highlighted_pos->y = where.y;
2273 just_compute_this_one_p =
2274 just_compute_p || ((this || that) && val != this && val != that);
2278 display_menu_item (mw, val, ws, &where, highlighted_p, horizontal_p,
2279 just_compute_this_one_p);
2281 if (highlighted_p && highlighted_pos)
2284 highlighted_pos->y = ws->height;
2286 highlighted_pos->x = ws->width;
2289 if (hit && !*hit_return)
2291 if (horizontal_p && hit->x > start.x && hit->x <= where.x)
2293 else if (!horizontal_p && hit->y > start.y && hit->y <= where.y)
2298 where.y = mw->menu.shadow_thickness;
2300 where.x = mw->menu.shadow_thickness;
2303 /* Draw slab edges around menu */
2304 if (!just_compute_p)
2305 shadow_draw(mw, ws->window, 0, 0, ws->width, ws->height, SHADOW_OUT);
2310 set_new_state (XlwMenuWidget mw, widget_value *val, int level)
2314 mw->menu.new_depth = 0;
2315 for (i = 0; i < level; i++)
2316 push_new_stack (mw, mw->menu.old_stack [i]);
2318 push_new_stack (mw, val);
2322 make_windows_if_needed (XlwMenuWidget mw, int n)
2326 XSetWindowAttributes xswa;
2331 window_state *windows;
2334 if (mw->menu.windows_length >= n)
2337 root = RootWindowOfScreen (XtScreen(mw));
2338 /* grab the visual and depth from the nearest shell ancestor */
2339 visual = CopyFromParent;
2340 depth = CopyFromParent;
2342 while (visual == CopyFromParent && p)
2346 visual = ((ShellWidget)p)->shell.visual;
2347 depth = p->core.depth;
2352 xswa.save_under = True;
2353 xswa.override_redirect = True;
2354 xswa.background_pixel = mw->core.background_pixel;
2355 xswa.border_pixel = mw->core.border_pixel;
2356 xswa.event_mask = (ExposureMask | ButtonMotionMask
2357 | ButtonReleaseMask | ButtonPressMask);
2358 xswa.cursor = mw->menu.cursor_shape;
2359 xswa.colormap = mw->core.colormap;
2360 mask = CWSaveUnder | CWOverrideRedirect | CWBackPixel | CWBorderPixel
2361 | CWEventMask | CWCursor | CWColormap;
2363 if (mw->menu.use_backing_store)
2365 xswa.backing_store = Always;
2366 mask |= CWBackingStore;
2369 if (!mw->menu.windows)
2372 (window_state *) XtMalloc (n * sizeof (window_state));
2378 (window_state *) XtRealloc ((char *) mw->menu.windows,
2379 n * sizeof (window_state));
2380 start_at = mw->menu.windows_length;
2382 mw->menu.windows_length = n;
2384 windows = mw->menu.windows;
2386 for (i = start_at; i < n; i++)
2390 windows [i].width = 1;
2391 windows [i].height = 1;
2392 windows [i].window =
2393 XCreateWindow (XtDisplay (mw),
2396 0, depth, CopyFromParent, visual, mask, &xswa);
2400 /* Make the window fit in the screen */
2402 fit_to_screen (XlwMenuWidget mw, window_state *ws, window_state *previous_ws,
2403 Boolean horizontal_p)
2405 int screen_width = WidthOfScreen (XtScreen (mw));
2406 int screen_height = HeightOfScreen (XtScreen (mw));
2410 else if ((int) (ws->x + ws->width) > screen_width)
2413 ws->x = previous_ws->x - ws->width;
2416 ws->x = screen_width - ws->width;
2418 /* This check is to make sure we cut off the right side
2419 instead of the left side if the menu is wider than the
2427 else if ((int) (ws->y + ws->height) > screen_height)
2431 /* A pulldown must either be entirely above or below the menubar.
2432 If we're here, the pulldown doesn't fit below the menubar, so
2433 let's determine if it will fit above the menubar.
2434 Only put it above if there is more room above than below.
2435 Note shadow_thickness offset to allow for slab surround.
2437 if (ws->y > (screen_height / 2))
2438 ws->y = previous_ws->y - ws->height + mw->menu.shadow_thickness;
2442 ws->y = screen_height - ws->height;
2443 /* if it's taller than the screen, display the topmost part
2444 that will fit, beginning at the top of the screen. */
2451 /* Updates old_stack from new_stack and redisplays. */
2453 remap_menubar (XlwMenuWidget mw)
2457 XPoint selection_position;
2458 int old_depth = mw->menu.old_depth;
2459 int new_depth = mw->menu.new_depth;
2460 widget_value **old_stack;
2461 widget_value **new_stack;
2462 window_state *windows;
2463 widget_value *old_selection;
2464 widget_value *new_selection;
2466 /* Check that enough windows and old_stack are ready. */
2467 make_windows_if_needed (mw, new_depth);
2468 make_old_stack_space (mw, new_depth);
2469 windows = mw->menu.windows;
2470 old_stack = mw->menu.old_stack;
2471 new_stack = mw->menu.new_stack;
2473 /* compute the last identical different entry */
2474 for (i = 1; i < old_depth && i < new_depth; i++)
2475 if (old_stack [i] != new_stack [i])
2479 if (lw_menu_accelerate
2481 && last_same == old_depth - 1
2482 && old_stack [last_same]->contents)
2485 /* Memorize the previously selected item to be able to refresh it */
2486 old_selection = last_same + 1 < old_depth ? old_stack [last_same + 1] : NULL;
2487 new_selection = last_same + 1 < new_depth ? new_stack [last_same + 1] : NULL;
2489 /* updates old_state from new_state. It has to be done now because
2490 display_menu (called below) uses the old_stack to know what to display. */
2491 for (i = last_same + 1; i < new_depth; i++)
2492 old_stack [i] = new_stack [i];
2494 mw->menu.old_depth = new_depth;
2496 /* refresh the last selection */
2497 selection_position.x = 0;
2498 selection_position.y = 0;
2499 display_menu (mw, last_same, new_selection == old_selection,
2500 &selection_position, NULL, NULL, old_selection, new_selection);
2502 /* Now popup the new menus */
2503 for (i = last_same + 1; i < new_depth && new_stack [i]->contents; i++)
2505 window_state *previous_ws = &windows [i - 1];
2506 window_state *ws = &windows [i];
2508 if (lw_menu_accelerate && i == new_depth - 1)
2511 ws->x = previous_ws->x + selection_position.x;
2512 ws->y = previous_ws->y + selection_position.y;
2514 /* take into account the slab around the new menu */
2515 ws->y -= mw->menu.shadow_thickness;
2518 widget_value *val = mw->menu.old_stack [i];
2519 if (val->contents->type == INCREMENTAL_TYPE)
2521 /* okay, we're now doing a lisp callback to incrementally generate
2522 more of the menu. */
2523 XtCallCallbackList ((Widget)mw,
2525 (XtPointer)val->contents);
2531 fit_to_screen (mw, ws, previous_ws, mw->menu.horizontal && i == 1);
2533 XClearWindow (XtDisplay (mw), ws->window);
2534 XMoveResizeWindow (XtDisplay (mw), ws->window, ws->x, ws->y,
2535 ws->width, ws->height);
2536 XMapRaised (XtDisplay (mw), ws->window);
2537 display_menu (mw, i, False, &selection_position, NULL, NULL, NULL, NULL);
2540 /* unmap the menus that popped down */
2542 last_same = new_depth;
2543 if (lw_menu_accelerate
2545 && new_stack [last_same - 1]->contents)
2548 for (i = last_same - 1; i < old_depth; i++)
2549 if (i >= last_same || !new_stack [i]->contents)
2550 XUnmapWindow (XtDisplay (mw), windows [i].window);
2554 motion_event_is_in_menu (XlwMenuWidget mw, XMotionEvent *ev, int level,
2555 XPoint *relative_pos)
2557 window_state *ws = &mw->menu.windows [level];
2558 int x = level == 0 ? ws->x : ws->x + mw->menu.shadow_thickness;
2559 int y = level == 0 ? ws->y : ws->y + mw->menu.shadow_thickness;
2560 relative_pos->x = ev->x_root - x;
2561 relative_pos->y = ev->y_root - y;
2562 return (x < ev->x_root && ev->x_root < (int) (x + ws->width) &&
2563 y < ev->y_root && ev->y_root < (int) (y + ws->height));
2567 map_event_to_widget_value (XlwMenuWidget mw, XMotionEvent *ev,
2568 widget_value **val_ptr, int *level,
2569 Boolean *inside_menu)
2572 XPoint relative_pos;
2576 *inside_menu = False;
2578 /* Find the window */
2580 for (i = mw->menu.old_depth - 1; i >= 0; i--)
2582 for (i = 0; i <= mw->menu.old_depth - 1; i++)
2585 ws = &mw->menu.windows [i];
2586 if (ws && motion_event_is_in_menu (mw, ev, i, &relative_pos))
2588 *inside_menu = True; /* special logic for menubar below... */
2589 if ((ev->type == ButtonPress) ||
2592 display_menu (mw, i, True, NULL, &relative_pos,
2593 val_ptr, NULL, NULL);
2597 *inside_menu = True;
2600 else if (mw->menu.horizontal || i == 0)
2602 /* if we're clicking on empty part of the menubar, then
2603 unpost the stay-up menu */
2604 *inside_menu = False;
2614 make_drawing_gcs (XlwMenuWidget mw)
2617 unsigned long flags = (GCFont | GCForeground | GCBackground);
2620 xgcv.font = default_font_of_font_list (mw->menu.font_list)->fid;
2622 xgcv.font = mw->menu.font->fid;
2625 xgcv.foreground = mw->core.background_pixel;
2626 xgcv.background = mw->menu.foreground;
2627 mw->menu.background_gc = XtGetGC ((Widget) mw, flags, &xgcv);
2629 xgcv.foreground = mw->menu.foreground;
2630 xgcv.background = mw->core.background_pixel;
2631 mw->menu.foreground_gc = XtGetGC ((Widget) mw, flags, &xgcv);
2633 if (mw->menu.select_color != (Pixel)-1)
2635 xgcv.foreground = mw->menu.select_color;
2639 Display *dpy = XtDisplay(mw);
2640 if (CellsOfScreen(DefaultScreenOfDisplay(dpy)) <= 2)
2642 xgcv.foreground = mw->menu.foreground;
2647 Colormap cmap = mw->core.colormap;
2648 xcolor.pixel = mw->core.background_pixel;
2649 XQueryColor (dpy, cmap, &xcolor);
2650 xcolor.red = (xcolor.red * 17) / 20;
2651 xcolor.green = (xcolor.green * 17) / 20;
2652 xcolor.blue = (xcolor.blue * 17) / 20;
2653 if (allocate_nearest_color (dpy, cmap, &xcolor))
2654 xgcv.foreground = xcolor.pixel;
2657 xgcv.background = mw->core.background_pixel;
2658 mw->menu.select_gc = XtGetGC ((Widget)mw, flags, &xgcv);
2660 xgcv.foreground = mw->menu.foreground;
2661 xgcv.background = mw->core.background_pixel;
2662 xgcv.fill_style = FillStippled;
2663 xgcv.stipple = mw->menu.gray_pixmap;
2664 mw->menu.inactive_gc = XtGetGC ((Widget)mw,
2665 (flags | GCFillStyle | GCStipple),
2668 xgcv.foreground = mw->menu.highlight_foreground;
2669 xgcv.background = mw->core.background_pixel;
2670 mw->menu.highlight_gc = XtGetGC ((Widget)mw, flags, &xgcv);
2672 xgcv.foreground = mw->menu.title_foreground;
2673 xgcv.background = mw->core.background_pixel;
2674 mw->menu.title_gc = XtGetGC ((Widget)mw, flags, &xgcv);
2676 xgcv.foreground = mw->menu.button_foreground;
2677 xgcv.background = mw->core.background_pixel;
2678 mw->menu.button_gc = XtGetGC ((Widget)mw, flags, &xgcv);
2680 xgcv.fill_style = FillStippled;
2681 xgcv.stipple = mw->menu.gray_pixmap;
2682 mw->menu.inactive_button_gc = XtGetGC ((Widget)mw,
2683 (flags | GCFillStyle | GCStipple),
2688 release_drawing_gcs (XlwMenuWidget mw)
2690 XtReleaseGC ((Widget) mw, mw->menu.foreground_gc);
2691 XtReleaseGC ((Widget) mw, mw->menu.button_gc);
2692 XtReleaseGC ((Widget) mw, mw->menu.highlight_gc);
2693 XtReleaseGC ((Widget) mw, mw->menu.title_gc);
2694 XtReleaseGC ((Widget) mw, mw->menu.inactive_gc);
2695 XtReleaseGC ((Widget) mw, mw->menu.inactive_button_gc);
2696 XtReleaseGC ((Widget) mw, mw->menu.background_gc);
2697 XtReleaseGC ((Widget) mw, mw->menu.select_gc);
2698 /* let's get some segvs if we try to use these... */
2699 mw->menu.foreground_gc = (GC) -1;
2700 mw->menu.button_gc = (GC) -1;
2701 mw->menu.highlight_gc = (GC) -1;
2702 mw->menu.title_gc = (GC) -1;
2703 mw->menu.inactive_gc = (GC) -1;
2704 mw->menu.inactive_button_gc = (GC) -1;
2705 mw->menu.background_gc = (GC) -1;
2706 mw->menu.select_gc = (GC) -1;
2709 #define MINL(x,y) ((((unsigned long) (x)) < ((unsigned long) (y))) \
2710 ? ((unsigned long) (x)) : ((unsigned long) (y)))
2713 make_shadow_gcs (XlwMenuWidget mw)
2716 unsigned long pm = 0;
2717 Display *dpy = XtDisplay ((Widget) mw);
2718 Colormap cmap = mw->core.colormap;
2720 int top_frobbed = 0, bottom_frobbed = 0;
2722 if (mw->menu.top_shadow_color == (Pixel) (-1))
2723 mw->menu.top_shadow_color = mw->core.background_pixel;
2724 if (mw->menu.bottom_shadow_color == (Pixel) (-1))
2725 mw->menu.bottom_shadow_color = mw->menu.foreground;
2727 if (mw->menu.top_shadow_color == mw->core.background_pixel ||
2728 mw->menu.top_shadow_color == mw->menu.foreground)
2730 topc.pixel = mw->core.background_pixel;
2731 XQueryColor (dpy, cmap, &topc);
2732 /* don't overflow/wrap! */
2733 topc.red = MINL (65535, topc.red * 1.2);
2734 topc.green = MINL (65535, topc.green * 1.2);
2735 topc.blue = MINL (65535, topc.blue * 1.2);
2736 if (allocate_nearest_color (dpy, cmap, &topc))
2738 if (topc.pixel == mw->core.background_pixel)
2740 XFreeColors( dpy, cmap, &topc.pixel, 1, 0);
2741 topc.red = MINL (65535, topc.red + 0x8000);
2742 topc.green = MINL (65535, topc.green + 0x8000);
2743 topc.blue = MINL (65535, topc.blue + 0x8000);
2744 if (allocate_nearest_color (dpy, cmap, &topc))
2746 mw->menu.top_shadow_color = topc.pixel;
2751 mw->menu.top_shadow_color = topc.pixel;
2757 if (mw->menu.bottom_shadow_color == mw->menu.foreground ||
2758 mw->menu.bottom_shadow_color == mw->core.background_pixel)
2760 botc.pixel = mw->core.background_pixel;
2761 XQueryColor (dpy, cmap, &botc);
2762 botc.red = (botc.red * 3) / 5;
2763 botc.green = (botc.green * 3) / 5;
2764 botc.blue = (botc.blue * 3) / 5;
2765 if (allocate_nearest_color (dpy, cmap, &botc))
2767 if (botc.pixel == mw->core.background_pixel)
2769 XFreeColors (dpy, cmap, &botc.pixel, 1, 0);
2770 botc.red = MINL (65535, botc.red + 0x4000);
2771 botc.green = MINL (65535, botc.green + 0x4000);
2772 botc.blue = MINL (65535, botc.blue + 0x4000);
2773 if (allocate_nearest_color (dpy, cmap, &botc))
2775 mw->menu.bottom_shadow_color = botc.pixel;
2780 mw->menu.bottom_shadow_color = botc.pixel;
2787 if (top_frobbed && bottom_frobbed)
2789 int top_avg = ((topc.red / 3) + (topc.green / 3) + (topc.blue / 3));
2790 int bot_avg = ((botc.red / 3) + (botc.green / 3) + (botc.blue / 3));
2791 if (bot_avg > top_avg)
2793 Pixel tmp = mw->menu.top_shadow_color;
2794 mw->menu.top_shadow_color = mw->menu.bottom_shadow_color;
2795 mw->menu.bottom_shadow_color = tmp;
2797 else if (topc.pixel == botc.pixel)
2799 if (botc.pixel == mw->menu.foreground)
2800 mw->menu.top_shadow_color = mw->core.background_pixel;
2802 mw->menu.bottom_shadow_color = mw->menu.foreground;
2806 if (!mw->menu.top_shadow_pixmap &&
2807 mw->menu.top_shadow_color == mw->core.background_pixel)
2809 mw->menu.top_shadow_pixmap = mw->menu.gray_pixmap;
2810 mw->menu.top_shadow_color = mw->menu.foreground;
2812 if (!mw->menu.bottom_shadow_pixmap &&
2813 mw->menu.bottom_shadow_color == mw->core.background_pixel)
2815 mw->menu.bottom_shadow_pixmap = mw->menu.gray_pixmap;
2816 mw->menu.bottom_shadow_color = mw->menu.foreground;
2819 xgcv.fill_style = FillOpaqueStippled;
2820 xgcv.foreground = mw->menu.top_shadow_color;
2821 xgcv.background = mw->core.background_pixel;
2822 /* xgcv.stipple = mw->menu.top_shadow_pixmap; gtb */
2823 if (mw->menu.top_shadow_pixmap &&
2824 mw->menu.top_shadow_pixmap != XmUNSPECIFIED_PIXMAP)
2825 xgcv.stipple = mw->menu.top_shadow_pixmap;
2828 pm = (xgcv.stipple ? GCStipple|GCFillStyle : 0);
2829 mw->menu.shadow_top_gc =
2830 XtGetGC((Widget)mw, GCForeground|GCBackground|pm, &xgcv);
2832 xgcv.foreground = mw->menu.bottom_shadow_color;
2833 /* xgcv.stipple = mw->menu.bottom_shadow_pixmap; gtb */
2834 if (mw->menu.bottom_shadow_pixmap &&
2835 mw->menu.bottom_shadow_pixmap != XmUNSPECIFIED_PIXMAP)
2836 xgcv.stipple = mw->menu.bottom_shadow_pixmap;
2839 pm = (xgcv.stipple ? GCStipple|GCFillStyle : 0);
2840 mw->menu.shadow_bottom_gc =
2841 XtGetGC ((Widget)mw, GCForeground|GCBackground|pm, &xgcv);
2846 release_shadow_gcs (XlwMenuWidget mw)
2848 XtReleaseGC ((Widget) mw, mw->menu.shadow_top_gc);
2849 XtReleaseGC ((Widget) mw, mw->menu.shadow_bottom_gc);
2854 extract_font_extents (XlwMenuWidget mw)
2857 /* Find the maximal ascent/descent of the fonts in the font list
2858 so that all menu items can be the same height... */
2859 mw->menu.font_ascent = 0;
2860 mw->menu.font_descent = 0;
2863 XmFontContext context;
2864 #if (XmVersion >= 1002)
2865 XmFontListEntry fontentry;
2867 XmStringCharSet charset;
2871 if (! XmFontListInitFontContext (&context, mw->menu.font_list))
2873 #if (XmVersion >= 1002)
2874 /* There is a BUG in the 1.2 version of XmFontListGetNextFont() (or more
2875 specifically, in _XmGetFirstFont()) that can cause a null pointer to be
2876 passed to XFontsOfFontSet. Use XmFontListNextEntry(), which is the
2877 newer equivalent, instead. Also, it supports font sets, and the
2878 older function doesn't. */
2879 while ((fontentry = XmFontListNextEntry (context)))
2883 XtPointer one_of_them = XmFontListEntryGetFont (fontentry, &rettype);
2884 if (rettype == XmFONT_IS_FONTSET)
2886 XFontSet fontset = (XFontSet) one_of_them;
2887 XFontStruct **fontstruct_list;
2888 char **fontname_list;
2889 int fontcount = XFontsOfFontSet (fontset, &fontstruct_list,
2891 while (--fontcount >= 0)
2893 font = fontstruct_list[fontcount];
2894 if (font->ascent > (int) mw->menu.font_ascent)
2895 mw->menu.font_ascent = font->ascent;
2896 if (font->descent > (int) mw->menu.font_descent)
2897 mw->menu.font_descent = font->descent;
2900 else /* XmFONT_IS_FONT */
2902 font = (XFontStruct *) one_of_them;
2903 if (font->ascent > (int) mw->menu.font_ascent)
2904 mw->menu.font_ascent = font->ascent;
2905 if (font->descent > (int) mw->menu.font_descent)
2906 mw->menu.font_descent = font->descent;
2909 #else /* motif 1.1 */
2910 while (XmFontListGetNextFont (context, &charset, &font))
2912 if (font->ascent > (int) mw->menu.font_ascent)
2913 mw->menu.font_ascent = font->ascent;
2914 if (font->descent > (int) mw->menu.font_descent)
2915 mw->menu.font_descent = font->descent;
2918 #endif /* Motif version */
2919 XmFontListFreeFontContext (context);
2921 #else /* Not Motif */
2922 # ifdef USE_XFONTSET
2923 XFontStruct **fontstruct_list;
2924 char **fontname_list;
2926 int fontcount = XFontsOfFontSet(mw->menu.font_set, &fontstruct_list,
2928 mw->menu.font_ascent = 0;
2929 mw->menu.font_descent = 0;
2930 # if 0 /* nasty, personal debug, Kazz */
2931 fprintf(stderr, "fontSet count is %d\n", fontcount);
2933 while (--fontcount >= 0) {
2934 font = fontstruct_list[fontcount];
2935 if (font->ascent > (int) mw->menu.font_ascent)
2936 mw->menu.font_ascent = font->ascent;
2937 if (font->descent > (int) mw->menu.font_descent)
2938 mw->menu.font_descent = font->descent;
2940 # else /* ! USE_XFONTSET */
2941 mw->menu.font_ascent = mw->menu.font->ascent;
2942 mw->menu.font_descent = mw->menu.font->descent;
2944 #endif /* NEED_MOTIF */
2948 static XFontStruct *
2949 default_font_of_font_list (XmFontList font_list)
2951 XFontStruct *font = 0;
2953 /* Xm/Label.c does this: */
2954 _XmFontListGetDefaultFont (font_list, &font);
2957 XmFontContext context;
2958 #if (XmVersion >= 1002)
2959 XmFontListEntry fontentry;
2961 XtPointer one_of_them;
2963 XmStringCharSet charset;
2966 if (! XmFontListInitFontContext (&context, font_list))
2968 #if (XmVersion >= 1002)
2969 /* There is a BUG in the 1.2 version of XmFontListGetNextFont() (or more
2970 specifically, in _XmGetFirstFont()) that can cause a null pointer to be
2971 passed to XFontsOfFontSet. Use XmFontListNextEntry(), which is the
2972 newer equivalent, instead. */
2973 fontentry = XmFontListNextEntry (context);
2974 one_of_them = XmFontListEntryGetFont (fontentry, &rettype);
2975 if (rettype == XmFONT_IS_FONTSET)
2977 XFontSet fontset = (XFontSet) one_of_them;
2978 XFontStruct **fontstruct_list;
2979 char **fontname_list;
2980 (void) XFontsOfFontSet (fontset, &fontstruct_list, &fontname_list);
2981 font = fontstruct_list[0];
2983 else /* XmFONT_IS_FONT */
2985 font = (XFontStruct *) one_of_them;
2988 if (! XmFontListGetNextFont (context, &charset, &font))
2992 XmFontListFreeFontContext (context);
2996 if (! font) abort ();
2999 #endif /* NEED_MOTIF */
3002 XlwMenuInitialize (Widget request, Widget new, ArgList args,
3005 /* Get the GCs and the widget size */
3006 XlwMenuWidget mw = (XlwMenuWidget)new;
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 /* #### Even if it's a kludge!!!, we should consider doing the same for
3020 /* The menu.font_list slot came from the *fontList resource (Motif standard.)
3021 The menu.font_list_2 slot came from the *font resource, for backward
3022 compatibility with older versions of this code, and consistency with the
3023 rest of emacs. If both font and fontList are specified, we use fontList.
3024 If only one is specified, we use that. If neither are specified, we
3025 use the "fallback" value. What a kludge!!!
3027 Note that this has the bug that a more general wildcard like "*fontList:"
3028 will override a more specific resource like "Emacs*menubar.font:". But
3029 I can't think of a way around that.
3031 if (mw->menu.font_list) /* if *fontList is specified, use that */
3033 else if (mw->menu.font_list_2) /* else if *font is specified, use that */
3034 mw->menu.font_list = mw->menu.font_list_2;
3035 else /* otherwise use default */
3036 mw->menu.font_list = mw->menu.fallback_font_list;
3039 make_drawing_gcs (mw);
3040 make_shadow_gcs (mw);
3041 extract_font_extents (mw);
3043 mw->menu.popped_up = False;
3044 mw->menu.pointer_grabbed = False;
3045 mw->menu.next_release_must_exit = False;
3047 mw->menu.old_depth = 1;
3048 mw->menu.old_stack = XtNew (widget_value*);
3049 mw->menu.old_stack_length = 1;
3050 mw->menu.old_stack [0] = mw->menu.contents;
3052 mw->menu.new_depth = 0;
3053 mw->menu.new_stack = 0;
3054 mw->menu.new_stack_length = 0;
3055 push_new_stack (mw, mw->menu.contents);
3057 mw->menu.windows = XtNew (window_state);
3058 mw->menu.windows_length = 1;
3059 mw->menu.windows [0].x = 0;
3060 mw->menu.windows [0].y = 0;
3061 mw->menu.windows [0].width = 0;
3062 mw->menu.windows [0].height = 0;
3065 mw->core.width = mw->menu.windows [0].width;
3066 mw->core.height = mw->menu.windows [0].height;
3070 XlwMenuClassInitialize (void)
3072 initialize_massaged_resource_char();
3076 XlwMenuRealize (Widget w, Mask *valueMask, XSetWindowAttributes *attributes)
3078 XlwMenuWidget mw = (XlwMenuWidget)w;
3079 XSetWindowAttributes xswa;
3082 (*xlwMenuWidgetClass->core_class.superclass->core_class.realize)
3083 (w, valueMask, attributes);
3085 xswa.save_under = True;
3086 xswa.cursor = mw->menu.cursor_shape;
3087 mask = CWSaveUnder | CWCursor;
3088 if (mw->menu.use_backing_store)
3090 xswa.backing_store = Always;
3091 mask |= CWBackingStore;
3093 XChangeWindowAttributes (XtDisplay (w), XtWindow (w), mask, &xswa);
3095 mw->menu.windows [0].window = XtWindow (w);
3096 mw->menu.windows [0].x = w->core.x;
3097 mw->menu.windows [0].y = w->core.y;
3098 mw->menu.windows [0].width = w->core.width;
3099 mw->menu.windows [0].height = w->core.height;
3102 /* Only the toplevel menubar/popup is a widget so it's the only one that
3103 receives expose events through Xt. So we repaint all the other panes
3104 when receiving an Expose event. */
3106 XlwMenuRedisplay (Widget w, XEvent *ev, Region region)
3108 XlwMenuWidget mw = (XlwMenuWidget)w;
3111 if (mw->core.being_destroyed) return;
3113 for (i = 0; i < mw->menu.old_depth; i++)
3114 display_menu (mw, i, False, NULL, NULL, NULL, NULL, NULL);
3115 set_new_state (mw, NULL, mw->menu.old_depth); /* #### - ??? */
3116 remap_menubar (mw); /* #### - do these two lines do anything? */
3120 XlwMenuDestroy (Widget w)
3123 XlwMenuWidget mw = (XlwMenuWidget) w;
3125 if (mw->menu.pointer_grabbed)
3127 XtUngrabPointer (w, CurrentTime);
3128 mw->menu.pointer_grabbed = False;
3131 release_drawing_gcs (mw);
3132 release_shadow_gcs (mw);
3134 /* this doesn't come from the resource db but is created explicitly
3135 so we must free it ourselves. */
3136 XFreePixmap (XtDisplay (mw), mw->menu.gray_pixmap);
3137 mw->menu.gray_pixmap = (Pixmap) -1;
3139 /* Don't free mw->menu.contents because that comes from our creator.
3140 The `*_stack' elements are just pointers into `contents' so leave
3141 that alone too. But free the stacks themselves. */
3142 if (mw->menu.old_stack) XtFree ((char *) mw->menu.old_stack);
3143 if (mw->menu.new_stack) XtFree ((char *) mw->menu.new_stack);
3145 /* Remember, you can't free anything that came from the resource
3146 database. This includes:
3148 mw->menu.top_shadow_pixmap
3149 mw->menu.bottom_shadow_pixmap
3152 Also the color cells of top_shadow_color, bottom_shadow_color,
3153 foreground, and button_foreground will never be freed until this
3154 client exits. Nice, eh?
3157 /* start from 1 because the one in slot 0 is w->core.window */
3158 for (i = 1; i < mw->menu.windows_length; i++)
3159 XDestroyWindow (XtDisplay (mw), mw->menu.windows [i].window);
3160 if (mw->menu.windows)
3161 XtFree ((char *) mw->menu.windows);
3165 XlwMenuSetValues (Widget current, Widget request, Widget new, ArgList args,
3168 XlwMenuWidget oldmw = (XlwMenuWidget)current;
3169 XlwMenuWidget newmw = (XlwMenuWidget)new;
3170 Boolean redisplay = False;
3173 if (newmw->menu.contents
3174 && newmw->menu.contents->contents
3175 && newmw->menu.contents->contents->change >= VISIBLE_CHANGE)
3178 if (newmw->core.background_pixel != oldmw->core.background_pixel
3179 || newmw->menu.foreground != oldmw->menu.foreground
3180 /* For the XEditResource protocol, which may want to change the font. */
3182 || newmw->menu.font_list != oldmw->menu.font_list
3183 || newmw->menu.font_list_2 != oldmw->menu.font_list_2
3184 || newmw->menu.fallback_font_list != oldmw->menu.fallback_font_list
3186 || newmw->menu.font != oldmw->menu.font
3190 release_drawing_gcs (newmw);
3191 make_drawing_gcs (newmw);
3194 for (i = 0; i < oldmw->menu.windows_length; i++)
3196 XSetWindowBackground (XtDisplay (oldmw),
3197 oldmw->menu.windows [i].window,
3198 newmw->core.background_pixel);
3199 /* clear windows and generate expose events */
3200 XClearArea (XtDisplay (oldmw), oldmw->menu.windows[i].window,
3209 XlwMenuResize (Widget w)
3211 XlwMenuWidget mw = (XlwMenuWidget)w;
3213 mw->menu.windows [0].width = mw->core.width;
3214 mw->menu.windows [0].height = mw->core.height;
3217 \f/* Action procedures */
3219 handle_single_motion_event (XlwMenuWidget mw, XMotionEvent *ev,
3226 if (!map_event_to_widget_value (mw, ev, &val, &level, &stay_up))
3228 /* we wind up here when: (a) the event is in the menubar, (b) the
3229 event isn't in the menubar or any of the panes, (c) the event is on
3230 a disabled menu item */
3231 pop_new_stack_if_no_contents (mw);
3232 if (select_p && !stay_up) {
3233 /* pop down all menus and exit */
3234 mw->menu.next_release_must_exit = True;
3235 set_new_state(mw, (val = NULL), 1);
3240 /* we wind up here when: (a) the event pops up a pull_right menu,
3241 (b) a menu item that is not disabled is highlighted */
3242 if (select_p && mw->menu.bounce_down
3243 && close_to_reference_time((Widget)mw,
3244 mw->menu.menu_bounce_time,
3247 /* motion can cause more than one event. Don't bounce right back
3248 up if we've just bounced down. */
3251 else if (select_p && mw->menu.bounce_down &&
3252 mw->menu.last_selected_val &&
3253 (mw->menu.last_selected_val == val))
3255 val = NULL; /* assigned to mw->last_selected_val below */
3256 mw->menu.menu_bounce_time = ev->time;
3257 /* popdown last menu if we're selecting the same menu item as we did
3258 last time and the XlwMenu.bounceDown resource is set, if the
3259 item is on the menubar itself, then exit. */
3260 if (level == (mw->menu.popped_up ? 0 : 1))
3261 mw->menu.next_release_must_exit = True;
3264 mw->menu.menu_bounce_time = 0;
3265 set_new_state (mw, val, level);
3267 mw->menu.last_selected_val = val;
3270 /* Sync with the display. Makes it feel better on X terms. */
3271 XFlush (XtDisplay (mw));
3275 handle_motion_event (XlwMenuWidget mw, XMotionEvent *ev,
3280 unsigned int state = ev->state;
3281 XMotionEvent *event= ev, dummy;
3283 /* allow motion events to be generated again */
3284 dummy.window = ev->window;
3286 && XQueryPointer (XtDisplay (mw), dummy.window,
3287 &dummy.root, &dummy.subwindow,
3288 &dummy.x_root, &dummy.y_root,
3291 && dummy.state == state
3292 && (dummy.x_root != x || dummy.y_root != y))
3294 /* don't handle the event twice or that breaks bounce_down. --Stig */
3295 dummy.type = ev->type;
3299 lw_menu_accelerate = False;
3300 handle_single_motion_event (mw, event, select_p);
3303 Time x_focus_timestamp_really_sucks_fix_me_better;
3306 Start (Widget w, XEvent *ev, String *params, Cardinal *num_params)
3308 XlwMenuWidget mw = (XlwMenuWidget)w;
3310 lw_menubar_widget = w;
3312 lw_menu_active = True;
3314 if (!mw->menu.pointer_grabbed)
3316 mw->menu.menu_post_time = ev->xbutton.time;
3317 mw->menu.menu_bounce_time = 0;
3318 mw->menu.next_release_must_exit = True;
3319 mw->menu.last_selected_val = NULL;
3320 x_focus_timestamp_really_sucks_fix_me_better =
3321 ((XButtonPressedEvent*)ev)->time;
3322 XtCallCallbackList ((Widget)mw, mw->menu.open, NULL);
3324 /* notes the absolute position of the menubar window */
3325 mw->menu.windows [0].x = ev->xmotion.x_root - ev->xmotion.x;
3326 mw->menu.windows [0].y = ev->xmotion.y_root - ev->xmotion.y;
3328 XtGrabPointer ((Widget)mw, False,
3329 (ButtonMotionMask | ButtonReleaseMask | ButtonPressMask),
3330 GrabModeAsync, GrabModeAsync,
3331 None, mw->menu.cursor_shape,
3332 ((XButtonPressedEvent*)ev)->time);
3333 mw->menu.pointer_grabbed = True;
3336 /* handles the down like a move, slots are mostly compatible */
3337 handle_motion_event (mw, &ev->xmotion, True);
3341 Drag (Widget w, XEvent *ev, String *params, Cardinal *num_params)
3343 XlwMenuWidget mw = (XlwMenuWidget)w;
3344 handle_motion_event (mw, &ev->xmotion, False);
3348 Select (Widget w, XEvent *ev, String *params, Cardinal *num_params)
3350 XlwMenuWidget mw = (XlwMenuWidget)w;
3351 widget_value *selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
3353 lw_menu_accelerate = False;
3355 /* If user releases the button quickly, without selecting anything,
3356 after the initial down-click that brought the menu up,
3358 if ((selected_item == 0 || selected_item->call_data == 0)
3359 && (!mw->menu.next_release_must_exit
3360 || close_to_reference_time(w, mw->menu.menu_post_time, ev)))
3362 mw->menu.next_release_must_exit = False;
3366 /* pop down everything */
3367 mw->menu.new_depth = 1;
3370 /* Destroy() only gets called for popup menus. Menubar widgets aren't
3371 destroyed when their menu panes get nuked. */
3372 if (mw->menu.pointer_grabbed)
3374 XtUngrabPointer ((Widget)w, ev->xmotion.time);
3375 mw->menu.pointer_grabbed = False;
3378 if (mw->menu.popped_up)
3380 mw->menu.popped_up = False;
3381 XtPopdown (XtParent (mw));
3384 lw_menu_active = False;
3386 x_focus_timestamp_really_sucks_fix_me_better =
3387 ((XButtonPressedEvent*)ev)->time;
3390 XtCallCallbackList ((Widget) mw, mw->menu.select, (XtPointer) selected_item);
3393 \f/* Action procedures for keyboard accelerators */
3397 xlw_set_menu (Widget w, widget_value *val)
3399 lw_menubar_widget = w;
3400 set_new_state ((XlwMenuWidget)w, val, 1);
3403 /* prepare the menu structure via the call-backs */
3405 xlw_map_menu (Time t)
3407 XlwMenuWidget mw = (XlwMenuWidget)lw_menubar_widget;
3409 lw_menu_accelerate = True;
3411 if (!mw->menu.pointer_grabbed)
3413 XWindowAttributes ret;
3416 unsigned int num_waste;
3418 lw_menu_active = True;
3420 mw->menu.menu_post_time = t;
3421 mw->menu.menu_bounce_time = 0;
3423 mw->menu.next_release_must_exit = True;
3424 mw->menu.last_selected_val = NULL;
3426 XtCallCallbackList ((Widget)mw, mw->menu.open, NULL);
3428 /* do this for keyboards too! */
3429 /* notes the absolute position of the menubar window */
3431 mw->menu.windows [0].x = ev->xmotion.x_root - ev->xmotion.x;
3432 mw->menu.windows [0].y = ev->xmotion.y_root - ev->xmotion.y;
3435 /* get the geometry of the menubar */
3437 /* there has to be a better way than this. */
3439 mw->menu.windows [0].x = 0;
3440 mw->menu.windows [0].y = 0;
3442 parent = XtWindow (lw_menubar_widget);
3445 XGetWindowAttributes (XtDisplay (lw_menubar_widget), parent, &ret);
3446 mw->menu.windows [0].x += ret.x;
3447 mw->menu.windows [0].y += ret.y;
3450 XQueryTree (XtDisplay (lw_menubar_widget), parent, &root, &parent, &waste,
3457 while (parent != root);
3459 XtGrabPointer ((Widget)mw, False,
3460 (ButtonMotionMask | ButtonReleaseMask | ButtonPressMask),
3461 GrabModeAsync, GrabModeAsync,
3462 None, mw->menu.cursor_shape, t);
3463 mw->menu.pointer_grabbed = True;
3467 /* display the stupid menu already */
3469 xlw_display_menu (Time t)
3471 XlwMenuWidget mw = (XlwMenuWidget)lw_menubar_widget;
3473 lw_menu_accelerate = True;
3477 /* Sync with the display. Makes it feel better on X terms. */
3478 XFlush (XtDisplay (mw));
3481 /* push a sub menu */
3483 xlw_push_menu (widget_value *val)
3485 push_new_stack ((XlwMenuWidget)lw_menubar_widget, val);
3488 /* pop a sub menu */
3492 if (((XlwMenuWidget)lw_menubar_widget)->menu.new_depth > 0)
3493 ((XlwMenuWidget)lw_menubar_widget)->menu.new_depth --;
3500 xlw_kill_menus (widget_value *val)
3502 XlwMenuWidget mw = (XlwMenuWidget)lw_menubar_widget;
3504 lw_menu_accelerate = False;
3506 mw->menu.new_depth = 1;
3509 if (mw->menu.pointer_grabbed)
3511 XtUngrabPointer (lw_menubar_widget, CurrentTime);
3512 mw->menu.pointer_grabbed = False;
3515 lw_menu_active = False;
3516 XtCallCallbackList (lw_menubar_widget, mw->menu.select, (XtPointer)val);
3519 /* set the menu item */
3521 xlw_set_item (widget_value *val)
3523 if (((XlwMenuWidget)lw_menubar_widget)->menu.new_depth > 0)
3524 ((XlwMenuWidget) lw_menubar_widget)->menu.new_depth --;
3525 push_new_stack ((XlwMenuWidget) lw_menubar_widget, val);
3528 /* get either the current entry or a list of all entries in the current submenu */
3530 xlw_get_entries (int allp)
3532 XlwMenuWidget mw = (XlwMenuWidget)lw_menubar_widget;
3535 if (mw->menu.new_depth >= 2)
3536 return mw->menu.new_stack [mw->menu.new_depth - 2]->contents;
3538 return mw->menu.new_stack[0];
3541 if (mw->menu.new_depth >= 1)
3542 return mw->menu.new_stack [mw->menu.new_depth - 1];
3548 xlw_menu_level (void)
3550 return ((XlwMenuWidget)lw_menubar_widget)->menu.new_depth;
3554 /* Special code to pop-up a menu */
3556 xlw_pop_up_menu (XlwMenuWidget mw, XButtonPressedEvent *event)
3558 int x = event->x_root;
3559 int y = event->y_root;
3562 int borderwidth = mw->menu.shadow_thickness;
3563 Screen* screen = XtScreen (mw);
3565 mw->menu.menu_post_time = event->time;
3566 mw->menu.menu_bounce_time = 0;
3567 mw->menu.next_release_must_exit = True;
3568 mw->menu.last_selected_val = NULL;
3570 XtCallCallbackList ((Widget) mw, mw->menu.open, NULL);
3574 w = mw->menu.windows [0].width;
3575 h = mw->menu.windows [0].height;
3580 if (x < borderwidth)
3583 if (x > WidthOfScreen (screen) - w - 2 * borderwidth)
3584 x = WidthOfScreen (screen) - w - 2 * borderwidth;
3586 if (y < borderwidth)
3589 if (y > HeightOfScreen (screen) - h - 2 * borderwidth)
3590 y = HeightOfScreen (screen) - h - 2 * borderwidth;
3592 mw->menu.popped_up = True;
3593 XtConfigureWidget (XtParent (mw), x, y, w, h,
3594 XtParent (mw)->core.border_width);
3595 XtPopup (XtParent (mw), XtGrabExclusive);
3596 display_menu (mw, 0, False, NULL, NULL, NULL, NULL, NULL);
3597 if (!mw->menu.pointer_grabbed)
3599 XtGrabPointer ((Widget)mw, False,
3600 (ButtonMotionMask | ButtonReleaseMask | ButtonPressMask),
3601 GrabModeAsync, GrabModeAsync,
3602 None, mw->menu.cursor_shape, event->time);
3603 mw->menu.pointer_grabbed = True;
3606 mw->menu.windows [0].x = x + borderwidth;
3607 mw->menu.windows [0].y = y + borderwidth;
3609 handle_motion_event (mw, (XMotionEvent *) event, True);
3615 * This is a horrible function which should not be needed.
3616 * use it to put the resize method back the way the XlwMenu
3617 * class initializer put it. Motif screws with this when
3618 * the XlwMenu class gets instantiated.
3621 xlw_unmunge_class_resize (Widget w)
3623 if (w->core.widget_class->core_class.resize != XlwMenuResize)
3624 w->core.widget_class->core_class.resize = XlwMenuResize;