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