summaryrefslogtreecommitdiffstats
path: root/src/nautilus-view-model.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/nautilus-view-model.c422
1 files changed, 422 insertions, 0 deletions
diff --git a/src/nautilus-view-model.c b/src/nautilus-view-model.c
new file mode 100644
index 0000000..2cb1c47
--- /dev/null
+++ b/src/nautilus-view-model.c
@@ -0,0 +1,422 @@
+#include "nautilus-view-model.h"
+#include "nautilus-view-item.h"
+#include "nautilus-global-preferences.h"
+
+struct _NautilusViewModel
+{
+ GObject parent_instance;
+
+ GHashTable *map_files_to_model;
+ GListStore *internal_model;
+ GtkMultiSelection *selection_model;
+ GtkSorter *sorter;
+ gulong sorter_changed_id;
+};
+
+static GType
+nautilus_view_model_get_item_type (GListModel *list)
+{
+ return NAUTILUS_TYPE_VIEW_ITEM;
+}
+
+static guint
+nautilus_view_model_get_n_items (GListModel *list)
+{
+ NautilusViewModel *self = NAUTILUS_VIEW_MODEL (list);
+
+ if (self->internal_model == NULL)
+ {
+ return 0;
+ }
+
+ return g_list_model_get_n_items (G_LIST_MODEL (self->internal_model));
+}
+
+static gpointer
+nautilus_view_model_get_item (GListModel *list,
+ guint position)
+{
+ NautilusViewModel *self = NAUTILUS_VIEW_MODEL (list);
+
+ if (self->internal_model == NULL)
+ {
+ return NULL;
+ }
+
+ return g_list_model_get_item (G_LIST_MODEL (self->internal_model), position);
+}
+
+static void
+nautilus_view_model_list_model_init (GListModelInterface *iface)
+{
+ iface->get_item_type = nautilus_view_model_get_item_type;
+ iface->get_n_items = nautilus_view_model_get_n_items;
+ iface->get_item = nautilus_view_model_get_item;
+}
+
+
+static gboolean
+nautilus_view_model_is_selected (GtkSelectionModel *model,
+ guint position)
+{
+ NautilusViewModel *self = NAUTILUS_VIEW_MODEL (model);
+ GtkSelectionModel *selection_model = GTK_SELECTION_MODEL (self->selection_model);
+
+ return gtk_selection_model_is_selected (selection_model, position);
+}
+
+static GtkBitset *
+nautilus_view_model_get_selection_in_range (GtkSelectionModel *model,
+ guint pos,
+ guint n_items)
+{
+ NautilusViewModel *self = NAUTILUS_VIEW_MODEL (model);
+ GtkSelectionModel *selection_model = GTK_SELECTION_MODEL (self->selection_model);
+
+ return gtk_selection_model_get_selection_in_range (selection_model, pos, n_items);
+}
+
+static gboolean
+nautilus_view_model_set_selection (GtkSelectionModel *model,
+ GtkBitset *selected,
+ GtkBitset *mask)
+{
+ NautilusViewModel *self = NAUTILUS_VIEW_MODEL (model);
+ GtkSelectionModel *selection_model = GTK_SELECTION_MODEL (self->selection_model);
+ gboolean res;
+
+ res = gtk_selection_model_set_selection (selection_model, selected, mask);
+
+ return res;
+}
+
+
+static void
+nautilus_view_model_selection_model_init (GtkSelectionModelInterface *iface)
+{
+ iface->is_selected = nautilus_view_model_is_selected;
+ iface->get_selection_in_range = nautilus_view_model_get_selection_in_range;
+ iface->set_selection = nautilus_view_model_set_selection;
+}
+
+G_DEFINE_TYPE_WITH_CODE (NautilusViewModel, nautilus_view_model, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL,
+ nautilus_view_model_list_model_init)
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_SELECTION_MODEL,
+ nautilus_view_model_selection_model_init))
+
+enum
+{
+ PROP_0,
+ PROP_SORTER,
+ N_PROPS
+};
+
+static GParamSpec *properties[N_PROPS] = { NULL, };
+
+static void
+dispose (GObject *object)
+{
+ NautilusViewModel *self = NAUTILUS_VIEW_MODEL (object);
+
+ if (self->selection_model != NULL)
+ {
+ g_signal_handlers_disconnect_by_func (self->selection_model,
+ gtk_selection_model_selection_changed,
+ self);
+ g_object_unref (self->selection_model);
+ self->selection_model = NULL;
+ }
+
+ if (self->internal_model != NULL)
+ {
+ g_signal_handlers_disconnect_by_func (self->internal_model,
+ g_list_model_items_changed,
+ self);
+ g_object_unref (self->internal_model);
+ self->internal_model = NULL;
+ }
+
+ g_clear_signal_handler (&self->sorter_changed_id, self->sorter);
+
+ G_OBJECT_CLASS (nautilus_view_model_parent_class)->dispose (object);
+}
+
+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);
+ g_clear_object (&self->sorter);
+}
+
+static void
+get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NautilusViewModel *self = NAUTILUS_VIEW_MODEL (object);
+
+ switch (prop_id)
+ {
+ case PROP_SORTER:
+ {
+ g_value_set_object (value, nautilus_view_model_get_sorter (self));
+ }
+ 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_SORTER:
+ {
+ nautilus_view_model_set_sorter (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);
+ self->selection_model = gtk_multi_selection_new (g_object_ref (G_LIST_MODEL (self->internal_model)));
+ self->map_files_to_model = g_hash_table_new (NULL, NULL);
+
+ g_signal_connect_swapped (self->internal_model, "items-changed",
+ G_CALLBACK (g_list_model_items_changed), self);
+ g_signal_connect_swapped (self->selection_model, "selection-changed",
+ G_CALLBACK (gtk_selection_model_selection_changed), self);
+}
+
+static void
+nautilus_view_model_class_init (NautilusViewModelClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = dispose;
+ object_class->finalize = finalize;
+ object_class->get_property = get_property;
+ object_class->set_property = set_property;
+ object_class->constructed = constructed;
+
+ properties[PROP_SORTER] =
+ g_param_spec_object ("sorter",
+ "", "",
+ GTK_TYPE_SORTER,
+ G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+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);
+
+ if (self->sorter == NULL)
+ {
+ return GTK_ORDERING_EQUAL;
+ }
+
+ return gtk_sorter_compare (self->sorter, (gpointer) a, (gpointer) b);
+}
+
+static void
+on_sorter_changed (GtkSorter *sorter,
+ GtkSorterChange change,
+ gpointer user_data)
+{
+ NautilusViewModel *self = NAUTILUS_VIEW_MODEL (user_data);
+
+ g_list_store_sort (self->internal_model, compare_data_func, self);
+}
+
+NautilusViewModel *
+nautilus_view_model_new (void)
+{
+ return g_object_new (NAUTILUS_TYPE_VIEW_MODEL, NULL);
+}
+
+GtkSorter *
+nautilus_view_model_get_sorter (NautilusViewModel *self)
+{
+ return self->sorter;
+}
+
+void
+nautilus_view_model_set_sorter (NautilusViewModel *self,
+ GtkSorter *sorter)
+{
+ if (self->sorter != NULL)
+ {
+ g_clear_signal_handler (&self->sorter_changed_id, self->sorter);
+ }
+
+ if (g_set_object (&self->sorter, sorter))
+ {
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SORTER]);
+ }
+
+ if (self->sorter != NULL)
+ {
+ self->sorter_changed_id = g_signal_connect (self->sorter, "changed",
+ G_CALLBACK (on_sorter_changed), self);
+ g_list_store_sort (self->internal_model, compare_data_func, self);
+ }
+}
+
+GQueue *
+nautilus_view_model_get_items_from_files (NautilusViewModel *self,
+ GQueue *files)
+{
+ GList *l;
+ guint n_items;
+ GQueue *items;
+
+ n_items = g_list_model_get_n_items (G_LIST_MODEL (self->internal_model));
+ items = g_queue_new ();
+ for (l = g_queue_peek_head_link (files); l != NULL; l = l->next)
+ {
+ NautilusFile *file1;
+
+ file1 = NAUTILUS_FILE (l->data);
+ for (guint i = 0; i < n_items; i++)
+ {
+ g_autoptr (NautilusViewItem) item = NULL;
+ NautilusFile *file2;
+ g_autofree gchar *file1_uri = NULL;
+ g_autofree gchar *file2_uri = NULL;
+
+ item = g_list_model_get_item (G_LIST_MODEL (self->internal_model), i);
+ file2 = nautilus_view_item_get_file (item);
+ 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 (items, item);
+ break;
+ }
+ }
+ }
+
+ return items;
+}
+
+NautilusViewItem *
+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,
+ NautilusViewItem *item)
+{
+ guint i;
+
+ if (g_list_store_find (self->internal_model, item, &i))
+ {
+ NautilusFile *file;
+
+ file = nautilus_view_item_get_file (item);
+ g_list_store_remove (self->internal_model, i);
+ g_hash_table_remove (self->map_files_to_model, file);
+ }
+}
+
+void
+nautilus_view_model_remove_all_items (NautilusViewModel *self)
+{
+ g_list_store_remove_all (self->internal_model);
+ g_hash_table_remove_all (self->map_files_to_model);
+}
+
+void
+nautilus_view_model_add_item (NautilusViewModel *self,
+ NautilusViewItem *item)
+{
+ g_hash_table_insert (self->map_files_to_model,
+ nautilus_view_item_get_file (item),
+ item);
+ g_list_store_insert_sorted (self->internal_model, item, compare_data_func, self);
+}
+
+void
+nautilus_view_model_add_items (NautilusViewModel *self,
+ GQueue *items)
+{
+ g_autofree gpointer *array = NULL;
+ GList *l;
+ int i = 0;
+
+ /* Sort items before adding them to the internal model. This ensures that
+ * the first sorted item is become the initial focus and scroll anchor. */
+ g_queue_sort (items, compare_data_func, self);
+
+ array = g_malloc_n (g_queue_get_length (items),
+ sizeof (NautilusViewItem *));
+
+ 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_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);
+}
+
+guint
+nautilus_view_model_get_index (NautilusViewModel *self,
+ NautilusViewItem *item)
+{
+ guint i = G_MAXUINT;
+ gboolean found;
+
+ found = g_list_store_find (self->internal_model, item, &i);
+ g_warn_if_fail (found);
+
+ return i;
+}