nautilus-file-operations.c 121 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"
38
#include "nautilus-file-operations-progress.h"
Alexander Larsson's avatar
Alexander Larsson committed
39
#include "nautilus-file-changes-queue.h"
40
#include "nautilus-lib-self-check-functions.h"
Ramiro Estrugo's avatar
Ramiro Estrugo committed
41

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

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

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

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

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

typedef struct {
75
	GIOSchedulerJob *io_job;	
Alexander Larsson's avatar
Alexander Larsson committed
76
	GTimer *time;
77
78
	GtkWindow *parent_window;
	int screen_num;
Alexander Larsson's avatar
Alexander Larsson committed
79
80
81
82
83
84
85
86
	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;
87
	gboolean delete_all;
Alexander Larsson's avatar
Alexander Larsson committed
88
89
90
91
92
93
94
95
96
97
98
99
100
101
} 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;

102
103
104
105
106
107
108
109
typedef struct {
	CommonJob common;
	GList *files;
	gboolean try_trash;
	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
128
129
	GList *trash_dirs;
	NautilusOpCallback done_callback;
	gpointer done_callback_data;
Alexander Larsson's avatar
Alexander Larsson committed
130
} EmptyTrashJob;
131

132
133
134
135
136
137
138
139
140
141
142
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;

143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
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;
} TransferInfo;

Alexander Larsson's avatar
Alexander Larsson committed
164
165
166
167
168
169
170
171
172
#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")
173
#define DELETE_ALL _("Delete _All")
Alexander Larsson's avatar
Alexander Larsson committed
174
175
176
177
178
#define REPLACE _("_Replace")
#define REPLACE_ALL _("Replace _All")
#define MERGE _("_Merge")
#define MERGE_ALL _("Merge _All")

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


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

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

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

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

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

		minutes = (seconds - hours * 60 * 60 + 30) / 60;
		
217
218
		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
219
220
221
222
223
224
		res = g_strconcat (h, ", ", m, NULL);
		g_free (h);
		g_free (m);
		return res;
	}
	
225
226
	return g_strdup_printf (ngettext ("approximately %'d hour",
					  "approximately %'d hours",
227
					  hours), hours);
Alexander Larsson's avatar
Alexander Larsson committed
228
229
}

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

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

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

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

	} 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.
270
		 */
271
272
273
274
275
276
		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.
			 */
277
			format = _("%'dst link to %s");
278
279
			break;
		case 2:
280
			/* appended to new link file */
281
			format = _("%'dnd link to %s");
282
283
			break;
		case 3:
284
			/* appended to new link file */
285
			format = _("%'drd link to %s");
286
287
			break;
		default:
288
			/* appended to new link file */
289
			format = _("%'dth link to %s");
290
291
			break;
		}
Alexander Larsson's avatar
Alexander Larsson committed
292
		result = g_strdup_printf (format, count, name);
293
	}
294

295
	return result;
296
297
}

Alexander Larsson's avatar
Alexander Larsson committed
298

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

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

/* 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)");

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

323
/* localizers: tag used to detect the xxth copy of a file */
324
325
326
327
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)
328
329
330
331
#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)

332
333
334
335
#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)
336
337

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

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

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

#define FIRST_COPY_DUPLICATE_FORMAT _(untranslated_first_copy_duplicate_format)
#define SECOND_COPY_DUPLICATE_FORMAT _(untranslated_second_copy_duplicate_format)
360
361
362
363
#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)

364
365
366
367
#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)
368
369

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

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

384
385
386
387
/* 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.
 */
388
static void
389
390
391
392
parse_previous_duplicate_name (const char *name,
			       char **name_base,
			       const char **suffix,
			       int *count)
393
394
{
	const char *tag;
395
396

	g_assert (name[0] != '\0');
397
	
398
	*suffix = strchr (name + 1, '.');
399
400
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
	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;
	}


428
	/* Check to see if we got one of st, nd, rd, th. */
