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