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.
This commit is contained in:
Niels De Graef 2021-12-08 00:09:08 +01:00
parent 67fd1f0c52
commit eed28c0941
17 changed files with 297 additions and 274 deletions

View File

@ -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)
{
gimp_editor_popup_menu (editor, NULL, NULL);
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)
{
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

View File

@ -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,

View File

@ -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__ */

View File

@ -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,

View File

@ -50,8 +50,6 @@ struct _GimpContainerEditorClass
GimpViewable *object);
void (* activate_item) (GimpContainerEditor *editor,
GimpViewable *object);
void (* context_item) (GimpContainerEditor *editor,
GimpViewable *object);
};

View File

@ -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)
{
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);
*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;
}
gimp_menu_position (menu, x, y);
}
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);
GimpContainerIconView *icon_view = GIMP_CONTAINER_ICON_VIEW (widget);
GtkTreeIter iter;
GtkTreePath *path;
GdkRectangle rect;
if (!gimp_container_icon_view_get_selected_single (icon_view, &iter))
return FALSE;
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);
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,6 +595,7 @@ gimp_container_icon_view_button_press (GtkWidget *widget,
GdkEventButton *bevent,
GimpContainerIconView *icon_view)
{
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;
}

View File

@ -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)
{
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);
*x += border.left;
*y += border.top;
}
gimp_menu_position (menu, x, y);
}
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);
GimpContainerTreeView *tree_view = GIMP_CONTAINER_TREE_VIEW (widget);
GtkTreeIter iter;
GtkTreePath *path;
GdkRectangle rect;
if (!gimp_container_tree_view_get_selected_single (tree_view, &iter))
return FALSE;
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);
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)
{

View File

@ -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,

View File

@ -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 */

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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)
{
gimp_editor_popup_menu (GIMP_EDITOR (editor), NULL, NULL);
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)
{
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

View File

@ -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 */

View File

@ -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__ */

View File

@ -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 */

View File

@ -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__ */