[nautilus/wip/antoniof/new-list-view-without-expanders: 18/23] list-view: Reimplement using GtkColumnView




commit fdbd6a36461f6c14c13038ca65b2f103ff46b1a6
Author: António Fernandes <antoniof gnome org>
Date:   Thu Apr 7 17:56:01 2022 +0100

    list-view: Reimplement using GtkColumnView
    
    GtkTreeView, while still available in GTK 4, is more limited in some
    more specialized situations which we have been relying on, such as
    drag-and-drop and high DPI icons.
    
    It's also been our long desire to adopt GListModel-based list widgets
    in order to unlock features and bugfixes which would have been
    basically impossible to do with GtkTreeView.
    
    We are thus dropping GtkTreeView for good and adopting GtkColumnView.
    
    The new implementation is radically different; almost no code remains
    from the old implementation. However, the new implementation has full
    feature parity with the old one with two exceptions:
    
        1. Expand subfolders as a tree: WIP in another branch.
        2. Performance for large number of items: WIP branch in GTK.
    
    Same as the old implementation, it still lacks drag-and-drop support,
    which is yet to be reimplemented for GTK4.
    
    The new implementation also implements new features which were but
    a dream in GtkTreeView:
    
      - Rubberband-selection.
      - Empty space inbetween and around the list of items to open context
        menu, start rubberband selection, drop items, clear selection...
      - File names in search results and recents are no longer squashed by
        the "Original location" column containing long paths. Instead, the
        original location is runs parallel to the filename.
      - With the location column gone, the size column can be displayed
        again in these two special locations.
      - Full-text-search snippets no longer compete for horizontal space
        with the filenames, but are displayed as accent-colored subtitles.
      - Sort order can be changed from the view menu, same as in grid view.
      - Per-folder sorting is shared with the grid view now, fixing an old
        inconsistency.
      - A starring animation ☆★
    
    Closes: https://gitlab.gnome.org/GNOME/nautilus/-/issues/320

 po/POTFILES.in                                  |    1 -
 src/meson.build                                 |   13 +-
 src/nautilus-dnd.c                              |    1 -
 src/nautilus-files-view.c                       |    2 +-
 src/nautilus-label-cell.c                       |  158 +
 src/nautilus-label-cell.h                       |   23 +
 src/nautilus-list-model.c                       | 1883 ----------
 src/nautilus-list-model.h                       |  115 -
 src/nautilus-list-view-dnd.c                    |  307 --
 src/nautilus-list-view-dnd.h                    |   36 -
 src/nautilus-list-view-private.h                |   80 -
 src/nautilus-list-view.c                        | 4170 ++++-------------------
 src/nautilus-list-view.h                        |   48 +-
 src/nautilus-name-cell.c                        |  291 ++
 src/nautilus-name-cell.h                        |   23 +
 src/nautilus-star-cell.c                        |  173 +
 src/nautilus-star-cell.h                        |   19 +
 src/nautilus-tree-view-drag-dest.c              | 1305 -------
 src/nautilus-tree-view-drag-dest.h              |   98 -
 src/nautilus-view-cell.c                        |    1 +
 src/resources/css/Adwaita.css                   |   70 +-
 src/resources/nautilus.gresource.xml            |    1 +
 src/resources/ui/nautilus-name-cell.ui          |   88 +
 src/resources/ui/nautilus-preferences-window.ui |    2 +-
 24 files changed, 1545 insertions(+), 7363 deletions(-)
---
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 878a9022a..19a277eeb 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -61,7 +61,6 @@ src/nautilus-shell-search-provider.c
 src/nautilus-special-location-bar.c
 src/nautilus-toolbar.c
 src/nautilus-trash-bar.c
-src/nautilus-tree-view-drag-dest.c
 src/nautilus-ui-utilities.c
 src/nautilus-vfs-file.c
 src/nautilus-view.c
diff --git a/src/meson.build b/src/meson.build
index 910b64842..edcfe0d91 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -87,19 +87,18 @@ libnautilus_sources = [
   'nautilus-grid-cell.h',
   'nautilus-grid-view.c',
   'nautilus-grid-view.h',
+  'nautilus-label-cell.c',
+  'nautilus-label-cell.h',
   'nautilus-list-base.c',
   'nautilus-list-base.h',
-  'nautilus-list-model.c',
-  'nautilus-list-model.h',
   'nautilus-list-view.c',
   'nautilus-list-view.h',
-  'nautilus-list-view-private.h',
-  'nautilus-list-view-dnd.c',
-  'nautilus-list-view-dnd.h',
   'nautilus-location-entry.c',
   'nautilus-location-entry.h',
   'nautilus-mime-actions.c',
   'nautilus-mime-actions.h',
+  'nautilus-name-cell.c',
+  'nautilus-name-cell.h',
   'nautilus-notebook.c',
   'nautilus-notebook.h',
   'nautilus-pathbar.c',
@@ -121,6 +120,8 @@ libnautilus_sources = [
   'nautilus-self-check-functions.h',
   'nautilus-shell-search-provider.c',
   'nautilus-special-location-bar.c',
+  'nautilus-star-cell.c',
+  'nautilus-star-cell.h',
   'nautilus-toolbar.c',
   'nautilus-toolbar.h',
   'nautilus-toolbar-menu-sections.h',
@@ -231,8 +232,6 @@ libnautilus_sources = [
   'nautilus-thumbnails.h',
   'nautilus-trash-monitor.c',
   'nautilus-trash-monitor.h',
-  'nautilus-tree-view-drag-dest.c',
-  'nautilus-tree-view-drag-dest.h',
   'nautilus-ui-utilities.c',
   'nautilus-ui-utilities.h',
   'nautilus-video-mime-types.h',
diff --git a/src/nautilus-dnd.c b/src/nautilus-dnd.c
index a1e9ff128..b43e758b8 100644
--- a/src/nautilus-dnd.c
+++ b/src/nautilus-dnd.c
@@ -33,7 +33,6 @@
 #include <gtk/gtk.h>
 #include <glib/gi18n.h>
 #include "nautilus-file-utilities.h"
-#include <src/nautilus-list-view-dnd.h>
 #include <stdio.h>
 #include <string.h>
 
diff --git a/src/nautilus-files-view.c b/src/nautilus-files-view.c
index 7b59199cc..8890c7a2c 100644
--- a/src/nautilus-files-view.c
+++ b/src/nautilus-files-view.c
@@ -9611,7 +9611,7 @@ nautilus_files_view_new (guint               id,
 
         case NAUTILUS_VIEW_LIST_ID:
         {
-            view = nautilus_list_view_new (slot);
+            view = NAUTILUS_FILES_VIEW (nautilus_list_view_new (slot));
         }
         break;
     }
diff --git a/src/nautilus-label-cell.c b/src/nautilus-label-cell.c
new file mode 100644
index 000000000..7d3319dba
--- /dev/null
+++ b/src/nautilus-label-cell.c
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2022 António Fernandes <antoniof gnome org>
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+/* Needed for NautilusColumn (full GType). */
+#include <nautilus-extension.h>
+
+#include "nautilus-label-cell.h"
+
+struct _NautilusLabelCell
+{
+    NautilusViewCell parent_instance;
+
+    GSignalGroup *item_signal_group;
+
+    NautilusColumn *column;
+    GQuark attribute_q;
+
+    GtkLabel *label;
+
+    gboolean show_snippet;
+};
+
+G_DEFINE_TYPE (NautilusLabelCell, nautilus_label_cell, NAUTILUS_TYPE_VIEW_CELL)
+
+enum
+{
+    PROP_0,
+    PROP_COLUMN,
+    N_PROPS
+};
+
+static GParamSpec *properties[N_PROPS] = { NULL, };
+
+static void
+on_file_changed (NautilusLabelCell *self)
+{
+    NautilusViewItem *item;
+    NautilusFile *file;
+    g_autofree gchar *string = NULL;
+
+    item = nautilus_view_cell_get_item (NAUTILUS_VIEW_CELL (self));
+    g_return_if_fail (item != NULL);
+    file = nautilus_view_item_get_file (item);
+
+    string = nautilus_file_get_string_attribute_q (file, self->attribute_q);
+    gtk_label_set_text (self->label, string);
+}
+
+static void
+nautilus_label_cell_set_property (GObject      *object,
+                                  guint         prop_id,
+                                  const GValue *value,
+                                  GParamSpec   *pspec)
+{
+    NautilusLabelCell *self = NAUTILUS_LABEL_CELL (object);
+
+    switch (prop_id)
+    {
+        case PROP_COLUMN:
+        {
+            self->column = g_value_get_object (value);
+        }
+        break;
+
+        default:
+        {
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        }
+    }
+}
+
+static void
+nautilus_label_cell_init (NautilusLabelCell *self)
+{
+    GtkWidget *child;
+
+    child = gtk_label_new (NULL);
+    adw_bin_set_child (ADW_BIN (self), child);
+    gtk_widget_set_valign (child, GTK_ALIGN_CENTER);
+    gtk_widget_add_css_class (child, "dim-label");
+    self->label = GTK_LABEL (child);
+
+    /* Connect automatically to an item. */
+    self->item_signal_group = g_signal_group_new (NAUTILUS_TYPE_VIEW_ITEM);
+    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);
+}
+
+static void
+nautilus_label_cell_constructed (GObject *object)
+{
+    NautilusLabelCell *self = NAUTILUS_LABEL_CELL (object);
+    g_autofree gchar *column_name = NULL;
+    gfloat xalign;
+
+    G_OBJECT_CLASS (nautilus_label_cell_parent_class)->constructed (object);
+
+    g_object_get (self->column,
+                  "attribute_q", &self->attribute_q,
+                  "name", &column_name,
+                  "xalign", &xalign,
+                  NULL);
+    gtk_label_set_xalign (self->label, xalign);
+
+    if (g_strcmp0 (column_name, "permissions") == 0)
+    {
+        gtk_widget_add_css_class (GTK_WIDGET (self->label), "monospace");
+    }
+    else
+    {
+        gtk_widget_add_css_class (GTK_WIDGET (self->label), "numeric");
+    }
+}
+
+static void
+nautilus_label_cell_finalize (GObject *object)
+{
+    NautilusLabelCell *self = (NautilusLabelCell *) object;
+
+    g_object_unref (self->item_signal_group);
+    G_OBJECT_CLASS (nautilus_label_cell_parent_class)->finalize (object);
+}
+
+static void
+nautilus_label_cell_class_init (NautilusLabelCellClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+    object_class->set_property = nautilus_label_cell_set_property;
+    object_class->constructed = nautilus_label_cell_constructed;
+    object_class->finalize = nautilus_label_cell_finalize;
+
+    properties[PROP_COLUMN] = g_param_spec_object ("column",
+                                                   "", "",
+                                                   NAUTILUS_TYPE_COLUMN,
+                                                   G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | 
G_PARAM_STATIC_STRINGS);
+
+    g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+NautilusViewCell *
+nautilus_label_cell_new (NautilusListBase *view,
+                         NautilusColumn   *column)
+{
+    return NAUTILUS_VIEW_CELL (g_object_new (NAUTILUS_TYPE_LABEL_CELL,
+                                             "view", view,
+                                             "column", column,
+                                             NULL));
+}
diff --git a/src/nautilus-label-cell.h b/src/nautilus-label-cell.h
new file mode 100644
index 000000000..b476ef9a0
--- /dev/null
+++ b/src/nautilus-label-cell.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2022 António Fernandes <antoniof gnome org>
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+/* Needed for NautilusColumn (typedef only). */
+#include "nautilus-types.h"
+
+#include "nautilus-view-cell.h"
+
+G_BEGIN_DECLS
+
+#define NAUTILUS_TYPE_LABEL_CELL (nautilus_label_cell_get_type())
+
+G_DECLARE_FINAL_TYPE (NautilusLabelCell, nautilus_label_cell, NAUTILUS, LABEL_CELL, NautilusViewCell)
+
+NautilusViewCell * nautilus_label_cell_new (NautilusListBase *view,
+                                            NautilusColumn   *column);
+
+G_END_DECLS
diff --git a/src/nautilus-list-view.c b/src/nautilus-list-view.c
index d06546c38..7d98b4c54 100644
--- a/src/nautilus-list-view.c
+++ b/src/nautilus-list-view.c
@@ -1,3094 +1,433 @@
-/* fm-list-view.c - implementation of list view of directory.
- *
- *  Copyright (C) 2000 Eazel, Inc.
- *  Copyright (C) 2001, 2002 Anders Carlsson <andersca gnu org>
- *
- *  The Gnome Library is free software; you can redistribute it and/or
- *  modify it under the terms of the GNU Library General Public License as
- *  published by the Free Software Foundation; either version 2 of the
- *  License, or (at your option) any later version.
- *
- *  The Gnome Library is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- *  Library General Public License for more details.
- *
- *  You should have received a copy of the GNU Library General Public
- *  License along with the Gnome Library; see the file COPYING.LIB.  If not,
- *  see <http://www.gnu.org/licenses/>.
+/*
+ * Copyright (C) 2000 Eazel, Inc.
+ * Copyright (C) 2001, 2002 Anders Carlsson <andersca gnu org>
+ * Copyright (C) 2022 GNOME project contributors
  *
- *  Authors: John Sullivan <sullivan eazel com>
- *           Anders Carlsson <andersca gnu org>
- *           David Emory Watson <dwatson cs ucr edu>
+ * SPDX-License-Identifier: GPL-3.0-or-later
  */
 
-#include "nautilus-list-view.h"
-#include "nautilus-list-view-private.h"
-
-#include <eel/eel-vfs-extensions.h>
-#include <gdk/gdk.h>
-#include <gdk/gdkkeysyms.h>
 #include <glib/gi18n.h>
-#include <gtk/gtk.h>
-#include <string.h>
 
-#define DEBUG_FLAG NAUTILUS_DEBUG_LIST_VIEW
-#include "nautilus-debug.h"
+/* Needed for NautilusColumn. */
+#include <nautilus-extension.h>
+
+#include "nautilus-list-base-private.h"
+#include "nautilus-list-view.h"
 
-#include "nautilus-clipboard.h"
 #include "nautilus-column-chooser.h"
 #include "nautilus-column-utilities.h"
-#include "nautilus-dnd.h"
-#include "nautilus-enums.h"
-#include "nautilus-error-reporting.h"
+#include "nautilus-directory.h"
+#include "nautilus-file.h"
 #include "nautilus-file-utilities.h"
-#include "nautilus-files-view-dnd.h"
 #include "nautilus-global-preferences.h"
-#include "nautilus-list-model.h"
-#include "nautilus-list-view-dnd.h"
+#include "nautilus-label-cell.h"
 #include "nautilus-metadata.h"
+#include "nautilus-name-cell.h"
 #include "nautilus-search-directory.h"
+#include "nautilus-star-cell.h"
 #include "nautilus-tag-manager.h"
-#include "nautilus-toolbar.h"
-#include "nautilus-tree-view-drag-dest.h"
-#include "nautilus-ui-utilities.h"
-#include "nautilus-view.h"
-#include "nautilus-tracker-utilities.h"
 
-struct SelectionForeachData
+struct _NautilusListView
 {
-    GList *list;
-    GtkTreeSelection *selection;
-};
+    NautilusListBase parent_instance;
 
-/*
- * The row height should be large enough to not clip emblems.
- * Computing this would be costly, so we just choose a number
- * that works well with the set of emblems we've designed.
- */
-#define LIST_VIEW_MINIMUM_ROW_HEIGHT    28
+    GtkColumnView *view_ui;
 
-/* The star icon itself is 16px, which leaves an empty 16px gutter on each side,
- * which is necessary to avoid the overlay scrollbar.
- */
-#define STAR_COLUMN_WIDTH 48
-
-/* We wait two seconds after row is collapsed to unload the subdirectory */
-#define COLLAPSE_TO_UNLOAD_DELAY 2
-
-/* According to Pango docs, alpha is a guint16 value between 0 and 65535. */
-#define ALPHA_55_PERCENT ((guint16) (0.55 * 0xffff))
-
-static GdkCursor *hand_cursor = NULL;
-
-static GList *nautilus_list_view_get_selection (NautilusFilesView *view);
-static GList *nautilus_list_view_get_selection_for_file_transfer (NautilusFilesView *view);
-static void   nautilus_list_view_set_zoom_level (NautilusListView     *view,
-                                                 NautilusListZoomLevel new_level);
-static void   nautilus_list_view_scroll_to_file (NautilusListView *view,
-                                                 NautilusFile     *file);
-static void   nautilus_list_view_sort_directories_first_changed (NautilusFilesView *view);
-
-static void   apply_columns_settings (NautilusListView *list_view,
-                                      char            **column_order,
-                                      char            **visible_columns);
-static char **get_visible_columns (NautilusListView *list_view);
-static char **get_default_visible_columns (NautilusListView *list_view);
-static char **get_column_order (NautilusListView *list_view);
-static char **get_default_column_order (NautilusListView *list_view);
-static void popup_column_header_menu (NautilusListView *list_view,
-                                      gdouble           x,
-                                      gdouble           y);
-
-G_DEFINE_TYPE (NautilusListView, nautilus_list_view, NAUTILUS_TYPE_FILES_VIEW);
-
-static const char *default_search_visible_columns[] =
-{
-    "name", "size", "where", NULL
-};
+    GActionGroup *action_group;
+    gint zoom_level;
 
-static const char *default_search_columns_order[] =
-{
-    "name", "size", "where", NULL
-};
+    gboolean directories_first;
 
-static const char *default_recent_visible_columns[] =
-{
-    "name", "where", "recency", NULL
-};
+    GQuark path_attribute_q;
+    GFile *file_path_base_location;
 
-static const char *default_recent_columns_order[] =
-{
-    "name", "where", "recency", NULL
+    GtkColumnViewColumn *star_column;
+    GtkWidget *column_editor;
+    GHashTable *factory_to_column_map;
 };
 
-static const char *default_trash_visible_columns[] =
+G_DEFINE_TYPE (NautilusListView, nautilus_list_view, NAUTILUS_TYPE_LIST_BASE)
+
+
+static void on_sorter_changed (GtkSorter      *sorter,
+                               GtkSorterChange change,
+                               gpointer        user_data);
+
+static const char *default_columns_for_recent[] =
 {
-    "name", "size", "trash_orig_path", "trashed_on", NULL
+    "name", "size", "recency", NULL
 };
 
-static const char *default_trash_columns_order[] =
+static const char *default_columns_for_trash[] =
 {
-    "name", "size", "trash_orig_path", "trashed_on", NULL
+    "name", "size", "trashed_on", NULL
 };
 
-static const gchar *
-get_default_sort_order (NautilusFile *file,
-                        gboolean     *reversed)
+static guint
+get_icon_size_for_zoom_level (NautilusListZoomLevel zoom_level)
 {
-    NautilusFileSortType sort_type;
-
-    /* This array makes the #NautilusFileSortType values correspond to the
-     * respective column attribute.
-     */
-    const char *attributes[] =
+    switch (zoom_level)
     {
-        "name",
-        "size",
-        "type",
-        "date_modified",
-        "date_accessed",
-        "date_created",
-        "starred",
-        "trashed_on",
-        "search_relevance",
-        "recency",
-        NULL
-    };
+        case NAUTILUS_LIST_ZOOM_LEVEL_SMALL:
+        {
+            return NAUTILUS_LIST_ICON_SIZE_SMALL;
+        }
+        break;
+
+        case NAUTILUS_LIST_ZOOM_LEVEL_STANDARD:
+        {
+            return NAUTILUS_LIST_ICON_SIZE_STANDARD;
+        }
+        break;
 
-    sort_type = nautilus_file_get_default_sort_type (file, reversed);
+        case NAUTILUS_LIST_ZOOM_LEVEL_LARGE:
+        {
+            return NAUTILUS_LIST_ICON_SIZE_LARGE;
+        }
+        break;
 
-    return attributes[sort_type];
+        case NAUTILUS_LIST_ZOOM_LEVEL_LARGER:
+        {
+            return NAUTILUS_LIST_ICON_SIZE_LARGER;
+        }
+        break;
+    }
+    g_return_val_if_reached (NAUTILUS_LIST_ICON_SIZE_STANDARD);
 }
 
-static void
-list_selection_changed_callback (GtkTreeSelection *selection,
-                                 gpointer          user_data)
+static guint
+real_get_icon_size (NautilusListBase *files_model_view)
 {
-    NautilusFilesView *view;
-
-    view = NAUTILUS_FILES_VIEW (user_data);
+    NautilusListView *self = NAUTILUS_LIST_VIEW (files_model_view);
 
-    nautilus_files_view_notify_selection_changed (view);
+    return get_icon_size_for_zoom_level (self->zoom_level);
 }
 
-static void
-activate_selected_items (NautilusListView *view)
+static GtkWidget *
+real_get_view_ui (NautilusListBase *files_model_view)
 {
-    GList *file_list;
+    NautilusListView *self = NAUTILUS_LIST_VIEW (files_model_view);
 
-    file_list = nautilus_list_view_get_selection (NAUTILUS_FILES_VIEW (view));
-    if (file_list != NULL)
-    {
-        nautilus_files_view_activate_files (NAUTILUS_FILES_VIEW (view),
-                                            file_list,
-                                            0, TRUE);
-        nautilus_file_list_free (file_list);
-    }
+    return GTK_WIDGET (self->view_ui);
 }
 
 static void
-activate_selected_items_alternate (NautilusListView *view,
-                                   NautilusFile     *file,
-                                   gboolean          open_in_tab)
+apply_columns_settings (NautilusListView  *self,
+                        char             **column_order,
+                        char             **visible_columns)
 {
-    GList *file_list;
-    NautilusOpenFlags flags;
-
-    flags = 0;
-
-    if (open_in_tab)
+    g_autolist (NautilusColumn) all_columns = NULL;
+    NautilusFile *file;
+    NautilusDirectory *directory;
+    g_autoptr (GFile) location = NULL;
+    g_autoptr (GList) view_columns = NULL;
+    GListModel *old_view_columns;
+    g_autoptr (GHashTable) visible_columns_hash = NULL;
+    g_autoptr (GHashTable) old_view_columns_hash = NULL;
+    int column_i = 0;
+
+    file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (self));
+    directory = nautilus_files_view_get_model (NAUTILUS_FILES_VIEW (self));
+    if (NAUTILUS_IS_SEARCH_DIRECTORY (directory))
     {
-        flags |= NAUTILUS_OPEN_FLAG_NEW_TAB;
-        flags |= NAUTILUS_OPEN_FLAG_DONT_MAKE_ACTIVE;
+        g_autoptr (NautilusQuery) query = NULL;
+
+        query = nautilus_search_directory_get_query (NAUTILUS_SEARCH_DIRECTORY (directory));
+        location = nautilus_query_get_location (query);
     }
     else
     {
-        flags |= NAUTILUS_OPEN_FLAG_NEW_WINDOW;
+        location = nautilus_file_get_location (file);
     }
 
-    if (file != NULL)
+    all_columns = nautilus_get_columns_for_file (file);
+    all_columns = nautilus_sort_columns (all_columns, column_order);
+
+    /* hash table to lookup if a given column should be visible */
+    visible_columns_hash = g_hash_table_new_full (g_str_hash,
+                                                  g_str_equal,
+                                                  (GDestroyNotify) g_free,
+                                                  (GDestroyNotify) g_free);
+    /* always show name column */
+    g_hash_table_insert (visible_columns_hash, g_strdup ("name"), g_strdup ("name"));
+
+    /* always show star column if supported */
+    if (nautilus_tag_manager_can_star_contents (nautilus_tag_manager_get (), location) ||
+        nautilus_is_starred_directory (location))
     {
-        nautilus_file_ref (file);
-        file_list = g_list_prepend (NULL, file);
+        g_hash_table_insert (visible_columns_hash, g_strdup ("starred"), g_strdup ("starred"));
     }
-    else
+
+    if (visible_columns != NULL)
     {
-        file_list = nautilus_list_view_get_selection (NAUTILUS_FILES_VIEW (view));
+        for (int i = 0; visible_columns[i] != NULL; ++i)
+        {
+            g_hash_table_insert (visible_columns_hash,
+                                 g_ascii_strdown (visible_columns[i], -1),
+                                 g_ascii_strdown (visible_columns[i], -1));
+        }
     }
-    nautilus_files_view_activate_files (NAUTILUS_FILES_VIEW (view),
-                                        file_list,
-                                        flags,
-                                        TRUE);
-    nautilus_file_list_free (file_list);
-}
 
-static gboolean
-button_event_modifies_selection (GdkEvent *event)
-{
-    GdkModifierType state;
+    old_view_columns_hash = g_hash_table_new_full (g_str_hash,
+                                                   g_str_equal,
+                                                   (GDestroyNotify) g_free,
+                                                   NULL);
+    old_view_columns = gtk_column_view_get_columns (self->view_ui);
+    for (guint i = 0; i < g_list_model_get_n_items (old_view_columns); i++)
+    {
+        g_autoptr (GtkColumnViewColumn) view_column = NULL;
+        GtkListItemFactory *factory;
+        NautilusColumn *nautilus_column;
+        gchar *name;
 
-    state = gdk_event_get_modifier_state (event);
+        view_column = g_list_model_get_item (old_view_columns, i);
+        factory = gtk_column_view_column_get_factory (view_column);
+        nautilus_column = g_hash_table_lookup (self->factory_to_column_map, factory);
+        g_object_get (nautilus_column, "name", &name, NULL);
+        g_hash_table_insert (old_view_columns_hash, name, view_column);
+    }
 
-    return (state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) != 0;
-}
+    for (GList *l = all_columns; l != NULL; l = l->next)
+    {
+        g_autofree char *name = NULL;
+        g_autofree char *lowercase = NULL;
 
-static int
-get_click_policy (void)
-{
-    return g_settings_get_enum (nautilus_preferences,
-                                NAUTILUS_PREFERENCES_CLICK_POLICY);
-}
+        g_object_get (G_OBJECT (l->data), "name", &name, NULL);
+        lowercase = g_ascii_strdown (name, -1);
 
-static void
-on_event_controller_motion_motion (GtkEventControllerMotion *controller,
-                                   double                    x,
-                                   double                    y,
-                                   gpointer                  user_data)
-{
-    NautilusListView *view;
-    GtkWidget *widget;
-    GtkTreePath *old_hover_path;
-    int x_in_bin;
-    int y_in_bin;
+        if (g_hash_table_lookup (visible_columns_hash, lowercase) != NULL)
+        {
+            GtkColumnViewColumn *view_column;
 
-    if (get_click_policy () != NAUTILUS_CLICK_POLICY_SINGLE)
-    {
-        return;
+            view_column = g_hash_table_lookup (old_view_columns_hash, name);
+            if (view_column != NULL)
+            {
+                view_columns = g_list_prepend (view_columns, view_column);
+            }
+        }
     }
 
-    view = user_data;
-    widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (controller));
-    old_hover_path = view->details->hover_path;
-
-    gtk_tree_view_convert_widget_to_bin_window_coords (GTK_TREE_VIEW (widget),
-                                                       x, y,
-                                                       &x_in_bin, &y_in_bin);
-    gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
-                                   x_in_bin, y_in_bin,
-                                   &view->details->hover_path,
-                                   NULL, NULL, NULL);
+    view_columns = g_list_reverse (view_columns);
 
-    if ((old_hover_path != NULL) != (view->details->hover_path != NULL))
+    /* hide columns that are not present in the configuration */
+    for (guint i = 0; i < g_list_model_get_n_items (old_view_columns); i++)
     {
-        if (view->details->hover_path != NULL)
+        g_autoptr (GtkColumnViewColumn) view_column = NULL;
+
+        view_column = g_list_model_get_item (old_view_columns, i);
+        if (g_list_find (view_columns, view_column) == NULL)
         {
-            gtk_widget_set_cursor (widget, hand_cursor);
+            gtk_column_view_column_set_visible (view_column, FALSE);
         }
         else
         {
-            gtk_widget_set_cursor (widget, NULL);
+            gtk_column_view_column_set_visible (view_column, TRUE);
         }
     }
 
-    if (old_hover_path != NULL)
-    {
-        gtk_tree_path_free (old_hover_path);
-    }
-}
-
-static void
-on_event_controller_motion_leave (GtkEventControllerMotion *controller,
-                                  gpointer                  user_data)
-{
-    NautilusListView *view;
-
-    view = user_data;
-
-    if (get_click_policy () != NAUTILUS_CLICK_POLICY_SINGLE ||
-        view->details->hover_path == NULL)
+    /* place columns in the correct order */
+    for (GList *l = view_columns; l != NULL; l = l->next, column_i++)
     {
-        return;
+        gtk_column_view_insert_column (self->view_ui, column_i, l->data);
     }
-
-    gtk_tree_path_free (view->details->hover_path);
-    view->details->hover_path = NULL;
 }
 
 static void
