[nautilus/wip/antoniof/new-list-view-without-expanders: 1/12] view-icon-controller: Abstract sharable code




commit 97d8a8c5389241294650a021b949a6b4777bb93b
Author: António Fernandes <antoniof gnome org>
Date:   Mon Jun 6 11:31:12 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.
    
    In order to avoid code duplication and still keep the NautilusFilesView
    class agnostic of the widgets used by final classes, create an abstract
    NautilusListBase class.
    
    But this abstract class needs to interact with the item widgets, which
    are going to be different between views. To resolve this, an abstract
    NautilusViewCell class is created for the item widgets, which is also
    going to be used for the new list view column cells.

 src/meson.build                                |    4 +
 src/nautilus-list-base-private.h               |   33 +
 src/nautilus-list-base.c                       | 1321 ++++++++++++++++++++++
 src/nautilus-list-base.h                       |   27 +
 src/nautilus-types.h                           |    3 +-
 src/nautilus-view-cell.c                       |  178 +++
 src/nautilus-view-cell.h                       |   31 +
 src/nautilus-view-icon-controller.c            | 1414 +++---------------------
 src/nautilus-view-icon-controller.h            |    5 +-
 src/nautilus-view-icon-item-ui.c               |  197 +---
 src/nautilus-view-icon-item-ui.h               |   10 +-
 src/nautilus-view-model.c                      |    8 +-
 src/nautilus-view-model.h                      |    1 +
 src/resources/ui/nautilus-view-icon-item-ui.ui |    2 +-
 14 files changed, 1786 insertions(+), 1448 deletions(-)
