nautilus-file-operations.c 127 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.
Alexander Larsson's avatar
Alexander Larsson committed
7
   Copyright (C) 2007 Red Hat, Inc.
Ettore Perazzoli's avatar
Ettore Perazzoli committed
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

   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.
   
Alexander Larsson's avatar
Alexander Larsson committed
24 25
   Authors: Alexander Larsson <alexl@redhat.com>
            Ettore Perazzoli <ettore@gnu.org> 
26
            Pavel Cisler <pavel@eazel.com> 
27
 */
Ettore Perazzoli's avatar
Ettore Perazzoli committed
28 29

#include <config.h>
30
#include <string.h>
31
#include <stdio.h>
Alexander Larsson's avatar
Alexander Larsson committed
32
#include <stdarg.h>
33
#include <locale.h>
Alexander Larsson's avatar
Alexander Larsson committed
34
#include <math.h>
35
#include "nautilus-file-operations.h"
Ettore Perazzoli's avatar
Ettore Perazzoli committed
36

Alexander Larsson's avatar
 
Alexander Larsson committed
37
#include "nautilus-debug-log.h"
Alexander Larsson's avatar
Alexander Larsson committed
38
#include "nautilus-file-changes-queue.h"
39
#include "nautilus-lib-self-check-functions.h"
Ramiro Estrugo's avatar
Ramiro Estrugo committed
40

Alexander Larsson's avatar
Alexander Larsson committed
41 42 43
#include "nautilus-progress-info.h"

#include <eel/eel-alert-dialog.h>
Ramiro Estrugo's avatar
Ramiro Estrugo committed
44
#include <eel/eel-glib-extensions.h>
45
#include <eel/eel-pango-extensions.h>
Ramiro Estrugo's avatar
Ramiro Estrugo committed
46 47
#include <eel/eel-gtk-extensions.h>
#include <eel/eel-stock-dialogs.h>
Ramiro Estrugo's avatar
Ramiro Estrugo committed
48
#include <eel/eel-vfs-extensions.h>
49
#include <eel/eel-mount-operation.h>
Ramiro Estrugo's avatar
Ramiro Estrugo committed
50

51
#include <glib/gstdio.h>
Ettore Perazzoli's avatar
Ettore Perazzoli committed
52
#include <gnome.h>
53
#include <gdk/gdkdnd.h>
54
#include <gtk/gtklabel.h>
55
#include <gtk/gtkmessagedialog.h>
56
#include <gtk/gtkwidget.h>
57
#include <gio/gio.h>
Alexander Larsson's avatar
Alexander Larsson committed
58
#include <glib/gurifuncs.h>
59
#include "nautilus-file-changes-queue.h"
Alexander Larsson's avatar
Alexander Larsson committed
60 61 62
#include "nautilus-file-private.h"
#include "nautilus-desktop-icon-file.h"
#include "nautilus-desktop-link-monitor.h"
Ramiro Estrugo's avatar
Ramiro Estrugo committed
63 64
#include "nautilus-global-preferences.h"
#include "nautilus-link.h"
65
#include "nautilus-autorun.h"
Ramiro Estrugo's avatar
Ramiro Estrugo committed
66
#include "nautilus-trash-monitor.h"
67
#include "nautilus-file-utilities.h"
Ettore Perazzoli's avatar
Ettore Perazzoli committed
68

Alexander Larsson's avatar
Alexander Larsson committed
69 70
static gboolean confirm_trash_auto_value;

71
/* TODO: TESTING!!! */
Alexander Larsson's avatar
Alexander Larsson committed
72 73

typedef struct {
74
	GIOSchedulerJob *io_job;	
Alexander Larsson's avatar
Alexander Larsson committed
75
	GTimer *time;
76 77
	GtkWindow *parent_window;
	int screen_num;
Alexander Larsson's avatar
Alexander Larsson committed
78 79 80 81 82 83 84 85
	NautilusProgressInfo *progress;
	GCancellable *cancellable;
	GHashTable *skip_files;
	GHashTable *skip_readdir_error;
	gboolean skip_all_error;
	gboolean skip_all_conflict;
	gboolean merge_all;
	gboolean replace_all;
86
	gboolean delete_all;
Alexander Larsson's avatar
Alexander Larsson committed
87 88 89 90 91 92 93 94 95 96 97 98 99 100
} CommonJob;

typedef struct {
	CommonJob common;
	gboolean is_move;
	GList *files;
	GFile *destination;
	GdkPoint *icon_positions;
	int n_icon_positions;
	GHashTable *debuting_files;
	NautilusCopyCallback  done_callback;
	gpointer done_callback_data;
} CopyMoveJob;

