diff options
author | Alexandru Pandelea <alexandru.pandelea@gmail.com> | 2016-07-26 13:10:51 +0300 |
---|---|---|
committer | Alexandru Pandelea <alexandru.pandelea@gmail.com> | 2016-07-26 13:10:51 +0300 |
commit | 840481fa160c9d496b5f9057d9f73b12387c2bf2 (patch) | |
tree | b768856076a4c07ddc6cb71a1ac772bd9b6618c6 | |
parent | 50f0b77e805d237ac7303dfbe1c1039edc43717d (diff) | |
download | nautilus-840481fa160c9d496b5f9057d9f73b12387c2bf2.tar.gz |
Add Batch Rename operation
-rw-r--r-- | src/nautilus-batch-rename-utilities.c | 43 | ||||
-rw-r--r-- | src/nautilus-batch-rename-utilities.h | 3 | ||||
-rw-r--r-- | src/nautilus-batch-rename.c | 221 | ||||
-rw-r--r-- | src/nautilus-directory-private.h | 2 | ||||
-rw-r--r-- | src/nautilus-directory.c | 23 | ||||
-rw-r--r-- | src/nautilus-directory.h | 2 | ||||
-rw-r--r-- | src/nautilus-file-private.h | 3 | ||||
-rw-r--r-- | src/nautilus-file-undo-operations.c | 183 | ||||
-rw-r--r-- | src/nautilus-file-undo-operations.h | 29 | ||||
-rw-r--r-- | src/nautilus-file.c | 243 | ||||
-rw-r--r-- | src/nautilus-file.h | 4 | ||||
-rw-r--r-- | src/nautilus-files-view.c | 21 |
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); } |