[gtk+/wip/gbsneto/other-locations] placesview: add context menu
- From: Georges Basile Stavracas Neto <gbsneto src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk+/wip/gbsneto/other-locations] placesview: add context menu
- Date: Fri, 3 Jul 2015 03:19:53 +0000 (UTC)
commit 7bf55caef66db582369acba7e04e2d5271f7ed80
Author: Georges Basile Stavracas Neto <georges stavracas gmail com>
Date: Fri Jul 3 00:17:37 2015 -0300
placesview: add context menu
Add a context menu, allowing the user to:
- open a location normally
- open in new tab (if possible)
- open in new window (if possible)
- mount a volume if unmounted
- unmount a volume if mounted
gtk/gtkplacesview.c | 311 ++++++++++++++++++++++++++++++++++++++++++++
gtk/gtkplacesviewrow.c | 12 ++
gtk/gtkplacesviewrow.h | 2 +
gtk/ui/gtkplacesview.ui | 1 +
gtk/ui/gtkplacesviewrow.ui | 96 +++++++-------
5 files changed, 376 insertions(+), 46 deletions(-)
---
diff --git a/gtk/gtkplacesview.c b/gtk/gtkplacesview.c
index 9f54e18..2ab47c2 100644
--- a/gtk/gtkplacesview.c
+++ b/gtk/gtkplacesview.c
@@ -59,6 +59,7 @@ struct _GtkPlacesViewPrivate
GtkWidget *drives_listbox;
GtkWidget *network_grid;
GtkWidget *network_listbox;
+ GtkWidget *popup_menu;
GtkWidget *recent_servers_listbox;
guint local_only : 1;
@@ -67,6 +68,10 @@ struct _GtkPlacesViewPrivate
static void mount_volume (GtkPlacesView *view,
GVolume *volume);
+static gboolean on_button_release_event (GtkWidget *widget,
+ GdkEventButton *event,
+ GtkPlacesViewRow *sidebar);
+
G_DEFINE_TYPE_WITH_PRIVATE (GtkPlacesView, gtk_places_view, GTK_TYPE_BOX)
/* GtkPlacesView properties & signals */
@@ -515,6 +520,11 @@ add_volume (GtkPlacesView *view,
"mount", mount,
NULL);
+ g_signal_connect (gtk_places_view_row_get_event_box (GTK_PLACES_VIEW_ROW (row)),
+ "button-release-event",
+ G_CALLBACK (on_button_release_event),
+ row);
+
if (is_network)
gtk_container_add (GTK_CONTAINER (priv->network_listbox), row);
else
@@ -589,6 +599,11 @@ add_mount (GtkPlacesView *view,
"mount", mount,
NULL);
+ g_signal_connect (gtk_places_view_row_get_event_box (GTK_PLACES_VIEW_ROW (row)),
+ "button-release-event",
+ G_CALLBACK (on_button_release_event),
+ row);
+
if (is_network)
gtk_container_add (GTK_CONTAINER (priv->network_listbox), row);
else
@@ -803,6 +818,33 @@ volume_mount_ready_callback (GObject *source_volume,
}
static void
+unmount_ready_callback (GObject *source_mount,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GtkPlacesViewPrivate *priv;
+ GMount *mount;
+ GError *error;
+
+ g_return_if_fail (GTK_IS_PLACES_VIEW (user_data));
+ g_return_if_fail (G_IS_MOUNT (source_mount));
+
+ priv = GTK_PLACES_VIEW (user_data)->priv;
+ mount = G_MOUNT (source_mount);
+ error = NULL;
+
+ g_clear_object (&priv->connection_cancellable);
+
+ g_mount_unmount_with_operation_finish (mount, res, &error);
+
+ if (error)
+ {
+ g_warning ("Unable to unmount mountpoint: %s", error->message);
+ g_clear_error (&error);
+ }
+}
+
+static void
mount_location (GtkPlacesView *view,
GFile *location)
{
@@ -860,6 +902,275 @@ mount_volume (GtkPlacesView *view,
g_object_unref (operation);
}
+/* Callback used when the file list's popup menu is detached */
+static void
+popup_menu_detach_cb (GtkWidget *attach_widget,
+ GtkMenu *menu)
+{
+ GtkPlacesViewPrivate *priv;
+
+ g_assert (GTK_IS_PLACES_VIEW (attach_widget));
+
+ priv = GTK_PLACES_VIEW (attach_widget)->priv;
+
+ priv->popup_menu = NULL;
+}
+
+static void
+get_view_and_file (GtkPlacesViewRow *row,
+ GtkWidget **view,
+ GFile **file)
+{
+ g_assert (GTK_IS_PLACES_VIEW_ROW (row));
+
+ if (view)
+ *view = gtk_widget_get_ancestor (GTK_WIDGET (row), GTK_TYPE_PLACES_VIEW);
+
+ if (file)
+ {
+ GVolume *volume;
+ GMount *mount;
+
+ volume = gtk_places_view_row_get_volume (row);
+ mount = gtk_places_view_row_get_mount (row);
+
+ if (mount)
+ *file = g_mount_get_root (mount);
+ else if (volume)
+ *file = g_volume_get_activation_root (volume);
+ else
+ *file = NULL;
+ }
+}
+
+static void
+open_cb (GtkMenuItem *item,
+ GtkPlacesViewRow *row)
+{
+ GtkWidget *view;
+ GFile *file;
+
+ get_view_and_file (row, &view, &file);
+
+ if (!view)
+ return;
+
+ emit_open_location (GTK_PLACES_VIEW (view), file, GTK_PLACES_OPEN_NORMAL);
+
+ g_clear_object (&file);
+}
+
+static void
+open_in_new_tab_cb (GtkMenuItem *item,
+ GtkPlacesViewRow *row)
+{
+ GtkWidget *view;
+ GFile *file;
+
+ get_view_and_file (row, &view, &file);
+
+ if (!view)
+ return;
+
+ emit_open_location (GTK_PLACES_VIEW (view), file, GTK_PLACES_OPEN_NEW_TAB);
+
+ g_clear_object (&file);
+}
+
+static void
+open_in_new_window_cb (GtkMenuItem *item,
+ GtkPlacesViewRow *row)
+{
+ GtkWidget *view;
+ GFile *file;
+
+ get_view_and_file (row, &view, &file);
+
+ if (!view)
+ return;
+
+ emit_open_location (GTK_PLACES_VIEW (view), file, GTK_PLACES_OPEN_NEW_WINDOW);
+
+ g_clear_object (&file);
+}
+
+static void
+mount_cb (GtkMenuItem *item,
+ GtkPlacesViewRow *row)
+{
+ GtkWidget *view;
+ GVolume *volume;
+
+ view = gtk_widget_get_ancestor (GTK_WIDGET (row), GTK_TYPE_PLACES_VIEW);
+ volume = gtk_places_view_row_get_volume (row);
+
+ if (!view || !volume)
+ return;
+
+ mount_volume (GTK_PLACES_VIEW (view), volume);
+}
+
+static void
+unmount_cb (GtkMenuItem *item,
+ GtkPlacesViewRow *row)
+{
+ GMountOperation *mount_op;
+ GtkWidget *view;
+ GMount *mount;
+
+ view = gtk_widget_get_ancestor (GTK_WIDGET (row), GTK_TYPE_PLACES_VIEW);
+ mount = gtk_places_view_row_get_mount (row);
+
+ if (!view || !mount)
+ return;
+
+ mount_op = gtk_mount_operation_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (row))));
+ g_mount_unmount_with_operation (mount,
+ 0,
+ mount_op,
+ NULL,
+ unmount_ready_callback,
+ view);
+ g_object_unref (mount_op);
+}
+
+/* Constructs the popup menu if needed */
+static void
+build_popup_menu (GtkPlacesView *view,
+ GtkPlacesViewRow *row)
+{
+ GtkPlacesViewPrivate *priv;
+ GtkWidget *item;
+ GMount *mount;
+
+ g_assert (GTK_IS_PLACES_VIEW (view));
+ g_assert (GTK_IS_PLACES_VIEW_ROW (row));
+
+ priv = view->priv;
+ mount = gtk_places_view_row_get_mount (row);
+
+ 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 (view),
+ popup_menu_detach_cb);
+
+ item = gtk_menu_item_new_with_mnemonic (_("_Open"));
+ g_signal_connect (item,
+ "activate",
+ G_CALLBACK (open_cb),
+ row);
+ gtk_widget_show (item);
+ gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), item);
+
+ if (priv->open_flags & GTK_PLACES_OPEN_NEW_TAB)
+ {
+ item = gtk_menu_item_new_with_mnemonic (_("Open in New _Tab"));
+ g_signal_connect (item,
+ "activate",
+ G_CALLBACK (open_in_new_tab_cb),
+ row);
+ gtk_widget_show (item);
+ gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), item);
+ }
+
+ if (priv->open_flags & GTK_PLACES_OPEN_NEW_WINDOW)
+ {
+ item = gtk_menu_item_new_with_mnemonic (_("Open in New _Window"));
+ g_signal_connect (item,
+ "activate",
+ G_CALLBACK (open_in_new_window_cb),
+ row);
+ gtk_widget_show (item);
+ gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), item);
+ }
+
+ /* Separator */
+ item = gtk_separator_menu_item_new ();
+ gtk_widget_show (item);
+ gtk_menu_shell_insert (GTK_MENU_SHELL (priv->popup_menu), item, -1);
+
+ /* Mount/Unmount items */
+ if (mount)
+ {
+ item = gtk_menu_item_new_with_mnemonic (_("_Unmount"));
+ g_signal_connect (item,
+ "activate",
+ G_CALLBACK (unmount_cb),
+ row);
+ gtk_widget_show (item);
+ gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), item);
+ }
+ else
+ {
+ item = gtk_menu_item_new_with_mnemonic (_("_Mount"));
+ g_signal_connect (item,
+ "activate",
+ G_CALLBACK (mount_cb),
+ row);
+ gtk_widget_show (item);
+ gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), item);
+ }
+}
+
+static void
+popup_menu (GtkPlacesViewRow *row,
+ GdkEventButton *event)
+{
+ GtkPlacesViewPrivate *priv;
+ GtkWidget *view;
+ gint button;
+
+ view = gtk_widget_get_ancestor (GTK_WIDGET (row), GTK_TYPE_PLACES_VIEW);
+ priv = GTK_PLACES_VIEW (view)->priv;
+
+ g_clear_pointer (&priv->popup_menu, gtk_widget_destroy);
+
+ build_popup_menu (GTK_PLACES_VIEW (view), row);
+
+ /* The event button needs to be 0 if we're popping up this menu from
+ * a button release, else a 2nd click outside the menu with any button
+ * other than the one that invoked the menu will be ignored (instead
+ * of dismissing the menu). This is a subtle fragility of the GTK menu code.
+ */
+ if (event)
+ {
+ if (event->type == GDK_BUTTON_RELEASE)
+ button = 0;
+ else
+ button = event->button;
+ }
+ else
+ {
+ button = 0;
+ }
+
+ gtk_menu_popup (GTK_MENU (priv->popup_menu),
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ button,
+ event ? event->time : gtk_get_current_event_time ());
+}
+
+static gboolean
+on_button_release_event (GtkWidget *widget,
+ GdkEventButton *event,
+ GtkPlacesViewRow *row)
+{
+ if (row &&
+ event &&
+ event->button == 3)
+ {
+ popup_menu (row, event);
+ }
+
+ return TRUE;
+}
+
static void
on_connect_button_clicked (GtkPlacesView *view)
{
diff --git a/gtk/gtkplacesviewrow.c b/gtk/gtkplacesviewrow.c
index 30edbbf..6152f11 100644
--- a/gtk/gtkplacesviewrow.c
+++ b/gtk/gtkplacesviewrow.c
@@ -29,12 +29,15 @@ struct _GtkPlacesViewRow
{
GtkListBoxRow parent_instance;
+ GtkEventBox *event_box;
GtkImage *icon_image;
GtkLabel *name_label;
GtkLabel *path_label;
GVolume *volume;
GMount *mount;
+
+ GdkWindow *event_window;
};
G_DEFINE_TYPE (GtkPlacesViewRow, gtk_places_view_row, GTK_TYPE_LIST_BOX_ROW)
@@ -176,6 +179,7 @@ gtk_places_view_row_class_init (GtkPlacesViewRowClass *klass)
gtk_widget_class_set_template_from_resource (widget_class, "/org/gtk/libgtk/ui/gtkplacesviewrow.ui");
+ gtk_widget_class_bind_template_child (widget_class, GtkPlacesViewRow, event_box);
gtk_widget_class_bind_template_child (widget_class, GtkPlacesViewRow, icon_image);
gtk_widget_class_bind_template_child (widget_class, GtkPlacesViewRow, name_label);
gtk_widget_class_bind_template_child (widget_class, GtkPlacesViewRow, path_label);
@@ -212,3 +216,11 @@ gtk_places_view_row_get_volume (GtkPlacesViewRow *row)
return row->volume;
}
+
+GtkWidget*
+gtk_places_view_row_get_event_box (GtkPlacesViewRow *row)
+{
+ g_return_val_if_fail (GTK_IS_PLACES_VIEW_ROW (row), NULL);
+
+ return GTK_WIDGET (row->event_box);
+}
diff --git a/gtk/gtkplacesviewrow.h b/gtk/gtkplacesviewrow.h
index 2746a35..6de480d 100644
--- a/gtk/gtkplacesviewrow.h
+++ b/gtk/gtkplacesviewrow.h
@@ -34,6 +34,8 @@ G_DECLARE_FINAL_TYPE (GtkPlacesViewRow, gtk_places_view_row, GTK, PLACES_VIEW_RO
GtkWidget* gtk_places_view_row_new (GVolume *volume,
GMount *mount);
+GtkWidget* gtk_places_view_row_get_event_box (GtkPlacesViewRow *row);
+
GMount* gtk_places_view_row_get_mount (GtkPlacesViewRow *row);
GVolume* gtk_places_view_row_get_volume (GtkPlacesViewRow *row);
diff --git a/gtk/ui/gtkplacesview.ui b/gtk/ui/gtkplacesview.ui
index 68ef7de..ce4ef83 100644
--- a/gtk/ui/gtkplacesview.ui
+++ b/gtk/ui/gtkplacesview.ui
@@ -40,6 +40,7 @@
<object class="GtkListBox" id="recent_servers_listbox">
<property name="visible">True</property>
<property name="can_focus">True</property>
+ <property name="selection_mode">none</property>
</object>
</child>
</object>
diff --git a/gtk/ui/gtkplacesviewrow.ui b/gtk/ui/gtkplacesviewrow.ui
index c17a1ed..1ccee77 100644
--- a/gtk/ui/gtkplacesviewrow.ui
+++ b/gtk/ui/gtkplacesviewrow.ui
@@ -1,5 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Generated with glade 3.18.1 -->
<interface>
<requires lib="gtk+" version="3.16"/>
<template class="GtkPlacesViewRow" parent="GtkListBoxRow">
@@ -10,56 +9,61 @@
<class name="volume-row" />
</style>
<child>
- <object class="GtkBox" id="box">
+ <object class="GtkEventBox" id="event_box">
<property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="margin_start">12</property>
- <property name="margin_end">12</property>
- <property name="margin_top">6</property>
- <property name="margin_bottom">6</property>
- <property name="border_width">0</property>
- <property name="spacing">18</property>
<child>
- <object class="GtkImage" id="icon_image">
+ <object class="GtkBox" id="box">
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="pixel_size">32</property>
+ <property name="margin_start">12</property>
+ <property name="margin_end">12</property>
+ <property name="margin_top">6</property>
+ <property name="margin_bottom">6</property>
+ <property name="border_width">0</property>
+ <property name="spacing">18</property>
+ <child>
+ <object class="GtkImage" id="icon_image">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="pixel_size">32</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="name_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="path_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="justify">right</property>
+ <property name="ellipsize">middle</property>
+ <property name="xalign">1</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
</object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="name_label">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="hexpand">True</property>
- <property name="xalign">0</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="path_label">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="justify">right</property>
- <property name="ellipsize">middle</property>
- <property name="xalign">1</property>
- <style>
- <class name="dim-label"/>
- </style>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">2</property>
- </packing>
</child>
</object>
</child>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]