[gtk/wip/matthiasc/context-menu: 4/6] textview: Redo context menus



commit 711e62fb3e318dde6da6364e37656a01212cb322
Author: Matthias Clasen <mclasen redhat com>
Date:   Mon Jan 28 16:47:53 2019 -0500

    textview: Redo context menus
    
    Drop the ::populate-popup signal and add
    gtk_text_view_get_default_context_menu(), as was done
    in GtkEntry and GtkLabel.

 gtk/gtktextview.c | 541 ++++++++++++++++++++++++++++++------------------------
 gtk/gtktextview.h |   7 +-
 2 files changed, 307 insertions(+), 241 deletions(-)
---
diff --git a/gtk/gtktextview.c b/gtk/gtktextview.c
index bbdf7efb68..8efc6d8f79 100644
--- a/gtk/gtktextview.c
+++ b/gtk/gtktextview.c
@@ -178,6 +178,8 @@ struct _GtkTextViewPrivate
   GtkAdjustment *hadjustment;
   GtkAdjustment *vadjustment;
 
+  GActionMap *context_actions;
+
   /* X offset between widget coordinates and buffer coordinates
    * taking left_padding in account
    */
@@ -271,7 +273,6 @@ struct _GtkTextViewPrivate
   guint vscroll_policy : 1;
   guint cursor_handle_dragged : 1;
   guint selection_handle_dragged : 1;
-  guint populate_all   : 1;
 };
 
 struct _GtkTextPendingScroll
@@ -292,7 +293,6 @@ typedef enum
 
 enum
 {
-  POPULATE_POPUP,
   MOVE_CURSOR,
   PAGE_HORIZONTALLY,
   SET_ANCHOR,
@@ -338,7 +338,6 @@ enum
   PROP_VSCROLL_POLICY,
   PROP_INPUT_PURPOSE,
   PROP_INPUT_HINTS,
-  PROP_POPULATE_ALL,
   PROP_MONOSPACE
 };
 
@@ -591,6 +590,10 @@ static void extend_selection (GtkTextView          *text_view,
                               GtkTextIter          *start,
                               GtkTextIter          *end);
 
+static void gtk_text_view_add_context_actions      (GtkTextView *text_view);
+static void gtk_text_view_update_clipboard_actions (GtkTextView *text_view);
+static void gtk_text_view_update_emoji_action      (GtkTextView *text_view);
+
 
 
 /* FIXME probably need the focus methods. */
@@ -954,22 +957,9 @@ gtk_text_view_class_init (GtkTextViewClass *klass)
                                                        GTK_INPUT_HINT_NONE,
                                                        GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
 