---
diff --git a/src/meson.build b/src/meson.build
index 35a0dce98..3525c2f7c 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -83,6 +83,8 @@ libnautilus_sources = [
   'nautilus-floating-bar.h',
   'nautilus-freedesktop-dbus.c',
   'nautilus-freedesktop-dbus.h',
+  'nautilus-list-base.c',
+  'nautilus-list-base.h',
   'nautilus-list-model.c',
   'nautilus-list-model.h',
   'nautilus-list-view.c',
@@ -122,6 +124,8 @@ libnautilus_sources = [
   'nautilus-trash-bar.h',
   'nautilus-view.c',
   'nautilus-view.h',
+  'nautilus-view-cell.c',
+  'nautilus-view-cell.h',
   'nautilus-view-icon-controller.c',
   'nautilus-view-icon-controller.h',
   'nautilus-view-icon-item-ui.c',
diff --git a/src/nautilus-list-base-private.h b/src/nautilus-list-base-private.h
new file mode 100644
index 000000000..0d5bbc7b5
--- /dev/null
+++ b/src/nautilus-list-base-private.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 The Files authors
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include "nautilus-list-base.h"
+#include "nautilus-view-model.h"
+#include "nautilus-view-cell.h"
+
+/*
+ * Private header to be included only by subclasses.
+ */
+
+G_BEGIN_DECLS
+
+/* Methods */
+NautilusViewModel *nautilus_list_base_get_model     (NautilusListBase *self);
+void               nautilus_list_base_set_icon_size (NautilusListBase *self,
+                                                            gint                    icon_size);
+void               nautilus_list_base_setup_gestures (NautilusListBase *self);
+
+/* 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);
+void                          setup_cell_common                 (GtkListItem      *listitem,
+                                                                 NautilusViewCell *cell);
+
+G_END_DECLS
diff --git a/src/nautilus-list-base.c b/src/nautilus-list-base.c
new file mode 100644
index 000000000..2e513a2eb
--- /dev/null
+++ b/src/nautilus-list-base.c
@@ -0,0 +1,1321 @@
+/*
+ * Copyright (C) 2022 The Files authors
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include "nautilus-list-base-private.h"
+
+#include "nautilus-clipboard.h"
+#include "nautilus-view-cell.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"
+
+/**
+ * NautilusListBase:
+ *
+ * Abstract class containing shared code for #NautilusFilesView implementations
+ * using a #GtkListBase-derived widget (e.g. GtkGridView, GtkColumnView) which
+ * takes a #NautilusViewModel instance as its model and and a #NautilusViewCell
+ * instance as #GtkListItem:child.
+ *
+ * It has been has been created to avoid code duplication in implementations,
+ * while keeping #NautilusFilesView implementation-agnostic (should the need for
+ * non-#GtkListBase views arise).
+ */
+
+typedef struct _NautilusListBasePrivate NautilusListBasePrivate;
+struct _NautilusListBasePrivate
+{
+    NautilusViewModel *model;
+
+    GList *cut_files;
+
+    guint scroll_to_file_handle_id;
+    guint prioritize_thumbnailing_handle_id;
+    GtkAdjustment *vadjustment;
+
+    gboolean single_click_mode;
+    gboolean activate_on_release;
+    gboolean deny_background_click;
+};
+
+G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (NautilusListBase, nautilus_list_base, 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)
+{
+    return NAUTILUS_VIEW_ITEM_MODEL (g_list_model_get_item (model, position));
+}
+
+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 (NautilusListBase *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_list_base_set_icon_size (NautilusListBase *self,
+                                  gint              icon_size)
+{
+    GListModel *model;
+    guint n_items;
+
+    model = G_LIST_MODEL (nautilus_list_base_get_model (NAUTILUS_LIST_BASE (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);
+    }
+}
+
+/* GtkListBase changes selection only with the primary button, and only after
+ * release. But we need to antecipate selection earlier if we are to activate it
+ * or open its context menu. This helper should be used in these situations if
+ * it's desirable to act on a multi-item selection, because it preserves it. */
+static void
+select_single_item_if_not_selected (NautilusListBase      *self,
+                                    NautilusViewItemModel *item)
+{
+    NautilusViewModel *model;
+    guint position;
+
+    model = nautilus_list_base_get_model (NAUTILUS_LIST_BASE (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 (GTK_SELECTION_MODEL (model), position, TRUE);
+    }
+}
+
+static void
+activate_selection_on_click (NautilusListBase *self,
+                             gboolean          open_in_new_tab)
+{
+    g_autolist (NautilusFile) selection = NULL;
+    NautilusOpenFlags flags = 0;
+    NautilusFilesView *files_view = NAUTILUS_FILES_VIEW (self);
+
+    selection = nautilus_view_get_selection (NAUTILUS_VIEW (self));
+    if (open_in_new_tab)
+    {
+        flags |= NAUTILUS_OPEN_FLAG_NEW_TAB;
+        flags |= NAUTILUS_OPEN_FLAG_DONT_MAKE_ACTIVE;
+    }
+    nautilus_files_view_activate_files (files_view, selection, flags, TRUE);
+}
+
+static void
+on_item_click_pressed (GtkGestureClick *gesture,
+                       gint             n_press,
+                       gdouble          x,
+                       gdouble          y,
+                       gpointer         user_data)
+{
+    NautilusViewCell *cell = user_data;
+    NautilusListBase *self = NAUTILUS_LIST_BASE (nautilus_view_cell_get_view (cell));
+    NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self);
+    NautilusViewItemModel *item;
+    guint button;
+    GdkModifierType modifiers;
+    gboolean selection_mode;
+
+    item = nautilus_view_cell_get_item (cell);
+    g_return_if_fail (item != NULL);
+    button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture));
+    modifiers = gtk_event_controller_get_current_event_state (GTK_EVENT_CONTROLLER (gesture));
+    selection_mode = (modifiers & (GDK_CONTROL_MASK | GDK_SHIFT_MASK));
+
+    /* Before anything else, store event state to be read by other handlers. */
+    priv->deny_background_click = TRUE;
+    priv->activate_on_release = (priv->single_click_mode &&
+                                 button == GDK_BUTTON_PRIMARY &&
+                                 n_press == 1 &&
+                                 !selection_mode);
+
+    /* It's safe to claim event sequence on press in the following cases because
+     * they don't interfere with touch scrolling. */
+    if (button == GDK_BUTTON_PRIMARY && n_press == 2 && !priv->single_click_mode)
+    {
+        activate_selection_on_click (self, modifiers & GDK_SHIFT_MASK);
+        gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
+    }
+    else if (button == GDK_BUTTON_MIDDLE && n_press == 1)
+    {
+        /* Antecipate selection, if necessary, to activate it. */
+        select_single_item_if_not_selected (self, item);
+        activate_selection_on_click (self, TRUE);
+        gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
+    }
+    else if (button == GDK_BUTTON_SECONDARY && n_press == 1)
+    {
+        gdouble view_x, view_y;
+
+        /* Antecipate selection, if necessary, for the context menu. */
+        select_single_item_if_not_selected (self, item);
+
+        gtk_widget_translate_coordinates (GTK_WIDGET (cell), GTK_WIDGET (self),
+                                          x, y,
+                                          &view_x, &view_y);
+        nautilus_files_view_pop_up_selection_context_menu (NAUTILUS_FILES_VIEW (self),
+                                                           view_x, view_y);
+        gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
+    }
+}
+
+static void
+on_item_click_released (GtkGestureClick *gesture,
+                        gint             n_press,
+                        gdouble          x,
+                        gdouble          y,
+                        gpointer         user_data)
+{
+    NautilusViewCell *cell = user_data;
+    NautilusListBase *self = NAUTILUS_LIST_BASE (nautilus_view_cell_get_view (cell));
+    NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self);
+
+    if (priv->activate_on_release)
+    {
+        NautilusViewModel *model;
+        NautilusViewItemModel *item;
+        guint i;
+
+        model = nautilus_list_base_get_model (NAUTILUS_LIST_BASE (self));
+        item = nautilus_view_cell_get_item (cell);
+        g_return_if_fail (item != NULL);
+        i = nautilus_view_model_get_index (model, item);
+
+        /* Antecipate selection, enforcing single selection of target item. */
+        gtk_selection_model_select_item (GTK_SELECTION_MODEL (model), i, TRUE);
+
+        activate_selection_on_click (self, FALSE);
+    }
+
+    priv->activate_on_release = FALSE;
+    priv->deny_background_click = FALSE;
+}
+
+static void
+on_item_click_stopped (GtkGestureClick *gesture,
+                       gpointer         user_data)
+{
+    NautilusViewCell *cell = user_data;
+    NautilusListBase *self = NAUTILUS_LIST_BASE (nautilus_view_cell_get_view (cell));
+    NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self);
+
+    priv->activate_on_release = FALSE;
+    priv->deny_background_click = FALSE;
+}
+
+static void
+on_view_click_pressed (GtkGestureClick *gesture,
+                       gint             n_press,
+                       gdouble          x,
+                       gdouble          y,
+                       gpointer         user_data)
+{
+    NautilusListBase *self = user_data;
+    NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self);
+    guint button;
+    GdkModifierType modifiers;
+    gboolean selection_mode;
+
+    if (priv->deny_background_click)
+    {
+        /* Item was clicked. */
+        gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
+        return;
+    }
+
+    /* Don't interfere with GtkListBase default selection handling when
+     * holding Ctrl and Shift. */
+    modifiers = gtk_event_controller_get_current_event_state (GTK_EVENT_CONTROLLER (gesture));
+    selection_mode = (modifiers & (GDK_CONTROL_MASK | GDK_SHIFT_MASK));
+    if (!selection_mode)
+    {
+        nautilus_view_set_selection (NAUTILUS_VIEW (self), NULL);
+    }
+
+    button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture));
+    if (button == GDK_BUTTON_SECONDARY)
+    {
+        GtkWidget *event_widget;
+        gdouble view_x;
+        gdouble view_y;
+
+        event_widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
+        gtk_widget_translate_coordinates (event_widget, GTK_WIDGET (self),
+                                          x, y,
+                                          &view_x, &view_y);
+        nautilus_files_view_pop_up_background_context_menu (NAUTILUS_FILES_VIEW (self),
+                                                            view_x, view_y);
+    }
+}
+
+static void
+on_item_longpress_pressed (GtkGestureLongPress *gesture,
+                           gdouble              x,
+                           gdouble              y,
+                           gpointer             user_data)
+{
+    NautilusViewCell *cell = user_data;
+    NautilusListBase *self = NAUTILUS_LIST_BASE (nautilus_view_cell_get_view (cell));
+    GtkWidget *event_widget;
+    gdouble view_x;
+    gdouble view_y;
+
+    event_widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
+
+    gtk_widget_translate_coordinates (event_widget,
+                                      GTK_WIDGET (self),
+                                      x, y, &view_x, &view_y);
+
+    nautilus_files_view_pop_up_selection_context_menu (NAUTILUS_FILES_VIEW (self),
+                                                       view_x, view_y);
+    gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
+}
+
+static void
+on_view_longpress_pressed (GtkGestureLongPress *gesture,
+                           gdouble              x,
+                           gdouble              y,
+                           gpointer             user_data)
+{
+    NautilusListBase *self = NAUTILUS_LIST_BASE (user_data);
+    GtkWidget *event_widget;
+    gdouble view_x;
+    gdouble view_y;
+
+    event_widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
+
+    gtk_widget_translate_coordinates (event_widget,
+                                      GTK_WIDGET (self),
+                                      x, y, &view_x, &view_y);
+
+    nautilus_view_set_selection (NAUTILUS_VIEW (self), NULL);
+    nautilus_files_view_pop_up_background_context_menu (NAUTILUS_FILES_VIEW (self),
+                                                        view_x, view_y);
+}
+
+void
+setup_cell_common (GtkListItem      *listitem,
+                   NautilusViewCell *cell)
+{
+    GtkEventController *controller;
+
+    g_object_bind_property (listitem, "item",
+                            cell, "item",
+                            G_BINDING_SYNC_CREATE);
+    gtk_list_item_set_child (listitem, GTK_WIDGET (cell));
+
+    controller = GTK_EVENT_CONTROLLER (gtk_gesture_click_new ());
+    gtk_widget_add_controller (GTK_WIDGET (cell), controller);
+    gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_BUBBLE);
+    gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (controller), 0);
+    g_signal_connect (controller, "pressed", G_CALLBACK (on_item_click_pressed), cell);
+    g_signal_connect (controller, "stopped", G_CALLBACK (on_item_click_stopped), cell);
+    g_signal_connect (controller, "released", G_CALLBACK (on_item_click_released), cell);
+
+    controller = GTK_EVENT_CONTROLLER (gtk_gesture_long_press_new ());
+    gtk_widget_add_controller (GTK_WIDGET (cell), controller);
+    gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_BUBBLE);
+    gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (controller), TRUE);
+    g_signal_connect (controller, "pressed", G_CALLBACK (on_item_longpress_pressed), cell);
+}
+
+static void
+nautilus_list_base_scroll_to_item (NautilusListBase *self,
+                                   guint             position)
+{
+    NAUTILUS_LIST_BASE_CLASS (G_OBJECT_GET_CLASS (self))->scroll_to_item (self, position);
+}
+
+static guint
+nautilus_list_base_get_icon_size (NautilusListBase *self)
+{
+    return NAUTILUS_LIST_BASE_CLASS (G_OBJECT_GET_CLASS (self))->get_icon_size (self);
+}
+
+static GtkWidget *
+nautilus_list_base_get_view_ui (NautilusListBase *self)
+{
+    return NAUTILUS_LIST_BASE_CLASS (G_OBJECT_GET_CLASS (self))->get_view_ui (self);
+}
+
+typedef struct
+{
+    NautilusListBase *self;
+    GQuark attribute_q;
+} NautilusListBaseSortData;
+
+static void
+real_begin_loading (NautilusFilesView *files_view)
+{
+    /*TODO move this to the files view class begin_loading and hook up? */
+
+
+    /* TODO: This calls sort once, and update_context_menus calls update_actions
+     * which calls the action again
+     */
+    update_sort_order_from_metadata_and_preferences (NAUTILUS_LIST_BASE (files_view));
+
+    /* 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)
+{
+    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)
+{
+    NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self);
+    int click_policy;
+
+    click_policy = g_settings_get_enum (nautilus_preferences,
+                                        NAUTILUS_PREFERENCES_CLICK_POLICY);
+
+    priv->single_click_mode = (click_policy == NAUTILUS_CLICK_POLICY_SINGLE);
+}
+
+static void
+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);
+    NautilusListBasePrivate *priv = nautilus_list_base_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)
+{
+    NautilusListBase *self = NAUTILUS_LIST_BASE (files_view);
+    NautilusListBasePrivate *priv = nautilus_list_base_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)
+{
+    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)
+{
+}
+
+static void
+real_remove_file (NautilusFilesView *files_view,
+                  NautilusFile      *file,
+                  NautilusDirectory *directory)
+{
+    NautilusListBase *self = NAUTILUS_LIST_BASE (files_view);
+    NautilusListBasePrivate *priv = nautilus_list_base_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 (NautilusListBase *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_list_base_get_icon_size (self));
+        g_queue_push_tail (models, item_model);
+    }
+
+    return models;
+}
+
+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 (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)
+{
+    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)
+{
+    NautilusListBasePrivate *priv = nautilus_list_base_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)
+{
+    NautilusListBase *self = NAUTILUS_LIST_BASE (files_view);
+
+    nautilus_list_base_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_list_base_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)
+{
+    NautilusListBase *self = NAUTILUS_LIST_BASE (files_view);
+    NautilusListBasePrivate *priv = nautilus_list_base_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_GRID_ICON_SIZE_SMALL);
+    item2 = nautilus_view_item_model_new (file2, NAUTILUS_GRID_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);
+    NautilusListBase *self = NAUTILUS_LIST_BASE (files_view);
+    NautilusListBasePrivate *priv = nautilus_list_base_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 (NautilusListBase *self)
+{
+    NautilusListBasePrivate *priv = nautilus_list_base_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_list_base_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)
+{
+    NautilusListBase *self = NAUTILUS_LIST_BASE (files_view);
+    NautilusListBasePrivate *priv = nautilus_list_base_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
+{
+    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)
+{
+    NautilusListBase *self = data->view;
+    NautilusListBasePrivate *priv = nautilus_list_base_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_list_base_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)
+{
+    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
+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_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_list_base_scroll_to_item (self, 0);
+    }
+}
+
+static void
+real_select_first (NautilusFilesView *files_view)
+{
+    NautilusListBase *self = NAUTILUS_LIST_BASE (files_view);
+    NautilusListBasePrivate *priv = nautilus_list_base_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 (NautilusListBase *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)
+{
+    NautilusListBase *self = NAUTILUS_LIST_BASE (files_view);
+    NautilusListBasePrivate *priv = nautilus_list_base_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)
+{
+    NautilusListBase *self = NAUTILUS_LIST_BASE (files_view);
+    NautilusListBasePrivate *priv = nautilus_list_base_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_list_base_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_list_base_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)
+{
+    NautilusListBase *self = NAUTILUS_LIST_BASE (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_list_base_get_view_ui (self),
+                           "move-cursor", step, count, &handled);
+}
+
+static void
+default_sort_order_changed_callback (NautilusListBase *self)
+{
+    update_sort_order_from_metadata_and_preferences (self);
+}
+
+static void
+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_OBJECT_CLASS (nautilus_list_base_parent_class)->dispose (object);
+}
+
+static void
+nautilus_list_base_finalize (GObject *object)
+{
+    NautilusListBase *self = NAUTILUS_LIST_BASE (object);
+    NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self);
+
+    g_clear_list (&priv->cut_files, g_object_unref);
+
+    G_OBJECT_CLASS (nautilus_list_base_parent_class)->finalize (object);
+}
+
+static gboolean
+prioritize_thumbnailing_on_idle (NautilusListBase *self)
+{
+    NautilusListBasePrivate *priv = nautilus_list_base_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)
+{
+    NautilusListBase *self = NAUTILUS_LIST_BASE (user_data);
+    NautilusListBasePrivate *priv = nautilus_list_base_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_list_base_class_init (NautilusListBaseClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS (klass);
+    NautilusFilesViewClass *files_view_class = NAUTILUS_FILES_VIEW_CLASS (klass);
+
+    object_class->dispose = nautilus_list_base_dispose;
+    object_class->finalize = nautilus_list_base_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->click_policy_changed = real_click_policy_changed;
+    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_list_base_init (NautilusListBase *self)
+{
+    NautilusListBasePrivate *priv = nautilus_list_base_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);
+
+    set_click_mode_from_settings (self);
+}
+
+NautilusViewModel *
+nautilus_list_base_get_model (NautilusListBase *self)
+{
+    NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self);
+
+    return priv->model;
+}
+
+void
+nautilus_list_base_setup_gestures (NautilusListBase *self)
+{
+    GtkWidget *view_ui = nautilus_list_base_get_view_ui (self);
+    GtkEventController *controller;
+
+    controller = GTK_EVENT_CONTROLLER (gtk_gesture_click_new ());
+    gtk_widget_add_controller (view_ui, controller);
+    gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (controller), 0);
+    g_signal_connect (controller, "pressed",
+                      G_CALLBACK (on_view_click_pressed), self);
+
+    controller = GTK_EVENT_CONTROLLER (gtk_gesture_long_press_new ());
+    gtk_widget_add_controller (view_ui, controller);
+    gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (controller), TRUE);
+    g_signal_connect (controller, "pressed",
+                      G_CALLBACK (on_view_longpress_pressed), self);
+}
diff --git a/src/nautilus-list-base.h b/src/nautilus-list-base.h
new file mode 100644
index 000000000..4b1e9d251
--- /dev/null
+++ b/src/nautilus-list-base.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2022 The Files authors
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include "nautilus-files-view.h"
+
+G_BEGIN_DECLS
+
+#define NAUTILUS_TYPE_LIST_BASE (nautilus_list_base_get_type())
+
+G_DECLARE_DERIVABLE_TYPE (NautilusListBase, nautilus_list_base, NAUTILUS, LIST_BASE, NautilusFilesView)
+
+struct _NautilusListBaseClass
+{
+        NautilusFilesViewClass parent_class;
+
+        guint      (*get_icon_size)  (NautilusListBase *self);
+        GtkWidget *(*get_view_ui)    (NautilusListBase *self);
+        void       (*scroll_to_item) (NautilusListBase *self,
+                                      guint                   position);
+};
+
+G_END_DECLS
diff --git a/src/nautilus-types.h b/src/nautilus-types.h
index 073a1576d..1fe935449 100644
--- a/src/nautilus-types.h
+++ b/src/nautilus-types.h
@@ -36,8 +36,9 @@ typedef struct _NautilusClipboard           NautilusClipboard;
 typedef struct _NautilusDirectory           NautilusDirectory;
 typedef struct  NautilusFile                NautilusFile;
 typedef struct  NautilusFileQueue           NautilusFileQueue;
-typedef struct _NautilusFilesView           NautilusFilesView;
+typedef struct _NautilusFilesModelView      NautilusFilesModelView;
 typedef struct _NautilusIconInfo            NautilusIconInfo;
+typedef struct _NautilusListBase            NautilusListBase;
 typedef struct  NautilusMonitor             NautilusMonitor;
 typedef struct _NautilusQuery               NautilusQuery;
 typedef struct _NautilusQueryEditor         NautilusQueryEditor;
diff --git a/src/nautilus-view-cell.c b/src/nautilus-view-cell.c
new file mode 100644
index 000000000..f7a8de069
--- /dev/null
+++ b/src/nautilus-view-cell.c
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2022 The Files authors
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include "nautilus-view-cell.h"
+#include "nautilus-list-base.h"
+
+/**
+ * NautilusViewCell:
+ *
+ * Abstract class of widgets tailored to be set as #GtkListItem:child in a view
+ * which subclasses #NautilusListBase.
+ *
+ * Subclass constructors should take a pointer to the #NautilusListBase view.
+ *
+ * The view is responsible for setting #NautilusViewCell:item. This can be done
+ * using a GBinding from #GtkListItem:item to #NautilusViewCell:item.
+ */
+
+typedef struct _NautilusViewCellPrivate NautilusViewCellPrivate;
+struct _NautilusViewCellPrivate
+{
+    AdwBin parent_instance;
+
+    NautilusListBase *view;
+    NautilusViewItemModel *item;
+
+    gboolean called_once;
+};
+
+
+G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (NautilusViewCell, nautilus_view_cell, ADW_TYPE_BIN)
+
+enum
+{
+    PROP_0,
+    PROP_VIEW,
+    PROP_ITEM,
+    N_PROPS
+};
+
+static void
+nautilus_view_cell_get_property (GObject    *object,
+                                 guint       prop_id,
+                                 GValue     *value,
+                                 GParamSpec *pspec)
+{
+    NautilusViewCell *self = NAUTILUS_VIEW_CELL (object);
+    NautilusViewCellPrivate *priv = nautilus_view_cell_get_instance_private (self);
+
+    switch (prop_id)
+    {
+        case PROP_VIEW:
+        {
+            g_value_set_object (value, priv->view);
+        }
+        break;
+
+        case PROP_ITEM:
+        {
+            g_value_set_object (value, priv->item);
+        }
+        break;
+
+        default:
+        {
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        }
+    }
+}
+
+static void
+nautilus_view_cell_set_property (GObject      *object,
+                                 guint         prop_id,
+                                 const GValue *value,
+                                 GParamSpec   *pspec)
+{
+    NautilusViewCell *self = NAUTILUS_VIEW_CELL (object);
+    NautilusViewCellPrivate *priv = nautilus_view_cell_get_instance_private (self);
+
+    switch (prop_id)
+    {
+        case PROP_VIEW:
+        {
+            priv->view = g_value_get_object (value);
+        }
+        break;
+
+        case PROP_ITEM:
+        {
+            priv->item = g_value_get_object (value);
+        }
+        break;
+
+        default:
+        {
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        }
+    }
+}
+
+static void
+nautilus_view_cell_class_init (NautilusViewCellClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+    object_class->get_property = nautilus_view_cell_get_property;
+    object_class->set_property = nautilus_view_cell_set_property;
+
+    g_object_class_install_property (object_class,
+                                     PROP_VIEW,
+                                     g_param_spec_object ("view",
+                                                          "", "",
+                                                          NAUTILUS_TYPE_LIST_BASE,
+                                                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+    g_object_class_install_property (object_class,
+                                     PROP_ITEM,
+                                     g_param_spec_object ("item",
+                                                          "", "",
+                                                          NAUTILUS_TYPE_VIEW_ITEM_MODEL,
+                                                          G_PARAM_READWRITE));
+}
+
+static void
+nautilus_view_cell_init (NautilusViewCell *self)
+{
+}
+
+gboolean
+nautilus_view_cell_once (NautilusViewCell *self)
+{
+    NautilusViewCellPrivate *priv = nautilus_view_cell_get_instance_private (self);
+
+    if (priv->called_once)
+    {
+        return FALSE;
+    }
+    priv->called_once = TRUE;
+
+    return TRUE;
+}
+
+NautilusListBase *
+nautilus_view_cell_get_view (NautilusViewCell *self)
+{
+    NautilusListBase *view;
+
+    g_return_val_if_fail (NAUTILUS_IS_VIEW_CELL (self), NULL);
+
+    g_object_get (self, "view", &view, NULL);
+
+    return view;
+}
+
+void
+nautilus_view_cell_set_item (NautilusViewCell      *self,
+                             NautilusViewItemModel *item)
+{
+    g_return_if_fail (NAUTILUS_IS_VIEW_CELL (self));
+    g_return_if_fail (item == NULL || NAUTILUS_IS_VIEW_ITEM_MODEL (item));
+
+    g_object_set (self, "item", item, NULL);
+}
+
+NautilusViewItemModel *
+nautilus_view_cell_get_item (NautilusViewCell *self)
+{
+    NautilusViewItemModel *item;
+
+    g_return_val_if_fail (NAUTILUS_IS_VIEW_CELL (self), NULL);
+
+    g_object_get (self, "item", &item, NULL);
+
+    return item;
+}
diff --git a/src/nautilus-view-cell.h b/src/nautilus-view-cell.h
new file mode 100644
index 000000000..4c978bfd4
--- /dev/null
+++ b/src/nautilus-view-cell.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2022 The Files authors
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <libadwaita-1/adwaita.h>
+
+#include "nautilus-types.h"
+#include "nautilus-view-item-model.h"
+
+G_BEGIN_DECLS
+
+#define NAUTILUS_TYPE_VIEW_CELL (nautilus_view_cell_get_type())
+
+G_DECLARE_DERIVABLE_TYPE (NautilusViewCell, nautilus_view_cell, NAUTILUS, VIEW_CELL, AdwBin)
+
+struct _NautilusViewCellClass
+{
+    AdwBinClass parent_class;
+};
+
+NautilusListBase *nautilus_view_cell_get_view (NautilusViewCell *self);
+void nautilus_view_cell_set_item (NautilusViewCell      *self,
+                                  NautilusViewItemModel *item);
+NautilusViewItemModel *nautilus_view_cell_get_item (NautilusViewCell *self);
+gboolean nautilus_view_cell_once (NautilusViewCell *self);
+
+G_END_DECLS
diff --git a/src/nautilus-view-icon-controller.c b/src/nautilus-view-icon-controller.c
index a186a7c53..5e879e170 100644
--- a/src/nautilus-view-icon-controller.c
+++ b/src/nautilus-view-icon-controller.c
@@ -1,193 +1,31 @@
+#include "nautilus-list-base-private.h"
 #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;
+    NautilusListBase 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 single_click_mode;
-    gboolean activate_on_release;
-    gboolean deny_background_click;
+    gboolean directories_first;
 
-    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)
-
-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_LIST_BASE)
 
 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,
@@ -206,258 +44,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 = g_list_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)
@@ -497,637 +83,162 @@ get_icon_size_for_zoom_level (NautilusGridZoomLevel zoom_level)
         {
             return NAUTILUS_GRID_ICON_SIZE_LARGE;
         }
