[nautilus/wip/razvan/automatic-decompression: 2/2] Add extract operation



commit ad0d96d59fbbfb937a5eb14bff01dfe0fd0e3c08
Author: Razvan Chitu <razvan ch95 gmail com>
Date:   Mon Jun 27 11:43:22 2016 +0300

    Add extract operation

 configure.ac                      |    2 +
 src/nautilus-application.c        |   19 ++
 src/nautilus-application.h        |    2 +
 src/nautilus-file-operations.c    |  447 ++++++++++++++++++++++++++++++++++++-
 src/nautilus-file-operations.h    |    9 +
 src/nautilus-files-view.c         |   10 +-
 src/nautilus-mime-actions.c       |   42 ++++
 src/nautilus-mime-actions.h       |    1 +
 src/nautilus-operations-manager.c |  157 +++++++++++++
 src/nautilus-operations-manager.h |   12 +
 10 files changed, 697 insertions(+), 4 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 493c5de..3d6b0a2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -11,6 +11,7 @@ m4_define(exif_minver,                 0.6.20)
 m4_define(exempi_minver,               2.1.0)
 m4_define(notify_minver,               0.7.0)
 m4_define(schemas_minver,              3.8.0)
+m4_define(autoar_minver,               0.1)
 
 dnl 1. If the library code has changed at all since last release, then increment revision.
 dnl 2. If any interfaces have been added, then increment current and set revision to 0.
@@ -270,6 +271,7 @@ dnl base libs
 PKG_CHECK_MODULES(BASE, [
        gtk+-3.0 >= gtk_minver
        glib-2.0 >= glib_minver
+    gnome-autoar >= autoar_minver
 ])
 
 dnl common libs (eel, nautilus)
diff --git a/src/nautilus-application.c b/src/nautilus-application.c
index 989f31d..2e2b474 100644
--- a/src/nautilus-application.c
+++ b/src/nautilus-application.c
@@ -76,6 +76,8 @@ typedef struct {
        GList *windows;
 
         GHashTable *notifications;
+
+        AutoarPref *arpref;
 } NautilusApplicationPrivate;
 
 G_DEFINE_TYPE_WITH_PRIVATE (NautilusApplication, nautilus_application, GTK_TYPE_APPLICATION);
@@ -547,6 +549,8 @@ nautilus_application_finalize (GObject *object)
        g_clear_object (&priv->fdb_manager);
        g_clear_object (&priv->search_provider);
 
+        g_clear_object (&priv->arpref);
+
        g_list_free (priv->windows);
 
         g_hash_table_destroy (priv->notifications);
@@ -948,6 +952,7 @@ static void
 nautilus_application_init (NautilusApplication *self)
 {
         NautilusApplicationPrivate *priv;
+        g_autoptr (GSettings) archive_settings;
 
         priv = nautilus_application_get_instance_private (self);
 
@@ -960,6 +965,10 @@ nautilus_application_init (NautilusApplication *self)
 
         nautilus_ensure_extension_points ();
         nautilus_ensure_extension_builtins ();
+
+
+        archive_settings = g_settings_new (AUTOAR_PREF_DEFAULT_GSCHEMA_ID);
+        priv->arpref = autoar_pref_new_with_gsettings (archive_settings);
 }
 
 static void
@@ -1038,6 +1047,16 @@ nautilus_application_get_default (void)
         return self;
 }
 