-  /**
-   * GtkTextView:populate-all:
-   *
-   * If :populate-all is %TRUE, the #GtkTextView::populate-popup
-   * signal is also emitted for touch popups.
-   */
-  g_object_class_install_property (gobject_class,
-                                   PROP_POPULATE_ALL,
-                                   g_param_spec_boolean ("populate-all",
-                                                         P_("Populate all"),
-                                                         P_("Whether to emit ::populate-popup for touch 
popups"),
-                                                         FALSE,
-                                                         GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
 
   /**
-   * GtkTextview:monospace:
+   * GtkTextView:monospace:
    *
    * If %TRUE, set the %GTK_STYLE_CLASS_MONOSPACE style class on the
    * text view to indicate that a monospace font is desired.
@@ -1231,36 +1221,6 @@ gtk_text_view_class_init (GtkTextViewClass *klass)
                  NULL,
                  G_TYPE_NONE, 0);
 
-  /**
-   * GtkTextView::populate-popup:
-   * @text_view: The text view on which the signal is emitted
-   * @popup: the container that is being populated
-   *
-   * The ::populate-popup signal gets emitted before showing the
-   * context menu of the text view.
-   *
-   * If you need to add items to the context menu, connect
-   * to this signal and append your items to the @popup, which
-   * will be a #GtkMenu in this case.
-   *
-   * If #GtkTextView:populate-all is %TRUE, this signal will
-   * also be emitted to populate touch popups. In this case,
-   * @popup will be a different container, e.g. a #GtkToolbar.
-   *
-   * The signal handler should not make assumptions about the
-   * type of @widget, but check whether @popup is a #GtkMenu
-   * or #GtkToolbar or another kind of container.
-   */
-  signals[POPULATE_POPUP] =
-    g_signal_new (I_("populate-popup"),
-                 G_OBJECT_CLASS_TYPE (gobject_class),
-                 G_SIGNAL_RUN_LAST,
-                 G_STRUCT_OFFSET (GtkTextViewClass, populate_popup),
-                 NULL, NULL,
-                 NULL,
-                 G_TYPE_NONE, 1,
-                 GTK_TYPE_WIDGET);
-  
   /**
    * GtkTextView::select-all:
    * @text_view: the object which received the signal
@@ -1709,6 +1669,8 @@ gtk_text_view_init (GtkTextView *text_view)
                           gtk_css_node_get_state (priv->text_window->css_node) & 
~GTK_STATE_FLAG_DROP_ACTIVE);
   gtk_css_node_set_visible (priv->selection_node, FALSE);
   g_object_unref (priv->selection_node);
+
+  gtk_text_view_add_context_actions (text_view);
 }
 
 GtkCssNode *
@@ -3752,13 +3714,6 @@ gtk_text_view_set_property (GObject         *object,
       gtk_text_view_set_input_hints (text_view, g_value_get_flags (value));
       break;
 
-    case PROP_POPULATE_ALL:
-      if (text_view->priv->populate_all != g_value_get_boolean (value))
-        {
-          text_view->priv->populate_all = g_value_get_boolean (value);
-          g_object_notify_by_pspec (object, pspec);
-        }
-      break;
     case PROP_MONOSPACE:
       gtk_text_view_set_monospace (text_view, g_value_get_boolean (value));
       break;
@@ -3875,10 +3830,6 @@ gtk_text_view_get_property (GObject         *object,
       g_value_set_flags (value, gtk_text_view_get_input_hints (text_view));
       break;
 
-    case PROP_POPULATE_ALL:
-      g_value_set_boolean (value, priv->populate_all);
-      break;
-
     case PROP_MONOSPACE:
       g_value_set_boolean (value, gtk_text_view_get_monospace (text_view));
       break;
@@ -8439,35 +8390,40 @@ gtk_text_view_set_virtual_cursor_pos (GtkTextView *text_view,
   text_view->priv->virtual_cursor_y = (y == -1) ? pos.y + pos.height / 2 : y;
 }
 
-/* Quick hack of a popup menu
- */
 static void
-activate_cb (GtkWidget   *menuitem,
-            GtkTextView *text_view)
+hide_selection_bubble (GtkTextView *text_view)
 {
-  const gchar *signal;
+  GtkTextViewPrivate *priv = text_view->priv;
 
-  signal = g_object_get_qdata (G_OBJECT (menuitem), quark_gtk_signal);
-  g_signal_emit_by_name (text_view, signal);
+  if (priv->selection_bubble && gtk_widget_get_visible (priv->selection_bubble))
+    gtk_widget_hide (priv->selection_bubble);
 }
 
 static void
-append_action_signal (GtkTextView  *text_view,
-                     GtkWidget    *menu,
-                     const gchar  *label,
-                     const gchar  *signal,
-                      gboolean      sensitive)
+cut_clipboard_activated (GSimpleAction *action,
+                         GVariant      *parameter,
+                         gpointer       user_data)
 {
-  GtkWidget *menuitem = gtk_menu_item_new_with_mnemonic (label);
+  g_signal_emit_by_name (user_data, "cut-clipboard");
+  hide_selection_bubble (GTK_TEXT_VIEW (user_data));
+}
 
-  g_object_set_qdata (G_OBJECT (menuitem), quark_gtk_signal, (char *)signal);
-  g_signal_connect (menuitem, "activate",
-                   G_CALLBACK (activate_cb), text_view);
+static void
+copy_clipboard_activated (GSimpleAction *action,
+                          GVariant      *parameter,
+                          gpointer       user_data)
+{
+  g_signal_emit_by_name (user_data, "copy-clipboard");
+  hide_selection_bubble (GTK_TEXT_VIEW (user_data));
+}
 
-  gtk_widget_set_sensitive (menuitem, sensitive);
-  
-  gtk_widget_show (menuitem);
-  gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
+static void
+paste_clipboard_activated (GSimpleAction *action,
+                           GVariant      *parameter,
+                           gpointer       user_data)
+{
+  g_signal_emit_by_name (user_data, "paste-clipboard");
+  hide_selection_bubble (GTK_TEXT_VIEW (user_data));
 }
 
 static void
@@ -8493,19 +8449,34 @@ gtk_text_view_select_all (GtkWidget *widget,
 }
 
 static void
-select_all_cb (GtkWidget   *menuitem,
-              GtkTextView *text_view)
+select_all_activated (GSimpleAction *action,
+                      GVariant      *parameter,
+                      gpointer       user_data)
 {
+  GtkTextView *text_view = user_data;
+
   gtk_text_view_select_all (GTK_WIDGET (text_view), TRUE);
 }
 
 static void
-delete_cb (GtkTextView *text_view)
+delete_selection_activated (GSimpleAction *action,
+                            GVariant      *parameter,
+                            gpointer       user_data)
 {
+  GtkTextView *text_view = user_data;
+
   gtk_text_buffer_delete_selection (get_buffer (text_view), TRUE,
                                    text_view->priv->editable);
 }
 
+static void
+insert_emoji_activated (GSimpleAction *action,
+                        GVariant      *parameter,
+                        gpointer       user_data)
+{
+  gtk_text_view_insert_emoji (GTK_TEXT_VIEW (user_data));
+}
+
 static void
 popup_menu_detach (GtkWidget *attach_widget,
                   GtkMenu   *menu)
@@ -8531,132 +8502,221 @@ range_contains_editable_text (const GtkTextIter *start,
   return FALSE;
 }
 
+static void
+gtk_text_view_add_context_actions (GtkTextView *text_view)
+{
+  GtkTextViewPrivate *priv = text_view->priv;
+
+  GActionEntry entries[] = {
+    { "cut-clipboard", cut_clipboard_activated, NULL, NULL, NULL },
+    { "copy-clipboard", copy_clipboard_activated, NULL, NULL, NULL },
+    { "paste-clipboard", paste_clipboard_activated, NULL, NULL, NULL },
+    { "delete-selection", delete_selection_activated, NULL, NULL, NULL },
+    { "select-all", select_all_activated, NULL, NULL, NULL },
+    { "insert-emoji", insert_emoji_activated, NULL, NULL, NULL },
+  };
+
+  GSimpleActionGroup *actions = g_simple_action_group_new ();
+  GAction *action;
+
+  priv->context_actions = G_ACTION_MAP (actions);
+
+  g_action_map_add_action_entries (G_ACTION_MAP (actions), entries, G_N_ELEMENTS (entries), text_view);
+
+  action = g_action_map_lookup_action (G_ACTION_MAP (actions), "cut-clipboard");
+  g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
+  action = g_action_map_lookup_action (G_ACTION_MAP (actions), "copy-clipboard");
+  g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
+  action = g_action_map_lookup_action (G_ACTION_MAP (actions), "paste-clipboard");
+  g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
+  action = g_action_map_lookup_action (G_ACTION_MAP (actions), "delete-selection");
+  g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
+  action = g_action_map_lookup_action (G_ACTION_MAP (actions), "select-all");
+  g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
+  action = g_action_map_lookup_action (G_ACTION_MAP (actions), "insert-emoji");
+  g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
+
+  gtk_widget_insert_action_group (GTK_WIDGET (text_view), "context", G_ACTION_GROUP (actions));
+}
+
+static void
+gtk_text_view_update_clipboard_actions (GtkTextView *text_view)
+{
+  GtkTextViewPrivate *priv = text_view->priv;
+  GdkClipboard *clipboard;
+  gboolean have_selection;
+  gboolean can_paste, can_insert;
+  GAction *action;
+  GtkTextIter iter, sel_start, sel_end;
+
+  clipboard = gtk_widget_get_clipboard (GTK_WIDGET (text_view));
+  can_paste = gdk_content_formats_contain_gtype (gdk_clipboard_get_formats (clipboard), G_TYPE_STRING);
+
+  have_selection = gtk_text_buffer_get_selection_bounds (get_buffer (text_view),
+                                                         &sel_start, &sel_end);
+
+  gtk_text_buffer_get_iter_at_mark (get_buffer (text_view),
+                                    &iter,
+                                    gtk_text_buffer_get_insert (get_buffer (text_view)));
+
+  can_insert = gtk_text_iter_can_insert (&iter, priv->editable);
+
+  action = g_action_map_lookup_action (priv->context_actions, "cut-clipboard");
+  g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
+                              have_selection &&
+                               range_contains_editable_text (&sel_start, &sel_end, priv->editable));
+
+  action = g_action_map_lookup_action (priv->context_actions, "copy-clipboard");
+  g_simple_action_set_enabled (G_SIMPLE_ACTION (action), have_selection);
+
+  action = g_action_map_lookup_action (priv->context_actions, "paste-clipboard");
+  g_simple_action_set_enabled (G_SIMPLE_ACTION (action), can_insert && can_paste);
+
+  action = g_action_map_lookup_action (priv->context_actions, "delete-selection");
+  g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
+                              have_selection &&
+                               range_contains_editable_text (&sel_start, &sel_end, priv->editable));
+
+  action = g_action_map_lookup_action (priv->context_actions, "select-all");
+  g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
+                               gtk_text_buffer_get_char_count (priv->buffer) > 0);
+}
+
+static void
+gtk_text_view_update_emoji_action (GtkTextView *text_view)
+{
+  GtkTextViewPrivate *priv = text_view->priv;
+  GAction *action;
+
+  action = g_action_map_lookup_action (priv->context_actions, "insert-emoji");
+  g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
+                               (gtk_text_view_get_input_hints (text_view) & GTK_INPUT_HINT_NO_EMOJI) == 0);
+}
+
+GMenuModel *
+gtk_text_view_get_default_context_menu (GtkTextView *textview)
+{
+  GMenu *menu, *section;
+  GMenuItem *item;
+
+  menu = g_menu_new ();
+
+  section = g_menu_new ();
+  item = g_menu_item_new (_("Cu_t"), "context.cut-clipboard");
+  g_menu_item_set_attribute (item, "touch-icon", "s", "edit-cut-symbolic");
+  g_menu_append_item (section, item);
+  g_object_unref (item);
+  item = g_menu_item_new (_("_Copy"), "context.copy-clipboard");
+  g_menu_item_set_attribute (item, "touch-icon", "s", "edit-copy-symbolic");
+  g_menu_append_item (section, item);
+  g_object_unref (item);
+  item = g_menu_item_new (_("_Paste"), "context.paste-clipboard");
+  g_menu_item_set_attribute (item, "touch-icon", "s", "edit-paste-symbolic");
+  g_menu_append_item (section, item);
+  g_object_unref (item);
+  item = g_menu_item_new (_("_Delete"), "context.delete-selection");
+  g_menu_item_set_attribute (item, "touch-icon", "s", "edit-delete-symbolic");
+  g_menu_append_item (section, item);
+  g_object_unref (item);
+  g_menu_append_section (menu, NULL, G_MENU_MODEL (section));
+  g_object_unref (section);
+
+  section = g_menu_new ();
+
+  item = g_menu_item_new (_("Select _All"), "context.select-all");
+  g_menu_item_set_attribute (item, "touch-icon", "s", "edit-select-all-symbolic");
+  g_menu_append_item (section, item);
+  g_object_unref (item);
+
+  item = g_menu_item_new ( _("Insert _Emoji"), "context.insert-emoji");
+  g_menu_item_set_attribute (item, "hidden-when", "s", "action-disabled");
+  g_menu_item_set_attribute (item, "touch-icon", "s", "face-smile-symbolic");
+  g_menu_append_item (section, item);
+  g_object_unref (item);
+  g_menu_append_section (menu, NULL, G_MENU_MODEL (section));
+  g_object_unref (section);
+
+  return G_MENU_MODEL (menu);
+}
+
 static void
 gtk_text_view_do_popup (GtkTextView    *text_view,
                         const GdkEvent *event)
 {
   GtkTextViewPrivate *priv = text_view->priv;
   GdkEvent *trigger_event;
+  GMenuModel *model;
+  GtkWidget *menu;
+
+  if (!gtk_widget_get_realized (GTK_WIDGET (text_view)))
+    return;
 
   if (event)
     trigger_event = gdk_event_copy (event);
   else
     trigger_event = gtk_get_current_event ();
 
-  if (gtk_widget_get_realized (GTK_WIDGET (text_view)))
+  gtk_text_view_update_clipboard_actions (text_view);
+
+  if (priv->popup_menu)
+    gtk_widget_destroy (priv->popup_menu);
+
+  model = gtk_widget_get_context_menu (GTK_WIDGET (text_view));
+  if (model)
+    g_object_ref (model);
+  else
+    model = gtk_text_view_get_default_context_menu (text_view);
+
+  priv->popup_menu = menu = gtk_menu_new_from_model (model);
+
+  gtk_style_context_add_class (gtk_widget_get_style_context (priv->popup_menu),
+                               GTK_STYLE_CLASS_CONTEXT_MENU);
+
+  gtk_menu_attach_to_widget (GTK_MENU (priv->popup_menu),
+                             GTK_WIDGET (text_view),
+                             popup_menu_detach);
+
+  if (trigger_event && gdk_event_triggers_context_menu (trigger_event))
+    gtk_menu_popup_at_pointer (GTK_MENU (priv->popup_menu), trigger_event);
+  else
     {
-      GtkWidget *menuitem;
-      gboolean have_selection;
-      gboolean can_insert, can_paste;
       GtkTextIter iter;
-      GtkTextIter sel_start, sel_end;
       GdkRectangle iter_location;
       GdkRectangle visible_rect;
       gboolean is_visible;
+          
+      gtk_text_view_get_iter_location (text_view, &iter, &iter_location);
+      gtk_text_view_get_visible_rect (text_view, &visible_rect);
 
-      if (priv->popup_menu)
-       gtk_widget_destroy (priv->popup_menu);
-
-      priv->popup_menu = gtk_menu_new ();
-      gtk_style_context_add_class (gtk_widget_get_style_context (priv->popup_menu),
-                                   GTK_STYLE_CLASS_CONTEXT_MENU);
-
-      gtk_menu_attach_to_widget (GTK_MENU (priv->popup_menu),
-                                GTK_WIDGET (text_view),
-                                popup_menu_detach);
-
-      have_selection = gtk_text_buffer_get_selection_bounds (get_buffer (text_view),
-                                                             &sel_start, &sel_end);
-
-      gtk_text_buffer_get_iter_at_mark (get_buffer (text_view),
-                                       &iter,
-                                       gtk_text_buffer_get_insert (get_buffer (text_view)));
+      is_visible = (iter_location.x + iter_location.width > visible_rect.x &&
+                    iter_location.x < visible_rect.x + visible_rect.width &&
+                    iter_location.y + iter_location.height > visible_rect.y &&
+                    iter_location.y < visible_rect.y + visible_rect.height);
 
-      can_insert = gtk_text_iter_can_insert (&iter, priv->editable);
-      can_paste = gdk_content_formats_contain_gtype (gdk_clipboard_get_formats (gtk_widget_get_clipboard 
(GTK_WIDGET (text_view))),
-                                                     GTK_TYPE_TEXT_BUFFER);
-
-      append_action_signal (text_view, priv->popup_menu, _("Cu_t"), "cut-clipboard",
-                           have_selection &&
-                            range_contains_editable_text (&sel_start, &sel_end,
-                                                          priv->editable));
-      append_action_signal (text_view, priv->popup_menu, _("_Copy"), "copy-clipboard",
-                           have_selection);
-      append_action_signal (text_view, priv->popup_menu, _("_Paste"), "paste-clipboard",
-                           can_insert && can_paste);
-
-      menuitem = gtk_menu_item_new_with_mnemonic (_("_Delete"));
-      gtk_widget_set_sensitive (menuitem,
-                               have_selection &&
-                               range_contains_editable_text (&sel_start, &sel_end,
-                                                             priv->editable));
-      g_signal_connect_swapped (menuitem, "activate",
-                               G_CALLBACK (delete_cb), text_view);
-      gtk_widget_show (menuitem);
-      gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menuitem);
-
-      menuitem = gtk_separator_menu_item_new ();
-      gtk_widget_show (menuitem);
-      gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menuitem);
-
-      menuitem = gtk_menu_item_new_with_mnemonic (_("Select _All"));
-      gtk_widget_set_sensitive (menuitem,
-                                gtk_text_buffer_get_char_count (priv->buffer) > 0);
-      g_signal_connect (menuitem, "activate",
-                       G_CALLBACK (select_all_cb), text_view);
-      gtk_widget_show (menuitem);
-      gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menuitem);
-
-      if ((gtk_text_view_get_input_hints (text_view) & GTK_INPUT_HINT_NO_EMOJI) == 0)
+      if (is_visible)
         {
-          menuitem = gtk_menu_item_new_with_mnemonic (_("Insert _Emoji"));
-          gtk_widget_set_sensitive (menuitem, can_insert);
-          g_signal_connect_swapped (menuitem, "activate",
-                                    G_CALLBACK (gtk_text_view_insert_emoji), text_view);
-          gtk_widget_show (menuitem);
-          gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menuitem);
+          gtk_text_view_buffer_to_surface_coords (text_view,
+                                                  GTK_TEXT_WINDOW_WIDGET,
+                                                  iter_location.x,
+                                                  iter_location.y,
+                                                  &iter_location.x,
+                                                  &iter_location.y);
+
+          gtk_menu_popup_at_rect (GTK_MENU (priv->popup_menu),
+                                  gtk_widget_get_surface (GTK_WIDGET (text_view)),
+                                  &iter_location,
+                                  GDK_GRAVITY_SOUTH_EAST,
+                                  GDK_GRAVITY_NORTH_WEST,
+                                  trigger_event);
         }
