summaryrefslogtreecommitdiff
path: root/libnautilus-private/nautilus-file-operations.c
diff options
context:
space:
mode:
Diffstat (limited to 'libnautilus-private/nautilus-file-operations.c')
-rw-r--r--libnautilus-private/nautilus-file-operations.c3273
1 files changed, 3104 insertions, 169 deletions
diff --git a/libnautilus-private/nautilus-file-operations.c b/libnautilus-private/nautilus-file-operations.c
index 74af423ba..cfef8376f 100644
--- a/libnautilus-private/nautilus-file-operations.c
+++ b/libnautilus-private/nautilus-file-operations.c
@@ -4,6 +4,7 @@
Copyright (C) 1999, 2000 Free Software Foundation
Copyright (C) 2000, 2001 Eazel, Inc.
+ Copyright (C) 2007 Red Hat, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
@@ -20,20 +21,27 @@
Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
- Authors: Ettore Perazzoli <ettore@gnu.org>
+ Authors: Alexander Larsson <alexl@redhat.com>
+ Ettore Perazzoli <ettore@gnu.org>
Pavel Cisler <pavel@eazel.com>
*/
#include <config.h>
#include <string.h>
#include <stdio.h>
+#include <stdarg.h>
#include <locale.h>
+#include <math.h>
#include "nautilus-file-operations.h"
#include "nautilus-debug-log.h"
#include "nautilus-file-operations-progress.h"
+#include "nautilus-file-changes-queue.h"
#include "nautilus-lib-self-check-functions.h"
+#include "nautilus-progress-info.h"
+
+#include <eel/eel-alert-dialog.h>
#include <eel/eel-glib-extensions.h>
#include <eel/eel-pango-extensions.h>
#include <eel/eel-gtk-extensions.h>
@@ -54,6 +62,9 @@
#include <libgnomevfs/gnome-vfs-utils.h>
#include <libgnomevfs/gnome-vfs-volume.h>
#include <libgnomevfs/gnome-vfs-volume-monitor.h>
+#include <gio/gfile.h>
+#include <glib/gurifuncs.h>
+#include <gio/gioscheduler.h>
#include "nautilus-file-changes-queue.h"
#include "nautilus-file-private.h"
#include "nautilus-desktop-icon-file.h"
@@ -63,6 +74,210 @@
#include "nautilus-trash-monitor.h"
#include "nautilus-file-utilities.h"
+static gboolean confirm_trash_auto_value;
+
+/* TODO:
+ * Add cancellation
+ * Implement missing functions:
+ * duplicate, new file, new folder, empty trash, set_permissions recursive
+ * Make delete handle recursive deletes
+ * Use CommonJob in trash/delete code
+ * TESTING!!!
+ */
+
+typedef struct {
+ GIOJob *io_job;
+ GTimer *time;
+ GtkWidget *parent_window;
+ NautilusProgressInfo *progress;
+ GCancellable *cancellable;
+ gboolean aborted;
+ GHashTable *skip_files;
+ GHashTable *skip_readdir_error;
+ gboolean skip_all_error;
+ gboolean skip_all_conflict;
+ gboolean merge_all;
+ gboolean replace_all;
+} CommonJob;
+
+typedef struct {
+ CommonJob common;
+ gboolean is_move;
+ GList *files;
+ GFile *destination;
+ GdkPoint *icon_positions;
+ int n_icon_positions;
+ int screen_num;
+ GHashTable *debuting_files;
+ NautilusCopyCallback done_callback;
+ gpointer done_callback_data;
+} CopyMoveJob;
+
+#define SECONDS_NEEDED_FOR_RELIABLE_TRANSFER_RATE 15
+#define NSEC_PER_SEC 1000000000
+#define NSEC_PER_MSEC 1000000
+
+#define IS_IO_ERROR(__error, KIND) (((__error)->domain == G_IO_ERROR && (__error)->code == G_IO_ERROR_ ## KIND))
+
+#define SKIP _("_Skip")
+#define SKIP_ALL _("S_kip All")
+#define RETRY _("_Retry")
+#define REPLACE _("_Replace")
+#define REPLACE_ALL _("Replace _All")
+#define MERGE _("_Merge")
+#define MERGE_ALL _("Merge _All")
+
+static char *
+format_time (int seconds)
+{
+ int minutes;
+ int hours;
+ char *res;
+
+ if (seconds < 0) {
+ /* Just to make sure... */
+ seconds = 0;
+ }
+
+ if (seconds < 60) {
+ return g_strdup_printf (ngettext ("%d second","%d seconds", (int) seconds), (int) seconds);
+ }
+
+ if (seconds < 60*60) {
+ minutes = (seconds + 30) / 60;
+ return g_strdup_printf (ngettext (_("%d minute"), _("%d minutes"), minutes), minutes);
+ }
+
+ hours = seconds / (60*60);
+
+ if (seconds < 60*60*4) {
+ char *h, *m;
+
+ minutes = (seconds - hours * 60 * 60 + 30) / 60;
+
+ h = g_strdup_printf (ngettext (_("%d hour"), _("%d hours"), hours), hours);
+ m = g_strdup_printf (ngettext (_("%d minute"), _("%d minutes"), minutes), minutes);
+ res = g_strconcat (h, ", ", m, NULL);
+ g_free (h);
+ g_free (m);
+ return res;
+ }
+
+ return g_strdup_printf (_("about %d hours"), hours);
+}
+
+static char *
+custom_full_name_to_string (char *format, va_list va)
+{
+ GFile *file;
+
+ file = va_arg (va, GFile *);
+
+ return g_file_get_parse_name (file);
+}
+
+static void
+custom_full_name_skip (va_list *va)
+{
+ va_arg (*va, GFile *);
+}
+
+static char *
+custom_basename_to_string (char *format, va_list va)
+{
+ GFile *file;
+ GFileInfo *info;
+ char *name, *basename;
+
+ file = va_arg (va, GFile *);
+
+ info = g_file_query_info (file,
+ G_FILE_ATTRIBUTE_STD_DISPLAY_NAME,
+ 0,
+ g_cancellable_get_current (),
+ NULL);
+
+ name = NULL;
+ if (info) {
+ name = g_strdup (g_file_info_get_display_name (info));
+ g_object_unref (info);
+ }
+
+ if (name == NULL) {
+ basename = g_file_get_basename (file);
+ if (g_utf8_validate (basename, -1, NULL)) {
+ name = basename;
+ } else {
+ name = g_uri_escape_string (basename, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, TRUE);
+ g_free (basename);
+ }
+ }
+
+ return name;
+}
+
+static void
+custom_basename_skip (va_list *va)
+{
+ va_arg (*va, GFile *);
+}
+
+
+static char *
+custom_size_to_string (char *format, va_list va)
+{
+ goffset size;
+
+ size = va_arg (va, goffset);
+ return g_format_file_size_for_display (size);
+}
+
+static void
+custom_size_skip (va_list *va)
+{
+ va_arg (*va, goffset);
+}
+
+static char *
+custom_time_to_string (char *format, va_list va)
+{
+ int secs;
+
+ secs = va_arg (va, int);
+ return format_time (secs);
+}
+
+static void
+custom_time_skip (va_list *va)
+{
+ va_arg (*va, int);
+}
+
+static EelPrintfHandler handlers[] = {
+ { 'F', custom_full_name_to_string, custom_full_name_skip },
+ { 'B', custom_basename_to_string, custom_basename_skip },
+ { 'S', custom_size_to_string, custom_size_skip },
+ { 'T', custom_time_to_string, custom_time_skip },
+ { 0 }
+};
+
+
+static char *
+f (const char *format, ...) {
+ va_list va;
+ char *res;
+
+ va_start (va, format);
+ res = eel_strdup_vprintf_with_custom (handlers, format, va);
+ va_end (va);
+
+ return res;
+}
+
+
+
+#ifdef GIO_CONVERSION_DONE
+
typedef enum TransferKind TransferKind;
typedef struct TransferInfo TransferInfo;
typedef struct IconPositionIterator IconPositionIterator;
@@ -126,6 +341,21 @@ transfer_info_destroy (TransferInfo *transfer_info)
g_free (transfer_info);
}
+#endif /* GIO_CONVERSION_DONE */
+
+static void
+setup_autos (void)
+{
+ static gboolean setup_autos = FALSE;
+ if (!setup_autos) {
+ setup_autos = TRUE;
+ eel_preferences_add_auto_boolean (NAUTILUS_PREFERENCES_CONFIRM_TRASH,
+ &confirm_trash_auto_value);
+ }
+}
+
+#ifdef GIO_CONVERSION_DONE
+
/* Struct used to control applying icon positions to
* top level items during a copy, drag, new folder creation and
* link creation
@@ -408,6 +638,81 @@ create_transfer_dialog (const GnomeVFSXferProgressInfo *progress_info,
}
}
+/* TODO: This should really use the gio display name */
+static const char *
+get_vfs_method_display_name (char *method)
+{
+ if (g_ascii_strcasecmp (method, "computer") == 0 ) {
+ return _("Computer");
+ } else if (g_ascii_strcasecmp (method, "network") == 0 ) {
+ return _("Network");
+ } else if (g_ascii_strcasecmp (method, "fonts") == 0 ) {
+ return _("Fonts");
+ } else if (g_ascii_strcasecmp (method, "themes") == 0 ) {
+ return _("Themes");
+ } else if (g_ascii_strcasecmp (method, "burn") == 0 ) {
+ return _("CD/DVD Creator");
+ } else if (g_ascii_strcasecmp (method, "smb") == 0 ) {
+ return _("Windows Network");
+ } else if (g_ascii_strcasecmp (method, "dns-sd") == 0 ) {
+ /* translators: this is the title of the "dns-sd:///" location */
+ return _("Services in");
+ }
+ return NULL;
+}
+
+/* TODO: This should really use the gio display name */
+static char *
+get_uri_shortname_for_display (GnomeVFSURI *uri)
+{
+ char *utf8_name, *name, *tmp;
+ char *text_uri, *local_file;
+ gboolean validated;
+ const char *method;
+
+
+ validated = FALSE;
+ name = gnome_vfs_uri_extract_short_name (uri);
+ if (name == NULL) {
+ name = gnome_vfs_uri_to_string (uri, GNOME_VFS_URI_HIDE_PASSWORD);
+ } else if (g_ascii_strcasecmp (uri->method_string, "file") == 0) {
+ text_uri = gnome_vfs_uri_to_string (uri, GNOME_VFS_URI_HIDE_PASSWORD);
+ local_file = g_filename_from_uri (text_uri, NULL, NULL);
+ g_free (name);
+ if (local_file == NULL) { /* Happens for e.g. file:///# */
+ local_file = g_strdup ("/");
+ }
+ name = g_filename_display_basename (local_file);
+ g_free (local_file);
+ g_free (text_uri);
+ validated = TRUE;
+ } else if (!gnome_vfs_uri_has_parent (uri)) {
+ /* Special-case the display name for roots that are not local files */
+ method = get_vfs_method_display_name (uri->method_string);
+ if (method == NULL) {
+ method = uri->method_string;
+ }
+
+ if (name == NULL ||
+ strcmp (name, GNOME_VFS_URI_PATH_STR) == 0) {
+ g_free (name);
+ name = g_strdup (method);
+ } else {
+ tmp = name;
+ name = g_strdup_printf ("%s: %s", method, name);
+ g_free (tmp);
+ }
+ }
+
+ if (!validated && !g_utf8_validate (name, -1, NULL)) {
+ utf8_name = eel_make_valid_utf8 (name);
+ g_free (name);
+ name = utf8_name;
+ }
+
+ return name;
+}
+
static void
progress_dialog_set_to_from_item_text (NautilusFileOperationsProgress *dialog,
const char *progress_verb,
@@ -435,7 +740,7 @@ progress_dialog_set_to_from_item_text (NautilusFileOperationsProgress *dialog,
if (from_uri != NULL) {
uri = gnome_vfs_uri_new (from_uri);
- item = nautilus_get_uri_shortname_for_display (uri);
+ item = get_uri_shortname_for_display (uri);
from_path = gnome_vfs_uri_extract_dirname (uri);
hostname = NULL;
@@ -518,10 +823,12 @@ handle_transfer_ok (const GnomeVFSXferProgressInfo *progress_info,
&& strcmp (progress_info->source_name, progress_info->target_name) != 0
&& progress_info->bytes_total != progress_info->bytes_copied) {
GList *delete_me;
+ GtkWidget *toplevel;
- delete_me = g_list_prepend (NULL, progress_info->target_name);
- nautilus_file_operations_delete (delete_me, transfer_info->parent_view, NULL, NULL);
- g_list_free (delete_me);
+ delete_me = g_list_prepend (NULL, g_file_new_for_uri (progress_info->target_name));
+ toplevel = gtk_widget_get_toplevel (transfer_info->parent_view);
+ nautilus_file_operations_delete (delete_me, GTK_WINDOW (toplevel), NULL, NULL);
+ eel_g_object_list_free (delete_me);
}
return 0;
@@ -1026,7 +1333,7 @@ handle_transfer_vfs_error (const GnomeVFSXferProgressInfo *progress_info,
error_dialog_button_pressed = eel_run_simple_dialog
(parent_for_error_dialog (transfer_info), TRUE,
GTK_MESSAGE_ERROR, text,
- detail, GTK_STOCK_CANCEL, _("_Retry"), NULL);
+ detail, GTK_STOCK_CANCEL, RETRY, NULL);
switch (error_dialog_button_pressed) {
case 0:
@@ -1043,7 +1350,7 @@ handle_transfer_vfs_error (const GnomeVFSXferProgressInfo *progress_info,
error_dialog_button_pressed = eel_run_simple_dialog
(parent_for_error_dialog (transfer_info), TRUE,
GTK_MESSAGE_ERROR, text,
- detail, _("_Skip"), GTK_STOCK_CANCEL, _("_Retry"), NULL);
+ detail, SKIP, GTK_STOCK_CANCEL, RETRY, NULL);
switch (error_dialog_button_pressed) {
case 0:
@@ -1211,7 +1518,7 @@ handle_transfer_overwrite (const GnomeVFSXferProgressInfo *progress_info,
GTK_MESSAGE_WARNING,
text,
secondary_text,
- _("_Skip"), _("_Replace"), NULL);
+ SKIP, REPLACE, NULL);
g_free (text);
g_free (secondary_text);
@@ -1230,7 +1537,7 @@ handle_transfer_overwrite (const GnomeVFSXferProgressInfo *progress_info,
result = eel_run_simple_dialog
(parent_for_error_dialog (transfer_info), TRUE, GTK_MESSAGE_WARNING, text,
secondary_text,
- _("S_kip All"), _("Replace _All"), _("_Skip"), _("_Replace"), NULL);
+ SKIP_ALL, REPLACE_ALL, SKIP, REPLACE, NULL);
g_free (text);
g_free (secondary_text);
@@ -1761,7 +2068,7 @@ sync_transfer_callback (GnomeVFSXferProgressInfo *progress_info, gpointer data)
*/
if (progress_info->source_name == NULL) {
/* remove any old metadata */
- nautilus_file_changes_queue_schedule_metadata_remove
+ nautilus_file_changes_queue_schedule_metadata_remove_by_uri
(progress_info->target_name);
} else {
nautilus_file_changes_queue_schedule_metadata_copy
@@ -1821,10 +2128,10 @@ sync_transfer_callback (GnomeVFSXferProgressInfo *progress_info, gpointer data)
case GNOME_VFS_XFER_PHASE_DELETESOURCE:
g_assert (progress_info->source_name != NULL);
if (progress_info->top_level_item) {
- nautilus_file_changes_queue_schedule_metadata_remove
+ nautilus_file_changes_queue_schedule_metadata_remove_by_uri
(progress_info->source_name);
}
- nautilus_file_changes_queue_file_removed (progress_info->source_name);
+ nautilus_file_changes_queue_file_removed_by_uri (progress_info->source_name);
break;
case GNOME_VFS_XFER_PHASE_COMPLETED:
@@ -1998,7 +2305,7 @@ nautilus_file_operations_copy_move (const GList *item_uris,
text_uri = gnome_vfs_uri_to_string (source_uri, GNOME_VFS_URI_HIDE_NONE);
- path = gnome_vfs_get_local_path_from_uri (text_uri);
+ path = g_filename_from_uri (text_uri, NULL, NULL);
volume = NULL;
if (path != NULL) {
@@ -2733,7 +3040,7 @@ nautilus_file_operations_new_file (GtkWidget *parent_view,
target_filename = g_filename_from_utf8 (_("new file"), -1, NULL, NULL, NULL);
- source_file_uri = gnome_vfs_get_uri_from_local_path (source_file_str);
+ source_file_uri = g_filename_to_uri (source_file_str, NULL, NULL);
new_data = g_new (struct NewFileData, 1);
new_data->tmp_file = g_strdup (source_file_str);
@@ -2752,84 +3059,692 @@ nautilus_file_operations_new_file (GtkWidget *parent_view,
g_free (target_filename);
}
-void
-nautilus_file_operations_delete (const GList *item_uris,
- GtkWidget *parent_view,
- NautilusDeleteCallback done_callback,
- gpointer done_callback_data)
+#endif /* GIO_CONVERSION_DONE */
+
+
+typedef struct {
+ GList *files;
+ GtkWindow *parent_window;
+ gboolean try_trash;
+ gboolean delete_if_all_already_in_trash;
+ NautilusDeleteCallback done_callback;
+ gpointer done_callback_data;
+} DeleteJob;
+
+static gboolean
+can_delete_without_confirm (GFile *file)
{
- GList *uri_list;
- const GList *p;
- const char *item_uri;
- NautilusFile *file;
- TransferInfo *transfer_info;
+ if (g_file_has_uri_scheme (file, "burn")) {
+ return TRUE;
+ }
- nautilus_debug_log_with_uri_list (FALSE, NAUTILUS_DEBUG_LOG_DOMAIN_USER, item_uris,
- "delete files:");
+ return FALSE;
+}
- uri_list = NULL;
- for (p = item_uris; p != NULL; p = p->next) {
- item_uri = (const char *) p->data;
-
- if (eel_uri_is_desktop (item_uri)) {
- file = nautilus_file_get_existing (item_uri);
- if (file != NULL) {
- if (NAUTILUS_IS_DESKTOP_ICON_FILE (file)) {
- NautilusDesktopLink *link;
-
- link = nautilus_desktop_icon_file_get_link (NAUTILUS_DESKTOP_ICON_FILE (file));
-
- if (link != NULL) {
- nautilus_desktop_link_monitor_delete_link (nautilus_desktop_link_monitor_get (),
- link,
- parent_view);
- g_object_unref (link);
- }
- }
- nautilus_file_unref (file);
- }
+static gboolean
+can_delete_files_without_confirm (GList *files)
+{
+ g_assert (files != NULL);
+
+ while (files != NULL) {
+ if (!can_delete_without_confirm (files->data)) {
+ return FALSE;
+ }
+
+ files = files->next;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+can_trash_file (GFile *file, GCancellable *cancellable)
+{
+ GFileInfo *info;
+ gboolean res;
+
+ res = FALSE;
+ info = g_file_query_info (file,
+ G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH,
+ 0,
+ cancellable,
+ NULL);
+
+ if (info) {
+ res = g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH);
+ g_object_unref (info);
+ }
+
+ return res;
+}
+
+static char *
+get_display_name (GFile *file, GCancellable *cancellable)
+{
+ GFileInfo *info;
+ char *name, *basename;
+
+ info = g_file_query_info (file,
+ G_FILE_ATTRIBUTE_STD_DISPLAY_NAME,
+ 0, cancellable, NULL);
+
+ name = NULL;
+ if (info) {
+ name = g_strdup (g_file_info_get_display_name (info));
+ g_object_unref (info);
+ }
+
+ if (name == NULL) {
+ basename = g_file_get_basename (file);
+ if (g_utf8_validate (basename, -1, NULL)) {
+ name = basename;
} else {
- uri_list = g_list_prepend (uri_list,
- gnome_vfs_uri_new (item_uri));
+ name = g_uri_escape_string (basename, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, TRUE);
+ g_free (basename);
}
}
- uri_list = g_list_reverse (uri_list);
+
+ return name;
+}
- if (uri_list == NULL) {
- return;
+static void
+delete_files (GList *files, GCancellable *cancellable, GtkWindow *parent_window)
+{
+ GList *l;
+ GFile *file;
+ GError *error;
+
+ for (l = files; l != NULL; l = l->next) {
+ file = l->data;
+
+ error = NULL;
+ if (!g_file_delete (file, cancellable, &error)) {
+ /* TODO-gio: Dialog here, and handle recursive deletes */
+ g_print ("Error deleting file: %s\n", error->message);
+ } else {
+ nautilus_file_changes_queue_schedule_metadata_remove (file);
+ nautilus_file_changes_queue_file_removed (file);
+ }
}
+}
- transfer_info = transfer_info_new (parent_view);
+static void
+trash_files (GList *files, GCancellable *cancellable, GtkWindow *parent_window)
+{
+ GList *l;
+ GFile *file;
+ GError *error;
- /* localizers: progress dialog title */
- transfer_info->operation_title = _("Deleting files");
- /* localizers: label prepended to the progress count */
- transfer_info->action_label =_("Files deleted:");
- /* localizers: label prepended to the name of the current file deleted */
- transfer_info->progress_verb =_("Deleting");
- transfer_info->preparation_name =_("Preparing to Delete files...");
- transfer_info->cleanup_name ="";
+ for (l = files; l != NULL; l = l->next) {
+ file = l->data;
- transfer_info->error_mode = GNOME_VFS_XFER_ERROR_MODE_QUERY;
- transfer_info->overwrite_mode = GNOME_VFS_XFER_OVERWRITE_MODE_REPLACE;
- transfer_info->kind = TRANSFER_DELETE;
+ error = NULL;
+ if (!g_file_trash (file, cancellable, &error)) {
+ /* TODO-gio: Dialog here, allow delete instead of trash, etc */
+ g_print ("Error trashing file: %s\n", error->message);
+ } else {
+ nautilus_file_changes_queue_schedule_metadata_remove (file);
+ nautilus_file_changes_queue_file_removed (file);
+ }
+ }
+}
+
+typedef struct {
+ GtkWindow *parent_window;
+ gboolean ignore_close_box;
+ GtkMessageType message_type;
+ const char *primary_text;
+ const char *secondary_text;
+ const char *details_text;
+ const char **button_titles;
- transfer_info->done_callback = done_callback;
- transfer_info->done_callback_data = done_callback_data;
- transfer_info->debuting_uris = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+ int result;
+} RunSimpleDialogData;
- gnome_vfs_async_xfer (&transfer_info->handle, uri_list, NULL,
- GNOME_VFS_XFER_DELETE_ITEMS | GNOME_VFS_XFER_RECURSIVE,
- GNOME_VFS_XFER_ERROR_MODE_QUERY,
- GNOME_VFS_XFER_OVERWRITE_MODE_REPLACE,
- GNOME_VFS_PRIORITY_DEFAULT,
- update_transfer_callback, transfer_info,
- sync_transfer_callback, NULL);
+static void
+do_run_simple_dialog (gpointer _data)
+{
+ RunSimpleDialogData *data = _data;
+ const char *button_title;
+ GtkWidget *dialog;
+ int result;
+ int response_id;
+
+ /* Create the dialog. */
+ dialog = eel_alert_dialog_new (data->parent_window,
+ 0,
+ data->message_type,
+ GTK_BUTTONS_NONE,
+ data->primary_text,
+ data->secondary_text);
+
+ for (response_id = 0;
+ data->button_titles[response_id] != NULL;
+ response_id++) {
+ button_title = data->button_titles[response_id];
+ gtk_dialog_add_button (GTK_DIALOG (dialog), button_title, response_id);
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), response_id);
+ }
+
+ if (data->details_text) {
+ eel_alert_dialog_set_details_label (EEL_ALERT_DIALOG (dialog),
+ data->details_text);
+ }
+
+ /* Run it. */
+ gtk_widget_show (dialog);
+ result = gtk_dialog_run (GTK_DIALOG (dialog));
+
+ while ((result == GTK_RESPONSE_NONE || result == GTK_RESPONSE_DELETE_EVENT) && data->ignore_close_box) {
+ gtk_widget_show (GTK_WIDGET (dialog));
+ result = gtk_dialog_run (GTK_DIALOG (dialog));
+ }
+
+ gtk_object_destroy (GTK_OBJECT (dialog));
+
+ data->result = result;
+}
+
+static int
+_run_simple_dialog (GIOJob *job,
+ GtkWindow *parent_window,
+ gboolean ignore_close_box,
+ GtkMessageType message_type,
+ const char *primary_text,
+ const char *secondary_text,
+ const char *details_text,
+ ...)
+{
+ RunSimpleDialogData *data;
+ va_list varargs;
+ int res;
+ const char *button_title;
+ GPtrArray *ptr_array;
+
+ data = g_new0 (RunSimpleDialogData, 1);
+ data->parent_window = parent_window;
+ data->ignore_close_box = ignore_close_box;
+ data->message_type = message_type;
+ data->primary_text = primary_text;
+ data->secondary_text = secondary_text;
+ data->details_text = details_text;
+
+ ptr_array = g_ptr_array_new ();
+ va_start (varargs, details_text);
+ while ((button_title = va_arg (varargs, const char *)) != NULL) {
+ g_ptr_array_add (ptr_array, (char *)button_title);
+ }
+ g_ptr_array_add (ptr_array, NULL);
+ data->button_titles = (const char **)g_ptr_array_free (ptr_array, FALSE);
+ va_end (varargs);
+
+ g_io_job_send_to_mainloop (job,
+ do_run_simple_dialog,
+ data,
+ NULL,
+ TRUE);
+
+ res = data->result;
+
+ g_free (data->button_titles);
+ g_free (data);
+ return res;
+}
+
+static int
+_run_alert (GIOJob *job,
+ GtkWindow *parent_window,
+ const char *primary_message,
+ const char *secondary_message,
+ const char *ok_label)
+{
+ return _run_simple_dialog (job, parent_window,
+ FALSE,
+ GTK_MESSAGE_WARNING,
+ primary_message,
+ secondary_message,
+ NULL,
+ GTK_STOCK_CANCEL,
+ ok_label,
+ NULL);
+}
+
+static int
+_run_yes_no_dialog (GIOJob *job,
+ const char *prompt,
+ const char *detail,
+ const char *yes_label,
+ const char *no_label,
+ GtkWindow *parent_window)
+{
+ return _run_simple_dialog (job, parent_window,
+ FALSE,
+ GTK_MESSAGE_QUESTION,
+ prompt,
+ detail,
+ NULL,
+ no_label,
+ yes_label,
+ NULL);
+}
+
+/* NOTE: This frees the primary / secondary strings, in order to
+ avoid doing that everywhere. So, make sure they are strduped */
+
+static int
+run_simple_dialog_va (CommonJob *job,
+ gboolean ignore_close_box,
+ GtkMessageType message_type,
+ char *primary_text,
+ char *secondary_text,
+ const char *details_text,
+ va_list varargs)
+{
+ RunSimpleDialogData *data;
+ int res;
+ const char *button_title;
+ GPtrArray *ptr_array;
+
+ g_timer_stop (job->time);
+
+ data = g_new0 (RunSimpleDialogData, 1);
+ data->parent_window = GTK_WINDOW (job->parent_window);
+ data->ignore_close_box = ignore_close_box;
+ data->message_type = message_type;
+ data->primary_text = primary_text;
+ data->secondary_text = secondary_text;
+ data->details_text = details_text;
+
+ ptr_array = g_ptr_array_new ();
+ while ((button_title = va_arg (varargs, const char *)) != NULL) {
+ g_ptr_array_add (ptr_array, (char *)button_title);
+ }
+ g_ptr_array_add (ptr_array, NULL);
+ data->button_titles = (const char **)g_ptr_array_free (ptr_array, FALSE);
+
+ g_io_job_send_to_mainloop (job->io_job,
+ do_run_simple_dialog,
+ data,
+ NULL,
+ TRUE);
+
+ res = data->result;
+
+ g_free (data->button_titles);
+ g_free (data);
+
+ g_timer_continue (job->time);
+
+ g_free (primary_text);
+ g_free (secondary_text);
+
+ return res;
+}
+
+#if 0 /* Not used at the moment */
+static int
+run_simple_dialog (CommonJob *job,
+ gboolean ignore_close_box,
+ GtkMessageType message_type,
+ char *primary_text,
+ char *secondary_text,
+ const char *details_text,
+ ...)
+{
+ va_list varargs;
+ int res;
+
+ va_start (varargs, details_text);
+ res = run_simple_dialog_va (job,
+ ignore_close_box,
+ message_type,
+ primary_text,
+ secondary_text,
+ details_text,
+ varargs);
+ va_end (varargs);
+ return res;
+}
+#endif
+
+static int
+run_error (CommonJob *job,
+ char *primary_text,
+ char *secondary_text,
+ const char *details_text,
+ ...)
+{
+ va_list varargs;
+ int res;
+
+ va_start (varargs, details_text);
+ res = run_simple_dialog_va (job,
+ FALSE,
+ GTK_MESSAGE_ERROR,
+ primary_text,
+ secondary_text,
+ details_text,
+ varargs);
+ va_end (varargs);
+ return res;
+}
+
+static int
+run_warning (CommonJob *job,
+ char *primary_text,
+ char *secondary_text,
+ const char *details_text,
+ ...)
+{
+ va_list varargs;
+ int res;
+
+ va_start (varargs, details_text);
+ res = run_simple_dialog_va (job,
+ FALSE,
+ GTK_MESSAGE_WARNING,
+ primary_text,
+ secondary_text,
+ details_text,
+ varargs);
+ va_end (varargs);
+ return res;
+}
+
+static gboolean
+confirm_delete_from_trash (GIOJob *job,
+ GtkWindow *parent_window,
+ GList *files)
+{
+ char *prompt;
+ char *file_name;
+ int file_count;
+ int response;
+
+ /* Just Say Yes if the preference says not to confirm. */
+ if (!confirm_trash_auto_value) {
+ return TRUE;
+ }
+
+ file_count = g_list_length (files);
+ g_assert (file_count > 0);
+
+ if (file_count == 1) {
+ file_name = get_display_name ((GFile *) files->data, NULL);
+ prompt = g_strdup_printf (_("Are you sure you want to permanently delete \"%s\" "
+ "from the trash?"), file_name);
+ g_free (file_name);
+ } else {
+ prompt = g_strdup_printf (ngettext("Are you sure you want to permanently delete "
+ "the %d selected item from the trash?",
+ "Are you sure you want to permanently delete "
+ "the %d selected items from the trash?",
+ file_count),
+ file_count);
+ }
+
+ response = _run_alert (job, parent_window,
+ prompt,
+ _("If you delete an item, it will be permanently lost."),
+ GTK_STOCK_DELETE);
+
+ return (response == 1);
+}
+
+static gboolean
+confirm_deletion (GIOJob *job,
+ GtkWindow *parent_window,
+ GList *files,
+ gboolean all)
+{
+ char *prompt;
+ char *detail;
+ int file_count;
+ GFile *file;
+ char *file_name;
+ int response;
+
+ file_count = g_list_length (files);
+ g_assert (file_count > 0);
+
+ if (file_count == 1) {
+ file = files->data;
+ if (g_file_has_uri_scheme (file, "x-nautilus-desktop")) {
+ /* Don't ask for desktop icons */
+ return TRUE;
+ }
+ file_name = get_display_name (file, NULL);
+ prompt = _("Cannot move file to trash, do you want to delete immediately?");
+ detail = g_strdup_printf (_("The file \"%s\" cannot be moved to the trash."), file_name);
+ g_free (file_name);
+ } else {
+ if (all) {
+ prompt = _("Cannot move items to trash, do you want to delete them immediately?");
+ detail = g_strdup_printf (ngettext("The selected item could not be moved to the Trash",
+ "The %d selected items could not be moved to the Trash",
+ file_count),
+ file_count);
+ } else {
+ prompt = _("Cannot move some items to trash, do you want to delete these immediately?");
+ detail = g_strdup_printf (_("%d of the selected items cannot be moved to the Trash"), file_count);
+ }
+ }
+
+ response = _run_yes_no_dialog (job,
+ prompt,
+ detail,
+ GTK_STOCK_DELETE, GTK_STOCK_CANCEL,
+ parent_window);
+
+ g_free (detail);
+
+ return (response == 1);
+}
+
+static gboolean
+confirm_delete_directly (GIOJob *job,
+ GtkWindow *parent_window,
+ GList *files)
+{
+ char *prompt;
+ char *file_name;
+ int file_count;
+ int response;
+
+ /* Just Say Yes if the preference says not to confirm. */
+ if (!confirm_trash_auto_value) {
+ return TRUE;
+ }
+
+ file_count = g_list_length (files);
+ g_assert (file_count > 0);
+
+ if (can_delete_files_without_confirm (files)) {
+ return TRUE;
+ }
+
+ if (file_count == 1) {
+ file_name = get_display_name (files->data, NULL);
+ prompt = g_strdup_printf (_("Are you sure you want to permanently delete \"%s\"?"),
+ file_name);
+ g_free (file_name);
+ } else {
+ prompt = g_strdup_printf (ngettext("Are you sure you want to permanently delete "
+ "the %d selected item?",
+ "Are you sure you want to permanently delete "
+ "the %d selected items?", file_count), file_count);
+ }
+
+ response = _run_alert (job, parent_window,
+ prompt,
+ _("If you delete an item, it will be permanently lost."),
+ GTK_STOCK_DELETE);
+
+ return response == 1;
+}
+
+
+static void delete_job_done (gpointer data);
+
+static void
+delete_job (GIOJob *io_job,
+ GCancellable *cancellable,
+ gpointer user_data)
+{
+ DeleteJob *job = user_data;
+ GList *trashable_files;
+ GList *untrashable_files;
+ GList *in_trash_files;
+ GList *no_confirm_files;
+ GList *l;
+ GFile *file;
+ gboolean confirmed;
+
+ /* Collect three lists: (1) items that can be moved to trash,
+ * (2) items that can only be deleted in place, and (3) items that
+ * are already in trash.
+ *
+ * Always move (1) to trash if non-empty.
+ * Delete (3) only if (1) and (2) are non-empty, otherwise ignore (3).
+ * Ask before deleting (2) if non-empty.
+ * Ask before deleting (3) if non-empty.
+ */
+
+ trashable_files = NULL;
+ untrashable_files = NULL;
+ in_trash_files = NULL;
+ no_confirm_files = NULL;
+
+ for (l = job->files; l != NULL; l = l->next) {
+ file = l->data;
+
+ if (job->try_trash &&
+ job->delete_if_all_already_in_trash &&
+ g_file_has_uri_scheme (file, "trash")) {
+ in_trash_files = g_list_prepend (in_trash_files, file);
+ } else if (can_delete_without_confirm (file)) {
+ no_confirm_files = g_list_prepend (no_confirm_files, file);
+ } else if (job->try_trash &&
+ can_trash_file (file, NULL)) {
+ trashable_files = g_list_prepend (trashable_files, file);
+ } else {
+ untrashable_files = g_list_prepend (untrashable_files, file);
+ }
+ }
+
+ if (in_trash_files != NULL && trashable_files == NULL && untrashable_files == NULL) {
+ if (confirm_delete_from_trash (io_job, job->parent_window, in_trash_files)) {
+ delete_files (in_trash_files, NULL, job->parent_window);
+ }
+ } else {
+ if (no_confirm_files != NULL) {
+ delete_files (no_confirm_files, NULL, job->parent_window);
+ }
+ if (trashable_files != NULL) {
+ trash_files (trashable_files, NULL, job->parent_window);
+ }
+ if (untrashable_files != NULL) {
+ if (job->try_trash) {
+ confirmed = confirm_deletion (io_job, job->parent_window,
+ untrashable_files, trashable_files == NULL);
+ } else {
+ confirmed = confirm_delete_directly (io_job, job->parent_window, untrashable_files);
+ }
+
+ if (confirmed) {
+ delete_files (untrashable_files, NULL, job->parent_window);
+ }
+ }
+ }
+
+ g_list_free (in_trash_files);
+ g_list_free (trashable_files);
+ g_list_free (untrashable_files);
+ g_list_free (no_confirm_files);
+
+ g_io_job_send_to_mainloop (io_job,
+ delete_job_done,
+ job,
+ NULL,
+ FALSE);
+
+}
+
+static void
+delete_job_done (gpointer user_data)
+{
+ DeleteJob *job = user_data;
+ GHashTable *debuting_uris;
+
+ eel_g_object_list_free (job->files);
+ if (job->parent_window) {
+ g_object_unref (job->parent_window);
+ }
+
+ if (job->done_callback) {
+ debuting_uris = g_hash_table_new_full (g_file_hash, (GEqualFunc)g_file_equal, g_object_unref, NULL);
+ job->done_callback (debuting_uris, job->done_callback_data);
+ g_hash_table_unref (debuting_uris);
+ }
+ g_free (job);
- gnome_vfs_uri_list_free (uri_list);
+ nautilus_file_changes_consume_changes (TRUE);
}
static void
+trash_or_delete_internal (GList *files,
+ GtkWindow *parent_window,
+ gboolean try_trash,
+ NautilusDeleteCallback done_callback,
+ gpointer done_callback_data)
+{
+ DeleteJob *job;
+
+ setup_autos ();
+
+ /* TODO: special case desktop icon link files ... */
+
+ /* TODO: Progress dialog, cancellation */
+
+ job = g_new0 (DeleteJob, 1);
+ job->files = eel_g_object_list_copy (files);
+ job->parent_window = g_object_ref (parent_window);
+ job->try_trash = try_trash;
+ job->done_callback = done_callback;
+ job->done_callback_data = done_callback_data;
+
+ g_schedule_io_job (delete_job,
+ job,
+ NULL,
+ 0,
+ NULL);
+}
+
+void
+nautilus_file_operations_trash_or_delete (GList *files,
+ GtkWindow *parent_window,
+ NautilusDeleteCallback done_callback,
+ gpointer done_callback_data)
+{
+ trash_or_delete_internal (files, parent_window,
+ TRUE,
+ done_callback, done_callback_data);
+}
+
+void
+nautilus_file_operations_delete (GList *files,
+ GtkWindow *parent_window,
+ NautilusDeleteCallback done_callback,
+ gpointer done_callback_data)
+{
+ trash_or_delete_internal (files, parent_window,
+ FALSE,
+ done_callback, done_callback_data);
+}
+
+#ifdef GIO_CONVERSION_DONE
+
+static void
do_empty_trash (GtkWidget *parent_view)
{
TransferInfo *transfer_info;
@@ -2838,7 +3753,8 @@ do_empty_trash (GtkWidget *parent_view)
nautilus_debug_log (FALSE, NAUTILUS_DEBUG_LOG_DOMAIN_USER,
"empty trash");
- trash_dir_list = nautilus_trash_monitor_get_trash_directories ();
+ /* TODO-gio: Implement */
+ trash_dir_list = NULL;
if (trash_dir_list != NULL) {
/* set up the move parameters */
transfer_info = transfer_info_new (parent_view);
@@ -2979,13 +3895,13 @@ typedef struct {
gpointer volume;
GnomeVFSVolumeOpCallback callback;
gpointer user_data;
-} NautilusUnmountCallback;
+} NautilusUnmountDataCallback;
static void
delete_callback (GHashTable *debuting_uris,
gpointer data)
{
- NautilusUnmountCallback *unmount_info;
+ NautilusUnmountDataCallback *unmount_info;
unmount_info = data;
if (GNOME_IS_VFS_VOLUME (unmount_info->volume)) {
@@ -3044,12 +3960,46 @@ prompt_empty_trash (GtkWidget *parent_view)
return result;
}
-void
-nautilus_file_operations_unmount_volume (GtkWidget *parent_view,
- GnomeVFSVolume *volume,
- GnomeVFSVolumeOpCallback callback,
- gpointer user_data)
+#endif /* GIO_CONVERSION_DONE */
+
+
+typedef struct {
+ NautilusUnmountCallback callback;
+ gpointer user_data;
+} UnmountData;
+
+static void
+unmount_volume_callback (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
{
+ UnmountData *data = user_data;
+ GError *error;
+
+ error = NULL;
+ g_volume_unmount_finish (G_VOLUME (source_object),
+ res, &error);
+ if (data->callback) {
+ data->callback (error, data->user_data);
+ }
+
+ if (error) {
+ g_error_free (error);
+ }
+
+ g_free (data);
+}
+
+
+void
+nautilus_file_operations_unmount_volume (GtkWindow *parent_window,
+ GVolume *volume,
+ NautilusUnmountCallback callback,
+ gpointer user_data)
+{
+ /* TODO-gio: Empty trash before unmount */
+
+#ifdef GIO_CONVERSION_DONE
gchar *trash_uri_str;
gboolean trash_is_empty;
@@ -3059,7 +4009,7 @@ nautilus_file_operations_unmount_volume (GtkWidget *parent_view,
trash_uri_str = get_trash_uri_for_volume (volume);
if (trash_uri_str) {
NautilusDirectory *trash_dir;
- trash_dir = nautilus_directory_get (trash_uri_str);
+ trash_dir = nautilus_directory_get_by_uri (trash_uri_str);
/* Check if the trash on this volume is empty,
* If the trash directory on this volume exists it's monitored
@@ -3075,18 +4025,20 @@ nautilus_file_operations_unmount_volume (GtkWidget *parent_view,
case GTK_RESPONSE_ACCEPT:
{
GList *trash_dir_list;
- NautilusUnmountCallback *unmount_cb;
+ NautilusUnmountDataCallback *unmount_cb;
+ GtkWidget *toplevel;
trash_dir_list = NULL;
- unmount_cb = g_new (NautilusUnmountCallback, 1);
+ unmount_cb = g_new (NautilusUnmountDataCallback, 1);
unmount_cb->volume = gnome_vfs_volume_ref (volume);
unmount_cb->callback = callback;
unmount_cb->user_data = user_data;
- trash_dir_list = g_list_append (trash_dir_list, trash_uri_str);
- nautilus_file_operations_delete (trash_dir_list, parent_view,
+ trash_dir_list = g_list_append (trash_dir_list, g_file_new_for_uri (trash_uri_str));
+ toplevel = gtk_widget_get_toplevel (parent_view);
+ nautilus_file_operations_delete (trash_dir_list, GTK_WINDOW (toplevel),
(NautilusDeleteCallback) delete_callback,
unmount_cb);
- g_list_free (trash_dir_list);
+ eel_g_object_list_free (trash_dir_list);
/* volume is unmounted in the callback */
break;
}
@@ -3098,78 +4050,19 @@ nautilus_file_operations_unmount_volume (GtkWidget *parent_view,
}
}
g_free (trash_uri_str);
+#endif
+ UnmountData *data;
+
+ data = g_new0 (UnmountData, 1);
+ data->callback = callback;
+ data->user_data = user_data;
+ g_volume_unmount (volume,
+ NULL,
+ unmount_volume_callback,
+ data);
}
-void
-nautilus_file_operations_unmount_drive (GtkWidget *parent_view,
- GnomeVFSDrive *drive,
- GnomeVFSVolumeOpCallback callback,
- gpointer user_data)
-{
- GList *volumes;
- GList *it;
- GList *trash_dir_list;
- GnomeVFSVolume *volume;
- gchar *trash_uri_str;
- gboolean trash_is_empty;
-
- g_return_if_fail (parent_view != NULL);
-
- trash_dir_list = NULL;
- trash_is_empty = TRUE;
- volumes = gnome_vfs_drive_get_mounted_volumes (drive);
- for (it = volumes; it != NULL; it = g_list_next (it)) {
- volume = it->data;
- trash_uri_str = get_trash_uri_for_volume (volume);
- if (trash_uri_str) {
- trash_dir_list = g_list_prepend (trash_dir_list, trash_uri_str);
- /* Check if any of the volumes have trash on them.
- * If the trash directories on the volumes exist they are monitored
- * by the trash monitor so this should give accurate results. */
- if (trash_is_empty) {
- NautilusDirectory *trash_dir;
- trash_dir = nautilus_directory_get (trash_uri_str);
- trash_is_empty = ! nautilus_directory_is_not_empty (trash_dir);
- }
- }
- gnome_vfs_volume_unref (volume);
- }
-
- if (trash_is_empty) {
- /* no trash so unmount as usual */
- gnome_vfs_drive_unmount (drive, callback, user_data);
- } else {
- switch (prompt_empty_trash (parent_view)) {
- case GTK_RESPONSE_ACCEPT:
- {
- NautilusUnmountCallback *unmount_cb;
- gchar *temp;
-
- unmount_cb = g_new (NautilusUnmountCallback, 1);
- unmount_cb->volume = gnome_vfs_drive_ref (drive);
- unmount_cb->callback = callback;
- unmount_cb->user_data = user_data;
- trash_dir_list = g_list_reverse (trash_dir_list);
- nautilus_file_operations_delete (trash_dir_list, parent_view,
- (NautilusDeleteCallback) delete_callback,
- unmount_cb);
-
- for (it = trash_dir_list; it != NULL; it = g_list_next (it)) {
- temp = it->data;
- g_free (temp);
- }
- g_list_free (trash_dir_list);
- /* volume is unmounted in the callback */
- break;
- }
- case GTK_RESPONSE_REJECT:
- gnome_vfs_drive_unmount (drive, callback, user_data);
- break;
- default:
- break;
- }
- }
-}
+#ifdef GIO_CONVERSION_DONE
struct RecursivePermissionsInfo {
GnomeVFSAsyncHandle *handle;
@@ -3177,22 +4070,22 @@ struct RecursivePermissionsInfo {
GnomeVFSURI *current_file;
GList *files;
GList *directories;
- GnomeVFSFilePermissions file_permissions;
- GnomeVFSFilePermissions file_mask;
- GnomeVFSFilePermissions dir_permissions;
- GnomeVFSFilePermissions dir_mask;
+ guint32 file_permissions;
+ guint32 file_mask;
+ guint32 dir_permissions;
+ guint32 dir_mask;
NautilusSetPermissionsCallback callback;
gpointer callback_data;
};
struct FileInfo {
char *name;
- GnomeVFSFilePermissions permissions;
+ guint32 permissions;
};
struct DirInfo {
GnomeVFSURI *uri;
- GnomeVFSFilePermissions permissions;
+ guint32 permissions;
};
static void set_permissions_run (struct RecursivePermissionsInfo *info);
@@ -3372,10 +4265,10 @@ set_permissions_run (struct RecursivePermissionsInfo *info)
void
nautilus_file_set_permissions_recursive (const char *directory,
- GnomeVFSFilePermissions file_permissions,
- GnomeVFSFilePermissions file_mask,
- GnomeVFSFilePermissions dir_permissions,
- GnomeVFSFilePermissions dir_mask,
+ guint32 file_permissions,
+ guint32 file_mask,
+ guint32 dir_permissions,
+ guint32 dir_mask,
NautilusSetPermissionsCallback callback,
gpointer callback_data)
{
@@ -3403,6 +4296,2044 @@ nautilus_file_set_permissions_recursive (const char *directo
set_permissions_load_dir (NULL, GNOME_VFS_OK, NULL, info);
}
+#endif /* GIO_CONVERSION_DONE */
+
+#define op_job_new(__type, parent_window) ((__type *)(init_common (sizeof(__type), parent_window)))
+
+static gpointer
+init_common (gsize job_size,
+ GtkWindow *parent_window)
+{
+ CommonJob *common;
+
+ common = g_malloc0 (job_size);
+
+ common->parent_window = g_object_ref (parent_window);
+ common->progress = nautilus_progress_info_new ();
+ common->cancellable = nautilus_progress_info_get_cancellable (common->progress);
+ common->time = g_timer_new ();
+
+ return common;
+}
+
+static void
+finalize_common (CommonJob *common)
+{
+ nautilus_progress_info_finish (common->progress);
+
+ g_timer_destroy (common->time);
+
+ if (common->parent_window) {
+ g_object_unref (common->parent_window);
+ }
+ if (common->skip_files) {
+ g_hash_table_destroy (common->skip_files);
+ }
+ if (common->skip_readdir_error) {
+ g_hash_table_destroy (common->skip_readdir_error);
+ }
+ g_object_unref (common->progress);
+ g_object_unref (common->cancellable);
+ g_free (common);
+}
+
+static void
+skip_file (CommonJob *common,
+ GFile *file)
+{
+ if (common->skip_files == NULL) {
+ common->skip_files =
+ g_hash_table_new_full (g_file_hash, (GEqualFunc)g_file_equal, g_object_unref, NULL);
+ }
+
+ g_hash_table_insert (common->skip_files, g_object_ref (file), file);
+}
+
+static void
+skip_readdir_error (CommonJob *common,
+ GFile *dir)
+{
+ if (common->skip_readdir_error == NULL) {
+ common->skip_readdir_error =
+ g_hash_table_new_full (g_file_hash, (GEqualFunc)g_file_equal, g_object_unref, NULL);
+ }
+
+ g_hash_table_insert (common->skip_readdir_error, g_object_ref (dir), dir);
+}
+
+static gboolean
+should_skip_file (CommonJob *common,
+ GFile *file)
+{
+ if (common->skip_files != NULL) {
+ return g_hash_table_lookup (common->skip_files, file) != NULL;
+ }
+ return FALSE;
+}
+
+static gboolean
+should_skip_readdir_error (CommonJob *common,
+ GFile *dir)
+{
+ if (common->skip_readdir_error != NULL) {
+ return g_hash_table_lookup (common->skip_readdir_error, dir) != NULL;
+ }
+ return FALSE;
+}
+
+typedef enum {
+ OP_KIND_COPY = 0
+} OpKind;
+
+typedef struct {
+ int num_files;
+ goffset num_bytes;
+ int num_files_since_progress;
+ OpKind op;
+} SourceInfo;
+
+typedef struct {
+ int num_files;
+ goffset num_bytes;
+ OpKind op;
+ guint64 last_report_time;
+} TransferInfo;
+
+static void
+report_count_progress (CommonJob *job,
+ SourceInfo *source_info)
+{
+ char *s;
+
+ if (source_info->op == OP_KIND_COPY) {
+ s = f (_("Preparing to copy %d files (%S)"),
+ source_info->num_files, source_info->num_bytes);
+ nautilus_progress_info_take_details (job->progress, s);
+ }
+
+ nautilus_progress_info_pulse_progress (job->progress);
+}
+
+static void
+count_file (GFileInfo *info,
+ CommonJob *job,
+ SourceInfo *source_info)
+{
+ source_info->num_files += 1;
+ source_info->num_bytes += g_file_info_get_size (info);
+
+ if (source_info->num_files_since_progress++ > 100) {
+ report_count_progress (job, source_info);
+ source_info->num_files_since_progress = 0;
+ }
+
+}
+static void
+scan_dir (GFile *dir,
+ SourceInfo *source_info,
+ CommonJob *job,
+ GQueue *dirs)
+{
+ GFileInfo *info;
+ GError *error;
+ GFile *subdir;
+ GFileEnumerator *enumerator;
+ char *primary, *secondary, *details;
+ int response;
+ SourceInfo saved_info;
+
+ saved_info = *source_info;
+
+ retry:
+ error = NULL;
+ enumerator = g_file_enumerate_children (dir,
+ G_FILE_ATTRIBUTE_STD_NAME","
+ G_FILE_ATTRIBUTE_STD_TYPE","
+ G_FILE_ATTRIBUTE_STD_SIZE,
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ job->cancellable,
+ &error);
+ if (enumerator) {
+ error = NULL;
+ while ((info = g_file_enumerator_next_file (enumerator, job->cancellable, &error)) != NULL) {
+ count_file (info, job, source_info);
+
+ if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) {
+ subdir = g_file_get_child (dir,
+ g_file_info_get_name (info));
+
+ /* Push to head, since we want depth-first */
+ g_queue_push_head (dirs, subdir);
+ }
+
+ g_object_unref (info);
+ }
+ g_file_enumerator_close (enumerator, job->cancellable, NULL);
+
+ if (error) {
+ primary = f (_("Error while copying."));
+ details = NULL;
+
+ if (IS_IO_ERROR (error, PERMISSION_DENIED)) {
+ secondary = f (_("Files in the folder \"%B\" cannot be copied because you do "
+ "not have permissions to read them."), dir);
+ } else {
+ secondary = f (_("There was an error getting information about the files in the folder \"%B\"."), dir);
+ details = error->message;
+ }
+
+ response = run_warning (job,
+ primary,
+ secondary,
+ details,
+ GTK_STOCK_CANCEL, RETRY, SKIP,
+ NULL);
+
+ g_error_free (error);
+
+ if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
+ job->aborted = TRUE;
+ } else if (response == 1) {
+ *source_info = saved_info;
+ goto retry;
+ } else if (response == 2) {
+ skip_readdir_error (job, dir);
+ } else {
+ g_assert_not_reached ();
+ }
+ }
+
+ } else if (job->skip_all_error) {
+ skip_file (job, dir);
+ } else {
+ primary = f (_("Error while copying."));
+ details = NULL;
+
+ if (IS_IO_ERROR (error, PERMISSION_DENIED)) {
+ secondary = f (_("The folder \"%B\" cannot be copied because you do not have "
+ "permissions to read it."), dir);
+ } else {
+ secondary = f (_("There was an error reading the folder \"%B\"."), dir);
+ details = error->message;
+ }
+
+ response = run_warning (job,
+ primary,
+ secondary,
+ details,
+ GTK_STOCK_CANCEL, SKIP_ALL, SKIP, RETRY,
+ NULL);
+
+ g_error_free (error);
+
+ if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
+ job->aborted = TRUE;
+ } else if (response == 1 || response == 2) {
+ if (response == 1) {
+ job->skip_all_error = TRUE;
+ }
+ skip_file (job, dir);
+ } else if (response == 3) {
+ goto retry;
+ } else {
+ g_assert_not_reached ();
+ }
+ }
+}
+
+static void
+scan_file (GFile *file,
+ SourceInfo *source_info,
+ CommonJob *job)
+{
+ GFileInfo *info;
+ GError *error;
+ GQueue *dirs;
+ GFile *dir;
+ char *primary;
+ char *secondary;
+ char *details;
+ int response;
+
+ dirs = g_queue_new ();
+
+ retry:
+ error = NULL;
+ info = g_file_query_info (file,
+ G_FILE_ATTRIBUTE_STD_TYPE","
+ G_FILE_ATTRIBUTE_STD_SIZE,
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ job->cancellable,
+ &error);
+
+ if (info) {
+ count_file (info, job, source_info);
+
+ if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) {
+ g_queue_push_head (dirs, g_object_ref (file));
+ }
+
+ g_object_unref (info);
+ } else if (job->skip_all_error) {
+ skip_file (job, file);
+ } else {
+ primary = f (_("Error while copying."));
+ details = NULL;
+
+ if (IS_IO_ERROR (error, PERMISSION_DENIED)) {
+ secondary = f (_("The file \"%B\" cannot be copied because you do not have "
+ "permissions to read it."), file);
+ } else {
+ secondary = f (_("There was an error getting information about \"%B\"."), file);
+ details = error->message;
+ }
+
+ response = run_warning (job,
+ primary,
+ secondary,
+ details,
+ GTK_STOCK_CANCEL, SKIP_ALL, SKIP, RETRY,
+ NULL);
+
+ g_error_free (error);
+
+ if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
+ job->aborted = TRUE;
+ } else if (response == 1 || response == 2) {
+ if (response == 1) {
+ job->skip_all_error = TRUE;
+ }
+ skip_file (job, file);
+ } else if (response == 3) {
+ goto retry;
+ } else {
+ g_assert_not_reached ();
+ }
+ }
+
+ while (!job->aborted &&
+ (dir = g_queue_pop_head (dirs)) != NULL) {
+ scan_dir (dir, source_info, job, dirs);
+ g_object_unref (dir);
+ }
+
+ /* Free all from queue if we exited early */
+ g_queue_foreach (dirs, (GFunc)g_object_unref, NULL);
+ g_queue_free (dirs);
+}
+
+static void
+scan_sources (GList *files,
+ SourceInfo *source_info,
+ CommonJob *job)
+{
+ GList *l;
+ GFile *file;
+
+ if (source_info->op == OP_KIND_COPY) {
+ nautilus_progress_info_set_status (job->progress,
+ _("Preparing for copy"));
+ }
+
+ for (l = files; l != NULL && !job->aborted; l = l->next) {
+ file = l->data;
+
+ scan_file (file,
+ source_info,
+ job);
+ }
+
+ /* Make sure we report the final count */
+ report_count_progress (job, source_info);
+}
+
+static void
+verify_destination (CommonJob *job,
+ GFile *dest,
+ char **dest_fs_id,
+ goffset required_size)
+{
+ GFileInfo *info, *fsinfo;
+ GError *error;
+ guint64 free_size;
+ char *primary, *secondary, *details;
+ int response;
+ GFileType file_type;
+
+ *dest_fs_id = NULL;
+
+ retry:
+
+ error = NULL;
+ info = g_file_query_info (dest,
+ G_FILE_ATTRIBUTE_STD_TYPE","
+ G_FILE_ATTRIBUTE_ID_FS,
+ 0,
+ job->cancellable,
+ &error);
+
+ if (info == NULL) {
+ primary = f (_("Error while copying to \"%B\"."), dest);
+ details = NULL;
+
+ if (IS_IO_ERROR (error, PERMISSION_DENIED)) {
+ secondary = f (_("You don't have permissions to access the destination folder."));
+ } else {
+ secondary = f (_("There was an error getting information about the destination."));
+ details = error->message;
+ }
+
+ response = run_error (job,
+ primary,
+ secondary,
+ details,
+ GTK_STOCK_CANCEL, RETRY,
+ NULL);
+
+ g_error_free (error);
+
+ if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
+ job->aborted = TRUE;
+ } else if (response == 1) {
+ goto retry;
+ } else {
+ g_assert_not_reached ();
+ }
+
+ return;
+ }
+
+ file_type = g_file_info_get_file_type (info);
+
+ *dest_fs_id =
+ g_strdup (g_file_info_get_attribute_string (info,
+ G_FILE_ATTRIBUTE_ID_FS));
+
+ g_object_unref (info);
+
+ if (file_type != G_FILE_TYPE_DIRECTORY) {
+ primary = f (_("Error while copying to \"%B\"."), dest);
+ secondary = f (_("The destination is not a folder."));
+
+ response = run_error (job,
+ primary,
+ secondary,
+ NULL,
+ GTK_STOCK_CANCEL,
+ NULL);
+
+ g_error_free (error);
+
+ job->aborted = TRUE;
+ return;
+ }
+
+ fsinfo = g_file_query_filesystem_info (dest,
+ G_FILE_ATTRIBUTE_FS_FREE","
+ G_FILE_ATTRIBUTE_FS_READONLY,
+ job->cancellable,
+ NULL);
+ if (fsinfo == NULL) {
+ /* All sorts of things can go wrong getting the fs info (like not supported)
+ * only check these things if the fs returns them
+ */
+ return;
+ }
+
+ if (required_size > 0 &&
+ g_file_info_has_attribute (fsinfo, G_FILE_ATTRIBUTE_FS_FREE)) {
+ free_size = g_file_info_get_attribute_uint64 (fsinfo,
+ G_FILE_ATTRIBUTE_FS_FREE);
+
+ if (free_size < required_size) {
+ primary = f (_("Error while copying to \"%B\"."), dest);
+ secondary = _("There is not enough space on the destination. Try to remove files to make space.");
+
+ details = f (_("There is %S availible, but %S is required."), free_size, required_size);
+
+ response = run_warning (job,
+ primary,
+ secondary,
+ details,
+ GTK_STOCK_CANCEL, RETRY,
+ NULL);
+
+ if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
+ job->aborted = TRUE;
+ } else if (response == 1) {
+ goto retry;
+ } else {
+ g_assert_not_reached ();
+ }
+ }
+ }
+
+ if (!job->aborted &&
+ g_file_info_get_attribute_boolean (fsinfo,
+ G_FILE_ATTRIBUTE_FS_READONLY)) {
+ primary = f (_("Error while copying to \"%B\"."), dest);
+ secondary = f (_("The destination is read-only."));
+
+ response = run_error (job,
+ primary,
+ secondary,
+ NULL,
+ GTK_STOCK_CANCEL,
+ NULL);
+
+ g_error_free (error);
+
+ job->aborted = TRUE;
+ }
+
+ g_object_unref (fsinfo);
+}
+
+static void
+report_copy_progress (CopyMoveJob *copy_job,
+ SourceInfo *source_info,
+ TransferInfo *transfer_info)
+{
+ int files_left;
+ goffset total_size;
+ double elapsed, transfer_rate;
+ int remaining_time;
+ guint64 now;
+ CommonJob *job;
+ gboolean is_move;
+
+ job = (CommonJob *)copy_job;
+
+ is_move = copy_job->is_move;
+
+ now = g_thread_gettime ();
+
+ if (transfer_info->last_report_time != 0 &&
+ ABS (transfer_info->last_report_time - now) < 100 * NSEC_PER_MSEC) {
+ return;
+ }
+ transfer_info->last_report_time = now;
+
+ files_left = source_info->num_files - transfer_info->num_files;
+
+ /* Races and whatnot could cause this to be negative... */
+ if (files_left < 0) {
+ files_left = 1;
+ }
+
+ if (source_info->num_files == 1) {
+ nautilus_progress_info_take_status (job->progress,
+ f (is_move ?
+ _("Moving \"%B\" to \"%B\""):
+ _("Copying \"%B\" to \"%B\""),
+ (GFile *)copy_job->files->data,
+ copy_job->destination));
+ } else if (copy_job->files != NULL &&
+ copy_job->files->next == NULL) {
+ nautilus_progress_info_take_status (job->progress,
+ f (is_move?
+ _("Moving %d files (in \"%B\") to \"%B\""):
+ _("Copying %d files (in \"%B\") to \"%B\""),
+ files_left,
+ (GFile *)copy_job->files->data,
+ copy_job->destination));
+ } else {
+ nautilus_progress_info_take_status (job->progress,
+ f (is_move?
+ _("Moving %d files to \"%B\""):
+ _("Copying %d files to \"%B\""),
+ files_left, copy_job->destination));
+ }
+
+ total_size = MAX (source_info->num_bytes, transfer_info->num_bytes);
+
+ elapsed = g_timer_elapsed (job->time, NULL);
+ if (elapsed < SECONDS_NEEDED_FOR_RELIABLE_TRANSFER_RATE) {
+ char *s;
+ s = f (_("%S of %S"), transfer_info->num_bytes, total_size);
+ nautilus_progress_info_take_details (job->progress, s);
+ } else {
+ char *s;
+ transfer_rate = transfer_info->num_bytes / elapsed;
+ remaining_time = (total_size - transfer_info->num_bytes) / transfer_rate;
+
+ s = f (_("%S of %S \xE2\x80\x94 %T left (%S/sec)"),
+ transfer_info->num_bytes, total_size,
+ remaining_time,
+ (goffset)transfer_rate);
+ nautilus_progress_info_take_details (job->progress, s);
+ }
+
+ nautilus_progress_info_set_progress (job->progress, (double)transfer_info->num_bytes / total_size);
+}
+
+static GFile *
+get_target_file (GFile *src,
+ GFile *dest_dir,
+ gboolean same_fs)
+{
+ char *basename;
+ GFile *dest;
+ GFileInfo *info;
+ const char *copyname;
+
+ dest = NULL;
+ if (!same_fs) {
+ info = g_file_query_info (src,
+ G_FILE_ATTRIBUTE_STD_COPY_NAME,
+ 0, NULL, NULL);
+
+ if (info) {
+ copyname = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_STD_COPY_NAME);
+
+ if (copyname) {
+ dest = g_file_get_child_for_display_name (dest_dir, copyname, NULL);
+ }
+
+ g_object_unref (info);
+ }
+ }
+
+ if (dest == NULL) {
+ basename = g_file_get_basename (src);
+ dest = g_file_get_child (dest_dir, basename);
+ g_free (basename);
+ }
+
+ return dest;
+}
+
+static gboolean
+has_fs_id (GFile *file, const char *fs_id)
+{
+ const char *id;
+ GFileInfo *info;
+ gboolean res;
+
+ res = FALSE;
+ info = g_file_query_info (file,
+ G_FILE_ATTRIBUTE_ID_FS,
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ NULL, NULL);
+
+ if (info) {
+ id = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_ID_FS);
+
+ if (id && strcmp (id, fs_id) == 0) {
+ res = TRUE;
+ }
+
+ g_object_unref (info);
+ }
+
+ return res;
+}
+
+static gboolean
+is_dir (GFile *file)
+{
+ GFileInfo *info;
+ gboolean res;
+
+ res = FALSE;
+ info = g_file_query_info (file,
+ G_FILE_ATTRIBUTE_STD_TYPE,
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ NULL, NULL);
+ if (info) {
+ res = g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY;
+ g_object_unref (info);
+ }
+
+ return res;
+}
+
+static void copy_move_file (CopyMoveJob *job,
+ GFile *src,
+ GFile *dest_dir,
+ gboolean same_fs,
+ SourceInfo *source_info,
+ TransferInfo *transfer_info,
+ GHashTable *debuting_files,
+ GdkPoint *point,
+ gboolean overwrite,
+ gboolean *skipped_file);
+
+static gboolean
+create_dest_dir (CommonJob *job,
+ GFile *src,
+ GFile *dest)
+{
+ GError *error;
+ char *primary, *secondary, *details;
+ int response;
+
+ retry:
+ /* First create the directory, then copy stuff to it before
+ copying the attributes, because we need to be sure we can write to it */
+
+ error = NULL;
+ if (!g_file_make_directory (dest, job->cancellable, &error)) {
+ primary = f (_("Error while copying."));
+ details = NULL;
+
+ if (IS_IO_ERROR (error, PERMISSION_DENIED)) {
+ secondary = f (_("The folder \"%B\" cannot be copied because you do not have "
+ "permissions to create it in the destination."), src);
+ } else {
+ secondary = f (_("There was an error creating the folder \"%B\"."), src);
+ details = error->message;
+ }
+
+ response = run_warning (job,
+ primary,
+ secondary,
+ details,
+ GTK_STOCK_CANCEL, SKIP, RETRY,
+ NULL);
+
+ g_error_free (error);
+
+ if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
+ job->aborted = TRUE;
+ } else if (response == 1) {
+ /* Skip: Do Nothing */
+ } else if (response == 2) {
+ goto retry;
+ } else {
+ g_assert_not_reached ();
+ }
+ return FALSE;
+ }
+ nautilus_file_changes_queue_file_added (dest);
+ return TRUE;
+}
+
+static void
+copy_move_directory (CopyMoveJob *copy_job,
+ GFile *src,
+ GFile *dest,
+ gboolean same_fs,
+ gboolean create_dest,
+ SourceInfo *source_info,
+ TransferInfo *transfer_info,
+ GHashTable *debuting_files,
+ gboolean *skipped_file)
+{
+ GFileInfo *info;
+ GError *error;
+ GFile *src_file;
+ GFileEnumerator *enumerator;
+ char *primary, *secondary, *details;
+ int response;
+ gboolean skip_error;
+ gboolean local_skipped_file;
+ CommonJob *job;
+
+ job = (CommonJob *)copy_job;
+
+ if (create_dest) {
+ if (!create_dest_dir (job, src, dest)) {
+ *skipped_file = TRUE;
+ return;
+ }
+ if (debuting_files) {
+ g_hash_table_replace (debuting_files, g_object_ref (dest), GINT_TO_POINTER (TRUE));
+ }
+
+ }
+
+ local_skipped_file = FALSE;
+
+ skip_error = should_skip_readdir_error (job, src);
+ retry:
+ error = NULL;
+ enumerator = g_file_enumerate_children (src,
+ G_FILE_ATTRIBUTE_STD_NAME,
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ job->cancellable,
+ &error);
+ if (enumerator) {
+ error = NULL;
+
+ while (!job->aborted &&
+ (info = g_file_enumerator_next_file (enumerator, job->cancellable, skip_error?NULL:&error)) != NULL) {
+ src_file = g_file_get_child (src,
+ g_file_info_get_name (info));
+ copy_move_file (copy_job, src_file, dest, same_fs, source_info, transfer_info, NULL, NULL, FALSE, &local_skipped_file);
+ g_object_unref (info);
+ }
+ g_file_enumerator_close (enumerator, job->cancellable, NULL);
+
+ if (error) {
+ if (copy_job->is_move) {
+ primary = f (_("Error while moving."));
+ } else {
+ primary = f (_("Error while copying."));
+ }
+ details = NULL;
+
+ if (IS_IO_ERROR (error, PERMISSION_DENIED)) {
+ secondary = f (_("Files in the folder \"%B\" cannot be copied because you do "
+ "not have permissions to read them."), src);
+ } else {
+ secondary = f (_("There was an error getting information about the files in the folder \"%B\"."), src);
+ details = error->message;
+ }
+
+ response = run_warning (job,
+ primary,
+ secondary,
+ details,
+ GTK_STOCK_CANCEL, _("_Skip files"),
+ NULL);
+
+ g_error_free (error);
+
+ if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
+ job->aborted = TRUE;
+ } else if (response == 1) {
+ /* Skip: Do Nothing */
+ local_skipped_file = TRUE;
+ } else {
+ g_assert_not_reached ();
+ }
+ }
+
+ /* Count the copied directory as a file */
+ transfer_info->num_files ++;
+ report_copy_progress (copy_job, source_info, transfer_info);
+
+ if (debuting_files) {
+ g_hash_table_replace (debuting_files, g_object_ref (dest), GINT_TO_POINTER (create_dest));
+ }
+ } else {
+ if (copy_job->is_move) {
+ primary = f (_("Error while moving."));
+ } else {
+ primary = f (_("Error while copying."));
+ }
+ details = NULL;
+
+ if (IS_IO_ERROR (error, PERMISSION_DENIED)) {
+ secondary = f (_("The folder \"%B\" cannot be copied because you do not have "
+ "permissions to read it."), src);
+ } else {
+ secondary = f (_("There was an error reading the folder \"%B\"."), src);
+ details = error->message;
+ }
+
+ response = run_warning (job,
+ primary,
+ secondary,
+ details,
+ GTK_STOCK_CANCEL, SKIP, RETRY,
+ NULL);
+
+ g_error_free (error);
+
+ if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
+ job->aborted = TRUE;
+ } else if (response == 1) {
+ /* Skip: Do Nothing */
+ local_skipped_file = TRUE;
+ } else if (response == 2) {
+ goto retry;
+ } else {
+ g_assert_not_reached ();
+ }
+ }
+
+ if (create_dest) {
+ /* Ignore errors here. Failure to copy metadata is not a hard error */
+ g_file_copy_attributes (src, dest,
+ G_FILE_COPY_NOFOLLOW_SYMLINKS,
+ job->cancellable, NULL);
+ }
+
+ if (!job->aborted && copy_job->is_move &&
+ /* Don't delete source if there was a skipped file */
+ !local_skipped_file) {
+ if (!g_file_delete (src, job->cancellable, &error)) {
+ if (job->skip_all_error) {
+ goto skip;
+ }
+ primary = f (_("Error while moving \"%B\"."), src);
+ secondary = f (_("Couldn't remove the source folder."));
+ details = error->message;
+
+ response = run_warning (job,
+ primary,
+ secondary,
+ details,
+ GTK_STOCK_CANCEL, SKIP_ALL, SKIP,
+ NULL);
+
+ if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
+ job->aborted = TRUE;
+ } else if (response == 1) { /* skip all */
+ job->skip_all_error = TRUE;
+ local_skipped_file = TRUE;
+ } else if (response == 2) { /* skip */
+ local_skipped_file = TRUE;
+ } else {
+ g_assert_not_reached ();
+ }
+
+ skip:
+ g_error_free (error);
+ }
+ }
+
+ if (local_skipped_file) {
+ *skipped_file = TRUE;
+ }
+}
+
+static gboolean
+remove_target_recursively (CommonJob *job,
+ GFile *src,
+ GFile *toplevel_dest,
+ GFile *file)
+{
+ GFileEnumerator *enumerator;
+ GError *error;
+ GFile *child;
+ gboolean stop;
+ char *primary, *secondary, *details;
+ int response;
+ GFileInfo *info;
+
+ stop = FALSE;
+
+ error = NULL;
+ enumerator = g_file_enumerate_children (file,
+ G_FILE_ATTRIBUTE_STD_NAME,
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ job->cancellable,
+ &error);
+ if (enumerator) {
+ error = NULL;
+
+ while (!job->aborted &&
+ (info = g_file_enumerator_next_file (enumerator, job->cancellable, &error)) != NULL) {
+ child = g_file_get_child (file,
+ g_file_info_get_name (info));
+ if (!remove_target_recursively (job, src, toplevel_dest, child)) {
+ stop = TRUE;
+ break;
+ }
+ g_object_unref (info);
+ }
+ g_file_enumerator_close (enumerator, job->cancellable, NULL);
+
+ } else if (IS_IO_ERROR (error, NOT_DIRECTORY)) {
+ /* Not a dir, continue */
+ g_error_free (error);
+
+ } else {
+ if (job->skip_all_error) {
+ goto skip1;
+ }
+
+ primary = f (_("Error while copying \"%B\"."), src);
+ secondary = f (_("Couldn't remove files from the already folder %F."), file);
+ details = error->message;
+
+ response = run_warning (job,
+ primary,
+ secondary,
+ details,
+ GTK_STOCK_CANCEL, SKIP_ALL, SKIP,
+ NULL);
+
+ if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
+ job->aborted = TRUE;
+ } else if (response == 1) { /* skip all */
+ job->skip_all_error = TRUE;
+ } else if (response == 2) { /* skip */
+ /* do nothing */
+ } else {
+ g_assert_not_reached ();
+ }
+ skip1:
+ g_error_free (error);
+
+ stop = TRUE;
+ }
+
+ if (stop) {
+ return FALSE;
+ }
+
+ error = NULL;
+
+ if (!g_file_delete (file, job->cancellable, &error)) {
+ if (job->skip_all_error) {
+ goto skip2;
+ }
+ primary = f (_("Error while copying \"%B\"."), src);
+ secondary = f (_("Couldn't remove the already existing file %F."), file);
+ details = error->message;
+
+ response = run_warning (job,
+ primary,
+ secondary,
+ details,
+ GTK_STOCK_CANCEL, SKIP_ALL, SKIP,
+ NULL);
+
+ if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
+ job->aborted = TRUE;
+ } else if (response == 1) { /* skip all */
+ job->skip_all_error = TRUE;
+ } else if (response == 2) { /* skip */
+ /* do nothing */
+ } else {
+ g_assert_not_reached ();
+ }
+
+ skip2:
+ g_error_free (error);
+
+ return FALSE;
+ }
+ nautilus_file_changes_queue_file_removed (file);
+ nautilus_file_changes_queue_schedule_metadata_remove (file);
+
+ return TRUE;
+
+}
+
+typedef struct {
+ CopyMoveJob *job;
+ goffset last_size;
+ SourceInfo *source_info;
+ TransferInfo *transfer_info;
+} ProgressData;
+
+static void
+copy_file_progress_callback (goffset current_num_bytes,
+ goffset total_num_bytes,
+ gpointer user_data)
+{
+ ProgressData *pdata;
+ goffset new_size;
+
+ pdata = user_data;
+
+ new_size = current_num_bytes - pdata->last_size;
+
+ if (new_size > 0) {
+ pdata->transfer_info->num_bytes += new_size;
+ pdata->last_size = current_num_bytes;
+ report_copy_progress (pdata->job,
+ pdata->source_info,
+ pdata->transfer_info);
+ }
+}
+
+/* Debuting files is non-NULL only for toplevel items */
+static void
+copy_move_file (CopyMoveJob *copy_job,
+ GFile *src,
+ GFile *dest_dir,
+ gboolean same_fs,
+ SourceInfo *source_info,
+ TransferInfo *transfer_info,
+ GHashTable *debuting_files,
+ GdkPoint *position,
+ gboolean overwrite,
+ gboolean *skipped_file)
+{
+ GFile *dest;
+ GError *error;
+ GFileCopyFlags flags;
+ char *primary, *secondary, *details;
+ int response;
+ ProgressData pdata;
+ gboolean would_recurse;
+ CommonJob *job;
+ gboolean res;
+
+ job = (CommonJob *)copy_job;
+
+ if (should_skip_file (job, src)) {
+ *skipped_file = TRUE;
+ return;
+ }
+
+ dest = get_target_file (src, dest_dir, same_fs);
+
+ retry:
+
+ error = NULL;
+ flags = G_FILE_COPY_NOFOLLOW_SYMLINKS;
+ if (overwrite) {
+ flags |= G_FILE_COPY_OVERWRITE;
+ }
+ pdata.job = copy_job;
+ pdata.last_size = 0;
+ pdata.source_info = source_info;
+ pdata.transfer_info = transfer_info;
+
+ if (copy_job->is_move) {
+ res = g_file_move (src, dest,
+ flags,
+ job->cancellable,
+ copy_file_progress_callback,
+ &pdata,
+ &error);
+ } else {
+ res = g_file_copy (src, dest,
+ flags,
+ job->cancellable,
+ copy_file_progress_callback,
+ &pdata,
+ &error);
+ }
+
+ if (res) {
+ transfer_info->num_files ++;
+ report_copy_progress (copy_job, source_info, transfer_info);
+
+ if (debuting_files) {
+ nautilus_file_changes_queue_schedule_metadata_copy (src, dest);
+ if (position) {
+ nautilus_file_changes_queue_schedule_position_set (dest, *position, copy_job->screen_num);
+ } else {
+ nautilus_file_changes_queue_schedule_position_remove (dest);
+ }
+
+ g_hash_table_replace (debuting_files, g_object_ref (dest), GINT_TO_POINTER (TRUE));
+ }
+ nautilus_file_changes_queue_file_added (dest);
+ g_object_unref (dest);
+ return;
+ }
+
+ /* Conflict */
+ if (!overwrite &&
+ IS_IO_ERROR (error, EXISTS)) {
+ gboolean is_merge;
+
+ is_merge = FALSE;
+ if (is_dir (dest)) {
+ if (is_dir (src)) {
+ is_merge = TRUE;
+ primary = f (_("A folder named \"%B\" already exists. Do you want to merge the source folder?"),
+ dest);
+ secondary = f (_("The source folder already exists in \"%B\". "
+ "Merging will ask for confirmation before replacing any files in the folder that conflict with the files being copied."),
+ dest_dir);
+
+ } else {
+ primary = f (_("A folder named \"%B\" already exists. Do you want to replace it?"),
+ dest);
+ secondary = f (_("The folder already exists in \"%F\". "
+ "Replacing it will remove all files in the folder."),
+ dest_dir);
+ }
+ } else {
+ primary = f (_("A file named \"%B\" already exists. Do you want to replace it?"),
+ dest);
+ secondary = f (_("The file already exists in \"%F\". "
+ "Replacing it will overwrite its content."),
+ dest_dir);
+ }
+
+ if ((is_merge && job->merge_all) ||
+ (!is_merge && job->replace_all)) {
+ g_free (primary);
+ g_free (secondary);
+ g_error_free (error);
+
+ overwrite = TRUE;
+ goto retry;
+ }
+
+ if (job->skip_all_conflict) {
+ g_free (primary);
+ g_free (secondary);
+ g_error_free (error);
+
+ goto out;
+ }
+
+ response = run_warning (job,
+ primary,
+ secondary,
+ NULL,
+ GTK_STOCK_CANCEL,
+ SKIP_ALL,
+ is_merge?MERGE_ALL:REPLACE_ALL,
+ SKIP,
+ is_merge?MERGE:REPLACE,
+ NULL);
+
+ g_error_free (error);
+
+ if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
+ job->aborted = TRUE;
+ } else if (response == 1 || response == 3) { /* skip all / skip */
+ if (response == 1) {
+ job->skip_all_conflict = TRUE;
+ }
+ } else if (response == 2 || response == 4) { /* merge/replace all / merge/replace*/
+ if (response == 2) {
+ if (is_merge) {
+ job->merge_all = TRUE;
+ } else {
+ job->replace_all = TRUE;
+ }
+ }
+ overwrite = TRUE;
+ goto retry;
+ } else {
+ g_assert_not_reached ();
+ }
+ }
+
+ else if (overwrite &&
+ IS_IO_ERROR (error, IS_DIRECTORY)) {
+
+ g_error_free (error);
+
+ if (remove_target_recursively (job, src, dest, dest)) {
+ goto retry;
+ }
+ }
+
+ /* Needs to recurse */
+ else if (IS_IO_ERROR (error, WOULD_RECURSE) ||
+ IS_IO_ERROR (error, WOULD_MERGE)) {
+ would_recurse = error->code == G_IO_ERROR_WOULD_RECURSE;
+ g_error_free (error);
+
+ if (overwrite && would_recurse) {
+ error = NULL;
+
+ /* Copying a dir onto file, first remove the file */
+ if (!g_file_delete (dest, job->cancellable, &error) &&
+ !IS_IO_ERROR (error, NOT_FOUND)) {
+ if (job->skip_all_error) {
+ goto out;
+ }
+ if (copy_job->is_move) {
+ primary = f (_("Error while moving \"%B\"."), src);
+ } else {
+ primary = f (_("Error while copying \"%B\"."), src);
+ }
+ secondary = f (_("Couldn't remove the already existing file with the same name in %F."), dest_dir);
+ details = error->message;
+
+ response = run_warning (job,
+ primary,
+ secondary,
+ details,
+ GTK_STOCK_CANCEL, SKIP_ALL, SKIP,
+ NULL);
+
+ g_error_free (error);
+
+ if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
+ job->aborted = TRUE;
+ } else if (response == 1) { /* skip all */
+ job->skip_all_error = TRUE;
+ } else if (response == 2) { /* skip */
+ /* do nothing */
+ } else {
+ g_assert_not_reached ();
+ }
+ goto out;
+
+ }
+ if (error) {
+ g_error_free (error);
+ error = NULL;
+ }
+ if (debuting_files) { /* Only remove metadata for toplevel items */
+ nautilus_file_changes_queue_schedule_metadata_remove (dest);
+ }
+ nautilus_file_changes_queue_file_removed (dest);
+ }
+
+ copy_move_directory (copy_job, src, dest, same_fs,
+ would_recurse,
+ source_info, transfer_info,
+ debuting_files, skipped_file);
+
+ g_object_unref (dest);
+ return;
+ }
+
+ /* Other error */
+ else {
+ if (job->skip_all_error) {
+ goto out;
+ }
+ primary = f (_("Error while copying \"%B\"."), src);
+ secondary = f (_("There was an error copying the file into %F."), dest_dir);
+ details = error->message;
+
+ response = run_warning (job,
+ primary,
+ secondary,
+ details,
+ GTK_STOCK_CANCEL, SKIP_ALL, SKIP,
+ NULL);
+
+ g_error_free (error);
+
+ if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
+ job->aborted = TRUE;
+ } else if (response == 1) { /* skip all */
+ job->skip_all_error = TRUE;
+ } else if (response == 2) { /* skip */
+ /* do nothing */
+ } else {
+ g_assert_not_reached ();
+ }
+ }
+ out:
+ *skipped_file = TRUE; /* Or aborted, but same-same */
+ g_object_unref (dest);
+}
+
+static void
+copy_files (CopyMoveJob *job,
+ const char *dest_fs_id,
+ SourceInfo *source_info,
+ TransferInfo *transfer_info)
+{
+ CommonJob *common;
+ GList *l;
+ GFile *src;
+ gboolean same_fs;
+ int i;
+ GdkPoint *point;
+ gboolean skipped_file;
+
+ common = &job->common;
+
+ report_copy_progress (job, source_info, transfer_info);
+
+ i = 0;
+ for (l = job->files;
+ l != NULL && !common->aborted ;
+ l = l->next) {
+ src = l->data;
+
+ if (i < job->n_icon_positions) {
+ point = &job->icon_positions[i];
+ } else {
+ point = NULL;
+ }
+
+
+ same_fs = FALSE;
+ if (dest_fs_id) {
+ same_fs = has_fs_id (src, dest_fs_id);
+ }
+
+ skipped_file = FALSE;
+ copy_move_file (job, src, job->destination,
+ same_fs,
+ source_info, transfer_info,
+ job->debuting_files,
+ point, FALSE, &skipped_file);
+ i++;
+ }
+}
+
+static void
+copy_job_done (gpointer user_data)
+{
+ CopyMoveJob *job;
+
+ job = user_data;
+ if (job->done_callback) {
+ job->done_callback (job->debuting_files, job->done_callback_data);
+ }
+
+ eel_g_object_list_free (job->files);
+ g_object_unref (job->destination);
+ g_hash_table_unref (job->debuting_files);
+ g_free (job->icon_positions);
+
+ finalize_common ((CommonJob *)job);
+
+ nautilus_file_changes_consume_changes (TRUE);
+}
+
+static void
+copy_job (GIOJob *io_job,
+ GCancellable *cancellable,
+ gpointer user_data)
+{
+ CopyMoveJob *job;
+ CommonJob *common;
+ SourceInfo source_info;
+ TransferInfo transfer_info;
+ char *dest_fs_id;
+
+ job = user_data;
+ common = &job->common;
+ common->io_job = io_job;
+
+ dest_fs_id = NULL;
+
+ nautilus_progress_info_start (job->common.progress);
+
+ memset (&source_info, 0, sizeof (SourceInfo));
+ scan_sources (job->files,
+ &source_info,
+ common);
+ if (common->aborted) {
+ goto aborted;
+ }
+
+ verify_destination (&job->common,
+ job->destination,
+ &dest_fs_id,
+ source_info.num_bytes);
+ if (common->aborted) {
+ goto aborted;
+ }
+
+ g_timer_start (job->common.time);
+
+ memset (&transfer_info, 0, sizeof (transfer_info));
+ copy_files (job,
+ dest_fs_id,
+ &source_info, &transfer_info);
+
+ aborted:
+
+ g_free (dest_fs_id);
+
+ g_io_job_send_to_mainloop (io_job,
+ copy_job_done,
+ job,
+ NULL,
+ FALSE);
+}
+
+void
+nautilus_file_operations_copy (GList *files,
+ GArray *relative_item_points,
+ GFile *target_dir,
+ GtkWindow *parent_window,
+ NautilusCopyCallback done_callback,
+ gpointer done_callback_data)
+{
+ CopyMoveJob *job;
+ GdkScreen *screen;
+
+ job = op_job_new (CopyMoveJob, parent_window);
+ job->done_callback = done_callback;
+ job->done_callback_data = done_callback_data;
+ job->files = eel_g_object_list_copy (files);
+ job->destination = g_object_ref (target_dir);
+ if (relative_item_points != NULL &&
+ relative_item_points->len > 0) {
+ job->icon_positions =
+ g_memdup (relative_item_points->data,
+ sizeof (GdkPoint) * relative_item_points->len);
+ job->n_icon_positions = relative_item_points->len;
+ }
+ job->screen_num = 0;
+ if (parent_window) {
+ screen = gtk_widget_get_screen (GTK_WIDGET (parent_window));
+ job->screen_num = gdk_screen_get_number (screen);
+ }
+ job->debuting_files = g_hash_table_new_full (g_file_hash, (GEqualFunc)g_file_equal, g_object_unref, NULL);
+
+ g_schedule_io_job (copy_job,
+ job,
+ NULL, /* destroy notify */
+ 0,
+ job->common.cancellable);
+}
+
+static void
+report_move_progress (CopyMoveJob *move_job, int total, int left)
+{
+ CommonJob *job;
+
+ job = (CommonJob *)move_job;
+
+ nautilus_progress_info_take_status (job->progress,
+ f (_("Preparing to Move to \"%B\""),
+ move_job->destination));
+
+ nautilus_progress_info_take_details (job->progress,
+ f (_("Preparing to move %d files"), left));
+
+ nautilus_progress_info_pulse_progress (job->progress);
+}
+
+static void
+move_file_prepare (CopyMoveJob *move_job,
+ GFile *src,
+ GFile *dest_dir,
+ gboolean same_fs,
+ GHashTable *debuting_files,
+ GdkPoint *position,
+ GList **copy_files,
+ GArray *copy_positions)
+{
+ GFile *dest;
+ GError *error;
+ CommonJob *job;
+ gboolean overwrite;
+ char *primary, *secondary, *details;
+ int response;
+ GFileCopyFlags flags;
+ gboolean would_recurse;
+
+ overwrite = FALSE;
+
+ job = (CommonJob *)move_job;
+
+ dest = get_target_file (src, dest_dir, same_fs);
+
+ retry:
+
+ flags = G_FILE_COPY_NOFOLLOW_SYMLINKS | G_FILE_COPY_NO_FALLBACK_FOR_MOVE;
+ if (overwrite) {
+ flags |= G_FILE_COPY_OVERWRITE;
+ }
+
+ error = NULL;
+ if (g_file_move (src, dest,
+ flags,
+ job->cancellable,
+ NULL,
+ NULL,
+ &error)) {
+
+ if (debuting_files) {
+ g_hash_table_replace (debuting_files, g_object_ref (dest), GINT_TO_POINTER (TRUE));
+ }
+
+ nautilus_file_changes_queue_file_moved (src, dest);
+ nautilus_file_changes_queue_schedule_metadata_move (src, dest);
+
+ return;
+ }
+
+ /* Conflict */
+ if (!overwrite &&
+ IS_IO_ERROR (error, EXISTS)) {
+ gboolean is_merge;
+
+ g_error_free (error);
+
+ is_merge = FALSE;
+ if (is_dir (dest)) {
+ if (is_dir (src)) {
+ is_merge = TRUE;
+ primary = f (_("A folder named \"%B\" already exists. Do you want to merge the source folder?"),
+ dest);
+ secondary = f (_("The source folder already exists in \"%B\". "
+ "Merging will ask for confirmation before replacing any files in the folder that conflict with the files being moved."),
+ dest_dir);
+
+ } else {
+ primary = f (_("A folder named \"%B\" already exists. Do you want to replace it?"),
+ dest);
+ secondary = f (_("The folder already exists in \"%F\". "
+ "Replacing it will remove all files in the folder."),
+ dest_dir);
+ }
+ } else {
+ primary = f (_("A file named \"%B\" already exists. Do you want to replace it?"),
+ dest);
+ secondary = f (_("The file already exists in \"%F\". "
+ "Replacing it will overwrite its content."),
+ dest_dir);
+ }
+
+ if ((is_merge && job->merge_all) ||
+ (!is_merge && job->replace_all)) {
+ g_free (primary);
+ g_free (secondary);
+ g_error_free (error);
+
+ overwrite = TRUE;
+ goto retry;
+ }
+
+ if (job->skip_all_conflict) {
+ g_free (primary);
+ g_free (secondary);
+ g_error_free (error);
+
+ goto out;
+ }
+
+ response = run_warning (job,
+ primary,
+ secondary,
+ NULL,
+ GTK_STOCK_CANCEL,
+ SKIP_ALL,
+ is_merge?MERGE_ALL:REPLACE_ALL,
+ SKIP,
+ is_merge?MERGE:REPLACE,
+ NULL);
+
+ g_error_free (error);
+
+ if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
+ job->aborted = TRUE;
+ } else if (response == 1 || response == 3) { /* skip all / skip */
+ if (response == 1) {
+ job->skip_all_conflict = TRUE;
+ }
+ } else if (response == 2 || response == 4) { /* merge/replace all / merge/replace*/
+ if (response == 2) {
+ if (is_merge) {
+ job->merge_all = TRUE;
+ } else {
+ job->replace_all = TRUE;
+ }
+ }
+ overwrite = TRUE;
+ goto retry;
+ } else {
+ g_assert_not_reached ();
+ }
+ }
+
+ else if (IS_IO_ERROR (error, WOULD_RECURSE) ||
+ IS_IO_ERROR (error, WOULD_MERGE) ||
+ IS_IO_ERROR (error, NOT_SUPPORTED)) {
+ gboolean delete_dest;
+ would_recurse = error->code == G_IO_ERROR_WOULD_RECURSE;
+ g_error_free (error);
+
+ delete_dest = FALSE;
+ if (overwrite && would_recurse) {
+ delete_dest = TRUE;
+
+ error = NULL;
+ }
+
+ *copy_files = g_list_prepend (*copy_files, src);
+ if (position) {
+ g_array_append_val (copy_positions, *position);
+ }
+ }
+
+ /* Other error */
+ else {
+ if (job->skip_all_error) {
+ goto out;
+ }
+ primary = f (_("Error while moving \"%B\"."), src);
+ secondary = f (_("There was an error moving the file into %F."), dest_dir);
+ details = error->message;
+
+ response = run_warning (job,
+ primary,
+ secondary,
+ details,
+ GTK_STOCK_CANCEL, SKIP_ALL, SKIP,
+ NULL);
+
+ g_error_free (error);
+
+ if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
+ job->aborted = TRUE;
+ } else if (response == 1) { /* skip all */
+ job->skip_all_error = TRUE;
+ } else if (response == 2) { /* skip */
+ /* do nothing */
+ } else {
+ g_assert_not_reached ();
+ }
+ }
+
+ out:
+ g_object_unref (dest);
+}
+
+static void
+move_files_prepare (CopyMoveJob *job,
+ const char *dest_fs_id,
+ GList **copy_files,
+ GArray *copy_positions)
+{
+ CommonJob *common;
+ GList *l;
+ GFile *src;
+ gboolean same_fs;
+ int i;
+ GdkPoint *point;
+ int total, left;
+
+ common = &job->common;
+
+ total = left = g_list_length (job->files);
+
+ report_move_progress (job, total, left);
+
+ i = 0;
+ for (l = job->files;
+ l != NULL && !common->aborted ;
+ l = l->next) {
+ src = l->data;
+
+ if (i < job->n_icon_positions) {
+ point = &job->icon_positions[i];
+ } else {
+ point = NULL;
+ }
+
+
+ same_fs = FALSE;
+ if (dest_fs_id) {
+ same_fs = has_fs_id (src, dest_fs_id);
+ }
+
+ move_file_prepare (job, src, job->destination,
+ same_fs,
+ job->debuting_files,
+ point,
+ copy_files,
+ copy_positions);
+ i++;
+ }
+
+}
+
+static void
+move_files (CopyMoveJob *job,
+ GList *files,
+ GArray *icon_positions,
+ const char *dest_fs_id,
+ SourceInfo *source_info,
+ TransferInfo *transfer_info)
+{
+ CommonJob *common;
+ GList *l;
+ GFile *src;
+ gboolean same_fs;
+ int i;
+ GdkPoint *point;
+ gboolean skipped_file;
+
+ common = &job->common;
+
+ report_copy_progress (job, source_info, transfer_info);
+
+ i = 0;
+ for (l = files;
+ l != NULL && !common->aborted ;
+ l = l->next) {
+ src = l->data;
+
+ if (i < icon_positions->len) {
+ point = &g_array_index (icon_positions, GdkPoint, i);
+ } else {
+ point = NULL;
+ }
+
+ same_fs = FALSE;
+ if (dest_fs_id) {
+ same_fs = has_fs_id (src, dest_fs_id);
+ }
+
+ /* Set overwrite to true, as the user has
+ selected overwrite on all toplevel items */
+ skipped_file = FALSE;
+ copy_move_file (job, src, job->destination,
+ same_fs,
+ source_info, transfer_info,
+ job->debuting_files,
+ point, TRUE, &skipped_file);
+ i++;
+ }
+}
+
+
+static void
+move_job_done (gpointer user_data)
+{
+ CopyMoveJob *job;
+
+ job = user_data;
+ if (job->done_callback) {
+ job->done_callback (job->debuting_files, job->done_callback_data);
+ }
+
+ eel_g_object_list_free (job->files);
+ g_object_unref (job->destination);
+ g_hash_table_unref (job->debuting_files);
+ g_free (job->icon_positions);
+
+ finalize_common ((CommonJob *)job);
+
+ nautilus_file_changes_consume_changes (TRUE);
+}
+
+static void
+move_job (GIOJob *io_job,
+ GCancellable *cancellable,
+ gpointer user_data)
+{
+ CopyMoveJob *job;
+ CommonJob *common;
+ GList *copy_files;
+ GArray *copy_positions;
+ SourceInfo source_info;
+ TransferInfo transfer_info;
+ char *dest_fs_id;
+
+ job = user_data;
+ common = &job->common;
+ common->io_job = io_job;
+
+ dest_fs_id = NULL;
+
+ copy_files = NULL;
+ copy_positions = NULL;
+
+ nautilus_progress_info_start (job->common.progress);
+
+ verify_destination (&job->common,
+ job->destination,
+ &dest_fs_id,
+ -1);
+ if (common->aborted) {
+ goto aborted;
+ }
+
+ copy_files = NULL;
+ copy_positions = g_array_new (FALSE, TRUE, sizeof (GdkPoint));
+
+ /* This moves all files that we can do without copy + delete */
+ move_files_prepare (job, dest_fs_id, &copy_files, copy_positions);
+ if (common->aborted) {
+ goto aborted;
+ }
+
+ copy_files = g_list_reverse (copy_files);
+
+ /* The rest we need to do deep copy + delete behind on,
+ so scan for size */
+
+ memset (&source_info, 0, sizeof (SourceInfo));
+ scan_sources (copy_files,
+ &source_info,
+ common);
+ if (common->aborted) {
+ goto aborted;
+ }
+
+ verify_destination (&job->common,
+ job->destination,
+ &dest_fs_id,
+ source_info.num_bytes);
+ if (common->aborted) {
+ goto aborted;
+ }
+
+ memset (&transfer_info, 0, sizeof (transfer_info));
+ move_files (job,
+ copy_files,
+ copy_positions,
+ dest_fs_id,
+ &source_info, &transfer_info);
+
+ aborted:
+ g_list_free (copy_files);
+ if (copy_positions) {
+ g_array_free (copy_positions, TRUE);
+ }
+
+ g_free (dest_fs_id);
+
+ g_io_job_send_to_mainloop (io_job,
+ move_job_done,
+ job,
+ NULL,
+ FALSE);
+}
+
+void
+nautilus_file_operations_move (GList *files,
+ GArray *relative_item_points,
+ GFile *target_dir,
+ GtkWindow *parent_window,
+ NautilusCopyCallback done_callback,
+ gpointer done_callback_data)
+{
+ CopyMoveJob *job;
+ GdkScreen *screen;
+
+ job = op_job_new (CopyMoveJob, parent_window);
+ job->is_move = TRUE;
+ job->done_callback = done_callback;
+ job->done_callback_data = done_callback_data;
+ job->files = eel_g_object_list_copy (files);
+ job->destination = g_object_ref (target_dir);
+ if (relative_item_points != NULL &&
+ relative_item_points->len > 0) {
+ job->icon_positions =
+ g_memdup (relative_item_points->data,
+ sizeof (GdkPoint) * relative_item_points->len);
+ job->n_icon_positions = relative_item_points->len;
+ }
+ job->screen_num = 0;
+ if (parent_window) {
+ screen = gtk_widget_get_screen (GTK_WIDGET (parent_window));
+ job->screen_num = gdk_screen_get_number (screen);
+ }
+ job->debuting_files = g_hash_table_new_full (g_file_hash, (GEqualFunc)g_file_equal, g_object_unref, NULL);
+
+ g_schedule_io_job (move_job,
+ job,
+ NULL, /* destroy notify */
+ 0,
+ job->common.cancellable);
+}
+
+
+
+static void
+not_supported_yet (void)
+{
+ eel_show_warning_dialog ("This operation isn't supported with the gio-based nautilus",
+ "This is work in progress. Please be patient",
+ NULL);
+}
+
+void
+nautilus_file_set_permissions_recursive (const char *directory,
+ guint32 file_permissions,
+ guint32 file_mask,
+ guint32 dir_permissions,
+ guint32 dir_mask,
+ NautilusSetPermissionsCallback callback,
+ gpointer callback_data)
+{
+ /* TODO-gio: Implement */
+ not_supported_yet ();
+}
+
+static GList *
+location_list_from_uri_list (const GList *uris)
+{
+ const GList *l;
+ GList *files;
+ GFile *f;
+
+ files = NULL;
+ for (l = uris; l != NULL; l = l->next) {
+ f = g_file_new_for_uri (l->data);
+ files = g_list_prepend (files, f);
+ }
+
+ return g_list_reverse (files);
+}
+
+void
+nautilus_file_operations_copy_move (const GList *item_uris,
+ GArray *relative_item_points,
+ const char *target_dir,
+ GdkDragAction copy_action,
+ GtkWidget *parent_view,
+ NautilusCopyCallback done_callback,
+ gpointer done_callback_data)
+{
+ GList *locations;
+ GFile *dest;
+ GtkWindow *parent_window;
+
+ dest = g_file_new_for_uri (target_dir);
+ locations = location_list_from_uri_list (item_uris);
+
+ parent_window = NULL;
+ if (parent_view) {
+ parent_window = (GtkWindow *)gtk_widget_get_ancestor (parent_view, GTK_TYPE_WINDOW);
+ }
+
+ if (copy_action == GDK_ACTION_COPY) {
+ nautilus_file_operations_copy (locations,
+ relative_item_points,
+ dest,
+ parent_window,
+ done_callback, done_callback_data);
+
+ } else if (copy_action == GDK_ACTION_MOVE) {
+
+ if (g_file_has_uri_scheme (dest, "trash")) {
+ nautilus_file_operations_trash_or_delete (locations,
+ parent_window,
+ done_callback, done_callback_data);
+ } else {
+ nautilus_file_operations_move (locations,
+ relative_item_points,
+ dest,
+ parent_window,
+ done_callback, done_callback_data);
+ }
+ } else {
+ /* TODO-gio: Implement link */
+ not_supported_yet ();
+ }
+
+ eel_g_object_list_free (locations);
+ g_object_unref (dest);
+}
+
+void
+nautilus_file_operations_new_folder (GtkWidget *parent_view,
+ GdkPoint *target_point,
+ const char *parent_dir,
+ NautilusNewFolderCallback done_callback,
+ gpointer data)
+{
+ /* TODO-gio: Implement */
+ not_supported_yet ();
+}
+
+void
+nautilus_file_operations_new_file_from_template (GtkWidget *parent_view,
+ GdkPoint *target_point,
+ const char *parent_dir,
+ const char *target_filename,
+ const char *template_uri,
+ NautilusNewFileCallback done_callback,
+ gpointer data)
+{
+ /* TODO-gio: Implement */
+ not_supported_yet ();
+}
+
+void
+nautilus_file_operations_new_file (GtkWidget *parent_view,
+ GdkPoint *target_point,
+ const char *parent_dir,
+ const char *initial_contents,
+ NautilusNewFileCallback done_callback,
+ gpointer data)
+{
+ /* TODO-gio: Implement */
+ not_supported_yet ();
+}
+
+
+void
+nautilus_file_operations_empty_trash (GtkWidget *parent_view)
+{
+ /* TODO-gio: Implement */
+ not_supported_yet ();
+}
+
#if !defined (NAUTILUS_OMIT_SELF_CHECK)
@@ -3411,6 +6342,8 @@ nautilus_self_check_file_operations (void)
{
setlocale (LC_MESSAGES, "C");
+#ifdef GIO_CONVERSION_DONE
+
/* test the next duplicate name generator */
EEL_CHECK_STRING_RESULT (get_duplicate_name (" (copy)", 1), " (another copy)");
EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo", 1), "foo (copy)");
@@ -3454,6 +6387,8 @@ nautilus_self_check_file_operations (void)
EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (123rd copy)", 1), "foo (124th copy)");
EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (123rd copy).txt", 1), "foo (124th copy).txt");
+#endif /* GIO_CONVERSION_DONE */
+
setlocale (LC_MESSAGES, "");
}