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