nautilus-file-operations.c 71.6 KB
Newer Older
Ettore Perazzoli's avatar
Ettore Perazzoli committed
1
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2

3
/* nautilus-file-operations.c - Nautilus file operations.
Ettore Perazzoli's avatar
Ettore Perazzoli committed
4

Elliot Lee's avatar
Elliot Lee committed
5
   Copyright (C) 1999, 2000 Free Software Foundation
6
   Copyright (C) 2000, 2001 Eazel, Inc.
Ettore Perazzoli's avatar
Ettore Perazzoli committed
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License as
   published by the Free Software Foundation; either version 2 of the
   License, or (at your option) any later version.
   
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.
   
   You should have received a copy of the GNU General Public
   License along with this program; if not, write to the
   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.
   
23 24
   Authors: Ettore Perazzoli <ettore@gnu.org> 
            Pavel Cisler <pavel@eazel.com> 
25
 */
Ettore Perazzoli's avatar
Ettore Perazzoli committed
26 27

#include <config.h>
28
#include <string.h>
29
#include "nautilus-file-operations.h"
Ettore Perazzoli's avatar
Ettore Perazzoli committed
30

31 32
#include "nautilus-file-operations-progress.h"
#include "nautilus-lib-self-check-functions.h"
Ramiro Estrugo's avatar
Ramiro Estrugo committed
33 34 35 36

#include <eel/eel-glib-extensions.h>
#include <eel/eel-gtk-extensions.h>
#include <eel/eel-stock-dialogs.h>
Ramiro Estrugo's avatar
Ramiro Estrugo committed
37 38
#include <eel/eel-vfs-extensions.h>

Ettore Perazzoli's avatar
Ettore Perazzoli committed
39
#include <gnome.h>
40
#include <gtk/gtklabel.h>
41
#include <libgnomevfs/gnome-vfs-async-ops.h>
42
#include <libgnomevfs/gnome-vfs-find-directory.h>
43 44
#include <libgnomevfs/gnome-vfs-ops.h>
#include <libgnomevfs/gnome-vfs-result.h>
Pavel Cisler's avatar
Pavel Cisler committed
45
#include <libgnomevfs/gnome-vfs-uri.h>
46
#include <libgnomevfs/gnome-vfs-utils.h>
47
#include "nautilus-file-changes-queue.h"
Ramiro Estrugo's avatar
Ramiro Estrugo committed
48 49 50
#include "nautilus-global-preferences.h"
#include "nautilus-link.h"
#include "nautilus-trash-monitor.h"
Ettore Perazzoli's avatar
Ettore Perazzoli committed
51

52
typedef enum {
53 54 55 56 57 58 59 60
	TRANSFER_MOVE,
	TRANSFER_COPY,
	TRANSFER_DUPLICATE,
	TRANSFER_MOVE_TO_TRASH,
	TRANSFER_EMPTY_TRASH,
	TRANSFER_DELETE,
	TRANSFER_LINK
} TransferKind;
61

62 63
/* Copy engine callback state */
typedef struct {
Ettore Perazzoli's avatar
Ettore Perazzoli committed
64
	GnomeVFSAsyncHandle *handle;
65
	NautilusFileOperationsProgress *progress_dialog;
Pavel Cisler's avatar
Pavel Cisler committed
66
	const char *operation_title;	/* "Copying files" */
67
	const char *action_label;	/* "Files copied:" */
Pavel Cisler's avatar
Pavel Cisler committed
68 69 70
	const char *progress_verb;	/* "Copying" */
	const char *preparation_name;	/* "Preparing To Copy..." */
	const char *cleanup_name;	/* "Finishing Move..." */
Ettore Perazzoli's avatar
Ettore Perazzoli committed
71 72
	GnomeVFSXferErrorMode error_mode;
	GnomeVFSXferOverwriteMode overwrite_mode;
Pavel Cisler's avatar
Pavel Cisler committed
73
	GtkWidget *parent_view;
74
	TransferKind kind;
75
	gboolean show_progress_dialog;
76
	void (* done_callback) (GHashTable *debuting_uris, gpointer data);
77
	gpointer done_callback_data;
78 79
	GHashTable *debuting_uris;
	gboolean cancelled;	
80
} TransferInfo;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
81