101 102 103 104
typedef struct {
	CommonJob common;
	GList *files;
	gboolean try_trash;
105
	gboolean user_cancel;
106 107 108 109
	NautilusDeleteCallback done_callback;
	gpointer done_callback_data;
} DeleteJob;

110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
typedef struct {
	CommonJob common;
	GFile *dest_dir;
	char *filename;
	gboolean make_dir;
	GFile *src;
	char *src_data;
	GdkPoint position;
	gboolean has_position;
	GFile *created_file;
	NautilusCreateCallback done_callback;
	gpointer done_callback_data;
} CreateJob;


Alexander Larsson's avatar
Alexander Larsson committed
125 126
typedef struct {
	CommonJob common;
127
	GList *trash_dirs;
128
	gboolean should_confirm;
129 130
	NautilusOpCallback done_callback;
	gpointer done_callback_data;
Alexander Larsson's avatar
Alexander Larsson committed
131
} EmptyTrashJob;
132

133 134 135 136 137 138 139 140 141 142 143
typedef struct {
	CommonJob common;
	GFile *file;
	NautilusOpCallback done_callback;
	gpointer done_callback_data;
	guint32 file_permissions;
	guint32 file_mask;
	guint32 dir_permissions;
	guint32 dir_mask;
} SetPermissionsJob;

144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
typedef enum {
	OP_KIND_COPY,
	OP_KIND_MOVE,
	OP_KIND_DELETE,
	OP_KIND_TRASH
} OpKind;

typedef struct {
	int num_files;
	goffset num_bytes;
	int num_files_since_progress;
	OpKind op;
} SourceInfo;

typedef struct {
	int num_files;
	goffset num_bytes;
	OpKind op;
	guint64 last_report_time;
163
	int last_reported_files_left;
164 165
} TransferInfo;

Alexander Larsson's avatar
Alexander Larsson committed
166 167 168 169 170 171 172 173 174
#define SECONDS_NEEDED_FOR_RELIABLE_TRANSFER_RATE 15
#define NSEC_PER_SEC 1000000000
#define NSEC_PER_MSEC 1000000

#define IS_IO_ERROR(__error, KIND) (((__error)->domain == G_IO_ERROR && (__error)->code == G_IO_ERROR_ ## KIND))

#define SKIP _("_Skip")
#define SKIP_ALL _("S_kip All")
#define RETRY _("_Retry")
175
#define DELETE_ALL _("Delete _All")
Alexander Larsson's avatar
Alexander Larsson committed
176 177 178 179 180
#define REPLACE _("_Replace")
#define REPLACE_ALL _("Replace _All")
#define MERGE _("_Merge")
#define MERGE_ALL _("Merge _All")

181 182 183 184 185 186
static void scan_sources (GList *files,
			  SourceInfo *source_info,
			  CommonJob *job,
			  OpKind kind);


187 188 189
static gboolean empty_trash_job (GIOSchedulerJob *io_job,
				 GCancellable *cancellable,
				 gpointer user_data);
190

Alexander Larsson's avatar
Alexander Larsson committed
191 192 193 194 195 196 197 198 199 200 201 202 203
static char *
format_time (int seconds)
{
	int minutes;
	int hours;
	char *res;

	if (seconds < 0) {
		/* Just to make sure... */
		seconds = 0;
	}
	
	if (seconds < 60) {
204
		return g_strdup_printf (ngettext ("%'d second","%'d seconds", (int) seconds), (int) seconds);
Alexander Larsson's avatar
Alexander Larsson committed
205 206 207 208
	}

	if (seconds < 60*60) {
		minutes = (seconds + 30) / 60;
209
		return g_strdup_printf (ngettext ("%'d minute", "%'d minutes", minutes), minutes);
Alexander Larsson's avatar
Alexander Larsson committed
210 211 212 213 214 215 216 217 218
	}

	hours = seconds / (60*60);
	
	if (seconds < 60*60*4) {
		char *h, *m;

		minutes = (seconds - hours * 60 * 60 + 30) / 60;
		
219 220
		h = g_strdup_printf (ngettext ("%'d hour", "%'d hours", hours), hours);
		m = g_strdup_printf (ngettext ("%'d minute", "%'d minutes", minutes), minutes);
Alexander Larsson's avatar
Alexander Larsson committed
221 222 223 224 225 226
		res = g_strconcat (h, ", ", m, NULL);
		g_free (h);
		g_free (m);
		return res;
	}
	
227 228
	return g_strdup_printf (ngettext ("approximately %'d hour",
					  "approximately %'d hours",
229
					  hours), hours);
Alexander Larsson's avatar
Alexander Larsson committed
230 231
}

