nautilus-file-operations.c 72.8 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 "nautilus-file-operations.h"
Ettore Perazzoli's avatar
Ettore Perazzoli committed
29

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

#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
36 37
#include <eel/eel-vfs-extensions.h>

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

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

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

81 82 83 84 85 86 87 88
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
89
	eel_nullify_when_destroyed (&result->parent_view);
90 91 92 93 94 95 96
	
	return result;
}

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

110 111 112 113 114 115 116 117 118 119 120 121
/* 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 *
122
icon_position_iterator_new (GArray *icon_positions, const GList *uris)
123 124
{
	IconPositionIterator *result;
125
	guint index;
126

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

Ramiro Estrugo's avatar
Ramiro Estrugo committed
137
	result->uris = eel_g_str_list_copy ((GList *)uris);
138 139 140 141 142 143 144 145 146 147 148 149 150
	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
151
	eel_g_list_free_deep (position_iterator->uris);
152 153 154
	g_free (position_iterator);
}

155 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
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;
}

197 198
#if GNOME2_CONVERSION_COMPLETE

199 200
static GdkFont *
get_label_font (void)
201
{
202 203
	GtkWidget *label;
	GtkStyle *style;
204 205 206 207 208 209
	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?
	 */
210 211 212

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

217
	return font;
218 219
}

220 221
#endif

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

230 231 232 233 234 235 236 237 238 239 240 241 242 243
	/* 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.
	 */
	
244
	/* get a nice length to ellipsize to, based on the font */
245
	font = get_label_font ();
246
	maximum_width = gdk_string_width (font, "MMMMMMMMMMMMMMMMMMMMMM");
247 248

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

254 255
	return result;
}
256

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

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

266 267 268 269
	return result;
}

static char *
270
extract_and_ellipsize_file_name_for_dialog (const char *uri)
271
{
272 273
	const char *last_part;
	char *unescaped, *result;
274
	
275
	last_part = g_basename (uri);
276
	g_return_val_if_fail (last_part != NULL, NULL);
277 278 279 280

	unescaped = gnome_vfs_unescape_string_for_display (last_part);
	result = ellipsize_string_for_dialog (unescaped);
	g_free (unescaped);
281

282 283 284
	return result;
}

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

292
	return transfer_info->parent_view;
293 294
}

295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347
static void
fit_rect_on_screen (GdkRectangle *rect)
{
	if (rect->x + rect->width > gdk_screen_width ()) {
		rect->x = gdk_screen_width () - rect->width;
	}

	if (rect->y + rect->height > gdk_screen_height ()) {
		rect->y = gdk_screen_height () - rect->height;
	}

	if (rect->x < 0) {
		rect->x = 0;
	}

	if (rect->y < 0) {
		rect->y = 0;
	}
}

static void
center_dialog_over_rect (GtkWindow *window, GdkRectangle rect)
{
	g_return_if_fail (GTK_WINDOW (window) != NULL);

	fit_rect_on_screen (&rect);

	gtk_widget_set_uposition (GTK_WIDGET (window), 
		rect.x + rect.width / 2 
			- GTK_WIDGET (window)->allocation.width / 2,
		rect.y + rect.height / 2
			- GTK_WIDGET (window)->allocation.height / 2);
}

static void
center_dialog_over_window (GtkWindow *window, GtkWindow *over)
{
	GdkRectangle rect;
	int x, y, w, h;

	g_return_if_fail (GTK_WINDOW (window) != NULL);
	g_return_if_fail (GTK_WINDOW (over) != NULL);

	gdk_window_get_root_origin (GTK_WIDGET (over)->window, &x, &y);
	gdk_window_get_size (GTK_WIDGET (over)->window, &w, &h);
	rect.x = x;
	rect.y = y;
	rect.width = w;
	rect.height = h;

	center_dialog_over_rect (window, rect);
}

348 349 350 351 352 353 354 355
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)
356
{
357
	transfer_info->cancelled = TRUE;
358 359
}

Ettore Perazzoli's avatar
Ettore Perazzoli committed
360
static void
361
create_transfer_dialog (const GnomeVFSXferProgressInfo *progress_info,
362
			TransferInfo *transfer_info)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
