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