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