[nautilus] views: add flow box based view



commit 1d166b5e3b34195efe04df950a3f5397c2c99ef7
Author: Carlos Soriano <csoriano gnome org>
Date:   Thu Apr 7 21:51:52 2016 +0200

    views: add flow box based view
    
    After all the rework on the window slots, views, and splitting
    the desktop, we are finally able to add a flow box based view for
    Nautilus.
    
    The GtkFlowBox is still not performance enough to be added as the
    default view, not even as an alternative in the user preferences.
    However, since the work on this is one of the biggest for Nautilus and
    gtk+, the decision was to merge a prototype in order to open the
    development, testing and iteration of the code in order to make it
    good enough for, in a not so far away future, have it as the main view.
    
    The work merged is not finished, and is an experiment and prototype in
    more things than just the GtkFlowBox, we want to create a single shared
    model with a complete MVC pattern between all the views. This will need
    quite a few iterations to get it right, but once is done right, I hope
    it's going to be good enough as an example to any application that wants
    multiple types of views with the new widgets of gtk+.
    
    This patch adds the GtkFlowBox view to be optionally used under a
    gsetting called use-experimental-views, turned off by default.

 data/org.gnome.nautilus.gschema.xml |    5 +
 src/Makefile.am                     |   12 +
 src/nautilus-canvas-view.c          |   20 +-
 src/nautilus-container-max-width.c  |  216 +++++++++
 src/nautilus-container-max-width.h  |   22 +
 src/nautilus-file.c                 |   19 +-
 src/nautilus-files-view.c           |  131 +++---
 src/nautilus-files-view.h           |   10 +-
 src/nautilus-global-preferences.h   |    3 +
 src/nautilus-list-view.c            |   14 +-
 src/nautilus-view-icon-controller.c |  876 +++++++++++++++++++++++++++++++++++
 src/nautilus-view-icon-controller.h |   24 +
 src/nautilus-view-icon-item-ui.c    |  268 +++++++++++
 src/nautilus-view-icon-item-ui.h    |   22 +
 src/nautilus-view-icon-ui.c         |  252 ++++++++++
 src/nautilus-view-icon-ui.h         |   37 ++
 src/nautilus-view-item-model.c      |  249 ++++++++++
 src/nautilus-view-item-model.h      |   41 ++
 src/nautilus-view-model.c           |  325 +++++++++++++
 src/nautilus-view-model.h           |   43 ++
 src/resources/css/Adwaita.css       |   12 +-
 21 files changed, 2513 insertions(+), 88 deletions(-)
---
diff --git a/data/org.gnome.nautilus.gschema.xml b/data/org.gnome.nautilus.gschema.xml
index 5dc677a..151acab 100644
--- a/data/org.gnome.nautilus.gschema.xml
+++ b/data/org.gnome.nautilus.gschema.xml
@@ -211,6 +211,11 @@
       <summary>Whether to open the hovered folder after a timeout when drag and drop operation</summary>
       <description>If this is set to true, when performing a drag and drop operation the hovered folder will 
open automatically after a timeout.</description>
     </key>
+    <key type="b" name="use-experimental-views">
+      <default>false</default>
+      <summary>Enable new experimental views</summary>
+      <description>Whether to use new experimental views using latest gtk+ widgets to help giving feedback 
and shape the future of them.</description>
+    </key>
   </schema>
 
   <schema path="/org/gnome/nautilus/compression/" id="org.gnome.nautilus.compression" 
gettext-domain="nautilus">
diff --git a/src/Makefile.am b/src/Makefile.am
index db0a802..c79c030 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -158,6 +158,8 @@ nautilus_no_main_sources = \
        nautilus-canvas-view.h                  \
        nautilus-canvas-view-container.c        \
        nautilus-canvas-view-container.h        \
+       nautilus-container-max-width.c          \
+       nautilus-container-max-width.h          \
        nautilus-dbus-manager.c                 \
        nautilus-dbus-manager.h                 \
        nautilus-desktop-item-properties.c      \
@@ -220,6 +222,16 @@ nautilus_no_main_sources = \
        nautilus-trash-bar.h                    \
        nautilus-view.c                         \
        nautilus-view.h                         \
+       nautilus-view-icon-controller.c         \
+       nautilus-view-icon-controller.h         \
+       nautilus-view-icon-item-ui.c            \
+       nautilus-view-icon-item-ui.h            \
+       nautilus-view-icon-ui.c                 \
+       nautilus-view-icon-ui.h                 \
+       nautilus-view-item-model.c              \
+       nautilus-view-item-model.h              \
+       nautilus-view-model.c                   \
+       nautilus-view-model.h                   \
        nautilus-window-slot.c                  \
        nautilus-window-slot.h                  \
        nautilus-window-slot-dnd.c              \
diff --git a/src/nautilus-canvas-view.c b/src/nautilus-canvas-view.c
index f1ed2c5..ab4512d 100644
--- a/src/nautilus-canvas-view.c
+++ b/src/nautilus-canvas-view.c
@@ -101,7 +101,7 @@ typedef struct
     gboolean supports_scaling;
     gboolean supports_keep_aligned;
 
