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