232
/* Note that we have these two separate functions with separate format
233
 * strings for ease of localization.
234 235 236
 */

static char *
Alexander Larsson's avatar
Alexander Larsson committed
237
get_link_name (const char *name, int count) 
238
{
239
	const char *format;
Alexander Larsson's avatar
Alexander Larsson committed
240
	char *result;
241
	
242 243 244 245 246 247 248
	g_assert (name != NULL);

	if (count < 1) {
		g_warning ("bad count in get_link_name");
		count = 1;
	}

249 250 251
	if (count <= 2) {
		/* Handle special cases for low numbers.
		 * Perhaps for some locales we will need to add more.
252
		 */
253
		switch (count) {
254 255 256
		default:
			g_assert_not_reached ();
			/* fall through */
257
		case 1:
258
			/* appended to new link file */
259
			format = _("Link to %s");
260 261
			break;
		case 2:
262
			/* appended to new link file */
263
			format = _("Another link to %s");
264 265
			break;
		}
Alexander Larsson's avatar
Alexander Larsson committed
266
		result = g_strdup_printf (format, name);
267 268 269 270 271

	} else {
		/* Handle special cases for the first few numbers of each ten.
		 * For locales where getting this exactly right is difficult,
		 * these can just be made all the same as the general case below.
272
		 */
273 274 275 276 277 278
		switch (count % 10) {
		case 1:
			/* Localizers: Feel free to leave out the "st" suffix
			 * if there's no way to do that nicely for a
			 * particular language.
			 */
279
			format = _("%'dst link to %s");
280 281
			break;
		case 2:
282
			/* appended to new link file */
283
			format = _("%'dnd link to %s");
284 285
			break;
		case 3:
286
			/* appended to new link file */
287
			format = _("%'drd link to %s");
288 289
			break;
		default:
290
			/* appended to new link file */
291
			format = _("%'dth link to %s");
292 293
			break;
		}
Alexander Larsson's avatar
Alexander Larsson committed
294
		result = g_strdup_printf (format, count, name);
295
	}
296

297
	return result;
298 299
}

Alexander Larsson's avatar
Alexander Larsson committed
300

301 302 303 304 305
/* Localizers: 
 * Feel free to leave out the st, nd, rd and th suffix or
 * make some or all of them match.
 */

306
/* localizers: tag used to detect the first copy of a file */
307
static const char untranslated_copy_duplicate_tag[] = N_(" (copy)");
308
/* localizers: tag used to detect the second copy of a file */
309
static const char untranslated_another_copy_duplicate_tag[] = N_(" (another copy)");
310 311 312 313 314 315 316 317

/* localizers: tag used to detect the x11th copy of a file */
static const char untranslated_x11th_copy_duplicate_tag[] = N_("th copy)");
/* localizers: tag used to detect the x12th copy of a file */
static const char untranslated_x12th_copy_duplicate_tag[] = N_("th copy)");
/* localizers: tag used to detect the x13th copy of a file */
static const char untranslated_x13th_copy_duplicate_tag[] = N_("th copy)");

318
/* localizers: tag used to detect the x1st copy of a file */
319
static const char untranslated_st_copy_duplicate_tag[] = N_("st copy)");
320
/* localizers: tag used to detect the x2nd copy of a file */
321
static const char untranslated_nd_copy_duplicate_tag[] = N_("nd copy)");
322
/* localizers: tag used to detect the x3rd copy of a file */
323
static const char untranslated_rd_copy_duplicate_tag[] = N_("rd copy)");
324

325
/* localizers: tag used to detect the xxth copy of a file */
326 327 328 329
static const char untranslated_th_copy_duplicate_tag[] = N_("th copy)");

#define COPY_DUPLICATE_TAG _(untranslated_copy_duplicate_tag)
#define ANOTHER_COPY_DUPLICATE_TAG _(untranslated_another_copy_duplicate_tag)
330 331 332 333
#define X11TH_COPY_DUPLICATE_TAG _(untranslated_x11th_copy_duplicate_tag)
#define X12TH_COPY_DUPLICATE_TAG _(untranslated_x12th_copy_duplicate_tag)
#define X13TH_COPY_DUPLICATE_TAG _(untranslated_x13th_copy_duplicate_tag)

