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