-        break;
-
-        case NAUTILUS_GRID_ZOOM_LEVEL_LARGER:
-        {
-            return NAUTILUS_GRID_ICON_SIZE_LARGER;
-        }
-        break;
-
-        case NAUTILUS_GRID_ZOOM_LEVEL_LARGEST:
-        {
-            return NAUTILUS_GRID_ICON_SIZE_LARGEST;
-        }
-        break;
-    }
-    g_return_val_if_reached (NAUTILUS_GRID_ICON_SIZE_STANDARD);
-}
-
-static gint
-get_default_zoom_level (void)
-{
-    NautilusGridZoomLevel default_zoom_level;
-
-    default_zoom_level = g_settings_get_enum (nautilus_icon_view_preferences,
-                                              NAUTILUS_PREFERENCES_ICON_VIEW_DEFAULT_ZOOM_LEVEL);
-
-    return default_zoom_level;
-}
-
-static void
-set_captions_from_preferences (NautilusViewIconController *self)
-{
-    g_auto (GStrv) value = NULL;
-    gint n_captions_for_zoom_level;
-
-    value = g_settings_get_strv (nautilus_icon_view_preferences,
-                                 NAUTILUS_PREFERENCES_ICON_VIEW_CAPTIONS);
-
-    /* Set a celling on the number of captions depending on the zoom level. */
-    n_captions_for_zoom_level = MIN (self->zoom_level,
-                                     G_N_ELEMENTS (self->caption_attributes));
-
-    /* Reset array to zeros beforehand, as we may not refill all elements. */
-    memset (&self->caption_attributes, 0, sizeof (self->caption_attributes));
-    for (gint i = 0, quark_i = 0;
-         value[i] != NULL && quark_i < n_captions_for_zoom_level;
-         i++)
-    {
-        if (g_strcmp0 (value[i], "none") == 0)
-        {
-            continue;
-        }
-
-        /* Convert to quarks in advance, otherwise each NautilusFile attribute
-         * getter would call g_quark_from_string() once for each file. */
-        self->caption_attributes[quark_i] = g_quark_from_string (value[i]);
-        quark_i++;
-    }
-}
-
-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 = g_list_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)
-{
-    self->zoom_level = new_level;
-
-    /* The zoom level may change how many captions are allowed. Update it before
-     * setting the icon size, under the assumption that NautilusViewIconItemUi
-     * 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_view_update_toolbar_menus (NAUTILUS_FILES_VIEW (self));
-}
-
-static void
-real_restore_standard_zoom_level (NautilusFilesView *files_view)
-{
-    NautilusViewIconController *self;
-
-    self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
-    g_action_group_change_action_state (self->action_group,
-                                        "zoom-to-level",
-                                        g_variant_new_int32 (NAUTILUS_GRID_ZOOM_LEVEL_LARGE));
-}
-
-static gboolean
-real_is_zoom_level_default (NautilusFilesView *files_view)
-{
-    NautilusViewIconController *self;
-    guint icon_size;
-
-    self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
-    icon_size = get_icon_size_for_zoom_level (self->zoom_level);
-
-    return icon_size == NAUTILUS_GRID_ICON_SIZE_LARGE;
-}
-
-static gboolean
-real_can_zoom_in (NautilusFilesView *files_view)
-{
-    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
-
-    return self->zoom_level < NAUTILUS_GRID_ZOOM_LEVEL_LARGEST;
-}
-
-static gboolean
-real_can_zoom_out (NautilusFilesView *files_view)
-{
-    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (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 = g_list_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)
-{
-    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 = g_list_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;
-        }
-    }
-
-    gtk_widget_activate_action (GTK_WIDGET (self->view_ui),
-                                "list.scroll-to-item",
-                                "u",
-                                i);
-
-    return get_rectangle_for_item_ui (self, item_ui);
-}
-
-static void
-set_click_mode_from_settings (NautilusViewIconController *self)
-{
-    int click_policy;
-
-    click_policy = g_settings_get_enum (nautilus_preferences,
-                                        NAUTILUS_PREFERENCES_CLICK_POLICY);
-
-    self->single_click_mode = (click_policy == NAUTILUS_CLICK_POLICY_SINGLE);
-}
-
-static void
-real_click_policy_changed (NautilusFilesView *files_view)
-{
-    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
-    set_click_mode_from_settings (self);
-}
-
-/* GtkListBase changes selection only with the primary button, and only after
- * release. But we need to antecipate selection earlier if we are to activate it
- * or open its context menu. This helper should be used in these situations if
- * it's desirable to act on a multi-item selection, because it preserves it. */
-static void
-select_single_item_if_not_selected (NautilusViewIconController *self,
-                                    NautilusViewItemModel      *item)
-{
-    GtkSelectionModel *selection_model = GTK_SELECTION_MODEL (self->model);
-    guint position;
-
-    position = nautilus_view_model_get_index (self->model, item);
-    if (!gtk_selection_model_is_selected (selection_model, position))
-    {
-        gtk_selection_model_select_item (selection_model, position, TRUE);
-    }
-}
-
-static void
-activate_selection_on_click (NautilusViewIconController *self,
-                             gboolean                    open_in_new_tab)
-{
-    g_autolist (NautilusFile) selection = NULL;
-    NautilusOpenFlags flags = 0;
-    NautilusFilesView *files_view = NAUTILUS_FILES_VIEW (self);
-
-    selection = nautilus_view_get_selection (NAUTILUS_VIEW (self));
-    if (open_in_new_tab)
-    {
-        flags |= NAUTILUS_OPEN_FLAG_NEW_TAB;
-        flags |= NAUTILUS_OPEN_FLAG_DONT_MAKE_ACTIVE;
-    }
-    nautilus_files_view_activate_files (files_view, selection, flags, TRUE);
-}
-
-static void
-on_item_click_pressed (GtkGestureClick *gesture,
-                       gint             n_press,
-                       gdouble          x,
-                       gdouble          y,
-                       gpointer         user_data)
-{
-    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (user_data);
-    GtkWidget *event_widget;
-    NautilusViewItemModel *item_model;
-    guint button;
-    GdkModifierType modifiers;
-    gboolean selection_mode;
-
-    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));
-    button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture));
-    modifiers = gtk_event_controller_get_current_event_state (GTK_EVENT_CONTROLLER (gesture));
-    selection_mode = (modifiers & (GDK_CONTROL_MASK | GDK_SHIFT_MASK));
-
-    /* Before anything else, store event state to be read by other handlers. */
-    self->deny_background_click = TRUE;
-    self->activate_on_release = (self->single_click_mode &&
-                                 button == GDK_BUTTON_PRIMARY &&
-                                 n_press == 1 &&
-                                 !selection_mode);
-
-    /* It's safe to claim event sequence on press in the following cases because
-     * they don't interfere with touch scrolling. */
-    if (button == GDK_BUTTON_PRIMARY && n_press == 2 && !self->single_click_mode)
-    {
-        activate_selection_on_click (self, modifiers & GDK_SHIFT_MASK);
-        gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
-    }
-    else if (button == GDK_BUTTON_MIDDLE && n_press == 1)
-    {
-        /* Antecipate selection, if necessary, to activate it. */
-        select_single_item_if_not_selected (self, item_model);
-        activate_selection_on_click (self, TRUE);
-        gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
-    }
-    else if (button == GDK_BUTTON_SECONDARY && n_press == 1)
-    {
-        gdouble view_x, view_y;
-
-        /* Antecipate selection, if necessary, for the context menu. */
-        select_single_item_if_not_selected (self, item_model);
-
-        gtk_widget_translate_coordinates (event_widget, GTK_WIDGET (self),
-                                          x, y,
-                                          &view_x, &view_y);
-        nautilus_files_view_pop_up_selection_context_menu (NAUTILUS_FILES_VIEW (self),
-                                                           view_x, view_y);
-        gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
-    }
-}
-
-static void
-on_item_click_released (GtkGestureClick *gesture,
-                        gint             n_press,
-                        gdouble          x,
-                        gdouble          y,
-                        gpointer         user_data)
-{
-    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (user_data);
-
-    if (self->activate_on_release)
-    {
-        GtkWidget *event_widget;
-        NautilusViewItemModel *item_model;
-        guint i;
-
-        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);
-
-        /* Antecipate selection, enforcing single selection of target item. */
-        gtk_selection_model_select_item (GTK_SELECTION_MODEL (self->model), i, TRUE);
-
-        activate_selection_on_click (self, FALSE);
-    }
-
-    self->activate_on_release = FALSE;
-    self->deny_background_click = FALSE;
-}
+        break;
 
