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