summaryrefslogtreecommitdiff
path: root/src/nautilus-operations-ui-manager.c
diff options
context:
space:
mode:
authorRazvan Chitu <razvan.ch95@gmail.com>2016-08-21 19:06:51 +0300
committerRazvan Chitu <razvan.ch95@gmail.com>2016-08-23 00:35:33 +0300
commitfd01842f996b7d07baf2aafb0f68308fc5ed2055 (patch)
tree434278662cd52a707ce4a0f4a3593cc930ec7369 /src/nautilus-operations-ui-manager.c
parentc389f97792143ec354369cc38729bfcb0fa106fb (diff)
downloadnautilus-fd01842f996b7d07baf2aafb0f68308fc5ed2055.tar.gz
file-conflict-dialog: separate file logic from UI management
In Nautilus, file conflicts are handled by a specific dialog. Previously, the dialog class managed both the UI and the related nautilus files. This lead to it being inflexible due to operation specific logic being mixed with the rest of the functionality. In order to change this, move file logic to a separate module and add methods for controlling the UI elements in the dialog. Create an operation-manager module to handle dialog controlling. Move anything related to Nautilus files from the dialog class to the new module. https://bugzilla.gnome.org/show_bug.cgi?id=770160
Diffstat (limited to 'src/nautilus-operations-ui-manager.c')
-rw-r--r--src/nautilus-operations-ui-manager.c449
1 files changed, 449 insertions, 0 deletions
diff --git a/src/nautilus-operations-ui-manager.c b/src/nautilus-operations-ui-manager.c
new file mode 100644
index 000000000..022c8ccc0
--- /dev/null
+++ b/src/nautilus-operations-ui-manager.c
@@ -0,0 +1,449 @@
+#include <glib/gi18n.h>
+
+#include "nautilus-operations-ui-manager.h"
+
+#include "nautilus-file.h"
+#include "nautilus-file-operations.h"
+#include "nautilus-file-conflict-dialog.h"
+
+typedef struct {
+ GSourceFunc source_func;
+ gpointer user_data;
+ GMutex mutex;
+ GCond cond;
+ gboolean completed;
+} ContextInvokeData;
+
+G_LOCK_DEFINE_STATIC (main_context_sync);
+
+static gboolean
+invoke_main_context_source_func_wrapper (gpointer user_data)
+{
+ ContextInvokeData *data = user_data;
+
+ g_mutex_lock (&data->mutex);
+
+ while (data->source_func (data->user_data));
+
+ data->completed = TRUE;
+
+ g_cond_signal (&data->cond);
+ g_mutex_unlock (&data->mutex);
+
+ return G_SOURCE_REMOVE;
+}
+
+/* This function is used to run UI on the main thread in order to ask the user
+ * for an action during an operation. Since the operation cannot progress until
+ * an action is provided by the user, the current thread needs to be blocked.
+ * For this we wait on a condition on the shared data. We proceed further
+ * unblocking the thread when the condition is set in the UI thread.
+ */
+static void
+invoke_main_context_sync (GMainContext *main_context,
+ GSourceFunc source_func,
+ gpointer user_data)
+{
+ ContextInvokeData data;
+ /* Allow only one thread at a time to invoke the main context so we
+ * don't get race conditions which could lead to multiple dialogs being
+ * displayed at the same time
+ */
+ G_LOCK (main_context_sync);
+
+ data.source_func = source_func;
+ data.user_data = user_data;
+
+ g_mutex_init (&data.mutex);
+ g_cond_init (&data.cond);
+ data.completed = FALSE;
+
+ g_mutex_lock (&data.mutex);
+
+ g_main_context_invoke (main_context,
+ invoke_main_context_source_func_wrapper,
+ &data);
+
+ while (!data.completed) {
+ g_cond_wait (&data.cond, &data.mutex);
+ }
+
+ g_mutex_unlock (&data.mutex);
+
+ G_UNLOCK (main_context_sync);
+
+ g_mutex_clear (&data.mutex);
+ g_cond_clear (&data.cond);
+}
+
+typedef struct {
+ GFile *source_name;
+ GFile *destination_name;
+ GFile *destination_directory_name;
+
+ GtkWindow *parent;
+
+ FileConflictResponse *response;
+
+ NautilusFile *source;
+ NautilusFile *destination;
+ NautilusFile *destination_directory;
+
+ NautilusFileConflictDialog *dialog;
+
+ NautilusFileListCallback on_file_list_ready;
+ NautilusFileListHandle *handle;
+ gulong source_handler_id;
+ gulong destination_handler_id;
+} FileConflictDialogData;
+
+void
+file_conflict_response_free (FileConflictResponse *response)
+{
+ g_free (response->new_name);
+ g_slice_free (FileConflictResponse, response);
+}
+
+static void
+set_copy_move_dialog_text (FileConflictDialogData *data)
+{
+ g_autofree gchar *primary_text = NULL;
+ g_autofree gchar *secondary_text = NULL;
+ const gchar *message_extra;
+ time_t source_mtime;
+ time_t destination_mtime;
+ g_autofree gchar *message = NULL;
+ g_autofree gchar *destination_name;
+ g_autofree gchar *destination_directory_name;
+ gboolean source_is_directory;
+ gboolean destination_is_directory;
+
+ source_mtime = nautilus_file_get_mtime (data->source);
+ destination_mtime = nautilus_file_get_mtime (data->destination);
+
+ destination_name = nautilus_file_get_display_name (data->destination);
+ destination_directory_name = nautilus_file_get_display_name (data->destination_directory);
+
+ source_is_directory = nautilus_file_is_directory (data->source);
+ destination_is_directory = nautilus_file_is_directory (data->destination);
+
+ if (destination_is_directory) {
+ if (source_is_directory) {
+ primary_text = g_strdup_printf (_("Merge folder “%s”?"),
+ destination_name);
+
+ message_extra = _("Merging will ask for confirmation before replacing any files in "
+ "the folder that conflict with the files being copied.");
+
+ if (source_mtime > destination_mtime) {
+ message = g_strdup_printf (_("An older folder with the same name already exists in “%s”."),
+ destination_directory_name);
+ } else if (source_mtime < destination_mtime) {
+ message = g_strdup_printf (_("A newer folder with the same name already exists in “%s”."),
+ destination_directory_name);
+ } else {
+ message = g_strdup_printf (_("Another folder with the same name already exists in “%s”."),
+ destination_directory_name);
+ }
+ } else {
+ primary_text = g_strdup_printf (_("Replace folder “%s”?"),
+ destination_name);
+ message_extra = _("Replacing it will remove all files in the folder.");
+ message = g_strdup_printf (_("A folder with the same name already exists in “%s”."),
+ destination_directory_name);
+ }
+ } else {
+ primary_text = g_strdup_printf (_("Replace file “%s”?"),
+ destination_name);
+
+ message_extra = _("Replacing it will overwrite its content.");
+
+ if (source_mtime > destination_mtime) {
+ message = g_strdup_printf (_("An older file with the same name already exists in “%s”."),
+ destination_directory_name);
+ } else if (source_mtime < destination_mtime) {
+ message = g_strdup_printf (_("A newer file with the same name already exists in “%s”."),
+ destination_directory_name);
+ } else {
+ message = g_strdup_printf (_("Another file with the same name already exists in “%s”."),
+ destination_directory_name);
+ }
+ }
+
+ 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;
+ GdkPixbuf *destination_pixbuf;
+
+ destination_pixbuf = nautilus_file_get_icon_pixbuf (data->destination,
+ NAUTILUS_CANVAS_ICON_SIZE_SMALL,
+ TRUE,
+ 1,
+ NAUTILUS_FILE_ICON_FLAGS_USE_THUMBNAILS);
+
+ source_pixbuf = nautilus_file_get_icon_pixbuf (data->source,
+ NAUTILUS_CANVAS_ICON_SIZE_SMALL,
+ TRUE,
+ 1,
+ NAUTILUS_FILE_ICON_FLAGS_USE_THUMBNAILS);
+
+ nautilus_file_conflict_dialog_set_images (data->dialog,
+ destination_pixbuf,
+ source_pixbuf);
+
+ g_object_unref (destination_pixbuf);
+ g_object_unref (source_pixbuf);
+}
+
+static void
+set_file_labels (FileConflictDialogData *data)
+{
+ GString *destination_label;
+ GString *source_label;
+ gboolean source_is_directory;
+ gboolean destination_is_directory;
+ gboolean should_show_type;
+ g_autofree char *destination_mime_type = NULL;
+ g_autofree char *destination_date = NULL;
+ g_autofree char *destination_size = NULL;
+ g_autofree char *destination_type = NULL;
+ g_autofree char *source_date = NULL;
+ g_autofree char *source_size = NULL;
+ g_autofree char *source_type = NULL;
+
+ source_is_directory = nautilus_file_is_directory (data->source);
+ destination_is_directory = nautilus_file_is_directory (data->destination);
+
+ destination_mime_type = nautilus_file_get_mime_type (data->destination);
+ should_show_type = !nautilus_file_is_mime_type (data->source,
+ destination_mime_type);
+
+ destination_date = nautilus_file_get_string_attribute (data->destination,
+ "date_modified");
+ destination_size = nautilus_file_get_string_attribute (data->destination,
+ "size");
+
+ if (should_show_type) {
+ destination_type = nautilus_file_get_string_attribute (data->destination,
+ "type");
+ }
+
+ destination_label = g_string_new (NULL);
+ if (destination_is_directory) {
+ g_string_append_printf (destination_label, "<b>%s</b>\n", _("Original folder"));
+ g_string_append_printf (destination_label, "%s %s\n", _("Items:"), destination_size);
+ }
+ else {
+ g_string_append_printf (destination_label, "<b>%s</b>\n", _("Original file"));
+ g_string_append_printf (destination_label, "%s %s\n", _("Size:"), destination_size);
+ }
+
+ if (should_show_type) {
+ g_string_append_printf (destination_label, "%s %s\n", _("Type:"), destination_type);
+ }
+
+ g_string_append_printf (destination_label, "%s %s", _("Last modified:"), destination_date);
+
+ source_date = nautilus_file_get_string_attribute (data->source,
+ "date_modified");
+ source_size = nautilus_file_get_string_attribute (data->source,
+ "size");
+
+ if (should_show_type) {
+ source_type = nautilus_file_get_string_attribute (data->source,
+ "type");
+ }
+
+ source_label = g_string_new (NULL);
+ if (source_is_directory) {
+ g_string_append_printf (source_label, "<b>%s</b>\n",
+ destination_is_directory ?
+ _("Merge with") : _("Replace with"));
+ g_string_append_printf (source_label, "%s %s\n", _("Items:"), source_size);
+ }
+ else {
+ g_string_append_printf (source_label, "<b>%s</b>\n", _("Replace with"));
+ g_string_append_printf (source_label, "%s %s\n", _("Size:"), source_size);
+ }
+
+ if (should_show_type) {
+ g_string_append_printf (source_label, "%s %s\n", _("Type:"), source_type);
+ }
+
+ g_string_append_printf (source_label, "%s %s", _("Last modified:"), source_date);
+
+ nautilus_file_conflict_dialog_set_file_labels (data->dialog,
+ destination_label->str,
+ source_label->str);
+
+ g_string_free (destination_label, TRUE);
+ g_string_free (source_label, TRUE);
+}
+
+static void
+set_conflict_name (FileConflictDialogData *data)
+{
+ g_autofree gchar *edit_name;
+
+ edit_name = nautilus_file_get_edit_name (data->destination);
+
+ nautilus_file_conflict_dialog_set_conflict_name (data->dialog,
+ edit_name);
+}
+
+static void
+set_replace_button_label (FileConflictDialogData *data)
+{
+ gboolean source_is_directory, destination_is_directory;
+
+ source_is_directory = nautilus_file_is_directory (data->source);
+ destination_is_directory = nautilus_file_is_directory (data->destination);
+
+ if (source_is_directory && destination_is_directory) {
+ nautilus_file_conflict_dialog_set_replace_button_label (data->dialog,
+ _("Merge"));
+ }
+}
+
+static void
+file_icons_changed (NautilusFile *file,
+ FileConflictDialogData *data)
+{
+ set_images (data);
+}
+
+static void
+copy_move_conflict_on_file_list_ready (GList *files,
+ gpointer user_data)
+{
+ FileConflictDialogData *data = user_data;
+ g_autofree gchar *title;
+
+ data->handle = NULL;
+
+ if (nautilus_file_is_directory (data->source)) {
+ title = g_strdup (nautilus_file_is_directory (data->destination) ?
+ _("Merge Folder") :
+ _("File and Folder conflict"));
+ } else {
+ title = g_strdup (nautilus_file_is_directory (data->destination) ?
+ _("File and Folder conflict") :
+ _("File conflict"));
+ }
+
+ gtk_window_set_title (GTK_WINDOW (data->dialog), title);
+
+ set_copy_move_dialog_text (data);
+
+ set_images (data);
+
+ set_file_labels (data);
+
+ set_conflict_name (data);
+
+ set_replace_button_label (data);
+
+ 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->source_handler_id = g_signal_connect (data->source, "changed",
+ G_CALLBACK (file_icons_changed), data);
+ data->destination_handler_id = g_signal_connect (data->destination, "changed",
+ G_CALLBACK (file_icons_changed), data);
+}
+
+static gboolean
+run_file_conflict_dialog (gpointer user_data)
+{
+ FileConflictDialogData *data = user_data;
+ int response_id;
+ GList *files = NULL;
+
+ data->source = nautilus_file_get (data->source_name);
+ data->destination = nautilus_file_get (data->destination_name);
+ data->destination_directory = nautilus_file_get (data->destination_directory_name);
+
+ data->dialog = nautilus_file_conflict_dialog_new (data->parent);
+
+ files = g_list_prepend (files, data->source);
+ files = g_list_prepend (files, data->destination);
+ files = g_list_prepend (files, data->destination_directory);
+
+ nautilus_file_list_call_when_ready (files,
+ NAUTILUS_FILE_ATTRIBUTES_FOR_ICON,
+ &data->handle,
+ data->on_file_list_ready,
+ data);
+
+ response_id = gtk_dialog_run (GTK_DIALOG (data->dialog));
+
+ if (data->handle != NULL) {
+ nautilus_file_list_cancel_call_when_ready (data->handle);
+ }
+
+ if (data->source_handler_id) {
+ g_signal_handler_disconnect (data->source, data->source_handler_id);
+ nautilus_file_monitor_remove (data->source, data);
+ }
+
+ if (data->destination_handler_id) {
+ g_signal_handler_disconnect (data->destination, data->destination_handler_id);
+ nautilus_file_monitor_remove (data->destination, data);
+ }
+
+ if (response_id == CONFLICT_RESPONSE_RENAME) {
+ data->response->new_name =
+ nautilus_file_conflict_dialog_get_new_name (data->dialog);
+ } else if (response_id != GTK_RESPONSE_CANCEL ||
+ response_id != GTK_RESPONSE_NONE) {
+ data->response->apply_to_all =
+ nautilus_file_conflict_dialog_get_apply_to_all (data->dialog);
+ }
+
+ data->response->id = response_id;
+
+ gtk_widget_destroy (GTK_WIDGET (data->dialog));
+
+ nautilus_file_unref (data->source);
+ nautilus_file_unref (data->destination);
+ nautilus_file_unref (data->destination_directory);
+ g_list_free (files);
+
+ return G_SOURCE_REMOVE;
+}
+
+FileConflictResponse *
+copy_move_conflict_ask_user_action (GtkWindow *parent_window,
+ GFile *source_name,
+ GFile *destination_name,
+ GFile *destination_directory_name)
+{
+ FileConflictDialogData *data;
+
+ data = g_slice_new0 (FileConflictDialogData);
+ data->parent = parent_window;
+ data->source_name = source_name;
+ data->destination_name = destination_name;
+ data->destination_directory_name = destination_directory_name;
+
+ data->response = g_slice_new0 (FileConflictResponse);
+ data->response->new_name = NULL;
+
+ data->on_file_list_ready = copy_move_conflict_on_file_list_ready;
+
+ invoke_main_context_sync (NULL,
+ run_file_conflict_dialog,
+ data);
+
+ g_slice_free (FileConflictDialogData, data);
+
+ return data->response;
+}