]> Pileus Git - ~andy/gtk/blob - gtk/tests/treeview-scrolling.c
filechooserbutton: Do not propagate state from the dialog unless it is active
[~andy/gtk] / gtk / tests / treeview-scrolling.c
1 /* Scrolling test suite for GtkTreeView
2  * Copyright (C) 2006  Kristian Rietveld  <kris@gtk.org>
3  * Copyright (C) 2007  Imendio AB,  Kristian Rietveld
4  * Copyright (C) 2009  Kristian Rietveld  <kris@gtk.org>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 /* Original v1.0 -- December 26, 2006
21  * Conversion to GLib/GTK+ test framework during December, 2007
22  */
23
24
25 #include <gtk/gtk.h>
26 #include <unistd.h>
27 #include <math.h>
28
29 #define VIEW_WIDTH 320
30 #define VIEW_HEIGHT 240
31
32 #define N_ROWS 1000
33 #define BIG_N_ROWS N_ROWS * 100
34
35 /*
36  * To do:
37  *   - Test that nothing happens if the row is fully visible.
38  *   - The tests are dependent on the theme/font (size measurements,
39  *     chosen paths).
40  *   - Convert to proper GTK+ coding style.
41  *   - Briefly test scrolling in tree stores as well.
42  *
43  * Important:
44  *   - For tests with "mixed height" models, you must ensure that
45  *     there are only two heights used in total and that the rows with
46  *     height A and B are strictly alternating.  The model creation
47  *     functions already do this for you, but take this into account
48  *     when you write a unit test that adds rows to such a created
49  *     model, you must follow this rule otherwise things will break.
50  */
51
52
53 /* Constructing models for testing */
54 static GtkTreeModel *
55 create_model (gboolean constant)
56 {
57         int i;
58
59         GtkTreeIter iter;
60         GtkListStore *store;
61
62         store = gtk_list_store_new (1, G_TYPE_STRING);
63
64         for (i = 0; i < N_ROWS; i++) {
65                 gtk_list_store_append (store, &iter);
66                 if (constant || i % 2 == 0)
67                         gtk_list_store_set (store, &iter, 0, "Foo", -1);
68                 else
69                         gtk_list_store_set (store, &iter, 0, "Sliff\nSloff\nBleh", -1);
70         }
71
72         return GTK_TREE_MODEL (store);
73 }
74
75 static GtkTreeModel *
76 create_big_model (gboolean constant)
77 {
78         int i;
79
80         GtkTreeIter iter;
81         GtkListStore *store;
82
83         store = gtk_list_store_new (1, G_TYPE_STRING);
84
85         for (i = 0; i < BIG_N_ROWS; i++) {
86                 gtk_list_store_append (store, &iter);
87                 if (constant || i % 2 == 0)
88                         gtk_list_store_set (store, &iter, 0, "Foo", -1);
89                 else
90                         gtk_list_store_set (store, &iter, 0, "Sliff\nSloff\nBleh", -1);
91         }
92
93         return GTK_TREE_MODEL (store);
94 }
95
96 /*
97  * Fixtures
98  */
99
100 typedef struct
101 {
102         GtkWidget *window;
103         GtkWidget *tree_view;
104 }
105 ScrollFixture;
106
107 static void
108 scroll_fixture_setup (ScrollFixture *fixture,
109                       GtkTreeModel  *model,
110                       gconstpointer  test_data)
111 {
112         GtkWidget *sw;
113         GtkCellRenderer *renderer;
114         GtkTreeViewColumn *column;
115
116         fixture->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
117
118         sw = gtk_scrolled_window_new (NULL, NULL);
119         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
120                                         GTK_POLICY_NEVER,
121                                         GTK_POLICY_ALWAYS);
122         gtk_container_add (GTK_CONTAINER (fixture->window), sw);
123
124         fixture->tree_view = gtk_tree_view_new_with_model (model);
125         g_object_unref (model);
126         gtk_scrolled_window_set_min_content_width (GTK_SCROLLED_WINDOW (sw), VIEW_WIDTH);
127         gtk_scrolled_window_set_min_content_height (GTK_SCROLLED_WINDOW (sw), VIEW_HEIGHT);
128         gtk_widget_set_size_request (fixture->tree_view, VIEW_WIDTH, VIEW_HEIGHT);
129
130         renderer = gtk_cell_renderer_text_new ();
131         g_object_set (renderer, "editable", TRUE, NULL);
132         column = gtk_tree_view_column_new_with_attributes ("Title",
133                                                            renderer,
134                                                            "text", 0,
135                                                            NULL);
136
137         gtk_tree_view_append_column (GTK_TREE_VIEW (fixture->tree_view), column);
138         gtk_container_add (GTK_CONTAINER (sw), fixture->tree_view);
139 }
140
141 /* sets up a fixture with a model with constant row heights */
142 static void
143 scroll_fixture_constant_setup (ScrollFixture *fixture,
144                                gconstpointer  test_data)
145 {
146         scroll_fixture_setup (fixture, create_model (TRUE), test_data);
147 }
148
149 /* sets up a fixture with a model with varying row heights */
150 static void
151 scroll_fixture_mixed_setup (ScrollFixture *fixture,
152                             gconstpointer  test_data)
153 {
154         scroll_fixture_setup (fixture, create_model (FALSE), test_data);
155 }
156
157 /* sets up a fixture with a large model with constant row heights */
158 static void
159 scroll_fixture_constant_big_setup (ScrollFixture *fixture,
160                                    gconstpointer  test_data)
161 {
162         scroll_fixture_setup (fixture, create_big_model (TRUE), test_data);
163 }
164
165 /* sets up a fixture with a large model with varying row heights */
166 static void
167 scroll_fixture_mixed_big_setup (ScrollFixture *fixture,
168                                 gconstpointer  test_data)
169 {
170         scroll_fixture_setup (fixture, create_big_model (FALSE), test_data);
171 }
172
173 /* sets up a fixture with only a single row for the "single row scroll" test */
174 static void
175 scroll_fixture_single_setup (ScrollFixture *fixture,
176                              gconstpointer  test_data)
177 {
178         GtkTreeStore *store;
179         GtkTreeIter iter, child;
180
181         store = gtk_tree_store_new (1, G_TYPE_STRING);
182
183         gtk_tree_store_append (store, &iter, NULL);
184         gtk_tree_store_set (store, &iter, 0, "Foo", -1);
185
186         gtk_tree_store_append (store, &child, &iter);
187         gtk_tree_store_set (store, &child, 0, "Two\nLines", -1);
188
189         /* The teardown will also destroy the model */
190         scroll_fixture_setup (fixture, GTK_TREE_MODEL (store), test_data);
191 }
192
193 /* sets up a fixture with a tree store */
194 static void
195 scroll_fixture_tree_setup (ScrollFixture *fixture,
196                            gconstpointer   test_data)
197 {
198         GtkTreeStore *store;
199         GtkTreeIter iter, child;
200         int i;
201
202         store = gtk_tree_store_new (1, G_TYPE_STRING);
203
204         gtk_tree_store_append (store, &iter, NULL);
205         gtk_tree_store_set (store, &iter, 0, "Root node", -1);
206
207         for (i = 0; i < 5; i++) {
208                 gtk_tree_store_append (store, &child, &iter);
209                 gtk_tree_store_set (store, &child, 0, "Child node", -1);
210         }
211
212         for (i = 0; i < 5; i++) {
213                 gtk_tree_store_append (store, &iter, NULL);
214                 gtk_tree_store_set (store, &iter, 0, "Other node", -1);
215         }
216
217         /* The teardown will also destroy the model */
218         scroll_fixture_setup (fixture, GTK_TREE_MODEL (store), test_data);
219 }
220
221 static void
222 scroll_fixture_mixed_tree_setup (ScrollFixture *fixture,
223                                  gconstpointer   test_data)
224 {
225         GtkTreeStore *store;
226         GtkTreeIter iter, child;
227         int i;
228
229         store = gtk_tree_store_new (1, G_TYPE_STRING);
230
231         gtk_tree_store_append (store, &iter, NULL);
232         gtk_tree_store_set (store, &iter, 0, "Root\nnode", -1);
233
234         for (i = 0; i < 5; i++) {
235                 gtk_tree_store_append (store, &child, &iter);
236                 if (i % 2 != 0)
237                         gtk_tree_store_set (store, &child, 0, "Child node", -1);
238                 else
239                         gtk_tree_store_set (store, &child,
240                                             0, "Child\nnode", -1);
241         }
242
243         for (i = 0; i < 5; i++) {
244                 gtk_tree_store_append (store, &iter, NULL);
245                 if (i % 2 == 0)
246                         gtk_tree_store_set (store, &iter, 0, "Other node", -1);
247                 else
248                         gtk_tree_store_set (store, &iter, 0, "Other\nnode", -1);
249         }
250
251         /* The teardown will also destroy the model */
252         scroll_fixture_setup (fixture, GTK_TREE_MODEL (store), test_data);
253 }
254
255 static void
256 scroll_fixture_teardown (ScrollFixture *fixture,
257                          gconstpointer  test_data)
258 {
259         gtk_widget_destroy (fixture->window);
260 }
261
262 /*
263  * Position check and helpers.
264  */
265 enum Pos
266 {
267         POS_TOP,
268         POS_CENTER,
269         POS_BOTTOM
270 };
271
272 static int
273 get_row_start_for_index (GtkTreeView *tree_view, int index)
274 {
275         gint height1, height2;
276         gint row_start;
277         GtkTreePath *path;
278         GdkRectangle rect;
279
280         path = gtk_tree_path_new_from_indices (0, -1);
281         gtk_tree_view_get_background_area (tree_view, path, NULL, &rect);
282         height1 = rect.height;
283
284         gtk_tree_path_next (path);
285         gtk_tree_view_get_background_area (tree_view, path, NULL, &rect);
286         height2 = rect.height;
287         gtk_tree_path_free (path);
288
289         row_start = (index / 2) * height1 + (index / 2) * height2;
290         if (index % 2)
291                 row_start += height1;
292
293         return row_start;
294 }
295
296 static enum Pos
297 get_pos_from_path (GtkTreeView   *tree_view,
298                    GtkTreePath   *path,
299                    gdouble        row_height,
300                    GtkAdjustment *vadjustment)
301 {
302         int row_start;
303
304         row_start = get_row_start_for_index (tree_view,
305                                              gtk_tree_path_get_indices (path)[0]);
306
307         if (row_start + row_height < gtk_adjustment_get_page_size (vadjustment))
308                 return POS_TOP;
309
310         if (row_start >= gtk_adjustment_get_upper (vadjustment) - gtk_adjustment_get_page_size (vadjustment))
311                 return POS_BOTTOM;
312
313         return POS_CENTER;
314 }
315
316 static void
317 assert_position_with_align (GtkTreeView  *tree_view,
318                             enum Pos      pos,
319                             gint          row_y,
320                             gint          row_start,
321                             gdouble       row_height,
322                             gdouble       row_align)
323 {
324         GtkAdjustment *vadjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (tree_view));
325
326         /* Switch on row-align: 0.0, 0.5, 1.0 */
327         switch ((int)(row_align * 2.)) {
328         case 0:
329                 if (pos == POS_TOP || pos == POS_CENTER) {
330                         /* The row in question is the first row
331                          * in the view.
332                          *    - rect.y should be zero
333                          *    - dy should be equal to the top
334                          *      y coordinate of the row.
335                          */
336                         g_assert (row_y == 0);
337                         g_assert (gtk_adjustment_get_value (vadjustment) == row_start);
338                 } else {
339                         /* The row can be anywhere at the last
340                          * page of the tree view.
341                          *   - dy is set to the start of the
342                          *     last page.
343                          */
344                         g_assert (gtk_adjustment_get_value (vadjustment) == gtk_adjustment_get_upper (vadjustment) - gtk_adjustment_get_page_size (vadjustment));
345                 }
346                 break;
347
348         case 1:
349                 /* 0.5 */
350                 if (pos == POS_TOP
351                     && row_start < (gtk_adjustment_get_page_size (vadjustment) - row_height) / 2) {
352                         /* For the first half of the top view we can't
353                          * center the row in the view, instead we
354                          * show the first page.
355                          *   - dy should be zero
356                          */
357                         g_assert (gtk_adjustment_get_value (vadjustment) == 0);
358                 } else if (pos == POS_BOTTOM
359                            && row_start + row_height >= gtk_adjustment_get_upper (vadjustment) - (gtk_adjustment_get_page_size (vadjustment) - row_height) / 2) {
360                         /* For the last half of the bottom view we
361                          * can't center the row in the view, instead
362                          * we show the last page.
363                          *   - dy should be the start of the 
364                          *     last page.
365                          */
366                         g_assert (gtk_adjustment_get_value (vadjustment) == gtk_adjustment_get_upper (vadjustment) - gtk_adjustment_get_page_size (vadjustment));
367                 } else {
368                         /* The row is located in the middle of
369                          * the view.
370                          *    - top y coordinate is equal to
371                          *      middle of the view minus
372                          *      half the height of the row.
373                          *      (ie. the row's center is at the
374                          *       center of the view).
375                          */
376                         gdouble middle = (gtk_adjustment_get_page_size (vadjustment) - row_height) / 2.0;
377                         g_assert (row_y == ceil (middle) || row_y == floor (middle));
378                 }
379                 break;
380
381         case 2:
382                 /* 1.0 */
383                 if (pos == POS_TOP) {
384                         /* The row can be anywhere on the
385                          * first page of the tree view.
386                          *   - dy is zero.
387                          */
388                         g_assert (gtk_adjustment_get_value (vadjustment) == 0);
389                 } else if (pos == POS_CENTER || pos == POS_BOTTOM) {
390                         /* The row is the last row visible in the
391                          * view.
392                          *   - rect.y is set to the top of the
393                          *     last row.
394                          *   - row_start is greater than page_size
395                          *     (ie we are not on the first page).
396                          *   - dy is greater than zero
397                          */
398                         g_assert (row_start >= gtk_adjustment_get_page_size (vadjustment)
399                                   || row_start + row_height >= gtk_adjustment_get_page_size (vadjustment));
400                         g_assert (row_y == gtk_adjustment_get_page_size (vadjustment) - row_height);
401                 }
402                 break;
403         }
404 }
405
406 static void
407 assert_position_without_align (GtkTreeView *tree_view,
408                                gdouble      row_start,
409                                gdouble      row_height)
410 {
411   GtkAdjustment *vadjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (tree_view));
412
413   /* Without align the tree view does as less work as possible,
414    * so basically we only have to check whether the row
415    * is visible on the screen.
416    */
417   g_assert (gtk_adjustment_get_value (vadjustment) <= row_start);
418   g_assert (gtk_adjustment_get_value (vadjustment) + gtk_adjustment_get_page_size (vadjustment) >= row_start + row_height);
419 }
420
421 static void
422 test_position (GtkTreeView *tree_view,
423                GtkTreePath *path,
424                gboolean     use_align,
425                gdouble      row_align)
426 {
427         gint pos;
428         gchar *path_str;
429         GdkRectangle rect;
430         GtkTreeModel *model;
431         gint row_start;
432
433         /* Get the location of the path we scrolled to */
434         gtk_tree_view_get_background_area (GTK_TREE_VIEW (tree_view),
435                                            path, NULL, &rect);
436
437         row_start = get_row_start_for_index (GTK_TREE_VIEW (tree_view),
438                                              gtk_tree_path_get_indices (path)[0]);
439
440         /* Ugh */
441         pos = get_pos_from_path (GTK_TREE_VIEW (tree_view),
442                                  path, rect.height,
443                                  gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (tree_view)));
444
445         /* This is only tested for during test_single() */
446         model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree_view));
447         if (gtk_tree_model_iter_n_children (model, NULL) == 1) {
448                 GtkAllocation allocation;
449                 GtkTreePath *tmppath;
450
451                 /* Test nothing is dangling at the bottom; read
452                  * description for test_single() for more information.
453                  */
454
455                 /* FIXME: hardcoded width */
456                 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
457                 if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (tree_view), 0, allocation.height - 30, &tmppath, NULL, NULL, NULL)) {
458                         g_assert_not_reached ();
459                         gtk_tree_path_free (tmppath);
460                 }
461         }
462
463         path_str = gtk_tree_path_to_string (path);
464         if (use_align) {
465                 assert_position_with_align (tree_view, pos, rect.y,
466                                             row_start, rect.height, row_align);
467         } else {
468                 assert_position_without_align (tree_view, row_start, rect.height);
469         }
470
471         g_free (path_str);
472 }
473
474 /*
475  * Scrolling code
476  */
477
478
479 /* Testing scrolling to various positions with various alignments */
480
481 static void
482 ensure_layout (void)
483 {
484         /* HACK: sleep for more than one frame, to give the paint clock
485          * time to prepare the new layout */
486         g_usleep (100 * 1000);
487
488         while (gtk_events_pending ())
489                 gtk_main_iteration ();
490 }
491
492 static void
493 scroll (ScrollFixture *fixture,
494         GtkTreePath   *path,
495         gboolean       use_align,
496         gdouble        row_align)
497 {
498         gtk_tree_view_set_cursor (GTK_TREE_VIEW (fixture->tree_view), path,
499                                   NULL, FALSE);
500         gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (fixture->tree_view),
501                                       path, NULL,
502                                       use_align, row_align, 0.0);
503
504         gtk_widget_show_all (fixture->window);
505
506         ensure_layout ();
507         test_position (GTK_TREE_VIEW (fixture->tree_view), path,
508                        use_align, row_align);
509 }
510
511 static void
512 scroll_no_align (ScrollFixture *fixture,
513                  gconstpointer  test_data)
514 {
515         GtkTreePath *path;
516
517         path = gtk_tree_path_new_from_string (test_data);
518         scroll (fixture, path, FALSE, 0.0);
519         gtk_tree_path_free (path);
520 }
521
522 static void
523 scroll_align_0_0 (ScrollFixture *fixture,
524                   gconstpointer  test_data)
525 {
526         GtkTreePath *path;
527
528         path = gtk_tree_path_new_from_string (test_data);
529         scroll (fixture, path, TRUE, 0.0);
530         gtk_tree_path_free (path);
531 }
532
533 static void
534 scroll_align_0_5 (ScrollFixture *fixture,
535                   gconstpointer  test_data)
536 {
537         GtkTreePath *path;
538
539         path = gtk_tree_path_new_from_string (test_data);
540         scroll (fixture, path, TRUE, 0.5);
541         gtk_tree_path_free (path);
542 }
543
544 static void
545 scroll_align_1_0 (ScrollFixture *fixture,
546                   gconstpointer  test_data)
547 {
548         GtkTreePath *path;
549
550         path = gtk_tree_path_new_from_string (test_data);
551         scroll (fixture, path, TRUE, 1.0);
552         gtk_tree_path_free (path);
553 }
554
555
556 static void
557 scroll_after_realize (ScrollFixture *fixture,
558                       GtkTreePath   *path,
559                       gboolean       use_align,
560                       gdouble        row_align)
561 {
562         gtk_widget_show_all (fixture->window);
563
564         while (gtk_events_pending ())
565                 gtk_main_iteration ();
566
567         gtk_tree_view_set_cursor (GTK_TREE_VIEW (fixture->tree_view), path,
568                                   NULL, FALSE);
569         gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (fixture->tree_view),
570                                       path, NULL,
571                                       use_align, row_align, 0.0);
572
573         ensure_layout ();
574         test_position (GTK_TREE_VIEW (fixture->tree_view), path,
575                        use_align, row_align);
576 }
577
578 static void
579 scroll_after_no_align (ScrollFixture *fixture,
580                        gconstpointer  test_data)
581 {
582         GtkTreePath *path;
583
584         path = gtk_tree_path_new_from_string (test_data);
585         scroll_after_realize (fixture, path, FALSE, 0.0);
586         gtk_tree_path_free (path);
587 }
588
589 static void
590 scroll_after_align_0_0 (ScrollFixture *fixture,
591                         gconstpointer  test_data)
592 {
593         GtkTreePath *path;
594
595         path = gtk_tree_path_new_from_string (test_data);
596         scroll_after_realize (fixture, path, TRUE, 0.0);
597         gtk_tree_path_free (path);
598 }
599
600 static void
601 scroll_after_align_0_5 (ScrollFixture *fixture,
602                         gconstpointer  test_data)
603 {
604         GtkTreePath *path;
605
606         path = gtk_tree_path_new_from_string (test_data);
607         scroll_after_realize (fixture, path, TRUE, 0.5);
608         gtk_tree_path_free (path);
609 }
610
611 static void
612 scroll_after_align_1_0 (ScrollFixture *fixture,
613                         gconstpointer  test_data)
614 {
615         GtkTreePath *path;
616
617         path = gtk_tree_path_new_from_string (test_data);
618         scroll_after_realize (fixture, path, TRUE, 1.0);
619         gtk_tree_path_free (path);
620 }
621
622
623 static void
624 scroll_both_realize (ScrollFixture *fixture,
625                      GtkTreePath   *path,
626                      gboolean       use_align,
627                      gdouble        row_align)
628 {
629         GtkTreePath *end;
630
631         gtk_widget_show_all (fixture->window);
632
633         /* Scroll to end */
634         end = gtk_tree_path_new_from_indices (999, -1);
635
636         gtk_tree_view_set_cursor (GTK_TREE_VIEW (fixture->tree_view), end,
637                                   NULL, FALSE);
638         gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (fixture->tree_view),
639                                       end, NULL,
640                                       use_align, row_align, 0.0);
641         gtk_tree_path_free (end);
642
643         while (gtk_events_pending ())
644                 gtk_main_iteration ();
645
646         /* Scroll to final position */
647         gtk_tree_view_set_cursor (GTK_TREE_VIEW (fixture->tree_view), path,
648                                   NULL, FALSE);
649         gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (fixture->tree_view),
650                                       path, NULL,
651                                       use_align, row_align, 0.0);
652
653         ensure_layout ();
654         test_position (GTK_TREE_VIEW (fixture->tree_view), path,
655                        use_align, row_align);
656 }
657
658 static void
659 scroll_both_no_align (ScrollFixture *fixture,
660                       gconstpointer  test_data)
661 {
662         GtkTreePath *path;
663
664         path = gtk_tree_path_new_from_string (test_data);
665         scroll_both_realize (fixture, path, FALSE, 0.0);
666         gtk_tree_path_free (path);
667 }
668
669 static void
670 scroll_both_align_0_0 (ScrollFixture *fixture,
671                        gconstpointer  test_data)
672 {
673         GtkTreePath *path;
674
675         path = gtk_tree_path_new_from_string (test_data);
676         scroll_both_realize (fixture, path, TRUE, 0.0);
677         gtk_tree_path_free (path);
678 }
679
680 static void
681 scroll_both_align_0_5 (ScrollFixture *fixture,
682                        gconstpointer  test_data)
683 {
684         GtkTreePath *path;
685
686         path = gtk_tree_path_new_from_string (test_data);
687         scroll_both_realize (fixture, path, TRUE, 0.5);
688         gtk_tree_path_free (path);
689 }
690
691 static void
692 scroll_both_align_1_0 (ScrollFixture *fixture,
693                        gconstpointer  test_data)
694 {
695         GtkTreePath *path;
696
697         path = gtk_tree_path_new_from_string (test_data);
698         scroll_both_realize (fixture, path, TRUE, 1.0);
699         gtk_tree_path_free (path);
700 }
701
702 /* Testing scrolling to a newly created row */
703 static void
704 create_new_row (GtkListStore *store,
705                 int           n,
706                 GtkTreeIter  *iter)
707 {
708         switch (n) {
709                 case 0:
710                         /* Prepend a row */
711                         gtk_list_store_prepend (store, iter);
712                         break;
713
714                 case 3:
715                         /* Add a row in the middle of the visible area */
716                         gtk_list_store_insert (store, iter, 3);
717                         break;
718
719                 case 4:
720                         /* Add a row in the middle of the visible area */
721                         gtk_list_store_insert (store, iter, 4);
722                         break;
723
724                 case 5:
725                         /* Add a row which is not completely visible */
726                         gtk_list_store_insert (store, iter, 5);
727                         break;
728
729                 case 8:
730                         /* Add a row which is not completely visible */
731                         gtk_list_store_insert (store, iter, 8);
732                         break;
733
734                 case 500:
735                         /* Add a row in the middle */
736                         gtk_list_store_insert (store, iter, 500);
737                         break;
738
739                 case 999:
740                         /* Append a row */
741                         gtk_list_store_append (store, iter);
742                         break;
743         }
744
745         gtk_list_store_set (store, iter, 0, "New...", -1);
746 }
747
748 static void
749 scroll_new_row_editing_started (GtkCellRenderer *cell,
750                                 GtkCellEditable *editable,
751                                 const char      *path,
752                                 gpointer         user_data)
753 {
754         GtkWidget **widget = user_data;
755
756         *widget = GTK_WIDGET (editable);
757 }
758
759 static void
760 test_editable_position (GtkWidget   *tree_view,
761                         GtkWidget   *editable,
762                         GtkTreePath *cursor_path)
763 {
764         GtkAllocation allocation;
765         GdkRectangle rect;
766
767         gtk_tree_view_get_background_area (GTK_TREE_VIEW (tree_view),
768                                            cursor_path, NULL, &rect);
769
770         /* There are all in bin_window coordinates */
771         gtk_widget_get_allocation (editable, &allocation);
772         g_assert (allocation.y == rect.y + ((rect.height - allocation.height) / 2));
773 }
774
775 static void
776 scroll_new_row (ScrollFixture *fixture,
777                 gconstpointer  test_data)
778 {
779         GtkTreeIter scroll_iter;
780         GtkTreePath *scroll_path;
781         GtkTreeModel *model;
782         GList *renderers;
783         GtkTreeViewColumn *column;
784         GtkWidget *editable;
785
786         /* The aim of this test is creating a new row at several places,
787          * and immediately put the cursor on it.  TreeView should correctly
788          * scroll to the row and show the editable widget.
789          *
790          * See #81627.
791          */
792
793         g_test_bug ("81627");
794
795         gtk_widget_show_all (fixture->window);
796
797         while (gtk_events_pending ())
798                 gtk_main_iteration ();
799
800         /* Create the new row and scroll to it */
801         model = gtk_tree_view_get_model (GTK_TREE_VIEW (fixture->tree_view));
802         create_new_row (GTK_LIST_STORE (model), GPOINTER_TO_INT (test_data),
803                         &scroll_iter);
804
805         /* Set up a signal handler to acquire the editable widget */
806         column = gtk_tree_view_get_column (GTK_TREE_VIEW (fixture->tree_view), 0);
807         renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
808
809         g_signal_connect (G_OBJECT (renderers->data), "editing-started",
810                           G_CALLBACK (scroll_new_row_editing_started),
811                           &editable);
812
813         /* Now set the cursor on the path and start editing */
814         scroll_path = gtk_tree_model_get_path (model, &scroll_iter);
815         gtk_tree_view_set_cursor (GTK_TREE_VIEW (fixture->tree_view),
816                                   scroll_path,
817                                   column,
818                                   TRUE);
819
820         ensure_layout ();
821
822         /* Test position */
823         test_position (GTK_TREE_VIEW (fixture->tree_view), scroll_path,
824                        FALSE, 0.0);
825         test_editable_position (fixture->tree_view, editable, scroll_path);
826
827         gtk_tree_path_free (scroll_path);
828 }
829
830 static void
831 scroll_new_row_tree (ScrollFixture *fixture,
832                      gconstpointer  test_data)
833 {
834         GtkTreeModel *model;
835         GtkAdjustment *vadjustment;
836         int i;
837
838         /* The goal of this test is to append new rows at the end of a tree
839          * store and immediately scroll to them.  If there is a parent
840          * node with a couple of childs in the "area above" to explore,
841          * this used to lead to unexpected results due to a bug.
842          *
843          * This issue has been reported by Miroslav Rajcic on
844          * gtk-app-devel-list:
845          * http://mail.gnome.org/archives/gtk-app-devel-list/2008-December/msg00068.html
846          */
847
848         gtk_widget_show_all (fixture->window);
849
850         gtk_tree_view_expand_all (GTK_TREE_VIEW (fixture->tree_view));
851
852         while (gtk_events_pending ())
853                 gtk_main_iteration ();
854
855         model = gtk_tree_view_get_model (GTK_TREE_VIEW (fixture->tree_view));
856         vadjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (fixture->tree_view));
857
858         for (i = 0; i < 5; i++) {
859                 GtkTreeIter scroll_iter;
860                 GtkTreePath *scroll_path;
861
862                 gtk_tree_store_append (GTK_TREE_STORE (model), &scroll_iter,
863                                        NULL);
864                 gtk_tree_store_set (GTK_TREE_STORE (model), &scroll_iter,
865                                     0, "New node", -1);
866
867                 scroll_path = gtk_tree_model_get_path (model, &scroll_iter);
868                 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (fixture->tree_view),
869                                               scroll_path, NULL, FALSE, 0.0, 0.0);
870                 gtk_tree_path_free (scroll_path);
871
872                 ensure_layout ();
873
874                 /* Test position, the scroll bar must be at the end */
875                 g_assert (gtk_adjustment_get_value (vadjustment) == gtk_adjustment_get_upper (vadjustment) - gtk_adjustment_get_page_size (vadjustment));
876         }
877 }
878
879 /* Test for GNOME bugzilla bug 359231; tests "recovery when removing a bunch of
880  * rows at the bottom.
881  */
882 static void
883 test_bug316689 (ScrollFixture *fixture,
884                 gconstpointer  test_data)
885 {
886         GtkTreeIter iter;
887         GtkTreePath *path;
888         GtkAdjustment *vadjustment;
889         GtkTreeModel *model;
890
891         /* The aim of this test is to scroll to the bottom of a TreeView,
892          * remove at least one page_size of items and check if TreeView
893          * correctly corrects the scroll bar (else they will look "broken").
894          *
895          * See #316689.
896          */
897
898         g_test_bug ("316689");
899
900         /* Scroll to some place close to the end */
901         path = gtk_tree_path_new_from_indices (N_ROWS - 4, -1);
902         scroll (fixture, path, FALSE, 0.0);
903         gtk_tree_path_free (path);
904
905         /* No need for a while events pending loop here, scroll() does this for us.
906          *
907          * We now remove a bunch of rows, wait for events to process and then
908          * check the adjustments to see if the TreeView gracefully recovered.
909          */
910         model = gtk_tree_view_get_model (GTK_TREE_VIEW (fixture->tree_view));
911
912         while (gtk_tree_model_iter_nth_child (model, &iter, NULL, N_ROWS - 15))
913                 gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
914
915         while (gtk_events_pending ())
916                 gtk_main_iteration ();
917
918         vadjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (fixture->tree_view));
919
920         g_assert (gtk_adjustment_get_value (vadjustment) + gtk_adjustment_get_page_size (vadjustment) <= gtk_adjustment_get_upper (vadjustment));
921         g_assert (gtk_adjustment_get_value (vadjustment) == gtk_adjustment_get_upper (vadjustment) - gtk_adjustment_get_page_size (vadjustment));
922 }
923
924
925 /* Test for GNOME bugzilla bug 359231 */
926 static void
927 test_bug359231 (void)
928 {
929         int i;
930         int height1, height2;
931         GtkTreePath *path;
932         GtkTreeIter iter, child;
933         GtkTreeStore *store;
934         GdkRectangle rect;
935         ScrollFixture *fixture;
936
937         /* See #359231. */
938         g_test_bug ("359231");
939
940         /* Create model (GtkTreeStore in this case) */
941         store = gtk_tree_store_new (1, G_TYPE_STRING);
942
943         gtk_tree_store_append (store, &iter, NULL);
944         gtk_tree_store_set (store, &iter, 0, "Foo", -1);
945
946         for (i = 0; i < 4; i++) {
947                 gtk_tree_store_append (store, &child, &iter);
948                 gtk_tree_store_set (store, &child, 0, "Two\nLines", -1);
949         }
950         
951         fixture = g_new0 (ScrollFixture, 1);
952         scroll_fixture_setup (fixture, GTK_TREE_MODEL (store), NULL);
953         gtk_widget_show_all (fixture->window);
954
955         while (gtk_events_pending ())
956                 gtk_main_iteration ();
957
958         /* Prepend some rows at the top, expand */
959         gtk_tree_store_prepend (store, &iter, NULL);
960         gtk_tree_store_set (store, &iter, 0, "Foo", -1);
961
962         gtk_tree_store_prepend (store, &child, &iter);
963         gtk_tree_store_set (store, &child, 0, "Two\nLines", -1);
964
965         gtk_tree_view_expand_all (GTK_TREE_VIEW (fixture->tree_view));
966
967         while (gtk_events_pending ())
968                 gtk_main_iteration ();
969
970         /* Test if height of row 0:0 is correct */
971         path = gtk_tree_path_new_from_indices (0, -1);
972         gtk_tree_view_get_background_area (GTK_TREE_VIEW (fixture->tree_view),
973                                            path, NULL, &rect);
974         height1 = rect.height;
975
976         gtk_tree_path_down (path);
977         gtk_tree_view_get_background_area (GTK_TREE_VIEW (fixture->tree_view),
978                                            path, NULL, &rect);
979         height2 = rect.height;
980         gtk_tree_path_free (path);
981
982         g_assert (height2 > height1);
983
984         /* Clean up; the tear down also cleans up the model */
985         scroll_fixture_teardown (fixture, NULL);
986 }
987
988 /* Test for GNOME bugzilla bug 93584.  We add 150 rows to an existing
989  * small model, and scroll to one of these with alignment.
990  */
991 static void
992 test_bug93584 (ScrollFixture *fixture,
993                gconstpointer  test_data)
994 {
995         int row, i;
996         GtkTreeStore *store;
997         GtkTreePath *path;
998
999         g_test_bug ("93584");
1000
1001         /* Mimic state as in original test case */
1002         g_signal_connect (G_OBJECT (fixture->tree_view), "realize",
1003                           G_CALLBACK (gtk_tree_view_expand_all), NULL);
1004         gtk_widget_show_all (fixture->window);
1005
1006         store = GTK_TREE_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (fixture->tree_view)));
1007
1008         /* Add 150 rows */
1009         for (i = 0; i < 150; i++) {
1010                 GtkTreeIter iter;
1011
1012                 gtk_tree_store_append (store, &iter, NULL);
1013                 gtk_tree_store_set (store, &iter, 0, "Row", -1);
1014         }
1015
1016         row = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (store), NULL);
1017         row -= 20;
1018
1019         while (gtk_events_pending ())
1020                 gtk_main_iteration ();
1021
1022         path = gtk_tree_path_new_from_indices (row, -1);
1023         scroll (fixture, path, TRUE, 0.5);
1024         gtk_tree_path_free (path);
1025 }
1026
1027 /* GNOME bugzilla bug 111500.  Expand a row and immediately scroll
1028  * to its first child.  Make sure that expansion happens in currently
1029  * invisible area.
1030  */
1031 static void
1032 test_bug111500 (ScrollFixture *fixture,
1033                 gconstpointer  test_data)
1034 {
1035         int i, len;
1036         GtkTreeStore *store;
1037         GtkTreeIter parent;
1038         GtkTreePath *path;
1039
1040         g_test_bug ("111500");
1041
1042         gtk_widget_show_all (fixture->window);
1043
1044         /* Make sure all events have been processed and the window
1045          * is visible.
1046          */
1047         while (gtk_events_pending ())
1048                 gtk_main_iteration ();
1049
1050         /* Further prepare model */
1051         store = GTK_TREE_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (fixture->tree_view)));
1052
1053         for (i = 0; i < 15; i++) {
1054                 GtkTreeIter iter;
1055
1056                 gtk_tree_store_append (store, &iter, NULL);
1057                 gtk_tree_store_set (store, &iter, 0, "Other node", -1);
1058         }
1059
1060         len = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (store), NULL);
1061         gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (store), &parent,
1062                                        NULL, len - 1);
1063
1064         for (i = 0; i < 5; i++) {
1065                 GtkTreeIter iter;
1066
1067                 gtk_tree_store_append (store, &iter, &parent);
1068                 gtk_tree_store_set (store, &iter, 0, "Row", -1);
1069         }
1070
1071         path = gtk_tree_path_new_from_indices (len - 1, -1);
1072         gtk_tree_view_expand_row (GTK_TREE_VIEW (fixture->tree_view),
1073                                   path, FALSE);
1074
1075         gtk_tree_path_down (path);
1076
1077         scroll (fixture, path, FALSE, 0.5);
1078         gtk_tree_path_free (path);
1079 }
1080
1081 static void
1082 test_bug111500_mixed (ScrollFixture *fixture,
1083                       gconstpointer  test_data)
1084 {
1085         int i, len;
1086         GtkTreeStore *store;
1087         GtkTreeIter parent;
1088         GtkTreePath *path;
1089
1090         g_test_bug ("111500");
1091
1092         gtk_widget_show_all (fixture->window);
1093
1094         /* Make sure all events have been processed and the window
1095          * is visible.
1096          */
1097         while (gtk_events_pending ())
1098                 gtk_main_iteration ();
1099
1100         /* Further prepare model */
1101         store = GTK_TREE_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (fixture->tree_view)));
1102
1103         for (i = 0; i < 15; i++) {
1104                 GtkTreeIter iter;
1105
1106                 gtk_tree_store_append (store, &iter, NULL);
1107                 if (i % 2 == 0)
1108                         gtk_tree_store_set (store, &iter, 0, "Other node", -1);
1109                 else
1110                         gtk_tree_store_set (store, &iter, 0, "Other\nnode", -1);
1111         }
1112
1113         len = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (store), NULL);
1114         gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (store), &parent,
1115                                        NULL, len - 1);
1116
1117         for (i = 0; i < 5; i++) {
1118                 GtkTreeIter iter;
1119
1120                 gtk_tree_store_append (store, &iter, &parent);
1121                 if (i % 2 != 0)
1122                         gtk_tree_store_set (store, &iter, 0, "Row", -1);
1123                 else
1124                         gtk_tree_store_set (store, &iter, 0, "Row\nRow", -1);
1125         }
1126
1127         path = gtk_tree_path_new_from_indices (len - 1, -1);
1128         gtk_tree_view_expand_row (GTK_TREE_VIEW (fixture->tree_view),
1129                                   path, FALSE);
1130
1131         gtk_tree_path_down (path);
1132
1133         scroll (fixture, path, FALSE, 0.5);
1134         gtk_tree_path_free (path);
1135 }
1136
1137 /* Test for GNOME bugzilla bug 163214.  Invalidate a couple of rows,
1138  * then scroll to one of these.
1139  */
1140 static void
1141 test_bug163214 (ScrollFixture *fixture,
1142                 gconstpointer  test_data)
1143 {
1144         int i;
1145         GtkListStore *store;
1146         GtkTreePath *path;
1147
1148         g_test_bug ("163214");
1149
1150         gtk_widget_show_all (fixture->window);
1151
1152         /* Make sure all events have been processed and the window
1153          * is visible.
1154          */
1155         while (gtk_events_pending ())
1156                 gtk_main_iteration ();
1157
1158         store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (fixture->tree_view)));
1159
1160         /* Invalidate a page of rows */
1161         for (i = 100; i < 110; i++) {
1162                 GtkTreeIter iter;
1163
1164                 gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (store), &iter,
1165                                                NULL, i);
1166                 gtk_list_store_set (store, &iter, 0, "Row", -1);
1167         }
1168
1169         /* Then scroll to that page. */
1170         path = gtk_tree_path_new_from_indices (105, -1);
1171         scroll (fixture, path, TRUE, 0.5);
1172         gtk_tree_path_free (path);
1173
1174         /* Make sure all events have been processed and the window
1175          * is visible.
1176          */
1177         while (gtk_events_pending ())
1178                 gtk_main_iteration ();
1179
1180         store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (fixture->tree_view)));
1181
1182         /* Invalidate a page of rows */
1183         for (i = 300; i < 310; i++) {
1184                 GtkTreeIter iter;
1185
1186                 gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (store), &iter,
1187                                                NULL, i);
1188                 gtk_list_store_set (store, &iter, 0, "Row", -1);
1189         }
1190
1191         /* Then scroll to the first row */
1192         path = gtk_tree_path_new_from_indices (0, -1);
1193         scroll (fixture, path, TRUE, 0.5);
1194         gtk_tree_path_free (path);
1195 }
1196
1197 /* Infrastructure for automatically adding tests */
1198 enum
1199 {
1200         BEFORE,
1201         AFTER,
1202         BOTH
1203 };
1204
1205 static const char *
1206 test_type_string (int test_type)
1207 {
1208         switch (test_type) {
1209                 case BEFORE:
1210                         return "before-realize";
1211
1212                 case AFTER:
1213                         return "after-realize";
1214
1215                 case BOTH:
1216                         return "both";
1217         }
1218
1219         return "???";
1220 }
1221
1222 static char *
1223 align_string (gboolean use_align,
1224               gdouble  row_align)
1225 {
1226         char *ret;
1227
1228         if (!use_align)
1229                 return g_strdup ("no-align");
1230
1231         ret = g_strdup_printf ("align-%1.1f", row_align);
1232         return ret;
1233 }
1234
1235 static void
1236 add_test (const char *path,
1237           gboolean    mixed,
1238           int         test_type,
1239           gboolean    use_align,
1240           gdouble     row_align,
1241           void (* setup) (ScrollFixture *, gconstpointer),
1242           void (* scroll_func) (ScrollFixture *, gconstpointer))
1243 {
1244         gchar *test_path;
1245         gchar *align;
1246
1247         align = align_string (use_align, row_align);
1248
1249         test_path = g_strdup_printf ("/TreeView/scrolling/%s/%s-height/path-%s-%s",
1250                                      test_type_string (test_type),
1251                                      mixed ? "mixed" : "constant",
1252                                      path, align);
1253         g_free (align);
1254
1255         g_test_add (test_path, ScrollFixture, path,
1256                     setup, scroll_func, scroll_fixture_teardown);
1257
1258         g_free (test_path);
1259 }
1260
1261 static void
1262 add_tests (gboolean mixed,
1263            int test_type,
1264            gboolean use_align,
1265            gdouble  row_align,
1266            void (*scroll_func) (ScrollFixture *, gconstpointer))
1267 {
1268         void (* setup) (ScrollFixture *, gconstpointer);
1269
1270         if (mixed)
1271                 setup = scroll_fixture_mixed_setup;
1272         else
1273                 setup = scroll_fixture_constant_setup;
1274
1275         add_test ("0", mixed, test_type, use_align, row_align, setup, scroll_func);
1276         add_test ("2", mixed, test_type, use_align, row_align, setup, scroll_func);
1277         add_test ("5", mixed, test_type, use_align, row_align, setup, scroll_func);
1278         /* We scroll to 8 to test a partial visible row.  The 8 is
1279          * based on my font setting of "Vera Sans 11" and
1280          * the separators set to 0.  (This should be made dynamic; FIXME).
1281          */
1282         add_test ("8", mixed, test_type, use_align, row_align, setup, scroll_func);
1283         add_test ("10", mixed, test_type, use_align, row_align, setup, scroll_func);
1284         add_test ("250", mixed, test_type, use_align, row_align, setup, scroll_func);
1285         add_test ("500", mixed, test_type, use_align, row_align, setup, scroll_func);
1286         add_test ("750", mixed, test_type, use_align, row_align, setup, scroll_func);
1287         add_test ("990", mixed, test_type, use_align, row_align, setup, scroll_func);
1288         add_test ("991", mixed, test_type, use_align, row_align, setup, scroll_func);
1289         add_test ("995", mixed, test_type, use_align, row_align, setup, scroll_func);
1290         add_test ("997", mixed, test_type, use_align, row_align, setup, scroll_func);
1291         add_test ("999", mixed, test_type, use_align, row_align, setup, scroll_func);
1292 }
1293
1294 int
1295 main (int argc, char **argv)
1296 {
1297         gtk_test_init (&argc, &argv);
1298
1299         /* Scrolls before realization */
1300         add_tests (FALSE, BEFORE, FALSE, 0.0, scroll_no_align);
1301         if (g_test_thorough ())
1302                 add_tests (TRUE, BEFORE, FALSE, 0.0, scroll_no_align);
1303
1304         add_tests (FALSE, BEFORE, TRUE, 0.0, scroll_align_0_0);
1305         if (g_test_thorough ())
1306                 add_tests (TRUE, BEFORE, TRUE, 0.0, scroll_align_0_0);
1307
1308         add_tests (FALSE, BEFORE, TRUE, 0.5, scroll_align_0_5);
1309         if (g_test_thorough ())
1310                 add_tests (TRUE, BEFORE, TRUE, 0.5, scroll_align_0_5);
1311
1312         add_tests (FALSE, BEFORE, TRUE, 1.0, scroll_align_1_0);
1313         if (g_test_thorough ())
1314                 add_tests (TRUE, BEFORE, TRUE, 1.0, scroll_align_1_0);
1315
1316         /* Scrolls after realization */
1317         add_tests (FALSE, AFTER, FALSE, 0.0, scroll_after_no_align);
1318         if (g_test_thorough ())
1319                 add_tests (TRUE, AFTER, FALSE, 0.0, scroll_after_no_align);
1320
1321         add_tests (FALSE, AFTER, TRUE, 0.0, scroll_after_align_0_0);
1322         if (g_test_thorough ())
1323                 add_tests (TRUE, AFTER, TRUE, 0.0, scroll_after_align_0_0);
1324
1325         add_tests (FALSE, AFTER, TRUE, 0.5, scroll_after_align_0_5);
1326         if (g_test_thorough ())
1327                 add_tests (TRUE, AFTER, TRUE, 0.5, scroll_after_align_0_5);
1328
1329         add_tests (FALSE, AFTER, TRUE, 1.0, scroll_after_align_1_0);
1330         if (g_test_thorough ())
1331                 add_tests (TRUE, AFTER, TRUE, 1.0, scroll_after_align_1_0);
1332
1333         /* Scroll to end before realization, to a real position after */
1334         if (g_test_thorough ()) {
1335                 add_tests (FALSE, BOTH, FALSE, 0.0, scroll_both_no_align);
1336                 add_tests (TRUE, BOTH, FALSE, 0.0, scroll_both_no_align);
1337
1338                 add_tests (FALSE, BOTH, TRUE, 0.0, scroll_both_align_0_0);
1339                 add_tests (TRUE, BOTH, TRUE, 0.0, scroll_both_align_0_0);
1340
1341                 add_tests (FALSE, BOTH, TRUE, 0.5, scroll_both_align_0_5);
1342                 add_tests (TRUE, BOTH, TRUE, 0.5, scroll_both_align_0_5);
1343
1344                 add_tests (FALSE, BOTH, TRUE, 1.0, scroll_both_align_1_0);
1345                 add_tests (TRUE, BOTH, TRUE, 1.0, scroll_both_align_1_0);
1346         }
1347
1348         /* Test different alignments in view with single row */
1349         g_test_add ("/TreeView/scrolling/single-row/no-align",
1350                     ScrollFixture, "0",
1351                     scroll_fixture_single_setup,
1352                     scroll_no_align,
1353                     scroll_fixture_teardown);
1354         g_test_add ("/TreeView/scrolling/single-row/align-0.0",
1355                     ScrollFixture, "0",
1356                     scroll_fixture_single_setup,
1357                     scroll_align_0_0,
1358                     scroll_fixture_teardown);
1359         g_test_add ("/TreeView/scrolling/single-row/align-0.5",
1360                     ScrollFixture, "0",
1361                     scroll_fixture_single_setup,
1362                     scroll_align_0_5,
1363                     scroll_fixture_teardown);
1364         g_test_add ("/TreeView/scrolling/single-row/align-1.0",
1365                     ScrollFixture, "0",
1366                     scroll_fixture_single_setup,
1367                     scroll_align_1_0,
1368                     scroll_fixture_teardown);
1369
1370         /* Test scrolling in a very large model; also very slow */
1371         if (g_test_slow ()) {
1372                 g_test_add ("/TreeView/scrolling/large-model/constant-height/middle-no-align",
1373                             ScrollFixture, "50000",
1374                             scroll_fixture_constant_big_setup,
1375                             scroll_no_align,
1376                             scroll_fixture_teardown);
1377                 g_test_add ("/TreeView/scrolling/large-model/constant-height/end-no-align",
1378                             ScrollFixture, "99999",
1379                             scroll_fixture_constant_big_setup,
1380                             scroll_no_align,
1381                             scroll_fixture_teardown);
1382
1383                 g_test_add ("/TreeView/scrolling/large-model/mixed-height/middle-no-align",
1384                             ScrollFixture, "50000",
1385                             scroll_fixture_mixed_big_setup,
1386                             scroll_no_align,
1387                             scroll_fixture_teardown);
1388                 g_test_add ("/TreeView/scrolling/large-model/mixed-height/end-no-align",
1389                             ScrollFixture, "99999",
1390                             scroll_fixture_mixed_big_setup,
1391                             scroll_no_align,
1392                             scroll_fixture_teardown);
1393         }
1394
1395         /* Test scrolling to a newly created row */
1396         g_test_add ("/TreeView/scrolling/new-row/path-0", ScrollFixture,
1397                     GINT_TO_POINTER (0),
1398                     scroll_fixture_constant_setup,
1399                     scroll_new_row,
1400                     scroll_fixture_teardown);
1401         g_test_add ("/TreeView/scrolling/new-row/path-4", ScrollFixture,
1402                     GINT_TO_POINTER (4),
1403                     scroll_fixture_constant_setup,
1404                     scroll_new_row,
1405                     scroll_fixture_teardown);
1406         /* We scroll to 8 to test a partial visible row.  The 8 is
1407          * based on my font setting of "Vera Sans 11" and
1408          * the separators set to 0.  (This should be made dynamic; FIXME).
1409          */
1410         g_test_add ("/TreeView/scrolling/new-row/path-8", ScrollFixture,
1411                     GINT_TO_POINTER (8),
1412                     scroll_fixture_constant_setup,
1413                     scroll_new_row,
1414                     scroll_fixture_teardown);
1415         g_test_add ("/TreeView/scrolling/new-row/path-500", ScrollFixture,
1416                     GINT_TO_POINTER (500),
1417                     scroll_fixture_constant_setup,
1418                     scroll_new_row,
1419                     scroll_fixture_teardown);
1420         g_test_add ("/TreeView/scrolling/new-row/path-999", ScrollFixture,
1421                     GINT_TO_POINTER (999),
1422                     scroll_fixture_constant_setup,
1423                     scroll_new_row,
1424                     scroll_fixture_teardown);
1425
1426         g_test_add ("/TreeView/scrolling/new-row/tree", ScrollFixture,
1427                     NULL,
1428                     scroll_fixture_tree_setup,
1429                     scroll_new_row_tree,
1430                     scroll_fixture_teardown);
1431
1432         /* Test scrolling to a newly created row, in a mixed height model */
1433         g_test_add ("/TreeView/scrolling/new-row-mixed/path-0", ScrollFixture,
1434                     GINT_TO_POINTER (0),
1435                     scroll_fixture_mixed_setup,
1436                     scroll_new_row,
1437                     scroll_fixture_teardown);
1438         g_test_add ("/TreeView/scrolling/new-row-mixed/path-3", ScrollFixture,
1439                     GINT_TO_POINTER (3),
1440                     scroll_fixture_mixed_setup,
1441                     scroll_new_row,
1442                     scroll_fixture_teardown);
1443         /* We scroll to 8 to test a partial visible row.  The 8 is
1444          * based on my font setting of "Vera Sans 11" and
1445          * the separators set to 0.  (This should be made dynamic; FIXME).
1446          */
1447         g_test_add ("/TreeView/scrolling/new-row-mixed/path-5", ScrollFixture,
1448                     GINT_TO_POINTER (5),
1449                     scroll_fixture_mixed_setup,
1450                     scroll_new_row,
1451                     scroll_fixture_teardown);
1452         g_test_add ("/TreeView/scrolling/new-row-mixed/path-500", ScrollFixture,
1453                     GINT_TO_POINTER (500),
1454                     scroll_fixture_mixed_setup,
1455                     scroll_new_row,
1456                     scroll_fixture_teardown);
1457         g_test_add ("/TreeView/scrolling/new-row-mixed/path-999", ScrollFixture,
1458                     GINT_TO_POINTER (999),
1459                     scroll_fixture_mixed_setup,
1460                     scroll_new_row,
1461                     scroll_fixture_teardown);
1462
1463         g_test_add ("/TreeView/scrolling/new-row-mixed/tree", ScrollFixture,
1464                     NULL,
1465                     scroll_fixture_mixed_tree_setup,
1466                     scroll_new_row_tree,
1467                     scroll_fixture_teardown);
1468
1469         /* Misc. tests */
1470         g_test_add ("/TreeView/scrolling/specific/bug-316689",
1471                         ScrollFixture, NULL,
1472                     scroll_fixture_constant_setup, test_bug316689,
1473                     scroll_fixture_teardown);
1474         g_test_add_func ("/TreeView/scrolling/specific/bug-359231",
1475                         test_bug359231);
1476         g_test_add ("/TreeView/scrolling/specific/bug-93584",
1477                     ScrollFixture, NULL,
1478                     scroll_fixture_tree_setup, test_bug93584,
1479                     scroll_fixture_teardown);
1480         g_test_add ("/TreeView/scrolling/specific/bug-111500",
1481                     ScrollFixture, NULL,
1482                     scroll_fixture_tree_setup, test_bug111500,
1483                     scroll_fixture_teardown);
1484         g_test_add ("/TreeView/scrolling/specific/bug-111500-mixed",
1485                     ScrollFixture, NULL,
1486                     scroll_fixture_mixed_tree_setup, test_bug111500_mixed,
1487                     scroll_fixture_teardown);
1488         g_test_add ("/TreeView/scrolling/specific/bug-163214",
1489                     ScrollFixture, NULL,
1490                     scroll_fixture_constant_setup, test_bug163214,
1491                     scroll_fixture_teardown);
1492
1493         return g_test_run ();
1494 }