-on_event_controller_motion_enter (GtkEventControllerMotion *controller,
-                                  double                    x,
-                                  double                    y,
-                                  gpointer                  user_data)
+real_scroll_to_item (NautilusListBase *files_model_view,
+                     guint             position)
 {
-    NautilusListView *view;
-    GtkWidget *widget;
-    int x_in_bin;
-    int y_in_bin;
+    NautilusListView *self = NAUTILUS_LIST_VIEW (files_model_view);
+    GtkWidget *child;
 
-    if (get_click_policy () != NAUTILUS_CLICK_POLICY_SINGLE)
-    {
-        return;
-    }
+    child = gtk_widget_get_last_child (GTK_WIDGET (self->view_ui));
 
-    view = user_data;
-    if (view->details->hover_path != NULL)
+    while (child != NULL && !GTK_IS_LIST_VIEW (child))
     {
-        gtk_tree_path_free (view->details->hover_path);
+        child = gtk_widget_get_prev_sibling (child);
     }
-    widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (controller));
-
-    gtk_tree_view_convert_widget_to_bin_window_coords (GTK_TREE_VIEW (widget),
-                                                       x, y,
-                                                       &x_in_bin, &y_in_bin);
-    gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
-                                   x_in_bin, y_in_bin,
-                                   &view->details->hover_path,
-                                   NULL, NULL, NULL);
 
-    if (view->details->hover_path != NULL)
+    if (child != NULL)
     {
-        gtk_widget_set_cursor (widget, hand_cursor);
+        gtk_widget_activate_action (child, "list.scroll-to-item", "u", position);
     }
 }
+typedef struct
+{
+    NautilusListView *self;
+    GQuark attribute_q;
+} NautilusListViewSortData;
 
-static void
-row_activated_callback (GtkTreeView       *treeview,
-                        GtkTreePath       *path,
-                        GtkTreeViewColumn *column,
-                        NautilusListView  *view)
+static gint
+nautilus_list_view_sort (gconstpointer a,
+                         gconstpointer b,
+                         gpointer      user_data)
 {
-    activate_selected_items (view);
+    NautilusListViewSortData *sort_data = user_data;
+    NautilusListView *self = sort_data->self;
+    NautilusFile *file_a;
+    NautilusFile *file_b;
+
+    file_a = nautilus_view_item_get_file (NAUTILUS_VIEW_ITEM ((gpointer) a));
+    file_b = nautilus_view_item_get_file (NAUTILUS_VIEW_ITEM ((gpointer) b));
+
+    /* The reversed argument is FALSE because the column sorter handles that
+     * itself and if we don't want to reverse the reverse. */
+    return nautilus_file_compare_for_sort_by_attribute_q (file_a, file_b,
+                                                          sort_data->attribute_q,
+                                                          self->directories_first,
+                                                          FALSE);
 }
 
-static gboolean
-check_starred_status (GtkTreeModel *model,
-                      GtkTreePath  *path,
-                      GtkTreeIter  *iter,
-                      gpointer      data)
+static char **
+get_default_visible_columns (NautilusListView *self)
 {
     NautilusFile *file;
-    GList *l;
-    GList *changed_files;
 
-    changed_files = data;
+    file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (self));
 
-    gtk_tree_model_get (GTK_TREE_MODEL (model),
-                        iter,
-                        NAUTILUS_LIST_MODEL_FILE_COLUMN, &file,
-                        -1);
-
-    if (!file)
+    if (nautilus_file_is_in_trash (file))
     {
-        return FALSE;
+        return g_strdupv ((gchar **) default_columns_for_trash);
     }
 
-    for (l = changed_files; l != NULL; l = l->next)
+    if (nautilus_file_is_in_recent (file))
     {
-        if (nautilus_file_compare_location (NAUTILUS_FILE (l->data), file) == 0)
-        {
-            gtk_tree_model_row_changed (model, path, iter);
-        }
+        return g_strdupv ((gchar **) default_columns_for_recent);
     }
 
-    nautilus_file_unref (file);
-
-    return FALSE;
+    return g_settings_get_strv (nautilus_list_view_preferences,
+                                NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_VISIBLE_COLUMNS);
 }
 
-static void
-on_starred_files_changed (NautilusTagManager *tag_manager,
-                          GList              *changed_files,
-                          gpointer            user_data)
+static char **
+get_visible_columns (NautilusListView *self)
 {
-    NautilusListView *list_view;
+    NautilusFile *file;
+    g_autofree gchar **visible_columns = NULL;
 
-    list_view = NAUTILUS_LIST_VIEW (user_data);
+    file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (self));
+
+    visible_columns = nautilus_file_get_metadata_list (file,
+                                                       NAUTILUS_METADATA_KEY_LIST_VIEW_VISIBLE_COLUMNS);
+    if (visible_columns == NULL || visible_columns[0] == NULL)
+    {
+        return get_default_visible_columns (self);
+    }
 
-    gtk_tree_model_foreach (GTK_TREE_MODEL (list_view->details->model),
-                            check_starred_status,
-                            changed_files);
+    return g_steal_pointer (&visible_columns);
 }
 
-static void
-on_star_cell_renderer_clicked (GtkTreePath      *path,
-                               NautilusListView *list_view)
+static char **
+get_default_column_order (NautilusListView *self)
 {
-    NautilusListModel *list_model;
     NautilusFile *file;
-    g_autofree gchar *uri = NULL;
-    GList *selection;
-    NautilusTagManager *tag_manager = nautilus_tag_manager_get ();
 
-    list_model = list_view->details->model;
+    file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (self));
 
-    file = nautilus_list_model_file_for_path (list_model, path);
-
-    if (file == NULL)
+    if (nautilus_file_is_in_trash (file))
     {
-        /* This row is a label, not a file */
-        return;
+        return g_strdupv ((gchar **) default_columns_for_trash);
     }
 
-    uri = nautilus_file_get_uri (file);
-    selection = g_list_prepend (NULL, file);
-
-    if (nautilus_tag_manager_file_is_starred (tag_manager, uri))
-    {
-        nautilus_tag_manager_unstar_files (tag_manager,
-                                           G_OBJECT (list_view),
-                                           selection,
-                                           NULL,
-                                           list_view->details->starred_cancellable);
-    }
-    else
+    if (nautilus_file_is_in_recent (file))
     {
-        nautilus_tag_manager_star_files (tag_manager,
-                                         G_OBJECT (list_view),
-                                         selection,
-                                         NULL,
-                                         list_view->details->starred_cancellable);
+        return g_strdupv ((gchar **) default_columns_for_recent);
     }
 
-    nautilus_file_list_free (selection);
+    return g_settings_get_strv (nautilus_list_view_preferences,
+                                NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_COLUMN_ORDER);
 }
 
-static void
-on_tree_view_click_gesture_pressed (GtkGestureClick *gesture,
-                                    gint             n_press,
-                                    gdouble          x,
-                                    gdouble          y,
-                                    gpointer         callback_data)
+static char **
+get_column_order (NautilusListView *self)
 {
-    NautilusListView *view;
-    GtkWidget *widget;
-    GtkTreeView *tree_view;
-    g_autoptr (GtkTreePath) path = NULL;
-    GtkTreeViewColumn *column;
-    GtkTreeSelection *selection;
-    guint button;
-    gint bin_x;
-    gint bin_y;
-    GdkEventSequence *sequence;
-    GdkEvent *event;
-    gboolean on_expander, show_expanders;
-    gboolean is_simple_click, path_selected;
     NautilusFile *file;
-    gboolean on_star;
-
-    view = NAUTILUS_LIST_VIEW (callback_data);
-    widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
-    tree_view = GTK_TREE_VIEW (widget);
-    selection = gtk_tree_view_get_selection (tree_view);
-    button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture));
+    g_autofree gchar **column_order = NULL;
 
-    gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, x, y, &bin_x, &bin_y);
-
-    view->details->last_event_button_x = bin_x;
-    view->details->last_event_button_y = bin_y;
-
-    /* Don't handle extra mouse buttons here */
-    if (button > 5)
-    {
-        return;
-    }
-
-    sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
-    event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence);
-
-    /* Column headers lie above bin_window, hence negative y coordinate. */
-    if (bin_y < 0)
-    {
-        if (button == GDK_BUTTON_SECONDARY)
-        {
-            popup_column_header_menu (view, x, y);
-            gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
-        }
-        else
-        {
-            gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
-        }
-        return;
-    }
+    file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (self));
 
-    nautilus_list_model_set_drag_view
-        (NAUTILUS_LIST_MODEL (gtk_tree_view_get_model (tree_view)),
-        tree_view,
-        bin_x, bin_y);
+    column_order = nautilus_file_get_metadata_list (file,
+                                                    NAUTILUS_METADATA_KEY_LIST_VIEW_COLUMN_ORDER);
 
-    /* Ignore double click if we are in single click mode */
-    if (get_click_policy () == NAUTILUS_CLICK_POLICY_SINGLE && n_press >= 2)
+    if (column_order != NULL && column_order[0] != NULL)
     {
-        return;
+        return g_steal_pointer (&column_order);
     }
 
-    is_simple_click = ((button == GDK_BUTTON_PRIMARY || button == GDK_BUTTON_MIDDLE) && (n_press == 1));
-
-    /* No item at this position */
-    if (!gtk_tree_view_get_path_at_pos (tree_view, bin_x, bin_y,
-                                        &path, &column, NULL, NULL))
-    {
-        if (is_simple_click)
-        {
-            g_clear_pointer (&view->details->first_click_path, gtk_tree_path_free);
-        }
-
-        gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (tree_view));
-
-        if (button == GDK_BUTTON_SECONDARY)
-        {
-            nautilus_files_view_pop_up_background_context_menu (NAUTILUS_FILES_VIEW (view),
-                                                                x, y);
-        }
+    return get_default_column_order (self);
+}
+static void
+update_columns_settings_from_metadata_and_preferences (NautilusListView *self)
+{
+    g_auto (GStrv) column_order = get_column_order (self);
+    g_auto (GStrv) visible_columns = get_visible_columns (self);
 
-        return;
-    }
+    apply_columns_settings (self, column_order, visible_columns);
+}
 
-    on_expander = FALSE;
-    path_selected = gtk_tree_selection_path_is_selected (selection, path);
-    show_expanders = g_settings_get_boolean (nautilus_list_view_preferences,
-                                             NAUTILUS_PREFERENCES_LIST_VIEW_USE_TREE);
+static GFile *
+get_base_location (NautilusListView *self)
+{
+    NautilusDirectory *directory;
+    GFile *base_location = NULL;
 
-    if (show_expanders)
+    directory = nautilus_files_view_get_model (NAUTILUS_FILES_VIEW (self));
+    if (NAUTILUS_IS_SEARCH_DIRECTORY (directory))
     {
-        GdkRectangle cell_area;
-
-        gtk_tree_view_get_cell_area (tree_view, path, column, &cell_area);
+        g_autoptr (NautilusQuery) query = NULL;
+        g_autoptr (GFile) location = NULL;
 
-        /* We assume that the cell area excludes the expander itself.
-         * Explanatory link for future reference:
-         * https://gitlab.gnome.org/GNOME/nautilus/merge_requests/97#note_58649 */
+        query = nautilus_search_directory_get_query (NAUTILUS_SEARCH_DIRECTORY (directory));
+        location = nautilus_query_get_location (query);
 
-        if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
+        if (!nautilus_is_recent_directory (location) &&
+            !nautilus_is_starred_directory (location) &&
+            !nautilus_is_trash_directory (location))
         {
-            on_expander = bin_x > (cell_area.x + cell_area.width);
+            base_location = g_steal_pointer (&location);
         }
-        else
-        {
-            on_expander = bin_x < cell_area.x;
-        }
-    }
-
-    /* Keep track of path of last click so double clicks only happen
-     * on the same item */
-    if (is_simple_click)
-    {
-        g_clear_pointer (&view->details->first_click_path, gtk_tree_path_free);
-        view->details->first_click_path = gtk_tree_path_copy (path);
     }
 
-    on_star = (g_hash_table_lookup (view->details->columns, "starred") == column &&
-               !gtk_tree_view_is_blank_at_pos (tree_view,
-                                               bin_x,
-                                               bin_y,
-                                               NULL,
-                                               NULL,
-                                               NULL,
-                                               NULL));
+    return base_location;
+}
 