-
-      g_signal_emit (text_view, signals[POPULATE_POPUP],
-                    0, priv->popup_menu);
-
-      if (trigger_event && gdk_event_triggers_context_menu (trigger_event))
-        gtk_menu_popup_at_pointer (GTK_MENU (priv->popup_menu), trigger_event);
       else
-        {
-          gtk_text_view_get_iter_location (text_view, &iter, &iter_location);
-          gtk_text_view_get_visible_rect (text_view, &visible_rect);
-
-          is_visible = (iter_location.x + iter_location.width > visible_rect.x &&
-                        iter_location.x < visible_rect.x + visible_rect.width &&
-                        iter_location.y + iter_location.height > visible_rect.y &&
-                        iter_location.y < visible_rect.y + visible_rect.height);
+        gtk_menu_popup_at_widget (GTK_MENU (priv->popup_menu),
+                                  GTK_WIDGET (text_view),
+                                  GDK_GRAVITY_CENTER,
+                                  GDK_GRAVITY_CENTER,
+                                  trigger_event);
 
-          if (is_visible)
-            {
-              gtk_text_view_buffer_to_surface_coords (text_view,
-                                                     GTK_TEXT_WINDOW_WIDGET,
-                                                     iter_location.x,
-                                                     iter_location.y,
-                                                     &iter_location.x,
-                                                     &iter_location.y);
-
-              gtk_menu_popup_at_rect (GTK_MENU (priv->popup_menu),
-                                      gtk_widget_get_surface (GTK_WIDGET (text_view)),
-                                      &iter_location,
-                                      GDK_GRAVITY_SOUTH_EAST,
-                                      GDK_GRAVITY_NORTH_WEST,
-                                      trigger_event);
-            }
-          else
-            gtk_menu_popup_at_widget (GTK_MENU (priv->popup_menu),
-                                      GTK_WIDGET (text_view),
-                                      GDK_GRAVITY_CENTER,
-                                      GDK_GRAVITY_CENTER,
-                                      trigger_event);
-
-          gtk_menu_shell_select_first (GTK_MENU_SHELL (priv->popup_menu), FALSE);
-        }
+      gtk_menu_shell_select_first (GTK_MENU_SHELL (priv->popup_menu), FALSE);
     }
 
   g_clear_object (&trigger_event);
