[gtk/shortcuts-rebased-again: 80/117] shortcuts: Mananage managed shortcuts with a custom model
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/shortcuts-rebased-again: 80/117] shortcuts: Mananage managed shortcuts with a custom model
- Date: Fri, 21 Jun 2019 15:34:14 +0000 (UTC)
commit 081daaf81f591cc9a868d96a00b9ad415e067ebd
Author: Benjamin Otte <otte redhat com>
Date: Mon Aug 20 06:42:22 2018 +0200
shortcuts: Mananage managed shortcuts with a custom model
Reduce the amount of special casing by using a ListModel for global and
managed shortcuts, too.
This way, the ListModel API will work for the ShortcutController in the
GtkShortcutManager and GtkRoot.
The only special case remaining is shortcut activation, which needs to
pass the right widget to the controller in the global/managed case.
gtk/gtkconcatmodel.c | 254 +++++++++++++++++++++++++
gtk/gtkconcatmodelprivate.h | 52 ++++++
gtk/gtkshortcutcontroller.c | 41 ++--
gtk/gtkshortcutcontrollerprivate.h | 3 -
gtk/gtkshortcutmanager.c | 41 ++--
gtk/gtkwidget.c | 13 +-
gtk/meson.build | 1 +
testsuite/gtk/concatmodel.c | 372 +++++++++++++++++++++++++++++++++++++
testsuite/gtk/meson.build | 1 +
9 files changed, 732 insertions(+), 46 deletions(-)
---
diff --git a/gtk/gtkconcatmodel.c b/gtk/gtkconcatmodel.c
new file mode 100644
index 0000000000..d96290e6f3
--- /dev/null
+++ b/gtk/gtkconcatmodel.c
@@ -0,0 +1,254 @@
+/*
+ * Copyright © 2018 Benjamin Otte
+ *
+ * This library 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 library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Benjamin Otte <otte gnome org>
+ */
+
+
+/*
+ * SECTION:gtkconcatmodel
+ * @Short_description: concatenation list model
+ * @Title: GtkConcatModel
+ * @See_also: #GListModel
+ *
+ * #GtkConcatModel is a #GListModel implementation that takes a list of list models
+ * and presents them as one concatenated list.
+ *
+ * Node that all the types of the passed in list models must match the concat model's
+ * type. If they are not, you must use a common ancestor type for the #GtkConcatModel,
+ * %G_TYPE_OBJECT being the ultimate option.
+ **/
+
+#include "config.h"
+
+#include "gtkconcatmodelprivate.h"
+
+struct _GtkConcatModel
+{
+ GObject parent_instance;
+
+ GType item_type;
+ guint n_items;
+
+ GList *models;
+};
+
+struct _GtkConcatModelClass
+{
+ GObjectClass parent_class;
+};
+
+static GType
+gtk_concat_model_list_model_get_item_type (GListModel *list)
+{
+ GtkConcatModel *self = GTK_CONCAT_MODEL (list);
+
+ return self->item_type;
+}
+
+static guint
+gtk_concat_model_list_model_get_n_items (GListModel *list)
+{
+ GtkConcatModel *self = GTK_CONCAT_MODEL (list);
+
+ return self->n_items;
+}
+
+static gpointer
+gtk_concat_model_list_model_get_item (GListModel *list,
+ guint position)
+{
+ GtkConcatModel *self = GTK_CONCAT_MODEL (list);
+ GList *l;
+
+ /* FIXME: Use an RBTree to make this O(log N) */
+ for (l = self->models; l; l = l->next)
+ {
+ guint n = g_list_model_get_n_items (l->data);
+
+ if (position < n)
+ return g_list_model_get_item (l->data, position);
+
+ position -= n;
+ }
+
+ return NULL;
+}
+
+static void
+gtk_concat_model_list_model_init (GListModelInterface *iface)
+{
+ iface->get_item_type = gtk_concat_model_list_model_get_item_type;
+ iface->get_n_items = gtk_concat_model_list_model_get_n_items;
+ iface->get_item = gtk_concat_model_list_model_get_item;
+}
+
+G_DEFINE_TYPE_WITH_CODE (GtkConcatModel, gtk_concat_model,
+ G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, gtk_concat_model_list_model_init))
+
+static void
+gtk_concat_model_items_changed (GListModel *model,
+ guint position,
+ guint removed,
+ guint added,
+ GtkConcatModel *self)
+{
+ GList *l;
+
+ for (l = self->models; l; l = l->next)
+ {
+ if (l->data == model)
+ break;
+
+ position += g_list_model_get_n_items (l->data);
+ }
+
+ self->n_items -= removed;
+ self->n_items += added;
+
+ g_list_model_items_changed (G_LIST_MODEL (self),
+ position,
+ removed,
+ added);
+}
+
+static void
+gtk_concat_model_remove_internal (GtkConcatModel *self,
+ GListModel *model,
+ gboolean emit_signals)
+{
+ guint n_items, position;
+ GList *l;
+
+ position = 0;
+ for (l = self->models; l; l = l->next)
+ {
+ if (l->data == model)
+ break;
+
+ position += g_list_model_get_n_items (l->data);
+ }
+
+ g_return_if_fail (l != NULL);
+
+ self->models = g_list_delete_link (self->models, l);
+ n_items = g_list_model_get_n_items (model);
+ self->n_items -= n_items;
+ g_signal_handlers_disconnect_by_func (model, gtk_concat_model_items_changed, self);
+ g_object_unref (model);
+
+ if (n_items && emit_signals)
+ g_list_model_items_changed (G_LIST_MODEL (self),
+ position,
+ n_items,
+ 0);
+}
+
+static void
+gtk_concat_model_dispose (GObject *object)
+{
+ GtkConcatModel *self = GTK_CONCAT_MODEL (object);
+
+ /* FIXME: Make this work without signal emissions */
+ while (self->models)
+ gtk_concat_model_remove_internal (self, self->models->data, FALSE);
+
+ G_OBJECT_CLASS (gtk_concat_model_parent_class)->dispose (object);
+}
+
+static void
+gtk_concat_model_class_init (GtkConcatModelClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = gtk_concat_model_dispose;
+}
+
+static void
+gtk_concat_model_init (GtkConcatModel *self)
+{
+}
+
+GtkConcatModel *
+gtk_concat_model_new (GType item_type)
+{
+ GtkConcatModel *self;
+
+ g_return_val_if_fail (g_type_is_a (item_type, G_TYPE_OBJECT), NULL);
+
+ self = g_object_new (GTK_TYPE_CONCAT_MODEL, NULL);
+
+ self->item_type = item_type;
+
+ return self;
+}
+
+void
+gtk_concat_model_append (GtkConcatModel *self,
+ GListModel *model)
+{
+ guint n_items;
+
+ g_return_if_fail (GTK_IS_CONCAT_MODEL (self));
+ g_return_if_fail (G_IS_LIST_MODEL (model));
+ g_return_if_fail (g_type_is_a (g_list_model_get_item_type (model), self->item_type));
+
+ g_object_ref (model);
+ g_signal_connect (model, "items-changed", G_CALLBACK (gtk_concat_model_items_changed), self);
+ self->models = g_list_append (self->models, model);
+ n_items = g_list_model_get_n_items (model);
+ self->n_items += n_items;
+
+ if (n_items)
+ g_list_model_items_changed (G_LIST_MODEL (self),
+ self->n_items - n_items,
+ 0,
+ n_items);
+}
+
+void
+gtk_concat_model_remove (GtkConcatModel *self,
+ GListModel *model)
+{
+ g_return_if_fail (GTK_IS_CONCAT_MODEL (self));
+ g_return_if_fail (G_IS_LIST_MODEL (model));
+
+ gtk_concat_model_remove_internal (self, model, TRUE);
+}
+
+GListModel *
+gtk_concat_model_get_model_for_item (GtkConcatModel *self,
+ guint position)
+{
+ GList *l;
+
+ g_return_val_if_fail (GTK_IS_CONCAT_MODEL (self), NULL);
+
+ /* FIXME: Use an RBTree to make this O(log N) */
+ for (l = self->models; l; l = l->next)
+ {
+ guint n = g_list_model_get_n_items (l->data);
+
+ if (position < n)
+ return l->data;
+
+ position -= n;
+ }
+
+ return NULL;
+}
+
diff --git a/gtk/gtkconcatmodelprivate.h b/gtk/gtkconcatmodelprivate.h
new file mode 100644
index 0000000000..005a9f3da8
--- /dev/null
+++ b/gtk/gtkconcatmodelprivate.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright © 2018 Benjamin Otte
+ *
+ * This library 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 library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Benjamin Otte <otte gnome org>
+ */
+
+
+#ifndef __GTK_CONCAT_MODEL_H__
+#define __GTK_CONCAT_MODEL_H__
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_CONCAT_MODEL (gtk_concat_model_get_type ())
+#define GTK_CONCAT_MODEL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_CONCAT_MODEL,
GtkConcatModel))
+#define GTK_CONCAT_MODEL_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_CONCAT_MODEL,
GtkConcatModelClass))
+#define GTK_IS_CONCAT_MODEL(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_CONCAT_MODEL))
+#define GTK_IS_CONCAT_MODEL_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_CONCAT_MODEL))
+#define GTK_CONCAT_MODEL_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_CONCAT_MODEL,
GtkConcatModelClass))
+
+typedef struct _GtkConcatModel GtkConcatModel;
+typedef struct _GtkConcatModelClass GtkConcatModelClass;
+
+GType gtk_concat_model_get_type (void) G_GNUC_CONST;
+
+GtkConcatModel * gtk_concat_model_new (GType item_type);
+
+void gtk_concat_model_append (GtkConcatModel *self,
+ GListModel *model);
+void gtk_concat_model_remove (GtkConcatModel *self,
+ GListModel *model);
+
+GListModel * gtk_concat_model_get_model_for_item (GtkConcatModel *self,
+ guint position);
+
+G_END_DECLS
+
+#endif /* __GTK_CONCAT_MODEL_H__ */
diff --git a/gtk/gtkshortcutcontroller.c b/gtk/gtkshortcutcontroller.c
index b2417d15bc..c1a0d80848 100644
--- a/gtk/gtkshortcutcontroller.c
+++ b/gtk/gtkshortcutcontroller.c
@@ -34,6 +34,7 @@
#include "gtkshortcutcontrollerprivate.h"
+#include "gtkconcatmodelprivate.h"
#include "gtkeventcontrollerprivate.h"
#include "gtkintl.h"
#include "gtkshortcut.h"
@@ -53,7 +54,6 @@ struct _GtkShortcutController
GdkModifierType mnemonics_modifiers;
guint custom_shortcuts : 1;
- guint run_managed : 1;
};
struct _GtkShortcutControllerClass
@@ -212,15 +212,27 @@ gtk_shortcut_controller_finalize (GObject *object)
static gboolean
gtk_shortcut_controller_trigger_shortcut (GtkShortcutController *self,
GtkShortcut *shortcut,
+ guint position,
const GdkEvent *event,
gboolean enable_mnemonics)
{
+ GtkWidget *widget;
+
if (!gtk_shortcut_trigger_trigger (gtk_shortcut_get_trigger (shortcut), event, enable_mnemonics))
return FALSE;
+ widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (self));
+ if (!self->custom_shortcuts &&
+ GTK_IS_CONCAT_MODEL (self->shortcuts))
+ {
+ GListModel *model = gtk_concat_model_get_model_for_item (GTK_CONCAT_MODEL (self->shortcuts), position);
+ if (GTK_IS_SHORTCUT_CONTROLLER (model))
+ widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (model));
+ }
+
return gtk_shortcut_action_activate (gtk_shortcut_get_action (shortcut),
GTK_SHORTCUT_ACTION_EXCLUSIVE, /* FIXME */
- gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (self)),
+ widget,
gtk_shortcut_get_arguments (shortcut));
}
@@ -230,34 +242,18 @@ gtk_shortcut_controller_run_controllers (GtkEventController *controller,
gboolean enable_mnemonics)
{
GtkShortcutController *self = GTK_SHORTCUT_CONTROLLER (controller);
- GtkWidget *widget;
- const GSList *l;
guint i;
for (i = 0; i < g_list_model_get_n_items (self->shortcuts); i++)
{
if (gtk_shortcut_controller_trigger_shortcut (self,
g_list_model_get_item (self->shortcuts, i),
+ i,
event,
enable_mnemonics))
return TRUE;
}
- if (self->run_managed)
- {
- GtkPropagationPhase current_phase = gtk_event_controller_get_propagation_phase (controller);
- widget = gtk_event_controller_get_widget (controller);
-
- for (l = g_object_get_data (G_OBJECT (widget), "gtk-shortcut-controllers"); l; l = l->next)
- {
- if (gtk_event_controller_get_propagation_phase (l->data) != current_phase)
- continue;
-
- if (gtk_shortcut_controller_run_controllers (l->data, event, enable_mnemonics))
- return TRUE;
- }
- }
-
return FALSE;
}
@@ -455,13 +451,6 @@ gtk_shortcut_controller_new_for_model (GListModel *model)
NULL);
}
-void
-gtk_shortcut_controller_set_run_managed (GtkShortcutController *controller,
- gboolean run_managed)
-{
- controller->run_managed = run_managed;
-}
-
/**
* gtk_shortcut_controller_add_shortcut:
* @self: the controller
diff --git a/gtk/gtkshortcutcontrollerprivate.h b/gtk/gtkshortcutcontrollerprivate.h
index a7086cd678..db1b52cecc 100644
--- a/gtk/gtkshortcutcontrollerprivate.h
+++ b/gtk/gtkshortcutcontrollerprivate.h
@@ -22,9 +22,6 @@
#include "gtkshortcutcontroller.h"
-void gtk_shortcut_controller_set_run_managed (GtkShortcutController *controller,
- gboolean
run_managed);
-
void gtk_shortcut_controller_root (GtkShortcutController *controller);
void gtk_shortcut_controller_unroot (GtkShortcutController *controller);
diff --git a/gtk/gtkshortcutmanager.c b/gtk/gtkshortcutmanager.c
index d3036f8772..bf9ba15052 100644
--- a/gtk/gtkshortcutmanager.c
+++ b/gtk/gtkshortcutmanager.c
@@ -21,36 +21,51 @@
#include "gtkshortcutmanager.h"
+#include "gtkconcatmodelprivate.h"
+
G_DEFINE_INTERFACE (GtkShortcutManager, gtk_shortcut_manager, G_TYPE_OBJECT)
-static void
-complain_if_reached (gpointer should_be_gone)
+static GtkConcatModel *
+gtk_shortcut_manager_get_model (GtkShortcutManager *self,
+ GtkPropagationPhase phase)
{
- g_critical ("Shortcut controllers failed to clean up.");
+ switch (phase)
+ {
+ case GTK_PHASE_CAPTURE:
+ return g_object_get_data (G_OBJECT (self), "gtk-shortcut-manager-capture");
+ case GTK_PHASE_BUBBLE:
+ return g_object_get_data (G_OBJECT (self), "gtk-shortcut-manager-bubble");
+ case GTK_PHASE_NONE:
+ case GTK_PHASE_TARGET:
+ return NULL;
+ default:
+ g_assert_not_reached ();
+ return NULL;
+ }
}
static void
gtk_shortcut_manager_default_add_controller (GtkShortcutManager *self,
GtkShortcutController *controller)
{
- GSList *controllers;
+ GtkConcatModel *model;
- controllers = g_object_steal_data (G_OBJECT (self), "gtk-shortcut-controllers");
- controllers = g_slist_prepend (controllers, g_object_ref (controller));
- g_object_set_data_full (G_OBJECT (self), "gtk-shortcut-controllers", controllers, complain_if_reached);
+ model = gtk_shortcut_manager_get_model (self,
+ gtk_event_controller_get_propagation_phase (GTK_EVENT_CONTROLLER
(controller)));
+ if (model)
+ gtk_concat_model_append (model, G_LIST_MODEL (controller));
}
static void
gtk_shortcut_manager_default_remove_controller (GtkShortcutManager *self,
GtkShortcutController *controller)
{
- GSList *controllers;
+ GtkConcatModel *model;
- controllers = g_object_steal_data (G_OBJECT (self), "gtk-shortcut-controllers");
- controllers = g_slist_remove (controllers, controller);
- if (controllers)
- g_object_set_data_full (G_OBJECT (self), "gtk-shortcut-controllers", controllers, complain_if_reached);
- g_object_unref (controller);
+ model = gtk_shortcut_manager_get_model (self,
+ gtk_event_controller_get_propagation_phase (GTK_EVENT_CONTROLLER
(controller)));
+ if (model)
+ gtk_concat_model_remove (model, G_LIST_MODEL (controller));
}
static void
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index 11b62d78cc..a45b706a4f 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -31,6 +31,7 @@
#include "gtkapplicationprivate.h"
#include "gtkbuildable.h"
#include "gtkbuilderprivate.h"
+#include "gtkconcatmodelprivate.h"
#include "gtkcontainerprivate.h"
#include "gtkcssboxesprivate.h"
#include "gtkcssfiltervalueprivate.h"
@@ -2876,13 +2877,17 @@ gtk_widget_init (GTypeInstance *instance, gpointer g_class)
if (g_type_is_a (G_TYPE_FROM_CLASS (g_class), GTK_TYPE_SHORTCUT_MANAGER))
{
- controller = gtk_shortcut_controller_new ();
- gtk_shortcut_controller_set_run_managed (GTK_SHORTCUT_CONTROLLER (controller), TRUE);
+ GtkConcatModel *model;
+
+ model = gtk_concat_model_new (GTK_TYPE_SHORTCUT);
+ g_object_set_data_full (G_OBJECT (widget), "gtk-shortcut-manager-bubble", model, g_object_unref);
+ controller = gtk_shortcut_controller_new_for_model (G_LIST_MODEL (model));
gtk_widget_add_controller (widget, controller);
- controller = gtk_shortcut_controller_new ();
+ model = gtk_concat_model_new (GTK_TYPE_SHORTCUT);
+ g_object_set_data_full (G_OBJECT (widget), "gtk-shortcut-manager-capture", model, g_object_unref);
+ controller = gtk_shortcut_controller_new_for_model (G_LIST_MODEL (model));
gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_CAPTURE);
- gtk_shortcut_controller_set_run_managed (GTK_SHORTCUT_CONTROLLER (controller), TRUE);
gtk_widget_add_controller (widget, controller);
}
diff --git a/gtk/meson.build b/gtk/meson.build
index fd163a5aac..36b7cff796 100644
--- a/gtk/meson.build
+++ b/gtk/meson.build
@@ -196,6 +196,7 @@ gtk_public_sources = files([
'gtkcombobox.c',
'gtkcomboboxtext.c',
'gtkcomposetable.c',
+ 'gtkconcatmodel.c',
'gtkcontainer.c',
'gtkcssprovider.c',
'gtkdialog.c',
diff --git a/testsuite/gtk/concatmodel.c b/testsuite/gtk/concatmodel.c
new file mode 100644
index 0000000000..3e6292b536
--- /dev/null
+++ b/testsuite/gtk/concatmodel.c
@@ -0,0 +1,372 @@
+/* GtkRBTree tests.
+ *
+ * Copyright (C) 2011, Red Hat, Inc.
+ * Authors: Benjamin Otte <otte gnome org>
+ *
+ * This library 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 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <locale.h>
+
+#include "../../gtk/gtkconcatmodelprivate.h"
+
+/* _gtk_rbtree_test */
+
+static GQuark number_quark;
+static GQuark changes_quark;
+
+static guint
+get (GListModel *model,
+ guint position)
+{
+ GObject *object = g_list_model_get_item (model, position);
+ g_assert (object != NULL);
+ return GPOINTER_TO_UINT (g_object_get_qdata (object, number_quark));
+}
+
+static char *
+model_to_string (GListModel *model)
+{
+ GString *string = g_string_new (NULL);
+ guint i;
+
+ for (i = 0; i < g_list_model_get_n_items (model); i++)
+ {
+ if (i > 0)
+ g_string_append (string, " ");
+ g_string_append_printf (string, "%u", get (model, i));
+ }
+
+ return g_string_free (string, FALSE);
+}
+
+static void
+add (GListStore *store,
+ guint number)
+{
+ GObject *object;
+
+ /* o cannot be differentiated from NULL, so don't use it */
+ g_assert (number != 0);
+
+ object = g_object_new (G_TYPE_OBJECT, NULL);
+ g_object_set_qdata (object, number_quark, GUINT_TO_POINTER (number));
+ g_list_store_append (store, object);
+ g_object_unref (object);
+}
+
+static void
+remove (GListStore *store,
+ guint position)
+{
+ g_list_store_remove (store, position);
+}
+
+#define assert_model(model, expected) G_STMT_START{ \
+ char *s = model_to_string (G_LIST_MODEL (model)); \
+ if (!g_str_equal (s, expected)) \
+ g_assertion_message_cmpstr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
+ #model " == " #expected, s, "==", expected); \
+ g_free (s); \
+}G_STMT_END
+
+#define assert_changes(model, expected) G_STMT_START{ \
+ GString *changes = g_object_get_qdata (G_OBJECT (model), changes_quark); \
+ if (!g_str_equal (changes->str, expected)) \
+ g_assertion_message_cmpstr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
+ #model " == " #expected, changes->str, "==", expected); \
+ g_string_set_size (changes, 0); \
+}G_STMT_END
+
+static GListStore *
+new_empty_store (void)
+{
+ return g_list_store_new (G_TYPE_OBJECT);
+}
+
+static GListStore *
+new_store (guint start,
+ guint end)
+{
+ GListStore *store = new_empty_store ();
+ guint i;
+
+ for (i = start; i <= end; i++)
+ add (store, i);
+
+ return store;
+}
+
+static void
+items_changed (GListModel *model,
+ guint position,
+ guint removed,
+ guint added,
+ GString *changes)
+{
+ g_assert (removed != 0 || added != 0);
+
+ if (changes->len)
+ g_string_append (changes, ", ");
+
+ if (removed == 1 && added == 0)
+ {
+ g_string_append_printf (changes, "-%u", position);
+ }
+ else if (removed == 0 && added == 1)
+ {
+ g_string_append_printf (changes, "+%u", position);
+ }
+ else
+ {
+ g_string_append_printf (changes, "%u", position);
+ if (removed > 0)
+ g_string_append_printf (changes, "-%u", removed);
+ if (added > 0)
+ g_string_append_printf (changes, "+%u", added);
+ }
+}
+
+static void
+free_changes (gpointer data)
+{
+ GString *changes = data;
+
+ /* all changes must have been checked via assert_changes() before */
+ g_assert_cmpstr (changes->str, ==, "");
+
+ g_string_free (changes, TRUE);
+}
+
+static GtkConcatModel *
+new_model (void)
+{
+ GtkConcatModel *model = gtk_concat_model_new (G_TYPE_OBJECT);
+ GString *changes;
+
+ changes = g_string_new ("");
+ g_object_set_qdata_full (G_OBJECT(model), changes_quark, changes, free_changes);
+ g_signal_connect (model, "items-changed", G_CALLBACK (items_changed), changes);
+
+ return model;
+}
+
+static void
+test_append (void)
+{
+ GListStore *store = new_store (1, 3);
+ GtkConcatModel *concat = new_model ();
+
+ gtk_concat_model_append (concat, G_LIST_MODEL (store));
+
+ assert_model (concat, "1 2 3");
+ assert_changes (concat, "0+3");
+
+ g_object_unref (store);
+ g_object_unref (concat);
+}
+
+static void
+test_append_and_add (void)
+{
+ GListStore *store = new_empty_store ();
+ GtkConcatModel *concat = new_model ();
+
+ gtk_concat_model_append (concat, G_LIST_MODEL (store));
+
+ add (store, 1);
+ add (store, 2);
+ add (store, 3);
+ assert_model (concat, "1 2 3");
+ assert_changes (concat, "+0, +1, +2");
+
+ g_object_unref (store);
+ g_object_unref (concat);
+}
+
+static void
+test_append_and_remove (void)
+{
+ GListStore *store = new_store (1, 3);
+ GtkConcatModel *concat = new_model ();
+
+ gtk_concat_model_append (concat, G_LIST_MODEL (store));
+ gtk_concat_model_remove (concat, G_LIST_MODEL (store));
+
+ assert_model (concat, "");
+ assert_changes (concat, "0+3, 0-3");
+
+ /* Check that all signal handlers are gone */
+ g_list_store_remove_all (store);
+
+ g_object_unref (store);
+ g_object_unref (concat);
+}
+
+static void
+test_append_and_remove_items (void)
+{
+ GListStore *store = new_empty_store ();
+ GtkConcatModel *concat = new_model ();
+
+ gtk_concat_model_append (concat, G_LIST_MODEL (store));
+
+ add (store, 1);
+ add (store, 2);
+ add (store, 3);
+ remove (store, 0);
+ remove (store, 1);
+ remove (store, 0);
+
+ assert_model (concat, "");
+ assert_changes (concat, "+0, +1, +2, -0, -1, -0");
+
+ g_object_unref (store);
+ g_object_unref (concat);
+}
+
+static void
+test_append_many (void)
+{
+ GListStore *store[5] = { new_store (1, 3), new_store (4, 4), new_store (5, 10), new_empty_store (),
new_store (11, 20) };
+ GtkConcatModel *concat = new_model ();
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (store); i++)
+ gtk_concat_model_append (concat, G_LIST_MODEL (store[i]));
+
+ assert_model (concat, "1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20");
+ assert_changes (concat, "0+3, +3, 4+6, 10+10");
+
+ for (i = 0; i < G_N_ELEMENTS (store); i++)
+ g_object_unref (store[i]);
+ g_object_unref (concat);
+}
+
+static void
+test_append_many_and_add (void)
+{
+ GListStore *store[3];
+ GtkConcatModel *concat = new_model ();
+ guint i, j;
+
+ for (i = 0; i < G_N_ELEMENTS (store); i++)
+ {
+ store[i] = new_empty_store ();
+ gtk_concat_model_append (concat, G_LIST_MODEL (store[i]));
+ }
+
+ for (i = 0; i < G_N_ELEMENTS (store); i++)
+ {
+ for (j = 0; j < G_N_ELEMENTS (store); j++)
+ {
+ add (store[(i + j) % G_N_ELEMENTS (store)], i * 3 + j + 1);
+ }
+ }
+
+ assert_model (concat, "1 6 8 2 4 9 3 5 7");
+ assert_changes (concat, "+0, +1, +2, +2, +4, +1, +6, +2, +5");
+
+ for (i = 0; i < G_N_ELEMENTS (store); i++)
+ g_object_unref (store[i]);
+ g_object_unref (concat);
+}
+
+static void
+test_append_many_and_remove (void)
+{
+ GListStore *store[5];
+ GtkConcatModel *concat = new_model ();
+ guint i;
+
+ store[0] = new_empty_store ();
+ gtk_concat_model_append (concat, G_LIST_MODEL (store[0]));
+ for (i = 1; i < G_N_ELEMENTS (store); i++)
+ {
+ store[i] = new_store (i * (i - 1) / 2 + 1, i * (i + 1) / 2);
+ gtk_concat_model_append (concat, G_LIST_MODEL (store[i]));
+ }
+
+ assert_model (concat, "1 2 3 4 5 6 7 8 9 10");
+ assert_changes (concat, "+0, 1+2, 3+3, 6+4");
+
+ for (i = 0; i < G_N_ELEMENTS (store); i++)
+ {
+ gtk_concat_model_remove (concat, G_LIST_MODEL (store[(3 * i) % G_N_ELEMENTS (store)]));
+ }
+
+ assert_model (concat, "");
+ assert_changes (concat, "3-3, -0, 2-4, 0-2");
+
+ for (i = 0; i < G_N_ELEMENTS (store); i++)
+ {
+ g_list_store_remove_all (store[i]);
+ g_object_unref (store[i]);
+ }
+
+ g_object_unref (concat);
+}
+
+static void
+test_append_many_and_remove_items (void)
+{
+ GListStore *store[5];
+ GtkConcatModel *concat = new_model ();
+ guint i;
+
+ store[0] = new_empty_store ();
+ gtk_concat_model_append (concat, G_LIST_MODEL (store[0]));
+ for (i = 1; i < G_N_ELEMENTS (store); i++)
+ {
+ store[i] = new_store (i * (i - 1) / 2 + 1, i * (i + 1) / 2);
+ gtk_concat_model_append (concat, G_LIST_MODEL (store[i]));
+ }
+
+ assert_model (concat, "1 2 3 4 5 6 7 8 9 10");
+ assert_changes (concat, "+0, 1+2, 3+3, 6+4");
+
+ for (i = 1; i < G_N_ELEMENTS (store); i++)
+ {
+ remove (store[i], 3 % i);
+ }
+
+ assert_model (concat, "2 5 6 7 8 9");
+ assert_changes (concat, "-0, -1, -1, -6");
+
+ for (i = 0; i < G_N_ELEMENTS (store); i++)
+ g_object_unref (store[i]);
+ g_object_unref (concat);
+}
+
+int
+main (int argc, char *argv[])
+{
+ g_test_init (&argc, &argv, NULL);
+ setlocale (LC_ALL, "C");
+ g_test_bug_base ("http://bugzilla.gnome.org/show_bug.cgi?id=%s");
+
+ number_quark = g_quark_from_static_string ("Hell and fire was spawned to be released.");
+ changes_quark = g_quark_from_static_string ("What did I see? Can I believe what I saw?");
+
+ g_test_add_func ("/compatmodel/append", test_append);
+ g_test_add_func ("/compatmodel/append_and_add", test_append_and_add);
+ g_test_add_func ("/compatmodel/append_and_remove", test_append_and_remove);
+ g_test_add_func ("/compatmodel/append_and_remove_items", test_append_and_remove_items);
+ g_test_add_func ("/compatmodel/append_many", test_append_many);
+ g_test_add_func ("/compatmodel/append_many_and_add", test_append_many_and_add);
+ g_test_add_func ("/compatmodel/append_many_and_remove", test_append_many_and_remove);
+ g_test_add_func ("/compatmodel/append_many_and_remove_items", test_append_many_and_remove_items);
+
+ return g_test_run ();
+}
diff --git a/testsuite/gtk/meson.build b/testsuite/gtk/meson.build
index 88016781ed..2dd1b584dc 100644
--- a/testsuite/gtk/meson.build
+++ b/testsuite/gtk/meson.build
@@ -17,6 +17,7 @@ tests = [
['builderparser'],
['cellarea'],
['check-icon-names'],
+ ['concatmodel', ['../../gtk/gtkconcatmodel.c'], ['-DGTK_COMPILATION', '-UG_ENABLE_DEBUG']],
['cssprovider'],
['rbtree-crash', ['../../gtk/gtkrbtree.c'], ['-DGTK_COMPILATION', '-UG_ENABLE_DEBUG']],
['defaultvalue'],
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]