[libdazzle] tree: add GListModel -> GtkTreeModel adapter
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libdazzle] tree: add GListModel -> GtkTreeModel adapter
- Date: Fri, 18 Aug 2017 07:34:31 +0000 (UTC)
commit ff04341e912eb032f86b03bea7ad415771fc8f11
Author: Christian Hergert <chergert redhat com>
Date: Fri Aug 18 00:33:58 2017 -0700
tree: add GListModel -> GtkTreeModel adapter
This object implements GtkTreeModel using the GListModel underneath for
storage of items.
src/dazzle.h | 1 +
src/tree/dzl-list-store-adapter.c | 397 +++++++++++++++++++++++++++++++++++++
src/tree/dzl-list-store-adapter.h | 42 ++++
src/tree/meson.build | 2 +
tests/meson.build | 7 +
tests/test-list-store-adapter.c | 104 ++++++++++
6 files changed, 553 insertions(+), 0 deletions(-)
---
diff --git a/src/dazzle.h b/src/dazzle.h
index fbeacb5..5ac1a21 100644
--- a/src/dazzle.h
+++ b/src/dazzle.h
@@ -119,6 +119,7 @@ G_BEGIN_DECLS
#include "suggestions/dzl-suggestion.h"
#include "theming/dzl-css-provider.h"
#include "theming/dzl-theme-manager.h"
+#include "tree/dzl-list-store-adapter.h"
#include "tree/dzl-tree.h"
#include "tree/dzl-tree-builder.h"
#include "tree/dzl-tree-node.h"
diff --git a/src/tree/dzl-list-store-adapter.c b/src/tree/dzl-list-store-adapter.c
new file mode 100644
index 0000000..93b9ab5
--- /dev/null
+++ b/src/tree/dzl-list-store-adapter.c
@@ -0,0 +1,397 @@
+/* dzl-list-store-adapter.c
+ *
+ * Copyright (C) 2017 Christian Hergert <chergert redhat com>
+ *
+ * This file is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2.1 of the License, or (at your option)
+ * any later version.
+ *
+ * This file 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 Lesser 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/>.
+ */
+
+#define G_LOG_DOMAIN "dzl-list-store-adapter"
+
+#include "bindings/dzl-signal-group.h"
+#include "tree/dzl-list-store-adapter.h"
+
+typedef struct
+{
+ DzlSignalGroup *signals;
+ GListModel *model;
+ gint length;
+ GType type;
+} DzlListStoreAdapterPrivate;
+
+enum {
+ PROP_0,
+ PROP_MODEL,
+ N_PROPS
+};
+
+static void tree_model_iface_init (GtkTreeModelIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (DzlListStoreAdapter, dzl_list_store_adapter, G_TYPE_OBJECT,
+ G_ADD_PRIVATE (DzlListStoreAdapter)
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL, tree_model_iface_init))
+
+static GParamSpec *properties [N_PROPS];
+
+static GtkTreeModelFlags
+dzl_list_store_adapter_get_flags (GtkTreeModel *model)
+{
+ return GTK_TREE_MODEL_ITERS_PERSIST | GTK_TREE_MODEL_LIST_ONLY;
+}
+
+static gint
+dzl_list_store_adapter_get_n_columns (GtkTreeModel *model)
+{
+ return 1;
+}
+
+static gboolean
+dzl_list_store_adapter_get_iter (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ GtkTreePath *path)
+{
+ DzlListStoreAdapter *self = (DzlListStoreAdapter *)model;
+ DzlListStoreAdapterPrivate *priv = dzl_list_store_adapter_get_instance_private (self);
+ gint pos;
+
+ g_assert (DZL_IS_LIST_STORE_ADAPTER (self));
+ g_assert (iter != NULL);
+ g_assert (path != NULL);
+
+ if (gtk_tree_path_get_depth (path) != 1)
+ return FALSE;
+
+ pos = gtk_tree_path_get_indices (path) [0];
+
+ if (pos >= priv->length)
+ return FALSE;
+
+ iter->user_data = GINT_TO_POINTER (pos);
+
+ return TRUE;
+}
+
+static GtkTreePath *
+dzl_list_store_adapter_get_path (GtkTreeModel *model,
+ GtkTreeIter *iter)
+{
+ return gtk_tree_path_new_from_indices (GPOINTER_TO_INT (iter), -1);
+}
+
+static void
+dzl_list_store_adapter_get_value (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gint column,
+ GValue *value)
+{
+ DzlListStoreAdapter *self = DZL_LIST_STORE_ADAPTER (model);
+ DzlListStoreAdapterPrivate *priv = dzl_list_store_adapter_get_instance_private (self);
+
+ g_value_init (value, priv->type);
+ g_value_take_object (value,
+ g_list_model_get_item (priv->model,
+ GPOINTER_TO_INT (iter->user_data)));
+}
+
+static gboolean
+dzl_list_store_adapter_iter_next (GtkTreeModel *model,
+ GtkTreeIter *iter)
+{
+ DzlListStoreAdapter *self = DZL_LIST_STORE_ADAPTER (model);
+ DzlListStoreAdapterPrivate *priv = dzl_list_store_adapter_get_instance_private (self);
+ gint pos = GPOINTER_TO_INT (iter->user_data) + 1;
+
+ iter->user_data = GINT_TO_POINTER (pos);
+
+ return pos < priv->length;
+}
+
+static gboolean
+dzl_list_store_adapter_iter_previous (GtkTreeModel *model,
+ GtkTreeIter *iter)
+{
+ gint pos = GPOINTER_TO_INT (iter->user_data) - 1;
+
+ iter->user_data = GINT_TO_POINTER (pos);
+
+ return pos >= 0;
+}
+
+static gboolean
+dzl_list_store_adapter_iter_children (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent)
+{
+ if (parent != NULL)
+ return FALSE;
+
+ iter->user_data = NULL;
+
+ return TRUE;
+}
+
+static gboolean
+dzl_list_store_adapter_iter_has_child (GtkTreeModel *model,
+ GtkTreeIter *iter)
+{
+ return iter == NULL;
+}
+
+static gint
+dzl_list_store_adapter_iter_n_children (GtkTreeModel *model,
+ GtkTreeIter *iter)
+{
+ DzlListStoreAdapter *self = DZL_LIST_STORE_ADAPTER (model);
+ DzlListStoreAdapterPrivate *priv = dzl_list_store_adapter_get_instance_private (self);
+
+ if (iter == NULL)
+ return priv->length;
+
+ return 0;
+}
+
+static gboolean
+dzl_list_store_adapter_iter_parent (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ GtkTreeIter *child)
+{
+ return FALSE;
+}
+
+static gboolean
+dzl_list_store_adapter_iter_nth_child (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent,
+ gint nth)
+{
+ DzlListStoreAdapter *self = DZL_LIST_STORE_ADAPTER (model);
+ DzlListStoreAdapterPrivate *priv = dzl_list_store_adapter_get_instance_private (self);
+
+ if (parent == NULL && nth < priv->length)
+ {
+ iter->user_data = GINT_TO_POINTER (nth);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+tree_model_iface_init (GtkTreeModelIface *iface)
+{
+ iface->get_flags = dzl_list_store_adapter_get_flags;
+ iface->get_n_columns = dzl_list_store_adapter_get_n_columns;
+ iface->get_iter = dzl_list_store_adapter_get_iter;
+ iface->get_path = dzl_list_store_adapter_get_path;
+ iface->get_value = dzl_list_store_adapter_get_value;
+ iface->iter_next = dzl_list_store_adapter_iter_next;
+ iface->iter_previous = dzl_list_store_adapter_iter_previous;
+ iface->iter_children = dzl_list_store_adapter_iter_children;
+ iface->iter_has_child = dzl_list_store_adapter_iter_has_child;
+ iface->iter_n_children = dzl_list_store_adapter_iter_n_children;
+ iface->iter_nth_child = dzl_list_store_adapter_iter_nth_child;
+ iface->iter_parent = dzl_list_store_adapter_iter_parent;
+}
+
+static void
+dzl_list_store_adapter_items_changed (DzlListStoreAdapter *self,
+ guint position,
+ guint removed,
+ guint added,
+ GListModel *model)
+{
+ DzlListStoreAdapterPrivate *priv = dzl_list_store_adapter_get_instance_private (self);
+ GtkTreePath *path;
+ GtkTreeIter iter = { 0 };
+
+ g_assert (DZL_IS_LIST_STORE_ADAPTER (self));
+ g_assert (G_IS_LIST_MODEL (model));
+
+ priv->length -= removed;
+ priv->length += added;
+
+ path = gtk_tree_path_new_from_indices (position, -1);
+
+ for (guint i = 0; i < removed; i++)
+ gtk_tree_model_row_deleted (GTK_TREE_MODEL (self), path);
+
+ for (guint i = 0; i < added; i++)
+ {
+ iter.user_data = GINT_TO_POINTER (position + i);
+ gtk_tree_model_row_inserted (GTK_TREE_MODEL (self), path, &iter);
+ gtk_tree_path_next (path);
+ }
+
+ gtk_tree_path_free (path);
+}
+
+static void
+dzl_list_store_adapter_bind (DzlListStoreAdapter *self,
+ GListModel *model,
+ DzlSignalGroup *signals)
+{
+ DzlListStoreAdapterPrivate *priv = dzl_list_store_adapter_get_instance_private (self);
+
+ g_assert (DZL_IS_LIST_STORE_ADAPTER (self));
+ g_assert (G_IS_LIST_MODEL (model));
+ g_assert (DZL_IS_SIGNAL_GROUP (signals));
+
+ priv->model = model;
+ priv->type = g_list_model_get_item_type (model);
+ priv->length = g_list_model_get_n_items (model);
+}
+
+static void
+dzl_list_store_adapter_unbind (DzlListStoreAdapter *self,
+ DzlSignalGroup *signals)
+{
+ DzlListStoreAdapterPrivate *priv = dzl_list_store_adapter_get_instance_private (self);
+
+ g_assert (DZL_IS_LIST_STORE_ADAPTER (self));
+ g_assert (DZL_IS_SIGNAL_GROUP (signals));
+
+ priv->model = NULL;
+ priv->length = 0;
+ priv->type = G_TYPE_INVALID;
+}
+
+static void
+dzl_list_store_adapter_finalize (GObject *object)
+{
+ DzlListStoreAdapter *self = (DzlListStoreAdapter *)object;
+ DzlListStoreAdapterPrivate *priv = dzl_list_store_adapter_get_instance_private (self);
+
+ g_clear_object (&priv->signals);
+
+ G_OBJECT_CLASS (dzl_list_store_adapter_parent_class)->finalize (object);
+}
+
+static void
+dzl_list_store_adapter_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ DzlListStoreAdapter *self = DZL_LIST_STORE_ADAPTER (object);
+
+ switch (prop_id)
+ {
+ case PROP_MODEL:
+ g_value_set_object (value, dzl_list_store_adapter_get_model (self));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+dzl_list_store_adapter_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ DzlListStoreAdapter *self = DZL_LIST_STORE_ADAPTER (object);
+
+ switch (prop_id)
+ {
+ case PROP_MODEL:
+ dzl_list_store_adapter_set_model (self, g_value_get_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+dzl_list_store_adapter_class_init (DzlListStoreAdapterClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = dzl_list_store_adapter_finalize;
+ object_class->get_property = dzl_list_store_adapter_get_property;
+ object_class->set_property = dzl_list_store_adapter_set_property;
+
+ properties [PROP_MODEL] =
+ g_param_spec_object ("model",
+ "Model",
+ "The model to be adapted",
+ G_TYPE_LIST_MODEL,
+ (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+dzl_list_store_adapter_init (DzlListStoreAdapter *self)
+{
+ DzlListStoreAdapterPrivate *priv = dzl_list_store_adapter_get_instance_private (self);
+
+ priv->signals = dzl_signal_group_new (G_TYPE_LIST_MODEL);
+
+ dzl_signal_group_connect_swapped (priv->signals,
+ "items-changed",
+ G_CALLBACK (dzl_list_store_adapter_items_changed),
+ self);
+
+ g_signal_connect_swapped (priv->signals,
+ "bind",
+ G_CALLBACK (dzl_list_store_adapter_bind),
+ self);
+
+ g_signal_connect_swapped (priv->signals,
+ "unbind",
+ G_CALLBACK (dzl_list_store_adapter_unbind),
+ self);
+}
+
+DzlListStoreAdapter *
+dzl_list_store_adapter_new (GListModel *model)
+{
+ return g_object_new (DZL_TYPE_LIST_STORE_ADAPTER,
+ "model", model,
+ NULL);
+}
+
+/**
+ * dzl_list_store_adapter_get_model:
+ * @self: A #DzlListStoreAdapter
+ *
+ * Gets the model being adapted.
+ *
+ * Returns: (transfer none): A #GListModel
+ *
+ * Since: 3.26
+ */
+GListModel *
+dzl_list_store_adapter_get_model (DzlListStoreAdapter *self)
+{
+ DzlListStoreAdapterPrivate *priv = dzl_list_store_adapter_get_instance_private (self);
+
+ g_return_val_if_fail (DZL_IS_LIST_STORE_ADAPTER (self), NULL);
+
+ return dzl_signal_group_get_target (priv->signals);
+}
+
+void
+dzl_list_store_adapter_set_model (DzlListStoreAdapter *self,
+ GListModel *model)
+{
+ DzlListStoreAdapterPrivate *priv = dzl_list_store_adapter_get_instance_private (self);
+
+ g_return_if_fail (DZL_IS_LIST_STORE_ADAPTER (self));
+ g_return_if_fail (!model || G_IS_LIST_MODEL (model));
+
+ dzl_signal_group_set_target (priv->signals, model);
+}
diff --git a/src/tree/dzl-list-store-adapter.h b/src/tree/dzl-list-store-adapter.h
new file mode 100644
index 0000000..66c98ca
--- /dev/null
+++ b/src/tree/dzl-list-store-adapter.h
@@ -0,0 +1,42 @@
+/* dzl-list-store-adapter.h
+ *
+ * Copyright (C) 2017 Christian Hergert <chergert redhat com>
+ *
+ * This file is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2.1 of the License, or (at your option)
+ * any later version.
+ *
+ * This file 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 Lesser 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 DZL_LIST_STORE_ADAPTER_H
+#define DZL_LIST_STORE_ADAPTER_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define DZL_TYPE_LIST_STORE_ADAPTER (dzl_list_store_adapter_get_type())
+
+G_DECLARE_DERIVABLE_TYPE (DzlListStoreAdapter, dzl_list_store_adapter, DZL, LIST_STORE_ADAPTER, GObject)
+
+struct _DzlListStoreAdapterClass
+{
+ GObjectClass parent_class;
+};
+
+DzlListStoreAdapter *dzl_list_store_adapter_new (GListModel *model);
+GListModel *dzl_list_store_adapter_get_model (DzlListStoreAdapter *self);
+void dzl_list_store_adapter_set_model (DzlListStoreAdapter *self,
+ GListModel *model);
+
+G_END_DECLS
+
+#endif /* DZL_LIST_STORE_ADAPTER_H */
diff --git a/src/tree/meson.build b/src/tree/meson.build
index 39ec060..4c72fa0 100644
--- a/src/tree/meson.build
+++ b/src/tree/meson.build
@@ -3,12 +3,14 @@ tree_headers = [
'dzl-tree-node.h',
'dzl-tree-types.h',
'dzl-tree.h',
+ 'dzl-list-store-adapter.h',
]
tree_sources = [
'dzl-tree-builder.c',
'dzl-tree.c',
'dzl-tree-node.c',
+ 'dzl-list-store-adapter.c',
]
libdazzle_public_headers += files(tree_headers)
diff --git a/tests/meson.build b/tests/meson.build
index f71c4bf..0098b2d 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -319,4 +319,11 @@ test_directory_reaper = executable('test-directory-reaper', 'test-directory-reap
)
test('test-directory-reaper', test_directory_reaper, env: test_env)
+test_list_store_adapter = executable('test-list-store-adapter', 'test-list-store-adapter.c',
+ c_args: test_cflags,
+ link_args: test_link_args,
+ dependencies: libdazzle_deps + [libdazzle_dep],
+)
+test('test-list-store-adapter', test_list_store_adapter, env: test_env)
+
endif
diff --git a/tests/test-list-store-adapter.c b/tests/test-list-store-adapter.c
new file mode 100644
index 0000000..278e85b
--- /dev/null
+++ b/tests/test-list-store-adapter.c
@@ -0,0 +1,104 @@
+/* test-list-store-adapter.c
+ *
+ * Copyright (C) 2017 Christian Hergert <chergert redhat com>
+ *
+ * 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 <dazzle.h>
+
+static void
+test_list_adapter (void)
+{
+ g_autoptr(GListStore) store = NULL;
+ g_autoptr(DzlListStoreAdapter) adapter = NULL;
+ GtkTreeIter iter;
+ gint n_children;
+
+ store = g_list_store_new (G_TYPE_OBJECT);
+ adapter = dzl_list_store_adapter_new (G_LIST_MODEL (store));
+
+ n_children = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (adapter), NULL);
+ g_assert_cmpint (n_children, ==, 0);
+
+ for (guint i = 0; i < 100; i++)
+ {
+ g_autoptr(GObject) obj = g_object_new (G_TYPE_OBJECT, NULL);
+ g_object_set_data (obj, "ID", GINT_TO_POINTER (i));
+ g_list_store_append (store, obj);
+
+ n_children = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (adapter), NULL);
+ g_assert_cmpint (n_children, ==, i + 1);
+ }
+
+ n_children = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (adapter), NULL);
+ g_assert_cmpint (n_children, ==, 100);
+
+ for (gint i = 99; i > 0; i -= 2)
+ g_list_store_remove (store, i);
+
+ n_children = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (adapter), NULL);
+ g_assert_cmpint (n_children, ==, 50);
+
+ for (guint i = 0; i < 50; i++)
+ {
+ g_autoptr(GObject) obj = g_list_model_get_item (G_LIST_MODEL (store), i);
+ gint id = GPOINTER_TO_INT (g_object_get_data (obj, "ID"));
+ g_assert_cmpint (id, ==, i * 2);
+ }
+
+ for (guint i = 10; i > 0; i--)
+ {
+ g_autoptr(GObject) obj = g_object_new (G_TYPE_OBJECT, NULL);
+ g_object_set_data (obj, "ID", GINT_TO_POINTER (i - 1));
+ g_list_store_insert (store, 20, obj);
+ }
+
+ for (guint i = 0; i < 60; i++)
+ {
+ g_autoptr(GObject) obj = g_list_model_get_item (G_LIST_MODEL (store), i);
+ gint id = GPOINTER_TO_INT (g_object_get_data (obj, "ID"));
+
+ if (i < 20)
+ g_assert_cmpint (id, ==, i * 2);
+ else if (i >= 30)
+ g_assert_cmpint (id, ==, (i-10) * 2);
+ else
+ g_assert_cmpint (id, ==, i - 20);
+ }
+
+ gtk_tree_model_get_iter_first (GTK_TREE_MODEL (adapter), &iter);
+
+ for (guint i = 0; i < 60; i++)
+ {
+ g_autoptr(GObject) obj = NULL;
+ g_autoptr(GObject) obj2 = NULL;
+
+ obj = g_list_model_get_item (G_LIST_MODEL (store), i);
+ gtk_tree_model_get (GTK_TREE_MODEL (adapter), &iter, 0, &obj2, -1);
+
+ g_assert (obj == obj2);
+
+ gtk_tree_model_iter_next (GTK_TREE_MODEL (adapter), &iter);
+ }
+}
+
+gint
+main (gint argc,
+ gchar *argv[])
+{
+ g_test_init (&argc, &argv, NULL);
+ g_test_add_func ("/Dazzle/ListStoreAdapter/basic", test_list_adapter);
+ return g_test_run ();
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]