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