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
8 and/or modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation, either version 3 of the
10 License, or (at your option) any later version.
12 The Lucid Widget Library is distributed in the hope that it will be
13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>. */
20 /* Created by devin@lucid.com */
27 #include <sys/types.h>
33 #include <X11/IntrinsicP.h>
34 #include <X11/ShellP.h>
35 #include <X11/StringDefs.h>
36 #include <X11/cursorfont.h>
37 #include <X11/bitmaps/gray>
41 #if XmVersion < 1002 /* 1.1 or ancient */
42 #undef XmFONTLIST_DEFAULT_TAG
43 #define XmFONTLIST_DEFAULT_TAG XmSTRING_DEFAULT_CHARSET
44 #endif /* XmVersion < 1.2 */
48 #include <sxe-utils.h>
50 #ifdef USE_DEBUG_MALLOC
54 /* simple, naive integer maximum */
56 #define max(a,b) ((a)>(b)?(a):(b))
60 xlwMenuTranslations[] = "<BtnDown>: start()\n\
61 <BtnMotion>: drag()\n\
65 extern Widget lw_menubar_widget;
67 #define offset(field) XtOffset(XlwMenuWidget, field)
68 static XtResource xlwMenuResources[] = {
70 /* There are three font list resources, so that we can accept either of
71 the resources *fontList: or *font:, and so that we can tell the
72 difference between them being specified, and being defaulted to a
73 font from the XtRString specified here. */
74 {XmNfontList, XmCFontList, XmRFontList, sizeof(XmFontList),
75 offset(menu.font_list), XtRImmediate, (XtPointer) 0}
77 {XtNfont, XtCFont, XmRFontList, sizeof(XmFontList),
78 offset(menu.font_list_2), XtRImmediate, (XtPointer) 0}
80 {XmNfontList, XmCFontList, XmRFontList, sizeof(XmFontList),
81 offset(menu.fallback_font_list),
82 /* We must use an iso8859-1 font here, or people without $LANG set lose.
83 It's fair to assume that those who do have $LANG set also have the
84 *fontList resource set, or at least know how to deal with this. */
86 (XtPointer) "-*-helvetica-bold-r-*-*-*-120-*-*-*-*-iso8859-1"}
89 {XtNfont, XtCFont, XtRFontStruct, sizeof(XFontStruct *),
90 offset(menu.font), XtRString, (XtPointer) "XtDefaultFont"}
93 /* #### Consider using the same method as for Motif; see the comment in
94 XlwMenuInitialize(). */
95 {XtNfontSet, XtCFontSet, XtRFontSet, sizeof(XFontSet),
96 offset(menu.font_set), XtRString, (XtPointer) "XtDefaultFontSet"}
100 {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
101 offset(menu.foreground), XtRString, (XtPointer) "XtDefaultForeground"}
103 {XtNbuttonForeground, XtCButtonForeground, XtRPixel, sizeof(Pixel),
104 offset(menu.button_foreground), XtRString,
105 (XtPointer) "XtDefaultForeground"}
107 {XtNhighlightForeground, XtCHighlightForeground, XtRPixel,
109 offset(menu.highlight_foreground), XtRString,
110 (XtPointer) "XtDefaultForeground"}
112 {XtNtitleForeground, XtCTitleForeground, XtRPixel, sizeof(Pixel),
113 offset(menu.title_foreground), XtRString,
114 (XtPointer) "XtDefaultForeground"}
116 {XtNmargin, XtCMargin, XtRDimension, sizeof(Dimension),
117 offset(menu.margin), XtRImmediate, (XtPointer) 2}
119 {XmNmarginWidth, XmCMarginWidth, XmRHorizontalDimension,
121 offset(menu.horizontal_margin), XtRImmediate, (XtPointer) 2}
123 {XmNmarginHeight, XmCMarginHeight, XmRVerticalDimension,
125 offset(menu.vertical_margin), XtRImmediate, (XtPointer) 1}
127 {XmNspacing, XmCSpacing, XmRHorizontalDimension, sizeof(Dimension),
128 offset(menu.column_spacing), XtRImmediate, (XtPointer) 4}
130 {XmNindicatorSize, XmCIndicatorSize, XtRDimension, sizeof(Dimension),
131 offset(menu.indicator_size), XtRImmediate, (XtPointer) 0}
134 {XmNshadowThickness, XmCShadowThickness, XmRHorizontalDimension,
135 sizeof(Dimension), offset(menu.shadow_thickness),
136 XtRImmediate, (XtPointer) 2}
139 {XmNshadowThickness, XmCShadowThickness, XtRDimension,
140 sizeof(Dimension), offset(menu.shadow_thickness),
141 XtRImmediate, (XtPointer) 2}
144 {XmNselectColor, XmCSelectColor, XtRPixel, sizeof(Pixel),
145 offset(menu.select_color), XtRImmediate, (XtPointer) - 1}
147 {XmNtopShadowColor, XmCTopShadowColor, XtRPixel, sizeof(Pixel),
148 offset(menu.top_shadow_color), XtRImmediate, (XtPointer) - 1}
150 {XmNbottomShadowColor, XmCBottomShadowColor, XtRPixel, sizeof(Pixel),
151 offset(menu.bottom_shadow_color), XtRImmediate, (XtPointer) - 1}
153 {XmNtopShadowPixmap, XmCTopShadowPixmap, XtRPixmap, sizeof(Pixmap),
154 offset(menu.top_shadow_pixmap), XtRImmediate, (XtPointer) None}
156 {XmNbottomShadowPixmap, XmCBottomShadowPixmap, XtRPixmap,
158 offset(menu.bottom_shadow_pixmap), XtRImmediate, (XtPointer) None}
161 {XtNopen, XtCCallback, XtRCallback, sizeof(XtPointer),
162 offset(menu.open), XtRCallback, (XtPointer) NULL}
164 {XtNselect, XtCCallback, XtRCallback, sizeof(XtPointer),
165 offset(menu.select), XtRCallback, (XtPointer) NULL}
167 {XtNmenu, XtCMenu, XtRPointer, sizeof(XtPointer),
168 offset(menu.contents), XtRImmediate, (XtPointer) NULL}
170 {XtNcursor, XtCCursor, XtRCursor, sizeof(Cursor),
171 offset(menu.cursor_shape), XtRString, (XtPointer) "right_ptr"}
173 {XtNhorizontal, XtCHorizontal, XtRInt, sizeof(int),
174 offset(menu.horizontal), XtRImmediate, (XtPointer) True},
175 {XtNuseBackingStore, XtCUseBackingStore, XtRBoolean, sizeof(Boolean),
176 offset(menu.use_backing_store), XtRImmediate, (XtPointer) False}
178 {XtNbounceDown, XtCBounceDown, XtRBoolean, sizeof(Boolean),
179 offset(menu.bounce_down), XtRImmediate, (XtPointer) True}
181 {XtNresourceLabels, XtCResourceLabels, XtRBoolean, sizeof(Boolean),
182 offset(menu.lookup_labels), XtRImmediate, (XtPointer) False}
187 static Boolean XlwMenuSetValues(Widget current, Widget request, Widget new,
188 ArgList args, Cardinal * num_args);
189 static void XlwMenuRealize(Widget w, Mask * valueMask,
190 XSetWindowAttributes * attributes);
191 static void XlwMenuRedisplay(Widget w, XEvent * ev, Region region);
192 static void XlwMenuResize(Widget w);
193 static void XlwMenuInitialize(Widget request, Widget new, ArgList args,
194 Cardinal * num_args);
195 static void XlwMenuDestroy(Widget w);
196 static void XlwMenuClassInitialize(void);
197 static void Start(Widget w, XEvent * ev, String * params,
198 Cardinal * num_params);
199 static void Drag(Widget w, XEvent * ev, String * params, Cardinal * num_params);
200 static void Select(Widget w, XEvent * ev, String * params,
201 Cardinal * num_params);
204 static XFontStruct *default_font_of_font_list(XmFontList);
207 static XtActionsRec xlwMenuActionsList[] = {
213 #define SuperClass ((CoreWidgetClass)&coreClassRec)
215 XlwMenuClassRec xlwMenuClassRec = {
216 { /* CoreClass fields initialization */
217 (WidgetClass) SuperClass, /* superclass */
218 "XlwMenu", /* class_name */
219 sizeof(XlwMenuRec), /* size */
220 XlwMenuClassInitialize, /* class_initialize */
221 NULL, /* class_part_initialize */
222 FALSE, /* class_inited */
223 XlwMenuInitialize, /* initialize */
224 NULL, /* initialize_hook */
225 XlwMenuRealize, /* realize */
226 xlwMenuActionsList, /* actions */
227 XtNumber(xlwMenuActionsList), /* num_actions */
228 xlwMenuResources, /* resources */
229 XtNumber(xlwMenuResources), /* resource_count */
230 NULLQUARK, /* xrm_class */
231 TRUE, /* compress_motion */
232 XtExposeCompressMaximal, /* compress_exposure */
233 TRUE, /* compress_enterleave */
234 FALSE, /* visible_interest */
235 XlwMenuDestroy, /* destroy */
236 XlwMenuResize, /* resize */
237 XlwMenuRedisplay, /* expose */
238 XlwMenuSetValues, /* set_values */
239 NULL, /* set_values_hook */
240 XtInheritSetValuesAlmost, /* set_values_almost */
241 NULL, /* get_values_hook */
242 NULL, /* #### - should this be set for grabs? accept_focus */
243 XtVersion, /* version */
244 NULL, /* callback_private */
245 xlwMenuTranslations, /* tm_table */
246 XtInheritQueryGeometry, /* query_geometry */
247 XtInheritDisplayAccelerator, /* display_accelerator */
250 , /* XlwMenuClass fields initialization */
257 WidgetClass xlwMenuWidgetClass = (WidgetClass) & xlwMenuClassRec;
259 extern int lw_menu_accelerate;
262 #if 0 /* Apparently not used anywhere */
264 static char *safe_strdup(char *s)
269 result = (char *)malloc(strlen(s) + 1);
278 /* Replacement for XAllocColor() that tries to return the nearest
279 available color if the colormap is full. From FSF Emacs. */
282 allocate_nearest_color(Display * display, Colormap screen_colormap,
285 int status = XAllocColor(display, screen_colormap, color_def);
290 /* If we got to this point, the colormap is full, so we're
291 going to try to get the next closest color.
292 The algorithm used is a least-squares matching, which is
293 what X uses for closest color matching with StaticColor visuals. */
296 unsigned long nearest_delta = ULONG_MAX;
298 int no_cells = XDisplayCells(display, XDefaultScreen(display));
299 /* Don't use alloca here because lwlib doesn't have the
300 necessary configuration information that src does. */
301 XColor *cells = (XColor *) malloc(sizeof(XColor) * no_cells);
303 for (x = 0; x < no_cells; x++)
306 XQueryColors(display, screen_colormap, cells, no_cells);
308 for (nearest = 0, x = 0; x < no_cells; x++) {
309 long dred = (color_def->red >> 8) - (cells[x].red >> 8);
311 (color_def->green >> 8) - (cells[x].green >> 8);
313 (color_def->blue >> 8) - (cells[x].blue >> 8);
314 unsigned long delta =
315 dred * dred + dgreen * dgreen + dblue * dblue;
317 if (delta < nearest_delta) {
319 nearest_delta = delta;
322 color_def->red = cells[nearest].red;
323 color_def->green = cells[nearest].green;
324 color_def->blue = cells[nearest].blue;
326 return XAllocColor(display, screen_colormap, color_def);
330 static void push_new_stack(XlwMenuWidget mw, widget_value * val)
332 if (!mw->menu.new_stack) {
333 mw->menu.new_stack_length = 10;
335 (widget_value **) XtCalloc(mw->menu.new_stack_length,
336 sizeof(widget_value *));
337 } else if (mw->menu.new_depth == mw->menu.new_stack_length) {
338 mw->menu.new_stack_length *= 2;
340 (widget_value **) XtRealloc((char *)mw->menu.new_stack,
341 mw->menu.new_stack_length *
342 sizeof(widget_value *));
344 mw->menu.new_stack[mw->menu.new_depth++] = val;
347 static void pop_new_stack_if_no_contents(XlwMenuWidget mw)
349 if (mw->menu.new_depth &&
350 !mw->menu.new_stack[mw->menu.new_depth - 1]->contents)
351 mw->menu.new_depth -= 1;
354 static void make_old_stack_space(XlwMenuWidget mw, int n)
356 if (!mw->menu.old_stack) {
357 mw->menu.old_stack_length = max(10, n);
359 (widget_value **) XtCalloc(mw->menu.old_stack_length,
360 sizeof(widget_value *));
361 } else if (mw->menu.old_stack_length < n) {
362 while (mw->menu.old_stack_length < n)
363 mw->menu.old_stack_length *= 2;
366 (widget_value **) XtRealloc((char *)mw->menu.old_stack,
367 mw->menu.old_stack_length *
368 sizeof(widget_value *));
373 close_to_reference_time(Widget w, Time reference_time, XEvent * ev)
377 (ev->xbutton.time - reference_time
378 < (Time) XtGetMultiClickTime(XtDisplay(w)));
382 static int string_width(XlwMenuWidget mw,
391 Dimension width, height;
392 XmStringExtent(mw->menu.font_list, s, &width, &height);
397 XmbTextExtents(mw->menu.font_set, s, strlen(s), &ri, &rl);
402 XTextExtents(mw->menu.font, s, strlen(s), &drop, &drop, &drop, &xcs);
404 # endif /* USE_XFONTSET */
408 static char massaged_resource_char[256];
410 static void initialize_massaged_resource_char(void)
413 for (j = 0; j < (int)sizeof(massaged_resource_char); j++) {
414 if ((j >= 'a' && j <= 'z') ||
415 (j >= 'A' && j <= 'Z') ||
416 (j >= '0' && j <= '9') || (j == '_') || (j >= 0xa0))
417 massaged_resource_char[j] = (char)j;
419 massaged_resource_char['_'] = '_';
420 massaged_resource_char['+'] = 'P'; /* Convert C++ to cPP */
421 massaged_resource_char['.'] = '_'; /* Convert Buffers... to buffers___ */
424 static int string_width_u(XlwMenuWidget mw,
433 Dimension width, height;
438 # else /* ! USE_XFONTSET */
450 if (!XmStringGetLtoR(string, XmFONTLIST_DEFAULT_TAG, &chars))
455 charslength = strlen(chars);
456 newchars = (char *)alloca(charslength + 1);
458 for (i = j = 0; chars[i] && (j < charslength); i++)
459 if (chars[i] == '%' && chars[i + 1] == '_')
462 newchars[j++] = chars[i];
466 newstring = XmStringLtoRCreate(newchars, XmFONTLIST_DEFAULT_TAG);
467 XmStringExtent(mw->menu.font_list, newstring, &width, &height);
468 XmStringFree(newstring);
473 XmbTextExtents(mw->menu.font_set, newchars, j, &ri, &rl);
475 # else /* ! USE_XFONTSET */
476 XTextExtents(mw->menu.font, newchars, j, &drop, &drop, &drop, &xcs);
478 # endif /* USE_XFONTSET */
482 static void massage_resource_name(const char *in, char *out)
484 /* Turn a random string into something suitable for using as a resource.
487 "Kill Buffer" -> "killBuffer"
488 "Find File..." -> "findFile___"
489 "Search and Replace..." -> "searchAndReplace___"
490 "C++ Mode Commands" -> "cppModeCommands"
492 Valid characters in a resource NAME component are: a-zA-Z0-9_
495 #ifdef PRINT_XLWMENU_RESOURCE_CONVERSIONS
496 /* Compile with -DPRINT_XLWMENU_RESOURCE_CONVERSIONS to generate a
497 translation file for menu localizations. */
498 char *save_in = in, *save_out = out;
501 Boolean firstp = True;
503 if (*in == '%' && *(in + 1) == '_')
508 if (*in == '%' && *(in + 1) == '%')
510 ch = massaged_resource_char[(unsigned char)*in++];
512 int int_ch = (int)(unsigned char)ch;
514 firstp ? tolower(int_ch) : toupper(int_ch);
517 massaged_resource_char[(unsigned char)
521 if (!*(in - 1)) /* Overshot the NULL byte? */
528 #ifdef PRINT_XLWMENU_RESOURCE_CONVERSIONS
529 printf("! Emacs*XlwMenu.%s.labelString:\t%s\n", save_out, save_in);
530 printf("Emacs*XlwMenu.%s.labelString:\n", save_out);
534 static XtResource nameResource[] = {
535 {"labelString", "LabelString", XtRString, sizeof(String),
539 /* This function searches STRING for parameter inserts of the form:
541 padding is either space (' ') or dash ('-') meaning
542 padding to the left or right of the inserted parameter.
543 In essence, all %1 strings are replaced by VALUE in the return value.
544 The caller is expected to free the return value using XtFree().
545 %% means insert one % (like printf).
546 %1 means insert VALUE.
547 %-1 means insert VALUE followed by one space. The latter is
548 not inserted if VALUE is a zero length string.
550 static char *parameterize_string(const char *string, const char *value)
554 unsigned int done = 0;
559 result = XtMalloc(1);
567 for (ntimes = 1, percent = string;
568 (percent = strchr(percent, '%')); ntimes++)
571 res_left = (ntimes * strlen(value)) + strlen(string) + 3;
572 result = XtMalloc(res_left+1);
573 result[res_left] = result[0] = '\0';
575 while ((percent = strchr(string, '%'))) {
576 unsigned int left_pad;
577 unsigned int right_pad;
579 off_t offset = percent-string;
581 if (percent[1] == '%') { /* it's a real % */
582 assert( res_left >= (1 + offset) );
584 strncat(result, string, 1 + offset);
585 res_left -= 1 + offset;
586 string = &percent[2]; /* after the second '%' */
587 continue; /* with the while() loop */
593 for (p = &percent[1]; /* test *p inside the loop */ ; p++) {
594 if (*p == ' ') { /* left pad */
596 } else if (*p == '-') { /* right pad */
598 } else if (*p == '1') { /* param and terminator */
599 assert( res_left >= offset );
601 strncat(result, string, offset);
602 if (value[0] != '\0') {
604 for (i = 0; i < left_pad; i++)
605 strncat(result, " ", res_left --);
606 strncat(result, value, res_left);
607 res_left -= strlen(value);
608 for (i = 0; i < right_pad; i++)
609 strncat(result, " ", res_left--);
611 string = &p[1]; /* after the '1' */
612 done++; /* no need to do old way */
613 break; /* out of for() loop */
614 } else { /* bogus, copy the format as is */
615 /* out of for() loop */
616 assert( res_left >= (1+ p - string) );
617 strncat(result, string, 1 + p - string);
618 res_left -= 1 + p - string;
619 string = (*p ? &p[1] : p);
620 offset = percent - string;
626 /* Copy the tail of the string */
627 strncat(result, string, res_left);
628 res_left -= strlen(string);
630 /* If we have not processed a % string, and we have a value, tail it. */
631 if (!done && value[0] != '\0') {
632 strncat(result, " ", res_left--);
633 strncat(result, value, res_left);
641 static XmString resource_widget_value(XlwMenuWidget mw, widget_value * val)
643 if (!val->toolkit_data) {
644 char *resourced_name = NULL;
645 char *converted_name, *str;
646 XmString complete_name;
647 char massaged_name[1024];
649 if (mw->menu.lookup_labels) {
650 /* Convert value style name into resource style name.
651 eg: "Free Willy" becomes "freeWilly" */
652 massage_resource_name(val->name, massaged_name);
654 /* If we have a value (parameter) see if we can find a "Named"
657 char named_name[1024];
658 sprintf(named_name, "%sNamed", massaged_name);
659 XtGetSubresources((Widget) mw,
660 (XtPointer) & resourced_name,
661 named_name, named_name,
662 nameResource, 1, NULL, 0);
665 /* If nothing yet, try to load from the massaged name. */
666 if (!resourced_name) {
667 XtGetSubresources((Widget) mw,
668 (XtPointer) & resourced_name,
669 massaged_name, massaged_name,
670 nameResource, 1, NULL, 0);
674 /* if (mw->menu.lookup_labels) */
675 /* Still nothing yet, use the name as the value. */
677 resourced_name = val->name;
679 /* Parameterize the string. */
681 parameterize_string(resourced_name, val->value);
683 /* nuke newline characters to prevent menubar screwups */
684 for (str = converted_name; *str; str++) {
689 /* Improve OSF's bottom line. */
690 #if (XmVersion >= 1002)
691 complete_name = XmStringCreateLocalized(converted_name);
693 complete_name = XmStringCreateLtoR(converted_name,
694 XmSTRING_DEFAULT_CHARSET);
696 XtFree(converted_name);
698 val->toolkit_data = complete_name;
699 val->free_toolkit_data = True;
701 return (XmString) val->toolkit_data;
706 /* These two routines should be a separate file..djw */
707 static char *xlw_create_localized_string(Widget w,
709 char **args, unsigned int nargs)
718 (XtPointer) & string,
719 name, name, nameResource, 1, NULL, 0);
724 return parameterize_string(string, arg);
728 xlw_create_localized_xmstring(Widget w,
729 char *name, char **args, unsigned int nargs)
731 char *string = xlw_create_localized_string(w, name, args, nargs);
733 XmStringCreateLtoR(string, XmSTRING_DEFAULT_CHARSET);
741 static char *resource_widget_value(XlwMenuWidget mw, widget_value * val)
743 if (!val->toolkit_data) {
744 char *resourced_name = NULL;
746 char massaged_name[1024];
748 if (mw->menu.lookup_labels) {
749 massage_resource_name(val->name, massaged_name);
751 XtGetSubresources((Widget) mw,
752 (XtPointer) & resourced_name,
753 massaged_name, massaged_name,
754 nameResource, 1, NULL, 0);
757 resourced_name = val->name;
759 complete_name = parameterize_string(resourced_name, val->value);
761 val->toolkit_data = complete_name;
762 /* nuke newline characters to prevent menubar screwups */
763 for (; *complete_name; complete_name++) {
764 if (complete_name[0] == '\n')
765 complete_name[0] = ' ';
767 val->free_toolkit_data = True;
769 return (char *)val->toolkit_data;
774 /* Code for drawing strings. */
775 static void string_draw(XlwMenuWidget mw, Window window, int x, int y, GC gc,
784 XmStringDraw(XtDisplay(mw), window, mw->menu.font_list, string, gc, x, y, 1000, /* ???? width */
785 XmALIGNMENT_BEGINNING, 0, /* ???? layout_direction */
789 XmbDrawString(XtDisplay(mw), window, mw->menu.font_set, gc,
790 x, y + mw->menu.font_ascent, string, strlen(string));
792 XDrawString(XtDisplay(mw), window, gc,
793 x, y + mw->menu.font_ascent, string, strlen(string));
794 # endif /* USE_XFONTSET */
800 string_draw_range(XlwMenuWidget mw,
802 int x, int y, GC gc, char *string, int start, int end)
805 Dimension width, height;
813 newstring = XmStringLtoRCreate(&string[start], XmFONTLIST_DEFAULT_TAG);
814 XmStringDraw(XtDisplay(mw), window, mw->menu.font_list, newstring, gc, x, y, 1000, /* ???? width */
815 XmALIGNMENT_BEGINNING, 0, /* ???? layout_direction */
817 XmStringExtent(mw->menu.font_list, newstring, &width, &height);
818 XmStringFree(newstring);
827 XmbDrawString(XtDisplay(mw), window, mw->menu.font_set, gc,
828 x, y + mw->menu.font_ascent, &string[start], end - start);
829 XmbTextExtents(mw->menu.font_set, &string[start], end - start, &ri,
838 XDrawString(XtDisplay(mw), window, gc,
839 x, y + mw->menu.font_ascent, &string[start], end - start);
840 XTextExtents(mw->menu.font, &string[start], end - start,
841 &drop, &drop, &drop, &xcs);
847 static void string_draw_u(XlwMenuWidget mw, Window window, int x, int y, GC gc,
860 if (!XmStringGetLtoR(string, XmFONTLIST_DEFAULT_TAG, &chars))
865 for (i = 0; chars[i]; ++i) {
866 if (chars[i] == '%' && chars[i + 1] == '_') {
869 x += string_draw_range(mw, window, x, y, gc, chars, s,
871 w = string_draw_range(mw, window, x, y, gc, chars,
874 /* underline next character */
875 XDrawLine(XtDisplay(mw), window, gc, x - 1,
876 y + mw->menu.font_ascent + 1,
877 x + w - 1, y + mw->menu.font_ascent + 1);
883 x += string_draw_range(mw, window, x, y, gc, chars, s, i);
890 binding_draw(XlwMenuWidget mw, Window w, int x, int y, GC gc, char *value)
893 XmString xm_value = XmStringCreateLtoR(value, XmSTRING_DEFAULT_CHARSET);
894 string_draw(mw, w, x, y, gc, xm_value);
895 XmStringFree(xm_value);
897 string_draw(mw, w, x, y, gc, value);
901 /* Low level code for drawing 3-D edges. */
903 shadow_rectangle_draw(Display * dpy,
909 unsigned int height, unsigned int thickness)
918 points[1].x = x + width;
920 points[2].x = x + width - thickness;
921 points[2].y = y + thickness;
923 points[3].y = y + thickness;
924 XFillPolygon(dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
926 points[0].y = y + thickness;
928 points[1].y = y + height;
929 points[2].x = x + thickness;
930 points[2].y = y + height - thickness;
931 points[3].x = x + thickness;
932 points[3].y = y + thickness;
933 XFillPolygon(dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
934 points[0].x = x + width;
936 points[1].x = x + width - thickness;
937 points[1].y = y + thickness;
938 points[2].x = x + width - thickness;
939 points[2].y = y + height - thickness;
940 points[3].x = x + width;
941 points[3].y = y + height - thickness;
942 XFillPolygon(dpy, window, bottom_gc, points, 4, Convex,
945 points[0].y = y + height;
946 points[1].x = x + width;
947 points[1].y = y + height;
948 points[2].x = x + width;
949 points[2].y = y + height - thickness;
950 points[3].x = x + thickness;
951 points[3].y = y + height - thickness;
952 XFillPolygon(dpy, window, bottom_gc, points, 4, Convex,
956 typedef enum e_shadow_type {
957 /* these are Motif compliant */
963 SHADOW_ETCHED_OUT_DASH,
964 SHADOW_ETCHED_IN_DASH,
967 SHADOW_SINGLE_DASHED_LINE,
968 SHADOW_DOUBLE_DASHED_LINE,
970 /* these are all non-Motif */
971 SHADOW_DOUBLE_ETCHED_OUT,
972 SHADOW_DOUBLE_ETCHED_IN,
973 SHADOW_DOUBLE_ETCHED_OUT_DASH,
974 SHADOW_DOUBLE_ETCHED_IN_DASH
978 shadow_draw(XlwMenuWidget mw,
981 unsigned int width, unsigned int height, shadow_type type)
983 Display *dpy = XtDisplay(mw);
986 int thickness = mw->menu.shadow_thickness;
990 Boolean etched = False;
992 switch ((unsigned int)type) {
993 case SHADOW_BACKGROUND:
994 top_gc = bottom_gc = mw->menu.background_gc;
996 case SHADOW_ETCHED_IN:
997 top_gc = mw->menu.shadow_bottom_gc;
998 bottom_gc = mw->menu.shadow_top_gc;
1001 case SHADOW_ETCHED_OUT:
1002 top_gc = mw->menu.shadow_top_gc;
1003 bottom_gc = mw->menu.shadow_bottom_gc;
1007 top_gc = mw->menu.shadow_bottom_gc;
1008 bottom_gc = mw->menu.shadow_top_gc;
1012 top_gc = mw->menu.shadow_top_gc;
1013 bottom_gc = mw->menu.shadow_bottom_gc;
1018 unsigned int half = thickness / 2;
1019 shadow_rectangle_draw(dpy,
1024 width - half, height - half,
1026 shadow_rectangle_draw(dpy,
1031 width - half, height - half, half);
1033 shadow_rectangle_draw(dpy,
1037 x, y, width, height, thickness);
1042 arrow_decoration_draw(XlwMenuWidget mw,
1044 int x, int y, unsigned int width, Boolean raised)
1046 Display *dpy = XtDisplay(mw);
1050 int thickness = mw->menu.shadow_thickness;
1053 int length = (int)((double)width * 0.87);
1054 int thick_med = (int)((double)thickness * 1.73);
1057 half_width = width / 2 + 1;
1059 half_width = width / 2;
1061 select_gc = mw->menu.background_gc;
1064 top_gc = mw->menu.shadow_bottom_gc;
1065 bottom_gc = mw->menu.shadow_top_gc;
1067 top_gc = mw->menu.shadow_top_gc;
1068 bottom_gc = mw->menu.shadow_bottom_gc;
1071 /* Fill internal area. We do this first so that the borders have a
1073 points[0].x = x + thickness;
1074 points[0].y = y + thickness;
1075 points[1].x = x + length - thickness;
1076 points[1].y = y + half_width;
1077 points[2].x = x + length - thickness;
1078 points[2].y = y + half_width + thickness;
1079 points[3].x = x + thickness;
1080 points[3].y = y + width - thickness;
1083 window, select_gc, points, 4, Convex, CoordModeOrigin);
1088 points[1].x = x + thickness;
1089 points[1].y = y + thick_med;
1090 points[2].x = x + thickness;
1091 points[2].y = y + width - thick_med;
1093 points[3].y = y + width;
1095 XFillPolygon(dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
1099 points[0].y = y + width;
1100 points[1].x = x + length;
1101 points[1].y = y + half_width;
1102 points[2].x = x + length - (thickness + thickness);
1103 points[2].y = y + half_width;
1104 points[3].x = x + thickness;
1105 points[3].y = y + width - thick_med;
1107 XFillPolygon(dpy, window, bottom_gc, points, 4, Convex,
1113 points[1].x = x + length;
1114 points[1].y = y + half_width;
1115 points[2].x = x + length - (thickness + thickness);
1116 points[2].y = y + half_width;
1117 points[3].x = x + thickness;
1118 points[3].y = y + thick_med;
1120 XFillPolygon(dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
1124 toggle_decoration_draw(XlwMenuWidget mw,
1126 int x, int y, unsigned int width, Boolean set)
1128 Display *dpy = XtDisplay(mw);
1129 int thickness = mw->menu.shadow_thickness;
1131 GC select_gc = mw->menu.select_gc;
1138 /* Fill internal area. */
1145 width - (2 * thickness),
1146 width - (2 * thickness));
1148 shadow_draw(mw, window, x, y, width, width, type);
1152 radio_decoration_draw(XlwMenuWidget mw,
1154 int x, int y, unsigned int width, Boolean enabled)
1156 Display *dpy = XtDisplay(mw);
1159 GC select_gc = mw->menu.select_gc;
1160 int thickness = mw->menu.shadow_thickness;
1170 half_width = width / 2;
1173 top_gc = mw->menu.shadow_bottom_gc;
1174 bottom_gc = mw->menu.shadow_top_gc;
1176 top_gc = mw->menu.shadow_top_gc;
1177 bottom_gc = mw->menu.shadow_bottom_gc;
1181 /* Draw the bottom first, just in case the regions overlap.
1182 The top should cast the longer shadow. */
1183 points[0].x = x; /* left corner */
1184 points[0].y = y + half_width;
1185 points[1].x = x + half_width; /* bottom corner */
1186 points[1].y = y + width;
1187 points[2].x = x + half_width; /* bottom inside corner */
1188 points[2].y = y + width - thickness;
1189 points[3].x = x + thickness; /* left inside corner */
1190 points[3].y = y + half_width;
1192 XFillPolygon(dpy, window, bottom_gc, points, 4, Convex,
1195 points[0].x = x + half_width; /* bottom corner */
1196 points[0].y = y + width;
1197 points[1].x = x + width; /* right corner */
1198 points[1].y = y + half_width;
1199 points[2].x = x + width - thickness; /* right inside corner */
1200 points[2].y = y + half_width;
1201 points[3].x = x + half_width; /* bottom inside corner */
1202 points[3].y = y + width - thickness;
1204 XFillPolygon(dpy, window, bottom_gc, points, 4, Convex,
1207 points[0].x = x; /* left corner */
1208 points[0].y = y + half_width;
1209 points[1].x = x + half_width; /* top corner */
1211 points[2].x = x + half_width; /* top inside corner */
1212 points[2].y = y + thickness;
1213 points[3].x = x + thickness; /* left inside corner */
1214 points[3].y = y + half_width;
1216 XFillPolygon(dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
1218 points[0].x = x + half_width; /* top corner */
1220 points[1].x = x + width; /* right corner */
1221 points[1].y = y + half_width;
1222 points[2].x = x + width - thickness; /* right inside corner */
1223 points[2].y = y + half_width;
1224 points[3].x = x + half_width; /* top inside corner */
1225 points[3].y = y + thickness;
1227 XFillPolygon(dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
1229 /* Draw the bottom first, just in case the regions overlap.
1230 The top should cast the longer shadow. */
1232 points[npoints].x = x; /* left corner */
1233 points[npoints++].y = y + half_width;
1234 points[npoints].x = x + half_width; /* bottom corner */
1235 points[npoints++].y = y + width;
1236 points[npoints].x = x + width; /* right corner */
1237 points[npoints++].y = y + half_width;
1238 points[npoints].x = x + width - thickness; /* right inside corner */
1239 points[npoints++].y = y + half_width;
1240 points[npoints].x = x + half_width; /* bottom inside corner */
1241 points[npoints++].y = y + width - thickness;
1242 points[npoints].x = x + thickness; /* left inside corner */
1243 points[npoints++].y = y + half_width;
1245 XFillPolygon(dpy, window, bottom_gc,
1246 points, npoints, Nonconvex, CoordModeOrigin);
1250 points[npoints].x = x; /* left corner */
1251 points[npoints++].y = y + half_width;
1252 points[npoints].x = x + half_width; /* top corner */
1253 points[npoints++].y = y;
1254 points[npoints].x = x + width; /* right corner */
1255 points[npoints++].y = y + half_width;
1256 points[npoints].x = x + width - thickness; /* right inside corner */
1257 points[npoints++].y = y + half_width;
1258 points[npoints].x = x + half_width; /* top inside corner */
1259 points[npoints++].y = y + thickness;
1260 points[npoints].x = x + thickness; /* left inside corner */
1261 points[npoints++].y = y + half_width;
1263 XFillPolygon(dpy, window, top_gc, points, npoints, Nonconvex,
1267 /* Fill internal area. */
1269 points[0].x = x + thickness;
1270 points[0].y = y + half_width;
1271 points[1].x = x + half_width;
1272 points[1].y = y + thickness;
1273 points[2].x = x + width - thickness;
1274 points[2].y = y + half_width;
1275 points[3].x = x + half_width;
1276 points[3].y = y + width - thickness;
1279 select_gc, points, 4, Convex, CoordModeOrigin);
1284 separator_decoration_draw(XlwMenuWidget mw,
1288 Boolean vertical, shadow_type type)
1290 Display *dpy = XtDisplay(mw);
1293 unsigned int offset = 0;
1294 unsigned int num_separators = 1;
1295 unsigned int top_line_thickness = 0;
1296 unsigned int bottom_line_thickness = 0;
1297 Boolean dashed = False;
1299 switch ((unsigned int)type) {
1300 case SHADOW_NO_LINE: /* nothing to do */
1302 case SHADOW_DOUBLE_LINE:
1304 case SHADOW_SINGLE_LINE:
1305 top_gc = bottom_gc = mw->menu.foreground_gc;
1306 top_line_thickness = 1;
1308 case SHADOW_DOUBLE_DASHED_LINE:
1310 case SHADOW_SINGLE_DASHED_LINE:
1311 top_gc = bottom_gc = mw->menu.foreground_gc;
1312 top_line_thickness = 1;
1315 case SHADOW_DOUBLE_ETCHED_OUT_DASH:
1317 case SHADOW_ETCHED_OUT_DASH:
1318 top_gc = mw->menu.shadow_top_gc;
1319 bottom_gc = mw->menu.shadow_bottom_gc;
1320 top_line_thickness = mw->menu.shadow_thickness / 2;
1321 bottom_line_thickness =
1322 mw->menu.shadow_thickness - top_line_thickness;
1325 case SHADOW_DOUBLE_ETCHED_IN_DASH:
1327 case SHADOW_ETCHED_IN_DASH:
1328 top_gc = mw->menu.shadow_bottom_gc;
1329 bottom_gc = mw->menu.shadow_top_gc;
1330 top_line_thickness = mw->menu.shadow_thickness / 2;
1331 bottom_line_thickness =
1332 mw->menu.shadow_thickness - top_line_thickness;
1335 case SHADOW_DOUBLE_ETCHED_OUT:
1337 case SHADOW_ETCHED_OUT:
1338 top_gc = mw->menu.shadow_top_gc;
1339 bottom_gc = mw->menu.shadow_bottom_gc;
1340 top_line_thickness = mw->menu.shadow_thickness / 2;
1341 bottom_line_thickness =
1342 mw->menu.shadow_thickness - top_line_thickness;
1344 case SHADOW_DOUBLE_ETCHED_IN:
1346 case SHADOW_ETCHED_IN:
1348 top_gc = mw->menu.shadow_bottom_gc;
1349 bottom_gc = mw->menu.shadow_top_gc;
1350 top_line_thickness = mw->menu.shadow_thickness / 2;
1351 bottom_line_thickness =
1352 mw->menu.shadow_thickness - top_line_thickness;
1358 values.line_style = LineOnOffDash;
1359 if (top_line_thickness > 0)
1360 XChangeGC(dpy, top_gc, GCLineStyle, &values);
1361 if (bottom_line_thickness > 0 && bottom_gc != top_gc)
1362 XChangeGC(dpy, bottom_gc, GCLineStyle, &values);
1365 while (num_separators--) {
1367 for (i = 0; i < top_line_thickness; i++)
1368 XDrawLine(dpy, window, top_gc, x, y + i, x + width,
1371 for (i = 0; i < bottom_line_thickness; i++)
1372 XDrawLine(dpy, window, bottom_gc,
1373 x, y + top_line_thickness + offset + i,
1375 y + top_line_thickness + offset + i);
1376 y += (top_line_thickness + offset + bottom_line_thickness + 1);
1381 values.line_style = LineSolid;
1382 if (top_line_thickness > 0)
1383 XChangeGC(dpy, top_gc, GCLineStyle, &values);
1384 if (bottom_line_thickness > 0 && bottom_gc != top_gc)
1385 XChangeGC(dpy, bottom_gc, GCLineStyle, &values);
1389 #define SLOPPY_TYPES 0 /* 0=off, 1=error check, 2=easy to please */
1391 #if SLOPPY_TYPES < 2
1393 static char *wv_types[] = {
1405 static void print_widget_value(widget_value * wv, int just_one, int depth)
1409 for (i = 0; i < depth; i++)
1413 printf("%s(null widget value pointer)\n", d);
1416 printf("%stype: %s\n", d, wv_types[wv->type]);
1418 printf("%sname: %s\n", d, (wv->name ? wv->name : "(null)"));
1421 printf("%sname: %s\n", d, wv->name);
1424 printf("%svalue: %s\n", d, wv->value);
1426 printf("%skey: %s\n", d, wv->key);
1427 printf("%senabled: %d\n", d, wv->enabled);
1429 printf("\n%scontents: \n", d);
1430 print_widget_value(wv->contents, 0, depth + 5);
1432 if (!just_one && wv->next) {
1434 print_widget_value(wv->next, 0, depth);
1437 #endif /* SLOPPY_TYPES < 2 */
1439 static Boolean all_dashes_p(char *s)
1442 if (!s || s[0] == '\0')
1444 for (p = s; *p == '-'; p++) ;
1446 if (*p == '!' || *p == '\0')
1450 #endif /* SLOPPY_TYPES */
1452 static widget_value_type menu_item_type(widget_value * val)
1454 if (val->type != UNSPECIFIED_TYPE)
1457 else if (all_dashes_p(val->name))
1458 return SEPARATOR_TYPE;
1459 else if (val->name && val->name[0] == '\0') /* push right */
1460 return PUSHRIGHT_TYPE;
1461 else if (val->contents) /* cascade */
1462 return CASCADE_TYPE;
1463 else if (val->call_data) /* push button */
1470 return UNSPECIFIED_TYPE; /* Not reached */
1475 label_button_size(XlwMenuWidget mw,
1478 unsigned int *toggle_width,
1479 unsigned int *label_width,
1480 unsigned int *bindings_width, unsigned int *height)
1482 *height = (mw->menu.font_ascent + mw->menu.font_descent +
1483 2 * mw->menu.vertical_margin +
1484 2 * mw->menu.shadow_thickness);
1485 /* no left column decoration */
1486 *toggle_width = mw->menu.horizontal_margin + mw->menu.shadow_thickness;
1488 *label_width = string_width_u(mw, resource_widget_value(mw, val));
1490 mw->menu.horizontal_margin + mw->menu.shadow_thickness;
1494 label_button_draw(XlwMenuWidget mw,
1497 Boolean highlighted,
1501 unsigned int height,
1502 unsigned int label_offset, unsigned int binding_tab)
1504 int y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin;
1509 mw->menu.shadow_thickness + mw->menu.horizontal_margin;
1511 if (highlighted && (in_menubar || val->contents))
1512 gc = mw->menu.highlight_gc;
1513 else if (in_menubar || val->contents)
1514 gc = mw->menu.foreground_gc;
1516 gc = mw->menu.title_gc;
1518 /* Draw the label string. */
1521 x + label_offset, y + y_offset,
1522 gc, resource_widget_value(mw, val));
1526 push_button_size(XlwMenuWidget mw,
1529 unsigned int *toggle_width,
1530 unsigned int *label_width,
1531 unsigned int *bindings_width, unsigned int *height)
1534 label_button_size(mw, val, in_menubar,
1535 toggle_width, label_width, bindings_width, height);
1537 /* key bindings to display? */
1538 if (!in_menubar && val->key) {
1542 XmStringCreateLtoR(val->key, XmSTRING_DEFAULT_CHARSET);
1543 w = string_width(mw, key);
1546 char *key = val->key;
1547 w = string_width(mw, key);
1549 *bindings_width += w + mw->menu.column_spacing;
1554 push_button_draw(XlwMenuWidget mw,
1557 Boolean highlighted,
1561 unsigned int height,
1562 unsigned int label_offset, unsigned int binding_offset)
1564 int y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin;
1567 Boolean menu_pb = in_menubar && (menu_item_type(val) == BUTTON_TYPE);
1569 /* Draw the label string. */
1572 mw->menu.shadow_thickness + mw->menu.horizontal_margin;
1576 gc = mw->menu.highlight_gc;
1578 gc = mw->menu.inactive_gc;
1579 } else if (menu_pb) {
1581 gc = mw->menu.button_gc;
1583 gc = mw->menu.inactive_button_gc;
1586 gc = mw->menu.foreground_gc;
1588 gc = mw->menu.inactive_gc;
1593 x + label_offset, y + y_offset,
1594 gc, resource_widget_value(mw, val));
1596 /* Draw the keybindings */
1598 if (!binding_offset) {
1599 unsigned int s_width =
1600 string_width(mw, resource_widget_value(mw, val));
1602 label_offset + s_width + mw->menu.shadow_thickness;
1604 binding_draw(mw, window,
1605 x + binding_offset + mw->menu.column_spacing,
1606 y + y_offset, gc, val->key);
1609 /* Draw the shadow */
1616 selected ? SHADOW_ETCHED_OUT : SHADOW_ETCHED_IN);
1621 type = SHADOW_BACKGROUND;
1624 shadow_draw(mw, window, x, y, width, height, type);
1627 static unsigned int arrow_decoration_height(XlwMenuWidget mw)
1629 int result = (mw->menu.font_ascent + mw->menu.font_descent) / 2;
1631 result += 2 * mw->menu.shadow_thickness;
1633 if (result > (mw->menu.font_ascent + mw->menu.font_descent))
1634 result = mw->menu.font_ascent + mw->menu.font_descent;
1640 cascade_button_size(XlwMenuWidget mw,
1643 unsigned int *toggle_width,
1644 unsigned int *label_width,
1645 unsigned int *arrow_width, unsigned int *height)
1648 label_button_size(mw, val, in_menubar,
1649 toggle_width, label_width, arrow_width, height);
1650 /* we have a pull aside arrow */
1653 arrow_decoration_height(mw) + mw->menu.column_spacing;
1658 cascade_button_draw(XlwMenuWidget mw,
1661 Boolean highlighted,
1665 unsigned int height,
1666 unsigned int label_offset, unsigned int binding_offset)
1670 /* Draw the label string. */
1671 label_button_draw(mw, val, in_menubar, highlighted,
1672 window, x, y, width, height, label_offset,
1675 /* Draw the pull aside arrow */
1676 if (!in_menubar && val->contents) {
1678 unsigned int arrow_height = arrow_decoration_height(mw);
1681 mw->menu.shadow_thickness + mw->menu.vertical_margin +
1682 (mw->menu.font_ascent + mw->menu.font_descent -
1685 if (!binding_offset) {
1686 unsigned int s_width =
1687 string_width(mw, resource_widget_value(mw, val));
1690 label_offset = mw->menu.shadow_thickness +
1691 mw->menu.horizontal_margin;
1694 label_offset + s_width + mw->menu.shadow_thickness;
1697 arrow_decoration_draw(mw,
1699 x + binding_offset +
1700 mw->menu.column_spacing, y + y_offset,
1701 arrow_height, highlighted);
1704 /* Draw the shadow */
1708 type = SHADOW_BACKGROUND;
1710 shadow_draw(mw, window, x, y, width, height, type);
1713 static unsigned int toggle_decoration_height(XlwMenuWidget mw)
1716 if (mw->menu.indicator_size > 0)
1717 rv = mw->menu.indicator_size;
1719 rv = mw->menu.font_ascent;
1721 if (rv > (mw->menu.font_ascent + mw->menu.font_descent))
1722 rv = mw->menu.font_ascent + mw->menu.font_descent;
1724 /* radio button can't be smaller than its border or a filling
1725 error will occur. */
1726 if (rv < 2 * mw->menu.shadow_thickness)
1727 rv = 2 * mw->menu.shadow_thickness;
1733 toggle_button_size(XlwMenuWidget mw,
1736 unsigned int *toggle_width,
1737 unsigned int *label_width,
1738 unsigned int *bindings_width, unsigned int *height)
1741 push_button_size(mw, val, in_menubar,
1742 toggle_width, label_width, bindings_width, height);
1743 /* we have a toggle */
1744 *toggle_width += toggle_decoration_height(mw) + mw->menu.column_spacing;
1748 toggle_button_draw(XlwMenuWidget mw,
1751 Boolean highlighted,
1755 unsigned int height,
1756 unsigned int label_tab, unsigned int binding_tab)
1760 unsigned int t_height = toggle_decoration_height(mw);
1762 /* Draw a toggle. */
1763 x_offset = mw->menu.shadow_thickness + mw->menu.horizontal_margin;
1764 y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin;
1766 (mw->menu.font_ascent + mw->menu.font_descent - t_height) / 2;
1768 toggle_decoration_draw(mw, window, x + x_offset, y + y_offset,
1769 t_height, val->selected);
1771 /* Draw the pushbutton parts. */
1772 push_button_draw(mw, val, in_menubar, highlighted, window, x, y, width,
1773 height, label_tab, binding_tab);
1776 static unsigned int radio_decoration_height(XlwMenuWidget mw)
1778 return toggle_decoration_height(mw);
1782 radio_button_draw(XlwMenuWidget mw,
1785 Boolean highlighted,
1789 unsigned int height,
1790 unsigned int label_tab, unsigned int binding_tab)
1794 unsigned int r_height = radio_decoration_height(mw);
1796 /* Draw a toggle. */
1797 x_offset = mw->menu.shadow_thickness + mw->menu.horizontal_margin;
1798 y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin;
1800 (mw->menu.font_ascent + mw->menu.font_descent - r_height) / 2;
1802 radio_decoration_draw(mw, window, x + x_offset, y + y_offset, r_height,
1805 /* Draw the pushbutton parts. */
1806 push_button_draw(mw, val, in_menubar, highlighted, window, x, y, width,
1807 height, label_tab, binding_tab);
1810 static struct _shadow_names {
1813 } shadow_names[] = {
1816 "singleLine", SHADOW_SINGLE_LINE}, {
1817 "doubleLine", SHADOW_DOUBLE_LINE}, {
1818 "singleDashedLine", SHADOW_SINGLE_DASHED_LINE}, {
1819 "doubleDashedLine", SHADOW_DOUBLE_DASHED_LINE}, {
1820 "noLine", SHADOW_NO_LINE}, {
1821 "shadowEtchedIn", SHADOW_ETCHED_IN}, {
1822 "shadowEtchedOut", SHADOW_ETCHED_OUT}, {
1823 "shadowEtchedInDash", SHADOW_ETCHED_IN_DASH}, {
1824 "shadowEtchedOutDash", SHADOW_ETCHED_OUT_DASH},
1827 "shadowDoubleEtchedIn", SHADOW_DOUBLE_ETCHED_IN}, {
1828 "shadowDoubleEtchedOut", SHADOW_DOUBLE_ETCHED_OUT}, {
1829 "shadowDoubleEtchedInDash", SHADOW_DOUBLE_ETCHED_IN_DASH}, {
1830 "shadowDoubleEtchedOutDash", SHADOW_DOUBLE_ETCHED_OUT_DASH}
1833 static shadow_type separator_type(char *name)
1837 for (i = 0; i < (int)(XtNumber(shadow_names)); i++) {
1838 if (strcmp(name, shadow_names[i].name) == 0)
1839 return shadow_names[i].type;
1842 return SHADOW_BACKGROUND;
1846 separator_decoration_height(XlwMenuWidget mw, widget_value * val)
1848 shadow_type st = separator_type(val->value);
1849 switch ((unsigned int)st) {
1850 case SHADOW_NO_LINE:
1851 case SHADOW_SINGLE_LINE:
1852 case SHADOW_SINGLE_DASHED_LINE:
1854 case SHADOW_DOUBLE_LINE:
1855 case SHADOW_DOUBLE_DASHED_LINE:
1857 case SHADOW_DOUBLE_ETCHED_OUT:
1858 case SHADOW_DOUBLE_ETCHED_IN:
1859 case SHADOW_DOUBLE_ETCHED_OUT_DASH:
1860 case SHADOW_DOUBLE_ETCHED_IN_DASH:
1861 return (1 + 2 * mw->menu.shadow_thickness);
1862 case SHADOW_ETCHED_OUT:
1863 case SHADOW_ETCHED_IN:
1865 return mw->menu.shadow_thickness;
1870 separator_size(XlwMenuWidget mw,
1873 unsigned int *toggle_width,
1874 unsigned int *label_width,
1875 unsigned int *rest_width, unsigned int *height)
1877 *height = separator_decoration_height(mw, val);
1879 *toggle_width = *rest_width = 0;
1883 separator_draw(XlwMenuWidget mw,
1886 Boolean highlighted,
1890 unsigned int height,
1891 unsigned int label_tab, unsigned int binding_tab)
1893 unsigned int sep_width;
1900 separator_decoration_draw(mw,
1905 in_menubar, separator_type(val->value));
1909 pushright_size(XlwMenuWidget mw,
1912 unsigned int *toggle_width,
1913 unsigned int *label_width,
1914 unsigned int *rest_width, unsigned int *height)
1916 *height = *label_width = *toggle_width = *rest_width = 0;
1920 size_menu_item(XlwMenuWidget mw,
1923 unsigned int *toggle_width,
1924 unsigned int *label_width,
1925 unsigned int *rest_width, unsigned int *height)
1927 void (*function_ptr) (XlwMenuWidget _mw,
1928 widget_value * _val,
1929 Boolean _in_menubar,
1930 unsigned int *_toggle_width,
1931 unsigned int *_label_width,
1932 unsigned int *_rest_width, unsigned int *_height);
1933 widget_value_type mt = menu_item_type(val);
1935 switch ((unsigned int)mt) {
1938 function_ptr = toggle_button_size;
1940 case SEPARATOR_TYPE:
1941 function_ptr = separator_size;
1943 case INCREMENTAL_TYPE:
1945 function_ptr = cascade_button_size;
1948 function_ptr = push_button_size;
1950 case PUSHRIGHT_TYPE:
1951 function_ptr = pushright_size;
1955 function_ptr = label_button_size;
1959 (*function_ptr) (mw,
1962 toggle_width, label_width, rest_width, height);
1966 display_menu_item(XlwMenuWidget mw,
1970 Boolean highlighted, Boolean horizontal, Boolean just_compute)
1972 int x = where->x /* + mw->menu.shadow_thickness */ ;
1973 int y = where->y /* + mw->menu.shadow_thickness */ ;
1974 unsigned int toggle_width;
1975 unsigned int label_width;
1976 unsigned int binding_width;
1978 unsigned int height;
1979 unsigned int label_tab;
1980 unsigned int binding_tab;
1981 void (*function_ptr) (XlwMenuWidget _mw,
1982 widget_value * _val,
1983 Boolean _in_menubar,
1984 Boolean _highlighted,
1987 unsigned int _width,
1988 unsigned int _height,
1989 unsigned int _label_tab,
1990 unsigned int _binding_tab);
1991 widget_value_type mt;
1993 size_menu_item(mw, val, horizontal,
1994 &toggle_width, &label_width, &binding_width, &height);
1997 width = toggle_width + label_width + binding_width;
1998 height = ws->height - 2 * mw->menu.shadow_thickness;
2000 width = ws->width - 2 * mw->menu.shadow_thickness;
2001 toggle_width = ws->toggle_width;
2002 label_width = ws->label_width;
2011 label_tab = toggle_width;
2012 binding_tab = toggle_width + label_width;
2014 mt = menu_item_type(val);
2015 switch ((unsigned int)mt) {
2017 function_ptr = toggle_button_draw;
2020 function_ptr = radio_button_draw;
2022 case SEPARATOR_TYPE:
2023 function_ptr = separator_draw;
2025 case INCREMENTAL_TYPE:
2027 function_ptr = cascade_button_draw;
2030 function_ptr = push_button_draw;
2033 function_ptr = label_button_draw;
2035 default: /* do no drawing */
2039 (*function_ptr) (mw,
2044 x, y, width, height, label_tab, binding_tab);
2047 static void size_menu(XlwMenuWidget mw, int level)
2049 unsigned int toggle_width;
2050 unsigned int label_width;
2051 unsigned int rest_width;
2052 unsigned int height;
2053 unsigned int max_toggle_width = 0;
2054 unsigned int max_label_width = 0;
2055 unsigned int max_rest_width = 0;
2056 unsigned int max_height = 0;
2057 int horizontal_p = mw->menu.horizontal && (level == 0);
2061 if (level >= mw->menu.old_depth)
2064 ws = &mw->menu.windows[level];
2066 for (val = mw->menu.old_stack[level]->contents; val; val = val->next) {
2071 &label_width, &rest_width, &height);
2074 toggle_width + label_width + rest_width;
2075 if (height > max_height)
2076 max_height = height;
2078 if (max_toggle_width < toggle_width)
2079 max_toggle_width = toggle_width;
2080 if (max_label_width < label_width)
2081 max_label_width = label_width;
2082 if (max_rest_width < rest_width)
2083 max_rest_width = rest_width;
2084 max_height += height;
2088 ws->height = max_height;
2089 ws->width = max_label_width + max_rest_width + max_toggle_width;
2090 ws->toggle_width = max_toggle_width;
2091 ws->label_width = max_label_width;
2093 ws->width += 2 * mw->menu.shadow_thickness;
2094 ws->height += 2 * mw->menu.shadow_thickness;
2098 display_menu(XlwMenuWidget mw, int level, Boolean just_compute_p,
2099 XPoint * highlighted_pos, XPoint * hit, widget_value ** hit_return,
2100 widget_value * this, widget_value * that)
2103 widget_value *following_item;
2106 int horizontal_p = mw->menu.horizontal && (level == 0);
2108 int just_compute_this_one_p;
2110 if (level >= mw->menu.old_depth)
2113 if (level < mw->menu.old_depth - 1)
2114 following_item = mw->menu.old_stack[level + 1];
2116 if (lw_menu_accelerate
2117 && level == mw->menu.old_depth - 1
2118 && mw->menu.old_stack[level]->type == CASCADE_TYPE)
2119 just_compute_p = True;
2120 following_item = NULL;
2123 #if SLOPPY_TYPES == 1
2124 puts("===================================================================");
2125 print_widget_value(following_item, 1, 0);
2131 where.x = mw->menu.shadow_thickness;
2132 where.y = mw->menu.shadow_thickness;
2134 ws = &mw->menu.windows[level];
2135 for (val = mw->menu.old_stack[level]->contents; val; val = val->next) {
2138 highlighted_p = (val == following_item);
2139 /* If this is the partition (the dummy item which says that menus
2140 after this should be flushright) then figure out how big the
2141 following items are. This means we walk down the tail of the
2142 list twice, but that's no big deal - it's short.
2144 if (horizontal_p && (menu_item_type(val) == PUSHRIGHT_TYPE)) {
2146 XPoint flushright_size;
2148 flushright_size.x = 0;
2149 flushright_size.y = 0;
2150 for (rest = val; rest; rest = rest->next)
2151 display_menu_item(mw, rest, ws,
2153 highlighted_p, horizontal_p,
2156 ws->width - (flushright_size.x +
2157 mw->menu.shadow_thickness);
2158 if (new_x > where.x)
2160 /* We know what we need; don't draw this item. */
2164 if (highlighted_p && highlighted_pos) {
2166 highlighted_pos->x = where.x;
2168 highlighted_pos->y = where.y;
2171 just_compute_this_one_p =
2172 just_compute_p || ((this || that) && val != this
2177 display_menu_item(mw, val, ws, &where, highlighted_p,
2178 horizontal_p, just_compute_this_one_p);
2180 if (highlighted_p && highlighted_pos) {
2182 highlighted_pos->y = ws->height;
2184 highlighted_pos->x = ws->width;
2187 if (hit && !*hit_return) {
2188 if (horizontal_p && hit->x > start.x
2189 && hit->x <= where.x)
2191 else if (!horizontal_p && hit->y > start.y
2192 && hit->y <= where.y)
2197 where.y = mw->menu.shadow_thickness;
2199 where.x = mw->menu.shadow_thickness;
2202 /* Draw slab edges around menu */
2203 if (!just_compute_p)
2204 shadow_draw(mw, ws->window, 0, 0, ws->width, ws->height,
2209 static void set_new_state(XlwMenuWidget mw, widget_value * val, int level)
2213 mw->menu.new_depth = 0;
2214 for (i = 0; i < level; i++)
2215 push_new_stack(mw, mw->menu.old_stack[i]);
2217 push_new_stack(mw, val);
2220 static void make_windows_if_needed(XlwMenuWidget mw, int n)
2224 XSetWindowAttributes xswa;
2229 window_state *windows;
2232 if (mw->menu.windows_length >= n)
2235 root = RootWindowOfScreen(XtScreen(mw));
2236 /* grab the visual and depth from the nearest shell ancestor */
2237 visual = CopyFromParent;
2238 depth = CopyFromParent;
2240 while (visual == CopyFromParent && p) {
2242 visual = ((ShellWidget) p)->shell.visual;
2243 depth = p->core.depth;
2248 xswa.save_under = True;
2249 xswa.override_redirect = True;
2250 xswa.background_pixel = mw->core.background_pixel;
2251 xswa.border_pixel = mw->core.border_pixel;
2252 xswa.event_mask = (ExposureMask | ButtonMotionMask
2253 | ButtonReleaseMask | ButtonPressMask);
2254 xswa.cursor = mw->menu.cursor_shape;
2255 xswa.colormap = mw->core.colormap;
2256 mask = CWSaveUnder | CWOverrideRedirect | CWBackPixel | CWBorderPixel
2257 | CWEventMask | CWCursor | CWColormap;
2259 if (mw->menu.use_backing_store) {
2260 xswa.backing_store = Always;
2261 mask |= CWBackingStore;
2264 if (!mw->menu.windows) {
2266 (window_state *) XtMalloc(n * sizeof(window_state));
2270 (window_state *) XtRealloc((char *)mw->menu.windows,
2271 n * sizeof(window_state));
2272 start_at = mw->menu.windows_length;
2274 mw->menu.windows_length = n;
2276 windows = mw->menu.windows;
2278 for (i = start_at; i < n; i++) {
2281 windows[i].width = 1;
2282 windows[i].height = 1;
2284 XCreateWindow(XtDisplay(mw),
2287 0, depth, CopyFromParent, visual, mask,
2292 /* Make the window fit in the screen */
2294 fit_to_screen(XlwMenuWidget mw, window_state * ws, window_state * previous_ws,
2295 Boolean horizontal_p)
2297 int screen_width = WidthOfScreen(XtScreen(mw));
2298 int screen_height = HeightOfScreen(XtScreen(mw));
2302 else if ((int)(ws->x + ws->width) > screen_width) {
2304 ws->x = previous_ws->x - ws->width;
2306 ws->x = screen_width - ws->width;
2308 /* This check is to make sure we cut off the right side
2309 instead of the left side if the menu is wider than the
2317 else if ((int)(ws->y + ws->height) > screen_height) {
2319 /* A pulldown must either be entirely above or below the menubar.
2320 If we're here, the pulldown doesn't fit below the menubar, so
2321 let's determine if it will fit above the menubar.
2322 Only put it above if there is more room above than below.
2323 Note shadow_thickness offset to allow for slab surround.
2325 if (ws->y > (screen_height / 2))
2327 previous_ws->y - ws->height +
2328 mw->menu.shadow_thickness;
2330 ws->y = screen_height - ws->height;
2331 /* if it's taller than the screen, display the topmost part
2332 that will fit, beginning at the top of the screen. */
2339 /* Updates old_stack from new_stack and redisplays. */
2340 static void remap_menubar(XlwMenuWidget mw)
2344 XPoint selection_position;
2345 int old_depth = mw->menu.old_depth;
2346 int new_depth = mw->menu.new_depth;
2347 widget_value **old_stack;
2348 widget_value **new_stack;
2349 window_state *windows;
2350 widget_value *old_selection;
2351 widget_value *new_selection;
2353 /* Check that enough windows and old_stack are ready. */
2354 make_windows_if_needed(mw, new_depth);
2355 make_old_stack_space(mw, new_depth);
2356 windows = mw->menu.windows;
2357 old_stack = mw->menu.old_stack;
2358 new_stack = mw->menu.new_stack;
2360 /* compute the last identical different entry */
2361 for (i = 1; i < old_depth && i < new_depth; i++)
2362 if (old_stack[i] != new_stack[i])
2366 if (lw_menu_accelerate
2368 && last_same == old_depth - 1 && old_stack[last_same]->contents)
2371 /* Memorize the previously selected item to be able to refresh it */
2373 last_same + 1 < old_depth ? old_stack[last_same + 1] : NULL;
2375 last_same + 1 < new_depth ? new_stack[last_same + 1] : NULL;
2377 /* updates old_state from new_state. It has to be done now because
2378 display_menu (called below) uses the old_stack to know what to display. */
2379 for (i = last_same + 1; i < new_depth; i++)
2380 old_stack[i] = new_stack[i];
2382 mw->menu.old_depth = new_depth;
2384 /* refresh the last selection */
2385 selection_position.x = 0;
2386 selection_position.y = 0;
2387 display_menu(mw, last_same, new_selection == old_selection,
2388 &selection_position, NULL, NULL, old_selection,
2391 /* Now popup the new menus */
2392 for (i = last_same + 1; i < new_depth && new_stack[i]->contents; i++) {
2393 window_state *previous_ws = &windows[i - 1];
2394 window_state *ws = &windows[i];
2396 if (lw_menu_accelerate && i == new_depth - 1)
2399 ws->x = previous_ws->x + selection_position.x;
2400 ws->y = previous_ws->y + selection_position.y;
2402 /* take into account the slab around the new menu */
2403 ws->y -= mw->menu.shadow_thickness;
2406 widget_value *val = mw->menu.old_stack[i];
2407 if (val->contents->type == INCREMENTAL_TYPE) {
2408 /* okay, we're now doing a lisp callback to incrementally generate
2409 more of the menu. */
2410 XtCallCallbackList((Widget) mw,
2412 (XtPointer) val->contents);
2418 fit_to_screen(mw, ws, previous_ws, mw->menu.horizontal
2421 XClearWindow(XtDisplay(mw), ws->window);
2422 XMoveResizeWindow(XtDisplay(mw), ws->window, ws->x, ws->y,
2423 ws->width, ws->height);
2424 XMapRaised(XtDisplay(mw), ws->window);
2425 display_menu(mw, i, False, &selection_position, NULL, NULL,
2429 /* unmap the menus that popped down */
2431 last_same = new_depth;
2432 if (lw_menu_accelerate
2433 && last_same > 1 && new_stack[last_same - 1]->contents)
2436 for (i = last_same - 1; i < old_depth; i++)
2437 if (i >= last_same || !new_stack[i]->contents)
2438 XUnmapWindow(XtDisplay(mw), windows[i].window);
2442 motion_event_is_in_menu(XlwMenuWidget mw, XMotionEvent * ev, int level,
2443 XPoint * relative_pos)
2445 window_state *ws = &mw->menu.windows[level];
2446 int x = level == 0 ? ws->x : ws->x + mw->menu.shadow_thickness;
2447 int y = level == 0 ? ws->y : ws->y + mw->menu.shadow_thickness;
2448 relative_pos->x = ev->x_root - x;
2449 relative_pos->y = ev->y_root - y;
2450 return (x < ev->x_root && ev->x_root < (int)(x + ws->width) &&
2451 y < ev->y_root && ev->y_root < (int)(y + ws->height));
2455 map_event_to_widget_value(XlwMenuWidget mw, XMotionEvent * ev,
2456 widget_value ** val_ptr, int *level,
2457 Boolean * inside_menu)
2460 XPoint relative_pos;
2464 *inside_menu = False;
2466 /* Find the window */
2468 for (i = mw->menu.old_depth - 1; i >= 0; i--)
2470 for (i = 0; i <= mw->menu.old_depth - 1; i++)
2473 ws = &mw->menu.windows[i];
2474 if (ws && motion_event_is_in_menu(mw, ev, i, &relative_pos)) {
2475 *inside_menu = True; /* special logic for menubar below... */
2476 if ((ev->type == ButtonPress) || (ev->state != 0)) {
2477 display_menu(mw, i, True, NULL, &relative_pos,
2478 val_ptr, NULL, NULL);
2481 *inside_menu = True;
2483 } else if (mw->menu.horizontal || i == 0) {
2484 /* if we're clicking on empty part of the menubar, then
2485 unpost the stay-up menu */
2486 *inside_menu = False;
2495 static void make_drawing_gcs(XlwMenuWidget mw)
2498 unsigned long flags = (GCFont | GCForeground | GCBackground);
2501 xgcv.font = default_font_of_font_list(mw->menu.font_list)->fid;
2503 xgcv.font = mw->menu.font->fid;
2506 xgcv.foreground = mw->core.background_pixel;
2507 xgcv.background = mw->menu.foreground;
2508 mw->menu.background_gc = XtGetGC((Widget) mw, flags, &xgcv);
2510 xgcv.foreground = mw->menu.foreground;
2511 xgcv.background = mw->core.background_pixel;
2512 mw->menu.foreground_gc = XtGetGC((Widget) mw, flags, &xgcv);
2514 if (mw->menu.select_color != (Pixel) - 1) {
2515 xgcv.foreground = mw->menu.select_color;
2517 Display *dpy = XtDisplay(mw);
2518 if (CellsOfScreen(DefaultScreenOfDisplay(dpy)) <= 2) { /* mono */
2519 xgcv.foreground = mw->menu.foreground;
2520 } else { /* color */
2522 Colormap cmap = mw->core.colormap;
2523 xcolor.pixel = mw->core.background_pixel;
2524 XQueryColor(dpy, cmap, &xcolor);
2525 xcolor.red = (xcolor.red * 17) / 20;
2526 xcolor.green = (xcolor.green * 17) / 20;
2527 xcolor.blue = (xcolor.blue * 17) / 20;
2528 if (allocate_nearest_color(dpy, cmap, &xcolor))
2529 xgcv.foreground = xcolor.pixel;
2532 xgcv.background = mw->core.background_pixel;
2533 mw->menu.select_gc = XtGetGC((Widget) mw, flags, &xgcv);
2535 xgcv.foreground = mw->menu.foreground;
2536 xgcv.background = mw->core.background_pixel;
2537 xgcv.fill_style = FillStippled;
2538 xgcv.stipple = mw->menu.gray_pixmap;
2539 mw->menu.inactive_gc = XtGetGC((Widget) mw,
2540 (flags | GCFillStyle | GCStipple),
2543 xgcv.foreground = mw->menu.highlight_foreground;
2544 xgcv.background = mw->core.background_pixel;
2545 mw->menu.highlight_gc = XtGetGC((Widget) mw, flags, &xgcv);
2547 xgcv.foreground = mw->menu.title_foreground;
2548 xgcv.background = mw->core.background_pixel;
2549 mw->menu.title_gc = XtGetGC((Widget) mw, flags, &xgcv);
2551 xgcv.foreground = mw->menu.button_foreground;
2552 xgcv.background = mw->core.background_pixel;
2553 mw->menu.button_gc = XtGetGC((Widget) mw, flags, &xgcv);
2555 xgcv.fill_style = FillStippled;
2556 xgcv.stipple = mw->menu.gray_pixmap;
2557 mw->menu.inactive_button_gc = XtGetGC((Widget) mw,
2558 (flags | GCFillStyle | GCStipple),
2562 static void release_drawing_gcs(XlwMenuWidget mw)
2564 XtReleaseGC((Widget) mw, mw->menu.foreground_gc);
2565 XtReleaseGC((Widget) mw, mw->menu.button_gc);
2566 XtReleaseGC((Widget) mw, mw->menu.highlight_gc);
2567 XtReleaseGC((Widget) mw, mw->menu.title_gc);
2568 XtReleaseGC((Widget) mw, mw->menu.inactive_gc);
2569 XtReleaseGC((Widget) mw, mw->menu.inactive_button_gc);
2570 XtReleaseGC((Widget) mw, mw->menu.background_gc);
2571 XtReleaseGC((Widget) mw, mw->menu.select_gc);
2572 /* let's get some segvs if we try to use these... */
2573 mw->menu.foreground_gc = (GC) - 1;
2574 mw->menu.button_gc = (GC) - 1;
2575 mw->menu.highlight_gc = (GC) - 1;
2576 mw->menu.title_gc = (GC) - 1;
2577 mw->menu.inactive_gc = (GC) - 1;
2578 mw->menu.inactive_button_gc = (GC) - 1;
2579 mw->menu.background_gc = (GC) - 1;
2580 mw->menu.select_gc = (GC) - 1;
2583 #define MINL(x,y) ((((unsigned long) (x)) < ((unsigned long) (y))) \
2584 ? ((unsigned long) (x)) : ((unsigned long) (y)))
2586 static void make_shadow_gcs(XlwMenuWidget mw)
2589 unsigned long pm = 0;
2590 Display *dpy = XtDisplay((Widget) mw);
2591 Colormap cmap = mw->core.colormap;
2593 int top_frobbed = 0, bottom_frobbed = 0;
2595 if (mw->menu.top_shadow_color == (Pixel) (-1))
2596 mw->menu.top_shadow_color = mw->core.background_pixel;
2597 if (mw->menu.bottom_shadow_color == (Pixel) (-1))
2598 mw->menu.bottom_shadow_color = mw->menu.foreground;
2600 if (mw->menu.top_shadow_color == mw->core.background_pixel ||
2601 mw->menu.top_shadow_color == mw->menu.foreground) {
2602 topc.pixel = mw->core.background_pixel;
2603 XQueryColor(dpy, cmap, &topc);
2604 /* don't overflow/wrap! */
2605 topc.red = MINL(65535, topc.red * 1.2);
2606 topc.green = MINL(65535, topc.green * 1.2);
2607 topc.blue = MINL(65535, topc.blue * 1.2);
2608 if (allocate_nearest_color(dpy, cmap, &topc)) {
2609 if (topc.pixel == mw->core.background_pixel) {
2610 XFreeColors(dpy, cmap, &topc.pixel, 1, 0);
2611 topc.red = MINL(65535, topc.red + 0x8000);
2612 topc.green = MINL(65535, topc.green + 0x8000);
2613 topc.blue = MINL(65535, topc.blue + 0x8000);
2614 if (allocate_nearest_color(dpy, cmap, &topc)) {
2615 mw->menu.top_shadow_color = topc.pixel;
2618 mw->menu.top_shadow_color = topc.pixel;
2624 if (mw->menu.bottom_shadow_color == mw->menu.foreground ||
2625 mw->menu.bottom_shadow_color == mw->core.background_pixel) {
2626 botc.pixel = mw->core.background_pixel;
2627 XQueryColor(dpy, cmap, &botc);
2628 botc.red = (botc.red * 3) / 5;
2629 botc.green = (botc.green * 3) / 5;
2630 botc.blue = (botc.blue * 3) / 5;
2631 if (allocate_nearest_color(dpy, cmap, &botc)) {
2632 if (botc.pixel == mw->core.background_pixel) {
2633 XFreeColors(dpy, cmap, &botc.pixel, 1, 0);
2634 botc.red = MINL(65535, botc.red + 0x4000);
2635 botc.green = MINL(65535, botc.green + 0x4000);
2636 botc.blue = MINL(65535, botc.blue + 0x4000);
2637 if (allocate_nearest_color(dpy, cmap, &botc)) {
2638 mw->menu.bottom_shadow_color =
2642 mw->menu.bottom_shadow_color = botc.pixel;
2649 if (top_frobbed && bottom_frobbed) {
2651 ((topc.red / 3) + (topc.green / 3) + (topc.blue / 3));
2653 ((botc.red / 3) + (botc.green / 3) + (botc.blue / 3));
2654 if (bot_avg > top_avg) {
2655 Pixel tmp = mw->menu.top_shadow_color;
2656 mw->menu.top_shadow_color =
2657 mw->menu.bottom_shadow_color;
2658 mw->menu.bottom_shadow_color = tmp;
2659 } else if (topc.pixel == botc.pixel) {
2660 if (botc.pixel == mw->menu.foreground)
2661 mw->menu.top_shadow_color =
2662 mw->core.background_pixel;
2664 mw->menu.bottom_shadow_color =
2665 mw->menu.foreground;
2669 if (!mw->menu.top_shadow_pixmap &&
2670 mw->menu.top_shadow_color == mw->core.background_pixel) {
2671 mw->menu.top_shadow_pixmap = mw->menu.gray_pixmap;
2672 mw->menu.top_shadow_color = mw->menu.foreground;
2674 if (!mw->menu.bottom_shadow_pixmap &&
2675 mw->menu.bottom_shadow_color == mw->core.background_pixel) {
2676 mw->menu.bottom_shadow_pixmap = mw->menu.gray_pixmap;
2677 mw->menu.bottom_shadow_color = mw->menu.foreground;
2680 xgcv.fill_style = FillOpaqueStippled;
2681 xgcv.foreground = mw->menu.top_shadow_color;
2682 xgcv.background = mw->core.background_pixel;
2683 /* xgcv.stipple = mw->menu.top_shadow_pixmap; gtb */
2684 if (mw->menu.top_shadow_pixmap &&
2685 mw->menu.top_shadow_pixmap != XmUNSPECIFIED_PIXMAP)
2686 xgcv.stipple = mw->menu.top_shadow_pixmap;
2689 pm = (xgcv.stipple ? GCStipple | GCFillStyle : 0);
2690 mw->menu.shadow_top_gc =
2691 XtGetGC((Widget) mw, GCForeground | GCBackground | pm, &xgcv);
2693 xgcv.foreground = mw->menu.bottom_shadow_color;
2694 /* xgcv.stipple = mw->menu.bottom_shadow_pixmap; gtb */
2695 if (mw->menu.bottom_shadow_pixmap &&
2696 mw->menu.bottom_shadow_pixmap != XmUNSPECIFIED_PIXMAP)
2697 xgcv.stipple = mw->menu.bottom_shadow_pixmap;
2700 pm = (xgcv.stipple ? GCStipple | GCFillStyle : 0);
2701 mw->menu.shadow_bottom_gc =
2702 XtGetGC((Widget) mw, GCForeground | GCBackground | pm, &xgcv);
2705 static void release_shadow_gcs(XlwMenuWidget mw)
2707 XtReleaseGC((Widget) mw, mw->menu.shadow_top_gc);
2708 XtReleaseGC((Widget) mw, mw->menu.shadow_bottom_gc);
2711 static void extract_font_extents(XlwMenuWidget mw)
2714 /* Find the maximal ascent/descent of the fonts in the font list
2715 so that all menu items can be the same height... */
2716 mw->menu.font_ascent = 0;
2717 mw->menu.font_descent = 0;
2720 XmFontContext context;
2721 #if (XmVersion >= 1002)
2722 XmFontListEntry fontentry;
2724 XmStringCharSet charset;
2728 if (!XmFontListInitFontContext(&context, mw->menu.font_list))
2730 #if (XmVersion >= 1002)
2731 /* There is a BUG in the 1.2 version of XmFontListGetNextFont() (or more
2732 specifically, in _XmGetFirstFont()) that can cause a null pointer to be
2733 passed to XFontsOfFontSet. Use XmFontListNextEntry(), which is the
2734 newer equivalent, instead. Also, it supports font sets, and the
2735 older function doesn't. */
2736 while ((fontentry = XmFontListNextEntry(context))) {
2739 XtPointer one_of_them =
2740 XmFontListEntryGetFont(fontentry, &rettype);
2741 if (rettype == XmFONT_IS_FONTSET) {
2742 XFontSet fontset = (XFontSet) one_of_them;
2743 XFontStruct **fontstruct_list;
2744 char **fontname_list;
2746 XFontsOfFontSet(fontset, &fontstruct_list,
2748 while (--fontcount >= 0) {
2749 font = fontstruct_list[fontcount];
2751 (int)mw->menu.font_ascent)
2752 mw->menu.font_ascent =
2755 (int)mw->menu.font_descent)
2756 mw->menu.font_descent =
2759 } else { /* XmFONT_IS_FONT */
2761 font = (XFontStruct *) one_of_them;
2762 if (font->ascent > (int)mw->menu.font_ascent)
2763 mw->menu.font_ascent = font->ascent;
2764 if (font->descent > (int)mw->menu.font_descent)
2765 mw->menu.font_descent = font->descent;
2768 #else /* motif 1.1 */
2769 while (XmFontListGetNextFont(context, &charset, &font)) {
2770 if (font->ascent > (int)mw->menu.font_ascent)
2771 mw->menu.font_ascent = font->ascent;
2772 if (font->descent > (int)mw->menu.font_descent)
2773 mw->menu.font_descent = font->descent;
2776 #endif /* Motif version */
2777 XmFontListFreeFontContext(context);
2779 #else /* Not Motif */
2780 # ifdef USE_XFONTSET
2781 XFontStruct **fontstruct_list;
2782 char **fontname_list;
2784 int fontcount = XFontsOfFontSet(mw->menu.font_set, &fontstruct_list,
2786 mw->menu.font_ascent = 0;
2787 mw->menu.font_descent = 0;
2788 # if 0 /* nasty, personal debug, Kazz */
2789 fprintf(stderr, "fontSet count is %d\n", fontcount);
2791 while (--fontcount >= 0) {
2792 font = fontstruct_list[fontcount];
2793 if (font->ascent > (int)mw->menu.font_ascent)
2794 mw->menu.font_ascent = font->ascent;
2795 if (font->descent > (int)mw->menu.font_descent)
2796 mw->menu.font_descent = font->descent;
2798 # else /* ! USE_XFONTSET */
2799 mw->menu.font_ascent = mw->menu.font->ascent;
2800 mw->menu.font_descent = mw->menu.font->descent;
2802 #endif /* NEED_MOTIF */
2806 static XFontStruct *default_font_of_font_list(XmFontList font_list)
2808 XFontStruct *font = 0;
2810 /* Xm/Label.c does this: */
2811 _XmFontListGetDefaultFont(font_list, &font);
2814 XmFontContext context;
2815 #if (XmVersion >= 1002)
2816 XmFontListEntry fontentry;
2818 XtPointer one_of_them;
2820 XmStringCharSet charset;
2823 if (!XmFontListInitFontContext(&context, font_list))
2825 #if (XmVersion >= 1002)
2826 /* There is a BUG in the 1.2 version of XmFontListGetNextFont() (or more
2827 specifically, in _XmGetFirstFont()) that can cause a null pointer to be
2828 passed to XFontsOfFontSet. Use XmFontListNextEntry(), which is the
2829 newer equivalent, instead. */
2830 fontentry = XmFontListNextEntry(context);
2831 one_of_them = XmFontListEntryGetFont(fontentry, &rettype);
2832 if (rettype == XmFONT_IS_FONTSET) {
2833 XFontSet fontset = (XFontSet) one_of_them;
2834 XFontStruct **fontstruct_list;
2835 char **fontname_list;
2836 (void)XFontsOfFontSet(fontset, &fontstruct_list,
2838 font = fontstruct_list[0];
2839 } else { /* XmFONT_IS_FONT */
2841 font = (XFontStruct *) one_of_them;
2844 if (!XmFontListGetNextFont(context, &charset, &font))
2848 XmFontListFreeFontContext(context);
2856 #endif /* NEED_MOTIF */
2859 XlwMenuInitialize(Widget request, Widget new, ArgList args, Cardinal * num_args)
2861 /* Get the GCs and the widget size */
2862 XlwMenuWidget mw = (XlwMenuWidget) new;
2864 RootWindowOfScreen(DefaultScreenOfDisplay(XtDisplay(mw)));
2865 Display *display = XtDisplay(mw);
2867 /* mw->menu.cursor = XCreateFontCursor (display, mw->menu.cursor_shape); */
2868 mw->menu.cursor = mw->menu.cursor_shape;
2870 mw->menu.gray_pixmap =
2871 XCreatePixmapFromBitmapData(display, window, (char *)gray_bits,
2872 gray_width, gray_height, 1, 0, 1);
2875 /* #### Even if it's a kludge!!!, we should consider doing the same for
2877 /* The menu.font_list slot came from the *fontList resource (Motif standard.)
2878 The menu.font_list_2 slot came from the *font resource, for backward
2879 compatibility with older versions of this code, and consistency with the
2880 rest of emacs. If both font and fontList are specified, we use fontList.
2881 If only one is specified, we use that. If neither are specified, we
2882 use the "fallback" value. What a kludge!!!
2884 Note that this has the bug that a more general wildcard like "*fontList:"
2885 will override a more specific resource like "Emacs*menubar.font:". But
2886 I can't think of a way around that.
2888 if (mw->menu.font_list) /* if *fontList is specified, use that */
2890 else if (mw->menu.font_list_2) /* else if *font is specified, use that */
2891 mw->menu.font_list = mw->menu.font_list_2;
2892 else /* otherwise use default */
2893 mw->menu.font_list = mw->menu.fallback_font_list;
2896 make_drawing_gcs(mw);
2897 make_shadow_gcs(mw);
2898 extract_font_extents(mw);
2900 mw->menu.popped_up = False;
2901 mw->menu.pointer_grabbed = False;
2902 mw->menu.next_release_must_exit = False;
2904 mw->menu.old_depth = 1;
2905 mw->menu.old_stack = XtNew(widget_value *);
2906 mw->menu.old_stack_length = 1;
2907 mw->menu.old_stack[0] = mw->menu.contents;
2909 mw->menu.new_depth = 0;
2910 mw->menu.new_stack = 0;
2911 mw->menu.new_stack_length = 0;
2912 push_new_stack(mw, mw->menu.contents);
2914 mw->menu.windows = XtNew(window_state);
2915 mw->menu.windows_length = 1;
2916 mw->menu.windows[0].x = 0;
2917 mw->menu.windows[0].y = 0;
2918 mw->menu.windows[0].width = 0;
2919 mw->menu.windows[0].height = 0;
2922 mw->core.width = mw->menu.windows[0].width;
2923 mw->core.height = mw->menu.windows[0].height;
2926 static void XlwMenuClassInitialize(void)
2928 initialize_massaged_resource_char();
2932 XlwMenuRealize(Widget w, Mask * valueMask, XSetWindowAttributes * attributes)
2934 XlwMenuWidget mw = (XlwMenuWidget) w;
2935 XSetWindowAttributes xswa;
2938 (*xlwMenuWidgetClass->core_class.superclass->core_class.realize)
2939 (w, valueMask, attributes);
2941 xswa.save_under = True;
2942 xswa.cursor = mw->menu.cursor_shape;
2943 mask = CWSaveUnder | CWCursor;
2944 if (mw->menu.use_backing_store) {
2945 xswa.backing_store = Always;
2946 mask |= CWBackingStore;
2948 XChangeWindowAttributes(XtDisplay(w), XtWindow(w), mask, &xswa);
2950 mw->menu.windows[0].window = XtWindow(w);
2951 mw->menu.windows[0].x = w->core.x;
2952 mw->menu.windows[0].y = w->core.y;
2953 mw->menu.windows[0].width = w->core.width;
2954 mw->menu.windows[0].height = w->core.height;
2957 /* Only the toplevel menubar/popup is a widget so it's the only one that
2958 receives expose events through Xt. So we repaint all the other panes
2959 when receiving an Expose event. */
2960 static void XlwMenuRedisplay(Widget w, XEvent * ev, Region region)
2962 XlwMenuWidget mw = (XlwMenuWidget) w;
2965 if (mw->core.being_destroyed)
2968 for (i = 0; i < mw->menu.old_depth; i++)
2969 display_menu(mw, i, False, NULL, NULL, NULL, NULL, NULL);
2970 set_new_state(mw, NULL, mw->menu.old_depth); /* #### - ??? */
2971 remap_menubar(mw); /* #### - do these two lines do anything? */
2974 static void XlwMenuDestroy(Widget w)
2977 XlwMenuWidget mw = (XlwMenuWidget) w;
2979 if (mw->menu.pointer_grabbed) {
2980 XtUngrabPointer(w, CurrentTime);
2981 mw->menu.pointer_grabbed = False;
2984 release_drawing_gcs(mw);
2985 release_shadow_gcs(mw);
2987 /* this doesn't come from the resource db but is created explicitly
2988 so we must free it ourselves. */
2989 XFreePixmap(XtDisplay(mw), mw->menu.gray_pixmap);
2990 mw->menu.gray_pixmap = (Pixmap) - 1;
2992 /* Don't free mw->menu.contents because that comes from our creator.
2993 The `*_stack' elements are just pointers into `contents' so leave
2994 that alone too. But free the stacks themselves. */
2995 if (mw->menu.old_stack)
2996 XtFree((char *)mw->menu.old_stack);
2997 if (mw->menu.new_stack)
2998 XtFree((char *)mw->menu.new_stack);
3000 /* Remember, you can't free anything that came from the resource
3001 database. This includes:
3003 mw->menu.top_shadow_pixmap
3004 mw->menu.bottom_shadow_pixmap
3007 Also the color cells of top_shadow_color, bottom_shadow_color,
3008 foreground, and button_foreground will never be freed until this
3009 client exits. Nice, eh?
3012 /* start from 1 because the one in slot 0 is w->core.window */
3013 for (i = 1; i < mw->menu.windows_length; i++)
3014 XDestroyWindow(XtDisplay(mw), mw->menu.windows[i].window);
3015 if (mw->menu.windows)
3016 XtFree((char *)mw->menu.windows);
3020 XlwMenuSetValues(Widget current, Widget request, Widget new, ArgList args,
3021 Cardinal * num_args)
3023 XlwMenuWidget oldmw = (XlwMenuWidget) current;
3024 XlwMenuWidget newmw = (XlwMenuWidget) new;
3025 Boolean redisplay = False;
3028 if (newmw->menu.contents
3029 && newmw->menu.contents->contents
3030 && newmw->menu.contents->contents->change >= VISIBLE_CHANGE)
3033 if (newmw->core.background_pixel != oldmw->core.background_pixel
3034 || newmw->menu.foreground != oldmw->menu.foreground
3035 /* For the XEditResource protocol, which may want to change the font. */
3037 || newmw->menu.font_list != oldmw->menu.font_list
3038 || newmw->menu.font_list_2 != oldmw->menu.font_list_2
3039 || newmw->menu.fallback_font_list != oldmw->menu.fallback_font_list
3041 || newmw->menu.font != oldmw->menu.font
3044 release_drawing_gcs(newmw);
3045 make_drawing_gcs(newmw);
3048 for (i = 0; i < oldmw->menu.windows_length; i++) {
3049 XSetWindowBackground(XtDisplay(oldmw),
3050 oldmw->menu.windows[i].window,
3051 newmw->core.background_pixel);
3052 /* clear windows and generate expose events */
3053 XClearArea(XtDisplay(oldmw),
3054 oldmw->menu.windows[i].window, 0, 0, 0, 0,
3062 static void XlwMenuResize(Widget w)
3064 XlwMenuWidget mw = (XlwMenuWidget) w;
3066 mw->menu.windows[0].width = mw->core.width;
3067 mw->menu.windows[0].height = mw->core.height;
3070 /* Action procedures */
3072 handle_single_motion_event(XlwMenuWidget mw, XMotionEvent * ev,
3079 if (!map_event_to_widget_value(mw, ev, &val, &level, &stay_up)) {
3080 /* we wind up here when: (a) the event is in the menubar, (b) the
3081 event isn't in the menubar or any of the panes, (c) the event is on
3082 a disabled menu item */
3083 pop_new_stack_if_no_contents(mw);
3084 if (select_p && !stay_up) {
3085 /* pop down all menus and exit */
3086 mw->menu.next_release_must_exit = True;
3087 set_new_state(mw, (val = NULL), 1);
3090 /* we wind up here when: (a) the event pops up a pull_right menu,
3091 (b) a menu item that is not disabled is highlighted */
3092 if (select_p && mw->menu.bounce_down
3093 && close_to_reference_time((Widget) mw,
3094 mw->menu.menu_bounce_time,
3096 /* motion can cause more than one event. Don't bounce right back
3097 up if we've just bounced down. */
3099 } else if (select_p && mw->menu.bounce_down &&
3100 mw->menu.last_selected_val &&
3101 (mw->menu.last_selected_val == val)) {
3102 val = NULL; /* assigned to mw->last_selected_val below */
3103 mw->menu.menu_bounce_time = ev->time;
3104 /* popdown last menu if we're selecting the same menu item as we did
3105 last time and the XlwMenu.bounceDown resource is set, if the
3106 item is on the menubar itself, then exit. */
3107 if (level == (mw->menu.popped_up ? 0 : 1))
3108 mw->menu.next_release_must_exit = True;
3110 mw->menu.menu_bounce_time = 0;
3111 set_new_state(mw, val, level);
3113 mw->menu.last_selected_val = val;
3116 /* Sync with the display. Makes it feel better on X terms. */
3117 XFlush(XtDisplay(mw));
3121 handle_motion_event(XlwMenuWidget mw, XMotionEvent * ev, Boolean select_p)
3125 unsigned int state = ev->state;
3126 XMotionEvent *event = ev, dummy;
3128 /* allow motion events to be generated again */
3129 dummy.window = ev->window;
3131 && XQueryPointer(XtDisplay(mw), dummy.window,
3132 &dummy.root, &dummy.subwindow,
3133 &dummy.x_root, &dummy.y_root,
3134 &dummy.x, &dummy.y, &dummy.state)
3135 && dummy.state == state && (dummy.x_root != x || dummy.y_root != y)) {
3136 /* don't handle the event twice or that breaks bounce_down. --Stig */
3137 dummy.type = ev->type;
3141 lw_menu_accelerate = False;
3142 handle_single_motion_event(mw, event, select_p);
3145 Time x_focus_timestamp_really_sucks_fix_me_better;
3147 static void Start(Widget w, XEvent * ev, String * params, Cardinal * num_params)
3149 XlwMenuWidget mw = (XlwMenuWidget) w;
3151 lw_menubar_widget = w;
3153 lw_menu_active = True;
3155 if (!mw->menu.pointer_grabbed) {
3156 mw->menu.menu_post_time = ev->xbutton.time;
3157 mw->menu.menu_bounce_time = 0;
3158 mw->menu.next_release_must_exit = True;
3159 mw->menu.last_selected_val = NULL;
3160 x_focus_timestamp_really_sucks_fix_me_better =
3161 ((XButtonPressedEvent *) ev)->time;
3162 XtCallCallbackList((Widget) mw, mw->menu.open, NULL);
3164 /* notes the absolute position of the menubar window */
3165 mw->menu.windows[0].x = ev->xmotion.x_root - ev->xmotion.x;
3166 mw->menu.windows[0].y = ev->xmotion.y_root - ev->xmotion.y;
3168 XtGrabPointer((Widget) mw, False,
3169 (ButtonMotionMask | ButtonReleaseMask |
3170 ButtonPressMask), GrabModeAsync, GrabModeAsync,
3171 None, mw->menu.cursor_shape,
3172 ((XButtonPressedEvent *) ev)->time);
3173 mw->menu.pointer_grabbed = True;
3176 /* handles the down like a move, slots are mostly compatible */
3177 handle_motion_event(mw, &ev->xmotion, True);
3180 static void Drag(Widget w, XEvent * ev, String * params, Cardinal * num_params)
3182 XlwMenuWidget mw = (XlwMenuWidget) w;
3183 handle_motion_event(mw, &ev->xmotion, False);
3187 Select(Widget w, XEvent * ev, String * params, Cardinal * num_params)
3189 XlwMenuWidget mw = (XlwMenuWidget) w;
3190 widget_value *selected_item =
3191 mw->menu.old_stack[mw->menu.old_depth - 1];
3193 lw_menu_accelerate = False;
3195 /* If user releases the button quickly, without selecting anything,
3196 after the initial down-click that brought the menu up,
3198 if ((selected_item == 0 || selected_item->call_data == 0)
3199 && (!mw->menu.next_release_must_exit
3200 || close_to_reference_time(w, mw->menu.menu_post_time, ev))) {
3201 mw->menu.next_release_must_exit = False;
3205 /* pop down everything */
3206 mw->menu.new_depth = 1;
3209 /* Destroy() only gets called for popup menus. Menubar widgets aren't
3210 destroyed when their menu panes get nuked. */
3211 if (mw->menu.pointer_grabbed) {
3212 XtUngrabPointer((Widget) w, ev->xmotion.time);
3213 mw->menu.pointer_grabbed = False;
3216 if (mw->menu.popped_up) {
3217 mw->menu.popped_up = False;
3218 XtPopdown(XtParent(mw));
3221 lw_menu_active = False;
3223 x_focus_timestamp_really_sucks_fix_me_better =
3224 ((XButtonPressedEvent *) ev)->time;
3227 XtCallCallbackList((Widget) mw, mw->menu.select,
3228 (XtPointer) selected_item);
3231 /* Action procedures for keyboard accelerators */
3234 void xlw_set_menu(Widget w, widget_value * val)
3236 lw_menubar_widget = w;
3237 set_new_state((XlwMenuWidget) w, val, 1);
3240 /* prepare the menu structure via the call-backs */
3241 void xlw_map_menu(Time t)
3243 XlwMenuWidget mw = (XlwMenuWidget) lw_menubar_widget;
3245 lw_menu_accelerate = True;
3247 if (!mw->menu.pointer_grabbed) {
3248 XWindowAttributes ret;
3249 Window parent = 0UL, root = 0UL;
3250 Window *waste = NULL;
3251 unsigned int num_waste;
3253 lw_menu_active = True;
3255 mw->menu.menu_post_time = t;
3256 mw->menu.menu_bounce_time = 0;
3258 mw->menu.next_release_must_exit = True;
3259 mw->menu.last_selected_val = NULL;
3261 XtCallCallbackList((Widget) mw, mw->menu.open, NULL);
3263 /* do this for keyboards too! */
3264 /* notes the absolute position of the menubar window */
3266 mw->menu.windows [0].x = ev->xmotion.x_root - ev->xmotion.x;
3267 mw->menu.windows [0].y = ev->xmotion.y_root - ev->xmotion.y;
3270 /* get the geometry of the menubar */
3272 /* there has to be a better way than this. */
3274 mw->menu.windows[0].x = 0;
3275 mw->menu.windows[0].y = 0;
3277 parent = XtWindow(lw_menubar_widget);
3279 XGetWindowAttributes(XtDisplay(lw_menubar_widget),
3281 mw->menu.windows[0].x += ret.x;
3282 mw->menu.windows[0].y += ret.y;
3285 XQueryTree(XtDisplay(lw_menubar_widget), parent,
3286 &root, &parent, &waste, &num_waste);
3291 while (parent != root);
3293 XtGrabPointer((Widget) mw, False,
3294 (ButtonMotionMask | ButtonReleaseMask |
3295 ButtonPressMask), GrabModeAsync, GrabModeAsync,
3296 None, mw->menu.cursor_shape, t);
3297 mw->menu.pointer_grabbed = True;
3301 /* display the stupid menu already */
3302 void xlw_display_menu(Time t)
3304 XlwMenuWidget mw = (XlwMenuWidget) lw_menubar_widget;
3306 lw_menu_accelerate = True;
3310 /* Sync with the display. Makes it feel better on X terms. */
3311 XFlush(XtDisplay(mw));
3314 /* push a sub menu */
3315 void xlw_push_menu(widget_value * val)
3317 push_new_stack((XlwMenuWidget) lw_menubar_widget, val);
3320 /* pop a sub menu */
3321 int xlw_pop_menu(void)
3323 if (((XlwMenuWidget) lw_menubar_widget)->menu.new_depth > 0)
3324 ((XlwMenuWidget) lw_menubar_widget)->menu.new_depth--;
3330 void xlw_kill_menus(widget_value * val)
3332 XlwMenuWidget mw = (XlwMenuWidget) lw_menubar_widget;
3334 lw_menu_accelerate = False;
3336 mw->menu.new_depth = 1;
3339 if (mw->menu.pointer_grabbed) {
3340 XtUngrabPointer(lw_menubar_widget, CurrentTime);
3341 mw->menu.pointer_grabbed = False;
3344 lw_menu_active = False;
3345 XtCallCallbackList(lw_menubar_widget, mw->menu.select, (XtPointer) val);
3348 /* set the menu item */
3349 void xlw_set_item(widget_value * val)
3351 if (((XlwMenuWidget) lw_menubar_widget)->menu.new_depth > 0)
3352 ((XlwMenuWidget) lw_menubar_widget)->menu.new_depth--;
3353 push_new_stack((XlwMenuWidget) lw_menubar_widget, val);
3356 /* get either the current entry or a list of all entries in the current submenu */
3357 widget_value *xlw_get_entries(int allp)
3359 XlwMenuWidget mw = (XlwMenuWidget) lw_menubar_widget;
3361 if (mw->menu.new_depth >= 2)
3362 return mw->menu.new_stack[mw->menu.new_depth -
3365 return mw->menu.new_stack[0];
3366 } else if (mw->menu.new_depth >= 1)
3367 return mw->menu.new_stack[mw->menu.new_depth - 1];
3372 int xlw_menu_level(void)
3374 return ((XlwMenuWidget) lw_menubar_widget)->menu.new_depth;
3377 /* Special code to pop-up a menu */
3378 void xlw_pop_up_menu(XlwMenuWidget mw, XButtonPressedEvent * event)
3380 int x = event->x_root;
3381 int y = event->y_root;
3384 int borderwidth = mw->menu.shadow_thickness;
3385 Screen *screen = XtScreen(mw);
3387 mw->menu.menu_post_time = event->time;
3388 mw->menu.menu_bounce_time = 0;
3389 mw->menu.next_release_must_exit = True;
3390 mw->menu.last_selected_val = NULL;
3392 XtCallCallbackList((Widget) mw, mw->menu.open, NULL);
3396 w = mw->menu.windows[0].width;
3397 h = mw->menu.windows[0].height;
3402 if (x < borderwidth)
3405 if (x > WidthOfScreen(screen) - w - 2 * borderwidth)
3406 x = WidthOfScreen(screen) - w - 2 * borderwidth;
3408 if (y < borderwidth)
3411 if (y > HeightOfScreen(screen) - h - 2 * borderwidth)
3412 y = HeightOfScreen(screen) - h - 2 * borderwidth;
3414 mw->menu.popped_up = True;
3415 XtConfigureWidget(XtParent(mw), x, y, w, h,
3416 XtParent(mw)->core.border_width);
3417 XtPopup(XtParent(mw), XtGrabExclusive);
3418 display_menu(mw, 0, False, NULL, NULL, NULL, NULL, NULL);
3419 if (!mw->menu.pointer_grabbed) {
3420 XtGrabPointer((Widget) mw, False,
3421 (ButtonMotionMask | ButtonReleaseMask |
3422 ButtonPressMask), GrabModeAsync, GrabModeAsync,
3423 None, mw->menu.cursor_shape, event->time);
3424 mw->menu.pointer_grabbed = True;
3427 mw->menu.windows[0].x = x + borderwidth;
3428 mw->menu.windows[0].y = y + borderwidth;
3430 handle_motion_event(mw, (XMotionEvent *) event, True);
3436 * This is a horrible function which should not be needed.
3437 * use it to put the resize method back the way the XlwMenu
3438 * class initializer put it. Motif screws with this when
3439 * the XlwMenu class gets instantiated.
3441 void xlw_unmunge_class_resize(Widget w)
3443 if (w->core.widget_class->core_class.resize != XlwMenuResize)
3444 w->core.widget_class->core_class.resize = XlwMenuResize;