-    if (is_simple_click && on_star)
-    {
-        on_star_cell_renderer_clicked (path, view);
-    }
-    else if (n_press == 2 && !on_star)
-    {
-        /* Double clicking does not trigger a D&D action. */
-        view->details->drag_button = 0;
+static void
+on_column_view_item_activated (GtkGridView *grid_view,
+                               guint        position,
+                               gpointer     user_data)
+{
+    NautilusListView *self = NAUTILUS_LIST_VIEW (user_data);
 
-        /* NOTE: Activation can actually destroy the view if we're switching */
-        if (!on_expander &&
-            view->details->first_click_path &&
-            gtk_tree_path_compare (path, view->details->first_click_path) == 0)
-        {
-            if ((button == GDK_BUTTON_PRIMARY) && button_event_modifies_selection (event))
-            {
-                file = nautilus_list_model_file_for_path (view->details->model, path);
-                if (file != NULL)
-                {
-                    activate_selected_items_alternate (view, file, TRUE);
-                    nautilus_file_unref (file);
-                }
-            }
-            else if ((button == GDK_BUTTON_PRIMARY || button == GDK_BUTTON_SECONDARY))
-            {
-                activate_selected_items (view);
-            }
-        }
-        else
-        {
-            return;
-        }
-    }
-    else
-    {
-        GdkModifierType state;
-        g_autoptr (GtkTreePath) cursor = NULL;
-        GList *selected_rows = NULL;
-
-        state = gdk_event_get_modifier_state (event);
-
-        /* We cannot easily match the expected behavior of Shift+click, so we
-         * must fall back to GtkTreeView's default event handling.
-         *
-         * If Shift and Ctrl are held simultateously, GtkTreeView ignores Shift,
-         * so we implement a more useful behavior ourselves.
-         */
-        if ((state & GDK_SHIFT_MASK) != 0 && (state & GDK_CONTROL_MASK) == 0)
-        {
-            return;
-        }
+    nautilus_files_view_activate_selection (NAUTILUS_FILES_VIEW (self));
+}
 
-        /* Let GtkTreeView handle tree expanding/collapsing. */
-        if (is_simple_click && on_expander)
-        {
-            return;
-        }
+static GtkColumnView *
+create_view_ui (NautilusListView *self)
+{
+    NautilusViewModel *model;
+    GtkWidget *widget;
 
-        /* As we don't let GtkTreeView default event handling go through, so we
-         * must grab the focus ourselves. */
-        gtk_widget_grab_focus (widget);
+    model = nautilus_list_base_get_model (NAUTILUS_LIST_BASE (self));
+    widget = gtk_column_view_new (GTK_SELECTION_MODEL (model));
 
-        if (!path_selected)
-        {
-            if ((state & GDK_CONTROL_MASK) != 0)
-            {
-                if ((state & GDK_SHIFT_MASK) != 0)
-                {
-                    gtk_tree_view_get_cursor (tree_view, &cursor, NULL);
-                    if (cursor != NULL)
-                    {
-                        gtk_tree_selection_select_range (selection, cursor, path);
-                    }
-                    else
-                    {
-                        gtk_tree_selection_select_path (selection, path);
-                    }
-                }
-                else
-                {
-                    gtk_tree_selection_select_path (selection, path);
-                }
-                selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
-
-                /* This unselects everything */
-                gtk_tree_view_set_cursor (tree_view, path, NULL, FALSE);
-
-                /* So select it again */
-                for (GList *l = selected_rows; l != NULL; l = l->next)
-                {
-                    gtk_tree_selection_select_path (selection, l->data);
-                }
-                g_list_free_full (selected_rows, (GDestroyNotify) gtk_tree_path_free);
-            }
-            else
-            {
-                gtk_tree_view_set_cursor (tree_view, path, NULL, FALSE);
-            }
-        }
-
-        if (is_simple_click)
-        {
-            view->details->drag_started = FALSE;
-            view->details->drag_button = button;
-            view->details->drag_x = bin_x;
-            view->details->drag_y = bin_y;
-            view->details->row_selected_on_button_down = path_selected;
-        }
-
-        if (button == GDK_BUTTON_SECONDARY)
-        {
-            nautilus_files_view_pop_up_selection_context_menu (NAUTILUS_FILES_VIEW (view),
-                                                               x, y);
-        }
-
-        /* Don't open a new tab if we are in single click mode (this would open 2 tabs),
-         * or if CTRL or SHIFT is pressed.
-         */
-        if (button == GDK_BUTTON_MIDDLE &&
-            get_click_policy () != NAUTILUS_CLICK_POLICY_SINGLE &&
-            !button_event_modifies_selection (event))
-        {
-            gtk_tree_selection_unselect_all (selection);
-            gtk_tree_selection_select_path (selection, path);
-
-            activate_selected_items_alternate (view, NULL, TRUE);
-        }
-    }
-
-    gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
-}
-
-static void
-on_tree_view_click_gesture_released (GtkGestureClick *gesture,
-                                     gint             n_press,
-                                     gdouble          x,
-                                     gdouble          y,
-                                     gpointer         callback_data)
-{
-    NautilusListView *view;
-    guint button;
-    GdkEventSequence *sequence;
-    GdkEvent *event;
-    GtkTreeView *tree_view;
-    GtkTreeSelection *selection;
-    gint x_in_bin;
-    gint y_in_bin;
-    GtkTreePath *path;
-    GdkModifierType state;
-
-    view = NAUTILUS_LIST_VIEW (callback_data);
-    button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture));
-    if (button != view->details->drag_button)
-    {
-        return;
-    }
-
-    view->details->drag_button = 0;
-
-    if (view->details->drag_started)
-    {
-        return;
-    }
-
-    /* Did not drag. */
-
-    sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
-    event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence);
-    /* Typically will only happen with GTK+ <= 3.22.30 and <= 3.93.0,
-     * where ::released is emitted after ::cancel, but can’t hurt to guard
-     * against it anyway.
-     */
-    if (event == NULL)
-    {
-        return;
-    }
-
-    tree_view = view->details->tree_view;
-    selection = gtk_tree_view_get_selection (tree_view);
-
-
-    gtk_tree_view_convert_widget_to_bin_window_coords (tree_view,
-                                                       x, y,
-                                                       &x_in_bin, &y_in_bin);
-
-    if (!gtk_tree_view_get_path_at_pos (tree_view, x_in_bin, y_in_bin, &path, NULL, NULL, NULL))
-    {
-        return;
-    }
-
-    state = gdk_event_get_modifier_state (event);
-
-    if ((button == GDK_BUTTON_PRIMARY || button == GDK_BUTTON_MIDDLE)
-        && ((state & GDK_CONTROL_MASK) != 0 ||
-            (state & GDK_SHIFT_MASK) == 0)
-        && view->details->row_selected_on_button_down)
-    {
-        if (!button_event_modifies_selection (event))
-        {
-            gtk_tree_selection_unselect_all (selection);
-            gtk_tree_selection_select_path (selection, path);
-        }
-        else
-        {
-            gtk_tree_selection_unselect_path (selection, path);
-        }
-    }
-
-    if ((get_click_policy () == NAUTILUS_CLICK_POLICY_SINGLE)
-        && !button_event_modifies_selection (event))
-    {
-        if (button == GDK_BUTTON_PRIMARY)
-        {
-            activate_selected_items (view);
-        }
-        else if (button == GDK_BUTTON_MIDDLE)
-        {
-            activate_selected_items_alternate (view, NULL, TRUE);
-        }
-    }
-    gtk_tree_path_free (path);
-}
-
-static gboolean
-on_event_controller_key_key_pressed (GtkEventControllerKey *controller,
-                                     unsigned int           keyval,
-                                     unsigned int           keycode,
-                                     GdkModifierType        state,
-                                     gpointer               user_data)
-{
-    GtkWidget *widget;
-    NautilusFilesView *view;
-    GtkTreeView *tree_view;
-
-    widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (controller));
-    view = NAUTILUS_FILES_VIEW (user_data);
-    tree_view = GTK_TREE_VIEW (widget);
-
-    NAUTILUS_LIST_VIEW (view)->details->last_event_button_x = -1;
-    NAUTILUS_LIST_VIEW (view)->details->last_event_button_y = -1;
-
-    if (keyval == GDK_KEY_Right)
-    {
-        g_autoptr (GtkTreePath) path = NULL;
-
-        gtk_tree_view_get_cursor (tree_view, &path, NULL);
-
-        if (path != NULL)
-        {
-            gtk_tree_view_expand_row (tree_view, path, FALSE);
-        }
-
-        return GDK_EVENT_STOP;
-    }
-
-    if (keyval == GDK_KEY_Left)
-    {
-        g_autoptr (GtkTreePath) path = NULL;
-
-        gtk_tree_view_get_cursor (tree_view, &path, NULL);
-
-        if (path != NULL && !gtk_tree_view_collapse_row (tree_view, path))
-        {
-            /* if the row is already collapsed or doesn't have any children,
-             * jump to the parent row instead.
-             */
-            if ((gtk_tree_path_get_depth (path) > 1) && gtk_tree_path_up (path))
-            {
-                gtk_tree_view_set_cursor (tree_view, path, NULL, FALSE);
-            }
-        }
-
-        return GDK_EVENT_STOP;
-    }
-
-    if (keyval == GDK_KEY_space)
-    {
-        if ((state & GDK_CONTROL_MASK) != 0)
-        {
-            return GDK_EVENT_PROPAGATE;
-        }
-
-        if (!gtk_widget_has_focus (GTK_WIDGET (NAUTILUS_LIST_VIEW (view)->details->tree_view)))
-        {
-            return GDK_EVENT_PROPAGATE;
-        }
-
-        if ((state & GDK_SHIFT_MASK) != 0)
-        {
-            activate_selected_items_alternate (NAUTILUS_LIST_VIEW (view), NULL, TRUE);
-        }
-
-        return GDK_EVENT_STOP;
-    }
-
-    if (keyval == GDK_KEY_v)
-    {
-        /* Eat Control + v to not enable type ahead */
-        if ((state & GDK_CONTROL_MASK) != 0)
-        {
-            return GDK_EVENT_STOP;
-        }
-    }
-
-    return GDK_EVENT_PROPAGATE;
-}
-
-static void
-subdirectory_done_loading_callback (NautilusDirectory *directory,
-                                    NautilusListView  *view)
-{
-    nautilus_list_model_subdirectory_done_loading (view->details->model, directory);
-}
-
-static void
-row_expanded_callback (GtkTreeView *treeview,
-                       GtkTreeIter *iter,
-                       GtkTreePath *path,
-                       gpointer     callback_data)
-{
-    NautilusListView *view;
-    NautilusDirectory *directory;
-    char *uri;
-
-    view = NAUTILUS_LIST_VIEW (callback_data);
-
-    if (!nautilus_list_model_load_subdirectory (view->details->model, path, &directory))
-    {
-        return;
-    }
-
-    uri = nautilus_directory_get_uri (directory);
-    DEBUG ("Row expanded callback for URI %s", uri);
-    g_free (uri);
-
-    nautilus_files_view_add_subdirectory (NAUTILUS_FILES_VIEW (view), directory);
-
-    if (nautilus_directory_are_all_files_seen (directory))
-    {
-        nautilus_list_model_subdirectory_done_loading (view->details->model,
-                                                       directory);
-    }
-    else
-    {
-        g_signal_connect_object (directory, "done-loading",
-                                 G_CALLBACK (subdirectory_done_loading_callback),
-                                 view, 0);
-    }
-
-    nautilus_directory_unref (directory);
-}
-
-typedef struct
-{
-    NautilusFile *file;
-    NautilusDirectory *directory;
-    NautilusListView *view;
-} UnloadDelayData;
-
-static void
-unload_delay_data_free (UnloadDelayData *unload_data)
-{
-    if (unload_data->view != NULL)
-    {
-        g_object_remove_weak_pointer (G_OBJECT (unload_data->view),
-                                      (gpointer *) &unload_data->view);
-    }
-
-    nautilus_directory_unref (unload_data->directory);
-    nautilus_file_unref (unload_data->file);
-
-    g_slice_free (UnloadDelayData, unload_data);
-}
-
-static UnloadDelayData *
-unload_delay_data_new (NautilusFile      *file,
-                       NautilusDirectory *parent_directory,
-                       NautilusListView  *view)
-{
-    UnloadDelayData *unload_data;
-
-    unload_data = g_slice_new0 (UnloadDelayData);
-    unload_data->view = view;
-    unload_data->file = nautilus_file_ref (file);
-    unload_data->directory = nautilus_directory_ref (parent_directory);
-
-    g_object_add_weak_pointer (G_OBJECT (unload_data->view),
-                               (gpointer *) &unload_data->view);
-
-    return unload_data;
-}
-
-static gboolean
-unload_file_timeout (gpointer data)
-{
-    UnloadDelayData *unload_data = data;
-    GtkTreeIter iter;
-    NautilusListModel *model;
-    GtkTreePath *path;
-
-    if (unload_data->view == NULL)
-    {
-        goto out;
-    }
-
-    model = unload_data->view->details->model;
-    if (nautilus_list_model_get_tree_iter_from_file (model,
-                                                     unload_data->file,
-                                                     unload_data->directory,
-                                                     &iter))
-    {
-        path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
-        if (!gtk_tree_view_row_expanded (unload_data->view->details->tree_view,
-                                         path))
-        {
-            nautilus_list_model_unload_subdirectory (model, &iter);
-        }
-        gtk_tree_path_free (path);
-    }
-
-out:
-    unload_delay_data_free (unload_data);
-    return FALSE;
-}
-
-static void
-row_collapsed_callback (GtkTreeView *treeview,
-                        GtkTreeIter *iter,
-                        GtkTreePath *path,
-                        gpointer     callback_data)
-{
-    NautilusListView *view;
-    NautilusFile *file;
-    NautilusDirectory *directory;
-    GtkTreeIter parent;
-    UnloadDelayData *unload_data;
-    GtkTreeModel *model;
-    char *uri;
-
-    view = NAUTILUS_LIST_VIEW (callback_data);
-    model = GTK_TREE_MODEL (view->details->model);
-
-    gtk_tree_model_get (model, iter,
-                        NAUTILUS_LIST_MODEL_FILE_COLUMN, &file,
-                        -1);
-
-    uri = nautilus_file_get_uri (file);
-    DEBUG ("Row collapsed callback for uri %s", uri);
-    g_free (uri);
-
-    directory = NULL;
-    if (gtk_tree_model_iter_parent (model, &parent, iter))
-    {
-        gtk_tree_model_get (model, &parent,
-                            NAUTILUS_LIST_MODEL_SUBDIRECTORY_COLUMN, &directory,
-                            -1);
-    }
-
-    unload_data = unload_delay_data_new (file, directory, view);
-    g_timeout_add_seconds (COLLAPSE_TO_UNLOAD_DELAY,
-                           unload_file_timeout,
-                           unload_data);
-
-    nautilus_file_unref (file);
-    nautilus_directory_unref (directory);
-}
-
-static void
-subdirectory_unloaded_callback (NautilusListModel *model,
-                                NautilusDirectory *directory,
-                                gpointer           callback_data)
-{
-    NautilusListView *view;
-
-    g_return_if_fail (NAUTILUS_IS_LIST_MODEL (model));
-    g_return_if_fail (NAUTILUS_IS_DIRECTORY (directory));
-
-    view = NAUTILUS_LIST_VIEW (callback_data);
-
-    g_signal_handlers_disconnect_by_func (directory,
-                                          G_CALLBACK (subdirectory_done_loading_callback),
-                                          view);
-    nautilus_files_view_remove_subdirectory (NAUTILUS_FILES_VIEW (view), directory);
-}
-
-static gboolean
-test_expand_row_callback (GtkTreeView *tree_view,
-                          GtkTreeIter *iter,
-                          GtkTreePath *path,
-                          gboolean     user_data)
-{
-    return !g_settings_get_boolean (nautilus_list_view_preferences,
-                                    NAUTILUS_PREFERENCES_LIST_VIEW_USE_TREE);
-}
-
-static void
-nautilus_list_view_reveal_selection (NautilusFilesView *view)
-{
-    g_autolist (NautilusFile) selection = NULL;
-
-    g_return_if_fail (NAUTILUS_IS_LIST_VIEW (view));
-
-    selection = nautilus_view_get_selection (NAUTILUS_VIEW (view));
-
-    /* Make sure at least one of the selected items is scrolled into view */
-    if (selection != NULL)
-    {
-        NautilusListView *list_view;
-        NautilusFile *file;
-        GtkTreeIter iter;
-        GtkTreePath *path;
-
-        list_view = NAUTILUS_LIST_VIEW (view);
-        file = selection->data;
-        if (nautilus_list_model_get_first_iter_for_file (list_view->details->model, file, &iter))
-        {
-            path = gtk_tree_model_get_path (GTK_TREE_MODEL (list_view->details->model), &iter);
-
-            gtk_tree_view_scroll_to_cell (list_view->details->tree_view, path, NULL, FALSE, 0.0, 0.0);
-
-            gtk_tree_path_free (path);
-        }
-    }
-}
-
-static gboolean
-sort_criterion_changes_due_to_user (GtkTreeView *tree_view)
-{
-    GList *columns, *p;
-    GtkTreeViewColumn *column;
-    GSignalInvocationHint *ihint;
-    gboolean ret;
-
-    ret = FALSE;
-
-    columns = gtk_tree_view_get_columns (tree_view);
-    for (p = columns; p != NULL; p = p->next)
-    {
-        column = p->data;
-        ihint = g_signal_get_invocation_hint (column);
-        if (ihint != NULL)
-        {
-            ret = TRUE;
-            break;
-        }
-    }
-    g_list_free (columns);
-
-    return ret;
-}
-
-static void
-sort_column_changed_callback (GtkTreeSortable  *sortable,
-                              NautilusListView *view)
-{
-    NautilusFile *file;
-    gint sort_column_id, default_sort_column_id;
-    GtkSortType reversed;
-    GQuark sort_attr, default_sort_attr;
-    char *reversed_attr, *default_reversed_attr;
-    gboolean default_sort_reversed;
-
-    file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (view));
-
-    gtk_tree_sortable_get_sort_column_id (sortable, &sort_column_id, &reversed);
-    sort_attr = nautilus_list_model_get_attribute_from_sort_column_id (view->details->model, sort_column_id);
-
-    default_sort_column_id = nautilus_list_model_get_sort_column_id_from_attribute (view->details->model,
-                                                                                    g_quark_from_string 
(get_default_sort_order (file, &default_sort_reversed)));
-    default_sort_attr = nautilus_list_model_get_attribute_from_sort_column_id (view->details->model, 
default_sort_column_id);
-    nautilus_file_set_metadata (file, NAUTILUS_METADATA_KEY_LIST_VIEW_SORT_COLUMN,
-                                g_quark_to_string (default_sort_attr), g_quark_to_string (sort_attr));
-
-    default_reversed_attr = (default_sort_reversed ? "true" : "false");
-
-    if (view->details->last_sort_attr != sort_attr &&
-        sort_criterion_changes_due_to_user (view->details->tree_view))
-    {
-        /* at this point, the sort order is always GTK_SORT_ASCENDING, if the sort column ID
-         * switched. Invert the sort order, if it's the default criterion with a reversed preference,
-         * or if it makes sense for the attribute (i.e. date). */
-        if (sort_attr == default_sort_attr)
-        {
-            /* use value from preferences */
-            reversed = g_settings_get_boolean (nautilus_preferences,
-                                               NAUTILUS_PREFERENCES_DEFAULT_SORT_IN_REVERSE_ORDER);
-        }
-        else
-        {
-            reversed = nautilus_file_is_date_sort_attribute_q (sort_attr);
-        }
-
-        if (reversed)
-        {
-            g_signal_handlers_block_by_func (sortable, sort_column_changed_callback, view);
-            gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (view->details->model),
-                                                  sort_column_id,
-                                                  GTK_SORT_DESCENDING);
-            g_signal_handlers_unblock_by_func (sortable, sort_column_changed_callback, view);
-        }
-    }
-
-
-    reversed_attr = (reversed ? "true" : "false");
-    nautilus_file_set_metadata (file, NAUTILUS_METADATA_KEY_LIST_VIEW_SORT_REVERSED,
-                                default_reversed_attr, reversed_attr);
-
-    /* Make sure selected item(s) is visible after sort */
-    nautilus_list_view_reveal_selection (NAUTILUS_FILES_VIEW (view));
-
-    view->details->last_sort_attr = sort_attr;
-}
-
-#if 0 && NAUTILUS_DND_NEEDS_GTK4_REIMPLEMENTATION
-static char *
-get_root_uri_callback (NautilusTreeViewDragDest *dest,
-                       gpointer                  user_data)
-{
-    NautilusListView *view;
-
-    view = NAUTILUS_LIST_VIEW (user_data);
-
-    return nautilus_files_view_get_uri (NAUTILUS_FILES_VIEW (view));
-}
-
-static NautilusFile *
-get_file_for_path_callback (NautilusTreeViewDragDest *dest,
-                            GtkTreePath              *path,
-                            gpointer                  user_data)
-{
-    NautilusListView *view;
-
-    view = NAUTILUS_LIST_VIEW (user_data);
-
-    return nautilus_list_model_file_for_path (view->details->model, path);
-}
-
-
-static void
-list_view_handle_uri_list (NautilusTreeViewDragDest *dest,
-                           const char               *item_uris,
-                           const char               *target_uri,
-                           GdkDragAction             action,
-                           NautilusListView         *view)
-{
-    nautilus_files_view_handle_uri_list_drop (NAUTILUS_FILES_VIEW (view),
-                                              item_uris, target_uri, action);
-}
-
-static void
-list_view_handle_text (NautilusTreeViewDragDest *dest,
-                       const char               *text,
-                       const char               *target_uri,
-                       GdkDragAction             action,
-                       NautilusListView         *view)
-{
-    nautilus_files_view_handle_text_drop (NAUTILUS_FILES_VIEW (view),
-                                          text, target_uri, action);
-}
-
-static void
-list_view_handle_raw (NautilusTreeViewDragDest *dest,
-                      const char               *raw_data,
-                      int                       length,
-                      const char               *target_uri,
-                      const char               *direct_save_uri,
-                      GdkDragAction             action,
-                      NautilusListView         *view)
-{
-    nautilus_files_view_handle_raw_drop (NAUTILUS_FILES_VIEW (view),
-                                         raw_data, length, target_uri, direct_save_uri,
-                                         action);
-}
-
-static void
-list_view_handle_hover (NautilusTreeViewDragDest *dest,
-                        const char               *target_uri,
-                        NautilusListView         *view)
-{
-    nautilus_files_view_handle_hover (NAUTILUS_FILES_VIEW (view), target_uri);
-}
-
-static void
-move_copy_items_callback (NautilusTreeViewDragDest *dest,
-                          const GList              *item_uris,
-                          const char               *target_uri,
-                          guint                     action,
-                          gpointer                  user_data)
-{
-    NautilusFilesView *view = user_data;
-
-    nautilus_clipboard_clear_if_colliding_uris (GTK_WIDGET (view),
-                                                item_uris);
-    nautilus_files_view_move_copy_items (view,
-                                         item_uris,
-                                         target_uri,
-                                         action);
-}
-#endif
-
-static void
-column_header_menu_toggled (GtkCheckButton   *menu_item,
-                            NautilusListView *list_view)
-{
-    NautilusFile *file;
-    char **visible_columns;
-    char **column_order;
-    const char *column;
-    gboolean active;
-    GPtrArray *ptr_array;
-    int i;
-
-    file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (list_view));
-    visible_columns = get_visible_columns (list_view);
-    column_order = get_column_order (list_view);
-    column = g_object_get_data (G_OBJECT (menu_item), "column-name");
-    active = gtk_check_button_get_active (menu_item);
-
-    /* Rebuild visible_columns to add or remove the toggled column. */
-    ptr_array = g_ptr_array_sized_new (g_strv_length (visible_columns));
-    for (i = 0; visible_columns[i] != NULL; ++i)
-    {
-        if (!active && g_strcmp0 (visible_columns[i], column))
-        {
-            continue;
-        }
-        g_ptr_array_add (ptr_array, visible_columns[i]);
-    }
-
-    if (active)
-    {
-        g_ptr_array_add (ptr_array, g_strdup (column));
-    }
-    g_ptr_array_add (ptr_array, NULL);
-
-    g_free (visible_columns);
-    visible_columns = (gchar **) g_ptr_array_free (ptr_array, FALSE);
-
-    nautilus_file_set_metadata_list (file,
-                                     NAUTILUS_METADATA_KEY_LIST_VIEW_VISIBLE_COLUMNS,
-                                     visible_columns);
-
-    /* set view values ourselves, as new metadata could not have been
-     * updated yet.
-     */
-    apply_columns_settings (list_view, column_order, visible_columns);
-
-    g_strfreev (column_order);
-    g_strfreev (visible_columns);
-}
-
-static void
-column_header_menu_use_default (GtkButton        *menu_item,
-                                NautilusListView *list_view)
-{
-    NautilusFile *file;
-    char **default_columns;
-    char **default_order;
-
-    file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (list_view));
-
-    nautilus_file_set_metadata_list (file, NAUTILUS_METADATA_KEY_LIST_VIEW_COLUMN_ORDER, NULL);
-    nautilus_file_set_metadata_list (file, NAUTILUS_METADATA_KEY_LIST_VIEW_VISIBLE_COLUMNS, NULL);
-
-    default_columns = get_default_visible_columns (list_view);
-    default_order = get_default_column_order (list_view);
-
-    /* set view values ourselves, as new metadata could not have been
-     * updated yet.
-     */
-    apply_columns_settings (list_view, default_order, default_columns);
-    /* Popdown the popover because the checkboxes are not updated. */
-    gtk_popover_popdown (GTK_POPOVER (list_view->details->columns_popover));
-
-    g_strfreev (default_columns);
-    g_strfreev (default_order);
-}
-
-static void
-popup_column_header_menu (NautilusListView *list_view,
-                          gdouble           x,
-                          gdouble           y)
-{
-    NautilusFile *file;
-    char **visible_columns;
-    char **column_order;
-    GList *all_columns;
-    GHashTable *visible_columns_hash;
-    int i;
-    GList *l;
-    GtkPopover *popover;
-    GtkWidget *menu;
-    GtkWidget *menu_item;
-
-    file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (list_view));
-
-    visible_columns = get_visible_columns (list_view);
-    column_order = get_column_order (list_view);
-
-    all_columns = nautilus_get_columns_for_file (file);
-    all_columns = nautilus_sort_columns (all_columns, column_order);
-
-    /* hash table to lookup if a given column should be visible */
-    visible_columns_hash = g_hash_table_new_full (g_str_hash,
-                                                  g_str_equal,
-                                                  (GDestroyNotify) g_free,
-                                                  (GDestroyNotify) g_free);
-    /* always show name column */
-    g_hash_table_insert (visible_columns_hash, g_strdup ("name"), g_strdup ("name"));
-    if (visible_columns != NULL)
-    {
-        for (i = 0; visible_columns[i] != NULL; ++i)
-        {
-            g_hash_table_insert (visible_columns_hash,
-                                 g_ascii_strdown (visible_columns[i], -1),
-                                 g_ascii_strdown (visible_columns[i], -1));
-        }
-    }
-
-    popover = GTK_POPOVER (list_view->details->columns_popover);
-    menu = list_view->details->columns_popover_box;
-    /* Remove all old items before repopulating. */
-    while ((menu_item = gtk_widget_get_first_child (menu)) != NULL)
-    {
-        gtk_box_remove (GTK_BOX (menu), menu_item);
-    }
-
-    for (l = all_columns; l != NULL; l = l->next)
-    {
-        char *name;
-        char *label;
-        char *lowercase;
-
-        g_object_get (G_OBJECT (l->data),
-                      "name", &name,
-                      "label", &label,
-                      NULL);
-        lowercase = g_ascii_strdown (name, -1);
-
-        menu_item = gtk_check_button_new_with_label (label);
-        gtk_box_append (GTK_BOX (menu), menu_item);
-
-        g_object_set_data_full (G_OBJECT (menu_item),
-                                "column-name", name, g_free);
-
-        /* name is always visible */
-        if (strcmp (lowercase, "name") == 0 || strcmp (lowercase, "starred") == 0)
-        {
-            gtk_widget_set_sensitive (menu_item, FALSE);
-        }
-
-        if (g_hash_table_lookup (visible_columns_hash, lowercase) != NULL)
-        {
-            gtk_check_button_set_active (GTK_CHECK_BUTTON (menu_item), TRUE);
-        }
-
-        g_signal_connect (menu_item,
-                          "toggled",
-                          G_CALLBACK (column_header_menu_toggled),
-                          list_view);
-
-        g_free (lowercase);
-        g_free (label);
-    }
-
-    menu_item = gtk_button_new_with_label (_("Use Default"));
-    gtk_box_append (GTK_BOX (menu), menu_item);
-
-    g_signal_connect (menu_item,
-                      "clicked",
-                      G_CALLBACK (column_header_menu_use_default),
-                      list_view);
-
-    gtk_widget_show (menu);
-    gtk_popover_set_pointing_to (popover, &(GdkRectangle){x, y, 0, 0});
-    gtk_popover_popup (popover);
-
-    g_hash_table_destroy (visible_columns_hash);
-    nautilus_column_list_free (all_columns);
-    g_strfreev (column_order);
-    g_strfreev (visible_columns);
-}
-
-static void
-apply_columns_settings (NautilusListView  *list_view,
-                        char             **column_order,
-                        char             **visible_columns)
-{
-    GList *all_columns;
-    NautilusFile *file;
-    GList *old_view_columns, *view_columns;
-    GHashTable *visible_columns_hash;
-    g_autoptr (GFile) location = NULL;
-    GtkTreeViewColumn *prev_view_column;
-    GList *l;
-    int i;
-
-    file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (list_view));
-    location = nautilus_file_get_location (file);
-
-    /* prepare ordered list of view columns using column_order and visible_columns */
-    view_columns = NULL;
-
-    all_columns = nautilus_get_columns_for_file (file);
-    all_columns = nautilus_sort_columns (all_columns, column_order);
-
-    /* hash table to lookup if a given column should be visible */
-    visible_columns_hash = g_hash_table_new_full (g_str_hash,
-                                                  g_str_equal,
-                                                  (GDestroyNotify) g_free,
-                                                  (GDestroyNotify) g_free);
-    /* always show name column */
-    g_hash_table_insert (visible_columns_hash, g_strdup ("name"), g_strdup ("name"));
-
-    /* always show star column if supported */
-    if (nautilus_tag_manager_can_star_contents (nautilus_tag_manager_get (), location) ||
-        nautilus_is_starred_directory (location))
-    {
-        g_hash_table_insert (visible_columns_hash, g_strdup ("starred"), g_strdup ("starred"));
-    }
-
-    if (visible_columns != NULL)
-    {
-        for (i = 0; visible_columns[i] != NULL; ++i)
-        {
-            g_hash_table_insert (visible_columns_hash,
-                                 g_ascii_strdown (visible_columns[i], -1),
-                                 g_ascii_strdown (visible_columns[i], -1));
-        }
-    }
-
-    for (l = all_columns; l != NULL; l = l->next)
-    {
-        char *name;
-        char *lowercase;
-
-        g_object_get (G_OBJECT (l->data), "name", &name, NULL);
-        lowercase = g_ascii_strdown (name, -1);
-
-        if (g_hash_table_lookup (visible_columns_hash, lowercase) != NULL)
-        {
-            GtkTreeViewColumn *view_column;
-
-            view_column = g_hash_table_lookup (list_view->details->columns, name);
-            if (view_column != NULL)
-            {
-                view_columns = g_list_prepend (view_columns, view_column);
-            }
-        }
-
-        g_free (name);
-        g_free (lowercase);
-    }
-
-    g_hash_table_destroy (visible_columns_hash);
-    nautilus_column_list_free (all_columns);
-
-    view_columns = g_list_reverse (view_columns);
-
-    /* hide columns that are not present in the configuration */
-    old_view_columns = gtk_tree_view_get_columns (list_view->details->tree_view);
-    for (l = old_view_columns; l != NULL; l = l->next)
-    {
-        if (g_list_find (view_columns, l->data) == NULL)
-        {
-            gtk_tree_view_column_set_visible (l->data, FALSE);
-        }
-    }
-    g_list_free (old_view_columns);
-
-    /* show new columns from the configuration */
-    for (l = view_columns; l != NULL; l = l->next)
-    {
-        gtk_tree_view_column_set_visible (l->data, TRUE);
-    }
-
-    /* place columns in the correct order */
-    prev_view_column = NULL;
-    for (l = view_columns; l != NULL; l = l->next)
-    {
-        gtk_tree_view_move_column_after (list_view->details->tree_view, l->data, prev_view_column);
-        prev_view_column = l->data;
-    }
-    g_list_free (view_columns);
-}
-
-static void
-starred_cell_data_func (GtkTreeViewColumn *column,
-                        GtkCellRenderer   *renderer,
-                        GtkTreeModel      *model,
-                        GtkTreeIter       *iter,
-                        NautilusListView  *view)
-{
-    g_autofree gchar *text = NULL;
-    g_autofree gchar *uri = NULL;
-    NautilusFile *file;
-
-    gtk_tree_model_get (model, iter,
-                        view->details->file_name_column_num, &text,
-                        -1);
-
-    gtk_tree_model_get (GTK_TREE_MODEL (model),
-                        iter,
-                        NAUTILUS_LIST_MODEL_FILE_COLUMN, &file,
-                        -1);
-
-    if (file == NULL)
-    {
-        /* This row is a label, not a file */
-        g_object_set (renderer,
-                      "icon-name", NULL,
-                      "mode", GTK_CELL_RENDERER_MODE_INERT,
-                      NULL);
-        return;
-    }
-
-    uri = nautilus_file_get_uri (file);
-
-    if (nautilus_tag_manager_file_is_starred (nautilus_tag_manager_get (), uri))
-    {
-        g_object_set (renderer,
-                      "icon-name", "starred-symbolic",
-                      NULL);
-    }
-    else
-    {
-        g_object_set (renderer,
-                      "icon-name", "non-starred-symbolic",
-                      NULL);
-    }
-
-    nautilus_file_unref (file);
-}
-
-static void
-filename_cell_data_func (GtkTreeViewColumn *column,
-                         GtkCellRenderer   *renderer,
-                         GtkTreeModel      *model,
-                         GtkTreeIter       *iter,
-                         NautilusListView  *view)
-{
-    char *text;
-    g_autofree gchar *escaped_text = NULL;
-    g_autofree gchar *escaped_name = NULL;
-    g_autofree gchar *replaced_text = NULL;
-    GtkTreePath *path;
-    PangoUnderline underline;
-    GString *display_text;
-    NautilusDirectory *directory;
-    NautilusQuery *query = NULL;
-    NautilusFile *file;
-    const gchar *snippet;
-
-    gtk_tree_model_get (model, iter,
-                        view->details->file_name_column_num, &text,
-                        -1);
-
-    escaped_name = g_markup_escape_text (text, -1);
-    display_text = g_string_new (escaped_name);
-
-    directory = nautilus_files_view_get_model (NAUTILUS_FILES_VIEW (view));
-
-    if (NAUTILUS_IS_SEARCH_DIRECTORY (directory))
-    {
-        query = nautilus_search_directory_get_query (NAUTILUS_SEARCH_DIRECTORY (directory));
-    }
-
-    if (get_click_policy () == NAUTILUS_CLICK_POLICY_SINGLE)
-    {
-        path = gtk_tree_model_get_path (model, iter);
-
-        if (view->details->hover_path == NULL ||
-            gtk_tree_path_compare (path, view->details->hover_path))
-        {
-            underline = PANGO_UNDERLINE_NONE;
-        }
-        else
-        {
-            underline = PANGO_UNDERLINE_SINGLE;
-        }
-
-        gtk_tree_path_free (path);
-    }
-    else
-    {
-        underline = PANGO_UNDERLINE_NONE;
-    }
-
-    if (query &&
-        nautilus_query_get_search_content (query) == NAUTILUS_QUERY_SEARCH_CONTENT_FULL_TEXT)
-    {
-        gtk_tree_model_get (model, iter,
-                            NAUTILUS_LIST_MODEL_FILE_COLUMN, &file,
-                            -1);
-
-        /* Rule out dummy row */
-        if (file != NULL)
-        {
-            snippet = nautilus_file_get_search_fts_snippet (file);
-            if (snippet)
-            {
-                replaced_text = g_regex_replace (view->details->regex,
-                                                 snippet,
-                                                 -1,
-                                                 0,
-                                                 " ",
-                                                 G_REGEX_MATCH_NEWLINE_ANY,
-                                                 NULL);
-
-                escaped_text = g_markup_escape_text (replaced_text, -1);
-
-                g_string_append_printf (display_text,
-                                        " <small><span alpha='50%%'><b>%s</b></span></small>",
-                                        escaped_text);
-            }
-        }
-        nautilus_file_unref (file);
-    }
-
-    g_object_set (G_OBJECT (renderer),
-                  "markup", display_text->str,
-                  "underline", underline,
-                  NULL);
-
-    g_free (text);
-    g_string_free (display_text, TRUE);
-}
-
-static void
-location_cell_data_func (GtkTreeViewColumn *column,
-                         GtkCellRenderer   *renderer,
-                         GtkTreeModel      *model,
-                         GtkTreeIter       *iter,
-                         NautilusListView  *view,
-                         gboolean           show_trash_orig)
-{
-    NautilusDirectory *directory;
-    GFile *home_location;
-    NautilusFile *file;
-    GFile *dir_location;
-    GFile *base_location;
-    gchar *where = NULL;
-
-    directory = nautilus_files_view_get_model (NAUTILUS_FILES_VIEW (view));
-
-    home_location = g_file_new_for_path (g_get_home_dir ());
-
-    gtk_tree_model_get (model, iter,
-                        NAUTILUS_LIST_MODEL_FILE_COLUMN, &file,
-                        -1);
-
-    /* The file might be NULL if we just toggled an expander
-     * and we're still loading the subdirectory.
-     */
-    if (file == NULL)
-    {
-        return;
-    }
-
-    if (show_trash_orig && nautilus_file_is_in_trash (file))
-    {
-        NautilusFile *orig_file;
-
-        orig_file = nautilus_file_get_trash_original_file (file);
-
-        if (orig_file != NULL)
-        {
-            nautilus_file_unref (file);
-            file = orig_file;
-        }
-    }
-
-    if (!nautilus_file_is_in_recent (file))
-    {
-        dir_location = nautilus_file_get_parent_location (file);
-    }
-    else
-    {
-        GFile *activation_location;
-
-        activation_location = nautilus_file_get_activation_location (file);
-        dir_location = g_file_get_parent (activation_location);
-
-        g_object_unref (activation_location);
-    }
-
-    if (!NAUTILUS_IS_SEARCH_DIRECTORY (directory))
-    {
-        base_location = g_object_ref (home_location);
-    }
-    else
-    {
-        NautilusQuery *query;
-        NautilusFile *base;
-        GFile *location;
-
-        query = nautilus_search_directory_get_query (NAUTILUS_SEARCH_DIRECTORY (directory));
-        location = nautilus_query_get_location (query);
-        base = nautilus_file_get (location);
-
-        if (!nautilus_file_is_in_recent (base))
-        {
-            base_location = nautilus_file_get_location (base);
-        }
-        else
-        {
-            base_location = g_object_ref (home_location);
-        }
-
-        nautilus_file_unref (base);
-        g_object_unref (location);
-        g_object_unref (query);
-    }
-
-    if (g_file_equal (base_location, dir_location))
-    {
-        /* Only occurs when search result is
-         * a direct child of the base location
-         */
-        where = g_strdup ("");
-    }
-    else if (g_file_equal (home_location, dir_location))
-    {
-        where = g_strdup (_("Home"));
-    }
-    else if (g_file_has_prefix (dir_location, base_location))
-    {
-        gchar *relative_path;
-
-        relative_path = g_file_get_relative_path (base_location, dir_location);
-        where = g_filename_display_name (relative_path);
-
-        g_free (relative_path);
-    }
-    else
-    {
-        where = g_file_get_path (dir_location);
-    }
-
-    g_object_set (G_OBJECT (renderer),
-                  "text", where,
-                  NULL);
-
-    g_free (where);
-
-    g_object_unref (base_location);
-    g_object_unref (dir_location);
-    nautilus_file_unref (file);
-    g_object_unref (home_location);
-}
-
-
-static void
-where_cell_data_func (GtkTreeViewColumn *column,
-                      GtkCellRenderer   *renderer,
-                      GtkTreeModel      *model,
-                      GtkTreeIter       *iter,
-                      NautilusListView  *view)
-{
-    location_cell_data_func (column, renderer, model, iter, view, FALSE);
-}
-
-static void
-trash_orig_path_cell_data_func (GtkTreeViewColumn *column,
-                                GtkCellRenderer   *renderer,
-                                GtkTreeModel      *model,
-                                GtkTreeIter       *iter,
-                                NautilusListView  *view)
-{
-    location_cell_data_func (column, renderer, model, iter, view, TRUE);
-}
-
-#define SMALL_ZOOM_ICON_PADDING 0
-#define STANDARD_ZOOM_ICON_PADDING 6
-#define LARGE_ZOOM_ICON_PADDING 6
-#define LARGER_ZOOM_ICON_PADDING 6
-
-static gint
-nautilus_list_view_get_icon_padding_for_zoom_level (NautilusListZoomLevel zoom_level)
-{
-    switch (zoom_level)
-    {
-        case NAUTILUS_LIST_ZOOM_LEVEL_SMALL:
-        {
-            return SMALL_ZOOM_ICON_PADDING;
-        }
-
-        case NAUTILUS_LIST_ZOOM_LEVEL_STANDARD:
-        {
-            return STANDARD_ZOOM_ICON_PADDING;
-        }
-
-        case NAUTILUS_LIST_ZOOM_LEVEL_LARGE:
-        {
-            return LARGE_ZOOM_ICON_PADDING;
-        }
-
-        case NAUTILUS_LIST_ZOOM_LEVEL_LARGER:
-        {
-            return LARGER_ZOOM_ICON_PADDING;
-        }
-
-        default:
-        {
-            g_assert_not_reached ();
-        }
-    }
-}
-
-static void
-set_up_pixbuf_size (NautilusListView *view)
-{
-    int icon_size, icon_padding;
-
-    /* Make all rows the same size. */
-    icon_size = nautilus_list_model_get_icon_size_for_zoom_level (view->details->zoom_level);
-    icon_padding = nautilus_list_view_get_icon_padding_for_zoom_level (view->details->zoom_level);
-    gtk_cell_renderer_set_fixed_size (GTK_CELL_RENDERER (view->details->pixbuf_cell),
-                                      -1, icon_size + 2 * icon_padding);
-
-    /* FIXME: https://bugzilla.gnome.org/show_bug.cgi?id=641518 */
-    gtk_tree_view_columns_autosize (view->details->tree_view);
-}
-
-static gint
-get_icon_scale_callback (NautilusListModel *model,
-                         NautilusListView  *view)
-{
-    /* FIXME: Temporary regression: HiDPI icons not supported, ignore scale. */
-    return 1;
-}
-
-static void
-on_longpress_gesture_pressed_event (GtkGestureLongPress *gesture,
-                                    gdouble              x,
-                                    gdouble              y,
-                                    gpointer             user_data)
-{
-    NautilusListView *view = user_data;
-    g_autolist (NautilusFile) selection = NULL;
-
-    selection = nautilus_view_get_selection (NAUTILUS_VIEW (view));
-    if (selection != NULL)
-    {
-        nautilus_files_view_pop_up_selection_context_menu (NAUTILUS_FILES_VIEW (view), x, y);
-    }
-    else
-    {
-        nautilus_files_view_pop_up_background_context_menu (NAUTILUS_FILES_VIEW (view), x, y);
-    }
-}
-
-static void
-on_tree_view_drag_gesture_drag_begin (GtkGestureDrag *gesture,
-                                      gdouble         start_x,
-                                      gdouble         start_y,
-                                      gpointer        user_data)
-{
-#if 0 && NAUTILUS_DND_NEEDS_GTK4_REIMPLEMENTATION
-    nautilus_list_view_dnd_init (NAUTILUS_LIST_VIEW (user_data));
-#endif
-}
-
-static void
-on_tree_view_drag_gesture_drag_update (GtkGestureDrag *gesture,
-                                       gdouble         offset_x,
-                                       gdouble         offset_y,
-                                       gpointer        user_data)
-{
-#if 0 && NAUTILUS_DND_NEEDS_GTK4_REIMPLEMENTATION
-    GdkEventSequence *sequence;
-    GdkEvent *event;
-    NautilusListView *list_view;
-
-    sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
-    event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence);
-    list_view = NAUTILUS_LIST_VIEW (user_data);
-
-    nautilus_list_view_dnd_drag_begin (list_view, offset_x, offset_y, event);
-#endif
-}
-
-static void
-list_view_use_tree_changed_callback (gpointer callback_data)
-{
-    GtkTreeView *tree_view;
-
-    tree_view = GTK_TREE_VIEW (callback_data);
-
-    gtk_tree_view_collapse_all (tree_view);
-}
-
-
-static void
-create_and_set_up_tree_view (NautilusListView *view)
-{
-    GtkCellRenderer *cell;
-    GtkTreeViewColumn *column;
-#if 0 && NAUTILUS_A11Y_NEEDS_GTK4_REIMPLEMENTATION
-    AtkObject *atk_obj;
-#endif
-    GList *nautilus_columns;
-    GList *l;
-    gchar **default_column_order, **default_visible_columns;
-    GtkWidget *content_widget;
-    GtkEventController *controller;
-
-    content_widget = nautilus_files_view_get_content_widget (NAUTILUS_FILES_VIEW (view));
-    view->details->tree_view = GTK_TREE_VIEW (gtk_tree_view_new ());
-    view->details->columns = g_hash_table_new_full (g_str_hash,
-                                                    g_str_equal,
-                                                    (GDestroyNotify) g_free,
-                                                    NULL);
-    gtk_tree_view_set_enable_search (view->details->tree_view, FALSE);
-
-#if 0 && NAUTILUS_DND_NEEDS_GTK4_REIMPLEMENTATION
-    view->details->drag_dest =
-        nautilus_tree_view_drag_dest_new (view->details->tree_view);
-#endif
-
-    /* Stop the tree view from performing select-all actions.
-     * It is desireable that the action is disabled while directory
-     * is loading.
-     */
-    g_signal_connect (view->details->tree_view, "select-all",
-                      G_CALLBACK (g_signal_stop_emission_by_name), "select-all");
-
-#if 0 && NAUTILUS_DND_NEEDS_GTK4_REIMPLEMENTATION
-    g_signal_connect_object (view->details->drag_dest,
-                             "get-root-uri",
-                             G_CALLBACK (get_root_uri_callback),
-                             view, 0);
-    g_signal_connect_object (view->details->drag_dest,
-                             "get-file-for-path",
-                             G_CALLBACK (get_file_for_path_callback),
-                             view, 0);
-    g_signal_connect_object (view->details->drag_dest,
-                             "move-copy-items",
-                             G_CALLBACK (move_copy_items_callback),
-                             view, 0);
-    g_signal_connect_object (view->details->drag_dest, "handle-uri-list",
-                             G_CALLBACK (list_view_handle_uri_list), view, 0);
-    g_signal_connect_object (view->details->drag_dest, "handle-text",
-                             G_CALLBACK (list_view_handle_text), view, 0);
-    g_signal_connect_object (view->details->drag_dest, "handle-raw",
-                             G_CALLBACK (list_view_handle_raw), view, 0);
-    g_signal_connect_object (view->details->drag_dest, "handle-hover",
-                             G_CALLBACK (list_view_handle_hover), view, 0);
-#endif
-
-    g_signal_connect_object (gtk_tree_view_get_selection (view->details->tree_view),
-                             "changed",
-                             G_CALLBACK (list_selection_changed_callback), view, 0);
-
-    controller = GTK_EVENT_CONTROLLER (gtk_gesture_drag_new ());
-    gtk_widget_add_controller (GTK_WIDGET (view->details->tree_view), controller);
-    gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_CAPTURE);
-    gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (controller), 0);
-
-    g_signal_connect (controller, "drag-begin",
-                      G_CALLBACK (on_tree_view_drag_gesture_drag_begin), view);
-    g_signal_connect (controller, "drag-update",
-                      G_CALLBACK (on_tree_view_drag_gesture_drag_update), view);
-
-    controller = GTK_EVENT_CONTROLLER (gtk_gesture_click_new ());
-    gtk_widget_add_controller (GTK_WIDGET (view->details->tree_view), controller);
-
-    gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_CAPTURE);
-    gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (controller), 0);
-
-    g_signal_connect (controller, "pressed",
-                      G_CALLBACK (on_tree_view_click_gesture_pressed), view);
-    g_signal_connect (controller, "released",
-                      G_CALLBACK (on_tree_view_click_gesture_released), view);
-
-    controller = gtk_event_controller_motion_new ();
-    gtk_widget_add_controller (GTK_WIDGET (view->details->tree_view), controller);
-    gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_CAPTURE);
-
-    g_signal_connect (controller, "enter",
-                      G_CALLBACK (on_event_controller_motion_enter), view);
-    g_signal_connect (controller, "leave",
-                      G_CALLBACK (on_event_controller_motion_leave), view);
-    g_signal_connect (controller, "motion",
-                      G_CALLBACK (on_event_controller_motion_motion), view);
-
-    controller = gtk_event_controller_key_new ();
-    gtk_widget_add_controller (GTK_WIDGET (view->details->tree_view), controller);
-
-    gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_BUBBLE);
-
-    g_signal_connect (controller, "key-pressed",
-                      G_CALLBACK (on_event_controller_key_key_pressed), view);
-
-    g_signal_connect_object (view->details->tree_view, "test-expand-row",
-                             G_CALLBACK (test_expand_row_callback), view, 0);
-    g_signal_connect_object (view->details->tree_view, "row-expanded",
-                             G_CALLBACK (row_expanded_callback), view, 0);
-    g_signal_connect_object (view->details->tree_view, "row-collapsed",
-                             G_CALLBACK (row_collapsed_callback), view, 0);
-    g_signal_connect_object (view->details->tree_view, "row-activated",
-                             G_CALLBACK (row_activated_callback), view, 0);
-
-    g_signal_connect_object (nautilus_list_view_preferences,
-                             "changed::" NAUTILUS_PREFERENCES_LIST_VIEW_USE_TREE,
-                             G_CALLBACK (list_view_use_tree_changed_callback),
-                             view->details->tree_view,
-                             G_CONNECT_SWAPPED);
-
-    view->details->model = g_object_new (NAUTILUS_TYPE_LIST_MODEL, NULL);
-    gtk_tree_view_set_model (view->details->tree_view, GTK_TREE_MODEL (view->details->model));
-    /* Need the model for the dnd drop icon "accept" change */
-    nautilus_list_model_set_drag_view (NAUTILUS_LIST_MODEL (view->details->model),
-                                       view->details->tree_view, 0, 0);
-
-    g_signal_connect_object (view->details->model, "sort-column-changed",
-                             G_CALLBACK (sort_column_changed_callback), view, 0);
-
-    g_signal_connect_object (view->details->model, "subdirectory-unloaded",
-                             G_CALLBACK (subdirectory_unloaded_callback), view, 0);
-
-    g_signal_connect_object (view->details->model, "get-icon-scale",
-                             G_CALLBACK (get_icon_scale_callback), view, 0);
-
-    controller = GTK_EVENT_CONTROLLER (gtk_gesture_long_press_new ());
-    gtk_widget_add_controller (GTK_WIDGET (content_widget), controller);
-    gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_CAPTURE);
-    gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (controller), TRUE);
-    g_signal_connect (controller, "pressed",
-                      (GCallback) on_longpress_gesture_pressed_event, view);
-
-    gtk_tree_selection_set_mode (gtk_tree_view_get_selection (view->details->tree_view), 
GTK_SELECTION_MULTIPLE);
-
-    g_settings_bind (nautilus_list_view_preferences, NAUTILUS_PREFERENCES_LIST_VIEW_USE_TREE,
-                     view->details->tree_view, "show-expanders",
-                     G_SETTINGS_BIND_DEFAULT);
-
-    nautilus_columns = nautilus_get_all_columns ();
-
-    for (l = nautilus_columns; l != NULL; l = l->next)
-    {
-        NautilusColumn *nautilus_column;
-        int column_num;
-        char *name;
-        char *label;
-        float xalign;
-        GtkSortType sort_order;
-
-        nautilus_column = NAUTILUS_COLUMN (l->data);
-
-        g_object_get (nautilus_column,
-                      "name", &name,
-                      "label", &label,
-                      "xalign", &xalign,
-                      "default-sort-order", &sort_order,
-                      NULL);
-
-        column_num = nautilus_list_model_add_column (view->details->model,
-                                                     nautilus_column);
-
-        /* Created the name column specially, because it
-         * has the icon in it.*/
-        if (!strcmp (name, "name"))
-        {
-            /* Create the file name column */
-            view->details->file_name_column = gtk_tree_view_column_new ();
-            gtk_tree_view_append_column (view->details->tree_view,
-                                         view->details->file_name_column);
-            view->details->file_name_column_num = column_num;
-
-            g_hash_table_insert (view->details->columns,
-                                 g_strdup ("name"),
-                                 view->details->file_name_column);
-
-            gtk_tree_view_set_search_column (view->details->tree_view, column_num);
-
-            gtk_tree_view_column_set_sort_column_id (view->details->file_name_column, column_num);
-            gtk_tree_view_column_set_title (view->details->file_name_column, _("Name"));
-            gtk_tree_view_column_set_resizable (view->details->file_name_column, TRUE);
-            gtk_tree_view_column_set_expand (view->details->file_name_column, TRUE);
-
-            /* Initial padding */
-            cell = gtk_cell_renderer_text_new ();
-            gtk_tree_view_column_pack_start (view->details->file_name_column, cell, FALSE);
-            g_object_set (cell, "xpad", 6, NULL);
-            g_settings_bind (nautilus_list_view_preferences, NAUTILUS_PREFERENCES_LIST_VIEW_USE_TREE,
-                             cell, "visible",
-                             G_SETTINGS_BIND_INVERT_BOOLEAN | G_SETTINGS_BIND_GET);
-
-            /* File icon */
-            cell = gtk_cell_renderer_pixbuf_new ();
-            view->details->pixbuf_cell = (GtkCellRendererPixbuf *) cell;
-            set_up_pixbuf_size (view);
-
-            gtk_tree_view_column_pack_start (view->details->file_name_column, cell, FALSE);
-            gtk_tree_view_column_set_attributes (view->details->file_name_column,
-                                                 cell,
-                                                 "texture", 
nautilus_list_model_get_column_id_from_zoom_level (view->details->zoom_level),
-                                                 NULL);
-
-            cell = gtk_cell_renderer_text_new ();
-            view->details->file_name_cell = (GtkCellRendererText *) cell;
-            g_object_set (cell,
-                          "ellipsize", PANGO_ELLIPSIZE_END,
-                          "single-paragraph-mode", FALSE,
-                          "width-chars", 30,
-                          "xpad", 5,
-                          NULL);
-
-            gtk_tree_view_column_pack_start (view->details->file_name_column, cell, TRUE);
-            gtk_tree_view_column_set_cell_data_func (view->details->file_name_column, cell,
-                                                     (GtkTreeCellDataFunc) filename_cell_data_func,
-                                                     view, NULL);
-        }
-        else
-        {
-            if (g_strcmp0 (name, "starred") == 0)
-            {
-                cell = gtk_cell_renderer_pixbuf_new ();
-                g_object_set (cell,
-                              "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE,
-                              NULL);
-
-                column = gtk_tree_view_column_new_with_attributes ("",
-                                                                   cell,
-                                                                   NULL);
-                gtk_tree_view_column_set_fixed_width (column, STAR_COLUMN_WIDTH);
-            }
-            else
-            {
-                PangoAttrList *attr_list = pango_attr_list_new ();
-
-                cell = gtk_cell_renderer_text_new ();
-
-                column = gtk_tree_view_column_new_with_attributes (label,
-                                                                   cell,
-                                                                   "text", column_num,
-                                                                   NULL);
-
-                pango_attr_list_insert (attr_list, pango_attr_foreground_alpha_new (ALPHA_55_PERCENT));
-                g_object_set (cell, "attributes", attr_list, NULL);
-                pango_attr_list_unref (attr_list);
-            }
-
-            gtk_tree_view_column_set_alignment (column, xalign);
-            g_object_set (cell,
-                          "xalign", xalign,
-                          "xpad", 5,
-                          NULL);
-            if (!strcmp (name, "permissions"))
-            {
-                g_object_set (cell,
-                              "family", "Monospace",
-                              NULL);
-            }
-            view->details->cells = g_list_append (view->details->cells,
-                                                  cell);
-
-            gtk_tree_view_append_column (view->details->tree_view, column);
-            gtk_tree_view_column_set_sort_column_id (column, column_num);
-            g_hash_table_insert (view->details->columns,
-                                 g_strdup (name),
-                                 column);
-
-            gtk_tree_view_column_set_resizable (column, TRUE);
-            gtk_tree_view_column_set_sort_order (column, sort_order);
-
-            if (!strcmp (name, "where"))
-            {
-                gtk_tree_view_column_set_cell_data_func (column, cell,
-                                                         (GtkTreeCellDataFunc) where_cell_data_func,
-                                                         view, NULL);
-            }
-            else if (!strcmp (name, "trash_orig_path"))
-            {
-                gtk_tree_view_column_set_cell_data_func (column, cell,
-                                                         (GtkTreeCellDataFunc) 
trash_orig_path_cell_data_func,
-                                                         view, NULL);
-            }
-            else if (!strcmp (name, "starred"))
-            {
-                gtk_tree_view_column_set_cell_data_func (column, cell,
-                                                         (GtkTreeCellDataFunc) starred_cell_data_func,
-                                                         view, NULL);
-            }
-        }
-        g_free (name);
-        g_free (label);
-    }
-    nautilus_column_list_free (nautilus_columns);
-
-    default_visible_columns = g_settings_get_strv (nautilus_list_view_preferences,
-                                                   NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_VISIBLE_COLUMNS);
-    default_column_order = g_settings_get_strv (nautilus_list_view_preferences,
-                                                NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_COLUMN_ORDER);
-
-    /* Apply the default column order and visible columns, to get it
-     * right most of the time. The metadata will be checked when a
-     * folder is loaded */
-    apply_columns_settings (view,
-                            default_column_order,
-                            default_visible_columns);
-
-    gtk_widget_show (GTK_WIDGET (view->details->tree_view));
-    gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (content_widget),
-                                   GTK_WIDGET (view->details->tree_view));
-
-#if 0 && NAUTILUS_A11Y_NEEDS_GTK4_REIMPLEMENTATION
-    atk_obj = gtk_widget_get_accessible (GTK_WIDGET (view->details->tree_view));
-    atk_object_set_name (atk_obj, _("List View"));
-#endif
-
-    g_strfreev (default_visible_columns);
-    g_strfreev (default_column_order);
-}
-
-static void
-nautilus_list_view_add_files (NautilusFilesView *view,
-                              GList             *files)
-{
-    NautilusListModel *model;
-    GList *l;
-
-    model = NAUTILUS_LIST_VIEW (view)->details->model;
-    for (l = files; l != NULL; l = l->next)
-    {
-        NautilusFile *parent;
-        NautilusDirectory *directory;
-
-        parent = nautilus_file_get_parent (NAUTILUS_FILE (l->data));
-        directory = nautilus_directory_get_for_file (parent);
-        nautilus_list_model_add_file (model, NAUTILUS_FILE (l->data), directory);
-
-        nautilus_file_unref (parent);
-        nautilus_directory_unref (directory);
-    }
-}
-
-static char **
-get_default_visible_columns (NautilusListView *list_view)
-{
-    NautilusFile *file;
-    NautilusDirectory *directory;
-
-    file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (list_view));
-
-    if (nautilus_file_is_in_trash (file))
-    {
-        return g_strdupv ((gchar **) default_trash_visible_columns);
-    }
-
-    if (nautilus_file_is_in_recent (file))
-    {
-        return g_strdupv ((gchar **) default_recent_visible_columns);
-    }
-
-    directory = nautilus_files_view_get_model (NAUTILUS_FILES_VIEW (list_view));
-    if (NAUTILUS_IS_SEARCH_DIRECTORY (directory))
-    {
-        return g_strdupv ((gchar **) default_search_visible_columns);
-    }
-
-    return g_settings_get_strv (nautilus_list_view_preferences,
-                                NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_VISIBLE_COLUMNS);
-}
-
-static char **
-get_visible_columns (NautilusListView *list_view)
-{
-    NautilusFile *file;
-    g_autofree gchar **visible_columns = NULL;
-
-    file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (list_view));
-
-    visible_columns = nautilus_file_get_metadata_list (file,
-                                                       NAUTILUS_METADATA_KEY_LIST_VIEW_VISIBLE_COLUMNS);
-    if (visible_columns == NULL)
-    {
-        return get_default_visible_columns (list_view);
-    }
-
-    return g_steal_pointer (&visible_columns);
-}
-
-static char **
-get_default_column_order (NautilusListView *list_view)
-{
-    NautilusFile *file;
-    NautilusDirectory *directory;
-
-    file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (list_view));
-
-    if (nautilus_file_is_in_trash (file))
-    {
-        return g_strdupv ((gchar **) default_trash_columns_order);
-    }
-
-    if (nautilus_file_is_in_recent (file))
-    {
-        return g_strdupv ((gchar **) default_recent_columns_order);
-    }
-
-    directory = nautilus_files_view_get_model (NAUTILUS_FILES_VIEW (list_view));
-    if (NAUTILUS_IS_SEARCH_DIRECTORY (directory))
-    {
-        return g_strdupv ((gchar **) default_search_columns_order);
-    }
-
-    return g_settings_get_strv (nautilus_list_view_preferences,
-                                NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_COLUMN_ORDER);
-}
-
-static char **
-get_column_order (NautilusListView *list_view)
-{
-    NautilusFile *file;
-    gchar **column_order;
-
-    file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (list_view));
-
-    column_order = nautilus_file_get_metadata_list
-                       (file,
-                       NAUTILUS_METADATA_KEY_LIST_VIEW_COLUMN_ORDER);
-
-    if (column_order != NULL)
-    {
-        return column_order;
-    }
-
-    return get_default_column_order (list_view);
-}
-
-static void
-check_allow_sort (NautilusListView *list_view)
-{
-    GList *column_names;
-    GList *l;
-    NautilusFile *file;
-    GtkTreeViewColumn *column;
-    gboolean allow_sorting;
-    int sort_column_id;
-
-    column_names = g_hash_table_get_keys (list_view->details->columns);
-    file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (list_view));
-    allow_sorting = !(nautilus_file_is_in_recent (file) || nautilus_file_is_in_search (file));
-
-    for (l = column_names; l != NULL; l = l->next)
-    {
-        column = g_hash_table_lookup (list_view->details->columns, l->data);
-        if (allow_sorting)
-        {
-            sort_column_id = nautilus_list_model_get_sort_column_id_from_attribute 
(list_view->details->model,
-                                                                                    g_quark_from_string 
(l->data));
-            /* Restore its original sorting id. We rely on that the keys of the hashmap
-             * use the same string than the sort criterias */
-            gtk_tree_view_column_set_sort_column_id (column, sort_column_id);
-        }
-        else
-        {
-            /* This disables the header and any sorting capability (like shortcuts),
-             * but leaving them interactionable so the user can still resize them */
-            gtk_tree_view_column_set_sort_column_id (column, -1);
-        }
-    }
-
-    g_list_free (column_names);
-}
-
-static void
-set_columns_settings_from_metadata_and_preferences (NautilusListView *list_view)
-{
-    char **column_order;
-    char **visible_columns;
-
-    column_order = get_column_order (list_view);
-    visible_columns = get_visible_columns (list_view);
-
-    apply_columns_settings (list_view, column_order, visible_columns);
-
-    g_strfreev (column_order);
-    g_strfreev (visible_columns);
-}
-
-static void
-set_sort_order_from_metadata_and_preferences (NautilusListView *list_view)
-{
-    char *sort_attribute;
-    int sort_column_id;
-    NautilusFile *file;
-    gboolean sort_reversed, default_sort_reversed;
-    const gchar *default_sort_order;
-
-    file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (list_view));
-    default_sort_order = get_default_sort_order (file, &default_sort_reversed);
-    if (!(nautilus_file_is_in_recent (file) || nautilus_file_is_in_search (file)))
-    {
-        sort_attribute = nautilus_file_get_metadata (file,
-                                                     NAUTILUS_METADATA_KEY_LIST_VIEW_SORT_COLUMN,
-                                                     NULL);
-        sort_column_id = nautilus_list_model_get_sort_column_id_from_attribute (list_view->details->model,
-                                                                                g_quark_from_string 
(sort_attribute));
-        g_free (sort_attribute);
-
-        if (sort_column_id == -1)
-        {
-            sort_column_id =
-                nautilus_list_model_get_sort_column_id_from_attribute (list_view->details->model,
-                                                                       g_quark_from_string 
(default_sort_order));
-        }
-
-        sort_reversed = nautilus_file_get_boolean_metadata (file,
-                                                            NAUTILUS_METADATA_KEY_LIST_VIEW_SORT_REVERSED,
-                                                            default_sort_reversed);
-    }
-    else
-    {
-        /* Make sure we use the default one and not one that the user used previously
-         * of the change to not allow sorting on search and recent, or the
-         * case that the user or some app modified directly the metadata */
-        sort_column_id = nautilus_list_model_get_sort_column_id_from_attribute (list_view->details->model,
-                                                                                g_quark_from_string 
(default_sort_order));
-        sort_reversed = default_sort_reversed;
-    }
-    gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (list_view->details->model),
-                                          sort_column_id,
-                                          sort_reversed ? GTK_SORT_DESCENDING : GTK_SORT_ASCENDING);
-}
-
-static NautilusListZoomLevel
-get_default_zoom_level (void)
-{
-    NautilusListZoomLevel default_zoom_level;
-
-    default_zoom_level = g_settings_get_enum (nautilus_list_view_preferences,
-                                              NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_ZOOM_LEVEL);
-
-    if (default_zoom_level < NAUTILUS_LIST_ZOOM_LEVEL_SMALL
-        || default_zoom_level > NAUTILUS_LIST_ZOOM_LEVEL_LARGER)
-    {
-        default_zoom_level = NAUTILUS_LIST_ZOOM_LEVEL_STANDARD;
-    }
-
-    return default_zoom_level;
-}
-
-static void
-nautilus_list_view_begin_loading (NautilusFilesView *view)
-{
-    NautilusListView *list_view;
-
-    list_view = NAUTILUS_LIST_VIEW (view);
-
-    nautilus_list_view_sort_directories_first_changed (NAUTILUS_FILES_VIEW (list_view));
-    set_sort_order_from_metadata_and_preferences (list_view);
-    set_columns_settings_from_metadata_and_preferences (list_view);
-    check_allow_sort (list_view);
-}
-
-static void
-nautilus_list_view_clear (NautilusFilesView *view)
-{
-    NautilusListView *list_view;
-    GtkTreeView *tree_view;
-    GtkTreeSelection *tree_selection;
-    GtkTreePath *path;
-
-    list_view = NAUTILUS_LIST_VIEW (view);
-
-    if (list_view->details->model != NULL)
-    {
-        tree_view = list_view->details->tree_view;
-
-        /* When the current cursor's row gets deleted, GTK will move the cursor to
-         * the next row, and when setting the cursor it also selects the new
-         * cursor's row, thereby triggering selection signals. The new cursor will
-         * soon be deleted again and the loop repeats.
-         *
-         * Since clear() removes all entries, those selections are useless but they
-         * take up most of the time in clear(). For example, when a search returns
-         * a large list, exiting from the search view would make nautilus hang.
-         *
-         * At the time the code is written simply removing the cursor solves the
-         * problem, but to be future-proof in case GTK does anything fancy with
-         * the current selection, we also remove the selection.
-         *
-         * Because GTK internally seeking the cursor takes time, only blocking the
-         * selection signal like everywhere else will not remove that overhead.
-         */
-
-        /* Clear the current selection */
-        tree_selection = gtk_tree_view_get_selection (tree_view);
-        gtk_tree_selection_unselect_all (tree_selection);
-
-        /* Clear the current cursor */
-        path = gtk_tree_path_new ();
-        gtk_tree_view_set_cursor (tree_view, path, NULL, FALSE);
-        gtk_tree_path_free (path);
-
-        nautilus_list_model_clear (list_view->details->model);
-    }
-}
-
-static void
-nautilus_list_view_file_changed (NautilusFilesView *view,
-                                 NautilusFile      *file,
-                                 NautilusDirectory *directory)
-{
-    NautilusListView *listview;
-
-    listview = NAUTILUS_LIST_VIEW (view);
-
-    nautilus_list_model_file_changed (listview->details->model, file, directory);
-}
-
-typedef struct
-{
-    GtkTreePath *path;
-    gboolean is_common;
-    gboolean is_root;
-} HasCommonParentData;
-
-static void
-tree_selection_has_common_parent_foreach_func (GtkTreeModel *model,
-                                               GtkTreePath  *path,
-                                               GtkTreeIter  *iter,
-                                               gpointer      user_data)
-{
-    HasCommonParentData *data;
-    GtkTreePath *parent_path;
-    gboolean has_parent;
-
-    data = (HasCommonParentData *) user_data;
-
-    parent_path = gtk_tree_path_copy (path);
-    gtk_tree_path_up (parent_path);
-
-    has_parent = (gtk_tree_path_get_depth (parent_path) > 0) ? TRUE : FALSE;
-
-    if (!has_parent)
-    {
-        data->is_root = TRUE;
-    }
-
-    if (data->is_common && !data->is_root)
-    {
-        if (data->path == NULL)
-        {
-            data->path = gtk_tree_path_copy (parent_path);
-        }
-        else if (gtk_tree_path_compare (data->path, parent_path) != 0)
-        {
-            data->is_common = FALSE;
-        }
-    }
-
-    gtk_tree_path_free (parent_path);
-}
-
-static void
-tree_selection_has_common_parent (GtkTreeSelection *selection,
-                                  gboolean         *is_common,
-                                  gboolean         *is_root)
-{
-    HasCommonParentData data;
-
-    g_assert (is_common != NULL);
-    g_assert (is_root != NULL);
-
-    data.path = NULL;
-    data.is_common = *is_common = TRUE;
-    data.is_root = *is_root = FALSE;
-
-    gtk_tree_selection_selected_foreach (selection,
-                                         tree_selection_has_common_parent_foreach_func,
-                                         &data);
-
-    *is_common = data.is_common;
-    *is_root = data.is_root;
-
-    if (data.path != NULL)
-    {
-        gtk_tree_path_free (data.path);
-    }
-}
-
-static char *
-nautilus_list_view_get_backing_uri (NautilusFilesView *view)
-{
-    NautilusListView *list_view;
-    NautilusListModel *list_model;
-    NautilusFile *file;
-    GtkTreeView *tree_view;
-    GtkTreeSelection *selection;
-    GtkTreePath *path;
-    GList *paths;
-    guint length;
-    char *uri;
-
-    g_return_val_if_fail (NAUTILUS_IS_LIST_VIEW (view), NULL);
-
-    list_view = NAUTILUS_LIST_VIEW (view);
-    list_model = list_view->details->model;
-    tree_view = list_view->details->tree_view;
-
-    g_assert (list_model);
-
-    /* We currently handle three common cases here:
-     * (a) if the selection contains non-filesystem items (i.e., the
-     *     "(Empty)" label), we return the uri of the parent.
-     * (b) if the selection consists of exactly one _expanded_ directory, we
-     *     return its URI.
-     * (c) if the selection consists of either exactly one item which is not
-     *     an expanded directory) or multiple items in the same directory,
-     *     we return the URI of the common parent.
-     */
-
-    uri = NULL;
-
-    selection = gtk_tree_view_get_selection (tree_view);
-    length = gtk_tree_selection_count_selected_rows (selection);
-
-    if (length == 1)
-    {
-        paths = gtk_tree_selection_get_selected_rows (selection, NULL);
-        path = (GtkTreePath *) paths->data;
-
-        file = nautilus_list_model_file_for_path (list_model, path);
-        if (file == NULL)
-        {
-            /* The selected item is a label, not a file */
-            gtk_tree_path_up (path);
-            file = nautilus_list_model_file_for_path (list_model, path);
-        }
-
-        if (file != NULL)
-        {
-            if (nautilus_file_is_directory (file) &&
-                gtk_tree_view_row_expanded (tree_view, path))
-            {
-                uri = nautilus_file_get_uri (file);
-            }
-            nautilus_file_unref (file);
-        }
-
-        gtk_tree_path_free (path);
-        g_list_free (paths);
-    }
-
-    if (uri == NULL && length > 0)
-    {
-        gboolean is_common, is_root;
-
-        /* Check that all the selected items belong to the same
-         * directory and that directory is not the root directory (which
-         * is handled by NautilusFilesView::get_backing_directory.) */
-
-        tree_selection_has_common_parent (selection, &is_common, &is_root);
-
-        if (is_common && !is_root)
-        {
-            paths = gtk_tree_selection_get_selected_rows (selection, NULL);
-            path = (GtkTreePath *) paths->data;
-
-            file = nautilus_list_model_file_for_path (list_model, path);
-            g_assert (file != NULL);
-            uri = nautilus_file_get_parent_uri (file);
-            nautilus_file_unref (file);
-
-            g_list_free_full (paths, (GDestroyNotify) gtk_tree_path_free);
-        }
-    }
-
-    if (uri != NULL)
-    {
-        return uri;
-    }
-
-    return NAUTILUS_FILES_VIEW_CLASS (nautilus_list_view_parent_class)->get_backing_uri (view);
-}
-
-static void
-nautilus_list_view_get_selection_foreach_func (GtkTreeModel *model,
-                                               GtkTreePath  *path,
-                                               GtkTreeIter  *iter,
-                                               gpointer      data)
-{
-    GList **list;
-    NautilusFile *file;
-
-    list = data;
-
-    gtk_tree_model_get (model, iter,
-                        NAUTILUS_LIST_MODEL_FILE_COLUMN, &file,
-                        -1);
-
-    if (file != NULL)
-    {
-        (*list) = g_list_prepend ((*list), file);
-    }
-}
-
-static GList *
-nautilus_list_view_get_selection (NautilusFilesView *view)
-{
-    GList *list;
-
-    list = NULL;
-
-    gtk_tree_selection_selected_foreach (gtk_tree_view_get_selection (NAUTILUS_LIST_VIEW 
(view)->details->tree_view),
-                                         nautilus_list_view_get_selection_foreach_func, &list);
-
-    return g_list_reverse (list);
-}
-
-static void
-nautilus_list_view_get_selection_for_file_transfer_foreach_func (GtkTreeModel *model,
-                                                                 GtkTreePath  *path,
-                                                                 GtkTreeIter  *iter,
-                                                                 gpointer      data)
-{
-    NautilusFile *file;
-    struct SelectionForeachData *selection_data;
-    GtkTreeIter parent, child;
-
-    selection_data = data;
-
-    gtk_tree_model_get (model, iter,
-                        NAUTILUS_LIST_MODEL_FILE_COLUMN, &file,
-                        -1);
-
-    if (file != NULL)
-    {
-        /* If the parent folder is also selected, don't include this file in the
-         * file operation, since that would copy it to the toplevel target instead
-         * of keeping it as a child of the copied folder
-         */
-        child = *iter;
-        while (gtk_tree_model_iter_parent (model, &parent, &child))
-        {
-            if (gtk_tree_selection_iter_is_selected (selection_data->selection,
-                                                     &parent))
-            {
-                return;
-            }
-            child = parent;
-        }
-
-        nautilus_file_ref (file);
-        selection_data->list = g_list_prepend (selection_data->list, file);
-    }
-}
-
-
-static GList *
-nautilus_list_view_get_selection_for_file_transfer (NautilusFilesView *view)
-{
-    struct SelectionForeachData selection_data;
-
-    selection_data.list = NULL;
-    selection_data.selection = gtk_tree_view_get_selection (NAUTILUS_LIST_VIEW (view)->details->tree_view);
-
-    gtk_tree_selection_selected_foreach (selection_data.selection,
-                                         nautilus_list_view_get_selection_for_file_transfer_foreach_func, 
&selection_data);
-
-    return g_list_reverse (selection_data.list);
-}
-
-static gboolean
-nautilus_list_view_is_empty (NautilusFilesView *view)
-{
-    return nautilus_list_model_is_empty (NAUTILUS_LIST_VIEW (view)->details->model);
-}
-
-static void
-nautilus_list_view_end_file_changes (NautilusFilesView *view)
-{
-    NautilusListView *list_view;
-
-    list_view = NAUTILUS_LIST_VIEW (view);
-
-    if (list_view->details->new_selection_path)
-    {
-        gtk_tree_view_set_cursor (list_view->details->tree_view,
-                                  list_view->details->new_selection_path,
-                                  NULL, FALSE);
-        gtk_tree_path_free (list_view->details->new_selection_path);
-        list_view->details->new_selection_path = NULL;
-    }
-}
-
-static void
-nautilus_list_view_remove_file (NautilusFilesView *view,
-                                NautilusFile      *file,
-                                NautilusDirectory *directory)
-{
-    GtkTreePath *path;
-    GtkTreePath *file_path;
-    GtkTreeIter iter;
-    GtkTreeIter temp_iter;
-    GtkTreeRowReference *row_reference;
-    NautilusListView *list_view;
-    GtkTreeModel *tree_model;
-    GtkTreeSelection *selection;
-
-    path = NULL;
-    row_reference = NULL;
-    list_view = NAUTILUS_LIST_VIEW (view);
-    tree_model = GTK_TREE_MODEL (list_view->details->model);
-
-    if (nautilus_list_model_get_tree_iter_from_file (list_view->details->model, file, directory, &iter))
-    {
-        selection = gtk_tree_view_get_selection (list_view->details->tree_view);
-        file_path = gtk_tree_model_get_path (tree_model, &iter);
-
-        if (gtk_tree_selection_path_is_selected (selection, file_path))
-        {
-            /* get reference for next element in the list view. If the element to be deleted is the
-             * last one, get reference to previous element. If there is only one element in view
-             * no need to select anything.
-             */
-            temp_iter = iter;
-
-            if (gtk_tree_model_iter_next (tree_model, &iter))
-            {
-                path = gtk_tree_model_get_path (tree_model, &iter);
-                row_reference = gtk_tree_row_reference_new (tree_model, path);
-            }
-            else
-            {
-                path = gtk_tree_model_get_path (tree_model, &temp_iter);
-                if (gtk_tree_path_prev (path))
-                {
-                    row_reference = gtk_tree_row_reference_new (tree_model, path);
-                }
-            }
-            gtk_tree_path_free (path);
-        }
-
-        gtk_tree_path_free (file_path);
-
-        nautilus_list_model_remove_file (list_view->details->model, file, directory);
-
-        if (gtk_tree_row_reference_valid (row_reference))
-        {
-            if (list_view->details->new_selection_path)
-            {
-                gtk_tree_path_free (list_view->details->new_selection_path);
-            }
-            list_view->details->new_selection_path = gtk_tree_row_reference_get_path (row_reference);
-        }
-
-        if (row_reference)
-        {
-            gtk_tree_row_reference_free (row_reference);
-        }
-    }
-}
-
-static void
-nautilus_list_view_set_selection (NautilusFilesView *view,
-                                  GList             *selection)
-{
-    NautilusListView *list_view;
-    NautilusListModel *model;
-    GtkTreeView *tree_view;
-    GtkTreeSelection *tree_selection;
-    GList *node;
-    gboolean cursor_is_set_on_selection = FALSE;
-    GList *iters, *l;
-    NautilusFile *file;
-
-    list_view = NAUTILUS_LIST_VIEW (view);
-    model = list_view->details->model;
-    tree_view = list_view->details->tree_view;
-    tree_selection = gtk_tree_view_get_selection (tree_view);
-
-    g_signal_handlers_block_by_func (tree_selection, list_selection_changed_callback, view);
-
-    gtk_tree_selection_unselect_all (tree_selection);
-    for (node = selection; node != NULL; node = node->next)
-    {
-        file = node->data;
-        iters = nautilus_list_model_get_all_iters_for_file (model, file);
-
-        for (l = iters; l != NULL; l = l->next)
-        {
-            if (!cursor_is_set_on_selection)
-            {
-                GtkTreePath *path;
-
-                path = gtk_tree_model_get_path (GTK_TREE_MODEL (model),
-                                                (GtkTreeIter *) l->data);
-                gtk_tree_view_set_cursor (tree_view, path, NULL, FALSE);
-                gtk_tree_path_free (path);
-
-                cursor_is_set_on_selection = TRUE;
-                continue;
-            }
-
-            gtk_tree_selection_select_iter (tree_selection,
-                                            (GtkTreeIter *) l->data);
-        }
-        g_list_free_full (iters, g_free);
-    }
-
-    g_signal_handlers_unblock_by_func (tree_selection, list_selection_changed_callback, view);
-    nautilus_files_view_notify_selection_changed (view);
-}
-
-static void
-nautilus_list_view_invert_selection (NautilusFilesView *view)
-{
-    NautilusListView *list_view;
-    GtkTreeSelection *tree_selection;
-    GList *node;
-    GList *iters, *l;
-    NautilusFile *file;
-    GList *selection = NULL;
-
-    list_view = NAUTILUS_LIST_VIEW (view);
-    tree_selection = gtk_tree_view_get_selection (list_view->details->tree_view);
-
-    g_signal_handlers_block_by_func (tree_selection, list_selection_changed_callback, view);
-
-    gtk_tree_selection_selected_foreach (tree_selection,
-                                         nautilus_list_view_get_selection_foreach_func, &selection);
-
-    gtk_tree_selection_select_all (tree_selection);
-
-    for (node = selection; node != NULL; node = node->next)
-    {
-        file = node->data;
-        iters = nautilus_list_model_get_all_iters_for_file (list_view->details->model, file);
-
-        for (l = iters; l != NULL; l = l->next)
-        {
-            gtk_tree_selection_unselect_iter (tree_selection,
-                                              (GtkTreeIter *) l->data);
-        }
-        g_list_free_full (iters, g_free);
-    }
-
-    g_list_free (selection);
-
-    g_signal_handlers_unblock_by_func (tree_selection, list_selection_changed_callback, view);
-    nautilus_files_view_notify_selection_changed (view);
-}
-
-static void
-nautilus_list_view_select_all (NautilusFilesView *view)
-{
-    gtk_tree_selection_select_all (gtk_tree_view_get_selection (NAUTILUS_LIST_VIEW 
(view)->details->tree_view));
-}
-
-static void
-nautilus_list_view_select_first (NautilusFilesView *view)
-{
-    GtkTreeSelection *selection;
-    GtkTreeIter iter;
-
-    if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (NAUTILUS_LIST_VIEW (view)->details->model), &iter))
-    {
-        return;
-    }
-    selection = gtk_tree_view_get_selection (NAUTILUS_LIST_VIEW (view)->details->tree_view);
-    gtk_tree_selection_unselect_all (selection);
-    gtk_tree_selection_select_iter (selection, &iter);
-}
-
-static void
-nautilus_list_view_zoom_to_level (NautilusFilesView *view,
-                                  gint               zoom_level)
-{
-    NautilusListView *list_view;
-
-    g_return_if_fail (NAUTILUS_IS_LIST_VIEW (view));
-
-    list_view = NAUTILUS_LIST_VIEW (view);
-
-    if (list_view->details->zoom_level == zoom_level)
-    {
-        return;
-    }
-
-    nautilus_list_view_set_zoom_level (list_view, zoom_level);
-    g_action_group_change_action_state (nautilus_files_view_get_action_group (view),
-                                        "zoom-to-level", g_variant_new_int32 (zoom_level));
-
-    nautilus_files_view_update_toolbar_menus (view);
-}
-
-static void
-action_zoom_to_level (GSimpleAction *action,
-                      GVariant      *state,
-                      gpointer       user_data)
-{
-    NautilusFilesView *view;
-    NautilusListZoomLevel zoom_level;
+    gtk_widget_set_hexpand (widget, TRUE);
 
-    g_assert (NAUTILUS_IS_FILES_VIEW (user_data));
 
-    view = NAUTILUS_FILES_VIEW (user_data);
-    zoom_level = g_variant_get_int32 (state);
-    nautilus_list_view_zoom_to_level (view, zoom_level);
+    /* We don't use the built-in child activation feature for click because it
+     * doesn't fill all our needs nor does it match our expected behavior.
+     * Instead, we roll our own event handling and double/single click mode.
+     * However, GtkColumnView:single-click-activate has other effects besides
+     * activation, as it affects the selection behavior as well (e.g. selects on
+     * hover). Setting it to FALSE gives us the expected behavior. */
+    gtk_column_view_set_single_click_activate (GTK_COLUMN_VIEW (widget), FALSE);
+    gtk_column_view_set_enable_rubberband (GTK_COLUMN_VIEW (widget), TRUE);
 
-    g_simple_action_set_state (G_SIMPLE_ACTION (action), state);
-    if (g_settings_get_enum (nautilus_list_view_preferences,
-                             NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_ZOOM_LEVEL) != zoom_level)
-    {
-        g_settings_set_enum (nautilus_list_view_preferences,
-                             NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_ZOOM_LEVEL,
-                             zoom_level);
-    }
+    /* While we don't want to use GTK's click activation, we'll let it handle
+     * the key activation part (with Enter).
+     */
+    g_signal_connect (widget, "activate", G_CALLBACK (on_column_view_item_activated), self);
+
+    return GTK_COLUMN_VIEW (widget);
 }
 
 static void