82 83 84 85 86 87 88 89
static TransferInfo *
transfer_info_new (GtkWidget *parent_view)
{
	TransferInfo *result;
	
	result = g_new0 (TransferInfo, 1);
	result->parent_view = parent_view;
	
Ramiro Estrugo's avatar
Ramiro Estrugo committed
90
	eel_nullify_when_destroyed (&result->parent_view);
91 92 93 94 95 96 97
	
	return result;
}

static void
transfer_info_destroy (TransferInfo *transfer_info)
{
Ramiro Estrugo's avatar
Ramiro Estrugo committed
98
	eel_nullify_cancel (&transfer_info->parent_view);
99 100
	
	if (transfer_info->progress_dialog != NULL) {
101
		nautilus_file_operations_progress_done (transfer_info->progress_dialog);
102 103 104
	}
	
	if (transfer_info->debuting_uris != NULL) {
105
		g_hash_table_destroy (transfer_info->debuting_uris);
106 107 108 109 110
	}
	
	g_free (transfer_info);
}

111 112 113 114 115 116 117 118 119 120 121 122
/* Struct used to control applying icon positions to 
 * top level items during a copy, drag, new folder creation and
 * link creation
 */
typedef struct {
	GdkPoint *icon_positions;
	int last_icon_position_index;
	GList *uris;
	const GList *last_uri;
} IconPositionIterator;

static IconPositionIterator *
123
icon_position_iterator_new (GArray *icon_positions, const GList *uris)
124 125
{
	IconPositionIterator *result;
126
	guint index;
127

128
	g_assert (icon_positions->len == g_list_length ((GList *)uris));
129 130
	result = g_new (IconPositionIterator, 1);
	
131
	/* make our own copy of the icon locations */
132
	result->icon_positions = g_new (GdkPoint, icon_positions->len);
133 134 135
	for (index = 0; index < icon_positions->len; index++) {
		result->icon_positions[index] = g_array_index (icon_positions, GdkPoint, index);
	}
136 137
	result->last_icon_position_index = 0;

Ramiro Estrugo's avatar
Ramiro Estrugo committed
138
	result->uris = eel_g_str_list_copy ((GList *)uris);
139 140 141 142 143 144 145 146 147 148 149 150 151
	result->last_uri = result->uris;

	return result;
}

static void
icon_position_iterator_free (IconPositionIterator *position_iterator)
{
	if (position_iterator == NULL) {
		return;
	}
	
	g_free (position_iterator->icon_positions);
Ramiro Estrugo's avatar
Ramiro Estrugo committed
152
	eel_g_list_free_deep (position_iterator->uris);
153 154 155
	g_free (position_iterator);
}

156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
static gboolean
icon_position_iterator_get_next (IconPositionIterator *position_iterator,
				 const char *next_uri,
				 GdkPoint *point)
{
	if (position_iterator == NULL) {
		return FALSE;
	}
		
	for (;;) {
		if (position_iterator->last_uri == NULL) {
			/* we are done, no more points left */
			return FALSE;
		}

		/* Scan for the next point that matches the source_name
		 * uri.
		 */
		if (strcmp ((const char *) position_iterator->last_uri->data, 
			    next_uri) == 0) {
			break;
		}
		
		/* Didn't match -- a uri must have been skipped by the copy 
		 * engine because of a name conflict. All we need to do is 
		 * skip ahead too.
		 */
		position_iterator->last_uri = position_iterator->last_uri->next;
		position_iterator->last_icon_position_index++; 
	}

	/* apply the location to the target file */
	*point = position_iterator->icon_positions
		[position_iterator->last_icon_position_index];

	/* advance to the next point for next time */
	position_iterator->last_uri = position_iterator->last_uri->next;
	position_iterator->last_icon_position_index++; 

	return TRUE;
}

198 199
#if GNOME2_CONVERSION_COMPLETE

