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