diff --git a/gtk/gtkfilterlistmodel.c b/gtk/gtkfilterlistmodel.c index a41b19fa6e..840115162d 100644 --- a/gtk/gtkfilterlistmodel.c +++ b/gtk/gtkfilterlistmodel.c @@ -190,6 +190,39 @@ gtk_filter_list_model_get_section (GtkSectionModel *model, *out_end = *out_start + gtk_bitset_get_size_in_range (self->matches, start, end - 1); } +static void +gtk_filter_list_model_sections_changed_cb (GtkSectionModel *model, + unsigned int position, + unsigned int n_items, + gpointer user_data) +{ + GtkFilterListModel *self = GTK_FILTER_LIST_MODEL (user_data); + unsigned int start, end; + + switch (self->strictness) + { + case GTK_FILTER_MATCH_NONE: + return; + + case GTK_FILTER_MATCH_ALL: + gtk_section_model_sections_changed (GTK_SECTION_MODEL (self), position, n_items); + break; + + case GTK_FILTER_MATCH_SOME: + if (position > 0) + start = gtk_bitset_get_size_in_range (self->matches, 0, position - 1); + else + start = 0; + end = gtk_bitset_get_size_in_range (self->matches, 0, position + n_items - 1); + if (end - start > 0) + gtk_section_model_sections_changed (GTK_SECTION_MODEL (self), start, end - start); + break; + + default: + g_assert_not_reached (); + } +} + static void gtk_filter_list_model_section_model_init (GtkSectionModelInterface *iface) { @@ -465,6 +498,7 @@ gtk_filter_list_model_clear_model (GtkFilterListModel *self) gtk_filter_list_model_stop_filtering (self); g_signal_handlers_disconnect_by_func (self->model, gtk_filter_list_model_items_changed_cb, self); + g_signal_handlers_disconnect_by_func (self->model, gtk_filter_list_model_sections_changed_cb, self); g_clear_object (&self->model); if (self->matches) gtk_bitset_remove_all (self->matches); @@ -829,6 +863,8 @@ gtk_filter_list_model_set_model (GtkFilterListModel *self, { self->model = g_object_ref (model); g_signal_connect (model, "items-changed", G_CALLBACK (gtk_filter_list_model_items_changed_cb), self); + if (GTK_IS_SECTION_MODEL (model)) + g_signal_connect (model, "sections-changed", G_CALLBACK (gtk_filter_list_model_sections_changed_cb), self); if (removed == 0) { self->strictness = GTK_FILTER_MATCH_NONE; diff --git a/gtk/gtkmultiselection.c b/gtk/gtkmultiselection.c index f549541baa..7e54d647b3 100644 --- a/gtk/gtkmultiselection.c +++ b/gtk/gtkmultiselection.c @@ -291,6 +291,17 @@ gtk_multi_selection_items_changed_cb (GListModel *model, g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_N_ITEMS]); } +static void +gtk_multi_selection_sections_changed_cb (GtkSectionModel *model, + unsigned int position, + unsigned int n_items, + gpointer user_data) +{ + GtkMultiSelection *self = GTK_MULTI_SELECTION (user_data); + + gtk_section_model_sections_changed (GTK_SECTION_MODEL (self), position, n_items); +} + static void gtk_multi_selection_clear_model (GtkMultiSelection *self) { @@ -300,6 +311,9 @@ gtk_multi_selection_clear_model (GtkMultiSelection *self) g_signal_handlers_disconnect_by_func (self->model, gtk_multi_selection_items_changed_cb, self); + g_signal_handlers_disconnect_by_func (self->model, + gtk_multi_selection_sections_changed_cb, + self); g_clear_object (&self->model); } @@ -490,6 +504,9 @@ gtk_multi_selection_set_model (GtkMultiSelection *self, "items-changed", G_CALLBACK (gtk_multi_selection_items_changed_cb), self); + if (GTK_IS_SECTION_MODEL (self->model)) + g_signal_connect (self->model, "sections-changed", + G_CALLBACK (gtk_multi_selection_sections_changed_cb), self); gtk_multi_selection_items_changed_cb (self->model, 0, n_items_before, diff --git a/gtk/gtknoselection.c b/gtk/gtknoselection.c index 20bff24df2..872ee2eaad 100644 --- a/gtk/gtknoselection.c +++ b/gtk/gtknoselection.c @@ -33,6 +33,8 @@ * * This model is meant to be used as a simple wrapper around a `GListModel` * when a `GtkSelectionModel` is required. + * + * `GtkNoSelection` passes through sections from the underlying model. */ struct _GtkNoSelection { @@ -152,15 +154,29 @@ gtk_no_selection_items_changed_cb (GListModel *model, g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_N_ITEMS]); } +static void +gtk_no_selection_sections_changed_cb (GtkSectionModel *model, + unsigned int position, + unsigned int n_items, + gpointer user_data) +{ + GtkNoSelection *self = GTK_NO_SELECTION (user_data); + + gtk_section_model_sections_changed (GTK_SECTION_MODEL (self), position, n_items); +} + static void gtk_no_selection_clear_model (GtkNoSelection *self) { if (self->model == NULL) return; - g_signal_handlers_disconnect_by_func (self->model, + g_signal_handlers_disconnect_by_func (self->model, gtk_no_selection_items_changed_cb, self); + g_signal_handlers_disconnect_by_func (self->model, + gtk_no_selection_sections_changed_cb, + self); g_clear_object (&self->model); } @@ -345,6 +361,9 @@ gtk_no_selection_set_model (GtkNoSelection *self, self->model = g_object_ref (model); g_signal_connect (self->model, "items-changed", G_CALLBACK (gtk_no_selection_items_changed_cb), self); + if (GTK_IS_SECTION_MODEL (self->model)) + g_signal_connect (self->model, "sections-changed", + G_CALLBACK (gtk_no_selection_sections_changed_cb), self); n_items_after = g_list_model_get_n_items (self->model); } else diff --git a/gtk/gtksingleselection.c b/gtk/gtksingleselection.c index 07f0c7eaee..e4a19176a9 100644 --- a/gtk/gtksingleselection.c +++ b/gtk/gtksingleselection.c @@ -300,15 +300,29 @@ gtk_single_selection_items_changed_cb (GListModel *model, g_object_thaw_notify (G_OBJECT (self)); } +static void +gtk_single_selection_sections_changed_cb (GtkSectionModel *model, + unsigned int position, + unsigned int n_items, + gpointer user_data) +{ + GtkSingleSelection *self = GTK_SINGLE_SELECTION (user_data); + + gtk_section_model_sections_changed (GTK_SECTION_MODEL (self), position, n_items); +} + static void gtk_single_selection_clear_model (GtkSingleSelection *self) { if (self->model == NULL) return; - g_signal_handlers_disconnect_by_func (self->model, + g_signal_handlers_disconnect_by_func (self->model, gtk_single_selection_items_changed_cb, self); + g_signal_handlers_disconnect_by_func (self->model, + gtk_single_selection_sections_changed_cb, + self); g_clear_object (&self->model); } @@ -558,7 +572,7 @@ gtk_single_selection_set_model (GtkSingleSelection *self, return; g_object_freeze_notify (G_OBJECT (self)); - + n_items_before = self->model ? g_list_model_get_n_items (self->model) : 0; gtk_single_selection_clear_model (self); @@ -567,6 +581,9 @@ gtk_single_selection_set_model (GtkSingleSelection *self, self->model = g_object_ref (model); g_signal_connect (self->model, "items-changed", G_CALLBACK (gtk_single_selection_items_changed_cb), self); + if (GTK_IS_SECTION_MODEL (self->model)) + g_signal_connect (self->model, "sections-changed", + G_CALLBACK (gtk_single_selection_sections_changed_cb), self); gtk_single_selection_items_changed_cb (self->model, 0, n_items_before, diff --git a/gtk/gtkslicelistmodel.c b/gtk/gtkslicelistmodel.c index 232dbe5aac..98bcee025d 100644 --- a/gtk/gtkslicelistmodel.c +++ b/gtk/gtkslicelistmodel.c @@ -20,6 +20,7 @@ #include "config.h" #include "gtkslicelistmodel.h" +#include "gtksectionmodelprivate.h" #include "gtkprivate.h" @@ -31,6 +32,8 @@ * This is useful when implementing paging by setting the size to the number * of elements per page and updating the offset whenever a different page is * opened. + * + * `GtkSliceListModel` passes through sections from the underlying model. */ #define DEFAULT_SIZE 10 @@ -52,8 +55,6 @@ struct _GtkSliceListModel GListModel *model; guint offset; guint size; - - guint n_items; }; struct _GtkSliceListModelClass @@ -111,8 +112,69 @@ gtk_slice_list_model_model_init (GListModelInterface *iface) iface->get_item = gtk_slice_list_model_get_item; } +static void +gtk_slice_list_model_get_section (GtkSectionModel *model, + guint position, + guint *start, + guint *end) +{ + GtkSliceListModel *self = GTK_SLICE_LIST_MODEL (model); + unsigned int n_items; + + n_items = g_list_model_get_n_items (G_LIST_MODEL (self)); + if (position >= n_items) + { + *start = n_items; + *end = G_MAXUINT; + } + else + { + gtk_list_model_get_section (self->model, position + self->offset, start, end); + + *start = MAX (*start, self->offset) - self->offset; + *end = MIN (*end - self->offset, n_items); + } +} + +static void +gtk_slice_list_model_sections_changed_cb (GtkSectionModel *model, + unsigned int position, + unsigned int n_items, + gpointer user_data) +{ + GtkSliceListModel *self = GTK_SLICE_LIST_MODEL (user_data); + unsigned int start = position; + unsigned int end = position + n_items; + unsigned int size; + + if (end <= self->offset) + return; + + size = g_list_model_get_n_items (G_LIST_MODEL (self)); + + end = MIN (end - self->offset, size); + + if (start <= self->offset) + start = 0; + else + start = start - self->offset; + + if (start >= size) + return; + + gtk_section_model_sections_changed (GTK_SECTION_MODEL (self), start, end - start); +} + +static void +gtk_slice_list_model_section_model_init (GtkSectionModelInterface *iface) +{ + iface->get_section = gtk_slice_list_model_get_section; +} + G_DEFINE_TYPE_WITH_CODE (GtkSliceListModel, gtk_slice_list_model, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, gtk_slice_list_model_model_init)) + G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, gtk_slice_list_model_model_init) + G_IMPLEMENT_INTERFACE (GTK_TYPE_SECTION_MODEL, gtk_slice_list_model_section_model_init)) + static void gtk_slice_list_model_items_changed_cb (GListModel *model, @@ -238,6 +300,7 @@ gtk_slice_list_model_clear_model (GtkSliceListModel *self) if (self->model == NULL) return; + g_signal_handlers_disconnect_by_func (self->model, gtk_slice_list_model_sections_changed_cb, self); g_signal_handlers_disconnect_by_func (self->model, gtk_slice_list_model_items_changed_cb, self); g_clear_object (&self->model); } @@ -387,6 +450,9 @@ gtk_slice_list_model_set_model (GtkSliceListModel *self, self->model = g_object_ref (model); g_signal_connect (model, "items-changed", G_CALLBACK (gtk_slice_list_model_items_changed_cb), self); added = g_list_model_get_n_items (G_LIST_MODEL (self)); + + if (GTK_IS_SECTION_MODEL (model)) + g_signal_connect (model, "sections-changed", G_CALLBACK (gtk_slice_list_model_sections_changed_cb), self); } else { diff --git a/gtk/gtksortlistmodel.c b/gtk/gtksortlistmodel.c index becfec012e..61ca5a8c64 100644 --- a/gtk/gtksortlistmodel.c +++ b/gtk/gtksortlistmodel.c @@ -873,9 +873,10 @@ gtk_sort_list_model_get_property (GObject *object, } static void -gtk_sort_list_model_sorter_changed_cb (GtkSorter *sorter, - int change, - GtkSortListModel *self) +gtk_sort_list_model_sorter_changed (GtkSorter *sorter, + int change, + GtkSortListModel *self, + gboolean sections_changed) { guint pos, n_items; @@ -928,8 +929,25 @@ gtk_sort_list_model_sorter_changed_cb (GtkSorter *sorter, gtk_sort_list_model_clear_items (self, &pos, &n_items); } - if (n_items > 0) - g_list_model_items_changed (G_LIST_MODEL (self), pos, n_items, n_items); + if (sections_changed && self->n_items > 0) + { + if (n_items > 0) + g_list_model_items_changed (G_LIST_MODEL (self), 0, self->n_items, self->n_items); + else + gtk_section_model_sections_changed (GTK_SECTION_MODEL (self), 0, self->n_items); + } + else if (n_items > 0) + { + g_list_model_items_changed (G_LIST_MODEL (self), pos, n_items, n_items); + } +} + +static void +gtk_sort_list_model_sorter_changed_cb (GtkSorter *sorter, + int change, + GtkSortListModel *self) +{ + gtk_sort_list_model_sorter_changed (sorter, change, self, FALSE); } static void @@ -955,7 +973,8 @@ gtk_sort_list_model_clear_real_sorter (GtkSortListModel *self) } static void -gtk_sort_list_model_ensure_real_sorter (GtkSortListModel *self) +gtk_sort_list_model_ensure_real_sorter (GtkSortListModel *self, + gboolean sections_changed) { if (self->sorter) { @@ -980,7 +999,7 @@ gtk_sort_list_model_ensure_real_sorter (GtkSortListModel *self) if (self->real_sorter) g_signal_connect (self->real_sorter, "changed", G_CALLBACK (gtk_sort_list_model_sorter_changed_cb), self); - gtk_sort_list_model_sorter_changed_cb (self->real_sorter, GTK_SORTER_CHANGE_DIFFERENT, self); + gtk_sort_list_model_sorter_changed (self->real_sorter, GTK_SORTER_CHANGE_DIFFERENT, self, sections_changed); } static void @@ -1202,7 +1221,7 @@ gtk_sort_list_model_set_sorter (GtkSortListModel *self, gtk_sort_list_model_clear_real_sorter (self); g_set_object (&self->sorter, sorter); - gtk_sort_list_model_ensure_real_sorter (self); + gtk_sort_list_model_ensure_real_sorter (self, FALSE); g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SORTER]); } @@ -1244,7 +1263,7 @@ gtk_sort_list_model_set_section_sorter (GtkSortListModel *self, gtk_sort_list_model_clear_real_sorter (self); g_set_object (&self->section_sorter, sorter); - gtk_sort_list_model_ensure_real_sorter (self); + gtk_sort_list_model_ensure_real_sorter (self, TRUE); g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SECTION_SORTER]); } diff --git a/testsuite/gtk/filterlistmodel.c b/testsuite/gtk/filterlistmodel.c index 9d48910313..22a49ab1bc 100644 --- a/testsuite/gtk/filterlistmodel.c +++ b/testsuite/gtk/filterlistmodel.c @@ -478,6 +478,17 @@ filter_func (gpointer item, return s[0] == s[1]; } +static void +sections_changed (GtkSectionModel *model, + unsigned int start, + unsigned int end, + gpointer user_data) +{ + gboolean *got_it = user_data; + + *got_it = TRUE; +} + static void test_sections (void) { @@ -499,6 +510,7 @@ test_sections (void) guint s, e; GtkFilterListModel *filtered; GtkFilter *filter; + gboolean got_it = FALSE; list = gtk_string_list_new (strings); sorter = GTK_SORTER (gtk_string_sorter_new (gtk_property_expression_new (GTK_TYPE_STRING_OBJECT, NULL, "string"))); @@ -541,6 +553,10 @@ test_sections (void) g_assert_cmpint (s, ==, 3); g_assert_cmpint (e, ==, 4); + g_signal_connect (filtered, "sections-changed", G_CALLBACK (sections_changed), &got_it); + gtk_sort_list_model_set_section_sorter (GTK_SORT_LIST_MODEL (sorted), NULL); + g_assert_true (got_it); + g_object_unref (filtered); g_object_unref (sorted); } diff --git a/testsuite/gtk/multiselection.c b/testsuite/gtk/multiselection.c index f6fffcb814..d709969376 100644 --- a/testsuite/gtk/multiselection.c +++ b/testsuite/gtk/multiselection.c @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2019, Red Hat, Inc. * Authors: Matthias Clasen * @@ -52,6 +52,42 @@ model_to_string (GListModel *model) return g_string_free (string, FALSE); } +static char * +section_model_to_string (GListModel *model) +{ + GString *string = g_string_new (NULL); + guint i, s, e, n; + + if (!GTK_IS_SECTION_MODEL (model)) + return model_to_string (model); + + n = g_list_model_get_n_items (model); + + i = 0; + while (i < n) + { + gtk_section_model_get_section (GTK_SECTION_MODEL (model), i, &s, &e); + + if (i > 0) + g_string_append (string, " "); + + g_string_append (string, "["); + + for (; i < e; i++) + { + if (i > s) + g_string_append (string, " "); + + g_string_append_printf (string, "%u", get (model, i)); + } + + g_string_append (string, "]"); + i = e; + } + + return g_string_free (string, FALSE); +} + static char * selection_to_string (GListModel *model) { @@ -140,6 +176,14 @@ insert (GListStore *store, g_free (s); \ }G_STMT_END +#define assert_section_model(model, expected) G_STMT_START{ \ + char *s = section_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 ignore_changes(model) G_STMT_START{ \ GString *changes = g_object_get_qdata (G_OBJECT (model), changes_quark); \ g_string_set_size (changes, 0); \ @@ -224,6 +268,20 @@ items_changed (GListModel *model, } } +static void +sections_changed (GListModel *model, + guint position, + guint n_items, + GString *changes) +{ + g_assert_true (n_items != 0); + + if (changes->len) + g_string_append (changes, ", "); + + g_string_append_printf (changes, "s%u:%u", position, n_items); +} + static void notify_n_items (GObject *object, GParamSpec *pspec, @@ -256,16 +314,17 @@ free_changes (gpointer data) } static GtkSelectionModel * -new_model (GListStore *store) +new_model (GListModel *store) { GtkSelectionModel *result; GString *changes; - result = GTK_SELECTION_MODEL (gtk_multi_selection_new (g_object_ref (G_LIST_MODEL (store)))); + result = GTK_SELECTION_MODEL (gtk_multi_selection_new (g_object_ref (store))); changes = g_string_new (""); - g_object_set_qdata_full (G_OBJECT(result), changes_quark, changes, free_changes); + g_object_set_qdata_full (G_OBJECT (result), changes_quark, changes, free_changes); g_signal_connect (result, "items-changed", G_CALLBACK (items_changed), changes); + g_signal_connect (result, "sections-changed", G_CALLBACK (sections_changed), changes); g_signal_connect (result, "notify::n-items", G_CALLBACK (notify_n_items), changes); changes = g_string_new (""); @@ -299,7 +358,7 @@ test_create (void) guint start, end; store = new_store (1, 5, 2); - selection = new_model (store); + selection = new_model (G_LIST_MODEL (store)); assert_model (selection, "1 3 5"); assert_changes (selection, ""); @@ -340,7 +399,7 @@ test_changes (void) gboolean ret; store = new_store (1, 5, 1); - selection = new_model (store); + selection = new_model (G_LIST_MODEL (store)); assert_model (selection, "1 2 3 4 5"); assert_changes (selection, ""); assert_selection (selection, ""); @@ -387,7 +446,7 @@ test_selection (void) gboolean ret; store = new_store (1, 5, 1); - selection = new_model (store); + selection = new_model (G_LIST_MODEL (store)); assert_selection (selection, ""); assert_selection_changes (selection, ""); @@ -442,7 +501,7 @@ test_select_range (void) gboolean ret; store = new_store (1, 5, 1); - selection = new_model (store); + selection = new_model (G_LIST_MODEL (store)); assert_selection (selection, ""); assert_selection_changes (selection, ""); @@ -477,7 +536,7 @@ test_readd (void) store = new_store (1, 5, 1); - selection = new_model (store); + selection = new_model (G_LIST_MODEL (store)); assert_model (selection, "1 2 3 4 5"); assert_selection (selection, ""); assert_selection_changes (selection, ""); @@ -506,7 +565,7 @@ test_set_selection (void) store = new_store (1, 10, 1); - selection = new_model (store); + selection = new_model (G_LIST_MODEL (store)); assert_model (selection, "1 2 3 4 5 6 7 8 9 10"); assert_selection (selection, ""); assert_selection_changes (selection, ""); @@ -547,7 +606,7 @@ test_selection_filter (void) gboolean ret; store = new_store (1, 5, 1); - selection = new_model (store); + selection = new_model (G_LIST_MODEL (store)); assert_selection (selection, ""); assert_selection_changes (selection, ""); @@ -660,7 +719,7 @@ test_set_model (void) store = new_store (1, 5, 1); m1 = G_LIST_MODEL (store); m2 = G_LIST_MODEL (gtk_slice_list_model_new (g_object_ref (m1), 0, 3)); - selection = new_model (store); + selection = new_model (G_LIST_MODEL (store)); assert_selection (selection, ""); assert_selection_changes (selection, ""); @@ -742,6 +801,67 @@ test_empty_filter (void) g_object_unref (selection); } +static int +by_n (gconstpointer p1, + gconstpointer p2, + gpointer data) +{ + guint n1 = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (p1), number_quark)); + guint n2 = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (p2), number_quark)); + unsigned int n = GPOINTER_TO_UINT (data); + + n1 = n1 / n; + n2 = n2 / n; + + if (n1 < n2) + return -1; + else if (n1 > n2) + return 1; + else + return 0; +} + +static int +compare (gconstpointer first, + gconstpointer second, + gpointer unused) +{ + return GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (first), number_quark)) + - GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (second), number_quark)); +} + +static void +test_sections (void) +{ + GListStore *store; + GtkSortListModel *sorted; + GtkSelectionModel *selection; + GtkSorter *sorter; + + store = new_store (1, 10, 1); + sorted = gtk_sort_list_model_new (G_LIST_MODEL (store), + GTK_SORTER (gtk_custom_sorter_new (compare, NULL, NULL))); + selection = new_model (G_LIST_MODEL (sorted)); + assert_model (selection, "1 2 3 4 5 6 7 8 9 10"); + assert_section_model (selection, "[1 2 3 4 5 6 7 8 9 10]"); + assert_changes (selection, ""); + + sorter = GTK_SORTER (gtk_custom_sorter_new (by_n, GUINT_TO_POINTER (3), NULL)); + gtk_sort_list_model_set_section_sorter (sorted, sorter); + g_object_unref (sorter); + + assert_section_model (selection, "[1 2] [3 4 5] [6 7 8] [9 10]"); + assert_changes (selection, "s0:10"); + + gtk_section_model_sections_changed (GTK_SECTION_MODEL (sorted), 0, 3); + assert_changes (selection, "s0:3"); + + gtk_section_model_sections_changed (GTK_SECTION_MODEL (sorted), 5, 3); + assert_changes (selection, "s5:3"); + + g_object_unref (selection); +} + int main (int argc, char *argv[]) { @@ -764,6 +884,7 @@ main (int argc, char *argv[]) g_test_add_func ("/multiselection/set-model", test_set_model); g_test_add_func ("/multiselection/empty", test_empty); g_test_add_func ("/multiselection/selection-filter/empty", test_empty_filter); + g_test_add_func ("/multiselection/sections", test_sections); return g_test_run (); } diff --git a/testsuite/gtk/noselection.c b/testsuite/gtk/noselection.c index 59e0526035..ab71d87b89 100644 --- a/testsuite/gtk/noselection.c +++ b/testsuite/gtk/noselection.c @@ -52,6 +52,42 @@ model_to_string (GListModel *model) return g_string_free (string, FALSE); } +static char * +section_model_to_string (GListModel *model) +{ + GString *string = g_string_new (NULL); + guint i, s, e, n; + + if (!GTK_IS_SECTION_MODEL (model)) + return model_to_string (model); + + n = g_list_model_get_n_items (model); + + i = 0; + while (i < n) + { + gtk_section_model_get_section (GTK_SECTION_MODEL (model), i, &s, &e); + + if (i > 0) + g_string_append (string, " "); + + g_string_append (string, "["); + + for (; i < e; i++) + { + if (i > s) + g_string_append (string, " "); + + g_string_append_printf (string, "%u", get (model, i)); + } + + g_string_append (string, "]"); + i = e; + } + + return g_string_free (string, FALSE); +} + static char * selection_to_string (GListModel *model) { @@ -136,6 +172,14 @@ insert (GListStore *store, g_free (s); \ }G_STMT_END +#define assert_section_model(model, expected) G_STMT_START{ \ + char *s = section_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 ignore_changes(model) G_STMT_START{ \ GString *changes = g_object_get_qdata (G_OBJECT (model), changes_quark); \ g_string_set_size (changes, 0); \ @@ -220,6 +264,20 @@ items_changed (GListModel *model, } } +static void +sections_changed (GListModel *model, + guint position, + guint n_items, + GString *changes) +{ + g_assert_true (n_items != 0); + + if (changes->len) + g_string_append (changes, ", "); + + g_string_append_printf (changes, "s%u:%u", position, n_items); +} + static void notify_n_items (GObject *object, GParamSpec *pspec, @@ -252,16 +310,17 @@ free_changes (gpointer data) } static GtkSelectionModel * -new_model (GListStore *store, gboolean autoselect, gboolean can_unselect) +new_model (GListModel *store, gboolean autoselect, gboolean can_unselect) { GtkSelectionModel *result; GString *changes; - result = GTK_SELECTION_MODEL (gtk_no_selection_new (g_object_ref (G_LIST_MODEL (store)))); + result = GTK_SELECTION_MODEL (gtk_no_selection_new (g_object_ref (store))); changes = g_string_new (""); - g_object_set_qdata_full (G_OBJECT(result), changes_quark, changes, free_changes); + g_object_set_qdata_full (G_OBJECT (result), changes_quark, changes, free_changes); g_signal_connect (result, "items-changed", G_CALLBACK (items_changed), changes); + g_signal_connect (result, "sections-changed", G_CALLBACK (sections_changed), changes); g_signal_connect (result, "notify::n-items", G_CALLBACK (notify_n_items), changes); changes = g_string_new (""); @@ -284,7 +343,7 @@ test_create (void) } store = new_store (1, 5, 2); - selection = new_model (store, FALSE, FALSE); + selection = new_model (G_LIST_MODEL (store), FALSE, FALSE); assert_model (selection, "1 3 5"); assert_changes (selection, ""); @@ -325,7 +384,7 @@ test_changes (void) } store = new_store (1, 5, 1); - selection = new_model (store, FALSE, FALSE); + selection = new_model (G_LIST_MODEL (store), FALSE, FALSE); assert_model (selection, "1 2 3 4 5"); assert_changes (selection, ""); assert_selection (selection, ""); @@ -367,7 +426,7 @@ test_selection (void) } store = new_store (1, 5, 1); - selection = new_model (store, TRUE, FALSE); + selection = new_model (G_LIST_MODEL (store), TRUE, FALSE); assert_selection (selection, ""); assert_selection_changes (selection, ""); @@ -444,7 +503,7 @@ test_query_range (void) GListStore *store; store = new_store (1, 5, 1); - selection = new_model (store, TRUE, TRUE); + selection = new_model (G_LIST_MODEL (store), TRUE, TRUE); check_get_selection (selection); gtk_selection_model_unselect_item (selection, 0); @@ -472,7 +531,7 @@ test_set_model (void) store = new_store (1, 5, 1); m1 = G_LIST_MODEL (store); m2 = G_LIST_MODEL (gtk_slice_list_model_new (g_object_ref (m1), 0, 3)); - selection = new_model (store, TRUE, TRUE); + selection = new_model (G_LIST_MODEL (store), TRUE, TRUE); assert_selection (selection, ""); assert_selection_changes (selection, ""); @@ -531,6 +590,67 @@ test_empty (void) g_object_unref (selection); } +static int +by_n (gconstpointer p1, + gconstpointer p2, + gpointer data) +{ + guint n1 = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (p1), number_quark)); + guint n2 = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (p2), number_quark)); + unsigned int n = GPOINTER_TO_UINT (data); + + n1 = n1 / n; + n2 = n2 / n; + + if (n1 < n2) + return -1; + else if (n1 > n2) + return 1; + else + return 0; +} + +static int +compare (gconstpointer first, + gconstpointer second, + gpointer unused) +{ + return GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (first), number_quark)) + - GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (second), number_quark)); +} + +static void +test_sections (void) +{ + GListStore *store; + GtkSortListModel *sorted; + GtkSelectionModel *selection; + GtkSorter *sorter; + + store = new_store (1, 10, 1); + sorted = gtk_sort_list_model_new (G_LIST_MODEL (store), + GTK_SORTER (gtk_custom_sorter_new (compare, NULL, NULL))); + selection = new_model (G_LIST_MODEL (sorted), TRUE, TRUE); + assert_model (selection, "1 2 3 4 5 6 7 8 9 10"); + assert_section_model (selection, "[1 2 3 4 5 6 7 8 9 10]"); + assert_changes (selection, ""); + + sorter = GTK_SORTER (gtk_custom_sorter_new (by_n, GUINT_TO_POINTER (3), NULL)); + gtk_sort_list_model_set_section_sorter (sorted, sorter); + g_object_unref (sorter); + + assert_section_model (selection, "[1 2] [3 4 5] [6 7 8] [9 10]"); + assert_changes (selection, "s0:10"); + + gtk_section_model_sections_changed (GTK_SECTION_MODEL (sorted), 0, 3); + assert_changes (selection, "s0:3"); + + gtk_section_model_sections_changed (GTK_SECTION_MODEL (sorted), 5, 3); + assert_changes (selection, "s5:3"); + + g_object_unref (selection); +} + int main (int argc, char *argv[]) { @@ -548,6 +668,7 @@ main (int argc, char *argv[]) g_test_add_func ("/noselection/changes", test_changes); g_test_add_func ("/noselection/set-model", test_set_model); g_test_add_func ("/noselection/empty", test_empty); + g_test_add_func ("/noselection/sections", test_sections); return g_test_run (); } diff --git a/testsuite/gtk/singleselection.c b/testsuite/gtk/singleselection.c index faad2d681d..1952da879e 100644 --- a/testsuite/gtk/singleselection.c +++ b/testsuite/gtk/singleselection.c @@ -52,6 +52,42 @@ model_to_string (GListModel *model) return g_string_free (string, FALSE); } +static char * +section_model_to_string (GListModel *model) +{ + GString *string = g_string_new (NULL); + guint i, s, e, n; + + if (!GTK_IS_SECTION_MODEL (model)) + return model_to_string (model); + + n = g_list_model_get_n_items (model); + + i = 0; + while (i < n) + { + gtk_section_model_get_section (GTK_SECTION_MODEL (model), i, &s, &e); + + if (i > 0) + g_string_append (string, " "); + + g_string_append (string, "["); + + for (; i < e; i++) + { + if (i > s) + g_string_append (string, " "); + + g_string_append_printf (string, "%u", get (model, i)); + } + + g_string_append (string, "]"); + i = e; + } + + return g_string_free (string, FALSE); +} + static char * selection_to_string (GListModel *model) { @@ -136,6 +172,14 @@ insert (GListStore *store, g_free (s); \ }G_STMT_END +#define assert_section_model(model, expected) G_STMT_START{ \ + char *s = section_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 ignore_changes(model) G_STMT_START{ \ GString *changes = g_object_get_qdata (G_OBJECT (model), changes_quark); \ g_string_set_size (changes, 0); \ @@ -220,6 +264,20 @@ items_changed (GListModel *model, } } +static void +sections_changed (GListModel *model, + guint position, + guint n_items, + GString *changes) +{ + g_assert_true (n_items != 0); + + if (changes->len) + g_string_append (changes, ", "); + + g_string_append_printf (changes, "s%u:%u", position, n_items); +} + static void notify_n_items (GObject *object, GParamSpec *pspec, @@ -252,12 +310,12 @@ free_changes (gpointer data) } static GtkSelectionModel * -new_model (GListStore *store, gboolean autoselect, gboolean can_unselect) +new_model (GListModel *store, gboolean autoselect, gboolean can_unselect) { GtkSelectionModel *result; GString *changes; - result = GTK_SELECTION_MODEL (gtk_single_selection_new (g_object_ref (G_LIST_MODEL (store)))); + result = GTK_SELECTION_MODEL (gtk_single_selection_new (g_object_ref (store))); /* We want to return an empty selection unless autoselect is true, * so undo the initial selection due to autoselect defaulting to TRUE. @@ -271,8 +329,9 @@ new_model (GListStore *store, gboolean autoselect, gboolean can_unselect) gtk_single_selection_set_can_unselect (GTK_SINGLE_SELECTION (result), can_unselect); changes = g_string_new (""); - g_object_set_qdata_full (G_OBJECT(result), changes_quark, changes, free_changes); + g_object_set_qdata_full (G_OBJECT (result), changes_quark, changes, free_changes); g_signal_connect (result, "items-changed", G_CALLBACK (items_changed), changes); + g_signal_connect (result, "sections-changed", G_CALLBACK (sections_changed), changes); g_signal_connect (result, "notify::n-items", G_CALLBACK (notify_n_items), changes); changes = g_string_new (""); @@ -296,7 +355,7 @@ test_create (void) } store = new_store (1, 5, 2); - selection = new_model (store, FALSE, FALSE); + selection = new_model (G_LIST_MODEL (store), FALSE, FALSE); g_assert_false (gtk_single_selection_get_autoselect (GTK_SINGLE_SELECTION (selection))); assert_model (selection, "1 3 5"); @@ -344,7 +403,7 @@ test_changes (void) } store = new_store (1, 5, 1); - selection = new_model (store, FALSE, FALSE); + selection = new_model (G_LIST_MODEL (store), FALSE, FALSE); assert_model (selection, "1 2 3 4 5"); assert_changes (selection, ""); assert_selection (selection, ""); @@ -386,7 +445,7 @@ test_selection (void) } store = new_store (1, 5, 1); - selection = new_model (store, TRUE, FALSE); + selection = new_model (G_LIST_MODEL (store), TRUE, FALSE); assert_selection (selection, "1"); assert_selection_changes (selection, ""); @@ -442,7 +501,7 @@ test_autoselect (void) } store = new_empty_store (); - selection = new_model (store, TRUE, FALSE); + selection = new_model (G_LIST_MODEL (store), TRUE, FALSE); assert_model (selection, ""); assert_changes (selection, ""); assert_selection (selection, ""); @@ -513,7 +572,7 @@ test_autoselect_toggle (void) } store = new_store (1, 1, 1); - selection = new_model (store, TRUE, TRUE); + selection = new_model (G_LIST_MODEL (store), TRUE, TRUE); assert_model (selection, "1"); assert_changes (selection, ""); assert_selection (selection, "1"); @@ -555,7 +614,7 @@ test_can_unselect (void) } store = new_store (1, 5, 1); - selection = new_model (store, TRUE, FALSE); + selection = new_model (G_LIST_MODEL (store), TRUE, FALSE); assert_selection (selection, "1"); assert_selection_changes (selection, ""); @@ -600,7 +659,7 @@ test_persistence (void) } store = new_store (1, 5, 1); - selection = new_model (store, TRUE, FALSE); + selection = new_model (G_LIST_MODEL (store), TRUE, FALSE); assert_selection (selection, "1"); assert_selection_changes (selection, ""); g_assert_true (gtk_selection_model_is_selected (selection, 0)); @@ -653,7 +712,7 @@ test_query_range (void) GListStore *store; store = new_store (1, 5, 1); - selection = new_model (store, TRUE, TRUE); + selection = new_model (G_LIST_MODEL (store), TRUE, TRUE); check_get_selection (selection); gtk_selection_model_unselect_item (selection, 0); @@ -681,7 +740,7 @@ test_set_model (void) store = new_store (1, 5, 1); m1 = G_LIST_MODEL (store); m2 = G_LIST_MODEL (gtk_slice_list_model_new (g_object_ref (m1), 0, 3)); - selection = new_model (store, TRUE, TRUE); + selection = new_model (G_LIST_MODEL (store), TRUE, TRUE); assert_selection (selection, "1"); assert_selection_changes (selection, ""); @@ -743,6 +802,67 @@ test_empty (void) g_object_unref (selection); } +static int +by_n (gconstpointer p1, + gconstpointer p2, + gpointer data) +{ + guint n1 = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (p1), number_quark)); + guint n2 = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (p2), number_quark)); + unsigned int n = GPOINTER_TO_UINT (data); + + n1 = n1 / n; + n2 = n2 / n; + + if (n1 < n2) + return -1; + else if (n1 > n2) + return 1; + else + return 0; +} + +static int +compare (gconstpointer first, + gconstpointer second, + gpointer unused) +{ + return GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (first), number_quark)) + - GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (second), number_quark)); +} + +static void +test_sections (void) +{ + GListStore *store; + GtkSortListModel *sorted; + GtkSelectionModel *selection; + GtkSorter *sorter; + + store = new_store (1, 10, 1); + sorted = gtk_sort_list_model_new (G_LIST_MODEL (store), + GTK_SORTER (gtk_custom_sorter_new (compare, NULL, NULL))); + selection = new_model (G_LIST_MODEL (sorted), TRUE, TRUE); + assert_model (selection, "1 2 3 4 5 6 7 8 9 10"); + assert_section_model (selection, "[1 2 3 4 5 6 7 8 9 10]"); + assert_changes (selection, ""); + + sorter = GTK_SORTER (gtk_custom_sorter_new (by_n, GUINT_TO_POINTER (3), NULL)); + gtk_sort_list_model_set_section_sorter (sorted, sorter); + g_object_unref (sorter); + + assert_section_model (selection, "[1 2] [3 4 5] [6 7 8] [9 10]"); + assert_changes (selection, "s0:10"); + + gtk_section_model_sections_changed (GTK_SECTION_MODEL (sorted), 0, 3); + assert_changes (selection, "s0:3"); + + gtk_section_model_sections_changed (GTK_SECTION_MODEL (sorted), 5, 3); + assert_changes (selection, "s5:3"); + + g_object_unref (selection); +} + int main (int argc, char *argv[]) { @@ -764,6 +884,7 @@ main (int argc, char *argv[]) g_test_add_func ("/singleselection/changes", test_changes); g_test_add_func ("/singleselection/set-model", test_set_model); g_test_add_func ("/singleselection/empty", test_empty); + g_test_add_func ("/singleselection/sections", test_sections); return g_test_run (); } diff --git a/testsuite/gtk/slicelistmodel.c b/testsuite/gtk/slicelistmodel.c index e86abd69e7..680f699eda 100644 --- a/testsuite/gtk/slicelistmodel.c +++ b/testsuite/gtk/slicelistmodel.c @@ -51,6 +51,41 @@ model_to_string (GListModel *model) return g_string_free (string, FALSE); } +static char * +section_model_to_string (GListModel *model) +{ + GString *string = g_string_new (NULL); + guint i, s, e; + + if (!GTK_IS_SECTION_MODEL (model)) + return model_to_string (model); + + i = 0; + while (i < g_list_model_get_n_items (model)) + { + gtk_section_model_get_section (GTK_SECTION_MODEL (model), i, &s, &e); + g_assert (s == i); + + if (i > 0) + g_string_append (string, " "); + + g_string_append (string, "["); + + for (; i < e; i++) + { + if (i > s) + g_string_append (string, " "); + + g_string_append_printf (string, "%u", get (model, i)); + } + + g_string_append (string, "]"); + i = e; + } + + return g_string_free (string, FALSE); +} + static GListStore * new_store (guint start, guint end, @@ -116,6 +151,14 @@ insert (GListStore *store, g_free (s); \ }G_STMT_END +#define assert_section_model(model, expected) G_STMT_START{ \ + char *s = section_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)) \ @@ -174,6 +217,20 @@ items_changed (GListModel *model, } } +static void +sections_changed (GListModel *model, + guint position, + guint n_items, + GString *changes) +{ + g_assert_true (n_items != 0); + + if (changes->len) + g_string_append (changes, ", "); + + g_string_append_printf (changes, "s%u:%u", position, n_items); +} + static void notify_n_items (GObject *object, GParamSpec *pspec, @@ -194,18 +251,19 @@ free_changes (gpointer data) } static GtkSliceListModel * -new_model (GListStore *store, guint offset, guint size) +new_model (GListModel *store, guint offset, guint size) { GtkSliceListModel *result; GString *changes; if (store) g_object_ref (store); - result = gtk_slice_list_model_new (G_LIST_MODEL (store), offset, size); + result = gtk_slice_list_model_new (store, offset, size); changes = g_string_new (""); - g_object_set_qdata_full (G_OBJECT(result), changes_quark, changes, free_changes); + g_object_set_qdata_full (G_OBJECT (result), changes_quark, changes, free_changes); g_signal_connect (result, "items-changed", G_CALLBACK (items_changed), changes); + g_signal_connect (result, "sections-changed", G_CALLBACK (sections_changed), changes); g_signal_connect (result, "notify::n-items", G_CALLBACK (notify_n_items), changes); return result; @@ -230,7 +288,7 @@ test_create (void) GListStore *store; store = new_store (1, 5, 2); - slice = new_model (store, 0, 10); + slice = new_model (G_LIST_MODEL (store), 0, 10); assert_model (slice, "1 3 5"); assert_changes (slice, ""); @@ -273,7 +331,7 @@ test_set_slice (void) GListStore *store; store = new_store (1, 7, 2); - slice = new_model (store, 0, 3); + slice = new_model (G_LIST_MODEL (store), 0, 3); assert_model (slice, "1 3 5"); assert_changes (slice, ""); @@ -302,7 +360,7 @@ test_changes (void) GListStore *store; store = new_store (1, 20, 1); - slice = new_model (store, 10, 5); + slice = new_model (G_LIST_MODEL (store), 10, 5); assert_model (slice, "11 12 13 14 15"); assert_changes (slice, ""); @@ -349,7 +407,7 @@ test_bug_added_equals_removed (void) GListStore *store; store = new_store (1, 10, 1); - slice = new_model (store, 0, 10); + slice = new_model (G_LIST_MODEL (store), 0, 10); assert_model (slice, "1 2 3 4 5 6 7 8 9 10"); assert_changes (slice, ""); @@ -368,7 +426,7 @@ test_bug_skip_amount (void) GListStore *store; store = new_store (1, 5, 1); - slice = new_model (store, 2, 2); + slice = new_model (G_LIST_MODEL (store), 2, 2); assert_model (slice, "3 4"); assert_changes (slice, ""); @@ -380,6 +438,76 @@ test_bug_skip_amount (void) g_object_unref (slice); } +static int +by_n (gconstpointer p1, + gconstpointer p2, + gpointer data) +{ + guint n1 = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (p1), number_quark)); + guint n2 = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (p2), number_quark)); + unsigned int n = GPOINTER_TO_UINT (data); + + n1 = n1 / n; + n2 = n2 / n; + + if (n1 < n2) + return -1; + else if (n1 > n2) + return 1; + else + return 0; +} + +static int +compare (gconstpointer first, + gconstpointer second, + gpointer unused) +{ + return GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (first), number_quark)) + - GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (second), number_quark)); +} + +static void +test_sections (void) +{ + GListStore *store; + GtkSortListModel *sorted; + GtkSliceListModel *slice; + GtkSorter *sorter; + + store = new_store (1, 10, 1); + sorted = gtk_sort_list_model_new (G_LIST_MODEL (store), + GTK_SORTER (gtk_custom_sorter_new (compare, NULL, NULL))); + slice = new_model (G_LIST_MODEL (sorted), 0, 10); + assert_model (slice, "1 2 3 4 5 6 7 8 9 10"); + assert_section_model (slice, "[1 2 3 4 5 6 7 8 9 10]"); + assert_changes (slice, ""); + + sorter = GTK_SORTER (gtk_custom_sorter_new (by_n, GUINT_TO_POINTER (3), NULL)); + gtk_sort_list_model_set_section_sorter (sorted, sorter); + g_object_unref (sorter); + + assert_section_model (slice, "[1 2] [3 4 5] [6 7 8] [9 10]"); + assert_changes (slice, "s0:10"); + + gtk_slice_list_model_set_size (slice, 5); + + assert_section_model (slice, "[1 2] [3 4 5]"); + assert_changes (slice, "5-5*"); + + gtk_slice_list_model_set_offset (slice, 1); + assert_section_model (slice, "[2] [3 4 5] [6]"); + assert_changes (slice, "0-5+5"); + + gtk_section_model_sections_changed (GTK_SECTION_MODEL (sorted), 0, 3); + assert_changes (slice, "s0:2"); + + gtk_section_model_sections_changed (GTK_SECTION_MODEL (sorted), 5, 3); + assert_changes (slice, "s4:1"); + + g_object_unref (slice); +} + int main (int argc, char *argv[]) { @@ -396,6 +524,7 @@ main (int argc, char *argv[]) g_test_add_func ("/slicelistmodel/changes", test_changes); g_test_add_func ("/slicelistmodel/bug/added_equals_removed", test_bug_added_equals_removed); g_test_add_func ("/slicelistmodel/bug/skip_amount", test_bug_skip_amount); + g_test_add_func ("/slicelistmodel/sections", test_sections); return g_test_run (); } diff --git a/testsuite/gtk/sortlistmodel.c b/testsuite/gtk/sortlistmodel.c index a9a4dc97f4..b0b85117ed 100644 --- a/testsuite/gtk/sortlistmodel.c +++ b/testsuite/gtk/sortlistmodel.c @@ -52,6 +52,41 @@ model_to_string (GListModel *model) return g_string_free (string, FALSE); } +static char * +section_model_to_string (GListModel *model) +{ + GString *string = g_string_new (NULL); + guint i, s, e; + + if (!GTK_IS_SECTION_MODEL (model)) + return model_to_string (model); + + i = 0; + while (i < g_list_model_get_n_items (model)) + { + gtk_section_model_get_section (GTK_SECTION_MODEL (model), i, &s, &e); + g_assert (s == i); + + if (i > 0) + g_string_append (string, " "); + + g_string_append (string, "["); + + for (; i < e; i++) + { + if (i > s) + g_string_append (string, " "); + + g_string_append_printf (string, "%u", get (model, i)); + } + + g_string_append (string, "]"); + i = e; + } + + return g_string_free (string, FALSE); +} + static void splice (GListStore *store, guint pos, @@ -115,6 +150,14 @@ insert (GListStore *store, g_free (s); \ }G_STMT_END +#define assert_section_model(model, expected) G_STMT_START{ \ + char *s = section_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)) \ @@ -176,6 +219,20 @@ items_changed (GListModel *model, } } +static void +sections_changed (GListModel *model, + guint position, + guint n_items, + GString *changes) +{ + g_assert_true (n_items != 0); + + if (changes->len) + g_string_append (changes, ", "); + + g_string_append_printf (changes, "s%u:%u", position, n_items); +} + static void notify_n_items (GObject *object, GParamSpec *pspec, @@ -236,6 +293,7 @@ new_model (gpointer model) changes = g_string_new (""); g_object_set_qdata_full (G_OBJECT(result), changes_quark, changes, free_changes); g_signal_connect (result, "items-changed", G_CALLBACK (items_changed), changes); + g_signal_connect (result, "sections-changed", G_CALLBACK (sections_changed), changes); g_signal_connect (result, "notify::n-items", G_CALLBACK (notify_n_items), changes); return result; @@ -571,55 +629,82 @@ test_add_remove_item (void) } static int -sort_func (gconstpointer p1, - gconstpointer p2, - gpointer data) +by_n (gconstpointer p1, + gconstpointer p2, + gpointer data) { - const char *s1 = gtk_string_object_get_string ((GtkStringObject *)p1); - const char *s2 = gtk_string_object_get_string ((GtkStringObject *)p2); + guint n1 = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (p1), number_quark)); + guint n2 = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (p2), number_quark)); + unsigned int n = GPOINTER_TO_UINT (data); - /* compare just the first byte */ - return (int)(s1[0]) - (int)(s2[0]); + n1 = n1 / n; + n2 = n2 / n; + + if (n1 < n2) + return -1; + else if (n1 > n2) + return 1; + else + return 0; +} + +static int +weird (gconstpointer p1, + gconstpointer p2, + gpointer data) +{ + guint n1 = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (p1), number_quark)); + guint n2 = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (p2), number_quark)); + + if (n1 == 5 && n2 != 5) + return -1; + else if (n1 != 5 && n2 == 5) + return 1; + + return by_n (p1, p2, data); } static void test_sections (void) { - GtkStringList *list; - const char *strings[] = { - "aaa", - "aab", - "abc", - "bbb", - "bq1", - "bq2", - "cc", - "cx", - NULL - }; + GListStore *store; + GtkSortListModel *model; GtkSorter *sorter; - GtkSortListModel *sorted; - GtkSorter *section_sorter; - guint s, e; - list = gtk_string_list_new (strings); - sorter = GTK_SORTER (gtk_string_sorter_new (gtk_property_expression_new (GTK_TYPE_STRING_OBJECT, NULL, "string"))); - sorted = gtk_sort_list_model_new (G_LIST_MODEL (list), sorter); - section_sorter = GTK_SORTER (gtk_custom_sorter_new (sort_func, NULL, NULL)); - gtk_sort_list_model_set_section_sorter (GTK_SORT_LIST_MODEL (sorted), section_sorter); - g_object_unref (section_sorter); + store = new_store ((guint[]) { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }); + model = new_model (store); + assert_model (model, "1 2 3 4 5 6 7 8 9 10"); + assert_section_model (model, "[1 2 3 4 5 6 7 8 9 10]"); + assert_changes (model, ""); - gtk_section_model_get_section (GTK_SECTION_MODEL (sorted), 0, &s, &e); - g_assert_cmpint (s, ==, 0); - g_assert_cmpint (e, ==, 3); - gtk_section_model_get_section (GTK_SECTION_MODEL (sorted), 3, &s, &e); - g_assert_cmpint (s, ==, 3); - g_assert_cmpint (e, ==, 6); - gtk_section_model_get_section (GTK_SECTION_MODEL (sorted), 6, &s, &e); - g_assert_cmpint (s, ==, 6); - g_assert_cmpint (e, ==, 8); + g_assert_true (gtk_sort_list_model_get_section_sorter (model) == NULL); - g_object_unref (sorted); + sorter = GTK_SORTER (gtk_custom_sorter_new (by_n, GUINT_TO_POINTER (3), NULL)); + gtk_sort_list_model_set_section_sorter (model, sorter); + g_assert_true (gtk_sort_list_model_get_section_sorter (model) == sorter); + g_object_unref (sorter); + + assert_changes (model, "s0:10"); + assert_section_model (model, "[1 2] [3 4 5] [6 7 8] [9 10]"); + + sorter = GTK_SORTER (gtk_custom_sorter_new (by_n, GUINT_TO_POINTER (5), NULL)); + g_assert_false (gtk_sort_list_model_get_section_sorter (model) == sorter); + gtk_sort_list_model_set_section_sorter (model, sorter); + g_assert_true (gtk_sort_list_model_get_section_sorter (model) == sorter); + g_object_unref (sorter); + + assert_changes (model, "s0:10"); + assert_section_model (model, "[1 2 3 4] [5 6 7 8 9] [10]"); + + sorter = GTK_SORTER (gtk_custom_sorter_new (weird, GUINT_TO_POINTER (5), NULL)); + gtk_sort_list_model_set_section_sorter (model, sorter); + g_object_unref (sorter); + + assert_changes (model, "0-10+10"); + assert_section_model (model, "[5] [1 2 3 4] [6 7 8 9] [10]"); + + g_object_unref (store); + g_object_unref (model); } int