[nautilus/wip/razvan/compression-support: 10/13] file-operations: implement extract operation



commit 37e7ec285e226e542c4381c9f312b44326b4042a
Author: Razvan Chitu <razvan ch95 gmail com>
Date:   Fri Aug 19 12:03:58 2016 +0300

    file-operations: implement extract operation
    
    Add a new operation for extracting archives using gnome-autoar.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=768646

 configure.ac                        |    2 +
 src/nautilus-file-operations.c      |  333 +++++++++++++++++++++++++++++++++++
 src/nautilus-file-operations.h      |    8 +-
 src/nautilus-file-undo-operations.c |  157 ++++++++++++++++
 src/nautilus-file-undo-operations.h |   29 +++
 src/nautilus-file-utilities.c       |  102 +++++++-----
 src/nautilus-file-utilities.h       |    1 +
 7 files changed, 590 insertions(+), 42 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index b875402..01b4382 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.
@@ -268,6 +269,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-file-operations.c b/src/nautilus-file-operations.c
index 3b3a6b4..0141248 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/gnome-autoar.h>
+
 #include "nautilus-operations-ui-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,11 +173,38 @@ typedef struct {
        int last_reported_files_left;
 } TransferInfo;
 
+typedef struct {
+        CommonJob common;
+        GFile *source;
+        GFile *destination_directory;
+
+        AutoarExtractor *extractor;
+
+        GFile *decided_destination;
+        gboolean output_created;
+
+        guint64 total_size;
+        guint total_files;
+} ExtractJob;
+
+typedef struct {
+       GList *extracted_files;
+       gint operations_left;
+
+        NautilusFileUndoInfo *undo_info;
+
+        NautilusExtractCallback done_callback;
+        gpointer done_callback_data;
+} ExtractBatch;
+
 #define SECONDS_NEEDED_FOR_RELIABLE_TRANSFER_RATE 8
 #define NSEC_PER_MICROSEC 1000
+#define PROGRESS_NOTIFY_INTERVAL 100 * NSEC_PER_MICROSEC
 
 #define MAXIMUM_DISPLAYED_FILE_NAME_LENGTH 50
 
+#define UNIQUE_FILE_HASH_LENGTH 5
+
 #define IS_IO_ERROR(__error, KIND) (((__error)->domain == G_IO_ERROR && (__error)->code == G_IO_ERROR_ ## 
KIND))
 
 #define CANCEL _("_Cancel")
@@ -6984,6 +7014,309 @@ nautilus_file_mark_desktop_file_trusted (GFile *file,
        g_object_unref (task);
 }
 
