[nautilus/wip/hadess/reinstate-multi-file-launching] mime-actions: Open files as groups if not sandboxed




commit ae61d235c5f1ea1cc57a061846c3858fb65333ab
Author: Bastien Nocera <hadess hadess net>
Date:   Thu Feb 6 15:04:31 2020 +0100

    mime-actions: Open files as groups if not sandboxed
    
    While sandboxed, we open files using the OpenURI portal, and we don't
    know which app is the default handler app for each file. As such, we
    have given up group-launching files with the same default handler when
    adapting nautilus to being sandboxed.[0]
    
    But this resulted in a feature regression in the non-sandboxed case,
    which is still the common case in production.
    
    Reinstate the code for the old behaviour by partially reverting the
    cited commit[0], but keep the current behaviour when running in inside
    a flatpak sandbox.
    
    Closes https://gitlab.gnome.org/GNOME/nautilus/-/issues/117
    
    [0] f5206a6daf0991d91e885a28bb66795a8ae12a41

 src/nautilus-mime-actions.c | 238 +++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 215 insertions(+), 23 deletions(-)
---
diff --git a/src/nautilus-mime-actions.c b/src/nautilus-mime-actions.c
index 26468c597..cbff2fa38 100644
--- a/src/nautilus-mime-actions.c
+++ b/src/nautilus-mime-actions.c
@@ -61,6 +61,12 @@ typedef struct
     char *uri;
 } LaunchLocation;
 
+typedef struct
+{
+    GAppInfo *application;
+    GList *uris;
+} ApplicationLaunchParameters;
+
 typedef struct
 {
     NautilusWindowSlot *slot;
@@ -85,7 +91,7 @@ typedef struct
     ActivateParameters *activation_params;
     GQueue *uris;
     GQueue *unhandled_uris;
-} ApplicationLaunchParameters;
+} ApplicationLaunchAsyncParameters;
 
 /* Microsoft mime types at 
https://blogs.msdn.microsoft.com/vsofficedeveloper/2008/05/08/office-2007-file-format-mime-types-for-http-content-streaming-2/
 */
 struct
@@ -345,19 +351,27 @@ launch_locations_from_file_list (GList *list)
 }
 
 static ApplicationLaunchParameters *
-application_launch_parameters_new (ActivateParameters *activation_params,
-                                   GQueue             *uris)
+application_launch_parameters_new (GAppInfo *application,
+                                   GList    *uris)
 {
     ApplicationLaunchParameters *result;
 
     result = g_new0 (ApplicationLaunchParameters, 1);
-    result->activation_params = activation_params;
-    result->uris = uris;
-    result->unhandled_uris = g_queue_new ();
+    result->application = g_object_ref (application);
+    result->uris = g_list_copy_deep (uris, (GCopyFunc) g_strdup, NULL);
 
     return result;
 }
 
+static void
+application_launch_parameters_free (ApplicationLaunchParameters *parameters)
+{
+    g_object_unref (parameters->application);
+    g_list_free_full (parameters->uris, g_free);
+
+    g_free (parameters);
+}
+
 static gboolean
 nautilus_mime_actions_check_if_required_attributes_ready (NautilusFile *file)
 {
@@ -792,6 +806,114 @@ nautilus_mime_file_opens_in_external_app (NautilusFile *file)
     return (activation_action == ACTIVATION_ACTION_OPEN_IN_APPLICATION);
 }
 
