summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRazvan Chitu <razvan.ch95@gmail.com>2016-08-21 12:22:29 +0300
committerRazvan Chitu <razvan.ch95@gmail.com>2016-08-22 12:29:03 +0300
commite5f68699d8c100b1ad299b69c9245644e3965d9e (patch)
tree8bd7095eb46bfec7f188593c554dd1ec35669dd3
parentf8a58f3cf26d963e25505047f241dbb14c3565df (diff)
downloadnautilus-e5f68699d8c100b1ad299b69c9245644e3965d9e.tar.gz
file-operations: implement compression operation
Add an operation for compressing files using gnome-autoar. The operation is similar in functionality to the one offered by file roller but comes with integrated progress feedback and support for undoing and redoing. https://bugzilla.gnome.org/show_bug.cgi?id=770199
-rw-r--r--src/nautilus-file-operations.c319
-rw-r--r--src/nautilus-file-operations.h9
-rw-r--r--src/nautilus-file-undo-operations.c142
-rw-r--r--src/nautilus-file-undo-operations.h29
4 files changed, 495 insertions, 4 deletions
diff --git a/src/nautilus-file-operations.c b/src/nautilus-file-operations.c
index 7b2f72e5c..ad6a25cfc 100644
--- a/src/nautilus-file-operations.c
+++ b/src/nautilus-file-operations.c
@@ -51,7 +51,6 @@
#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"
@@ -153,7 +152,8 @@ typedef enum {
OP_KIND_COPY,
OP_KIND_MOVE,
OP_KIND_DELETE,
- OP_KIND_TRASH
+ OP_KIND_TRASH,
+ OP_KIND_COMPRESS
} OpKind;
typedef struct {
@@ -186,6 +186,23 @@ typedef struct {
gpointer done_callback_data;
} ExtractJob;
+typedef struct {
+ CommonJob common;
+ GList *source_files;
+ GFile *output_file;
+
+ AutoarFormat format;
+ AutoarFilter filter;
+
+ guint64 total_size;
+ guint total_files;
+
+ gboolean success;
+
+ NautilusCreateCallback done_callback;
+ gpointer done_callback_data;
+} CompressJob;
+
#define SECONDS_NEEDED_FOR_RELIABLE_TRANSFER_RATE 8
#define NSEC_PER_MICROSEC 1000
#define PROGRESS_NOTIFY_INTERVAL 100 * NSEC_PER_MICROSEC
@@ -2710,7 +2727,12 @@ report_preparing_count_progress (CommonJob *job,
source_info->num_files),
source_info->num_files);
break;
- }
+ case OP_KIND_COMPRESS:
+ s = f (ngettext("Preparing to compress %'d file",
+ "Preparing to compress %'d files",
+ source_info->num_files),
+ source_info->num_files);
+ }
nautilus_progress_info_take_details (job->progress, s);
nautilus_progress_info_pulse_progress (job->progress);
@@ -2743,7 +2765,9 @@ get_scan_primary (OpKind kind)
return f (_("Error while deleting."));
case OP_KIND_TRASH:
return f (_("Error while moving files to trash."));
- }
+ case OP_KIND_COMPRESS:
+ return f (_("Error while compressing files."));
+ }
}
static void
@@ -7403,6 +7427,293 @@ nautilus_file_operations_extract_files (GList *files,
g_task_run_in_thread (task, extract_task_thread_func);
}
+static void
+compress_task_done (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ CompressJob *compress_job = user_data;
+
+ if (compress_job->done_callback) {
+ compress_job->done_callback (compress_job->output_file,
+ compress_job->success,
+ compress_job->done_callback_data);
+ }
+
+ g_object_unref (compress_job->output_file);
+ g_list_free_full (compress_job->source_files, g_object_unref);
+
+ finalize_common ((CommonJob *)compress_job);
+
+ nautilus_file_changes_consume_changes (TRUE);
+}
+
+static void
+compress_job_on_progress (AutoarCompressor *compressor,
+ guint64 completed_size,
+ guint completed_files,
+ gpointer user_data)
+{
+ CompressJob *compress_job = user_data;
+ CommonJob *common = user_data;
+ char *status;
+ char *details;
+ int files_left;
+ double elapsed;
+ double transfer_rate;
+ int remaining_time;
+
+ files_left = compress_job->total_files - completed_files;
+
+ if (compress_job->total_files == 1) {
+ status = f (_("Compressing “%B” into “%B”"),
+ G_FILE (compress_job->source_files->data),
+ compress_job->output_file);
+ } else {
+ status = f (ngettext ("Compressing %'d file into “%B”",
+ "Compressing %'d files into “%B”",
+ compress_job->total_files),
+ compress_job->total_files,
+ compress_job->output_file);
+
+ }
+
+ nautilus_progress_info_take_status (common->progress, status);
+
+ 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 = (compress_job->total_size - completed_size) / transfer_rate;
+ } else if (completed_files > 0) {
+ transfer_rate = completed_files / elapsed;
+ remaining_time = (compress_job->total_files - completed_files) / transfer_rate;
+ }
+ }
+
+ if (elapsed < SECONDS_NEEDED_FOR_RELIABLE_TRANSFER_RATE ||
+ transfer_rate == 0) {
+ if (compress_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, compress_job->total_size);
+ } else {
+ details = f (_("%'d / %'d"),
+ files_left > 0 ? completed_files + 1 : completed_files,
+ compress_job->total_files);
+ }
+ } else {
+ if (compress_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, compress_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,
+ compress_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, compress_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,
+ compress_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,
+ compress_job->total_size);
+}
+
+static void
+compress_job_on_error (AutoarCompressor *compressor,
+ GError *error,
+ gpointer user_data)
+{
+ CompressJob *compress_job = user_data;
+ char *status;
+
+ if (compress_job->total_files == 1) {
+ status = f (_("Error compressing “%B” into “%B”"),
+ G_FILE (compress_job->source_files->data),
+ compress_job->output_file);
+ } else {
+ status = f (ngettext ("Error compressing %'d file into “%B”",
+ "Error compressing %'d files into “%B”",
+ compress_job->total_files),
+ compress_job->total_files,
+ compress_job->output_file);
+ }
+
+ nautilus_progress_info_take_status (compress_job->common.progress,
+ status);
+
+ run_error ((CommonJob *)compress_job,
+ _("There was an error while compressing files."),
+ g_strdup (error->message),
+ NULL,
+ FALSE,
+ CANCEL,
+ NULL);
+
+ abort_job ((CommonJob *)compress_job);
+}
+
+static void
+compress_job_on_completed (AutoarCompressor *compressor,
+ gpointer user_data)
+{
+ CompressJob *compress_job = user_data;
+ g_autoptr (GFile) destination_directory;
+ char *status;
+
+ if (compress_job->total_files == 1) {
+ status = f (_("Compressed “%B” into “%B”"),
+ G_FILE (compress_job->source_files->data),
+ compress_job->output_file);
+ } else {
+ status = f (ngettext ("Compressed %'d file into “%B”",
+ "Compressed %'d files into “%B”",
+ compress_job->total_files),
+ compress_job->total_files,
+ compress_job->output_file);
+ }
+
+ nautilus_progress_info_take_status (compress_job->common.progress,
+ status);
+
+ nautilus_file_changes_queue_file_added (compress_job->output_file);
+
+ destination_directory = g_file_get_parent (compress_job->output_file);
+ nautilus_progress_info_set_destination (compress_job->common.progress,
+ destination_directory);
+}
+
+static void
+compress_task_thread_func (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ CompressJob *compress_job = task_data;
+ SourceInfo source_info;
+ g_autoptr (AutoarCompressor) compressor;
+
+ g_timer_start (compress_job->common.time);
+
+ nautilus_progress_info_start (compress_job->common.progress);
+
+ scan_sources (compress_job->source_files,
+ &source_info,
+ (CommonJob *)compress_job,
+ OP_KIND_COMPRESS);
+
+ compress_job->total_files = source_info.num_files;
+ compress_job->total_size = source_info.num_bytes;
+
+ compressor = autoar_compressor_new (compress_job->source_files,
+ compress_job->output_file,
+ compress_job->format,
+ compress_job->filter,
+ FALSE);
+
+ autoar_compressor_set_output_is_dest (compressor, TRUE);
+
+ autoar_compressor_set_notify_interval (compressor,
+ PROGRESS_NOTIFY_INTERVAL);
+
+ g_signal_connect (compressor, "progress",
+ G_CALLBACK (compress_job_on_progress), compress_job);
+ g_signal_connect (compressor, "error",
+ G_CALLBACK (compress_job_on_error), compress_job);
+ g_signal_connect (compressor, "completed",
+ G_CALLBACK (compress_job_on_completed), compress_job);
+ autoar_compressor_start (compressor,
+ compress_job->common.cancellable);
+
+ compress_job->success = g_file_query_exists (compress_job->output_file,
+ NULL);
+
+ /* There is nothing to undo if the output was not created */
+ if (compress_job->common.undo_info != NULL && !compress_job->success) {
+ g_clear_object (&compress_job->common.undo_info);
+ }
+}
+
+void
+nautilus_file_operations_compress (GList *files,
+ GFile *output,
+ AutoarFormat format,
+ AutoarFilter filter,
+ GtkWindow *parent_window,
+ NautilusCreateCallback done_callback,
+ gpointer done_callback_data)
+{
+ g_autoptr (GTask) task;
+ CompressJob *compress_job;
+
+ compress_job = op_job_new (CompressJob, parent_window);
+ compress_job->source_files = g_list_copy_deep (files,
+ (GCopyFunc)g_object_ref,
+ NULL);
+ compress_job->output_file = g_object_ref (output);
+ compress_job->format = format;
+ compress_job->filter = filter;
+ compress_job->done_callback = done_callback;
+ compress_job->done_callback_data = done_callback_data;
+
+ inhibit_power_manager ((CommonJob *)compress_job, _("Compressing Files"));
+
+ if (!nautilus_file_undo_manager_is_operating ()) {
+ compress_job->common.undo_info = nautilus_file_undo_info_compress_new (files,
+ output,
+ format,
+ filter);
+ }
+
+ task = g_task_new (NULL, compress_job->common.cancellable,
+ compress_task_done, compress_job);
+ g_task_set_task_data (task, compress_job, NULL);
+ g_task_run_in_thread (task, compress_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 fb209d136..31b782dba 100644
--- a/src/nautilus-file-operations.h
+++ b/src/nautilus-file-operations.h
@@ -26,6 +26,8 @@
#include <gtk/gtk.h>
#include <gio/gio.h>
+#include <gnome-autoar/gnome-autoar.h>
+
#define SECONDS_NEEDED_FOR_APROXIMATE_TRANSFER_RATE 1
@@ -154,6 +156,13 @@ void nautilus_file_operations_extract_files (GList *files,
GtkWindow *parent_window,
NautilusExtractCallback done_callback,
gpointer done_callback_data);
+void nautilus_file_operations_compress (GList *files,
+ GFile *output,
+ AutoarFormat format,
+ AutoarFilter filter,
+ GtkWindow *parent_window,
+ NautilusCreateCallback 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 6025848c9..9e9531006 100644
--- a/src/nautilus-file-undo-operations.c
+++ b/src/nautilus-file-undo-operations.c
@@ -1849,3 +1849,145 @@ nautilus_file_undo_info_extract_new (GList *sources,
return NAUTILUS_FILE_UNDO_INFO (self);
}
+
+
+/* compress */
+G_DEFINE_TYPE (NautilusFileUndoInfoCompress, nautilus_file_undo_info_compress, NAUTILUS_TYPE_FILE_UNDO_INFO)
+
+struct _NautilusFileUndoInfoCompressDetails {
+ GList *sources;
+ GFile *output;
+ AutoarFormat format;
+ AutoarFilter filter;
+};
+
+static void
+compress_callback (GFile *new_file,
+ gboolean success,
+ gpointer callback_data)
+{
+ NautilusFileUndoInfoCompress *self = NAUTILUS_FILE_UNDO_INFO_COMPRESS (callback_data);
+
+ if (success) {
+ g_object_unref (self->priv->output);
+
+ self->priv->output = g_object_ref (new_file);
+ }
+
+ file_undo_info_transfer_callback (NULL, success, self);
+}
+
+static void
+compress_strings_func (NautilusFileUndoInfo *info,
+ gchar **undo_label,
+ gchar **undo_description,
+ gchar **redo_label,
+ gchar **redo_description)
+{
+ NautilusFileUndoInfoCompress *self = NAUTILUS_FILE_UNDO_INFO_COMPRESS (info);
+ g_autofree gchar *output_name;
+ gint sources_count;
+
+ output_name = g_file_get_parse_name (self->priv->output);
+ *undo_description = g_strdup_printf (_("Delete '%s'"), output_name);
+
+ sources_count = g_list_length (self->priv->sources);
+ if (sources_count == 1) {
+ GFile *source;
+ g_autofree gchar *source_name;
+
+ source = self->priv->sources->data;
+ source_name = g_file_get_parse_name (source);
+
+ *redo_description = g_strdup_printf (_("Compress '%s'"), source_name);
+ } else {
+ *redo_description = g_strdup_printf (_("Compress '%d' files"), sources_count);
+ }
+
+ *undo_label = g_strdup (_("_Undo Compress"));
+ *redo_label = g_strdup (_("_Redo Compress"));
+}
+
+static void
+compress_redo_func (NautilusFileUndoInfo *info,
+ GtkWindow *parent_window)
+{
+ NautilusFileUndoInfoCompress *self = NAUTILUS_FILE_UNDO_INFO_COMPRESS (info);
+
+ nautilus_file_operations_compress (self->priv->sources,
+ self->priv->output,
+ self->priv->format,
+ self->priv->filter,
+ parent_window,
+ compress_callback,
+ self);
+}
+
+static void
+compress_undo_func (NautilusFileUndoInfo *info,
+ GtkWindow *parent_window)
+{
+ NautilusFileUndoInfoCompress *self = NAUTILUS_FILE_UNDO_INFO_COMPRESS (info);
+ GList *files = NULL;
+
+ files = g_list_prepend (files, self->priv->output);
+
+ nautilus_file_operations_delete (files, parent_window,
+ file_undo_info_delete_callback, self);
+
+ g_list_free (files);
+}
+
+static void
+nautilus_file_undo_info_compress_init (NautilusFileUndoInfoCompress *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, nautilus_file_undo_info_compress_get_type (),
+ NautilusFileUndoInfoCompressDetails);
+}
+
+static void
+nautilus_file_undo_info_compress_finalize (GObject *obj)
+{
+ NautilusFileUndoInfoCompress *self = NAUTILUS_FILE_UNDO_INFO_COMPRESS (obj);
+
+ g_list_free_full (self->priv->sources, g_object_unref);
+ g_clear_object (&self->priv->output);
+
+ G_OBJECT_CLASS (nautilus_file_undo_info_compress_parent_class)->finalize (obj);
+}
+
+static void
+nautilus_file_undo_info_compress_class_init (NautilusFileUndoInfoCompressClass *klass)
+{
+ GObjectClass *oclass = G_OBJECT_CLASS (klass);
+ NautilusFileUndoInfoClass *iclass = NAUTILUS_FILE_UNDO_INFO_CLASS (klass);
+
+ oclass->finalize = nautilus_file_undo_info_compress_finalize;
+
+ iclass->undo_func = compress_undo_func;
+ iclass->redo_func = compress_redo_func;
+ iclass->strings_func = compress_strings_func;
+
+ g_type_class_add_private (klass, sizeof (NautilusFileUndoInfoCompressDetails));
+}
+
+NautilusFileUndoInfo *
+nautilus_file_undo_info_compress_new (GList *sources,
+ GFile *output,
+ AutoarFormat format,
+ AutoarFilter filter)
+{
+ NautilusFileUndoInfoCompress *self;
+
+ self = g_object_new (NAUTILUS_TYPE_FILE_UNDO_INFO_COMPRESS,
+ "item-count", 1,
+ "op-type", NAUTILUS_FILE_UNDO_OP_COMPRESS,
+ NULL);
+
+ self->priv->sources = g_list_copy_deep (sources, (GCopyFunc)g_object_ref, NULL);
+ self->priv->output = g_object_ref (output);
+ self->priv->format = format;
+ self->priv->filter = filter;
+
+ return NAUTILUS_FILE_UNDO_INFO (self);
+}
diff --git a/src/nautilus-file-undo-operations.h b/src/nautilus-file-undo-operations.h
index ef01363b6..940246916 100644
--- a/src/nautilus-file-undo-operations.h
+++ b/src/nautilus-file-undo-operations.h
@@ -27,6 +27,7 @@
#include <gio/gio.h>
#include <gtk/gtk.h>
+#include <gnome-autoar/gnome-autoar.h>
typedef enum {
NAUTILUS_FILE_UNDO_OP_COPY,
@@ -37,6 +38,7 @@ typedef enum {
NAUTILUS_FILE_UNDO_OP_CREATE_FILE_FROM_TEMPLATE,
NAUTILUS_FILE_UNDO_OP_CREATE_FOLDER,
NAUTILUS_FILE_UNDO_OP_EXTRACT,
+ NAUTILUS_FILE_UNDO_OP_COMPRESS,
NAUTILUS_FILE_UNDO_OP_MOVE_TO_TRASH,
NAUTILUS_FILE_UNDO_OP_RESTORE_FROM_TRASH,
NAUTILUS_FILE_UNDO_OP_CREATE_LINK,
@@ -324,5 +326,32 @@ NautilusFileUndoInfo * nautilus_file_undo_info_extract_new (GList *sources,
void nautilus_file_undo_info_extract_set_outputs (NautilusFileUndoInfoExtract *self,
GList *outputs);
+/* compress */
+#define NAUTILUS_TYPE_FILE_UNDO_INFO_COMPRESS (nautilus_file_undo_info_compress_get_type ())
+#define NAUTILUS_FILE_UNDO_INFO_COMPRESS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), NAUTILUS_TYPE_FILE_UNDO_INFO_COMPRESS, NautilusFileUndoInfoCompress))
+#define NAUTILUS_FILE_UNDO_INFO_COMPRESS_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), NAUTILUS_TYPE_FILE_UNDO_INFO_COMPRESS, NautilusFileUndoInfoCompressClass))
+#define NAUTILUS_IS_FILE_UNDO_INFO_COMPRESS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), NAUTILUS_TYPE_FILE_UNDO_INFO_COMPRESS))
+#define NAUTILUS_IS_FILE_UNDO_INFO_COMPRESS_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), NAUTILUS_TYPE_FILE_UNDO_INFO_COMPRESS))
+#define NAUTILUS_FILE_UNDO_INFO_COMPRESS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), NAUTILUS_TYPE_FILE_UNDO_INFO_COMPRESS, NautilusFileUndoInfoCompressClass))
+
+typedef struct _NautilusFileUndoInfoCompress NautilusFileUndoInfoCompress;
+typedef struct _NautilusFileUndoInfoCompressClass NautilusFileUndoInfoCompressClass;
+typedef struct _NautilusFileUndoInfoCompressDetails NautilusFileUndoInfoCompressDetails;
+
+struct _NautilusFileUndoInfoCompress {
+ NautilusFileUndoInfo parent;
+ NautilusFileUndoInfoCompressDetails *priv;
+};
+
+struct _NautilusFileUndoInfoCompressClass {
+ NautilusFileUndoInfoClass parent_class;
+};
+
+GType nautilus_file_undo_info_compress_get_type (void) G_GNUC_CONST;
+NautilusFileUndoInfo * nautilus_file_undo_info_compress_new (GList *sources,
+ GFile *output,
+ AutoarFormat format,
+ AutoarFilter filter);
+
#endif /* __NAUTILUS_FILE_UNDO_OPERATIONS_H__ */