From cb406e31a41511b0450ab9a714813de335afd445 Mon Sep 17 00:00:00 2001 From: Corey Berla Date: Fri, 20 Jan 2023 10:36:59 -0800 Subject: files-view: Replace REMOVE_FILE with REMOVE_FILES Files are added in bulk to the view with ADD_FILES, but then removed from the view with REMOVE_FILE, which takes a single file. This is very inefficient in the case where a large number of items are deleted. In other words, if you delete a folder with 1,000 items, the process completes very quickly, but if you delete the 1,000 items, Nautilus will hang for an extended period of time. This can be a common problem if the view is current showing Trash, and the the trash is emptied. --- src/nautilus-files-view.c | 35 ++++++++++++++++----- src/nautilus-files-view.h | 4 +-- src/nautilus-list-base.c | 28 ++++++++++++----- src/nautilus-view-model.c | 79 ++++++++++++++++++++++++++++++++++++++++------- src/nautilus-view-model.h | 6 ++-- 5 files changed, 120 insertions(+), 32 deletions(-) diff --git a/src/nautilus-files-view.c b/src/nautilus-files-view.c index 01028dd58..aeefce69c 100644 --- a/src/nautilus-files-view.c +++ b/src/nautilus-files-view.c @@ -129,7 +129,7 @@ enum END_LOADING, FILE_CHANGED, MOVE_COPY_ITEMS, - REMOVE_FILE, + REMOVE_FILES, SELECTION_CHANGED, TRASH, DELETE, @@ -4336,6 +4336,8 @@ process_old_files (NautilusFilesView *view) if (files_added != NULL || files_changed != NULL) { + NautilusDirectory *prev_directory = NULL; + GList *files_removed = NULL; gboolean send_selection_change = FALSE; g_signal_emit (view, signals[BEGIN_FILE_CHANGES], 0); @@ -4365,9 +4367,22 @@ process_old_files (NautilusFilesView *view) gboolean should_show_file; pending = node->data; should_show_file = still_should_show_file (view, pending); - g_signal_emit (view, - signals[should_show_file ? FILE_CHANGED : REMOVE_FILE], 0, - pending->file, pending->directory); + if (should_show_file) + { + g_signal_emit (view, + signals[FILE_CHANGED], 0, pending->file, pending->directory); + } + else + { + if (prev_directory != NULL && prev_directory != pending->directory) + { + g_signal_emit (view, signals[REMOVE_FILES], 0, + files_removed, pending->directory); + g_clear_pointer (&files_removed, g_list_free); + } + + files_removed = g_list_prepend (files_removed, pending->file); + } /* Acknowledge the files that were pending to be revealed */ if (g_hash_table_contains (priv->pending_reveal, pending->file)) @@ -4386,6 +4401,12 @@ process_old_files (NautilusFilesView *view) } } + if (files_removed != NULL) + { + g_signal_emit (view, signals[REMOVE_FILES], 0, + files_removed, pending->directory); + g_clear_pointer (&files_removed, g_list_free); + } if (files_changed != NULL) { g_autolist (NautilusFile) selection = NULL; @@ -9570,14 +9591,14 @@ nautilus_files_view_class_init (NautilusFilesViewClass *klass) NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 2, NAUTILUS_TYPE_FILE, NAUTILUS_TYPE_DIRECTORY); - signals[REMOVE_FILE] = + signals[REMOVE_FILES] = g_signal_new ("remove-file", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (NautilusFilesViewClass, remove_file), + G_STRUCT_OFFSET (NautilusFilesViewClass, remove_files), NULL, NULL, g_cclosure_marshal_generic, - G_TYPE_NONE, 2, NAUTILUS_TYPE_FILE, NAUTILUS_TYPE_DIRECTORY); + G_TYPE_NONE, 2, G_TYPE_POINTER, NAUTILUS_TYPE_DIRECTORY); signals[SELECTION_CHANGED] = g_signal_new ("selection-changed", G_TYPE_FROM_CLASS (klass), diff --git a/src/nautilus-files-view.h b/src/nautilus-files-view.h index 536c613f6..2e088c364 100644 --- a/src/nautilus-files-view.h +++ b/src/nautilus-files-view.h @@ -60,8 +60,8 @@ struct _NautilusFilesViewClass { */ void (* add_files) (NautilusFilesView *view, GList *files); - void (* remove_file) (NautilusFilesView *view, - NautilusFile *file, + void (* remove_files) (NautilusFilesView *view, + GList *files, NautilusDirectory *directory); /* The 'file_changed' signal is emitted to signal a change in a file, diff --git a/src/nautilus-list-base.c b/src/nautilus-list-base.c index cb0c2fa81..00deee8e7 100644 --- a/src/nautilus-list-base.c +++ b/src/nautilus-list-base.c @@ -1160,18 +1160,30 @@ real_end_file_changes (NautilusFilesView *files_view) } static void -real_remove_file (NautilusFilesView *files_view, - NautilusFile *file, - NautilusDirectory *directory) +real_remove_files (NautilusFilesView *files_view, + GList *files, + NautilusDirectory *directory) { NautilusListBase *self = NAUTILUS_LIST_BASE (files_view); NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); - NautilusViewItem *item; + g_autoptr (GList) items = NULL; - item = nautilus_view_model_get_item_from_file (priv->model, file); - if (item != NULL) + + for (GList *l = files; l != NULL; l = l->next) + { + NautilusViewItem *item; + + item = nautilus_view_model_get_item_from_file (priv->model, l->data); + + if (item != NULL) + { + items = g_list_prepend (items, item); + } + } + + if (items != NULL) { - nautilus_view_model_remove_item (priv->model, item, directory); + nautilus_view_model_remove_items (priv->model, items, directory); } } @@ -1813,7 +1825,7 @@ nautilus_list_base_class_init (NautilusListBaseClass *klass) files_view_class->get_selection = real_get_selection; files_view_class->get_selection_for_file_transfer = real_get_selection_for_file_transfer; files_view_class->is_empty = real_is_empty; - files_view_class->remove_file = real_remove_file; + files_view_class->remove_files = real_remove_files; files_view_class->select_all = real_select_all; files_view_class->set_selection = real_set_selection; files_view_class->invert_selection = real_invert_selection; diff --git a/src/nautilus-view-model.c b/src/nautilus-view-model.c index 4ee2c9037..c41995fbc 100644 --- a/src/nautilus-view-model.c +++ b/src/nautilus-view-model.c @@ -395,28 +395,83 @@ nautilus_view_model_get_item_from_file (NautilusViewModel *self, return g_hash_table_lookup (self->map_files_to_model, file); } +static gint +sort_for_model (gconstpointer a, + gconstpointer b, + gpointer user_data) +{ + GListStore *store = user_data; + + guint pos_a, pos_b; + + g_list_store_find (store, (gpointer) a, &pos_a); + g_list_store_find (store, (gpointer) b, &pos_b); + + if (pos_a < pos_b) + { + return -1; + } + else if (pos_a > pos_b) + { + return 1; + } + else + { + return 0; + } +} + void -nautilus_view_model_remove_item (NautilusViewModel *self, - NautilusViewItem *item, - NautilusDirectory *directory) +nautilus_view_model_remove_items (NautilusViewModel *self, + GList *items, + NautilusDirectory *directory) { - NautilusFile *file; - g_autoptr (NautilusFile) parent = NULL; - GListStore *dir_store; - guint i; + g_autoptr (NautilusFile) parent = nautilus_directory_get_corresponding_file (directory); + GListStore *dir_store = get_directory_store (self, parent); + guint start, i; + guint n_items_in_range = 0; - file = nautilus_view_item_get_file (item); - parent = nautilus_directory_get_corresponding_file (directory); - dir_store = get_directory_store (self, parent); - if (g_list_store_find (dir_store, item, &i)) + /* The order of the items in the directory store, is not necessarily the order + * that we received them in. Let's sort the items according to the sort on + * the directory store to minimize the calls to ::items-changed. */ + items = g_list_sort_with_data (items, sort_for_model, dir_store); + + /* Reverse the sort so that removing items doesn't impact the index */ + items = g_list_reverse (items); + + for (GList *l = items; l != NULL; l = l->next) { - g_list_store_remove (dir_store, i); + NautilusViewItem *item = l->data; + NautilusFile *file = nautilus_view_item_get_file (item); + + if (g_list_store_find (dir_store, item, &i)) + { + if (n_items_in_range > 0 && i != start - 1) + { + g_list_store_splice (dir_store, start, n_items_in_range, 0, 0); + n_items_in_range = 0; + } + + /* `start` is a moving target that gets decremented as we move down the list */ + start = i; + n_items_in_range++; + } + else + { + g_warning ("Failed to remove item %s", nautilus_file_get_uri (file)); + } + g_hash_table_remove (self->map_files_to_model, file); if (nautilus_file_is_directory (file)) { g_hash_table_remove (self->directory_reverse_map, file); } } + + if (n_items_in_range > 0) + { + g_list_store_splice (dir_store, start, n_items_in_range, 0, 0); + } } void diff --git a/src/nautilus-view-model.h b/src/nautilus-view-model.h index 718412652..89b3c3713 100644 --- a/src/nautilus-view-model.h +++ b/src/nautilus-view-model.h @@ -21,9 +21,9 @@ NautilusViewItem * nautilus_view_model_get_item_from_file (NautilusViewModel *se GQueue * nautilus_view_model_get_items_from_files (NautilusViewModel *self, GQueue *files); /* Don't use inside a loop, use nautilus_view_model_remove_all_items instead. */ -void nautilus_view_model_remove_item (NautilusViewModel *self, - NautilusViewItem *item, - NautilusDirectory *directory); +void nautilus_view_model_remove_items (NautilusViewModel *self, + GList *items, + NautilusDirectory *directory); void nautilus_view_model_remove_all_items (NautilusViewModel *self); /* Don't use inside a loop, use nautilus_view_model_add_items instead. */ void nautilus_view_model_add_item (NautilusViewModel *self, -- cgit v1.2.1