summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCorey Berla <corey@berla.me>2023-01-20 10:36:59 -0800
committerCorey Berla <corey@berla.me>2023-04-07 20:46:45 -0700
commitcb406e31a41511b0450ab9a714813de335afd445 (patch)
tree63161b1ddfe18eecd5aebc9fa99a66a7d49abdb2
parent65f420907b0541a371a0815399c8834ceb72a62f (diff)
downloadnautilus-wip/corey/performance-remove-files.tar.gz
files-view: Replace REMOVE_FILE with REMOVE_FILESwip/corey/performance-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.
-rw-r--r--src/nautilus-files-view.c35
-rw-r--r--src/nautilus-files-view.h4
-rw-r--r--src/nautilus-list-base.c28
-rw-r--r--src/nautilus-view-model.c79
-rw-r--r--src/nautilus-view-model.h6
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,