+
+static unsigned int
+mime_application_hash (GAppInfo *app)
+{
+    const char *id;
+
+    id = g_app_info_get_id (app);
+
+    if (id == NULL)
+    {
+        return GPOINTER_TO_UINT (app);
+    }
+
+    return g_str_hash (id);
+}
+
+static void
+list_to_parameters_foreach (GAppInfo  *application,
+                            GList     *uris,
+                            GList    **ret)
+{
+    ApplicationLaunchParameters *parameters;
+
+    uris = g_list_reverse (uris);
+
+    parameters = application_launch_parameters_new
+                     (application, uris);
+    *ret = g_list_prepend (*ret, parameters);
+}
+
+
+/**
+ * make_activation_parameters
+ *
+ * Construct a list of ApplicationLaunchParameters from a list of NautilusFiles,
+ * where files that have the same default application are put into the same
+ * launch parameter, and others are put into the unhandled_files list.
+ *
+ * @files: Files to use for construction.
+ * @unhandled_files: Files without any default application will be put here.
+ *
+ * Return value: Newly allocated list of ApplicationLaunchParameters.
+ **/
+static GList *
+make_activation_parameters (GList  *uris,
+                            GList **unhandled_uris)
+{
+    GList *ret, *l, *app_uris;
+    NautilusFile *file;
+    GAppInfo *app, *old_app;
+    GHashTable *app_table;
+    char *uri;
+
+    ret = NULL;
+    *unhandled_uris = NULL;
+
+    app_table = g_hash_table_new_full
+                    ((GHashFunc) mime_application_hash,
+                    (GEqualFunc) g_app_info_equal,
+                    (GDestroyNotify) g_object_unref,
+                    (GDestroyNotify) g_list_free);
+
+    for (l = uris; l != NULL; l = l->next)
+    {
+        uri = l->data;
+        file = nautilus_file_get_by_uri (uri);
+
+        app = nautilus_mime_get_default_application_for_file (file);
+        if (app != NULL)
+        {
+            app_uris = NULL;
+
+            if (g_hash_table_lookup_extended (app_table, app,
+                                              (gpointer *) &old_app,
+                                              (gpointer *) &app_uris))
+            {
+                g_hash_table_steal (app_table, old_app);
+
+                app_uris = g_list_prepend (app_uris, uri);
+
+                g_object_unref (app);
+                app = old_app;
+            }
+            else
+            {
+                app_uris = g_list_prepend (NULL, uri);
+            }
+
+            g_hash_table_insert (app_table, app, app_uris);
+        }
+        else
+        {
+            *unhandled_uris = g_list_prepend (*unhandled_uris, uri);
+        }
+        nautilus_file_unref (file);
+    }
+
+    g_hash_table_foreach (app_table,
+                          (GHFunc) list_to_parameters_foreach,
+                          &ret);
+
+    g_hash_table_destroy (app_table);
+
+    *unhandled_uris = g_list_reverse (*unhandled_uris);
+
+    return g_list_reverse (ret);
+}
+
 static gboolean
 file_was_cancelled (NautilusFile *file)
 {
@@ -844,7 +966,7 @@ activation_parameters_free (ActivateParameters *parameters)
 }
 
 static void
-application_launch_parameters_free (ApplicationLaunchParameters *parameters)
+application_launch_async_parameters_free (ApplicationLaunchAsyncParameters *parameters)
 {
     g_queue_free (parameters->unhandled_uris);
     g_queue_free (parameters->uris);
@@ -1370,11 +1492,11 @@ out:
 }
 
 static void
-on_launch_default_for_uri (GObject      *source_object,
-                           GAsyncResult *res,
-                           gpointer      user_data)
+launch_default_for_uris_callback (GObject      *source_object,
+                                  GAsyncResult *res,
+                                  gpointer      user_data)
 {
-    ApplicationLaunchParameters *params;
+    ApplicationLaunchAsyncParameters *params;
     ActivateParameters *activation_params;
     char *uri;
     gboolean sandboxed;
@@ -1396,7 +1518,7 @@ on_launch_default_for_uri (GObject      *source_object,
         nautilus_launch_default_for_uri_async (g_queue_peek_head (params->uris),
                                                activation_params->parent_window,
                                                activation_params->cancellable,
-                                               on_launch_default_for_uri,
+                                               launch_default_for_uris_callback,
                                                params);
     }
     else
@@ -1406,7 +1528,7 @@ on_launch_default_for_uri (GObject      *source_object,
             application_unhandled_uri (activation_params, uri);
         }
 
-        application_launch_parameters_free (params);
+        application_launch_async_parameters_free (params);
     }
 }
 