363
{
364
	if (!transfer_info->show_progress_dialog) {
365
		return;
366
	}
367

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

370
	transfer_info->progress_dialog = nautilus_file_operations_progress_new 
371
		(transfer_info->operation_title, "", "", "", 0, 0);
Ettore Perazzoli's avatar
Ettore Perazzoli committed
372

373 374 375
	/* Treat clicking on the close box or use of the escape key
	 * the same as clicking cancel.
	 */
376
	g_signal_connect (transfer_info->progress_dialog,
377 378
			  "response",
			  G_CALLBACK (handle_response_callback),
379
			  transfer_info);
380
	g_signal_connect (transfer_info->progress_dialog,
381 382 383
			  "close",
			  G_CALLBACK (handle_close_callback),
			  transfer_info);
Ettore Perazzoli's avatar
Ettore Perazzoli committed
384

385
	/* Make the progress dialog show up over the window we are copying into */
386 387
	if (transfer_info->parent_view != NULL) {
		center_dialog_over_window (GTK_WINDOW (transfer_info->progress_dialog), 
388
					   GTK_WINDOW (gtk_widget_get_toplevel (transfer_info->parent_view)));
389
	}
390 391

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

Pavel Cisler's avatar
Pavel Cisler committed
394
static void
395
progress_dialog_set_to_from_item_text (NautilusFileOperationsProgress *dialog,
396 397 398
				       const char *progress_verb,
				       const char *from_uri, const char *to_uri, 
				       gulong index, gulong size)
Pavel Cisler's avatar
Pavel Cisler committed
399 400 401 402 403 404 405 406
{
	char *item;
	char *from_path;
	char *to_path;
	char *progress_label_text;
	const char *from_prefix;
	const char *to_prefix;
	GnomeVFSURI *uri;
407
	int length;
Pavel Cisler's avatar
Pavel Cisler committed
408 409 410 411 412 413 414 415 416 417 418 419

	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);
420 421 422 423 424 425 426

		/* remove the last '/' */
		length = strlen (from_path);
		if (from_path [length - 1] == '/') {
			from_path [length - 1] = '\0';
		}
		
Pavel Cisler's avatar
Pavel Cisler committed
427 428 429
		gnome_vfs_uri_unref (uri);
		g_assert (progress_verb);
		progress_label_text = g_strdup_printf ("%s:", progress_verb);
430
		/* "From" dialog label, source path gets placed next to it in the dialog */
Pavel Cisler's avatar
Pavel Cisler committed
431 432 433 434
		from_prefix = _("From:");
	}

	if (to_uri != NULL) {
435
		uri = gnome_vfs_uri_new (to_uri);
Pavel Cisler's avatar
Pavel Cisler committed
436
		to_path = gnome_vfs_uri_extract_dirname (uri);
437 438 439 440 441 442 443

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

Pavel Cisler's avatar
Pavel Cisler committed
444
		gnome_vfs_uri_unref (uri);
445
		/* "To" dialog label, source path gets placed next to it in the dialog */
Pavel Cisler's avatar
Pavel Cisler committed
446 447 448
		to_prefix = _("To:");
	}

449 450 451 452 453 454 455
	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
456 457 458 459 460 461 462

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

Pavel Cisler's avatar
Pavel Cisler committed
463
static int
464
handle_transfer_ok (const GnomeVFSXferProgressInfo *progress_info,
465
		    TransferInfo *transfer_info)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