-static void
-on_item_click_stopped (GtkGestureClick *gesture,
-                       gpointer         user_data)
-{
-    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (user_data);
+        case NAUTILUS_GRID_ZOOM_LEVEL_LARGER:
+        {
+            return NAUTILUS_GRID_ICON_SIZE_LARGER;
+        }
+        break;
 
-    self->activate_on_release = FALSE;
-    self->deny_background_click = FALSE;
+        case NAUTILUS_GRID_ZOOM_LEVEL_LARGEST:
+        {
+            return NAUTILUS_GRID_ICON_SIZE_LARGEST;
+        }
+        break;
+    }
+    g_return_val_if_reached (NAUTILUS_GRID_ICON_SIZE_STANDARD);
 }
 
-static void
-on_view_click_pressed (GtkGestureClick *gesture,
-                       gint             n_press,
-                       gdouble          x,
-                       gdouble          y,
-                       gpointer         user_data)
+static guint
+real_get_icon_size (NautilusListBase *files_model_view)
 {
-    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (user_data);
-    guint button;
-    GdkModifierType modifiers;
-    gboolean selection_mode;
-
-    if (self->deny_background_click)
-    {
-        /* Item was clicked. */
-        gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
-        return;
-    }
-
-    /* Don't interfere with GtkGridView default selection handling when
-     * holding Ctrl and Shift. */
-    modifiers = gtk_event_controller_get_current_event_state (GTK_EVENT_CONTROLLER (gesture));
-    selection_mode = (modifiers & (GDK_CONTROL_MASK | GDK_SHIFT_MASK));
-    if (!selection_mode)
-    {
-        nautilus_view_set_selection (NAUTILUS_VIEW (self), NULL);
-    }
+    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_model_view);
 
