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