Restore mistakenly reverted code from 2010-04-17T12:33:05Z!eliz@gnu.org merged in...
[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
ebd15611 1565create_and_show_popup_menu (FRAME_PTR f, widget_value *first_wv, int x, int y, int for_click, unsigned int timestamp)
488dd4c4
JD
1566{
1567 int i;
1568 Arg av[2];
1569 int ac = 0;
1570 XButtonPressedEvent dummy;
1571 LWLIB_ID menu_id;
1572 Widget menu;
488dd4c4 1573
62af879c
KL
1574 if (! FRAME_X_P (f))
1575 abort ();
1576
488dd4c4
JD
1577 menu_id = widget_id_tick++;
1578 menu = lw_create_widget ("popup", first_wv->name, menu_id, first_wv,
1579 f->output_data.x->widget, 1, 0,
1580 popup_selection_callback,
1581 popup_deactivate_callback,
1582 menu_highlight_callback);
1583
52214050 1584#ifdef USE_LUCID
99852628 1585 apply_systemfont_to_menu (menu);
52214050 1586#endif
99852628 1587
488dd4c4
JD
1588 dummy.type = ButtonPress;
1589 dummy.serial = 0;
1590 dummy.send_event = 0;
1591 dummy.display = FRAME_X_DISPLAY (f);
1592 dummy.time = CurrentTime;
1593 dummy.root = FRAME_X_DISPLAY_INFO (f)->root_window;
1594 dummy.window = dummy.root;
1595 dummy.subwindow = dummy.root;
1596 dummy.x = x;
1597 dummy.y = y;
1598
1599 /* Adjust coordinates to be root-window-relative. */
9882535b
KS
1600 x += f->left_pos + FRAME_OUTER_TO_INNER_DIFF_X (f);
1601 y += f->top_pos + FRAME_OUTER_TO_INNER_DIFF_Y (f);
488dd4c4
JD
1602
1603 dummy.x_root = x;
1604 dummy.y_root = y;
1605
1606 dummy.state = 0;
1607 dummy.button = 0;
1608 for (i = 0; i < 5; i++)
1609 if (FRAME_X_DISPLAY_INFO (f)->grabbed & (1 << i))
1610 dummy.button = i;
1611
1612 /* Don't allow any geometry request from the user. */
1613 XtSetArg (av[ac], XtNgeometry, 0); ac++;
1614 XtSetValues (menu, av, ac);
1615
1616 /* Display the menu. */
1617 lw_popup_menu (menu, (XEvent *) &dummy);
1618 popup_activated_flag = 1;
98a20c65 1619 x_activate_timeout_atimer ();
59cfb104 1620
af89e871
JD
1621 {
1622 int fact = 4 * sizeof (LWLIB_ID);
1623 int specpdl_count = SPECPDL_INDEX ();
1624 record_unwind_protect (pop_down_menu,
1625 Fcons (make_number (menu_id >> (fact)),
1626 make_number (menu_id & ~(-1 << (fact)))));
488dd4c4 1627
af89e871 1628 /* Process events that apply to the menu. */
95bdef2e 1629 popup_get_selection ((XEvent *) 0, FRAME_X_DISPLAY_INFO (f), menu_id, 1);
488dd4c4 1630
af89e871
JD
1631 unbind_to (specpdl_count, Qnil);
1632 }
488dd4c4
JD
1633}
1634
1635#endif /* not USE_GTK */
1636
ef7417fd
SM
1637Lisp_Object
1638xmenu_show (FRAME_PTR f, int x, int y, int for_click, int keymaps,
1639 Lisp_Object title, char **error, EMACS_UINT timestamp)
18686d47 1640{
78589e07 1641 int i;
78589e07 1642 widget_value *wv, *save_wv = 0, *first_wv = 0, *prev_wv = 0;
101bb4a5
RS
1643 widget_value **submenu_stack
1644 = (widget_value **) alloca (menu_items_used * sizeof (widget_value *));
1645 Lisp_Object *subprefix_stack
1646 = (Lisp_Object *) alloca (menu_items_used * sizeof (Lisp_Object));
1647 int submenu_depth = 0;
4e8d3549 1648
78c8278d
RS
1649 int first_pane;
1650
62af879c
KL
1651 if (! FRAME_X_P (f))
1652 abort ();
1653
78589e07
RS
1654 *error = NULL;
1655
742f715d
KH
1656 if (menu_items_used <= MENU_ITEMS_PANE_LENGTH)
1657 {
1658 *error = "Empty menu";
1659 return Qnil;
1660 }
63c414df 1661
78589e07
RS
1662 /* Create a tree of widget_value objects
1663 representing the panes and their items. */
f7fab165 1664 wv = xmalloc_widget_value ();
78589e07
RS
1665 wv->name = "menu";
1666 wv->value = 0;
1667 wv->enabled = 1;
3427a3db 1668 wv->button_type = BUTTON_TYPE_NONE;
27ad7b52 1669 wv->help =Qnil;
78589e07 1670 first_wv = wv;
78c8278d 1671 first_pane = 1;
177c0ea7 1672
78589e07
RS
1673 /* Loop over all panes and items, filling in the tree. */
1674 i = 0;
1675 while (i < menu_items_used)
1676 {
101bb4a5
RS
1677 if (EQ (XVECTOR (menu_items)->contents[i], Qnil))
1678 {
1679 submenu_stack[submenu_depth++] = save_wv;
1680 save_wv = prev_wv;
1681 prev_wv = 0;
78c8278d 1682 first_pane = 1;
101bb4a5
RS
1683 i++;
1684 }
1685 else if (EQ (XVECTOR (menu_items)->contents[i], Qlambda))
1686 {
1687 prev_wv = save_wv;
1688 save_wv = submenu_stack[--submenu_depth];
78c8278d 1689 first_pane = 0;
101bb4a5
RS
1690 i++;
1691 }
1692 else if (EQ (XVECTOR (menu_items)->contents[i], Qt)
1693 && submenu_depth != 0)
1694 i += MENU_ITEMS_PANE_LENGTH;
fcaa7665
RS
1695 /* Ignore a nil in the item list.
1696 It's meaningful only for dialog boxes. */
1697 else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
1698 i += 1;
101bb4a5 1699 else if (EQ (XVECTOR (menu_items)->contents[i], Qt))
78589e07
RS
1700 {
1701 /* Create a new pane. */
1702 Lisp_Object pane_name, prefix;
1703 char *pane_string;
177c0ea7 1704
4c329aa8
GM
1705 pane_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME);
1706 prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
177c0ea7 1707
703dc2a8 1708#ifndef HAVE_MULTILINGUAL_MENU
4c329aa8
GM
1709 if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name))
1710 {
8af9fa55 1711 pane_name = ENCODE_MENU_STRING (pane_name);
3ae565b3 1712 ASET (menu_items, i + MENU_ITEMS_PANE_NAME, pane_name);
4c329aa8 1713 }
703dc2a8 1714#endif
78589e07 1715 pane_string = (NILP (pane_name)
d5db4077 1716 ? "" : (char *) SDATA (pane_name));
101bb4a5 1717 /* If there is just one top-level pane, put all its items directly
78589e07
RS
1718 under the top-level menu. */
1719 if (menu_items_n_panes == 1)
1720 pane_string = "";
1721
1722 /* If the pane has a meaningful name,
1723 make the pane a top-level menu item
1724 with its items as a submenu beneath it. */
78c8278d 1725 if (!keymaps && strcmp (pane_string, ""))
78589e07 1726 {
f7fab165 1727 wv = xmalloc_widget_value ();
78589e07
RS
1728 if (save_wv)
1729 save_wv->next = wv;
1730 else
1731 first_wv->contents = wv;
1732 wv->name = pane_string;
1733 if (keymaps && !NILP (prefix))
1734 wv->name++;
1735 wv->value = 0;
1736 wv->enabled = 1;
3427a3db 1737 wv->button_type = BUTTON_TYPE_NONE;
27ad7b52 1738 wv->help = Qnil;
78c8278d
RS
1739 save_wv = wv;
1740 prev_wv = 0;
78589e07 1741 }
78c8278d
RS
1742 else if (first_pane)
1743 {
1744 save_wv = wv;
1745 prev_wv = 0;
1746 }
1747 first_pane = 0;
78589e07
RS
1748 i += MENU_ITEMS_PANE_LENGTH;
1749 }
1750 else
1751 {
1752 /* Create a new item within current pane. */
9cd50434 1753 Lisp_Object item_name, enable, descrip, def, type, selected, help;
4c329aa8
GM
1754 item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
1755 enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
1756 descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY);
1757 def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION);
1758 type = AREF (menu_items, i + MENU_ITEMS_ITEM_TYPE);
1759 selected = AREF (menu_items, i + MENU_ITEMS_ITEM_SELECTED);
1760 help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP);
3427a3db 1761
703dc2a8 1762#ifndef HAVE_MULTILINGUAL_MENU
3427a3db 1763 if (STRINGP (item_name) && STRING_MULTIBYTE (item_name))
4c329aa8 1764 {
646f98ec 1765 item_name = ENCODE_MENU_STRING (item_name);
3ae565b3 1766 ASET (menu_items, i + MENU_ITEMS_ITEM_NAME, item_name);
4c329aa8 1767 }
177c0ea7 1768
3427a3db 1769 if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
4c329aa8 1770 {
646f98ec 1771 descrip = ENCODE_MENU_STRING (descrip);
3ae565b3 1772 ASET (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY, descrip);
4c329aa8
GM
1773 }
1774#endif /* not HAVE_MULTILINGUAL_MENU */
177c0ea7 1775
f7fab165 1776 wv = xmalloc_widget_value ();
177c0ea7 1777 if (prev_wv)
78589e07 1778 prev_wv->next = wv;
177c0ea7 1779 else
78589e07 1780 save_wv->contents = wv;
d5db4077 1781 wv->name = (char *) SDATA (item_name);
78589e07 1782 if (!NILP (descrip))
d5db4077 1783 wv->key = (char *) SDATA (descrip);
78589e07 1784 wv->value = 0;
a352a815
RS
1785 /* If this item has a null value,
1786 make the call_data null so that it won't display a box
1787 when the mouse is on it. */
1788 wv->call_data
1789 = (!NILP (def) ? (void *) &XVECTOR (menu_items)->contents[i] : 0);
78589e07 1790 wv->enabled = !NILP (enable);
3427a3db
GM
1791
1792 if (NILP (type))
1793 wv->button_type = BUTTON_TYPE_NONE;
1794 else if (EQ (type, QCtoggle))
1795 wv->button_type = BUTTON_TYPE_TOGGLE;
1796 else if (EQ (type, QCradio))
1797 wv->button_type = BUTTON_TYPE_RADIO;
1798 else
1799 abort ();
1800
1801 wv->selected = !NILP (selected);
cc45fb40 1802
0b1f4572
RS
1803 if (! STRINGP (help))
1804 help = Qnil;
1805
1806 wv->help = help;
cc45fb40 1807
78589e07
RS
1808 prev_wv = wv;
1809
1810 i += MENU_ITEMS_ITEM_LENGTH;
1811 }
1812 }
1813
c98fcf4b 1814 /* Deal with the title, if it is non-nil. */
4dedbfe0
PR
1815 if (!NILP (title))
1816 {
f7fab165
RS
1817 widget_value *wv_title = xmalloc_widget_value ();
1818 widget_value *wv_sep1 = xmalloc_widget_value ();
1819 widget_value *wv_sep2 = xmalloc_widget_value ();
4dedbfe0
PR
1820
1821 wv_sep2->name = "--";
1822 wv_sep2->next = first_wv->contents;
27ad7b52 1823 wv_sep2->help = Qnil;
4dedbfe0
PR
1824
1825 wv_sep1->name = "--";
1826 wv_sep1->next = wv_sep2;
27ad7b52 1827 wv_sep1->help = Qnil;
4dedbfe0 1828
703dc2a8
KH
1829#ifndef HAVE_MULTILINGUAL_MENU
1830 if (STRING_MULTIBYTE (title))
646f98ec 1831 title = ENCODE_MENU_STRING (title);
703dc2a8 1832#endif
177c0ea7 1833
d5db4077 1834 wv_title->name = (char *) SDATA (title);
cc45fb40 1835 wv_title->enabled = TRUE;
3427a3db 1836 wv_title->button_type = BUTTON_TYPE_NONE;
27ad7b52 1837 wv_title->help = Qnil;
a9d8395f 1838 wv_title->next = wv_sep1;
4dedbfe0
PR
1839 first_wv->contents = wv_title;
1840 }
1841
78589e07
RS
1842 /* No selection has been chosen yet. */
1843 menu_item_selection = 0;
1844
488dd4c4 1845 /* Actually create and show the menu until popped down. */
a20903d0 1846 create_and_show_popup_menu (f, first_wv, x, y, for_click, timestamp);
18686d47 1847
488dd4c4
JD
1848 /* Free the widget_value objects we used to specify the contents. */
1849 free_menubar_widget_value_tree (first_wv);
18686d47 1850
78589e07
RS
1851 /* Find the selected item, and its pane, to return
1852 the proper value. */
1853 if (menu_item_selection != 0)
1854 {
c63f6952 1855 Lisp_Object prefix, entry;
78589e07 1856
6bbd7a29 1857 prefix = entry = Qnil;
78589e07
RS
1858 i = 0;
1859 while (i < menu_items_used)
1860 {
101bb4a5
RS
1861 if (EQ (XVECTOR (menu_items)->contents[i], Qnil))
1862 {
1863 subprefix_stack[submenu_depth++] = prefix;
1864 prefix = entry;
1865 i++;
1866 }
1867 else if (EQ (XVECTOR (menu_items)->contents[i], Qlambda))
1868 {
1869 prefix = subprefix_stack[--submenu_depth];
1870 i++;
1871 }
1872 else if (EQ (XVECTOR (menu_items)->contents[i], Qt))
78589e07
RS
1873 {
1874 prefix
1875 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
1876 i += MENU_ITEMS_PANE_LENGTH;
1877 }
d31d42cc
RS
1878 /* Ignore a nil in the item list.
1879 It's meaningful only for dialog boxes. */
1880 else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
1881 i += 1;
78589e07
RS
1882 else
1883 {
1884 entry
1885 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_VALUE];
1886 if (menu_item_selection == &XVECTOR (menu_items)->contents[i])
1887 {
1888 if (keymaps != 0)
1889 {
101bb4a5
RS
1890 int j;
1891
78589e07
RS
1892 entry = Fcons (entry, Qnil);
1893 if (!NILP (prefix))
1894 entry = Fcons (prefix, entry);
101bb4a5 1895 for (j = submenu_depth - 1; j >= 0; j--)
e48087b7 1896 if (!NILP (subprefix_stack[j]))
5964e450 1897 entry = Fcons (subprefix_stack[j], entry);
78589e07
RS
1898 }
1899 return entry;
1900 }
1901 i += MENU_ITEMS_ITEM_LENGTH;
1902 }
1903 }
1904 }
be6ed24a
RS
1905 else if (!for_click)
1906 /* Make "Cancel" equivalent to C-g. */
1907 Fsignal (Qquit, Qnil);
78589e07
RS
1908
1909 return Qnil;
18686d47 1910}
4dedbfe0 1911\f
488dd4c4
JD
1912#ifdef USE_GTK
1913static void
971de7fb 1914dialog_selection_callback (GtkWidget *widget, gpointer client_data)
488dd4c4
JD
1915{
1916 /* The EMACS_INT cast avoids a warning. There's no problem
1917 as long as pointers have enough bits to hold small integers. */
1918 if ((int) (EMACS_INT) client_data != -1)
1919 menu_item_selection = (Lisp_Object *) client_data;
1920
1921 popup_activated_flag = 0;
1922}
1923
1924/* Pop up the dialog for frame F defined by FIRST_WV and loop until the
1925 dialog pops down.
1926 menu_item_selection will be set to the selection. */
1927static void
971de7fb 1928create_and_show_dialog (FRAME_PTR f, widget_value *first_wv)
488dd4c4
JD
1929{
1930 GtkWidget *menu;
1931
62af879c
KL
1932 if (! FRAME_X_P (f))
1933 abort ();
1934
488dd4c4
JD
1935 menu = xg_create_widget ("dialog", first_wv->name, f, first_wv,
1936 G_CALLBACK (dialog_selection_callback),
1937 G_CALLBACK (popup_deactivate_callback),
1938 0);
1939
1940 if (menu)
1941 {
af89e871 1942 int specpdl_count = SPECPDL_INDEX ();
a130b901 1943 record_unwind_protect (pop_down_menu, make_save_value (menu, 0));
af89e871 1944
488dd4c4
JD
1945 /* Display the menu. */
1946 gtk_widget_show_all (menu);
1947
1948 /* Process events that apply to the menu. */
f1d1cd24 1949 popup_widget_loop (1, menu);
177c0ea7 1950
af89e871 1951 unbind_to (specpdl_count, Qnil);
488dd4c4
JD
1952 }
1953}
1954
1955#else /* not USE_GTK */
4dedbfe0 1956static void
ebd15611 1957dialog_selection_callback (Widget widget, LWLIB_ID id, XtPointer client_data)
4dedbfe0 1958{
01d5e892
RS
1959 /* The EMACS_INT cast avoids a warning. There's no problem
1960 as long as pointers have enough bits to hold small integers. */
1961 if ((int) (EMACS_INT) client_data != -1)
4dedbfe0 1962 menu_item_selection = (Lisp_Object *) client_data;
488dd4c4 1963
4dedbfe0
PR
1964 BLOCK_INPUT;
1965 lw_destroy_all_widgets (id);
1966 UNBLOCK_INPUT;
9572375b 1967 popup_activated_flag = 0;
4dedbfe0 1968}
18686d47 1969
488dd4c4 1970
488dd4c4
JD
1971/* Pop up the dialog for frame F defined by FIRST_WV and loop until the
1972 dialog pops down.
1973 menu_item_selection will be set to the selection. */
1974static void
ebd15611 1975create_and_show_dialog (FRAME_PTR f, widget_value *first_wv)
488dd4c4
JD
1976{
1977 LWLIB_ID dialog_id;
1978
62af879c
KL
1979 if (!FRAME_X_P (f))
1980 abort();
1981
488dd4c4 1982 dialog_id = widget_id_tick++;
52214050 1983#ifdef USE_LUCID
1ecb2d3f
JD
1984 apply_systemfont_to_dialog (f->output_data.x->widget);
1985#endif
488dd4c4
JD
1986 lw_create_widget (first_wv->name, "dialog", dialog_id, first_wv,
1987 f->output_data.x->widget, 1, 0,
1988 dialog_selection_callback, 0, 0);
1989 lw_modify_all_widgets (dialog_id, first_wv->contents, True);
488dd4c4
JD
1990 /* Display the dialog box. */
1991 lw_pop_up_all_widgets (dialog_id);
1992 popup_activated_flag = 1;
98a20c65 1993 x_activate_timeout_atimer ();
488dd4c4
JD
1994
1995 /* Process events that apply to the dialog box.
1996 Also handle timers. */
1997 {
1998 int count = SPECPDL_INDEX ();
1999 int fact = 4 * sizeof (LWLIB_ID);
177c0ea7 2000
488dd4c4 2001 /* xdialog_show_unwind is responsible for popping the dialog box down. */
af89e871 2002 record_unwind_protect (pop_down_menu,
488dd4c4
JD
2003 Fcons (make_number (dialog_id >> (fact)),
2004 make_number (dialog_id & ~(-1 << (fact)))));
2005
df470e3b 2006 popup_get_selection ((XEvent *) 0, FRAME_X_DISPLAY_INFO (f),
95bdef2e 2007 dialog_id, 1);
488dd4c4
JD
2008
2009 unbind_to (count, Qnil);
2010 }
2011}
2012
2013#endif /* not USE_GTK */
2014
165e1749
FP
2015static char * button_names [] = {
2016 "button1", "button2", "button3", "button4", "button5",
2017 "button6", "button7", "button8", "button9", "button10" };
2018
2019static Lisp_Object
971de7fb 2020xdialog_show (FRAME_PTR f, int keymaps, Lisp_Object title, Lisp_Object header, char **error_name)
165e1749
FP
2021{
2022 int i, nb_buttons=0;
80670155 2023 char dialog_name[6];
165e1749 2024
faa935b6 2025 widget_value *wv, *first_wv = 0, *prev_wv = 0;
165e1749 2026
fcaa7665
RS
2027 /* Number of elements seen so far, before boundary. */
2028 int left_count = 0;
2029 /* 1 means we've seen the boundary between left-hand elts and right-hand. */
2030 int boundary_seen = 0;
2031
62af879c
KL
2032 if (! FRAME_X_P (f))
2033 abort ();
2034
6a040d6a 2035 *error_name = NULL;
165e1749 2036
80670155
RS
2037 if (menu_items_n_panes > 1)
2038 {
6a040d6a 2039 *error_name = "Multiple panes in dialog box";
80670155
RS
2040 return Qnil;
2041 }
2042
165e1749
FP
2043 /* Create a tree of widget_value objects
2044 representing the text label and buttons. */
2045 {
2046 Lisp_Object pane_name, prefix;
2047 char *pane_string;
2048 pane_name = XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME];
2049 prefix = XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_PREFIX];
2050 pane_string = (NILP (pane_name)
177c0ea7 2051 ? "" : (char *) SDATA (pane_name));
f7fab165 2052 prev_wv = xmalloc_widget_value ();
165e1749
FP
2053 prev_wv->value = pane_string;
2054 if (keymaps && !NILP (prefix))
2055 prev_wv->name++;
2056 prev_wv->enabled = 1;
2057 prev_wv->name = "message";
27ad7b52 2058 prev_wv->help = Qnil;
165e1749 2059 first_wv = prev_wv;
177c0ea7 2060
165e1749
FP
2061 /* Loop over all panes and items, filling in the tree. */
2062 i = MENU_ITEMS_PANE_LENGTH;
2063 while (i < menu_items_used)
2064 {
177c0ea7 2065
165e1749
FP
2066 /* Create a new item within current pane. */
2067 Lisp_Object item_name, enable, descrip;
2068 item_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_NAME];
2069 enable = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_ENABLE];
2070 descrip
2071 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_EQUIV_KEY];
177c0ea7 2072
80670155
RS
2073 if (NILP (item_name))
2074 {
2075 free_menubar_widget_value_tree (first_wv);
6a040d6a 2076 *error_name = "Submenu in dialog items";
80670155
RS
2077 return Qnil;
2078 }
fcaa7665
RS
2079 if (EQ (item_name, Qquote))
2080 {
2081 /* This is the boundary between left-side elts
2082 and right-side elts. Stop incrementing right_count. */
2083 boundary_seen = 1;
2084 i++;
2085 continue;
2086 }
86e71abf 2087 if (nb_buttons >= 9)
80670155
RS
2088 {
2089 free_menubar_widget_value_tree (first_wv);
6a040d6a 2090 *error_name = "Too many dialog items";
80670155
RS
2091 return Qnil;
2092 }
2093
f7fab165 2094 wv = xmalloc_widget_value ();
165e1749 2095 prev_wv->next = wv;
80670155 2096 wv->name = (char *) button_names[nb_buttons];
165e1749 2097 if (!NILP (descrip))
d5db4077
KR
2098 wv->key = (char *) SDATA (descrip);
2099 wv->value = (char *) SDATA (item_name);
165e1749
FP
2100 wv->call_data = (void *) &XVECTOR (menu_items)->contents[i];
2101 wv->enabled = !NILP (enable);
27ad7b52 2102 wv->help = Qnil;
165e1749
FP
2103 prev_wv = wv;
2104
fcaa7665
RS
2105 if (! boundary_seen)
2106 left_count++;
2107
165e1749
FP
2108 nb_buttons++;
2109 i += MENU_ITEMS_ITEM_LENGTH;
2110 }
2111
fcaa7665
RS
2112 /* If the boundary was not specified,
2113 by default put half on the left and half on the right. */
2114 if (! boundary_seen)
2115 left_count = nb_buttons - nb_buttons / 2;
2116
f7fab165 2117 wv = xmalloc_widget_value ();
80670155 2118 wv->name = dialog_name;
27ad7b52 2119 wv->help = Qnil;
6a040d6a
NR
2120
2121 /* Frame title: 'Q' = Question, 'I' = Information.
2122 Can also have 'E' = Error if, one day, we want
2123 a popup for errors. */
2124 if (NILP(header))
2125 dialog_name[0] = 'Q';
2126 else
2127 dialog_name[0] = 'I';
2128
80670155
RS
2129 /* Dialog boxes use a really stupid name encoding
2130 which specifies how many buttons to use
6a040d6a 2131 and how many buttons are on the right. */
80670155
RS
2132 dialog_name[1] = '0' + nb_buttons;
2133 dialog_name[2] = 'B';
2134 dialog_name[3] = 'R';
fcaa7665
RS
2135 /* Number of buttons to put on the right. */
2136 dialog_name[4] = '0' + nb_buttons - left_count;
80670155 2137 dialog_name[5] = 0;
165e1749
FP
2138 wv->contents = first_wv;
2139 first_wv = wv;
165e1749
FP
2140 }
2141
165e1749
FP
2142 /* No selection has been chosen yet. */
2143 menu_item_selection = 0;
2144
488dd4c4
JD
2145 /* Actually create and show the dialog. */
2146 create_and_show_dialog (f, first_wv);
f02cac82 2147
488dd4c4
JD
2148 /* Free the widget_value objects we used to specify the contents. */
2149 free_menubar_widget_value_tree (first_wv);
177c0ea7 2150
488dd4c4
JD
2151 /* Find the selected item, and its pane, to return
2152 the proper value. */
165e1749
FP
2153 if (menu_item_selection != 0)
2154 {
2155 Lisp_Object prefix;
2156
2157 prefix = Qnil;
2158 i = 0;
2159 while (i < menu_items_used)
2160 {
2161 Lisp_Object entry;
2162
2163 if (EQ (XVECTOR (menu_items)->contents[i], Qt))
2164 {
2165 prefix
2166 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
2167 i += MENU_ITEMS_PANE_LENGTH;
2168 }
85996cfb
GM
2169 else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
2170 {
2171 /* This is the boundary between left-side elts and
2172 right-side elts. */
2173 ++i;
2174 }
165e1749
FP
2175 else
2176 {
2177 entry
2178 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_VALUE];
2179 if (menu_item_selection == &XVECTOR (menu_items)->contents[i])
2180 {
2181 if (keymaps != 0)
2182 {
2183 entry = Fcons (entry, Qnil);
2184 if (!NILP (prefix))
2185 entry = Fcons (prefix, entry);
2186 }
2187 return entry;
2188 }
2189 i += MENU_ITEMS_ITEM_LENGTH;
2190 }
2191 }
2192 }
9f6fcdc5
JD
2193 else
2194 /* Make "Cancel" equivalent to C-g. */
2195 Fsignal (Qquit, Qnil);
165e1749
FP
2196
2197 return Qnil;
2198}
ba461919 2199
488dd4c4 2200#else /* not USE_X_TOOLKIT && not USE_GTK */
78589e07 2201
3e703b25
GM
2202/* The frame of the last activated non-toolkit menu bar.
2203 Used to generate menu help events. */
2204
2205static struct frame *menu_help_frame;
2206
2207
62145073
GM
2208/* Show help HELP_STRING, or clear help if HELP_STRING is null.
2209
2210 PANE is the pane number, and ITEM is the menu item number in
2211 the menu (currently not used).
177c0ea7 2212
62145073
GM
2213 This cannot be done with generating a HELP_EVENT because
2214 XMenuActivate contains a loop that doesn't let Emacs process
2215 keyboard events. */
3e703b25
GM
2216
2217static void
0521f580 2218menu_help_callback (char *help_string, int pane, int item)
3e703b25 2219{
62145073
GM
2220 extern Lisp_Object Qmenu_item;
2221 Lisp_Object *first_item;
2222 Lisp_Object pane_name;
2223 Lisp_Object menu_object;
177c0ea7 2224
62145073
GM
2225 first_item = XVECTOR (menu_items)->contents;
2226 if (EQ (first_item[0], Qt))
2227 pane_name = first_item[MENU_ITEMS_PANE_NAME];
2228 else if (EQ (first_item[0], Qquote))
2229 /* This shouldn't happen, see xmenu_show. */
ce33e8eb 2230 pane_name = empty_unibyte_string;
62145073
GM
2231 else
2232 pane_name = first_item[MENU_ITEMS_ITEM_NAME];
177c0ea7 2233
62145073
GM
2234 /* (menu-item MENU-NAME PANE-NUMBER) */
2235 menu_object = Fcons (Qmenu_item,
2236 Fcons (pane_name,
2237 Fcons (make_number (pane), Qnil)));
ba461919 2238 show_help_echo (help_string ? build_string (help_string) : Qnil,
62145073 2239 Qnil, menu_object, make_number (item), 1);
3e703b25 2240}
177c0ea7 2241
af89e871 2242static Lisp_Object
0521f580 2243pop_down_menu (Lisp_Object arg)
af89e871 2244{
a130b901
JD
2245 struct Lisp_Save_Value *p1 = XSAVE_VALUE (Fcar (arg));
2246 struct Lisp_Save_Value *p2 = XSAVE_VALUE (Fcdr (arg));
59cfb104 2247
a130b901
JD
2248 FRAME_PTR f = p1->pointer;
2249 XMenu *menu = p2->pointer;
af89e871
JD
2250
2251 BLOCK_INPUT;
af89e871 2252#ifndef MSDOS
a130b901
JD
2253 XUngrabPointer (FRAME_X_DISPLAY (f), CurrentTime);
2254 XUngrabKeyboard (FRAME_X_DISPLAY (f), CurrentTime);
af89e871 2255#endif
a130b901 2256 XMenuDestroy (FRAME_X_DISPLAY (f), menu);
af89e871
JD
2257
2258#ifdef HAVE_X_WINDOWS
2259 /* Assume the mouse has moved out of the X window.
2260 If it has actually moved in, we will get an EnterNotify. */
2261 x_mouse_leave (FRAME_X_DISPLAY_INFO (f));
2262
2263 /* State that no mouse buttons are now held.
2264 (The oldXMenu code doesn't track this info for us.)
2265 That is not necessarily true, but the fiction leads to reasonable
2266 results, and it is a pain to ask which are actually held now. */
2267 FRAME_X_DISPLAY_INFO (f)->grabbed = 0;
2268
2269#endif /* HAVE_X_WINDOWS */
2270
2271 UNBLOCK_INPUT;
2272
2273 return Qnil;
2274}
2275
3e703b25 2276
1fb99a3a 2277Lisp_Object
0521f580 2278xmenu_show (FRAME_PTR f, int x, int y, int for_click, int keymaps, Lisp_Object title, char **error, unsigned int timestamp)
dcfdbac7 2279{
177c0ea7 2280 Window root;
78589e07
RS
2281 XMenu *menu;
2282 int pane, selidx, lpane, status;
2283 Lisp_Object entry, pane_prefix;
dcfdbac7
JB
2284 char *datap;
2285 int ulx, uly, width, height;
2286 int dispwidth, dispheight;
453a4f1b 2287 int i, j, lines, maxlines;
4e8d3549 2288 int maxwidth;
78589e07
RS
2289 int dummy_int;
2290 unsigned int dummy_uint;
af89e871 2291 int specpdl_count = SPECPDL_INDEX ();
088831f6 2292
a9e1244d 2293 if (! FRAME_X_P (f) && ! FRAME_MSDOS_P (f))
62af879c
KL
2294 abort ();
2295
07a675b7 2296 *error = 0;
78589e07
RS
2297 if (menu_items_n_panes == 0)
2298 return Qnil;
088831f6 2299
742f715d
KH
2300 if (menu_items_used <= MENU_ITEMS_PANE_LENGTH)
2301 {
2302 *error = "Empty menu";
2303 return Qnil;
2304 }
2305
78589e07 2306 /* Figure out which root window F is on. */
92280f67 2307 XGetGeometry (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), &root,
78589e07
RS
2308 &dummy_int, &dummy_int, &dummy_uint, &dummy_uint,
2309 &dummy_uint, &dummy_uint);
18686d47 2310
78589e07 2311 /* Make the menu on that window. */
92280f67 2312 menu = XMenuCreate (FRAME_X_DISPLAY (f), root, "emacs");
78589e07 2313 if (menu == NULL)
dcfdbac7
JB
2314 {
2315 *error = "Can't create menu";
78589e07 2316 return Qnil;
dcfdbac7 2317 }
78589e07 2318
d130d129
RS
2319 /* Don't GC while we prepare and show the menu,
2320 because we give the oldxmenu library pointers to the
2321 contents of strings. */
2322 inhibit_garbage_collection ();
2323
87485d6f 2324#ifdef HAVE_X_WINDOWS
78589e07 2325 /* Adjust coordinates to relative to the outer (window manager) window. */
453a4f1b
JD
2326 x += FRAME_OUTER_TO_INNER_DIFF_X (f);
2327 y += FRAME_OUTER_TO_INNER_DIFF_Y (f);
87485d6f 2328#endif /* HAVE_X_WINDOWS */
78589e07
RS
2329
2330 /* Adjust coordinates to be root-window-relative. */
9882535b
KS
2331 x += f->left_pos;
2332 y += f->top_pos;
177c0ea7 2333
78589e07 2334 /* Create all the necessary panes and their items. */
453a4f1b 2335 maxlines = lines = i = 0;
78589e07 2336 while (i < menu_items_used)
dcfdbac7 2337 {
78589e07 2338 if (EQ (XVECTOR (menu_items)->contents[i], Qt))
dcfdbac7 2339 {
78589e07
RS
2340 /* Create a new pane. */
2341 Lisp_Object pane_name, prefix;
2342 char *pane_string;
2343
453a4f1b
JD
2344 maxlines = max (maxlines, lines);
2345 lines = 0;
78589e07
RS
2346 pane_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_NAME];
2347 prefix = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
2348 pane_string = (NILP (pane_name)
d5db4077 2349 ? "" : (char *) SDATA (pane_name));
78589e07
RS
2350 if (keymaps && !NILP (prefix))
2351 pane_string++;
2352
92280f67 2353 lpane = XMenuAddPane (FRAME_X_DISPLAY (f), menu, pane_string, TRUE);
78589e07
RS
2354 if (lpane == XM_FAILURE)
2355 {
92280f67 2356 XMenuDestroy (FRAME_X_DISPLAY (f), menu);
78589e07
RS
2357 *error = "Can't create pane";
2358 return Qnil;
2359 }
2360 i += MENU_ITEMS_PANE_LENGTH;
4e8d3549
RS
2361
2362 /* Find the width of the widest item in this pane. */
2363 maxwidth = 0;
2364 j = i;
2365 while (j < menu_items_used)
2366 {
2367 Lisp_Object item;
2368 item = XVECTOR (menu_items)->contents[j];
2369 if (EQ (item, Qt))
2370 break;
2371 if (NILP (item))
2372 {
2373 j++;
2374 continue;
2375 }
d5db4077 2376 width = SBYTES (item);
4e8d3549
RS
2377 if (width > maxwidth)
2378 maxwidth = width;
2379
2380 j += MENU_ITEMS_ITEM_LENGTH;
2381 }
dcfdbac7 2382 }
fcaa7665
RS
2383 /* Ignore a nil in the item list.
2384 It's meaningful only for dialog boxes. */
2385 else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
2386 i += 1;
78589e07 2387 else
dcfdbac7 2388 {
78589e07 2389 /* Create a new item within current pane. */
3e703b25 2390 Lisp_Object item_name, enable, descrip, help;
4e8d3549 2391 unsigned char *item_data;
3e703b25 2392 char *help_string;
78589e07
RS
2393
2394 item_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_NAME];
2395 enable = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_ENABLE];
2396 descrip
2397 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_EQUIV_KEY];
3e703b25 2398 help = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_HELP];
d5db4077 2399 help_string = STRINGP (help) ? SDATA (help) : NULL;
177c0ea7 2400
78589e07 2401 if (!NILP (descrip))
4e8d3549 2402 {
d5db4077 2403 int gap = maxwidth - SBYTES (item_name);
4e8d3549
RS
2404 /* if alloca is fast, use that to make the space,
2405 to reduce gc needs. */
2406 item_data
2407 = (unsigned char *) alloca (maxwidth
d5db4077 2408 + SBYTES (descrip) + 1);
72af86bd 2409 memcpy (item_data, SDATA (item_name), SBYTES (item_name));
d5db4077 2410 for (j = SCHARS (item_name); j < maxwidth; j++)
4e8d3549 2411 item_data[j] = ' ';
72af86bd 2412 memcpy (item_data + j, SDATA (descrip), SBYTES (descrip));
d5db4077 2413 item_data[j + SBYTES (descrip)] = 0;
4e8d3549
RS
2414 }
2415 else
d5db4077 2416 item_data = SDATA (item_name);
78589e07 2417
92280f67
RS
2418 if (XMenuAddSelection (FRAME_X_DISPLAY (f),
2419 menu, lpane, 0, item_data,
3e703b25 2420 !NILP (enable), help_string)
dcfdbac7
JB
2421 == XM_FAILURE)
2422 {
92280f67 2423 XMenuDestroy (FRAME_X_DISPLAY (f), menu);
dcfdbac7 2424 *error = "Can't add selection to menu";
78589e07 2425 return Qnil;
dcfdbac7 2426 }
78589e07 2427 i += MENU_ITEMS_ITEM_LENGTH;
453a4f1b 2428 lines++;
dcfdbac7
JB
2429 }
2430 }
4e8d3549 2431
453a4f1b
JD
2432 maxlines = max (maxlines, lines);
2433
78589e07 2434 /* All set and ready to fly. */
92280f67 2435 XMenuRecompute (FRAME_X_DISPLAY (f), menu);
63685b9d
GM
2436 dispwidth = DisplayWidth (FRAME_X_DISPLAY (f), FRAME_X_SCREEN_NUMBER (f));
2437 dispheight = DisplayHeight (FRAME_X_DISPLAY (f), FRAME_X_SCREEN_NUMBER (f));
78589e07
RS
2438 x = min (x, dispwidth);
2439 y = min (y, dispheight);
2440 x = max (x, 1);
2441 y = max (y, 1);
92280f67 2442 XMenuLocate (FRAME_X_DISPLAY (f), menu, 0, 0, x, y,
dcfdbac7
JB
2443 &ulx, &uly, &width, &height);
2444 if (ulx+width > dispwidth)
2445 {
78589e07 2446 x -= (ulx + width) - dispwidth;
dcfdbac7
JB
2447 ulx = dispwidth - width;
2448 }
2449 if (uly+height > dispheight)
2450 {
78589e07 2451 y -= (uly + height) - dispheight;
dcfdbac7
JB
2452 uly = dispheight - height;
2453 }
2de7397f
EZ
2454#ifndef HAVE_X_WINDOWS
2455 if (FRAME_HAS_MINIBUF_P (f) && uly+height > dispheight - 1)
2456 {
2457 /* Move the menu away of the echo area, to avoid overwriting the
2458 menu with help echo messages or vice versa. */
2459 if (BUFFERP (echo_area_buffer[0]) && WINDOWP (echo_area_window))
2460 {
2461 y -= WINDOW_TOTAL_LINES (XWINDOW (echo_area_window));
2462 uly -= WINDOW_TOTAL_LINES (XWINDOW (echo_area_window));
2463 }
2464 else
2465 {
2466 y--;
2467 uly--;
2468 }
2469 }
2470#endif
78589e07
RS
2471 if (ulx < 0) x -= ulx;
2472 if (uly < 0) y -= uly;
121e4555 2473
453a4f1b
JD
2474 if (! for_click)
2475 {
2476 /* If position was not given by a mouse click, adjust so upper left
2477 corner of the menu as a whole ends up at given coordinates. This
2478 is what x-popup-menu says in its documentation. */
2479 x += width/2;
2480 y += 1.5*height/(maxlines+2);
2481 }
2482
121e4555 2483 XMenuSetAEQ (menu, TRUE);
78589e07
RS
2484 XMenuSetFreeze (menu, TRUE);
2485 pane = selidx = 0;
3e703b25 2486
c3438661
JD
2487#ifndef MSDOS
2488 XMenuActivateSetWaitFunction (x_menu_wait_for_event, FRAME_X_DISPLAY (f));
2489#endif
59cfb104 2490
a130b901
JD
2491 record_unwind_protect (pop_down_menu,
2492 Fcons (make_save_value (f, 0),
2493 make_save_value (menu, 0)));
c3438661 2494
3e703b25
GM
2495 /* Help display under X won't work because XMenuActivate contains
2496 a loop that doesn't give Emacs a chance to process it. */
2497 menu_help_frame = f;
92280f67 2498 status = XMenuActivate (FRAME_X_DISPLAY (f), menu, &pane, &selidx,
af89e871
JD
2499 x, y, ButtonReleaseMask, &datap,
2500 menu_help_callback);
a352a815 2501
dcfdbac7
JB
2502 switch (status)
2503 {
2504 case XM_SUCCESS:
2505#ifdef XDEBUG
2506 fprintf (stderr, "pane= %d line = %d\n", panes, selidx);
2507#endif
fa6d54d9 2508
78589e07
RS
2509 /* Find the item number SELIDX in pane number PANE. */
2510 i = 0;
2511 while (i < menu_items_used)
fa6d54d9 2512 {
78589e07 2513 if (EQ (XVECTOR (menu_items)->contents[i], Qt))
088831f6 2514 {
78589e07
RS
2515 if (pane == 0)
2516 pane_prefix
2517 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
2518 pane--;
2519 i += MENU_ITEMS_PANE_LENGTH;
088831f6 2520 }
78589e07 2521 else
ab6ee1a0 2522 {
78589e07 2523 if (pane == -1)
ab6ee1a0 2524 {
78589e07 2525 if (selidx == 0)
ab6ee1a0 2526 {
78589e07
RS
2527 entry
2528 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_VALUE];
2529 if (keymaps != 0)
ab6ee1a0 2530 {
78589e07
RS
2531 entry = Fcons (entry, Qnil);
2532 if (!NILP (pane_prefix))
2533 entry = Fcons (pane_prefix, entry);
ab6ee1a0 2534 }
78589e07 2535 break;
ab6ee1a0 2536 }
78589e07 2537 selidx--;
ab6ee1a0 2538 }
78589e07 2539 i += MENU_ITEMS_ITEM_LENGTH;
ab6ee1a0
RS
2540 }
2541 }
78589e07 2542 break;
dcfdbac7 2543
78589e07 2544 case XM_FAILURE:
78589e07
RS
2545 *error = "Can't activate menu";
2546 case XM_IA_SELECT:
9f6fcdc5
JD
2547 entry = Qnil;
2548 break;
78589e07 2549 case XM_NO_SELECT:
be6ed24a
RS
2550 /* Make "Cancel" equivalent to C-g unless FOR_CLICK (which means
2551 the menu was invoked with a mouse event as POSITION). */
9f6fcdc5
JD
2552 if (! for_click)
2553 Fsignal (Qquit, Qnil);
78589e07
RS
2554 entry = Qnil;
2555 break;
dcfdbac7 2556 }
a5285df3 2557
af89e871 2558 unbind_to (specpdl_count, Qnil);
a5285df3 2559
78589e07 2560 return entry;
dcfdbac7 2561}
4dedbfe0 2562
78589e07 2563#endif /* not USE_X_TOOLKIT */
1e659e4c
RS
2564
2565#endif /* HAVE_MENUS */
e3135734 2566
b79b8a1c
CY
2567/* Detect if a dialog or menu has been posted. */
2568
2569int
971de7fb 2570popup_activated (void)
b79b8a1c
CY
2571{
2572 return popup_activated_flag;
2573}
e3135734
CY
2574
2575/* The following is used by delayed window autoselection. */
2576
2577DEFUN ("menu-or-popup-active-p", Fmenu_or_popup_active_p, Smenu_or_popup_active_p, 0, 0, 0,
2578 doc: /* Return t if a menu or popup dialog is active. */)
5842a27b 2579 (void)
e3135734
CY
2580{
2581#ifdef HAVE_MENUS
2582 return (popup_activated ()) ? Qt : Qnil;
2583#else
2584 return Qnil;
2585#endif /* HAVE_MENUS */
2586}
088831f6 2587\f
dfcf069d 2588void
971de7fb 2589syms_of_xmenu (void)
dcfdbac7 2590{
d67b4f80 2591 Qdebug_on_next_call = intern_c_string ("debug-on-next-call");
0314aacb
RS
2592 staticpro (&Qdebug_on_next_call);
2593
8ed87156 2594#ifdef USE_X_TOOLKIT
177c0ea7 2595 widget_id_tick = (1<<16);
88766961 2596 next_menubar_widget_id = 1;
8ed87156
RS
2597#endif
2598
508fb067 2599 defsubr (&Smenu_or_popup_active_p);
22badffe
JD
2600
2601#if defined (USE_GTK) || defined (USE_X_TOOLKIT)
12b6af5c 2602 defsubr (&Sx_menu_bar_open_internal);
d67b4f80
DN
2603 Ffset (intern_c_string ("accelerate-menu"),
2604 intern_c_string (Sx_menu_bar_open_internal.symbol_name));
22badffe
JD
2605#endif
2606
1e659e4c 2607#ifdef HAVE_MENUS
165e1749 2608 defsubr (&Sx_popup_dialog);
1e659e4c 2609#endif
dcfdbac7 2610}
6b61353c
KH
2611
2612/* arch-tag: 92ea573c-398e-496e-ac73-2436f7d63242
2613 (do not change this comment) */