[nautilus/wip/antoniof/new-list-view-continuation: 76/86] view-icon-controller: Abstract sharable code




commit 0a1618c5ca58af592530cf1e6a07c04ac5d89384
Author: António Fernandes <antoniof gnome org>
Date:   Fri Apr 8 23:27:43 2022 +0100

    view-icon-controller: Abstract sharable code
    
    The new list view is going to be GtkColumnView-based, so it's going to
    share some code with the GtkGridView-based view.

 src/meson.build                     |    2 +
 src/nautilus-files-model-view.c     |  999 ++++++++++++++++++++++++++++++++
 src/nautilus-files-model-view.h     |   44 ++
 src/nautilus-view-icon-controller.c | 1075 +++--------------------------------
 src/nautilus-view-icon-controller.h |    5 +-
 src/nautilus-view-model.c           |   14 +-
 src/nautilus-view-model.h           |    1 +
 7 files changed, 1147 insertions(+), 993 deletions(-)
---
diff --git a/src/meson.build b/src/meson.build
index 35a0dce98..141a20ba4 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -75,6 +75,8 @@ libnautilus_sources = [
   'nautilus-error-reporting.h',
   'nautilus-preferences-window.c',
   'nautilus-preferences-window.h',
+  'nautilus-files-model-view.c',
+  'nautilus-files-model-view.h',
   'nautilus-files-view.c',
   'nautilus-files-view.h',
   'nautilus-files-view-dnd.c',
diff --git a/src/nautilus-files-model-view.c b/src/nautilus-files-model-view.c
new file mode 100644
index 000000000..869a389a3
--- /dev/null
+++ b/src/nautilus-files-model-view.c
@@ -0,0 +1,999 @@
+#include "nautilus-clipboard.h"
+#include "nautilus-files-model-view.h"
+#include "nautilus-view-item-model.h"
+#include "nautilus-view-model.h"
+#include "nautilus-files-view.h"
+#include "nautilus-file.h"
+#include "nautilus-metadata.h"
+#include "nautilus-global-preferences.h"
+#include "nautilus-thumbnails.h"
+
+typedef struct _NautilusFilesModelViewPrivate NautilusFilesModelViewPrivate;
+struct _NautilusFilesModelViewPrivate
+{
+    NautilusViewModel *model;
+
+    GList *cut_files;
+
+    guint scroll_to_file_handle_id;
+    guint prioritize_thumbnailing_handle_id;
+    GtkAdjustment *vadjustment;
+};
+
+G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (NautilusFilesModelView, nautilus_files_model_view, 
NAUTILUS_TYPE_FILES_VIEW)
+
+typedef struct
+{
+    const NautilusFileSortType sort_type;
+    const gchar *metadata_name;
+} SortConstants;
+
+static const SortConstants sorts_constants[] =
+{
+    {
+        NAUTILUS_FILE_SORT_BY_DISPLAY_NAME,
+        "name",
+    },
+    {
+        NAUTILUS_FILE_SORT_BY_SIZE,
+        "size",
+    },
+    {
+        NAUTILUS_FILE_SORT_BY_TYPE,
+        "type",
+    },
+    {
+        NAUTILUS_FILE_SORT_BY_MTIME,
+        "modification date",
+    },
+    {
+        NAUTILUS_FILE_SORT_BY_ATIME,
+        "access date",
+    },
+    {
+        NAUTILUS_FILE_SORT_BY_BTIME,
+        "creation date",
+    },
+    {
+        NAUTILUS_FILE_SORT_BY_TRASHED_TIME,
+        "trashed",
+    },
+    {
+        NAUTILUS_FILE_SORT_BY_SEARCH_RELEVANCE,
+        "search_relevance",
+    },
+    {
+        NAUTILUS_FILE_SORT_BY_RECENCY,
+        "recency",
+    },
+};
+
+static inline NautilusViewItemModel *
+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_MODEL (gtk_tree_list_row_get_item (GTK_TREE_LIST_ROW (row)));
+}
+
+static const SortConstants *
+get_sorts_constants_from_sort_type (NautilusFileSortType sort_type)
+{
+    guint i;
+
+    for (i = 0; i < G_N_ELEMENTS (sorts_constants); i++)
+    {
+        if (sort_type == sorts_constants[i].sort_type)
+        {
+            return &sorts_constants[i];
+        }
+    }
+
+    return &sorts_constants[0];
+}
+
+static const SortConstants *
+get_sorts_constants_from_metadata_text (const char *metadata_name)
+{
+    guint i;
+
+    for (i = 0; i < G_N_ELEMENTS (sorts_constants); i++)
+    {
+        if (g_strcmp0 (sorts_constants[i].metadata_name, metadata_name) == 0)
+        {
+            return &sorts_constants[i];
+        }
+    }
+
+    return &sorts_constants[0];
+}
+
+const NautilusFileSortType
+get_sorts_type_from_metadata_text (const char *metadata_name)
+{
+    return get_sorts_constants_from_metadata_text (metadata_name)->sort_type;
+}
+
+static const SortConstants *
+get_default_sort_order (NautilusFile *file,
+                        gboolean     *reversed)
+{
+    NautilusFileSortType sort_type;
+
+    sort_type = nautilus_file_get_default_sort_type (file, reversed);
+
+    return get_sorts_constants_from_sort_type (sort_type);
+}
+
+static const SortConstants *
+get_directory_sort_by (NautilusFile *file,
+                       gboolean     *reversed)
+{
+    const SortConstants *default_sort;
+    g_autofree char *sort_by = NULL;
+
+    default_sort = get_default_sort_order (file, reversed);
+    g_return_val_if_fail (default_sort != NULL, NULL);
+
+    sort_by = nautilus_file_get_metadata (file,
+                                          NAUTILUS_METADATA_KEY_ICON_VIEW_SORT_BY,
+                                          default_sort->metadata_name);
+
+    *reversed = nautilus_file_get_boolean_metadata (file,
+                                                    NAUTILUS_METADATA_KEY_ICON_VIEW_SORT_REVERSED,
+                                                    *reversed);
+
+    return get_sorts_constants_from_metadata_text (sort_by);
+}
+
+void
+set_directory_sort_metadata (NautilusFile *file,
+                             const gchar  *metadata_name,
+                             gboolean      reversed)
+{
+    const SortConstants *default_sort;
+    gboolean default_reversed;
+
+    default_sort = get_default_sort_order (file, &default_reversed);
+
+    nautilus_file_set_metadata (file,
+                                NAUTILUS_METADATA_KEY_ICON_VIEW_SORT_BY,
+                                default_sort->metadata_name,
+                                metadata_name);
+    nautilus_file_set_boolean_metadata (file,
+                                        NAUTILUS_METADATA_KEY_ICON_VIEW_SORT_REVERSED,
+                                        default_reversed,
+                                        reversed);
+}
+
+static void
+update_sort_order_from_metadata_and_preferences (NautilusFilesModelView *self)
+{
+    const SortConstants *default_directory_sort;
+    GActionGroup *view_action_group;
+    gboolean reversed;
+
+    default_directory_sort = get_directory_sort_by (nautilus_files_view_get_directory_as_file 
(NAUTILUS_FILES_VIEW (self)),
+                                                    &reversed);
+    view_action_group = nautilus_files_view_get_action_group (NAUTILUS_FILES_VIEW (self));
+    g_action_group_change_action_state (view_action_group,
+                                        "sort",
+                                        g_variant_new ("(sb)",
+                                                       default_directory_sort->metadata_name,
+                                                       reversed));
+}
+
+void
+nautilus_files_model_view_set_icon_size (NautilusFilesModelView *self,
+                                         gint                    icon_size)
+{
+    GListModel *model;
+    guint n_items;
+
+    model = G_LIST_MODEL (nautilus_files_model_view_get_model (NAUTILUS_FILES_MODEL_VIEW (self)));
+
+    n_items = g_list_model_get_n_items (model);
+    for (guint i = 0; i < n_items; i++)
+    {
+        g_autoptr (NautilusViewItemModel) current_item_model = NULL;
+
+        current_item_model = get_view_item (model, i);
+        nautilus_view_item_model_set_icon_size (current_item_model, icon_size);
+    }
+}
+
+static void
+nautilus_files_model_view_scroll_to_item (NautilusFilesModelView *self,
+                                          guint                   position)
+{
+    NAUTILUS_FILES_MODEL_VIEW_CLASS (G_OBJECT_GET_CLASS (self))->scroll_to_item (self, position);
+}
+
+static guint
+nautilus_files_model_view_get_icon_size (NautilusFilesModelView *self)
+{
+    return NAUTILUS_FILES_MODEL_VIEW_CLASS (G_OBJECT_GET_CLASS (self))->get_icon_size (self);
+}
+
+static GtkWidget *
+nautilus_files_model_view_get_view_ui (NautilusFilesModelView *self)
+{
+    return NAUTILUS_FILES_MODEL_VIEW_CLASS (G_OBJECT_GET_CLASS (self))->get_view_ui (self);
+}
+
+typedef struct
+{
+    NautilusFilesModelView *self;
+    GQuark attribute_q;
+} NautilusFilesModelViewSortData;
+
+static void
+real_begin_loading (NautilusFilesView *files_view)
+{
+    /*TODO move this to the files view class begin_loading and hook up? */
+
+    /* We could have changed to the trash directory or to searching, and then
+     * we need to update the menus */
+    nautilus_files_view_update_context_menus (files_view);
+    nautilus_files_view_update_toolbar_menus (files_view);
+}
+
+static void
+real_clear (NautilusFilesView *files_view)
+{
+    NautilusFilesModelView *self = NAUTILUS_FILES_MODEL_VIEW (files_view);
+    NautilusFilesModelViewPrivate *priv = nautilus_files_model_view_get_instance_private (self);
+
+    nautilus_view_model_remove_all_items (priv->model);
+}
+
+static void
+real_file_changed (NautilusFilesView *files_view,
+                   NautilusFile      *file,
+                   NautilusDirectory *directory)
+{
+    NautilusFilesModelView *self = NAUTILUS_FILES_MODEL_VIEW (files_view);
+    NautilusFilesModelViewPrivate *priv = nautilus_files_model_view_get_instance_private (self);
+    NautilusViewItemModel *item_model;
+
+    item_model = nautilus_view_model_get_item_from_file (priv->model, file);
+    nautilus_view_item_model_file_changed (item_model);
+}
+
+static GList *
+real_get_selection (NautilusFilesView *files_view)
+{
+    NautilusFilesModelView *self = NAUTILUS_FILES_MODEL_VIEW (files_view);
+    NautilusFilesModelViewPrivate *priv = nautilus_files_model_view_get_instance_private (self);
+    g_autoptr (GtkSelectionFilterModel) selection = NULL;
+    guint n_selected;
+    GList *selected_files = NULL;
+
+    selection = gtk_selection_filter_model_new (GTK_SELECTION_MODEL (priv->model));
+    n_selected = g_list_model_get_n_items (G_LIST_MODEL (selection));
+    for (guint i = 0; i < n_selected; i++)
+    {
+        g_autoptr (NautilusViewItemModel) item_model = NULL;
+
+        item_model = get_view_item (G_LIST_MODEL (selection), i);
+        selected_files = g_list_prepend (selected_files,
+                                         g_object_ref (nautilus_view_item_model_get_file (item_model)));
+    }
+
+    return selected_files;
+}
+
+static gboolean
+real_is_empty (NautilusFilesView *files_view)
+{
+    NautilusFilesModelView *self = NAUTILUS_FILES_MODEL_VIEW (files_view);
+    NautilusFilesModelViewPrivate *priv = nautilus_files_model_view_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)
+{
+}
+
+static void
+real_remove_file (NautilusFilesView *files_view,
+                  NautilusFile      *file,
+                  NautilusDirectory *directory)
+{
+    NautilusFilesModelView *self = NAUTILUS_FILES_MODEL_VIEW (files_view);
+    NautilusFilesModelViewPrivate *priv = nautilus_files_model_view_get_instance_private (self);
+    NautilusViewItemModel *item_model;
+
+    item_model = nautilus_view_model_get_item_from_file (priv->model, file);
+    if (item_model != NULL)
+    {
+        nautilus_view_model_remove_item (priv->model, item_model);
+    }
+}
+
+static GQueue *
+convert_glist_to_queue (GList *list)
+{
+    GList *l;
+    GQueue *queue;
+
+    queue = g_queue_new ();
+    for (l = list; l != NULL; l = l->next)
+    {
+        g_queue_push_tail (queue, l->data);
+    }
+
+    return queue;
+}
+
+static GQueue *
+convert_files_to_item_models (NautilusFilesModelView *self,
+                              GQueue                 *files)
+{
+    GList *l;
+    GQueue *models;
+
+    models = g_queue_new ();
+    for (l = g_queue_peek_head_link (files); l != NULL; l = l->next)
+    {
+        NautilusViewItemModel *item_model;
+
+        item_model = nautilus_view_item_model_new (NAUTILUS_FILE (l->data),
+                                                   nautilus_files_model_view_get_icon_size (self));
+        g_queue_push_tail (models, item_model);
+    }
+
+    return models;
+}
+
+static void
+real_set_selection (NautilusFilesView *files_view,
+                    GList             *selection)
+{
+    NautilusFilesModelView *self = NAUTILUS_FILES_MODEL_VIEW (files_view);
+    NautilusFilesModelViewPrivate *priv = nautilus_files_model_view_get_instance_private (self);
+    g_autoptr (GQueue) selection_files = NULL;
+    g_autoptr (GQueue) selection_item_models = NULL;
+    g_autoptr (GtkBitset) update_set = NULL;
+    g_autoptr (GtkBitset) selection_set = NULL;
+
+    update_set = gtk_selection_model_get_selection (GTK_SELECTION_MODEL (priv->model));
+    selection_set = gtk_bitset_new_empty ();
+
+    /* Convert file list into set of model indices */
+    selection_files = convert_glist_to_queue (selection);
+    selection_item_models = nautilus_view_model_get_items_from_files (priv->model, selection_files);
+    for (GList *l = g_queue_peek_head_link (selection_item_models); l != NULL; l = l->next)
+    {
+        gtk_bitset_add (selection_set,
+                        nautilus_view_model_get_index (priv->model, l->data));
+    }
+
+    gtk_bitset_union (update_set, selection_set);
+    gtk_selection_model_set_selection (GTK_SELECTION_MODEL (priv->model),
+                                       selection_set,
+                                       update_set);
+}
+
+static void
+real_select_all (NautilusFilesView *files_view)
+{
+    NautilusFilesModelView *self = NAUTILUS_FILES_MODEL_VIEW (files_view);
+    NautilusFilesModelViewPrivate *priv = nautilus_files_model_view_get_instance_private (self);
+
+    gtk_selection_model_select_all (GTK_SELECTION_MODEL (priv->model));
+}
+
+static void
+real_invert_selection (NautilusFilesView *files_view)
+{
+    NautilusFilesModelView *self = NAUTILUS_FILES_MODEL_VIEW (files_view);
+    NautilusFilesModelViewPrivate *priv = nautilus_files_model_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;
+
+    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 (NautilusFilesModelView *self)
+{
+    NautilusFilesModelViewPrivate *priv = nautilus_files_model_view_get_instance_private (self);
+    g_autolist (NautilusFile) selection = NULL;
+    NautilusFile *file;
+    NautilusViewItemModel *item_model;
+
+    selection = nautilus_view_get_selection (NAUTILUS_VIEW (self));
+    if (selection == NULL)
+    {
+        return G_MAXUINT;
+    }
+
+    file = NAUTILUS_FILE (selection->data);
+    item_model = nautilus_view_model_get_item_from_file (priv->model, file);
+
+    return nautilus_view_model_get_index (priv->model, item_model);
+}
+
+static void
+real_reveal_selection (NautilusFilesView *files_view)
+{
+    NautilusFilesModelView *self = NAUTILUS_FILES_MODEL_VIEW (files_view);
+
+    nautilus_files_model_view_scroll_to_item (self, get_first_selected_item (self));
+}
+
+static gboolean
+showing_recent_directory (NautilusFilesView *view)
+{
+    NautilusFile *file;
+
+    file = nautilus_files_view_get_directory_as_file (view);
+    if (file != NULL)
+    {
+        return nautilus_file_is_in_recent (file);
+    }
+    return FALSE;
+}
+
+static gboolean
+showing_search_directory (NautilusFilesView *view)
+{
+    NautilusFile *file;
+
+    file = nautilus_files_view_get_directory_as_file (view);
+    if (file != NULL)
+    {
+        return nautilus_file_is_in_search (file);
+    }
+    return FALSE;
+}
+
+static void
+real_update_actions_state (NautilusFilesView *files_view)
+{
+    GAction *action;
+    GActionGroup *view_action_group;
+
+    NAUTILUS_FILES_VIEW_CLASS (nautilus_files_model_view_parent_class)->update_actions_state (files_view);
+
+    view_action_group = nautilus_files_view_get_action_group (files_view);
+    action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group), "sort");
+    g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
+                                 !showing_recent_directory (files_view) &&
+                                 !showing_search_directory (files_view));
+}
+
+
+static int
+real_compare_files (NautilusFilesView *files_view,
+                    NautilusFile      *file1,
+                    NautilusFile      *file2)
+{
+    NautilusFilesModelView *self = NAUTILUS_FILES_MODEL_VIEW (files_view);
+    NautilusFilesModelViewPrivate *priv = nautilus_files_model_view_get_instance_private (self);
+    GtkSorter *sorter;
+    g_autoptr (NautilusViewItemModel) item1 = NULL;
+    g_autoptr (NautilusViewItemModel) item2 = NULL;
+
+    sorter = nautilus_view_model_get_sorter (priv->model);
+    if (sorter == NULL)
+    {
+        return 0;
+    }
+
+    /* Generate fake model items for sorter use only. */
+    item1 = nautilus_view_item_model_new (file1, NAUTILUS_LIST_ICON_SIZE_SMALL);
+    item2 = nautilus_view_item_model_new (file2, NAUTILUS_LIST_ICON_SIZE_SMALL);
+
+    return gtk_sorter_compare (sorter, item1, item2);
+}
+
+static void
+on_clipboard_contents_received (GObject      *source_object,
+                                GAsyncResult *res,
+                                gpointer      user_data)
+{
+    NautilusFilesView *files_view = NAUTILUS_FILES_VIEW (source_object);
+    NautilusFilesModelView *self = NAUTILUS_FILES_MODEL_VIEW (files_view);
+    NautilusFilesModelViewPrivate *priv = nautilus_files_model_view_get_instance_private (self);
+    NautilusClipboard *clip;
+    NautilusViewItemModel *item;
+
+    for (GList *l = priv->cut_files; l != NULL; l = l->next)
+    {
+        item = nautilus_view_model_get_item_from_file (priv->model, l->data);
+        if (item != NULL)
+        {
+            nautilus_view_item_model_set_cut (item, FALSE);
+        }
+    }
+    g_clear_list (&priv->cut_files, g_object_unref);
+
+    clip = nautilus_files_view_get_clipboard_finish (files_view, res, NULL);
+    if (clip != NULL && nautilus_clipboard_is_cut (clip))
+    {
+        priv->cut_files = g_list_copy_deep (nautilus_clipboard_peek_files (clip),
+                                            (GCopyFunc) g_object_ref,
+                                            NULL);
+    }
+
+    for (GList *l = priv->cut_files; l != NULL; l = l->next)
+    {
+        item = nautilus_view_model_get_item_from_file (priv->model, l->data);
+        if (item != NULL)
+        {
+            nautilus_view_item_model_set_cut (item, TRUE);
+        }
+    }
+}
+
+static void
+update_clipboard_status (NautilusFilesView *view)
+{
+    nautilus_files_view_get_clipboard_async (view,
+                                             on_clipboard_contents_received,
+                                             NULL);
+}
+
+static void
+on_clipboard_owner_changed (GdkClipboard *clipboard,
+                            gpointer      user_data)
+{
+    update_clipboard_status (NAUTILUS_FILES_VIEW (user_data));
+}
+
+static void
+real_end_loading (NautilusFilesView *files_view,
+                  gboolean           all_files_seen)
+{
+    update_clipboard_status (files_view);
+}
+
+static guint
+get_first_visible_item (NautilusFilesModelView *self)
+{
+    NautilusFilesModelViewPrivate *priv = nautilus_files_model_view_get_instance_private (self);
+    guint n_items;
+    gdouble scrolled_y;
+    GtkWidget *view_ui;
+
+    n_items = g_list_model_get_n_items (G_LIST_MODEL (priv->model));
+    scrolled_y = gtk_adjustment_get_value (priv->vadjustment);
+    view_ui = nautilus_files_model_view_get_view_ui (self);
+    for (guint i = 0; i < n_items; i++)
+    {
+        g_autoptr (NautilusViewItemModel) item = NULL;
+        GtkWidget *item_ui;
+
+        item = get_view_item (G_LIST_MODEL (priv->model), i);
+        item_ui = nautilus_view_item_model_get_item_ui (item);
+        if (item_ui != NULL)
+        {
+            gdouble y;
+
+            gtk_widget_translate_coordinates (item_ui, view_ui,
+                                              0, 0, NULL, &y);
+            if (gtk_widget_is_visible (item_ui) && y >= scrolled_y)
+            {
+                return i;
+            }
+        }
+    }
+
+    return G_MAXUINT;
+}
+
+static char *
+real_get_first_visible_file (NautilusFilesView *files_view)
+{
+    NautilusFilesModelView *self = NAUTILUS_FILES_MODEL_VIEW (files_view);
+    NautilusFilesModelViewPrivate *priv = nautilus_files_model_view_get_instance_private (self);
+    guint i;
+    g_autoptr (NautilusViewItemModel) item = NULL;
+    gchar *uri = NULL;
+
+    i = get_first_visible_item (self);
+    if (i < G_MAXUINT)
+    {
+        item = get_view_item (G_LIST_MODEL (priv->model), i);
+        uri = nautilus_file_get_uri (nautilus_view_item_model_get_file (item));
+    }
+    return uri;
+}
+
+typedef struct
+{
+    NautilusFilesModelView *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)
+{
+    NautilusFilesModelView *self = data->view;
+    NautilusFilesModelViewPrivate *priv = nautilus_files_model_view_get_instance_private (self);
+    g_autoptr (NautilusFile) file = NULL;
+    NautilusViewItemModel *item;
+    guint i;
+
+    file = nautilus_file_get_existing_by_uri (data->uri);
+    item = nautilus_view_model_get_item_from_file (priv->model, file);
+    i = nautilus_view_model_get_index (priv->model, item);
+
+    nautilus_files_model_view_scroll_to_item (self, i);
+
+    priv->scroll_to_file_handle_id = 0;
+    return G_SOURCE_REMOVE;
+}
+
+static void
+real_scroll_to_file (NautilusFilesView *files_view,
+                     const char        *uri)
+{
+    NautilusFilesModelView *self = NAUTILUS_FILES_MODEL_VIEW (files_view);
+    NautilusFilesModelViewPrivate *priv = nautilus_files_model_view_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
+real_add_files (NautilusFilesView *files_view,
+                GList             *files)
+{
+    NautilusFilesModelView *self = NAUTILUS_FILES_MODEL_VIEW (files_view);
+    NautilusFilesModelViewPrivate *priv = nautilus_files_model_view_get_instance_private (self);
+    g_autoptr (GQueue) files_queue = NULL;
+    g_autoptr (GQueue) item_models = NULL;
+    gdouble adjustment_value;
+
+    files_queue = convert_glist_to_queue (files);
+    item_models = convert_files_to_item_models (self, files_queue);
+    nautilus_view_model_add_items (priv->model, item_models);
+
+    /* GtkListBase anchoring doesn't cope well with our lazy loading.
+     * Assuming that GtkListBase|list.scroll-to-item resets the anchor to 0, use
+     * that as a workaround to prevent scrolling while we are at the top. */
+    adjustment_value = gtk_adjustment_get_value (priv->vadjustment);
+    if (G_APPROX_VALUE (adjustment_value, 0.0, DBL_EPSILON))
+    {
+        nautilus_files_model_view_scroll_to_item (self, 0);
+    }
+}
+
+static void
+real_select_first (NautilusFilesView *files_view)
+{
+    NautilusFilesModelView *self = NAUTILUS_FILES_MODEL_VIEW (files_view);
+    NautilusFilesModelViewPrivate *priv = nautilus_files_model_view_get_instance_private (self);
+    g_autoptr (NautilusViewItemModel) item = NULL;
+    NautilusFile *file;
+    g_autoptr (GList) selection = NULL;
+
+    item = get_view_item (G_LIST_MODEL (priv->model), 0);
+    if (item == NULL)
+    {
+        return;
+    }
+    file = nautilus_view_item_model_get_file (item);
+    selection = g_list_prepend (selection, file);
+    nautilus_view_set_selection (NAUTILUS_VIEW (files_view), selection);
+}
+
+static GdkRectangle *
+get_rectangle_for_item_ui (NautilusFilesModelView *self,
+                           GtkWidget              *item_ui)
+{
+    GdkRectangle *rectangle;
+    GtkWidget *content_widget;
+    gdouble view_x;
+    gdouble view_y;
+
+    rectangle = g_new0 (GdkRectangle, 1);
+    gtk_widget_get_allocation (item_ui, rectangle);
+
+    content_widget = nautilus_files_view_get_content_widget (NAUTILUS_FILES_VIEW (self));
+    gtk_widget_translate_coordinates (item_ui, content_widget,
+                                      rectangle->x, rectangle->y,
+                                      &view_x, &view_y);
+    rectangle->x = view_x;
+    rectangle->y = view_y;
+
+    return rectangle;
+}
+
+static GdkRectangle *
+real_compute_rename_popover_pointing_to (NautilusFilesView *files_view)
+{
+    NautilusFilesModelView *self = NAUTILUS_FILES_MODEL_VIEW (files_view);
+    NautilusFilesModelViewPrivate *priv = nautilus_files_model_view_get_instance_private (self);
+    g_autoptr (NautilusViewItemModel) item = NULL;
+    GtkWidget *item_ui;
+
+    /* We only allow one item to be renamed with a popover */
+    item = get_view_item (G_LIST_MODEL (priv->model), get_first_selected_item (self));
+    item_ui = nautilus_view_item_model_get_item_ui (item);
+    g_return_val_if_fail (item_ui != NULL, NULL);
+
+    return get_rectangle_for_item_ui (self, item_ui);
+}
+
+static GdkRectangle *
+real_reveal_for_selection_context_menu (NautilusFilesView *files_view)
+{
+    NautilusFilesModelView *self = NAUTILUS_FILES_MODEL_VIEW (files_view);
+    NautilusFilesModelViewPrivate *priv = nautilus_files_model_view_get_instance_private (self);
+    g_autoptr (GtkSelectionFilterModel) selection = NULL;
+    guint n_selected;
+    GtkWidget *focus_child;
+    guint i;
+    GtkWidget *item_ui;
+
+    selection = gtk_selection_filter_model_new (GTK_SELECTION_MODEL (priv->model));
+    n_selected = g_list_model_get_n_items (G_LIST_MODEL (selection));
+    g_return_val_if_fail (n_selected > 0, NULL);
+
+    /* Get the focused item_ui, if selected.
+     * Otherwise, get the selected item_ui which is sorted the lowest.*/
+    focus_child = gtk_widget_get_focus_child (nautilus_files_model_view_get_view_ui (self));
+    for (i = 0; i < n_selected; i++)
+    {
+        g_autoptr (NautilusViewItemModel) item = NULL;
+
+        item = get_view_item (G_LIST_MODEL (selection), i);
+        item_ui = nautilus_view_item_model_get_item_ui (item);
+        if (item_ui != NULL && gtk_widget_get_parent (item_ui) == focus_child)
+        {
+            break;
+        }
+    }
+    nautilus_files_model_view_scroll_to_item (self, i);
+
+    return get_rectangle_for_item_ui (self, item_ui);
+}
+
+static void
+real_preview_selection_event (NautilusFilesView *files_view,
+                              GtkDirectionType   direction)
+{
+    NautilusFilesModelView *self = NAUTILUS_FILES_MODEL_VIEW (files_view);
+    GtkMovementStep step;
+    gint count;
+    gboolean handled;
+
+    step = (direction == GTK_DIR_UP || direction == GTK_DIR_DOWN) ?
+           GTK_MOVEMENT_DISPLAY_LINES : GTK_MOVEMENT_VISUAL_POSITIONS;
+    count = (direction == GTK_DIR_RIGHT || direction == GTK_DIR_DOWN) ?
+            1 : -1;
+
+    g_signal_emit_by_name (nautilus_files_model_view_get_view_ui (self),
+                           "move-cursor", step, count, &handled);
+}
+
+static void
+default_sort_order_changed_callback (NautilusFilesModelView *self)
+{
+    update_sort_order_from_metadata_and_preferences (self);
+}
+
+static void
+nautilus_files_model_view_dispose (GObject *object)
+{
+    NautilusFilesModelView *self = NAUTILUS_FILES_MODEL_VIEW (object);
+    NautilusFilesModelViewPrivate *priv = nautilus_files_model_view_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_OBJECT_CLASS (nautilus_files_model_view_parent_class)->dispose (object);
+}
+
+static void
+nautilus_files_model_view_finalize (GObject *object)
+{
+    NautilusFilesModelView *self = NAUTILUS_FILES_MODEL_VIEW (object);
+    NautilusFilesModelViewPrivate *priv = nautilus_files_model_view_get_instance_private (self);
+
+    g_clear_list (&priv->cut_files, g_object_unref);
+
+    G_OBJECT_CLASS (nautilus_files_model_view_parent_class)->finalize (object);
+}
+
+static gboolean
+prioritize_thumbnailing_on_idle (NautilusFilesModelView *self)
+{
+    NautilusFilesModelViewPrivate *priv = nautilus_files_model_view_get_instance_private (self);
+    gdouble page_size;
+    GtkWidget *first_visible_child;
+    GtkWidget *next_child;
+    guint first_index;
+    guint next_index;
+    gdouble y;
+    guint last_index;
+    g_autoptr (NautilusViewItemModel) first_item = NULL;
+    NautilusFile *file;
+
+    priv->prioritize_thumbnailing_handle_id = 0;
+
+    page_size = gtk_adjustment_get_page_size (priv->vadjustment);
+    first_index = get_first_visible_item (self);
+    if (first_index == G_MAXUINT)
+    {
+        return G_SOURCE_REMOVE;
+    }
+
+    first_item = get_view_item (G_LIST_MODEL (priv->model), first_index);
+
+    first_visible_child = nautilus_view_item_model_get_item_ui (first_item);
+
+    for (next_index = first_index + 1; next_index < g_list_model_get_n_items (G_LIST_MODEL (priv->model)); 
next_index++)
+    {
+        g_autoptr (NautilusViewItemModel) next_item = NULL;
+
+        next_item = get_view_item (G_LIST_MODEL (priv->model), next_index);
+        next_child = nautilus_view_item_model_get_item_ui (next_item);
+        if (next_child == NULL)
+        {
+            break;
+        }
+        if (gtk_widget_translate_coordinates (next_child, first_visible_child,
+                                              0, 0, NULL, &y))
+        {
+            if (y > page_size)
+            {
+                break;
+            }
+        }
+    }
+    last_index = next_index - 1;
+
+    /* Do the iteration in reverse to give higher priority to the top */
+    for (gint i = 0; i <= last_index - first_index; i++)
+    {
+        g_autoptr (NautilusViewItemModel) item = NULL;
+
+        item = get_view_item (G_LIST_MODEL (priv->model), last_index - i);
+        g_return_val_if_fail (item != NULL, G_SOURCE_REMOVE);
+
+        file = nautilus_view_item_model_get_file (NAUTILUS_VIEW_ITEM_MODEL (item));
+        if (file != NULL && nautilus_file_is_thumbnailing (file))
+        {
+            g_autofree gchar *uri = nautilus_file_get_uri (file);
+            nautilus_thumbnail_prioritize (uri);
+        }
+    }
+
+    return G_SOURCE_REMOVE;
+}
+
+static void
+on_vadjustment_changed (GtkAdjustment *adjustment,
+                        gpointer       user_data)
+{
+    NautilusFilesModelView *self = NAUTILUS_FILES_MODEL_VIEW (user_data);
+    NautilusFilesModelViewPrivate *priv = nautilus_files_model_view_get_instance_private (self);
+    guint handle_id;
+
+    /* Schedule on idle to rate limit and to avoid delaying scrolling. */
+    if (priv->prioritize_thumbnailing_handle_id == 0)
+    {
+        handle_id = g_idle_add ((GSourceFunc) prioritize_thumbnailing_on_idle, self);
+        priv->prioritize_thumbnailing_handle_id = handle_id;
+    }
+}
+
+static void
+nautilus_files_model_view_class_init (NautilusFilesModelViewClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS (klass);
+    NautilusFilesViewClass *files_view_class = NAUTILUS_FILES_VIEW_CLASS (klass);
+
+    object_class->dispose = nautilus_files_model_view_dispose;
+    object_class->finalize = nautilus_files_model_view_finalize;
+
+    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->file_changed = real_file_changed;
+    files_view_class->get_selection = real_get_selection;
+    /* TODO: remove this get_selection_for_file_transfer, this doesn't even
+     * take into account we could us the view for recursive search :/
+     * CanvasView has the same issue. */
+    files_view_class->get_selection_for_file_transfer = real_get_selection;
+    files_view_class->is_empty = real_is_empty;
+    files_view_class->remove_file = real_remove_file;
+    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->compare_files = real_compare_files;
+    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->reveal_selection = real_reveal_selection;
+    files_view_class->update_actions_state = real_update_actions_state;
+    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;
+}
+
+static void
+nautilus_files_model_view_init (NautilusFilesModelView *self)
+{
+    NautilusFilesModelViewPrivate *priv = nautilus_files_model_view_get_instance_private (self);
+    GtkWidget *content_widget;
+    GtkAdjustment *vadjustment;
+
+    gtk_widget_add_css_class (GTK_WIDGET (self), "view");
+
+    g_signal_connect_object (nautilus_preferences,
+                             "changed::" NAUTILUS_PREFERENCES_DEFAULT_SORT_ORDER,
+                             G_CALLBACK (default_sort_order_changed_callback),
+                             self,
+                             G_CONNECT_SWAPPED);
+    g_signal_connect_object (nautilus_preferences,
+                             "changed::" NAUTILUS_PREFERENCES_DEFAULT_SORT_IN_REVERSE_ORDER,
+                             G_CALLBACK (default_sort_order_changed_callback),
+                             self,
+                             G_CONNECT_SWAPPED);
+
+    /* React to clipboard changes */
+    g_signal_connect_object (gdk_display_get_clipboard (gdk_display_get_default ()),
+                             "changed",
+                             G_CALLBACK (on_clipboard_owner_changed), self, 0);
+
+    content_widget = nautilus_files_view_get_content_widget (NAUTILUS_FILES_VIEW (self));
+    vadjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (content_widget));
+
+    priv->vadjustment = vadjustment;
+    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);
+}
+
+NautilusViewModel *
+nautilus_files_model_view_get_model (NautilusFilesModelView *self)
+{
+    NautilusFilesModelViewPrivate *priv = nautilus_files_model_view_get_instance_private (self);
+
+    return priv->model;
+}
diff --git a/src/nautilus-files-model-view.h b/src/nautilus-files-model-view.h
new file mode 100644
index 000000000..1de12ba2a
--- /dev/null
+++ b/src/nautilus-files-model-view.h
@@ -0,0 +1,44 @@
+/* nautilus-files-model-view.h
+ *
+ * Header of abstract type, to be considered private to its subclasses.
+ * */
+
+#pragma once
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+#include "nautilus-files-view.h"
+#include "nautilus-view-model.h"
+
+G_BEGIN_DECLS
+
+#define NAUTILUS_TYPE_FILES_MODEL_VIEW (nautilus_files_model_view_get_type())
+
+G_DECLARE_DERIVABLE_TYPE (NautilusFilesModelView, nautilus_files_model_view, NAUTILUS, FILES_MODEL_VIEW, 
NautilusFilesView)
+
+struct _NautilusFilesModelViewClass
+{
+        NautilusFilesViewClass parent_class;
+
+        guint      (*get_icon_size)  (NautilusFilesModelView *self);
+        GtkWidget *(*get_view_ui)    (NautilusFilesModelView *self);
+        void       (*scroll_to_item) (NautilusFilesModelView *self,
+                                      guint                   position);
+};
+
+/* Methods */
+NautilusViewModel *nautilus_files_model_view_get_model     (NautilusFilesModelView *self);
+void               nautilus_files_model_view_set_icon_size (NautilusFilesModelView *self,
+                                                            gint                    icon_size);
+
+/* Shareable helpers */
+void                          set_directory_sort_metadata       (NautilusFile *file,
+                                                                 const gchar  *metadata_name,
+                                                                 gboolean      reversed);
+const NautilusFileSortType    get_sorts_type_from_metadata_text (const char   *metadata_name);
+
+#define listitem_get_view_item(li) \
+(NAUTILUS_VIEW_ITEM_MODEL (gtk_tree_list_row_get_item (GTK_TREE_LIST_ROW (gtk_list_item_get_item (li)))))
+
+G_END_DECLS
diff --git a/src/nautilus-view-icon-controller.c b/src/nautilus-view-icon-controller.c
index 0da09dc83..4e5ff94a5 100644
--- a/src/nautilus-view-icon-controller.c
+++ b/src/nautilus-view-icon-controller.c
@@ -1,212 +1,34 @@
 #include "nautilus-view-icon-controller.h"
 #include "nautilus-view-item-model.h"
 #include "nautilus-view-icon-item-ui.h"
