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