200 201
static GdkFont *
get_label_font (void)
202
{
203 204
	GtkWidget *label;
	GtkStyle *style;
205 206 207 208 209 210
	GdkFont *font;

	/* FIXME: Does this really pick up the correct font if the rc
	 * file has a special case for the particular dialog we are
	 * preparing?
	 */
211 212 213

	label = gtk_label_new ("");
	style = gtk_widget_get_style (label);
214 215 216
	font = style->font;
	gdk_font_ref (font);
	gtk_object_sink (GTK_OBJECT (label));
217

218
	return font;
219 220
}

221 222
#endif

223
static char *
224
ellipsize_string_for_dialog (const char *str)
225
{
226 227
	char *result;
#ifdef GNOME2_CONVERSION_COMPLETE
228
	GdkFont *font;
229
	int maximum_width;
230

231 232 233 234 235 236 237 238 239 240 241 242 243 244
	/* I believe the most appropriate fix here is to change it to be based on
	 * some number of characters, rather than on a pixel width.
	 * We just can't do anything sane with the pixel width since
	 * we don't have all the PangoLayout information.
	 *
	 * So in brief we want an ellipsize function in eel which
	 * takes a number of chars, computes log attrs for the string,
	 * and chops the string at one of the is_cursor_position points,
	 * such that the string has fewer chars than the given number.
	 *
	 * Code in nautilus-news.c implements this in a UTF-8-unsafe way,
	 * should be moved to Eel and used here as well.
	 */
	
245
	/* get a nice length to ellipsize to, based on the font */
246
	font = get_label_font ();
247
	maximum_width = gdk_string_width (font, "MMMMMMMMMMMMMMMMMMMMMM");
248 249

	result = eel_string_ellipsize (str, font, maximum_width, EEL_ELLIPSIZE_MIDDLE);
250
	gdk_font_unref (font);
251 252 253
#else
	result = g_strdup (str);
#endif
254

255 256
	return result;
}
257

258 259 260 261
static char *
format_and_ellipsize_uri_for_dialog (const char *uri)
{
	char *unescaped, *result;
262

Ramiro Estrugo's avatar
Ramiro Estrugo committed
263
	unescaped = eel_format_uri_for_display (uri);
264
	result = ellipsize_string_for_dialog (unescaped);
265
	g_free (unescaped);
266

267 268 269 270
	return result;
}

static char *
271
extract_and_ellipsize_file_name_for_dialog (const char *uri)
272
{
273
	char *basename;
274
	char *unescaped, *result;
275
	
276 277
	basename = g_path_get_basename (uri);
	g_return_val_if_fail (basename != NULL, NULL);
278

279
	unescaped = gnome_vfs_unescape_string_for_display (basename);
280 281
	result = ellipsize_string_for_dialog (unescaped);
	g_free (unescaped);
282
	g_free (basename);
283

284 285 286
	return result;
}

287
static GtkWidget *
288
parent_for_error_dialog (TransferInfo *transfer_info)
289
{
290
	if (transfer_info->progress_dialog != NULL) {
291
		return GTK_WIDGET (transfer_info->progress_dialog);
292 293
	}

294
	return transfer_info->parent_view;
295 296
}

297 298 299 300 301 302 303 304
static void
handle_response_callback (GtkDialog *dialog, int response, TransferInfo *transfer_info)
{
	transfer_info->cancelled = TRUE;
}

static void
handle_close_callback (GtkDialog *dialog, TransferInfo *transfer_info)
305
{
306
	transfer_info->cancelled = TRUE;
307 308
}

Ettore Perazzoli's avatar
Ettore Perazzoli committed
309
static void
310
create_transfer_dialog (const GnomeVFSXferProgressInfo *progress_info,
311
			TransferInfo *transfer_info)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