-#include "nautilus-view-model.h"
-#include "nautilus-files-view.h"
 #include "nautilus-file.h"
-#include "nautilus-metadata.h"
-#include "nautilus-window-slot.h"
-#include "nautilus-directory.h"
-#include "nautilus-clipboard.h"
 #include "nautilus-global-preferences.h"
-#include "nautilus-thumbnails.h"
 
 struct _NautilusViewIconController
 {
-    NautilusFilesView parent_instance;
+    NautilusFilesModelView parent_instance;
 
     GtkGridView *view_ui;
-    NautilusViewModel *model;
-
-    GList *cut_files;
 
     GActionGroup *action_group;
     gint zoom_level;
-    GQuark caption_attributes[NAUTILUS_VIEW_ICON_N_CAPTIONS];
 
+    gboolean directories_first;
     gboolean single_click_mode;
+
     gboolean activate_on_release;
     gboolean deny_background_click;
 
-    guint scroll_to_file_handle_id;
-    guint prioritize_thumbnailing_handle_id;
-    GtkAdjustment *vadjustment;
+    GQuark caption_attributes[NAUTILUS_VIEW_ICON_N_CAPTIONS];
 
     NautilusFileSortType sort_type;
-    gboolean directories_first;
     gboolean reversed;
 };
 
