summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/org.gnome.nautilus.gschema.xml5
-rw-r--r--src/Makefile.am12
-rw-r--r--src/nautilus-canvas-view.c20
-rw-r--r--src/nautilus-container-max-width.c216
-rw-r--r--src/nautilus-container-max-width.h22
-rw-r--r--src/nautilus-file.c19
-rw-r--r--src/nautilus-files-view.c131
-rw-r--r--src/nautilus-files-view.h10
-rw-r--r--src/nautilus-global-preferences.h3
-rw-r--r--src/nautilus-list-view.c14
-rw-r--r--src/nautilus-view-icon-controller.c876
-rw-r--r--src/nautilus-view-icon-controller.h24
-rw-r--r--src/nautilus-view-icon-item-ui.c268
-rw-r--r--src/nautilus-view-icon-item-ui.h22
-rw-r--r--src/nautilus-view-icon-ui.c252
-rw-r--r--src/nautilus-view-icon-ui.h37
-rw-r--r--src/nautilus-view-item-model.c249
-rw-r--r--src/nautilus-view-item-model.h41
-rw-r--r--src/nautilus-view-model.c325
-rw-r--r--src/nautilus-view-model.h43
-rw-r--r--src/resources/css/Adwaita.css12
21 files changed, 2513 insertions, 88 deletions
diff --git a/data/org.gnome.nautilus.gschema.xml b/data/org.gnome.nautilus.gschema.xml
index 5dc677a5c..151acabfb 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 db0a8027d..c79c03064 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 f1ed2c58f..ab4512d78 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 000000000..b94e01699
--- /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 000000000..935721d4c
--- /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 919e76376..b289487af 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 5ae94bbae..3fdb70eb9 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 6e3cd5df6..7bf0521e3 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 bf1c896f6..7e52f6c77 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 38cd3a886..2d4a34a1e 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 000000000..ae8040eb9
--- /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 000000000..814d6d635
--- /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 000000000..46ccba3ec
--- /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 000000000..a28eca137
--- /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 000000000..6df82a7ba
--- /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 000000000..5361acc17
--- /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 000000000..42f1911be
--- /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 000000000..39a8bc0dd
--- /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 000000000..2f054e5e2
--- /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 000000000..9734032ed
--- /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 48beeeecf..fc05297b6 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;}
+