c95e70e418
Fixes rhbz#1822912
518 lines
15 KiB
Diff
518 lines
15 KiB
Diff
From 5757ab6a8a0af67413f19602a388cb1c2b115676 Mon Sep 17 00:00:00 2001
|
|
From: Felipe Borges <felipeborges@gnome.org>
|
|
Date: Fri, 20 Dec 2019 13:53:14 +0100
|
|
Subject: [PATCH] EogWindow: Use Portals for "Open With" when possible
|
|
|
|
If we detect that Eog is running inside a Flatpak container, we
|
|
can use the OpenURI Portal[0] to open an AppChooser dialog for
|
|
the user to pick an appliation.
|
|
|
|
[0] https://flatpak.github.io/xdg-desktop-portal/portal-docs.html#gdbus-org.freedesktop.portal.OpenURI
|
|
|
|
Fixes #98
|
|
---
|
|
data/eog-gear-menu.ui | 9 +--
|
|
data/popup-menus.ui | 7 +-
|
|
src/eog-util.c | 180 ++++++++++++++++++++++++++++++++++++++++++
|
|
src/eog-util.h | 6 ++
|
|
src/eog-window.c | 140 +++++++++++---------------------
|
|
5 files changed, 240 insertions(+), 102 deletions(-)
|
|
|
|
diff --git a/data/eog-gear-menu.ui b/data/eog-gear-menu.ui
|
|
index 1e9cb4a3..724156b7 100644
|
|
--- a/data/eog-gear-menu.ui
|
|
+++ b/data/eog-gear-menu.ui
|
|
@@ -6,11 +6,10 @@
|
|
<attribute name="label" translatable="yes">_Open…</attribute>
|
|
<attribute name="action">win.open</attribute>
|
|
</item>
|
|
- <submenu>
|
|
- <attribute name="label" translatable="yes">Op_en With</attribute>
|
|
- <link name="submenu" id="open-with-menu">
|
|
- </link>
|
|
- </submenu>
|
|
+ <item>
|
|
+ <attribute name="label" translatable="yes">Op_en With…</attribute>
|
|
+ <attribute name="action">win.open-with</attribute>
|
|
+ </item>
|
|
</section>
|
|
<section>
|
|
<item>
|
|
diff --git a/data/popup-menus.ui b/data/popup-menus.ui
|
|
index aae780c0..ee629d74 100644
|
|
--- a/data/popup-menus.ui
|
|
+++ b/data/popup-menus.ui
|
|
@@ -2,11 +2,10 @@
|
|
<interface>
|
|
<menu id="view-popup-menu">
|
|
<section>
|
|
- <submenu>
|
|
+ <item>
|
|
<attribute name="label" translatable="yes">Open _with</attribute>
|
|
- <link name="submenu" id="open-with-menu">
|
|
- </link>
|
|
- </submenu>
|
|
+ <attribute name="action">win.open-with</attribute>
|
|
+ </item>
|
|
</section>
|
|
<section>
|
|
<item>
|
|
diff --git a/src/eog-util.c b/src/eog-util.c
|
|
index a6828a58..3e253dc9 100644
|
|
--- a/src/eog-util.c
|
|
+++ b/src/eog-util.c
|
|
@@ -22,6 +22,9 @@
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
+/* For O_PATH */
|
|
+#define _GNU_SOURCE
|
|
+
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
@@ -37,12 +40,16 @@
|
|
#include "eog-debug.h"
|
|
|
|
#include <errno.h>
|
|
+#include <fcntl.h>
|
|
#include <string.h>
|
|
#include <glib.h>
|
|
#include <glib/gprintf.h>
|
|
#include <gtk/gtk.h>
|
|
#include <gio/gio.h>
|
|
+#include <gio/gunixfdlist.h>
|
|
#include <glib/gi18n.h>
|
|
+#include <sys/stat.h>
|
|
+#include <sys/types.h>
|
|
|
|
void
|
|
eog_util_show_help (const gchar *section, GtkWindow *parent)
|
|
@@ -503,3 +510,176 @@ eog_util_show_file_in_filemanager (GFile *file, GtkWindow *toplevel)
|
|
if (!done)
|
|
_eog_util_show_file_in_filemanager_fallback (file, toplevel);
|
|
}
|
|
+
|
|
+/* Portal */
|
|
+
|
|
+gboolean
|
|
+eog_util_is_running_inside_flatpak (void)
|
|
+{
|
|
+ return g_file_test ("/.flatpak-info", G_FILE_TEST_EXISTS);
|
|
+}
|
|
+
|
|
+static void
|
|
+response_cb (GDBusConnection *connection, const char *sender_name, const char *object_path, const char *interface_name, const char *signal_name, GVariant *parameters, gpointer user_data)
|
|
+{
|
|
+ GFile *file = G_FILE (user_data);
|
|
+ guint32 response;
|
|
+ guint signal_id;
|
|
+
|
|
+ signal_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (file), "signal-id"));
|
|
+ g_dbus_connection_signal_unsubscribe (connection, signal_id);
|
|
+
|
|
+ g_variant_get (parameters, "(u@a{sv})", &response, NULL);
|
|
+ if (response == 0) {
|
|
+ g_debug ("Opening file");
|
|
+ } else if (response == 1) {
|
|
+ g_debug ("User cancelled opening file");
|
|
+ } else {
|
|
+ g_warning ("Failed to open file via portal");
|
|
+ }
|
|
+}
|
|
+
|
|
+static void
|
|
+open_file_complete_cb (GObject *source, GAsyncResult *result, gpointer user_data)
|
|
+{
|
|
+ GDBusProxy *proxy = G_DBUS_PROXY (source);
|
|
+ GFile *file = G_FILE (user_data);
|
|
+ GVariant *return_value = NULL;
|
|
+ const char *handle;
|
|
+ char *object_path = NULL;
|
|
+ GError *error = NULL;
|
|
+
|
|
+ return_value = g_dbus_proxy_call_with_unix_fd_list_finish (proxy, NULL, result, &error);
|
|
+ if (!return_value) {
|
|
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
|
+ g_warning ("Failed to open file via portal: %s", error->message);
|
|
+
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ g_variant_get (return_value, "(o)", &object_path);
|
|
+ handle = (const char *)g_object_get_data (G_OBJECT (file), "handle");
|
|
+ if (strcmp (handle, object_path) != 0) {
|
|
+ GDBusConnection *connection;
|
|
+ guint signal_id;
|
|
+
|
|
+ connection = g_dbus_proxy_get_connection (proxy);
|
|
+ signal_id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (file), "signal-id"));
|
|
+ g_dbus_connection_signal_unsubscribe (connection, signal_id);
|
|
+
|
|
+ signal_id = g_dbus_connection_signal_subscribe (connection,
|
|
+ "org.freedesktop.portal.Desktop",
|
|
+ "org.freedesktop.portal.Request",
|
|
+ "Response",
|
|
+ handle,
|
|
+ NULL,
|
|
+ G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE,
|
|
+ response_cb,
|
|
+ file,
|
|
+ NULL);
|
|
+ g_object_set_data (G_OBJECT (file), "signal-id", GUINT_TO_POINTER (signal_id));
|
|
+ }
|
|
+
|
|
+out:
|
|
+ if (return_value)
|
|
+ g_variant_unref (return_value);
|
|
+ if (object_path)
|
|
+ g_free (object_path);
|
|
+}
|
|
+
|
|
+static void
|
|
+open_with_flatpak_portal_cb (GObject *source, GAsyncResult *result, gpointer user_data)
|
|
+{
|
|
+ GVariantBuilder builder;
|
|
+ GUnixFDList *fd_list;
|
|
+ GFile *file;
|
|
+ GDBusProxy *proxy;
|
|
+ GDBusConnection *connection;
|
|
+ GError *error = NULL;
|
|
+ guint signal_id;
|
|
+ char *sender, *token, *handle;
|
|
+ int fd;
|
|
+
|
|
+ file = G_FILE (user_data);
|
|
+ fd = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (file), "fd"));
|
|
+
|
|
+ proxy = g_dbus_proxy_new_for_bus_finish (result, &error);
|
|
+ if (!proxy) {
|
|
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
|
+ g_warning ("Failed to create D-Bus proxy for OpenURI portal: %s", error->message);
|
|
+
|
|
+ close (fd);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ connection = g_dbus_proxy_get_connection (proxy);
|
|
+ sender = g_strdup (g_dbus_connection_get_unique_name (connection) + 1);
|
|
+ for (guint i = 0; sender[i] != '\0'; i++) {
|
|
+ if (sender[i] == '.') {
|
|
+ sender[i] = '_';
|
|
+ }
|
|
+ }
|
|
+
|
|
+ token = g_strdup_printf ("eog%u", g_random_int ());
|
|
+ handle = g_strdup_printf ("/org/freedesktop/portal/desktop/request/%s/%s", sender, token);
|
|
+
|
|
+ g_object_set_data_full (G_OBJECT (file), "handle", handle, g_free);
|
|
+ g_free (sender);
|
|
+
|
|
+ signal_id = g_dbus_connection_signal_subscribe (connection,
|
|
+ "org.freedesktop.portal.Desktop",
|
|
+ "org.freedesktop.portal.Request",
|
|
+ "Response",
|
|
+ handle,
|
|
+ NULL,
|
|
+ G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE,
|
|
+ response_cb,
|
|
+ file,
|
|
+ NULL);
|
|
+ g_object_set_data (G_OBJECT (file), "signal-id", GUINT_TO_POINTER (signal_id));
|
|
+
|
|
+ g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
|
|
+ g_variant_builder_add (&builder, "{sv}", "handle_token", g_variant_new_string (token));
|
|
+ g_variant_builder_add (&builder, "{sv}", "ask", g_variant_new ("b", TRUE));
|
|
+ g_free (token);
|
|
+
|
|
+ fd_list = g_unix_fd_list_new_from_array (&fd, 1);
|
|
+ g_dbus_proxy_call_with_unix_fd_list (proxy,
|
|
+ "OpenFile",
|
|
+ g_variant_new ("(s@h@a{sv})",
|
|
+ "",
|
|
+ g_variant_new ("h", 0),
|
|
+ g_variant_builder_end (&builder)),
|
|
+ G_DBUS_CALL_FLAGS_NONE,
|
|
+ -1,
|
|
+ fd_list,
|
|
+ NULL,
|
|
+ open_file_complete_cb,
|
|
+ file);
|
|
+ g_object_unref (fd_list);
|
|
+}
|
|
+
|
|
+void
|
|
+eog_util_open_file_with_flatpak_portal (GFile *file)
|
|
+{
|
|
+ const gchar *path;
|
|
+ int fd;
|
|
+
|
|
+ path = g_file_get_path (file);
|
|
+ fd = open (path, O_PATH | O_CLOEXEC);
|
|
+ if (fd == -1) {
|
|
+ g_warning ("Failed to open %s: %s", path, g_strerror (errno));
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ g_object_set_data (G_OBJECT (file), "fd", GINT_TO_POINTER (fd));
|
|
+ g_dbus_proxy_new_for_bus (G_BUS_TYPE_SESSION,
|
|
+ G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
|
|
+ NULL,
|
|
+ "org.freedesktop.portal.Desktop",
|
|
+ "/org/freedesktop/portal/desktop",
|
|
+ "org.freedesktop.portal.OpenURI",
|
|
+ NULL,
|
|
+ open_with_flatpak_portal_cb,
|
|
+ file);
|
|
+}
|
|
diff --git a/src/eog-util.h b/src/eog-util.h
|
|
index 9f2ad4c1..d328e1f9 100644
|
|
--- a/src/eog-util.h
|
|
+++ b/src/eog-util.h
|
|
@@ -69,6 +69,12 @@ G_GNUC_INTERNAL
|
|
void eog_util_show_file_in_filemanager (GFile *file,
|
|
GtkWindow *toplevel);
|
|
|
|
+G_GNUC_INTERNAL
|
|
+gboolean eog_util_is_running_inside_flatpak (void);
|
|
+
|
|
+G_GNUC_INTERNAL
|
|
+void eog_util_open_file_with_flatpak_portal (GFile *file);
|
|
+
|
|
G_END_DECLS
|
|
|
|
#endif /* __EOG_UTIL_H__ */
|
|
diff --git a/src/eog-window.c b/src/eog-window.c
|
|
index d6fc3834..75d7c138 100644
|
|
--- a/src/eog-window.c
|
|
+++ b/src/eog-window.c
|
|
@@ -137,9 +137,6 @@ struct _EogWindowPrivate {
|
|
GtkWidget *message_area;
|
|
GtkWidget *properties_dlg;
|
|
|
|
- GMenu *open_with_menu;
|
|
- GPtrArray *appinfo;
|
|
-
|
|
GtkBuilder *gear_menu_builder;
|
|
|
|
GtkWidget *fullscreen_popup;
|
|
@@ -196,7 +193,6 @@ static void fullscreen_set_timeout (EogWindow *window);
|
|
static void fullscreen_clear_timeout (EogWindow *window);
|
|
static void slideshow_set_timeout (EogWindow *window);
|
|
static void update_action_groups_state (EogWindow *window);
|
|
-static void eog_window_update_open_with_menu (EogWindow *window, EogImage *image);
|
|
static void eog_window_list_store_image_added (GtkTreeModel *tree_model,
|
|
GtkTreePath *path,
|
|
GtkTreeIter *iter,
|
|
@@ -997,8 +993,6 @@ eog_window_display_image (EogWindow *window, EogImage *image)
|
|
|
|
update_status_bar (window);
|
|
|
|
- eog_window_update_open_with_menu (window, image);
|
|
-
|
|
file = eog_image_get_file (image);
|
|
g_idle_add_full (G_PRIORITY_LOW,
|
|
(GSourceFunc) add_file_to_recent_files,
|
|
@@ -1046,28 +1040,24 @@ _eog_window_launch_appinfo_with_files (EogWindow *window,
|
|
}
|
|
|
|
static void
|
|
-eog_window_action_open_with (GSimpleAction *action,
|
|
- GVariant *parameter,
|
|
- gpointer user_data)
|
|
+app_chooser_dialog_response_cb (GtkDialog *dialog,
|
|
+ gint response_id,
|
|
+ gpointer data)
|
|
{
|
|
EogWindow *window;
|
|
GAppInfo *app;
|
|
GFile *file;
|
|
GList *files = NULL;
|
|
- guint32 index = G_MAXUINT32;
|
|
-
|
|
|
|
- g_return_if_fail (EOG_IS_WINDOW (user_data));
|
|
- window = EOG_WINDOW (user_data);
|
|
+ g_return_if_fail (EOG_IS_WINDOW (data));
|
|
|
|
- index = g_variant_get_uint32 (parameter);
|
|
- if (G_UNLIKELY (index >= window->priv->appinfo->len))
|
|
- return;
|
|
+ window = EOG_WINDOW (data);
|
|
|
|
- app = g_ptr_array_index (window->priv->appinfo, index);
|
|
- if (!app)
|
|
- return;
|
|
+ if (response_id != GTK_RESPONSE_OK) {
|
|
+ goto out;
|
|
+ }
|
|
|
|
+ app = gtk_app_chooser_get_app_info (GTK_APP_CHOOSER (dialog));
|
|
file = eog_image_get_file (window->priv->image);
|
|
files = g_list_append (files, file);
|
|
|
|
@@ -1075,73 +1065,56 @@ eog_window_action_open_with (GSimpleAction *action,
|
|
|
|
g_list_free (files);
|
|
g_object_unref (file);
|
|
+
|
|
+out:
|
|
+ gtk_widget_destroy (GTK_WIDGET (dialog));
|
|
}
|
|
|
|
static void
|
|
-eog_window_update_open_with_menu (EogWindow *window, EogImage *image)
|
|
+eog_window_open_file_chooser_dialog (EogWindow *window)
|
|
{
|
|
- EogWindowPrivate *priv;
|
|
- GFile *file;
|
|
+ GtkWidget *dialog;
|
|
GFileInfo *file_info;
|
|
- GList *apps = NULL, *li = NULL;
|
|
- const gchar *mime_type;
|
|
- guint32 count = 0;
|
|
+ GFile *file;
|
|
+ const gchar *mime_type = NULL;
|
|
|
|
- priv = window->priv;
|
|
+ file = eog_image_get_file (window->priv->image);
|
|
+ file_info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, 0, NULL, NULL);
|
|
+ mime_type = g_content_type_get_mime_type (
|
|
+ g_file_info_get_content_type (file_info));
|
|
+ g_object_unref (file_info);
|
|
|
|
- g_menu_remove_all (priv->open_with_menu);
|
|
- g_ptr_array_free (priv->appinfo, TRUE);
|
|
- priv->appinfo = g_ptr_array_new_with_free_func (g_object_unref);
|
|
+ dialog = gtk_app_chooser_dialog_new_for_content_type (GTK_WINDOW (window),
|
|
+ GTK_DIALOG_MODAL |
|
|
+ GTK_DIALOG_DESTROY_WITH_PARENT |
|
|
+ GTK_DIALOG_USE_HEADER_BAR,
|
|
+ mime_type);
|
|
+ gtk_widget_show (dialog);
|
|
|
|
- file = eog_image_get_file (image);
|
|
- file_info = g_file_query_info (file,
|
|
- G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
|
|
- 0, NULL, NULL);
|
|
+ g_signal_connect_object (dialog, "response",
|
|
+ G_CALLBACK (app_chooser_dialog_response_cb),
|
|
+ window, 0);
|
|
|
|
- if (file_info == NULL) {
|
|
- g_object_unref (file);
|
|
- return;
|
|
- }
|
|
-
|
|
- mime_type = g_file_info_get_content_type (file_info);
|
|
- apps = g_app_info_get_all_for_type (mime_type);
|
|
- g_object_unref (file_info);
|
|
- if (!apps) {
|
|
- g_object_unref (file);
|
|
- return;
|
|
- }
|
|
+ g_object_unref (file);
|
|
+}
|
|
|
|
- for (li = apps; li != NULL; li = li->next) {
|
|
- GAppInfo *app = li->data;
|
|
- gchar *label;
|
|
- GIcon *icon;
|
|
- GVariant *value;
|
|
- GMenuItem *item;
|
|
+static void
|
|
+eog_window_action_open_with (GSimpleAction *action,
|
|
+ GVariant *parameter,
|
|
+ gpointer user_data)
|
|
+{
|
|
+ EogWindow *window;
|
|
|
|
- /* Do not include eog itself */
|
|
- if (g_ascii_strcasecmp (g_app_info_get_executable (app),
|
|
- g_get_prgname ()) == 0) {
|
|
- g_object_unref (app);
|
|
- continue;
|
|
- }
|
|
+ g_return_if_fail (EOG_IS_WINDOW (user_data));
|
|
+ window = EOG_WINDOW (user_data);
|
|
|
|
- label = g_strdup (g_app_info_get_display_name (app));
|
|
- item = g_menu_item_new (label, NULL);
|
|
- g_free (label);
|
|
- icon = g_app_info_get_icon (app);
|
|
- g_menu_item_set_icon (item, icon);
|
|
+ if (eog_util_is_running_inside_flatpak ()) {
|
|
+ GFile *file = eog_image_get_file (window->priv->image);
|
|
|
|
- value = g_variant_new_uint32 (count++);
|
|
- g_menu_item_set_action_and_target_value (item,
|
|
- "win.open-with",
|
|
- value);
|
|
- g_ptr_array_add (priv->appinfo, app);
|
|
- g_menu_append_item (priv->open_with_menu, item);
|
|
- g_object_unref (item);
|
|
+ eog_util_open_file_with_flatpak_portal (file);
|
|
+ } else {
|
|
+ eog_window_open_file_chooser_dialog (window);
|
|
}
|
|
-
|
|
- g_object_unref (file);
|
|
- g_list_free (apps);
|
|
}
|
|
|
|
static void
|
|
@@ -3973,7 +3946,7 @@ readonly_state_handler (GSimpleAction *action,
|
|
static const GActionEntry window_actions[] = {
|
|
/* Stateless actions on the window. */
|
|
{ "open", eog_window_action_file_open },
|
|
- { "open-with", eog_window_action_open_with, "u" },
|
|
+ { "open-with", eog_window_action_open_with },
|
|
{ "open-folder", eog_window_action_open_containing_folder },
|
|
{ "save", eog_window_action_save },
|
|
{ "save-as", eog_window_action_save_as },
|
|
@@ -4290,12 +4263,6 @@ eog_window_construct_ui (EogWindow *window)
|
|
gtk_header_bar_pack_end (GTK_HEADER_BAR (headerbar), fullscreen_button);
|
|
gtk_widget_show (fullscreen_button);
|
|
|
|
- priv->open_with_menu = g_menu_new ();
|
|
- priv->appinfo = g_ptr_array_new_with_free_func (g_object_unref);
|
|
- builder_object = gtk_builder_get_object (builder, "open-with-menu");
|
|
- g_menu_append_section (G_MENU (builder_object),
|
|
- NULL,
|
|
- G_MENU_MODEL (priv->open_with_menu));
|
|
priv->gear_menu_builder = builder;
|
|
builder = NULL;
|
|
|
|
@@ -4388,10 +4355,7 @@ eog_window_construct_ui (EogWindow *window)
|
|
|
|
builder = gtk_builder_new_from_resource ("/org/gnome/eog/ui/popup-menus.ui");
|
|
builder_object = gtk_builder_get_object (builder, "view-popup-menu");
|
|
- GObject *open_with_menu = gtk_builder_get_object (builder, "open-with-menu");
|
|
- g_menu_append_section (G_MENU (open_with_menu),
|
|
- NULL,
|
|
- G_MENU_MODEL (priv->open_with_menu));
|
|
+
|
|
popup_menu = gtk_menu_new_from_model (G_MENU_MODEL (builder_object));
|
|
|
|
eog_scroll_view_set_popup (EOG_SCROLL_VIEW (priv->view),
|
|
@@ -4603,16 +4567,6 @@ eog_window_dispose (GObject *object)
|
|
priv->image = NULL;
|
|
}
|
|
|
|
- if (priv->open_with_menu != NULL) {
|
|
- g_object_unref (priv->open_with_menu);
|
|
- priv->open_with_menu = NULL;
|
|
- }
|
|
-
|
|
- if (priv->appinfo != NULL) {
|
|
- g_ptr_array_free (priv->appinfo, TRUE);
|
|
- priv->appinfo = NULL;
|
|
- }
|
|
-
|
|
fullscreen_clear_timeout (window);
|
|
|
|
if (window->priv->fullscreen_popup != NULL) {
|
|
--
|
|
2.26.0
|
|
|