-G_DEFINE_TYPE (NautilusViewIconController, nautilus_view_icon_controller, NAUTILUS_TYPE_FILES_VIEW)
-
-static inline NautilusViewItemModel *
-model_get_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_MODEL (gtk_tree_list_row_get_item (GTK_TREE_LIST_ROW (row)));
-}
-
-static inline NautilusViewItemModel *
-listitem_get_item (GtkListItem *listitem)
-{
-    GtkTreeListRow *row = gtk_list_item_get_item (listitem);
-
-    g_return_val_if_fail (GTK_IS_TREE_LIST_ROW (row), NULL);
-    return NAUTILUS_VIEW_ITEM_MODEL (gtk_tree_list_row_get_item (GTK_TREE_LIST_ROW (row)));
-}
-
-typedef struct
-{
-    const NautilusFileSortType sort_type;
-    const gchar *metadata_name;
-} SortConstants;
-
-static const SortConstants sorts_constants[] =
-{
-    {
-        NAUTILUS_FILE_SORT_BY_DISPLAY_NAME,
-        "name",
-    },
-    {
-        NAUTILUS_FILE_SORT_BY_SIZE,
-        "size",
-    },
-    {
-        NAUTILUS_FILE_SORT_BY_TYPE,
-        "type",
-    },
-    {
-        NAUTILUS_FILE_SORT_BY_MTIME,
-        "modification date",
-    },
-    {
-        NAUTILUS_FILE_SORT_BY_ATIME,
-        "access date",
-    },
-    {
-        NAUTILUS_FILE_SORT_BY_BTIME,
-        "creation date",
-    },
-    {
-        NAUTILUS_FILE_SORT_BY_TRASHED_TIME,
-        "trashed",
-    },
-    {
-        NAUTILUS_FILE_SORT_BY_SEARCH_RELEVANCE,
-        "search_relevance",
-    },
-    {
-        NAUTILUS_FILE_SORT_BY_RECENCY,
-        "recency",
-    },
-};
+G_DEFINE_TYPE (NautilusViewIconController, nautilus_view_icon_controller, NAUTILUS_TYPE_FILES_MODEL_VIEW)
 
 static guint get_icon_size_for_zoom_level (NautilusGridZoomLevel zoom_level);
 