@@ -8722,32 +8782,58 @@ show_or_hide_handles (GtkWidget   *popover,
 }
 
 static void
-activate_bubble_cb (GtkWidget   *item,
-                    GtkTextView *text_view)
+append_bubble_item (GtkTextView *text_view,
+                    GtkWidget   *toolbar,
+                    GMenuModel  *model,
+                    int          index)
 {
-  const gchar *signal;
+  GtkTextViewPrivate *priv = text_view->priv;
+  GtkWidget *item, *image;
+  GVariant *att;
+  const char *icon_name;
+  const char *action_name;
+  GAction *action;
+  GMenuModel *link;
+
+  link = g_menu_model_get_item_link (model, index, "section");
+  if (link)
+    {
+      int i;
+      for (i = 0; i < g_menu_model_get_n_items (link); i++)
+        append_bubble_item (text_view, toolbar, link, i);
+      g_object_unref (link);
+      return;
+    }
 
-  signal = g_object_get_qdata (G_OBJECT (item), quark_gtk_signal);
-  gtk_widget_hide (text_view->priv->selection_bubble);
-  g_signal_emit_by_name (text_view, signal);
-}
+  att = g_menu_model_get_item_attribute_value (model, index, "touch-icon", G_VARIANT_TYPE_STRING);
+  if (att == NULL)
+    return;
 