-    button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture));
-    if (button == GDK_BUTTON_SECONDARY)
-    {
-        GtkWidget *event_widget;
-        gdouble view_x;
-        gdouble view_y;
-
-        event_widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
-        gtk_widget_translate_coordinates (event_widget, GTK_WIDGET (self),
-                                          x, y,
-                                          &view_x, &view_y);
-        nautilus_files_view_pop_up_background_context_menu (NAUTILUS_FILES_VIEW (self),
-                                                            view_x, view_y);
-    }
+    return get_icon_size_for_zoom_level (self->zoom_level);
 }
 
-static void
-on_longpress_gesture_pressed_callback (GtkGestureLongPress *gesture,
-                                       gdouble              x,
-                                       gdouble              y,
-                                       gpointer             user_data)
+static GtkWidget *
+real_get_view_ui (NautilusListBase *files_model_view)
 {
-    NautilusViewIconController *self;
-    GtkWidget *event_widget;
-    gdouble view_x;
-    gdouble view_y;
+    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_model_view);
 
-    self = NAUTILUS_VIEW_ICON_CONTROLLER (user_data);
-    event_widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
-
-    gtk_widget_translate_coordinates (event_widget,
-                                      GTK_WIDGET (self),
-                                      x, y, &view_x, &view_y);
-    if (NAUTILUS_IS_VIEW_ICON_ITEM_UI (event_widget))
-    {
-        nautilus_files_view_pop_up_selection_context_menu (NAUTILUS_FILES_VIEW (self),
-                                                           view_x, view_y);
-        gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
-    }
-    else
-    {
-        nautilus_view_set_selection (NAUTILUS_VIEW (self), NULL);
-        nautilus_files_view_pop_up_background_context_menu (NAUTILUS_FILES_VIEW (self),
-                                                            view_x, view_y);
-    }
+    return GTK_WIDGET (self->view_ui);
 }
 
-static int
-real_compare_files (NautilusFilesView *files_view,
-                    NautilusFile      *file1,
-                    NautilusFile      *file2)
+static gint
+get_default_zoom_level (void)
 {
-    GActionGroup *view_action_group;
-    GAction *action;
-    const gchar *target_name;
-    gboolean reversed;
-    const SortConstants *sort_constants;
-    gboolean directories_first;
+    NautilusGridZoomLevel default_zoom_level;
 
-    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);
+    default_zoom_level = g_settings_get_enum (nautilus_icon_view_preferences,
+                                              NAUTILUS_PREFERENCES_ICON_VIEW_DEFAULT_ZOOM_LEVEL);
 
-    return nautilus_file_compare_for_sort (file1, file2,
-                                           sort_constants->sort_type,
-                                           directories_first,
-                                           reversed);
+    return default_zoom_level;
 }
 
 static void
-on_clipboard_contents_received (GObject      *source_object,
-                                GAsyncResult *res,
-                                gpointer      user_data)
+set_captions_from_preferences (NautilusViewIconController *self)
 {
-    NautilusFilesView *files_view = NAUTILUS_FILES_VIEW (source_object);
-    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
-    NautilusClipboard *clip;
-    NautilusViewItemModel *item;
+    g_auto (GStrv) value = NULL;
+    gint n_captions_for_zoom_level;
 
-    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);
+    value = g_settings_get_strv (nautilus_icon_view_preferences,
+                                 NAUTILUS_PREFERENCES_ICON_VIEW_CAPTIONS);
 