312
{
313
	if (!transfer_info->show_progress_dialog) {
314
		return;
315
	}
316

317
	g_return_if_fail (transfer_info->progress_dialog == NULL);
Ettore Perazzoli's avatar
Ettore Perazzoli committed
318

319
	transfer_info->progress_dialog = nautilus_file_operations_progress_new 
320
		(transfer_info->operation_title, "", "", "", 0, 0);
Ettore Perazzoli's avatar
Ettore Perazzoli committed
321

322 323 324
	/* Treat clicking on the close box or use of the escape key
	 * the same as clicking cancel.
	 */
325
	g_signal_connect (transfer_info->progress_dialog,
326 327
			  "response",
			  G_CALLBACK (handle_response_callback),
328
			  transfer_info);
329
	g_signal_connect (transfer_info->progress_dialog,
330 331 332
			  "close",
			  G_CALLBACK (handle_close_callback),
			  transfer_info);
Ettore Perazzoli's avatar
Ettore Perazzoli committed
333

334
	/* Make the progress dialog show up over the window we are copying into */
335
	if (transfer_info->parent_view != NULL) {
336 337 338
		gtk_window_set_transient_for (
			GTK_WINDOW (transfer_info->progress_dialog), 
			GTK_WINDOW (gtk_widget_get_toplevel (transfer_info->parent_view)));
339
	}
340 341

	gtk_widget_show (GTK_WIDGET (transfer_info->progress_dialog));
Ettore Perazzoli's avatar
Ettore Perazzoli committed
342 343
}

Pavel Cisler's avatar
Pavel Cisler committed
344
static void
345
progress_dialog_set_to_from_item_text (NautilusFileOperationsProgress *dialog,
346 347 348
				       const char *progress_verb,
				       const char *from_uri, const char *to_uri, 
				       gulong index, gulong size)
Pavel Cisler's avatar
Pavel Cisler committed
349 350 351 352 353 354 355 356
{
	char *item;
	char *from_path;
	char *to_path;
	char *progress_label_text;
	const char *from_prefix;
	const char *to_prefix;
	GnomeVFSURI *uri;
357
	int length;
Pavel Cisler's avatar
Pavel Cisler committed
358 359 360 361 362 363 364 365 366 367 368 369

	item = NULL;
	from_path = NULL;
	to_path = NULL;
	from_prefix = "";
	to_prefix = "";
	progress_label_text = NULL;

	if (from_uri != NULL) {
		uri = gnome_vfs_uri_new (from_uri);
		item = gnome_vfs_uri_extract_short_name (uri);
		from_path = gnome_vfs_uri_extract_dirname (uri);
370 371 372 373 374 375 376

		/* remove the last '/' */
		length = strlen (from_path);
		if (from_path [length - 1] == '/') {
			from_path [length - 1] = '\0';
		}
		
Pavel Cisler's avatar
Pavel Cisler committed
377 378 379
		gnome_vfs_uri_unref (uri);
		g_assert (progress_verb);
		progress_label_text = g_strdup_printf ("%s:", progress_verb);
380
		/* "From" dialog label, source path gets placed next to it in the dialog */
Pavel Cisler's avatar
Pavel Cisler committed
381 382 383 384
		from_prefix = _("From:");
	}

	if (to_uri != NULL) {
385
		uri = gnome_vfs_uri_new (to_uri);
Pavel Cisler's avatar
Pavel Cisler committed
386
		to_path = gnome_vfs_uri_extract_dirname (uri);
387 388 389 390 391 392 393

		/* remove the last '/' */
		length = strlen (to_path);
		if (to_path [length - 1] == '/') {
			to_path [length - 1] = '\0';
		}

Pavel Cisler's avatar
Pavel Cisler committed
394
		gnome_vfs_uri_unref (uri);
395
		/* "To" dialog label, source path gets placed next to it in the dialog */
Pavel Cisler's avatar
Pavel Cisler committed
396 397 398
		to_prefix = _("To:");
	}

399 400 401 402 403 404 405
	nautilus_file_operations_progress_new_file
		(dialog,
		 progress_label_text != NULL ? progress_label_text : "",
		 item != NULL ? item : "",
		 from_path != NULL ? from_path : "",
		 to_path != NULL ? to_path : "",
		 from_prefix, to_prefix, index, size);
Pavel Cisler's avatar
Pavel Cisler committed
406 407 408 409 410 411 412

	g_free (progress_label_text);
	g_free (item);
	g_free (from_path);
	g_free (to_path);
}

