Set _NET_WM_WINDOW_TYPE in menus. Looks bad with compiz otherwise.
[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 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 XtDestroyWidget (mw->menu.windows [i].w);
2181 #ifdef HAVE_XFT
2182 if (mw->menu.windows [i].xft_draw)
2183 XftDrawDestroy (mw->menu.windows [i].xft_draw);
2184 #endif
2185 }
2186
2187 if (mw->menu.windows)
2188 XtFree ((char *) mw->menu.windows);
2189 }
2190
2191 #ifdef HAVE_XFT
2192 static int
2193 facename_changed (XlwMenuWidget newmw,
2194 XlwMenuWidget oldmw)
2195 {
2196 /* This will fore a new XftFont even if the same string is set.
2197 This is good, as rendering parameters may have changed and
2198 we just want to do a redisplay. */
2199 return newmw->menu.faceName != oldmw->menu.faceName;
2200 }
2201 #endif
2202
2203 static Boolean
2204 XlwMenuSetValues (current, request, new)
2205 Widget current;
2206 Widget request;
2207 Widget new;
2208 {
2209 XlwMenuWidget oldmw = (XlwMenuWidget)current;
2210 XlwMenuWidget newmw = (XlwMenuWidget)new;
2211 Boolean redisplay = False;
2212 int i;
2213
2214 if (newmw->menu.contents
2215 && newmw->menu.contents->contents
2216 && newmw->menu.contents->contents->change >= VISIBLE_CHANGE)
2217 redisplay = True;
2218 /* Do redisplay if the contents are entirely eliminated. */
2219 if (newmw->menu.contents
2220 && newmw->menu.contents->contents == 0
2221 && newmw->menu.contents->change >= VISIBLE_CHANGE)
2222 redisplay = True;
2223
2224 if (newmw->core.background_pixel != oldmw->core.background_pixel
2225 || newmw->menu.foreground != oldmw->menu.foreground
2226 #ifdef HAVE_XFT
2227 || facename_changed (newmw, oldmw)
2228 #endif
2229 #ifdef HAVE_X_I18N
2230 || newmw->menu.fontSet != oldmw->menu.fontSet
2231 || (newmw->menu.fontSet == NULL && newmw->menu.font != oldmw->menu.font)
2232 #else
2233 || newmw->menu.font != oldmw->menu.font
2234 #endif
2235 )
2236 {
2237 release_drawing_gcs (newmw);
2238 make_drawing_gcs (newmw);
2239
2240 release_shadow_gcs (newmw);
2241 /* Cause the shadow colors to be recalculated. */
2242 newmw->menu.top_shadow_color = -1;
2243 newmw->menu.bottom_shadow_color = -1;
2244 make_shadow_gcs (newmw);
2245
2246 redisplay = True;
2247
2248 if (XtIsRealized (current))
2249 /* If the menu is currently displayed, change the display. */
2250 for (i = 0; i < oldmw->menu.windows_length; i++)
2251 {
2252 XSetWindowBackground (XtDisplay (oldmw),
2253 oldmw->menu.windows [i].window,
2254 newmw->core.background_pixel);
2255 /* clear windows and generate expose events */
2256 XClearArea (XtDisplay (oldmw), oldmw->menu.windows[i].window,
2257 0, 0, 0, 0, True);
2258 }
2259 }
2260
2261 #ifdef HAVE_XFT
2262 if (facename_changed (newmw, oldmw))
2263 {
2264 int i;
2265 int screen = XScreenNumberOfScreen (newmw->core.screen);
2266 if (newmw->menu.xft_font)
2267 XftFontClose (XtDisplay (newmw), newmw->menu.xft_font);
2268 openXftFont (newmw);
2269 for (i = 0; i < newmw->menu.windows_length; i++)
2270 {
2271 if (newmw->menu.windows [i].xft_draw)
2272 XftDrawDestroy (newmw->menu.windows [i].xft_draw);
2273 newmw->menu.windows [i].xft_draw = 0;
2274 }
2275 if (newmw->menu.xft_font)
2276 for (i = 0; i < newmw->menu.windows_length; i++)
2277 newmw->menu.windows [i].xft_draw
2278 = XftDrawCreate (XtDisplay (newmw),
2279 newmw->menu.windows [i].window,
2280 DefaultVisual (XtDisplay (newmw), screen),
2281 newmw->core.colormap);
2282 }
2283 #endif
2284 #ifdef HAVE_X_I18N
2285 if (newmw->menu.fontSet != oldmw->menu.fontSet && newmw->menu.fontSet != NULL)
2286 {
2287 redisplay = True;
2288 newmw->menu.font_extents = XExtentsOfFontSet (newmw->menu.fontSet);
2289 }
2290 #endif
2291
2292 return redisplay;
2293 }
2294
2295 static void
2296 XlwMenuResize (w)
2297 Widget w;
2298 {
2299 XlwMenuWidget mw = (XlwMenuWidget)w;
2300
2301 if (mw->menu.popped_up)
2302 {
2303 /* Don't allow the popup menu to resize itself. */
2304 mw->core.width = mw->menu.windows [0].width;
2305 mw->core.height = mw->menu.windows [0].height;
2306 mw->core.parent->core.width = mw->core.width;
2307 mw->core.parent->core.height = mw->core.height;
2308 }
2309 else
2310 {
2311 mw->menu.windows [0].width = mw->core.width;
2312 mw->menu.windows [0].height = mw->core.height;
2313 create_pixmap_for_menu (&mw->menu.windows [0], mw);
2314 }
2315 }
2316
2317 \f/* Action procedures */
2318 static void
2319 handle_single_motion_event (mw, ev)
2320 XlwMenuWidget mw;
2321 XMotionEvent* ev;
2322 {
2323 widget_value* val;
2324 int level;
2325
2326 if (!map_event_to_widget_value (mw, ev, &val, &level))
2327 pop_new_stack_if_no_contents (mw);
2328 else
2329 set_new_state (mw, val, level);
2330 remap_menubar (mw);
2331
2332 /* Sync with the display. Makes it feel better on X terms. */
2333 XSync (XtDisplay (mw), False);
2334 }
2335
2336 static void
2337 handle_motion_event (mw, ev)
2338 XlwMenuWidget mw;
2339 XMotionEvent* ev;
2340 {
2341 int x = ev->x_root;
2342 int y = ev->y_root;
2343 int state = ev->state;
2344 XMotionEvent oldev = *ev;
2345
2346 /* allow motion events to be generated again */
2347 if (ev->is_hint
2348 && XQueryPointer (XtDisplay (mw), ev->window,
2349 &ev->root, &ev->subwindow,
2350 &ev->x_root, &ev->y_root,
2351 &ev->x, &ev->y,
2352 &ev->state)
2353 && ev->state == state
2354 && (ev->x_root != x || ev->y_root != y))
2355 handle_single_motion_event (mw, ev);
2356 else
2357 handle_single_motion_event (mw, &oldev);
2358 }
2359
2360 static void
2361 Start (w, ev, params, num_params)
2362 Widget w;
2363 XEvent *ev;
2364 String *params;
2365 Cardinal *num_params;
2366 {
2367 XlwMenuWidget mw = (XlwMenuWidget)w;
2368
2369 if (!mw->menu.popped_up)
2370 {
2371 menu_post_event = *ev;
2372 /* If event is set to CurrentTime, get the last known time stamp.
2373 This is for calculating if (popup) menus should stay up after
2374 a fast click. */
2375 if (menu_post_event.xbutton.time == CurrentTime)
2376 menu_post_event.xbutton.time
2377 = XtLastTimestampProcessed (XtDisplay (w));
2378
2379 pop_up_menu (mw, (XButtonPressedEvent*) ev);
2380 }
2381 else
2382 {
2383 /* If we push a button while the menu is posted semipermanently,
2384 releasing the button should always pop the menu down. */
2385 next_release_must_exit = 1;
2386
2387 /* notes the absolute position of the menubar window */
2388 mw->menu.windows [0].x = ev->xmotion.x_root - ev->xmotion.x;
2389 mw->menu.windows [0].y = ev->xmotion.y_root - ev->xmotion.y;
2390
2391 /* handles the down like a move, slots are compatible */
2392 ev->xmotion.is_hint = 0;
2393 handle_motion_event (mw, &ev->xmotion);
2394 }
2395 }
2396
2397 static void
2398 Drag (w, ev, params, num_params)
2399 Widget w;
2400 XEvent *ev;
2401 String *params;
2402 Cardinal *num_params;
2403 {
2404 XlwMenuWidget mw = (XlwMenuWidget)w;
2405 if (mw->menu.popped_up)
2406 handle_motion_event (mw, &ev->xmotion);
2407 }
2408
2409 /* Do nothing.
2410 This is how we handle presses and releases of modifier keys. */
2411 static void
2412 Nothing (w, ev, params, num_params)
2413 Widget w;
2414 XEvent *ev;
2415 String *params;
2416 Cardinal *num_params;
2417 {
2418 }
2419
2420 static widget_value *
2421 find_first_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 (lw_separator_p (current->name, &separator, 0) || !current->enabled
2430 || (skip_titles && !current->call_data && !current->contents))
2431 if (current->next)
2432 current=current->next;
2433 else
2434 return NULL;
2435
2436 return current;
2437 }
2438
2439 static widget_value *
2440 find_next_selectable (mw, item, skip_titles)
2441 XlwMenuWidget mw;
2442 widget_value *item;
2443 int skip_titles;
2444 {
2445 widget_value *current = item;
2446 enum menu_separator separator;
2447
2448 while (current->next && (current=current->next) &&
2449 (lw_separator_p (current->name, &separator, 0) || !current->enabled
2450 || (skip_titles && !current->call_data && !current->contents)))
2451 ;
2452
2453 if (current == item)
2454 {
2455 if (mw->menu.old_depth < 2)
2456 return current;
2457 current = mw->menu.old_stack [mw->menu.old_depth - 2]->contents;
2458
2459 while (lw_separator_p (current->name, &separator, 0)
2460 || !current->enabled
2461 || (skip_titles && !current->call_data
2462 && !current->contents))
2463 {
2464 if (current->next)
2465 current=current->next;
2466
2467 if (current == item)
2468 break;
2469 }
2470
2471 }
2472
2473 return current;
2474 }
2475
2476 static widget_value *
2477 find_prev_selectable (mw, item, skip_titles)
2478 XlwMenuWidget mw;
2479 widget_value *item;
2480 int skip_titles;
2481 {
2482 widget_value *current = item;
2483 widget_value *prev = item;
2484
2485 while ((current=find_next_selectable (mw, current, skip_titles))
2486 != item)
2487 {
2488 if (prev == current)
2489 break;
2490 prev=current;
2491 }
2492
2493 return prev;
2494 }
2495
2496 static void
2497 Down (w, ev, params, num_params)
2498 Widget w;
2499 XEvent *ev;
2500 String *params;
2501 Cardinal *num_params;
2502 {
2503 XlwMenuWidget mw = (XlwMenuWidget) w;
2504 widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2505 int popup_menu_p = mw->menu.top_depth == 1;
2506
2507 /* Inside top-level menu-bar? */
2508 if (mw->menu.old_depth == mw->menu.top_depth)
2509 /* When <down> in the menu-bar is pressed, display the corresponding
2510 sub-menu and select the first selectable menu item there.
2511 If this is a popup menu, skip title item of the popup. */
2512 set_new_state (mw,
2513 find_first_selectable (mw,
2514 selected_item->contents,
2515 popup_menu_p),
2516 mw->menu.old_depth);
2517 else
2518 /* Highlight next possible (enabled and not separator) menu item. */
2519 set_new_state (mw, find_next_selectable (mw, selected_item, popup_menu_p),
2520 mw->menu.old_depth - 1);
2521
2522 remap_menubar (mw);
2523 }
2524
2525 static void
2526 Up (w, ev, params, num_params)
2527 Widget w;
2528 XEvent *ev;
2529 String *params;
2530 Cardinal *num_params;
2531 {
2532 XlwMenuWidget mw = (XlwMenuWidget) w;
2533 widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2534 int popup_menu_p = mw->menu.top_depth == 1;
2535
2536 /* Inside top-level menu-bar? */
2537 if (mw->menu.old_depth == mw->menu.top_depth)
2538 {
2539 /* FIXME: this is tricky. <up> in the menu-bar should select the
2540 last selectable item in the list. So we select the first
2541 selectable one and find the previous selectable item. Is there
2542 a better way? */
2543 /* If this is a popup menu, skip title item of the popup. */
2544 set_new_state (mw,
2545 find_first_selectable (mw,
2546 selected_item->contents,
2547 popup_menu_p),
2548 mw->menu.old_depth);
2549 remap_menubar (mw);
2550 selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2551 set_new_state (mw,
2552 find_prev_selectable (mw,
2553 selected_item,
2554 popup_menu_p),
2555 mw->menu.old_depth - 1);
2556 }
2557 else
2558 /* Highlight previous (enabled and not separator) menu item. */
2559 set_new_state (mw, find_prev_selectable (mw, selected_item, popup_menu_p),
2560 mw->menu.old_depth - 1);
2561
2562 remap_menubar (mw);
2563 }
2564
2565 void
2566 Left (w, ev, params, num_params)
2567 Widget w;
2568 XEvent *ev;
2569 String *params;
2570 Cardinal *num_params;
2571 {
2572 XlwMenuWidget mw = (XlwMenuWidget) w;
2573 widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2574
2575 /* Inside top-level menu-bar? */
2576 if (mw->menu.old_depth == mw->menu.top_depth)
2577 /* When <left> in the menu-bar is pressed, display the previous item on
2578 the menu-bar. If the current item is the first one, highlight the
2579 last item in the menubar (probably Help). */
2580 set_new_state (mw, find_prev_selectable (mw, selected_item, 0),
2581 mw->menu.old_depth - 1);
2582 else if (mw->menu.old_depth == 1
2583 && selected_item->contents) /* Is this menu item expandable? */
2584 {
2585 set_new_state (mw, selected_item->contents, mw->menu.old_depth);
2586 remap_menubar (mw);
2587 selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2588 if (!selected_item->enabled && find_first_selectable (mw,
2589 selected_item,
2590 0))
2591 set_new_state (mw, find_first_selectable (mw, selected_item, 0),
2592 mw->menu.old_depth - 1);
2593 }
2594
2595 else
2596 {
2597 pop_new_stack_if_no_contents (mw);
2598 set_new_state (mw, mw->menu.old_stack [mw->menu.old_depth - 2],
2599 mw->menu.old_depth - 2);
2600 }
2601
2602 remap_menubar (mw);
2603 }
2604
2605 void
2606 Right (w, ev, params, num_params)
2607 Widget w;
2608 XEvent *ev;
2609 String *params;
2610 Cardinal *num_params;
2611 {
2612 XlwMenuWidget mw = (XlwMenuWidget) w;
2613 widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2614
2615 /* Inside top-level menu-bar? */
2616 if (mw->menu.old_depth == mw->menu.top_depth)
2617 /* When <right> in the menu-bar is pressed, display the next item on
2618 the menu-bar. If the current item is the last one, highlight the
2619 first item (probably File). */
2620 set_new_state (mw, find_next_selectable (mw, selected_item, 0),
2621 mw->menu.old_depth - 1);
2622 else if (selected_item->contents) /* Is this menu item expandable? */
2623 {
2624 set_new_state (mw, selected_item->contents, mw->menu.old_depth);
2625 remap_menubar (mw);
2626 selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2627 if (!selected_item->enabled && find_first_selectable (mw,
2628 selected_item,
2629 0))
2630 set_new_state (mw, find_first_selectable (mw, selected_item, 0),
2631 mw->menu.old_depth - 1);
2632 }
2633 else
2634 {
2635 pop_new_stack_if_no_contents (mw);
2636 set_new_state (mw, mw->menu.old_stack [mw->menu.old_depth - 2],
2637 mw->menu.old_depth - 2);
2638 }
2639
2640 remap_menubar (mw);
2641 }
2642
2643 /* Handle key press and release events while menu is popped up.
2644 Our action is to get rid of the menu. */
2645 static void
2646 Key (w, ev, params, num_params)
2647 Widget w;
2648 XEvent *ev;
2649 String *params;
2650 Cardinal *num_params;
2651 {
2652 XlwMenuWidget mw = (XlwMenuWidget)w;
2653
2654 /* Pop down everything. */
2655 mw->menu.new_depth = 1;
2656 remap_menubar (mw);
2657
2658 if (mw->menu.popped_up)
2659 {
2660 mw->menu.popped_up = False;
2661 ungrab_all ((Widget)mw, ev->xmotion.time);
2662 if (XtIsShell (XtParent ((Widget) mw)))
2663 XtPopdown (XtParent ((Widget) mw));
2664 else
2665 {
2666 XtRemoveGrab ((Widget) mw);
2667 display_menu (mw, 0, False, NULL, NULL, NULL);
2668 }
2669 }
2670
2671 /* callback */
2672 XtCallCallbackList ((Widget)mw, mw->menu.select, (XtPointer)0);
2673 }
2674
2675 static void
2676 Select (w, ev, params, num_params)
2677 Widget w;
2678 XEvent *ev;
2679 String *params;
2680 Cardinal *num_params;
2681 {
2682 XlwMenuWidget mw = (XlwMenuWidget)w;
2683 widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2684
2685 /* If user releases the button quickly, without selecting anything,
2686 after the initial down-click that brought the menu up,
2687 do nothing. */
2688 if ((selected_item == 0
2689 || ((widget_value *) selected_item)->call_data == 0)
2690 && !next_release_must_exit
2691 && (ev->xbutton.time - menu_post_event.xbutton.time
2692 < XtGetMultiClickTime (XtDisplay (w))))
2693 return;
2694
2695 /* pop down everything. */
2696 mw->menu.new_depth = 1;
2697 remap_menubar (mw);
2698
2699 if (mw->menu.popped_up)
2700 {
2701 mw->menu.popped_up = False;
2702 ungrab_all ((Widget)mw, ev->xmotion.time);
2703 if (XtIsShell (XtParent ((Widget) mw)))
2704 XtPopdown (XtParent ((Widget) mw));
2705 else
2706 {
2707 XtRemoveGrab ((Widget) mw);
2708 display_menu (mw, 0, False, NULL, NULL, NULL);
2709 }
2710 }
2711
2712 /* callback */
2713 XtCallCallbackList ((Widget)mw, mw->menu.select, (XtPointer)selected_item);
2714 }
2715
2716
2717 \f/* Special code to pop-up a menu */
2718 static void
2719 pop_up_menu (mw, event)
2720 XlwMenuWidget mw;
2721 XButtonPressedEvent* event;
2722 {
2723 int x = event->x_root;
2724 int y = event->y_root;
2725 int w;
2726 int h;
2727 int borderwidth = mw->menu.shadow_thickness;
2728 Screen* screen = XtScreen (mw);
2729 Display *display = XtDisplay (mw);
2730
2731 next_release_must_exit = 0;
2732
2733 mw->menu.inside_entry = NULL;
2734 XtCallCallbackList ((Widget)mw, mw->menu.open, NULL);
2735
2736 if (XtIsShell (XtParent ((Widget)mw)))
2737 size_menu (mw, 0);
2738
2739 w = mw->menu.windows [0].width;
2740 h = mw->menu.windows [0].height;
2741
2742 x -= borderwidth;
2743 y -= borderwidth;
2744 if (x < borderwidth)
2745 x = borderwidth;
2746 if (x + w + 2 * borderwidth > WidthOfScreen (screen))
2747 x = WidthOfScreen (screen) - w - 2 * borderwidth;
2748 if (y < borderwidth)
2749 y = borderwidth;
2750 if (y + h + 2 * borderwidth> HeightOfScreen (screen))
2751 y = HeightOfScreen (screen) - h - 2 * borderwidth;
2752
2753 mw->menu.popped_up = True;
2754 if (XtIsShell (XtParent ((Widget)mw)))
2755 {
2756 XtConfigureWidget (XtParent ((Widget)mw), x, y, w, h,
2757 XtParent ((Widget)mw)->core.border_width);
2758 XtPopup (XtParent ((Widget)mw), XtGrabExclusive);
2759 display_menu (mw, 0, False, NULL, NULL, NULL);
2760 mw->menu.windows [0].x = x + borderwidth;
2761 mw->menu.windows [0].y = y + borderwidth;
2762 mw->menu.top_depth = 1; /* Popup menus don't have a bar so top is 1 */
2763 }
2764 else
2765 {
2766 XEvent *ev = (XEvent *) event;
2767
2768 XtAddGrab ((Widget) mw, True, True);
2769
2770 /* notes the absolute position of the menubar window */
2771 mw->menu.windows [0].x = ev->xmotion.x_root - ev->xmotion.x;
2772 mw->menu.windows [0].y = ev->xmotion.y_root - ev->xmotion.y;
2773 mw->menu.top_depth = 2;
2774 }
2775
2776 #ifdef emacs
2777 x_catch_errors (display);
2778 #endif
2779 if (XtGrabPointer ((Widget)mw, False,
2780 (PointerMotionMask
2781 | PointerMotionHintMask
2782 | ButtonReleaseMask
2783 | ButtonPressMask),
2784 GrabModeAsync, GrabModeAsync, None,
2785 mw->menu.cursor_shape,
2786 event->time) == Success)
2787 {
2788 if (! GRAB_KEYBOARD
2789 || XtGrabKeyboard ((Widget)mw, False, GrabModeAsync,
2790 GrabModeAsync, event->time) == Success)
2791 {
2792 XtSetKeyboardFocus((Widget)mw, None);
2793 pointer_grabbed = 1;
2794 }
2795 else
2796 XtUngrabPointer ((Widget)mw, event->time);
2797 }
2798
2799 #ifdef emacs
2800 if (x_had_errors_p (display))
2801 {
2802 pointer_grabbed = 0;
2803 XtUngrabPointer ((Widget)mw, event->time);
2804 }
2805 x_uncatch_errors ();
2806 #endif
2807
2808 ((XMotionEvent*)event)->is_hint = 0;
2809 handle_motion_event (mw, (XMotionEvent*)event);
2810 }
2811
2812 /* arch-tag: 657f43dd-dfd0-4cc9-910c-52935f01176e
2813 (do not change this comment) */