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