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