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