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 */
47 #include "lwlib-internal.h"
49 #include <sxe-utils.h>
51 #ifdef USE_DEBUG_MALLOC
56 /* simple, naive integer maximum */
58 #define max(a,b) ((a)>(b)?(a):(b))
62 xlwMenuTranslations[] = "<BtnDown>: start()\n\
63 <BtnMotion>: drag()\n\
67 extern Widget lw_menubar_widget;
69 #define offset(field) XtOffset(XlwMenuWidget, field)
70 static XtResource xlwMenuResources[] = {
72 /* There are three font list resources, so that we can accept either of
73 the resources *fontList: or *font:, and so that we can tell the
74 difference between them being specified, and being defaulted to a
75 font from the XtRString specified here. */
76 {XmNfontList, XmCFontList, XmRFontList, sizeof(XmFontList),
77 offset(menu.font_list), XtRImmediate, (XtPointer) 0}
79 {XtNfont, XtCFont, XmRFontList, sizeof(XmFontList),
80 offset(menu.font_list_2), XtRImmediate, (XtPointer) 0}
82 {XmNfontList, XmCFontList, XmRFontList, sizeof(XmFontList),
83 offset(menu.fallback_font_list),
84 /* We must use an iso8859-1 font here, or people without $LANG set lose.
85 It's fair to assume that those who do have $LANG set also have the
86 *fontList resource set, or at least know how to deal with this. */
88 (XtPointer) "-*-helvetica-bold-r-*-*-*-120-*-*-*-*-iso8859-1"}
91 {XtNfont, XtCFont, XtRFontStruct, sizeof(XFontStruct *),
92 offset(menu.font), XtRString, (XtPointer) "XtDefaultFont"}
95 /* #### Consider using the same method as for Motif; see the comment in
96 XlwMenuInitialize(). */
97 {XtNfontSet, XtCFontSet, XtRFontSet, sizeof(XFontSet),
98 offset(menu.font_set), XtRString, (XtPointer) "XtDefaultFontSet"}
102 {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
103 offset(menu.foreground), XtRString, (XtPointer) "XtDefaultForeground"}
105 {XtNbuttonForeground, XtCButtonForeground, XtRPixel, sizeof(Pixel),
106 offset(menu.button_foreground), XtRString,
107 (XtPointer) "XtDefaultForeground"}
109 {XtNhighlightForeground, XtCHighlightForeground, XtRPixel,
111 offset(menu.highlight_foreground), XtRString,
112 (XtPointer) "XtDefaultForeground"}
114 {XtNtitleForeground, XtCTitleForeground, XtRPixel, sizeof(Pixel),
115 offset(menu.title_foreground), XtRString,
116 (XtPointer) "XtDefaultForeground"}
118 {XtNmargin, XtCMargin, XtRDimension, sizeof(Dimension),
119 offset(menu.margin), XtRImmediate, (XtPointer) 2}
121 {XmNmarginWidth, XmCMarginWidth, XmRHorizontalDimension,
123 offset(menu.horizontal_margin), XtRImmediate, (XtPointer) 2}
125 {XmNmarginHeight, XmCMarginHeight, XmRVerticalDimension,
127 offset(menu.vertical_margin), XtRImmediate, (XtPointer) 1}
129 {XmNspacing, XmCSpacing, XmRHorizontalDimension, sizeof(Dimension),
130 offset(menu.column_spacing), XtRImmediate, (XtPointer) 4}
132 {XmNindicatorSize, XmCIndicatorSize, XtRDimension, sizeof(Dimension),
133 offset(menu.indicator_size), XtRImmediate, (XtPointer) 0}
136 {XmNshadowThickness, XmCShadowThickness, XmRHorizontalDimension,
137 sizeof(Dimension), offset(menu.shadow_thickness),
138 XtRImmediate, (XtPointer) 2}
141 {XmNshadowThickness, XmCShadowThickness, XtRDimension,
142 sizeof(Dimension), offset(menu.shadow_thickness),
143 XtRImmediate, (XtPointer) 2}
146 {XmNselectColor, XmCSelectColor, XtRPixel, sizeof(Pixel),
147 offset(menu.select_color), XtRImmediate, (XtPointer) - 1}
149 {XmNtopShadowColor, XmCTopShadowColor, XtRPixel, sizeof(Pixel),
150 offset(menu.top_shadow_color), XtRImmediate, (XtPointer) - 1}
152 {XmNbottomShadowColor, XmCBottomShadowColor, XtRPixel, sizeof(Pixel),
153 offset(menu.bottom_shadow_color), XtRImmediate, (XtPointer) - 1}
155 {XmNtopShadowPixmap, XmCTopShadowPixmap, XtRPixmap, sizeof(Pixmap),
156 offset(menu.top_shadow_pixmap), XtRImmediate, (XtPointer) None}
158 {XmNbottomShadowPixmap, XmCBottomShadowPixmap, XtRPixmap,
160 offset(menu.bottom_shadow_pixmap), XtRImmediate, (XtPointer) None}
163 {XtNopen, XtCCallback, XtRCallback, sizeof(XtPointer),
164 offset(menu.open), XtRCallback, (XtPointer) NULL}
166 {XtNselect, XtCCallback, XtRCallback, sizeof(XtPointer),
167 offset(menu.select), XtRCallback, (XtPointer) NULL}
169 {XtNmenu, XtCMenu, XtRPointer, sizeof(XtPointer),
170 offset(menu.contents), XtRImmediate, (XtPointer) NULL}
172 {XtNcursor, XtCCursor, XtRCursor, sizeof(Cursor),
173 offset(menu.cursor_shape), XtRString, (XtPointer) "right_ptr"}
175 {XtNhorizontal, XtCHorizontal, XtRInt, sizeof(int),
176 offset(menu.horizontal), XtRImmediate, (XtPointer) True},
177 {XtNuseBackingStore, XtCUseBackingStore, XtRBoolean, sizeof(Boolean),
178 offset(menu.use_backing_store), XtRImmediate, (XtPointer) False}
180 {XtNbounceDown, XtCBounceDown, XtRBoolean, sizeof(Boolean),
181 offset(menu.bounce_down), XtRImmediate, (XtPointer) True}
183 {XtNresourceLabels, XtCResourceLabels, XtRBoolean, sizeof(Boolean),
184 offset(menu.lookup_labels), XtRImmediate, (XtPointer) False}
189 static Boolean XlwMenuSetValues(Widget current, Widget request, Widget new,
190 ArgList args, Cardinal * num_args);
191 static void XlwMenuRealize(Widget w, Mask * valueMask,
192 XSetWindowAttributes * attributes);
193 static void XlwMenuRedisplay(Widget w, XEvent * ev, Region region);
194 static void XlwMenuResize(Widget w);
195 static void XlwMenuInitialize(Widget request, Widget new, ArgList args,
196 Cardinal * num_args);
197 static void XlwMenuDestroy(Widget w);
198 static void XlwMenuClassInitialize(void);
199 static void Start(Widget w, XEvent * ev, String * params,
200 Cardinal * num_params);
201 static void Drag(Widget w, XEvent * ev, String * params, Cardinal * num_params);
202 static void Select(Widget w, XEvent * ev, String * params,
203 Cardinal * num_params);
206 static XFontStruct *default_font_of_font_list(XmFontList);
209 static XtActionsRec xlwMenuActionsList[] = {
215 #define SuperClass ((CoreWidgetClass)&coreClassRec)
217 XlwMenuClassRec xlwMenuClassRec = {
218 { /* CoreClass fields initialization */
219 (WidgetClass) SuperClass, /* superclass */
220 "XlwMenu", /* class_name */
221 sizeof(XlwMenuRec), /* size */
222 XlwMenuClassInitialize, /* class_initialize */
223 NULL, /* class_part_initialize */
224 FALSE, /* class_inited */
225 XlwMenuInitialize, /* initialize */
226 NULL, /* initialize_hook */
227 XlwMenuRealize, /* realize */
228 xlwMenuActionsList, /* actions */
229 XtNumber(xlwMenuActionsList), /* num_actions */
230 xlwMenuResources, /* resources */
231 XtNumber(xlwMenuResources), /* resource_count */
232 NULLQUARK, /* xrm_class */
233 TRUE, /* compress_motion */
234 XtExposeCompressMaximal, /* compress_exposure */
235 TRUE, /* compress_enterleave */
236 FALSE, /* visible_interest */
237 XlwMenuDestroy, /* destroy */
238 XlwMenuResize, /* resize */
239 XlwMenuRedisplay, /* expose */
240 XlwMenuSetValues, /* set_values */
241 NULL, /* set_values_hook */
242 XtInheritSetValuesAlmost, /* set_values_almost */
243 NULL, /* get_values_hook */
244 NULL, /* #### - should this be set for grabs? accept_focus */
245 XtVersion, /* version */
246 NULL, /* callback_private */
247 xlwMenuTranslations, /* tm_table */
248 XtInheritQueryGeometry, /* query_geometry */
249 XtInheritDisplayAccelerator, /* display_accelerator */
252 , /* XlwMenuClass fields initialization */
259 WidgetClass xlwMenuWidgetClass = (WidgetClass) & xlwMenuClassRec;
261 extern int lw_menu_accelerate;
264 #if 0 /* Apparently not used anywhere */
266 static char *safe_strdup(char *s)
271 result = (char *)malloc(strlen(s) + 1);
280 /* Replacement for XAllocColor() that tries to return the nearest
281 available color if the colormap is full. From FSF Emacs. */
284 allocate_nearest_color(Display * display, Colormap screen_colormap,
287 int status = XAllocColor(display, screen_colormap, color_def);
292 /* If we got to this point, the colormap is full, so we're
293 going to try to get the next closest color.
294 The algorithm used is a least-squares matching, which is
295 what X uses for closest color matching with StaticColor visuals. */
298 unsigned long nearest_delta = ULONG_MAX;
300 int no_cells = XDisplayCells(display, XDefaultScreen(display));
301 /* Don't use alloca here because lwlib doesn't have the
302 necessary configuration information that src does. */
303 XColor *cells = (XColor *) malloc(sizeof(XColor) * no_cells);
305 for (x = 0; x < no_cells; x++)
308 XQueryColors(display, screen_colormap, cells, no_cells);
310 for (nearest = 0, x = 0; x < no_cells; x++) {
311 long dred = (color_def->red >> 8) - (cells[x].red >> 8);
313 (color_def->green >> 8) - (cells[x].green >> 8);
315 (color_def->blue >> 8) - (cells[x].blue >> 8);
316 unsigned long delta =
317 dred * dred + dgreen * dgreen + dblue * dblue;
319 if (delta < nearest_delta) {
321 nearest_delta = delta;
324 color_def->red = cells[nearest].red;
325 color_def->green = cells[nearest].green;
326 color_def->blue = cells[nearest].blue;
328 return XAllocColor(display, screen_colormap, color_def);
332 static void push_new_stack(XlwMenuWidget mw, widget_value * val)
334 if (!mw->menu.new_stack) {
335 mw->menu.new_stack_length = 10;
337 (widget_value **) XtCalloc(mw->menu.new_stack_length,
338 sizeof(widget_value *));
339 } else if (mw->menu.new_depth == mw->menu.new_stack_length) {
340 mw->menu.new_stack_length *= 2;
342 (widget_value **) XtRealloc((char *)mw->menu.new_stack,
343 mw->menu.new_stack_length *
344 sizeof(widget_value *));
346 mw->menu.new_stack[mw->menu.new_depth++] = val;
349 static void pop_new_stack_if_no_contents(XlwMenuWidget mw)
351 if (mw->menu.new_depth &&
352 !mw->menu.new_stack[mw->menu.new_depth - 1]->contents)
353 mw->menu.new_depth -= 1;
356 static void make_old_stack_space(XlwMenuWidget mw, int n)
358 if (!mw->menu.old_stack) {
359 mw->menu.old_stack_length = max(10, n);
361 (widget_value **) XtCalloc(mw->menu.old_stack_length,
362 sizeof(widget_value *));
363 } else if (mw->menu.old_stack_length < n) {
364 while (mw->menu.old_stack_length < n)
365 mw->menu.old_stack_length *= 2;
368 (widget_value **) XtRealloc((char *)mw->menu.old_stack,
369 mw->menu.old_stack_length *
370 sizeof(widget_value *));
375 close_to_reference_time(Widget w, Time reference_time, XEvent * ev)
379 (ev->xbutton.time - reference_time
380 < (Time) XtGetMultiClickTime(XtDisplay(w)));
384 static int string_width(XlwMenuWidget mw,
393 Dimension width, height;
394 XmStringExtent(mw->menu.font_list, s, &width, &height);
399 XmbTextExtents(mw->menu.font_set, s, strlen(s), &ri, &rl);
404 XTextExtents(mw->menu.font, s, strlen(s), &drop, &drop, &drop, &xcs);
406 # endif /* USE_XFONTSET */
410 static char massaged_resource_char[256];
412 static void initialize_massaged_resource_char(void)
415 for (j = 0; j < (int)sizeof(massaged_resource_char); j++) {
416 if ((j >= 'a' && j <= 'z') ||
417 (j >= 'A' && j <= 'Z') ||
418 (j >= '0' && j <= '9') || (j == '_') || (j >= 0xa0))
419 massaged_resource_char[j] = (char)j;
421 massaged_resource_char['_'] = '_';
422 massaged_resource_char['+'] = 'P'; /* Convert C++ to cPP */
423 massaged_resource_char['.'] = '_'; /* Convert Buffers... to buffers___ */
426 static int string_width_u(XlwMenuWidget mw,
435 Dimension width, height;
440 # else /* ! USE_XFONTSET */
452 if (!XmStringGetLtoR(string, XmFONTLIST_DEFAULT_TAG, &chars))
457 charslength = strlen(chars);
458 newchars = (char *)alloca(charslength + 1);
460 for (i = j = 0; chars[i] && (j < charslength); i++)
461 if (chars[i] == '%' && chars[i + 1] == '_')
464 newchars[j++] = chars[i];
468 newstring = XmStringLtoRCreate(newchars, XmFONTLIST_DEFAULT_TAG);
469 XmStringExtent(mw->menu.font_list, newstring, &width, &height);
470 XmStringFree(newstring);
475 XmbTextExtents(mw->menu.font_set, newchars, j, &ri, &rl);
477 # else /* ! USE_XFONTSET */
478 XTextExtents(mw->menu.font, newchars, j, &drop, &drop, &drop, &xcs);
480 # endif /* USE_XFONTSET */
484 static void massage_resource_name(const char *in, char *out)
486 /* Turn a random string into something suitable for using as a resource.
489 "Kill Buffer" -> "killBuffer"
490 "Find File..." -> "findFile___"
491 "Search and Replace..." -> "searchAndReplace___"
492 "C++ Mode Commands" -> "cppModeCommands"
494 Valid characters in a resource NAME component are: a-zA-Z0-9_
497 #ifdef PRINT_XLWMENU_RESOURCE_CONVERSIONS
498 /* Compile with -DPRINT_XLWMENU_RESOURCE_CONVERSIONS to generate a
499 translation file for menu localizations. */
500 char *save_in = in, *save_out = out;
503 Boolean firstp = True;
505 if (*in == '%' && *(in + 1) == '_')
510 if (*in == '%' && *(in + 1) == '%')
512 ch = massaged_resource_char[(unsigned char)*in++];
514 int int_ch = (int)(unsigned char)ch;
516 firstp ? tolower(int_ch) : toupper(int_ch);
519 massaged_resource_char[(unsigned char)
523 if (!*(in - 1)) /* Overshot the NULL byte? */
530 #ifdef PRINT_XLWMENU_RESOURCE_CONVERSIONS
531 printf("! Emacs*XlwMenu.%s.labelString:\t%s\n", save_out, save_in);
532 printf("Emacs*XlwMenu.%s.labelString:\n", save_out);
536 static XtResource nameResource[] = {
537 {"labelString", "LabelString", XtRString, sizeof(String),
541 /* This function searches STRING for parameter inserts of the form:
543 padding is either space (' ') or dash ('-') meaning
544 padding to the left or right of the inserted parameter.
545 In essence, all %1 strings are replaced by VALUE in the return value.
546 The caller is expected to free the return value using XtFree().
547 %% means insert one % (like printf).
548 %1 means insert VALUE.
549 %-1 means insert VALUE followed by one space. The latter is
550 not inserted if VALUE is a zero length string.
552 static char *parameterize_string(const char *string, const char *value)
556 unsigned int done = 0;
561 result = XtMalloc(1);
569 for (ntimes = 1, percent = string;
570 (percent = strchr(percent, '%')); ntimes++)
573 res_left = (ntimes * strlen(value)) + strlen(string) + 3;
574 result = XtMalloc(res_left+1);
575 result[res_left] = result[0] = '\0';
577 while ((percent = strchr(string, '%'))) {
578 unsigned int left_pad;
579 unsigned int right_pad;
581 off_t offset = percent-string;
583 if (percent[1] == '%') { /* it's a real % */
584 assert( offset >=0 &&
585 res_left >= (size_t)(1 + offset));
587 strncat(result, string, 1 + offset);
588 res_left -= 1 + offset;
589 string = &percent[2]; /* after the second '%' */
590 continue; /* with the while() loop */
596 for (p = &percent[1]; /* test *p inside the loop */ ; p++) {
597 if (*p == ' ') { /* left pad */
599 } else if (*p == '-') { /* right pad */
601 } else if (*p == '1') { /* param and terminator */
602 assert( offset >= 0 &&
603 res_left >= (size_t)offset );
605 strncat(result, string, offset);
606 if (value[0] != '\0') {
608 for (i = 0; i < left_pad; i++)
609 strncat(result, " ", res_left --);
610 strncat(result, value, res_left);
611 res_left -= strlen(value);
612 for (i = 0; i < right_pad; i++)
613 strncat(result, " ", res_left--);
615 string = &p[1]; /* after the '1' */
616 done++; /* no need to do old way */
617 break; /* out of for() loop */
618 } else { /* bogus, copy the format as is */
619 /* out of for() loop */
620 off_t remain = 1 + p - string;
621 assert( remain >=0 &&
622 res_left >= (size_t)remain );
623 strncat(result, string, remain);
625 string = (*p ? &p[1] : p);
626 offset = percent - string;
632 /* Copy the tail of the string */
633 strncat(result, string, res_left);
634 res_left -= strlen(string);
636 /* If we have not processed a % string, and we have a value, tail it. */
637 if (!done && value[0] != '\0') {
638 strncat(result, " ", res_left--);
639 strncat(result, value, res_left);
647 static XmString resource_widget_value(XlwMenuWidget mw, widget_value * val)
649 if (!val->toolkit_data) {
650 char *resourced_name = NULL;
651 char *converted_name, *str;
652 XmString complete_name;
653 char massaged_name[1024];
655 if (mw->menu.lookup_labels) {
656 /* Convert value style name into resource style name.
657 eg: "Free Willy" becomes "freeWilly" */
658 massage_resource_name(val->name, massaged_name);
660 /* If we have a value (parameter) see if we can find a "Named"
663 char named_name[1024];
664 int sz = snprintf(named_name, sizeof(named_name),
665 "%sNamed", massaged_name);
666 assert(sz >= 0 && sz < sizeof(named_name));
667 XtGetSubresources((Widget) mw,
668 (XtPointer) & resourced_name,
669 named_name, named_name,
670 nameResource, 1, NULL, 0);
673 /* If nothing yet, try to load from the massaged name. */
674 if (!resourced_name) {
675 XtGetSubresources((Widget) mw,
676 (XtPointer) & resourced_name,
677 massaged_name, massaged_name,
678 nameResource, 1, NULL, 0);
682 /* if (mw->menu.lookup_labels) */
683 /* Still nothing yet, use the name as the value. */
685 resourced_name = val->name;
687 /* Parameterize the string. */
689 parameterize_string(resourced_name, val->value);
691 /* nuke newline characters to prevent menubar screwups */
692 for (str = converted_name; *str; str++) {
697 /* Improve OSF's bottom line. */
698 #if (XmVersion >= 1002)
699 complete_name = XmStringCreateLocalized(converted_name);
701 complete_name = XmStringCreateLtoR(converted_name,
702 XmSTRING_DEFAULT_CHARSET);
704 XtFree(converted_name);
706 val->toolkit_data = complete_name;
707 val->free_toolkit_data = True;
709 return (XmString) val->toolkit_data;
714 /* These two routines should be a separate file..djw */
715 static char *xlw_create_localized_string(Widget w,
717 char **args, unsigned int nargs)
726 (XtPointer) & string,
727 name, name, nameResource, 1, NULL, 0);
732 return parameterize_string(string, arg);
736 xlw_create_localized_xmstring(Widget w,
737 char *name, char **args, unsigned int nargs)
739 char *string = xlw_create_localized_string(w, name, args, nargs);
741 XmStringCreateLtoR(string, XmSTRING_DEFAULT_CHARSET);
749 static char *resource_widget_value(XlwMenuWidget mw, widget_value * val)
751 if (!val->toolkit_data) {
752 char *resourced_name = NULL;
754 char massaged_name[1024];
756 if (mw->menu.lookup_labels) {
757 massage_resource_name(val->name, massaged_name);
759 XtGetSubresources((Widget) mw,
760 (XtPointer) & resourced_name,
761 massaged_name, massaged_name,
762 nameResource, 1, NULL, 0);
765 resourced_name = val->name;
767 complete_name = parameterize_string(resourced_name, val->value);
769 val->toolkit_data = complete_name;
770 /* nuke newline characters to prevent menubar screwups */
771 for (; *complete_name; complete_name++) {
772 if (complete_name[0] == '\n')
773 complete_name[0] = ' ';
775 val->free_toolkit_data = True;
777 return (char *)val->toolkit_data;
782 /* Code for drawing strings. */
783 static void string_draw(XlwMenuWidget mw, Window window, int x, int y, GC gc,
792 XmStringDraw(XtDisplay(mw), window, mw->menu.font_list, string, gc, x, y, 1000, /* ???? width */
793 XmALIGNMENT_BEGINNING, 0, /* ???? layout_direction */
797 XmbDrawString(XtDisplay(mw), window, mw->menu.font_set, gc,
798 x, y + mw->menu.font_ascent, string, strlen(string));
800 XDrawString(XtDisplay(mw), window, gc,
801 x, y + mw->menu.font_ascent, string, strlen(string));
802 # endif /* USE_XFONTSET */
808 string_draw_range(XlwMenuWidget mw,
810 int x, int y, GC gc, char *string, int start, int end)
813 Dimension width, height;
821 newstring = XmStringLtoRCreate(&string[start], XmFONTLIST_DEFAULT_TAG);
822 XmStringDraw(XtDisplay(mw), window, mw->menu.font_list, newstring, gc, x, y, 1000, /* ???? width */
823 XmALIGNMENT_BEGINNING, 0, /* ???? layout_direction */
825 XmStringExtent(mw->menu.font_list, newstring, &width, &height);
826 XmStringFree(newstring);
835 XmbDrawString(XtDisplay(mw), window, mw->menu.font_set, gc,
836 x, y + mw->menu.font_ascent, &string[start], end - start);
837 XmbTextExtents(mw->menu.font_set, &string[start], end - start, &ri,
846 XDrawString(XtDisplay(mw), window, gc,
847 x, y + mw->menu.font_ascent, &string[start], end - start);
848 XTextExtents(mw->menu.font, &string[start], end - start,
849 &drop, &drop, &drop, &xcs);
855 static void string_draw_u(XlwMenuWidget mw, Window window, int x, int y, GC gc,
868 if (!XmStringGetLtoR(string, XmFONTLIST_DEFAULT_TAG, &chars))
873 for (i = 0; chars[i]; ++i) {
874 if (chars[i] == '%' && chars[i + 1] == '_') {
877 x += string_draw_range(mw, window, x, y, gc, chars, s,
879 w = string_draw_range(mw, window, x, y, gc, chars,
882 /* underline next character */
883 XDrawLine(XtDisplay(mw), window, gc, x - 1,
884 y + mw->menu.font_ascent + 1,
885 x + w - 1, y + mw->menu.font_ascent + 1);
891 x += string_draw_range(mw, window, x, y, gc, chars, s, i);
898 binding_draw(XlwMenuWidget mw, Window w, int x, int y, GC gc, char *value)
901 XmString xm_value = XmStringCreateLtoR(value, XmSTRING_DEFAULT_CHARSET);
902 string_draw(mw, w, x, y, gc, xm_value);
903 XmStringFree(xm_value);
905 string_draw(mw, w, x, y, gc, value);
909 /* Low level code for drawing 3-D edges. */
911 shadow_rectangle_draw(Display * dpy,
917 unsigned int height, unsigned int thickness)
926 points[1].x = x + width;
928 points[2].x = x + width - thickness;
929 points[2].y = y + thickness;
931 points[3].y = y + thickness;
932 XFillPolygon(dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
934 points[0].y = y + thickness;
936 points[1].y = y + height;
937 points[2].x = x + thickness;
938 points[2].y = y + height - thickness;
939 points[3].x = x + thickness;
940 points[3].y = y + thickness;
941 XFillPolygon(dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
942 points[0].x = x + width;
944 points[1].x = x + width - thickness;
945 points[1].y = y + thickness;
946 points[2].x = x + width - thickness;
947 points[2].y = y + height - thickness;
948 points[3].x = x + width;
949 points[3].y = y + height - thickness;
950 XFillPolygon(dpy, window, bottom_gc, points, 4, Convex,
953 points[0].y = y + height;
954 points[1].x = x + width;
955 points[1].y = y + height;
956 points[2].x = x + width;
957 points[2].y = y + height - thickness;
958 points[3].x = x + thickness;
959 points[3].y = y + height - thickness;
960 XFillPolygon(dpy, window, bottom_gc, points, 4, Convex,
964 typedef enum e_shadow_type {
965 /* these are Motif compliant */
971 SHADOW_ETCHED_OUT_DASH,
972 SHADOW_ETCHED_IN_DASH,
975 SHADOW_SINGLE_DASHED_LINE,
976 SHADOW_DOUBLE_DASHED_LINE,
978 /* these are all non-Motif */
979 SHADOW_DOUBLE_ETCHED_OUT,
980 SHADOW_DOUBLE_ETCHED_IN,
981 SHADOW_DOUBLE_ETCHED_OUT_DASH,
982 SHADOW_DOUBLE_ETCHED_IN_DASH
986 shadow_draw(XlwMenuWidget mw,
989 unsigned int width, unsigned int height, shadow_type type)
991 Display *dpy = XtDisplay(mw);
994 int thickness = mw->menu.shadow_thickness;
998 Boolean etched = False;
1000 switch ((unsigned int)type) {
1001 case SHADOW_BACKGROUND:
1002 top_gc = bottom_gc = mw->menu.background_gc;
1004 case SHADOW_ETCHED_IN:
1005 top_gc = mw->menu.shadow_bottom_gc;
1006 bottom_gc = mw->menu.shadow_top_gc;
1009 case SHADOW_ETCHED_OUT:
1010 top_gc = mw->menu.shadow_top_gc;
1011 bottom_gc = mw->menu.shadow_bottom_gc;
1015 top_gc = mw->menu.shadow_bottom_gc;
1016 bottom_gc = mw->menu.shadow_top_gc;
1020 top_gc = mw->menu.shadow_top_gc;
1021 bottom_gc = mw->menu.shadow_bottom_gc;
1026 unsigned int half = thickness / 2;
1027 shadow_rectangle_draw(dpy,
1032 width - half, height - half,
1034 shadow_rectangle_draw(dpy,
1039 width - half, height - half, half);
1041 shadow_rectangle_draw(dpy,
1045 x, y, width, height, thickness);
1050 arrow_decoration_draw(XlwMenuWidget mw,
1052 int x, int y, unsigned int width, Boolean raised)
1054 Display *dpy = XtDisplay(mw);
1058 int thickness = mw->menu.shadow_thickness;
1061 int length = (int)((double)width * 0.87);
1062 int thick_med = (int)((double)thickness * 1.73);
1065 half_width = width / 2 + 1;
1067 half_width = width / 2;
1069 select_gc = mw->menu.background_gc;
1072 top_gc = mw->menu.shadow_bottom_gc;
1073 bottom_gc = mw->menu.shadow_top_gc;
1075 top_gc = mw->menu.shadow_top_gc;
1076 bottom_gc = mw->menu.shadow_bottom_gc;
1079 /* Fill internal area. We do this first so that the borders have a
1081 points[0].x = x + thickness;
1082 points[0].y = y + thickness;
1083 points[1].x = x + length - thickness;
1084 points[1].y = y + half_width;
1085 points[2].x = x + length - thickness;
1086 points[2].y = y + half_width + thickness;
1087 points[3].x = x + thickness;
1088 points[3].y = y + width - thickness;
1091 window, select_gc, points, 4, Convex, CoordModeOrigin);
1096 points[1].x = x + thickness;
1097 points[1].y = y + thick_med;
1098 points[2].x = x + thickness;
1099 points[2].y = y + width - thick_med;
1101 points[3].y = y + width;
1103 XFillPolygon(dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
1107 points[0].y = y + width;
1108 points[1].x = x + length;
1109 points[1].y = y + half_width;
1110 points[2].x = x + length - (thickness + thickness);
1111 points[2].y = y + half_width;
1112 points[3].x = x + thickness;
1113 points[3].y = y + width - thick_med;
1115 XFillPolygon(dpy, window, bottom_gc, points, 4, Convex,
1121 points[1].x = x + length;
1122 points[1].y = y + half_width;
1123 points[2].x = x + length - (thickness + thickness);
1124 points[2].y = y + half_width;
1125 points[3].x = x + thickness;
1126 points[3].y = y + thick_med;
1128 XFillPolygon(dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
1132 toggle_decoration_draw(XlwMenuWidget mw,
1134 int x, int y, unsigned int width, Boolean set)
1136 Display *dpy = XtDisplay(mw);
1137 int thickness = mw->menu.shadow_thickness;
1139 GC select_gc = mw->menu.select_gc;
1146 /* Fill internal area. */
1153 width - (2 * thickness),
1154 width - (2 * thickness));
1156 shadow_draw(mw, window, x, y, width, width, type);
1160 radio_decoration_draw(XlwMenuWidget mw,
1162 int x, int y, unsigned int width, Boolean enabled)
1164 Display *dpy = XtDisplay(mw);
1167 GC select_gc = mw->menu.select_gc;
1168 int thickness = mw->menu.shadow_thickness;
1178 half_width = width / 2;
1181 top_gc = mw->menu.shadow_bottom_gc;
1182 bottom_gc = mw->menu.shadow_top_gc;
1184 top_gc = mw->menu.shadow_top_gc;
1185 bottom_gc = mw->menu.shadow_bottom_gc;
1189 /* Draw the bottom first, just in case the regions overlap.
1190 The top should cast the longer shadow. */
1191 points[0].x = x; /* left corner */
1192 points[0].y = y + half_width;
1193 points[1].x = x + half_width; /* bottom corner */
1194 points[1].y = y + width;
1195 points[2].x = x + half_width; /* bottom inside corner */
1196 points[2].y = y + width - thickness;
1197 points[3].x = x + thickness; /* left inside corner */
1198 points[3].y = y + half_width;
1200 XFillPolygon(dpy, window, bottom_gc, points, 4, Convex,
1203 points[0].x = x + half_width; /* bottom corner */
1204 points[0].y = y + width;
1205 points[1].x = x + width; /* right corner */
1206 points[1].y = y + half_width;
1207 points[2].x = x + width - thickness; /* right inside corner */
1208 points[2].y = y + half_width;
1209 points[3].x = x + half_width; /* bottom inside corner */
1210 points[3].y = y + width - thickness;
1212 XFillPolygon(dpy, window, bottom_gc, points, 4, Convex,
1215 points[0].x = x; /* left corner */
1216 points[0].y = y + half_width;
1217 points[1].x = x + half_width; /* top corner */
1219 points[2].x = x + half_width; /* top inside corner */
1220 points[2].y = y + thickness;
1221 points[3].x = x + thickness; /* left inside corner */
1222 points[3].y = y + half_width;
1224 XFillPolygon(dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
1226 points[0].x = x + half_width; /* top corner */
1228 points[1].x = x + width; /* right corner */
1229 points[1].y = y + half_width;
1230 points[2].x = x + width - thickness; /* right inside corner */
1231 points[2].y = y + half_width;
1232 points[3].x = x + half_width; /* top inside corner */
1233 points[3].y = y + thickness;
1235 XFillPolygon(dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
1237 /* Draw the bottom first, just in case the regions overlap.
1238 The top should cast the longer shadow. */
1240 points[npoints].x = x; /* left corner */
1241 points[npoints++].y = y + half_width;
1242 points[npoints].x = x + half_width; /* bottom corner */
1243 points[npoints++].y = y + width;
1244 points[npoints].x = x + width; /* right corner */
1245 points[npoints++].y = y + half_width;
1246 points[npoints].x = x + width - thickness; /* right inside corner */
1247 points[npoints++].y = y + half_width;
1248 points[npoints].x = x + half_width; /* bottom inside corner */
1249 points[npoints++].y = y + width - thickness;
1250 points[npoints].x = x + thickness; /* left inside corner */
1251 points[npoints++].y = y + half_width;
1253 XFillPolygon(dpy, window, bottom_gc,
1254 points, npoints, Nonconvex, CoordModeOrigin);
1258 points[npoints].x = x; /* left corner */
1259 points[npoints++].y = y + half_width;
1260 points[npoints].x = x + half_width; /* top corner */
1261 points[npoints++].y = y;
1262 points[npoints].x = x + width; /* right corner */
1263 points[npoints++].y = y + half_width;
1264 points[npoints].x = x + width - thickness; /* right inside corner */
1265 points[npoints++].y = y + half_width;
1266 points[npoints].x = x + half_width; /* top inside corner */
1267 points[npoints++].y = y + thickness;
1268 points[npoints].x = x + thickness; /* left inside corner */
1269 points[npoints++].y = y + half_width;
1271 XFillPolygon(dpy, window, top_gc, points, npoints, Nonconvex,
1275 /* Fill internal area. */
1277 points[0].x = x + thickness;
1278 points[0].y = y + half_width;
1279 points[1].x = x + half_width;
1280 points[1].y = y + thickness;
1281 points[2].x = x + width - thickness;
1282 points[2].y = y + half_width;
1283 points[3].x = x + half_width;
1284 points[3].y = y + width - thickness;
1287 select_gc, points, 4, Convex, CoordModeOrigin);
1292 separator_decoration_draw(XlwMenuWidget mw,
1296 Boolean vertical, shadow_type type)
1298 Display *dpy = XtDisplay(mw);
1301 unsigned int offset = 0;
1302 unsigned int num_separators = 1;
1303 unsigned int top_line_thickness = 0;
1304 unsigned int bottom_line_thickness = 0;
1305 Boolean dashed = False;
1307 switch ((unsigned int)type) {
1308 case SHADOW_NO_LINE: /* nothing to do */
1310 case SHADOW_DOUBLE_LINE:
1312 case SHADOW_SINGLE_LINE:
1313 top_gc = bottom_gc = mw->menu.foreground_gc;
1314 top_line_thickness = 1;
1316 case SHADOW_DOUBLE_DASHED_LINE:
1318 case SHADOW_SINGLE_DASHED_LINE:
1319 top_gc = bottom_gc = mw->menu.foreground_gc;
1320 top_line_thickness = 1;
1323 case SHADOW_DOUBLE_ETCHED_OUT_DASH:
1325 case SHADOW_ETCHED_OUT_DASH:
1326 top_gc = mw->menu.shadow_top_gc;
1327 bottom_gc = mw->menu.shadow_bottom_gc;
1328 top_line_thickness = mw->menu.shadow_thickness / 2;
1329 bottom_line_thickness =
1330 mw->menu.shadow_thickness - top_line_thickness;
1333 case SHADOW_DOUBLE_ETCHED_IN_DASH:
1335 case SHADOW_ETCHED_IN_DASH:
1336 top_gc = mw->menu.shadow_bottom_gc;
1337 bottom_gc = mw->menu.shadow_top_gc;
1338 top_line_thickness = mw->menu.shadow_thickness / 2;
1339 bottom_line_thickness =
1340 mw->menu.shadow_thickness - top_line_thickness;
1343 case SHADOW_DOUBLE_ETCHED_OUT:
1345 case SHADOW_ETCHED_OUT:
1346 top_gc = mw->menu.shadow_top_gc;
1347 bottom_gc = mw->menu.shadow_bottom_gc;
1348 top_line_thickness = mw->menu.shadow_thickness / 2;
1349 bottom_line_thickness =
1350 mw->menu.shadow_thickness - top_line_thickness;
1352 case SHADOW_DOUBLE_ETCHED_IN:
1354 case SHADOW_ETCHED_IN:
1356 top_gc = mw->menu.shadow_bottom_gc;
1357 bottom_gc = mw->menu.shadow_top_gc;
1358 top_line_thickness = mw->menu.shadow_thickness / 2;
1359 bottom_line_thickness =
1360 mw->menu.shadow_thickness - top_line_thickness;
1366 values.line_style = LineOnOffDash;
1367 if (top_line_thickness > 0)
1368 XChangeGC(dpy, top_gc, GCLineStyle, &values);
1369 if (bottom_line_thickness > 0 && bottom_gc != top_gc)
1370 XChangeGC(dpy, bottom_gc, GCLineStyle, &values);
1373 while (num_separators--) {
1375 for (i = 0; i < top_line_thickness; i++)
1376 XDrawLine(dpy, window, top_gc, x, y + i, x + width,
1379 for (i = 0; i < bottom_line_thickness; i++)
1380 XDrawLine(dpy, window, bottom_gc,
1381 x, y + top_line_thickness + offset + i,
1383 y + top_line_thickness + offset + i);
1384 y += (top_line_thickness + offset + bottom_line_thickness + 1);
1389 values.line_style = LineSolid;
1390 if (top_line_thickness > 0)
1391 XChangeGC(dpy, top_gc, GCLineStyle, &values);
1392 if (bottom_line_thickness > 0 && bottom_gc != top_gc)
1393 XChangeGC(dpy, bottom_gc, GCLineStyle, &values);
1397 #define SLOPPY_TYPES 0 /* 0=off, 1=error check, 2=easy to please */
1399 #if SLOPPY_TYPES < 2
1401 static char *wv_types[] = {
1413 static void print_widget_value(widget_value * wv, int just_one, int depth)
1417 for (i = 0; i < depth; i++)
1421 printf("%s(null widget value pointer)\n", d);
1424 printf("%stype: %s\n", d, wv_types[wv->type]);
1426 printf("%sname: %s\n", d, (wv->name ? wv->name : "(null)"));
1429 printf("%sname: %s\n", d, wv->name);
1432 printf("%svalue: %s\n", d, wv->value);
1434 printf("%skey: %s\n", d, wv->key);
1435 printf("%senabled: %d\n", d, wv->enabled);
1437 printf("\n%scontents: \n", d);
1438 print_widget_value(wv->contents, 0, depth + 5);
1440 if (!just_one && wv->next) {
1442 print_widget_value(wv->next, 0, depth);
1445 #endif /* SLOPPY_TYPES < 2 */
1447 static Boolean all_dashes_p(char *s)
1450 if (!s || s[0] == '\0')
1452 for (p = s; *p == '-'; p++) ;
1454 if (*p == '!' || *p == '\0')
1458 #endif /* SLOPPY_TYPES */
1460 static widget_value_type menu_item_type(widget_value * val)
1462 if (val->type != UNSPECIFIED_TYPE)
1465 else if (all_dashes_p(val->name))
1466 return SEPARATOR_TYPE;
1467 else if (val->name && val->name[0] == '\0') /* push right */
1468 return PUSHRIGHT_TYPE;
1469 else if (val->contents) /* cascade */
1470 return CASCADE_TYPE;
1471 else if (val->call_data) /* push button */
1478 return UNSPECIFIED_TYPE; /* Not reached */
1483 label_button_size(XlwMenuWidget mw,
1486 unsigned int *toggle_width,
1487 unsigned int *label_width,
1488 unsigned int *bindings_width, unsigned int *height)
1490 *height = (mw->menu.font_ascent + mw->menu.font_descent +
1491 2 * mw->menu.vertical_margin +
1492 2 * mw->menu.shadow_thickness);
1493 /* no left column decoration */
1494 *toggle_width = mw->menu.horizontal_margin + mw->menu.shadow_thickness;
1496 *label_width = string_width_u(mw, resource_widget_value(mw, val));
1498 mw->menu.horizontal_margin + mw->menu.shadow_thickness;
1502 label_button_draw(XlwMenuWidget mw,
1505 Boolean highlighted,
1509 unsigned int height,
1510 unsigned int label_offset, unsigned int binding_tab)
1512 int y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin;
1517 mw->menu.shadow_thickness + mw->menu.horizontal_margin;
1519 if (highlighted && (in_menubar || val->contents))
1520 gc = mw->menu.highlight_gc;
1521 else if (in_menubar || val->contents)
1522 gc = mw->menu.foreground_gc;
1524 gc = mw->menu.title_gc;
1526 /* Draw the label string. */
1529 x + label_offset, y + y_offset,
1530 gc, resource_widget_value(mw, val));
1534 push_button_size(XlwMenuWidget mw,
1537 unsigned int *toggle_width,
1538 unsigned int *label_width,
1539 unsigned int *bindings_width, unsigned int *height)
1542 label_button_size(mw, val, in_menubar,
1543 toggle_width, label_width, bindings_width, height);
1545 /* key bindings to display? */
1546 if (!in_menubar && val->key) {
1550 XmStringCreateLtoR(val->key, XmSTRING_DEFAULT_CHARSET);
1551 w = string_width(mw, key);
1554 char *key = val->key;
1555 w = string_width(mw, key);
1557 *bindings_width += w + mw->menu.column_spacing;
1562 push_button_draw(XlwMenuWidget mw,
1565 Boolean highlighted,
1569 unsigned int height,
1570 unsigned int label_offset, unsigned int binding_offset)
1572 int y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin;
1575 Boolean menu_pb = in_menubar && (menu_item_type(val) == BUTTON_TYPE);
1577 /* Draw the label string. */
1580 mw->menu.shadow_thickness + mw->menu.horizontal_margin;
1584 gc = mw->menu.highlight_gc;
1586 gc = mw->menu.inactive_gc;
1587 } else if (menu_pb) {
1589 gc = mw->menu.button_gc;
1591 gc = mw->menu.inactive_button_gc;
1594 gc = mw->menu.foreground_gc;
1596 gc = mw->menu.inactive_gc;
1601 x + label_offset, y + y_offset,
1602 gc, resource_widget_value(mw, val));
1604 /* Draw the keybindings */
1606 if (!binding_offset) {
1607 unsigned int s_width =
1608 string_width(mw, resource_widget_value(mw, val));
1610 label_offset + s_width + mw->menu.shadow_thickness;
1612 binding_draw(mw, window,
1613 x + binding_offset + mw->menu.column_spacing,
1614 y + y_offset, gc, val->key);
1617 /* Draw the shadow */
1624 selected ? SHADOW_ETCHED_OUT : SHADOW_ETCHED_IN);
1629 type = SHADOW_BACKGROUND;
1632 shadow_draw(mw, window, x, y, width, height, type);
1635 static unsigned int arrow_decoration_height(XlwMenuWidget mw)
1637 int result = (mw->menu.font_ascent + mw->menu.font_descent) / 2;
1639 result += 2 * mw->menu.shadow_thickness;
1641 if (result > (mw->menu.font_ascent + mw->menu.font_descent))
1642 result = mw->menu.font_ascent + mw->menu.font_descent;
1648 cascade_button_size(XlwMenuWidget mw,
1651 unsigned int *toggle_width,
1652 unsigned int *label_width,
1653 unsigned int *arrow_width, unsigned int *height)
1656 label_button_size(mw, val, in_menubar,
1657 toggle_width, label_width, arrow_width, height);
1658 /* we have a pull aside arrow */
1661 arrow_decoration_height(mw) + mw->menu.column_spacing;
1666 cascade_button_draw(XlwMenuWidget mw,
1669 Boolean highlighted,
1673 unsigned int height,
1674 unsigned int label_offset, unsigned int binding_offset)
1678 /* Draw the label string. */
1679 label_button_draw(mw, val, in_menubar, highlighted,
1680 window, x, y, width, height, label_offset,
1683 /* Draw the pull aside arrow */
1684 if (!in_menubar && val->contents) {
1686 unsigned int arrow_height = arrow_decoration_height(mw);
1689 mw->menu.shadow_thickness + mw->menu.vertical_margin +
1690 (mw->menu.font_ascent + mw->menu.font_descent -
1693 if (!binding_offset) {
1694 unsigned int s_width =
1695 string_width(mw, resource_widget_value(mw, val));
1698 label_offset = mw->menu.shadow_thickness +
1699 mw->menu.horizontal_margin;
1702 label_offset + s_width + mw->menu.shadow_thickness;
1705 arrow_decoration_draw(mw,
1707 x + binding_offset +
1708 mw->menu.column_spacing, y + y_offset,
1709 arrow_height, highlighted);
1712 /* Draw the shadow */
1716 type = SHADOW_BACKGROUND;
1718 shadow_draw(mw, window, x, y, width, height, type);
1721 static unsigned int toggle_decoration_height(XlwMenuWidget mw)
1724 if (mw->menu.indicator_size > 0)
1725 rv = mw->menu.indicator_size;
1727 rv = mw->menu.font_ascent;
1729 if (rv > (mw->menu.font_ascent + mw->menu.font_descent))
1730 rv = mw->menu.font_ascent + mw->menu.font_descent;
1732 /* radio button can't be smaller than its border or a filling
1733 error will occur. */
1734 if (rv < 2 * mw->menu.shadow_thickness)
1735 rv = 2 * mw->menu.shadow_thickness;
1741 toggle_button_size(XlwMenuWidget mw,
1744 unsigned int *toggle_width,
1745 unsigned int *label_width,
1746 unsigned int *bindings_width, unsigned int *height)
1749 push_button_size(mw, val, in_menubar,
1750 toggle_width, label_width, bindings_width, height);
1751 /* we have a toggle */
1752 *toggle_width += toggle_decoration_height(mw) + mw->menu.column_spacing;
1756 toggle_button_draw(XlwMenuWidget mw,
1759 Boolean highlighted,
1763 unsigned int height,
1764 unsigned int label_tab, unsigned int binding_tab)
1768 unsigned int t_height = toggle_decoration_height(mw);
1770 /* Draw a toggle. */
1771 x_offset = mw->menu.shadow_thickness + mw->menu.horizontal_margin;
1772 y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin;
1774 (mw->menu.font_ascent + mw->menu.font_descent - t_height) / 2;
1776 toggle_decoration_draw(mw, window, x + x_offset, y + y_offset,
1777 t_height, val->selected);
1779 /* Draw the pushbutton parts. */
1780 push_button_draw(mw, val, in_menubar, highlighted, window, x, y, width,
1781 height, label_tab, binding_tab);
1784 static unsigned int radio_decoration_height(XlwMenuWidget mw)
1786 return toggle_decoration_height(mw);
1790 radio_button_draw(XlwMenuWidget mw,
1793 Boolean highlighted,
1797 unsigned int height,
1798 unsigned int label_tab, unsigned int binding_tab)
1802 unsigned int r_height = radio_decoration_height(mw);
1804 /* Draw a toggle. */
1805 x_offset = mw->menu.shadow_thickness + mw->menu.horizontal_margin;
1806 y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin;
1808 (mw->menu.font_ascent + mw->menu.font_descent - r_height) / 2;
1810 radio_decoration_draw(mw, window, x + x_offset, y + y_offset, r_height,
1813 /* Draw the pushbutton parts. */
1814 push_button_draw(mw, val, in_menubar, highlighted, window, x, y, width,
1815 height, label_tab, binding_tab);
1818 static struct _shadow_names {
1821 } shadow_names[] = {
1824 "singleLine", SHADOW_SINGLE_LINE}, {
1825 "doubleLine", SHADOW_DOUBLE_LINE}, {
1826 "singleDashedLine", SHADOW_SINGLE_DASHED_LINE}, {
1827 "doubleDashedLine", SHADOW_DOUBLE_DASHED_LINE}, {
1828 "noLine", SHADOW_NO_LINE}, {
1829 "shadowEtchedIn", SHADOW_ETCHED_IN}, {
1830 "shadowEtchedOut", SHADOW_ETCHED_OUT}, {
1831 "shadowEtchedInDash", SHADOW_ETCHED_IN_DASH}, {
1832 "shadowEtchedOutDash", SHADOW_ETCHED_OUT_DASH},
1835 "shadowDoubleEtchedIn", SHADOW_DOUBLE_ETCHED_IN}, {
1836 "shadowDoubleEtchedOut", SHADOW_DOUBLE_ETCHED_OUT}, {
1837 "shadowDoubleEtchedInDash", SHADOW_DOUBLE_ETCHED_IN_DASH}, {
1838 "shadowDoubleEtchedOutDash", SHADOW_DOUBLE_ETCHED_OUT_DASH}
1841 static shadow_type separator_type(char *name)
1845 for (i = 0; i < (int)(XtNumber(shadow_names)); i++) {
1846 if (strcmp(name, shadow_names[i].name) == 0)
1847 return shadow_names[i].type;
1850 return SHADOW_BACKGROUND;
1854 separator_decoration_height(XlwMenuWidget mw, widget_value * val)
1856 shadow_type st = separator_type(val->value);
1857 switch ((unsigned int)st) {
1858 case SHADOW_NO_LINE:
1859 case SHADOW_SINGLE_LINE:
1860 case SHADOW_SINGLE_DASHED_LINE:
1862 case SHADOW_DOUBLE_LINE:
1863 case SHADOW_DOUBLE_DASHED_LINE:
1865 case SHADOW_DOUBLE_ETCHED_OUT:
1866 case SHADOW_DOUBLE_ETCHED_IN:
1867 case SHADOW_DOUBLE_ETCHED_OUT_DASH:
1868 case SHADOW_DOUBLE_ETCHED_IN_DASH:
1869 return (1 + 2 * mw->menu.shadow_thickness);
1870 case SHADOW_ETCHED_OUT:
1871 case SHADOW_ETCHED_IN:
1873 return mw->menu.shadow_thickness;
1878 separator_size(XlwMenuWidget mw,
1881 unsigned int *toggle_width,
1882 unsigned int *label_width,
1883 unsigned int *rest_width, unsigned int *height)
1885 *height = separator_decoration_height(mw, val);
1887 *toggle_width = *rest_width = 0;
1891 separator_draw(XlwMenuWidget mw,
1894 Boolean highlighted,
1898 unsigned int height,
1899 unsigned int label_tab, unsigned int binding_tab)
1901 unsigned int sep_width;
1908 separator_decoration_draw(mw,
1913 in_menubar, separator_type(val->value));
1917 pushright_size(XlwMenuWidget mw,
1920 unsigned int *toggle_width,
1921 unsigned int *label_width,
1922 unsigned int *rest_width, unsigned int *height)
1924 *height = *label_width = *toggle_width = *rest_width = 0;
1928 size_menu_item(XlwMenuWidget mw,
1931 unsigned int *toggle_width,
1932 unsigned int *label_width,
1933 unsigned int *rest_width, unsigned int *height)
1935 void (*function_ptr) (XlwMenuWidget _mw,
1936 widget_value * _val,
1937 Boolean _in_menubar,
1938 unsigned int *_toggle_width,
1939 unsigned int *_label_width,
1940 unsigned int *_rest_width, unsigned int *_height);
1941 widget_value_type mt = menu_item_type(val);
1943 switch ((unsigned int)mt) {
1946 function_ptr = toggle_button_size;
1948 case SEPARATOR_TYPE:
1949 function_ptr = separator_size;
1951 case INCREMENTAL_TYPE:
1953 function_ptr = cascade_button_size;
1956 function_ptr = push_button_size;
1958 case PUSHRIGHT_TYPE:
1959 function_ptr = pushright_size;
1963 function_ptr = label_button_size;
1967 (*function_ptr) (mw,
1970 toggle_width, label_width, rest_width, height);
1974 display_menu_item(XlwMenuWidget mw,
1978 Boolean highlighted, Boolean horizontal, Boolean just_compute)
1980 int x = where->x /* + mw->menu.shadow_thickness */ ;
1981 int y = where->y /* + mw->menu.shadow_thickness */ ;
1982 unsigned int toggle_width;
1983 unsigned int label_width;
1984 unsigned int binding_width;
1986 unsigned int height;
1987 unsigned int label_tab;
1988 unsigned int binding_tab;
1989 void (*function_ptr) (XlwMenuWidget _mw,
1990 widget_value * _val,
1991 Boolean _in_menubar,
1992 Boolean _highlighted,
1995 unsigned int _width,
1996 unsigned int _height,
1997 unsigned int _label_tab,
1998 unsigned int _binding_tab);
1999 widget_value_type mt;
2001 size_menu_item(mw, val, horizontal,
2002 &toggle_width, &label_width, &binding_width, &height);
2005 width = toggle_width + label_width + binding_width;
2006 height = ws->height - 2 * mw->menu.shadow_thickness;
2008 width = ws->width - 2 * mw->menu.shadow_thickness;
2009 toggle_width = ws->toggle_width;
2010 label_width = ws->label_width;
2019 label_tab = toggle_width;
2020 binding_tab = toggle_width + label_width;
2022 mt = menu_item_type(val);
2023 switch ((unsigned int)mt) {
2025 function_ptr = toggle_button_draw;
2028 function_ptr = radio_button_draw;
2030 case SEPARATOR_TYPE:
2031 function_ptr = separator_draw;
2033 case INCREMENTAL_TYPE:
2035 function_ptr = cascade_button_draw;
2038 function_ptr = push_button_draw;
2041 function_ptr = label_button_draw;
2043 default: /* do no drawing */
2047 (*function_ptr) (mw,
2052 x, y, width, height, label_tab, binding_tab);
2055 static void size_menu(XlwMenuWidget mw, int level)
2057 unsigned int toggle_width;
2058 unsigned int label_width;
2059 unsigned int rest_width;
2060 unsigned int height;
2061 unsigned int max_toggle_width = 0;
2062 unsigned int max_label_width = 0;
2063 unsigned int max_rest_width = 0;
2064 unsigned int max_height = 0;
2065 int horizontal_p = mw->menu.horizontal && (level == 0);
2069 if (level >= mw->menu.old_depth)
2072 ws = &mw->menu.windows[level];
2074 for (val = mw->menu.old_stack[level]->contents; val; val = val->next) {
2079 &label_width, &rest_width, &height);
2082 toggle_width + label_width + rest_width;
2083 if (height > max_height)
2084 max_height = height;
2086 if (max_toggle_width < toggle_width)
2087 max_toggle_width = toggle_width;
2088 if (max_label_width < label_width)
2089 max_label_width = label_width;
2090 if (max_rest_width < rest_width)
2091 max_rest_width = rest_width;
2092 max_height += height;
2096 ws->height = max_height;
2097 ws->width = max_label_width + max_rest_width + max_toggle_width;
2098 ws->toggle_width = max_toggle_width;
2099 ws->label_width = max_label_width;
2101 ws->width += 2 * mw->menu.shadow_thickness;
2102 ws->height += 2 * mw->menu.shadow_thickness;
2106 display_menu(XlwMenuWidget mw, int level, Boolean just_compute_p,
2107 XPoint * highlighted_pos, XPoint * hit, widget_value ** hit_return,
2108 widget_value * this, widget_value * that)
2111 widget_value *following_item;
2114 int horizontal_p = mw->menu.horizontal && (level == 0);
2116 int just_compute_this_one_p;
2118 if (level >= mw->menu.old_depth)
2121 if (level < mw->menu.old_depth - 1)
2122 following_item = mw->menu.old_stack[level + 1];
2124 if (lw_menu_accelerate
2125 && level == mw->menu.old_depth - 1
2126 && mw->menu.old_stack[level]->type == CASCADE_TYPE)
2127 just_compute_p = True;
2128 following_item = NULL;
2131 #if SLOPPY_TYPES == 1
2132 puts("===================================================================");
2133 print_widget_value(following_item, 1, 0);
2139 where.x = mw->menu.shadow_thickness;
2140 where.y = mw->menu.shadow_thickness;
2142 ws = &mw->menu.windows[level];
2143 for (val = mw->menu.old_stack[level]->contents; val; val = val->next) {
2146 highlighted_p = (val == following_item);
2147 /* If this is the partition (the dummy item which says that menus
2148 after this should be flushright) then figure out how big the
2149 following items are. This means we walk down the tail of the
2150 list twice, but that's no big deal - it's short.
2152 if (horizontal_p && (menu_item_type(val) == PUSHRIGHT_TYPE)) {
2154 XPoint flushright_size;
2156 flushright_size.x = 0;
2157 flushright_size.y = 0;
2158 for (rest = val; rest; rest = rest->next)
2159 display_menu_item(mw, rest, ws,
2161 highlighted_p, horizontal_p,
2164 ws->width - (flushright_size.x +
2165 mw->menu.shadow_thickness);
2166 if (new_x > where.x)
2168 /* We know what we need; don't draw this item. */
2172 if (highlighted_p && highlighted_pos) {
2174 highlighted_pos->x = where.x;
2176 highlighted_pos->y = where.y;
2179 just_compute_this_one_p =
2180 just_compute_p || ((this || that) && val != this
2185 display_menu_item(mw, val, ws, &where, highlighted_p,
2186 horizontal_p, just_compute_this_one_p);
2188 if (highlighted_p && highlighted_pos) {
2190 highlighted_pos->y = ws->height;
2192 highlighted_pos->x = ws->width;
2195 if (hit && !*hit_return) {
2196 if (horizontal_p && hit->x > start.x
2197 && hit->x <= where.x)
2199 else if (!horizontal_p && hit->y > start.y
2200 && hit->y <= where.y)
2205 where.y = mw->menu.shadow_thickness;
2207 where.x = mw->menu.shadow_thickness;
2210 /* Draw slab edges around menu */
2211 if (!just_compute_p)
2212 shadow_draw(mw, ws->window, 0, 0, ws->width, ws->height,
2217 static void set_new_state(XlwMenuWidget mw, widget_value * val, int level)
2221 mw->menu.new_depth = 0;
2222 for (i = 0; i < level; i++)
2223 push_new_stack(mw, mw->menu.old_stack[i]);
2225 push_new_stack(mw, val);
2228 static void make_windows_if_needed(XlwMenuWidget mw, int n)
2232 XSetWindowAttributes xswa;
2237 window_state *windows;
2240 if (mw->menu.windows_length >= n)
2243 root = RootWindowOfScreen(XtScreen(mw));
2244 /* grab the visual and depth from the nearest shell ancestor */
2245 visual = CopyFromParent;
2246 depth = CopyFromParent;
2248 while (visual == CopyFromParent && p) {
2250 visual = ((ShellWidget) p)->shell.visual;
2251 depth = p->core.depth;
2256 xswa.save_under = True;
2257 xswa.override_redirect = True;
2258 xswa.background_pixel = mw->core.background_pixel;
2259 xswa.border_pixel = mw->core.border_pixel;
2260 xswa.event_mask = (ExposureMask | ButtonMotionMask
2261 | ButtonReleaseMask | ButtonPressMask);
2262 xswa.cursor = mw->menu.cursor_shape;
2263 xswa.colormap = mw->core.colormap;
2264 mask = CWSaveUnder | CWOverrideRedirect | CWBackPixel | CWBorderPixel
2265 | CWEventMask | CWCursor | CWColormap;
2267 if (mw->menu.use_backing_store) {
2268 xswa.backing_store = Always;
2269 mask |= CWBackingStore;
2272 if (!mw->menu.windows) {
2274 (window_state *) XtMalloc(n * sizeof(window_state));
2278 (window_state *) XtRealloc((char *)mw->menu.windows,
2279 n * sizeof(window_state));
2280 start_at = mw->menu.windows_length;
2282 mw->menu.windows_length = n;
2284 windows = mw->menu.windows;
2286 for (i = start_at; i < n; i++) {
2289 windows[i].width = 1;
2290 windows[i].height = 1;
2292 XCreateWindow(XtDisplay(mw),
2295 0, depth, CopyFromParent, visual, mask,
2300 /* Make the window fit in the screen */
2302 fit_to_screen(XlwMenuWidget mw, window_state * ws, window_state * previous_ws,
2303 Boolean horizontal_p)
2305 int screen_width = WidthOfScreen(XtScreen(mw));
2306 int screen_height = HeightOfScreen(XtScreen(mw));
2310 else if ((int)(ws->x + ws->width) > screen_width) {
2312 ws->x = previous_ws->x - ws->width;
2314 ws->x = screen_width - ws->width;
2316 /* This check is to make sure we cut off the right side
2317 instead of the left side if the menu is wider than the
2325 else if ((int)(ws->y + ws->height) > screen_height) {
2327 /* A pulldown must either be entirely above or below the menubar.
2328 If we're here, the pulldown doesn't fit below the menubar, so
2329 let's determine if it will fit above the menubar.
2330 Only put it above if there is more room above than below.
2331 Note shadow_thickness offset to allow for slab surround.
2333 if (ws->y > (screen_height / 2))
2335 previous_ws->y - ws->height +
2336 mw->menu.shadow_thickness;
2338 ws->y = screen_height - ws->height;
2339 /* if it's taller than the screen, display the topmost part
2340 that will fit, beginning at the top of the screen. */
2347 /* Updates old_stack from new_stack and redisplays. */
2348 static void remap_menubar(XlwMenuWidget mw)
2352 XPoint selection_position;
2353 int old_depth = mw->menu.old_depth;
2354 int new_depth = mw->menu.new_depth;
2355 widget_value **old_stack;
2356 widget_value **new_stack;
2357 window_state *windows;
2358 widget_value *old_selection;
2359 widget_value *new_selection;
2361 /* Check that enough windows and old_stack are ready. */
2362 make_windows_if_needed(mw, new_depth);
2363 make_old_stack_space(mw, new_depth);
2364 windows = mw->menu.windows;
2365 old_stack = mw->menu.old_stack;
2366 new_stack = mw->menu.new_stack;
2368 /* compute the last identical different entry */
2369 for (i = 1; i < old_depth && i < new_depth; i++)
2370 if (old_stack[i] != new_stack[i])
2374 if (lw_menu_accelerate
2376 && last_same == old_depth - 1 && old_stack[last_same]->contents)
2379 /* Memorize the previously selected item to be able to refresh it */
2381 last_same + 1 < old_depth ? old_stack[last_same + 1] : NULL;
2383 last_same + 1 < new_depth ? new_stack[last_same + 1] : NULL;
2385 /* updates old_state from new_state. It has to be done now because
2386 display_menu (called below) uses the old_stack to know what to display. */
2387 for (i = last_same + 1; i < new_depth; i++)
2388 old_stack[i] = new_stack[i];
2390 mw->menu.old_depth = new_depth;
2392 /* refresh the last selection */
2393 selection_position.x = 0;
2394 selection_position.y = 0;
2395 display_menu(mw, last_same, new_selection == old_selection,
2396 &selection_position, NULL, NULL, old_selection,
2399 /* Now popup the new menus */
2400 for (i = last_same + 1; i < new_depth && new_stack[i]->contents; i++) {
2401 window_state *previous_ws = &windows[i - 1];
2402 window_state *ws = &windows[i];
2404 if (lw_menu_accelerate && i == new_depth - 1)
2407 ws->x = previous_ws->x + selection_position.x;
2408 ws->y = previous_ws->y + selection_position.y;
2410 /* take into account the slab around the new menu */
2411 ws->y -= mw->menu.shadow_thickness;
2414 widget_value *val = mw->menu.old_stack[i];
2415 if (val->contents->type == INCREMENTAL_TYPE) {
2416 /* okay, we're now doing a lisp callback to incrementally generate
2417 more of the menu. */
2418 XtCallCallbackList((Widget) mw,
2420 (XtPointer) val->contents);
2426 fit_to_screen(mw, ws, previous_ws, mw->menu.horizontal
2429 XClearWindow(XtDisplay(mw), ws->window);
2430 XMoveResizeWindow(XtDisplay(mw), ws->window, ws->x, ws->y,
2431 ws->width, ws->height);
2432 XMapRaised(XtDisplay(mw), ws->window);
2433 display_menu(mw, i, False, &selection_position, NULL, NULL,
2437 /* unmap the menus that popped down */
2439 last_same = new_depth;
2440 if (lw_menu_accelerate
2441 && last_same > 1 && new_stack[last_same - 1]->contents)
2444 for (i = last_same - 1; i < old_depth; i++)
2445 if (i >= last_same || !new_stack[i]->contents)
2446 XUnmapWindow(XtDisplay(mw), windows[i].window);
2450 motion_event_is_in_menu(XlwMenuWidget mw, XMotionEvent * ev, int level,
2451 XPoint * relative_pos)
2453 window_state *ws = &mw->menu.windows[level];
2454 int x = level == 0 ? ws->x : ws->x + mw->menu.shadow_thickness;
2455 int y = level == 0 ? ws->y : ws->y + mw->menu.shadow_thickness;
2456 relative_pos->x = ev->x_root - x;
2457 relative_pos->y = ev->y_root - y;
2458 return (x < ev->x_root && ev->x_root < (int)(x + ws->width) &&
2459 y < ev->y_root && ev->y_root < (int)(y + ws->height));
2463 map_event_to_widget_value(XlwMenuWidget mw, XMotionEvent * ev,
2464 widget_value ** val_ptr, int *level,
2465 Boolean * inside_menu)
2468 XPoint relative_pos;
2472 *inside_menu = False;
2474 /* Find the window */
2476 for (i = mw->menu.old_depth - 1; i >= 0; i--)
2478 for (i = 0; i <= mw->menu.old_depth - 1; i++)
2481 ws = &mw->menu.windows[i];
2482 if (ws && motion_event_is_in_menu(mw, ev, i, &relative_pos)) {
2483 *inside_menu = True; /* special logic for menubar below... */
2484 if ((ev->type == ButtonPress) || (ev->state != 0)) {
2485 display_menu(mw, i, True, NULL, &relative_pos,
2486 val_ptr, NULL, NULL);
2489 *inside_menu = True;
2491 } else if (mw->menu.horizontal || i == 0) {
2492 /* if we're clicking on empty part of the menubar, then
2493 unpost the stay-up menu */
2494 *inside_menu = False;
2503 static void make_drawing_gcs(XlwMenuWidget mw)
2506 unsigned long flags = (GCFont | GCForeground | GCBackground);
2509 xgcv.font = default_font_of_font_list(mw->menu.font_list)->fid;
2511 xgcv.font = mw->menu.font->fid;
2514 xgcv.foreground = mw->core.background_pixel;
2515 xgcv.background = mw->menu.foreground;
2516 mw->menu.background_gc = XtGetGC((Widget) mw, flags, &xgcv);
2518 xgcv.foreground = mw->menu.foreground;
2519 xgcv.background = mw->core.background_pixel;
2520 mw->menu.foreground_gc = XtGetGC((Widget) mw, flags, &xgcv);
2522 if (mw->menu.select_color != (Pixel) - 1) {
2523 xgcv.foreground = mw->menu.select_color;
2525 Display *dpy = XtDisplay(mw);
2526 if (CellsOfScreen(DefaultScreenOfDisplay(dpy)) <= 2) { /* mono */
2527 xgcv.foreground = mw->menu.foreground;
2528 } else { /* color */
2530 Colormap cmap = mw->core.colormap;
2531 xcolor.pixel = mw->core.background_pixel;
2532 XQueryColor(dpy, cmap, &xcolor);
2533 xcolor.red = (xcolor.red * 17) / 20;
2534 xcolor.green = (xcolor.green * 17) / 20;
2535 xcolor.blue = (xcolor.blue * 17) / 20;
2536 if (allocate_nearest_color(dpy, cmap, &xcolor))
2537 xgcv.foreground = xcolor.pixel;
2540 xgcv.background = mw->core.background_pixel;
2541 mw->menu.select_gc = XtGetGC((Widget) mw, flags, &xgcv);
2543 xgcv.foreground = mw->menu.foreground;
2544 xgcv.background = mw->core.background_pixel;
2545 xgcv.fill_style = FillStippled;
2546 xgcv.stipple = mw->menu.gray_pixmap;
2547 mw->menu.inactive_gc = XtGetGC((Widget) mw,
2548 (flags | GCFillStyle | GCStipple),
2551 xgcv.foreground = mw->menu.highlight_foreground;
2552 xgcv.background = mw->core.background_pixel;
2553 mw->menu.highlight_gc = XtGetGC((Widget) mw, flags, &xgcv);
2555 xgcv.foreground = mw->menu.title_foreground;
2556 xgcv.background = mw->core.background_pixel;
2557 mw->menu.title_gc = XtGetGC((Widget) mw, flags, &xgcv);
2559 xgcv.foreground = mw->menu.button_foreground;
2560 xgcv.background = mw->core.background_pixel;
2561 mw->menu.button_gc = XtGetGC((Widget) mw, flags, &xgcv);
2563 xgcv.fill_style = FillStippled;
2564 xgcv.stipple = mw->menu.gray_pixmap;
2565 mw->menu.inactive_button_gc = XtGetGC((Widget) mw,
2566 (flags | GCFillStyle | GCStipple),
2570 static void release_drawing_gcs(XlwMenuWidget mw)
2572 XtReleaseGC((Widget) mw, mw->menu.foreground_gc);
2573 XtReleaseGC((Widget) mw, mw->menu.button_gc);
2574 XtReleaseGC((Widget) mw, mw->menu.highlight_gc);
2575 XtReleaseGC((Widget) mw, mw->menu.title_gc);
2576 XtReleaseGC((Widget) mw, mw->menu.inactive_gc);
2577 XtReleaseGC((Widget) mw, mw->menu.inactive_button_gc);
2578 XtReleaseGC((Widget) mw, mw->menu.background_gc);
2579 XtReleaseGC((Widget) mw, mw->menu.select_gc);
2580 /* let's get some segvs if we try to use these... */
2581 mw->menu.foreground_gc = (GC) - 1;
2582 mw->menu.button_gc = (GC) - 1;
2583 mw->menu.highlight_gc = (GC) - 1;
2584 mw->menu.title_gc = (GC) - 1;
2585 mw->menu.inactive_gc = (GC) - 1;
2586 mw->menu.inactive_button_gc = (GC) - 1;
2587 mw->menu.background_gc = (GC) - 1;
2588 mw->menu.select_gc = (GC) - 1;
2591 #define MINL(x,y) ((((unsigned long) (x)) < ((unsigned long) (y))) \
2592 ? ((unsigned long) (x)) : ((unsigned long) (y)))
2594 static void make_shadow_gcs(XlwMenuWidget mw)
2597 unsigned long pm = 0;
2598 Display *dpy = XtDisplay((Widget) mw);
2599 Colormap cmap = mw->core.colormap;
2601 int top_frobbed = 0, bottom_frobbed = 0;
2603 if (mw->menu.top_shadow_color == (Pixel) (-1))
2604 mw->menu.top_shadow_color = mw->core.background_pixel;
2605 if (mw->menu.bottom_shadow_color == (Pixel) (-1))
2606 mw->menu.bottom_shadow_color = mw->menu.foreground;
2608 if (mw->menu.top_shadow_color == mw->core.background_pixel ||
2609 mw->menu.top_shadow_color == mw->menu.foreground) {
2610 topc.pixel = mw->core.background_pixel;
2611 XQueryColor(dpy, cmap, &topc);
2612 /* don't overflow/wrap! */
2613 topc.red = MINL(65535, topc.red * 1.2);
2614 topc.green = MINL(65535, topc.green * 1.2);
2615 topc.blue = MINL(65535, topc.blue * 1.2);
2616 if (allocate_nearest_color(dpy, cmap, &topc)) {
2617 if (topc.pixel == mw->core.background_pixel) {
2618 XFreeColors(dpy, cmap, &topc.pixel, 1, 0);
2619 topc.red = MINL(65535, topc.red + 0x8000);
2620 topc.green = MINL(65535, topc.green + 0x8000);
2621 topc.blue = MINL(65535, topc.blue + 0x8000);
2622 if (allocate_nearest_color(dpy, cmap, &topc)) {
2623 mw->menu.top_shadow_color = topc.pixel;
2626 mw->menu.top_shadow_color = topc.pixel;
2632 if (mw->menu.bottom_shadow_color == mw->menu.foreground ||
2633 mw->menu.bottom_shadow_color == mw->core.background_pixel) {
2634 botc.pixel = mw->core.background_pixel;
2635 XQueryColor(dpy, cmap, &botc);
2636 botc.red = (botc.red * 3) / 5;
2637 botc.green = (botc.green * 3) / 5;
2638 botc.blue = (botc.blue * 3) / 5;
2639 if (allocate_nearest_color(dpy, cmap, &botc)) {
2640 if (botc.pixel == mw->core.background_pixel) {
2641 XFreeColors(dpy, cmap, &botc.pixel, 1, 0);
2642 botc.red = MINL(65535, botc.red + 0x4000);
2643 botc.green = MINL(65535, botc.green + 0x4000);
2644 botc.blue = MINL(65535, botc.blue + 0x4000);
2645 if (allocate_nearest_color(dpy, cmap, &botc)) {
2646 mw->menu.bottom_shadow_color =
2650 mw->menu.bottom_shadow_color = botc.pixel;
2657 if (top_frobbed && bottom_frobbed) {
2659 ((topc.red / 3) + (topc.green / 3) + (topc.blue / 3));
2661 ((botc.red / 3) + (botc.green / 3) + (botc.blue / 3));
2662 if (bot_avg > top_avg) {
2663 Pixel tmp = mw->menu.top_shadow_color;
2664 mw->menu.top_shadow_color =
2665 mw->menu.bottom_shadow_color;
2666 mw->menu.bottom_shadow_color = tmp;
2667 } else if (topc.pixel == botc.pixel) {
2668 if (botc.pixel == mw->menu.foreground)
2669 mw->menu.top_shadow_color =
2670 mw->core.background_pixel;
2672 mw->menu.bottom_shadow_color =
2673 mw->menu.foreground;
2677 if (!mw->menu.top_shadow_pixmap &&
2678 mw->menu.top_shadow_color == mw->core.background_pixel) {
2679 mw->menu.top_shadow_pixmap = mw->menu.gray_pixmap;
2680 mw->menu.top_shadow_color = mw->menu.foreground;
2682 if (!mw->menu.bottom_shadow_pixmap &&
2683 mw->menu.bottom_shadow_color == mw->core.background_pixel) {
2684 mw->menu.bottom_shadow_pixmap = mw->menu.gray_pixmap;
2685 mw->menu.bottom_shadow_color = mw->menu.foreground;
2688 xgcv.fill_style = FillOpaqueStippled;
2689 xgcv.foreground = mw->menu.top_shadow_color;
2690 xgcv.background = mw->core.background_pixel;
2691 /* xgcv.stipple = mw->menu.top_shadow_pixmap; gtb */
2692 if (mw->menu.top_shadow_pixmap &&
2693 mw->menu.top_shadow_pixmap != XmUNSPECIFIED_PIXMAP)
2694 xgcv.stipple = mw->menu.top_shadow_pixmap;
2697 pm = (xgcv.stipple ? GCStipple | GCFillStyle : 0);
2698 mw->menu.shadow_top_gc =
2699 XtGetGC((Widget) mw, GCForeground | GCBackground | pm, &xgcv);
2701 xgcv.foreground = mw->menu.bottom_shadow_color;
2702 /* xgcv.stipple = mw->menu.bottom_shadow_pixmap; gtb */
2703 if (mw->menu.bottom_shadow_pixmap &&
2704 mw->menu.bottom_shadow_pixmap != XmUNSPECIFIED_PIXMAP)
2705 xgcv.stipple = mw->menu.bottom_shadow_pixmap;
2708 pm = (xgcv.stipple ? GCStipple | GCFillStyle : 0);
2709 mw->menu.shadow_bottom_gc =
2710 XtGetGC((Widget) mw, GCForeground | GCBackground | pm, &xgcv);
2713 static void release_shadow_gcs(XlwMenuWidget mw)
2715 XtReleaseGC((Widget) mw, mw->menu.shadow_top_gc);
2716 XtReleaseGC((Widget) mw, mw->menu.shadow_bottom_gc);
2719 static void extract_font_extents(XlwMenuWidget mw)
2722 /* Find the maximal ascent/descent of the fonts in the font list
2723 so that all menu items can be the same height... */
2724 mw->menu.font_ascent = 0;
2725 mw->menu.font_descent = 0;
2728 XmFontContext context;
2729 #if (XmVersion >= 1002)
2730 XmFontListEntry fontentry;
2732 XmStringCharSet charset;
2736 if (!XmFontListInitFontContext(&context, mw->menu.font_list))
2738 #if (XmVersion >= 1002)
2739 /* There is a BUG in the 1.2 version of XmFontListGetNextFont() (or more
2740 specifically, in _XmGetFirstFont()) that can cause a null pointer to be
2741 passed to XFontsOfFontSet. Use XmFontListNextEntry(), which is the
2742 newer equivalent, instead. Also, it supports font sets, and the
2743 older function doesn't. */
2744 while ((fontentry = XmFontListNextEntry(context))) {
2747 XtPointer one_of_them =
2748 XmFontListEntryGetFont(fontentry, &rettype);
2749 if (rettype == XmFONT_IS_FONTSET) {
2750 XFontSet fontset = (XFontSet) one_of_them;
2751 XFontStruct **fontstruct_list;
2752 char **fontname_list;
2754 XFontsOfFontSet(fontset, &fontstruct_list,
2756 while (--fontcount >= 0) {
2757 font = fontstruct_list[fontcount];
2759 (int)mw->menu.font_ascent)
2760 mw->menu.font_ascent =
2763 (int)mw->menu.font_descent)
2764 mw->menu.font_descent =
2767 } else { /* XmFONT_IS_FONT */
2769 font = (XFontStruct *) one_of_them;
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 #else /* motif 1.1 */
2777 while (XmFontListGetNextFont(context, &charset, &font)) {
2778 if (font->ascent > (int)mw->menu.font_ascent)
2779 mw->menu.font_ascent = font->ascent;
2780 if (font->descent > (int)mw->menu.font_descent)
2781 mw->menu.font_descent = font->descent;
2784 #endif /* Motif version */
2785 XmFontListFreeFontContext(context);
2787 #else /* Not Motif */
2788 # ifdef USE_XFONTSET
2789 XFontStruct **fontstruct_list;
2790 char **fontname_list;
2792 int fontcount = XFontsOfFontSet(mw->menu.font_set, &fontstruct_list,
2794 mw->menu.font_ascent = 0;
2795 mw->menu.font_descent = 0;
2796 # if 0 /* nasty, personal debug, Kazz */
2797 fprintf(stderr, "fontSet count is %d\n", fontcount);
2799 while (--fontcount >= 0) {
2800 font = fontstruct_list[fontcount];
2801 if (font->ascent > (int)mw->menu.font_ascent)
2802 mw->menu.font_ascent = font->ascent;
2803 if (font->descent > (int)mw->menu.font_descent)
2804 mw->menu.font_descent = font->descent;
2806 # else /* ! USE_XFONTSET */
2807 mw->menu.font_ascent = mw->menu.font->ascent;
2808 mw->menu.font_descent = mw->menu.font->descent;
2810 #endif /* NEED_MOTIF */
2814 static XFontStruct *default_font_of_font_list(XmFontList font_list)
2816 XFontStruct *font = 0;
2818 /* Xm/Label.c does this: */
2819 _XmFontListGetDefaultFont(font_list, &font);
2822 XmFontContext context;
2823 #if (XmVersion >= 1002)
2824 XmFontListEntry fontentry;
2826 XtPointer one_of_them;
2828 XmStringCharSet charset;
2831 if (!XmFontListInitFontContext(&context, font_list))
2833 #if (XmVersion >= 1002)
2834 /* There is a BUG in the 1.2 version of XmFontListGetNextFont() (or more
2835 specifically, in _XmGetFirstFont()) that can cause a null pointer to be
2836 passed to XFontsOfFontSet. Use XmFontListNextEntry(), which is the
2837 newer equivalent, instead. */
2838 fontentry = XmFontListNextEntry(context);
2839 one_of_them = XmFontListEntryGetFont(fontentry, &rettype);
2840 if (rettype == XmFONT_IS_FONTSET) {
2841 XFontSet fontset = (XFontSet) one_of_them;
2842 XFontStruct **fontstruct_list;
2843 char **fontname_list;
2844 (void)XFontsOfFontSet(fontset, &fontstruct_list,
2846 font = fontstruct_list[0];
2847 } else { /* XmFONT_IS_FONT */
2849 font = (XFontStruct *) one_of_them;
2852 if (!XmFontListGetNextFont(context, &charset, &font))
2856 XmFontListFreeFontContext(context);
2864 #endif /* NEED_MOTIF */
2867 XlwMenuInitialize(Widget request, Widget new, ArgList args, Cardinal * num_args)
2869 /* Get the GCs and the widget size */
2870 XlwMenuWidget mw = (XlwMenuWidget) new;
2872 RootWindowOfScreen(DefaultScreenOfDisplay(XtDisplay(mw)));
2873 Display *display = XtDisplay(mw);
2875 /* mw->menu.cursor = XCreateFontCursor (display, mw->menu.cursor_shape); */
2876 mw->menu.cursor = mw->menu.cursor_shape;
2878 mw->menu.gray_pixmap =
2879 XCreatePixmapFromBitmapData(display, window, (char *)gray_bits,
2880 gray_width, gray_height, 1, 0, 1);
2883 /* #### Even if it's a kludge!!!, we should consider doing the same for
2885 /* The menu.font_list slot came from the *fontList resource (Motif standard.)
2886 The menu.font_list_2 slot came from the *font resource, for backward
2887 compatibility with older versions of this code, and consistency with the
2888 rest of emacs. If both font and fontList are specified, we use fontList.
2889 If only one is specified, we use that. If neither are specified, we
2890 use the "fallback" value. What a kludge!!!
2892 Note that this has the bug that a more general wildcard like "*fontList:"
2893 will override a more specific resource like "Emacs*menubar.font:". But
2894 I can't think of a way around that.
2896 if (mw->menu.font_list) /* if *fontList is specified, use that */
2898 else if (mw->menu.font_list_2) /* else if *font is specified, use that */
2899 mw->menu.font_list = mw->menu.font_list_2;
2900 else /* otherwise use default */
2901 mw->menu.font_list = mw->menu.fallback_font_list;
2904 make_drawing_gcs(mw);
2905 make_shadow_gcs(mw);
2906 extract_font_extents(mw);
2908 mw->menu.popped_up = False;
2909 mw->menu.pointer_grabbed = False;
2910 mw->menu.next_release_must_exit = False;
2912 mw->menu.old_depth = 1;
2913 mw->menu.old_stack = XtNew(widget_value *);
2914 mw->menu.old_stack_length = 1;
2915 mw->menu.old_stack[0] = mw->menu.contents;
2917 mw->menu.new_depth = 0;
2918 mw->menu.new_stack = 0;
2919 mw->menu.new_stack_length = 0;
2920 push_new_stack(mw, mw->menu.contents);
2922 mw->menu.windows = XtNew(window_state);
2923 mw->menu.windows_length = 1;
2924 mw->menu.windows[0].x = 0;
2925 mw->menu.windows[0].y = 0;
2926 mw->menu.windows[0].width = 0;
2927 mw->menu.windows[0].height = 0;
2930 mw->core.width = mw->menu.windows[0].width;
2931 mw->core.height = mw->menu.windows[0].height;
2934 static void XlwMenuClassInitialize(void)
2936 initialize_massaged_resource_char();
2940 XlwMenuRealize(Widget w, Mask * valueMask, XSetWindowAttributes * attributes)
2942 XlwMenuWidget mw = (XlwMenuWidget) w;
2943 XSetWindowAttributes xswa;
2946 (*xlwMenuWidgetClass->core_class.superclass->core_class.realize)
2947 (w, valueMask, attributes);
2949 xswa.save_under = True;
2950 xswa.cursor = mw->menu.cursor_shape;
2951 mask = CWSaveUnder | CWCursor;
2952 if (mw->menu.use_backing_store) {
2953 xswa.backing_store = Always;
2954 mask |= CWBackingStore;
2956 XChangeWindowAttributes(XtDisplay(w), XtWindow(w), mask, &xswa);
2958 mw->menu.windows[0].window = XtWindow(w);
2959 mw->menu.windows[0].x = w->core.x;
2960 mw->menu.windows[0].y = w->core.y;
2961 mw->menu.windows[0].width = w->core.width;
2962 mw->menu.windows[0].height = w->core.height;
2965 /* Only the toplevel menubar/popup is a widget so it's the only one that
2966 receives expose events through Xt. So we repaint all the other panes
2967 when receiving an Expose event. */
2968 static void XlwMenuRedisplay(Widget w, XEvent * ev, Region region)
2970 XlwMenuWidget mw = (XlwMenuWidget) w;
2973 if (mw->core.being_destroyed)
2976 for (i = 0; i < mw->menu.old_depth; i++)
2977 display_menu(mw, i, False, NULL, NULL, NULL, NULL, NULL);
2978 set_new_state(mw, NULL, mw->menu.old_depth); /* #### - ??? */
2979 remap_menubar(mw); /* #### - do these two lines do anything? */
2982 static void XlwMenuDestroy(Widget w)
2985 XlwMenuWidget mw = (XlwMenuWidget) w;
2987 if (mw->menu.pointer_grabbed) {
2988 XtUngrabPointer(w, CurrentTime);
2989 mw->menu.pointer_grabbed = False;
2992 release_drawing_gcs(mw);
2993 release_shadow_gcs(mw);
2995 /* this doesn't come from the resource db but is created explicitly
2996 so we must free it ourselves. */
2997 XFreePixmap(XtDisplay(mw), mw->menu.gray_pixmap);
2998 mw->menu.gray_pixmap = (Pixmap) - 1;
3000 /* Don't free mw->menu.contents because that comes from our creator.
3001 The `*_stack' elements are just pointers into `contents' so leave
3002 that alone too. But free the stacks themselves. */
3003 if (mw->menu.old_stack)
3004 XtFree((char *)mw->menu.old_stack);
3005 if (mw->menu.new_stack)
3006 XtFree((char *)mw->menu.new_stack);
3008 /* Remember, you can't free anything that came from the resource
3009 database. This includes:
3011 mw->menu.top_shadow_pixmap
3012 mw->menu.bottom_shadow_pixmap
3015 Also the color cells of top_shadow_color, bottom_shadow_color,
3016 foreground, and button_foreground will never be freed until this
3017 client exits. Nice, eh?
3020 /* start from 1 because the one in slot 0 is w->core.window */
3021 for (i = 1; i < mw->menu.windows_length; i++)
3022 XDestroyWindow(XtDisplay(mw), mw->menu.windows[i].window);
3023 if (mw->menu.windows)
3024 XtFree((char *)mw->menu.windows);
3028 XlwMenuSetValues(Widget current, Widget request, Widget new, ArgList args,
3029 Cardinal * num_args)
3031 XlwMenuWidget oldmw = (XlwMenuWidget) current;
3032 XlwMenuWidget newmw = (XlwMenuWidget) new;
3033 Boolean redisplay = False;
3036 if (newmw->menu.contents
3037 && newmw->menu.contents->contents
3038 && newmw->menu.contents->contents->change >= VISIBLE_CHANGE)
3041 if (newmw->core.background_pixel != oldmw->core.background_pixel
3042 || newmw->menu.foreground != oldmw->menu.foreground
3043 /* For the XEditResource protocol, which may want to change the font. */
3045 || newmw->menu.font_list != oldmw->menu.font_list
3046 || newmw->menu.font_list_2 != oldmw->menu.font_list_2
3047 || newmw->menu.fallback_font_list != oldmw->menu.fallback_font_list
3049 || newmw->menu.font != oldmw->menu.font
3052 release_drawing_gcs(newmw);
3053 make_drawing_gcs(newmw);
3056 for (i = 0; i < oldmw->menu.windows_length; i++) {
3057 XSetWindowBackground(XtDisplay(oldmw),
3058 oldmw->menu.windows[i].window,
3059 newmw->core.background_pixel);
3060 /* clear windows and generate expose events */
3061 XClearArea(XtDisplay(oldmw),
3062 oldmw->menu.windows[i].window, 0, 0, 0, 0,
3070 static void XlwMenuResize(Widget w)
3072 XlwMenuWidget mw = (XlwMenuWidget) w;
3074 mw->menu.windows[0].width = mw->core.width;
3075 mw->menu.windows[0].height = mw->core.height;
3078 /* Action procedures */
3080 handle_single_motion_event(XlwMenuWidget mw, XMotionEvent * ev,
3087 if (!map_event_to_widget_value(mw, ev, &val, &level, &stay_up)) {
3088 /* we wind up here when: (a) the event is in the menubar, (b) the
3089 event isn't in the menubar or any of the panes, (c) the event is on
3090 a disabled menu item */
3091 pop_new_stack_if_no_contents(mw);
3092 if (select_p && !stay_up) {
3093 /* pop down all menus and exit */
3094 mw->menu.next_release_must_exit = True;
3095 set_new_state(mw, (val = NULL), 1);
3098 /* we wind up here when: (a) the event pops up a pull_right menu,
3099 (b) a menu item that is not disabled is highlighted */
3100 if (select_p && mw->menu.bounce_down
3101 && close_to_reference_time((Widget) mw,
3102 mw->menu.menu_bounce_time,
3104 /* motion can cause more than one event. Don't bounce right back
3105 up if we've just bounced down. */
3107 } else if (select_p && mw->menu.bounce_down &&
3108 mw->menu.last_selected_val &&
3109 (mw->menu.last_selected_val == val)) {
3110 val = NULL; /* assigned to mw->last_selected_val below */
3111 mw->menu.menu_bounce_time = ev->time;
3112 /* popdown last menu if we're selecting the same menu item as we did
3113 last time and the XlwMenu.bounceDown resource is set, if the
3114 item is on the menubar itself, then exit. */
3115 if (level == (mw->menu.popped_up ? 0 : 1))
3116 mw->menu.next_release_must_exit = True;
3118 mw->menu.menu_bounce_time = 0;
3119 set_new_state(mw, val, level);
3121 mw->menu.last_selected_val = val;
3124 /* Sync with the display. Makes it feel better on X terms. */
3125 XFlush(XtDisplay(mw));
3129 handle_motion_event(XlwMenuWidget mw, XMotionEvent * ev, Boolean select_p)
3133 unsigned int state = ev->state;
3134 XMotionEvent *event = ev, dummy;
3136 /* allow motion events to be generated again */
3137 dummy.window = ev->window;
3139 && XQueryPointer(XtDisplay(mw), dummy.window,
3140 &dummy.root, &dummy.subwindow,
3141 &dummy.x_root, &dummy.y_root,
3142 &dummy.x, &dummy.y, &dummy.state)
3143 && dummy.state == state && (dummy.x_root != x || dummy.y_root != y)) {
3144 /* don't handle the event twice or that breaks bounce_down. --Stig */
3145 dummy.type = ev->type;
3149 lw_menu_accelerate = False;
3150 handle_single_motion_event(mw, event, select_p);
3153 Time x_focus_timestamp_really_sucks_fix_me_better;
3155 static void Start(Widget w, XEvent * ev, String * params, Cardinal * num_params)
3157 XlwMenuWidget mw = (XlwMenuWidget) w;
3159 lw_menubar_widget = w;
3161 lw_menu_active = True;
3163 if (!mw->menu.pointer_grabbed) {
3164 mw->menu.menu_post_time = ev->xbutton.time;
3165 mw->menu.menu_bounce_time = 0;
3166 mw->menu.next_release_must_exit = True;
3167 mw->menu.last_selected_val = NULL;
3168 x_focus_timestamp_really_sucks_fix_me_better =
3169 ((XButtonPressedEvent *) ev)->time;
3170 XtCallCallbackList((Widget) mw, mw->menu.open, NULL);
3172 /* notes the absolute position of the menubar window */
3173 mw->menu.windows[0].x = ev->xmotion.x_root - ev->xmotion.x;
3174 mw->menu.windows[0].y = ev->xmotion.y_root - ev->xmotion.y;
3176 XtGrabPointer((Widget) mw, False,
3177 (ButtonMotionMask | ButtonReleaseMask |
3178 ButtonPressMask), GrabModeAsync, GrabModeAsync,
3179 None, mw->menu.cursor_shape,
3180 ((XButtonPressedEvent *) ev)->time);
3181 mw->menu.pointer_grabbed = True;
3184 /* handles the down like a move, slots are mostly compatible */
3185 handle_motion_event(mw, &ev->xmotion, True);
3188 static void Drag(Widget w, XEvent * ev, String * params, Cardinal * num_params)
3190 XlwMenuWidget mw = (XlwMenuWidget) w;
3191 handle_motion_event(mw, &ev->xmotion, False);
3195 Select(Widget w, XEvent * ev, String * params, Cardinal * num_params)
3197 XlwMenuWidget mw = (XlwMenuWidget) w;
3198 widget_value *selected_item =
3199 mw->menu.old_stack[mw->menu.old_depth - 1];
3201 lw_menu_accelerate = False;
3203 /* If user releases the button quickly, without selecting anything,
3204 after the initial down-click that brought the menu up,
3206 if ((selected_item == 0 || selected_item->call_data == 0)
3207 && (!mw->menu.next_release_must_exit
3208 || close_to_reference_time(w, mw->menu.menu_post_time, ev))) {
3209 mw->menu.next_release_must_exit = False;
3213 /* pop down everything */
3214 mw->menu.new_depth = 1;
3217 /* Destroy() only gets called for popup menus. Menubar widgets aren't
3218 destroyed when their menu panes get nuked. */
3219 if (mw->menu.pointer_grabbed) {
3220 XtUngrabPointer((Widget) w, ev->xmotion.time);
3221 mw->menu.pointer_grabbed = False;
3224 if (mw->menu.popped_up) {
3225 mw->menu.popped_up = False;
3226 XtPopdown(XtParent(mw));
3229 lw_menu_active = False;
3231 x_focus_timestamp_really_sucks_fix_me_better =
3232 ((XButtonPressedEvent *) ev)->time;
3235 XtCallCallbackList((Widget) mw, mw->menu.select,
3236 (XtPointer) selected_item);
3239 /* Action procedures for keyboard accelerators */
3242 void xlw_set_menu(Widget w, widget_value * val)
3244 lw_menubar_widget = w;
3245 set_new_state((XlwMenuWidget) w, val, 1);
3248 /* prepare the menu structure via the call-backs */
3249 void xlw_map_menu(Time t)
3251 XlwMenuWidget mw = (XlwMenuWidget) lw_menubar_widget;
3253 lw_menu_accelerate = True;
3255 if (!mw->menu.pointer_grabbed) {
3256 XWindowAttributes ret;
3257 Window parent = 0UL, root = 0UL;
3258 Window *waste = NULL;
3259 unsigned int num_waste;
3261 lw_menu_active = True;
3263 mw->menu.menu_post_time = t;
3264 mw->menu.menu_bounce_time = 0;
3266 mw->menu.next_release_must_exit = True;
3267 mw->menu.last_selected_val = NULL;
3269 XtCallCallbackList((Widget) mw, mw->menu.open, NULL);
3271 /* do this for keyboards too! */
3272 /* notes the absolute position of the menubar window */
3274 mw->menu.windows [0].x = ev->xmotion.x_root - ev->xmotion.x;
3275 mw->menu.windows [0].y = ev->xmotion.y_root - ev->xmotion.y;
3278 /* get the geometry of the menubar */
3280 /* there has to be a better way than this. */
3282 mw->menu.windows[0].x = 0;
3283 mw->menu.windows[0].y = 0;
3285 parent = XtWindow(lw_menubar_widget);
3287 XGetWindowAttributes(XtDisplay(lw_menubar_widget),
3289 mw->menu.windows[0].x += ret.x;
3290 mw->menu.windows[0].y += ret.y;
3293 XQueryTree(XtDisplay(lw_menubar_widget), parent,
3294 &root, &parent, &waste, &num_waste);
3299 while (parent != root);
3301 XtGrabPointer((Widget) mw, False,
3302 (ButtonMotionMask | ButtonReleaseMask |
3303 ButtonPressMask), GrabModeAsync, GrabModeAsync,
3304 None, mw->menu.cursor_shape, t);
3305 mw->menu.pointer_grabbed = True;
3309 /* display the stupid menu already */
3310 void xlw_display_menu(Time t)
3312 XlwMenuWidget mw = (XlwMenuWidget) lw_menubar_widget;
3314 lw_menu_accelerate = True;
3318 /* Sync with the display. Makes it feel better on X terms. */
3319 XFlush(XtDisplay(mw));
3322 /* push a sub menu */
3323 void xlw_push_menu(widget_value * val)
3325 push_new_stack((XlwMenuWidget) lw_menubar_widget, val);
3328 /* pop a sub menu */
3329 int xlw_pop_menu(void)
3331 if (((XlwMenuWidget) lw_menubar_widget)->menu.new_depth > 0)
3332 ((XlwMenuWidget) lw_menubar_widget)->menu.new_depth--;
3338 void xlw_kill_menus(widget_value * val)
3340 XlwMenuWidget mw = (XlwMenuWidget) lw_menubar_widget;
3342 lw_menu_accelerate = False;
3344 mw->menu.new_depth = 1;
3347 if (mw->menu.pointer_grabbed) {
3348 XtUngrabPointer(lw_menubar_widget, CurrentTime);
3349 mw->menu.pointer_grabbed = False;
3352 lw_menu_active = False;
3353 XtCallCallbackList(lw_menubar_widget, mw->menu.select, (XtPointer) val);
3356 /* set the menu item */
3357 void xlw_set_item(widget_value * val)
3359 if (((XlwMenuWidget) lw_menubar_widget)->menu.new_depth > 0)
3360 ((XlwMenuWidget) lw_menubar_widget)->menu.new_depth--;
3361 push_new_stack((XlwMenuWidget) lw_menubar_widget, val);
3364 /* get either the current entry or a list of all entries in the current submenu */
3365 widget_value *xlw_get_entries(int allp)
3367 XlwMenuWidget mw = (XlwMenuWidget) lw_menubar_widget;
3369 if (mw->menu.new_depth >= 2)
3370 return mw->menu.new_stack[mw->menu.new_depth -
3373 return mw->menu.new_stack[0];
3374 } else if (mw->menu.new_depth >= 1)
3375 return mw->menu.new_stack[mw->menu.new_depth - 1];
3380 int xlw_menu_level(void)
3382 return ((XlwMenuWidget) lw_menubar_widget)->menu.new_depth;
3385 /* Special code to pop-up a menu */
3386 void xlw_pop_up_menu(XlwMenuWidget mw, XButtonPressedEvent * event)
3388 int x = event->x_root;
3389 int y = event->y_root;
3392 int borderwidth = mw->menu.shadow_thickness;
3393 Screen *screen = XtScreen(mw);
3395 mw->menu.menu_post_time = event->time;
3396 mw->menu.menu_bounce_time = 0;
3397 mw->menu.next_release_must_exit = True;
3398 mw->menu.last_selected_val = NULL;
3400 XtCallCallbackList((Widget) mw, mw->menu.open, NULL);
3404 w = mw->menu.windows[0].width;
3405 h = mw->menu.windows[0].height;
3410 if (x < borderwidth)
3413 if (x > WidthOfScreen(screen) - w - 2 * borderwidth)
3414 x = WidthOfScreen(screen) - w - 2 * borderwidth;
3416 if (y < borderwidth)
3419 if (y > HeightOfScreen(screen) - h - 2 * borderwidth)
3420 y = HeightOfScreen(screen) - h - 2 * borderwidth;
3422 mw->menu.popped_up = True;
3423 XtConfigureWidget(XtParent(mw), x, y, w, h,
3424 XtParent(mw)->core.border_width);
3425 XtPopup(XtParent(mw), XtGrabExclusive);
3426 display_menu(mw, 0, False, NULL, NULL, NULL, NULL, NULL);
3427 if (!mw->menu.pointer_grabbed) {
3428 XtGrabPointer((Widget) mw, False,
3429 (ButtonMotionMask | ButtonReleaseMask |
3430 ButtonPressMask), GrabModeAsync, GrabModeAsync,
3431 None, mw->menu.cursor_shape, event->time);
3432 mw->menu.pointer_grabbed = True;
3435 mw->menu.windows[0].x = x + borderwidth;
3436 mw->menu.windows[0].y = y + borderwidth;
3438 handle_motion_event(mw, (XMotionEvent *) event, True);
3444 * This is a horrible function which should not be needed.
3445 * use it to put the resize method back the way the XlwMenu
3446 * class initializer put it. Motif screws with this when
3447 * the XlwMenu class gets instantiated.
3449 void xlw_unmunge_class_resize(Widget w)
3451 if (w->core.widget_class->core_class.resize != XlwMenuResize)
3452 w->core.widget_class->core_class.resize = XlwMenuResize;