[PATCH] Improve Nautilus XFer code



Nautilus used to handle all the error handling in its asynchronous
gnome_vfs_async_xfer callback, but it is not guaranteed to be always
called, so problems may remain unhandled and cause data loss ([1]).
[2] has some details.

This patch still needs some serious testing.

[1] http://bugzilla.gnome.org/show_bug.cgi?id=144726
[2] http://blogs.gnome.org/edit/cneumair/2006/01/10

-- 
Christian Neumair <chris gnome-de org>
Index: libnautilus-private/nautilus-file-operations.c
===================================================================
RCS file: /cvs/gnome/nautilus/libnautilus-private/nautilus-file-operations.c,v
retrieving revision 1.203
diff -u -p -r1.203 nautilus-file-operations.c
--- libnautilus-private/nautilus-file-operations.c	18 Dec 2005 01:21:18 -0000	1.203
+++ libnautilus-private/nautilus-file-operations.c	10 Jan 2006 20:26:33 -0000
@@ -59,24 +59,28 @@
 #include "nautilus-link.h"
 #include "nautilus-trash-monitor.h"
 
-typedef enum   TransferKind         TransferKind;
-typedef struct TransferInfo         TransferInfo;
+typedef enum   OperationKind        OperationKind;
+typedef struct OperationInfo        OperationInfo;
 typedef struct IconPositionIterator IconPositionIterator;
 