429
430
431
432
433
434
435
436
	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);
	}
437

438
439
440
	if (tag == NULL) {
		tag = strstr (name, ST_COPY_DUPLICATE_TAG);
	}
441
442
443
444
445
446
447
448
449
450
	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);
	}

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

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

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

489
490

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

495
	if (count <= 2) {
496

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

511
		}
512
		result = g_strdup_printf (format, base, suffix);
513
514
515
516
517
518
	} 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.
		 */
519
520
521
522
523
524

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

537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
		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;
			}
		}

555
		result = g_strdup_printf (format, base, count, suffix);
556
557
	}

558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
	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;
}

577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
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;
}


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

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

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

622
	file = va_arg (va, GFile *);
623

624
	info = g_file_query_info (file,
625
				  G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
626
627
628
				  0,
				  g_cancellable_get_current (),
				  NULL);
629
	
630
631
632
633
634
	name = NULL;
	if (info) {
		name = g_strdup (g_file_info_get_display_name (info));
		g_object_unref (info);
	}
635
	
636
637
638
639
640
641
642
	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);
643
644
		}
	}
645
646
647
648
649
650
651

	/* 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);
	}
652
653
	
	return name;
654
655
}

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


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

668
	size = va_arg (va, goffset);
669
	return g_format_size_for_display (size);
670
671
}

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

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

683
684
685
	secs = va_arg (va, int);
	return format_time (secs);
}
686

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

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

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

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


709
710
711
712
713
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 },
714
	{ 'V', custom_mount_to_string, custom_mount_skip },
715
716
	{ 0 }
};
717
718


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

728
729
	return res;
}
730

731
732
733
734
735
736
737
738
739
740
#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);
741
742
743
744

	if (parent_window) {
		common->parent_window = g_object_ref (parent_window);
	}
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
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
	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);
	
	if (common->parent_window) {
		 g_object_unref (common->parent_window);
	}
	if (common->skip_files) {
		g_hash_table_destroy (common->skip_files);
	}
	if (common->skip_readdir_error) {
		g_hash_table_destroy (common->skip_readdir_error);
	}
	g_object_unref (common->progress);
	g_object_unref (common->cancellable);
	g_free (common);
}

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

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

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

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

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

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

823
824
825
826
827
828
829
830
831
832
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
833

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

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

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

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

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

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

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

873
static gboolean
Alexander Larsson's avatar
Alexander Larsson committed
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
do_run_simple_dialog (gpointer _data)
{
	RunSimpleDialogData *data = _data;
	const char *button_title;
        GtkWidget *dialog;
	int result;
	int response_id;

	/* Create the dialog. */
	dialog = eel_alert_dialog_new (data->parent_window, 
	                               0,
	                               data->message_type,
	                               GTK_BUTTONS_NONE,
	                               data->primary_text,
	                               data->secondary_text);

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

Alexander Larsson's avatar
Alexander Larsson committed
898
899
900
901
902
903
904
905
906
907
908
909
910
911
	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));
	}
	
912
913
	gtk_object_destroy (GTK_OBJECT (dialog));

Alexander Larsson's avatar
Alexander Larsson committed
914
	data->result = result;
915
916
	
	return FALSE;
Alexander Larsson's avatar
Alexander Larsson committed
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
}

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

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

	g_timer_stop (job->time);
	
	data = g_new0 (RunSimpleDialogData, 1);
	data->parent_window = GTK_WINDOW (job->parent_window);
	data->ignore_close_box = ignore_close_box;
	data->message_type = message_type;
	data->primary_text = primary_text;
	data->secondary_text = secondary_text;
	data->details_text = details_text;

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

953
954
955
956
	g_io_scheduler_job_send_to_mainloop (job->io_job,
					     do_run_simple_dialog,
					     data,
					     NULL);
Alexander Larsson's avatar
Alexander Larsson committed
957
958
959
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

	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,
For faster browsing, not all history is shown. View entire blame