466
{
467 468
	if (transfer_info->cancelled
		&& progress_info->phase != GNOME_VFS_XFER_PHASE_COMPLETED) {
469 470
		/* If cancelled, delete any partially copied files that are laying
		 * around and return.
471
		 */
472 473
		if (progress_info->target_name != NULL
		    && progress_info->bytes_total != progress_info->bytes_copied) {
474
			GList *delete_me;
475

476
			delete_me = g_list_prepend (NULL, progress_info->target_name);
477 478 479 480
			nautilus_file_operations_delete (delete_me, transfer_info->parent_view);
			g_list_free (delete_me);
		}

481 482 483
		return 0;
	}
	
Ettore Perazzoli's avatar
Ettore Perazzoli committed
484
	switch (progress_info->phase) {
Pavel Cisler's avatar
Pavel Cisler committed
485
	case GNOME_VFS_XFER_PHASE_INITIAL:
486
		create_transfer_dialog (progress_info, transfer_info);
487
		return 1;
Pavel Cisler's avatar
Pavel Cisler committed
488 489

	case GNOME_VFS_XFER_PHASE_COLLECTING:
490
		if (transfer_info->progress_dialog != NULL) {
491
			nautilus_file_operations_progress_set_operation_string
492 493
				(transfer_info->progress_dialog,
				 transfer_info->preparation_name);
494
		}
495
		return 1;
Pavel Cisler's avatar
Pavel Cisler committed
496 497

	case GNOME_VFS_XFER_PHASE_READYTOGO:
498
		if (transfer_info->progress_dialog != NULL) {
499 500 501
			nautilus_file_operations_progress_set_operation_string
				(transfer_info->progress_dialog,
				 transfer_info->action_label);
502
			nautilus_file_operations_progress_set_total
503 504 505
				(transfer_info->progress_dialog,
				 progress_info->files_total,
				 progress_info->bytes_total);
506
		}
507
		return 1;
Pavel Cisler's avatar
Pavel Cisler committed
508
				 
509
	case GNOME_VFS_XFER_PHASE_DELETESOURCE:
510
		nautilus_file_changes_consume_changes (FALSE);
511
		if (transfer_info->progress_dialog != NULL) {
512 513 514 515 516 517 518
			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
519

520
			nautilus_file_operations_progress_update_sizes
521
				(transfer_info->progress_dialog,
522
				 MIN (progress_info->bytes_copied, 
523
				      progress_info->bytes_total),
524
				 MIN (progress_info->total_bytes_copied,
525
				      progress_info->bytes_total));
526
		}
527
		return 1;
528 529

	case GNOME_VFS_XFER_PHASE_MOVING:
Pavel Cisler's avatar
Pavel Cisler committed
530 531
	case GNOME_VFS_XFER_PHASE_OPENSOURCE:
	case GNOME_VFS_XFER_PHASE_OPENTARGET:
532 533
		/* fall through */
	case GNOME_VFS_XFER_PHASE_COPYING:
534
		if (transfer_info->progress_dialog != NULL) {
535
			if (progress_info->bytes_copied == 0) {
536 537 538 539 540 541 542
				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);
543
			} else {
544
				nautilus_file_operations_progress_update_sizes
545
					(transfer_info->progress_dialog,
546
					 MIN (progress_info->bytes_copied, 
547
					      progress_info->bytes_total),
548
					 MIN (progress_info->total_bytes_copied,
549
					      progress_info->bytes_total));
550
			}
Ettore Perazzoli's avatar
Ettore Perazzoli committed
551
		}
552
		return 1;
Pavel Cisler's avatar
Pavel Cisler committed
553

554
	case GNOME_VFS_XFER_PHASE_CLEANUP:
555
		if (transfer_info->progress_dialog != NULL) {
556 557
			nautilus_file_operations_progress_clear
				(transfer_info->progress_dialog);
558
			nautilus_file_operations_progress_set_operation_string
559 560
				(transfer_info->progress_dialog,
				 transfer_info->cleanup_name);
561
		}
562
		return 1;
563

Ettore Perazzoli's avatar
Ettore Perazzoli committed
564
	case GNOME_VFS_XFER_PHASE_COMPLETED:
565
		nautilus_file_changes_consume_changes (TRUE);
566
		if (transfer_info->done_callback != NULL) {
567 568 569 570
			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;
571
		}
572 573

		transfer_info_destroy (transfer_info);
574
		return 1;
Pavel Cisler's avatar
Pavel Cisler committed
575

Ettore Perazzoli's avatar
Ettore Perazzoli committed
576
	default:
577
		return 1;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
578 579 580
	}
}

581 582 583 584 585 586 587 588
typedef enum {
	ERROR_READ_ONLY,
	ERROR_NOT_READABLE,
	ERROR_NOT_WRITABLE,
	ERROR_NOT_ENOUGH_PERMISSIONS,
	ERROR_NO_SPACE,
	ERROR_OTHER
} NautilusFileOperationsErrorKind;
589

590 591 592 593 594 595 596 597 598 599 600
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,
601 602 603 604
		    TransferKind operation_kind,
		    NautilusFileOperationsErrorKind error_kind,
		    NautilusFileOperationsErrorLocation error_location,
		    GnomeVFSResult error)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