-    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);
-    }
+    /* Set a celling on the number of captions depending on the zoom level. */
+    n_captions_for_zoom_level = MIN (self->zoom_level,
+                                     G_N_ELEMENTS (self->caption_attributes));
 
-    for (GList *l = self->cut_files; l != NULL; l = l->next)
+    /* Reset array to zeros beforehand, as we may not refill all elements. */
+    memset (&self->caption_attributes, 0, sizeof (self->caption_attributes));
+    for (gint i = 0, quark_i = 0;
+         value[i] != NULL && quark_i < n_captions_for_zoom_level;
+         i++)
     {
-        item = nautilus_view_model_get_item_from_file (self->model, l->data);
-        if (item != NULL)
+        if (g_strcmp0 (value[i], "none") == 0)
         {
-            nautilus_view_item_model_set_cut (item, TRUE);
+            continue;
         }
+
+        /* Convert to quarks in advance, otherwise each NautilusFile attribute
+         * getter would call g_quark_from_string() once for each file. */
+        self->caption_attributes[quark_i] = g_quark_from_string (value[i]);
+        quark_i++;
     }
 }
 
 static void
-update_clipboard_status (NautilusFilesView *files_view)
+set_zoom_level (NautilusViewIconController *self,
+                guint                       new_level)
 {
-    nautilus_files_view_get_clipboard_async (files_view,
-                                             on_clipboard_contents_received,
-                                             NULL);
-}
+    self->zoom_level = new_level;
 
-static void
-on_clipboard_owner_changed (GdkClipboard *clipboard,
-                            gpointer      user_data)
-{
-    update_clipboard_status (NAUTILUS_FILES_VIEW (user_data));
-}
+    /* The zoom level may change how many captions are allowed. Update it before
+     * setting the icon size, under the assumption that NautilusViewIconItemUi
+     * updates captions whenever the icon size is set*/
+    set_captions_from_preferences (self);
 
+    nautilus_list_base_set_icon_size (NAUTILUS_LIST_BASE (self),
+                                      get_icon_size_for_zoom_level (new_level));
+
+    nautilus_files_view_update_toolbar_menus (NAUTILUS_FILES_VIEW (self));
+}
 
 static void
-real_end_loading (NautilusFilesView *files_view,
-                  gboolean           all_files_seen)
+real_restore_standard_zoom_level (NautilusFilesView *files_view)
 {
-    update_clipboard_status (files_view);
+    NautilusViewIconController *self;
+
+    self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
+    g_action_group_change_action_state (self->action_group,
+                                        "zoom-to-level",
+                                        g_variant_new_int32 (NAUTILUS_GRID_ZOOM_LEVEL_LARGE));
 }
 
-static guint
-get_first_visible_item (NautilusViewIconController *self)
+static gboolean
+real_is_zoom_level_default (NautilusFilesView *files_view)
 {
-    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;
+    NautilusViewIconController *self;
+    guint icon_size;
 
-        item = g_list_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;
-            }
-        }
-    }
+    self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
+    icon_size = get_icon_size_for_zoom_level (self->zoom_level);
 
-    return G_MAXUINT;
+    return icon_size == NAUTILUS_GRID_ICON_SIZE_LARGE;
 }
 
-static char *
-real_get_first_visible_file (NautilusFilesView *files_view)
+static gboolean
+real_can_zoom_in (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 = g_list_model_get_item (G_LIST_MODEL (self->model), i);
-        uri = nautilus_file_get_uri (nautilus_view_item_model_get_file (item));
-    }
-    return uri;
+    return self->zoom_level < NAUTILUS_GRID_ZOOM_LEVEL_LARGEST;
 }
 
-typedef struct
+static gboolean
+real_can_zoom_out (NautilusFilesView *files_view)
 {
-    NautilusViewIconController *view;
-    char *uri;
-} ScrollToFileData;
+    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
 
-static void
-scroll_to_file_data_free (ScrollToFileData *data)
-{
-    g_free (data->uri);
-    g_free (data);
+    return self->zoom_level > NAUTILUS_GRID_ZOOM_LEVEL_SMALL;
 }
 
-static gboolean
-scroll_to_file_on_idle (ScrollToFileData *data)
+static void
+real_scroll_to_item (NautilusListBase *files_model_view,
+                     guint             position)
 {
-    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);
+    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_model_view);
 
     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;
+                                position);
 }
 
 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_list_base_get_model (NAUTILUS_LIST_BASE (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
@@ -1136,8 +247,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 */
@@ -1146,86 +257,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_list_base_get_model (NAUTILUS_LIST_BASE (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 = NAUTILUS_VIEW_ITEM_MODEL (g_list_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,
@@ -1254,119 +304,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_list_base_set_icon_size (NAUTILUS_LIST_BASE (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 = g_list_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 = g_list_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 = g_list_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,
@@ -1378,11 +331,9 @@ bind_item_ui (GtkSignalListItemFactory *factory,
     item_ui = gtk_list_item_get_child (listitem);
     item_model = NAUTILUS_VIEW_ITEM_MODEL (gtk_list_item_get_item (listitem));
 
-    nautilus_view_icon_item_ui_set_model (NAUTILUS_VIEW_ICON_ITEM_UI (item_ui),
-                                          item_model);
     nautilus_view_item_model_set_item_ui (item_model, item_ui);
 
-    if (nautilus_view_icon_item_ui_once (NAUTILUS_VIEW_ICON_ITEM_UI (item_ui)))
+    if (nautilus_view_cell_once (NAUTILUS_VIEW_CELL (item_ui)))
     {
         GtkWidget *parent;
 
@@ -1404,13 +355,10 @@ unbind_item_ui (GtkSignalListItemFactory *factory,
                 GtkListItem              *listitem,
                 gpointer                  user_data)
 {
-    NautilusViewIconItemUi *item_ui;
     NautilusViewItemModel *item_model;
 
-    item_ui = NAUTILUS_VIEW_ICON_ITEM_UI (gtk_list_item_get_child (listitem));
     item_model = NAUTILUS_VIEW_ITEM_MODEL (gtk_list_item_get_item (listitem));
 
-    nautilus_view_icon_item_ui_set_model (item_ui, NULL);
     nautilus_view_item_model_set_item_ui (item_model, NULL);
 }
 
@@ -1421,39 +369,28 @@ setup_item_ui (GtkSignalListItemFactory *factory,
 {
     NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (user_data);
     NautilusViewIconItemUi *item_ui;
-    GtkEventController *controller;
 
-    item_ui = nautilus_view_icon_item_ui_new ();
+    item_ui = nautilus_view_icon_item_ui_new (NAUTILUS_LIST_BASE (self));
+    setup_cell_common (listitem, NAUTILUS_VIEW_CELL (item_ui));
+
     nautilus_view_item_ui_set_caption_attributes (item_ui, self->caption_attributes);
-    gtk_list_item_set_child (listitem, GTK_WIDGET (item_ui));
-
-    controller = GTK_EVENT_CONTROLLER (gtk_gesture_click_new ());
-    gtk_widget_add_controller (GTK_WIDGET (item_ui), controller);
-    gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_BUBBLE);
-    gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (controller), 0);
-    g_signal_connect (controller, "pressed", G_CALLBACK (on_item_click_pressed), self);
-    g_signal_connect (controller, "released", G_CALLBACK (on_item_click_released), self);
-    g_signal_connect (controller, "stopped", G_CALLBACK (on_item_click_stopped), self);
-
-    controller = GTK_EVENT_CONTROLLER (gtk_gesture_long_press_new ());
-    gtk_widget_add_controller (GTK_WIDGET (item_ui), controller);
-    gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_BUBBLE);
-    gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (controller), TRUE);
-    g_signal_connect (controller, "pressed", G_CALLBACK (on_longpress_gesture_pressed_callback), self);
 }
 
 static GtkGridView *
 create_view_ui (NautilusViewIconController *self)
 {
+    NautilusViewModel *model;
     GtkListItemFactory *factory;
     GtkWidget *widget;
 
+    model = nautilus_list_base_get_model (NAUTILUS_LIST_BASE (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);
 
@@ -1476,130 +413,35 @@ const GActionEntry view_icon_actions[] =
     { "zoom-to-level", NULL, NULL, "100", action_zoom_to_level }
 };
 
-static void
-constructed (GObject *object)
-{
-    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));
-
-    self->vadjustment = vadjustment;
-    g_signal_connect (vadjustment, "changed", (GCallback) on_vadjustment_changed, self);
-    g_signal_connect (vadjustment, "value-changed", (GCallback) on_vadjustment_changed, self);
-
-    self->model = nautilus_view_model_new ();
-
-    self->view_ui = create_view_ui (self);
-    gtk_widget_show (GTK_WIDGET (self->view_ui));
-
-    g_signal_connect_swapped (GTK_SELECTION_MODEL (self->model),
-                              "selection-changed",
-                              G_CALLBACK (nautilus_files_view_notify_selection_changed),
-                              NAUTILUS_FILES_VIEW (self));
-
-    controller = GTK_EVENT_CONTROLLER (gtk_gesture_click_new ());
-    gtk_widget_add_controller (GTK_WIDGET (content_widget), controller);
-    gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_BUBBLE);
-    gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (controller), 0);
-    g_signal_connect (controller, "pressed",
-                      G_CALLBACK (on_view_click_pressed), self);
-
-    controller = GTK_EVENT_CONTROLLER (gtk_gesture_long_press_new ());
-    gtk_widget_add_controller (GTK_WIDGET (self->view_ui), controller);
-    gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_BUBBLE);
-    gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (controller), TRUE);
-    g_signal_connect (controller, "pressed",
-                      (GCallback) on_longpress_gesture_pressed_callback, self);
-
-    gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (content_widget),
-                                   GTK_WIDGET (self->view_ui));
-
-    self->action_group = nautilus_files_view_get_action_group (NAUTILUS_FILES_VIEW (self));
-    g_action_map_add_action_entries (G_ACTION_MAP (self->action_group),
-                                     view_icon_actions,
-                                     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);
+    NautilusListBaseClass *files_model_view_class = NAUTILUS_LIST_BASE_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;
+
+    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)
 {
-    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);
+    GtkWidget *content_widget;
 
