Change default values.
[emacs.git] / lwlib / xlwmenu.c
blob691213ab3bedc0b7c93c915d94aabe9bc1c24b57
1 /* Implements a lightweight menubar widget.
2 Copyright (C) 1992 Lucid, Inc.
3 Copyright (C) 2002 Free Software Foundation, Inc.
5 This file is part of the Lucid Widget Library.
7 The Lucid Widget Library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
10 any later version.
12 The Lucid Widget Library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GNU Emacs; see the file COPYING. If not, write to the
19 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA. */
22 /* Created by devin@lucid.com */
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
28 #include "../src/lisp.h"
30 #include <stdio.h>
32 #include <sys/types.h>
33 #if (defined __sun) && !(defined SUNOS41)
34 #define SUNOS41
35 #include <X11/Xos.h>
36 #undef SUNOS41
37 #else
38 #include <X11/Xos.h>
39 #endif
40 #include <X11/IntrinsicP.h>
41 #include <X11/ObjectP.h>
42 #include <X11/StringDefs.h>
43 #include <X11/cursorfont.h>
44 #include "xlwmenuP.h"
46 #ifdef emacs
48 /* Defined in xfns.c. When config.h defines `static' as empty, we get
49 redefinition errors when gray_bitmap is included more than once, so
50 we're referring to the one include in xfns.c here. */
52 extern int gray_bitmap_width;
53 extern int gray_bitmap_height;
54 extern char *gray_bitmap_bits;
56 /* Defined in xterm.c. */
57 extern int x_alloc_nearest_color_for_widget __P ((Widget, Colormap, XColor*));
58 extern int x_alloc_lighter_color_for_widget __P ((Widget, Display*, Colormap,
59 unsigned long *,
60 double, int));
61 extern int x_catch_errors __P ((Display*));
62 extern int x_uncatch_errors __P ((Display*, int));
63 extern int x_had_errors_p __P ((Display*));
64 extern int x_clear_errors __P ((Display*));
65 extern unsigned long x_copy_dpy_color __P ((Display *, Colormap,
66 unsigned long));
68 /* Defined in xfaces.c. */
69 extern void x_free_dpy_colors __P ((Display *, Screen *, Colormap,
70 unsigned long *pixels, int npixels));
71 #else /* not emacs */
73 #include <X11/bitmaps/gray>
74 #define gray_bitmap_width gray_width
75 #define gray_bitmap_height gray_height
76 #define gray_bitmap_bits gray_bits
78 #endif /* not emacs */
80 static int pointer_grabbed;
81 static XEvent menu_post_event;
83 XFontStruct *xlwmenu_default_font;
85 static char
86 xlwMenuTranslations [] =
87 "<BtnDown>: start()\n\
88 <Motion>: drag()\n\
89 <BtnUp>: select()\n\
90 <Key>Shift_L: nothing()\n\
91 <Key>Shift_R: nothing()\n\
92 <Key>Meta_L: nothing()\n\
93 <Key>Meta_R: nothing()\n\
94 <Key>Control_L: nothing()\n\
95 <Key>Control_R: nothing()\n\
96 <Key>Hyper_L: nothing()\n\
97 <Key>Hyper_R: nothing()\n\
98 <Key>Super_L: nothing()\n\
99 <Key>Super_R: nothing()\n\
100 <Key>Alt_L: nothing()\n\
101 <Key>Alt_R: nothing()\n\
102 <Key>Caps_Lock: nothing()\n\
103 <Key>Shift_Lock: nothing()\n\
104 <KeyUp>Shift_L: nothing()\n\
105 <KeyUp>Shift_R: nothing()\n\
106 <KeyUp>Meta_L: nothing()\n\
107 <KeyUp>Meta_R: nothing()\n\
108 <KeyUp>Control_L: nothing()\n\
109 <KeyUp>Control_R: nothing()\n\
110 <KeyUp>Hyper_L: nothing()\n\
111 <KeyUp>Hyper_R: nothing()\n\
112 <KeyUp>Super_L: nothing()\n\
113 <KeyUp>Super_R: nothing()\n\
114 <KeyUp>Alt_L: nothing()\n\
115 <KeyUp>Alt_R: nothing()\n\
116 <KeyUp>Caps_Lock: nothing()\n\
117 <KeyUp>Shift_Lock:nothing()\n\
118 <Key>Return: select()\n\
119 <Key>Down: down()\n\
120 <Key>Up: up()\n\
121 <Key>Left: left()\n\
122 <Key>Right: right()\n\
123 <Key>: key()\n\
124 <KeyUp>: key()\n\
127 /* FIXME: Space should toggle toggleable menu item but not remove the menu
128 so you can toggle the next one without entering the menu again. */
130 /* FIXME: Should ESC close one level of menu structure or the complete menu? */
132 /* FIXME: F10 should enter the menu, the first one in the menu-bar. */
134 #define offset(field) XtOffset(XlwMenuWidget, field)
135 static XtResource
136 xlwMenuResources[] =
138 {XtNfont, XtCFont, XtRFontStruct, sizeof(XFontStruct *),
139 offset(menu.font),XtRString, "XtDefaultFont"},
140 {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
141 offset(menu.foreground), XtRString, "XtDefaultForeground"},
142 {XtNbuttonForeground, XtCButtonForeground, XtRPixel, sizeof(Pixel),
143 offset(menu.button_foreground), XtRString, "XtDefaultForeground"},
144 {XtNmargin, XtCMargin, XtRDimension, sizeof(Dimension),
145 offset(menu.margin), XtRImmediate, (XtPointer)1},
146 {XtNhorizontalSpacing, XtCMargin, XtRDimension, sizeof(Dimension),
147 offset(menu.horizontal_spacing), XtRImmediate, (XtPointer)3},
148 {XtNverticalSpacing, XtCMargin, XtRDimension, sizeof(Dimension),
149 offset(menu.vertical_spacing), XtRImmediate, (XtPointer)2},
150 {XtNarrowSpacing, XtCMargin, XtRDimension, sizeof(Dimension),
151 offset(menu.arrow_spacing), XtRImmediate, (XtPointer)10},
153 {XmNshadowThickness, XmCShadowThickness, XtRDimension,
154 sizeof (Dimension), offset (menu.shadow_thickness),
155 XtRImmediate, (XtPointer)1},
156 {XmNtopShadowColor, XmCTopShadowColor, XtRPixel, sizeof (Pixel),
157 offset (menu.top_shadow_color), XtRImmediate, (XtPointer)-1},
158 {XmNbottomShadowColor, XmCBottomShadowColor, XtRPixel, sizeof (Pixel),
159 offset (menu.bottom_shadow_color), XtRImmediate, (XtPointer)-1},
160 {XmNtopShadowPixmap, XmCTopShadowPixmap, XtRPixmap, sizeof (Pixmap),
161 offset (menu.top_shadow_pixmap), XtRImmediate, (XtPointer)None},
162 {XmNbottomShadowPixmap, XmCBottomShadowPixmap, XtRPixmap, sizeof (Pixmap),
163 offset (menu.bottom_shadow_pixmap), XtRImmediate, (XtPointer)None},
165 {XtNopen, XtCCallback, XtRCallback, sizeof(XtPointer),
166 offset(menu.open), XtRCallback, (XtPointer)NULL},
167 {XtNselect, XtCCallback, XtRCallback, sizeof(XtPointer),
168 offset(menu.select), XtRCallback, (XtPointer)NULL},
169 {XtNhighlightCallback, XtCCallback, XtRCallback, sizeof(XtPointer),
170 offset(menu.highlight), XtRCallback, (XtPointer)NULL},
171 {XtNmenu, XtCMenu, XtRPointer, sizeof(XtPointer),
172 offset(menu.contents), XtRImmediate, (XtPointer)NULL},
173 {XtNcursor, XtCCursor, XtRCursor, sizeof(Cursor),
174 offset(menu.cursor_shape), XtRString, (XtPointer)"right_ptr"},
175 {XtNhorizontal, XtCHorizontal, XtRInt, sizeof(int),
176 offset(menu.horizontal), XtRImmediate, (XtPointer)True},
178 #undef offset
180 static Boolean XlwMenuSetValues();
181 static void XlwMenuRealize();
182 static void XlwMenuRedisplay();
183 static void XlwMenuResize();
184 static void XlwMenuInitialize();
185 static void XlwMenuRedisplay();
186 static void XlwMenuDestroy();
187 static void XlwMenuClassInitialize();
188 static void Start();
189 static void Drag();
190 static void Down();
191 static void Up();
192 static void Left();
193 static void Right();
194 static void Select();
195 static void Key();
196 static void Nothing();
197 static int separator_height ();
199 static XtActionsRec
200 xlwMenuActionsList [] =
202 {"start", Start},
203 {"drag", Drag},
204 {"down", Down},
205 {"up", Up},
206 {"left", Left},
207 {"right", Right},
208 {"select", Select},
209 {"key", Key},
210 {"nothing", Nothing},
213 #define SuperClass ((CoreWidgetClass)&coreClassRec)
215 XlwMenuClassRec xlwMenuClassRec =
217 { /* CoreClass fields initialization */
218 (WidgetClass) SuperClass, /* superclass */
219 "XlwMenu", /* class_name */
220 sizeof(XlwMenuRec), /* size */
221 XlwMenuClassInitialize, /* class_initialize */
222 NULL, /* class_part_initialize */
223 FALSE, /* class_inited */
224 XlwMenuInitialize, /* initialize */
225 NULL, /* initialize_hook */
226 XlwMenuRealize, /* realize */
227 xlwMenuActionsList, /* actions */
228 XtNumber(xlwMenuActionsList), /* num_actions */
229 xlwMenuResources, /* resources */
230 XtNumber(xlwMenuResources), /* resource_count */
231 NULLQUARK, /* xrm_class */
232 TRUE, /* compress_motion */
233 TRUE, /* compress_exposure */
234 TRUE, /* compress_enterleave */
235 FALSE, /* visible_interest */
236 XlwMenuDestroy, /* destroy */
237 XlwMenuResize, /* resize */
238 XlwMenuRedisplay, /* expose */
239 XlwMenuSetValues, /* set_values */
240 NULL, /* set_values_hook */
241 XtInheritSetValuesAlmost, /* set_values_almost */
242 NULL, /* get_values_hook */
243 NULL, /* accept_focus */
244 XtVersion, /* version */
245 NULL, /* callback_private */
246 xlwMenuTranslations, /* tm_table */
247 XtInheritQueryGeometry, /* query_geometry */
248 XtInheritDisplayAccelerator, /* display_accelerator */
249 NULL /* extension */
250 }, /* XlwMenuClass fields initialization */
252 0 /* dummy */
256 WidgetClass xlwMenuWidgetClass = (WidgetClass) &xlwMenuClassRec;
258 int submenu_destroyed;
260 /* For debug, if installation-directory is non-nil this is not an installed
261 Emacs. In that case we do not grab the keyboard to make it easier to
262 debug. */
263 #define GRAB_KEYBOARD (EQ (Vinstallation_directory, Qnil))
265 static int next_release_must_exit;
267 \f/* Utilities */
269 /* Ungrab pointer and keyboard */
270 static void
271 ungrab_all (w, ungrabtime)
272 Widget w;
273 Time ungrabtime;
275 XtUngrabPointer (w, ungrabtime);
276 if (GRAB_KEYBOARD) XtUngrabKeyboard (w, ungrabtime);
279 /* Like abort, but remove grabs from widget W before. */
281 static void
282 abort_gracefully (w)
283 Widget w;
285 if (XtIsShell (XtParent (w)))
286 XtRemoveGrab (w);
287 ungrab_all (w, CurrentTime);
288 abort ();
291 static void
292 push_new_stack (mw, val)
293 XlwMenuWidget mw;
294 widget_value* val;
296 if (!mw->menu.new_stack)
298 mw->menu.new_stack_length = 10;
299 mw->menu.new_stack =
300 (widget_value**)XtCalloc (mw->menu.new_stack_length,
301 sizeof (widget_value*));
303 else if (mw->menu.new_depth == mw->menu.new_stack_length)
305 mw->menu.new_stack_length *= 2;
306 mw->menu.new_stack =
307 (widget_value**)XtRealloc ((char*)mw->menu.new_stack,
308 mw->menu.new_stack_length * sizeof (widget_value*));
310 mw->menu.new_stack [mw->menu.new_depth++] = val;
313 static void
314 pop_new_stack_if_no_contents (mw)
315 XlwMenuWidget mw;
317 if (mw->menu.new_depth)
319 if (!mw->menu.new_stack [mw->menu.new_depth - 1]->contents)
320 mw->menu.new_depth -= 1;
324 static void
325 make_old_stack_space (mw, n)
326 XlwMenuWidget mw;
327 int n;
329 if (!mw->menu.old_stack)
331 mw->menu.old_stack_length = 10;
332 mw->menu.old_stack =
333 (widget_value**)XtCalloc (mw->menu.old_stack_length,
334 sizeof (widget_value*));
336 else if (mw->menu.old_stack_length < n)
338 mw->menu.old_stack_length *= 2;
339 mw->menu.old_stack =
340 (widget_value**)XtRealloc ((char*)mw->menu.old_stack,
341 mw->menu.old_stack_length * sizeof (widget_value*));
345 \f/* Size code */
347 string_width (mw, s)
348 XlwMenuWidget mw;
349 char *s;
351 XCharStruct xcs;
352 int drop;
354 XTextExtents (mw->menu.font, s, strlen (s), &drop, &drop, &drop, &xcs);
355 return xcs.width;
358 static int
359 arrow_width (mw)
360 XlwMenuWidget mw;
362 return (mw->menu.font->ascent * 3/4) | 1;
365 /* Return the width of toggle buttons of widget MW. */
367 static int
368 toggle_button_width (mw)
369 XlwMenuWidget mw;
371 return ((mw->menu.font->ascent + mw->menu.font->descent) * 2 / 3) | 1;
375 /* Return the width of radio buttons of widget MW. */
377 static int
378 radio_button_width (mw)
379 XlwMenuWidget mw;
381 return toggle_button_width (mw) * 1.41;
385 static XtResource
386 nameResource[] =
388 {"labelString", "LabelString", XtRString, sizeof(String),
389 0, XtRImmediate, 0},
392 static char*
393 resource_widget_value (mw, val)
394 XlwMenuWidget mw;
395 widget_value *val;
397 if (!val->toolkit_data)
399 char* resourced_name = NULL;
400 char* complete_name;
401 XtGetSubresources ((Widget) mw,
402 (XtPointer) &resourced_name,
403 val->name, val->name,
404 nameResource, 1, NULL, 0);
405 if (!resourced_name)
406 resourced_name = val->name;
407 if (!val->value)
409 complete_name = (char *) XtMalloc (strlen (resourced_name) + 1);
410 strcpy (complete_name, resourced_name);
412 else
414 int complete_length =
415 strlen (resourced_name) + strlen (val->value) + 2;
416 complete_name = XtMalloc (complete_length);
417 *complete_name = 0;
418 strcat (complete_name, resourced_name);
419 strcat (complete_name, " ");
420 strcat (complete_name, val->value);
423 val->toolkit_data = complete_name;
424 val->free_toolkit_data = True;
426 return (char*)val->toolkit_data;
429 /* Returns the sizes of an item */
430 static void
431 size_menu_item (mw, val, horizontal_p, label_width, rest_width, button_width,
432 height)
433 XlwMenuWidget mw;
434 widget_value* val;
435 int horizontal_p;
436 int* label_width;
437 int* rest_width;
438 int* button_width;
439 int* height;
441 enum menu_separator separator;
443 if (lw_separator_p (val->name, &separator, 0))
445 *height = separator_height (separator);
446 *label_width = 1;
447 *rest_width = 0;
448 *button_width = 0;
450 else
452 *height =
453 mw->menu.font->ascent + mw->menu.font->descent
454 + 2 * mw->menu.vertical_spacing + 2 * mw->menu.shadow_thickness;
456 *label_width =
457 string_width (mw, resource_widget_value (mw, val))
458 + mw->menu.horizontal_spacing + mw->menu.shadow_thickness;
460 *rest_width = mw->menu.horizontal_spacing + mw->menu.shadow_thickness;
461 if (!horizontal_p)
463 if (val->contents)
464 /* Add width of the arrow displayed for submenus. */
465 *rest_width += arrow_width (mw) + mw->menu.arrow_spacing;
466 else if (val->key)
467 /* Add width of key equivalent string. */
468 *rest_width += (string_width (mw, val->key)
469 + mw->menu.arrow_spacing);
471 if (val->button_type == BUTTON_TYPE_TOGGLE)
472 *button_width = (toggle_button_width (mw)
473 + mw->menu.horizontal_spacing);
474 else if (val->button_type == BUTTON_TYPE_RADIO)
475 *button_width = (radio_button_width (mw)
476 + mw->menu.horizontal_spacing);
481 static void
482 size_menu (mw, level)
483 XlwMenuWidget mw;
484 int level;
486 unsigned int label_width = 0;
487 int rest_width = 0;
488 int button_width = 0;
489 int max_rest_width = 0;
490 int max_button_width = 0;
491 unsigned int height = 0;
492 int horizontal_p = mw->menu.horizontal && (level == 0);
493 widget_value* val;
494 window_state* ws;
496 if (level >= mw->menu.old_depth)
497 abort_gracefully ((Widget) mw);
499 ws = &mw->menu.windows [level];
500 ws->width = 0;
501 ws->height = 0;
502 ws->label_width = 0;
503 ws->button_width = 0;
505 for (val = mw->menu.old_stack [level]->contents; val; val = val->next)
507 size_menu_item (mw, val, horizontal_p, &label_width, &rest_width,
508 &button_width, &height);
509 if (horizontal_p)
511 ws->width += label_width + rest_width;
512 if (height > ws->height)
513 ws->height = height;
515 else
517 if (label_width > ws->label_width)
518 ws->label_width = label_width;
519 if (rest_width > max_rest_width)
520 max_rest_width = rest_width;
521 if (button_width > max_button_width)
522 max_button_width = button_width;
523 ws->height += height;
527 if (horizontal_p)
528 ws->label_width = ws->button_width = 0;
529 else
531 ws->width = ws->label_width + max_rest_width + max_button_width;
532 ws->button_width = max_button_width;
535 ws->width += 2 * mw->menu.shadow_thickness;
536 ws->height += 2 * mw->menu.shadow_thickness;
538 if (horizontal_p)
540 ws->width += 2 * mw->menu.margin;
541 ws->height += 2 * mw->menu.margin;
546 \f/* Display code */
548 static void
549 draw_arrow (mw, window, gc, x, y, width, down_p)
550 XlwMenuWidget mw;
551 Window window;
552 GC gc;
553 int x;
554 int y;
555 int width;
556 int down_p;
558 Display *dpy = XtDisplay (mw);
559 GC top_gc = mw->menu.shadow_top_gc;
560 GC bottom_gc = mw->menu.shadow_bottom_gc;
561 int thickness = mw->menu.shadow_thickness;
562 int height = width;
563 XPoint pt[10];
564 /* alpha = atan (0.5)
565 factor = (1 + sin (alpha)) / cos (alpha) */
566 double factor = 1.62;
567 int thickness2 = thickness * factor;
569 y += (mw->menu.font->ascent + mw->menu.font->descent - height) / 2;
571 if (down_p)
573 GC temp;
574 temp = top_gc;
575 top_gc = bottom_gc;
576 bottom_gc = temp;
579 pt[0].x = x;
580 pt[0].y = y + height;
581 pt[1].x = x + thickness;
582 pt[1].y = y + height - thickness2;
583 pt[2].x = x + thickness2;
584 pt[2].y = y + thickness2;
585 pt[3].x = x;
586 pt[3].y = y;
587 XFillPolygon (dpy, window, top_gc, pt, 4, Convex, CoordModeOrigin);
589 pt[0].x = x;
590 pt[0].y = y;
591 pt[1].x = x + thickness;
592 pt[1].y = y + thickness2;
593 pt[2].x = x + width - thickness2;
594 pt[2].y = y + height / 2;
595 pt[3].x = x + width;
596 pt[3].y = y + height / 2;
597 XFillPolygon (dpy, window, top_gc, pt, 4, Convex, CoordModeOrigin);
599 pt[0].x = x;
600 pt[0].y = y + height;
601 pt[1].x = x + thickness;
602 pt[1].y = y + height - thickness2;
603 pt[2].x = x + width - thickness2;
604 pt[2].y = y + height / 2;
605 pt[3].x = x + width;
606 pt[3].y = y + height / 2;
607 XFillPolygon (dpy, window, bottom_gc, pt, 4, Convex, CoordModeOrigin);
612 static void
613 draw_shadow_rectangle (mw, window, x, y, width, height, erase_p, down_p)
614 XlwMenuWidget mw;
615 Window window;
616 int x;
617 int y;
618 int width;
619 int height;
620 int erase_p;
621 int down_p;
623 Display *dpy = XtDisplay (mw);
624 GC top_gc = !erase_p ? mw->menu.shadow_top_gc : mw->menu.background_gc;
625 GC bottom_gc = !erase_p ? mw->menu.shadow_bottom_gc : mw->menu.background_gc;
626 int thickness = mw->menu.shadow_thickness;
627 XPoint points [4];
629 if (!erase_p && down_p)
631 GC temp;
632 temp = top_gc;
633 top_gc = bottom_gc;
634 bottom_gc = temp;
637 points [0].x = x;
638 points [0].y = y;
639 points [1].x = x + width;
640 points [1].y = y;
641 points [2].x = x + width - thickness;
642 points [2].y = y + thickness;
643 points [3].x = x;
644 points [3].y = y + thickness;
645 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
646 points [0].x = x;
647 points [0].y = y + thickness;
648 points [1].x = x;
649 points [1].y = y + height;
650 points [2].x = x + thickness;
651 points [2].y = y + height - thickness;
652 points [3].x = x + thickness;
653 points [3].y = y + thickness;
654 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
655 points [0].x = x + width;
656 points [0].y = y;
657 points [1].x = x + width - thickness;
658 points [1].y = y + thickness;
659 points [2].x = x + width - thickness;
660 points [2].y = y + height - thickness;
661 points [3].x = x + width;
662 points [3].y = y + height - thickness;
663 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
664 points [0].x = x;
665 points [0].y = y + height;
666 points [1].x = x + width;
667 points [1].y = y + height;
668 points [2].x = x + width;
669 points [2].y = y + height - thickness;
670 points [3].x = x + thickness;
671 points [3].y = y + height - thickness;
672 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
676 static void
677 draw_shadow_rhombus (mw, window, x, y, width, height, erase_p, down_p)
678 XlwMenuWidget mw;
679 Window window;
680 int x;
681 int y;
682 int width;
683 int height;
684 int erase_p;
685 int down_p;
687 Display *dpy = XtDisplay (mw);
688 GC top_gc = !erase_p ? mw->menu.shadow_top_gc : mw->menu.background_gc;
689 GC bottom_gc = !erase_p ? mw->menu.shadow_bottom_gc : mw->menu.background_gc;
690 int thickness = mw->menu.shadow_thickness;
691 XPoint points [4];
693 if (!erase_p && down_p)
695 GC temp;
696 temp = top_gc;
697 top_gc = bottom_gc;
698 bottom_gc = temp;
701 points [0].x = x;
702 points [0].y = y + height / 2;
703 points [1].x = x + thickness;
704 points [1].y = y + height / 2;
705 points [2].x = x + width / 2;
706 points [2].y = y + thickness;
707 points [3].x = x + width / 2;
708 points [3].y = y;
709 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
710 points [0].x = x + width / 2;
711 points [0].y = y;
712 points [1].x = x + width / 2;
713 points [1].y = y + thickness;
714 points [2].x = x + width - thickness;
715 points [2].y = y + height / 2;
716 points [3].x = x + width;
717 points [3].y = y + height / 2;
718 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
719 points [0].x = x;
720 points [0].y = y + height / 2;
721 points [1].x = x + thickness;
722 points [1].y = y + height / 2;
723 points [2].x = x + width / 2;
724 points [2].y = y + height - thickness;
725 points [3].x = x + width / 2;
726 points [3].y = y + height;
727 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
728 points [0].x = x + width / 2;
729 points [0].y = y + height;
730 points [1].x = x + width / 2;
731 points [1].y = y + height - thickness;
732 points [2].x = x + width - thickness;
733 points [2].y = y + height / 2;
734 points [3].x = x + width;
735 points [3].y = y + height / 2;
736 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
740 /* Draw a toggle button on widget MW, X window WINDOW. X/Y is the
741 top-left corner of the menu item. SELECTED_P non-zero means the
742 toggle button is selected. */
744 static void
745 draw_toggle (mw, window, x, y, selected_p)
746 XlwMenuWidget mw;
747 Window window;
748 int x, y, selected_p;
750 int width, height;
752 width = toggle_button_width (mw);
753 height = width;
754 x += mw->menu.horizontal_spacing;
755 y += (mw->menu.font->ascent - height) / 2;
756 draw_shadow_rectangle (mw, window, x, y, width, height, False, selected_p);
760 /* Draw a radio button on widget MW, X window WINDOW. X/Y is the
761 top-left corner of the menu item. SELECTED_P non-zero means the
762 toggle button is selected. */
764 static void
765 draw_radio (mw, window, x, y, selected_p)
766 XlwMenuWidget mw;
767 Window window;
768 int x, y, selected_p;
770 int width, height;
772 width = radio_button_width (mw);
773 height = width;
774 x += mw->menu.horizontal_spacing;
775 y += (mw->menu.font->ascent - height) / 2;
776 draw_shadow_rhombus (mw, window, x, y, width, height, False, selected_p);
780 /* Draw a menu separator on widget MW, X window WINDOW. X/Y is the
781 top-left corner of the menu item. WIDTH is the width of the
782 separator to draw. TYPE is the separator type. */
784 static void
785 draw_separator (mw, window, x, y, width, type)
786 XlwMenuWidget mw;
787 Window window;
788 int x, y, width;
789 enum menu_separator type;
791 Display *dpy = XtDisplay (mw);
792 XGCValues xgcv;
794 switch (type)
796 case SEPARATOR_NO_LINE:
797 break;
799 case SEPARATOR_SINGLE_LINE:
800 XDrawLine (dpy, window, mw->menu.foreground_gc,
801 x, y, x + width, y);
802 break;
804 case SEPARATOR_DOUBLE_LINE:
805 draw_separator (mw, window, x, y, width, SEPARATOR_SINGLE_LINE);
806 draw_separator (mw, window, x, y + 2, width, SEPARATOR_SINGLE_LINE);
807 break;
809 case SEPARATOR_SINGLE_DASHED_LINE:
810 xgcv.line_style = LineOnOffDash;
811 XChangeGC (dpy, mw->menu.foreground_gc, GCLineStyle, &xgcv);
812 XDrawLine (dpy, window, mw->menu.foreground_gc,
813 x, y, x + width, y);
814 xgcv.line_style = LineSolid;
815 XChangeGC (dpy, mw->menu.foreground_gc, GCLineStyle, &xgcv);
816 break;
818 case SEPARATOR_DOUBLE_DASHED_LINE:
819 draw_separator (mw, window, x, y, width,
820 SEPARATOR_SINGLE_DASHED_LINE);
821 draw_separator (mw, window, x, y + 2, width,
822 SEPARATOR_SINGLE_DASHED_LINE);
823 break;
825 case SEPARATOR_SHADOW_ETCHED_IN:
826 XDrawLine (dpy, window, mw->menu.shadow_bottom_gc,
827 x, y, x + width, y);
828 XDrawLine (dpy, window, mw->menu.shadow_top_gc,
829 x, y + 1, x + width, y + 1);
830 break;
832 case SEPARATOR_SHADOW_ETCHED_OUT:
833 XDrawLine (dpy, window, mw->menu.shadow_top_gc,
834 x, y, x + width, y);
835 XDrawLine (dpy, window, mw->menu.shadow_bottom_gc,
836 x, y + 1, x + width, y + 1);
837 break;
839 case SEPARATOR_SHADOW_ETCHED_IN_DASH:
840 xgcv.line_style = LineOnOffDash;
841 XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
842 XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
843 draw_separator (mw, window, x, y, width, SEPARATOR_SHADOW_ETCHED_IN);
844 xgcv.line_style = LineSolid;
845 XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
846 XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
847 break;
849 case SEPARATOR_SHADOW_ETCHED_OUT_DASH:
850 xgcv.line_style = LineOnOffDash;
851 XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
852 XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
853 draw_separator (mw, window, x, y, width, SEPARATOR_SHADOW_ETCHED_OUT);
854 xgcv.line_style = LineSolid;
855 XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
856 XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
857 break;
859 case SEPARATOR_SHADOW_DOUBLE_ETCHED_IN:
860 draw_separator (mw, window, x, y, width, SEPARATOR_SHADOW_ETCHED_IN);
861 draw_separator (mw, window, x, y + 3, width, SEPARATOR_SHADOW_ETCHED_IN);
862 break;
864 case SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT:
865 draw_separator (mw, window, x, y, width,
866 SEPARATOR_SHADOW_ETCHED_OUT);
867 draw_separator (mw, window, x, y + 3, width,
868 SEPARATOR_SHADOW_ETCHED_OUT);
869 break;
871 case SEPARATOR_SHADOW_DOUBLE_ETCHED_IN_DASH:
872 xgcv.line_style = LineOnOffDash;
873 XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
874 XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
875 draw_separator (mw, window, x, y, width,
876 SEPARATOR_SHADOW_DOUBLE_ETCHED_IN);
877 xgcv.line_style = LineSolid;
878 XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
879 XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
880 break;
882 case SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT_DASH:
883 xgcv.line_style = LineOnOffDash;
884 XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
885 XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
886 draw_separator (mw, window, x, y, width,
887 SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT);
888 xgcv.line_style = LineSolid;
889 XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
890 XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
891 break;
893 default:
894 abort ();
899 /* Return the pixel height of menu separator SEPARATOR. */
901 static int
902 separator_height (separator)
903 enum menu_separator separator;
905 switch (separator)
907 case SEPARATOR_NO_LINE:
908 return 2;
910 case SEPARATOR_SINGLE_LINE:
911 case SEPARATOR_SINGLE_DASHED_LINE:
912 return 1;
914 case SEPARATOR_DOUBLE_LINE:
915 case SEPARATOR_DOUBLE_DASHED_LINE:
916 return 3;
918 case SEPARATOR_SHADOW_ETCHED_IN:
919 case SEPARATOR_SHADOW_ETCHED_OUT:
920 case SEPARATOR_SHADOW_ETCHED_IN_DASH:
921 case SEPARATOR_SHADOW_ETCHED_OUT_DASH:
922 return 2;
924 case SEPARATOR_SHADOW_DOUBLE_ETCHED_IN:
925 case SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT:
926 case SEPARATOR_SHADOW_DOUBLE_ETCHED_IN_DASH:
927 case SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT_DASH:
928 return 5;
930 default:
931 abort ();
936 /* Display the menu item and increment where.x and where.y to show how large
937 the menu item was. */
939 static void
940 display_menu_item (mw, val, ws, where, highlighted_p, horizontal_p,
941 just_compute_p)
942 XlwMenuWidget mw;
943 widget_value* val;
944 window_state* ws;
945 XPoint* where;
946 Boolean highlighted_p;
947 Boolean horizontal_p;
948 Boolean just_compute_p;
950 GC deco_gc;
951 GC text_gc;
952 int font_ascent = mw->menu.font->ascent;
953 int font_descent = mw->menu.font->descent;
954 int shadow = mw->menu.shadow_thickness;
955 int margin = mw->menu.margin;
956 int h_spacing = mw->menu.horizontal_spacing;
957 int v_spacing = mw->menu.vertical_spacing;
958 int label_width;
959 int rest_width;
960 int button_width;
961 int height;
962 int width;
963 enum menu_separator separator;
964 int separator_p = lw_separator_p (val->name, &separator, 0);
966 /* compute the sizes of the item */
967 size_menu_item (mw, val, horizontal_p, &label_width, &rest_width,
968 &button_width, &height);
970 if (horizontal_p)
971 width = label_width + rest_width;
972 else
974 label_width = ws->label_width;
975 width = ws->width - 2 * shadow;
978 /* Only highlight an enabled item that has a callback. */
979 if (highlighted_p)
980 if (!val->enabled || !(val->call_data || val->contents))
981 highlighted_p = 0;
983 /* do the drawing. */
984 if (!just_compute_p)
986 /* Add the shadow border of the containing menu */
987 int x = where->x + shadow;
988 int y = where->y + shadow;
990 if (horizontal_p)
992 x += margin;
993 y += margin;
996 /* pick the foreground and background GC. */
997 if (val->enabled)
998 text_gc = mw->menu.foreground_gc;
999 else
1000 text_gc = mw->menu.inactive_gc;
1001 deco_gc = mw->menu.foreground_gc;
1003 if (separator_p)
1005 draw_separator (mw, ws->window, x, y, width, separator);
1007 else
1009 int x_offset = x + h_spacing + shadow;
1010 char* display_string = resource_widget_value (mw, val);
1011 draw_shadow_rectangle (mw, ws->window, x, y, width, height, True,
1012 False);
1014 /* Deal with centering a menu title. */
1015 if (!horizontal_p && !val->contents && !val->call_data)
1017 int l = string_width (mw, display_string);
1019 if (width > l)
1020 x_offset = (width - l) >> 1;
1022 else if (!horizontal_p && ws->button_width)
1023 x_offset += ws->button_width;
1026 XDrawString (XtDisplay (mw), ws->window, text_gc, x_offset,
1027 y + v_spacing + shadow + font_ascent,
1028 display_string, strlen (display_string));
1030 if (!horizontal_p)
1032 if (val->button_type == BUTTON_TYPE_TOGGLE)
1033 draw_toggle (mw, ws->window, x, y + v_spacing + shadow,
1034 val->selected);
1035 else if (val->button_type == BUTTON_TYPE_RADIO)
1036 draw_radio (mw, ws->window, x, y + v_spacing + shadow,
1037 val->selected);
1039 if (val->contents)
1041 int a_w = arrow_width (mw);
1042 draw_arrow (mw, ws->window, deco_gc,
1043 x + width - a_w
1044 - mw->menu.horizontal_spacing
1045 - mw->menu.shadow_thickness,
1046 y + v_spacing + shadow, a_w,
1047 highlighted_p);
1049 else if (val->key)
1051 XDrawString (XtDisplay (mw), ws->window, text_gc,
1052 x + label_width + mw->menu.arrow_spacing,
1053 y + v_spacing + shadow + font_ascent,
1054 val->key, strlen (val->key));
1057 else
1059 XDrawRectangle (XtDisplay (mw), ws->window,
1060 mw->menu.background_gc,
1061 x + shadow, y + shadow,
1062 label_width + h_spacing - 1,
1063 font_ascent + font_descent + 2 * v_spacing - 1);
1064 draw_shadow_rectangle (mw, ws->window, x, y, width, height,
1065 True, False);
1068 if (highlighted_p)
1069 draw_shadow_rectangle (mw, ws->window, x, y, width, height, False,
1070 False);
1074 where->x += width;
1075 where->y += height;
1078 static void
1079 display_menu (mw, level, just_compute_p, highlighted_pos, hit, hit_return,
1080 this, that)
1081 XlwMenuWidget mw;
1082 int level;
1083 Boolean just_compute_p;
1084 XPoint* highlighted_pos;
1085 XPoint* hit;
1086 widget_value** hit_return;
1087 widget_value* this;
1088 widget_value* that;
1090 widget_value* val;
1091 widget_value* following_item;
1092 window_state* ws;
1093 XPoint where;
1094 int horizontal_p = mw->menu.horizontal && (level == 0);
1095 int highlighted_p;
1096 int just_compute_this_one_p;
1097 /* This is set nonzero if the element containing HIGHLIGHTED_POS
1098 is disabled, so that we do not return any subsequent element either. */
1099 int no_return = 0;
1100 enum menu_separator separator;
1102 if (level >= mw->menu.old_depth)
1103 abort_gracefully ((Widget) mw);
1105 if (level < mw->menu.old_depth - 1)
1106 following_item = mw->menu.old_stack [level + 1];
1107 else
1108 following_item = NULL;
1110 if (hit)
1111 *hit_return = NULL;
1113 where.x = 0;
1114 where.y = 0;
1116 ws = &mw->menu.windows [level];
1117 for (val = mw->menu.old_stack [level]->contents; val; val = val->next)
1119 highlighted_p = val == following_item;
1120 if (highlighted_p && highlighted_pos)
1122 if (horizontal_p)
1123 highlighted_pos->x = where.x;
1124 else
1125 highlighted_pos->y = where.y;
1128 just_compute_this_one_p =
1129 just_compute_p || ((this || that) && val != this && val != that);
1131 display_menu_item (mw, val, ws, &where, highlighted_p, horizontal_p,
1132 just_compute_this_one_p);
1134 if (highlighted_p && highlighted_pos)
1136 if (horizontal_p)
1137 highlighted_pos->y = where.y;
1138 else
1139 highlighted_pos->x = where.x;
1142 if (hit
1143 && !*hit_return
1144 && (horizontal_p ? hit->x < where.x : hit->y < where.y)
1145 && !lw_separator_p (val->name, &separator, 0)
1146 && !no_return)
1148 if (val->enabled)
1149 *hit_return = val;
1150 else
1151 no_return = 1;
1154 if (horizontal_p)
1155 where.y = 0;
1156 else
1157 where.x = 0;
1160 if (!just_compute_p)
1161 draw_shadow_rectangle (mw, ws->window, 0, 0, ws->width, ws->height,
1162 False, False);
1165 \f/* Motion code */
1166 static void
1167 set_new_state (mw, val, level)
1168 XlwMenuWidget mw;
1169 widget_value* val;
1170 int level;
1172 int i;
1174 mw->menu.new_depth = 0;
1175 for (i = 0; i < level; i++)
1176 push_new_stack (mw, mw->menu.old_stack [i]);
1177 push_new_stack (mw, val);
1180 static void
1181 make_windows_if_needed (mw, n)
1182 XlwMenuWidget mw;
1183 int n;
1185 int i;
1186 int start_at;
1187 XSetWindowAttributes xswa;
1188 int mask;
1189 Window root = RootWindowOfScreen (DefaultScreenOfDisplay (XtDisplay (mw)));
1190 window_state* windows;
1192 if (mw->menu.windows_length >= n)
1193 return;
1195 xswa.save_under = True;
1196 xswa.override_redirect = True;
1197 xswa.background_pixel = mw->core.background_pixel;
1198 xswa.border_pixel = mw->core.border_pixel;
1199 xswa.event_mask =
1200 ExposureMask | PointerMotionMask | PointerMotionHintMask
1201 | ButtonReleaseMask | ButtonPressMask;
1202 xswa.cursor = mw->menu.cursor_shape;
1203 mask = CWSaveUnder | CWOverrideRedirect | CWBackPixel | CWBorderPixel
1204 | CWEventMask | CWCursor;
1206 if (!mw->menu.windows)
1208 mw->menu.windows =
1209 (window_state*)XtMalloc (n * sizeof (window_state));
1210 start_at = 0;
1212 else
1214 mw->menu.windows =
1215 (window_state*)XtRealloc ((char*)mw->menu.windows,
1216 n * sizeof (window_state));
1217 start_at = mw->menu.windows_length;
1219 mw->menu.windows_length = n;
1221 windows = mw->menu.windows;
1223 for (i = start_at; i < n; i++)
1225 windows [i].x = 0;
1226 windows [i].y = 0;
1227 windows [i].width = 1;
1228 windows [i].height = 1;
1229 windows [i].window =
1230 XCreateWindow (XtDisplay (mw), root, 0, 0, 1, 1,
1231 0, 0, CopyFromParent, CopyFromParent, mask, &xswa);
1235 /* Value is non-zero if WINDOW is part of menu bar widget W. */
1238 xlwmenu_window_p (w, window)
1239 Widget w;
1240 Window window;
1242 XlwMenuWidget mw = (XlwMenuWidget) w;
1243 int i;
1245 for (i = 0; i < mw->menu.windows_length; ++i)
1246 if (window == mw->menu.windows[i].window)
1247 break;
1249 return i < mw->menu.windows_length;
1252 /* Make the window fit in the screen */
1253 static void
1254 fit_to_screen (mw, ws, previous_ws, horizontal_p)
1255 XlwMenuWidget mw;
1256 window_state* ws;
1257 window_state* previous_ws;
1258 Boolean horizontal_p;
1260 unsigned int screen_width = WidthOfScreen (XtScreen (mw));
1261 unsigned int screen_height = HeightOfScreen (XtScreen (mw));
1262 /* 1 if we are unable to avoid an overlap between
1263 this menu and the parent menu in the X dimension. */
1264 int horizontal_overlap = 0;
1266 if (ws->x < 0)
1267 ws->x = 0;
1268 else if (ws->x + ws->width > screen_width)
1270 if (!horizontal_p)
1271 /* The addition of shadow-thickness for a sub-menu's position is
1272 to reflect a similar adjustment when the menu is displayed to
1273 the right of the invoking menu-item; it makes the sub-menu
1274 look more `attached' to the menu-item. */
1275 ws->x = previous_ws->x - ws->width + mw->menu.shadow_thickness;
1276 else
1277 ws->x = screen_width - ws->width;
1278 if (ws->x < 0)
1280 ws->x = 0;
1281 horizontal_overlap = 1;
1284 /* If we overlap in X, try to avoid overlap in Y. */
1285 if (horizontal_overlap
1286 && ws->y < previous_ws->y + previous_ws->height
1287 && previous_ws->y < ws->y + ws->height)
1289 /* Put this menu right below or right above PREVIOUS_WS
1290 if there's room. */
1291 if (previous_ws->y + previous_ws->height + ws->height < screen_height)
1292 ws->y = previous_ws->y + previous_ws->height;
1293 else if (previous_ws->y - ws->height > 0)
1294 ws->y = previous_ws->y - ws->height;
1297 if (ws->y < 0)
1298 ws->y = 0;
1299 else if (ws->y + ws->height > screen_height)
1301 if (horizontal_p)
1302 ws->y = previous_ws->y - ws->height;
1303 else
1304 ws->y = screen_height - ws->height;
1305 if (ws->y < 0)
1306 ws->y = 0;
1310 /* Updates old_stack from new_stack and redisplays. */
1311 static void
1312 remap_menubar (mw)
1313 XlwMenuWidget mw;
1315 int i;
1316 int last_same;
1317 XPoint selection_position;
1318 int old_depth = mw->menu.old_depth;
1319 int new_depth = mw->menu.new_depth;
1320 widget_value** old_stack;
1321 widget_value** new_stack;
1322 window_state* windows;
1323 widget_value* old_selection;
1324 widget_value* new_selection;
1326 /* Check that enough windows and old_stack are ready. */
1327 make_windows_if_needed (mw, new_depth);
1328 make_old_stack_space (mw, new_depth);
1329 windows = mw->menu.windows;
1330 old_stack = mw->menu.old_stack;
1331 new_stack = mw->menu.new_stack;
1333 /* compute the last identical different entry */
1334 for (i = 1; i < old_depth && i < new_depth; i++)
1335 if (old_stack [i] != new_stack [i])
1336 break;
1337 last_same = i - 1;
1339 /* Memorize the previously selected item to be able to refresh it */
1340 old_selection = last_same + 1 < old_depth ? old_stack [last_same + 1] : NULL;
1341 if (old_selection && !old_selection->enabled)
1342 old_selection = NULL;
1343 new_selection = last_same + 1 < new_depth ? new_stack [last_same + 1] : NULL;
1344 if (new_selection && !new_selection->enabled)
1345 new_selection = NULL;
1347 /* Call callback when the hightlighted item changes. */
1348 if (old_selection || new_selection)
1349 XtCallCallbackList ((Widget)mw, mw->menu.highlight,
1350 (XtPointer) new_selection);
1352 /* updates old_state from new_state. It has to be done now because
1353 display_menu (called below) uses the old_stack to know what to display. */
1354 for (i = last_same + 1; i < new_depth; i++)
1355 old_stack [i] = new_stack [i];
1356 mw->menu.old_depth = new_depth;
1358 /* refresh the last selection */
1359 selection_position.x = 0;
1360 selection_position.y = 0;
1361 display_menu (mw, last_same, new_selection == old_selection,
1362 &selection_position, NULL, NULL, old_selection, new_selection);
1364 /* Now place the new menus. */
1365 for (i = last_same + 1; i < new_depth && new_stack[i]->contents; i++)
1367 window_state *previous_ws = &windows[i - 1];
1368 window_state *ws = &windows[i];
1370 ws->x = (previous_ws->x + selection_position.x
1371 + mw->menu.shadow_thickness);
1372 if (mw->menu.horizontal && i == 1)
1373 ws->x += mw->menu.margin;
1375 #if 0
1376 if (!mw->menu.horizontal || i > 1)
1377 ws->x += mw->menu.shadow_thickness;
1378 #endif
1380 ws->y = (previous_ws->y + selection_position.y
1381 + mw->menu.shadow_thickness);
1382 if (mw->menu.horizontal && i == 1)
1383 ws->y += mw->menu.margin;
1385 size_menu (mw, i);
1387 fit_to_screen (mw, ws, previous_ws, mw->menu.horizontal && i == 1);
1389 XClearWindow (XtDisplay (mw), ws->window);
1390 XMoveResizeWindow (XtDisplay (mw), ws->window, ws->x, ws->y,
1391 ws->width, ws->height);
1392 XMapRaised (XtDisplay (mw), ws->window);
1393 display_menu (mw, i, False, &selection_position, NULL, NULL, NULL, NULL);
1396 /* unmap the menus that popped down */
1397 for (i = new_depth - 1; i < old_depth; i++)
1398 if (i >= new_depth || !new_stack[i]->contents)
1399 XUnmapWindow (XtDisplay (mw), windows[i].window);
1402 static Boolean
1403 motion_event_is_in_menu (mw, ev, level, relative_pos)
1404 XlwMenuWidget mw;
1405 XMotionEvent* ev;
1406 int level;
1407 XPoint* relative_pos;
1409 window_state* ws = &mw->menu.windows [level];
1410 int shadow = level == 0 ? 0 : mw->menu.shadow_thickness;
1411 int x = ws->x + shadow;
1412 int y = ws->y + shadow;
1413 relative_pos->x = ev->x_root - x;
1414 relative_pos->y = ev->y_root - y;
1415 return (x - shadow < ev->x_root && ev->x_root < x + ws->width
1416 && y - shadow < ev->y_root && ev->y_root < y + ws->height);
1419 static Boolean
1420 map_event_to_widget_value (mw, ev, val, level)
1421 XlwMenuWidget mw;
1422 XMotionEvent* ev;
1423 widget_value** val;
1424 int* level;
1426 int i;
1427 XPoint relative_pos;
1428 window_state* ws;
1430 *val = NULL;
1432 /* Find the window */
1433 for (i = mw->menu.old_depth - 1; i >= 0; i--)
1435 ws = &mw->menu.windows [i];
1436 if (ws && motion_event_is_in_menu (mw, ev, i, &relative_pos))
1438 display_menu (mw, i, True, NULL, &relative_pos, val, NULL, NULL);
1440 if (*val)
1442 *level = i + 1;
1443 return True;
1447 return False;
1450 \f/* Procedures */
1451 static void
1452 make_drawing_gcs (mw)
1453 XlwMenuWidget mw;
1455 XGCValues xgcv;
1457 xgcv.font = mw->menu.font->fid;
1458 xgcv.foreground = mw->menu.foreground;
1459 xgcv.background = mw->core.background_pixel;
1460 mw->menu.foreground_gc = XtGetGC ((Widget)mw,
1461 GCFont | GCForeground | GCBackground,
1462 &xgcv);
1464 xgcv.font = mw->menu.font->fid;
1465 xgcv.foreground = mw->menu.button_foreground;
1466 xgcv.background = mw->core.background_pixel;
1467 mw->menu.button_gc = XtGetGC ((Widget)mw,
1468 GCFont | GCForeground | GCBackground,
1469 &xgcv);
1471 xgcv.font = mw->menu.font->fid;
1472 xgcv.foreground = mw->menu.foreground;
1473 xgcv.background = mw->core.background_pixel;
1474 xgcv.fill_style = FillStippled;
1475 xgcv.stipple = mw->menu.gray_pixmap;
1476 mw->menu.inactive_gc = XtGetGC ((Widget)mw,
1477 (GCFont | GCForeground | GCBackground
1478 | GCFillStyle | GCStipple), &xgcv);
1480 xgcv.font = mw->menu.font->fid;
1481 xgcv.foreground = mw->menu.button_foreground;
1482 xgcv.background = mw->core.background_pixel;
1483 xgcv.fill_style = FillStippled;
1484 xgcv.stipple = mw->menu.gray_pixmap;
1485 mw->menu.inactive_button_gc = XtGetGC ((Widget)mw,
1486 (GCFont | GCForeground | GCBackground
1487 | GCFillStyle | GCStipple), &xgcv);
1489 xgcv.font = mw->menu.font->fid;
1490 xgcv.foreground = mw->core.background_pixel;
1491 xgcv.background = mw->menu.foreground;
1492 mw->menu.background_gc = XtGetGC ((Widget)mw,
1493 GCFont | GCForeground | GCBackground,
1494 &xgcv);
1497 static void
1498 release_drawing_gcs (mw)
1499 XlwMenuWidget mw;
1501 XtReleaseGC ((Widget) mw, mw->menu.foreground_gc);
1502 XtReleaseGC ((Widget) mw, mw->menu.button_gc);
1503 XtReleaseGC ((Widget) mw, mw->menu.inactive_gc);
1504 XtReleaseGC ((Widget) mw, mw->menu.inactive_button_gc);
1505 XtReleaseGC ((Widget) mw, mw->menu.background_gc);
1506 /* let's get some segvs if we try to use these... */
1507 mw->menu.foreground_gc = (GC) -1;
1508 mw->menu.button_gc = (GC) -1;
1509 mw->menu.inactive_gc = (GC) -1;
1510 mw->menu.inactive_button_gc = (GC) -1;
1511 mw->menu.background_gc = (GC) -1;
1514 #define MINL(x,y) ((((unsigned long) (x)) < ((unsigned long) (y))) \
1515 ? ((unsigned long) (x)) : ((unsigned long) (y)))
1517 static void
1518 make_shadow_gcs (mw)
1519 XlwMenuWidget mw;
1521 XGCValues xgcv;
1522 unsigned long pm = 0;
1523 Display *dpy = XtDisplay ((Widget) mw);
1524 Screen *screen = XtScreen ((Widget) mw);
1525 Colormap cmap = mw->core.colormap;
1526 XColor topc, botc;
1527 int top_frobbed = 0, bottom_frobbed = 0;
1529 mw->menu.free_top_shadow_color_p = 0;
1530 mw->menu.free_bottom_shadow_color_p = 0;
1532 if (mw->menu.top_shadow_color == -1)
1533 mw->menu.top_shadow_color = mw->core.background_pixel;
1534 else
1535 mw->menu.top_shadow_color = mw->menu.top_shadow_color;
1537 if (mw->menu.bottom_shadow_color == -1)
1538 mw->menu.bottom_shadow_color = mw->menu.foreground;
1539 else
1540 mw->menu.bottom_shadow_color = mw->menu.bottom_shadow_color;
1542 if (mw->menu.top_shadow_color == mw->core.background_pixel ||
1543 mw->menu.top_shadow_color == mw->menu.foreground)
1545 topc.pixel = mw->core.background_pixel;
1546 #ifdef emacs
1547 if (x_alloc_lighter_color_for_widget ((Widget) mw, dpy, cmap,
1548 &topc.pixel,
1549 1.2, 0x8000))
1550 #else
1551 XQueryColor (dpy, cmap, &topc);
1552 /* don't overflow/wrap! */
1553 topc.red = MINL (65535, topc.red * 1.2);
1554 topc.green = MINL (65535, topc.green * 1.2);
1555 topc.blue = MINL (65535, topc.blue * 1.2);
1556 if (XAllocColor (dpy, cmap, &topc))
1557 #endif
1559 mw->menu.top_shadow_color = topc.pixel;
1560 mw->menu.free_top_shadow_color_p = 1;
1561 top_frobbed = 1;
1564 if (mw->menu.bottom_shadow_color == mw->menu.foreground ||
1565 mw->menu.bottom_shadow_color == mw->core.background_pixel)
1567 botc.pixel = mw->core.background_pixel;
1568 #ifdef emacs
1569 if (x_alloc_lighter_color_for_widget ((Widget) mw, dpy, cmap,
1570 &botc.pixel,
1571 0.6, 0x4000))
1572 #else
1573 XQueryColor (dpy, cmap, &botc);
1574 botc.red *= 0.6;
1575 botc.green *= 0.6;
1576 botc.blue *= 0.6;
1577 if (XAllocColor (dpy, cmap, &botc))
1578 #endif
1580 mw->menu.bottom_shadow_color = botc.pixel;
1581 mw->menu.free_bottom_shadow_color_p = 1;
1582 bottom_frobbed = 1;
1586 if (top_frobbed && bottom_frobbed)
1588 if (topc.pixel == botc.pixel)
1590 if (botc.pixel == mw->menu.foreground)
1592 if (mw->menu.free_top_shadow_color_p)
1594 x_free_dpy_colors (dpy, screen, cmap,
1595 &mw->menu.top_shadow_color, 1);
1596 mw->menu.free_top_shadow_color_p = 0;
1598 mw->menu.top_shadow_color = mw->core.background_pixel;
1600 else
1602 if (mw->menu.free_bottom_shadow_color_p)
1604 x_free_dpy_colors (dpy, screen, cmap,
1605 &mw->menu.bottom_shadow_color, 1);
1606 mw->menu.free_bottom_shadow_color_p = 0;
1608 mw->menu.bottom_shadow_color = mw->menu.foreground;
1613 if (!mw->menu.top_shadow_pixmap &&
1614 mw->menu.top_shadow_color == mw->core.background_pixel)
1616 mw->menu.top_shadow_pixmap = mw->menu.gray_pixmap;
1617 if (mw->menu.free_top_shadow_color_p)
1619 x_free_dpy_colors (dpy, screen, cmap, &mw->menu.top_shadow_color, 1);
1620 mw->menu.free_top_shadow_color_p = 0;
1622 mw->menu.top_shadow_color = mw->menu.foreground;
1624 if (!mw->menu.bottom_shadow_pixmap &&
1625 mw->menu.bottom_shadow_color == mw->core.background_pixel)
1627 mw->menu.bottom_shadow_pixmap = mw->menu.gray_pixmap;
1628 if (mw->menu.free_bottom_shadow_color_p)
1630 x_free_dpy_colors (dpy, screen, cmap,
1631 &mw->menu.bottom_shadow_color, 1);
1632 mw->menu.free_bottom_shadow_color_p = 0;
1634 mw->menu.bottom_shadow_color = mw->menu.foreground;
1637 xgcv.fill_style = FillStippled;
1638 xgcv.foreground = mw->menu.top_shadow_color;
1639 xgcv.stipple = mw->menu.top_shadow_pixmap;
1640 pm = (xgcv.stipple ? GCStipple|GCFillStyle : 0);
1641 mw->menu.shadow_top_gc = XtGetGC ((Widget)mw, GCForeground | pm, &xgcv);
1643 xgcv.foreground = mw->menu.bottom_shadow_color;
1644 xgcv.stipple = mw->menu.bottom_shadow_pixmap;
1645 pm = (xgcv.stipple ? GCStipple|GCFillStyle : 0);
1646 mw->menu.shadow_bottom_gc = XtGetGC ((Widget)mw, GCForeground | pm, &xgcv);
1650 static void
1651 release_shadow_gcs (mw)
1652 XlwMenuWidget mw;
1654 Display *dpy = XtDisplay ((Widget) mw);
1655 Screen *screen = XtScreen ((Widget) mw);
1656 Colormap cmap = mw->core.colormap;
1657 Pixel px[2];
1658 int i = 0;
1660 if (mw->menu.free_top_shadow_color_p)
1661 px[i++] = mw->menu.top_shadow_color;
1662 if (mw->menu.free_bottom_shadow_color_p)
1663 px[i++] = mw->menu.bottom_shadow_color;
1664 if (i > 0)
1665 x_free_dpy_colors (dpy, screen, cmap, px, i);
1667 XtReleaseGC ((Widget) mw, mw->menu.shadow_top_gc);
1668 XtReleaseGC ((Widget) mw, mw->menu.shadow_bottom_gc);
1671 static void
1672 XlwMenuInitialize (request, mw, args, num_args)
1673 Widget request;
1674 XlwMenuWidget mw;
1675 ArgList args;
1676 Cardinal *num_args;
1678 /* Get the GCs and the widget size */
1679 XSetWindowAttributes xswa;
1680 int mask;
1682 Window window = RootWindowOfScreen (DefaultScreenOfDisplay (XtDisplay (mw)));
1683 Display* display = XtDisplay (mw);
1685 #if 0
1686 widget_value *tem = (widget_value *) XtMalloc (sizeof (widget_value));
1688 /* _XtCreate is freeing the object that was passed to us,
1689 so make a copy that we will actually keep. */
1690 lwlib_bcopy (mw->menu.contents, tem, sizeof (widget_value));
1691 mw->menu.contents = tem;
1692 #endif
1694 /* mw->menu.cursor = XCreateFontCursor (display, mw->menu.cursor_shape); */
1695 mw->menu.cursor = mw->menu.cursor_shape;
1697 mw->menu.gray_pixmap
1698 = XCreatePixmapFromBitmapData (display, window, gray_bitmap_bits,
1699 gray_bitmap_width, gray_bitmap_height,
1700 (unsigned long)1, (unsigned long)0, 1);
1702 /* I don't understand why this ends up 0 sometimes,
1703 but it does. This kludge works around it.
1704 Can anyone find a real fix? -- rms. */
1705 if (mw->menu.font == 0)
1706 mw->menu.font = xlwmenu_default_font;
1708 make_drawing_gcs (mw);
1709 make_shadow_gcs (mw);
1711 xswa.background_pixel = mw->core.background_pixel;
1712 xswa.border_pixel = mw->core.border_pixel;
1713 mask = CWBackPixel | CWBorderPixel;
1715 mw->menu.popped_up = False;
1717 mw->menu.old_depth = 1;
1718 mw->menu.old_stack = (widget_value**)XtMalloc (sizeof (widget_value*));
1719 mw->menu.old_stack_length = 1;
1720 mw->menu.old_stack [0] = mw->menu.contents;
1722 mw->menu.new_depth = 0;
1723 mw->menu.new_stack = 0;
1724 mw->menu.new_stack_length = 0;
1725 push_new_stack (mw, mw->menu.contents);
1727 mw->menu.windows = (window_state*)XtMalloc (sizeof (window_state));
1728 mw->menu.windows_length = 1;
1729 mw->menu.windows [0].x = 0;
1730 mw->menu.windows [0].y = 0;
1731 mw->menu.windows [0].width = 0;
1732 mw->menu.windows [0].height = 0;
1733 size_menu (mw, 0);
1735 mw->core.width = mw->menu.windows [0].width;
1736 mw->core.height = mw->menu.windows [0].height;
1739 static void
1740 XlwMenuClassInitialize ()
1744 static void
1745 XlwMenuRealize (w, valueMask, attributes)
1746 Widget w;
1747 Mask *valueMask;
1748 XSetWindowAttributes *attributes;
1750 XlwMenuWidget mw = (XlwMenuWidget)w;
1751 XSetWindowAttributes xswa;
1752 int mask;
1754 (*xlwMenuWidgetClass->core_class.superclass->core_class.realize)
1755 (w, valueMask, attributes);
1757 xswa.save_under = True;
1758 xswa.cursor = mw->menu.cursor_shape;
1759 mask = CWSaveUnder | CWCursor;
1760 XChangeWindowAttributes (XtDisplay (w), XtWindow (w), mask, &xswa);
1762 mw->menu.windows [0].window = XtWindow (w);
1763 mw->menu.windows [0].x = w->core.x;
1764 mw->menu.windows [0].y = w->core.y;
1765 mw->menu.windows [0].width = w->core.width;
1766 mw->menu.windows [0].height = w->core.height;
1769 /* Only the toplevel menubar/popup is a widget so it's the only one that
1770 receives expose events through Xt. So we repaint all the other panes
1771 when receiving an Expose event. */
1772 static void
1773 XlwMenuRedisplay (w, ev, region)
1774 Widget w;
1775 XEvent* ev;
1776 Region region;
1778 XlwMenuWidget mw = (XlwMenuWidget)w;
1779 int i;
1781 /* If we have a depth beyond 1, it's because a submenu was displayed.
1782 If the submenu has been destroyed, set the depth back to 1. */
1783 if (submenu_destroyed)
1785 mw->menu.old_depth = 1;
1786 submenu_destroyed = 0;
1789 for (i = 0; i < mw->menu.old_depth; i++)
1790 display_menu (mw, i, False, NULL, NULL, NULL, NULL, NULL);
1794 /* Part of a hack to make the menu redisplay when a tooltip frame
1795 over a menu item is unmapped. */
1797 void
1798 xlwmenu_redisplay (w)
1799 Widget w;
1801 XlwMenuRedisplay (w, NULL, None);
1804 static void
1805 XlwMenuDestroy (w)
1806 Widget w;
1808 int i;
1809 XlwMenuWidget mw = (XlwMenuWidget) w;
1811 if (pointer_grabbed)
1812 ungrab_all ((Widget)w, CurrentTime);
1813 pointer_grabbed = 0;
1815 submenu_destroyed = 1;
1817 release_drawing_gcs (mw);
1818 release_shadow_gcs (mw);
1820 /* this doesn't come from the resource db but is created explicitly
1821 so we must free it ourselves. */
1822 XFreePixmap (XtDisplay (mw), mw->menu.gray_pixmap);
1823 mw->menu.gray_pixmap = (Pixmap) -1;
1825 #if 0
1826 /* Do free mw->menu.contents because nowadays we copy it
1827 during initialization. */
1828 XtFree (mw->menu.contents);
1829 #endif
1831 /* Don't free mw->menu.contents because that comes from our creator.
1832 The `*_stack' elements are just pointers into `contents' so leave
1833 that alone too. But free the stacks themselves. */
1834 if (mw->menu.old_stack) XtFree ((char *) mw->menu.old_stack);
1835 if (mw->menu.new_stack) XtFree ((char *) mw->menu.new_stack);
1837 /* Remember, you can't free anything that came from the resource
1838 database. This includes:
1839 mw->menu.cursor
1840 mw->menu.top_shadow_pixmap
1841 mw->menu.bottom_shadow_pixmap
1842 mw->menu.font
1843 Also the color cells of top_shadow_color, bottom_shadow_color,
1844 foreground, and button_foreground will never be freed until this
1845 client exits. Nice, eh?
1848 /* start from 1 because the one in slot 0 is w->core.window */
1849 for (i = 1; i < mw->menu.windows_length; i++)
1850 XDestroyWindow (XtDisplay (mw), mw->menu.windows [i].window);
1851 if (mw->menu.windows)
1852 XtFree ((char *) mw->menu.windows);
1855 static Boolean
1856 XlwMenuSetValues (current, request, new)
1857 Widget current;
1858 Widget request;
1859 Widget new;
1861 XlwMenuWidget oldmw = (XlwMenuWidget)current;
1862 XlwMenuWidget newmw = (XlwMenuWidget)new;
1863 Boolean redisplay = False;
1864 int i;
1866 if (newmw->menu.contents
1867 && newmw->menu.contents->contents
1868 && newmw->menu.contents->contents->change >= VISIBLE_CHANGE)
1869 redisplay = True;
1870 /* Do redisplay if the contents are entirely eliminated. */
1871 if (newmw->menu.contents
1872 && newmw->menu.contents->contents == 0
1873 && newmw->menu.contents->change >= VISIBLE_CHANGE)
1874 redisplay = True;
1876 if (newmw->core.background_pixel != oldmw->core.background_pixel
1877 || newmw->menu.foreground != oldmw->menu.foreground
1878 || newmw->menu.font != oldmw->menu.font)
1880 release_drawing_gcs (newmw);
1881 make_drawing_gcs (newmw);
1883 release_shadow_gcs (newmw);
1884 /* Cause the shadow colors to be recalculated. */
1885 newmw->menu.top_shadow_color = -1;
1886 newmw->menu.bottom_shadow_color = -1;
1887 make_shadow_gcs (newmw);
1889 redisplay = True;
1891 if (XtIsRealized (current))
1892 /* If the menu is currently displayed, change the display. */
1893 for (i = 0; i < oldmw->menu.windows_length; i++)
1895 XSetWindowBackground (XtDisplay (oldmw),
1896 oldmw->menu.windows [i].window,
1897 newmw->core.background_pixel);
1898 /* clear windows and generate expose events */
1899 XClearArea (XtDisplay (oldmw), oldmw->menu.windows[i].window,
1900 0, 0, 0, 0, True);
1904 return redisplay;
1907 static void
1908 XlwMenuResize (w)
1909 Widget w;
1911 XlwMenuWidget mw = (XlwMenuWidget)w;
1913 if (mw->menu.popped_up)
1915 /* Don't allow the popup menu to resize itself. */
1916 mw->core.width = mw->menu.windows [0].width;
1917 mw->core.height = mw->menu.windows [0].height;
1918 mw->core.parent->core.width = mw->core.width ;
1919 mw->core.parent->core.height = mw->core.height ;
1921 else
1923 mw->menu.windows [0].width = mw->core.width;
1924 mw->menu.windows [0].height = mw->core.height;
1928 \f/* Action procedures */
1929 static void
1930 handle_single_motion_event (mw, ev)
1931 XlwMenuWidget mw;
1932 XMotionEvent* ev;
1934 widget_value* val;
1935 int level;
1937 if (!map_event_to_widget_value (mw, ev, &val, &level))
1938 pop_new_stack_if_no_contents (mw);
1939 else
1940 set_new_state (mw, val, level);
1941 remap_menubar (mw);
1943 /* Sync with the display. Makes it feel better on X terms. */
1944 XSync (XtDisplay (mw), False);
1947 static void
1948 handle_motion_event (mw, ev)
1949 XlwMenuWidget mw;
1950 XMotionEvent* ev;
1952 int x = ev->x_root;
1953 int y = ev->y_root;
1954 int state = ev->state;
1956 handle_single_motion_event (mw, ev);
1958 /* allow motion events to be generated again */
1959 if (ev->is_hint
1960 && XQueryPointer (XtDisplay (mw), ev->window,
1961 &ev->root, &ev->subwindow,
1962 &ev->x_root, &ev->y_root,
1963 &ev->x, &ev->y,
1964 &ev->state)
1965 && ev->state == state
1966 && (ev->x_root != x || ev->y_root != y))
1967 handle_single_motion_event (mw, ev);
1970 static void
1971 Start (w, ev, params, num_params)
1972 Widget w;
1973 XEvent *ev;
1974 String *params;
1975 Cardinal *num_params;
1977 XlwMenuWidget mw = (XlwMenuWidget)w;
1979 if (!mw->menu.popped_up)
1981 menu_post_event = *ev;
1982 pop_up_menu (mw, (XButtonPressedEvent*) ev);
1984 else
1986 /* If we push a button while the menu is posted semipermanently,
1987 releasing the button should always pop the menu down. */
1988 next_release_must_exit = 1;
1990 /* notes the absolute position of the menubar window */
1991 mw->menu.windows [0].x = ev->xmotion.x_root - ev->xmotion.x;
1992 mw->menu.windows [0].y = ev->xmotion.y_root - ev->xmotion.y;
1994 /* handles the down like a move, slots are compatible */
1995 handle_motion_event (mw, &ev->xmotion);
1999 static void
2000 Drag (w, ev, params, num_params)
2001 Widget w;
2002 XEvent *ev;
2003 String *params;
2004 Cardinal *num_params;
2006 XlwMenuWidget mw = (XlwMenuWidget)w;
2007 if (mw->menu.popped_up)
2008 handle_motion_event (mw, &ev->xmotion);
2011 /* Do nothing.
2012 This is how we handle presses and releases of modifier keys. */
2013 static void
2014 Nothing (w, ev, params, num_params)
2015 Widget w;
2016 XEvent *ev;
2017 String *params;
2018 Cardinal *num_params;
2022 widget_value *
2023 find_first_selectable (mw, item)
2024 XlwMenuWidget mw;
2025 widget_value *item;
2027 widget_value *current = item;
2028 enum menu_separator separator;
2030 while (lw_separator_p (current->name, &separator, 0) || !current->enabled)
2031 if (current->next)
2032 current=current->next;
2033 else
2034 return NULL;
2036 return current;
2039 widget_value *
2040 find_next_selectable (mw, item)
2041 XlwMenuWidget mw;
2042 widget_value *item;
2044 widget_value *current = item;
2045 enum menu_separator separator;
2047 while (current->next && (current=current->next) &&
2048 (lw_separator_p (current->name, &separator, 0) || !current->enabled))
2051 if (current == item)
2053 current = mw->menu.old_stack [mw->menu.old_depth - 2]->contents;
2055 while (lw_separator_p (current->name, &separator, 0) || !current->enabled)
2057 if (current->next)
2058 current=current->next;
2060 if (current == item)
2061 break;
2066 return current;
2069 widget_value *
2070 find_prev_selectable (mw, item)
2071 XlwMenuWidget mw;
2072 widget_value *item;
2074 widget_value *current = item;
2075 widget_value *prev = item;
2077 while ((current=find_next_selectable (mw, current)) != item)
2079 if (prev == current)
2080 break;
2081 prev=current;
2084 return prev;
2087 static void
2088 Down (w, ev, params, num_params)
2089 Widget w;
2090 XEvent *ev;
2091 String *params;
2092 Cardinal *num_params;
2094 XlwMenuWidget mw = (XlwMenuWidget) w;
2095 widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2097 /* Inside top-level menu-bar? */
2098 if (mw->menu.old_depth == 2)
2099 /* When <down> in the menu-bar is pressed, display the corresponding
2100 sub-menu and select the first selectable menu item there. */
2101 set_new_state (mw, find_first_selectable (mw, selected_item->contents), mw->menu.old_depth);
2102 else
2103 /* Highlight next possible (enabled and not separator) menu item. */
2104 set_new_state (mw, find_next_selectable (mw, selected_item), mw->menu.old_depth - 1);
2106 remap_menubar (mw);
2109 static void
2110 Up (w, ev, params, num_params)
2111 Widget w;
2112 XEvent *ev;
2113 String *params;
2114 Cardinal *num_params;
2116 XlwMenuWidget mw = (XlwMenuWidget) w;
2117 widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2119 /* Inside top-level menu-bar? */
2120 if (mw->menu.old_depth == 2)
2122 /* FIXME: this is tricky. <up> in the menu-bar should select the
2123 last selectable item in the list. So we select the first
2124 selectable one and find the previous selectable item. Is there
2125 a better way? */
2126 set_new_state (mw, find_first_selectable (mw, selected_item->contents), mw->menu.old_depth);
2127 remap_menubar (mw);
2128 selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2129 set_new_state (mw, find_prev_selectable (mw, selected_item), mw->menu.old_depth - 1);
2131 else
2132 /* Highlight previous (enabled and not separator) menu item. */
2133 set_new_state (mw, find_prev_selectable (mw, selected_item), mw->menu.old_depth - 1);
2135 remap_menubar (mw);
2138 static void
2139 Left (w, ev, params, num_params)
2140 Widget w;
2141 XEvent *ev;
2142 String *params;
2143 Cardinal *num_params;
2145 XlwMenuWidget mw = (XlwMenuWidget) w;
2146 widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2148 /* Inside top-level menu-bar? */
2149 if (mw->menu.old_depth == 2)
2150 /* When <left> in the menu-bar is pressed, display the previous item on
2151 the menu-bar. If the current item is the first one, highlight the
2152 last item in the menubar (probably Help). */
2153 set_new_state (mw, find_prev_selectable (mw, selected_item), mw->menu.old_depth - 1);
2154 else
2156 pop_new_stack_if_no_contents (mw);
2157 set_new_state (mw, mw->menu.old_stack [mw->menu.old_depth - 2], mw->menu.old_depth - 2);
2160 remap_menubar (mw);
2163 static void
2164 Right (w, ev, params, num_params)
2165 Widget w;
2166 XEvent *ev;
2167 String *params;
2168 Cardinal *num_params;
2170 XlwMenuWidget mw = (XlwMenuWidget) w;
2171 widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2173 /* Inside top-level menu-bar? */
2174 if (mw->menu.old_depth == 2)
2175 /* When <right> in the menu-bar is pressed, display the next item on
2176 the menu-bar. If the current item is the last one, highlight the
2177 first item (probably File). */
2178 set_new_state (mw, find_next_selectable (mw, selected_item), mw->menu.old_depth - 1);
2179 else if (selected_item->contents) /* Is this menu item expandable? */
2181 set_new_state (mw, selected_item->contents, mw->menu.old_depth);
2182 remap_menubar (mw);
2183 selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2184 if (!selected_item->enabled && find_first_selectable (mw, selected_item))
2185 set_new_state (mw, find_first_selectable (mw, selected_item), mw->menu.old_depth - 1);
2187 else
2189 pop_new_stack_if_no_contents (mw);
2190 set_new_state (mw, mw->menu.old_stack [mw->menu.old_depth - 2], mw->menu.old_depth - 2);
2193 remap_menubar (mw);
2196 /* Handle key press and release events while menu is popped up.
2197 Our action is to get rid of the menu. */
2198 static void
2199 Key (w, ev, params, num_params)
2200 Widget w;
2201 XEvent *ev;
2202 String *params;
2203 Cardinal *num_params;
2205 XlwMenuWidget mw = (XlwMenuWidget)w;
2207 /* Pop down everything. */
2208 mw->menu.new_depth = 1;
2209 remap_menubar (mw);
2211 if (mw->menu.popped_up)
2213 mw->menu.popped_up = False;
2214 ungrab_all ((Widget)mw, ev->xmotion.time);
2215 if (XtIsShell (XtParent ((Widget) mw)))
2216 XtPopdown (XtParent ((Widget) mw));
2217 else
2219 XtRemoveGrab ((Widget) mw);
2220 display_menu (mw, 0, False, NULL, NULL, NULL, NULL, NULL);
2224 /* callback */
2225 XtCallCallbackList ((Widget)mw, mw->menu.select, (XtPointer)0);
2228 static void
2229 Select (w, ev, params, num_params)
2230 Widget w;
2231 XEvent *ev;
2232 String *params;
2233 Cardinal *num_params;
2235 XlwMenuWidget mw = (XlwMenuWidget)w;
2236 widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2238 /* If user releases the button quickly, without selecting anything,
2239 after the initial down-click that brought the menu up,
2240 do nothing. */
2241 if ((selected_item == 0
2242 || ((widget_value *) selected_item)->call_data == 0)
2243 && !next_release_must_exit
2244 && (ev->xbutton.time - menu_post_event.xbutton.time
2245 < XtGetMultiClickTime (XtDisplay (w))))
2246 return;
2248 /* pop down everything. */
2249 mw->menu.new_depth = 1;
2250 remap_menubar (mw);
2252 if (mw->menu.popped_up)
2254 mw->menu.popped_up = False;
2255 ungrab_all ((Widget)mw, ev->xmotion.time);
2256 if (XtIsShell (XtParent ((Widget) mw)))
2257 XtPopdown (XtParent ((Widget) mw));
2258 else
2260 XtRemoveGrab ((Widget) mw);
2261 display_menu (mw, 0, False, NULL, NULL, NULL, NULL, NULL);
2265 /* callback */
2266 XtCallCallbackList ((Widget)mw, mw->menu.select, (XtPointer)selected_item);
2270 \f/* Special code to pop-up a menu */
2271 void
2272 pop_up_menu (mw, event)
2273 XlwMenuWidget mw;
2274 XButtonPressedEvent* event;
2276 int x = event->x_root;
2277 int y = event->y_root;
2278 int w;
2279 int h;
2280 int borderwidth = mw->menu.shadow_thickness;
2281 Screen* screen = XtScreen (mw);
2282 Display *display = XtDisplay (mw);
2283 int count;
2285 next_release_must_exit = 0;
2287 XtCallCallbackList ((Widget)mw, mw->menu.open, NULL);
2289 if (XtIsShell (XtParent ((Widget)mw)))
2290 size_menu (mw, 0);
2292 w = mw->menu.windows [0].width;
2293 h = mw->menu.windows [0].height;
2295 x -= borderwidth;
2296 y -= borderwidth;
2297 if (x < borderwidth)
2298 x = borderwidth;
2299 if (x + w + 2 * borderwidth > WidthOfScreen (screen))
2300 x = WidthOfScreen (screen) - w - 2 * borderwidth;
2301 if (y < borderwidth)
2302 y = borderwidth;
2303 if (y + h + 2 * borderwidth> HeightOfScreen (screen))
2304 y = HeightOfScreen (screen) - h - 2 * borderwidth;
2306 mw->menu.popped_up = True;
2307 if (XtIsShell (XtParent ((Widget)mw)))
2309 XtConfigureWidget (XtParent ((Widget)mw), x, y, w, h,
2310 XtParent ((Widget)mw)->core.border_width);
2311 XtPopup (XtParent ((Widget)mw), XtGrabExclusive);
2312 display_menu (mw, 0, False, NULL, NULL, NULL, NULL, NULL);
2313 mw->menu.windows [0].x = x + borderwidth;
2314 mw->menu.windows [0].y = y + borderwidth;
2316 else
2318 XEvent *ev = (XEvent *) event;
2320 XtAddGrab ((Widget) mw, True, True);
2322 /* notes the absolute position of the menubar window */
2323 mw->menu.windows [0].x = ev->xmotion.x_root - ev->xmotion.x;
2324 mw->menu.windows [0].y = ev->xmotion.y_root - ev->xmotion.y;
2327 #ifdef emacs
2328 count = x_catch_errors (display);
2329 #endif
2330 if (XtGrabPointer ((Widget)mw, False,
2331 (PointerMotionMask
2332 | PointerMotionHintMask
2333 | ButtonReleaseMask
2334 | ButtonPressMask),
2335 GrabModeAsync, GrabModeAsync, None,
2336 mw->menu.cursor_shape,
2337 event->time) == Success)
2339 if (! GRAB_KEYBOARD
2340 || XtGrabKeyboard ((Widget)mw, False, GrabModeAsync,
2341 GrabModeAsync, event->time) == Success)
2343 XtSetKeyboardFocus((Widget)mw, None);
2344 pointer_grabbed = 1;
2346 else
2347 XtUngrabPointer ((Widget)mw, event->time);
2350 #ifdef emacs
2351 if (x_had_errors_p (display))
2353 pointer_grabbed = 0;
2354 XtUngrabPointer ((Widget)mw, event->time);
2356 x_uncatch_errors (display, count);
2357 #endif
2359 handle_motion_event (mw, (XMotionEvent*)event);