diff options
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/nautilus-file-conflict-dialog.c | 473 | ||||
-rw-r--r-- | src/nautilus-file-conflict-dialog.h | 30 | ||||
-rw-r--r-- | src/nautilus-file-operations.c | 147 | ||||
-rw-r--r-- | src/nautilus-operations-ui-manager.c | 449 | ||||
-rw-r--r-- | src/nautilus-operations-ui-manager.h | 27 |
6 files changed, 648 insertions, 480 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 652cab7ce..6532c0101 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -278,6 +278,8 @@ nautilus_no_main_sources = \ nautilus-rename-file-popover-controller.h \ nautilus-new-folder-dialog-controller.c \ nautilus-new-folder-dialog-controller.h \ + nautilus-operations-ui-manager.c \ + nautilus-operations-ui-manager.h \ nautilus-file-operations.c \ nautilus-file-operations.h \ nautilus-file-private.h \ diff --git a/src/nautilus-file-conflict-dialog.c b/src/nautilus-file-conflict-dialog.c index bfce4c948..0ba63a117 100644 --- a/src/nautilus-file-conflict-dialog.c +++ b/src/nautilus-file-conflict-dialog.c @@ -32,18 +32,11 @@ #include "nautilus-file.h" #include "nautilus-icon-info.h" +#include "nautilus-operations-ui-manager.h" struct _NautilusFileConflictDialogDetails { - /* conflicting objects */ - NautilusFile *source; - NautilusFile *destination; - NautilusFile *dest_dir; - gchar *conflict_name; - NautilusFileListHandle *handle; - gulong src_handler_id; - gulong dest_handler_id; /* UI objects */ GtkWidget *titles_vbox; @@ -52,6 +45,7 @@ struct _NautilusFileConflictDialogDetails GtkWidget *expander; GtkWidget *entry; GtkWidget *checkbox; + GtkWidget *skip_button; GtkWidget *rename_button; GtkWidget *replace_button; GtkWidget *dest_image; @@ -66,307 +60,128 @@ G_DEFINE_TYPE (NautilusFileConflictDialog, (G_TYPE_INSTANCE_GET_PRIVATE ((object), NAUTILUS_TYPE_FILE_CONFLICT_DIALOG, \ NautilusFileConflictDialogDetails)) -static void -file_icons_changed (NautilusFile *file, - NautilusFileConflictDialog *fcd) -{ - GdkPixbuf *pixbuf; - - pixbuf = nautilus_file_get_icon_pixbuf (fcd->details->destination, - NAUTILUS_CANVAS_ICON_SIZE_SMALL, - TRUE, - gtk_widget_get_scale_factor (fcd->details->dest_image), - NAUTILUS_FILE_ICON_FLAGS_USE_THUMBNAILS); - gtk_image_set_from_pixbuf (GTK_IMAGE (fcd->details->dest_image), pixbuf); - g_object_unref (pixbuf); - - pixbuf = nautilus_file_get_icon_pixbuf (fcd->details->source, - NAUTILUS_CANVAS_ICON_SIZE_SMALL, - TRUE, - gtk_widget_get_scale_factor (fcd->details->src_image), - NAUTILUS_FILE_ICON_FLAGS_USE_THUMBNAILS); - - gtk_image_set_from_pixbuf (GTK_IMAGE (fcd->details->src_image), pixbuf); - g_object_unref (pixbuf); +void +nautilus_file_conflict_dialog_set_text (NautilusFileConflictDialog *fcd, + gchar *primary_text, + gchar *secondary_text) +{ + NautilusFileConflictDialogDetails *details = fcd->details; + GtkWidget *label; + PangoAttrList *attr_list; + + label = gtk_label_new (primary_text); + gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); + gtk_label_set_line_wrap_mode (GTK_LABEL (label), PANGO_WRAP_WORD_CHAR); + gtk_label_set_xalign (GTK_LABEL (label), 0.0); + gtk_box_pack_start (GTK_BOX (details->titles_vbox), + label, FALSE, FALSE, 0); + gtk_widget_show (label); + + attr_list = pango_attr_list_new (); + pango_attr_list_insert (attr_list, pango_attr_weight_new (PANGO_WEIGHT_BOLD)); + pango_attr_list_insert (attr_list, pango_attr_scale_new (PANGO_SCALE_LARGE)); + g_object_set (label, + "attributes", attr_list, + NULL); + + pango_attr_list_unref (attr_list); + + label = gtk_label_new (secondary_text); + gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); + gtk_label_set_xalign (GTK_LABEL (label), 0.0); + gtk_box_pack_start (GTK_BOX (details->titles_vbox), + label, FALSE, FALSE, 0); + gtk_widget_show (label); } -static void -file_list_ready_cb (GList *files, - gpointer user_data) +void +nautilus_file_conflict_dialog_set_images (NautilusFileConflictDialog *fcd, + GdkPixbuf *destination_pixbuf, + GdkPixbuf *source_pixbuf) { - NautilusFileConflictDialog *fcd = user_data; - NautilusFile *src, *dest, *dest_dir; - time_t src_mtime, dest_mtime; - gboolean source_is_dir, dest_is_dir, should_show_type; - NautilusFileConflictDialogDetails *details; - char *primary_text, *message, *secondary_text; - const gchar *message_extra; - char *dest_name, *dest_dir_name, *edit_name; - char *label_text; - char *size, *date, *type = NULL; - GdkPixbuf *pixbuf; - GtkWidget *label; - GString *str; - PangoAttrList *attr_list; - - details = fcd->details; - - details->handle = NULL; - - dest_dir = g_list_nth_data (files, 0); - dest = g_list_nth_data (files, 1); - src = g_list_nth_data (files, 2); - - src_mtime = nautilus_file_get_mtime (src); - dest_mtime = nautilus_file_get_mtime (dest); - - dest_name = nautilus_file_get_display_name (dest); - dest_dir_name = nautilus_file_get_display_name (dest_dir); - - source_is_dir = nautilus_file_is_directory (src); - dest_is_dir = nautilus_file_is_directory (dest); - - type = nautilus_file_get_mime_type (dest); - should_show_type = !nautilus_file_is_mime_type (src, type); - - g_free (type); - type = NULL; - - /* Set up the right labels */ - if (dest_is_dir) { - if (source_is_dir) { - primary_text = g_strdup_printf - (_("Merge folder “%s”?"), - dest_name); - - message_extra = - _("Merging will ask for confirmation before replacing any files in " - "the folder that conflict with the files being copied."); - - if (src_mtime > dest_mtime) { - message = g_strdup_printf ( - _("An older folder with the same name already exists in “%s”."), - dest_dir_name); - } else if (src_mtime < dest_mtime) { - message = g_strdup_printf ( - _("A newer folder with the same name already exists in “%s”."), - dest_dir_name); - } else { - message = g_strdup_printf ( - _("Another folder with the same name already exists in “%s”."), - dest_dir_name); - } - } else { - message_extra = - _("Replacing it will remove all files in the folder."); - primary_text = g_strdup_printf - (_("Replace folder “%s”?"), dest_name); - message = g_strdup_printf - (_("A folder with the same name already exists in “%s”."), - dest_dir_name); - } - } else { - primary_text = g_strdup_printf - (_("Replace file “%s”?"), dest_name); - - message_extra = _("Replacing it will overwrite its content."); - - if (src_mtime > dest_mtime) { - message = g_strdup_printf ( - _("An older file with the same name already exists in “%s”."), - dest_dir_name); - } else if (src_mtime < dest_mtime) { - message = g_strdup_printf ( - _("A newer file with the same name already exists in “%s”."), - dest_dir_name); - } else { - message = g_strdup_printf ( - _("Another file with the same name already exists in “%s”."), - dest_dir_name); - } - } - - secondary_text = g_strdup_printf ("%s\n%s", message, message_extra); - g_free (message); - - label = gtk_label_new (primary_text); - gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); - gtk_label_set_line_wrap_mode (GTK_LABEL (label), PANGO_WRAP_WORD_CHAR); - gtk_label_set_xalign (GTK_LABEL (label), 0.0); - gtk_box_pack_start (GTK_BOX (details->titles_vbox), - label, FALSE, FALSE, 0); - gtk_widget_show (label); - - attr_list = pango_attr_list_new (); - pango_attr_list_insert (attr_list, pango_attr_weight_new (PANGO_WEIGHT_BOLD)); - pango_attr_list_insert (attr_list, pango_attr_scale_new (PANGO_SCALE_LARGE)); - g_object_set (label, - "attributes", attr_list, - NULL); - - pango_attr_list_unref (attr_list); - - label = gtk_label_new (secondary_text); - gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); - gtk_label_set_xalign (GTK_LABEL (label), 0.0); - gtk_box_pack_start (GTK_BOX (details->titles_vbox), - label, FALSE, FALSE, 0); - gtk_widget_show (label); - g_free (primary_text); - g_free (secondary_text); - - /* Set up file icons */ - pixbuf = nautilus_file_get_icon_pixbuf (dest, - NAUTILUS_CANVAS_ICON_SIZE_SMALL, - TRUE, - gtk_widget_get_scale_factor (fcd->details->titles_vbox), - NAUTILUS_FILE_ICON_FLAGS_USE_THUMBNAILS); - details->dest_image = gtk_image_new_from_pixbuf (pixbuf); - gtk_box_pack_start (GTK_BOX (details->first_hbox), - details->dest_image, FALSE, FALSE, 0); - gtk_widget_show (details->dest_image); - g_object_unref (pixbuf); - - pixbuf = nautilus_file_get_icon_pixbuf (src, - NAUTILUS_CANVAS_ICON_SIZE_SMALL, - TRUE, - gtk_widget_get_scale_factor (fcd->details->titles_vbox), - NAUTILUS_FILE_ICON_FLAGS_USE_THUMBNAILS); - details->src_image = gtk_image_new_from_pixbuf (pixbuf); - gtk_box_pack_start (GTK_BOX (details->second_hbox), - details->src_image, FALSE, FALSE, 0); - gtk_widget_show (details->src_image); - g_object_unref (pixbuf); - - /* Set up labels */ - label = gtk_label_new (NULL); - date = nautilus_file_get_string_attribute (dest, - "date_modified"); - size = nautilus_file_get_string_attribute (dest, "size"); - - if (should_show_type) { - type = nautilus_file_get_string_attribute (dest, "type"); - } - - str = g_string_new (NULL); - if (dest_is_dir) { - g_string_append_printf (str, "<b>%s</b>\n", _("Original folder")); - g_string_append_printf (str, "%s %s\n", _("Items:"), size); - } - else { - g_string_append_printf (str, "<b>%s</b>\n", _("Original file")); - g_string_append_printf (str, "%s %s\n", _("Size:"), size); - } - - if (should_show_type) { - g_string_append_printf (str, "%s %s\n", _("Type:"), type); - } - - g_string_append_printf (str, "%s %s", _("Last modified:"), date); - - label_text = str->str; - gtk_label_set_markup (GTK_LABEL (label), - label_text); - gtk_box_pack_start (GTK_BOX (details->first_hbox), - label, FALSE, FALSE, 0); - gtk_widget_show (label); - - g_free (size); - g_free (type); - g_free (date); - g_string_erase (str, 0, -1); - - /* Second label */ - label = gtk_label_new (NULL); - date = nautilus_file_get_string_attribute (src, - "date_modified"); - size = nautilus_file_get_string_attribute (src, "size"); - - if (should_show_type) { - type = nautilus_file_get_string_attribute (src, "type"); - } - - if (source_is_dir) { - g_string_append_printf (str, "<b>%s</b>\n", dest_is_dir ? _("Merge with") : _("Replace with")); - g_string_append_printf (str, "%s %s\n", _("Items:"), size); - } - else { - g_string_append_printf (str, "<b>%s</b>\n", _("Replace with")); - g_string_append_printf (str, "%s %s\n", _("Size:"), size); - } - - if (should_show_type) { - g_string_append_printf (str, "%s %s\n", _("Type:"), type); - } - - g_string_append_printf (str, "%s %s", _("Last modified:"), date); - label_text = g_string_free (str, FALSE); - - gtk_label_set_markup (GTK_LABEL (label), - label_text); - gtk_box_pack_start (GTK_BOX (details->second_hbox), - label, FALSE, FALSE, 0); - gtk_widget_show (label); + NautilusFileConflictDialogDetails *details = fcd->details; + + if (details->dest_image == NULL) { + details->dest_image = gtk_image_new_from_pixbuf (destination_pixbuf); + gtk_box_pack_start (GTK_BOX (details->first_hbox), + details->dest_image, FALSE, FALSE, 0); + gtk_widget_show (details->dest_image); + } else { + gtk_image_set_from_pixbuf (GTK_IMAGE (details->dest_image), + destination_pixbuf); + } + + if (details->src_image == NULL) { + details->src_image = gtk_image_new_from_pixbuf (source_pixbuf); + gtk_box_pack_start (GTK_BOX (details->second_hbox), + details->src_image, FALSE, FALSE, 0); + gtk_widget_show (details->src_image); + } else { + gtk_image_set_from_pixbuf (GTK_IMAGE (details->src_image), + source_pixbuf); + } +} - g_free (size); - g_free (date); - g_free (type); - g_free (label_text); +void +nautilus_file_conflict_dialog_set_file_labels (NautilusFileConflictDialog *fcd, + gchar *destination_label, + gchar *source_label) +{ + NautilusFileConflictDialogDetails *details = fcd->details; + GtkWidget *label; + + label = gtk_label_new (NULL); + gtk_label_set_markup (GTK_LABEL (label), + destination_label); + gtk_box_pack_start (GTK_BOX (details->first_hbox), + label, FALSE, FALSE, 0); + gtk_widget_show (label); + + label = gtk_label_new (NULL); + gtk_label_set_markup (GTK_LABEL (label), + source_label); + gtk_box_pack_start (GTK_BOX (details->second_hbox), + label, FALSE, FALSE, 0); + gtk_widget_show (label); +} - /* Populate the entry */ - edit_name = nautilus_file_get_edit_name (dest); - details->conflict_name = edit_name; +void +nautilus_file_conflict_dialog_set_conflict_name (NautilusFileConflictDialog *fcd, + gchar *conflict_name) +{ + NautilusFileConflictDialogDetails *details = fcd->details; - gtk_entry_set_text (GTK_ENTRY (details->entry), edit_name); + details->conflict_name = g_strdup (conflict_name); - if (source_is_dir && dest_is_dir) { - gtk_button_set_label (GTK_BUTTON (details->replace_button), - _("Merge")); - } + gtk_entry_set_text (GTK_ENTRY (details->entry), details->conflict_name); - nautilus_file_monitor_add (src, fcd, NAUTILUS_FILE_ATTRIBUTES_FOR_ICON); - nautilus_file_monitor_add (dest, fcd, NAUTILUS_FILE_ATTRIBUTES_FOR_ICON); - - details->src_handler_id = g_signal_connect (src, "changed", - G_CALLBACK (file_icons_changed), fcd); - details->dest_handler_id = g_signal_connect (dest, "changed", - G_CALLBACK (file_icons_changed), fcd); } -static void -build_dialog_appearance (NautilusFileConflictDialog *fcd) +void +nautilus_file_conflict_dialog_set_replace_button_label (NautilusFileConflictDialog *fcd, + gchar *label) { - GList *files = NULL; - NautilusFileConflictDialogDetails *details = fcd->details; + NautilusFileConflictDialogDetails *details = fcd->details; - files = g_list_prepend (files, details->source); - files = g_list_prepend (files, details->destination); - files = g_list_prepend (files, details->dest_dir); - - nautilus_file_list_call_when_ready (files, - NAUTILUS_FILE_ATTRIBUTES_FOR_ICON, - &details->handle, file_list_ready_cb, fcd); - g_list_free (files); + gtk_button_set_label (GTK_BUTTON (details->replace_button), + label); } -static void -set_source_and_destination (GtkWidget *w, - GFile *source, - GFile *destination, - GFile *dest_dir) +void +nautilus_file_conflict_dialog_disable_skip (NautilusFileConflictDialog *fcd) { - NautilusFileConflictDialog *dialog; - NautilusFileConflictDialogDetails *details; + NautilusFileConflictDialogDetails *details = fcd->details; - dialog = NAUTILUS_FILE_CONFLICT_DIALOG (w); - details = dialog->details; + gtk_widget_hide (details->skip_button); +} - details->source = nautilus_file_get (source); - details->destination = nautilus_file_get (destination); - details->dest_dir = nautilus_file_get (dest_dir); +void +nautilus_file_conflict_dialog_disable_apply_to_all (NautilusFileConflictDialog *fcd) +{ + NautilusFileConflictDialogDetails *details = fcd->details; - build_dialog_appearance (dialog); + gtk_widget_hide (details->checkbox); } static void @@ -549,12 +364,15 @@ nautilus_file_conflict_dialog_init (NautilusFileConflictDialog *fcd) G_CALLBACK (checkbox_toggled_cb), dialog); /* Add buttons */ - gtk_dialog_add_buttons (dialog, - _("_Cancel"), - GTK_RESPONSE_CANCEL, - _("_Skip"), - CONFLICT_RESPONSE_SKIP, - NULL); + gtk_dialog_add_button (dialog, + _("_Cancel"), + GTK_RESPONSE_CANCEL); + + details->skip_button = + gtk_dialog_add_button (dialog, + _("_Skip"), + CONFLICT_RESPONSE_SKIP); + details->rename_button = gtk_dialog_add_button (dialog, _("Re_name"), @@ -583,24 +401,6 @@ do_finalize (GObject *self) g_free (details->conflict_name); - if (details->handle != NULL) { - nautilus_file_list_cancel_call_when_ready (details->handle); - } - - if (details->src_handler_id) { - g_signal_handler_disconnect (details->source, details->src_handler_id); - nautilus_file_monitor_remove (details->source, self); - } - - if (details->dest_handler_id) { - g_signal_handler_disconnect (details->destination, details->dest_handler_id); - nautilus_file_monitor_remove (details->destination, self); - } - - nautilus_file_unref (details->source); - nautilus_file_unref (details->destination); - nautilus_file_unref (details->dest_dir); - G_OBJECT_CLASS (nautilus_file_conflict_dialog_parent_class)->finalize (self); } @@ -626,45 +426,18 @@ nautilus_file_conflict_dialog_get_apply_to_all (NautilusFileConflictDialog *dial (GTK_TOGGLE_BUTTON (dialog->details->checkbox)); } -GtkWidget * -nautilus_file_conflict_dialog_new (GtkWindow *parent, - GFile *source, - GFile *destination, - GFile *dest_dir) +NautilusFileConflictDialog * +nautilus_file_conflict_dialog_new (GtkWindow *parent) { - GtkWidget *dialog; - NautilusFile *src, *dest; - gboolean source_is_dir, dest_is_dir; - - src = nautilus_file_get (source); - dest = nautilus_file_get (destination); - - source_is_dir = nautilus_file_is_directory (src); - dest_is_dir = nautilus_file_is_directory (dest); - - if (source_is_dir) { - dialog = GTK_WIDGET (g_object_new (NAUTILUS_TYPE_FILE_CONFLICT_DIALOG, - "use-header-bar", TRUE, - "modal", TRUE, - "title", dest_is_dir ? _("Merge Folder") : _("File and Folder conflict"), - NULL)); - } else { - dialog = GTK_WIDGET (g_object_new (NAUTILUS_TYPE_FILE_CONFLICT_DIALOG, - "use-header-bar", TRUE, - "modal", TRUE, - "title", dest_is_dir ? _("File and Folder conflict") : _("File conflict"), - NULL)); - } - - set_source_and_destination (dialog, - source, - destination, - dest_dir); + NautilusFileConflictDialog *dialog; + + dialog = NAUTILUS_FILE_CONFLICT_DIALOG (g_object_new (NAUTILUS_TYPE_FILE_CONFLICT_DIALOG, + "use-header-bar", TRUE, + "modal", TRUE, + NULL)); + gtk_window_set_transient_for (GTK_WINDOW (dialog), parent); - g_object_unref (src); - g_object_unref (dest); - return dialog; } diff --git a/src/nautilus-file-conflict-dialog.h b/src/nautilus-file-conflict-dialog.h index b7767ce6d..eee92d38f 100644 --- a/src/nautilus-file-conflict-dialog.h +++ b/src/nautilus-file-conflict-dialog.h @@ -56,19 +56,27 @@ struct _NautilusFileConflictDialogClass { GtkDialogClass parent_class; }; -enum -{ - CONFLICT_RESPONSE_SKIP = 1, - CONFLICT_RESPONSE_REPLACE = 2, - CONFLICT_RESPONSE_RENAME = 3, -}; - GType nautilus_file_conflict_dialog_get_type (void) G_GNUC_CONST; -GtkWidget* nautilus_file_conflict_dialog_new (GtkWindow *parent, - GFile *source, - GFile *destination, - GFile *dest_dir); +NautilusFileConflictDialog* nautilus_file_conflict_dialog_new (GtkWindow *parent); + +void nautilus_file_conflict_dialog_set_text (NautilusFileConflictDialog *fcd, + gchar *primary_text, + gchar *secondary_text); +void nautilus_file_conflict_dialog_set_images (NautilusFileConflictDialog *fcd, + GdkPixbuf *source_pixbuf, + GdkPixbuf *destination_pixbuf); +void nautilus_file_conflict_dialog_set_file_labels (NautilusFileConflictDialog *fcd, + gchar *destination_label, + gchar *source_label); +void nautilus_file_conflict_dialog_set_conflict_name (NautilusFileConflictDialog *fcd, + gchar *conflict_name); +void nautilus_file_conflict_dialog_set_replace_button_label (NautilusFileConflictDialog *fcd, + gchar *label); + +void nautilus_file_conflict_dialog_disable_skip (NautilusFileConflictDialog *fcd); +void nautilus_file_conflict_dialog_disable_apply_to_all (NautilusFileConflictDialog *fcd); + char* nautilus_file_conflict_dialog_get_new_name (NautilusFileConflictDialog *dialog); gboolean nautilus_file_conflict_dialog_get_apply_to_all (NautilusFileConflictDialog *dialog); diff --git a/src/nautilus-file-operations.c b/src/nautilus-file-operations.c index 804a1c773..518420540 100644 --- a/src/nautilus-file-operations.c +++ b/src/nautilus-file-operations.c @@ -51,13 +51,13 @@ #include <gtk/gtk.h> #include <gio/gio.h> #include <glib.h> +#include "nautilus-operations-ui-manager.h" #include "nautilus-file-changes-queue.h" #include "nautilus-file-private.h" #include "nautilus-global-preferences.h" #include "nautilus-link.h" #include "nautilus-trash-monitor.h" #include "nautilus-file-utilities.h" -#include "nautilus-file-conflict-dialog.h" #include "nautilus-file-undo-operations.h" #include "nautilus-file-undo-manager.h" @@ -4292,117 +4292,26 @@ is_trusted_desktop_file (GFile *file, return res; } -typedef struct { - int id; - char *new_name; - gboolean apply_to_all; -} ConflictResponseData; - -typedef struct { - GFile *src; - GFile *dest; - GFile *dest_dir; - GtkWindow *parent; - ConflictResponseData *resp_data; - /* Dialogs are ran from operation threads, which need to be blocked until - * the user gives a valid response - */ - gboolean completed; - GMutex mutex; - GCond cond; -} ConflictDialogData; - -static gboolean -do_run_conflict_dialog (gpointer _data) -{ - ConflictDialogData *data = _data; - GtkWidget *dialog; - int response; - - g_mutex_lock (&data->mutex); - - dialog = nautilus_file_conflict_dialog_new (data->parent, - data->src, - data->dest, - data->dest_dir); - response = gtk_dialog_run (GTK_DIALOG (dialog)); - - if (response == CONFLICT_RESPONSE_RENAME) { - data->resp_data->new_name = - nautilus_file_conflict_dialog_get_new_name (NAUTILUS_FILE_CONFLICT_DIALOG (dialog)); - } else if (response != GTK_RESPONSE_CANCEL && - response != GTK_RESPONSE_NONE) { - data->resp_data->apply_to_all = - nautilus_file_conflict_dialog_get_apply_to_all - (NAUTILUS_FILE_CONFLICT_DIALOG (dialog)); - } - - data->resp_data->id = response; - data->completed = TRUE; - - gtk_widget_destroy (dialog); - - g_cond_signal (&data->cond); - g_mutex_unlock (&data->mutex); - - return FALSE; -} - -static ConflictResponseData * -run_conflict_dialog (CommonJob *job, - GFile *src, - GFile *dest, - GFile *dest_dir) +static FileConflictResponse * +handle_copy_move_conflict (CommonJob *job, + GFile *src, + GFile *dest, + GFile *dest_dir) { - ConflictDialogData *data; - ConflictResponseData *resp_data; - - g_timer_stop (job->time); - - data = g_slice_new0 (ConflictDialogData); - data->parent = job->parent_window; - data->src = src; - data->dest = dest; - data->dest_dir = dest_dir; - - resp_data = g_slice_new0 (ConflictResponseData); - resp_data->new_name = NULL; - data->resp_data = resp_data; - - data->completed = FALSE; - g_mutex_init (&data->mutex); - g_cond_init (&data->cond); + FileConflictResponse *response; - nautilus_progress_info_pause (job->progress); + g_timer_stop (job->time); + nautilus_progress_info_pause (job->progress); - g_mutex_lock (&data->mutex); + response = copy_move_conflict_ask_user_action (job->parent_window, + src, + dest, + dest_dir); - g_main_context_invoke (NULL, - do_run_conflict_dialog, - data); + nautilus_progress_info_resume (job->progress); + g_timer_continue (job->time); - while (!data->completed) { - g_cond_wait (&data->cond, &data->mutex); - } - - nautilus_progress_info_resume (job->progress); - - g_mutex_unlock (&data->mutex); - g_mutex_clear (&data->mutex); - g_cond_clear (&data->cond); - - g_slice_free (ConflictDialogData, data); - - g_timer_continue (job->time); - - return resp_data; -} - -static void -conflict_response_data_free (ConflictResponseData *data) -{ - g_free (data->new_name); - g_slice_free (ConflictResponseData, data); + return response; } static GFile * @@ -4649,7 +4558,7 @@ copy_move_file (CopyMoveJob *copy_job, if (!overwrite && IS_IO_ERROR (error, EXISTS)) { gboolean is_merge; - ConflictResponseData *response; + FileConflictResponse *response; g_error_free (error); @@ -4675,17 +4584,17 @@ copy_move_file (CopyMoveJob *copy_job, goto out; } - response = run_conflict_dialog (job, src, dest, dest_dir); + response = handle_copy_move_conflict (job, src, dest, dest_dir); if (response->id == GTK_RESPONSE_CANCEL || response->id == GTK_RESPONSE_DELETE_EVENT) { - conflict_response_data_free (response); + file_conflict_response_free (response); abort_job (job); } else if (response->id == CONFLICT_RESPONSE_SKIP) { if (response->apply_to_all) { job->skip_all_conflict = TRUE; } - conflict_response_data_free (response); + file_conflict_response_free (response); } else if (response->id == CONFLICT_RESPONSE_REPLACE) { /* merge/replace */ if (response->apply_to_all) { if (is_merge) { @@ -4695,13 +4604,13 @@ copy_move_file (CopyMoveJob *copy_job, } } overwrite = TRUE; - conflict_response_data_free (response); + file_conflict_response_free (response); goto retry; } else if (response->id == CONFLICT_RESPONSE_RENAME) { g_object_unref (dest); dest = get_target_file_for_display_name (dest_dir, response->new_name); - conflict_response_data_free (response); + file_conflict_response_free (response); goto retry; } else { g_assert_not_reached (); @@ -5292,7 +5201,7 @@ move_file_prepare (CopyMoveJob *move_job, else if (!overwrite && IS_IO_ERROR (error, EXISTS)) { gboolean is_merge; - ConflictResponseData *response; + FileConflictResponse *response; g_error_free (error); @@ -5311,17 +5220,17 @@ move_file_prepare (CopyMoveJob *move_job, goto out; } - response = run_conflict_dialog (job, src, dest, dest_dir); + response = handle_copy_move_conflict (job, src, dest, dest_dir); if (response->id == GTK_RESPONSE_CANCEL || response->id == GTK_RESPONSE_DELETE_EVENT) { - conflict_response_data_free (response); + file_conflict_response_free (response); abort_job (job); } else if (response->id == CONFLICT_RESPONSE_SKIP) { if (response->apply_to_all) { job->skip_all_conflict = TRUE; } - conflict_response_data_free (response); + file_conflict_response_free (response); } else if (response->id == CONFLICT_RESPONSE_REPLACE) { /* merge/replace */ if (response->apply_to_all) { if (is_merge) { @@ -5331,13 +5240,13 @@ move_file_prepare (CopyMoveJob *move_job, } } overwrite = TRUE; - conflict_response_data_free (response); + file_conflict_response_free (response); goto retry; } else if (response->id == CONFLICT_RESPONSE_RENAME) { g_object_unref (dest); dest = get_target_file_for_display_name (dest_dir, response->new_name); - conflict_response_data_free (response); + file_conflict_response_free (response); goto retry; } else { g_assert_not_reached (); 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; +} diff --git a/src/nautilus-operations-ui-manager.h b/src/nautilus-operations-ui-manager.h new file mode 100644 index 000000000..8c9102c74 --- /dev/null +++ b/src/nautilus-operations-ui-manager.h @@ -0,0 +1,27 @@ +#ifndef NAUTILUS_OPERATIONS_UI_MANAGER +#define NAUTILUS_OPERATIONS_UI_MANAGER + +#include <gio/gio.h> +#include <gtk/gtk.h> + +typedef struct { + int id; + char *new_name; + gboolean apply_to_all; +} FileConflictResponse; + +void file_conflict_response_free (FileConflictResponse *data); + +FileConflictResponse * copy_move_conflict_ask_user_action (GtkWindow *parent_window, + GFile *src, + GFile *dest, + GFile *dest_dir); + +enum +{ + CONFLICT_RESPONSE_SKIP = 1, + CONFLICT_RESPONSE_REPLACE = 2, + CONFLICT_RESPONSE_RENAME = 3, +}; + +#endif /* NAUTILUS_OPERATIONS_UI_MANAGER */ |