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