Use *font for Xft font names for Lucid menus and dialogs.
[bpt/emacs.git] / lwlib / xlwmenu.c
1 /* Implements a lightweight menubar widget.
2
3 Copyright (C) 1992 Lucid, Inc.
4 Copyright (C) 1994-1995, 1997, 1999-2011 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 char
75 xlwMenuTranslations [] =
76 "<BtnDown>: start()\n\
77 <Motion>: drag()\n\
78 <BtnUp>: select()\n\
79 <Key>Shift_L: nothing()\n\
80 <Key>Shift_R: nothing()\n\
81 <Key>Meta_L: nothing()\n\
82 <Key>Meta_R: nothing()\n\
83 <Key>Control_L: nothing()\n\
84 <Key>Control_R: nothing()\n\
85 <Key>Hyper_L: nothing()\n\
86 <Key>Hyper_R: nothing()\n\
87 <Key>Super_L: nothing()\n\
88 <Key>Super_R: nothing()\n\
89 <Key>Alt_L: nothing()\n\
90 <Key>Alt_R: nothing()\n\
91 <Key>Caps_Lock: nothing()\n\
92 <Key>Shift_Lock: nothing()\n\
93 <KeyUp>Shift_L: nothing()\n\
94 <KeyUp>Shift_R: nothing()\n\
95 <KeyUp>Meta_L: nothing()\n\
96 <KeyUp>Meta_R: nothing()\n\
97 <KeyUp>Control_L: nothing()\n\
98 <KeyUp>Control_R: nothing()\n\
99 <KeyUp>Hyper_L: nothing()\n\
100 <KeyUp>Hyper_R: nothing()\n\
101 <KeyUp>Super_L: nothing()\n\
102 <KeyUp>Super_R: nothing()\n\
103 <KeyUp>Alt_L: nothing()\n\
104 <KeyUp>Alt_R: nothing()\n\
105 <KeyUp>Caps_Lock: nothing()\n\
106 <KeyUp>Shift_Lock:nothing()\n\
107 <Key>Return: select()\n\
108 <Key>Down: down()\n\
109 <Key>Up: up()\n\
110 <Key>Left: left()\n\
111 <Key>Right: right()\n\
112 <Key>: key()\n\
113 <KeyUp>: key()\n\
114 ";
115
116 /* FIXME: Space should toggle toggleable menu item but not remove the menu
117 so you can toggle the next one without entering the menu again. */
118
119 /* FIXME: Should ESC close one level of menu structure or the complete menu? */
120
121 /* FIXME: F10 should enter the menu, the first one in the menu-bar. */
122
123 #define offset(field) XtOffset(XlwMenuWidget, field)
124 static XtResource
125 xlwMenuResources[] =
126 {
127 #ifdef HAVE_X_I18N
128 {XtNfontSet, XtCFontSet, XtRFontSet, sizeof(XFontSet),
129 offset(menu.fontSet), XtRFontSet, NULL},
130 #endif
131 #ifdef HAVE_XFT
132 #define DEFAULT_FONTNAME "Sans-10"
133 #else
134 #define DEFAULT_FONTNAME "XtDefaultFont"
135 #endif
136 {XtNfont, XtCFont, XtRString, sizeof(String),
137 offset(menu.fontName), XtRString, DEFAULT_FONTNAME },
138 {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
139 offset(menu.foreground), XtRString, "XtDefaultForeground"},
140 {XtNdisabledForeground, XtCDisabledForeground, XtRPixel, sizeof(Pixel),
141 offset(menu.disabled_foreground), XtRString, (XtPointer)NULL},
142 {XtNbuttonForeground, XtCButtonForeground, XtRPixel, sizeof(Pixel),
143 offset(menu.button_foreground), XtRString, "XtDefaultForeground"},
144 {XtNmargin, XtCMargin, XtRDimension, sizeof(Dimension),
145 offset(menu.margin), XtRImmediate, (XtPointer)1},
146 {XtNhorizontalSpacing, XtCMargin, XtRDimension, sizeof(Dimension),
147 offset(menu.horizontal_spacing), XtRImmediate, (XtPointer)3},
148 {XtNverticalSpacing, XtCMargin, XtRDimension, sizeof(Dimension),
149 offset(menu.vertical_spacing), XtRImmediate, (XtPointer)2},
150 {XtNarrowSpacing, XtCMargin, XtRDimension, sizeof(Dimension),
151 offset(menu.arrow_spacing), XtRImmediate, (XtPointer)10},
152
153 {XmNshadowThickness, XmCShadowThickness, XtRDimension,
154 sizeof (Dimension), offset (menu.shadow_thickness),
155 XtRImmediate, (XtPointer)1},
156 {XmNtopShadowColor, XmCTopShadowColor, XtRPixel, sizeof (Pixel),
157 offset (menu.top_shadow_color), XtRImmediate, (XtPointer)-1},
158 {XmNbottomShadowColor, XmCBottomShadowColor, XtRPixel, sizeof (Pixel),
159 offset (menu.bottom_shadow_color), XtRImmediate, (XtPointer)-1},
160 {XmNtopShadowPixmap, XmCTopShadowPixmap, XtRPixmap, sizeof (Pixmap),
161 offset (menu.top_shadow_pixmap), XtRImmediate, (XtPointer)None},
162 {XmNbottomShadowPixmap, XmCBottomShadowPixmap, XtRPixmap, sizeof (Pixmap),
163 offset (menu.bottom_shadow_pixmap), XtRImmediate, (XtPointer)None},
164
165 {XtNopen, XtCCallback, XtRCallback, sizeof(XtPointer),
166 offset(menu.open), XtRCallback, (XtPointer)NULL},
167 {XtNselect, XtCCallback, XtRCallback, sizeof(XtPointer),
168 offset(menu.select), XtRCallback, (XtPointer)NULL},
169 {XtNhighlightCallback, XtCCallback, XtRCallback, sizeof(XtPointer),
170 offset(menu.highlight), XtRCallback, (XtPointer)NULL},
171 {XtNenterCallback, XtCCallback, XtRCallback, sizeof(XtPointer),
172 offset(menu.enter), XtRCallback, (XtPointer)NULL},
173 {XtNleaveCallback, XtCCallback, XtRCallback, sizeof(XtPointer),
174 offset(menu.leave), XtRCallback, (XtPointer)NULL},
175 {XtNmenu, XtCMenu, XtRPointer, sizeof(XtPointer),
176 offset(menu.contents), XtRImmediate, (XtPointer)NULL},
177 {XtNcursor, XtCCursor, XtRCursor, sizeof(Cursor),
178 offset(menu.cursor_shape), XtRString, (XtPointer)"right_ptr"},
179 {XtNhorizontal, XtCHorizontal, XtRInt, sizeof(int),
180 offset(menu.horizontal), XtRImmediate, (XtPointer)True},
181 };
182 #undef offset
183
184 static Boolean XlwMenuSetValues(Widget current, Widget request, Widget new,
185 ArgList args, Cardinal *num_args);
186 static void XlwMenuRealize(Widget, Mask *, XSetWindowAttributes *);
187 static void XlwMenuResize(Widget w);
188 static void XlwMenuInitialize(Widget, Widget, ArgList, Cardinal *);
189 static void XlwMenuRedisplay(Widget w, XEvent *ev, Region region);
190 static void XlwMenuDestroy(Widget w);
191 static void XlwMenuClassInitialize(void);
192 static void Start(Widget w, XEvent *ev, String *params, Cardinal *num_params);
193 static void Drag(Widget w, XEvent *ev, String *params, Cardinal *num_params);
194 static void Down(Widget w, XEvent *ev, String *params, Cardinal *num_params);
195 static void Up(Widget w, XEvent *ev, String *params, Cardinal *num_params);
196 static void Left(Widget w, XEvent *ev, String *params, Cardinal *num_params);
197 static void Right(Widget w, XEvent *ev, String *params, Cardinal *num_params);
198 static void Select(Widget w, XEvent *ev, String *params, Cardinal *num_params);
199 static void Key(Widget w, XEvent *ev, String *params, Cardinal *num_params);
200 static void Nothing(Widget w, XEvent *ev, String *params, Cardinal *num_params);
201 static int separator_height (enum menu_separator);
202 static void pop_up_menu (XlwMenuWidget, XButtonPressedEvent *);
203 static void abort_gracefully (Widget w) NO_RETURN;
204
205 static XtActionsRec
206 xlwMenuActionsList [] =
207 {
208 {"start", Start},
209 {"drag", Drag},
210 {"down", Down},
211 {"up", Up},
212 {"left", Left},
213 {"right", Right},
214 {"select", Select},
215 {"key", Key},
216 {"MenuGadgetEscape", Key}, /* Compatibility with Lesstif/Motif. */
217 {"nothing", Nothing},
218 };
219
220 #define SuperClass ((CoreWidgetClass)&coreClassRec)
221
222 XlwMenuClassRec xlwMenuClassRec =
223 {
224 { /* CoreClass fields initialization */
225 (WidgetClass) SuperClass, /* superclass */
226 "XlwMenu", /* class_name */
227 sizeof(XlwMenuRec), /* size */
228 XlwMenuClassInitialize, /* class_initialize */
229 NULL, /* class_part_initialize */
230 FALSE, /* class_inited */
231 XlwMenuInitialize, /* initialize */
232 NULL, /* initialize_hook */
233 XlwMenuRealize, /* realize */
234 xlwMenuActionsList, /* actions */
235 XtNumber(xlwMenuActionsList), /* num_actions */
236 xlwMenuResources, /* resources */
237 XtNumber(xlwMenuResources), /* resource_count */
238 NULLQUARK, /* xrm_class */
239 TRUE, /* compress_motion */
240 XtExposeCompressMaximal, /* compress_exposure */
241 TRUE, /* compress_enterleave */
242 FALSE, /* visible_interest */
243 XlwMenuDestroy, /* destroy */
244 XlwMenuResize, /* resize */
245 XlwMenuRedisplay, /* expose */
246 XlwMenuSetValues, /* set_values */
247 NULL, /* set_values_hook */
248 XtInheritSetValuesAlmost, /* set_values_almost */
249 NULL, /* get_values_hook */
250 NULL, /* accept_focus */
251 XtVersion, /* version */
252 NULL, /* callback_private */
253 xlwMenuTranslations, /* tm_table */
254 XtInheritQueryGeometry, /* query_geometry */
255 XtInheritDisplayAccelerator, /* display_accelerator */
256 NULL /* extension */
257 }, /* XlwMenuClass fields initialization */
258 {
259 0 /* dummy */
260 },
261 };
262
263 WidgetClass xlwMenuWidgetClass = (WidgetClass) &xlwMenuClassRec;
264
265 int submenu_destroyed;
266
267 /* For debug, if installation-directory is non-nil this is not an installed
268 Emacs. In that case we do not grab the keyboard to make it easier to
269 debug. */
270 #define GRAB_KEYBOARD (EQ (Vinstallation_directory, Qnil))
271
272 static int next_release_must_exit;
273
274 \f/* Utilities */
275
276 /* Ungrab pointer and keyboard */
277 static void
278 ungrab_all (Widget w, Time ungrabtime)
279 {
280 XtUngrabPointer (w, ungrabtime);
281 if (GRAB_KEYBOARD) XtUngrabKeyboard (w, ungrabtime);
282 }
283
284 /* Like abort, but remove grabs from widget W before. */
285
286 static void
287 abort_gracefully (Widget w)
288 {
289 if (XtIsShell (XtParent (w)))
290 XtRemoveGrab (w);
291 ungrab_all (w, CurrentTime);
292 abort ();
293 }
294
295 static void
296 push_new_stack (XlwMenuWidget mw, widget_value *val)
297 {
298 if (!mw->menu.new_stack)
299 {
300 mw->menu.new_stack_length = 10;
301 mw->menu.new_stack =
302 (widget_value**)XtCalloc (mw->menu.new_stack_length,
303 sizeof (widget_value*));
304 }
305 else if (mw->menu.new_depth == mw->menu.new_stack_length)
306 {
307 mw->menu.new_stack_length *= 2;
308 mw->menu.new_stack =
309 (widget_value**)XtRealloc ((char*)mw->menu.new_stack,
310 mw->menu.new_stack_length * sizeof (widget_value*));
311 }
312 mw->menu.new_stack [mw->menu.new_depth++] = val;
313 }
314
315 static void
316 pop_new_stack_if_no_contents (XlwMenuWidget mw)
317 {
318 if (mw->menu.new_depth > 1)
319 {
320 if (!mw->menu.new_stack [mw->menu.new_depth - 1]->contents)
321 mw->menu.new_depth -= 1;
322 }
323 }
324
325 static void
326 make_old_stack_space (XlwMenuWidget mw, int n)
327 {
328 if (!mw->menu.old_stack)
329 {
330 mw->menu.old_stack_length = 10;
331 mw->menu.old_stack =
332 (widget_value**)XtCalloc (mw->menu.old_stack_length,
333 sizeof (widget_value*));
334 }
335 else if (mw->menu.old_stack_length < n)
336 {
337 mw->menu.old_stack_length *= 2;
338 mw->menu.old_stack =
339 (widget_value**)XtRealloc ((char*)mw->menu.old_stack,
340 mw->menu.old_stack_length * sizeof (widget_value*));
341 }
342 }
343
344 \f/* Size code */
345 static int
346 string_width (XlwMenuWidget mw, char *s)
347 {
348 XCharStruct xcs;
349 int drop;
350 #ifdef HAVE_XFT
351 if (mw->menu.xft_font)
352 {
353 XGlyphInfo gi;
354 XftTextExtentsUtf8 (XtDisplay (mw), mw->menu.xft_font,
355 (FcChar8 *) s,
356 strlen (s), &gi);
357 return gi.width;
358 }
359 #endif
360 #ifdef HAVE_X_I18N
361 if (mw->menu.fontSet)
362 {
363 XRectangle ink, logical;
364 XmbTextExtents (mw->menu.fontSet, s, strlen (s), &ink, &logical);
365 return logical.width;
366 }
367 #endif
368
369 XTextExtents (mw->menu.font, s, strlen (s), &drop, &drop, &drop, &xcs);
370 return xcs.width;
371
372 }
373
374 #ifdef HAVE_XFT
375 #define MENU_FONT_HEIGHT(mw) \
376 ((mw)->menu.xft_font != NULL \
377 ? (mw)->menu.xft_font->height \
378 : ((mw)->menu.fontSet != NULL \
379 ? (mw)->menu.font_extents->max_logical_extent.height \
380 : (mw)->menu.font->ascent + (mw)->menu.font->descent))
381 #define MENU_FONT_ASCENT(mw) \
382 ((mw)->menu.xft_font != NULL \
383 ? (mw)->menu.xft_font->ascent \
384 : ((mw)->menu.fontSet != NULL \
385 ? - (mw)->menu.font_extents->max_logical_extent.y \
386 : (mw)->menu.font->ascent))
387 #else
388 #ifdef HAVE_X_I18N
389 #define MENU_FONT_HEIGHT(mw) \
390 ((mw)->menu.fontSet != NULL \
391 ? (mw)->menu.font_extents->max_logical_extent.height \
392 : (mw)->menu.font->ascent + (mw)->menu.font->descent)
393 #define MENU_FONT_ASCENT(mw) \
394 ((mw)->menu.fontSet != NULL \
395 ? - (mw)->menu.font_extents->max_logical_extent.y \
396 : (mw)->menu.font->ascent)
397 #else
398 #define MENU_FONT_HEIGHT(mw) \
399 ((mw)->menu.font->ascent + (mw)->menu.font->descent)
400 #define MENU_FONT_ASCENT(mw) ((mw)->menu.font->ascent)
401 #endif
402 #endif
403
404 static int
405 arrow_width (XlwMenuWidget mw)
406 {
407 return (MENU_FONT_ASCENT (mw) * 3/4) | 1;
408 }
409
410 /* Return the width of toggle buttons of widget MW. */
411
412 static int
413 toggle_button_width (XlwMenuWidget mw)
414 {
415 return (MENU_FONT_HEIGHT (mw) * 2 / 3) | 1;
416 }
417
418
419 /* Return the width of radio buttons of widget MW. */
420
421 static int
422 radio_button_width (XlwMenuWidget mw)
423 {
424 return toggle_button_width (mw) * 1.41;
425 }
426
427
428 static XtResource
429 nameResource[] =
430 {
431 {"labelString", "LabelString", XtRString, sizeof(String),
432 0, XtRImmediate, 0},
433 };
434
435 static char*
436 resource_widget_value (XlwMenuWidget mw, widget_value *val)
437 {
438 if (!val->toolkit_data)
439 {
440 char* resourced_name = NULL;
441 char* complete_name;
442 XtGetSubresources ((Widget) mw,
443 (XtPointer) &resourced_name,
444 val->name, val->name,
445 nameResource, 1, NULL, 0);
446 if (!resourced_name)
447 resourced_name = val->name;
448 if (!val->value)
449 {
450 complete_name = (char *) XtMalloc (strlen (resourced_name) + 1);
451 strcpy (complete_name, resourced_name);
452 }
453 else
454 {
455 int complete_length =
456 strlen (resourced_name) + strlen (val->value) + 2;
457 complete_name = XtMalloc (complete_length);
458 *complete_name = 0;
459 strcat (complete_name, resourced_name);
460 strcat (complete_name, " ");
461 strcat (complete_name, val->value);
462 }
463
464 val->toolkit_data = complete_name;
465 val->free_toolkit_data = True;
466 }
467 return (char*)val->toolkit_data;
468 }
469
470 /* Returns the sizes of an item */
471 static void
472 size_menu_item (XlwMenuWidget mw,
473 widget_value* val,
474 int horizontal_p,
475 int* label_width,
476 int* rest_width,
477 int* button_width,
478 int* height)
479 {
480 enum menu_separator separator;
481
482 if (lw_separator_p (val->name, &separator, 0))
483 {
484 *height = separator_height (separator);
485 *label_width = 1;
486 *rest_width = 0;
487 *button_width = 0;
488 }
489 else
490 {
491 *height = MENU_FONT_HEIGHT (mw)
492 + 2 * mw->menu.vertical_spacing + 2 * mw->menu.shadow_thickness;
493
494 *label_width =
495 string_width (mw, resource_widget_value (mw, val))
496 + mw->menu.horizontal_spacing + mw->menu.shadow_thickness;
497
498 *rest_width = mw->menu.horizontal_spacing + mw->menu.shadow_thickness;
499 if (!horizontal_p)
500 {
501 if (val->contents)
502 /* Add width of the arrow displayed for submenus. */
503 *rest_width += arrow_width (mw) + mw->menu.arrow_spacing;
504 else if (val->key)
505 /* Add width of key equivalent string. */
506 *rest_width += (string_width (mw, val->key)
507 + mw->menu.arrow_spacing);
508
509 if (val->button_type == BUTTON_TYPE_TOGGLE)
510 *button_width = (toggle_button_width (mw)
511 + mw->menu.horizontal_spacing);
512 else if (val->button_type == BUTTON_TYPE_RADIO)
513 *button_width = (radio_button_width (mw)
514 + mw->menu.horizontal_spacing);
515 }
516 }
517 }
518
519 static void
520 size_menu (XlwMenuWidget mw, int level)
521 {
522 int label_width = 0;
523 int rest_width = 0;
524 int button_width = 0;
525 int max_rest_width = 0;
526 int max_button_width = 0;
527 int height = 0;
528 int horizontal_p = mw->menu.horizontal && (level == 0);
529 widget_value* val;
530 window_state* ws;
531
532 if (level >= mw->menu.old_depth)
533 abort_gracefully ((Widget) mw);
534
535 ws = &mw->menu.windows [level];
536 ws->width = 0;
537 ws->height = 0;
538 ws->label_width = 0;
539 ws->button_width = 0;
540
541 for (val = mw->menu.old_stack [level]->contents; val; val = val->next)
542 {
543 size_menu_item (mw, val, horizontal_p, &label_width, &rest_width,
544 &button_width, &height);
545 if (horizontal_p)
546 {
547 ws->width += label_width + rest_width;
548 if (height > ws->height)
549 ws->height = height;
550 }
551 else
552 {
553 if (label_width > ws->label_width)
554 ws->label_width = label_width;
555 if (rest_width > max_rest_width)
556 max_rest_width = rest_width;
557 if (button_width > max_button_width)
558 max_button_width = button_width;
559 ws->height += height;
560 }
561 }
562
563 if (horizontal_p)
564 ws->label_width = ws->button_width = 0;
565 else
566 {
567 ws->width = ws->label_width + max_rest_width + max_button_width;
568 ws->button_width = max_button_width;
569 }
570
571 ws->width += 2 * mw->menu.shadow_thickness;
572 ws->height += 2 * mw->menu.shadow_thickness;
573 ws->max_rest_width = max_rest_width;
574
575 if (horizontal_p)
576 {
577 ws->width += 2 * mw->menu.margin;
578 ws->height += 2 * mw->menu.margin;
579 }
580 }
581
582
583 \f/* Display code */
584
585 static void
586 draw_arrow (XlwMenuWidget mw,
587 Window window,
588 GC gc,
589 int x,
590 int y,
591 int width,
592 int down_p)
593 {
594 Display *dpy = XtDisplay (mw);
595 GC top_gc = mw->menu.shadow_top_gc;
596 GC bottom_gc = mw->menu.shadow_bottom_gc;
597 int thickness = mw->menu.shadow_thickness;
598 int height = width;
599 XPoint pt[10];
600 /* alpha = atan (0.5)
601 factor = (1 + sin (alpha)) / cos (alpha) */
602 double factor = 1.62;
603 int thickness2 = thickness * factor;
604
605 y += (MENU_FONT_HEIGHT (mw) - height) / 2;
606
607 if (down_p)
608 {
609 GC temp;
610 temp = top_gc;
611 top_gc = bottom_gc;
612 bottom_gc = temp;
613 }
614
615 pt[0].x = x;
616 pt[0].y = y + height;
617 pt[1].x = x + thickness;
618 pt[1].y = y + height - thickness2;
619 pt[2].x = x + thickness2;
620 pt[2].y = y + thickness2;
621 pt[3].x = x;
622 pt[3].y = y;
623 XFillPolygon (dpy, window, top_gc, pt, 4, Convex, CoordModeOrigin);
624
625 pt[0].x = x;
626 pt[0].y = y;
627 pt[1].x = x + thickness;
628 pt[1].y = y + thickness2;
629 pt[2].x = x + width - thickness2;
630 pt[2].y = y + height / 2;
631 pt[3].x = x + width;
632 pt[3].y = y + height / 2;
633 XFillPolygon (dpy, window, top_gc, pt, 4, Convex, CoordModeOrigin);
634
635 pt[0].x = x;
636 pt[0].y = y + height;
637 pt[1].x = x + thickness;
638 pt[1].y = y + height - thickness2;
639 pt[2].x = x + width - thickness2;
640 pt[2].y = y + height / 2;
641 pt[3].x = x + width;
642 pt[3].y = y + height / 2;
643 XFillPolygon (dpy, window, bottom_gc, pt, 4, Convex, CoordModeOrigin);
644 }
645
646
647
648 static void
649 draw_shadow_rectangle (XlwMenuWidget mw,
650 Window window,
651 int x,
652 int y,
653 int width,
654 int height,
655 int erase_p,
656 int down_p)
657 {
658 Display *dpy = XtDisplay (mw);
659 GC top_gc = !erase_p ? mw->menu.shadow_top_gc : mw->menu.background_gc;
660 GC bottom_gc = !erase_p ? mw->menu.shadow_bottom_gc : mw->menu.background_gc;
661 int thickness = mw->menu.shadow_thickness;
662 XPoint points [4];
663
664 if (!erase_p && down_p)
665 {
666 GC temp;
667 temp = top_gc;
668 top_gc = bottom_gc;
669 bottom_gc = temp;
670 }
671
672 points [0].x = x;
673 points [0].y = y;
674 points [1].x = x + width;
675 points [1].y = y;
676 points [2].x = x + width - thickness;
677 points [2].y = y + thickness;
678 points [3].x = x;
679 points [3].y = y + thickness;
680 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
681 points [0].x = x;
682 points [0].y = y + thickness;
683 points [1].x = x;
684 points [1].y = y + height;
685 points [2].x = x + thickness;
686 points [2].y = y + height - thickness;
687 points [3].x = x + thickness;
688 points [3].y = y + thickness;
689 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
690 points [0].x = x + width;
691 points [0].y = y;
692 points [1].x = x + width - thickness;
693 points [1].y = y + thickness;
694 points [2].x = x + width - thickness;
695 points [2].y = y + height - thickness;
696 points [3].x = x + width;
697 points [3].y = y + height - thickness;
698 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
699 points [0].x = x;
700 points [0].y = y + height;
701 points [1].x = x + width;
702 points [1].y = y + height;
703 points [2].x = x + width;
704 points [2].y = y + height - thickness;
705 points [3].x = x + thickness;
706 points [3].y = y + height - thickness;
707 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
708 }
709
710
711 static void
712 draw_shadow_rhombus (XlwMenuWidget mw,
713 Window window,
714 int x,
715 int y,
716 int width,
717 int height,
718 int erase_p,
719 int down_p)
720 {
721 Display *dpy = XtDisplay (mw);
722 GC top_gc = !erase_p ? mw->menu.shadow_top_gc : mw->menu.background_gc;
723 GC bottom_gc = !erase_p ? mw->menu.shadow_bottom_gc : mw->menu.background_gc;
724 int thickness = mw->menu.shadow_thickness;
725 XPoint points [4];
726
727 if (!erase_p && down_p)
728 {
729 GC temp;
730 temp = top_gc;
731 top_gc = bottom_gc;
732 bottom_gc = temp;
733 }
734
735 points [0].x = x;
736 points [0].y = y + height / 2;
737 points [1].x = x + thickness;
738 points [1].y = y + height / 2;
739 points [2].x = x + width / 2;
740 points [2].y = y + thickness;
741 points [3].x = x + width / 2;
742 points [3].y = y;
743 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
744 points [0].x = x + width / 2;
745 points [0].y = y;
746 points [1].x = x + width / 2;
747 points [1].y = y + thickness;
748 points [2].x = x + width - thickness;
749 points [2].y = y + height / 2;
750 points [3].x = x + width;
751 points [3].y = y + height / 2;
752 XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin);
753 points [0].x = x;
754 points [0].y = y + height / 2;
755 points [1].x = x + thickness;
756 points [1].y = y + height / 2;
757 points [2].x = x + width / 2;
758 points [2].y = y + height - thickness;
759 points [3].x = x + width / 2;
760 points [3].y = y + height;
761 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
762 points [0].x = x + width / 2;
763 points [0].y = y + height;
764 points [1].x = x + width / 2;
765 points [1].y = y + height - thickness;
766 points [2].x = x + width - thickness;
767 points [2].y = y + height / 2;
768 points [3].x = x + width;
769 points [3].y = y + height / 2;
770 XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin);
771 }
772
773
774 /* Draw a toggle button on widget MW, X window WINDOW. X/Y is the
775 top-left corner of the menu item. SELECTED_P non-zero means the
776 toggle button is selected. */
777
778 static void
779 draw_toggle (XlwMenuWidget mw, Window window, int x, int y, int selected_p)
780 {
781 int width, height;
782
783 width = toggle_button_width (mw);
784 height = width;
785 x += mw->menu.horizontal_spacing;
786 y += (MENU_FONT_ASCENT (mw) - height) / 2;
787 draw_shadow_rectangle (mw, window, x, y, width, height, False, selected_p);
788 }
789
790
791 /* Draw a radio button on widget MW, X window WINDOW. X/Y is the
792 top-left corner of the menu item. SELECTED_P non-zero means the
793 toggle button is selected. */
794
795 static void
796 draw_radio (XlwMenuWidget mw, Window window, int x, int y, int selected_p)
797 {
798 int width, height;
799
800 width = radio_button_width (mw);
801 height = width;
802 x += mw->menu.horizontal_spacing;
803 y += (MENU_FONT_ASCENT (mw) - height) / 2;
804 draw_shadow_rhombus (mw, window, x, y, width, height, False, selected_p);
805 }
806
807
808 /* Draw a menu separator on widget MW, X window WINDOW. X/Y is the
809 top-left corner of the menu item. WIDTH is the width of the
810 separator to draw. TYPE is the separator type. */
811
812 static void
813 draw_separator (XlwMenuWidget mw,
814 Window window,
815 int x,
816 int y,
817 int width,
818 enum menu_separator type)
819 {
820 Display *dpy = XtDisplay (mw);
821 XGCValues xgcv;
822
823 switch (type)
824 {
825 case SEPARATOR_NO_LINE:
826 break;
827
828 case SEPARATOR_SINGLE_LINE:
829 XDrawLine (dpy, window, mw->menu.foreground_gc,
830 x, y, x + width, y);
831 break;
832
833 case SEPARATOR_DOUBLE_LINE:
834 draw_separator (mw, window, x, y, width, SEPARATOR_SINGLE_LINE);
835 draw_separator (mw, window, x, y + 2, width, SEPARATOR_SINGLE_LINE);
836 break;
837
838 case SEPARATOR_SINGLE_DASHED_LINE:
839 xgcv.line_style = LineOnOffDash;
840 XChangeGC (dpy, mw->menu.foreground_gc, GCLineStyle, &xgcv);
841 XDrawLine (dpy, window, mw->menu.foreground_gc,
842 x, y, x + width, y);
843 xgcv.line_style = LineSolid;
844 XChangeGC (dpy, mw->menu.foreground_gc, GCLineStyle, &xgcv);
845 break;
846
847 case SEPARATOR_DOUBLE_DASHED_LINE:
848 draw_separator (mw, window, x, y, width,
849 SEPARATOR_SINGLE_DASHED_LINE);
850 draw_separator (mw, window, x, y + 2, width,
851 SEPARATOR_SINGLE_DASHED_LINE);
852 break;
853
854 case SEPARATOR_SHADOW_ETCHED_IN:
855 XDrawLine (dpy, window, mw->menu.shadow_bottom_gc,
856 x, y, x + width, y);
857 XDrawLine (dpy, window, mw->menu.shadow_top_gc,
858 x, y + 1, x + width, y + 1);
859 break;
860
861 case SEPARATOR_SHADOW_ETCHED_OUT:
862 XDrawLine (dpy, window, mw->menu.shadow_top_gc,
863 x, y, x + width, y);
864 XDrawLine (dpy, window, mw->menu.shadow_bottom_gc,
865 x, y + 1, x + width, y + 1);
866 break;
867
868 case SEPARATOR_SHADOW_ETCHED_IN_DASH:
869 xgcv.line_style = LineOnOffDash;
870 XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
871 XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
872 draw_separator (mw, window, x, y, width, SEPARATOR_SHADOW_ETCHED_IN);
873 xgcv.line_style = LineSolid;
874 XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
875 XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
876 break;
877
878 case SEPARATOR_SHADOW_ETCHED_OUT_DASH:
879 xgcv.line_style = LineOnOffDash;
880 XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
881 XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
882 draw_separator (mw, window, x, y, width, SEPARATOR_SHADOW_ETCHED_OUT);
883 xgcv.line_style = LineSolid;
884 XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
885 XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
886 break;
887
888 case SEPARATOR_SHADOW_DOUBLE_ETCHED_IN:
889 draw_separator (mw, window, x, y, width, SEPARATOR_SHADOW_ETCHED_IN);
890 draw_separator (mw, window, x, y + 3, width, SEPARATOR_SHADOW_ETCHED_IN);
891 break;
892
893 case SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT:
894 draw_separator (mw, window, x, y, width,
895 SEPARATOR_SHADOW_ETCHED_OUT);
896 draw_separator (mw, window, x, y + 3, width,
897 SEPARATOR_SHADOW_ETCHED_OUT);
898 break;
899
900 case SEPARATOR_SHADOW_DOUBLE_ETCHED_IN_DASH:
901 xgcv.line_style = LineOnOffDash;
902 XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
903 XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
904 draw_separator (mw, window, x, y, width,
905 SEPARATOR_SHADOW_DOUBLE_ETCHED_IN);
906 xgcv.line_style = LineSolid;
907 XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
908 XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
909 break;
910
911 case SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT_DASH:
912 xgcv.line_style = LineOnOffDash;
913 XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
914 XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
915 draw_separator (mw, window, x, y, width,
916 SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT);
917 xgcv.line_style = LineSolid;
918 XChangeGC (dpy, mw->menu.shadow_bottom_gc, GCLineStyle, &xgcv);
919 XChangeGC (dpy, mw->menu.shadow_top_gc, GCLineStyle, &xgcv);
920 break;
921
922 default:
923 abort ();
924 }
925 }
926
927
928 /* Return the pixel height of menu separator SEPARATOR. */
929
930 static int
931 separator_height (enum menu_separator separator)
932 {
933 switch (separator)
934 {
935 case SEPARATOR_NO_LINE:
936 return 2;
937
938 case SEPARATOR_SINGLE_LINE:
939 case SEPARATOR_SINGLE_DASHED_LINE:
940 return 1;
941
942 case SEPARATOR_DOUBLE_LINE:
943 case SEPARATOR_DOUBLE_DASHED_LINE:
944 return 3;
945
946 case SEPARATOR_SHADOW_ETCHED_IN:
947 case SEPARATOR_SHADOW_ETCHED_OUT:
948 case SEPARATOR_SHADOW_ETCHED_IN_DASH:
949 case SEPARATOR_SHADOW_ETCHED_OUT_DASH:
950 return 2;
951
952 case SEPARATOR_SHADOW_DOUBLE_ETCHED_IN:
953 case SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT:
954 case SEPARATOR_SHADOW_DOUBLE_ETCHED_IN_DASH:
955 case SEPARATOR_SHADOW_DOUBLE_ETCHED_OUT_DASH:
956 return 5;
957
958 default:
959 abort ();
960 }
961 }
962
963
964 /* Display the menu item and increment where.x and where.y to show how large
965 the menu item was. */
966
967 static void
968 display_menu_item (XlwMenuWidget mw,
969 widget_value* val,
970 window_state* ws,
971 XPoint* where,
972 Boolean highlighted_p,
973 Boolean horizontal_p,
974 Boolean just_compute_p)
975 {
976 GC deco_gc;
977 GC text_gc;
978 int font_height = MENU_FONT_HEIGHT (mw);
979 int font_ascent = MENU_FONT_ASCENT (mw);
980 int shadow = mw->menu.shadow_thickness;
981 int margin = mw->menu.margin;
982 int h_spacing = mw->menu.horizontal_spacing;
983 int v_spacing = mw->menu.vertical_spacing;
984 int label_width;
985 int rest_width;
986 int button_width;
987 int height;
988 int width;
989 enum menu_separator separator;
990 int separator_p = lw_separator_p (val->name, &separator, 0);
991 #ifdef HAVE_XFT
992 XftColor *xftfg;
993 #endif
994
995 /* compute the sizes of the item */
996 size_menu_item (mw, val, horizontal_p, &label_width, &rest_width,
997 &button_width, &height);
998
999 if (horizontal_p)
1000 width = label_width + rest_width;
1001 else
1002 {
1003 label_width = ws->label_width;
1004 width = ws->width - 2 * shadow;
1005 }
1006
1007 /* Only highlight an enabled item that has a callback. */
1008 if (highlighted_p)
1009 if (!val->enabled || !(val->call_data || val->contents))
1010 highlighted_p = 0;
1011
1012 /* do the drawing. */
1013 if (!just_compute_p)
1014 {
1015 /* Add the shadow border of the containing menu */
1016 int x = where->x + shadow;
1017 int y = where->y + shadow;
1018
1019 if (horizontal_p)
1020 {
1021 x += margin;
1022 y += margin;
1023 }
1024
1025 /* pick the foreground and background GC. */
1026 if (val->enabled)
1027 text_gc = mw->menu.foreground_gc;
1028 else
1029 text_gc = mw->menu.disabled_gc;
1030 deco_gc = mw->menu.foreground_gc;
1031 #ifdef HAVE_XFT
1032 xftfg = val->enabled ? &mw->menu.xft_fg : &mw->menu.xft_disabled_fg;
1033 #endif
1034
1035 if (separator_p)
1036 {
1037 draw_separator (mw, ws->pixmap, x, y, width, separator);
1038 }
1039 else
1040 {
1041 int x_offset = x + h_spacing + shadow;
1042 char* display_string = resource_widget_value (mw, val);
1043 draw_shadow_rectangle (mw, ws->pixmap, x, y, width, height, True,
1044 False);
1045
1046 /* Deal with centering a menu title. */
1047 if (!horizontal_p && !val->contents && !val->call_data)
1048 {
1049 int l = string_width (mw, display_string);
1050
1051 if (width > l)
1052 x_offset = (width - l) >> 1;
1053 }
1054 else if (!horizontal_p && ws->button_width)
1055 x_offset += ws->button_width;
1056
1057
1058 #ifdef HAVE_XFT
1059 if (ws->xft_draw)
1060 {
1061 int draw_y = y + v_spacing + shadow;
1062 XftDrawStringUtf8 (ws->xft_draw, xftfg,
1063 mw->menu.xft_font,
1064 x_offset, draw_y + font_ascent,
1065 (unsigned char *) display_string,
1066 strlen (display_string));
1067 }
1068 else
1069 #endif
1070 #ifdef HAVE_X_I18N
1071 if (mw->menu.fontSet)
1072 XmbDrawString (XtDisplay (mw), ws->pixmap, mw->menu.fontSet,
1073 text_gc, x_offset,
1074 y + v_spacing + shadow + font_ascent,
1075 display_string, strlen (display_string));
1076 else
1077 #endif
1078 XDrawString (XtDisplay (mw), ws->pixmap,
1079 text_gc, x_offset,
1080 y + v_spacing + shadow + font_ascent,
1081 display_string, strlen (display_string));
1082
1083 if (!horizontal_p)
1084 {
1085 if (val->button_type == BUTTON_TYPE_TOGGLE)
1086 draw_toggle (mw, ws->pixmap, x, y + v_spacing + shadow,
1087 val->selected);
1088 else if (val->button_type == BUTTON_TYPE_RADIO)
1089 draw_radio (mw, ws->pixmap, x, y + v_spacing + shadow,
1090 val->selected);
1091
1092 if (val->contents)
1093 {
1094 int a_w = arrow_width (mw);
1095 draw_arrow (mw, ws->pixmap, deco_gc,
1096 x + width - a_w
1097 - mw->menu.horizontal_spacing
1098 - mw->menu.shadow_thickness,
1099 y + v_spacing + shadow, a_w,
1100 highlighted_p);
1101 }
1102 else if (val->key)
1103 {
1104 #ifdef HAVE_XFT
1105 if (ws->xft_draw)
1106 {
1107 int draw_x = ws->width - ws->max_rest_width
1108 + mw->menu.arrow_spacing;
1109 int draw_y = y + v_spacing + shadow + font_ascent;
1110 XftDrawStringUtf8 (ws->xft_draw, xftfg,
1111 mw->menu.xft_font,
1112 draw_x, draw_y,
1113 (unsigned char *) val->key,
1114 strlen (val->key));
1115 }
1116 else
1117 #endif
1118 #ifdef HAVE_X_I18N
1119 if (mw->menu.fontSet)
1120 XmbDrawString (XtDisplay (mw), ws->pixmap,
1121 mw->menu.fontSet,
1122 text_gc,
1123 x + label_width + mw->menu.arrow_spacing,
1124 y + v_spacing + shadow + font_ascent,
1125 val->key, strlen (val->key));
1126 else
1127 #endif
1128 XDrawString (XtDisplay (mw), ws->pixmap,
1129 text_gc,
1130 x + label_width + mw->menu.arrow_spacing,
1131 y + v_spacing + shadow + font_ascent,
1132 val->key, strlen (val->key));
1133 }
1134 }
1135 else
1136 {
1137 XDrawRectangle (XtDisplay (mw), ws->pixmap,
1138 mw->menu.background_gc,
1139 x + shadow, y + shadow,
1140 label_width + h_spacing - 1,
1141 font_height + 2 * v_spacing - 1);
1142 draw_shadow_rectangle (mw, ws->pixmap, x, y, width, height,
1143 True, False);
1144 }
1145
1146 if (highlighted_p)
1147 draw_shadow_rectangle (mw, ws->pixmap, x, y, width, height, False,
1148 False);
1149 }
1150 }
1151
1152 where->x += width;
1153 where->y += height;
1154 }
1155
1156 static void
1157 display_menu (XlwMenuWidget mw,
1158 int level,
1159 Boolean just_compute_p,
1160 XPoint *highlighted_pos,
1161 XPoint *hit,
1162 widget_value **hit_return)
1163 {
1164 widget_value* val;
1165 widget_value* following_item;
1166 window_state* ws;
1167 XPoint where;
1168 int horizontal_p = mw->menu.horizontal && (level == 0);
1169 int highlighted_p;
1170 int no_return = 0;
1171 enum menu_separator separator;
1172
1173 if (level >= mw->menu.old_depth)
1174 abort_gracefully ((Widget) mw);
1175
1176 if (level < mw->menu.old_depth - 1)
1177 following_item = mw->menu.old_stack [level + 1];
1178 else
1179 following_item = NULL;
1180
1181 if (hit)
1182 *hit_return = NULL;
1183
1184 where.x = 0;
1185 where.y = 0;
1186
1187 ws = &mw->menu.windows [level];
1188
1189 if (!just_compute_p)
1190 XFillRectangle (XtDisplay (mw), ws->pixmap, mw->menu.background_gc,
1191 0, 0, ws->width, ws->height);
1192
1193 for (val = mw->menu.old_stack [level]->contents; val; val = val->next)
1194 {
1195 highlighted_p = val == following_item;
1196 if (highlighted_p && highlighted_pos)
1197 {
1198 if (horizontal_p)
1199 highlighted_pos->x = where.x;
1200 else
1201 highlighted_pos->y = where.y;
1202 }
1203
1204 display_menu_item (mw, val, ws, &where, highlighted_p, horizontal_p,
1205 just_compute_p);
1206
1207 if (highlighted_p && highlighted_pos)
1208 {
1209 if (horizontal_p)
1210 highlighted_pos->y = where.y;
1211 else
1212 highlighted_pos->x = where.x;
1213 }
1214
1215 if (hit
1216 && !*hit_return
1217 && (horizontal_p ? hit->x < where.x : hit->y < where.y)
1218 && !lw_separator_p (val->name, &separator, 0)
1219 && !no_return)
1220 {
1221 if (val->enabled)
1222 *hit_return = val;
1223 else
1224 no_return = 1;
1225 if (mw->menu.inside_entry != val)
1226 {
1227 if (mw->menu.inside_entry)
1228 XtCallCallbackList ((Widget)mw, mw->menu.leave,
1229 (XtPointer) mw->menu.inside_entry);
1230 mw->menu.inside_entry = val;
1231 XtCallCallbackList ((Widget)mw, mw->menu.enter,
1232 (XtPointer) mw->menu.inside_entry);
1233 }
1234 }
1235
1236 if (horizontal_p)
1237 where.y = 0;
1238 else
1239 where.x = 0;
1240 }
1241
1242 if (!just_compute_p)
1243 {
1244 draw_shadow_rectangle (mw, ws->pixmap, 0, 0, ws->width, ws->height,
1245 False, False);
1246 XCopyArea (XtDisplay (mw), ws->pixmap, ws->window,
1247 mw->menu.foreground_gc, 0, 0, ws->width, ws->height, 0, 0);
1248 }
1249 }
1250
1251 \f/* Motion code */
1252 static void
1253 set_new_state (XlwMenuWidget mw, widget_value *val, int level)
1254 {
1255 int i;
1256
1257 mw->menu.new_depth = 0;
1258 for (i = 0; i < level; i++)
1259 push_new_stack (mw, mw->menu.old_stack [i]);
1260 push_new_stack (mw, val);
1261 }
1262
1263 static void
1264 expose_cb (Widget widget,
1265 XtPointer closure,
1266 XEvent* event,
1267 Boolean* continue_to_dispatch)
1268 {
1269 XlwMenuWidget mw = (XlwMenuWidget) closure;
1270 int i;
1271
1272 *continue_to_dispatch = False;
1273 for (i = 0; i < mw->menu.windows_length; ++i)
1274 if (mw->menu.windows [i].w == widget) break;
1275 if (i < mw->menu.windows_length && i < mw->menu.old_depth)
1276 display_menu (mw, i, False, NULL, NULL, NULL);
1277 }
1278
1279 static void
1280 set_window_type (Widget w, XlwMenuWidget mw)
1281 {
1282 int popup_menu_p = mw->menu.top_depth == 1;
1283 Atom type = XInternAtom (XtDisplay (w),
1284 popup_menu_p
1285 ? "_NET_WM_WINDOW_TYPE_POPUP_MENU"
1286 : "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU",
1287 False);
1288
1289 XChangeProperty (XtDisplay (w), XtWindow (w),
1290 XInternAtom (XtDisplay (w), "_NET_WM_WINDOW_TYPE", False),
1291 XA_ATOM, 32, PropModeReplace,
1292 (unsigned char *)&type, 1);
1293 }
1294
1295
1296 static void
1297 make_windows_if_needed (XlwMenuWidget mw, int n)
1298 {
1299 int i;
1300 int start_at;
1301 window_state* windows;
1302
1303 if (mw->menu.windows_length >= n)
1304 return;
1305
1306 if (!mw->menu.windows)
1307 {
1308 mw->menu.windows =
1309 (window_state*)XtMalloc (n * sizeof (window_state));
1310 start_at = 0;
1311 }
1312 else
1313 {
1314 mw->menu.windows =
1315 (window_state*)XtRealloc ((char*)mw->menu.windows,
1316 n * sizeof (window_state));
1317 start_at = mw->menu.windows_length;
1318 }
1319 mw->menu.windows_length = n;
1320
1321 windows = mw->menu.windows;
1322
1323 for (i = start_at; i < n; i++)
1324 {
1325 Arg av[10];
1326 int ac = 0;
1327 windows [i].x = 0;
1328 windows [i].y = 0;
1329 windows [i].width = 1;
1330 windows [i].height = 1;
1331 windows [i].max_rest_width = 0;
1332 XtSetArg (av[ac], XtNwidth, 1); ++ac;
1333 XtSetArg (av[ac], XtNheight, 1); ++ac;
1334 XtSetArg (av[ac], XtNsaveUnder, True); ++ac;
1335 XtSetArg (av[ac], XtNbackground, mw->core.background_pixel); ++ac;
1336 XtSetArg (av[ac], XtNborderColor, mw->core.border_pixel); ++ac;
1337 XtSetArg (av[ac], XtNborderWidth, mw->core.border_width); ++ac;
1338 XtSetArg (av[ac], XtNcursor, mw->menu.cursor_shape); ++ac;
1339 windows [i].w =
1340 XtCreatePopupShell ("sub", overrideShellWidgetClass,
1341 (Widget) mw, av, ac);
1342 XtRealizeWidget (windows [i].w);
1343 XtAddEventHandler (windows [i].w, ExposureMask, False, expose_cb, mw);
1344 windows [i].window = XtWindow (windows [i].w);
1345 windows [i].pixmap = None;
1346 #ifdef HAVE_XFT
1347 windows [i].xft_draw = 0;
1348 #endif
1349 set_window_type (windows [i].w, mw);
1350 }
1351 XFlush (XtDisplay (mw));
1352 }
1353
1354 /* Value is non-zero if WINDOW is part of menu bar widget W. */
1355
1356 int
1357 xlwmenu_window_p (Widget w, Window window)
1358 {
1359 XlwMenuWidget mw = (XlwMenuWidget) w;
1360 int i;
1361
1362 for (i = 0; i < mw->menu.windows_length; ++i)
1363 if (window == mw->menu.windows[i].window)
1364 break;
1365
1366 return i < mw->menu.windows_length;
1367 }
1368
1369 /* Make the window fit in the screen */
1370 static void
1371 fit_to_screen (XlwMenuWidget mw,
1372 window_state *ws,
1373 window_state *previous_ws,
1374 Boolean horizontal_p)
1375 {
1376 unsigned int screen_width = WidthOfScreen (XtScreen (mw));
1377 unsigned int screen_height = HeightOfScreen (XtScreen (mw));
1378 /* 1 if we are unable to avoid an overlap between
1379 this menu and the parent menu in the X dimension. */
1380 int horizontal_overlap = 0;
1381
1382 if (ws->x < 0)
1383 ws->x = 0;
1384 else if (ws->x + ws->width > screen_width)
1385 {
1386 if (!horizontal_p)
1387 /* The addition of shadow-thickness for a sub-menu's position is
1388 to reflect a similar adjustment when the menu is displayed to
1389 the right of the invoking menu-item; it makes the sub-menu
1390 look more `attached' to the menu-item. */
1391 ws->x = previous_ws->x - ws->width + mw->menu.shadow_thickness;
1392 else
1393 ws->x = screen_width - ws->width;
1394 if (ws->x < 0)
1395 {
1396 ws->x = 0;
1397 horizontal_overlap = 1;
1398 }
1399 }
1400 /* If we overlap in X, try to avoid overlap in Y. */
1401 if (horizontal_overlap
1402 && ws->y < previous_ws->y + previous_ws->height
1403 && previous_ws->y < ws->y + ws->height)
1404 {
1405 /* Put this menu right below or right above PREVIOUS_WS
1406 if there's room. */
1407 if (previous_ws->y + previous_ws->height + ws->height < screen_height)
1408 ws->y = previous_ws->y + previous_ws->height;
1409 else if (previous_ws->y - ws->height > 0)
1410 ws->y = previous_ws->y - ws->height;
1411 }
1412
1413 if (ws->y < 0)
1414 ws->y = 0;
1415 else if (ws->y + ws->height > screen_height)
1416 {
1417 if (horizontal_p)
1418 ws->y = previous_ws->y - ws->height;
1419 else
1420 ws->y = screen_height - ws->height;
1421 if (ws->y < 0)
1422 ws->y = 0;
1423 }
1424 }
1425
1426 static void
1427 create_pixmap_for_menu (window_state* ws, XlwMenuWidget mw)
1428 {
1429 if (ws->pixmap != None)
1430 {
1431 XFreePixmap (XtDisplay (ws->w), ws->pixmap);
1432 ws->pixmap = None;
1433 }
1434 ws->pixmap = XCreatePixmap (XtDisplay (ws->w), ws->window,
1435 ws->width, ws->height,
1436 DefaultDepthOfScreen (XtScreen (ws->w)));
1437 #ifdef HAVE_XFT
1438 if (ws->xft_draw)
1439 XftDrawDestroy (ws->xft_draw);
1440 if (mw->menu.xft_font)
1441 {
1442 int screen = XScreenNumberOfScreen (mw->core.screen);
1443 ws->xft_draw = XftDrawCreate (XtDisplay (ws->w),
1444 ws->pixmap,
1445 DefaultVisual (XtDisplay (ws->w), screen),
1446 mw->core.colormap);
1447 }
1448 else
1449 ws->xft_draw = 0;
1450 #endif
1451 }
1452
1453 /* Updates old_stack from new_stack and redisplays. */
1454 static void
1455 remap_menubar (XlwMenuWidget mw)
1456 {
1457 int i;
1458 int last_same;
1459 XPoint selection_position;
1460 int old_depth = mw->menu.old_depth;
1461 int new_depth = mw->menu.new_depth;
1462 widget_value** old_stack;
1463 widget_value** new_stack;
1464 window_state* windows;
1465 widget_value* old_selection;
1466 widget_value* new_selection;
1467
1468 /* Check that enough windows and old_stack are ready. */
1469 make_windows_if_needed (mw, new_depth);
1470 make_old_stack_space (mw, new_depth);
1471 windows = mw->menu.windows;
1472 old_stack = mw->menu.old_stack;
1473 new_stack = mw->menu.new_stack;
1474
1475 /* compute the last identical different entry */
1476 for (i = 1; i < old_depth && i < new_depth; i++)
1477 if (old_stack [i] != new_stack [i])
1478 break;
1479 last_same = i - 1;
1480
1481 /* Memorize the previously selected item to be able to refresh it */
1482 old_selection = last_same + 1 < old_depth ? old_stack [last_same + 1] : NULL;
1483 if (old_selection && !old_selection->enabled)
1484 old_selection = NULL;
1485 new_selection = last_same + 1 < new_depth ? new_stack [last_same + 1] : NULL;
1486 if (new_selection && !new_selection->enabled)
1487 new_selection = NULL;
1488
1489 /* Call callback when the hightlighted item changes. */
1490 if (old_selection || new_selection)
1491 XtCallCallbackList ((Widget)mw, mw->menu.highlight,
1492 (XtPointer) new_selection);
1493
1494 /* updates old_state from new_state. It has to be done now because
1495 display_menu (called below) uses the old_stack to know what to display. */
1496 for (i = last_same + 1; i < new_depth; i++)
1497 {
1498 XtPopdown (mw->menu.windows [i].w);
1499 old_stack [i] = new_stack [i];
1500 }
1501 mw->menu.old_depth = new_depth;
1502
1503 /* refresh the last selection */
1504 selection_position.x = 0;
1505 selection_position.y = 0;
1506 display_menu (mw, last_same, new_selection == old_selection,
1507 &selection_position, NULL, NULL);
1508
1509 /* Now place the new menus. */
1510 for (i = last_same + 1; i < new_depth && new_stack[i]->contents; i++)
1511 {
1512 window_state *previous_ws = &windows[i - 1];
1513 window_state *ws = &windows[i];
1514
1515 ws->x = (previous_ws->x + selection_position.x
1516 + mw->menu.shadow_thickness);
1517 if (mw->menu.horizontal && i == 1)
1518 ws->x += mw->menu.margin;
1519
1520 #if 0
1521 if (!mw->menu.horizontal || i > 1)
1522 ws->x += mw->menu.shadow_thickness;
1523 #endif
1524
1525 ws->y = (previous_ws->y + selection_position.y
1526 + mw->menu.shadow_thickness);
1527 if (mw->menu.horizontal && i == 1)
1528 ws->y += mw->menu.margin;
1529
1530 size_menu (mw, i);
1531
1532 fit_to_screen (mw, ws, previous_ws, mw->menu.horizontal && i == 1);
1533
1534 create_pixmap_for_menu (ws, mw);
1535 XtMoveWidget (ws->w, ws->x, ws->y);
1536 XtPopup (ws->w, XtGrabNone);
1537 XtResizeWidget (ws->w, ws->width, ws->height,
1538 mw->core.border_width);
1539 XtResizeWindow (ws->w);
1540 display_menu (mw, i, False, &selection_position, NULL, NULL);
1541 }
1542
1543 /* unmap the menus that popped down */
1544 for (i = new_depth - 1; i < old_depth; i++)
1545 if (i >= new_depth || (i > 0 && !new_stack[i]->contents))
1546 XtPopdown (windows[i].w);
1547 }
1548
1549 static Boolean
1550 motion_event_is_in_menu (XlwMenuWidget mw,
1551 XMotionEvent *ev,
1552 int level,
1553 XPoint *relative_pos)
1554 {
1555 window_state* ws = &mw->menu.windows [level];
1556 int shadow = level == 0 ? 0 : mw->menu.shadow_thickness;
1557 int x = ws->x + shadow;
1558 int y = ws->y + shadow;
1559 relative_pos->x = ev->x_root - x;
1560 relative_pos->y = ev->y_root - y;
1561 return (x - shadow < ev->x_root && ev->x_root < x + ws->width
1562 && y - shadow < ev->y_root && ev->y_root < y + ws->height);
1563 }
1564
1565 static Boolean
1566 map_event_to_widget_value (XlwMenuWidget mw,
1567 XMotionEvent *ev,
1568 widget_value **val,
1569 int *level)
1570 {
1571 int i;
1572 XPoint relative_pos;
1573 window_state* ws;
1574 int inside = 0;
1575
1576 *val = NULL;
1577
1578 /* Find the window */
1579 for (i = mw->menu.old_depth - 1; i >= 0; i--)
1580 {
1581 ws = &mw->menu.windows [i];
1582 if (ws && motion_event_is_in_menu (mw, ev, i, &relative_pos))
1583 {
1584 inside = 1;
1585 display_menu (mw, i, True, NULL, &relative_pos, val);
1586
1587 if (*val)
1588 {
1589 *level = i + 1;
1590 return True;
1591 }
1592 }
1593 }
1594
1595 if (!inside)
1596 {
1597 if (mw->menu.inside_entry != NULL)
1598 XtCallCallbackList ((Widget)mw, mw->menu.leave,
1599 (XtPointer) mw->menu.inside_entry);
1600 mw->menu.inside_entry = NULL;
1601 }
1602
1603 return False;
1604 }
1605
1606 \f/* Procedures */
1607 static void
1608 make_drawing_gcs (XlwMenuWidget mw)
1609 {
1610 XGCValues xgcv;
1611 float scale;
1612 XtGCMask mask = GCForeground | GCBackground;
1613
1614 #ifdef HAVE_X_I18N
1615 if (!mw->menu.fontSet && mw->menu.font)
1616 {
1617 xgcv.font = mw->menu.font->fid;
1618 mask |= GCFont;
1619 }
1620 #else
1621 if (mw->menu.font)
1622 {
1623 xgcv.font = mw->menu.font->fid;
1624 mask |= GCFont;
1625 }
1626 #endif
1627 xgcv.foreground = mw->menu.foreground;
1628 xgcv.background = mw->core.background_pixel;
1629 mw->menu.foreground_gc = XtGetGC ((Widget)mw, mask, &xgcv);
1630
1631 xgcv.foreground = mw->menu.button_foreground;
1632 mw->menu.button_gc = XtGetGC ((Widget)mw, mask, &xgcv);
1633
1634 xgcv.background = mw->core.background_pixel;
1635
1636 #define BRIGHTNESS(color) (((color) & 0xff) + (((color) >> 8) & 0xff) + (((color) >> 16) & 0xff))
1637
1638 /* Allocate color for disabled menu-items. */
1639 mw->menu.disabled_foreground = mw->menu.foreground;
1640 if (BRIGHTNESS(mw->menu.foreground) < BRIGHTNESS(mw->core.background_pixel))
1641 scale = 2.3;
1642 else
1643 scale = 0.55;
1644
1645 x_alloc_lighter_color_for_widget ((Widget) mw, XtDisplay ((Widget) mw),
1646 mw->core.colormap,
1647 &mw->menu.disabled_foreground,
1648 scale,
1649 0x8000);
1650
1651 if (mw->menu.foreground == mw->menu.disabled_foreground
1652 || mw->core.background_pixel == mw->menu.disabled_foreground)
1653 {
1654 /* Too few colors, use stipple. */
1655 xgcv.foreground = mw->menu.foreground;
1656 xgcv.fill_style = FillStippled;
1657 xgcv.stipple = mw->menu.gray_pixmap;
1658 mw->menu.disabled_gc = XtGetGC ((Widget)mw, mask
1659 | GCFillStyle | GCStipple, &xgcv);
1660 }
1661 else
1662 {
1663 /* Many colors available, use disabled pixel. */
1664 xgcv.foreground = mw->menu.disabled_foreground;
1665 mw->menu.disabled_gc = XtGetGC ((Widget)mw, mask, &xgcv);
1666 }
1667
1668 xgcv.foreground = mw->menu.button_foreground;
1669 xgcv.background = mw->core.background_pixel;
1670 xgcv.fill_style = FillStippled;
1671 xgcv.stipple = mw->menu.gray_pixmap;
1672 mw->menu.inactive_button_gc = XtGetGC ((Widget)mw, mask
1673 | GCFillStyle | GCStipple, &xgcv);
1674
1675 xgcv.foreground = mw->core.background_pixel;
1676 xgcv.background = mw->menu.foreground;
1677 mw->menu.background_gc = XtGetGC ((Widget)mw, mask, &xgcv);
1678 }
1679
1680 static void
1681 release_drawing_gcs (XlwMenuWidget mw)
1682 {
1683 XtReleaseGC ((Widget) mw, mw->menu.foreground_gc);
1684 XtReleaseGC ((Widget) mw, mw->menu.button_gc);
1685 XtReleaseGC ((Widget) mw, mw->menu.disabled_gc);
1686 XtReleaseGC ((Widget) mw, mw->menu.inactive_button_gc);
1687 XtReleaseGC ((Widget) mw, mw->menu.background_gc);
1688 /* let's get some segvs if we try to use these... */
1689 mw->menu.foreground_gc = (GC) -1;
1690 mw->menu.button_gc = (GC) -1;
1691 mw->menu.disabled_gc = (GC) -1;
1692 mw->menu.inactive_button_gc = (GC) -1;
1693 mw->menu.background_gc = (GC) -1;
1694 }
1695
1696 #define MINL(x,y) ((((unsigned long) (x)) < ((unsigned long) (y))) \
1697 ? ((unsigned long) (x)) : ((unsigned long) (y)))
1698
1699 static void
1700 make_shadow_gcs (XlwMenuWidget mw)
1701 {
1702 XGCValues xgcv;
1703 unsigned long pm = 0;
1704 Display *dpy = XtDisplay ((Widget) mw);
1705 Screen *screen = XtScreen ((Widget) mw);
1706 Colormap cmap = mw->core.colormap;
1707 XColor topc, botc;
1708 int top_frobbed = 0, bottom_frobbed = 0;
1709
1710 mw->menu.free_top_shadow_color_p = 0;
1711 mw->menu.free_bottom_shadow_color_p = 0;
1712
1713 if (mw->menu.top_shadow_color == -1)
1714 mw->menu.top_shadow_color = mw->core.background_pixel;
1715 else
1716 mw->menu.top_shadow_color = mw->menu.top_shadow_color;
1717
1718 if (mw->menu.bottom_shadow_color == -1)
1719 mw->menu.bottom_shadow_color = mw->menu.foreground;
1720 else
1721 mw->menu.bottom_shadow_color = mw->menu.bottom_shadow_color;
1722
1723 if (mw->menu.top_shadow_color == mw->core.background_pixel ||
1724 mw->menu.top_shadow_color == mw->menu.foreground)
1725 {
1726 topc.pixel = mw->core.background_pixel;
1727 #ifdef emacs
1728 if (x_alloc_lighter_color_for_widget ((Widget) mw, dpy, cmap,
1729 &topc.pixel,
1730 1.2, 0x8000))
1731 #else
1732 XQueryColor (dpy, cmap, &topc);
1733 /* don't overflow/wrap! */
1734 topc.red = MINL (65535, topc.red * 1.2);
1735 topc.green = MINL (65535, topc.green * 1.2);
1736 topc.blue = MINL (65535, topc.blue * 1.2);
1737 if (XAllocColor (dpy, cmap, &topc))
1738 #endif
1739 {
1740 mw->menu.top_shadow_color = topc.pixel;
1741 mw->menu.free_top_shadow_color_p = 1;
1742 top_frobbed = 1;
1743 }
1744 }
1745 if (mw->menu.bottom_shadow_color == mw->menu.foreground ||
1746 mw->menu.bottom_shadow_color == mw->core.background_pixel)
1747 {
1748 botc.pixel = mw->core.background_pixel;
1749 #ifdef emacs
1750 if (x_alloc_lighter_color_for_widget ((Widget) mw, dpy, cmap,
1751 &botc.pixel,
1752 0.6, 0x4000))
1753 #else
1754 XQueryColor (dpy, cmap, &botc);
1755 botc.red *= 0.6;
1756 botc.green *= 0.6;
1757 botc.blue *= 0.6;
1758 if (XAllocColor (dpy, cmap, &botc))
1759 #endif
1760 {
1761 mw->menu.bottom_shadow_color = botc.pixel;
1762 mw->menu.free_bottom_shadow_color_p = 1;
1763 bottom_frobbed = 1;
1764 }
1765 }
1766
1767 if (top_frobbed && bottom_frobbed)
1768 {
1769 if (topc.pixel == botc.pixel)
1770 {
1771 if (botc.pixel == mw->menu.foreground)
1772 {
1773 if (mw->menu.free_top_shadow_color_p)
1774 {
1775 x_free_dpy_colors (dpy, screen, cmap,
1776 &mw->menu.top_shadow_color, 1);
1777 mw->menu.free_top_shadow_color_p = 0;
1778 }
1779 mw->menu.top_shadow_color = mw->core.background_pixel;
1780 }
1781 else
1782 {
1783 if (mw->menu.free_bottom_shadow_color_p)
1784 {
1785 x_free_dpy_colors (dpy, screen, cmap,
1786 &mw->menu.bottom_shadow_color, 1);
1787 mw->menu.free_bottom_shadow_color_p = 0;
1788 }
1789 mw->menu.bottom_shadow_color = mw->menu.foreground;
1790 }
1791 }
1792 }
1793
1794 if (!mw->menu.top_shadow_pixmap &&
1795 mw->menu.top_shadow_color == mw->core.background_pixel)
1796 {
1797 mw->menu.top_shadow_pixmap = mw->menu.gray_pixmap;
1798 if (mw->menu.free_top_shadow_color_p)
1799 {
1800 x_free_dpy_colors (dpy, screen, cmap, &mw->menu.top_shadow_color, 1);
1801 mw->menu.free_top_shadow_color_p = 0;
1802 }
1803 mw->menu.top_shadow_color = mw->menu.foreground;
1804 }
1805 if (!mw->menu.bottom_shadow_pixmap &&
1806 mw->menu.bottom_shadow_color == mw->core.background_pixel)
1807 {
1808 mw->menu.bottom_shadow_pixmap = mw->menu.gray_pixmap;
1809 if (mw->menu.free_bottom_shadow_color_p)
1810 {
1811 x_free_dpy_colors (dpy, screen, cmap,
1812 &mw->menu.bottom_shadow_color, 1);
1813 mw->menu.free_bottom_shadow_color_p = 0;
1814 }
1815 mw->menu.bottom_shadow_color = mw->menu.foreground;
1816 }
1817
1818 xgcv.fill_style = FillStippled;
1819 xgcv.foreground = mw->menu.top_shadow_color;
1820 xgcv.stipple = mw->menu.top_shadow_pixmap;
1821 pm = (xgcv.stipple ? GCStipple|GCFillStyle : 0);
1822 mw->menu.shadow_top_gc = XtGetGC ((Widget)mw, GCForeground | pm, &xgcv);
1823
1824 xgcv.foreground = mw->menu.bottom_shadow_color;
1825 xgcv.stipple = mw->menu.bottom_shadow_pixmap;
1826 pm = (xgcv.stipple ? GCStipple|GCFillStyle : 0);
1827 mw->menu.shadow_bottom_gc = XtGetGC ((Widget)mw, GCForeground | pm, &xgcv);
1828 }
1829
1830
1831 static void
1832 release_shadow_gcs (XlwMenuWidget mw)
1833 {
1834 Display *dpy = XtDisplay ((Widget) mw);
1835 Screen *screen = XtScreen ((Widget) mw);
1836 Colormap cmap = mw->core.colormap;
1837 Pixel px[2];
1838 int i = 0;
1839
1840 if (mw->menu.free_top_shadow_color_p)
1841 px[i++] = mw->menu.top_shadow_color;
1842 if (mw->menu.free_bottom_shadow_color_p)
1843 px[i++] = mw->menu.bottom_shadow_color;
1844 if (i > 0)
1845 x_free_dpy_colors (dpy, screen, cmap, px, i);
1846
1847 XtReleaseGC ((Widget) mw, mw->menu.shadow_top_gc);
1848 XtReleaseGC ((Widget) mw, mw->menu.shadow_bottom_gc);
1849 }
1850
1851 #ifdef HAVE_XFT
1852 static XftFont *
1853 getDefaultXftFont (XlwMenuWidget mw)
1854 {
1855 int screen = XScreenNumberOfScreen (mw->core.screen);
1856 return XftFontOpenName (XtDisplay (mw), screen, DEFAULT_FONTNAME);
1857 }
1858
1859 static int
1860 openXftFont (XlwMenuWidget mw)
1861 {
1862 char *fname = mw->menu.fontName;
1863
1864 mw->menu.xft_font = 0;
1865 mw->menu.default_face = fname && strcmp (fname, DEFAULT_FONTNAME) == 0;
1866
1867 if (fname && strcmp (fname, "none") != 0)
1868 {
1869 int screen = XScreenNumberOfScreen (mw->core.screen);
1870 int len = strlen (fname), i = len-1;
1871 /* Try to convert Gtk-syntax (Sans 9) to Xft syntax Sans-9. */
1872 while (i > 0 && isdigit (fname[i]))
1873 --i;
1874 if (fname[i] == ' ')
1875 {
1876 fname = xstrdup (mw->menu.fontName);
1877 fname[i] = '-';
1878 }
1879
1880 mw->menu.font = XLoadQueryFont (XtDisplay (mw), fname);
1881 if (!mw->menu.font)
1882 {
1883 mw->menu.xft_font = XftFontOpenName (XtDisplay (mw), screen, fname);
1884 if (!mw->menu.xft_font)
1885 {
1886 fprintf (stderr, "Can't find font '%s'\n", fname);
1887 mw->menu.xft_font = getDefaultXftFont (mw);
1888 }
1889 }
1890 }
1891
1892 if (fname != mw->menu.fontName) free (fname);
1893
1894 return mw->menu.xft_font != 0;
1895 }
1896 #endif
1897
1898 static void
1899 XlwMenuInitialize (Widget request, Widget w, ArgList args, Cardinal *num_args)
1900 {
1901 /* Get the GCs and the widget size */
1902 XlwMenuWidget mw = (XlwMenuWidget) w;
1903 Window window = RootWindowOfScreen (DefaultScreenOfDisplay (XtDisplay (mw)));
1904 Display* display = XtDisplay (mw);
1905
1906 #if 0
1907 widget_value *tem = (widget_value *) XtMalloc (sizeof (widget_value));
1908
1909 /* _XtCreate is freeing the object that was passed to us,
1910 so make a copy that we will actually keep. */
1911 memcpy (tem, mw->menu.contents, sizeof (widget_value));
1912 mw->menu.contents = tem;
1913 #endif
1914
1915 /* mw->menu.cursor = XCreateFontCursor (display, mw->menu.cursor_shape); */
1916 mw->menu.cursor = mw->menu.cursor_shape;
1917
1918 mw->menu.gray_pixmap
1919 = XCreatePixmapFromBitmapData (display, window, gray_bitmap_bits,
1920 gray_bitmap_width, gray_bitmap_height,
1921 (unsigned long)1, (unsigned long)0, 1);
1922
1923 #ifdef HAVE_XFT
1924 if (openXftFont (mw))
1925 ;
1926 else
1927 #endif
1928 {
1929 mw->menu.font = XLoadQueryFont (display, mw->menu.fontName);
1930 if (!mw->menu.font)
1931 {
1932 mw->menu.font = XLoadQueryFont (display, "fixed");
1933 if (!mw->menu.font)
1934 {
1935 fprintf (stderr, "Menu font fixed not found, can't continue.\n");
1936 abort ();
1937 }
1938 }
1939 }
1940
1941 #ifdef HAVE_X_I18N
1942 if (mw->menu.fontSet)
1943 mw->menu.font_extents = XExtentsOfFontSet (mw->menu.fontSet);
1944 #endif
1945
1946 make_drawing_gcs (mw);
1947 make_shadow_gcs (mw);
1948
1949 mw->menu.popped_up = False;
1950
1951 mw->menu.old_depth = 1;
1952 mw->menu.old_stack = (widget_value**)XtMalloc (sizeof (widget_value*));
1953 mw->menu.old_stack_length = 1;
1954 mw->menu.old_stack [0] = mw->menu.contents;
1955
1956 mw->menu.new_depth = 0;
1957 mw->menu.new_stack = 0;
1958 mw->menu.new_stack_length = 0;
1959 push_new_stack (mw, mw->menu.contents);
1960
1961 mw->menu.windows = (window_state*)XtMalloc (sizeof (window_state));
1962 mw->menu.windows_length = 1;
1963 mw->menu.windows [0].x = 0;
1964 mw->menu.windows [0].y = 0;
1965 mw->menu.windows [0].width = 0;
1966 mw->menu.windows [0].height = 0;
1967 mw->menu.windows [0].max_rest_width = 0;
1968 mw->menu.windows [0].pixmap = None;
1969 #ifdef HAVE_XFT
1970 mw->menu.windows [0].xft_draw = 0;
1971 #endif
1972 size_menu (mw, 0);
1973
1974 mw->core.width = mw->menu.windows [0].width;
1975 mw->core.height = mw->menu.windows [0].height;
1976 }
1977
1978 static void
1979 XlwMenuClassInitialize (void)
1980 {
1981 }
1982
1983 static void
1984 XlwMenuRealize (Widget w, Mask *valueMask, XSetWindowAttributes *attributes)
1985 {
1986 XlwMenuWidget mw = (XlwMenuWidget)w;
1987 XSetWindowAttributes xswa;
1988 int mask;
1989
1990 (*xlwMenuWidgetClass->core_class.superclass->core_class.realize)
1991 (w, valueMask, attributes);
1992
1993 xswa.save_under = True;
1994 xswa.cursor = mw->menu.cursor_shape;
1995 mask = CWSaveUnder | CWCursor;
1996 /* I sometimes get random BadCursor errors while creating the first
1997 frame on a display. I can not find their reason, but they are
1998 annoying so for now let's ignore any errors here. -- lorentey */
1999 #ifdef emacs
2000 x_catch_errors (XtDisplay (w));
2001 #endif
2002 XChangeWindowAttributes (XtDisplay (w), XtWindow (w), mask, &xswa);
2003 #ifdef emacs
2004 x_uncatch_errors ();
2005 #endif
2006
2007 mw->menu.windows [0].w = w;
2008 mw->menu.windows [0].window = XtWindow (w);
2009 mw->menu.windows [0].x = w->core.x;
2010 mw->menu.windows [0].y = w->core.y;
2011 mw->menu.windows [0].width = w->core.width;
2012 mw->menu.windows [0].height = w->core.height;
2013
2014 set_window_type (mw->menu.windows [0].w, mw);
2015 create_pixmap_for_menu (&mw->menu.windows [0], mw);
2016
2017 #ifdef HAVE_XFT
2018 if (mw->menu.xft_font)
2019 {
2020 XColor colors[3];
2021 colors[0].pixel = mw->menu.xft_fg.pixel = mw->menu.foreground;
2022 colors[1].pixel = mw->menu.xft_bg.pixel = mw->core.background_pixel;
2023 colors[2].pixel = mw->menu.xft_disabled_fg.pixel
2024 = mw->menu.disabled_foreground;
2025 XQueryColors (XtDisplay (mw), mw->core.colormap, colors, 3);
2026 mw->menu.xft_fg.color.alpha = 0xFFFF;
2027 mw->menu.xft_fg.color.red = colors[0].red;
2028 mw->menu.xft_fg.color.green = colors[0].green;
2029 mw->menu.xft_fg.color.blue = colors[0].blue;
2030 mw->menu.xft_bg.color.alpha = 0xFFFF;
2031 mw->menu.xft_bg.color.red = colors[1].red;
2032 mw->menu.xft_bg.color.green = colors[1].green;
2033 mw->menu.xft_bg.color.blue = colors[1].blue;
2034 mw->menu.xft_disabled_fg.color.alpha = 0xFFFF;
2035 mw->menu.xft_disabled_fg.color.red = colors[2].red;
2036 mw->menu.xft_disabled_fg.color.green = colors[2].green;
2037 mw->menu.xft_disabled_fg.color.blue = colors[2].blue;
2038 }
2039 #endif
2040 }
2041
2042 /* Only the toplevel menubar/popup is a widget so it's the only one that
2043 receives expose events through Xt. So we repaint all the other panes
2044 when receiving an Expose event. */
2045 static void
2046 XlwMenuRedisplay (Widget w, XEvent *ev, Region region)
2047 {
2048 XlwMenuWidget mw = (XlwMenuWidget)w;
2049
2050 /* If we have a depth beyond 1, it's because a submenu was displayed.
2051 If the submenu has been destroyed, set the depth back to 1. */
2052 if (submenu_destroyed)
2053 {
2054 mw->menu.old_depth = 1;
2055 submenu_destroyed = 0;
2056 }
2057
2058 display_menu (mw, 0, False, NULL, NULL, NULL);
2059 }
2060
2061
2062 /* Part of a hack to make the menu redisplay when a tooltip frame
2063 over a menu item is unmapped. */
2064
2065 void
2066 xlwmenu_redisplay (Widget w)
2067 {
2068 XlwMenuRedisplay (w, NULL, None);
2069 }
2070
2071 static void
2072 XlwMenuDestroy (Widget w)
2073 {
2074 int i;
2075 XlwMenuWidget mw = (XlwMenuWidget) w;
2076
2077 if (pointer_grabbed)
2078 ungrab_all ((Widget)w, CurrentTime);
2079 pointer_grabbed = 0;
2080
2081 submenu_destroyed = 1;
2082
2083 release_drawing_gcs (mw);
2084 release_shadow_gcs (mw);
2085
2086 /* this doesn't come from the resource db but is created explicitly
2087 so we must free it ourselves. */
2088 XFreePixmap (XtDisplay (mw), mw->menu.gray_pixmap);
2089 mw->menu.gray_pixmap = (Pixmap) -1;
2090
2091 #if 0
2092 /* Do free mw->menu.contents because nowadays we copy it
2093 during initialization. */
2094 XtFree (mw->menu.contents);
2095 #endif
2096
2097 /* Don't free mw->menu.contents because that comes from our creator.
2098 The `*_stack' elements are just pointers into `contents' so leave
2099 that alone too. But free the stacks themselves. */
2100 if (mw->menu.old_stack) XtFree ((char *) mw->menu.old_stack);
2101 if (mw->menu.new_stack) XtFree ((char *) mw->menu.new_stack);
2102
2103 /* Remember, you can't free anything that came from the resource
2104 database. This includes:
2105 mw->menu.cursor
2106 mw->menu.top_shadow_pixmap
2107 mw->menu.bottom_shadow_pixmap
2108 mw->menu.font
2109 Also the color cells of top_shadow_color, bottom_shadow_color,
2110 foreground, and button_foreground will never be freed until this
2111 client exits. Nice, eh?
2112 */
2113
2114 #ifdef HAVE_XFT
2115 if (mw->menu.windows [0].xft_draw)
2116 XftDrawDestroy (mw->menu.windows [0].xft_draw);
2117 if (mw->menu.xft_font)
2118 XftFontClose (XtDisplay (mw), mw->menu.xft_font);
2119 #endif
2120
2121 if (mw->menu.windows [0].pixmap != None)
2122 XFreePixmap (XtDisplay (mw), mw->menu.windows [0].pixmap);
2123 /* start from 1 because the one in slot 0 is w->core.window */
2124 for (i = 1; i < mw->menu.windows_length; i++)
2125 {
2126 if (mw->menu.windows [i].pixmap != None)
2127 XFreePixmap (XtDisplay (mw), mw->menu.windows [i].pixmap);
2128 #ifdef HAVE_XFT
2129 if (mw->menu.windows [i].xft_draw)
2130 XftDrawDestroy (mw->menu.windows [i].xft_draw);
2131 #endif
2132 }
2133
2134 if (mw->menu.windows)
2135 XtFree ((char *) mw->menu.windows);
2136 }
2137
2138 #ifdef HAVE_XFT
2139 static int
2140 fontname_changed (XlwMenuWidget newmw,
2141 XlwMenuWidget oldmw)
2142 {
2143 /* This will force a new XftFont even if the same string is set.
2144 This is good, as rendering parameters may have changed and
2145 we just want to do a redisplay. */
2146 return newmw->menu.fontName != oldmw->menu.fontName;
2147 }
2148 #endif
2149
2150 static Boolean
2151 XlwMenuSetValues (Widget current, Widget request, Widget new,
2152 ArgList args, Cardinal *num_args)
2153 {
2154 XlwMenuWidget oldmw = (XlwMenuWidget)current;
2155 XlwMenuWidget newmw = (XlwMenuWidget)new;
2156 Boolean redisplay = False;
2157 int i;
2158
2159 if (newmw->menu.contents
2160 && newmw->menu.contents->contents
2161 && newmw->menu.contents->contents->change >= VISIBLE_CHANGE)
2162 redisplay = True;
2163 /* Do redisplay if the contents are entirely eliminated. */
2164 if (newmw->menu.contents
2165 && newmw->menu.contents->contents == 0
2166 && newmw->menu.contents->change >= VISIBLE_CHANGE)
2167 redisplay = True;
2168
2169 if (newmw->core.background_pixel != oldmw->core.background_pixel
2170 || newmw->menu.foreground != oldmw->menu.foreground
2171 #ifdef HAVE_XFT
2172 || fontname_changed (newmw, oldmw)
2173 #endif
2174 #ifdef HAVE_X_I18N
2175 || newmw->menu.fontSet != oldmw->menu.fontSet
2176 || (newmw->menu.fontSet == NULL && newmw->menu.font != oldmw->menu.font)
2177 #else
2178 || newmw->menu.font != oldmw->menu.font
2179 #endif
2180 )
2181 {
2182 release_drawing_gcs (newmw);
2183 make_drawing_gcs (newmw);
2184
2185 release_shadow_gcs (newmw);
2186 /* Cause the shadow colors to be recalculated. */
2187 newmw->menu.top_shadow_color = -1;
2188 newmw->menu.bottom_shadow_color = -1;
2189 make_shadow_gcs (newmw);
2190
2191 redisplay = True;
2192
2193 if (XtIsRealized (current))
2194 /* If the menu is currently displayed, change the display. */
2195 for (i = 0; i < oldmw->menu.windows_length; i++)
2196 {
2197 XSetWindowBackground (XtDisplay (oldmw),
2198 oldmw->menu.windows [i].window,
2199 newmw->core.background_pixel);
2200 /* clear windows and generate expose events */
2201 XClearArea (XtDisplay (oldmw), oldmw->menu.windows[i].window,
2202 0, 0, 0, 0, True);
2203 }
2204 }
2205
2206 #ifdef HAVE_XFT
2207 if (fontname_changed (newmw, oldmw))
2208 {
2209 int i;
2210 int screen = XScreenNumberOfScreen (newmw->core.screen);
2211 if (newmw->menu.xft_font)
2212 XftFontClose (XtDisplay (newmw), newmw->menu.xft_font);
2213 openXftFont (newmw);
2214 for (i = 0; i < newmw->menu.windows_length; i++)
2215 {
2216 if (newmw->menu.windows [i].xft_draw)
2217 XftDrawDestroy (newmw->menu.windows [i].xft_draw);
2218 newmw->menu.windows [i].xft_draw = 0;
2219 }
2220 if (newmw->menu.xft_font)
2221 for (i = 0; i < newmw->menu.windows_length; i++)
2222 newmw->menu.windows [i].xft_draw
2223 = XftDrawCreate (XtDisplay (newmw),
2224 newmw->menu.windows [i].window,
2225 DefaultVisual (XtDisplay (newmw), screen),
2226 newmw->core.colormap);
2227 }
2228 #endif
2229 #ifdef HAVE_X_I18N
2230 if (newmw->menu.fontSet != oldmw->menu.fontSet && newmw->menu.fontSet != NULL)
2231 {
2232 redisplay = True;
2233 newmw->menu.font_extents = XExtentsOfFontSet (newmw->menu.fontSet);
2234 }
2235 #endif
2236
2237 return redisplay;
2238 }
2239
2240 static void
2241 XlwMenuResize (Widget w)
2242 {
2243 XlwMenuWidget mw = (XlwMenuWidget)w;
2244
2245 if (mw->menu.popped_up)
2246 {
2247 /* Don't allow the popup menu to resize itself. */
2248 mw->core.width = mw->menu.windows [0].width;
2249 mw->core.height = mw->menu.windows [0].height;
2250 mw->core.parent->core.width = mw->core.width;
2251 mw->core.parent->core.height = mw->core.height;
2252 }
2253 else
2254 {
2255 mw->menu.windows [0].width = mw->core.width;
2256 mw->menu.windows [0].height = mw->core.height;
2257 create_pixmap_for_menu (&mw->menu.windows [0], mw);
2258 }
2259 }
2260
2261 \f/* Action procedures */
2262 static void
2263 handle_single_motion_event (XlwMenuWidget mw, XMotionEvent *ev)
2264 {
2265 widget_value* val;
2266 int level;
2267
2268 if (!map_event_to_widget_value (mw, ev, &val, &level))
2269 pop_new_stack_if_no_contents (mw);
2270 else
2271 set_new_state (mw, val, level);
2272 remap_menubar (mw);
2273
2274 /* Sync with the display. Makes it feel better on X terms. */
2275 XSync (XtDisplay (mw), False);
2276 }
2277
2278 static void
2279 handle_motion_event (XlwMenuWidget mw, XMotionEvent *ev)
2280 {
2281 int x = ev->x_root;
2282 int y = ev->y_root;
2283 int state = ev->state;
2284 XMotionEvent oldev = *ev;
2285
2286 /* allow motion events to be generated again */
2287 if (ev->is_hint
2288 && XQueryPointer (XtDisplay (mw), ev->window,
2289 &ev->root, &ev->subwindow,
2290 &ev->x_root, &ev->y_root,
2291 &ev->x, &ev->y,
2292 &ev->state)
2293 && ev->state == state
2294 && (ev->x_root != x || ev->y_root != y))
2295 handle_single_motion_event (mw, ev);
2296 else
2297 handle_single_motion_event (mw, &oldev);
2298 }
2299
2300 static void
2301 Start (Widget w, XEvent *ev, String *params, Cardinal *num_params)
2302 {
2303 XlwMenuWidget mw = (XlwMenuWidget)w;
2304
2305 if (!mw->menu.popped_up)
2306 {
2307 menu_post_event = *ev;
2308 /* If event is set to CurrentTime, get the last known time stamp.
2309 This is for calculating if (popup) menus should stay up after
2310 a fast click. */
2311 if (menu_post_event.xbutton.time == CurrentTime)
2312 menu_post_event.xbutton.time
2313 = XtLastTimestampProcessed (XtDisplay (w));
2314
2315 pop_up_menu (mw, (XButtonPressedEvent*) ev);
2316 }
2317 else
2318 {
2319 /* If we push a button while the menu is posted semipermanently,
2320 releasing the button should always pop the menu down. */
2321 next_release_must_exit = 1;
2322
2323 /* notes the absolute position of the menubar window */
2324 mw->menu.windows [0].x = ev->xmotion.x_root - ev->xmotion.x;
2325 mw->menu.windows [0].y = ev->xmotion.y_root - ev->xmotion.y;
2326
2327 /* handles the down like a move, slots are compatible */
2328 ev->xmotion.is_hint = 0;
2329 handle_motion_event (mw, &ev->xmotion);
2330 }
2331 }
2332
2333 static void
2334 Drag (Widget w, XEvent *ev, String *params, Cardinal *num_params)
2335 {
2336 XlwMenuWidget mw = (XlwMenuWidget)w;
2337 if (mw->menu.popped_up)
2338 handle_motion_event (mw, &ev->xmotion);
2339 }
2340
2341 /* Do nothing.
2342 This is how we handle presses and releases of modifier keys. */
2343 static void
2344 Nothing (Widget w, XEvent *ev, String *params, Cardinal *num_params)
2345 {
2346 }
2347
2348 static widget_value *
2349 find_first_selectable (XlwMenuWidget mw, widget_value *item, int skip_titles)
2350 {
2351 widget_value *current = item;
2352 enum menu_separator separator;
2353
2354 while (lw_separator_p (current->name, &separator, 0) || !current->enabled
2355 || (skip_titles && !current->call_data && !current->contents))
2356 if (current->next)
2357 current=current->next;
2358 else
2359 return NULL;
2360
2361 return current;
2362 }
2363
2364 static widget_value *
2365 find_next_selectable (XlwMenuWidget mw, widget_value *item, int skip_titles)
2366 {
2367 widget_value *current = item;
2368 enum menu_separator separator;
2369
2370 while (current->next && (current=current->next) &&
2371 (lw_separator_p (current->name, &separator, 0) || !current->enabled
2372 || (skip_titles && !current->call_data && !current->contents)))
2373 ;
2374
2375 if (current == item)
2376 {
2377 if (mw->menu.old_depth < 2)
2378 return current;
2379 current = mw->menu.old_stack [mw->menu.old_depth - 2]->contents;
2380
2381 while (lw_separator_p (current->name, &separator, 0)
2382 || !current->enabled
2383 || (skip_titles && !current->call_data
2384 && !current->contents))
2385 {
2386 if (current->next)
2387 current=current->next;
2388
2389 if (current == item)
2390 break;
2391 }
2392
2393 }
2394
2395 return current;
2396 }
2397
2398 static widget_value *
2399 find_prev_selectable (XlwMenuWidget mw, widget_value *item, int skip_titles)
2400 {
2401 widget_value *current = item;
2402 widget_value *prev = item;
2403
2404 while ((current=find_next_selectable (mw, current, skip_titles))
2405 != item)
2406 {
2407 if (prev == current)
2408 break;
2409 prev=current;
2410 }
2411
2412 return prev;
2413 }
2414
2415 static void
2416 Down (Widget w, XEvent *ev, String *params, Cardinal *num_params)
2417 {
2418 XlwMenuWidget mw = (XlwMenuWidget) w;
2419 widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2420 int popup_menu_p = mw->menu.top_depth == 1;
2421
2422 /* Inside top-level menu-bar? */
2423 if (mw->menu.old_depth == mw->menu.top_depth)
2424 /* When <down> in the menu-bar is pressed, display the corresponding
2425 sub-menu and select the first selectable menu item there.
2426 If this is a popup menu, skip title item of the popup. */
2427 set_new_state (mw,
2428 find_first_selectable (mw,
2429 selected_item->contents,
2430 popup_menu_p),
2431 mw->menu.old_depth);
2432 else
2433 /* Highlight next possible (enabled and not separator) menu item. */
2434 set_new_state (mw, find_next_selectable (mw, selected_item, popup_menu_p),
2435 mw->menu.old_depth - 1);
2436
2437 remap_menubar (mw);
2438 }
2439
2440 static void
2441 Up (Widget w, XEvent *ev, String *params, Cardinal *num_params)
2442 {
2443 XlwMenuWidget mw = (XlwMenuWidget) w;
2444 widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2445 int popup_menu_p = mw->menu.top_depth == 1;
2446
2447 /* Inside top-level menu-bar? */
2448 if (mw->menu.old_depth == mw->menu.top_depth)
2449 {
2450 /* FIXME: this is tricky. <up> in the menu-bar should select the
2451 last selectable item in the list. So we select the first
2452 selectable one and find the previous selectable item. Is there
2453 a better way? */
2454 /* If this is a popup menu, skip title item of the popup. */
2455 set_new_state (mw,
2456 find_first_selectable (mw,
2457 selected_item->contents,
2458 popup_menu_p),
2459 mw->menu.old_depth);
2460 remap_menubar (mw);
2461 selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2462 set_new_state (mw,
2463 find_prev_selectable (mw,
2464 selected_item,
2465 popup_menu_p),
2466 mw->menu.old_depth - 1);
2467 }
2468 else
2469 /* Highlight previous (enabled and not separator) menu item. */
2470 set_new_state (mw, find_prev_selectable (mw, selected_item, popup_menu_p),
2471 mw->menu.old_depth - 1);
2472
2473 remap_menubar (mw);
2474 }
2475
2476 void
2477 Left (Widget w, XEvent *ev, String *params, Cardinal *num_params)
2478 {
2479 XlwMenuWidget mw = (XlwMenuWidget) w;
2480 widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2481
2482 /* Inside top-level menu-bar? */
2483 if (mw->menu.old_depth == mw->menu.top_depth)
2484 /* When <left> in the menu-bar is pressed, display the previous item on
2485 the menu-bar. If the current item is the first one, highlight the
2486 last item in the menubar (probably Help). */
2487 set_new_state (mw, find_prev_selectable (mw, selected_item, 0),
2488 mw->menu.old_depth - 1);
2489 else if (mw->menu.old_depth == 1
2490 && selected_item->contents) /* Is this menu item expandable? */
2491 {
2492 set_new_state (mw, selected_item->contents, mw->menu.old_depth);
2493 remap_menubar (mw);
2494 selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2495 if (!selected_item->enabled && find_first_selectable (mw,
2496 selected_item,
2497 0))
2498 set_new_state (mw, find_first_selectable (mw, selected_item, 0),
2499 mw->menu.old_depth - 1);
2500 }
2501
2502 else
2503 {
2504 pop_new_stack_if_no_contents (mw);
2505 set_new_state (mw, mw->menu.old_stack [mw->menu.old_depth - 2],
2506 mw->menu.old_depth - 2);
2507 }
2508
2509 remap_menubar (mw);
2510 }
2511
2512 void
2513 Right (Widget w, XEvent *ev, String *params, Cardinal *num_params)
2514 {
2515 XlwMenuWidget mw = (XlwMenuWidget) w;
2516 widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2517
2518 /* Inside top-level menu-bar? */
2519 if (mw->menu.old_depth == mw->menu.top_depth)
2520 /* When <right> in the menu-bar is pressed, display the next item on
2521 the menu-bar. If the current item is the last one, highlight the
2522 first item (probably File). */
2523 set_new_state (mw, find_next_selectable (mw, selected_item, 0),
2524 mw->menu.old_depth - 1);
2525 else if (selected_item->contents) /* Is this menu item expandable? */
2526 {
2527 set_new_state (mw, selected_item->contents, mw->menu.old_depth);
2528 remap_menubar (mw);
2529 selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2530 if (!selected_item->enabled && find_first_selectable (mw,
2531 selected_item,
2532 0))
2533 set_new_state (mw, find_first_selectable (mw, selected_item, 0),
2534 mw->menu.old_depth - 1);
2535 }
2536 else
2537 {
2538 pop_new_stack_if_no_contents (mw);
2539 set_new_state (mw, mw->menu.old_stack [mw->menu.old_depth - 2],
2540 mw->menu.old_depth - 2);
2541 }
2542
2543 remap_menubar (mw);
2544 }
2545
2546 /* Handle key press and release events while menu is popped up.
2547 Our action is to get rid of the menu. */
2548 static void
2549 Key (Widget w, XEvent *ev, String *params, Cardinal *num_params)
2550 {
2551 XlwMenuWidget mw = (XlwMenuWidget)w;
2552
2553 /* Pop down everything. */
2554 mw->menu.new_depth = 1;
2555 remap_menubar (mw);
2556
2557 if (mw->menu.popped_up)
2558 {
2559 mw->menu.popped_up = False;
2560 ungrab_all ((Widget)mw, ev->xmotion.time);
2561 if (XtIsShell (XtParent ((Widget) mw)))
2562 XtPopdown (XtParent ((Widget) mw));
2563 else
2564 {
2565 XtRemoveGrab ((Widget) mw);
2566 display_menu (mw, 0, False, NULL, NULL, NULL);
2567 }
2568 }
2569
2570 /* callback */
2571 XtCallCallbackList ((Widget)mw, mw->menu.select, (XtPointer)0);
2572 }
2573
2574 static void
2575 Select (Widget w, XEvent *ev, String *params, Cardinal *num_params)
2576 {
2577 XlwMenuWidget mw = (XlwMenuWidget)w;
2578 widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1];
2579
2580 /* If user releases the button quickly, without selecting anything,
2581 after the initial down-click that brought the menu up,
2582 do nothing. */
2583 if ((selected_item == 0
2584 || ((widget_value *) selected_item)->call_data == 0)
2585 && !next_release_must_exit
2586 && (ev->xbutton.time - menu_post_event.xbutton.time
2587 < XtGetMultiClickTime (XtDisplay (w))))
2588 return;
2589
2590 /* pop down everything. */
2591 mw->menu.new_depth = 1;
2592 remap_menubar (mw);
2593
2594 if (mw->menu.popped_up)
2595 {
2596 mw->menu.popped_up = False;
2597 ungrab_all ((Widget)mw, ev->xmotion.time);
2598 if (XtIsShell (XtParent ((Widget) mw)))
2599 XtPopdown (XtParent ((Widget) mw));
2600 else
2601 {
2602 XtRemoveGrab ((Widget) mw);
2603 display_menu (mw, 0, False, NULL, NULL, NULL);
2604 }
2605 }
2606
2607 /* callback */
2608 XtCallCallbackList ((Widget)mw, mw->menu.select, (XtPointer)selected_item);
2609 }
2610
2611
2612 \f/* Special code to pop-up a menu */
2613 static void
2614 pop_up_menu (XlwMenuWidget mw, XButtonPressedEvent *event)
2615 {
2616 int x = event->x_root;
2617 int y = event->y_root;
2618 int w;
2619 int h;
2620 int borderwidth = mw->menu.shadow_thickness;
2621 Screen* screen = XtScreen (mw);
2622 Display *display = XtDisplay (mw);
2623
2624 next_release_must_exit = 0;
2625
2626 mw->menu.inside_entry = NULL;
2627 XtCallCallbackList ((Widget)mw, mw->menu.open, NULL);
2628
2629 if (XtIsShell (XtParent ((Widget)mw)))
2630 size_menu (mw, 0);
2631
2632 w = mw->menu.windows [0].width;
2633 h = mw->menu.windows [0].height;
2634
2635 x -= borderwidth;
2636 y -= borderwidth;
2637 if (x < borderwidth)
2638 x = borderwidth;
2639 if (x + w + 2 * borderwidth > WidthOfScreen (screen))
2640 x = WidthOfScreen (screen) - w - 2 * borderwidth;
2641 if (y < borderwidth)
2642 y = borderwidth;
2643 if (y + h + 2 * borderwidth> HeightOfScreen (screen))
2644 y = HeightOfScreen (screen) - h - 2 * borderwidth;
2645
2646 mw->menu.popped_up = True;
2647 if (XtIsShell (XtParent ((Widget)mw)))
2648 {
2649 XtConfigureWidget (XtParent ((Widget)mw), x, y, w, h,
2650 XtParent ((Widget)mw)->core.border_width);
2651 XtPopup (XtParent ((Widget)mw), XtGrabExclusive);
2652 display_menu (mw, 0, False, NULL, NULL, NULL);
2653 mw->menu.windows [0].x = x + borderwidth;
2654 mw->menu.windows [0].y = y + borderwidth;
2655 mw->menu.top_depth = 1; /* Popup menus don't have a bar so top is 1 */
2656 }
2657 else
2658 {
2659 XEvent *ev = (XEvent *) event;
2660
2661 XtAddGrab ((Widget) mw, True, True);
2662
2663 /* notes the absolute position of the menubar window */
2664 mw->menu.windows [0].x = ev->xmotion.x_root - ev->xmotion.x;
2665 mw->menu.windows [0].y = ev->xmotion.y_root - ev->xmotion.y;
2666 mw->menu.top_depth = 2;
2667 }
2668
2669 #ifdef emacs
2670 x_catch_errors (display);
2671 #endif
2672 if (XtGrabPointer ((Widget)mw, False,
2673 (PointerMotionMask
2674 | PointerMotionHintMask
2675 | ButtonReleaseMask
2676 | ButtonPressMask),
2677 GrabModeAsync, GrabModeAsync, None,
2678 mw->menu.cursor_shape,
2679 event->time) == Success)
2680 {
2681 if (! GRAB_KEYBOARD
2682 || XtGrabKeyboard ((Widget)mw, False, GrabModeAsync,
2683 GrabModeAsync, event->time) == Success)
2684 {
2685 XtSetKeyboardFocus((Widget)mw, None);
2686 pointer_grabbed = 1;
2687 }
2688 else
2689 XtUngrabPointer ((Widget)mw, event->time);
2690 }
2691
2692 #ifdef emacs
2693 if (x_had_errors_p (display))
2694 {
2695 pointer_grabbed = 0;
2696 XtUngrabPointer ((Widget)mw, event->time);
2697 }
2698 x_uncatch_errors ();
2699 #endif
2700
2701 ((XMotionEvent*)event)->is_hint = 0;
2702 handle_motion_event (mw, (XMotionEvent*)event);
2703 }