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




commit 54981fb7131c3b09313070e8219073712fe4a744
Author: António Fernandes <antoniof gnome org>
Date:   Wed Dec 30 15:54:59 2020 +0000

    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[1], but keep the current
    behaviour when running in inside a flatpak sandbox.
    
    Closes https://gitlab.gnome.org/GNOME/nautilus/-/issues/117
    
    [0] f5206a6daf0991d91e885a28bb66795a8ae12a41
    [1] based on revert patch with revert conflicts resolved by hadess

 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]