334 335 336 337
#define ST_COPY_DUPLICATE_TAG _(untranslated_st_copy_duplicate_tag)
#define ND_COPY_DUPLICATE_TAG _(untranslated_nd_copy_duplicate_tag)
#define RD_COPY_DUPLICATE_TAG _(untranslated_rd_copy_duplicate_tag)
#define TH_COPY_DUPLICATE_TAG _(untranslated_th_copy_duplicate_tag)
338 339

/* localizers: appended to first file copy */
340
static const char untranslated_first_copy_duplicate_format[] = N_("%s (copy)%s");
341
/* localizers: appended to second file copy */
342
static const char untranslated_second_copy_duplicate_format[] = N_("%s (another copy)%s");
343 344

/* localizers: appended to x11th file copy */
345
static const char untranslated_x11th_copy_duplicate_format[] = N_("%s (%'dth copy)%s");
346
/* localizers: appended to x12th file copy */
347
static const char untranslated_x12th_copy_duplicate_format[] = N_("%s (%'dth copy)%s");
348
/* localizers: appended to x13th file copy */
349
static const char untranslated_x13th_copy_duplicate_format[] = N_("%s (%'dth copy)%s");
350

351
/* localizers: appended to x1st file copy */
352
static const char untranslated_st_copy_duplicate_format[] = N_("%s (%'dst copy)%s");
353
/* localizers: appended to x2nd file copy */
354
static const char untranslated_nd_copy_duplicate_format[] = N_("%s (%'dnd copy)%s");
355
/* localizers: appended to x3rd file copy */
356
static const char untranslated_rd_copy_duplicate_format[] = N_("%s (%'drd copy)%s");
357
/* localizers: appended to xxth file copy */
358
static const char untranslated_th_copy_duplicate_format[] = N_("%s (%'dth copy)%s");
359 360 361

#define FIRST_COPY_DUPLICATE_FORMAT _(untranslated_first_copy_duplicate_format)
#define SECOND_COPY_DUPLICATE_FORMAT _(untranslated_second_copy_duplicate_format)
362 363 364 365
#define X11TH_COPY_DUPLICATE_FORMAT _(untranslated_x11th_copy_duplicate_format)
#define X12TH_COPY_DUPLICATE_FORMAT _(untranslated_x12th_copy_duplicate_format)
#define X13TH_COPY_DUPLICATE_FORMAT _(untranslated_x13th_copy_duplicate_format)

366 367 368 369
#define ST_COPY_DUPLICATE_FORMAT _(untranslated_st_copy_duplicate_format)
#define ND_COPY_DUPLICATE_FORMAT _(untranslated_nd_copy_duplicate_format)
#define RD_COPY_DUPLICATE_FORMAT _(untranslated_rd_copy_duplicate_format)
#define TH_COPY_DUPLICATE_FORMAT _(untranslated_th_copy_duplicate_format)
370 371

static char *
372
extract_string_until (const char *original, const char *until_substring)
373 374 375
{
	char *result;
	
376
	g_assert ((int) strlen (original) >= until_substring - original);
377
	g_assert (until_substring - original >= 0);
378

379 380 381
	result = g_malloc (until_substring - original + 1);
	strncpy (result, original, until_substring - original);
	result[until_substring - original] = '\0';
382 383 384 385
	
	return result;
}

386 387 388 389
/* Dismantle a file name, separating the base name, the file suffix and removing any
 * (xxxcopy), etc. string. Figure out the count that corresponds to the given
 * (xxxcopy) substring.
 */
390
static void
391 392 393 394
parse_previous_duplicate_name (const char *name,
			       char **name_base,
			       const char **suffix,
			       int *count)
