From 9c0d3df4478461fad864aaf8b59636456b5e4127 Mon Sep 17 00:00:00 2001 From: Rado Rodopski Date: Mon, 20 Oct 2014 01:12:28 +0300 Subject: [PATCH 1/4] Added proper implementation of the "ShowFolderInOSWindow" function for Linux. The target file is being highlighted, and "Show Extension Folder" (still) works. For the highlighting to work, I had to detect the default file manager on the system, then run it with the target file as a parameter. If we failed to automatically determine the default file manager, the fallback behavior is to just open the directory containing the file (without the file being highlighted). This is for https://trello.com/c/RF1ddQGK/898-linux-show-in-os-for-selected-file --- appshell/appshell_extensions_gtk.cpp | 62 ++++++++++++++++++++++++---- 1 file changed, 54 insertions(+), 8 deletions(-) diff --git a/appshell/appshell_extensions_gtk.cpp b/appshell/appshell_extensions_gtk.cpp index 4a3d58e45..6e81809fc 100644 --- a/appshell/appshell_extensions_gtk.cpp +++ b/appshell/appshell_extensions_gtk.cpp @@ -550,15 +550,61 @@ int ShowFolderInOSWindow(ExtensionString pathname) { int error = NO_ERROR; GError *gerror = NULL; - gchar *uri = g_strdup_printf("file://%s", pathname.c_str()); - - if (!gtk_show_uri(NULL, uri, GDK_CURRENT_TIME, &gerror)) { - error = ConvertGnomeErrorCode(gerror); - g_warning("%s", gerror->message); - g_error_free(gerror); + gchar *uri = NULL, *cmd_line = NULL; + FILE *fp = NULL; + gchar line[256] = {0}; + const gchar xdg_query[] = "xdg-mime query default inode/directory"; + const gchar *fmanagers[] = {"nautilus", "nemo", "thunar", "caja", "dolphin", "konqueror", "krusader", "pcmanfm"}; + static int fmanager_index = 0; + static gboolean fmanager_detected = FALSE; + static gboolean ShowInOs_FirstRun = TRUE; + + if (ShowInOs_FirstRun == TRUE) { + if ((fp = popen(xdg_query, "r")) != NULL) { + if (fgets(line, sizeof(line), fp)) { // Caring about the first (and only) line read + // Convert to lowercase + g_utf8_strdown(line, g_utf8_strlen(line, -1)); + + for(int i=0; i<(sizeof(fmanagers) / sizeof(fmanagers[0])); i++) { + if (g_strstr_len(line, g_utf8_strlen(line, -1), fmanagers[i])) { + fmanager_detected = TRUE; + fmanager_index = i; + break; + } + } + } + pclose(fp); + } + else { + g_warning("popen failed"); + } + if (fmanager_detected == FALSE) g_warning("Couldn't detect the default File Manager for this distro!"); + ShowInOs_FirstRun = FALSE; + } + + if (fmanager_detected == TRUE) { + cmd_line = g_strdup_printf("%s '%s'", fmanagers[fmanager_index], pathname.c_str()); + if (!g_spawn_command_line_async(cmd_line, &gerror)) { + error = ConvertGnomeErrorCode(gerror); + g_warning("%s", gerror->message); + g_error_free(gerror); + } + g_free(cmd_line); + } + else { // Could not detect the file manager, so fall back to the original implementation + if (g_file_test(pathname.c_str(), G_FILE_TEST_IS_DIR)) { + uri = g_strdup_printf("file://%s", pathname.c_str()); + } else { + // Extract the parent directory name (only for files) + uri = g_strdup_printf("file://%s", g_path_get_dirname(pathname.c_str()) ); + } + if (!gtk_show_uri(NULL, uri, GDK_CURRENT_TIME, &gerror)) { + error = ConvertGnomeErrorCode(gerror); + g_warning("%s", gerror->message); + g_error_free(gerror); + } + g_free(uri); } - - g_free(uri); return error; } From 844d162beaeaef5227a002919ee3b47278774e65 Mon Sep 17 00:00:00 2001 From: Rado Rodopski Date: Tue, 21 Oct 2014 21:58:46 +0300 Subject: [PATCH 2/4] Proper fix for "Show in OS" for Linux. Now using a IPC call to FileManager1, similar to what Firefox does. The fallback remains the same. That is - call gtk_show_uri() on the file's parent directory (probably not needed, but just in case). Also, fixed the URI generation. Also, updated the appshell.gyp file to include the new compilation/linking flags needed for the new function to compile properly. The library "libdbus-glib-1-dev" is now a dependency, so it should be added to the wiki page. --- appshell.gyp | 8 +- appshell/appshell_extensions_gtk.cpp | 115 ++++++++++++++++----------- 2 files changed, 72 insertions(+), 51 deletions(-) diff --git a/appshell.gyp b/appshell.gyp index 2d5f4012f..e1d42dc33 100755 --- a/appshell.gyp +++ b/appshell.gyp @@ -239,7 +239,7 @@ } ], 'cflags': [ - ' #include #include +#include // +#include // TODO: Ask permission to use libdbus-glib-1 +#include // #include "appshell_extensions.h" #include "appshell_extensions_platform.h" #include "client_handler.h" @@ -546,58 +549,62 @@ void BringBrowserWindowToFront(CefRefPtr browser) } } +gboolean OrgFreedesktopFileManager1ShowItems(const gchar *aPath) { + static gboolean org_freedesktop_FileManager1_exists = TRUE; + GError* error = NULL; + + if (!org_freedesktop_FileManager1_exists) { + return FALSE; + } + + DBusGConnection* dbusGConnection = dbus_g_bus_get(DBUS_BUS_SESSION, &error); + + if (!dbusGConnection) { + if (error) { + g_printerr("Failed to open connection to session bus: %s\n", error->message); + g_error_free(error); + } + return FALSE; + } + + gchar *uri = g_filename_to_uri(aPath, NULL, NULL); + if (uri == NULL) { + return FALSE; + } + + DBusConnection* dbusConnection = dbus_g_connection_get_connection(dbusGConnection); + // Make sure we do not exit the entire program if DBus connection get lost. + dbus_connection_set_exit_on_disconnect(dbusConnection, FALSE); + + DBusGProxy* dbusGProxy = dbus_g_proxy_new_for_name(dbusGConnection, + "org.freedesktop.FileManager1", + "/org/freedesktop/FileManager1", + "org.freedesktop.FileManager1"); + + const gchar *uris[2] = { uri, NULL }; + gboolean rv_dbus_call = dbus_g_proxy_call(dbusGProxy, "ShowItems", NULL, G_TYPE_STRV, uris, + G_TYPE_STRING, "", G_TYPE_INVALID, G_TYPE_INVALID); + + g_object_unref(dbusGProxy); + dbus_g_connection_unref(dbusGConnection); + g_free(uri); + + if (!rv_dbus_call) { + org_freedesktop_FileManager1_exists = FALSE; + return FALSE; + } + + return TRUE; +} + int ShowFolderInOSWindow(ExtensionString pathname) { int error = NO_ERROR; GError *gerror = NULL; - gchar *uri = NULL, *cmd_line = NULL; - FILE *fp = NULL; - gchar line[256] = {0}; - const gchar xdg_query[] = "xdg-mime query default inode/directory"; - const gchar *fmanagers[] = {"nautilus", "nemo", "thunar", "caja", "dolphin", "konqueror", "krusader", "pcmanfm"}; - static int fmanager_index = 0; - static gboolean fmanager_detected = FALSE; - static gboolean ShowInOs_FirstRun = TRUE; - - if (ShowInOs_FirstRun == TRUE) { - if ((fp = popen(xdg_query, "r")) != NULL) { - if (fgets(line, sizeof(line), fp)) { // Caring about the first (and only) line read - // Convert to lowercase - g_utf8_strdown(line, g_utf8_strlen(line, -1)); - - for(int i=0; i<(sizeof(fmanagers) / sizeof(fmanagers[0])); i++) { - if (g_strstr_len(line, g_utf8_strlen(line, -1), fmanagers[i])) { - fmanager_detected = TRUE; - fmanager_index = i; - break; - } - } - } - pclose(fp); - } - else { - g_warning("popen failed"); - } - if (fmanager_detected == FALSE) g_warning("Couldn't detect the default File Manager for this distro!"); - ShowInOs_FirstRun = FALSE; - } + gchar *uri = NULL, *parentdir = NULL; - if (fmanager_detected == TRUE) { - cmd_line = g_strdup_printf("%s '%s'", fmanagers[fmanager_index], pathname.c_str()); - if (!g_spawn_command_line_async(cmd_line, &gerror)) { - error = ConvertGnomeErrorCode(gerror); - g_warning("%s", gerror->message); - g_error_free(gerror); - } - g_free(cmd_line); - } - else { // Could not detect the file manager, so fall back to the original implementation - if (g_file_test(pathname.c_str(), G_FILE_TEST_IS_DIR)) { - uri = g_strdup_printf("file://%s", pathname.c_str()); - } else { - // Extract the parent directory name (only for files) - uri = g_strdup_printf("file://%s", g_path_get_dirname(pathname.c_str()) ); - } + if (g_file_test(pathname.c_str(), G_FILE_TEST_IS_DIR)) { + uri = g_filename_to_uri(pathname.c_str(), NULL, NULL); if (!gtk_show_uri(NULL, uri, GDK_CURRENT_TIME, &gerror)) { error = ConvertGnomeErrorCode(gerror); g_warning("%s", gerror->message); @@ -605,6 +612,20 @@ int ShowFolderInOSWindow(ExtensionString pathname) } g_free(uri); } + else { + if (!OrgFreedesktopFileManager1ShowItems(pathname.c_str())) { + // Fall back to using gtk_show_uri on the dirname (without highlighting the file) + parentdir = g_path_get_dirname(pathname.c_str()); + uri = g_filename_to_uri(parentdir, NULL, NULL); + if (!gtk_show_uri(NULL, uri, GDK_CURRENT_TIME, &gerror)) { + error = ConvertGnomeErrorCode(gerror); + g_warning("%s", gerror->message); + g_error_free(gerror); + } + g_free(parentdir); + g_free(uri); + } + } return error; } From 9e836f7ed40e4b9f3008d9eecd8b101efe33aa3a Mon Sep 17 00:00:00 2001 From: Rado Rodopski Date: Tue, 21 Oct 2014 22:24:29 +0300 Subject: [PATCH 3/4] Clean up some comments. --- appshell/appshell_extensions_gtk.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/appshell/appshell_extensions_gtk.cpp b/appshell/appshell_extensions_gtk.cpp index 627678478..c3ad9a746 100644 --- a/appshell/appshell_extensions_gtk.cpp +++ b/appshell/appshell_extensions_gtk.cpp @@ -25,9 +25,9 @@ #include #include #include -#include // -#include // TODO: Ask permission to use libdbus-glib-1 -#include // +#include +#include +#include #include "appshell_extensions.h" #include "appshell_extensions_platform.h" #include "client_handler.h" From 6ec99baae62e530875d6a4d306a8e640abfde037 Mon Sep 17 00:00:00 2001 From: Rado Rodopski Date: Thu, 23 Oct 2014 04:27:32 +0300 Subject: [PATCH 4/4] Rewrote the DBus method call code to use the new APIs. The GLib library now includes its own DBus bindings, and so they recommend switching to the new bindings for new applications. This requires glib-2.0 version at least 2.26 (at the moment of writing the latest version is 2.40). Removed the dependency of the binding library (libdbus-glib-1), updated appshell.gyp accordingly. --- appshell.gyp | 8 +-- appshell/appshell_extensions_gtk.cpp | 73 ++++++++++++++++------------ 2 files changed, 47 insertions(+), 34 deletions(-) diff --git a/appshell.gyp b/appshell.gyp index e1d42dc33..81e4d4133 100755 --- a/appshell.gyp +++ b/appshell.gyp @@ -239,7 +239,7 @@ } ], 'cflags': [ - ' #include +#include #include -#include -#include -#include #include "appshell_extensions.h" #include "appshell_extensions_platform.h" #include "client_handler.h" @@ -549,48 +547,63 @@ void BringBrowserWindowToFront(CefRefPtr browser) } } -gboolean OrgFreedesktopFileManager1ShowItems(const gchar *aPath) { - static gboolean org_freedesktop_FileManager1_exists = TRUE; - GError* error = NULL; +gboolean FileManager1_ShowItems(const gchar *path) { + static gboolean FileManager1_exists = TRUE; + GDBusProxy *proxy = NULL; + GDBusProxyFlags flags = G_DBUS_PROXY_FLAGS_NONE; + gchar *uri = NULL; + GVariant *call_result = NULL; + GError *error = NULL; - if (!org_freedesktop_FileManager1_exists) { + if (!FileManager1_exists) { return FALSE; } - DBusGConnection* dbusGConnection = dbus_g_bus_get(DBUS_BUS_SESSION, &error); + proxy = g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SESSION, + flags, + NULL, + "org.freedesktop.FileManager1", // name + "/org/freedesktop/FileManager1", // path + "org.freedesktop.FileManager1", // iface + NULL, + &error); - if (!dbusGConnection) { - if (error) { - g_printerr("Failed to open connection to session bus: %s\n", error->message); - g_error_free(error); - } + if (proxy == NULL) { + g_printerr("Error creating proxy: %s\n", error->message); + g_error_free(error); return FALSE; } - gchar *uri = g_filename_to_uri(aPath, NULL, NULL); + uri = g_filename_to_uri(path, NULL, NULL); if (uri == NULL) { return FALSE; } - DBusConnection* dbusConnection = dbus_g_connection_get_connection(dbusGConnection); - // Make sure we do not exit the entire program if DBus connection get lost. - dbus_connection_set_exit_on_disconnect(dbusConnection, FALSE); - - DBusGProxy* dbusGProxy = dbus_g_proxy_new_for_name(dbusGConnection, - "org.freedesktop.FileManager1", - "/org/freedesktop/FileManager1", - "org.freedesktop.FileManager1"); - + // The "ShowItems" method requires two parameters + // 1. An array of URI strings, the files to show + // 2. The DESKTOP_STARTUP_ID environment variable, used to prevent focus stealing + // (Reference: http://www.freedesktop.org/wiki/Specifications/file-manager-interface/) const gchar *uris[2] = { uri, NULL }; - gboolean rv_dbus_call = dbus_g_proxy_call(dbusGProxy, "ShowItems", NULL, G_TYPE_STRV, uris, - G_TYPE_STRING, "", G_TYPE_INVALID, G_TYPE_INVALID); + const gchar *startup_id = "dontstealmyfocus"; - g_object_unref(dbusGProxy); - dbus_g_connection_unref(dbusGConnection); + call_result = g_dbus_proxy_call_sync(proxy, + "ShowItems", // method + g_variant_new("(^ass)", uris, startup_id), // parameters + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); + + g_object_unref(proxy); g_free(uri); - if (!rv_dbus_call) { - org_freedesktop_FileManager1_exists = FALSE; + if (call_result != NULL) { + g_variant_unref(call_result); + } + else { + g_printerr("Error calling the 'ShowItems' method: %s\n", error->message); + g_error_free(error); + FileManager1_exists = FALSE; return FALSE; } @@ -613,7 +626,7 @@ int ShowFolderInOSWindow(ExtensionString pathname) g_free(uri); } else { - if (!OrgFreedesktopFileManager1ShowItems(pathname.c_str())) { + if (!FileManager1_ShowItems(pathname.c_str())) { // Fall back to using gtk_show_uri on the dirname (without highlighting the file) parentdir = g_path_get_dirname(pathname.c_str()); uri = g_filename_to_uri(parentdir, NULL, NULL);