root/src/pgtkmenu.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. pgtk_menu_set_in_use
  2. DEFUN
  3. popup_widget_loop
  4. pgtk_activate_menubar
  5. popup_deactivate_callback
  6. show_help_event
  7. menu_highlight_callback
  8. menubar_selection_callback
  9. update_frame_menubar
  10. set_frame_menubar
  11. initialize_frame_menubar
  12. popup_selection_callback
  13. pop_down_menu
  14. create_and_show_popup_menu
  15. cleanup_widget_value_tree
  16. pgtk_menu_show
  17. dialog_selection_callback
  18. create_and_show_dialog
  19. pgtk_dialog_show
  20. pgtk_popup_dialog
  21. popup_activated
  22. DEFUN
  23. syms_of_pgtkmenu
  24. syms_of_pgtkmenu_for_pdumper

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

/* [<][>][^][v][top][bottom][index][help] */