Split smie-indent-calculate into more manageable chunks.
[emacs.git] / lwlib / xlwmenu.c
blob1bb0d50b6d7dce960232c472da1faaa2120828bb
1 /* Implements a lightweight menubar widget.
2 Copyright (C) 1992 Lucid, Inc.
3 Copyright (C) 1994, 1995, 1997, 1999, 2000, 2001, 2002, 2003, 2004,
4 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
6 This file is part of the Lucid Widget Library.
8 The Lucid Widget Library is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2, or (at your option)
11 any later version.
13 The Lucid Widget Library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with GNU Emacs; see the file COPYING. If not, write to the
20 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 Boston, MA 02110-1301, USA. */
23 /* Created by devin@lucid.com */
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
29 #include <setjmp.h>
30 #include "lisp.h"
32 #include <stdio.h>
33 #include <ctype.h>
35 #include <sys/types.h>
36 #if (defined __sun) && !(defined SUNOS41)
37 #define SUNOS41
38 #include <X11/Xos.h>
39 #undef SUNOS41
40 #else
41 #include <X11/Xos.h>
42 #endif
43 #include <X11/IntrinsicP.h>
44 #include <X11/ObjectP.h>
45 #include <X11/StringDefs.h>
46 #include <X11/cursorfont.h>
47 #include <X11/Shell.h>
48 #include "xlwmenuP.h"
50 #ifdef emacs
52 /* Defined in xfns.c. When config.h defines `static' as empty, we get
53 redefinition errors when gray_bitmap is included more than once, so
54 we're referring to the one include in xfns.c here. */
56 extern int gray_bitmap_width;
57 extern int gray_bitmap_height;
58 extern char *gray_bitmap_bits;
60 #include "xterm.h"
62 #else /* not emacs */
64 #include <X11/bitmaps/gray>
65 #define gray_bitmap_width gray_width
66 #define gray_bitmap_height gray_height
67 #define gray_bitmap_bits gray_bits
69 #endif /* not emacs */
71 static int pointer_grabbed;
72 static XEvent menu_post_event;
74 static XFontStruct *xlwmenu_default_font;
76 static char
77 xlwMenuTranslations [] =
78 "<BtnDown>: start()\n\
79 <Motion>: drag()\n\
80 <BtnUp>: select()\n\
81 <Key>Shift_L: nothing()\n\
82 <Key>Shift_R: nothing()\n\
83 <Key>Meta_L: nothing()\n\
84 <Key>Meta_R: nothing()\n\
85 <Key>Control_L: nothing()\n\
86 <Key>Control_R: nothing()\n\
87 <Key>Hyper_L: nothing()\n\
88 <Key>Hyper_R: nothing()\n\
89 <Key>Super_L: nothing()\n\
90 <Key>Super_R: nothing()\n\
91 <Key>Alt_L: nothing()\n\
92 <Key>Alt_R: nothing()\n\
93 <Key>Caps_Lock: nothing()\n\
94 <Key>Shift_Lock: nothing()\n\
95 <KeyUp>Shift_L: nothing()\n\
96 <KeyUp>Shift_R: nothing()\n\
97 <KeyUp>Meta_L: nothing()\n\
98 <KeyUp>Meta_R: nothing()\n\
99 <KeyUp>Control_L: nothing()\n\
100 <KeyUp>Control_R: nothing()\n\
101 <KeyUp>Hyper_L: nothing()\n\
102 <KeyUp>Hyper_R: nothing()\n\
103 <KeyUp>Super_L: nothing()\n\
104 <KeyUp>Super_R: nothing()\n\
105 <KeyUp>Alt_L: nothing()\n\
106 <KeyUp>Alt_R: nothing()\n\
107 <KeyUp>Caps_Lock: nothing()\n\
108 <KeyUp>Shift_Lock:nothing()\n\
109 <Key>Return: select()\n\
110 <Key>Down: down()\n\
111 <Key>Up: up()\n\
112 <Key>Left: left()\n\
113 <Key>Right: right()\n\
114 <Key>: key()\n\
115 <KeyUp>: key()\n\
118 /* FIXME: Space should toggle toggleable menu item but not remove the menu
119 so you can toggle the next one without entering the menu again. */
121 /* FIXME: Should ESC close one level of menu structure or the complete menu? */
123 /* FIXME: F10 should enter the menu, the first one in the menu-bar. */
125 #define offset(field) XtOffset(XlwMenuWidget, field)
126 static XtResource
127 xlwMenuResources[] =
129 #ifdef HAVE_X_I18N
130 {XtNfontSet, XtCFontSet, XtRFontSet, sizeof(XFontSet),
131 offset(menu.fontSet), XtRFontSet, NULL},
132 #endif
133 #ifdef HAVE_XFT
134 #define DEFAULT_FACENAME "Sans-10"
135 {XtNfaceName, XtCFaceName, XtRString, sizeof(String),
136 offset(menu.faceName), XtRString, DEFAULT_FACENAME},
137 {XtNdefaultFace, XtCDefaultFace, XtRInt, sizeof(int),
138 offset(menu.default_face), XtRImmediate, (XtPointer)1},
139 #endif
140 {XtNfont, XtCFont, XtRFontStruct, sizeof(XFontStruct *),
141 offset(menu.font), XtRString, "XtDefaultFont"},
142 {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
143 offset(menu.foreground), XtRString, "XtDefaultForeground"},
144 {XtNdisabledForeground, XtCDisabledForeground, XtRPixel, sizeof(Pixel),
145 offset(menu.disabled_foreground), XtRString, (XtPointer)NULL},
146 {XtNbuttonForeground, XtCButtonForeground, XtRPixel, sizeof(Pixel),
147 offset(menu.button_foreground), XtRString, "XtDefaultForeground"},
148 {XtNmargin, XtCMargin, XtRDimension, sizeof(Dimension),
149 offset(menu.margin), XtRImmediate, (XtPointer)1},
150 {XtNhorizontalSpacing, XtCMargin, XtRDimension, sizeof(Dimension),
151 offset(menu.horizontal_spacing), XtRImmediate, (XtPointer)3},
152 {XtNverticalSpacing, XtCMargin, XtRDimension, sizeof(Dimension),
153 offset(menu.vertical_spacing), XtRImmediate, (XtPointer)2},
154 {XtNarrowSpacing, XtCMargin, XtRDimension, sizeof(Dimension),
155 offset(menu.arrow_spacing), XtRImmediate, (XtPointer)10},
157 {XmNshadowThickness, XmCShadowThickness, XtRDimension,
158 sizeof (Dimension), offset (menu.shadow_thickness),
159 XtRImmediate, (XtPointer)1},
160 {XmNtopShadowColor, XmCTopShadowColor, XtRPixel, sizeof (Pixel),
161 offset (menu.top_shadow_color), XtRImmediate, (XtPointer)-1},
162 {XmNbottomShadowColor, XmCBottomShadowColor, XtRPixel, sizeof (Pixel),
163 offset (menu.bottom_shadow_color), XtRImmediate, (XtPointer)-1},
164 {XmNtopShadowPixmap, XmCTopShadowPixmap, XtRPixmap, sizeof (Pixmap),
165 offset (menu.top_shadow_pixmap), XtRImmediate, (XtPointer)None},
166 {XmNbottomShadowPixmap, XmCBottomShadowPixmap, XtRPixmap, sizeof (Pixmap),
167 offset (menu.bottom_shadow_pixmap), XtRImmediate, (XtPointer)None},
169 {XtNopen, XtCCallback, XtRCallback, sizeof(XtPointer),
170 offset(menu.open), XtRCallback, (XtPointer)NULL},
171 {XtNselect, XtCCallback, XtRCallback, sizeof(XtPointer),
172 offset(menu.select), XtRCallback, (XtPointer)NULL},
173 {XtNhighlightCallback, XtCCallback, XtRCallback, sizeof(XtPointer),
174 offset(menu.highlight), XtRCallback, (XtPointer)NULL},
175 {XtNenterCallback, XtCCallback, XtRCallback, sizeof(XtPointer),
176 offset(menu.enter), XtRCallback, (XtPointer)NULL},
177 {XtNleaveCallback, XtCCallback, XtRCallback, sizeof(XtPointer),
178 offset(menu.leave), XtRCallback, (XtPointer)NULL},
179 {XtNmenu, XtCMenu, XtRPointer, sizeof(XtPointer),
180 offset(menu.contents), XtRImmediate, (XtPointer)NULL},
181 {XtNcursor, XtCCursor, XtRCursor, sizeof(Cursor),
182 offset(menu.cursor_shape), XtRString, (XtPointer)"right_ptr"},
183 {XtNhorizontal, XtCHorizontal, XtRInt, sizeof(int),
184 offset(menu.horizontal), XtRImmediate, (XtPointer)True},
186 #undef offset
188 static Boolean XlwMenuSetValues();
189 static void XlwMenuRealize();
190 static void XlwMenuResize();
191 static void XlwMenuInitialize();
192 static void XlwMenuRedisplay();
193 static void XlwMenuDestroy();
194 static void XlwMenuClassInitialize();
195 static void Start();
196 static void Drag();
197 static void Down();
198 static void Up();
199 static void Left();
200 static void Right();
201 static void Select();
202 static void Key();
203 static void Nothing();
204 static int separator_height __P ((enum menu_separator));
205 static void pop_up_menu __P ((XlwMenuWidget, XButtonPressedEvent *));
208 static XtActionsRec
209 xlwMenuActionsList [] =
211 {"start", Start},
212 {"drag", Drag},
213 {"down", Down},
214 {"up", Up},
215 {"left", Left},
216 {"right", Right},
217 {"select", Select},
218 {"key", Key},
219 {"MenuGadgetEscape", Key}, /* Compatibility with Lesstif/Motif. */
220 {"nothing", Nothing},
223 #define SuperClass ((CoreWidgetClass)&coreClassRec)
225 XlwMenuClassRec xlwMenuClassRec =
227 { /* CoreClass fields initialization */
228 (WidgetClass) SuperClass, /* superclass */
229 "XlwMenu", /* class_name */
230 sizeof(XlwMenuRec), /* size */
231 XlwMenuClassInitialize, /* class_initialize */
232 NULL, /* class_part_initialize */
233 FALSE, /* class_inited */
234 XlwMenuInitialize, /* initialize */
235 NULL, /* initialize_hook */
236 XlwMenuRealize, /* realize */
237 xlwMenuActionsList, /* actions */
238 XtNumber(xlwMenuActionsList), /* num_actions */
239 xlwMenuResources, /* resources */
240 XtNumber(xlwMenuResources), /* resource_count */
241 NULLQUARK, /* xrm_class */
242 TRUE, /* compress_motion */
243 XtExposeCompressMaximal, /* compress_exposure */
244 TRUE, /* compress_enterleave */
245 FALSE, /* visible_interest */
246 XlwMenuDestroy, /* destroy */
247 XlwMenuResize, /* resize */
248 XlwMenuRedisplay, /* expose */
249 XlwMenuSetValues, /* set_values */
250 NULL, /* set_values_hook */
251 XtInheritSetValuesAlmost, /* set_values_almost */
252 NULL, /* get_values_hook */
253 NULL, /* accept_focus */
254 XtVersion, /* version */
255 NULL, /* callback_private */
256 xlwMenuTranslations, /* tm_table */
257 XtInheritQueryGeometry, /* query_geometry */
258 XtInheritDisplayAccelerator, /* display_accelerator */
259 NULL /* extension */
260 }, /* XlwMenuClass fields initialization */
262 0 /* dummy */
266 WidgetClass xlwMenuWidgetClass = (WidgetClass) &xlwMenuClassRec;
268 int submenu_destroyed;
270 /* For debug, if installation-directory is non-nil this is not an installed
271 Emacs. In that case we do not grab the keyboard to make it easier to
272 debug. */
273 #define GRAB_KEYBOARD (EQ (Vinstallation_directory, Qnil))
275 static int next_release_must_exit;
277 \f/* Utilities */
279 /* Ungrab pointer and keyboard */
280 static void
281 ungrab_all (w, ungrabtime)
282 Widget w;
283 Time ungrabtime;
285 XtUngrabPointer (w, ungrabtime);
286 if (GRAB_KEYBOARD) XtUngrabKeyboard (w, ungrabtime);
289 /* Like abort, but remove grabs from widget W before. */
291 static void
292 abort_gracefully (w)
293 Widget w;
295 if (XtIsShell (XtParent (w)))
296 XtRemoveGrab (w);
297 ungrab_all (w, CurrentTime);
298 abort ();
301 static void
302 push_new_stack (mw, val)
303 XlwMenuWidget mw;
304 widget_value* val;
306 if (!mw->menu.new_stack)
308 mw->menu.new_stack_length = 10;
309 mw->menu.new_stack =
310 (widget_value**)XtCalloc (mw->menu.new_stack_length,
311 sizeof (widget_value*));
313 else if (mw->menu.new_depth == mw->menu.new_stack_length)
315 mw->menu.new_stack_length *= 2;
316 mw->menu.new_stack =
317 (widget_value**)XtRealloc ((char*)mw->menu.new_stack,
318 mw->menu.new_stack_length * sizeof (widget_value*));
320 mw->menu.new_stack [mw->menu.new_depth++] = val;
323 static void
324 pop_new_stack_if_no_contents (mw)
325 XlwMenuWidget mw;
327 if (mw->menu.new_depth > 1)
329 if (!mw->menu.new_stack [mw->menu.new_depth - 1]->contents)
330 mw->menu.new_depth -= 1;
334 static void
335 make_old_stack_space (mw, n)
336 XlwMenuWidget mw;
337 int n;
339 if (!mw->menu.old_stack)
341 mw->menu.old_stack_length = 10;
342 mw->menu.old_stack =
343 (widget_value**)XtCalloc (mw->menu.old_stack_length,
344 sizeof (widget_value*));
346 else if (mw->menu.old_stack_length < n)
348 mw->menu.old_stack_length *= 2;
349 mw->menu.old_stack =
350 (widget_value**)XtRealloc ((char*)mw->menu.old_stack,
351 mw->menu.old_stack_length * sizeof (widget_value*));
355 \f/* Size code */
356 static int
357 string_width (mw, s)
358 XlwMenuWidget mw;
359 char *s;
361 XCharStruct xcs;
362 int drop;
363 #ifdef HAVE_XFT
364 if (mw->menu.xft_font)
366 XGlyphInfo gi;
367 XftTextExtentsUtf8 (XtDisplay (mw), mw->menu.xft_font,
368 (FcChar8 *) s,
369 strlen (s), &gi);
370 return gi.width;
372 #endif
373 #ifdef HAVE_X_I18N
374 if (mw->menu.fontSet)
376 XRectangle ink, logical;
377 XmbTextExtents (mw->menu.fontSet, s, strlen (s), &ink, &logical);
378 return logical.width;
380 #endif
382 XTextExtents (mw->menu.font, s, strlen (s), &drop, &drop, &drop, &xcs);
383 return xcs.width;
387 #ifdef HAVE_XFT
388 #define MENU_FONT_HEIGHT(mw) \
389 ((mw)->menu.xft_font != NULL \
390 ? (mw)->menu.xft_font->height \
391 : ((mw)->menu.fontSet != NULL \
392 ? (mw)->menu.font_extents->max_logical_extent.height \
393 : (mw)->menu.font->ascent + (mw)->menu.font->descent))
394 #define MENU_FONT_ASCENT(mw) \
395 ((mw)->menu.xft_font != NULL \
396 ? (mw)->menu.xft_font->ascent \
397 : ((mw)->menu.fontSet != NULL \
398 ? - (mw)->menu.font_extents->max_logical_extent.y \
399 : (mw)->menu.font->ascent))
400 #else
401 #ifdef HAVE_X_I18N
402 #define MENU_FONT_HEIGHT(mw) \
403 ((mw)->menu.fontSet != NULL \
404 ? (mw)->menu.font_extents->max_logical_extent.height \
405 : (mw)->menu.font->ascent + (mw)->menu.font->descent)
406 #define MENU_FONT_ASCENT(mw) \
407 ((mw)->menu.fontSet != NULL \
408 ? - (mw)->menu.font_extents->max_logical_extent.y \
409 : (mw)->menu.font->ascent)
410 #else
411 #define MENU_FONT_HEIGHT(mw) \
412 ((mw)->menu.font->ascent + (mw)->menu.font->descent)
413 #define MENU_FONT_ASCENT(mw) ((mw)->menu.font->ascent)
414 #endif
415 #endif
417 static int
418 arrow_width (mw)
419 XlwMenuWidget mw;
421 return (MENU_FONT_ASCENT (mw) * 3/4) | 1;
424 /* Return the width of toggle buttons of widget MW. */
426 static int
427 toggle_button_width (mw)
428 XlwMenuWidget mw;
430 return (MENU_FONT_HEIGHT (mw) * 2 / 3) | 1;
434 /* Return the width of radio buttons of widget MW. */
436 static int
437 radio_button_width (mw)
438 XlwMenuWidget mw;
440 return toggle_button_width (mw) * 1.41;
444 static XtResource
445 nameResource[] =
447 {"labelString", "LabelString", XtRString, sizeof(String),
448 0, XtRImmediate, 0},
451 static char*
452 resource_widget_value (mw, val)
453 XlwMenuWidget mw;
454 widget_value *val;
456 if (!val->toolkit_data)
458 char* resourced_name = NULL;
459 char* complete_name;
460 XtGetSubresources ((Widget) mw,
461 (XtPointer) &resourced_name,
462 val->name, val->name,
463 nameResource, 1, NULL, 0);
464 if (!resourced_name)
465 resourced_name = val->name;
466 if (!val->value)
468 complete_name = (char *) XtMalloc (strlen (resourced_name) + 1);
469 strcpy (complete_name, resourced_name);
471 else
473 int complete_length =
474 strlen (resourced_name) + strlen (val->value) + 2;
475 complete_name = XtMalloc (complete_length);
476 *complete_name = 0;
477 strcat (complete_name, resourced_name);
478 strcat (complete_name, " ");
479 strcat (complete_name, val->value);
482 val->toolkit_data = complete_name;
483 val->free_toolkit_data = True;
485 return (char*)val->toolkit_data;
488 /* Returns the sizes of an item */
489 static void
490 size_menu_item (mw, val, horizontal_p, label_width, rest_width, button_width,
491 height)
492 XlwMenuWidget mw;
493 widget_value* val;
494 int horizontal_p;
495 int* label_width;
496 int* rest_width;
497 int* button_width;
498 int* height;
500 enum menu_separator separator;
502 if (lw_separator_p (val->name, &separator, 0))
504 *height = separator_height (separator);
505 *label_width = 1;
506 *rest_width = 0;
507 *button_width = 0;
509 else
511 *height = MENU_FONT_HEIGHT (mw)
512 + 2 * mw->menu.vertical_spacing + 2 * mw->menu.shadow_thickness;
514 *label_width =
515 string_width (mw, resource_widget_value (mw, val))
516 + mw->menu.horizontal_spacing + mw->menu.shadow_thickness;
518 *rest_width = mw->menu.horizontal_spacing + mw->menu.shadow_thickness;
519 if (!horizontal_p)
521 if (val->contents)
522 /* Add width of the arrow displayed for submenus. */
523 *rest_width += arrow_width (mw) + mw->menu.arrow_spacing;
524 else if (val->key)
525 /* Add width of key equivalent string. */
526 *rest_width += (string_width (mw, val->key)
527 + mw->menu.arrow_spacing);
529 if (val->button_type == BUTTON_TYPE_TOGGLE)
530 *button_width = (toggle_button_width (mw)
531 + mw->menu.horizontal_spacing);
532 else if (val->button_type == BUTTON_TYPE_RADIO)
533 *button_width = (radio_button_width (mw)
534 + mw->menu.horizontal_spacing);
539 static void
540 size_menu (mw, level)
541 XlwMenuWidget mw;
542 int level;
544 unsigned int label_width = 0;
545 int rest_width = 0;
546 int button_width = 0;
547 int max_rest_width = 0;
548 int max_button_width = 0;
549 unsigned int height = 0;
550 int horizontal_p = mw->menu.horizontal && (level == 0);
551 widget_value* val;
552 window_state* ws;
554 if (level >= mw->menu.old_depth)
555 abort_gracefully ((Widget) mw);
557 ws = &mw->menu.windows [level];
558 ws->width = 0;
559 ws->height = 0;
560 ws->label_width = 0;
561 ws->button_width = 0;
563 for (val = mw->menu.old_stack [level]->contents; val; val = val->next)
565 size_menu_item (mw, val, horizontal_p, &label_width, &rest_width,
566 &button_width, &height);
567 if (horizontal_p)
569 ws->width += label_width + rest_width;
570 if (height > ws->height)
571 ws->height = height;
573 else
575 if (label_width > ws->label_width)
576 ws->label_width = label_width;
577 if (rest_width > max_rest_width)
578 max_rest_width = rest_width;
579 if (button_width > max_button_width)
580 max_button_width = button_width;
581 ws->height += height;
585 if (horizontal_p)
586 ws->label_width = ws->button_width = 0;
587 else
589 ws->width = ws->label_width + max_rest_width + max_button_width;
590 ws->button_width = max_button_width;
593 ws->width += 2 * mw->menu.shadow_thickness;
594 ws->height += 2 * mw->menu.shadow_thickness;
595 ws->max_rest_width = max_rest_width;
597 if (horizontal_p)
599 ws->width += 2 * mw->menu.margin;
600 ws->height += 2 * mw->menu.margin;
605 \f/* Display code */
607 static void
608 draw_arrow (mw, window, gc, x, y, width, down_p)
609 XlwMenuWidget mw;
610 Window window;
611 GC gc;
612 int x;
613 int y;
614 int width;
615 int down_p;
617 Display *dpy = XtDisplay (mw);
618 GC top_gc = mw->menu.shadow_top_gc;
619 GC bottom_gc = mw->menu.shadow_bottom_gc;
620 int thickness = mw->menu.shadow_thickness;
621 int height = width;
622 XPoint pt[10];
623 /* alpha = atan (0.5)
624 factor = (1 + sin (alpha)) / cos (alpha) */
625 double factor = 1.62;
626 int thickness2 = thickness * factor;
628 y += (MENU_FONT_HEIGHT (mw) - height) / 2;
630 if (down_p)
632 GC temp;
633 temp = top_gc;
634 top_gc = bottom_gc;
635 bottom_gc = temp;
638 pt[0].x = x;
639 pt[0].y = y + height;
640 pt[1].x = x + thickness;
641 pt[1].y = y + height - thickness2;
642 pt[2].x = x + thickness2;
643 pt[2].y = y + thickness2;
644 pt[3].x = x;
645 pt[3].y = y;
646 XFillPolygon (dpy, window, top_gc, pt, 4, Convex, CoordModeOrigin);
648 pt[0].x = x;
649 pt[0].y = y;
650 pt[1].x = x + thickness;
651 pt[1].y = y + thickness2;
652 pt[2].x = x + width - thickness2;
653 pt[2].y = y + height / 2;
654 pt[3].x = x + width;
655 pt[3].y = y + height / 2;
656 XFillPolygon (dpy, window, top_gc, pt, 4, Convex, CoordModeOrigin);
658 pt[0].x = x;
659 pt[0].y = y + height;
660 pt[1].x = x + thickness;
661 pt[1].y = y + height - thickness2;
662 pt[2].x = x + width - thickness2;
663 pt[2].y = y + height / 2;
664 pt[3].x = x + width;
665 pt[3].y = y + height / 2;
666 XFillPolygon (dpy, window, bottom_gc, pt, 4, Convex, CoordModeOrigin);
671 static void
672 draw_shadow_rectangle (mw, window, x, y, width, height, erase_p, down_p)
673 XlwMenuWidget mw;
674 Window window;
675 int x;
676 int y;
677 int width;
678 int height;
679 int erase_p;
680 int down_p;
682 Display *dpy = XtDisplay (mw);
683 GC top_gc = !erase_p ? mw->menu.shadow_top_gc : mw->menu.background_gc;
684 GC bottom_gc = !erase_p ? mw->menu.shadow_bottom_gc : mw->menu.background_gc;
685 int thickness = mw->menu.shadow_thickness;
686 XPoint points [4];
688 if (!erase_p && down_p)
690 GC temp;
691 temp = top_gc;
692 top_gc = bottom_gc;
693 bottom_gc = temp;
696 points [0].x = x;
697 points [0].y = y;
698 points [1].x = x + width;
699 points [1].y = y;
700 points [2].x = x + width - thickness;
701 points [2].y = y + thickness;
702 points [3].x = x;
703 points [3].y = y + thickness;
704 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
705 points [0].x = x;
706 points [0].y = y + thickness;
707 points [1].x = x;
708 points [1].y = y + height;
709 points [2].x = x + thickness;
710 points [2].y = y + height - thickness;
711 points [3].x = x + thickness;
712 points [3].y = y + thickness;
713 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
714 points [0].x = x + width;
715 points [0].y = y;
716 points [1].x = x + width - thickness;
717 points [1].y = y + thickness;
718 points [2].x = x + width - thickness;
719 points [2].y = y + height - thickness;
720 points [3].x = x + width;
721 points [3].y = y + height - thickness;
722 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
723 points [0].x = x;
724 points [0].y = y + height;
725 points [1].x = x + width;
726 points [1].y = y + height;
727 points [2].x = x + width;
728 points [2].y = y + height - thickness;
729 points [3].x = x + thickness;
730 points [3].y = y + height - thickness;
731 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
735 static void
736 draw_shadow_rhombus (mw, window, x, y, width, height, erase_p, down_p)
737 XlwMenuWidget mw;
738 Window window;
739 int x;
740 int y;
741 int width;
742 int height;
743 int erase_p;
744 int down_p;
746 Display *dpy = XtDisplay (mw);
747 GC top_gc = !erase_p ? mw->menu.shadow_top_gc : mw->menu.background_gc;
748 GC bottom_gc = !erase_p ? mw->menu.shadow_bottom_gc : mw->menu.background_gc;
749 int thickness = mw->menu.shadow_thickness;
750 XPoint points [4];
752 if (!erase_p && down_p)
754 GC temp;
755 temp = top_gc;
756 top_gc = bottom_gc;
757 bottom_gc = temp;
760 points [0].x = x;
761 points [0].y = y + height / 2;
762 points [1].x = x + thickness;
763 points [1].y = y + height / 2;
764 points [2].x = x + width / 2;
765 points [2].y = y + thickness;
766 points [3].x = x + width / 2;
767 points [3].y = y;
768 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
769 points [0].x = x + width / 2;
770 points [0].y = y;
771 points [1].x = x + width / 2;
772 points [1].y = y + thickness;
773 points [2].x = x + width - thickness;
774 points [2].y = y + height / 2;
775 points [3].x = x + width;
776 points [3].y = y + height / 2;
777 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
778 points [0].x = x;
779 points [0].y = y + height / 2;
780 points [1].x = x + thickness;
781 points [1].y = y + height / 2;
782 points [2].x = x + width / 2;
783 points [2].y = y + height - thickness;
784 points [3].x = x + width / 2;
785 points [3].y = y + height;
786 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
787 points [0].x = x + width / 2;
788 points [0].y = y + height;
789 points [1].x = x + width / 2;
790 points [1].y = y + height - thickness;
791 points [2].x = x + width - thickness;
792 points [2].y = y + height / 2;
793 points [3].x = x + width;
794 points [3].y = y + height / 2;
795 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
799 /* Draw a toggle button on widget MW, X window WINDOW. X/Y is the
800 top-left corner of the menu item. SELECTED_P non-zero means the
801 toggle button is selected. */
803 static void
804 draw_toggle (mw, window, x, y, selected_p)
805 XlwMenuWidget mw;
806 Window window;
807 int x, y, selected_p;
809 int width, height;
811 width = toggle_button_width (mw);
812 height = width;
813 x += mw->menu.horizontal_spacing;
814 y += (MENU_FONT_ASCENT (mw) - height) / 2;
815 draw_shadow_rectangle (mw, window, x, y, width, height, False, selected_p);
819 /* Draw a radio button on widget MW, X window WINDOW. X/Y is the
820 top-left corner of the menu item. SELECTED_P non-zero means the
821 toggle button is selected. */
823 static void
824 draw_radio (mw, window, x, y, selected_p)
825 XlwMenuWidget mw;
826 Window window;
827 int x, y, selected_p;
829 int width, height;
831 width = radio_button_width (mw);
832 height = width;
833 x += mw->menu.horizontal_spacing;
834 y += (MENU_FONT_ASCENT (mw) - height) / 2;
835 draw_shadow_rhombus (mw, window, x, y, width, height, False, selected_p);
839 /* Draw a menu separator on widget MW, X window WINDOW. X/Y is the
840 top-left corner of the menu item. WIDTH is the width of the
841 separator to draw. TYPE is the separator type. */
843 static void
844 draw_separator (mw, window, x, y, width, type)
845 XlwMenuWidget mw;
846 Window window;
847 int x, y, width;
848 enum menu_separator type;
850 Display *dpy = XtDisplay (mw);
851 XGCValues xgcv;
853 switch (type)
855 case SEPARATOR_NO_LINE:
856 break;
858 case SEPARATOR_SINGLE_LINE:
859 XDrawLine (dpy, window, mw->menu.foreground_gc,
860 x, y, x + width, y);
861 break;
863 case SEPARATOR_DOUBLE_LINE:
864 draw_separator (mw, window, x, y, width, SEPARATOR_SINGLE_LINE);
865 draw_separator (mw, window, x, y + 2, width, SEPARATOR_SINGLE_LINE);
866 break;
868 case SEPARATOR_SINGLE_DASHED_LINE:
869 xgcv.line_style = LineOnOffDash;
870 XChangeGC (dpy, mw->menu.foreground_gc, GCLineStyle, &xgcv);
871 XDrawLine (dpy, window, mw->menu.foreground_gc,
872 x, y, x + width, y);
873 xgcv.line_style = LineSolid;
874 XChangeGC (dpy, mw->menu.foreground_gc, GCLineStyle, &xgcv);
875 break;
877 case SEPARATOR_DOUBLE_DASHED_LINE:
878 draw_separator (mw, window, x, y, width,
879 SEPARATOR_SINGLE_DASHED_LINE);
880 draw_separator (mw, window, x, y + 2, width,
881 SEPARATOR_SINGLE_DASHED_LINE);
882 break;
884 case SEPARATOR_SHADOW_ETCHED_IN:
885 XDrawLine (dpy, window, mw->menu.shadow_bottom_gc,
886 x, y, x + width, y);
887 XDrawLine (dpy, window, mw->menu.shadow_top_gc,
888 x, y + 1, x + width, y + 1);
889 break;
891 case SEPARATOR_SHADOW_ETCHED_OUT:
892 XDrawLine (dpy, window, mw->menu.shadow_top_gc,
893 x, y, x + width, y);
894 XDrawLine (dpy, window, mw->menu.shadow_bottom_gc,
895 x, y + 1, x + width, y + 1);
896 break;
898 case SEPARATOR_SHADOW_ETCHED_IN_DASH:
899 xgcv.line_style = LineOnOffDash;
900 XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
901 XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
902 draw_separator (mw, window, x, y, width, SEPARATOR_SHADOW_ETCHED_IN);
903 xgcv.line_style = LineSolid;
904 XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
905 XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
906 break;
908 case SEPARATOR_SHADOW_ETCHED_OUT_DASH:
909 xgcv.line_style = LineOnOffDash;
910 XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
911 XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
912 draw_separator (mw, window, x, y, width, SEPARATOR_SHADOW_ETCHED_OUT);
913 xgcv.line_style = LineSolid;
914 XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
915 XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
916 break;
918 case SEPARATOR_SHADOW_DOUBLE_ETCHED_IN:
919 draw_separator (mw, window, x, y, width, SEPARATOR_SHADOW_ETCHED_IN);
920 draw_separator (mw, window, x, y + 3, width, SEPARATOR_SHADOW_ETCHED_IN);
921 break;
923 case SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT:
924 draw_separator (mw, window, x, y, width,
925 SEPARATOR_SHADOW_ETCHED_OUT);
926 draw_separator (mw, window, x, y + 3, width,
927 SEPARATOR_SHADOW_ETCHED_OUT);
928 break;
930 case SEPARATOR_SHADOW_DOUBLE_ETCHED_IN_DASH:
931 xgcv.line_style = LineOnOffDash;
932 XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
933 XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
934 draw_separator (mw, window, x, y, width,
935 SEPARATOR_SHADOW_DOUBLE_ETCHED_IN);
936 xgcv.line_style = LineSolid;
937 XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
938 XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
939 break;
941 case SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT_DASH:
942 xgcv.line_style = LineOnOffDash;
943 XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
944 XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
945 draw_separator (mw, window, x, y, width,
946 SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT);
947 xgcv.line_style = LineSolid;
948 XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
949 XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
950 break;
952 default:
953 abort ();
958 /* Return the pixel height of menu separator SEPARATOR. */
960 static int
961 separator_height (separator)
962 enum menu_separator separator;
964 switch (separator)
966 case SEPARATOR_NO_LINE:
967 return 2;
969 case SEPARATOR_SINGLE_LINE:
970 case SEPARATOR_SINGLE_DASHED_LINE:
971 return 1;
973 case SEPARATOR_DOUBLE_LINE:
974 case SEPARATOR_DOUBLE_DASHED_LINE:
975 return 3;
977 case SEPARATOR_SHADOW_ETCHED_IN:
978 case SEPARATOR_SHADOW_ETCHED_OUT:
979 case SEPARATOR_SHADOW_ETCHED_IN_DASH:
980 case SEPARATOR_SHADOW_ETCHED_OUT_DASH:
981 return 2;
983 case SEPARATOR_SHADOW_DOUBLE_ETCHED_IN:
984 case SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT:
985 case SEPARATOR_SHADOW_DOUBLE_ETCHED_IN_DASH:
986 case SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT_DASH:
987 return 5;
989 default:
990 abort ();
995 /* Display the menu item and increment where.x and where.y to show how large
996 the menu item was. */
998 static void
999 display_menu_item (mw, val, ws, where, highlighted_p, horizontal_p,
1000 just_compute_p)
1001 XlwMenuWidget mw;
1002 widget_value* val;
1003 window_state* ws;
1004 XPoint* where;
1005 Boolean highlighted_p;
1006 Boolean horizontal_p;
1007 Boolean just_compute_p;
1009 GC deco_gc;
1010 GC text_gc;
1011 int font_height = MENU_FONT_HEIGHT (mw);
1012 int font_ascent = MENU_FONT_ASCENT (mw);
1013 int shadow = mw->menu.shadow_thickness;
1014 int margin = mw->menu.margin;
1015 int h_spacing = mw->menu.horizontal_spacing;
1016 int v_spacing = mw->menu.vertical_spacing;
1017 int label_width;
1018 int rest_width;
1019 int button_width;
1020 int height;
1021 int width;
1022 enum menu_separator separator;
1023 int separator_p = lw_separator_p (val->name, &separator, 0);
1024 #ifdef HAVE_XFT
1025 XftColor *xftfg;
1026 #endif
1028 /* compute the sizes of the item */
1029 size_menu_item (mw, val, horizontal_p, &label_width, &rest_width,
1030 &button_width, &height);
1032 if (horizontal_p)
1033 width = label_width + rest_width;
1034 else
1036 label_width = ws->label_width;
1037 width = ws->width - 2 * shadow;
1040 /* Only highlight an enabled item that has a callback. */
1041 if (highlighted_p)
1042 if (!val->enabled || !(val->call_data || val->contents))
1043 highlighted_p = 0;
1045 /* do the drawing. */
1046 if (!just_compute_p)
1048 /* Add the shadow border of the containing menu */
1049 int x = where->x + shadow;
1050 int y = where->y + shadow;
1052 if (horizontal_p)
1054 x += margin;
1055 y += margin;
1058 /* pick the foreground and background GC. */
1059 if (val->enabled)
1060 text_gc = mw->menu.foreground_gc;
1061 else
1062 text_gc = mw->menu.disabled_gc;
1063 deco_gc = mw->menu.foreground_gc;
1064 #ifdef HAVE_XFT
1065 xftfg = val->enabled ? &mw->menu.xft_fg : &mw->menu.xft_disabled_fg;
1066 #endif
1068 if (separator_p)
1070 draw_separator (mw, ws->pixmap, x, y, width, separator);
1072 else
1074 int x_offset = x + h_spacing + shadow;
1075 char* display_string = resource_widget_value (mw, val);
1076 draw_shadow_rectangle (mw, ws->pixmap, x, y, width, height, True,
1077 False);
1079 /* Deal with centering a menu title. */
1080 if (!horizontal_p && !val->contents && !val->call_data)
1082 int l = string_width (mw, display_string);
1084 if (width > l)
1085 x_offset = (width - l) >> 1;
1087 else if (!horizontal_p && ws->button_width)
1088 x_offset += ws->button_width;
1091 #ifdef HAVE_XFT
1092 if (ws->xft_draw)
1094 int draw_y = y + v_spacing + shadow;
1095 XftDrawStringUtf8 (ws->xft_draw, xftfg,
1096 mw->menu.xft_font,
1097 x_offset, draw_y + font_ascent,
1098 (unsigned char *) display_string,
1099 strlen (display_string));
1101 else
1102 #endif
1103 #ifdef HAVE_X_I18N
1104 if (mw->menu.fontSet)
1105 XmbDrawString (XtDisplay (mw), ws->pixmap, mw->menu.fontSet,
1106 text_gc, x_offset,
1107 y + v_spacing + shadow + font_ascent,
1108 display_string, strlen (display_string));
1109 else
1110 #endif
1111 XDrawString (XtDisplay (mw), ws->pixmap,
1112 text_gc, x_offset,
1113 y + v_spacing + shadow + font_ascent,
1114 display_string, strlen (display_string));
1116 if (!horizontal_p)
1118 if (val->button_type == BUTTON_TYPE_TOGGLE)
1119 draw_toggle (mw, ws->pixmap, x, y + v_spacing + shadow,
1120 val->selected);
1121 else if (val->button_type == BUTTON_TYPE_RADIO)
1122 draw_radio (mw, ws->pixmap, x, y + v_spacing + shadow,
1123 val->selected);
1125 if (val->contents)
1127 int a_w = arrow_width (mw);
1128 draw_arrow (mw, ws->pixmap, deco_gc,
1129 x + width - a_w
1130 - mw->menu.horizontal_spacing
1131 - mw->menu.shadow_thickness,
1132 y + v_spacing + shadow, a_w,
1133 highlighted_p);
1135 else if (val->key)
1137 #ifdef HAVE_XFT
1138 if (ws->xft_draw)
1140 XGlyphInfo gi;
1141 int draw_x = ws->width - ws->max_rest_width
1142 + mw->menu.arrow_spacing;
1143 int draw_y = y + v_spacing + shadow + font_ascent;
1144 XftDrawStringUtf8 (ws->xft_draw, xftfg,
1145 mw->menu.xft_font,
1146 draw_x, draw_y,
1147 (unsigned char *) val->key,
1148 strlen (val->key));
1150 else
1151 #endif
1152 #ifdef HAVE_X_I18N
1153 if (mw->menu.fontSet)
1154 XmbDrawString (XtDisplay (mw), ws->pixmap,
1155 mw->menu.fontSet,
1156 text_gc,
1157 x + label_width + mw->menu.arrow_spacing,
1158 y + v_spacing + shadow + font_ascent,
1159 val->key, strlen (val->key));
1160 else
1161 #endif
1162 XDrawString (XtDisplay (mw), ws->pixmap,
1163 text_gc,
1164 x + label_width + mw->menu.arrow_spacing,
1165 y + v_spacing + shadow + font_ascent,
1166 val->key, strlen (val->key));
1169 else
1171 XDrawRectangle (XtDisplay (mw), ws->pixmap,
1172 mw->menu.background_gc,
1173 x + shadow, y + shadow,
1174 label_width + h_spacing - 1,
1175 font_height + 2 * v_spacing - 1);
1176 draw_shadow_rectangle (mw, ws->pixmap, x, y, width, height,
1177 True, False);
1180 if (highlighted_p)
1181 draw_shadow_rectangle (mw, ws->pixmap, x, y, width, height, False,
1182 False);
1186 where->x += width;
1187 where->y += height;
1190 static void
1191 display_menu (mw, level, just_compute_p, highlighted_pos, hit, hit_return)
1192 XlwMenuWidget mw;
1193 int level;
1194 Boolean just_compute_p;
1195 XPoint* highlighted_pos;
1196 XPoint* hit;
1197 widget_value** hit_return;
1199 widget_value* val;
1200 widget_value* following_item;
1201 window_state* ws;
1202 XPoint where;
1203 int horizontal_p = mw->menu.horizontal && (level == 0);
1204 int highlighted_p;
1205 int no_return = 0;
1206 enum menu_separator separator;
1208 if (level >= mw->menu.old_depth)
1209 abort_gracefully ((Widget) mw);
1211 if (level < mw->menu.old_depth - 1)
1212 following_item = mw->menu.old_stack [level + 1];
1213 else
1214 following_item = NULL;
1216 if (hit)
1217 *hit_return = NULL;
1219 where.x = 0;
1220 where.y = 0;
1222 ws = &mw->menu.windows [level];
1224 if (!just_compute_p)
1225 XFillRectangle (XtDisplay (mw), ws->pixmap, mw->menu.background_gc,
1226 0, 0, ws->width, ws->height);
1228 for (val = mw->menu.old_stack [level]->contents; val; val = val->next)
1230 highlighted_p = val == following_item;
1231 if (highlighted_p && highlighted_pos)
1233 if (horizontal_p)
1234 highlighted_pos->x = where.x;
1235 else
1236 highlighted_pos->y = where.y;
1239 display_menu_item (mw, val, ws, &where, highlighted_p, horizontal_p,
1240 just_compute_p);
1242 if (highlighted_p && highlighted_pos)
1244 if (horizontal_p)
1245 highlighted_pos->y = where.y;
1246 else
1247 highlighted_pos->x = where.x;
1250 if (hit
1251 && !*hit_return
1252 && (horizontal_p ? hit->x < where.x : hit->y < where.y)
1253 && !lw_separator_p (val->name, &separator, 0)
1254 && !no_return)
1256 if (val->enabled)
1257 *hit_return = val;
1258 else
1259 no_return = 1;
1260 if (mw->menu.inside_entry != val)
1262 if (mw->menu.inside_entry)
1263 XtCallCallbackList ((Widget)mw, mw->menu.leave,
1264 (XtPointer) mw->menu.inside_entry);
1265 mw->menu.inside_entry = val;
1266 XtCallCallbackList ((Widget)mw, mw->menu.enter,
1267 (XtPointer) mw->menu.inside_entry);
1271 if (horizontal_p)
1272 where.y = 0;
1273 else
1274 where.x = 0;
1277 if (!just_compute_p)
1279 draw_shadow_rectangle (mw, ws->pixmap, 0, 0, ws->width, ws->height,
1280 False, False);
1281 XCopyArea (XtDisplay (mw), ws->pixmap, ws->window,
1282 mw->menu.foreground_gc, 0, 0, ws->width, ws->height, 0, 0);
1286 \f/* Motion code */
1287 static void
1288 set_new_state (mw, val, level)
1289 XlwMenuWidget mw;
1290 widget_value* val;
1291 int level;
1293 int i;
1295 mw->menu.new_depth = 0;
1296 for (i = 0; i < level; i++)
1297 push_new_stack (mw, mw->menu.old_stack [i]);
1298 push_new_stack (mw, val);
1301 static void
1302 expose_cb (Widget widget,
1303 XtPointer closure,
1304 XEvent* event,
1305 Boolean* continue_to_dispatch)
1307 XlwMenuWidget mw = (XlwMenuWidget) closure;
1308 int i;
1310 *continue_to_dispatch = False;
1311 for (i = 0; i < mw->menu.windows_length; ++i)
1312 if (mw->menu.windows [i].w == widget) break;
1313 if (i < mw->menu.windows_length && i < mw->menu.old_depth)
1314 display_menu (mw, i, False, NULL, NULL, NULL, NULL, NULL);
1317 static void
1318 set_window_type (Widget w, XlwMenuWidget mw)
1320 int popup_menu_p = mw->menu.top_depth == 1;
1321 Atom type = XInternAtom (XtDisplay (w),
1322 popup_menu_p
1323 ? "_NET_WM_WINDOW_TYPE_POPUP_MENU"
1324 : "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU",
1325 False);
1327 XChangeProperty (XtDisplay (w), XtWindow (w),
1328 XInternAtom (XtDisplay (w), "_NET_WM_WINDOW_TYPE", False),
1329 XA_ATOM, 32, PropModeReplace,
1330 (unsigned char *)&type, 1);
1334 static void
1335 make_windows_if_needed (mw, n)
1336 XlwMenuWidget mw;
1337 int n;
1339 int i;
1340 int start_at;
1341 window_state* windows;
1342 #ifdef HAVE_XFT
1343 int screen = XScreenNumberOfScreen (mw->core.screen);
1344 #endif
1346 if (mw->menu.windows_length >= n)
1347 return;
1349 if (!mw->menu.windows)
1351 mw->menu.windows =
1352 (window_state*)XtMalloc (n * sizeof (window_state));
1353 start_at = 0;
1355 else
1357 mw->menu.windows =
1358 (window_state*)XtRealloc ((char*)mw->menu.windows,
1359 n * sizeof (window_state));
1360 start_at = mw->menu.windows_length;
1362 mw->menu.windows_length = n;
1364 windows = mw->menu.windows;
1366 for (i = start_at; i < n; i++)
1368 Arg av[10];
1369 int ac = 0;
1370 windows [i].x = 0;
1371 windows [i].y = 0;
1372 windows [i].width = 1;
1373 windows [i].height = 1;
1374 windows [i].max_rest_width = 0;
1375 XtSetArg (av[ac], XtNwidth, 1); ++ac;
1376 XtSetArg (av[ac], XtNheight, 1); ++ac;
1377 XtSetArg (av[ac], XtNsaveUnder, True); ++ac;
1378 XtSetArg (av[ac], XtNbackground, mw->core.background_pixel); ++ac;
1379 XtSetArg (av[ac], XtNborderColor, mw->core.border_pixel); ++ac;
1380 XtSetArg (av[ac], XtNborderWidth, mw->core.border_width); ++ac;
1381 XtSetArg (av[ac], XtNcursor, mw->menu.cursor_shape); ++ac;
1382 windows [i].w =
1383 XtCreatePopupShell ("sub", overrideShellWidgetClass,
1384 (Widget) mw, av, ac);
1385 XtRealizeWidget (windows [i].w);
1386 XtAddEventHandler (windows [i].w, ExposureMask, False, expose_cb, mw);
1387 windows [i].window = XtWindow (windows [i].w);
1388 windows [i].pixmap = None;
1389 #ifdef HAVE_XFT
1390 windows [i].xft_draw = 0;
1391 #endif
1392 set_window_type (windows [i].w, mw);
1396 /* Value is non-zero if WINDOW is part of menu bar widget W. */
1399 xlwmenu_window_p (w, window)
1400 Widget w;
1401 Window window;
1403 XlwMenuWidget mw = (XlwMenuWidget) w;
1404 int i;
1406 for (i = 0; i < mw->menu.windows_length; ++i)
1407 if (window == mw->menu.windows[i].window)
1408 break;
1410 return i < mw->menu.windows_length;
1413 /* Make the window fit in the screen */
1414 static void
1415 fit_to_screen (mw, ws, previous_ws, horizontal_p)
1416 XlwMenuWidget mw;
1417 window_state* ws;
1418 window_state* previous_ws;
1419 Boolean horizontal_p;
1421 unsigned int screen_width = WidthOfScreen (XtScreen (mw));
1422 unsigned int screen_height = HeightOfScreen (XtScreen (mw));
1423 /* 1 if we are unable to avoid an overlap between
1424 this menu and the parent menu in the X dimension. */
1425 int horizontal_overlap = 0;
1427 if (ws->x < 0)
1428 ws->x = 0;
1429 else if (ws->x + ws->width > screen_width)
1431 if (!horizontal_p)
1432 /* The addition of shadow-thickness for a sub-menu's position is
1433 to reflect a similar adjustment when the menu is displayed to
1434 the right of the invoking menu-item; it makes the sub-menu
1435 look more `attached' to the menu-item. */
1436 ws->x = previous_ws->x - ws->width + mw->menu.shadow_thickness;
1437 else
1438 ws->x = screen_width - ws->width;
1439 if (ws->x < 0)
1441 ws->x = 0;
1442 horizontal_overlap = 1;
1445 /* If we overlap in X, try to avoid overlap in Y. */
1446 if (horizontal_overlap
1447 && ws->y < previous_ws->y + previous_ws->height
1448 && previous_ws->y < ws->y + ws->height)
1450 /* Put this menu right below or right above PREVIOUS_WS
1451 if there's room. */
1452 if (previous_ws->y + previous_ws->height + ws->height < screen_height)
1453 ws->y = previous_ws->y + previous_ws->height;
1454 else if (previous_ws->y - ws->height > 0)
1455 ws->y = previous_ws->y - ws->height;
1458 if (ws->y < 0)
1459 ws->y = 0;
1460 else if (ws->y + ws->height > screen_height)
1462 if (horizontal_p)
1463 ws->y = previous_ws->y - ws->height;
1464 else
1465 ws->y = screen_height - ws->height;
1466 if (ws->y < 0)
1467 ws->y = 0;
1471 static void
1472 create_pixmap_for_menu (window_state* ws, XlwMenuWidget mw)
1474 if (ws->pixmap != None)
1476 XFreePixmap (XtDisplay (ws->w), ws->pixmap);
1477 ws->pixmap = None;
1479 ws->pixmap = XCreatePixmap (XtDisplay (ws->w), ws->window,
1480 ws->width, ws->height,
1481 DefaultDepthOfScreen (XtScreen (ws->w)));
1482 #ifdef HAVE_XFT
1483 if (ws->xft_draw)
1484 XftDrawDestroy (ws->xft_draw);
1485 if (mw->menu.xft_font)
1487 int screen = XScreenNumberOfScreen (mw->core.screen);
1488 ws->xft_draw = XftDrawCreate (XtDisplay (ws->w),
1489 ws->pixmap,
1490 DefaultVisual (XtDisplay (ws->w), screen),
1491 mw->core.colormap);
1493 else
1494 ws->xft_draw = 0;
1495 #endif
1498 /* Updates old_stack from new_stack and redisplays. */
1499 static void
1500 remap_menubar (mw)
1501 XlwMenuWidget mw;
1503 int i;
1504 int last_same;
1505 XPoint selection_position;
1506 int old_depth = mw->menu.old_depth;
1507 int new_depth = mw->menu.new_depth;
1508 widget_value** old_stack;
1509 widget_value** new_stack;
1510 window_state* windows;
1511 widget_value* old_selection;
1512 widget_value* new_selection;
1514 /* Check that enough windows and old_stack are ready. */
1515 make_windows_if_needed (mw, new_depth);
1516 make_old_stack_space (mw, new_depth);
1517 windows = mw->menu.windows;
1518 old_stack = mw->menu.old_stack;
1519 new_stack = mw->menu.new_stack;
1521 /* compute the last identical different entry */
1522 for (i = 1; i < old_depth && i < new_depth; i++)
1523 if (old_stack [i] != new_stack [i])
1524 break;
1525 last_same = i - 1;
1527 /* Memorize the previously selected item to be able to refresh it */
1528 old_selection = last_same + 1 < old_depth ? old_stack [last_same + 1] : NULL;
1529 if (old_selection && !old_selection->enabled)
1530 old_selection = NULL;
1531 new_selection = last_same + 1 < new_depth ? new_stack [last_same + 1] : NULL;
1532 if (new_selection && !new_selection->enabled)
1533 new_selection = NULL;
1535 /* Call callback when the hightlighted item changes. */
1536 if (old_selection || new_selection)
1537 XtCallCallbackList ((Widget)mw, mw->menu.highlight,
1538 (XtPointer) new_selection);
1540 /* updates old_state from new_state. It has to be done now because
1541 display_menu (called below) uses the old_stack to know what to display. */
1542 for (i = last_same + 1; i < new_depth; i++)
1544 XtPopdown (mw->menu.windows [i].w);
1545 old_stack [i] = new_stack [i];
1547 mw->menu.old_depth = new_depth;
1549 /* refresh the last selection */
1550 selection_position.x = 0;
1551 selection_position.y = 0;
1552 display_menu (mw, last_same, new_selection == old_selection,
1553 &selection_position, NULL, NULL);
1555 /* Now place the new menus. */
1556 for (i = last_same + 1; i < new_depth && new_stack[i]->contents; i++)
1558 window_state *previous_ws = &windows[i - 1];
1559 window_state *ws = &windows[i];
1561 ws->x = (previous_ws->x + selection_position.x
1562 + mw->menu.shadow_thickness);
1563 if (mw->menu.horizontal && i == 1)
1564 ws->x += mw->menu.margin;
1566 #if 0
1567 if (!mw->menu.horizontal || i > 1)
1568 ws->x += mw->menu.shadow_thickness;
1569 #endif
1571 ws->y = (previous_ws->y + selection_position.y
1572 + mw->menu.shadow_thickness);
1573 if (mw->menu.horizontal && i == 1)
1574 ws->y += mw->menu.margin;
1576 size_menu (mw, i);
1578 fit_to_screen (mw, ws, previous_ws, mw->menu.horizontal && i == 1);
1580 XtVaSetValues (ws->w, XtNwidth, ws->width, XtNheight, ws->height,
1581 XtNx, ws->x, XtNy, ws->y, NULL);
1582 create_pixmap_for_menu (ws, mw);
1583 XtPopup (ws->w, XtGrabNone);
1584 display_menu (mw, i, False, &selection_position, NULL, NULL);
1587 /* unmap the menus that popped down */
1588 for (i = new_depth - 1; i < old_depth; i++)
1589 if (i >= new_depth || (i > 0 && !new_stack[i]->contents))
1590 XtPopdown (windows[i].w);
1593 static Boolean
1594 motion_event_is_in_menu (mw, ev, level, relative_pos)
1595 XlwMenuWidget mw;
1596 XMotionEvent* ev;
1597 int level;
1598 XPoint* relative_pos;
1600 window_state* ws = &mw->menu.windows [level];
1601 int shadow = level == 0 ? 0 : mw->menu.shadow_thickness;
1602 int x = ws->x + shadow;
1603 int y = ws->y + shadow;
1604 relative_pos->x = ev->x_root - x;
1605 relative_pos->y = ev->y_root - y;
1606 return (x - shadow < ev->x_root && ev->x_root < x + ws->width
1607 && y - shadow < ev->y_root && ev->y_root < y + ws->height);
1610 static Boolean
1611 map_event_to_widget_value (mw, ev, val, level)
1612 XlwMenuWidget mw;
1613 XMotionEvent* ev;
1614 widget_value** val;
1615 int* level;
1617 int i;
1618 XPoint relative_pos;
1619 window_state* ws;
1620 int inside = 0;
1622 *val = NULL;
1624 /* Find the window */
1625 for (i = mw->menu.old_depth - 1; i >= 0; i--)
1627 ws = &mw->menu.windows [i];
1628 if (ws && motion_event_is_in_menu (mw, ev, i, &relative_pos))
1630 inside = 1;
1631 display_menu (mw, i, True, NULL, &relative_pos, val);
1633 if (*val)
1635 *level = i + 1;
1636 return True;
1641 if (!inside)
1643 if (mw->menu.inside_entry != NULL)
1644 XtCallCallbackList ((Widget)mw, mw->menu.leave,
1645 (XtPointer) mw->menu.inside_entry);
1646 mw->menu.inside_entry = NULL;
1649 return False;
1652 \f/* Procedures */
1653 static void
1654 make_drawing_gcs (mw)
1655 XlwMenuWidget mw;
1657 XGCValues xgcv;
1658 float scale;
1659 XtGCMask mask = GCForeground | GCBackground;
1661 #ifdef HAVE_X_I18N
1662 if (!mw->menu.fontSet)
1664 xgcv.font = mw->menu.font->fid;
1665 mask |= GCFont;
1667 #else
1668 xgcv.font = mw->menu.font->fid;
1669 mask |= GCFont;
1670 #endif
1671 xgcv.foreground = mw->menu.foreground;
1672 xgcv.background = mw->core.background_pixel;
1673 mw->menu.foreground_gc = XtGetGC ((Widget)mw, mask, &xgcv);
1675 xgcv.foreground = mw->menu.button_foreground;
1676 mw->menu.button_gc = XtGetGC ((Widget)mw, mask, &xgcv);
1678 xgcv.background = mw->core.background_pixel;
1680 #define BRIGHTNESS(color) (((color) & 0xff) + (((color) >> 8) & 0xff) + (((color) >> 16) & 0xff))
1682 /* Allocate color for disabled menu-items. */
1683 mw->menu.disabled_foreground = mw->menu.foreground;
1684 if (BRIGHTNESS(mw->menu.foreground) < BRIGHTNESS(mw->core.background_pixel))
1685 scale = 2.3;
1686 else
1687 scale = 0.55;
1689 x_alloc_lighter_color_for_widget ((Widget) mw, XtDisplay ((Widget) mw),
1690 mw->core.colormap,
1691 &mw->menu.disabled_foreground,
1692 scale,
1693 0x8000);
1695 if (mw->menu.foreground == mw->menu.disabled_foreground
1696 || mw->core.background_pixel == mw->menu.disabled_foreground)
1698 /* Too few colors, use stipple. */
1699 xgcv.foreground = mw->menu.foreground;
1700 xgcv.fill_style = FillStippled;
1701 xgcv.stipple = mw->menu.gray_pixmap;
1702 mw->menu.disabled_gc = XtGetGC ((Widget)mw, mask
1703 | GCFillStyle | GCStipple, &xgcv);
1705 else
1707 /* Many colors available, use disabled pixel. */
1708 xgcv.foreground = mw->menu.disabled_foreground;
1709 mw->menu.disabled_gc = XtGetGC ((Widget)mw, mask, &xgcv);
1712 xgcv.foreground = mw->menu.button_foreground;
1713 xgcv.background = mw->core.background_pixel;
1714 xgcv.fill_style = FillStippled;
1715 xgcv.stipple = mw->menu.gray_pixmap;
1716 mw->menu.inactive_button_gc = XtGetGC ((Widget)mw, mask
1717 | GCFillStyle | GCStipple, &xgcv);
1719 xgcv.foreground = mw->core.background_pixel;
1720 xgcv.background = mw->menu.foreground;
1721 mw->menu.background_gc = XtGetGC ((Widget)mw, mask, &xgcv);
1724 static void
1725 release_drawing_gcs (mw)
1726 XlwMenuWidget mw;
1728 XtReleaseGC ((Widget) mw, mw->menu.foreground_gc);
1729 XtReleaseGC ((Widget) mw, mw->menu.button_gc);
1730 XtReleaseGC ((Widget) mw, mw->menu.disabled_gc);
1731 XtReleaseGC ((Widget) mw, mw->menu.inactive_button_gc);
1732 XtReleaseGC ((Widget) mw, mw->menu.background_gc);
1733 /* let's get some segvs if we try to use these... */
1734 mw->menu.foreground_gc = (GC) -1;
1735 mw->menu.button_gc = (GC) -1;
1736 mw->menu.disabled_gc = (GC) -1;
1737 mw->menu.inactive_button_gc = (GC) -1;
1738 mw->menu.background_gc = (GC) -1;
1741 #define MINL(x,y) ((((unsigned long) (x)) < ((unsigned long) (y))) \
1742 ? ((unsigned long) (x)) : ((unsigned long) (y)))
1744 static void
1745 make_shadow_gcs (mw)
1746 XlwMenuWidget mw;
1748 XGCValues xgcv;
1749 unsigned long pm = 0;
1750 Display *dpy = XtDisplay ((Widget) mw);
1751 Screen *screen = XtScreen ((Widget) mw);
1752 Colormap cmap = mw->core.colormap;
1753 XColor topc, botc;
1754 int top_frobbed = 0, bottom_frobbed = 0;
1756 mw->menu.free_top_shadow_color_p = 0;
1757 mw->menu.free_bottom_shadow_color_p = 0;
1759 if (mw->menu.top_shadow_color == -1)
1760 mw->menu.top_shadow_color = mw->core.background_pixel;
1761 else
1762 mw->menu.top_shadow_color = mw->menu.top_shadow_color;
1764 if (mw->menu.bottom_shadow_color == -1)
1765 mw->menu.bottom_shadow_color = mw->menu.foreground;
1766 else
1767 mw->menu.bottom_shadow_color = mw->menu.bottom_shadow_color;
1769 if (mw->menu.top_shadow_color == mw->core.background_pixel ||
1770 mw->menu.top_shadow_color == mw->menu.foreground)
1772 topc.pixel = mw->core.background_pixel;
1773 #ifdef emacs
1774 if (x_alloc_lighter_color_for_widget ((Widget) mw, dpy, cmap,
1775 &topc.pixel,
1776 1.2, 0x8000))
1777 #else
1778 XQueryColor (dpy, cmap, &topc);
1779 /* don't overflow/wrap! */
1780 topc.red = MINL (65535, topc.red * 1.2);
1781 topc.green = MINL (65535, topc.green * 1.2);
1782 topc.blue = MINL (65535, topc.blue * 1.2);
1783 if (XAllocColor (dpy, cmap, &topc))
1784 #endif
1786 mw->menu.top_shadow_color = topc.pixel;
1787 mw->menu.free_top_shadow_color_p = 1;
1788 top_frobbed = 1;
1791 if (mw->menu.bottom_shadow_color == mw->menu.foreground ||
1792 mw->menu.bottom_shadow_color == mw->core.background_pixel)
1794 botc.pixel = mw->core.background_pixel;
1795 #ifdef emacs
1796 if (x_alloc_lighter_color_for_widget ((Widget) mw, dpy, cmap,
1797 &botc.pixel,
1798 0.6, 0x4000))
1799 #else
1800 XQueryColor (dpy, cmap, &botc);
1801 botc.red *= 0.6;
1802 botc.green *= 0.6;
1803 botc.blue *= 0.6;
1804 if (XAllocColor (dpy, cmap, &botc))
1805 #endif
1807 mw->menu.bottom_shadow_color = botc.pixel;
1808 mw->menu.free_bottom_shadow_color_p = 1;
1809 bottom_frobbed = 1;
1813 if (top_frobbed && bottom_frobbed)
1815 if (topc.pixel == botc.pixel)
1817 if (botc.pixel == mw->menu.foreground)
1819 if (mw->menu.free_top_shadow_color_p)
1821 x_free_dpy_colors (dpy, screen, cmap,
1822 &mw->menu.top_shadow_color, 1);
1823 mw->menu.free_top_shadow_color_p = 0;
1825 mw->menu.top_shadow_color = mw->core.background_pixel;
1827 else
1829 if (mw->menu.free_bottom_shadow_color_p)
1831 x_free_dpy_colors (dpy, screen, cmap,
1832 &mw->menu.bottom_shadow_color, 1);
1833 mw->menu.free_bottom_shadow_color_p = 0;
1835 mw->menu.bottom_shadow_color = mw->menu.foreground;
1840 if (!mw->menu.top_shadow_pixmap &&
1841 mw->menu.top_shadow_color == mw->core.background_pixel)
1843 mw->menu.top_shadow_pixmap = mw->menu.gray_pixmap;
1844 if (mw->menu.free_top_shadow_color_p)
1846 x_free_dpy_colors (dpy, screen, cmap, &mw->menu.top_shadow_color, 1);
1847 mw->menu.free_top_shadow_color_p = 0;
1849 mw->menu.top_shadow_color = mw->menu.foreground;
1851 if (!mw->menu.bottom_shadow_pixmap &&
1852 mw->menu.bottom_shadow_color == mw->core.background_pixel)
1854 mw->menu.bottom_shadow_pixmap = mw->menu.gray_pixmap;
1855 if (mw->menu.free_bottom_shadow_color_p)
1857 x_free_dpy_colors (dpy, screen, cmap,
1858 &mw->menu.bottom_shadow_color, 1);
1859 mw->menu.free_bottom_shadow_color_p = 0;
1861 mw->menu.bottom_shadow_color = mw->menu.foreground;
1864 xgcv.fill_style = FillStippled;
1865 xgcv.foreground = mw->menu.top_shadow_color;
1866 xgcv.stipple = mw->menu.top_shadow_pixmap;
1867 pm = (xgcv.stipple ? GCStipple|GCFillStyle : 0);
1868 mw->menu.shadow_top_gc = XtGetGC ((Widget)mw, GCForeground | pm, &xgcv);
1870 xgcv.foreground = mw->menu.bottom_shadow_color;
1871 xgcv.stipple = mw->menu.bottom_shadow_pixmap;
1872 pm = (xgcv.stipple ? GCStipple|GCFillStyle : 0);
1873 mw->menu.shadow_bottom_gc = XtGetGC ((Widget)mw, GCForeground | pm, &xgcv);
1877 static void
1878 release_shadow_gcs (mw)
1879 XlwMenuWidget mw;
1881 Display *dpy = XtDisplay ((Widget) mw);
1882 Screen *screen = XtScreen ((Widget) mw);
1883 Colormap cmap = mw->core.colormap;
1884 Pixel px[2];
1885 int i = 0;
1887 if (mw->menu.free_top_shadow_color_p)
1888 px[i++] = mw->menu.top_shadow_color;
1889 if (mw->menu.free_bottom_shadow_color_p)
1890 px[i++] = mw->menu.bottom_shadow_color;
1891 if (i > 0)
1892 x_free_dpy_colors (dpy, screen, cmap, px, i);
1894 XtReleaseGC ((Widget) mw, mw->menu.shadow_top_gc);
1895 XtReleaseGC ((Widget) mw, mw->menu.shadow_bottom_gc);
1898 #ifdef HAVE_XFT
1899 static int
1900 openXftFont (mw)
1901 XlwMenuWidget mw;
1903 char *fname = mw->menu.faceName;
1905 mw->menu.xft_font = 0;
1906 mw->menu.default_face = fname && strcmp (fname, DEFAULT_FACENAME) == 0;
1908 if (fname && strcmp (fname, "none") != 0)
1910 int screen = XScreenNumberOfScreen (mw->core.screen);
1911 int len = strlen (fname), i = len-1;
1912 /* Try to convert Gtk-syntax (Sans 9) to Xft syntax Sans-9. */
1913 while (i > 0 && isdigit (fname[i]))
1914 --i;
1915 if (fname[i] == ' ')
1917 fname = xstrdup (mw->menu.faceName);
1918 fname[i] = '-';
1921 mw->menu.xft_font = XftFontOpenName (XtDisplay (mw), screen, fname);
1922 if (!mw->menu.xft_font)
1924 fprintf (stderr, "Can't find font '%s'\n", fname);
1925 mw->menu.xft_font = XftFontOpenName (XtDisplay (mw), screen,
1926 DEFAULT_FACENAME);
1930 if (fname != mw->menu.faceName) free (fname);
1932 return mw->menu.xft_font != 0;
1934 #endif
1936 static void
1937 XlwMenuInitialize (request, mw, args, num_args)
1938 Widget request;
1939 XlwMenuWidget mw;
1940 ArgList args;
1941 Cardinal *num_args;
1943 /* Get the GCs and the widget size */
1945 Window window = RootWindowOfScreen (DefaultScreenOfDisplay (XtDisplay (mw)));
1946 Display* display = XtDisplay (mw);
1948 #if 0
1949 widget_value *tem = (widget_value *) XtMalloc (sizeof (widget_value));
1951 /* _XtCreate is freeing the object that was passed to us,
1952 so make a copy that we will actually keep. */
1953 lwlib_bcopy (mw->menu.contents, tem, sizeof (widget_value));
1954 mw->menu.contents = tem;
1955 #endif
1957 /* mw->menu.cursor = XCreateFontCursor (display, mw->menu.cursor_shape); */
1958 mw->menu.cursor = mw->menu.cursor_shape;
1960 mw->menu.gray_pixmap
1961 = XCreatePixmapFromBitmapData (display, window, gray_bitmap_bits,
1962 gray_bitmap_width, gray_bitmap_height,
1963 (unsigned long)1, (unsigned long)0, 1);
1965 #ifdef HAVE_XFT
1966 if (openXftFont (mw))
1968 else
1969 #endif
1971 if (!mw->menu.font)
1973 if (!xlwmenu_default_font)
1974 xlwmenu_default_font = XLoadQueryFont (display, "fixed");
1975 mw->menu.font = xlwmenu_default_font;
1976 if (!mw->menu.font)
1978 fprintf (stderr, "Menu font fixed not found, can't continue.\n");
1979 abort ();
1983 #ifdef HAVE_X_I18N
1984 if (mw->menu.fontSet)
1985 mw->menu.font_extents = XExtentsOfFontSet (mw->menu.fontSet);
1986 #endif
1988 make_drawing_gcs (mw);
1989 make_shadow_gcs (mw);
1991 mw->menu.popped_up = False;
1993 mw->menu.old_depth = 1;
1994 mw->menu.old_stack = (widget_value**)XtMalloc (sizeof (widget_value*));
1995 mw->menu.old_stack_length = 1;
1996 mw->menu.old_stack [0] = mw->menu.contents;
1998 mw->menu.new_depth = 0;
1999 mw->menu.new_stack = 0;
2000 mw->menu.new_stack_length = 0;
2001 push_new_stack (mw, mw->menu.contents);
2003 mw->menu.windows = (window_state*)XtMalloc (sizeof (window_state));
2004 mw->menu.windows_length = 1;
2005 mw->menu.windows [0].x = 0;
2006 mw->menu.windows [0].y = 0;
2007 mw->menu.windows [0].width = 0;
2008 mw->menu.windows [0].height = 0;
2009 mw->menu.windows [0].max_rest_width = 0;
2010 mw->menu.windows [0].pixmap = None;
2011 #ifdef HAVE_XFT
2012 mw->menu.windows [0].xft_draw = 0;
2013 #endif
2014 size_menu (mw, 0);
2016 mw->core.width = mw->menu.windows [0].width;
2017 mw->core.height = mw->menu.windows [0].height;
2020 static void
2021 XlwMenuClassInitialize ()
2023 xlwmenu_default_font = 0;
2026 static void
2027 XlwMenuRealize (w, valueMask, attributes)
2028 Widget w;
2029 Mask *valueMask;
2030 XSetWindowAttributes *attributes;
2032 XlwMenuWidget mw = (XlwMenuWidget)w;
2033 XSetWindowAttributes xswa;
2034 int mask;
2036 (*xlwMenuWidgetClass->core_class.superclass->core_class.realize)
2037 (w, valueMask, attributes);
2039 xswa.save_under = True;
2040 xswa.cursor = mw->menu.cursor_shape;
2041 mask = CWSaveUnder | CWCursor;
2042 /* I sometimes get random BadCursor errors while creating the first
2043 frame on a display. I can not find their reason, but they are
2044 annoying so for now let's ignore any errors here. -- lorentey */
2045 #ifdef emacs
2046 x_catch_errors (XtDisplay (w));
2047 #endif
2048 XChangeWindowAttributes (XtDisplay (w), XtWindow (w), mask, &xswa);
2049 #ifdef emacs
2050 x_uncatch_errors ();
2051 #endif
2053 mw->menu.windows [0].w = w;
2054 mw->menu.windows [0].window = XtWindow (w);
2055 mw->menu.windows [0].x = w->core.x;
2056 mw->menu.windows [0].y = w->core.y;
2057 mw->menu.windows [0].width = w->core.width;
2058 mw->menu.windows [0].height = w->core.height;
2060 set_window_type (mw->menu.windows [0].w, mw);
2061 create_pixmap_for_menu (&mw->menu.windows [0], mw);
2063 #ifdef HAVE_XFT
2064 if (mw->menu.xft_font)
2066 XColor colors[3];
2067 colors[0].pixel = mw->menu.xft_fg.pixel = mw->menu.foreground;
2068 colors[1].pixel = mw->menu.xft_bg.pixel = mw->core.background_pixel;
2069 colors[2].pixel = mw->menu.xft_disabled_fg.pixel
2070 = mw->menu.disabled_foreground;
2071 XQueryColors (XtDisplay (mw), mw->core.colormap, colors, 3);
2072 mw->menu.xft_fg.color.alpha = 0xFFFF;
2073 mw->menu.xft_fg.color.red = colors[0].red;
2074 mw->menu.xft_fg.color.green = colors[0].green;
2075 mw->menu.xft_fg.color.blue = colors[0].blue;
2076 mw->menu.xft_bg.color.alpha = 0xFFFF;
2077 mw->menu.xft_bg.color.red = colors[1].red;
2078 mw->menu.xft_bg.color.green = colors[1].green;
2079 mw->menu.xft_bg.color.blue = colors[1].blue;
2080 mw->menu.xft_disabled_fg.color.alpha = 0xFFFF;
2081 mw->menu.xft_disabled_fg.color.red = colors[2].red;
2082 mw->menu.xft_disabled_fg.color.green = colors[2].green;
2083 mw->menu.xft_disabled_fg.color.blue = colors[2].blue;
2085 #endif
2088 /* Only the toplevel menubar/popup is a widget so it's the only one that
2089 receives expose events through Xt. So we repaint all the other panes
2090 when receiving an Expose event. */
2091 static void
2092 XlwMenuRedisplay (w, ev, region)
2093 Widget w;
2094 XEvent* ev;
2095 Region region;
2097 XlwMenuWidget mw = (XlwMenuWidget)w;
2098 int i;
2100 /* If we have a depth beyond 1, it's because a submenu was displayed.
2101 If the submenu has been destroyed, set the depth back to 1. */
2102 if (submenu_destroyed)
2104 mw->menu.old_depth = 1;
2105 submenu_destroyed = 0;
2108 display_menu (mw, 0, False, NULL, NULL, NULL);
2112 /* Part of a hack to make the menu redisplay when a tooltip frame
2113 over a menu item is unmapped. */
2115 void
2116 xlwmenu_redisplay (w)
2117 Widget w;
2119 XlwMenuRedisplay (w, NULL, None);
2122 static void
2123 XlwMenuDestroy (w)
2124 Widget w;
2126 int i;
2127 XlwMenuWidget mw = (XlwMenuWidget) w;
2129 if (pointer_grabbed)
2130 ungrab_all ((Widget)w, CurrentTime);
2131 pointer_grabbed = 0;
2133 submenu_destroyed = 1;
2135 release_drawing_gcs (mw);
2136 release_shadow_gcs (mw);
2138 /* this doesn't come from the resource db but is created explicitly
2139 so we must free it ourselves. */
2140 XFreePixmap (XtDisplay (mw), mw->menu.gray_pixmap);
2141 mw->menu.gray_pixmap = (Pixmap) -1;
2143 #if 0
2144 /* Do free mw->menu.contents because nowadays we copy it
2145 during initialization. */
2146 XtFree (mw->menu.contents);
2147 #endif
2149 /* Don't free mw->menu.contents because that comes from our creator.
2150 The `*_stack' elements are just pointers into `contents' so leave
2151 that alone too. But free the stacks themselves. */
2152 if (mw->menu.old_stack) XtFree ((char *) mw->menu.old_stack);
2153 if (mw->menu.new_stack) XtFree ((char *) mw->menu.new_stack);
2155 /* Remember, you can't free anything that came from the resource
2156 database. This includes:
2157 mw->menu.cursor
2158 mw->menu.top_shadow_pixmap
2159 mw->menu.bottom_shadow_pixmap
2160 mw->menu.font
2161 Also the color cells of top_shadow_color, bottom_shadow_color,
2162 foreground, and button_foreground will never be freed until this
2163 client exits. Nice, eh?
2166 #ifdef HAVE_XFT
2167 if (mw->menu.windows [0].xft_draw)
2168 XftDrawDestroy (mw->menu.windows [0].xft_draw);
2169 if (mw->menu.xft_font)
2170 XftFontClose (XtDisplay (mw), mw->menu.xft_font);
2171 #endif
2173 if (mw->menu.windows [0].pixmap != None)
2174 XFreePixmap (XtDisplay (mw), mw->menu.windows [0].pixmap);
2175 /* start from 1 because the one in slot 0 is w->core.window */
2176 for (i = 1; i < mw->menu.windows_length; i++)
2178 if (mw->menu.windows [i].pixmap != None)
2179 XFreePixmap (XtDisplay (mw), mw->menu.windows [i].pixmap);
2180 #ifdef HAVE_XFT
2181 if (mw->menu.windows [i].xft_draw)
2182 XftDrawDestroy (mw->menu.windows [i].xft_draw);
2183 #endif
2186 if (mw->menu.windows)
2187 XtFree ((char *) mw->menu.windows);
2190 #ifdef HAVE_XFT
2191 static int
2192 facename_changed (XlwMenuWidget newmw,
2193 XlwMenuWidget oldmw)
2195 /* This will fore a new XftFont even if the same string is set.
2196 This is good, as rendering parameters may have changed and
2197 we just want to do a redisplay. */
2198 return newmw->menu.faceName != oldmw->menu.faceName;
2200 #endif
2202 static Boolean
2203 XlwMenuSetValues (current, request, new)
2204 Widget current;
2205 Widget request;
2206 Widget new;
2208 XlwMenuWidget oldmw = (XlwMenuWidget)current;
2209 XlwMenuWidget newmw = (XlwMenuWidget)new;
2210 Boolean redisplay = False;
2211 int i;
2213 if (newmw->menu.contents
2214 && newmw->menu.contents->contents
2215 && newmw->menu.contents->contents->change >= VISIBLE_CHANGE)
2216 redisplay = True;
2217 /* Do redisplay if the contents are entirely eliminated. */
2218 if (newmw->menu.contents
2219 && newmw->menu.contents->contents == 0
2220 && newmw->menu.contents->change >= VISIBLE_CHANGE)
2221 redisplay = True;
2223 if (newmw->core.background_pixel != oldmw->core.background_pixel
2224 || newmw->menu.foreground != oldmw->menu.foreground
2225 #ifdef HAVE_XFT
2226 || facename_changed (newmw, oldmw)
2227 #endif
2228 #ifdef HAVE_X_I18N
2229 || newmw->menu.fontSet != oldmw->menu.fontSet
2230 || (newmw->menu.fontSet == NULL && newmw->menu.font != oldmw->menu.font)
2231 #else
2232 || newmw->menu.font != oldmw->menu.font
2233 #endif
2236 release_drawing_gcs (newmw);
2237 make_drawing_gcs (newmw);
2239 release_shadow_gcs (newmw);
2240 /* Cause the shadow colors to be recalculated. */
2241 newmw->menu.top_shadow_color = -1;
2242 newmw->menu.bottom_shadow_color = -1;
2243 make_shadow_gcs (newmw);
2245 redisplay = True;
2247 if (XtIsRealized (current))
2248 /* If the menu is currently displayed, change the display. */
2249 for (i = 0; i < oldmw->menu.windows_length; i++)
2251 XSetWindowBackground (XtDisplay (oldmw),
2252 oldmw->menu.windows [i].window,
2253 newmw->core.background_pixel);
2254 /* clear windows and generate expose events */
2255 XClearArea (XtDisplay (oldmw), oldmw->menu.windows[i].window,
2256 0, 0, 0, 0, True);
2260 #ifdef HAVE_XFT
2261 if (facename_changed (newmw, oldmw))
2263 int i;
2264 int screen = XScreenNumberOfScreen (newmw->core.screen);
2265 if (newmw->menu.xft_font)
2266 XftFontClose (XtDisplay (newmw), newmw->menu.xft_font);
2267 openXftFont (newmw);
2268 for (i = 0; i < newmw->menu.windows_length; i++)
2270 if (newmw->menu.windows [i].xft_draw)
2271 XftDrawDestroy (newmw->menu.windows [i].xft_draw);
2272 newmw->menu.windows [i].xft_draw = 0;
2274 if (newmw->menu.xft_font)
2275 for (i = 0; i < newmw->menu.windows_length; i++)
2276 newmw->menu.windows [i].xft_draw
2277 = XftDrawCreate (XtDisplay (newmw),
2278 newmw->menu.windows [i].window,
2279 DefaultVisual (XtDisplay (newmw), screen),
2280 newmw->core.colormap);
2282 #endif
2283 #ifdef HAVE_X_I18N
2284 if (newmw->menu.fontSet != oldmw->menu.fontSet && newmw->menu.fontSet != NULL)
2286 redisplay = True;
2287 newmw->menu.font_extents = XExtentsOfFontSet (newmw->menu.fontSet);
2289 #endif
2291 return redisplay;
2294 static void
2295 XlwMenuResize (w)
2296 Widget w;
2298 XlwMenuWidget mw = (XlwMenuWidget)w;
2300 if (mw->menu.popped_up)
2302 /* Don't allow the popup menu to resize itself. */
2303 mw->core.width = mw->menu.windows [0].width;
2304 mw->core.height = mw->menu.windows [0].height;
2305 mw->core.parent->core.width = mw->core.width;
2306 mw->core.parent->core.height = mw->core.height;
2308 else
2310 mw->menu.windows [0].width = mw->core.width;
2311 mw->menu.windows [0].height = mw->core.height;
2312 create_pixmap_for_menu (&mw->menu.windows [0], mw);
2316 \f/* Action procedures */
2317 static void
2318 handle_single_motion_event (mw, ev)
2319 XlwMenuWidget mw;
2320 XMotionEvent* ev;
2322 widget_value* val;
2323 int level;
2325 if (!map_event_to_widget_value (mw, ev, &val, &level))
2326 pop_new_stack_if_no_contents (mw);
2327 else
2328 set_new_state (mw, val, level);
2329 remap_menubar (mw);
2331 /* Sync with the display. Makes it feel better on X terms. */
2332 XSync (XtDisplay (mw), False);
2335 static void
2336 handle_motion_event (mw, ev)
2337 XlwMenuWidget mw;
2338 XMotionEvent* ev;
2340 int x = ev->x_root;
2341 int y = ev->y_root;
2342 int state = ev->state;
2343 XMotionEvent oldev = *ev;
2345 /* allow motion events to be generated again */
2346 if (ev->is_hint
2347 && XQueryPointer (XtDisplay (mw), ev->window,
2348 &ev->root, &ev->subwindow,
2349 &ev->x_root, &ev->y_root,
2350 &ev->x, &ev->y,
2351 &ev->state)
2352 && ev->state == state
2353 && (ev->x_root != x || ev->y_root != y))
2354 handle_single_motion_event (mw, ev);
2355 else
2356 handle_single_motion_event (mw, &oldev);
2359 static void
2360 Start (w, ev, params, num_params)
2361 Widget w;
2362 XEvent *ev;
2363 String *params;
2364 Cardinal *num_params;
2366 XlwMenuWidget mw = (XlwMenuWidget)w;
2368 if (!mw->menu.popped_up)
2370 menu_post_event = *ev;
2371 /* If event is set to CurrentTime, get the last known time stamp.
2372 This is for calculating if (popup) menus should stay up after
2373 a fast click. */
2374 if (menu_post_event.xbutton.time == CurrentTime)
2375 menu_post_event.xbutton.time
2376 = XtLastTimestampProcessed (XtDisplay (w));
2378 pop_up_menu (mw, (XButtonPressedEvent*) ev);
2380 else
2382 /* If we push a button while the menu is posted semipermanently,
2383 releasing the button should always pop the menu down. */
2384 next_release_must_exit = 1;
2386 /* notes the absolute position of the menubar window */
2387 mw->menu.windows [0].x = ev->xmotion.x_root - ev->xmotion.x;
2388 mw->menu.windows [0].y = ev->xmotion.y_root - ev->xmotion.y;
2390 /* handles the down like a move, slots are compatible */
2391 ev->xmotion.is_hint = 0;
2392 handle_motion_event (mw, &ev->xmotion);
2396 static void
2397 Drag (w, ev, params, num_params)
2398 Widget w;
2399 XEvent *ev;
2400 String *params;
2401 Cardinal *num_params;
2403 XlwMenuWidget mw = (XlwMenuWidget)w;
2404 if (mw->menu.popped_up)
2405 handle_motion_event (mw, &ev->xmotion);
2408 /* Do nothing.
2409 This is how we handle presses and releases of modifier keys. */
2410 static void
2411 Nothing (w, ev, params, num_params)
2412 Widget w;
2413 XEvent *ev;
2414 String *params;
2415 Cardinal *num_params;
2419 static widget_value *
2420 find_first_selectable (mw, item, skip_titles)
2421 XlwMenuWidget mw;
2422 widget_value *item;
2423 int skip_titles;
2425 widget_value *current = item;
2426 enum menu_separator separator;
2428 while (lw_separator_p (current->name, &separator, 0) || !current->enabled
2429 || (skip_titles && !current->call_data && !current->contents))
2430 if (current->next)
2431 current=current->next;
2432 else
2433 return NULL;
2435 return current;
2438 static widget_value *
2439 find_next_selectable (mw, item, skip_titles)
2440 XlwMenuWidget mw;
2441 widget_value *item;
2442 int skip_titles;
2444 widget_value *current = item;
2445 enum menu_separator separator;
2447 while (current->next && (current=current->next) &&
2448 (lw_separator_p (current->name, &separator, 0) || !current->enabled
2449 || (skip_titles && !current->call_data && !current->contents)))
2452 if (current == item)
2454 if (mw->menu.old_depth < 2)
2455 return current;
2456 current = mw->menu.old_stack [mw->menu.old_depth - 2]->contents;
2458 while (lw_separator_p (current->name, &separator, 0)
2459 || !current->enabled
2460 || (skip_titles && !current->call_data
2461 && !current->contents))
2463 if (current->next)
2464 current=current->next;
2466 if (current == item)
2467 break;
2472 return current;
2475 static widget_value *
2476 find_prev_selectable (mw, item, skip_titles)
2477 XlwMenuWidget mw;
2478 widget_value *item;
2479 int skip_titles;
2481 widget_value *current = item;
2482 widget_value *prev = item;
2484 while ((current=find_next_selectable (mw, current, skip_titles))
2485 != item)
2487 if (prev == current)
2488 break;
2489 prev=current;
2492 return prev;
2495 static void
2496 Down (w, ev, params, num_params)
2497 Widget w;
2498 XEvent *ev;
2499 String *params;
2500 Cardinal *num_params;
2502 XlwMenuWidget mw = (XlwMenuWidget) w;
2503 widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2504 int popup_menu_p = mw->menu.top_depth == 1;
2506 /* Inside top-level menu-bar? */
2507 if (mw->menu.old_depth == mw->menu.top_depth)
2508 /* When <down> in the menu-bar is pressed, display the corresponding
2509 sub-menu and select the first selectable menu item there.
2510 If this is a popup menu, skip title item of the popup. */
2511 set_new_state (mw,
2512 find_first_selectable (mw,
2513 selected_item->contents,
2514 popup_menu_p),
2515 mw->menu.old_depth);
2516 else
2517 /* Highlight next possible (enabled and not separator) menu item. */
2518 set_new_state (mw, find_next_selectable (mw, selected_item, popup_menu_p),
2519 mw->menu.old_depth - 1);
2521 remap_menubar (mw);
2524 static void
2525 Up (w, ev, params, num_params)
2526 Widget w;
2527 XEvent *ev;
2528 String *params;
2529 Cardinal *num_params;
2531 XlwMenuWidget mw = (XlwMenuWidget) w;
2532 widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2533 int popup_menu_p = mw->menu.top_depth == 1;
2535 /* Inside top-level menu-bar? */
2536 if (mw->menu.old_depth == mw->menu.top_depth)
2538 /* FIXME: this is tricky. <up> in the menu-bar should select the
2539 last selectable item in the list. So we select the first
2540 selectable one and find the previous selectable item. Is there
2541 a better way? */
2542 /* If this is a popup menu, skip title item of the popup. */
2543 set_new_state (mw,
2544 find_first_selectable (mw,
2545 selected_item->contents,
2546 popup_menu_p),
2547 mw->menu.old_depth);
2548 remap_menubar (mw);
2549 selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2550 set_new_state (mw,
2551 find_prev_selectable (mw,
2552 selected_item,
2553 popup_menu_p),
2554 mw->menu.old_depth - 1);
2556 else
2557 /* Highlight previous (enabled and not separator) menu item. */
2558 set_new_state (mw, find_prev_selectable (mw, selected_item, popup_menu_p),
2559 mw->menu.old_depth - 1);
2561 remap_menubar (mw);
2564 void
2565 Left (w, ev, params, num_params)
2566 Widget w;
2567 XEvent *ev;
2568 String *params;
2569 Cardinal *num_params;
2571 XlwMenuWidget mw = (XlwMenuWidget) w;
2572 widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2574 /* Inside top-level menu-bar? */
2575 if (mw->menu.old_depth == mw->menu.top_depth)
2576 /* When <left> in the menu-bar is pressed, display the previous item on
2577 the menu-bar. If the current item is the first one, highlight the
2578 last item in the menubar (probably Help). */
2579 set_new_state (mw, find_prev_selectable (mw, selected_item, 0),
2580 mw->menu.old_depth - 1);
2581 else if (mw->menu.old_depth == 1
2582 && selected_item->contents) /* Is this menu item expandable? */
2584 set_new_state (mw, selected_item->contents, mw->menu.old_depth);
2585 remap_menubar (mw);
2586 selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2587 if (!selected_item->enabled && find_first_selectable (mw,
2588 selected_item,
2590 set_new_state (mw, find_first_selectable (mw, selected_item, 0),
2591 mw->menu.old_depth - 1);
2594 else
2596 pop_new_stack_if_no_contents (mw);
2597 set_new_state (mw, mw->menu.old_stack [mw->menu.old_depth - 2],
2598 mw->menu.old_depth - 2);
2601 remap_menubar (mw);
2604 void
2605 Right (w, ev, params, num_params)
2606 Widget w;
2607 XEvent *ev;
2608 String *params;
2609 Cardinal *num_params;
2611 XlwMenuWidget mw = (XlwMenuWidget) w;
2612 widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2614 /* Inside top-level menu-bar? */
2615 if (mw->menu.old_depth == mw->menu.top_depth)
2616 /* When <right> in the menu-bar is pressed, display the next item on
2617 the menu-bar. If the current item is the last one, highlight the
2618 first item (probably File). */
2619 set_new_state (mw, find_next_selectable (mw, selected_item, 0),
2620 mw->menu.old_depth - 1);
2621 else if (selected_item->contents) /* Is this menu item expandable? */
2623 set_new_state (mw, selected_item->contents, mw->menu.old_depth);
2624 remap_menubar (mw);
2625 selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2626 if (!selected_item->enabled && find_first_selectable (mw,
2627 selected_item,
2629 set_new_state (mw, find_first_selectable (mw, selected_item, 0),
2630 mw->menu.old_depth - 1);
2632 else
2634 pop_new_stack_if_no_contents (mw);
2635 set_new_state (mw, mw->menu.old_stack [mw->menu.old_depth - 2],
2636 mw->menu.old_depth - 2);
2639 remap_menubar (mw);
2642 /* Handle key press and release events while menu is popped up.
2643 Our action is to get rid of the menu. */
2644 static void
2645 Key (w, ev, params, num_params)
2646 Widget w;
2647 XEvent *ev;
2648 String *params;
2649 Cardinal *num_params;
2651 XlwMenuWidget mw = (XlwMenuWidget)w;
2653 /* Pop down everything. */
2654 mw->menu.new_depth = 1;
2655 remap_menubar (mw);
2657 if (mw->menu.popped_up)
2659 mw->menu.popped_up = False;
2660 ungrab_all ((Widget)mw, ev->xmotion.time);
2661 if (XtIsShell (XtParent ((Widget) mw)))
2662 XtPopdown (XtParent ((Widget) mw));
2663 else
2665 XtRemoveGrab ((Widget) mw);
2666 display_menu (mw, 0, False, NULL, NULL, NULL);
2670 /* callback */
2671 XtCallCallbackList ((Widget)mw, mw->menu.select, (XtPointer)0);
2674 static void
2675 Select (w, ev, params, num_params)
2676 Widget w;
2677 XEvent *ev;
2678 String *params;
2679 Cardinal *num_params;
2681 XlwMenuWidget mw = (XlwMenuWidget)w;
2682 widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2684 /* If user releases the button quickly, without selecting anything,
2685 after the initial down-click that brought the menu up,
2686 do nothing. */
2687 if ((selected_item == 0
2688 || ((widget_value *) selected_item)->call_data == 0)
2689 && !next_release_must_exit
2690 && (ev->xbutton.time - menu_post_event.xbutton.time
2691 < XtGetMultiClickTime (XtDisplay (w))))
2692 return;
2694 /* pop down everything. */
2695 mw->menu.new_depth = 1;
2696 remap_menubar (mw);
2698 if (mw->menu.popped_up)
2700 mw->menu.popped_up = False;
2701 ungrab_all ((Widget)mw, ev->xmotion.time);
2702 if (XtIsShell (XtParent ((Widget) mw)))
2703 XtPopdown (XtParent ((Widget) mw));
2704 else
2706 XtRemoveGrab ((Widget) mw);
2707 display_menu (mw, 0, False, NULL, NULL, NULL);
2711 /* callback */
2712 XtCallCallbackList ((Widget)mw, mw->menu.select, (XtPointer)selected_item);
2716 \f/* Special code to pop-up a menu */
2717 static void
2718 pop_up_menu (mw, event)
2719 XlwMenuWidget mw;
2720 XButtonPressedEvent* event;
2722 int x = event->x_root;
2723 int y = event->y_root;
2724 int w;
2725 int h;
2726 int borderwidth = mw->menu.shadow_thickness;
2727 Screen* screen = XtScreen (mw);
2728 Display *display = XtDisplay (mw);
2730 next_release_must_exit = 0;
2732 mw->menu.inside_entry = NULL;
2733 XtCallCallbackList ((Widget)mw, mw->menu.open, NULL);
2735 if (XtIsShell (XtParent ((Widget)mw)))
2736 size_menu (mw, 0);
2738 w = mw->menu.windows [0].width;
2739 h = mw->menu.windows [0].height;
2741 x -= borderwidth;
2742 y -= borderwidth;
2743 if (x < borderwidth)
2744 x = borderwidth;
2745 if (x + w + 2 * borderwidth > WidthOfScreen (screen))
2746 x = WidthOfScreen (screen) - w - 2 * borderwidth;
2747 if (y < borderwidth)
2748 y = borderwidth;
2749 if (y + h + 2 * borderwidth> HeightOfScreen (screen))
2750 y = HeightOfScreen (screen) - h - 2 * borderwidth;
2752 mw->menu.popped_up = True;
2753 if (XtIsShell (XtParent ((Widget)mw)))
2755 XtConfigureWidget (XtParent ((Widget)mw), x, y, w, h,
2756 XtParent ((Widget)mw)->core.border_width);
2757 XtPopup (XtParent ((Widget)mw), XtGrabExclusive);
2758 display_menu (mw, 0, False, NULL, NULL, NULL);
2759 mw->menu.windows [0].x = x + borderwidth;
2760 mw->menu.windows [0].y = y + borderwidth;
2761 mw->menu.top_depth = 1; /* Popup menus don't have a bar so top is 1 */
2763 else
2765 XEvent *ev = (XEvent *) event;
2767 XtAddGrab ((Widget) mw, True, True);
2769 /* notes the absolute position of the menubar window */
2770 mw->menu.windows [0].x = ev->xmotion.x_root - ev->xmotion.x;
2771 mw->menu.windows [0].y = ev->xmotion.y_root - ev->xmotion.y;
2772 mw->menu.top_depth = 2;
2775 #ifdef emacs
2776 x_catch_errors (display);
2777 #endif
2778 if (XtGrabPointer ((Widget)mw, False,
2779 (PointerMotionMask
2780 | PointerMotionHintMask
2781 | ButtonReleaseMask
2782 | ButtonPressMask),
2783 GrabModeAsync, GrabModeAsync, None,
2784 mw->menu.cursor_shape,
2785 event->time) == Success)
2787 if (! GRAB_KEYBOARD
2788 || XtGrabKeyboard ((Widget)mw, False, GrabModeAsync,
2789 GrabModeAsync, event->time) == Success)
2791 XtSetKeyboardFocus((Widget)mw, None);
2792 pointer_grabbed = 1;
2794 else
2795 XtUngrabPointer ((Widget)mw, event->time);
2798 #ifdef emacs
2799 if (x_had_errors_p (display))
2801 pointer_grabbed = 0;
2802 XtUngrabPointer ((Widget)mw, event->time);
2804 x_uncatch_errors ();
2805 #endif
2807 ((XMotionEvent*)event)->is_hint = 0;
2808 handle_motion_event (mw, (XMotionEvent*)event);
2811 /* arch-tag: 657f43dd-dfd0-4cc9-910c-52935f01176e
2812 (do not change this comment) */