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