[gimp/nielsdg/fix-context-menus: 5/6] app: Popup menu at rect in GimpEditor




commit eed28c09411fa69b9f73ac47f2d94b014df7a762
Author: Niels De Graef <nielsdegraef gmail com>
Date:   Wed Dec 8 00:09:08 2021 +0100

    app: Popup menu at rect in GimpEditor
    
    Rather than trying to fix up our own heuristics using a
    `GtkMenuPositionFunc`, use whatever GTK provides to position given a
    specific rectangle, which also has the benefit of nicely integrating
    with GDK backends such as Wayland. Another advantage is that we can use
    GdkGravity to center the popup.
    
    Since GTK 3, GtkWidget also gained a "popup-menu" signal, which we
    can/should use instead of rolling our own context signals.

 app/widgets/gimpcolormapeditor.c    | 49 ++++++++++++++++---
 app/widgets/gimpcolormapselection.c | 51 +++++++++----------
 app/widgets/gimpcolormapselection.h |  7 ++-
 app/widgets/gimpcontainereditor.c   | 37 ++------------
 app/widgets/gimpcontainereditor.h   |  2 -
 app/widgets/gimpcontainericonview.c | 98 +++++++++++++++----------------------
 app/widgets/gimpcontainertreeview.c | 72 +++++++--------------------
 app/widgets/gimpcontainerview.c     | 44 -----------------
 app/widgets/gimpcontainerview.h     |  7 ---
 app/widgets/gimpeditor.c            | 22 +++++++++
 app/widgets/gimpeditor.h            |  6 +++
 app/widgets/gimpitemtreeview.c      | 15 ------
 app/widgets/gimppaletteeditor.c     | 54 +++++++++++++++-----
 app/widgets/gimppaletteview.c       | 47 +++++++++++++-----
 app/widgets/gimppaletteview.h       |  6 +++
 app/widgets/gimpuimanager.c         | 40 +++++++++++++++
 app/widgets/gimpuimanager.h         | 10 ++++
 17 files changed, 295 insertions(+), 272 deletions(-)
---
diff --git a/app/widgets/gimpcolormapeditor.c b/app/widgets/gimpcolormapeditor.c
index 8a2152822a..c27f5af97f 100644
--- a/app/widgets/gimpcolormapeditor.c
+++ b/app/widgets/gimpcolormapeditor.c
@@ -58,7 +58,11 @@ static void   gimp_colormap_editor_color_update    (GimpColorDialog      *dialog
                                                     GimpColorDialogState  state,
                                                     GimpColormapEditor   *editor);
 
-static void   gimp_colormap_editor_entry_popup     (GimpEditor           *editor);
+static gboolean   gimp_colormap_editor_entry_button_press (GtkWidget     *widget,
+                                                           GdkEvent      *event,
+                                                           gpointer       user_data);
+static gboolean   gimp_colormap_editor_entry_popup     (GtkWidget            *widget,
+                                                        gpointer              user_data);
 static void   gimp_colormap_editor_color_clicked   (GimpColormapEditor   *editor,
                                                     GimpPaletteEntry     *entry,
                                                     GdkModifierType       state);
@@ -177,15 +181,18 @@ gimp_colormap_editor_set_context (GimpDocked  *docked,
       gtk_box_pack_start (GTK_BOX (editor), editor->selection, TRUE, TRUE, 0);
       gtk_widget_show (editor->selection);
 
-      g_signal_connect_swapped (editor->selection, "color-context",
-                                G_CALLBACK (gimp_colormap_editor_entry_popup),
-                                editor);
       g_signal_connect_swapped (editor->selection, "color-clicked",
                                 G_CALLBACK (gimp_colormap_editor_color_clicked),
                                 editor);
       g_signal_connect_swapped (editor->selection, "color-activated",
                                 G_CALLBACK (gimp_colormap_editor_edit_color),
                                 editor);
+      g_signal_connect (editor->selection, "button-press-event",
+                        G_CALLBACK (gimp_colormap_editor_entry_button_press),
+                        editor);
+      g_signal_connect (editor->selection, "popup-menu",
+                        G_CALLBACK (gimp_colormap_editor_entry_popup),
+                        editor);
     }
 }
 
@@ -361,10 +368,38 @@ gimp_colormap_editor_color_update (GimpColorDialog      *dialog,
     }
 }
 
-static void
-gimp_colormap_editor_entry_popup (GimpEditor *editor)
+static gboolean
+gimp_colormap_editor_entry_button_press (GtkWidget *widget,
+                                         GdkEvent  *event,
+                                         gpointer   user_data)
+{
+  if (gdk_event_triggers_context_menu (event))
+    {
+      gimp_editor_popup_menu_at_pointer (GIMP_EDITOR (user_data), event);
+      return GDK_EVENT_STOP;
+    }
+
+  return GDK_EVENT_PROPAGATE;
+}
+
+static gboolean
+gimp_colormap_editor_entry_popup (GtkWidget *widget,
+                                  gpointer   user_data)
 {
-  gimp_editor_popup_menu (editor, NULL, NULL);
+  GimpColormapEditor *editor = GIMP_COLORMAP_EDITOR (user_data);
+  GimpColormapSelection *selection = GIMP_COLORMAP_SELECTION (widget);
+  GimpPaletteEntry *selected;
+  GdkRectangle rect;
+
+  selected = gimp_colormap_selection_get_selected_entry (selection);
+  if (!selected)
+    return GDK_EVENT_PROPAGATE;
+
+  gimp_colormap_selection_get_entry_rect (selection, selected, &rect);
+  return gimp_editor_popup_menu_at_rect (GIMP_EDITOR (editor),
+                                         gtk_widget_get_window (widget),
+                                         &rect, GDK_GRAVITY_CENTER, GDK_GRAVITY_NORTH_WEST,
+                                         NULL);
 }
 
 static void