605
{
606 607 608 609 610 611
	/* 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
612

613
	const char *error_string;
614
	char *result;
Pavel Cisler's avatar
Pavel Cisler committed
615

616 617
	error_string = NULL;
	result = NULL;
Pavel Cisler's avatar
Pavel Cisler committed
618

619
	if (error_location == ERROR_LOCATION_SOURCE_PARENT) {
620

621
		switch (operation_kind) {
622 623
		case TRANSFER_MOVE:
		case TRANSFER_MOVE_TO_TRASH:
624
			if (error_kind == ERROR_READ_ONLY) {
625
				error_string = _("Error while moving.\n\n"
626 627
						 "\"%s\" cannot be moved because it is on "
						 "a read-only disk.");
628 629
			}
			break;
630

631 632
		case TRANSFER_DELETE:
		case TRANSFER_EMPTY_TRASH:
633 634 635
			switch (error_kind) {
			case ERROR_NOT_ENOUGH_PERMISSIONS:
			case ERROR_NOT_WRITABLE:
636
				error_string = _("Error while deleting.\n\n"
637
						 "\"%s\" cannot be deleted because you do not have "
638 639 640 641
						 "permissions to modify its parent folder.");
				break;
			
			case ERROR_READ_ONLY:
642
				error_string = _("Error while deleting.\n\n"
643 644 645 646 647 648
						 "\"%s\" cannot be deleted because it is on "
						 "a read-only disk.");
				break;

			default:
				break;
649 650 651 652 653 654 655 656 657 658 659
			}
			break;

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

662
	} else if (error_location == ERROR_LOCATION_SOURCE_OR_PARENT) {
663

664 665
		g_assert (source_name != NULL);

666 667 668 669
		/* 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.
		 */
670
		switch (operation_kind) {
671
		case TRANSFER_MOVE:
672
			if (error_kind == ERROR_NOT_ENOUGH_PERMISSIONS) {
673
				error_string = _("Error while moving.\n\n"
674 675 676
						 "\"%s\" cannot be moved because you do not have "
						 "permissions to change it or its parent folder.");
			}
677
			break;
678
		case TRANSFER_MOVE_TO_TRASH:
679
			if (error_kind == ERROR_NOT_ENOUGH_PERMISSIONS) {
680
				error_string = _("Error while moving.\n\n"
681
						 "\"%s\" cannot be moved to the Trash because you do not have "
682 683
						 "permissions to change it or its parent folder.");
			}
684
			break;
685

686
		default:
687
			g_assert_not_reached ();
688 689
			break;
		}
690

691 692 693 694
		if (error_string != NULL) {
			g_assert (source_name != NULL);
			result = g_strdup_printf (error_string, source_name);
		}
695

696
	} else if (error_location == ERROR_LOCATION_SOURCE) {
697

698 699 700
		g_assert (source_name != NULL);

		switch (operation_kind) {
701 702
		case TRANSFER_COPY:
		case TRANSFER_DUPLICATE:
703
			if (error_kind == ERROR_NOT_READABLE) {
704
				error_string = _("Error while copying.\n\n"
705 706
						 "\"%s\" cannot be copied because you do not have "
						 "permissions to read it.");
707 708
			}
			break;
709

710 711 712
		default:
			g_assert_not_reached ();
			break;
713
		}
714

715 716 717
		if (error_string != NULL) {
			g_assert (source_name != NULL);
			result = g_strdup_printf (error_string, source_name);
718
		}
Pavel Cisler's avatar
Pavel Cisler committed
719

720 721 722 723
	} else if (error_location == ERROR_LOCATION_TARGET) {

		if (error_kind == ERROR_NO_SPACE) {
			switch (operation_kind) {
724 725
			case TRANSFER_COPY:
			case TRANSFER_DUPLICATE:
726
				error_string = _("Error while copying to \"%s\".\n\n"
727
				   		 "There is not enough space on the destination.");
728
				break;
729 730
			case TRANSFER_MOVE_TO_TRASH:
			case TRANSFER_MOVE:
731
				error_string = _("Error while moving to \"%s\".\n\n"
732
				   		 "There is not enough space on the destination.");
733
				break;
734
			case TRANSFER_LINK:
735
				error_string = _("Error while creating link in \"%s\".\n\n"
736
				   		 "There is not enough space on the destination.");
737 738 739 740 741 742 743
				break;
			default:
				g_assert_not_reached ();
				break;
			}
		} else {
			switch (operation_kind) {
744 745
			case TRANSFER_COPY:
			case TRANSFER_DUPLICATE:
746
				if (error_kind == ERROR_NOT_ENOUGH_PERMISSIONS) {
747
					error_string = _("Error while copying to \"%s\".\n\n"
748
					   		 "You do not have permissions to write to "
749
					   		 "this folder.");
750
				} else if (error_kind == ERROR_NOT_WRITABLE) {
751
					error_string = _("Error while copying to \"%s\".\n\n"
752
					   		 "The destination disk is read-only.");
753 754
				} 
				break;
755 756
			case TRANSFER_MOVE:
			case TRANSFER_MOVE_TO_TRASH:
757
				if (error_kind == ERROR_NOT_ENOUGH_PERMISSIONS) {
758
					error_string = _("Error while moving items to \"%s\".\n\n"
759
					   		 "You do not have permissions to write to "
760
					   		 "this folder.");
761
				} else if (error_kind == ERROR_NOT_WRITABLE) {
762
					error_string = _("Error while moving items to \"%s\".\n\n"
763
					   		 "The destination disk is read-only.");
764 765 766
				} 

				break;
767
			case TRANSFER_LINK:
768
				if (error_kind == ERROR_NOT_ENOUGH_PERMISSIONS) {
769
					error_string = _("Error while creating links in \"%s\".\n\n"
770
					   		 "You do not have permissions to write to "
771
					   		 "this folder.");
772
				} else if (error_kind == ERROR_NOT_WRITABLE) {
773
					error_string = _("Error while creating links in \"%s\".\n\n"
774
					   		 "The destination disk is read-only.");
775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791
				} 
				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
		 */
792 793
		g_message ("Hit unexpected error \"%s\" while doing a file operation.",
			   gnome_vfs_result_to_string (error));
794 795 796 797

		/* FIXMEs: we need to consider a single item
		 * move/copy and not offer to continue in that case
		 */
798 799
		if (source_name != NULL) {
			switch (operation_kind) {
800 801
			case TRANSFER_COPY:
			case TRANSFER_DUPLICATE:
802
				error_string = _("Error \"%s\" while copying \"%s\".\n\n"
803 804
						 "Would you like to continue?");
				break;
805
			case TRANSFER_MOVE:
806
				error_string = _("Error \"%s\" while moving \"%s\".\n\n"
807 808
						 "Would you like to continue?");
				break;
809
			case TRANSFER_LINK:
810
				error_string = _("Error \"%s\" while creating a link to \"%s\".\n\n"
811 812
						 "Would you like to continue?");
				break;
813 814 815
			case TRANSFER_DELETE:
			case TRANSFER_EMPTY_TRASH:
			case TRANSFER_MOVE_TO_TRASH:
816
				error_string = _("Error \"%s\" while deleting \"%s\".\n\n"
817 818 819
						 "Would you like to continue?");
				break;
			default:
820
				g_assert_not_reached ();
821 822 823
				break;
			}
	
824
			result = g_strdup_printf (error_string, 
825 826
						  gnome_vfs_result_to_string (error),
						  source_name);
827
		} else {
828
			switch (operation_kind) {
829 830
			case TRANSFER_COPY:
			case TRANSFER_DUPLICATE:
831
				error_string = _("Error \"%s\" while copying.\n\n"
832 833
						 "Would you like to continue?");
				break;
834
			case TRANSFER_MOVE:
835
				error_string = _("Error \"%s\" while moving.\n\n"
836 837
						 "Would you like to continue?");
				break;
838
			case TRANSFER_LINK:
839
				error_string = _("Error \"%s\" while linking.\n\n"
840 841
						 "Would you like to continue?");
				break;
842 843 844
			case TRANSFER_DELETE:
			case TRANSFER_EMPTY_TRASH:
			case TRANSFER_MOVE_TO_TRASH:
845
				error_string = _("Error \"%s\" while deleting.\n\n"
846 847 848
						 "Would you like to continue?");
				break;
			default:
849
				g_assert_not_reached ();
850 851 852
				break;
			}
	
853
			result = g_strdup_printf (error_string, 
854
						  gnome_vfs_result_to_string (error));
855
		}
856 857 858
	}
	return result;
}
859

Pavel Cisler's avatar