@@ -3228,708 +567,567 @@ action_visible_columns (GSimpleAction *action,
                         GVariant      *state,
                         gpointer       user_data)
 {
-    NautilusListView *list_view;
-
-    list_view = NAUTILUS_LIST_VIEW (user_data);
+    NautilusListView *self = NAUTILUS_LIST_VIEW (user_data);
 
-    if (list_view->details->column_editor)
+    if (self->column_editor)
     {
-        gtk_window_present (GTK_WINDOW (list_view->details->column_editor));
+        gtk_widget_show (self->column_editor);
     }
     else
     {
-        list_view->details->column_editor = create_column_editor (list_view);
-        g_object_add_weak_pointer (G_OBJECT (list_view->details->column_editor),
-                                   (gpointer *) &list_view->details->column_editor);
+        self->column_editor = create_column_editor (self);
+        g_object_add_weak_pointer (G_OBJECT (self->column_editor),
+                                   (gpointer *) &self->column_editor);
 
-        gtk_widget_show (list_view->details->column_editor);
+        gtk_widget_show (self->column_editor);
     }
 }
 
-const GActionEntry list_view_entries[] =
-{
-    { "visible-columns", action_visible_columns },
-    { "zoom-to-level", NULL, NULL, "1", action_zoom_to_level }
-};
-
 static void