-static const SortConstants *
-get_sorts_constants_from_sort_type (NautilusFileSortType sort_type)
-{
-    guint i;
-
-    for (i = 0; i < G_N_ELEMENTS (sorts_constants); i++)
-    {
-        if (sort_type == sorts_constants[i].sort_type)
-        {
-            return &sorts_constants[i];
-        }
-    }
-
-    return &sorts_constants[0];
-}
-
-static const SortConstants *
-get_sorts_constants_from_metadata_text (const char *metadata_name)
-{
-    guint i;
-
-    for (i = 0; i < G_N_ELEMENTS (sorts_constants); i++)
-    {
-        if (g_strcmp0 (sorts_constants[i].metadata_name, metadata_name) == 0)
-        {
-            return &sorts_constants[i];
-        }
-    }
-
-    return &sorts_constants[0];
-}
-
-static const SortConstants *
-get_default_sort_order (NautilusFile *file,
-                        gboolean     *reversed)
-{
-    NautilusFileSortType sort_type;
-
-    sort_type = nautilus_file_get_default_sort_type (file, reversed);
-
-    return get_sorts_constants_from_sort_type (sort_type);
-}
-
-static const SortConstants *
-get_directory_sort_by (NautilusFile *file,
-                       gboolean     *reversed)
-{
-    const SortConstants *default_sort;
-    g_autofree char *sort_by = NULL;
-
-    default_sort = get_default_sort_order (file, reversed);
-    g_return_val_if_fail (default_sort != NULL, NULL);
-
-    sort_by = nautilus_file_get_metadata (file,
-                                          NAUTILUS_METADATA_KEY_ICON_VIEW_SORT_BY,
-                                          default_sort->metadata_name);
-
-    *reversed = nautilus_file_get_boolean_metadata (file,
-                                                    NAUTILUS_METADATA_KEY_ICON_VIEW_SORT_REVERSED,
-                                                    *reversed);
-
-    return get_sorts_constants_from_metadata_text (sort_by);
-}
-
-static void
-set_directory_sort_metadata (NautilusFile        *file,
-                             const SortConstants *sort,
-                             gboolean             reversed)
-{
-    const SortConstants *default_sort;
-    gboolean default_reversed;
-
-    default_sort = get_default_sort_order (file, &default_reversed);
-
-    nautilus_file_set_metadata (file,
-                                NAUTILUS_METADATA_KEY_ICON_VIEW_SORT_BY,
-                                default_sort->metadata_name,
-                                sort->metadata_name);
-    nautilus_file_set_boolean_metadata (file,
-                                        NAUTILUS_METADATA_KEY_ICON_VIEW_SORT_REVERSED,
-                                        default_reversed,
-                                        reversed);
-}
-
-static void
-update_sort_order_from_metadata_and_preferences (NautilusViewIconController *self)
-{
-    const SortConstants *default_directory_sort;
-    GActionGroup *view_action_group;
-    gboolean reversed;
-
-    default_directory_sort = get_directory_sort_by (nautilus_files_view_get_directory_as_file 
(NAUTILUS_FILES_VIEW (self)),
-                                                    &reversed);
-    view_action_group = nautilus_files_view_get_action_group (NAUTILUS_FILES_VIEW (self));
-    g_action_group_change_action_state (view_action_group,
-                                        "sort",
-                                        g_variant_new ("(sb)",
-                                                       default_directory_sort->metadata_name,
-                                                       reversed));
-}
-
 static gint
 nautilus_view_icon_controller_sort (gconstpointer a,
                                     gconstpointer b,
@@ -225,258 +47,6 @@ nautilus_view_icon_controller_sort (gconstpointer a,
                                            self->reversed);
 }
 
