X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Ftests%2Ffiltermodel.c;h=507ceefa305aa785314ff5b51a929236f39cc238;hb=9d0febc9a64a5bfb0fcfc3a88de4757f6c1ff090;hp=22253e0144061ebd6aac28df28388f2be3b3e546;hpb=c229306a18f6453c144397bfbbf2284afa9731eb;p=~andy%2Fgtk diff --git a/gtk/tests/filtermodel.c b/gtk/tests/filtermodel.c index 22253e014..507ceefa3 100644 --- a/gtk/tests/filtermodel.c +++ b/gtk/tests/filtermodel.c @@ -1,5 +1,5 @@ /* Extensive GtkTreeModelFilter tests. - * Copyright (C) 2009 Kristian Rietveld + * Copyright (C) 2009,2011 Kristian Rietveld * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -12,22 +12,29 @@ * 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, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * License along with this library. If not, see . */ #include +#include +#include "treemodel.h" +#include "gtktreemodelrefcount.h" /* Left to do: * - Proper coverage checking to see if the unit tests cover * all possible cases. - * - Verify if the ref counting is done properly for both the - * normal ref_count and the zero_ref_count. One way to test - * this area is by collapsing/expanding branches on the view - * that is connected to the filter model. * - Check if the iterator stamp is incremented at the correct times. + * + * For more thorough testing: + * - Test with randomized models. + * - Extensively test a filter model wrapping a sort model, + * or a sort model wrapping a filter model by: + * # Checking structure. + * # Checking for correct signals emissions. + * # Checking correct reference counting. + * # Tests should be done with the sort and filter model + * in various filtering and sorting states. */ @@ -90,271 +97,6 @@ create_tree_store (int depth, return store; } -/* - * Signal monitor - */ - -typedef enum -{ - ROW_INSERTED, - ROW_DELETED, - ROW_CHANGED, - ROW_HAS_CHILD_TOGGLED, - ROWS_REORDERED, - LAST_SIGNAL -} -SignalName; - -static const char * -signal_name_to_string (SignalName signal) -{ - switch (signal) - { - case ROW_INSERTED: - return "row-inserted"; - - case ROW_DELETED: - return "row-deleted"; - - case ROW_CHANGED: - return "row-changed"; - - case ROW_HAS_CHILD_TOGGLED: - return "row-has-child-toggled"; - - case ROWS_REORDERED: - return "rows-reordered"; - - default: - /* Fall through */ - break; - } - - return "(unknown)"; -} - -typedef struct -{ - SignalName signal; - GtkTreePath *path; -} -Signal; - - -static Signal * -signal_new (SignalName signal, GtkTreePath *path) -{ - Signal *s; - - s = g_new0 (Signal, 1); - s->signal = signal; - s->path = gtk_tree_path_copy (path); - - return s; -} - -static void -signal_free (Signal *s) -{ - if (s->path) - gtk_tree_path_free (s->path); - - g_free (s); -} - - -typedef struct -{ - GQueue *queue; - GtkTreeModel *client; - guint signal_ids[LAST_SIGNAL]; -} -SignalMonitor; - - -static void -signal_monitor_generic_handler (SignalMonitor *m, - SignalName signal, - GtkTreeModel *model, - GtkTreePath *path) -{ - Signal *s; - - if (g_queue_is_empty (m->queue)) - { - g_error ("Signal queue empty\n"); - g_assert_not_reached (); - } - - if (m->client != model) - { - g_error ("Model mismatch; expected %p, got %p\n", - m->client, model); - g_assert_not_reached (); - } - - s = g_queue_peek_tail (m->queue); - -#if 0 - /* For debugging: output signals that are coming in. Leaks memory. */ - g_print ("signal=%d path=%s\n", signal, gtk_tree_path_to_string (path)); -#endif - - if (s->signal != signal - || gtk_tree_path_compare (s->path, path) != 0) - { - gchar *path_str, *s_path_str; - - s_path_str = gtk_tree_path_to_string (s->path); - path_str = gtk_tree_path_to_string (path); - - g_error ("Signals don't match; expected signal %s path %s, got signal %s path %s\n", - signal_name_to_string (s->signal), s_path_str, - signal_name_to_string (signal), path_str); - - g_free (s_path_str); - g_free (path_str); - - g_assert_not_reached (); - } - - s = g_queue_pop_tail (m->queue); - - signal_free (s); -} - -static void -signal_monitor_row_inserted (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer data) -{ - signal_monitor_generic_handler (data, ROW_INSERTED, - model, path); -} - -static void -signal_monitor_row_deleted (GtkTreeModel *model, - GtkTreePath *path, - gpointer data) -{ - signal_monitor_generic_handler (data, ROW_DELETED, - model, path); -} - -static void -signal_monitor_row_changed (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer data) -{ - signal_monitor_generic_handler (data, ROW_CHANGED, - model, path); -} - -static void -signal_monitor_row_has_child_toggled (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer data) -{ - signal_monitor_generic_handler (data, ROW_HAS_CHILD_TOGGLED, - model, path); -} - -static void -signal_monitor_rows_reordered (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gint *new_order, - gpointer data) -{ - signal_monitor_generic_handler (data, ROWS_REORDERED, - model, path); -} - -static SignalMonitor * -signal_monitor_new (GtkTreeModel *client) -{ - SignalMonitor *m; - - m = g_new0 (SignalMonitor, 1); - m->client = g_object_ref (client); - m->queue = g_queue_new (); - - m->signal_ids[ROW_INSERTED] = g_signal_connect (client, - "row-inserted", - G_CALLBACK (signal_monitor_row_inserted), - m); - m->signal_ids[ROW_DELETED] = g_signal_connect (client, - "row-deleted", - G_CALLBACK (signal_monitor_row_deleted), - m); - m->signal_ids[ROW_CHANGED] = g_signal_connect (client, - "row-changed", - G_CALLBACK (signal_monitor_row_changed), - m); - m->signal_ids[ROW_HAS_CHILD_TOGGLED] = g_signal_connect (client, - "row-has-child-toggled", - G_CALLBACK (signal_monitor_row_has_child_toggled), - m); - m->signal_ids[ROWS_REORDERED] = g_signal_connect (client, - "rows-reordered", - G_CALLBACK (signal_monitor_rows_reordered), - m); - - return m; -} - -static void -signal_monitor_free (SignalMonitor *m) -{ - int i; - - for (i = 0; i < LAST_SIGNAL; i++) - g_signal_handler_disconnect (m->client, m->signal_ids[i]); - - g_object_unref (m->client); - - if (m->queue) - g_queue_free (m->queue); - - g_free (m); -} - -static void -signal_monitor_assert_is_empty (SignalMonitor *m) -{ - g_assert (g_queue_is_empty (m->queue)); -} - -static void -signal_monitor_append_signal_path (SignalMonitor *m, - SignalName signal, - GtkTreePath *path) -{ - Signal *s; - - s = signal_new (signal, path); - g_queue_push_head (m->queue, s); -} - -static void -signal_monitor_append_signal (SignalMonitor *m, - SignalName signal, - const gchar *path_string) -{ - Signal *s; - GtkTreePath *path; - - path = gtk_tree_path_new_from_string (path_string); - - s = signal_new (signal, path); - g_queue_push_head (m->queue, s); - - gtk_tree_path_free (path); -} - /* * Fixture */ @@ -411,6 +153,23 @@ filter_test_setup_generic (FilterTest *fixture, fixture->monitor = signal_monitor_new (filter); } +static void +filter_test_setup_expand_root (FilterTest *fixture) +{ + int i; + GtkTreePath *path; + + path = gtk_tree_path_new_from_indices (0, -1); + + for (i = 0; i < LEVEL_LENGTH; i++) + { + gtk_tree_view_expand_row (GTK_TREE_VIEW (fixture->tree_view), + path, FALSE); + gtk_tree_path_next (path); + } + gtk_tree_path_free (path); +} + static void filter_test_setup (FilterTest *fixture, gconstpointer test_data) @@ -432,6 +191,14 @@ filter_test_setup_unfiltered (FilterTest *fixture, filter_test_setup_generic (fixture, test_data, 3, FALSE, TRUE); } +static void +filter_test_setup_unfiltered_root_expanded (FilterTest *fixture, + gconstpointer test_data) +{ + filter_test_setup_unfiltered (fixture, test_data); + filter_test_setup_expand_root (fixture); +} + static void filter_test_setup_empty_unfiltered (FilterTest *fixture, gconstpointer test_data) @@ -439,6 +206,14 @@ filter_test_setup_empty_unfiltered (FilterTest *fixture, filter_test_setup_generic (fixture, test_data, 3, TRUE, TRUE); } +static void +filter_test_setup_empty_unfiltered_root_expanded (FilterTest *fixture, + gconstpointer test_data) +{ + filter_test_setup_empty_unfiltered (fixture, test_data); + filter_test_setup_expand_root (fixture); +} + static GtkTreePath * strip_virtual_root (GtkTreePath *path, GtkTreePath *root_path) @@ -463,6 +238,32 @@ strip_virtual_root (GtkTreePath *path, return real_path; } +static int +count_visible (FilterTest *fixture, + GtkTreePath *store_path) +{ + int i; + int n_visible = 0; + GtkTreeIter iter; + + gtk_tree_model_get_iter (GTK_TREE_MODEL (fixture->store), + &iter, store_path); + + for (i = 0; i < LEVEL_LENGTH; i++) + { + gboolean visible; + + gtk_tree_model_get (GTK_TREE_MODEL (fixture->store), &iter, + 1, &visible, + -1); + + if (visible) + n_visible++; + } + + return n_visible; +} + static void filter_test_append_refilter_signals_recurse (FilterTest *fixture, GtkTreePath *store_path, @@ -530,26 +331,48 @@ filter_test_append_refilter_signals_recurse (FilterTest *fixture, /* This row will be inserted */ signal_monitor_append_signal_path (fixture->monitor, ROW_CHANGED, real_path); - signal_monitor_append_signal_path (fixture->monitor, - ROW_HAS_CHILD_TOGGLED, - real_path); - if (depth > 1 - && gtk_tree_model_iter_has_child (GTK_TREE_MODEL (fixture->store), - &iter)) + if (gtk_tree_model_iter_has_child (GTK_TREE_MODEL (fixture->store), + &iter)) { - GtkTreePath *store_copy; - GtkTreePath *filter_copy; - - store_copy = gtk_tree_path_copy (store_path); - filter_copy = gtk_tree_path_copy (filter_path); - filter_test_append_refilter_signals_recurse (fixture, - store_copy, - filter_copy, - depth - 1, - root_path); - gtk_tree_path_free (store_copy); - gtk_tree_path_free (filter_copy); + signal_monitor_append_signal_path (fixture->monitor, + ROW_HAS_CHILD_TOGGLED, + real_path); + + if (depth > 1) + { + GtkTreePath *store_copy; + GtkTreePath *filter_copy; + + store_copy = gtk_tree_path_copy (store_path); + filter_copy = gtk_tree_path_copy (filter_path); + filter_test_append_refilter_signals_recurse (fixture, + store_copy, + filter_copy, + depth - 1, + root_path); + gtk_tree_path_free (store_copy); + gtk_tree_path_free (filter_copy); + } + else if (depth == 1) + { + GtkTreePath *tmp_path; + + /* If all child rows are invisible, then the last row to + * become invisible will emit row-has-child-toggled on the + * parent. + */ + + tmp_path = gtk_tree_path_copy (store_path); + gtk_tree_path_append_index (tmp_path, 0); + + if (count_visible (fixture, tmp_path) == 0) + signal_monitor_append_signal_path (fixture->monitor, + ROW_HAS_CHILD_TOGGLED, + real_path); + + gtk_tree_path_free (tmp_path); + } } gtk_tree_path_next (filter_path); @@ -660,6 +483,8 @@ filter_test_teardown (FilterTest *fixture, { signal_monitor_free (fixture->monitor); + gtk_widget_destroy (fixture->tree_view); + g_object_unref (fixture->filter); g_object_unref (fixture->store); } @@ -733,6 +558,9 @@ check_filter_model_recurse (FilterTest *fixture, gtk_tree_path_copy (store_parent_path), tmp); } + else + /* Only when we do not recurse we need to free tmp */ + gtk_tree_path_free (tmp); gtk_tree_path_next (filter_parent_path); filter_has_next = gtk_tree_model_iter_next (GTK_TREE_MODEL (fixture->filter), &filter_iter); @@ -780,24 +608,26 @@ check_filter_model_with_root (FilterTest *fixture, static void check_level_length (GtkTreeModelFilter *filter, const gchar *level, - const int length) + const int expected_length) { if (!level) { - int l = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (filter), NULL); - g_return_if_fail (l == length); + int model_length; + + model_length = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (filter), NULL); + g_assert_cmpint (model_length, ==, expected_length); } else { - int l; + int model_length; gboolean retrieved_iter = FALSE; GtkTreeIter iter; retrieved_iter = gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (filter), &iter, level); g_return_if_fail (retrieved_iter); - l = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (filter), &iter); - g_return_if_fail (l == length); + model_length = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (filter), &iter); + g_assert_cmpint (model_length, ==, expected_length); } } @@ -908,6 +738,59 @@ static void filled_hide_child_levels (FilterTest *fixture, gconstpointer user_data) { + set_path_visibility (fixture, "0:2", FALSE); + check_filter_model (fixture); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + check_level_length (fixture->filter, "0", LEVEL_LENGTH - 1); + + set_path_visibility (fixture, "0:4", FALSE); + check_filter_model (fixture); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + check_level_length (fixture->filter, "0", LEVEL_LENGTH - 2); + + set_path_visibility (fixture, "0:4:3", FALSE); + check_filter_model (fixture); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + check_level_length (fixture->filter, "0", LEVEL_LENGTH - 2); + + set_path_visibility (fixture, "0:4:0", FALSE); + set_path_visibility (fixture, "0:4:1", FALSE); + set_path_visibility (fixture, "0:4:2", FALSE); + set_path_visibility (fixture, "0:4:4", FALSE); + check_filter_model (fixture); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + check_level_length (fixture->filter, "0", LEVEL_LENGTH - 2); + + /* Since "0:2" is hidden, "0:4" must be "0:3" in the filter model */ + set_path_visibility (fixture, "0:4", TRUE); + check_filter_model (fixture); + check_level_length (fixture->filter, "0:3", 0); + + set_path_visibility (fixture, "0:2", TRUE); + check_filter_model (fixture); + check_level_length (fixture->filter, "0:2", LEVEL_LENGTH); + check_level_length (fixture->filter, "0:3", LEVEL_LENGTH); + check_level_length (fixture->filter, "0:4", 0); + + /* Once 0:4:0 got inserted, 0:4 became a parent. Because 0:4 is + * not visible, not signals are emitted. + */ + set_path_visibility (fixture, "0:4:2", TRUE); + set_path_visibility (fixture, "0:4:4", TRUE); + signal_monitor_assert_is_empty (fixture->monitor); + check_level_length (fixture->filter, "0:4", 2); +} + +static void +filled_hide_child_levels_root_expanded (FilterTest *fixture, + gconstpointer user_data) +{ + GtkTreePath *path; + + path = gtk_tree_path_new_from_indices (0, -1); + gtk_tree_view_expand_row (GTK_TREE_VIEW (fixture->tree_view), path, FALSE); + gtk_tree_path_free (path); + signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0:2"); set_path_visibility (fixture, "0:2", FALSE); check_filter_model (fixture); @@ -935,10 +818,6 @@ filled_hide_child_levels (FilterTest *fixture, /* Since "0:2" is hidden, "0:4" must be "0:3" in the filter model */ signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:3"); - /* FIXME: Actually, the filter model should not be emitted the - * row-has-child-toggled signal here. *However* an extraneous emission - * of this signal does not hurt and is allowed. - */ signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:3"); set_path_visibility (fixture, "0:4", TRUE); check_filter_model (fixture); @@ -952,13 +831,8 @@ filled_hide_child_levels (FilterTest *fixture, check_level_length (fixture->filter, "0:3", LEVEL_LENGTH); check_level_length (fixture->filter, "0:4", 0); - signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:4:0"); - /* Once 0:4:0 got inserted, 0:4 became a parent */ + /* has-child-toggled for 0:4 is required. */ signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:4"); - signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:4:0"); - signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:4:1"); - signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:4:1"); - set_path_visibility (fixture, "0:4:2", TRUE); set_path_visibility (fixture, "0:4:4", TRUE); signal_monitor_assert_is_empty (fixture->monitor); @@ -1101,13 +975,11 @@ filled_vroot_hide_child_levels (FilterTest *fixture, { GtkTreePath *path = (GtkTreePath *)user_data; - signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0:2"); set_path_visibility (fixture, "2:0:2", FALSE); check_filter_model_with_root (fixture, path); check_level_length (fixture->filter, NULL, LEVEL_LENGTH); check_level_length (fixture->filter, "0", LEVEL_LENGTH - 1); - signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0:3"); set_path_visibility (fixture, "2:0:4", FALSE); check_filter_model_with_root (fixture, path); check_level_length (fixture->filter, NULL, LEVEL_LENGTH); @@ -1127,49 +999,96 @@ filled_vroot_hide_child_levels (FilterTest *fixture, check_level_length (fixture->filter, "0", LEVEL_LENGTH - 2); /* Since "0:2" is hidden, "0:4" must be "0:3" in the filter model */ - signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:3"); - /* FIXME: Actually, the filter model should not be emitted the - * row-has-child-toggled signal here. *However* an extraneous emission - * of this signal does not hurt and is allowed. - */ - signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:3"); set_path_visibility (fixture, "2:0:4", TRUE); check_filter_model_with_root (fixture, path); check_level_length (fixture->filter, "0:3", 0); - signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:2"); - signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:2"); set_path_visibility (fixture, "2:0:2", TRUE); check_filter_model_with_root (fixture, path); check_level_length (fixture->filter, "0:2", LEVEL_LENGTH); check_level_length (fixture->filter, "0:3", LEVEL_LENGTH); check_level_length (fixture->filter, "0:4", 0); - /* FIXME: Inconsistency! For the non-vroot case we also receive two - * row-has-child-toggled signals here. + /* Once 0:4:0 got inserted, 0:4 became a parent. However, 0:4 is not + * visible, so no signal should be emitted. */ - signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:4:0"); - /* Once 0:4:0 got inserted, 0:4 became a parent */ - signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:4"); - signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:4:1"); set_path_visibility (fixture, "2:0:4:2", TRUE); set_path_visibility (fixture, "2:0:4:4", TRUE); check_level_length (fixture->filter, "0:4", 2); + signal_monitor_assert_is_empty (fixture->monitor); } - static void -empty_show_nodes (FilterTest *fixture, - gconstpointer user_data) +filled_vroot_hide_child_levels_root_expanded (FilterTest *fixture, + gconstpointer user_data) { - check_filter_model (fixture); - check_level_length (fixture->filter, NULL, 0); + GtkTreePath *path = (GtkTreePath *)user_data; + GtkTreePath *tmp_path; - signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0"); - signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0"); - set_path_visibility (fixture, "3", TRUE); - check_filter_model (fixture); - check_level_length (fixture->filter, NULL, 1); + tmp_path = gtk_tree_path_new_from_indices (0, -1); + gtk_tree_view_expand_row (GTK_TREE_VIEW (fixture->tree_view), tmp_path, FALSE); + gtk_tree_path_free (tmp_path); + + signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0:2"); + set_path_visibility (fixture, "2:0:2", FALSE); + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + check_level_length (fixture->filter, "0", LEVEL_LENGTH - 1); + + signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0:3"); + set_path_visibility (fixture, "2:0:4", FALSE); + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + check_level_length (fixture->filter, "0", LEVEL_LENGTH - 2); + + set_path_visibility (fixture, "2:0:4:3", FALSE); + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + check_level_length (fixture->filter, "0", LEVEL_LENGTH - 2); + + set_path_visibility (fixture, "2:0:4:0", FALSE); + set_path_visibility (fixture, "2:0:4:1", FALSE); + set_path_visibility (fixture, "2:0:4:2", FALSE); + set_path_visibility (fixture, "2:0:4:4", FALSE); + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + check_level_length (fixture->filter, "0", LEVEL_LENGTH - 2); + + /* Since "0:2" is hidden, "0:4" must be "0:3" in the filter model */ + signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:3"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:3"); + set_path_visibility (fixture, "2:0:4", TRUE); + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, "0:3", 0); + + signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:2"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:2"); + set_path_visibility (fixture, "2:0:2", TRUE); + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, "0:2", LEVEL_LENGTH); + check_level_length (fixture->filter, "0:3", LEVEL_LENGTH); + check_level_length (fixture->filter, "0:4", 0); + + /* Once 0:4:0 got inserted, 0:4 became a parent */ + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:4"); + set_path_visibility (fixture, "2:0:4:2", TRUE); + set_path_visibility (fixture, "2:0:4:4", TRUE); + check_level_length (fixture->filter, "0:4", 2); + signal_monitor_assert_is_empty (fixture->monitor); +} + +static void +empty_show_nodes (FilterTest *fixture, + gconstpointer user_data) +{ + check_filter_model (fixture); + check_level_length (fixture->filter, NULL, 0); + + signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0"); + set_path_visibility (fixture, "3", TRUE); + check_filter_model (fixture); + check_level_length (fixture->filter, NULL, 1); check_level_length (fixture->filter, "0", 0); set_path_visibility (fixture, "3:2:2", TRUE); @@ -1177,9 +1096,7 @@ empty_show_nodes (FilterTest *fixture, check_level_length (fixture->filter, NULL, 1); check_level_length (fixture->filter, "0", 0); - signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:0"); signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0"); - signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:0"); set_path_visibility (fixture, "3:2", TRUE); check_filter_model (fixture); check_level_length (fixture->filter, NULL, 1); @@ -1217,8 +1134,6 @@ empty_show_multiple_nodes (FilterTest *fixture, signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0"); signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "1"); signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "1"); - signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "1"); - signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "1"); /* We simulate a change in visible func condition with this. The * visibility state of multiple nodes changes at once, we emit row-changed @@ -1233,6 +1148,7 @@ empty_show_multiple_nodes (FilterTest *fixture, gtk_tree_path_append_index (changed_path, 2); gtk_tree_model_get_iter (GTK_TREE_MODEL (fixture->store), &iter, changed_path); + /* Invisible node - so no signals expected */ gtk_tree_model_row_changed (GTK_TREE_MODEL (fixture->store), changed_path, &iter); @@ -1257,9 +1173,7 @@ empty_show_multiple_nodes (FilterTest *fixture, check_level_length (fixture->filter, NULL, 2); check_level_length (fixture->filter, "0", 0); - signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:0"); signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0"); - signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:0"); set_path_visibility (fixture, "3:2", TRUE); check_filter_model (fixture); check_level_length (fixture->filter, NULL, 2); @@ -1451,9 +1365,28 @@ unfiltered_hide_single (FilterTest *fixture, signal_monitor_assert_is_empty (fixture->monitor); check_level_length (fixture->filter, NULL, LEVEL_LENGTH); - /* The view only shows the root level, so the filter model only has - * the first two levels cached. + /* The view only shows the root level, so we only expect signals + * for the root level. */ + filter_test_append_refilter_signals (fixture, 1); + filter_test_enable_filter (fixture); + + check_filter_model (fixture); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 1); +} + +static void +unfiltered_hide_single_root_expanded (FilterTest *fixture, + gconstpointer user_data) + +{ + signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2"); + set_path_visibility (fixture, "2", FALSE); + + signal_monitor_assert_is_empty (fixture->monitor); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + filter_test_append_refilter_signals (fixture, 2); filter_test_enable_filter (fixture); @@ -1465,6 +1398,29 @@ static void unfiltered_hide_single_child (FilterTest *fixture, gconstpointer user_data) +{ + /* This row is not shown, so its signal is not propagated */ + set_path_visibility (fixture, "2:2", FALSE); + + signal_monitor_assert_is_empty (fixture->monitor); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + check_level_length (fixture->filter, "2", LEVEL_LENGTH); + + /* The view only shows the root level, so we only expect signals + * for the root level. + */ + filter_test_append_refilter_signals (fixture, 0); + filter_test_enable_filter (fixture); + + check_filter_model (fixture); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + check_level_length (fixture->filter, "2", LEVEL_LENGTH - 1); +} + +static void +unfiltered_hide_single_child_root_expanded (FilterTest *fixture, + gconstpointer user_data) + { signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2:2"); signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2"); @@ -1474,9 +1430,6 @@ unfiltered_hide_single_child (FilterTest *fixture, check_level_length (fixture->filter, NULL, LEVEL_LENGTH); check_level_length (fixture->filter, "2", LEVEL_LENGTH); - /* The view only shows the root level, so the filter model only has - * the first two levels cached. - */ filter_test_append_refilter_signals (fixture, 2); filter_test_enable_filter (fixture); @@ -1489,6 +1442,40 @@ static void unfiltered_hide_single_multi_level (FilterTest *fixture, gconstpointer user_data) +{ + /* This row is not shown, so its signal is not propagated */ + set_path_visibility (fixture, "2:2:2", FALSE); + + /* This row is not shown, so its signal is not propagated */ + set_path_visibility (fixture, "2:2", FALSE); + + signal_monitor_assert_is_empty (fixture->monitor); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + check_level_length (fixture->filter, "2", LEVEL_LENGTH); + check_level_length (fixture->filter, "2:2", LEVEL_LENGTH); + + /* The view only shows the root level, so we only expect signals + * for the root level. + */ + filter_test_append_refilter_signals (fixture, 1); + filter_test_enable_filter (fixture); + + check_filter_model (fixture); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + check_level_length (fixture->filter, "2", LEVEL_LENGTH - 1); + + set_path_visibility (fixture, "2:2", TRUE); + + check_filter_model (fixture); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + check_level_length (fixture->filter, "2", LEVEL_LENGTH); + check_level_length (fixture->filter, "2:2", LEVEL_LENGTH - 1); +} + +static void +unfiltered_hide_single_multi_level_root_expanded (FilterTest *fixture, + gconstpointer user_data) + { /* This row is not shown, so its signal is not propagated */ set_path_visibility (fixture, "2:2:2", FALSE); @@ -1502,9 +1489,6 @@ unfiltered_hide_single_multi_level (FilterTest *fixture, check_level_length (fixture->filter, "2", LEVEL_LENGTH); check_level_length (fixture->filter, "2:2", LEVEL_LENGTH); - /* The view only shows the root level, so the filter model only has - * the first two levels cached. - */ filter_test_append_refilter_signals (fixture, 2); filter_test_enable_filter (fixture); @@ -1523,6 +1507,7 @@ unfiltered_hide_single_multi_level (FilterTest *fixture, } + static void unfiltered_vroot_hide_single (FilterTest *fixture, gconstpointer user_data) @@ -1537,11 +1522,11 @@ unfiltered_vroot_hide_single (FilterTest *fixture, signal_monitor_assert_is_empty (fixture->monitor); check_level_length (fixture->filter, NULL, LEVEL_LENGTH); - /* The view only shows the root level, so the filter model only has - * the first two levels cached. (We add an additional level to + /* The view only shows the root level, so we only expect signals + * for the root level. (Though for the depth argument, we have to * take the virtual root into account). */ - filter_test_append_refilter_signals_with_vroot (fixture, 3, path); + filter_test_append_refilter_signals_with_vroot (fixture, 2, path); filter_test_enable_filter (fixture); check_filter_model_with_root (fixture, path); @@ -1552,6 +1537,32 @@ static void unfiltered_vroot_hide_single_child (FilterTest *fixture, gconstpointer user_data) +{ + GtkTreePath *path = (GtkTreePath *)user_data; + + /* Not visible, so no signal will be received. */ + set_path_visibility (fixture, "2:2:2", FALSE); + + signal_monitor_assert_is_empty (fixture->monitor); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + check_level_length (fixture->filter, "2", LEVEL_LENGTH); + + /* The view only shows the root level, so we only expect signals + * for the root level. (Though for the depth argument, we have to + * take the virtual root into account). + */ + filter_test_append_refilter_signals_with_vroot (fixture, 2, path); + filter_test_enable_filter (fixture); + + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + check_level_length (fixture->filter, "2", LEVEL_LENGTH - 1); +} + +static void +unfiltered_vroot_hide_single_child_root_expanded (FilterTest *fixture, + gconstpointer user_data) + { GtkTreePath *path = (GtkTreePath *)user_data; @@ -1563,10 +1574,6 @@ unfiltered_vroot_hide_single_child (FilterTest *fixture, check_level_length (fixture->filter, NULL, LEVEL_LENGTH); check_level_length (fixture->filter, "2", LEVEL_LENGTH); - /* The view only shows the root level, so the filter model only has - * the first two levels cached. (We add an additional level to take - * the virtual root into account). - */ filter_test_append_refilter_signals_with_vroot (fixture, 3, path); filter_test_enable_filter (fixture); @@ -1579,6 +1586,43 @@ static void unfiltered_vroot_hide_single_multi_level (FilterTest *fixture, gconstpointer user_data) +{ + GtkTreePath *path = (GtkTreePath *)user_data; + + /* This row is not shown, so its signal is not propagated */ + set_path_visibility (fixture, "2:2:2:2", FALSE); + + /* Not shown, so no signal */ + set_path_visibility (fixture, "2:2:2", FALSE); + + signal_monitor_assert_is_empty (fixture->monitor); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + check_level_length (fixture->filter, "2", LEVEL_LENGTH); + check_level_length (fixture->filter, "2:2", LEVEL_LENGTH); + + /* We only expect signals for the root level. The depth is 2 + * because we have to take the virtual root into account. + */ + filter_test_append_refilter_signals_with_vroot (fixture, 2, path); + filter_test_enable_filter (fixture); + + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + check_level_length (fixture->filter, "2", LEVEL_LENGTH - 1); + + /* Not shown, so no signal */ + set_path_visibility (fixture, "2:2:2", TRUE); + + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + check_level_length (fixture->filter, "2", LEVEL_LENGTH); + check_level_length (fixture->filter, "2:2", LEVEL_LENGTH - 1); +} + +static void +unfiltered_vroot_hide_single_multi_level_root_expanded (FilterTest *fixture, + gconstpointer user_data) + { GtkTreePath *path = (GtkTreePath *)user_data; @@ -1594,9 +1638,6 @@ unfiltered_vroot_hide_single_multi_level (FilterTest *fixture, check_level_length (fixture->filter, "2", LEVEL_LENGTH); check_level_length (fixture->filter, "2:2", LEVEL_LENGTH); - /* The view only shows the root level, so the filter model only has - * the first two levels cached. - */ filter_test_append_refilter_signals_with_vroot (fixture, 3, path); filter_test_enable_filter (fixture); @@ -1614,8 +1655,6 @@ unfiltered_vroot_hide_single_multi_level (FilterTest *fixture, check_level_length (fixture->filter, "2:2", LEVEL_LENGTH - 1); } - - static void unfiltered_show_single (FilterTest *fixture, gconstpointer user_data) @@ -1628,10 +1667,8 @@ unfiltered_show_single (FilterTest *fixture, signal_monitor_assert_is_empty (fixture->monitor); check_level_length (fixture->filter, NULL, LEVEL_LENGTH); - /* The view only shows the root level, so the filter model only has - * the first two levels cached. - */ - filter_test_append_refilter_signals (fixture, 2); + /* We only expect signals for the root level */ + filter_test_append_refilter_signals (fixture, 1); filter_test_enable_filter (fixture); check_filter_model (fixture); @@ -1642,6 +1679,35 @@ static void unfiltered_show_single_child (FilterTest *fixture, gconstpointer user_data) +{ + set_path_visibility (fixture, "2:2", TRUE); + + signal_monitor_assert_is_empty (fixture->monitor); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + check_level_length (fixture->filter, "2", LEVEL_LENGTH); + + /* We only expect signals for the root level */ + filter_test_append_refilter_signals (fixture, 1); + filter_test_enable_filter (fixture); + + check_filter_model (fixture); + check_level_length (fixture->filter, NULL, 0); + + /* From here we are filtered, "2" in the real model is "0" in the filter + * model. + */ + signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0"); + set_path_visibility (fixture, "2", TRUE); + signal_monitor_assert_is_empty (fixture->monitor); + check_level_length (fixture->filter, NULL, 1); + check_level_length (fixture->filter, "0", 1); +} + +static void +unfiltered_show_single_child_root_expanded (FilterTest *fixture, + gconstpointer user_data) + { signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2:2"); signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2"); @@ -1651,10 +1717,7 @@ unfiltered_show_single_child (FilterTest *fixture, check_level_length (fixture->filter, NULL, LEVEL_LENGTH); check_level_length (fixture->filter, "2", LEVEL_LENGTH); - /* The view only shows the root level, so the filter model only has - * the first two levels cached. - */ - filter_test_append_refilter_signals (fixture, 3); + filter_test_append_refilter_signals (fixture, 2); filter_test_enable_filter (fixture); check_filter_model (fixture); @@ -1676,13 +1739,10 @@ unfiltered_show_single_multi_level (FilterTest *fixture, gconstpointer user_data) { - /* The view is not showing this row (collapsed state), so it is not + /* The view is not showing these rows (collapsed state), so it is not * referenced. The signal should not go through. */ set_path_visibility (fixture, "2:2:2", TRUE); - - signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2:2"); - signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2"); set_path_visibility (fixture, "2:2", TRUE); signal_monitor_assert_is_empty (fixture->monitor); @@ -1690,10 +1750,8 @@ unfiltered_show_single_multi_level (FilterTest *fixture, check_level_length (fixture->filter, "2", LEVEL_LENGTH); check_level_length (fixture->filter, "2:2", LEVEL_LENGTH); - /* The view only shows the root level, so the filter model only has - * the first two levels cached. - */ - filter_test_append_refilter_signals (fixture, 3); + /* We only expect signals for the first level */ + filter_test_append_refilter_signals (fixture, 1); filter_test_enable_filter (fixture); check_filter_model (fixture); @@ -1711,28 +1769,64 @@ unfiltered_show_single_multi_level (FilterTest *fixture, check_level_length (fixture->filter, "0:0", 1); } - static void -unfiltered_vroot_show_single (FilterTest *fixture, - gconstpointer user_data) +unfiltered_show_single_multi_level_root_expanded (FilterTest *fixture, + gconstpointer user_data) { - GtkTreePath *path = (GtkTreePath *)user_data; + /* The view is not showing this row (collapsed state), so it is not + * referenced. The signal should not go through. + */ + set_path_visibility (fixture, "2:2:2", TRUE); - signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2"); - signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2"); + signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2:2"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2"); set_path_visibility (fixture, "2:2", TRUE); signal_monitor_assert_is_empty (fixture->monitor); check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + check_level_length (fixture->filter, "2", LEVEL_LENGTH); + check_level_length (fixture->filter, "2:2", LEVEL_LENGTH); - /* The view only shows the root level, so the filter model only has - * the first two levels cached. - */ - filter_test_append_refilter_signals_with_vroot (fixture, 3, path); + filter_test_append_refilter_signals (fixture, 2); filter_test_enable_filter (fixture); - check_filter_model_with_root (fixture, path); + check_filter_model (fixture); + check_level_length (fixture->filter, NULL, 0); + + /* From here we are filtered, "2" in the real model is "0" in the filter + * model. + */ + signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0"); + set_path_visibility (fixture, "2", TRUE); + check_filter_model (fixture); + check_level_length (fixture->filter, NULL, 1); + check_level_length (fixture->filter, "0", 1); + check_level_length (fixture->filter, "0:0", 1); +} + +static void +unfiltered_vroot_show_single (FilterTest *fixture, + gconstpointer user_data) + +{ + GtkTreePath *path = (GtkTreePath *)user_data; + + signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2"); + set_path_visibility (fixture, "2:2", TRUE); + + signal_monitor_assert_is_empty (fixture->monitor); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + + /* The view only shows the root level, so the filter model only has + * the first two levels cached. + */ + filter_test_append_refilter_signals_with_vroot (fixture, 2, path); + filter_test_enable_filter (fixture); + + check_filter_model_with_root (fixture, path); check_level_length (fixture->filter, NULL, 1); } @@ -1743,8 +1837,6 @@ unfiltered_vroot_show_single_child (FilterTest *fixture, { GtkTreePath *path = (GtkTreePath *)user_data; - signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2:2"); - signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2"); set_path_visibility (fixture, "2:2:2", TRUE); signal_monitor_assert_is_empty (fixture->monitor); @@ -1771,6 +1863,39 @@ unfiltered_vroot_show_single_child (FilterTest *fixture, check_level_length (fixture->filter, "0", 1); } +static void +unfiltered_vroot_show_single_child_root_expanded (FilterTest *fixture, + gconstpointer user_data) + +{ + GtkTreePath *path = (GtkTreePath *)user_data; + + signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2:2"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2"); + set_path_visibility (fixture, "2:2:2", TRUE); + + signal_monitor_assert_is_empty (fixture->monitor); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + check_level_length (fixture->filter, "2", LEVEL_LENGTH); + + filter_test_append_refilter_signals_with_vroot (fixture, 3, path); + filter_test_enable_filter (fixture); + + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, NULL, 0); + + /* From here we are filtered, "2" in the real model is "0" in the filter + * model. + */ + signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0"); + set_path_visibility (fixture, "2:2", TRUE); + signal_monitor_assert_is_empty (fixture->monitor); + check_level_length (fixture->filter, NULL, 1); + check_level_length (fixture->filter, "0", 1); +} + + static void unfiltered_vroot_show_single_multi_level (FilterTest *fixture, gconstpointer user_data) @@ -1783,8 +1908,6 @@ unfiltered_vroot_show_single_multi_level (FilterTest *fixture, */ set_path_visibility (fixture, "2:2:2:2", TRUE); - signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2:2"); - signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2"); set_path_visibility (fixture, "2:2:2", TRUE); signal_monitor_assert_is_empty (fixture->monitor); @@ -1792,10 +1915,8 @@ unfiltered_vroot_show_single_multi_level (FilterTest *fixture, check_level_length (fixture->filter, "2", LEVEL_LENGTH); check_level_length (fixture->filter, "2:2", LEVEL_LENGTH); - /* The view only shows the root level, so the filter model only has - * the first two levels cached. - */ - filter_test_append_refilter_signals_with_vroot (fixture, 4, path); + /* We only expect signals for the root level */ + filter_test_append_refilter_signals_with_vroot (fixture, 2, path); filter_test_enable_filter (fixture); check_filter_model_with_root (fixture, path); @@ -1813,1188 +1934,5185 @@ unfiltered_vroot_show_single_multi_level (FilterTest *fixture, check_level_length (fixture->filter, "0:0", 1); } +static void +unfiltered_vroot_show_single_multi_level_root_expanded (FilterTest *fixture, + gconstpointer user_data) -static gboolean -specific_path_dependent_filter_func (GtkTreeModel *model, - GtkTreeIter *iter, - gpointer data) { - GtkTreePath *path; + GtkTreePath *path = (GtkTreePath *)user_data; - path = gtk_tree_model_get_path (model, iter); - if (gtk_tree_path_get_indices (path)[0] < 4) - return FALSE; + /* The view is not showing this row (collapsed state), so it is not + * referenced. The signal should not go through. + */ + set_path_visibility (fixture, "2:2:2:2", TRUE); - return TRUE; + signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2:2"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2"); + set_path_visibility (fixture, "2:2:2", TRUE); + + signal_monitor_assert_is_empty (fixture->monitor); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + check_level_length (fixture->filter, "2", LEVEL_LENGTH); + check_level_length (fixture->filter, "2:2", LEVEL_LENGTH); + + filter_test_append_refilter_signals_with_vroot (fixture, 3, path); + filter_test_enable_filter (fixture); + + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, NULL, 0); + + /* From here we are filtered, "2" in the real model is "0" in the filter + * model. + */ + signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0"); + set_path_visibility (fixture, "2:2", TRUE); + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, NULL, 1); + check_level_length (fixture->filter, "0", 1); + check_level_length (fixture->filter, "0:0", 1); } static void -specific_path_dependent_filter (void) +unfiltered_rows_reordered_root_level (FilterTest *fixture, + gconstpointer user_data) { - int i; - GtkTreeIter iter; - GtkListStore *list; - GtkTreeModel *sort; - GtkTreeModel *filter; + int order0[] = { 1, 2, 3, 4, 0 }; + int order1[] = { 0, 2, 1, 3, 4 }; + int order2[] = { 4, 0, 1, 2, 3 }; + GtkTreeIter iter0, iter1, iter2, iter3, iter4; + GtkTreePath *path; - list = gtk_list_store_new (1, G_TYPE_INT); - gtk_list_store_insert_with_values (list, &iter, 0, 0, 1, -1); - gtk_list_store_insert_with_values (list, &iter, 1, 0, 2, -1); - gtk_list_store_insert_with_values (list, &iter, 2, 0, 3, -1); - gtk_list_store_insert_with_values (list, &iter, 3, 0, 4, -1); - gtk_list_store_insert_with_values (list, &iter, 4, 0, 5, -1); - gtk_list_store_insert_with_values (list, &iter, 5, 0, 6, -1); - gtk_list_store_insert_with_values (list, &iter, 6, 0, 7, -1); - gtk_list_store_insert_with_values (list, &iter, 7, 0, 8, -1); + gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store), + &iter0, "0"); + gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store), + &iter1, "1"); + gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store), + &iter2, "2"); + gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store), + &iter3, "3"); + gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store), + &iter4, "4"); - sort = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (list)); - filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (sort), NULL); - gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter), - specific_path_dependent_filter_func, - NULL, NULL); + path = gtk_tree_path_new (); + signal_monitor_append_signal_reordered (fixture->monitor, + ROWS_REORDERED, + path, order0, 5); + gtk_tree_store_move_after (fixture->store, &iter0, &iter4); + signal_monitor_assert_is_empty (fixture->monitor); - gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sort), 0, - GTK_SORT_DESCENDING); + signal_monitor_append_signal_reordered (fixture->monitor, + ROWS_REORDERED, + path, order1, 5); + gtk_tree_store_move_after (fixture->store, &iter2, &iter3); + signal_monitor_assert_is_empty (fixture->monitor); - for (i = 0; i < 4; i++) - { - if (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (list), &iter, - NULL, 1)) - gtk_list_store_remove (list, &iter); + signal_monitor_append_signal_reordered (fixture->monitor, + ROWS_REORDERED, + path, order2, 5); + gtk_tree_store_move_before (fixture->store, &iter0, &iter1); + signal_monitor_assert_is_empty (fixture->monitor); - if (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (list), &iter, - NULL, 2)) - gtk_list_store_remove (list, &iter); - } + gtk_tree_path_free (path); } - -static gboolean -specific_append_after_collapse_visible_func (GtkTreeModel *model, - GtkTreeIter *iter, - gpointer data) +static void +unfiltered_rows_reordered_child_level (FilterTest *fixture, + gconstpointer user_data) { - gint number; - gboolean hide_negative_numbers; + int order0[] = { 1, 2, 3, 4, 0 }; + int order1[] = { 0, 2, 1, 3, 4 }; + int order2[] = { 4, 0, 1, 2, 3 }; + GtkTreeIter iter0, iter1, iter2, iter3, iter4; + GtkTreePath *path; - gtk_tree_model_get (model, iter, 1, &number, -1); - hide_negative_numbers = GPOINTER_TO_INT (g_object_get_data (data, "private-hide-negative-numbers")); + /* Expand row 0 */ + path = gtk_tree_path_new_from_indices (0, -1); + gtk_tree_view_expand_row (GTK_TREE_VIEW (fixture->tree_view), path, FALSE); - return (number >= 0 || !hide_negative_numbers); + gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store), + &iter0, "0:0"); + gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store), + &iter1, "0:1"); + gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store), + &iter2, "0:2"); + gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store), + &iter3, "0:3"); + gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store), + &iter4, "0:4"); + + signal_monitor_append_signal_reordered (fixture->monitor, + ROWS_REORDERED, + path, order0, 5); + gtk_tree_store_move_after (fixture->store, &iter0, &iter4); + signal_monitor_assert_is_empty (fixture->monitor); + + signal_monitor_append_signal_reordered (fixture->monitor, + ROWS_REORDERED, + path, order1, 5); + gtk_tree_store_move_after (fixture->store, &iter2, &iter3); + signal_monitor_assert_is_empty (fixture->monitor); + + signal_monitor_append_signal_reordered (fixture->monitor, + ROWS_REORDERED, + path, order2, 5); + gtk_tree_store_move_before (fixture->store, &iter0, &iter1); + signal_monitor_assert_is_empty (fixture->monitor); + + gtk_tree_path_free (path); } static void -specific_append_after_collapse (void) +filtered_rows_reordered_root_level_first_hidden (FilterTest *fixture, + gconstpointer user_data) { - /* This test is based on one of the test cases I found in my - * old test cases directory. I unfortunately do not have a record - * from who this test case originated. -Kris. - * - * General idea: - * - Construct tree. - * - Show tree, expand, collapse. - * - Add a row. - */ + int order0[] = { 1, 2, 3, 0 }; + int order1[] = { 0, 2, 1, 3 }; + int order2[] = { 3, 0, 1, 2 }; + GtkTreeIter iter1, iter2, iter3, iter4; + GtkTreePath *path; - GtkTreeIter iter; - GtkTreeIter child_iter; - GtkTreeIter child_iter2; - GtkTreePath *append_path; - GtkTreeStore *store; - GtkTreeModel *filter; - GtkTreeModel *sort; + /* Hide middle path */ + signal_monitor_append_signal (fixture->monitor, + ROW_DELETED, "0"); + set_path_visibility (fixture, "0", FALSE); + signal_monitor_assert_is_empty (fixture->monitor); - GtkWidget *window; - GtkWidget *tree_view; + gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store), + &iter1, "1"); + gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store), + &iter2, "2"); + gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store), + &iter3, "3"); + gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store), + &iter4, "4"); - store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_INT); + path = gtk_tree_path_new (); + signal_monitor_append_signal_reordered (fixture->monitor, + ROWS_REORDERED, + path, order0, 4); + gtk_tree_store_move_after (fixture->store, &iter1, &iter4); + signal_monitor_assert_is_empty (fixture->monitor); - filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (store), NULL); - g_object_set_data (G_OBJECT (filter), "private-hide-negative-numbers", - GINT_TO_POINTER (FALSE)); - gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter), - specific_append_after_collapse_visible_func, - filter, NULL); + signal_monitor_append_signal_reordered (fixture->monitor, + ROWS_REORDERED, + path, order1, 4); + gtk_tree_store_move_after (fixture->store, &iter3, &iter4); + signal_monitor_assert_is_empty (fixture->monitor); - sort = gtk_tree_model_sort_new_with_model (filter); + signal_monitor_append_signal_reordered (fixture->monitor, + ROWS_REORDERED, + path, order2, 4); + gtk_tree_store_move_before (fixture->store, &iter1, &iter2); + signal_monitor_assert_is_empty (fixture->monitor); - window = gtk_window_new (GTK_WINDOW_TOPLEVEL); - tree_view = gtk_tree_view_new_with_model (sort); - gtk_container_add (GTK_CONTAINER (window), tree_view); - gtk_widget_realize (tree_view); + gtk_tree_path_free (path); +} - while (gtk_events_pending ()) - gtk_main_iteration (); +static void +filtered_rows_reordered_root_level_middle_hidden (FilterTest *fixture, + gconstpointer user_data) +{ + int order0[] = { 1, 2, 3, 0 }; + int order1[] = { 0, 2, 1, 3 }; + int order2[] = { 3, 0, 1, 2 }; + GtkTreeIter iter0, iter1, iter3, iter4; + GtkTreePath *path; - gtk_tree_store_prepend (store, &iter, NULL); - gtk_tree_store_set (store, &iter, - 0, "hallo", 1, 1, -1); + /* Hide middle path */ + signal_monitor_append_signal (fixture->monitor, + ROW_DELETED, "2"); + set_path_visibility (fixture, "2", FALSE); + signal_monitor_assert_is_empty (fixture->monitor); - gtk_tree_store_append (store, &child_iter, &iter); - gtk_tree_store_set (store, &child_iter, - 0, "toemaar", 1, 1, -1); + gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store), + &iter0, "0"); + gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store), + &iter1, "1"); + gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store), + &iter3, "3"); + gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store), + &iter4, "4"); - gtk_tree_store_append (store, &child_iter2, &child_iter); - gtk_tree_store_set (store, &child_iter2, - 0, "very deep", 1, 1, -1); + path = gtk_tree_path_new (); + signal_monitor_append_signal_reordered (fixture->monitor, + ROWS_REORDERED, + path, order0, 4); + gtk_tree_store_move_after (fixture->store, &iter0, &iter4); + signal_monitor_assert_is_empty (fixture->monitor); - append_path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &child_iter2); + signal_monitor_append_signal_reordered (fixture->monitor, + ROWS_REORDERED, + path, order1, 4); + gtk_tree_store_move_after (fixture->store, &iter3, &iter4); + signal_monitor_assert_is_empty (fixture->monitor); - gtk_tree_store_append (store, &child_iter, &iter); - gtk_tree_store_set (store, &child_iter, - 0, "sja", 1, 1, -1); + signal_monitor_append_signal_reordered (fixture->monitor, + ROWS_REORDERED, + path, order2, 4); + gtk_tree_store_move_before (fixture->store, &iter0, &iter1); + signal_monitor_assert_is_empty (fixture->monitor); - gtk_tree_store_append (store, &child_iter, &iter); - gtk_tree_store_set (store, &child_iter, - 0, "some word", 1, -1, -1); + gtk_tree_path_free (path); +} - /* Expand and collapse the tree */ - gtk_tree_view_expand_all (GTK_TREE_VIEW (tree_view)); - while (gtk_events_pending ()) - gtk_main_iteration (); +static void +filtered_rows_reordered_child_level_first_hidden (FilterTest *fixture, + gconstpointer user_data) +{ + int order0[] = { 1, 2, 3, 0 }; + int order1[] = { 0, 2, 1, 3 }; + int order2[] = { 3, 0, 1, 2 }; + GtkTreeIter iter1, iter2, iter3, iter4; + GtkTreePath *path; - gtk_tree_view_collapse_all (GTK_TREE_VIEW (tree_view)); - while (gtk_events_pending ()) - gtk_main_iteration (); + /* Expand row 0 */ + path = gtk_tree_path_new_from_indices (0, -1); + gtk_tree_view_expand_row (GTK_TREE_VIEW (fixture->tree_view), path, TRUE); - /* Add another it */ - g_object_set_data (G_OBJECT (filter), "private-hide-negative-numbers", - GINT_TO_POINTER (TRUE)); + /* Hide middle path */ + signal_monitor_append_signal (fixture->monitor, + ROW_DELETED, "0:0"); + set_path_visibility (fixture, "0:0", FALSE); + signal_monitor_assert_is_empty (fixture->monitor); - if (gtk_tree_model_get_iter (GTK_TREE_MODEL (store), &iter, append_path)) - { - gtk_tree_store_append (store, &child_iter, &iter); - gtk_tree_store_set (store, &child_iter, - 0, "new new new !!", 1, 1, -1); - } - gtk_tree_path_free (append_path); + gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store), + &iter1, "0:1"); + gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store), + &iter2, "0:2"); + gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store), + &iter3, "0:3"); + gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store), + &iter4, "0:4"); - /* Expand */ - gtk_tree_view_expand_all (GTK_TREE_VIEW (tree_view)); - while (gtk_events_pending ()) - gtk_main_iteration (); -} + signal_monitor_append_signal_reordered (fixture->monitor, + ROWS_REORDERED, + path, order0, 4); + gtk_tree_store_move_after (fixture->store, &iter1, &iter4); + signal_monitor_assert_is_empty (fixture->monitor); + signal_monitor_append_signal_reordered (fixture->monitor, + ROWS_REORDERED, + path, order1, 4); + gtk_tree_store_move_after (fixture->store, &iter3, &iter4); + signal_monitor_assert_is_empty (fixture->monitor); -static gint -specific_sort_filter_remove_node_compare_func (GtkTreeModel *model, - GtkTreeIter *iter1, - GtkTreeIter *iter2, - gpointer data) -{ - return -1; + signal_monitor_append_signal_reordered (fixture->monitor, + ROWS_REORDERED, + path, order2, 4); + gtk_tree_store_move_before (fixture->store, &iter1, &iter2); + signal_monitor_assert_is_empty (fixture->monitor); + + gtk_tree_path_free (path); } -static gboolean -specific_sort_filter_remove_node_visible_func (GtkTreeModel *model, - GtkTreeIter *iter, - gpointer data) +static void +filtered_rows_reordered_child_level_middle_hidden (FilterTest *fixture, + gconstpointer user_data) { - char *item = NULL; + int order0[] = { 1, 2, 3, 0 }; + int order1[] = { 0, 2, 1, 3 }; + int order2[] = { 3, 0, 1, 2 }; + GtkTreeIter iter0, iter1, iter3, iter4; + GtkTreePath *path; - /* Do reference the model */ - gtk_tree_model_get (model, iter, 0, &item, -1); - g_free (item); + /* Expand row 0 */ + path = gtk_tree_path_new_from_indices (0, -1); + gtk_tree_view_expand_row (GTK_TREE_VIEW (fixture->tree_view), path, FALSE); - return FALSE; + /* Hide middle path */ + signal_monitor_append_signal (fixture->monitor, + ROW_DELETED, "0:2"); + set_path_visibility (fixture, "0:2", FALSE); + signal_monitor_assert_is_empty (fixture->monitor); + + gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store), + &iter0, "0:0"); + gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store), + &iter1, "0:1"); + gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store), + &iter3, "0:3"); + gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store), + &iter4, "0:4"); + + signal_monitor_append_signal_reordered (fixture->monitor, + ROWS_REORDERED, + path, order0, 4); + gtk_tree_store_move_after (fixture->store, &iter0, &iter4); + signal_monitor_assert_is_empty (fixture->monitor); + + signal_monitor_append_signal_reordered (fixture->monitor, + ROWS_REORDERED, + path, order1, 4); + gtk_tree_store_move_after (fixture->store, &iter3, &iter4); + signal_monitor_assert_is_empty (fixture->monitor); + + signal_monitor_append_signal_reordered (fixture->monitor, + ROWS_REORDERED, + path, order2, 4); + gtk_tree_store_move_before (fixture->store, &iter0, &iter1); + signal_monitor_assert_is_empty (fixture->monitor); + + gtk_tree_path_free (path); } static void -specific_sort_filter_remove_node (void) +filtered_rows_reordered_child_level_4_hidden (FilterTest *fixture, + gconstpointer user_data) { - /* This test is based on one of the test cases I found in my - * old test cases directory. I unfortunately do not have a record - * from who this test case originated. -Kris. - * - * General idea: - * - Create tree store, sort, filter models. The sort model has - * a default sort func that is enabled, filter model a visible func - * that defaults to returning FALSE. - * - Remove a node from the tree store. - */ - - GtkTreeIter iter; - GtkTreeStore *store; - GtkTreeModel *filter; - GtkTreeModel *sort; - - GtkWidget *window; - GtkWidget *tree_view; + int order0[] = { 0 }; + GtkTreeIter iter1, iter4; + GtkTreePath *path; - store = gtk_tree_store_new (1, G_TYPE_STRING); - gtk_tree_store_append (store, &iter, NULL); - gtk_tree_store_set (store, &iter, 0, "Hello1", -1); + /* Expand row 0 */ + path = gtk_tree_path_new_from_indices (0, -1); + gtk_tree_view_expand_row (GTK_TREE_VIEW (fixture->tree_view), path, FALSE); + + /* Hide last 4 paths */ + signal_monitor_append_signal (fixture->monitor, + ROW_DELETED, "0:4"); + signal_monitor_append_signal (fixture->monitor, + ROW_DELETED, "0:3"); + signal_monitor_append_signal (fixture->monitor, + ROW_DELETED, "0:2"); + signal_monitor_append_signal (fixture->monitor, + ROW_DELETED, "0:0"); + set_path_visibility (fixture, "0:4", FALSE); + set_path_visibility (fixture, "0:3", FALSE); + set_path_visibility (fixture, "0:2", FALSE); + set_path_visibility (fixture, "0:0", FALSE); + signal_monitor_assert_is_empty (fixture->monitor); - gtk_tree_store_append (store, &iter, NULL); - gtk_tree_store_set (store, &iter, 0, "Hello2", -1); + gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store), + &iter1, "0:1"); + gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store), + &iter4, "0:4"); - sort = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (store)); - gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (sort), - specific_sort_filter_remove_node_compare_func, NULL, NULL); + signal_monitor_append_signal_reordered (fixture->monitor, + ROWS_REORDERED, + path, order0, 1); + gtk_tree_store_move_after (fixture->store, &iter1, &iter4); + signal_monitor_assert_is_empty (fixture->monitor); - filter = gtk_tree_model_filter_new (sort, NULL); - gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter), - specific_sort_filter_remove_node_visible_func, - filter, NULL); + gtk_tree_path_free (path); +} +static void +filtered_rows_reordered_child_level_all_hidden (FilterTest *fixture, + gconstpointer user_data) +{ + GtkTreeIter iter1, iter4; + GtkTreePath *path; - window = gtk_window_new (GTK_WINDOW_TOPLEVEL); - tree_view = gtk_tree_view_new_with_model (filter); - gtk_container_add (GTK_CONTAINER (window), tree_view); - gtk_widget_realize (tree_view); + /* Expand row 0 */ + path = gtk_tree_path_new_from_indices (0, -1); + gtk_tree_view_expand_row (GTK_TREE_VIEW (fixture->tree_view), path, FALSE); + gtk_tree_path_free (path); - while (gtk_events_pending ()) - gtk_main_iteration (); + /* Hide last 4 paths */ + signal_monitor_append_signal (fixture->monitor, + ROW_DELETED, "0:4"); + signal_monitor_append_signal (fixture->monitor, + ROW_DELETED, "0:3"); + signal_monitor_append_signal (fixture->monitor, + ROW_DELETED, "0:2"); + signal_monitor_append_signal (fixture->monitor, + ROW_DELETED, "0:1"); + signal_monitor_append_signal (fixture->monitor, + ROW_DELETED, "0:0"); + signal_monitor_append_signal (fixture->monitor, + ROW_HAS_CHILD_TOGGLED, "0"); + set_path_visibility (fixture, "0:4", FALSE); + set_path_visibility (fixture, "0:3", FALSE); + set_path_visibility (fixture, "0:2", FALSE); + set_path_visibility (fixture, "0:1", FALSE); + set_path_visibility (fixture, "0:0", FALSE); + signal_monitor_assert_is_empty (fixture->monitor); - /* Remove a node */ - gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter); - gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter); - gtk_tree_store_remove (store, &iter); + gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store), + &iter1, "0:1"); + gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store), + &iter4, "0:4"); - while (gtk_events_pending ()) - gtk_main_iteration (); + gtk_tree_store_move_after (fixture->store, &iter1, &iter4); + signal_monitor_assert_is_empty (fixture->monitor); } - static void -specific_sort_filter_remove_root (void) +insert_before (void) { - /* This test is based on one of the test cases I found in my - * old test cases directory. I unfortunately do not have a record - * from who this test case originated. -Kris. + GtkTreeStore *store; + GtkTreeModel *filter; + GtkWidget *tree_view; + SignalMonitor *monitor; + GtkTreeIter iter; + GtkTreeIter last_iter; + GtkTreePath *path; + + /* This tests two aspects of the row-inserted handling: + * 1) If the newly inserted node was already handled by building + * the root level, don't handle it a second time. + * 2) Offsets of existing nodes must be updated when a new + * node is inserted. */ - GtkTreeModel *model, *sort, *filter; - GtkTreeIter root, mid, leaf; - GtkTreePath *path; + store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN); + filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (store), NULL); + gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (filter), + 1); - model = GTK_TREE_MODEL (gtk_tree_store_new (1, G_TYPE_INT)); - gtk_tree_store_append (GTK_TREE_STORE (model), &root, NULL); - gtk_tree_store_append (GTK_TREE_STORE (model), &mid, &root); - gtk_tree_store_append (GTK_TREE_STORE (model), &leaf, &mid); + tree_view = gtk_tree_view_new_with_model (filter); + monitor = signal_monitor_new (filter); - path = gtk_tree_model_get_path (model, &mid); + check_level_length (GTK_TREE_MODEL_FILTER (filter), NULL, 0); - sort = gtk_tree_model_sort_new_with_model (model); - filter = gtk_tree_model_filter_new (sort, path); + /* Insert 0 */ + path = gtk_tree_path_new_from_indices (0, -1); + signal_monitor_append_signal_path (monitor, ROW_INSERTED, path); + gtk_tree_path_free (path); - gtk_tree_store_remove (GTK_TREE_STORE (model), &root); + gtk_tree_store_insert_with_values (store, &iter, NULL, 0, + 0, "Foo", 1, TRUE, -1); - g_object_unref (filter); - g_object_unref (sort); - g_object_unref (model); -} + signal_monitor_assert_is_empty (monitor); + check_level_length (GTK_TREE_MODEL_FILTER (filter), NULL, 1); + /* Insert 1 */ + path = gtk_tree_path_new_from_indices (1, -1); + signal_monitor_append_signal_path (monitor, ROW_INSERTED, path); + gtk_tree_path_free (path); -static void -specific_root_mixed_visibility (void) -{ - int i; - GtkTreeModel *filter; - /* A bit nasty, apologies */ - FilterTest fixture; + gtk_tree_store_insert_with_values (store, &iter, NULL, 1, + 0, "Foo", 1, TRUE, -1); + last_iter = iter; - fixture.store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN); + signal_monitor_assert_is_empty (monitor); + check_level_length (GTK_TREE_MODEL_FILTER (filter), NULL, 2); - for (i = 0; i < LEVEL_LENGTH; i++) - { - GtkTreeIter iter; + /* Insert on 1 again -- invisible */ + gtk_tree_store_insert_with_values (store, &iter, NULL, 1, + 0, "Foo", 1, FALSE, -1); - gtk_tree_store_insert (fixture.store, &iter, NULL, i); - if (i % 2 == 0) - create_tree_store_set_values (fixture.store, &iter, TRUE); - else - create_tree_store_set_values (fixture.store, &iter, FALSE); - } + signal_monitor_assert_is_empty (monitor); + check_level_length (GTK_TREE_MODEL_FILTER (filter), NULL, 2); - filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (fixture.store), NULL); - fixture.filter = GTK_TREE_MODEL_FILTER (filter); - fixture.monitor = NULL; + /* Insert on 1 again -- visible */ + path = gtk_tree_path_new_from_indices (1, -1); + signal_monitor_append_signal_path (monitor, ROW_INSERTED, path); + gtk_tree_path_free (path); - gtk_tree_model_filter_set_visible_column (fixture.filter, 1); + gtk_tree_store_insert_with_values (store, &iter, NULL, 1, + 0, "Foo", 1, TRUE, -1); - /* In order to trigger the potential bug, we should not access - * the filter model here (so don't call the check functions). - */ + signal_monitor_assert_is_empty (monitor); + check_level_length (GTK_TREE_MODEL_FILTER (filter), NULL, 3); - /* Change visibility of an odd row to TRUE */ - set_path_visibility (&fixture, "3", TRUE); - check_filter_model (&fixture); - check_level_length (fixture.filter, NULL, 4); -} + /* Modify the iter that should be at the last position and check the + * signal we get. + */ + path = gtk_tree_path_new_from_indices (2, -1); + signal_monitor_append_signal_path (monitor, ROW_CHANGED, path); + gtk_tree_path_free (path); + gtk_tree_store_set (store, &last_iter, 0, "Foo changed", -1); + signal_monitor_assert_is_empty (monitor); + check_level_length (GTK_TREE_MODEL_FILTER (filter), NULL, 3); -static gboolean -specific_has_child_filter_filter_func (GtkTreeModel *model, - GtkTreeIter *iter, - gpointer data) -{ - return gtk_tree_model_iter_has_child (model, iter); + g_object_unref (filter); + g_object_unref (store); + gtk_widget_destroy (tree_view); } static void -specific_has_child_filter (void) +insert_child (void) { + GtkTreeStore *store; GtkTreeModel *filter; - GtkTreeIter iter, root; - /* A bit nasty, apologies */ - FilterTest fixture; - - fixture.store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN); - filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (fixture.store), NULL); - fixture.filter = GTK_TREE_MODEL_FILTER (filter); - fixture.monitor = NULL; + GtkWidget *tree_view; + SignalMonitor *monitor; + GtkTreeIter parent, iter; + GtkTreePath *path; - /* We will filter on parent state using a filter function. We will - * manually keep the boolean column in sync, so that we can use - * check_filter_model() to check the consistency of the model. - */ - /* FIXME: We need a check_filter_model() that is not tied to LEVEL_LENGTH - * to be able to check the structure here. We keep the calls to - * check_filter_model() commented out until then. - */ - gtk_tree_model_filter_set_visible_func (fixture.filter, - specific_has_child_filter_filter_func, - NULL, NULL); + store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN); - gtk_tree_store_append (fixture.store, &root, NULL); - create_tree_store_set_values (fixture.store, &root, FALSE); + gtk_tree_store_insert_with_values (store, &parent, NULL, 0, + 0, "Parent", 1, TRUE, -1); - /* check_filter_model (&fixture); */ - check_level_length (fixture.filter, NULL, 0); - gtk_tree_store_append (fixture.store, &iter, &root); - create_tree_store_set_values (fixture.store, &iter, TRUE); + filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (store), NULL); + gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (filter), + 1); - /* Parent must now be visible. Do the level length check first, - * to avoid modifying the child model triggering a row-changed to - * the filter model. + tree_view = gtk_tree_view_new_with_model (filter); + monitor = signal_monitor_new (filter); + + /* Insert child -- invisible */ + path = gtk_tree_path_new_from_indices (0, -1); + signal_monitor_append_signal_path (monitor, ROW_HAS_CHILD_TOGGLED, path); + /* The signal is received twice, once a pass through from GtkTreeStore + * and one generated by GtkTreeModelFilter. Not accurate, but cannot + * hurt. */ - check_level_length (fixture.filter, NULL, 1); - check_level_length (fixture.filter, "0", 0); + signal_monitor_append_signal_path (monitor, ROW_HAS_CHILD_TOGGLED, path); + gtk_tree_path_free (path); - set_path_visibility (&fixture, "0", TRUE); - /* check_filter_model (&fixture); */ + gtk_tree_store_insert_with_values (store, &iter, &parent, 1, + 0, "Child", 1, FALSE, -1); - gtk_tree_store_append (fixture.store, &root, NULL); - check_level_length (fixture.filter, NULL, 1); + signal_monitor_assert_is_empty (monitor); + check_level_length (GTK_TREE_MODEL_FILTER (filter), NULL, 1); - gtk_tree_store_append (fixture.store, &iter, &root); - check_level_length (fixture.filter, NULL, 2); - check_level_length (fixture.filter, "1", 0); + /* Insert child */ + path = gtk_tree_path_new_from_indices (0, 0, -1); + gtk_tree_path_up (path); /* 0 */ + signal_monitor_append_signal_path (monitor, ROW_HAS_CHILD_TOGGLED, path); + gtk_tree_path_free (path); - create_tree_store_set_values (fixture.store, &root, TRUE); - create_tree_store_set_values (fixture.store, &iter, TRUE); + gtk_tree_store_insert_with_values (store, &iter, &parent, 0, + 0, "Child", 1, TRUE, -1); - /* check_filter_model (&fixture); */ + signal_monitor_assert_is_empty (monitor); + check_level_length (GTK_TREE_MODEL_FILTER (filter), NULL, 1); - gtk_tree_store_append (fixture.store, &iter, &root); - create_tree_store_set_values (fixture.store, &iter, TRUE); - check_level_length (fixture.filter, NULL, 2); - check_level_length (fixture.filter, "0", 0); - check_level_length (fixture.filter, "1", 0); - - /* Now remove one of the remaining child rows */ - gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture.store), - &iter, "0:0"); - gtk_tree_store_remove (fixture.store, &iter); + /* Insert child -- invisible */ + gtk_tree_store_insert_with_values (store, &iter, &parent, 1, + 0, "Child", 1, FALSE, -1); - check_level_length (fixture.filter, NULL, 1); - check_level_length (fixture.filter, "0", 0); + signal_monitor_assert_is_empty (monitor); + check_level_length (GTK_TREE_MODEL_FILTER (filter), NULL, 1); - set_path_visibility (&fixture, "0", FALSE); - /* check_filter_model (&fixture); */ + g_object_unref (filter); + g_object_unref (store); + gtk_widget_destroy (tree_view); } -static gboolean -specific_root_has_child_filter_filter_func (GtkTreeModel *model, - GtkTreeIter *iter, - gpointer data) + +static void +remove_node (void) { - int depth; - GtkTreePath *path; + GtkTreeIter iter, iter1, iter2, iter3; + GtkListStore *list; + GtkTreeModel *filter; + GtkWidget *view G_GNUC_UNUSED; - path = gtk_tree_model_get_path (model, iter); - depth = gtk_tree_path_get_depth (path); + list = gtk_list_store_new (1, G_TYPE_INT); + gtk_list_store_insert_with_values (list, &iter1, 0, 0, 1, -1); + gtk_list_store_insert_with_values (list, &iter, 1, 0, 2, -1); + gtk_list_store_insert_with_values (list, &iter, 2, 0, 3, -1); + gtk_list_store_insert_with_values (list, &iter, 3, 0, 4, -1); + gtk_list_store_insert_with_values (list, &iter, 4, 0, 5, -1); + gtk_list_store_insert_with_values (list, &iter, 5, 0, 6, -1); + gtk_list_store_insert_with_values (list, &iter2, 6, 0, 7, -1); + gtk_list_store_insert_with_values (list, &iter3, 7, 0, 8, -1); + + filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (list), NULL); + view = gtk_tree_view_new_with_model (filter); + + gtk_list_store_remove (list, &iter1); + gtk_list_store_remove (list, &iter3); + gtk_list_store_remove (list, &iter2); + + gtk_widget_destroy (view); + g_object_unref (filter); + g_object_unref (list); +} + +static void +remove_node_vroot (void) +{ + GtkTreeIter parent, root; + GtkTreeIter iter, iter1, iter2, iter3; + GtkTreeStore *tree; + GtkTreeModel *filter; + GtkTreePath *path; + GtkWidget *view G_GNUC_UNUSED; + + tree = gtk_tree_store_new (1, G_TYPE_INT); + gtk_tree_store_insert_with_values (tree, &parent, NULL, 0, 0, 0, -1); + gtk_tree_store_insert_with_values (tree, &root, &parent, 0, 0, 0, -1); + + gtk_tree_store_insert_with_values (tree, &iter1, &root, 0, 0, 1, -1); + gtk_tree_store_insert_with_values (tree, &iter, &root, 1, 0, 2, -1); + gtk_tree_store_insert_with_values (tree, &iter, &root, 2, 0, 3, -1); + gtk_tree_store_insert_with_values (tree, &iter, &root, 3, 0, 4, -1); + gtk_tree_store_insert_with_values (tree, &iter, &root, 4, 0, 5, -1); + gtk_tree_store_insert_with_values (tree, &iter, &root, 5, 0, 6, -1); + gtk_tree_store_insert_with_values (tree, &iter2, &root, 6, 0, 7, -1); + gtk_tree_store_insert_with_values (tree, &iter3, &root, 7, 0, 8, -1); + + path = gtk_tree_path_new_from_indices (0, 0, -1); + filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (tree), path); gtk_tree_path_free (path); - if (depth > 1) - return TRUE; - /* else */ - return gtk_tree_model_iter_has_child (model, iter); + view = gtk_tree_view_new_with_model (filter); + + gtk_tree_store_remove (tree, &iter1); + gtk_tree_store_remove (tree, &iter3); + gtk_tree_store_remove (tree, &iter2); + + gtk_widget_destroy (view); + g_object_unref (filter); + g_object_unref (tree); } static void -specific_root_has_child_filter (void) +remove_vroot_ancestor (void) { + GtkTreeIter parent, root; + GtkTreeIter iter, iter1, iter2, iter3; + GtkTreeStore *tree; GtkTreeModel *filter; - GtkTreeIter iter, root; - /* A bit nasty, apologies */ - FilterTest fixture; + GtkTreePath *path; + GtkWidget *view G_GNUC_UNUSED; + + tree = gtk_tree_store_new (1, G_TYPE_INT); + gtk_tree_store_insert_with_values (tree, &parent, NULL, 0, 0, 0, -1); + gtk_tree_store_insert_with_values (tree, &root, &parent, 0, 0, 0, -1); + + gtk_tree_store_insert_with_values (tree, &iter1, &root, 0, 0, 1, -1); + gtk_tree_store_insert_with_values (tree, &iter, &root, 1, 0, 2, -1); + gtk_tree_store_insert_with_values (tree, &iter, &root, 2, 0, 3, -1); + gtk_tree_store_insert_with_values (tree, &iter, &root, 3, 0, 4, -1); + gtk_tree_store_insert_with_values (tree, &iter, &root, 4, 0, 5, -1); + gtk_tree_store_insert_with_values (tree, &iter, &root, 5, 0, 6, -1); + gtk_tree_store_insert_with_values (tree, &iter2, &root, 6, 0, 7, -1); + gtk_tree_store_insert_with_values (tree, &iter3, &root, 7, 0, 8, -1); + + path = gtk_tree_path_new_from_indices (0, 0, -1); + filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (tree), path); + gtk_tree_path_free (path); - /* This is a variation on the above test case wherein the has-child - * check for visibility only applies to root level nodes. - */ + view = gtk_tree_view_new_with_model (filter); - fixture.store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN); - filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (fixture.store), NULL); - fixture.filter = GTK_TREE_MODEL_FILTER (filter); - fixture.monitor = NULL; + gtk_tree_store_remove (tree, &parent); - /* We will filter on parent state using a filter function. We will - * manually keep the boolean column in sync, so that we can use - * check_filter_model() to check the consistency of the model. - */ - /* FIXME: We need a check_filter_model() that is not tied to LEVEL_LENGTH - * to be able to check the structure here. We keep the calls to - * check_filter_model() commented out until then. - */ - gtk_tree_model_filter_set_visible_func (fixture.filter, - specific_root_has_child_filter_filter_func, - NULL, NULL); + gtk_widget_destroy (view); + g_object_unref (filter); + g_object_unref (tree); +} - gtk_tree_store_append (fixture.store, &root, NULL); - create_tree_store_set_values (fixture.store, &root, FALSE); +static void +ref_count_single_level (void) +{ + GtkTreeIter iter[5]; + GtkTreeModel *model; + GtkTreeModelRefCount *ref_model; + GtkTreeModel *filter_model; + GtkWidget *tree_view; - /* check_filter_model (&fixture); */ - check_level_length (fixture.filter, NULL, 0); + model = gtk_tree_model_ref_count_new (); + ref_model = GTK_TREE_MODEL_REF_COUNT (model); - gtk_tree_store_append (fixture.store, &iter, &root); - create_tree_store_set_values (fixture.store, &iter, TRUE); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter[0], NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter[1], NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter[2], NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter[3], NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter[4], NULL); - /* Parent must now be visible. Do the level length check first, - * to avoid modifying the child model triggering a row-changed to - * the filter model. + assert_root_level_unreferenced (ref_model); + + filter_model = gtk_tree_model_filter_new (model, NULL); + tree_view = gtk_tree_view_new_with_model (filter_model); + + assert_node_ref_count (ref_model, &iter[0], 2); + assert_node_ref_count (ref_model, &iter[1], 1); + assert_node_ref_count (ref_model, &iter[2], 1); + assert_node_ref_count (ref_model, &iter[3], 1); + assert_node_ref_count (ref_model, &iter[4], 1); + + gtk_widget_destroy (tree_view); + + assert_node_ref_count (ref_model, &iter[0], 1); + assert_node_ref_count (ref_model, &iter[1], 0); + assert_node_ref_count (ref_model, &iter[2], 0); + assert_node_ref_count (ref_model, &iter[3], 0); + assert_node_ref_count (ref_model, &iter[4], 0); + + g_object_unref (filter_model); + + assert_node_ref_count (ref_model, &iter[0], 0); + + g_object_unref (ref_model); +} + +static void +ref_count_two_levels (void) +{ + GtkTreeIter parent1, parent2, iter, iter_first; + GtkTreeModel *model; + GtkTreeModelRefCount *ref_model; + GtkTreeModel *filter_model; + GtkWidget *tree_view; + + model = gtk_tree_model_ref_count_new (); + ref_model = GTK_TREE_MODEL_REF_COUNT (model); + + gtk_tree_store_append (GTK_TREE_STORE (model), &parent1, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &parent2, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter_first, &parent2); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, &parent2); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, &parent2); + + assert_entire_model_unreferenced (ref_model); + + filter_model = gtk_tree_model_filter_new (model, NULL); + tree_view = gtk_tree_view_new_with_model (filter_model); + + /* This is quite confusing: + * - node 0 has a ref count of 2 because it is referenced as the + * first node in a level and by the tree view. + * - node 1 has a ref count of 2 because it is referenced by its + * child level and by the tree view. */ - check_level_length (fixture.filter, NULL, 1); - check_level_length (fixture.filter, "0", 1); + assert_root_level_referenced (ref_model, 2); + assert_node_ref_count (ref_model, &iter_first, 1); + assert_node_ref_count (ref_model, &iter, 0); - set_path_visibility (&fixture, "0", TRUE); - /* check_filter_model (&fixture); */ + gtk_tree_view_expand_all (GTK_TREE_VIEW (tree_view)); - gtk_tree_store_append (fixture.store, &root, NULL); - check_level_length (fixture.filter, NULL, 1); + assert_node_ref_count (ref_model, &parent1, 2); + assert_node_ref_count (ref_model, &parent2, 2); + assert_node_ref_count (ref_model, &iter_first, 2); + assert_node_ref_count (ref_model, &iter, 1); - gtk_tree_store_append (fixture.store, &iter, &root); - check_level_length (fixture.filter, NULL, 2); - check_level_length (fixture.filter, "1", 1); + gtk_tree_view_collapse_all (GTK_TREE_VIEW (tree_view)); - create_tree_store_set_values (fixture.store, &root, TRUE); - create_tree_store_set_values (fixture.store, &iter, TRUE); + /* The child level is not destroyed because its parent is visible */ + assert_node_ref_count (ref_model, &parent1, 2); + assert_node_ref_count (ref_model, &parent2, 2); + assert_node_ref_count (ref_model, &iter_first, 1); + assert_node_ref_count (ref_model, &iter, 0); - /* check_filter_model (&fixture); */ + gtk_tree_model_filter_clear_cache (GTK_TREE_MODEL_FILTER (filter_model)); - gtk_tree_store_append (fixture.store, &iter, &root); - create_tree_store_set_values (fixture.store, &iter, TRUE); - check_level_length (fixture.filter, NULL, 2); - check_level_length (fixture.filter, "0", 1); - check_level_length (fixture.filter, "1", 2); + assert_node_ref_count (ref_model, &parent1, 2); + assert_node_ref_count (ref_model, &parent2, 2); + assert_node_ref_count (ref_model, &iter_first, 1); + assert_node_ref_count (ref_model, &iter, 0); - /* Now remove one of the remaining child rows */ - gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture.store), - &iter, "0:0"); - gtk_tree_store_remove (fixture.store, &iter); + gtk_widget_destroy (tree_view); - check_level_length (fixture.filter, NULL, 1); - check_level_length (fixture.filter, "0", 2); + assert_root_level_referenced (ref_model, 1); + assert_node_ref_count (ref_model, &iter_first, 1); + assert_node_ref_count (ref_model, &iter, 0); - set_path_visibility (&fixture, "0", FALSE); - /* check_filter_model (&fixture); */ -} + gtk_tree_model_filter_clear_cache (GTK_TREE_MODEL_FILTER (filter_model)); + /* The root level and first level remain cached, only the references on the + * first nodes of these levels are kept. + */ + assert_node_ref_count (ref_model, &parent1, 1); + assert_node_ref_count (ref_model, &parent2, 1); + assert_node_ref_count (ref_model, &iter_first, 1); + assert_node_ref_count (ref_model, &iter, 0); + + g_object_unref (filter_model); + g_object_unref (ref_model); +} static void -specific_filter_add_child (void) +ref_count_three_levels (void) { - /* This test is based on one of the test cases I found in my - * old test cases directory. I unfortunately do not have a record - * from who this test case originated. -Kris. + GtkTreeIter grandparent1, grandparent2, parent1, parent2; + GtkTreeIter iter_parent1, iter_parent2, iter_parent2_first; + GtkTreeModel *model; + GtkTreeModelRefCount *ref_model; + GtkTreeModel *filter_model; + GtkTreePath *path; + GtkWidget *tree_view; + + model = gtk_tree_model_ref_count_new (); + ref_model = GTK_TREE_MODEL_REF_COUNT (model); + + /* + grandparent1 + * + grandparent2 + * + parent1 + * + iter_parent1 + * + parent2 + * + iter_parent2_first + * + iter_parent2 */ - GtkTreeIter iter; - GtkTreeIter iter_first; - GtkTreeIter child; - GtkTreeStore *store; - GtkTreeModel *filter; + gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent1, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent2, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &parent1, &grandparent2); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter_parent1, &parent1); + gtk_tree_store_append (GTK_TREE_STORE (model), &parent2, &grandparent2); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter_parent2_first, &parent2); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter_parent2, &parent2); - store = gtk_tree_store_new (1, G_TYPE_STRING); + assert_entire_model_unreferenced (ref_model); - gtk_tree_store_append (store, &iter_first, NULL); - gtk_tree_store_set (store, &iter_first, 0, "Hello", -1); + filter_model = gtk_tree_model_filter_new (model, NULL); + tree_view = gtk_tree_view_new_with_model (filter_model); - gtk_tree_store_append (store, &iter, NULL); - gtk_tree_store_set (store, &iter, 0, "Hello", -1); + /* This is quite confusing: + * - node 0 has a ref count of 2 because it is referenced as the + * first node in a level and by the tree view. + * - node 1 has a ref count of 2 because it is referenced by its + * child level and by the tree view. + */ + assert_root_level_referenced (ref_model, 2); + assert_node_ref_count (ref_model, &parent1, 1); + assert_node_ref_count (ref_model, &parent2, 0); + assert_level_unreferenced (ref_model, &parent1); + assert_level_unreferenced (ref_model, &parent2); + + path = gtk_tree_path_new_from_indices (1, -1); + gtk_tree_view_expand_row (GTK_TREE_VIEW (tree_view), path, FALSE); + + assert_node_ref_count (ref_model, &grandparent1, 2); + assert_node_ref_count (ref_model, &grandparent2, 2); + assert_node_ref_count (ref_model, &parent1, 3); + assert_node_ref_count (ref_model, &parent2, 2); + assert_node_ref_count (ref_model, &iter_parent1, 1); + assert_node_ref_count (ref_model, &iter_parent2_first, 1); + assert_node_ref_count (ref_model, &iter_parent2, 0); + + gtk_tree_view_expand_row (GTK_TREE_VIEW (tree_view), path, TRUE); + + assert_node_ref_count (ref_model, &grandparent1, 2); + assert_node_ref_count (ref_model, &grandparent2, 2); + assert_node_ref_count (ref_model, &parent1, 3); + assert_node_ref_count (ref_model, &parent2, 2); + assert_node_ref_count (ref_model, &iter_parent1, 2); + assert_node_ref_count (ref_model, &iter_parent2_first, 2); + assert_node_ref_count (ref_model, &iter_parent2, 1); - gtk_tree_store_append (store, &iter, NULL); - gtk_tree_store_set (store, &iter, 0, "Hello", -1); + gtk_tree_view_collapse_all (GTK_TREE_VIEW (tree_view)); - gtk_tree_store_append (store, &iter, NULL); - gtk_tree_store_set (store, &iter, 0, "Hello", -1); + assert_node_ref_count (ref_model, &grandparent1, 2); + assert_node_ref_count (ref_model, &grandparent2, 2); + assert_node_ref_count (ref_model, &parent1, 2); + assert_node_ref_count (ref_model, &parent2, 1); + assert_node_ref_count (ref_model, &iter_parent1, 1); + assert_node_ref_count (ref_model, &iter_parent2_first, 1); + assert_node_ref_count (ref_model, &iter_parent2, 0); + + gtk_tree_model_filter_clear_cache (GTK_TREE_MODEL_FILTER (filter_model)); + + assert_node_ref_count (ref_model, &grandparent1, 2); + assert_node_ref_count (ref_model, &grandparent2, 2); + assert_node_ref_count (ref_model, &parent1, 1); + assert_node_ref_count (ref_model, &parent2, 0); + assert_node_ref_count (ref_model, &iter_parent1, 0); + assert_node_ref_count (ref_model, &iter_parent2_first, 0); + assert_node_ref_count (ref_model, &iter_parent2, 0); + + gtk_tree_view_expand_row (GTK_TREE_VIEW (tree_view), path, FALSE); + + assert_node_ref_count (ref_model, &grandparent1, 2); + assert_node_ref_count (ref_model, &grandparent2, 2); + assert_node_ref_count (ref_model, &parent1, 3); + assert_node_ref_count (ref_model, &parent2, 2); + assert_node_ref_count (ref_model, &iter_parent1, 1); + assert_node_ref_count (ref_model, &iter_parent2_first, 1); + assert_node_ref_count (ref_model, &iter_parent2, 0); + + gtk_tree_path_append_index (path, 1); + gtk_tree_view_expand_row (GTK_TREE_VIEW (tree_view), path, FALSE); + + assert_node_ref_count (ref_model, &grandparent1, 2); + assert_node_ref_count (ref_model, &grandparent2, 2); + assert_node_ref_count (ref_model, &parent1, 3); + assert_node_ref_count (ref_model, &parent2, 2); + assert_node_ref_count (ref_model, &iter_parent1, 1); + assert_node_ref_count (ref_model, &iter_parent2_first, 2); + assert_node_ref_count (ref_model, &iter_parent2, 1); + + gtk_tree_view_collapse_row (GTK_TREE_VIEW (tree_view), path); + + assert_node_ref_count (ref_model, &grandparent1, 2); + assert_node_ref_count (ref_model, &grandparent2, 2); + assert_node_ref_count (ref_model, &parent1, 3); + assert_node_ref_count (ref_model, &parent2, 2); + assert_node_ref_count (ref_model, &iter_parent1, 1); + assert_node_ref_count (ref_model, &iter_parent2_first, 1); + assert_node_ref_count (ref_model, &iter_parent2, 0); + + gtk_tree_model_filter_clear_cache (GTK_TREE_MODEL_FILTER (filter_model)); + + assert_node_ref_count (ref_model, &grandparent1, 2); + assert_node_ref_count (ref_model, &grandparent2, 2); + assert_node_ref_count (ref_model, &parent1, 3); + assert_node_ref_count (ref_model, &parent2, 2); + assert_node_ref_count (ref_model, &iter_parent1, 1); + assert_node_ref_count (ref_model, &iter_parent2_first, 1); + assert_node_ref_count (ref_model, &iter_parent2, 0); - filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (store), NULL); + gtk_tree_path_up (path); + gtk_tree_view_collapse_row (GTK_TREE_VIEW (tree_view), path); + gtk_tree_path_free (path); - gtk_tree_store_set (store, &iter, 0, "Hello", -1); - gtk_tree_store_append (store, &child, &iter_first); - gtk_tree_store_set (store, &child, 0, "Hello", -1); + assert_node_ref_count (ref_model, &grandparent1, 2); + assert_node_ref_count (ref_model, &grandparent2, 2); + assert_node_ref_count (ref_model, &parent1, 2); + assert_node_ref_count (ref_model, &parent2, 1); + assert_node_ref_count (ref_model, &iter_parent1, 1); + assert_node_ref_count (ref_model, &iter_parent2_first, 1); + assert_node_ref_count (ref_model, &iter_parent2, 0); + + gtk_tree_model_filter_clear_cache (GTK_TREE_MODEL_FILTER (filter_model)); + + assert_node_ref_count (ref_model, &grandparent1, 2); + assert_node_ref_count (ref_model, &grandparent2, 2); + assert_node_ref_count (ref_model, &parent1, 1); + assert_node_ref_count (ref_model, &parent2, 0); + assert_node_ref_count (ref_model, &iter_parent1, 0); + assert_node_ref_count (ref_model, &iter_parent2_first, 0); + assert_node_ref_count (ref_model, &iter_parent2, 0); + + gtk_widget_destroy (tree_view); + + gtk_tree_model_filter_clear_cache (GTK_TREE_MODEL_FILTER (filter_model)); + + /* The root level and first level remain cached, only the references on the + * first nodes of these levels are kept. Grandparent2 is the parent + * of the first level with parent1, so grandparent2 keeps a reference + * as well. + */ + assert_node_ref_count (ref_model, &grandparent1, 1); + assert_node_ref_count (ref_model, &grandparent2, 1); + assert_node_ref_count (ref_model, &parent1, 1); + assert_node_ref_count (ref_model, &parent2, 0); + assert_node_ref_count (ref_model, &iter_parent1, 0); + assert_node_ref_count (ref_model, &iter_parent2_first, 0); + assert_node_ref_count (ref_model, &iter_parent2, 0); + + g_object_unref (filter_model); + g_object_unref (ref_model); } static void -specific_list_store_clear (void) +ref_count_delete_row (void) { - GtkTreeIter iter; - GtkListStore *list; - GtkTreeModel *filter; - GtkWidget *view; + GtkTreeIter grandparent1, grandparent2, parent1, parent2; + GtkTreeIter iter_parent1, iter_parent2, iter_parent2_first; + GtkTreeModel *model; + GtkTreeModelRefCount *ref_model; + GtkTreeModel *filter_model; + GtkTreePath *path; + GtkWidget *tree_view; - list = gtk_list_store_new (1, G_TYPE_INT); - gtk_list_store_insert_with_values (list, &iter, 0, 0, 1, -1); - gtk_list_store_insert_with_values (list, &iter, 1, 0, 2, -1); - gtk_list_store_insert_with_values (list, &iter, 2, 0, 3, -1); - gtk_list_store_insert_with_values (list, &iter, 3, 0, 4, -1); - gtk_list_store_insert_with_values (list, &iter, 4, 0, 5, -1); - gtk_list_store_insert_with_values (list, &iter, 5, 0, 6, -1); - gtk_list_store_insert_with_values (list, &iter, 6, 0, 7, -1); - gtk_list_store_insert_with_values (list, &iter, 7, 0, 8, -1); + model = gtk_tree_model_ref_count_new (); + ref_model = GTK_TREE_MODEL_REF_COUNT (model); - filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (list), NULL); - view = gtk_tree_view_new_with_model (filter); + /* + grandparent1 + * + grandparent2 + * + parent1 + * + iter_parent1 + * + parent2 + * + iter_parent2_first + * + iter_parent2 + */ - gtk_list_store_clear (list); + gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent1, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent2, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &parent1, &grandparent2); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter_parent1, &parent1); + gtk_tree_store_append (GTK_TREE_STORE (model), &parent2, &grandparent2); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter_parent2_first, &parent2); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter_parent2, &parent2); + + assert_entire_model_unreferenced (ref_model); + + filter_model = gtk_tree_model_filter_new (model, NULL); + tree_view = gtk_tree_view_new_with_model (filter_model); + + assert_root_level_referenced (ref_model, 2); + assert_node_ref_count (ref_model, &parent1, 1); + assert_node_ref_count (ref_model, &parent2, 0); + assert_level_unreferenced (ref_model, &parent1); + assert_level_unreferenced (ref_model, &parent2); + + path = gtk_tree_path_new_from_indices (1, -1); + gtk_tree_view_expand_row (GTK_TREE_VIEW (tree_view), path, TRUE); + gtk_tree_path_free (path); + + assert_node_ref_count (ref_model, &grandparent1, 2); + assert_node_ref_count (ref_model, &grandparent2, 2); + assert_node_ref_count (ref_model, &parent1, 3); + assert_node_ref_count (ref_model, &parent2, 2); + assert_node_ref_count (ref_model, &iter_parent1, 2); + assert_node_ref_count (ref_model, &iter_parent2_first, 2); + assert_node_ref_count (ref_model, &iter_parent2, 1); + + gtk_tree_store_remove (GTK_TREE_STORE (model), &iter_parent2); + + assert_node_ref_count (ref_model, &grandparent1, 2); + assert_node_ref_count (ref_model, &grandparent2, 2); + assert_node_ref_count (ref_model, &parent1, 3); + assert_node_ref_count (ref_model, &parent2, 2); + assert_node_ref_count (ref_model, &iter_parent1, 2); + assert_node_ref_count (ref_model, &iter_parent2_first, 2); + + gtk_tree_store_remove (GTK_TREE_STORE (model), &parent1); + + assert_node_ref_count (ref_model, &grandparent1, 2); + assert_node_ref_count (ref_model, &grandparent2, 2); + assert_node_ref_count (ref_model, &parent2, 3); + assert_level_referenced (ref_model, 2, &parent2); + + gtk_tree_store_remove (GTK_TREE_STORE (model), &grandparent2); + + assert_node_ref_count (ref_model, &grandparent1, 2); + + gtk_tree_model_filter_clear_cache (GTK_TREE_MODEL_FILTER (filter_model)); + + assert_node_ref_count (ref_model, &grandparent1, 2); + + gtk_widget_destroy (tree_view); + gtk_tree_model_filter_clear_cache (GTK_TREE_MODEL_FILTER (filter_model)); + + assert_node_ref_count (ref_model, &grandparent1, 1); + + g_object_unref (filter_model); + + assert_node_ref_count (ref_model, &grandparent1, 0); + + g_object_unref (ref_model); } static void -specific_bug_300089 (void) +ref_count_filter_row_length_1 (void) { - /* Test case for GNOME Bugzilla bug 300089. Written by - * Matthias Clasen. - */ - GtkTreeModel *sort_model, *child_model; + GtkTreeIter level1_1; + GtkTreeIter level2_1; + GtkTreeIter level3_1; + GtkTreeIter level4_1; + GtkTreeModel *model; + GtkTreeModelRefCount *ref_model; + GtkTreeModel *filter_model; GtkTreePath *path; - GtkTreeIter iter, iter2, sort_iter; + GtkWidget *tree_view; + GType column_types[] = { G_TYPE_BOOLEAN }; + + model = gtk_tree_model_ref_count_new (); + ref_model = GTK_TREE_MODEL_REF_COUNT (model); - child_model = GTK_TREE_MODEL (gtk_tree_store_new (1, G_TYPE_STRING)); + gtk_tree_store_set_column_types (GTK_TREE_STORE (model), 1, + column_types); - gtk_tree_store_append (GTK_TREE_STORE (child_model), &iter, NULL); - gtk_tree_store_set (GTK_TREE_STORE (child_model), &iter, 0, "A", -1); - gtk_tree_store_append (GTK_TREE_STORE (child_model), &iter, NULL); - gtk_tree_store_set (GTK_TREE_STORE (child_model), &iter, 0, "B", -1); - gtk_tree_store_append (GTK_TREE_STORE (child_model), &iter2, &iter); - gtk_tree_store_set (GTK_TREE_STORE (child_model), &iter2, 0, "D", -1); - gtk_tree_store_append (GTK_TREE_STORE (child_model), &iter2, &iter); - gtk_tree_store_set (GTK_TREE_STORE (child_model), &iter2, 0, "E", -1); + /* + level1_1 + * + level2_1 + * + level3_1 + * + level4_1 + * + * Node level1_1 is expanded. This makes that levels 1 and 2 are + * visible. Level 3 is cached because its parent is visible. Level 4 + * is not cached. + */ - gtk_tree_store_append (GTK_TREE_STORE (child_model), &iter, NULL); - gtk_tree_store_set (GTK_TREE_STORE (child_model), &iter, 0, "C", -1); + gtk_tree_store_append (GTK_TREE_STORE (model), &level1_1, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &level2_1, &level1_1); + gtk_tree_store_append (GTK_TREE_STORE (model), &level3_1, &level2_1); + gtk_tree_store_append (GTK_TREE_STORE (model), &level4_1, &level3_1); + gtk_tree_store_set (GTK_TREE_STORE (model), &level1_1, 0, TRUE, -1); + gtk_tree_store_set (GTK_TREE_STORE (model), &level2_1, 0, TRUE, -1); + gtk_tree_store_set (GTK_TREE_STORE (model), &level3_1, 0, TRUE, -1); + gtk_tree_store_set (GTK_TREE_STORE (model), &level4_1, 0, TRUE, -1); - sort_model = GTK_TREE_MODEL (gtk_tree_model_sort_new_with_model (child_model)); - gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sort_model), - 0, GTK_SORT_ASCENDING); + assert_entire_model_unreferenced (ref_model); - path = gtk_tree_path_new_from_indices (1, 1, -1); + filter_model = gtk_tree_model_filter_new (model, NULL); + gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (filter_model), 0); + tree_view = gtk_tree_view_new_with_model (filter_model); - /* make sure a level is constructed */ - gtk_tree_model_get_iter (sort_model, &sort_iter, path); + assert_node_ref_count (ref_model, &level1_1, 3); + assert_node_ref_count (ref_model, &level2_1, 1); + assert_node_ref_count (ref_model, &level3_1, 0); + assert_node_ref_count (ref_model, &level4_1, 0); - /* change the "E" row in a way that causes it to change position */ - gtk_tree_model_get_iter (child_model, &iter, path); - gtk_tree_store_set (GTK_TREE_STORE (child_model), &iter, 0, "A", -1); -} + path = gtk_tree_path_new_from_indices (0, -1); + gtk_tree_view_expand_row (GTK_TREE_VIEW (tree_view), path, FALSE); + gtk_tree_path_free (path); + assert_node_ref_count (ref_model, &level1_1, 3); + assert_node_ref_count (ref_model, &level2_1, 3); + assert_node_ref_count (ref_model, &level3_1, 1); + assert_node_ref_count (ref_model, &level4_1, 0); -static int -specific_bug_301558_sort_func (GtkTreeModel *model, - GtkTreeIter *a, - GtkTreeIter *b, - gpointer data) -{ - int i, j; + gtk_tree_store_set (GTK_TREE_STORE (model), &level4_1, 0, FALSE, -1); - gtk_tree_model_get (model, a, 0, &i, -1); - gtk_tree_model_get (model, b, 0, &j, -1); + assert_node_ref_count (ref_model, &level1_1, 3); + assert_node_ref_count (ref_model, &level2_1, 3); + assert_node_ref_count (ref_model, &level3_1, 1); + assert_node_ref_count (ref_model, &level4_1, 0); - return j - i; + /* level3_1 has a visible parent, so the node is kept in the cache. */ + gtk_tree_store_set (GTK_TREE_STORE (model), &level3_1, 0, FALSE, -1); + + assert_node_ref_count (ref_model, &level1_1, 3); + assert_node_ref_count (ref_model, &level2_1, 3); + assert_node_ref_count (ref_model, &level3_1, 1); + assert_node_ref_count (ref_model, &level4_1, 0); + + /* level2_1 has a visible parent, so is kept in the cache. However, + * the external reference should be released. + */ + gtk_tree_store_set (GTK_TREE_STORE (model), &level2_1, 0, FALSE, -1); + + assert_node_ref_count (ref_model, &level1_1, 3); + assert_node_ref_count (ref_model, &level2_1, 1); + assert_node_ref_count (ref_model, &level3_1, 0); + assert_node_ref_count (ref_model, &level4_1, 0); + + gtk_tree_store_set (GTK_TREE_STORE (model), &level1_1, 0, FALSE, -1); + + assert_node_ref_count (ref_model, &level1_1, 2); + assert_node_ref_count (ref_model, &level2_1, 1); + assert_node_ref_count (ref_model, &level3_1, 0); + assert_node_ref_count (ref_model, &level4_1, 0); + + gtk_widget_destroy (tree_view); + gtk_tree_model_filter_clear_cache (GTK_TREE_MODEL_FILTER (filter_model)); + + assert_node_ref_count (ref_model, &level1_1, 2); + assert_node_ref_count (ref_model, &level2_1, 1); + + g_object_unref (filter_model); + + assert_node_ref_count (ref_model, &level1_1, 0); + + g_object_unref (ref_model); +} + +static void +ref_count_filter_row_length_1_remove_in_root_level (void) +{ + GtkTreeIter level1_1; + GtkTreeIter level2_1; + GtkTreeIter level3_1; + GtkTreeIter level4_1; + GtkTreeModel *model; + GtkTreeModelRefCount *ref_model; + GtkTreeModel *filter_model; + GtkTreePath *path; + GtkWidget *tree_view; + GType column_types[] = { G_TYPE_BOOLEAN }; + + model = gtk_tree_model_ref_count_new (); + ref_model = GTK_TREE_MODEL_REF_COUNT (model); + + gtk_tree_store_set_column_types (GTK_TREE_STORE (model), 1, + column_types); + + + /* + level1_1 + * + level2_1 + * + level3_1 + * + level4_1 + */ + + gtk_tree_store_append (GTK_TREE_STORE (model), &level1_1, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &level2_1, &level1_1); + gtk_tree_store_append (GTK_TREE_STORE (model), &level3_1, &level2_1); + gtk_tree_store_append (GTK_TREE_STORE (model), &level4_1, &level3_1); + + gtk_tree_store_set (GTK_TREE_STORE (model), &level1_1, 0, TRUE, -1); + gtk_tree_store_set (GTK_TREE_STORE (model), &level2_1, 0, TRUE, -1); + gtk_tree_store_set (GTK_TREE_STORE (model), &level3_1, 0, TRUE, -1); + gtk_tree_store_set (GTK_TREE_STORE (model), &level4_1, 0, TRUE, -1); + + assert_entire_model_unreferenced (ref_model); + + filter_model = gtk_tree_model_filter_new (model, NULL); + gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (filter_model), 0); + tree_view = gtk_tree_view_new_with_model (filter_model); + + assert_node_ref_count (ref_model, &level1_1, 3); + assert_node_ref_count (ref_model, &level2_1, 1); + assert_node_ref_count (ref_model, &level3_1, 0); + assert_node_ref_count (ref_model, &level4_1, 0); + + path = gtk_tree_path_new_from_indices (0, -1); + gtk_tree_view_expand_row (GTK_TREE_VIEW (tree_view), path, TRUE); + gtk_tree_path_free (path); + + assert_node_ref_count (ref_model, &level1_1, 3); + assert_node_ref_count (ref_model, &level2_1, 3); + assert_node_ref_count (ref_model, &level3_1, 3); + assert_node_ref_count (ref_model, &level4_1, 2); + + gtk_tree_store_set (GTK_TREE_STORE (model), &level1_1, 0, FALSE, -1); + + assert_node_ref_count (ref_model, &level1_1, 2); + assert_node_ref_count (ref_model, &level2_1, 1); + assert_node_ref_count (ref_model, &level3_1, 0); + assert_node_ref_count (ref_model, &level4_1, 0); + + gtk_widget_destroy (tree_view); + gtk_tree_model_filter_clear_cache (GTK_TREE_MODEL_FILTER (filter_model)); + + assert_node_ref_count (ref_model, &level1_1, 2); + assert_node_ref_count (ref_model, &level2_1, 1); + assert_node_ref_count (ref_model, &level3_1, 0); + assert_node_ref_count (ref_model, &level4_1, 0); + + g_object_unref (filter_model); + + assert_node_ref_count (ref_model, &level1_1, 0); + assert_node_ref_count (ref_model, &level2_1, 0); + assert_node_ref_count (ref_model, &level3_1, 0); + assert_node_ref_count (ref_model, &level4_1, 0); + + g_object_unref (ref_model); +} + +static void +ref_count_filter_row_length_1_remove_in_child_level (void) +{ + GtkTreeIter level1_1; + GtkTreeIter level2_1; + GtkTreeIter level3_1; + GtkTreeIter level4_1; + GtkTreeModel *model; + GtkTreeModelRefCount *ref_model; + GtkTreeModel *filter_model; + GtkTreePath *path; + GtkWidget *tree_view; + GType column_types[] = { G_TYPE_BOOLEAN }; + + model = gtk_tree_model_ref_count_new (); + ref_model = GTK_TREE_MODEL_REF_COUNT (model); + + gtk_tree_store_set_column_types (GTK_TREE_STORE (model), 1, + column_types); + + + /* + level1_1 + * + level2_1 + * + level3_1 + * + level4_1 + */ + + gtk_tree_store_append (GTK_TREE_STORE (model), &level1_1, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &level2_1, &level1_1); + gtk_tree_store_append (GTK_TREE_STORE (model), &level3_1, &level2_1); + gtk_tree_store_append (GTK_TREE_STORE (model), &level4_1, &level3_1); + + gtk_tree_store_set (GTK_TREE_STORE (model), &level1_1, 0, TRUE, -1); + gtk_tree_store_set (GTK_TREE_STORE (model), &level2_1, 0, TRUE, -1); + gtk_tree_store_set (GTK_TREE_STORE (model), &level3_1, 0, TRUE, -1); + gtk_tree_store_set (GTK_TREE_STORE (model), &level4_1, 0, TRUE, -1); + + assert_entire_model_unreferenced (ref_model); + + filter_model = gtk_tree_model_filter_new (model, NULL); + gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (filter_model), 0); + tree_view = gtk_tree_view_new_with_model (filter_model); + + assert_node_ref_count (ref_model, &level1_1, 3); + assert_node_ref_count (ref_model, &level2_1, 1); + assert_node_ref_count (ref_model, &level3_1, 0); + assert_node_ref_count (ref_model, &level4_1, 0); + + path = gtk_tree_path_new_from_indices (0, -1); + gtk_tree_view_expand_row (GTK_TREE_VIEW (tree_view), path, TRUE); + gtk_tree_path_free (path); + + assert_node_ref_count (ref_model, &level1_1, 3); + assert_node_ref_count (ref_model, &level2_1, 3); + assert_node_ref_count (ref_model, &level3_1, 3); + assert_node_ref_count (ref_model, &level4_1, 2); + + gtk_tree_store_set (GTK_TREE_STORE (model), &level2_1, 0, FALSE, -1); + + assert_node_ref_count (ref_model, &level1_1, 3); + assert_node_ref_count (ref_model, &level2_1, 1); + assert_node_ref_count (ref_model, &level3_1, 0); + assert_node_ref_count (ref_model, &level4_1, 0); + + gtk_widget_destroy (tree_view); + gtk_tree_model_filter_clear_cache (GTK_TREE_MODEL_FILTER (filter_model)); + + assert_node_ref_count (ref_model, &level1_1, 2); + assert_node_ref_count (ref_model, &level2_1, 1); + assert_node_ref_count (ref_model, &level3_1, 0); + assert_node_ref_count (ref_model, &level4_1, 0); + + g_object_unref (filter_model); + + assert_node_ref_count (ref_model, &level1_1, 0); + assert_node_ref_count (ref_model, &level2_1, 0); + assert_node_ref_count (ref_model, &level3_1, 0); + assert_node_ref_count (ref_model, &level4_1, 0); + + g_object_unref (ref_model); +} + +static void +ref_count_filter_row_length_gt_1 (void) +{ + GtkTreeIter level1_1, level1_2; + GtkTreeIter level2_1, level2_2; + GtkTreeIter level3_1, level3_2; + GtkTreeIter level4_1, level4_2; + GtkTreeModel *model; + GtkTreeModelRefCount *ref_model; + GtkTreeModel *filter_model; + GtkTreePath *path; + GtkWidget *tree_view; + GType column_types[] = { G_TYPE_BOOLEAN }; + + model = gtk_tree_model_ref_count_new (); + ref_model = GTK_TREE_MODEL_REF_COUNT (model); + + gtk_tree_store_set_column_types (GTK_TREE_STORE (model), 1, + column_types); + + + /* + level1_1 + * + level1_2 + * + level2_1 + * + level2_2 + * + level3_1 + * + level3_2 + * + level4_1 + * + level4_2 + * + * Node level1_2 is expanded. This makes that levels 1 and 2 are + * visible. Level 3 is cached because its parent is visible. Level 4 + * is not cached. + */ + + gtk_tree_store_append (GTK_TREE_STORE (model), &level1_1, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &level1_2, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &level2_1, &level1_2); + gtk_tree_store_append (GTK_TREE_STORE (model), &level2_2, &level1_2); + gtk_tree_store_append (GTK_TREE_STORE (model), &level3_1, &level2_2); + gtk_tree_store_append (GTK_TREE_STORE (model), &level3_2, &level2_2); + gtk_tree_store_append (GTK_TREE_STORE (model), &level4_1, &level3_2); + gtk_tree_store_append (GTK_TREE_STORE (model), &level4_2, &level3_2); + + gtk_tree_store_set (GTK_TREE_STORE (model), &level1_1, 0, TRUE, -1); + gtk_tree_store_set (GTK_TREE_STORE (model), &level1_2, 0, TRUE, -1); + gtk_tree_store_set (GTK_TREE_STORE (model), &level2_1, 0, TRUE, -1); + gtk_tree_store_set (GTK_TREE_STORE (model), &level2_2, 0, TRUE, -1); + gtk_tree_store_set (GTK_TREE_STORE (model), &level3_1, 0, TRUE, -1); + gtk_tree_store_set (GTK_TREE_STORE (model), &level3_2, 0, TRUE, -1); + gtk_tree_store_set (GTK_TREE_STORE (model), &level4_1, 0, TRUE, -1); + gtk_tree_store_set (GTK_TREE_STORE (model), &level4_2, 0, TRUE, -1); + + assert_entire_model_unreferenced (ref_model); + + filter_model = gtk_tree_model_filter_new (model, NULL); + gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (filter_model), 0); + tree_view = gtk_tree_view_new_with_model (filter_model); + + assert_node_ref_count (ref_model, &level1_1, 2); + assert_node_ref_count (ref_model, &level1_2, 2); + assert_node_ref_count (ref_model, &level2_1, 1); + assert_node_ref_count (ref_model, &level2_2, 0); + assert_node_ref_count (ref_model, &level3_1, 0); + assert_node_ref_count (ref_model, &level3_2, 0); + assert_node_ref_count (ref_model, &level4_1, 0); + assert_node_ref_count (ref_model, &level4_2, 0); + + path = gtk_tree_path_new_from_indices (1, -1); + gtk_tree_view_expand_row (GTK_TREE_VIEW (tree_view), path, FALSE); + gtk_tree_path_free (path); + + assert_node_ref_count (ref_model, &level1_1, 2); + assert_node_ref_count (ref_model, &level1_2, 2); + assert_node_ref_count (ref_model, &level2_1, 2); + assert_node_ref_count (ref_model, &level2_2, 2); + assert_node_ref_count (ref_model, &level3_1, 1); + assert_node_ref_count (ref_model, &level3_2, 0); + assert_node_ref_count (ref_model, &level4_1, 0); + assert_node_ref_count (ref_model, &level4_2, 0); + + gtk_tree_store_set (GTK_TREE_STORE (model), &level4_1, 0, FALSE, -1); + + assert_node_ref_count (ref_model, &level1_1, 2); + assert_node_ref_count (ref_model, &level1_2, 2); + assert_node_ref_count (ref_model, &level2_1, 2); + assert_node_ref_count (ref_model, &level2_2, 2); + assert_node_ref_count (ref_model, &level3_1, 1); + assert_node_ref_count (ref_model, &level3_2, 0); + assert_node_ref_count (ref_model, &level4_1, 0); + assert_node_ref_count (ref_model, &level4_2, 0); + + gtk_tree_store_set (GTK_TREE_STORE (model), &level3_1, 0, FALSE, -1); + + assert_node_ref_count (ref_model, &level1_1, 2); + assert_node_ref_count (ref_model, &level1_2, 2); + assert_node_ref_count (ref_model, &level2_1, 2); + assert_node_ref_count (ref_model, &level2_2, 2); + assert_node_ref_count (ref_model, &level3_1, 0); + assert_node_ref_count (ref_model, &level3_2, 1); + assert_node_ref_count (ref_model, &level4_1, 0); + assert_node_ref_count (ref_model, &level4_2, 0); + + gtk_tree_store_set (GTK_TREE_STORE (model), &level2_2, 0, FALSE, -1); + + assert_node_ref_count (ref_model, &level1_1, 2); + assert_node_ref_count (ref_model, &level1_2, 2); + assert_node_ref_count (ref_model, &level2_1, 2); + assert_node_ref_count (ref_model, &level2_2, 0); + assert_node_ref_count (ref_model, &level3_1, 0); + assert_node_ref_count (ref_model, &level3_2, 0); + assert_node_ref_count (ref_model, &level4_1, 0); + assert_node_ref_count (ref_model, &level4_2, 0); + + gtk_tree_store_set (GTK_TREE_STORE (model), &level1_2, 0, FALSE, -1); + + assert_node_ref_count (ref_model, &level1_1, 2); + assert_node_ref_count (ref_model, &level1_2, 0); + assert_node_ref_count (ref_model, &level2_1, 0); + assert_node_ref_count (ref_model, &level2_2, 0); + assert_node_ref_count (ref_model, &level3_1, 0); + assert_node_ref_count (ref_model, &level3_2, 0); + assert_node_ref_count (ref_model, &level4_1, 0); + assert_node_ref_count (ref_model, &level4_2, 0); + + gtk_widget_destroy (tree_view); + gtk_tree_model_filter_clear_cache (GTK_TREE_MODEL_FILTER (filter_model)); + + assert_node_ref_count (ref_model, &level1_1, 1); + + g_object_unref (filter_model); + + assert_node_ref_count (ref_model, &level1_1, 0); + + g_object_unref (ref_model); +} + +static void +ref_count_filter_row_length_gt_1_visible_children (void) +{ + GtkTreeIter level1_1, level1_2; + GtkTreeIter level2_1, level2_2; + GtkTreeIter level3_1, level3_2; + GtkTreeIter level4_1, level4_2; + GtkTreeModel *model; + GtkTreeModelRefCount *ref_model; + GtkTreeModel *filter_model; + GtkTreePath *path; + GtkWidget *tree_view; + GType column_types[] = { G_TYPE_BOOLEAN }; + + model = gtk_tree_model_ref_count_new (); + ref_model = GTK_TREE_MODEL_REF_COUNT (model); + + gtk_tree_store_set_column_types (GTK_TREE_STORE (model), 1, + column_types); + + + /* + level1_1 + * + level1_2 + * + level2_1 + * + level2_2 + * + level3_1 + * + level3_2 + * + level4_1 + * + level4_2 + */ + + gtk_tree_store_append (GTK_TREE_STORE (model), &level1_1, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &level1_2, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &level2_1, &level1_2); + gtk_tree_store_append (GTK_TREE_STORE (model), &level2_2, &level1_2); + gtk_tree_store_append (GTK_TREE_STORE (model), &level3_1, &level2_2); + gtk_tree_store_append (GTK_TREE_STORE (model), &level3_2, &level2_2); + gtk_tree_store_append (GTK_TREE_STORE (model), &level4_1, &level3_2); + gtk_tree_store_append (GTK_TREE_STORE (model), &level4_2, &level3_2); + + gtk_tree_store_set (GTK_TREE_STORE (model), &level1_1, 0, TRUE, -1); + gtk_tree_store_set (GTK_TREE_STORE (model), &level1_2, 0, TRUE, -1); + gtk_tree_store_set (GTK_TREE_STORE (model), &level2_1, 0, TRUE, -1); + gtk_tree_store_set (GTK_TREE_STORE (model), &level2_2, 0, TRUE, -1); + gtk_tree_store_set (GTK_TREE_STORE (model), &level3_1, 0, TRUE, -1); + gtk_tree_store_set (GTK_TREE_STORE (model), &level3_2, 0, TRUE, -1); + gtk_tree_store_set (GTK_TREE_STORE (model), &level4_1, 0, TRUE, -1); + gtk_tree_store_set (GTK_TREE_STORE (model), &level4_2, 0, TRUE, -1); + + assert_entire_model_unreferenced (ref_model); + + filter_model = gtk_tree_model_filter_new (model, NULL); + gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (filter_model), 0); + tree_view = gtk_tree_view_new_with_model (filter_model); + + assert_node_ref_count (ref_model, &level1_1, 2); + assert_node_ref_count (ref_model, &level1_2, 2); + assert_node_ref_count (ref_model, &level2_1, 1); + assert_node_ref_count (ref_model, &level2_2, 0); + assert_node_ref_count (ref_model, &level3_1, 0); + assert_node_ref_count (ref_model, &level3_2, 0); + assert_node_ref_count (ref_model, &level4_1, 0); + assert_node_ref_count (ref_model, &level4_2, 0); + + path = gtk_tree_path_new_from_indices (1, -1); + gtk_tree_view_expand_row (GTK_TREE_VIEW (tree_view), path, TRUE); + gtk_tree_path_free (path); + + assert_node_ref_count (ref_model, &level1_1, 2); + assert_node_ref_count (ref_model, &level1_2, 2); + assert_node_ref_count (ref_model, &level2_1, 2); + assert_node_ref_count (ref_model, &level2_2, 2); + assert_node_ref_count (ref_model, &level3_1, 2); + assert_node_ref_count (ref_model, &level3_2, 2); + assert_node_ref_count (ref_model, &level4_1, 2); + assert_node_ref_count (ref_model, &level4_2, 1); + + gtk_tree_store_set (GTK_TREE_STORE (model), &level2_2, 0, FALSE, -1); + + assert_node_ref_count (ref_model, &level1_1, 2); + assert_node_ref_count (ref_model, &level1_2, 2); + assert_node_ref_count (ref_model, &level2_1, 2); + assert_node_ref_count (ref_model, &level2_2, 0); + assert_node_ref_count (ref_model, &level3_1, 0); + assert_node_ref_count (ref_model, &level3_2, 0); + assert_node_ref_count (ref_model, &level4_1, 0); + assert_node_ref_count (ref_model, &level4_2, 0); + + gtk_widget_destroy (tree_view); + gtk_tree_model_filter_clear_cache (GTK_TREE_MODEL_FILTER (filter_model)); + + assert_node_ref_count (ref_model, &level1_1, 1); + assert_node_ref_count (ref_model, &level1_2, 1); + assert_node_ref_count (ref_model, &level2_1, 1); + assert_node_ref_count (ref_model, &level2_2, 0); + assert_node_ref_count (ref_model, &level3_1, 0); + assert_node_ref_count (ref_model, &level3_2, 0); + assert_node_ref_count (ref_model, &level4_1, 0); + assert_node_ref_count (ref_model, &level4_2, 0); + + g_object_unref (filter_model); + + assert_node_ref_count (ref_model, &level1_1, 0); + assert_node_ref_count (ref_model, &level1_2, 0); + assert_node_ref_count (ref_model, &level2_1, 0); + assert_node_ref_count (ref_model, &level2_2, 0); + assert_node_ref_count (ref_model, &level3_1, 0); + assert_node_ref_count (ref_model, &level3_2, 0); + assert_node_ref_count (ref_model, &level4_1, 0); + assert_node_ref_count (ref_model, &level4_2, 0); + + g_object_unref (ref_model); +} + +static void +ref_count_cleanup (void) +{ + GtkTreeIter grandparent1, grandparent2, parent1, parent2; + GtkTreeIter iter_parent1, iter_parent2, iter_parent2_first; + GtkTreeModel *model; + GtkTreeModelRefCount *ref_model; + GtkTreeModel *filter_model; + GtkWidget *tree_view; + + model = gtk_tree_model_ref_count_new (); + ref_model = GTK_TREE_MODEL_REF_COUNT (model); + + /* + grandparent1 + * + grandparent2 + * + parent1 + * + iter_parent1 + * + parent2 + * + iter_parent2_first + * + iter_parent2 + */ + + gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent1, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent2, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &parent1, &grandparent2); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter_parent1, &parent1); + gtk_tree_store_append (GTK_TREE_STORE (model), &parent2, &grandparent2); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter_parent2_first, &parent2); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter_parent2, &parent2); + + filter_model = gtk_tree_model_filter_new (model, NULL); + tree_view = gtk_tree_view_new_with_model (filter_model); + + gtk_tree_view_expand_all (GTK_TREE_VIEW (tree_view)); + + assert_node_ref_count (ref_model, &grandparent1, 2); + assert_node_ref_count (ref_model, &grandparent2, 2); + assert_node_ref_count (ref_model, &parent1, 3); + assert_node_ref_count (ref_model, &parent2, 2); + assert_node_ref_count (ref_model, &iter_parent1, 2); + assert_node_ref_count (ref_model, &iter_parent2_first, 2); + assert_node_ref_count (ref_model, &iter_parent2, 1); + + gtk_widget_destroy (tree_view); + + assert_node_ref_count (ref_model, &grandparent1, 1); + assert_node_ref_count (ref_model, &grandparent2, 1); + assert_node_ref_count (ref_model, &parent1, 2); + assert_node_ref_count (ref_model, &parent2, 1); + assert_node_ref_count (ref_model, &iter_parent1, 1); + assert_node_ref_count (ref_model, &iter_parent2_first, 1); + assert_node_ref_count (ref_model, &iter_parent2, 0); + + gtk_tree_model_filter_clear_cache (GTK_TREE_MODEL_FILTER (filter_model)); + + /* The root level and first level remain cached, only the references on the + * first nodes of these levels are kept. Grandparent2 is the parent + * of the first level with parent1, so grandparent2 keeps a reference + * as well. + */ + assert_node_ref_count (ref_model, &grandparent1, 1); + assert_node_ref_count (ref_model, &grandparent2, 1); + assert_node_ref_count (ref_model, &parent1, 1); + assert_node_ref_count (ref_model, &parent2, 0); + assert_node_ref_count (ref_model, &iter_parent1, 0); + assert_node_ref_count (ref_model, &iter_parent2_first, 0); + assert_node_ref_count (ref_model, &iter_parent2, 0); + + g_object_unref (filter_model); + g_object_unref (ref_model); +} + +static void +ref_count_row_ref (void) +{ + GtkTreeIter grandparent1, grandparent2, parent1, parent2; + GtkTreeIter iter_parent1, iter_parent2, iter_parent2_first; + GtkTreeModel *model; + GtkTreeModelRefCount *ref_model; + GtkTreeModel *filter_model; + GtkWidget *tree_view; + GtkTreePath *path; + GtkTreeRowReference *row_ref; + + model = gtk_tree_model_ref_count_new (); + ref_model = GTK_TREE_MODEL_REF_COUNT (model); + + /* + grandparent1 + * + grandparent2 + * + parent1 + * + iter_parent1 + * + parent2 + * + iter_parent2 + * + iter_parent2 + */ + + gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent1, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent2, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &parent1, &grandparent2); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter_parent1, &parent1); + gtk_tree_store_append (GTK_TREE_STORE (model), &parent2, &grandparent2); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter_parent2_first, &parent2); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter_parent2, &parent2); + + filter_model = gtk_tree_model_filter_new (model, NULL); + tree_view = gtk_tree_view_new_with_model (filter_model); + + path = gtk_tree_path_new_from_indices (1, 1, 1, -1); + row_ref = gtk_tree_row_reference_new (filter_model, path); + gtk_tree_path_free (path); + + assert_node_ref_count (ref_model, &grandparent1, 2); + assert_node_ref_count (ref_model, &grandparent2, 3); + assert_node_ref_count (ref_model, &parent1, 1); + assert_node_ref_count (ref_model, &parent2, 2); + assert_node_ref_count (ref_model, &iter_parent1, 0); + assert_node_ref_count (ref_model, &iter_parent2_first, 1); + assert_node_ref_count (ref_model, &iter_parent2, 1); + + gtk_tree_row_reference_free (row_ref); + + assert_node_ref_count (ref_model, &grandparent1, 2); + assert_node_ref_count (ref_model, &grandparent2, 2); + assert_node_ref_count (ref_model, &parent1, 1); + assert_node_ref_count (ref_model, &parent2, 1); + assert_node_ref_count (ref_model, &iter_parent1, 0); + assert_node_ref_count (ref_model, &iter_parent2_first, 1); + assert_node_ref_count (ref_model, &iter_parent2, 0); + + path = gtk_tree_path_new_from_indices (1, 1, 1, -1); + row_ref = gtk_tree_row_reference_new (filter_model, path); + gtk_tree_path_free (path); + + assert_node_ref_count (ref_model, &grandparent1, 2); + assert_node_ref_count (ref_model, &grandparent2, 3); + assert_node_ref_count (ref_model, &parent1, 1); + assert_node_ref_count (ref_model, &parent2, 2); + assert_node_ref_count (ref_model, &iter_parent1, 0); + assert_node_ref_count (ref_model, &iter_parent2_first, 1); + assert_node_ref_count (ref_model, &iter_parent2, 1); + + gtk_tree_store_remove (GTK_TREE_STORE (model), &parent2); + + assert_node_ref_count (ref_model, &grandparent1, 2); + assert_node_ref_count (ref_model, &grandparent2, 2); + assert_node_ref_count (ref_model, &parent1, 1); + assert_node_ref_count (ref_model, &iter_parent1, 0); + + gtk_tree_row_reference_free (row_ref); + + assert_node_ref_count (ref_model, &grandparent1, 2); + assert_node_ref_count (ref_model, &grandparent2, 2); + assert_node_ref_count (ref_model, &parent1, 1); + assert_node_ref_count (ref_model, &iter_parent1, 0); + + gtk_widget_destroy (tree_view); + + gtk_tree_model_filter_clear_cache (GTK_TREE_MODEL_FILTER (filter_model)); + + /* The root level and first level remain cached, only the references on the + * first nodes of these levels are kept. Grandparent2 is the parent + * of the first level with parent1, so grandparent2 keeps a reference + * as well. + */ + assert_node_ref_count (ref_model, &grandparent1, 1); + assert_node_ref_count (ref_model, &grandparent2, 1); + assert_node_ref_count (ref_model, &parent1, 1); + + g_object_unref (filter_model); + g_object_unref (ref_model); +} + +static void +ref_count_transfer_root_level_insert (void) +{ + GtkTreeIter grandparent1, grandparent2, grandparent3; + GtkTreeIter new_node; + GtkTreeModel *model; + GtkTreeModelRefCount *ref_model; + GtkTreeModel *filter_model; + GtkWidget *tree_view; + + model = gtk_tree_model_ref_count_new (); + ref_model = GTK_TREE_MODEL_REF_COUNT (model); + + /* + grandparent1 + * + grandparent2 + * + grandparent3 + */ + + gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent1, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent2, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent3, NULL); + + filter_model = gtk_tree_model_filter_new (model, NULL); + tree_view = gtk_tree_view_new_with_model (filter_model); + + assert_node_ref_count (ref_model, &grandparent1, 2); + assert_node_ref_count (ref_model, &grandparent2, 1); + assert_node_ref_count (ref_model, &grandparent3, 1); + + gtk_tree_store_prepend (GTK_TREE_STORE (model), &new_node, NULL); + + assert_node_ref_count (ref_model, &new_node, 2); + assert_node_ref_count (ref_model, &grandparent1, 1); + assert_node_ref_count (ref_model, &grandparent2, 1); + assert_node_ref_count (ref_model, &grandparent3, 1); + + gtk_widget_destroy (tree_view); + g_object_unref (filter_model); + g_object_unref (ref_model); +} + +static void +ref_count_transfer_root_level_remove (void) +{ + GtkTreeIter grandparent1, grandparent2, grandparent3; + GtkTreeModel *model; + GtkTreeModelRefCount *ref_model; + GtkTreeModel *filter_model; + GtkWidget *tree_view; + + model = gtk_tree_model_ref_count_new (); + ref_model = GTK_TREE_MODEL_REF_COUNT (model); + + /* + grandparent1 + * + grandparent2 + * + grandparent3 + */ + + gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent1, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent2, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent3, NULL); + + filter_model = gtk_tree_model_filter_new (model, NULL); + tree_view = gtk_tree_view_new_with_model (filter_model); + + assert_node_ref_count (ref_model, &grandparent1, 2); + assert_node_ref_count (ref_model, &grandparent2, 1); + assert_node_ref_count (ref_model, &grandparent3, 1); + + gtk_tree_store_remove (GTK_TREE_STORE (model), &grandparent1); + + assert_node_ref_count (ref_model, &grandparent2, 2); + assert_node_ref_count (ref_model, &grandparent3, 1); + + gtk_tree_store_remove (GTK_TREE_STORE (model), &grandparent2); + + assert_node_ref_count (ref_model, &grandparent3, 2); + + gtk_widget_destroy (tree_view); + g_object_unref (filter_model); + g_object_unref (ref_model); +} + +static void +ref_count_transfer_root_level_remove_filtered (void) +{ + GtkTreeIter grandparent1, grandparent2, grandparent3, grandparent4; + GtkTreeModel *model; + GtkTreeModelRefCount *ref_model; + GtkTreeModel *filter_model; + GtkWidget *tree_view; + GType column_types[] = { G_TYPE_BOOLEAN }; + + model = gtk_tree_model_ref_count_new (); + ref_model = GTK_TREE_MODEL_REF_COUNT (model); + + gtk_tree_store_set_column_types (GTK_TREE_STORE (model), 1, + column_types); + + /* + grandparent1 + * + grandparent2 + * + grandparent3 + * + grandparent4 + */ + + gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent1, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent2, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent3, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent4, NULL); + + /* Filter first node */ + gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent1, 0, FALSE, -1); + gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent2, 0, TRUE, -1); + gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent3, 0, TRUE, -1); + gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent4, 0, TRUE, -1); + + filter_model = gtk_tree_model_filter_new (model, NULL); + gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (filter_model), 0); + tree_view = gtk_tree_view_new_with_model (filter_model); + + assert_node_ref_count (ref_model, &grandparent1, 0); + assert_node_ref_count (ref_model, &grandparent2, 2); + assert_node_ref_count (ref_model, &grandparent3, 1); + assert_node_ref_count (ref_model, &grandparent4, 1); + + gtk_tree_store_remove (GTK_TREE_STORE (model), &grandparent2); + + assert_node_ref_count (ref_model, &grandparent1, 0); + assert_node_ref_count (ref_model, &grandparent3, 2); + assert_node_ref_count (ref_model, &grandparent4, 1); + + gtk_tree_store_remove (GTK_TREE_STORE (model), &grandparent3); + + assert_node_ref_count (ref_model, &grandparent1, 0); + assert_node_ref_count (ref_model, &grandparent4, 2); + + gtk_tree_store_remove (GTK_TREE_STORE (model), &grandparent4); + + /* Check level length to get root level cached again */ + check_level_length (GTK_TREE_MODEL_FILTER (filter_model), NULL, 0); + + assert_node_ref_count (ref_model, &grandparent1, 1); + + gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent2, NULL); + + assert_node_ref_count (ref_model, &grandparent1, 1); + assert_node_ref_count (ref_model, &grandparent2, 0); + + gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent2, 0, TRUE, -1); + + assert_node_ref_count (ref_model, &grandparent1, 1); + assert_node_ref_count (ref_model, &grandparent2, 1); + + check_level_length (GTK_TREE_MODEL_FILTER (filter_model), NULL, 1); + + gtk_widget_destroy (tree_view); + g_object_unref (filter_model); + g_object_unref (ref_model); +} + +static void +ref_count_transfer_root_level_reordered (void) +{ + GtkTreeIter grandparent1, grandparent2, grandparent3; + GtkTreeModel *model; + GtkTreeModelRefCount *ref_model; + GtkTreeModel *filter_model; + GtkWidget *tree_view; + + model = gtk_tree_model_ref_count_new (); + ref_model = GTK_TREE_MODEL_REF_COUNT (model); + + /* + grandparent1 + * + grandparent2 + * + grandparent3 + */ + + gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent1, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent2, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent3, NULL); + + filter_model = gtk_tree_model_filter_new (model, NULL); + tree_view = gtk_tree_view_new_with_model (filter_model); + + assert_node_ref_count (ref_model, &grandparent1, 2); + assert_node_ref_count (ref_model, &grandparent2, 1); + assert_node_ref_count (ref_model, &grandparent3, 1); + + /* gtk_tree_store_move() will emit rows-reordered */ + gtk_tree_store_move_after (GTK_TREE_STORE (model), + &grandparent1, &grandparent3); + + assert_node_ref_count (ref_model, &grandparent2, 2); + assert_node_ref_count (ref_model, &grandparent3, 1); + assert_node_ref_count (ref_model, &grandparent1, 1); + + gtk_widget_destroy (tree_view); + g_object_unref (filter_model); + g_object_unref (ref_model); +} + +static void +ref_count_transfer_root_level_reordered_filtered (void) +{ + GtkTreeIter grandparent1, grandparent2, grandparent3; + GtkTreeModel *model; + GtkTreeModelRefCount *ref_model; + GtkTreeModel *filter_model; + GtkWidget *tree_view; + GType column_types[] = { G_TYPE_BOOLEAN }; + + model = gtk_tree_model_ref_count_new (); + ref_model = GTK_TREE_MODEL_REF_COUNT (model); + + gtk_tree_store_set_column_types (GTK_TREE_STORE (model), 1, + column_types); + + /* + grandparent1 + * + grandparent2 + * + grandparent3 + */ + + gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent1, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent2, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent3, NULL); + + /* Test with 1 node filtered */ + gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent2, 0, TRUE, -1); + gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent3, 0, TRUE, -1); + + filter_model = gtk_tree_model_filter_new (model, NULL); + gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (filter_model), 0); + tree_view = gtk_tree_view_new_with_model (filter_model); + + assert_node_ref_count (ref_model, &grandparent1, 0); + assert_node_ref_count (ref_model, &grandparent2, 2); + assert_node_ref_count (ref_model, &grandparent3, 1); + + /* Move the invisible node grandparent1 */ + + /* gtk_tree_store_move() will emit rows-reordered */ + gtk_tree_store_move_after (GTK_TREE_STORE (model), + &grandparent1, &grandparent3); + + assert_node_ref_count (ref_model, &grandparent2, 2); + assert_node_ref_count (ref_model, &grandparent3, 1); + assert_node_ref_count (ref_model, &grandparent1, 0); + + /* Move the invisible node grandparent1 */ + + /* gtk_tree_store_move() will emit rows-reordered */ + gtk_tree_store_move_before (GTK_TREE_STORE (model), + &grandparent1, &grandparent2); + + assert_node_ref_count (ref_model, &grandparent1, 0); + assert_node_ref_count (ref_model, &grandparent2, 2); + assert_node_ref_count (ref_model, &grandparent3, 1); + + /* Now swap grandparent2 and grandparent3, first reference must transfer */ + /* gtk_tree_store_swap() will emit rows-reordered */ + gtk_tree_store_swap (GTK_TREE_STORE (model), + &grandparent2, &grandparent3); + + assert_node_ref_count (ref_model, &grandparent1, 0); + assert_node_ref_count (ref_model, &grandparent3, 2); + assert_node_ref_count (ref_model, &grandparent2, 1); + + /* Swap back */ + gtk_tree_store_swap (GTK_TREE_STORE (model), + &grandparent2, &grandparent3); + + assert_node_ref_count (ref_model, &grandparent1, 0); + assert_node_ref_count (ref_model, &grandparent2, 2); + assert_node_ref_count (ref_model, &grandparent3, 1); + + gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent1, 0, TRUE, -1); + + assert_node_ref_count (ref_model, &grandparent1, 2); + assert_node_ref_count (ref_model, &grandparent2, 1); + assert_node_ref_count (ref_model, &grandparent3, 1); + + /* Test with two nodes filtered */ + gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent1, 0, FALSE, -1); + gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent2, 0, FALSE, -1); + + assert_node_ref_count (ref_model, &grandparent1, 0); + assert_node_ref_count (ref_model, &grandparent2, 0); + assert_node_ref_count (ref_model, &grandparent3, 2); + + /* gtk_tree_store_move() will emit rows-reordered */ + gtk_tree_store_move_before (GTK_TREE_STORE (model), + &grandparent3, &grandparent1); + + assert_node_ref_count (ref_model, &grandparent3, 2); + assert_node_ref_count (ref_model, &grandparent2, 0); + assert_node_ref_count (ref_model, &grandparent1, 0); + + gtk_widget_destroy (tree_view); + g_object_unref (filter_model); + g_object_unref (ref_model); +} + +static void +ref_count_transfer_root_level_filter (void) +{ + GtkTreeIter grandparent1, grandparent2, grandparent3, grandparent4; + GtkTreeIter new_node; + GtkTreeModel *model; + GtkTreeModelRefCount *ref_model; + GtkTreeModel *filter_model; + GtkWidget *tree_view; + GType column_types[] = { G_TYPE_BOOLEAN }; + + model = gtk_tree_model_ref_count_new (); + ref_model = GTK_TREE_MODEL_REF_COUNT (model); + + gtk_tree_store_set_column_types (GTK_TREE_STORE (model), 1, + column_types); + + /* + grandparent1 + * + grandparent2 + * + grandparent3 + * + grandparent4 + */ + + gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent1, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent2, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent3, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent4, NULL); + + /* Filter first node */ + gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent1, 0, FALSE, -1); + gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent2, 0, TRUE, -1); + gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent3, 0, TRUE, -1); + gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent4, 0, TRUE, -1); + + filter_model = gtk_tree_model_filter_new (model, NULL); + gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (filter_model), 0); + tree_view = gtk_tree_view_new_with_model (filter_model); + + assert_node_ref_count (ref_model, &grandparent1, 0); + assert_node_ref_count (ref_model, &grandparent2, 2); + assert_node_ref_count (ref_model, &grandparent3, 1); + assert_node_ref_count (ref_model, &grandparent4, 1); + + gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent2, 0, FALSE, -1); + + assert_node_ref_count (ref_model, &grandparent1, 0); + assert_node_ref_count (ref_model, &grandparent2, 0); + assert_node_ref_count (ref_model, &grandparent3, 2); + assert_node_ref_count (ref_model, &grandparent4, 1); + + gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent3, 0, FALSE, -1); + + assert_node_ref_count (ref_model, &grandparent1, 0); + assert_node_ref_count (ref_model, &grandparent2, 0); + assert_node_ref_count (ref_model, &grandparent3, 0); + assert_node_ref_count (ref_model, &grandparent4, 2); + + gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent4, 0, FALSE, -1); + + assert_node_ref_count (ref_model, &grandparent1, 0); + assert_node_ref_count (ref_model, &grandparent2, 0); + assert_node_ref_count (ref_model, &grandparent3, 0); + assert_node_ref_count (ref_model, &grandparent4, 1); + + gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent2, 0, TRUE, -1); + + assert_node_ref_count (ref_model, &grandparent1, 0); + assert_node_ref_count (ref_model, &grandparent2, 2); + assert_node_ref_count (ref_model, &grandparent3, 0); + assert_node_ref_count (ref_model, &grandparent4, 0); + + gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent2, 0, FALSE, -1); + + assert_node_ref_count (ref_model, &grandparent1, 0); + assert_node_ref_count (ref_model, &grandparent2, 0); + assert_node_ref_count (ref_model, &grandparent3, 0); + assert_node_ref_count (ref_model, &grandparent4, 1); + + gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent1, 0, TRUE, -1); + + assert_node_ref_count (ref_model, &grandparent1, 2); + assert_node_ref_count (ref_model, &grandparent2, 0); + assert_node_ref_count (ref_model, &grandparent3, 0); + assert_node_ref_count (ref_model, &grandparent4, 0); + + gtk_tree_store_prepend (GTK_TREE_STORE (model), &new_node, NULL); + + assert_node_ref_count (ref_model, &new_node, 0); + assert_node_ref_count (ref_model, &grandparent1, 2); + assert_node_ref_count (ref_model, &grandparent2, 0); + assert_node_ref_count (ref_model, &grandparent3, 0); + assert_node_ref_count (ref_model, &grandparent4, 0); + + gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent1, 0, FALSE, -1); + + assert_node_ref_count (ref_model, &new_node, 0); + assert_node_ref_count (ref_model, &grandparent1, 0); + assert_node_ref_count (ref_model, &grandparent2, 0); + assert_node_ref_count (ref_model, &grandparent3, 0); + assert_node_ref_count (ref_model, &grandparent4, 1); + + gtk_tree_store_remove (GTK_TREE_STORE (model), &new_node); + gtk_tree_store_prepend (GTK_TREE_STORE (model), &new_node, NULL); + + assert_node_ref_count (ref_model, &new_node, 0); + assert_node_ref_count (ref_model, &grandparent1, 0); + assert_node_ref_count (ref_model, &grandparent2, 0); + assert_node_ref_count (ref_model, &grandparent3, 0); + assert_node_ref_count (ref_model, &grandparent4, 1); + + gtk_tree_store_set (GTK_TREE_STORE (model), &new_node, 0, TRUE, -1); + + assert_node_ref_count (ref_model, &new_node, 2); + assert_node_ref_count (ref_model, &grandparent1, 0); + assert_node_ref_count (ref_model, &grandparent2, 0); + assert_node_ref_count (ref_model, &grandparent3, 0); + assert_node_ref_count (ref_model, &grandparent4, 0); + + gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent2, 0, TRUE, -1); + gtk_tree_store_remove (GTK_TREE_STORE (model), &new_node); + + assert_node_ref_count (ref_model, &grandparent1, 0); + assert_node_ref_count (ref_model, &grandparent2, 2); + assert_node_ref_count (ref_model, &grandparent3, 0); + assert_node_ref_count (ref_model, &grandparent4, 0); + + gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent4, 0, TRUE, -1); + gtk_tree_store_remove (GTK_TREE_STORE (model), &grandparent2); + + gtk_widget_destroy (tree_view); + g_object_unref (filter_model); + g_object_unref (ref_model); +} + +static void +ref_count_transfer_child_level_insert (void) +{ + GtkTreeIter grandparent1; + GtkTreeIter parent1, parent2, parent3; + GtkTreeIter new_node; + GtkTreeModel *model; + GtkTreeModelRefCount *ref_model; + GtkTreeModel *filter_model; + GtkWidget *tree_view; + + model = gtk_tree_model_ref_count_new (); + ref_model = GTK_TREE_MODEL_REF_COUNT (model); + + /* + grandparent1 + * + parent1 + * + parent2 + * + parent3 + */ + + gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent1, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &parent1, &grandparent1); + gtk_tree_store_append (GTK_TREE_STORE (model), &parent2, &grandparent1); + gtk_tree_store_append (GTK_TREE_STORE (model), &parent3, &grandparent1); + + filter_model = gtk_tree_model_filter_new (model, NULL); + tree_view = gtk_tree_view_new_with_model (filter_model); + + assert_node_ref_count (ref_model, &grandparent1, 3); + assert_node_ref_count (ref_model, &parent1, 1); + assert_node_ref_count (ref_model, &parent2, 0); + assert_node_ref_count (ref_model, &parent3, 0); + + gtk_tree_store_prepend (GTK_TREE_STORE (model), &new_node, &grandparent1); + + assert_node_ref_count (ref_model, &grandparent1, 3); + assert_node_ref_count (ref_model, &new_node, 1); + assert_node_ref_count (ref_model, &parent1, 0); + assert_node_ref_count (ref_model, &parent2, 0); + assert_node_ref_count (ref_model, &parent3, 0); + + gtk_widget_destroy (tree_view); + g_object_unref (filter_model); + g_object_unref (ref_model); +} + +static void +ref_count_transfer_child_level_remove (void) +{ + GtkTreeIter grandparent1; + GtkTreeIter parent1, parent2, parent3; + GtkTreeModel *model; + GtkTreeModelRefCount *ref_model; + GtkTreeModel *filter_model; + GtkWidget *tree_view; + + model = gtk_tree_model_ref_count_new (); + ref_model = GTK_TREE_MODEL_REF_COUNT (model); + + /* + grandparent1 + * + parent1 + * + parent2 + * + parent3 + */ + + gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent1, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &parent1, &grandparent1); + gtk_tree_store_append (GTK_TREE_STORE (model), &parent2, &grandparent1); + gtk_tree_store_append (GTK_TREE_STORE (model), &parent3, &grandparent1); + + filter_model = gtk_tree_model_filter_new (model, NULL); + tree_view = gtk_tree_view_new_with_model (filter_model); + + assert_node_ref_count (ref_model, &grandparent1, 3); + assert_node_ref_count (ref_model, &parent1, 1); + assert_node_ref_count (ref_model, &parent2, 0); + assert_node_ref_count (ref_model, &parent3, 0); + + gtk_tree_store_remove (GTK_TREE_STORE (model), &parent1); + + assert_node_ref_count (ref_model, &grandparent1, 3); + assert_node_ref_count (ref_model, &parent2, 1); + assert_node_ref_count (ref_model, &parent3, 0); + + gtk_tree_store_remove (GTK_TREE_STORE (model), &parent2); + + assert_node_ref_count (ref_model, &grandparent1, 3); + assert_node_ref_count (ref_model, &parent3, 1); + + gtk_widget_destroy (tree_view); + g_object_unref (filter_model); + g_object_unref (ref_model); +} + +static void +ref_count_transfer_child_level_remove_filtered (void) +{ + GtkTreeIter grandparent1; + GtkTreeIter parent1, parent2, parent3, parent4; + GtkTreeModel *model; + GtkTreeModelRefCount *ref_model; + GtkTreeModel *filter_model; + GtkWidget *tree_view; + GType column_types[] = { G_TYPE_BOOLEAN }; + + model = gtk_tree_model_ref_count_new (); + ref_model = GTK_TREE_MODEL_REF_COUNT (model); + + gtk_tree_store_set_column_types (GTK_TREE_STORE (model), 1, + column_types); + + /* + grandparent1 + * + parent1 + * + parent2 + * + parent3 + * + parent4 + */ + + gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent1, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &parent1, &grandparent1); + gtk_tree_store_append (GTK_TREE_STORE (model), &parent2, &grandparent1); + gtk_tree_store_append (GTK_TREE_STORE (model), &parent3, &grandparent1); + gtk_tree_store_append (GTK_TREE_STORE (model), &parent4, &grandparent1); + + /* Filter first node */ + gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent1, 0, TRUE, -1); + gtk_tree_store_set (GTK_TREE_STORE (model), &parent1, 0, FALSE, -1); + gtk_tree_store_set (GTK_TREE_STORE (model), &parent2, 0, TRUE, -1); + gtk_tree_store_set (GTK_TREE_STORE (model), &parent3, 0, TRUE, -1); + gtk_tree_store_set (GTK_TREE_STORE (model), &parent4, 0, TRUE, -1); + + filter_model = gtk_tree_model_filter_new (model, NULL); + gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (filter_model), 0); + tree_view = gtk_tree_view_new_with_model (filter_model); + + assert_node_ref_count (ref_model, &grandparent1, 3); + assert_node_ref_count (ref_model, &parent1, 0); + assert_node_ref_count (ref_model, &parent2, 1); + assert_node_ref_count (ref_model, &parent3, 0); + assert_node_ref_count (ref_model, &parent4, 0); + + gtk_tree_store_remove (GTK_TREE_STORE (model), &parent2); + + assert_node_ref_count (ref_model, &grandparent1, 3); + assert_node_ref_count (ref_model, &parent1, 0); + assert_node_ref_count (ref_model, &parent3, 1); + assert_node_ref_count (ref_model, &parent4, 0); + + gtk_tree_store_remove (GTK_TREE_STORE (model), &parent3); + + assert_node_ref_count (ref_model, &grandparent1, 3); + assert_node_ref_count (ref_model, &parent1, 0); + assert_node_ref_count (ref_model, &parent4, 1); + + gtk_tree_store_remove (GTK_TREE_STORE (model), &parent4); + + /* Check level length to get level cached again */ + check_level_length (GTK_TREE_MODEL_FILTER (filter_model), "0", 0); + + assert_node_ref_count (ref_model, &grandparent1, 3); + assert_node_ref_count (ref_model, &parent1, 1); + + gtk_tree_store_append (GTK_TREE_STORE (model), &parent2, &grandparent1); + + assert_node_ref_count (ref_model, &grandparent1, 3); + assert_node_ref_count (ref_model, &parent1, 1); + assert_node_ref_count (ref_model, &parent2, 0); + + gtk_tree_store_set (GTK_TREE_STORE (model), &parent2, 0, TRUE, -1); + + assert_node_ref_count (ref_model, &grandparent1, 3); + assert_node_ref_count (ref_model, &parent1, 1); + assert_node_ref_count (ref_model, &parent2, 0); + + check_level_length (GTK_TREE_MODEL_FILTER (filter_model), "0", 1); + + gtk_widget_destroy (tree_view); + g_object_unref (filter_model); + g_object_unref (ref_model); +} + +static void +ref_count_transfer_child_level_reordered (void) +{ + GtkTreeIter grandparent1; + GtkTreeIter parent1, parent2, parent3; + GtkTreeModel *model; + GtkTreeModelRefCount *ref_model; + GtkTreeModel *filter_model; + GtkWidget *tree_view; + + model = gtk_tree_model_ref_count_new (); + ref_model = GTK_TREE_MODEL_REF_COUNT (model); + + /* + grandparent1 + * + parent1 + * + parent2 + * + parent3 + */ + + gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent1, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &parent1, &grandparent1); + gtk_tree_store_append (GTK_TREE_STORE (model), &parent2, &grandparent1); + gtk_tree_store_append (GTK_TREE_STORE (model), &parent3, &grandparent1); + + filter_model = gtk_tree_model_filter_new (model, NULL); + tree_view = gtk_tree_view_new_with_model (filter_model); + + assert_node_ref_count (ref_model, &grandparent1, 3); + assert_node_ref_count (ref_model, &parent1, 1); + assert_node_ref_count (ref_model, &parent2, 0); + assert_node_ref_count (ref_model, &parent3, 0); + + /* gtk_tree_store_move() will emit rows-reordered */ + gtk_tree_store_move_after (GTK_TREE_STORE (model), + &parent1, &parent3); + + assert_node_ref_count (ref_model, &grandparent1, 3); + assert_node_ref_count (ref_model, &parent2, 1); + assert_node_ref_count (ref_model, &parent3, 0); + assert_node_ref_count (ref_model, &parent1, 0); + + gtk_widget_destroy (tree_view); + g_object_unref (filter_model); + g_object_unref (ref_model); +} + +static void +ref_count_transfer_child_level_reordered_filtered (void) +{ + GtkTreeIter grandparent1; + GtkTreeIter parent1, parent2, parent3; + GtkTreeModel *model; + GtkTreeModelRefCount *ref_model; + GtkTreeModel *filter_model; + GtkWidget *tree_view; + GType column_types[] = { G_TYPE_BOOLEAN }; + + model = gtk_tree_model_ref_count_new (); + ref_model = GTK_TREE_MODEL_REF_COUNT (model); + + gtk_tree_store_set_column_types (GTK_TREE_STORE (model), 1, + column_types); + + /* + grandparent1 + * + parent1 + * + parent2 + * + parent3 + */ + + gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent1, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &parent1, &grandparent1); + gtk_tree_store_append (GTK_TREE_STORE (model), &parent2, &grandparent1); + gtk_tree_store_append (GTK_TREE_STORE (model), &parent3, &grandparent1); + + /* Test with 1 node filtered (parent1) */ + gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent1, 0, TRUE, -1); + gtk_tree_store_set (GTK_TREE_STORE (model), &parent2, 0, TRUE, -1); + gtk_tree_store_set (GTK_TREE_STORE (model), &parent3, 0, TRUE, -1); + + filter_model = gtk_tree_model_filter_new (model, NULL); + gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (filter_model), 0); + tree_view = gtk_tree_view_new_with_model (filter_model); + + assert_node_ref_count (ref_model, &grandparent1, 3); + assert_node_ref_count (ref_model, &parent1, 0); + assert_node_ref_count (ref_model, &parent2, 1); + assert_node_ref_count (ref_model, &parent3, 0); + + /* Move invisible node parent 1 */ + + /* gtk_tree_store_move() will emit rows-reordered */ + gtk_tree_store_move_after (GTK_TREE_STORE (model), + &parent1, &parent3); + + assert_node_ref_count (ref_model, &grandparent1, 3); + assert_node_ref_count (ref_model, &parent2, 1); + assert_node_ref_count (ref_model, &parent3, 0); + assert_node_ref_count (ref_model, &parent1, 0); + + /* Move invisible node parent 1 */ + + /* gtk_tree_store_move() will emit rows-reordered */ + gtk_tree_store_move_before (GTK_TREE_STORE (model), + &parent1, &parent2); + + assert_node_ref_count (ref_model, &grandparent1, 3); + assert_node_ref_count (ref_model, &parent1, 0); + assert_node_ref_count (ref_model, &parent2, 1); + assert_node_ref_count (ref_model, &parent3, 0); + + /* Now swap parent2 and parent2, first reference must transfer */ + /* gtk_tree_store_swap() will emit rows-reordered */ + gtk_tree_store_swap (GTK_TREE_STORE (model), + &parent2, &parent3); + + assert_node_ref_count (ref_model, &grandparent1, 3); + assert_node_ref_count (ref_model, &parent1, 0); + assert_node_ref_count (ref_model, &parent3, 1); + assert_node_ref_count (ref_model, &parent2, 0); + + /* Swap back */ + gtk_tree_store_swap (GTK_TREE_STORE (model), + &parent2, &parent3); + + assert_node_ref_count (ref_model, &grandparent1, 3); + assert_node_ref_count (ref_model, &parent1, 0); + assert_node_ref_count (ref_model, &parent2, 1); + assert_node_ref_count (ref_model, &parent3, 0); + + gtk_tree_store_set (GTK_TREE_STORE (model), &parent1, 0, TRUE, -1); + + assert_node_ref_count (ref_model, &parent1, 1); + assert_node_ref_count (ref_model, &parent2, 0); + assert_node_ref_count (ref_model, &parent3, 0); + + /* Test with two nodes filtered */ + gtk_tree_store_set (GTK_TREE_STORE (model), &parent1, 0, FALSE, -1); + gtk_tree_store_set (GTK_TREE_STORE (model), &parent2, 0, FALSE, -1); + + assert_node_ref_count (ref_model, &parent1, 0); + assert_node_ref_count (ref_model, &parent2, 0); + assert_node_ref_count (ref_model, &parent3, 1); + + /* gtk_tree_store_move() will emit rows-reordered */ + gtk_tree_store_move_before (GTK_TREE_STORE (model), + &parent3, &parent1); + + assert_node_ref_count (ref_model, &parent3, 1); + assert_node_ref_count (ref_model, &parent2, 0); + assert_node_ref_count (ref_model, &parent1, 0); + + gtk_widget_destroy (tree_view); + g_object_unref (filter_model); + g_object_unref (ref_model); +} + +static void +ref_count_transfer_child_level_filter (void) +{ + GtkTreeIter root; + GtkTreeIter grandparent1, grandparent2, grandparent3, grandparent4; + GtkTreeIter new_node; + GtkTreeModel *model; + GtkTreeModelRefCount *ref_model; + GtkTreeModel *filter_model; + GtkWidget *tree_view; + GType column_types[] = { G_TYPE_BOOLEAN }; + + model = gtk_tree_model_ref_count_new (); + ref_model = GTK_TREE_MODEL_REF_COUNT (model); + + gtk_tree_store_set_column_types (GTK_TREE_STORE (model), 1, + column_types); + + /* + root + * + grandparent1 + * + grandparent2 + * + grandparent3 + * + grandparent4 + */ + + gtk_tree_store_append (GTK_TREE_STORE (model), &root, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent1, &root); + gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent2, &root); + gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent3, &root); + gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent4, &root); + + /* Filter first node */ + gtk_tree_store_set (GTK_TREE_STORE (model), &root, 0, TRUE, -1); + gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent1, 0, FALSE, -1); + gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent2, 0, TRUE, -1); + gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent3, 0, TRUE, -1); + gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent4, 0, TRUE, -1); + + filter_model = gtk_tree_model_filter_new (model, NULL); + gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (filter_model), 0); + tree_view = gtk_tree_view_new_with_model (filter_model); + + assert_node_ref_count (ref_model, &grandparent1, 0); + assert_node_ref_count (ref_model, &grandparent2, 1); + assert_node_ref_count (ref_model, &grandparent3, 0); + assert_node_ref_count (ref_model, &grandparent4, 0); + + gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent2, 0, FALSE, -1); + + assert_node_ref_count (ref_model, &grandparent1, 0); + assert_node_ref_count (ref_model, &grandparent2, 0); + assert_node_ref_count (ref_model, &grandparent3, 1); + assert_node_ref_count (ref_model, &grandparent4, 0); + + gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent3, 0, FALSE, -1); + + assert_node_ref_count (ref_model, &grandparent1, 0); + assert_node_ref_count (ref_model, &grandparent2, 0); + assert_node_ref_count (ref_model, &grandparent3, 0); + assert_node_ref_count (ref_model, &grandparent4, 1); + + gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent4, 0, FALSE, -1); + + assert_node_ref_count (ref_model, &grandparent1, 0); + assert_node_ref_count (ref_model, &grandparent2, 0); + assert_node_ref_count (ref_model, &grandparent3, 0); + assert_node_ref_count (ref_model, &grandparent4, 1); + + gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent2, 0, TRUE, -1); + + assert_node_ref_count (ref_model, &grandparent1, 0); + assert_node_ref_count (ref_model, &grandparent2, 1); + assert_node_ref_count (ref_model, &grandparent3, 0); + assert_node_ref_count (ref_model, &grandparent4, 0); + + gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent2, 0, FALSE, -1); + + assert_node_ref_count (ref_model, &grandparent1, 0); + assert_node_ref_count (ref_model, &grandparent2, 0); + assert_node_ref_count (ref_model, &grandparent3, 0); + assert_node_ref_count (ref_model, &grandparent4, 1); + + gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent1, 0, TRUE, -1); + + assert_node_ref_count (ref_model, &grandparent1, 1); + assert_node_ref_count (ref_model, &grandparent2, 0); + assert_node_ref_count (ref_model, &grandparent3, 0); + assert_node_ref_count (ref_model, &grandparent4, 0); + + gtk_tree_store_prepend (GTK_TREE_STORE (model), &new_node, &root); + + assert_node_ref_count (ref_model, &new_node, 0); + assert_node_ref_count (ref_model, &grandparent1, 1); + assert_node_ref_count (ref_model, &grandparent2, 0); + assert_node_ref_count (ref_model, &grandparent3, 0); + assert_node_ref_count (ref_model, &grandparent4, 0); + + gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent1, 0, FALSE, -1); + + assert_node_ref_count (ref_model, &new_node, 0); + assert_node_ref_count (ref_model, &grandparent1, 0); + assert_node_ref_count (ref_model, &grandparent2, 0); + assert_node_ref_count (ref_model, &grandparent3, 0); + assert_node_ref_count (ref_model, &grandparent4, 1); + + gtk_tree_store_remove (GTK_TREE_STORE (model), &new_node); + gtk_tree_store_prepend (GTK_TREE_STORE (model), &new_node, &root); + + assert_node_ref_count (ref_model, &new_node, 0); + assert_node_ref_count (ref_model, &grandparent1, 0); + assert_node_ref_count (ref_model, &grandparent2, 0); + assert_node_ref_count (ref_model, &grandparent3, 0); + assert_node_ref_count (ref_model, &grandparent4, 1); + + gtk_tree_store_set (GTK_TREE_STORE (model), &new_node, 0, TRUE, -1); + + assert_node_ref_count (ref_model, &new_node, 1); + assert_node_ref_count (ref_model, &grandparent1, 0); + assert_node_ref_count (ref_model, &grandparent2, 0); + assert_node_ref_count (ref_model, &grandparent3, 0); + assert_node_ref_count (ref_model, &grandparent4, 0); + + gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent2, 0, TRUE, -1); + gtk_tree_store_remove (GTK_TREE_STORE (model), &new_node); + + assert_node_ref_count (ref_model, &grandparent1, 0); + assert_node_ref_count (ref_model, &grandparent2, 1); + assert_node_ref_count (ref_model, &grandparent3, 0); + assert_node_ref_count (ref_model, &grandparent4, 0); + + gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent4, 0, TRUE, -1); + gtk_tree_store_remove (GTK_TREE_STORE (model), &grandparent2); + + gtk_widget_destroy (tree_view); + g_object_unref (filter_model); + g_object_unref (ref_model); +} + + +static gboolean +specific_path_dependent_filter_func (GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + GtkTreePath *path; + + path = gtk_tree_model_get_path (model, iter); + if (gtk_tree_path_get_indices (path)[0] < 4) + return FALSE; + + return TRUE; +} + +static void +specific_path_dependent_filter (void) +{ + int i; + GtkTreeIter iter; + GtkListStore *list; + GtkTreeModel *sort; + GtkTreeModel *filter; + + list = gtk_list_store_new (1, G_TYPE_INT); + gtk_list_store_insert_with_values (list, &iter, 0, 0, 1, -1); + gtk_list_store_insert_with_values (list, &iter, 1, 0, 2, -1); + gtk_list_store_insert_with_values (list, &iter, 2, 0, 3, -1); + gtk_list_store_insert_with_values (list, &iter, 3, 0, 4, -1); + gtk_list_store_insert_with_values (list, &iter, 4, 0, 5, -1); + gtk_list_store_insert_with_values (list, &iter, 5, 0, 6, -1); + gtk_list_store_insert_with_values (list, &iter, 6, 0, 7, -1); + gtk_list_store_insert_with_values (list, &iter, 7, 0, 8, -1); + + sort = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (list)); + filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (sort), NULL); + gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter), + specific_path_dependent_filter_func, + NULL, NULL); + + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sort), 0, + GTK_SORT_DESCENDING); + + for (i = 0; i < 4; i++) + { + if (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (list), &iter, + NULL, 1)) + gtk_list_store_remove (list, &iter); + + if (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (list), &iter, + NULL, 2)) + gtk_list_store_remove (list, &iter); + } + + g_object_unref (filter); + g_object_unref (sort); + g_object_unref (list); +} + + +static gboolean +specific_append_after_collapse_visible_func (GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + gint number; + gboolean hide_negative_numbers; + + gtk_tree_model_get (model, iter, 1, &number, -1); + hide_negative_numbers = GPOINTER_TO_INT (g_object_get_data (data, "private-hide-negative-numbers")); + + return (number >= 0 || !hide_negative_numbers); +} + +static void +specific_append_after_collapse (void) +{ + /* This test is based on one of the test cases I found in my + * old test cases directory. I unfortunately do not have a record + * from who this test case originated. -Kris. + * + * General idea: + * - Construct tree. + * - Show tree, expand, collapse. + * - Add a row. + */ + + GtkTreeIter iter; + GtkTreeIter child_iter; + GtkTreeIter child_iter2; + GtkTreePath *append_path; + GtkTreeStore *store; + GtkTreeModel *filter; + GtkTreeModel *sort; + + GtkWidget *window; + GtkWidget *tree_view; + + store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_INT); + + filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (store), NULL); + g_object_set_data (G_OBJECT (filter), "private-hide-negative-numbers", + GINT_TO_POINTER (FALSE)); + gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter), + specific_append_after_collapse_visible_func, + filter, NULL); + + sort = gtk_tree_model_sort_new_with_model (filter); + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + tree_view = gtk_tree_view_new_with_model (sort); + gtk_container_add (GTK_CONTAINER (window), tree_view); + gtk_widget_realize (tree_view); + + while (gtk_events_pending ()) + gtk_main_iteration (); + + gtk_tree_store_prepend (store, &iter, NULL); + gtk_tree_store_set (store, &iter, + 0, "hallo", 1, 1, -1); + + gtk_tree_store_append (store, &child_iter, &iter); + gtk_tree_store_set (store, &child_iter, + 0, "toemaar", 1, 1, -1); + + gtk_tree_store_append (store, &child_iter2, &child_iter); + gtk_tree_store_set (store, &child_iter2, + 0, "very deep", 1, 1, -1); + + append_path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &child_iter2); + + gtk_tree_store_append (store, &child_iter, &iter); + gtk_tree_store_set (store, &child_iter, + 0, "sja", 1, 1, -1); + + gtk_tree_store_append (store, &child_iter, &iter); + gtk_tree_store_set (store, &child_iter, + 0, "some word", 1, -1, -1); + + /* Expand and collapse the tree */ + gtk_tree_view_expand_all (GTK_TREE_VIEW (tree_view)); + while (gtk_events_pending ()) + gtk_main_iteration (); + + gtk_tree_view_collapse_all (GTK_TREE_VIEW (tree_view)); + while (gtk_events_pending ()) + gtk_main_iteration (); + + /* Add another it */ + g_object_set_data (G_OBJECT (filter), "private-hide-negative-numbers", + GINT_TO_POINTER (TRUE)); + + if (gtk_tree_model_get_iter (GTK_TREE_MODEL (store), &iter, append_path)) + { + gtk_tree_store_append (store, &child_iter, &iter); + gtk_tree_store_set (store, &child_iter, + 0, "new new new !!", 1, 1, -1); + } + gtk_tree_path_free (append_path); + + /* Expand */ + gtk_tree_view_expand_all (GTK_TREE_VIEW (tree_view)); + while (gtk_events_pending ()) + gtk_main_iteration (); +} + + +static gint +specific_sort_filter_remove_node_compare_func (GtkTreeModel *model, + GtkTreeIter *iter1, + GtkTreeIter *iter2, + gpointer data) +{ + return -1; +} + +static gboolean +specific_sort_filter_remove_node_visible_func (GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + char *item = NULL; + + /* Do reference the model */ + gtk_tree_model_get (model, iter, 0, &item, -1); + g_free (item); + + return FALSE; +} + +static void +specific_sort_filter_remove_node (void) +{ + /* This test is based on one of the test cases I found in my + * old test cases directory. I unfortunately do not have a record + * from who this test case originated. -Kris. + * + * General idea: + * - Create tree store, sort, filter models. The sort model has + * a default sort func that is enabled, filter model a visible func + * that defaults to returning FALSE. + * - Remove a node from the tree store. + */ + + GtkTreeIter iter; + GtkTreeStore *store; + GtkTreeModel *filter; + GtkTreeModel *sort; + + GtkWidget *window; + GtkWidget *tree_view; + + store = gtk_tree_store_new (1, G_TYPE_STRING); + gtk_tree_store_append (store, &iter, NULL); + gtk_tree_store_set (store, &iter, 0, "Hello1", -1); + + gtk_tree_store_append (store, &iter, NULL); + gtk_tree_store_set (store, &iter, 0, "Hello2", -1); + + sort = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (store)); + gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (sort), + specific_sort_filter_remove_node_compare_func, NULL, NULL); + + filter = gtk_tree_model_filter_new (sort, NULL); + gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter), + specific_sort_filter_remove_node_visible_func, + filter, NULL); + + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + tree_view = gtk_tree_view_new_with_model (filter); + gtk_container_add (GTK_CONTAINER (window), tree_view); + gtk_widget_realize (tree_view); + + while (gtk_events_pending ()) + gtk_main_iteration (); + + /* Remove a node */ + gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter); + gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter); + gtk_tree_store_remove (store, &iter); + + while (gtk_events_pending ()) + gtk_main_iteration (); +} + + +static void +specific_sort_filter_remove_root (void) +{ + /* This test is based on one of the test cases I found in my + * old test cases directory. I unfortunately do not have a record + * from who this test case originated. -Kris. + */ + + GtkTreeModel *model, *sort, *filter; + GtkTreeIter root, mid, leaf; + GtkTreePath *path; + + model = GTK_TREE_MODEL (gtk_tree_store_new (1, G_TYPE_INT)); + gtk_tree_store_append (GTK_TREE_STORE (model), &root, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &mid, &root); + gtk_tree_store_append (GTK_TREE_STORE (model), &leaf, &mid); + + path = gtk_tree_model_get_path (model, &mid); + + sort = gtk_tree_model_sort_new_with_model (model); + filter = gtk_tree_model_filter_new (sort, path); + + gtk_tree_path_free (path); + + gtk_tree_store_remove (GTK_TREE_STORE (model), &root); + + g_object_unref (filter); + g_object_unref (sort); + g_object_unref (model); +} + + +static void +specific_root_mixed_visibility (void) +{ + int i; + GtkTreeModel *filter; + /* A bit nasty, apologies */ + FilterTest fixture; + + fixture.store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN); + + for (i = 0; i < LEVEL_LENGTH; i++) + { + GtkTreeIter iter; + + gtk_tree_store_insert (fixture.store, &iter, NULL, i); + if (i % 2 == 0) + create_tree_store_set_values (fixture.store, &iter, TRUE); + else + create_tree_store_set_values (fixture.store, &iter, FALSE); + } + + filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (fixture.store), NULL); + fixture.filter = GTK_TREE_MODEL_FILTER (filter); + fixture.monitor = NULL; + + gtk_tree_model_filter_set_visible_column (fixture.filter, 1); + + /* In order to trigger the potential bug, we should not access + * the filter model here (so don't call the check functions). + */ + + /* Change visibility of an odd row to TRUE */ + set_path_visibility (&fixture, "3", TRUE); + check_filter_model (&fixture); + check_level_length (fixture.filter, NULL, 4); +} + + + +static gboolean +specific_has_child_filter_filter_func (GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + return gtk_tree_model_iter_has_child (model, iter); +} + +static void +specific_has_child_filter (void) +{ + GtkTreeModel *filter; + GtkTreeIter iter, root; + FilterTest fixture; /* This is not how it should be done */ + GtkWidget *tree_view; + + fixture.store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN); + filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (fixture.store), NULL); + fixture.filter = GTK_TREE_MODEL_FILTER (filter); + fixture.monitor = signal_monitor_new (filter); + + tree_view = gtk_tree_view_new_with_model (filter); + + /* We will filter on parent state using a filter function. We will + * manually keep the boolean column in sync, so that we can use + * check_filter_model() to check the consistency of the model. + */ + /* FIXME: We need a check_filter_model() that is not tied to LEVEL_LENGTH + * to be able to check the structure here. We keep the calls to + * check_filter_model() commented out until then. + */ + gtk_tree_model_filter_set_visible_func (fixture.filter, + specific_has_child_filter_filter_func, + NULL, NULL); + + /* The first node will be initially invisible: no signals */ + gtk_tree_store_append (fixture.store, &root, NULL); + create_tree_store_set_values (fixture.store, &root, FALSE); + + /* check_filter_model (&fixture); */ + check_level_length (fixture.filter, NULL, 0); + signal_monitor_assert_is_empty (fixture.monitor); + + /* Insert a child node. This will cause the parent to become visible + * since there is a child now. + */ + signal_monitor_append_signal (fixture.monitor, ROW_INSERTED, "0"); + signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "0"); + signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "0"); + + gtk_tree_store_append (fixture.store, &iter, &root); + create_tree_store_set_values (fixture.store, &iter, TRUE); + + /* Parent must now be visible. Do the level length check first, + * to avoid modifying the child model triggering a row-changed to + * the filter model. + */ + check_level_length (fixture.filter, NULL, 1); + check_level_length (fixture.filter, "0", 0); + signal_monitor_assert_is_empty (fixture.monitor); + + /* This should propagate row-changed */ + signal_monitor_append_signal (fixture.monitor, ROW_CHANGED, "0"); + signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "0"); + + set_path_visibility (&fixture, "0", TRUE); + /* check_filter_model (&fixture); */ + signal_monitor_assert_is_empty (fixture.monitor); + + /* New root node, no child, so no signal */ + gtk_tree_store_append (fixture.store, &root, NULL); + check_level_length (fixture.filter, NULL, 1); + signal_monitor_assert_is_empty (fixture.monitor); + + /* When the child comes in, this node will become visible */ + signal_monitor_append_signal (fixture.monitor, ROW_INSERTED, "1"); + signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "1"); + signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "1"); + signal_monitor_append_signal (fixture.monitor, ROW_CHANGED, "1"); + signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "1"); + + gtk_tree_store_append (fixture.store, &iter, &root); + check_level_length (fixture.filter, NULL, 2); + check_level_length (fixture.filter, "1", 0); + + create_tree_store_set_values (fixture.store, &root, TRUE); + create_tree_store_set_values (fixture.store, &iter, TRUE); + + /* check_filter_model (&fixture); */ + signal_monitor_assert_is_empty (fixture.monitor); + + /* Add another child for 1 */ + gtk_tree_store_append (fixture.store, &iter, &root); + create_tree_store_set_values (fixture.store, &iter, TRUE); + check_level_length (fixture.filter, NULL, 2); + check_level_length (fixture.filter, "0", 0); + check_level_length (fixture.filter, "1", 0); + signal_monitor_assert_is_empty (fixture.monitor); + + /* Now remove one of the remaining child rows */ + signal_monitor_append_signal (fixture.monitor, ROW_DELETED, "0"); + + gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture.store), + &iter, "0:0"); + gtk_tree_store_remove (fixture.store, &iter); + + check_level_length (fixture.filter, NULL, 1); + check_level_length (fixture.filter, "0", 0); + + set_path_visibility (&fixture, "0", FALSE); + /* check_filter_model (&fixture); */ + signal_monitor_assert_is_empty (fixture.monitor); + + g_object_unref (fixture.filter); + g_object_unref (fixture.store); + gtk_widget_destroy (tree_view); +} + + +static gboolean +specific_root_has_child_filter_filter_func (GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + int depth; + GtkTreePath *path; + + path = gtk_tree_model_get_path (model, iter); + depth = gtk_tree_path_get_depth (path); + gtk_tree_path_free (path); + + if (depth > 1) + return TRUE; + /* else */ + return gtk_tree_model_iter_has_child (model, iter); +} + +static void +specific_root_has_child_filter (void) +{ + GtkTreeModel *filter; + GtkTreeIter iter, root; + FilterTest fixture; /* This is not how it should be done ... */ + GtkWidget *tree_view; + + /* This is a variation on the above test case, specific has-child-filter, + * herein the has-child check for visibility only applies to root level + * nodes. In this test, children are always visible because we + * only filter based on the "has child" criterion. + */ + + fixture.store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN); + filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (fixture.store), NULL); + fixture.filter = GTK_TREE_MODEL_FILTER (filter); + fixture.monitor = signal_monitor_new (filter); + + tree_view = gtk_tree_view_new_with_model (filter); + + /* We will filter on parent state using a filter function. We will + * manually keep the boolean column in sync, so that we can use + * check_filter_model() to check the consistency of the model. + */ + /* FIXME: We need a check_filter_model() that is not tied to LEVEL_LENGTH + * to be able to check the structure here. We keep the calls to + * check_filter_model() commented out until then. + */ + gtk_tree_model_filter_set_visible_func (fixture.filter, + specific_root_has_child_filter_filter_func, + NULL, NULL); + + /* Add a first node, this will be invisible initially, so no signal + * should be emitted. + */ + gtk_tree_store_append (fixture.store, &root, NULL); + create_tree_store_set_values (fixture.store, &root, FALSE); + + signal_monitor_assert_is_empty (fixture.monitor); + /* check_filter_model (&fixture); */ + check_level_length (fixture.filter, NULL, 0); + + /* Add a child node. This will cause the parent to become visible, + * so we expect row-inserted signals for both. + */ + signal_monitor_append_signal (fixture.monitor, ROW_INSERTED, "0"); + signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "0"); + signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "0"); + + gtk_tree_store_append (fixture.store, &iter, &root); + signal_monitor_assert_is_empty (fixture.monitor); + + check_level_length (fixture.filter, NULL, 1); + check_level_length (fixture.filter, "0", 1); + + /* Modify the content of iter, no signals because the parent is not + * expanded. + */ + create_tree_store_set_values (fixture.store, &iter, TRUE); + signal_monitor_assert_is_empty (fixture.monitor); + + /* Parent must now be visible. Do the level length check first, + * to avoid modifying the child model triggering a row-changed to + * the filter model. + */ + check_level_length (fixture.filter, NULL, 1); + check_level_length (fixture.filter, "0", 1); + + /* Modify path 0 */ + signal_monitor_append_signal (fixture.monitor, ROW_CHANGED, "0"); + signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "0"); + + set_path_visibility (&fixture, "0", TRUE); + /* check_filter_model (&fixture); */ + + signal_monitor_assert_is_empty (fixture.monitor); + + /* Insert another node in the root level. Initially invisible, so + * not expecting any signal. + */ + gtk_tree_store_append (fixture.store, &root, NULL); + check_level_length (fixture.filter, NULL, 1); + + signal_monitor_assert_is_empty (fixture.monitor); + + /* Adding a child node which also makes parent at path 1 visible. */ + signal_monitor_append_signal (fixture.monitor, ROW_INSERTED, "1"); + signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "1"); + signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "1"); + + gtk_tree_store_append (fixture.store, &iter, &root); + check_level_length (fixture.filter, NULL, 2); + check_level_length (fixture.filter, "1", 1); + + signal_monitor_assert_is_empty (fixture.monitor); + + /* Check if row-changed is propagated */ + signal_monitor_append_signal (fixture.monitor, ROW_CHANGED, "1"); + signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "1"); + + create_tree_store_set_values (fixture.store, &root, TRUE); + create_tree_store_set_values (fixture.store, &iter, TRUE); + /* check_filter_model (&fixture); */ + signal_monitor_assert_is_empty (fixture.monitor); + + /* Insert another child under node 1 */ + gtk_tree_store_append (fixture.store, &iter, &root); + create_tree_store_set_values (fixture.store, &iter, TRUE); + check_level_length (fixture.filter, NULL, 2); + check_level_length (fixture.filter, "0", 1); + check_level_length (fixture.filter, "1", 2); + signal_monitor_assert_is_empty (fixture.monitor); + + /* Set a child node to invisible. This should not yield any + * change, because filtering is only done on whether the root + * node has a child, which it still has. + */ + set_path_visibility (&fixture, "0:0", FALSE); + signal_monitor_assert_is_empty (fixture.monitor); + + /* Now remove one of the remaining child rows */ + signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "0"); + signal_monitor_append_signal (fixture.monitor, ROW_DELETED, "0"); + + gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture.store), + &iter, "0:0"); + gtk_tree_store_remove (fixture.store, &iter); + + check_level_length (fixture.filter, NULL, 1); + check_level_length (fixture.filter, "0", 2); + signal_monitor_assert_is_empty (fixture.monitor); + + /* Set visibility of 0 to FALSE, no-op for filter model since + * the child 0:0 is already gone + */ + set_path_visibility (&fixture, "0", FALSE); + /* check_filter_model (&fixture); */ + signal_monitor_assert_is_empty (fixture.monitor); + + g_object_unref (fixture.filter); + g_object_unref (fixture.store); + gtk_widget_destroy (tree_view); +} + +static void +specific_has_child_filter_on_sort_model (void) +{ + GtkTreeModel *filter; + GtkTreeModel *sort_model; + GtkTreeIter iter, root; + FilterTest fixture; /* This is not how it should be done */ + GtkWidget *tree_view; + + fixture.store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN); + sort_model = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (fixture.store)); + filter = gtk_tree_model_filter_new (sort_model, NULL); + fixture.filter = GTK_TREE_MODEL_FILTER (filter); + fixture.monitor = signal_monitor_new (filter); + + tree_view = gtk_tree_view_new_with_model (filter); + + /* We will filter on parent state using a filter function. We will + * manually keep the boolean column in sync, so that we can use + * check_filter_model() to check the consistency of the model. + */ + /* FIXME: We need a check_filter_model() that is not tied to LEVEL_LENGTH + * to be able to check the structure here. We keep the calls to + * check_filter_model() commented out until then. + */ + gtk_tree_model_filter_set_visible_func (fixture.filter, + specific_has_child_filter_filter_func, + NULL, NULL); + + /* The first node will be initially invisible: no signals */ + gtk_tree_store_append (fixture.store, &root, NULL); + create_tree_store_set_values (fixture.store, &root, FALSE); + + /* check_filter_model (&fixture); */ + check_level_length (fixture.filter, NULL, 0); + signal_monitor_assert_is_empty (fixture.monitor); + + /* Insert a child node. This will cause the parent to become visible + * since there is a child now. + */ + signal_monitor_append_signal (fixture.monitor, ROW_INSERTED, "0"); + signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "0"); + + gtk_tree_store_append (fixture.store, &iter, &root); + create_tree_store_set_values (fixture.store, &iter, TRUE); + + /* Parent must now be visible. Do the level length check first, + * to avoid modifying the child model triggering a row-changed to + * the filter model. + */ + check_level_length (fixture.filter, NULL, 1); + check_level_length (fixture.filter, "0", 0); + signal_monitor_assert_is_empty (fixture.monitor); + + /* This should propagate row-changed */ + signal_monitor_append_signal (fixture.monitor, ROW_CHANGED, "0"); + signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "0"); + + set_path_visibility (&fixture, "0", TRUE); + /* check_filter_model (&fixture); */ + signal_monitor_assert_is_empty (fixture.monitor); + + /* New root node, no child, so no signal */ + gtk_tree_store_append (fixture.store, &root, NULL); + check_level_length (fixture.filter, NULL, 1); + signal_monitor_assert_is_empty (fixture.monitor); + + /* When the child comes in, this node will become visible */ + signal_monitor_append_signal (fixture.monitor, ROW_INSERTED, "1"); + signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "1"); + signal_monitor_append_signal (fixture.monitor, ROW_CHANGED, "1"); + signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "1"); + + gtk_tree_store_append (fixture.store, &iter, &root); + check_level_length (fixture.filter, NULL, 2); + check_level_length (fixture.filter, "1", 0); + + create_tree_store_set_values (fixture.store, &root, TRUE); + create_tree_store_set_values (fixture.store, &iter, TRUE); + + /* check_filter_model (&fixture); */ + signal_monitor_assert_is_empty (fixture.monitor); + + /* Add another child for 1 */ + gtk_tree_store_append (fixture.store, &iter, &root); + create_tree_store_set_values (fixture.store, &iter, TRUE); + check_level_length (fixture.filter, NULL, 2); + check_level_length (fixture.filter, "0", 0); + check_level_length (fixture.filter, "1", 0); + signal_monitor_assert_is_empty (fixture.monitor); + + /* Now remove one of the remaining child rows */ + signal_monitor_append_signal (fixture.monitor, ROW_DELETED, "0"); + + gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture.store), + &iter, "0:0"); + gtk_tree_store_remove (fixture.store, &iter); + + check_level_length (fixture.filter, NULL, 1); + check_level_length (fixture.filter, "0", 0); + + set_path_visibility (&fixture, "0", FALSE); + /* check_filter_model (&fixture); */ + signal_monitor_assert_is_empty (fixture.monitor); + + g_object_unref (fixture.filter); + g_object_unref (fixture.store); + gtk_widget_destroy (tree_view); +} + +static gboolean +specific_at_least_2_children_filter_filter_func (GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + return gtk_tree_model_iter_n_children (model, iter) >= 2; +} + +static void +specific_at_least_2_children_filter (void) +{ + GtkTreeModel *filter; + GtkTreeIter iter, root; + FilterTest fixture; /* This is not how it should be done */ + GtkWidget *tree_view; + + fixture.store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN); + filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (fixture.store), NULL); + fixture.filter = GTK_TREE_MODEL_FILTER (filter); + fixture.monitor = signal_monitor_new (filter); + + tree_view = gtk_tree_view_new_with_model (filter); + + gtk_tree_model_filter_set_visible_func (fixture.filter, + specific_at_least_2_children_filter_filter_func, + NULL, NULL); + + /* The first node will be initially invisible: no signals */ + gtk_tree_store_append (fixture.store, &root, NULL); + create_tree_store_set_values (fixture.store, &root, FALSE); + + /* check_filter_model (&fixture); */ + check_level_length (fixture.filter, NULL, 0); + signal_monitor_assert_is_empty (fixture.monitor); + + /* Insert a child node. Nothing should happen. + */ + gtk_tree_store_append (fixture.store, &iter, &root); + create_tree_store_set_values (fixture.store, &iter, TRUE); + + check_level_length (fixture.filter, NULL, 0); + signal_monitor_assert_is_empty (fixture.monitor); + + /* Insert a second child node. This will cause the parent to become + * visible. + */ + signal_monitor_append_signal (fixture.monitor, ROW_INSERTED, "0"); + signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "0"); + + gtk_tree_store_append (fixture.store, &iter, &root); + create_tree_store_set_values (fixture.store, &iter, TRUE); + + /* Parent must now be visible. Do the level length check first, + * to avoid modifying the child model triggering a row-changed to + * the filter model. + */ + check_level_length (fixture.filter, NULL, 1); + check_level_length (fixture.filter, "0", 0); + signal_monitor_assert_is_empty (fixture.monitor); + + /* This should propagate row-changed */ + signal_monitor_append_signal (fixture.monitor, ROW_CHANGED, "0"); + signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "0"); + + set_path_visibility (&fixture, "0", TRUE); + /* check_filter_model (&fixture); */ + signal_monitor_assert_is_empty (fixture.monitor); + + /* New root node, no child, so no signal */ + gtk_tree_store_append (fixture.store, &root, NULL); + check_level_length (fixture.filter, NULL, 1); + signal_monitor_assert_is_empty (fixture.monitor); + + /* First child, no signal, no change */ + gtk_tree_store_append (fixture.store, &iter, &root); + check_level_length (fixture.filter, NULL, 1); + signal_monitor_assert_is_empty (fixture.monitor); + + /* When the second child comes in, this node will become visible */ + signal_monitor_append_signal (fixture.monitor, ROW_INSERTED, "1"); + signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "1"); + signal_monitor_append_signal (fixture.monitor, ROW_CHANGED, "1"); + signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "1"); + + gtk_tree_store_append (fixture.store, &iter, &root); + check_level_length (fixture.filter, NULL, 2); + check_level_length (fixture.filter, "1", 0); + + create_tree_store_set_values (fixture.store, &root, TRUE); + create_tree_store_set_values (fixture.store, &iter, TRUE); + + /* check_filter_model (&fixture); */ + signal_monitor_assert_is_empty (fixture.monitor); + + /* Add another child for 1 */ + gtk_tree_store_append (fixture.store, &iter, &root); + create_tree_store_set_values (fixture.store, &iter, TRUE); + check_level_length (fixture.filter, NULL, 2); + check_level_length (fixture.filter, "0", 0); + check_level_length (fixture.filter, "1", 0); + signal_monitor_assert_is_empty (fixture.monitor); + + /* Now remove one of the remaining child rows */ + signal_monitor_append_signal (fixture.monitor, ROW_DELETED, "0"); + + gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture.store), + &iter, "0:0"); + gtk_tree_store_remove (fixture.store, &iter); + + check_level_length (fixture.filter, NULL, 1); + check_level_length (fixture.filter, "0", 0); + + set_path_visibility (&fixture, "0", FALSE); + /* check_filter_model (&fixture); */ + signal_monitor_assert_is_empty (fixture.monitor); + + g_object_unref (fixture.filter); + g_object_unref (fixture.store); + gtk_widget_destroy (tree_view); +} + +static void +specific_at_least_2_children_filter_on_sort_model (void) +{ + GtkTreeRowReference *ref; + GtkTreeModel *filter; + GtkTreeModel *sort_model; + GtkTreeIter iter, root; + FilterTest fixture; /* This is not how it should be done */ + GtkWidget *tree_view; + + fixture.store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN); + sort_model = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (fixture.store)); + filter = gtk_tree_model_filter_new (sort_model, NULL); + fixture.filter = GTK_TREE_MODEL_FILTER (filter); + fixture.monitor = signal_monitor_new (filter); + + tree_view = gtk_tree_view_new_with_model (filter); + + gtk_tree_model_filter_set_visible_func (fixture.filter, + specific_at_least_2_children_filter_filter_func, + NULL, NULL); + + /* The first node will be initially invisible: no signals */ + gtk_tree_store_append (fixture.store, &root, NULL); + create_tree_store_set_values (fixture.store, &root, FALSE); + + /* check_filter_model (&fixture); */ + check_level_length (fixture.filter, NULL, 0); + signal_monitor_assert_is_empty (fixture.monitor); + + /* Insert a child node. Nothing should happen. + */ + gtk_tree_store_append (fixture.store, &iter, &root); + create_tree_store_set_values (fixture.store, &iter, TRUE); + + check_level_length (fixture.filter, NULL, 0); + signal_monitor_assert_is_empty (fixture.monitor); + + { + GtkTreePath *path = gtk_tree_path_new_from_indices (0, 0, -1); + + ref = gtk_tree_row_reference_new (sort_model, path); + gtk_tree_path_free (path); + } + + /* Insert a second child node. This will cause the parent to become + * visible. + */ + signal_monitor_append_signal (fixture.monitor, ROW_INSERTED, "0"); + signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "0"); + + gtk_tree_store_append (fixture.store, &iter, &root); + create_tree_store_set_values (fixture.store, &iter, TRUE); + + /* Parent must now be visible. Do the level length check first, + * to avoid modifying the child model triggering a row-changed to + * the filter model. + */ + check_level_length (fixture.filter, NULL, 1); + check_level_length (fixture.filter, "0", 0); + signal_monitor_assert_is_empty (fixture.monitor); + + /* This should propagate row-changed */ + signal_monitor_append_signal (fixture.monitor, ROW_CHANGED, "0"); + signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "0"); + + set_path_visibility (&fixture, "0", TRUE); + /* check_filter_model (&fixture); */ + signal_monitor_assert_is_empty (fixture.monitor); + + /* New root node, no child, so no signal */ + gtk_tree_store_append (fixture.store, &root, NULL); + check_level_length (fixture.filter, NULL, 1); + signal_monitor_assert_is_empty (fixture.monitor); + + gtk_tree_row_reference_free (ref); + g_object_unref (fixture.filter); + g_object_unref (fixture.store); + gtk_widget_destroy (tree_view); +} + + +static void +specific_filter_add_child (void) +{ + /* This test is based on one of the test cases I found in my + * old test cases directory. I unfortunately do not have a record + * from who this test case originated. -Kris. + */ + + GtkTreeIter iter; + GtkTreeIter iter_first; + GtkTreeIter child; + GtkTreeStore *store; + GtkTreeModel *filter G_GNUC_UNUSED; + + store = gtk_tree_store_new (1, G_TYPE_STRING); + + gtk_tree_store_append (store, &iter_first, NULL); + gtk_tree_store_set (store, &iter_first, 0, "Hello", -1); + + gtk_tree_store_append (store, &iter, NULL); + gtk_tree_store_set (store, &iter, 0, "Hello", -1); + + gtk_tree_store_append (store, &iter, NULL); + gtk_tree_store_set (store, &iter, 0, "Hello", -1); + + gtk_tree_store_append (store, &iter, NULL); + gtk_tree_store_set (store, &iter, 0, "Hello", -1); + + filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (store), NULL); + + gtk_tree_store_set (store, &iter, 0, "Hello", -1); + gtk_tree_store_append (store, &child, &iter_first); + gtk_tree_store_set (store, &child, 0, "Hello", -1); +} + +static void +specific_list_store_clear (void) +{ + GtkTreeIter iter; + GtkListStore *list; + GtkTreeModel *filter; + GtkWidget *view G_GNUC_UNUSED; + + list = gtk_list_store_new (1, G_TYPE_INT); + gtk_list_store_insert_with_values (list, &iter, 0, 0, 1, -1); + gtk_list_store_insert_with_values (list, &iter, 1, 0, 2, -1); + gtk_list_store_insert_with_values (list, &iter, 2, 0, 3, -1); + gtk_list_store_insert_with_values (list, &iter, 3, 0, 4, -1); + gtk_list_store_insert_with_values (list, &iter, 4, 0, 5, -1); + gtk_list_store_insert_with_values (list, &iter, 5, 0, 6, -1); + gtk_list_store_insert_with_values (list, &iter, 6, 0, 7, -1); + gtk_list_store_insert_with_values (list, &iter, 7, 0, 8, -1); + + filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (list), NULL); + view = gtk_tree_view_new_with_model (filter); + + gtk_list_store_clear (list); +} + +static void +specific_sort_ref_leaf_and_remove_ancestor (void) +{ + GtkTreeIter iter, child, child2, child3; + GtkTreeStore *tree; + GtkTreeModel *sort; + GtkTreePath *path; + GtkTreeRowReference *rowref; + GtkWidget *view G_GNUC_UNUSED; + + tree = gtk_tree_store_new (1, G_TYPE_INT); + gtk_tree_store_insert_with_values (tree, &iter, NULL, 0, 0, 1, -1); + gtk_tree_store_insert_with_values (tree, &iter, NULL, 1, 0, 2, -1); + gtk_tree_store_insert_with_values (tree, &iter, NULL, 2, 0, 3, -1); + gtk_tree_store_insert_with_values (tree, &iter, NULL, 3, 0, 4, -1); + + gtk_tree_store_insert_with_values (tree, &child, &iter, 0, 0, 50, -1); + gtk_tree_store_insert_with_values (tree, &child2, &child, 0, 0, 6, -1); + gtk_tree_store_insert_with_values (tree, &child3, &child2, 0, 0, 7, -1); + + sort = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (tree)); + view = gtk_tree_view_new_with_model (sort); + gtk_tree_view_expand_all (GTK_TREE_VIEW (view)); + + path = gtk_tree_path_new_from_indices (3, 0, 0, 0, -1); + rowref = gtk_tree_row_reference_new (sort, path); + gtk_tree_path_free (path); + + path = gtk_tree_path_new_from_indices (3, 0, 0, 0, -1); + rowref = gtk_tree_row_reference_new (sort, path); + gtk_tree_path_free (path); + + path = gtk_tree_path_new_from_indices (3, 0, -1); + rowref = gtk_tree_row_reference_new (sort, path); + gtk_tree_path_free (path); + + path = gtk_tree_path_new_from_indices (3, -1); + rowref = gtk_tree_row_reference_new (sort, path); + gtk_tree_path_free (path); + + /* Deleting a parent */ + path = gtk_tree_path_new_from_indices (3, 0, -1); + gtk_tree_model_get_iter (GTK_TREE_MODEL (tree), &iter, path); + gtk_tree_store_remove (tree, &iter); + gtk_tree_path_free (path); + + gtk_tree_row_reference_free (rowref); +} + +static void +specific_ref_leaf_and_remove_ancestor (void) +{ + GtkTreeIter iter, child, child2, child3; + GtkTreeStore *tree; + GtkTreeModel *filter; + GtkTreePath *path; + GtkTreeRowReference *rowref; + GtkWidget *view G_GNUC_UNUSED; + + tree = gtk_tree_store_new (1, G_TYPE_INT); + gtk_tree_store_insert_with_values (tree, &iter, NULL, 0, 0, 1, -1); + gtk_tree_store_insert_with_values (tree, &iter, NULL, 1, 0, 2, -1); + gtk_tree_store_insert_with_values (tree, &iter, NULL, 2, 0, 3, -1); + gtk_tree_store_insert_with_values (tree, &iter, NULL, 3, 0, 4, -1); + + gtk_tree_store_insert_with_values (tree, &child, &iter, 0, 0, 50, -1); + gtk_tree_store_insert_with_values (tree, &child2, &child, 0, 0, 6, -1); + gtk_tree_store_insert_with_values (tree, &child3, &child2, 0, 0, 7, -1); + + filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (tree), NULL); + view = gtk_tree_view_new_with_model (filter); + gtk_tree_view_expand_all (GTK_TREE_VIEW (view)); + + path = gtk_tree_path_new_from_indices (3, 0, 0, 0, -1); + rowref = gtk_tree_row_reference_new (filter, path); + gtk_tree_path_free (path); + + path = gtk_tree_path_new_from_indices (3, 0, 0, 0, -1); + rowref = gtk_tree_row_reference_new (filter, path); + gtk_tree_path_free (path); + + path = gtk_tree_path_new_from_indices (3, 0, -1); + rowref = gtk_tree_row_reference_new (filter, path); + gtk_tree_path_free (path); + + path = gtk_tree_path_new_from_indices (3, -1); + rowref = gtk_tree_row_reference_new (filter, path); + gtk_tree_path_free (path); + + /* Deleting a parent */ + path = gtk_tree_path_new_from_indices (3, 0, -1); + gtk_tree_model_get_iter (GTK_TREE_MODEL (tree), &iter, path); + gtk_tree_store_remove (tree, &iter); + gtk_tree_path_free (path); + + gtk_tree_row_reference_free (rowref); +} + +static void +specific_virtual_ref_leaf_and_remove_ancestor (void) +{ + GtkTreeIter iter, child, child2, child3; + GtkTreeStore *tree; + GtkTreeModel *filter; + GtkTreePath *path; + GtkTreeRowReference *rowref; + GtkWidget *view G_GNUC_UNUSED; + + tree = gtk_tree_store_new (1, G_TYPE_INT); + gtk_tree_store_insert_with_values (tree, &iter, NULL, 0, 0, 1, -1); + gtk_tree_store_insert_with_values (tree, &iter, NULL, 1, 0, 2, -1); + gtk_tree_store_insert_with_values (tree, &iter, NULL, 2, 0, 3, -1); + gtk_tree_store_insert_with_values (tree, &iter, NULL, 3, 0, 4, -1); + + gtk_tree_store_insert_with_values (tree, &child, &iter, 0, 0, 50, -1); + gtk_tree_store_insert_with_values (tree, &child2, &child, 0, 0, 6, -1); + gtk_tree_store_insert_with_values (tree, &child3, &child2, 0, 0, 7, -1); + + /* Set a virtual root of 3:0 */ + path = gtk_tree_path_new_from_indices (3, 0, -1); + filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (tree), path); + gtk_tree_path_free (path); + + view = gtk_tree_view_new_with_model (filter); + gtk_tree_view_expand_all (GTK_TREE_VIEW (view)); + + path = gtk_tree_path_new_from_indices (0, 0, -1); + rowref = gtk_tree_row_reference_new (filter, path); + gtk_tree_path_free (path); + + path = gtk_tree_path_new_from_indices (0, 0, -1); + rowref = gtk_tree_row_reference_new (filter, path); + gtk_tree_path_free (path); + + path = gtk_tree_path_new_from_indices (0, -1); + rowref = gtk_tree_row_reference_new (filter, path); + gtk_tree_path_free (path); + + /* Deleting the virtual root */ + path = gtk_tree_path_new_from_indices (3, 0, -1); + gtk_tree_model_get_iter (GTK_TREE_MODEL (tree), &iter, path); + gtk_tree_store_remove (tree, &iter); + gtk_tree_path_free (path); + + gtk_tree_row_reference_free (rowref); +} + + +static int +specific_bug_301558_sort_func (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer data) +{ + int i, j; + + gtk_tree_model_get (model, a, 0, &i, -1); + gtk_tree_model_get (model, b, 0, &j, -1); + + return j - i; +} + +static void +specific_bug_301558 (void) +{ + /* Test case for GNOME Bugzilla bug 301558 provided by + * Markku Vire. + */ + GtkTreeStore *tree; + GtkTreeModel *filter; + GtkTreeModel *sort; + GtkTreeIter root, iter, iter2; + GtkWidget *view G_GNUC_UNUSED; + int i; + gboolean add; + + g_test_bug ("301558"); + + tree = gtk_tree_store_new (2, G_TYPE_INT, G_TYPE_BOOLEAN); + gtk_tree_store_append (tree, &iter, NULL); + gtk_tree_store_set (tree, &iter, 0, 123, 1, TRUE, -1); + gtk_tree_store_append (tree, &iter2, &iter); + gtk_tree_store_set (tree, &iter2, 0, 73, 1, TRUE, -1); + + sort = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (tree)); + gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (sort), + specific_bug_301558_sort_func, + NULL, NULL); + + filter = gtk_tree_model_filter_new (sort, NULL); + gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (filter), 1); + + view = gtk_tree_view_new_with_model (filter); + + while (gtk_events_pending ()) + gtk_main_iteration (); + + add = TRUE; + + for (i = 0; i < 10; i++) + { + if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (tree), &root)) + g_assert_not_reached (); + + if (add) + { + gtk_tree_store_append (tree, &iter, &root); + gtk_tree_store_set (tree, &iter, 0, 456, 1, TRUE, -1); + } + else + { + int n; + n = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (tree), &root); + gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (tree), &iter, + &root, n - 1); + gtk_tree_store_remove (tree, &iter); + } + + add = !add; + } +} + + +static gboolean +specific_bug_311955_filter_func (GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + int value; + + gtk_tree_model_get (model, iter, 0, &value, -1); + + return (value != 0); +} + +static void +specific_bug_311955 (void) +{ + /* This is a test case for GNOME Bugzilla bug 311955. It was written + * by Markku Vire. + */ + GtkTreeIter iter, child, root; + GtkTreeStore *store; + GtkTreeModel *sort; + GtkTreeModel *filter; + + GtkWidget *window G_GNUC_UNUSED; + GtkWidget *tree_view; + int i; + int n; + GtkTreePath *path; + + g_test_bug ("311955"); + + store = gtk_tree_store_new (1, G_TYPE_INT); + + gtk_tree_store_append (store, &root, NULL); + gtk_tree_store_set (store, &root, 0, 33, -1); + + gtk_tree_store_append (store, &iter, &root); + gtk_tree_store_set (store, &iter, 0, 50, -1); + + gtk_tree_store_append (store, &iter, NULL); + gtk_tree_store_set (store, &iter, 0, 22, -1); + + sort = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (store)); + filter = gtk_tree_model_filter_new (sort, NULL); + + gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter), + specific_bug_311955_filter_func, + NULL, NULL); + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + tree_view = gtk_tree_view_new_with_model (filter); + g_object_unref (store); + + gtk_tree_view_expand_all (GTK_TREE_VIEW (tree_view)); + + while (gtk_events_pending ()) + gtk_main_iteration (); + + check_level_length (GTK_TREE_MODEL_FILTER (filter), NULL, 2); + check_level_length (GTK_TREE_MODEL_FILTER (filter), "0", 1); + + /* Fill model */ + for (i = 0; i < 4; i++) + { + gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &root); + + gtk_tree_store_append (store, &iter, &root); + + if (i < 3) + gtk_tree_store_set (store, &iter, 0, i, -1); + + if (i % 2 == 0) + { + gtk_tree_store_append (store, &child, &iter); + gtk_tree_store_set (store, &child, 0, 10, -1); + } + } + + while (gtk_events_pending ()) + gtk_main_iteration (); + + check_level_length (GTK_TREE_MODEL_FILTER (filter), "0", 3); + check_level_length (GTK_TREE_MODEL_FILTER (filter), "0:2", 1); + + /* Remove bottommost child from the tree. */ + gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &root); + n = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (store), &root); + + if (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (store), &iter, &root, n - 2)) + { + if (gtk_tree_model_iter_children (GTK_TREE_MODEL (store), &child, &iter)) + gtk_tree_store_remove (store, &child); + } + else + g_assert_not_reached (); + + path = gtk_tree_path_new_from_indices (0, 2, -1); + gtk_tree_view_expand_row (GTK_TREE_VIEW (tree_view), path, FALSE); + gtk_tree_path_free (path); + + check_level_length (GTK_TREE_MODEL_FILTER (filter), "0", 3); + check_level_length (GTK_TREE_MODEL_FILTER (filter), "0:2", 0); +} + +static void +specific_bug_311955_clean (void) +{ + /* Cleaned up version of the test case for GNOME Bugzilla bug 311955, + * which is easier to understand. + */ + GtkTreeIter iter, child, grandchild; + GtkTreeStore *store; + GtkTreeModel *sort; + GtkTreeModel *filter; + + GtkWidget *tree_view; + GtkTreePath *path; + + store = gtk_tree_store_new (1, G_TYPE_INT); + + gtk_tree_store_append (store, &iter, NULL); + gtk_tree_store_set (store, &iter, 0, 1, -1); + + gtk_tree_store_append (store, &child, &iter); + gtk_tree_store_set (store, &child, 0, 1, -1); + + sort = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (store)); + filter = gtk_tree_model_filter_new (sort, NULL); + + gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter), + specific_bug_311955_filter_func, + NULL, NULL); + + tree_view = gtk_tree_view_new_with_model (filter); + g_object_unref (store); + + gtk_tree_view_expand_all (GTK_TREE_VIEW (tree_view)); + + while (gtk_events_pending ()) + gtk_main_iteration (); + + check_level_length (GTK_TREE_MODEL_FILTER (filter), NULL, 1); + check_level_length (GTK_TREE_MODEL_FILTER (filter), "0", 1); + + gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter); + + gtk_tree_store_append (store, &child, &iter); + gtk_tree_store_set (store, &child, 0, 0, -1); + + gtk_tree_store_append (store, &child, &iter); + gtk_tree_store_set (store, &child, 0, 1, -1); + + gtk_tree_store_append (store, &child, &iter); + gtk_tree_store_set (store, &child, 0, 1, -1); + + gtk_tree_store_append (store, &grandchild, &child); + gtk_tree_store_set (store, &grandchild, 0, 1, -1); + + gtk_tree_store_append (store, &child, &iter); + /* Don't set a value: assume 0 */ + + /* Remove leaf node, check trigger row-has-child-toggled */ + path = gtk_tree_path_new_from_indices (0, 3, 0, -1); + gtk_tree_model_get_iter (GTK_TREE_MODEL (store), &iter, path); + gtk_tree_path_free (path); + gtk_tree_store_remove (store, &iter); + + path = gtk_tree_path_new_from_indices (0, 2, -1); + gtk_tree_view_expand_row (GTK_TREE_VIEW (tree_view), path, FALSE); + gtk_tree_path_free (path); + + check_level_length (GTK_TREE_MODEL_FILTER (filter), "0", 3); + check_level_length (GTK_TREE_MODEL_FILTER (filter), "0:2", 0); + + gtk_widget_destroy (tree_view); +} + +static void +specific_bug_346800 (void) +{ + /* This is a test case for GNOME Bugzilla bug 346800. It was written + * by Jonathan Matthew. + */ + + GtkTreeIter node_iters[50]; + GtkTreeIter child_iters[50]; + GtkTreeModel *model; + GtkTreeModelFilter *filter; + GtkTreeStore *store; + GType *columns; + int i; + int items = 50; + columns = g_new (GType, 2); + columns[0] = G_TYPE_STRING; + columns[1] = G_TYPE_BOOLEAN; + store = gtk_tree_store_newv (2, columns); + model = GTK_TREE_MODEL (store); + + g_test_bug ("346800"); + + filter = GTK_TREE_MODEL_FILTER (gtk_tree_model_filter_new (model, NULL)); + gtk_tree_model_filter_set_visible_column (filter, 1); + + for (i=0; i 6) + { + gtk_tree_store_set (GTK_TREE_STORE (model), &child_iters[i-1], 1, + (i & 1) ? TRUE : FALSE, -1); + gtk_tree_model_filter_refilter (filter); + + gtk_tree_store_set (GTK_TREE_STORE (model), &child_iters[i-2], 1, + (i & 1) ? FALSE: TRUE, -1); + gtk_tree_model_filter_refilter (filter); + } + } +} + +static gboolean +specific_bug_464173_visible_func (GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + gboolean *visible = (gboolean *)data; + + return *visible; } static void -specific_bug_301558 (void) +specific_bug_464173 (void) { - /* Test case for GNOME Bugzilla bug 301558 provided by - * Markku Vire. + /* Test case for GNOME Bugzilla bug 464173, test case written + * by Andreas Koehler. */ - GtkTreeStore *tree; - GtkTreeModel *filter; - GtkTreeModel *sort; - GtkTreeIter root, iter, iter2; - GtkWidget *view; - int i; - gboolean add; - - tree = gtk_tree_store_new (2, G_TYPE_INT, G_TYPE_BOOLEAN); - gtk_tree_store_append (tree, &iter, NULL); - gtk_tree_store_set (tree, &iter, 0, 123, 1, TRUE, -1); - gtk_tree_store_append (tree, &iter2, &iter); - gtk_tree_store_set (tree, &iter2, 0, 73, 1, TRUE, -1); - - sort = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (tree)); - gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (sort), - specific_bug_301558_sort_func, - NULL, NULL); - - filter = gtk_tree_model_filter_new (sort, NULL); - gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (filter), 1); - - view = gtk_tree_view_new_with_model (filter); + GtkTreeStore *model; + GtkTreeModelFilter *f_model; + GtkTreeIter iter1, iter2; + GtkWidget *view G_GNUC_UNUSED; + gboolean visible = TRUE; - while (gtk_events_pending ()) - gtk_main_iteration (); + g_test_bug ("464173"); - add = TRUE; + model = gtk_tree_store_new (1, G_TYPE_STRING); + gtk_tree_store_append (model, &iter1, NULL); + gtk_tree_store_set (model, &iter1, 0, "Foo", -1); + gtk_tree_store_append (model, &iter2, &iter1); + gtk_tree_store_set (model, &iter2, 0, "Bar", -1); - for (i = 0; i < 10; i++) - { - if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (tree), &root)) - g_assert_not_reached (); + f_model = GTK_TREE_MODEL_FILTER (gtk_tree_model_filter_new (GTK_TREE_MODEL(model), NULL)); + gtk_tree_model_filter_set_visible_func (f_model, + specific_bug_464173_visible_func, + &visible, NULL); - if (add) - { - gtk_tree_store_append (tree, &iter, &root); - gtk_tree_store_set (tree, &iter, 0, 456, 1, TRUE, -1); - } - else - { - int n; - n = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (tree), &root); - gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (tree), &iter, - &root, n - 1); - gtk_tree_store_remove (tree, &iter); - } + view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (f_model)); - add = !add; - } + visible = FALSE; + gtk_tree_model_filter_refilter (f_model); } static gboolean -specific_bug_311955_filter_func (GtkTreeModel *model, +specific_bug_540201_filter_func (GtkTreeModel *model, GtkTreeIter *iter, gpointer data) { - int value; + gboolean has_children; - gtk_tree_model_get (model, iter, 0, &value, -1); + has_children = gtk_tree_model_iter_has_child (model, iter); - return (value != 0); + return has_children; } static void -specific_bug_311955 (void) +specific_bug_540201 (void) { - /* This is a test case for GNOME Bugzilla bug 311955. It was written - * by Markku Vire. + /* Test case for GNOME Bugzilla bug 540201, steps provided by + * Charles Day. */ - GtkTreeIter iter, child, root; + GtkTreeIter iter, root; GtkTreeStore *store; - GtkTreeModel *sort; GtkTreeModel *filter; - GtkWidget *window; - GtkWidget *tree_view; - int i; - int n; + GtkWidget *tree_view G_GNUC_UNUSED; + + g_test_bug ("540201"); store = gtk_tree_store_new (1, G_TYPE_INT); gtk_tree_store_append (store, &root, NULL); gtk_tree_store_set (store, &root, 0, 33, -1); + filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (store), NULL); + tree_view = gtk_tree_view_new_with_model (filter); + + gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter), + specific_bug_540201_filter_func, + NULL, NULL); + gtk_tree_store_append (store, &iter, &root); gtk_tree_store_set (store, &iter, 0, 50, -1); - gtk_tree_store_append (store, &iter, NULL); + gtk_tree_store_append (store, &iter, &root); gtk_tree_store_set (store, &iter, 0, 22, -1); - sort = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (store)); - filter = gtk_tree_model_filter_new (sort, NULL); - gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter), - specific_bug_311955_filter_func, - NULL, NULL); + gtk_tree_store_append (store, &root, NULL); + gtk_tree_store_set (store, &root, 0, 33, -1); - window = gtk_window_new (GTK_WINDOW_TOPLEVEL); - tree_view = gtk_tree_view_new_with_model (filter); - g_object_unref (store); + gtk_tree_store_append (store, &iter, &root); + gtk_tree_store_set (store, &iter, 0, 22, -1); +} - gtk_tree_view_expand_all (GTK_TREE_VIEW (tree_view)); - while (gtk_events_pending ()) - gtk_main_iteration (); +static gboolean +specific_bug_549287_visible_func (GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + gboolean result = FALSE; + + result = gtk_tree_model_iter_has_child (model, iter); + + return result; +} + +static void +specific_bug_549287 (void) +{ + /* Test case for GNOME Bugzilla bug 529287, provided by Julient Puydt */ + + int i; + GtkTreeStore *store; + GtkTreeModel *filtered; + GtkWidget *view G_GNUC_UNUSED; + GtkTreeIter iter; + GtkTreeIter *swap, *parent, *child; + + g_test_bug ("529287"); + + store = gtk_tree_store_new (1, G_TYPE_STRING); + filtered = gtk_tree_model_filter_new (GTK_TREE_MODEL (store), NULL); + gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filtered), + specific_bug_549287_visible_func, + NULL, NULL); + + view = gtk_tree_view_new_with_model (filtered); - /* Fill model */ for (i = 0; i < 4; i++) { - gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &root); + if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter)) + { + parent = gtk_tree_iter_copy (&iter); + child = gtk_tree_iter_copy (&iter); - gtk_tree_store_append (store, &iter, &root); + while (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (store), + child, parent, 0)) + { - if (i < 3) - gtk_tree_store_set (store, &iter, 0, i, -1); + swap = parent; + parent = child; + child = swap; + } - if (i % 2 == 0) + gtk_tree_store_append (store, child, parent); + gtk_tree_store_set (store, child, + 0, "Something", + -1); + + gtk_tree_iter_free (parent); + gtk_tree_iter_free (child); + } + else { - gtk_tree_store_append (store, &child, &iter); - gtk_tree_store_set (store, &child, 0, 10, -1); + gtk_tree_store_append (store, &iter, NULL); + gtk_tree_store_set (store, &iter, + 0, "Something", + -1); } - } - while (gtk_events_pending ()) - gtk_main_iteration (); + /* since we inserted something, we changed the visibility conditions: */ + gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filtered)); + } +} - /* Remove bottommost child from the tree. */ - gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &root); - n = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (store), &root); +static gboolean +specific_bug_621076_visible_func (GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + gboolean visible = FALSE; + gchar *str = NULL; - if (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (store), &iter, &root, n - 2)) + gtk_tree_model_get (model, iter, 0, &str, -1); + if (str != NULL && g_str_has_prefix (str, "visible")) { - if (gtk_tree_model_iter_children (GTK_TREE_MODEL (store), &child, &iter)) - gtk_tree_store_remove (store, &child); + visible = TRUE; } else - g_assert_not_reached (); + { + GtkTreeIter child_iter; + gboolean valid; + + /* Recursively check if we have a visible child */ + for (valid = gtk_tree_model_iter_children (model, &child_iter, iter); + valid; valid = gtk_tree_model_iter_next (model, &child_iter)) + { + if (specific_bug_621076_visible_func (model, &child_iter, data)) + { + visible = TRUE; + break; + } + } + } + + if (str) + g_free (str); + + return visible; } static void -specific_bug_346800 (void) +specific_bug_621076 (void) { - /* This is a test case for GNOME Bugzilla bug 346800. It was written - * by Jonathan Matthew. + /* Test case for GNOME Bugzilla bug 621076, provided by Xavier Claessens */ + + /* This test case differs from has-child-filter and root-has-child-filter + * in that the visible function both filters on content and model + * structure. Also, it is recursive. */ - GtkTreeIter node_iters[50]; - GtkTreeIter child_iters[50]; - GtkTreeModel *model; - GtkTreeModelFilter *filter; GtkTreeStore *store; - GType *columns; - int i; - int items = 50; - columns = g_new (GType, 2); - columns[0] = G_TYPE_STRING; - columns[1] = G_TYPE_BOOLEAN; - store = gtk_tree_store_newv (2, columns); - model = GTK_TREE_MODEL (store); + GtkTreeModel *filter; + GtkWidget *view; + GtkTreeIter group_iter; + GtkTreeIter item_iter; + SignalMonitor *monitor; - filter = GTK_TREE_MODEL_FILTER (gtk_tree_model_filter_new (model, NULL)); - gtk_tree_model_filter_set_visible_column (filter, 1); + g_test_bug ("621076"); - for (i=0; i