1 /* Pure GTK3 menu and toolbar module.
2 Copyright (C) 2019-2021 Free Software Foundation, Inc.
3
4 This file is part of GNU Emacs.
5
6 GNU Emacs is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or (at
9 your option) any later version.
10
11 GNU Emacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
18
19 /*
20 */
21
22
23 /* This should be the first include, as it may set up #defines affecting
24 interpretation of even the system includes. */
25 #include <config.h>
26
27 #include "lisp.h"
28 #include "frame.h"
29 #include "window.h"
30 #include "character.h"
31 #include "buffer.h"
32 #include "keymap.h"
33 #include "coding.h"
34 #include "commands.h"
35 #include "blockinput.h"
36 #include "termhooks.h"
37 #include "keyboard.h"
38 #include "menu.h"
39 #include "pdumper.h"
40 #include "xgselect.h"
41
42 #include "gtkutil.h"
43 #include <gtk/gtk.h>
44
45 /* Flag which when set indicates a dialog or menu has been posted by
46 Xt on behalf of one of the widget sets. */
47 static int popup_activated_flag;
48
49 /* Set menu_items_inuse so no other popup menu or dialog is created. */
50
51 void
pgtk_menu_set_in_use(bool in_use)52 pgtk_menu_set_in_use (bool in_use)
53 {
54 Lisp_Object frames, frame;
55
56 menu_items_inuse = in_use;
57 popup_activated_flag = in_use;
58
59 /* Don't let frames in `above' z-group obscure popups. */
60 FOR_EACH_FRAME (frames, frame)
61 {
62 struct frame *f = XFRAME (frame);
63
64 if (in_use && FRAME_Z_GROUP_ABOVE (f))
65 x_set_z_group (f, Qabove_suspended, Qabove);
66 else if (!in_use && FRAME_Z_GROUP_ABOVE_SUSPENDED (f))
67 x_set_z_group (f, Qabove, Qabove_suspended);
68 }
69 }
70
71 DEFUN ("x-menu-bar-open-internal", Fx_menu_bar_open_internal, Sx_menu_bar_open_internal, 0, 1, "i",
72 doc: /* Start key navigation of the menu bar in FRAME.
73 This initially opens the first menu bar item and you can then navigate with the
74 arrow keys, select a menu entry with the return key or cancel with the
75 escape key. If FRAME has no menu bar this function does nothing.
76
77 If FRAME is nil or not given, use the selected frame. */)
78 (Lisp_Object frame)
79 {
80 GtkWidget *menubar;
81 struct frame *f;
82
83 block_input ();
84 f = decode_window_system_frame (frame);
85
86 if (FRAME_EXTERNAL_MENU_BAR (f))
87 set_frame_menubar (f, true);
88
89 menubar = FRAME_X_OUTPUT (f)->menubar_widget;
90 if (menubar)
91 {
92 /* Activate the first menu. */
93 GList *children = gtk_container_get_children (GTK_CONTAINER (menubar));
94
95 if (children)
96 {
97 g_signal_emit_by_name (children->data, "activate_item");
98 g_list_free (children);
99 }
100 }
101 unblock_input ();
102
103 return Qnil;
104 }
105
106 /* Loop util popup_activated_flag is set to zero in a callback.
107 Used for popup menus and dialogs. */
108
109 static void
popup_widget_loop(bool do_timers,GtkWidget * widget)110 popup_widget_loop (bool do_timers, GtkWidget *widget)
111 {
112 ++popup_activated_flag;
113
114 /* Process events in the Gtk event loop until done. */
115 while (popup_activated_flag)
116 gtk_main_iteration ();
117 }
118
119 void
pgtk_activate_menubar(struct frame * f)120 pgtk_activate_menubar (struct frame *f)
121 {
122 set_frame_menubar (f, true);
123
124 popup_activated_flag = 1;
125
126 /* f->output_data.pgtk->menubar_active = 1; */
127 }
128
129 /* This callback is invoked when a dialog or menu is finished being
130 used and has been unposted. */
131
132 static void
popup_deactivate_callback(GtkWidget * widget,gpointer client_data)133 popup_deactivate_callback (GtkWidget *widget, gpointer client_data)
134 {
135 popup_activated_flag = 0;
136 }
137
138 /* Function that finds the frame for WIDGET and shows the HELP text
139 for that widget.
140 F is the frame if known, or NULL if not known. */
141 static void
show_help_event(struct frame * f,GtkWidget * widget,Lisp_Object help)142 show_help_event (struct frame *f, GtkWidget *widget, Lisp_Object help)
143 {
144 /* Don't show this tooltip.
145 * Tooltips are always tied to main widget, so stacking order
146 * on Wayland is:
147 * (above)
148 * - menu
149 * - tooltip
150 * - main widget
151 * (below)
152 * This is applicable to tooltips for menu, and menu tooltips
153 * are shown below menus.
154 * As a workaround, I entrust Gtk with menu tooltips, and
155 * let emacs not to show menu tooltips.
156 */
157
158 #if 0
159 Lisp_Object frame;
160
161 if (f)
162 {
163 XSETFRAME (frame, f);
164 kbd_buffer_store_help_event (frame, help);
165 }
166 else
167 show_help_echo (help, Qnil, Qnil, Qnil);
168 #endif
169 }
170
171 /* Callback called when menu items are highlighted/unhighlighted
172 while moving the mouse over them. WIDGET is the menu bar or menu
173 popup widget. ID is its LWLIB_ID. CALL_DATA contains a pointer to
174 the data structure for the menu item, or null in case of
175 unhighlighting. */
176
177 static void
menu_highlight_callback(GtkWidget * widget,gpointer call_data)178 menu_highlight_callback (GtkWidget *widget, gpointer call_data)
179 {
180 xg_menu_item_cb_data *cb_data;
181 Lisp_Object help;
182
183 cb_data = g_object_get_data (G_OBJECT (widget), XG_ITEM_DATA);
184 if (!cb_data)
185 return;
186
187 help = call_data ? cb_data->help : Qnil;
188
189 /* If popup_activated_flag is greater than 1 we are in a popup menu.
190 Don't pass the frame to show_help_event for those.
191 Passing frame creates an Emacs event. As we are looping in
192 popup_widget_loop, it won't be handled. Passing NULL shows the tip
193 directly without using an Emacs event. This is what the Lucid code
194 does below. */
195 show_help_event (popup_activated_flag <= 1 ? cb_data->cl_data->f : NULL,
196 widget, help);
197 }
198
199 /* Gtk calls callbacks just because we tell it what item should be
200 selected in a radio group. If this variable is set to a non-zero
201 value, we are creating menus and don't want callbacks right now.
202 */
203 static bool xg_crazy_callback_abort;
204
205 /* This callback is called from the menu bar pulldown menu
206 when the user makes a selection.
207 Figure out what the user chose
208 and put the appropriate events into the keyboard buffer. */
209 static void
menubar_selection_callback(GtkWidget * widget,gpointer client_data)210 menubar_selection_callback (GtkWidget *widget, gpointer client_data)
211 {
212 xg_menu_item_cb_data *cb_data = client_data;
213
214 if (xg_crazy_callback_abort)
215 return;
216
217 if (!cb_data || !cb_data->cl_data || !cb_data->cl_data->f)
218 return;
219
220 /* For a group of radio buttons, GTK calls the selection callback first
221 for the item that was active before the selection and then for the one that
222 is active after the selection. For C-h k this means we get the help on
223 the deselected item and then the selected item is executed. Prevent that
224 by ignoring the non-active item. */
225 if (GTK_IS_RADIO_MENU_ITEM (widget)
226 && !gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (widget)))
227 return;
228
229 /* When a menu is popped down, X generates a focus event (i.e. focus
230 goes back to the frame below the menu). Since GTK buffers events,
231 we force it out here before the menu selection event. Otherwise
232 sit-for will exit at once if the focus event follows the menu selection
233 event. */
234
235 block_input ();
236 while (gtk_events_pending ())
237 gtk_main_iteration ();
238 unblock_input ();
239
240 find_and_call_menu_selection (cb_data->cl_data->f,
241 cb_data->cl_data->menu_bar_items_used,
242 cb_data->cl_data->menu_bar_vector,
243 cb_data->call_data);
244 }
245
246 /* Recompute all the widgets of frame F, when the menu bar has been
247 changed. */
248
249 static void
update_frame_menubar(struct frame * f)250 update_frame_menubar (struct frame *f)
251 {
252 xg_update_frame_menubar (f);
253 }
254
255 /* Set the contents of the menubar widgets of frame F.
256 The argument FIRST_TIME is currently ignored;
257 it is set the first time this is called, from initialize_frame_menubar. */
258
259 void
set_frame_menubar(struct frame * f,bool deep_p)260 set_frame_menubar (struct frame *f, bool deep_p)
261 {
262 GtkWidget *menubar_widget;
263 Lisp_Object items;
264 widget_value *wv, *first_wv, *prev_wv = 0;
265 int i;
266 int *submenu_start, *submenu_end;
267 bool *submenu_top_level_items;
268 int *submenu_n_panes;
269
270
271 menubar_widget = f->output_data.pgtk->menubar_widget;
272
273 XSETFRAME (Vmenu_updating_frame, f);
274
275 if (!menubar_widget)
276 deep_p = true;
277
278 if (deep_p)
279 {
280 struct buffer *prev = current_buffer;
281 Lisp_Object buffer;
282 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
283 int previous_menu_items_used = f->menu_bar_items_used;
284 Lisp_Object *previous_items
285 = alloca (previous_menu_items_used * sizeof *previous_items);
286 int subitems;
287
288 /* If we are making a new widget, its contents are empty,
289 do always reinitialize them. */
290 if (!menubar_widget)
291 previous_menu_items_used = 0;
292
293 buffer = XWINDOW (FRAME_SELECTED_WINDOW (f))->contents;
294 specbind (Qinhibit_quit, Qt);
295 /* Don't let the debugger step into this code
296 because it is not reentrant. */
297 specbind (Qdebug_on_next_call, Qnil);
298
299 record_unwind_save_match_data ();
300 if (NILP (Voverriding_local_map_menu_flag))
301 {
302 specbind (Qoverriding_terminal_local_map, Qnil);
303 specbind (Qoverriding_local_map, Qnil);
304 }
305
306 set_buffer_internal_1 (XBUFFER (buffer));
307
308 /* Run the Lucid hook. */
309 safe_run_hooks (Qactivate_menubar_hook);
310
311 /* If it has changed current-menubar from previous value,
312 really recompute the menubar from the value. */
313 if (!NILP (Vlucid_menu_bar_dirty_flag))
314 call0 (Qrecompute_lucid_menubar);
315 safe_run_hooks (Qmenu_bar_update_hook);
316 fset_menu_bar_items (f, menu_bar_items (FRAME_MENU_BAR_ITEMS (f)));
317
318 items = FRAME_MENU_BAR_ITEMS (f);
319
320 /* Save the frame's previous menu bar contents data. */
321 if (previous_menu_items_used)
322 memcpy (previous_items, xvector_contents (f->menu_bar_vector),
323 previous_menu_items_used * word_size);
324
325 /* Fill in menu_items with the current menu bar contents.
326 This can evaluate Lisp code. */
327 save_menu_items ();
328
329 menu_items = f->menu_bar_vector;
330 menu_items_allocated = VECTORP (menu_items) ? ASIZE (menu_items) : 0;
331 subitems = ASIZE (items) / 4;
332 submenu_start = alloca ((subitems + 1) * sizeof *submenu_start);
333 submenu_end = alloca (subitems * sizeof *submenu_end);
334 submenu_n_panes = alloca (subitems * sizeof *submenu_n_panes);
335 submenu_top_level_items = alloca (subitems
336 * sizeof *submenu_top_level_items);
337 init_menu_items ();
338 for (i = 0; i < subitems; i++)
339 {
340 Lisp_Object key, string, maps;
341
342 key = AREF (items, 4 * i);
343 string = AREF (items, 4 * i + 1);
344 maps = AREF (items, 4 * i + 2);
345 if (NILP (string))
346 break;
347
348 submenu_start[i] = menu_items_used;
349
350 menu_items_n_panes = 0;
351 submenu_top_level_items[i]
352 = parse_single_submenu (key, string, maps);
353 submenu_n_panes[i] = menu_items_n_panes;
354
355 submenu_end[i] = menu_items_used;
356 }
357
358 submenu_start[i] = -1;
359 finish_menu_items ();
360
361 /* Convert menu_items into widget_value trees
362 to display the menu. This cannot evaluate Lisp code. */
363
364 wv = make_widget_value ("menubar", NULL, true, Qnil);
365 wv->button_type = BUTTON_TYPE_NONE;
366 first_wv = wv;
367
368 for (i = 0; submenu_start[i] >= 0; i++)
369 {
370 menu_items_n_panes = submenu_n_panes[i];
371 wv = digest_single_submenu (submenu_start[i], submenu_end[i],
372 submenu_top_level_items[i]);
373 if (prev_wv)
374 prev_wv->next = wv;
375 else
376 first_wv->contents = wv;
377 /* Don't set wv->name here; GC during the loop might relocate it. */
378 wv->enabled = true;
379 wv->button_type = BUTTON_TYPE_NONE;
380 prev_wv = wv;
381 }
382
383 set_buffer_internal_1 (prev);
384
385 /* If there has been no change in the Lisp-level contents
386 of the menu bar, skip redisplaying it. Just exit. */
387
388 /* Compare the new menu items with the ones computed last time. */
389 for (i = 0; i < previous_menu_items_used; i++)
390 if (menu_items_used == i
391 || (!EQ (previous_items[i], AREF (menu_items, i))))
392 break;
393 if (i == menu_items_used && i == previous_menu_items_used && i != 0)
394 {
395 /* The menu items have not changed. Don't bother updating
396 the menus in any form, since it would be a no-op. */
397 free_menubar_widget_value_tree (first_wv);
398 discard_menu_items ();
399 unbind_to (specpdl_count, Qnil);
400 return;
401 }
402
403 /* The menu items are different, so store them in the frame. */
404 fset_menu_bar_vector (f, menu_items);
405 f->menu_bar_items_used = menu_items_used;
406
407 /* This undoes save_menu_items. */
408 unbind_to (specpdl_count, Qnil);
409
410 /* Now GC cannot happen during the lifetime of the widget_value,
411 so it's safe to store data from a Lisp_String. */
412 wv = first_wv->contents;
413 for (i = 0; i < ASIZE (items); i += 4)
414 {
415 Lisp_Object string;
416 string = AREF (items, i + 1);
417 if (NILP (string))
418 break;
419 wv->name = SSDATA (string);
420 update_submenu_strings (wv->contents);
421 wv = wv->next;
422 }
423
424 }
425 else
426 {
427 /* Make a widget-value tree containing
428 just the top level menu bar strings. */
429
430 wv = make_widget_value ("menubar", NULL, true, Qnil);
431 wv->button_type = BUTTON_TYPE_NONE;
432 first_wv = wv;
433
434 items = FRAME_MENU_BAR_ITEMS (f);
435 for (i = 0; i < ASIZE (items); i += 4)
436 {
437 Lisp_Object string;
438
439 string = AREF (items, i + 1);
440 if (NILP (string))
441 break;
442
443 wv = make_widget_value (SSDATA (string), NULL, true, Qnil);
444 wv->button_type = BUTTON_TYPE_NONE;
445 /* This prevents lwlib from assuming this
446 menu item is really supposed to be empty. */
447 /* The intptr_t cast avoids a warning.
448 This value just has to be different from small integers. */
449 wv->call_data = (void *) (intptr_t) (-1);
450
451 if (prev_wv)
452 prev_wv->next = wv;
453 else
454 first_wv->contents = wv;
455 prev_wv = wv;
456 }
457
458 /* Forget what we thought we knew about what is in the
459 detailed contents of the menu bar menus.
460 Changing the top level always destroys the contents. */
461 f->menu_bar_items_used = 0;
462 }
463
464 block_input ();
465
466 xg_crazy_callback_abort = true;
467 if (menubar_widget)
468 {
469 /* The fourth arg is DEEP_P, which says to consider the entire
470 menu trees we supply, rather than just the menu bar item names. */
471 xg_modify_menubar_widgets (menubar_widget,
472 f,
473 first_wv,
474 deep_p,
475 G_CALLBACK (menubar_selection_callback),
476 G_CALLBACK (popup_deactivate_callback),
477 G_CALLBACK (menu_highlight_callback));
478 }
479 else
480 {
481 menubar_widget
482 = xg_create_widget ("menubar", "menubar", f, first_wv,
483 G_CALLBACK (menubar_selection_callback),
484 G_CALLBACK (popup_deactivate_callback),
485 G_CALLBACK (menu_highlight_callback));
486
487 f->output_data.pgtk->menubar_widget = menubar_widget;
488 }
489
490 free_menubar_widget_value_tree (first_wv);
491 update_frame_menubar (f);
492
493 xg_crazy_callback_abort = false;
494
495 unblock_input ();
496 }
497
498 /* Called from Fx_create_frame to create the initial menubar of a frame
499 before it is mapped, so that the window is mapped with the menubar already
500 there instead of us tacking it on later and thrashing the window after it
501 is visible. */
502
503 void
initialize_frame_menubar(struct frame * f)504 initialize_frame_menubar (struct frame *f)
505 {
506 /* This function is called before the first chance to redisplay
507 the frame. It has to be, so the frame will have the right size. */
508 fset_menu_bar_items (f, menu_bar_items (FRAME_MENU_BAR_ITEMS (f)));
509 set_frame_menubar (f, true);
510 }
511
512
513 /* x_menu_show actually displays a menu using the panes and items in menu_items
514 and returns the value selected from it.
515 There are two versions of x_menu_show, one for Xt and one for Xlib.
516 Both assume input is blocked by the caller. */
517
518 /* F is the frame the menu is for.
519 X and Y are the frame-relative specified position,
520 relative to the inside upper left corner of the frame F.
521 Bitfield MENUFLAGS bits are:
522 MENU_FOR_CLICK is set if this menu was invoked for a mouse click.
523 MENU_KEYMAPS is set if this menu was specified with keymaps;
524 in that case, we return a list containing the chosen item's value
525 and perhaps also the pane's prefix.
526 TITLE is the specified menu title.
527 ERROR is a place to store an error message string in case of failure.
528 (We return nil on failure, but the value doesn't actually matter.) */
529
530 /* The item selected in the popup menu. */
531 static Lisp_Object *volatile menu_item_selection;
532
533 static void
popup_selection_callback(GtkWidget * widget,gpointer client_data)534 popup_selection_callback (GtkWidget *widget, gpointer client_data)
535 {
536 xg_menu_item_cb_data *cb_data = client_data;
537
538 if (xg_crazy_callback_abort)
539 return;
540 if (cb_data)
541 menu_item_selection = cb_data->call_data;
542 }
543
544 static void
pop_down_menu(void * arg)545 pop_down_menu (void *arg)
546 {
547 popup_activated_flag = 0;
548 block_input ();
549 gtk_widget_destroy (GTK_WIDGET (arg));
550 unblock_input ();
551 }
552
553 /* Pop up the menu for frame F defined by FIRST_WV at X/Y and loop until the
554 menu pops down.
555 menu_item_selection will be set to the selection. */
556 static void
create_and_show_popup_menu(struct frame * f,widget_value * first_wv,int x,int y,bool for_click)557 create_and_show_popup_menu (struct frame *f, widget_value * first_wv,
558 int x, int y, bool for_click)
559 {
560 GtkWidget *menu;
561 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
562
563 eassert (FRAME_PGTK_P (f));
564
565 xg_crazy_callback_abort = true;
566 menu = xg_create_widget ("popup", first_wv->name, f, first_wv,
567 G_CALLBACK (popup_selection_callback),
568 G_CALLBACK (popup_deactivate_callback),
569 G_CALLBACK (menu_highlight_callback));
570 xg_crazy_callback_abort = false;
571
572 /* Display the menu. */
573 gtk_widget_show_all (menu);
574
575 if (for_click)
576 gtk_menu_popup_at_pointer (GTK_MENU (menu),
577 FRAME_DISPLAY_INFO (f)->last_click_event);
578 else
579 {
580 GdkRectangle rect;
581 rect.x = x;
582 rect.y = y;
583 rect.width = 1;
584 rect.height = 1;
585 gtk_menu_popup_at_rect (GTK_MENU (menu),
586 gtk_widget_get_window (FRAME_GTK_WIDGET (f)),
587 &rect,
588 GDK_GRAVITY_NORTH_WEST, GDK_GRAVITY_NORTH_WEST,
589 FRAME_DISPLAY_INFO (f)->last_click_event);
590 }
591
592 record_unwind_protect_ptr (pop_down_menu, menu);
593
594 if (gtk_widget_get_mapped (menu))
595 {
596 /* Set this to one. popup_widget_loop increases it by one, so it becomes
597 two. show_help_echo uses this to detect popup menus. */
598 popup_activated_flag = 1;
599 /* Process events that apply to the menu. */
600 popup_widget_loop (true, menu);
601 }
602
603 unbind_to (specpdl_count, Qnil);
604
605 /* Must reset this manually because the button release event is not passed
606 to Emacs event loop. */
607 FRAME_DISPLAY_INFO (f)->grabbed = 0;
608 }
609
610 static void
cleanup_widget_value_tree(void * arg)611 cleanup_widget_value_tree (void *arg)
612 {
613 free_menubar_widget_value_tree (arg);
614 }
615
616 Lisp_Object
pgtk_menu_show(struct frame * f,int x,int y,int menuflags,Lisp_Object title,const char ** error_name)617 pgtk_menu_show (struct frame *f, int x, int y, int menuflags,
618 Lisp_Object title, const char **error_name)
619 {
620 int i;
621 widget_value *wv, *save_wv = 0, *first_wv = 0, *prev_wv = 0;
622 widget_value **submenu_stack
623 = alloca (menu_items_used * sizeof *submenu_stack);
624 Lisp_Object *subprefix_stack
625 = alloca (menu_items_used * sizeof *subprefix_stack);
626 int submenu_depth = 0;
627
628 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
629
630 eassert (FRAME_PGTK_P (f));
631
632 *error_name = NULL;
633
634 if (!FRAME_GTK_OUTER_WIDGET (f)) {
635 *error_name = "Can't popup from child frames.";
636 return Qnil;
637 }
638
639 if (menu_items_used <= MENU_ITEMS_PANE_LENGTH)
640 {
641 *error_name = "Empty menu";
642 return Qnil;
643 }
644
645 block_input ();
646
647 /* Create a tree of widget_value objects
648 representing the panes and their items. */
649 wv = make_widget_value ("menu", NULL, true, Qnil);
650 wv->button_type = BUTTON_TYPE_NONE;
651 first_wv = wv;
652 bool first_pane = true;
653
654 /* Loop over all panes and items, filling in the tree. */
655 i = 0;
656 while (i < menu_items_used)
657 {
658 if (NILP (AREF (menu_items, i)))
659 {
660 submenu_stack[submenu_depth++] = save_wv;
661 save_wv = prev_wv;
662 prev_wv = 0;
663 first_pane = true;
664 i++;
665 }
666 else if (EQ (AREF (menu_items, i), Qlambda))
667 {
668 prev_wv = save_wv;
669 save_wv = submenu_stack[--submenu_depth];
670 first_pane = false;
671 i++;
672 }
673 else if (EQ (AREF (menu_items, i), Qt) && submenu_depth != 0)
674 i += MENU_ITEMS_PANE_LENGTH;
675 /* Ignore a nil in the item list.
676 It's meaningful only for dialog boxes. */
677 else if (EQ (AREF (menu_items, i), Qquote))
678 i += 1;
679 else if (EQ (AREF (menu_items, i), Qt))
680 {
681 /* Create a new pane. */
682 Lisp_Object pane_name, prefix;
683 const char *pane_string;
684
685 pane_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME);
686 prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
687
688 #ifndef HAVE_MULTILINGUAL_MENU
689 if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name))
690 {
691 pane_name = ENCODE_MENU_STRING (pane_name);
692 ASET (menu_items, i + MENU_ITEMS_PANE_NAME, pane_name);
693 }
694 #endif
695 pane_string = (NILP (pane_name) ? "" : SSDATA (pane_name));
696 /* If there is just one top-level pane, put all its items directly
697 under the top-level menu. */
698 if (menu_items_n_panes == 1)
699 pane_string = "";
700
701 /* If the pane has a meaningful name,
702 make the pane a top-level menu item
703 with its items as a submenu beneath it. */
704 if (!(menuflags & MENU_KEYMAPS) && strcmp (pane_string, ""))
705 {
706 wv = make_widget_value (pane_string, NULL, true, Qnil);
707 if (save_wv)
708 save_wv->next = wv;
709 else
710 first_wv->contents = wv;
711 if ((menuflags & MENU_KEYMAPS) && !NILP (prefix))
712 wv->name++;
713 wv->button_type = BUTTON_TYPE_NONE;
714 save_wv = wv;
715 prev_wv = 0;
716 }
717 else if (first_pane)
718 {
719 save_wv = wv;
720 prev_wv = 0;
721 }
722 first_pane = false;
723 i += MENU_ITEMS_PANE_LENGTH;
724 }
725 else
726 {
727 /* Create a new item within current pane. */
728 Lisp_Object item_name, enable, descrip, def, type, selected, help;
729 item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
730 enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
731 descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY);
732 def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION);
733 type = AREF (menu_items, i + MENU_ITEMS_ITEM_TYPE);
734 selected = AREF (menu_items, i + MENU_ITEMS_ITEM_SELECTED);
735 help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP);
736
737 #ifndef HAVE_MULTILINGUAL_MENU
738 if (STRINGP (item_name) && STRING_MULTIBYTE (item_name))
739 {
740 item_name = ENCODE_MENU_STRING (item_name);
741 ASET (menu_items, i + MENU_ITEMS_ITEM_NAME, item_name);
742 }
743
744 if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
745 {
746 descrip = ENCODE_MENU_STRING (descrip);
747 ASET (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY, descrip);
748 }
749 #endif /* not HAVE_MULTILINGUAL_MENU */
750
751 wv = make_widget_value (SSDATA (item_name), NULL, !NILP (enable),
752 STRINGP (help) ? help : Qnil);
753 if (prev_wv)
754 prev_wv->next = wv;
755 else
756 save_wv->contents = wv;
757 if (!NILP (descrip))
758 wv->key = SSDATA (descrip);
759 /* If this item has a null value,
760 make the call_data null so that it won't display a box
761 when the mouse is on it. */
762 wv->call_data = !NILP (def) ? aref_addr (menu_items, i) : 0;
763
764 if (NILP (type))
765 wv->button_type = BUTTON_TYPE_NONE;
766 else if (EQ (type, QCtoggle))
767 wv->button_type = BUTTON_TYPE_TOGGLE;
768 else if (EQ (type, QCradio))
769 wv->button_type = BUTTON_TYPE_RADIO;
770 else
771 emacs_abort ();
772
773 wv->selected = !NILP (selected);
774
775 prev_wv = wv;
776
777 i += MENU_ITEMS_ITEM_LENGTH;
778 }
779 }
780
781 /* Deal with the title, if it is non-nil. */
782 if (!NILP (title))
783 {
784 widget_value *wv_title;
785 widget_value *wv_sep1 = make_widget_value ("--", NULL, false, Qnil);
786 widget_value *wv_sep2 = make_widget_value ("--", NULL, false, Qnil);
787
788 wv_sep2->next = first_wv->contents;
789 wv_sep1->next = wv_sep2;
790
791 #ifndef HAVE_MULTILINGUAL_MENU
792 if (STRING_MULTIBYTE (title))
793 title = ENCODE_MENU_STRING (title);
794 #endif
795
796 wv_title = make_widget_value (SSDATA (title), NULL, true, Qnil);
797 wv_title->button_type = BUTTON_TYPE_NONE;
798 wv_title->next = wv_sep1;
799 first_wv->contents = wv_title;
800 }
801
802 /* No selection has been chosen yet. */
803 menu_item_selection = 0;
804
805 /* Make sure to free the widget_value objects we used to specify the
806 contents even with longjmp. */
807 record_unwind_protect_ptr (cleanup_widget_value_tree, first_wv);
808
809 /* Actually create and show the menu until popped down. */
810 create_and_show_popup_menu (f, first_wv, x, y, menuflags & MENU_FOR_CLICK);
811
812 unbind_to (specpdl_count, Qnil);
813
814 /* Find the selected item, and its pane, to return
815 the proper value. */
816 if (menu_item_selection != 0)
817 {
818 Lisp_Object prefix, entry;
819
820 prefix = entry = Qnil;
821 i = 0;
822 while (i < menu_items_used)
823 {
824 if (NILP (AREF (menu_items, i)))
825 {
826 subprefix_stack[submenu_depth++] = prefix;
827 prefix = entry;
828 i++;
829 }
830 else if (EQ (AREF (menu_items, i), Qlambda))
831 {
832 prefix = subprefix_stack[--submenu_depth];
833 i++;
834 }
835 else if (EQ (AREF (menu_items, i), Qt))
836 {
837 prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
838 i += MENU_ITEMS_PANE_LENGTH;
839 }
840 /* Ignore a nil in the item list.
841 It's meaningful only for dialog boxes. */
842 else if (EQ (AREF (menu_items, i), Qquote))
843 i += 1;
844 else
845 {
846 entry = AREF (menu_items, i + MENU_ITEMS_ITEM_VALUE);
847 if (menu_item_selection == aref_addr (menu_items, i))
848 {
849 if (menuflags & MENU_KEYMAPS)
850 {
851 int j;
852
853 entry = list1 (entry);
854 if (!NILP (prefix))
855 entry = Fcons (prefix, entry);
856 for (j = submenu_depth - 1; j >= 0; j--)
857 if (!NILP (subprefix_stack[j]))
858 entry = Fcons (subprefix_stack[j], entry);
859 }
860 unblock_input ();
861 return entry;
862 }
863 i += MENU_ITEMS_ITEM_LENGTH;
864 }
865 }
866 }
867 else if (!(menuflags & MENU_FOR_CLICK))
868 {
869 unblock_input ();
870 /* Make "Cancel" equivalent to C-g. */
871 quit ();
872 }
873
874 unblock_input ();
875 return Qnil;
876 }
877
878 static void
dialog_selection_callback(GtkWidget * widget,gpointer client_data)879 dialog_selection_callback (GtkWidget *widget, gpointer client_data)
880 {
881 /* Treat the pointer as an integer. There's no problem
882 as long as pointers have enough bits to hold small integers. */
883 if ((intptr_t) client_data != -1)
884 menu_item_selection = client_data;
885
886 popup_activated_flag = 0;
887 }
888
889 /* Pop up the dialog for frame F defined by FIRST_WV and loop until the
890 dialog pops down.
891 menu_item_selection will be set to the selection. */
892 static void
create_and_show_dialog(struct frame * f,widget_value * first_wv)893 create_and_show_dialog (struct frame *f, widget_value *first_wv)
894 {
895 GtkWidget *menu;
896
897 eassert (FRAME_PGTK_P (f));
898
899 menu = xg_create_widget ("dialog", first_wv->name, f, first_wv,
900 G_CALLBACK (dialog_selection_callback),
901 G_CALLBACK (popup_deactivate_callback), 0);
902
903 if (menu)
904 {
905 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
906 record_unwind_protect_ptr (pop_down_menu, menu);
907
908 /* Display the menu. */
909 gtk_widget_show_all (menu);
910
911 /* Process events that apply to the menu. */
912 popup_widget_loop (true, menu);
913
914 unbind_to (specpdl_count, Qnil);
915 }
916 }
917
918 static const char *button_names[] = {
919 "button1", "button2", "button3", "button4", "button5",
920 "button6", "button7", "button8", "button9", "button10"
921 };
922
923 Lisp_Object
pgtk_dialog_show(struct frame * f,Lisp_Object title,Lisp_Object header,const char ** error_name)924 pgtk_dialog_show (struct frame *f, Lisp_Object title,
925 Lisp_Object header, const char **error_name)
926 {
927 int i, nb_buttons = 0;
928 char dialog_name[6];
929
930 widget_value *wv, *first_wv = 0, *prev_wv = 0;
931
932 /* Number of elements seen so far, before boundary. */
933 int left_count = 0;
934 /* Whether we've seen the boundary between left-hand elts and right-hand. */
935 bool boundary_seen = false;
936
937 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
938
939 eassert (FRAME_PGTK_P (f));
940
941 *error_name = NULL;
942
943 if (!FRAME_GTK_OUTER_WIDGET (f)) {
944 *error_name = "Can't popup from child frames.";
945 return Qnil;
946 }
947
948 if (menu_items_n_panes > 1)
949 {
950 *error_name = "Multiple panes in dialog box";
951 return Qnil;
952 }
953
954 /* Create a tree of widget_value objects
955 representing the text label and buttons. */
956 {
957 Lisp_Object pane_name;
958 const char *pane_string;
959 pane_name = AREF (menu_items, MENU_ITEMS_PANE_NAME);
960 pane_string = (NILP (pane_name) ? "" : SSDATA (pane_name));
961 prev_wv = make_widget_value ("message", (char *) pane_string, true, Qnil);
962 first_wv = prev_wv;
963
964 /* Loop over all panes and items, filling in the tree. */
965 i = MENU_ITEMS_PANE_LENGTH;
966 while (i < menu_items_used)
967 {
968
969 /* Create a new item within current pane. */
970 Lisp_Object item_name, enable, descrip;
971 item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
972 enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
973 descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY);
974
975 if (NILP (item_name))
976 {
977 free_menubar_widget_value_tree (first_wv);
978 *error_name = "Submenu in dialog items";
979 return Qnil;
980 }
981 if (EQ (item_name, Qquote))
982 {
983 /* This is the boundary between left-side elts
984 and right-side elts. Stop incrementing right_count. */
985 boundary_seen = true;
986 i++;
987 continue;
988 }
989 if (nb_buttons >= 9)
990 {
991 free_menubar_widget_value_tree (first_wv);
992 *error_name = "Too many dialog items";
993 return Qnil;
994 }
995
996 wv = make_widget_value (button_names[nb_buttons],
997 SSDATA (item_name), !NILP (enable), Qnil);
998 prev_wv->next = wv;
999 if (!NILP (descrip))
1000 wv->key = SSDATA (descrip);
1001 wv->call_data = aref_addr (menu_items, i);
1002 prev_wv = wv;
1003
1004 if (!boundary_seen)
1005 left_count++;
1006
1007 nb_buttons++;
1008 i += MENU_ITEMS_ITEM_LENGTH;
1009 }
1010
1011 /* If the boundary was not specified,
1012 by default put half on the left and half on the right. */
1013 if (!boundary_seen)
1014 left_count = nb_buttons - nb_buttons / 2;
1015
1016 wv = make_widget_value (dialog_name, NULL, false, Qnil);
1017
1018 /* Frame title: 'Q' = Question, 'I' = Information.
1019 Can also have 'E' = Error if, one day, we want
1020 a popup for errors. */
1021 if (NILP (header))
1022 dialog_name[0] = 'Q';
1023 else
1024 dialog_name[0] = 'I';
1025
1026 /* Dialog boxes use a really stupid name encoding
1027 which specifies how many buttons to use
1028 and how many buttons are on the right. */
1029 dialog_name[1] = '0' + nb_buttons;
1030 dialog_name[2] = 'B';
1031 dialog_name[3] = 'R';
1032 /* Number of buttons to put on the right. */
1033 dialog_name[4] = '0' + nb_buttons - left_count;
1034 dialog_name[5] = 0;
1035 wv->contents = first_wv;
1036 first_wv = wv;
1037 }
1038
1039 /* No selection has been chosen yet. */
1040 menu_item_selection = 0;
1041
1042 /* Make sure to free the widget_value objects we used to specify the
1043 contents even with longjmp. */
1044 record_unwind_protect_ptr (cleanup_widget_value_tree, first_wv);
1045
1046 /* Actually create and show the dialog. */
1047 create_and_show_dialog (f, first_wv);
1048
1049 unbind_to (specpdl_count, Qnil);
1050
1051 /* Find the selected item, and its pane, to return
1052 the proper value. */
1053 if (menu_item_selection != 0)
1054 {
1055 i = 0;
1056 while (i < menu_items_used)
1057 {
1058 Lisp_Object entry;
1059
1060 if (EQ (AREF (menu_items, i), Qt))
1061 i += MENU_ITEMS_PANE_LENGTH;
1062 else if (EQ (AREF (menu_items, i), Qquote))
1063 {
1064 /* This is the boundary between left-side elts and
1065 right-side elts. */
1066 ++i;
1067 }
1068 else
1069 {
1070 entry = AREF (menu_items, i + MENU_ITEMS_ITEM_VALUE);
1071 if (menu_item_selection == aref_addr (menu_items, i))
1072 return entry;
1073 i += MENU_ITEMS_ITEM_LENGTH;
1074 }
1075 }
1076 }
1077 else
1078 /* Make "Cancel" equivalent to C-g. */
1079 quit ();
1080
1081 return Qnil;
1082 }
1083
1084 Lisp_Object
pgtk_popup_dialog(struct frame * f,Lisp_Object header,Lisp_Object contents)1085 pgtk_popup_dialog (struct frame *f, Lisp_Object header, Lisp_Object contents)
1086 {
1087 Lisp_Object title;
1088 const char *error_name;
1089 Lisp_Object selection;
1090 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
1091
1092 check_window_system (f);
1093
1094 /* Decode the dialog items from what was specified. */
1095 title = Fcar (contents);
1096 CHECK_STRING (title);
1097 record_unwind_protect_void (unuse_menu_items);
1098
1099 if (NILP (Fcar (Fcdr (contents))))
1100 /* No buttons specified, add an "Ok" button so users can pop down
1101 the dialog. Also, the lesstif/motif version crashes if there are
1102 no buttons. */
1103 contents = list2 (title, Fcons (build_string ("Ok"), Qt));
1104
1105 list_of_panes (list1 (contents));
1106
1107 /* Display them in a dialog box. */
1108 block_input ();
1109 selection = pgtk_dialog_show (f, title, header, &error_name);
1110 unblock_input ();
1111
1112 unbind_to (specpdl_count, Qnil);
1113 discard_menu_items ();
1114
1115 if (error_name)
1116 error ("%s", error_name);
1117 return selection;
1118 }
1119
1120 /* Detect if a dialog or menu has been posted. MSDOS has its own
1121 implementation on msdos.c. */
1122
1123 int
popup_activated(void)1124 popup_activated (void)
1125 {
1126 return popup_activated_flag;
1127 }
1128
1129 /* The following is used by delayed window autoselection. */
1130
1131 DEFUN ("menu-or-popup-active-p", Fmenu_or_popup_active_p, Smenu_or_popup_active_p, 0, 0, 0,
1132 doc: /* Return t if a menu or popup dialog is active.
1133 \(On MS Windows, this refers to the selected frame.) */)
1134 (void)
1135 {
1136 return (popup_activated ())? Qt : Qnil;
1137 }
1138
1139 static void syms_of_pgtkmenu_for_pdumper (void);
1140
1141 void
syms_of_pgtkmenu(void)1142 syms_of_pgtkmenu (void)
1143 {
1144 DEFSYM (Qdebug_on_next_call, "debug-on-next-call");
1145 defsubr (&Smenu_or_popup_active_p);
1146
1147 DEFSYM (Qframe_monitor_workarea, "frame-monitor-workarea");
1148
1149 defsubr (&Sx_menu_bar_open_internal);
1150 Ffset (intern_c_string ("accelerate-menu"),
1151 intern_c_string (Sx_menu_bar_open_internal.s.symbol_name));
1152
1153 pdumper_do_now_and_after_load (syms_of_pgtkmenu_for_pdumper);
1154 }
1155
1156 static void
syms_of_pgtkmenu_for_pdumper(void)1157 syms_of_pgtkmenu_for_pdumper (void)
1158 {
1159 }
1160