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