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