+static void
+extract_task_done (GObject      *source_object,
+                   GAsyncResult *res,
+                   gpointer      user_data)
+{
+        ExtractBatch *batch;
+        ExtractJob *extract_job;
+
+        batch = user_data;
+        extract_job = g_task_get_task_data (G_TASK (res));
+
+        batch->operations_left--;
+
+        if (extract_job->output_created) {
+                batch->extracted_files = g_list_prepend (batch->extracted_files,
+                                                         g_object_ref (extract_job->decided_destination));
+        }
+
+        if (batch->operations_left == 0) {
+                batch->extracted_files = g_list_reverse (batch->extracted_files);
+
+                if (batch->done_callback) {
+                        batch->done_callback (batch->extracted_files,
+                                              batch->done_callback_data);
+                }
+
+                if (batch->undo_info) {
+                        nautilus_file_undo_info_extract_set_outputs (NAUTILUS_FILE_UNDO_INFO_EXTRACT 
(batch->undo_info),
+                                                                     batch->extracted_files);
+                        extract_job->common.undo_info = g_object_ref (batch->undo_info);
+                }
+
+                g_clear_object (&batch->undo_info);
+                g_list_free_full (batch->extracted_files, g_object_unref);
+                g_slice_free (ExtractBatch, batch);
+        }
+
+        g_signal_handlers_disconnect_by_data (extract_job->extractor,
+                                              extract_job);
+
+        g_clear_object (&extract_job->source);
+        g_clear_object (&extract_job->destination_directory);
+        g_clear_object (&extract_job->extractor);
+        g_clear_object (&extract_job->decided_destination);
+
+        finalize_common ((CommonJob *)extract_job);
+
+        nautilus_file_changes_consume_changes (TRUE);
+}
+
+static void
+extract_scanned_handler (AutoarExtractor *extractor,
+                         guint            files_count,
+                         gpointer         user_data)
+{
+        ExtractJob *extract_job = user_data;
+
+        extract_job->total_files = autoar_extractor_get_total_files (extractor);
+        extract_job->total_size = autoar_extractor_get_total_size (extractor);
+}
+
+static GFile*
+extract_decide_destination_handler (AutoarExtractor *extractor,
+                                    GFile           *destination,
+                                    GList           *files,
+                                    gpointer         user_data)
+{
+        ExtractJob *extract_job = user_data;
+
+        nautilus_progress_info_set_details (extract_job->common.progress,
+                                            _("Verifying destination"));
+
+        extract_job->decided_destination =
+                nautilus_ensure_file_unique_in_parent_directory (destination);
+
+        if (job_aborted ((CommonJob *)extract_job)) {
+                return NULL;
+        }
+
+        return g_object_ref (extract_job->decided_destination);
+}
+
+static void
+extract_progress_handler (AutoarExtractor *extractor,
+                          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;
+        double 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 = -1;
+
+        if (elapsed > 0) {
+                if (completed_size > 0) {
+                        transfer_rate = completed_size / elapsed;
+                        remaining_time = (extract_job->total_size - completed_size) / transfer_rate;
+                } else if (completed_files > 0) {
+                        transfer_rate = completed_files / elapsed;
+                        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 (AutoarExtractor *extractor,
+                       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);
+
+        abort_job ((CommonJob *)extract_job);
+}
+
+static void
+extract_completed_handler (AutoarExtractor *extractor,
+                           gpointer         user_data)
+{
+        ExtractJob *extract_job = user_data;
+
+        nautilus_file_changes_queue_file_added (extract_job->decided_destination);
+
+        nautilus_progress_info_take_status (extract_job->common.progress,
+                                            f (_("Extracted “%B” to “%B”"),
+                                               extract_job->source,
+                                               extract_job->destination_directory));
+
+        nautilus_progress_info_set_destination (extract_job->common.progress,
+                                                extract_job->destination_directory);
+}
+
+static void
+extract_task_thread_func (GTask        *task,
+                          gpointer      source_object,
+                          gpointer      task_data,
+                          GCancellable *cancellable)
+{
+        ExtractJob *extract_job = task_data;
+
+        extract_job->extractor = autoar_extractor_new (extract_job->source,
+                                                       extract_job->destination_directory);
+
+        autoar_extractor_set_notify_interval (extract_job->extractor,
+                                              PROGRESS_NOTIFY_INTERVAL);
+
+        g_signal_connect (extract_job->extractor, "scanned",
+                          G_CALLBACK (extract_scanned_handler), extract_job);
+        g_signal_connect (extract_job->extractor, "error",
+                          G_CALLBACK (extract_error_handler), extract_job);
+        g_signal_connect (extract_job->extractor, "decide-destination",
+                          G_CALLBACK (extract_decide_destination_handler), extract_job);
+        g_signal_connect (extract_job->extractor, "progress",
+                          G_CALLBACK (extract_progress_handler), extract_job);
+        g_signal_connect (extract_job->extractor, "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_extractor_start (extract_job->extractor, extract_job->common.cancellable);
+
+        if (g_file_query_exists (extract_job->decided_destination, NULL)) {
+                extract_job->output_created = TRUE;
+        }
+}
+
+void
+nautilus_file_operations_extract_files (GList                   *files,
+                                        GFile                   *destination_directory,
+                                        GtkWindow               *parent_window,
+                                        NautilusExtractCallback  done_callback,
+                                        gpointer                 done_callback_data)
+{
+        ExtractBatch *batch;
+        GList *l;
+
+        batch = g_slice_new0 (ExtractBatch);
+
+        batch->operations_left = g_list_length (files);
+        batch->done_callback = done_callback;
+        batch->done_callback_data = done_callback_data;
+
+        if (!nautilus_file_undo_manager_is_operating ()) {
+                batch->undo_info = 
+                        nautilus_file_undo_info_extract_new (files,
+                                                             destination_directory);
+        }
+
+        for (l = files; l != NULL; l = l->next) {
+                GFile *source;
+                g_autoptr (GTask) task;
+                ExtractJob *extract_job;
+
+                source = l->data;
+
+                extract_job = op_job_new (ExtractJob, parent_window);
+                extract_job->source = g_object_ref (source);
+                extract_job->destination_directory = g_object_ref (destination_directory);
+
+                inhibit_power_manager ((CommonJob *)extract_job, _("Extracting Files"));
+
+                task = g_task_new (NULL, extract_job->common.cancellable, extract_task_done, batch);
+                g_task_set_task_data (task, extract_job, NULL);
+                g_task_run_in_thread (task, extract_task_thread_func);
+        }
+}
+
 #if !defined (NAUTILUS_OMIT_SELF_CHECK)
 
 void
diff --git a/src/nautilus-file-operations.h b/src/nautilus-file-operations.h
index 08b11ca..4377a90 100644
--- a/src/nautilus-file-operations.h
+++ b/src/nautilus-file-operations.h
@@ -44,6 +44,8 @@ typedef void (* NautilusMountCallback)     (GVolume    *volume,
                                            gboolean    success,
                                            GObject    *callback_data_object);
 typedef void (* NautilusUnmountCallback)   (gpointer    callback_data);
+typedef void (* NautilusExtractCallback)   (GList    *outputs,
+                                            gpointer  callback_data);
 
 /* FIXME: int copy_action should be an enum */
 
@@ -147,6 +149,10 @@ void nautilus_file_mark_desktop_file_trusted (GFile           *file,
                                              gboolean          interactive,
                                              NautilusOpCallback done_callback,
                                              gpointer          done_callback_data);
-
+void nautilus_file_operations_extract_files (GList                   *files,
+                                             GFile                   *destination_directory,
+                                             GtkWindow               *parent_window,
+                                             NautilusExtractCallback  done_callback,
+                                             gpointer                 done_callback_data);
 
 #endif /* NAUTILUS_FILE_OPERATIONS_H */
diff --git a/src/nautilus-file-undo-operations.c b/src/nautilus-file-undo-operations.c
index 17b8c1d..6025848 100644
--- a/src/nautilus-file-undo-operations.c
+++ b/src/nautilus-file-undo-operations.c
@@ -1692,3 +1692,160 @@ nautilus_file_undo_info_ownership_new (NautilusFileUndoOp  op_type,
 
        return NAUTILUS_FILE_UNDO_INFO (retval);
 }
+
+/* extract */
+G_DEFINE_TYPE (NautilusFileUndoInfoExtract, nautilus_file_undo_info_extract, NAUTILUS_TYPE_FILE_UNDO_INFO)
+
+struct _NautilusFileUndoInfoExtractDetails {
+        GList *sources;
+        GFile *destination_directory;
+        GList *outputs;
+};
+
+static void
+extract_callback (GList    *outputs,
+                  gpointer  callback_data)
+{
+        NautilusFileUndoInfoExtract *self = NAUTILUS_FILE_UNDO_INFO_EXTRACT (callback_data);
+        gboolean success;
+
+        nautilus_file_undo_info_extract_set_outputs (self, outputs);
+
+        success = self->priv->outputs != NULL;
+
+        file_undo_info_transfer_callback (NULL, success, self);
+}
+
+static void
+extract_strings_func (NautilusFileUndoInfo  *info,
+                      gchar                **undo_label,
+                      gchar                **undo_description,
+                      gchar                **redo_label,
+                      gchar                **redo_description)
+{
+        NautilusFileUndoInfoExtract *self = NAUTILUS_FILE_UNDO_INFO_EXTRACT (info);
+        gint total_sources;
+        gint total_outputs;
+
+        *undo_label = g_strdup (_("_Undo Extract"));
+        *redo_label = g_strdup (_("_Redo Extract"));
+
+        total_sources = g_list_length (self->priv->sources);
+        total_outputs = g_list_length (self->priv->outputs);
+
+        if (total_outputs == 1) {
+                GFile *output;
+                g_autofree gchar *name;
+
+                output = self->priv->outputs->data;
+                name = g_file_get_parse_name (output);
+
+                *undo_description = g_strdup_printf (_("Delete '%s'"), name);
+        } else {
+                *undo_description = g_strdup_printf (_("Delete %d extracted files"),
+                                                     total_outputs);
+        }
+
+        if (total_sources == 1) {
+                GFile *source;
+                g_autofree gchar *name;
+
+                source = self->priv->sources->data;
+                name = g_file_get_parse_name (source);
+
+                *undo_description = g_strdup_printf (_("Extract '%s'"), name);
+        } else {
+                *undo_description = g_strdup_printf (_("Extract %d files"),
+                                                     total_sources);
+        }
+}
+
+static void
+extract_redo_func (NautilusFileUndoInfo *info,
+                   GtkWindow            *parent_window)
+{
+        NautilusFileUndoInfoExtract *self = NAUTILUS_FILE_UNDO_INFO_EXTRACT (info);
+
+        nautilus_file_operations_extract_files (self->priv->sources,
+                                                self->priv->destination_directory,
+                                                parent_window,
+                                                extract_callback,
+                                                self);
+}
+
+static void
+extract_undo_func (NautilusFileUndoInfo *info,
+                   GtkWindow            *parent_window)
+{
+        NautilusFileUndoInfoExtract *self = NAUTILUS_FILE_UNDO_INFO_EXTRACT (info);
+
+        nautilus_file_operations_delete (self->priv->outputs, parent_window,
+                                         file_undo_info_delete_callback, self);
+}
+
+static void
+nautilus_file_undo_info_extract_init (NautilusFileUndoInfoExtract *self)
+{
+        self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, nautilus_file_undo_info_extract_get_type (),
+                                                  NautilusFileUndoInfoExtractDetails);
+}
+
+static void
+nautilus_file_undo_info_extract_finalize (GObject *obj)
+{
+        NautilusFileUndoInfoExtract *self = NAUTILUS_FILE_UNDO_INFO_EXTRACT (obj);
+
+        g_object_unref (self->priv->destination_directory);
+        g_list_free_full (self->priv->sources, g_object_unref);
+        if (self->priv->outputs) {
+                g_list_free_full (self->priv->outputs, g_object_unref);
+        }
+
+        G_OBJECT_CLASS (nautilus_file_undo_info_extract_parent_class)->finalize (obj);
+}
+
+static void
+nautilus_file_undo_info_extract_class_init (NautilusFileUndoInfoExtractClass *klass)
+{
+        GObjectClass *oclass = G_OBJECT_CLASS (klass);
+        NautilusFileUndoInfoClass *iclass = NAUTILUS_FILE_UNDO_INFO_CLASS (klass);
+
+        oclass->finalize = nautilus_file_undo_info_extract_finalize;
+
+        iclass->undo_func = extract_undo_func;
+        iclass->redo_func = extract_redo_func;
+        iclass->strings_func = extract_strings_func;
+
+        g_type_class_add_private (klass, sizeof (NautilusFileUndoInfoExtractDetails));
+}
+
+void
+nautilus_file_undo_info_extract_set_outputs (NautilusFileUndoInfoExtract *self,
+                                             GList                       *outputs)
+{
+        if (self->priv->outputs) {
+                g_list_free_full (self->priv->outputs, g_object_unref);
+        }
+        self->priv->outputs = g_list_copy_deep (outputs,
+                                                (GCopyFunc)g_object_ref,
+                                                NULL);
+}
+
+NautilusFileUndoInfo *
+nautilus_file_undo_info_extract_new (GList *sources,
+                                     GFile *destination_directory)
+{
+        NautilusFileUndoInfoExtract *self;
+
+        self = g_object_new (NAUTILUS_TYPE_FILE_UNDO_INFO_EXTRACT,
+                             "item-count", 1,
+                             "op-type", NAUTILUS_FILE_UNDO_OP_EXTRACT,
+                             NULL);
+
+        self->priv->sources = g_list_copy_deep (sources,
+                                                (GCopyFunc)g_object_ref,
+                                                NULL);
+        self->priv->destination_directory = g_object_ref (destination_directory);
+
+        return NAUTILUS_FILE_UNDO_INFO (self);
+}
diff --git a/src/nautilus-file-undo-operations.h b/src/nautilus-file-undo-operations.h
index cec1c7c..ef01363 100644
--- a/src/nautilus-file-undo-operations.h
+++ b/src/nautilus-file-undo-operations.h
@@ -36,6 +36,7 @@ typedef enum {
        NAUTILUS_FILE_UNDO_OP_CREATE_EMPTY_FILE,
        NAUTILUS_FILE_UNDO_OP_CREATE_FILE_FROM_TEMPLATE,
        NAUTILUS_FILE_UNDO_OP_CREATE_FOLDER,
+       NAUTILUS_FILE_UNDO_OP_EXTRACT,
        NAUTILUS_FILE_UNDO_OP_MOVE_TO_TRASH,
        NAUTILUS_FILE_UNDO_OP_RESTORE_FROM_TRASH,
        NAUTILUS_FILE_UNDO_OP_CREATE_LINK,
@@ -296,4 +297,32 @@ NautilusFileUndoInfo *nautilus_file_undo_info_ownership_new (NautilusFileUndoOp
                                                             const char         *current_data,
                                                             const char         *new_data);
 
+/* extract */
+#define NAUTILUS_TYPE_FILE_UNDO_INFO_EXTRACT         (nautilus_file_undo_info_extract_get_type ())
+#define NAUTILUS_FILE_UNDO_INFO_EXTRACT(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), 
NAUTILUS_TYPE_FILE_UNDO_INFO_EXTRACT, NautilusFileUndoInfoExtract))
+#define NAUTILUS_FILE_UNDO_INFO_EXTRACT_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), 
NAUTILUS_TYPE_FILE_UNDO_INFO_EXTRACT, NautilusFileUndoInfoExtractClass))
+#define NAUTILUS_IS_FILE_UNDO_INFO_EXTRACT(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), 
NAUTILUS_TYPE_FILE_UNDO_INFO_EXTRACT))
+#define NAUTILUS_IS_FILE_UNDO_INFO_EXTRACT_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), 
NAUTILUS_TYPE_FILE_UNDO_INFO_EXTRACT))
+#define NAUTILUS_FILE_UNDO_INFO_EXTRACT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), 
NAUTILUS_TYPE_FILE_UNDO_INFO_EXTRACT, NautilusFileUndoInfoExtractClass))
+
+typedef struct _NautilusFileUndoInfoExtract        NautilusFileUndoInfoExtract;
+typedef struct _NautilusFileUndoInfoExtractClass   NautilusFileUndoInfoExtractClass;
+typedef struct _NautilusFileUndoInfoExtractDetails NautilusFileUndoInfoExtractDetails;
+
+struct _NautilusFileUndoInfoExtract {
+        NautilusFileUndoInfo parent;
+        NautilusFileUndoInfoExtractDetails *priv;
+};
+
+struct _NautilusFileUndoInfoExtractClass {
+        NautilusFileUndoInfoClass parent_class;
+};
+
+GType nautilus_file_undo_info_extract_get_type (void) G_GNUC_CONST;
+NautilusFileUndoInfo * nautilus_file_undo_info_extract_new (GList *sources,
+                                                            GFile *destination_directory);
+void nautilus_file_undo_info_extract_set_outputs (NautilusFileUndoInfoExtract *self,
+                                                  GList                       *outputs);
+
+
 #endif /* __NAUTILUS_FILE_UNDO_OPERATIONS_H__ */