-static void
-real_begin_loading (NautilusFilesView *files_view)
-{
-    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
-
-    /* TODO: This calls sort once, and update_context_menus calls update_actions which calls
-     * the action again
-     */
-    update_sort_order_from_metadata_and_preferences (self);
-
-    /*TODO move this to the files view class begin_loading and hook up? */
-
-    /* We could have changed to the trash directory or to searching, and then
-     * we need to update the menus */
-    nautilus_files_view_update_context_menus (files_view);
-    nautilus_files_view_update_toolbar_menus (files_view);
-}
-
-static void
-real_clear (NautilusFilesView *files_view)
-{
-    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
-
-    nautilus_view_model_remove_all_items (self->model);
-}
-
-static void
-real_file_changed (NautilusFilesView *files_view,
-                   NautilusFile      *file,
-                   NautilusDirectory *directory)
-{
-    NautilusViewIconController *self;
-    NautilusViewItemModel *item_model;
-
-    self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
-    item_model = nautilus_view_model_get_item_from_file (self->model, file);
-    nautilus_view_item_model_file_changed (item_model);
-}
-
-static GList *
-real_get_selection (NautilusFilesView *files_view)
-{
-    NautilusViewIconController *self;
-    g_autoptr (GtkSelectionFilterModel) selection = NULL;
-    guint n_selected;
-    GList *selected_files = NULL;
-
-    self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
-    selection = gtk_selection_filter_model_new (GTK_SELECTION_MODEL (self->model));
-    n_selected = g_list_model_get_n_items (G_LIST_MODEL (selection));
-    for (guint i = 0; i < n_selected; i++)
-    {
-        g_autoptr (NautilusViewItemModel) item_model = NULL;
-
-        item_model = model_get_item (G_LIST_MODEL (selection), i);
-        selected_files = g_list_prepend (selected_files,
-                                         g_object_ref (nautilus_view_item_model_get_file (item_model)));
-    }
-
-    return selected_files;
-}
-
-static gboolean
-real_is_empty (NautilusFilesView *files_view)
-{
-    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
-
-    return g_list_model_get_n_items (G_LIST_MODEL (self->model)) == 0;
-}
-
-static void
-real_end_file_changes (NautilusFilesView *files_view)
-{
-}
-
-static void
-real_remove_file (NautilusFilesView *files_view,
-                  NautilusFile      *file,
-                  NautilusDirectory *directory)
-{
-    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
-    NautilusViewItemModel *item_model;
-
-    item_model = nautilus_view_model_get_item_from_file (self->model, file);
-    if (item_model != NULL)
-    {
-        nautilus_view_model_remove_item (self->model, item_model);
-    }
-}
-
-static GQueue *
-convert_glist_to_queue (GList *list)
-{
-    GList *l;
-    GQueue *queue;
-
-    queue = g_queue_new ();
-    for (l = list; l != NULL; l = l->next)
-    {
-        g_queue_push_tail (queue, l->data);
-    }
-
-    return queue;
-}
-
-static GQueue *
-convert_files_to_item_models (NautilusViewIconController *self,
-                              GQueue                     *files)
-{
-    GList *l;
-    GQueue *models;
-
-    models = g_queue_new ();
-    for (l = g_queue_peek_head_link (files); l != NULL; l = l->next)
-    {
-        NautilusViewItemModel *item_model;
-
-        item_model = nautilus_view_item_model_new (NAUTILUS_FILE (l->data),
-                                                   get_icon_size_for_zoom_level (self->zoom_level));
-        g_queue_push_tail (models, item_model);
-    }
-
-    return models;
-}
-
-static void
-real_set_selection (NautilusFilesView *files_view,
-                    GList             *selection)
-{
-    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
-    g_autoptr (GQueue) selection_files = NULL;
-    g_autoptr (GQueue) selection_item_models = NULL;
-    g_autoptr (GtkBitset) update_set = NULL;
-    g_autoptr (GtkBitset) selection_set = NULL;
-
-    update_set = gtk_selection_model_get_selection (GTK_SELECTION_MODEL (self->model));
-    selection_set = gtk_bitset_new_empty ();
-
-    /* Convert file list into set of model indices */
-    selection_files = convert_glist_to_queue (selection);
-    selection_item_models = nautilus_view_model_get_items_from_files (self->model, selection_files);
-    for (GList *l = g_queue_peek_head_link (selection_item_models); l != NULL ; l = l->next)
-    {
-        gtk_bitset_add (selection_set,
-                        nautilus_view_model_get_index (self->model, l->data));
-    }
-
-    gtk_bitset_union (update_set, selection_set);
-    gtk_selection_model_set_selection (GTK_SELECTION_MODEL (self->model),
-                                       selection_set,
-                                       update_set);
-}
-
-static void
-real_select_all (NautilusFilesView *files_view)
-{
-    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
-    gtk_selection_model_select_all (GTK_SELECTION_MODEL (self->model));
-}
-
-static void
-real_invert_selection (NautilusFilesView *files_view)
-{
-    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
-    GtkSelectionModel *selection_model = GTK_SELECTION_MODEL (self->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 (self->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 (NautilusViewIconController *self)
-{
-    g_autolist (NautilusFile) selection = NULL;
-    NautilusFile *file;
-    NautilusViewItemModel *item_model;
-
-    selection = nautilus_view_get_selection (NAUTILUS_VIEW (self));
-    if (selection == NULL)
-    {
-        return G_MAXUINT;
-    }
-
-    file = NAUTILUS_FILE (selection->data);
-    item_model = nautilus_view_model_get_item_from_file (self->model, file);
-
-    return nautilus_view_model_get_index (self->model, item_model);
-}
-
-static void
-real_reveal_selection (NautilusFilesView *files_view)
-{
-    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
-
-    gtk_widget_activate_action (GTK_WIDGET (self->view_ui),
-                                "list.scroll-to-item",
-                                "u",
-                                get_first_selected_item (self));
-}
-
-static gboolean
-showing_recent_directory (NautilusFilesView *view)
-{
-    NautilusFile *file;
-
-    file = nautilus_files_view_get_directory_as_file (view);
-    if (file != NULL)
-    {
-        return nautilus_file_is_in_recent (file);
-    }
-    return FALSE;
-}
-
-static gboolean
-showing_search_directory (NautilusFilesView *view)
-{
-    NautilusFile *file;
-
-    file = nautilus_files_view_get_directory_as_file (view);
-    if (file != NULL)
-    {
-        return nautilus_file_is_in_search (file);
-    }
-    return FALSE;
-}
-
-static void
-real_update_actions_state (NautilusFilesView *files_view)
-{
-    GAction *action;
-    GActionGroup *view_action_group;
-
-    NAUTILUS_FILES_VIEW_CLASS (nautilus_view_icon_controller_parent_class)->update_actions_state 
(files_view);
-
-    view_action_group = nautilus_files_view_get_action_group (files_view);
-    action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group), "sort");
-    g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
-                                 !showing_recent_directory (files_view) &&
-                                 !showing_search_directory (files_view));
-}
-
 static void
 real_bump_zoom_level (NautilusFilesView *files_view,
                       int                zoom_increment)
@@ -533,6 +103,22 @@ get_icon_size_for_zoom_level (NautilusGridZoomLevel zoom_level)
     g_return_val_if_reached (NAUTILUS_GRID_ICON_SIZE_STANDARD);
 }
 
+static guint
+real_get_icon_size (NautilusFilesModelView *files_model_view)
+{
+    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_model_view);
+
+    return get_icon_size_for_zoom_level (self->zoom_level);
+}
+
+static GtkWidget *
+real_get_view_ui (NautilusFilesModelView *files_model_view)
+{
+    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_model_view);
+
+    return GTK_WIDGET (self->view_ui);
+}
+
 static gint
 get_default_zoom_level (void)
 {
@@ -575,23 +161,6 @@ set_captions_from_preferences (NautilusViewIconController *self)
     }
 }
 
-static void
-set_icon_size (NautilusViewIconController *self,
-               gint                        icon_size)
-{
-    guint n_items;
-
-    n_items = g_list_model_get_n_items (G_LIST_MODEL (self->model));
-    for (guint i = 0; i < n_items; i++)
-    {
-        g_autoptr (NautilusViewItemModel) current_item_model = NULL;
-
-        current_item_model = model_get_item (G_LIST_MODEL (self->model), i);
-        nautilus_view_item_model_set_icon_size (current_item_model,
-                                                get_icon_size_for_zoom_level (self->zoom_level));
-    }
-}
-
 static void
 set_zoom_level (NautilusViewIconController *self,
                 guint                       new_level)
@@ -603,7 +172,8 @@ set_zoom_level (NautilusViewIconController *self,
      * updates captions whenever the icon size is set*/
     set_captions_from_preferences (self);
 
-    set_icon_size (self, get_icon_size_for_zoom_level (new_level));
+    nautilus_files_model_view_set_icon_size (NAUTILUS_FILES_MODEL_VIEW (self),
+                                             get_icon_size_for_zoom_level (new_level));
 
     nautilus_files_view_update_toolbar_menus (NAUTILUS_FILES_VIEW (self));
 }
@@ -647,78 +217,16 @@ real_can_zoom_out (NautilusFilesView *files_view)
     return self->zoom_level > NAUTILUS_GRID_ZOOM_LEVEL_SMALL;
 }
 