-    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);
+    gtk_widget_add_css_class (GTK_WIDGET (self), "nautilus-grid-view");
 
     set_captions_from_preferences (self);
     g_signal_connect_object (nautilus_icon_view_preferences,
@@ -1608,11 +450,25 @@ nautilus_view_icon_controller_init (NautilusViewIconController *self)
                              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);
+
+    content_widget = nautilus_files_view_get_content_widget (NAUTILUS_FILES_VIEW (self));
+
+    self->view_ui = create_view_ui (self);
+    nautilus_list_base_setup_gestures (NAUTILUS_LIST_BASE (self));
+
+    gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (content_widget),
+                                   GTK_WIDGET (self->view_ui));
+
+    self->action_group = nautilus_files_view_get_action_group (NAUTILUS_FILES_VIEW (self));
+    g_action_map_add_action_entries (G_ACTION_MAP (self->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));
 }
 
 NautilusViewIconController *
diff --git a/src/nautilus-view-icon-controller.h b/src/nautilus-view-icon-controller.h
index be70c94bd..0dfa878e0 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-list-base.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, NautilusListBase)
 
 NautilusViewIconController *nautilus_view_icon_controller_new (NautilusWindowSlot *slot);
 
diff --git a/src/nautilus-view-icon-item-ui.c b/src/nautilus-view-icon-item-ui.c
index d0ebff7b4..4c5897a89 100644
--- a/src/nautilus-view-icon-item-ui.c
+++ b/src/nautilus-view-icon-item-ui.c
@@ -1,13 +1,11 @@
 #include "nautilus-view-icon-item-ui.h"
-#include "nautilus-view-item-model.h"
-#include "nautilus-file.h"
-#include "nautilus-thumbnails.h"
 
 struct _NautilusViewIconItemUi
 {
-    GtkBox parent_instance;
+    NautilusViewCell parent_instance;
+
+    GSignalGroup *item_signal_group;
 
-    NautilusViewItemModel *model;
     GQuark *caption_attributes;
 
     GtkWidget *fixed_height_box;
@@ -16,24 +14,16 @@ struct _NautilusViewIconItemUi
     GtkWidget *first_caption;
     GtkWidget *second_caption;
     GtkWidget *third_caption;
-
-    gboolean called_once;
 };
 