-enum TransferKind {
+static void icon_position_iterator_free (IconPositionIterator *position_iterator);
+
+enum OperationKind {
 	TRANSFER_MOVE,
 	TRANSFER_COPY,
 	TRANSFER_DUPLICATE,
 	TRANSFER_MOVE_TO_TRASH,
 	TRANSFER_EMPTY_TRASH,
 	TRANSFER_DELETE,
-	TRANSFER_LINK
+	TRANSFER_LINK,
+	CREATE_FOLDER,
+	CREATE_FILE
 };
 
 /* Copy engine callback state */
-struct TransferInfo {
+struct OperationInfo {
+	/* basic info */
 	GnomeVFSAsyncHandle *handle;
-	NautilusFileOperationsProgress *progress_dialog;
 	const char *operation_title;	/* "Copying files" */
 	const char *action_label;	/* "Files copied:" */
 	const char *progress_verb;	/* "Copying" */
@@ -85,41 +89,47 @@ struct TransferInfo {
 	GnomeVFSXferErrorMode error_mode;
 	GnomeVFSXferOverwriteMode overwrite_mode;
 	GtkWidget *parent_view;
-	TransferKind kind;
-	void (* done_callback) (GHashTable *debuting_uris, gpointer data);
-	gpointer done_callback_data;
+	OperationKind kind;
+	void (* done_callback) (GHashTable *debuting_uris, gpointer user_data);
+	gpointer done_callback_user_data;
 	GHashTable *debuting_uris;
 	gboolean cancelled;	
+	NautilusFileOperationsProgress *progress_dialog;
 	IconPositionIterator *iterator;
+	int last_sync_response;
 };
 
-static TransferInfo *
-transfer_info_new (GtkWidget *parent_view)
+static OperationInfo *
+operation_info_new (GtkWidget *parent_view)
 {
-	TransferInfo *result;
-	
-	result = g_new0 (TransferInfo, 1);
+	OperationInfo *result;
+
+	result = g_new0 (OperationInfo, 1);
 	result->parent_view = parent_view;
-	
+	result->debuting_uris = g_hash_table_new_full
+		(g_str_hash, g_str_equal, g_free, NULL);
+
 	eel_add_weak_pointer (&result->parent_view);
 	
 	return result;
 }
 
 static void
-transfer_info_destroy (TransferInfo *transfer_info)
+operation_info_destroy (OperationInfo *operation_info)
 {
-	eel_remove_weak_pointer (&transfer_info->parent_view);
-	
-	if (transfer_info->progress_dialog != NULL) {
-		nautilus_file_operations_progress_done (transfer_info->progress_dialog);
+	eel_remove_weak_pointer (&operation_info->parent_view);
+
+	icon_position_iterator_free (operation_info->iterator);
+
+	if (operation_info->progress_dialog != NULL) {
+		nautilus_file_operations_progress_done (operation_info->progress_dialog);
 	}
 	
-	if (transfer_info->debuting_uris != NULL) {
-		g_hash_table_destroy (transfer_info->debuting_uris);
+	if (operation_info->debuting_uris != NULL) {
+		g_hash_table_destroy (operation_info->debuting_uris);
 	}
 	
-	g_free (transfer_info);
+	g_free (operation_info);
 }
 
 /* Struct used to control applying icon positions to 
@@ -347,58 +357,58 @@ extract_and_ellipsize_file_name_for_dial
 }
 
 static GtkWidget *
-parent_for_error_dialog (TransferInfo *transfer_info)
+parent_for_error_dialog (OperationInfo *operation_info)
 {
-	if (transfer_info->progress_dialog != NULL) {
-		return GTK_WIDGET (transfer_info->progress_dialog);
+	if (operation_info->progress_dialog != NULL) {
+		return GTK_WIDGET (operation_info->progress_dialog);
 	}
 
-	return transfer_info->parent_view;
+	return operation_info->parent_view;
 }
 
 static void
-handle_response_callback (GtkDialog *dialog, int response, TransferInfo *transfer_info)
+handle_response_callback (GtkDialog *dialog, int response, OperationInfo *operation_info)
 {
-	transfer_info->cancelled = TRUE;
+	operation_info->cancelled = TRUE;
 }
 
 static void
-handle_close_callback (GtkDialog *dialog, TransferInfo *transfer_info)
+handle_close_callback (GtkDialog *dialog, OperationInfo *operation_info)
 {
-	transfer_info->cancelled = TRUE;
+	operation_info->cancelled = TRUE;
 }
 
 static void
-create_transfer_dialog (const GnomeVFSXferProgressInfo *progress_info,
-			TransferInfo *transfer_info)
+create_operation_dialog (const GnomeVFSXferProgressInfo *progress_info,
+			OperationInfo *operation_info)
 {
-	g_return_if_fail (transfer_info->progress_dialog == NULL);
+	g_return_if_fail (operation_info->progress_dialog == NULL);
 
-	transfer_info->progress_dialog = nautilus_file_operations_progress_new 
-		(transfer_info->operation_title, "", "", "", 0, 0, TRUE);
+	operation_info->progress_dialog = nautilus_file_operations_progress_new 
+		(operation_info->operation_title, "", "", "", 0, 0, TRUE);
 
 	/* Treat clicking on the close box or use of the escape key
 	 * the same as clicking cancel.
 	 */
-	g_signal_connect (transfer_info->progress_dialog,
+	g_signal_connect (operation_info->progress_dialog,
 			  "response",
 			  G_CALLBACK (handle_response_callback),
-			  transfer_info);
-	g_signal_connect (transfer_info->progress_dialog,
+			  operation_info);
+	g_signal_connect (operation_info->progress_dialog,
 			  "close",
 			  G_CALLBACK (handle_close_callback),
-			  transfer_info);
+			  operation_info);
 
 	/* Make the progress dialog show up over the window we are copying into */
-	if (transfer_info->parent_view != NULL) {
+	if (operation_info->parent_view != NULL) {
 		GtkWidget *toplevel;
 
 		/* Transient-for-desktop are visible on all desktops, we don't want
 		   that. */
-		toplevel = gtk_widget_get_toplevel (transfer_info->parent_view);
+		toplevel = gtk_widget_get_toplevel (operation_info->parent_view);
 		if (toplevel != NULL &&
 		    g_object_get_data (G_OBJECT (toplevel), "is_desktop_window") == NULL) {
-			gtk_window_set_transient_for (GTK_WINDOW (transfer_info->progress_dialog), 
+			gtk_window_set_transient_for (GTK_WINDOW (operation_info->progress_dialog), 
 						      GTK_WINDOW (toplevel));
 		}
 	}
@@ -491,123 +501,91 @@ progress_dialog_set_to_from_item_text (N
 	g_free (to_text);
 }
 
-static int
-handle_transfer_ok (const GnomeVFSXferProgressInfo *progress_info,
-		    TransferInfo *transfer_info)
+static void
+update_progress_dialog (const GnomeVFSXferProgressInfo *progress_info,
+			OperationInfo *operation_info)
 {
-	if (transfer_info->cancelled
-		&& progress_info->phase != GNOME_VFS_XFER_PHASE_COMPLETED) {
-		/* If cancelled, delete any partially copied files that are laying
-		 * around and return. Don't delete the source though..
-		 */
-		if (progress_info->target_name != NULL
-		    && progress_info->source_name != NULL
-		    && strcmp (progress_info->source_name, progress_info->target_name) != 0
-		    && progress_info->bytes_total != progress_info->bytes_copied) {
-			GList *delete_me;
-
-			delete_me = g_list_prepend (NULL, progress_info->target_name);
-			nautilus_file_operations_delete (delete_me, transfer_info->parent_view);
-			g_list_free (delete_me);
-		}
-
-		return 0;
-	}
-	
 	switch (progress_info->phase) {
 	case GNOME_VFS_XFER_PHASE_INITIAL:
-		create_transfer_dialog (progress_info, transfer_info);
-		return 1;
+		break;
 
 	case GNOME_VFS_XFER_PHASE_COLLECTING:
-		if (transfer_info->progress_dialog != NULL) {
+		if (operation_info->progress_dialog != NULL) {
 			nautilus_file_operations_progress_set_operation_string
-				(transfer_info->progress_dialog,
-				 transfer_info->preparation_name);
+				(operation_info->progress_dialog,
+				 operation_info->preparation_name);
 		}
-		return 1;
+		break;
 
 	case GNOME_VFS_XFER_PHASE_READYTOGO:
-		if (transfer_info->progress_dialog != NULL) {
+		if (operation_info->progress_dialog != NULL) {
 			nautilus_file_operations_progress_set_operation_string
-				(transfer_info->progress_dialog,
-				 transfer_info->action_label);
+				(operation_info->progress_dialog,
+				 operation_info->action_label);
 			nautilus_file_operations_progress_set_total
-				(transfer_info->progress_dialog,
+				(operation_info->progress_dialog,
 				 progress_info->files_total,
 				 progress_info->bytes_total);
 		}
-		return 1;
-				 
+		break;
+
 	case GNOME_VFS_XFER_PHASE_DELETESOURCE:
-		nautilus_file_changes_consume_changes (FALSE);
-		if (transfer_info->progress_dialog != NULL) {
+		if (operation_info->progress_dialog != NULL) {
 			progress_dialog_set_to_from_item_text
-				(transfer_info->progress_dialog,
-				 transfer_info->progress_verb,
+				(operation_info->progress_dialog,
+				 operation_info->progress_verb,
 				 progress_info->source_name,
 				 NULL,
 				 progress_info->file_index,
 				 progress_info->file_size);
 
 			nautilus_file_operations_progress_update_sizes
-				(transfer_info->progress_dialog,
+				(operation_info->progress_dialog,
 				 MIN (progress_info->bytes_copied, 
 				      progress_info->bytes_total),
 				 MIN (progress_info->total_bytes_copied,
 				      progress_info->bytes_total));
 		}
-		return 1;
+		break;
 
 	case GNOME_VFS_XFER_PHASE_MOVING:
 	case GNOME_VFS_XFER_PHASE_OPENSOURCE:
 	case GNOME_VFS_XFER_PHASE_OPENTARGET:
 		/* fall through */
 	case GNOME_VFS_XFER_PHASE_COPYING:
-		if (transfer_info->progress_dialog != NULL) {
+		if (operation_info->progress_dialog != NULL) {
 			if (progress_info->bytes_copied == 0) {
 				progress_dialog_set_to_from_item_text
-					(transfer_info->progress_dialog,
-					 transfer_info->progress_verb,
+					(operation_info->progress_dialog,
+					 operation_info->progress_verb,
 					 progress_info->source_name,
 					 progress_info->target_name,
 					 progress_info->file_index,
 					 progress_info->file_size);
 			} else {
 				nautilus_file_operations_progress_update_sizes
-					(transfer_info->progress_dialog,
+					(operation_info->progress_dialog,
 					 MIN (progress_info->bytes_copied, 
 					      progress_info->bytes_total),
 					 MIN (progress_info->total_bytes_copied,
 					      progress_info->bytes_total));
 			}
 		}
-		return 1;
+		break;
 
 	case GNOME_VFS_XFER_PHASE_CLEANUP:
-		if (transfer_info->progress_dialog != NULL) {
+		if (operation_info->progress_dialog != NULL) {
 			nautilus_file_operations_progress_clear
-				(transfer_info->progress_dialog);
+				(operation_info->progress_dialog);
 			nautilus_file_operations_progress_set_operation_string
-				(transfer_info->progress_dialog,
-				 transfer_info->cleanup_name);
+				(operation_info->progress_dialog,
+				 operation_info->cleanup_name);
 		}
-		return 1;
+		break;
 
 	case GNOME_VFS_XFER_PHASE_COMPLETED:
-		nautilus_file_changes_consume_changes (TRUE);
-		if (transfer_info->done_callback != NULL) {
-			transfer_info->done_callback (transfer_info->debuting_uris,
-						      transfer_info->done_callback_data);
-			/* done_callback now owns (will free) debuting_uris */
-			transfer_info->debuting_uris = NULL;
-		}
-
-		transfer_info_destroy (transfer_info);
-		return 1;
-
 	default:
-		return 1;
+		break;
 	}
 }
 
@@ -632,7 +610,7 @@ typedef enum {
 
 static void
 build_error_string (const char *source_name, const char *target_name,
-		    TransferKind operation_kind,
+		    OperationKind operation_kind,
 		    NautilusFileOperationsErrorKind error_kind,
 		    NautilusFileOperationsErrorLocation error_location,
 		    GnomeVFSResult error,
@@ -687,6 +665,25 @@ build_error_string (const char *source_n
 			}
 			break;
 
+		case CREATE_FILE:
+		case CREATE_FOLDER:
+			switch (error) {
+			*error_string = g_strdup_printf (_("Error While Creating \"%s\"."), target_name);
+
+			case GNOME_VFS_ERROR_ACCESS_DENIED:
+				detail_format = _("You do not have permissions to write to the destination.");
+				break;
+
+			case GNOME_VFS_ERROR_NO_SPACE:
+				detail_format = _("There is no space on the destination.");
+				break;
+
+			default:
+				detail_format = (char *) gnome_vfs_result_to_string (error);
+				break;
+			}
+			break;
+
 		default:
 			g_assert_not_reached ();
 			break;
@@ -900,11 +897,11 @@ build_error_string (const char *source_n
 }
 
 static int
-handle_transfer_vfs_error (const GnomeVFSXferProgressInfo *progress_info,
-			   TransferInfo *transfer_info)
+handle_operation_vfs_error (const GnomeVFSXferProgressInfo *progress_info,
+			    OperationInfo *operation_info)
 {
-	/* Notice that the error mode in `transfer_info' is the one we have been
-         * requested, but the transfer is always performed in mode
+	/* Notice that the error mode in `operation_info' is the one we have been
+         * requested, but the operation is always performed in mode
          * `GNOME_VFS_XFER_ERROR_MODE_QUERY'.
          */
 
@@ -917,26 +914,28 @@ handle_transfer_vfs_error (const GnomeVF
 	NautilusFileOperationsErrorKind error_kind;
 	NautilusFileOperationsErrorLocation error_location;
 	
-	switch (transfer_info->error_mode) {
+	switch (operation_info->error_mode) {
 	case GNOME_VFS_XFER_ERROR_MODE_QUERY:
 
-		/* transfer error, prompt the user to continue or cancel */
+		/* operation error, prompt the user to continue or cancel */
 
 		/* stop timeout while waiting for user */
-		nautilus_file_operations_progress_pause_timeout (transfer_info->progress_dialog);
+		if (operation_info->progress_dialog != NULL) {
+			nautilus_file_operations_progress_pause_timeout (operation_info->progress_dialog);
+		}
 
 		formatted_source_name = NULL;
 		formatted_target_name = NULL;
 
 		if (progress_info->source_name != NULL) {
 			formatted_source_name = format_and_ellipsize_uri_for_dialog
-				(parent_for_error_dialog (transfer_info),
+				(parent_for_error_dialog (operation_info),
 				 progress_info->source_name);
 		}
 
 		if (progress_info->target_name != NULL) {
 			formatted_target_name = format_and_ellipsize_uri_for_dialog
-				(parent_for_error_dialog (transfer_info),
+				(parent_for_error_dialog (operation_info),
 				 progress_info->target_name);
 		}
 
@@ -948,36 +947,36 @@ handle_transfer_vfs_error (const GnomeVF
 		 */
 		if ((progress_info->vfs_status == GNOME_VFS_ERROR_READ_ONLY_FILE_SYSTEM
 				|| progress_info->vfs_status == GNOME_VFS_ERROR_READ_ONLY)
-			&& (transfer_info->kind == TRANSFER_DELETE
-				|| transfer_info->kind == TRANSFER_EMPTY_TRASH)) {
+			&& (operation_info->kind == TRANSFER_DELETE
+				|| operation_info->kind == TRANSFER_EMPTY_TRASH)) {
 			error_location = ERROR_LOCATION_SOURCE_PARENT;
 			error_kind = ERROR_READ_ONLY;
 		} else if (progress_info->vfs_status == GNOME_VFS_ERROR_ACCESS_DENIED
-			&& (transfer_info->kind == TRANSFER_DELETE
-				|| transfer_info->kind == TRANSFER_EMPTY_TRASH)) {
+			&& (operation_info->kind == TRANSFER_DELETE
+				|| operation_info->kind == TRANSFER_EMPTY_TRASH)) {
 			error_location = ERROR_LOCATION_SOURCE_PARENT;
 			error_kind = ERROR_NOT_ENOUGH_PERMISSIONS;
 		} else if ((progress_info->vfs_status == GNOME_VFS_ERROR_READ_ONLY_FILE_SYSTEM
 				|| progress_info->vfs_status == GNOME_VFS_ERROR_READ_ONLY)
-			&& (transfer_info->kind == TRANSFER_MOVE
-				|| transfer_info->kind == TRANSFER_MOVE_TO_TRASH)
+			&& (operation_info->kind == TRANSFER_MOVE
+				|| operation_info->kind == TRANSFER_MOVE_TO_TRASH)
 			&& progress_info->phase != GNOME_VFS_XFER_CHECKING_DESTINATION) {
 			error_location = ERROR_LOCATION_SOURCE_PARENT;
 			error_kind = ERROR_READ_ONLY;
 		} else if (progress_info->vfs_status == GNOME_VFS_ERROR_ACCESS_DENIED
-			&& transfer_info->kind == TRANSFER_MOVE
+			&& operation_info->kind == TRANSFER_MOVE
 			&& progress_info->phase == GNOME_VFS_XFER_PHASE_OPENTARGET) {
 			error_location = ERROR_LOCATION_TARGET;
 			error_kind = ERROR_NOT_ENOUGH_PERMISSIONS;
 		} else if (progress_info->vfs_status == GNOME_VFS_ERROR_ACCESS_DENIED
-			&& (transfer_info->kind == TRANSFER_MOVE
-				|| transfer_info->kind == TRANSFER_MOVE_TO_TRASH)
+			&& (operation_info->kind == TRANSFER_MOVE
+				|| operation_info->kind == TRANSFER_MOVE_TO_TRASH)
 			&& progress_info->phase != GNOME_VFS_XFER_CHECKING_DESTINATION) {
 			error_location = ERROR_LOCATION_SOURCE_OR_PARENT;
 			error_kind = ERROR_NOT_ENOUGH_PERMISSIONS;
 		} else if (progress_info->vfs_status == GNOME_VFS_ERROR_ACCESS_DENIED
-			&& (transfer_info->kind == TRANSFER_COPY
-				|| transfer_info->kind == TRANSFER_DUPLICATE)
+			&& (operation_info->kind == TRANSFER_COPY
+				|| operation_info->kind == TRANSFER_DUPLICATE)
 			&& (progress_info->phase == GNOME_VFS_XFER_PHASE_OPENSOURCE
 				|| progress_info->phase == GNOME_VFS_XFER_PHASE_COLLECTING
 				|| progress_info->phase == GNOME_VFS_XFER_PHASE_INITIAL)) {
@@ -996,27 +995,29 @@ handle_transfer_vfs_error (const GnomeVF
 			error_location = ERROR_LOCATION_TARGET;
 			error_kind = ERROR_NO_SPACE;
 		} else if (progress_info->vfs_status == GNOME_VFS_ERROR_DIRECTORY_NOT_EMPTY
-			   && transfer_info->kind == TRANSFER_MOVE) {
+			   && operation_info->kind == TRANSFER_MOVE) {
 			error_location = ERROR_LOCATION_SOURCE_OR_PARENT;
 			error_kind = ERROR_SOURCE_IN_TARGET;
 		}
 
 		build_error_string (formatted_source_name, formatted_target_name,
-				    transfer_info->kind,
+				    operation_info->kind,
 				    error_kind, error_location,
 				    progress_info->vfs_status,
 				    &text, &detail);
 
 		if (error_location == ERROR_LOCATION_TARGET ||
-		    error_kind == ERROR_SOURCE_IN_TARGET) {
+		    error_kind == ERROR_SOURCE_IN_TARGET ||
+		    operation_info->kind == CREATE_FILE ||
+		    operation_info->kind == CREATE_FOLDER) {
 			/* We can't continue, just tell the user. */
-			eel_run_simple_dialog (parent_for_error_dialog (transfer_info),
+			eel_run_simple_dialog (parent_for_error_dialog (operation_info),
 				TRUE, GTK_MESSAGE_ERROR, text, detail, GTK_STOCK_OK, NULL);
 			error_dialog_result = GNOME_VFS_XFER_ERROR_ACTION_ABORT;
 
 		} else if (progress_info->files_total == 1) {
 			error_dialog_button_pressed = eel_run_simple_dialog
-				(parent_for_error_dialog (transfer_info), TRUE, 
+				(parent_for_error_dialog (operation_info), TRUE, 
 				 GTK_MESSAGE_ERROR, text, 
 				 detail, GTK_STOCK_CANCEL, _("_Retry"), NULL);
 
@@ -1033,7 +1034,7 @@ handle_transfer_vfs_error (const GnomeVF
 			}
 		} else {
 			error_dialog_button_pressed = eel_run_simple_dialog
-				(parent_for_error_dialog (transfer_info), TRUE, 
+				(parent_for_error_dialog (operation_info), TRUE, 
 				 GTK_MESSAGE_ERROR, text, 
 				 detail, _("_Skip"), GTK_STOCK_CANCEL, _("_Retry"), NULL);
 
@@ -1058,15 +1059,17 @@ handle_transfer_vfs_error (const GnomeVF
 		g_free (formatted_source_name);
 		g_free (formatted_target_name);
 
-		nautilus_file_operations_progress_resume_timeout (transfer_info->progress_dialog);
+		if (operation_info->progress_dialog != NULL) {
+			nautilus_file_operations_progress_resume_timeout (operation_info->progress_dialog);
+		}
 
 		return error_dialog_result;
 
 	case GNOME_VFS_XFER_ERROR_MODE_ABORT:
 	default:
-		if (transfer_info->progress_dialog != NULL) {
+		if (operation_info->progress_dialog != NULL) {
 			nautilus_file_operations_progress_done
-				(transfer_info->progress_dialog);
+				(operation_info->progress_dialog);
 		}
 		return GNOME_VFS_XFER_ERROR_ACTION_ABORT;
 	}
@@ -1110,21 +1113,23 @@ is_directory (const char *uri)
 
 static int
 handle_transfer_overwrite (const GnomeVFSXferProgressInfo *progress_info,
-		           TransferInfo *transfer_info)
+		           OperationInfo *operation_info)
 {
 	int result;
 	char *text, *primary_text, *secondary_text, *formatted_name;
 	gboolean is_merge, target_is_dir;
 
-	nautilus_file_operations_progress_pause_timeout (transfer_info->progress_dialog);	
+	if (operation_info->progress_dialog != NULL) {
+		nautilus_file_operations_progress_pause_timeout (operation_info->progress_dialog);	
+	}
 
 	/* Handle special case files such as Trash, mount links and home directory */	
 	if (is_special_link (progress_info->target_name)) {
 		formatted_name = extract_and_ellipsize_file_name_for_dialog
-			(parent_for_error_dialog (transfer_info),
+			(parent_for_error_dialog (operation_info),
 			 progress_info->target_name);
 		
-		if (transfer_info->kind == TRANSFER_MOVE) {
+		if (operation_info->kind == TRANSFER_MOVE) {
 			primary_text = g_strdup_printf (_("Could not move \"%s\" to the new location."),
 			                                formatted_name);
 						
@@ -1140,7 +1145,7 @@ handle_transfer_overwrite (const GnomeVF
 					   "to copy the item, rename it and try again.");
 		}
 		
-		eel_run_simple_dialog (parent_for_error_dialog (transfer_info),
+		eel_run_simple_dialog (parent_for_error_dialog (operation_info),
 				       TRUE, GTK_MESSAGE_ERROR,
 				       primary_text, secondary_text,
 				       GTK_STOCK_OK, NULL);
@@ -1148,14 +1153,16 @@ handle_transfer_overwrite (const GnomeVF
 		g_free (primary_text);
 		g_free (formatted_name);
 
-		nautilus_file_operations_progress_resume_timeout (transfer_info->progress_dialog);
+		if (operation_info->progress_dialog != NULL) {
+			nautilus_file_operations_progress_resume_timeout (operation_info->progress_dialog);
+		}
 
 		return GNOME_VFS_XFER_OVERWRITE_ACTION_SKIP;
 	}
 	
 	/* transfer conflict, prompt the user to replace or skip */
 	formatted_name = format_and_ellipsize_uri_for_dialog (
-		parent_for_error_dialog (transfer_info), progress_info->target_name);
+		parent_for_error_dialog (operation_info), progress_info->target_name);
 
 	target_is_dir = is_directory (progress_info->target_name);
 	if (target_is_dir) {
@@ -1180,7 +1187,7 @@ handle_transfer_overwrite (const GnomeVF
 		 * Replace All
 		 */
 		result = eel_run_simple_dialog 
-			(parent_for_error_dialog (transfer_info),
+			(parent_for_error_dialog (operation_info),
 			 TRUE,
 			 GTK_MESSAGE_QUESTION, 
 			 text, 
@@ -1188,7 +1195,9 @@ handle_transfer_overwrite (const GnomeVF
 			 _("_Skip"), _("_Replace"), NULL);
 		g_free (text);	 
 
-		nautilus_file_operations_progress_resume_timeout (transfer_info->progress_dialog);
+		if (operation_info->progress_dialog != NULL) {
+			nautilus_file_operations_progress_pause_timeout (operation_info->progress_dialog);
+		}
 					 
 		switch (result) {
 		case 0:
@@ -1201,12 +1210,14 @@ handle_transfer_overwrite (const GnomeVF
 		}
 	} else {
 		result = eel_run_simple_dialog
-			(parent_for_error_dialog (transfer_info), TRUE, GTK_MESSAGE_QUESTION, text, 
+			(parent_for_error_dialog (operation_info), TRUE, GTK_MESSAGE_QUESTION, text, 
 			 secondary_text, 
 			 _("S_kip All"), _("Replace _All"), _("_Skip"), _("_Replace"), NULL);
 		g_free (text);
 
-		nautilus_file_operations_progress_resume_timeout (transfer_info->progress_dialog);
+		if (operation_info->progress_dialog != NULL) {
+			nautilus_file_operations_progress_resume_timeout (operation_info->progress_dialog);
+		}
 
 		switch (result) {
 		case 0:
@@ -1589,7 +1600,8 @@ get_duplicate_name (const char *name, in
 }
 
 static char *
-get_next_duplicate_name (char *name, int count_increment)
+get_next_duplicate_name (const char *name,
+			 int count_increment)
 {
 	char *unescaped_name;
 	char *unescaped_tmp_name;
@@ -1598,8 +1610,6 @@ get_next_duplicate_name (char *name, int
 	char *new_file;
 
 	unescaped_tmp_name = gnome_vfs_unescape_string (name, "/");
-	g_free (name);
-
 	unescaped_name = g_filename_to_utf8 (unescaped_tmp_name, -1,
 					     NULL, NULL, NULL);
 	if (!unescaped_name) {
@@ -1626,53 +1636,101 @@ get_next_duplicate_name (char *name, int
 }
 
 static int
-handle_transfer_duplicate (GnomeVFSXferProgressInfo *progress_info,
-			   TransferInfo *transfer_info)
+handle_operation_duplicate (GnomeVFSXferProgressInfo *progress_info,
+			    OperationInfo *operation_info)
 {
-	switch (transfer_info->kind) {
+	char *old_name = progress_info->duplicate_name;
+	int old_count = progress_info->duplicate_count;
+
+	switch (operation_info->kind) {
 	case TRANSFER_LINK:
-		progress_info->duplicate_name = get_link_name
-			(progress_info->duplicate_name,
-			 progress_info->duplicate_count);
+		progress_info->duplicate_name = get_link_name (old_name, old_count);
 		break;
 
 	case TRANSFER_COPY:
 	case TRANSFER_MOVE_TO_TRASH:
-		progress_info->duplicate_name = get_next_duplicate_name
-			(progress_info->duplicate_name,
-			 progress_info->duplicate_count);
+		progress_info->duplicate_name = get_next_duplicate_name (old_name, old_count);
+		break;
+
+	case CREATE_FILE:
+	case CREATE_FOLDER:
+		if (progress_info->vfs_status == GNOME_VFS_ERROR_NAME_TOO_LONG) {
+			/* special case an 8.3 file system */
+			progress_info->duplicate_name = g_strndup (old_name, 8);
+			g_free (old_name);
+			old_name = progress_info->duplicate_name;
+
+			progress_info->duplicate_name = g_strdup_printf
+				("%s.%d", old_name, old_count);
+		} else {
+			progress_info->duplicate_name = get_next_duplicate_name (old_name, old_count);
+		}
 		break;
+
 	default:
 		break;
 		/* For all other cases we use the name as-is. */
 	}
 
+	if (old_name != progress_info->duplicate_name) {
+		g_free (old_name);
+
+		icon_position_iterator_update_uri
+			(operation_info->iterator,
+			 progress_info->target_name,
+			 progress_info->duplicate_name);
+	}
+
+
 	return GNOME_VFS_XFER_ERROR_ACTION_SKIP;
 }
 
 static int
-update_transfer_callback (GnomeVFSAsyncHandle *handle,
-	       GnomeVFSXferProgressInfo *progress_info,
-	       gpointer data)
+async_operation_callback (GnomeVFSAsyncHandle *handle,
+			  GnomeVFSXferProgressInfo *progress_info,
+			  gpointer data)
 {
-	TransferInfo *transfer_info;
+	OperationInfo *operation_info;
+	int ret;
 
-	transfer_info = (TransferInfo *) data;
+	operation_info = (OperationInfo *) data;
 
 	switch (progress_info->status) {
 	case GNOME_VFS_XFER_PROGRESS_STATUS_OK:
-		return handle_transfer_ok (progress_info, transfer_info);
+		update_progress_dialog (progress_info, operation_info);
+		break;
+
 	case GNOME_VFS_XFER_PROGRESS_STATUS_VFSERROR:
-		return handle_transfer_vfs_error (progress_info, transfer_info);
 	case GNOME_VFS_XFER_PROGRESS_STATUS_OVERWRITE:
-		return handle_transfer_overwrite (progress_info, transfer_info);
 	case GNOME_VFS_XFER_PROGRESS_STATUS_DUPLICATE:
-		return handle_transfer_duplicate (progress_info, transfer_info);
+		break;
+
 	default:
 		g_warning (_("Unknown GnomeVFSXferProgressStatus %d"),
 			   progress_info->status);
 		return 0;
 	}
+
+	/* It's not entirely predictable when this is called,
+	 * but when this is the case the sync call already
+	 * ran before this one, so we don't want to mess up its
+	 * results. Blame GnomeVFS.
+	 * */
+	ret = operation_info->last_sync_response;
+
+	switch (progress_info->phase) {
+	case GNOME_VFS_XFER_PHASE_DELETESOURCE:
+		nautilus_file_changes_consume_changes (FALSE);
+		break;
+
+	case GNOME_VFS_XFER_PHASE_COMPLETED:
+		operation_info_destroy (operation_info);
+
+	default:
+		break;
+	}
+
+	return ret;
 }
 
 static void
@@ -1689,119 +1747,196 @@ apply_one_position (IconPositionIterator
 	}
 }
 
-typedef struct {
-	GHashTable		*debuting_uris;
-	IconPositionIterator	*iterator;
-} SyncTransferInfo;
-
-/* Low-level callback, called for every copy engine operation.
- * Generates notifications about new, deleted and moved files.
- */
 static int
-sync_transfer_callback (GnomeVFSXferProgressInfo *progress_info, gpointer data)
+handle_operation_ok (const GnomeVFSXferProgressInfo *progress_info,
+		     OperationInfo *operation_info)
 {
 	GHashTable	     *debuting_uris;
 	IconPositionIterator *position_iterator;
 	gboolean              really_moved;
 
-	if (data != NULL) {
-		debuting_uris	  = ((SyncTransferInfo *) data)->debuting_uris;
-		position_iterator = ((SyncTransferInfo *) data)->iterator;
-	} else {
-		debuting_uris     = NULL;
-		position_iterator = NULL;
+	if (operation_info->cancelled
+	    && progress_info->phase != GNOME_VFS_XFER_PHASE_COMPLETED) {
+
+		/* If cancelled, delete any partially copied files that are laying
+		 * around and return. Don't delete the source though..
+		 */
+		if (progress_info->target_name != NULL
+		    && progress_info->source_name != NULL
+		    && strcmp (progress_info->source_name, progress_info->target_name) != 0
+		    && progress_info->bytes_total != progress_info->bytes_copied) {
+			GList *delete_me;
+
+			delete_me = g_list_prepend (NULL, progress_info->target_name);
+			nautilus_file_operations_delete (delete_me, operation_info->parent_view);
+			g_list_free (delete_me);
+		}
+
+		return 0;
 	}
 
-	if (progress_info->status == GNOME_VFS_XFER_PROGRESS_STATUS_OK) {
-		switch (progress_info->phase) {
-		case GNOME_VFS_XFER_PHASE_OPENTARGET:
-			if (progress_info->top_level_item) {
-				/* this is one of the selected copied or moved items -- we need
-				 * to make sure it's metadata gets copied over
-				 */
-				if (progress_info->source_name == NULL) {
-					/* remove any old metadata */
-					nautilus_file_changes_queue_schedule_metadata_remove 
-						(progress_info->target_name);
-				} else {
-					nautilus_file_changes_queue_schedule_metadata_copy 
-						(progress_info->source_name, progress_info->target_name);
+	debuting_uris	  = operation_info->debuting_uris;
+	position_iterator = operation_info->iterator;
 
-				}
+	switch (progress_info->phase) {
+	case GNOME_VFS_XFER_PHASE_INITIAL:
+	case GNOME_VFS_XFER_CHECKING_DESTINATION:
+	case GNOME_VFS_XFER_PHASE_COLLECTING:
+	case GNOME_VFS_XFER_PHASE_READYTOGO:
+	case GNOME_VFS_XFER_PHASE_OPENSOURCE:
+		break;
 
-				apply_one_position (position_iterator,
-						    progress_info->source_name,
-						    progress_info->target_name);
-
-				if (debuting_uris != NULL) {
-					g_hash_table_replace (debuting_uris,
-							      g_strdup (progress_info->target_name),
-							      GINT_TO_POINTER (TRUE));
-				}
+	case GNOME_VFS_XFER_PHASE_OPENTARGET:
+		if (progress_info->source_name == NULL) {
+			/* remove any old metadata */
+			nautilus_file_changes_queue_schedule_metadata_remove 
+				(progress_info->target_name);
+		} else {
+			nautilus_file_changes_queue_schedule_metadata_copy 
+				(progress_info->source_name, progress_info->target_name);
+
+		}
+
+		if (progress_info->top_level_item) {
+			apply_one_position (position_iterator,
+					    progress_info->source_name,
+					    progress_info->target_name);
+
+			if (debuting_uris != NULL) {
+				g_hash_table_replace (debuting_uris,
+						      g_strdup (progress_info->target_name),
+						      GINT_TO_POINTER (TRUE));
 			}
-			nautilus_file_changes_queue_file_added (progress_info->target_name);
-			break;
+		}
 
-		case GNOME_VFS_XFER_PHASE_MOVING:
-			g_assert (progress_info->source_name != NULL);
+		nautilus_file_changes_queue_file_added (progress_info->target_name);
+		break;
 
-			/* If the source and target are the same, that
-			 * means we "moved" something in place. No
-			 * actual change happened, so we really don't
-			 * want to send out any change notification,
-			 * but we do want to select the files as
-			 * "newly moved here" so we put them into the
-			 * debuting_uris set.
-			 */
-			really_moved = strcmp (progress_info->source_name,
-					       progress_info->target_name) != 0;
+	case GNOME_VFS_XFER_PHASE_COPYING:
+		break;
 
-			if (progress_info->top_level_item) {
-				if (really_moved) {
-					nautilus_file_changes_queue_schedule_metadata_move 
-						(progress_info->source_name, progress_info->target_name);
-				}
+	case GNOME_VFS_XFER_PHASE_MOVING:
+		g_assert (progress_info->source_name != NULL);
 
-				apply_one_position (position_iterator,
-						    progress_info->source_name,
-						    progress_info->target_name);
-
-				if (debuting_uris != NULL) {
-					g_hash_table_replace (debuting_uris,
-							      g_strdup (progress_info->target_name),
-							      GINT_TO_POINTER (really_moved));
-				}
+		/* If the source and target are the same, that
+		 * means we "moved" something in place. No
+		 * actual change happened, so we really don't
+		 * want to send out any change notification,
+		 * but we do want to select the files as
+		 * "newly moved here" so we put them into the
+		 * debuting_uris set.
+		 */
+		really_moved = strcmp (progress_info->source_name,
+				       progress_info->target_name) != 0;
+
+		if (really_moved) {
+			nautilus_file_changes_queue_schedule_metadata_move (progress_info->source_name,
+									    progress_info->target_name);
+		}
+
+		if (progress_info->top_level_item) {
+			apply_one_position (position_iterator,
+					    progress_info->source_name,
+					    progress_info->target_name);
+
+			if (debuting_uris != NULL) {
+				g_hash_table_replace (debuting_uris,
+						      g_strdup (progress_info->target_name),
+						      GINT_TO_POINTER (really_moved));
 			}
+
 			if (really_moved) {
 				nautilus_file_changes_queue_file_moved (progress_info->source_name,
 									progress_info->target_name);
 			}
-			break;
-			
-		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 
-					(progress_info->source_name);
-			}
-			nautilus_file_changes_queue_file_removed (progress_info->source_name);
-			break;
-			
-		case GNOME_VFS_XFER_PHASE_COMPLETED:
-			/* done, clean up */
-			icon_position_iterator_free (position_iterator);
-			/* SyncXferInfo doesn't own the debuting_uris hash table - don't free it here.
-			 */
-			g_free (data);
-			break;
+		}
 
-		default:
-			break;
+		break;
+
+	case GNOME_VFS_XFER_PHASE_READSOURCE:
+	case GNOME_VFS_XFER_PHASE_WRITETARGET:
+	case GNOME_VFS_XFER_PHASE_CLOSESOURCE:
+	case GNOME_VFS_XFER_PHASE_CLOSETARGET:
+		break;
+
+	case GNOME_VFS_XFER_PHASE_DELETESOURCE:
+		g_assert (progress_info->source_name != NULL);
+		nautilus_file_changes_queue_schedule_metadata_remove (progress_info->source_name);
+		nautilus_file_changes_queue_file_removed (progress_info->source_name);
+		break;
+
+	case GNOME_VFS_XFER_PHASE_SETATTRIBUTES:
+	case GNOME_VFS_XFER_PHASE_FILECOMPLETED:
+	case GNOME_VFS_XFER_PHASE_CLEANUP:
+		break;
+
+	case GNOME_VFS_XFER_PHASE_COMPLETED:
+		nautilus_file_changes_consume_changes (TRUE);
+
+		if (operation_info->done_callback != NULL) {
+			operation_info->done_callback (operation_info->debuting_uris,
+						       operation_info->done_callback_user_data);
+			operation_info->debuting_uris = NULL;
 		}
+
+		/* cleanup is done in the async callback */
+		break;
+
+	default:
+		g_warning (_("Unknown GnomeVFSXferPhase %d"),
+			   progress_info->phase);
+		return 0;
 	}
+
 	return 1;
 }
 
+/* Low-level callback, called for every copy engine operation.
+ * Generates notifications about new, deleted and moved files,
+ * and handles problems.
+ */
+static int
+sync_operation_callback (GnomeVFSXferProgressInfo *progress_info,
+			 gpointer data)
+{
+	OperationInfo *operation_info = (OperationInfo *) data;
+	int ret;
+
+	g_assert (operation_info != NULL);
+
+	if (progress_info->phase == GNOME_VFS_XFER_PHASE_INITIAL &&
+	    operation_info->kind != CREATE_FILE &&
+	    operation_info->kind != CREATE_FOLDER) {
+		create_operation_dialog (progress_info, operation_info);
+	}
+
+	switch (progress_info->status) {
+	case GNOME_VFS_XFER_PROGRESS_STATUS_OK:
+		ret = handle_operation_ok (progress_info, operation_info);
+		break;
+
+	case GNOME_VFS_XFER_PROGRESS_STATUS_VFSERROR:
+		ret = handle_operation_vfs_error (progress_info, operation_info);
+		break;
+
+	case GNOME_VFS_XFER_PROGRESS_STATUS_OVERWRITE:
+		ret = handle_transfer_overwrite (progress_info, operation_info);
+		break;
+
+	case GNOME_VFS_XFER_PROGRESS_STATUS_DUPLICATE:
+		ret = handle_operation_duplicate (progress_info, operation_info);
+		break;
+	default:
+		g_warning (_("Unknown GnomeVFSXferProgressStatus %d"),
+			   progress_info->status);
+		ret = 0;
+	}
+
+	operation_info->last_sync_response = ret;
+
+	return ret;
+}
+
 static gboolean
 check_target_directory_is_or_in_trash (GnomeVFSURI *trash_dir_uri, GnomeVFSURI *target_dir_uri)
 {
@@ -1840,8 +1975,8 @@ nautilus_file_operations_copy_move (cons
 				    const char *target_dir,
 				    GdkDragAction copy_action,
 				    GtkWidget *parent_view,
-				    void (*done_callback) (GHashTable *debuting_uris, gpointer data),
-				    gpointer done_callback_data)
+				    NautilusFileOperationsCallback done_callback,
+				    gpointer done_callback_user_data)
 {
 	const GList *p;
 	GnomeVFSXferOptions move_options;
@@ -1851,8 +1986,7 @@ nautilus_file_operations_copy_move (cons
 	GnomeVFSURI *trash_dir_uri;
 	GnomeVFSURI *uri;
 
-	TransferInfo *transfer_info;
-	SyncTransferInfo *sync_transfer_info;
+	OperationInfo *operation_info;
 	GnomeVFSResult result;
 	gboolean target_is_trash;
 	gboolean duplicate;
@@ -1998,7 +2132,7 @@ nautilus_file_operations_copy_move (cons
 	}
 	
 	/* set up the copy/move parameters */
-	transfer_info = transfer_info_new (parent_view);
+	operation_info = operation_info_new (parent_view);
 	if (relative_item_points != NULL && relative_item_points->len > 0) {
 		screen = gtk_widget_get_screen (GTK_WIDGET (parent_view));
 		screen_num = gdk_screen_get_number (screen);
@@ -2015,52 +2149,52 @@ nautilus_file_operations_copy_move (cons
 		/* when moving to trash, handle name conflicts automatically */
 		move_options |= GNOME_VFS_XFER_USE_UNIQUE_NAMES;
 		/* localizers: progress dialog title */
-		transfer_info->operation_title = _("Moving files to the Trash");
+		operation_info->operation_title = _("Moving files to the Trash");
 		/* localizers: label prepended to the progress count */
-		transfer_info->action_label =_("Throwing out file:");
+		operation_info->action_label =_("Throwing out file:");
 		/* localizers: label prepended to the name of the current file moved */
-		transfer_info->progress_verb =_("Moving");
-		transfer_info->preparation_name =_("Preparing to Move to Trash...");
+		operation_info->progress_verb =_("Moving");
+		operation_info->preparation_name =_("Preparing to Move to Trash...");
 
-		transfer_info->kind = TRANSFER_MOVE_TO_TRASH;
+		operation_info->kind = TRANSFER_MOVE_TO_TRASH;
 
 	} else if ((move_options & GNOME_VFS_XFER_REMOVESOURCE) != 0) {
 		/* localizers: progress dialog title */
-		transfer_info->operation_title = _("Moving files");
+		operation_info->operation_title = _("Moving files");
 		/* localizers: label prepended to the progress count */
-		transfer_info->action_label =_("Moving file:");
+		operation_info->action_label =_("Moving file:");
 		/* localizers: label prepended to the name of the current file moved */
-		transfer_info->progress_verb =_("Moving");
-		transfer_info->preparation_name =_("Preparing To Move...");
-		transfer_info->cleanup_name = _("Finishing Move...");
+		operation_info->progress_verb =_("Moving");
+		operation_info->preparation_name =_("Preparing To Move...");
+		operation_info->cleanup_name = _("Finishing Move...");
 
-		transfer_info->kind = TRANSFER_MOVE;
+		operation_info->kind = TRANSFER_MOVE;
 
 	} else if ((move_options & GNOME_VFS_XFER_LINK_ITEMS) != 0) {
 		/* when creating links, handle name conflicts automatically */
 		move_options |= GNOME_VFS_XFER_USE_UNIQUE_NAMES;
 		/* localizers: progress dialog title */
-		transfer_info->operation_title = _("Creating links to files");
+		operation_info->operation_title = _("Creating links to files");
 		/* localizers: label prepended to the progress count */
-		transfer_info->action_label =_("Linking file:");
+		operation_info->action_label =_("Linking file:");
 		/* localizers: label prepended to the name of the current file linked */
-		transfer_info->progress_verb =_("Linking");
-		transfer_info->preparation_name = _("Preparing to Create Links...");
-		transfer_info->cleanup_name = _("Finishing Creating Links...");
+		operation_info->progress_verb =_("Linking");
+		operation_info->preparation_name = _("Preparing to Create Links...");
+		operation_info->cleanup_name = _("Finishing Creating Links...");
 
-		transfer_info->kind = TRANSFER_LINK;
+		operation_info->kind = TRANSFER_LINK;
 
 	} else {
 		/* localizers: progress dialog title */
-		transfer_info->operation_title = _("Copying files");
+		operation_info->operation_title = _("Copying files");
 		/* localizers: label prepended to the progress count */
-		transfer_info->action_label =_("Copying file:");
+		operation_info->action_label =_("Copying file:");
 		/* localizers: label prepended to the name of the current file copied */
-		transfer_info->progress_verb =_("Copying");
-		transfer_info->preparation_name =_("Preparing To Copy...");
-		transfer_info->cleanup_name = "";
+		operation_info->progress_verb =_("Copying");
+		operation_info->preparation_name =_("Preparing To Copy...");
+		operation_info->cleanup_name = "";
 
-		transfer_info->kind = TRANSFER_COPY;
+		operation_info->kind = TRANSFER_COPY;
 	}
 
 	/* we'll need to check for copy into Trash and for moving/copying the Trash itself */
@@ -2151,25 +2285,20 @@ nautilus_file_operations_copy_move (cons
 		}
 	}
 
-	transfer_info->error_mode = GNOME_VFS_XFER_ERROR_MODE_QUERY;
-	transfer_info->overwrite_mode = GNOME_VFS_XFER_OVERWRITE_MODE_QUERY;
-	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);
-
-	sync_transfer_info = g_new (SyncTransferInfo, 1);
-	sync_transfer_info->iterator = icon_position_iterator;
-	sync_transfer_info->debuting_uris = transfer_info->debuting_uris;
+	operation_info->error_mode = GNOME_VFS_XFER_ERROR_MODE_QUERY;
+	operation_info->overwrite_mode = GNOME_VFS_XFER_OVERWRITE_MODE_QUERY;
+	operation_info->done_callback = done_callback;
+	operation_info->done_callback_user_data = done_callback_user_data;
 
-	transfer_info->iterator = sync_transfer_info->iterator;
+	operation_info->iterator = icon_position_iterator;
 
 	if (result == GNOME_VFS_OK) {
-		gnome_vfs_async_xfer (&transfer_info->handle, source_uri_list, target_uri_list,
+		gnome_vfs_async_xfer (&operation_info->handle, source_uri_list, target_uri_list,
 		      		      move_options, GNOME_VFS_XFER_ERROR_MODE_QUERY, 
 		      		      GNOME_VFS_XFER_OVERWRITE_MODE_QUERY,
 				      GNOME_VFS_PRIORITY_DEFAULT,
-		      		      update_transfer_callback, transfer_info,
-		      		      sync_transfer_callback, sync_transfer_info);
+		      		      async_operation_callback, operation_info,
+		      		      sync_operation_callback, operation_info);
 	}
 
 	gnome_vfs_uri_list_free (source_uri_list);
@@ -2181,123 +2310,62 @@ nautilus_file_operations_copy_move (cons
 }
 
 typedef struct {
-	GnomeVFSAsyncHandle *handle;
-	NautilusNewFolderCallback done_callback;
-	gpointer data;
-	GtkWidget *parent_view;
-	IconPositionIterator *iterator;
-} NewFolderTransferState;
+	NautilusNewFileCallback callback;
+	gpointer callback_user_data;
+} NewFileInfo;
 
-static int
-handle_new_folder_vfs_error (const GnomeVFSXferProgressInfo *progress_info, NewFolderTransferState *state)
+static void
+get_new_file_uri (gpointer       key,
+		  gpointer       value,
+		  gpointer       user_data)
 {
-	const char *error_string;
-	char *error_string_to_free;
+	char *uri;
+	char **uri_out;
 
-	error_string_to_free = NULL;
+	uri = key;
+	uri_out = user_data;
 
-	if (progress_info->vfs_status == GNOME_VFS_ERROR_ACCESS_DENIED) {
-		error_string = _("You do not have permissions to write to the destination.");
-	} else if (progress_info->vfs_status == GNOME_VFS_ERROR_NO_SPACE) {
-		error_string = _("There is no space on the destination.");
-	} else {
-		error_string = g_strdup_printf (_("Error \"%s\" creating new folder."), 
-						gnome_vfs_result_to_string (progress_info->vfs_status));
-		error_string_to_free = (char *)error_string;
-	}
-	
-	eel_show_error_dialog (_("Error creating new folder."), error_string,
-				    GTK_WINDOW (gtk_widget_get_toplevel (state->parent_view)));
-	
-	g_free (error_string_to_free);
-	
-	return GNOME_VFS_XFER_ERROR_ACTION_ABORT;
+	*uri_out = uri;
 }
 
-static int
-new_folder_transfer_callback (GnomeVFSAsyncHandle *handle,
-			      GnomeVFSXferProgressInfo *progress_info,
-			      gpointer data)
-{
-	NewFolderTransferState *state;
-	char *temp_string;
-	char *new_uri;
-	
-	state = (NewFolderTransferState *) data;
+static void
+new_file_done_callback (GHashTable *debuting_uris,
+			gpointer user_data)
+{
+	NewFileInfo *new_file_info = (NewFileInfo *) user_data;
+	char *uri;
 
-	switch (progress_info->phase) {
+	g_assert (new_file_info != NULL);
 
-	case GNOME_VFS_XFER_PHASE_COMPLETED:
-		eel_remove_weak_pointer (&state->parent_view);
-		g_free (state);
-		return 0;
+	uri = NULL;
 
-	default:
-		switch (progress_info->status) {
-		case GNOME_VFS_XFER_PROGRESS_STATUS_OK:
-			nautilus_file_changes_consume_changes (TRUE);
-			new_uri = NULL;
-			if (progress_info->vfs_status == GNOME_VFS_OK) {
-				new_uri = progress_info->target_name;
-			}
-			(* state->done_callback) (new_uri,
-						  state->data);
-			return 1;
-	
-		case GNOME_VFS_XFER_PROGRESS_STATUS_DUPLICATE:
-	
-			temp_string = progress_info->duplicate_name;
-	
-			if (progress_info->vfs_status == GNOME_VFS_ERROR_NAME_TOO_LONG) {
-				/* special case an 8.3 file system */
-				progress_info->duplicate_name = g_strndup (temp_string, 8);
-				progress_info->duplicate_name[8] = '\0';
-				g_free (temp_string);
-				temp_string = progress_info->duplicate_name;
-				progress_info->duplicate_name = g_strdup_printf
-					("%s.%d", 
-					 progress_info->duplicate_name,
-					 progress_info->duplicate_count);
-			} else {
-				progress_info->duplicate_name = g_strdup_printf
-					("%s%%20%d", 
-					 progress_info->duplicate_name,
-					 progress_info->duplicate_count);
-			}
-			g_free (temp_string);
-
-			icon_position_iterator_update_uri
-				(state->iterator,
-				 progress_info->target_name,
-				 progress_info->duplicate_name);
+	if (debuting_uris != NULL) {
+		g_hash_table_foreach (debuting_uris, get_new_file_uri, &uri);
+	}
 
-			return GNOME_VFS_XFER_ERROR_ACTION_SKIP;
-	
-		case GNOME_VFS_XFER_PROGRESS_STATUS_VFSERROR:
-			return handle_new_folder_vfs_error (progress_info, state);
-		
-	
+	if (new_file_info->callback != NULL) {
+		(* new_file_info->callback) (uri, new_file_info->callback_user_data);
+	}
 
-		default:
-			g_warning (_("Unknown GnomeVFSXferProgressStatus %d"),
-				   progress_info->status);
-			return 0;
-		}
+	if (debuting_uris != NULL) {
+		g_hash_table_destroy (debuting_uris);
 	}
+
+	g_free (new_file_info);
 }
 
 void 
 nautilus_file_operations_new_folder (GtkWidget *parent_view, 
 				     GdkPoint *target_point,
 				     const char *parent_dir,
-				     NautilusNewFolderCallback done_callback,
-				     gpointer data)
+				     NautilusNewFileCallback done_callback,
+				     gpointer done_callback_user_data)
 {
 	GList *target_uri_list;
 	GnomeVFSURI *uri, *parent_uri;
 	char *text_uri, *dirname;
-	NewFolderTransferState *state;
-	SyncTransferInfo *sync_transfer_info;
+	OperationInfo *operation_info;
+	NewFileInfo *new_file_info;
 
 	/* pass in the target directory and the new folder name as a destination URI */
 	parent_uri = gnome_vfs_uri_new (parent_dir);
@@ -2310,165 +2378,34 @@ nautilus_file_operations_new_folder (Gtk
 
 	text_uri = gnome_vfs_uri_to_string (uri, GNOME_VFS_URI_HIDE_NONE);
 
-	sync_transfer_info = g_new (SyncTransferInfo, 1);
-	sync_transfer_info->iterator = icon_position_iterator_new_single
+	operation_info = operation_info_new (parent_view);
+	operation_info->iterator = icon_position_iterator_new_single
 		(target_point, text_uri,
 		 gdk_screen_get_number (gtk_widget_get_screen (parent_view)),
 		 FALSE);
-	sync_transfer_info->debuting_uris = NULL;
+	operation_info->kind = CREATE_FOLDER;
 
-	g_free (text_uri);
+	new_file_info = g_new (NewFileInfo, 1);
+	new_file_info->callback = done_callback;
+	new_file_info->callback_user_data = done_callback_user_data;
 
-	state = g_new (NewFolderTransferState, 1);
-	state->done_callback = done_callback;
-	state->data = data;
-	state->parent_view = parent_view;
-	state->iterator = sync_transfer_info->iterator;
-	eel_add_weak_pointer (&state->parent_view);
+	operation_info->done_callback = new_file_done_callback;
+	operation_info->done_callback_user_data = new_file_info;
 
-	gnome_vfs_async_xfer (&state->handle, NULL, target_uri_list,
+	g_free (text_uri);
+
+	gnome_vfs_async_xfer (&operation_info->handle, NULL, target_uri_list,
 	      		      GNOME_VFS_XFER_NEW_UNIQUE_DIRECTORY,
 	      		      GNOME_VFS_XFER_ERROR_MODE_QUERY, 
 	      		      GNOME_VFS_XFER_OVERWRITE_MODE_QUERY,
 			      GNOME_VFS_PRIORITY_DEFAULT,
-	      		      new_folder_transfer_callback, state,
-	      		      sync_transfer_callback, sync_transfer_info);
+	      		      async_operation_callback, operation_info,
+	      		      sync_operation_callback, operation_info);
 
 	gnome_vfs_uri_list_free (target_uri_list);
 	gnome_vfs_uri_unref (parent_uri);
 }
 
-typedef struct {
-	GnomeVFSAsyncHandle *handle;
-	NautilusNewFileCallback done_callback;
-	gpointer data;
-	GtkWidget *parent_view;
-	GHashTable *debuting_uris;
-	IconPositionIterator *iterator;
-} NewFileTransferState;
-
-
-static int
-handle_new_file_vfs_error (const GnomeVFSXferProgressInfo *progress_info, NewFileTransferState *state)
-{
-	const char *error_string;
-	char *error_string_to_free;
-
-	error_string_to_free = NULL;
-
-	if (progress_info->vfs_status == GNOME_VFS_ERROR_ACCESS_DENIED) {
-		error_string = _("You do not have permissions to write to the destination.");
-	} else if (progress_info->vfs_status == GNOME_VFS_ERROR_NO_SPACE) {
-		error_string = _("There is no space on the destination.");
-	} else {
-		error_string = g_strdup_printf (_("Error \"%s\" creating new document."), 
-						gnome_vfs_result_to_string (progress_info->vfs_status));
-		error_string_to_free = (char *)error_string;
-	}
-	
-	eel_show_error_dialog (_("Error creating new document."), error_string,
-			       GTK_WINDOW (gtk_widget_get_toplevel (state->parent_view)));
-	
-	g_free (error_string_to_free);
-	
-	return GNOME_VFS_XFER_ERROR_ACTION_ABORT;
-}
-
-static void
-get_new_file_uri (gpointer       key,
-		  gpointer       value,
-		  gpointer       user_data)
-{
-	char *uri;
-	char **uri_out;
-
-	uri = key;
-	uri_out = user_data;
-
-	*uri_out = uri;
-}
-
-
-static int
-new_file_transfer_callback (GnomeVFSAsyncHandle *handle,
-			      GnomeVFSXferProgressInfo *progress_info,
-			      gpointer data)
-{
-	NewFileTransferState *state;
-	char *temp_string;
-	char **temp_strings;
-	char *uri;
-	
-	state = (NewFileTransferState *) data;
-
-	switch (progress_info->phase) {
-
-	case GNOME_VFS_XFER_PHASE_COMPLETED:
-		uri = NULL;
-		
-		g_hash_table_foreach (state->debuting_uris,
-				      get_new_file_uri, &uri);
-
-		(* state->done_callback) (uri, state->data);
-		/* uri is owned by hashtable, don't free */
-		eel_remove_weak_pointer (&state->parent_view);
-		g_hash_table_destroy (state->debuting_uris);
-		g_free (state);
-		return 0;
-
-	default:
-		switch (progress_info->status) {
-		case GNOME_VFS_XFER_PROGRESS_STATUS_OK:
-			nautilus_file_changes_consume_changes (TRUE);
-			return 1;
-	
-		case GNOME_VFS_XFER_PROGRESS_STATUS_DUPLICATE:
-	
-			temp_string = progress_info->duplicate_name;
-	
-			if (progress_info->vfs_status == GNOME_VFS_ERROR_NAME_TOO_LONG) {
-				/* special case an 8.3 file system */
-				progress_info->duplicate_name = g_strndup (temp_string, 8);
-				progress_info->duplicate_name[8] = '\0';
-				g_free (temp_string);
-				temp_string = progress_info->duplicate_name;
-				progress_info->duplicate_name = g_strdup_printf
-					("%s.%d", 
-					 progress_info->duplicate_name,
-					 progress_info->duplicate_count);
-			} else {
-				temp_strings = g_strsplit (temp_string, ".", 2);
-				if (temp_strings[1] != NULL) {
-					progress_info->duplicate_name = g_strdup_printf
-						("%s%%20%d.%s", 
-						 temp_strings[0],
-						 progress_info->duplicate_count,
-						 temp_strings[1]);
-				} else {
-					progress_info->duplicate_name = g_strdup_printf
-						("%s%%20%d", 
-						 progress_info->duplicate_name,
-						 progress_info->duplicate_count);
-				}
-				g_strfreev (temp_strings);
-			}
-			g_free (temp_string);
-
-			return GNOME_VFS_XFER_ERROR_ACTION_SKIP;
-	
-		case GNOME_VFS_XFER_PROGRESS_STATUS_VFSERROR:
-			return handle_new_file_vfs_error (progress_info, state);
-		
-	
-
-		default:
-			g_warning (_("Unknown GnomeVFSXferProgressStatus %d"),
-				   progress_info->status);
-			return 0;
-		}
-	}
-}
-
 void 
 nautilus_file_operations_new_file_from_template (GtkWidget *parent_view, 
 						 GdkPoint *target_point,
@@ -2476,14 +2413,14 @@ nautilus_file_operations_new_file_from_t
 						 const char *target_filename,
 						 const char *template_uri,
 						 NautilusNewFileCallback done_callback,
-						 gpointer data)
+						 gpointer done_callback_user_data)
 {
 	GList *target_uri_list;
 	GList *source_uri_list;
 	GnomeVFSURI *target_uri, *parent_uri, *source_uri;
 	GnomeVFSXferOptions options;
-	NewFileTransferState *state;
-	SyncTransferInfo *sync_transfer_info;
+	OperationInfo *operation_info;
+	NewFileInfo *new_file_info;
 	char *tmp;
 
 	g_assert (parent_dir != NULL);
@@ -2494,7 +2431,7 @@ nautilus_file_operations_new_file_from_t
 
 	source_uri = gnome_vfs_uri_new (template_uri);
 	if (source_uri == NULL) {
-		(*done_callback) (NULL, data);
+		(*done_callback) (NULL, done_callback_user_data);
 		return;
 	}
 
@@ -2506,33 +2443,33 @@ nautilus_file_operations_new_file_from_t
 		g_free (tmp);
 	}
 
-	sync_transfer_info = g_new (SyncTransferInfo, 1);
-	sync_transfer_info->iterator = icon_position_iterator_new_single
+	operation_info = operation_info_new (parent_view);
+	operation_info->iterator = icon_position_iterator_new_single
 		(target_point, template_uri,
 		 gdk_screen_get_number (gtk_widget_get_screen (parent_view)),
 		 TRUE);
-	sync_transfer_info->debuting_uris = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+	operation_info->kind = CREATE_FILE;
+
+	new_file_info = g_new (NewFileInfo, 1);
+	new_file_info->callback = done_callback;
+	new_file_info->callback_user_data = done_callback_user_data;
 
-	state = g_new (NewFileTransferState, 1);
-	state->done_callback = done_callback;
-	state->data = data;
-	state->parent_view = parent_view;
-	state->iterator = sync_transfer_info->iterator;
-	state->debuting_uris = sync_transfer_info->debuting_uris;
-	eel_add_weak_pointer (&state->parent_view);
+	operation_info->done_callback = new_file_done_callback;
+	operation_info->done_callback_user_data = new_file_info;
 
 	target_uri_list = g_list_prepend (NULL, target_uri);
 	source_uri_list = g_list_prepend (NULL, source_uri);
 
 	options = GNOME_VFS_XFER_USE_UNIQUE_NAMES;
 
-	gnome_vfs_async_xfer (&state->handle, source_uri_list, target_uri_list,
+	gnome_vfs_async_xfer (&operation_info->handle,
+			      source_uri_list, target_uri_list,
 	      		      options,
 	      		      GNOME_VFS_XFER_ERROR_MODE_QUERY, 
 	      		      GNOME_VFS_XFER_OVERWRITE_MODE_QUERY,
 			      GNOME_VFS_PRIORITY_DEFAULT,
-			      new_file_transfer_callback, state,
-	      		      sync_transfer_callback, sync_transfer_info);
+			      async_operation_callback, operation_info,
+	      		      sync_operation_callback, operation_info);
 
 	gnome_vfs_uri_list_free (target_uri_list);
 	gnome_vfs_uri_list_free (source_uri_list);
@@ -2622,7 +2559,7 @@ nautilus_file_operations_delete (const G
 	const GList *p;
 	const char *item_uri;
 	NautilusFile *file;
-	TransferInfo *transfer_info;
+	OperationInfo *operation_info;
 
 	uri_list = NULL;
 	for (p = item_uris; p != NULL; p = p->next) {
@@ -2656,28 +2593,28 @@ nautilus_file_operations_delete (const G
 		return;
 	}
 
-	transfer_info = transfer_info_new (parent_view);
+	operation_info = operation_info_new (parent_view);
 
 	/* localizers: progress dialog title */
-	transfer_info->operation_title = _("Deleting files");
+	operation_info->operation_title = _("Deleting files");
 	/* localizers: label prepended to the progress count */
-	transfer_info->action_label =_("Files deleted:");
+	operation_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 ="";
-
-	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;
+	operation_info->progress_verb =_("Deleting");
+	operation_info->preparation_name =_("Preparing to Delete files...");
+	operation_info->cleanup_name ="";
+
+	operation_info->error_mode = GNOME_VFS_XFER_ERROR_MODE_QUERY;
+	operation_info->overwrite_mode = GNOME_VFS_XFER_OVERWRITE_MODE_REPLACE;
+	operation_info->kind = TRANSFER_DELETE;
 	
-	gnome_vfs_async_xfer (&transfer_info->handle, uri_list,  NULL,
+	gnome_vfs_async_xfer (&operation_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);
+	      		      async_operation_callback, operation_info,
+	      		      sync_operation_callback, operation_info);
 
 	gnome_vfs_uri_list_free (uri_list);
 }
@@ -2685,33 +2622,33 @@ nautilus_file_operations_delete (const G
 static void
 do_empty_trash (GtkWidget *parent_view)
 {
-	TransferInfo *transfer_info;
+	OperationInfo *operation_info;
 	GList *trash_dir_list;
 
 	trash_dir_list = nautilus_trash_monitor_get_trash_directories ();
 	if (trash_dir_list != NULL) {
 		/* set up the move parameters */
-		transfer_info = transfer_info_new (parent_view);
+		operation_info = operation_info_new (parent_view);
 
 		/* localizers: progress dialog title */
-		transfer_info->operation_title = _("Emptying the Trash");
+		operation_info->operation_title = _("Emptying the Trash");
 		/* localizers: label prepended to the progress count */
-		transfer_info->action_label =_("Files deleted:");
+		operation_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 Empty the Trash...");
-		transfer_info->cleanup_name ="";
-		transfer_info->error_mode = GNOME_VFS_XFER_ERROR_MODE_QUERY;
-		transfer_info->overwrite_mode = GNOME_VFS_XFER_OVERWRITE_MODE_REPLACE;
-		transfer_info->kind = TRANSFER_EMPTY_TRASH;
+		operation_info->progress_verb =_("Deleting");
+		operation_info->preparation_name =_("Preparing to Empty the Trash...");
+		operation_info->cleanup_name ="";
+		operation_info->error_mode = GNOME_VFS_XFER_ERROR_MODE_QUERY;
+		operation_info->overwrite_mode = GNOME_VFS_XFER_OVERWRITE_MODE_REPLACE;
+		operation_info->kind = TRANSFER_EMPTY_TRASH;
 
-		gnome_vfs_async_xfer (&transfer_info->handle, trash_dir_list, NULL,
+		gnome_vfs_async_xfer (&operation_info->handle, trash_dir_list, NULL,
 		      		      GNOME_VFS_XFER_EMPTY_DIRECTORIES,
 		      		      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);
+		      		      async_operation_callback, operation_info,
+		      		      sync_operation_callback, operation_info);
 	}
 
 	gnome_vfs_uri_list_free (trash_dir_list);

Attachment: signature.asc
Description: Dies ist ein digital signierter Nachrichtenteil



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]