-nautilus_list_view_set_zoom_level (NautilusListView      *view,
-                                   NautilusListZoomLevel  new_level)
+action_sort_order_changed (GSimpleAction *action,
+                           GVariant      *value,
+                           gpointer       user_data)
 {
-    int column;
+    const gchar *target_name;
+    gboolean reversed;
+    NautilusFileSortType sort_type;
+    NautilusListView *self;
+    GListModel *view_columns;
+    g_autoptr (GtkColumnViewColumn) sort_column = NULL;
+    GtkSorter *sorter;
 
-    g_return_if_fail (NAUTILUS_IS_LIST_VIEW (view));
-    g_return_if_fail (new_level >= NAUTILUS_LIST_ZOOM_LEVEL_SMALL &&
-                      new_level <= NAUTILUS_LIST_ZOOM_LEVEL_LARGER);
+    /* This array makes the #NautilusFileSortType values correspond to the
+     * respective column attribute.
+     */
+    const char *attributes[] =
+    {
+        "name",
+        "size",
+        "type",
+        "date_modified",
+        "date_accessed",
+        "date_created",
+        "starred",
+        "trashed_on",
+        "search_relevance",
+        "recency",
+        NULL
+    };
 
-    if (view->details->zoom_level == new_level)
+    /* Don't resort if the action is in the same state as before */
+    if (g_variant_equal (value, g_action_get_state (G_ACTION (action))))
     {
         return;
     }
 
-    view->details->zoom_level = new_level;
-
-    /* Select correctly scaled icons. */
-    column = nautilus_list_model_get_column_id_from_zoom_level (new_level);
-    gtk_tree_view_column_set_attributes (view->details->file_name_column,
-                                         GTK_CELL_RENDERER (view->details->pixbuf_cell),
-                                         "texture", column,
-                                         NULL);
-    set_up_pixbuf_size (view);
-}
-
-static void
-nautilus_list_view_bump_zoom_level (NautilusFilesView *view,
-                                    int                zoom_increment)
-{
-    NautilusListView *list_view;
-    gint new_level;
+    self = NAUTILUS_LIST_VIEW (user_data);
+    g_variant_get (value, "(&sb)", &target_name, &reversed);
 
-    g_return_if_fail (NAUTILUS_IS_LIST_VIEW (view));
+    if (g_strcmp0 (target_name, "unknown") == 0)
+    {
+        /* Sort order has been changed without using this action. */
+        g_simple_action_set_state (action, value);
+        return;
+    }
 
-    list_view = NAUTILUS_LIST_VIEW (view);
-    new_level = list_view->details->zoom_level + zoom_increment;
+    sort_type = get_sorts_type_from_metadata_text (target_name);
 
-    if (new_level >= NAUTILUS_LIST_ZOOM_LEVEL_SMALL &&
-        new_level <= NAUTILUS_LIST_ZOOM_LEVEL_LARGER)
+    view_columns = gtk_column_view_get_columns (self->view_ui);
+    for (guint i = 0; i < g_list_model_get_n_items (view_columns); i++)
     {
-        nautilus_list_view_zoom_to_level (view, new_level);
-    }
-}
+        g_autoptr (GtkColumnViewColumn) view_column = NULL;
+        GtkListItemFactory *factory;
+        NautilusColumn *nautilus_column;
+        gchar *attribute;
 
