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