diff --git a/app/widgets/gimpcolormapselection.c b/app/widgets/gimpcolormapselection.c
index 7d582fac0c..22db9804b7 100644
--- a/app/widgets/gimpcolormapselection.c
+++ b/app/widgets/gimpcolormapselection.c
@@ -58,7 +58,6 @@ enum
 
 enum
 {
-  COLOR_CONTEXT,
   COLOR_CLICKED,
   COLOR_ACTIVATED,
   LAST_SIGNAL
@@ -106,9 +105,6 @@ static void   gimp_colormap_selection_entry_selected  (GimpPaletteView       *vi
 static void   gimp_colormap_selection_entry_activated (GimpPaletteView       *view,
                                                        GimpPaletteEntry      *entry,
                                                        GimpColormapSelection *selection);
-static void   gimp_colormap_selection_entry_context   (GimpPaletteView       *view,
-                                                       GimpPaletteEntry      *entry,
-                                                       GimpColormapSelection *selection);
 static void   gimp_colormap_selection_color_dropped   (GimpPaletteView       *view,
                                                        GimpPaletteEntry      *entry,
                                                        const GimpRGB         *color,
@@ -137,14 +133,6 @@ gimp_colormap_selection_class_init (GimpColormapSelectionClass* klass)
   GObjectClass   *object_class = G_OBJECT_CLASS (klass);
   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
 
-  signals[COLOR_CONTEXT] =
-    g_signal_new ("color-context",
-                  G_TYPE_FROM_CLASS (klass),
-                  G_SIGNAL_RUN_FIRST,
-                  G_STRUCT_OFFSET (GimpColormapSelectionClass, color_context),
-                  NULL, NULL, NULL,
-                  G_TYPE_NONE, 1,
-                  G_TYPE_POINTER);
   signals[COLOR_CLICKED] =
     g_signal_new ("color-clicked",
                   G_TYPE_FROM_CLASS (klass),
@@ -215,9 +203,6 @@ gimp_colormap_selection_init (GimpColormapSelection *selection)
   g_signal_connect (selection->view, "entry-activated",
                     G_CALLBACK (gimp_colormap_selection_entry_activated),
                     selection);
-  g_signal_connect (selection->view, "entry-context",
-                    G_CALLBACK (gimp_colormap_selection_entry_context),
-                    selection);
   g_signal_connect (selection->view, "color-dropped",
                     G_CALLBACK (gimp_colormap_selection_color_dropped),
                     selection);
@@ -471,6 +456,32 @@ gimp_colormap_selection_max_index (GimpColormapSelection *selection)
   return MAX (0, gimp_image_get_colormap_size (image) - 1);
 }
 
+GimpPaletteEntry *
+gimp_colormap_selection_get_selected_entry (GimpColormapSelection *selection)
+{
+  g_return_val_if_fail (GIMP_IS_COLORMAP_SELECTION (selection), NULL);
+
+  return gimp_palette_view_get_selected_entry (GIMP_PALETTE_VIEW (selection->view));
+}
+
+void
+gimp_colormap_selection_get_entry_rect (GimpColormapSelection *selection,
+                                        GimpPaletteEntry      *entry,
+                                        GdkRectangle          *rect)
+{
+  GtkAllocation allocation;
+
+  g_return_if_fail (GIMP_IS_COLORMAP_SELECTION (selection));
+  g_return_if_fail (entry);
+  g_return_if_fail (rect);
+
+  gimp_palette_view_get_entry_rect (GIMP_PALETTE_VIEW (selection->view),
+                                    entry, rect);
+  gtk_widget_get_allocation (GTK_WIDGET (selection), &allocation);
+  /* rect->x += allocation.x; */
+  /* rect->y += allocation.y; */
+}
+
 
 /*  private functions  */
 
@@ -603,16 +614,6 @@ gimp_colormap_selection_entry_activated (GimpPaletteView       *view,
   g_signal_emit (selection, signals[COLOR_ACTIVATED], 0, entry);
 }
 
-static void
-gimp_colormap_selection_entry_context (GimpPaletteView       *view,
-                                       GimpPaletteEntry      *entry,
-                                       GimpColormapSelection *selection)
-{
-  gimp_colormap_selection_set_index (selection, entry->position, NULL);
-
-  g_signal_emit (selection, signals[COLOR_CONTEXT], 0, entry);
-}
-
 static void
 gimp_colormap_selection_color_dropped (GimpPaletteView       *view,
                                        GimpPaletteEntry      *entry,
diff --git a/app/widgets/gimpcolormapselection.h b/app/widgets/gimpcolormapselection.h
index e524d1b42f..d500ba1bbb 100644
--- a/app/widgets/gimpcolormapselection.h
+++ b/app/widgets/gimpcolormapselection.h
@@ -54,8 +54,6 @@ struct _GimpColormapSelectionClass
 {
   GtkBoxClass  parent_class;
 
-  void (* color_context)   (GimpColormapSelection *selection,
-                            GimpPaletteEntry      *entry);
   void (* color_clicked)   (GimpColormapSelection *selection,
                             GimpPaletteEntry      *entry,
                             GdkModifierType        state);
@@ -76,6 +74,11 @@ gboolean    gimp_colormap_selection_set_index  (GimpColormapSelection *selection
 
 gint        gimp_colormap_selection_max_index  (GimpColormapSelection *selection);
 
+GimpPaletteEntry * gimp_colormap_selection_get_selected_entry (GimpColormapSelection *selection);
+
+void               gimp_colormap_selection_get_entry_rect (GimpColormapSelection *selection,
+                                                           GimpPaletteEntry      *entry,
+                                                           GdkRectangle          *rect);
 
 #endif /* __GIMP_COLORMAP_SELECTION_H__ */
 
diff --git a/app/widgets/gimpcontainereditor.c b/app/widgets/gimpcontainereditor.c
index cbfe1d037b..ae5f59277c 100644
--- a/app/widgets/gimpcontainereditor.c
+++ b/app/widgets/gimpcontainereditor.c
@@ -93,12 +93,6 @@ static void   gimp_container_editor_activate_item    (GtkWidget           *widge
                                                       GimpViewable        *viewable,
                                                       gpointer             insert_data,
                                                       GimpContainerEditor *editor);
-static void   gimp_container_editor_context_item     (GtkWidget           *widget,
-                                                      GimpViewable        *viewable,
-                                                      gpointer             insert_data,
-                                                      GimpContainerEditor *editor);
-static void   gimp_container_editor_real_context_item(GimpContainerEditor *editor,
-                                                      GimpViewable        *viewable);
 
 static GtkWidget * gimp_container_editor_get_preview (GimpDocked       *docked,
                                                       GimpContext      *context,
@@ -136,7 +130,6 @@ gimp_container_editor_class_init (GimpContainerEditorClass *klass)
 
   klass->select_item     = NULL;
   klass->activate_item   = NULL;
-  klass->context_item    = gimp_container_editor_real_context_item;
 
   g_object_class_install_property (object_class, PROP_VIEW_TYPE,
                                    g_param_spec_enum ("view-type",
@@ -288,9 +281,9 @@ gimp_container_editor_constructed (GObject *object)
   g_signal_connect_object (editor->view, "activate-item",
                            G_CALLBACK (gimp_container_editor_activate_item),
                            editor, 0);
-  g_signal_connect_object (editor->view, "context-item",
-                           G_CALLBACK (gimp_container_editor_context_item),
-                           editor, 0);
+  /* g_signal_connect_object (editor->view, "context-item", XXX maybe listen to popup-menu? */
+  /*                          G_CALLBACK (gimp_container_editor_context_item), */
+  /*                          editor, 0); */
 
   {
     GimpObject *object = gimp_context_get_by_type (editor->priv->context,
@@ -461,30 +454,6 @@ gimp_container_editor_activate_item (GtkWidget           *widget,
     klass->activate_item (editor, viewable);
 }
 
-static void
-gimp_container_editor_context_item (GtkWidget           *widget,
-                                    GimpViewable        *viewable,
-                                    gpointer             insert_data,
-                                    GimpContainerEditor *editor)
-{
-  GimpContainerEditorClass *klass = GIMP_CONTAINER_EDITOR_GET_CLASS (editor);
-
-  if (klass->context_item)
-    klass->context_item (editor, viewable);
-}
-
-static void
-gimp_container_editor_real_context_item (GimpContainerEditor *editor,
-                                         GimpViewable        *viewable)
-{
-  GimpContainer *container = gimp_container_view_get_container (editor->view);
-
-  if (viewable && gimp_container_have (container, GIMP_OBJECT (viewable)))
-    {
-      gimp_editor_popup_menu (GIMP_EDITOR (editor->view), NULL, NULL);
-    }
-}
-
 static GtkWidget *
 gimp_container_editor_get_preview (GimpDocked   *docked,
                                    GimpContext  *context,
diff --git a/app/widgets/gimpcontainereditor.h b/app/widgets/gimpcontainereditor.h
index 728d604e30..3d16dc4de2 100644
--- a/app/widgets/gimpcontainereditor.h
+++ b/app/widgets/gimpcontainereditor.h
@@ -50,8 +50,6 @@ struct _GimpContainerEditorClass
                           GimpViewable        *object);
   void (* activate_item) (GimpContainerEditor *editor,
                           GimpViewable        *object);
-  void (* context_item)  (GimpContainerEditor *editor,
-                          GimpViewable        *object);
 };
 
 
diff --git a/app/widgets/gimpcontainericonview.c b/app/widgets/gimpcontainericonview.c
index ecc89176a6..c0e9d0c221 100644
--- a/app/widgets/gimpcontainericonview.c
+++ b/app/widgets/gimpcontainericonview.c
@@ -241,68 +241,25 @@ gimp_container_icon_view_unmap (GtkWidget *widget)
   GTK_WIDGET_CLASS (parent_class)->unmap (widget);
 }
 
-static void
-gimp_container_icon_view_menu_position (GtkMenu  *menu,
-                                        gint     *x,
-                                        gint     *y,
-                                        gpointer  data)
+static gboolean
+gimp_container_icon_view_popup_menu (GtkWidget *widget)
 {
-  GimpContainerIconView *icon_view = GIMP_CONTAINER_ICON_VIEW (data);
-  GtkWidget             *widget    = GTK_WIDGET (icon_view->view);
-  GtkAllocation          allocation;
-#if 0
-  GtkTreeIter            selected_iter;
-#endif
-
-  gtk_widget_get_allocation (widget, &allocation);
-
-  gdk_window_get_origin (gtk_widget_get_window (widget), x, y);
-
-  if (! gtk_widget_get_has_window (widget))
-    {
-      *x += allocation.x;
-      *y += allocation.y;
-    }
-
-#if 0
-  if (gimp_container_icon_view_get_selected_single (icon_view, &selected_iter))
-    {
-      GtkTreePath  *path;
-      GdkRectangle  cell_rect;
-      gint          center;
-
-      path = gtk_tree_model_get_path (icon_view->model, &selected_iter);
-      gtk_icon_view_get_cell_area (icon_view->view, path,
-                                   icon_view->main_column, &cell_rect);
-      gtk_tree_path_free (path);
-
-      center = cell_rect.y + cell_rect.height / 2;
-      center = CLAMP (center, 0, allocation.height);
+  GimpContainerIconView *icon_view = GIMP_CONTAINER_ICON_VIEW (widget);
+  GtkTreeIter            iter;
+  GtkTreePath           *path;
+  GdkRectangle           rect;
 
-      *x += allocation.width / 2;
-      *y += center;
-    }
-  else
-#endif
-    {
-      GtkStyleContext *style = gtk_widget_get_style_context (widget);
-      GtkBorder        border;
-
-      gtk_style_context_get_border (style, 0, &border);
-
-      *x += border.left;
-      *y += border.top;
-    }
+  if (!gimp_container_icon_view_get_selected_single (icon_view, &iter))
+    return FALSE;
 
-  gimp_menu_position (menu, x, y);
-}
+  path = gtk_tree_model_get_path (icon_view->model, &iter);
+  gtk_icon_view_get_cell_rect (icon_view->view, path, NULL, &rect);
+  gtk_tree_path_free (path);
 
-static gboolean
-gimp_container_icon_view_popup_menu (GtkWidget *widget)
-{
-  return gimp_editor_popup_menu (GIMP_EDITOR (widget),
-                                 gimp_container_icon_view_menu_position,
-                                 widget);
+  return gimp_editor_popup_menu_at_rect (GIMP_EDITOR (widget),
+                                         gtk_widget_get_window (GTK_WIDGET (icon_view->view)),
+                                         &rect, GDK_GRAVITY_CENTER, GDK_GRAVITY_NORTH_WEST,
+                                         NULL);
 }
 
 GtkWidget *
@@ -638,7 +595,8 @@ gimp_container_icon_view_button_press (GtkWidget             *widget,
                                        GdkEventButton        *bevent,
                                        GimpContainerIconView *icon_view)
 {
-  GtkTreePath *path;
+  GimpContainerView *container_view = GIMP_CONTAINER_VIEW (icon_view);
+  GtkTreePath       *path;
 
   icon_view->priv->dnd_renderer = NULL;
 
@@ -658,10 +616,32 @@ gimp_container_icon_view_button_press (GtkWidget             *widget,
 
       icon_view->priv->dnd_renderer = renderer;
 
+      if (gdk_event_triggers_context_menu ((GdkEvent *) bevent))
+        {
+          /* If the clicked item is not selected, it becomes the new
+           * selection. Otherwise, we use the current selection. This
+           * allows to not break multiple selection when right-clicking.
+           */
+          if (! gimp_container_view_is_item_selected (container_view, renderer->viewable))
+            gimp_container_view_item_selected (container_view, renderer->viewable);
+          /* Show the context menu. */
+          if (gimp_container_view_get_container (container_view))
+            gimp_editor_popup_menu_at_pointer (GIMP_EDITOR (icon_view), (GdkEvent *) bevent);
+        }
+
       g_object_unref (renderer);
 
       gtk_tree_path_free (path);
     }
+  else
+    {
+      if (gdk_event_triggers_context_menu ((GdkEvent *) bevent))
+        {
+          gimp_editor_popup_menu_at_pointer (GIMP_EDITOR (icon_view), (GdkEvent *) bevent);
+        }
+
+      return TRUE;
+    }
 
   return FALSE;
 }
diff --git a/app/widgets/gimpcontainertreeview.c b/app/widgets/gimpcontainertreeview.c
index af1c335a4d..dd8c57d282 100644
--- a/app/widgets/gimpcontainertreeview.c
+++ b/app/widgets/gimpcontainertreeview.c
@@ -420,64 +420,26 @@ gimp_container_tree_view_unmap (GtkWidget *widget)
   GTK_WIDGET_CLASS (parent_class)->unmap (widget);
 }
 
-static void
-gimp_container_tree_view_menu_position (GtkMenu  *menu,
-                                        gint     *x,
-                                        gint     *y,
-                                        gpointer  data)
+static gboolean
+gimp_container_tree_view_popup_menu (GtkWidget *widget)
 {
-  GimpContainerTreeView *tree_view = GIMP_CONTAINER_TREE_VIEW (data);
-  GtkWidget             *widget    = GTK_WIDGET (tree_view->view);
-  GtkAllocation          allocation;
-  GtkTreeIter            selected_iter;
-
-  gtk_widget_get_allocation (widget, &allocation);
-
-  gdk_window_get_origin (gtk_widget_get_window (widget), x, y);
-
-  if (! gtk_widget_get_has_window (widget))
-    {
-      *x += allocation.x;
-      *y += allocation.y;
-    }
-
-  if (gimp_container_tree_view_get_selected_single (tree_view, &selected_iter))
-    {
-      GtkTreePath  *path;
-      GdkRectangle  cell_rect;
-      gint          center;
-
-      path = gtk_tree_model_get_path (tree_view->model, &selected_iter);
-      gtk_tree_view_get_cell_area (tree_view->view, path,
-                                   tree_view->main_column, &cell_rect);
-      gtk_tree_path_free (path);
-
-      center = cell_rect.y + cell_rect.height / 2;
-      center = CLAMP (center, 0, allocation.height);
-
-      *x += allocation.width / 2;
-      *y += center;
-    }
-  else
-    {
-      GtkStyleContext *style = gtk_widget_get_style_context (widget);
-      GtkBorder        border;
-
-      gtk_style_context_get_border (style, 0, &border);
+  GimpContainerTreeView *tree_view = GIMP_CONTAINER_TREE_VIEW (widget);
+  GtkTreeIter            iter;
+  GtkTreePath           *path;
+  GdkRectangle           rect;
 
-      *x += border.left;
-      *y += border.top;
-    }
+  if (!gimp_container_tree_view_get_selected_single (tree_view, &iter))
+    return FALSE;
 
-  gimp_menu_position (menu, x, y);
-}
+  path = gtk_tree_model_get_path (tree_view->model, &iter);
+  gtk_tree_view_get_cell_area (tree_view->view, path,
+                               tree_view->main_column, &rect);
+  gtk_tree_path_free (path);
 
-static gboolean
-gimp_container_tree_view_popup_menu (GtkWidget *widget)
-{
-  return gimp_editor_popup_menu (GIMP_EDITOR (widget),
-                                 gimp_container_tree_view_menu_position,
-                                 widget);
+  return gimp_editor_popup_menu_at_rect (GIMP_EDITOR (widget),
+                                         gtk_tree_view_get_bin_window (tree_view->view),
+                                         &rect, GDK_GRAVITY_CENTER, GDK_GRAVITY_NORTH_WEST,
+                                         NULL);
 }
 
 GtkWidget *
@@ -1484,7 +1446,7 @@ gimp_container_tree_view_button (GtkWidget             *widget,
             gimp_container_view_item_selected (container_view, renderer->viewable);
           /* Show the context menu. */
           if (gimp_container_view_get_container (container_view))
-            gimp_container_view_item_context (container_view, renderer->viewable);
+            gimp_editor_popup_menu_at_pointer (GIMP_EDITOR (tree_view), (GdkEvent *) bevent);
         }
       else if (bevent->button == 1)
         {
diff --git a/app/widgets/gimpcontainerview.c b/app/widgets/gimpcontainerview.c
index e6bbed0e4d..eac2c20020 100644
--- a/app/widgets/gimpcontainerview.c
+++ b/app/widgets/gimpcontainerview.c
@@ -48,7 +48,6 @@ enum
   SELECT_ITEM,
   SELECT_ITEMS,
   ACTIVATE_ITEM,
-  CONTEXT_ITEM,
   LAST_SIGNAL
 };
 
@@ -191,21 +190,9 @@ gimp_container_view_default_init (GimpContainerViewInterface *iface)
                   GIMP_TYPE_OBJECT,
                   G_TYPE_POINTER);
 
-  view_signals[CONTEXT_ITEM] =
-    g_signal_new ("context-item",
-                  G_TYPE_FROM_INTERFACE (iface),
-                  G_SIGNAL_RUN_FIRST,
-                  G_STRUCT_OFFSET (GimpContainerViewInterface, context_item),
-                  NULL, NULL,
-                  gimp_marshal_VOID__OBJECT_POINTER,
-                  G_TYPE_NONE, 2,
-                  GIMP_TYPE_OBJECT,
-                  G_TYPE_POINTER);
-
   iface->select_item        = NULL;
   iface->select_items       = NULL;
   iface->activate_item      = NULL;
-  iface->context_item       = NULL;
 
   iface->set_container      = gimp_container_view_real_set_container;
   iface->set_context        = gimp_container_view_real_set_context;
@@ -709,27 +696,6 @@ gimp_container_view_activate_item (GimpContainerView *view,
                  viewable, insert_data);
 }
 
-void
-gimp_container_view_context_item (GimpContainerView *view,
-                                  GimpViewable      *viewable)
-{
-  GimpContainerViewPrivate *private;
-  gpointer                  insert_data;
-
-  g_return_if_fail (GIMP_IS_CONTAINER_VIEW (view));
-  g_return_if_fail (GIMP_IS_VIEWABLE (viewable));
-
-  private = GIMP_CONTAINER_VIEW_GET_PRIVATE (view);
-
-  if (gimp_container_frozen (private->container))
-    return;
-
-  insert_data = g_hash_table_lookup (private->item_hash, viewable);
-
-  g_signal_emit (view, view_signals[CONTEXT_ITEM], 0,
-                 viewable, insert_data);
-}
-
 gpointer
 gimp_container_view_lookup (GimpContainerView *view,
                             GimpViewable      *viewable)
@@ -975,16 +941,6 @@ gimp_container_view_item_activated (GimpContainerView *view,
   gimp_container_view_activate_item (view, viewable);
 }
 
-void
-gimp_container_view_item_context (GimpContainerView *view,
-                                  GimpViewable      *viewable)
-{
-  g_return_if_fail (GIMP_IS_CONTAINER_VIEW (view));
-  g_return_if_fail (GIMP_IS_VIEWABLE (viewable));
-
-  gimp_container_view_context_item (view, viewable);
-}
-
 void
 gimp_container_view_set_property (GObject      *object,
                                   guint         property_id,
diff --git a/app/widgets/gimpcontainerview.h b/app/widgets/gimpcontainerview.h
index 0b5ad9ba01..3094710ac0 100644
--- a/app/widgets/gimpcontainerview.h
+++ b/app/widgets/gimpcontainerview.h
@@ -54,9 +54,6 @@ struct _GimpContainerViewInterface
   void     (* activate_item)      (GimpContainerView *view,
                                    GimpViewable      *object,
                                    gpointer           insert_data);
-  void     (* context_item)       (GimpContainerView *view,
-                                   GimpViewable      *object,
-                                   gpointer           insert_data);
 
   /*  virtual functions  */
   void     (* set_container)      (GimpContainerView *view,
@@ -133,8 +130,6 @@ gboolean           gimp_container_view_select_item        (GimpContainerView  *v
                                                            GimpViewable       *viewable);
 void               gimp_container_view_activate_item      (GimpContainerView  *view,
                                                            GimpViewable       *viewable);
-void               gimp_container_view_context_item       (GimpContainerView  *view,
-                                                           GimpViewable       *viewable);
 gint               gimp_container_view_get_selected       (GimpContainerView  *view,
                                                            GList             **items,
                                                            GList             **items_data);
@@ -156,8 +151,6 @@ gboolean           gimp_container_view_multi_selected     (GimpContainerView  *v
                                                            GList              *paths);
 void               gimp_container_view_item_activated     (GimpContainerView  *view,
                                                            GimpViewable       *item);
-void               gimp_container_view_item_context       (GimpContainerView  *view,
-                                                           GimpViewable       *item);
 
 /*  convenience functions  */
 
diff --git a/app/widgets/gimpeditor.c b/app/widgets/gimpeditor.c
index c84cd5718e..a70ddccc3a 100644
--- a/app/widgets/gimpeditor.c
+++ b/app/widgets/gimpeditor.c
@@ -482,6 +482,28 @@ gimp_editor_popup_menu_at_pointer (GimpEditor     *editor,
   return FALSE;
 }
 
+gboolean
+gimp_editor_popup_menu_at_rect (GimpEditor         *editor,
+                                GdkWindow          *window,
+                                const GdkRectangle *rect,
+                                GdkGravity          rect_anchor,
+                                GdkGravity          menu_anchor,
+                                const GdkEvent     *trigger_event)
+{
+  g_return_val_if_fail (GIMP_IS_EDITOR (editor), FALSE);
+
+  if (editor->priv->ui_manager && editor->priv->ui_path)
+    {
+      gimp_ui_manager_update (editor->priv->ui_manager, editor->priv->popup_data);
+      gimp_ui_manager_ui_popup_at_rect (editor->priv->ui_manager, editor->priv->ui_path,
+                                        window, rect, rect_anchor, menu_anchor,
+                                        trigger_event, NULL, NULL);
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
 GtkWidget *
 gimp_editor_add_button (GimpEditor  *editor,
                         const gchar *icon_name,
diff --git a/app/widgets/gimpeditor.h b/app/widgets/gimpeditor.h
index 0ba60b96eb..1cecb2e5f0 100644
--- a/app/widgets/gimpeditor.h
+++ b/app/widgets/gimpeditor.h
@@ -61,6 +61,12 @@ gboolean    gimp_editor_popup_menu        (GimpEditor           *editor,
 gboolean    gimp_editor_popup_menu_at_pointer
                                           (GimpEditor           *editor,
                                            const GdkEvent       *trigger_event);
+gboolean   gimp_editor_popup_menu_at_rect (GimpEditor           *editor,
+                                           GdkWindow            *window,
+                                           const GdkRectangle   *rect,
+                                           GdkGravity            rect_anchor,
+                                           GdkGravity            menu_anchor,
+                                           const GdkEvent       *trigger_event);
 
 GtkWidget * gimp_editor_add_button        (GimpEditor           *editor,
                                            const gchar          *icon_name,
diff --git a/app/widgets/gimpitemtreeview.c b/app/widgets/gimpitemtreeview.c
index 5f163db569..8850fe380a 100644
--- a/app/widgets/gimpitemtreeview.c
+++ b/app/widgets/gimpitemtreeview.c
@@ -134,9 +134,6 @@ static gboolean gimp_item_tree_view_select_items    (GimpContainerView *view,
 static void   gimp_item_tree_view_activate_item     (GimpContainerView *view,
                                                      GimpViewable      *item,
                                                      gpointer           insert_data);
-static void   gimp_item_tree_view_context_item      (GimpContainerView *view,
-                                                     GimpViewable      *item,
-                                                     gpointer           insert_data);
 
 static gboolean gimp_item_tree_view_drop_possible   (GimpContainerTreeView *view,
                                                      GimpDndType        src_type,
@@ -307,7 +304,6 @@ gimp_item_tree_view_view_iface_init (GimpContainerViewInterface *iface)
   iface->select_item        = gimp_item_tree_view_select_item;
   iface->select_items       = gimp_item_tree_view_select_items;
   iface->activate_item      = gimp_item_tree_view_activate_item;
-  iface->context_item       = gimp_item_tree_view_context_item;
 }
 
 static void
@@ -1182,17 +1178,6 @@ gimp_item_tree_view_activate_item (GimpContainerView *view,
     }
 }
 
-static void
-gimp_item_tree_view_context_item (GimpContainerView *view,
-                                  GimpViewable      *item,
-                                  gpointer           insert_data)
-{
-  if (parent_view_iface->context_item)
-    parent_view_iface->context_item (view, item, insert_data);
-
-  gimp_editor_popup_menu (GIMP_EDITOR (view), NULL, NULL);
-}
-
 static gboolean
 gimp_item_tree_view_drop_possible (GimpContainerTreeView   *tree_view,
                                    GimpDndType              src_type,
diff --git a/app/widgets/gimppaletteeditor.c b/app/widgets/gimppaletteeditor.c
index 6abee86f45..2335dad337 100644
--- a/app/widgets/gimppaletteeditor.c
+++ b/app/widgets/gimppaletteeditor.c
@@ -106,9 +106,11 @@ static void   palette_editor_entry_selected        (GimpPaletteView   *view,
 static void   palette_editor_entry_activated       (GimpPaletteView   *view,
                                                     GimpPaletteEntry  *entry,
                                                     GimpPaletteEditor *editor);
-static void   palette_editor_entry_context         (GimpPaletteView   *view,
-                                                    GimpPaletteEntry  *entry,
-                                                    GimpPaletteEditor *editor);
+static gboolean palette_editor_button_press_event  (GtkWidget *widget,
+                                                    GdkEvent  *event,
+                                                    gpointer   user_data);
+static gboolean palette_editor_popup_menu          (GtkWidget *widget,
+                                                    gpointer   user_data);
 static void   palette_editor_color_dropped         (GimpPaletteView   *view,
                                                     GimpPaletteEntry  *entry,
                                                     const GimpRGB     *color,
@@ -219,12 +221,15 @@ gimp_palette_editor_init (GimpPaletteEditor *editor)
   g_signal_connect (editor->view, "entry-activated",
                     G_CALLBACK (palette_editor_entry_activated),
                     editor);
-  g_signal_connect (editor->view, "entry-context",
-                    G_CALLBACK (palette_editor_entry_context),
-                    editor);
   g_signal_connect (editor->view, "color-dropped",
                     G_CALLBACK (palette_editor_color_dropped),
                     editor);
+  g_signal_connect (editor->view, "button-press-event",
+                    G_CALLBACK (palette_editor_button_press_event),
+                    editor);
+  g_signal_connect (editor->view, "popup-menu",
+                    G_CALLBACK (palette_editor_popup_menu),
+                    editor);
 
   gimp_dnd_viewable_dest_add (editor->view,
                               GIMP_TYPE_PALETTE,
@@ -825,12 +830,39 @@ palette_editor_entry_activated (GimpPaletteView   *view,
     }
 }
 
-static void
-palette_editor_entry_context (GimpPaletteView   *view,
-                              GimpPaletteEntry  *entry,
-                              GimpPaletteEditor *editor)
+static gboolean
+palette_editor_button_press_event (GtkWidget *widget,
+                                   GdkEvent  *event,
+                                   gpointer   user_data)
+{
+  GimpPaletteEditor *editor = GIMP_PALETTE_EDITOR (user_data);
+
+  if (gdk_event_triggers_context_menu (event))
+    {
+      gimp_editor_popup_menu_at_pointer (GIMP_EDITOR (editor), event);
+      return GDK_EVENT_STOP;
+    }
+
+  return GDK_EVENT_PROPAGATE;
+}
+
+static gboolean
+palette_editor_popup_menu (GtkWidget *widget,
+                           gpointer   user_data)
 {
-  gimp_editor_popup_menu (GIMP_EDITOR (editor), NULL, NULL);
+  GimpPaletteEditor *editor = GIMP_PALETTE_EDITOR (user_data);
+  GimpPaletteEntry *selected;
+  GdkRectangle rect;
+
+  selected = gimp_palette_view_get_selected_entry (GIMP_PALETTE_VIEW (editor->view));
+  if (!selected)
+    return GDK_EVENT_PROPAGATE;
+
+  gimp_palette_view_get_entry_rect (GIMP_PALETTE_VIEW (editor->view), selected, &rect);
+  return gimp_editor_popup_menu_at_rect (GIMP_EDITOR (editor),
+                                         gtk_widget_get_window (GTK_WIDGET (editor->view)),
+                                         &rect, GDK_GRAVITY_CENTER, GDK_GRAVITY_NORTH_WEST,
+                                         NULL);
 }
 
 static void
diff --git a/app/widgets/gimppaletteview.c b/app/widgets/gimppaletteview.c
index 6b10d05056..ee6168c99f 100644
--- a/app/widgets/gimppaletteview.c
+++ b/app/widgets/gimppaletteview.c
@@ -42,7 +42,6 @@ enum
   ENTRY_CLICKED,
   ENTRY_SELECTED,
   ENTRY_ACTIVATED,
-  ENTRY_CONTEXT,
   COLOR_DROPPED,
   LAST_SIGNAL
 };
@@ -119,15 +118,6 @@ gimp_palette_view_class_init (GimpPaletteViewClass *klass)
                   G_TYPE_NONE, 1,
                   G_TYPE_POINTER);
 
-  view_signals[ENTRY_CONTEXT] =
-    g_signal_new ("entry-context",
-                  G_TYPE_FROM_CLASS (klass),
-                  G_SIGNAL_RUN_FIRST,
-                  G_STRUCT_OFFSET (GimpPaletteViewClass, entry_context),
-                  NULL, NULL, NULL,
-                  G_TYPE_NONE, 1,
-                  G_TYPE_POINTER);
-
   view_signals[COLOR_DROPPED] =
     g_signal_new ("color-dropped",
                   G_TYPE_FROM_CLASS (klass),
@@ -226,7 +216,9 @@ gimp_palette_view_button_press (GtkWidget      *widget,
       if (entry != view->selected)
         gimp_palette_view_select_entry (view, entry);
 
-      g_signal_emit (view, view_signals[ENTRY_CONTEXT], 0, entry);
+      /* Usually the menu is provided by a GimpEditor.
+          * Make sure it's also run by returning FALSE here */
+      return FALSE;
     }
   else if (bevent->button == 1)
     {
@@ -404,6 +396,39 @@ gimp_palette_view_select_entry (GimpPaletteView  *view,
   g_signal_emit (view, view_signals[ENTRY_SELECTED], 0, view->selected);
 }
 
+GimpPaletteEntry *
+gimp_palette_view_get_selected_entry (GimpPaletteView *view)
+{
+  g_return_val_if_fail (GIMP_IS_PALETTE_VIEW (view), NULL);
+
+  return view->selected;
+}
+
+void
+gimp_palette_view_get_entry_rect (GimpPaletteView  *view,
+                                  GimpPaletteEntry *entry,
+                                  GdkRectangle     *rect)
+{
+  GimpViewRendererPalette *renderer;
+  GtkAllocation            allocation;
+  gint                     row, col;
+
+  g_return_if_fail (GIMP_IS_PALETTE_VIEW (view));
+  g_return_if_fail (entry);
+  g_return_if_fail (rect);
+
+  gtk_widget_get_allocation (GTK_WIDGET (view), &allocation);
+
+  renderer = GIMP_VIEW_RENDERER_PALETTE (GIMP_VIEW (view)->renderer);
+  row = entry->position / renderer->columns;
+  col = entry->position % renderer->columns;
+
+  rect->x = allocation.x + col * renderer->cell_width;
+  rect->y = allocation.y + row * renderer->cell_height;
+  rect->width = renderer->cell_width;
+  rect->height = renderer->cell_height;
+}
+
 
 /*  private functions  */
 
diff --git a/app/widgets/gimppaletteview.h b/app/widgets/gimppaletteview.h
index 68de628fc6..9c215e43e5 100644
--- a/app/widgets/gimppaletteview.h
+++ b/app/widgets/gimppaletteview.h
@@ -66,5 +66,11 @@ GType   gimp_palette_view_get_type     (void) G_GNUC_CONST;
 void    gimp_palette_view_select_entry (GimpPaletteView  *view,
                                         GimpPaletteEntry *entry);
 
+GimpPaletteEntry * gimp_palette_view_get_selected_entry (GimpPaletteView *view);
+
+void               gimp_palette_view_get_entry_rect     (GimpPaletteView  *view,
+                                                         GimpPaletteEntry *entry,
+                                                         GdkRectangle     *rect);
+
 
 #endif /* __GIMP_PALETTE_VIEW_H__ */
diff --git a/app/widgets/gimpuimanager.c b/app/widgets/gimpuimanager.c
index be7cd4ad46..c3300cb87d 100644
--- a/app/widgets/gimpuimanager.c
+++ b/app/widgets/gimpuimanager.c
@@ -801,6 +801,46 @@ gimp_ui_manager_ui_popup_at_pointer (GimpUIManager  *manager,
   gtk_menu_popup_at_pointer (GTK_MENU (menu), trigger_event);
 }
 
+void
+gimp_ui_manager_ui_popup_at_rect (GimpUIManager      *manager,
+                                  const gchar        *ui_path,
+                                  GdkWindow          *window,
+                                  const GdkRectangle *rect,
+                                  GdkGravity          rect_anchor,
+                                  GdkGravity          menu_anchor,
+                                  const GdkEvent     *trigger_event,
+                                  GDestroyNotify      popdown_func,
+                                  gpointer            popdown_data)
+{
+  GtkWidget *menu;
+
+  g_return_if_fail (GIMP_IS_UI_MANAGER (manager));
+  g_return_if_fail (ui_path != NULL);
+
+  menu = gimp_ui_manager_get_widget (manager, ui_path);
+
+  if (GTK_IS_MENU_ITEM (menu))
+    menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (menu));
+
+  if (! menu)
+    return;
+
+  g_return_if_fail (GTK_IS_MENU (menu));
+
+  if (popdown_func && popdown_data)
+    {
+      g_object_set_data_full (G_OBJECT (manager), "popdown-data",
+                              popdown_data, popdown_func);
+      g_signal_connect (menu, "selection-done",
+                        G_CALLBACK (gimp_ui_manager_delete_popdown_data),
+                        manager);
+    }
+
+  gtk_menu_popup_at_rect (GTK_MENU (menu), window,
+                          rect, rect_anchor, menu_anchor,
+                          trigger_event);
+}
+
 
 /*  private functions  */
 
diff --git a/app/widgets/gimpuimanager.h b/app/widgets/gimpuimanager.h
index 6c508d283b..2b6cabf5f6 100644
--- a/app/widgets/gimpuimanager.h
+++ b/app/widgets/gimpuimanager.h
@@ -148,6 +148,16 @@ void            gimp_ui_manager_ui_popup_at_pointer
                                              const GdkEvent         *trigger_event,
                                              GDestroyNotify          popdown_func,
                                              gpointer                popdown_data);
+void            gimp_ui_manager_ui_popup_at_rect
+                                            (GimpUIManager           *manager,
+                                             const gchar             *ui_path,
+                                             GdkWindow               *window,
+                                             const GdkRectangle      *rect,
+                                             GdkGravity               rect_anchor,
+                                             GdkGravity               menu_anchor,
+                                             const GdkEvent          *trigger_event,
+                                             GDestroyNotify           popdown_func,
+                                             gpointer                 popdown_data);
 
 
 #endif  /* __GIMP_UI_MANAGER_H__ */


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