-static void
-nautilus_list_view_restore_standard_zoom_level (NautilusFilesView *view)
-{
-    nautilus_list_view_zoom_to_level (view, NAUTILUS_LIST_ZOOM_LEVEL_STANDARD);
-}
+        view_column = g_list_model_get_item (view_columns, i);
+        factory = gtk_column_view_column_get_factory (view_column);
+        nautilus_column = g_hash_table_lookup (self->factory_to_column_map, factory);
+        if (nautilus_column == NULL)
+        {
+            continue;
+        }
+        g_object_get (nautilus_column, "attribute", &attribute, NULL);
+        if (g_strcmp0 (attributes[sort_type], attribute) == 0)
+        {
+            sort_column = g_steal_pointer (&view_column);
+            break;
+        }
+    }
 
-static gboolean
-nautilus_list_view_can_zoom_in (NautilusFilesView *view)
-{
-    g_return_val_if_fail (NAUTILUS_IS_LIST_VIEW (view), FALSE);
+    sorter = gtk_column_view_get_sorter (self->view_ui);
 
-    return NAUTILUS_LIST_VIEW (view)->details->zoom_level < NAUTILUS_LIST_ZOOM_LEVEL_LARGER;
-}
+    g_signal_handlers_block_by_func (sorter, on_sorter_changed, self);
+    /* FIXME: Set NULL to stop drawing the arrow on previous sort column
+     * to workaround https://gitlab.gnome.org/GNOME/gtk/-/issues/4696 */
+    gtk_column_view_sort_by_column (self->view_ui, NULL, FALSE);
+    gtk_column_view_sort_by_column (self->view_ui, sort_column, reversed);
+    g_signal_handlers_unblock_by_func (sorter, on_sorter_changed, self);
 
-static gboolean
-nautilus_list_view_can_zoom_out (NautilusFilesView *view)
-{
-    g_return_val_if_fail (NAUTILUS_IS_LIST_VIEW (view), FALSE);
+    set_directory_sort_metadata (nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (self)),
+                                 target_name,
+                                 reversed);
 
-    return NAUTILUS_LIST_VIEW (view)->details->zoom_level > NAUTILUS_LIST_ZOOM_LEVEL_SMALL;
+    g_simple_action_set_state (action, value);
 }
 
-static gboolean
-nautilus_list_view_is_zoom_level_default (NautilusFilesView *view)
+static void
+set_zoom_level (NautilusListView *self,
+                guint             new_level)
 {
-    NautilusListView *list_view;
-    guint icon_size;
+    self->zoom_level = new_level;
 
-    list_view = NAUTILUS_LIST_VIEW (view);
-    icon_size = nautilus_list_model_get_icon_size_for_zoom_level (list_view->details->zoom_level);
+    nautilus_list_base_set_icon_size (NAUTILUS_LIST_BASE (self),
+                                      get_icon_size_for_zoom_level (new_level));
 
-    return icon_size == NAUTILUS_LIST_ICON_SIZE_STANDARD;
+    nautilus_files_view_update_toolbar_menus (NAUTILUS_FILES_VIEW (self));
 }
 
 static void
-nautilus_list_view_click_policy_changed (NautilusFilesView *directory_view)
+action_zoom_to_level (GSimpleAction *action,
+                      GVariant      *state,
+                      gpointer       user_data)
 {
-    GdkDisplay *display;
-    NautilusListView *view;
-    GtkTreeIter iter;
-    GtkTreeView *tree;
+    NautilusListView *self = NAUTILUS_LIST_VIEW (user_data);
+    int zoom_level;
 
-    view = NAUTILUS_LIST_VIEW (directory_view);
-    display = gtk_widget_get_display (GTK_WIDGET (view));
+    zoom_level = g_variant_get_int32 (state);
+    set_zoom_level (self, zoom_level);
+    g_simple_action_set_state (G_SIMPLE_ACTION (action), state);
 
-    /* ensure that we unset the hand cursor and refresh underlined rows */
-    if (get_click_policy () == NAUTILUS_CLICK_POLICY_DOUBLE)
+    if (g_settings_get_enum (nautilus_list_view_preferences,
+                             NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_ZOOM_LEVEL) != zoom_level)
     {
-        if (view->details->hover_path != NULL)
-        {
-            if (gtk_tree_model_get_iter (GTK_TREE_MODEL (view->details->model),
-                                         &iter, view->details->hover_path))
-            {
-                gtk_tree_model_row_changed (GTK_TREE_MODEL (view->details->model),
-                                            view->details->hover_path, &iter);
-            }
+        g_settings_set_enum (nautilus_list_view_preferences,
+                             NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_ZOOM_LEVEL,
+                             zoom_level);
+    }
+}
 
-            gtk_tree_path_free (view->details->hover_path);
-            view->details->hover_path = NULL;
-        }
+const GActionEntry list_view_entries[] =
+{
+    { "visible-columns", action_visible_columns },
+    { "sort", NULL, "(sb)", "('invalid',false)", action_sort_order_changed },
+    { "zoom-to-level", NULL, NULL, "1", action_zoom_to_level }
+};
 
-        tree = view->details->tree_view;
-        if (gtk_widget_get_realized (GTK_WIDGET (tree)))
-        {
-            gtk_widget_set_cursor (GTK_WIDGET (tree), NULL);
+static void
+real_begin_loading (NautilusFilesView *files_view)
+{
+    NautilusListView *self = NAUTILUS_LIST_VIEW (files_view);
+    NautilusFile *file;
 
-            if (display != NULL)
-            {
-                gdk_display_flush (display);
-            }
-        }
+    NAUTILUS_FILES_VIEW_CLASS (nautilus_list_view_parent_class)->begin_loading (files_view);
 
-        g_clear_object (&hand_cursor);
+    update_columns_settings_from_metadata_and_preferences (self);
+
+    self->path_attribute_q = 0;
+    g_clear_object (&self->file_path_base_location);
+    file = nautilus_files_view_get_directory_as_file (files_view);
+    if (nautilus_file_is_in_trash (file))
+    {
+        self->path_attribute_q = g_quark_from_string ("trash_orig_path");
+        self->file_path_base_location = get_base_location (self);
     }
-    else if (get_click_policy () == NAUTILUS_CLICK_POLICY_SINGLE)
+    else if (nautilus_file_is_in_search (file) ||
+             nautilus_file_is_in_recent (file) ||
+             nautilus_file_is_in_starred (file))
     {
-        if (hand_cursor == NULL)
-        {
-            hand_cursor = gdk_cursor_new_from_name ("pointer", NULL);
-        }
+        self->path_attribute_q = g_quark_from_string ("where");
+        self->file_path_base_location = get_base_location (self);
     }
 }
 
 static void
-default_sort_order_changed_callback (gpointer callback_data)
+real_bump_zoom_level (NautilusFilesView *files_view,
+                      int                zoom_increment)
 {
-    NautilusListView *list_view;
+    NautilusListView *self = NAUTILUS_LIST_VIEW (files_view);
+    NautilusListZoomLevel new_level;
 
-    list_view = NAUTILUS_LIST_VIEW (callback_data);
+    new_level = self->zoom_level + zoom_increment;
 
-    set_sort_order_from_metadata_and_preferences (list_view);
+    if (new_level >= NAUTILUS_LIST_ZOOM_LEVEL_SMALL &&
+        new_level <= NAUTILUS_LIST_ZOOM_LEVEL_LARGER)
+    {
+        g_action_group_change_action_state (self->action_group,
+                                            "zoom-to-level",
+                                            g_variant_new_int32 (new_level));
+    }
 }
 
-static void
-default_visible_columns_changed_callback (gpointer callback_data)
+static gint
+get_default_zoom_level (void)
 {
-    NautilusListView *list_view;
+    NautilusListZoomLevel default_zoom_level;
 
-    list_view = NAUTILUS_LIST_VIEW (callback_data);
+    default_zoom_level = g_settings_get_enum (nautilus_list_view_preferences,
+                                              NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_ZOOM_LEVEL);
 
-    set_columns_settings_from_metadata_and_preferences (list_view);
+    return default_zoom_level;
 }
 
 static void
-default_column_order_changed_callback (gpointer callback_data)
+real_restore_standard_zoom_level (NautilusFilesView *files_view)
 {
-    NautilusListView *list_view;
+    NautilusListView *self;
 
-    list_view = NAUTILUS_LIST_VIEW (callback_data);
-
-    set_columns_settings_from_metadata_and_preferences (list_view);
+    self = NAUTILUS_LIST_VIEW (files_view);
+    g_action_group_change_action_state (self->action_group,
+                                        "zoom-to-level",
+                                        g_variant_new_int32 (NAUTILUS_LIST_ZOOM_LEVEL_STANDARD));
 }
 
-static void
-nautilus_list_view_sort_directories_first_changed (NautilusFilesView *view)
+static gboolean
+real_can_zoom_in (NautilusFilesView *files_view)
 {
-    NautilusListView *list_view;
-
-    list_view = NAUTILUS_LIST_VIEW (view);
+    NautilusListView *self = NAUTILUS_LIST_VIEW (files_view);
 
-    nautilus_list_model_set_should_sort_directories_first (list_view->details->model,
-                                                           nautilus_files_view_should_sort_directories_first 
(view));
+    return self->zoom_level < NAUTILUS_LIST_ZOOM_LEVEL_LARGER;
 }
 
-static int
-nautilus_list_view_compare_files (NautilusFilesView *view,
-                                  NautilusFile      *file1,
-                                  NautilusFile      *file2)
+static gboolean
+real_can_zoom_out (NautilusFilesView *files_view)
 {
-    NautilusListView *list_view;
+    NautilusListView *self = NAUTILUS_LIST_VIEW (files_view);
 
-    list_view = NAUTILUS_LIST_VIEW (view);
-    return nautilus_list_model_compare_func (list_view->details->model, file1, file2);
+    return self->zoom_level > NAUTILUS_LIST_ZOOM_LEVEL_SMALL;
 }
 
-static void
-nautilus_list_view_dispose (GObject *object)
+static gboolean
+real_is_zoom_level_default (NautilusFilesView *files_view)
 {
-    NautilusListView *list_view;
-
-    list_view = NAUTILUS_LIST_VIEW (object);
-
-    if (list_view->details->model)
-    {
-        g_object_unref (list_view->details->model);
-        list_view->details->model = NULL;
-    }
-
-#if 0 && NAUTILUS_DND_NEEDS_GTK4_REIMPLEMENTATION
-    if (list_view->details->drag_dest)
-    {
-        g_object_unref (list_view->details->drag_dest);
-        list_view->details->drag_dest = NULL;
-    }
-#endif
-
-    g_signal_handlers_disconnect_by_func (nautilus_preferences,
-                                          default_sort_order_changed_callback,
-                                          list_view);
-    g_signal_handlers_disconnect_by_func (nautilus_list_view_preferences,
-                                          default_visible_columns_changed_callback,
-                                          list_view);
-    g_signal_handlers_disconnect_by_func (nautilus_list_view_preferences,
-                                          default_column_order_changed_callback,
-                                          list_view);
+    NautilusListView *self;
+    guint icon_size;
 
-    g_clear_pointer (&list_view->details->columns_popover, gtk_widget_unparent);
+    self = NAUTILUS_LIST_VIEW (files_view);
+    icon_size = get_icon_size_for_zoom_level (self->zoom_level);
 
-    G_OBJECT_CLASS (nautilus_list_view_parent_class)->dispose (object);
+    return icon_size == NAUTILUS_LIST_ICON_SIZE_STANDARD;
 }
 
 static void
-nautilus_list_view_finalize (GObject *object)
+real_sort_directories_first_changed (NautilusFilesView *files_view)
 {
-    NautilusListView *list_view;
-
-    list_view = NAUTILUS_LIST_VIEW (object);
-
-    g_free (list_view->details->original_name);
-    list_view->details->original_name = NULL;
-
-    if (list_view->details->first_click_path)
-    {
-        gtk_tree_path_free (list_view->details->first_click_path);
-    }
-    if (list_view->details->new_selection_path)
-    {
-        gtk_tree_path_free (list_view->details->new_selection_path);
-    }
-
-    g_list_free (list_view->details->cells);
-    g_hash_table_destroy (list_view->details->columns);
-
-    if (list_view->details->hover_path != NULL)
-    {
-        gtk_tree_path_free (list_view->details->hover_path);
-    }
-
-    if (list_view->details->column_editor != NULL)
-    {
-        gtk_window_destroy (GTK_WINDOW (list_view->details->column_editor));
-    }
-
-    g_regex_unref (list_view->details->regex);
-
-    g_cancellable_cancel (list_view->details->starred_cancellable);
-    g_clear_object (&list_view->details->starred_cancellable);
+    NautilusListView *self = NAUTILUS_LIST_VIEW (files_view);
 
-    g_signal_handlers_disconnect_by_func (nautilus_tag_manager_get (),
-                                          on_starred_files_changed,
-                                          list_view);
+    self->directories_first = nautilus_files_view_should_sort_directories_first (NAUTILUS_FILES_VIEW (self));
 
-    g_free (list_view->details);
-
-    G_OBJECT_CLASS (nautilus_list_view_parent_class)->finalize (object);
+    /* Reset the sorter to trigger ressorting */
+    nautilus_view_model_set_sorter (nautilus_list_base_get_model (NAUTILUS_LIST_BASE (self)),
+                                    gtk_column_view_get_sorter (self->view_ui));
 }
 
-static char *
-nautilus_list_view_get_first_visible_file (NautilusFilesView *view)
+static void
+on_sorter_changed (GtkSorter       *sorter,
+                   GtkSorterChange  change,
+                   gpointer         user_data)
 {
-    NautilusFile *file;
-    GtkTreePath *path;
-    GtkTreeIter iter;
-    NautilusListView *list_view;
-
-    list_view = NAUTILUS_LIST_VIEW (view);
-
-    if (gtk_tree_view_get_path_at_pos (list_view->details->tree_view,
-                                       0, 0,
-                                       &path, NULL, NULL, NULL))
-    {
-        gtk_tree_model_get_iter (GTK_TREE_MODEL (list_view->details->model),
-                                 &iter, path);
-
-        gtk_tree_path_free (path);
-
-        gtk_tree_model_get (GTK_TREE_MODEL (list_view->details->model),
-                            &iter,
-                            NAUTILUS_LIST_MODEL_FILE_COLUMN, &file,
-                            -1);
-        if (file)
-        {
-            char *uri;
-
-            uri = nautilus_file_get_uri (file);
-
-            nautilus_file_unref (file);
+    NautilusListView *self = NAUTILUS_LIST_VIEW (user_data);
 
-            return uri;
-        }
-    }
+    /* When user clicks a header to change sort order, we don't know what the
+     * new sort order is. Make sure the sort menu doesn't indicate a outdated
+     * action state. */
+    g_action_group_change_action_state (nautilus_files_view_get_action_group (NAUTILUS_FILES_VIEW (self)),
+                                        "sort", g_variant_new ("(sb)", "unknown", FALSE));
+}
 
-    return NULL;
+static guint
+real_get_view_id (NautilusFilesView *files_view)
+{
+    return NAUTILUS_VIEW_LIST_ID;
 }
 
 static void
-nautilus_list_view_scroll_to_file (NautilusListView *view,
-                                   NautilusFile     *file)
+on_item_click_released_workaround (GtkGestureClick *gesture,
+                                   gint             n_press,
+                                   gdouble          x,
+                                   gdouble          y,
+                                   gpointer         user_data)
 {
-    GtkTreePath *path;
-    GtkTreeIter iter;
+    NautilusViewCell *cell = user_data;
+    NautilusListView *self = NAUTILUS_LIST_VIEW (nautilus_view_cell_get_view (cell));
+    GdkModifierType modifiers;
 
-    if (!nautilus_list_model_get_first_iter_for_file (view->details->model, file, &iter))
+    modifiers = gtk_event_controller_get_current_event_state (GTK_EVENT_CONTROLLER (gesture));
+    if (n_press == 1 &&
+        modifiers & (GDK_CONTROL_MASK | GDK_SHIFT_MASK))
     {
-        return;
-    }
+        NautilusViewModel *model;
+        NautilusViewItem *item;
+        guint i;
 
-    path = gtk_tree_model_get_path (GTK_TREE_MODEL (view->details->model), &iter);
+        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);
 
-    gtk_tree_view_scroll_to_cell (view->details->tree_view,
-                                  path, NULL,
-                                  TRUE, 0.0, 0.0);
-
-    gtk_tree_path_free (path);
+        gtk_widget_activate_action (GTK_WIDGET (cell),
+                                    "list.select-item",
+                                    "(ubb)",
+                                    i,
+                                    modifiers & GDK_CONTROL_MASK,
+                                    modifiers & GDK_SHIFT_MASK);
+    }
 }
 