-static void
-append_bubble_action (GtkTextView  *text_view,
-                      GtkWidget    *toolbar,
-                      const gchar  *label,
-                      const gchar  *icon_name,
-                      const gchar  *signal,
-                      gboolean      sensitive)
-{
-  GtkWidget *item;
+  icon_name = g_variant_get_string (att, NULL);
+  g_variant_unref (att);
+
+  att = g_menu_model_get_item_attribute_value (model, index, "action", G_VARIANT_TYPE_STRING);
+  if (att == NULL)
+    return;
+  action_name = g_variant_get_string (att, NULL);
+  g_variant_unref (att);
+
+  if (g_str_has_prefix (action_name, "context."))
+    {
+      action = g_action_map_lookup_action (priv->context_actions, action_name + strlen ("context."));
+      if (action && !g_action_get_enabled (action))
+        return;
+    }
 
-  item = gtk_button_new_from_icon_name (icon_name);
+  item = gtk_button_new ();
   gtk_widget_set_focus_on_click (item, FALSE);
-  gtk_widget_set_tooltip_text (item, label);
-  g_object_set_qdata (G_OBJECT (item), quark_gtk_signal, (char *)signal);
-  g_signal_connect (item, "clicked", G_CALLBACK (activate_bubble_cb), text_view);
-  gtk_widget_set_sensitive (GTK_WIDGET (item), sensitive);
+  image = gtk_image_new_from_icon_name (icon_name);
+  gtk_widget_show (image);
+  gtk_container_add (GTK_CONTAINER (item), image);
+  gtk_style_context_add_class (gtk_widget_get_style_context (item), "image-button");
+  gtk_actionable_set_action_name (GTK_ACTIONABLE (item), action_name);
+
+  gtk_widget_show (GTK_WIDGET (item));
   gtk_container_add (GTK_CONTAINER (toolbar), item);
 }
 