+AutoarPref *
+nautilus_application_get_arpref (NautilusApplication *self)
+{
+        NautilusApplicationPrivate *priv;
+
+        priv = nautilus_application_get_instance_private (self);
+
+        return priv->arpref;
+}
+
 void
 nautilus_application_send_notification (NautilusApplication *self,
                                         const gchar         *notification_id,
diff --git a/src/nautilus-application.h b/src/nautilus-application.h
index 197a276..6498816 100644
--- a/src/nautilus-application.h
+++ b/src/nautilus-application.h
@@ -24,6 +24,7 @@
 #include <gdk/gdk.h>
 #include <gio/gio.h>
 #include <gtk/gtk.h>
+#include <gnome-autoar/autoar.h>
 
 #include "nautilus-bookmark-list.h"
 #include "nautilus-window.h"
@@ -71,6 +72,7 @@ void nautilus_application_open_location_full (NautilusApplication     *applicati
                                               NautilusWindowSlot      *target_slot);
 
 NautilusApplication *nautilus_application_get_default (void);
+AutoarPref * nautilus_application_get_arpref (NautilusApplication *self);
 void nautilus_application_send_notification (NautilusApplication *self,
                                              const gchar         *notification_id,
                                              GNotification       *notification);
diff --git a/src/nautilus-file-operations.c b/src/nautilus-file-operations.c
index 7ea2ada..032384d 100644
--- a/src/nautilus-file-operations.c
+++ b/src/nautilus-file-operations.c
@@ -51,6 +51,8 @@
 #include <gtk/gtk.h>
 #include <gio/gio.h>
 #include <glib.h>
+#include <gnome-autoar/autoar.h>
+
 #include "nautilus-operations-manager.h"
 #include "nautilus-file-changes-queue.h"
 #include "nautilus-file-private.h"
@@ -61,6 +63,7 @@
 #include "nautilus-file-conflict-dialog.h"
 #include "nautilus-file-undo-operations.h"
 #include "nautilus-file-undo-manager.h"
+#include "nautilus-application.h"
 
 /* TODO: TESTING!!! */
 
@@ -170,6 +173,23 @@ typedef struct {
        int last_reported_files_left;
 } TransferInfo;
 
+typedef struct {
+        CommonJob common;
+        GFile *source;
+        GFile *output;
+
+       NautilusExtractCallback done_callback;
+       gpointer done_callback_data;
+
+       AutoarExtract *arextract;
+
+       GFile *confirmed_destination;
+       gboolean success;
+
+        guint64 total_size;
+        guint total_files;
+} ExtractJob;
+
 #define SECONDS_NEEDED_FOR_RELIABLE_TRANSFER_RATE 8
 #define NSEC_PER_MICROSEC 1000
 
@@ -1838,7 +1858,7 @@ delete_files (CommonJob *job, GList *files, int *files_skipped)
        SourceInfo source_info;
        TransferInfo transfer_info;
        gboolean skipped_file;
-       
+
        if (job_aborted (job)) {
                return;
        }
@@ -5780,7 +5800,7 @@ link_file (CopyMoveJob *job,
                        g_object_unref (new_dest);
                }
        }