395 396
{
	const char *tag;
397 398

	g_assert (name[0] != '\0');
399
	
400
	*suffix = strchr (name + 1, '.');
401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429
	if (*suffix == NULL || (*suffix)[1] == '\0') {
		/* no suffix */
		*suffix = "";
	}

	tag = strstr (name, COPY_DUPLICATE_TAG);
	if (tag != NULL) {
		if (tag > *suffix) {
			/* handle case "foo. (copy)" */
			*suffix = "";
		}
		*name_base = extract_string_until (name, tag);
		*count = 1;
		return;
	}


	tag = strstr (name, ANOTHER_COPY_DUPLICATE_TAG);
	if (tag != NULL) {
		if (tag > *suffix) {
			/* handle case "foo. (another copy)" */
			*suffix = "";
		}
		*name_base = extract_string_until (name, tag);
		*count = 2;
		return;
	}


430
	/* Check to see if we got one of st, nd, rd, th. */
431 432 433 434 435 436 437 438
	tag = strstr (name, X11TH_COPY_DUPLICATE_TAG);

	if (tag == NULL) {
		tag = strstr (name, X12TH_COPY_DUPLICATE_TAG);
	}
	if (tag == NULL) {
		tag = strstr (name, X13TH_COPY_DUPLICATE_TAG);
	}
439

440 441 442
	if (tag == NULL) {
		tag = strstr (name, ST_COPY_DUPLICATE_TAG);
	}
443 444 445 446 447 448 449 450 451 452
	if (tag == NULL) {
		tag = strstr (name, ND_COPY_DUPLICATE_TAG);
	}
	if (tag == NULL) {
		tag = strstr (name, RD_COPY_DUPLICATE_TAG);
	}
	if (tag == NULL) {
		tag = strstr (name, TH_COPY_DUPLICATE_TAG);
	}

453
	/* If we got one of st, nd, rd, th, fish out the duplicate number. */
454
	if (tag != NULL) {
455
		/* localizers: opening parentheses to match the "th copy)" string */
456
		tag = strstr (name, _(" ("));
457 458 459 460 461 462
		if (tag != NULL) {
			if (tag > *suffix) {
				/* handle case "foo. (22nd copy)" */
				*suffix = "";
			}
			*name_base = extract_string_until (name, tag);
463
			/* localizers: opening parentheses of the "th copy)" string */
464
			if (sscanf (tag, _(" (%'d"), count) == 1) {
465 466 467 468
				if (*count < 1 || *count > 1000000) {
					/* keep the count within a reasonable range */
					*count = 0;
				}
469 470 471 472 473 474 475 476 477 478
				return;
			}
			*count = 0;
			return;
		}
	}

	
	*count = 0;
	if (**suffix != '\0') {
479 480
		*name_base = extract_string_until (name, *suffix);
	} else {
481
		*name_base = g_strdup (name);
482 483 484
	}
}

485
static char *
486
make_next_duplicate_name (const char *base, const char *suffix, int count)
487
{
488 489 490
	const char *format;
	char *result;

491 492

	if (count < 1) {
493
		g_warning ("bad count %d in get_duplicate_name", count);
494 495 496
		count = 1;
	}

497
	if (count <= 2) {
498

499 500 501 502
		/* Handle special cases for low numbers.
		 * Perhaps for some locales we will need to add more.
		 */
		switch (count) {
503 504 505
		default:
			g_assert_not_reached ();
			/* fall through */
506
		case 1:
507
			format = FIRST_COPY_DUPLICATE_FORMAT;
508 509
			break;
		case 2:
510
			format = SECOND_COPY_DUPLICATE_FORMAT;
511
			break;
512

513
		}
514
		result = g_strdup_printf (format, base, suffix);
515 516 517 518 519 520
	} else {

		/* Handle special cases for the first few numbers of each ten.
		 * For locales where getting this exactly right is difficult,
		 * these can just be made all the same as the general case below.
		 */
521 522 523 524 525 526

		/* Handle special cases for x11th - x20th.
		 */
		switch (count % 100) {
		case 11:
			format = X11TH_COPY_DUPLICATE_FORMAT;
527
			break;
528 529
		case 12:
			format = X12TH_COPY_DUPLICATE_FORMAT;
530
			break;
531 532
		case 13:
			format = X13TH_COPY_DUPLICATE_FORMAT;
533 534
			break;
		default:
535
			format = NULL;
536 537 538
			break;
		}

539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556
		if (format == NULL) {
			switch (count % 10) {
			case 1:
				format = ST_COPY_DUPLICATE_FORMAT;
				break;
			case 2:
				format = ND_COPY_DUPLICATE_FORMAT;
				break;
			case 3:
				format = RD_COPY_DUPLICATE_FORMAT;
				break;
			default:
				/* The general case. */
				format = TH_COPY_DUPLICATE_FORMAT;
				break;
			}
		}

557
		result = g_strdup_printf (format, base, count, suffix);
558 559
	}

560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578
	return result;
}

static char *
get_duplicate_name (const char *name, int count_increment)
{
	char *result;
	char *name_base;
	const char *suffix;
	int count;

	parse_previous_duplicate_name (name, &name_base, &suffix, &count);
	result = make_next_duplicate_name (name_base, suffix, count + count_increment);

	g_free (name_base);

	return result;
}

579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600
static gboolean
has_invalid_xml_char (char *str)
{
	gunichar c;

	while (*str != 0) {
		c = g_utf8_get_char (str);
		/* characters XML permits */
		if (!(c == 0x9 ||
		      c == 0xA ||
		      c == 0xD ||
		      (c >= 0x20 && c <= 0xD7FF) ||
		      (c >= 0xE000 && c <= 0xFFFD) ||
		      (c >= 0x10000 && c <= 0x10FFFF))) {
			return TRUE;
		}
		str = g_utf8_next_char (str);
	}
	return FALSE;
}