@@ -1415,9 +1537,16 @@ activate_files (ActivateParameters *parameters)
 {
     NautilusFile *file;
     NautilusWindowOpenFlags flags;
+    g_autoptr (GList) open_in_app_parameters = NULL;
+    g_autoptr (GList) unhandled_open_in_app_uris = NULL;
+    ApplicationLaunchParameters *one_parameters;
     int count;
     g_autofree char *old_working_dir = NULL;
     GdkScreen *screen;
+    gint num_apps;
+    gint num_unhandled;
+    gint num_files;
+    gboolean open_files;
     g_autoptr (GQueue) launch_files = NULL;
     g_autoptr (GQueue) launch_in_terminal_files = NULL;
     g_autoptr (GQueue) open_in_app_uris = NULL;
@@ -1612,26 +1741,89 @@ activate_files (ActivateParameters *parameters)
         }
     }
 
-    if (g_queue_is_empty (open_in_app_uris))
-    {
-        activation_parameters_free (parameters);
-    }
-    else
+    if (!g_queue_is_empty (open_in_app_uris) &&
+        g_file_test ("/.flatpak-info", G_FILE_TEST_EXISTS))
     {
         const char *uri;
-        ApplicationLaunchParameters *params;
+        ApplicationLaunchAsyncParameters *async_params;
 
         uri = g_queue_peek_head (open_in_app_uris);
-        params = application_launch_parameters_new (parameters,
-                                                    g_queue_copy (open_in_app_uris));
+
+        async_params = g_new0 (ApplicationLaunchAsyncParameters, 1);
+        async_params->activation_params = parameters;
+        async_params->uris = g_steal_pointer (&open_in_app_uris);
 
         gtk_recent_manager_add_item (gtk_recent_manager_get_default (), uri);
         nautilus_launch_default_for_uri_async (uri,
                                                parameters->parent_window,
                                                parameters->cancellable,
-                                               on_launch_default_for_uri,
-                                               params);
+                                               launch_default_for_uris_callback,
+                                               async_params);
+        return;
+    }
+
+    if (open_in_app_uris != NULL)
+    {
+        open_in_app_parameters = make_activation_parameters (g_queue_peek_head_link (open_in_app_uris),
+                                                             &unhandled_open_in_app_uris);
     }
+
+    num_apps = g_list_length (open_in_app_parameters);
+    num_unhandled = g_list_length (unhandled_open_in_app_uris);
+    num_files = g_queue_get_length (open_in_app_uris);
+    open_files = TRUE;
+
+    if (g_queue_is_empty (open_in_app_uris) &&
+        (!parameters->user_confirmation ||
+         num_files + num_unhandled > SILENT_OPEN_LIMIT) &&
+        num_apps > 1)
+    {
+        GtkDialog *dialog;
+        char *prompt;
+        g_autofree char *detail = NULL;
+        int response;
+
+        pause_activation_timed_cancel (parameters);
+
+        prompt = _("Are you sure you want to open all files?");
+        detail = g_strdup_printf (ngettext ("This will open %d separate application.",
+                                            "This will open %d separate applications.", num_apps), num_apps);
+        dialog = eel_show_yes_no_dialog (prompt, detail,
+                                         _("_OK"), _("_Cancel"),
+                                         parameters->parent_window);
+        response = gtk_dialog_run (dialog);
+        gtk_widget_destroy (GTK_WIDGET (dialog));
+
+        unpause_activation_timed_cancel (parameters);
+
+        if (response != GTK_RESPONSE_YES)
+        {
+            open_files = FALSE;
+        }
+    }
+
+    if (open_files)
+    {
+        for (l = open_in_app_parameters; l != NULL; l = l->next)
+        {
+            one_parameters = l->data;
+
+            nautilus_launch_application_by_uri (one_parameters->application,
+                                                one_parameters->uris,
+                                                parameters->parent_window);
+            application_launch_parameters_free (one_parameters);
+        }
+
+        for (l = unhandled_open_in_app_uris; l != NULL; l = l->next)
+        {
+            char *uri = l->data;
+
+            /* this does not block */
+            application_unhandled_uri (parameters, uri);
+        }
+    }
+
+    activation_parameters_free (parameters);
 }
 
 static void


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]