-       /* Conflict */
+       /* FileConflict */
        if (error != NULL && IS_IO_ERROR (error, EXISTS)) {
                g_object_unref (dest);
                dest = get_target_file_for_link (src, dest_dir, *dest_fs_type, count++);
@@ -7108,6 +7128,429 @@ nautilus_file_mark_desktop_file_trusted (GFile *file,
        g_object_unref (task);
 }
 
+static FileConflictResponse *
+signal_extract_conflict (CommonJob *job,
+                         GFile *src,
+                         GFile *dest,
+                         GFile *dest_dir)
+{
+        FileConflictResponse *response;
+
+        g_timer_stop (job->time);
+        nautilus_progress_info_pause (job->progress);
+
+        response = get_extract_file_conflict_response (job->parent_window,
+                                                       src,
+                                                       dest,
+                                                       dest_dir);
+
+        nautilus_progress_info_resume (job->progress);
+        g_timer_continue (job->time);
+
+        return response;
+}
+
+static gboolean
+remove_file (GFile *file,
+             GError **error)
+{
+        gboolean success = TRUE;
+        g_autoptr (GFileEnumerator) enumerator = NULL;
+
+        if (g_file_delete (file, NULL, error)) {
+                return TRUE;
+        }
+
+        if (g_error_matches (*error, G_IO_ERROR, G_IO_ERROR_NOT_EMPTY)) {
+                g_clear_error (error);
+        } else {
+                return FALSE;
+        }
+
+        enumerator = g_file_enumerate_children (file,
+                                                G_FILE_ATTRIBUTE_STANDARD_NAME,
+                                                G_FILE_QUERY_INFO_NONE,
+                                                NULL, error);
+
+        if (enumerator) {
+                GFileInfo *info;
+
+                while ((info = g_file_enumerator_next_file (enumerator, NULL, error)) != NULL) {
+                        g_autoptr(GFile) child;
+
+                        child = g_file_get_child (file, g_file_info_get_name (info));
+
+                        success = remove_file (child, error);
+
+                        g_object_unref (info);
+
+                        if (!success) {
+                                return FALSE;
+                        }
+                }
+        } else {
+                return FALSE;
+        }
+
+        g_file_delete (file, NULL, error);
+
+        return success && *error == NULL;
+}
+
+static gboolean
+extract_check_archive (ExtractJob  *extract_job,
+                       GError     **error)
+{
+        g_autoptr (GFile) destination_directory = NULL;
+        g_autoptr (GFileInfo) info;
+        guint64 free_space;
+        double free_space_percentage;
+
+        g_clear_error (error);
+
+        destination_directory = g_file_get_parent (extract_job->confirmed_destination);
+
+        info = g_file_query_filesystem_info (destination_directory,
+                                             G_FILE_ATTRIBUTE_FILESYSTEM_FREE,
+                                             extract_job->common.cancellable,
+                                             error);
+
+        if (info == NULL) {
+                return FALSE;
+        }
+
+        free_space = g_file_info_get_attribute_uint64 (info,
+                                                       G_FILE_ATTRIBUTE_FILESYSTEM_FREE);
+
+        free_space_percentage = ((double) extract_job->total_size) / ((double) free_space);
+
+        return TRUE;
+}
+
+static void
+extract_task_done (GObject *source_object,
+                   GAsyncResult *res,
+                   gpointer user_data)
+{
+        ExtractJob *extract_job = user_data;
+
+        if (extract_job->done_callback) {
+                extract_job->done_callback (extract_job->source,
+                                            extract_job->confirmed_destination,
+                                            extract_job->success,
+                                            extract_job->done_callback_data);
+        }
+
+        g_clear_object (&extract_job->source);
+        g_clear_object (&extract_job->output);
+        g_clear_object (&extract_job->arextract);
+        g_clear_object (&extract_job->confirmed_destination);
+
+        finalize_common ((CommonJob *)extract_job);
+}
+
+static void
+extract_scanned_handler (AutoarExtract *arextract,
+                         guint          files_count,
+                         gpointer       user_data)
+{
+        ExtractJob *extract_job = user_data;
+
+        extract_job->total_files = autoar_extract_get_files (arextract);
+        extract_job->total_size = autoar_extract_get_size (arextract);
+}
+
+static GFile*
+extract_confirm_dest_handler (AutoarExtract *arextract,
+                              GFile         *destination,
+                              GList         *files,
+                              gpointer       user_data)
+{
+        ExtractJob *extract_job = user_data;
+        GFileType file_type;
+        g_autoptr (GFile) destination_directory;
+        GFile *confirmed_destination = NULL;
+
+        nautilus_progress_info_set_details (extract_job->common.progress,
+                                            _("Checking destination"));
+
+        destination_directory = g_file_get_parent (destination);
+
+        confirmed_destination = g_object_ref (destination);
+        file_type = g_file_query_file_type (confirmed_destination,
+                                            G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+                                            NULL);
+
+        while (file_type != G_FILE_TYPE_UNKNOWN) {
+                FileConflictResponse *response;
+                GError *error = NULL;
+
+                response = signal_extract_conflict ((CommonJob *)extract_job,
+                                                    extract_job->source,
+                                                    confirmed_destination,
+                                                    destination_directory);
+
+                switch (response->id) {
+                        case GTK_RESPONSE_CANCEL:
+                        case GTK_RESPONSE_DELETE_EVENT:
+                                g_cancellable_cancel (extract_job->common.cancellable);
+
+                                g_clear_object (&confirmed_destination);
+                                conflict_response_data_free (response);
+
+                                goto out;
+                        case CONFLICT_RESPONSE_REPLACE:
+                                nautilus_progress_info_set_details (extract_job->common.progress,
+                                                                    _("Replacing existing destination"));
+
+                                if (!remove_file (confirmed_destination, &error)) {
+                                        run_error ((CommonJob *)extract_job,
+                                                   f (_("Error while extracting “%B”."),
+                                                      extract_job->source),
+                                                   f (_("The existing destination “%B” could not be 
replaced."),
+                                                      confirmed_destination),
+                                                   error->message,
+                                                   FALSE,
+                                                   CANCEL,
+                                                   NULL);
+
+                                       g_cancellable_cancel (extract_job->common.cancellable);
+
+                                        g_error_free (error);
+                                }
+
+                               conflict_response_data_free (response);
+
+                                goto out;
+                        case CONFLICT_RESPONSE_RENAME:
+                                g_clear_object (&confirmed_destination);
+
+                                confirmed_destination = get_target_file_for_display_name 
(destination_directory,
+                                                                                          
response->new_name);
+
+                                break;
+                        default:
+                                g_assert_not_reached ();
+                }
+
+                file_type = g_file_query_file_type (confirmed_destination,
+                                                    G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+                                                    NULL);
+
+                conflict_response_data_free (response);
+
+        }
+out:
+       extract_job->confirmed_destination = confirmed_destination;
+
+        // extract_check_archive (extract_job, NULL);
+
+        return g_object_ref (confirmed_destination);
+}
+
+static void
+extract_progress_handler (AutoarExtract *arextract,
+                          guint64        completed_size,
+                          guint          completed_files,
+                          gpointer       user_data)
+{
+        ExtractJob *extract_job = user_data;
+        CommonJob *common = user_data;
+        char *details;
+        int files_left;
+        double elapsed, transfer_rate;
+        int remaining_time;
+
+        files_left = extract_job->total_files - completed_files;
+
+        nautilus_progress_info_take_status (common->progress,
+                                            f (_("Extracting “%B”"), extract_job->source));
+
+        elapsed = g_timer_elapsed (common->time, NULL);
+        transfer_rate = 0;
+        remaining_time = INT_MAX;
+        if (elapsed > 0) {
+                transfer_rate = completed_size / elapsed;
+                if (transfer_rate > 0) {
+                        remaining_time = (extract_job->total_size - completed_size) / transfer_rate;
+                }
+        }
+
+        if (transfer_rate == 0) {
+                transfer_rate = completed_files / elapsed;
+                if (transfer_rate > 0) {
+                        remaining_time = (extract_job->total_files - completed_files) / transfer_rate;
+                }
+        }
+
+        if (elapsed < SECONDS_NEEDED_FOR_RELIABLE_TRANSFER_RATE &&
+            transfer_rate > 0) {
+                if (extract_job->total_files == 1) {
+                        /* To translators: %S will expand to a size like "2 bytes" or "3 MB", so something 
like "4 kb / 4 MB" */
+                        details = f (_("%S / %S"), completed_size, extract_job->total_size);
+                } else {
+                        details = f (_("%'d / %'d"),
+                                     files_left > 0 ? completed_files + 1 : completed_files,
+                                     extract_job->total_files);
+                }
+        } else {
+                if (extract_job->total_files == 1) {
+                        if (files_left > 0) {
+                                /* To translators: %S will expand to a size like "2 bytes" or "3 MB", %T to 
a time duration like
+                                 * "2 minutes". So the whole thing will be something like "2 kb / 4 MB -- 2 
hours left (4kb/sec)"
+                                 *
+                                 * The singular/plural form will be used depending on the remaining time 
(i.e. the %T argument).
+                                 */
+                                details = f (ngettext ("%S / %S \xE2\x80\x94 %T left (%S/sec)",
+                                                       "%S / %S \xE2\x80\x94 %T left (%S/sec)",
+                                                       seconds_count_format_time_units (remaining_time)),
+                                             completed_size, extract_job->total_size,
+                                             remaining_time,
+                                             (goffset)transfer_rate);
+                        } else {
+                                /* To translators: %S will expand to a size like "2 bytes" or "3 MB". */
+                                details = f (_("%S / %S"),
+                                             completed_size,
+                                             extract_job->total_size);
+                        }
+                } else {
+                        if (files_left > 0) {
+                                /* To translators: %T will expand to a time duration like "2 minutes".
+                                 * So the whole thing will be something like "1 / 5 -- 2 hours left 
(4kb/sec)"
+                                 *
+                                 * The singular/plural form will be used depending on the remaining time 
(i.e. the %T argument).
+                                 */
+                                details = f (ngettext ("%'d / %'d \xE2\x80\x94 %T left (%S/sec)",
+                                                       "%'d / %'d \xE2\x80\x94 %T left (%S/sec)",
+                                                       seconds_count_format_time_units (remaining_time)),
+                                             completed_files + 1, extract_job->total_files,
+                                             remaining_time,
+                                             (goffset)transfer_rate);
+                        } else {
+                                /* To translators: %'d is the number of files completed for the operation,
+                                 * so it will be something like 2/14. */
+                                details = f (_("%'d / %'d"),
+                                             completed_files,
+                                             extract_job->total_files);
+                        }
+                }
+        }
+
+        nautilus_progress_info_take_details (common->progress, details);
+
+        if (elapsed > SECONDS_NEEDED_FOR_APROXIMATE_TRANSFER_RATE) {
+                nautilus_progress_info_set_remaining_time (common->progress,
+                                                           remaining_time);
+                nautilus_progress_info_set_elapsed_time (common->progress,
+                                                         elapsed);
+        }
+
+        nautilus_progress_info_set_progress (common->progress,
+                                             completed_size,
+                                             extract_job->total_size);
+}
+
+static void
+extract_error_handler (AutoarExtract *arextract,
+                       GError *error,
+                       gpointer user_data)
+{
+        ExtractJob *extract_job = user_data;
+
+        nautilus_progress_info_take_status (extract_job->common.progress,
+                                            f (_("Error extracting “%B””"),
+                                               extract_job->source));
+
+        run_error ((CommonJob *)extract_job,
+                   f (_("There was an error while extracting “%B”."),
+                      extract_job->source),
+                   g_strdup (error->message),
+                   NULL,
+                   FALSE,
+                   CANCEL,
+                   NULL);
+}
+
+static void
+extract_completed_handler (AutoarExtract *arextract,
+                          gpointer user_data)
+{
+        ExtractJob *extract_job = user_data;
+
+        extract_job->success = TRUE;
+
+        nautilus_progress_info_take_status (extract_job->common.progress,
+                                            f (_("Finished extracting “%B” to “%B”"),
+                                               extract_job->source,
+                                               extract_job->confirmed_destination));
+
+        nautilus_progress_info_set_destination (extract_job->common.progress,
+                                               extract_job->confirmed_destination);
+}
+
+static void
+extract_task_thread_func (GTask        *task,
+                          gpointer      source_object,
+                          gpointer      task_data,
+                          GCancellable *cancellable)
+{
+        ExtractJob *extract_job = task_data;
+        AutoarPref *arpref;
+
+        arpref = nautilus_application_get_arpref (nautilus_application_get_default ());
+
+        extract_job->arextract = autoar_extract_new_file (extract_job->source,
+                                                          extract_job->output,
+                                                          arpref);
+
+        autoar_extract_set_notify_interval (extract_job->arextract, 100000);
+
+        g_signal_connect (extract_job->arextract, "scanned",
+                          G_CALLBACK (extract_scanned_handler), extract_job);
+        g_signal_connect (extract_job->arextract, "error",
+                          G_CALLBACK (extract_error_handler), extract_job);
+        g_signal_connect (extract_job->arextract, "confirm-dest",
+                          G_CALLBACK (extract_confirm_dest_handler), extract_job);
+        g_signal_connect (extract_job->arextract, "progress",
+                          G_CALLBACK (extract_progress_handler), extract_job);
+        g_signal_connect (extract_job->arextract, "completed",
+                          G_CALLBACK (extract_completed_handler), extract_job);
+
+        g_timer_start (extract_job->common.time);
+
+        nautilus_progress_info_start (extract_job->common.progress);
+
+        nautilus_progress_info_set_details (extract_job->common.progress,
+                                            _("Scanning archive contents"));
+
+        autoar_extract_start (extract_job->arextract, extract_job->common.cancellable);
+}
+
+void
+nautilus_file_operations_extract (GFile                   *source,
+                                  GFile                   *output,
+                                  GtkWindow               *parent_window,
+                                  NautilusExtractCallback  done_callback,
+                                  gpointer                done_callback_data)
+{
+        GTask *task;
+        ExtractJob *job;
+
+        job = op_job_new (ExtractJob, parent_window);
+        job->source = g_object_ref (source);
+        job->output = g_object_ref (output);
+        job->done_callback = done_callback;
+        job->done_callback = done_callback_data;
+        job->success = FALSE;
+
+        inhibit_power_manager ((CommonJob *)job, _("Extracting Files"));
+
+        task = g_task_new (NULL, job->common.cancellable, extract_task_done, job);
+        g_task_set_task_data (task, job, NULL);
+        g_task_run_in_thread (task, extract_task_thread_func);
+        g_object_unref (task);
+}
+
 #if !defined (NAUTILUS_OMIT_SELF_CHECK)
 
 void
diff --git a/src/nautilus-file-operations.h b/src/nautilus-file-operations.h
index 08b11ca..50ae56c 100644
--- a/src/nautilus-file-operations.h
+++ b/src/nautilus-file-operations.h
@@ -44,6 +44,10 @@ typedef void (* NautilusMountCallback)     (GVolume    *volume,
                                            gboolean    success,
                                            GObject    *callback_data_object);
 typedef void (* NautilusUnmountCallback)   (gpointer    callback_data);
+typedef void (* NautilusExtractCallback)   (GFile    *source,
+                                           GFile    *destination,
+                                           gboolean  success,
+                                           gpointer  callback_data);
 
 /* FIXME: int copy_action should be an enum */
 
@@ -147,6 +151,11 @@ void nautilus_file_mark_desktop_file_trusted (GFile           *file,
                                              gboolean          interactive,
                                              NautilusOpCallback done_callback,
                                              gpointer          done_callback_data);
+void nautilus_file_operations_extract (GFile                   *source,
+                                       GFile                   *output,
+                                       GtkWindow               *parent_window,
+                                       NautilusExtractCallback  done_callback,
+                                       gpointer                        done_callback_data);
 
 
 #endif /* NAUTILUS_FILE_OPERATIONS_H */
diff --git a/src/nautilus-files-view.c b/src/nautilus-files-view.c
index 3c9a8ea..06ec752 100644
--- a/src/nautilus-files-view.c
+++ b/src/nautilus-files-view.c
@@ -6644,7 +6644,7 @@ update_selection_menu (NautilusFilesView *view)
         GList *selection, *l;
         NautilusFile *file;
         gint selection_count;
-        gboolean show_app, show_run;
+        gboolean show_extract, show_app, show_run;
         gboolean item_opens_in_view;
         gchar *item_label;
         GAppInfo *app;
@@ -6682,12 +6682,16 @@ update_selection_menu (NautilusFilesView *view)
         g_free (item_label);
 
         /* Open With <App> menu item */
-        show_app = show_run = item_opens_in_view = selection_count != 0;
+        show_extract = show_app = show_run = item_opens_in_view = selection_count != 0;
         for (l = selection; l != NULL; l = l->next) {
                 NautilusFile *file;
 
                 file = NAUTILUS_FILE (selection->data);
 
+                if (!nautilus_mime_file_extracts (file)) {
+                        show_extract = FALSE;
+                }
+
                 if (!nautilus_mime_file_opens_in_external_app (file)) {
                         show_app = FALSE;
                 }
@@ -6726,6 +6730,8 @@ update_selection_menu (NautilusFilesView *view)
                 g_object_unref (app);
         } else if (show_run) {
                 item_label = g_strdup (_("Run"));
+        } else if (show_extract) {
+                item_label = g_strdup (_("Extract Here"));
         } else {
                 item_label = g_strdup (_("Open"));
         }
diff --git a/src/nautilus-mime-actions.c b/src/nautilus-mime-actions.c
index e196dce..db72235 100644
--- a/src/nautilus-mime-actions.c
+++ b/src/nautilus-mime-actions.c
@@ -35,6 +35,7 @@
 #include <glib/gstdio.h>
 #include <string.h>
 #include <gdk/gdkx.h>
+#include <gnome-autoar/autoar.h>
 
 #include "nautilus-file-attributes.h"
 #include "nautilus-file.h"
@@ -43,6 +44,7 @@
 #include "nautilus-program-choosing.h"
 #include "nautilus-global-preferences.h"
 #include "nautilus-signaller.h"
+#include "nautilus-application.h"
 
 #define DEBUG_FLAG NAUTILUS_DEBUG_MIME
 #include "nautilus-debug.h"
@@ -54,6 +56,7 @@ typedef enum {
        ACTIVATION_ACTION_LAUNCH_IN_TERMINAL,
        ACTIVATION_ACTION_OPEN_IN_VIEW,
        ACTIVATION_ACTION_OPEN_IN_APPLICATION,
+        ACTIVATION_ACTION_EXTRACT,
        ACTIVATION_ACTION_DO_NOTHING,
 } ActivationAction;
 
@@ -677,6 +680,16 @@ get_activation_action (NautilusFile *file)
 {
        ActivationAction action;
        char *activation_uri;
+        g_autofree char* mime_type;
+        AutoarPref *arpref;
+
+        arpref = nautilus_application_get_arpref (nautilus_application_get_default ());
+
+        mime_type = nautilus_file_get_mime_type (file);
+
+        if (autoar_pref_check_mime_type_d (arpref, mime_type)) {
+                return ACTIVATION_ACTION_EXTRACT;
+        }
 
        if (nautilus_file_is_nautilus_link (file)) {
                return ACTIVATION_ACTION_LAUNCH_DESKTOP_FILE;
@@ -715,6 +728,12 @@ get_activation_action (NautilusFile *file)
 }
 
 gboolean
+nautilus_mime_file_extracts (NautilusFile *file)
+{
+        return get_activation_action (file) == ACTIVATION_ACTION_EXTRACT;
+}
+
+gboolean
 nautilus_mime_file_launches (NautilusFile *file)
 {
        ActivationAction activation_action;
@@ -1506,6 +1525,7 @@ activate_files (ActivateParameters *parameters)
        GList *open_in_app_uris;
        GList *open_in_app_parameters;
        GList *unhandled_open_in_app_uris;
+        GList *extract_files;
        ApplicationLaunchParameters *one_parameters;
        GList *open_in_view_files;
        GList *l;
@@ -1528,6 +1548,7 @@ activate_files (ActivateParameters *parameters)
        launch_in_terminal_files = NULL;
        open_in_app_uris = NULL;
        open_in_view_files = NULL;
+        extract_files = NULL;
 
        for (l = parameters->locations; l != NULL; l = l->next) {
                location = l->data;
@@ -1560,6 +1581,9 @@ activate_files (ActivateParameters *parameters)
                case ACTIVATION_ACTION_OPEN_IN_VIEW :
                        open_in_view_files = g_list_prepend (open_in_view_files, file);
                        break;
+                case ACTIVATION_ACTION_EXTRACT :
+                        extract_files = g_list_prepend (extract_files, file);
+                        break;
                case ACTIVATION_ACTION_OPEN_IN_APPLICATION :
                        open_in_app_uris = g_list_prepend (open_in_app_uris, location->uri);
                        break;
@@ -1674,6 +1698,24 @@ activate_files (ActivateParameters *parameters)
                }
        }
 
+        extract_files = g_list_reverse (extract_files);
+        if (extract_files) {
+                for (l = extract_files; l != NULL; l = l->next) {
+                        file = NAUTILUS_FILE (l->data);
+                        g_autoptr (GFile) source;
+                        g_autoptr (GFile) output;
+
+                        source = nautilus_file_get_location (file);
+                        output = nautilus_file_get_parent_location (file);
+
+                        nautilus_file_operations_extract (source,
+                                                          output,
+                                                          parameters->parent_window,
+                                                          NULL,
+                                                          NULL);
+                }
+        }
+
        open_in_app_parameters = NULL;
        unhandled_open_in_app_uris = NULL;
 
diff --git a/src/nautilus-mime-actions.h b/src/nautilus-mime-actions.h
index f96ca66..c3f9f87 100644
--- a/src/nautilus-mime-actions.h
+++ b/src/nautilus-mime-actions.h
@@ -38,6 +38,7 @@ GList *                nautilus_mime_get_applications_for_file            (Nauti
 GAppInfo *             nautilus_mime_get_default_application_for_files    (GList                   *files);
 
 gboolean               nautilus_mime_file_opens_in_external_app           (NautilusFile            *file);
+gboolean               nautilus_mime_file_extracts                        (NautilusFile            *file);
 gboolean               nautilus_mime_file_launches                        (NautilusFile            *file);
 void                   nautilus_mime_activate_files                       (GtkWindow               
*parent_window,
                                                                           NautilusWindowSlot      *slot,
diff --git a/src/nautilus-operations-manager.c b/src/nautilus-operations-manager.c
index a26b2b0..8a39cc7 100644
--- a/src/nautilus-operations-manager.c
+++ b/src/nautilus-operations-manager.c
@@ -59,6 +59,13 @@ create_copy_move_dialog (FileConflictDialogData *data)
 }
 
 static void
+create_extract_dialog (FileConflictDialogData *data)
+{
+        data->dialog = nautilus_file_conflict_dialog_new (data->parent,
+                                                          _("Extract conflict"));
+}
+
+static void
 set_copy_move_dialog_text (FileConflictDialogData *data)
 {
         g_autofree gchar *primary_text = NULL;
@@ -141,6 +148,42 @@ set_copy_move_dialog_text (FileConflictDialogData *data)
 }
 
 static void
+set_extract_dialog_text (FileConflictDialogData *data)
+{
+
+        g_autofree gchar *primary_text = NULL;
+        g_autofree gchar *secondary_text = NULL;
+        const gchar *message_extra;
+        g_autofree gchar *message = NULL;
+        g_autofree gchar *dest_name;
+        g_autofree gchar *dest_dir_name;
+
+        dest_name = nautilus_file_get_display_name (data->destination);
+        dest_dir_name = nautilus_file_get_display_name (data->dest_dir);
+
+        if (nautilus_file_is_directory (data->destination)) {
+                primary_text = g_strdup_printf (_("Replace folder “%s”?"), dest_name);
+                message = g_strdup_printf
+                        (_("A folder with the same name as the extract destination already exists in “%s”."),
+                         dest_dir_name);
+                message_extra =
+                        _("Replacing it will remove all files in the folder.");
+        } else {
+                primary_text = g_strdup_printf (_("Replace file “%s”?"), dest_name);
+                message = g_strdup_printf
+                        (_("A file with the same name as the extract destination already exists in “%s”."),
+                         dest_dir_name);
+                message_extra = _("Replacing it will overwrite its content.");
+        }
+
+        secondary_text = g_strdup_printf ("%s\n%s", message, message_extra);
+
+        nautilus_file_conflict_dialog_set_text (data->dialog,
+                                                primary_text,
+                                                secondary_text);
+}
+
+static void
 set_images (FileConflictDialogData *data)
 {
         GdkPixbuf *source_pixbuf;
@@ -306,6 +349,34 @@ copy_move_file_list_ready_cb (GList    *files,
 }
 
 static void
+extract_file_list_ready_cb (GList    *files,
+                            gpointer  user_data)
+{
+        FileConflictDialogData *data = user_data;
+
+        data->handle = NULL;
+
+        set_extract_dialog_text (data);
+
+        set_images (data);
+
+        set_file_labels (data);
+
+        set_conflict_name (data);
+
+        nautilus_file_conflict_dialog_disable_skip (data->dialog);
+        nautilus_file_conflict_dialog_disable_apply_to_all (data->dialog);
+
+        nautilus_file_monitor_add (data->source, data, NAUTILUS_FILE_ATTRIBUTES_FOR_ICON);
+        nautilus_file_monitor_add (data->destination, data, NAUTILUS_FILE_ATTRIBUTES_FOR_ICON);
+
+        data->src_handler_id = g_signal_connect (data->source, "changed",
+                          G_CALLBACK (file_icons_changed), data);
+        data->dest_handler_id = g_signal_connect (data->destination, "changed",
+                          G_CALLBACK (file_icons_changed), data);
+}
+
+static void
 build_dialog_appearance (FileConflictDialogData *data)
 {
         GList *files = NULL;
@@ -427,3 +498,89 @@ get_copy_move_file_conflict_response (GtkWindow *parent_window,
 
         return get_file_conflict_response (data);
 }
+
+FileConflictResponse *
+get_extract_file_conflict_response (GtkWindow *parent_window,
+                                    GFile     *src_name,
+                                    GFile     *dest_name,
+                                    GFile     *dest_dir_name)
+{
+        FileConflictDialogData *data;
+
+        data = g_slice_new0 (FileConflictDialogData);
+        data->parent = parent_window;
+        data->src_name = src_name;
+        data->dest_name = dest_name;
+        data->dest_dir_name = dest_dir_name;
+
+        data->create_dialog = create_extract_dialog;
+        data->files_list_ready_cb = extract_file_list_ready_cb;
+
+        return get_file_conflict_response (data);
+}
+
+typedef struct {
+        GFile *source;
+
+        GtkWindow *parent;
+
+        NameConflictResponse *response;
+
+        GtkWidget *dialog;
+        /* Dialogs are ran from operation threads, which need to be blocked until
+         * the user gives a valid response
+         */
+        gboolean completed;
+        GMutex mutex;
+        GCond cond;
+} NameConflictData;
+
+static gboolean
+run_name_conflict_dialog (gpointer _data)
+{
+        NameConflictData *data = _data;
+
+
+
+
+        return FALSE;
+}
+
+
+NameConflictResponse *
+get_extract_name_conflict_response (GtkWindow *parent_window,
+                                    GFile     *source)
+{
+        NameConflictData *data;
+        NameConflictResponse *response;
+
+        data = g_slice_new0 (NameConflictResponse);
+        data->parent = parent_window;
+        data->source = source;
+
+        response = g_slice_new0 (NameConflictResponse);
+        response->new_name = NULL;
+        data->response = response;
+
+        data->completed = FALSE;
+        g_mutex_init (&data->mutex);
+        g_cond_init (&data->cond);
+
+        g_mutex_lock (&data->mutex);
+
+        g_main_context_invoke (NULL,
+                               run_name_conflict_dialog,
+                               data);
+
+        while (!data->completed) {
+                g_cond_wait (&data->cond, &data->mutex);
+        }
+
+        g_mutex_unlock (&data->mutex);
+        g_mutex_clear (&data->mutex);
+        g_cond_clear (&data->cond);
+
+        g_slice_free (FileConflictDialogData, data);
+
+        return response;
+}
diff --git a/src/nautilus-operations-manager.h b/src/nautilus-operations-manager.h
index 84d15d0..c91bafd 100644
--- a/src/nautilus-operations-manager.h
+++ b/src/nautilus-operations-manager.h
@@ -13,5 +13,17 @@ FileConflictResponse * get_copy_move_file_conflict_response (GtkWindow *parent_w
                                                              GFile     *src,
                                                              GFile     *dest,
                                                              GFile     *dest_dir);
+FileConflictResponse * get_extract_file_conflict_response (GtkWindow *parent_window,
+                                                           GFile     *src,
+                                                           GFile     *dest,
+                                                           GFile     *dest_dir);
+
+typedef struct {
+    int id;
+    char *new_name;
+} NameConflictResponse;
+
+NameConflictResponse * get_extract_name_conflict_response (GtkWindow *parent_window,
+                                                           GFile     *source);
 
 #endif



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