Merge from emacs-23
[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 XlwMenuWidget mw = (XlwMenuWidget) closure;
1308 int i;
1309
1310 *continue_to_dispatch = False;
1311 for (i = 0; i < mw->menu.windows_length; ++i)
1312 if (mw->menu.windows [i].w == widget) break;
1313 if (i < mw->menu.windows_length && i < mw->menu.old_depth)
1314 display_menu (mw, i, False, NULL, NULL, NULL, NULL, NULL);
1315 }
1316
1317 static void
1318 set_window_type (Widget w, XlwMenuWidget mw)
1319 {
1320 int popup_menu_p = mw->menu.top_depth == 1;
1321 Atom type = XInternAtom (XtDisplay (w),
1322 popup_menu_p
1323 ? "_NET_WM_WINDOW_TYPE_POPUP_MENU"
1324 : "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU",
1325 False);
1326
1327 XChangeProperty (XtDisplay (w), XtWindow (w),
1328 XInternAtom (XtDisplay (w), "_NET_WM_WINDOW_TYPE", False),
1329 XA_ATOM, 32, PropModeReplace,
1330 (unsigned char *)&type, 1);
1331 }
1332
1333
1334 static void
1335 make_windows_if_needed (mw, n)
1336 XlwMenuWidget mw;
1337 int n;
1338 {
1339 int i;
1340 int start_at;
1341 window_state* windows;
1342 #ifdef HAVE_XFT
1343 int screen = XScreenNumberOfScreen (mw->core.screen);
1344 #endif
1345
1346 if (mw->menu.windows_length >= n)
1347 return;
1348
1349 if (!mw->menu.windows)
1350 {
1351 mw->menu.windows =
1352 (window_state*)XtMalloc (n * sizeof (window_state));
1353 start_at = 0;
1354 }
1355 else
1356 {
1357 mw->menu.windows =
1358 (window_state*)XtRealloc ((char*)mw->menu.windows,
1359 n * sizeof (window_state));
1360 start_at = mw->menu.windows_length;
1361 }
1362 mw->menu.windows_length = n;
1363
1364 windows = mw->menu.windows;
1365
1366 for (i = start_at; i < n; i++)
1367 {
1368 Arg av[10];
1369 int ac = 0;
1370 windows [i].x = 0;
1371 windows [i].y = 0;
1372 windows [i].width = 1;
1373 windows [i].height = 1;
1374 windows [i].max_rest_width = 0;
1375 XtSetArg (av[ac], XtNwidth, 1); ++ac;
1376 XtSetArg (av[ac], XtNheight, 1); ++ac;
1377 XtSetArg (av[ac], XtNsaveUnder, True); ++ac;
1378 XtSetArg (av[ac], XtNbackground, mw->core.background_pixel); ++ac;
1379 XtSetArg (av[ac], XtNborderColor, mw->core.border_pixel); ++ac;
1380 XtSetArg (av[ac], XtNborderWidth, mw->core.border_width); ++ac;
1381 XtSetArg (av[ac], XtNcursor, mw->menu.cursor_shape); ++ac;
1382 windows [i].w =
1383 XtCreatePopupShell ("sub", overrideShellWidgetClass,
1384 (Widget) mw, av, ac);
1385 XtRealizeWidget (windows [i].w);
1386 XtAddEventHandler (windows [i].w, ExposureMask, False, expose_cb, mw);
1387 windows [i].window = XtWindow (windows [i].w);
1388 windows [i].pixmap = None;
1389 #ifdef HAVE_XFT
1390 windows [i].xft_draw = 0;
1391 #endif
1392 set_window_type (windows [i].w, mw);
1393 }
1394 }
1395
1396 /* Value is non-zero if WINDOW is part of menu bar widget W. */
1397
1398 int
1399 xlwmenu_window_p (w, window)
1400 Widget w;
1401 Window window;
1402 {
1403 XlwMenuWidget mw = (XlwMenuWidget) w;
1404 int i;
1405
1406 for (i = 0; i < mw->menu.windows_length; ++i)
1407 if (window == mw->menu.windows[i].window)
1408 break;
1409
1410 return i < mw->menu.windows_length;
1411 }
1412
1413 /* Make the window fit in the screen */
1414 static void
1415 fit_to_screen (mw, ws, previous_ws, horizontal_p)
1416 XlwMenuWidget mw;
1417 window_state* ws;
1418 window_state* previous_ws;
1419 Boolean horizontal_p;
1420 {
1421 unsigned int screen_width = WidthOfScreen (XtScreen (mw));
1422 unsigned int screen_height = HeightOfScreen (XtScreen (mw));
1423 /* 1 if we are unable to avoid an overlap between
1424 this menu and the parent menu in the X dimension. */
1425 int horizontal_overlap = 0;
1426
1427 if (ws->x < 0)
1428 ws->x = 0;
1429 else if (ws->x + ws->width > screen_width)
1430 {
1431 if (!horizontal_p)
1432 /* The addition of shadow-thickness for a sub-menu's position is
1433 to reflect a similar adjustment when the menu is displayed to
1434 the right of the invoking menu-item; it makes the sub-menu
1435 look more `attached' to the menu-item. */
1436 ws->x = previous_ws->x - ws->width + mw->menu.shadow_thickness;
1437 else
1438 ws->x = screen_width - ws->width;
1439 if (ws->x < 0)
1440 {
1441 ws->x = 0;
1442 horizontal_overlap = 1;
1443 }
1444 }
1445 /* If we overlap in X, try to avoid overlap in Y. */
1446 if (horizontal_overlap
1447 && ws->y < previous_ws->y + previous_ws->height
1448 && previous_ws->y < ws->y + ws->height)
1449 {
1450 /* Put this menu right below or right above PREVIOUS_WS
1451 if there's room. */
1452 if (previous_ws->y + previous_ws->height + ws->height < screen_height)
1453 ws->y = previous_ws->y + previous_ws->height;
1454 else if (previous_ws->y - ws->height > 0)
1455 ws->y = previous_ws->y - ws->height;
1456 }
1457
1458 if (ws->y < 0)
1459 ws->y = 0;
1460 else if (ws->y + ws->height > screen_height)
1461 {
1462 if (horizontal_p)
1463 ws->y = previous_ws->y - ws->height;
1464 else
1465 ws->y = screen_height - ws->height;
1466 if (ws->y < 0)
1467 ws->y = 0;
1468 }
1469 }
1470
1471 static void
1472 create_pixmap_for_menu (window_state* ws, XlwMenuWidget mw)
1473 {
1474 if (ws->pixmap != None)
1475 {
1476 XFreePixmap (XtDisplay (ws->w), ws->pixmap);
1477 ws->pixmap = None;
1478 }
1479 ws->pixmap = XCreatePixmap (XtDisplay (ws->w), ws->window,
1480 ws->width, ws->height,
1481 DefaultDepthOfScreen (XtScreen (ws->w)));
1482 #ifdef HAVE_XFT
1483 if (ws->xft_draw)
1484 XftDrawDestroy (ws->xft_draw);
1485 if (mw->menu.xft_font)
1486 {
1487 int screen = XScreenNumberOfScreen (mw->core.screen);
1488 ws->xft_draw = XftDrawCreate (XtDisplay (ws->w),
1489 ws->pixmap,
1490 DefaultVisual (XtDisplay (ws->w), screen),
1491 mw->core.colormap);
1492 }
1493 else
1494 ws->xft_draw = 0;
1495 #endif
1496 }
1497
1498 /* Updates old_stack from new_stack and redisplays. */
1499 static void
1500 remap_menubar (mw)
1501 XlwMenuWidget mw;
1502 {
1503 int i;
1504 int last_same;
1505 XPoint selection_position;
1506 int old_depth = mw->menu.old_depth;
1507 int new_depth = mw->menu.new_depth;
1508 widget_value** old_stack;
1509 widget_value** new_stack;
1510 window_state* windows;
1511 widget_value* old_selection;
1512 widget_value* new_selection;
1513
1514 /* Check that enough windows and old_stack are ready. */
1515 make_windows_if_needed (mw, new_depth);
1516 make_old_stack_space (mw, new_depth);
1517 windows = mw->menu.windows;
1518 old_stack = mw->menu.old_stack;
1519 new_stack = mw->menu.new_stack;
1520
1521 /* compute the last identical different entry */
1522 for (i = 1; i < old_depth && i < new_depth; i++)
1523 if (old_stack [i] != new_stack [i])
1524 break;
1525 last_same = i - 1;
1526
1527 /* Memorize the previously selected item to be able to refresh it */
1528 old_selection = last_same + 1 < old_depth ? old_stack [last_same + 1] : NULL;
1529 if (old_selection && !old_selection->enabled)
1530 old_selection = NULL;
1531 new_selection = last_same + 1 < new_depth ? new_stack [last_same + 1] : NULL;
1532 if (new_selection && !new_selection->enabled)
1533 new_selection = NULL;
1534
1535 /* Call callback when the hightlighted item changes. */
1536 if (old_selection || new_selection)
1537 XtCallCallbackList ((Widget)mw, mw->menu.highlight,
1538 (XtPointer) new_selection);
1539
1540 /* updates old_state from new_state. It has to be done now because
1541 display_menu (called below) uses the old_stack to know what to display. */
1542 for (i = last_same + 1; i < new_depth; i++)
1543 {
1544 XtPopdown (mw->menu.windows [i].w);
1545 old_stack [i] = new_stack [i];
1546 }
1547 mw->menu.old_depth = new_depth;
1548
1549 /* refresh the last selection */
1550 selection_position.x = 0;
1551 selection_position.y = 0;
1552 display_menu (mw, last_same, new_selection == old_selection,
1553 &selection_position, NULL, NULL);
1554
1555 /* Now place the new menus. */
1556 for (i = last_same + 1; i < new_depth && new_stack[i]->contents; i++)
1557 {
1558 window_state *previous_ws = &windows[i - 1];
1559 window_state *ws = &windows[i];
1560
1561 ws->x = (previous_ws->x + selection_position.x
1562 + mw->menu.shadow_thickness);
1563 if (mw->menu.horizontal && i == 1)
1564 ws->x += mw->menu.margin;
1565
1566 #if 0
1567 if (!mw->menu.horizontal || i > 1)
1568 ws->x += mw->menu.shadow_thickness;
1569 #endif
1570
1571 ws->y = (previous_ws->y + selection_position.y
1572 + mw->menu.shadow_thickness);
1573 if (mw->menu.horizontal && i == 1)
1574 ws->y += mw->menu.margin;
1575
1576 size_menu (mw, i);
1577
1578 fit_to_screen (mw, ws, previous_ws, mw->menu.horizontal && i == 1);
1579
1580 XtVaSetValues (ws->w, XtNwidth, ws->width, XtNheight, ws->height,
1581 XtNx, ws->x, XtNy, ws->y, NULL);
1582 create_pixmap_for_menu (ws, mw);
1583 XtPopup (ws->w, XtGrabNone);
1584 display_menu (mw, i, False, &selection_position, NULL, NULL);
1585 }
1586
1587 /* unmap the menus that popped down */
1588 for (i = new_depth - 1; i < old_depth; i++)
1589 if (i >= new_depth || (i > 0 && !new_stack[i]->contents))
1590 XtPopdown (windows[i].w);
1591 }
1592
1593 static Boolean
1594 motion_event_is_in_menu (mw, ev, level, relative_pos)
1595 XlwMenuWidget mw;
1596 XMotionEvent* ev;
1597 int level;
1598 XPoint* relative_pos;
1599 {
1600 window_state* ws = &mw->menu.windows [level];
1601 int shadow = level == 0 ? 0 : mw->menu.shadow_thickness;
1602 int x = ws->x + shadow;
1603 int y = ws->y + shadow;
1604 relative_pos->x = ev->x_root - x;
1605 relative_pos->y = ev->y_root - y;
1606 return (x - shadow < ev->x_root && ev->x_root < x + ws->width
1607 && y - shadow < ev->y_root && ev->y_root < y + ws->height);
1608 }
1609
1610 static Boolean
1611 map_event_to_widget_value (mw, ev, val, level)
1612 XlwMenuWidget mw;
1613 XMotionEvent* ev;
1614 widget_value** val;
1615 int* level;
1616 {
1617 int i;
1618 XPoint relative_pos;
1619 window_state* ws;
1620 int inside = 0;
1621
1622 *val = NULL;
1623
1624 /* Find the window */
1625 for (i = mw->menu.old_depth - 1; i >= 0; i--)
1626 {
1627 ws = &mw->menu.windows [i];
1628 if (ws && motion_event_is_in_menu (mw, ev, i, &relative_pos))
1629 {
1630 inside = 1;
1631 display_menu (mw, i, True, NULL, &relative_pos, val);
1632
1633 if (*val)
1634 {
1635 *level = i + 1;
1636 return True;
1637 }
1638 }
1639 }
1640
1641 if (!inside)
1642 {
1643 if (mw->menu.inside_entry != NULL)
1644 XtCallCallbackList ((Widget)mw, mw->menu.leave,
1645 (XtPointer) mw->menu.inside_entry);
1646 mw->menu.inside_entry = NULL;
1647 }
1648
1649 return False;
1650 }
1651
1652 \f/* Procedures */
1653 static void
1654 make_drawing_gcs (mw)
1655 XlwMenuWidget mw;
1656 {
1657 XGCValues xgcv;
1658 float scale;
1659 XtGCMask mask = GCForeground | GCBackground;
1660
1661 #ifdef HAVE_X_I18N
1662 if (!mw->menu.fontSet)
1663 {
1664 xgcv.font = mw->menu.font->fid;
1665 mask |= GCFont;
1666 }
1667 #else
1668 xgcv.font = mw->menu.font->fid;
1669 mask |= GCFont;
1670 #endif
1671 xgcv.foreground = mw->menu.foreground;
1672 xgcv.background = mw->core.background_pixel;
1673 mw->menu.foreground_gc = XtGetGC ((Widget)mw, mask, &xgcv);
1674
1675 xgcv.foreground = mw->menu.button_foreground;
1676 mw->menu.button_gc = XtGetGC ((Widget)mw, mask, &xgcv);
1677
1678 xgcv.background = mw->core.background_pixel;
1679
1680 #define BRIGHTNESS(color) (((color) & 0xff) + (((color) >> 8) & 0xff) + (((color) >> 16) & 0xff))
1681
1682 /* Allocate color for disabled menu-items. */
1683 mw->menu.disabled_foreground = mw->menu.foreground;
1684 if (BRIGHTNESS(mw->menu.foreground) < BRIGHTNESS(mw->core.background_pixel))
1685 scale = 2.3;
1686 else
1687 scale = 0.55;
1688
1689 x_alloc_lighter_color_for_widget ((Widget) mw, XtDisplay ((Widget) mw),
1690 mw->core.colormap,
1691 &mw->menu.disabled_foreground,
1692 scale,
1693 0x8000);
1694
1695 if (mw->menu.foreground == mw->menu.disabled_foreground
1696 || mw->core.background_pixel == mw->menu.disabled_foreground)
1697 {
1698 /* Too few colors, use stipple. */
1699 xgcv.foreground = mw->menu.foreground;
1700 xgcv.fill_style = FillStippled;
1701 xgcv.stipple = mw->menu.gray_pixmap;
1702 mw->menu.disabled_gc = XtGetGC ((Widget)mw, mask
1703 | GCFillStyle | GCStipple, &xgcv);
1704 }
1705 else
1706 {
1707 /* Many colors available, use disabled pixel. */
1708 xgcv.foreground = mw->menu.disabled_foreground;
1709 mw->menu.disabled_gc = XtGetGC ((Widget)mw, mask, &xgcv);
1710 }
1711
1712 xgcv.foreground = mw->menu.button_foreground;
1713 xgcv.background = mw->core.background_pixel;
1714 xgcv.fill_style = FillStippled;
1715 xgcv.stipple = mw->menu.gray_pixmap;
1716 mw->menu.inactive_button_gc = XtGetGC ((Widget)mw, mask
1717 | GCFillStyle | GCStipple, &xgcv);
1718
1719 xgcv.foreground = mw->core.background_pixel;
1720 xgcv.background = mw->menu.foreground;
1721 mw->menu.background_gc = XtGetGC ((Widget)mw, mask, &xgcv);
1722 }
1723
1724 static void
1725 release_drawing_gcs (mw)
1726 XlwMenuWidget mw;
1727 {
1728 XtReleaseGC ((Widget) mw, mw->menu.foreground_gc);
1729 XtReleaseGC ((Widget) mw, mw->menu.button_gc);
1730 XtReleaseGC ((Widget) mw, mw->menu.disabled_gc);
1731 XtReleaseGC ((Widget) mw, mw->menu.inactive_button_gc);
1732 XtReleaseGC ((Widget) mw, mw->menu.background_gc);
1733 /* let's get some segvs if we try to use these... */
1734 mw->menu.foreground_gc = (GC) -1;
1735 mw->menu.button_gc = (GC) -1;
1736 mw->menu.disabled_gc = (GC) -1;
1737 mw->menu.inactive_button_gc = (GC) -1;
1738 mw->menu.background_gc = (GC) -1;
1739 }
1740
1741 #define MINL(x,y) ((((unsigned long) (x)) < ((unsigned long) (y))) \
1742 ? ((unsigned long) (x)) : ((unsigned long) (y)))
1743
1744 static void
1745 make_shadow_gcs (mw)
1746 XlwMenuWidget mw;
1747 {
1748 XGCValues xgcv;
1749 unsigned long pm = 0;
1750 Display *dpy = XtDisplay ((Widget) mw);
1751 Screen *screen = XtScreen ((Widget) mw);
1752 Colormap cmap = mw->core.colormap;
1753 XColor topc, botc;
1754 int top_frobbed = 0, bottom_frobbed = 0;
1755
1756 mw->menu.free_top_shadow_color_p = 0;
1757 mw->menu.free_bottom_shadow_color_p = 0;
1758
1759 if (mw->menu.top_shadow_color == -1)
1760 mw->menu.top_shadow_color = mw->core.background_pixel;
1761 else
1762 mw->menu.top_shadow_color = mw->menu.top_shadow_color;
1763
1764 if (mw->menu.bottom_shadow_color == -1)
1765 mw->menu.bottom_shadow_color = mw->menu.foreground;
1766 else
1767 mw->menu.bottom_shadow_color = mw->menu.bottom_shadow_color;
1768
1769 if (mw->menu.top_shadow_color == mw->core.background_pixel ||
1770 mw->menu.top_shadow_color == mw->menu.foreground)
1771 {
1772 topc.pixel = mw->core.background_pixel;
1773 #ifdef emacs
1774 if (x_alloc_lighter_color_for_widget ((Widget) mw, dpy, cmap,
1775 &topc.pixel,
1776 1.2, 0x8000))
1777 #else
1778 XQueryColor (dpy, cmap, &topc);
1779 /* don't overflow/wrap! */
1780 topc.red = MINL (65535, topc.red * 1.2);
1781 topc.green = MINL (65535, topc.green * 1.2);
1782 topc.blue = MINL (65535, topc.blue * 1.2);
1783 if (XAllocColor (dpy, cmap, &topc))
1784 #endif
1785 {
1786 mw->menu.top_shadow_color = topc.pixel;
1787 mw->menu.free_top_shadow_color_p = 1;
1788 top_frobbed = 1;
1789 }
1790 }
1791 if (mw->menu.bottom_shadow_color == mw->menu.foreground ||
1792 mw->menu.bottom_shadow_color == mw->core.background_pixel)
1793 {
1794 botc.pixel = mw->core.background_pixel;
1795 #ifdef emacs
1796 if (x_alloc_lighter_color_for_widget ((Widget) mw, dpy, cmap,
1797 &botc.pixel,
1798 0.6, 0x4000))
1799 #else
1800 XQueryColor (dpy, cmap, &botc);
1801 botc.red *= 0.6;
1802 botc.green *= 0.6;
1803 botc.blue *= 0.6;
1804 if (XAllocColor (dpy, cmap, &botc))
1805 #endif
1806 {
1807 mw->menu.bottom_shadow_color = botc.pixel;
1808 mw->menu.free_bottom_shadow_color_p = 1;
1809 bottom_frobbed = 1;
1810 }
1811 }
1812
1813 if (top_frobbed && bottom_frobbed)
1814 {
1815 if (topc.pixel == botc.pixel)
1816 {
1817 if (botc.pixel == mw->menu.foreground)
1818 {
1819 if (mw->menu.free_top_shadow_color_p)
1820 {
1821 x_free_dpy_colors (dpy, screen, cmap,
1822 &mw->menu.top_shadow_color, 1);
1823 mw->menu.free_top_shadow_color_p = 0;
1824 }
1825 mw->menu.top_shadow_color = mw->core.background_pixel;
1826 }
1827 else
1828 {
1829 if (mw->menu.free_bottom_shadow_color_p)
1830 {
1831 x_free_dpy_colors (dpy, screen, cmap,
1832 &mw->menu.bottom_shadow_color, 1);
1833 mw->menu.free_bottom_shadow_color_p = 0;
1834 }
1835 mw->menu.bottom_shadow_color = mw->menu.foreground;
1836 }
1837 }
1838 }
1839
1840 if (!mw->menu.top_shadow_pixmap &&
1841 mw->menu.top_shadow_color == mw->core.background_pixel)
1842 {
1843 mw->menu.top_shadow_pixmap = mw->menu.gray_pixmap;
1844 if (mw->menu.free_top_shadow_color_p)
1845 {
1846 x_free_dpy_colors (dpy, screen, cmap, &mw->menu.top_shadow_color, 1);
1847 mw->menu.free_top_shadow_color_p = 0;
1848 }
1849 mw->menu.top_shadow_color = mw->menu.foreground;
1850 }
1851 if (!mw->menu.bottom_shadow_pixmap &&
1852 mw->menu.bottom_shadow_color == mw->core.background_pixel)
1853 {
1854 mw->menu.bottom_shadow_pixmap = mw->menu.gray_pixmap;
1855 if (mw->menu.free_bottom_shadow_color_p)
1856 {
1857 x_free_dpy_colors (dpy, screen, cmap,
1858 &mw->menu.bottom_shadow_color, 1);
1859 mw->menu.free_bottom_shadow_color_p = 0;
1860 }
1861 mw->menu.bottom_shadow_color = mw->menu.foreground;
1862 }
1863
1864 xgcv.fill_style = FillStippled;
1865 xgcv.foreground = mw->menu.top_shadow_color;
1866 xgcv.stipple = mw->menu.top_shadow_pixmap;
1867 pm = (xgcv.stipple ? GCStipple|GCFillStyle : 0);
1868 mw->menu.shadow_top_gc = XtGetGC ((Widget)mw, GCForeground | pm, &xgcv);
1869
1870 xgcv.foreground = mw->menu.bottom_shadow_color;
1871 xgcv.stipple = mw->menu.bottom_shadow_pixmap;
1872 pm = (xgcv.stipple ? GCStipple|GCFillStyle : 0);
1873 mw->menu.shadow_bottom_gc = XtGetGC ((Widget)mw, GCForeground | pm, &xgcv);
1874 }
1875
1876
1877 static void
1878 release_shadow_gcs (mw)
1879 XlwMenuWidget mw;
1880 {
1881 Display *dpy = XtDisplay ((Widget) mw);
1882 Screen *screen = XtScreen ((Widget) mw);
1883 Colormap cmap = mw->core.colormap;
1884 Pixel px[2];
1885 int i = 0;
1886
1887 if (mw->menu.free_top_shadow_color_p)
1888 px[i++] = mw->menu.top_shadow_color;
1889 if (mw->menu.free_bottom_shadow_color_p)
1890 px[i++] = mw->menu.bottom_shadow_color;
1891 if (i > 0)
1892 x_free_dpy_colors (dpy, screen, cmap, px, i);
1893
1894 XtReleaseGC ((Widget) mw, mw->menu.shadow_top_gc);
1895 XtReleaseGC ((Widget) mw, mw->menu.shadow_bottom_gc);
1896 }
1897
1898 #ifdef HAVE_XFT
1899 static int
1900 openXftFont (mw)
1901 XlwMenuWidget mw;
1902 {
1903 char *fname = mw->menu.faceName;
1904
1905 mw->menu.xft_font = 0;
1906 mw->menu.default_face = fname && strcmp (fname, DEFAULT_FACENAME) == 0;
1907
1908 if (fname && strcmp (fname, "none") != 0)
1909 {
1910 int screen = XScreenNumberOfScreen (mw->core.screen);
1911 int len = strlen (fname), i = len-1;
1912 /* Try to convert Gtk-syntax (Sans 9) to Xft syntax Sans-9. */
1913 while (i > 0 && isdigit (fname[i]))
1914 --i;
1915 if (fname[i] == ' ')
1916 {
1917 fname = xstrdup (mw->menu.faceName);
1918 fname[i] = '-';
1919 }
1920
1921 mw->menu.xft_font = XftFontOpenName (XtDisplay (mw), screen, fname);
1922 if (!mw->menu.xft_font)
1923 {
1924 fprintf (stderr, "Can't find font '%s'\n", fname);
1925 mw->menu.xft_font = XftFontOpenName (XtDisplay (mw), screen,
1926 DEFAULT_FACENAME);
1927 }
1928 }
1929
1930 if (fname != mw->menu.faceName) free (fname);
1931
1932 return mw->menu.xft_font != 0;
1933 }
1934 #endif
1935
1936 static void
1937 XlwMenuInitialize (request, mw, args, num_args)
1938 Widget request;
1939 XlwMenuWidget mw;
1940 ArgList args;
1941 Cardinal *num_args;
1942 {
1943 /* Get the GCs and the widget size */
1944
1945 Window window = RootWindowOfScreen (DefaultScreenOfDisplay (XtDisplay (mw)));
1946 Display* display = XtDisplay (mw);
1947
1948 #if 0
1949 widget_value *tem = (widget_value *) XtMalloc (sizeof (widget_value));
1950
1951 /* _XtCreate is freeing the object that was passed to us,
1952 so make a copy that we will actually keep. */
1953 lwlib_bcopy (mw->menu.contents, tem, sizeof (widget_value));
1954 mw->menu.contents = tem;
1955 #endif
1956
1957 /* mw->menu.cursor = XCreateFontCursor (display, mw->menu.cursor_shape); */
1958 mw->menu.cursor = mw->menu.cursor_shape;
1959
1960 mw->menu.gray_pixmap
1961 = XCreatePixmapFromBitmapData (display, window, gray_bitmap_bits,
1962 gray_bitmap_width, gray_bitmap_height,
1963 (unsigned long)1, (unsigned long)0, 1);
1964
1965 #ifdef HAVE_XFT
1966 if (openXftFont (mw))
1967 ;
1968 else
1969 #endif
1970
1971 if (!mw->menu.font)
1972 {
1973 if (!xlwmenu_default_font)
1974 xlwmenu_default_font = XLoadQueryFont (display, "fixed");
1975 mw->menu.font = xlwmenu_default_font;
1976 if (!mw->menu.font)
1977 {
1978 fprintf (stderr, "Menu font fixed not found, can't continue.\n");
1979 abort ();
1980 }
1981 }
1982
1983 #ifdef HAVE_X_I18N
1984 if (mw->menu.fontSet)
1985 mw->menu.font_extents = XExtentsOfFontSet (mw->menu.fontSet);
1986 #endif
1987
1988 make_drawing_gcs (mw);
1989 make_shadow_gcs (mw);
1990
1991 mw->menu.popped_up = False;
1992
1993 mw->menu.old_depth = 1;
1994 mw->menu.old_stack = (widget_value**)XtMalloc (sizeof (widget_value*));
1995 mw->menu.old_stack_length = 1;
1996 mw->menu.old_stack [0] = mw->menu.contents;
1997
1998 mw->menu.new_depth = 0;
1999 mw->menu.new_stack = 0;
2000 mw->menu.new_stack_length = 0;
2001 push_new_stack (mw, mw->menu.contents);
2002
2003 mw->menu.windows = (window_state*)XtMalloc (sizeof (window_state));
2004 mw->menu.windows_length = 1;
2005 mw->menu.windows [0].x = 0;
2006 mw->menu.windows [0].y = 0;
2007 mw->menu.windows [0].width = 0;
2008 mw->menu.windows [0].height = 0;
2009 mw->menu.windows [0].max_rest_width = 0;
2010 mw->menu.windows [0].pixmap = None;
2011 #ifdef HAVE_XFT
2012 mw->menu.windows [0].xft_draw = 0;
2013 #endif
2014 size_menu (mw, 0);
2015
2016 mw->core.width = mw->menu.windows [0].width;
2017 mw->core.height = mw->menu.windows [0].height;
2018 }
2019
2020 static void
2021 XlwMenuClassInitialize ()
2022 {
2023 xlwmenu_default_font = 0;
2024 }
2025
2026 static void
2027 XlwMenuRealize (w, valueMask, attributes)
2028 Widget w;
2029 Mask *valueMask;
2030 XSetWindowAttributes *attributes;
2031 {
2032 XlwMenuWidget mw = (XlwMenuWidget)w;
2033 XSetWindowAttributes xswa;
2034 int mask;
2035
2036 (*xlwMenuWidgetClass->core_class.superclass->core_class.realize)
2037 (w, valueMask, attributes);
2038
2039 xswa.save_under = True;
2040 xswa.cursor = mw->menu.cursor_shape;
2041 mask = CWSaveUnder | CWCursor;
2042 /* I sometimes get random BadCursor errors while creating the first
2043 frame on a display. I can not find their reason, but they are
2044 annoying so for now let's ignore any errors here. -- lorentey */
2045 #ifdef emacs
2046 x_catch_errors (XtDisplay (w));
2047 #endif
2048 XChangeWindowAttributes (XtDisplay (w), XtWindow (w), mask, &xswa);
2049 #ifdef emacs
2050 x_uncatch_errors ();
2051 #endif
2052
2053 mw->menu.windows [0].w = w;
2054 mw->menu.windows [0].window = XtWindow (w);
2055 mw->menu.windows [0].x = w->core.x;
2056 mw->menu.windows [0].y = w->core.y;
2057 mw->menu.windows [0].width = w->core.width;
2058 mw->menu.windows [0].height = w->core.height;
2059
2060 set_window_type (mw->menu.windows [0].w, mw);
2061 create_pixmap_for_menu (&mw->menu.windows [0], mw);
2062
2063 #ifdef HAVE_XFT
2064 if (mw->menu.xft_font)
2065 {
2066 XColor colors[3];
2067 colors[0].pixel = mw->menu.xft_fg.pixel = mw->menu.foreground;
2068 colors[1].pixel = mw->menu.xft_bg.pixel = mw->core.background_pixel;
2069 colors[2].pixel = mw->menu.xft_disabled_fg.pixel
2070 = mw->menu.disabled_foreground;
2071 XQueryColors (XtDisplay (mw), mw->core.colormap, colors, 3);
2072 mw->menu.xft_fg.color.alpha = 0xFFFF;
2073 mw->menu.xft_fg.color.red = colors[0].red;
2074 mw->menu.xft_fg.color.green = colors[0].green;
2075 mw->menu.xft_fg.color.blue = colors[0].blue;
2076 mw->menu.xft_bg.color.alpha = 0xFFFF;
2077 mw->menu.xft_bg.color.red = colors[1].red;
2078 mw->menu.xft_bg.color.green = colors[1].green;
2079 mw->menu.xft_bg.color.blue = colors[1].blue;
2080 mw->menu.xft_disabled_fg.color.alpha = 0xFFFF;
2081 mw->menu.xft_disabled_fg.color.red = colors[2].red;
2082 mw->menu.xft_disabled_fg.color.green = colors[2].green;
2083 mw->menu.xft_disabled_fg.color.blue = colors[2].blue;
2084 }
2085 #endif
2086 }
2087
2088 /* Only the toplevel menubar/popup is a widget so it's the only one that
2089 receives expose events through Xt. So we repaint all the other panes
2090 when receiving an Expose event. */
2091 static void
2092 XlwMenuRedisplay (w, ev, region)
2093 Widget w;
2094 XEvent* ev;
2095 Region region;
2096 {
2097 XlwMenuWidget mw = (XlwMenuWidget)w;
2098 int i;
2099
2100 /* If we have a depth beyond 1, it's because a submenu was displayed.
2101 If the submenu has been destroyed, set the depth back to 1. */
2102 if (submenu_destroyed)
2103 {
2104 mw->menu.old_depth = 1;
2105 submenu_destroyed = 0;
2106 }
2107
2108 display_menu (mw, 0, False, NULL, NULL, NULL);
2109 }
2110
2111
2112 /* Part of a hack to make the menu redisplay when a tooltip frame
2113 over a menu item is unmapped. */
2114
2115 void
2116 xlwmenu_redisplay (w)
2117 Widget w;
2118 {
2119 XlwMenuRedisplay (w, NULL, None);
2120 }
2121
2122 static void
2123 XlwMenuDestroy (w)
2124 Widget w;
2125 {
2126 int i;
2127 XlwMenuWidget mw = (XlwMenuWidget) w;
2128
2129 if (pointer_grabbed)
2130 ungrab_all ((Widget)w, CurrentTime);
2131 pointer_grabbed = 0;
2132
2133 submenu_destroyed = 1;
2134
2135 release_drawing_gcs (mw);
2136 release_shadow_gcs (mw);
2137
2138 /* this doesn't come from the resource db but is created explicitly
2139 so we must free it ourselves. */
2140 XFreePixmap (XtDisplay (mw), mw->menu.gray_pixmap);
2141 mw->menu.gray_pixmap = (Pixmap) -1;
2142
2143 #if 0
2144 /* Do free mw->menu.contents because nowadays we copy it
2145 during initialization. */
2146 XtFree (mw->menu.contents);
2147 #endif
2148
2149 /* Don't free mw->menu.contents because that comes from our creator.
2150 The `*_stack' elements are just pointers into `contents' so leave
2151 that alone too. But free the stacks themselves. */
2152 if (mw->menu.old_stack) XtFree ((char *) mw->menu.old_stack);
2153 if (mw->menu.new_stack) XtFree ((char *) mw->menu.new_stack);
2154
2155 /* Remember, you can't free anything that came from the resource
2156 database. This includes:
2157 mw->menu.cursor
2158 mw->menu.top_shadow_pixmap
2159 mw->menu.bottom_shadow_pixmap
2160 mw->menu.font
2161 Also the color cells of top_shadow_color, bottom_shadow_color,
2162 foreground, and button_foreground will never be freed until this
2163 client exits. Nice, eh?
2164 */
2165
2166 #ifdef HAVE_XFT
2167 if (mw->menu.windows [0].xft_draw)
2168 XftDrawDestroy (mw->menu.windows [0].xft_draw);
2169 if (mw->menu.xft_font)
2170 XftFontClose (XtDisplay (mw), mw->menu.xft_font);
2171 #endif
2172
2173 if (mw->menu.windows [0].pixmap != None)
2174 XFreePixmap (XtDisplay (mw), mw->menu.windows [0].pixmap);
2175 /* start from 1 because the one in slot 0 is w->core.window */
2176 for (i = 1; i < mw->menu.windows_length; i++)
2177 {
2178 if (mw->menu.windows [i].pixmap != None)
2179 XFreePixmap (XtDisplay (mw), mw->menu.windows [i].pixmap);
2180 #ifdef HAVE_XFT
2181 if (mw->menu.windows [i].xft_draw)
2182 XftDrawDestroy (mw->menu.windows [i].xft_draw);
2183 #endif
2184 }
2185
2186 if (mw->menu.windows)
2187 XtFree ((char *) mw->menu.windows);
2188 }
2189
2190 #ifdef HAVE_XFT
2191 static int
2192 facename_changed (XlwMenuWidget newmw,
2193 XlwMenuWidget oldmw)
2194 {
2195 /* This will fore a new XftFont even if the same string is set.
2196 This is good, as rendering parameters may have changed and
2197 we just want to do a redisplay. */
2198 return newmw->menu.faceName != oldmw->menu.faceName;
2199 }
2200 #endif
2201
2202 static Boolean
2203 XlwMenuSetValues (current, request, new)
2204 Widget current;
2205 Widget request;
2206 Widget new;
2207 {
2208 XlwMenuWidget oldmw = (XlwMenuWidget)current;
2209 XlwMenuWidget newmw = (XlwMenuWidget)new;
2210 Boolean redisplay = False;
2211 int i;
2212
2213 if (newmw->menu.contents
2214 && newmw->menu.contents->contents
2215 && newmw->menu.contents->contents->change >= VISIBLE_CHANGE)
2216 redisplay = True;
2217 /* Do redisplay if the contents are entirely eliminated. */
2218 if (newmw->menu.contents
2219 && newmw->menu.contents->contents == 0
2220 && newmw->menu.contents->change >= VISIBLE_CHANGE)
2221 redisplay = True;
2222
2223 if (newmw->core.background_pixel != oldmw->core.background_pixel
2224 || newmw->menu.foreground != oldmw->menu.foreground
2225 #ifdef HAVE_XFT
2226 || facename_changed (newmw, oldmw)
2227 #endif
2228 #ifdef HAVE_X_I18N
2229 || newmw->menu.fontSet != oldmw->menu.fontSet
2230 || (newmw->menu.fontSet == NULL && newmw->menu.font != oldmw->menu.font)
2231 #else
2232 || newmw->menu.font != oldmw->menu.font
2233 #endif
2234 )
2235 {
2236 release_drawing_gcs (newmw);
2237 make_drawing_gcs (newmw);
2238
2239 release_shadow_gcs (newmw);
2240 /* Cause the shadow colors to be recalculated. */
2241 newmw->menu.top_shadow_color = -1;
2242 newmw->menu.bottom_shadow_color = -1;
2243 make_shadow_gcs (newmw);
2244
2245 redisplay = True;
2246
2247 if (XtIsRealized (current))
2248 /* If the menu is currently displayed, change the display. */
2249 for (i = 0; i < oldmw->menu.windows_length; i++)
2250 {
2251 XSetWindowBackground (XtDisplay (oldmw),
2252 oldmw->menu.windows [i].window,
2253 newmw->core.background_pixel);
2254 /* clear windows and generate expose events */
2255 XClearArea (XtDisplay (oldmw), oldmw->menu.windows[i].window,
2256 0, 0, 0, 0, True);
2257 }
2258 }
2259
2260 #ifdef HAVE_XFT
2261 if (facename_changed (newmw, oldmw))
2262 {
2263 int i;
2264 int screen = XScreenNumberOfScreen (newmw->core.screen);
2265 if (newmw->menu.xft_font)
2266 XftFontClose (XtDisplay (newmw), newmw->menu.xft_font);
2267 openXftFont (newmw);
2268 for (i = 0; i < newmw->menu.windows_length; i++)
2269 {
2270 if (newmw->menu.windows [i].xft_draw)
2271 XftDrawDestroy (newmw->menu.windows [i].xft_draw);
2272 newmw->menu.windows [i].xft_draw = 0;
2273 }
2274 if (newmw->menu.xft_font)
2275 for (i = 0; i < newmw->menu.windows_length; i++)
2276 newmw->menu.windows [i].xft_draw
2277 = XftDrawCreate (XtDisplay (newmw),
2278 newmw->menu.windows [i].window,
2279 DefaultVisual (XtDisplay (newmw), screen),
2280 newmw->core.colormap);
2281 }
2282 #endif
2283 #ifdef HAVE_X_I18N
2284 if (newmw->menu.fontSet != oldmw->menu.fontSet && newmw->menu.fontSet != NULL)
2285 {
2286 redisplay = True;
2287 newmw->menu.font_extents = XExtentsOfFontSet (newmw->menu.fontSet);
2288 }
2289 #endif
2290
2291 return redisplay;
2292 }
2293
2294 static void
2295 XlwMenuResize (w)
2296 Widget w;
2297 {
2298 XlwMenuWidget mw = (XlwMenuWidget)w;
2299
2300 if (mw->menu.popped_up)
2301 {
2302 /* Don't allow the popup menu to resize itself. */
2303 mw->core.width = mw->menu.windows [0].width;
2304 mw->core.height = mw->menu.windows [0].height;
2305 mw->core.parent->core.width = mw->core.width;
2306 mw->core.parent->core.height = mw->core.height;
2307 }
2308 else
2309 {
2310 mw->menu.windows [0].width = mw->core.width;
2311 mw->menu.windows [0].height = mw->core.height;
2312 create_pixmap_for_menu (&mw->menu.windows [0], mw);
2313 }
2314 }
2315
2316 \f/* Action procedures */
2317 static void
2318 handle_single_motion_event (mw, ev)
2319 XlwMenuWidget mw;
2320 XMotionEvent* ev;
2321 {
2322 widget_value* val;
2323 int level;
2324
2325 if (!map_event_to_widget_value (mw, ev, &val, &level))
2326 pop_new_stack_if_no_contents (mw);
2327 else
2328 set_new_state (mw, val, level);
2329 remap_menubar (mw);
2330
2331 /* Sync with the display. Makes it feel better on X terms. */
2332 XSync (XtDisplay (mw), False);
2333 }
2334
2335 static void
2336 handle_motion_event (mw, ev)
2337 XlwMenuWidget mw;
2338 XMotionEvent* ev;
2339 {
2340 int x = ev->x_root;
2341 int y = ev->y_root;
2342 int state = ev->state;
2343 XMotionEvent oldev = *ev;
2344
2345 /* allow motion events to be generated again */
2346 if (ev->is_hint
2347 && XQueryPointer (XtDisplay (mw), ev->window,
2348 &ev->root, &ev->subwindow,
2349 &ev->x_root, &ev->y_root,
2350 &ev->x, &ev->y,
2351 &ev->state)
2352 && ev->state == state
2353 && (ev->x_root != x || ev->y_root != y))
2354 handle_single_motion_event (mw, ev);
2355 else
2356 handle_single_motion_event (mw, &oldev);
2357 }
2358
2359 static void
2360 Start (w, ev, params, num_params)
2361 Widget w;
2362 XEvent *ev;
2363 String *params;
2364 Cardinal *num_params;
2365 {
2366 XlwMenuWidget mw = (XlwMenuWidget)w;
2367
2368 if (!mw->menu.popped_up)
2369 {
2370 menu_post_event = *ev;
2371 /* If event is set to CurrentTime, get the last known time stamp.
2372 This is for calculating if (popup) menus should stay up after
2373 a fast click. */
2374 if (menu_post_event.xbutton.time == CurrentTime)
2375 menu_post_event.xbutton.time
2376 = XtLastTimestampProcessed (XtDisplay (w));
2377
2378 pop_up_menu (mw, (XButtonPressedEvent*) ev);
2379 }
2380 else
2381 {
2382 /* If we push a button while the menu is posted semipermanently,
2383 releasing the button should always pop the menu down. */
2384 next_release_must_exit = 1;
2385
2386 /* notes the absolute position of the menubar window */
2387 mw->menu.windows [0].x = ev->xmotion.x_root - ev->xmotion.x;
2388 mw->menu.windows [0].y = ev->xmotion.y_root - ev->xmotion.y;
2389
2390 /* handles the down like a move, slots are compatible */
2391 ev->xmotion.is_hint = 0;
2392 handle_motion_event (mw, &ev->xmotion);
2393 }
2394 }
2395
2396 static void
2397 Drag (w, ev, params, num_params)
2398 Widget w;
2399 XEvent *ev;
2400 String *params;
2401 Cardinal *num_params;
2402 {
2403 XlwMenuWidget mw = (XlwMenuWidget)w;
2404 if (mw->menu.popped_up)
2405 handle_motion_event (mw, &ev->xmotion);
2406 }
2407
2408 /* Do nothing.
2409 This is how we handle presses and releases of modifier keys. */
2410 static void
2411 Nothing (w, ev, params, num_params)
2412 Widget w;
2413 XEvent *ev;
2414 String *params;
2415 Cardinal *num_params;
2416 {
2417 }
2418
2419 static widget_value *
2420 find_first_selectable (mw, item, skip_titles)
2421 XlwMenuWidget mw;
2422 widget_value *item;
2423 int skip_titles;
2424 {
2425 widget_value *current = item;
2426 enum menu_separator separator;
2427
2428 while (lw_separator_p (current->name, &separator, 0) || !current->enabled
2429 || (skip_titles && !current->call_data && !current->contents))
2430 if (current->next)
2431 current=current->next;
2432 else
2433 return NULL;
2434
2435 return current;
2436 }
2437
2438 static widget_value *
2439 find_next_selectable (mw, item, skip_titles)
2440 XlwMenuWidget mw;
2441 widget_value *item;
2442 int skip_titles;
2443 {
2444 widget_value *current = item;
2445 enum menu_separator separator;
2446
2447 while (current->next && (current=current->next) &&
2448 (lw_separator_p (current->name, &separator, 0) || !current->enabled
2449 || (skip_titles && !current->call_data && !current->contents)))
2450 ;
2451
2452 if (current == item)
2453 {
2454 if (mw->menu.old_depth < 2)
2455 return current;
2456 current = mw->menu.old_stack [mw->menu.old_depth - 2]->contents;
2457
2458 while (lw_separator_p (current->name, &separator, 0)
2459 || !current->enabled
2460 || (skip_titles && !current->call_data
2461 && !current->contents))
2462 {
2463 if (current->next)
2464 current=current->next;
2465
2466 if (current == item)
2467 break;
2468 }
2469
2470 }
2471
2472 return current;
2473 }
2474
2475 static widget_value *
2476 find_prev_selectable (mw, item, skip_titles)
2477 XlwMenuWidget mw;
2478 widget_value *item;
2479 int skip_titles;
2480 {
2481 widget_value *current = item;
2482 widget_value *prev = item;
2483
2484 while ((current=find_next_selectable (mw, current, skip_titles))
2485 != item)
2486 {
2487 if (prev == current)
2488 break;
2489 prev=current;
2490 }
2491
2492 return prev;
2493 }
2494
2495 static void
2496 Down (w, ev, params, num_params)
2497 Widget w;
2498 XEvent *ev;
2499 String *params;
2500 Cardinal *num_params;
2501 {
2502 XlwMenuWidget mw = (XlwMenuWidget) w;
2503 widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2504 int popup_menu_p = mw->menu.top_depth == 1;
2505
2506 /* Inside top-level menu-bar? */
2507 if (mw->menu.old_depth == mw->menu.top_depth)
2508 /* When <down> in the menu-bar is pressed, display the corresponding
2509 sub-menu and select the first selectable menu item there.
2510 If this is a popup menu, skip title item of the popup. */
2511 set_new_state (mw,
2512 find_first_selectable (mw,
2513 selected_item->contents,
2514 popup_menu_p),
2515 mw->menu.old_depth);
2516 else
2517 /* Highlight next possible (enabled and not separator) menu item. */
2518 set_new_state (mw, find_next_selectable (mw, selected_item, popup_menu_p),
2519 mw->menu.old_depth - 1);
2520
2521 remap_menubar (mw);
2522 }
2523
2524 static void
2525 Up (w, ev, params, num_params)
2526 Widget w;
2527 XEvent *ev;
2528 String *params;
2529 Cardinal *num_params;
2530 {
2531 XlwMenuWidget mw = (XlwMenuWidget) w;
2532 widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2533 int popup_menu_p = mw->menu.top_depth == 1;
2534
2535 /* Inside top-level menu-bar? */
2536 if (mw->menu.old_depth == mw->menu.top_depth)
2537 {
2538 /* FIXME: this is tricky. <up> in the menu-bar should select the
2539 last selectable item in the list. So we select the first
2540 selectable one and find the previous selectable item. Is there
2541 a better way? */
2542 /* If this is a popup menu, skip title item of the popup. */
2543 set_new_state (mw,
2544 find_first_selectable (mw,
2545 selected_item->contents,
2546 popup_menu_p),
2547 mw->menu.old_depth);
2548 remap_menubar (mw);
2549 selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2550 set_new_state (mw,
2551 find_prev_selectable (mw,
2552 selected_item,
2553 popup_menu_p),
2554 mw->menu.old_depth - 1);
2555 }
2556 else
2557 /* Highlight previous (enabled and not separator) menu item. */
2558 set_new_state (mw, find_prev_selectable (mw, selected_item, popup_menu_p),
2559 mw->menu.old_depth - 1);
2560
2561 remap_menubar (mw);
2562 }
2563
2564 void
2565 Left (w, ev, params, num_params)
2566 Widget w;
2567 XEvent *ev;
2568 String *params;
2569 Cardinal *num_params;
2570 {
2571 XlwMenuWidget mw = (XlwMenuWidget) w;
2572 widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2573
2574 /* Inside top-level menu-bar? */
2575 if (mw->menu.old_depth == mw->menu.top_depth)
2576 /* When <left> in the menu-bar is pressed, display the previous item on
2577 the menu-bar. If the current item is the first one, highlight the
2578 last item in the menubar (probably Help). */
2579 set_new_state (mw, find_prev_selectable (mw, selected_item, 0),
2580 mw->menu.old_depth - 1);
2581 else if (mw->menu.old_depth == 1
2582 && selected_item->contents) /* Is this menu item expandable? */
2583 {
2584 set_new_state (mw, selected_item->contents, mw->menu.old_depth);
2585 remap_menubar (mw);
2586 selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2587 if (!selected_item->enabled && find_first_selectable (mw,
2588 selected_item,
2589 0))
2590 set_new_state (mw, find_first_selectable (mw, selected_item, 0),
2591 mw->menu.old_depth - 1);
2592 }
2593
2594 else
2595 {
2596 pop_new_stack_if_no_contents (mw);
2597 set_new_state (mw, mw->menu.old_stack [mw->menu.old_depth - 2],
2598 mw->menu.old_depth - 2);
2599 }
2600
2601 remap_menubar (mw);
2602 }
2603
2604 void
2605 Right (w, ev, params, num_params)
2606 Widget w;
2607 XEvent *ev;
2608 String *params;
2609 Cardinal *num_params;
2610 {
2611 XlwMenuWidget mw = (XlwMenuWidget) w;
2612 widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2613
2614 /* Inside top-level menu-bar? */
2615 if (mw->menu.old_depth == mw->menu.top_depth)
2616 /* When <right> in the menu-bar is pressed, display the next item on
2617 the menu-bar. If the current item is the last one, highlight the
2618 first item (probably File). */
2619 set_new_state (mw, find_next_selectable (mw, selected_item, 0),
2620 mw->menu.old_depth - 1);
2621 else if (selected_item->contents) /* Is this menu item expandable? */
2622 {
2623 set_new_state (mw, selected_item->contents, mw->menu.old_depth);
2624 remap_menubar (mw);
2625 selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2626 if (!selected_item->enabled && find_first_selectable (mw,
2627 selected_item,
2628 0))
2629 set_new_state (mw, find_first_selectable (mw, selected_item, 0),
2630 mw->menu.old_depth - 1);
2631 }
2632 else
2633 {
2634 pop_new_stack_if_no_contents (mw);
2635 set_new_state (mw, mw->menu.old_stack [mw->menu.old_depth - 2],
2636 mw->menu.old_depth - 2);
2637 }
2638
2639 remap_menubar (mw);
2640 }
2641
2642 /* Handle key press and release events while menu is popped up.
2643 Our action is to get rid of the menu. */
2644 static void
2645 Key (w, ev, params, num_params)
2646 Widget w;
2647 XEvent *ev;
2648 String *params;
2649 Cardinal *num_params;
2650 {
2651 XlwMenuWidget mw = (XlwMenuWidget)w;
2652
2653 /* Pop down everything. */
2654 mw->menu.new_depth = 1;
2655 remap_menubar (mw);
2656
2657 if (mw->menu.popped_up)
2658 {
2659 mw->menu.popped_up = False;
2660 ungrab_all ((Widget)mw, ev->xmotion.time);
2661 if (XtIsShell (XtParent ((Widget) mw)))
2662 XtPopdown (XtParent ((Widget) mw));
2663 else
2664 {
2665 XtRemoveGrab ((Widget) mw);
2666 display_menu (mw, 0, False, NULL, NULL, NULL);
2667 }
2668 }
2669
2670 /* callback */
2671 XtCallCallbackList ((Widget)mw, mw->menu.select, (XtPointer)0);
2672 }
2673
2674 static void
2675 Select (w, ev, params, num_params)
2676 Widget w;
2677 XEvent *ev;
2678 String *params;
2679 Cardinal *num_params;
2680 {
2681 XlwMenuWidget mw = (XlwMenuWidget)w;
2682 widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2683
2684 /* If user releases the button quickly, without selecting anything,
2685 after the initial down-click that brought the menu up,
2686 do nothing. */
2687 if ((selected_item == 0
2688 || ((widget_value *) selected_item)->call_data == 0)
2689 && !next_release_must_exit
2690 && (ev->xbutton.time - menu_post_event.xbutton.time
2691 < XtGetMultiClickTime (XtDisplay (w))))
2692 return;
2693
2694 /* pop down everything. */
2695 mw->menu.new_depth = 1;
2696 remap_menubar (mw);
2697
2698 if (mw->menu.popped_up)
2699 {
2700 mw->menu.popped_up = False;
2701 ungrab_all ((Widget)mw, ev->xmotion.time);
2702 if (XtIsShell (XtParent ((Widget) mw)))
2703 XtPopdown (XtParent ((Widget) mw));
2704 else
2705 {
2706 XtRemoveGrab ((Widget) mw);
2707 display_menu (mw, 0, False, NULL, NULL, NULL);
2708 }
2709 }
2710
2711 /* callback */
2712 XtCallCallbackList ((Widget)mw, mw->menu.select, (XtPointer)selected_item);
2713 }
2714
2715
2716 \f/* Special code to pop-up a menu */
2717 static void
2718 pop_up_menu (mw, event)
2719 XlwMenuWidget mw;
2720 XButtonPressedEvent* event;
2721 {
2722 int x = event->x_root;
2723 int y = event->y_root;
2724 int w;
2725 int h;
2726 int borderwidth = mw->menu.shadow_thickness;
2727 Screen* screen = XtScreen (mw);
2728 Display *display = XtDisplay (mw);
2729
2730 next_release_must_exit = 0;
2731
2732 mw->menu.inside_entry = NULL;
2733 XtCallCallbackList ((Widget)mw, mw->menu.open, NULL);
2734
2735 if (XtIsShell (XtParent ((Widget)mw)))
2736 size_menu (mw, 0);
2737
2738 w = mw->menu.windows [0].width;
2739 h = mw->menu.windows [0].height;
2740
2741 x -= borderwidth;
2742 y -= borderwidth;
2743 if (x < borderwidth)
2744 x = borderwidth;
2745 if (x + w + 2 * borderwidth > WidthOfScreen (screen))
2746 x = WidthOfScreen (screen) - w - 2 * borderwidth;
2747 if (y < borderwidth)
2748 y = borderwidth;
2749 if (y + h + 2 * borderwidth> HeightOfScreen (screen))
2750 y = HeightOfScreen (screen) - h - 2 * borderwidth;
2751
2752 mw->menu.popped_up = True;
2753 if (XtIsShell (XtParent ((Widget)mw)))
2754 {
2755 XtConfigureWidget (XtParent ((Widget)mw), x, y, w, h,
2756 XtParent ((Widget)mw)->core.border_width);
2757 XtPopup (XtParent ((Widget)mw), XtGrabExclusive);
2758 display_menu (mw, 0, False, NULL, NULL, NULL);
2759 mw->menu.windows [0].x = x + borderwidth;
2760 mw->menu.windows [0].y = y + borderwidth;
2761 mw->menu.top_depth = 1; /* Popup menus don't have a bar so top is 1 */
2762 }
2763 else
2764 {
2765 XEvent *ev = (XEvent *) event;
2766
2767 XtAddGrab ((Widget) mw, True, True);
2768
2769 /* notes the absolute position of the menubar window */
2770 mw->menu.windows [0].x = ev->xmotion.x_root - ev->xmotion.x;
2771 mw->menu.windows [0].y = ev->xmotion.y_root - ev->xmotion.y;
2772 mw->menu.top_depth = 2;
2773 }
2774
2775 #ifdef emacs
2776 x_catch_errors (display);
2777 #endif
2778 if (XtGrabPointer ((Widget)mw, False,
2779 (PointerMotionMask
2780 | PointerMotionHintMask
2781 | ButtonReleaseMask
2782 | ButtonPressMask),
2783 GrabModeAsync, GrabModeAsync, None,
2784 mw->menu.cursor_shape,
2785 event->time) == Success)
2786 {
2787 if (! GRAB_KEYBOARD
2788 || XtGrabKeyboard ((Widget)mw, False, GrabModeAsync,
2789 GrabModeAsync, event->time) == Success)
2790 {
2791 XtSetKeyboardFocus((Widget)mw, None);
2792 pointer_grabbed = 1;
2793 }
2794 else
2795 XtUngrabPointer ((Widget)mw, event->time);
2796 }
2797
2798 #ifdef emacs
2799 if (x_had_errors_p (display))
2800 {
2801 pointer_grabbed = 0;
2802 XtUngrabPointer ((Widget)mw, event->time);
2803 }
2804 x_uncatch_errors ();
2805 #endif
2806
2807 ((XMotionEvent*)event)->is_hint = 0;
2808 handle_motion_event (mw, (XMotionEvent*)event);
2809 }
2810
2811 /* arch-tag: 657f43dd-dfd0-4cc9-910c-52935f01176e
2812 (do not change this comment) */