601 602 603 604
static char *
custom_full_name_to_string (char *format, va_list va)
{
	GFile *file;
605
	
606
	file = va_arg (va, GFile *);
607
	
608
	return g_file_get_parse_name (file);
609 610 611
}

static void
612
custom_full_name_skip (va_list *va)
613
{
614
	(void) va_arg (*va, GFile *);
615 616
}

617 618
static char *
custom_basename_to_string (char *format, va_list va)
619
{
620 621
	GFile *file;
	GFileInfo *info;
622
	char *name, *basename, *tmp;
623

624
	file = va_arg (va, GFile *);
625

626
	info = g_file_query_info (file,
627
				  G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
628 629 630
				  0,
				  g_cancellable_get_current (),
				  NULL);
631
	
632 633 634 635 636
	name = NULL;
	if (info) {
		name = g_strdup (g_file_info_get_display_name (info));
		g_object_unref (info);
	}
637
	
638 639 640 641 642 643 644
	if (name == NULL) {
		basename = g_file_get_basename (file);
		if (g_utf8_validate (basename, -1, NULL)) {
			name = basename;
		} else {
			name = g_uri_escape_string (basename, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, TRUE);
			g_free (basename);
645 646
		}
	}
647 648 649 650 651 652 653

	/* Some chars can't be put in the markup we use for the dialogs... */
	if (has_invalid_xml_char (name)) {
		tmp = name;
		name = g_uri_escape_string (name, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, TRUE);
		g_free (tmp);
	}
654 655
	
	return name;
656 657
}

658 659
static void
custom_basename_skip (va_list *va)
660
{
661
	(void) va_arg (*va, GFile *);
662
}
663 664


665 666 667 668
static char *
custom_size_to_string (char *format, va_list va)
{
	goffset size;
669

670
	size = va_arg (va, goffset);
671
	return g_format_size_for_display (size);
672 673
}

674
static void
675
custom_size_skip (va_list *va)
676
{
677
	(void) va_arg (*va, goffset);
678 679
}

680 681
static char *
custom_time_to_string (char *format, va_list va)
682
{
683
	int secs;
684

685 686 687
	secs = va_arg (va, int);
	return format_time (secs);
}
688

689 690 691
static void
custom_time_skip (va_list *va)
{
692
	(void) va_arg (*va, int);
693
}
694

695
static char *
696
custom_mount_to_string (char *format, va_list va)
697
{
698
	GMount *mount;
699

700 701
	mount = va_arg (va, GMount *);
	return g_mount_get_name (mount);
702 703 704
}

static void
705
custom_mount_skip (va_list *va)
706
{
707
	(void) va_arg (*va, GMount *);
708 709 710
}


711 712 713 714 715
static EelPrintfHandler handlers[] = {
	{ 'F', custom_full_name_to_string, custom_full_name_skip },
	{ 'B', custom_basename_to_string, custom_basename_skip },
	{ 'S', custom_size_to_string, custom_size_skip },
	{ 'T', custom_time_to_string, custom_time_skip },
716
	{ 'V', custom_mount_to_string, custom_mount_skip },
717 718
	{ 0 }
};
719 720


721 722 723 724
static char *
f (const char *format, ...) {
	va_list va;
	char *res;
725
	
726 727 728
	va_start (va, format);
	res = eel_strdup_vprintf_with_custom (handlers, format, va);
	va_end (va);
729

730 731
	return res;
}
732

733 734 735 736 737 738 739 740 741 742
#define op_job_new(__type, parent_window) ((__type *)(init_common (sizeof(__type), parent_window)))

static gpointer
init_common (gsize job_size,
	     GtkWindow *parent_window)
{
	CommonJob *common;
	GdkScreen *screen;

	common = g_malloc0 (job_size);
743 744

	if (parent_window) {
745 746
		common->parent_window = parent_window;
		eel_add_weak_pointer (&common->parent_window);
747
	}
748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766
	common->progress = nautilus_progress_info_new ();
	common->cancellable = nautilus_progress_info_get_cancellable (common->progress);
	common->time = g_timer_new ();

	common->screen_num = 0;
	if (parent_window) {
		screen = gtk_widget_get_screen (GTK_WIDGET (parent_window));
		common->screen_num = gdk_screen_get_number (screen);
	}
	
	return common;
}

