1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
3 /* nautilus-file-operations.c - Nautilus file operations.
5 Copyright (C) 1999, 2000 Free Software Foundation
6 Copyright (C) 2000, 2001 Eazel, Inc.
7 Copyright (C) 2007 Red Hat, Inc.
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License as
11 published by the Free Software Foundation; either version 2 of the
12 License, or (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 General Public License for more details.
19 You should have received a copy of the GNU General Public
20 License along with this program; if not, write to the
21 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 Boston, MA 02111-1307, USA.
24 Authors: Alexander Larsson <alexl@redhat.com>
25 Ettore Perazzoli <ettore@gnu.org>
26 Pavel Cisler <pavel@eazel.com>
35 #include "nautilus-file-operations.h"
37 #include "nautilus-debug-log.h"
38 #include "nautilus-file-changes-queue.h"
39 #include "nautilus-lib-self-check-functions.h"
41 #include "nautilus-progress-info.h"
43 #include <eel/eel-alert-dialog.h>
44 #include <eel/eel-glib-extensions.h>
45 #include <eel/eel-pango-extensions.h>
46 #include <eel/eel-gtk-extensions.h>
47 #include <eel/eel-stock-dialogs.h>
48 #include <eel/eel-vfs-extensions.h>
49 #include <eel/eel-mount-operation.h>
51 #include <glib/gstdio.h>
53 #include <gdk/gdkdnd.h>
54 #include <gtk/gtklabel.h>
55 #include <gtk/gtkmessagedialog.h>
56 #include <gtk/gtkwidget.h>
58 #include <glib/gurifuncs.h>
59 #include "nautilus-file-changes-queue.h"
60 #include "nautilus-file-private.h"
61 #include "nautilus-desktop-icon-file.h"
62 #include "nautilus-desktop-link-monitor.h"
63 #include "nautilus-global-preferences.h"
64 #include "nautilus-link.h"
65 #include "nautilus-autorun.h"
66 #include "nautilus-trash-monitor.h"
67 #include "nautilus-file-utilities.h"
69 static gboolean confirm_trash_auto_value
;
71 /* TODO: TESTING!!! */
74 GIOSchedulerJob
*io_job
;
76 GtkWindow
*parent_window
;
78 NautilusProgressInfo
*progress
;
79 GCancellable
*cancellable
;
80 GHashTable
*skip_files
;
81 GHashTable
*skip_readdir_error
;
82 gboolean skip_all_error
;
83 gboolean skip_all_conflict
;
94 GdkPoint
*icon_positions
;
96 GHashTable
*debuting_files
;
97 NautilusCopyCallback done_callback
;
98 gpointer done_callback_data
;
105 gboolean user_cancel
;
106 NautilusDeleteCallback done_callback
;
107 gpointer done_callback_data
;
118 gboolean has_position
;
120 NautilusCreateCallback done_callback
;
121 gpointer done_callback_data
;
128 NautilusOpCallback done_callback
;
129 gpointer done_callback_data
;
135 NautilusOpCallback done_callback
;
136 gpointer done_callback_data
;
137 guint32 file_permissions
;
139 guint32 dir_permissions
;
153 int num_files_since_progress
;
161 guint64 last_report_time
;
162 int last_reported_files_left
;
165 #define SECONDS_NEEDED_FOR_RELIABLE_TRANSFER_RATE 15
166 #define NSEC_PER_SEC 1000000000
167 #define NSEC_PER_MSEC 1000000
169 #define IS_IO_ERROR(__error, KIND) (((__error)->domain == G_IO_ERROR && (__error)->code == G_IO_ERROR_ ## KIND))
171 #define SKIP _("_Skip")
172 #define SKIP_ALL _("S_kip All")
173 #define RETRY _("_Retry")
174 #define DELETE_ALL _("Delete _All")
175 #define REPLACE _("_Replace")
176 #define REPLACE_ALL _("Replace _All")
177 #define MERGE _("_Merge")
178 #define MERGE_ALL _("Merge _All")
180 static void scan_sources (GList
*files
,
181 SourceInfo
*source_info
,
186 static gboolean
empty_trash_job (GIOSchedulerJob
*io_job
,
187 GCancellable
*cancellable
,
191 format_time (int seconds
)
198 /* Just to make sure... */
203 return g_strdup_printf (ngettext ("%'d second","%'d seconds", (int) seconds
), (int) seconds
);
206 if (seconds
< 60*60) {
207 minutes
= (seconds
+ 30) / 60;
208 return g_strdup_printf (ngettext ("%'d minute", "%'d minutes", minutes
), minutes
);
211 hours
= seconds
/ (60*60);
213 if (seconds
< 60*60*4) {
216 minutes
= (seconds
- hours
* 60 * 60 + 30) / 60;
218 h
= g_strdup_printf (ngettext ("%'d hour", "%'d hours", hours
), hours
);
219 m
= g_strdup_printf (ngettext ("%'d minute", "%'d minutes", minutes
), minutes
);
220 res
= g_strconcat (h
, ", ", m
, NULL
);
226 return g_strdup_printf (ngettext ("approximately %'d hour",
227 "approximately %'d hours",
231 /* Note that we have these two separate functions with separate format
232 * strings for ease of localization.
236 get_link_name (const char *name
, int count
)
241 g_assert (name
!= NULL
);
244 g_warning ("bad count in get_link_name");
249 /* Handle special cases for low numbers.
250 * Perhaps for some locales we will need to add more.
254 g_assert_not_reached ();
257 /* appended to new link file */
258 format
= _("Link to %s");
261 /* appended to new link file */
262 format
= _("Another link to %s");
265 result
= g_strdup_printf (format
, name
);
268 /* Handle special cases for the first few numbers of each ten.
269 * For locales where getting this exactly right is difficult,
270 * these can just be made all the same as the general case below.
272 switch (count
% 10) {
274 /* Localizers: Feel free to leave out the "st" suffix
275 * if there's no way to do that nicely for a
276 * particular language.
278 format
= _("%'dst link to %s");
281 /* appended to new link file */
282 format
= _("%'dnd link to %s");
285 /* appended to new link file */
286 format
= _("%'drd link to %s");
289 /* appended to new link file */
290 format
= _("%'dth link to %s");
293 result
= g_strdup_printf (format
, count
, name
);
301 * Feel free to leave out the st, nd, rd and th suffix or
302 * make some or all of them match.
305 /* localizers: tag used to detect the first copy of a file */
306 static const char untranslated_copy_duplicate_tag
[] = N_(" (copy)");
307 /* localizers: tag used to detect the second copy of a file */
308 static const char untranslated_another_copy_duplicate_tag
[] = N_(" (another copy)");
310 /* localizers: tag used to detect the x11th copy of a file */
311 static const char untranslated_x11th_copy_duplicate_tag
[] = N_("th copy)");
312 /* localizers: tag used to detect the x12th copy of a file */
313 static const char untranslated_x12th_copy_duplicate_tag
[] = N_("th copy)");
314 /* localizers: tag used to detect the x13th copy of a file */
315 static const char untranslated_x13th_copy_duplicate_tag
[] = N_("th copy)");
317 /* localizers: tag used to detect the x1st copy of a file */
318 static const char untranslated_st_copy_duplicate_tag
[] = N_("st copy)");
319 /* localizers: tag used to detect the x2nd copy of a file */
320 static const char untranslated_nd_copy_duplicate_tag
[] = N_("nd copy)");
321 /* localizers: tag used to detect the x3rd copy of a file */
322 static const char untranslated_rd_copy_duplicate_tag
[] = N_("rd copy)");
324 /* localizers: tag used to detect the xxth copy of a file */
325 static const char untranslated_th_copy_duplicate_tag
[] = N_("th copy)");
327 #define COPY_DUPLICATE_TAG _(untranslated_copy_duplicate_tag)
328 #define ANOTHER_COPY_DUPLICATE_TAG _(untranslated_another_copy_duplicate_tag)
329 #define X11TH_COPY_DUPLICATE_TAG _(untranslated_x11th_copy_duplicate_tag)
330 #define X12TH_COPY_DUPLICATE_TAG _(untranslated_x12th_copy_duplicate_tag)
331 #define X13TH_COPY_DUPLICATE_TAG _(untranslated_x13th_copy_duplicate_tag)
333 #define ST_COPY_DUPLICATE_TAG _(untranslated_st_copy_duplicate_tag)
334 #define ND_COPY_DUPLICATE_TAG _(untranslated_nd_copy_duplicate_tag)
335 #define RD_COPY_DUPLICATE_TAG _(untranslated_rd_copy_duplicate_tag)
336 #define TH_COPY_DUPLICATE_TAG _(untranslated_th_copy_duplicate_tag)
338 /* localizers: appended to first file copy */
339 static const char untranslated_first_copy_duplicate_format
[] = N_("%s (copy)%s");
340 /* localizers: appended to second file copy */
341 static const char untranslated_second_copy_duplicate_format
[] = N_("%s (another copy)%s");
343 /* localizers: appended to x11th file copy */
344 static const char untranslated_x11th_copy_duplicate_format
[] = N_("%s (%'dth copy)%s");
345 /* localizers: appended to x12th file copy */
346 static const char untranslated_x12th_copy_duplicate_format
[] = N_("%s (%'dth copy)%s");
347 /* localizers: appended to x13th file copy */
348 static const char untranslated_x13th_copy_duplicate_format
[] = N_("%s (%'dth copy)%s");
350 /* localizers: appended to x1st file copy */
351 static const char untranslated_st_copy_duplicate_format
[] = N_("%s (%'dst copy)%s");
352 /* localizers: appended to x2nd file copy */
353 static const char untranslated_nd_copy_duplicate_format
[] = N_("%s (%'dnd copy)%s");
354 /* localizers: appended to x3rd file copy */
355 static const char untranslated_rd_copy_duplicate_format
[] = N_("%s (%'drd copy)%s");
356 /* localizers: appended to xxth file copy */
357 static const char untranslated_th_copy_duplicate_format
[] = N_("%s (%'dth copy)%s");
359 #define FIRST_COPY_DUPLICATE_FORMAT _(untranslated_first_copy_duplicate_format)
360 #define SECOND_COPY_DUPLICATE_FORMAT _(untranslated_second_copy_duplicate_format)
361 #define X11TH_COPY_DUPLICATE_FORMAT _(untranslated_x11th_copy_duplicate_format)
362 #define X12TH_COPY_DUPLICATE_FORMAT _(untranslated_x12th_copy_duplicate_format)
363 #define X13TH_COPY_DUPLICATE_FORMAT _(untranslated_x13th_copy_duplicate_format)
365 #define ST_COPY_DUPLICATE_FORMAT _(untranslated_st_copy_duplicate_format)
366 #define ND_COPY_DUPLICATE_FORMAT _(untranslated_nd_copy_duplicate_format)
367 #define RD_COPY_DUPLICATE_FORMAT _(untranslated_rd_copy_duplicate_format)
368 #define TH_COPY_DUPLICATE_FORMAT _(untranslated_th_copy_duplicate_format)
371 extract_string_until (const char *original
, const char *until_substring
)
375 g_assert ((int) strlen (original
) >= until_substring
- original
);
376 g_assert (until_substring
- original
>= 0);
378 result
= g_malloc (until_substring
- original
+ 1);
379 strncpy (result
, original
, until_substring
- original
);
380 result
[until_substring
- original
] = '\0';
385 /* Dismantle a file name, separating the base name, the file suffix and removing any
386 * (xxxcopy), etc. string. Figure out the count that corresponds to the given
387 * (xxxcopy) substring.
390 parse_previous_duplicate_name (const char *name
,
397 g_assert (name
[0] != '\0');
399 *suffix
= strchr (name
+ 1, '.');
400 if (*suffix
== NULL
|| (*suffix
)[1] == '\0') {
405 tag
= strstr (name
, COPY_DUPLICATE_TAG
);
408 /* handle case "foo. (copy)" */
411 *name_base
= extract_string_until (name
, tag
);
417 tag
= strstr (name
, ANOTHER_COPY_DUPLICATE_TAG
);
420 /* handle case "foo. (another copy)" */
423 *name_base
= extract_string_until (name
, tag
);
429 /* Check to see if we got one of st, nd, rd, th. */
430 tag
= strstr (name
, X11TH_COPY_DUPLICATE_TAG
);
433 tag
= strstr (name
, X12TH_COPY_DUPLICATE_TAG
);
436 tag
= strstr (name
, X13TH_COPY_DUPLICATE_TAG
);
440 tag
= strstr (name
, ST_COPY_DUPLICATE_TAG
);
443 tag
= strstr (name
, ND_COPY_DUPLICATE_TAG
);
446 tag
= strstr (name
, RD_COPY_DUPLICATE_TAG
);
449 tag
= strstr (name
, TH_COPY_DUPLICATE_TAG
);
452 /* If we got one of st, nd, rd, th, fish out the duplicate number. */
454 /* localizers: opening parentheses to match the "th copy)" string */
455 tag
= strstr (name
, _(" ("));
458 /* handle case "foo. (22nd copy)" */
461 *name_base
= extract_string_until (name
, tag
);
462 /* localizers: opening parentheses of the "th copy)" string */
463 if (sscanf (tag
, _(" (%'d"), count
) == 1) {
464 if (*count
< 1 || *count
> 1000000) {
465 /* keep the count within a reasonable range */
477 if (**suffix
!= '\0') {
478 *name_base
= extract_string_until (name
, *suffix
);
480 *name_base
= g_strdup (name
);
485 make_next_duplicate_name (const char *base
, const char *suffix
, int count
)
492 g_warning ("bad count %d in get_duplicate_name", count
);
498 /* Handle special cases for low numbers.
499 * Perhaps for some locales we will need to add more.
503 g_assert_not_reached ();
506 format
= FIRST_COPY_DUPLICATE_FORMAT
;
509 format
= SECOND_COPY_DUPLICATE_FORMAT
;
513 result
= g_strdup_printf (format
, base
, suffix
);
516 /* Handle special cases for the first few numbers of each ten.
517 * For locales where getting this exactly right is difficult,
518 * these can just be made all the same as the general case below.
521 /* Handle special cases for x11th - x20th.
523 switch (count
% 100) {
525 format
= X11TH_COPY_DUPLICATE_FORMAT
;
528 format
= X12TH_COPY_DUPLICATE_FORMAT
;
531 format
= X13TH_COPY_DUPLICATE_FORMAT
;
538 if (format
== NULL
) {
539 switch (count
% 10) {
541 format
= ST_COPY_DUPLICATE_FORMAT
;
544 format
= ND_COPY_DUPLICATE_FORMAT
;
547 format
= RD_COPY_DUPLICATE_FORMAT
;
550 /* The general case. */
551 format
= TH_COPY_DUPLICATE_FORMAT
;
556 result
= g_strdup_printf (format
, base
, count
, suffix
);
563 get_duplicate_name (const char *name
, int count_increment
)
570 parse_previous_duplicate_name (name
, &name_base
, &suffix
, &count
);
571 result
= make_next_duplicate_name (name_base
, suffix
, count
+ count_increment
);
579 has_invalid_xml_char (char *str
)
584 c
= g_utf8_get_char (str
);
585 /* characters XML permits */
589 (c
>= 0x20 && c
<= 0xD7FF) ||
590 (c
>= 0xE000 && c
<= 0xFFFD) ||
591 (c
>= 0x10000 && c
<= 0x10FFFF))) {
594 str
= g_utf8_next_char (str
);
601 custom_full_name_to_string (char *format
, va_list va
)
605 file
= va_arg (va
, GFile
*);
607 return g_file_get_parse_name (file
);
611 custom_full_name_skip (va_list *va
)
613 va_arg (*va
, GFile
*);
617 custom_basename_to_string (char *format
, va_list va
)
621 char *name
, *basename
, *tmp
;
623 file
= va_arg (va
, GFile
*);
625 info
= g_file_query_info (file
,
626 G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME
,
628 g_cancellable_get_current (),
633 name
= g_strdup (g_file_info_get_display_name (info
));
634 g_object_unref (info
);
638 basename
= g_file_get_basename (file
);
639 if (g_utf8_validate (basename
, -1, NULL
)) {
642 name
= g_uri_escape_string (basename
, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH
, TRUE
);
647 /* Some chars can't be put in the markup we use for the dialogs... */
648 if (has_invalid_xml_char (name
)) {
650 name
= g_uri_escape_string (name
, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH
, TRUE
);
658 custom_basename_skip (va_list *va
)
660 va_arg (*va
, GFile
*);
665 custom_size_to_string (char *format
, va_list va
)
669 size
= va_arg (va
, goffset
);
670 return g_format_size_for_display (size
);
674 custom_size_skip (va_list *va
)
676 va_arg (*va
, goffset
);
680 custom_time_to_string (char *format
, va_list va
)
684 secs
= va_arg (va
, int);
685 return format_time (secs
);
689 custom_time_skip (va_list *va
)
695 custom_mount_to_string (char *format
, va_list va
)
699 mount
= va_arg (va
, GMount
*);
700 return g_mount_get_name (mount
);
704 custom_mount_skip (va_list *va
)
706 va_arg (*va
, GMount
*);
710 static EelPrintfHandler handlers
[] = {
711 { 'F', custom_full_name_to_string
, custom_full_name_skip
},
712 { 'B', custom_basename_to_string
, custom_basename_skip
},
713 { 'S', custom_size_to_string
, custom_size_skip
},
714 { 'T', custom_time_to_string
, custom_time_skip
},
715 { 'V', custom_mount_to_string
, custom_mount_skip
},
721 f (const char *format
, ...) {
725 va_start (va
, format
);
726 res
= eel_strdup_vprintf_with_custom (handlers
, format
, va
);
732 #define op_job_new(__type, parent_window) ((__type *)(init_common (sizeof(__type), parent_window)))
735 init_common (gsize job_size
,
736 GtkWindow
*parent_window
)
741 common
= g_malloc0 (job_size
);
744 common
->parent_window
= parent_window
;
745 eel_add_weak_pointer (&common
->parent_window
);
747 common
->progress
= nautilus_progress_info_new ();
748 common
->cancellable
= nautilus_progress_info_get_cancellable (common
->progress
);
749 common
->time
= g_timer_new ();
751 common
->screen_num
= 0;
753 screen
= gtk_widget_get_screen (GTK_WIDGET (parent_window
));
754 common
->screen_num
= gdk_screen_get_number (screen
);
761 finalize_common (CommonJob
*common
)
763 nautilus_progress_info_finish (common
->progress
);
765 g_timer_destroy (common
->time
);
767 eel_remove_weak_pointer (&common
->parent_window
);
768 if (common
->skip_files
) {
769 g_hash_table_destroy (common
->skip_files
);
771 if (common
->skip_readdir_error
) {
772 g_hash_table_destroy (common
->skip_readdir_error
);
774 g_object_unref (common
->progress
);
775 g_object_unref (common
->cancellable
);
780 skip_file (CommonJob
*common
,
783 if (common
->skip_files
== NULL
) {
785 g_hash_table_new_full (g_file_hash
, (GEqualFunc
)g_file_equal
, g_object_unref
, NULL
);
788 g_hash_table_insert (common
->skip_files
, g_object_ref (file
), file
);
792 skip_readdir_error (CommonJob
*common
,
795 if (common
->skip_readdir_error
== NULL
) {
796 common
->skip_readdir_error
=
797 g_hash_table_new_full (g_file_hash
, (GEqualFunc
)g_file_equal
, g_object_unref
, NULL
);
800 g_hash_table_insert (common
->skip_readdir_error
, g_object_ref (dir
), dir
);
804 should_skip_file (CommonJob
*common
,
807 if (common
->skip_files
!= NULL
) {
808 return g_hash_table_lookup (common
->skip_files
, file
) != NULL
;
814 should_skip_readdir_error (CommonJob
*common
,
817 if (common
->skip_readdir_error
!= NULL
) {
818 return g_hash_table_lookup (common
->skip_readdir_error
, dir
) != NULL
;
826 static gboolean setup_autos
= FALSE
;
829 eel_preferences_add_auto_boolean (NAUTILUS_PREFERENCES_CONFIRM_TRASH
,
830 &confirm_trash_auto_value
);
835 can_delete_without_confirm (GFile
*file
)
837 if (g_file_has_uri_scheme (file
, "burn") ||
838 g_file_has_uri_scheme (file
, "x-nautilus-desktop")) {
846 can_delete_files_without_confirm (GList
*files
)
848 g_assert (files
!= NULL
);
850 while (files
!= NULL
) {
851 if (!can_delete_without_confirm (files
->data
)) {
862 GtkWindow
**parent_window
;
863 gboolean ignore_close_box
;
864 GtkMessageType message_type
;
865 const char *primary_text
;
866 const char *secondary_text
;
867 const char *details_text
;
868 const char **button_titles
;
871 } RunSimpleDialogData
;
874 do_run_simple_dialog (gpointer _data
)
876 RunSimpleDialogData
*data
= _data
;
877 const char *button_title
;
882 /* Create the dialog. */
883 dialog
= eel_alert_dialog_new (*data
->parent_window
,
888 data
->secondary_text
);
890 for (response_id
= 0;
891 data
->button_titles
[response_id
] != NULL
;
893 button_title
= data
->button_titles
[response_id
];
894 gtk_dialog_add_button (GTK_DIALOG (dialog
), button_title
, response_id
);
895 gtk_dialog_set_default_response (GTK_DIALOG (dialog
), response_id
);
898 if (data
->details_text
) {
899 eel_alert_dialog_set_details_label (EEL_ALERT_DIALOG (dialog
),
904 gtk_widget_show (dialog
);
905 result
= gtk_dialog_run (GTK_DIALOG (dialog
));
907 while ((result
== GTK_RESPONSE_NONE
|| result
== GTK_RESPONSE_DELETE_EVENT
) && data
->ignore_close_box
) {
908 gtk_widget_show (GTK_WIDGET (dialog
));
909 result
= gtk_dialog_run (GTK_DIALOG (dialog
));
912 gtk_object_destroy (GTK_OBJECT (dialog
));
914 data
->result
= result
;
919 /* NOTE: This frees the primary / secondary strings, in order to
920 avoid doing that everywhere. So, make sure they are strduped */
923 run_simple_dialog_va (CommonJob
*job
,
924 gboolean ignore_close_box
,
925 GtkMessageType message_type
,
927 char *secondary_text
,
928 const char *details_text
,
931 RunSimpleDialogData
*data
;
933 const char *button_title
;
934 GPtrArray
*ptr_array
;
936 g_timer_stop (job
->time
);
938 data
= g_new0 (RunSimpleDialogData
, 1);
939 data
->parent_window
= &job
->parent_window
;
940 data
->ignore_close_box
= ignore_close_box
;
941 data
->message_type
= message_type
;
942 data
->primary_text
= primary_text
;
943 data
->secondary_text
= secondary_text
;
944 data
->details_text
= details_text
;
946 ptr_array
= g_ptr_array_new ();
947 while ((button_title
= va_arg (varargs
, const char *)) != NULL
) {
948 g_ptr_array_add (ptr_array
, (char *)button_title
);
950 g_ptr_array_add (ptr_array
, NULL
);
951 data
->button_titles
= (const char **)g_ptr_array_free (ptr_array
, FALSE
);
953 nautilus_progress_info_pause (job
->progress
);
954 g_io_scheduler_job_send_to_mainloop (job
->io_job
,
955 do_run_simple_dialog
,
958 nautilus_progress_info_resume (job
->progress
);
961 g_free (data
->button_titles
);
964 g_timer_continue (job
->time
);
966 g_free (primary_text
);
967 g_free (secondary_text
);
972 #if 0 /* Not used at the moment */
974 run_simple_dialog (CommonJob
*job
,
975 gboolean ignore_close_box
,
976 GtkMessageType message_type
,
978 char *secondary_text
,
979 const char *details_text
,
985 va_start (varargs
, details_text
);
986 res
= run_simple_dialog_va (job
,
999 run_error (CommonJob
*job
,
1001 char *secondary_text
,
1002 const char *details_text
,
1008 va_start (varargs
, details_text
);
1009 res
= run_simple_dialog_va (job
,
1021 run_warning (CommonJob
*job
,
1023 char *secondary_text
,
1024 const char *details_text
,
1030 va_start (varargs
, details_text
);
1031 res
= run_simple_dialog_va (job
,
1033 GTK_MESSAGE_WARNING
,
1043 run_question (CommonJob
*job
,
1045 char *secondary_text
,
1046 const char *details_text
,
1052 va_start (varargs
, details_text
);
1053 res
= run_simple_dialog_va (job
,
1055 GTK_MESSAGE_QUESTION
,
1065 abort_job (CommonJob
*job
)
1067 g_cancellable_cancel (job
->cancellable
);
1072 job_aborted (CommonJob
*job
)
1074 return g_cancellable_is_cancelled (job
->cancellable
);
1078 confirm_delete_from_trash (CommonJob
*job
,
1085 /* Just Say Yes if the preference says not to confirm. */
1086 if (!confirm_trash_auto_value
) {
1090 file_count
= g_list_length (files
);
1091 g_assert (file_count
> 0);
1093 if (file_count
== 1) {
1094 prompt
= f (_("Are you sure you want to permanently delete \"%B\" "
1095 "from the trash?"), files
->data
);
1097 prompt
= f (ngettext("Are you sure you want to permanently delete "
1098 "the %'d selected item from the trash?",
1099 "Are you sure you want to permanently delete "
1100 "the %'d selected items from the trash?",
1105 response
= run_warning (job
,
1107 f (_("If you delete an item, it will be permanently lost.")),
1109 GTK_STOCK_CANCEL
, GTK_STOCK_DELETE
,
1112 return (response
== 1);
1116 confirm_empty_trash (CommonJob
*job
)
1121 /* Just Say Yes if the preference says not to confirm. */
1122 if (!confirm_trash_auto_value
) {
1126 prompt
= f (_("Empty all of the items from the trash?"));
1128 response
= run_warning (job
,
1130 f(_("If you choose to empty the trash, all items "
1131 "in it will be permanently lost. Please note "
1132 "that you can also delete them separately.")),
1134 GTK_STOCK_CANCEL
, GTK_STOCK_DELETE
,
1137 return (response
== 1);
1141 confirm_delete_directly (CommonJob
*job
,
1148 /* Just Say Yes if the preference says not to confirm. */
1149 if (!confirm_trash_auto_value
) {
1153 file_count
= g_list_length (files
);
1154 g_assert (file_count
> 0);
1156 if (can_delete_files_without_confirm (files
)) {
1160 if (file_count
== 1) {
1161 prompt
= f (_("Are you sure you want to permanently delete \"%B\"?"),
1164 prompt
= f (ngettext("Are you sure you want to permanently delete "
1165 "the %'d selected item?",
1166 "Are you sure you want to permanently delete "
1167 "the %'d selected items?", file_count
),
1171 response
= run_warning (job
,
1173 f (_("If you delete an item, it will be permanently lost.")),
1175 GTK_STOCK_CANCEL
, GTK_STOCK_DELETE
,
1178 return response
== 1;
1182 report_delete_progress (CommonJob
*job
,
1183 SourceInfo
*source_info
,
1184 TransferInfo
*transfer_info
)
1187 double elapsed
, transfer_rate
;
1191 now
= g_thread_gettime ();
1192 if (transfer_info
->last_report_time
!= 0 &&
1193 ABS (transfer_info
->last_report_time
- now
) < 100 * NSEC_PER_MSEC
) {
1196 transfer_info
->last_report_time
= now
;
1198 files_left
= source_info
->num_files
- transfer_info
->num_files
;
1200 /* Races and whatnot could cause this to be negative... */
1201 if (files_left
< 0) {
1205 nautilus_progress_info_take_status (job
->progress
,
1206 f (_("Deleting files")));
1208 elapsed
= g_timer_elapsed (job
->time
, NULL
);
1209 if (elapsed
< SECONDS_NEEDED_FOR_RELIABLE_TRANSFER_RATE
) {
1211 s
= f (ngettext ("%'d file left to delete",
1212 "%'d files left to delete",
1215 nautilus_progress_info_take_details (job
->progress
, s
);
1218 transfer_rate
= transfer_info
->num_files
/ elapsed
;
1219 remaining_time
= files_left
/ transfer_rate
;
1221 /* To translators: %T will expand to a time like "2 minutes" */
1222 s
= f (ngettext ("%'d file left to delete \xE2\x80\x94 %T left",
1223 "%'d files left to delete \xE2\x80\x94 %T left",
1225 files_left
, remaining_time
);
1226 nautilus_progress_info_take_details (job
->progress
, s
);
1229 if (source_info
->num_files
!= 0) {
1230 nautilus_progress_info_set_progress (job
->progress
, transfer_info
->num_files
, source_info
->num_files
);
1234 static void delete_file (CommonJob
*job
, GFile
*file
,
1235 gboolean
*skipped_file
,
1236 SourceInfo
*source_info
,
1237 TransferInfo
*transfer_info
,
1241 delete_dir (CommonJob
*job
, GFile
*dir
,
1242 gboolean
*skipped_file
,
1243 SourceInfo
*source_info
,
1244 TransferInfo
*transfer_info
,
1250 GFileEnumerator
*enumerator
;
1251 char *primary
, *secondary
, *details
;
1253 gboolean skip_error
;
1254 gboolean local_skipped_file
;
1256 local_skipped_file
= FALSE
;
1258 skip_error
= should_skip_readdir_error (job
, dir
);
1261 enumerator
= g_file_enumerate_children (dir
,
1262 G_FILE_ATTRIBUTE_STANDARD_NAME
,
1263 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS
,
1269 while (!job_aborted (job
) &&
1270 (info
= g_file_enumerator_next_file (enumerator
, job
->cancellable
, skip_error
?NULL
:&error
)) != NULL
) {
1271 file
= g_file_get_child (dir
,
1272 g_file_info_get_name (info
));
1273 delete_file (job
, file
, &local_skipped_file
, source_info
, transfer_info
, FALSE
);
1274 g_object_unref (file
);
1275 g_object_unref (info
);
1277 g_file_enumerator_close (enumerator
, job
->cancellable
, NULL
);
1278 g_object_unref (enumerator
);
1280 if (error
&& IS_IO_ERROR (error
, CANCELLED
)) {
1281 g_error_free (error
);
1283 primary
= f (_("Error while deleting."));
1286 if (IS_IO_ERROR (error
, PERMISSION_DENIED
)) {
1287 secondary
= f (_("Files in the folder \"%B\" cannot be deleted because you do "
1288 "not have permissions to see them."), dir
);
1290 secondary
= f (_("There was an error getting information about the files in the folder \"%B\"."), dir
);
1291 details
= error
->message
;
1294 response
= run_warning (job
,
1298 GTK_STOCK_CANCEL
, _("_Skip files"),
1301 g_error_free (error
);
1303 if (response
== 0 || response
== GTK_RESPONSE_DELETE_EVENT
) {
1305 } else if (response
== 1) {
1306 /* Skip: Do Nothing */
1307 local_skipped_file
= TRUE
;
1309 g_assert_not_reached ();
1313 } else if (IS_IO_ERROR (error
, CANCELLED
)) {
1314 g_error_free (error
);
1316 primary
= f (_("Error while deleting."));
1318 if (IS_IO_ERROR (error
, PERMISSION_DENIED
)) {
1319 secondary
= f (_("The folder \"%B\" cannot be deleted because you do not have "
1320 "permissions to read it."), dir
);
1322 secondary
= f (_("There was an error reading the folder \"%B\"."), dir
);
1323 details
= error
->message
;
1326 response
= run_warning (job
,
1330 GTK_STOCK_CANCEL
, SKIP
, RETRY
,
1333 g_error_free (error
);
1335 if (response
== 0 || response
== GTK_RESPONSE_DELETE_EVENT
) {
1337 } else if (response
== 1) {
1338 /* Skip: Do Nothing */
1339 local_skipped_file
= TRUE
;
1340 } else if (response
== 2) {
1343 g_assert_not_reached ();
1347 if (!job_aborted (job
) &&
1348 /* Don't delete dir if there was a skipped file */
1349 !local_skipped_file
) {
1350 if (!g_file_delete (dir
, job
->cancellable
, &error
)) {
1351 if (job
->skip_all_error
) {
1354 primary
= f (_("Error while deleting."));
1355 secondary
= f (_("Couldn't remove the folder %B."), dir
);
1356 details
= error
->message
;
1358 response
= run_warning (job
,
1362 GTK_STOCK_CANCEL
, SKIP_ALL
, SKIP
,
1365 if (response
== 0 || response
== GTK_RESPONSE_DELETE_EVENT
) {
1367 } else if (response
== 1) { /* skip all */
1368 job
->skip_all_error
= TRUE
;
1369 local_skipped_file
= TRUE
;
1370 } else if (response
== 2) { /* skip */
1371 local_skipped_file
= TRUE
;
1373 g_assert_not_reached ();
1377 g_error_free (error
);
1380 nautilus_file_changes_queue_schedule_metadata_remove (dir
);
1382 nautilus_file_changes_queue_file_removed (dir
);
1383 transfer_info
->num_files
++;
1384 report_delete_progress (job
, source_info
, transfer_info
);
1389 if (local_skipped_file
) {
1390 *skipped_file
= TRUE
;
1395 delete_file (CommonJob
*job
, GFile
*file
,
1396 gboolean
*skipped_file
,
1397 SourceInfo
*source_info
,
1398 TransferInfo
*transfer_info
,
1402 char *primary
, *secondary
, *details
;
1405 if (should_skip_file (job
, file
)) {
1406 *skipped_file
= TRUE
;
1411 if (g_file_delete (file
, job
->cancellable
, &error
)) {
1413 nautilus_file_changes_queue_schedule_metadata_remove (file
);
1415 nautilus_file_changes_queue_file_removed (file
);
1416 transfer_info
->num_files
++;
1417 report_delete_progress (job
, source_info
, transfer_info
);
1421 if (IS_IO_ERROR (error
, NOT_EMPTY
)) {
1422 g_error_free (error
);
1423 delete_dir (job
, file
,
1425 source_info
, transfer_info
,
1429 } else if (IS_IO_ERROR (error
, CANCELLED
)) {
1430 g_error_free (error
);
1433 if (job
->skip_all_error
) {
1436 primary
= f (_("Error while deleting."));
1437 secondary
= f (_("There was an error deleting %B."), file
);
1438 details
= error
->message
;
1440 response
= run_warning (job
,
1444 GTK_STOCK_CANCEL
, SKIP_ALL
, SKIP
,
1447 if (response
== 0 || response
== GTK_RESPONSE_DELETE_EVENT
) {
1449 } else if (response
== 1) { /* skip all */
1450 job
->skip_all_error
= TRUE
;
1451 } else if (response
== 2) { /* skip */
1454 g_assert_not_reached ();
1457 g_error_free (error
);
1460 *skipped_file
= TRUE
;
1464 delete_files (CommonJob
*job
, GList
*files
)
1468 SourceInfo source_info
;
1469 TransferInfo transfer_info
;
1470 gboolean skipped_file
;
1472 if (job_aborted (job
)) {
1476 scan_sources (files
,
1480 if (job_aborted (job
)) {
1484 g_timer_start (job
->time
);
1486 memset (&transfer_info
, 0, sizeof (transfer_info
));
1487 report_delete_progress (job
, &source_info
, &transfer_info
);
1490 l
!= NULL
&& !job_aborted (job
);
1494 skipped_file
= FALSE
;
1495 delete_file (job
, file
,
1497 &source_info
, &transfer_info
,
1503 report_trash_progress (CommonJob
*job
,
1510 files_left
= total_files
- files_trashed
;
1512 nautilus_progress_info_take_status (job
->progress
,
1513 f (_("Moving files to trash")));
1515 s
= f (ngettext ("%'d file left to trash",
1516 "%'d files left to trash",
1519 nautilus_progress_info_take_details (job
->progress
, s
);
1521 if (total_files
!= 0) {
1522 nautilus_progress_info_set_progress (job
->progress
, files_trashed
, total_files
);
1528 trash_files (CommonJob
*job
, GList
*files
)
1534 int total_files
, files_trashed
;
1535 char *primary
, *secondary
, *details
;
1538 if (job_aborted (job
)) {
1542 total_files
= g_list_length (files
);
1545 report_trash_progress (job
, files_trashed
, total_files
);
1549 l
!= NULL
&& !job_aborted (job
);
1554 if (!g_file_trash (file
, job
->cancellable
, &error
)) {
1555 if (job
->skip_all_error
) {
1559 if (job
->delete_all
) {
1560 to_delete
= g_list_prepend (to_delete
, file
);
1564 primary
= f (_("Cannot move file to trash, do you want to delete immediately?"));
1565 secondary
= f (_("The file \"%B\" cannot be moved to the trash."), file
);
1567 if (!IS_IO_ERROR (error
, NOT_SUPPORTED
)) {
1568 details
= error
->message
;
1571 response
= run_question (job
,
1575 GTK_STOCK_CANCEL
, SKIP_ALL
, SKIP
, DELETE_ALL
, GTK_STOCK_DELETE
,
1578 if (response
== 0 || response
== GTK_RESPONSE_DELETE_EVENT
) {
1580 } else if (response
== 1) { /* skip all */
1581 job
->skip_all_error
= TRUE
;
1582 } else if (response
== 2) { /* skip */
1584 } else if (response
== 3) { /* delete all */
1585 to_delete
= g_list_prepend (to_delete
, file
);
1586 job
->delete_all
= TRUE
;
1587 } else if (response
== 4) { /* delete */
1588 to_delete
= g_list_prepend (to_delete
, file
);
1592 g_error_free (error
);
1595 nautilus_file_changes_queue_schedule_metadata_remove (file
);
1596 nautilus_file_changes_queue_file_removed (file
);
1599 report_trash_progress (job
, files_trashed
, total_files
);
1604 to_delete
= g_list_reverse (to_delete
);
1605 delete_files (job
, to_delete
);
1606 g_list_free (to_delete
);
1611 delete_job_done (gpointer user_data
)
1614 GHashTable
*debuting_uris
;
1618 eel_g_object_list_free (job
->files
);
1620 if (job
->done_callback
) {
1621 debuting_uris
= g_hash_table_new_full (g_file_hash
, (GEqualFunc
)g_file_equal
, g_object_unref
, NULL
);
1622 job
->done_callback (debuting_uris
, job
->user_cancel
, job
->done_callback_data
);
1623 g_hash_table_unref (debuting_uris
);
1626 finalize_common ((CommonJob
*)job
);
1628 nautilus_file_changes_consume_changes (TRUE
);
1634 delete_job (GIOSchedulerJob
*io_job
,
1635 GCancellable
*cancellable
,
1638 DeleteJob
*job
= user_data
;
1639 GList
*to_trash_files
;
1640 GList
*to_delete_files
;
1645 gboolean must_confirm_delete_in_trash
;
1646 gboolean must_confirm_delete
;
1648 common
= (CommonJob
*)job
;
1649 common
->io_job
= io_job
;
1651 nautilus_progress_info_start (job
->common
.progress
);
1653 to_trash_files
= NULL
;
1654 to_delete_files
= NULL
;
1656 must_confirm_delete_in_trash
= FALSE
;
1657 must_confirm_delete
= FALSE
;
1659 for (l
= job
->files
; l
!= NULL
; l
= l
->next
) {
1662 if (job
->try_trash
&&
1663 g_file_has_uri_scheme (file
, "trash")) {
1664 must_confirm_delete_in_trash
= TRUE
;
1665 to_delete_files
= g_list_prepend (to_delete_files
, file
);
1666 } else if (can_delete_without_confirm (file
)) {
1667 to_delete_files
= g_list_prepend (to_delete_files
, file
);
1669 if (job
->try_trash
) {
1670 to_trash_files
= g_list_prepend (to_trash_files
, file
);
1672 must_confirm_delete
= TRUE
;
1673 to_delete_files
= g_list_prepend (to_delete_files
, file
);
1678 if (to_delete_files
!= NULL
) {
1679 to_delete_files
= g_list_reverse (to_delete_files
);
1681 if (must_confirm_delete_in_trash
) {
1682 confirmed
= confirm_delete_from_trash (common
, to_delete_files
);
1683 } else if (must_confirm_delete
) {
1684 confirmed
= confirm_delete_directly (common
, to_delete_files
);
1687 delete_files (common
, to_delete_files
);
1689 job
->user_cancel
= TRUE
;
1693 if (to_trash_files
!= NULL
) {
1694 to_trash_files
= g_list_reverse (to_trash_files
);
1696 trash_files (common
, to_trash_files
);
1699 g_list_free (to_trash_files
);
1700 g_list_free (to_delete_files
);
1702 g_io_scheduler_job_send_to_mainloop_async (io_job
,
1711 trash_or_delete_internal (GList
*files
,
1712 GtkWindow
*parent_window
,
1714 NautilusDeleteCallback done_callback
,
1715 gpointer done_callback_data
)
1721 /* TODO: special case desktop icon link files ... */
1723 job
= op_job_new (DeleteJob
, parent_window
);
1724 job
->files
= eel_g_object_list_copy (files
);
1725 job
->try_trash
= try_trash
;
1726 job
->user_cancel
= FALSE
;
1727 job
->done_callback
= done_callback
;
1728 job
->done_callback_data
= done_callback_data
;
1730 g_io_scheduler_push_job (delete_job
,
1738 nautilus_file_operations_trash_or_delete (GList
*files
,
1739 GtkWindow
*parent_window
,
1740 NautilusDeleteCallback done_callback
,
1741 gpointer done_callback_data
)
1743 trash_or_delete_internal (files
, parent_window
,
1745 done_callback
, done_callback_data
);
1749 nautilus_file_operations_delete (GList
*files
,
1750 GtkWindow
*parent_window
,
1751 NautilusDeleteCallback done_callback
,
1752 gpointer done_callback_data
)
1754 trash_or_delete_internal (files
, parent_window
,
1756 done_callback
, done_callback_data
);
1764 GtkWindow
*parent_window
;
1768 unmount_mount_callback (GObject
*source_object
,
1772 UnmountData
*data
= user_data
;
1779 unmounted
= g_mount_unmount_finish (G_MOUNT (source_object
),
1782 unmounted
= g_mount_eject_finish (G_MOUNT (source_object
),
1787 if (error
->code
!= G_IO_ERROR_FAILED_HANDLED
) {
1789 primary
= f (_("Unable to eject %V"), source_object
);
1791 primary
= f (_("Unable to unmount %V"), source_object
);
1793 eel_show_error_dialog (primary
,
1795 data
->parent_window
);
1798 g_error_free (error
);
1801 eel_remove_weak_pointer (&data
->parent_window
);
1802 g_object_unref (data
->mount
);
1807 do_unmount (UnmountData
*data
)
1810 g_mount_eject (data
->mount
,
1812 unmount_mount_callback
,
1815 g_mount_unmount (data
->mount
,
1817 unmount_mount_callback
,
1823 dir_has_files (GFile
*dir
)
1825 GFileEnumerator
*enumerator
;
1827 GFileInfo
*file_info
;
1831 enumerator
= g_file_enumerate_children (dir
,
1832 G_FILE_ATTRIBUTE_STANDARD_NAME
,
1836 file_info
= g_file_enumerator_next_file (enumerator
, NULL
, NULL
);
1837 if (file_info
!= NULL
) {
1839 g_object_unref (file_info
);
1842 g_file_enumerator_close (enumerator
, NULL
, NULL
);
1843 g_object_unref (enumerator
);
1851 get_trash_dirs_for_mount (GMount
*mount
)
1858 root
= g_mount_get_root (mount
);
1865 if (g_file_is_native (root
)) {
1866 relpath
= g_strdup_printf (".Trash/%d", getuid ());
1867 trash
= g_file_resolve_relative_path (root
, relpath
);
1870 list
= g_list_prepend (list
, g_file_get_child (trash
, "files"));
1871 list
= g_list_prepend (list
, g_file_get_child (trash
, "info"));
1873 g_object_unref (trash
);
1875 relpath
= g_strdup_printf (".Trash-%d", getuid ());
1876 trash
= g_file_get_child (root
, relpath
);
1879 list
= g_list_prepend (list
, g_file_get_child (trash
, "files"));
1880 list
= g_list_prepend (list
, g_file_get_child (trash
, "info"));
1882 g_object_unref (trash
);
1885 g_object_unref (root
);
1891 has_trash_files (GMount
*mount
)
1897 dirs
= get_trash_dirs_for_mount (mount
);
1901 for (l
= dirs
; l
!= NULL
; l
= l
->next
) {
1904 if (dir_has_files (dir
)) {
1910 eel_g_object_list_free (dirs
);
1917 prompt_empty_trash (GtkWindow
*parent_window
)
1924 if (parent_window
!= NULL
) {
1925 screen
= gtk_widget_get_screen (GTK_WIDGET (parent_window
));
1928 /* Do we need to be modal ? */
1929 dialog
= gtk_message_dialog_new (NULL
, GTK_DIALOG_MODAL
,
1930 GTK_MESSAGE_QUESTION
, GTK_BUTTONS_NONE
,
1931 _("Do you want to empty the trash before you unmount?"));
1932 gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog
),
1933 _("In order to regain the "
1934 "free space on this volume "
1935 "the trash must be emptied. "
1936 "All trashed items on the volume "
1937 "will be permanently lost."));
1938 gtk_dialog_add_buttons (GTK_DIALOG (dialog
),
1939 _("Don't Empty Trash"), GTK_RESPONSE_REJECT
,
1940 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
1941 _("Empty Trash"), GTK_RESPONSE_ACCEPT
, NULL
);
1942 gtk_dialog_set_default_response (GTK_DIALOG (dialog
), GTK_RESPONSE_ACCEPT
);
1943 gtk_window_set_title (GTK_WINDOW (dialog
), ""); /* as per HIG */
1944 gtk_window_set_skip_taskbar_hint (GTK_WINDOW (dialog
), TRUE
);
1946 gtk_window_set_screen (GTK_WINDOW (dialog
), screen
);
1948 atk_object_set_role (gtk_widget_get_accessible (dialog
), ATK_ROLE_ALERT
);
1949 gtk_window_set_wmclass (GTK_WINDOW (dialog
), "empty_trash",
1952 /* Make transient for the window group */
1953 gtk_widget_realize (dialog
);
1954 if (screen
!= NULL
) {
1955 gdk_window_set_transient_for (GTK_WIDGET (dialog
)->window
,
1956 gdk_screen_get_root_window (screen
));
1959 result
= gtk_dialog_run (GTK_DIALOG (dialog
));
1960 gtk_widget_destroy (dialog
);
1965 nautilus_file_operations_unmount_mount (GtkWindow
*parent_window
,
1968 gboolean check_trash
)
1973 data
= g_new0 (UnmountData
, 1);
1974 if (parent_window
) {
1975 data
->parent_window
= parent_window
;
1976 eel_add_weak_pointer (&data
->parent_window
);
1979 data
->eject
= eject
;
1980 data
->mount
= g_object_ref (mount
);
1982 if (check_trash
&& has_trash_files (mount
)) {
1983 response
= prompt_empty_trash (parent_window
);
1985 if (response
== GTK_RESPONSE_ACCEPT
) {
1988 job
= op_job_new (EmptyTrashJob
, parent_window
);
1989 job
->trash_dirs
= get_trash_dirs_for_mount (mount
);
1990 job
->done_callback
= (NautilusOpCallback
)do_unmount
;
1991 job
->done_callback_data
= data
;
1992 g_io_scheduler_push_job (empty_trash_job
,
1998 } else if (response
== GTK_RESPONSE_CANCEL
) {
1999 eel_remove_weak_pointer (&data
->parent_window
);
2000 g_object_unref (data
->mount
);
2010 volume_mount_cb (GObject
*source_object
,
2014 GMountOperation
*mount_op
= user_data
;
2020 if (!g_volume_mount_finish (G_VOLUME (source_object
), res
, &error
)) {
2021 if (error
->code
!= G_IO_ERROR_FAILED_HANDLED
) {
2022 name
= g_volume_get_name (G_VOLUME (source_object
));
2023 primary
= g_strdup_printf (_("Unable to mount %s"), name
);
2025 eel_show_error_dialog (primary
,
2030 g_error_free (error
);
2033 g_object_unref (mount_op
);
2038 nautilus_file_operations_mount_volume (GtkWindow
*parent_window
,
2041 GMountOperation
*mount_op
;
2043 mount_op
= eel_mount_operation_new (parent_window
);
2044 g_volume_mount (volume
, 0, mount_op
, NULL
, volume_mount_cb
, mount_op
);
2049 report_count_progress (CommonJob
*job
,
2050 SourceInfo
*source_info
)
2054 switch (source_info
->op
) {
2057 s
= f (ngettext("Preparing to copy %'d file (%S)",
2058 "Preparing to copy %'d files (%S)",
2059 source_info
->num_files
),
2060 source_info
->num_files
, source_info
->num_bytes
);
2063 s
= f (ngettext("Preparing to move %'d file (%S)",
2064 "Preparing to move %'d files (%S)",
2065 source_info
->num_files
),
2066 source_info
->num_files
, source_info
->num_bytes
);
2068 case OP_KIND_DELETE
:
2069 s
= f (ngettext("Preparing to delete %'d file (%S)",
2070 "Preparing to delete %'d files (%S)",
2071 source_info
->num_files
),
2072 source_info
->num_files
, source_info
->num_bytes
);
2075 s
= f (ngettext("Preparing to trash %'d file",
2076 "Preparing to trash %'d files",
2077 source_info
->num_files
),
2078 source_info
->num_files
);
2082 nautilus_progress_info_take_details (job
->progress
, s
);
2083 nautilus_progress_info_pulse_progress (job
->progress
);
2087 count_file (GFileInfo
*info
,
2089 SourceInfo
*source_info
)
2091 source_info
->num_files
+= 1;
2092 source_info
->num_bytes
+= g_file_info_get_size (info
);
2094 if (source_info
->num_files_since_progress
++ > 100) {
2095 report_count_progress (job
, source_info
);
2096 source_info
->num_files_since_progress
= 0;
2101 get_scan_primary (OpKind kind
)
2106 return f (_("Error while copying."));
2108 return f (_("Error while moving."));
2109 case OP_KIND_DELETE
:
2110 return f (_("Error while deleting."));
2112 return f (_("Error while moving files to trash."));
2117 scan_dir (GFile
*dir
,
2118 SourceInfo
*source_info
,
2125 GFileEnumerator
*enumerator
;
2126 char *primary
, *secondary
, *details
;
2128 SourceInfo saved_info
;
2130 saved_info
= *source_info
;
2134 enumerator
= g_file_enumerate_children (dir
,
2135 G_FILE_ATTRIBUTE_STANDARD_NAME
","
2136 G_FILE_ATTRIBUTE_STANDARD_TYPE
","
2137 G_FILE_ATTRIBUTE_STANDARD_SIZE
,
2138 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS
,
2143 while ((info
= g_file_enumerator_next_file (enumerator
, job
->cancellable
, &error
)) != NULL
) {
2144 count_file (info
, job
, source_info
);
2146 if (g_file_info_get_file_type (info
) == G_FILE_TYPE_DIRECTORY
) {
2147 subdir
= g_file_get_child (dir
,
2148 g_file_info_get_name (info
));
2150 /* Push to head, since we want depth-first */
2151 g_queue_push_head (dirs
, subdir
);
2154 g_object_unref (info
);
2156 g_file_enumerator_close (enumerator
, job
->cancellable
, NULL
);
2157 g_object_unref (enumerator
);
2159 if (error
&& IS_IO_ERROR (error
, CANCELLED
)) {
2160 g_error_free (error
);
2162 primary
= get_scan_primary (source_info
->op
);
2165 if (IS_IO_ERROR (error
, PERMISSION_DENIED
)) {
2166 secondary
= f (_("Files in the folder \"%B\" cannot be handled because you do "
2167 "not have permissions to see them."), dir
);
2169 secondary
= f (_("There was an error getting information about the files in the folder \"%B\"."), dir
);
2170 details
= error
->message
;
2173 response
= run_warning (job
,
2177 GTK_STOCK_CANCEL
, RETRY
, SKIP
,
2180 g_error_free (error
);
2182 if (response
== 0 || response
== GTK_RESPONSE_DELETE_EVENT
) {
2184 } else if (response
== 1) {
2185 *source_info
= saved_info
;
2187 } else if (response
== 2) {
2188 skip_readdir_error (job
, dir
);
2190 g_assert_not_reached ();
2194 } else if (job
->skip_all_error
) {
2195 g_error_free (error
);
2196 skip_file (job
, dir
);
2197 } else if (IS_IO_ERROR (error
, CANCELLED
)) {
2198 g_error_free (error
);
2200 primary
= get_scan_primary (source_info
->op
);
2203 if (IS_IO_ERROR (error
, PERMISSION_DENIED
)) {
2204 secondary
= f (_("The folder \"%B\" cannot be handled because you do not have "
2205 "permissions to read it."), dir
);
2207 secondary
= f (_("There was an error reading the folder \"%B\"."), dir
);
2208 details
= error
->message
;
2211 response
= run_warning (job
,
2215 GTK_STOCK_CANCEL
, SKIP_ALL
, SKIP
, RETRY
,
2218 g_error_free (error
);
2220 if (response
== 0 || response
== GTK_RESPONSE_DELETE_EVENT
) {
2222 } else if (response
== 1 || response
== 2) {
2223 if (response
== 1) {
2224 job
->skip_all_error
= TRUE
;
2226 skip_file (job
, dir
);
2227 } else if (response
== 3) {
2230 g_assert_not_reached ();
2236 scan_file (GFile
*file
,
2237 SourceInfo
*source_info
,
2249 dirs
= g_queue_new ();
2253 info
= g_file_query_info (file
,
2254 G_FILE_ATTRIBUTE_STANDARD_TYPE
","
2255 G_FILE_ATTRIBUTE_STANDARD_SIZE
,
2256 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS
,
2261 count_file (info
, job
, source_info
);
2263 if (g_file_info_get_file_type (info
) == G_FILE_TYPE_DIRECTORY
) {
2264 g_queue_push_head (dirs
, g_object_ref (file
));
2267 g_object_unref (info
);
2268 } else if (job
->skip_all_error
) {
2269 g_error_free (error
);
2270 skip_file (job
, file
);
2271 } else if (IS_IO_ERROR (error
, CANCELLED
)) {
2272 g_error_free (error
);
2274 primary
= get_scan_primary (source_info
->op
);
2277 if (IS_IO_ERROR (error
, PERMISSION_DENIED
)) {
2278 secondary
= f (_("The file \"%B\" cannot be handled because you do not have "
2279 "permissions to read it."), file
);
2281 secondary
= f (_("There was an error getting information about \"%B\"."), file
);
2282 details
= error
->message
;
2285 response
= run_warning (job
,
2289 GTK_STOCK_CANCEL
, SKIP_ALL
, SKIP
, RETRY
,
2292 g_error_free (error
);
2294 if (response
== 0 || response
== GTK_RESPONSE_DELETE_EVENT
) {
2296 } else if (response
== 1 || response
== 2) {
2297 if (response
== 1) {
2298 job
->skip_all_error
= TRUE
;
2300 skip_file (job
, file
);
2301 } else if (response
== 3) {
2304 g_assert_not_reached ();
2308 while (!job_aborted (job
) &&
2309 (dir
= g_queue_pop_head (dirs
)) != NULL
) {
2310 scan_dir (dir
, source_info
, job
, dirs
);
2311 g_object_unref (dir
);
2314 /* Free all from queue if we exited early */
2315 g_queue_foreach (dirs
, (GFunc
)g_object_unref
, NULL
);
2316 g_queue_free (dirs
);
2320 scan_sources (GList
*files
,
2321 SourceInfo
*source_info
,
2328 memset (source_info
, 0, sizeof (SourceInfo
));
2329 source_info
->op
= kind
;
2331 report_count_progress (job
, source_info
);
2333 for (l
= files
; l
!= NULL
&& !job_aborted (job
); l
= l
->next
) {
2341 /* Make sure we report the final count */
2342 report_count_progress (job
, source_info
);
2346 verify_destination (CommonJob
*job
,
2349 goffset required_size
)
2351 GFileInfo
*info
, *fsinfo
;
2354 char *primary
, *secondary
, *details
;
2356 GFileType file_type
;
2365 info
= g_file_query_info (dest
,
2366 G_FILE_ATTRIBUTE_STANDARD_TYPE
","
2367 G_FILE_ATTRIBUTE_ID_FILESYSTEM
,
2373 if (IS_IO_ERROR (error
, CANCELLED
)) {
2374 g_error_free (error
);
2378 primary
= f (_("Error while copying to \"%B\"."), dest
);
2381 if (IS_IO_ERROR (error
, PERMISSION_DENIED
)) {
2382 secondary
= f (_("You don't have permissions to access the destination folder."));
2384 secondary
= f (_("There was an error getting information about the destination."));
2385 details
= error
->message
;
2388 response
= run_error (job
,
2392 GTK_STOCK_CANCEL
, RETRY
,
2395 g_error_free (error
);
2397 if (response
== 0 || response
== GTK_RESPONSE_DELETE_EVENT
) {
2399 } else if (response
== 1) {
2402 g_assert_not_reached ();
2408 file_type
= g_file_info_get_file_type (info
);
2412 g_strdup (g_file_info_get_attribute_string (info
,
2413 G_FILE_ATTRIBUTE_ID_FILESYSTEM
));
2416 g_object_unref (info
);
2418 if (file_type
!= G_FILE_TYPE_DIRECTORY
) {
2419 primary
= f (_("Error while copying to \"%B\"."), dest
);
2420 secondary
= f (_("The destination is not a folder."));
2422 response
= run_error (job
,
2433 fsinfo
= g_file_query_filesystem_info (dest
,
2434 G_FILE_ATTRIBUTE_FILESYSTEM_FREE
","
2435 G_FILE_ATTRIBUTE_FILESYSTEM_READONLY
,
2438 if (fsinfo
== NULL
) {
2439 /* All sorts of things can go wrong getting the fs info (like not supported)
2440 * only check these things if the fs returns them
2445 if (required_size
> 0 &&
2446 g_file_info_has_attribute (fsinfo
, G_FILE_ATTRIBUTE_FILESYSTEM_FREE
)) {
2447 free_size
= g_file_info_get_attribute_uint64 (fsinfo
,
2448 G_FILE_ATTRIBUTE_FILESYSTEM_FREE
);
2450 if (free_size
< required_size
) {
2451 primary
= f (_("Error while copying to \"%B\"."), dest
);
2452 secondary
= f(_("There is not enough space on the destination. Try to remove files to make space."));
2454 details
= f (_("There is %S available, but %S is required."), free_size
, required_size
);
2456 response
= run_warning (job
,
2460 GTK_STOCK_CANCEL
, RETRY
,
2463 if (response
== 0 || response
== GTK_RESPONSE_DELETE_EVENT
) {
2465 } else if (response
== 1) {
2468 g_assert_not_reached ();
2473 if (!job_aborted (job
) &&
2474 g_file_info_get_attribute_boolean (fsinfo
,
2475 G_FILE_ATTRIBUTE_FILESYSTEM_READONLY
)) {
2476 primary
= f (_("Error while copying to \"%B\"."), dest
);
2477 secondary
= f (_("The destination is read-only."));
2479 response
= run_error (job
,
2486 g_error_free (error
);
2491 g_object_unref (fsinfo
);
2495 report_copy_progress (CopyMoveJob
*copy_job
,
2496 SourceInfo
*source_info
,
2497 TransferInfo
*transfer_info
)
2501 double elapsed
, transfer_rate
;
2507 job
= (CommonJob
*)copy_job
;
2509 is_move
= copy_job
->is_move
;
2511 now
= g_thread_gettime ();
2513 if (transfer_info
->last_report_time
!= 0 &&
2514 ABS (transfer_info
->last_report_time
- now
) < 100 * NSEC_PER_MSEC
) {
2517 transfer_info
->last_report_time
= now
;
2519 files_left
= source_info
->num_files
- transfer_info
->num_files
;
2521 /* Races and whatnot could cause this to be negative... */
2522 if (files_left
< 0) {
2526 if (files_left
!= transfer_info
->last_reported_files_left
||
2527 transfer_info
->last_reported_files_left
== 0) {
2528 /* Avoid changing this unless files_left changed since last time */
2529 transfer_info
->last_reported_files_left
= files_left
;
2531 if (source_info
->num_files
== 1) {
2532 if (copy_job
->destination
!= NULL
) {
2533 nautilus_progress_info_take_status (job
->progress
,
2535 _("Moving \"%B\" to \"%B\""):
2536 _("Copying \"%B\" to \"%B\""),
2537 (GFile
*)copy_job
->files
->data
,
2538 copy_job
->destination
));
2540 nautilus_progress_info_take_status (job
->progress
,
2541 f (_("Duplicating \"%B\""),
2542 (GFile
*)copy_job
->files
->data
));
2544 } else if (copy_job
->files
!= NULL
&&
2545 copy_job
->files
->next
== NULL
) {
2546 if (copy_job
->destination
!= NULL
) {
2547 nautilus_progress_info_take_status (job
->progress
,
2549 ngettext ("Moving %'d file (in \"%B\") to \"%B\"",
2550 "Moving %'d files (in \"%B\") to \"%B\"",
2553 ngettext ("Copying %'d file (in \"%B\") to \"%B\"",
2554 "Copying %'d files (in \"%B\") to \"%B\"",
2557 (GFile
*)copy_job
->files
->data
,
2558 copy_job
->destination
));
2560 nautilus_progress_info_take_status (job
->progress
,
2561 f (ngettext ("Duplicating %'d file (in \"%B\")",
2562 "Duplicating %'d files (in \"%B\")",
2565 (GFile
*)copy_job
->files
->data
));
2568 if (copy_job
->destination
!= NULL
) {
2569 nautilus_progress_info_take_status (job
->progress
,
2571 ngettext ("Moving %'d file to \"%B\"",
2572 "Moving %'d files to \"%B\"",
2575 ngettext ("Copying %'d file to \"%B\"",
2576 "Copying %'d files to \"%B\"",
2578 files_left
, copy_job
->destination
));
2580 nautilus_progress_info_take_status (job
->progress
,
2581 f (ngettext ("Duplicating %'d file",
2582 "Duplicating %'d files",
2589 total_size
= MAX (source_info
->num_bytes
, transfer_info
->num_bytes
);
2591 elapsed
= g_timer_elapsed (job
->time
, NULL
);
2594 transfer_rate
= transfer_info
->num_bytes
/ elapsed
;
2597 if (elapsed
< SECONDS_NEEDED_FOR_RELIABLE_TRANSFER_RATE
&&
2598 transfer_rate
> 0) {
2600 /* To translators: %S will expand to a size like "2 bytes" or "3 MB", so something like "4 kb of 4 MB" */
2601 s
= f (_("%S of %S"), transfer_info
->num_bytes
, total_size
);
2602 nautilus_progress_info_take_details (job
->progress
, s
);
2605 remaining_time
= (total_size
- transfer_info
->num_bytes
) / transfer_rate
;
2607 /* To translators: %S will expand to a size like "2 bytes" or "3 MB", %T to a time duration like
2608 * "2 minutes". So the whole thing will be something like "2 kb of 4 MB -- 2 hours left (4kb/sec)"
2610 s
= f (_("%S of %S \xE2\x80\x94 %T left (%S/sec)"),
2611 transfer_info
->num_bytes
, total_size
,
2613 (goffset
)transfer_rate
);
2614 nautilus_progress_info_take_details (job
->progress
, s
);
2617 nautilus_progress_info_set_progress (job
->progress
, transfer_info
->num_bytes
, total_size
);
2621 get_unique_target_file (GFile
*src
,
2626 const char *editname
, *end
;
2627 char *basename
, *new_name
;
2632 info
= g_file_query_info (src
,
2633 G_FILE_ATTRIBUTE_STANDARD_EDIT_NAME
,
2636 editname
= g_file_info_get_attribute_string (info
, G_FILE_ATTRIBUTE_STANDARD_EDIT_NAME
);
2638 if (editname
!= NULL
) {
2639 new_name
= get_duplicate_name (editname
, count
);
2640 dest
= g_file_get_child_for_display_name (dest_dir
, new_name
, NULL
);
2644 g_object_unref (info
);
2648 basename
= g_file_get_basename (src
);
2650 if (g_utf8_validate (basename
, -1, NULL
)) {
2651 new_name
= get_duplicate_name (basename
, count
);
2652 dest
= g_file_get_child_for_display_name (dest_dir
, new_name
, NULL
);
2657 end
= strrchr (basename
, '.');
2659 count
+= atoi (end
+ 1);
2661 new_name
= g_strdup_printf ("%s.%d", basename
, count
);
2662 dest
= g_file_get_child (dest_dir
, new_name
);
2673 get_target_file_for_link (GFile
*src
,
2677 const char *editname
;
2678 char *basename
, *new_name
;
2683 info
= g_file_query_info (src
,
2684 G_FILE_ATTRIBUTE_STANDARD_EDIT_NAME
,
2687 editname
= g_file_info_get_attribute_string (info
, G_FILE_ATTRIBUTE_STANDARD_EDIT_NAME
);
2689 if (editname
!= NULL
) {
2690 new_name
= get_link_name (editname
, count
);
2691 dest
= g_file_get_child_for_display_name (dest_dir
, new_name
, NULL
);
2695 g_object_unref (info
);
2699 basename
= g_file_get_basename (src
);
2701 if (g_utf8_validate (basename
, -1, NULL
)) {
2702 new_name
= get_link_name (basename
, count
);
2703 dest
= g_file_get_child_for_display_name (dest_dir
, new_name
, NULL
);
2709 new_name
= g_strdup_printf ("%s.lnk", basename
);
2711 new_name
= g_strdup_printf ("%s.lnk%d", basename
, count
);
2713 dest
= g_file_get_child (dest_dir
, new_name
);
2724 get_target_file (GFile
*src
,
2731 const char *copyname
;
2735 info
= g_file_query_info (src
,
2736 G_FILE_ATTRIBUTE_STANDARD_COPY_NAME
,
2740 copyname
= g_file_info_get_attribute_string (info
, G_FILE_ATTRIBUTE_STANDARD_COPY_NAME
);
2743 dest
= g_file_get_child_for_display_name (dest_dir
, copyname
, NULL
);
2746 g_object_unref (info
);
2751 basename
= g_file_get_basename (src
);
2752 dest
= g_file_get_child (dest_dir
, basename
);
2760 has_fs_id (GFile
*file
, const char *fs_id
)
2767 info
= g_file_query_info (file
,
2768 G_FILE_ATTRIBUTE_ID_FILESYSTEM
,
2769 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS
,
2773 id
= g_file_info_get_attribute_string (info
, G_FILE_ATTRIBUTE_ID_FILESYSTEM
);
2775 if (id
&& strcmp (id
, fs_id
) == 0) {
2779 g_object_unref (info
);
2786 is_dir (GFile
*file
)
2792 info
= g_file_query_info (file
,
2793 G_FILE_ATTRIBUTE_STANDARD_TYPE
,
2794 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS
,
2797 res
= g_file_info_get_file_type (info
) == G_FILE_TYPE_DIRECTORY
;
2798 g_object_unref (info
);
2804 static void copy_move_file (CopyMoveJob
*job
,
2808 gboolean unique_names
,
2809 SourceInfo
*source_info
,
2810 TransferInfo
*transfer_info
,
2811 GHashTable
*debuting_files
,
2814 gboolean
*skipped_file
);
2817 create_dest_dir (CommonJob
*job
,
2822 char *primary
, *secondary
, *details
;
2826 /* First create the directory, then copy stuff to it before
2827 copying the attributes, because we need to be sure we can write to it */
2830 if (!g_file_make_directory (dest
, job
->cancellable
, &error
)) {
2831 if (IS_IO_ERROR (error
, CANCELLED
)) {
2832 g_error_free (error
);
2835 primary
= f (_("Error while copying."));
2838 if (IS_IO_ERROR (error
, PERMISSION_DENIED
)) {
2839 secondary
= f (_("The folder \"%B\" cannot be copied because you do not have "
2840 "permissions to create it in the destination."), src
);
2842 secondary
= f (_("There was an error creating the folder \"%B\"."), src
);
2843 details
= error
->message
;
2846 response
= run_warning (job
,
2850 GTK_STOCK_CANCEL
, SKIP
, RETRY
,
2853 g_error_free (error
);
2855 if (response
== 0 || response
== GTK_RESPONSE_DELETE_EVENT
) {
2857 } else if (response
== 1) {
2858 /* Skip: Do Nothing */
2859 } else if (response
== 2) {
2862 g_assert_not_reached ();
2866 nautilus_file_changes_queue_file_added (dest
);
2871 copy_move_directory (CopyMoveJob
*copy_job
,
2875 gboolean create_dest
,
2876 SourceInfo
*source_info
,
2877 TransferInfo
*transfer_info
,
2878 GHashTable
*debuting_files
,
2879 gboolean
*skipped_file
)
2884 GFileEnumerator
*enumerator
;
2885 char *primary
, *secondary
, *details
;
2887 gboolean skip_error
;
2888 gboolean local_skipped_file
;
2891 job
= (CommonJob
*)copy_job
;
2894 if (!create_dest_dir (job
, src
, dest
)) {
2895 *skipped_file
= TRUE
;
2898 if (debuting_files
) {
2899 g_hash_table_replace (debuting_files
, g_object_ref (dest
), GINT_TO_POINTER (TRUE
));
2904 local_skipped_file
= FALSE
;
2906 skip_error
= should_skip_readdir_error (job
, src
);
2909 enumerator
= g_file_enumerate_children (src
,
2910 G_FILE_ATTRIBUTE_STANDARD_NAME
,
2911 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS
,
2917 while (!job_aborted (job
) &&
2918 (info
= g_file_enumerator_next_file (enumerator
, job
->cancellable
, skip_error
?NULL
:&error
)) != NULL
) {
2919 src_file
= g_file_get_child (src
,
2920 g_file_info_get_name (info
));
2921 copy_move_file (copy_job
, src_file
, dest
, same_fs
, FALSE
, source_info
, transfer_info
, NULL
, NULL
, FALSE
, &local_skipped_file
);
2922 g_object_unref (src_file
);
2923 g_object_unref (info
);
2925 g_file_enumerator_close (enumerator
, job
->cancellable
, NULL
);
2926 g_object_unref (enumerator
);
2928 if (error
&& IS_IO_ERROR (error
, CANCELLED
)) {
2929 g_error_free (error
);
2931 if (copy_job
->is_move
) {
2932 primary
= f (_("Error while moving."));
2934 primary
= f (_("Error while copying."));
2938 if (IS_IO_ERROR (error
, PERMISSION_DENIED
)) {
2939 secondary
= f (_("Files in the folder \"%B\" cannot be copied because you do "
2940 "not have permissions to see them."), src
);
2942 secondary
= f (_("There was an error getting information about the files in the folder \"%B\"."), src
);
2943 details
= error
->message
;
2946 response
= run_warning (job
,
2950 GTK_STOCK_CANCEL
, _("_Skip files"),
2953 g_error_free (error
);
2955 if (response
== 0 || response
== GTK_RESPONSE_DELETE_EVENT
) {
2957 } else if (response
== 1) {
2958 /* Skip: Do Nothing */
2959 local_skipped_file
= TRUE
;
2961 g_assert_not_reached ();
2965 /* Count the copied directory as a file */
2966 transfer_info
->num_files
++;
2967 report_copy_progress (copy_job
, source_info
, transfer_info
);
2969 if (debuting_files
) {
2970 g_hash_table_replace (debuting_files
, g_object_ref (dest
), GINT_TO_POINTER (create_dest
));
2972 } else if (IS_IO_ERROR (error
, CANCELLED
)) {
2973 g_error_free (error
);
2975 if (copy_job
->is_move
) {
2976 primary
= f (_("Error while moving."));
2978 primary
= f (_("Error while copying."));
2982 if (IS_IO_ERROR (error
, PERMISSION_DENIED
)) {
2983 secondary
= f (_("The folder \"%B\" cannot be copied because you do not have "
2984 "permissions to read it."), src
);
2986 secondary
= f (_("There was an error reading the folder \"%B\"."), src
);
2987 details
= error
->message
;
2990 response
= run_warning (job
,
2994 GTK_STOCK_CANCEL
, SKIP
, RETRY
,
2997 g_error_free (error
);
2999 if (response
== 0 || response
== GTK_RESPONSE_DELETE_EVENT
) {
3001 } else if (response
== 1) {
3002 /* Skip: Do Nothing */
3003 local_skipped_file
= TRUE
;
3004 } else if (response
== 2) {
3007 g_assert_not_reached ();
3012 /* Ignore errors here. Failure to copy metadata is not a hard error */
3013 g_file_copy_attributes (src
, dest
,
3014 G_FILE_COPY_NOFOLLOW_SYMLINKS
,
3015 job
->cancellable
, NULL
);
3018 if (!job_aborted (job
) && copy_job
->is_move
&&
3019 /* Don't delete source if there was a skipped file */
3020 !local_skipped_file
) {
3021 if (!g_file_delete (src
, job
->cancellable
, &error
)) {
3022 if (job
->skip_all_error
) {
3025 primary
= f (_("Error while moving \"%B\"."), src
);
3026 secondary
= f (_("Couldn't remove the source folder."));
3027 details
= error
->message
;
3029 response
= run_warning (job
,
3033 GTK_STOCK_CANCEL
, SKIP_ALL
, SKIP
,
3036 if (response
== 0 || response
== GTK_RESPONSE_DELETE_EVENT
) {
3038 } else if (response
== 1) { /* skip all */
3039 job
->skip_all_error
= TRUE
;
3040 local_skipped_file
= TRUE
;
3041 } else if (response
== 2) { /* skip */
3042 local_skipped_file
= TRUE
;
3044 g_assert_not_reached ();
3048 g_error_free (error
);
3052 if (local_skipped_file
) {
3053 *skipped_file
= TRUE
;
3058 remove_target_recursively (CommonJob
*job
,
3060 GFile
*toplevel_dest
,
3063 GFileEnumerator
*enumerator
;
3067 char *primary
, *secondary
, *details
;
3074 enumerator
= g_file_enumerate_children (file
,
3075 G_FILE_ATTRIBUTE_STANDARD_NAME
,
3076 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS
,
3082 while (!job_aborted (job
) &&
3083 (info
= g_file_enumerator_next_file (enumerator
, job
->cancellable
, &error
)) != NULL
) {
3084 child
= g_file_get_child (file
,
3085 g_file_info_get_name (info
));
3086 if (!remove_target_recursively (job
, src
, toplevel_dest
, child
)) {
3090 g_object_unref (child
);
3091 g_object_unref (info
);
3093 g_file_enumerator_close (enumerator
, job
->cancellable
, NULL
);
3094 g_object_unref (enumerator
);
3096 } else if (IS_IO_ERROR (error
, NOT_DIRECTORY
)) {
3097 /* Not a dir, continue */
3098 g_error_free (error
);
3100 } else if (IS_IO_ERROR (error
, CANCELLED
)) {
3101 g_error_free (error
);
3103 if (job
->skip_all_error
) {
3107 primary
= f (_("Error while copying \"%B\"."), src
);
3108 secondary
= f (_("Couldn't remove files from the already existing folder %F."), file
);
3109 details
= error
->message
;
3111 response
= run_warning (job
,
3115 GTK_STOCK_CANCEL
, SKIP_ALL
, SKIP
,
3118 if (response
== 0 || response
== GTK_RESPONSE_DELETE_EVENT
) {
3120 } else if (response
== 1) { /* skip all */
3121 job
->skip_all_error
= TRUE
;
3122 } else if (response
== 2) { /* skip */
3125 g_assert_not_reached ();
3128 g_error_free (error
);
3139 if (!g_file_delete (file
, job
->cancellable
, &error
)) {
3140 if (job
->skip_all_error
||
3141 IS_IO_ERROR (error
, CANCELLED
)) {
3144 primary
= f (_("Error while copying \"%B\"."), src
);
3145 secondary
= f (_("Couldn't remove the already existing file %F."), file
);
3146 details
= error
->message
;
3148 response
= run_warning (job
,
3152 GTK_STOCK_CANCEL
, SKIP_ALL
, SKIP
,
3155 if (response
== 0 || response
== GTK_RESPONSE_DELETE_EVENT
) {
3157 } else if (response
== 1) { /* skip all */
3158 job
->skip_all_error
= TRUE
;
3159 } else if (response
== 2) { /* skip */
3162 g_assert_not_reached ();
3166 g_error_free (error
);
3170 nautilus_file_changes_queue_file_removed (file
);
3171 nautilus_file_changes_queue_schedule_metadata_remove (file
);
3180 SourceInfo
*source_info
;
3181 TransferInfo
*transfer_info
;
3185 copy_file_progress_callback (goffset current_num_bytes
,
3186 goffset total_num_bytes
,
3189 ProgressData
*pdata
;
3194 new_size
= current_num_bytes
- pdata
->last_size
;
3197 pdata
->transfer_info
->num_bytes
+= new_size
;
3198 pdata
->last_size
= current_num_bytes
;
3199 report_copy_progress (pdata
->job
,
3201 pdata
->transfer_info
);
3205 /* Debuting files is non-NULL only for toplevel items */
3207 copy_move_file (CopyMoveJob
*copy_job
,
3211 gboolean unique_names
,
3212 SourceInfo
*source_info
,
3213 TransferInfo
*transfer_info
,
3214 GHashTable
*debuting_files
,
3217 gboolean
*skipped_file
)
3221 GFileCopyFlags flags
;
3222 char *primary
, *secondary
, *details
;
3225 gboolean would_recurse
;
3230 job
= (CommonJob
*)copy_job
;
3232 if (should_skip_file (job
, src
)) {
3233 *skipped_file
= TRUE
;
3240 dest
= get_unique_target_file (src
, dest_dir
, same_fs
, unique_name_nr
++);
3242 dest
= get_target_file (src
, dest_dir
, same_fs
);
3248 flags
= G_FILE_COPY_NOFOLLOW_SYMLINKS
;
3250 flags
|= G_FILE_COPY_OVERWRITE
;
3252 pdata
.job
= copy_job
;
3253 pdata
.last_size
= 0;
3254 pdata
.source_info
= source_info
;
3255 pdata
.transfer_info
= transfer_info
;
3257 if (copy_job
->is_move
) {
3258 res
= g_file_move (src
, dest
,
3261 copy_file_progress_callback
,
3265 res
= g_file_copy (src
, dest
,
3268 copy_file_progress_callback
,
3274 transfer_info
->num_files
++;
3275 report_copy_progress (copy_job
, source_info
, transfer_info
);
3277 if (debuting_files
) {
3278 nautilus_file_changes_queue_schedule_metadata_copy (src
, dest
);
3280 nautilus_file_changes_queue_schedule_position_set (dest
, *position
, job
->screen_num
);
3282 nautilus_file_changes_queue_schedule_position_remove (dest
);
3285 g_hash_table_replace (debuting_files
, g_object_ref (dest
), GINT_TO_POINTER (TRUE
));
3287 nautilus_file_changes_queue_file_added (dest
);
3288 g_object_unref (dest
);
3294 IS_IO_ERROR (error
, EXISTS
)) {
3298 g_object_unref (dest
);
3299 dest
= get_unique_target_file (src
, dest_dir
, same_fs
, unique_name_nr
++);
3300 g_error_free (error
);
3305 if (is_dir (dest
)) {
3308 primary
= f (_("A folder named \"%B\" already exists. Do you want to merge the source folder?"),
3310 secondary
= f (_("The source folder already exists in \"%B\". "
3311 "Merging will ask for confirmation before replacing any files in the folder that conflict with the files being copied."),
3315 primary
= f (_("A folder named \"%B\" already exists. Do you want to replace it?"),
3317 secondary
= f (_("The folder already exists in \"%F\". "
3318 "Replacing it will remove all files in the folder."),
3322 primary
= f (_("A file named \"%B\" already exists. Do you want to replace it?"),
3324 secondary
= f (_("The file already exists in \"%F\". "
3325 "Replacing it will overwrite its content."),
3329 if ((is_merge
&& job
->merge_all
) ||
3330 (!is_merge
&& job
->replace_all
)) {
3333 g_error_free (error
);
3339 if (job
->skip_all_conflict
) {
3342 g_error_free (error
);
3347 response
= run_warning (job
,
3353 is_merge
?MERGE_ALL
:REPLACE_ALL
,
3355 is_merge
?MERGE
:REPLACE
,
3358 g_error_free (error
);
3360 if (response
== 0 || response
== GTK_RESPONSE_DELETE_EVENT
) {
3362 } else if (response
== 1 || response
== 3) { /* skip all / skip */
3363 if (response
== 1) {
3364 job
->skip_all_conflict
= TRUE
;
3366 } else if (response
== 2 || response
== 4) { /* merge/replace all / merge/replace*/
3367 if (response
== 2) {
3369 job
->merge_all
= TRUE
;
3371 job
->replace_all
= TRUE
;
3377 g_assert_not_reached ();
3381 else if (overwrite
&&
3382 IS_IO_ERROR (error
, IS_DIRECTORY
)) {
3384 g_error_free (error
);
3386 if (remove_target_recursively (job
, src
, dest
, dest
)) {
3391 /* Needs to recurse */
3392 else if (IS_IO_ERROR (error
, WOULD_RECURSE
) ||
3393 IS_IO_ERROR (error
, WOULD_MERGE
)) {
3394 would_recurse
= error
->code
== G_IO_ERROR_WOULD_RECURSE
;
3395 g_error_free (error
);
3397 if (overwrite
&& would_recurse
) {
3400 /* Copying a dir onto file, first remove the file */
3401 if (!g_file_delete (dest
, job
->cancellable
, &error
) &&
3402 !IS_IO_ERROR (error
, NOT_FOUND
)) {
3403 if (job
->skip_all_error
) {
3404 g_error_free (error
);
3407 if (copy_job
->is_move
) {
3408 primary
= f (_("Error while moving \"%B\"."), src
);
3410 primary
= f (_("Error while copying \"%B\"."), src
);
3412 secondary
= f (_("Couldn't remove the already existing file with the same name in %F."), dest_dir
);
3413 details
= error
->message
;
3415 response
= run_warning (job
,
3419 GTK_STOCK_CANCEL
, SKIP_ALL
, SKIP
,
3422 g_error_free (error
);
3424 if (response
== 0 || response
== GTK_RESPONSE_DELETE_EVENT
) {
3426 } else if (response
== 1) { /* skip all */
3427 job
->skip_all_error
= TRUE
;
3428 } else if (response
== 2) { /* skip */
3431 g_assert_not_reached ();
3437 g_error_free (error
);
3440 if (debuting_files
) { /* Only remove metadata for toplevel items */
3441 nautilus_file_changes_queue_schedule_metadata_remove (dest
);
3443 nautilus_file_changes_queue_file_removed (dest
);
3446 copy_move_directory (copy_job
, src
, dest
, same_fs
,
3448 source_info
, transfer_info
,
3449 debuting_files
, skipped_file
);
3451 g_object_unref (dest
);
3455 else if (IS_IO_ERROR (error
, CANCELLED
)) {
3456 g_error_free (error
);
3461 if (job
->skip_all_error
) {
3462 g_error_free (error
);
3465 primary
= f (_("Error while copying \"%B\"."), src
);
3466 secondary
= f (_("There was an error copying the file into %F."), dest_dir
);
3467 details
= error
->message
;
3469 response
= run_warning (job
,
3473 GTK_STOCK_CANCEL
, SKIP_ALL
, SKIP
,
3476 g_error_free (error
);
3478 if (response
== 0 || response
== GTK_RESPONSE_DELETE_EVENT
) {
3480 } else if (response
== 1) { /* skip all */
3481 job
->skip_all_error
= TRUE
;
3482 } else if (response
== 2) { /* skip */
3485 g_assert_not_reached ();
3489 *skipped_file
= TRUE
; /* Or aborted, but same-same */
3490 g_object_unref (dest
);
3494 copy_files (CopyMoveJob
*job
,
3495 const char *dest_fs_id
,
3496 SourceInfo
*source_info
,
3497 TransferInfo
*transfer_info
)
3505 gboolean skipped_file
;
3506 gboolean unique_names
;
3509 common
= &job
->common
;
3511 report_copy_progress (job
, source_info
, transfer_info
);
3513 unique_names
= (job
->destination
== NULL
);
3515 for (l
= job
->files
;
3516 l
!= NULL
&& !job_aborted (common
);
3520 if (i
< job
->n_icon_positions
) {
3521 point
= &job
->icon_positions
[i
];
3529 same_fs
= has_fs_id (src
, dest_fs_id
);
3532 if (job
->destination
) {
3533 dest
= g_object_ref (job
->destination
);
3535 dest
= g_file_get_parent (src
);
3539 skipped_file
= FALSE
;
3540 copy_move_file (job
, src
, dest
,
3541 same_fs
, unique_names
,
3542 source_info
, transfer_info
,
3543 job
->debuting_files
,
3544 point
, FALSE
, &skipped_file
);
3545 g_object_unref (dest
);
3552 copy_job_done (gpointer user_data
)
3557 if (job
->done_callback
) {
3558 job
->done_callback (job
->debuting_files
, job
->done_callback_data
);
3561 eel_g_object_list_free (job
->files
);
3562 if (job
->destination
) {
3563 g_object_unref (job
->destination
);
3565 g_hash_table_unref (job
->debuting_files
);
3566 g_free (job
->icon_positions
);
3568 finalize_common ((CommonJob
*)job
);
3570 nautilus_file_changes_consume_changes (TRUE
);
3575 copy_job (GIOSchedulerJob
*io_job
,
3576 GCancellable
*cancellable
,
3581 SourceInfo source_info
;
3582 TransferInfo transfer_info
;
3587 common
= &job
->common
;
3588 common
->io_job
= io_job
;
3592 nautilus_progress_info_start (job
->common
.progress
);
3594 scan_sources (job
->files
,
3598 if (job_aborted (common
)) {
3602 if (job
->destination
) {
3603 dest
= g_object_ref (job
->destination
);
3605 /* Duplication, no dest,
3606 * use source for free size, etc
3608 dest
= g_file_get_parent (job
->files
->data
);
3611 verify_destination (&job
->common
,
3614 source_info
.num_bytes
);
3615 g_object_unref (dest
);
3616 if (job_aborted (common
)) {
3620 g_timer_start (job
->common
.time
);
3622 memset (&transfer_info
, 0, sizeof (transfer_info
));
3625 &source_info
, &transfer_info
);
3629 g_free (dest_fs_id
);
3631 g_io_scheduler_job_send_to_mainloop_async (io_job
,
3640 nautilus_file_operations_copy (GList
*files
,
3641 GArray
*relative_item_points
,
3643 GtkWindow
*parent_window
,
3644 NautilusCopyCallback done_callback
,
3645 gpointer done_callback_data
)
3649 job
= op_job_new (CopyMoveJob
, parent_window
);
3650 job
->done_callback
= done_callback
;
3651 job
->done_callback_data
= done_callback_data
;
3652 job
->files
= eel_g_object_list_copy (files
);
3653 job
->destination
= g_object_ref (target_dir
);
3654 if (relative_item_points
!= NULL
&&
3655 relative_item_points
->len
> 0) {
3656 job
->icon_positions
=
3657 g_memdup (relative_item_points
->data
,
3658 sizeof (GdkPoint
) * relative_item_points
->len
);
3659 job
->n_icon_positions
= relative_item_points
->len
;
3661 job
->debuting_files
= g_hash_table_new_full (g_file_hash
, (GEqualFunc
)g_file_equal
, g_object_unref
, NULL
);
3663 g_io_scheduler_push_job (copy_job
,
3665 NULL
, /* destroy notify */
3667 job
->common
.cancellable
);
3671 report_move_progress (CopyMoveJob
*move_job
, int total
, int left
)
3675 job
= (CommonJob
*)move_job
;
3677 nautilus_progress_info_take_status (job
->progress
,
3678 f (_("Preparing to Move to \"%B\""),
3679 move_job
->destination
));
3681 nautilus_progress_info_take_details (job
->progress
,
3682 f (ngettext ("Preparing to move %'d file",
3683 "Preparing to move %'d files",
3686 nautilus_progress_info_pulse_progress (job
->progress
);
3690 move_file_prepare (CopyMoveJob
*move_job
,
3694 GHashTable
*debuting_files
,
3697 GArray
*copy_positions
)
3703 char *primary
, *secondary
, *details
;
3705 GFileCopyFlags flags
;
3706 gboolean would_recurse
;
3710 job
= (CommonJob
*)move_job
;
3712 dest
= get_target_file (src
, dest_dir
, same_fs
);
3716 flags
= G_FILE_COPY_NOFOLLOW_SYMLINKS
| G_FILE_COPY_NO_FALLBACK_FOR_MOVE
;
3718 flags
|= G_FILE_COPY_OVERWRITE
;
3722 if (g_file_move (src
, dest
,
3729 if (debuting_files
) {
3730 g_hash_table_replace (debuting_files
, g_object_ref (dest
), GINT_TO_POINTER (TRUE
));
3733 nautilus_file_changes_queue_file_moved (src
, dest
);
3734 nautilus_file_changes_queue_schedule_metadata_move (src
, dest
);
3736 nautilus_file_changes_queue_schedule_position_set (dest
, *position
, job
->screen_num
);
3738 nautilus_file_changes_queue_schedule_position_remove (dest
);
3746 IS_IO_ERROR (error
, EXISTS
)) {
3749 g_error_free (error
);
3752 if (is_dir (dest
)) {
3755 primary
= f (_("A folder named \"%B\" already exists. Do you want to merge the source folder?"),
3757 secondary
= f (_("The source folder already exists in \"%B\". "
3758 "Merging will ask for confirmation before replacing any files in the folder that conflict with the files being moved."),
3762 primary
= f (_("A folder named \"%B\" already exists. Do you want to replace it?"),
3764 secondary
= f (_("The folder already exists in \"%F\". "
3765 "Replacing it will remove all files in the folder."),
3769 primary
= f (_("A file named \"%B\" already exists. Do you want to replace it?"),
3771 secondary
= f (_("The file already exists in \"%F\". "
3772 "Replacing it will overwrite its content."),
3776 if ((is_merge
&& job
->merge_all
) ||
3777 (!is_merge
&& job
->replace_all
)) {
3785 if (job
->skip_all_conflict
) {
3792 response
= run_warning (job
,
3798 is_merge
?MERGE_ALL
:REPLACE_ALL
,
3800 is_merge
?MERGE
:REPLACE
,
3803 if (response
== 0 || response
== GTK_RESPONSE_DELETE_EVENT
) {
3805 } else if (response
== 1 || response
== 3) { /* skip all / skip */
3806 if (response
== 1) {
3807 job
->skip_all_conflict
= TRUE
;
3809 } else if (response
== 2 || response
== 4) { /* merge/replace all / merge/replace*/
3810 if (response
== 2) {
3812 job
->merge_all
= TRUE
;
3814 job
->replace_all
= TRUE
;
3820 g_assert_not_reached ();
3824 else if (IS_IO_ERROR (error
, WOULD_RECURSE
) ||
3825 IS_IO_ERROR (error
, WOULD_MERGE
) ||
3826 IS_IO_ERROR (error
, NOT_SUPPORTED
)) {
3827 gboolean delete_dest
;
3828 would_recurse
= error
->code
== G_IO_ERROR_WOULD_RECURSE
;
3829 g_error_free (error
);
3831 delete_dest
= FALSE
;
3832 if (overwrite
&& would_recurse
) {
3838 *copy_files
= g_list_prepend (*copy_files
, src
);
3840 g_array_append_val (copy_positions
, *position
);
3844 else if (IS_IO_ERROR (error
, CANCELLED
)) {
3845 g_error_free (error
);
3850 if (job
->skip_all_error
) {
3853 primary
= f (_("Error while moving \"%B\"."), src
);
3854 secondary
= f (_("There was an error moving the file into %F."), dest_dir
);
3855 details
= error
->message
;
3857 response
= run_warning (job
,
3861 GTK_STOCK_CANCEL
, SKIP_ALL
, SKIP
,
3864 g_error_free (error
);
3866 if (response
== 0 || response
== GTK_RESPONSE_DELETE_EVENT
) {
3868 } else if (response
== 1) { /* skip all */
3869 job
->skip_all_error
= TRUE
;
3870 } else if (response
== 2) { /* skip */
3873 g_assert_not_reached ();
3878 g_object_unref (dest
);
3882 move_files_prepare (CopyMoveJob
*job
,
3883 const char *dest_fs_id
,
3885 GArray
*copy_positions
)
3895 common
= &job
->common
;
3897 total
= left
= g_list_length (job
->files
);
3899 report_move_progress (job
, total
, left
);
3902 for (l
= job
->files
;
3903 l
!= NULL
&& !job_aborted (common
);
3907 if (i
< job
->n_icon_positions
) {
3908 point
= &job
->icon_positions
[i
];
3916 same_fs
= has_fs_id (src
, dest_fs_id
);
3919 move_file_prepare (job
, src
, job
->destination
,
3921 job
->debuting_files
,
3925 report_move_progress (job
, total
, --left
);
3932 move_files (CopyMoveJob
*job
,
3934 GArray
*icon_positions
,
3935 const char *dest_fs_id
,
3936 SourceInfo
*source_info
,
3937 TransferInfo
*transfer_info
)
3945 gboolean skipped_file
;
3947 common
= &job
->common
;
3949 report_copy_progress (job
, source_info
, transfer_info
);
3953 l
!= NULL
&& !job_aborted (common
);
3957 if (i
< icon_positions
->len
) {
3958 point
= &g_array_index (icon_positions
, GdkPoint
, i
);
3965 same_fs
= has_fs_id (src
, dest_fs_id
);
3968 /* Set overwrite to true, as the user has
3969 selected overwrite on all toplevel items */
3970 skipped_file
= FALSE
;
3971 copy_move_file (job
, src
, job
->destination
,
3973 source_info
, transfer_info
,
3974 job
->debuting_files
,
3975 point
, TRUE
, &skipped_file
);
3982 move_job_done (gpointer user_data
)
3987 if (job
->done_callback
) {
3988 job
->done_callback (job
->debuting_files
, job
->done_callback_data
);
3991 eel_g_object_list_free (job
->files
);
3992 g_object_unref (job
->destination
);
3993 g_hash_table_unref (job
->debuting_files
);
3994 g_free (job
->icon_positions
);
3996 finalize_common ((CommonJob
*)job
);
3998 nautilus_file_changes_consume_changes (TRUE
);
4003 move_job (GIOSchedulerJob
*io_job
,
4004 GCancellable
*cancellable
,
4010 GArray
*copy_positions
;
4011 SourceInfo source_info
;
4012 TransferInfo transfer_info
;
4016 common
= &job
->common
;
4017 common
->io_job
= io_job
;
4022 copy_positions
= NULL
;
4024 nautilus_progress_info_start (job
->common
.progress
);
4026 verify_destination (&job
->common
,
4030 if (job_aborted (common
)) {
4035 copy_positions
= g_array_new (FALSE
, TRUE
, sizeof (GdkPoint
));
4037 /* This moves all files that we can do without copy + delete */
4038 move_files_prepare (job
, dest_fs_id
, ©_files
, copy_positions
);
4039 if (job_aborted (common
)) {
4043 copy_files
= g_list_reverse (copy_files
);
4045 /* The rest we need to do deep copy + delete behind on,
4048 scan_sources (copy_files
,
4052 if (job_aborted (common
)) {
4056 verify_destination (&job
->common
,
4059 source_info
.num_bytes
);
4060 if (job_aborted (common
)) {
4064 memset (&transfer_info
, 0, sizeof (transfer_info
));
4069 &source_info
, &transfer_info
);
4072 g_list_free (copy_files
);
4073 if (copy_positions
) {
4074 g_array_free (copy_positions
, TRUE
);
4077 g_free (dest_fs_id
);
4079 g_io_scheduler_job_send_to_mainloop (io_job
,
4088 nautilus_file_operations_move (GList
*files
,
4089 GArray
*relative_item_points
,
4091 GtkWindow
*parent_window
,
4092 NautilusCopyCallback done_callback
,
4093 gpointer done_callback_data
)
4097 job
= op_job_new (CopyMoveJob
, parent_window
);
4098 job
->is_move
= TRUE
;
4099 job
->done_callback
= done_callback
;
4100 job
->done_callback_data
= done_callback_data
;
4101 job
->files
= eel_g_object_list_copy (files
);
4102 job
->destination
= g_object_ref (target_dir
);
4103 if (relative_item_points
!= NULL
&&
4104 relative_item_points
->len
> 0) {
4105 job
->icon_positions
=
4106 g_memdup (relative_item_points
->data
,
4107 sizeof (GdkPoint
) * relative_item_points
->len
);
4108 job
->n_icon_positions
= relative_item_points
->len
;
4110 job
->debuting_files
= g_hash_table_new_full (g_file_hash
, (GEqualFunc
)g_file_equal
, g_object_unref
, NULL
);
4112 g_io_scheduler_push_job (move_job
,
4114 NULL
, /* destroy notify */
4116 job
->common
.cancellable
);
4120 report_link_progress (CopyMoveJob
*link_job
, int total
, int left
)
4124 job
= (CommonJob
*)link_job
;
4126 nautilus_progress_info_take_status (job
->progress
,
4127 f (_("Creating links in \"%B\""),
4128 link_job
->destination
));
4130 nautilus_progress_info_take_details (job
->progress
,
4131 f (ngettext ("Making link to %'d file",
4132 "Making links to %'d files",
4135 nautilus_progress_info_set_progress (job
->progress
, left
, total
);
4140 link_file (CopyMoveJob
*job
,
4141 GFile
*src
, GFile
*dest_dir
,
4142 GHashTable
*debuting_files
,
4151 char *primary
, *secondary
, *details
;
4154 common
= (CommonJob
*)job
;
4158 dest
= get_target_file_for_link (src
, dest_dir
, count
);
4163 path
= g_file_get_path (src
);
4166 } else if (g_file_make_symbolic_link (dest
,
4168 common
->cancellable
,
4171 if (debuting_files
) {
4172 g_hash_table_replace (debuting_files
, g_object_ref (dest
), GINT_TO_POINTER (TRUE
));
4175 nautilus_file_changes_queue_file_added (dest
);
4177 nautilus_file_changes_queue_schedule_position_set (dest
, *position
, common
->screen_num
);
4179 nautilus_file_changes_queue_schedule_position_remove (dest
);
4182 g_object_unref (dest
);
4189 if (error
!= NULL
&& IS_IO_ERROR (error
, EXISTS
)) {
4190 g_object_unref (dest
);
4191 dest
= get_target_file_for_link (src
, dest_dir
, count
++);
4192 g_error_free (error
);
4196 else if (error
!= NULL
&& IS_IO_ERROR (error
, CANCELLED
)) {
4197 g_error_free (error
);
4202 if (common
->skip_all_error
) {
4205 primary
= f (_("Error while creating link to %B."), src
);
4207 secondary
= f (_("Symbolic links only supported for local files"));
4209 } else if (IS_IO_ERROR (error
, NOT_SUPPORTED
)) {
4210 secondary
= f (_("The target doesn't support symbolic links."));
4213 secondary
= f (_("There was an error creating the symlink in %F."), dest_dir
);
4214 details
= error
->message
;
4217 response
= run_warning (common
,
4221 GTK_STOCK_CANCEL
, SKIP_ALL
, SKIP
,
4225 g_error_free (error
);
4228 if (response
== 0 || response
== GTK_RESPONSE_DELETE_EVENT
) {
4230 } else if (response
== 1) { /* skip all */
4231 common
->skip_all_error
= TRUE
;
4232 } else if (response
== 2) { /* skip */
4235 g_assert_not_reached ();
4240 g_object_unref (dest
);
4244 link_job_done (gpointer user_data
)
4249 if (job
->done_callback
) {
4250 job
->done_callback (job
->debuting_files
, job
->done_callback_data
);
4253 eel_g_object_list_free (job
->files
);
4254 g_object_unref (job
->destination
);
4255 g_hash_table_unref (job
->debuting_files
);
4256 g_free (job
->icon_positions
);
4258 finalize_common ((CommonJob
*)job
);
4260 nautilus_file_changes_consume_changes (TRUE
);
4265 link_job (GIOSchedulerJob
*io_job
,
4266 GCancellable
*cancellable
,
4272 GArray
*copy_positions
;
4280 common
= &job
->common
;
4281 common
->io_job
= io_job
;
4284 copy_positions
= NULL
;
4286 nautilus_progress_info_start (job
->common
.progress
);
4288 verify_destination (&job
->common
,
4292 if (job_aborted (common
)) {
4296 total
= left
= g_list_length (job
->files
);
4298 report_link_progress (job
, total
, left
);
4301 for (l
= job
->files
;
4302 l
!= NULL
&& !job_aborted (common
);
4306 if (i
< job
->n_icon_positions
) {
4307 point
= &job
->icon_positions
[i
];
4313 link_file (job
, src
, job
->destination
,
4314 job
->debuting_files
,
4316 report_link_progress (job
, total
, --left
);
4323 g_io_scheduler_job_send_to_mainloop (io_job
,
4332 nautilus_file_operations_link (GList
*files
,
4333 GArray
*relative_item_points
,
4335 GtkWindow
*parent_window
,
4336 NautilusCopyCallback done_callback
,
4337 gpointer done_callback_data
)
4341 job
= op_job_new (CopyMoveJob
, parent_window
);
4342 job
->done_callback
= done_callback
;
4343 job
->done_callback_data
= done_callback_data
;
4344 job
->files
= eel_g_object_list_copy (files
);
4345 job
->destination
= g_object_ref (target_dir
);
4346 if (relative_item_points
!= NULL
&&
4347 relative_item_points
->len
> 0) {
4348 job
->icon_positions
=
4349 g_memdup (relative_item_points
->data
,
4350 sizeof (GdkPoint
) * relative_item_points
->len
);
4351 job
->n_icon_positions
= relative_item_points
->len
;
4353 job
->debuting_files
= g_hash_table_new_full (g_file_hash
, (GEqualFunc
)g_file_equal
, g_object_unref
, NULL
);
4355 g_io_scheduler_push_job (link_job
,
4357 NULL
, /* destroy notify */
4359 job
->common
.cancellable
);
4364 nautilus_file_operations_duplicate (GList
*files
,
4365 GArray
*relative_item_points
,
4366 GtkWindow
*parent_window
,
4367 NautilusCopyCallback done_callback
,
4368 gpointer done_callback_data
)
4372 job
= op_job_new (CopyMoveJob
, parent_window
);
4373 job
->done_callback
= done_callback
;
4374 job
->done_callback_data
= done_callback_data
;
4375 job
->files
= eel_g_object_list_copy (files
);
4376 job
->destination
= NULL
;
4377 if (relative_item_points
!= NULL
&&
4378 relative_item_points
->len
> 0) {
4379 job
->icon_positions
=
4380 g_memdup (relative_item_points
->data
,
4381 sizeof (GdkPoint
) * relative_item_points
->len
);
4382 job
->n_icon_positions
= relative_item_points
->len
;
4384 job
->debuting_files
= g_hash_table_new_full (g_file_hash
, (GEqualFunc
)g_file_equal
, g_object_unref
, NULL
);
4386 g_io_scheduler_push_job (copy_job
,
4388 NULL
, /* destroy notify */
4390 job
->common
.cancellable
);
4394 set_permissions_job_done (gpointer user_data
)
4396 SetPermissionsJob
*job
;
4400 g_object_unref (job
->file
);
4402 if (job
->done_callback
) {
4403 job
->done_callback (job
->done_callback_data
);
4406 finalize_common ((CommonJob
*)job
);
4411 set_permissions_file (SetPermissionsJob
*job
,
4416 GFileInfo
*child_info
;
4421 GFileEnumerator
*enumerator
;
4424 common
= (CommonJob
*)job
;
4426 nautilus_progress_info_pulse_progress (common
->progress
);
4431 info
= g_file_query_info (file
,
4432 G_FILE_ATTRIBUTE_STANDARD_TYPE
","
4433 G_FILE_ATTRIBUTE_UNIX_MODE
,
4434 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS
,
4435 common
->cancellable
,
4443 if (g_file_info_get_file_type (info
) == G_FILE_TYPE_DIRECTORY
) {
4444 value
= job
->dir_permissions
;
4445 mask
= job
->dir_mask
;
4447 value
= job
->file_permissions
;
4448 mask
= job
->file_mask
;
4452 if (!job_aborted (common
) &&
4453 g_file_info_has_attribute (info
, G_FILE_ATTRIBUTE_UNIX_MODE
)) {
4454 current
= g_file_info_get_attribute_uint32 (info
, G_FILE_ATTRIBUTE_UNIX_MODE
);
4455 current
= (current
& ~mask
) | value
;
4457 g_file_set_attribute_uint32 (file
, G_FILE_ATTRIBUTE_UNIX_MODE
,
4458 current
, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS
,
4459 common
->cancellable
, NULL
);
4462 if (!job_aborted (common
) &&
4463 g_file_info_get_file_type (info
) == G_FILE_TYPE_DIRECTORY
) {
4464 enumerator
= g_file_enumerate_children (file
,
4465 G_FILE_ATTRIBUTE_STANDARD_NAME
","
4466 G_FILE_ATTRIBUTE_STANDARD_TYPE
","
4467 G_FILE_ATTRIBUTE_UNIX_MODE
,
4468 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS
,
4469 common
->cancellable
,
4472 while (!job_aborted (common
) &&
4473 (child_info
= g_file_enumerator_next_file (enumerator
, common
->cancellable
, NULL
)) != NULL
) {
4474 child
= g_file_get_child (file
,
4475 g_file_info_get_name (child_info
));
4476 set_permissions_file (job
, child
, child_info
);
4477 g_object_unref (child
);
4478 g_object_unref (child_info
);
4480 g_file_enumerator_close (enumerator
, common
->cancellable
, NULL
);
4481 g_object_unref (enumerator
);
4485 g_object_unref (info
);
4491 set_permissions_job (GIOSchedulerJob
*io_job
,
4492 GCancellable
*cancellable
,
4495 SetPermissionsJob
*job
= user_data
;
4498 common
= (CommonJob
*)job
;
4499 common
->io_job
= io_job
;
4501 nautilus_progress_info_set_status (common
->progress
,
4502 _("Setting permissions"));
4504 nautilus_progress_info_start (job
->common
.progress
);
4506 set_permissions_file (job
, job
->file
, NULL
);
4508 g_io_scheduler_job_send_to_mainloop_async (io_job
,
4509 set_permissions_job_done
,
4519 nautilus_file_set_permissions_recursive (const char *directory
,
4520 guint32 file_permissions
,
4522 guint32 dir_permissions
,
4524 NautilusOpCallback callback
,
4525 gpointer callback_data
)
4527 SetPermissionsJob
*job
;
4529 job
= op_job_new (SetPermissionsJob
, NULL
);
4530 job
->file
= g_file_new_for_uri (directory
);
4531 job
->file_permissions
= file_permissions
;
4532 job
->file_mask
= file_mask
;
4533 job
->dir_permissions
= dir_permissions
;
4534 job
->dir_mask
= dir_mask
;
4535 job
->done_callback
= callback
;
4536 job
->done_callback_data
= callback_data
;
4538 g_io_scheduler_push_job (set_permissions_job
,
4546 location_list_from_uri_list (const GList
*uris
)
4553 for (l
= uris
; l
!= NULL
; l
= l
->next
) {
4554 f
= g_file_new_for_uri (l
->data
);
4555 files
= g_list_prepend (files
, f
);
4558 return g_list_reverse (files
);
4562 NautilusCopyCallback real_callback
;
4567 callback_for_move_to_trash (GHashTable
*debuting_uris
,
4568 gboolean user_cancelled
,
4569 MoveTrashCBData
*data
)
4571 if (data
->real_callback
)
4572 data
->real_callback (debuting_uris
, data
->real_data
);
4573 g_slice_free (MoveTrashCBData
, data
);
4577 nautilus_file_operations_copy_move (const GList
*item_uris
,
4578 GArray
*relative_item_points
,
4579 const char *target_dir
,
4580 GdkDragAction copy_action
,
4581 GtkWidget
*parent_view
,
4582 NautilusCopyCallback done_callback
,
4583 gpointer done_callback_data
)
4586 GFile
*dest
, *src_dir
;
4587 GtkWindow
*parent_window
;
4591 dest
= g_file_new_for_uri (target_dir
);
4593 locations
= location_list_from_uri_list (item_uris
);
4595 parent_window
= NULL
;
4597 parent_window
= (GtkWindow
*)gtk_widget_get_ancestor (parent_view
, GTK_TYPE_WINDOW
);
4600 if (copy_action
== GDK_ACTION_COPY
) {
4601 src_dir
= g_file_get_parent (locations
->data
);
4602 if (target_dir
== NULL
||
4604 g_file_equal (src_dir
, dest
))) {
4605 nautilus_file_operations_duplicate (locations
,
4606 relative_item_points
,
4608 done_callback
, done_callback_data
);
4610 nautilus_file_operations_copy (locations
,
4611 relative_item_points
,
4614 done_callback
, done_callback_data
);
4617 g_object_unref (src_dir
);
4620 } else if (copy_action
== GDK_ACTION_MOVE
) {
4621 if (g_file_has_uri_scheme (dest
, "trash")) {
4622 MoveTrashCBData
*cb_data
;
4624 cb_data
= g_slice_new0 (MoveTrashCBData
);
4625 cb_data
->real_callback
= done_callback
;
4626 cb_data
->real_data
= done_callback_data
;
4627 nautilus_file_operations_trash_or_delete (locations
,
4629 (NautilusDeleteCallback
) callback_for_move_to_trash
,
4632 nautilus_file_operations_move (locations
,
4633 relative_item_points
,
4636 done_callback
, done_callback_data
);
4639 nautilus_file_operations_link (locations
,
4640 relative_item_points
,
4643 done_callback
, done_callback_data
);
4646 eel_g_object_list_free (locations
);
4648 g_object_unref (dest
);
4653 create_job_done (gpointer user_data
)
4658 if (job
->done_callback
) {
4659 job
->done_callback (job
->created_file
, job
->done_callback_data
);
4662 g_object_unref (job
->dest_dir
);
4664 g_object_unref (job
->src
);
4666 g_free (job
->src_data
);
4667 g_free (job
->filename
);
4668 if (job
->created_file
) {
4669 g_object_unref (job
->created_file
);
4672 finalize_common ((CommonJob
*)job
);
4674 nautilus_file_changes_consume_changes (TRUE
);
4679 create_job (GIOSchedulerJob
*io_job
,
4680 GCancellable
*cancellable
,
4687 char *filename
, *filename2
;
4690 gboolean filename_is_utf8
;
4691 char *primary
, *secondary
, *details
;
4694 GFileOutputStream
*out
;
4697 common
= &job
->common
;
4698 common
->io_job
= io_job
;
4700 nautilus_progress_info_start (job
->common
.progress
);
4705 verify_destination (common
,
4708 if (job_aborted (common
)) {
4712 filename
= g_strdup (job
->filename
);
4713 filename_is_utf8
= FALSE
;
4715 filename_is_utf8
= g_utf8_validate (filename
, -1, NULL
);
4717 if (filename
== NULL
) {
4718 if (job
->make_dir
) {
4719 /* localizers: the initial name of a new folder */
4720 filename
= g_strdup (_("untitled folder"));
4721 filename_is_utf8
= TRUE
; /* Pass in utf8 */
4723 if (job
->src
!= NULL
) {
4724 filename
= g_file_get_basename (job
->src
);
4726 if (filename
== NULL
) {
4727 /* localizers: the initial name of a new empty file */
4728 filename
= g_strdup (_("new file"));
4729 filename_is_utf8
= TRUE
; /* Pass in utf8 */
4734 if (filename_is_utf8
) {
4735 dest
= g_file_get_child_for_display_name (job
->dest_dir
, filename
, NULL
);
4738 dest
= g_file_get_child (job
->dest_dir
, filename
);
4745 if (job
->make_dir
) {
4746 res
= g_file_make_directory (dest
,
4747 common
->cancellable
,
4751 res
= g_file_copy (job
->src
,
4754 common
->cancellable
,
4759 if (job
->src_data
) {
4760 data
= job
->src_data
;
4763 out
= g_file_create (dest
,
4765 common
->cancellable
,
4768 res
= g_output_stream_write_all (G_OUTPUT_STREAM (out
),
4769 data
, strlen (data
),
4771 common
->cancellable
,
4774 res
= g_output_stream_close (G_OUTPUT_STREAM (out
),
4775 common
->cancellable
,
4779 /* This will close if the write failed and we didn't close */
4780 g_object_unref (out
);
4788 job
->created_file
= g_object_ref (dest
);
4789 nautilus_file_changes_queue_file_added (dest
);
4790 if (job
->has_position
) {
4791 nautilus_file_changes_queue_schedule_position_set (dest
, job
->position
, common
->screen_num
);
4793 nautilus_file_changes_queue_schedule_position_remove (dest
);
4796 if (error
!= NULL
&& IS_IO_ERROR (error
, EXISTS
)) {
4797 g_object_unref (dest
);
4799 filename2
= g_strdup_printf ("%s %d", filename
, ++count
);
4800 if (filename_is_utf8
) {
4801 dest
= g_file_get_child_for_display_name (job
->dest_dir
, filename2
, NULL
);
4804 dest
= g_file_get_child (job
->dest_dir
, filename2
);
4807 g_error_free (error
);
4811 else if (error
!= NULL
&& IS_IO_ERROR (error
, CANCELLED
)) {
4812 g_error_free (error
);
4817 if (job
->make_dir
) {
4818 primary
= f (_("Error while creating directory %B."), dest
);
4820 primary
= f (_("Error while creating file %B."), dest
);
4822 secondary
= f (_("There was an error creating the directory in %F."), job
->dest_dir
);
4823 details
= error
->message
;
4825 response
= run_warning (common
,
4829 GTK_STOCK_CANCEL
, SKIP
,
4832 g_error_free (error
);
4834 if (response
== 0 || response
== GTK_RESPONSE_DELETE_EVENT
) {
4836 } else if (response
== 1) { /* skip */
4839 g_assert_not_reached ();
4846 g_object_unref (dest
);
4849 g_io_scheduler_job_send_to_mainloop_async (io_job
,
4858 nautilus_file_operations_new_folder (GtkWidget
*parent_view
,
4859 GdkPoint
*target_point
,
4860 const char *parent_dir
,
4861 NautilusCreateCallback done_callback
,
4862 gpointer done_callback_data
)
4865 GtkWindow
*parent_window
;
4867 parent_window
= NULL
;
4869 parent_window
= (GtkWindow
*)gtk_widget_get_ancestor (parent_view
, GTK_TYPE_WINDOW
);
4872 job
= op_job_new (CreateJob
, parent_window
);
4873 job
->done_callback
= done_callback
;
4874 job
->done_callback_data
= done_callback_data
;
4875 job
->dest_dir
= g_file_new_for_uri (parent_dir
);
4876 job
->make_dir
= TRUE
;
4877 if (target_point
!= NULL
) {
4878 job
->position
= *target_point
;
4879 job
->has_position
= TRUE
;
4882 g_io_scheduler_push_job (create_job
,
4884 NULL
, /* destroy notify */
4886 job
->common
.cancellable
);
4890 nautilus_file_operations_new_file_from_template (GtkWidget
*parent_view
,
4891 GdkPoint
*target_point
,
4892 const char *parent_dir
,
4893 const char *target_filename
,
4894 const char *template_uri
,
4895 NautilusCreateCallback done_callback
,
4896 gpointer done_callback_data
)
4899 GtkWindow
*parent_window
;
4901 parent_window
= NULL
;
4903 parent_window
= (GtkWindow
*)gtk_widget_get_ancestor (parent_view
, GTK_TYPE_WINDOW
);
4906 job
= op_job_new (CreateJob
, parent_window
);
4907 job
->done_callback
= done_callback
;
4908 job
->done_callback_data
= done_callback_data
;
4909 job
->dest_dir
= g_file_new_for_uri (parent_dir
);
4910 if (target_point
!= NULL
) {
4911 job
->position
= *target_point
;
4912 job
->has_position
= TRUE
;
4914 job
->filename
= g_strdup (target_filename
);
4917 job
->src
= g_file_new_for_uri (template_uri
);
4920 g_io_scheduler_push_job (create_job
,
4922 NULL
, /* destroy notify */
4924 job
->common
.cancellable
);
4928 nautilus_file_operations_new_file (GtkWidget
*parent_view
,
4929 GdkPoint
*target_point
,
4930 const char *parent_dir
,
4931 const char *target_filename
,
4932 const char *initial_contents
,
4933 NautilusCreateCallback done_callback
,
4934 gpointer done_callback_data
)
4937 GtkWindow
*parent_window
;
4939 parent_window
= NULL
;
4941 parent_window
= (GtkWindow
*)gtk_widget_get_ancestor (parent_view
, GTK_TYPE_WINDOW
);
4944 job
= op_job_new (CreateJob
, parent_window
);
4945 job
->done_callback
= done_callback
;
4946 job
->done_callback_data
= done_callback_data
;
4947 job
->dest_dir
= g_file_new_for_uri (parent_dir
);
4948 if (target_point
!= NULL
) {
4949 job
->position
= *target_point
;
4950 job
->has_position
= TRUE
;
4952 job
->src_data
= g_strdup (initial_contents
);
4953 job
->filename
= g_strdup (target_filename
);
4955 g_io_scheduler_push_job (create_job
,
4957 NULL
, /* destroy notify */
4959 job
->common
.cancellable
);
4965 delete_trash_file (CommonJob
*job
,
4968 gboolean del_children
)
4972 GFileEnumerator
*enumerator
;
4974 if (job_aborted (job
)) {
4979 enumerator
= g_file_enumerate_children (file
,
4980 G_FILE_ATTRIBUTE_STANDARD_NAME
","
4981 G_FILE_ATTRIBUTE_STANDARD_TYPE
,
4982 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS
,
4986 while (!job_aborted (job
) &&
4987 (info
= g_file_enumerator_next_file (enumerator
, job
->cancellable
, NULL
)) != NULL
) {
4988 child
= g_file_get_child (file
,
4989 g_file_info_get_name (info
));
4990 delete_trash_file (job
, child
, TRUE
,
4991 g_file_info_get_file_type (info
) == G_FILE_TYPE_DIRECTORY
);
4992 g_object_unref (child
);
4993 g_object_unref (info
);
4995 g_file_enumerator_close (enumerator
, job
->cancellable
, NULL
);
4996 g_object_unref (enumerator
);
5000 if (!job_aborted (job
) && del_file
) {
5001 g_file_delete (file
, job
->cancellable
, NULL
);
5006 empty_trash_job_done (gpointer user_data
)
5012 eel_g_object_list_free (job
->trash_dirs
);
5014 if (job
->done_callback
) {
5015 job
->done_callback (job
->done_callback_data
);
5018 finalize_common ((CommonJob
*)job
);
5023 empty_trash_job (GIOSchedulerJob
*io_job
,
5024 GCancellable
*cancellable
,
5027 EmptyTrashJob
*job
= user_data
;
5031 common
= (CommonJob
*)job
;
5032 common
->io_job
= io_job
;
5034 nautilus_progress_info_start (job
->common
.progress
);
5036 if (confirm_empty_trash (common
)) {
5037 for (l
= job
->trash_dirs
;
5038 l
!= NULL
&& !job_aborted (common
);
5040 delete_trash_file (common
, l
->data
, FALSE
, TRUE
);
5044 g_io_scheduler_job_send_to_mainloop_async (io_job
,
5045 empty_trash_job_done
,
5053 nautilus_file_operations_empty_trash (GtkWidget
*parent_view
)
5056 GtkWindow
*parent_window
;
5058 parent_window
= NULL
;
5060 parent_window
= (GtkWindow
*)gtk_widget_get_ancestor (parent_view
, GTK_TYPE_WINDOW
);
5063 job
= op_job_new (EmptyTrashJob
, parent_window
);
5064 job
->trash_dirs
= g_list_prepend (job
->trash_dirs
,
5065 g_file_new_for_uri ("trash:"));
5067 g_io_scheduler_push_job (empty_trash_job
,
5075 #if !defined (NAUTILUS_OMIT_SELF_CHECK)
5078 nautilus_self_check_file_operations (void)
5080 setlocale (LC_MESSAGES
, "C");
5083 /* test the next duplicate name generator */
5084 EEL_CHECK_STRING_RESULT (get_duplicate_name (" (copy)", 1), " (another copy)");
5085 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo", 1), "foo (copy)");
5086 EEL_CHECK_STRING_RESULT (get_duplicate_name (".bashrc", 1), ".bashrc (copy)");
5087 EEL_CHECK_STRING_RESULT (get_duplicate_name (".foo.txt", 1), ".foo (copy).txt");
5088 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo foo", 1), "foo foo (copy)");
5089 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo.txt", 1), "foo (copy).txt");
5090 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo foo.txt", 1), "foo foo (copy).txt");
5091 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo foo.txt txt", 1), "foo foo (copy).txt txt");
5092 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo...txt", 1), "foo (copy)...txt");
5093 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo...", 1), "foo (copy)...");
5094 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo. (copy)", 1), "foo. (another copy)");
5095 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (copy)", 1), "foo (another copy)");
5096 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (copy).txt", 1), "foo (another copy).txt");
5097 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (another copy)", 1), "foo (3rd copy)");
5098 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (another copy).txt", 1), "foo (3rd copy).txt");
5099 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo foo (another copy).txt", 1), "foo foo (3rd copy).txt");
5100 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (13th copy)", 1), "foo (14th copy)");
5101 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (13th copy).txt", 1), "foo (14th copy).txt");
5102 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (21st copy)", 1), "foo (22nd copy)");
5103 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (21st copy).txt", 1), "foo (22nd copy).txt");
5104 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (22nd copy)", 1), "foo (23rd copy)");
5105 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (22nd copy).txt", 1), "foo (23rd copy).txt");
5106 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (23rd copy)", 1), "foo (24th copy)");
5107 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (23rd copy).txt", 1), "foo (24th copy).txt");
5108 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (24th copy)", 1), "foo (25th copy)");
5109 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (24th copy).txt", 1), "foo (25th copy).txt");
5110 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo foo (24th copy)", 1), "foo foo (25th copy)");
5111 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo foo (24th copy).txt", 1), "foo foo (25th copy).txt");
5112 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo foo (100000000000000th copy).txt", 1), "foo foo (copy).txt");
5113 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (10th copy)", 1), "foo (11th copy)");
5114 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (10th copy).txt", 1), "foo (11th copy).txt");
5115 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (11th copy)", 1), "foo (12th copy)");
5116 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (11th copy).txt", 1), "foo (12th copy).txt");
5117 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (12th copy)", 1), "foo (13th copy)");
5118 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (12th copy).txt", 1), "foo (13th copy).txt");
5119 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (110th copy)", 1), "foo (111th copy)");
5120 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (110th copy).txt", 1), "foo (111th copy).txt");
5121 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (122nd copy)", 1), "foo (123rd copy)");
5122 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (122nd copy).txt", 1), "foo (123rd copy).txt");
5123 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (123rd copy)", 1), "foo (124th copy)");
5124 EEL_CHECK_STRING_RESULT (get_duplicate_name ("foo (123rd copy).txt", 1), "foo (124th copy).txt");
5126 setlocale (LC_MESSAGES
, "");