@@ -8757,24 +8843,14 @@ gtk_text_view_selection_bubble_popup_show (gpointer user_data)
   GtkTextView *text_view = user_data;
   GtkTextViewPrivate *priv = text_view->priv;
   cairo_rectangle_int_t rect;
-  GdkClipboard *clipboard;
-  gboolean has_selection;
-  gboolean has_clipboard;
-  gboolean can_insert;
-  gboolean all_selected;
-  GtkTextIter iter;
-  GtkTextIter sel_start, sel_end;
-  GtkTextIter start, end;
   GtkWidget *box;
   GtkWidget *toolbar;
+  GMenuModel *model;
+  int i;
 
-  priv->selection_bubble_timeout_id = 0;
-  has_selection = gtk_text_buffer_get_selection_bounds (get_buffer (text_view),
-                                                        &sel_start, &sel_end);
-  gtk_text_buffer_get_bounds (get_buffer (text_view), &start, &end);
+  gtk_text_view_update_clipboard_actions (text_view);
 
-  all_selected = gtk_text_iter_equal (&start, &sel_start) &&
-                 gtk_text_iter_equal (&end, &sel_end);
+  priv->selection_bubble_timeout_id = 0;
 
   if (priv->selection_bubble)
     gtk_widget_destroy (priv->selection_bubble);
