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