-static GdkRectangle *
-get_rectangle_for_item_ui (NautilusViewIconController *self,
-                           GtkWidget                  *item_ui)
-{
-    GdkRectangle *rectangle;
-    GtkWidget *content_widget;
-    gdouble view_x;
-    gdouble view_y;
-
-    rectangle = g_new0 (GdkRectangle, 1);
-    gtk_widget_get_allocation (item_ui, rectangle);
-
-    content_widget = nautilus_files_view_get_content_widget (NAUTILUS_FILES_VIEW (self));
-    gtk_widget_translate_coordinates (item_ui, content_widget,
-                                      rectangle->x, rectangle->y,
-                                      &view_x, &view_y);
-    rectangle->x = view_x;
-    rectangle->y = view_y;
-
-    return rectangle;
-}
-
-static GdkRectangle *
-real_compute_rename_popover_pointing_to (NautilusFilesView *files_view)
-{
-    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
-    g_autoptr (NautilusViewItemModel) item = NULL;
-    GtkWidget *item_ui;
-
-    /* We only allow one item to be renamed with a popover */
-    item = model_get_item (G_LIST_MODEL (self->model), get_first_selected_item (self));
-    item_ui = nautilus_view_item_model_get_item_ui (item);
-    g_return_val_if_fail (item_ui != NULL, NULL);
-
-    return get_rectangle_for_item_ui (self, item_ui);
-}
-
-static GdkRectangle *
-real_reveal_for_selection_context_menu (NautilusFilesView *files_view)
+static void
+real_scroll_to_item (NautilusFilesModelView *files_model_view,
+                     guint                   position)
 {
-    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
-    g_autoptr (GtkSelectionFilterModel) selection = NULL;
-    guint n_selected;
-    GtkWidget *focus_child;
-    guint i;
-    GtkWidget *item_ui;
-
-    selection = gtk_selection_filter_model_new (GTK_SELECTION_MODEL (self->model));
-    n_selected = g_list_model_get_n_items (G_LIST_MODEL (selection));
-    g_return_val_if_fail (n_selected > 0, NULL);
-
-    /* Get the focused item_ui, if selected.
-     * Otherwise, get the selected item_ui which is sorted the lowest.*/
-    focus_child = gtk_widget_get_focus_child (GTK_WIDGET (self->view_ui));
-    for (i = 0; i < n_selected; i++)
-    {
-        g_autoptr (NautilusViewItemModel) item = NULL;
-
-        item = model_get_item (G_LIST_MODEL (selection), i);
-        item_ui = nautilus_view_item_model_get_item_ui (item);
-        if (item_ui != NULL && gtk_widget_get_parent (item_ui) == focus_child)
-        {
-            break;
-        }
-    }
+    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_model_view);
 
     gtk_widget_activate_action (GTK_WIDGET (self->view_ui),
                                 "list.scroll-to-item",
                                 "u",
-                                i);
-
-    return get_rectangle_for_item_ui (self, item_ui);
+                                position);
 }
 
 static void
@@ -747,13 +255,14 @@ static void
 select_single_item_if_not_selected (NautilusViewIconController *self,
                                     NautilusViewItemModel      *item)
 {
-    GtkSelectionModel *selection_model = GTK_SELECTION_MODEL (self->model);
+    NautilusViewModel *model;
     guint position;
 
-    position = nautilus_view_model_get_index (self->model, item);
-    if (!gtk_selection_model_is_selected (selection_model, position))
+    model = nautilus_files_model_view_get_model (NAUTILUS_FILES_MODEL_VIEW (self));
+    position = nautilus_view_model_get_index (model, item);
+    if (!gtk_selection_model_is_selected (GTK_SELECTION_MODEL (model), position))
     {
-        gtk_selection_model_select_item (selection_model, position, TRUE);
+        gtk_selection_model_select_item (GTK_SELECTION_MODEL (model), position, TRUE);
     }
 }
 
@@ -842,16 +351,18 @@ on_item_click_released (GtkGestureClick *gesture,
 
     if (self->activate_on_release)
     {
+        NautilusViewModel *model;
         GtkWidget *event_widget;
         NautilusViewItemModel *item_model;
         guint i;
 
+        model = nautilus_files_model_view_get_model (NAUTILUS_FILES_MODEL_VIEW (self));
         event_widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
         item_model = nautilus_view_icon_item_ui_get_model (NAUTILUS_VIEW_ICON_ITEM_UI (event_widget));
-        i = nautilus_view_model_get_index (self->model, item_model);
+        i = nautilus_view_model_get_index (model, item_model);
 
         /* Antecipate selection, enforcing single selection of target item. */
-        gtk_selection_model_select_item (GTK_SELECTION_MODEL (self->model), i, TRUE);
+        gtk_selection_model_select_item (GTK_SELECTION_MODEL (model), i, TRUE);
 
         activate_selection_on_click (self, FALSE);
     }
@@ -945,207 +456,19 @@ on_longpress_gesture_pressed_callback (GtkGestureLongPress *gesture,
     }
 }
 
-static int
-real_compare_files (NautilusFilesView *files_view,
-                    NautilusFile      *file1,
-                    NautilusFile      *file2)
-{
-    GActionGroup *view_action_group;
-    GAction *action;
-    const gchar *target_name;
-    gboolean reversed;
-    const SortConstants *sort_constants;
-    gboolean directories_first;
-
-    view_action_group = nautilus_files_view_get_action_group (files_view);
-    action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group), "sort");
-    g_variant_get (g_action_get_state (action), "(&sb)", &target_name, &reversed);
-    sort_constants = get_sorts_constants_from_metadata_text (target_name);
-    directories_first = nautilus_files_view_should_sort_directories_first (files_view);
-
-    return nautilus_file_compare_for_sort (file1, file2,
-                                           sort_constants->sort_type,
-                                           directories_first,
-                                           reversed);
-}
-
-static void
-on_clipboard_contents_received (GObject      *source_object,
-                                GAsyncResult *res,
-                                gpointer      user_data)
-{
-    NautilusFilesView *files_view = NAUTILUS_FILES_VIEW (source_object);
-    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
-    NautilusClipboard *clip;
-    NautilusViewItemModel *item;
-
-    for (GList *l = self->cut_files; l != NULL; l = l->next)
-    {
-        item = nautilus_view_model_get_item_from_file (self->model, l->data);
-        if (item != NULL)
-        {
-            nautilus_view_item_model_set_cut (item, FALSE);
-        }
-    }
-    g_clear_list (&self->cut_files, g_object_unref);
-
-    clip = nautilus_files_view_get_clipboard_finish (files_view, res, NULL);
-    if (clip != NULL && nautilus_clipboard_is_cut (clip))
-    {
-        self->cut_files = g_list_copy_deep (nautilus_clipboard_peek_files (clip),
-                                            (GCopyFunc) g_object_ref,
-                                            NULL);
-    }
-
-    for (GList *l = self->cut_files; l != NULL; l = l->next)
-    {
-        item = nautilus_view_model_get_item_from_file (self->model, l->data);
-        if (item != NULL)
-        {
-            nautilus_view_item_model_set_cut (item, TRUE);
-        }
-    }
-}
-
-static void
-update_clipboard_status (NautilusFilesView *files_view)
-{
-    nautilus_files_view_get_clipboard_async (files_view,
-                                             on_clipboard_contents_received,
-                                             NULL);
-}
-
-static void
-on_clipboard_owner_changed (GdkClipboard *clipboard,
-                            gpointer      user_data)
-{
-    update_clipboard_status (NAUTILUS_FILES_VIEW (user_data));
-}
-
-
-static void
-real_end_loading (NautilusFilesView *files_view,
-                  gboolean           all_files_seen)
-{
-    update_clipboard_status (files_view);
-}
-
-static guint
-get_first_visible_item (NautilusViewIconController *self)
-{
-    guint n_items;
-    gdouble scrolled_y;
-
-    n_items = g_list_model_get_n_items (G_LIST_MODEL (self->model));
-    scrolled_y = gtk_adjustment_get_value (self->vadjustment);
-    for (guint i = 0; i < n_items; i++)
-    {
-        g_autoptr (NautilusViewItemModel) item = NULL;
-        GtkWidget *item_ui;
-
-        item = model_get_item (G_LIST_MODEL (self->model), i);
-        item_ui = nautilus_view_item_model_get_item_ui (item);
-        if (item_ui != NULL)
-        {
-            gdouble y;
-
-            gtk_widget_translate_coordinates (item_ui, GTK_WIDGET (self->view_ui),
-                                              0, 0, NULL, &y);
-            if (gtk_widget_is_visible (item_ui) && y >= scrolled_y)
-            {
-                return i;
-            }
-        }
-    }
-
-    return G_MAXUINT;
-}
-
-static char *
-real_get_first_visible_file (NautilusFilesView *files_view)
-{
-    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
-    guint i;
-    g_autoptr (NautilusViewItemModel) item = NULL;
-    gchar *uri = NULL;
-
-    i = get_first_visible_item (self);
-    if (i < G_MAXUINT)
-    {
-        item = model_get_item (G_LIST_MODEL (self->model), i);
-        uri = nautilus_file_get_uri (nautilus_view_item_model_get_file (item));
-    }
-    return uri;
-}
-
-typedef struct
-{
-    NautilusViewIconController *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)
-{
-    NautilusViewIconController *self = data->view;
-    g_autoptr (NautilusFile) file = NULL;
-    NautilusViewItemModel *item;
-    guint i;
-
-    file = nautilus_file_get_existing_by_uri (data->uri);
-    item = nautilus_view_model_get_item_from_file (self->model, file);
-    i = nautilus_view_model_get_index (self->model, item);
-
-    gtk_widget_activate_action (GTK_WIDGET (self->view_ui),
-                                "list.scroll-to-item",
-                                "u",
-                                i);
-
-    self->scroll_to_file_handle_id = 0;
-    return G_SOURCE_REMOVE;
-}
-
-static void
-real_scroll_to_file (NautilusFilesView *files_view,
-                     const char        *uri)
-{
-    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
-    ScrollToFileData *data;
-    guint handle_id;
-
-    /* Not exactly sure why, but the child widgets are not yet realized when
-     * this is usually called (which is when view finishes loading. Maybe
-     * because GtkFlowBox only generates children at the next GMainContext
-     * iteration? Anyway, doing it on idle as low priority works. */
-
-    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);
-    self->scroll_to_file_handle_id = handle_id;
-}
-
 static void
 real_sort_directories_first_changed (NautilusFilesView *files_view)
 {
     NautilusViewIconController *self;
+    NautilusViewModel *model;
     g_autoptr (GtkCustomSorter) sorter = NULL;
 
     self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
     self->directories_first = nautilus_files_view_should_sort_directories_first (NAUTILUS_FILES_VIEW (self));
 
+    model = nautilus_files_model_view_get_model (NAUTILUS_FILES_MODEL_VIEW (self));
     sorter = gtk_custom_sorter_new (nautilus_view_icon_controller_sort, self, NULL);
-    nautilus_view_model_set_sorter (self->model, GTK_SORTER (sorter));
+    nautilus_view_model_set_sorter (model, GTK_SORTER (sorter));
 }
 
 static void
