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