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