-    /* Needed for async operations. Suposedly we would use cancellable and gtask,
+    /* FIXME: Needed for async operations. Suposedly we would use cancellable and gtask,
      * sadly gtkclipboard doesn't support that.
      * We follow this pattern for checking validity of the object in the views.
      * Ideally we would connect to a weak reference and do a cancellable.
@@ -447,12 +447,13 @@ nautilus_canvas_view_remove_file (NautilusFilesView *view,
 }
 
 static void
-nautilus_canvas_view_add_file (NautilusFilesView *view,
-                               NautilusFile      *file,
-                               NautilusDirectory *directory)
+nautilus_canvas_view_add_files (NautilusFilesView *view,
+                                GList             *files,
+                                NautilusDirectory *directory)
 {
     NautilusCanvasView *canvas_view;
     NautilusCanvasContainer *canvas_container;
+    GList *l;
 
     g_assert (directory == nautilus_files_view_get_model (view));
 
@@ -465,10 +466,13 @@ nautilus_canvas_view_add_file (NautilusFilesView *view,
         nautilus_canvas_container_reset_scroll_region (canvas_container);
     }
 
-    if (nautilus_canvas_container_add (canvas_container,
-                                       NAUTILUS_CANVAS_ICON_DATA (file)))
+    for (l = files; l != NULL; l = l->next)
     {
-        nautilus_file_ref (file);
+        if (nautilus_canvas_container_add (canvas_container,
+                                           NAUTILUS_CANVAS_ICON_DATA (l->data)))
+        {
+            nautilus_file_ref (NAUTILUS_FILE (l->data));
+        }
     }
 }
 
@@ -2007,7 +2011,7 @@ nautilus_canvas_view_class_init (NautilusCanvasViewClass *klass)
 
     klass->create_canvas_container = real_create_canvas_container;
 
-    nautilus_files_view_class->add_file = nautilus_canvas_view_add_file;
+    nautilus_files_view_class->add_files = nautilus_canvas_view_add_files;
     nautilus_files_view_class->begin_loading = nautilus_canvas_view_begin_loading;
     nautilus_files_view_class->bump_zoom_level = nautilus_canvas_view_bump_zoom_level;
     nautilus_files_view_class->can_zoom_in = nautilus_canvas_view_can_zoom_in;
diff --git a/src/nautilus-container-max-width.c b/src/nautilus-container-max-width.c
new file mode 100644
index 0000000..b94e016
--- /dev/null
+++ b/src/nautilus-container-max-width.c
@@ -0,0 +1,216 @@
+#include "nautilus-container-max-width.h"
+
+struct _NautilusContainerMaxWidth
+{
+    GtkBin parent_instance;
+    guint max_width;
+};
+
+G_DEFINE_TYPE (NautilusContainerMaxWidth, nautilus_container_max_width, GTK_TYPE_BIN)
+
+enum
+{
+    PROP_0,
+    PROP_MAX_WIDTH,
+    N_PROPS
+};
+
+void
+nautilus_container_max_width_set_max_width (NautilusContainerMaxWidth *self,
+                                            guint                      max_width)
+{
+    self->max_width = max_width;
+    gtk_widget_queue_allocate (GTK_WIDGET (self));
+}
+
+guint
+nautilus_container_max_width_get_max_width (NautilusContainerMaxWidth *self)
+{
+    return self->max_width;
+}
+
+NautilusContainerMaxWidth *
+nautilus_container_max_width_new (void)
+{
+    return g_object_new (NAUTILUS_TYPE_CONTAINER_MAX_WIDTH, NULL);
+}
+
+static void
+nautilus_container_max_width_finalize (GObject *object)
+{
+    G_OBJECT_CLASS (nautilus_container_max_width_parent_class)->finalize (object);
+}
+
+static void
+nautilus_container_max_width_get_property (GObject    *object,
+                                           guint       prop_id,
+                                           GValue     *value,
+                                           GParamSpec *pspec)
+{
+    NautilusContainerMaxWidth *self = NAUTILUS_CONTAINER_MAX_WIDTH (object);
+
+    switch (prop_id)
+    {
+        case PROP_MAX_WIDTH:
+        {
+            g_value_set_int (value, self->max_width);
+        }
+
+        default:
+        {
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        }
+        break;
+    }
+}
+
+static void
+nautilus_container_max_width_set_property (GObject      *object,
+                                           guint         prop_id,
+                                           const GValue *value,
+                                           GParamSpec   *pspec)
+{
+    NautilusContainerMaxWidth *self = NAUTILUS_CONTAINER_MAX_WIDTH (object);
+
+    switch (prop_id)
+    {
+        case PROP_MAX_WIDTH:
+        {
+            nautilus_container_max_width_set_max_width (self, g_value_get_int (value));
+        }
+        break;
+
+        default:
+        {
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        }
+        break;
+    }
+}
+
+static void
+get_preferred_width (GtkWidget *widget,
+                     gint      *minimum_size,
+                     gint      *natural_size)
+{
+    GtkWidget *child;
+    NautilusContainerMaxWidth *self;
+    GtkStyleContext *style_context;
+    GtkBorder padding;
+
+    self = NAUTILUS_CONTAINER_MAX_WIDTH (widget);
+    child = gtk_bin_get_child (GTK_BIN (self));
+
+    *natural_size = 0;
+    *minimum_size = 0;
+    gtk_widget_get_preferred_width (child, minimum_size, natural_size);
+
+    *minimum_size = self->max_width == -1 ? *minimum_size : 96;
+    *natural_size = self->max_width == -1 ? *natural_size :
+                    MAX (*minimum_size, MIN (self->max_width, *natural_size));
+
+    style_context = gtk_widget_get_style_context (child);
+    gtk_style_context_get_padding (style_context,
+                                   gtk_widget_get_state_flags (child),
+                                   &padding);
+    *minimum_size += padding.left + padding.right;
+    *natural_size += padding.left + padding.right;
+}
+
+static void
+get_preferred_height (GtkWidget *widget,
+                      gint      *minimum_size,
+                      gint      *natural_size)
+{
+    GtkWidget *child;
+    NautilusContainerMaxWidth *self;
+    gint minimum_width = 0;
+    gint natural_width = 0;
+    GtkStyleContext *style_context;
+    GtkBorder padding;
+
+    self = NAUTILUS_CONTAINER_MAX_WIDTH (widget);
+    child = gtk_bin_get_child (GTK_BIN (self));
+
+    get_preferred_width (widget, &minimum_width, &natural_width);
+    natural_width = self->max_width == -1 ? natural_width : MIN (self->max_width, natural_width);
+
+    gtk_widget_get_preferred_height_for_width (child, natural_width, minimum_size, natural_size);
+
+    style_context = gtk_widget_get_style_context (child);
+    gtk_style_context_get_padding (style_context,
+                                   gtk_widget_get_state_flags (child),
+                                   &padding);
+    *minimum_size += padding.top + padding.bottom;
+    *natural_size += padding.top + padding.bottom;
+}
+
+static void
+get_preferred_height_for_width (GtkWidget *widget,
+                                gint       width,
+                                gint      *minimum_size,
+                                gint      *natural_size)
+{
+    get_preferred_height (widget, minimum_size, natural_size);
+}
+
+static void
+size_allocate (GtkWidget     *widget,
+               GtkAllocation *allocation)
+{
+    GTK_WIDGET_CLASS (nautilus_container_max_width_parent_class)->size_allocate (widget, allocation);
+}
+
+static void
+get_preferred_width_for_height (GtkWidget *widget,
+                                gint       height,
+                                gint      *minimum_size,
+                                gint      *natural_size)
+{
+    get_preferred_width (widget, minimum_size, natural_size);
+}
+
+static void
+constructed (GObject *obj)
+{
+    NautilusContainerMaxWidth *self = NAUTILUS_CONTAINER_MAX_WIDTH (obj);
+
+    G_OBJECT_CLASS (nautilus_container_max_width_parent_class)->constructed (obj);
+
+    /* We want our parent to gives our preferred width */
+    gtk_widget_set_halign (GTK_WIDGET (self), GTK_ALIGN_CENTER);
+    self->max_width = -1;
+}
+
+static void
+nautilus_container_max_width_class_init (NautilusContainerMaxWidthClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS (klass);
+    GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+    object_class->finalize = nautilus_container_max_width_finalize;
+    object_class->get_property = nautilus_container_max_width_get_property;
+    object_class->set_property = nautilus_container_max_width_set_property;
+    object_class->constructed = constructed;
+
+    widget_class->get_preferred_width = get_preferred_width;
+    widget_class->get_preferred_width_for_height = get_preferred_width_for_height;
+    widget_class->get_preferred_height = get_preferred_height;
+    widget_class->get_preferred_height_for_width = get_preferred_height_for_width;
+    widget_class->size_allocate = size_allocate;
+
+    g_object_class_install_property (object_class,
+                                     PROP_MAX_WIDTH,
+                                     g_param_spec_int ("max-width",
+                                                       "Max width",
+                                                       "The max width of the container",
+                                                       G_MININT,
+                                                       G_MAXINT,
+                                                       0,
+                                                       G_PARAM_READWRITE));
+}
+
+static void
+nautilus_container_max_width_init (NautilusContainerMaxWidth *self)
+{
+}
diff --git a/src/nautilus-container-max-width.h b/src/nautilus-container-max-width.h
new file mode 100644
index 0000000..935721d
--- /dev/null
+++ b/src/nautilus-container-max-width.h
@@ -0,0 +1,22 @@
+#ifndef NAUTILUS_CONTAINER_MAX_WIDTH_H
+#define NAUTILUS_CONTAINER_MAX_WIDTH_H
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define NAUTILUS_TYPE_CONTAINER_MAX_WIDTH (nautilus_container_max_width_get_type())
+
+G_DECLARE_FINAL_TYPE (NautilusContainerMaxWidth, nautilus_container_max_width, NAUTILUS, 
CONTAINER_MAX_WIDTH, GtkBin)
+
+NautilusContainerMaxWidth *nautilus_container_max_width_new (void);
+
+void nautilus_container_max_width_set_max_width (NautilusContainerMaxWidth *self,
+                                                 guint                      max_width);
+guint nautilus_container_max_width_get_max_width (NautilusContainerMaxWidth *self);
+
+G_END_DECLS
+
+#endif /* NAUTILUS_CONTAINER_MAX_WIDTH_H */
+
diff --git a/src/nautilus-file.c b/src/nautilus-file.c
index 919e763..b289487 100644
--- a/src/nautilus-file.c
+++ b/src/nautilus-file.c
@@ -5361,13 +5361,20 @@ nautilus_file_get_thumbnail_icon (NautilusFile          *file,
             /* We don't want frames around small icons */
             if (!gdk_pixbuf_get_has_alpha (file->details->thumbnail) || s >= 128 * scale)
             {
-                if (nautilus_is_video_file (file))
-                {
-                    nautilus_ui_frame_video (&pixbuf);
-                }
-                else
+                gboolean use_experimental_views;
+
+                use_experimental_views = g_settings_get_boolean (nautilus_preferences,
+                                                                 
NAUTILUS_PREFERENCES_USE_EXPERIMENTAL_VIEWS);
+                if (!use_experimental_views)
                 {
-                    nautilus_ui_frame_image (&pixbuf);
+                    if (nautilus_is_video_file (file))
+                    {
+                        nautilus_ui_frame_video (&pixbuf);
+                    }
+                    else
+                    {
+                        nautilus_ui_frame_image (&pixbuf);
+                    }
                 }
             }
 
diff --git a/src/nautilus-files-view.c b/src/nautilus-files-view.c
index 5ae94bb..3fdb70e 100644
--- a/src/nautilus-files-view.c
+++ b/src/nautilus-files-view.c
@@ -35,6 +35,7 @@
 #include "nautilus-error-reporting.h"
 #include "nautilus-file-undo-manager.h"
 #include "nautilus-floating-bar.h"
+#include "nautilus-view-icon-controller.h"
 #include "nautilus-list-view.h"
 #include "nautilus-canvas-view.h"
 #include "nautilus-mime-actions.h"
@@ -130,10 +131,9 @@
 
 #define MIN_COMMON_FILENAME_PREFIX_LENGTH 4
 
-
 enum
 {
-    ADD_FILE,
+    ADD_FILES,
     BEGIN_FILE_CHANGES,
     BEGIN_LOADING,
     CLEAR,
@@ -3632,35 +3632,39 @@ debuting_files_data_free (DebutingFilesData *data)
  * it selects and reveals them all.
  */
 static void
-debuting_files_add_file_callback (NautilusFilesView *view,
-                                  NautilusFile      *new_file,
-                                  NautilusDirectory *directory,
-                                  DebutingFilesData *data)
+debuting_files_add_files_callback (NautilusFilesView *view,
+                                   GList             *new_files,
+                                   NautilusDirectory *directory,
+                                   DebutingFilesData *data)
 {
     GFile *location;
+    GList *l;
 
     nautilus_profile_start (NULL);
 
-    location = nautilus_file_get_location (new_file);
-
-    if (g_hash_table_remove (data->debuting_files, location))
+    for (l = new_files; l != NULL; l = l->next)
     {
-        nautilus_file_ref (new_file);
-        data->added_files = g_list_prepend (data->added_files, new_file);
+        location = nautilus_file_get_location (NAUTILUS_FILE (l->data));
 
-        if (g_hash_table_size (data->debuting_files) == 0)
+        if (g_hash_table_remove (data->debuting_files, location))
         {
-            nautilus_files_view_call_set_selection (view, data->added_files);
-            nautilus_files_view_reveal_selection (view);
-            g_signal_handlers_disconnect_by_func (view,
-                                                  G_CALLBACK (debuting_files_add_file_callback),
-                                                  data);
+            nautilus_file_ref (NAUTILUS_FILE (l->data));
+            data->added_files = g_list_prepend (data->added_files, NAUTILUS_FILE (l->data));
+
         }
+        g_object_unref (location);
     }
 
-    nautilus_profile_end (NULL);
+    if (g_hash_table_size (data->debuting_files) == 0)
+    {
+        nautilus_files_view_call_set_selection (view, data->added_files);
+        nautilus_files_view_reveal_selection (view);
+        g_signal_handlers_disconnect_by_func (view,
+                                              G_CALLBACK (debuting_files_add_files_callback),
+                                              data);
+    }
 
-    g_object_unref (location);
+    nautilus_profile_end (NULL);
 }
 
 typedef struct
@@ -3685,13 +3689,18 @@ copy_move_done_data_free (CopyMoveDoneData *data)
 }
 
 static void
-pre_copy_move_add_file_callback (NautilusFilesView *view,
-                                 NautilusFile      *new_file,
+pre_copy_move_add_files_callback (NautilusFilesView *view,
+                                 GList             *new_files,
                                  NautilusDirectory *directory,
                                  CopyMoveDoneData  *data)
 {
-    nautilus_file_ref (new_file);
-    data->added_files = g_list_prepend (data->added_files, new_file);
+    GList *l;
+
+    for (l = new_files; l != NULL; l = l->next)
+    {
+        nautilus_file_ref (NAUTILUS_FILE (l->data));
+        data->added_files = g_list_prepend (data->added_files, l->data);
+    }
 }
 
 /* This needs to be called prior to nautilus_file_operations_copy_move.
@@ -3711,11 +3720,11 @@ pre_copy_move (NautilusFilesView *directory_view)
                                (gpointer *) &copy_move_done_data->directory_view);
 
     /* We need to run after the default handler adds the folder we want to
-     * operate on. The ADD_FILE signal is registered as G_SIGNAL_RUN_LAST, so we
+     * operate on. The ADD_FILES signal is registered as G_SIGNAL_RUN_LAST, so we
      * must use connect_after.
      */
-    g_signal_connect (directory_view, "add-file",
-                      G_CALLBACK (pre_copy_move_add_file_callback), copy_move_done_data);
+    g_signal_connect (directory_view, "add-files",
+                      G_CALLBACK (pre_copy_move_add_files_callback), copy_move_done_data);
 
     return copy_move_done_data;
 }
@@ -3791,17 +3800,17 @@ copy_move_done_callback (GHashTable *debuting_files,
         nautilus_file_list_free (copy_move_done_data->added_files);
         copy_move_done_data->added_files = failed_files;
 
-        /* We're passed the same data used by pre_copy_move_add_file_callback, so disconnecting
+        /* We're passed the same data used by pre_copy_move_add_files_callback, so disconnecting
          * it will free data. We've already siphoned off the added_files we need, and stashed the
          * directory_view pointer.
          */
         g_signal_handlers_disconnect_by_func (directory_view,
-                                              G_CALLBACK (pre_copy_move_add_file_callback),
+                                              G_CALLBACK (pre_copy_move_add_files_callback),
                                               data);
 
         /* Any items in the debuting_files hash table that have
          * "FALSE" as their value aren't really being copied
-         * or moved, so we can't wait for an add_file signal
+         * or moved, so we can't wait for an add_files signal
          * to come in for those.
          */
         g_hash_table_foreach_remove (debuting_files,
@@ -3822,12 +3831,12 @@ copy_move_done_callback (GHashTable *debuting_files,
         else
         {
             /* We need to run after the default handler adds the folder we want to
-             * operate on. The ADD_FILE signal is registered as G_SIGNAL_RUN_LAST, so we
+             * operate on. The ADD_FILES signal is registered as G_SIGNAL_RUN_LAST, so we
              * must use connect_after.
              */
             g_signal_connect_data (directory_view,
-                                   "add-file",
-                                   G_CALLBACK (debuting_files_add_file_callback),
+                                   "add-files",
+                                   G_CALLBACK (debuting_files_add_files_callback),
                                    debuting_files_data,
                                    (GClosureNotify) debuting_files_data_free,
                                    G_CONNECT_AFTER);
@@ -4055,6 +4064,7 @@ process_old_files (NautilusFilesView *view)
     GList *files_added, *files_changed, *node;
     FileAndDirectory *pending;
     GList *selection, *files;
+    g_autoptr (GList) pending_additions = NULL;
 
     priv = nautilus_files_view_get_instance_private (view);
     files_added = priv->old_added_files;
@@ -4070,8 +4080,7 @@ process_old_files (NautilusFilesView *view)
         for (node = files_added; node != NULL; node = node->next)
         {
             pending = node->data;
-            g_signal_emit (view,
-                           signals[ADD_FILE], 0, pending->file, pending->directory);
+            pending_additions = g_list_prepend (pending_additions, pending->file);
             /* Acknowledge the files that were pending to be revealed */
             if (g_hash_table_contains (priv->pending_reveal, pending->file))
             {
@@ -4081,6 +4090,12 @@ process_old_files (NautilusFilesView *view)
             }
         }
 
+        if (files_added != NULL)
+        {
+            g_signal_emit (view,
+                           signals[ADD_FILES], 0, pending_additions, pending->directory);
+        }
+
         for (node = files_changed; node != NULL; node = node->next)
         {
             gboolean should_show_file;
@@ -8013,21 +8028,21 @@ nautilus_files_view_reset_view_menu (NautilusFilesView *view)
 {
     NautilusFilesViewPrivate *priv;
     GActionGroup *view_action_group;
-    GVariant *variant;
-    GVariantIter iter;
-    gboolean show_sort_trash, show_sort_access, show_sort_modification, sort_available;
-    const gchar *hint;
+    gboolean sort_available;
     g_autofree gchar *zoom_level_percent = NULL;
+    NautilusFile *file;
 
     view_action_group = nautilus_files_view_get_action_group (view);
     priv = nautilus_files_view_get_instance_private (view);
+    file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (view));
 
     gtk_widget_set_visible (priv->visible_columns,
                             g_action_group_has_action (view_action_group, "visible-columns"));
 
     sort_available = g_action_group_get_action_enabled (view_action_group, "sort");
-    show_sort_trash = show_sort_modification = show_sort_access = FALSE;
     gtk_widget_set_visible (priv->sort_menu, sort_available);
+    gtk_widget_set_visible (priv->sort_trash_time,
+                            nautilus_file_is_in_trash (file));
 
     /* We want to make insensitive available actions but that are not current
      * available due to the directory
@@ -8037,24 +8052,6 @@ nautilus_files_view_reset_view_menu (NautilusFilesView *view)
     gtk_widget_set_sensitive (priv->zoom_controls_box,
                               !nautilus_files_view_is_empty (view));
 
-    if (sort_available)
-    {
-        variant = g_action_group_get_action_state_hint (view_action_group, "sort");
-        g_variant_iter_init (&iter, variant);
-
-        while (g_variant_iter_next (&iter, "&s", &hint))
-        {
-            if (g_strcmp0 (hint, "trash-time") == 0)
-            {
-                show_sort_trash = TRUE;
-            }
-        }
-
-        g_variant_unref (variant);
-    }
-
-    gtk_widget_set_visible (priv->sort_trash_time, show_sort_trash);
-
     zoom_level_percent = g_strdup_printf ("%.0f%%", nautilus_files_view_get_zoom_level_percentage (view) * 
100.0);
     gtk_label_set_label (GTK_LABEL (priv->zoom_level_label), zoom_level_percent);
 }
@@ -9331,14 +9328,14 @@ nautilus_files_view_class_init (NautilusFilesViewClass *klass)
     widget_class->grab_focus = nautilus_files_view_grab_focus;
 
 
-    signals[ADD_FILE] =
-        g_signal_new ("add-file",
+    signals[ADD_FILES] =
+        g_signal_new ("add-files",
                       G_TYPE_FROM_CLASS (klass),
                       G_SIGNAL_RUN_LAST,
-                      G_STRUCT_OFFSET (NautilusFilesViewClass, add_file),
+                      G_STRUCT_OFFSET (NautilusFilesViewClass, add_files),
                       NULL, NULL,
                       g_cclosure_marshal_generic,
-                      G_TYPE_NONE, 2, NAUTILUS_TYPE_FILE, NAUTILUS_TYPE_DIRECTORY);
+                      G_TYPE_NONE, 2, G_TYPE_POINTER, NAUTILUS_TYPE_DIRECTORY);
     signals[BEGIN_FILE_CHANGES] =
         g_signal_new ("begin-file-changes",
                       G_TYPE_FROM_CLASS (klass),
@@ -9693,12 +9690,22 @@ nautilus_files_view_new (guint               id,
                          NautilusWindowSlot *slot)
 {
     NautilusFilesView *view = NULL;
+    gboolean use_experimental_views;
 
+    use_experimental_views = g_settings_get_boolean (nautilus_preferences,
+                                                     NAUTILUS_PREFERENCES_USE_EXPERIMENTAL_VIEWS);
     switch (id)
     {
         case NAUTILUS_VIEW_GRID_ID:
         {
-            view = nautilus_canvas_view_new (slot);
+            if (use_experimental_views)
+            {
+                view = NAUTILUS_FILES_VIEW (nautilus_view_icon_controller_new (slot));
+            }
+            else
+            {
+                view = nautilus_canvas_view_new (slot);
+            }
         }
         break;
 
diff --git a/src/nautilus-files-view.h b/src/nautilus-files-view.h
index 6e3cd5d..7bf0521 100644
--- a/src/nautilus-files-view.h
+++ b/src/nautilus-files-view.h
@@ -57,12 +57,12 @@ struct _NautilusFilesViewClass {
          */
         void         (* begin_file_changes)     (NautilusFilesView *view);
 
-        /* The 'add_file' signal is emitted to add one file to the view.
+        /* The 'add_files' signal is emitted to add a set of files to the view.
          * It must be replaced by each subclass.
          */
-        void    (* add_file)                    (NautilusFilesView *view,
-                                                 NautilusFile      *file,
-                                                 NautilusDirectory *directory);
+        void    (* add_files)                    (NautilusFilesView *view,
+                                                  GList             *files,
+                                                  NautilusDirectory *directory);
         void    (* remove_file)                 (NautilusFilesView *view,
                                                  NautilusFile      *file,
                                                  NautilusDirectory *directory);
@@ -324,8 +324,6 @@ char *            nautilus_files_view_get_title                  (NautilusFilesV
 gboolean          nautilus_files_view_supports_zooming           (NautilusFilesView      *view);
 void              nautilus_files_view_bump_zoom_level            (NautilusFilesView      *view,
                                                                   int                     zoom_increment);
-void              nautilus_files_view_zoom_to_level              (NautilusFilesView      *view,
-                                                                  gint                    level);
 gboolean          nautilus_files_view_can_zoom_in                (NautilusFilesView      *view);
 gboolean          nautilus_files_view_can_zoom_out               (NautilusFilesView      *view);
 void              nautilus_files_view_update_menus               (NautilusFilesView      *view);
diff --git a/src/nautilus-global-preferences.h b/src/nautilus-global-preferences.h
index bf1c896..7e52f6c 100644
--- a/src/nautilus-global-preferences.h
+++ b/src/nautilus-global-preferences.h
@@ -93,6 +93,9 @@ typedef enum
 /* Icon View */
 #define NAUTILUS_PREFERENCES_ICON_VIEW_DEFAULT_ZOOM_LEVEL              "default-zoom-level"
 
+/* Experimental views */
+#define NAUTILUS_PREFERENCES_USE_EXPERIMENTAL_VIEWS "use-experimental-views"
+
 /* Which text attributes appear beneath icon names */
 #define NAUTILUS_PREFERENCES_ICON_VIEW_CAPTIONS                                "captions"
 
diff --git a/src/nautilus-list-view.c b/src/nautilus-list-view.c
index 38cd3a8..2d4a34a 100644
--- a/src/nautilus-list-view.c
+++ b/src/nautilus-list-view.c
@@ -2062,14 +2062,18 @@ create_and_set_up_tree_view (NautilusListView *view)
 }
 
 static void
-nautilus_list_view_add_file (NautilusFilesView *view,
-                             NautilusFile      *file,
-                             NautilusDirectory *directory)
+nautilus_list_view_add_files (NautilusFilesView *view,
+                              GList      *files,
+                              NautilusDirectory *directory)
 {
     NautilusListModel *model;
+    GList *l;
 
     model = NAUTILUS_LIST_VIEW (view)->details->model;
-    nautilus_list_model_add_file (model, file, directory);
+    for (l = files; l != NULL; l = l->next)
+    {
+        nautilus_list_model_add_file (model, NAUTILUS_FILE (l->data), directory);
+    }
 }
 
 static char **
@@ -3496,7 +3500,7 @@ nautilus_list_view_class_init (NautilusListViewClass *class)
     G_OBJECT_CLASS (class)->dispose = nautilus_list_view_dispose;
     G_OBJECT_CLASS (class)->finalize = nautilus_list_view_finalize;
 
-    nautilus_files_view_class->add_file = nautilus_list_view_add_file;
+    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;
diff --git a/src/nautilus-view-icon-controller.c b/src/nautilus-view-icon-controller.c
new file mode 100644
index 0000000..ae8040e
--- /dev/null
+++ b/src/nautilus-view-icon-controller.c
@@ -0,0 +1,876 @@
+#include "nautilus-view-icon-controller.h"
+#include "nautilus-view-icon-ui.h"
+#include "nautilus-view-item-model.h"
+#include "nautilus-view-icon-item-ui.h"
+#include "nautilus-view-model.h"
+#include "nautilus-files-view.h"
+#include "nautilus-file.h"
+#include "nautilus-metadata.h"
+#include "nautilus-window-slot.h"
+#include "nautilus-directory.h"
+#include "nautilus-global-preferences.h"
+
+struct _NautilusViewIconController
+{
+    NautilusFilesView parent_instance;
+
+    NautilusViewIconUi *view_ui;
+    NautilusViewModel *model;
+
+    GIcon *view_icon;
+    GActionGroup *action_group;
+    gint zoom_level;
+};
+
+G_DEFINE_TYPE (NautilusViewIconController, nautilus_view_icon_controller, NAUTILUS_TYPE_FILES_VIEW)
+
+typedef struct
+{
+    const NautilusFileSortType sort_type;
+    const gchar *metadata_name;
+    const gchar *action_target_name;
+    gboolean reversed;
+} SortConstants;
+
+static const SortConstants sorts_constants[] =
+{
+    {
+        NAUTILUS_FILE_SORT_BY_DISPLAY_NAME,
+        "name",
+        "name",
+        FALSE,
+    },
+    {
+        NAUTILUS_FILE_SORT_BY_DISPLAY_NAME,
+        "name",
+        "name-desc",
+        TRUE,
+    },
+    {
+        NAUTILUS_FILE_SORT_BY_SIZE,
+        "size",
+        "size",
+        TRUE,
+    },
+    {
+        NAUTILUS_FILE_SORT_BY_TYPE,
+        "type",
+        "type",
+        FALSE,
+    },
+    {
+        NAUTILUS_FILE_SORT_BY_MTIME,
+        "modification date",
+        "modification-date",
+        FALSE,
+    },
+    {
+        NAUTILUS_FILE_SORT_BY_MTIME,
+        "modification date",
+        "modification-date-desc",
+        TRUE,
+    },
+    {
+        NAUTILUS_FILE_SORT_BY_ATIME,
+        "access date",
+        "access-date",
+        FALSE,
+    },
+    {
+        NAUTILUS_FILE_SORT_BY_ATIME,
+        "access date",
+        "access-date-desc",
+        TRUE,
+    },
+    {
+        NAUTILUS_FILE_SORT_BY_TRASHED_TIME,
+        "trashed",
+        "trash-time",
+        TRUE,
+    },
+    {
+        NAUTILUS_FILE_SORT_BY_SEARCH_RELEVANCE,
+        NULL,
+        "search-relevance",
+        TRUE,
+    }
+};
+
+static guint get_icon_size_for_zoom_level (NautilusCanvasZoomLevel zoom_level);
+
+static const SortConstants *
+get_sorts_constants_from_action_target_name (const gchar *action_target_name)
+{
+    int i;
+
+    for (i = 0; i < G_N_ELEMENTS (sorts_constants); i++)
+    {
+        if (g_strcmp0 (sorts_constants[i].action_target_name, action_target_name) == 0)
+        {
+            return &sorts_constants[i];
+        }
+    }
+
+    return &sorts_constants[0];
+}
+
+static const SortConstants *
+get_sorts_constants_from_sort_type (NautilusFileSortType sort_type,
+                                    gboolean             reversed)
+{
+    guint i;
+
+    for (i = 0; i < G_N_ELEMENTS (sorts_constants); i++)
+    {
+        if (sort_type == sorts_constants[i].sort_type
+            && reversed == sorts_constants[i].reversed)
+        {
+            return &sorts_constants[i];
+        }
+    }
+
+    return &sorts_constants[0];
+}
+
+static const SortConstants *
+get_sorts_constants_from_metadata_text (const char *metadata_name,
+                                        gboolean    reversed)
+{
+    guint i;
+
+    for (i = 0; i < G_N_ELEMENTS (sorts_constants); i++)
+    {
+        if (g_strcmp0 (sorts_constants[i].metadata_name, metadata_name) == 0
+            && reversed == sorts_constants[i].reversed)
+        {
+            return &sorts_constants[i];
+        }
+    }
+
+    return &sorts_constants[0];
+}
+
+static const SortConstants *
+get_default_sort_order (NautilusFile *file)
+{
+    NautilusFileSortType sort_type;
+    NautilusFileSortType default_sort_order;
+    gboolean reversed;
+
+    default_sort_order = g_settings_get_enum (nautilus_preferences,
+                                              NAUTILUS_PREFERENCES_DEFAULT_SORT_ORDER);
+    reversed = g_settings_get_boolean (nautilus_preferences,
+                                       NAUTILUS_PREFERENCES_DEFAULT_SORT_IN_REVERSE_ORDER);
+
+    /* If this is a special folder (e.g. search or recent), override the sort
+     * order and reversed flag with values appropriate for the folder */
+    sort_type = nautilus_file_get_default_sort_type (file, &reversed);
+
+    if (sort_type == NAUTILUS_FILE_SORT_NONE)
+    {
+        sort_type = CLAMP (default_sort_order,
+                           NAUTILUS_FILE_SORT_BY_DISPLAY_NAME,
+                           NAUTILUS_FILE_SORT_BY_ATIME);
+    }
+
+    return get_sorts_constants_from_sort_type (sort_type, reversed);
+}
+
+static const SortConstants *
+get_directory_sort_by (NautilusFile *file)
+{
+    const SortConstants *default_sort;
+    g_autofree char *sort_by = NULL;
+    gboolean reversed;
+
+    default_sort = get_default_sort_order (file);
+    g_return_val_if_fail (default_sort != NULL, NULL);
+
+    sort_by = nautilus_file_get_metadata (file,
+                                          NAUTILUS_METADATA_KEY_ICON_VIEW_SORT_BY,
+                                          default_sort->metadata_name);
+
+    reversed = nautilus_file_get_boolean_metadata (file,
+                                                   NAUTILUS_METADATA_KEY_ICON_VIEW_SORT_REVERSED,
+                                                   default_sort->reversed);
+
+    return get_sorts_constants_from_metadata_text (sort_by, reversed);
+}
+
+static void
+set_directory_sort_metadata (NautilusFile        *file,
+                             const SortConstants *sort)
+{
+    const SortConstants *default_sort;
+
+    default_sort = get_default_sort_order (file);
+
+    nautilus_file_set_metadata (file,
+                                NAUTILUS_METADATA_KEY_ICON_VIEW_SORT_BY,
+                                default_sort->metadata_name,
+                                sort->metadata_name);
+    nautilus_file_set_boolean_metadata (file,
+                                        NAUTILUS_METADATA_KEY_ICON_VIEW_SORT_REVERSED,
+                                        default_sort->reversed,
+                                        sort->reversed);
+}
+
+static void
+update_sort_order_from_metadata_and_preferences (NautilusViewIconController *self)
+{
+    const SortConstants *default_directory_sort;
+    GActionGroup *view_action_group;
+
+    default_directory_sort = get_directory_sort_by (nautilus_files_view_get_directory_as_file 
(NAUTILUS_FILES_VIEW (self)));
+    view_action_group = nautilus_files_view_get_action_group (NAUTILUS_FILES_VIEW (self));
+    g_action_group_change_action_state (view_action_group,
+                                        "sort",
+                                        g_variant_new_string (get_sorts_constants_from_sort_type 
(default_directory_sort->sort_type, default_directory_sort->reversed)->action_target_name));
+}
+
+static void
+real_begin_loading (NautilusFilesView *files_view)
+{
+    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
+
+    /* TODO: This calls sort once, and update_context_menus calls update_actions which calls */
+    /* the action again */
+    update_sort_order_from_metadata_and_preferences (self);
+
+    /*TODO move this to the files view class begin_loading and hook up? */
+
+    /* We could have changed to the trash directory or to searching, and then
+     * we need to update the menus */
+    nautilus_files_view_update_context_menus (files_view);
+    nautilus_files_view_update_toolbar_menus (files_view);
+}
+
+static void
+real_clear (NautilusFilesView *files_view)
+{
+    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
+
+    g_list_store_remove_all (nautilus_view_model_get_g_model (self->model));
+}
+
+
+/* FIXME: ideally this should go into the model so there is not need to
+ * recreate the model with the new data */
+static void
+real_file_changed (NautilusFilesView *files_view,
+                   NautilusFile      *file,
+                   NautilusDirectory *directory)
+{
+    NautilusViewIconController *self;
+    NautilusViewItemModel *item_model;
+    NautilusViewItemModel *new_item_model;
+
+    self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
+    item_model = nautilus_view_model_get_item_from_file (self->model, file);
+    nautilus_view_model_remove_item (self->model, item_model);
+    new_item_model = nautilus_view_item_model_new (file,
+                                                   get_icon_size_for_zoom_level (self->zoom_level));
+    nautilus_view_model_add_item (self->model, new_item_model);
+}
+
+static GList *
+real_get_selection (NautilusFilesView *files_view)
+{
+    NautilusViewIconController *self;
+    GList *selected_files = NULL;
+    GList *l;
+    g_autoptr (GQueue) selected_items = NULL;
+
+    self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
+    selected_items = nautilus_view_model_get_selected (self->model);
+    for (l = g_queue_peek_head_link (selected_items); l != NULL; l = l->next)
+    {
+        selected_files = g_list_prepend (selected_files, l->data);
+    }
+
+    return selected_files;
+}
+
+
+static GList *
+real_get_selection_for_file_transfer (NautilusFilesView *files_view)
+{
+    return NULL;
+}
+
+static gboolean
+real_is_empty (NautilusFilesView *files_view)
+{
+    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
+
+    return g_list_model_get_n_items (G_LIST_MODEL (nautilus_view_model_get_g_model (self->model))) == 0;
+}
+
+static void
+real_end_file_changes (NautilusFilesView *files_view)
+{
+}
+
+static void
+real_remove_file (NautilusFilesView *files_view,
+                  NautilusFile      *file,
+                  NautilusDirectory *directory)
+{
+    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
+    NautilusFile *current_file;
+    NautilusViewItemModel *current_item_model;
+    guint i = 0;
+
+    while ((current_item_model = NAUTILUS_VIEW_ITEM_MODEL (g_list_model_get_item (G_LIST_MODEL 
(nautilus_view_model_get_g_model (self->model)), i))))
+    {
+        current_file = nautilus_view_item_model_get_file (current_item_model);
+        if (current_file == file)
+        {
+            g_list_store_remove (nautilus_view_model_get_g_model (self->model), i);
+            break;
+        }
+        i++;
+    }
+}
+
+static GQueue *
+convert_glist_to_queue (GList *list)
+{
+    GList *l;
+    GQueue *queue;
+
+    queue = g_queue_new ();
+    for (l = list; l != NULL; l = l->next)
+    {
+        g_queue_push_tail (queue, l->data);
+    }
+
+    return queue;
+}
+
+static GQueue *
+convert_files_to_item_models (NautilusViewIconController *self,
+                              GQueue                     *files)
+{
+    GList *l;
+    GQueue *models;
+
+    models = g_queue_new ();
+    for (l = g_queue_peek_head_link (files); l != NULL; l = l->next)
+    {
+        NautilusViewItemModel *item_model;
+
+        item_model = nautilus_view_item_model_new (NAUTILUS_FILE (l->data),
+                                                   get_icon_size_for_zoom_level (self->zoom_level));
+        g_queue_push_tail (models, item_model);
+    }
+
+    return models;
+}
+
+static void
+real_set_selection (NautilusFilesView *files_view,
+                    GList             *selection)
+{
+    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
+    g_autoptr (GQueue) selection_files = NULL;
+    g_autoptr (GQueue) selection_item_models = NULL;
+
+    selection_files = convert_glist_to_queue (selection);
+    selection_item_models = nautilus_view_model_get_items_from_files (self->model, selection_files);
+    nautilus_view_model_set_selected (self->model, selection_item_models);
+    nautilus_files_view_notify_selection_changed (files_view);
+}
+
+static void
+real_select_all (NautilusFilesView *files_view)
+{
+    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
+    gtk_flow_box_select_all (GTK_FLOW_BOX (self->view_ui));
+}
+
+static void
+real_reveal_selection (NautilusFilesView *files_view)
+{
+}
+
+static gboolean
+showing_recent_directory (NautilusFilesView *view)
+{
+    NautilusFile *file;
+
+    file = nautilus_files_view_get_directory_as_file (view);
+    if (file != NULL)
+    {
+        return nautilus_file_is_in_recent (file);
+    }
+    return FALSE;
+}
+
+static gboolean
+showing_search_directory (NautilusFilesView *view)
+{
+    NautilusFile *file;
+
+    file = nautilus_files_view_get_directory_as_file (view);
+    if (file != NULL)
+    {
+        return nautilus_file_is_in_search (file);
+    }
+    return FALSE;
+}
+
+static void
+real_update_actions_state (NautilusFilesView *files_view)
+{
+    GAction *action;
+    GActionGroup *view_action_group;
+
+    NAUTILUS_FILES_VIEW_CLASS (nautilus_view_icon_controller_parent_class)->update_actions_state 
(files_view);
+
+    view_action_group = nautilus_files_view_get_action_group (files_view);
+    action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group), "sort");
+    g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
+                                 !showing_recent_directory (files_view) &&
+                                 !showing_search_directory (files_view));
+}
+
+static void
+real_bump_zoom_level (NautilusFilesView *files_view,
+                      int                zoom_increment)
+{
+    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
+    NautilusCanvasZoomLevel new_level;
+
+    new_level = self->zoom_level + zoom_increment;
+
+    if (new_level >= NAUTILUS_CANVAS_ZOOM_LEVEL_SMALL &&
+        new_level <= NAUTILUS_CANVAS_ZOOM_LEVEL_LARGER)
+    {
+        g_action_group_change_action_state (self->action_group,
+                                            "zoom-to-level",
+                                            g_variant_new_int32 (new_level));
+    }
+}
+
+static guint
+get_icon_size_for_zoom_level (NautilusCanvasZoomLevel zoom_level)
+{
+    switch (zoom_level)
+    {
+        case NAUTILUS_CANVAS_ZOOM_LEVEL_SMALL:
+        {
+            return NAUTILUS_CANVAS_ICON_SIZE_SMALL;
+        }
+        break;
+
+        case NAUTILUS_CANVAS_ZOOM_LEVEL_STANDARD:
+        {
+            return NAUTILUS_CANVAS_ICON_SIZE_STANDARD;
+        }
+        break;
+
+        case NAUTILUS_CANVAS_ZOOM_LEVEL_LARGE:
+        {
+            return NAUTILUS_CANVAS_ICON_SIZE_LARGE;
+        }
+        break;
+
+        case NAUTILUS_CANVAS_ZOOM_LEVEL_LARGER:
+        {
+            return NAUTILUS_CANVAS_ICON_SIZE_LARGER;
+        }
+        break;
+    }
+    g_return_val_if_reached (NAUTILUS_CANVAS_ICON_SIZE_STANDARD);
+}
+
+static gint
+get_default_zoom_level ()
+{
+    NautilusCanvasZoomLevel default_zoom_level;
+
+    default_zoom_level = g_settings_get_enum (nautilus_icon_view_preferences,
+                                              NAUTILUS_PREFERENCES_ICON_VIEW_DEFAULT_ZOOM_LEVEL);
+
+    return default_zoom_level;
+}
+
+static void
+set_icon_size (NautilusViewIconController *self,
+               gint                        icon_size)
+{
+    NautilusViewItemModel *current_item_model;
+    guint i = 0;
+
+    while ((current_item_model = NAUTILUS_VIEW_ITEM_MODEL (g_list_model_get_item (G_LIST_MODEL 
(nautilus_view_model_get_g_model (self->model)), i))))
+    {
+        nautilus_view_item_model_set_icon_size (current_item_model,
+                                                get_icon_size_for_zoom_level (self->zoom_level));
+        i++;
+    }
+}
+
+static void
+set_zoom_level (NautilusViewIconController *self,
+                guint                       new_level)
+{
+    self->zoom_level = new_level;
+
+    set_icon_size (self, get_icon_size_for_zoom_level (new_level));
+
+    nautilus_files_view_update_toolbar_menus (NAUTILUS_FILES_VIEW (self));
+}
+
+static void
+real_restore_standard_zoom_level (NautilusFilesView *files_view)
+{
+    NautilusViewIconController *self;
+
+    self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
+    g_action_group_change_action_state (self->action_group,
+                                        "zoom-to-level",
+                                        g_variant_new_int32 (NAUTILUS_CANVAS_ZOOM_LEVEL_LARGE));
+}
+
+static gfloat
+real_get_zoom_level_percentage (NautilusFilesView *files_view)
+{
+    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
+
+    return (gfloat) get_icon_size_for_zoom_level (self->zoom_level) /
+           NAUTILUS_CANVAS_ICON_SIZE_LARGE;
+}
+
+static gboolean
+real_can_zoom_in (NautilusFilesView *files_view)
+{
+    return TRUE;
+}
+
+static gboolean
+real_can_zoom_out (NautilusFilesView *files_view)
+{
+    return TRUE;
+}
+
+static GdkRectangle *
+real_compute_rename_popover_pointing_to (NautilusFilesView *files_view)
+{
+    NautilusViewIconController *self;
+    GdkRectangle *allocation;
+    GtkAdjustment *vadjustment;
+    GtkAdjustment *hadjustment;
+    GtkWidget *parent_container;
+    g_autoptr (GQueue) selection_files = NULL;
+    g_autoptr (GQueue) selection_item_models = NULL;
+    GList *selection;
+    GtkWidget *icon_item_ui;
+
+    self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
+    allocation = g_new0 (GdkRectangle, 1);
+
+    parent_container = nautilus_files_view_get_content_widget (files_view);
+    vadjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (parent_container));
+    hadjustment = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (parent_container));
+    selection = nautilus_view_get_selection (NAUTILUS_VIEW (files_view));
+    selection_files = convert_glist_to_queue (selection);
+    selection_item_models = nautilus_view_model_get_items_from_files (self->model, selection_files);
+    /* We only allow one item to be renamed with a popover */
+    icon_item_ui = nautilus_view_item_model_get_item_ui (g_queue_peek_head (selection_item_models));
+    gtk_widget_get_allocation (icon_item_ui, allocation);
+
+    allocation->x -= gtk_adjustment_get_value (hadjustment);
+    allocation->y -= gtk_adjustment_get_value (vadjustment);
+
+    return allocation;
+}
+
+static void
+real_click_policy_changed (NautilusFilesView *files_view)
+{
+}
+
+static gboolean
+on_button_press_event (GtkWidget *widget,
+                       GdkEvent  *event,
+                       gpointer   user_data)
+{
+    NautilusViewIconController *self;
+    GList *selection;
+    GtkWidget *child_at_pos;
+    NautilusViewItemModel *item_model;
+    GdkEventButton *event_button;
+
+    self = NAUTILUS_VIEW_ICON_CONTROLLER (user_data);
+    event_button = (GdkEventButton *) event;
+
+    if ((event_button->button == GDK_BUTTON_SECONDARY))
+    {
+        /* Need to update the selection so the popup has the right actions enabled */
+        selection = nautilus_view_get_selection (NAUTILUS_VIEW (self));
+        child_at_pos = GTK_WIDGET (gtk_flow_box_get_child_at_pos (GTK_FLOW_BOX (self->view_ui),
+                                                                  event_button->x, event_button->y));
+        item_model = nautilus_view_icon_item_ui_get_model (NAUTILUS_VIEW_ICON_ITEM_UI (child_at_pos));
+        selection = g_list_prepend (selection, nautilus_view_item_model_get_file (item_model));
+        nautilus_view_set_selection (NAUTILUS_VIEW (self), selection);
+
+        nautilus_files_view_pop_up_selection_context_menu (NAUTILUS_FILES_VIEW (self),
+                                                           event_button);
+    }
+
+    return GDK_EVENT_STOP;
+}
+
+static int
+real_compare_files (NautilusFilesView *files_view,
+                    NautilusFile      *file1,
+                    NautilusFile      *file2)
+{
+    if (file1 < file2)
+    {
+        return -1;
+    }
+
+    if (file1 > file2)
+    {
+        return +1;
+    }
+
+    return 0;
+}
+
+static gboolean
+real_using_manual_layout (NautilusFilesView *files_view)
+{
+    return FALSE;
+}
+
+static void
+real_end_loading (NautilusFilesView *files_view,
+                  gboolean           all_files_seen)
+{
+}
+
+static char *
+real_get_first_visible_file (NautilusFilesView *files_view)
+{
+    return NULL;
+}
+
+static void
+real_scroll_to_file (NautilusFilesView *files_view,
+                     const char        *uri)
+{
+}
+
+static void
+real_sort_directories_first_changed (NautilusFilesView *files_view)
+{
+    NautilusViewModelSortData sort_data;
+    NautilusViewModelSortData *current_sort_data;
+    NautilusViewIconController *self;
+
+    self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
+    current_sort_data = nautilus_view_model_get_sort_type (self->model);
+    sort_data.sort_type = current_sort_data->sort_type;
+    sort_data.reversed = current_sort_data->reversed;
+    sort_data.directories_first = nautilus_files_view_should_sort_directories_first (NAUTILUS_FILES_VIEW 
(self));
+
+    nautilus_view_model_set_sort_type (self->model, &sort_data);
+}
+
+static void
+action_sort_order_changed (GSimpleAction *action,
+                           GVariant      *value,
+                           gpointer       user_data)
+{
+    const gchar *target_name;
+    const SortConstants *sorts_constants;
+    NautilusViewModelSortData sort_data;
+    NautilusViewIconController *self;
+
+    /* Don't resort if the action is in the same state as before */
+    if (g_strcmp0 (g_variant_get_string (value, NULL), g_variant_get_string (g_action_get_state (G_ACTION 
(action)), NULL)) == 0)
+    {
+        return;
+    }
+
+    self = NAUTILUS_VIEW_ICON_CONTROLLER (user_data);
+    target_name = g_variant_get_string (value, NULL);
+    sorts_constants = get_sorts_constants_from_action_target_name (target_name);
+    sort_data.sort_type = sorts_constants->sort_type;
+    sort_data.reversed = sorts_constants->reversed;
+    sort_data.directories_first = nautilus_files_view_should_sort_directories_first (NAUTILUS_FILES_VIEW 
(self));
+
+    nautilus_view_model_set_sort_type (self->model, &sort_data);
+    set_directory_sort_metadata (nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (self)),
+                                 sorts_constants);
+
+    g_simple_action_set_state (action, value);
+}
+
+static void
+real_add_files (NautilusFilesView *files_view,
+                GList             *files,
+                NautilusDirectory *directory)
+{
+    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
+    g_autoptr (GQueue) files_queue;
+    g_autoptr (GQueue) item_models;
+
+    files_queue = convert_glist_to_queue (files);
+    item_models = convert_files_to_item_models (self, files_queue);
+    nautilus_view_model_set_items (self->model, item_models);
+}
+
+
+static guint
+real_get_view_id (NautilusFilesView *files_view)
+{
+    return NAUTILUS_VIEW_GRID_ID;
+}
+
+static GIcon *
+real_get_icon (NautilusFilesView *files_view)
+{
+    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
+
+    return self->view_icon;
+}
+
+static void
+real_select_first (NautilusFilesView *files_view)
+{
+}
+
+static void
+action_zoom_to_level (GSimpleAction *action,
+                      GVariant      *state,
+                      gpointer       user_data)
+{
+    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (user_data);
+    int zoom_level;
+
+    zoom_level = g_variant_get_int32 (state);
+    set_zoom_level (self, zoom_level);
+    g_simple_action_set_state (G_SIMPLE_ACTION (action), state);
+
+    if (g_settings_get_enum (nautilus_icon_view_preferences,
+                             NAUTILUS_PREFERENCES_ICON_VIEW_DEFAULT_ZOOM_LEVEL) != zoom_level)
+    {
+        g_settings_set_enum (nautilus_icon_view_preferences,
+                             NAUTILUS_PREFERENCES_ICON_VIEW_DEFAULT_ZOOM_LEVEL,
+                             zoom_level);
+    }
+}
+
+static void
+finalize (GObject *object)
+{
+    G_OBJECT_CLASS (nautilus_view_icon_controller_parent_class)->finalize (object);
+}
+
+
+const GActionEntry view_icon_actions[] =
+{
+    { "sort", NULL, "s", "'invalid'", action_sort_order_changed },
+    { "zoom-to-level", NULL, NULL, "100", action_zoom_to_level }
+};
+
+static void
+constructed (GObject *object)
+{
+    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (object);
+    GtkWidget *content_widget;
+    GActionGroup *view_action_group;
+
+    self->model = nautilus_view_model_new ();
+    self->view_ui = nautilus_view_icon_ui_new (self);
+    g_signal_connect_after (GTK_WIDGET (self->view_ui), "button-press-event",
+                            (GCallback) on_button_press_event, self);
+    gtk_widget_show (GTK_WIDGET (self->view_ui));
+    self->view_icon = g_themed_icon_new ("view-grid-symbolic");
+
+    content_widget = nautilus_files_view_get_content_widget (NAUTILUS_FILES_VIEW (self));
+    gtk_container_add (GTK_CONTAINER (content_widget), GTK_WIDGET (self->view_ui));
+
+    self->action_group = nautilus_files_view_get_action_group (NAUTILUS_FILES_VIEW (self));
+    g_action_map_add_action_entries (G_ACTION_MAP (self->action_group),
+                                     view_icon_actions,
+                                     G_N_ELEMENTS (view_icon_actions),
+                                     self);
+
+    gtk_widget_show_all (GTK_WIDGET (self));
+
+    view_action_group = nautilus_files_view_get_action_group (NAUTILUS_FILES_VIEW (self));
+    g_action_map_add_action_entries (G_ACTION_MAP (view_action_group),
+                                     view_icon_actions,
+                                     G_N_ELEMENTS (view_icon_actions),
+                                     self);
+    self->zoom_level = get_default_zoom_level ();
+    /* Keep the action synced with the actual value, so the toolbar can poll it */
+    g_action_group_change_action_state (nautilus_files_view_get_action_group (NAUTILUS_FILES_VIEW (self)),
+                                        "zoom-to-level", g_variant_new_int32 (self->zoom_level));
+}
+
+static void
+nautilus_view_icon_controller_class_init (NautilusViewIconControllerClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS (klass);
+    NautilusFilesViewClass *files_view_class = NAUTILUS_FILES_VIEW_CLASS (klass);
+
+    object_class->finalize = finalize;
+    object_class->constructed = constructed;
+
+    files_view_class->add_files = real_add_files;
+    files_view_class->begin_loading = real_begin_loading;
+    files_view_class->bump_zoom_level = real_bump_zoom_level;
+    files_view_class->can_zoom_in = real_can_zoom_in;
+    files_view_class->can_zoom_out = real_can_zoom_out;
+    files_view_class->click_policy_changed = real_click_policy_changed;
+    files_view_class->clear = real_clear;
+    files_view_class->file_changed = real_file_changed;
+    files_view_class->get_selection = real_get_selection;
+    files_view_class->get_selection_for_file_transfer = real_get_selection_for_file_transfer;
+    files_view_class->is_empty = real_is_empty;
+    files_view_class->remove_file = real_remove_file;
+    files_view_class->update_actions_state = real_update_actions_state;
+    files_view_class->reveal_selection = real_reveal_selection;
+    files_view_class->select_all = real_select_all;
+    files_view_class->set_selection = real_set_selection;
+    files_view_class->compare_files = real_compare_files;
+    files_view_class->sort_directories_first_changed = real_sort_directories_first_changed;
+    files_view_class->end_file_changes = real_end_file_changes;
+    files_view_class->using_manual_layout = real_using_manual_layout;
+    files_view_class->end_loading = real_end_loading;
+    files_view_class->get_view_id = real_get_view_id;
+    files_view_class->get_first_visible_file = real_get_first_visible_file;
+    files_view_class->scroll_to_file = real_scroll_to_file;
+    files_view_class->get_icon = real_get_icon;
+    files_view_class->select_first = real_select_first;
+    files_view_class->restore_standard_zoom_level = real_restore_standard_zoom_level;
+    files_view_class->get_zoom_level_percentage = real_get_zoom_level_percentage;
+    files_view_class->compute_rename_popover_pointing_to = real_compute_rename_popover_pointing_to;
+}
+
+static void
+nautilus_view_icon_controller_init (NautilusViewIconController *self)
+{
+}
+
+NautilusViewIconController *
+nautilus_view_icon_controller_new (NautilusWindowSlot *slot)
+{
+    return g_object_new (NAUTILUS_TYPE_VIEW_ICON_CONTROLLER,
+                         "window-slot", slot,
+                         NULL);
+}
+
+NautilusViewModel *
+nautilus_view_icon_controller_get_model (NautilusViewIconController *self)
+{
+    g_return_val_if_fail (NAUTILUS_IS_VIEW_ICON_CONTROLLER (self), NULL);
+
+    return self->model;
+}
diff --git a/src/nautilus-view-icon-controller.h b/src/nautilus-view-icon-controller.h
new file mode 100644
index 0000000..814d6d6
--- /dev/null
+++ b/src/nautilus-view-icon-controller.h
@@ -0,0 +1,24 @@
+#ifndef NAUTILUS_VIEW_ICON_CONTROLLER_H
+#define NAUTILUS_VIEW_ICON_CONTROLLER_H
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+#include "nautilus-files-view.h"
+#include "nautilus-window-slot.h"
+#include "nautilus-view-model.h"
+
+G_BEGIN_DECLS
+
+#define NAUTILUS_TYPE_VIEW_ICON_CONTROLLER (nautilus_view_icon_controller_get_type())
+
+G_DECLARE_FINAL_TYPE (NautilusViewIconController, nautilus_view_icon_controller, NAUTILUS, 
VIEW_ICON_CONTROLLER, NautilusFilesView)
+
+NautilusViewIconController *nautilus_view_icon_controller_new (NautilusWindowSlot *slot);
+
+NautilusViewModel * nautilus_view_icon_controller_get_model (NautilusViewIconController *self);
+
+G_END_DECLS
+
+#endif /* NAUTILUS_VIEW_ICON_CONTROLLER_H */
+
diff --git a/src/nautilus-view-icon-item-ui.c b/src/nautilus-view-icon-item-ui.c
new file mode 100644
index 0000000..46ccba3
--- /dev/null
+++ b/src/nautilus-view-icon-item-ui.c
@@ -0,0 +1,268 @@
+#include "nautilus-view-icon-item-ui.h"
+#include "nautilus-view-item-model.h"
+#include "nautilus-container-max-width.h"
+#include "nautilus-file.h"
+#include "nautilus-thumbnails.h"
+
+struct _NautilusViewIconItemUi
+{
+    GtkFlowBoxChild parent_instance;
+
+    NautilusViewItemModel *model;
+
+    NautilusContainerMaxWidth *item_container;
+    GtkWidget *icon;
+    GtkLabel *label;
+};
+
+G_DEFINE_TYPE (NautilusViewIconItemUi, nautilus_view_icon_item_ui, GTK_TYPE_FLOW_BOX_CHILD)
+
+enum
+{
+    PROP_0,
+    PROP_MODEL,
+    N_PROPS
+};
+
+static GtkWidget *
+create_icon (NautilusViewIconItemUi *self)
+{
+    NautilusFileIconFlags flags;
+    g_autoptr (GdkPixbuf) icon_pixbuf;
+    GtkImage *icon;
+    GtkBox *fixed_height_box;
+    GtkStyleContext *style_context;
+    NautilusFile *file;
+    guint icon_size;
+
+    file = nautilus_view_item_model_get_file (self->model);
+    icon_size = nautilus_view_item_model_get_icon_size (self->model);
+    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_pixbuf = nautilus_file_get_icon_pixbuf (file, icon_size,
+                                                 TRUE, 1, flags);
+    icon = GTK_IMAGE (gtk_image_new_from_pixbuf (icon_pixbuf));
+    gtk_widget_set_hexpand (GTK_WIDGET (icon), TRUE);
+    gtk_widget_set_vexpand (GTK_WIDGET (icon), TRUE);
+    gtk_widget_set_valign (GTK_WIDGET (icon), GTK_ALIGN_CENTER);
+    gtk_widget_set_halign (GTK_WIDGET (icon), GTK_ALIGN_CENTER);
+
+    fixed_height_box = GTK_BOX (gtk_box_new (GTK_ORIENTATION_VERTICAL, 0));
+    gtk_widget_set_valign (GTK_WIDGET (fixed_height_box), GTK_ALIGN_CENTER);
+    gtk_widget_set_halign (GTK_WIDGET (fixed_height_box), GTK_ALIGN_CENTER);
+    gtk_widget_set_size_request (GTK_WIDGET (fixed_height_box), icon_size, icon_size);
+
+    if (nautilus_can_thumbnail (file) &&
+        nautilus_file_should_show_thumbnail (file))
+    {
+        style_context = gtk_widget_get_style_context (GTK_WIDGET (fixed_height_box));
+        gtk_style_context_add_class (style_context, "icon-background");
+    }
+
+    gtk_box_pack_start (fixed_height_box, GTK_WIDGET (icon), FALSE, FALSE, 0);
+
+    return GTK_WIDGET (fixed_height_box);
+}
+
+static void
+update_icon (NautilusViewIconItemUi *self)
+{
+    GtkBox *box;
+    guint icon_size;
+
+    icon_size = nautilus_view_item_model_get_icon_size (self->model);
+    nautilus_container_max_width_set_max_width (NAUTILUS_CONTAINER_MAX_WIDTH (self->item_container),
+                                                icon_size);
+    box = GTK_BOX (gtk_bin_get_child (GTK_BIN (self->item_container)));
+    if (self->icon)
+    {
+        gtk_container_remove (GTK_CONTAINER (box), GTK_WIDGET (self->icon));
+    }
+    self->icon = create_icon (self);
+    gtk_widget_show_all (GTK_WIDGET (self->icon));
+    gtk_box_pack_start (box, GTK_WIDGET (self->icon), FALSE, FALSE, 0);
+}
+
+static void
+on_view_item_file_changed (GObject    *object,
+                           GParamSpec *pspec,
+                           gpointer    user_data)
+{
+    NautilusViewIconItemUi *self = NAUTILUS_VIEW_ICON_ITEM_UI (user_data);
+    NautilusFile *file;
+
+    file = nautilus_view_item_model_get_file (self->model);
+
+    if (self->icon)
+    {
+        update_icon (self);
+    }
+
+    if (self->label)
+    {
+        gtk_label_set_text (self->label,
+                            nautilus_file_get_display_name (file));
+    }
+}
+
+static void
+on_view_item_size_changed (GObject    *object,
+                           GParamSpec *pspec,
+                           gpointer    user_data)
+{
+    NautilusViewIconItemUi *self = NAUTILUS_VIEW_ICON_ITEM_UI (user_data);
+
+    if (self->icon)
+    {
+        update_icon (self);
+    }
+}
+
+static void
+constructed (GObject *object)
+{
+    NautilusViewIconItemUi *self = NAUTILUS_VIEW_ICON_ITEM_UI (object);
+    GtkBox *container;
+    GtkLabel *label;
+    GtkStyleContext *style_context;
+    NautilusFile *file;
+    guint icon_size;
+
+    G_OBJECT_CLASS (nautilus_view_icon_item_ui_parent_class)->constructed (object);
+
+    file = nautilus_view_item_model_get_file (self->model);
+    icon_size = nautilus_view_item_model_get_icon_size (self->model);
+    container = GTK_BOX (gtk_box_new (GTK_ORIENTATION_VERTICAL, 0));
+    self->item_container = nautilus_container_max_width_new ();
+
+    self->icon = create_icon (self);
+    gtk_box_pack_start (container, GTK_WIDGET (self->icon), FALSE, FALSE, 0);
+
+    label = GTK_LABEL (gtk_label_new (nautilus_file_get_display_name (file)));
+    gtk_widget_show (GTK_WIDGET (label));
+    gtk_label_set_ellipsize (label, PANGO_ELLIPSIZE_END);
+    gtk_label_set_line_wrap (label, TRUE);
+    gtk_label_set_line_wrap_mode (label, PANGO_WRAP_WORD_CHAR);
+    gtk_label_set_lines (label, 4);
+    gtk_label_set_justify (label, GTK_JUSTIFY_CENTER);
+    gtk_widget_set_valign (GTK_WIDGET (label), GTK_ALIGN_START);
+    gtk_box_pack_end (container, GTK_WIDGET (label), TRUE, TRUE, 0);
+
+    style_context = gtk_widget_get_style_context (GTK_WIDGET (container));
+    gtk_style_context_add_class (style_context, "icon-item-background");
+
+    gtk_widget_set_valign (GTK_WIDGET (container), GTK_ALIGN_START);
+    gtk_widget_set_halign (GTK_WIDGET (container), GTK_ALIGN_CENTER);
+
+    gtk_container_add (GTK_CONTAINER (self->item_container),
+                       GTK_WIDGET (container));
+    nautilus_container_max_width_set_max_width (NAUTILUS_CONTAINER_MAX_WIDTH (self->item_container),
+                                                icon_size);
+
+    gtk_container_add (GTK_CONTAINER (self), GTK_WIDGET (self->item_container));
+    gtk_widget_show_all (GTK_WIDGET (self->item_container));
+
+    g_signal_connect (self->model, "notify::icon-size",
+                      (GCallback) on_view_item_size_changed, self);
+    g_signal_connect (self->model, "notify::file",
+                      (GCallback) on_view_item_file_changed, self);
+}
+
+static void
+finalize (GObject *object)
+{
+    NautilusViewIconItemUi *self = (NautilusViewIconItemUi *) object;
+
+    g_signal_handlers_disconnect_by_data (self->model, self);
+    G_OBJECT_CLASS (nautilus_view_icon_item_ui_parent_class)->finalize (object);
+}
+
+static void
+get_property (GObject    *object,
+              guint       prop_id,
+              GValue     *value,
+              GParamSpec *pspec)
+{
+    NautilusViewIconItemUi *self = NAUTILUS_VIEW_ICON_ITEM_UI (object);
+
+    switch (prop_id)
+    {
+        case PROP_MODEL:
+        {
+            g_value_set_object (value, self->model);
+        }
+        break;
+
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+set_model (NautilusViewIconItemUi *self,
+           NautilusViewItemModel  *model)
+{
+    self->model = g_object_ref (model);
+}
+
+static void
+set_property (GObject      *object,
+              guint         prop_id,
+              const GValue *value,
+              GParamSpec   *pspec)
+{
+    NautilusViewIconItemUi *self = NAUTILUS_VIEW_ICON_ITEM_UI (object);
+
+    switch (prop_id)
+    {
+        case PROP_MODEL:
+        {
+            set_model (self, g_value_get_object (value));
+        }
+        break;
+
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+nautilus_view_icon_item_ui_class_init (NautilusViewIconItemUiClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+    object_class->finalize = finalize;
+    object_class->get_property = get_property;
+    object_class->set_property = set_property;
+    object_class->constructed = constructed;
+
+    g_object_class_install_property (object_class,
+                                     PROP_MODEL,
+                                     g_param_spec_object ("model",
+                                                          "Item model",
+                                                          "The item model that this UI reprensents",
+                                                          NAUTILUS_TYPE_VIEW_ITEM_MODEL,
+                                                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+nautilus_view_icon_item_ui_init (NautilusViewIconItemUi *self)
+{
+}
+
+NautilusViewIconItemUi *
+nautilus_view_icon_item_ui_new (NautilusViewItemModel *model)
+{
+    return g_object_new (NAUTILUS_TYPE_VIEW_ICON_ITEM_UI,
+                         "model", model,
+                         NULL);
+}
+
+NautilusViewItemModel *
+nautilus_view_icon_item_ui_get_model (NautilusViewIconItemUi *self)
+{
+    return self->model;
+}
diff --git a/src/nautilus-view-icon-item-ui.h b/src/nautilus-view-icon-item-ui.h
new file mode 100644
index 0000000..a28eca1
--- /dev/null
+++ b/src/nautilus-view-icon-item-ui.h
@@ -0,0 +1,22 @@
+#ifndef NAUTILUS_VIEW_ICON_ITEM_UI_H
+#define NAUTILUS_VIEW_ICON_ITEM_UI_H
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+#include "nautilus-view-item-model.h"
+
+G_BEGIN_DECLS
+
+#define NAUTILUS_TYPE_VIEW_ICON_ITEM_UI (nautilus_view_icon_item_ui_get_type())
+
+G_DECLARE_FINAL_TYPE (NautilusViewIconItemUi, nautilus_view_icon_item_ui, NAUTILUS, VIEW_ICON_ITEM_UI, 
GtkFlowBoxChild)
+
+NautilusViewIconItemUi * nautilus_view_icon_item_ui_new (NautilusViewItemModel *item_model);
+
+NautilusViewItemModel * nautilus_view_icon_item_ui_get_model (NautilusViewIconItemUi *self);
+
+G_END_DECLS
+
+#endif /* NAUTILUS_VIEW_ICON_ITEM_UI_H */
+
diff --git a/src/nautilus-view-icon-ui.c b/src/nautilus-view-icon-ui.c
new file mode 100644
index 0000000..6df82a7
--- /dev/null
+++ b/src/nautilus-view-icon-ui.c
@@ -0,0 +1,252 @@
+/* nautilus-view-icon-ui.c
+ *
+ * Copyright (C) 2016 Carlos Soriano <csoriano gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <glib.h>
+
+#include "nautilus-view-icon-ui.h"
+#include "nautilus-view-icon-item-ui.h"
+#include "nautilus-view-icon-controller.h"
+#include "nautilus-files-view.h"
+#include "nautilus-file.h"
+#include "nautilus-directory.h"
+#include "nautilus-global-preferences.h"
+
+struct _NautilusViewIconUi
+{
+    GtkFlowBox parent_instance;
+
+    NautilusViewIconController *controller;
+};
+
+G_DEFINE_TYPE (NautilusViewIconUi, nautilus_view_icon_ui, GTK_TYPE_FLOW_BOX)
+
+enum
+{
+    PROP_0,
+    PROP_CONTROLLER,
+    N_PROPS
+};
+
+static void
+set_controller (NautilusViewIconUi         *self,
+                NautilusViewIconController *controller)
+{
+    self->controller = controller;
+
+    g_object_notify (G_OBJECT (self), "controller");
+}
+
+static void
+get_property (GObject    *object,
+              guint       prop_id,
+              GValue     *value,
+              GParamSpec *pspec)
+{
+    NautilusViewIconUi *self = NAUTILUS_VIEW_ICON_UI (object);
+
+    switch (prop_id)
+    {
+        case PROP_CONTROLLER:
+        {
+            g_value_set_object (value, self->controller);
+        }
+        break;
+
+        default:
+        {
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        }
+    }
+}
+
+static void
+set_property (GObject      *object,
+              guint         prop_id,
+              const GValue *value,
+              GParamSpec   *pspec)
+{
+    NautilusViewIconUi *self = NAUTILUS_VIEW_ICON_UI (object);
+
+    switch (prop_id)
+    {
+        case PROP_CONTROLLER:
+        {
+            set_controller (self, g_value_get_object (value));
+        }
+        break;
+
+        default:
+        {
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        }
+    }
+}
+
+static void
+on_view_item_model_selected_changed (GObject    *object,
+                                     GParamSpec *pspec,
+                                     gpointer    user_data)
+{
+    NautilusViewIconUi *self;
+    NautilusViewItemModel *item_model;
+    GtkFlowBoxChild *item_ui;
+
+    self = NAUTILUS_VIEW_ICON_UI (user_data);
+    item_model = NAUTILUS_VIEW_ITEM_MODEL (object);
+    item_ui = GTK_FLOW_BOX_CHILD (nautilus_view_item_model_get_item_ui (item_model));
+    if (nautilus_view_item_model_get_is_selected (item_model) && !gtk_flow_box_child_is_selected (item_ui))
+    {
+        gtk_flow_box_select_child (GTK_FLOW_BOX (self), item_ui);
+    }
+    else if (!nautilus_view_item_model_get_is_selected (item_model) && gtk_flow_box_child_is_selected 
(item_ui))
+    {
+        gtk_flow_box_unselect_child (GTK_FLOW_BOX (self), item_ui);
+    }
+}
+
+
+static GtkWidget *
+create_widget_func (gpointer item,
+                    gpointer user_data)
+{
+    NautilusViewIconUi *self = NAUTILUS_VIEW_ICON_UI (user_data);
+    NautilusViewItemModel *item_model = NAUTILUS_VIEW_ITEM_MODEL (item);
+    NautilusViewIconItemUi *child;
+
+    child = nautilus_view_icon_item_ui_new (item_model);
+    nautilus_view_item_model_set_item_ui (item_model, GTK_WIDGET (child));
+    gtk_widget_show (GTK_WIDGET (child));
+
+    g_signal_connect (item_model, "notify::selected",
+                      (GCallback) on_view_item_model_selected_changed, self);
+
+    return GTK_WIDGET (child);
+}
+
+static void
+on_child_activated (GtkFlowBox      *flow_box,
+                    GtkFlowBoxChild *child,
+                    gpointer         user_data)
+{
+    NautilusViewIconUi *self = NAUTILUS_VIEW_ICON_UI (user_data);
+    NautilusViewItemModel *item_model;
+    NautilusFile *file;
+    g_autoptr (GList) list = NULL;
+
+    item_model = nautilus_view_icon_item_ui_get_model (NAUTILUS_VIEW_ICON_ITEM_UI (child));
+    file = nautilus_view_item_model_get_file (item_model);
+    list = g_list_append (list, file);
+
+    nautilus_files_view_activate_files (NAUTILUS_FILES_VIEW (self->controller), list, 0, TRUE);
+}
+
+static void
+on_ui_selected_children_changed (GtkFlowBox *box,
+                                 gpointer    user_data)
+{
+    NautilusViewIconUi *self;
+    GList *selected_children_ui;
+    GList *l;
+    GList *files_selection;
+
+    self = NAUTILUS_VIEW_ICON_UI (user_data);
+    files_selection = NULL;
+
+    selected_children_ui = gtk_flow_box_get_selected_children (GTK_FLOW_BOX (self));
+    for (l = selected_children_ui; l != NULL; l = l->next)
+    {
+        NautilusViewItemModel *item_model;
+        NautilusFile *file;
+
+        item_model = nautilus_view_icon_item_ui_get_model (NAUTILUS_VIEW_ICON_ITEM_UI (l->data));
+        file = nautilus_view_item_model_get_file (item_model);
+        files_selection = g_list_prepend (files_selection, file);
+    }
+
+    nautilus_view_set_selection (NAUTILUS_VIEW (self->controller), files_selection);
+}
+
+static void
+finalize (GObject *object)
+{
+    G_OBJECT_CLASS (nautilus_view_icon_ui_parent_class)->finalize (object);
+}
+
+static void
+constructed (GObject *object)
+{
+    NautilusViewIconUi *self = NAUTILUS_VIEW_ICON_UI (object);
+    NautilusViewModel *model;
+    GListStore *gmodel;
+
+    G_OBJECT_CLASS (nautilus_view_icon_ui_parent_class)->constructed (object);
+
+    gtk_flow_box_set_activate_on_single_click (GTK_FLOW_BOX (self), FALSE);
+    gtk_flow_box_set_max_children_per_line (GTK_FLOW_BOX (self), 20);
+    gtk_flow_box_set_selection_mode (GTK_FLOW_BOX (self), GTK_SELECTION_MULTIPLE);
+    gtk_flow_box_set_homogeneous (GTK_FLOW_BOX (self), FALSE);
+    gtk_flow_box_set_row_spacing (GTK_FLOW_BOX (self), 4);
+    gtk_flow_box_set_column_spacing (GTK_FLOW_BOX (self), 8);
+    gtk_widget_set_valign (GTK_WIDGET (self), GTK_ALIGN_START);
+    gtk_widget_set_margin_top (GTK_WIDGET (self), 10);
+    gtk_widget_set_margin_start (GTK_WIDGET (self), 10);
+    gtk_widget_set_margin_bottom (GTK_WIDGET (self), 10);
+    gtk_widget_set_margin_end (GTK_WIDGET (self), 10);
+
+    model = nautilus_view_icon_controller_get_model (self->controller);
+    gmodel = nautilus_view_model_get_g_model (model);
+    gtk_flow_box_bind_model (GTK_FLOW_BOX (self),
+                             G_LIST_MODEL (gmodel),
+                             create_widget_func, self, NULL);
+
+    g_signal_connect (self, "child-activated", (GCallback) on_child_activated, self);
+    g_signal_connect (self, "selected-children-changed", (GCallback) on_ui_selected_children_changed, self);
+}
+
+static void
+nautilus_view_icon_ui_class_init (NautilusViewIconUiClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+    object_class->finalize = finalize;
+    object_class->set_property = set_property;
+    object_class->get_property = get_property;
+    object_class->constructed = constructed;
+
+    g_object_class_install_property (object_class,
+                                     PROP_CONTROLLER,
+                                     g_param_spec_object ("controller",
+                                                          "Controller",
+                                                          "The controller of the view",
+                                                          NAUTILUS_TYPE_VIEW_ICON_CONTROLLER,
+                                                          G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+nautilus_view_icon_ui_init (NautilusViewIconUi *self)
+{
+}
+
+NautilusViewIconUi *
+nautilus_view_icon_ui_new (NautilusViewIconController *controller)
+{
+    return g_object_new (NAUTILUS_TYPE_VIEW_ICON_UI,
+                         "controller", controller,
+                         NULL);
+}
diff --git a/src/nautilus-view-icon-ui.h b/src/nautilus-view-icon-ui.h
new file mode 100644
index 0000000..5361acc
--- /dev/null
+++ b/src/nautilus-view-icon-ui.h
@@ -0,0 +1,37 @@
+/* nautilus-view-icon-ui.h
+ *
+ * Copyright (C) 2016 Carlos Soriano <csoriano gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef NAUTILUS_VIEW_ICON_UI_H
+#define NAUTILUS_VIEW_ICON_UI_H
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+#include "nautilus-view-icon-controller.h"
+
+G_BEGIN_DECLS
+
+#define NAUTILUS_TYPE_VIEW_ICON_UI (nautilus_view_icon_ui_get_type())
+
+G_DECLARE_FINAL_TYPE (NautilusViewIconUi, nautilus_view_icon_ui, NAUTILUS, VIEW_ICON_UI, GtkFlowBox)
+
+NautilusViewIconUi * nautilus_view_icon_ui_new (NautilusViewIconController *controller);
+
+G_END_DECLS
+
+#endif /* NAUTILUS_VIEW_ICON_UI_H */
+
diff --git a/src/nautilus-view-item-model.c b/src/nautilus-view-item-model.c
new file mode 100644
index 0000000..42f1911
--- /dev/null
+++ b/src/nautilus-view-item-model.c
@@ -0,0 +1,249 @@
+#include "nautilus-view-item-model.h"
+#include "nautilus-file.h"
+
+struct _NautilusViewItemModel
+{
+    GObject parent_instance;
+    guint icon_size;
+    NautilusFile *file;
+    GtkLabel *label;
+    gboolean selected;
+    GtkWidget *item_ui;
+};
+
+G_DEFINE_TYPE (NautilusViewItemModel, nautilus_view_item_model, G_TYPE_OBJECT)
+
+enum
+{
+    PROP_0,
+    PROP_FILE,
+    PROP_ICON_SIZE,
+    PROP_SELECTED,
+    PROP_ITEM_UI,
+    N_PROPS
+};
+
+static void
+nautilus_view_item_model_finalize (GObject *object)
+{
+    G_OBJECT_CLASS (nautilus_view_item_model_parent_class)->finalize (object);
+}
+
+static void
+nautilus_view_item_model_get_property (GObject    *object,
+                                       guint       prop_id,
+                                       GValue     *value,
+                                       GParamSpec *pspec)
+{
+    NautilusViewItemModel *self = NAUTILUS_VIEW_ITEM_MODEL (object);
+
+    switch (prop_id)
+    {
+        case PROP_FILE:
+        {
+            g_value_set_object (value, self->file);
+        }
+        break;
+
+        case PROP_ICON_SIZE:
+        {
+            g_value_set_int (value, self->icon_size);
+        }
+        break;
+
+        case PROP_SELECTED:
+        {
+            g_value_set_boolean (value, self->selected);
+        }
+        break;
+
+        case PROP_ITEM_UI:
+        {
+            g_value_set_object (value, self->item_ui);
+        }
+        break;
+
+        default:
+        {
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        }
+    }
+}
+
+static void
+nautilus_view_item_model_set_property (GObject      *object,
+                                       guint         prop_id,
+                                       const GValue *value,
+                                       GParamSpec   *pspec)
+{
+    NautilusViewItemModel *self = NAUTILUS_VIEW_ITEM_MODEL (object);
+
+    switch (prop_id)
+    {
+        case PROP_FILE:
+        {
+            nautilus_view_item_model_set_file (self, g_value_get_object (value));
+        }
+        break;
+
+        case PROP_ICON_SIZE:
+        {
+            nautilus_view_item_model_set_icon_size (self, g_value_get_int (value));
+        }
+        break;
+
+        case PROP_SELECTED:
+        {
+            nautilus_view_item_model_set_selected (self, g_value_get_boolean (value));
+        }
+        break;
+
+        case PROP_ITEM_UI:
+        {
+            nautilus_view_item_model_set_item_ui (self, g_value_get_object (value));
+        }
+        break;
+
+        default:
+        {
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        }
+    }
+}
+
+static void
+nautilus_view_item_model_init (NautilusViewItemModel *self)
+{
+}
+
+static void
+nautilus_view_item_model_class_init (NautilusViewItemModelClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+    object_class->finalize = nautilus_view_item_model_finalize;
+    object_class->get_property = nautilus_view_item_model_get_property;
+    object_class->set_property = nautilus_view_item_model_set_property;
+
+    g_object_class_install_property (object_class,
+                                     PROP_ICON_SIZE,
+                                     g_param_spec_int ("icon-size",
+                                                       "Icon size",
+                                                       "The size in pixels of the icon",
+                                                       NAUTILUS_CANVAS_ICON_SIZE_SMALL,
+                                                       NAUTILUS_CANVAS_ICON_SIZE_LARGER,
+                                                       NAUTILUS_CANVAS_ICON_SIZE_LARGE,
+                                                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+    g_object_class_install_property (object_class,
+                                     PROP_FILE,
+                                     g_param_spec_object ("file",
+                                                          "File",
+                                                          "The file the icon item represents",
+                                                          NAUTILUS_TYPE_FILE,
+                                                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+    g_object_class_install_property (object_class,
+                                     PROP_SELECTED,
+                                     g_param_spec_boolean ("selected",
+                                                           "Selected",
+                                                           "Sets the item as selected",
+                                                           FALSE,
+                                                           G_PARAM_READWRITE));
+
+    g_object_class_install_property (object_class,
+                                     PROP_ITEM_UI,
+                                     g_param_spec_object ("item-ui",
+                                                          "Item ui",
+                                                          "The UI that reprensents the item model",
+                                                          GTK_TYPE_WIDGET,
+                                                          G_PARAM_READWRITE));
+}
+
+NautilusViewItemModel *
+nautilus_view_item_model_new (NautilusFile *file,
+                              guint         icon_size)
+{
+    return g_object_new (NAUTILUS_TYPE_VIEW_ITEM_MODEL,
+                         "file", file,
+                         "icon-size", icon_size,
+                         NULL);
+}
+
+guint
+nautilus_view_item_model_get_icon_size (NautilusViewItemModel *self)
+{
+    g_return_val_if_fail (NAUTILUS_IS_VIEW_ITEM_MODEL (self), -1);
+
+    return self->icon_size;
+}
+
+void
+nautilus_view_item_model_set_icon_size (NautilusViewItemModel *self,
+                                        guint                  icon_size)
+{
+    g_return_if_fail (NAUTILUS_IS_VIEW_ITEM_MODEL (self));
+
+    self->icon_size = icon_size;
+
+    g_object_notify (G_OBJECT (self), "icon-size");
+}
+
+NautilusFile *
+nautilus_view_item_model_get_file (NautilusViewItemModel *self)
+{
+    g_return_val_if_fail (NAUTILUS_IS_VIEW_ITEM_MODEL (self), NULL);
+
+    return self->file;
+}
+
+void
+nautilus_view_item_model_set_file (NautilusViewItemModel *self,
+                                   NautilusFile          *file)
+{
+    g_return_if_fail (NAUTILUS_IS_VIEW_ITEM_MODEL (self));
+
+    g_clear_object (&self->file);
+    self->file = g_object_ref (file);
+
+    g_object_notify (G_OBJECT (self), "file");
+}
+
+gboolean
+nautilus_view_item_model_get_is_selected (NautilusViewItemModel *self)
+{
+    g_return_val_if_fail (NAUTILUS_IS_VIEW_ITEM_MODEL (self), FALSE);
+
+    return self->selected;
+}
+
+void
+nautilus_view_item_model_set_selected (NautilusViewItemModel *self,
+                                       gboolean               selected)
+{
+    g_return_if_fail (NAUTILUS_IS_VIEW_ITEM_MODEL (self));
+
+    if (self->selected != !!selected)
+    {
+        self->selected = !!selected;
+        g_object_notify (G_OBJECT (self), "selected");
+    }
+}
+
+GtkWidget *
+nautilus_view_item_model_get_item_ui (NautilusViewItemModel *self)
+{
+    g_return_val_if_fail (NAUTILUS_IS_VIEW_ITEM_MODEL (self), NULL);
+
+    return self->item_ui;
+}
+
+void
+nautilus_view_item_model_set_item_ui (NautilusViewItemModel *self,
+                                      GtkWidget             *item_ui)
+{
+    g_return_if_fail (NAUTILUS_IS_VIEW_ITEM_MODEL (self));
+
+    g_clear_object (&self->item_ui);
+    self->item_ui = g_object_ref (item_ui);
+
+    g_object_notify (G_OBJECT (self), "item-ui");
+}
diff --git a/src/nautilus-view-item-model.h b/src/nautilus-view-item-model.h
new file mode 100644
index 0000000..39a8bc0
--- /dev/null
+++ b/src/nautilus-view-item-model.h
@@ -0,0 +1,41 @@
+#ifndef NAUTILUS_VIEW_ITEM_MODEL_H
+#define NAUTILUS_VIEW_ITEM_MODEL_H
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+#include "nautilus-file.h"
+
+G_BEGIN_DECLS
+
+#define NAUTILUS_TYPE_VIEW_ITEM_MODEL (nautilus_view_item_model_get_type())
+
+G_DECLARE_FINAL_TYPE (NautilusViewItemModel, nautilus_view_item_model, NAUTILUS, VIEW_ITEM_MODEL, GObject)
+
+NautilusViewItemModel * nautilus_view_item_model_new (NautilusFile *file,
+                                                      guint         icon_size);
+
+void nautilus_view_item_model_set_icon_size (NautilusViewItemModel *self,
+                                             guint                 icon_size);
+
+guint nautilus_view_item_model_get_icon_size (NautilusViewItemModel *self);
+
+void nautilus_view_item_model_set_file (NautilusViewItemModel *self,
+                                        NautilusFile         *file);
+
+NautilusFile * nautilus_view_item_model_get_file (NautilusViewItemModel *self);
+
+void nautilus_view_item_model_set_selected (NautilusViewItemModel *self,
+                                            gboolean               selected);
+
+gboolean nautilus_view_item_model_get_is_selected (NautilusViewItemModel *self);
+
+void nautilus_view_item_model_set_item_ui (NautilusViewItemModel *self,
+                                           GtkWidget             *item_ui);
+
+GtkWidget * nautilus_view_item_model_get_item_ui (NautilusViewItemModel *self);
+
+G_END_DECLS
+
+#endif /* NAUTILUS_VIEW_ITEM_MODEL_H */
+
diff --git a/src/nautilus-view-model.c b/src/nautilus-view-model.c
new file mode 100644
index 0000000..2f054e5
--- /dev/null
+++ b/src/nautilus-view-model.c
@@ -0,0 +1,325 @@
+#include "nautilus-view-model.h"
+#include "nautilus-view-item-model.h"
+#include "nautilus-global-preferences.h"
+
+struct _NautilusViewModel
+{
+    GObject parent_instance;
+
+    GHashTable *map_files_to_model;
+    GListStore *internal_model;
+    NautilusViewModelSortData *sort_data;
+};
+
+G_DEFINE_TYPE (NautilusViewModel, nautilus_view_model, G_TYPE_OBJECT)
+
+enum
+{
+    PROP_0,
+    PROP_SORT_TYPE,
+    PROP_G_MODEL,
+    N_PROPS
+};
+
+static void
+finalize (GObject *object)
+{
+    NautilusViewModel *self = NAUTILUS_VIEW_MODEL (object);
+
+    G_OBJECT_CLASS (nautilus_view_model_parent_class)->finalize (object);
+
+    g_hash_table_destroy (self->map_files_to_model);
+    if (self->sort_data)
+    {
+        g_free (self->sort_data);
+    }
+    g_object_unref (self->internal_model);
+}
+
+static void
+get_property (GObject    *object,
+              guint       prop_id,
+              GValue     *value,
+              GParamSpec *pspec)
+{
+    NautilusViewModel *self = NAUTILUS_VIEW_MODEL (object);
+
+    switch (prop_id)
+    {
+        case PROP_SORT_TYPE:
+        {
+            g_value_set_object (value, self->sort_data);
+        }
+        break;
+
+        case PROP_G_MODEL:
+        {
+            g_value_set_object (value, self->internal_model);
+        }
+        break;
+
+        default:
+        {
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        }
+    }
+}
+
+static void
+set_property (GObject      *object,
+              guint         prop_id,
+              const GValue *value,
+              GParamSpec   *pspec)
+{
+    NautilusViewModel *self = NAUTILUS_VIEW_MODEL (object);
+
+    switch (prop_id)
+    {
+        case PROP_SORT_TYPE:
+        {
+            nautilus_view_model_set_sort_type (self, g_value_get_object (value));
+        }
+        break;
+
+        default:
+        {
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        }
+    }
+}
+
+static void
+constructed (GObject *object)
+{
+    NautilusViewModel *self = NAUTILUS_VIEW_MODEL (object);
+
+    G_OBJECT_CLASS (nautilus_view_model_parent_class)->constructed (object);
+
+    self->internal_model = g_list_store_new (NAUTILUS_TYPE_VIEW_ITEM_MODEL);
+    self->map_files_to_model = g_hash_table_new (NULL, NULL);
+}
+
+static void
+nautilus_view_model_class_init (NautilusViewModelClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+    object_class->finalize = finalize;
+    object_class->get_property = get_property;
+    object_class->set_property = set_property;
+    object_class->constructed = constructed;
+}
+
+static void
+nautilus_view_model_init (NautilusViewModel *self)
+{
+}
+
+static gint
+compare_data_func (gconstpointer a,
+                   gconstpointer b,
+                   gpointer      user_data)
+{
+    NautilusViewModel *self = NAUTILUS_VIEW_MODEL (user_data);
+    NautilusFile *file_a;
+    NautilusFile *file_b;
+
+    file_a = nautilus_view_item_model_get_file (NAUTILUS_VIEW_ITEM_MODEL ((gpointer) a));
+    file_b = nautilus_view_item_model_get_file (NAUTILUS_VIEW_ITEM_MODEL ((gpointer) b));
+
+    return nautilus_file_compare_for_sort (file_a, file_b,
+                                           self->sort_data->sort_type,
+                                           self->sort_data->directories_first,
+                                           self->sort_data->reversed);
+}
+
+NautilusViewModel *
+nautilus_view_model_new ()
+{
+    return g_object_new (NAUTILUS_TYPE_VIEW_MODEL, NULL);
+}
+
+void
+nautilus_view_model_set_sort_type (NautilusViewModel         *self,
+                                   NautilusViewModelSortData *sort_data)
+{
+    if (self->sort_data)
+    {
+        g_free (self->sort_data);
+    }
+
+    self->sort_data = g_new (NautilusViewModelSortData, 1);
+    self->sort_data->sort_type = sort_data->sort_type;
+    self->sort_data->reversed = sort_data->reversed;
+    self->sort_data->directories_first = sort_data->directories_first;
+
+    g_list_store_sort (self->internal_model, compare_data_func, self);
+}
+
+NautilusViewModelSortData *
+nautilus_view_model_get_sort_type (NautilusViewModel *self)
+{
+    return self->sort_data;
+}
+
+GListStore *
+nautilus_view_model_get_g_model (NautilusViewModel *self)
+{
+    return self->internal_model;
+}
+
+GQueue *
+nautilus_view_model_get_items_from_files (NautilusViewModel *self,
+                                          GQueue            *files)
+{
+    GList *l;
+    NautilusViewItemModel *item_model;
+    GQueue *item_models;
+
+    item_models = g_queue_new ();
+    for (l = g_queue_peek_head_link (files); l != NULL; l = l->next)
+    {
+        NautilusFile *file1;
+        gint i = 0;
+
+        file1 = NAUTILUS_FILE (l->data);
+        while ((item_model = g_list_model_get_item (G_LIST_MODEL (self->internal_model), i)))
+        {
+            NautilusFile *file2;
+            g_autofree gchar *file1_uri;
+            g_autofree gchar *file2_uri;
+
+            file2 = nautilus_view_item_model_get_file (item_model);
+            file1_uri = nautilus_file_get_uri (file1);
+            file2_uri = nautilus_file_get_uri (file2);
+            if (g_strcmp0 (file1_uri, file2_uri) == 0)
+            {
+                g_queue_push_tail (item_models, item_model);
+                break;
+            }
+
+            i++;
+        }
+    }
+
+    return item_models;
+}
+
+NautilusViewItemModel *
+nautilus_view_model_get_item_from_file (NautilusViewModel *self,
+                                        NautilusFile      *file)
+{
+    return g_hash_table_lookup (self->map_files_to_model, file);
+}
+
+void
+nautilus_view_model_remove_item (NautilusViewModel     *self,
+                                 NautilusViewItemModel *item)
+{
+    NautilusViewItemModel *item_model;
+    gint i;
+
+    i = 0;
+    item_model = NULL;
+    while ((item_model = g_list_model_get_item (G_LIST_MODEL (self->internal_model), i)))
+    {
+        if (item_model == item)
+        {
+            break;
+        }
+
+        i++;
+    }
+
+    if (item_model != NULL)
+    {
+        g_list_store_remove (self->internal_model, i);
+    }
+}
+
+void
+nautilus_view_model_add_item (NautilusViewModel     *self,
+                              NautilusViewItemModel *item)
+{
+    g_list_store_insert_sorted (self->internal_model, item, compare_data_func, self);
+}
+
+void
+nautilus_view_model_set_selected (NautilusViewModel *self,
+                                  GQueue            *item_models)
+{
+    GList *l;
+    NautilusViewItemModel *item_model;
+
+    gint i = 0;
+    while ((item_model = g_list_model_get_item (G_LIST_MODEL (self->internal_model), i)))
+    {
+        gboolean selected;
+
+        selected = FALSE;
+        for (l = g_queue_peek_head_link (item_models); l != NULL; l = l->next)
+        {
+            NautilusViewItemModel *selected_item_model;
+
+            selected_item_model = NAUTILUS_VIEW_ITEM_MODEL (l->data);
+            if (item_model == selected_item_model)
+            {
+                selected = TRUE;
+                break;
+            }
+        }
+        i++;
+
+        nautilus_view_item_model_set_selected (item_model, selected);
+    }
+}
+
+GQueue *
+nautilus_view_model_get_selected (NautilusViewModel *self)
+{
+    NautilusViewItemModel *item_model;
+    GQueue *selected_items;
+    gint i;
+
+    i = 0;
+    selected_items = g_queue_new ();
+    while ((item_model = g_list_model_get_item (G_LIST_MODEL (self->internal_model), i)))
+    {
+        if (nautilus_view_item_model_get_is_selected (item_model))
+        {
+            g_queue_push_tail (selected_items,
+                               g_object_ref (nautilus_view_item_model_get_file (item_model)));
+        }
+        i++;
+    }
+
+    return selected_items;
+}
+
+void
+nautilus_view_model_set_items (NautilusViewModel *self,
+                               GQueue            *items)
+{
+    g_autofree gpointer *array = NULL;
+    GList *l;
+    int i = 0;
+
+    array = g_malloc_n (g_queue_get_length (items),
+                        sizeof (NautilusViewItemModel *));
+
+    g_hash_table_remove_all (self->map_files_to_model);
+    for (l = g_queue_peek_head_link (items); l != NULL; l = l->next)
+    {
+        array[i] = l->data;
+        g_hash_table_insert (self->map_files_to_model,
+                             nautilus_view_item_model_get_file (l->data),
+                             l->data);
+        i++;
+    }
+
+    g_list_store_splice (self->internal_model,
+                         g_list_model_get_n_items (G_LIST_MODEL (self->internal_model)),
+                         0, array, g_queue_get_length (items));
+
+    g_list_store_sort (self->internal_model, compare_data_func, self);
+}
diff --git a/src/nautilus-view-model.h b/src/nautilus-view-model.h
new file mode 100644
index 0000000..9734032
--- /dev/null
+++ b/src/nautilus-view-model.h
@@ -0,0 +1,43 @@
+#ifndef NAUTILUS_VIEW_MODEL_H
+#define NAUTILUS_VIEW_MODEL_H
+
+#include <glib.h>
+#include "nautilus-file.h"
+#include "nautilus-view-item-model.h"
+
+G_BEGIN_DECLS
+
+#define NAUTILUS_TYPE_VIEW_MODEL (nautilus_view_model_get_type())
+
+G_DECLARE_FINAL_TYPE (NautilusViewModel, nautilus_view_model, NAUTILUS, VIEW_MODEL, GObject)
+
+typedef struct
+{
+    NautilusFileSortType sort_type;
+    gboolean reversed;
+    gboolean directories_first;
+} NautilusViewModelSortData;
+
+NautilusViewModel * nautilus_view_model_new (void);
+
+void nautilus_view_model_set_sort_type (NautilusViewModel         *self,
+                                        NautilusViewModelSortData *sort_data);
+NautilusViewModelSortData * nautilus_view_model_get_sort_type (NautilusViewModel *self);
+GListStore * nautilus_view_model_get_g_model (NautilusViewModel *self);
+NautilusViewItemModel * nautilus_view_model_get_item_from_file (NautilusViewModel *self,
+                                                                NautilusFile      *file);
+GQueue * nautilus_view_model_get_items_from_files (NautilusViewModel *self,
+                                                   GQueue            *files);
+void nautilus_view_model_remove_item (NautilusViewModel     *self,
+                                      NautilusViewItemModel *item);
+void nautilus_view_model_add_item (NautilusViewModel     *self,
+                                   NautilusViewItemModel *item);
+void nautilus_view_model_set_selected (NautilusViewModel *self,
+                                       GQueue            *item_models);
+GQueue * nautilus_view_model_get_selected (NautilusViewModel *self);
+void nautilus_view_model_set_items (NautilusViewModel *self,
+                                    GQueue            *items);
+G_END_DECLS
+
+#endif /* NAUTILUS_VIEW_MODEL_H */
+
diff --git a/src/resources/css/Adwaita.css b/src/resources/css/Adwaita.css
index 48beeee..fc05297 100644
--- a/src/resources/css/Adwaita.css
+++ b/src/resources/css/Adwaita.css
@@ -184,4 +184,14 @@ searchbar { border-top: 1px solid @borders; }
 .conflict-row:selected {
   background: @theme_selected_bg_color;
   color: @theme_selected_fg_color;
-}
\ No newline at end of file
+}
+
+/* Icon view */
+flowboxchild:selected{background-color:transparent;}
+
+flowboxchild > widget > box > .icon-background {padding:0px; background-color:black; border-color:#4a90d9; 
border-style:solid; border-width:0px;}
+flowboxchild:selected > widget > box > .icon-background {padding:0px; background-color:black; 
border-color:#4a90d9; border-style:solid; border-width:0px;}
+
+flowboxchild > widget > .icon-item-background {padding:4px;}
+flowboxchild:selected > widget > .icon-item-background {padding:4px; background-color:#4a90d9; 
border-color:#4a90d9; border-style:solid; border-width:0px; border-radius:4px 4px 4px 4px;}
+


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