static void
finalize_common (CommonJob *common)
{
	nautilus_progress_info_finish (common->progress);

	g_timer_destroy (common->time);
767 768

	eel_remove_weak_pointer (&common->parent_window);
769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823
	if (common->skip_files) {
		g_hash_table_destroy (common->skip_files);
	}
	if (common->skip_readdir_error) {
		g_hash_table_destroy (common->skip_readdir_error);
	}
	g_object_unref (common->progress);
	g_object_unref (common->cancellable);
	g_free (common);
}

static void
skip_file (CommonJob *common,
	   GFile *file)
{
	if (common->skip_files == NULL) {
		common->skip_files =
			g_hash_table_new_full (g_file_hash, (GEqualFunc)g_file_equal, g_object_unref, NULL);
	}

	g_hash_table_insert (common->skip_files, g_object_ref (file), file);
}

static void
skip_readdir_error (CommonJob *common,
		    GFile *dir)
{
	if (common->skip_readdir_error == NULL) {
		common->skip_readdir_error =
			g_hash_table_new_full (g_file_hash, (GEqualFunc)g_file_equal, g_object_unref, NULL);
	}

	g_hash_table_insert (common->skip_readdir_error, g_object_ref (dir), dir);
}

static gboolean
should_skip_file (CommonJob *common,
		  GFile *file)
{
	if (common->skip_files != NULL) {
		return g_hash_table_lookup (common->skip_files, file) != NULL;
	}
	return FALSE;
}

static gboolean
should_skip_readdir_error (CommonJob *common,
			   GFile *dir)
{
	if (common->skip_readdir_error != NULL) {
		return g_hash_table_lookup (common->skip_readdir_error, dir) != NULL;
	}
	return FALSE;
}

824 825 826 827 828 829 830 831 832 833
static void
setup_autos (void)
{
	static gboolean setup_autos = FALSE;
	if (!setup_autos) {
		setup_autos = TRUE;
		eel_preferences_add_auto_boolean (NAUTILUS_PREFERENCES_CONFIRM_TRASH,
						  &confirm_trash_auto_value);
	}
}
Alexander Larsson's avatar
 
Alexander Larsson committed
834

Alexander Larsson's avatar
Alexander Larsson committed
835 836 837
static gboolean
can_delete_without_confirm (GFile *file)
{
838 839
	if (g_file_has_uri_scheme (file, "burn") ||
	    g_file_has_uri_scheme (file, "x-nautilus-desktop")) {
Alexander Larsson's avatar
Alexander Larsson committed
840
		return TRUE;
Alexander Larsson's avatar
Alexander Larsson committed
841 842
	}

Alexander Larsson's avatar
Alexander Larsson committed
843
	return FALSE;
844 845
}

Alexander Larsson's avatar
Alexander Larsson committed
846 847
static gboolean
can_delete_files_without_confirm (GList *files)
848
{
Alexander Larsson's avatar
Alexander Larsson committed
849
	g_assert (files != NULL);
850

Alexander Larsson's avatar
Alexander Larsson committed
851 852 853 854
	while (files != NULL) {
		if (!can_delete_without_confirm (files->data)) {
			return FALSE;
		}
Alexander Larsson's avatar
 
Alexander Larsson committed
855

Alexander Larsson's avatar
Alexander Larsson committed
856 857
		files = files->next;
	}
Pavel Cisler's avatar
Pavel Cisler committed
858

Alexander Larsson's avatar
Alexander Larsson committed
859 860
	return TRUE;
}
861

Alexander Larsson's avatar
Alexander Larsson committed
862
typedef struct {
863
	GtkWindow **parent_window;
Alexander Larsson's avatar
Alexander Larsson committed
864 865 866 867 868 869 870 871 872
	gboolean ignore_close_box;
	GtkMessageType message_type;
	const char *primary_text;
	const char *secondary_text;
	const char *details_text;
	const char **button_titles;
	
	int result;
} RunSimpleDialogData;
873

