diff options
author | Razvan Chitu <razvan.ch95@gmail.com> | 2016-08-21 12:22:29 +0300 |
---|---|---|
committer | Razvan Chitu <razvan.ch95@gmail.com> | 2016-08-22 12:29:03 +0300 |
commit | e5f68699d8c100b1ad299b69c9245644e3965d9e (patch) | |
tree | 8bd7095eb46bfec7f188593c554dd1ec35669dd3 | |
parent | f8a58f3cf26d963e25505047f241dbb14c3565df (diff) | |
download | nautilus-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.c | 319 | ||||
-rw-r--r-- | src/nautilus-file-operations.h | 9 | ||||
-rw-r--r-- | src/nautilus-file-undo-operations.c | 142 | ||||
-rw-r--r-- | src/nautilus-file-undo-operations.h | 29 |
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__ */ |