Don't redraw lucid menus more than needed. Use double buffer to reduce flicker.
[bpt/emacs.git] / lwlib / xlwmenu.c
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.
5
6 This file is part of the Lucid Widget Library.
7
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.
12
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.
17
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. */
22
23 /* Created by devin@lucid.com */
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include <setjmp.h>
30 #include "lisp.h"
31
32 #include <stdio.h>
33 #include <ctype.h>
34
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"
49
50 #ifdef emacs
51
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. */
55
56 extern int gray_bitmap_width;
57 extern int gray_bitmap_height;
58 extern char *gray_bitmap_bits;
59
60 #include "xterm.h"
61
62 #else /* not emacs */
63
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
68
69 #endif /* not emacs */
70
71 static int pointer_grabbed;
72 static XEvent menu_post_event;
73
74 static XFontStruct *xlwmenu_default_font;
75
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\
116 ";
117
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. */
120
121 /* FIXME: Should ESC close one level of menu structure or the complete menu? */
122
123 /* FIXME: F10 should enter the menu, the first one in the menu-bar. */
124
125 #define offset(field) XtOffset(XlwMenuWidget, field)
126 static XtResource
127 xlwMenuResources[] =
128 {
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},
156
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},
168
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},
185 };
186 #undef offset
187
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 *));
206
207
208 static XtActionsRec
209 xlwMenuActionsList [] =
210 {
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},
221 };
222
223 #define SuperClass ((CoreWidgetClass)&coreClassRec)
224
225 XlwMenuClassRec xlwMenuClassRec =
226 {
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 */
261 {
262 0 /* dummy */
263 },
264 };
265
266 WidgetClass xlwMenuWidgetClass = (WidgetClass) &xlwMenuClassRec;
267
268 int submenu_destroyed;
269
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))
274
275 static int next_release_must_exit;
276
277 \f/* Utilities */
278
279 /* Ungrab pointer and keyboard */
280 static void
281 ungrab_all (w, ungrabtime)
282 Widget w;
283 Time ungrabtime;
284 {
285 XtUngrabPointer (w, ungrabtime);
286 if (GRAB_KEYBOARD) XtUngrabKeyboard (w, ungrabtime);
287 }
288
289 /* Like abort, but remove grabs from widget W before. */
290
291 static void
292 abort_gracefully (w)
293 Widget w;
294 {
295 if (XtIsShell (XtParent (w)))
296 XtRemoveGrab (w);
297 ungrab_all (w, CurrentTime);
298 abort ();
299 }
300
301 static void
302 push_new_stack (mw, val)
303 XlwMenuWidget mw;
304 widget_value* val;
305 {
306 if (!mw->menu.new_stack)
307 {
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*));
312 }
313 else if (mw->menu.new_depth == mw->menu.new_stack_length)
314 {
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*));
319 }
320 mw->menu.new_stack [mw->menu.new_depth++] = val;
321 }
322
323 static void
324 pop_new_stack_if_no_contents (mw)
325 XlwMenuWidget mw;
326 {
327 if (mw->menu.new_depth > 1)
328 {
329 if (!mw->menu.new_stack [mw->menu.new_depth - 1]->contents)
330 mw->menu.new_depth -= 1;
331 }
332 }
333
334 static void
335 make_old_stack_space (mw, n)
336 XlwMenuWidget mw;
337 int n;
338 {
339 if (!mw->menu.old_stack)
340 {
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*));
345 }
346 else if (mw->menu.old_stack_length < n)
347 {
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*));
352 }
353 }
354
355 \f/* Size code */
356 static int
357 string_width (mw, s)
358 XlwMenuWidget mw;
359 char *s;
360 {
361 XCharStruct xcs;
362 int drop;
363 #ifdef HAVE_XFT
364 if (mw->menu.xft_font)
365 {
366 XGlyphInfo gi;
367 XftTextExtentsUtf8 (XtDisplay (mw), mw->menu.xft_font,
368 (FcChar8 *) s,
369 strlen (s), &gi);
370 return gi.width;
371 }
372 #endif
373 #ifdef HAVE_X_I18N
374 if (mw->menu.fontSet)
375 {
376 XRectangle ink, logical;
377 XmbTextExtents (mw->menu.fontSet, s, strlen (s), &ink, &logical);
378 return logical.width;
379 }
380 #endif
381
382 XTextExtents (mw->menu.font, s, strlen (s), &drop, &drop, &drop, &xcs);
383 return xcs.width;
384
385 }
386
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
416
417 static int
418 arrow_width (mw)
419 XlwMenuWidget mw;
420 {
421 return (MENU_FONT_ASCENT (mw) * 3/4) | 1;
422 }
423
424 /* Return the width of toggle buttons of widget MW. */
425
426 static int
427 toggle_button_width (mw)
428 XlwMenuWidget mw;
429 {
430 return (MENU_FONT_HEIGHT (mw) * 2 / 3) | 1;
431 }
432
433
434 /* Return the width of radio buttons of widget MW. */
435
436 static int
437 radio_button_width (mw)
438 XlwMenuWidget mw;
439 {
440 return toggle_button_width (mw) * 1.41;
441 }
442
443
444 static XtResource
445 nameResource[] =
446 {
447 {"labelString", "LabelString", XtRString, sizeof(String),
448 0, XtRImmediate, 0},
449 };
450
451 static char*
452 resource_widget_value (mw, val)
453 XlwMenuWidget mw;
454 widget_value *val;
455 {
456 if (!val->toolkit_data)
457 {
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)
467 {
468 complete_name = (char *) XtMalloc (strlen (resourced_name) + 1);
469 strcpy (complete_name, resourced_name);
470 }
471 else
472 {
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);
480 }
481
482 val->toolkit_data = complete_name;
483 val->free_toolkit_data = True;
484 }
485 return (char*)val->toolkit_data;
486 }
487
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;
499 {
500 enum menu_separator separator;
501
502 if (lw_separator_p (val->name, &separator, 0))
503 {
504 *height = separator_height (separator);
505 *label_width = 1;
506 *rest_width = 0;
507 *button_width = 0;
508 }
509 else
510 {
511 *height = MENU_FONT_HEIGHT (mw)
512 + 2 * mw->menu.vertical_spacing + 2 * mw->menu.shadow_thickness;
513
514 *label_width =
515 string_width (mw, resource_widget_value (mw, val))
516 + mw->menu.horizontal_spacing + mw->menu.shadow_thickness;
517
518 *rest_width = mw->menu.horizontal_spacing + mw->menu.shadow_thickness;
519 if (!horizontal_p)
520 {
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);
528
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);
535 }
536 }
537 }
538
539 static void
540 size_menu (mw, level)
541 XlwMenuWidget mw;
542 int level;
543 {
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;
553
554 if (level >= mw->menu.old_depth)
555 abort_gracefully ((Widget) mw);
556
557 ws = &mw->menu.windows [level];
558 ws->width = 0;
559 ws->height = 0;
560 ws->label_width = 0;
561 ws->button_width = 0;
562
563 for (val = mw->menu.old_stack [level]->contents; val; val = val->next)
564 {
565 size_menu_item (mw, val, horizontal_p, &label_width, &rest_width,
566 &button_width, &height);
567 if (horizontal_p)
568 {
569 ws->width += label_width + rest_width;
570 if (height > ws->height)
571 ws->height = height;
572 }
573 else
574 {
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;
582 }
583 }
584
585 if (horizontal_p)
586 ws->label_width = ws->button_width = 0;
587 else
588 {
589 ws->width = ws->label_width + max_rest_width + max_button_width;
590 ws->button_width = max_button_width;
591 }
592
593 ws->width += 2 * mw->menu.shadow_thickness;
594 ws->height += 2 * mw->menu.shadow_thickness;
595 ws->max_rest_width = max_rest_width;
596
597 if (horizontal_p)
598 {
599 ws->width += 2 * mw->menu.margin;
600 ws->height += 2 * mw->menu.margin;
601 }
602 }
603
604
605 \f/* Display code */
606
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;
616 {
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;
627
628 y += (MENU_FONT_HEIGHT (mw) - height) / 2;
629
630 if (down_p)
631 {
632 GC temp;
633 temp = top_gc;
634 top_gc = bottom_gc;
635 bottom_gc = temp;
636 }
637
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);
647
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);
657
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);
667 }
668
669
670
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;
681 {
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];
687
688 if (!erase_p && down_p)
689 {
690 GC temp;
691 temp = top_gc;
692 top_gc = bottom_gc;
693 bottom_gc = temp;
694 }
695
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);
732 }
733
734
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;
745 {
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];
751
752 if (!erase_p && down_p)
753 {
754 GC temp;
755 temp = top_gc;
756 top_gc = bottom_gc;
757 bottom_gc = temp;
758 }
759
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);
796 }
797
798
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. */
802
803 static void
804 draw_toggle (mw, window, x, y, selected_p)
805 XlwMenuWidget mw;
806 Window window;
807 int x, y, selected_p;
808 {
809 int width, height;
810
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);
816 }
817
818
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. */
822
823 static void
824 draw_radio (mw, window, x, y, selected_p)
825 XlwMenuWidget mw;
826 Window window;
827 int x, y, selected_p;
828 {
829 int width, height;
830
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);
836 }
837
838
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. */
842
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;
849 {
850 Display *dpy = XtDisplay (mw);
851 XGCValues xgcv;
852
853 switch (type)
854 {
855 case SEPARATOR_NO_LINE:
856 break;
857
858 case SEPARATOR_SINGLE_LINE:
859 XDrawLine (dpy, window, mw->menu.foreground_gc,
860 x, y, x + width, y);
861 break;
862
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;
867
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;
876
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;
883
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;
890
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;
897
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;
907
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;
917
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;
922
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;
929
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;
940
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;
951
952 default:
953 abort ();
954 }
955 }
956
957
958 /* Return the pixel height of menu separator SEPARATOR. */
959
960 static int
961 separator_height (separator)
962 enum menu_separator separator;
963 {
964 switch (separator)
965 {
966 case SEPARATOR_NO_LINE:
967 return 2;
968
969 case SEPARATOR_SINGLE_LINE:
970 case SEPARATOR_SINGLE_DASHED_LINE:
971 return 1;
972
973 case SEPARATOR_DOUBLE_LINE:
974 case SEPARATOR_DOUBLE_DASHED_LINE:
975 return 3;
976
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;
982
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;
988
989 default:
990 abort ();
991 }
992 }
993
994
995 /* Display the menu item and increment where.x and where.y to show how large
996 the menu item was. */
997
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;
1008 {
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
1027
1028 /* compute the sizes of the item */
1029 size_menu_item (mw, val, horizontal_p, &label_width, &rest_width,
1030 &button_width, &height);
1031
1032 if (horizontal_p)
1033 width = label_width + rest_width;
1034 else
1035 {
1036 label_width = ws->label_width;
1037 width = ws->width - 2 * shadow;
1038 }
1039
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;
1044
1045 /* do the drawing. */
1046 if (!just_compute_p)
1047 {
1048 /* Add the shadow border of the containing menu */
1049 int x = where->x + shadow;
1050 int y = where->y + shadow;
1051
1052 if (horizontal_p)
1053 {
1054 x += margin;
1055 y += margin;
1056 }
1057
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
1067
1068 if (separator_p)
1069 {
1070 draw_separator (mw, ws->pixmap, x, y, width, separator);
1071 }
1072 else
1073 {
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);
1078
1079 /* Deal with centering a menu title. */
1080 if (!horizontal_p && !val->contents && !val->call_data)
1081 {
1082 int l = string_width (mw, display_string);
1083
1084 if (width > l)
1085 x_offset = (width - l) >> 1;
1086 }
1087 else if (!horizontal_p && ws->button_width)
1088 x_offset += ws->button_width;
1089
1090
1091 #ifdef HAVE_XFT
1092 if (ws->xft_draw)
1093 {
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));
1100 }
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));
1115
1116 if (!horizontal_p)
1117 {
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);
1124
1125 if (val->contents)
1126 {
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);
1134 }
1135 else if (val->key)
1136 {
1137 #ifdef HAVE_XFT
1138 if (ws->xft_draw)
1139 {
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));
1149 }
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));
1167 }
1168 }
1169 else
1170 {
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);
1178 }
1179
1180 if (highlighted_p)
1181 draw_shadow_rectangle (mw, ws->pixmap, x, y, width, height, False,
1182 False);
1183 }
1184 }
1185
1186 where->x += width;
1187 where->y += height;
1188 }
1189
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;
1198 {
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;
1207
1208 if (level >= mw->menu.old_depth)
1209 abort_gracefully ((Widget) mw);
1210
1211 if (level < mw->menu.old_depth - 1)
1212 following_item = mw->menu.old_stack [level + 1];
1213 else
1214 following_item = NULL;
1215
1216 if (hit)
1217 *hit_return = NULL;
1218
1219 where.x = 0;
1220 where.y = 0;
1221
1222 ws = &mw->menu.windows [level];
1223
1224 if (!just_compute_p)
1225 XFillRectangle (XtDisplay (mw), ws->pixmap, mw->menu.background_gc,
1226 0, 0, ws->width, ws->height);
1227
1228 for (val = mw->menu.old_stack [level]->contents; val; val = val->next)
1229 {
1230 highlighted_p = val == following_item;
1231 if (highlighted_p && highlighted_pos)
1232 {
1233 if (horizontal_p)
1234 highlighted_pos->x = where.x;
1235 else
1236 highlighted_pos->y = where.y;
1237 }
1238
1239 display_menu_item (mw, val, ws, &where, highlighted_p, horizontal_p,
1240 just_compute_p);
1241
1242 if (highlighted_p && highlighted_pos)
1243 {
1244 if (horizontal_p)
1245 highlighted_pos->y = where.y;
1246 else
1247 highlighted_pos->x = where.x;
1248 }
1249
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)
1255 {
1256 if (val->enabled)
1257 *hit_return = val;
1258 else
1259 no_return = 1;
1260 if (mw->menu.inside_entry != val)
1261 {
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);
1268 }
1269 }
1270
1271 if (horizontal_p)
1272 where.y = 0;
1273 else
1274 where.x = 0;
1275 }
1276
1277 if (!just_compute_p)
1278 {
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);
1283 }
1284 }
1285
1286 \f/* Motion code */
1287 static void
1288 set_new_state (mw, val, level)
1289 XlwMenuWidget mw;
1290 widget_value* val;
1291 int level;
1292 {
1293 int i;
1294
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);
1299 }
1300
1301 static void
1302 expose_cb (Widget widget,
1303 XtPointer closure,
1304 XEvent* event,
1305 Boolean* continue_to_dispatch)
1306 {
1307 *continue_to_dispatch = False;
1308 XlwMenuWidget mw = (XlwMenuWidget) closure;
1309 int i;
1310
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);
1315 }
1316
1317 static void
1318 make_windows_if_needed (mw, n)
1319 XlwMenuWidget mw;
1320 int n;
1321 {
1322 int i;
1323 int start_at;
1324 window_state* windows;
1325 #ifdef HAVE_XFT
1326 int screen = XScreenNumberOfScreen (mw->core.screen);
1327 #endif
1328
1329 if (mw->menu.windows_length >= n)
1330 return;
1331
1332 if (!mw->menu.windows)
1333 {
1334 mw->menu.windows =
1335 (window_state*)XtMalloc (n * sizeof (window_state));
1336 start_at = 0;
1337 }
1338 else
1339 {
1340 mw->menu.windows =
1341 (window_state*)XtRealloc ((char*)mw->menu.windows,
1342 n * sizeof (window_state));
1343 start_at = mw->menu.windows_length;
1344 }
1345 mw->menu.windows_length = n;
1346
1347 windows = mw->menu.windows;
1348
1349 for (i = start_at; i < n; i++)
1350 {
1351 Arg av[10];
1352 int ac = 0;
1353 windows [i].x = 0;
1354 windows [i].y = 0;
1355 windows [i].width = 1;
1356 windows [i].height = 1;
1357 windows [i].max_rest_width = 0;
1358 XtSetArg (av[ac], XtNwidth, 1); ++ac;
1359 XtSetArg (av[ac], XtNheight, 1); ++ac;
1360 XtSetArg (av[ac], XtNsaveUnder, True); ++ac;
1361 XtSetArg (av[ac], XtNbackground, mw->core.background_pixel); ++ac;
1362 XtSetArg (av[ac], XtNborderColor, mw->core.border_pixel); ++ac;
1363 XtSetArg (av[ac], XtNborderWidth, mw->core.border_width); ++ac;
1364 XtSetArg (av[ac], XtNcursor, mw->menu.cursor_shape); ++ac;
1365 windows [i].w =
1366 XtCreatePopupShell ("sub", overrideShellWidgetClass,
1367 (Widget) mw, av, ac);
1368 XtRealizeWidget (windows [i].w);
1369 XtAddEventHandler (windows [i].w, ExposureMask, False, expose_cb, mw);
1370 windows [i].window = XtWindow (windows [i].w);
1371 windows [i].pixmap = None;
1372 #ifdef HAVE_XFT
1373 windows [i].xft_draw = 0;
1374 #endif
1375 }
1376 }
1377
1378 /* Value is non-zero if WINDOW is part of menu bar widget W. */
1379
1380 int
1381 xlwmenu_window_p (w, window)
1382 Widget w;
1383 Window window;
1384 {
1385 XlwMenuWidget mw = (XlwMenuWidget) w;
1386 int i;
1387
1388 for (i = 0; i < mw->menu.windows_length; ++i)
1389 if (window == mw->menu.windows[i].window)
1390 break;
1391
1392 return i < mw->menu.windows_length;
1393 }
1394
1395 /* Make the window fit in the screen */
1396 static void
1397 fit_to_screen (mw, ws, previous_ws, horizontal_p)
1398 XlwMenuWidget mw;
1399 window_state* ws;
1400 window_state* previous_ws;
1401 Boolean horizontal_p;
1402 {
1403 unsigned int screen_width = WidthOfScreen (XtScreen (mw));
1404 unsigned int screen_height = HeightOfScreen (XtScreen (mw));
1405 /* 1 if we are unable to avoid an overlap between
1406 this menu and the parent menu in the X dimension. */
1407 int horizontal_overlap = 0;
1408
1409 if (ws->x < 0)
1410 ws->x = 0;
1411 else if (ws->x + ws->width > screen_width)
1412 {
1413 if (!horizontal_p)
1414 /* The addition of shadow-thickness for a sub-menu's position is
1415 to reflect a similar adjustment when the menu is displayed to
1416 the right of the invoking menu-item; it makes the sub-menu
1417 look more `attached' to the menu-item. */
1418 ws->x = previous_ws->x - ws->width + mw->menu.shadow_thickness;
1419 else
1420 ws->x = screen_width - ws->width;
1421 if (ws->x < 0)
1422 {
1423 ws->x = 0;
1424 horizontal_overlap = 1;
1425 }
1426 }
1427 /* If we overlap in X, try to avoid overlap in Y. */
1428 if (horizontal_overlap
1429 && ws->y < previous_ws->y + previous_ws->height
1430 && previous_ws->y < ws->y + ws->height)
1431 {
1432 /* Put this menu right below or right above PREVIOUS_WS
1433 if there's room. */
1434 if (previous_ws->y + previous_ws->height + ws->height < screen_height)
1435 ws->y = previous_ws->y + previous_ws->height;
1436 else if (previous_ws->y - ws->height > 0)
1437 ws->y = previous_ws->y - ws->height;
1438 }
1439
1440 if (ws->y < 0)
1441 ws->y = 0;
1442 else if (ws->y + ws->height > screen_height)
1443 {
1444 if (horizontal_p)
1445 ws->y = previous_ws->y - ws->height;
1446 else
1447 ws->y = screen_height - ws->height;
1448 if (ws->y < 0)
1449 ws->y = 0;
1450 }
1451 }
1452
1453 static void
1454 create_pixmap_for_menu (window_state* ws, XlwMenuWidget mw)
1455 {
1456 if (ws->pixmap != None)
1457 {
1458 XFreePixmap (XtDisplay (ws->w), ws->pixmap);
1459 ws->pixmap = None;
1460 }
1461 ws->pixmap = XCreatePixmap (XtDisplay (ws->w), ws->window,
1462 ws->width, ws->height,
1463 DefaultDepthOfScreen (XtScreen (ws->w)));
1464 #ifdef HAVE_XFT
1465 if (ws->xft_draw)
1466 XftDrawDestroy (ws->xft_draw);
1467 if (mw->menu.xft_font)
1468 {
1469 int screen = XScreenNumberOfScreen (mw->core.screen);
1470 ws->xft_draw = XftDrawCreate (XtDisplay (ws->w),
1471 ws->pixmap,
1472 DefaultVisual (XtDisplay (ws->w), screen),
1473 mw->core.colormap);
1474 }
1475 else
1476 ws->xft_draw = 0;
1477 #endif
1478 }
1479
1480 /* Updates old_stack from new_stack and redisplays. */
1481 static void
1482 remap_menubar (mw)
1483 XlwMenuWidget mw;
1484 {
1485 int i;
1486 int last_same;
1487 XPoint selection_position;
1488 int old_depth = mw->menu.old_depth;
1489 int new_depth = mw->menu.new_depth;
1490 widget_value** old_stack;
1491 widget_value** new_stack;
1492 window_state* windows;
1493 widget_value* old_selection;
1494 widget_value* new_selection;
1495
1496 /* Check that enough windows and old_stack are ready. */
1497 make_windows_if_needed (mw, new_depth);
1498 make_old_stack_space (mw, new_depth);
1499 windows = mw->menu.windows;
1500 old_stack = mw->menu.old_stack;
1501 new_stack = mw->menu.new_stack;
1502
1503 /* compute the last identical different entry */
1504 for (i = 1; i < old_depth && i < new_depth; i++)
1505 if (old_stack [i] != new_stack [i])
1506 break;
1507 last_same = i - 1;
1508
1509 /* Memorize the previously selected item to be able to refresh it */
1510 old_selection = last_same + 1 < old_depth ? old_stack [last_same + 1] : NULL;
1511 if (old_selection && !old_selection->enabled)
1512 old_selection = NULL;
1513 new_selection = last_same + 1 < new_depth ? new_stack [last_same + 1] : NULL;
1514 if (new_selection && !new_selection->enabled)
1515 new_selection = NULL;
1516
1517 /* Call callback when the hightlighted item changes. */
1518 if (old_selection || new_selection)
1519 XtCallCallbackList ((Widget)mw, mw->menu.highlight,
1520 (XtPointer) new_selection);
1521
1522 /* updates old_state from new_state. It has to be done now because
1523 display_menu (called below) uses the old_stack to know what to display. */
1524 for (i = last_same + 1; i < new_depth; i++)
1525 {
1526 XtPopdown (mw->menu.windows [i].w);
1527 old_stack [i] = new_stack [i];
1528 }
1529 mw->menu.old_depth = new_depth;
1530
1531 /* refresh the last selection */
1532 selection_position.x = 0;
1533 selection_position.y = 0;
1534 display_menu (mw, last_same, new_selection == old_selection,
1535 &selection_position, NULL, NULL);
1536
1537 /* Now place the new menus. */
1538 for (i = last_same + 1; i < new_depth && new_stack[i]->contents; i++)
1539 {
1540 window_state *previous_ws = &windows[i - 1];
1541 window_state *ws = &windows[i];
1542
1543 ws->x = (previous_ws->x + selection_position.x
1544 + mw->menu.shadow_thickness);
1545 if (mw->menu.horizontal && i == 1)
1546 ws->x += mw->menu.margin;
1547
1548 #if 0
1549 if (!mw->menu.horizontal || i > 1)
1550 ws->x += mw->menu.shadow_thickness;
1551 #endif
1552
1553 ws->y = (previous_ws->y + selection_position.y
1554 + mw->menu.shadow_thickness);
1555 if (mw->menu.horizontal && i == 1)
1556 ws->y += mw->menu.margin;
1557
1558 size_menu (mw, i);
1559
1560 fit_to_screen (mw, ws, previous_ws, mw->menu.horizontal && i == 1);
1561
1562 XtVaSetValues (ws->w, XtNwidth, ws->width, XtNheight, ws->height,
1563 XtNx, ws->x, XtNy, ws->y, NULL);
1564 create_pixmap_for_menu (ws, mw);
1565 XtPopup (ws->w, XtGrabNone);
1566 display_menu (mw, i, False, &selection_position, NULL, NULL);
1567 }
1568
1569 /* unmap the menus that popped down */
1570 for (i = new_depth - 1; i < old_depth; i++)
1571 if (i >= new_depth || (i > 0 && !new_stack[i]->contents))
1572 XtPopdown (windows[i].w);
1573 }
1574
1575 static Boolean
1576 motion_event_is_in_menu (mw, ev, level, relative_pos)
1577 XlwMenuWidget mw;
1578 XMotionEvent* ev;
1579 int level;
1580 XPoint* relative_pos;
1581 {
1582 window_state* ws = &mw->menu.windows [level];
1583 int shadow = level == 0 ? 0 : mw->menu.shadow_thickness;
1584 int x = ws->x + shadow;
1585 int y = ws->y + shadow;
1586 relative_pos->x = ev->x_root - x;
1587 relative_pos->y = ev->y_root - y;
1588 return (x - shadow < ev->x_root && ev->x_root < x + ws->width
1589 && y - shadow < ev->y_root && ev->y_root < y + ws->height);
1590 }
1591
1592 static Boolean
1593 map_event_to_widget_value (mw, ev, val, level)
1594 XlwMenuWidget mw;
1595 XMotionEvent* ev;
1596 widget_value** val;
1597 int* level;
1598 {
1599 int i;
1600 XPoint relative_pos;
1601 window_state* ws;
1602 int inside = 0;
1603
1604 *val = NULL;
1605
1606 /* Find the window */
1607 for (i = mw->menu.old_depth - 1; i >= 0; i--)
1608 {
1609 ws = &mw->menu.windows [i];
1610 if (ws && motion_event_is_in_menu (mw, ev, i, &relative_pos))
1611 {
1612 inside = 1;
1613 display_menu (mw, i, True, NULL, &relative_pos, val);
1614
1615 if (*val)
1616 {
1617 *level = i + 1;
1618 return True;
1619 }
1620 }
1621 }
1622
1623 if (!inside)
1624 {
1625 if (mw->menu.inside_entry != NULL)
1626 XtCallCallbackList ((Widget)mw, mw->menu.leave,
1627 (XtPointer) mw->menu.inside_entry);
1628 mw->menu.inside_entry = NULL;
1629 }
1630
1631 return False;
1632 }
1633
1634 \f/* Procedures */
1635 static void
1636 make_drawing_gcs (mw)
1637 XlwMenuWidget mw;
1638 {
1639 XGCValues xgcv;
1640 float scale;
1641 XtGCMask mask = GCForeground | GCBackground;
1642
1643 #ifdef HAVE_X_I18N
1644 if (!mw->menu.fontSet)
1645 {
1646 xgcv.font = mw->menu.font->fid;
1647 mask |= GCFont;
1648 }
1649 #else
1650 xgcv.font = mw->menu.font->fid;
1651 mask |= GCFont;
1652 #endif
1653 xgcv.foreground = mw->menu.foreground;
1654 xgcv.background = mw->core.background_pixel;
1655 mw->menu.foreground_gc = XtGetGC ((Widget)mw, mask, &xgcv);
1656
1657 xgcv.foreground = mw->menu.button_foreground;
1658 mw->menu.button_gc = XtGetGC ((Widget)mw, mask, &xgcv);
1659
1660 xgcv.background = mw->core.background_pixel;
1661
1662 #define BRIGHTNESS(color) (((color) & 0xff) + (((color) >> 8) & 0xff) + (((color) >> 16) & 0xff))
1663
1664 /* Allocate color for disabled menu-items. */
1665 mw->menu.disabled_foreground = mw->menu.foreground;
1666 if (BRIGHTNESS(mw->menu.foreground) < BRIGHTNESS(mw->core.background_pixel))
1667 scale = 2.3;
1668 else
1669 scale = 0.55;
1670
1671 x_alloc_lighter_color_for_widget ((Widget) mw, XtDisplay ((Widget) mw),
1672 mw->core.colormap,
1673 &mw->menu.disabled_foreground,
1674 scale,
1675 0x8000);
1676
1677 if (mw->menu.foreground == mw->menu.disabled_foreground
1678 || mw->core.background_pixel == mw->menu.disabled_foreground)
1679 {
1680 /* Too few colors, use stipple. */
1681 xgcv.foreground = mw->menu.foreground;
1682 xgcv.fill_style = FillStippled;
1683 xgcv.stipple = mw->menu.gray_pixmap;
1684 mw->menu.disabled_gc = XtGetGC ((Widget)mw, mask
1685 | GCFillStyle | GCStipple, &xgcv);
1686 }
1687 else
1688 {
1689 /* Many colors available, use disabled pixel. */
1690 xgcv.foreground = mw->menu.disabled_foreground;
1691 mw->menu.disabled_gc = XtGetGC ((Widget)mw, mask, &xgcv);
1692 }
1693
1694 xgcv.foreground = mw->menu.button_foreground;
1695 xgcv.background = mw->core.background_pixel;
1696 xgcv.fill_style = FillStippled;
1697 xgcv.stipple = mw->menu.gray_pixmap;
1698 mw->menu.inactive_button_gc = XtGetGC ((Widget)mw, mask
1699 | GCFillStyle | GCStipple, &xgcv);
1700
1701 xgcv.foreground = mw->core.background_pixel;
1702 xgcv.background = mw->menu.foreground;
1703 mw->menu.background_gc = XtGetGC ((Widget)mw, mask, &xgcv);
1704 }
1705
1706 static void
1707 release_drawing_gcs (mw)
1708 XlwMenuWidget mw;
1709 {
1710 XtReleaseGC ((Widget) mw, mw->menu.foreground_gc);
1711 XtReleaseGC ((Widget) mw, mw->menu.button_gc);
1712 XtReleaseGC ((Widget) mw, mw->menu.disabled_gc);
1713 XtReleaseGC ((Widget) mw, mw->menu.inactive_button_gc);
1714 XtReleaseGC ((Widget) mw, mw->menu.background_gc);
1715 /* let's get some segvs if we try to use these... */
1716 mw->menu.foreground_gc = (GC) -1;
1717 mw->menu.button_gc = (GC) -1;
1718 mw->menu.disabled_gc = (GC) -1;
1719 mw->menu.inactive_button_gc = (GC) -1;
1720 mw->menu.background_gc = (GC) -1;
1721 }
1722
1723 #define MINL(x,y) ((((unsigned long) (x)) < ((unsigned long) (y))) \
1724 ? ((unsigned long) (x)) : ((unsigned long) (y)))
1725
1726 static void
1727 make_shadow_gcs (mw)
1728 XlwMenuWidget mw;
1729 {
1730 XGCValues xgcv;
1731 unsigned long pm = 0;
1732 Display *dpy = XtDisplay ((Widget) mw);
1733 Screen *screen = XtScreen ((Widget) mw);
1734 Colormap cmap = mw->core.colormap;
1735 XColor topc, botc;
1736 int top_frobbed = 0, bottom_frobbed = 0;
1737
1738 mw->menu.free_top_shadow_color_p = 0;
1739 mw->menu.free_bottom_shadow_color_p = 0;
1740
1741 if (mw->menu.top_shadow_color == -1)
1742 mw->menu.top_shadow_color = mw->core.background_pixel;
1743 else
1744 mw->menu.top_shadow_color = mw->menu.top_shadow_color;
1745
1746 if (mw->menu.bottom_shadow_color == -1)
1747 mw->menu.bottom_shadow_color = mw->menu.foreground;
1748 else
1749 mw->menu.bottom_shadow_color = mw->menu.bottom_shadow_color;
1750
1751 if (mw->menu.top_shadow_color == mw->core.background_pixel ||
1752 mw->menu.top_shadow_color == mw->menu.foreground)
1753 {
1754 topc.pixel = mw->core.background_pixel;
1755 #ifdef emacs
1756 if (x_alloc_lighter_color_for_widget ((Widget) mw, dpy, cmap,
1757 &topc.pixel,
1758 1.2, 0x8000))
1759 #else
1760 XQueryColor (dpy, cmap, &topc);
1761 /* don't overflow/wrap! */
1762 topc.red = MINL (65535, topc.red * 1.2);
1763 topc.green = MINL (65535, topc.green * 1.2);
1764 topc.blue = MINL (65535, topc.blue * 1.2);
1765 if (XAllocColor (dpy, cmap, &topc))
1766 #endif
1767 {
1768 mw->menu.top_shadow_color = topc.pixel;
1769 mw->menu.free_top_shadow_color_p = 1;
1770 top_frobbed = 1;
1771 }
1772 }
1773 if (mw->menu.bottom_shadow_color == mw->menu.foreground ||
1774 mw->menu.bottom_shadow_color == mw->core.background_pixel)
1775 {
1776 botc.pixel = mw->core.background_pixel;
1777 #ifdef emacs
1778 if (x_alloc_lighter_color_for_widget ((Widget) mw, dpy, cmap,
1779 &botc.pixel,
1780 0.6, 0x4000))
1781 #else
1782 XQueryColor (dpy, cmap, &botc);
1783 botc.red *= 0.6;
1784 botc.green *= 0.6;
1785 botc.blue *= 0.6;
1786 if (XAllocColor (dpy, cmap, &botc))
1787 #endif
1788 {
1789 mw->menu.bottom_shadow_color = botc.pixel;
1790 mw->menu.free_bottom_shadow_color_p = 1;
1791 bottom_frobbed = 1;
1792 }
1793 }
1794
1795 if (top_frobbed && bottom_frobbed)
1796 {
1797 if (topc.pixel == botc.pixel)
1798 {
1799 if (botc.pixel == mw->menu.foreground)
1800 {
1801 if (mw->menu.free_top_shadow_color_p)
1802 {
1803 x_free_dpy_colors (dpy, screen, cmap,
1804 &mw->menu.top_shadow_color, 1);
1805 mw->menu.free_top_shadow_color_p = 0;
1806 }
1807 mw->menu.top_shadow_color = mw->core.background_pixel;
1808 }
1809 else
1810 {
1811 if (mw->menu.free_bottom_shadow_color_p)
1812 {
1813 x_free_dpy_colors (dpy, screen, cmap,
1814 &mw->menu.bottom_shadow_color, 1);
1815 mw->menu.free_bottom_shadow_color_p = 0;
1816 }
1817 mw->menu.bottom_shadow_color = mw->menu.foreground;
1818 }
1819 }
1820 }
1821
1822 if (!mw->menu.top_shadow_pixmap &&
1823 mw->menu.top_shadow_color == mw->core.background_pixel)
1824 {
1825 mw->menu.top_shadow_pixmap = mw->menu.gray_pixmap;
1826 if (mw->menu.free_top_shadow_color_p)
1827 {
1828 x_free_dpy_colors (dpy, screen, cmap, &mw->menu.top_shadow_color, 1);
1829 mw->menu.free_top_shadow_color_p = 0;
1830 }
1831 mw->menu.top_shadow_color = mw->menu.foreground;
1832 }
1833 if (!mw->menu.bottom_shadow_pixmap &&
1834 mw->menu.bottom_shadow_color == mw->core.background_pixel)
1835 {
1836 mw->menu.bottom_shadow_pixmap = mw->menu.gray_pixmap;
1837 if (mw->menu.free_bottom_shadow_color_p)
1838 {
1839 x_free_dpy_colors (dpy, screen, cmap,
1840 &mw->menu.bottom_shadow_color, 1);
1841 mw->menu.free_bottom_shadow_color_p = 0;
1842 }
1843 mw->menu.bottom_shadow_color = mw->menu.foreground;
1844 }
1845
1846 xgcv.fill_style = FillStippled;
1847 xgcv.foreground = mw->menu.top_shadow_color;
1848 xgcv.stipple = mw->menu.top_shadow_pixmap;
1849 pm = (xgcv.stipple ? GCStipple|GCFillStyle : 0);
1850 mw->menu.shadow_top_gc = XtGetGC ((Widget)mw, GCForeground | pm, &xgcv);
1851
1852 xgcv.foreground = mw->menu.bottom_shadow_color;
1853 xgcv.stipple = mw->menu.bottom_shadow_pixmap;
1854 pm = (xgcv.stipple ? GCStipple|GCFillStyle : 0);
1855 mw->menu.shadow_bottom_gc = XtGetGC ((Widget)mw, GCForeground | pm, &xgcv);
1856 }
1857
1858
1859 static void
1860 release_shadow_gcs (mw)
1861 XlwMenuWidget mw;
1862 {
1863 Display *dpy = XtDisplay ((Widget) mw);
1864 Screen *screen = XtScreen ((Widget) mw);
1865 Colormap cmap = mw->core.colormap;
1866 Pixel px[2];
1867 int i = 0;
1868
1869 if (mw->menu.free_top_shadow_color_p)
1870 px[i++] = mw->menu.top_shadow_color;
1871 if (mw->menu.free_bottom_shadow_color_p)
1872 px[i++] = mw->menu.bottom_shadow_color;
1873 if (i > 0)
1874 x_free_dpy_colors (dpy, screen, cmap, px, i);
1875
1876 XtReleaseGC ((Widget) mw, mw->menu.shadow_top_gc);
1877 XtReleaseGC ((Widget) mw, mw->menu.shadow_bottom_gc);
1878 }
1879
1880 #ifdef HAVE_XFT
1881 static int
1882 openXftFont (mw)
1883 XlwMenuWidget mw;
1884 {
1885 char *fname = mw->menu.faceName;
1886
1887 mw->menu.xft_font = 0;
1888 mw->menu.default_face = fname && strcmp (fname, DEFAULT_FACENAME) == 0;
1889
1890 if (fname && strcmp (fname, "none") != 0)
1891 {
1892 int screen = XScreenNumberOfScreen (mw->core.screen);
1893 int len = strlen (fname), i = len-1;
1894 /* Try to convert Gtk-syntax (Sans 9) to Xft syntax Sans-9. */
1895 while (i > 0 && isdigit (fname[i]))
1896 --i;
1897 if (fname[i] == ' ')
1898 {
1899 fname = xstrdup (mw->menu.faceName);
1900 fname[i] = '-';
1901 }
1902
1903 mw->menu.xft_font = XftFontOpenName (XtDisplay (mw), screen, fname);
1904 if (!mw->menu.xft_font)
1905 {
1906 fprintf (stderr, "Can't find font '%s'\n", fname);
1907 mw->menu.xft_font = XftFontOpenName (XtDisplay (mw), screen,
1908 DEFAULT_FACENAME);
1909 }
1910 }
1911
1912 if (fname != mw->menu.faceName) free (fname);
1913
1914 return mw->menu.xft_font != 0;
1915 }
1916 #endif
1917
1918 static void
1919 XlwMenuInitialize (request, mw, args, num_args)
1920 Widget request;
1921 XlwMenuWidget mw;
1922 ArgList args;
1923 Cardinal *num_args;
1924 {
1925 /* Get the GCs and the widget size */
1926
1927 Window window = RootWindowOfScreen (DefaultScreenOfDisplay (XtDisplay (mw)));
1928 Display* display = XtDisplay (mw);
1929
1930 #if 0
1931 widget_value *tem = (widget_value *) XtMalloc (sizeof (widget_value));
1932
1933 /* _XtCreate is freeing the object that was passed to us,
1934 so make a copy that we will actually keep. */
1935 lwlib_bcopy (mw->menu.contents, tem, sizeof (widget_value));
1936 mw->menu.contents = tem;
1937 #endif
1938
1939 /* mw->menu.cursor = XCreateFontCursor (display, mw->menu.cursor_shape); */
1940 mw->menu.cursor = mw->menu.cursor_shape;
1941
1942 mw->menu.gray_pixmap
1943 = XCreatePixmapFromBitmapData (display, window, gray_bitmap_bits,
1944 gray_bitmap_width, gray_bitmap_height,
1945 (unsigned long)1, (unsigned long)0, 1);
1946
1947 #ifdef HAVE_XFT
1948 if (openXftFont (mw))
1949 ;
1950 else
1951 #endif
1952
1953 if (!mw->menu.font)
1954 {
1955 if (!xlwmenu_default_font)
1956 xlwmenu_default_font = XLoadQueryFont (display, "fixed");
1957 mw->menu.font = xlwmenu_default_font;
1958 if (!mw->menu.font)
1959 {
1960 fprintf (stderr, "Menu font fixed not found, can't continue.\n");
1961 abort ();
1962 }
1963 }
1964
1965 #ifdef HAVE_X_I18N
1966 if (mw->menu.fontSet)
1967 mw->menu.font_extents = XExtentsOfFontSet (mw->menu.fontSet);
1968 #endif
1969
1970 make_drawing_gcs (mw);
1971 make_shadow_gcs (mw);
1972
1973 mw->menu.popped_up = False;
1974
1975 mw->menu.old_depth = 1;
1976 mw->menu.old_stack = (widget_value**)XtMalloc (sizeof (widget_value*));
1977 mw->menu.old_stack_length = 1;
1978 mw->menu.old_stack [0] = mw->menu.contents;
1979
1980 mw->menu.new_depth = 0;
1981 mw->menu.new_stack = 0;
1982 mw->menu.new_stack_length = 0;
1983 push_new_stack (mw, mw->menu.contents);
1984
1985 mw->menu.windows = (window_state*)XtMalloc (sizeof (window_state));
1986 mw->menu.windows_length = 1;
1987 mw->menu.windows [0].x = 0;
1988 mw->menu.windows [0].y = 0;
1989 mw->menu.windows [0].width = 0;
1990 mw->menu.windows [0].height = 0;
1991 mw->menu.windows [0].max_rest_width = 0;
1992 mw->menu.windows [0].pixmap = None;
1993 #ifdef HAVE_XFT
1994 mw->menu.windows [0].xft_draw = 0;
1995 #endif
1996 size_menu (mw, 0);
1997
1998 mw->core.width = mw->menu.windows [0].width;
1999 mw->core.height = mw->menu.windows [0].height;
2000 }
2001
2002 static void
2003 XlwMenuClassInitialize ()
2004 {
2005 xlwmenu_default_font = 0;
2006 }
2007
2008 static void
2009 XlwMenuRealize (w, valueMask, attributes)
2010 Widget w;
2011 Mask *valueMask;
2012 XSetWindowAttributes *attributes;
2013 {
2014 XlwMenuWidget mw = (XlwMenuWidget)w;
2015 XSetWindowAttributes xswa;
2016 int mask;
2017
2018 (*xlwMenuWidgetClass->core_class.superclass->core_class.realize)
2019 (w, valueMask, attributes);
2020
2021 xswa.save_under = True;
2022 xswa.cursor = mw->menu.cursor_shape;
2023 mask = CWSaveUnder | CWCursor;
2024 /* I sometimes get random BadCursor errors while creating the first
2025 frame on a display. I can not find their reason, but they are
2026 annoying so for now let's ignore any errors here. -- lorentey */
2027 #ifdef emacs
2028 x_catch_errors (XtDisplay (w));
2029 #endif
2030 XChangeWindowAttributes (XtDisplay (w), XtWindow (w), mask, &xswa);
2031 #ifdef emacs
2032 x_uncatch_errors ();
2033 #endif
2034
2035 mw->menu.windows [0].w = w;
2036 mw->menu.windows [0].window = XtWindow (w);
2037 mw->menu.windows [0].x = w->core.x;
2038 mw->menu.windows [0].y = w->core.y;
2039 mw->menu.windows [0].width = w->core.width;
2040 mw->menu.windows [0].height = w->core.height;
2041
2042 create_pixmap_for_menu (&mw->menu.windows [0], mw);
2043
2044 #ifdef HAVE_XFT
2045 if (mw->menu.xft_font)
2046 {
2047 XColor colors[3];
2048 colors[0].pixel = mw->menu.xft_fg.pixel = mw->menu.foreground;
2049 colors[1].pixel = mw->menu.xft_bg.pixel = mw->core.background_pixel;
2050 colors[2].pixel = mw->menu.xft_disabled_fg.pixel
2051 = mw->menu.disabled_foreground;
2052 XQueryColors (XtDisplay (mw), mw->core.colormap, colors, 3);
2053 mw->menu.xft_fg.color.alpha = 0xFFFF;
2054 mw->menu.xft_fg.color.red = colors[0].red;
2055 mw->menu.xft_fg.color.green = colors[0].green;
2056 mw->menu.xft_fg.color.blue = colors[0].blue;
2057 mw->menu.xft_bg.color.alpha = 0xFFFF;
2058 mw->menu.xft_bg.color.red = colors[1].red;
2059 mw->menu.xft_bg.color.green = colors[1].green;
2060 mw->menu.xft_bg.color.blue = colors[1].blue;
2061 mw->menu.xft_disabled_fg.color.alpha = 0xFFFF;
2062 mw->menu.xft_disabled_fg.color.red = colors[2].red;
2063 mw->menu.xft_disabled_fg.color.green = colors[2].green;
2064 mw->menu.xft_disabled_fg.color.blue = colors[2].blue;
2065 }
2066 #endif
2067 }
2068
2069 /* Only the toplevel menubar/popup is a widget so it's the only one that
2070 receives expose events through Xt. So we repaint all the other panes
2071 when receiving an Expose event. */
2072 static void
2073 XlwMenuRedisplay (w, ev, region)
2074 Widget w;
2075 XEvent* ev;
2076 Region region;
2077 {
2078 XlwMenuWidget mw = (XlwMenuWidget)w;
2079 int i;
2080
2081 /* If we have a depth beyond 1, it's because a submenu was displayed.
2082 If the submenu has been destroyed, set the depth back to 1. */
2083 if (submenu_destroyed)
2084 {
2085 mw->menu.old_depth = 1;
2086 submenu_destroyed = 0;
2087 }
2088
2089 display_menu (mw, 0, False, NULL, NULL, NULL);
2090 }
2091
2092
2093 /* Part of a hack to make the menu redisplay when a tooltip frame
2094 over a menu item is unmapped. */
2095
2096 void
2097 xlwmenu_redisplay (w)
2098 Widget w;
2099 {
2100 XlwMenuRedisplay (w, NULL, None);
2101 }
2102
2103 static void
2104 XlwMenuDestroy (w)
2105 Widget w;
2106 {
2107 int i;
2108 XlwMenuWidget mw = (XlwMenuWidget) w;
2109
2110 if (pointer_grabbed)
2111 ungrab_all ((Widget)w, CurrentTime);
2112 pointer_grabbed = 0;
2113
2114 submenu_destroyed = 1;
2115
2116 release_drawing_gcs (mw);
2117 release_shadow_gcs (mw);
2118
2119 /* this doesn't come from the resource db but is created explicitly
2120 so we must free it ourselves. */
2121 XFreePixmap (XtDisplay (mw), mw->menu.gray_pixmap);
2122 mw->menu.gray_pixmap = (Pixmap) -1;
2123
2124 #if 0
2125 /* Do free mw->menu.contents because nowadays we copy it
2126 during initialization. */
2127 XtFree (mw->menu.contents);
2128 #endif
2129
2130 /* Don't free mw->menu.contents because that comes from our creator.
2131 The `*_stack' elements are just pointers into `contents' so leave
2132 that alone too. But free the stacks themselves. */
2133 if (mw->menu.old_stack) XtFree ((char *) mw->menu.old_stack);
2134 if (mw->menu.new_stack) XtFree ((char *) mw->menu.new_stack);
2135
2136 /* Remember, you can't free anything that came from the resource
2137 database. This includes:
2138 mw->menu.cursor
2139 mw->menu.top_shadow_pixmap
2140 mw->menu.bottom_shadow_pixmap
2141 mw->menu.font
2142 Also the color cells of top_shadow_color, bottom_shadow_color,
2143 foreground, and button_foreground will never be freed until this
2144 client exits. Nice, eh?
2145 */
2146
2147 #ifdef HAVE_XFT
2148 if (mw->menu.windows [0].xft_draw)
2149 XftDrawDestroy (mw->menu.windows [0].xft_draw);
2150 if (mw->menu.xft_font)
2151 XftFontClose (XtDisplay (mw), mw->menu.xft_font);
2152 #endif
2153
2154 if (mw->menu.windows [0].pixmap != None)
2155 XFreePixmap (XtDisplay (mw), mw->menu.windows [0].pixmap);
2156 /* start from 1 because the one in slot 0 is w->core.window */
2157 for (i = 1; i < mw->menu.windows_length; i++)
2158 {
2159 if (mw->menu.windows [i].pixmap != None)
2160 XFreePixmap (XtDisplay (mw), mw->menu.windows [i].pixmap);
2161 XtDestroyWidget (mw->menu.windows [i].w);
2162 #ifdef HAVE_XFT
2163 if (mw->menu.windows [i].xft_draw)
2164 XftDrawDestroy (mw->menu.windows [i].xft_draw);
2165 #endif
2166 }
2167
2168 if (mw->menu.windows)
2169 XtFree ((char *) mw->menu.windows);
2170 }
2171
2172 #ifdef HAVE_XFT
2173 static int
2174 facename_changed (XlwMenuWidget newmw,
2175 XlwMenuWidget oldmw)
2176 {
2177 /* This will fore a new XftFont even if the same string is set.
2178 This is good, as rendering parameters may have changed and
2179 we just want to do a redisplay. */
2180 return newmw->menu.faceName != oldmw->menu.faceName;
2181 }
2182 #endif
2183
2184 static Boolean
2185 XlwMenuSetValues (current, request, new)
2186 Widget current;
2187 Widget request;
2188 Widget new;
2189 {
2190 XlwMenuWidget oldmw = (XlwMenuWidget)current;
2191 XlwMenuWidget newmw = (XlwMenuWidget)new;
2192 Boolean redisplay = False;
2193 int i;
2194
2195 if (newmw->menu.contents
2196 && newmw->menu.contents->contents
2197 && newmw->menu.contents->contents->change >= VISIBLE_CHANGE)
2198 redisplay = True;
2199 /* Do redisplay if the contents are entirely eliminated. */
2200 if (newmw->menu.contents
2201 && newmw->menu.contents->contents == 0
2202 && newmw->menu.contents->change >= VISIBLE_CHANGE)
2203 redisplay = True;
2204
2205 if (newmw->core.background_pixel != oldmw->core.background_pixel
2206 || newmw->menu.foreground != oldmw->menu.foreground
2207 #ifdef HAVE_XFT
2208 || facename_changed (newmw, oldmw)
2209 #endif
2210 #ifdef HAVE_X_I18N
2211 || newmw->menu.fontSet != oldmw->menu.fontSet
2212 || (newmw->menu.fontSet == NULL && newmw->menu.font != oldmw->menu.font)
2213 #else
2214 || newmw->menu.font != oldmw->menu.font
2215 #endif
2216 )
2217 {
2218 release_drawing_gcs (newmw);
2219 make_drawing_gcs (newmw);
2220
2221 release_shadow_gcs (newmw);
2222 /* Cause the shadow colors to be recalculated. */
2223 newmw->menu.top_shadow_color = -1;
2224 newmw->menu.bottom_shadow_color = -1;
2225 make_shadow_gcs (newmw);
2226
2227 redisplay = True;
2228
2229 if (XtIsRealized (current))
2230 /* If the menu is currently displayed, change the display. */
2231 for (i = 0; i < oldmw->menu.windows_length; i++)
2232 {
2233 XSetWindowBackground (XtDisplay (oldmw),
2234 oldmw->menu.windows [i].window,
2235 newmw->core.background_pixel);
2236 /* clear windows and generate expose events */
2237 XClearArea (XtDisplay (oldmw), oldmw->menu.windows[i].window,
2238 0, 0, 0, 0, True);
2239 }
2240 }
2241
2242 #ifdef HAVE_XFT
2243 if (facename_changed (newmw, oldmw))
2244 {
2245 int i;
2246 int screen = XScreenNumberOfScreen (newmw->core.screen);
2247 if (newmw->menu.xft_font)
2248 XftFontClose (XtDisplay (newmw), newmw->menu.xft_font);
2249 openXftFont (newmw);
2250 for (i = 0; i < newmw->menu.windows_length; i++)
2251 {
2252 if (newmw->menu.windows [i].xft_draw)
2253 XftDrawDestroy (newmw->menu.windows [i].xft_draw);
2254 newmw->menu.windows [i].xft_draw = 0;
2255 }
2256 if (newmw->menu.xft_font)
2257 for (i = 0; i < newmw->menu.windows_length; i++)
2258 newmw->menu.windows [i].xft_draw
2259 = XftDrawCreate (XtDisplay (newmw),
2260 newmw->menu.windows [i].window,
2261 DefaultVisual (XtDisplay (newmw), screen),
2262 newmw->core.colormap);
2263 }
2264 #endif
2265 #ifdef HAVE_X_I18N
2266 if (newmw->menu.fontSet != oldmw->menu.fontSet && newmw->menu.fontSet != NULL)
2267 {
2268 redisplay = True;
2269 newmw->menu.font_extents = XExtentsOfFontSet (newmw->menu.fontSet);
2270 }
2271 #endif
2272
2273 return redisplay;
2274 }
2275
2276 static void
2277 XlwMenuResize (w)
2278 Widget w;
2279 {
2280 XlwMenuWidget mw = (XlwMenuWidget)w;
2281
2282 if (mw->menu.popped_up)
2283 {
2284 /* Don't allow the popup menu to resize itself. */
2285 mw->core.width = mw->menu.windows [0].width;
2286 mw->core.height = mw->menu.windows [0].height;
2287 mw->core.parent->core.width = mw->core.width;
2288 mw->core.parent->core.height = mw->core.height;
2289 }
2290 else
2291 {
2292 mw->menu.windows [0].width = mw->core.width;
2293 mw->menu.windows [0].height = mw->core.height;
2294 create_pixmap_for_menu (&mw->menu.windows [0], mw);
2295 }
2296 }
2297
2298 \f/* Action procedures */
2299 static void
2300 handle_single_motion_event (mw, ev)
2301 XlwMenuWidget mw;
2302 XMotionEvent* ev;
2303 {
2304 widget_value* val;
2305 int level;
2306
2307 if (!map_event_to_widget_value (mw, ev, &val, &level))
2308 pop_new_stack_if_no_contents (mw);
2309 else
2310 set_new_state (mw, val, level);
2311 remap_menubar (mw);
2312
2313 /* Sync with the display. Makes it feel better on X terms. */
2314 XSync (XtDisplay (mw), False);
2315 }
2316
2317 static void
2318 handle_motion_event (mw, ev)
2319 XlwMenuWidget mw;
2320 XMotionEvent* ev;
2321 {
2322 int x = ev->x_root;
2323 int y = ev->y_root;
2324 int state = ev->state;
2325 XMotionEvent oldev = *ev;
2326
2327 /* allow motion events to be generated again */
2328 if (ev->is_hint
2329 && XQueryPointer (XtDisplay (mw), ev->window,
2330 &ev->root, &ev->subwindow,
2331 &ev->x_root, &ev->y_root,
2332 &ev->x, &ev->y,
2333 &ev->state)
2334 && ev->state == state
2335 && (ev->x_root != x || ev->y_root != y))
2336 handle_single_motion_event (mw, ev);
2337 else
2338 handle_single_motion_event (mw, &oldev);
2339 }
2340
2341 static void
2342 Start (w, ev, params, num_params)
2343 Widget w;
2344 XEvent *ev;
2345 String *params;
2346 Cardinal *num_params;
2347 {
2348 XlwMenuWidget mw = (XlwMenuWidget)w;
2349
2350 if (!mw->menu.popped_up)
2351 {
2352 menu_post_event = *ev;
2353 /* If event is set to CurrentTime, get the last known time stamp.
2354 This is for calculating if (popup) menus should stay up after
2355 a fast click. */
2356 if (menu_post_event.xbutton.time == CurrentTime)
2357 menu_post_event.xbutton.time
2358 = XtLastTimestampProcessed (XtDisplay (w));
2359
2360 pop_up_menu (mw, (XButtonPressedEvent*) ev);
2361 }
2362 else
2363 {
2364 /* If we push a button while the menu is posted semipermanently,
2365 releasing the button should always pop the menu down. */
2366 next_release_must_exit = 1;
2367
2368 /* notes the absolute position of the menubar window */
2369 mw->menu.windows [0].x = ev->xmotion.x_root - ev->xmotion.x;
2370 mw->menu.windows [0].y = ev->xmotion.y_root - ev->xmotion.y;
2371
2372 /* handles the down like a move, slots are compatible */
2373 ev->xmotion.is_hint = 0;
2374 handle_motion_event (mw, &ev->xmotion);
2375 }
2376 }
2377
2378 static void
2379 Drag (w, ev, params, num_params)
2380 Widget w;
2381 XEvent *ev;
2382 String *params;
2383 Cardinal *num_params;
2384 {
2385 XlwMenuWidget mw = (XlwMenuWidget)w;
2386 if (mw->menu.popped_up)
2387 handle_motion_event (mw, &ev->xmotion);
2388 }
2389
2390 /* Do nothing.
2391 This is how we handle presses and releases of modifier keys. */
2392 static void
2393 Nothing (w, ev, params, num_params)
2394 Widget w;
2395 XEvent *ev;
2396 String *params;
2397 Cardinal *num_params;
2398 {
2399 }
2400
2401 static widget_value *
2402 find_first_selectable (mw, item, skip_titles)
2403 XlwMenuWidget mw;
2404 widget_value *item;
2405 int skip_titles;
2406 {
2407 widget_value *current = item;
2408 enum menu_separator separator;
2409
2410 while (lw_separator_p (current->name, &separator, 0) || !current->enabled
2411 || (skip_titles && !current->call_data && !current->contents))
2412 if (current->next)
2413 current=current->next;
2414 else
2415 return NULL;
2416
2417 return current;
2418 }
2419
2420 static widget_value *
2421 find_next_selectable (mw, item, skip_titles)
2422 XlwMenuWidget mw;
2423 widget_value *item;
2424 int skip_titles;
2425 {
2426 widget_value *current = item;
2427 enum menu_separator separator;
2428
2429 while (current->next && (current=current->next) &&
2430 (lw_separator_p (current->name, &separator, 0) || !current->enabled
2431 || (skip_titles && !current->call_data && !current->contents)))
2432 ;
2433
2434 if (current == item)
2435 {
2436 if (mw->menu.old_depth < 2)
2437 return current;
2438 current = mw->menu.old_stack [mw->menu.old_depth - 2]->contents;
2439
2440 while (lw_separator_p (current->name, &separator, 0)
2441 || !current->enabled
2442 || (skip_titles && !current->call_data
2443 && !current->contents))
2444 {
2445 if (current->next)
2446 current=current->next;
2447
2448 if (current == item)
2449 break;
2450 }
2451
2452 }
2453
2454 return current;
2455 }
2456
2457 static widget_value *
2458 find_prev_selectable (mw, item, skip_titles)
2459 XlwMenuWidget mw;
2460 widget_value *item;
2461 int skip_titles;
2462 {
2463 widget_value *current = item;
2464 widget_value *prev = item;
2465
2466 while ((current=find_next_selectable (mw, current, skip_titles))
2467 != item)
2468 {
2469 if (prev == current)
2470 break;
2471 prev=current;
2472 }
2473
2474 return prev;
2475 }
2476
2477 static void
2478 Down (w, ev, params, num_params)
2479 Widget w;
2480 XEvent *ev;
2481 String *params;
2482 Cardinal *num_params;
2483 {
2484 XlwMenuWidget mw = (XlwMenuWidget) w;
2485 widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2486 int popup_menu_p = mw->menu.top_depth == 1;
2487
2488 /* Inside top-level menu-bar? */
2489 if (mw->menu.old_depth == mw->menu.top_depth)
2490 /* When <down> in the menu-bar is pressed, display the corresponding
2491 sub-menu and select the first selectable menu item there.
2492 If this is a popup menu, skip title item of the popup. */
2493 set_new_state (mw,
2494 find_first_selectable (mw,
2495 selected_item->contents,
2496 popup_menu_p),
2497 mw->menu.old_depth);
2498 else
2499 /* Highlight next possible (enabled and not separator) menu item. */
2500 set_new_state (mw, find_next_selectable (mw, selected_item, popup_menu_p),
2501 mw->menu.old_depth - 1);
2502
2503 remap_menubar (mw);
2504 }
2505
2506 static void
2507 Up (w, ev, params, num_params)
2508 Widget w;
2509 XEvent *ev;
2510 String *params;
2511 Cardinal *num_params;
2512 {
2513 XlwMenuWidget mw = (XlwMenuWidget) w;
2514 widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2515 int popup_menu_p = mw->menu.top_depth == 1;
2516
2517 /* Inside top-level menu-bar? */
2518 if (mw->menu.old_depth == mw->menu.top_depth)
2519 {
2520 /* FIXME: this is tricky. <up> in the menu-bar should select the
2521 last selectable item in the list. So we select the first
2522 selectable one and find the previous selectable item. Is there
2523 a better way? */
2524 /* If this is a popup menu, skip title item of the popup. */
2525 set_new_state (mw,
2526 find_first_selectable (mw,
2527 selected_item->contents,
2528 popup_menu_p),
2529 mw->menu.old_depth);
2530 remap_menubar (mw);
2531 selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2532 set_new_state (mw,
2533 find_prev_selectable (mw,
2534 selected_item,
2535 popup_menu_p),
2536 mw->menu.old_depth - 1);
2537 }
2538 else
2539 /* Highlight previous (enabled and not separator) menu item. */
2540 set_new_state (mw, find_prev_selectable (mw, selected_item, popup_menu_p),
2541 mw->menu.old_depth - 1);
2542
2543 remap_menubar (mw);
2544 }
2545
2546 void
2547 Left (w, ev, params, num_params)
2548 Widget w;
2549 XEvent *ev;
2550 String *params;
2551 Cardinal *num_params;
2552 {
2553 XlwMenuWidget mw = (XlwMenuWidget) w;
2554 widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2555
2556 /* Inside top-level menu-bar? */
2557 if (mw->menu.old_depth == mw->menu.top_depth)
2558 /* When <left> in the menu-bar is pressed, display the previous item on
2559 the menu-bar. If the current item is the first one, highlight the
2560 last item in the menubar (probably Help). */
2561 set_new_state (mw, find_prev_selectable (mw, selected_item, 0),
2562 mw->menu.old_depth - 1);
2563 else if (mw->menu.old_depth == 1
2564 && selected_item->contents) /* Is this menu item expandable? */
2565 {
2566 set_new_state (mw, selected_item->contents, mw->menu.old_depth);
2567 remap_menubar (mw);
2568 selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2569 if (!selected_item->enabled && find_first_selectable (mw,
2570 selected_item,
2571 0))
2572 set_new_state (mw, find_first_selectable (mw, selected_item, 0),
2573 mw->menu.old_depth - 1);
2574 }
2575
2576 else
2577 {
2578 pop_new_stack_if_no_contents (mw);
2579 set_new_state (mw, mw->menu.old_stack [mw->menu.old_depth - 2],
2580 mw->menu.old_depth - 2);
2581 }
2582
2583 remap_menubar (mw);
2584 }
2585
2586 void
2587 Right (w, ev, params, num_params)
2588 Widget w;
2589 XEvent *ev;
2590 String *params;
2591 Cardinal *num_params;
2592 {
2593 XlwMenuWidget mw = (XlwMenuWidget) w;
2594 widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2595
2596 /* Inside top-level menu-bar? */
2597 if (mw->menu.old_depth == mw->menu.top_depth)
2598 /* When <right> in the menu-bar is pressed, display the next item on
2599 the menu-bar. If the current item is the last one, highlight the
2600 first item (probably File). */
2601 set_new_state (mw, find_next_selectable (mw, selected_item, 0),
2602 mw->menu.old_depth - 1);
2603 else if (selected_item->contents) /* Is this menu item expandable? */
2604 {
2605 set_new_state (mw, selected_item->contents, mw->menu.old_depth);
2606 remap_menubar (mw);
2607 selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2608 if (!selected_item->enabled && find_first_selectable (mw,
2609 selected_item,
2610 0))
2611 set_new_state (mw, find_first_selectable (mw, selected_item, 0),
2612 mw->menu.old_depth - 1);
2613 }
2614 else
2615 {
2616 pop_new_stack_if_no_contents (mw);
2617 set_new_state (mw, mw->menu.old_stack [mw->menu.old_depth - 2],
2618 mw->menu.old_depth - 2);
2619 }
2620
2621 remap_menubar (mw);
2622 }
2623
2624 /* Handle key press and release events while menu is popped up.
2625 Our action is to get rid of the menu. */
2626 static void
2627 Key (w, ev, params, num_params)
2628 Widget w;
2629 XEvent *ev;
2630 String *params;
2631 Cardinal *num_params;
2632 {
2633 XlwMenuWidget mw = (XlwMenuWidget)w;
2634
2635 /* Pop down everything. */
2636 mw->menu.new_depth = 1;
2637 remap_menubar (mw);
2638
2639 if (mw->menu.popped_up)
2640 {
2641 mw->menu.popped_up = False;
2642 ungrab_all ((Widget)mw, ev->xmotion.time);
2643 if (XtIsShell (XtParent ((Widget) mw)))
2644 XtPopdown (XtParent ((Widget) mw));
2645 else
2646 {
2647 XtRemoveGrab ((Widget) mw);
2648 display_menu (mw, 0, False, NULL, NULL, NULL);
2649 }
2650 }
2651
2652 /* callback */
2653 XtCallCallbackList ((Widget)mw, mw->menu.select, (XtPointer)0);
2654 }
2655
2656 static void
2657 Select (w, ev, params, num_params)
2658 Widget w;
2659 XEvent *ev;
2660 String *params;
2661 Cardinal *num_params;
2662 {
2663 XlwMenuWidget mw = (XlwMenuWidget)w;
2664 widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2665
2666 /* If user releases the button quickly, without selecting anything,
2667 after the initial down-click that brought the menu up,
2668 do nothing. */
2669 if ((selected_item == 0
2670 || ((widget_value *) selected_item)->call_data == 0)
2671 && !next_release_must_exit
2672 && (ev->xbutton.time - menu_post_event.xbutton.time
2673 < XtGetMultiClickTime (XtDisplay (w))))
2674 return;
2675
2676 /* pop down everything. */
2677 mw->menu.new_depth = 1;
2678 remap_menubar (mw);
2679
2680 if (mw->menu.popped_up)
2681 {
2682 mw->menu.popped_up = False;
2683 ungrab_all ((Widget)mw, ev->xmotion.time);
2684 if (XtIsShell (XtParent ((Widget) mw)))
2685 XtPopdown (XtParent ((Widget) mw));
2686 else
2687 {
2688 XtRemoveGrab ((Widget) mw);
2689 display_menu (mw, 0, False, NULL, NULL, NULL);
2690 }
2691 }
2692
2693 /* callback */
2694 XtCallCallbackList ((Widget)mw, mw->menu.select, (XtPointer)selected_item);
2695 }
2696
2697
2698 \f/* Special code to pop-up a menu */
2699 static void
2700 pop_up_menu (mw, event)
2701 XlwMenuWidget mw;
2702 XButtonPressedEvent* event;
2703 {
2704 int x = event->x_root;
2705 int y = event->y_root;
2706 int w;
2707 int h;
2708 int borderwidth = mw->menu.shadow_thickness;
2709 Screen* screen = XtScreen (mw);
2710 Display *display = XtDisplay (mw);
2711
2712 next_release_must_exit = 0;
2713
2714 mw->menu.inside_entry = NULL;
2715 XtCallCallbackList ((Widget)mw, mw->menu.open, NULL);
2716
2717 if (XtIsShell (XtParent ((Widget)mw)))
2718 size_menu (mw, 0);
2719
2720 w = mw->menu.windows [0].width;
2721 h = mw->menu.windows [0].height;
2722
2723 x -= borderwidth;
2724 y -= borderwidth;
2725 if (x < borderwidth)
2726 x = borderwidth;
2727 if (x + w + 2 * borderwidth > WidthOfScreen (screen))
2728 x = WidthOfScreen (screen) - w - 2 * borderwidth;
2729 if (y < borderwidth)
2730 y = borderwidth;
2731 if (y + h + 2 * borderwidth> HeightOfScreen (screen))
2732 y = HeightOfScreen (screen) - h - 2 * borderwidth;
2733
2734 mw->menu.popped_up = True;
2735 if (XtIsShell (XtParent ((Widget)mw)))
2736 {
2737 XtConfigureWidget (XtParent ((Widget)mw), x, y, w, h,
2738 XtParent ((Widget)mw)->core.border_width);
2739 XtPopup (XtParent ((Widget)mw), XtGrabExclusive);
2740 display_menu (mw, 0, False, NULL, NULL, NULL);
2741 mw->menu.windows [0].x = x + borderwidth;
2742 mw->menu.windows [0].y = y + borderwidth;
2743 mw->menu.top_depth = 1; /* Popup menus don't have a bar so top is 1 */
2744 }
2745 else
2746 {
2747 XEvent *ev = (XEvent *) event;
2748
2749 XtAddGrab ((Widget) mw, True, True);
2750
2751 /* notes the absolute position of the menubar window */
2752 mw->menu.windows [0].x = ev->xmotion.x_root - ev->xmotion.x;
2753 mw->menu.windows [0].y = ev->xmotion.y_root - ev->xmotion.y;
2754 mw->menu.top_depth = 2;
2755 }
2756
2757 #ifdef emacs
2758 x_catch_errors (display);
2759 #endif
2760 if (XtGrabPointer ((Widget)mw, False,
2761 (PointerMotionMask
2762 | PointerMotionHintMask
2763 | ButtonReleaseMask
2764 | ButtonPressMask),
2765 GrabModeAsync, GrabModeAsync, None,
2766 mw->menu.cursor_shape,
2767 event->time) == Success)
2768 {
2769 if (! GRAB_KEYBOARD
2770 || XtGrabKeyboard ((Widget)mw, False, GrabModeAsync,
2771 GrabModeAsync, event->time) == Success)
2772 {
2773 XtSetKeyboardFocus((Widget)mw, None);
2774 pointer_grabbed = 1;
2775 }
2776 else
2777 XtUngrabPointer ((Widget)mw, event->time);
2778 }
2779
2780 #ifdef emacs
2781 if (x_had_errors_p (display))
2782 {
2783 pointer_grabbed = 0;
2784 XtUngrabPointer ((Widget)mw, event->time);
2785 }
2786 x_uncatch_errors ();
2787 #endif
2788
2789 ((XMotionEvent*)event)->is_hint = 0;
2790 handle_motion_event (mw, (XMotionEvent*)event);
2791 }
2792
2793 /* arch-tag: 657f43dd-dfd0-4cc9-910c-52935f01176e
2794 (do not change this comment) */