1 /* Implements a lightweight menubar widget.
2 Copyright (C) 1992, 1993, 1994 Lucid, Inc.
3 Copyright (C) 1995 Tinker Systems and INS Engineering Corp.
5 This file is part of the Lucid Widget Library.
7 The Lucid Widget Library is free software: you can redistribute it
8 and/or modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation, either version 3 of the
10 License, or (at your option) any later version.
12 The Lucid Widget Library is distributed in the hope that it will be
13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>. */
20 /* Created by devin@lucid.com */
27 #include <sys/types.h>
33 #include <X11/IntrinsicP.h>
34 #include <X11/ShellP.h>
35 #include <X11/StringDefs.h>
36 #include <X11/cursorfont.h>
37 #include <X11/bitmaps/gray>
41 #if XmVersion < 1002 /* 1.1 or ancient */
42 #undef XmFONTLIST_DEFAULT_TAG
43 #define XmFONTLIST_DEFAULT_TAG XmSTRING_DEFAULT_CHARSET
44 #endif /* XmVersion < 1.2 */
48 #ifdef USE_DEBUG_MALLOC
52 /* simple, naive integer maximum */
54 #define max(a,b) ((a)>(b)?(a):(b))
58 xlwMenuTranslations[] = "<BtnDown>: start()\n\
59 <BtnMotion>: drag()\n\
63 extern Widget lw_menubar_widget;
65 #define offset(field) XtOffset(XlwMenuWidget, field)
66 static XtResource xlwMenuResources[] = {
68 /* There are three font list resources, so that we can accept either of
69 the resources *fontList: or *font:, and so that we can tell the
70 difference between them being specified, and being defaulted to a
71 font from the XtRString specified here. */
72 {XmNfontList, XmCFontList, XmRFontList, sizeof(XmFontList),
73 offset(menu.font_list), XtRImmediate, (XtPointer) 0}
75 {XtNfont, XtCFont, XmRFontList, sizeof(XmFontList),
76 offset(menu.font_list_2), XtRImmediate, (XtPointer) 0}
78 {XmNfontList, XmCFontList, XmRFontList, sizeof(XmFontList),
79 offset(menu.fallback_font_list),
80 /* We must use an iso8859-1 font here, or people without $LANG set lose.
81 It's fair to assume that those who do have $LANG set also have the
82 *fontList resource set, or at least know how to deal with this. */
84 (XtPointer) "-*-helvetica-bold-r-*-*-*-120-*-*-*-*-iso8859-1"}
87 {XtNfont, XtCFont, XtRFontStruct, sizeof(XFontStruct *),
88 offset(menu.font), XtRString, (XtPointer) "XtDefaultFont"}
91 /* #### Consider using the same method as for Motif; see the comment in
92 XlwMenuInitialize(). */
93 {XtNfontSet, XtCFontSet, XtRFontSet, sizeof(XFontSet),
94 offset(menu.font_set), XtRString, (XtPointer) "XtDefaultFontSet"}
98 {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
99 offset(menu.foreground), XtRString, (XtPointer) "XtDefaultForeground"}
101 {XtNbuttonForeground, XtCButtonForeground, XtRPixel, sizeof(Pixel),
102 offset(menu.button_foreground), XtRString,
103 (XtPointer) "XtDefaultForeground"}
105 {XtNhighlightForeground, XtCHighlightForeground, XtRPixel,
107 offset(menu.highlight_foreground), XtRString,
108 (XtPointer) "XtDefaultForeground"}
110 {XtNtitleForeground, XtCTitleForeground, XtRPixel, sizeof(Pixel),
111 offset(menu.title_foreground), XtRString,
112 (XtPointer) "XtDefaultForeground"}
114 {XtNmargin, XtCMargin, XtRDimension, sizeof(Dimension),
115 offset(menu.margin), XtRImmediate, (XtPointer) 2}
117 {XmNmarginWidth, XmCMarginWidth, XmRHorizontalDimension,
119 offset(menu.horizontal_margin), XtRImmediate, (XtPointer) 2}
121 {XmNmarginHeight, XmCMarginHeight, XmRVerticalDimension,
123 offset(menu.vertical_margin), XtRImmediate, (XtPointer) 1}
125 {XmNspacing, XmCSpacing, XmRHorizontalDimension, sizeof(Dimension),
126 offset(menu.column_spacing), XtRImmediate, (XtPointer) 4}
128 {XmNindicatorSize, XmCIndicatorSize, XtRDimension, sizeof(Dimension),
129 offset(menu.indicator_size), XtRImmediate, (XtPointer) 0}
132 {XmNshadowThickness, XmCShadowThickness, XmRHorizontalDimension,
133 sizeof(Dimension), offset(menu.shadow_thickness),
134 XtRImmediate, (XtPointer) 2}
137 {XmNshadowThickness, XmCShadowThickness, XtRDimension,
138 sizeof(Dimension), offset(menu.shadow_thickness),
139 XtRImmediate, (XtPointer) 2}
142 {XmNselectColor, XmCSelectColor, XtRPixel, sizeof(Pixel),
143 offset(menu.select_color), XtRImmediate, (XtPointer) - 1}
145 {XmNtopShadowColor, XmCTopShadowColor, XtRPixel, sizeof(Pixel),
146 offset(menu.top_shadow_color), XtRImmediate, (XtPointer) - 1}
148 {XmNbottomShadowColor, XmCBottomShadowColor, XtRPixel, sizeof(Pixel),
149 offset(menu.bottom_shadow_color), XtRImmediate, (XtPointer) - 1}
151 {XmNtopShadowPixmap, XmCTopShadowPixmap, XtRPixmap, sizeof(Pixmap),
152 offset(menu.top_shadow_pixmap), XtRImmediate, (XtPointer) None}
154 {XmNbottomShadowPixmap, XmCBottomShadowPixmap, XtRPixmap,
156 offset(menu.bottom_shadow_pixmap), XtRImmediate, (XtPointer) None}
159 {XtNopen, XtCCallback, XtRCallback, sizeof(XtPointer),
160 offset(menu.open), XtRCallback, (XtPointer) NULL}
162 {XtNselect, XtCCallback, XtRCallback, sizeof(XtPointer),
163 offset(menu.select), XtRCallback, (XtPointer) NULL}
165 {XtNmenu, XtCMenu, XtRPointer, sizeof(XtPointer),
166 offset(menu.contents), XtRImmediate, (XtPointer) NULL}
168 {XtNcursor, XtCCursor, XtRCursor, sizeof(Cursor),
169 offset(menu.cursor_shape), XtRString, (XtPointer) "right_ptr"}
171 {XtNhorizontal, XtCHorizontal, XtRInt, sizeof(int),
172 offset(menu.horizontal), XtRImmediate, (XtPointer) True},
173 {XtNuseBackingStore, XtCUseBackingStore, XtRBoolean, sizeof(Boolean),
174 offset(menu.use_backing_store), XtRImmediate, (XtPointer) False}
176 {XtNbounceDown, XtCBounceDown, XtRBoolean, sizeof(Boolean),
177 offset(menu.bounce_down), XtRImmediate, (XtPointer) True}
179 {XtNresourceLabels, XtCResourceLabels, XtRBoolean, sizeof(Boolean),
180 offset(menu.lookup_labels), XtRImmediate, (XtPointer) False}
185 static Boolean XlwMenuSetValues(Widget current, Widget request, Widget new,
186 ArgList args, Cardinal * num_args);
187 static void XlwMenuRealize(Widget w, Mask * valueMask,
188 XSetWindowAttributes * attributes);
189 static void XlwMenuRedisplay(Widget w, XEvent * ev, Region region);
190 static void XlwMenuResize(Widget w);
191 static void XlwMenuInitialize(Widget request, Widget new, ArgList args,
192 Cardinal * num_args);
193 static void XlwMenuDestroy(Widget w);
194 static void XlwMenuClassInitialize(void);
195 static void Start(Widget w, XEvent * ev, String * params,
196 Cardinal * num_params);
197 static void Drag(Widget w, XEvent * ev, String * params, Cardinal * num_params);
198 static void Select(Widget w, XEvent * ev, String * params,
199 Cardinal * num_params);
202 static XFontStruct *default_font_of_font_list(XmFontList);
205 static XtActionsRec xlwMenuActionsList[] = {
211 #define SuperClass ((CoreWidgetClass)&coreClassRec)
213 XlwMenuClassRec xlwMenuClassRec = {
214 { /* CoreClass fields initialization */
215 (WidgetClass) SuperClass, /* superclass */
216 "XlwMenu", /* class_name */
217 sizeof(XlwMenuRec), /* size */
218 XlwMenuClassInitialize, /* class_initialize */
219 NULL, /* class_part_initialize */
220 FALSE, /* class_inited */
221 XlwMenuInitialize, /* initialize */
222 NULL, /* initialize_hook */
223 XlwMenuRealize, /* realize */
224 xlwMenuActionsList, /* actions */
225 XtNumber(xlwMenuActionsList), /* num_actions */
226 xlwMenuResources, /* resources */
227 XtNumber(xlwMenuResources), /* resource_count */
228 NULLQUARK, /* xrm_class */
229 TRUE, /* compress_motion */
230 XtExposeCompressMaximal, /* compress_exposure */
231 TRUE, /* compress_enterleave */
232 FALSE, /* visible_interest */
233 XlwMenuDestroy, /* destroy */
234 XlwMenuResize, /* resize */
235 XlwMenuRedisplay, /* expose */
236 XlwMenuSetValues, /* set_values */
237 NULL, /* set_values_hook */
238 XtInheritSetValuesAlmost, /* set_values_almost */
239 NULL, /* get_values_hook */
240 NULL, /* #### - should this be set for grabs? accept_focus */
241 XtVersion, /* version */
242 NULL, /* callback_private */
243 xlwMenuTranslations, /* tm_table */
244 XtInheritQueryGeometry, /* query_geometry */
245 XtInheritDisplayAccelerator, /* display_accelerator */
248 , /* XlwMenuClass fields initialization */
255 WidgetClass xlwMenuWidgetClass = (WidgetClass) & xlwMenuClassRec;
257 extern int lw_menu_accelerate;
260 #if 0 /* Apparently not used anywhere */
262 static char *safe_strdup(char *s)
267 result = (char *)malloc(strlen(s) + 1);
276 /* Replacement for XAllocColor() that tries to return the nearest
277 available color if the colormap is full. From FSF Emacs. */
280 allocate_nearest_color(Display * display, Colormap screen_colormap,
283 int status = XAllocColor(display, screen_colormap, color_def);
288 /* If we got to this point, the colormap is full, so we're
289 going to try to get the next closest color.
290 The algorithm used is a least-squares matching, which is
291 what X uses for closest color matching with StaticColor visuals. */
294 unsigned long nearest_delta = ULONG_MAX;
296 int no_cells = XDisplayCells(display, XDefaultScreen(display));
297 /* Don't use alloca here because lwlib doesn't have the
298 necessary configuration information that src does. */
299 XColor *cells = (XColor *) malloc(sizeof(XColor) * no_cells);
301 for (x = 0; x < no_cells; x++)
304 XQueryColors(display, screen_colormap, cells, no_cells);
306 for (nearest = 0, x = 0; x < no_cells; x++) {
307 long dred = (color_def->red >> 8) - (cells[x].red >> 8);
309 (color_def->green >> 8) - (cells[x].green >> 8);
311 (color_def->blue >> 8) - (cells[x].blue >> 8);
312 unsigned long delta =
313 dred * dred + dgreen * dgreen + dblue * dblue;
315 if (delta < nearest_delta) {
317 nearest_delta = delta;
320 color_def->red = cells[nearest].red;
321 color_def->green = cells[nearest].green;
322 color_def->blue = cells[nearest].blue;
324 return XAllocColor(display, screen_colormap, color_def);
328 static void push_new_stack(XlwMenuWidget mw, widget_value * val)
330 if (!mw->menu.new_stack) {
331 mw->menu.new_stack_length = 10;
333 (widget_value **) XtCalloc(mw->menu.new_stack_length,
334 sizeof(widget_value *));
335 } else if (mw->menu.new_depth == mw->menu.new_stack_length) {
336 mw->menu.new_stack_length *= 2;
338 (widget_value **) XtRealloc((char *)mw->menu.new_stack,
339 mw->menu.new_stack_length *
340 sizeof(widget_value *));
342 mw->menu.new_stack[mw->menu.new_depth++] = val;
345 static void pop_new_stack_if_no_contents(XlwMenuWidget mw)
347 if (mw->menu.new_depth &&
348 !mw->menu.new_stack[mw->menu.new_depth - 1]->contents)
349 mw->menu.new_depth -= 1;
352 static void make_old_stack_space(XlwMenuWidget mw, int n)
354 if (!mw->menu.old_stack) {
355 mw->menu.old_stack_length = max(10, n);
357 (widget_value **) XtCalloc(mw->menu.old_stack_length,
358 sizeof(widget_value *));
359 } else if (mw->menu.old_stack_length < n) {
360 while (mw->menu.old_stack_length < n)
361 mw->menu.old_stack_length *= 2;
364 (widget_value **) XtRealloc((char *)mw->menu.old_stack,
365 mw->menu.old_stack_length *
366 sizeof(widget_value *));
371 close_to_reference_time(Widget w, Time reference_time, XEvent * ev)
375 (ev->xbutton.time - reference_time
376 < (Time) XtGetMultiClickTime(XtDisplay(w)));
380 static int string_width(XlwMenuWidget mw,
389 Dimension width, height;
390 XmStringExtent(mw->menu.font_list, s, &width, &height);
395 XmbTextExtents(mw->menu.font_set, s, strlen(s), &ri, &rl);
400 XTextExtents(mw->menu.font, s, strlen(s), &drop, &drop, &drop, &xcs);
402 # endif /* USE_XFONTSET */
406 static char massaged_resource_char[256];
408 static void initialize_massaged_resource_char(void)
411 for (j = 0; j < (int)sizeof(massaged_resource_char); j++) {
412 if ((j >= 'a' && j <= 'z') ||
413 (j >= 'A' && j <= 'Z') ||
414 (j >= '0' && j <= '9') || (j == '_') || (j >= 0xa0))
415 massaged_resource_char[j] = (char)j;
417 massaged_resource_char['_'] = '_';
418 massaged_resource_char['+'] = 'P'; /* Convert C++ to cPP */
419 massaged_resource_char['.'] = '_'; /* Convert Buffers... to buffers___ */
422 static int string_width_u(XlwMenuWidget mw,
431 Dimension width, height;
436 # else /* ! USE_XFONTSET */
448 if (!XmStringGetLtoR(string, XmFONTLIST_DEFAULT_TAG, &chars))
453 charslength = strlen(chars);
454 newchars = (char *)alloca(charslength + 1);
456 for (i = j = 0; chars[i] && (j < charslength); i++)
457 if (chars[i] == '%' && chars[i + 1] == '_')
460 newchars[j++] = chars[i];
464 newstring = XmStringLtoRCreate(newchars, XmFONTLIST_DEFAULT_TAG);
465 XmStringExtent(mw->menu.font_list, newstring, &width, &height);
466 XmStringFree(newstring);
471 XmbTextExtents(mw->menu.font_set, newchars, j, &ri, &rl);
473 # else /* ! USE_XFONTSET */
474 XTextExtents(mw->menu.font, newchars, j, &drop, &drop, &drop, &xcs);
476 # endif /* USE_XFONTSET */
480 static void massage_resource_name(const char *in, char *out)
482 /* Turn a random string into something suitable for using as a resource.
485 "Kill Buffer" -> "killBuffer"
486 "Find File..." -> "findFile___"
487 "Search and Replace..." -> "searchAndReplace___"
488 "C++ Mode Commands" -> "cppModeCommands"
490 Valid characters in a resource NAME component are: a-zA-Z0-9_
493 #ifdef PRINT_XLWMENU_RESOURCE_CONVERSIONS
494 /* Compile with -DPRINT_XLWMENU_RESOURCE_CONVERSIONS to generate a
495 translation file for menu localizations. */
496 char *save_in = in, *save_out = out;
499 Boolean firstp = True;
501 if (*in == '%' && *(in + 1) == '_')
506 if (*in == '%' && *(in + 1) == '%')
508 ch = massaged_resource_char[(unsigned char)*in++];
510 int int_ch = (int)(unsigned char)ch;
512 firstp ? tolower(int_ch) : toupper(int_ch);
515 massaged_resource_char[(unsigned char)
519 if (!*(in - 1)) /* Overshot the NULL byte? */
526 #ifdef PRINT_XLWMENU_RESOURCE_CONVERSIONS
527 printf("! Emacs*XlwMenu.%s.labelString:\t%s\n", save_out, save_in);
528 printf("Emacs*XlwMenu.%s.labelString:\n", save_out);
532 static XtResource nameResource[] = {
533 {"labelString", "LabelString", XtRString, sizeof(String),
537 /* This function searches STRING for parameter inserts of the form:
539 padding is either space (' ') or dash ('-') meaning
540 padding to the left or right of the inserted parameter.
541 In essence, all %1 strings are replaced by VALUE in the return value.
542 The caller is expected to free the return value using XtFree().
543 %% means insert one % (like printf).
544 %1 means insert VALUE.
545 %-1 means insert VALUE followed by one space. The latter is
546 not inserted if VALUE is a zero length string.
548 static char *parameterize_string(const char *string, const char *value)
552 unsigned int done = 0;
556 result = XtMalloc(1);
564 for (ntimes = 1, percent = string;
565 (percent = strchr(percent, '%')); ntimes++)
568 result = XtMalloc((ntimes * strlen(value)) + strlen(string) + 4);
571 while ((percent = strchr(string, '%'))) {
572 unsigned int left_pad;
573 unsigned int right_pad;
576 if (percent[1] == '%') { /* it's a real % */
577 strncat(result, string, 1 + percent - string); /* incl % */
578 string = &percent[2]; /* after the second '%' */
579 continue; /* with the while() loop */
585 for (p = &percent[1]; /* test *p inside the loop */ ; p++) {
586 if (*p == ' ') { /* left pad */
588 } else if (*p == '-') { /* right pad */
590 } else if (*p == '1') { /* param and terminator */
591 strncat(result, string, percent - string);
592 if (value[0] != '\0') {
594 for (i = 0; i < left_pad; i++)
596 strcat(result, value);
597 for (i = 0; i < right_pad; i++)
600 string = &p[1]; /* after the '1' */
601 done++; /* no need to do old way */
602 break; /* out of for() loop */
603 } else { /* bogus, copy the format as is */
604 /* out of for() loop */
605 strncat(result, string, 1 + p - string);
606 string = (*p ? &p[1] : p);
612 /* Copy the tail of the string */
613 strcat(result, string);
615 /* If we have not processed a % string, and we have a value, tail it. */
616 if (!done && value[0] != '\0') {
618 strcat(result, value);
626 static XmString resource_widget_value(XlwMenuWidget mw, widget_value * val)
628 if (!val->toolkit_data) {
629 char *resourced_name = NULL;
630 char *converted_name, *str;
631 XmString complete_name;
632 char massaged_name[1024];
634 if (mw->menu.lookup_labels) {
635 /* Convert value style name into resource style name.
636 eg: "Free Willy" becomes "freeWilly" */
637 massage_resource_name(val->name, massaged_name);
639 /* If we have a value (parameter) see if we can find a "Named"
642 char named_name[1024];
643 sprintf(named_name, "%sNamed", massaged_name);
644 XtGetSubresources((Widget) mw,
645 (XtPointer) & resourced_name,
646 named_name, named_name,
647 nameResource, 1, NULL, 0);
650 /* If nothing yet, try to load from the massaged name. */
651 if (!resourced_name) {
652 XtGetSubresources((Widget) mw,
653 (XtPointer) & resourced_name,
654 massaged_name, massaged_name,
655 nameResource, 1, NULL, 0);
659 /* if (mw->menu.lookup_labels) */
660 /* Still nothing yet, use the name as the value. */
662 resourced_name = val->name;
664 /* Parameterize the string. */
666 parameterize_string(resourced_name, val->value);
668 /* nuke newline characters to prevent menubar screwups */
669 for (str = converted_name; *str; str++) {
674 /* Improve OSF's bottom line. */
675 #if (XmVersion >= 1002)
676 complete_name = XmStringCreateLocalized(converted_name);
678 complete_name = XmStringCreateLtoR(converted_name,
679 XmSTRING_DEFAULT_CHARSET);
681 XtFree(converted_name);
683 val->toolkit_data = complete_name;
684 val->free_toolkit_data = True;
686 return (XmString) val->toolkit_data;
691 /* These two routines should be a separate file..djw */
692 static char *xlw_create_localized_string(Widget w,
694 char **args, unsigned int nargs)
703 (XtPointer) & string,
704 name, name, nameResource, 1, NULL, 0);
709 return parameterize_string(string, arg);
713 xlw_create_localized_xmstring(Widget w,
714 char *name, char **args, unsigned int nargs)
716 char *string = xlw_create_localized_string(w, name, args, nargs);
718 XmStringCreateLtoR(string, XmSTRING_DEFAULT_CHARSET);
726 static char *resource_widget_value(XlwMenuWidget mw, widget_value * val)
728 if (!val->toolkit_data) {
729 char *resourced_name = NULL;
731 char massaged_name[1024];
733 if (mw->menu.lookup_labels) {
734 massage_resource_name(val->name, massaged_name);
736 XtGetSubresources((Widget) mw,
737 (XtPointer) & resourced_name,
738 massaged_name, massaged_name,
739 nameResource, 1, NULL, 0);
742 resourced_name = val->name;
744 complete_name = parameterize_string(resourced_name, val->value);
746 val->toolkit_data = complete_name;
747 /* nuke newline characters to prevent menubar screwups */
748 for (; *complete_name; complete_name++) {
749 if (complete_name[0] == '\n')
750 complete_name[0] = ' ';
752 val->free_toolkit_data = True;
754 return (char *)val->toolkit_data;
759 /* Code for drawing strings. */
760 static void string_draw(XlwMenuWidget mw, Window window, int x, int y, GC gc,
769 XmStringDraw(XtDisplay(mw), window, mw->menu.font_list, string, gc, x, y, 1000, /* ???? width */
770 XmALIGNMENT_BEGINNING, 0, /* ???? layout_direction */
774 XmbDrawString(XtDisplay(mw), window, mw->menu.font_set, gc,
775 x, y + mw->menu.font_ascent, string, strlen(string));
777 XDrawString(XtDisplay(mw), window, gc,
778 x, y + mw->menu.font_ascent, string, strlen(string));
779 # endif /* USE_XFONTSET */
785 string_draw_range(XlwMenuWidget mw,
787 int x, int y, GC gc, char *string, int start, int end)
790 Dimension width, height;
798 newstring = XmStringLtoRCreate(&string[start], XmFONTLIST_DEFAULT_TAG);
799 XmStringDraw(XtDisplay(mw), window, mw->menu.font_list, newstring, gc, x, y, 1000, /* ???? width */
800 XmALIGNMENT_BEGINNING, 0, /* ???? layout_direction */
802 XmStringExtent(mw->menu.font_list, newstring, &width, &height);
803 XmStringFree(newstring);
812 XmbDrawString(XtDisplay(mw), window, mw->menu.font_set, gc,
813 x, y + mw->menu.font_ascent, &string[start], end - start);
814 XmbTextExtents(mw->menu.font_set, &string[start], end - start, &ri,
823 XDrawString(XtDisplay(mw), window, gc,
824 x, y + mw->menu.font_ascent, &string[start], end - start);
825 XTextExtents(mw->menu.font, &string[start], end - start,
826 &drop, &drop, &drop, &xcs);
832 static void string_draw_u(XlwMenuWidget mw, Window window, int x, int y, GC gc,
845 if (!XmStringGetLtoR(string, XmFONTLIST_DEFAULT_TAG, &chars))
850 for (i = 0; chars[i]; ++i) {
851 if (chars[i] == '%' && chars[i + 1] == '_') {
854 x += string_draw_range(mw, window, x, y, gc, chars, s,
856 w = string_draw_range(mw, window, x, y, gc, chars,
859 /* underline next character */
860 XDrawLine(XtDisplay(mw), window, gc, x - 1,
861 y + mw->menu.font_ascent + 1,
862 x + w - 1, y + mw->menu.font_ascent + 1);
868 x += string_draw_range(mw, window, x, y, gc, chars, s, i);
875 binding_draw(XlwMenuWidget mw, Window w, int x, int y, GC gc, char *value)
878 XmString xm_value = XmStringCreateLtoR(value, XmSTRING_DEFAULT_CHARSET);
879 string_draw(mw, w, x, y, gc, xm_value);
880 XmStringFree(xm_value);
882 string_draw(mw, w, x, y, gc, value);
886 /* Low level code for drawing 3-D edges. */
888 shadow_rectangle_draw(Display * dpy,
894 unsigned int height, unsigned int thickness)
903 points[1].x = x + width;
905 points[2].x = x + width - thickness;
906 points[2].y = y + thickness;
908 points[3].y = y + thickness;
909 XFillPolygon(dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
911 points[0].y = y + thickness;
913 points[1].y = y + height;
914 points[2].x = x + thickness;
915 points[2].y = y + height - thickness;
916 points[3].x = x + thickness;
917 points[3].y = y + thickness;
918 XFillPolygon(dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
919 points[0].x = x + width;
921 points[1].x = x + width - thickness;
922 points[1].y = y + thickness;
923 points[2].x = x + width - thickness;
924 points[2].y = y + height - thickness;
925 points[3].x = x + width;
926 points[3].y = y + height - thickness;
927 XFillPolygon(dpy, window, bottom_gc, points, 4, Convex,
930 points[0].y = y + height;
931 points[1].x = x + width;
932 points[1].y = y + height;
933 points[2].x = x + width;
934 points[2].y = y + height - thickness;
935 points[3].x = x + thickness;
936 points[3].y = y + height - thickness;
937 XFillPolygon(dpy, window, bottom_gc, points, 4, Convex,
941 typedef enum e_shadow_type {
942 /* these are Motif compliant */
948 SHADOW_ETCHED_OUT_DASH,
949 SHADOW_ETCHED_IN_DASH,
952 SHADOW_SINGLE_DASHED_LINE,
953 SHADOW_DOUBLE_DASHED_LINE,
955 /* these are all non-Motif */
956 SHADOW_DOUBLE_ETCHED_OUT,
957 SHADOW_DOUBLE_ETCHED_IN,
958 SHADOW_DOUBLE_ETCHED_OUT_DASH,
959 SHADOW_DOUBLE_ETCHED_IN_DASH
963 shadow_draw(XlwMenuWidget mw,
966 unsigned int width, unsigned int height, shadow_type type)
968 Display *dpy = XtDisplay(mw);
971 int thickness = mw->menu.shadow_thickness;
975 Boolean etched = False;
977 switch ((unsigned int)type) {
978 case SHADOW_BACKGROUND:
979 top_gc = bottom_gc = mw->menu.background_gc;
981 case SHADOW_ETCHED_IN:
982 top_gc = mw->menu.shadow_bottom_gc;
983 bottom_gc = mw->menu.shadow_top_gc;
986 case SHADOW_ETCHED_OUT:
987 top_gc = mw->menu.shadow_top_gc;
988 bottom_gc = mw->menu.shadow_bottom_gc;
992 top_gc = mw->menu.shadow_bottom_gc;
993 bottom_gc = mw->menu.shadow_top_gc;
997 top_gc = mw->menu.shadow_top_gc;
998 bottom_gc = mw->menu.shadow_bottom_gc;
1003 unsigned int half = thickness / 2;
1004 shadow_rectangle_draw(dpy,
1009 width - half, height - half,
1011 shadow_rectangle_draw(dpy,
1016 width - half, height - half, half);
1018 shadow_rectangle_draw(dpy,
1022 x, y, width, height, thickness);
1027 arrow_decoration_draw(XlwMenuWidget mw,
1029 int x, int y, unsigned int width, Boolean raised)
1031 Display *dpy = XtDisplay(mw);
1035 int thickness = mw->menu.shadow_thickness;
1038 int length = (int)((double)width * 0.87);
1039 int thick_med = (int)((double)thickness * 1.73);
1042 half_width = width / 2 + 1;
1044 half_width = width / 2;
1046 select_gc = mw->menu.background_gc;
1049 top_gc = mw->menu.shadow_bottom_gc;
1050 bottom_gc = mw->menu.shadow_top_gc;
1052 top_gc = mw->menu.shadow_top_gc;
1053 bottom_gc = mw->menu.shadow_bottom_gc;
1056 /* Fill internal area. We do this first so that the borders have a
1058 points[0].x = x + thickness;
1059 points[0].y = y + thickness;
1060 points[1].x = x + length - thickness;
1061 points[1].y = y + half_width;
1062 points[2].x = x + length - thickness;
1063 points[2].y = y + half_width + thickness;
1064 points[3].x = x + thickness;
1065 points[3].y = y + width - thickness;
1068 window, select_gc, points, 4, Convex, CoordModeOrigin);
1073 points[1].x = x + thickness;
1074 points[1].y = y + thick_med;
1075 points[2].x = x + thickness;
1076 points[2].y = y + width - thick_med;
1078 points[3].y = y + width;
1080 XFillPolygon(dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
1084 points[0].y = y + width;
1085 points[1].x = x + length;
1086 points[1].y = y + half_width;
1087 points[2].x = x + length - (thickness + thickness);
1088 points[2].y = y + half_width;
1089 points[3].x = x + thickness;
1090 points[3].y = y + width - thick_med;
1092 XFillPolygon(dpy, window, bottom_gc, points, 4, Convex,
1098 points[1].x = x + length;
1099 points[1].y = y + half_width;
1100 points[2].x = x + length - (thickness + thickness);
1101 points[2].y = y + half_width;
1102 points[3].x = x + thickness;
1103 points[3].y = y + thick_med;
1105 XFillPolygon(dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
1109 toggle_decoration_draw(XlwMenuWidget mw,
1111 int x, int y, unsigned int width, Boolean set)
1113 Display *dpy = XtDisplay(mw);
1114 int thickness = mw->menu.shadow_thickness;
1116 GC select_gc = mw->menu.select_gc;
1123 /* Fill internal area. */
1130 width - (2 * thickness),
1131 width - (2 * thickness));
1133 shadow_draw(mw, window, x, y, width, width, type);
1137 radio_decoration_draw(XlwMenuWidget mw,
1139 int x, int y, unsigned int width, Boolean enabled)
1141 Display *dpy = XtDisplay(mw);
1144 GC select_gc = mw->menu.select_gc;
1145 int thickness = mw->menu.shadow_thickness;
1155 half_width = width / 2;
1158 top_gc = mw->menu.shadow_bottom_gc;
1159 bottom_gc = mw->menu.shadow_top_gc;
1161 top_gc = mw->menu.shadow_top_gc;
1162 bottom_gc = mw->menu.shadow_bottom_gc;
1166 /* Draw the bottom first, just in case the regions overlap.
1167 The top should cast the longer shadow. */
1168 points[0].x = x; /* left corner */
1169 points[0].y = y + half_width;
1170 points[1].x = x + half_width; /* bottom corner */
1171 points[1].y = y + width;
1172 points[2].x = x + half_width; /* bottom inside corner */
1173 points[2].y = y + width - thickness;
1174 points[3].x = x + thickness; /* left inside corner */
1175 points[3].y = y + half_width;
1177 XFillPolygon(dpy, window, bottom_gc, points, 4, Convex,
1180 points[0].x = x + half_width; /* bottom corner */
1181 points[0].y = y + width;
1182 points[1].x = x + width; /* right corner */
1183 points[1].y = y + half_width;
1184 points[2].x = x + width - thickness; /* right inside corner */
1185 points[2].y = y + half_width;
1186 points[3].x = x + half_width; /* bottom inside corner */
1187 points[3].y = y + width - thickness;
1189 XFillPolygon(dpy, window, bottom_gc, points, 4, Convex,
1192 points[0].x = x; /* left corner */
1193 points[0].y = y + half_width;
1194 points[1].x = x + half_width; /* top corner */
1196 points[2].x = x + half_width; /* top inside corner */
1197 points[2].y = y + thickness;
1198 points[3].x = x + thickness; /* left inside corner */
1199 points[3].y = y + half_width;
1201 XFillPolygon(dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
1203 points[0].x = x + half_width; /* top corner */
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; /* top inside corner */
1210 points[3].y = y + thickness;
1212 XFillPolygon(dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
1214 /* Draw the bottom first, just in case the regions overlap.
1215 The top should cast the longer shadow. */
1217 points[npoints].x = x; /* left corner */
1218 points[npoints++].y = y + half_width;
1219 points[npoints].x = x + half_width; /* bottom corner */
1220 points[npoints++].y = y + width;
1221 points[npoints].x = x + width; /* right corner */
1222 points[npoints++].y = y + half_width;
1223 points[npoints].x = x + width - thickness; /* right inside corner */
1224 points[npoints++].y = y + half_width;
1225 points[npoints].x = x + half_width; /* bottom inside corner */
1226 points[npoints++].y = y + width - thickness;
1227 points[npoints].x = x + thickness; /* left inside corner */
1228 points[npoints++].y = y + half_width;
1230 XFillPolygon(dpy, window, bottom_gc,
1231 points, npoints, Nonconvex, CoordModeOrigin);
1235 points[npoints].x = x; /* left corner */
1236 points[npoints++].y = y + half_width;
1237 points[npoints].x = x + half_width; /* top corner */
1238 points[npoints++].y = y;
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; /* top inside corner */
1244 points[npoints++].y = y + thickness;
1245 points[npoints].x = x + thickness; /* left inside corner */
1246 points[npoints++].y = y + half_width;
1248 XFillPolygon(dpy, window, top_gc, points, npoints, Nonconvex,
1252 /* Fill internal area. */
1254 points[0].x = x + thickness;
1255 points[0].y = y + half_width;
1256 points[1].x = x + half_width;
1257 points[1].y = y + thickness;
1258 points[2].x = x + width - thickness;
1259 points[2].y = y + half_width;
1260 points[3].x = x + half_width;
1261 points[3].y = y + width - thickness;
1264 select_gc, points, 4, Convex, CoordModeOrigin);
1269 separator_decoration_draw(XlwMenuWidget mw,
1273 Boolean vertical, shadow_type type)
1275 Display *dpy = XtDisplay(mw);
1278 unsigned int offset = 0;
1279 unsigned int num_separators = 1;
1280 unsigned int top_line_thickness = 0;
1281 unsigned int bottom_line_thickness = 0;
1282 Boolean dashed = False;
1284 switch ((unsigned int)type) {
1285 case SHADOW_NO_LINE: /* nothing to do */
1287 case SHADOW_DOUBLE_LINE:
1289 case SHADOW_SINGLE_LINE:
1290 top_gc = bottom_gc = mw->menu.foreground_gc;
1291 top_line_thickness = 1;
1293 case SHADOW_DOUBLE_DASHED_LINE:
1295 case SHADOW_SINGLE_DASHED_LINE:
1296 top_gc = bottom_gc = mw->menu.foreground_gc;
1297 top_line_thickness = 1;
1300 case SHADOW_DOUBLE_ETCHED_OUT_DASH:
1302 case SHADOW_ETCHED_OUT_DASH:
1303 top_gc = mw->menu.shadow_top_gc;
1304 bottom_gc = mw->menu.shadow_bottom_gc;
1305 top_line_thickness = mw->menu.shadow_thickness / 2;
1306 bottom_line_thickness =
1307 mw->menu.shadow_thickness - top_line_thickness;
1310 case SHADOW_DOUBLE_ETCHED_IN_DASH:
1312 case SHADOW_ETCHED_IN_DASH:
1313 top_gc = mw->menu.shadow_bottom_gc;
1314 bottom_gc = mw->menu.shadow_top_gc;
1315 top_line_thickness = mw->menu.shadow_thickness / 2;
1316 bottom_line_thickness =
1317 mw->menu.shadow_thickness - top_line_thickness;
1320 case SHADOW_DOUBLE_ETCHED_OUT:
1322 case SHADOW_ETCHED_OUT:
1323 top_gc = mw->menu.shadow_top_gc;
1324 bottom_gc = mw->menu.shadow_bottom_gc;
1325 top_line_thickness = mw->menu.shadow_thickness / 2;
1326 bottom_line_thickness =
1327 mw->menu.shadow_thickness - top_line_thickness;
1329 case SHADOW_DOUBLE_ETCHED_IN:
1331 case SHADOW_ETCHED_IN:
1333 top_gc = mw->menu.shadow_bottom_gc;
1334 bottom_gc = mw->menu.shadow_top_gc;
1335 top_line_thickness = mw->menu.shadow_thickness / 2;
1336 bottom_line_thickness =
1337 mw->menu.shadow_thickness - top_line_thickness;
1343 values.line_style = LineOnOffDash;
1344 if (top_line_thickness > 0)
1345 XChangeGC(dpy, top_gc, GCLineStyle, &values);
1346 if (bottom_line_thickness > 0 && bottom_gc != top_gc)
1347 XChangeGC(dpy, bottom_gc, GCLineStyle, &values);
1350 while (num_separators--) {
1352 for (i = 0; i < top_line_thickness; i++)
1353 XDrawLine(dpy, window, top_gc, x, y + i, x + width,
1356 for (i = 0; i < bottom_line_thickness; i++)
1357 XDrawLine(dpy, window, bottom_gc,
1358 x, y + top_line_thickness + offset + i,
1360 y + top_line_thickness + offset + i);
1361 y += (top_line_thickness + offset + bottom_line_thickness + 1);
1366 values.line_style = LineSolid;
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);
1374 #define SLOPPY_TYPES 0 /* 0=off, 1=error check, 2=easy to please */
1376 #if SLOPPY_TYPES < 2
1378 static char *wv_types[] = {
1390 static void print_widget_value(widget_value * wv, int just_one, int depth)
1394 for (i = 0; i < depth; i++)
1398 printf("%s(null widget value pointer)\n", d);
1401 printf("%stype: %s\n", d, wv_types[wv->type]);
1403 printf("%sname: %s\n", d, (wv->name ? wv->name : "(null)"));
1406 printf("%sname: %s\n", d, wv->name);
1409 printf("%svalue: %s\n", d, wv->value);
1411 printf("%skey: %s\n", d, wv->key);
1412 printf("%senabled: %d\n", d, wv->enabled);
1414 printf("\n%scontents: \n", d);
1415 print_widget_value(wv->contents, 0, depth + 5);
1417 if (!just_one && wv->next) {
1419 print_widget_value(wv->next, 0, depth);
1422 #endif /* SLOPPY_TYPES < 2 */
1424 static Boolean all_dashes_p(char *s)
1427 if (!s || s[0] == '\0')
1429 for (p = s; *p == '-'; p++) ;
1431 if (*p == '!' || *p == '\0')
1435 #endif /* SLOPPY_TYPES */
1437 static widget_value_type menu_item_type(widget_value * val)
1439 if (val->type != UNSPECIFIED_TYPE)
1442 else if (all_dashes_p(val->name))
1443 return SEPARATOR_TYPE;
1444 else if (val->name && val->name[0] == '\0') /* push right */
1445 return PUSHRIGHT_TYPE;
1446 else if (val->contents) /* cascade */
1447 return CASCADE_TYPE;
1448 else if (val->call_data) /* push button */
1455 return UNSPECIFIED_TYPE; /* Not reached */
1460 label_button_size(XlwMenuWidget mw,
1463 unsigned int *toggle_width,
1464 unsigned int *label_width,
1465 unsigned int *bindings_width, unsigned int *height)
1467 *height = (mw->menu.font_ascent + mw->menu.font_descent +
1468 2 * mw->menu.vertical_margin +
1469 2 * mw->menu.shadow_thickness);
1470 /* no left column decoration */
1471 *toggle_width = mw->menu.horizontal_margin + mw->menu.shadow_thickness;
1473 *label_width = string_width_u(mw, resource_widget_value(mw, val));
1475 mw->menu.horizontal_margin + mw->menu.shadow_thickness;
1479 label_button_draw(XlwMenuWidget mw,
1482 Boolean highlighted,
1486 unsigned int height,
1487 unsigned int label_offset, unsigned int binding_tab)
1489 int y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin;
1494 mw->menu.shadow_thickness + mw->menu.horizontal_margin;
1496 if (highlighted && (in_menubar || val->contents))
1497 gc = mw->menu.highlight_gc;
1498 else if (in_menubar || val->contents)
1499 gc = mw->menu.foreground_gc;
1501 gc = mw->menu.title_gc;
1503 /* Draw the label string. */
1506 x + label_offset, y + y_offset,
1507 gc, resource_widget_value(mw, val));
1511 push_button_size(XlwMenuWidget mw,
1514 unsigned int *toggle_width,
1515 unsigned int *label_width,
1516 unsigned int *bindings_width, unsigned int *height)
1519 label_button_size(mw, val, in_menubar,
1520 toggle_width, label_width, bindings_width, height);
1522 /* key bindings to display? */
1523 if (!in_menubar && val->key) {
1527 XmStringCreateLtoR(val->key, XmSTRING_DEFAULT_CHARSET);
1528 w = string_width(mw, key);
1531 char *key = val->key;
1532 w = string_width(mw, key);
1534 *bindings_width += w + mw->menu.column_spacing;
1539 push_button_draw(XlwMenuWidget mw,
1542 Boolean highlighted,
1546 unsigned int height,
1547 unsigned int label_offset, unsigned int binding_offset)
1549 int y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin;
1552 Boolean menu_pb = in_menubar && (menu_item_type(val) == BUTTON_TYPE);
1554 /* Draw the label string. */
1557 mw->menu.shadow_thickness + mw->menu.horizontal_margin;
1561 gc = mw->menu.highlight_gc;
1563 gc = mw->menu.inactive_gc;
1564 } else if (menu_pb) {
1566 gc = mw->menu.button_gc;
1568 gc = mw->menu.inactive_button_gc;
1571 gc = mw->menu.foreground_gc;
1573 gc = mw->menu.inactive_gc;
1578 x + label_offset, y + y_offset,
1579 gc, resource_widget_value(mw, val));
1581 /* Draw the keybindings */
1583 if (!binding_offset) {
1584 unsigned int s_width =
1585 string_width(mw, resource_widget_value(mw, val));
1587 label_offset + s_width + mw->menu.shadow_thickness;
1589 binding_draw(mw, window,
1590 x + binding_offset + mw->menu.column_spacing,
1591 y + y_offset, gc, val->key);
1594 /* Draw the shadow */
1601 selected ? SHADOW_ETCHED_OUT : SHADOW_ETCHED_IN);
1606 type = SHADOW_BACKGROUND;
1609 shadow_draw(mw, window, x, y, width, height, type);
1612 static unsigned int arrow_decoration_height(XlwMenuWidget mw)
1614 int result = (mw->menu.font_ascent + mw->menu.font_descent) / 2;
1616 result += 2 * mw->menu.shadow_thickness;
1618 if (result > (mw->menu.font_ascent + mw->menu.font_descent))
1619 result = mw->menu.font_ascent + mw->menu.font_descent;
1625 cascade_button_size(XlwMenuWidget mw,
1628 unsigned int *toggle_width,
1629 unsigned int *label_width,
1630 unsigned int *arrow_width, unsigned int *height)
1633 label_button_size(mw, val, in_menubar,
1634 toggle_width, label_width, arrow_width, height);
1635 /* we have a pull aside arrow */
1638 arrow_decoration_height(mw) + mw->menu.column_spacing;
1643 cascade_button_draw(XlwMenuWidget mw,
1646 Boolean highlighted,
1650 unsigned int height,
1651 unsigned int label_offset, unsigned int binding_offset)
1655 /* Draw the label string. */
1656 label_button_draw(mw, val, in_menubar, highlighted,
1657 window, x, y, width, height, label_offset,
1660 /* Draw the pull aside arrow */
1661 if (!in_menubar && val->contents) {
1663 unsigned int arrow_height = arrow_decoration_height(mw);
1666 mw->menu.shadow_thickness + mw->menu.vertical_margin +
1667 (mw->menu.font_ascent + mw->menu.font_descent -
1670 if (!binding_offset) {
1671 unsigned int s_width =
1672 string_width(mw, resource_widget_value(mw, val));
1675 label_offset = mw->menu.shadow_thickness +
1676 mw->menu.horizontal_margin;
1679 label_offset + s_width + mw->menu.shadow_thickness;
1682 arrow_decoration_draw(mw,
1684 x + binding_offset +
1685 mw->menu.column_spacing, y + y_offset,
1686 arrow_height, highlighted);
1689 /* Draw the shadow */
1693 type = SHADOW_BACKGROUND;
1695 shadow_draw(mw, window, x, y, width, height, type);
1698 static unsigned int toggle_decoration_height(XlwMenuWidget mw)
1701 if (mw->menu.indicator_size > 0)
1702 rv = mw->menu.indicator_size;
1704 rv = mw->menu.font_ascent;
1706 if (rv > (mw->menu.font_ascent + mw->menu.font_descent))
1707 rv = mw->menu.font_ascent + mw->menu.font_descent;
1709 /* radio button can't be smaller than its border or a filling
1710 error will occur. */
1711 if (rv < 2 * mw->menu.shadow_thickness)
1712 rv = 2 * mw->menu.shadow_thickness;
1718 toggle_button_size(XlwMenuWidget mw,
1721 unsigned int *toggle_width,
1722 unsigned int *label_width,
1723 unsigned int *bindings_width, unsigned int *height)
1726 push_button_size(mw, val, in_menubar,
1727 toggle_width, label_width, bindings_width, height);
1728 /* we have a toggle */
1729 *toggle_width += toggle_decoration_height(mw) + mw->menu.column_spacing;
1733 toggle_button_draw(XlwMenuWidget mw,
1736 Boolean highlighted,
1740 unsigned int height,
1741 unsigned int label_tab, unsigned int binding_tab)
1745 unsigned int t_height = toggle_decoration_height(mw);
1747 /* Draw a toggle. */
1748 x_offset = mw->menu.shadow_thickness + mw->menu.horizontal_margin;
1749 y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin;
1751 (mw->menu.font_ascent + mw->menu.font_descent - t_height) / 2;
1753 toggle_decoration_draw(mw, window, x + x_offset, y + y_offset,
1754 t_height, val->selected);
1756 /* Draw the pushbutton parts. */
1757 push_button_draw(mw, val, in_menubar, highlighted, window, x, y, width,
1758 height, label_tab, binding_tab);
1761 static unsigned int radio_decoration_height(XlwMenuWidget mw)
1763 return toggle_decoration_height(mw);
1767 radio_button_draw(XlwMenuWidget mw,
1770 Boolean highlighted,
1774 unsigned int height,
1775 unsigned int label_tab, unsigned int binding_tab)
1779 unsigned int r_height = radio_decoration_height(mw);
1781 /* Draw a toggle. */
1782 x_offset = mw->menu.shadow_thickness + mw->menu.horizontal_margin;
1783 y_offset = mw->menu.shadow_thickness + mw->menu.vertical_margin;
1785 (mw->menu.font_ascent + mw->menu.font_descent - r_height) / 2;
1787 radio_decoration_draw(mw, window, x + x_offset, y + y_offset, r_height,
1790 /* Draw the pushbutton parts. */
1791 push_button_draw(mw, val, in_menubar, highlighted, window, x, y, width,
1792 height, label_tab, binding_tab);
1795 static struct _shadow_names {
1798 } shadow_names[] = {
1801 "singleLine", SHADOW_SINGLE_LINE}, {
1802 "doubleLine", SHADOW_DOUBLE_LINE}, {
1803 "singleDashedLine", SHADOW_SINGLE_DASHED_LINE}, {
1804 "doubleDashedLine", SHADOW_DOUBLE_DASHED_LINE}, {
1805 "noLine", SHADOW_NO_LINE}, {
1806 "shadowEtchedIn", SHADOW_ETCHED_IN}, {
1807 "shadowEtchedOut", SHADOW_ETCHED_OUT}, {
1808 "shadowEtchedInDash", SHADOW_ETCHED_IN_DASH}, {
1809 "shadowEtchedOutDash", SHADOW_ETCHED_OUT_DASH},
1812 "shadowDoubleEtchedIn", SHADOW_DOUBLE_ETCHED_IN}, {
1813 "shadowDoubleEtchedOut", SHADOW_DOUBLE_ETCHED_OUT}, {
1814 "shadowDoubleEtchedInDash", SHADOW_DOUBLE_ETCHED_IN_DASH}, {
1815 "shadowDoubleEtchedOutDash", SHADOW_DOUBLE_ETCHED_OUT_DASH}
1818 static shadow_type separator_type(char *name)
1822 for (i = 0; i < (int)(XtNumber(shadow_names)); i++) {
1823 if (strcmp(name, shadow_names[i].name) == 0)
1824 return shadow_names[i].type;
1827 return SHADOW_BACKGROUND;
1831 separator_decoration_height(XlwMenuWidget mw, widget_value * val)
1833 shadow_type st = separator_type(val->value);
1834 switch ((unsigned int)st) {
1835 case SHADOW_NO_LINE:
1836 case SHADOW_SINGLE_LINE:
1837 case SHADOW_SINGLE_DASHED_LINE:
1839 case SHADOW_DOUBLE_LINE:
1840 case SHADOW_DOUBLE_DASHED_LINE:
1842 case SHADOW_DOUBLE_ETCHED_OUT:
1843 case SHADOW_DOUBLE_ETCHED_IN:
1844 case SHADOW_DOUBLE_ETCHED_OUT_DASH:
1845 case SHADOW_DOUBLE_ETCHED_IN_DASH:
1846 return (1 + 2 * mw->menu.shadow_thickness);
1847 case SHADOW_ETCHED_OUT:
1848 case SHADOW_ETCHED_IN:
1850 return mw->menu.shadow_thickness;
1855 separator_size(XlwMenuWidget mw,
1858 unsigned int *toggle_width,
1859 unsigned int *label_width,
1860 unsigned int *rest_width, unsigned int *height)
1862 *height = separator_decoration_height(mw, val);
1864 *toggle_width = *rest_width = 0;
1868 separator_draw(XlwMenuWidget mw,
1871 Boolean highlighted,
1875 unsigned int height,
1876 unsigned int label_tab, unsigned int binding_tab)
1878 unsigned int sep_width;
1885 separator_decoration_draw(mw,
1890 in_menubar, separator_type(val->value));
1894 pushright_size(XlwMenuWidget mw,
1897 unsigned int *toggle_width,
1898 unsigned int *label_width,
1899 unsigned int *rest_width, unsigned int *height)
1901 *height = *label_width = *toggle_width = *rest_width = 0;
1905 size_menu_item(XlwMenuWidget mw,
1908 unsigned int *toggle_width,
1909 unsigned int *label_width,
1910 unsigned int *rest_width, unsigned int *height)
1912 void (*function_ptr) (XlwMenuWidget _mw,
1913 widget_value * _val,
1914 Boolean _in_menubar,
1915 unsigned int *_toggle_width,
1916 unsigned int *_label_width,
1917 unsigned int *_rest_width, unsigned int *_height);
1918 widget_value_type mt = menu_item_type(val);
1920 switch ((unsigned int)mt) {
1923 function_ptr = toggle_button_size;
1925 case SEPARATOR_TYPE:
1926 function_ptr = separator_size;
1928 case INCREMENTAL_TYPE:
1930 function_ptr = cascade_button_size;
1933 function_ptr = push_button_size;
1935 case PUSHRIGHT_TYPE:
1936 function_ptr = pushright_size;
1940 function_ptr = label_button_size;
1944 (*function_ptr) (mw,
1947 toggle_width, label_width, rest_width, height);
1951 display_menu_item(XlwMenuWidget mw,
1955 Boolean highlighted, Boolean horizontal, Boolean just_compute)
1957 int x = where->x /* + mw->menu.shadow_thickness */ ;
1958 int y = where->y /* + mw->menu.shadow_thickness */ ;
1959 unsigned int toggle_width;
1960 unsigned int label_width;
1961 unsigned int binding_width;
1963 unsigned int height;
1964 unsigned int label_tab;
1965 unsigned int binding_tab;
1966 void (*function_ptr) (XlwMenuWidget _mw,
1967 widget_value * _val,
1968 Boolean _in_menubar,
1969 Boolean _highlighted,
1972 unsigned int _width,
1973 unsigned int _height,
1974 unsigned int _label_tab,
1975 unsigned int _binding_tab);
1976 widget_value_type mt;
1978 size_menu_item(mw, val, horizontal,
1979 &toggle_width, &label_width, &binding_width, &height);
1982 width = toggle_width + label_width + binding_width;
1983 height = ws->height - 2 * mw->menu.shadow_thickness;
1985 width = ws->width - 2 * mw->menu.shadow_thickness;
1986 toggle_width = ws->toggle_width;
1987 label_width = ws->label_width;
1996 label_tab = toggle_width;
1997 binding_tab = toggle_width + label_width;
1999 mt = menu_item_type(val);
2000 switch ((unsigned int)mt) {
2002 function_ptr = toggle_button_draw;
2005 function_ptr = radio_button_draw;
2007 case SEPARATOR_TYPE:
2008 function_ptr = separator_draw;
2010 case INCREMENTAL_TYPE:
2012 function_ptr = cascade_button_draw;
2015 function_ptr = push_button_draw;
2018 function_ptr = label_button_draw;
2020 default: /* do no drawing */
2024 (*function_ptr) (mw,
2029 x, y, width, height, label_tab, binding_tab);
2032 static void size_menu(XlwMenuWidget mw, int level)
2034 unsigned int toggle_width;
2035 unsigned int label_width;
2036 unsigned int rest_width;
2037 unsigned int height;
2038 unsigned int max_toggle_width = 0;
2039 unsigned int max_label_width = 0;
2040 unsigned int max_rest_width = 0;
2041 unsigned int max_height = 0;
2042 int horizontal_p = mw->menu.horizontal && (level == 0);
2046 if (level >= mw->menu.old_depth)
2049 ws = &mw->menu.windows[level];
2051 for (val = mw->menu.old_stack[level]->contents; val; val = val->next) {
2056 &label_width, &rest_width, &height);
2059 toggle_width + label_width + rest_width;
2060 if (height > max_height)
2061 max_height = height;
2063 if (max_toggle_width < toggle_width)
2064 max_toggle_width = toggle_width;
2065 if (max_label_width < label_width)
2066 max_label_width = label_width;
2067 if (max_rest_width < rest_width)
2068 max_rest_width = rest_width;
2069 max_height += height;
2073 ws->height = max_height;
2074 ws->width = max_label_width + max_rest_width + max_toggle_width;
2075 ws->toggle_width = max_toggle_width;
2076 ws->label_width = max_label_width;
2078 ws->width += 2 * mw->menu.shadow_thickness;
2079 ws->height += 2 * mw->menu.shadow_thickness;
2083 display_menu(XlwMenuWidget mw, int level, Boolean just_compute_p,
2084 XPoint * highlighted_pos, XPoint * hit, widget_value ** hit_return,
2085 widget_value * this, widget_value * that)
2088 widget_value *following_item;
2091 int horizontal_p = mw->menu.horizontal && (level == 0);
2093 int just_compute_this_one_p;
2095 if (level >= mw->menu.old_depth)
2098 if (level < mw->menu.old_depth - 1)
2099 following_item = mw->menu.old_stack[level + 1];
2101 if (lw_menu_accelerate
2102 && level == mw->menu.old_depth - 1
2103 && mw->menu.old_stack[level]->type == CASCADE_TYPE)
2104 just_compute_p = True;
2105 following_item = NULL;
2108 #if SLOPPY_TYPES == 1
2109 puts("===================================================================");
2110 print_widget_value(following_item, 1, 0);
2116 where.x = mw->menu.shadow_thickness;
2117 where.y = mw->menu.shadow_thickness;
2119 ws = &mw->menu.windows[level];
2120 for (val = mw->menu.old_stack[level]->contents; val; val = val->next) {
2123 highlighted_p = (val == following_item);
2124 /* If this is the partition (the dummy item which says that menus
2125 after this should be flushright) then figure out how big the
2126 following items are. This means we walk down the tail of the
2127 list twice, but that's no big deal - it's short.
2129 if (horizontal_p && (menu_item_type(val) == PUSHRIGHT_TYPE)) {
2131 XPoint flushright_size;
2133 flushright_size.x = 0;
2134 flushright_size.y = 0;
2135 for (rest = val; rest; rest = rest->next)
2136 display_menu_item(mw, rest, ws,
2138 highlighted_p, horizontal_p,
2141 ws->width - (flushright_size.x +
2142 mw->menu.shadow_thickness);
2143 if (new_x > where.x)
2145 /* We know what we need; don't draw this item. */
2149 if (highlighted_p && highlighted_pos) {
2151 highlighted_pos->x = where.x;
2153 highlighted_pos->y = where.y;
2156 just_compute_this_one_p =
2157 just_compute_p || ((this || that) && val != this
2162 display_menu_item(mw, val, ws, &where, highlighted_p,
2163 horizontal_p, just_compute_this_one_p);
2165 if (highlighted_p && highlighted_pos) {
2167 highlighted_pos->y = ws->height;
2169 highlighted_pos->x = ws->width;
2172 if (hit && !*hit_return) {
2173 if (horizontal_p && hit->x > start.x
2174 && hit->x <= where.x)
2176 else if (!horizontal_p && hit->y > start.y
2177 && hit->y <= where.y)
2182 where.y = mw->menu.shadow_thickness;
2184 where.x = mw->menu.shadow_thickness;
2187 /* Draw slab edges around menu */
2188 if (!just_compute_p)
2189 shadow_draw(mw, ws->window, 0, 0, ws->width, ws->height,
2194 static void set_new_state(XlwMenuWidget mw, widget_value * val, int level)
2198 mw->menu.new_depth = 0;
2199 for (i = 0; i < level; i++)
2200 push_new_stack(mw, mw->menu.old_stack[i]);
2202 push_new_stack(mw, val);
2205 static void make_windows_if_needed(XlwMenuWidget mw, int n)
2209 XSetWindowAttributes xswa;
2214 window_state *windows;
2217 if (mw->menu.windows_length >= n)
2220 root = RootWindowOfScreen(XtScreen(mw));
2221 /* grab the visual and depth from the nearest shell ancestor */
2222 visual = CopyFromParent;
2223 depth = CopyFromParent;
2225 while (visual == CopyFromParent && p) {
2227 visual = ((ShellWidget) p)->shell.visual;
2228 depth = p->core.depth;
2233 xswa.save_under = True;
2234 xswa.override_redirect = True;
2235 xswa.background_pixel = mw->core.background_pixel;
2236 xswa.border_pixel = mw->core.border_pixel;
2237 xswa.event_mask = (ExposureMask | ButtonMotionMask
2238 | ButtonReleaseMask | ButtonPressMask);
2239 xswa.cursor = mw->menu.cursor_shape;
2240 xswa.colormap = mw->core.colormap;
2241 mask = CWSaveUnder | CWOverrideRedirect | CWBackPixel | CWBorderPixel
2242 | CWEventMask | CWCursor | CWColormap;
2244 if (mw->menu.use_backing_store) {
2245 xswa.backing_store = Always;
2246 mask |= CWBackingStore;
2249 if (!mw->menu.windows) {
2251 (window_state *) XtMalloc(n * sizeof(window_state));
2255 (window_state *) XtRealloc((char *)mw->menu.windows,
2256 n * sizeof(window_state));
2257 start_at = mw->menu.windows_length;
2259 mw->menu.windows_length = n;
2261 windows = mw->menu.windows;
2263 for (i = start_at; i < n; i++) {
2266 windows[i].width = 1;
2267 windows[i].height = 1;
2269 XCreateWindow(XtDisplay(mw),
2272 0, depth, CopyFromParent, visual, mask,
2277 /* Make the window fit in the screen */
2279 fit_to_screen(XlwMenuWidget mw, window_state * ws, window_state * previous_ws,
2280 Boolean horizontal_p)
2282 int screen_width = WidthOfScreen(XtScreen(mw));
2283 int screen_height = HeightOfScreen(XtScreen(mw));
2287 else if ((int)(ws->x + ws->width) > screen_width) {
2289 ws->x = previous_ws->x - ws->width;
2291 ws->x = screen_width - ws->width;
2293 /* This check is to make sure we cut off the right side
2294 instead of the left side if the menu is wider than the
2302 else if ((int)(ws->y + ws->height) > screen_height) {
2304 /* A pulldown must either be entirely above or below the menubar.
2305 If we're here, the pulldown doesn't fit below the menubar, so
2306 let's determine if it will fit above the menubar.
2307 Only put it above if there is more room above than below.
2308 Note shadow_thickness offset to allow for slab surround.
2310 if (ws->y > (screen_height / 2))
2312 previous_ws->y - ws->height +
2313 mw->menu.shadow_thickness;
2315 ws->y = screen_height - ws->height;
2316 /* if it's taller than the screen, display the topmost part
2317 that will fit, beginning at the top of the screen. */
2324 /* Updates old_stack from new_stack and redisplays. */
2325 static void remap_menubar(XlwMenuWidget mw)
2329 XPoint selection_position;
2330 int old_depth = mw->menu.old_depth;
2331 int new_depth = mw->menu.new_depth;
2332 widget_value **old_stack;
2333 widget_value **new_stack;
2334 window_state *windows;
2335 widget_value *old_selection;
2336 widget_value *new_selection;
2338 /* Check that enough windows and old_stack are ready. */
2339 make_windows_if_needed(mw, new_depth);
2340 make_old_stack_space(mw, new_depth);
2341 windows = mw->menu.windows;
2342 old_stack = mw->menu.old_stack;
2343 new_stack = mw->menu.new_stack;
2345 /* compute the last identical different entry */
2346 for (i = 1; i < old_depth && i < new_depth; i++)
2347 if (old_stack[i] != new_stack[i])
2351 if (lw_menu_accelerate
2353 && last_same == old_depth - 1 && old_stack[last_same]->contents)
2356 /* Memorize the previously selected item to be able to refresh it */
2358 last_same + 1 < old_depth ? old_stack[last_same + 1] : NULL;
2360 last_same + 1 < new_depth ? new_stack[last_same + 1] : NULL;
2362 /* updates old_state from new_state. It has to be done now because
2363 display_menu (called below) uses the old_stack to know what to display. */
2364 for (i = last_same + 1; i < new_depth; i++)
2365 old_stack[i] = new_stack[i];
2367 mw->menu.old_depth = new_depth;
2369 /* refresh the last selection */
2370 selection_position.x = 0;
2371 selection_position.y = 0;
2372 display_menu(mw, last_same, new_selection == old_selection,
2373 &selection_position, NULL, NULL, old_selection,
2376 /* Now popup the new menus */
2377 for (i = last_same + 1; i < new_depth && new_stack[i]->contents; i++) {
2378 window_state *previous_ws = &windows[i - 1];
2379 window_state *ws = &windows[i];
2381 if (lw_menu_accelerate && i == new_depth - 1)
2384 ws->x = previous_ws->x + selection_position.x;
2385 ws->y = previous_ws->y + selection_position.y;
2387 /* take into account the slab around the new menu */
2388 ws->y -= mw->menu.shadow_thickness;
2391 widget_value *val = mw->menu.old_stack[i];
2392 if (val->contents->type == INCREMENTAL_TYPE) {
2393 /* okay, we're now doing a lisp callback to incrementally generate
2394 more of the menu. */
2395 XtCallCallbackList((Widget) mw,
2397 (XtPointer) val->contents);
2403 fit_to_screen(mw, ws, previous_ws, mw->menu.horizontal
2406 XClearWindow(XtDisplay(mw), ws->window);
2407 XMoveResizeWindow(XtDisplay(mw), ws->window, ws->x, ws->y,
2408 ws->width, ws->height);
2409 XMapRaised(XtDisplay(mw), ws->window);
2410 display_menu(mw, i, False, &selection_position, NULL, NULL,
2414 /* unmap the menus that popped down */
2416 last_same = new_depth;
2417 if (lw_menu_accelerate
2418 && last_same > 1 && new_stack[last_same - 1]->contents)
2421 for (i = last_same - 1; i < old_depth; i++)
2422 if (i >= last_same || !new_stack[i]->contents)
2423 XUnmapWindow(XtDisplay(mw), windows[i].window);
2427 motion_event_is_in_menu(XlwMenuWidget mw, XMotionEvent * ev, int level,
2428 XPoint * relative_pos)
2430 window_state *ws = &mw->menu.windows[level];
2431 int x = level == 0 ? ws->x : ws->x + mw->menu.shadow_thickness;
2432 int y = level == 0 ? ws->y : ws->y + mw->menu.shadow_thickness;
2433 relative_pos->x = ev->x_root - x;
2434 relative_pos->y = ev->y_root - y;
2435 return (x < ev->x_root && ev->x_root < (int)(x + ws->width) &&
2436 y < ev->y_root && ev->y_root < (int)(y + ws->height));
2440 map_event_to_widget_value(XlwMenuWidget mw, XMotionEvent * ev,
2441 widget_value ** val_ptr, int *level,
2442 Boolean * inside_menu)
2445 XPoint relative_pos;
2449 *inside_menu = False;
2451 /* Find the window */
2453 for (i = mw->menu.old_depth - 1; i >= 0; i--)
2455 for (i = 0; i <= mw->menu.old_depth - 1; i++)
2458 ws = &mw->menu.windows[i];
2459 if (ws && motion_event_is_in_menu(mw, ev, i, &relative_pos)) {
2460 *inside_menu = True; /* special logic for menubar below... */
2461 if ((ev->type == ButtonPress) || (ev->state != 0)) {
2462 display_menu(mw, i, True, NULL, &relative_pos,
2463 val_ptr, NULL, NULL);
2466 *inside_menu = True;
2468 } else if (mw->menu.horizontal || i == 0) {
2469 /* if we're clicking on empty part of the menubar, then
2470 unpost the stay-up menu */
2471 *inside_menu = False;
2480 static void make_drawing_gcs(XlwMenuWidget mw)
2483 unsigned long flags = (GCFont | GCForeground | GCBackground);
2486 xgcv.font = default_font_of_font_list(mw->menu.font_list)->fid;
2488 xgcv.font = mw->menu.font->fid;
2491 xgcv.foreground = mw->core.background_pixel;
2492 xgcv.background = mw->menu.foreground;
2493 mw->menu.background_gc = XtGetGC((Widget) mw, flags, &xgcv);
2495 xgcv.foreground = mw->menu.foreground;
2496 xgcv.background = mw->core.background_pixel;
2497 mw->menu.foreground_gc = XtGetGC((Widget) mw, flags, &xgcv);
2499 if (mw->menu.select_color != (Pixel) - 1) {
2500 xgcv.foreground = mw->menu.select_color;
2502 Display *dpy = XtDisplay(mw);
2503 if (CellsOfScreen(DefaultScreenOfDisplay(dpy)) <= 2) { /* mono */
2504 xgcv.foreground = mw->menu.foreground;
2505 } else { /* color */
2507 Colormap cmap = mw->core.colormap;
2508 xcolor.pixel = mw->core.background_pixel;
2509 XQueryColor(dpy, cmap, &xcolor);
2510 xcolor.red = (xcolor.red * 17) / 20;
2511 xcolor.green = (xcolor.green * 17) / 20;
2512 xcolor.blue = (xcolor.blue * 17) / 20;
2513 if (allocate_nearest_color(dpy, cmap, &xcolor))
2514 xgcv.foreground = xcolor.pixel;
2517 xgcv.background = mw->core.background_pixel;
2518 mw->menu.select_gc = XtGetGC((Widget) mw, flags, &xgcv);
2520 xgcv.foreground = mw->menu.foreground;
2521 xgcv.background = mw->core.background_pixel;
2522 xgcv.fill_style = FillStippled;
2523 xgcv.stipple = mw->menu.gray_pixmap;
2524 mw->menu.inactive_gc = XtGetGC((Widget) mw,
2525 (flags | GCFillStyle | GCStipple),
2528 xgcv.foreground = mw->menu.highlight_foreground;
2529 xgcv.background = mw->core.background_pixel;
2530 mw->menu.highlight_gc = XtGetGC((Widget) mw, flags, &xgcv);
2532 xgcv.foreground = mw->menu.title_foreground;
2533 xgcv.background = mw->core.background_pixel;
2534 mw->menu.title_gc = XtGetGC((Widget) mw, flags, &xgcv);
2536 xgcv.foreground = mw->menu.button_foreground;
2537 xgcv.background = mw->core.background_pixel;
2538 mw->menu.button_gc = XtGetGC((Widget) mw, flags, &xgcv);
2540 xgcv.fill_style = FillStippled;
2541 xgcv.stipple = mw->menu.gray_pixmap;
2542 mw->menu.inactive_button_gc = XtGetGC((Widget) mw,
2543 (flags | GCFillStyle | GCStipple),
2547 static void release_drawing_gcs(XlwMenuWidget mw)
2549 XtReleaseGC((Widget) mw, mw->menu.foreground_gc);
2550 XtReleaseGC((Widget) mw, mw->menu.button_gc);
2551 XtReleaseGC((Widget) mw, mw->menu.highlight_gc);
2552 XtReleaseGC((Widget) mw, mw->menu.title_gc);
2553 XtReleaseGC((Widget) mw, mw->menu.inactive_gc);
2554 XtReleaseGC((Widget) mw, mw->menu.inactive_button_gc);
2555 XtReleaseGC((Widget) mw, mw->menu.background_gc);
2556 XtReleaseGC((Widget) mw, mw->menu.select_gc);
2557 /* let's get some segvs if we try to use these... */
2558 mw->menu.foreground_gc = (GC) - 1;
2559 mw->menu.button_gc = (GC) - 1;
2560 mw->menu.highlight_gc = (GC) - 1;
2561 mw->menu.title_gc = (GC) - 1;
2562 mw->menu.inactive_gc = (GC) - 1;
2563 mw->menu.inactive_button_gc = (GC) - 1;
2564 mw->menu.background_gc = (GC) - 1;
2565 mw->menu.select_gc = (GC) - 1;
2568 #define MINL(x,y) ((((unsigned long) (x)) < ((unsigned long) (y))) \
2569 ? ((unsigned long) (x)) : ((unsigned long) (y)))
2571 static void make_shadow_gcs(XlwMenuWidget mw)
2574 unsigned long pm = 0;
2575 Display *dpy = XtDisplay((Widget) mw);
2576 Colormap cmap = mw->core.colormap;
2578 int top_frobbed = 0, bottom_frobbed = 0;
2580 if (mw->menu.top_shadow_color == (Pixel) (-1))
2581 mw->menu.top_shadow_color = mw->core.background_pixel;
2582 if (mw->menu.bottom_shadow_color == (Pixel) (-1))
2583 mw->menu.bottom_shadow_color = mw->menu.foreground;
2585 if (mw->menu.top_shadow_color == mw->core.background_pixel ||
2586 mw->menu.top_shadow_color == mw->menu.foreground) {
2587 topc.pixel = mw->core.background_pixel;
2588 XQueryColor(dpy, cmap, &topc);
2589 /* don't overflow/wrap! */
2590 topc.red = MINL(65535, topc.red * 1.2);
2591 topc.green = MINL(65535, topc.green * 1.2);
2592 topc.blue = MINL(65535, topc.blue * 1.2);
2593 if (allocate_nearest_color(dpy, cmap, &topc)) {
2594 if (topc.pixel == mw->core.background_pixel) {
2595 XFreeColors(dpy, cmap, &topc.pixel, 1, 0);
2596 topc.red = MINL(65535, topc.red + 0x8000);
2597 topc.green = MINL(65535, topc.green + 0x8000);
2598 topc.blue = MINL(65535, topc.blue + 0x8000);
2599 if (allocate_nearest_color(dpy, cmap, &topc)) {
2600 mw->menu.top_shadow_color = topc.pixel;
2603 mw->menu.top_shadow_color = topc.pixel;
2609 if (mw->menu.bottom_shadow_color == mw->menu.foreground ||
2610 mw->menu.bottom_shadow_color == mw->core.background_pixel) {
2611 botc.pixel = mw->core.background_pixel;
2612 XQueryColor(dpy, cmap, &botc);
2613 botc.red = (botc.red * 3) / 5;
2614 botc.green = (botc.green * 3) / 5;
2615 botc.blue = (botc.blue * 3) / 5;
2616 if (allocate_nearest_color(dpy, cmap, &botc)) {
2617 if (botc.pixel == mw->core.background_pixel) {
2618 XFreeColors(dpy, cmap, &botc.pixel, 1, 0);
2619 botc.red = MINL(65535, botc.red + 0x4000);
2620 botc.green = MINL(65535, botc.green + 0x4000);
2621 botc.blue = MINL(65535, botc.blue + 0x4000);
2622 if (allocate_nearest_color(dpy, cmap, &botc)) {
2623 mw->menu.bottom_shadow_color =
2627 mw->menu.bottom_shadow_color = botc.pixel;
2634 if (top_frobbed && bottom_frobbed) {
2636 ((topc.red / 3) + (topc.green / 3) + (topc.blue / 3));
2638 ((botc.red / 3) + (botc.green / 3) + (botc.blue / 3));
2639 if (bot_avg > top_avg) {
2640 Pixel tmp = mw->menu.top_shadow_color;
2641 mw->menu.top_shadow_color =
2642 mw->menu.bottom_shadow_color;
2643 mw->menu.bottom_shadow_color = tmp;
2644 } else if (topc.pixel == botc.pixel) {
2645 if (botc.pixel == mw->menu.foreground)
2646 mw->menu.top_shadow_color =
2647 mw->core.background_pixel;
2649 mw->menu.bottom_shadow_color =
2650 mw->menu.foreground;
2654 if (!mw->menu.top_shadow_pixmap &&
2655 mw->menu.top_shadow_color == mw->core.background_pixel) {
2656 mw->menu.top_shadow_pixmap = mw->menu.gray_pixmap;
2657 mw->menu.top_shadow_color = mw->menu.foreground;
2659 if (!mw->menu.bottom_shadow_pixmap &&
2660 mw->menu.bottom_shadow_color == mw->core.background_pixel) {
2661 mw->menu.bottom_shadow_pixmap = mw->menu.gray_pixmap;
2662 mw->menu.bottom_shadow_color = mw->menu.foreground;
2665 xgcv.fill_style = FillOpaqueStippled;
2666 xgcv.foreground = mw->menu.top_shadow_color;
2667 xgcv.background = mw->core.background_pixel;
2668 /* xgcv.stipple = mw->menu.top_shadow_pixmap; gtb */
2669 if (mw->menu.top_shadow_pixmap &&
2670 mw->menu.top_shadow_pixmap != XmUNSPECIFIED_PIXMAP)
2671 xgcv.stipple = mw->menu.top_shadow_pixmap;
2674 pm = (xgcv.stipple ? GCStipple | GCFillStyle : 0);
2675 mw->menu.shadow_top_gc =
2676 XtGetGC((Widget) mw, GCForeground | GCBackground | pm, &xgcv);
2678 xgcv.foreground = mw->menu.bottom_shadow_color;
2679 /* xgcv.stipple = mw->menu.bottom_shadow_pixmap; gtb */
2680 if (mw->menu.bottom_shadow_pixmap &&
2681 mw->menu.bottom_shadow_pixmap != XmUNSPECIFIED_PIXMAP)
2682 xgcv.stipple = mw->menu.bottom_shadow_pixmap;
2685 pm = (xgcv.stipple ? GCStipple | GCFillStyle : 0);
2686 mw->menu.shadow_bottom_gc =
2687 XtGetGC((Widget) mw, GCForeground | GCBackground | pm, &xgcv);
2690 static void release_shadow_gcs(XlwMenuWidget mw)
2692 XtReleaseGC((Widget) mw, mw->menu.shadow_top_gc);
2693 XtReleaseGC((Widget) mw, mw->menu.shadow_bottom_gc);
2696 static void extract_font_extents(XlwMenuWidget mw)
2699 /* Find the maximal ascent/descent of the fonts in the font list
2700 so that all menu items can be the same height... */
2701 mw->menu.font_ascent = 0;
2702 mw->menu.font_descent = 0;
2705 XmFontContext context;
2706 #if (XmVersion >= 1002)
2707 XmFontListEntry fontentry;
2709 XmStringCharSet charset;
2713 if (!XmFontListInitFontContext(&context, mw->menu.font_list))
2715 #if (XmVersion >= 1002)
2716 /* There is a BUG in the 1.2 version of XmFontListGetNextFont() (or more
2717 specifically, in _XmGetFirstFont()) that can cause a null pointer to be
2718 passed to XFontsOfFontSet. Use XmFontListNextEntry(), which is the
2719 newer equivalent, instead. Also, it supports font sets, and the
2720 older function doesn't. */
2721 while ((fontentry = XmFontListNextEntry(context))) {
2724 XtPointer one_of_them =
2725 XmFontListEntryGetFont(fontentry, &rettype);
2726 if (rettype == XmFONT_IS_FONTSET) {
2727 XFontSet fontset = (XFontSet) one_of_them;
2728 XFontStruct **fontstruct_list;
2729 char **fontname_list;
2731 XFontsOfFontSet(fontset, &fontstruct_list,
2733 while (--fontcount >= 0) {
2734 font = fontstruct_list[fontcount];
2736 (int)mw->menu.font_ascent)
2737 mw->menu.font_ascent =
2740 (int)mw->menu.font_descent)
2741 mw->menu.font_descent =
2744 } else { /* XmFONT_IS_FONT */
2746 font = (XFontStruct *) one_of_them;
2747 if (font->ascent > (int)mw->menu.font_ascent)
2748 mw->menu.font_ascent = font->ascent;
2749 if (font->descent > (int)mw->menu.font_descent)
2750 mw->menu.font_descent = font->descent;
2753 #else /* motif 1.1 */
2754 while (XmFontListGetNextFont(context, &charset, &font)) {
2755 if (font->ascent > (int)mw->menu.font_ascent)
2756 mw->menu.font_ascent = font->ascent;
2757 if (font->descent > (int)mw->menu.font_descent)
2758 mw->menu.font_descent = font->descent;
2761 #endif /* Motif version */
2762 XmFontListFreeFontContext(context);
2764 #else /* Not Motif */
2765 # ifdef USE_XFONTSET
2766 XFontStruct **fontstruct_list;
2767 char **fontname_list;
2769 int fontcount = XFontsOfFontSet(mw->menu.font_set, &fontstruct_list,
2771 mw->menu.font_ascent = 0;
2772 mw->menu.font_descent = 0;
2773 # if 0 /* nasty, personal debug, Kazz */
2774 fprintf(stderr, "fontSet count is %d\n", fontcount);
2776 while (--fontcount >= 0) {
2777 font = fontstruct_list[fontcount];
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;
2783 # else /* ! USE_XFONTSET */
2784 mw->menu.font_ascent = mw->menu.font->ascent;
2785 mw->menu.font_descent = mw->menu.font->descent;
2787 #endif /* NEED_MOTIF */
2791 static XFontStruct *default_font_of_font_list(XmFontList font_list)
2793 XFontStruct *font = 0;
2795 /* Xm/Label.c does this: */
2796 _XmFontListGetDefaultFont(font_list, &font);
2799 XmFontContext context;
2800 #if (XmVersion >= 1002)
2801 XmFontListEntry fontentry;
2803 XtPointer one_of_them;
2805 XmStringCharSet charset;
2808 if (!XmFontListInitFontContext(&context, font_list))
2810 #if (XmVersion >= 1002)
2811 /* There is a BUG in the 1.2 version of XmFontListGetNextFont() (or more
2812 specifically, in _XmGetFirstFont()) that can cause a null pointer to be
2813 passed to XFontsOfFontSet. Use XmFontListNextEntry(), which is the
2814 newer equivalent, instead. */
2815 fontentry = XmFontListNextEntry(context);
2816 one_of_them = XmFontListEntryGetFont(fontentry, &rettype);
2817 if (rettype == XmFONT_IS_FONTSET) {
2818 XFontSet fontset = (XFontSet) one_of_them;
2819 XFontStruct **fontstruct_list;
2820 char **fontname_list;
2821 (void)XFontsOfFontSet(fontset, &fontstruct_list,
2823 font = fontstruct_list[0];
2824 } else { /* XmFONT_IS_FONT */
2826 font = (XFontStruct *) one_of_them;
2829 if (!XmFontListGetNextFont(context, &charset, &font))
2833 XmFontListFreeFontContext(context);
2841 #endif /* NEED_MOTIF */
2844 XlwMenuInitialize(Widget request, Widget new, ArgList args, Cardinal * num_args)
2846 /* Get the GCs and the widget size */
2847 XlwMenuWidget mw = (XlwMenuWidget) new;
2849 RootWindowOfScreen(DefaultScreenOfDisplay(XtDisplay(mw)));
2850 Display *display = XtDisplay(mw);
2852 /* mw->menu.cursor = XCreateFontCursor (display, mw->menu.cursor_shape); */
2853 mw->menu.cursor = mw->menu.cursor_shape;
2855 mw->menu.gray_pixmap =
2856 XCreatePixmapFromBitmapData(display, window, (char *)gray_bits,
2857 gray_width, gray_height, 1, 0, 1);
2860 /* #### Even if it's a kludge!!!, we should consider doing the same for
2862 /* The menu.font_list slot came from the *fontList resource (Motif standard.)
2863 The menu.font_list_2 slot came from the *font resource, for backward
2864 compatibility with older versions of this code, and consistency with the
2865 rest of emacs. If both font and fontList are specified, we use fontList.
2866 If only one is specified, we use that. If neither are specified, we
2867 use the "fallback" value. What a kludge!!!
2869 Note that this has the bug that a more general wildcard like "*fontList:"
2870 will override a more specific resource like "Emacs*menubar.font:". But
2871 I can't think of a way around that.
2873 if (mw->menu.font_list) /* if *fontList is specified, use that */
2875 else if (mw->menu.font_list_2) /* else if *font is specified, use that */
2876 mw->menu.font_list = mw->menu.font_list_2;
2877 else /* otherwise use default */
2878 mw->menu.font_list = mw->menu.fallback_font_list;
2881 make_drawing_gcs(mw);
2882 make_shadow_gcs(mw);
2883 extract_font_extents(mw);
2885 mw->menu.popped_up = False;
2886 mw->menu.pointer_grabbed = False;
2887 mw->menu.next_release_must_exit = False;
2889 mw->menu.old_depth = 1;
2890 mw->menu.old_stack = XtNew(widget_value *);
2891 mw->menu.old_stack_length = 1;
2892 mw->menu.old_stack[0] = mw->menu.contents;
2894 mw->menu.new_depth = 0;
2895 mw->menu.new_stack = 0;
2896 mw->menu.new_stack_length = 0;
2897 push_new_stack(mw, mw->menu.contents);
2899 mw->menu.windows = XtNew(window_state);
2900 mw->menu.windows_length = 1;
2901 mw->menu.windows[0].x = 0;
2902 mw->menu.windows[0].y = 0;
2903 mw->menu.windows[0].width = 0;
2904 mw->menu.windows[0].height = 0;
2907 mw->core.width = mw->menu.windows[0].width;
2908 mw->core.height = mw->menu.windows[0].height;
2911 static void XlwMenuClassInitialize(void)
2913 initialize_massaged_resource_char();
2917 XlwMenuRealize(Widget w, Mask * valueMask, XSetWindowAttributes * attributes)
2919 XlwMenuWidget mw = (XlwMenuWidget) w;
2920 XSetWindowAttributes xswa;
2923 (*xlwMenuWidgetClass->core_class.superclass->core_class.realize)
2924 (w, valueMask, attributes);
2926 xswa.save_under = True;
2927 xswa.cursor = mw->menu.cursor_shape;
2928 mask = CWSaveUnder | CWCursor;
2929 if (mw->menu.use_backing_store) {
2930 xswa.backing_store = Always;
2931 mask |= CWBackingStore;
2933 XChangeWindowAttributes(XtDisplay(w), XtWindow(w), mask, &xswa);
2935 mw->menu.windows[0].window = XtWindow(w);
2936 mw->menu.windows[0].x = w->core.x;
2937 mw->menu.windows[0].y = w->core.y;
2938 mw->menu.windows[0].width = w->core.width;
2939 mw->menu.windows[0].height = w->core.height;
2942 /* Only the toplevel menubar/popup is a widget so it's the only one that
2943 receives expose events through Xt. So we repaint all the other panes
2944 when receiving an Expose event. */
2945 static void XlwMenuRedisplay(Widget w, XEvent * ev, Region region)
2947 XlwMenuWidget mw = (XlwMenuWidget) w;
2950 if (mw->core.being_destroyed)
2953 for (i = 0; i < mw->menu.old_depth; i++)
2954 display_menu(mw, i, False, NULL, NULL, NULL, NULL, NULL);
2955 set_new_state(mw, NULL, mw->menu.old_depth); /* #### - ??? */
2956 remap_menubar(mw); /* #### - do these two lines do anything? */
2959 static void XlwMenuDestroy(Widget w)
2962 XlwMenuWidget mw = (XlwMenuWidget) w;
2964 if (mw->menu.pointer_grabbed) {
2965 XtUngrabPointer(w, CurrentTime);
2966 mw->menu.pointer_grabbed = False;
2969 release_drawing_gcs(mw);
2970 release_shadow_gcs(mw);
2972 /* this doesn't come from the resource db but is created explicitly
2973 so we must free it ourselves. */
2974 XFreePixmap(XtDisplay(mw), mw->menu.gray_pixmap);
2975 mw->menu.gray_pixmap = (Pixmap) - 1;
2977 /* Don't free mw->menu.contents because that comes from our creator.
2978 The `*_stack' elements are just pointers into `contents' so leave
2979 that alone too. But free the stacks themselves. */
2980 if (mw->menu.old_stack)
2981 XtFree((char *)mw->menu.old_stack);
2982 if (mw->menu.new_stack)
2983 XtFree((char *)mw->menu.new_stack);
2985 /* Remember, you can't free anything that came from the resource
2986 database. This includes:
2988 mw->menu.top_shadow_pixmap
2989 mw->menu.bottom_shadow_pixmap
2992 Also the color cells of top_shadow_color, bottom_shadow_color,
2993 foreground, and button_foreground will never be freed until this
2994 client exits. Nice, eh?
2997 /* start from 1 because the one in slot 0 is w->core.window */
2998 for (i = 1; i < mw->menu.windows_length; i++)
2999 XDestroyWindow(XtDisplay(mw), mw->menu.windows[i].window);
3000 if (mw->menu.windows)
3001 XtFree((char *)mw->menu.windows);
3005 XlwMenuSetValues(Widget current, Widget request, Widget new, ArgList args,
3006 Cardinal * num_args)
3008 XlwMenuWidget oldmw = (XlwMenuWidget) current;
3009 XlwMenuWidget newmw = (XlwMenuWidget) new;
3010 Boolean redisplay = False;
3013 if (newmw->menu.contents
3014 && newmw->menu.contents->contents
3015 && newmw->menu.contents->contents->change >= VISIBLE_CHANGE)
3018 if (newmw->core.background_pixel != oldmw->core.background_pixel
3019 || newmw->menu.foreground != oldmw->menu.foreground
3020 /* For the XEditResource protocol, which may want to change the font. */
3022 || newmw->menu.font_list != oldmw->menu.font_list
3023 || newmw->menu.font_list_2 != oldmw->menu.font_list_2
3024 || newmw->menu.fallback_font_list != oldmw->menu.fallback_font_list
3026 || newmw->menu.font != oldmw->menu.font
3029 release_drawing_gcs(newmw);
3030 make_drawing_gcs(newmw);
3033 for (i = 0; i < oldmw->menu.windows_length; i++) {
3034 XSetWindowBackground(XtDisplay(oldmw),
3035 oldmw->menu.windows[i].window,
3036 newmw->core.background_pixel);
3037 /* clear windows and generate expose events */
3038 XClearArea(XtDisplay(oldmw),
3039 oldmw->menu.windows[i].window, 0, 0, 0, 0,
3047 static void XlwMenuResize(Widget w)
3049 XlwMenuWidget mw = (XlwMenuWidget) w;
3051 mw->menu.windows[0].width = mw->core.width;
3052 mw->menu.windows[0].height = mw->core.height;
3055 /* Action procedures */
3057 handle_single_motion_event(XlwMenuWidget mw, XMotionEvent * ev,
3064 if (!map_event_to_widget_value(mw, ev, &val, &level, &stay_up)) {
3065 /* we wind up here when: (a) the event is in the menubar, (b) the
3066 event isn't in the menubar or any of the panes, (c) the event is on
3067 a disabled menu item */
3068 pop_new_stack_if_no_contents(mw);
3069 if (select_p && !stay_up) {
3070 /* pop down all menus and exit */
3071 mw->menu.next_release_must_exit = True;
3072 set_new_state(mw, (val = NULL), 1);
3075 /* we wind up here when: (a) the event pops up a pull_right menu,
3076 (b) a menu item that is not disabled is highlighted */
3077 if (select_p && mw->menu.bounce_down
3078 && close_to_reference_time((Widget) mw,
3079 mw->menu.menu_bounce_time,
3081 /* motion can cause more than one event. Don't bounce right back
3082 up if we've just bounced down. */
3084 } else if (select_p && mw->menu.bounce_down &&
3085 mw->menu.last_selected_val &&
3086 (mw->menu.last_selected_val == val)) {
3087 val = NULL; /* assigned to mw->last_selected_val below */
3088 mw->menu.menu_bounce_time = ev->time;
3089 /* popdown last menu if we're selecting the same menu item as we did
3090 last time and the XlwMenu.bounceDown resource is set, if the
3091 item is on the menubar itself, then exit. */
3092 if (level == (mw->menu.popped_up ? 0 : 1))
3093 mw->menu.next_release_must_exit = True;
3095 mw->menu.menu_bounce_time = 0;
3096 set_new_state(mw, val, level);
3098 mw->menu.last_selected_val = val;
3101 /* Sync with the display. Makes it feel better on X terms. */
3102 XFlush(XtDisplay(mw));
3106 handle_motion_event(XlwMenuWidget mw, XMotionEvent * ev, Boolean select_p)
3110 unsigned int state = ev->state;
3111 XMotionEvent *event = ev, dummy;
3113 /* allow motion events to be generated again */
3114 dummy.window = ev->window;
3116 && XQueryPointer(XtDisplay(mw), dummy.window,
3117 &dummy.root, &dummy.subwindow,
3118 &dummy.x_root, &dummy.y_root,
3119 &dummy.x, &dummy.y, &dummy.state)
3120 && dummy.state == state && (dummy.x_root != x || dummy.y_root != y)) {
3121 /* don't handle the event twice or that breaks bounce_down. --Stig */
3122 dummy.type = ev->type;
3126 lw_menu_accelerate = False;
3127 handle_single_motion_event(mw, event, select_p);
3130 Time x_focus_timestamp_really_sucks_fix_me_better;
3132 static void Start(Widget w, XEvent * ev, String * params, Cardinal * num_params)
3134 XlwMenuWidget mw = (XlwMenuWidget) w;
3136 lw_menubar_widget = w;
3138 lw_menu_active = True;
3140 if (!mw->menu.pointer_grabbed) {
3141 mw->menu.menu_post_time = ev->xbutton.time;
3142 mw->menu.menu_bounce_time = 0;
3143 mw->menu.next_release_must_exit = True;
3144 mw->menu.last_selected_val = NULL;
3145 x_focus_timestamp_really_sucks_fix_me_better =
3146 ((XButtonPressedEvent *) ev)->time;
3147 XtCallCallbackList((Widget) mw, mw->menu.open, NULL);
3149 /* notes the absolute position of the menubar window */
3150 mw->menu.windows[0].x = ev->xmotion.x_root - ev->xmotion.x;
3151 mw->menu.windows[0].y = ev->xmotion.y_root - ev->xmotion.y;
3153 XtGrabPointer((Widget) mw, False,
3154 (ButtonMotionMask | ButtonReleaseMask |
3155 ButtonPressMask), GrabModeAsync, GrabModeAsync,
3156 None, mw->menu.cursor_shape,
3157 ((XButtonPressedEvent *) ev)->time);
3158 mw->menu.pointer_grabbed = True;
3161 /* handles the down like a move, slots are mostly compatible */
3162 handle_motion_event(mw, &ev->xmotion, True);
3165 static void Drag(Widget w, XEvent * ev, String * params, Cardinal * num_params)
3167 XlwMenuWidget mw = (XlwMenuWidget) w;
3168 handle_motion_event(mw, &ev->xmotion, False);
3172 Select(Widget w, XEvent * ev, String * params, Cardinal * num_params)
3174 XlwMenuWidget mw = (XlwMenuWidget) w;
3175 widget_value *selected_item =
3176 mw->menu.old_stack[mw->menu.old_depth - 1];
3178 lw_menu_accelerate = False;
3180 /* If user releases the button quickly, without selecting anything,
3181 after the initial down-click that brought the menu up,
3183 if ((selected_item == 0 || selected_item->call_data == 0)
3184 && (!mw->menu.next_release_must_exit
3185 || close_to_reference_time(w, mw->menu.menu_post_time, ev))) {
3186 mw->menu.next_release_must_exit = False;
3190 /* pop down everything */
3191 mw->menu.new_depth = 1;
3194 /* Destroy() only gets called for popup menus. Menubar widgets aren't
3195 destroyed when their menu panes get nuked. */
3196 if (mw->menu.pointer_grabbed) {
3197 XtUngrabPointer((Widget) w, ev->xmotion.time);
3198 mw->menu.pointer_grabbed = False;
3201 if (mw->menu.popped_up) {
3202 mw->menu.popped_up = False;
3203 XtPopdown(XtParent(mw));
3206 lw_menu_active = False;
3208 x_focus_timestamp_really_sucks_fix_me_better =
3209 ((XButtonPressedEvent *) ev)->time;
3212 XtCallCallbackList((Widget) mw, mw->menu.select,
3213 (XtPointer) selected_item);
3216 /* Action procedures for keyboard accelerators */
3219 void xlw_set_menu(Widget w, widget_value * val)
3221 lw_menubar_widget = w;
3222 set_new_state((XlwMenuWidget) w, val, 1);
3225 /* prepare the menu structure via the call-backs */
3226 void xlw_map_menu(Time t)
3228 XlwMenuWidget mw = (XlwMenuWidget) lw_menubar_widget;
3230 lw_menu_accelerate = True;
3232 if (!mw->menu.pointer_grabbed) {
3233 XWindowAttributes ret;
3234 Window parent = 0UL, root = 0UL;
3235 Window *waste = NULL;
3236 unsigned int num_waste;
3238 lw_menu_active = True;
3240 mw->menu.menu_post_time = t;
3241 mw->menu.menu_bounce_time = 0;
3243 mw->menu.next_release_must_exit = True;
3244 mw->menu.last_selected_val = NULL;
3246 XtCallCallbackList((Widget) mw, mw->menu.open, NULL);
3248 /* do this for keyboards too! */
3249 /* notes the absolute position of the menubar window */
3251 mw->menu.windows [0].x = ev->xmotion.x_root - ev->xmotion.x;
3252 mw->menu.windows [0].y = ev->xmotion.y_root - ev->xmotion.y;
3255 /* get the geometry of the menubar */
3257 /* there has to be a better way than this. */
3259 mw->menu.windows[0].x = 0;
3260 mw->menu.windows[0].y = 0;
3262 parent = XtWindow(lw_menubar_widget);
3264 XGetWindowAttributes(XtDisplay(lw_menubar_widget),
3266 mw->menu.windows[0].x += ret.x;
3267 mw->menu.windows[0].y += ret.y;
3270 XQueryTree(XtDisplay(lw_menubar_widget), parent,
3271 &root, &parent, &waste, &num_waste);
3276 while (parent != root);
3278 XtGrabPointer((Widget) mw, False,
3279 (ButtonMotionMask | ButtonReleaseMask |
3280 ButtonPressMask), GrabModeAsync, GrabModeAsync,
3281 None, mw->menu.cursor_shape, t);
3282 mw->menu.pointer_grabbed = True;
3286 /* display the stupid menu already */
3287 void xlw_display_menu(Time t)
3289 XlwMenuWidget mw = (XlwMenuWidget) lw_menubar_widget;
3291 lw_menu_accelerate = True;
3295 /* Sync with the display. Makes it feel better on X terms. */
3296 XFlush(XtDisplay(mw));
3299 /* push a sub menu */
3300 void xlw_push_menu(widget_value * val)
3302 push_new_stack((XlwMenuWidget) lw_menubar_widget, val);
3305 /* pop a sub menu */
3306 int xlw_pop_menu(void)
3308 if (((XlwMenuWidget) lw_menubar_widget)->menu.new_depth > 0)
3309 ((XlwMenuWidget) lw_menubar_widget)->menu.new_depth--;
3315 void xlw_kill_menus(widget_value * val)
3317 XlwMenuWidget mw = (XlwMenuWidget) lw_menubar_widget;
3319 lw_menu_accelerate = False;
3321 mw->menu.new_depth = 1;
3324 if (mw->menu.pointer_grabbed) {
3325 XtUngrabPointer(lw_menubar_widget, CurrentTime);
3326 mw->menu.pointer_grabbed = False;
3329 lw_menu_active = False;
3330 XtCallCallbackList(lw_menubar_widget, mw->menu.select, (XtPointer) val);
3333 /* set the menu item */
3334 void xlw_set_item(widget_value * val)
3336 if (((XlwMenuWidget) lw_menubar_widget)->menu.new_depth > 0)
3337 ((XlwMenuWidget) lw_menubar_widget)->menu.new_depth--;
3338 push_new_stack((XlwMenuWidget) lw_menubar_widget, val);
3341 /* get either the current entry or a list of all entries in the current submenu */
3342 widget_value *xlw_get_entries(int allp)
3344 XlwMenuWidget mw = (XlwMenuWidget) lw_menubar_widget;
3346 if (mw->menu.new_depth >= 2)
3347 return mw->menu.new_stack[mw->menu.new_depth -
3350 return mw->menu.new_stack[0];
3351 } else if (mw->menu.new_depth >= 1)
3352 return mw->menu.new_stack[mw->menu.new_depth - 1];
3357 int xlw_menu_level(void)
3359 return ((XlwMenuWidget) lw_menubar_widget)->menu.new_depth;
3362 /* Special code to pop-up a menu */
3363 void xlw_pop_up_menu(XlwMenuWidget mw, XButtonPressedEvent * event)
3365 int x = event->x_root;
3366 int y = event->y_root;
3369 int borderwidth = mw->menu.shadow_thickness;
3370 Screen *screen = XtScreen(mw);
3372 mw->menu.menu_post_time = event->time;
3373 mw->menu.menu_bounce_time = 0;
3374 mw->menu.next_release_must_exit = True;
3375 mw->menu.last_selected_val = NULL;
3377 XtCallCallbackList((Widget) mw, mw->menu.open, NULL);
3381 w = mw->menu.windows[0].width;
3382 h = mw->menu.windows[0].height;
3387 if (x < borderwidth)
3390 if (x > WidthOfScreen(screen) - w - 2 * borderwidth)
3391 x = WidthOfScreen(screen) - w - 2 * borderwidth;
3393 if (y < borderwidth)
3396 if (y > HeightOfScreen(screen) - h - 2 * borderwidth)
3397 y = HeightOfScreen(screen) - h - 2 * borderwidth;
3399 mw->menu.popped_up = True;
3400 XtConfigureWidget(XtParent(mw), x, y, w, h,
3401 XtParent(mw)->core.border_width);
3402 XtPopup(XtParent(mw), XtGrabExclusive);
3403 display_menu(mw, 0, False, NULL, NULL, NULL, NULL, NULL);
3404 if (!mw->menu.pointer_grabbed) {
3405 XtGrabPointer((Widget) mw, False,
3406 (ButtonMotionMask | ButtonReleaseMask |
3407 ButtonPressMask), GrabModeAsync, GrabModeAsync,
3408 None, mw->menu.cursor_shape, event->time);
3409 mw->menu.pointer_grabbed = True;
3412 mw->menu.windows[0].x = x + borderwidth;
3413 mw->menu.windows[0].y = y + borderwidth;
3415 handle_motion_event(mw, (XMotionEvent *) event, True);
3421 * This is a horrible function which should not be needed.
3422 * use it to put the resize method back the way the XlwMenu
3423 * class initializer put it. Motif screws with this when
3424 * the XlwMenu class gets instantiated.
3426 void xlw_unmunge_class_resize(Widget w)
3428 if (w->core.widget_class->core_class.resize != XlwMenuResize)
3429 w->core.widget_class->core_class.resize = XlwMenuResize;