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