Fix minor problems found by static checking.
[bpt/emacs.git] / src / xmenu.c
CommitLineData
dcfdbac7 1/* X Communication module for terminals which understand the X protocol.
95df8112 2
ab422c4d
PE
3Copyright (C) 1986, 1988, 1993-1994, 1996, 1999-2013 Free Software
4Foundation, Inc.
dcfdbac7
JB
5
6This file is part of GNU Emacs.
7
9ec0b715 8GNU Emacs is free software: you can redistribute it and/or modify
dcfdbac7 9it under the terms of the GNU General Public License as published by
9ec0b715
GM
10the Free Software Foundation, either version 3 of the License, or
11(at your option) any later version.
dcfdbac7
JB
12
13GNU Emacs is distributed in the hope that it will be useful,
14but WITHOUT ANY WARRANTY; without even the implied warranty of
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
9ec0b715 19along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
dcfdbac7 20
c9e60620 21/* X pop-up deck-of-cards menu facility for GNU Emacs.
dcfdbac7
JB
22 *
23 * Written by Jon Arnold and Roman Budzianowski
24 * Mods and rewrite by Robert Krawitz
25 *
26 */
27
18686d47
RS
28/* Modified by Fred Pierresteguy on December 93
29 to make the popup menus and menubar use the Xt. */
30
78589e07
RS
31/* Rewritten for clarity and GC protection by rms in Feb 94. */
32
68c45bf0
PE
33#include <config.h>
34
565620a5 35#include <stdio.h>
7ee72033 36
dcfdbac7 37#include "lisp.h"
02067692 38#include "keyboard.h"
5ee707b8 39#include "keymap.h"
7708e9bd 40#include "frame.h"
428a555e 41#include "termhooks.h"
dcfdbac7 42#include "window.h"
9ac0d9e0 43#include "blockinput.h"
e5560ff7 44#include "character.h"
88766961 45#include "buffer.h"
cc45fb40
SM
46#include "charset.h"
47#include "coding.h"
c3438661 48#include "sysselect.h"
dcfdbac7 49
eeee3112
RS
50#ifdef MSDOS
51#include "msdos.h"
52#endif
53
87485d6f 54#ifdef HAVE_X_WINDOWS
dcfdbac7
JB
55/* This may include sys/types.h, and that somehow loses
56 if this is not done before the other system files. */
57#include "xterm.h"
87485d6f 58#endif
dcfdbac7
JB
59
60/* Load sys/types.h if not already loaded.
61 In some systems loading it twice is suicidal. */
62#ifndef makedev
63#include <sys/types.h>
64#endif
65
66#include "dispextern.h"
67
87485d6f 68#ifdef HAVE_X_WINDOWS
646f98ec
DL
69/* Defining HAVE_MULTILINGUAL_MENU would mean that the toolkit menu
70 code accepts the Emacs internal encoding. */
703dc2a8 71#undef HAVE_MULTILINGUAL_MENU
18686d47 72#ifdef USE_X_TOOLKIT
991786dd 73#include "widget.h"
18686d47
RS
74#include <X11/Xlib.h>
75#include <X11/IntrinsicP.h>
76#include <X11/CoreP.h>
77#include <X11/StringDefs.h>
60f312e2 78#include <X11/Shell.h>
5ee80bd3 79#ifdef USE_LUCID
99852628
JD
80#include "xsettings.h"
81#include "../lwlib/xlwmenu.h"
5262c5c7
CY
82#ifdef HAVE_XAW3D
83#include <X11/Xaw3d/Paned.h>
84#else /* !HAVE_XAW3D */
1d1c1567 85#include <X11/Xaw/Paned.h>
5262c5c7 86#endif /* HAVE_XAW3D */
5ee80bd3 87#endif /* USE_LUCID */
c865c575 88#ifdef USE_MOTIF
dee186b6 89#include "../lwlib/lwlib.h"
c865c575 90#endif
a352a815 91#else /* not USE_X_TOOLKIT */
488dd4c4 92#ifndef USE_GTK
a352a815 93#include "../oldXMenu/XMenu.h"
488dd4c4 94#endif
a352a815
RS
95#endif /* not USE_X_TOOLKIT */
96#endif /* HAVE_X_WINDOWS */
18686d47 97
e7c9048f
AS
98#ifdef USE_GTK
99#include "gtkutil.h"
93d5ca1f
JD
100#ifdef HAVE_GTK3
101#include "xgselect.h"
102#endif
e7c9048f
AS
103#endif
104
105#include "menu.h"
106
dcfdbac7
JB
107#ifndef TRUE
108#define TRUE 1
78589e07 109#endif /* no TRUE */
dcfdbac7 110
955cbe7b 111static Lisp_Object Qdebug_on_next_call;
0314aacb 112
04bab72c 113#if defined (USE_X_TOOLKIT) || defined (USE_GTK)
a10c8269 114static Lisp_Object xdialog_show (struct frame *, bool, Lisp_Object, Lisp_Object,
42ca4633 115 const char **);
3427a3db 116#endif
78589e07 117\f
4dedbfe0 118/* Flag which when set indicates a dialog or menu has been posted by
c98fcf4b 119 Xt on behalf of one of the widget sets. */
488dd4c4 120static int popup_activated_flag;
4dedbfe0 121
bd3a4da2 122\f
88766961 123#ifdef USE_X_TOOLKIT
bd3a4da2 124
281585b0
PE
125static int next_menubar_widget_id;
126
7556890b 127/* Return the frame whose ->output_data.x->id equals ID, or 0 if none. */
bd3a4da2 128
88766961 129static struct frame *
ebd15611 130menubar_id_to_frame (LWLIB_ID id)
88766961
RS
131{
132 Lisp_Object tail, frame;
a10c8269 133 struct frame *f;
bd3a4da2 134
5b04e9f9 135 FOR_EACH_FRAME (tail, frame)
bd3a4da2 136 {
88766961 137 f = XFRAME (frame);
2d764c78 138 if (!FRAME_WINDOW_P (f))
88766961 139 continue;
7556890b 140 if (f->output_data.x->id == id)
88766961 141 return f;
bd3a4da2 142 }
88766961 143 return 0;
bd3a4da2 144}
88766961
RS
145
146#endif
4dedbfe0 147\f
a894238b
JD
148#ifdef HAVE_X_WINDOWS
149/* Return the mouse position in *X and *Y. The coordinates are window
150 relative for the edit window in frame F.
151 This is for Fx_popup_menu. The mouse_position_hook can not
152 be used for X, as it returns window relative coordinates
153 for the window where the mouse is in. This could be the menu bar,
154 the scroll bar or the edit window. Fx_popup_menu needs to be
155 sure it is the edit window. */
ef7417fd 156void
a10c8269 157mouse_position_for_popup (struct frame *f, int *x, int *y)
a894238b
JD
158{
159 Window root, dummy_window;
160 int dummy;
161
65b02bb9 162 eassert (FRAME_X_P (f));
62af879c 163
4d7e6e51 164 block_input ();
177c0ea7 165
a894238b
JD
166 XQueryPointer (FRAME_X_DISPLAY (f),
167 DefaultRootWindow (FRAME_X_DISPLAY (f)),
168
169 /* The root window which contains the pointer. */
170 &root,
171
172 /* Window pointer is on, not used */
173 &dummy_window,
174
175 /* The position on that root window. */
176 x, y,
177
178 /* x/y in dummy_window coordinates, not used. */
179 &dummy, &dummy,
180
181 /* Modifier keys and pointer buttons, about which
182 we don't care. */
183 (unsigned int *) &dummy);
184
4d7e6e51 185 unblock_input ();
a894238b
JD
186
187 /* xmenu_show expects window coordinates, not root window
188 coordinates. Translate. */
9882535b
KS
189 *x -= f->left_pos + FRAME_OUTER_TO_INNER_DIFF_X (f);
190 *y -= f->top_pos + FRAME_OUTER_TO_INNER_DIFF_Y (f);
a894238b
JD
191}
192
193#endif /* HAVE_X_WINDOWS */
194
c3438661
JD
195#ifndef MSDOS
196
4475bec4
PE
197#if defined USE_GTK || defined USE_MOTIF
198
b3935289
JD
199/* Set menu_items_inuse so no other popup menu or dialog is created. */
200
201void
971de7fb 202x_menu_set_in_use (int in_use)
b3935289
JD
203{
204 menu_items_inuse = in_use ? Qt : Qnil;
5ae53dcf 205 popup_activated_flag = in_use;
73dba342 206#ifdef USE_X_TOOLKIT
98a20c65
CY
207 if (popup_activated_flag)
208 x_activate_timeout_atimer ();
73dba342 209#endif
b3935289
JD
210}
211
4475bec4
PE
212#endif
213
c3438661
JD
214/* Wait for an X event to arrive or for a timer to expire. */
215
6f37259d
PE
216#ifndef USE_MOTIF
217static
218#endif
b3935289 219void
c3438661
JD
220x_menu_wait_for_event (void *data)
221{
c3438661
JD
222 /* Another way to do this is to register a timer callback, that can be
223 done in GTK and Xt. But we have to do it like this when using only X
224 anyway, and with callbacks we would have three variants for timer handling
225 instead of the small ifdefs below. */
226
227 while (
228#ifdef USE_X_TOOLKIT
f1d1cd24 229 ! XtAppPending (Xt_app_con)
c3438661
JD
230#elif defined USE_GTK
231 ! gtk_events_pending ()
232#else
7d652d97 233 ! XPending (data)
c3438661
JD
234#endif
235 )
236 {
43aac990 237 struct timespec next_time = timer_check (), *ntp;
d486344e 238 fd_set read_fds;
c3438661
JD
239 struct x_display_info *dpyinfo;
240 int n = 0;
241
242 FD_ZERO (&read_fds);
243 for (dpyinfo = x_display_list; dpyinfo; dpyinfo = dpyinfo->next)
244 {
245 int fd = ConnectionNumber (dpyinfo->display);
246 FD_SET (fd, &read_fds);
247 if (fd > n) n = fd;
11814156 248 XFlush (dpyinfo->display);
c3438661
JD
249 }
250
43aac990 251 if (! timespec_valid_p (next_time))
e90292a9
JD
252 ntp = 0;
253 else
254 ntp = &next_time;
c3438661 255
8f5f35cc 256#if defined USE_GTK && defined HAVE_GTK3
63def6b6
CY
257 /* Gtk3 have arrows on menus when they don't fit. When the
258 pointer is over an arrow, a timeout scrolls it a bit. Use
259 xg_select so that timeout gets triggered. */
260 xg_select (n + 1, &read_fds, NULL, NULL, ntp, NULL);
93d5ca1f 261#else
d35af63c 262 pselect (n + 1, &read_fds, NULL, NULL, ntp, NULL);
93d5ca1f 263#endif
c3438661
JD
264 }
265}
266#endif /* ! MSDOS */
267
78589e07 268\f
488dd4c4 269#if defined (USE_X_TOOLKIT) || defined (USE_GTK)
18686d47 270
f1d1cd24
JD
271#ifdef USE_X_TOOLKIT
272
4dedbfe0 273/* Loop in Xt until the menu pulldown or dialog popup has been
f56e7ad2 274 popped down (deactivated). This is used for x-popup-menu
f02cac82
RS
275 and x-popup-dialog; it is not used for the menu bar.
276
2e2b8e22 277 NOTE: All calls to popup_get_selection should be protected
c98fcf4b 278 with BLOCK_INPUT, UNBLOCK_INPUT wrappers. */
aa669def 279
f02cac82 280static void
ebd15611 281popup_get_selection (XEvent *initial_event, struct x_display_info *dpyinfo, LWLIB_ID id, int do_timers)
78589e07 282{
4dedbfe0 283 XEvent event;
f02cac82 284
8b806a85
JD
285 while (popup_activated_flag)
286 {
c0b0f54b 287 if (initial_event)
8b806a85
JD
288 {
289 event = *initial_event;
290 initial_event = 0;
291 }
292 else
c3438661
JD
293 {
294 if (do_timers) x_menu_wait_for_event (0);
295 XtAppNextEvent (Xt_app_con, &event);
296 }
78589e07 297
10624005 298 /* Make sure we don't consider buttons grabbed after menu goes.
8b806a85
JD
299 And make sure to deactivate for any ButtonRelease,
300 even if XtDispatchEvent doesn't do that. */
301 if (event.type == ButtonRelease
302 && dpyinfo->display == event.xbutton.display)
10624005
KH
303 {
304 dpyinfo->grabbed &= ~(1 << event.xbutton.button);
177c0ea7 305#ifdef USE_MOTIF /* Pretending that the event came from a
8b806a85
JD
306 Btn1Down seems the only way to convince Motif to
307 activate its callbacks; setting the XmNmenuPost
308 isn't working. --marcus@sysc.pdx.edu. */
309 event.xbutton.button = 1;
310 /* Motif only pops down menus when no Ctrl, Alt or Mod
311 key is pressed and the button is released. So reset key state
312 so Motif thinks this is the case. */
313 event.xbutton.state = 0;
c8b5aa3d 314#endif
10624005 315 }
33385c6f 316 /* Pop down on C-g and Escape. */
2e2b8e22 317 else if (event.type == KeyPress
10624005 318 && dpyinfo->display == event.xbutton.display)
8b806a85
JD
319 {
320 KeySym keysym = XLookupKeysym (&event.xkey, 0);
9f6fcdc5 321
33385c6f
JD
322 if ((keysym == XK_g && (event.xkey.state & ControlMask) != 0)
323 || keysym == XK_Escape) /* Any escape, ignore modifiers. */
9f6fcdc5 324 popup_activated_flag = 0;
8b806a85 325 }
177c0ea7 326
8b806a85 327 x_dispatch_event (&event, event.xany.display);
aa669def 328 }
78589e07
RS
329}
330
12b6af5c 331DEFUN ("x-menu-bar-open-internal", Fx_menu_bar_open_internal, Sx_menu_bar_open_internal, 0, 1, "i",
22badffe
JD
332 doc: /* Start key navigation of the menu bar in FRAME.
333This initially opens the first menu bar item and you can then navigate with the
334arrow keys, select a menu entry with the return key or cancel with the
335escape key. If FRAME has no menu bar this function does nothing.
336
337If FRAME is nil or not given, use the selected frame. */)
5842a27b 338 (Lisp_Object frame)
22badffe
JD
339{
340 XEvent ev;
a10c8269 341 struct frame *f = decode_window_system_frame (frame);
22badffe 342 Widget menubar;
4d7e6e51 343 block_input ();
22badffe
JD
344
345 if (FRAME_EXTERNAL_MENU_BAR (f))
346 set_frame_menubar (f, 0, 1);
347
348 menubar = FRAME_X_OUTPUT (f)->menubar_widget;
349 if (menubar)
350 {
351 Window child;
d7e6881a 352 bool error_p = 0;
22badffe
JD
353
354 x_catch_errors (FRAME_X_DISPLAY (f));
355 memset (&ev, 0, sizeof ev);
356 ev.xbutton.display = FRAME_X_DISPLAY (f);
357 ev.xbutton.window = XtWindow (menubar);
aad3612f 358 ev.xbutton.root = FRAME_DISPLAY_INFO (f)->root_window;
22badffe
JD
359 ev.xbutton.time = XtLastTimestampProcessed (FRAME_X_DISPLAY (f));
360 ev.xbutton.button = Button1;
361 ev.xbutton.x = ev.xbutton.y = FRAME_MENUBAR_HEIGHT (f) / 2;
362 ev.xbutton.same_screen = True;
363
364#ifdef USE_MOTIF
365 {
366 Arg al[2];
367 WidgetList list;
368 Cardinal nr;
369 XtSetArg (al[0], XtNchildren, &list);
370 XtSetArg (al[1], XtNnumChildren, &nr);
371 XtGetValues (menubar, al, 2);
372 ev.xbutton.window = XtWindow (list[0]);
373 }
374#endif
375
376 XTranslateCoordinates (FRAME_X_DISPLAY (f),
377 /* From-window, to-window. */
378 ev.xbutton.window, ev.xbutton.root,
379
380 /* From-position, to-position. */
381 ev.xbutton.x, ev.xbutton.y,
382 &ev.xbutton.x_root, &ev.xbutton.y_root,
383
384 /* Child of win. */
385 &child);
386 error_p = x_had_errors_p (FRAME_X_DISPLAY (f));
387 x_uncatch_errors ();
388
389 if (! error_p)
390 {
391 ev.type = ButtonPress;
392 ev.xbutton.state = 0;
393
394 XtDispatchEvent (&ev);
395 ev.xbutton.type = ButtonRelease;
396 ev.xbutton.state = Button1Mask;
397 XtDispatchEvent (&ev);
398 }
399 }
400
4d7e6e51 401 unblock_input ();
57d671b4
AS
402
403 return Qnil;
22badffe 404}
488dd4c4
JD
405#endif /* USE_X_TOOLKIT */
406
22badffe 407
488dd4c4 408#ifdef USE_GTK
12b6af5c 409DEFUN ("x-menu-bar-open-internal", Fx_menu_bar_open_internal, Sx_menu_bar_open_internal, 0, 1, "i",
22badffe
JD
410 doc: /* Start key navigation of the menu bar in FRAME.
411This initially opens the first menu bar item and you can then navigate with the
412arrow keys, select a menu entry with the return key or cancel with the
413escape key. If FRAME has no menu bar this function does nothing.
414
415If FRAME is nil or not given, use the selected frame. */)
5842a27b 416 (Lisp_Object frame)
22badffe
JD
417{
418 GtkWidget *menubar;
a10c8269 419 struct frame *f;
718aeeb8 420
4d7e6e51 421 block_input ();
7452b7bd 422 f = decode_window_system_frame (frame);
22badffe
JD
423
424 if (FRAME_EXTERNAL_MENU_BAR (f))
425 set_frame_menubar (f, 0, 1);
426
427 menubar = FRAME_X_OUTPUT (f)->menubar_widget;
428 if (menubar)
429 {
430 /* Activate the first menu. */
431 GList *children = gtk_container_get_children (GTK_CONTAINER (menubar));
432
32d46135
JD
433 if (children)
434 {
435 g_signal_emit_by_name (children->data, "activate_item");
436 popup_activated_flag = 1;
437 g_list_free (children);
438 }
22badffe 439 }
4d7e6e51 440 unblock_input ();
22badffe
JD
441
442 return Qnil;
443}
444
488dd4c4
JD
445/* Loop util popup_activated_flag is set to zero in a callback.
446 Used for popup menus and dialogs. */
f1d1cd24 447
488dd4c4 448static void
971de7fb 449popup_widget_loop (int do_timers, GtkWidget *widget)
488dd4c4
JD
450{
451 ++popup_activated_flag;
452
453 /* Process events in the Gtk event loop until done. */
454 while (popup_activated_flag)
455 {
c3438661 456 if (do_timers) x_menu_wait_for_event (0);
488dd4c4
JD
457 gtk_main_iteration ();
458 }
459}
460#endif
461
88766961
RS
462/* Activate the menu bar of frame F.
463 This is called from keyboard.c when it gets the
3b8f9651 464 MENU_BAR_ACTIVATE_EVENT out of the Emacs event queue.
88766961
RS
465
466 To activate the menu bar, we use the X button-press event
ac78b144 467 that was saved in saved_menu_event.
88766961
RS
468 That makes the toolkit do its thing.
469
470 But first we recompute the menu bar contents (the whole tree).
471
472 The reason for saving the button event until here, instead of
473 passing it to the toolkit right away, is that we can safely
474 execute Lisp code. */
177c0ea7 475
dfcf069d 476void
a10c8269 477x_activate_menubar (struct frame *f)
88766961 478{
65b02bb9 479 eassert (FRAME_X_P (f));
62af879c 480
ac78b144 481 if (!f->output_data.x->saved_menu_event->type)
88766961
RS
482 return;
483
177c0ea7 484#ifdef USE_GTK
6b61353c
KH
485 if (! xg_win_to_widget (FRAME_X_DISPLAY (f),
486 f->output_data.x->saved_menu_event->xany.window))
488dd4c4
JD
487 return;
488#endif
177c0ea7 489
a9be6839 490 set_frame_menubar (f, 0, 1);
4d7e6e51 491 block_input ();
cf28cebc 492 popup_activated_flag = 1;
488dd4c4
JD
493#ifdef USE_GTK
494 XPutBackEvent (f->output_data.x->display_info->display,
495 f->output_data.x->saved_menu_event);
488dd4c4 496#else
86fad4ec 497 XtDispatchEvent (f->output_data.x->saved_menu_event);
488dd4c4 498#endif
4d7e6e51 499 unblock_input ();
177c0ea7 500
88766961 501 /* Ignore this if we get it a second time. */
ac78b144 502 f->output_data.x->saved_menu_event->type = 0;
88766961
RS
503}
504
4dedbfe0
PR
505/* This callback is invoked when the user selects a menubar cascade
506 pushbutton, but before the pulldown menu is posted. */
78589e07 507
488dd4c4 508#ifndef USE_GTK
78589e07 509static void
ebd15611 510popup_activate_callback (Widget widget, LWLIB_ID id, XtPointer client_data)
78589e07 511{
4dedbfe0 512 popup_activated_flag = 1;
98a20c65 513 x_activate_timeout_atimer ();
7555d825 514}
488dd4c4 515#endif
7555d825
GM
516
517/* This callback is invoked when a dialog or menu is finished being
518 used and has been unposted. */
519
488dd4c4 520static void
5db81e33
SM
521popup_deactivate_callback (
522#ifdef USE_GTK
523 GtkWidget *widget, gpointer client_data
488dd4c4 524#else
5db81e33
SM
525 Widget widget, LWLIB_ID id, XtPointer client_data
526#endif
527 )
7555d825 528{
7555d825 529 popup_activated_flag = 0;
78589e07
RS
530}
531
850df50b 532
488dd4c4
JD
533/* Function that finds the frame for WIDGET and shows the HELP text
534 for that widget.
535 F is the frame if known, or NULL if not known. */
536static void
a10c8269 537show_help_event (struct frame *f, xt_or_gtk_widget widget, Lisp_Object help)
850df50b 538{
488dd4c4 539 Lisp_Object frame;
850df50b 540
850df50b 541 if (f)
9cd50434
GM
542 {
543 XSETFRAME (frame, f);
544 kbd_buffer_store_help_event (frame, help);
545 }
850df50b
GM
546 else
547 {
6b61353c 548#if 0 /* This code doesn't do anything useful. ++kfs */
177c0ea7 549 /* WIDGET is the popup menu. It's parent is the frame's
850df50b 550 widget. See which frame that is. */
488dd4c4 551 xt_or_gtk_widget frame_widget = XtParent (widget);
850df50b
GM
552 Lisp_Object tail;
553
8e50cc2d 554 for (tail = Vframe_list; CONSP (tail); tail = XCDR (tail))
850df50b
GM
555 {
556 frame = XCAR (tail);
8e50cc2d 557 if (FRAMEP (frame)
850df50b
GM
558 && (f = XFRAME (frame),
559 FRAME_X_P (f) && f->output_data.x->widget == frame_widget))
560 break;
561 }
6b61353c 562#endif
f868cd8a 563 show_help_echo (help, Qnil, Qnil, Qnil);
9cd50434 564 }
850df50b
GM
565}
566
488dd4c4
JD
567/* Callback called when menu items are highlighted/unhighlighted
568 while moving the mouse over them. WIDGET is the menu bar or menu
569 popup widget. ID is its LWLIB_ID. CALL_DATA contains a pointer to
570 the data structure for the menu item, or null in case of
571 unhighlighting. */
4dedbfe0 572
488dd4c4 573#ifdef USE_GTK
032f1620 574static void
971de7fb 575menu_highlight_callback (GtkWidget *widget, gpointer call_data)
488dd4c4
JD
576{
577 xg_menu_item_cb_data *cb_data;
578 Lisp_Object help;
177c0ea7 579
7d652d97 580 cb_data = g_object_get_data (G_OBJECT (widget), XG_ITEM_DATA);
488dd4c4
JD
581 if (! cb_data) return;
582
583 help = call_data ? cb_data->help : Qnil;
584
585 /* If popup_activated_flag is greater than 1 we are in a popup menu.
a560d974
JD
586 Don't pass the frame to show_help_event for those.
587 Passing frame creates an Emacs event. As we are looping in
8d5ed899 588 popup_widget_loop, it won't be handled. Passing NULL shows the tip
a560d974
JD
589 directly without using an Emacs event. This is what the Lucid code
590 does below. */
591 show_help_event (popup_activated_flag <= 1 ? cb_data->cl_data->f : NULL,
592 widget, help);
488dd4c4
JD
593}
594#else
032f1620 595static void
ebd15611 596menu_highlight_callback (Widget widget, LWLIB_ID id, void *call_data)
488dd4c4 597{
7d652d97
PE
598 widget_value *wv = call_data;
599 Lisp_Object help = wv ? wv->help : Qnil;
177c0ea7 600
488dd4c4 601 /* Determine the frame for the help event. */
7d652d97 602 struct frame *f = menubar_id_to_frame (id);
488dd4c4
JD
603
604 show_help_event (f, widget, help);
605}
606#endif
607
488dd4c4
JD
608#ifdef USE_GTK
609/* Gtk calls callbacks just because we tell it what item should be
610 selected in a radio group. If this variable is set to a non-zero
611 value, we are creating menus and don't want callbacks right now.
612*/
613static int xg_crazy_callback_abort;
614
615/* This callback is called from the menu bar pulldown menu
616 when the user makes a selection.
617 Figure out what the user chose
618 and put the appropriate events into the keyboard buffer. */
619static void
971de7fb 620menubar_selection_callback (GtkWidget *widget, gpointer client_data)
488dd4c4 621{
7d652d97 622 xg_menu_item_cb_data *cb_data = client_data;
488dd4c4
JD
623
624 if (xg_crazy_callback_abort)
625 return;
626
627 if (! cb_data || ! cb_data->cl_data || ! cb_data->cl_data->f)
628 return;
629
b79c8219
JD
630 /* For a group of radio buttons, GTK calls the selection callback first
631 for the item that was active before the selection and then for the one that
632 is active after the selection. For C-h k this means we get the help on
633 the deselected item and then the selected item is executed. Prevent that
634 by ignoring the non-active item. */
635 if (GTK_IS_RADIO_MENU_ITEM (widget)
636 && ! gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (widget)))
637 return;
638
81f09877
JD
639 /* When a menu is popped down, X generates a focus event (i.e. focus
640 goes back to the frame below the menu). Since GTK buffers events,
641 we force it out here before the menu selection event. Otherwise
642 sit-for will exit at once if the focus event follows the menu selection
643 event. */
644
4d7e6e51 645 block_input ();
81f09877
JD
646 while (gtk_events_pending ())
647 gtk_main_iteration ();
4d7e6e51 648 unblock_input ();
81f09877 649
488dd4c4
JD
650 find_and_call_menu_selection (cb_data->cl_data->f,
651 cb_data->cl_data->menu_bar_items_used,
652 cb_data->cl_data->menu_bar_vector,
653 cb_data->call_data);
654}
655
656#else /* not USE_GTK */
657
658/* This callback is called from the menu bar pulldown menu
659 when the user makes a selection.
660 Figure out what the user chose
661 and put the appropriate events into the keyboard buffer. */
662static void
ebd15611 663menubar_selection_callback (Widget widget, LWLIB_ID id, XtPointer client_data)
488dd4c4 664{
a10c8269 665 struct frame *f;
488dd4c4
JD
666
667 f = menubar_id_to_frame (id);
668 if (!f)
669 return;
670 find_and_call_menu_selection (f, f->menu_bar_items_used,
e69b0960 671 f->menu_bar_vector, client_data);
488dd4c4
JD
672}
673#endif /* not USE_GTK */
f61a541b
GM
674\f
675/* Recompute all the widgets of frame F, when the menu bar has been
18e27ea8 676 changed. */
f61a541b 677
18e27ea8 678static void
a10c8269 679update_frame_menubar (struct frame *f)
18686d47 680{
488dd4c4 681#ifdef USE_GTK
18e27ea8 682 xg_update_frame_menubar (f);
488dd4c4 683#else
62af879c 684 struct x_output *x;
cffa74ea 685 int columns, rows;
177c0ea7 686
65b02bb9 687 eassert (FRAME_X_P (f));
62af879c
KL
688
689 x = f->output_data.x;
690
f61a541b 691 if (!x->menubar_widget || XtIsManaged (x->menubar_widget))
18e27ea8 692 return;
18686d47 693
4d7e6e51 694 block_input ();
f61a541b
GM
695 /* Save the size of the frame because the pane widget doesn't accept
696 to resize itself. So force it. */
9882535b
KS
697 columns = FRAME_COLS (f);
698 rows = FRAME_LINES (f);
cffa74ea 699
f61a541b
GM
700 /* Do the voodoo which means "I'm changing lots of things, don't try
701 to refigure sizes until I'm done." */
4dedbfe0 702 lw_refigure_widget (x->column_widget, False);
cffa74ea 703
f61a541b
GM
704 /* The order in which children are managed is the top to bottom
705 order in which they are displayed in the paned window. First,
706 remove the text-area widget. */
18686d47
RS
707 XtUnmanageChild (x->edit_widget);
708
f61a541b
GM
709 /* Remove the menubar that is there now, and put up the menubar that
710 should be there. */
711 XtManageChild (x->menubar_widget);
712 XtMapWidget (x->menubar_widget);
713 XtVaSetValues (x->menubar_widget, XtNmappedWhenManaged, 1, NULL);
18686d47 714
c98fcf4b 715 /* Re-manage the text-area widget, and then thrash the sizes. */
18686d47 716 XtManageChild (x->edit_widget);
4dedbfe0 717 lw_refigure_widget (x->column_widget, True);
cffa74ea
FP
718
719 /* Force the pane widget to resize itself with the right values. */
720 EmacsFrameSetCharSize (x->edit_widget, columns, rows);
4d7e6e51 721 unblock_input ();
488dd4c4 722#endif
18686d47
RS
723}
724
99852628 725#ifdef USE_LUCID
1ecb2d3f 726static void
ebd15611 727apply_systemfont_to_dialog (Widget w)
1ecb2d3f
JD
728{
729 const char *fn = xsettings_get_system_normal_font ();
51b59d79 730 if (fn)
1ecb2d3f
JD
731 {
732 XrmDatabase db = XtDatabase (XtDisplay (w));
733 if (db)
3928f2b6 734 XrmPutStringResource (&db, "*dialog.font", fn);
1ecb2d3f
JD
735 }
736}
737
99852628 738static void
3928f2b6 739apply_systemfont_to_menu (struct frame *f, Widget w)
99852628
JD
740{
741 const char *fn = xsettings_get_system_normal_font ();
99852628 742
3928f2b6 743 if (fn)
99852628 744 {
3928f2b6
JD
745 XrmDatabase db = XtDatabase (XtDisplay (w));
746 if (db)
747 {
748 XrmPutStringResource (&db, "*menubar*font", fn);
749 XrmPutStringResource (&db, "*popup*font", fn);
750 }
99852628 751 }
99852628 752}
3928f2b6 753
99852628
JD
754#endif
755
4bcdbab1
KH
756/* Set the contents of the menubar widgets of frame F.
757 The argument FIRST_TIME is currently ignored;
758 it is set the first time this is called, from initialize_frame_menubar. */
759
18686d47 760void
a10c8269 761set_frame_menubar (struct frame *f, bool first_time, bool deep_p)
18686d47 762{
62af879c 763 xt_or_gtk_widget menubar_widget;
488dd4c4
JD
764#ifdef USE_X_TOOLKIT
765 LWLIB_ID id;
766#endif
faa935b6 767 Lisp_Object items;
4d19cb8e 768 widget_value *wv, *first_wv, *prev_wv = 0;
3e26f69c 769 int i;
ced89c24 770 int *submenu_start, *submenu_end;
7cded46f
PE
771 bool *submenu_top_level_items;
772 int *submenu_n_panes;
cc45fb40 773
65b02bb9 774 eassert (FRAME_X_P (f));
62af879c
KL
775
776 menubar_widget = f->output_data.x->menubar_widget;
18686d47 777
bfc524bc
RS
778 XSETFRAME (Vmenu_updating_frame, f);
779
488dd4c4 780#ifdef USE_X_TOOLKIT
7556890b
RS
781 if (f->output_data.x->id == 0)
782 f->output_data.x->id = next_menubar_widget_id++;
783 id = f->output_data.x->id;
488dd4c4 784#endif
177c0ea7 785
88766961
RS
786 if (! menubar_widget)
787 deep_p = 1;
a9be6839
RS
788 /* Make the first call for any given frame always go deep. */
789 else if (!f->output_data.x->saved_menu_event && !deep_p)
745c34fb
RS
790 {
791 deep_p = 1;
23f86fce 792 f->output_data.x->saved_menu_event = xmalloc (sizeof (XEvent));
a9be6839 793 f->output_data.x->saved_menu_event->type = 0;
745c34fb 794 }
18686d47 795
6b61353c
KH
796#ifdef USE_GTK
797 /* If we have detached menus, we must update deep so detached menus
798 also gets updated. */
799 deep_p = deep_p || xg_have_tear_offs ();
800#endif
801
88766961 802 if (deep_p)
18686d47 803 {
88766961
RS
804 /* Make a widget-value tree representing the entire menu trees. */
805
806 struct buffer *prev = current_buffer;
807 Lisp_Object buffer;
d311d28c 808 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
88766961
RS
809 int previous_menu_items_used = f->menu_bar_items_used;
810 Lisp_Object *previous_items
38182d90 811 = alloca (previous_menu_items_used * sizeof *previous_items);
3e26f69c 812 int subitems;
88766961 813
0b1cf399
RS
814 /* If we are making a new widget, its contents are empty,
815 do always reinitialize them. */
816 if (! menubar_widget)
817 previous_menu_items_used = 0;
818
e74aeda8 819 buffer = XWINDOW (FRAME_SELECTED_WINDOW (f))->contents;
88766961
RS
820 specbind (Qinhibit_quit, Qt);
821 /* Don't let the debugger step into this code
822 because it is not reentrant. */
823 specbind (Qdebug_on_next_call, Qnil);
824
89f2614d 825 record_unwind_save_match_data ();
88766961
RS
826 if (NILP (Voverriding_local_map_menu_flag))
827 {
828 specbind (Qoverriding_terminal_local_map, Qnil);
829 specbind (Qoverriding_local_map, Qnil);
830 }
18686d47 831
88766961 832 set_buffer_internal_1 (XBUFFER (buffer));
18686d47 833
88766961 834 /* Run the Lucid hook. */
950eaee7 835 safe_run_hooks (Qactivate_menubar_hook);
177c0ea7 836
88766961
RS
837 /* If it has changed current-menubar from previous value,
838 really recompute the menubar from the value. */
839 if (! NILP (Vlucid_menu_bar_dirty_flag))
840 call0 (Qrecompute_lucid_menubar);
a57634d4 841 safe_run_hooks (Qmenu_bar_update_hook);
f00af5b1 842 fset_menu_bar_items (f, menu_bar_items (FRAME_MENU_BAR_ITEMS (f)));
18686d47 843
88766961 844 items = FRAME_MENU_BAR_ITEMS (f);
8d8a3494 845
88766961 846 /* Save the frame's previous menu bar contents data. */
86c04183 847 if (previous_menu_items_used)
91f2d272 848 memcpy (previous_items, XVECTOR (f->menu_bar_vector)->contents,
663e2b3f 849 previous_menu_items_used * word_size);
8d8a3494 850
ced89c24
RS
851 /* Fill in menu_items with the current menu bar contents.
852 This can evaluate Lisp code. */
1fc4d463
RS
853 save_menu_items ();
854
e69b0960 855 menu_items = f->menu_bar_vector;
86c04183 856 menu_items_allocated = VECTORP (menu_items) ? ASIZE (menu_items) : 0;
77b37c05 857 subitems = ASIZE (items) / 4;
38182d90
PE
858 submenu_start = alloca ((subitems + 1) * sizeof *submenu_start);
859 submenu_end = alloca (subitems * sizeof *submenu_end);
860 submenu_n_panes = alloca (subitems * sizeof *submenu_n_panes);
861 submenu_top_level_items = alloca (subitems
862 * sizeof *submenu_top_level_items);
88766961 863 init_menu_items ();
e3019616 864 for (i = 0; i < subitems; i++)
88766961
RS
865 {
866 Lisp_Object key, string, maps;
867
28be1ada
DA
868 key = AREF (items, 4 * i);
869 string = AREF (items, 4 * i + 1);
870 maps = AREF (items, 4 * i + 2);
88766961
RS
871 if (NILP (string))
872 break;
873
ced89c24
RS
874 submenu_start[i] = menu_items_used;
875
876 menu_items_n_panes = 0;
877 submenu_top_level_items[i]
878 = parse_single_submenu (key, string, maps);
37dc84ff 879 submenu_n_panes[i] = menu_items_n_panes;
ced89c24
RS
880
881 submenu_end[i] = menu_items_used;
882 }
883
3e26f69c 884 submenu_start[i] = -1;
ced89c24
RS
885 finish_menu_items ();
886
887 /* Convert menu_items into widget_value trees
888 to display the menu. This cannot evaluate Lisp code. */
889
890 wv = xmalloc_widget_value ();
891 wv->name = "menubar";
892 wv->value = 0;
893 wv->enabled = 1;
894 wv->button_type = BUTTON_TYPE_NONE;
895 wv->help = Qnil;
896 first_wv = wv;
897
908589fd 898 for (i = 0; submenu_start[i] >= 0; i++)
ced89c24 899 {
37dc84ff 900 menu_items_n_panes = submenu_n_panes[i];
ced89c24
RS
901 wv = digest_single_submenu (submenu_start[i], submenu_end[i],
902 submenu_top_level_items[i]);
177c0ea7 903 if (prev_wv)
88766961
RS
904 prev_wv->next = wv;
905 else
906 first_wv->contents = wv;
907 /* Don't set wv->name here; GC during the loop might relocate it. */
908 wv->enabled = 1;
3427a3db 909 wv->button_type = BUTTON_TYPE_NONE;
88766961
RS
910 prev_wv = wv;
911 }
912
88766961 913 set_buffer_internal_1 (prev);
8d8a3494 914
88766961
RS
915 /* If there has been no change in the Lisp-level contents
916 of the menu bar, skip redisplaying it. Just exit. */
917
510d5bf6 918 /* Compare the new menu items with the ones computed last time. */
88766961
RS
919 for (i = 0; i < previous_menu_items_used; i++)
920 if (menu_items_used == i
28be1ada 921 || (!EQ (previous_items[i], AREF (menu_items, i))))
88766961 922 break;
62555c22 923 if (i == menu_items_used && i == previous_menu_items_used && i != 0)
88766961 924 {
510d5bf6
RS
925 /* The menu items have not changed. Don't bother updating
926 the menus in any form, since it would be a no-op. */
88766961 927 free_menubar_widget_value_tree (first_wv);
86fad4ec 928 discard_menu_items ();
1fc4d463 929 unbind_to (specpdl_count, Qnil);
88766961
RS
930 return;
931 }
932
510d5bf6 933 /* The menu items are different, so store them in the frame. */
f00af5b1 934 fset_menu_bar_vector (f, menu_items);
1fc4d463
RS
935 f->menu_bar_items_used = menu_items_used;
936
bf732638 937 /* This undoes save_menu_items. */
1fc4d463
RS
938 unbind_to (specpdl_count, Qnil);
939
88766961
RS
940 /* Now GC cannot happen during the lifetime of the widget_value,
941 so it's safe to store data from a Lisp_String. */
942 wv = first_wv->contents;
77b37c05 943 for (i = 0; i < ASIZE (items); i += 4)
88766961
RS
944 {
945 Lisp_Object string;
28be1ada 946 string = AREF (items, i + 1);
88766961 947 if (NILP (string))
2bf436c3 948 break;
51b59d79 949 wv->name = SSDATA (string);
2bf436c3
JD
950 update_submenu_strings (wv->contents);
951 wv = wv->next;
88766961
RS
952 }
953
4d19cb8e 954 }
88766961
RS
955 else
956 {
957 /* Make a widget-value tree containing
958 just the top level menu bar strings. */
4d19cb8e 959
ced89c24
RS
960 wv = xmalloc_widget_value ();
961 wv->name = "menubar";
962 wv->value = 0;
963 wv->enabled = 1;
964 wv->button_type = BUTTON_TYPE_NONE;
965 wv->help = Qnil;
966 first_wv = wv;
967
88766961 968 items = FRAME_MENU_BAR_ITEMS (f);
77b37c05 969 for (i = 0; i < ASIZE (items); i += 4)
88766961
RS
970 {
971 Lisp_Object string;
972
28be1ada 973 string = AREF (items, i + 1);
88766961
RS
974 if (NILP (string))
975 break;
976
f7fab165 977 wv = xmalloc_widget_value ();
51b59d79 978 wv->name = SSDATA (string);
88766961
RS
979 wv->value = 0;
980 wv->enabled = 1;
3427a3db 981 wv->button_type = BUTTON_TYPE_NONE;
27ad7b52 982 wv->help = Qnil;
fe8fa62f
RS
983 /* This prevents lwlib from assuming this
984 menu item is really supposed to be empty. */
d01a7826 985 /* The intptr_t cast avoids a warning.
fe8fa62f 986 This value just has to be different from small integers. */
d01a7826 987 wv->call_data = (void *) (intptr_t) (-1);
88766961 988
177c0ea7 989 if (prev_wv)
88766961
RS
990 prev_wv->next = wv;
991 else
992 first_wv->contents = wv;
993 prev_wv = wv;
994 }
62555c22
RS
995
996 /* Forget what we thought we knew about what is in the
997 detailed contents of the menu bar menus.
998 Changing the top level always destroys the contents. */
999 f->menu_bar_items_used = 0;
88766961 1000 }
4dedbfe0 1001
88766961 1002 /* Create or update the menu bar widget. */
aa669def 1003
4d7e6e51 1004 block_input ();
aa669def 1005
488dd4c4
JD
1006#ifdef USE_GTK
1007 xg_crazy_callback_abort = 1;
1008 if (menubar_widget)
1009 {
6b61353c 1010 /* The fourth arg is DEEP_P, which says to consider the entire
488dd4c4
JD
1011 menu trees we supply, rather than just the menu bar item names. */
1012 xg_modify_menubar_widgets (menubar_widget,
1013 f,
1014 first_wv,
1015 deep_p,
1016 G_CALLBACK (menubar_selection_callback),
1017 G_CALLBACK (popup_deactivate_callback),
1018 G_CALLBACK (menu_highlight_callback));
1019 }
1020 else
1021 {
488dd4c4 1022 menubar_widget
177c0ea7 1023 = xg_create_widget ("menubar", "menubar", f, first_wv,
488dd4c4
JD
1024 G_CALLBACK (menubar_selection_callback),
1025 G_CALLBACK (popup_deactivate_callback),
1026 G_CALLBACK (menu_highlight_callback));
1027
1028 f->output_data.x->menubar_widget = menubar_widget;
1029 }
1030
177c0ea7 1031
488dd4c4 1032#else /* not USE_GTK */
18686d47 1033 if (menubar_widget)
4dedbfe0
PR
1034 {
1035 /* Disable resizing (done for Motif!) */
7556890b 1036 lw_allow_resizing (f->output_data.x->widget, False);
4dedbfe0
PR
1037
1038 /* The third arg is DEEP_P, which says to consider the entire
1039 menu trees we supply, rather than just the menu bar item names. */
88766961 1040 lw_modify_all_widgets (id, first_wv, deep_p);
4dedbfe0 1041
c98fcf4b 1042 /* Re-enable the edit widget to resize. */
7556890b 1043 lw_allow_resizing (f->output_data.x->widget, True);
4dedbfe0 1044 }
18686d47
RS
1045 else
1046 {
9f6fcdc5
JD
1047 char menuOverride[] = "Ctrl<KeyPress>g: MenuGadgetEscape()";
1048 XtTranslations override = XtParseTranslationTable (menuOverride);
1049
3928f2b6
JD
1050#ifdef USE_LUCID
1051 apply_systemfont_to_menu (f, f->output_data.x->column_widget);
1052#endif
1053 menubar_widget = lw_create_widget ("menubar", "menubar", id,
1054 first_wv,
7556890b 1055 f->output_data.x->column_widget,
4dedbfe0
PR
1056 0,
1057 popup_activate_callback,
1058 menubar_selection_callback,
850df50b
GM
1059 popup_deactivate_callback,
1060 menu_highlight_callback);
7556890b 1061 f->output_data.x->menubar_widget = menubar_widget;
9f6fcdc5
JD
1062
1063 /* Make menu pop down on C-g. */
1064 XtOverrideTranslations (menubar_widget, override);
18686d47 1065 }
1d1c1567
KH
1066
1067 {
cf28cebc 1068 int menubar_size;
723f5a07
J
1069 if (f->output_data.x->menubar_widget)
1070 XtRealizeWidget (f->output_data.x->menubar_widget);
1071
cf28cebc 1072 menubar_size
7556890b
RS
1073 = (f->output_data.x->menubar_widget
1074 ? (f->output_data.x->menubar_widget->core.height
1075 + f->output_data.x->menubar_widget->core.border_width)
1d1c1567
KH
1076 : 0);
1077
5c646d5a
JD
1078#if 1 /* Experimentally, we now get the right results
1079 for -geometry -0-0 without this. 24 Aug 96, rms.
1080 Maybe so, but the menu bar size is missing the pixels so the
cf28cebc 1081 WM size hints are off by these pixels. Jan D, oct 2009. */
5ee80bd3 1082#ifdef USE_LUCID
1d1c1567
KH
1083 if (FRAME_EXTERNAL_MENU_BAR (f))
1084 {
1085 Dimension ibw = 0;
7556890b 1086 XtVaGetValues (f->output_data.x->column_widget,
1d1c1567
KH
1087 XtNinternalBorderWidth, &ibw, NULL);
1088 menubar_size += ibw;
1089 }
5ee80bd3 1090#endif /* USE_LUCID */
5c646d5a 1091#endif /* 1 */
1d1c1567 1092
7556890b 1093 f->output_data.x->menubar_height = menubar_size;
1d1c1567 1094 }
488dd4c4 1095#endif /* not USE_GTK */
177c0ea7 1096
18686d47 1097 free_menubar_widget_value_tree (first_wv);
364cd450 1098 update_frame_menubar (f);
18686d47 1099
488dd4c4
JD
1100#ifdef USE_GTK
1101 xg_crazy_callback_abort = 0;
1102#endif
1103
4d7e6e51 1104 unblock_input ();
18686d47 1105}
85f487d1 1106
8e6208c5 1107/* Called from Fx_create_frame to create the initial menubar of a frame
4dedbfe0
PR
1108 before it is mapped, so that the window is mapped with the menubar already
1109 there instead of us tacking it on later and thrashing the window after it
1110 is visible. */
1111
1112void
a10c8269 1113initialize_frame_menubar (struct frame *f)
4dedbfe0
PR
1114{
1115 /* This function is called before the first chance to redisplay
1116 the frame. It has to be, so the frame will have the right size. */
f00af5b1 1117 fset_menu_bar_items (f, menu_bar_items (FRAME_MENU_BAR_ITEMS (f)));
88766961 1118 set_frame_menubar (f, 1, 1);
4dedbfe0
PR
1119}
1120
aba25348 1121
4dedbfe0 1122/* Get rid of the menu bar of frame F, and free its storage.
488dd4c4
JD
1123 This is used when deleting a frame, and when turning off the menu bar.
1124 For GTK this function is in gtkutil.c. */
4dedbfe0 1125
488dd4c4 1126#ifndef USE_GTK
85f487d1 1127void
a10c8269 1128free_frame_menubar (struct frame *f)
85f487d1
FP
1129{
1130 Widget menubar_widget;
85f487d1 1131
65b02bb9 1132 eassert (FRAME_X_P (f));
62af879c 1133
7556890b 1134 menubar_widget = f->output_data.x->menubar_widget;
a45bad2a
RS
1135
1136 f->output_data.x->menubar_height = 0;
177c0ea7 1137
85f487d1
FP
1138 if (menubar_widget)
1139 {
aba25348
GM
1140#ifdef USE_MOTIF
1141 /* Removing the menu bar magically changes the shell widget's x
1142 and y position of (0, 0) which, when the menu bar is turned
53964682 1143 on again, leads to pull-down menus appearing in strange
aba25348
GM
1144 positions near the upper-left corner of the display. This
1145 happens only with some window managers like twm and ctwm,
1146 but not with other like Motif's mwm or kwm, because the
1147 latter generate ConfigureNotify events when the menu bar
1148 is switched off, which fixes the shell position. */
1149 Position x0, y0, x1, y1;
1150#endif
177c0ea7 1151
4d7e6e51 1152 block_input ();
aba25348
GM
1153
1154#ifdef USE_MOTIF
ae556422
GM
1155 if (f->output_data.x->widget)
1156 XtVaGetValues (f->output_data.x->widget, XtNx, &x0, XtNy, &y0, NULL);
aba25348 1157#endif
177c0ea7 1158
7556890b 1159 lw_destroy_all_widgets ((LWLIB_ID) f->output_data.x->id);
0c05dcd3 1160 f->output_data.x->menubar_widget = NULL;
aba25348 1161
ae556422
GM
1162 if (f->output_data.x->widget)
1163 {
23243f29 1164#ifdef USE_MOTIF
ae556422
GM
1165 XtVaGetValues (f->output_data.x->widget, XtNx, &x1, XtNy, &y1, NULL);
1166 if (x1 == 0 && y1 == 0)
1167 XtVaSetValues (f->output_data.x->widget, XtNx, x0, XtNy, y0, NULL);
aba25348 1168#endif
880e6158 1169 x_set_window_size (f, 0, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f), 1);
23243f29 1170 }
4d7e6e51 1171 unblock_input ();
85f487d1
FP
1172 }
1173}
488dd4c4 1174#endif /* not USE_GTK */
78589e07 1175
488dd4c4 1176#endif /* USE_X_TOOLKIT || USE_GTK */
78589e07
RS
1177\f
1178/* xmenu_show actually displays a menu using the panes and items in menu_items
1179 and returns the value selected from it.
1180 There are two versions of xmenu_show, one for Xt and one for Xlib.
1181 Both assume input is blocked by the caller. */
1182
1183/* F is the frame the menu is for.
1184 X and Y are the frame-relative specified position,
1185 relative to the inside upper left corner of the frame F.
7cded46f
PE
1186 FOR_CLICK is true if this menu was invoked for a mouse click.
1187 KEYMAPS is true if this menu was specified with keymaps;
78589e07
RS
1188 in that case, we return a list containing the chosen item's value
1189 and perhaps also the pane's prefix.
1190 TITLE is the specified menu title.
1191 ERROR is a place to store an error message string in case of failure.
1192 (We return nil on failure, but the value doesn't actually matter.) */
18686d47 1193
488dd4c4
JD
1194#if defined (USE_X_TOOLKIT) || defined (USE_GTK)
1195
1196/* The item selected in the popup menu. */
1197static Lisp_Object *volatile menu_item_selection;
1198
1199#ifdef USE_GTK
1200
1201/* Used when position a popup menu. See menu_position_func and
1202 create_and_show_popup_menu below. */
1203struct next_popup_x_y
1204{
a10c8269 1205 struct frame *f;
488dd4c4
JD
1206 int x;
1207 int y;
1208};
1209
1210/* The menu position function to use if we are not putting a popup
1211 menu where the pointer is.
1212 MENU is the menu to pop up.
1213 X and Y shall on exit contain x/y where the menu shall pop up.
1214 PUSH_IN is not documented in the GTK manual.
1215 USER_DATA is any data passed in when calling gtk_menu_popup.
1216 Here it points to a struct next_popup_x_y where the coordinates
7b76ca1c 1217 to store in *X and *Y are as well as the frame for the popup.
488dd4c4
JD
1218
1219 Here only X and Y are used. */
1220static void
971de7fb 1221menu_position_func (GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer user_data)
488dd4c4 1222{
7d652d97 1223 struct next_popup_x_y *data = user_data;
7b76ca1c 1224 GtkRequisition req;
aad3612f 1225 struct x_display_info *dpyinfo = FRAME_DISPLAY_INFO (data->f);
fd2f80c6
CY
1226 int disp_width = x_display_pixel_width (dpyinfo);
1227 int disp_height = x_display_pixel_height (dpyinfo);
b9de078a 1228
7b76ca1c
JD
1229 *x = data->x;
1230 *y = data->y;
1231
1232 /* Check if there is room for the menu. If not, adjust x/y so that
1233 the menu is fully visible. */
0afb4571 1234 gtk_widget_get_preferred_size (GTK_WIDGET (menu), NULL, &req);
7b76ca1c
JD
1235 if (data->x + req.width > disp_width)
1236 *x -= data->x + req.width - disp_width;
1237 if (data->y + req.height > disp_height)
1238 *y -= data->y + req.height - disp_height;
488dd4c4
JD
1239}
1240
1241static void
971de7fb 1242popup_selection_callback (GtkWidget *widget, gpointer client_data)
488dd4c4 1243{
7d652d97 1244 xg_menu_item_cb_data *cb_data = client_data;
488dd4c4
JD
1245
1246 if (xg_crazy_callback_abort) return;
7d652d97 1247 if (cb_data) menu_item_selection = cb_data->call_data;
488dd4c4
JD
1248}
1249
27e498e6
PE
1250static void
1251pop_down_menu (void *arg)
af89e871
JD
1252{
1253 popup_activated_flag = 0;
4d7e6e51 1254 block_input ();
27e498e6 1255 gtk_widget_destroy (GTK_WIDGET (arg));
4d7e6e51 1256 unblock_input ();
af89e871
JD
1257}
1258
488dd4c4
JD
1259/* Pop up the menu for frame F defined by FIRST_WV at X/Y and loop until the
1260 menu pops down.
1261 menu_item_selection will be set to the selection. */
1262static void
6bbe6da8
DA
1263create_and_show_popup_menu (struct frame *f, widget_value *first_wv,
1264 int x, int y, bool for_click)
488dd4c4
JD
1265{
1266 int i;
1267 GtkWidget *menu;
1268 GtkMenuPositionFunc pos_func = 0; /* Pop up at pointer. */
1269 struct next_popup_x_y popup_x_y;
d311d28c 1270 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
7cded46f 1271 bool use_pos_func = ! for_click;
93d5ca1f
JD
1272
1273#ifdef HAVE_GTK3
1274 /* Always use position function for Gtk3. Otherwise menus may become
1275 too small to show anything. */
1276 use_pos_func = 1;
1277#endif
488dd4c4 1278
65b02bb9 1279 eassert (FRAME_X_P (f));
62af879c 1280
488dd4c4
JD
1281 xg_crazy_callback_abort = 1;
1282 menu = xg_create_widget ("popup", first_wv->name, f, first_wv,
1283 G_CALLBACK (popup_selection_callback),
1284 G_CALLBACK (popup_deactivate_callback),
1285 G_CALLBACK (menu_highlight_callback));
1286 xg_crazy_callback_abort = 0;
177c0ea7 1287
93d5ca1f 1288 if (use_pos_func)
488dd4c4
JD
1289 {
1290 /* Not invoked by a click. pop up at x/y. */
1291 pos_func = menu_position_func;
1292
1293 /* Adjust coordinates to be root-window-relative. */
9882535b
KS
1294 x += f->left_pos + FRAME_OUTER_TO_INNER_DIFF_X (f);
1295 y += f->top_pos + FRAME_OUTER_TO_INNER_DIFF_Y (f);
488dd4c4
JD
1296
1297 popup_x_y.x = x;
1298 popup_x_y.y = y;
7b76ca1c 1299 popup_x_y.f = f;
488dd4c4 1300
9b85e63d
JD
1301 i = 0; /* gtk_menu_popup needs this to be 0 for a non-button popup. */
1302 }
93d5ca1f
JD
1303
1304 if (for_click)
9b85e63d
JD
1305 {
1306 for (i = 0; i < 5; i++)
aad3612f 1307 if (FRAME_DISPLAY_INFO (f)->grabbed & (1 << i))
9b85e63d
JD
1308 break;
1309 }
59cfb104 1310
488dd4c4
JD
1311 /* Display the menu. */
1312 gtk_widget_show_all (menu);
c0df13a6 1313
a20903d0 1314 gtk_menu_popup (GTK_MENU (menu), 0, 0, pos_func, &popup_x_y, i,
6bbe6da8 1315 FRAME_DISPLAY_INFO (f)->last_user_time);
177c0ea7 1316
27e498e6 1317 record_unwind_protect_ptr (pop_down_menu, menu);
af89e871 1318
e547b051 1319 if (gtk_widget_get_mapped (menu))
ff18668f
JD
1320 {
1321 /* Set this to one. popup_widget_loop increases it by one, so it becomes
1322 two. show_help_echo uses this to detect popup menus. */
1323 popup_activated_flag = 1;
1324 /* Process events that apply to the menu. */
1325 popup_widget_loop (1, menu);
1326 }
488dd4c4 1327
af89e871 1328 unbind_to (specpdl_count, Qnil);
177c0ea7 1329
488dd4c4
JD
1330 /* Must reset this manually because the button release event is not passed
1331 to Emacs event loop. */
aad3612f 1332 FRAME_DISPLAY_INFO (f)->grabbed = 0;
488dd4c4
JD
1333}
1334
1335#else /* not USE_GTK */
18686d47 1336
8ed87156 1337/* We need a unique id for each widget handled by the Lucid Widget
cc17e9bf
KH
1338 library.
1339
1340 For the main windows, and popup menus, we use this counter,
88766961 1341 which we increment each time after use. This starts from 1<<16.
cc17e9bf 1342
88766961
RS
1343 For menu bars, we use numbers starting at 0, counted in
1344 next_menubar_widget_id. */
8ed87156 1345LWLIB_ID widget_id_tick;
165e1749 1346
4dedbfe0 1347static void
ebd15611 1348popup_selection_callback (Widget widget, LWLIB_ID id, XtPointer client_data)
4dedbfe0 1349{
7d652d97 1350 menu_item_selection = client_data;
4dedbfe0
PR
1351}
1352
af89e871
JD
1353/* ARG is the LWLIB ID of the dialog box, represented
1354 as a Lisp object as (HIGHPART . LOWPART). */
1355
27e498e6 1356static void
ebd15611 1357pop_down_menu (Lisp_Object arg)
af89e871
JD
1358{
1359 LWLIB_ID id = (XINT (XCAR (arg)) << 4 * sizeof (LWLIB_ID)
1360 | XINT (XCDR (arg)));
1361
4d7e6e51 1362 block_input ();
af89e871 1363 lw_destroy_all_widgets (id);
4d7e6e51 1364 unblock_input ();
af89e871 1365 popup_activated_flag = 0;
af89e871
JD
1366}
1367
488dd4c4
JD
1368/* Pop up the menu for frame F defined by FIRST_WV at X/Y and loop until the
1369 menu pops down.
1370 menu_item_selection will be set to the selection. */
1371static void
a10c8269 1372create_and_show_popup_menu (struct frame *f, widget_value *first_wv,
6bbe6da8 1373 int x, int y, bool for_click)
488dd4c4
JD
1374{
1375 int i;
1376 Arg av[2];
1377 int ac = 0;
65969f63
CY
1378 XEvent dummy;
1379 XButtonPressedEvent *event = &(dummy.xbutton);
488dd4c4
JD
1380 LWLIB_ID menu_id;
1381 Widget menu;
488dd4c4 1382
65b02bb9 1383 eassert (FRAME_X_P (f));
62af879c 1384
3928f2b6
JD
1385#ifdef USE_LUCID
1386 apply_systemfont_to_menu (f, f->output_data.x->widget);
1387#endif
1388
488dd4c4
JD
1389 menu_id = widget_id_tick++;
1390 menu = lw_create_widget ("popup", first_wv->name, menu_id, first_wv,
1391 f->output_data.x->widget, 1, 0,
1392 popup_selection_callback,
1393 popup_deactivate_callback,
1394 menu_highlight_callback);
1395
65969f63
CY
1396 event->type = ButtonPress;
1397 event->serial = 0;
1398 event->send_event = 0;
1399 event->display = FRAME_X_DISPLAY (f);
1400 event->time = CurrentTime;
aad3612f 1401 event->root = FRAME_DISPLAY_INFO (f)->root_window;
65969f63
CY
1402 event->window = event->subwindow = event->root;
1403 event->x = x;
1404 event->y = y;
488dd4c4
JD
1405
1406 /* Adjust coordinates to be root-window-relative. */
9882535b
KS
1407 x += f->left_pos + FRAME_OUTER_TO_INNER_DIFF_X (f);
1408 y += f->top_pos + FRAME_OUTER_TO_INNER_DIFF_Y (f);
488dd4c4 1409
65969f63
CY
1410 event->x_root = x;
1411 event->y_root = y;
488dd4c4 1412
65969f63
CY
1413 event->state = 0;
1414 event->button = 0;
488dd4c4 1415 for (i = 0; i < 5; i++)
aad3612f 1416 if (FRAME_DISPLAY_INFO (f)->grabbed & (1 << i))
65969f63 1417 event->button = i;
488dd4c4
JD
1418
1419 /* Don't allow any geometry request from the user. */
1420 XtSetArg (av[ac], XtNgeometry, 0); ac++;
1421 XtSetValues (menu, av, ac);
1422
1423 /* Display the menu. */
65969f63 1424 lw_popup_menu (menu, &dummy);
488dd4c4 1425 popup_activated_flag = 1;
98a20c65 1426 x_activate_timeout_atimer ();
59cfb104 1427
af89e871
JD
1428 {
1429 int fact = 4 * sizeof (LWLIB_ID);
d311d28c 1430 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
af89e871
JD
1431 record_unwind_protect (pop_down_menu,
1432 Fcons (make_number (menu_id >> (fact)),
1433 make_number (menu_id & ~(-1 << (fact)))));
488dd4c4 1434
af89e871 1435 /* Process events that apply to the menu. */
aad3612f 1436 popup_get_selection (0, FRAME_DISPLAY_INFO (f), menu_id, 1);
488dd4c4 1437
af89e871
JD
1438 unbind_to (specpdl_count, Qnil);
1439 }
488dd4c4
JD
1440}
1441
1442#endif /* not USE_GTK */
1443
27e498e6
PE
1444static void
1445cleanup_widget_value_tree (void *arg)
ba24cea2 1446{
27e498e6 1447 free_menubar_widget_value_tree (arg);
ba24cea2
YM
1448}
1449
ef7417fd 1450Lisp_Object
a10c8269 1451xmenu_show (struct frame *f, int x, int y, bool for_click, bool keymaps,
6bbe6da8 1452 Lisp_Object title, const char **error_name)
18686d47 1453{
78589e07 1454 int i;
78589e07 1455 widget_value *wv, *save_wv = 0, *first_wv = 0, *prev_wv = 0;
101bb4a5 1456 widget_value **submenu_stack
38182d90 1457 = alloca (menu_items_used * sizeof *submenu_stack);
101bb4a5 1458 Lisp_Object *subprefix_stack
38182d90 1459 = alloca (menu_items_used * sizeof *subprefix_stack);
101bb4a5 1460 int submenu_depth = 0;
4e8d3549 1461
78c8278d
RS
1462 int first_pane;
1463
9898bd0e 1464 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
ba24cea2 1465
65b02bb9 1466 eassert (FRAME_X_P (f));
62af879c 1467
d4323972 1468 *error_name = NULL;
78589e07 1469
742f715d
KH
1470 if (menu_items_used <= MENU_ITEMS_PANE_LENGTH)
1471 {
d4323972 1472 *error_name = "Empty menu";
742f715d
KH
1473 return Qnil;
1474 }
63c414df 1475
f0177f86
EZ
1476 block_input ();
1477
78589e07
RS
1478 /* Create a tree of widget_value objects
1479 representing the panes and their items. */
f7fab165 1480 wv = xmalloc_widget_value ();
78589e07
RS
1481 wv->name = "menu";
1482 wv->value = 0;
1483 wv->enabled = 1;
3427a3db 1484 wv->button_type = BUTTON_TYPE_NONE;
27ad7b52 1485 wv->help =Qnil;
78589e07 1486 first_wv = wv;
78c8278d 1487 first_pane = 1;
177c0ea7 1488
78589e07
RS
1489 /* Loop over all panes and items, filling in the tree. */
1490 i = 0;
1491 while (i < menu_items_used)
1492 {
28be1ada 1493 if (EQ (AREF (menu_items, i), Qnil))
101bb4a5
RS
1494 {
1495 submenu_stack[submenu_depth++] = save_wv;
1496 save_wv = prev_wv;
1497 prev_wv = 0;
78c8278d 1498 first_pane = 1;
101bb4a5
RS
1499 i++;
1500 }
28be1ada 1501 else if (EQ (AREF (menu_items, i), Qlambda))
101bb4a5
RS
1502 {
1503 prev_wv = save_wv;
1504 save_wv = submenu_stack[--submenu_depth];
78c8278d 1505 first_pane = 0;
101bb4a5
RS
1506 i++;
1507 }
28be1ada 1508 else if (EQ (AREF (menu_items, i), Qt)
101bb4a5
RS
1509 && submenu_depth != 0)
1510 i += MENU_ITEMS_PANE_LENGTH;
fcaa7665
RS
1511 /* Ignore a nil in the item list.
1512 It's meaningful only for dialog boxes. */
28be1ada 1513 else if (EQ (AREF (menu_items, i), Qquote))
fcaa7665 1514 i += 1;
28be1ada 1515 else if (EQ (AREF (menu_items, i), Qt))
78589e07
RS
1516 {
1517 /* Create a new pane. */
1518 Lisp_Object pane_name, prefix;
6d1f7fee 1519 const char *pane_string;
177c0ea7 1520
4c329aa8
GM
1521 pane_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME);
1522 prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
177c0ea7 1523
703dc2a8 1524#ifndef HAVE_MULTILINGUAL_MENU
4c329aa8
GM
1525 if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name))
1526 {
8af9fa55 1527 pane_name = ENCODE_MENU_STRING (pane_name);
3ae565b3 1528 ASET (menu_items, i + MENU_ITEMS_PANE_NAME, pane_name);
4c329aa8 1529 }
703dc2a8 1530#endif
78589e07 1531 pane_string = (NILP (pane_name)
51b59d79 1532 ? "" : SSDATA (pane_name));
101bb4a5 1533 /* If there is just one top-level pane, put all its items directly
78589e07
RS
1534 under the top-level menu. */
1535 if (menu_items_n_panes == 1)
1536 pane_string = "";
1537
1538 /* If the pane has a meaningful name,
1539 make the pane a top-level menu item
1540 with its items as a submenu beneath it. */
78c8278d 1541 if (!keymaps && strcmp (pane_string, ""))
78589e07 1542 {
f7fab165 1543 wv = xmalloc_widget_value ();
78589e07
RS
1544 if (save_wv)
1545 save_wv->next = wv;
1546 else
1547 first_wv->contents = wv;
b00876c9 1548 wv->name = (char *) pane_string;
78589e07
RS
1549 if (keymaps && !NILP (prefix))
1550 wv->name++;
1551 wv->value = 0;
1552 wv->enabled = 1;
3427a3db 1553 wv->button_type = BUTTON_TYPE_NONE;
27ad7b52 1554 wv->help = Qnil;
78c8278d
RS
1555 save_wv = wv;
1556 prev_wv = 0;
78589e07 1557 }
78c8278d
RS
1558 else if (first_pane)
1559 {
1560 save_wv = wv;
1561 prev_wv = 0;
1562 }
1563 first_pane = 0;
78589e07
RS
1564 i += MENU_ITEMS_PANE_LENGTH;
1565 }
1566 else
1567 {
1568 /* Create a new item within current pane. */
9cd50434 1569 Lisp_Object item_name, enable, descrip, def, type, selected, help;
4c329aa8
GM
1570 item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
1571 enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
1572 descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY);
1573 def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION);
1574 type = AREF (menu_items, i + MENU_ITEMS_ITEM_TYPE);
1575 selected = AREF (menu_items, i + MENU_ITEMS_ITEM_SELECTED);
1576 help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP);
3427a3db 1577
703dc2a8 1578#ifndef HAVE_MULTILINGUAL_MENU
3427a3db 1579 if (STRINGP (item_name) && STRING_MULTIBYTE (item_name))
4c329aa8 1580 {
646f98ec 1581 item_name = ENCODE_MENU_STRING (item_name);
3ae565b3 1582 ASET (menu_items, i + MENU_ITEMS_ITEM_NAME, item_name);
4c329aa8 1583 }
177c0ea7 1584
3427a3db 1585 if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
4c329aa8 1586 {
646f98ec 1587 descrip = ENCODE_MENU_STRING (descrip);
3ae565b3 1588 ASET (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY, descrip);
4c329aa8
GM
1589 }
1590#endif /* not HAVE_MULTILINGUAL_MENU */
177c0ea7 1591
f7fab165 1592 wv = xmalloc_widget_value ();
177c0ea7 1593 if (prev_wv)
78589e07 1594 prev_wv->next = wv;
177c0ea7 1595 else
78589e07 1596 save_wv->contents = wv;
51b59d79 1597 wv->name = SSDATA (item_name);
78589e07 1598 if (!NILP (descrip))
51b59d79 1599 wv->key = SSDATA (descrip);
78589e07 1600 wv->value = 0;
a352a815
RS
1601 /* If this item has a null value,
1602 make the call_data null so that it won't display a box
1603 when the mouse is on it. */
4939150c 1604 wv->call_data = !NILP (def) ? aref_addr (menu_items, i) : 0;
78589e07 1605 wv->enabled = !NILP (enable);
3427a3db
GM
1606
1607 if (NILP (type))
1608 wv->button_type = BUTTON_TYPE_NONE;
1609 else if (EQ (type, QCtoggle))
1610 wv->button_type = BUTTON_TYPE_TOGGLE;
1611 else if (EQ (type, QCradio))
1612 wv->button_type = BUTTON_TYPE_RADIO;
1613 else
1088b922 1614 emacs_abort ();
3427a3db
GM
1615
1616 wv->selected = !NILP (selected);
cc45fb40 1617
0b1f4572
RS
1618 if (! STRINGP (help))
1619 help = Qnil;
1620
1621 wv->help = help;
cc45fb40 1622
78589e07
RS
1623 prev_wv = wv;
1624
1625 i += MENU_ITEMS_ITEM_LENGTH;
1626 }
1627 }
1628
c98fcf4b 1629 /* Deal with the title, if it is non-nil. */
4dedbfe0
PR
1630 if (!NILP (title))
1631 {
f7fab165
RS
1632 widget_value *wv_title = xmalloc_widget_value ();
1633 widget_value *wv_sep1 = xmalloc_widget_value ();
1634 widget_value *wv_sep2 = xmalloc_widget_value ();
4dedbfe0
PR
1635
1636 wv_sep2->name = "--";
1637 wv_sep2->next = first_wv->contents;
27ad7b52 1638 wv_sep2->help = Qnil;
4dedbfe0
PR
1639
1640 wv_sep1->name = "--";
1641 wv_sep1->next = wv_sep2;
27ad7b52 1642 wv_sep1->help = Qnil;
4dedbfe0 1643
703dc2a8
KH
1644#ifndef HAVE_MULTILINGUAL_MENU
1645 if (STRING_MULTIBYTE (title))
646f98ec 1646 title = ENCODE_MENU_STRING (title);
703dc2a8 1647#endif
177c0ea7 1648
51b59d79 1649 wv_title->name = SSDATA (title);
cc45fb40 1650 wv_title->enabled = TRUE;
3427a3db 1651 wv_title->button_type = BUTTON_TYPE_NONE;
27ad7b52 1652 wv_title->help = Qnil;
a9d8395f 1653 wv_title->next = wv_sep1;
4dedbfe0
PR
1654 first_wv->contents = wv_title;
1655 }
1656
78589e07
RS
1657 /* No selection has been chosen yet. */
1658 menu_item_selection = 0;
1659
ba24cea2
YM
1660 /* Make sure to free the widget_value objects we used to specify the
1661 contents even with longjmp. */
27e498e6 1662 record_unwind_protect_ptr (cleanup_widget_value_tree, first_wv);
ba24cea2 1663
488dd4c4 1664 /* Actually create and show the menu until popped down. */
6bbe6da8 1665 create_and_show_popup_menu (f, first_wv, x, y, for_click);
18686d47 1666
ba24cea2 1667 unbind_to (specpdl_count, Qnil);
18686d47 1668
78589e07
RS
1669 /* Find the selected item, and its pane, to return
1670 the proper value. */
1671 if (menu_item_selection != 0)
1672 {
c63f6952 1673 Lisp_Object prefix, entry;
78589e07 1674
6bbd7a29 1675 prefix = entry = Qnil;
78589e07
RS
1676 i = 0;
1677 while (i < menu_items_used)
1678 {
28be1ada 1679 if (EQ (AREF (menu_items, i), Qnil))
101bb4a5
RS
1680 {
1681 subprefix_stack[submenu_depth++] = prefix;
1682 prefix = entry;
1683 i++;
1684 }
28be1ada 1685 else if (EQ (AREF (menu_items, i), Qlambda))
101bb4a5
RS
1686 {
1687 prefix = subprefix_stack[--submenu_depth];
1688 i++;
1689 }
28be1ada 1690 else if (EQ (AREF (menu_items, i), Qt))
78589e07
RS
1691 {
1692 prefix
28be1ada 1693 = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
78589e07
RS
1694 i += MENU_ITEMS_PANE_LENGTH;
1695 }
d31d42cc
RS
1696 /* Ignore a nil in the item list.
1697 It's meaningful only for dialog boxes. */
28be1ada 1698 else if (EQ (AREF (menu_items, i), Qquote))
d31d42cc 1699 i += 1;
78589e07
RS
1700 else
1701 {
1702 entry
28be1ada 1703 = AREF (menu_items, i + MENU_ITEMS_ITEM_VALUE);
4939150c 1704 if (menu_item_selection == aref_addr (menu_items, i))
78589e07 1705 {
7cded46f 1706 if (keymaps)
78589e07 1707 {
101bb4a5
RS
1708 int j;
1709
6c6f1994 1710 entry = list1 (entry);
78589e07
RS
1711 if (!NILP (prefix))
1712 entry = Fcons (prefix, entry);
101bb4a5 1713 for (j = submenu_depth - 1; j >= 0; j--)
e48087b7 1714 if (!NILP (subprefix_stack[j]))
5964e450 1715 entry = Fcons (subprefix_stack[j], entry);
78589e07 1716 }
f0177f86 1717 unblock_input ();
78589e07
RS
1718 return entry;
1719 }
1720 i += MENU_ITEMS_ITEM_LENGTH;
1721 }
1722 }
1723 }
be6ed24a 1724 else if (!for_click)
f0177f86
EZ
1725 {
1726 unblock_input ();
1727 /* Make "Cancel" equivalent to C-g. */
1728 Fsignal (Qquit, Qnil);
1729 }
78589e07 1730
f0177f86 1731 unblock_input ();
78589e07 1732 return Qnil;
18686d47 1733}
4dedbfe0 1734\f
488dd4c4
JD
1735#ifdef USE_GTK
1736static void
971de7fb 1737dialog_selection_callback (GtkWidget *widget, gpointer client_data)
488dd4c4 1738{
8ac068ac 1739 /* Treat the pointer as an integer. There's no problem
488dd4c4 1740 as long as pointers have enough bits to hold small integers. */
d01a7826 1741 if ((intptr_t) client_data != -1)
7d652d97 1742 menu_item_selection = client_data;
488dd4c4
JD
1743
1744 popup_activated_flag = 0;
1745}
1746
1747/* Pop up the dialog for frame F defined by FIRST_WV and loop until the
1748 dialog pops down.
1749 menu_item_selection will be set to the selection. */
1750static void
a10c8269 1751create_and_show_dialog (struct frame *f, widget_value *first_wv)
488dd4c4
JD
1752{
1753 GtkWidget *menu;
1754
65b02bb9 1755 eassert (FRAME_X_P (f));
62af879c 1756
488dd4c4
JD
1757 menu = xg_create_widget ("dialog", first_wv->name, f, first_wv,
1758 G_CALLBACK (dialog_selection_callback),
1759 G_CALLBACK (popup_deactivate_callback),
1760 0);
1761
1762 if (menu)
1763 {
d311d28c 1764 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
27e498e6 1765 record_unwind_protect_ptr (pop_down_menu, menu);
af89e871 1766
488dd4c4
JD
1767 /* Display the menu. */
1768 gtk_widget_show_all (menu);
1769
1770 /* Process events that apply to the menu. */
f1d1cd24 1771 popup_widget_loop (1, menu);
177c0ea7 1772
af89e871 1773 unbind_to (specpdl_count, Qnil);
488dd4c4
JD
1774 }
1775}
1776
1777#else /* not USE_GTK */
4dedbfe0 1778static void
ebd15611 1779dialog_selection_callback (Widget widget, LWLIB_ID id, XtPointer client_data)
4dedbfe0 1780{
4475bec4 1781 /* Treat the pointer as an integer. There's no problem
01d5e892 1782 as long as pointers have enough bits to hold small integers. */
4475bec4 1783 if ((intptr_t) client_data != -1)
7d652d97 1784 menu_item_selection = client_data;
488dd4c4 1785
4d7e6e51 1786 block_input ();
4dedbfe0 1787 lw_destroy_all_widgets (id);
4d7e6e51 1788 unblock_input ();
9572375b 1789 popup_activated_flag = 0;
4dedbfe0 1790}
18686d47 1791
488dd4c4 1792
488dd4c4
JD
1793/* Pop up the dialog for frame F defined by FIRST_WV and loop until the
1794 dialog pops down.
1795 menu_item_selection will be set to the selection. */
1796static void
a10c8269 1797create_and_show_dialog (struct frame *f, widget_value *first_wv)
488dd4c4
JD
1798{
1799 LWLIB_ID dialog_id;
1800
65b02bb9 1801 eassert (FRAME_X_P (f));
62af879c 1802
488dd4c4 1803 dialog_id = widget_id_tick++;
52214050 1804#ifdef USE_LUCID
1ecb2d3f
JD
1805 apply_systemfont_to_dialog (f->output_data.x->widget);
1806#endif
488dd4c4
JD
1807 lw_create_widget (first_wv->name, "dialog", dialog_id, first_wv,
1808 f->output_data.x->widget, 1, 0,
1809 dialog_selection_callback, 0, 0);
1810 lw_modify_all_widgets (dialog_id, first_wv->contents, True);
488dd4c4
JD
1811 /* Display the dialog box. */
1812 lw_pop_up_all_widgets (dialog_id);
1813 popup_activated_flag = 1;
98a20c65 1814 x_activate_timeout_atimer ();
488dd4c4
JD
1815
1816 /* Process events that apply to the dialog box.
1817 Also handle timers. */
1818 {
d311d28c 1819 ptrdiff_t count = SPECPDL_INDEX ();
488dd4c4 1820 int fact = 4 * sizeof (LWLIB_ID);
177c0ea7 1821
488dd4c4 1822 /* xdialog_show_unwind is responsible for popping the dialog box down. */
af89e871 1823 record_unwind_protect (pop_down_menu,
488dd4c4
JD
1824 Fcons (make_number (dialog_id >> (fact)),
1825 make_number (dialog_id & ~(-1 << (fact)))));
1826
aad3612f 1827 popup_get_selection (0, FRAME_DISPLAY_INFO (f), dialog_id, 1);
488dd4c4
JD
1828
1829 unbind_to (count, Qnil);
1830 }
1831}
1832
1833#endif /* not USE_GTK */
1834
42ca4633 1835static const char * button_names [] = {
165e1749
FP
1836 "button1", "button2", "button3", "button4", "button5",
1837 "button6", "button7", "button8", "button9", "button10" };
1838
1839static Lisp_Object
a10c8269 1840xdialog_show (struct frame *f,
7cded46f 1841 bool keymaps,
42ca4633
J
1842 Lisp_Object title,
1843 Lisp_Object header,
1844 const char **error_name)
165e1749
FP
1845{
1846 int i, nb_buttons=0;
80670155 1847 char dialog_name[6];
165e1749 1848
faa935b6 1849 widget_value *wv, *first_wv = 0, *prev_wv = 0;
165e1749 1850
fcaa7665
RS
1851 /* Number of elements seen so far, before boundary. */
1852 int left_count = 0;
1853 /* 1 means we've seen the boundary between left-hand elts and right-hand. */
1854 int boundary_seen = 0;
1855
9898bd0e 1856 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
ba24cea2 1857
65b02bb9 1858 eassert (FRAME_X_P (f));
62af879c 1859
6a040d6a 1860 *error_name = NULL;
165e1749 1861
80670155
RS
1862 if (menu_items_n_panes > 1)
1863 {
6a040d6a 1864 *error_name = "Multiple panes in dialog box";
80670155
RS
1865 return Qnil;
1866 }
1867
165e1749
FP
1868 /* Create a tree of widget_value objects
1869 representing the text label and buttons. */
1870 {
1871 Lisp_Object pane_name, prefix;
6d1f7fee 1872 const char *pane_string;
28be1ada
DA
1873 pane_name = AREF (menu_items, MENU_ITEMS_PANE_NAME);
1874 prefix = AREF (menu_items, MENU_ITEMS_PANE_PREFIX);
165e1749 1875 pane_string = (NILP (pane_name)
51b59d79 1876 ? "" : SSDATA (pane_name));
f7fab165 1877 prev_wv = xmalloc_widget_value ();
b00876c9 1878 prev_wv->value = (char *) pane_string;
165e1749
FP
1879 if (keymaps && !NILP (prefix))
1880 prev_wv->name++;
1881 prev_wv->enabled = 1;
1882 prev_wv->name = "message";
27ad7b52 1883 prev_wv->help = Qnil;
165e1749 1884 first_wv = prev_wv;
177c0ea7 1885
165e1749
FP
1886 /* Loop over all panes and items, filling in the tree. */
1887 i = MENU_ITEMS_PANE_LENGTH;
1888 while (i < menu_items_used)
1889 {
177c0ea7 1890
165e1749
FP
1891 /* Create a new item within current pane. */
1892 Lisp_Object item_name, enable, descrip;
28be1ada
DA
1893 item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
1894 enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
165e1749 1895 descrip
28be1ada 1896 = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY);
177c0ea7 1897
80670155
RS
1898 if (NILP (item_name))
1899 {
1900 free_menubar_widget_value_tree (first_wv);
6a040d6a 1901 *error_name = "Submenu in dialog items";
80670155
RS
1902 return Qnil;
1903 }
fcaa7665
RS
1904 if (EQ (item_name, Qquote))
1905 {
1906 /* This is the boundary between left-side elts
1907 and right-side elts. Stop incrementing right_count. */
1908 boundary_seen = 1;
1909 i++;
1910 continue;
1911 }
86e71abf 1912 if (nb_buttons >= 9)
80670155
RS
1913 {
1914 free_menubar_widget_value_tree (first_wv);
6a040d6a 1915 *error_name = "Too many dialog items";
80670155
RS
1916 return Qnil;
1917 }
1918
f7fab165 1919 wv = xmalloc_widget_value ();
165e1749 1920 prev_wv->next = wv;
80670155 1921 wv->name = (char *) button_names[nb_buttons];
165e1749 1922 if (!NILP (descrip))
51b59d79
PE
1923 wv->key = SSDATA (descrip);
1924 wv->value = SSDATA (item_name);
4939150c 1925 wv->call_data = aref_addr (menu_items, i);
165e1749 1926 wv->enabled = !NILP (enable);
27ad7b52 1927 wv->help = Qnil;
165e1749
FP
1928 prev_wv = wv;
1929
fcaa7665
RS
1930 if (! boundary_seen)
1931 left_count++;
1932
165e1749
FP
1933 nb_buttons++;
1934 i += MENU_ITEMS_ITEM_LENGTH;
1935 }
1936
fcaa7665
RS
1937 /* If the boundary was not specified,
1938 by default put half on the left and half on the right. */
1939 if (! boundary_seen)
1940 left_count = nb_buttons - nb_buttons / 2;
1941
f7fab165 1942 wv = xmalloc_widget_value ();
80670155 1943 wv->name = dialog_name;
27ad7b52 1944 wv->help = Qnil;
6a040d6a
NR
1945
1946 /* Frame title: 'Q' = Question, 'I' = Information.
1947 Can also have 'E' = Error if, one day, we want
1948 a popup for errors. */
5e617bc2 1949 if (NILP (header))
6a040d6a
NR
1950 dialog_name[0] = 'Q';
1951 else
1952 dialog_name[0] = 'I';
1953
80670155
RS
1954 /* Dialog boxes use a really stupid name encoding
1955 which specifies how many buttons to use
6a040d6a 1956 and how many buttons are on the right. */
80670155
RS
1957 dialog_name[1] = '0' + nb_buttons;
1958 dialog_name[2] = 'B';
1959 dialog_name[3] = 'R';
fcaa7665
RS
1960 /* Number of buttons to put on the right. */
1961 dialog_name[4] = '0' + nb_buttons - left_count;
80670155 1962 dialog_name[5] = 0;
165e1749
FP
1963 wv->contents = first_wv;
1964 first_wv = wv;
165e1749
FP
1965 }
1966
165e1749
FP
1967 /* No selection has been chosen yet. */
1968 menu_item_selection = 0;
1969
ba24cea2
YM
1970 /* Make sure to free the widget_value objects we used to specify the
1971 contents even with longjmp. */
27e498e6 1972 record_unwind_protect_ptr (cleanup_widget_value_tree, first_wv);
ba24cea2 1973
488dd4c4
JD
1974 /* Actually create and show the dialog. */
1975 create_and_show_dialog (f, first_wv);
f02cac82 1976
ba24cea2 1977 unbind_to (specpdl_count, Qnil);
177c0ea7 1978
488dd4c4
JD
1979 /* Find the selected item, and its pane, to return
1980 the proper value. */
165e1749
FP
1981 if (menu_item_selection != 0)
1982 {
1983 Lisp_Object prefix;
1984
1985 prefix = Qnil;
1986 i = 0;
1987 while (i < menu_items_used)
1988 {
1989 Lisp_Object entry;
1990
28be1ada 1991 if (EQ (AREF (menu_items, i), Qt))
165e1749
FP
1992 {
1993 prefix
28be1ada 1994 = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
165e1749
FP
1995 i += MENU_ITEMS_PANE_LENGTH;
1996 }
28be1ada 1997 else if (EQ (AREF (menu_items, i), Qquote))
85996cfb
GM
1998 {
1999 /* This is the boundary between left-side elts and
2000 right-side elts. */
2001 ++i;
2002 }
165e1749
FP
2003 else
2004 {
2005 entry
28be1ada 2006 = AREF (menu_items, i + MENU_ITEMS_ITEM_VALUE);
4939150c 2007 if (menu_item_selection == aref_addr (menu_items, i))
165e1749
FP
2008 {
2009 if (keymaps != 0)
2010 {
6c6f1994 2011 entry = list1 (entry);
165e1749
FP
2012 if (!NILP (prefix))
2013 entry = Fcons (prefix, entry);
2014 }
2015 return entry;
2016 }
2017 i += MENU_ITEMS_ITEM_LENGTH;
2018 }
2019 }
2020 }
9f6fcdc5
JD
2021 else
2022 /* Make "Cancel" equivalent to C-g. */
2023 Fsignal (Qquit, Qnil);
165e1749
FP
2024
2025 return Qnil;
2026}
ba461919 2027
0afa0aab
EZ
2028Lisp_Object
2029xw_popup_dialog (struct frame *f, Lisp_Object header, Lisp_Object contents)
2030{
2031 Lisp_Object title;
2032 const char *error_name;
2033 Lisp_Object selection;
2034 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
2035
2036 check_window_system (f);
2037
2038 /* Decode the dialog items from what was specified. */
2039 title = Fcar (contents);
2040 CHECK_STRING (title);
2041 record_unwind_protect_void (unuse_menu_items);
2042
2043 if (NILP (Fcar (Fcdr (contents))))
2044 /* No buttons specified, add an "Ok" button so users can pop down
2045 the dialog. Also, the lesstif/motif version crashes if there are
2046 no buttons. */
2047 contents = list2 (title, Fcons (build_string ("Ok"), Qt));
2048
2049 list_of_panes (list1 (contents));
2050
2051 /* Display them in a dialog box. */
2052 block_input ();
2053 selection = xdialog_show (f, 0, title, header, &error_name);
2054 unblock_input ();
2055
2056 unbind_to (specpdl_count, Qnil);
2057 discard_menu_items ();
2058
2059 if (error_name) error ("%s", error_name);
2060 return selection;
2061}
2062
488dd4c4 2063#else /* not USE_X_TOOLKIT && not USE_GTK */
78589e07 2064
3e703b25
GM
2065/* The frame of the last activated non-toolkit menu bar.
2066 Used to generate menu help events. */
2067
2068static struct frame *menu_help_frame;
2069
2070
62145073
GM
2071/* Show help HELP_STRING, or clear help if HELP_STRING is null.
2072
2073 PANE is the pane number, and ITEM is the menu item number in
2074 the menu (currently not used).
177c0ea7 2075
62145073
GM
2076 This cannot be done with generating a HELP_EVENT because
2077 XMenuActivate contains a loop that doesn't let Emacs process
2078 keyboard events. */
3e703b25
GM
2079
2080static void
eb18f6cc 2081menu_help_callback (char const *help_string, int pane, int item)
3e703b25 2082{
62145073
GM
2083 Lisp_Object *first_item;
2084 Lisp_Object pane_name;
2085 Lisp_Object menu_object;
177c0ea7 2086
91f2d272 2087 first_item = XVECTOR (menu_items)->contents;
62145073
GM
2088 if (EQ (first_item[0], Qt))
2089 pane_name = first_item[MENU_ITEMS_PANE_NAME];
2090 else if (EQ (first_item[0], Qquote))
2091 /* This shouldn't happen, see xmenu_show. */
ce33e8eb 2092 pane_name = empty_unibyte_string;
62145073
GM
2093 else
2094 pane_name = first_item[MENU_ITEMS_ITEM_NAME];
177c0ea7 2095
62145073 2096 /* (menu-item MENU-NAME PANE-NUMBER) */
6c6f1994 2097 menu_object = list3 (Qmenu_item, pane_name, make_number (pane));
ba461919 2098 show_help_echo (help_string ? build_string (help_string) : Qnil,
f868cd8a 2099 Qnil, menu_object, make_number (item));
3e703b25 2100}
177c0ea7 2101
27e498e6 2102static void
0521f580 2103pop_down_menu (Lisp_Object arg)
af89e871 2104{
a10c8269 2105 struct frame *f = XSAVE_POINTER (arg, 0);
3346c1d0 2106 XMenu *menu = XSAVE_POINTER (arg, 1);
af89e871 2107
4d7e6e51 2108 block_input ();
af89e871 2109#ifndef MSDOS
a130b901
JD
2110 XUngrabPointer (FRAME_X_DISPLAY (f), CurrentTime);
2111 XUngrabKeyboard (FRAME_X_DISPLAY (f), CurrentTime);
af89e871 2112#endif
a130b901 2113 XMenuDestroy (FRAME_X_DISPLAY (f), menu);
af89e871
JD
2114
2115#ifdef HAVE_X_WINDOWS
2116 /* Assume the mouse has moved out of the X window.
2117 If it has actually moved in, we will get an EnterNotify. */
aad3612f 2118 x_mouse_leave (FRAME_DISPLAY_INFO (f));
af89e871
JD
2119
2120 /* State that no mouse buttons are now held.
2121 (The oldXMenu code doesn't track this info for us.)
2122 That is not necessarily true, but the fiction leads to reasonable
2123 results, and it is a pain to ask which are actually held now. */
aad3612f 2124 FRAME_DISPLAY_INFO (f)->grabbed = 0;
af89e871
JD
2125
2126#endif /* HAVE_X_WINDOWS */
2127
4d7e6e51 2128 unblock_input ();
af89e871
JD
2129}
2130
3e703b25 2131
1fb99a3a 2132Lisp_Object
a10c8269 2133xmenu_show (struct frame *f, int x, int y, bool for_click, bool keymaps,
6bbe6da8 2134 Lisp_Object title, const char **error_name)
dcfdbac7 2135{
177c0ea7 2136 Window root;
78589e07
RS
2137 XMenu *menu;
2138 int pane, selidx, lpane, status;
2139 Lisp_Object entry, pane_prefix;
dcfdbac7
JB
2140 char *datap;
2141 int ulx, uly, width, height;
2142 int dispwidth, dispheight;
453a4f1b 2143 int i, j, lines, maxlines;
4e8d3549 2144 int maxwidth;
78589e07
RS
2145 int dummy_int;
2146 unsigned int dummy_uint;
d311d28c 2147 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
088831f6 2148
65b02bb9 2149 eassert (FRAME_X_P (f) || FRAME_MSDOS_P (f));
62af879c 2150
bf501fb9 2151 *error_name = 0;
78589e07
RS
2152 if (menu_items_n_panes == 0)
2153 return Qnil;
088831f6 2154
742f715d
KH
2155 if (menu_items_used <= MENU_ITEMS_PANE_LENGTH)
2156 {
bf501fb9 2157 *error_name = "Empty menu";
742f715d
KH
2158 return Qnil;
2159 }
2160
f0177f86
EZ
2161 block_input ();
2162
78589e07 2163 /* Figure out which root window F is on. */
92280f67 2164 XGetGeometry (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), &root,
78589e07
RS
2165 &dummy_int, &dummy_int, &dummy_uint, &dummy_uint,
2166 &dummy_uint, &dummy_uint);
18686d47 2167
78589e07 2168 /* Make the menu on that window. */
92280f67 2169 menu = XMenuCreate (FRAME_X_DISPLAY (f), root, "emacs");
78589e07 2170 if (menu == NULL)
dcfdbac7 2171 {
bf501fb9 2172 *error_name = "Can't create menu";
f0177f86 2173 unblock_input ();
78589e07 2174 return Qnil;
dcfdbac7 2175 }
78589e07 2176
d130d129
RS
2177 /* Don't GC while we prepare and show the menu,
2178 because we give the oldxmenu library pointers to the
2179 contents of strings. */
2180 inhibit_garbage_collection ();
2181
87485d6f 2182#ifdef HAVE_X_WINDOWS
78589e07 2183 /* Adjust coordinates to relative to the outer (window manager) window. */
453a4f1b
JD
2184 x += FRAME_OUTER_TO_INNER_DIFF_X (f);
2185 y += FRAME_OUTER_TO_INNER_DIFF_Y (f);
87485d6f 2186#endif /* HAVE_X_WINDOWS */
78589e07
RS
2187
2188 /* Adjust coordinates to be root-window-relative. */
9882535b
KS
2189 x += f->left_pos;
2190 y += f->top_pos;
177c0ea7 2191
78589e07 2192 /* Create all the necessary panes and their items. */
eef9bc79
PE
2193 maxwidth = maxlines = lines = i = 0;
2194 lpane = XM_FAILURE;
78589e07 2195 while (i < menu_items_used)
dcfdbac7 2196 {
28be1ada 2197 if (EQ (AREF (menu_items, i), Qt))
dcfdbac7 2198 {
78589e07
RS
2199 /* Create a new pane. */
2200 Lisp_Object pane_name, prefix;
6d1f7fee 2201 const char *pane_string;
78589e07 2202
453a4f1b
JD
2203 maxlines = max (maxlines, lines);
2204 lines = 0;
28be1ada
DA
2205 pane_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME);
2206 prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
78589e07 2207 pane_string = (NILP (pane_name)
51b59d79 2208 ? "" : SSDATA (pane_name));
78589e07
RS
2209 if (keymaps && !NILP (prefix))
2210 pane_string++;
2211
92280f67 2212 lpane = XMenuAddPane (FRAME_X_DISPLAY (f), menu, pane_string, TRUE);
78589e07
RS
2213 if (lpane == XM_FAILURE)
2214 {
92280f67 2215 XMenuDestroy (FRAME_X_DISPLAY (f), menu);
bf501fb9 2216 *error_name = "Can't create pane";
f0177f86 2217 unblock_input ();
78589e07
RS
2218 return Qnil;
2219 }
2220 i += MENU_ITEMS_PANE_LENGTH;
4e8d3549
RS
2221
2222 /* Find the width of the widest item in this pane. */
4e8d3549
RS
2223 j = i;
2224 while (j < menu_items_used)
2225 {
2226 Lisp_Object item;
28be1ada 2227 item = AREF (menu_items, j);
4e8d3549
RS
2228 if (EQ (item, Qt))
2229 break;
2230 if (NILP (item))
2231 {
2232 j++;
2233 continue;
2234 }
d5db4077 2235 width = SBYTES (item);
4e8d3549
RS
2236 if (width > maxwidth)
2237 maxwidth = width;
2238
2239 j += MENU_ITEMS_ITEM_LENGTH;
2240 }
dcfdbac7 2241 }
fcaa7665
RS
2242 /* Ignore a nil in the item list.
2243 It's meaningful only for dialog boxes. */
28be1ada 2244 else if (EQ (AREF (menu_items, i), Qquote))
fcaa7665 2245 i += 1;
78589e07 2246 else
dcfdbac7 2247 {
78589e07 2248 /* Create a new item within current pane. */
3e703b25 2249 Lisp_Object item_name, enable, descrip, help;
eb18f6cc
PE
2250 char *item_data;
2251 char const *help_string;
78589e07 2252
28be1ada
DA
2253 item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
2254 enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
78589e07 2255 descrip
28be1ada
DA
2256 = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY);
2257 help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP);
eb18f6cc 2258 help_string = STRINGP (help) ? SSDATA (help) : NULL;
177c0ea7 2259
78589e07 2260 if (!NILP (descrip))
4e8d3549 2261 {
4e8d3549
RS
2262 /* if alloca is fast, use that to make the space,
2263 to reduce gc needs. */
38182d90 2264 item_data = alloca (maxwidth + SBYTES (descrip) + 1);
eb18f6cc 2265 memcpy (item_data, SSDATA (item_name), SBYTES (item_name));
d5db4077 2266 for (j = SCHARS (item_name); j < maxwidth; j++)
4e8d3549 2267 item_data[j] = ' ';
eb18f6cc 2268 memcpy (item_data + j, SSDATA (descrip), SBYTES (descrip));
d5db4077 2269 item_data[j + SBYTES (descrip)] = 0;
4e8d3549
RS
2270 }
2271 else
eb18f6cc 2272 item_data = SSDATA (item_name);
78589e07 2273
eef9bc79
PE
2274 if (lpane == XM_FAILURE
2275 || (XMenuAddSelection (FRAME_X_DISPLAY (f),
2276 menu, lpane, 0, item_data,
2277 !NILP (enable), help_string)
2278 == XM_FAILURE))
dcfdbac7 2279 {
92280f67 2280 XMenuDestroy (FRAME_X_DISPLAY (f), menu);
bf501fb9 2281 *error_name = "Can't add selection to menu";
f0177f86 2282 unblock_input ();
78589e07 2283 return Qnil;
dcfdbac7 2284 }
78589e07 2285 i += MENU_ITEMS_ITEM_LENGTH;
453a4f1b 2286 lines++;
dcfdbac7
JB
2287 }
2288 }
4e8d3549 2289
453a4f1b
JD
2290 maxlines = max (maxlines, lines);
2291
78589e07 2292 /* All set and ready to fly. */
92280f67 2293 XMenuRecompute (FRAME_X_DISPLAY (f), menu);
63685b9d
GM
2294 dispwidth = DisplayWidth (FRAME_X_DISPLAY (f), FRAME_X_SCREEN_NUMBER (f));
2295 dispheight = DisplayHeight (FRAME_X_DISPLAY (f), FRAME_X_SCREEN_NUMBER (f));
78589e07
RS
2296 x = min (x, dispwidth);
2297 y = min (y, dispheight);
2298 x = max (x, 1);
2299 y = max (y, 1);
92280f67 2300 XMenuLocate (FRAME_X_DISPLAY (f), menu, 0, 0, x, y,
dcfdbac7
JB
2301 &ulx, &uly, &width, &height);
2302 if (ulx+width > dispwidth)
2303 {
78589e07 2304 x -= (ulx + width) - dispwidth;
dcfdbac7
JB
2305 ulx = dispwidth - width;
2306 }
2307 if (uly+height > dispheight)
2308 {
78589e07 2309 y -= (uly + height) - dispheight;
dcfdbac7
JB
2310 uly = dispheight - height;
2311 }
2de7397f
EZ
2312#ifndef HAVE_X_WINDOWS
2313 if (FRAME_HAS_MINIBUF_P (f) && uly+height > dispheight - 1)
2314 {
2315 /* Move the menu away of the echo area, to avoid overwriting the
2316 menu with help echo messages or vice versa. */
2317 if (BUFFERP (echo_area_buffer[0]) && WINDOWP (echo_area_window))
2318 {
2319 y -= WINDOW_TOTAL_LINES (XWINDOW (echo_area_window));
2320 uly -= WINDOW_TOTAL_LINES (XWINDOW (echo_area_window));
2321 }
2322 else
2323 {
2324 y--;
2325 uly--;
2326 }
2327 }
2328#endif
78589e07
RS
2329 if (ulx < 0) x -= ulx;
2330 if (uly < 0) y -= uly;
121e4555 2331
453a4f1b
JD
2332 if (! for_click)
2333 {
2334 /* If position was not given by a mouse click, adjust so upper left
2335 corner of the menu as a whole ends up at given coordinates. This
2336 is what x-popup-menu says in its documentation. */
2337 x += width/2;
2338 y += 1.5*height/(maxlines+2);
2339 }
2340
121e4555 2341 XMenuSetAEQ (menu, TRUE);
78589e07
RS
2342 XMenuSetFreeze (menu, TRUE);
2343 pane = selidx = 0;
3e703b25 2344
c3438661
JD
2345#ifndef MSDOS
2346 XMenuActivateSetWaitFunction (x_menu_wait_for_event, FRAME_X_DISPLAY (f));
2347#endif
59cfb104 2348
1396ac86 2349 record_unwind_protect (pop_down_menu, make_save_ptr_ptr (f, menu));
c3438661 2350
3e703b25
GM
2351 /* Help display under X won't work because XMenuActivate contains
2352 a loop that doesn't give Emacs a chance to process it. */
2353 menu_help_frame = f;
92280f67 2354 status = XMenuActivate (FRAME_X_DISPLAY (f), menu, &pane, &selidx,
af89e871
JD
2355 x, y, ButtonReleaseMask, &datap,
2356 menu_help_callback);
eef9bc79 2357 entry = pane_prefix = Qnil;
a352a815 2358
dcfdbac7
JB
2359 switch (status)
2360 {
2361 case XM_SUCCESS:
2362#ifdef XDEBUG
2363 fprintf (stderr, "pane= %d line = %d\n", panes, selidx);
2364#endif
fa6d54d9 2365
78589e07
RS
2366 /* Find the item number SELIDX in pane number PANE. */
2367 i = 0;
2368 while (i < menu_items_used)
fa6d54d9 2369 {
28be1ada 2370 if (EQ (AREF (menu_items, i), Qt))
088831f6 2371 {
78589e07
RS
2372 if (pane == 0)
2373 pane_prefix
28be1ada 2374 = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
78589e07
RS
2375 pane--;
2376 i += MENU_ITEMS_PANE_LENGTH;
088831f6 2377 }
78589e07 2378 else
ab6ee1a0 2379 {
78589e07 2380 if (pane == -1)
ab6ee1a0 2381 {
78589e07 2382 if (selidx == 0)
ab6ee1a0 2383 {
78589e07 2384 entry
28be1ada 2385 = AREF (menu_items, i + MENU_ITEMS_ITEM_VALUE);
7cded46f 2386 if (keymaps)
ab6ee1a0 2387 {
6c6f1994 2388 entry = list1 (entry);
78589e07
RS
2389 if (!NILP (pane_prefix))
2390 entry = Fcons (pane_prefix, entry);
ab6ee1a0 2391 }
78589e07 2392 break;
ab6ee1a0 2393 }
78589e07 2394 selidx--;
ab6ee1a0 2395 }
78589e07 2396 i += MENU_ITEMS_ITEM_LENGTH;
ab6ee1a0
RS
2397 }
2398 }
78589e07 2399 break;
dcfdbac7 2400
78589e07 2401 case XM_FAILURE:
bf501fb9 2402 *error_name = "Can't activate menu";
78589e07 2403 case XM_IA_SELECT:
9f6fcdc5 2404 break;
78589e07 2405 case XM_NO_SELECT:
be6ed24a
RS
2406 /* Make "Cancel" equivalent to C-g unless FOR_CLICK (which means
2407 the menu was invoked with a mouse event as POSITION). */
9f6fcdc5 2408 if (! for_click)
f0177f86
EZ
2409 {
2410 unblock_input ();
2411 Fsignal (Qquit, Qnil);
2412 }
78589e07 2413 break;
dcfdbac7 2414 }
a5285df3 2415
f0177f86 2416 unblock_input ();
af89e871 2417 unbind_to (specpdl_count, Qnil);
a5285df3 2418
78589e07 2419 return entry;
dcfdbac7 2420}
4dedbfe0 2421
78589e07 2422#endif /* not USE_X_TOOLKIT */
1e659e4c 2423
d009ae66
EZ
2424#ifndef MSDOS
2425/* Detect if a dialog or menu has been posted. MSDOS has its own
2426 implementation on msdos.c. */
b79b8a1c 2427
077288cf 2428int ATTRIBUTE_CONST
971de7fb 2429popup_activated (void)
b79b8a1c
CY
2430{
2431 return popup_activated_flag;
2432}
d009ae66 2433#endif /* not MSDOS */
e3135734
CY
2434
2435/* The following is used by delayed window autoselection. */
2436
2437DEFUN ("menu-or-popup-active-p", Fmenu_or_popup_active_p, Smenu_or_popup_active_p, 0, 0, 0,
2438 doc: /* Return t if a menu or popup dialog is active. */)
5842a27b 2439 (void)
e3135734 2440{
e3135734 2441 return (popup_activated ()) ? Qt : Qnil;
e3135734 2442}
088831f6 2443\f
dfcf069d 2444void
971de7fb 2445syms_of_xmenu (void)
dcfdbac7 2446{
cd3520a4 2447 DEFSYM (Qdebug_on_next_call, "debug-on-next-call");
0314aacb 2448
8ed87156 2449#ifdef USE_X_TOOLKIT
177c0ea7 2450 widget_id_tick = (1<<16);
88766961 2451 next_menubar_widget_id = 1;
8ed87156
RS
2452#endif
2453
508fb067 2454 defsubr (&Smenu_or_popup_active_p);
22badffe
JD
2455
2456#if defined (USE_GTK) || defined (USE_X_TOOLKIT)
12b6af5c 2457 defsubr (&Sx_menu_bar_open_internal);
d67b4f80
DN
2458 Ffset (intern_c_string ("accelerate-menu"),
2459 intern_c_string (Sx_menu_bar_open_internal.symbol_name));
22badffe 2460#endif
dcfdbac7 2461}