shortcuts: Mananage managed shortcuts with a custom model

Reduce the amount of special casing by using a list model
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.
This commit is contained in:
Benjamin Otte
2018-08-20 06:42:22 +02:00
committed by Matthias Clasen
parent a10a295515
commit 0c81698911
9 changed files with 732 additions and 46 deletions

254
gtk/gtkconcatmodel.c Normal file
View File

@@ -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;
}

View File

@@ -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__ */

View File

@@ -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,
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));
}
@@ -232,34 +244,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, x, y, enable_mnemonics))
return TRUE;
}
}
return FALSE;
}
@@ -483,13 +479,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

View File

@@ -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);

View File

@@ -20,6 +20,7 @@
#include "config.h"
#include "gtkshortcutmanager.h"
#include "gtkconcatmodelprivate.h"
/**
* SECTION:gtkshortcutmanager
@@ -29,36 +30,50 @@
* The GtkShortcutManager interface is used to implement
* shortcut scopes.
*/
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

View File

@@ -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"
@@ -2462,13 +2463,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);
}

View File

@@ -199,6 +199,7 @@ gtk_public_sources = files([
'gtkcombobox.c',
'gtkcomboboxtext.c',
'gtkcomposetable.c',
'gtkconcatmodel.c',
'gtkconstraintguide.c',
'gtkconstraintlayout.c',
'gtkconstraint.c',

372
testsuite/gtk/concatmodel.c Normal file
View File

@@ -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 ();
}

View File

@@ -17,6 +17,7 @@ tests = [
['builderparser'],
['cellarea'],
['check-icon-names'],
['concatmodel', ['../../gtk/gtkconcatmodel.c'], ['-DGTK_COMPILATION', '-UG_ENABLE_DEBUG']],
['constraint-solver', [
'../../gtk/gtkconstraintsolver.c',
'../../gtk/gtkconstraintexpression.c',