@@ -8795,25 +8871,15 @@ gtk_text_view_selection_bubble_popup_show (gpointer user_data)
   gtk_container_add (GTK_CONTAINER (priv->selection_bubble), box);
   gtk_container_add (GTK_CONTAINER (box), toolbar);
 
-  gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &iter,
-                                    gtk_text_buffer_get_insert (get_buffer (text_view)));
-  can_insert = gtk_text_iter_can_insert (&iter, priv->editable);
-  clipboard = gtk_widget_get_clipboard (GTK_WIDGET (text_view));
-  has_clipboard = gdk_content_formats_contain_gtype (gdk_clipboard_get_formats (clipboard), 
GTK_TYPE_TEXT_BUFFER);
-
-  append_bubble_action (text_view, toolbar, _("Select all"), "edit-select-all-symbolic", "select-all", 
!all_selected);
-
-  if (range_contains_editable_text (&sel_start, &sel_end, priv->editable) && has_selection)
-    append_bubble_action (text_view, toolbar, _("Cut"), "edit-cut-symbolic", "cut-clipboard", TRUE);
-
-  if (has_selection)
-    append_bubble_action (text_view, toolbar, _("Copy"), "edit-copy-symbolic", "copy-clipboard", TRUE);
-
-  if (can_insert)
-    append_bubble_action (text_view, toolbar, _("Paste"), "edit-paste-symbolic", "paste-clipboard", 
has_clipboard);
+  model = gtk_widget_get_context_menu (GTK_WIDGET (text_view));
+  if (model)
+    g_object_ref (model);
+  else
+    model = gtk_text_view_get_default_context_menu (text_view);
 
