* buffer.h (FETCH_MULTIBYTE_CHAR): Define as inline.
[emacs.git] / src / xmenu.c
blobdc98b980278f2cda22e7d4070f60900e6b61e4fe
1 /* X Communication module for terminals which understand the X protocol.
3 Copyright (C) 1986, 1988, 1993-1994, 1996, 1999-2012
4 Free Software Foundation, Inc.
6 This file is part of GNU Emacs.
8 GNU Emacs is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
13 GNU Emacs is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
21 /* X pop-up deck-of-cards menu facility for GNU Emacs.
23 * Written by Jon Arnold and Roman Budzianowski
24 * Mods and rewrite by Robert Krawitz
28 /* Modified by Fred Pierresteguy on December 93
29 to make the popup menus and menubar use the Xt. */
31 /* Rewritten for clarity and GC protection by rms in Feb 94. */
33 #include <config.h>
35 #if 0 /* Why was this included? And without syssignal.h? */
36 /* On 4.3 this loses if it comes after xterm.h. */
37 #include <signal.h>
38 #endif
40 #include <stdio.h>
41 #include <setjmp.h>
43 #include "lisp.h"
44 #include "keyboard.h"
45 #include "keymap.h"
46 #include "frame.h"
47 #include "termhooks.h"
48 #include "window.h"
49 #include "blockinput.h"
50 #include "character.h"
51 #include "buffer.h"
52 #include "charset.h"
53 #include "coding.h"
54 #include "sysselect.h"
56 #ifdef MSDOS
57 #include "msdos.h"
58 #endif
60 #ifdef HAVE_X_WINDOWS
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"
64 #endif
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
72 #include "dispextern.h"
74 #ifdef HAVE_X_WINDOWS
75 /* Defining HAVE_MULTILINGUAL_MENU would mean that the toolkit menu
76 code accepts the Emacs internal encoding. */
77 #undef HAVE_MULTILINGUAL_MENU
78 #ifdef USE_X_TOOLKIT
79 #include "widget.h"
80 #include <X11/Xlib.h>
81 #include <X11/IntrinsicP.h>
82 #include <X11/CoreP.h>
83 #include <X11/StringDefs.h>
84 #include <X11/Shell.h>
85 #ifdef USE_LUCID
86 #include "xsettings.h"
87 #include "../lwlib/xlwmenu.h"
88 #ifdef HAVE_XAW3D
89 #include <X11/Xaw3d/Paned.h>
90 #else /* !HAVE_XAW3D */
91 #include <X11/Xaw/Paned.h>
92 #endif /* HAVE_XAW3D */
93 #endif /* USE_LUCID */
94 #ifdef USE_MOTIF
95 #include "../lwlib/lwlib.h"
96 #endif
97 #else /* not USE_X_TOOLKIT */
98 #ifndef USE_GTK
99 #include "../oldXMenu/XMenu.h"
100 #endif
101 #endif /* not USE_X_TOOLKIT */
102 #endif /* HAVE_X_WINDOWS */
104 #ifdef USE_GTK
105 #include "gtkutil.h"
106 #ifdef HAVE_GTK3
107 #include "xgselect.h"
108 #endif
109 #endif
111 #include "menu.h"
113 #ifndef TRUE
114 #define TRUE 1
115 #endif /* no TRUE */
117 static Lisp_Object Qdebug_on_next_call;
119 #if defined (USE_X_TOOLKIT) || defined (USE_GTK)
120 static Lisp_Object xdialog_show (FRAME_PTR, int, Lisp_Object, Lisp_Object,
121 const char **);
122 #endif
124 /* Flag which when set indicates a dialog or menu has been posted by
125 Xt on behalf of one of the widget sets. */
126 static int popup_activated_flag;
129 #ifdef USE_X_TOOLKIT
131 static int next_menubar_widget_id;
133 /* Return the frame whose ->output_data.x->id equals ID, or 0 if none. */
135 static struct frame *
136 menubar_id_to_frame (LWLIB_ID id)
138 Lisp_Object tail, frame;
139 FRAME_PTR f;
141 for (tail = Vframe_list; CONSP (tail); tail = XCDR (tail))
143 frame = XCAR (tail);
144 if (!FRAMEP (frame))
145 continue;
146 f = XFRAME (frame);
147 if (!FRAME_WINDOW_P (f))
148 continue;
149 if (f->output_data.x->id == id)
150 return f;
152 return 0;
155 #endif
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. */
165 void
166 mouse_position_for_popup (FRAME_PTR f, int *x, int *y)
168 Window root, dummy_window;
169 int dummy;
171 if (! FRAME_X_P (f))
172 abort ();
174 BLOCK_INPUT;
176 XQueryPointer (FRAME_X_DISPLAY (f),
177 DefaultRootWindow (FRAME_X_DISPLAY (f)),
179 /* The root window which contains the pointer. */
180 &root,
182 /* Window pointer is on, not used */
183 &dummy_window,
185 /* The position on that root window. */
186 x, y,
188 /* x/y in dummy_window coordinates, not used. */
189 &dummy, &dummy,
191 /* Modifier keys and pointer buttons, about which
192 we don't care. */
193 (unsigned int *) &dummy);
195 UNBLOCK_INPUT;
197 /* xmenu_show expects window coordinates, not root window
198 coordinates. Translate. */
199 *x -= f->left_pos + FRAME_OUTER_TO_INNER_DIFF_X (f);
200 *y -= f->top_pos + FRAME_OUTER_TO_INNER_DIFF_Y (f);
203 #endif /* HAVE_X_WINDOWS */
205 #ifdef HAVE_MENUS
207 DEFUN ("x-popup-dialog", Fx_popup_dialog, Sx_popup_dialog, 2, 3, 0,
208 doc: /* Pop up a dialog box and return user's selection.
209 POSITION specifies which frame to use.
210 This is normally a mouse button event or a window or frame.
211 If POSITION is t, it means to use the frame the mouse is on.
212 The dialog box appears in the middle of the specified frame.
214 CONTENTS specifies the alternatives to display in the dialog box.
215 It is a list of the form (DIALOG ITEM1 ITEM2...).
216 Each ITEM is a cons cell (STRING . VALUE).
217 The return value is VALUE from the chosen item.
219 An ITEM may also be just a string--that makes a nonselectable item.
220 An ITEM may also be nil--that means to put all preceding items
221 on the left of the dialog box and all following items on the right.
222 \(By default, approximately half appear on each side.)
224 If HEADER is non-nil, the frame title for the box is "Information",
225 otherwise it is "Question".
227 If the user gets rid of the dialog box without making a valid choice,
228 for instance using the window manager, then this produces a quit and
229 `x-popup-dialog' does not return. */)
230 (Lisp_Object position, Lisp_Object contents, Lisp_Object header)
232 FRAME_PTR f = NULL;
233 Lisp_Object window;
235 check_x ();
237 /* Decode the first argument: find the window or frame to use. */
238 if (EQ (position, Qt)
239 || (CONSP (position) && (EQ (XCAR (position), Qmenu_bar)
240 || EQ (XCAR (position), Qtool_bar))))
242 #if 0 /* Using the frame the mouse is on may not be right. */
243 /* Use the mouse's current position. */
244 FRAME_PTR new_f = SELECTED_FRAME ();
245 Lisp_Object bar_window;
246 enum scroll_bar_part part;
247 Time time;
248 Lisp_Object x, y;
250 (*mouse_position_hook) (&new_f, 1, &bar_window, &part, &x, &y, &time);
252 if (new_f != 0)
253 XSETFRAME (window, new_f);
254 else
255 window = selected_window;
256 #endif
257 window = selected_window;
259 else if (CONSP (position))
261 Lisp_Object tem;
262 tem = Fcar (position);
263 if (CONSP (tem))
264 window = Fcar (Fcdr (position));
265 else
267 tem = Fcar (Fcdr (position)); /* EVENT_START (position) */
268 window = Fcar (tem); /* POSN_WINDOW (tem) */
271 else if (WINDOWP (position) || FRAMEP (position))
272 window = position;
273 else
274 window = Qnil;
276 /* Decode where to put the menu. */
278 if (FRAMEP (window))
279 f = XFRAME (window);
280 else if (WINDOWP (window))
282 CHECK_LIVE_WINDOW (window);
283 f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
285 else
286 /* ??? Not really clean; should be CHECK_WINDOW_OR_FRAME,
287 but I don't want to make one now. */
288 CHECK_WINDOW (window);
290 if (! FRAME_X_P (f) && ! FRAME_MSDOS_P (f))
291 error ("Can not put X dialog on this terminal");
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.
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);
304 #if ! defined (USE_X_TOOLKIT) && ! defined (USE_GTK)
305 /* Display a menu with these alternatives
306 in the middle of frame F. */
308 Lisp_Object x, y, frame, newpos;
309 XSETFRAME (frame, f);
310 XSETINT (x, x_pixel_width (f) / 2);
311 XSETINT (y, x_pixel_height (f) / 2);
312 newpos = Fcons (Fcons (x, Fcons (y, Qnil)), Fcons (frame, Qnil));
314 return Fx_popup_menu (newpos,
315 Fcons (Fcar (contents), Fcons (contents, Qnil)));
317 #else
319 Lisp_Object title;
320 const char *error_name;
321 Lisp_Object selection;
322 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
324 /* Decode the dialog items from what was specified. */
325 title = Fcar (contents);
326 CHECK_STRING (title);
327 record_unwind_protect (unuse_menu_items, Qnil);
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));
335 list_of_panes (Fcons (contents, Qnil));
337 /* Display them in a dialog box. */
338 BLOCK_INPUT;
339 selection = xdialog_show (f, 0, title, header, &error_name);
340 UNBLOCK_INPUT;
342 unbind_to (specpdl_count, Qnil);
343 discard_menu_items ();
345 if (error_name) error ("%s", error_name);
346 return selection;
348 #endif
352 #ifndef MSDOS
354 #if defined USE_GTK || defined USE_MOTIF
356 /* Set menu_items_inuse so no other popup menu or dialog is created. */
358 void
359 x_menu_set_in_use (int in_use)
361 menu_items_inuse = in_use ? Qt : Qnil;
362 popup_activated_flag = in_use;
363 #ifdef USE_X_TOOLKIT
364 if (popup_activated_flag)
365 x_activate_timeout_atimer ();
366 #endif
369 #endif
371 /* Wait for an X event to arrive or for a timer to expire. */
373 #ifndef USE_MOTIF
374 static
375 #endif
376 void
377 x_menu_wait_for_event (void *data)
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. */
384 while (
385 #ifdef USE_X_TOOLKIT
386 ! XtAppPending (Xt_app_con)
387 #elif defined USE_GTK
388 ! gtk_events_pending ()
389 #else
390 ! XPending ((Display*) data)
391 #endif
394 EMACS_TIME next_time = timer_check (), *ntp;
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;
401 FD_ZERO (&read_fds);
402 for (dpyinfo = x_display_list; dpyinfo; dpyinfo = dpyinfo->next)
404 int fd = ConnectionNumber (dpyinfo->display);
405 FD_SET (fd, &read_fds);
406 if (fd > n) n = fd;
407 XFlush (dpyinfo->display);
410 if (secs < 0 && usecs < 0)
411 ntp = 0;
412 else
413 ntp = &next_time;
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. */
420 xg_select (n + 1, &read_fds, (SELECT_TYPE *)0, (SELECT_TYPE *)0, ntp);
421 #else
422 select (n + 1, &read_fds, (SELECT_TYPE *)0, (SELECT_TYPE *)0, ntp);
423 #endif
426 #endif /* ! MSDOS */
429 #if defined (USE_X_TOOLKIT) || defined (USE_GTK)
431 #ifdef USE_X_TOOLKIT
433 /* Loop in Xt until the menu pulldown or dialog popup has been
434 popped down (deactivated). This is used for x-popup-menu
435 and x-popup-dialog; it is not used for the menu bar.
437 NOTE: All calls to popup_get_selection should be protected
438 with BLOCK_INPUT, UNBLOCK_INPUT wrappers. */
440 static void
441 popup_get_selection (XEvent *initial_event, struct x_display_info *dpyinfo, LWLIB_ID id, int do_timers)
443 XEvent event;
445 while (popup_activated_flag)
447 if (initial_event)
449 event = *initial_event;
450 initial_event = 0;
452 else
454 if (do_timers) x_menu_wait_for_event (0);
455 XtAppNextEvent (Xt_app_con, &event);
458 /* Make sure we don't consider buttons grabbed after menu goes.
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)
464 dpyinfo->grabbed &= ~(1 << event.xbutton.button);
465 #ifdef USE_MOTIF /* Pretending that the event came from a
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;
474 #endif
476 /* Pop down on C-g and Escape. */
477 else if (event.type == KeyPress
478 && dpyinfo->display == event.xbutton.display)
480 KeySym keysym = XLookupKeysym (&event.xkey, 0);
482 if ((keysym == XK_g && (event.xkey.state & ControlMask) != 0)
483 || keysym == XK_Escape) /* Any escape, ignore modifiers. */
484 popup_activated_flag = 0;
487 x_dispatch_event (&event, event.xany.display);
491 DEFUN ("x-menu-bar-open-internal", Fx_menu_bar_open_internal, Sx_menu_bar_open_internal, 0, 1, "i",
492 doc: /* Start key navigation of the menu bar in FRAME.
493 This initially opens the first menu bar item and you can then navigate with the
494 arrow keys, select a menu entry with the return key or cancel with the
495 escape key. If FRAME has no menu bar this function does nothing.
497 If FRAME is nil or not given, use the selected frame. */)
498 (Lisp_Object frame)
500 XEvent ev;
501 FRAME_PTR f = check_x_frame (frame);
502 Widget menubar;
503 BLOCK_INPUT;
505 if (FRAME_EXTERNAL_MENU_BAR (f))
506 set_frame_menubar (f, 0, 1);
508 menubar = FRAME_X_OUTPUT (f)->menubar_widget;
509 if (menubar)
511 Window child;
512 int error_p = 0;
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;
524 #ifdef USE_MOTIF
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]);
534 #endif
536 XTranslateCoordinates (FRAME_X_DISPLAY (f),
537 /* From-window, to-window. */
538 ev.xbutton.window, ev.xbutton.root,
540 /* From-position, to-position. */
541 ev.xbutton.x, ev.xbutton.y,
542 &ev.xbutton.x_root, &ev.xbutton.y_root,
544 /* Child of win. */
545 &child);
546 error_p = x_had_errors_p (FRAME_X_DISPLAY (f));
547 x_uncatch_errors ();
549 if (! error_p)
551 ev.type = ButtonPress;
552 ev.xbutton.state = 0;
554 XtDispatchEvent (&ev);
555 ev.xbutton.type = ButtonRelease;
556 ev.xbutton.state = Button1Mask;
557 XtDispatchEvent (&ev);
561 UNBLOCK_INPUT;
563 return Qnil;
565 #endif /* USE_X_TOOLKIT */
568 #ifdef USE_GTK
569 DEFUN ("x-menu-bar-open-internal", Fx_menu_bar_open_internal, Sx_menu_bar_open_internal, 0, 1, "i",
570 doc: /* Start key navigation of the menu bar in FRAME.
571 This initially opens the first menu bar item and you can then navigate with the
572 arrow keys, select a menu entry with the return key or cancel with the
573 escape key. If FRAME has no menu bar this function does nothing.
575 If FRAME is nil or not given, use the selected frame. */)
576 (Lisp_Object frame)
578 GtkWidget *menubar;
579 FRAME_PTR f;
581 /* gcc 2.95 doesn't accept the FRAME_PTR declaration after
582 BLOCK_INPUT. */
584 BLOCK_INPUT;
585 f = check_x_frame (frame);
587 if (FRAME_EXTERNAL_MENU_BAR (f))
588 set_frame_menubar (f, 0, 1);
590 menubar = FRAME_X_OUTPUT (f)->menubar_widget;
591 if (menubar)
593 /* Activate the first menu. */
594 GList *children = gtk_container_get_children (GTK_CONTAINER (menubar));
596 if (children)
598 g_signal_emit_by_name (children->data, "activate_item");
599 popup_activated_flag = 1;
600 g_list_free (children);
603 UNBLOCK_INPUT;
605 return Qnil;
608 /* Loop util popup_activated_flag is set to zero in a callback.
609 Used for popup menus and dialogs. */
611 static void
612 popup_widget_loop (int do_timers, GtkWidget *widget)
614 ++popup_activated_flag;
616 /* Process events in the Gtk event loop until done. */
617 while (popup_activated_flag)
619 if (do_timers) x_menu_wait_for_event (0);
620 gtk_main_iteration ();
623 #endif
625 /* Activate the menu bar of frame F.
626 This is called from keyboard.c when it gets the
627 MENU_BAR_ACTIVATE_EVENT out of the Emacs event queue.
629 To activate the menu bar, we use the X button-press event
630 that was saved in saved_menu_event.
631 That makes the toolkit do its thing.
633 But first we recompute the menu bar contents (the whole tree).
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. */
639 void
640 x_activate_menubar (FRAME_PTR f)
642 if (! FRAME_X_P (f))
643 abort ();
645 if (!f->output_data.x->saved_menu_event->type)
646 return;
648 #ifdef USE_GTK
649 if (! xg_win_to_widget (FRAME_X_DISPLAY (f),
650 f->output_data.x->saved_menu_event->xany.window))
651 return;
652 #endif
654 set_frame_menubar (f, 0, 1);
655 BLOCK_INPUT;
656 popup_activated_flag = 1;
657 #ifdef USE_GTK
658 XPutBackEvent (f->output_data.x->display_info->display,
659 f->output_data.x->saved_menu_event);
660 #else
661 XtDispatchEvent (f->output_data.x->saved_menu_event);
662 #endif
663 UNBLOCK_INPUT;
665 /* Ignore this if we get it a second time. */
666 f->output_data.x->saved_menu_event->type = 0;
669 /* This callback is invoked when the user selects a menubar cascade
670 pushbutton, but before the pulldown menu is posted. */
672 #ifndef USE_GTK
673 static void
674 popup_activate_callback (Widget widget, LWLIB_ID id, XtPointer client_data)
676 popup_activated_flag = 1;
677 #ifdef USE_X_TOOLKIT
678 x_activate_timeout_atimer ();
679 #endif
681 #endif
683 /* This callback is invoked when a dialog or menu is finished being
684 used and has been unposted. */
686 #ifdef USE_GTK
687 static void
688 popup_deactivate_callback (GtkWidget *widget, gpointer client_data)
690 popup_activated_flag = 0;
692 #else
693 static void
694 popup_deactivate_callback (Widget widget, LWLIB_ID id, XtPointer client_data)
696 popup_activated_flag = 0;
698 #endif
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. */
704 static void
705 show_help_event (FRAME_PTR f, xt_or_gtk_widget widget, Lisp_Object help)
707 Lisp_Object frame;
709 if (f)
711 XSETFRAME (frame, f);
712 kbd_buffer_store_help_event (frame, help);
714 else
716 #if 0 /* This code doesn't do anything useful. ++kfs */
717 /* WIDGET is the popup menu. It's parent is the frame's
718 widget. See which frame that is. */
719 xt_or_gtk_widget frame_widget = XtParent (widget);
720 Lisp_Object tail;
722 for (tail = Vframe_list; CONSP (tail); tail = XCDR (tail))
724 frame = XCAR (tail);
725 if (FRAMEP (frame)
726 && (f = XFRAME (frame),
727 FRAME_X_P (f) && f->output_data.x->widget == frame_widget))
728 break;
730 #endif
731 show_help_echo (help, Qnil, Qnil, Qnil);
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. */
741 #ifdef USE_GTK
742 static void
743 menu_highlight_callback (GtkWidget *widget, gpointer call_data)
745 xg_menu_item_cb_data *cb_data;
746 Lisp_Object help;
748 cb_data = (xg_menu_item_cb_data*) g_object_get_data (G_OBJECT (widget),
749 XG_ITEM_DATA);
750 if (! cb_data) return;
752 help = call_data ? cb_data->help : Qnil;
754 /* If popup_activated_flag is greater than 1 we are in a popup menu.
755 Don't pass the frame to show_help_event for those.
756 Passing frame creates an Emacs event. As we are looping in
757 popup_widget_loop, it won't be handled. Passing NULL shows the tip
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);
763 #else
764 static void
765 menu_highlight_callback (Widget widget, LWLIB_ID id, void *call_data)
767 struct frame *f;
768 Lisp_Object help;
770 widget_value *wv = (widget_value *) call_data;
772 help = wv ? wv->help : Qnil;
774 /* Determine the frame for the help event. */
775 f = menubar_id_to_frame (id);
777 show_help_event (f, widget, help);
779 #endif
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.
786 static int xg_crazy_callback_abort;
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. */
792 static void
793 menubar_selection_callback (GtkWidget *widget, gpointer client_data)
795 xg_menu_item_cb_data *cb_data = (xg_menu_item_cb_data*) client_data;
797 if (xg_crazy_callback_abort)
798 return;
800 if (! cb_data || ! cb_data->cl_data || ! cb_data->cl_data->f)
801 return;
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;
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. */
818 BLOCK_INPUT;
819 while (gtk_events_pending ())
820 gtk_main_iteration ();
821 UNBLOCK_INPUT;
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);
829 #else /* not USE_GTK */
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. */
835 static void
836 menubar_selection_callback (Widget widget, LWLIB_ID id, XtPointer client_data)
838 FRAME_PTR f;
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);
846 #endif /* not USE_GTK */
848 /* Recompute all the widgets of frame F, when the menu bar has been
849 changed. Value is non-zero if widgets were updated. */
851 static int
852 update_frame_menubar (FRAME_PTR f)
854 #ifdef USE_GTK
855 return xg_update_frame_menubar (f);
856 #else
857 struct x_output *x;
858 int columns, rows;
860 if (! FRAME_X_P (f))
861 abort ();
863 x = f->output_data.x;
865 if (!x->menubar_widget || XtIsManaged (x->menubar_widget))
866 return 0;
868 BLOCK_INPUT;
869 /* Save the size of the frame because the pane widget doesn't accept
870 to resize itself. So force it. */
871 columns = FRAME_COLS (f);
872 rows = FRAME_LINES (f);
874 /* Do the voodoo which means "I'm changing lots of things, don't try
875 to refigure sizes until I'm done." */
876 lw_refigure_widget (x->column_widget, False);
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. */
881 XtUnmanageChild (x->edit_widget);
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);
889 /* Re-manage the text-area widget, and then thrash the sizes. */
890 XtManageChild (x->edit_widget);
891 lw_refigure_widget (x->column_widget, True);
893 /* Force the pane widget to resize itself with the right values. */
894 EmacsFrameSetCharSize (x->edit_widget, columns, rows);
895 UNBLOCK_INPUT;
896 #endif
897 return 1;
900 #ifdef USE_LUCID
901 static void
902 apply_systemfont_to_dialog (Widget w)
904 const char *fn = xsettings_get_system_normal_font ();
905 if (fn)
907 XrmDatabase db = XtDatabase (XtDisplay (w));
908 if (db)
909 XrmPutStringResource (&db, "*dialog.font", fn);
913 static void
914 apply_systemfont_to_menu (struct frame *f, Widget w)
916 const char *fn = xsettings_get_system_normal_font ();
918 if (fn)
920 XrmDatabase db = XtDatabase (XtDisplay (w));
921 if (db)
923 XrmPutStringResource (&db, "*menubar*font", fn);
924 XrmPutStringResource (&db, "*popup*font", fn);
929 #endif
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. */
935 void
936 set_frame_menubar (FRAME_PTR f, int first_time, int deep_p)
938 xt_or_gtk_widget menubar_widget;
939 #ifdef USE_X_TOOLKIT
940 LWLIB_ID id;
941 #endif
942 Lisp_Object items;
943 widget_value *wv, *first_wv, *prev_wv = 0;
944 int i;
945 int *submenu_start, *submenu_end;
946 int *submenu_top_level_items, *submenu_n_panes;
948 if (! FRAME_X_P (f))
949 abort ();
951 menubar_widget = f->output_data.x->menubar_widget;
953 XSETFRAME (Vmenu_updating_frame, f);
955 #ifdef USE_X_TOOLKIT
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;
959 #endif
961 if (! menubar_widget)
962 deep_p = 1;
963 /* Make the first call for any given frame always go deep. */
964 else if (!f->output_data.x->saved_menu_event && !deep_p)
966 deep_p = 1;
967 f->output_data.x->saved_menu_event = (XEvent*)xmalloc (sizeof (XEvent));
968 f->output_data.x->saved_menu_event->type = 0;
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
977 if (deep_p)
979 /* Make a widget-value tree representing the entire menu trees. */
981 struct buffer *prev = current_buffer;
982 Lisp_Object buffer;
983 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
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));
988 int subitems;
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;
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);
1001 record_unwind_save_match_data ();
1002 if (NILP (Voverriding_local_map_menu_flag))
1004 specbind (Qoverriding_terminal_local_map, Qnil);
1005 specbind (Qoverriding_local_map, Qnil);
1008 set_buffer_internal_1 (XBUFFER (buffer));
1010 /* Run the Lucid hook. */
1011 safe_run_hooks (Qactivate_menubar_hook);
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);
1017 safe_run_hooks (Qmenu_bar_update_hook);
1018 FRAME_MENU_BAR_ITEMS (f) = menu_bar_items (FRAME_MENU_BAR_ITEMS (f));
1020 items = FRAME_MENU_BAR_ITEMS (f);
1022 /* Save the frame's previous menu bar contents data. */
1023 if (previous_menu_items_used)
1024 memcpy (previous_items, XVECTOR (f->menu_bar_vector)->contents,
1025 previous_menu_items_used * sizeof (Lisp_Object));
1027 /* Fill in menu_items with the current menu bar contents.
1028 This can evaluate Lisp code. */
1029 save_menu_items ();
1031 menu_items = f->menu_bar_vector;
1032 menu_items_allocated = VECTORP (menu_items) ? ASIZE (menu_items) : 0;
1033 subitems = ASIZE (items) / 4;
1034 submenu_start = (int *) alloca ((subitems + 1) * sizeof (int));
1035 submenu_end = (int *) alloca (subitems * sizeof (int));
1036 submenu_n_panes = (int *) alloca (subitems * sizeof (int));
1037 submenu_top_level_items = (int *) alloca (subitems * sizeof (int));
1038 init_menu_items ();
1039 for (i = 0; i < subitems; i++)
1041 Lisp_Object key, string, maps;
1043 key = XVECTOR (items)->contents[4 * i];
1044 string = XVECTOR (items)->contents[4 * i + 1];
1045 maps = XVECTOR (items)->contents[4 * i + 2];
1046 if (NILP (string))
1047 break;
1049 submenu_start[i] = menu_items_used;
1051 menu_items_n_panes = 0;
1052 submenu_top_level_items[i]
1053 = parse_single_submenu (key, string, maps);
1054 submenu_n_panes[i] = menu_items_n_panes;
1056 submenu_end[i] = menu_items_used;
1059 submenu_start[i] = -1;
1060 finish_menu_items ();
1062 /* Convert menu_items into widget_value trees
1063 to display the menu. This cannot evaluate Lisp code. */
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;
1073 for (i = 0; 0 <= submenu_start[i]; i++)
1075 menu_items_n_panes = submenu_n_panes[i];
1076 wv = digest_single_submenu (submenu_start[i], submenu_end[i],
1077 submenu_top_level_items[i]);
1078 if (prev_wv)
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;
1084 wv->button_type = BUTTON_TYPE_NONE;
1085 prev_wv = wv;
1088 set_buffer_internal_1 (prev);
1090 /* If there has been no change in the Lisp-level contents
1091 of the menu bar, skip redisplaying it. Just exit. */
1093 /* Compare the new menu items with the ones computed last time. */
1094 for (i = 0; i < previous_menu_items_used; i++)
1095 if (menu_items_used == i
1096 || (!EQ (previous_items[i], XVECTOR (menu_items)->contents[i])))
1097 break;
1098 if (i == menu_items_used && i == previous_menu_items_used && i != 0)
1100 /* The menu items have not changed. Don't bother updating
1101 the menus in any form, since it would be a no-op. */
1102 free_menubar_widget_value_tree (first_wv);
1103 discard_menu_items ();
1104 unbind_to (specpdl_count, Qnil);
1105 return;
1108 /* The menu items are different, so store them in the frame. */
1109 f->menu_bar_vector = menu_items;
1110 f->menu_bar_items_used = menu_items_used;
1112 /* This undoes save_menu_items. */
1113 unbind_to (specpdl_count, Qnil);
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;
1118 for (i = 0; i < ASIZE (items); i += 4)
1120 Lisp_Object string;
1121 string = XVECTOR (items)->contents[i + 1];
1122 if (NILP (string))
1123 break;
1124 wv->name = SSDATA (string);
1125 update_submenu_strings (wv->contents);
1126 wv = wv->next;
1130 else
1132 /* Make a widget-value tree containing
1133 just the top level menu bar strings. */
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;
1143 items = FRAME_MENU_BAR_ITEMS (f);
1144 for (i = 0; i < ASIZE (items); i += 4)
1146 Lisp_Object string;
1148 string = XVECTOR (items)->contents[i + 1];
1149 if (NILP (string))
1150 break;
1152 wv = xmalloc_widget_value ();
1153 wv->name = SSDATA (string);
1154 wv->value = 0;
1155 wv->enabled = 1;
1156 wv->button_type = BUTTON_TYPE_NONE;
1157 wv->help = Qnil;
1158 /* This prevents lwlib from assuming this
1159 menu item is really supposed to be empty. */
1160 /* The intptr_t cast avoids a warning.
1161 This value just has to be different from small integers. */
1162 wv->call_data = (void *) (intptr_t) (-1);
1164 if (prev_wv)
1165 prev_wv->next = wv;
1166 else
1167 first_wv->contents = wv;
1168 prev_wv = wv;
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;
1177 /* Create or update the menu bar widget. */
1179 BLOCK_INPUT;
1181 #ifdef USE_GTK
1182 xg_crazy_callback_abort = 1;
1183 if (menubar_widget)
1185 /* The fourth arg is DEEP_P, which says to consider the entire
1186 menu trees we supply, rather than just the menu bar item names. */
1187 xg_modify_menubar_widgets (menubar_widget,
1189 first_wv,
1190 deep_p,
1191 G_CALLBACK (menubar_selection_callback),
1192 G_CALLBACK (popup_deactivate_callback),
1193 G_CALLBACK (menu_highlight_callback));
1195 else
1197 menubar_widget
1198 = xg_create_widget ("menubar", "menubar", f, first_wv,
1199 G_CALLBACK (menubar_selection_callback),
1200 G_CALLBACK (popup_deactivate_callback),
1201 G_CALLBACK (menu_highlight_callback));
1203 f->output_data.x->menubar_widget = menubar_widget;
1207 #else /* not USE_GTK */
1208 if (menubar_widget)
1210 /* Disable resizing (done for Motif!) */
1211 lw_allow_resizing (f->output_data.x->widget, False);
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. */
1215 lw_modify_all_widgets (id, first_wv, deep_p);
1217 /* Re-enable the edit widget to resize. */
1218 lw_allow_resizing (f->output_data.x->widget, True);
1220 else
1222 char menuOverride[] = "Ctrl<KeyPress>g: MenuGadgetEscape()";
1223 XtTranslations override = XtParseTranslationTable (menuOverride);
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,
1230 f->output_data.x->column_widget,
1232 popup_activate_callback,
1233 menubar_selection_callback,
1234 popup_deactivate_callback,
1235 menu_highlight_callback);
1236 f->output_data.x->menubar_widget = menubar_widget;
1238 /* Make menu pop down on C-g. */
1239 XtOverrideTranslations (menubar_widget, override);
1243 int menubar_size;
1244 if (f->output_data.x->menubar_widget)
1245 XtRealizeWidget (f->output_data.x->menubar_widget);
1247 menubar_size
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)
1251 : 0);
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
1256 WM size hints are off by these pixels. Jan D, oct 2009. */
1257 #ifdef USE_LUCID
1258 if (FRAME_EXTERNAL_MENU_BAR (f))
1260 Dimension ibw = 0;
1261 XtVaGetValues (f->output_data.x->column_widget,
1262 XtNinternalBorderWidth, &ibw, NULL);
1263 menubar_size += ibw;
1265 #endif /* USE_LUCID */
1266 #endif /* 1 */
1268 f->output_data.x->menubar_height = menubar_size;
1270 #endif /* not USE_GTK */
1272 free_menubar_widget_value_tree (first_wv);
1273 update_frame_menubar (f);
1275 #ifdef USE_GTK
1276 xg_crazy_callback_abort = 0;
1277 #endif
1279 UNBLOCK_INPUT;
1282 /* Called from Fx_create_frame to create the initial menubar of a frame
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. */
1287 void
1288 initialize_frame_menubar (FRAME_PTR f)
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));
1293 set_frame_menubar (f, 1, 1);
1297 /* Get rid of the menu bar of frame F, and free its storage.
1298 This is used when deleting a frame, and when turning off the menu bar.
1299 For GTK this function is in gtkutil.c. */
1301 #ifndef USE_GTK
1302 void
1303 free_frame_menubar (FRAME_PTR f)
1305 Widget menubar_widget;
1307 if (! FRAME_X_P (f))
1308 abort ();
1310 menubar_widget = f->output_data.x->menubar_widget;
1312 f->output_data.x->menubar_height = 0;
1314 if (menubar_widget)
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
1319 on again, leads to pull-down menus appearing in strange
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
1328 BLOCK_INPUT;
1330 #ifdef USE_MOTIF
1331 if (f->output_data.x->widget)
1332 XtVaGetValues (f->output_data.x->widget, XtNx, &x0, XtNy, &y0, NULL);
1333 #endif
1335 lw_destroy_all_widgets ((LWLIB_ID) f->output_data.x->id);
1336 f->output_data.x->menubar_widget = NULL;
1338 if (f->output_data.x->widget)
1340 #ifdef USE_MOTIF
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);
1344 #endif
1345 x_set_window_size (f, 0, FRAME_COLS (f), FRAME_LINES (f));
1347 UNBLOCK_INPUT;
1350 #endif /* not USE_GTK */
1352 #endif /* USE_X_TOOLKIT || USE_GTK */
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. */
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.
1362 FOR_CLICK is nonzero if this menu was invoked for a mouse click.
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.) */
1370 #if defined (USE_X_TOOLKIT) || defined (USE_GTK)
1372 /* The item selected in the popup menu. */
1373 static Lisp_Object *volatile menu_item_selection;
1375 #ifdef USE_GTK
1377 /* Used when position a popup menu. See menu_position_func and
1378 create_and_show_popup_menu below. */
1379 struct next_popup_x_y
1381 FRAME_PTR f;
1382 int x;
1383 int y;
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
1393 to store in *X and *Y are as well as the frame for the popup.
1395 Here only X and Y are used. */
1396 static void
1397 menu_position_func (GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer user_data)
1399 struct next_popup_x_y* data = (struct next_popup_x_y*)user_data;
1400 GtkRequisition req;
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);
1405 *x = data->x;
1406 *y = data->y;
1408 /* Check if there is room for the menu. If not, adjust x/y so that
1409 the menu is fully visible. */
1410 gtk_widget_get_preferred_size (GTK_WIDGET (menu), NULL, &req);
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;
1417 static void
1418 popup_selection_callback (GtkWidget *widget, gpointer client_data)
1420 xg_menu_item_cb_data *cb_data = (xg_menu_item_cb_data*) client_data;
1422 if (xg_crazy_callback_abort) return;
1423 if (cb_data) menu_item_selection = (Lisp_Object *) cb_data->call_data;
1426 static Lisp_Object
1427 pop_down_menu (Lisp_Object arg)
1429 struct Lisp_Save_Value *p = XSAVE_VALUE (arg);
1431 popup_activated_flag = 0;
1432 BLOCK_INPUT;
1433 gtk_widget_destroy (GTK_WIDGET (p->pointer));
1434 UNBLOCK_INPUT;
1435 return Qnil;
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. */
1441 static void
1442 create_and_show_popup_menu (FRAME_PTR f, widget_value *first_wv, int x, int y,
1443 int for_click, Time timestamp)
1445 int i;
1446 GtkWidget *menu;
1447 GtkMenuPositionFunc pos_func = 0; /* Pop up at pointer. */
1448 struct next_popup_x_y popup_x_y;
1449 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
1450 int use_pos_func = ! for_click;
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
1458 if (! FRAME_X_P (f))
1459 abort ();
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;
1468 if (use_pos_func)
1470 /* Not invoked by a click. pop up at x/y. */
1471 pos_func = menu_position_func;
1473 /* Adjust coordinates to be root-window-relative. */
1474 x += f->left_pos + FRAME_OUTER_TO_INNER_DIFF_X (f);
1475 y += f->top_pos + FRAME_OUTER_TO_INNER_DIFF_Y (f);
1477 popup_x_y.x = x;
1478 popup_x_y.y = y;
1479 popup_x_y.f = f;
1481 i = 0; /* gtk_menu_popup needs this to be 0 for a non-button popup. */
1484 if (for_click)
1486 for (i = 0; i < 5; i++)
1487 if (FRAME_X_DISPLAY_INFO (f)->grabbed & (1 << i))
1488 break;
1491 /* Display the menu. */
1492 gtk_widget_show_all (menu);
1494 gtk_menu_popup (GTK_MENU (menu), 0, 0, pos_func, &popup_x_y, i,
1495 timestamp ? timestamp : gtk_get_current_event_time ());
1497 record_unwind_protect (pop_down_menu, make_save_value (menu, 0));
1499 if (gtk_widget_get_mapped (menu))
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);
1508 unbind_to (specpdl_count, Qnil);
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;
1515 #else /* not USE_GTK */
1517 /* We need a unique id for each widget handled by the Lucid Widget
1518 library.
1520 For the main windows, and popup menus, we use this counter,
1521 which we increment each time after use. This starts from 1<<16.
1523 For menu bars, we use numbers starting at 0, counted in
1524 next_menubar_widget_id. */
1525 LWLIB_ID widget_id_tick;
1527 static void
1528 popup_selection_callback (Widget widget, LWLIB_ID id, XtPointer client_data)
1530 menu_item_selection = (Lisp_Object *) client_data;
1533 /* ARG is the LWLIB ID of the dialog box, represented
1534 as a Lisp object as (HIGHPART . LOWPART). */
1536 static Lisp_Object
1537 pop_down_menu (Lisp_Object arg)
1539 LWLIB_ID id = (XINT (XCAR (arg)) << 4 * sizeof (LWLIB_ID)
1540 | XINT (XCDR (arg)));
1542 BLOCK_INPUT;
1543 lw_destroy_all_widgets (id);
1544 UNBLOCK_INPUT;
1545 popup_activated_flag = 0;
1547 return Qnil;
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. */
1553 static void
1554 create_and_show_popup_menu (FRAME_PTR f, widget_value *first_wv,
1555 int x, int y, int for_click, Time timestamp)
1557 int i;
1558 Arg av[2];
1559 int ac = 0;
1560 XEvent dummy;
1561 XButtonPressedEvent *event = &(dummy.xbutton);
1562 LWLIB_ID menu_id;
1563 Widget menu;
1565 if (! FRAME_X_P (f))
1566 abort ();
1568 #ifdef USE_LUCID
1569 apply_systemfont_to_menu (f, f->output_data.x->widget);
1570 #endif
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);
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;
1589 /* Adjust coordinates to be root-window-relative. */
1590 x += f->left_pos + FRAME_OUTER_TO_INNER_DIFF_X (f);
1591 y += f->top_pos + FRAME_OUTER_TO_INNER_DIFF_Y (f);
1593 event->x_root = x;
1594 event->y_root = y;
1596 event->state = 0;
1597 event->button = 0;
1598 for (i = 0; i < 5; i++)
1599 if (FRAME_X_DISPLAY_INFO (f)->grabbed & (1 << i))
1600 event->button = i;
1602 /* Don't allow any geometry request from the user. */
1603 XtSetArg (av[ac], XtNgeometry, 0); ac++;
1604 XtSetValues (menu, av, ac);
1606 /* Display the menu. */
1607 lw_popup_menu (menu, &dummy);
1608 popup_activated_flag = 1;
1609 x_activate_timeout_atimer ();
1612 int fact = 4 * sizeof (LWLIB_ID);
1613 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
1614 record_unwind_protect (pop_down_menu,
1615 Fcons (make_number (menu_id >> (fact)),
1616 make_number (menu_id & ~(-1 << (fact)))));
1618 /* Process events that apply to the menu. */
1619 popup_get_selection ((XEvent *) 0, FRAME_X_DISPLAY_INFO (f), menu_id, 1);
1621 unbind_to (specpdl_count, Qnil);
1625 #endif /* not USE_GTK */
1627 static Lisp_Object
1628 cleanup_widget_value_tree (Lisp_Object arg)
1630 struct Lisp_Save_Value *p = XSAVE_VALUE (arg);
1631 widget_value *wv = p->pointer;
1633 free_menubar_widget_value_tree (wv);
1635 return Qnil;
1638 Lisp_Object
1639 xmenu_show (FRAME_PTR f, int x, int y, int for_click, int keymaps,
1640 Lisp_Object title, const char **error_name, Time timestamp)
1642 int i;
1643 widget_value *wv, *save_wv = 0, *first_wv = 0, *prev_wv = 0;
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;
1650 int first_pane;
1652 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
1654 if (! FRAME_X_P (f))
1655 abort ();
1657 *error_name = NULL;
1659 if (menu_items_used <= MENU_ITEMS_PANE_LENGTH)
1661 *error_name = "Empty menu";
1662 return Qnil;
1665 /* Create a tree of widget_value objects
1666 representing the panes and their items. */
1667 wv = xmalloc_widget_value ();
1668 wv->name = "menu";
1669 wv->value = 0;
1670 wv->enabled = 1;
1671 wv->button_type = BUTTON_TYPE_NONE;
1672 wv->help =Qnil;
1673 first_wv = wv;
1674 first_pane = 1;
1676 /* Loop over all panes and items, filling in the tree. */
1677 i = 0;
1678 while (i < menu_items_used)
1680 if (EQ (XVECTOR (menu_items)->contents[i], Qnil))
1682 submenu_stack[submenu_depth++] = save_wv;
1683 save_wv = prev_wv;
1684 prev_wv = 0;
1685 first_pane = 1;
1686 i++;
1688 else if (EQ (XVECTOR (menu_items)->contents[i], Qlambda))
1690 prev_wv = save_wv;
1691 save_wv = submenu_stack[--submenu_depth];
1692 first_pane = 0;
1693 i++;
1695 else if (EQ (XVECTOR (menu_items)->contents[i], Qt)
1696 && submenu_depth != 0)
1697 i += MENU_ITEMS_PANE_LENGTH;
1698 /* Ignore a nil in the item list.
1699 It's meaningful only for dialog boxes. */
1700 else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
1701 i += 1;
1702 else if (EQ (XVECTOR (menu_items)->contents[i], Qt))
1704 /* Create a new pane. */
1705 Lisp_Object pane_name, prefix;
1706 const char *pane_string;
1708 pane_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME);
1709 prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
1711 #ifndef HAVE_MULTILINGUAL_MENU
1712 if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name))
1714 pane_name = ENCODE_MENU_STRING (pane_name);
1715 ASET (menu_items, i + MENU_ITEMS_PANE_NAME, pane_name);
1717 #endif
1718 pane_string = (NILP (pane_name)
1719 ? "" : SSDATA (pane_name));
1720 /* If there is just one top-level pane, put all its items directly
1721 under the top-level menu. */
1722 if (menu_items_n_panes == 1)
1723 pane_string = "";
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. */
1728 if (!keymaps && strcmp (pane_string, ""))
1730 wv = xmalloc_widget_value ();
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;
1740 wv->button_type = BUTTON_TYPE_NONE;
1741 wv->help = Qnil;
1742 save_wv = wv;
1743 prev_wv = 0;
1745 else if (first_pane)
1747 save_wv = wv;
1748 prev_wv = 0;
1750 first_pane = 0;
1751 i += MENU_ITEMS_PANE_LENGTH;
1753 else
1755 /* Create a new item within current pane. */
1756 Lisp_Object item_name, enable, descrip, def, type, selected, help;
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);
1765 #ifndef HAVE_MULTILINGUAL_MENU
1766 if (STRINGP (item_name) && STRING_MULTIBYTE (item_name))
1768 item_name = ENCODE_MENU_STRING (item_name);
1769 ASET (menu_items, i + MENU_ITEMS_ITEM_NAME, item_name);
1772 if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
1774 descrip = ENCODE_MENU_STRING (descrip);
1775 ASET (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY, descrip);
1777 #endif /* not HAVE_MULTILINGUAL_MENU */
1779 wv = xmalloc_widget_value ();
1780 if (prev_wv)
1781 prev_wv->next = wv;
1782 else
1783 save_wv->contents = wv;
1784 wv->name = SSDATA (item_name);
1785 if (!NILP (descrip))
1786 wv->key = SSDATA (descrip);
1787 wv->value = 0;
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
1792 = (!NILP (def) ? (void *) &XVECTOR (menu_items)->contents[i] : 0);
1793 wv->enabled = !NILP (enable);
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 ();
1804 wv->selected = !NILP (selected);
1806 if (! STRINGP (help))
1807 help = Qnil;
1809 wv->help = help;
1811 prev_wv = wv;
1813 i += MENU_ITEMS_ITEM_LENGTH;
1817 /* Deal with the title, if it is non-nil. */
1818 if (!NILP (title))
1820 widget_value *wv_title = xmalloc_widget_value ();
1821 widget_value *wv_sep1 = xmalloc_widget_value ();
1822 widget_value *wv_sep2 = xmalloc_widget_value ();
1824 wv_sep2->name = "--";
1825 wv_sep2->next = first_wv->contents;
1826 wv_sep2->help = Qnil;
1828 wv_sep1->name = "--";
1829 wv_sep1->next = wv_sep2;
1830 wv_sep1->help = Qnil;
1832 #ifndef HAVE_MULTILINGUAL_MENU
1833 if (STRING_MULTIBYTE (title))
1834 title = ENCODE_MENU_STRING (title);
1835 #endif
1837 wv_title->name = SSDATA (title);
1838 wv_title->enabled = TRUE;
1839 wv_title->button_type = BUTTON_TYPE_NONE;
1840 wv_title->help = Qnil;
1841 wv_title->next = wv_sep1;
1842 first_wv->contents = wv_title;
1845 /* No selection has been chosen yet. */
1846 menu_item_selection = 0;
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));
1853 /* Actually create and show the menu until popped down. */
1854 create_and_show_popup_menu (f, first_wv, x, y, for_click, timestamp);
1856 unbind_to (specpdl_count, Qnil);
1858 /* Find the selected item, and its pane, to return
1859 the proper value. */
1860 if (menu_item_selection != 0)
1862 Lisp_Object prefix, entry;
1864 prefix = entry = Qnil;
1865 i = 0;
1866 while (i < menu_items_used)
1868 if (EQ (XVECTOR (menu_items)->contents[i], Qnil))
1870 subprefix_stack[submenu_depth++] = prefix;
1871 prefix = entry;
1872 i++;
1874 else if (EQ (XVECTOR (menu_items)->contents[i], Qlambda))
1876 prefix = subprefix_stack[--submenu_depth];
1877 i++;
1879 else if (EQ (XVECTOR (menu_items)->contents[i], Qt))
1881 prefix
1882 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
1883 i += MENU_ITEMS_PANE_LENGTH;
1885 /* Ignore a nil in the item list.
1886 It's meaningful only for dialog boxes. */
1887 else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
1888 i += 1;
1889 else
1891 entry
1892 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_VALUE];
1893 if (menu_item_selection == &XVECTOR (menu_items)->contents[i])
1895 if (keymaps != 0)
1897 int j;
1899 entry = Fcons (entry, Qnil);
1900 if (!NILP (prefix))
1901 entry = Fcons (prefix, entry);
1902 for (j = submenu_depth - 1; j >= 0; j--)
1903 if (!NILP (subprefix_stack[j]))
1904 entry = Fcons (subprefix_stack[j], entry);
1906 return entry;
1908 i += MENU_ITEMS_ITEM_LENGTH;
1912 else if (!for_click)
1913 /* Make "Cancel" equivalent to C-g. */
1914 Fsignal (Qquit, Qnil);
1916 return Qnil;
1919 #ifdef USE_GTK
1920 static void
1921 dialog_selection_callback (GtkWidget *widget, gpointer client_data)
1923 /* Treat the pointer as an integer. There's no problem
1924 as long as pointers have enough bits to hold small integers. */
1925 if ((intptr_t) client_data != -1)
1926 menu_item_selection = (Lisp_Object *) client_data;
1928 popup_activated_flag = 0;
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. */
1934 static void
1935 create_and_show_dialog (FRAME_PTR f, widget_value *first_wv)
1937 GtkWidget *menu;
1939 if (! FRAME_X_P (f))
1940 abort ();
1942 menu = xg_create_widget ("dialog", first_wv->name, f, first_wv,
1943 G_CALLBACK (dialog_selection_callback),
1944 G_CALLBACK (popup_deactivate_callback),
1947 if (menu)
1949 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
1950 record_unwind_protect (pop_down_menu, make_save_value (menu, 0));
1952 /* Display the menu. */
1953 gtk_widget_show_all (menu);
1955 /* Process events that apply to the menu. */
1956 popup_widget_loop (1, menu);
1958 unbind_to (specpdl_count, Qnil);
1962 #else /* not USE_GTK */
1963 static void
1964 dialog_selection_callback (Widget widget, LWLIB_ID id, XtPointer client_data)
1966 /* Treat the pointer as an integer. There's no problem
1967 as long as pointers have enough bits to hold small integers. */
1968 if ((intptr_t) client_data != -1)
1969 menu_item_selection = (Lisp_Object *) client_data;
1971 BLOCK_INPUT;
1972 lw_destroy_all_widgets (id);
1973 UNBLOCK_INPUT;
1974 popup_activated_flag = 0;
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. */
1981 static void
1982 create_and_show_dialog (FRAME_PTR f, widget_value *first_wv)
1984 LWLIB_ID dialog_id;
1986 if (!FRAME_X_P (f))
1987 abort ();
1989 dialog_id = widget_id_tick++;
1990 #ifdef USE_LUCID
1991 apply_systemfont_to_dialog (f->output_data.x->widget);
1992 #endif
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);
1997 /* Display the dialog box. */
1998 lw_pop_up_all_widgets (dialog_id);
1999 popup_activated_flag = 1;
2000 x_activate_timeout_atimer ();
2002 /* Process events that apply to the dialog box.
2003 Also handle timers. */
2005 ptrdiff_t count = SPECPDL_INDEX ();
2006 int fact = 4 * sizeof (LWLIB_ID);
2008 /* xdialog_show_unwind is responsible for popping the dialog box down. */
2009 record_unwind_protect (pop_down_menu,
2010 Fcons (make_number (dialog_id >> (fact)),
2011 make_number (dialog_id & ~(-1 << (fact)))));
2013 popup_get_selection ((XEvent *) 0, FRAME_X_DISPLAY_INFO (f),
2014 dialog_id, 1);
2016 unbind_to (count, Qnil);
2020 #endif /* not USE_GTK */
2022 static const char * button_names [] = {
2023 "button1", "button2", "button3", "button4", "button5",
2024 "button6", "button7", "button8", "button9", "button10" };
2026 static Lisp_Object
2027 xdialog_show (FRAME_PTR f,
2028 int keymaps,
2029 Lisp_Object title,
2030 Lisp_Object header,
2031 const char **error_name)
2033 int i, nb_buttons=0;
2034 char dialog_name[6];
2036 widget_value *wv, *first_wv = 0, *prev_wv = 0;
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;
2043 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
2045 if (! FRAME_X_P (f))
2046 abort ();
2048 *error_name = NULL;
2050 if (menu_items_n_panes > 1)
2052 *error_name = "Multiple panes in dialog box";
2053 return Qnil;
2056 /* Create a tree of widget_value objects
2057 representing the text label and buttons. */
2059 Lisp_Object pane_name, prefix;
2060 const char *pane_string;
2061 pane_name = XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME];
2062 prefix = XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_PREFIX];
2063 pane_string = (NILP (pane_name)
2064 ? "" : SSDATA (pane_name));
2065 prev_wv = xmalloc_widget_value ();
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";
2071 prev_wv->help = Qnil;
2072 first_wv = prev_wv;
2074 /* Loop over all panes and items, filling in the tree. */
2075 i = MENU_ITEMS_PANE_LENGTH;
2076 while (i < menu_items_used)
2079 /* Create a new item within current pane. */
2080 Lisp_Object item_name, enable, descrip;
2081 item_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_NAME];
2082 enable = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_ENABLE];
2083 descrip
2084 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_EQUIV_KEY];
2086 if (NILP (item_name))
2088 free_menubar_widget_value_tree (first_wv);
2089 *error_name = "Submenu in dialog items";
2090 return Qnil;
2092 if (EQ (item_name, Qquote))
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;
2100 if (nb_buttons >= 9)
2102 free_menubar_widget_value_tree (first_wv);
2103 *error_name = "Too many dialog items";
2104 return Qnil;
2107 wv = xmalloc_widget_value ();
2108 prev_wv->next = wv;
2109 wv->name = (char *) button_names[nb_buttons];
2110 if (!NILP (descrip))
2111 wv->key = SSDATA (descrip);
2112 wv->value = SSDATA (item_name);
2113 wv->call_data = (void *) &XVECTOR (menu_items)->contents[i];
2114 wv->enabled = !NILP (enable);
2115 wv->help = Qnil;
2116 prev_wv = wv;
2118 if (! boundary_seen)
2119 left_count++;
2121 nb_buttons++;
2122 i += MENU_ITEMS_ITEM_LENGTH;
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;
2130 wv = xmalloc_widget_value ();
2131 wv->name = dialog_name;
2132 wv->help = Qnil;
2134 /* Frame title: 'Q' = Question, 'I' = Information.
2135 Can also have 'E' = Error if, one day, we want
2136 a popup for errors. */
2137 if (NILP (header))
2138 dialog_name[0] = 'Q';
2139 else
2140 dialog_name[0] = 'I';
2142 /* Dialog boxes use a really stupid name encoding
2143 which specifies how many buttons to use
2144 and how many buttons are on the right. */
2145 dialog_name[1] = '0' + nb_buttons;
2146 dialog_name[2] = 'B';
2147 dialog_name[3] = 'R';
2148 /* Number of buttons to put on the right. */
2149 dialog_name[4] = '0' + nb_buttons - left_count;
2150 dialog_name[5] = 0;
2151 wv->contents = first_wv;
2152 first_wv = wv;
2155 /* No selection has been chosen yet. */
2156 menu_item_selection = 0;
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));
2163 /* Actually create and show the dialog. */
2164 create_and_show_dialog (f, first_wv);
2166 unbind_to (specpdl_count, Qnil);
2168 /* Find the selected item, and its pane, to return
2169 the proper value. */
2170 if (menu_item_selection != 0)
2172 Lisp_Object prefix;
2174 prefix = Qnil;
2175 i = 0;
2176 while (i < menu_items_used)
2178 Lisp_Object entry;
2180 if (EQ (XVECTOR (menu_items)->contents[i], Qt))
2182 prefix
2183 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
2184 i += MENU_ITEMS_PANE_LENGTH;
2186 else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
2188 /* This is the boundary between left-side elts and
2189 right-side elts. */
2190 ++i;
2192 else
2194 entry
2195 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_VALUE];
2196 if (menu_item_selection == &XVECTOR (menu_items)->contents[i])
2198 if (keymaps != 0)
2200 entry = Fcons (entry, Qnil);
2201 if (!NILP (prefix))
2202 entry = Fcons (prefix, entry);
2204 return entry;
2206 i += MENU_ITEMS_ITEM_LENGTH;
2210 else
2211 /* Make "Cancel" equivalent to C-g. */
2212 Fsignal (Qquit, Qnil);
2214 return Qnil;
2217 #else /* not USE_X_TOOLKIT && not USE_GTK */
2219 /* The frame of the last activated non-toolkit menu bar.
2220 Used to generate menu help events. */
2222 static struct frame *menu_help_frame;
2225 /* Show help HELP_STRING, or clear help if HELP_STRING is null.
2227 PANE is the pane number, and ITEM is the menu item number in
2228 the menu (currently not used).
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. */
2234 static void
2235 menu_help_callback (char const *help_string, int pane, int item)
2237 Lisp_Object *first_item;
2238 Lisp_Object pane_name;
2239 Lisp_Object menu_object;
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. */
2246 pane_name = empty_unibyte_string;
2247 else
2248 pane_name = first_item[MENU_ITEMS_ITEM_NAME];
2250 /* (menu-item MENU-NAME PANE-NUMBER) */
2251 menu_object = Fcons (Qmenu_item,
2252 Fcons (pane_name,
2253 Fcons (make_number (pane), Qnil)));
2254 show_help_echo (help_string ? build_string (help_string) : Qnil,
2255 Qnil, menu_object, make_number (item));
2258 static Lisp_Object
2259 pop_down_menu (Lisp_Object arg)
2261 struct Lisp_Save_Value *p1 = XSAVE_VALUE (Fcar (arg));
2262 struct Lisp_Save_Value *p2 = XSAVE_VALUE (Fcdr (arg));
2264 FRAME_PTR f = p1->pointer;
2265 XMenu *menu = p2->pointer;
2267 BLOCK_INPUT;
2268 #ifndef MSDOS
2269 XUngrabPointer (FRAME_X_DISPLAY (f), CurrentTime);
2270 XUngrabKeyboard (FRAME_X_DISPLAY (f), CurrentTime);
2271 #endif
2272 XMenuDestroy (FRAME_X_DISPLAY (f), menu);
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));
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;
2285 #endif /* HAVE_X_WINDOWS */
2287 UNBLOCK_INPUT;
2289 return Qnil;
2293 Lisp_Object
2294 xmenu_show (FRAME_PTR f, int x, int y, int for_click, int keymaps,
2295 Lisp_Object title, const char **error_name, Time timestamp)
2297 Window root;
2298 XMenu *menu;
2299 int pane, selidx, lpane, status;
2300 Lisp_Object entry, pane_prefix;
2301 char *datap;
2302 int ulx, uly, width, height;
2303 int dispwidth, dispheight;
2304 int i, j, lines, maxlines;
2305 int maxwidth;
2306 int dummy_int;
2307 unsigned int dummy_uint;
2308 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
2310 if (! FRAME_X_P (f) && ! FRAME_MSDOS_P (f))
2311 abort ();
2313 *error_name = 0;
2314 if (menu_items_n_panes == 0)
2315 return Qnil;
2317 if (menu_items_used <= MENU_ITEMS_PANE_LENGTH)
2319 *error_name = "Empty menu";
2320 return Qnil;
2323 /* Figure out which root window F is on. */
2324 XGetGeometry (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), &root,
2325 &dummy_int, &dummy_int, &dummy_uint, &dummy_uint,
2326 &dummy_uint, &dummy_uint);
2328 /* Make the menu on that window. */
2329 menu = XMenuCreate (FRAME_X_DISPLAY (f), root, "emacs");
2330 if (menu == NULL)
2332 *error_name = "Can't create menu";
2333 return Qnil;
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 ();
2341 #ifdef HAVE_X_WINDOWS
2342 /* Adjust coordinates to relative to the outer (window manager) window. */
2343 x += FRAME_OUTER_TO_INNER_DIFF_X (f);
2344 y += FRAME_OUTER_TO_INNER_DIFF_Y (f);
2345 #endif /* HAVE_X_WINDOWS */
2347 /* Adjust coordinates to be root-window-relative. */
2348 x += f->left_pos;
2349 y += f->top_pos;
2351 /* Create all the necessary panes and their items. */
2352 maxwidth = maxlines = lines = i = 0;
2353 lpane = XM_FAILURE;
2354 while (i < menu_items_used)
2356 if (EQ (XVECTOR (menu_items)->contents[i], Qt))
2358 /* Create a new pane. */
2359 Lisp_Object pane_name, prefix;
2360 const char *pane_string;
2362 maxlines = max (maxlines, lines);
2363 lines = 0;
2364 pane_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_NAME];
2365 prefix = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
2366 pane_string = (NILP (pane_name)
2367 ? "" : SSDATA (pane_name));
2368 if (keymaps && !NILP (prefix))
2369 pane_string++;
2371 lpane = XMenuAddPane (FRAME_X_DISPLAY (f), menu, pane_string, TRUE);
2372 if (lpane == XM_FAILURE)
2374 XMenuDestroy (FRAME_X_DISPLAY (f), menu);
2375 *error_name = "Can't create pane";
2376 return Qnil;
2378 i += MENU_ITEMS_PANE_LENGTH;
2380 /* Find the width of the widest item in this pane. */
2381 j = i;
2382 while (j < menu_items_used)
2384 Lisp_Object item;
2385 item = XVECTOR (menu_items)->contents[j];
2386 if (EQ (item, Qt))
2387 break;
2388 if (NILP (item))
2390 j++;
2391 continue;
2393 width = SBYTES (item);
2394 if (width > maxwidth)
2395 maxwidth = width;
2397 j += MENU_ITEMS_ITEM_LENGTH;
2400 /* Ignore a nil in the item list.
2401 It's meaningful only for dialog boxes. */
2402 else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
2403 i += 1;
2404 else
2406 /* Create a new item within current pane. */
2407 Lisp_Object item_name, enable, descrip, help;
2408 char *item_data;
2409 char const *help_string;
2411 item_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_NAME];
2412 enable = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_ENABLE];
2413 descrip
2414 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_EQUIV_KEY];
2415 help = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_HELP];
2416 help_string = STRINGP (help) ? SSDATA (help) : NULL;
2418 if (!NILP (descrip))
2420 /* if alloca is fast, use that to make the space,
2421 to reduce gc needs. */
2422 item_data = (char *) alloca (maxwidth + SBYTES (descrip) + 1);
2423 memcpy (item_data, SSDATA (item_name), SBYTES (item_name));
2424 for (j = SCHARS (item_name); j < maxwidth; j++)
2425 item_data[j] = ' ';
2426 memcpy (item_data + j, SSDATA (descrip), SBYTES (descrip));
2427 item_data[j + SBYTES (descrip)] = 0;
2429 else
2430 item_data = SSDATA (item_name);
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))
2438 XMenuDestroy (FRAME_X_DISPLAY (f), menu);
2439 *error_name = "Can't add selection to menu";
2440 return Qnil;
2442 i += MENU_ITEMS_ITEM_LENGTH;
2443 lines++;
2447 maxlines = max (maxlines, lines);
2449 /* All set and ready to fly. */
2450 XMenuRecompute (FRAME_X_DISPLAY (f), menu);
2451 dispwidth = DisplayWidth (FRAME_X_DISPLAY (f), FRAME_X_SCREEN_NUMBER (f));
2452 dispheight = DisplayHeight (FRAME_X_DISPLAY (f), FRAME_X_SCREEN_NUMBER (f));
2453 x = min (x, dispwidth);
2454 y = min (y, dispheight);
2455 x = max (x, 1);
2456 y = max (y, 1);
2457 XMenuLocate (FRAME_X_DISPLAY (f), menu, 0, 0, x, y,
2458 &ulx, &uly, &width, &height);
2459 if (ulx+width > dispwidth)
2461 x -= (ulx + width) - dispwidth;
2462 ulx = dispwidth - width;
2464 if (uly+height > dispheight)
2466 y -= (uly + height) - dispheight;
2467 uly = dispheight - height;
2469 #ifndef HAVE_X_WINDOWS
2470 if (FRAME_HAS_MINIBUF_P (f) && uly+height > dispheight - 1)
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))
2476 y -= WINDOW_TOTAL_LINES (XWINDOW (echo_area_window));
2477 uly -= WINDOW_TOTAL_LINES (XWINDOW (echo_area_window));
2479 else
2481 y--;
2482 uly--;
2485 #endif
2486 if (ulx < 0) x -= ulx;
2487 if (uly < 0) y -= uly;
2489 if (! for_click)
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);
2498 XMenuSetAEQ (menu, TRUE);
2499 XMenuSetFreeze (menu, TRUE);
2500 pane = selidx = 0;
2502 #ifndef MSDOS
2503 XMenuActivateSetWaitFunction (x_menu_wait_for_event, FRAME_X_DISPLAY (f));
2504 #endif
2506 record_unwind_protect (pop_down_menu,
2507 Fcons (make_save_value (f, 0),
2508 make_save_value (menu, 0)));
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;
2513 status = XMenuActivate (FRAME_X_DISPLAY (f), menu, &pane, &selidx,
2514 x, y, ButtonReleaseMask, &datap,
2515 menu_help_callback);
2516 entry = pane_prefix = Qnil;
2518 switch (status)
2520 case XM_SUCCESS:
2521 #ifdef XDEBUG
2522 fprintf (stderr, "pane= %d line = %d\n", panes, selidx);
2523 #endif
2525 /* Find the item number SELIDX in pane number PANE. */
2526 i = 0;
2527 while (i < menu_items_used)
2529 if (EQ (XVECTOR (menu_items)->contents[i], Qt))
2531 if (pane == 0)
2532 pane_prefix
2533 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
2534 pane--;
2535 i += MENU_ITEMS_PANE_LENGTH;
2537 else
2539 if (pane == -1)
2541 if (selidx == 0)
2543 entry
2544 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_VALUE];
2545 if (keymaps != 0)
2547 entry = Fcons (entry, Qnil);
2548 if (!NILP (pane_prefix))
2549 entry = Fcons (pane_prefix, entry);
2551 break;
2553 selidx--;
2555 i += MENU_ITEMS_ITEM_LENGTH;
2558 break;
2560 case XM_FAILURE:
2561 *error_name = "Can't activate menu";
2562 case XM_IA_SELECT:
2563 break;
2564 case XM_NO_SELECT:
2565 /* Make "Cancel" equivalent to C-g unless FOR_CLICK (which means
2566 the menu was invoked with a mouse event as POSITION). */
2567 if (! for_click)
2568 Fsignal (Qquit, Qnil);
2569 break;
2572 unbind_to (specpdl_count, Qnil);
2574 return entry;
2577 #endif /* not USE_X_TOOLKIT */
2579 #endif /* HAVE_MENUS */
2581 #ifndef MSDOS
2582 /* Detect if a dialog or menu has been posted. MSDOS has its own
2583 implementation on msdos.c. */
2586 popup_activated (void)
2588 return popup_activated_flag;
2590 #endif /* not MSDOS */
2592 /* The following is used by delayed window autoselection. */
2594 DEFUN ("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. */)
2596 (void)
2598 #ifdef HAVE_MENUS
2599 return (popup_activated ()) ? Qt : Qnil;
2600 #else
2601 return Qnil;
2602 #endif /* HAVE_MENUS */
2605 void
2606 syms_of_xmenu (void)
2608 DEFSYM (Qdebug_on_next_call, "debug-on-next-call");
2610 #ifdef USE_X_TOOLKIT
2611 widget_id_tick = (1<<16);
2612 next_menubar_widget_id = 1;
2613 #endif
2615 defsubr (&Smenu_or_popup_active_p);
2617 #if defined (USE_GTK) || defined (USE_X_TOOLKIT)
2618 defsubr (&Sx_menu_bar_open_internal);
2619 Ffset (intern_c_string ("accelerate-menu"),
2620 intern_c_string (Sx_menu_bar_open_internal.symbol_name));
2621 #endif
2623 #ifdef HAVE_MENUS
2624 defsubr (&Sx_popup_dialog);
2625 #endif