@@ -1154,8 +477,8 @@ action_sort_order_changed (GSimpleAction *action,
                            gpointer       user_data)
 {
     const gchar *target_name;
-    const SortConstants *sort_constants;
-    NautilusViewIconController *self;
+    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (user_data);
+    NautilusViewModel *model;
     g_autoptr (GtkCustomSorter) sorter = NULL;
 
     /* Don't resort if the action is in the same state as before */
@@ -1164,86 +487,25 @@ action_sort_order_changed (GSimpleAction *action,
         return;
     }
 
-    self = NAUTILUS_VIEW_ICON_CONTROLLER (user_data);
     g_variant_get (value, "(&sb)", &target_name, &self->reversed);
-    sort_constants = get_sorts_constants_from_metadata_text (target_name);
-    self->sort_type = sort_constants->sort_type;
-    self->directories_first = nautilus_files_view_should_sort_directories_first (NAUTILUS_FILES_VIEW (self));
+    self->sort_type = get_sorts_type_from_metadata_text (target_name);
 
     sorter = gtk_custom_sorter_new (nautilus_view_icon_controller_sort, self, NULL);
-    nautilus_view_model_set_sorter (self->model, GTK_SORTER (sorter));
+    model = nautilus_files_model_view_get_model (NAUTILUS_FILES_MODEL_VIEW (self));
+    nautilus_view_model_set_sorter (model, GTK_SORTER (sorter));
     set_directory_sort_metadata (nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (self)),
-                                 sort_constants,
+                                 target_name,
                                  self->reversed);
 
     g_simple_action_set_state (action, value);
 }
 
-static void
-real_add_files (NautilusFilesView *files_view,
-                GList             *files)
-{
-    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
-    g_autoptr (GQueue) files_queue = NULL;
-    g_autoptr (GQueue) item_models = NULL;
-    gdouble adjustment_value;
-
-    files_queue = convert_glist_to_queue (files);
-    item_models = convert_files_to_item_models (self, files_queue);
-    nautilus_view_model_add_items (self->model, item_models);
-
-    /* GtkListBase anchoring doesn't cope well with our lazy loading.
-     * Assuming that GtkListBase|list.scroll-to-item resets the anchor to 0, use
-     * that as a workaround to prevent scrolling while we are at the top. */
-    adjustment_value = gtk_adjustment_get_value (self->vadjustment);
-    if (G_APPROX_VALUE (adjustment_value, 0.0, DBL_EPSILON))
-    {
-        gtk_widget_activate_action (GTK_WIDGET (self->view_ui), "list.scroll-to-item", "u", 0);
-    }
-}
-
-
 static guint
 real_get_view_id (NautilusFilesView *files_view)
 {
     return NAUTILUS_VIEW_GRID_ID;
 }
 
-static void
-real_select_first (NautilusFilesView *files_view)
-{
-    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
-    g_autoptr (NautilusViewItemModel) item = NULL;
-    NautilusFile *file;
-    g_autoptr (GList) selection = NULL;
-
-    item = model_get_item (G_LIST_MODEL (self->model), 0);
-    if (item == NULL)
-    {
-        return;
-    }
-    file = nautilus_view_item_model_get_file (item);
-    selection = g_list_prepend (selection, file);
-    nautilus_view_set_selection (NAUTILUS_VIEW (files_view), selection);
-}
-
-static void
-real_preview_selection_event (NautilusFilesView *files_view,
-                              GtkDirectionType   direction)
-{
-    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
-    GtkMovementStep step;
-    gint count;
-    gboolean handled;
-
-    step = (direction == GTK_DIR_UP || direction == GTK_DIR_DOWN) ?
-           GTK_MOVEMENT_DISPLAY_LINES : GTK_MOVEMENT_VISUAL_POSITIONS;
-    count = (direction == GTK_DIR_RIGHT || direction == GTK_DIR_DOWN) ?
-            1 : -1;
-
-    g_signal_emit_by_name (self->view_ui, "move-cursor", step, count, &handled);
-}
-
 static void
 action_zoom_to_level (GSimpleAction *action,
                       GVariant      *state,
@@ -1272,119 +534,22 @@ on_captions_preferences_changed (NautilusViewIconController *self)
 
     /* Hack: this relies on the assumption that NautilusViewIconItemUi updates
      * captions whenever the icon size is set (even if it's the same value). */
-    set_icon_size (self, get_icon_size_for_zoom_level (self->zoom_level));
-}
-
-static void
-on_default_sort_order_changed (NautilusViewIconController *self)
-{
-    update_sort_order_from_metadata_and_preferences (self);
+    nautilus_files_model_view_set_icon_size (NAUTILUS_FILES_MODEL_VIEW (self),
+                                             get_icon_size_for_zoom_level (self->zoom_level));
 }
 
 static void
 dispose (GObject *object)
 {
-    NautilusViewIconController *self;
-
-    self = NAUTILUS_VIEW_ICON_CONTROLLER (object);
-
-    g_clear_handle_id (&self->scroll_to_file_handle_id, g_source_remove);
-    g_clear_handle_id (&self->prioritize_thumbnailing_handle_id, g_source_remove);
-
     G_OBJECT_CLASS (nautilus_view_icon_controller_parent_class)->dispose (object);
 }
 
 static void
 finalize (GObject *object)
 {
-    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (object);
-
-    g_clear_list (&self->cut_files, g_object_unref);
-
     G_OBJECT_CLASS (nautilus_view_icon_controller_parent_class)->finalize (object);
 }
 