Pavel Cisler's avatar
Pavel Cisler committed
413
static int
414
handle_transfer_ok (const GnomeVFSXferProgressInfo *progress_info,
415
		    TransferInfo *transfer_info)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
416
{
417 418
	if (transfer_info->cancelled
		&& progress_info->phase != GNOME_VFS_XFER_PHASE_COMPLETED) {
419 420
		/* If cancelled, delete any partially copied files that are laying
		 * around and return.
421
		 */
422 423
		if (progress_info->target_name != NULL
		    && progress_info->bytes_total != progress_info->bytes_copied) {
424
			GList *delete_me;
425

426
			delete_me = g_list_prepend (NULL, progress_info->target_name);
427 428 429 430
			nautilus_file_operations_delete (delete_me, transfer_info->parent_view);
			g_list_free (delete_me);
		}

431 432 433
		return 0;
	}
	
Ettore Perazzoli's avatar
Ettore Perazzoli committed
434
	switch (progress_info->phase) {
Pavel Cisler's avatar
Pavel Cisler committed
435
	case GNOME_VFS_XFER_PHASE_INITIAL:
436
		create_transfer_dialog (progress_info, transfer_info);
437
		return 1;
Pavel Cisler's avatar
Pavel Cisler committed
438 439

	case GNOME_VFS_XFER_PHASE_COLLECTING:
440
		if (transfer_info->progress_dialog != NULL) {
441
			nautilus_file_operations_progress_set_operation_string
442 443
				(transfer_info->progress_dialog,
				 transfer_info->preparation_name);
444
		}
445
		return 1;
Pavel Cisler's avatar
Pavel Cisler committed
446 447

	case GNOME_VFS_XFER_PHASE_READYTOGO:
448
		if (transfer_info->progress_dialog != NULL) {
449 450 451
			nautilus_file_operations_progress_set_operation_string
				(transfer_info->progress_dialog,
				 transfer_info->action_label);
452
			nautilus_file_operations_progress_set_total
453 454 455
				(transfer_info->progress_dialog,
				 progress_info->files_total,
				 progress_info->bytes_total);
456
		}
457
		return 1;
Pavel Cisler's avatar
Pavel Cisler committed
458
				 
459
	case GNOME_VFS_XFER_PHASE_DELETESOURCE:
460
		nautilus_file_changes_consume_changes (FALSE);
461
		if (transfer_info->progress_dialog != NULL) {
462 463 464 465 466 467 468
			progress_dialog_set_to_from_item_text
				(transfer_info->progress_dialog,
				 transfer_info->progress_verb,
				 progress_info->source_name,
				 NULL,
				 progress_info->file_index,
				 progress_info->file_size);
Pavel Cisler's avatar
Pavel Cisler committed
469

470
			nautilus_file_operations_progress_update_sizes
471
				(transfer_info->progress_dialog,
472
				 MIN (progress_info->bytes_copied, 
473
				      progress_info->bytes_total),
474
				 MIN (progress_info->total_bytes_copied,
475
				      progress_info->bytes_total));
476
		}
477
		return 1;
478 479

	case GNOME_VFS_XFER_PHASE_MOVING:
Pavel Cisler's avatar
Pavel Cisler committed
480 481
	case GNOME_VFS_XFER_PHASE_OPENSOURCE:
	case GNOME_VFS_XFER_PHASE_OPENTARGET:
482 483
		/* fall through */
	case GNOME_VFS_XFER_PHASE_COPYING:
484
		if (transfer_info->progress_dialog != NULL) {
485
			if (progress_info->bytes_copied == 0) {
486 487 488 489 490 491 492
				progress_dialog_set_to_from_item_text
					(transfer_info->progress_dialog,
					 transfer_info->progress_verb,
					 progress_info->source_name,
					 progress_info->target_name,
					 progress_info->file_index,
					 progress_info->file_size);
493
			} else {
494
				nautilus_file_operations_progress_update_sizes
495
					(transfer_info->progress_dialog,
496
					 MIN (progress_info->bytes_copied, 
497
					      progress_info->bytes_total),
498
					 MIN (progress_info->total_bytes_copied,
499
					      progress_info->bytes_total));
500
			}
Ettore Perazzoli's avatar
Ettore Perazzoli committed
501
		}
502
		return 1;
Pavel Cisler's avatar
Pavel Cisler committed
503

504
	case GNOME_VFS_XFER_PHASE_CLEANUP:
505
		if (transfer_info->progress_dialog != NULL) {
506 507
			nautilus_file_operations_progress_clear
				(transfer_info->progress_dialog);
508
			nautilus_file_operations_progress_set_operation_string
509 510
				(transfer_info->progress_dialog,
				 transfer_info->cleanup_name);
511
		}
512
		return 1;
513

Ettore Perazzoli's avatar
Ettore Perazzoli committed
514
	case GNOME_VFS_XFER_PHASE_COMPLETED:
515
		nautilus_file_changes_consume_changes (TRUE);
516
		if (transfer_info->done_callback != NULL) {
517 518 519 520
			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;
521
		}
522 523

		transfer_info_destroy (transfer_info);
524
		return 1;
Pavel Cisler's avatar
Pavel Cisler committed
525

Ettore Perazzoli's avatar
Ettore Perazzoli committed
526
	default:
527
		return 1;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
528 529 530
	}
}