874
static gboolean
Alexander Larsson's avatar
Alexander Larsson committed
875 876 877 878 879 880 881 882 883
do_run_simple_dialog (gpointer _data)
{
	RunSimpleDialogData *data = _data;
	const char *button_title;
        GtkWidget *dialog;
	int result;
	int response_id;

	/* Create the dialog. */
884
	dialog = eel_alert_dialog_new (*data->parent_window, 
Alexander Larsson's avatar
Alexander Larsson committed
885 886 887 888 889 890 891 892 893 894 895 896 897
	                               0,
	                               data->message_type,
	                               GTK_BUTTONS_NONE,
	                               data->primary_text,
	                               data->secondary_text);

	for (response_id = 0;
	     data->button_titles[response_id] != NULL;
	     response_id++) {
		button_title = data->button_titles[response_id];
		gtk_dialog_add_button (GTK_DIALOG (dialog), button_title, response_id);
		gtk_dialog_set_default_response (GTK_DIALOG (dialog), response_id);
	}
898

Alexander Larsson's avatar
Alexander Larsson committed
899 900 901 902 903 904 905 906 907 908 909 910 911 912
	if (data->details_text) {
		eel_alert_dialog_set_details_label (EEL_ALERT_DIALOG (dialog),
						    data->details_text);
	}
	
	/* Run it. */
        gtk_widget_show (dialog);
        result = gtk_dialog_run (GTK_DIALOG (dialog));
	
	while ((result == GTK_RESPONSE_NONE || result == GTK_RESPONSE_DELETE_EVENT) && data->ignore_close_box) {
		gtk_widget_show (GTK_WIDGET (dialog));
		result = gtk_dialog_run (GTK_DIALOG (dialog));
	}
	
913 914
	gtk_object_destroy (GTK_OBJECT (dialog));

Alexander Larsson's avatar
Alexander Larsson committed
915
	data->result = result;
916 917
	
	return FALSE;
Alexander Larsson's avatar
Alexander Larsson committed
918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939
}

/* NOTE: This frees the primary / secondary strings, in order to
   avoid doing that everywhere. So, make sure they are strduped */

static int
run_simple_dialog_va (CommonJob *job,
		      gboolean ignore_close_box,
		      GtkMessageType message_type,
		      char *primary_text,
		      char *secondary_text,
		      const char *details_text,
		      va_list varargs)
{
	RunSimpleDialogData *data;
	int res;
	const char *button_title;
	GPtrArray *ptr_array;

	g_timer_stop (job->time);
	
	data = g_new0 (RunSimpleDialogData, 1);
940
	data->parent_window = &job->parent_window;
Alexander Larsson's avatar
Alexander Larsson committed
941 942 943 944 945 946 947 948 949 950 951 952 953
	data->ignore_close_box = ignore_close_box;
	data->message_type = message_type;
	data->primary_text = primary_text;
	data->secondary_text = secondary_text;
	data->details_text = details_text;

	ptr_array = g_ptr_array_new ();
	while ((button_title = va_arg (varargs, const char *)) != NULL) {
		g_ptr_array_add (ptr_array, (char *)button_title);
	}
	g_ptr_array_add (ptr_array, NULL);
	data->button_titles = (const char **)g_ptr_array_free (ptr_array, FALSE);

954
	nautilus_progress_info_pause (job->progress);
955 956 957 958
	g_io_scheduler_job_send_to_mainloop (job->io_job,
					     do_run_simple_dialog,
					     data,
					     NULL);
959
	nautilus_progress_info_resume (job->progress);
Alexander Larsson's avatar
Alexander Larsson committed
960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042
	res = data->result;

	g_free (data->button_titles);
	g_free (data);

	g_timer_continue (job->time);

	g_free (primary_text);
	g_free (secondary_text);
	
	return res;
}

#if 0 /* Not used at the moment */
static int
run_simple_dialog (CommonJob *job,
		   gboolean ignore_close_box,
		   GtkMessageType message_type,
		   char *primary_text,
		   char *secondary_text,
		   const char *details_text,
		   ...)
{
	va_list varargs;
	int res;

	va_start (varargs, details_text);
	res = run_simple_dialog_va (job,
				    ignore_close_box,
				    message_type,
				    primary_text,
				    secondary_text,
				    details_text,
				    varargs);
	va_end (varargs);
	return res;
}
#endif

static int
run_error (CommonJob *job,
	   char *primary_text,
	   char *secondary_text,
	   const char *details_text,
	   ...)
{
	va_list varargs;
	int res;

	va_start (varargs, details_text);
	res = run_simple_dialog_va (job,
				    FALSE,
				    GTK_MESSAGE_ERROR,
				    primary_text,
				    secondary_text,
				    details_text,
				    varargs);
	va_end (varargs);
	return res;
}

static int
run_warning (CommonJob *job,
	     char *primary_text,
	     char *secondary_text,
	     const char *details_text,
	     ...)
{
	va_list varargs;
	int res;

	va_start (varargs, details_text);
	res = run_simple_dialog_va (job,
				    FALSE,
				    GTK_MESSAGE_WARNING,
				    primary_text,
				    secondary_text,
				    details_text,
				    varargs);
	va_end (varargs);
	return res;
}

1043