-static gboolean
-prioritize_thumbnailing_on_idle (NautilusViewIconController *self)
-{
-    gdouble page_size;
-    GtkWidget *first_visible_child;
-    GtkWidget *next_child;
-    guint first_index;
-    guint next_index;
-    gdouble y;
-    guint last_index;
-    g_autoptr (NautilusViewItemModel) first_item = NULL;
-    NautilusFile *file;
-
-    self->prioritize_thumbnailing_handle_id = 0;
-
-    page_size = gtk_adjustment_get_page_size (self->vadjustment);
-    first_index = get_first_visible_item (self);
-    if (first_index == G_MAXUINT)
-    {
-        return G_SOURCE_REMOVE;
-    }
-
-    first_item = model_get_item (G_LIST_MODEL (self->model), first_index);
-
-    first_visible_child = nautilus_view_item_model_get_item_ui (first_item);
-
-    for (next_index = first_index + 1; next_index < g_list_model_get_n_items (G_LIST_MODEL (self->model)); 
next_index++)
-    {
-        g_autoptr (NautilusViewItemModel) next_item = NULL;
-
-        next_item = model_get_item (G_LIST_MODEL (self->model), next_index);
-        next_child = nautilus_view_item_model_get_item_ui (next_item);
-        if (next_child == NULL)
-        {
-            break;
-        }
-        if (gtk_widget_translate_coordinates (next_child, first_visible_child,
-                                              0, 0, NULL, &y))
-        {
-            if (y > page_size)
-            {
-                break;
-            }
-        }
-    }
-    last_index = next_index - 1;
-
-    /* Do the iteration in reverse to give higher priority to the top */
-    for (gint i = 0; i <= last_index - first_index; i++)
-    {
-        g_autoptr (NautilusViewItemModel) item = NULL;
-
-        item = model_get_item (G_LIST_MODEL (self->model), last_index - i);
-        g_return_val_if_fail (item != NULL, G_SOURCE_REMOVE);
-
-        file = nautilus_view_item_model_get_file (NAUTILUS_VIEW_ITEM_MODEL (item));
-        if (file != NULL && nautilus_file_is_thumbnailing (file))
-        {
-            g_autofree gchar *uri = nautilus_file_get_uri (file);
-            nautilus_thumbnail_prioritize (uri);
-        }
-    }
-
-    return G_SOURCE_REMOVE;
-}
-
-static void
-on_vadjustment_changed (GtkAdjustment *adjustment,
-                        gpointer       user_data)
-{
-    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (user_data);
-    guint handle_id;
-
-    /* Schedule on idle to rate limit and to avoid delaying scrolling. */
-    if (self->prioritize_thumbnailing_handle_id == 0)
-    {
-        handle_id = g_idle_add ((GSourceFunc) prioritize_thumbnailing_on_idle, self);
-        self->prioritize_thumbnailing_handle_id = handle_id;
-    }
-}
-
 static void
 bind_item_ui (GtkSignalListItemFactory *factory,
               GtkListItem              *listitem,
@@ -1394,7 +559,7 @@ bind_item_ui (GtkSignalListItemFactory *factory,
     NautilusViewItemModel *item_model;
 
     item_ui = gtk_list_item_get_child (listitem);
-    item_model = listitem_get_item (listitem);
+    item_model = listitem_get_view_item (listitem);
     g_return_if_fail (item_model != NULL);
 
     nautilus_view_icon_item_ui_set_model (NAUTILUS_VIEW_ICON_ITEM_UI (item_ui),
@@ -1427,7 +592,7 @@ unbind_item_ui (GtkSignalListItemFactory *factory,
     NautilusViewItemModel *item_model;
 
     item_ui = NAUTILUS_VIEW_ICON_ITEM_UI (gtk_list_item_get_child (listitem));
-    item_model = listitem_get_item (listitem);
+    item_model = listitem_get_view_item (listitem);
 
     nautilus_view_icon_item_ui_set_model (item_ui, NULL);
     /* item may be NULL when row has just been destroyed. */
@@ -1468,15 +633,18 @@ setup_item_ui (GtkSignalListItemFactory *factory,
 static GtkGridView *
 create_view_ui (NautilusViewIconController *self)
 {
+    NautilusViewModel *model;
     GtkListItemFactory *factory;
     GtkWidget *widget;
 
+    model = nautilus_files_model_view_get_model (NAUTILUS_FILES_MODEL_VIEW (self));
+
     factory = gtk_signal_list_item_factory_new ();
     g_signal_connect (factory, "setup", G_CALLBACK (setup_item_ui), self);
     g_signal_connect (factory, "bind", G_CALLBACK (bind_item_ui), self);
     g_signal_connect (factory, "unbind", G_CALLBACK (unbind_item_ui), self);
 
-    widget = gtk_grid_view_new (GTK_SELECTION_MODEL (self->model), factory);
+    widget = gtk_grid_view_new (GTK_SELECTION_MODEL (model), factory);
     gtk_widget_set_focusable (widget, TRUE);
     gtk_widget_set_valign (widget, GTK_ALIGN_START);
 
@@ -1500,30 +668,49 @@ const GActionEntry view_icon_actions[] =
 };
 
 static void
-constructed (GObject *object)
+nautilus_view_icon_controller_class_init (NautilusViewIconControllerClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS (klass);
+    NautilusFilesViewClass *files_view_class = NAUTILUS_FILES_VIEW_CLASS (klass);
+    NautilusFilesModelViewClass *files_model_view_class = NAUTILUS_FILES_MODEL_VIEW_CLASS (klass);
+
+    object_class->dispose = dispose;
+    object_class->finalize = finalize;
+
+    files_view_class->bump_zoom_level = real_bump_zoom_level;
+    files_view_class->can_zoom_in = real_can_zoom_in;
+    files_view_class->can_zoom_out = real_can_zoom_out;
+    files_view_class->click_policy_changed = real_click_policy_changed;
+    files_view_class->sort_directories_first_changed = real_sort_directories_first_changed;
+    files_view_class->get_view_id = real_get_view_id;
+    files_view_class->restore_standard_zoom_level = real_restore_standard_zoom_level;
+    files_view_class->is_zoom_level_default = real_is_zoom_level_default;
+
+    files_model_view_class->get_icon_size = real_get_icon_size;
+    files_model_view_class->get_view_ui = real_get_view_ui;
+    files_model_view_class->scroll_to_item = real_scroll_to_item;
+}
+
+static void
+nautilus_view_icon_controller_init (NautilusViewIconController *self)
 {
-    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (object);
     GtkWidget *content_widget;
-    GtkAdjustment *vadjustment;
-    GActionGroup *view_action_group;
     GtkEventController *controller;
 
-    content_widget = nautilus_files_view_get_content_widget (NAUTILUS_FILES_VIEW (self));
-    vadjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (content_widget));
+    gtk_widget_add_css_class (GTK_WIDGET (self), "nautilus-grid-view");
+    set_click_mode_from_settings (self);
 
-    self->vadjustment = vadjustment;
-    g_signal_connect (vadjustment, "changed", (GCallback) on_vadjustment_changed, self);
-    g_signal_connect (vadjustment, "value-changed", (GCallback) on_vadjustment_changed, self);
+    set_captions_from_preferences (self);
+    g_signal_connect_object (nautilus_icon_view_preferences,
+                             "changed::" NAUTILUS_PREFERENCES_ICON_VIEW_CAPTIONS,
+                             G_CALLBACK (on_captions_preferences_changed),
+                             self,
+                             G_CONNECT_SWAPPED);
 
-    self->model = nautilus_view_model_new ();
 
-    self->view_ui = create_view_ui (self);
-    gtk_widget_show (GTK_WIDGET (self->view_ui));
+    content_widget = nautilus_files_view_get_content_widget (NAUTILUS_FILES_VIEW (self));
 
-    g_signal_connect_swapped (GTK_SELECTION_MODEL (self->model),
-                              "selection-changed",
-                              G_CALLBACK (nautilus_files_view_notify_selection_changed),
-                              NAUTILUS_FILES_VIEW (self));
+    self->view_ui = create_view_ui (self);
 
     controller = GTK_EVENT_CONTROLLER (gtk_gesture_click_new ());
     gtk_widget_add_controller (GTK_WIDGET (content_widget), controller);
@@ -1548,96 +735,12 @@ constructed (GObject *object)
                                      G_N_ELEMENTS (view_icon_actions),
                                      self);
 
-    gtk_widget_show (GTK_WIDGET (self));
-
-    view_action_group = nautilus_files_view_get_action_group (NAUTILUS_FILES_VIEW (self));
-    g_action_map_add_action_entries (G_ACTION_MAP (view_action_group),
-                                     view_icon_actions,
-                                     G_N_ELEMENTS (view_icon_actions),
-                                     self);
     self->zoom_level = get_default_zoom_level ();
     /* Keep the action synced with the actual value, so the toolbar can poll it */
     g_action_group_change_action_state (nautilus_files_view_get_action_group (NAUTILUS_FILES_VIEW (self)),
                                         "zoom-to-level", g_variant_new_int32 (self->zoom_level));
 }
 
-static void
-nautilus_view_icon_controller_class_init (NautilusViewIconControllerClass *klass)
-{
-    GObjectClass *object_class = G_OBJECT_CLASS (klass);
-    NautilusFilesViewClass *files_view_class = NAUTILUS_FILES_VIEW_CLASS (klass);
-
-    object_class->dispose = dispose;
-    object_class->finalize = finalize;
-    object_class->constructed = constructed;
-
-    files_view_class->add_files = real_add_files;
-    files_view_class->begin_loading = real_begin_loading;
-    files_view_class->bump_zoom_level = real_bump_zoom_level;
-    files_view_class->can_zoom_in = real_can_zoom_in;
-    files_view_class->can_zoom_out = real_can_zoom_out;
-    files_view_class->click_policy_changed = real_click_policy_changed;
-    files_view_class->clear = real_clear;
-    files_view_class->file_changed = real_file_changed;
-    files_view_class->get_selection = real_get_selection;
-    /* TODO: remove this get_selection_for_file_transfer, this doesn't even
-     * take into account we could us the view for recursive search :/
-     * CanvasView has the same issue. */
-    files_view_class->get_selection_for_file_transfer = real_get_selection;
-    files_view_class->is_empty = real_is_empty;
-    files_view_class->remove_file = real_remove_file;
-    files_view_class->update_actions_state = real_update_actions_state;
-    files_view_class->reveal_selection = real_reveal_selection;
-    files_view_class->select_all = real_select_all;
-    files_view_class->invert_selection = real_invert_selection;
-    files_view_class->set_selection = real_set_selection;
-    files_view_class->compare_files = real_compare_files;
-    files_view_class->sort_directories_first_changed = real_sort_directories_first_changed;
-    files_view_class->end_file_changes = real_end_file_changes;
-    files_view_class->end_loading = real_end_loading;
-    files_view_class->get_view_id = real_get_view_id;
-    files_view_class->get_first_visible_file = real_get_first_visible_file;
-    files_view_class->scroll_to_file = real_scroll_to_file;
-    files_view_class->select_first = real_select_first;
-    files_view_class->restore_standard_zoom_level = real_restore_standard_zoom_level;
-    files_view_class->is_zoom_level_default = real_is_zoom_level_default;
-    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;
-}
-
-static void
-nautilus_view_icon_controller_init (NautilusViewIconController *self)
-{
-    gtk_widget_add_css_class (GTK_WIDGET (self), "view");
-    gtk_widget_add_css_class (GTK_WIDGET (self), "nautilus-grid-view");
-    set_click_mode_from_settings (self);
-
-    g_signal_connect_object (nautilus_preferences,
-                             "changed::" NAUTILUS_PREFERENCES_DEFAULT_SORT_ORDER,
-                             G_CALLBACK (on_default_sort_order_changed),
-                             self,
-                             G_CONNECT_SWAPPED);
-    g_signal_connect_object (nautilus_preferences,
-                             "changed::" NAUTILUS_PREFERENCES_DEFAULT_SORT_IN_REVERSE_ORDER,
-                             G_CALLBACK (on_default_sort_order_changed),
-                             self,
-                             G_CONNECT_SWAPPED);
-
-    set_captions_from_preferences (self);
-    g_signal_connect_object (nautilus_icon_view_preferences,
-                             "changed::" NAUTILUS_PREFERENCES_ICON_VIEW_CAPTIONS,
-                             G_CALLBACK (on_captions_preferences_changed),
-                             self,
-                             G_CONNECT_SWAPPED);
-
-    g_signal_connect_object (gdk_display_get_clipboard (gdk_display_get_default ()),
-                             "changed",
-                             G_CALLBACK (on_clipboard_owner_changed),
-                             self,
-                             0);
-}
-
 NautilusViewIconController *
 nautilus_view_icon_controller_new (NautilusWindowSlot *slot)
 {
diff --git a/src/nautilus-view-icon-controller.h b/src/nautilus-view-icon-controller.h
index be70c94bd..f0e71da57 100644
--- a/src/nautilus-view-icon-controller.h
+++ b/src/nautilus-view-icon-controller.h
@@ -3,15 +3,14 @@
 #include <glib.h>
 #include <gtk/gtk.h>
 
-#include "nautilus-files-view.h"
+#include "nautilus-files-model-view.h"
 #include "nautilus-window-slot.h"
-#include "nautilus-view-model.h"
 
 G_BEGIN_DECLS
 
 #define NAUTILUS_TYPE_VIEW_ICON_CONTROLLER (nautilus_view_icon_controller_get_type())
 
-G_DECLARE_FINAL_TYPE (NautilusViewIconController, nautilus_view_icon_controller, NAUTILUS, 
VIEW_ICON_CONTROLLER, NautilusFilesView)
+G_DECLARE_FINAL_TYPE (NautilusViewIconController, nautilus_view_icon_controller, NAUTILUS, 
VIEW_ICON_CONTROLLER, NautilusFilesModelView)
 
 NautilusViewIconController *nautilus_view_icon_controller_new (NautilusWindowSlot *slot);
 
diff --git a/src/nautilus-view-model.c b/src/nautilus-view-model.c
index cc7afa9b0..6a80d81d0 100644
--- a/src/nautilus-view-model.c
+++ b/src/nautilus-view-model.c
@@ -205,10 +205,7 @@ get_property (GObject    *object,
     {
         case PROP_SORTER:
         {
-            GtkTreeListRowSorter *row_sorter;
-
-            row_sorter = GTK_TREE_LIST_ROW_SORTER (gtk_sort_list_model_get_sorter (self->sort_model));
-            g_value_set_object (value, gtk_tree_list_row_sorter_get_sorter (row_sorter));
+            g_value_set_object (value, nautilus_view_model_get_sorter (self));
         }
         break;
 
@@ -323,6 +320,15 @@ nautilus_view_model_new ()
     return g_object_new (NAUTILUS_TYPE_VIEW_MODEL, NULL);
 }
 
+GtkSorter *
+nautilus_view_model_get_sorter (NautilusViewModel *self)
+{
+    GtkTreeListRowSorter *row_sorter;
+
+    row_sorter = GTK_TREE_LIST_ROW_SORTER (gtk_sort_list_model_get_sorter (self->sort_model));
+    return row_sorter != NULL ? gtk_tree_list_row_sorter_get_sorter (row_sorter) : NULL;
+}
+
 void
 nautilus_view_model_set_sorter (NautilusViewModel *self,
                                 GtkSorter         *sorter)
diff --git a/src/nautilus-view-model.h b/src/nautilus-view-model.h
index 78cc747e8..70efbf684 100644
--- a/src/nautilus-view-model.h
+++ b/src/nautilus-view-model.h
@@ -12,6 +12,7 @@ G_DECLARE_FINAL_TYPE (NautilusViewModel, nautilus_view_model, NAUTILUS, VIEW_MOD
 
 NautilusViewModel * nautilus_view_model_new (void);
 
+GtkSorter *nautilus_view_model_get_sorter (NautilusViewModel *self);
 void nautilus_view_model_set_sorter (NautilusViewModel *self,
                                      GtkSorter         *sorter);
 void nautilus_view_model_sort (NautilusViewModel *self);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]