531 532 533 534 535 536 537 538
typedef enum {
	ERROR_READ_ONLY,
	ERROR_NOT_READABLE,
	ERROR_NOT_WRITABLE,
	ERROR_NOT_ENOUGH_PERMISSIONS,
	ERROR_NO_SPACE,
	ERROR_OTHER
} NautilusFileOperationsErrorKind;
539

540 541 542 543 544 545 546 547 548 549 550
typedef enum {
	ERROR_LOCATION_UNKNOWN,
	ERROR_LOCATION_SOURCE,
	ERROR_LOCATION_SOURCE_PARENT,
	ERROR_LOCATION_SOURCE_OR_PARENT,
	ERROR_LOCATION_TARGET
} NautilusFileOperationsErrorLocation;


static char *
build_error_string (const char *source_name, const char *target_name,
551 552 553 554
		    TransferKind operation_kind,
		    NautilusFileOperationsErrorKind error_kind,
		    NautilusFileOperationsErrorLocation error_location,
		    GnomeVFSResult error)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
555
{
556 557 558 559 560 561
	/* Avoid clever message composing here, just use brute force and
	 * duplicate the different flavors of error messages for all the
	 * possible permutations.
	 * That way localizers have an easier time and can even rearrange the
	 * order of the words in the messages easily.
	 */
Ettore Perazzoli's avatar
Ettore Perazzoli committed
562

563
	const char *error_string;
564
	char *result;
Pavel Cisler's avatar
Pavel Cisler committed
565

566 567
	error_string = NULL;
	result = NULL;
Pavel Cisler's avatar
Pavel Cisler committed
568

569
	if (error_location == ERROR_LOCATION_SOURCE_PARENT) {
570

571
		switch (operation_kind) {
572 573
		case TRANSFER_MOVE:
		case TRANSFER_MOVE_TO_TRASH:
574
			if (error_kind == ERROR_READ_ONLY) {
575
				error_string = _("Error while moving.\n\n"
576 577
						 "\"%s\" cannot be moved because it is on "
						 "a read-only disk.");
578 579
			}
			break;
580

581 582
		case TRANSFER_DELETE:
		case TRANSFER_EMPTY_TRASH:
583 584 585
			switch (error_kind) {
			case ERROR_NOT_ENOUGH_PERMISSIONS:
			case ERROR_NOT_WRITABLE:
586
				error_string = _("Error while deleting.\n\n"
587
						 "\"%s\" cannot be deleted because you do not have "
588 589 590 591
						 "permissions to modify its parent folder.");
				break;
			
			case ERROR_READ_ONLY:
592
				error_string = _("Error while deleting.\n\n"
593 594 595 596 597 598
						 "\"%s\" cannot be deleted because it is on "
						 "a read-only disk.");
				break;

			default:
				break;
599 600 601 602 603 604 605 606 607 608 609
			}
			break;

		default:
			g_assert_not_reached ();
			break;
		}
		
		if (error_string != NULL) {
			g_assert (source_name != NULL);
			result = g_strdup_printf (error_string, source_name);
610
		}
611

612
	} else if (error_location == ERROR_LOCATION_SOURCE_OR_PARENT) {
613

614 615
		g_assert (source_name != NULL);

616 617 618 619
		/* FIXME: Would be better if we could distinguish source vs parent permissions
		 * better somehow. The GnomeVFS copy engine would have to do some snooping
		 * after the failure in this case.
		 */
620
		switch (operation_kind) {
621
		case TRANSFER_MOVE:
622
			if (error_kind == ERROR_NOT_ENOUGH_PERMISSIONS) {
623
				error_string = _("Error while moving.\n\n"
624 625 626
						 "\"%s\" cannot be moved because you do not have "
						 "permissions to change it or its parent folder.");
			}
627
			break;
628
		case TRANSFER_MOVE_TO_TRASH:
629
			if (error_kind == ERROR_NOT_ENOUGH_PERMISSIONS) {
630
				error_string = _("Error while moving.\n\n"
631
						 "\"%s\" cannot be moved to the Trash because you do not have "
632 633
						 "permissions to change it or its parent folder.");
			}
634
			break;
635

636
		default:
637
			g_assert_not_reached ();
638 639
			break;
		}
640

641 642 643 644
		if (error_string != NULL) {
			g_assert (source_name != NULL);
			result = g_strdup_printf (error_string, source_name);
		}
645

646
	} else if (error_location == ERROR_LOCATION_SOURCE) {
647

648 649 650
		g_assert (source_name != NULL);

		switch (operation_kind) {
651 652
		case TRANSFER_COPY:
		case TRANSFER_DUPLICATE:
653
			if (error_kind == ERROR_NOT_READABLE) {
654
				error_string = _("Error while copying.\n\n"
655 656
						 "\"%s\" cannot be copied because you do not have "
						 "permissions to read it.");
657 658
			}
			break;
659

660 661 662
		default:
			g_assert_not_reached ();
			break;
663
		}
664

665 666 667
		if (error_string != NULL) {
			g_assert (source_name != NULL);
			result = g_strdup_printf (error_string, source_name);
668
		}
Pavel Cisler's avatar
Pavel Cisler committed
669

670 671 672 673
	} else if (error_location == ERROR_LOCATION_TARGET) {

		if (error_kind == ERROR_NO_SPACE) {
			switch (operation_kind) {
674 675
			case TRANSFER_COPY:
			case TRANSFER_DUPLICATE:
676
				error_string = _("Error while copying to \"%s\".\n\n"
677
				   		 "There is not enough space on the destination.");
678
				break;
679 680
			case TRANSFER_MOVE_TO_TRASH:
			case TRANSFER_MOVE:
681
				error_string = _("Error while moving to \"%s\".\n\n"
682
				   		 "There is not enough space on the destination.");
683
				break;
684
			case TRANSFER_LINK:
685
				error_string = _("Error while creating link in \"%s\".\n\n"
686
				   		 "There is not enough space on the destination.");
687 688 689 690 691 692 693
				break;
			default:
				g_assert_not_reached ();
				break;
			}
		} else {
			switch (operation_kind) {
694 695
			case TRANSFER_COPY:
			case TRANSFER_DUPLICATE:
696
				if (error_kind == ERROR_NOT_ENOUGH_PERMISSIONS) {
697
					error_string = _("Error while copying to \"%s\".\n\n"
698
					   		 "You do not have permissions to write to "
699
					   		 "this folder.");
700
				} else if (error_kind == ERROR_NOT_WRITABLE) {
701
					error_string = _("Error while copying to \"%s\".\n\n"
702
					   		 "The destination disk is read-only.");
703 704
				} 
				break;
705 706
			case TRANSFER_MOVE:
			case TRANSFER_MOVE_TO_TRASH:
707
				if (error_kind == ERROR_NOT_ENOUGH_PERMISSIONS) {
708
					error_string = _("Error while moving items to \"%s\".\n\n"
709
					   		 "You do not have permissions to write to "
710
					   		 "this folder.");
711
				} else if (error_kind == ERROR_NOT_WRITABLE) {
712
					error_string = _("Error while moving items to \"%s\".\n\n"
713
					   		 "The destination disk is read-only.");
714 715 716
				} 

				break;
717
			case TRANSFER_LINK:
718
				if (error_kind == ERROR_NOT_ENOUGH_PERMISSIONS) {
719
					error_string = _("Error while creating links in \"%s\".\n\n"
720
					   		 "You do not have permissions to write to "
721
					   		 "this folder.");
722
				} else if (error_kind == ERROR_NOT_WRITABLE) {
723
					error_string = _("Error while creating links in \"%s\".\n\n"
724
					   		 "The destination disk is read-only.");
725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741
				} 
				break;
			default:
				g_assert_not_reached ();
				break;
			}
		}
		if (error_string != NULL) {
			g_assert (target_name != NULL);
			result = g_strdup_printf (error_string, target_name);
		}
	}
	
	if (result == NULL) {
		/* None of the specific error messages apply, use a catch-all
		 * generic error
		 */
742 743
		g_message ("Hit unexpected error \"%s\" while doing a file operation.",
			   gnome_vfs_result_to_string (error));
744 745 746 747

		/* FIXMEs: we need to consider a single item
		 * move/copy and not offer to continue in that case
		 */
748 749
		if (source_name != NULL) {
			switch (operation_kind) {
750 751
			case TRANSFER_COPY:
			case TRANSFER_DUPLICATE:
752
				error_string = _("Error \"%s\" while copying \"%s\".\n\n"
753 754
						 "Would you like to continue?");
				break;
755
			case TRANSFER_MOVE:
756
				error_string = _("Error \"%s\" while moving \"%s\".\n\n"
757 758
						 "Would you like to continue?");
				break;
759
			case TRANSFER_LINK:
760
				error_string = _("Error \"%s\" while creating a link to \"%s\".\n\n"
761 762
						 "Would you like to continue?");
				break;
763 764 765
			case TRANSFER_DELETE:
			case TRANSFER_EMPTY_TRASH:
			case TRANSFER_MOVE_TO_TRASH:
766
				error_string = _("Error \"%s\" while deleting \"%s\".\n\n"
767 768 769
						 "Would you like to continue?");
				break;
			default:
770
				g_assert_not_reached ();
771 772 773
				break;
			}
	
774
			result = g_strdup_printf (error_string, 
775 776
						  gnome_vfs_result_to_string (error),
						  source_name);
777
		} else {
778
			switch (operation_kind) {
779 780
			case TRANSFER_COPY:
			case TRANSFER_DUPLICATE:
781
				error_string = _("Error \"%s\" while copying.\n\n"
782 783
						 "Would you like to continue?");
				break;
784
			case TRANSFER_MOVE:
785
				error_string = _("Error \"%s\" while moving.\n\n"
786 787
						 "Would you like to continue?");
				break;
788
			case TRANSFER_LINK:
789
				error_string = _("Error \"%s\" while linking.\n\n"
790 791
						 "Would you like to continue?");
				break;
792 793 794
			case TRANSFER_DELETE:
			case TRANSFER_EMPTY_TRASH:
			case TRANSFER_MOVE_TO_TRASH:
795
				error_string = _("Error \"%s\" while deleting.\n\n"
796 797 798
						 "Would you like to continue?");
				break;
			default:
799
				g_assert_not_reached ();
800 801 802
				break;
			}
	
803
			result = g_strdup_printf (error_string, 
804
						  gnome_vfs_result_to_string (error));
805
		}
806 807 808
	}
	return result;
}
809

810
static int
811
handle_transfer_vfs_error (const GnomeVFSXferProgressInfo *progress_info,
812
			   TransferInfo *transfer_info)
813
{
814
	/* Notice that the error mode in `transfer_info' is the one we have been
815 816 817
         * requested, but the transfer is always performed in mode
         * `GNOME_VFS_XFER_ERROR_MODE_QUERY'.
         */
818

819 820 821
	int error_dialog_button_pressed;
	int error_dialog_result;
	char *text;
822 823
	char *formatted_source_name;