-G_DEFINE_TYPE (NautilusViewIconItemUi, nautilus_view_icon_item_ui, GTK_TYPE_BOX)
-
-enum
-{
-    PROP_0,
-    PROP_MODEL,
-    N_PROPS
-};
+G_DEFINE_TYPE (NautilusViewIconItemUi, nautilus_view_icon_item_ui, NAUTILUS_TYPE_VIEW_CELL)
 
 #define EXTRA_WIDTH_FOR_TEXT 36
 
 static void
 update_icon (NautilusViewIconItemUi *self)
 {
+    NautilusViewItemModel *item;
     NautilusFileIconFlags flags;
     g_autoptr (GdkPaintable) icon_paintable = NULL;
     GtkStyleContext *style_context;
@@ -41,8 +31,10 @@ update_icon (NautilusViewIconItemUi *self)
     guint icon_size;
     g_autofree gchar *thumbnail_path = NULL;
 
-    file = nautilus_view_item_model_get_file (self->model);
-    icon_size = nautilus_view_item_model_get_icon_size (self->model);
+    item = nautilus_view_cell_get_item (NAUTILUS_VIEW_CELL (self));
+    g_return_if_fail (item != NULL);
+    file = nautilus_view_item_model_get_file (item);
+    icon_size = nautilus_view_item_model_get_icon_size (item);
     flags = NAUTILUS_FILE_ICON_FLAGS_USE_THUMBNAILS |
             NAUTILUS_FILE_ICON_FLAGS_FORCE_THUMBNAIL_SIZE |
             NAUTILUS_FILE_ICON_FLAGS_USE_EMBLEMS |
@@ -76,6 +68,7 @@ update_icon (NautilusViewIconItemUi *self)
 static void
 update_captions (NautilusViewIconItemUi *self)
 {
+    NautilusViewItemModel *item;
     NautilusFile *file;
     GtkWidget * const caption_labels[] =
     {
@@ -85,7 +78,9 @@ update_captions (NautilusViewIconItemUi *self)
     };
     G_STATIC_ASSERT (G_N_ELEMENTS (caption_labels) == NAUTILUS_VIEW_ICON_N_CAPTIONS);
 
-    file = nautilus_view_item_model_get_file (self->model);
+    item = nautilus_view_cell_get_item (NAUTILUS_VIEW_CELL (self));
+    g_return_if_fail (item != NULL);
+    file = nautilus_view_item_model_get_file (item);
     for (guint i = 0; i < NAUTILUS_VIEW_ICON_N_CAPTIONS; i++)
     {
         GQuark attribute_q = self->caption_attributes[i];
@@ -105,9 +100,12 @@ update_captions (NautilusViewIconItemUi *self)
 static void
 on_file_changed (NautilusViewIconItemUi *self)
 {
+    NautilusViewItemModel *item;
     NautilusFile *file;
 
-    file = nautilus_view_item_model_get_file (self->model);
+    item = nautilus_view_cell_get_item (NAUTILUS_VIEW_CELL (self));
+    g_return_if_fail (item != NULL);
+    file = nautilus_view_item_model_get_file (item);
 
     update_icon (self);
 
@@ -117,25 +115,18 @@ on_file_changed (NautilusViewIconItemUi *self)
 }
 
 static void
-on_view_item_size_changed (GObject    *object,
-                           GParamSpec *pspec,
-                           gpointer    user_data)
+on_item_size_changed (NautilusViewIconItemUi *self)
 {
-    NautilusViewIconItemUi *self = NAUTILUS_VIEW_ICON_ITEM_UI (user_data);
-
     update_icon (self);
     update_captions (self);
 }
 
 static void
-on_view_item_is_cut_changed (GObject    *object,
-                             GParamSpec *pspec,
-                             gpointer    user_data)
+on_item_is_cut_changed (NautilusViewIconItemUi *self)
 {
-    NautilusViewIconItemUi *self = NAUTILUS_VIEW_ICON_ITEM_UI (user_data);
     gboolean is_cut;
 
-    g_object_get (object, "is-cut", &is_cut, NULL);
+    g_object_get (self, "is-cut", &is_cut, NULL);
     if (is_cut)
     {
         gtk_widget_add_css_class (self->icon, "cut");
@@ -146,100 +137,15 @@ on_view_item_is_cut_changed (GObject    *object,
     }
 }
 
-static void
-set_model (NautilusViewIconItemUi *self,
-           NautilusViewItemModel  *model);
-
 static void
 finalize (GObject *object)
 {
     NautilusViewIconItemUi *self = (NautilusViewIconItemUi *) object;
 
-    set_model (self, NULL);
+    g_object_unref (self->item_signal_group);
     G_OBJECT_CLASS (nautilus_view_icon_item_ui_parent_class)->finalize (object);
 }
 
-static void
-get_property (GObject    *object,
-              guint       prop_id,
-              GValue     *value,
-              GParamSpec *pspec)
-{
-    NautilusViewIconItemUi *self = NAUTILUS_VIEW_ICON_ITEM_UI (object);
-
-    switch (prop_id)
-    {
-        case PROP_MODEL:
-        {
-            g_value_set_object (value, self->model);
-        }
-        break;
-
-        default:
-            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-    }
-}
-
-static void
-set_model (NautilusViewIconItemUi *self,
-           NautilusViewItemModel  *model)
-{
-    NautilusFile *file;
-
-    if (self->model == model)
-    {
-        return;
-    }
-
-    if (self->model != NULL)
-    {
-        g_signal_handlers_disconnect_by_data (self->model, self);
-        g_clear_object (&self->model);
-    }
-
-    if (model == NULL)
-    {
-        return;
-    }
-
-    self->model = g_object_ref (model);
-
-    file = nautilus_view_item_model_get_file (self->model);
-
-    update_icon (self);
-    gtk_label_set_text (GTK_LABEL (self->label),
-                        nautilus_file_get_display_name (file));
-    update_captions (self);
-
-    g_signal_connect (self->model, "notify::icon-size",
-                      (GCallback) on_view_item_size_changed, self);
-    g_signal_connect (self->model, "notify::is-cut",
-                      (GCallback) on_view_item_is_cut_changed, self);
-    g_signal_connect_swapped (self->model, "file-changed",
-                              (GCallback) on_file_changed, self);
-}
-
-static void
-set_property (GObject      *object,
-              guint         prop_id,
-              const GValue *value,
-              GParamSpec   *pspec)
-{
-    NautilusViewIconItemUi *self = NAUTILUS_VIEW_ICON_ITEM_UI (object);
-
-    switch (prop_id)
-    {
-        case PROP_MODEL:
-        {
-            set_model (self, g_value_get_object (value));
-        }
-        break;
-
-        default:
-            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-    }
-}
-
 static void
 nautilus_view_icon_item_ui_class_init (NautilusViewIconItemUiClass *klass)
 {
@@ -247,16 +153,6 @@ nautilus_view_icon_item_ui_class_init (NautilusViewIconItemUiClass *klass)
     GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
 
     object_class->finalize = finalize;
-    object_class->get_property = get_property;
-    object_class->set_property = set_property;
-
-    g_object_class_install_property (object_class,
-                                     PROP_MODEL,
-                                     g_param_spec_object ("model",
-                                                          "Item model",
-                                                          "The item model that this UI reprensents",
-                                                          NAUTILUS_TYPE_VIEW_ITEM_MODEL,
-                                                          G_PARAM_READWRITE));
 
     gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/nautilus/ui/nautilus-view-icon-item-ui.ui");
 
@@ -273,6 +169,22 @@ nautilus_view_icon_item_ui_init (NautilusViewIconItemUi *self)
 {
     gtk_widget_init_template (GTK_WIDGET (self));
 
+    /* Connect automatically to an item. */
+    self->item_signal_group = g_signal_group_new (NAUTILUS_TYPE_VIEW_ITEM_MODEL);
+    g_signal_group_connect_swapped (self->item_signal_group, "notify::icon-size",
+                                    (GCallback) on_item_size_changed, self);
+    g_signal_group_connect_swapped (self->item_signal_group, "notify::is-cut",
+                                    (GCallback) on_item_is_cut_changed, self);
+    g_signal_group_connect_swapped (self->item_signal_group, "file-changed",
+                                    (GCallback) on_file_changed, self);
+    g_signal_connect_object (self->item_signal_group, "bind",
+                             (GCallback) on_file_changed, self,
+                             G_CONNECT_SWAPPED);
+
+    g_object_bind_property (self, "item",
+                            self->item_signal_group, "target",
+                            G_BINDING_SYNC_CREATE);
+
 #if PANGO_VERSION_CHECK (1, 44, 4)
     {
         PangoAttrList *attr_list;
@@ -288,26 +200,11 @@ nautilus_view_icon_item_ui_init (NautilusViewIconItemUi *self)
 }
 
 NautilusViewIconItemUi *
-nautilus_view_icon_item_ui_new (void)
-{
-    return g_object_new (NAUTILUS_TYPE_VIEW_ICON_ITEM_UI, NULL);
-}
-
-void
-nautilus_view_icon_item_ui_set_model (NautilusViewIconItemUi *self,
-                                      NautilusViewItemModel  *model)
-{
-    g_object_set (self, "model", model, NULL);
-}
-
-NautilusViewItemModel *
-nautilus_view_icon_item_ui_get_model (NautilusViewIconItemUi *self)
+nautilus_view_icon_item_ui_new (NautilusListBase *view)
 {
-    NautilusViewItemModel *model = NULL;
-
-    g_object_get (self, "model", &model, NULL);
-
-    return model;
+    return g_object_new (NAUTILUS_TYPE_VIEW_ICON_ITEM_UI,
+                         "view", view,
+                         NULL);
 }
 
 void
@@ -316,15 +213,3 @@ nautilus_view_item_ui_set_caption_attributes (NautilusViewIconItemUi *self,
 {
     self->caption_attributes = attrs;
 }
-
-gboolean
-nautilus_view_icon_item_ui_once (NautilusViewIconItemUi *self)
-{
-    if (self->called_once)
-    {
-        return FALSE;
-    }
-
-    self->called_once = TRUE;
-    return TRUE;
-}
diff --git a/src/nautilus-view-icon-item-ui.h b/src/nautilus-view-icon-item-ui.h
index edae77dc8..5f6d1ad93 100644
--- a/src/nautilus-view-icon-item-ui.h
+++ b/src/nautilus-view-icon-item-ui.h
@@ -3,7 +3,7 @@
 #include <glib.h>
 #include <gtk/gtk.h>
 
-#include "nautilus-view-item-model.h"
+#include "nautilus-view-cell.h"
 
 G_BEGIN_DECLS
 
@@ -17,14 +17,10 @@ enum
 
 #define NAUTILUS_TYPE_VIEW_ICON_ITEM_UI (nautilus_view_icon_item_ui_get_type())
 
-G_DECLARE_FINAL_TYPE (NautilusViewIconItemUi, nautilus_view_icon_item_ui, NAUTILUS, VIEW_ICON_ITEM_UI, 
GtkBox)
+G_DECLARE_FINAL_TYPE (NautilusViewIconItemUi, nautilus_view_icon_item_ui, NAUTILUS, VIEW_ICON_ITEM_UI, 
NautilusViewCell)
 
-NautilusViewIconItemUi * nautilus_view_icon_item_ui_new (void);
-void nautilus_view_icon_item_ui_set_model (NautilusViewIconItemUi *self,
-                                           NautilusViewItemModel  *model);
-NautilusViewItemModel *nautilus_view_icon_item_ui_get_model (NautilusViewIconItemUi *self);
+NautilusViewIconItemUi * nautilus_view_icon_item_ui_new (NautilusListBase *view);
 void nautilus_view_item_ui_set_caption_attributes (NautilusViewIconItemUi *self,
                                                    GQuark                 *attrs);
-gboolean nautilus_view_icon_item_ui_once (NautilusViewIconItemUi *self);
 
 G_END_DECLS
diff --git a/src/nautilus-view-model.c b/src/nautilus-view-model.c
index 2d4deee02..55c6cf8b6 100644
--- a/src/nautilus-view-model.c
+++ b/src/nautilus-view-model.c
@@ -172,7 +172,7 @@ get_property (GObject    *object,
     {
         case PROP_SORTER:
         {
-            g_value_set_object (value, self->sorter);
+            g_value_set_object (value, nautilus_view_model_get_sorter (self));
         }
         break;
 
@@ -275,6 +275,12 @@ nautilus_view_model_new ()
     return g_object_new (NAUTILUS_TYPE_VIEW_MODEL, NULL);
 }
 
+GtkSorter *
+nautilus_view_model_get_sorter (NautilusViewModel *self)
+{
+    return self->sorter;
+}
+
 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 1771cd6e2..d76c3987d 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);
 NautilusViewItemModel * nautilus_view_model_get_item_from_file (NautilusViewModel *self,
diff --git a/src/resources/ui/nautilus-view-icon-item-ui.ui b/src/resources/ui/nautilus-view-icon-item-ui.ui
index 0b0f18d26..e614c538f 100644
--- a/src/resources/ui/nautilus-view-icon-item-ui.ui
+++ b/src/resources/ui/nautilus-view-icon-item-ui.ui
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
   <requires lib="gtk" version="4.0"/>
-  <template class="NautilusViewIconItemUi" parent="GtkBox">
+  <template class="NautilusViewIconItemUi" parent="NautilusViewCell">
     <child>
       <object class="AdwClamp">
         <property name="maximum-size">0</property>


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