diff --git a/src/nautilus-file-utilities.c b/src/nautilus-file-utilities.c
index 14929aa..993f470 100644
--- a/src/nautilus-file-utilities.c
+++ b/src/nautilus-file-utilities.c
@@ -585,50 +585,70 @@ nautilus_get_mounted_mount_for_root (GFile *location)
 
 char *
 nautilus_ensure_unique_file_name (const char *directory_uri,
-                                 const char *base_name,
-                                 const char *extension)
+                                  const char *base_name,
+                                  const char *extension)
 {
-       GFileInfo *info;
-       char *filename;
-       GFile *dir, *child;
-       int copy;
-       char *res;
+        g_autofree char *initial_name = NULL;
+        g_autoptr (GFile) dir = NULL;
+        g_autoptr (GFile) child = NULL;
+        int copy;
+        char *res;
 
-       dir = g_file_new_for_uri (directory_uri);
+        dir = g_file_new_for_uri (directory_uri);
 
-       info = g_file_query_info (dir, G_FILE_ATTRIBUTE_STANDARD_TYPE, 0, NULL, NULL);
-       if (info == NULL) {
-               g_object_unref (dir);
-               return NULL;
-       }
-       g_object_unref (info);
+        if (!g_file_query_exists (dir, NULL)) {
+                return NULL;
+        }
 
-       filename = g_strdup_printf ("%s%s",
-                                   base_name,
-                                   extension);
-       child = g_file_get_child (dir, filename);
-       g_free (filename);
-       
-       copy = 1;
-       while ((info = g_file_query_info (child, G_FILE_ATTRIBUTE_STANDARD_TYPE, 0, NULL, NULL)) != NULL) {
-               g_object_unref (info);
-               g_object_unref (child);
-               
-               filename = g_strdup_printf ("%s-%d%s",
-                                           base_name,
-                                           copy,
-                                           extension);
-               child = g_file_get_child (dir, filename);
-               g_free (filename);
-               
-               copy++;
-       }
+        initial_name = g_strdup_printf ("%s%s",
+                                        base_name,
+                                        extension ? extension : "");
+        child = g_file_get_child (dir, initial_name);
 
-       res = g_file_get_uri (child);
-       g_object_unref (child);
-       g_object_unref (dir);
-       
-       return res;
+        copy = 1;
+        while (g_file_query_exists (child, NULL)) {
+                g_autofree char *filename;
+
+                g_object_unref (child);
+
+                filename = g_strdup_printf ("%s (%d)%s",
+                                            base_name,
+                                            copy,
+                                            extension ? extension : "");
+                child = g_file_get_child (dir, filename);
+
+                copy++;
+        }
+
+        res = g_file_get_uri (child);
+
+        return res;
+}
+
+GFile *
+nautilus_ensure_file_unique_in_parent_directory (GFile *initial)
+{
+        g_autoptr (GFile) parent = NULL;
+        g_autofree char *parent_uri = NULL;
+        g_autofree char *basename = NULL;
+        g_autofree char *basename_without_extension = NULL;
+        const char *extension;
+        g_autofree char *unique_name = NULL;
+
+        parent = g_file_get_parent (initial);
+
+        g_return_val_if_fail (parent != NULL, NULL);
+
+        parent_uri = g_file_get_uri (parent);
+        basename = g_file_get_basename (initial);
+        basename_without_extension = eel_filename_strip_extension (basename);
+        extension = eel_filename_get_extension_offset (basename);
+
+        unique_name = nautilus_ensure_unique_file_name (parent_uri,
+                                                        basename_without_extension,
+                                                        extension);
+
+        return g_file_new_for_uri (unique_name);
 }
 
 GFile *
diff --git a/src/nautilus-file-utilities.h b/src/nautilus-file-utilities.h
index 6d46df5..f9a5473 100644
--- a/src/nautilus-file-utilities.h
+++ b/src/nautilus-file-utilities.h
@@ -71,6 +71,7 @@ gboolean nautilus_uri_parse                          (const char  *uri,
 char *   nautilus_ensure_unique_file_name            (const char *directory_uri,
                                                      const char *base_name,
                                                      const char *extension);
+GFile * nautilus_ensure_file_unique_in_parent_directory (GFile *initial);
 
 GFile *  nautilus_find_existing_uri_in_hierarchy     (GFile *location);
 


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