summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexandru Pandelea <alexandru.pandelea@gmail.com>2016-07-26 13:10:51 +0300
committerAlexandru Pandelea <alexandru.pandelea@gmail.com>2016-07-26 13:10:51 +0300
commit840481fa160c9d496b5f9057d9f73b12387c2bf2 (patch)
treeb768856076a4c07ddc6cb71a1ac772bd9b6618c6
parent50f0b77e805d237ac7303dfbe1c1039edc43717d (diff)
downloadnautilus-840481fa160c9d496b5f9057d9f73b12387c2bf2.tar.gz
Add Batch Rename operation
-rw-r--r--src/nautilus-batch-rename-utilities.c43
-rw-r--r--src/nautilus-batch-rename-utilities.h3
-rw-r--r--src/nautilus-batch-rename.c221
-rw-r--r--src/nautilus-directory-private.h2
-rw-r--r--src/nautilus-directory.c23
-rw-r--r--src/nautilus-directory.h2
-rw-r--r--src/nautilus-file-private.h3
-rw-r--r--src/nautilus-file-undo-operations.c183
-rw-r--r--src/nautilus-file-undo-operations.h29
-rw-r--r--src/nautilus-file.c243
-rw-r--r--src/nautilus-file.h4
-rw-r--r--src/nautilus-files-view.c21
12 files changed, 702 insertions, 75 deletions
diff --git a/src/nautilus-batch-rename-utilities.c b/src/nautilus-batch-rename-utilities.c
index c8efc0863..da9bc8fe7 100644
--- a/src/nautilus-batch-rename-utilities.c
+++ b/src/nautilus-batch-rename-utilities.c
@@ -209,12 +209,14 @@ list_has_duplicates (NautilusBatchRename *dialog,
GList *new_names,
GList *selection,
GList *parents_list,
- gboolean same_parent)
+ gboolean same_parent,
+ GCancellable *cancellable)
{
GList *directory_files, *l1, *l2, *l3, *result;
NautilusFile *file1, *file2;
- GString *file_name1, *file_name2, *new_name;
+ GString *file_name1, *file_name2, *file_name3, *new_name;
NautilusDirectory *parent;
+ gboolean is_renameable_desktop_file, have_conflict;
result = NULL;
@@ -239,16 +241,33 @@ list_has_duplicates (NautilusBatchRename *dialog,
l3 = selection;
for (l1 = new_names; l1 != NULL; l1 = l1->next) {
+ if (g_cancellable_is_cancelled (cancellable)) {
+ return NULL;
+ g_list_free_full (result, g_free);
+ break;
+ }
+
file1 = NAUTILUS_FILE (l3->data);
new_name = l1->data;
+ is_renameable_desktop_file = nautilus_file_is_mime_type (file1, "application/x-desktop");
+
+ have_conflict = FALSE;
+
+ if (!is_renameable_desktop_file && strstr (new_name->str, "/") != NULL) {
+ result = g_list_prepend (result, strdup (new_name->str));
+
+ continue;
+ }
+
g_string_erase (file_name1, 0, -1);
g_string_append (file_name1, nautilus_file_get_name (file1));
/* check for duplicate only if the name has changed */
if (!g_string_equal (new_name, file_name1)) {
+ /* check with already existing files */
for (l2 = directory_files; l2 != NULL; l2 = l2->next) {
- file2 = NAUTILUS_FILE (l2->data);
+ file2 = NAUTILUS_FILE (l2->data);
g_string_erase (file_name2, 0, -1);
g_string_append (file_name2, nautilus_file_get_name (file2));
@@ -256,14 +275,32 @@ list_has_duplicates (NautilusBatchRename *dialog,
if (g_string_equal (new_name, file_name2) &&
!file_name_changed (selection, new_names, new_name, NULL)) {
result = g_list_prepend (result, strdup (new_name->str));
+ have_conflict = TRUE;
+
break;
}
}
+
+ /* check with files that will result from the batch rename, unless
+ * this file already has a conflict */
+ if (!have_conflict)
+ for (l2 = new_names; l2 != NULL; l2 = l2->next) {
+ file_name3 = l2->data;
+ if (l1 != l2 && g_string_equal (new_name, file_name3)) {
+ result = g_list_prepend (result, strdup (new_name->str));
+
+ break;
+ }
+ }
}
l3 = l3->next;
}
+ if (g_cancellable_is_cancelled (cancellable)) {
+ return NULL;
+ }
+
g_string_free (file_name1, TRUE);
g_string_free (file_name2, TRUE);
diff --git a/src/nautilus-batch-rename-utilities.h b/src/nautilus-batch-rename-utilities.h
index 087098ff6..7c51fe65e 100644
--- a/src/nautilus-batch-rename-utilities.h
+++ b/src/nautilus-batch-rename-utilities.h
@@ -15,7 +15,8 @@ GList* list_has_duplicates (NautilusBatchRename *di
GList *names,
GList *selection,
GList *parents_list,
- gboolean same_parent);
+ gboolean same_parent,
+ GCancellable *cancellable);
GList* nautilus_batch_rename_sort (GList *selection,
SortingMode mode,
diff --git a/src/nautilus-batch-rename.c b/src/nautilus-batch-rename.c
index 8bfaeb2fc..0b3e8795d 100644
--- a/src/nautilus-batch-rename.c
+++ b/src/nautilus-batch-rename.c
@@ -55,8 +55,11 @@ struct _NautilusBatchRename
GtkWidget *conflict_up;
GList *listbox_rows;
+ GList *listbox_labels_new;
+ GList *listbox_labels_old;
GList *selection;
+ GList *new_names;
NautilusBatchRenameMode mode;
NautilusDirectory *model;
@@ -76,6 +79,9 @@ struct _NautilusBatchRename
gint checked_parents;
GList *duplicates;
GList *distinct_parents;
+ GTask *task;
+ GCancellable *cancellable;
+ gboolean checking_conflicts;
GtkSizeGroup *size_group1;
GtkSizeGroup *size_group2;
@@ -186,20 +192,7 @@ rename_files_on_names_accepted (NautilusBatchRename *dialog,
GList *new_names)
{
/* do the actual rename here */
- GList *l1;
- GList *l2;
- GList *selection;
- NautilusFile *file;
- GString *new_name;
-
- selection = dialog->selection;
-
- for (l1 = selection, l2 = new_names; l1 != NULL && l2 != NULL; l1 = l1->next, l2 = l2->next) {
- file = NAUTILUS_FILE (l1->data);
- new_name = l2->data;
-
- nautilus_rename_file (file, new_name->str, NULL, NULL);
- }
+ nautilus_file_batch_rename (dialog->selection, new_names, NULL, NULL);
gtk_window_close (GTK_WINDOW (dialog));
}
@@ -250,6 +243,7 @@ create_row_for_label (NautilusBatchRename *dialog,
label_new = g_object_new (GTK_TYPE_LABEL,
"label",new_text,
"hexpand", TRUE,
+ "selectable", TRUE,
"xalign", 0.0,
"margin-start", 6,
NULL);
@@ -257,6 +251,7 @@ create_row_for_label (NautilusBatchRename *dialog,
label_old = g_object_new (GTK_TYPE_LABEL,
"label",old_text,
"hexpand", TRUE,
+ "selectable", TRUE,
"xalign", 0.0,
"margin-start", 6,
NULL);
@@ -264,14 +259,17 @@ create_row_for_label (NautilusBatchRename *dialog,
gtk_label_set_ellipsize (GTK_LABEL (label_new), PANGO_ELLIPSIZE_END);
gtk_label_set_ellipsize (GTK_LABEL (label_old), PANGO_ELLIPSIZE_END);
- gtk_size_group_add_widget (dialog->size_group1, label_new);
- gtk_size_group_add_widget (dialog->size_group2, label_old);
+ //gtk_size_group_add_widget (dialog->size_group1, label_new);
+ //gtk_size_group_add_widget (dialog->size_group2, label_old);
gtk_box_pack_end (GTK_BOX (box), label_new, TRUE, FALSE, 0);
gtk_box_pack_end (GTK_BOX (box), icon, TRUE, FALSE, 0);
gtk_box_pack_end (GTK_BOX (box), label_old, TRUE, FALSE, 0);
gtk_list_box_row_set_selectable (GTK_LIST_BOX_ROW (row), TRUE);
+ dialog->listbox_labels_new = g_list_append (dialog->listbox_labels_new, label_new);
+ dialog->listbox_labels_old = g_list_append (dialog->listbox_labels_old, label_old);
+
gtk_container_add (GTK_CONTAINER (row), box);
gtk_widget_show_all (row);
@@ -279,31 +277,21 @@ create_row_for_label (NautilusBatchRename *dialog,
}
static void
-fill_display_listbox (NautilusBatchRename *dialog,
- GList *new_names)
+fill_display_listbox (NautilusBatchRename *dialog)
{
GtkWidget *row;
GList *l1;
GList *l2;
- GList *l;
NautilusFile *file;
GString *new_name;
- /* clear rows from listbox (if any) */
- if (dialog->listbox_rows != NULL)
- for (l = dialog->listbox_rows; l != NULL; l = l->next) {
- gtk_container_remove (GTK_CONTAINER (dialog->conflict_listbox),
- GTK_WIDGET (l->data));
- }
-
- g_list_free (dialog->listbox_rows);
dialog->listbox_rows = NULL;
dialog->size_group1 = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
dialog->size_group2 = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
/* add rows to a list so that they can be removed when the renaming
* result changes */
- for (l1 = new_names, l2 = dialog->selection; l1 != NULL || l2 != NULL; l1 = l1->next, l2 = l2->next) {
+ for (l1 = dialog->new_names, l2 = dialog->selection; l1 != NULL || l2 != NULL; l1 = l1->next, l2 = l2->next) {
file = NAUTILUS_FILE (l2->data);
new_name = l1->data;
@@ -333,28 +321,28 @@ file_has_conflict (NautilusBatchRename *dialog,
static void
select_nth_conflict (NautilusBatchRename *dialog)
{
- GList *l, *new_names, *l2;
+ GList *l, *l2;
GString *file_name, *display_text, *new_name;
gint n, nth_conflict;
- gint selected_row;
NautilusFile *file;
-
- selected_row = 0;
+ GtkAdjustment *adjustment;
+ GtkAllocation allocation;
nth_conflict = dialog->selected_conflict;
n = nth_conflict;
l = g_list_nth (dialog->duplicates, n);
+ /* the conflict that has to be selected */
file_name = g_string_new (l->data);
display_text = g_string_new ("");
n = 0;
- new_names = batch_rename_get_new_names (dialog);
l2 = dialog->selection;
- for (l = new_names; l != NULL; l = l->next) {
+ for (l = dialog->new_names; l != NULL; l = l->next) {
file = NAUTILUS_FILE (l2->data);
+
new_name = l->data;
/* g_strcmp0 is used for not selecting a file that doesn't change
@@ -364,7 +352,9 @@ select_nth_conflict (NautilusBatchRename *dialog)
nth_conflict == 0)
break;
- if (file_has_conflict (dialog, new_name))
+ /* a file can only have a conflict if it's name has changed */
+ if (g_strcmp0 (new_name->str, nautilus_file_get_name (file)) &&
+ file_has_conflict (dialog, new_name))
nth_conflict--;
n++;
@@ -376,13 +366,22 @@ select_nth_conflict (NautilusBatchRename *dialog)
gtk_list_box_select_row (GTK_LIST_BOX (dialog->conflict_listbox),
l->data);
- g_string_append_printf (display_text,
- "\"%s\" would conflict with an existing file.",
- file_name->str);
+ /* scroll to the selected row */
+ adjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (dialog->scrolled_window));
+ gtk_widget_get_allocation (GTK_WIDGET (l->data), &allocation);
+ gtk_adjustment_set_value (adjustment, (allocation.height + 1)*n);
+
+ if (strstr (file_name->str, "/") == NULL)
+ g_string_append_printf (display_text,
+ "\"%s\" would conflict with an existing file.",
+ file_name->str);
+ else
+ g_string_append_printf (display_text,
+ "\"%s\" has unallowed character '/'.",
+ file_name->str);
+
gtk_label_set_label (GTK_LABEL (dialog->conflict_label),
display_text->str);
-
- g_list_free_full (new_names, string_free);
}
static void
@@ -414,11 +413,31 @@ move_next_conflict_up (NautilusBatchRename *dialog)
}
static void
-update_conflicts (NautilusBatchRename *dialog,
- GList *new_names)
+update_listbox (NautilusBatchRename *dialog)
{
+ GList *l1, *l2;
+ NautilusFile *file;
+ gchar *old_name;
+ GtkLabel *label;
+ GString *new_name;
+
/* Update listbox that shows the result of the renaming for each file */
- fill_display_listbox (dialog, new_names);
+ for (l1 = dialog->new_names, l2 = dialog->listbox_labels_new; l1 != NULL && l2 != NULL; l1 = l1->next, l2 = l2->next) {
+ label = GTK_LABEL (l2->data);
+ new_name = l1->data;
+
+ gtk_label_set_label (label, new_name->str);
+ }
+
+ for (l1 = dialog->selection, l2 = dialog->listbox_labels_old; l1 != NULL && l2 != NULL; l1 = l1->next, l2 = l2->next) {
+ label = GTK_LABEL (l2->data);
+ file = NAUTILUS_FILE (l1->data);
+
+ old_name = nautilus_file_get_name (file);
+
+ gtk_label_set_label (label, old_name);
+ }
+
/* check if there are name conflicts and display them if they exist */
if (dialog->duplicates != NULL) {
@@ -456,16 +475,13 @@ check_conflict_for_file (NautilusBatchRename *dialog,
NautilusFile *file1, *file2;
GString *new_name, *file_name1, *file_name2;
GList *l1, *l2, *l3;
- GList *new_names;
-
- new_names = batch_rename_get_new_names (dialog);
file_name1 = g_string_new ("");
file_name2 = g_string_new ("");
current_directory = nautilus_directory_get_uri (directory);
- for (l1 = dialog->selection, l2 = new_names; l1 != NULL && l2 != NULL; l1 = l1->next, l2 = l2->next) {
+ for (l1 = dialog->selection, l2 = dialog->new_names; l1 != NULL && l2 != NULL; l1 = l1->next, l2 = l2->next) {
file1 = NAUTILUS_FILE (l1->data);
g_string_erase (file_name1, 0, -1);
@@ -490,7 +506,7 @@ check_conflict_for_file (NautilusBatchRename *dialog,
g_free (name);
if (g_string_equal (new_name, file_name2) &&
- !file_name_changed (dialog->selection, new_names, new_name, parent_uri)) {
+ !file_name_changed (dialog->selection, dialog->new_names, new_name, parent_uri)) {
dialog->duplicates = g_list_prepend (dialog->duplicates,
strdup (new_name->str));
break;
@@ -502,8 +518,6 @@ check_conflict_for_file (NautilusBatchRename *dialog,
* the listbox with the conflicts if it is. */
if (dialog->checked_parents == g_list_length (dialog->distinct_parents) - 1) {
dialog->duplicates = g_list_reverse (dialog->duplicates);
-
- update_conflicts (dialog, new_names);
}
dialog->checked_parents++;
@@ -512,9 +526,66 @@ check_conflict_for_file (NautilusBatchRename *dialog,
}
static void
+list_has_duplicates_callback (GObject *object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ NautilusBatchRename *dialog;
+
+ dialog = NAUTILUS_BATCH_RENAME (object);
+
+ dialog->checking_conflicts = FALSE;
+
+ update_listbox (dialog);
+}
+
+static void
+list_has_duplicates_async_thread (GTask *task,
+ gpointer object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ NautilusBatchRename *dialog;
+
+ dialog = NAUTILUS_BATCH_RENAME (object);
+
+ dialog->duplicates = list_has_duplicates (dialog,
+ dialog->model,
+ dialog->new_names,
+ dialog->selection,
+ dialog->distinct_parents,
+ dialog->same_parent,
+ cancellable);
+
+ g_task_return_pointer (task, object, NULL);
+
+}
+
+static void
+list_has_duplicates_async (NautilusBatchRename *dialog,
+ int io_priority,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ if (dialog->checking_conflicts == TRUE)
+ g_cancellable_cancel (dialog->cancellable);
+
+ dialog->cancellable = g_cancellable_new ();
+
+ dialog->checking_conflicts = TRUE;
+ dialog->task = g_task_new (dialog, dialog->cancellable, callback, user_data);
+
+ g_task_set_priority (dialog->task, io_priority);
+ g_task_run_in_thread (dialog->task, list_has_duplicates_async_thread);
+
+ g_object_unref (dialog->task);
+}
+
+static void
file_names_widget_entry_on_changed (NautilusBatchRename *dialog)
{
- GList *new_names;
+ if (dialog->cancellable != NULL)
+ g_cancellable_cancel (dialog->cancellable);
if(dialog->selection == NULL)
return;
@@ -524,33 +595,42 @@ file_names_widget_entry_on_changed (NautilusBatchRename *dialog)
dialog->duplicates = NULL;
}
- new_names = batch_rename_get_new_names(dialog);
- dialog->checked_parents = 0;
- dialog->duplicates = list_has_duplicates (dialog,
- dialog->model,
- new_names,
- dialog->selection,
- dialog->distinct_parents,
- dialog->same_parent);
+ if (dialog->new_names != NULL)
+ g_list_free_full (dialog->new_names, string_free);
- update_conflicts (dialog, new_names);
+ dialog->new_names = batch_rename_get_new_names (dialog);
+ dialog->checked_parents = 0;
- g_list_free_full (new_names, string_free);
+ list_has_duplicates_async (dialog,
+ G_PRIORITY_DEFAULT,
+ list_has_duplicates_callback,
+ NULL);
}
static void
file_names_widget_on_activate (NautilusBatchRename *dialog)
{
- GList *new_names;
+ GList *l;
+
+ /* wait for checking conflicts to finish, to be sure that
+ * the rename can actually take place */
+ while (dialog->checking_conflicts){
+
+ }
if (!gtk_widget_is_sensitive (dialog->rename_button))
return;
- new_names = batch_rename_get_new_names(dialog);
+ /* clear rows from listbox */
+ if (dialog->listbox_rows != NULL)
+ for (l = dialog->listbox_rows; l != NULL; l = l->next)
+ gtk_container_remove (GTK_CONTAINER (dialog->conflict_listbox),
+ GTK_WIDGET (l->data));
+
+ g_list_free (dialog->listbox_rows);
/* if names are all right call rename_files_on_names_accepted*/
- rename_files_on_names_accepted (dialog, new_names);
- g_list_free_full (new_names, string_free);
+ rename_files_on_names_accepted (dialog, dialog->new_names);
}
static void
@@ -673,6 +753,11 @@ nautilus_batch_rename_finalize (GObject *object)
g_hash_table_destroy (dialog->create_date);
g_list_free_full (dialog->distinct_parents, g_free);
+ g_list_free_full (dialog->new_names, string_free);
+ g_list_free_full (dialog->duplicates, g_free);
+
+ g_object_unref (dialog->size_group1);
+ g_object_unref (dialog->size_group2);
G_OBJECT_CLASS (nautilus_batch_rename_parent_class)->finalize (object);
}
@@ -759,6 +844,8 @@ nautilus_batch_rename_new (GList *selection, NautilusDirectory *model, NautilusW
/* update display text */
file_names_widget_entry_on_changed (dialog);
+ fill_display_listbox (dialog);
+
g_free (dialog_title);
return GTK_WIDGET (dialog);
@@ -783,4 +870,8 @@ nautilus_batch_rename_init (NautilusBatchRename *self)
gtk_label_set_ellipsize (GTK_LABEL (self->conflict_label), PANGO_ELLIPSIZE_END);
self->duplicates = NULL;
+ self->new_names = NULL;
+ self->listbox_rows = NULL;
+
+ self->checking_conflicts = FALSE;
} \ No newline at end of file
diff --git a/src/nautilus-directory-private.h b/src/nautilus-directory-private.h
index dae1b96ce..0128fa02a 100644
--- a/src/nautilus-directory-private.h
+++ b/src/nautilus-directory-private.h
@@ -185,6 +185,8 @@ void nautilus_directory_emit_files_changed (NautilusD
GList *changed_files);
void nautilus_directory_emit_change_signals (NautilusDirectory *directory,
GList *changed_files);
+void nautilus_directory_emit_change_selection (NautilusDirectory *directory,
+ GList *selection);
void emit_change_signals_for_all_files (NautilusDirectory *directory);
void emit_change_signals_for_all_files_in_all_directories (void);
void nautilus_directory_emit_done_loading (NautilusDirectory *directory);
diff --git a/src/nautilus-directory.c b/src/nautilus-directory.c
index 3595406fe..13ecb4a22 100644
--- a/src/nautilus-directory.c
+++ b/src/nautilus-directory.c
@@ -44,6 +44,7 @@ enum {
FILES_CHANGED,
DONE_LOADING,
LOAD_ERROR,
+ CHANGE_SELECTION,
LAST_SIGNAL
};
@@ -159,6 +160,15 @@ nautilus_directory_class_init (NautilusDirectoryClass *klass)
G_TYPE_FILE,
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
+ signals[CHANGE_SELECTION] =
+ g_signal_new ("change-selection",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (NautilusDirectoryClass, change_selection),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE, 1, G_TYPE_POINTER);
+
klass->get_file_list = real_get_file_list;
klass->is_editable = real_is_editable;
klass->handles_location = real_handles_location;
@@ -885,6 +895,19 @@ nautilus_directory_emit_files_changed (NautilusDirectory *directory,
}
void
+nautilus_directory_emit_change_selection (NautilusDirectory *directory,
+ GList *selection)
+{
+ nautilus_profile_start (NULL);
+ if (selection != NULL) {
+ g_signal_emit (directory,
+ signals[CHANGE_SELECTION], 0,
+ selection);
+ }
+ nautilus_profile_end (NULL);
+}
+
+void
nautilus_directory_emit_change_signals (NautilusDirectory *directory,
GList *changed_files)
{
diff --git a/src/nautilus-directory.h b/src/nautilus-directory.h
index dc8c73253..ca4fae7c3 100644
--- a/src/nautilus-directory.h
+++ b/src/nautilus-directory.h
@@ -103,6 +103,8 @@ typedef struct
void (* load_error) (NautilusDirectory *directory,
GError *error);
+ void (* change_selection) (NautilusDirectory *directory,
+ GList *selection);
/*** Virtual functions for subclasses to override. ***/
gboolean (* contains_file) (NautilusDirectory *directory,
NautilusFile *file);
diff --git a/src/nautilus-file-private.h b/src/nautilus-file-private.h
index c4986511b..f0ecf7294 100644
--- a/src/nautilus-file-private.h
+++ b/src/nautilus-file-private.h
@@ -214,6 +214,9 @@ struct NautilusFileDetails
typedef struct {
NautilusFile *file;
+ GList *files;
+ gint renamed_files;
+ gint skipped_files;
GCancellable *cancellable;
NautilusFileOperationCallback callback;
gpointer callback_data;
diff --git a/src/nautilus-file-undo-operations.c b/src/nautilus-file-undo-operations.c
index 25d90bb18..0bcf281d7 100644
--- a/src/nautilus-file-undo-operations.c
+++ b/src/nautilus-file-undo-operations.c
@@ -977,6 +977,189 @@ nautilus_file_undo_info_rename_set_data_post (NautilusFileUndoInfoRename *self,
self->priv->new_file = g_object_ref (new_file);
}
+/* batch rename */
+G_DEFINE_TYPE (NautilusFileUndoInfoBatchRename, nautilus_file_undo_info_batch_rename, NAUTILUS_TYPE_FILE_UNDO_INFO);
+
+struct _NautilusFileUndoInfoBatchRenameDetails {
+ GList *old_files;
+ GList *new_files;
+ GList *old_display_names;
+ GList *new_display_names;
+};
+
+static void
+batch_rename_strings_func (NautilusFileUndoInfo *info,
+ gchar **undo_label,
+ gchar **undo_description,
+ gchar **redo_label,
+ gchar **redo_description)
+{
+ NautilusFileUndoInfoBatchRename *self = NAUTILUS_FILE_UNDO_INFO_BATCH_RENAME (info);
+
+ *undo_description = g_strdup_printf (_("Batch rename '%d' files"),
+ g_list_length (self->priv->new_files));
+ *redo_description = g_strdup_printf (_("Batch rename '%d' files"),
+ g_list_length (self->priv->new_files));
+
+ *undo_label = g_strdup (_("_Undo Batch rename"));
+ *redo_label = g_strdup (_("_Redo Batch rename"));
+}
+
+static void
+batch_rename_redo_func (NautilusFileUndoInfo *info,
+ GtkWindow *parent_window)
+{
+ NautilusFileUndoInfoBatchRename *self = NAUTILUS_FILE_UNDO_INFO_BATCH_RENAME (info);
+
+ GList *l, *files;
+ NautilusFile *file;
+ GFile *old_file;
+
+ files = NULL;
+
+ for (l = self->priv->old_files; l != NULL; l = l->next) {
+ old_file = l->data;
+
+ file = nautilus_file_get (old_file);
+ files = g_list_append (files, file);
+ }
+
+ nautilus_file_batch_rename (files, self->priv->new_display_names, file_undo_info_operation_callback, self);
+}
+
+static void
+batch_rename_undo_func (NautilusFileUndoInfo *info,
+ GtkWindow *parent_window)
+{
+ NautilusFileUndoInfoBatchRename *self = NAUTILUS_FILE_UNDO_INFO_BATCH_RENAME (info);
+
+ GList *l, *files;
+ NautilusFile *file;
+ GFile *new_file;
+
+ files = NULL;
+
+ for (l = self->priv->new_files; l != NULL; l = l->next) {
+ new_file = l->data;
+
+ file = nautilus_file_get (new_file);
+ files = g_list_append (files, file);
+ }
+
+ nautilus_file_batch_rename (files, self->priv->old_display_names, file_undo_info_operation_callback, self);
+}
+
+static void
+nautilus_file_undo_info_batch_rename_init (NautilusFileUndoInfoBatchRename *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, nautilus_file_undo_info_batch_rename_get_type (),
+ NautilusFileUndoInfoBatchRenameDetails);
+}
+
+static void
+nautilus_file_undo_info_batch_rename_finalize (GObject *obj)
+{
+ GList *l;
+ GFile *file;
+ GString *string;
+ NautilusFileUndoInfoBatchRename *self = NAUTILUS_FILE_UNDO_INFO_BATCH_RENAME (obj);
+
+ for (l = self->priv->new_files; l != NULL; l = l->next){
+ file = l->data;
+
+ g_clear_object (&file);
+ }
+
+ for (l = self->priv->old_files; l != NULL; l = l->next){
+ file = l->data;
+
+ g_clear_object (&file);
+ }
+
+ for (l = self->priv->new_display_names; l != NULL; l = l->next) {
+ string = l->data;
+
+ g_string_free (string, TRUE);
+ }
+
+ for (l = self->priv->old_display_names; l != NULL; l = l->next) {
+ string = l->data;
+
+ g_string_free (string, TRUE);
+ }
+
+ g_list_free (self->priv->new_files);
+ g_list_free (self->priv->old_files);
+ g_list_free (self->priv->new_display_names);
+ g_list_free (self->priv->old_display_names);
+
+ G_OBJECT_CLASS (nautilus_file_undo_info_batch_rename_parent_class)->finalize (obj);
+}
+
+static void
+nautilus_file_undo_info_batch_rename_class_init (NautilusFileUndoInfoBatchRenameClass *klass)
+{
+ GObjectClass *oclass = G_OBJECT_CLASS (klass);
+ NautilusFileUndoInfoClass *iclass = NAUTILUS_FILE_UNDO_INFO_CLASS (klass);
+
+ oclass->finalize = nautilus_file_undo_info_batch_rename_finalize;
+
+ iclass->undo_func = batch_rename_undo_func;
+ iclass->redo_func = batch_rename_redo_func;
+ iclass->strings_func = batch_rename_strings_func;
+
+ g_type_class_add_private (klass, sizeof (NautilusFileUndoInfoBatchRenameDetails));
+}
+
+NautilusFileUndoInfo *
+nautilus_file_undo_info_batch_rename_new (gint item_count)
+{
+ return g_object_new (NAUTILUS_TYPE_FILE_UNDO_INFO_BATCH_RENAME,
+ "op-type", NAUTILUS_FILE_UNDO_OP_BATCH_RENAME,
+ "item-count", item_count,
+ NULL);
+}
+
+void
+nautilus_file_undo_info_batch_rename_set_data_pre (NautilusFileUndoInfoBatchRename *self,
+ GList *old_files)
+{
+ GList *l;
+ GString *old_name;
+ GFile *file;
+
+ self->priv->old_files = old_files;
+ self->priv->old_display_names = NULL;
+
+ for (l = old_files; l != NULL; l = l->next) {
+ file = l->data;
+
+ old_name = g_string_new (g_file_get_basename (file));
+
+ self->priv->old_display_names = g_list_append (self->priv->old_display_names, old_name);
+ }
+}
+
+void
+nautilus_file_undo_info_batch_rename_set_data_post (NautilusFileUndoInfoBatchRename *self,
+ GList *new_files)
+{
+ GList *l;
+ GString *new_name;
+ GFile *file;
+
+ self->priv->new_files = new_files;
+ self->priv->new_display_names = NULL;
+
+ for (l = new_files; l != NULL; l = l->next) {
+ file = l->data;
+
+ new_name = g_string_new (g_file_get_basename (file));
+
+ self->priv->new_display_names = g_list_append (self->priv->new_display_names, new_name);
+ }
+}
+
/* trash */
G_DEFINE_TYPE (NautilusFileUndoInfoTrash, nautilus_file_undo_info_trash, NAUTILUS_TYPE_FILE_UNDO_INFO)
diff --git a/src/nautilus-file-undo-operations.h b/src/nautilus-file-undo-operations.h
index cec1c7c4e..8a153c32d 100644
--- a/src/nautilus-file-undo-operations.h
+++ b/src/nautilus-file-undo-operations.h
@@ -33,6 +33,7 @@ typedef enum {
NAUTILUS_FILE_UNDO_OP_DUPLICATE,
NAUTILUS_FILE_UNDO_OP_MOVE,
NAUTILUS_FILE_UNDO_OP_RENAME,
+ NAUTILUS_FILE_UNDO_OP_BATCH_RENAME,
NAUTILUS_FILE_UNDO_OP_CREATE_EMPTY_FILE,
NAUTILUS_FILE_UNDO_OP_CREATE_FILE_FROM_TEMPLATE,
NAUTILUS_FILE_UNDO_OP_CREATE_FOLDER,
@@ -185,6 +186,34 @@ void nautilus_file_undo_info_rename_set_data_pre (NautilusFileUndoInfoRename *se
void nautilus_file_undo_info_rename_set_data_post (NautilusFileUndoInfoRename *self,
GFile *new_file);
+/* batch rename */
+#define NAUTILUS_TYPE_FILE_UNDO_INFO_BATCH_RENAME (nautilus_file_undo_info_batch_rename_get_type ())
+#define NAUTILUS_FILE_UNDO_INFO_BATCH_RENAME(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), NAUTILUS_TYPE_FILE_UNDO_INFO_BATCH_RENAME, NautilusFileUndoInfoBatchRename))
+#define NAUTILUS_FILE_UNDO_INFO_BATCH_RENAME_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), NAUTILUS_TYPE_FILE_UNDO_INFO_BATCH_RENAME, NautilusFileUndoInfoBatchRenameClass))
+#define NAUTILUS_IS_FILE_UNDO_INFO_BATCH_RENAME(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), NAUTILUS_TYPE_FILE_UNDO_INFO_BATCH_RENAME))
+#define NAUTILUS_IS_FILE_UNDO_INFO_BATCH_RENAME_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), NAUTILUS_TYPE_FILE_UNDO_INFO_BATCH_RENAME))
+#define NAUTILUS_FILE_UNDO_INFO_BATCH_RENAME_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), NAUTILUS_TYPE_FILE_UNDO_INFO_BATCH_RENAME, NautilusFileUndoInfoBatchRenameClass))
+
+typedef struct _NautilusFileUndoInfoBatchRename NautilusFileUndoInfoBatchRename;
+typedef struct _NautilusFileUndoInfoBatchRenameClass NautilusFileUndoInfoBatchRenameClass;
+typedef struct _NautilusFileUndoInfoBatchRenameDetails NautilusFileUndoInfoBatchRenameDetails;
+
+struct _NautilusFileUndoInfoBatchRename {
+ NautilusFileUndoInfo parent;
+ NautilusFileUndoInfoBatchRenameDetails *priv;
+};
+
+struct _NautilusFileUndoInfoBatchRenameClass {
+ NautilusFileUndoInfoClass parent_class;
+};
+
+GType nautilus_file_undo_info_batch_rename_get_type (void) G_GNUC_CONST;
+NautilusFileUndoInfo *nautilus_file_undo_info_batch_rename_new (gint item_count);
+void nautilus_file_undo_info_batch_rename_set_data_pre (NautilusFileUndoInfoBatchRename *self,
+ GList *old_files);
+void nautilus_file_undo_info_batch_rename_set_data_post (NautilusFileUndoInfoBatchRename *self,
+ GList *new_files);
+
/* trash */
#define NAUTILUS_TYPE_FILE_UNDO_INFO_TRASH (nautilus_file_undo_info_trash_get_type ())
#define NAUTILUS_FILE_UNDO_INFO_TRASH(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), NAUTILUS_TYPE_FILE_UNDO_INFO_TRASH, NautilusFileUndoInfoTrash))
diff --git a/src/nautilus-file.c b/src/nautilus-file.c
index e3cbdfe20..928b90bf9 100644
--- a/src/nautilus-file.c
+++ b/src/nautilus-file.c
@@ -1647,15 +1647,37 @@ nautilus_file_operation_new (NautilusFile *file,
static void
nautilus_file_operation_remove (NautilusFileOperation *op)
{
+ GList *l;
+ NautilusFile *file;
+
op->file->details->operations_in_progress = g_list_remove
(op->file->details->operations_in_progress, op);
+
+ if (g_list_length (op->files) != 0) {
+ for (l = op->files; l != NULL; l = l->next) {
+ file = NAUTILUS_FILE (l->data);
+ file->details->operations_in_progress = g_list_remove
+ (file->details->operations_in_progress, op);
+ }
+ }
}
void
nautilus_file_operation_free (NautilusFileOperation *op)
{
+ NautilusFile *file;
+ GList *l;
+
nautilus_file_operation_remove (op);
- nautilus_file_unref (op->file);
+
+ if (g_list_length (op->files) == 0)
+ nautilus_file_unref (op->file);
+ else
+ for (l = op->files; l != NULL; l = l->next) {
+ file = NAUTILUS_FILE (l->data);
+ nautilus_file_unref (file);
+ }
+
g_object_unref (op->cancellable);
if (op->free_data) {
op->free_data (op->data);
@@ -1668,6 +1690,21 @@ nautilus_file_operation_free (NautilusFileOperation *op)
g_free (op);
}
+typedef struct {
+ NautilusDirectory *directory;
+ GList *files;
+} SelectionData;
+
+static gboolean
+change_selection_callback (gpointer user_data)
+{
+ SelectionData *data;
+
+ data = user_data;
+ nautilus_directory_emit_change_selection (data->directory, data->files);
+
+ return FALSE;
+}
void
nautilus_file_operation_complete (NautilusFileOperation *op,
@@ -1678,8 +1715,20 @@ nautilus_file_operation_complete (NautilusFileOperation *op,
* This makes it easier for some clients who see the "reverting"
* as "changing back".
*/
+ SelectionData *data;
+
nautilus_file_operation_remove (op);
- nautilus_file_changed (op->file);
+
+ if (g_list_length (op->files) == 0) {
+ nautilus_file_changed (op->file);
+ } else {
+ data = g_malloc (sizeof (SelectionData*));
+ data->directory = op->file->details->directory;
+ data->files = op->files;
+
+ g_idle_add (change_selection_callback, data);
+ }
+
if (op->callback) {
(* op->callback) (op->file, result_file, error, op->callback_data);
}
@@ -1752,7 +1801,16 @@ rename_get_info_callback (GObject *source_object,
g_object_unref (new_info);
}
- nautilus_file_operation_complete (op, NULL, error);
+
+ op->renamed_files++;
+
+ if (op->renamed_files + op->skipped_files == g_list_length (op->files)) {
+ nautilus_file_operation_complete (op, NULL, error);
+ }
+
+ if (g_list_length (op->files) == 0)
+ nautilus_file_operation_complete (op, NULL, error);
+
if (error) {
g_error_free (error);
}
@@ -1778,7 +1836,6 @@ rename_callback (GObject *source_object,
nautilus_file_undo_info_rename_set_data_post (NAUTILUS_FILE_UNDO_INFO_RENAME (op->undo_info),
new_file);
}
-
g_file_query_info_async (new_file,
NAUTILUS_FILE_DEFAULT_ATTRIBUTES,
0,
@@ -1811,6 +1868,170 @@ nautilus_file_rename (NautilusFile *file,
callback_data);
}
+static void
+real_batch_rename (GList *files,
+ GList *new_names,
+ NautilusFileOperationCallback callback,
+ gpointer callback_data)
+{
+ GList *l1, *l2, *old_files, *new_files;
+ NautilusFileOperation *op;
+ GFile *location;
+ GString *new_name;
+ NautilusFile *file;
+ GError *error;
+ GFile *new_file;
+ gboolean is_renameable_desktop_file, success, name_changed;
+ gchar *uri, *desktop_name, *old_name;
+
+ error = NULL;
+ old_files = NULL;
+ new_files = NULL;
+
+ /* Set up a batch renaming operation. */
+ op = nautilus_file_operation_new (files->data, callback, callback_data);
+ op->files = files;
+ op->renamed_files = 0;
+ op->skipped_files = 0;
+
+ for (l1 = files->next; l1 != NULL; l1 = l1->next) {
+ file = NAUTILUS_FILE (l1->data);
+
+ file->details->operations_in_progress = g_list_prepend (file->details->operations_in_progress,
+ op);
+ }
+
+ for (l1 = files, l2 = new_names; l1 != NULL && l2 != NULL; l1 = l1->next, l2 = l2->next) {
+ file = NAUTILUS_FILE (l1->data);
+ new_name = l2->data;
+
+ location = nautilus_file_get_location (file);
+ old_files = g_list_append (old_files, location);
+
+ is_renameable_desktop_file =
+ is_desktop_file (file) && can_rename_desktop_file (file);
+
+ /* Test the name-hasn't-changed */
+ if (!is_renameable_desktop_file && name_is (file, new_name->str)) {
+ new_file = nautilus_file_get_location (file);
+ new_files = g_list_append (new_files, new_file);
+ op->skipped_files++;
+
+ continue;
+ }
+
+ /* Can't rename a file that's already gone.
+ * We need to check this here because there may be a new
+ * file with the same name.
+ */
+ if (nautilus_file_is_gone (file)) {
+ nautilus_file_changed (file);
+
+ new_file = nautilus_file_get_location (file);
+ new_files = g_list_append (new_files, new_file);
+ op->skipped_files++;
+
+
+ continue;
+ }
+
+ /* Self-owned files can't be renamed */
+ if (nautilus_file_is_self_owned (file)) {
+ nautilus_file_changed (file);
+
+ new_file = nautilus_file_get_location (file);
+ new_files = g_list_append (new_files, new_file);
+ op->skipped_files++;
+
+ continue;
+ }
+
+ if (is_renameable_desktop_file) {
+ /* Don't actually change the name if the new name is the same.
+ * This helps for the vfolder method where this can happen and
+ * we want to minimize actual changes
+ */
+ uri = nautilus_file_get_uri (file);
+ old_name = nautilus_link_local_get_text (uri);
+ if (old_name != NULL && strcmp (new_name->str, old_name) == 0) {
+ success = TRUE;
+ name_changed = FALSE;
+ } else {
+ success = nautilus_link_local_set_text (uri, new_name->str);
+ name_changed = TRUE;
+ }
+
+ g_free (old_name);
+ g_free (uri);
+
+ if (!success) {
+ new_file = nautilus_file_get_location (file);
+ new_files = g_list_append (new_files, new_file);
+ op->skipped_files++;
+
+ continue;
+ }
+
+ new_name = g_string_append (new_name, ".desktop");
+ desktop_name = g_strdelimit (new_name->str, "/", '-');
+ new_name = g_string_insert (new_name, 0, desktop_name);
+
+ g_free (desktop_name);
+
+ if (name_is (file, new_name->str)) {
+ if (name_changed) {
+ nautilus_file_invalidate_attributes (file,
+ NAUTILUS_FILE_ATTRIBUTE_INFO |
+ NAUTILUS_FILE_ATTRIBUTE_LINK_INFO);
+ }
+
+ new_file = nautilus_file_get_location (file);
+ new_files = g_list_append (new_files, new_file);
+ op->skipped_files++;
+
+ continue;
+ }
+ }
+
+ g_assert (G_IS_FILE (location));
+
+ /* Do the renaming. */
+ new_file = g_file_set_display_name (location,
+ new_name->str,
+ op->cancellable,
+ &error);
+
+ new_files = g_list_append (new_files, new_file);
+
+ op->file = file;
+ g_file_query_info_async (new_file,
+ NAUTILUS_FILE_DEFAULT_ATTRIBUTES,
+ 0,
+ G_PRIORITY_DEFAULT,
+ op->cancellable,
+ rename_get_info_callback,
+ op);
+
+ if (error != NULL) {
+ g_error_free (error);
+ error = NULL;
+ }
+ }
+
+ /* Tell the undo manager a batch rename is taking place */
+ if (!nautilus_file_undo_manager_is_operating ()) {
+ op->undo_info = nautilus_file_undo_info_batch_rename_new (g_list_length (new_files));
+
+ nautilus_file_undo_info_batch_rename_set_data_pre (NAUTILUS_FILE_UNDO_INFO_BATCH_RENAME (op->undo_info),
+ old_files);
+
+ nautilus_file_undo_info_batch_rename_set_data_post (NAUTILUS_FILE_UNDO_INFO_BATCH_RENAME (op->undo_info),
+ new_files);
+
+ nautilus_file_undo_manager_set_action (op->undo_info);
+ }
+}
+
gboolean
nautilus_file_rename_handle_file_gone (NautilusFile *file,
NautilusFileOperationCallback callback,
@@ -1819,7 +2040,7 @@ nautilus_file_rename_handle_file_gone (NautilusFile *file,
GError *error;
if (nautilus_file_is_gone (file)) {
- /* Claim that something changed even if the rename
+ /* Claim that something changed even if the rename
* failed. This makes it easier for some clients who
* see the "reverting" to the old name as "changing
* back".
@@ -1835,6 +2056,18 @@ nautilus_file_rename_handle_file_gone (NautilusFile *file,
return FALSE;
}
+void
+nautilus_file_batch_rename (GList *files,
+ GList *new_names,
+ NautilusFileOperationCallback callback,
+ gpointer callback_data)
+{
+ real_batch_rename (files,
+ new_names,
+ callback,
+ callback_data);
+}
+
static void
real_rename (NautilusFile *file,
const char *new_name,
diff --git a/src/nautilus-file.h b/src/nautilus-file.h
index 10505753f..3c65a533c 100644
--- a/src/nautilus-file.h
+++ b/src/nautilus-file.h
@@ -324,6 +324,10 @@ void nautilus_file_rename (Nautilu
const char *new_name,
NautilusFileOperationCallback callback,
gpointer callback_data);
+void nautilus_file_batch_rename (GList *files,
+ GList *new_names,
+ NautilusFileOperationCallback callback,
+ gpointer callback_data);
void nautilus_file_cancel (NautilusFile *file,
NautilusFileOperationCallback callback,
gpointer callback_data);
diff --git a/src/nautilus-files-view.c b/src/nautilus-files-view.c
index 3f5ef6b7e..2abaa3129 100644
--- a/src/nautilus-files-view.c
+++ b/src/nautilus-files-view.c
@@ -3928,6 +3928,18 @@ files_changed_callback (NautilusDirectory *directory,
}
static void
+change_selection_callback (NautilusDirectory *directory,
+ GList *selection,
+ gpointer callback_data)
+{
+ NautilusFilesView *view;
+
+ view = NAUTILUS_FILES_VIEW (callback_data);
+
+ nautilus_view_set_selection (view, selection);
+}
+
+static void
done_loading_callback (NautilusDirectory *directory,
gpointer callback_data)
{
@@ -3944,7 +3956,6 @@ done_loading_callback (NautilusDirectory *directory,
*/
unschedule_display_of_pending_files (view);
schedule_timeout_display_of_pending_files (view, UPDATE_INTERVAL_MIN);
-
remove_loading_floating_bar (view);
}
nautilus_profile_end (NULL);
@@ -4000,6 +4011,9 @@ nautilus_files_view_add_subdirectory (NautilusFilesView *view,
g_signal_connect
(directory, "files-changed",
G_CALLBACK (files_changed_callback), view);
+ g_signal_connect
+ (directory, "change-selection",
+ G_CALLBACK (change_selection_callback), view);
view->details->subdirectory_list = g_list_prepend (
view->details->subdirectory_list, directory);
@@ -7208,6 +7222,11 @@ finish_loading (NautilusFilesView *view)
(view->details->model, "files-changed",
G_CALLBACK (files_changed_callback), view);
+ g_signal_connect (view->details->model,
+ "change-selection",
+ G_CALLBACK (change_selection_callback),
+ view);
+
nautilus_profile_end (NULL);
}