-  if (priv->populate_all)
-    g_signal_emit (text_view, signals[POPULATE_POPUP], 0, box);
+  for (i = 0; i < g_menu_model_get_n_items (model); i++)
+    append_bubble_item (text_view, toolbar, model, i);
+  g_object_unref (model);
 
   gtk_text_view_get_selection_rect (text_view, &rect);
   rect.x -= priv->xoffset;
@@ -9752,6 +9818,7 @@ gtk_text_view_set_input_hints (GtkTextView   *text_view,
                     NULL);
 
       g_object_notify (G_OBJECT (text_view), "input-hints");
+      gtk_text_view_update_emoji_action (text_view);
     }
 }
 
diff --git a/gtk/gtktextview.h b/gtk/gtktextview.h
index 74256b4877..a55fda5633 100644
--- a/gtk/gtktextview.h
+++ b/gtk/gtktextview.h
@@ -120,8 +120,6 @@ struct _GtkTextView
 /**
  * GtkTextViewClass:
  * @parent_class: The object class structure needs to be the first
- * @populate_popup: The class handler for the #GtkTextView::populate-popup
- *   signal.
  * @move_cursor: The class handler for the #GtkTextView::move-cursor
  *   keybinding signal.
  * @set_anchor: The class handler for the #GtkTextView::set-anchor
@@ -159,8 +157,6 @@ struct _GtkTextViewClass
 
   /*< public >*/
 
-  void (* populate_popup)        (GtkTextView      *text_view,
-                                  GtkWidget        *popup);
   void (* move_cursor)           (GtkTextView      *text_view,
                                   GtkMovementStep   step,
                                   gint              count,
@@ -435,6 +431,9 @@ void             gtk_text_view_set_monospace          (GtkTextView      *text_vi
 GDK_AVAILABLE_IN_ALL
 gboolean         gtk_text_view_get_monospace          (GtkTextView      *text_view);
 
+GDK_AVAILABLE_IN_ALL
+GMenuModel *     gtk_text_view_get_default_context_menu (GtkTextView *textview);
+
 G_END_DECLS
 
 #endif /* __GTK_TEXT_VIEW_H__ */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]