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