+/* This whole event handler is a workaround to a GtkColumnView bug: it
+ * activates the list|select-item action twice, which may cause the
+ * second activation to reverse the effects of the first:
+ * https://gitlab.gnome.org/GNOME/gtk/-/issues/4819
+ *
+ * As a workaround, we are going to activate the action a 3rd time.
+ * The third time is the charm, as the saying goes. */
 static void
-list_view_scroll_to_file (NautilusFilesView *view,
-                          const char        *uri)
+setup_selection_click_workaround (NautilusViewCell *cell)
 {
-    NautilusFile *file;
+    GtkEventController *controller;
 
-    if (uri != NULL)
-    {
-        /* Only if existing, since we don't want to add the file to
-         *  the directory if it has been removed since then */
-        file = nautilus_file_get_existing_by_uri (uri);
-        if (file != NULL)
-        {
-            nautilus_list_view_scroll_to_file (NAUTILUS_LIST_VIEW (view), file);
-            nautilus_file_unref (file);
-        }
-    }
+    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), GDK_BUTTON_PRIMARY);
+    g_signal_connect (controller, "released", G_CALLBACK (on_item_click_released_workaround), cell);
 }
 
 static void
-on_clipboard_contents_received (GObject      *source_object,
-                                GAsyncResult *res,
-                                gpointer      user_data)
+setup_name_cell (GtkSignalListItemFactory *factory,
+                 GtkListItem              *listitem,
+                 gpointer                  user_data)
 {
-    NautilusFilesView *files_view = NAUTILUS_FILES_VIEW (source_object);
-    NautilusListViewDetails *details = NAUTILUS_LIST_VIEW (files_view)->details;
-    NautilusClipboard *clip;
+    NautilusListView *self = NAUTILUS_LIST_VIEW (user_data);
+    NautilusViewCell *cell;
 
-    clip = nautilus_files_view_get_clipboard_finish (files_view, res, NULL);
-    if (clip != NULL && nautilus_clipboard_is_cut (clip))
-    {
-        GList *files;
+    cell = nautilus_name_cell_new (NAUTILUS_LIST_BASE (self));
+    setup_cell_common (listitem, cell);
 
-        files = nautilus_clipboard_peek_files (clip);
-        nautilus_list_model_set_highlight_for_files (details->model, files);
-    }
-    else
+    nautilus_name_cell_set_path (NAUTILUS_NAME_CELL (cell),
+                                 self->path_attribute_q,
+                                 self->file_path_base_location);
+    if (NAUTILUS_IS_SEARCH_DIRECTORY (nautilus_files_view_get_model (NAUTILUS_FILES_VIEW (self))))
     {
-        nautilus_list_model_set_highlight_for_files (details->model, NULL);
+        nautilus_name_cell_show_snippet (NAUTILUS_NAME_CELL (cell));
     }
+
+    gtk_list_item_set_child (listitem, GTK_WIDGET (cell));
+
+    setup_selection_click_workaround (cell);
 }
 
 static void
-update_clipboard_status (NautilusFilesView *view)
+bind_name_cell (GtkSignalListItemFactory *factory,
+                GtkListItem              *listitem,
+                gpointer                  user_data)
 {
-    nautilus_files_view_get_clipboard_async (view,
-                                             on_clipboard_contents_received,
-                                             NULL);
+    NautilusViewItem *item;
+
+    item = NAUTILUS_VIEW_ITEM (gtk_list_item_get_item (listitem));
+
+    nautilus_view_item_set_item_ui (item, gtk_list_item_get_child (listitem));
 }
 
 static void
-on_clipboard_owner_changed (GdkClipboard *clipboard,
-                            gpointer      user_data)
+unbind_name_cell (GtkSignalListItemFactory *factory,
+                  GtkListItem              *listitem,
+                  gpointer                  user_data)
 {
-    update_clipboard_status (NAUTILUS_FILES_VIEW (user_data));
+    NautilusViewItem *item;
+
+    item = NAUTILUS_VIEW_ITEM (gtk_list_item_get_item (listitem));
+    g_return_if_fail (NAUTILUS_IS_VIEW_ITEM (item));
+
+    nautilus_view_item_set_item_ui (item, NULL);
 }
 
 static void
-nautilus_list_view_end_loading (NautilusFilesView *view,
-                                gboolean           all_files_seen)
+setup_star_cell (GtkSignalListItemFactory *factory,
+                 GtkListItem              *listitem,
+                 gpointer                  user_data)
 {
-    update_clipboard_status (view);
-}
+    NautilusViewCell *cell;
 
-static guint
-nautilus_list_view_get_id (NautilusFilesView *view)
-{
-    return NAUTILUS_VIEW_LIST_ID;
+    cell = nautilus_star_cell_new (NAUTILUS_LIST_BASE (user_data));
+    setup_cell_common (listitem, cell);
+    setup_selection_click_workaround (cell);
 }
 
-static GdkRectangle *
-get_rectangle_for_path (NautilusListView *list_view,
-                        GtkTreePath      *path)
+static void
+setup_label_cell (GtkSignalListItemFactory *factory,
+                  GtkListItem              *listitem,
+                  gpointer                  user_data)
 {
-    GtkTreeView *tree_view = list_view->details->tree_view;
-    GdkRectangle *rect = g_malloc0 (sizeof (GdkRectangle));
-    int header_height;
-
-    gtk_tree_view_get_cell_area (tree_view,
-                                 path,
-                                 list_view->details->file_name_column,
-                                 rect);
-    gtk_tree_view_convert_bin_window_to_widget_coords (tree_view,
-                                                       rect->x, rect->y,
-                                                       &rect->x, &rect->y);
-
-    /* FIXME Due to smooth scrolling, we may get the cell area while the view is
-     * still scrolling (and still outside the view), not at the final position
-     * of the cell after scrolling.
-     * https://bugzilla.gnome.org/show_bug.cgi?id=746773
-     * The following workaround guesses the final "y" coordinate by clamping it
-     * to the widget edge. Note that the top edge has got columns header, which
-     * is private, so first guess the header height from the difference between
-     * widget coordinates and bin cooridinates.
-     */
-    gtk_tree_view_convert_bin_window_to_widget_coords (tree_view,
-                                                       0, 0,
-                                                       NULL, &header_height);
+    NautilusListView *self = user_data;
+    NautilusColumn *nautilus_column;
+    NautilusViewCell *cell;
 
-    rect->y = CLAMP (rect->y,
-                     header_height,
-                     gtk_widget_get_allocated_height (GTK_WIDGET (list_view)) - rect->height);
-    /* End of workaround */
+    nautilus_column = g_hash_table_lookup (self->factory_to_column_map, factory);
 
-    return rect;
+    cell = nautilus_label_cell_new (NAUTILUS_LIST_BASE (user_data), nautilus_column);
+    setup_cell_common (listitem, cell);
+    setup_selection_click_workaround (cell);
 }
 
-static GdkRectangle *
-nautilus_list_view_compute_rename_popover_pointing_to (NautilusFilesView *view)
+static void
+setup_view_columns (NautilusListView *self)
 {
-    NautilusListView *list_view;
-    GtkTreeView *tree_view;
-    GtkTreeSelection *selection;
-    GList *list;
-    GtkTreePath *path;
-    GdkRectangle *rect;
-
-    list_view = NAUTILUS_LIST_VIEW (view);
-    tree_view = list_view->details->tree_view;
-    selection = gtk_tree_view_get_selection (tree_view);
-    list = gtk_tree_selection_get_selected_rows (selection, NULL);
-    path = list->data;
-    rect = get_rectangle_for_path (list_view, path);
-
-    if (list_view->details->last_event_button_x > 0)
-    {
-        /* Point to the position in the row where it was clicked. */
-        rect->x = list_view->details->last_event_button_x;
-        /* Make it zero width to point exactly at rect->x.*/
-        rect->width = 0;
-    }
+    GtkListItemFactory *factory;
+    g_autolist (NautilusColumn) nautilus_columns = NULL;
 
-    g_list_free_full (list, (GDestroyNotify) gtk_tree_path_free);
+    nautilus_columns = nautilus_get_all_columns ();
 
-    return rect;
-}
+    self->factory_to_column_map = g_hash_table_new_full (g_direct_hash,
+                                                         g_direct_equal,
+                                                         NULL,
+                                                         g_object_unref);
 
-static GdkRectangle *
-nautilus_list_view_reveal_for_selection_context_menu (NautilusFilesView *view)
-{
-    NautilusListView *list_view;
-    GtkTreeView *tree_view;
-    GtkTreeSelection *tree_selection;
-    GtkTreePath *path;
-    GdkRectangle *rect;
+    for (GList *l = nautilus_columns; l != NULL; l = l->next)
+    {
+        NautilusColumn *nautilus_column = NAUTILUS_COLUMN (l->data);
+        g_autofree gchar *name = NULL;
+        g_autofree gchar *label = NULL;
+        GQuark attribute_q = 0;
+        GtkSortType sort_order;
+        NautilusListViewSortData *sort_data;
+        g_autoptr (GtkCustomSorter) sorter = NULL;
+        g_autoptr (GtkColumnViewColumn) view_column = NULL;
 
-    g_return_val_if_fail (NAUTILUS_IS_LIST_VIEW (view), NULL);
+        g_object_get (nautilus_column,
+                      "name", &name,
+                      "label", &label,
+                      "attribute_q", &attribute_q,
+                      "default-sort-order", &sort_order,
+                      NULL);
 
-    list_view = NAUTILUS_LIST_VIEW (view);
-    tree_view = list_view->details->tree_view;
-    tree_selection = gtk_tree_view_get_selection (tree_view);
-    g_return_val_if_fail (tree_selection != NULL, NULL);
+        sort_data = g_new0 (NautilusListViewSortData, 1);
+        sort_data->self = self;
+        sort_data->attribute_q = attribute_q;
+        sorter = gtk_custom_sorter_new (nautilus_list_view_sort, sort_data, g_free);
 
-    /* Get the path to the last focused item, if selected. Otherwise, get
-     * the path to the selected item which is sorted the lowest.
-     */
-    gtk_tree_view_get_cursor (tree_view, &path, NULL);
-    if (path == NULL || !gtk_tree_selection_path_is_selected (tree_selection, path))
-    {
-        GList *list;
+        factory = gtk_signal_list_item_factory_new ();
+        view_column = gtk_column_view_column_new (NULL, factory);
+        gtk_column_view_column_set_expand (view_column, FALSE);
+        gtk_column_view_column_set_resizable (view_column, TRUE);
+        gtk_column_view_column_set_title (view_column, label);
+        gtk_column_view_column_set_sorter (view_column, GTK_SORTER (sorter));
 
-        list = gtk_tree_selection_get_selected_rows (tree_selection, NULL);
-        list = g_list_last (list);
-        path = g_steal_pointer (&list->data);
+        if (!strcmp (name, "name"))
+        {
+            g_signal_connect (factory, "setup", G_CALLBACK (setup_name_cell), self);
+            g_signal_connect (factory, "bind", G_CALLBACK (bind_name_cell), self);
+            g_signal_connect (factory, "unbind", G_CALLBACK (unbind_name_cell), self);
 
-        g_list_free_full (list, (GDestroyNotify) gtk_tree_path_free);
-    }
+            gtk_column_view_column_set_expand (view_column, TRUE);
+        }
+        else if (g_strcmp0 (name, "starred") == 0)
+        {
+            g_signal_connect (factory, "setup", G_CALLBACK (setup_star_cell), self);
 
-    gtk_tree_view_scroll_to_cell (tree_view, path, NULL, FALSE, 0.0, 0.0);
+            gtk_column_view_column_set_title (view_column, "");
+            gtk_column_view_column_set_resizable (view_column, FALSE);
 
-    rect = get_rectangle_for_path (list_view, path);
-    rect->width = rect->height;
+            self->star_column = view_column;
+        }
+        else
+        {
+            g_signal_connect (factory, "setup", G_CALLBACK (setup_label_cell), self);
+        }
 
-    gtk_tree_path_free (path);
+        gtk_column_view_append_column (self->view_ui, view_column);
 
-    return rect;
+        g_hash_table_insert (self->factory_to_column_map,
+                             factory,
+                             g_object_ref (nautilus_column));
+    }
 }
 
 static void
-nautilus_list_view_preview_selection_event (NautilusFilesView *view,
-                                            GtkDirectionType   direction)
+nautilus_list_view_init (NautilusListView *self)
 {
-    NautilusListView *list_view;
-    GtkTreeView *tree_view;
-    GtkTreeSelection *selection;
-    GList *list;
-    GtkTreeIter iter;
-    GtkTreePath *path;
-    GtkTreeModel *tree_model;
-    gboolean moved;
-
-    /* We only support up and down movements for the list view */
-    if (direction != GTK_DIR_UP && direction != GTK_DIR_DOWN)
-    {
-        return;
-    }
+    NautilusViewModel *model;
+    GtkWidget *content_widget;
+    GtkSorter *sorter;
 
-    list_view = NAUTILUS_LIST_VIEW (view);
-    tree_view = list_view->details->tree_view;
-    selection = gtk_tree_view_get_selection (tree_view);
-    list = gtk_tree_selection_get_selected_rows (selection, &tree_model);
+    gtk_widget_add_css_class (GTK_WIDGET (self), "nautilus-list-view");
 
-    if (list == NULL)
-    {
-        return;
-    }
+    g_signal_connect_object (nautilus_list_view_preferences,
+                             "changed::" NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_VISIBLE_COLUMNS,
+                             G_CALLBACK (update_columns_settings_from_metadata_and_preferences),
+                             self,
+                             G_CONNECT_SWAPPED);
+    g_signal_connect_object (nautilus_list_view_preferences,
+                             "changed::" NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_COLUMN_ORDER,
+                             G_CALLBACK (update_columns_settings_from_metadata_and_preferences),
+                             self,
+                             G_CONNECT_SWAPPED);
 
-    /* Advance the first selected item, since that's what we use for
-     * the previewer */
-    path = list->data;
-    moved = FALSE;
-    if (gtk_tree_model_get_iter (tree_model, &iter, path))
-    {
-        if (direction == GTK_DIR_UP)
-        {
-            moved = gtk_tree_model_iter_previous (tree_model, &iter);
-        }
-        else
-        {
-            moved = gtk_tree_model_iter_next (tree_model, &iter);
-        }
-    }
+    content_widget = nautilus_files_view_get_content_widget (NAUTILUS_FILES_VIEW (self));
 
-    if (moved)
-    {
-        g_signal_handlers_block_by_func (selection, list_selection_changed_callback, view);
+    self->view_ui = create_view_ui (self);
+    nautilus_list_base_setup_gestures (NAUTILUS_LIST_BASE (self));
 
-        gtk_tree_selection_unselect_all (selection);
-        gtk_tree_selection_select_iter (selection, &iter);
+    setup_view_columns (self);
 
-        g_signal_handlers_unblock_by_func (selection, list_selection_changed_callback, view);
-        nautilus_files_view_notify_selection_changed (view);
-    }
+    model = nautilus_list_base_get_model (NAUTILUS_LIST_BASE (self));
+    sorter = gtk_column_view_get_sorter (self->view_ui);
+    nautilus_view_model_set_sorter (model, sorter);
+    g_signal_connect_object (sorter, "changed", G_CALLBACK (on_sorter_changed), self, 0);
+
+    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),
+                                     list_view_entries,
+                                     G_N_ELEMENTS (list_view_entries),
+                                     self);
+
+    self->zoom_level = get_default_zoom_level ();
+    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_list_view_dispose (GObject *object)
+{
+    NautilusListView *self = NAUTILUS_LIST_VIEW (object);
 
-    g_list_free_full (list, (GDestroyNotify) gtk_tree_path_free);
+    g_clear_object (&self->file_path_base_location);
+    g_clear_pointer (&self->factory_to_column_map, g_hash_table_destroy);
+
+    G_OBJECT_CLASS (nautilus_list_view_parent_class)->dispose (object);
 }
 
 static void
-nautilus_list_view_class_init (NautilusListViewClass *class)
+nautilus_list_view_finalize (GObject *object)
 {
-    NautilusFilesViewClass *nautilus_files_view_class;
-
-    nautilus_files_view_class = NAUTILUS_FILES_VIEW_CLASS (class);
-
-    G_OBJECT_CLASS (class)->dispose = nautilus_list_view_dispose;
-    G_OBJECT_CLASS (class)->finalize = nautilus_list_view_finalize;
-
-    nautilus_files_view_class->add_files = nautilus_list_view_add_files;
-    nautilus_files_view_class->begin_loading = nautilus_list_view_begin_loading;
-    nautilus_files_view_class->end_loading = nautilus_list_view_end_loading;
-    nautilus_files_view_class->bump_zoom_level = nautilus_list_view_bump_zoom_level;
-    nautilus_files_view_class->can_zoom_in = nautilus_list_view_can_zoom_in;
-    nautilus_files_view_class->can_zoom_out = nautilus_list_view_can_zoom_out;
-    nautilus_files_view_class->is_zoom_level_default = nautilus_list_view_is_zoom_level_default;
-    nautilus_files_view_class->click_policy_changed = nautilus_list_view_click_policy_changed;
-    nautilus_files_view_class->clear = nautilus_list_view_clear;
-    nautilus_files_view_class->file_changed = nautilus_list_view_file_changed;
-    nautilus_files_view_class->get_backing_uri = nautilus_list_view_get_backing_uri;
-    nautilus_files_view_class->get_selection = nautilus_list_view_get_selection;
-    nautilus_files_view_class->get_selection_for_file_transfer = 
nautilus_list_view_get_selection_for_file_transfer;
-    nautilus_files_view_class->is_empty = nautilus_list_view_is_empty;
-    nautilus_files_view_class->remove_file = nautilus_list_view_remove_file;
-    nautilus_files_view_class->restore_standard_zoom_level = nautilus_list_view_restore_standard_zoom_level;
-    nautilus_files_view_class->reveal_selection = nautilus_list_view_reveal_selection;
-    nautilus_files_view_class->select_all = nautilus_list_view_select_all;
-    nautilus_files_view_class->select_first = nautilus_list_view_select_first;
-    nautilus_files_view_class->set_selection = nautilus_list_view_set_selection;
-    nautilus_files_view_class->invert_selection = nautilus_list_view_invert_selection;
-    nautilus_files_view_class->compare_files = nautilus_list_view_compare_files;
-    nautilus_files_view_class->sort_directories_first_changed = 
nautilus_list_view_sort_directories_first_changed;
-    nautilus_files_view_class->end_file_changes = nautilus_list_view_end_file_changes;
-    nautilus_files_view_class->get_view_id = nautilus_list_view_get_id;
-    nautilus_files_view_class->get_first_visible_file = nautilus_list_view_get_first_visible_file;
-    nautilus_files_view_class->scroll_to_file = list_view_scroll_to_file;
-    nautilus_files_view_class->compute_rename_popover_pointing_to = 
nautilus_list_view_compute_rename_popover_pointing_to;
-    nautilus_files_view_class->reveal_for_selection_context_menu = 
nautilus_list_view_reveal_for_selection_context_menu;
-    nautilus_files_view_class->preview_selection_event = nautilus_list_view_preview_selection_event;
+    G_OBJECT_CLASS (nautilus_list_view_parent_class)->finalize (object);
 }
 
 static void
