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