[evince/891-provide-context-menu-for-annotations-in-sidebar] sidebar-annotations: Add context menu for sidebar annotations
- From: Nelson Benítez León <nbenitez src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evince/891-provide-context-menu-for-annotations-in-sidebar] sidebar-annotations: Add context menu for sidebar annotations
- Date: Mon, 4 Jun 2018 00:22:28 +0000 (UTC)
commit a817c8c6ea28a9afe315bcc1d2ace1583134e222
Author: Nelson Benítez León <nbenitezl+gnome gmail com>
Date: Fri Apr 27 23:27:13 2018 +0500
sidebar-annotations: Add context menu for sidebar annotations
Implement new contextual menu for annotations in sidebar, we
reuse annotation actions used by EvWindow in its context
menu, so same actions will be shown in both places.
We also implement the GtkWidget "popup" signal so the context
menu can also be triggered by keyboard (usually Shift-F10 or
specific context menu key in some keyboards).
https://bugzilla.gnome.org/show_bug.cgi?id=795631
shell/ev-sidebar-annotations.c | 222 +++++++++++++++++++++++++++++++++++++----
shell/ev-window.c | 7 ++
shell/ev-window.h | 2 +
shell/evince-menus.ui | 25 +++++
4 files changed, 236 insertions(+), 20 deletions(-)
---
diff --git a/shell/ev-sidebar-annotations.c b/shell/ev-sidebar-annotations.c
index 2815f827..0ba13385 100644
--- a/shell/ev-sidebar-annotations.c
+++ b/shell/ev-sidebar-annotations.c
@@ -28,6 +28,8 @@
#include "ev-jobs.h"
#include "ev-job-scheduler.h"
#include "ev-stock-icons.h"
+#include "ev-window.h"
+#include "ev-utils.h"
enum {
PROP_0,
@@ -52,13 +54,21 @@ struct _EvSidebarAnnotationsPrivate {
GtkWidget *swindow;
GtkWidget *tree_view;
+ GMenuModel *popup_model;
+ GtkWidget *popup;
+
EvJob *job;
guint selection_changed_id;
};
static void ev_sidebar_annotations_page_iface_init (EvSidebarPageInterface *iface);
static void ev_sidebar_annotations_load (EvSidebarAnnotations *sidebar_annots);
-
+static gboolean ev_sidebar_annotations_popup_menu (GtkWidget *widget);
+static gboolean ev_sidebar_annotations_popup_menu_show (EvSidebarAnnotations *sidebar_annots,
+ GdkWindow *rect_window,
+ const GdkRectangle *rect,
+ EvMapping *annot_mapping,
+ const GdkEvent *event);
static guint signals[N_SIGNALS];
G_DEFINE_TYPE_EXTENDED (EvSidebarAnnotations,
@@ -82,6 +92,7 @@ ev_sidebar_annotations_dispose (GObject *object)
priv->document = NULL;
}
+ g_clear_object (&priv->popup_model);
G_OBJECT_CLASS (ev_sidebar_annotations_parent_class)->dispose (object);
}
@@ -116,6 +127,7 @@ ev_sidebar_annotations_init (EvSidebarAnnotations *ev_annots)
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
GtkTreeSelection *selection;
+ GtkBuilder *builder;
ev_annots->priv = EV_SIDEBAR_ANNOTATIONS_GET_PRIVATE (ev_annots);
@@ -150,6 +162,10 @@ ev_sidebar_annotations_init (EvSidebarAnnotations *ev_annots)
gtk_container_add (GTK_CONTAINER (ev_annots->priv->swindow), ev_annots->priv->tree_view);
gtk_widget_show (ev_annots->priv->tree_view);
+ /* Annotation pop-up */
+ builder = gtk_builder_new_from_resource ("/org/gnome/evince/gtk/menus.ui");
+ ev_annots->priv->popup_model = g_object_ref (G_MENU_MODEL (gtk_builder_get_object (builder,
"annotation-popup")));
+ g_object_unref (builder);
gtk_box_pack_start (GTK_BOX (ev_annots), ev_annots->priv->swindow, TRUE, TRUE, 0);
gtk_widget_show (ev_annots->priv->swindow);
}
@@ -178,9 +194,11 @@ static void
ev_sidebar_annotations_class_init (EvSidebarAnnotationsClass *klass)
{
GObjectClass *g_object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *gtk_widget_class = GTK_WIDGET_CLASS (klass);
g_object_class->get_property = ev_sidebar_annotations_get_property;
g_object_class->dispose = ev_sidebar_annotations_dispose;
+ gtk_widget_class->popup_menu = ev_sidebar_annotations_popup_menu;
g_type_class_add_private (g_object_class, sizeof (EvSidebarAnnotationsPrivate));
@@ -218,17 +236,41 @@ ev_sidebar_annotations_annot_removed (EvSidebarAnnotations *sidebar_annots)
ev_sidebar_annotations_load (sidebar_annots);
}
+/**
+ * iter_has_mapping:
+ * @model: a #GtkTreeModel
+ * @iter: a #GtkTreeIter contained in @model
+ * @mapping_out: (out) (allow-none): if non-%NULL, will be
+ * filled with the found mapping (#EvMapping)
+ *
+ * Checks whether @iter contains a #EvMapping, optionally
+ * placing it in @mapping_out.
+ */
+static gboolean
+iter_has_mapping (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ EvMapping **mapping_out)
+{
+ EvMapping *mapping = NULL;
+
+ gtk_tree_model_get (model, iter,
+ COLUMN_ANNOT_MAPPING, &mapping,
+ -1);
+
+ if (mapping_out && mapping)
+ *mapping_out = mapping;
+
+ return mapping != NULL;
+}
+
static void
ev_sidebar_annotations_activate_result_at_iter (EvSidebarAnnotations *sidebar_annots,
GtkTreeModel *model,
GtkTreeIter *iter)
{
- EvMapping *mapping = NULL;
+ EvMapping *mapping;
- gtk_tree_model_get (model, iter,
- COLUMN_ANNOT_MAPPING, &mapping,
- -1);
- if (mapping)
+ if (iter_has_mapping (model, iter, &mapping))
g_signal_emit (sidebar_annots, signals[ANNOT_ACTIVATED], 0, mapping);
}
@@ -243,6 +285,43 @@ selection_changed_cb (GtkTreeSelection *selection,
ev_sidebar_annotations_activate_result_at_iter (sidebar_annots, model, &iter);
}
+static gboolean
+sidebar_tree_button_release_cb (GtkTreeView *view,
+ GdkEventButton *event,
+ EvSidebarAnnotations *sidebar_annots)
+{
+ GtkTreeModel *model;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ GtkTreeSelection *selection;
+ GdkRectangle rect;
+ EvMapping *annot_mapping;
+
+ gtk_tree_view_get_path_at_pos (view, event->x, event->y, &path,
+ NULL, NULL, NULL);
+ if (!path)
+ return GDK_EVENT_PROPAGATE;
+
+ model = gtk_tree_view_get_model (view);
+ gtk_tree_model_get_iter (model, &iter, path);
+ gtk_tree_path_free (path);
+ selection = gtk_tree_view_get_selection (view);
+
+ if (event->button == GDK_BUTTON_SECONDARY && event->type == GDK_BUTTON_RELEASE &&
+ iter_has_mapping (model, &iter, &annot_mapping) &&
+ gtk_tree_selection_iter_is_selected (selection, &iter)) {
+ rect.x = event->x;
+ rect.y = event->y;
+ rect.width = rect.height = 20;
+ ev_sidebar_annotations_popup_menu_show (sidebar_annots,
+ gtk_tree_view_get_bin_window (view),
+ &rect, annot_mapping, (GdkEvent *) event);
+ return GDK_EVENT_STOP;
+ }
+
+ return GDK_EVENT_PROPAGATE;
+}
+
static gboolean
sidebar_tree_button_press_cb (GtkTreeView *view,
GdkEventButton *event,
@@ -252,28 +331,41 @@ sidebar_tree_button_press_cb (GtkTreeView *view,
GtkTreePath *path;
GtkTreeIter iter;
GtkTreeSelection *selection;
+ EvMapping *annot_mapping;
+ GdkRectangle rect;
gtk_tree_view_get_path_at_pos (view, event->x, event->y, &path,
NULL, NULL, NULL);
if (!path)
- return FALSE;
-
+ return GDK_EVENT_PROPAGATE;
+
+ model = gtk_tree_view_get_model (view);
+ gtk_tree_model_get_iter (model, &iter, path);
+ gtk_tree_path_free (path);
selection = gtk_tree_view_get_selection (view);
- if (!gtk_tree_selection_path_is_selected (selection, path)) {
- gtk_tree_path_free (path);
- return FALSE;
- }
- model = gtk_tree_view_get_model (view);
- gtk_tree_model_get_iter (model, &iter, path);
- gtk_tree_path_free (path);
+ if (gdk_event_triggers_context_menu ((const GdkEvent *) event) &&
+ iter_has_mapping (model, &iter, &annot_mapping)) {
- ev_sidebar_annotations_activate_result_at_iter (sidebar_annots, model, &iter);
+ if (!EV_IS_ANNOTATION (annot_mapping->data))
+ return GDK_EVENT_PROPAGATE;
+
+ rect.x = event->x;
+ rect.y = event->y;
+ rect.width = rect.height = 20;
+ gtk_tree_selection_select_iter (selection, &iter);
+
+ return GDK_EVENT_STOP;
+ } else {
+ if (!gtk_tree_selection_iter_is_selected (selection, &iter))
+ gtk_tree_selection_select_iter (selection, &iter);
+ else
+ /* This will reveal annotation again in case was scrolled out of EvView */
+ ev_sidebar_annotations_activate_result_at_iter (sidebar_annots, model, &iter);
+ }
- /* Always return FALSE so the tree view gets the event and can update
- * the selection etc.
- */
- return FALSE;
+ /* Propagate so the tree view gets the event and can update the selection etc. */
+ return GDK_EVENT_PROPAGATE;
}
static void
@@ -317,6 +409,9 @@ job_finished_callback (EvJobAnnots *job,
g_signal_connect (priv->tree_view, "button-press-event",
G_CALLBACK (sidebar_tree_button_press_cb),
sidebar_annots);
+ g_signal_connect (priv->tree_view, "button-release-event",
+ G_CALLBACK (sidebar_tree_button_release_cb),
+ sidebar_annots);
model = gtk_tree_store_new (N_COLUMNS,
@@ -488,6 +583,93 @@ ev_sidebar_annotations_document_changed_cb (EvDocumentModel *model,
ev_sidebar_annotations_load (sidebar_annots);
}
+static void
+ev_sidebar_annotations_popup_detach_cb (GtkWidget *widget,
+ GtkMenu *menu)
+{
+ EvSidebarAnnotations *sidebar_annots = EV_SIDEBAR_ANNOTATIONS (widget);
+ sidebar_annots->priv->popup = NULL;
+}
+
+/* event parameter can be NULL, that means popup was triggered from keyboard */
+static gboolean
+ev_sidebar_annotations_popup_menu_show (EvSidebarAnnotations *sidebar_annots,
+ GdkWindow *rect_window,
+ const GdkRectangle *rect,
+ EvMapping *annot_mapping,
+ const GdkEvent *event)
+{
+ GtkWidget *window;
+
+ EvSidebarAnnotationsPrivate *priv = sidebar_annots->priv;
+
+ if (!EV_IS_ANNOTATION (annot_mapping->data))
+ return FALSE;
+
+ if (!priv->popup) {
+ priv->popup = gtk_menu_new_from_model (priv->popup_model);
+ gtk_menu_attach_to_widget (GTK_MENU (priv->popup), GTK_WIDGET (sidebar_annots),
+ ev_sidebar_annotations_popup_detach_cb);
+ }
+
+ window = gtk_widget_get_toplevel (GTK_WIDGET (sidebar_annots));
+
+ ev_window_handle_annot_popup (EV_WINDOW (window), EV_ANNOTATION (annot_mapping->data));
+
+#if GTK_CHECK_VERSION (3, 22, 0)
+ gtk_menu_popup_at_rect (GTK_MENU (priv->popup), rect_window, rect,
+ GDK_GRAVITY_SOUTH_WEST, GDK_GRAVITY_NORTH_WEST, event);
+#else
+ if (event != NULL)
+ gtk_menu_popup_for_device (GTK_MENU (priv->popup),
+ gdk_event_get_device (event),
+ NULL, NULL, NULL, NULL, NULL, 0,
+ gdk_event_get_time (event));
+ else
+ /* popup was triggered from keyboard */
+ gtk_menu_popup (GTK_MENU (priv->popup), NULL, NULL,
+ ev_gui_menu_position_tree_selection,
+ priv->tree_view, 0,
+ gtk_get_current_event_time ());
+#endif
+
+ return TRUE;
+}
+
+static gboolean
+ev_sidebar_annotations_popup_menu (GtkWidget *widget)
+{
+ GtkTreeView *tree_view;
+ GtkTreeModel *model;
+ GtkTreePath *path;
+ GtkTreeSelection *selection;
+ EvMapping *annot_mapping;
+ GtkTreeIter iter;
+ GdkRectangle rect;
+
+ EvSidebarAnnotations *sidebar_annots = EV_SIDEBAR_ANNOTATIONS (widget);
+ EvSidebarAnnotationsPrivate *priv = sidebar_annots->priv;
+
+ tree_view = GTK_TREE_VIEW (priv->tree_view);
+ selection = gtk_tree_view_get_selection (tree_view);
+ if (!gtk_tree_selection_get_selected (selection, &model, &iter))
+ return FALSE;
+
+ if (!iter_has_mapping (model, &iter, &annot_mapping))
+ return FALSE;
+
+ path = gtk_tree_model_get_path (model, &iter);
+
+ gtk_tree_view_get_cell_area (tree_view, path,
+ gtk_tree_view_get_column (tree_view, 0),
+ &rect);
+ gtk_tree_path_free (path);
+
+ return ev_sidebar_annotations_popup_menu_show (sidebar_annots,
+ gtk_tree_view_get_bin_window (tree_view),
+ &rect, annot_mapping, NULL);
+}
+
/* EvSidebarPageIface */
static void
ev_sidebar_annotations_set_model (EvSidebarPage *sidebar_page,
diff --git a/shell/ev-window.c b/shell/ev-window.c
index 36a6d031..4de2a0fa 100644
--- a/shell/ev-window.c
+++ b/shell/ev-window.c
@@ -5391,6 +5391,13 @@ search_bar_search_mode_enabled_changed (GtkSearchBar *search_bar,
}
}
+void
+ev_window_handle_annot_popup (EvWindow *ev_window,
+ EvAnnotation *annot)
+{
+ view_menu_annot_popup (ev_window, annot);
+}
+
static void
ev_window_show_find_bar (EvWindow *ev_window,
gboolean restart)
diff --git a/shell/ev-window.h b/shell/ev-window.h
index 28d031a8..dd5afff6 100644
--- a/shell/ev-window.h
+++ b/shell/ev-window.h
@@ -95,6 +95,8 @@ EvDocumentModel *ev_window_get_document_model (EvWindow *ev_win
void ev_window_focus_view (EvWindow *ev_window);
GtkWidget *ev_window_get_toolbar (EvWindow *ev_window);
void ev_window_file_open_dialog (EvWindow *ev_window);
+void ev_window_handle_annot_popup (EvWindow *ev_window,
+ EvAnnotation *annot);
G_END_DECLS
diff --git a/shell/evince-menus.ui b/shell/evince-menus.ui
index 73a8df18..2d74b1a4 100644
--- a/shell/evince-menus.ui
+++ b/shell/evince-menus.ui
@@ -326,4 +326,29 @@
</item>
</section>
</menu>
+
+ <menu id="annotation-popup">
+ <section>
+ <item>
+ <attribute name="label" translatable="yes">Annotation Properties…</attribute>
+ <attribute name="action">win.annot-properties</attribute>
+ <attribute name='hidden-when'>action-disabled</attribute>
+ </item>
+ <item>
+ <attribute name="label" translatable="yes">Remove Annotation</attribute>
+ <attribute name="action">win.remove-annot</attribute>
+ <attribute name='hidden-when'>action-disabled</attribute>
+ </item>
+ <item>
+ <attribute name="label" translatable="yes">_Open Attachment</attribute>
+ <attribute name="action">win.open-attachment</attribute>
+ <attribute name='hidden-when'>action-disabled</attribute>
+ </item>
+ <item>
+ <attribute name="label" translatable="yes">_Save Attachment As…</attribute>
+ <attribute name="action">win.save-attachment</attribute>
+ <attribute name='hidden-when'>action-disabled</attribute>
+ </item>
+ </section>
+ </menu>
</interface>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]