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>
50 #include <sxe-memory.h>
52 #ifdef USE_DEBUG_MALLOC
57 xlwMenuTranslations[] = "<BtnDown>: start()\n\
58 <BtnMotion>: drag()\n\
62 extern Widget lw_menubar_widget;
64 #define offset(field) XtOffset(XlwMenuWidget, field)
65 static XtResource xlwMenuResources[] = {
67 /* There are three font list resources, so that we can accept either of
68 the resources *fontList: or *font:, and so that we can tell the
69 difference between them being specified, and being defaulted to a
70 font from the XtRString specified here. */
71 {XmNfontList, XmCFontList, XmRFontList, sizeof(XmFontList),
72 offset(menu.font_list), XtRImmediate, (XtPointer) 0}
74 {XtNfont, XtCFont, XmRFontList, sizeof(XmFontList),
75 offset(menu.font_list_2), XtRImmediate, (XtPointer) 0}
77 {XmNfontList, XmCFontList, XmRFontList, sizeof(XmFontList),
78 offset(menu.fallback_font_list),
79 /* We must use an iso8859-1 font here, or people without $LANG set lose.
80 It's fair to assume that those who do have $LANG set also have the
81 *fontList resource set, or at least know how to deal with this. */
83 (XtPointer) "-*-helvetica-bold-r-*-*-*-120-*-*-*-*-iso8859-1"}
86 {XtNfont, XtCFont, XtRFontStruct, sizeof(XFontStruct *),
87 offset(menu.font), XtRString, (XtPointer) "XtDefaultFont"}
90 /* #### Consider using the same method as for Motif; see the comment in
91 XlwMenuInitialize(). */
92 {XtNfontSet, XtCFontSet, XtRFontSet, sizeof(XFontSet),
93 offset(menu.font_set), XtRString, (XtPointer) "XtDefaultFontSet"}
97 {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
98 offset(menu.foreground), XtRString, (XtPointer) "XtDefaultForeground"}
100 {XtNbuttonForeground, XtCButtonForeground, XtRPixel, sizeof(Pixel),
101 offset(menu.button_foreground), XtRString,
102 (XtPointer) "XtDefaultForeground"}
104 {XtNhighlightForeground, XtCHighlightForeground, XtRPixel,
106 offset(menu.highlight_foreground), XtRString,
107 (XtPointer) "XtDefaultForeground"}
109 {XtNtitleForeground, XtCTitleForeground, XtRPixel, sizeof(Pixel),
110 offset(menu.title_foreground), XtRString,
111 (XtPointer) "XtDefaultForeground"}
113 {XtNmargin, XtCMargin, XtRDimension, sizeof(Dimension),
114 offset(menu.margin), XtRImmediate, (XtPointer) 2}
116 {XmNmarginWidth, XmCMarginWidth, XmRHorizontalDimension,
118 offset(menu.horizontal_margin), XtRImmediate, (XtPointer) 2}
120 {XmNmarginHeight, XmCMarginHeight, XmRVerticalDimension,
122 offset(menu.vertical_margin), XtRImmediate, (XtPointer) 1}
124 {XmNspacing, XmCSpacing, XmRHorizontalDimension, sizeof(Dimension),
125 offset(menu.column_spacing), XtRImmediate, (XtPointer) 4}
127 {XmNindicatorSize, XmCIndicatorSize, XtRDimension, sizeof(Dimension),
128 offset(menu.indicator_size), XtRImmediate, (XtPointer) 0}
131 {XmNshadowThickness, XmCShadowThickness, XmRHorizontalDimension,
132 sizeof(Dimension), offset(menu.shadow_thickness),
133 XtRImmediate, (XtPointer) 2}
136 {XmNshadowThickness, XmCShadowThickness, XtRDimension,
137 sizeof(Dimension), offset(menu.shadow_thickness),
138 XtRImmediate, (XtPointer) 2}
141 {XmNselectColor, XmCSelectColor, XtRPixel, sizeof(Pixel),
142 offset(menu.select_color), XtRImmediate, (XtPointer) - 1}
144 {XmNtopShadowColor, XmCTopShadowColor, XtRPixel, sizeof(Pixel),
145 offset(menu.top_shadow_color), XtRImmediate, (XtPointer) - 1}
147 {XmNbottomShadowColor, XmCBottomShadowColor, XtRPixel, sizeof(Pixel),
148 offset(menu.bottom_shadow_color), XtRImmediate, (XtPointer) - 1}
150 {XmNtopShadowPixmap, XmCTopShadowPixmap, XtRPixmap, sizeof(Pixmap),
151 offset(menu.top_shadow_pixmap), XtRImmediate, (XtPointer) None}
153 {XmNbottomShadowPixmap, XmCBottomShadowPixmap, XtRPixmap,
155 offset(menu.bottom_shadow_pixmap), XtRImmediate, (XtPointer) None}
158 {XtNopen, XtCCallback, XtRCallback, sizeof(XtPointer),
159 offset(menu.open), XtRCallback, (XtPointer) NULL}
161 {XtNselect, XtCCallback, XtRCallback, sizeof(XtPointer),
162 offset(menu.select), XtRCallback, (XtPointer) NULL}
164 {XtNmenu, XtCMenu, XtRPointer, sizeof(XtPointer),
165 offset(menu.contents), XtRImmediate, (XtPointer) NULL}
167 {XtNcursor, XtCCursor, XtRCursor, sizeof(Cursor),
168 offset(menu.cursor_shape), XtRString, (XtPointer) "right_ptr"}
170 {XtNhorizontal, XtCHorizontal, XtRInt, sizeof(int),
171 offset(menu.horizontal), XtRImmediate, (XtPointer) True},
172 {XtNuseBackingStore, XtCUseBackingStore, XtRBoolean, sizeof(Boolean),
173 offset(menu.use_backing_store), XtRImmediate, (XtPointer) False}
175 {XtNbounceDown, XtCBounceDown, XtRBoolean, sizeof(Boolean),
176 offset(menu.bounce_down), XtRImmediate, (XtPointer) True}
178 {XtNresourceLabels, XtCResourceLabels, XtRBoolean, sizeof(Boolean),
179 offset(menu.lookup_labels), XtRImmediate, (XtPointer) False}
184 static Boolean XlwMenuSetValues(Widget current, Widget request, Widget new,
185 ArgList args, Cardinal * num_args);
186 static void XlwMenuRealize(Widget w, Mask * valueMask,
187 XSetWindowAttributes * attributes);
188 static void XlwMenuRedisplay(Widget w, XEvent * ev, Region region);
189 static void XlwMenuResize(Widget w);
190 static void XlwMenuInitialize(Widget request, Widget new, ArgList args,
191 Cardinal * num_args);
192 static void XlwMenuDestroy(Widget w);
193 static void XlwMenuClassInitialize(void);
194 static void Start(Widget w, XEvent * ev, String * params,
195 Cardinal * num_params);
196 static void Drag(Widget w, XEvent * ev, String * params, Cardinal * num_params);
197 static void Select(Widget w, XEvent * ev, String * params,
198 Cardinal * num_params);
201 static XFontStruct *default_font_of_font_list(XmFontList);
204 static XtActionsRec xlwMenuActionsList[] = {
210 #define SuperClass ((CoreWidgetClass)&coreClassRec)
212 XlwMenuClassRec xlwMenuClassRec = {
213 { /* CoreClass fields initialization */
214 (WidgetClass) SuperClass, /* superclass */
215 "XlwMenu", /* class_name */
216 sizeof(XlwMenuRec), /* size */
217 XlwMenuClassInitialize, /* class_initialize */
218 NULL, /* class_part_initialize */
219 FALSE, /* class_inited */
220 XlwMenuInitialize, /* initialize */
221 NULL, /* initialize_hook */
222 XlwMenuRealize, /* realize */
223 xlwMenuActionsList, /* actions */
224 XtNumber(xlwMenuActionsList), /* num_actions */
225 xlwMenuResources, /* resources */
226 XtNumber(xlwMenuResources), /* resource_count */
227 NULLQUARK, /* xrm_class */
228 TRUE, /* compress_motion */
229 XtExposeCompressMaximal, /* compress_exposure */
230 TRUE, /* compress_enterleave */
231 FALSE, /* visible_interest */
232 XlwMenuDestroy, /* destroy */
233 XlwMenuResize, /* resize */
234 XlwMenuRedisplay, /* expose */
235 XlwMenuSetValues, /* set_values */
236 NULL, /* set_values_hook */
237 XtInheritSetValuesAlmost, /* set_values_almost */
238 NULL, /* get_values_hook */
239 NULL, /* #### - should this be set for grabs? accept_focus */
240 XtVersion, /* version */
241 NULL, /* callback_private */
242 xlwMenuTranslations, /* tm_table */
243 XtInheritQueryGeometry, /* query_geometry */
244 XtInheritDisplayAccelerator, /* display_accelerator */
247 , /* XlwMenuClass fields initialization */
254 WidgetClass xlwMenuWidgetClass = (WidgetClass) & xlwMenuClassRec;
256 extern int lw_menu_accelerate;
259 #if 0 /* Apparently not used anywhere */
261 static char *safe_strdup(char *s)
266 result = (char *)malloc(strlen(s) + 1);
275 /* Replacement for XAllocColor() that tries to return the nearest
276 available color if the colormap is full. From FSF Emacs. */
279 allocate_nearest_color(Display * display, Colormap screen_colormap,
282 int status = XAllocColor(display, screen_colormap, color_def);
287 /* If we got to this point, the colormap is full, so we're
288 going to try to get the next closest color.
289 The algorithm used is a least-squares matching, which is
290 what X uses for closest color matching with StaticColor visuals. */
293 unsigned long nearest_delta = ULONG_MAX;
295 int no_cells = XDisplayCells(display, XDefaultScreen(display));
296 /* Don't use alloca here because lwlib doesn't have the
297 necessary configuration information that src does. */
298 XColor *cells = (XColor *) malloc(sizeof(XColor) * no_cells);
300 for (x = 0; x < no_cells; x++)
303 XQueryColors(display, screen_colormap, cells, no_cells);
305 for (nearest = 0, x = 0; x < no_cells; x++) {
306 long dred = (color_def->red >> 8) - (cells[x].red >> 8);
308 (color_def->green >> 8) - (cells[x].green >> 8);
310 (color_def->blue >> 8) - (cells[x].blue >> 8);
311 unsigned long delta =
312 dred * dred + dgreen * dgreen + dblue * dblue;
314 if (delta < nearest_delta) {
316 nearest_delta = delta;
319 color_def->red = cells[nearest].red;
320 color_def->green = cells[nearest].green;
321 color_def->blue = cells[nearest].blue;
323 return XAllocColor(display, screen_colormap, color_def);
327 static void push_new_stack(XlwMenuWidget mw, widget_value * val)
329 if (!mw->menu.new_stack) {
330 mw->menu.new_stack_length = 10;
332 (widget_value **) XtCalloc(mw->menu.new_stack_length,
333 sizeof(widget_value *));
334 } else if (mw->menu.new_depth == mw->menu.new_stack_length) {
335 mw->menu.new_stack_length *= 2;
337 (widget_value **) XtRealloc((char *)mw->menu.new_stack,
338 mw->menu.new_stack_length *
339 sizeof(widget_value *));
341 mw->menu.new_stack[mw->menu.new_depth++] = val;
344 static void pop_new_stack_if_no_contents(XlwMenuWidget mw)
346 if (mw->menu.new_depth &&
347 !mw->menu.new_stack[mw->menu.new_depth - 1]->contents)
348 mw->menu.new_depth -= 1;
351 static void make_old_stack_space(XlwMenuWidget mw, int n)
353 if (!mw->menu.old_stack) {
354 mw->menu.old_stack_length = max(10, n);
356 (widget_value **) XtCalloc(mw->menu.old_stack_length,
357 sizeof(widget_value *));
358 } else if (mw->menu.old_stack_length < n) {
359 while (mw->menu.old_stack_length < n)
360 mw->menu.old_stack_length *= 2;
363 (widget_value **) XtRealloc((char *)mw->menu.old_stack,
364 mw->menu.old_stack_length *
365 sizeof(widget_value *));
370 close_to_reference_time(Widget w, Time reference_time, XEvent * ev)
374 (ev->xbutton.time - reference_time
375 < (Time) XtGetMultiClickTime(XtDisplay(w)));
379 static int string_width(XlwMenuWidget mw,
388 Dimension width, height;
389 XmStringExtent(mw->menu.font_list, s, &width, &height);
394 XmbTextExtents(mw->menu.font_set, s, strlen(s), &ri, &rl);
399 XTextExtents(mw->menu.font, s, strlen(s), &drop, &drop, &drop, &xcs);
401 # endif /* USE_XFONTSET */
405 static char massaged_resource_char[256];
407 static void initialize_massaged_resource_char(void)
410 for (j = 0; j < (int)sizeof(massaged_resource_char); j++) {
411 if ((j >= 'a' && j <= 'z') ||
412 (j >= 'A' && j <= 'Z') ||
413 (j >= '0' && j <= '9') || (j == '_') || (j >= 0xa0))
414 massaged_resource_char[j] = (char)j;
416 massaged_resource_char['_'] = '_';
417 massaged_resource_char['+'] = 'P'; /* Convert C++ to cPP */
418 massaged_resource_char['.'] = '_'; /* Convert Buffers... to buffers___ */
421 static int string_width_u(XlwMenuWidget mw,
430 Dimension width, height;
435 # else /* ! USE_XFONTSET */
447 if (!XmStringGetLtoR(string, XmFONTLIST_DEFAULT_TAG, &chars))
452 charslength = strlen(chars);
453 newchars = (char *)alloca(charslength + 1);
455 for (i = j = 0; chars[i] && (j < charslength); i++)
456 if (chars[i] == '%' && chars[i + 1] == '_')
459 newchars[j++] = chars[i];
463 newstring = XmStringLtoRCreate(newchars, XmFONTLIST_DEFAULT_TAG);
464 XmStringExtent(mw->menu.font_list, newstring, &width, &height);
465 XmStringFree(newstring);
470 XmbTextExtents(mw->menu.font_set, newchars, j, &ri, &rl);
472 # else /* ! USE_XFONTSET */
473 XTextExtents(mw->menu.font, newchars, j, &drop, &drop, &drop, &xcs);
475 # endif /* USE_XFONTSET */
479 static void massage_resource_name(const char *in, char *out)
481 /* Turn a random string into something suitable for using as a resource.
484 "Kill Buffer" -> "killBuffer"
485 "Find File..." -> "findFile___"
486 "Search and Replace..." -> "searchAndReplace___"
487 "C++ Mode Commands" -> "cppModeCommands"
489 Valid characters in a resource NAME component are: a-zA-Z0-9_
492 #ifdef PRINT_XLWMENU_RESOURCE_CONVERSIONS
493 /* Compile with -DPRINT_XLWMENU_RESOURCE_CONVERSIONS to generate a
494 translation file for menu localizations. */
495 char *save_in = in, *save_out = out;
498 Boolean firstp = True;
500 if (*in == '%' && *(in + 1) == '_')
505 if (*in == '%' && *(in + 1) == '%')
507 ch = massaged_resource_char[(unsigned char)*in++];
509 int int_ch = (int)(unsigned char)ch;
511 firstp ? tolower(int_ch) : toupper(int_ch);
514 massaged_resource_char[(unsigned char)
518 if (!*(in - 1)) /* Overshot the NULL byte? */
525 #ifdef PRINT_XLWMENU_RESOURCE_CONVERSIONS
526 printf("! Emacs*XlwMenu.%s.labelString:\t%s\n", save_out, save_in);
527 printf("Emacs*XlwMenu.%s.labelString:\n", save_out);
531 static XtResource nameResource[] = {
532 {"labelString", "LabelString", XtRString, sizeof(String),
536 /* This function searches STRING for parameter inserts of the form:
538 padding is either space (' ') or dash ('-') meaning
539 padding to the left or right of the inserted parameter.
540 In essence, all %1 strings are replaced by VALUE in the return value.
541 The caller is expected to free the return value using XtFree().
542 %% means insert one % (like printf).
543 %1 means insert VALUE.
544 %-1 means insert VALUE followed by one space. The latter is
545 not inserted if VALUE is a zero length string.
547 static char *parameterize_string(const char *string, const char *value)
551 unsigned int done = 0;
556 result = XtMalloc(1);
564 for (ntimes = 1, percent = string;
565 (percent = strchr(percent, '%')); ntimes++)
568 res_left = (ntimes * strlen(value)) + strlen(string) + 3;
569 result = XtMalloc(res_left+1);
570 result[res_left] = result[0] = '\0';
572 while ((percent = strchr(string, '%'))) {
573 unsigned int left_pad;
574 unsigned int right_pad;
576 off_t offset = percent-string;
578 if (percent[1] == '%') { /* it's a real % */
579 assert( offset >=0 &&
580 res_left >= (size_t)(1 + offset));
582 strncat(result, string, 1 + offset);
583 res_left -= 1 + offset;
584 string = &percent[2]; /* after the second '%' */
585 continue; /* with the while() loop */
591 for (p = &percent[1]; /* test *p inside the loop */ ; p++) {
592 if (*p == ' ') { /* left pad */
594 } else if (*p == '-') { /* right pad */
596 } else if (*p == '1') { /* param and terminator */
597 assert( offset >= 0 &&
598 res_left >= (size_t)offset );
600 strncat(result, string, offset);
601 if (value[0] != '\0') {
603 for (i = 0; i < left_pad; i++)
604 strncat(result, " ", res_left --);
605 strncat(result, value, res_left);
606 res_left -= strlen(value);
607 for (i = 0; i < right_pad; i++)
608 strncat(result, " ", res_left--);
610 string = &p[1]; /* after the '1' */
611 done++; /* no need to do old way */
612 break; /* out of for() loop */
613 } else { /* bogus, copy the format as is */
614 /* out of for() loop */
615 off_t remain = 1 + p - string;
616 assert( remain >=0 &&
617 res_left >= (size_t)remain );
618 strncat(result, string, remain);
620 string = (*p ? &p[1] : p);
621 offset = percent - string;
627 /* Copy the tail of the string */
628 strncat(result, string, res_left);
629 res_left -= strlen(string);
631 /* If we have not processed a % string, and we have a value, tail it. */
632 if (!done && value[0] != '\0') {
633 strncat(result, " ", res_left--);
634 strncat(result, value, res_left);
642 static XmString resource_widget_value(XlwMenuWidget mw, widget_value * val)
644 if (!val->toolkit_data) {
645 char *resourced_name = NULL;
646 char *converted_name, *str;
647 XmString complete_name;
648 char massaged_name[1024];
650 if (mw->menu.lookup_labels) {
651 /* Convert value style name into resource style name.
652 eg: "Free Willy" becomes "freeWilly" */
653 massage_resource_name(val->name, massaged_name);
655 /* If we have a value (parameter) see if we can find a "Named"
658 char named_name[1024];
659 int sz = snprintf(named_name, sizeof(named_name),
660 "%sNamed", massaged_name);
661 assert(sz >= 0 && sz < sizeof(named_name));
662 XtGetSubresources((Widget) mw,
663 (XtPointer) & resourced_name,
664 named_name, named_name,
665 nameResource, 1, NULL, 0);
668 /* If nothing yet, try to load from the massaged name. */
669 if (!resourced_name) {
670 XtGetSubresources((Widget) mw,
671 (XtPointer) & resourced_name,
672 massaged_name, massaged_name,
673 nameResource, 1, NULL, 0);
677 /* if (mw->menu.lookup_labels) */
678 /* Still nothing yet, use the name as the value. */
680 resourced_name = val->name;
682 /* Parameterize the string. */
684 parameterize_string(resourced_name, val->value);
686 /* nuke newline characters to prevent menubar screwups */
687 for (str = converted_name; *str; str++) {
692 /* Improve OSF's bottom line. */
693 #if (XmVersion >= 1002)
694 complete_name = XmStringCreateLocalized(converted_name);
696 complete_name = XmStringCreateLtoR(converted_name,
697 XmSTRING_DEFAULT_CHARSET);
699 XtFree(converted_name);
701 val->toolkit_data = complete_name;
702 val->free_toolkit_data = True;
704 return (XmString) val->toolkit_data;
709 /* These two routines should be a separate file..djw */
710 static char *xlw_create_localized_string(Widget w,
712 char **args, unsigned int nargs)
721 (XtPointer) & string,
722 name, name, nameResource, 1, NULL, 0);
727 return parameterize_string(string, arg);
731 xlw_create_localized_xmstring(Widget w,
732 char *name, char **args, unsigned int nargs)
734 char *string = xlw_create_localized_string(w, name, args, nargs);
736 XmStringCreateLtoR(string, XmSTRING_DEFAULT_CHARSET);
744 static char *resource_widget_value(XlwMenuWidget mw, widget_value * val)
746 if (!val->toolkit_data) {
747 char *resourced_name = NULL;
749 char massaged_name[1024];
751 if (mw->menu.lookup_labels) {
752 massage_resource_name(val->name, massaged_name);
754 XtGetSubresources((Widget) mw,
755 (XtPointer) & resourced_name,
756 massaged_name, massaged_name,
757 nameResource, 1, NULL, 0);
760 resourced_name = val->name;
762 complete_name = parameterize_string(resourced_name, val->value);
764 val->toolkit_data = complete_name;
765 /* nuke newline characters to prevent menubar screwups */
766 for (; *complete_name; complete_name++) {
767 if (complete_name[0] == '\n')
768 complete_name[0] = ' ';
770 val->free_toolkit_data = True;
772 return (char *)val->toolkit_data;
777 /* Code for drawing strings. */
778 static void string_draw(XlwMenuWidget mw, Window window, int x, int y, GC gc,
787 XmStringDraw(XtDisplay(mw), window, mw->menu.font_list, string, gc, x, y, 1000, /* ???? width */
788 XmALIGNMENT_BEGINNING, 0, /* ???? layout_direction */
792 XmbDrawString(XtDisplay(mw), window, mw->menu.font_set, gc,
793 x, y + mw->menu.font_ascent, string, strlen(string));
795 XDrawString(XtDisplay(mw), window, gc,
796 x, y + mw->menu.font_ascent, string, strlen(string));
797 # endif /* USE_XFONTSET */
803 string_draw_range(XlwMenuWidget mw,
805 int x, int y, GC gc, char *string, int start, int end)
808 Dimension width, height;
816 newstring = XmStringLtoRCreate(&string[start], XmFONTLIST_DEFAULT_TAG);
817 XmStringDraw(XtDisplay(mw), window, mw->menu.font_list, newstring, gc, x, y, 1000, /* ???? width */
818 XmALIGNMENT_BEGINNING, 0, /* ???? layout_direction */
820 XmStringExtent(mw->menu.font_list, newstring, &width, &height);
821 XmStringFree(newstring);
830 XmbDrawString(XtDisplay(mw), window, mw->menu.font_set, gc,
831 x, y + mw->menu.font_ascent, &string[start], end - start);
832 XmbTextExtents(mw->menu.font_set, &string[start], end - start, &ri,
841 XDrawString(XtDisplay(mw), window, gc,
842 x, y + mw->menu.font_ascent, &string[start], end - start);
843 XTextExtents(mw->menu.font, &string[start], end - start,
844 &drop, &drop, &drop, &xcs);
850 static void string_draw_u(XlwMenuWidget mw, Window window, int x, int y, GC gc,
863 if (!XmStringGetLtoR(string, XmFONTLIST_DEFAULT_TAG, &chars))
868 for (i = 0; chars[i]; ++i) {
869 if (chars[i] == '%' && chars[i + 1] == '_') {
872 x += string_draw_range(mw, window, x, y, gc, chars, s,
874 w = string_draw_range(mw, window, x, y, gc, chars,
877 /* underline next character */
878 XDrawLine(XtDisplay(mw), window, gc, x - 1,
879 y + mw->menu.font_ascent + 1,
880 x + w - 1, y + mw->menu.font_ascent + 1);
886 x += string_draw_range(mw, window, x, y, gc, chars, s, i);
893 binding_draw(XlwMenuWidget mw, Window w, int x, int y, GC gc, char *value)
896 XmString xm_value = XmStringCreateLtoR(value, XmSTRING_DEFAULT_CHARSET);
897 string_draw(mw, w, x, y, gc, xm_value);
898 XmStringFree(xm_value);
900 string_draw(mw, w, x, y, gc, value);
904 /* Low level code for drawing 3-D edges. */
906 shadow_rectangle_draw(Display * dpy,
912 unsigned int height, unsigned int thickness)
921 points[1].x = x + width;
923 points[2].x = x + width - thickness;
924 points[2].y = y + thickness;
926 points[3].y = y + thickness;
927 XFillPolygon(dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
929 points[0].y = y + thickness;
931 points[1].y = y + height;
932 points[2].x = x + thickness;
933 points[2].y = y + height - thickness;
934 points[3].x = x + thickness;
935 points[3].y = y + thickness;
936 XFillPolygon(dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
937 points[0].x = x + width;
939 points[1].x = x + width - thickness;
940 points[1].y = y + thickness;
941 points[2].x = x + width - thickness;
942 points[2].y = y + height - thickness;
943 points[3].x = x + width;
944 points[3].y = y + height - thickness;
945 XFillPolygon(dpy, window, bottom_gc, points, 4, Convex,
948 points[0].y = y + height;
949 points[1].x = x + width;
950 points[1].y = y + height;
951 points[2].x = x + width;
952 points[2].y = y + height - thickness;
953 points[3].x = x + thickness;
954 points[3].y = y + height - thickness;
955 XFillPolygon(dpy, window, bottom_gc, points, 4, Convex,
959 typedef enum e_shadow_type {
960 /* these are Motif compliant */
966 SHADOW_ETCHED_OUT_DASH,
967 SHADOW_ETCHED_IN_DASH,
970 SHADOW_SINGLE_DASHED_LINE,
971 SHADOW_DOUBLE_DASHED_LINE,
973 /* these are all non-Motif */
974 SHADOW_DOUBLE_ETCHED_OUT,
975 SHADOW_DOUBLE_ETCHED_IN,
976 SHADOW_DOUBLE_ETCHED_OUT_DASH,
977 SHADOW_DOUBLE_ETCHED_IN_DASH
981 shadow_draw(XlwMenuWidget mw,
984 unsigned int width, unsigned int height, shadow_type type)
986 Display *dpy = XtDisplay(mw);
989 int thickness = mw->menu.shadow_thickness;
993 Boolean etched = False;
995 switch ((unsigned int)type) {
996 case SHADOW_BACKGROUND:
997 top_gc = bottom_gc = mw->menu.background_gc;
999 case SHADOW_ETCHED_IN:
1000 top_gc = mw->menu.shadow_bottom_gc;
1001 bottom_gc = mw->menu.shadow_top_gc;
1004 case SHADOW_ETCHED_OUT:
1005 top_gc = mw->menu.shadow_top_gc;
1006 bottom_gc = mw->menu.shadow_bottom_gc;
1010 top_gc = mw->menu.shadow_bottom_gc;
1011 bottom_gc = mw->menu.shadow_top_gc;
1015 top_gc = mw->menu.shadow_top_gc;
1016 bottom_gc = mw->menu.shadow_bottom_gc;
1021 unsigned int half = thickness / 2;
1022 shadow_rectangle_draw(dpy,
1027 width - half, height - half,
1029 shadow_rectangle_draw(dpy,
1034 width - half, height - half, half);
1036 shadow_rectangle_draw(dpy,
1040 x, y, width, height, thickness);
1045 arrow_decoration_draw(XlwMenuWidget mw,
1047 int x, int y, unsigned int width, Boolean raised)
1049 Display *dpy = XtDisplay(mw);
1053 int thickness = mw->menu.shadow_thickness;
1056 int length = (int)((double)width * 0.87);
1057 int thick_med = (int)((double)thickness * 1.73);
1060 half_width = width / 2 + 1;
1062 half_width = width / 2;
1064 select_gc = mw->menu.background_gc;
1067 top_gc = mw->menu.shadow_bottom_gc;
1068 bottom_gc = mw->menu.shadow_top_gc;
1070 top_gc = mw->menu.shadow_top_gc;
1071 bottom_gc = mw->menu.shadow_bottom_gc;
1074 /* Fill internal area. We do this first so that the borders have a
1076 points[0].x = x + thickness;
1077 points[0].y = y + thickness;
1078 points[1].x = x + length - thickness;
1079 points[1].y = y + half_width;
1080 points[2].x = x + length - thickness;
1081 points[2].y = y + half_width + thickness;
1082 points[3].x = x + thickness;
1083 points[3].y = y + width - thickness;
1086 window, select_gc, points, 4, Convex, CoordModeOrigin);
1091 points[1].x = x + thickness;
1092 points[1].y = y + thick_med;
1093 points[2].x = x + thickness;
1094 points[2].y = y + width - thick_med;
1096 points[3].y = y + width;
1098 XFillPolygon(dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
1102 points[0].y = y + width;
1103 points[1].x = x + length;
1104 points[1].y = y + half_width;
1105 points[2].x = x + length - (thickness + thickness);
1106 points[2].y = y + half_width;
1107 points[3].x = x + thickness;
1108 points[3].y = y + width - thick_med;
1110 XFillPolygon(dpy, window, bottom_gc, points, 4, Convex,
1116 points[1].x = x + length;
1117 points[1].y = y + half_width;
1118 points[2].x = x + length - (thickness + thickness);
1119 points[2].y = y + half_width;
1120 points[3].x = x + thickness;
1121 points[3].y = y + thick_med;
1123 XFillPolygon(dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
1127 toggle_decoration_draw(XlwMenuWidget mw,
1129 int x, int y, unsigned int width, Boolean set)
1131 Display *dpy = XtDisplay(mw);
1132 int thickness = mw->menu.shadow_thickness;
1134 GC select_gc = mw->menu.select_gc;
1141 /* Fill internal area. */
1148 width - (2 * thickness),
1149 width - (2 * thickness));
1151 shadow_draw(mw, window, x, y, width, width, type);
1155 radio_decoration_draw(XlwMenuWidget mw,
1157 int x, int y, unsigned int width, Boolean enabled)
1159 Display *dpy = XtDisplay(mw);
1162 GC select_gc = mw->menu.select_gc;
1163 int thickness = mw->menu.shadow_thickness;
1173 half_width = width / 2;
1176 top_gc = mw->menu.shadow_bottom_gc;
1177 bottom_gc = mw->menu.shadow_top_gc;
1179 top_gc = mw->menu.shadow_top_gc;
1180 bottom_gc = mw->menu.shadow_bottom_gc;
1184 /* Draw the bottom first, just in case the regions overlap.
1185 The top should cast the longer shadow. */
1186 points[0].x = x; /* left corner */
1187 points[0].y = y + half_width;
1188 points[1].x = x + half_width; /* bottom corner */
1189 points[1].y = y + width;
1190 points[2].x = x + half_width; /* bottom inside corner */
1191 points[2].y = y + width - thickness;
1192 points[3].x = x + thickness; /* left inside corner */
1193 points[3].y = y + half_width;
1195 XFillPolygon(dpy, window, bottom_gc, points, 4, Convex,
1198 points[0].x = x + half_width; /* bottom corner */
1199 points[0].y = y + width;
1200 points[1].x = x + width; /* right corner */
1201 points[1].y = y + half_width;
1202 points[2].x = x + width - thickness; /* right inside corner */
1203 points[2].y = y + half_width;
1204 points[3].x = x + half_width; /* bottom inside corner */
1205 points[3].y = y + width - thickness;
1207 XFillPolygon(dpy, window, bottom_gc, points, 4, Convex,
1210 points[0].x = x; /* left corner */
1211 points[0].y = y + half_width;
1212 points[1].x = x + half_width; /* top corner */
1214 points[2].x = x + half_width; /* top inside corner */
1215 points[2].y = y + thickness;
1216 points[3].x = x + thickness; /* left inside corner */
1217 points[3].y = y + half_width;
1219 XFillPolygon(dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
1221 points[0].x = x + half_width; /* top corner */
1223 points[1].x = x + width; /* right corner */
1224 points[1].y = y + half_width;
1225 points[2].x = x + width - thickness; /* right inside corner */
1226 points[2].y = y + half_width;
1227 points[3].x = x + half_width; /* top inside corner */
1228 points[3].y = y + thickness;
1230 XFillPolygon(dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
1232 /* Draw the bottom first, just in case the regions overlap.
1233 The top should cast the longer shadow. */
1235 points[npoints].x = x; /* left corner */
1236 points[npoints++].y = y + half_width;
1237 points[npoints].x = x + half_width; /* bottom corner */
1238 points[npoints++].y = y + width;
1239 points[npoints].x = x + width; /* right corner */
1240 points[npoints++].y = y + half_width;
1241 points[npoints].x = x + width - thickness; /* right inside corner */
1242 points[npoints++].y = y + half_width;
1243 points[npoints].x = x + half_width; /* bottom inside corner */
1244 points[npoints++].y = y + width - thickness;
1245 points[npoints].x = x + thickness; /* left inside corner */
1246 points[npoints++].y = y + half_width;
1248 XFillPolygon(dpy, window, bottom_gc,
1249 points, npoints, Nonconvex, CoordModeOrigin);
1253 points[npoints].x = x; /* left corner */
1254 points[npoints++].y = y + half_width;
1255 points[npoints].x = x + half_width; /* top corner */
1256 points[npoints++].y = y;
1257 points[npoints].x = x + width; /* right corner */
1258 points[npoints++].y = y + half_width;
1259 points[npoints].x = x + width - thickness; /* right inside corner */
1260 points[npoints++].y = y + half_width;
1261 points[npoints].x = x + half_width; /* top inside corner */
1262 points[npoints++].y = y + thickness;
1263 points[npoints].x = x + thickness; /* left inside corner */
1264 points[npoints++].y = y + half_width;
1266 XFillPolygon(dpy, window, top_gc, points, npoints, Nonconvex,
1270 /* Fill internal area. */
1272 points[0].x = x + thickness;
1273 points[0].y = y + half_width;
1274 points[1].x = x + half_width;
1275 points[1].y = y + thickness;
1276 points[2].x = x + width - thickness;
1277 points[2].y = y + half_width;
1278 points[3].x = x + half_width;
1279 points[3].y = y + width - thickness;
1282 select_gc, points, 4, Convex, CoordModeOrigin);
1287 separator_decoration_draw(XlwMenuWidget mw,
1291 Boolean vertical, shadow_type type)
1293 Display *dpy = XtDisplay(mw);
1296 unsigned int offset = 0;
1297 unsigned int num_separators = 1;
1298 unsigned int top_line_thickness = 0;
1299 unsigned int bottom_line_thickness = 0;
1300 Boolean dashed = False;
1302 switch ((unsigned int)type) {
1303 case SHADOW_NO_LINE: /* nothing to do */
1305 case SHADOW_DOUBLE_LINE:
1307 case SHADOW_SINGLE_LINE:
1308 top_gc = bottom_gc = mw->menu.foreground_gc;
1309 top_line_thickness = 1;
1311 case SHADOW_DOUBLE_DASHED_LINE:
1313 case SHADOW_SINGLE_DASHED_LINE:
1314 top_gc = bottom_gc = mw->menu.foreground_gc;
1315 top_line_thickness = 1;
1318 case SHADOW_DOUBLE_ETCHED_OUT_DASH:
1320 case SHADOW_ETCHED_OUT_DASH:
1321 top_gc = mw->menu.shadow_top_gc;
1322 bottom_gc = mw->menu.shadow_bottom_gc;
1323 top_line_thickness = mw->menu.shadow_thickness / 2;
1324 bottom_line_thickness =
1325 mw->menu.shadow_thickness - top_line_thickness;
1328 case SHADOW_DOUBLE_ETCHED_IN_DASH:
1330 case SHADOW_ETCHED_IN_DASH:
1331 top_gc = mw->menu.shadow_bottom_gc;
1332 bottom_gc = mw->menu.shadow_top_gc;
1333 top_line_thickness = mw->menu.shadow_thickness / 2;
1334 bottom_line_thickness =
1335 mw->menu.shadow_thickness - top_line_thickness;
1338 case SHADOW_DOUBLE_ETCHED_OUT:
1340 case SHADOW_ETCHED_OUT:
1341 top_gc = mw->menu.shadow_top_gc;
1342 bottom_gc = mw->menu.shadow_bottom_gc;
1343 top_line_thickness = mw->menu.shadow_thickness / 2;
1344 bottom_line_thickness =
1345 mw->menu.shadow_thickness - top_line_thickness;
1347 case SHADOW_DOUBLE_ETCHED_IN:
1349 case SHADOW_ETCHED_IN:
1351 top_gc = mw->menu.shadow_bottom_gc;
1352 bottom_gc = mw->menu.shadow_top_gc;
1353 top_line_thickness = mw->menu.shadow_thickness / 2;
1354 bottom_line_thickness =
1355 mw->menu.shadow_thickness - top_line_thickness;
1361 values.line_style = LineOnOffDash;
1362 if (top_line_thickness > 0)
1363 XChangeGC(dpy, top_gc, GCLineStyle, &values);
1364 if (bottom_line_thickness > 0 && bottom_gc != top_gc)
1365 XChangeGC(dpy, bottom_gc, GCLineStyle, &values);
1368 while (num_separators--) {
1370 for (i = 0; i < top_line_thickness; i++)
1371 XDrawLine(dpy, window, top_gc, x, y + i, x + width,
1374 for (i = 0; i < bottom_line_thickness; i++)
1375 XDrawLine(dpy, window, bottom_gc,
1376 x, y + top_line_thickness + offset + i,
1378 y + top_line_thickness + offset + i);
1379 y += (top_line_thickness + offset + bottom_line_thickness + 1);
1384 values.line_style = LineSolid;
1385 if (top_line_thickness > 0)
1386 XChangeGC(dpy, top_gc, GCLineStyle, &values);
1387 if (bottom_line_thickness > 0 && bottom_gc != top_gc)
1388 XChangeGC(dpy, bottom_gc, GCLineStyle, &values);
1392 #define SLOPPY_TYPES 0 /* 0=off, 1=error check, 2=easy to please */
1394 #if SLOPPY_TYPES < 2
1396 static char *wv_types[] = {
1408 static void print_widget_value(widget_value * wv, int just_one, int depth)
1412 for (i = 0; i < depth; i++)
1416 printf("%s(null widget value pointer)\n", d);
1419 printf("%stype: %s\n", d, wv_types[wv->type]);
1421 printf("%sname: %s\n", d, (wv->name ? wv->name : "(null)"));
1424 printf("%sname: %s\n", d, wv->name);
1427 printf("%svalue: %s\n", d, wv->value);
1429 printf("%skey: %s\n", d, wv->key);
1430 printf("%senabled: %d\n", d, wv->enabled);
1432 printf("\n%scontents: \n", d);
1433 print_widget_value(wv->contents, 0, depth + 5);
1435 if (!just_one && wv->next) {
1437 print_widget_value(wv->next, 0, depth);
1440 #endif /* SLOPPY_TYPES < 2 */
1442 static Boolean all_dashes_p(char *s)
1445 if (!s || s[0] == '\0')
1447 for (p = s; *p == '-'; p++) ;
1449 if (*p == '!' || *p == '\0')
1453 #endif /* SLOPPY_TYPES */
1455 static widget_value_type menu_item_type(widget_value * val)
1457 if (val->type != UNSPECIFIED_TYPE)
1460 else if (all_dashes_p(val->name))
1461 return SEPARATOR_TYPE;
1462 else if (val->name && val->name[0] == '\0') /* push right */
1463 return PUSHRIGHT_TYPE;
1464 else if (val->contents) /* cascade */
1465 return CASCADE_TYPE;
1466 else if (val->call_data) /* push button */
1473 return UNSPECIFIED_TYPE; /* Not reached */
1478 label_button_size(XlwMenuWidget mw,
1481 unsigned int *toggle_width,
1482 unsigned int *label_width,
1483 unsigned int *bindings_width, unsigned int *height)
1485 *height = (mw->menu.font_ascent + mw->menu.font_descent +
1486 2 * mw->menu.vertical_margin +
1487 2 * mw->menu.shadow_thickness);
1488 /* no left column decoration */
1489 *toggle_width = mw->menu.horizontal_margin + mw->menu.shadow_thickness;
1491 *label_width = string_width_u(mw, resource_widget_value(mw, val));
1493 mw->menu.horizontal_margin + mw->menu.shadow_thickness;
1497 label_button_draw(XlwMenuWidget mw,
1500 Boolean highlighted,
1504 unsigned int height,
1505 unsigned int label_offset, unsigned int binding_tab)
1507 int y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin;
1512 mw->menu.shadow_thickness + mw->menu.horizontal_margin;
1514 if (highlighted && (in_menubar || val->contents))
1515 gc = mw->menu.highlight_gc;
1516 else if (in_menubar || val->contents)
1517 gc = mw->menu.foreground_gc;
1519 gc = mw->menu.title_gc;
1521 /* Draw the label string. */
1524 x + label_offset, y + y_offset,
1525 gc, resource_widget_value(mw, val));
1529 push_button_size(XlwMenuWidget mw,
1532 unsigned int *toggle_width,
1533 unsigned int *label_width,
1534 unsigned int *bindings_width, unsigned int *height)
1537 label_button_size(mw, val, in_menubar,
1538 toggle_width, label_width, bindings_width, height);
1540 /* key bindings to display? */
1541 if (!in_menubar && val->key) {
1545 XmStringCreateLtoR(val->key, XmSTRING_DEFAULT_CHARSET);
1546 w = string_width(mw, key);
1549 char *key = val->key;
1550 w = string_width(mw, key);
1552 *bindings_width += w + mw->menu.column_spacing;
1557 push_button_draw(XlwMenuWidget mw,
1560 Boolean highlighted,
1564 unsigned int height,
1565 unsigned int label_offset, unsigned int binding_offset)
1567 int y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin;
1570 Boolean menu_pb = in_menubar && (menu_item_type(val) == BUTTON_TYPE);
1572 /* Draw the label string. */
1575 mw->menu.shadow_thickness + mw->menu.horizontal_margin;
1579 gc = mw->menu.highlight_gc;
1581 gc = mw->menu.inactive_gc;
1582 } else if (menu_pb) {
1584 gc = mw->menu.button_gc;
1586 gc = mw->menu.inactive_button_gc;
1589 gc = mw->menu.foreground_gc;
1591 gc = mw->menu.inactive_gc;
1596 x + label_offset, y + y_offset,
1597 gc, resource_widget_value(mw, val));
1599 /* Draw the keybindings */
1601 if (!binding_offset) {
1602 unsigned int s_width =
1603 string_width(mw, resource_widget_value(mw, val));
1605 label_offset + s_width + mw->menu.shadow_thickness;
1607 binding_draw(mw, window,
1608 x + binding_offset + mw->menu.column_spacing,
1609 y + y_offset, gc, val->key);
1612 /* Draw the shadow */
1619 selected ? SHADOW_ETCHED_OUT : SHADOW_ETCHED_IN);
1624 type = SHADOW_BACKGROUND;
1627 shadow_draw(mw, window, x, y, width, height, type);
1630 static unsigned int arrow_decoration_height(XlwMenuWidget mw)
1632 int result = (mw->menu.font_ascent + mw->menu.font_descent) / 2;
1634 result += 2 * mw->menu.shadow_thickness;
1636 if (result > (mw->menu.font_ascent + mw->menu.font_descent))
1637 result = mw->menu.font_ascent + mw->menu.font_descent;
1643 cascade_button_size(XlwMenuWidget mw,
1646 unsigned int *toggle_width,
1647 unsigned int *label_width,
1648 unsigned int *arrow_width, unsigned int *height)
1651 label_button_size(mw, val, in_menubar,
1652 toggle_width, label_width, arrow_width, height);
1653 /* we have a pull aside arrow */
1656 arrow_decoration_height(mw) + mw->menu.column_spacing;
1661 cascade_button_draw(XlwMenuWidget mw,
1664 Boolean highlighted,
1668 unsigned int height,
1669 unsigned int label_offset, unsigned int binding_offset)
1673 /* Draw the label string. */
1674 label_button_draw(mw, val, in_menubar, highlighted,
1675 window, x, y, width, height, label_offset,
1678 /* Draw the pull aside arrow */
1679 if (!in_menubar && val->contents) {
1681 unsigned int arrow_height = arrow_decoration_height(mw);
1684 mw->menu.shadow_thickness + mw->menu.vertical_margin +
1685 (mw->menu.font_ascent + mw->menu.font_descent -
1688 if (!binding_offset) {
1689 unsigned int s_width =
1690 string_width(mw, resource_widget_value(mw, val));
1693 label_offset = mw->menu.shadow_thickness +
1694 mw->menu.horizontal_margin;
1697 label_offset + s_width + mw->menu.shadow_thickness;
1700 arrow_decoration_draw(mw,
1702 x + binding_offset +
1703 mw->menu.column_spacing, y + y_offset,
1704 arrow_height, highlighted);
1707 /* Draw the shadow */
1711 type = SHADOW_BACKGROUND;
1713 shadow_draw(mw, window, x, y, width, height, type);
1716 static unsigned int toggle_decoration_height(XlwMenuWidget mw)
1719 if (mw->menu.indicator_size > 0)
1720 rv = mw->menu.indicator_size;
1722 rv = mw->menu.font_ascent;
1724 if (rv > (mw->menu.font_ascent + mw->menu.font_descent))
1725 rv = mw->menu.font_ascent + mw->menu.font_descent;
1727 /* radio button can't be smaller than its border or a filling
1728 error will occur. */
1729 if (rv < 2 * mw->menu.shadow_thickness)
1730 rv = 2 * mw->menu.shadow_thickness;
1736 toggle_button_size(XlwMenuWidget mw,
1739 unsigned int *toggle_width,
1740 unsigned int *label_width,
1741 unsigned int *bindings_width, unsigned int *height)
1744 push_button_size(mw, val, in_menubar,
1745 toggle_width, label_width, bindings_width, height);
1746 /* we have a toggle */
1747 *toggle_width += toggle_decoration_height(mw) + mw->menu.column_spacing;
1751 toggle_button_draw(XlwMenuWidget mw,
1754 Boolean highlighted,
1758 unsigned int height,
1759 unsigned int label_tab, unsigned int binding_tab)
1763 unsigned int t_height = toggle_decoration_height(mw);
1765 /* Draw a toggle. */
1766 x_offset = mw->menu.shadow_thickness + mw->menu.horizontal_margin;
1767 y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin;
1769 (mw->menu.font_ascent + mw->menu.font_descent - t_height) / 2;
1771 toggle_decoration_draw(mw, window, x + x_offset, y + y_offset,
1772 t_height, val->selected);
1774 /* Draw the pushbutton parts. */
1775 push_button_draw(mw, val, in_menubar, highlighted, window, x, y, width,
1776 height, label_tab, binding_tab);
1779 static unsigned int radio_decoration_height(XlwMenuWidget mw)
1781 return toggle_decoration_height(mw);
1785 radio_button_draw(XlwMenuWidget mw,
1788 Boolean highlighted,
1792 unsigned int height,
1793 unsigned int label_tab, unsigned int binding_tab)
1797 unsigned int r_height = radio_decoration_height(mw);
1799 /* Draw a toggle. */
1800 x_offset = mw->menu.shadow_thickness + mw->menu.horizontal_margin;
1801 y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin;
1803 (mw->menu.font_ascent + mw->menu.font_descent - r_height) / 2;
1805 radio_decoration_draw(mw, window, x + x_offset, y + y_offset, r_height,
1808 /* Draw the pushbutton parts. */
1809 push_button_draw(mw, val, in_menubar, highlighted, window, x, y, width,
1810 height, label_tab, binding_tab);
1813 static struct _shadow_names {
1816 } shadow_names[] = {
1819 "singleLine", SHADOW_SINGLE_LINE}, {
1820 "doubleLine", SHADOW_DOUBLE_LINE}, {
1821 "singleDashedLine", SHADOW_SINGLE_DASHED_LINE}, {
1822 "doubleDashedLine", SHADOW_DOUBLE_DASHED_LINE}, {
1823 "noLine", SHADOW_NO_LINE}, {
1824 "shadowEtchedIn", SHADOW_ETCHED_IN}, {
1825 "shadowEtchedOut", SHADOW_ETCHED_OUT}, {
1826 "shadowEtchedInDash", SHADOW_ETCHED_IN_DASH}, {
1827 "shadowEtchedOutDash", SHADOW_ETCHED_OUT_DASH},
1830 "shadowDoubleEtchedIn", SHADOW_DOUBLE_ETCHED_IN}, {
1831 "shadowDoubleEtchedOut", SHADOW_DOUBLE_ETCHED_OUT}, {
1832 "shadowDoubleEtchedInDash", SHADOW_DOUBLE_ETCHED_IN_DASH}, {
1833 "shadowDoubleEtchedOutDash", SHADOW_DOUBLE_ETCHED_OUT_DASH}
1836 static shadow_type separator_type(char *name)
1840 for (i = 0; i < (int)(XtNumber(shadow_names)); i++) {
1841 if (strcmp(name, shadow_names[i].name) == 0)
1842 return shadow_names[i].type;
1845 return SHADOW_BACKGROUND;
1849 separator_decoration_height(XlwMenuWidget mw, widget_value * val)
1851 shadow_type st = separator_type(val->value);
1852 switch ((unsigned int)st) {
1853 case SHADOW_NO_LINE:
1854 case SHADOW_SINGLE_LINE:
1855 case SHADOW_SINGLE_DASHED_LINE:
1857 case SHADOW_DOUBLE_LINE:
1858 case SHADOW_DOUBLE_DASHED_LINE:
1860 case SHADOW_DOUBLE_ETCHED_OUT:
1861 case SHADOW_DOUBLE_ETCHED_IN:
1862 case SHADOW_DOUBLE_ETCHED_OUT_DASH:
1863 case SHADOW_DOUBLE_ETCHED_IN_DASH:
1864 return (1 + 2 * mw->menu.shadow_thickness);
1865 case SHADOW_ETCHED_OUT:
1866 case SHADOW_ETCHED_IN:
1868 return mw->menu.shadow_thickness;
1873 separator_size(XlwMenuWidget mw,
1876 unsigned int *toggle_width,
1877 unsigned int *label_width,
1878 unsigned int *rest_width, unsigned int *height)
1880 *height = separator_decoration_height(mw, val);
1882 *toggle_width = *rest_width = 0;
1886 separator_draw(XlwMenuWidget mw,
1889 Boolean highlighted,
1893 unsigned int height,
1894 unsigned int label_tab, unsigned int binding_tab)
1896 unsigned int sep_width;
1903 separator_decoration_draw(mw,
1908 in_menubar, separator_type(val->value));
1912 pushright_size(XlwMenuWidget mw,
1915 unsigned int *toggle_width,
1916 unsigned int *label_width,
1917 unsigned int *rest_width, unsigned int *height)
1919 *height = *label_width = *toggle_width = *rest_width = 0;
1923 size_menu_item(XlwMenuWidget mw,
1926 unsigned int *toggle_width,
1927 unsigned int *label_width,
1928 unsigned int *rest_width, unsigned int *height)
1930 void (*function_ptr) (XlwMenuWidget _mw,
1931 widget_value * _val,
1932 Boolean _in_menubar,
1933 unsigned int *_toggle_width,
1934 unsigned int *_label_width,
1935 unsigned int *_rest_width, unsigned int *_height);
1936 widget_value_type mt = menu_item_type(val);
1938 switch ((unsigned int)mt) {
1941 function_ptr = toggle_button_size;
1943 case SEPARATOR_TYPE:
1944 function_ptr = separator_size;
1946 case INCREMENTAL_TYPE:
1948 function_ptr = cascade_button_size;
1951 function_ptr = push_button_size;
1953 case PUSHRIGHT_TYPE:
1954 function_ptr = pushright_size;
1958 function_ptr = label_button_size;
1962 (*function_ptr) (mw,
1965 toggle_width, label_width, rest_width, height);
1969 display_menu_item(XlwMenuWidget mw,
1973 Boolean highlighted, Boolean horizontal, Boolean just_compute)
1975 int x = where->x /* + mw->menu.shadow_thickness */ ;
1976 int y = where->y /* + mw->menu.shadow_thickness */ ;
1977 unsigned int toggle_width;
1978 unsigned int label_width;
1979 unsigned int binding_width;
1981 unsigned int height;
1982 unsigned int label_tab;
1983 unsigned int binding_tab;
1984 void (*function_ptr) (XlwMenuWidget _mw,
1985 widget_value * _val,
1986 Boolean _in_menubar,
1987 Boolean _highlighted,
1990 unsigned int _width,
1991 unsigned int _height,
1992 unsigned int _label_tab,
1993 unsigned int _binding_tab);
1994 widget_value_type mt;
1996 size_menu_item(mw, val, horizontal,
1997 &toggle_width, &label_width, &binding_width, &height);
2000 width = toggle_width + label_width + binding_width;
2001 height = ws->height - 2 * mw->menu.shadow_thickness;
2003 width = ws->width - 2 * mw->menu.shadow_thickness;
2004 toggle_width = ws->toggle_width;
2005 label_width = ws->label_width;
2014 label_tab = toggle_width;
2015 binding_tab = toggle_width + label_width;
2017 mt = menu_item_type(val);
2018 switch ((unsigned int)mt) {
2020 function_ptr = toggle_button_draw;
2023 function_ptr = radio_button_draw;
2025 case SEPARATOR_TYPE:
2026 function_ptr = separator_draw;
2028 case INCREMENTAL_TYPE:
2030 function_ptr = cascade_button_draw;
2033 function_ptr = push_button_draw;
2036 function_ptr = label_button_draw;
2038 default: /* do no drawing */
2042 (*function_ptr) (mw,
2047 x, y, width, height, label_tab, binding_tab);
2050 static void size_menu(XlwMenuWidget mw, int level)
2052 unsigned int toggle_width;
2053 unsigned int label_width;
2054 unsigned int rest_width;
2055 unsigned int height;
2056 unsigned int max_toggle_width = 0;
2057 unsigned int max_label_width = 0;
2058 unsigned int max_rest_width = 0;
2059 unsigned int max_height = 0;
2060 int horizontal_p = mw->menu.horizontal && (level == 0);
2064 if (level >= mw->menu.old_depth)
2067 ws = &mw->menu.windows[level];
2069 for (val = mw->menu.old_stack[level]->contents; val; val = val->next) {
2074 &label_width, &rest_width, &height);
2077 toggle_width + label_width + rest_width;
2078 if (height > max_height)
2079 max_height = height;
2081 if (max_toggle_width < toggle_width)
2082 max_toggle_width = toggle_width;
2083 if (max_label_width < label_width)
2084 max_label_width = label_width;
2085 if (max_rest_width < rest_width)
2086 max_rest_width = rest_width;
2087 max_height += height;
2091 ws->height = max_height;
2092 ws->width = max_label_width + max_rest_width + max_toggle_width;
2093 ws->toggle_width = max_toggle_width;
2094 ws->label_width = max_label_width;
2096 ws->width += 2 * mw->menu.shadow_thickness;
2097 ws->height += 2 * mw->menu.shadow_thickness;
2101 display_menu(XlwMenuWidget mw, int level, Boolean just_compute_p,
2102 XPoint * highlighted_pos, XPoint * hit, widget_value ** hit_return,
2103 widget_value * this, widget_value * that)
2106 widget_value *following_item;
2109 int horizontal_p = mw->menu.horizontal && (level == 0);
2111 int just_compute_this_one_p;
2113 if (level >= mw->menu.old_depth)
2116 if (level < mw->menu.old_depth - 1)
2117 following_item = mw->menu.old_stack[level + 1];
2119 if (lw_menu_accelerate
2120 && level == mw->menu.old_depth - 1
2121 && mw->menu.old_stack[level]->type == CASCADE_TYPE)
2122 just_compute_p = True;
2123 following_item = NULL;
2126 #if SLOPPY_TYPES == 1
2127 puts("===================================================================");
2128 print_widget_value(following_item, 1, 0);
2134 where.x = mw->menu.shadow_thickness;
2135 where.y = mw->menu.shadow_thickness;
2137 ws = &mw->menu.windows[level];
2138 for (val = mw->menu.old_stack[level]->contents; val; val = val->next) {
2141 highlighted_p = (val == following_item);
2142 /* If this is the partition (the dummy item which says that menus
2143 after this should be flushright) then figure out how big the
2144 following items are. This means we walk down the tail of the
2145 list twice, but that's no big deal - it's short.
2147 if (horizontal_p && (menu_item_type(val) == PUSHRIGHT_TYPE)) {
2149 XPoint flushright_size;
2151 flushright_size.x = 0;
2152 flushright_size.y = 0;
2153 for (rest = val; rest; rest = rest->next)
2154 display_menu_item(mw, rest, ws,
2156 highlighted_p, horizontal_p,
2159 ws->width - (flushright_size.x +
2160 mw->menu.shadow_thickness);
2161 if (new_x > where.x)
2163 /* We know what we need; don't draw this item. */
2167 if (highlighted_p && highlighted_pos) {
2169 highlighted_pos->x = where.x;
2171 highlighted_pos->y = where.y;
2174 just_compute_this_one_p =
2175 just_compute_p || ((this || that) && val != this
2180 display_menu_item(mw, val, ws, &where, highlighted_p,
2181 horizontal_p, just_compute_this_one_p);
2183 if (highlighted_p && highlighted_pos) {
2185 highlighted_pos->y = ws->height;
2187 highlighted_pos->x = ws->width;
2190 if (hit && !*hit_return) {
2191 if (horizontal_p && hit->x > start.x
2192 && hit->x <= where.x)
2194 else if (!horizontal_p && hit->y > start.y
2195 && hit->y <= where.y)
2200 where.y = mw->menu.shadow_thickness;
2202 where.x = mw->menu.shadow_thickness;
2205 /* Draw slab edges around menu */
2206 if (!just_compute_p)
2207 shadow_draw(mw, ws->window, 0, 0, ws->width, ws->height,
2212 static void set_new_state(XlwMenuWidget mw, widget_value * val, int level)
2216 mw->menu.new_depth = 0;
2217 for (i = 0; i < level; i++)
2218 push_new_stack(mw, mw->menu.old_stack[i]);
2220 push_new_stack(mw, val);
2223 static void make_windows_if_needed(XlwMenuWidget mw, int n)
2227 XSetWindowAttributes xswa;
2232 window_state *windows;
2235 if (mw->menu.windows_length >= n)
2238 root = RootWindowOfScreen(XtScreen(mw));
2239 /* grab the visual and depth from the nearest shell ancestor */
2240 visual = CopyFromParent;
2241 depth = CopyFromParent;
2243 while (visual == CopyFromParent && p) {
2245 visual = ((ShellWidget) p)->shell.visual;
2246 depth = p->core.depth;
2251 xswa.save_under = True;
2252 xswa.override_redirect = True;
2253 xswa.background_pixel = mw->core.background_pixel;
2254 xswa.border_pixel = mw->core.border_pixel;
2255 xswa.event_mask = (ExposureMask | ButtonMotionMask
2256 | ButtonReleaseMask | ButtonPressMask);
2257 xswa.cursor = mw->menu.cursor_shape;
2258 xswa.colormap = mw->core.colormap;
2259 mask = CWSaveUnder | CWOverrideRedirect | CWBackPixel | CWBorderPixel
2260 | CWEventMask | CWCursor | CWColormap;
2262 if (mw->menu.use_backing_store) {
2263 xswa.backing_store = Always;
2264 mask |= CWBackingStore;
2267 if (!mw->menu.windows) {
2269 (window_state *) XtMalloc(n * sizeof(window_state));
2273 (window_state *) XtRealloc((char *)mw->menu.windows,
2274 n * sizeof(window_state));
2275 start_at = mw->menu.windows_length;
2277 mw->menu.windows_length = n;
2279 windows = mw->menu.windows;
2281 for (i = start_at; i < n; i++) {
2284 windows[i].width = 1;
2285 windows[i].height = 1;
2287 XCreateWindow(XtDisplay(mw),
2290 0, depth, CopyFromParent, visual, mask,
2295 /* Make the window fit in the screen */
2297 fit_to_screen(XlwMenuWidget mw, window_state * ws, window_state * previous_ws,
2298 Boolean horizontal_p)
2300 int screen_width = WidthOfScreen(XtScreen(mw));
2301 int screen_height = HeightOfScreen(XtScreen(mw));
2305 else if ((int)(ws->x + ws->width) > screen_width) {
2307 ws->x = previous_ws->x - ws->width;
2309 ws->x = screen_width - ws->width;
2311 /* This check is to make sure we cut off the right side
2312 instead of the left side if the menu is wider than the
2320 else if ((int)(ws->y + ws->height) > screen_height) {
2322 /* A pulldown must either be entirely above or below the menubar.
2323 If we're here, the pulldown doesn't fit below the menubar, so
2324 let's determine if it will fit above the menubar.
2325 Only put it above if there is more room above than below.
2326 Note shadow_thickness offset to allow for slab surround.
2328 if (ws->y > (screen_height / 2))
2330 previous_ws->y - ws->height +
2331 mw->menu.shadow_thickness;
2333 ws->y = screen_height - ws->height;
2334 /* if it's taller than the screen, display the topmost part
2335 that will fit, beginning at the top of the screen. */
2342 /* Updates old_stack from new_stack and redisplays. */
2343 static void remap_menubar(XlwMenuWidget mw)
2347 XPoint selection_position;
2348 int old_depth = mw->menu.old_depth;
2349 int new_depth = mw->menu.new_depth;
2350 widget_value **old_stack;
2351 widget_value **new_stack;
2352 window_state *windows;
2353 widget_value *old_selection;
2354 widget_value *new_selection;
2356 /* Check that enough windows and old_stack are ready. */
2357 make_windows_if_needed(mw, new_depth);
2358 make_old_stack_space(mw, new_depth);
2359 windows = mw->menu.windows;
2360 old_stack = mw->menu.old_stack;
2361 new_stack = mw->menu.new_stack;
2363 /* compute the last identical different entry */
2364 for (i = 1; i < old_depth && i < new_depth; i++)
2365 if (old_stack[i] != new_stack[i])
2369 if (lw_menu_accelerate
2371 && last_same == old_depth - 1 && old_stack[last_same]->contents)
2374 /* Memorize the previously selected item to be able to refresh it */
2376 last_same + 1 < old_depth ? old_stack[last_same + 1] : NULL;
2378 last_same + 1 < new_depth ? new_stack[last_same + 1] : NULL;
2380 /* updates old_state from new_state. It has to be done now because
2381 display_menu (called below) uses the old_stack to know what to display. */
2382 for (i = last_same + 1; i < new_depth; i++)
2383 old_stack[i] = new_stack[i];
2385 mw->menu.old_depth = new_depth;
2387 /* refresh the last selection */
2388 selection_position.x = 0;
2389 selection_position.y = 0;
2390 display_menu(mw, last_same, new_selection == old_selection,
2391 &selection_position, NULL, NULL, old_selection,
2394 /* Now popup the new menus */
2395 for (i = last_same + 1; i < new_depth && new_stack[i]->contents; i++) {
2396 window_state *previous_ws = &windows[i - 1];
2397 window_state *ws = &windows[i];
2399 if (lw_menu_accelerate && i == new_depth - 1)
2402 ws->x = previous_ws->x + selection_position.x;
2403 ws->y = previous_ws->y + selection_position.y;
2405 /* take into account the slab around the new menu */
2406 ws->y -= mw->menu.shadow_thickness;
2409 widget_value *val = mw->menu.old_stack[i];
2410 if (val->contents->type == INCREMENTAL_TYPE) {
2411 /* okay, we're now doing a lisp callback to incrementally generate
2412 more of the menu. */
2413 XtCallCallbackList((Widget) mw,
2415 (XtPointer) val->contents);
2421 fit_to_screen(mw, ws, previous_ws, mw->menu.horizontal
2424 XClearWindow(XtDisplay(mw), ws->window);
2425 XMoveResizeWindow(XtDisplay(mw), ws->window, ws->x, ws->y,
2426 ws->width, ws->height);
2427 XMapRaised(XtDisplay(mw), ws->window);
2428 display_menu(mw, i, False, &selection_position, NULL, NULL,
2432 /* unmap the menus that popped down */
2434 last_same = new_depth;
2435 if (lw_menu_accelerate
2436 && last_same > 1 && new_stack[last_same - 1]->contents)
2439 for (i = last_same - 1; i < old_depth; i++)
2440 if (i >= last_same || !new_stack[i]->contents)
2441 XUnmapWindow(XtDisplay(mw), windows[i].window);
2445 motion_event_is_in_menu(XlwMenuWidget mw, XMotionEvent * ev, int level,
2446 XPoint * relative_pos)
2448 window_state *ws = &mw->menu.windows[level];
2449 int x = level == 0 ? ws->x : ws->x + mw->menu.shadow_thickness;
2450 int y = level == 0 ? ws->y : ws->y + mw->menu.shadow_thickness;
2451 relative_pos->x = ev->x_root - x;
2452 relative_pos->y = ev->y_root - y;
2453 return (x < ev->x_root && ev->x_root < (int)(x + ws->width) &&
2454 y < ev->y_root && ev->y_root < (int)(y + ws->height));
2458 map_event_to_widget_value(XlwMenuWidget mw, XMotionEvent * ev,
2459 widget_value ** val_ptr, int *level,
2460 Boolean * inside_menu)
2463 XPoint relative_pos;
2467 *inside_menu = False;
2469 /* Find the window */
2471 for (i = mw->menu.old_depth - 1; i >= 0; i--)
2473 for (i = 0; i <= mw->menu.old_depth - 1; i++)
2476 ws = &mw->menu.windows[i];
2477 if (ws && motion_event_is_in_menu(mw, ev, i, &relative_pos)) {
2478 *inside_menu = True; /* special logic for menubar below... */
2479 if ((ev->type == ButtonPress) || (ev->state != 0)) {
2480 display_menu(mw, i, True, NULL, &relative_pos,
2481 val_ptr, NULL, NULL);
2484 *inside_menu = True;
2486 } else if (mw->menu.horizontal || i == 0) {
2487 /* if we're clicking on empty part of the menubar, then
2488 unpost the stay-up menu */
2489 *inside_menu = False;
2498 static void make_drawing_gcs(XlwMenuWidget mw)
2501 unsigned long flags = (GCFont | GCForeground | GCBackground);
2504 xgcv.font = default_font_of_font_list(mw->menu.font_list)->fid;
2506 xgcv.font = mw->menu.font->fid;
2509 xgcv.foreground = mw->core.background_pixel;
2510 xgcv.background = mw->menu.foreground;
2511 mw->menu.background_gc = XtGetGC((Widget) mw, flags, &xgcv);
2513 xgcv.foreground = mw->menu.foreground;
2514 xgcv.background = mw->core.background_pixel;
2515 mw->menu.foreground_gc = XtGetGC((Widget) mw, flags, &xgcv);
2517 if (mw->menu.select_color != (Pixel) - 1) {
2518 xgcv.foreground = mw->menu.select_color;
2520 Display *dpy = XtDisplay(mw);
2521 if (CellsOfScreen(DefaultScreenOfDisplay(dpy)) <= 2) { /* mono */
2522 xgcv.foreground = mw->menu.foreground;
2523 } else { /* color */
2525 Colormap cmap = mw->core.colormap;
2526 xcolor.pixel = mw->core.background_pixel;
2527 XQueryColor(dpy, cmap, &xcolor);
2528 xcolor.red = (xcolor.red * 17) / 20;
2529 xcolor.green = (xcolor.green * 17) / 20;
2530 xcolor.blue = (xcolor.blue * 17) / 20;
2531 if (allocate_nearest_color(dpy, cmap, &xcolor))
2532 xgcv.foreground = xcolor.pixel;
2535 xgcv.background = mw->core.background_pixel;
2536 mw->menu.select_gc = XtGetGC((Widget) mw, flags, &xgcv);
2538 xgcv.foreground = mw->menu.foreground;
2539 xgcv.background = mw->core.background_pixel;
2540 xgcv.fill_style = FillStippled;
2541 xgcv.stipple = mw->menu.gray_pixmap;
2542 mw->menu.inactive_gc = XtGetGC((Widget) mw,
2543 (flags | GCFillStyle | GCStipple),
2546 xgcv.foreground = mw->menu.highlight_foreground;
2547 xgcv.background = mw->core.background_pixel;
2548 mw->menu.highlight_gc = XtGetGC((Widget) mw, flags, &xgcv);
2550 xgcv.foreground = mw->menu.title_foreground;
2551 xgcv.background = mw->core.background_pixel;
2552 mw->menu.title_gc = XtGetGC((Widget) mw, flags, &xgcv);
2554 xgcv.foreground = mw->menu.button_foreground;
2555 xgcv.background = mw->core.background_pixel;
2556 mw->menu.button_gc = XtGetGC((Widget) mw, flags, &xgcv);
2558 xgcv.fill_style = FillStippled;
2559 xgcv.stipple = mw->menu.gray_pixmap;
2560 mw->menu.inactive_button_gc = XtGetGC((Widget) mw,
2561 (flags | GCFillStyle | GCStipple),
2565 static void release_drawing_gcs(XlwMenuWidget mw)
2567 XtReleaseGC((Widget) mw, mw->menu.foreground_gc);
2568 XtReleaseGC((Widget) mw, mw->menu.button_gc);
2569 XtReleaseGC((Widget) mw, mw->menu.highlight_gc);
2570 XtReleaseGC((Widget) mw, mw->menu.title_gc);
2571 XtReleaseGC((Widget) mw, mw->menu.inactive_gc);
2572 XtReleaseGC((Widget) mw, mw->menu.inactive_button_gc);
2573 XtReleaseGC((Widget) mw, mw->menu.background_gc);
2574 XtReleaseGC((Widget) mw, mw->menu.select_gc);
2575 /* let's get some segvs if we try to use these... */
2576 mw->menu.foreground_gc = (GC) - 1;
2577 mw->menu.button_gc = (GC) - 1;
2578 mw->menu.highlight_gc = (GC) - 1;
2579 mw->menu.title_gc = (GC) - 1;
2580 mw->menu.inactive_gc = (GC) - 1;
2581 mw->menu.inactive_button_gc = (GC) - 1;
2582 mw->menu.background_gc = (GC) - 1;
2583 mw->menu.select_gc = (GC) - 1;
2586 #define MINL(x,y) ((((unsigned long) (x)) < ((unsigned long) (y))) \
2587 ? ((unsigned long) (x)) : ((unsigned long) (y)))
2589 static void make_shadow_gcs(XlwMenuWidget mw)
2592 unsigned long pm = 0;
2593 Display *dpy = XtDisplay((Widget) mw);
2594 Colormap cmap = mw->core.colormap;
2596 int top_frobbed = 0, bottom_frobbed = 0;
2598 if (mw->menu.top_shadow_color == (Pixel) (-1))
2599 mw->menu.top_shadow_color = mw->core.background_pixel;
2600 if (mw->menu.bottom_shadow_color == (Pixel) (-1))
2601 mw->menu.bottom_shadow_color = mw->menu.foreground;
2603 if (mw->menu.top_shadow_color == mw->core.background_pixel ||
2604 mw->menu.top_shadow_color == mw->menu.foreground) {
2605 topc.pixel = mw->core.background_pixel;
2606 XQueryColor(dpy, cmap, &topc);
2607 /* don't overflow/wrap! */
2608 topc.red = MINL(65535, topc.red * 1.2);
2609 topc.green = MINL(65535, topc.green * 1.2);
2610 topc.blue = MINL(65535, topc.blue * 1.2);
2611 if (allocate_nearest_color(dpy, cmap, &topc)) {
2612 if (topc.pixel == mw->core.background_pixel) {
2613 XFreeColors(dpy, cmap, &topc.pixel, 1, 0);
2614 topc.red = MINL(65535, topc.red + 0x8000);
2615 topc.green = MINL(65535, topc.green + 0x8000);
2616 topc.blue = MINL(65535, topc.blue + 0x8000);
2617 if (allocate_nearest_color(dpy, cmap, &topc)) {
2618 mw->menu.top_shadow_color = topc.pixel;
2621 mw->menu.top_shadow_color = topc.pixel;
2627 if (mw->menu.bottom_shadow_color == mw->menu.foreground ||
2628 mw->menu.bottom_shadow_color == mw->core.background_pixel) {
2629 botc.pixel = mw->core.background_pixel;
2630 XQueryColor(dpy, cmap, &botc);
2631 botc.red = (botc.red * 3) / 5;
2632 botc.green = (botc.green * 3) / 5;
2633 botc.blue = (botc.blue * 3) / 5;
2634 if (allocate_nearest_color(dpy, cmap, &botc)) {
2635 if (botc.pixel == mw->core.background_pixel) {
2636 XFreeColors(dpy, cmap, &botc.pixel, 1, 0);
2637 botc.red = MINL(65535, botc.red + 0x4000);
2638 botc.green = MINL(65535, botc.green + 0x4000);
2639 botc.blue = MINL(65535, botc.blue + 0x4000);
2640 if (allocate_nearest_color(dpy, cmap, &botc)) {
2641 mw->menu.bottom_shadow_color =
2645 mw->menu.bottom_shadow_color = botc.pixel;
2652 if (top_frobbed && bottom_frobbed) {
2654 ((topc.red / 3) + (topc.green / 3) + (topc.blue / 3));
2656 ((botc.red / 3) + (botc.green / 3) + (botc.blue / 3));
2657 if (bot_avg > top_avg) {
2658 Pixel tmp = mw->menu.top_shadow_color;
2659 mw->menu.top_shadow_color =
2660 mw->menu.bottom_shadow_color;
2661 mw->menu.bottom_shadow_color = tmp;
2662 } else if (topc.pixel == botc.pixel) {
2663 if (botc.pixel == mw->menu.foreground)
2664 mw->menu.top_shadow_color =
2665 mw->core.background_pixel;
2667 mw->menu.bottom_shadow_color =
2668 mw->menu.foreground;
2672 if (!mw->menu.top_shadow_pixmap &&
2673 mw->menu.top_shadow_color == mw->core.background_pixel) {
2674 mw->menu.top_shadow_pixmap = mw->menu.gray_pixmap;
2675 mw->menu.top_shadow_color = mw->menu.foreground;
2677 if (!mw->menu.bottom_shadow_pixmap &&
2678 mw->menu.bottom_shadow_color == mw->core.background_pixel) {
2679 mw->menu.bottom_shadow_pixmap = mw->menu.gray_pixmap;
2680 mw->menu.bottom_shadow_color = mw->menu.foreground;
2683 xgcv.fill_style = FillOpaqueStippled;
2684 xgcv.foreground = mw->menu.top_shadow_color;
2685 xgcv.background = mw->core.background_pixel;
2686 /* xgcv.stipple = mw->menu.top_shadow_pixmap; gtb */
2687 if (mw->menu.top_shadow_pixmap &&
2688 mw->menu.top_shadow_pixmap != XmUNSPECIFIED_PIXMAP)
2689 xgcv.stipple = mw->menu.top_shadow_pixmap;
2692 pm = (xgcv.stipple ? GCStipple | GCFillStyle : 0);
2693 mw->menu.shadow_top_gc =
2694 XtGetGC((Widget) mw, GCForeground | GCBackground | pm, &xgcv);
2696 xgcv.foreground = mw->menu.bottom_shadow_color;
2697 /* xgcv.stipple = mw->menu.bottom_shadow_pixmap; gtb */
2698 if (mw->menu.bottom_shadow_pixmap &&
2699 mw->menu.bottom_shadow_pixmap != XmUNSPECIFIED_PIXMAP)
2700 xgcv.stipple = mw->menu.bottom_shadow_pixmap;
2703 pm = (xgcv.stipple ? GCStipple | GCFillStyle : 0);
2704 mw->menu.shadow_bottom_gc =
2705 XtGetGC((Widget) mw, GCForeground | GCBackground | pm, &xgcv);
2708 static void release_shadow_gcs(XlwMenuWidget mw)
2710 XtReleaseGC((Widget) mw, mw->menu.shadow_top_gc);
2711 XtReleaseGC((Widget) mw, mw->menu.shadow_bottom_gc);
2714 static void extract_font_extents(XlwMenuWidget mw)
2717 /* Find the maximal ascent/descent of the fonts in the font list
2718 so that all menu items can be the same height... */
2719 mw->menu.font_ascent = 0;
2720 mw->menu.font_descent = 0;
2723 XmFontContext context;
2724 #if (XmVersion >= 1002)
2725 XmFontListEntry fontentry;
2727 XmStringCharSet charset;
2731 if (!XmFontListInitFontContext(&context, mw->menu.font_list))
2733 #if (XmVersion >= 1002)
2734 /* There is a BUG in the 1.2 version of XmFontListGetNextFont() (or more
2735 specifically, in _XmGetFirstFont()) that can cause a null pointer to be
2736 passed to XFontsOfFontSet. Use XmFontListNextEntry(), which is the
2737 newer equivalent, instead. Also, it supports font sets, and the
2738 older function doesn't. */
2739 while ((fontentry = XmFontListNextEntry(context))) {
2742 XtPointer one_of_them =
2743 XmFontListEntryGetFont(fontentry, &rettype);
2744 if (rettype == XmFONT_IS_FONTSET) {
2745 XFontSet fontset = (XFontSet) one_of_them;
2746 XFontStruct **fontstruct_list;
2747 char **fontname_list;
2749 XFontsOfFontSet(fontset, &fontstruct_list,
2751 while (--fontcount >= 0) {
2752 font = fontstruct_list[fontcount];
2754 (int)mw->menu.font_ascent)
2755 mw->menu.font_ascent =
2758 (int)mw->menu.font_descent)
2759 mw->menu.font_descent =
2762 } else { /* XmFONT_IS_FONT */
2764 font = (XFontStruct *) one_of_them;
2765 if (font->ascent > (int)mw->menu.font_ascent)
2766 mw->menu.font_ascent = font->ascent;
2767 if (font->descent > (int)mw->menu.font_descent)
2768 mw->menu.font_descent = font->descent;
2771 #else /* motif 1.1 */
2772 while (XmFontListGetNextFont(context, &charset, &font)) {
2773 if (font->ascent > (int)mw->menu.font_ascent)
2774 mw->menu.font_ascent = font->ascent;
2775 if (font->descent > (int)mw->menu.font_descent)
2776 mw->menu.font_descent = font->descent;
2779 #endif /* Motif version */
2780 XmFontListFreeFontContext(context);
2782 #else /* Not Motif */
2783 # ifdef USE_XFONTSET
2784 XFontStruct **fontstruct_list;
2785 char **fontname_list;
2787 int fontcount = XFontsOfFontSet(mw->menu.font_set, &fontstruct_list,
2789 mw->menu.font_ascent = 0;
2790 mw->menu.font_descent = 0;
2791 # if 0 /* nasty, personal debug, Kazz */
2792 fprintf(stderr, "fontSet count is %d\n", fontcount);
2794 while (--fontcount >= 0) {
2795 font = fontstruct_list[fontcount];
2796 if (font->ascent > (int)mw->menu.font_ascent)
2797 mw->menu.font_ascent = font->ascent;
2798 if (font->descent > (int)mw->menu.font_descent)
2799 mw->menu.font_descent = font->descent;
2801 # else /* ! USE_XFONTSET */
2802 mw->menu.font_ascent = mw->menu.font->ascent;
2803 mw->menu.font_descent = mw->menu.font->descent;
2805 #endif /* NEED_MOTIF */
2809 static XFontStruct *default_font_of_font_list(XmFontList font_list)
2811 XFontStruct *font = 0;
2813 /* Xm/Label.c does this: */
2814 _XmFontListGetDefaultFont(font_list, &font);
2817 XmFontContext context;
2818 #if (XmVersion >= 1002)
2819 XmFontListEntry fontentry;
2821 XtPointer one_of_them;
2823 XmStringCharSet charset;
2826 if (!XmFontListInitFontContext(&context, font_list))
2828 #if (XmVersion >= 1002)
2829 /* There is a BUG in the 1.2 version of XmFontListGetNextFont() (or more
2830 specifically, in _XmGetFirstFont()) that can cause a null pointer to be
2831 passed to XFontsOfFontSet. Use XmFontListNextEntry(), which is the
2832 newer equivalent, instead. */
2833 fontentry = XmFontListNextEntry(context);
2834 one_of_them = XmFontListEntryGetFont(fontentry, &rettype);
2835 if (rettype == XmFONT_IS_FONTSET) {
2836 XFontSet fontset = (XFontSet) one_of_them;
2837 XFontStruct **fontstruct_list;
2838 char **fontname_list;
2839 (void)XFontsOfFontSet(fontset, &fontstruct_list,
2841 font = fontstruct_list[0];
2842 } else { /* XmFONT_IS_FONT */
2844 font = (XFontStruct *) one_of_them;
2847 if (!XmFontListGetNextFont(context, &charset, &font))
2851 XmFontListFreeFontContext(context);
2859 #endif /* NEED_MOTIF */
2862 XlwMenuInitialize(Widget request, Widget new, ArgList args, Cardinal * num_args)
2864 /* Get the GCs and the widget size */
2865 XlwMenuWidget mw = (XlwMenuWidget) new;
2867 RootWindowOfScreen(DefaultScreenOfDisplay(XtDisplay(mw)));
2868 Display *display = XtDisplay(mw);
2870 /* mw->menu.cursor = XCreateFontCursor (display, mw->menu.cursor_shape); */
2871 mw->menu.cursor = mw->menu.cursor_shape;
2873 mw->menu.gray_pixmap =
2874 XCreatePixmapFromBitmapData(display, window, (char *)gray_bits,
2875 gray_width, gray_height, 1, 0, 1);
2878 /* #### Even if it's a kludge!!!, we should consider doing the same for
2880 /* The menu.font_list slot came from the *fontList resource (Motif standard.)
2881 The menu.font_list_2 slot came from the *font resource, for backward
2882 compatibility with older versions of this code, and consistency with the
2883 rest of emacs. If both font and fontList are specified, we use fontList.
2884 If only one is specified, we use that. If neither are specified, we
2885 use the "fallback" value. What a kludge!!!
2887 Note that this has the bug that a more general wildcard like "*fontList:"
2888 will override a more specific resource like "Emacs*menubar.font:". But
2889 I can't think of a way around that.
2891 if (mw->menu.font_list) /* if *fontList is specified, use that */
2893 else if (mw->menu.font_list_2) /* else if *font is specified, use that */
2894 mw->menu.font_list = mw->menu.font_list_2;
2895 else /* otherwise use default */
2896 mw->menu.font_list = mw->menu.fallback_font_list;
2899 make_drawing_gcs(mw);
2900 make_shadow_gcs(mw);
2901 extract_font_extents(mw);
2903 mw->menu.popped_up = False;
2904 mw->menu.pointer_grabbed = False;
2905 mw->menu.next_release_must_exit = False;
2907 mw->menu.old_depth = 1;
2908 mw->menu.old_stack = XtNew(widget_value *);
2909 mw->menu.old_stack_length = 1;
2910 mw->menu.old_stack[0] = mw->menu.contents;
2912 mw->menu.new_depth = 0;
2913 mw->menu.new_stack = 0;
2914 mw->menu.new_stack_length = 0;
2915 push_new_stack(mw, mw->menu.contents);
2917 mw->menu.windows = XtNew(window_state);
2918 mw->menu.windows_length = 1;
2919 mw->menu.windows[0].x = 0;
2920 mw->menu.windows[0].y = 0;
2921 mw->menu.windows[0].width = 0;
2922 mw->menu.windows[0].height = 0;
2925 mw->core.width = mw->menu.windows[0].width;
2926 mw->core.height = mw->menu.windows[0].height;
2929 static void XlwMenuClassInitialize(void)
2931 initialize_massaged_resource_char();
2935 XlwMenuRealize(Widget w, Mask * valueMask, XSetWindowAttributes * attributes)
2937 XlwMenuWidget mw = (XlwMenuWidget) w;
2938 XSetWindowAttributes xswa;
2941 (*xlwMenuWidgetClass->core_class.superclass->core_class.realize)
2942 (w, valueMask, attributes);
2944 xswa.save_under = True;
2945 xswa.cursor = mw->menu.cursor_shape;
2946 mask = CWSaveUnder | CWCursor;
2947 if (mw->menu.use_backing_store) {
2948 xswa.backing_store = Always;
2949 mask |= CWBackingStore;
2951 XChangeWindowAttributes(XtDisplay(w), XtWindow(w), mask, &xswa);
2953 mw->menu.windows[0].window = XtWindow(w);
2954 mw->menu.windows[0].x = w->core.x;
2955 mw->menu.windows[0].y = w->core.y;
2956 mw->menu.windows[0].width = w->core.width;
2957 mw->menu.windows[0].height = w->core.height;
2960 /* Only the toplevel menubar/popup is a widget so it's the only one that
2961 receives expose events through Xt. So we repaint all the other panes
2962 when receiving an Expose event. */
2963 static void XlwMenuRedisplay(Widget w, XEvent * ev, Region region)
2965 XlwMenuWidget mw = (XlwMenuWidget) w;
2968 if (mw->core.being_destroyed)
2971 for (i = 0; i < mw->menu.old_depth; i++)
2972 display_menu(mw, i, False, NULL, NULL, NULL, NULL, NULL);
2973 set_new_state(mw, NULL, mw->menu.old_depth); /* #### - ??? */
2974 remap_menubar(mw); /* #### - do these two lines do anything? */
2977 static void XlwMenuDestroy(Widget w)
2980 XlwMenuWidget mw = (XlwMenuWidget) w;
2982 if (mw->menu.pointer_grabbed) {
2983 XtUngrabPointer(w, CurrentTime);
2984 mw->menu.pointer_grabbed = False;
2987 release_drawing_gcs(mw);
2988 release_shadow_gcs(mw);
2990 /* this doesn't come from the resource db but is created explicitly
2991 so we must free it ourselves. */
2992 XFreePixmap(XtDisplay(mw), mw->menu.gray_pixmap);
2993 mw->menu.gray_pixmap = (Pixmap) - 1;
2995 /* Don't free mw->menu.contents because that comes from our creator.
2996 The `*_stack' elements are just pointers into `contents' so leave
2997 that alone too. But free the stacks themselves. */
2998 if (mw->menu.old_stack)
2999 XtFree((char *)mw->menu.old_stack);
3000 if (mw->menu.new_stack)
3001 XtFree((char *)mw->menu.new_stack);
3003 /* Remember, you can't free anything that came from the resource
3004 database. This includes:
3006 mw->menu.top_shadow_pixmap
3007 mw->menu.bottom_shadow_pixmap
3010 Also the color cells of top_shadow_color, bottom_shadow_color,
3011 foreground, and button_foreground will never be freed until this
3012 client exits. Nice, eh?
3015 /* start from 1 because the one in slot 0 is w->core.window */
3016 for (i = 1; i < mw->menu.windows_length; i++)
3017 XDestroyWindow(XtDisplay(mw), mw->menu.windows[i].window);
3018 if (mw->menu.windows)
3019 XtFree((char *)mw->menu.windows);
3023 XlwMenuSetValues(Widget current, Widget request, Widget new, ArgList args,
3024 Cardinal * num_args)
3026 XlwMenuWidget oldmw = (XlwMenuWidget) current;
3027 XlwMenuWidget newmw = (XlwMenuWidget) new;
3028 Boolean redisplay = False;
3031 if (newmw->menu.contents
3032 && newmw->menu.contents->contents
3033 && newmw->menu.contents->contents->change >= VISIBLE_CHANGE)
3036 if (newmw->core.background_pixel != oldmw->core.background_pixel
3037 || newmw->menu.foreground != oldmw->menu.foreground
3038 /* For the XEditResource protocol, which may want to change the font. */
3040 || newmw->menu.font_list != oldmw->menu.font_list
3041 || newmw->menu.font_list_2 != oldmw->menu.font_list_2
3042 || newmw->menu.fallback_font_list != oldmw->menu.fallback_font_list
3044 || newmw->menu.font != oldmw->menu.font
3047 release_drawing_gcs(newmw);
3048 make_drawing_gcs(newmw);
3051 for (i = 0; i < oldmw->menu.windows_length; i++) {
3052 XSetWindowBackground(XtDisplay(oldmw),
3053 oldmw->menu.windows[i].window,
3054 newmw->core.background_pixel);
3055 /* clear windows and generate expose events */
3056 XClearArea(XtDisplay(oldmw),
3057 oldmw->menu.windows[i].window, 0, 0, 0, 0,
3065 static void XlwMenuResize(Widget w)
3067 XlwMenuWidget mw = (XlwMenuWidget) w;
3069 mw->menu.windows[0].width = mw->core.width;
3070 mw->menu.windows[0].height = mw->core.height;
3073 /* Action procedures */
3075 handle_single_motion_event(XlwMenuWidget mw, XMotionEvent * ev,
3082 if (!map_event_to_widget_value(mw, ev, &val, &level, &stay_up)) {
3083 /* we wind up here when: (a) the event is in the menubar, (b) the
3084 event isn't in the menubar or any of the panes, (c) the event is on
3085 a disabled menu item */
3086 pop_new_stack_if_no_contents(mw);
3087 if (select_p && !stay_up) {
3088 /* pop down all menus and exit */
3089 mw->menu.next_release_must_exit = True;
3090 set_new_state(mw, (val = NULL), 1);
3093 /* we wind up here when: (a) the event pops up a pull_right menu,
3094 (b) a menu item that is not disabled is highlighted */
3095 if (select_p && mw->menu.bounce_down
3096 && close_to_reference_time((Widget) mw,
3097 mw->menu.menu_bounce_time,
3099 /* motion can cause more than one event. Don't bounce right back
3100 up if we've just bounced down. */
3102 } else if (select_p && mw->menu.bounce_down &&
3103 mw->menu.last_selected_val &&
3104 (mw->menu.last_selected_val == val)) {
3105 val = NULL; /* assigned to mw->last_selected_val below */
3106 mw->menu.menu_bounce_time = ev->time;
3107 /* popdown last menu if we're selecting the same menu item as we did
3108 last time and the XlwMenu.bounceDown resource is set, if the
3109 item is on the menubar itself, then exit. */
3110 if (level == (mw->menu.popped_up ? 0 : 1))
3111 mw->menu.next_release_must_exit = True;
3113 mw->menu.menu_bounce_time = 0;
3114 set_new_state(mw, val, level);
3116 mw->menu.last_selected_val = val;
3119 /* Sync with the display. Makes it feel better on X terms. */
3120 XFlush(XtDisplay(mw));
3124 handle_motion_event(XlwMenuWidget mw, XMotionEvent * ev, Boolean select_p)
3128 unsigned int state = ev->state;
3129 XMotionEvent *event = ev, dummy;
3131 /* allow motion events to be generated again */
3132 dummy.window = ev->window;
3134 && XQueryPointer(XtDisplay(mw), dummy.window,
3135 &dummy.root, &dummy.subwindow,
3136 &dummy.x_root, &dummy.y_root,
3137 &dummy.x, &dummy.y, &dummy.state)
3138 && dummy.state == state && (dummy.x_root != x || dummy.y_root != y)) {
3139 /* don't handle the event twice or that breaks bounce_down. --Stig */
3140 dummy.type = ev->type;
3144 lw_menu_accelerate = False;
3145 handle_single_motion_event(mw, event, select_p);
3148 Time x_focus_timestamp_really_sucks_fix_me_better;
3150 static void Start(Widget w, XEvent * ev, String * params, Cardinal * num_params)
3152 XlwMenuWidget mw = (XlwMenuWidget) w;
3154 lw_menubar_widget = w;
3156 lw_menu_active = True;
3158 if (!mw->menu.pointer_grabbed) {
3159 mw->menu.menu_post_time = ev->xbutton.time;
3160 mw->menu.menu_bounce_time = 0;
3161 mw->menu.next_release_must_exit = True;
3162 mw->menu.last_selected_val = NULL;
3163 x_focus_timestamp_really_sucks_fix_me_better =
3164 ((XButtonPressedEvent *) ev)->time;
3165 XtCallCallbackList((Widget) mw, mw->menu.open, NULL);
3167 /* notes the absolute position of the menubar window */
3168 mw->menu.windows[0].x = ev->xmotion.x_root - ev->xmotion.x;
3169 mw->menu.windows[0].y = ev->xmotion.y_root - ev->xmotion.y;
3171 XtGrabPointer((Widget) mw, False,
3172 (ButtonMotionMask | ButtonReleaseMask |
3173 ButtonPressMask), GrabModeAsync, GrabModeAsync,
3174 None, mw->menu.cursor_shape,
3175 ((XButtonPressedEvent *) ev)->time);
3176 mw->menu.pointer_grabbed = True;
3179 /* handles the down like a move, slots are mostly compatible */
3180 handle_motion_event(mw, &ev->xmotion, True);
3183 static void Drag(Widget w, XEvent * ev, String * params, Cardinal * num_params)
3185 XlwMenuWidget mw = (XlwMenuWidget) w;
3186 handle_motion_event(mw, &ev->xmotion, False);
3190 Select(Widget w, XEvent * ev, String * params, Cardinal * num_params)
3192 XlwMenuWidget mw = (XlwMenuWidget) w;
3193 widget_value *selected_item =
3194 mw->menu.old_stack[mw->menu.old_depth - 1];
3196 lw_menu_accelerate = False;
3198 /* If user releases the button quickly, without selecting anything,
3199 after the initial down-click that brought the menu up,
3201 if ((selected_item == 0 || selected_item->call_data == 0)
3202 && (!mw->menu.next_release_must_exit
3203 || close_to_reference_time(w, mw->menu.menu_post_time, ev))) {
3204 mw->menu.next_release_must_exit = False;
3208 /* pop down everything */
3209 mw->menu.new_depth = 1;
3212 /* Destroy() only gets called for popup menus. Menubar widgets aren't
3213 destroyed when their menu panes get nuked. */
3214 if (mw->menu.pointer_grabbed) {
3215 XtUngrabPointer((Widget) w, ev->xmotion.time);
3216 mw->menu.pointer_grabbed = False;
3219 if (mw->menu.popped_up) {
3220 mw->menu.popped_up = False;
3221 XtPopdown(XtParent(mw));
3224 lw_menu_active = False;
3226 x_focus_timestamp_really_sucks_fix_me_better =
3227 ((XButtonPressedEvent *) ev)->time;
3230 XtCallCallbackList((Widget) mw, mw->menu.select,
3231 (XtPointer) selected_item);
3234 /* Action procedures for keyboard accelerators */
3237 void xlw_set_menu(Widget w, widget_value * val)
3239 lw_menubar_widget = w;
3240 set_new_state((XlwMenuWidget) w, val, 1);
3243 /* prepare the menu structure via the call-backs */
3244 void xlw_map_menu(Time t)
3246 XlwMenuWidget mw = (XlwMenuWidget) lw_menubar_widget;
3248 lw_menu_accelerate = True;
3250 if (!mw->menu.pointer_grabbed) {
3251 XWindowAttributes ret;
3252 Window parent = 0UL, root = 0UL;
3253 Window *waste = NULL;
3254 unsigned int num_waste;
3256 lw_menu_active = True;
3258 mw->menu.menu_post_time = t;
3259 mw->menu.menu_bounce_time = 0;
3261 mw->menu.next_release_must_exit = True;
3262 mw->menu.last_selected_val = NULL;
3264 XtCallCallbackList((Widget) mw, mw->menu.open, NULL);
3266 /* do this for keyboards too! */
3267 /* notes the absolute position of the menubar window */
3269 mw->menu.windows [0].x = ev->xmotion.x_root - ev->xmotion.x;
3270 mw->menu.windows [0].y = ev->xmotion.y_root - ev->xmotion.y;
3273 /* get the geometry of the menubar */
3275 /* there has to be a better way than this. */
3277 mw->menu.windows[0].x = 0;
3278 mw->menu.windows[0].y = 0;
3280 parent = XtWindow(lw_menubar_widget);
3282 XGetWindowAttributes(XtDisplay(lw_menubar_widget),
3284 mw->menu.windows[0].x += ret.x;
3285 mw->menu.windows[0].y += ret.y;
3288 XQueryTree(XtDisplay(lw_menubar_widget), parent,
3289 &root, &parent, &waste, &num_waste);
3294 while (parent != root);
3296 XtGrabPointer((Widget) mw, False,
3297 (ButtonMotionMask | ButtonReleaseMask |
3298 ButtonPressMask), GrabModeAsync, GrabModeAsync,
3299 None, mw->menu.cursor_shape, t);
3300 mw->menu.pointer_grabbed = True;
3304 /* display the stupid menu already */
3305 void xlw_display_menu(Time t)
3307 XlwMenuWidget mw = (XlwMenuWidget) lw_menubar_widget;
3309 lw_menu_accelerate = True;
3313 /* Sync with the display. Makes it feel better on X terms. */
3314 XFlush(XtDisplay(mw));
3317 /* push a sub menu */
3318 void xlw_push_menu(widget_value * val)
3320 push_new_stack((XlwMenuWidget) lw_menubar_widget, val);
3323 /* pop a sub menu */
3324 int xlw_pop_menu(void)
3326 if (((XlwMenuWidget) lw_menubar_widget)->menu.new_depth > 0)
3327 ((XlwMenuWidget) lw_menubar_widget)->menu.new_depth--;
3333 void xlw_kill_menus(widget_value * val)
3335 XlwMenuWidget mw = (XlwMenuWidget) lw_menubar_widget;
3337 lw_menu_accelerate = False;
3339 mw->menu.new_depth = 1;
3342 if (mw->menu.pointer_grabbed) {
3343 XtUngrabPointer(lw_menubar_widget, CurrentTime);
3344 mw->menu.pointer_grabbed = False;
3347 lw_menu_active = False;
3348 XtCallCallbackList(lw_menubar_widget, mw->menu.select, (XtPointer) val);
3351 /* set the menu item */
3352 void xlw_set_item(widget_value * val)
3354 if (((XlwMenuWidget) lw_menubar_widget)->menu.new_depth > 0)
3355 ((XlwMenuWidget) lw_menubar_widget)->menu.new_depth--;
3356 push_new_stack((XlwMenuWidget) lw_menubar_widget, val);
3359 /* get either the current entry or a list of all entries in the current submenu */
3360 widget_value *xlw_get_entries(int allp)
3362 XlwMenuWidget mw = (XlwMenuWidget) lw_menubar_widget;
3364 if (mw->menu.new_depth >= 2)
3365 return mw->menu.new_stack[mw->menu.new_depth -
3368 return mw->menu.new_stack[0];
3369 } else if (mw->menu.new_depth >= 1)
3370 return mw->menu.new_stack[mw->menu.new_depth - 1];
3375 int xlw_menu_level(void)
3377 return ((XlwMenuWidget) lw_menubar_widget)->menu.new_depth;
3380 /* Special code to pop-up a menu */
3381 void xlw_pop_up_menu(XlwMenuWidget mw, XButtonPressedEvent * event)
3383 int x = event->x_root;
3384 int y = event->y_root;
3387 int borderwidth = mw->menu.shadow_thickness;
3388 Screen *screen = XtScreen(mw);
3390 mw->menu.menu_post_time = event->time;
3391 mw->menu.menu_bounce_time = 0;
3392 mw->menu.next_release_must_exit = True;
3393 mw->menu.last_selected_val = NULL;
3395 XtCallCallbackList((Widget) mw, mw->menu.open, NULL);
3399 w = mw->menu.windows[0].width;
3400 h = mw->menu.windows[0].height;
3405 if (x < borderwidth)
3408 if (x > WidthOfScreen(screen) - w - 2 * borderwidth)
3409 x = WidthOfScreen(screen) - w - 2 * borderwidth;
3411 if (y < borderwidth)
3414 if (y > HeightOfScreen(screen) - h - 2 * borderwidth)
3415 y = HeightOfScreen(screen) - h - 2 * borderwidth;
3417 mw->menu.popped_up = True;
3418 XtConfigureWidget(XtParent(mw), x, y, w, h,
3419 XtParent(mw)->core.border_width);
3420 XtPopup(XtParent(mw), XtGrabExclusive);
3421 display_menu(mw, 0, False, NULL, NULL, NULL, NULL, NULL);
3422 if (!mw->menu.pointer_grabbed) {
3423 XtGrabPointer((Widget) mw, False,
3424 (ButtonMotionMask | ButtonReleaseMask |
3425 ButtonPressMask), GrabModeAsync, GrabModeAsync,
3426 None, mw->menu.cursor_shape, event->time);
3427 mw->menu.pointer_grabbed = True;
3430 mw->menu.windows[0].x = x + borderwidth;
3431 mw->menu.windows[0].y = y + borderwidth;
3433 handle_motion_event(mw, (XMotionEvent *) event, True);
3439 * This is a horrible function which should not be needed.
3440 * use it to put the resize method back the way the XlwMenu
3441 * class initializer put it. Motif screws with this when
3442 * the XlwMenu class gets instantiated.
3444 void xlw_unmunge_class_resize(Widget w)
3446 if (w->core.widget_class->core_class.resize != XlwMenuResize)
3447 w->core.widget_class->core_class.resize = XlwMenuResize;