From ab240ae6e146b26fd16d906e21ff2108846a632d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Fernandes?= Date: Sun, 23 Jul 2023 10:40:56 +0100 Subject: [PATCH 1/7] list-base: Drop .scroll-to-file workaround Introduced by 827e1061b95de08c24d0c948617c6d3e9bf7f3fd As per that commit message, this is unnecessary now, but I forgot to remove it and even carried it over to list-base.c Drop it now. --- src/nautilus-list-base.c | 49 ++++------------------------------------ 1 file changed, 5 insertions(+), 44 deletions(-) diff --git a/src/nautilus-list-base.c b/src/nautilus-list-base.c index 26ee2ebe8e..36035890f4 100644 --- a/src/nautilus-list-base.c +++ b/src/nautilus-list-base.c @@ -43,7 +43,6 @@ struct _NautilusListBasePrivate GList *cut_files; - guint scroll_to_file_handle_id; guint prioritize_thumbnailing_handle_id; GtkAdjustment *vadjustment; @@ -901,7 +900,6 @@ real_clear (NautilusFilesView *files_view) NautilusListBase *self = NAUTILUS_LIST_BASE (files_view); NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); - g_clear_handle_id (&priv->scroll_to_file_handle_id, g_source_remove); nautilus_view_model_remove_all_items (priv->model); } @@ -1396,57 +1394,21 @@ real_get_last_visible_file (NautilusFilesView *files_view) return uri; } -typedef struct -{ - NautilusListBase *view; - char *uri; -} ScrollToFileData; - static void -scroll_to_file_data_free (ScrollToFileData *data) -{ - g_free (data->uri); - g_free (data); -} - -static gboolean -scroll_to_file_on_idle (ScrollToFileData *data) +real_scroll_to_file (NautilusFilesView *files_view, + const char *uri) { - NautilusListBase *self = data->view; + NautilusListBase *self = NAUTILUS_LIST_BASE (files_view); NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); - g_autoptr (NautilusFile) file = NULL; + g_autoptr (NautilusFile) file = nautilus_file_get_existing_by_uri (uri); NautilusViewItem *item; guint i; - priv->scroll_to_file_handle_id = 0; - - file = nautilus_file_get_existing_by_uri (data->uri); item = nautilus_view_model_find_item_for_file (priv->model, file); - g_return_val_if_fail (item != NULL, G_SOURCE_REMOVE); + g_return_if_fail (item != NULL); i = nautilus_view_model_get_index (priv->model, item); nautilus_list_base_scroll_to_item (self, i); - - return G_SOURCE_REMOVE; -} - -static void -real_scroll_to_file (NautilusFilesView *files_view, - const char *uri) -{ - NautilusListBase *self = NAUTILUS_LIST_BASE (files_view); - NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); - ScrollToFileData *data; - guint handle_id; - - data = g_new (ScrollToFileData, 1); - data->view = self; - data->uri = g_strdup (uri); - handle_id = g_idle_add_full (G_PRIORITY_LOW, - (GSourceFunc) scroll_to_file_on_idle, - data, - (GDestroyNotify) scroll_to_file_data_free); - priv->scroll_to_file_handle_id = handle_id; } static void @@ -1587,7 +1549,6 @@ nautilus_list_base_dispose (GObject *object) NautilusListBase *self = NAUTILUS_LIST_BASE (object); NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); - g_clear_handle_id (&priv->scroll_to_file_handle_id, g_source_remove); g_clear_handle_id (&priv->prioritize_thumbnailing_handle_id, g_source_remove); g_clear_handle_id (&priv->hover_timer_id, g_source_remove); -- GitLab From 891bb3d426ae322c47be3cc984ebd28029780e67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Fernandes?= Date: Sun, 23 Jul 2023 12:46:24 +0100 Subject: [PATCH 2/7] files-view: Create NautilusViewModel directly The end goal is having multiple NautilusListBase-derived views sharing the same model, and for that we need to make NautilusListBase a mere consumer of the model. As a first step, create and destroy the model in NautilusFilesView. Overview of the plan: https://gitlab.gnome.org/GNOME/nautilus/-/issues/3042 --- src/nautilus-files-view.c | 23 ++++++++++++++++++++++- src/nautilus-files-view.h | 1 + src/nautilus-list-base.c | 8 +------- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/nautilus-files-view.c b/src/nautilus-files-view.c index dbc6d62032..778e1ebff0 100644 --- a/src/nautilus-files-view.c +++ b/src/nautilus-files-view.c @@ -64,6 +64,7 @@ #include "nautilus-filename-utilities.h" #include "nautilus-floating-bar.h" #include "nautilus-global-preferences.h" +#include "nautilus-grid-view.h" #include "nautilus-icon-info.h" #include "nautilus-icon-names.h" #include "nautilus-list-view.h" @@ -83,7 +84,7 @@ #include "nautilus-trash-monitor.h" #include "nautilus-ui-utilities.h" #include "nautilus-view.h" -#include "nautilus-grid-view.h" +#include "nautilus-view-model.h" #include "nautilus-window.h" #include "nautilus-tracker-utilities.h" @@ -167,6 +168,8 @@ typedef struct GFile *location; guint dir_merge_id; + NautilusViewModel *model; + NautilusQuery *search_query; GFile *location_before_search; NautilusDirectory *outgoing_search; @@ -3369,6 +3372,7 @@ nautilus_files_view_dispose (GObject *object) g_clear_object (&priv->location_before_search); g_clear_object (&priv->outgoing_search); g_clear_object (&priv->location); + g_clear_object (&priv->model); adw_bin_set_child (ADW_BIN (view), NULL); gtk_widget_dispose_template (GTK_WIDGET (view), NAUTILUS_TYPE_FILES_VIEW); @@ -9804,6 +9808,13 @@ nautilus_files_view_init (NautilusFilesView *view) priv = nautilus_files_view_get_instance_private (view); + priv->model = nautilus_view_model_new (); + g_signal_connect_object (GTK_SELECTION_MODEL (priv->model), + "selection-changed", + G_CALLBACK (nautilus_files_view_notify_selection_changed), + view, + G_CONNECT_SWAPPED); + /* Toolbar menu */ builder = gtk_builder_new_from_resource ("/org/gnome/nautilus/ui/nautilus-toolbar-view-menu.ui"); priv->toolbar_menu_sections = g_new0 (NautilusToolbarMenuSections, 1); @@ -10015,3 +10026,13 @@ nautilus_files_view_new (guint id, return view; } + +/* Temporary helper to be removed in upcoming Merge Requests. Generic pointer to + * avoid including nautilus-view-model.h in the header */ +gpointer +nautilus_files_view_get_model (NautilusFilesView *self) +{ + NautilusFilesViewPrivate *priv = nautilus_files_view_get_instance_private (self); + + return (gpointer) g_object_ref (priv->model); +} diff --git a/src/nautilus-files-view.h b/src/nautilus-files-view.h index 24500c3fa6..19bd594994 100644 --- a/src/nautilus-files-view.h +++ b/src/nautilus-files-view.h @@ -257,6 +257,7 @@ void nautilus_files_view_add_subdirectory (Nautilu NautilusDirectory *directory); void nautilus_files_view_remove_subdirectory (NautilusFilesView *view, NautilusDirectory *directory); +gpointer nautilus_files_view_get_model (NautilusFilesView *view); /* file operations */ char * nautilus_files_view_get_backing_uri (NautilusFilesView *view); diff --git a/src/nautilus-list-base.c b/src/nautilus-list-base.c index 36035890f4..5d5bea2f00 100644 --- a/src/nautilus-list-base.c +++ b/src/nautilus-list-base.c @@ -1773,13 +1773,7 @@ nautilus_list_base_init (NautilusListBase *self) g_signal_connect (vadjustment, "changed", (GCallback) on_vadjustment_changed, self); g_signal_connect (vadjustment, "value-changed", (GCallback) on_vadjustment_changed, self); - priv->model = nautilus_view_model_new (); - - g_signal_connect_object (GTK_SELECTION_MODEL (priv->model), - "selection-changed", - G_CALLBACK (nautilus_files_view_notify_selection_changed), - NAUTILUS_FILES_VIEW (self), - G_CONNECT_SWAPPED); + priv->model = NAUTILUS_VIEW_MODEL (nautilus_files_view_get_model (NAUTILUS_FILES_VIEW (self))); set_click_mode_from_settings (self); -- GitLab From c911df7702c2e1189358b0cd8e6e68e766e3281d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Fernandes?= Date: Sat, 9 Sep 2023 16:19:08 +0100 Subject: [PATCH 3/7] files-view: Manage NautilusViewModel directly Now that we own the model, move in the code which populates and depopulates it, along with the handling of change notifications. --- src/nautilus-files-view.c | 79 +++++++++++++++++++++++++++++++++++ src/nautilus-files-view.h | 2 - src/nautilus-list-base.c | 86 --------------------------------------- 3 files changed, 79 insertions(+), 88 deletions(-) diff --git a/src/nautilus-files-view.c b/src/nautilus-files-view.c index 778e1ebff0..f619786b9c 100644 --- a/src/nautilus-files-view.c +++ b/src/nautilus-files-view.c @@ -4353,6 +4353,73 @@ _g_lists_sort_and_check_for_intersection (GList **list_1, return FALSE; } +static void +real_add_files (NautilusFilesView *self, + GList *files) +{ + NautilusFilesViewPrivate *priv = nautilus_files_view_get_instance_private (self); + g_autolist (NautilusViewItem) items = NULL; + + items = g_list_copy_deep (files, (GCopyFunc) nautilus_view_item_new, NULL); + nautilus_view_model_add_items (priv->model, items); +} + +static void +real_remove_files (NautilusFilesView *self, + GList *files, + NautilusDirectory *directory) +{ + NautilusFilesViewPrivate *priv = nautilus_files_view_get_instance_private (self); + g_autoptr (GList) items = NULL; + + for (GList *l = files; l != NULL; l = l->next) + { + NautilusViewItem *item; + + item = nautilus_view_model_find_item_for_file (priv->model, l->data); + if (item != NULL) + { + items = g_list_prepend (items, item); + } + } + + if (items != NULL) + { + nautilus_view_model_remove_items (priv->model, items, directory); + } +} + +static void +real_file_changed (NautilusFilesView *self, + NautilusFile *file, + NautilusDirectory *directory) +{ + NautilusFilesViewPrivate *priv = nautilus_files_view_get_instance_private (self); + g_autoptr (NautilusFile) directory_as_file = NULL; + NautilusViewItem *item; + + directory_as_file = nautilus_directory_get_corresponding_file (directory); + if (file == directory_as_file) + { + /* We don't care about changes to the current directory itself here, so + * silently ignore it. This happens only with self-owned files.*/ + return; + } + + item = nautilus_view_model_find_item_for_file (priv->model, file); + if (item != NULL) + { + nautilus_view_item_file_changed (item); + } + else + { + /* When a file that was hidden is not hidden anymore (e.g. undoing the + * rename operation which made it hidden), we get a change notification + * for a file that's not in our model. Let's add it then. */ + real_add_files (self, &(GList){ .data = file }); + } +} + static void process_old_files (NautilusFilesView *view) { @@ -8686,6 +8753,14 @@ file_changed_callback (NautilusFile *file, schedule_update_status (view); } +static void +real_clear (NautilusFilesView *self) +{ + NautilusFilesViewPrivate *priv = nautilus_files_view_get_instance_private (self); + + nautilus_view_model_remove_all_items (priv->model); +} + static void emit_clear (NautilusFilesView *self) { @@ -9691,6 +9766,10 @@ nautilus_files_view_class_init (NautilusFilesViewClass *klass) g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + klass->clear = real_clear; + klass->add_files = real_add_files; + klass->remove_files = real_remove_files; + klass->file_changed = real_file_changed; klass->begin_loading = real_begin_loading; klass->get_backing_uri = real_get_backing_uri; klass->update_context_menus = real_update_context_menus; diff --git a/src/nautilus-files-view.h b/src/nautilus-files-view.h index 19bd594994..e3654233ab 100644 --- a/src/nautilus-files-view.h +++ b/src/nautilus-files-view.h @@ -56,7 +56,6 @@ struct _NautilusFilesViewClass { void (* begin_file_changes) (NautilusFilesView *view); /* The 'add_files' signal is emitted to add a set of files to the view. - * It must be replaced by each subclass. */ void (* add_files) (NautilusFilesView *view, GList *files); @@ -66,7 +65,6 @@ struct _NautilusFilesViewClass { /* The 'file_changed' signal is emitted to signal a change in a file, * including the file being removed. - * It must be replaced by each subclass. */ void (* file_changed) (NautilusFilesView *view, NautilusFile *file, diff --git a/src/nautilus-list-base.c b/src/nautilus-list-base.c index 5d5bea2f00..712683084f 100644 --- a/src/nautilus-list-base.c +++ b/src/nautilus-list-base.c @@ -70,9 +70,6 @@ enum static GParamSpec *properties[N_PROPS] = { NULL, }; -static void real_add_files (NautilusFilesView *files_view, - GList *files); - static inline NautilusViewItem * get_view_item (GListModel *model, guint position) @@ -894,15 +891,6 @@ real_begin_loading (NautilusFilesView *files_view) } } -static void -real_clear (NautilusFilesView *files_view) -{ - NautilusListBase *self = NAUTILUS_LIST_BASE (files_view); - NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); - - nautilus_view_model_remove_all_items (priv->model); -} - static void set_click_mode_from_settings (NautilusListBase *self) { @@ -921,37 +909,6 @@ real_click_policy_changed (NautilusFilesView *files_view) set_click_mode_from_settings (NAUTILUS_LIST_BASE (files_view)); } -static void -real_file_changed (NautilusFilesView *files_view, - NautilusFile *file, - NautilusDirectory *directory) -{ - NautilusListBase *self = NAUTILUS_LIST_BASE (files_view); - g_autoptr (NautilusFile) directory_as_file = NULL; - NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); - NautilusViewItem *item; - - directory_as_file = nautilus_directory_get_corresponding_file (directory); - if (file == directory_as_file) - { - /* We don't care about changes to the current directory itself here, so - * silently ignore it. This happens only with self-owned files.*/ - return; - } - - item = nautilus_view_model_find_item_for_file (priv->model, file); - if (item != NULL) - { - nautilus_view_item_file_changed (item); - } - else - { - /* When a file that was hidden is not hidden anymore (e.g. undoing the - * rename operation which made it hidden), we get a change notification - * for a file that's not in our model. Let's add it then. */ - real_add_files (files_view, &(GList){ .data = file }); - } -} static gboolean is_ancestor_selected (GtkTreeListRow *row, GtkBitset *selection) @@ -1045,33 +1002,6 @@ real_end_file_changes (NautilusFilesView *files_view) nautilus_view_model_sort (priv->model); } -static void -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); - g_autoptr (GList) items = NULL; - - for (GList *l = files; l != NULL; l = l->next) - { - NautilusViewItem *item; - - item = nautilus_view_model_find_item_for_file (priv->model, l->data); - - if (item != NULL) - { - items = g_list_prepend (items, item); - } - } - - if (items != NULL) - { - nautilus_view_model_remove_items (priv->model, items, directory); - } -} - static void real_set_selection (NautilusFilesView *files_view, GList *selection) @@ -1411,18 +1341,6 @@ real_scroll_to_file (NautilusFilesView *files_view, nautilus_list_base_scroll_to_item (self, i); } -static void -real_add_files (NautilusFilesView *files_view, - GList *files) -{ - NautilusListBase *self = NAUTILUS_LIST_BASE (files_view); - NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); - g_autolist (NautilusViewItem) items = NULL; - - items = g_list_copy_deep (files, (GCopyFunc) nautilus_view_item_new, NULL); - nautilus_view_model_add_items (priv->model, items); -} - static void real_select_first (NautilusFilesView *files_view) { @@ -1722,15 +1640,11 @@ nautilus_list_base_class_init (NautilusListBaseClass *klass) widget_class->focus = nautilus_list_base_focus; - files_view_class->add_files = real_add_files; files_view_class->begin_loading = real_begin_loading; - files_view_class->clear = real_clear; files_view_class->click_policy_changed = real_click_policy_changed; - files_view_class->file_changed = real_file_changed; 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_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; -- GitLab From 49b631f80e9ea556cceda16be8c2306b81c603ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Fernandes?= Date: Sat, 9 Sep 2023 16:20:57 +0100 Subject: [PATCH 4/7] files-view: Remove ::is-empty signal Now that the model lives here, we can check directly. --- src/nautilus-files-view.c | 6 +++--- src/nautilus-files-view.h | 5 ----- src/nautilus-list-base.c | 10 ---------- 3 files changed, 3 insertions(+), 18 deletions(-) diff --git a/src/nautilus-files-view.c b/src/nautilus-files-view.c index f619786b9c..a14f9e9b07 100644 --- a/src/nautilus-files-view.c +++ b/src/nautilus-files-view.c @@ -944,11 +944,11 @@ nautilus_files_view_supports_extract_here (NautilusFilesView *view) } static gboolean -nautilus_files_view_is_empty (NautilusFilesView *view) +nautilus_files_view_is_empty (NautilusFilesView *self) { - g_return_val_if_fail (NAUTILUS_IS_FILES_VIEW (view), FALSE); + NautilusFilesViewPrivate *priv = nautilus_files_view_get_instance_private (self); - return NAUTILUS_FILES_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->is_empty (view); + return g_list_model_get_n_items (G_LIST_MODEL (priv->model)) == 0; } /** diff --git a/src/nautilus-files-view.h b/src/nautilus-files-view.h index e3654233ab..dc30fdcf2a 100644 --- a/src/nautilus-files-view.h +++ b/src/nautilus-files-view.h @@ -181,11 +181,6 @@ struct _NautilusFilesViewClass { void (* update_actions_state) (NautilusFilesView *view); - /* is_empty is a function pointer that subclasses must - * override to report whether the view contains any items. - */ - gboolean (* is_empty) (NautilusFilesView *view); - /* Preference change callbacks, overridden by icon and list views. * Icon and list views respond by synchronizing to the new preference * values and forcing an update if appropriate. diff --git a/src/nautilus-list-base.c b/src/nautilus-list-base.c index 712683084f..987ca4587e 100644 --- a/src/nautilus-list-base.c +++ b/src/nautilus-list-base.c @@ -984,15 +984,6 @@ real_get_selection_for_file_transfer (NautilusFilesView *files_view) return get_selection (files_view, TRUE); } -static gboolean -real_is_empty (NautilusFilesView *files_view) -{ - NautilusListBase *self = NAUTILUS_LIST_BASE (files_view); - NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); - - return g_list_model_get_n_items (G_LIST_MODEL (priv->model)) == 0; -} - static void real_end_file_changes (NautilusFilesView *files_view) { @@ -1644,7 +1635,6 @@ nautilus_list_base_class_init (NautilusListBaseClass *klass) files_view_class->click_policy_changed = real_click_policy_changed; 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->select_all = real_select_all; files_view_class->set_selection = real_set_selection; files_view_class->invert_selection = real_invert_selection; -- GitLab From bdded1353267728bdfdadbd840ce1b330ce284f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Fernandes?= Date: Sat, 9 Sep 2023 16:21:36 +0100 Subject: [PATCH 5/7] files-view: Sort after file changes directly We connect to our own signal, because the default handler is overriden by the subclass. But the subclass simply sorts the model. We don't need to have the subclass do something we can do ourselves. So, convert our connected handler into a default handler and trigger the sorting there. --- src/nautilus-files-view.c | 9 ++++----- src/nautilus-files-view.h | 3 +-- src/nautilus-list-base.c | 10 ---------- 3 files changed, 5 insertions(+), 17 deletions(-) diff --git a/src/nautilus-files-view.c b/src/nautilus-files-view.c index a14f9e9b07..14fb2f801f 100644 --- a/src/nautilus-files-view.c +++ b/src/nautilus-files-view.c @@ -4264,12 +4264,14 @@ process_new_files (NautilusFilesView *view) } static void -on_end_file_changes (NautilusFilesView *view) +real_end_file_changes (NautilusFilesView *view) { NautilusFilesViewPrivate *priv; priv = nautilus_files_view_get_instance_private (view); + nautilus_view_model_sort (priv->model); + /* Addition and removal of files modify the empty state */ nautilus_files_view_check_empty_states (view); update_extend_search_revealer (view); @@ -9770,6 +9772,7 @@ nautilus_files_view_class_init (NautilusFilesViewClass *klass) klass->add_files = real_add_files; klass->remove_files = real_remove_files; klass->file_changed = real_file_changed; + klass->end_file_changes = real_end_file_changes; klass->begin_loading = real_begin_loading; klass->get_backing_uri = real_get_backing_uri; klass->update_context_menus = real_update_context_menus; @@ -9899,10 +9902,6 @@ nautilus_files_view_init (NautilusFilesView *view) priv->toolbar_menu_sections = g_new0 (NautilusToolbarMenuSections, 1); priv->toolbar_menu_sections->sort_section = G_MENU_MODEL (g_object_ref (gtk_builder_get_object (builder, "sort_section"))); - g_signal_connect (view, - "end-file-changes", - G_CALLBACK (on_end_file_changes), - view); g_signal_connect (view, "notify::selection", G_CALLBACK (nautilus_files_view_preview_update), diff --git a/src/nautilus-files-view.h b/src/nautilus-files-view.h index dc30fdcf2a..df7678d4fc 100644 --- a/src/nautilus-files-view.h +++ b/src/nautilus-files-view.h @@ -71,9 +71,8 @@ struct _NautilusFilesViewClass { NautilusDirectory *directory); /* The 'end_file_changes' signal is emitted after a set of files - * are added to the view. It can be replaced by a subclass to do any + * are added to the view. It can be connected to in order to do any * necessary cleanup (typically, cleanup for code in begin_file_changes). - * The default implementation does nothing. */ void (* end_file_changes) (NautilusFilesView *view); diff --git a/src/nautilus-list-base.c b/src/nautilus-list-base.c index 987ca4587e..c0b5e57c3d 100644 --- a/src/nautilus-list-base.c +++ b/src/nautilus-list-base.c @@ -984,15 +984,6 @@ real_get_selection_for_file_transfer (NautilusFilesView *files_view) return get_selection (files_view, TRUE); } -static void -real_end_file_changes (NautilusFilesView *files_view) -{ - NautilusListBase *self = NAUTILUS_LIST_BASE (files_view); - NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); - - nautilus_view_model_sort (priv->model); -} - static void real_set_selection (NautilusFilesView *files_view, GList *selection) @@ -1638,7 +1629,6 @@ nautilus_list_base_class_init (NautilusListBaseClass *klass) files_view_class->select_all = real_select_all; files_view_class->set_selection = real_set_selection; files_view_class->invert_selection = real_invert_selection; - files_view_class->end_file_changes = real_end_file_changes; files_view_class->end_loading = real_end_loading; files_view_class->get_first_visible_file = real_get_first_visible_file; files_view_class->get_last_visible_file = real_get_last_visible_file; -- GitLab From 17193fc3e8bbad023ecb787a5b92b84efe7df2da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Fernandes?= Date: Sat, 9 Sep 2023 16:46:02 +0100 Subject: [PATCH 6/7] list-base: Make scroll method public This is going to be called by NautilusFilesView next commit. --- src/nautilus-list-base-private.h | 3 --- src/nautilus-list-base.c | 10 +++++----- src/nautilus-list-base.h | 4 ++++ src/nautilus-list-view.c | 2 +- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/nautilus-list-base-private.h b/src/nautilus-list-base-private.h index c4901977c9..cbd3f117ba 100644 --- a/src/nautilus-list-base-private.h +++ b/src/nautilus-list-base-private.h @@ -27,7 +27,4 @@ void setup_cell_hover (NautilusViewCel void setup_cell_hover_inner_target (NautilusViewCell *cell, GtkWidget *target); -void set_focus_item (NautilusListBase *self, - NautilusViewItem *item); - G_END_DECLS diff --git a/src/nautilus-list-base.c b/src/nautilus-list-base.c index c0b5e57c3d..5b1ba02f1c 100644 --- a/src/nautilus-list-base.c +++ b/src/nautilus-list-base.c @@ -81,8 +81,8 @@ get_view_item (GListModel *model, } void -set_focus_item (NautilusListBase *self, - NautilusViewItem *item) +nautilus_list_base_set_focus_item (NautilusListBase *self, + NautilusViewItem *item) { NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); GtkWidget *item_widget = nautilus_view_item_get_item_ui (item); @@ -134,7 +134,7 @@ select_single_item_if_not_selected (NautilusListBase *self, if (!gtk_selection_model_is_selected (GTK_SELECTION_MODEL (model), position)) { gtk_selection_model_select_item (GTK_SELECTION_MODEL (model), position, TRUE); - set_focus_item (self, item); + nautilus_list_base_set_focus_item (self, item); } } @@ -1032,7 +1032,7 @@ real_set_selection (NautilusFilesView *files_view, guint first_position = gtk_bitset_get_nth (new_selection_set, 0); first_selected_item = get_view_item (G_LIST_MODEL (priv->model), first_position); - set_focus_item (self, first_selected_item); + nautilus_list_base_set_focus_item (self, first_selected_item); } gtk_bitset_union (update_set, new_selection_set); @@ -1440,7 +1440,7 @@ real_preview_selection_event (NautilusFilesView *files_view, } item = get_view_item (G_LIST_MODEL (priv->model), i); - set_focus_item (self, item); + nautilus_list_base_set_focus_item (self, item); } static void diff --git a/src/nautilus-list-base.h b/src/nautilus-list-base.h index 007ab07a62..6972bfc5ef 100644 --- a/src/nautilus-list-base.h +++ b/src/nautilus-list-base.h @@ -7,6 +7,7 @@ #pragma once #include "nautilus-files-view.h" +#include "nautilus-view-item.h" G_BEGIN_DECLS @@ -24,4 +25,7 @@ struct _NautilusListBaseClass guint position); }; +void nautilus_list_base_set_focus_item (NautilusListBase *self, + NautilusViewItem *item); + G_END_DECLS diff --git a/src/nautilus-list-view.c b/src/nautilus-list-view.c index e4d6b1370a..9584ae35ae 100644 --- a/src/nautilus-list-view.c +++ b/src/nautilus-list-view.c @@ -912,7 +912,7 @@ tree_expander_shortcut_cb (GtkWidget *widget, parent_item = NAUTILUS_VIEW_ITEM (gtk_tree_list_row_get_item (parent)); g_return_val_if_fail (parent_item != NULL, FALSE); - set_focus_item (NAUTILUS_LIST_BASE (self), parent_item); + nautilus_list_base_set_focus_item (NAUTILUS_LIST_BASE (self), parent_item); } } else -- GitLab From 097f29d9d0fa7748ace5ecd8e4fff0e70a41b77e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Fernandes?= Date: Sat, 9 Sep 2023 16:46:56 +0100 Subject: [PATCH 7/7] files-view: Manage selection directly Previous commits moved populating/depopulating the model. With this commit, we move selection setting/getting too. --- src/nautilus-files-view.c | 175 +++++++++++++++++++++++++++++++------ src/nautilus-files-view.h | 36 -------- src/nautilus-list-base.c | 178 -------------------------------------- 3 files changed, 149 insertions(+), 240 deletions(-) diff --git a/src/nautilus-files-view.c b/src/nautilus-files-view.c index 14fb2f801f..75a58279b0 100644 --- a/src/nautilus-files-view.c +++ b/src/nautilus-files-view.c @@ -67,6 +67,7 @@ #include "nautilus-grid-view.h" #include "nautilus-icon-info.h" #include "nautilus-icon-names.h" +#include "nautilus-list-base.h" #include "nautilus-list-view.h" #include "nautilus-metadata.h" #include "nautilus-mime-actions.h" @@ -364,6 +365,16 @@ G_DEFINE_TYPE_WITH_CODE (NautilusFilesView, G_IMPLEMENT_INTERFACE (NAUTILUS_TYPE_VIEW, nautilus_files_view_iface_init) G_ADD_PRIVATE (NautilusFilesView)); +static inline NautilusViewItem * +get_view_item (GListModel *model, + guint position) +{ + g_autoptr (GtkTreeListRow) row = g_list_model_get_item (model, position); + + g_return_val_if_fail (GTK_IS_TREE_LIST_ROW (row), NULL); + return NAUTILUS_VIEW_ITEM (gtk_tree_list_row_get_item (row)); +} + /* * Floating Bar code */ @@ -725,44 +736,168 @@ nautilus_files_view_get_backing_uri (NautilusFilesView *view) * **/ static void -nautilus_files_view_select_all (NautilusFilesView *view) +nautilus_files_view_select_all (NautilusFilesView *self) { - g_return_if_fail (NAUTILUS_IS_FILES_VIEW (view)); + NautilusFilesViewPrivate *priv = nautilus_files_view_get_instance_private (self); - NAUTILUS_FILES_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->select_all (view); + gtk_selection_model_select_all (GTK_SELECTION_MODEL (priv->model)); } static void -nautilus_files_view_select_first (NautilusFilesView *view) +nautilus_files_view_select_first (NautilusFilesView *self) { - g_return_if_fail (NAUTILUS_IS_FILES_VIEW (view)); + NautilusFilesViewPrivate *priv = nautilus_files_view_get_instance_private (self); - NAUTILUS_FILES_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->select_first (view); + gtk_selection_model_select_item (GTK_SELECTION_MODEL (priv->model), 0, TRUE); } static void -nautilus_files_view_call_set_selection (NautilusFilesView *view, +nautilus_files_view_call_set_selection (NautilusFilesView *self, GList *selection) { - g_return_if_fail (NAUTILUS_IS_FILES_VIEW (view)); + NautilusFilesViewPrivate *priv = nautilus_files_view_get_instance_private (self); + g_autoptr (GList) files_to_find = g_list_copy (selection); + g_autoptr (GtkBitset) update_set = NULL; + g_autoptr (GtkBitset) new_selection_set = NULL; + g_autoptr (GtkBitset) old_selection_set = NULL; + guint n_items; + + old_selection_set = gtk_selection_model_get_selection (GTK_SELECTION_MODEL (priv->model)); + /* We aren't allowed to modify the actual selection bitset */ + update_set = gtk_bitset_copy (old_selection_set); + new_selection_set = gtk_bitset_new_empty (); + + /* Convert file list into set of model indices */ + n_items = g_list_model_get_n_items (G_LIST_MODEL (priv->model)); + for (guint position = 0; position < n_items; position++) + { + g_autoptr (NautilusViewItem) item = get_view_item (G_LIST_MODEL (priv->model), position); - NAUTILUS_FILES_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->set_selection (view, selection); + GList *link = g_list_find (files_to_find, nautilus_view_item_get_file (item)); + if (link != NULL) + { + /* Found item to select */ + gtk_bitset_add (new_selection_set, position); + + /* Remove found file from the list of files yet to find. */ + files_to_find = g_list_delete_link (files_to_find, link); + if (files_to_find == NULL) + { + /* We've matched everything... */ + break; + } + } + } + /* ...have we not? */ + g_warn_if_fail (files_to_find == NULL); + + /* Set focus on the first selected row. */ + if (!gtk_bitset_is_empty (new_selection_set)) + { + g_autoptr (NautilusViewItem) first_selected_item = NULL; + guint first_position = gtk_bitset_get_nth (new_selection_set, 0); + + first_selected_item = get_view_item (G_LIST_MODEL (priv->model), first_position); + nautilus_list_base_set_focus_item (NAUTILUS_LIST_BASE (self), first_selected_item); + } + + gtk_bitset_union (update_set, new_selection_set); + gtk_selection_model_set_selection (GTK_SELECTION_MODEL (priv->model), + new_selection_set, + update_set); } +static gboolean +is_ancestor_selected (GtkTreeListRow *row, + GtkBitset *selection) +{ + g_autoptr (GtkTreeListRow) parent = gtk_tree_list_row_get_parent (row); + GtkTreeListRow *grandparent; + + /* Walk up the tree looking for a selected ancestor. */ + while (parent != NULL) + { + guint parent_position = gtk_tree_list_row_get_position (parent); + if (gtk_bitset_contains (selection, parent_position)) + { + return TRUE; + } + + grandparent = gtk_tree_list_row_get_parent (parent); + g_object_unref (parent); + parent = grandparent; + } + + return FALSE; +} + +static GList * +get_selection_internal (NautilusFilesView *self, + gboolean for_file_transfer) +{ + NautilusFilesViewPrivate *priv = nautilus_files_view_get_instance_private (self); + g_autoptr (GtkBitset) selection = NULL; + GtkBitsetIter iter; + guint i; + GList *selected_files = NULL; + + selection = gtk_selection_model_get_selection (GTK_SELECTION_MODEL (priv->model)); + + for (gtk_bitset_iter_init_last (&iter, selection, &i); + gtk_bitset_iter_is_valid (&iter); + gtk_bitset_iter_previous (&iter, &i)) + { + g_autoptr (GtkTreeListRow) row = NULL; + g_autoptr (NautilusViewItem) item = NULL; + NautilusFile *file; + + row = GTK_TREE_LIST_ROW (g_list_model_get_item (G_LIST_MODEL (priv->model), i)); + + if (for_file_transfer && is_ancestor_selected (row, selection)) + { + /* If an ancestor is already selected, don't include its descendants + * in the selection of files to copy/move. */ + continue; + } + + item = NAUTILUS_VIEW_ITEM (gtk_tree_list_row_get_item (row)); + file = nautilus_view_item_get_file (item); + + selected_files = g_list_prepend (selected_files, g_object_ref (file)); + } + + return selected_files; +} + +/* The difference from get_selection() is that any files in the selection that + * also has a parent folder in the selection is not included */ static GList * nautilus_files_view_get_selection_for_file_transfer (NautilusFilesView *view) { g_return_val_if_fail (NAUTILUS_IS_FILES_VIEW (view), NULL); - return NAUTILUS_FILES_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->get_selection_for_file_transfer (view); + return get_selection_internal (NAUTILUS_FILES_VIEW (view), TRUE); } static void -nautilus_files_view_invert_selection (NautilusFilesView *view) +nautilus_files_view_invert_selection (NautilusFilesView *self) { - g_return_if_fail (NAUTILUS_IS_FILES_VIEW (view)); + NautilusFilesViewPrivate *priv = nautilus_files_view_get_instance_private (self); + GtkSelectionModel *selection_model = GTK_SELECTION_MODEL (priv->model); + g_autoptr (GtkBitset) selected = NULL; + g_autoptr (GtkBitset) all = NULL; + g_autoptr (GtkBitset) new_selected = NULL; - NAUTILUS_FILES_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->invert_selection (view); + selected = gtk_selection_model_get_selection (selection_model); + + /* We are going to flip the selection state of every item in the model. */ + all = gtk_bitset_new_range (0, g_list_model_get_n_items (G_LIST_MODEL (priv->model))); + + /* The new selection is all items minus the ones currently selected. */ + new_selected = gtk_bitset_copy (all); + gtk_bitset_subtract (new_selected, selected); + + gtk_selection_model_set_selection (selection_model, new_selected, all); } /** @@ -1061,24 +1196,12 @@ nautilus_files_view_scroll_to_file (NautilusFilesView *view, NAUTILUS_FILES_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->scroll_to_file (view, uri); } -/** - * nautilus_files_view_get_selection: - * - * Get a list of NautilusFile pointers that represents the - * currently-selected items in this view. Subclasses must override - * the signal handler for the 'get_selection' signal. Callers are - * responsible for g_free-ing the list (and unrefing its data). - * @view: NautilusFilesView whose selected items are of interest. - * - * Return value: GList of NautilusFile pointers representing the selection. - * - **/ static GList * nautilus_files_view_get_selection (NautilusView *view) { g_return_val_if_fail (NAUTILUS_IS_FILES_VIEW (view), NULL); - return NAUTILUS_FILES_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->get_selection (NAUTILUS_FILES_VIEW (view)); + return get_selection_internal (NAUTILUS_FILES_VIEW (view), FALSE); } typedef struct diff --git a/src/nautilus-files-view.h b/src/nautilus-files-view.h index df7678d4fc..aafdaf2243 100644 --- a/src/nautilus-files-view.h +++ b/src/nautilus-files-view.h @@ -108,42 +108,6 @@ struct _NautilusFilesViewClass { char * (* get_backing_uri) (NautilusFilesView *view); - /* get_selection is not a signal; it is just a function pointer for - * subclasses to replace (override). Subclasses must replace it - * with a function that returns a newly-allocated GList of - * NautilusFile pointers. - */ - GList * (* get_selection) (NautilusFilesView *view); - - /* get_selection_for_file_transfer is a function pointer for - * subclasses to replace (override). Subclasses must replace it - * with a function that returns a newly-allocated GList of - * NautilusFile pointers. The difference from get_selection is - * that any files in the selection that also has a parent folder - * in the selection is not included. - */ - GList * (* get_selection_for_file_transfer)(NautilusFilesView *view); - - /* select_all is a function pointer that subclasses must override to - * select all of the items in the view */ - void (* select_all) (NautilusFilesView *view); - - /* select_first is a function pointer that subclasses must override to - * select the first item in the view */ - void (* select_first) (NautilusFilesView *view); - - /* set_selection is a function pointer that subclasses must - * override to select the specified items (and unselect all - * others). The argument is a list of NautilusFiles. */ - - void (* set_selection) (NautilusFilesView *view, - GList *selection); - - /* invert_selection is a function pointer that subclasses must - * override to invert selection. */ - - void (* invert_selection) (NautilusFilesView *view); - /* bump_zoom_level is a function pointer that subclasses must override * to change the zoom level of an object. */ void (* bump_zoom_level) (NautilusFilesView *view, diff --git a/src/nautilus-list-base.c b/src/nautilus-list-base.c index 5b1ba02f1c..d6343ae7fe 100644 --- a/src/nautilus-list-base.c +++ b/src/nautilus-list-base.c @@ -909,169 +909,6 @@ real_click_policy_changed (NautilusFilesView *files_view) set_click_mode_from_settings (NAUTILUS_LIST_BASE (files_view)); } -static gboolean -is_ancestor_selected (GtkTreeListRow *row, - GtkBitset *selection) -{ - g_autoptr (GtkTreeListRow) parent = gtk_tree_list_row_get_parent (row); - GtkTreeListRow *grandparent; - - /* Walk up the tree looking for a selected ancestor. */ - while (parent != NULL) - { - guint parent_position = gtk_tree_list_row_get_position (parent); - if (gtk_bitset_contains (selection, parent_position)) - { - return TRUE; - } - - grandparent = gtk_tree_list_row_get_parent (parent); - g_object_unref (parent); - parent = grandparent; - } - - return FALSE; -} - -static GList * -get_selection (NautilusFilesView *files_view, - gboolean for_file_transfer) -{ - NautilusListBase *self = NAUTILUS_LIST_BASE (files_view); - NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); - g_autoptr (GtkBitset) selection = NULL; - GtkBitsetIter iter; - guint i; - GList *selected_files = NULL; - - selection = gtk_selection_model_get_selection (GTK_SELECTION_MODEL (priv->model)); - - for (gtk_bitset_iter_init_last (&iter, selection, &i); - gtk_bitset_iter_is_valid (&iter); - gtk_bitset_iter_previous (&iter, &i)) - { - g_autoptr (GtkTreeListRow) row = NULL; - g_autoptr (NautilusViewItem) item = NULL; - NautilusFile *file; - - row = GTK_TREE_LIST_ROW (g_list_model_get_item (G_LIST_MODEL (priv->model), i)); - - if (for_file_transfer && is_ancestor_selected (row, selection)) - { - /* If an ancestor is already selected, don't include its descendants - * in the selection of files to copy/move. */ - continue; - } - - item = NAUTILUS_VIEW_ITEM (gtk_tree_list_row_get_item (row)); - file = nautilus_view_item_get_file (item); - - selected_files = g_list_prepend (selected_files, g_object_ref (file)); - } - - return selected_files; -} - -static GList * -real_get_selection (NautilusFilesView *files_view) -{ - return get_selection (files_view, FALSE); -} - -static GList * -real_get_selection_for_file_transfer (NautilusFilesView *files_view) -{ - return get_selection (files_view, TRUE); -} - -static void -real_set_selection (NautilusFilesView *files_view, - GList *selection) -{ - NautilusListBase *self = NAUTILUS_LIST_BASE (files_view); - NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); - g_autoptr (GList) files_to_find = g_list_copy (selection); - g_autoptr (GtkBitset) update_set = NULL; - g_autoptr (GtkBitset) new_selection_set = NULL; - g_autoptr (GtkBitset) old_selection_set = NULL; - guint n_items; - - old_selection_set = gtk_selection_model_get_selection (GTK_SELECTION_MODEL (priv->model)); - /* We aren't allowed to modify the actual selection bitset */ - update_set = gtk_bitset_copy (old_selection_set); - new_selection_set = gtk_bitset_new_empty (); - - /* Convert file list into set of model indices */ - n_items = g_list_model_get_n_items (G_LIST_MODEL (priv->model)); - for (guint position = 0; position < n_items; position++) - { - g_autoptr (NautilusViewItem) item = get_view_item (G_LIST_MODEL (priv->model), position); - - GList *link = g_list_find (files_to_find, nautilus_view_item_get_file (item)); - if (link != NULL) - { - /* Found item to select */ - gtk_bitset_add (new_selection_set, position); - - /* Remove found file from the list of files yet to find. */ - files_to_find = g_list_delete_link (files_to_find, link); - if (files_to_find == NULL) - { - /* We've matched everything... */ - break; - } - } - } - /* ...have we not? */ - g_warn_if_fail (files_to_find == NULL); - - /* Set focus on the first selected row. */ - if (!gtk_bitset_is_empty (new_selection_set)) - { - g_autoptr (NautilusViewItem) first_selected_item = NULL; - guint first_position = gtk_bitset_get_nth (new_selection_set, 0); - - first_selected_item = get_view_item (G_LIST_MODEL (priv->model), first_position); - nautilus_list_base_set_focus_item (self, first_selected_item); - } - - gtk_bitset_union (update_set, new_selection_set); - gtk_selection_model_set_selection (GTK_SELECTION_MODEL (priv->model), - new_selection_set, - update_set); -} - -static void -real_select_all (NautilusFilesView *files_view) -{ - NautilusListBase *self = NAUTILUS_LIST_BASE (files_view); - NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); - - gtk_selection_model_select_all (GTK_SELECTION_MODEL (priv->model)); -} - -static void -real_invert_selection (NautilusFilesView *files_view) -{ - NautilusListBase *self = NAUTILUS_LIST_BASE (files_view); - NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); - GtkSelectionModel *selection_model = GTK_SELECTION_MODEL (priv->model); - g_autoptr (GtkBitset) selected = NULL; - g_autoptr (GtkBitset) all = NULL; - g_autoptr (GtkBitset) new_selected = NULL; - - selected = gtk_selection_model_get_selection (selection_model); - - /* We are going to flip the selection state of every item in the model. */ - all = gtk_bitset_new_range (0, g_list_model_get_n_items (G_LIST_MODEL (priv->model))); - - /* The new selection is all items minus the ones currently selected. */ - new_selected = gtk_bitset_copy (all); - gtk_bitset_subtract (new_selected, selected); - - gtk_selection_model_set_selection (selection_model, new_selected, all); -} - static guint get_first_selected_item (NautilusListBase *self) { @@ -1323,15 +1160,6 @@ real_scroll_to_file (NautilusFilesView *files_view, nautilus_list_base_scroll_to_item (self, i); } -static void -real_select_first (NautilusFilesView *files_view) -{ - NautilusListBase *self = NAUTILUS_LIST_BASE (files_view); - NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); - - gtk_selection_model_select_item (GTK_SELECTION_MODEL (priv->model), 0, TRUE); -} - static GdkRectangle * get_rectangle_for_item_ui (NautilusListBase *self, GtkWidget *item_ui) @@ -1624,17 +1452,11 @@ nautilus_list_base_class_init (NautilusListBaseClass *klass) files_view_class->begin_loading = real_begin_loading; files_view_class->click_policy_changed = real_click_policy_changed; - 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->select_all = real_select_all; - files_view_class->set_selection = real_set_selection; - files_view_class->invert_selection = real_invert_selection; files_view_class->end_loading = real_end_loading; files_view_class->get_first_visible_file = real_get_first_visible_file; files_view_class->get_last_visible_file = real_get_last_visible_file; files_view_class->reveal_selection = real_reveal_selection; files_view_class->scroll_to_file = real_scroll_to_file; - files_view_class->select_first = real_select_first; files_view_class->compute_rename_popover_pointing_to = real_compute_rename_popover_pointing_to; files_view_class->reveal_for_selection_context_menu = real_reveal_for_selection_context_menu; files_view_class->preview_selection_event = real_preview_selection_event; -- GitLab