-nautilus_list_view_init (NautilusListView *list_view)
+nautilus_list_view_class_init (NautilusListViewClass *klass)
 {
-    GActionGroup *view_action_group;
-    GdkClipboard *clipboard;
-
-    list_view->details = g_new0 (NautilusListViewDetails, 1);
-
-    /* ensure that the zoom level is always set before settings up the tree view columns */
-    list_view->details->zoom_level = get_default_zoom_level ();
-
-    create_and_set_up_tree_view (list_view);
-
-    gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (list_view)),
-                                 "nautilus-list-view");
-
-    list_view->details->columns_popover = gtk_popover_new ();
-    gtk_widget_set_parent (list_view->details->columns_popover,
-                           GTK_WIDGET (list_view));
-    g_signal_connect (list_view->details->columns_popover, "destroy", G_CALLBACK (gtk_widget_unparent), 
NULL);
-
-    list_view->details->columns_popover_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
-    gtk_widget_set_margin_top (list_view->details->columns_popover_box, 6);
-    gtk_widget_set_margin_bottom (list_view->details->columns_popover_box, 6);
-    gtk_widget_set_margin_start (list_view->details->columns_popover_box, 6);
-    gtk_widget_set_margin_end (list_view->details->columns_popover_box, 6);
-    gtk_popover_set_child (GTK_POPOVER (list_view->details->columns_popover),
-                           list_view->details->columns_popover_box);
-
-    g_signal_connect_swapped (nautilus_preferences,
-                              "changed::" NAUTILUS_PREFERENCES_DEFAULT_SORT_ORDER,
-                              G_CALLBACK (default_sort_order_changed_callback),
-                              list_view);
-    g_signal_connect_swapped (nautilus_preferences,
-                              "changed::" NAUTILUS_PREFERENCES_DEFAULT_SORT_IN_REVERSE_ORDER,
-                              G_CALLBACK (default_sort_order_changed_callback),
-                              list_view);
-    g_signal_connect_swapped (nautilus_list_view_preferences,
-                              "changed::" NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_VISIBLE_COLUMNS,
-                              G_CALLBACK (default_visible_columns_changed_callback),
-                              list_view);
-    g_signal_connect_swapped (nautilus_list_view_preferences,
-                              "changed::" NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_COLUMN_ORDER,
-                              G_CALLBACK (default_column_order_changed_callback),
-                              list_view);
-
-    /* React to clipboard changes */
-    clipboard = gdk_display_get_clipboard (gdk_display_get_default ());
-    g_signal_connect_object (clipboard, "changed",
-                             G_CALLBACK (on_clipboard_owner_changed), list_view,
-                             0);
-
-    nautilus_list_view_click_policy_changed (NAUTILUS_FILES_VIEW (list_view));
-
-    nautilus_list_view_set_zoom_level (list_view, get_default_zoom_level ());
-
-    list_view->details->hover_path = NULL;
-
-    view_action_group = nautilus_files_view_get_action_group (NAUTILUS_FILES_VIEW (list_view));
-    g_action_map_add_action_entries (G_ACTION_MAP (view_action_group),
-                                     list_view_entries,
-                                     G_N_ELEMENTS (list_view_entries),
-                                     list_view);
-    /* 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 
(list_view)),
-                                        "zoom-to-level", g_variant_new_int32 (get_default_zoom_level ()));
+    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);
 
-    list_view->details->regex = g_regex_new ("\\R+", 0, G_REGEX_MATCH_NEWLINE_ANY, NULL);
+    object_class->dispose = nautilus_list_view_dispose;
+    object_class->finalize = nautilus_list_view_finalize;
 
-    list_view->details->starred_cancellable = g_cancellable_new ();
+    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->sort_directories_first_changed = real_sort_directories_first_changed;
+    files_view_class->get_view_id = real_get_view_id;
+    files_view_class->restore_standard_zoom_level = real_restore_standard_zoom_level;
+    files_view_class->is_zoom_level_default = real_is_zoom_level_default;
 
-    g_signal_connect (nautilus_tag_manager_get (),
-                      "starred-changed",
-                      (GCallback) on_starred_files_changed,
-                      list_view);
+    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;
 }
 
-NautilusFilesView *
+NautilusListView *
 nautilus_list_view_new (NautilusWindowSlot *slot)
 {
     return g_object_new (NAUTILUS_TYPE_LIST_VIEW,
diff --git a/src/nautilus-list-view.h b/src/nautilus-list-view.h
index 7e19621e8..8c2133648 100644
--- a/src/nautilus-list-view.h
+++ b/src/nautilus-list-view.h
@@ -1,40 +1,22 @@
-
-/* fm-list-view.h - interface for list view of directory.
-
-   Copyright (C) 2000 Eazel, Inc.
-   Copyright (C) 2001 Anders Carlsson <andersca gnu org>
-   
-   The Gnome Library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Library General Public License as
-   published by the Free Software Foundation; either version 2 of the
-   License, or (at your option) any later version.
-
-   The Gnome Library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Library General Public License for more details.
-
-   You should have received a copy of the GNU Library General Public
-   License along with the Gnome Library; see the file COPYING.LIB.  If not,
-   see <http://www.gnu.org/licenses/>.
-
-   Authors: John Sullivan <sullivan eazel com>
-            Anders Carlsson <andersca gnu org>
-*/
+/*
+ * Copyright (C) 2000 Eazel, Inc.
+ * Copyright (C) 2001, 2002 Anders Carlsson <andersca gnu org>
+ * Copyright (C) 2022 GNOME project contributors
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
 
 #pragma once
 
-#include "nautilus-files-view.h"
+#include "nautilus-list-base.h"
+#include "nautilus-window-slot.h"
+
+G_BEGIN_DECLS
 
-#define NAUTILUS_TYPE_LIST_VIEW (nautilus_list_view_get_type ())
-G_DECLARE_FINAL_TYPE (NautilusListView, nautilus_list_view, NAUTILUS, LIST_VIEW, NautilusFilesView)
+#define NAUTILUS_TYPE_LIST_VIEW (nautilus_list_view_get_type())
 
-typedef struct NautilusListViewDetails NautilusListViewDetails;
+G_DECLARE_FINAL_TYPE (NautilusListView, nautilus_list_view, NAUTILUS, LIST_VIEW, NautilusListBase)
 
-struct _NautilusListView
-{
-       NautilusFilesView parent_instance;
-       NautilusListViewDetails *details;
-};
+NautilusListView *nautilus_list_view_new (NautilusWindowSlot *slot);
 
-NautilusFilesView * nautilus_list_view_new (NautilusWindowSlot *slot);
\ No newline at end of file
+G_END_DECLS
diff --git a/src/nautilus-name-cell.c b/src/nautilus-name-cell.c
new file mode 100644
index 000000000..71e6e5022
--- /dev/null
+++ b/src/nautilus-name-cell.c
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2022 The GNOME project contributors
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include "nautilus-name-cell.h"
+#include "nautilus-file-utilities.h"
+
+struct _NautilusNameCell
+{
+    NautilusViewCell parent_instance;
+
+    GSignalGroup *item_signal_group;
+
+    GQuark path_attribute_q;
+    GFile *file_path_base_location;
+
+    GtkWidget *fixed_height_box;
+    GtkWidget *icon;
+    GtkWidget *label;
+    GtkWidget *snippet;
+    GtkWidget *path;
+
+    gboolean show_snippet;
+};
+
+G_DEFINE_TYPE (NautilusNameCell, nautilus_name_cell, NAUTILUS_TYPE_VIEW_CELL)
+
+static gchar *
+get_path_text (NautilusFile *file,
+               GQuark        path_attribute_q,
+               GFile        *base_location)
+{
+    gchar *path_text;
+    g_autofree gchar *path = NULL;
+    g_autoptr (GFile) dir_location = NULL;
+    g_autoptr (GFile) home_location = g_file_new_for_path (g_get_home_dir ());
+
+    if (path_attribute_q == 0)
+    {
+        return NULL;
+    }
+
+    path = nautilus_file_get_string_attribute_q (file, path_attribute_q);
+    dir_location = g_file_new_for_commandline_arg (path);
+
+    if (g_file_equal (base_location, dir_location))
+    {
+        /* Only occurs when search result is
+         * a direct child of the base location
+         */
+        return NULL;
+    }
+
+    if (g_file_has_prefix (dir_location, base_location))
+    {
+        g_autofree gchar *relative_path = NULL;
+
+        relative_path = g_file_get_relative_path (base_location, dir_location);
+        path_text = g_filename_display_name (relative_path);
+    }
+    else if (g_file_equal (dir_location, home_location))
+    {
+        path_text = nautilus_compute_title_for_location (home_location);
+    }
+    else if (base_location == NULL &&
+             g_file_has_prefix (dir_location, home_location))
+    {
+        g_autofree gchar *relative_path = NULL;
+
+        relative_path = g_file_get_relative_path (home_location, dir_location);
+        path_text = g_filename_display_name (relative_path);
+    }
+    else
+    {
+        path_text = g_file_get_path (dir_location);
+    }
+
+    return path_text;
+}
+
+static gchar *
+get_fts_snippet (NautilusFile *file)
+{
+    const gchar *snippet;
+    g_autoptr (GRegex) regex = NULL;
+
+    snippet = nautilus_file_get_search_fts_snippet (file);
+    if (snippet == NULL)
+    {
+        return NULL;
+    }
+
+    /* Flatten the text by replacing new lines with spaces */
+    regex = g_regex_new ("\\R+", 0, G_REGEX_MATCH_NEWLINE_ANY, NULL);
+    return g_regex_replace (regex,
+                            snippet,
+                            -1,
+                            0,
+                            " ",
+                            G_REGEX_MATCH_NEWLINE_ANY,
+                            NULL);
+}
+
+static void
+update_labels (NautilusNameCell *self)
+{
+    NautilusViewItem *item;
+    NautilusFile *file;
+    g_autofree gchar *display_name = NULL;
+    g_autofree gchar *path_text = NULL;
+    g_autofree gchar *fts_snippet = NULL;
+
+    item = nautilus_view_cell_get_item (NAUTILUS_VIEW_CELL (self));
+    g_return_if_fail (item != NULL);
+    file = nautilus_view_item_get_file (item);
+
+    display_name = nautilus_file_get_display_name (file);
+    path_text = get_path_text (file,
+                               self->path_attribute_q,
+                               self->file_path_base_location);
+    if (self->show_snippet)
+    {
+        fts_snippet = get_fts_snippet (file);
+    }
+
+    gtk_label_set_text (GTK_LABEL (self->label), display_name);
+    gtk_label_set_text (GTK_LABEL (self->path), path_text);
+    gtk_label_set_text (GTK_LABEL (self->snippet), fts_snippet);
+
+    gtk_widget_set_visible (self->path, (path_text != NULL));
+    gtk_widget_set_visible (self->snippet, (fts_snippet != NULL));
+}
+
+static void
+update_icon (NautilusNameCell *self)
+{
+    NautilusFileIconFlags flags;
+    g_autoptr (GdkPaintable) icon_paintable = NULL;
+    GtkStyleContext *style_context;
+    NautilusViewItem *item;
+    NautilusFile *file;
+    guint icon_size;
+    int icon_height;
+    int extra_margin;
+    g_autofree gchar *thumbnail_path = NULL;
+
+    item = nautilus_view_cell_get_item (NAUTILUS_VIEW_CELL (self));
+    g_return_if_fail (item != NULL);
+
+    file = nautilus_view_item_get_file (item);
+    icon_size = nautilus_view_item_get_icon_size (item);
+    flags = NAUTILUS_FILE_ICON_FLAGS_USE_THUMBNAILS |
+            NAUTILUS_FILE_ICON_FLAGS_FORCE_THUMBNAIL_SIZE |
+            NAUTILUS_FILE_ICON_FLAGS_USE_EMBLEMS |
+            NAUTILUS_FILE_ICON_FLAGS_USE_ONE_EMBLEM;
+
+    icon_paintable = nautilus_file_get_icon_paintable (file, icon_size, 1, flags);
+    gtk_picture_set_paintable (GTK_PICTURE (self->icon), icon_paintable);
+
+    /* Set the same width for all icons regardless of aspect ratio.
+     * Don't set the width here because it would get GtkPicture w4h confused.
+     */
+    gtk_widget_set_size_request (self->fixed_height_box, icon_size, -1);
+
+    /* Give all items the same minimum width. This cannot be done by setting the
+     * width request directly, as above, because it would get mess up with
+     * height for width calculations.
+     *
+     * Instead we must add margins on both sides of the icon which, summed up
+     * with the icon's actual width, equal the desired item width. */
+    icon_height = gdk_paintable_get_intrinsic_height (icon_paintable);
+    extra_margin = (icon_size - icon_height) / 2;
+    gtk_widget_set_margin_top (self->fixed_height_box, extra_margin);
+    gtk_widget_set_margin_bottom (self->fixed_height_box, extra_margin);
+
+    style_context = gtk_widget_get_style_context (self->icon);
+    thumbnail_path = nautilus_file_get_thumbnail_path (file);
+    if (icon_size >= NAUTILUS_THUMBNAIL_MINIMUM_ICON_SIZE &&
+        thumbnail_path != NULL &&
+        nautilus_file_should_show_thumbnail (file))
+    {
+        gtk_style_context_add_class (style_context, "thumbnail");
+    }
+    else
+    {
+        gtk_style_context_remove_class (style_context, "thumbnail");
+    }
+}
+
+static void
+on_file_changed (NautilusNameCell *self)
+{
+    update_icon (self);
+    update_labels (self);
+}
+
+static void
+on_item_size_changed (NautilusNameCell *self)
+{
+    update_icon (self);
+}
+
+static void
+on_item_is_cut_changed (NautilusNameCell *self)
+{
+    gboolean is_cut;
+
+    g_object_get (self, "is-cut", &is_cut, NULL);
+    if (is_cut)
+    {
+        gtk_widget_add_css_class (self->icon, "cut");
+    }
+    else
+    {
+        gtk_widget_remove_css_class (self->icon, "cut");
+    }
+}
+
+static void
+nautilus_name_cell_init (NautilusNameCell *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);
+    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);
+}
+
+static void
+nautilus_name_cell_finalize (GObject *object)
+{
+    NautilusNameCell *self = (NautilusNameCell *) object;
+
+    g_object_unref (self->item_signal_group);
+    g_object_unref (self->file_path_base_location);
+    G_OBJECT_CLASS (nautilus_name_cell_parent_class)->finalize (object);
+}
+
+static void
+nautilus_name_cell_class_init (NautilusNameCellClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS (klass);
+    GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+    object_class->finalize = nautilus_name_cell_finalize;
+
+    gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/nautilus/ui/nautilus-name-cell.ui");
+
+    gtk_widget_class_bind_template_child (widget_class, NautilusNameCell, fixed_height_box);
+    gtk_widget_class_bind_template_child (widget_class, NautilusNameCell, icon);
+    gtk_widget_class_bind_template_child (widget_class, NautilusNameCell, label);
+    gtk_widget_class_bind_template_child (widget_class, NautilusNameCell, snippet);
+    gtk_widget_class_bind_template_child (widget_class, NautilusNameCell, path);
+}
+
+NautilusViewCell *
+nautilus_name_cell_new (NautilusListBase *view)
+{
+    return NAUTILUS_VIEW_CELL (g_object_new (NAUTILUS_TYPE_NAME_CELL,
+                                             "view", view,
+                                             NULL));
+}
+
+void
+nautilus_name_cell_set_path (NautilusNameCell *self,
+                             GQuark            path_attribute_q,
+                             GFile            *base_location)
+{
+    self->path_attribute_q = path_attribute_q;
+    g_set_object (&self->file_path_base_location, base_location);
+}
+
+void
+nautilus_name_cell_show_snippet (NautilusNameCell *self)
+{
+    self->show_snippet = TRUE;
+}
diff --git a/src/nautilus-name-cell.h b/src/nautilus-name-cell.h
new file mode 100644
index 000000000..62862cdf5
--- /dev/null
+++ b/src/nautilus-name-cell.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2022 António Fernandes <antoniof gnome org>
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include "nautilus-view-cell.h"
+
+G_BEGIN_DECLS
+
+#define NAUTILUS_TYPE_NAME_CELL (nautilus_name_cell_get_type())
+
+G_DECLARE_FINAL_TYPE (NautilusNameCell, nautilus_name_cell, NAUTILUS, NAME_CELL, NautilusViewCell)
+
+NautilusViewCell * nautilus_name_cell_new (NautilusListBase *view);
+void nautilus_name_cell_set_path (NautilusNameCell *self,
+                                  GQuark            path_attribute_q,
+                                  GFile            *base_location);
+void nautilus_name_cell_show_snippet (NautilusNameCell *self);
+
+G_END_DECLS
diff --git a/src/nautilus-star-cell.c b/src/nautilus-star-cell.c
new file mode 100644
index 000000000..a47a6b2ff
--- /dev/null
+++ b/src/nautilus-star-cell.c
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2022 António Fernandes <antoniof gnome org>
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include "nautilus-star-cell.h"
+#include "nautilus-tag-manager.h"
+
+struct _NautilusStarCell
+{
+    NautilusViewCell parent_instance;
+
+    GSignalGroup *item_signal_group;
+
+    GtkImage *star;
+};
+
+G_DEFINE_TYPE (NautilusStarCell, nautilus_star_cell, NAUTILUS_TYPE_VIEW_CELL)
+
+static void
+on_star_click_released (GtkGestureClick *gesture,
+                        gint             n_press,
+                        gdouble          x,
+                        gdouble          y,
+                        gpointer         user_data)
+{
+    NautilusStarCell *self = user_data;
+    NautilusTagManager *tag_manager = nautilus_tag_manager_get ();
+    NautilusViewItem *item;
+    NautilusFile *file;
+    g_autofree gchar *uri = NULL;
+
+    item = nautilus_view_cell_get_item (NAUTILUS_VIEW_CELL (self));
+    g_return_if_fail (item != NULL);
+    file = nautilus_view_item_get_file (item);
+    uri = nautilus_file_get_uri (file);
+
+    if (nautilus_tag_manager_file_is_starred (tag_manager, uri))
+    {
+        nautilus_tag_manager_unstar_files (tag_manager,
+                                           G_OBJECT (item),
+                                           &(GList){ file, NULL },
+                                           NULL,
+                                           NULL);
+        gtk_widget_remove_css_class (GTK_WIDGET (self->star), "added");
+    }
+    else
+    {
+        nautilus_tag_manager_star_files (tag_manager,
+                                         G_OBJECT (item),
+                                         &(GList){ file, NULL },
+                                         NULL,
+                                         NULL);
+        gtk_widget_add_css_class (GTK_WIDGET (self->star), "added");
+    }
+
+    gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
+}
+
+static void
+update_star (GtkImage     *star,
+             NautilusFile *file)
+{
+    gboolean is_starred;
+    g_autofree gchar *file_uri = NULL;
+
+    g_return_if_fail (NAUTILUS_IS_FILE (file));
+
+    file_uri = nautilus_file_get_uri (file);
+    is_starred = nautilus_tag_manager_file_is_starred (nautilus_tag_manager_get (),
+                                                       file_uri);
+
+    gtk_image_set_from_icon_name (star, is_starred ? "starred-symbolic" : "non-starred-symbolic");
+}
+
+static void
+on_file_changed (NautilusStarCell *self)
+{
+    NautilusViewItem *item;
+    NautilusFile *file;
+    g_autofree gchar *string = NULL;
+
+    item = nautilus_view_cell_get_item (NAUTILUS_VIEW_CELL (self));
+    g_return_if_fail (item != NULL);
+    file = nautilus_view_item_get_file (item);
+
+    update_star (self->star, file);
+}
+
+static void
+on_starred_changed (NautilusTagManager *tag_manager,
+                    GList              *changed_files,
+                    gpointer            user_data)
+{
+    NautilusStarCell *self = user_data;
+    NautilusViewItem *item;
+    NautilusFile *file;
+
+    item = nautilus_view_cell_get_item (NAUTILUS_VIEW_CELL (self));
+    if (item == NULL)
+    {
+        return;
+    }
+
+    file = nautilus_view_item_get_file (item);
+    if (g_list_find (changed_files, file))
+    {
+        update_star (self->star, file);
+    }
+}
+
+static void
+nautilus_star_cell_init (NautilusStarCell *self)
+{
+    GtkWidget *star;
+    GtkGesture *gesture;
+
+    /* Create star icon */
+    star = gtk_image_new ();
+    gtk_widget_set_halign (star, GTK_ALIGN_END);
+    gtk_widget_set_valign (star, GTK_ALIGN_CENTER);
+    gtk_widget_add_css_class (star, "dim-label");
+    gtk_widget_add_css_class (star, "star");
+    adw_bin_set_child (ADW_BIN (self), star);
+    self->star = GTK_IMAGE (star);
+
+    /* Make it clickable */
+    gesture = gtk_gesture_click_new ();
+    gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture), GDK_BUTTON_PRIMARY);
+    g_signal_connect (gesture, "released", G_CALLBACK (on_star_click_released), self);
+    gtk_widget_add_controller (star, GTK_EVENT_CONTROLLER (gesture));
+
+    /* Update on tag changes */
+    g_signal_connect_object (nautilus_tag_manager_get (), "starred-changed",
+                             G_CALLBACK (on_starred_changed), self, 0);
+
+    /* Connect automatically to an item. */
+    self->item_signal_group = g_signal_group_new (NAUTILUS_TYPE_VIEW_ITEM);
+    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);
+}
+
+static void
+nautilus_star_cell_finalize (GObject *object)
+{
+    NautilusStarCell *self = (NautilusStarCell *) object;
+
+    g_object_unref (self->item_signal_group);
+    G_OBJECT_CLASS (nautilus_star_cell_parent_class)->finalize (object);
+}
+
+static void
+nautilus_star_cell_class_init (NautilusStarCellClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+    object_class->finalize = nautilus_star_cell_finalize;
+}
+
+NautilusViewCell *
+nautilus_star_cell_new (NautilusListBase *view)
+{
+    return NAUTILUS_VIEW_CELL (g_object_new (NAUTILUS_TYPE_STAR_CELL,
+                                             "view", view,
+                                             NULL));
+}
diff --git a/src/nautilus-star-cell.h b/src/nautilus-star-cell.h
new file mode 100644
index 000000000..415a74514
--- /dev/null
+++ b/src/nautilus-star-cell.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2022 António Fernandes <antoniof gnome org>
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include "nautilus-view-cell.h"
+
+G_BEGIN_DECLS
+
+#define NAUTILUS_TYPE_STAR_CELL (nautilus_star_cell_get_type())
+
+G_DECLARE_FINAL_TYPE (NautilusStarCell, nautilus_star_cell, NAUTILUS, STAR_CELL, NautilusViewCell)
+
+NautilusViewCell * nautilus_star_cell_new (NautilusListBase *view);
+
+G_END_DECLS
diff --git a/src/nautilus-view-cell.c b/src/nautilus-view-cell.c
index bd5fb30d8..6f28fd8ff 100644
--- a/src/nautilus-view-cell.c
+++ b/src/nautilus-view-cell.c
@@ -106,6 +106,7 @@ nautilus_view_cell_set_property (GObject      *object,
 static void
 nautilus_view_cell_init (NautilusViewCell *self)
 {
+    gtk_widget_set_name (GTK_WIDGET (self), "NautilusViewCell");
 }
 
 static void
diff --git a/src/resources/css/Adwaita.css b/src/resources/css/Adwaita.css
index 830eeed06..b2c93ffc4 100644
--- a/src/resources/css/Adwaita.css
+++ b/src/resources/css/Adwaita.css
@@ -138,13 +138,6 @@
     color: shade(@disk_space_free, 0.7);
 }
 
-/* As a workaround for GtkTreeView showing thick border above first row when
- * horizontal grid lines are enabled, we add them using CSS instead. Exclude the
- * drop targets to let through the default feedback style. */
-.nautilus-list-view .view:not(:drop(active)) {
-    border-bottom: 1px solid @window_bg_color;
-}
-
 .search-information {
   background-color: @accent_bg_color;
   color:white;
@@ -165,7 +158,53 @@
   padding: 6px;
 }
 
-.nautilus-grid-view .thumbnail {
+/* Column view */
+
+/* Setup padding on the list. Horizontal padding must be set on the columnview
+ * for it to calculate column widths correctly. */
+.nautilus-list-view columnview {
+  padding-left: 24px;
+  padding-right: 24px;
+}
+.nautilus-list-view columnview > listview {
+  padding-top: 12px;
+  padding-bottom: 24px;
+}
+
+/* Use negative margins to extend rubberbanding area into the columnview's
+ * padding, then apply positive margin on rows to reestablish positioning. */
+.nautilus-list-view columnview > listview {
+  margin-left: -24px;
+  margin-right: -24px;
+}
+.nautilus-list-view columnview > listview > row {
+  margin-left: 24px;
+  margin-right: 24px;
+}
+
+.nautilus-list-view columnview > listview > row {
+  border-radius: 6px;
+  margin-top: 4px;
+  margin-bottom: 4px;
+}
+
+/* GTK unconditionally sets padding on GtkColumnViewCell, even with .data-table.
+ * We don't want this to hpappen because we have event controllers on the child,
+ * which should thus cover the whole area of the row. */
+.nautilus-list-view columnview > listview > row > cell {
+  padding: 0px;
+}
+
+.nautilus-list-view #NautilusViewCell {
+  padding: 6px;
+}
+
+.nautilus-list-view image.star {
+  padding: 6px;
+}
+
+/* Both views */
+.view .thumbnail {
   background: url('/org/gnome/nautilus/Checkerboard.png') repeat;
   box-shadow: 0px 1px 2px 0px @shade_color,
               0px 0px 0px 1px @shade_color,
@@ -173,6 +212,19 @@
   border-radius: 2px;
 }
 
-.nautilus-grid-view .cut {
+.view .cut {
   opacity: 0.55;
 }
+
+.view image.star:hover {
+  opacity: 1;
+}
+
+@keyframes rotate_star {
+  from { -gtk-icon-transform: rotate(-72deg); }
+  to {}
+}
+
+.view image.star.added {
+  animation: rotate_star 0.4s ease;
+}
diff --git a/src/resources/nautilus.gresource.xml b/src/resources/nautilus.gresource.xml
index 8d5db0e18..d6ae0a20a 100644
--- a/src/resources/nautilus.gresource.xml
+++ b/src/resources/nautilus.gresource.xml
@@ -26,6 +26,7 @@
     <file>ui/nautilus-files-view-select-items.ui</file>
     <file>ui/nautilus-operations-ui-manager-request-passphrase.ui</file>
     <file>ui/nautilus-grid-cell.ui</file>
+    <file>ui/nautilus-name-cell.ui</file>
     <file alias="gtk/ui/nautilusgtksidebarrow.ui">../gtk/nautilusgtksidebarrow.ui</file>
     <file alias="gtk/ui/nautilusgtkplacesview.ui">../gtk/nautilusgtkplacesview.ui</file>
     <file alias="gtk/ui/nautilusgtkplacesviewrow.ui">../gtk/nautilusgtkplacesviewrow.ui</file>
diff --git a/src/resources/ui/nautilus-name-cell.ui b/src/resources/ui/nautilus-name-cell.ui
new file mode 100644
index 000000000..366e6e370
--- /dev/null
+++ b/src/resources/ui/nautilus-name-cell.ui
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <requires lib="gtk" version="4.0"/>
+  <template class="NautilusNameCell" parent="NautilusViewCell">
+    <child>
+      <object class="GtkBox">
+        <property name="spacing">6</property>
+        <property name="orientation">horizontal</property>
+        <property name="halign">fill</property>
+        <property name="valign">center</property>
+        <child>
+          <object class="GtkBox" id="fixed_height_box">
+            <property name="orientation">vertical</property>
+            <property name="halign">center</property>
+            <property name="height-request">16</property>
+            <property name="valign">center</property>
+            <child>
+              <object class="GtkPicture" id="icon">
+                <property name="halign">center</property>
+                <property name="valign">center</property>
+                <property name="can-shrink">False</property>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="GtkBox">
+            <property name="orientation">vertical</property>
+            <property name="halign">fill</property>
+            <property name="hexpand">True</property>
+            <property name="valign">center</property>
+            <style>
+              <class name="column-name-labels-box"/>
+            </style>
+            <child>
+              <object class="GtkLabel" id="path">
+                <property name="visible">False</property>
+                <property name="ellipsize">start</property>
+                <property name="justify">left</property>
+                <property name="lines">2</property>
+                <property name="wrap">True</property>
+                <property name="wrap-mode">word-char</property>
+                <property name="halign">fill</property>
+                <property name="xalign">0.0</property>
+                <attributes>
+                  <attribute name="insert-hyphens" value="false"></attribute>
+                </attributes>
+                <style>
+                  <class name="caption"/>
+                  <class name="dim-label"/>
+                </style>
+              </object>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label">
+                <property name="ellipsize">middle</property>
+                <property name="lines">1</property>
+                <property name="max-width-chars">-1</property>
+                <property name="wrap">False</property>
+                <property name="wrap-mode">word-char</property>
+                <property name="halign">start</property>
+                <attributes>
+                  <attribute name="insert-hyphens" value="false"></attribute>
+                </attributes>
+              </object>
+            </child>
+            <child>
+              <object class="GtkLabel" id="snippet">
+                <property name="visible">False</property>
+                <property name="ellipsize">end</property>
+                <property name="justify">left</property>
+                <property name="lines">2</property>
+                <property name="wrap">True</property>
+                <property name="wrap-mode">word</property>
+                <property name="halign">fill</property>
+                <property name="xalign">0.0</property>
+                <style>
+                  <class name="caption-heading"/>
+                  <class name="accent"/>
+                </style>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/src/resources/ui/nautilus-preferences-window.ui b/src/resources/ui/nautilus-preferences-window.ui
index 0c5d08cee..b747beaee 100644
--- a/src/resources/ui/nautilus-preferences-window.ui
+++ b/src/resources/ui/nautilus-preferences-window.ui
@@ -34,7 +34,7 @@
                 <property name="title" translatable="yes">_Expandable Folders in List View</property>
                 <property name="title_lines">0</property>
                 <property name="use_underline">True</property>
-                <property name="visible">True</property>
+                <property name="visible">False</property>
                 <child>
                   <object class="GtkSwitch" id="use_tree_view_switch">
                     <property name="valign">center</property>


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