]> Pileus Git - ~andy/gtk/blob - gtk/tests/filtermodel.c
Emit row-has-child-toggled when a first node becomes visible in a level
[~andy/gtk] / gtk / tests / filtermodel.c
1 /* Extensive GtkTreeModelFilter tests.
2  * Copyright (C) 2009  Kristian Rietveld  <kris@gtk.org>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include <gtk/gtk.h>
21
22
23 /*
24  * Model creation
25  */
26
27 #define LEVEL_LENGTH 5
28
29 static void
30 create_tree_store_set_values (GtkTreeStore *store,
31                               GtkTreeIter  *iter,
32                               gboolean      visible)
33 {
34   GtkTreePath *path;
35
36   path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), iter);
37   gtk_tree_store_set (store, iter,
38                       0, gtk_tree_path_to_string (path),
39                       1, visible,
40                       -1);
41   gtk_tree_path_free (path);
42 }
43
44 static void
45 create_tree_store_recurse (int           depth,
46                            GtkTreeStore *store,
47                            GtkTreeIter  *parent,
48                            gboolean      visible)
49 {
50   int i;
51
52   for (i = 0; i < LEVEL_LENGTH; i++)
53     {
54       GtkTreeIter iter;
55
56       gtk_tree_store_insert (store, &iter, parent, i);
57       create_tree_store_set_values (store, &iter, visible);
58
59       if (depth > 0)
60         create_tree_store_recurse (depth - 1, store, &iter, visible);
61     }
62 }
63
64 static GtkTreeStore *
65 create_tree_store (int      depth,
66                    gboolean visible)
67 {
68   GtkTreeStore *store;
69
70   store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN);
71
72   create_tree_store_recurse (depth, store, NULL, visible);
73
74   return store;
75 }
76
77 /*
78  * Signal monitor
79  */
80
81 typedef enum
82 {
83   ROW_INSERTED,
84   ROW_DELETED,
85   ROW_CHANGED,
86   ROW_HAS_CHILD_TOGGLED,
87   ROWS_REORDERED,
88   LAST_SIGNAL
89 }
90 SignalName;
91
92 typedef struct
93 {
94   SignalName signal;
95   GtkTreePath *path;
96 }
97 Signal;
98
99
100 static Signal *
101 signal_new (SignalName signal, GtkTreePath *path)
102 {
103   Signal *s;
104
105   s = g_new0 (Signal, 1);
106   s->signal = signal;
107   s->path = gtk_tree_path_copy (path);
108
109   return s;
110 }
111
112 static void
113 signal_free (Signal *s)
114 {
115   if (s->path)
116     gtk_tree_path_free (s->path);
117
118   g_free (s);
119 }
120
121
122 typedef struct
123 {
124   GQueue *queue;
125   GtkTreeModel *client;
126   guint signal_ids[LAST_SIGNAL];
127 }
128 SignalMonitor;
129
130
131 static void
132 signal_monitor_generic_handler (SignalMonitor *m,
133                                 SignalName     signal,
134                                 GtkTreeModel  *model,
135                                 GtkTreePath   *path)
136 {
137   Signal *s;
138
139   g_return_if_fail (m->client == model);
140   g_return_if_fail (!g_queue_is_empty (m->queue));
141
142 #if 0
143   /* For debugging: output signals that are coming in.  Leaks memory. */
144   g_print ("signal=%d  path=%s\n", signal, gtk_tree_path_to_string (path));
145 #endif
146
147   s = g_queue_peek_tail (m->queue);
148
149   g_return_if_fail (s->signal == signal);
150
151   s = g_queue_pop_tail (m->queue);
152
153   g_return_if_fail (!gtk_tree_path_compare (path, s->path));
154
155   signal_free (s);
156 }
157
158 static void
159 signal_monitor_row_inserted (GtkTreeModel *model,
160                              GtkTreePath  *path,
161                              GtkTreeIter  *iter,
162                              gpointer      data)
163 {
164   signal_monitor_generic_handler (data, ROW_INSERTED,
165                                   model, path);
166 }
167
168 static void
169 signal_monitor_row_deleted (GtkTreeModel *model,
170                             GtkTreePath  *path,
171                             gpointer      data)
172 {
173   signal_monitor_generic_handler (data, ROW_DELETED,
174                                   model, path);
175 }
176
177 static void
178 signal_monitor_row_changed (GtkTreeModel *model,
179                             GtkTreePath  *path,
180                             GtkTreeIter  *iter,
181                             gpointer      data)
182 {
183   signal_monitor_generic_handler (data, ROW_CHANGED,
184                                   model, path);
185 }
186
187 static void
188 signal_monitor_row_has_child_toggled (GtkTreeModel *model,
189                                       GtkTreePath  *path,
190                                       GtkTreeIter  *iter,
191                                       gpointer      data)
192 {
193   signal_monitor_generic_handler (data, ROW_HAS_CHILD_TOGGLED,
194                                   model, path);
195 }
196
197 static void
198 signal_monitor_rows_reordered (GtkTreeModel *model,
199                                GtkTreePath  *path,
200                                GtkTreeIter  *iter,
201                                gint         *new_order,
202                                gpointer      data)
203 {
204   signal_monitor_generic_handler (data, ROWS_REORDERED,
205                                   model, path);
206 }
207
208 static SignalMonitor *
209 signal_monitor_new (GtkTreeModel *client)
210 {
211   SignalMonitor *m;
212
213   m = g_new0 (SignalMonitor, 1);
214   m->client = g_object_ref (client);
215   m->queue = g_queue_new ();
216
217   m->signal_ids[ROW_INSERTED] = g_signal_connect (client,
218                                                   "row-inserted",
219                                                   G_CALLBACK (signal_monitor_row_inserted),
220                                                   m);
221   m->signal_ids[ROW_DELETED] = g_signal_connect (client,
222                                                  "row-deleted",
223                                                  G_CALLBACK (signal_monitor_row_deleted),
224                                                  m);
225   m->signal_ids[ROW_CHANGED] = g_signal_connect (client,
226                                                  "row-changed",
227                                                  G_CALLBACK (signal_monitor_row_changed),
228                                                  m);
229   m->signal_ids[ROW_HAS_CHILD_TOGGLED] = g_signal_connect (client,
230                                                            "row-has-child-toggled",
231                                                            G_CALLBACK (signal_monitor_row_has_child_toggled),
232                                                            m);
233   m->signal_ids[ROWS_REORDERED] = g_signal_connect (client,
234                                                     "rows-reordered",
235                                                     G_CALLBACK (signal_monitor_rows_reordered),
236                                                     m);
237
238   return m;
239 }
240
241 static void
242 signal_monitor_free (SignalMonitor *m)
243 {
244   int i;
245
246   for (i = 0; i < LAST_SIGNAL; i++)
247     g_signal_handler_disconnect (m->client, m->signal_ids[i]);
248
249   g_object_unref (m->client);
250
251   if (m->queue)
252     g_queue_free (m->queue);
253
254   g_free (m);
255 }
256
257 static void
258 signal_monitor_assert_is_empty (SignalMonitor *m)
259 {
260   g_assert (g_queue_is_empty (m->queue));
261 }
262
263 static void
264 signal_monitor_append_signal_path (SignalMonitor *m,
265                                    SignalName     signal,
266                                    GtkTreePath   *path)
267 {
268   Signal *s;
269
270   s = signal_new (signal, path);
271   g_queue_push_head (m->queue, s);
272 }
273
274 static void
275 signal_monitor_append_signal (SignalMonitor *m,
276                               SignalName     signal,
277                               const gchar   *path_string)
278 {
279   Signal *s;
280   GtkTreePath *path;
281
282   path = gtk_tree_path_new_from_string (path_string);
283
284   s = signal_new (signal, path);
285   g_queue_push_head (m->queue, s);
286
287   gtk_tree_path_free (path);
288 }
289
290 /*
291  * Fixture
292  */
293
294 typedef struct
295 {
296   GtkWidget *tree_view;
297
298   GtkTreeStore *store;
299   GtkTreeModelFilter *filter;
300
301   SignalMonitor *monitor;
302 } FilterTest;
303
304 static void
305 filter_test_setup_generic (FilterTest    *fixture,
306                            gconstpointer  test_data,
307                            int            depth,
308                            gboolean       empty,
309                            gboolean       unfiltered)
310 {
311   const GtkTreePath *vroot = test_data;
312   GtkTreeModel *filter;
313
314   fixture->store = create_tree_store (depth, !empty);
315
316   /* Please forgive me for casting const away. */
317   filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (fixture->store),
318                                       (GtkTreePath *)vroot);
319   fixture->filter = GTK_TREE_MODEL_FILTER (filter);
320
321   if (!unfiltered)
322     gtk_tree_model_filter_set_visible_column (fixture->filter, 1);
323
324   /* We need a tree view that's listening to get ref counting from that
325    * side.
326    */
327   fixture->tree_view = gtk_tree_view_new_with_model (filter);
328
329   fixture->monitor = signal_monitor_new (filter);
330 }
331
332 static void
333 filter_test_setup (FilterTest    *fixture,
334                    gconstpointer  test_data)
335 {
336   filter_test_setup_generic (fixture, test_data, 3, FALSE, FALSE);
337 }
338
339 static void
340 filter_test_setup_empty (FilterTest    *fixture,
341                          gconstpointer  test_data)
342 {
343   filter_test_setup_generic (fixture, test_data, 3, TRUE, FALSE);
344 }
345
346 static void
347 filter_test_setup_unfiltered (FilterTest    *fixture,
348                               gconstpointer  test_data)
349 {
350   filter_test_setup_generic (fixture, test_data, 3, FALSE, TRUE);
351 }
352
353 static void
354 filter_test_setup_empty_unfiltered (FilterTest    *fixture,
355                                     gconstpointer  test_data)
356 {
357   filter_test_setup_generic (fixture, test_data, 3, TRUE, TRUE);
358 }
359
360 static GtkTreePath *
361 strip_virtual_root (GtkTreePath *path,
362                     GtkTreePath *root_path)
363 {
364   GtkTreePath *real_path;
365
366   if (root_path)
367     {
368       int j;
369       int depth = gtk_tree_path_get_depth (path);
370       int root_depth = gtk_tree_path_get_depth (root_path);
371
372       real_path = gtk_tree_path_new ();
373
374       for (j = 0; j < depth - root_depth; j++)
375         gtk_tree_path_append_index (real_path,
376                                     gtk_tree_path_get_indices (path)[root_depth + j]);
377     }
378   else
379     real_path = gtk_tree_path_copy (path);
380
381   return real_path;
382 }
383
384 static void
385 filter_test_append_refilter_signals_recurse (FilterTest  *fixture,
386                                              GtkTreePath *store_path,
387                                              GtkTreePath *filter_path,
388                                              int          depth,
389                                              GtkTreePath *root_path)
390 {
391   int i;
392   int rows_deleted = 0;
393   GtkTreeIter iter;
394
395   gtk_tree_path_down (store_path);
396   gtk_tree_path_down (filter_path);
397
398   gtk_tree_model_get_iter (GTK_TREE_MODEL (fixture->store),
399                            &iter, store_path);
400
401   for (i = 0; i < LEVEL_LENGTH; i++)
402     {
403       gboolean visible;
404       GtkTreePath *real_path;
405
406       gtk_tree_model_get (GTK_TREE_MODEL (fixture->store), &iter,
407                           1, &visible,
408                           -1);
409
410       if (root_path &&
411           (!gtk_tree_path_is_descendant (store_path, root_path)
412            || !gtk_tree_path_compare (store_path, root_path)))
413         {
414           if (!gtk_tree_path_compare (store_path, root_path))
415             {
416               if (depth > 1
417                   && gtk_tree_model_iter_has_child (GTK_TREE_MODEL (fixture->store),
418                                                     &iter))
419                 {
420                   GtkTreePath *store_copy;
421                   GtkTreePath *filter_copy;
422
423                   store_copy = gtk_tree_path_copy (store_path);
424                   filter_copy = gtk_tree_path_copy (filter_path);
425                   filter_test_append_refilter_signals_recurse (fixture,
426                                                                store_copy,
427                                                                filter_copy,
428                                                                depth - 1,
429                                                                root_path);
430                   gtk_tree_path_free (store_copy);
431                   gtk_tree_path_free (filter_copy);
432                 }
433             }
434
435           gtk_tree_path_next (store_path);
436           gtk_tree_model_iter_next (GTK_TREE_MODEL (fixture->store), &iter);
437
438           if (visible)
439             gtk_tree_path_next (filter_path);
440
441           continue;
442         }
443
444       real_path = strip_virtual_root (filter_path, root_path);
445
446       if (visible)
447         {
448           /* This row will be inserted */
449           signal_monitor_append_signal_path (fixture->monitor, ROW_CHANGED,
450                                              real_path);
451           signal_monitor_append_signal_path (fixture->monitor,
452                                              ROW_HAS_CHILD_TOGGLED,
453                                              real_path);
454
455           if (depth > 1
456               && gtk_tree_model_iter_has_child (GTK_TREE_MODEL (fixture->store),
457                                                 &iter))
458             {
459               GtkTreePath *store_copy;
460               GtkTreePath *filter_copy;
461
462               store_copy = gtk_tree_path_copy (store_path);
463               filter_copy = gtk_tree_path_copy (filter_path);
464               filter_test_append_refilter_signals_recurse (fixture,
465                                                            store_copy,
466                                                            filter_copy,
467                                                            depth - 1,
468                                                            root_path);
469               gtk_tree_path_free (store_copy);
470               gtk_tree_path_free (filter_copy);
471             }
472
473           gtk_tree_path_next (filter_path);
474         }
475       else
476         {
477           /* This row will be deleted */
478           rows_deleted++;
479           signal_monitor_append_signal_path (fixture->monitor, ROW_DELETED,
480                                              real_path);
481         }
482
483       gtk_tree_path_free (real_path);
484
485       gtk_tree_path_next (store_path);
486       gtk_tree_model_iter_next (GTK_TREE_MODEL (fixture->store), &iter);
487     }
488
489   if (rows_deleted == LEVEL_LENGTH
490       && gtk_tree_path_get_depth (filter_path) > 1)
491     {
492       GtkTreePath *real_path;
493
494       gtk_tree_path_up (store_path);
495       gtk_tree_path_up (filter_path);
496
497       /* A row-has-child-toggled will be emitted on the parent */
498       if (!root_path
499           || (root_path
500               && gtk_tree_path_is_descendant (store_path, root_path)
501               && gtk_tree_path_compare (store_path, root_path)))
502         {
503           real_path = strip_virtual_root (filter_path, root_path);
504           signal_monitor_append_signal_path (fixture->monitor,
505                                              ROW_HAS_CHILD_TOGGLED,
506                                              real_path);
507
508           gtk_tree_path_free (real_path);
509         }
510     }
511 }
512
513 static void
514 filter_test_append_refilter_signals (FilterTest *fixture,
515                                      int         depth)
516 {
517   /* A special function that walks the tree store like the
518    * model validation functions below.
519    */
520   GtkTreePath *path;
521   GtkTreePath *filter_path;
522
523   path = gtk_tree_path_new ();
524   filter_path = gtk_tree_path_new ();
525   filter_test_append_refilter_signals_recurse (fixture,
526                                                path,
527                                                filter_path,
528                                                depth,
529                                                NULL);
530   gtk_tree_path_free (path);
531   gtk_tree_path_free (filter_path);
532 }
533
534 static void
535 filter_test_append_refilter_signals_with_vroot (FilterTest  *fixture,
536                                                 int          depth,
537                                                 GtkTreePath *root_path)
538 {
539   /* A special function that walks the tree store like the
540    * model validation functions below.
541    */
542   GtkTreePath *path;
543   GtkTreePath *filter_path;
544
545   path = gtk_tree_path_new ();
546   filter_path = gtk_tree_path_new ();
547   filter_test_append_refilter_signals_recurse (fixture,
548                                                path,
549                                                filter_path,
550                                                depth,
551                                                root_path);
552   gtk_tree_path_free (path);
553   gtk_tree_path_free (filter_path);
554 }
555
556 static void
557 filter_test_enable_filter (FilterTest *fixture)
558 {
559   gtk_tree_model_filter_set_visible_column (fixture->filter, 1);
560   gtk_tree_model_filter_refilter (fixture->filter);
561 }
562
563 static void
564 filter_test_teardown (FilterTest    *fixture,
565                       gconstpointer  test_data)
566 {
567   signal_monitor_free (fixture->monitor);
568
569   g_object_unref (fixture->filter);
570   g_object_unref (fixture->store);
571 }
572
573 /*
574  * Model structure validation
575  */
576
577 static void
578 check_filter_model_recurse (FilterTest  *fixture,
579                             GtkTreePath *store_parent_path,
580                             GtkTreePath *filter_parent_path)
581 {
582   int i;
583   GtkTreeIter store_iter;
584   GtkTreeIter filter_iter;
585   gboolean store_has_next, filter_has_next;
586
587   gtk_tree_path_down (store_parent_path);
588   gtk_tree_path_down (filter_parent_path);
589
590   store_has_next = gtk_tree_model_get_iter (GTK_TREE_MODEL (fixture->store),
591                                             &store_iter, store_parent_path);
592   filter_has_next = gtk_tree_model_get_iter (GTK_TREE_MODEL (fixture->filter),
593                                              &filter_iter, filter_parent_path);
594
595   for (i = 0; i < LEVEL_LENGTH; i++)
596     {
597       gboolean visible;
598
599       g_return_if_fail (store_has_next == TRUE);
600
601       gtk_tree_model_get (GTK_TREE_MODEL (fixture->store),
602                           &store_iter,
603                           1, &visible,
604                           -1);
605
606       if (visible)
607         {
608           GtkTreePath *tmp;
609           gchar *filter_str, *store_str;
610
611           g_return_if_fail (filter_has_next == TRUE);
612
613           /* Verify path */
614           tmp = gtk_tree_model_get_path (GTK_TREE_MODEL (fixture->filter),
615                                          &filter_iter);
616           g_return_if_fail (gtk_tree_path_compare (tmp, filter_parent_path) == 0);
617
618           /* Verify model content */
619           gtk_tree_model_get (GTK_TREE_MODEL (fixture->store),
620                               &store_iter,
621                               0, &store_str,
622                               -1);
623           gtk_tree_model_get (GTK_TREE_MODEL (fixture->filter),
624                               &filter_iter,
625                               0, &filter_str,
626                               -1);
627
628           g_return_if_fail (g_strcmp0 (store_str, filter_str) == 0);
629
630           g_free (store_str);
631           g_free (filter_str);
632
633           if (gtk_tree_model_iter_has_child (GTK_TREE_MODEL (fixture->filter),
634                                              &filter_iter))
635             {
636               g_return_if_fail (gtk_tree_model_iter_has_child (GTK_TREE_MODEL (fixture->store), &store_iter));
637
638               check_filter_model_recurse (fixture,
639                                           gtk_tree_path_copy (store_parent_path),
640                                           tmp);
641             }
642
643           gtk_tree_path_next (filter_parent_path);
644           filter_has_next = gtk_tree_model_iter_next (GTK_TREE_MODEL (fixture->filter), &filter_iter);
645         }
646
647       gtk_tree_path_next (store_parent_path);
648       store_has_next = gtk_tree_model_iter_next (GTK_TREE_MODEL (fixture->store), &store_iter);
649     }
650
651   /* Both models should have no more content! */
652   g_return_if_fail (store_has_next == FALSE);
653   g_return_if_fail (filter_has_next == FALSE);
654
655   gtk_tree_path_free (store_parent_path);
656   gtk_tree_path_free (filter_parent_path);
657 }
658
659 static void
660 check_filter_model (FilterTest *fixture)
661 {
662   GtkTreePath *path;
663
664   if (fixture->monitor)
665     signal_monitor_assert_is_empty (fixture->monitor);
666
667   path = gtk_tree_path_new ();
668
669   check_filter_model_recurse (fixture, path, gtk_tree_path_copy (path));
670 }
671
672 static void
673 check_filter_model_with_root (FilterTest  *fixture,
674                               GtkTreePath *path)
675 {
676   if (fixture->monitor)
677     signal_monitor_assert_is_empty (fixture->monitor);
678
679   check_filter_model_recurse (fixture,
680                               gtk_tree_path_copy (path),
681                               gtk_tree_path_new ());
682 }
683
684 /* Helpers */
685
686 static void
687 check_level_length (GtkTreeModelFilter *filter,
688                     const gchar        *level,
689                     const int           length)
690 {
691   if (!level)
692     {
693       int l = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (filter), NULL);
694       g_return_if_fail (l == length);
695     }
696   else
697     {
698       int l;
699       gboolean retrieved_iter = FALSE;
700       GtkTreeIter iter;
701
702       retrieved_iter = gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (filter),
703                                                             &iter, level);
704       g_return_if_fail (retrieved_iter);
705       l = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (filter), &iter);
706       g_return_if_fail (l == length);
707     }
708 }
709
710 static void
711 set_path_visibility (FilterTest  *fixture,
712                      const gchar *path,
713                      gboolean     visible)
714 {
715   GtkTreeIter store_iter;
716
717   gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store),
718                                        &store_iter, path);
719   gtk_tree_store_set (fixture->store, &store_iter,
720                       1, visible,
721                       -1);
722 }
723
724 static void
725 insert_path_with_visibility (FilterTest  *fixture,
726                              const gchar *path_string,
727                              gboolean     visible)
728 {
729   int position;
730   GtkTreePath *path;
731   GtkTreeIter parent, iter;
732
733   path = gtk_tree_path_new_from_string (path_string);
734   position = gtk_tree_path_get_indices (path)[gtk_tree_path_get_depth (path)];
735   gtk_tree_path_up (path);
736
737   if (gtk_tree_model_get_iter (GTK_TREE_MODEL (fixture->store), &parent, path))
738     {
739       gtk_tree_store_insert (fixture->store, &iter, &parent, position);
740       create_tree_store_set_values (fixture->store, &iter, visible);
741     }
742   gtk_tree_path_free (path);
743 }
744
745 /*
746  * The actual tests.
747  */
748
749 static void
750 verify_test_suite (FilterTest    *fixture,
751                    gconstpointer  user_data)
752 {
753   check_filter_model (fixture);
754 }
755
756 static void
757 verify_test_suite_vroot (FilterTest    *fixture,
758                          gconstpointer  user_data)
759 {
760   check_filter_model_with_root (fixture, (GtkTreePath *)user_data);
761 }
762
763
764 static void
765 filled_hide_root_level (FilterTest    *fixture,
766                         gconstpointer  user_data)
767 {
768   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "2");
769   set_path_visibility (fixture, "2", FALSE);
770   check_filter_model (fixture);
771   check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 1);
772
773   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0");
774   set_path_visibility (fixture, "0", FALSE);
775   check_filter_model (fixture);
776   check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 2);
777
778   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "2");
779   set_path_visibility (fixture, "4", FALSE);
780   check_filter_model (fixture);
781   check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 3);
782
783
784   /* Hide remaining */
785   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0");
786   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0");
787
788   set_path_visibility (fixture, "1", FALSE);
789   check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 4);
790
791   set_path_visibility (fixture, "3", FALSE);
792   check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 5);
793
794   check_filter_model (fixture);
795
796   /* Show some */
797   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0");
798   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
799   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "1");
800   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "1");
801
802   set_path_visibility (fixture, "1", TRUE);
803   check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 4);
804
805   set_path_visibility (fixture, "3", TRUE);
806   check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 3);
807
808   check_filter_model (fixture);
809 }
810
811 static void
812 filled_hide_child_levels (FilterTest    *fixture,
813                           gconstpointer  user_data)
814 {
815   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0:2");
816   set_path_visibility (fixture, "0:2", FALSE);
817   check_filter_model (fixture);
818   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
819   check_level_length (fixture->filter, "0", LEVEL_LENGTH - 1);
820
821   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0:3");
822   set_path_visibility (fixture, "0:4", FALSE);
823   check_filter_model (fixture);
824   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
825   check_level_length (fixture->filter, "0", LEVEL_LENGTH - 2);
826
827   set_path_visibility (fixture, "0:4:3", FALSE);
828   check_filter_model (fixture);
829   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
830   check_level_length (fixture->filter, "0", LEVEL_LENGTH - 2);
831
832   set_path_visibility (fixture, "0:4:0", FALSE);
833   set_path_visibility (fixture, "0:4:1", FALSE);
834   set_path_visibility (fixture, "0:4:2", FALSE);
835   set_path_visibility (fixture, "0:4:4", FALSE);
836   check_filter_model (fixture);
837   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
838   check_level_length (fixture->filter, "0", LEVEL_LENGTH - 2);
839
840   /* Since "0:2" is hidden, "0:4" must be "0:3" in the filter model */
841   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:3");
842   /* FIXME: Actually, the filter model should not be emitted the
843    * row-has-child-toggled signal here.  *However* an extraneous emission
844    * of this signal does not hurt and is allowed.
845    */
846   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:3");
847   set_path_visibility (fixture, "0:4", TRUE);
848   check_filter_model (fixture);
849   check_level_length (fixture->filter, "0:3", 0);
850
851   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:2");
852   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:2");
853   set_path_visibility (fixture, "0:2", TRUE);
854   check_filter_model (fixture);
855   check_level_length (fixture->filter, "0:2", LEVEL_LENGTH);
856   check_level_length (fixture->filter, "0:3", LEVEL_LENGTH);
857   check_level_length (fixture->filter, "0:4", 0);
858
859   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:4:0");
860   /* Once 0:4:0 got inserted, 0:4 became a parent */
861   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:4");
862   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:4:0");
863   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:4:1");
864   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:4:1");
865
866   set_path_visibility (fixture, "0:4:2", TRUE);
867   set_path_visibility (fixture, "0:4:4", TRUE);
868   signal_monitor_assert_is_empty (fixture->monitor);
869   check_level_length (fixture->filter, "0:4", 2);
870 }
871
872
873 static void
874 filled_vroot_hide_root_level (FilterTest    *fixture,
875                               gconstpointer  user_data)
876 {
877   GtkTreePath *path = (GtkTreePath *)user_data;
878
879   /* These changes do not affect the filter's root level */
880   set_path_visibility (fixture, "0", FALSE);
881   check_filter_model_with_root (fixture, path);
882   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
883   check_level_length (fixture->filter, "0", LEVEL_LENGTH);
884
885   set_path_visibility (fixture, "4", FALSE);
886   check_filter_model_with_root (fixture, path);
887   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
888   check_level_length (fixture->filter, "0", LEVEL_LENGTH);
889
890   /* Even though we set the virtual root parent node to FALSE,
891    * the virtual root contents remain.
892    */
893   set_path_visibility (fixture, "2", FALSE);
894   check_filter_model_with_root (fixture, path);
895   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
896   check_level_length (fixture->filter, "0", LEVEL_LENGTH);
897
898   /* No change */
899   set_path_visibility (fixture, "1", FALSE);
900   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
901   check_level_length (fixture->filter, "0", LEVEL_LENGTH);
902
903   set_path_visibility (fixture, "3", FALSE);
904   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
905   check_level_length (fixture->filter, "0", LEVEL_LENGTH);
906
907   check_filter_model_with_root (fixture, path);
908
909   /* Show some */
910   set_path_visibility (fixture, "2", TRUE);
911   check_filter_model_with_root (fixture, path);
912   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
913   check_level_length (fixture->filter, "0", LEVEL_LENGTH);
914
915   set_path_visibility (fixture, "1", TRUE);
916   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
917   check_level_length (fixture->filter, "0", LEVEL_LENGTH);
918
919   set_path_visibility (fixture, "3", TRUE);
920   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
921   check_level_length (fixture->filter, "0", LEVEL_LENGTH);
922
923   check_filter_model_with_root (fixture, path);
924
925   /* Now test changes in the virtual root level */
926   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "2");
927   set_path_visibility (fixture, "2:2", FALSE);
928   check_filter_model_with_root (fixture, path);
929   check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 1);
930
931   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "3");
932   set_path_visibility (fixture, "2:4", FALSE);
933   check_filter_model_with_root (fixture, path);
934   check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 2);
935
936   set_path_visibility (fixture, "1:4", FALSE);
937   check_filter_model_with_root (fixture, path);
938   check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 2);
939
940   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "3");
941   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "3");
942   set_path_visibility (fixture, "2:4", TRUE);
943   check_filter_model_with_root (fixture, path);
944   check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 1);
945
946   set_path_visibility (fixture, "2", FALSE);
947   check_filter_model_with_root (fixture, path);
948   check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 1);
949
950   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0");
951   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0");
952   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0");
953   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0");
954   set_path_visibility (fixture, "2:0", FALSE);
955   set_path_visibility (fixture, "2:1", FALSE);
956   set_path_visibility (fixture, "2:2", FALSE);
957   set_path_visibility (fixture, "2:3", FALSE);
958   set_path_visibility (fixture, "2:4", FALSE);
959   check_filter_model_with_root (fixture, path);
960   check_level_length (fixture->filter, NULL, 0);
961
962   set_path_visibility (fixture, "2", TRUE);
963   check_filter_model_with_root (fixture, path);
964   check_level_length (fixture->filter, NULL, 0);
965
966   set_path_visibility (fixture, "1:4", FALSE);
967   check_filter_model_with_root (fixture, path);
968   check_level_length (fixture->filter, NULL, 0);
969
970   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0");
971   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
972   set_path_visibility (fixture, "2:4", TRUE);
973   check_filter_model_with_root (fixture, path);
974   check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 4);
975
976   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0");
977   set_path_visibility (fixture, "2:4", FALSE);
978   check_filter_model_with_root (fixture, path);
979   check_level_length (fixture->filter, NULL, 0);
980
981   set_path_visibility (fixture, "2", FALSE);
982   check_filter_model_with_root (fixture, path);
983   check_level_length (fixture->filter, NULL, 0);
984
985   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0");
986   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
987   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "1");
988   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "1");
989   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "2");
990   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2");
991   set_path_visibility (fixture, "2:0", TRUE);
992   set_path_visibility (fixture, "2:1", TRUE);
993   set_path_visibility (fixture, "2:2", TRUE);
994   check_filter_model_with_root (fixture, path);
995   check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 2);
996
997   set_path_visibility (fixture, "2", TRUE);
998   check_filter_model_with_root (fixture, path);
999   check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 2);
1000 }
1001
1002 static void
1003 filled_vroot_hide_child_levels (FilterTest    *fixture,
1004                                 gconstpointer  user_data)
1005 {
1006   GtkTreePath *path = (GtkTreePath *)user_data;
1007
1008   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0:2");
1009   set_path_visibility (fixture, "2:0:2", FALSE);
1010   check_filter_model_with_root (fixture, path);
1011   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1012   check_level_length (fixture->filter, "0", LEVEL_LENGTH - 1);
1013
1014   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0:3");
1015   set_path_visibility (fixture, "2:0:4", FALSE);
1016   check_filter_model_with_root (fixture, path);
1017   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1018   check_level_length (fixture->filter, "0", LEVEL_LENGTH - 2);
1019
1020   set_path_visibility (fixture, "2:0:4:3", FALSE);
1021   check_filter_model_with_root (fixture, path);
1022   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1023   check_level_length (fixture->filter, "0", LEVEL_LENGTH - 2);
1024
1025   set_path_visibility (fixture, "2:0:4:0", FALSE);
1026   set_path_visibility (fixture, "2:0:4:1", FALSE);
1027   set_path_visibility (fixture, "2:0:4:2", FALSE);
1028   set_path_visibility (fixture, "2:0:4:4", FALSE);
1029   check_filter_model_with_root (fixture, path);
1030   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1031   check_level_length (fixture->filter, "0", LEVEL_LENGTH - 2);
1032
1033   /* Since "0:2" is hidden, "0:4" must be "0:3" in the filter model */
1034   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:3");
1035   /* FIXME: Actually, the filter model should not be emitted the
1036    * row-has-child-toggled signal here.  *However* an extraneous emission
1037    * of this signal does not hurt and is allowed.
1038    */
1039   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:3");
1040   set_path_visibility (fixture, "2:0:4", TRUE);
1041   check_filter_model_with_root (fixture, path);
1042   check_level_length (fixture->filter, "0:3", 0);
1043
1044   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:2");
1045   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:2");
1046   set_path_visibility (fixture, "2:0:2", TRUE);
1047   check_filter_model_with_root (fixture, path);
1048   check_level_length (fixture->filter, "0:2", LEVEL_LENGTH);
1049   check_level_length (fixture->filter, "0:3", LEVEL_LENGTH);
1050   check_level_length (fixture->filter, "0:4", 0);
1051
1052   /* FIXME: Inconsistency!  For the non-vroot case we also receive two
1053    * row-has-child-toggled signals here.
1054    */
1055   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:4:0");
1056   /* Once 0:4:0 got inserted, 0:4 became a parent */
1057   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:4");
1058   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:4:1");
1059   set_path_visibility (fixture, "2:0:4:2", TRUE);
1060   set_path_visibility (fixture, "2:0:4:4", TRUE);
1061   check_level_length (fixture->filter, "0:4", 2);
1062 }
1063
1064
1065 static void
1066 empty_show_nodes (FilterTest    *fixture,
1067                   gconstpointer  user_data)
1068 {
1069   check_filter_model (fixture);
1070   check_level_length (fixture->filter, NULL, 0);
1071
1072   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0");
1073   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
1074   set_path_visibility (fixture, "3", TRUE);
1075   check_filter_model (fixture);
1076   check_level_length (fixture->filter, NULL, 1);
1077   check_level_length (fixture->filter, "0", 0);
1078
1079   set_path_visibility (fixture, "3:2:2", TRUE);
1080   check_filter_model (fixture);
1081   check_level_length (fixture->filter, NULL, 1);
1082   check_level_length (fixture->filter, "0", 0);
1083
1084   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:0");
1085   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
1086   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:0");
1087   set_path_visibility (fixture, "3:2", TRUE);
1088   check_filter_model (fixture);
1089   check_level_length (fixture->filter, NULL, 1);
1090   check_level_length (fixture->filter, "0", 1);
1091   check_level_length (fixture->filter, "0:0", 1);
1092   check_level_length (fixture->filter, "0:0:0", 0);
1093
1094   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0");
1095   set_path_visibility (fixture, "3", FALSE);
1096   check_filter_model (fixture);
1097   check_level_length (fixture->filter, NULL, 0);
1098
1099   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0");
1100   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
1101   set_path_visibility (fixture, "3:2:1", TRUE);
1102   set_path_visibility (fixture, "3", TRUE);
1103   check_filter_model (fixture);
1104   check_level_length (fixture->filter, NULL, 1);
1105   check_level_length (fixture->filter, "0", 1);
1106   check_level_length (fixture->filter, "0:0", 2);
1107   check_level_length (fixture->filter, "0:0:0", 0);
1108 }
1109
1110 static void
1111 empty_vroot_show_nodes (FilterTest    *fixture,
1112                         gconstpointer  user_data)
1113 {
1114   GtkTreePath *path = (GtkTreePath *)user_data;
1115
1116   check_filter_model_with_root (fixture, path);
1117   check_level_length (fixture->filter, NULL, 0);
1118
1119   set_path_visibility (fixture, "2", TRUE);
1120   check_filter_model_with_root (fixture, path);
1121   check_level_length (fixture->filter, NULL, 0);
1122
1123   set_path_visibility (fixture, "2:2:2", TRUE);
1124   check_filter_model_with_root (fixture, path);
1125   check_level_length (fixture->filter, NULL, 0);
1126
1127   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0");
1128   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
1129   set_path_visibility (fixture, "2:2", TRUE);
1130   check_filter_model_with_root (fixture, path);
1131   check_level_length (fixture->filter, NULL, 1);
1132   check_level_length (fixture->filter, "0", 1);
1133   check_level_length (fixture->filter, "0:0", 0);
1134
1135   set_path_visibility (fixture, "3", TRUE);
1136   check_filter_model_with_root (fixture, path);
1137   check_level_length (fixture->filter, NULL, 1);
1138
1139   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0");
1140   set_path_visibility (fixture, "2:2", FALSE);
1141   check_filter_model_with_root (fixture, path);
1142   check_level_length (fixture->filter, NULL, 0);
1143
1144   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0");
1145   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
1146   set_path_visibility (fixture, "2:2:1", TRUE);
1147   set_path_visibility (fixture, "2:2", TRUE);
1148   check_filter_model_with_root (fixture, path);
1149   check_level_length (fixture->filter, NULL, 1);
1150   check_level_length (fixture->filter, "0", 2);
1151   check_level_length (fixture->filter, "0:1", 0);
1152 }
1153
1154
1155 static void
1156 unfiltered_hide_single (FilterTest    *fixture,
1157                         gconstpointer  user_data)
1158
1159 {
1160   signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2");
1161   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2");
1162   set_path_visibility (fixture, "2", FALSE);
1163
1164   signal_monitor_assert_is_empty (fixture->monitor);
1165   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1166
1167   /* The view only shows the root level, so the filter model only has
1168    * the first two levels cached.
1169    */
1170   filter_test_append_refilter_signals (fixture, 2);
1171   filter_test_enable_filter (fixture);
1172
1173   check_filter_model (fixture);
1174   check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 1);
1175 }
1176
1177 static void
1178 unfiltered_hide_single_child (FilterTest    *fixture,
1179                               gconstpointer  user_data)
1180
1181 {
1182   signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2:2");
1183   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2");
1184   set_path_visibility (fixture, "2:2", FALSE);
1185
1186   signal_monitor_assert_is_empty (fixture->monitor);
1187   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1188   check_level_length (fixture->filter, "2", LEVEL_LENGTH);
1189
1190   /* The view only shows the root level, so the filter model only has
1191    * the first two levels cached.
1192    */
1193   filter_test_append_refilter_signals (fixture, 2);
1194   filter_test_enable_filter (fixture);
1195
1196   check_filter_model (fixture);
1197   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1198   check_level_length (fixture->filter, "2", LEVEL_LENGTH - 1);
1199 }
1200
1201 static void
1202 unfiltered_hide_single_multi_level (FilterTest    *fixture,
1203                                     gconstpointer  user_data)
1204
1205 {
1206   /* This row is not shown, so its signal is not propagated */
1207   set_path_visibility (fixture, "2:2:2", FALSE);
1208
1209   signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2:2");
1210   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2");
1211   set_path_visibility (fixture, "2:2", FALSE);
1212
1213   signal_monitor_assert_is_empty (fixture->monitor);
1214   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1215   check_level_length (fixture->filter, "2", LEVEL_LENGTH);
1216   check_level_length (fixture->filter, "2:2", LEVEL_LENGTH);
1217
1218   /* The view only shows the root level, so the filter model only has
1219    * the first two levels cached.
1220    */
1221   filter_test_append_refilter_signals (fixture, 2);
1222   filter_test_enable_filter (fixture);
1223
1224   check_filter_model (fixture);
1225   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1226   check_level_length (fixture->filter, "2", LEVEL_LENGTH - 1);
1227
1228   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "2:2");
1229   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2");
1230   set_path_visibility (fixture, "2:2", TRUE);
1231
1232   check_filter_model (fixture);
1233   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1234   check_level_length (fixture->filter, "2", LEVEL_LENGTH);
1235   check_level_length (fixture->filter, "2:2", LEVEL_LENGTH - 1);
1236 }
1237
1238
1239 static void
1240 unfiltered_vroot_hide_single (FilterTest    *fixture,
1241                               gconstpointer  user_data)
1242
1243 {
1244   GtkTreePath *path = (GtkTreePath *)user_data;
1245
1246   signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2");
1247   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2");
1248   set_path_visibility (fixture, "2:2", FALSE);
1249
1250   signal_monitor_assert_is_empty (fixture->monitor);
1251   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1252
1253   /* The view only shows the root level, so the filter model only has
1254    * the first two levels cached.  (We add an additional level to
1255    * take the virtual root into account).
1256    */
1257   filter_test_append_refilter_signals_with_vroot (fixture, 3, path);
1258   filter_test_enable_filter (fixture);
1259
1260   check_filter_model_with_root (fixture, path);
1261   check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 1);
1262 }
1263
1264 static void
1265 unfiltered_vroot_hide_single_child (FilterTest    *fixture,
1266                                     gconstpointer  user_data)
1267
1268 {
1269   GtkTreePath *path = (GtkTreePath *)user_data;
1270
1271   signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2:2");
1272   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2");
1273   set_path_visibility (fixture, "2:2:2", FALSE);
1274
1275   signal_monitor_assert_is_empty (fixture->monitor);
1276   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1277   check_level_length (fixture->filter, "2", LEVEL_LENGTH);
1278
1279   /* The view only shows the root level, so the filter model only has
1280    * the first two levels cached.  (We add an additional level to take
1281    * the virtual root into account).
1282    */
1283   filter_test_append_refilter_signals_with_vroot (fixture, 3, path);
1284   filter_test_enable_filter (fixture);
1285
1286   check_filter_model_with_root (fixture, path);
1287   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1288   check_level_length (fixture->filter, "2", LEVEL_LENGTH - 1);
1289 }
1290
1291 static void
1292 unfiltered_vroot_hide_single_multi_level (FilterTest    *fixture,
1293                                           gconstpointer  user_data)
1294
1295 {
1296   GtkTreePath *path = (GtkTreePath *)user_data;
1297
1298   /* This row is not shown, so its signal is not propagated */
1299   set_path_visibility (fixture, "2:2:2:2", FALSE);
1300
1301   signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2:2");
1302   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2");
1303   set_path_visibility (fixture, "2:2:2", FALSE);
1304
1305   signal_monitor_assert_is_empty (fixture->monitor);
1306   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1307   check_level_length (fixture->filter, "2", LEVEL_LENGTH);
1308   check_level_length (fixture->filter, "2:2", LEVEL_LENGTH);
1309
1310   /* The view only shows the root level, so the filter model only has
1311    * the first two levels cached.
1312    */
1313   filter_test_append_refilter_signals_with_vroot (fixture, 3, path);
1314   filter_test_enable_filter (fixture);
1315
1316   check_filter_model_with_root (fixture, path);
1317   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1318   check_level_length (fixture->filter, "2", LEVEL_LENGTH - 1);
1319
1320   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "2:2");
1321   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2");
1322   set_path_visibility (fixture, "2:2:2", TRUE);
1323
1324   check_filter_model_with_root (fixture, path);
1325   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1326   check_level_length (fixture->filter, "2", LEVEL_LENGTH);
1327   check_level_length (fixture->filter, "2:2", LEVEL_LENGTH - 1);
1328 }
1329
1330
1331
1332 static void
1333 unfiltered_show_single (FilterTest    *fixture,
1334                         gconstpointer  user_data)
1335
1336 {
1337   signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2");
1338   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2");
1339   set_path_visibility (fixture, "2", TRUE);
1340
1341   signal_monitor_assert_is_empty (fixture->monitor);
1342   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1343
1344   /* The view only shows the root level, so the filter model only has
1345    * the first two levels cached.
1346    */
1347   filter_test_append_refilter_signals (fixture, 2);
1348   filter_test_enable_filter (fixture);
1349
1350   check_filter_model (fixture);
1351   check_level_length (fixture->filter, NULL, 1);
1352 }
1353
1354 static void
1355 unfiltered_show_single_child (FilterTest    *fixture,
1356                               gconstpointer  user_data)
1357
1358 {
1359   signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2:2");
1360   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2");
1361   set_path_visibility (fixture, "2:2", TRUE);
1362
1363   signal_monitor_assert_is_empty (fixture->monitor);
1364   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1365   check_level_length (fixture->filter, "2", LEVEL_LENGTH);
1366
1367   /* The view only shows the root level, so the filter model only has
1368    * the first two levels cached.
1369    */
1370   filter_test_append_refilter_signals (fixture, 3);
1371   filter_test_enable_filter (fixture);
1372
1373   check_filter_model (fixture);
1374   check_level_length (fixture->filter, NULL, 0);
1375
1376   /* From here we are filtered, "2" in the real model is "0" in the filter
1377    * model.
1378    */
1379   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0");
1380   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
1381   set_path_visibility (fixture, "2", TRUE);
1382   signal_monitor_assert_is_empty (fixture->monitor);
1383   check_level_length (fixture->filter, NULL, 1);
1384   check_level_length (fixture->filter, "0", 1);
1385 }
1386
1387 static void
1388 unfiltered_show_single_multi_level (FilterTest    *fixture,
1389                                     gconstpointer  user_data)
1390
1391 {
1392   /* The view is not showing this row (collapsed state), so it is not
1393    * referenced.  The signal should not go through.
1394    */
1395   set_path_visibility (fixture, "2:2:2", TRUE);
1396
1397   signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2:2");
1398   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2");
1399   set_path_visibility (fixture, "2:2", TRUE);
1400
1401   signal_monitor_assert_is_empty (fixture->monitor);
1402   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1403   check_level_length (fixture->filter, "2", LEVEL_LENGTH);
1404   check_level_length (fixture->filter, "2:2", LEVEL_LENGTH);
1405
1406   /* The view only shows the root level, so the filter model only has
1407    * the first two levels cached.
1408    */
1409   filter_test_append_refilter_signals (fixture, 3);
1410   filter_test_enable_filter (fixture);
1411
1412   check_filter_model (fixture);
1413   check_level_length (fixture->filter, NULL, 0);
1414
1415   /* From here we are filtered, "2" in the real model is "0" in the filter
1416    * model.
1417    */
1418   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0");
1419   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
1420   set_path_visibility (fixture, "2", TRUE);
1421   check_filter_model (fixture);
1422   check_level_length (fixture->filter, NULL, 1);
1423   check_level_length (fixture->filter, "0", 1);
1424   check_level_length (fixture->filter, "0:0", 1);
1425 }
1426
1427
1428 static void
1429 unfiltered_vroot_show_single (FilterTest    *fixture,
1430                               gconstpointer  user_data)
1431
1432 {
1433   GtkTreePath *path = (GtkTreePath *)user_data;
1434
1435   signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2");
1436   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2");
1437   set_path_visibility (fixture, "2:2", TRUE);
1438
1439   signal_monitor_assert_is_empty (fixture->monitor);
1440   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1441
1442   /* The view only shows the root level, so the filter model only has
1443    * the first two levels cached.
1444    */
1445   filter_test_append_refilter_signals_with_vroot (fixture, 3, path);
1446   filter_test_enable_filter (fixture);
1447
1448   check_filter_model_with_root (fixture, path);
1449   check_level_length (fixture->filter, NULL, 1);
1450 }
1451
1452 static void
1453 unfiltered_vroot_show_single_child (FilterTest    *fixture,
1454                                     gconstpointer  user_data)
1455
1456 {
1457   GtkTreePath *path = (GtkTreePath *)user_data;
1458
1459   signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2:2");
1460   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2");
1461   set_path_visibility (fixture, "2:2:2", TRUE);
1462
1463   signal_monitor_assert_is_empty (fixture->monitor);
1464   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1465   check_level_length (fixture->filter, "2", LEVEL_LENGTH);
1466
1467   /* The view only shows the root level, so the filter model only has
1468    * the first two levels cached.
1469    */
1470   filter_test_append_refilter_signals_with_vroot (fixture, 2, path);
1471   filter_test_enable_filter (fixture);
1472
1473   check_filter_model_with_root (fixture, path);
1474   check_level_length (fixture->filter, NULL, 0);
1475
1476   /* From here we are filtered, "2" in the real model is "0" in the filter
1477    * model.
1478    */
1479   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0");
1480   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
1481   set_path_visibility (fixture, "2:2", TRUE);
1482   signal_monitor_assert_is_empty (fixture->monitor);
1483   check_level_length (fixture->filter, NULL, 1);
1484   check_level_length (fixture->filter, "0", 1);
1485 }
1486
1487 static void
1488 unfiltered_vroot_show_single_multi_level (FilterTest    *fixture,
1489                                           gconstpointer  user_data)
1490
1491 {
1492   GtkTreePath *path = (GtkTreePath *)user_data;
1493
1494   /* The view is not showing this row (collapsed state), so it is not
1495    * referenced.  The signal should not go through.
1496    */
1497   set_path_visibility (fixture, "2:2:2:2", TRUE);
1498
1499   signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2:2");
1500   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2");
1501   set_path_visibility (fixture, "2:2:2", TRUE);
1502
1503   signal_monitor_assert_is_empty (fixture->monitor);
1504   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1505   check_level_length (fixture->filter, "2", LEVEL_LENGTH);
1506   check_level_length (fixture->filter, "2:2", LEVEL_LENGTH);
1507
1508   /* The view only shows the root level, so the filter model only has
1509    * the first two levels cached.
1510    */
1511   filter_test_append_refilter_signals_with_vroot (fixture, 4, path);
1512   filter_test_enable_filter (fixture);
1513
1514   check_filter_model_with_root (fixture, path);
1515   check_level_length (fixture->filter, NULL, 0);
1516
1517   /* From here we are filtered, "2" in the real model is "0" in the filter
1518    * model.
1519    */
1520   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0");
1521   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
1522   set_path_visibility (fixture, "2:2", TRUE);
1523   check_filter_model_with_root (fixture, path);
1524   check_level_length (fixture->filter, NULL, 1);
1525   check_level_length (fixture->filter, "0", 1);
1526   check_level_length (fixture->filter, "0:0", 1);
1527 }
1528
1529
1530 static gboolean
1531 specific_path_dependent_filter_func (GtkTreeModel *model,
1532                                      GtkTreeIter  *iter,
1533                                      gpointer      data)
1534 {
1535   GtkTreePath *path;
1536
1537   path = gtk_tree_model_get_path (model, iter);
1538   if (gtk_tree_path_get_indices (path)[0] < 4)
1539     return FALSE;
1540
1541   return TRUE;
1542 }
1543
1544 static void
1545 specific_path_dependent_filter (void)
1546 {
1547   int i;
1548   GtkTreeIter iter;
1549   GtkListStore *list;
1550   GtkTreeModel *sort;
1551   GtkTreeModel *filter;
1552
1553   list = gtk_list_store_new (1, G_TYPE_INT);
1554   gtk_list_store_insert_with_values (list, &iter, 0, 0, 1, -1);
1555   gtk_list_store_insert_with_values (list, &iter, 1, 0, 2, -1);
1556   gtk_list_store_insert_with_values (list, &iter, 2, 0, 3, -1);
1557   gtk_list_store_insert_with_values (list, &iter, 3, 0, 4, -1);
1558   gtk_list_store_insert_with_values (list, &iter, 4, 0, 5, -1);
1559   gtk_list_store_insert_with_values (list, &iter, 5, 0, 6, -1);
1560   gtk_list_store_insert_with_values (list, &iter, 6, 0, 7, -1);
1561   gtk_list_store_insert_with_values (list, &iter, 7, 0, 8, -1);
1562
1563   sort = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (list));
1564   filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (sort), NULL);
1565   gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter),
1566                                           specific_path_dependent_filter_func,
1567                                           NULL, NULL);
1568
1569   gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sort), 0,
1570                                         GTK_SORT_DESCENDING);
1571
1572   for (i = 0; i < 4; i++)
1573     {
1574       if (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (list), &iter,
1575                                          NULL, 1))
1576         gtk_list_store_remove (list, &iter);
1577
1578       if (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (list), &iter,
1579                                          NULL, 2))
1580         gtk_list_store_remove (list, &iter);
1581     }
1582 }
1583
1584
1585 static gboolean
1586 specific_append_after_collapse_visible_func (GtkTreeModel *model,
1587                                              GtkTreeIter  *iter,
1588                                              gpointer      data)
1589 {
1590   gint number;
1591   gboolean hide_negative_numbers;
1592
1593   gtk_tree_model_get (model, iter, 1, &number, -1);
1594   hide_negative_numbers = GPOINTER_TO_INT (g_object_get_data (data, "private-hide-negative-numbers"));
1595
1596   return (number >= 0 || !hide_negative_numbers);
1597 }
1598
1599 static void
1600 specific_append_after_collapse (void)
1601 {
1602   /* This test is based on one of the test cases I found in my
1603    * old test cases directory.  I unfortunately do not have a record
1604    * from who this test case originated.  -Kris.
1605    *
1606    * General idea:
1607    * - Construct tree.
1608    * - Show tree, expand, collapse.
1609    * - Add a row.
1610    */
1611
1612   GtkTreeIter iter;
1613   GtkTreeIter child_iter;
1614   GtkTreeIter child_iter2;
1615   GtkTreePath *append_path;
1616   GtkTreeStore *store;
1617   GtkTreeModel *filter;
1618   GtkTreeModel *sort;
1619
1620   GtkWidget *window;
1621   GtkWidget *tree_view;
1622
1623   store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_INT);
1624
1625   filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (store), NULL);
1626   g_object_set_data (G_OBJECT (filter), "private-hide-negative-numbers",
1627                      GINT_TO_POINTER (FALSE));
1628   gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter),
1629                                           specific_append_after_collapse_visible_func,
1630                                           filter, NULL);
1631
1632   sort = gtk_tree_model_sort_new_with_model (filter);
1633
1634   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1635   tree_view = gtk_tree_view_new_with_model (sort);
1636   gtk_container_add (GTK_CONTAINER (window), tree_view);
1637   gtk_widget_realize (tree_view);
1638
1639   while (gtk_events_pending ())
1640     gtk_main_iteration ();
1641
1642   gtk_tree_store_prepend (store, &iter, NULL);
1643   gtk_tree_store_set (store, &iter,
1644                       0, "hallo", 1, 1, -1);
1645
1646   gtk_tree_store_append (store, &child_iter, &iter);
1647   gtk_tree_store_set (store, &child_iter,
1648                       0, "toemaar", 1, 1, -1);
1649
1650   gtk_tree_store_append (store, &child_iter2, &child_iter);
1651   gtk_tree_store_set (store, &child_iter2,
1652                       0, "very deep", 1, 1, -1);
1653
1654   append_path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &child_iter2);
1655
1656   gtk_tree_store_append (store, &child_iter, &iter);
1657   gtk_tree_store_set (store, &child_iter,
1658                       0, "sja", 1, 1, -1);
1659
1660   gtk_tree_store_append (store, &child_iter, &iter);
1661   gtk_tree_store_set (store, &child_iter,
1662                       0, "some word", 1, -1, -1);
1663
1664   /* Expand and collapse the tree */
1665   gtk_tree_view_expand_all (GTK_TREE_VIEW (tree_view));
1666   while (gtk_events_pending ())
1667     gtk_main_iteration ();
1668
1669   gtk_tree_view_collapse_all (GTK_TREE_VIEW (tree_view));
1670   while (gtk_events_pending ())
1671     gtk_main_iteration ();
1672
1673   /* Add another it */
1674   g_object_set_data (G_OBJECT (filter), "private-hide-negative-numbers",
1675                      GINT_TO_POINTER (TRUE));
1676
1677   if (gtk_tree_model_get_iter (GTK_TREE_MODEL (store), &iter, append_path))
1678     {
1679       gtk_tree_store_append (store, &child_iter, &iter);
1680       gtk_tree_store_set (store, &child_iter,
1681                           0, "new new new !!", 1, 1, -1);
1682     }
1683   gtk_tree_path_free (append_path);
1684
1685   /* Expand */
1686   gtk_tree_view_expand_all (GTK_TREE_VIEW (tree_view));
1687   while (gtk_events_pending ())
1688     gtk_main_iteration ();
1689 }
1690
1691
1692 static gint
1693 specific_sort_filter_remove_node_compare_func (GtkTreeModel  *model,
1694                                                GtkTreeIter   *iter1,
1695                                                GtkTreeIter   *iter2,
1696                                                gpointer       data)
1697 {
1698   return -1;
1699 }
1700
1701 static gboolean
1702 specific_sort_filter_remove_node_visible_func (GtkTreeModel  *model,
1703                                                GtkTreeIter   *iter,
1704                                                gpointer       data)
1705 {
1706   char *item = NULL;
1707
1708   /* Do reference the model */
1709   gtk_tree_model_get (model, iter, 0, &item, -1);
1710   g_free (item);
1711
1712   return FALSE;
1713 }
1714
1715 static void
1716 specific_sort_filter_remove_node (void)
1717 {
1718   /* This test is based on one of the test cases I found in my
1719    * old test cases directory.  I unfortunately do not have a record
1720    * from who this test case originated.  -Kris.
1721    *
1722    * General idea:
1723    *  - Create tree store, sort, filter models.  The sort model has
1724    *    a default sort func that is enabled, filter model a visible func
1725    *    that defaults to returning FALSE.
1726    *  - Remove a node from the tree store.
1727    */
1728
1729   GtkTreeIter iter;
1730   GtkTreeStore *store;
1731   GtkTreeModel *filter;
1732   GtkTreeModel *sort;
1733
1734   GtkWidget *window;
1735   GtkWidget *tree_view;
1736
1737   store = gtk_tree_store_new (1, G_TYPE_STRING);
1738   gtk_tree_store_append (store, &iter, NULL);
1739   gtk_tree_store_set (store, &iter, 0, "Hello1", -1);
1740
1741   gtk_tree_store_append (store, &iter, NULL);
1742   gtk_tree_store_set (store, &iter, 0, "Hello2", -1);
1743
1744   sort = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (store));
1745   gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (sort),
1746                                            specific_sort_filter_remove_node_compare_func, NULL, NULL);
1747
1748   filter = gtk_tree_model_filter_new (sort, NULL);
1749   gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter),
1750                                           specific_sort_filter_remove_node_visible_func,
1751                                           filter, NULL);
1752
1753
1754   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1755   tree_view = gtk_tree_view_new_with_model (filter);
1756   gtk_container_add (GTK_CONTAINER (window), tree_view);
1757   gtk_widget_realize (tree_view);
1758
1759   while (gtk_events_pending ())
1760     gtk_main_iteration ();
1761
1762   /* Remove a node */
1763   gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter);
1764   gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter);
1765   gtk_tree_store_remove (store, &iter);
1766
1767   while (gtk_events_pending ())
1768     gtk_main_iteration ();
1769 }
1770
1771
1772 static void
1773 specific_sort_filter_remove_root (void)
1774 {
1775   /* This test is based on one of the test cases I found in my
1776    * old test cases directory.  I unfortunately do not have a record
1777    * from who this test case originated.  -Kris.
1778    */
1779
1780   GtkTreeModel *model, *sort, *filter;
1781   GtkTreeIter root, mid, leaf;
1782   GtkTreePath *path;
1783
1784   model = GTK_TREE_MODEL (gtk_tree_store_new (1, G_TYPE_INT));
1785   gtk_tree_store_append (GTK_TREE_STORE (model), &root, NULL);
1786   gtk_tree_store_append (GTK_TREE_STORE (model), &mid, &root);
1787   gtk_tree_store_append (GTK_TREE_STORE (model), &leaf, &mid);
1788
1789   path = gtk_tree_model_get_path (model, &mid);
1790
1791   sort = gtk_tree_model_sort_new_with_model (model);
1792   filter = gtk_tree_model_filter_new (sort, path);
1793
1794   gtk_tree_store_remove (GTK_TREE_STORE (model), &root);
1795
1796   g_object_unref (filter);
1797   g_object_unref (sort);
1798   g_object_unref (model);
1799 }
1800
1801
1802 static void
1803 specific_root_mixed_visibility (void)
1804 {
1805   int i;
1806   GtkTreeModel *filter;
1807   /* A bit nasty, apologies */
1808   FilterTest fixture;
1809
1810   fixture.store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN);
1811
1812   for (i = 0; i < LEVEL_LENGTH; i++)
1813     {
1814       GtkTreeIter iter;
1815
1816       gtk_tree_store_insert (fixture.store, &iter, NULL, i);
1817       if (i % 2 == 0)
1818         create_tree_store_set_values (fixture.store, &iter, TRUE);
1819       else
1820         create_tree_store_set_values (fixture.store, &iter, FALSE);
1821     }
1822
1823   filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (fixture.store), NULL);
1824   fixture.filter = GTK_TREE_MODEL_FILTER (filter);
1825   fixture.monitor = NULL;
1826
1827   gtk_tree_model_filter_set_visible_column (fixture.filter, 1);
1828
1829   /* In order to trigger the potential bug, we should not access
1830    * the filter model here (so don't call the check functions).
1831    */
1832
1833   /* Change visibility of an odd row to TRUE */
1834   set_path_visibility (&fixture, "3", TRUE);
1835   check_filter_model (&fixture);
1836   check_level_length (fixture.filter, NULL, 4);
1837 }
1838
1839
1840
1841 static gboolean
1842 specific_has_child_filter_filter_func (GtkTreeModel *model,
1843                                        GtkTreeIter  *iter,
1844                                        gpointer      data)
1845 {
1846   return gtk_tree_model_iter_has_child (model, iter);
1847 }
1848
1849 static void
1850 specific_has_child_filter (void)
1851 {
1852   GtkTreeModel *filter;
1853   GtkTreeIter iter, root;
1854   /* A bit nasty, apologies */
1855   FilterTest fixture;
1856
1857   fixture.store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN);
1858   filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (fixture.store), NULL);
1859   fixture.filter = GTK_TREE_MODEL_FILTER (filter);
1860   fixture.monitor = NULL;
1861
1862   /* We will filter on parent state using a filter function.  We will
1863    * manually keep the boolean column in sync, so that we can use
1864    * check_filter_model() to check the consistency of the model.
1865    */
1866   /* FIXME: We need a check_filter_model() that is not tied to LEVEL_LENGTH
1867    * to be able to check the structure here.  We keep the calls to
1868    * check_filter_model() commented out until then.
1869    */
1870   gtk_tree_model_filter_set_visible_func (fixture.filter,
1871                                           specific_has_child_filter_filter_func,
1872                                           NULL, NULL);
1873
1874   gtk_tree_store_append (fixture.store, &root, NULL);
1875   create_tree_store_set_values (fixture.store, &root, FALSE);
1876
1877   /* check_filter_model (&fixture); */
1878   check_level_length (fixture.filter, NULL, 0);
1879
1880   gtk_tree_store_append (fixture.store, &iter, &root);
1881   create_tree_store_set_values (fixture.store, &iter, TRUE);
1882
1883   /* Parent must now be visible.  Do the level length check first,
1884    * to avoid modifying the child model triggering a row-changed to
1885    * the filter model.
1886    */
1887   check_level_length (fixture.filter, NULL, 1);
1888   check_level_length (fixture.filter, "0", 0);
1889
1890   set_path_visibility (&fixture, "0", TRUE);
1891   /* check_filter_model (&fixture); */
1892
1893   gtk_tree_store_append (fixture.store, &root, NULL);
1894   check_level_length (fixture.filter, NULL, 1);
1895
1896   gtk_tree_store_append (fixture.store, &iter, &root);
1897   check_level_length (fixture.filter, NULL, 2);
1898   check_level_length (fixture.filter, "1", 0);
1899
1900   create_tree_store_set_values (fixture.store, &root, TRUE);
1901   create_tree_store_set_values (fixture.store, &iter, TRUE);
1902
1903   /* check_filter_model (&fixture); */
1904
1905   gtk_tree_store_append (fixture.store, &iter, &root);
1906   create_tree_store_set_values (fixture.store, &iter, TRUE);
1907   check_level_length (fixture.filter, NULL, 2);
1908   check_level_length (fixture.filter, "0", 0);
1909   check_level_length (fixture.filter, "1", 0);
1910
1911   /* Now remove one of the remaining child rows */
1912   gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture.store),
1913                                        &iter, "0:0");
1914   gtk_tree_store_remove (fixture.store, &iter);
1915
1916   check_level_length (fixture.filter, NULL, 1);
1917   check_level_length (fixture.filter, "0", 0);
1918
1919   set_path_visibility (&fixture, "0", FALSE);
1920   /* check_filter_model (&fixture); */
1921 }
1922
1923
1924 static gboolean
1925 specific_root_has_child_filter_filter_func (GtkTreeModel *model,
1926                                             GtkTreeIter  *iter,
1927                                             gpointer      data)
1928 {
1929   int depth;
1930   GtkTreePath *path;
1931
1932   path = gtk_tree_model_get_path (model, iter);
1933   depth = gtk_tree_path_get_depth (path);
1934   gtk_tree_path_free (path);
1935
1936   if (depth > 1)
1937     return TRUE;
1938   /* else */
1939   return gtk_tree_model_iter_has_child (model, iter);
1940 }
1941
1942 static void
1943 specific_root_has_child_filter (void)
1944 {
1945   GtkTreeModel *filter;
1946   GtkTreeIter iter, root;
1947   /* A bit nasty, apologies */
1948   FilterTest fixture;
1949
1950   /* This is a variation on the above test case wherein the has-child
1951    * check for visibility only applies to root level nodes.
1952    */
1953
1954   fixture.store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN);
1955   filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (fixture.store), NULL);
1956   fixture.filter = GTK_TREE_MODEL_FILTER (filter);
1957   fixture.monitor = NULL;
1958
1959   /* We will filter on parent state using a filter function.  We will
1960    * manually keep the boolean column in sync, so that we can use
1961    * check_filter_model() to check the consistency of the model.
1962    */
1963   /* FIXME: We need a check_filter_model() that is not tied to LEVEL_LENGTH
1964    * to be able to check the structure here.  We keep the calls to
1965    * check_filter_model() commented out until then.
1966    */
1967   gtk_tree_model_filter_set_visible_func (fixture.filter,
1968                                           specific_root_has_child_filter_filter_func,
1969                                           NULL, NULL);
1970
1971   gtk_tree_store_append (fixture.store, &root, NULL);
1972   create_tree_store_set_values (fixture.store, &root, FALSE);
1973
1974   /* check_filter_model (&fixture); */
1975   check_level_length (fixture.filter, NULL, 0);
1976
1977   gtk_tree_store_append (fixture.store, &iter, &root);
1978   create_tree_store_set_values (fixture.store, &iter, TRUE);
1979
1980   /* Parent must now be visible.  Do the level length check first,
1981    * to avoid modifying the child model triggering a row-changed to
1982    * the filter model.
1983    */
1984   check_level_length (fixture.filter, NULL, 1);
1985   check_level_length (fixture.filter, "0", 1);
1986
1987   set_path_visibility (&fixture, "0", TRUE);
1988   /* check_filter_model (&fixture); */
1989
1990   gtk_tree_store_append (fixture.store, &root, NULL);
1991   check_level_length (fixture.filter, NULL, 1);
1992
1993   gtk_tree_store_append (fixture.store, &iter, &root);
1994   check_level_length (fixture.filter, NULL, 2);
1995   check_level_length (fixture.filter, "1", 1);
1996
1997   create_tree_store_set_values (fixture.store, &root, TRUE);
1998   create_tree_store_set_values (fixture.store, &iter, TRUE);
1999
2000   /* check_filter_model (&fixture); */
2001
2002   gtk_tree_store_append (fixture.store, &iter, &root);
2003   create_tree_store_set_values (fixture.store, &iter, TRUE);
2004   check_level_length (fixture.filter, NULL, 2);
2005   check_level_length (fixture.filter, "0", 1);
2006   check_level_length (fixture.filter, "1", 2);
2007
2008   /* Now remove one of the remaining child rows */
2009   gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture.store),
2010                                        &iter, "0:0");
2011   gtk_tree_store_remove (fixture.store, &iter);
2012
2013   check_level_length (fixture.filter, NULL, 1);
2014   check_level_length (fixture.filter, "0", 2);
2015
2016   set_path_visibility (&fixture, "0", FALSE);
2017   /* check_filter_model (&fixture); */
2018 }
2019
2020
2021 static void
2022 specific_filter_add_child (void)
2023 {
2024   /* This test is based on one of the test cases I found in my
2025    * old test cases directory.  I unfortunately do not have a record
2026    * from who this test case originated.  -Kris.
2027    */
2028
2029   GtkTreeIter iter;
2030   GtkTreeIter iter_first;
2031   GtkTreeIter child;
2032   GtkTreeStore *store;
2033   GtkTreeModel *filter;
2034
2035   store = gtk_tree_store_new (1, G_TYPE_STRING);
2036
2037   gtk_tree_store_append (store, &iter_first, NULL);
2038   gtk_tree_store_set (store, &iter_first, 0, "Hello", -1);
2039
2040   gtk_tree_store_append (store, &iter, NULL);
2041   gtk_tree_store_set (store, &iter, 0, "Hello", -1);
2042
2043   gtk_tree_store_append (store, &iter, NULL);
2044   gtk_tree_store_set (store, &iter, 0, "Hello", -1);
2045
2046   gtk_tree_store_append (store, &iter, NULL);
2047   gtk_tree_store_set (store, &iter, 0, "Hello", -1);
2048
2049   filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (store), NULL);
2050
2051   gtk_tree_store_set (store, &iter, 0, "Hello", -1);
2052   gtk_tree_store_append (store, &child, &iter_first);
2053   gtk_tree_store_set (store, &child, 0, "Hello", -1);
2054 }
2055
2056
2057 static void
2058 specific_bug_300089 (void)
2059 {
2060   /* Test case for GNOME Bugzilla bug 300089.  Written by
2061    * Matthias Clasen.
2062    */
2063   GtkTreeModel *sort_model, *child_model;
2064   GtkTreePath *path;
2065   GtkTreeIter iter, iter2, sort_iter;
2066
2067   child_model = GTK_TREE_MODEL (gtk_tree_store_new (1, G_TYPE_STRING));
2068
2069   gtk_tree_store_append (GTK_TREE_STORE (child_model), &iter, NULL);
2070   gtk_tree_store_set (GTK_TREE_STORE (child_model), &iter, 0, "A", -1);
2071   gtk_tree_store_append (GTK_TREE_STORE (child_model), &iter, NULL);
2072   gtk_tree_store_set (GTK_TREE_STORE (child_model), &iter, 0, "B", -1);
2073
2074   gtk_tree_store_append (GTK_TREE_STORE (child_model), &iter2, &iter);
2075   gtk_tree_store_set (GTK_TREE_STORE (child_model), &iter2, 0, "D", -1);
2076   gtk_tree_store_append (GTK_TREE_STORE (child_model), &iter2, &iter);
2077   gtk_tree_store_set (GTK_TREE_STORE (child_model), &iter2, 0, "E", -1);
2078
2079   gtk_tree_store_append (GTK_TREE_STORE (child_model), &iter, NULL);
2080   gtk_tree_store_set (GTK_TREE_STORE (child_model), &iter, 0, "C", -1);
2081
2082
2083   sort_model = GTK_TREE_MODEL (gtk_tree_model_sort_new_with_model (child_model));
2084   gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sort_model),
2085                                         0, GTK_SORT_ASCENDING);
2086
2087   path = gtk_tree_path_new_from_indices (1, 1, -1);
2088
2089   /* make sure a level is constructed */ 
2090   gtk_tree_model_get_iter (sort_model, &sort_iter, path);
2091
2092   /* change the "E" row in a way that causes it to change position */ 
2093   gtk_tree_model_get_iter (child_model, &iter, path);
2094   gtk_tree_store_set (GTK_TREE_STORE (child_model), &iter, 0, "A", -1);
2095 }
2096
2097
2098 static int
2099 specific_bug_301558_sort_func (GtkTreeModel *model,
2100                                GtkTreeIter  *a,
2101                                GtkTreeIter  *b,
2102                                gpointer      data)
2103 {
2104   int i, j;
2105
2106   gtk_tree_model_get (model, a, 0, &i, -1);
2107   gtk_tree_model_get (model, b, 0, &j, -1);
2108
2109   return j - i;
2110 }
2111
2112 static void
2113 specific_bug_301558 (void)
2114 {
2115   /* Test case for GNOME Bugzilla bug 301558 provided by
2116    * Markku Vire.
2117    */
2118   GtkTreeStore *tree;
2119   GtkTreeModel *filter;
2120   GtkTreeModel *sort;
2121   GtkTreeIter root, iter, iter2;
2122   GtkWidget *view;
2123   int i;
2124   gboolean add;
2125
2126   tree = gtk_tree_store_new (2, G_TYPE_INT, G_TYPE_BOOLEAN);
2127   gtk_tree_store_append (tree, &iter, NULL);
2128   gtk_tree_store_set (tree, &iter, 0, 123, 1, TRUE, -1);
2129   gtk_tree_store_append (tree, &iter2, &iter);
2130   gtk_tree_store_set (tree, &iter2, 0, 73, 1, TRUE, -1);
2131
2132   sort = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (tree));
2133   gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (sort),
2134                                            specific_bug_301558_sort_func,
2135                                            NULL, NULL);
2136
2137   filter = gtk_tree_model_filter_new (sort, NULL);
2138   gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (filter), 1);
2139
2140   view = gtk_tree_view_new_with_model (filter);
2141
2142   while (gtk_events_pending ())
2143     gtk_main_iteration ();
2144
2145   add = TRUE;
2146
2147   for (i = 0; i < 10; i++)
2148     {
2149       if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (tree), &root))
2150         g_assert_not_reached ();
2151
2152       if (add)
2153         {
2154           gtk_tree_store_append (tree, &iter, &root);
2155           gtk_tree_store_set (tree, &iter, 0, 456, 1, TRUE, -1);
2156         }
2157       else
2158         {
2159           int n;
2160           n = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (tree), &root);
2161           gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (tree), &iter,
2162                                          &root, n - 1);
2163           gtk_tree_store_remove (tree, &iter);
2164         }
2165
2166       add = !add;
2167     }
2168 }
2169
2170
2171 static gboolean
2172 specific_bug_311955_filter_func (GtkTreeModel *model,
2173                                  GtkTreeIter  *iter,
2174                                  gpointer      data)
2175 {
2176   int value;
2177
2178   gtk_tree_model_get (model, iter, 0, &value, -1);
2179
2180   return (value != 0);
2181 }
2182
2183 static void
2184 specific_bug_311955 (void)
2185 {
2186   /* This is a test case for GNOME Bugzilla bug 311955.  It was written
2187    * by Markku Vire.
2188    */
2189   GtkTreeIter iter, child, root;
2190   GtkTreeStore *store;
2191   GtkTreeModel *sort;
2192   GtkTreeModel *filter;
2193
2194   GtkWidget *window;
2195   GtkWidget *tree_view;
2196   int i;
2197   int n;
2198
2199   store = gtk_tree_store_new (1, G_TYPE_INT);
2200
2201   gtk_tree_store_append (store, &root, NULL);
2202   gtk_tree_store_set (store, &root, 0, 33, -1);
2203
2204   gtk_tree_store_append (store, &iter, &root);
2205   gtk_tree_store_set (store, &iter, 0, 50, -1);
2206
2207   gtk_tree_store_append (store, &iter, NULL);
2208   gtk_tree_store_set (store, &iter, 0, 22, -1);
2209
2210   sort = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (store));
2211   filter = gtk_tree_model_filter_new (sort, NULL);
2212
2213   gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter),
2214                                           specific_bug_311955_filter_func,
2215                                           NULL, NULL);
2216
2217   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2218   tree_view = gtk_tree_view_new_with_model (filter);
2219   g_object_unref (store);
2220
2221   gtk_tree_view_expand_all (GTK_TREE_VIEW (tree_view));
2222
2223   while (gtk_events_pending ())
2224     gtk_main_iteration ();
2225
2226   /* Fill model */
2227   for (i = 0; i < 4; i++)
2228     {
2229       gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &root);
2230
2231       gtk_tree_store_append (store, &iter, &root);
2232
2233       if (i < 3)
2234         gtk_tree_store_set (store, &iter, 0, i, -1);
2235
2236       if (i % 2 == 0)
2237         {
2238           gtk_tree_store_append (store, &child, &iter);
2239           gtk_tree_store_set (store, &child, 0, 10, -1);
2240         }
2241     }
2242
2243   while (gtk_events_pending ())
2244     gtk_main_iteration ();
2245
2246   /* Remove bottommost child from the tree. */
2247   gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &root);
2248   n = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (store), &root);
2249
2250   if (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (store), &iter, &root, n - 2))
2251     {
2252       if (gtk_tree_model_iter_children (GTK_TREE_MODEL (store), &child, &iter))
2253         gtk_tree_store_remove (store, &child);
2254     }
2255   else
2256     g_assert_not_reached ();
2257 }
2258
2259 static void
2260 specific_bug_346800 (void)
2261 {
2262   /* This is a test case for GNOME Bugzilla bug 346800.  It was written
2263    * by Jonathan Matthew.
2264    */
2265
2266   GtkTreeIter node_iters[50];
2267   GtkTreeIter child_iters[50];
2268   GtkTreeModel *model;
2269   GtkTreeModelFilter *filter;
2270   GtkTreeStore *store;
2271   GType *columns;
2272   int i;
2273   int items = 50;
2274   columns = g_new (GType, 2);
2275   columns[0] = G_TYPE_STRING;
2276   columns[1] = G_TYPE_BOOLEAN;
2277   store = gtk_tree_store_newv (2, columns);
2278   model = GTK_TREE_MODEL (store);
2279
2280   filter = GTK_TREE_MODEL_FILTER (gtk_tree_model_filter_new (model, NULL));
2281   gtk_tree_model_filter_set_visible_column (filter, 1);
2282
2283   for (i=0; i<items; i++)
2284     {
2285       /* allocate random amounts of junk, otherwise the filter model's arrays can expand without moving */
2286
2287       g_malloc (138);
2288       gtk_tree_store_append (store, &node_iters[i], NULL);
2289       gtk_tree_store_set (store, &node_iters[i],
2290                           0, "something",
2291                           1, ((i%6) == 0) ? FALSE : TRUE,
2292                           -1);
2293
2294       g_malloc (47);
2295       gtk_tree_store_append (store, &child_iters[i], &node_iters[i]);
2296       gtk_tree_store_set (store, &child_iters[i],
2297                           0, "something else",
2298                           1, FALSE,
2299                           -1);
2300       gtk_tree_model_filter_refilter (filter);
2301
2302       if (i > 6)
2303         {
2304           gtk_tree_store_set (GTK_TREE_STORE (model), &child_iters[i-1], 1,
2305                               (i & 1) ? TRUE : FALSE, -1);
2306           gtk_tree_model_filter_refilter (filter);
2307
2308           gtk_tree_store_set (GTK_TREE_STORE (model), &child_iters[i-2], 1,
2309                               (i & 1) ? FALSE: TRUE, -1);
2310           gtk_tree_model_filter_refilter (filter);
2311         }
2312     }
2313 }
2314
2315
2316 static void
2317 specific_bug_364946 (void)
2318 {
2319   /* This is a test case for GNOME Bugzilla bug 364946.  It was written
2320    * by Andreas Koehler.
2321    */
2322   GtkTreeStore *store;
2323   GtkTreeIter a, aa, aaa, aab, iter;
2324   GtkTreeModel *s_model;
2325
2326   store = gtk_tree_store_new (1, G_TYPE_STRING);
2327
2328   gtk_tree_store_append (store, &a, NULL);
2329   gtk_tree_store_set (store, &a, 0, "0", -1);
2330
2331   gtk_tree_store_append (store, &aa, &a);
2332   gtk_tree_store_set (store, &aa, 0, "0:0", -1);
2333
2334   gtk_tree_store_append (store, &aaa, &aa);
2335   gtk_tree_store_set (store, &aaa, 0, "0:0:0", -1);
2336
2337   gtk_tree_store_append (store, &aab, &aa);
2338   gtk_tree_store_set (store, &aab, 0, "0:0:1", -1);
2339
2340   s_model = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (store));
2341   gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (s_model), 0,
2342                                         GTK_SORT_ASCENDING);
2343
2344   gtk_tree_model_get_iter_from_string (s_model, &iter, "0:0:0");
2345
2346   gtk_tree_store_set (store, &aaa, 0, "0:0:0", -1);
2347   gtk_tree_store_remove (store, &aaa);
2348   gtk_tree_store_remove (store, &aab);
2349
2350   gtk_tree_model_sort_clear_cache (GTK_TREE_MODEL_SORT (s_model));
2351 }
2352
2353
2354 static gboolean
2355 specific_bug_464173_visible_func (GtkTreeModel *model,
2356                                   GtkTreeIter  *iter,
2357                                   gpointer      data)
2358 {
2359   gboolean *visible = (gboolean *)data;
2360
2361   return *visible;
2362 }
2363
2364 static void
2365 specific_bug_464173 (void)
2366 {
2367   /* Test case for GNOME Bugzilla bug 464173, test case written
2368    * by Andreas Koehler.
2369    */
2370   GtkTreeStore *model;
2371   GtkTreeModelFilter *f_model;
2372   GtkTreeIter iter1, iter2;
2373   GtkWidget *view;
2374   gboolean visible = TRUE;
2375
2376   model = gtk_tree_store_new (1, G_TYPE_STRING);
2377   gtk_tree_store_append (model, &iter1, NULL);
2378   gtk_tree_store_set (model, &iter1, 0, "Foo", -1);
2379   gtk_tree_store_append (model, &iter2, &iter1);
2380   gtk_tree_store_set (model, &iter2, 0, "Bar", -1);
2381
2382   f_model = GTK_TREE_MODEL_FILTER (gtk_tree_model_filter_new (GTK_TREE_MODEL(model), NULL));
2383   gtk_tree_model_filter_set_visible_func (f_model,
2384                                           specific_bug_464173_visible_func,
2385                                           &visible, NULL);
2386
2387   view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (f_model));
2388
2389   visible = FALSE;
2390   gtk_tree_model_filter_refilter (f_model);
2391 }
2392
2393
2394 static gboolean
2395 specific_bug_540201_filter_func (GtkTreeModel *model,
2396                                  GtkTreeIter  *iter,
2397                                  gpointer      data)
2398 {
2399   gboolean has_children;
2400
2401   has_children = gtk_tree_model_iter_has_child (model, iter);
2402
2403   return has_children;
2404 }
2405
2406 static void
2407 specific_bug_540201 (void)
2408 {
2409   /* Test case for GNOME Bugzilla bug 540201, steps provided by
2410    * Charles Day.
2411    */
2412   GtkTreeIter iter, root;
2413   GtkTreeStore *store;
2414   GtkTreeModel *filter;
2415
2416   GtkWidget *tree_view;
2417
2418   store = gtk_tree_store_new (1, G_TYPE_INT);
2419
2420   gtk_tree_store_append (store, &root, NULL);
2421   gtk_tree_store_set (store, &root, 0, 33, -1);
2422
2423   filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (store), NULL);
2424   tree_view = gtk_tree_view_new_with_model (filter);
2425
2426   gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter),
2427                                           specific_bug_540201_filter_func,
2428                                           NULL, NULL);
2429
2430   gtk_tree_store_append (store, &iter, &root);
2431   gtk_tree_store_set (store, &iter, 0, 50, -1);
2432
2433   gtk_tree_store_append (store, &iter, &root);
2434   gtk_tree_store_set (store, &iter, 0, 22, -1);
2435
2436
2437   gtk_tree_store_append (store, &root, NULL);
2438   gtk_tree_store_set (store, &root, 0, 33, -1);
2439
2440   gtk_tree_store_append (store, &iter, &root);
2441   gtk_tree_store_set (store, &iter, 0, 22, -1);
2442 }
2443
2444
2445 static gboolean
2446 specific_bug_549287_visible_func (GtkTreeModel *model,
2447                                   GtkTreeIter  *iter,
2448                                   gpointer      data)
2449 {
2450   gboolean result = FALSE;
2451
2452   result = gtk_tree_model_iter_has_child (model, iter);
2453
2454   return result;
2455 }
2456
2457 static void
2458 specific_bug_549287 (void)
2459 {
2460   /* Test case for GNOME Bugzilla bug 529287, provided by Julient Puydt */
2461
2462   int i;
2463   GtkTreeStore *store;
2464   GtkTreeModel *filtered;
2465   GtkWidget *view;
2466   GtkTreeIter iter;
2467   GtkTreeIter *swap, *parent, *child;
2468
2469   store = gtk_tree_store_new (1, G_TYPE_STRING);
2470   filtered = gtk_tree_model_filter_new (GTK_TREE_MODEL (store), NULL);
2471   gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filtered),
2472                                           specific_bug_549287_visible_func,
2473                                           NULL, NULL);
2474
2475   view = gtk_tree_view_new_with_model (filtered);
2476
2477   for (i = 0; i < 4; i++)
2478     {
2479       if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter))
2480         {
2481           parent = gtk_tree_iter_copy (&iter);
2482           child = gtk_tree_iter_copy (&iter);
2483
2484           while (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (store),
2485                                                 child, parent, 0))
2486             {
2487
2488               swap = parent;
2489               parent = child;
2490               child = swap;
2491             }
2492
2493           gtk_tree_store_append (store, child, parent);
2494           gtk_tree_store_set (store, child,
2495                               0, "Something",
2496                               -1);
2497
2498           gtk_tree_iter_free (parent);
2499           gtk_tree_iter_free (child);
2500         }
2501       else
2502         {
2503           gtk_tree_store_append (store, &iter, NULL);
2504           gtk_tree_store_set (store, &iter,
2505                               0, "Something",
2506                               -1);
2507         }
2508
2509       /* since we inserted something, we changed the visibility conditions: */
2510       gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filtered));
2511     }
2512 }
2513
2514 /* main */
2515
2516 int
2517 main (int    argc,
2518       char **argv)
2519 {
2520   gtk_test_init (&argc, &argv, NULL);
2521
2522   g_test_add ("/FilterModel/self/verify-test-suite",
2523               FilterTest, NULL,
2524               filter_test_setup,
2525               verify_test_suite,
2526               filter_test_teardown);
2527
2528   g_test_add ("/FilterModel/self/verify-test-suite/vroot/depth-1",
2529               FilterTest, gtk_tree_path_new_from_indices (2, -1),
2530               filter_test_setup,
2531               verify_test_suite_vroot,
2532               filter_test_teardown);
2533   g_test_add ("/FilterModel/self/verify-test-suite/vroot/depth-2",
2534               FilterTest, gtk_tree_path_new_from_indices (2, 3, -1),
2535               filter_test_setup,
2536               verify_test_suite_vroot,
2537               filter_test_teardown);
2538
2539
2540   g_test_add ("/FilterModel/filled/hide-root-level",
2541               FilterTest, NULL,
2542               filter_test_setup,
2543               filled_hide_root_level,
2544               filter_test_teardown);
2545   g_test_add ("/FilterModel/filled/hide-child-levels",
2546               FilterTest, NULL,
2547               filter_test_setup,
2548               filled_hide_child_levels,
2549               filter_test_teardown);
2550
2551   g_test_add ("/FilterModel/filled/hide-root-level/vroot",
2552               FilterTest, gtk_tree_path_new_from_indices (2, -1),
2553               filter_test_setup,
2554               filled_vroot_hide_root_level,
2555               filter_test_teardown);
2556   g_test_add ("/FilterModel/filled/hide-child-levels/vroot",
2557               FilterTest, gtk_tree_path_new_from_indices (2, -1),
2558               filter_test_setup,
2559               filled_vroot_hide_child_levels,
2560               filter_test_teardown);
2561
2562
2563   g_test_add ("/FilterModel/empty/show-nodes",
2564               FilterTest, NULL,
2565               filter_test_setup_empty,
2566               empty_show_nodes,
2567               filter_test_teardown);
2568
2569   g_test_add ("/FilterModel/empty/show-nodes/vroot",
2570               FilterTest, gtk_tree_path_new_from_indices (2, -1),
2571               filter_test_setup_empty,
2572               empty_vroot_show_nodes,
2573               filter_test_teardown);
2574
2575
2576   g_test_add ("/FilterModel/unfiltered/hide-single",
2577               FilterTest, NULL,
2578               filter_test_setup_unfiltered,
2579               unfiltered_hide_single,
2580               filter_test_teardown);
2581   g_test_add ("/FilterModel/unfiltered/hide-single-child",
2582               FilterTest, NULL,
2583               filter_test_setup_unfiltered,
2584               unfiltered_hide_single_child,
2585               filter_test_teardown);
2586   g_test_add ("/FilterModel/unfiltered/hide-single-multi-level",
2587               FilterTest, NULL,
2588               filter_test_setup_unfiltered,
2589               unfiltered_hide_single_multi_level,
2590               filter_test_teardown);
2591
2592   g_test_add ("/FilterModel/unfiltered/hide-single/vroot",
2593               FilterTest, gtk_tree_path_new_from_indices (2, -1),
2594               filter_test_setup_unfiltered,
2595               unfiltered_vroot_hide_single,
2596               filter_test_teardown);
2597   g_test_add ("/FilterModel/unfiltered/hide-single-child/vroot",
2598               FilterTest, gtk_tree_path_new_from_indices (2, -1),
2599               filter_test_setup_unfiltered,
2600               unfiltered_vroot_hide_single_child,
2601               filter_test_teardown);
2602   g_test_add ("/FilterModel/unfiltered/hide-single-multi-level/vroot",
2603               FilterTest, gtk_tree_path_new_from_indices (2, -1),
2604               filter_test_setup_unfiltered,
2605               unfiltered_vroot_hide_single_multi_level,
2606               filter_test_teardown);
2607
2608
2609
2610   g_test_add ("/FilterModel/unfiltered/show-single",
2611               FilterTest, NULL,
2612               filter_test_setup_empty_unfiltered,
2613               unfiltered_show_single,
2614               filter_test_teardown);
2615   g_test_add ("/FilterModel/unfiltered/show-single-child",
2616               FilterTest, NULL,
2617               filter_test_setup_empty_unfiltered,
2618               unfiltered_show_single_child,
2619               filter_test_teardown);
2620   g_test_add ("/FilterModel/unfiltered/show-single-multi-level",
2621               FilterTest, NULL,
2622               filter_test_setup_empty_unfiltered,
2623               unfiltered_show_single_multi_level,
2624               filter_test_teardown);
2625
2626   g_test_add ("/FilterModel/unfiltered/show-single/vroot",
2627               FilterTest, gtk_tree_path_new_from_indices (2, -1),
2628               filter_test_setup_empty_unfiltered,
2629               unfiltered_vroot_show_single,
2630               filter_test_teardown);
2631   g_test_add ("/FilterModel/unfiltered/show-single-child/vroot",
2632               FilterTest, gtk_tree_path_new_from_indices (2, -1),
2633               filter_test_setup_empty_unfiltered,
2634               unfiltered_vroot_show_single_child,
2635               filter_test_teardown);
2636   g_test_add ("/FilterModel/unfiltered/show-single-multi-level/vroot",
2637               FilterTest, gtk_tree_path_new_from_indices (2, -1),
2638               filter_test_setup_empty_unfiltered,
2639               unfiltered_vroot_show_single_multi_level,
2640               filter_test_teardown);
2641
2642
2643   g_test_add_func ("/FilterModel/specific/path-dependent-filter",
2644                    specific_path_dependent_filter);
2645   g_test_add_func ("/FilterModel/specific/append-after-collapse",
2646                    specific_append_after_collapse);
2647   g_test_add_func ("/FilterModel/specific/sort-filter-remove-node",
2648                    specific_sort_filter_remove_node);
2649   g_test_add_func ("/FilterModel/specific/sort-filter-remove-root",
2650                    specific_sort_filter_remove_root);
2651   g_test_add_func ("/FilterModel/specific/root-mixed-visibility",
2652                    specific_root_mixed_visibility);
2653   g_test_add_func ("/FilterModel/specific/has-child-filter",
2654                    specific_has_child_filter);
2655   g_test_add_func ("/FilterModel/specific/root-has-child-filter",
2656                    specific_root_has_child_filter);
2657   g_test_add_func ("/FilterModel/specific/filter-add-child",
2658                    specific_filter_add_child);
2659
2660   g_test_add_func ("/FilterModel/specific/bug-300089",
2661                    specific_bug_300089);
2662   g_test_add_func ("/FilterModel/specific/bug-301558",
2663                    specific_bug_301558);
2664   g_test_add_func ("/FilterModel/specific/bug-311955",
2665                    specific_bug_311955);
2666   g_test_add_func ("/FilterModel/specific/bug-346800",
2667                    specific_bug_346800);
2668   g_test_add_func ("/FilterModel/specific/bug-364946",
2669                    specific_bug_364946);
2670   g_test_add_func ("/FilterModel/specific/bug-464173",
2671                    specific_bug_464173);
2672   g_test_add_func ("/FilterModel/specific/bug-540201",
2673                    specific_bug_540201);
2674   g_test_add_func ("/FilterModel/specific/bug-549287",
2675                    specific_bug_549287);
2676
2677   return g_test_run ();
2678 }