]> Pileus Git - ~andy/gtk/blob - tests/testextendedlayout.c
Adding tests ported over from the old branch by Matthias.
[~andy/gtk] / tests / testextendedlayout.c
1 /* testextendedlayout.c
2  * Copyright (C) 2007 Mathias Hasselmann <mathias.hasselmann@gmx.de>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include <config.h>
21 #include <gtk/gtk.h>
22 #include <stdarg.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26
27 #ifdef GDK_WINDOWING_X11
28 #include "x11/gdkx.h"
29 #endif
30
31 #define IS_VALID_BASELINE(Baseline) ((Baseline) >= 0)
32
33 typedef enum _GuideType GuideType;
34 typedef enum _TestResult TestResult;
35
36 typedef struct _Guide Guide;
37
38 typedef struct _TestCase TestCase;
39 typedef struct _TestSuite TestSuite;
40
41 enum _GuideFlags
42 {
43   GUIDE_FLAGS_HORIZONTAL = (1 << 0),
44   GUIDE_FLAGS_VERTICAL = (1 << 1)
45 };
46
47 enum _GuideType
48 {
49   GUIDE_BASELINE,
50
51   GUIDE_INTERIOUR_VERTICAL,
52   GUIDE_INTERIOUR_HORIZONTAL,
53   GUIDE_INTERIOUR_BOTH,
54
55   GUIDE_EXTERIOUR_VERTICAL,
56   GUIDE_EXTERIOUR_HORIZONTAL,
57   GUIDE_EXTERIOUR_BOTH
58 };
59
60 enum _TestResult
61 {
62   TEST_RESULT_NONE,
63   TEST_RESULT_SUCCESS,
64   TEST_RESULT_FAILURE
65 };
66
67 enum
68 {
69   RESULT_COLUMN_MESSAGE,
70   RESULT_COLUMN_WEIGHT,
71   RESULT_COLUMN_ICON,
72   RESULT_COLUMN_RESULT,
73   RESULT_COLUNN_COUNT
74 };
75
76 enum
77 {
78   TEST_COLUMN_LABEL,
79   TEST_COLUMN_SELECTED,
80   TEST_COLUMN_TEST_CASE,
81   TEST_COLUMN_HAS_TEST_CASE,
82   TEST_COLUMN_PAGE_INDEX,
83   TEST_COLUMN_COUNT
84 };
85
86 struct _Guide
87 {
88   GtkWidget *widget;
89   GuideType type;
90   gint group;
91 };
92
93 struct _TestCase
94 {
95   TestSuite *suite;
96   const gchar *name;
97   const gchar *detail;
98   GtkWidget *widget;
99   GList *guides;
100   guint idle;
101 };
102
103 struct _TestSuite
104 {
105   GtkTreeSelection *selection;
106   GtkWidget *test_current_button;
107   GtkListStore *tests;
108
109   GtkWidget *window;
110   GtkWidget *notebook;
111   GtkWidget *baselines;
112   GtkWidget *interiour;
113   GtkWidget *exteriour;
114   GtkWidget *statusbar;
115
116   GtkTreeStore *results;
117   GtkWidget *results_view;
118   gint n_test_cases;
119   gint level;
120
121   GdkPixmap *tile;
122   GtkWidget *current;
123   GtkWidget *hover;
124   gint timestamp;
125
126   GtkTreeIter parent;
127 };
128
129 static const gchar lorem_ipsum[] =
130   "<span weight=\"bold\" size=\"xx-large\">"
131   "Lorem ipsum</span> dolor sit amet, consectetuer "
132   "adipiscing elit. Aliquam sed erat. Proin lectus "
133   "orci, venenatis pharetra, egestas id, tincidunt "
134   "vel, eros. Integer fringilla. Aenean justo ipsum, "        
135   "luctus ut, volutpat laoreet, vehicula in, libero.";
136
137 const gchar *captions[] =
138   { 
139     "<span size='xx-small'>xx-Small</span>",
140     "<span weight='bold'>Bold</span>",
141     "<span size='large'>Large</span>",
142     "<span size='xx-large'>xx-Large</span>",
143     NULL
144   };
145
146 static char * mask_xpm[] = 
147   {
148     "20 20 2 1",
149     "   c #000000",
150     "#  c #FFFFFF",
151     " # # # # # # # # # #",
152     "# # # # # # # # # # ",
153     " # # # # # # # # # #",
154     "# # # # # # # # # # ",
155     " # # # # # # # # # #",
156     "# # # # # # # # # # ",
157     " # # # # # # # # # #",
158     "# # # # # # # # # # ",
159     " # # # # # # # # # #",
160     "# # # # # # # # # # ",
161     " # # # # # # # # # #",
162     "# # # # # # # # # # ",
163     " # # # # # # # # # #",
164     "# # # # # # # # # # ",
165     " # # # # # # # # # #",
166     "# # # # # # # # # # ",
167     " # # # # # # # # # #",
168     "# # # # # # # # # # ",
169     " # # # # # # # # # #",
170     "# # # # # # # # # # "
171   };
172
173 static gint8 dashes[] = { 1, 5 };
174
175 static void
176 set_widget_name (GtkWidget   *widget,
177                  const gchar *format,
178                  ...)
179 {
180   gchar *name, *dash;
181   va_list args;
182
183   va_start (args, format);
184   name = g_strdup_vprintf (format, args);
185   va_end (args);
186
187   for(dash = name; NULL != (dash = strchr (dash, ' ')); )
188     *dash = '-';
189
190   gtk_widget_set_name (widget, name);
191   g_free (name);
192 }
193
194 static Guide*
195 guide_new (GtkWidget   *widget,
196            GuideType    type,
197            gint         group)
198 {
199   Guide* self = g_new0 (Guide, 1);
200
201   self->widget = widget;
202   self->type = type;
203   self->group = group;
204
205   return self;
206 }
207
208 static TestCase*
209 test_case_new (TestSuite   *suite,
210                const gchar *name,
211                const gchar *detail,
212                GtkWidget   *widget)
213 {
214   TestCase* self = g_new0 (TestCase, 1);
215
216   self->suite = suite;
217   self->name = name;
218   self->detail = detail;
219   self->widget = widget;
220
221   return self;
222 }
223
224 static void
225 update_status (TestSuite *suite,
226                GtkWidget *child)
227 {
228   const gchar *widget_name = gtk_widget_get_name (child);
229   const gchar *type_name = G_OBJECT_TYPE_NAME (child);
230   GString *status = g_string_new (type_name);
231
232   if (strcmp (widget_name, type_name))
233     g_string_append_printf (status, " (%s)", widget_name);
234
235   g_string_append_printf (status,
236                           "@%p:\nposition=%dx%d; size=%dx%d; requisition=%dx%d",
237                           child,
238                           child->allocation.x,
239                           child->allocation.y,
240                           child->allocation.width,
241                           child->allocation.height,
242                           child->requisition.width,
243                           child->requisition.height);
244
245   if (GTK_IS_EXTENDED_LAYOUT (child))
246     {
247       GtkExtendedLayout *layout = (GtkExtendedLayout*) child;
248       GtkRequisition min_size, nat_size;
249       gint min_height, nat_height;
250       gint min_width, nat_width;
251
252       gtk_extended_layout_get_desired_size (layout, &min_size, &nat_size);
253       g_string_append_printf (status, "; minimal-size: %dx%d, natural-size: %dx%d",
254                               min_size.width, min_size.height,
255                               nat_size.width, nat_size.height);
256
257       gtk_extended_layout_get_height_for_width (layout,
258                                                 child->allocation.width,
259                                                 &min_height, &nat_height);
260       g_string_append_printf (status, "; height-for-%d: minimal: %d, natural: %d",
261                               child->allocation.width, min_height, nat_height);
262
263       gtk_extended_layout_get_width_for_height (layout,
264                                                 child->allocation.height,
265                                                 &min_width, &nat_width);
266       g_string_append_printf (status, "; width-for-%d: minimal: %d, natural: %d",
267                               child->allocation.height, min_width, nat_width);
268
269     }
270
271   gtk_label_set_text (GTK_LABEL (suite->statusbar), status->str);
272   g_string_free (status, TRUE);
273 }
274
275 static void
276 item_activate_cb (GtkWidget *item,
277                   gpointer   data)
278 {
279   GtkWidget *widget = data;
280   TestCase *test;
281
282   test = g_object_get_data (G_OBJECT (widget), "test-case");
283   update_status (test->suite, widget);
284   test->suite->current = widget;
285
286   gtk_widget_queue_draw (test->widget);
287 }
288
289 static void
290 test_case_append_guide (TestCase  *self,
291                         GtkWidget *widget,
292                         GuideType  type,
293                         gint       group)
294 {
295   const gchar *widget_name;
296   const gchar *type_name;
297   gchar *item_label;
298   GtkWidget *popup;
299   GtkWidget *item;
300   Guide *guide;
301
302   guide = guide_new (widget, type, group);
303   self->guides = g_list_append (self->guides, guide);
304   g_object_set_data (G_OBJECT (widget), "test-case", self);
305
306   widget_name = gtk_widget_get_name (widget);
307   type_name = G_OBJECT_TYPE_NAME (widget);
308
309   item_label = g_strconcat (type_name,
310                             strcmp (widget_name, type_name) ? " (" : NULL,
311                             widget_name, ")", NULL);
312
313   item = gtk_menu_item_new_with_label (item_label);
314   popup = g_object_get_data (G_OBJECT (self->widget), "popup");
315
316   if (!popup)
317     {
318       popup = gtk_menu_new ();
319       g_object_set_data (G_OBJECT (self->widget), "popup", popup);
320     }
321
322   g_signal_connect (item, "activate", G_CALLBACK (item_activate_cb), widget);
323   gtk_menu_shell_append (GTK_MENU_SHELL (popup), item);
324   gtk_widget_show (item);
325
326   g_free (item_label);
327 }
328
329 static void
330 append_natural_size_box (TestCase           *test,
331                          GtkWidget          *parent,
332                          gboolean            vertical,
333                          gboolean            table,
334                          gboolean            ellipses)
335 {
336   GtkWidget *container = NULL;
337   GtkWidget *button, *label;
338
339   PangoEllipsizeMode ellipsize_mode;
340   gint i, j, k, l;
341
342   for (i = 0; i < (table ? 9 : 6); ++i)
343     {
344       ellipsize_mode = ellipses ?
345         PANGO_ELLIPSIZE_START + i/(table ? 3 : 2) : 
346         PANGO_ELLIPSIZE_NONE;
347
348       if (!i || (ellipses && 0 == i % (table ? 3 : 2)))
349         {
350           label = gtk_label_new (NULL);
351
352           switch(ellipsize_mode)
353             {
354               case PANGO_ELLIPSIZE_NONE:
355                 gtk_label_set_markup (GTK_LABEL (label), "<b>No ellipses</b>");
356                 break;
357               case PANGO_ELLIPSIZE_START:
358                 gtk_label_set_markup (GTK_LABEL (label), "<b>Ellipses at start</b>");
359                 break;
360               case PANGO_ELLIPSIZE_MIDDLE:
361                 gtk_label_set_markup (GTK_LABEL (label), "<b>Ellipses in the middle</b>");
362                 break;
363               case PANGO_ELLIPSIZE_END:
364                 gtk_label_set_markup (GTK_LABEL (label), "<b>Ellipses at end</b>");
365                 break;
366             }
367
368           if (vertical)
369             {
370               gtk_misc_set_alignment (GTK_MISC (label), 0.0, 1.0);
371               gtk_label_set_angle (GTK_LABEL (label), 90);
372             }
373           else
374             {
375               gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
376               gtk_label_set_angle (GTK_LABEL (label), 0);
377             }
378
379           gtk_box_pack_start (GTK_BOX (parent), label, FALSE, TRUE, 0);
380         }
381
382       if (table)
383         {
384           k = 1 + i / 3 + i % 3;
385
386           if (i < 7)
387             {
388               if (i < 6)
389                 container = gtk_table_new (vertical ? k : 1,
390                                            vertical ? 1 : k, FALSE);
391               else
392                 container = gtk_table_new (vertical ? k : 3,
393                                            vertical ? 3 : k, FALSE);
394
395               gtk_table_set_col_spacings (GTK_TABLE (container), 4);
396               gtk_table_set_row_spacings (GTK_TABLE (container), 4);
397             }
398         }
399       else if (vertical)
400         container = gtk_vbox_new (FALSE, 4);
401       else
402         container = gtk_hbox_new (FALSE, 4);
403
404       if (!gtk_widget_get_parent (container))
405         gtk_box_pack_start (GTK_BOX (parent), container, FALSE, TRUE, 0);
406
407       for (j = 0, l = i < 6 ? i / 3 : i - 6; j <= l; ++j)
408         {
409           label = gtk_label_new (NULL);
410           gtk_label_set_markup (GTK_LABEL (label), "<small>Small Button</small>");
411           gtk_label_set_angle (GTK_LABEL (label), vertical ? 90 : 0);
412           gtk_label_set_ellipsize (GTK_LABEL (label), ellipsize_mode);
413
414           button = gtk_button_new ();
415           set_widget_name (button, "small-%d-%d-%d", ellipses, i, j);
416           gtk_container_add (GTK_CONTAINER (button), label);
417
418           if (!table)
419             gtk_box_pack_start (GTK_BOX (container), button, FALSE, TRUE, 0);
420           else if (i < 6)
421             gtk_table_attach (GTK_TABLE (container), button,
422                               vertical ? 0 : j, vertical ? 1 : j + 1,
423                               vertical ? j : 0, vertical ? j + 1 : 1,
424                               GTK_FILL, GTK_FILL, 0, 0);
425           else
426             gtk_table_attach (GTK_TABLE (container), button,
427                               vertical ? i - 6 : j,
428                               vertical ? i - 5 : j < l ? j + 1 : 3,
429                               vertical ? j : i - 6, 
430                               vertical ? (j < l ? j + 1 : 3) : i - 5,
431                               GTK_FILL, GTK_FILL, 0, 0);
432
433           test_case_append_guide (test, button,
434                                   vertical ? GUIDE_EXTERIOUR_HORIZONTAL 
435                                            : GUIDE_EXTERIOUR_VERTICAL,
436                                   6 == i ? 3 : 7 == i && j ? 4 : j);
437         }
438
439       for (j = 0, l = (i < 6 ? i % 3 : 1); j < l; ++j)
440         {
441           label = gtk_label_new (NULL);
442           gtk_label_set_markup (GTK_LABEL (label), "<small>Large Button</small>");
443           gtk_label_set_angle (GTK_LABEL (label), vertical ? 90 : 0);
444
445           button = gtk_button_new ();
446           set_widget_name (button, "large-%d-%d-%d", ellipses, i, j);
447           gtk_container_add (GTK_CONTAINER (button), label);
448
449           if (table)
450             gtk_table_attach (GTK_TABLE (container), button,
451                               vertical ? MAX (0, i - 6) : i/3 + j + 1, 
452                               vertical ? MAX (1, i - 5) : i/3 + j + 2,
453                               vertical ? i/3 + j + 1 : MAX (0, i - 6), 
454                               vertical ? i/3 + j + 2 : MAX (1, i - 5),
455                               vertical ? GTK_FILL : GTK_FILL | GTK_EXPAND,
456                               vertical ? GTK_FILL | GTK_EXPAND : GTK_FILL,
457                               0, 0);
458           else
459             gtk_box_pack_start (GTK_BOX (container), button, TRUE, TRUE, 0);
460
461           test_case_append_guide (test, button, 
462                                   vertical ? GUIDE_EXTERIOUR_HORIZONTAL 
463                                            : GUIDE_EXTERIOUR_VERTICAL,
464                                   i < 6 ? 5 + i + j : 12);
465         }
466     }
467 }
468
469 static gboolean
470 restore_paned (gpointer data)
471 {
472   GtkPaned *paned;
473   GtkWidget *hint;
474   gint pos;
475
476
477   paned = GTK_PANED (data);
478   hint = gtk_paned_get_child2 (paned);
479   gtk_widget_set_sensitive (hint, TRUE);
480
481   pos = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (paned), "initial-position"));
482   gtk_paned_set_position (paned, pos);
483
484   return FALSE;
485 }
486
487 static gboolean
488 shrink_paned_timeout (gpointer data)
489 {
490   GtkPaned *paned;
491   gint pos;
492
493   paned = GTK_PANED (data);
494   pos = gtk_paned_get_position (paned);
495
496   if (pos < 20)
497     {
498       g_timeout_add (1000, restore_paned, paned);
499       return FALSE;
500     }
501
502   gtk_paned_set_position (paned, pos - 5);
503   return TRUE;
504 }
505
506 static void
507 shrink_paned (GtkWidget *button,
508               gpointer   data)
509 {
510   GtkPaned *paned;
511   GtkWidget *hint;
512
513   paned = GTK_PANED (data);
514   hint = gtk_paned_get_child2 (paned);
515   gtk_widget_set_sensitive (hint, FALSE);
516
517   g_object_set_data (G_OBJECT (paned), "initial-position",
518                      GINT_TO_POINTER (gtk_paned_get_position (paned)));
519   g_timeout_add (50, shrink_paned_timeout, paned);
520 }
521
522 static TestCase*
523 natural_size_test_new (TestSuite *suite,
524                        gboolean   vertical,
525                        gboolean   table)
526 {
527   GtkWidget *box, *paned, *hint, *button;
528   const gchar *detail;
529   TestCase *test;
530
531   if (vertical)
532     {
533       detail = table ? "GtkTable, vertical" : "GtkVBox";
534       hint = gtk_alignment_new (0.5, 1.0, 1.0, 0.0);
535       box = gtk_hbox_new (FALSE, 6);
536       paned = gtk_vpaned_new ();
537     }
538   else
539     {
540       detail = table ? "GtkTable, horizontal" : "GtkHBox";
541       hint = gtk_alignment_new (1.0, 0.5, 0.0, 1.0);
542       box = gtk_vbox_new (FALSE, 6);
543       paned = gtk_hpaned_new ();
544     }
545
546   test = test_case_new (suite, "Natural Size", detail, paned);
547   gtk_container_set_border_width (GTK_CONTAINER (test->widget), 6);
548
549   gtk_container_set_border_width (GTK_CONTAINER (box), 6);
550   gtk_paned_pack1 (GTK_PANED (test->widget), box, TRUE, TRUE);
551
552   append_natural_size_box (test, box, vertical, table, FALSE);
553   append_natural_size_box (test, box, vertical, table, TRUE);
554
555   button = gtk_button_new_with_label ("Shrink to check ellipsing");
556   g_signal_connect (button, "clicked", G_CALLBACK (shrink_paned), test->widget);
557
558   if (!vertical) 
559     gtk_label_set_angle (GTK_LABEL (GTK_BIN (button)->child), -90);
560
561   gtk_container_set_border_width (GTK_CONTAINER (hint), 6);
562   gtk_container_add (GTK_CONTAINER (hint), button);
563   gtk_paned_pack2 (GTK_PANED (test->widget), hint, FALSE, FALSE);
564
565   return test;
566 }
567
568 static void
569 on_socket_realized (GtkWidget *widget,
570                     gpointer   data)
571 {
572   gtk_socket_add_id (GTK_SOCKET (widget), GPOINTER_TO_INT (data)); 
573 }
574
575 static void
576 on_xembed_socket_realized (GtkWidget *widget,
577                            gpointer   data)
578 {
579   GdkNativeWindow plug_id = 0;
580   GError *error = NULL;
581   gchar **argv = data;
582   gint child_stdout;
583
584   if (g_spawn_async_with_pipes (NULL, argv, NULL, 0,
585                                 NULL, NULL, NULL,
586                                 NULL, &child_stdout, NULL,
587                                 &error))
588     {
589       gchar *plug_str;
590       char buffer[32];
591       gint len;
592
593       len = read (child_stdout, buffer, sizeof (buffer) - 1);
594       close (child_stdout);
595
596       if (len > 0)
597         {
598           buffer[len] = '\0';
599           plug_id = atoi (buffer);
600         }
601
602       plug_str = g_strdup_printf ("plug-id=%d", plug_id);
603       g_print ("%s: %s\n", gtk_widget_get_name (widget), plug_str);
604       gtk_widget_set_tooltip_text (widget, plug_str);
605       g_free (plug_str);
606     }
607   else
608     {
609       GtkWidget *plug, *label;
610       gchar *error_message;
611
612       error_message = g_strdup_printf (
613         "Failed to create external plug:\n%s",
614         error ? error->message : "No details available.");
615
616       label = gtk_label_new (error_message);
617       g_warning (error_message);
618
619       g_free (error_message);
620       g_clear_error (&error);
621
622       if (argv[2] && g_str_equal (argv[2], "--vertical"))
623         gtk_label_set_angle (GTK_LABEL (label), 90);
624
625       plug = gtk_plug_new (0);
626       gtk_container_add (GTK_CONTAINER (plug), label);
627       gtk_widget_show_all (plug);
628
629       plug_id = gtk_plug_get_id (GTK_PLUG (plug));
630     }
631
632   gtk_socket_add_id (GTK_SOCKET (widget), plug_id); 
633 }
634
635 static void
636 natural_size_test_misc_create_child (TestCase  *test,
637                                      GtkWidget *box,
638                                      gchar     *arg0,
639                                      gint       orientation,
640                                      gint       type)
641 {
642   const gchar *type_names[] =
643   {
644     "align", "socket-gtkplug", "socket-xembed",
645     "cell-view", "tree-view", "tree-view-scrolled",
646   };
647
648   const gchar *numbers[] = 
649   { 
650     "First", "Second", "Third", "Fourth", "Fifth",
651     "Sixth", "Seventh", "Eighth", "Nineth", NULL
652   };
653
654   GtkWidget *label, *child, *view, *align, *plug;
655   GdkNativeWindow plug_id;
656
657   GtkListStore *store = NULL;
658   GtkTreeViewColumn *column;
659   GtkCellRenderer *cell;
660   GtkTreePath *path;
661
662   gchar **argv = NULL;
663   gint i, argc;
664
665   GdkColor color;
666
667   if (type >= 3)
668     {
669       store = gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
670
671       for (i = 0; numbers[i] && (type > 4 || i < 2); ++i)
672         {
673           gchar *small = g_strdup_printf ("%s Small Cell", numbers[i]);
674           gchar *large = g_strdup_printf ("%s Large Cell", numbers[i]);
675
676           GtkTreeIter iter;
677
678           gtk_list_store_append (store, &iter);
679           gtk_list_store_set (store, &iter, 0, GTK_STOCK_ABOUT,
680                                             1, small, 2, large, -1);
681
682           g_free (large);
683           g_free (small);
684         }
685     }
686
687   for (i = 0; i < 2; ++i)
688     {
689       label = gtk_label_new ("Hello World");
690
691       gtk_label_set_ellipsize (GTK_LABEL (label),
692                                i ? PANGO_ELLIPSIZE_END : 
693                                    PANGO_ELLIPSIZE_NONE);
694
695       if (!orientation)
696         gtk_label_set_angle (GTK_LABEL (label), 90);
697
698       switch (type)
699         {
700           case 0:
701             child = label;
702             break;
703
704           case 1:
705             plug = gtk_plug_new (0);
706             plug_id = gtk_plug_get_id (GTK_PLUG (plug));
707             gtk_container_add (GTK_CONTAINER (plug), label);
708             gtk_widget_show_all (plug);
709
710             child = gtk_socket_new ();
711
712             g_signal_connect (child, "realize",
713                               G_CALLBACK (on_socket_realized),
714                               GINT_TO_POINTER (plug_id));
715             break;
716
717           case 2:
718             child = gtk_socket_new ();
719
720             argv = g_new0 (gchar*, 5);
721             argc = 0;
722
723             argv[argc++] = arg0;
724             argv[argc++] = "--action=create-plug";
725
726             if (!orientation)
727               argv[argc++] = "--vertical";
728             if (i)
729               argv[argc++] = "--ellipsize";
730
731             g_signal_connect_data (child, "realize",
732                                    G_CALLBACK (on_xembed_socket_realized),
733                                    argv, (GClosureNotify) g_free, 0);
734             break;
735
736           case 3:
737             child = gtk_cell_view_new ();
738
739             if (gdk_color_parse ("#ffc", &color))
740               gtk_cell_view_set_background_color (GTK_CELL_VIEW (child), &color);
741
742             cell = gtk_cell_renderer_pixbuf_new ();
743             gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (child), cell, FALSE);
744             gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (child), cell,
745                                             "stock-id", 0, NULL);
746
747             cell = gtk_cell_renderer_text_new ();
748             gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (child), cell, FALSE);
749             gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (child), cell,
750                                             "text", 1, NULL);
751
752             if (i)
753               g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
754
755             cell = gtk_cell_renderer_text_new ();
756             gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (child), cell, TRUE);
757             gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (child), cell,
758                                             "text", 2, NULL);
759
760             gtk_cell_view_set_model (GTK_CELL_VIEW (child),
761                                      GTK_TREE_MODEL (store));
762
763             path = gtk_tree_path_new_from_indices (0, -1);
764             gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (child), path);
765             gtk_tree_path_free (path);
766
767             break;
768
769           case 4:
770           case 5:
771             view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
772             gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (view), FALSE);
773
774             cell = gtk_cell_renderer_pixbuf_new ();
775             column = gtk_tree_view_column_new_with_attributes (NULL, cell, "stock-id", 0, NULL);
776             gtk_tree_view_append_column (GTK_TREE_VIEW (view), column);
777
778             cell = gtk_cell_renderer_text_new ();
779             column = gtk_tree_view_column_new_with_attributes ("Bar", cell, "text", 1, NULL);
780             gtk_tree_view_append_column (GTK_TREE_VIEW (view), column);
781
782             if (i)
783               g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
784
785             cell = gtk_cell_renderer_text_new ();
786             column = gtk_tree_view_column_new_with_attributes ("Foo", cell, "text", 2, NULL);
787             gtk_tree_view_append_column (GTK_TREE_VIEW (view), column);
788
789             if (type > 4)
790               {
791                 child = gtk_scrolled_window_new (NULL, NULL);
792                 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (child),
793                                                      GTK_SHADOW_IN);
794                 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (child),
795                                                 GTK_POLICY_NEVER,
796                                                 GTK_POLICY_ALWAYS);
797                 gtk_container_add (GTK_CONTAINER (child), view);
798               }
799             else
800               child = view;
801
802             break;
803
804           default:
805             continue;
806         }
807
808       align = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
809       gtk_box_pack_start (GTK_BOX (box), align, FALSE, TRUE, 0);
810       gtk_container_add (GTK_CONTAINER (align), child);
811
812       set_widget_name (child, "%s-%s-ellipsize-%s",
813                        orientation ? "horizontal" : "vertical",
814                        type_names[type], i ? "end" : "none");
815
816       test_case_append_guide (test, child, 
817                               orientation ? GUIDE_EXTERIOUR_VERTICAL
818                                           : GUIDE_EXTERIOUR_HORIZONTAL,
819                               type < 3 ? orientation : type - 1);
820     }
821 }
822
823 static TestCase*
824 natural_size_test_misc_new (TestSuite *suite,
825                             gchar     *arg0)
826 {
827   const gchar *captions[] = 
828   {
829     "<b>GtkAligment</b>",
830     "<b>GtkSocket with GtkPlug</b>",
831     "<b>GtkSocket with XEMBED</b>",
832     "<b>GtkCellView</b>",
833     "<b>GtkTreeView</b>",
834     "<b>GtkTreeView within GtkScrolledWindow</b>"
835   };
836
837   GtkWidget *box, *hpaned, *vpaned, *label;
838   gint orientation, type;
839   TestCase *test;
840
841   test = test_case_new (suite,  "Natural Size", "Various Widgets", 
842                         gtk_vbox_new (FALSE, 6));
843
844   gtk_container_set_border_width (GTK_CONTAINER (test->widget), 6);
845
846   vpaned = NULL; /* silence the gcc */
847
848   for (orientation = 0; orientation < 2; ++orientation)
849     {
850       label = gtk_label_new ("Move the handle to test\n"
851                              "natural size allocation");
852       gtk_misc_set_padding (GTK_MISC (label), 6, 6);
853
854       if (orientation)
855         {
856           gtk_label_set_angle (GTK_LABEL (label), 90);
857           gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
858
859           hpaned = gtk_hpaned_new ();
860           box = gtk_hbox_new (FALSE, 6);
861
862           gtk_box_pack_start (GTK_BOX (box), label, FALSE, TRUE, 0);
863           gtk_box_pack_start (GTK_BOX (box), gtk_vseparator_new (), FALSE, TRUE, 0);
864           gtk_box_pack_start (GTK_BOX (box), vpaned, TRUE, TRUE, 0);
865
866           gtk_paned_pack2 (GTK_PANED (hpaned), box, TRUE, FALSE);
867
868           box = gtk_vbox_new (FALSE, 6);
869
870           gtk_paned_pack1 (GTK_PANED (hpaned), box, TRUE, TRUE);
871           gtk_box_pack_start (GTK_BOX (test->widget), hpaned, TRUE, TRUE, 0);
872         }
873       else
874         {
875           gtk_misc_set_alignment (GTK_MISC (label), 0.5, 1.0);
876
877           vpaned = gtk_vpaned_new ();
878           box = gtk_hbox_new (FALSE, 6);
879
880           gtk_paned_pack1 (GTK_PANED (vpaned), box, TRUE, TRUE);
881           gtk_paned_pack2 (GTK_PANED (vpaned), label, FALSE, FALSE);
882         }
883
884       for (type = 0; type < (orientation ? 6 : 3); ++type)
885         {
886           label = gtk_label_new (NULL);
887           gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
888           gtk_label_set_markup (GTK_LABEL (label), captions[type]);
889           gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
890           gtk_box_pack_start (GTK_BOX (box), label, FALSE, TRUE, 0);
891
892           if (!orientation)
893             gtk_label_set_angle (GTK_LABEL (label), 90);
894
895           natural_size_test_misc_create_child (test, box, arg0, orientation, type);
896         }
897     }
898
899   return test;
900 }
901
902 static TestCase*
903 size_for_allocation_test_new (TestSuite *suite,
904                               gboolean   vertical,
905                               gboolean   table)
906 {
907   GtkWidget *container, *child;
908   TestCase *test;
909   int i;
910
911   if (vertical)
912     {
913       test = test_case_new (suite,
914                             "Size for Allocation", 
915                             table ? "Height for Width, GtkTable"
916                                   : "Height for Width, GtkVBox",
917                             gtk_hpaned_new ());
918
919       if (table)
920         {
921           container = gtk_table_new (4, 1, FALSE);
922           gtk_orientable_set_orientation (GTK_ORIENTABLE (container), 
923                                           GTK_ORIENTATION_VERTICAL);
924         }
925       else
926         container = gtk_vbox_new (FALSE, 6);
927
928       child = gtk_label_new ("Move the handle to test\n"
929                              "height-for-width requests");
930
931       gtk_label_set_angle (GTK_LABEL (child), 90);
932     }
933   else
934     {
935       test = test_case_new (suite,
936                             "Size for Allocation", 
937                             table ? "Width for Height, GtkTable"
938                                   : "Width for Height, GtkHBox",
939                             gtk_vpaned_new ());
940
941       if (table)
942         {
943           container = gtk_table_new (1, 4, FALSE);
944           gtk_orientable_set_orientation (GTK_ORIENTABLE (container), 
945                                      GTK_ORIENTATION_HORIZONTAL);
946         }
947       else
948         container = gtk_hbox_new (FALSE, 6);
949
950       child = gtk_label_new ("Move the handle to test\n"
951                              "width-for-height requests");
952     }
953
954   gtk_container_set_border_width (GTK_CONTAINER (test->widget), 6);
955   gtk_container_set_border_width (GTK_CONTAINER (container), 6);
956   gtk_misc_set_padding (GTK_MISC (child), 6, 6);
957
958   gtk_paned_pack1 (GTK_PANED (test->widget), container, TRUE, FALSE);
959   gtk_paned_pack2 (GTK_PANED (test->widget), child, FALSE, FALSE);
960
961   for (i = 0; i < 4; ++i)
962     {
963       if (2 != i)
964         {       
965           child = gtk_label_new (lorem_ipsum);
966           gtk_label_set_line_wrap (GTK_LABEL (child), TRUE);
967           gtk_label_set_use_markup (GTK_LABEL (child), TRUE);
968           test_case_append_guide (test, child, GUIDE_EXTERIOUR_BOTH, -1);
969           test_case_append_guide (test, child, GUIDE_INTERIOUR_BOTH, -1);
970
971           if (!table)
972             gtk_box_pack_start (GTK_BOX (container), child, FALSE, TRUE, 0);
973           else if (vertical)
974             gtk_table_attach (GTK_TABLE (container), child, 0, 1, i, i + 1,
975                               GTK_EXPAND|GTK_FILL, GTK_FILL, 0, 0);
976           else
977             gtk_table_attach (GTK_TABLE (container), child, i, i + 1, 0, 1, 
978                               GTK_FILL, GTK_EXPAND|GTK_FILL, 0, 0);
979
980           if (i > 0)
981             gtk_label_set_full_size (GTK_LABEL (child), TRUE);
982
983           if (i > 2)
984             gtk_label_set_angle (GTK_LABEL (child), vertical ? 180 : 270);
985           else if (!vertical)
986             gtk_label_set_angle (GTK_LABEL (child), 90);
987
988           set_widget_name (child, "%s-label-and-%g-degree",
989                            i > 0 ? "full-size" : "regular",
990                            gtk_label_get_angle (GTK_LABEL (child)));
991         }
992       else
993         {       
994           child = gtk_button_new ();
995           set_widget_name (child, "the-button");
996           gtk_container_add (GTK_CONTAINER (child),
997                              gtk_image_new_from_stock (GTK_STOCK_DIALOG_INFO,
998                                                        GTK_ICON_SIZE_DIALOG));
999
1000           if (!table)
1001             gtk_box_pack_start (GTK_BOX (container), child, TRUE, TRUE, 0);
1002           else if (vertical)
1003             gtk_table_attach (GTK_TABLE (container), child, 0, 1, i, i + 1,
1004                               GTK_EXPAND|GTK_FILL, GTK_EXPAND|GTK_FILL, 0, 0);
1005           else
1006             gtk_table_attach (GTK_TABLE (container), child, i, i + 1, 0, 1,
1007                               GTK_EXPAND|GTK_FILL, GTK_EXPAND|GTK_FILL, 0, 0);
1008         }
1009     }
1010
1011   return test;
1012 }
1013
1014 static TestCase*
1015 baseline_test_new (TestSuite *suite)
1016 {
1017   GtkWidget *child;
1018   GtkWidget *view;
1019   GtkWidget *label;
1020
1021   TestCase *test = test_case_new (suite, 
1022                                   "Baseline Alignment", "Real-World Example",
1023                                   gtk_table_new (3, 3, FALSE));
1024
1025   gtk_container_set_border_width (GTK_CONTAINER (test->widget), 12);
1026   gtk_table_set_col_spacings (GTK_TABLE (test->widget), 6);
1027   gtk_table_set_row_spacings (GTK_TABLE (test->widget), 6);
1028
1029   child = gtk_entry_new ();
1030   gtk_entry_set_text (GTK_ENTRY (child), "Test...");
1031   test_case_append_guide (test, child, GUIDE_BASELINE, 0);
1032   gtk_table_attach (GTK_TABLE (test->widget), child, 1, 2, 0, 1, 
1033                     GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0);
1034
1035   label = gtk_label_new_with_mnemonic ("_Title:");
1036   test_case_append_guide (test, label, GUIDE_BASELINE, 0);
1037   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
1038   gtk_label_set_mnemonic_widget  (GTK_LABEL (label), child);
1039   gtk_table_attach (GTK_TABLE (test->widget), label, 0, 1, 0, 1, 
1040                     GTK_FILL, GTK_FILL, 0, 0);
1041
1042   label = gtk_label_new_with_mnemonic ("Notice on\ntwo rows.");
1043   test_case_append_guide (test, label, GUIDE_BASELINE, 0);
1044   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
1045   gtk_table_attach (GTK_TABLE (test->widget), label, 2, 3, 0, 2, 
1046                     GTK_FILL, GTK_FILL, 0, 0);
1047
1048   child = gtk_font_button_new ();
1049   test_case_append_guide (test, child, GUIDE_BASELINE, 1);
1050   gtk_table_attach (GTK_TABLE (test->widget), child, 1, 2, 1, 2, 
1051                     GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0);
1052
1053   label = gtk_label_new_with_mnemonic ("_Font:");
1054   test_case_append_guide (test, label, GUIDE_BASELINE, 1);
1055   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
1056   gtk_label_set_mnemonic_widget  (GTK_LABEL (label), child);
1057   gtk_table_attach (GTK_TABLE (test->widget), label, 0, 1, 1, 2, 
1058                     GTK_FILL, GTK_FILL, 0, 0);
1059
1060   view = gtk_text_view_new ();
1061   gtk_widget_set_size_request (view, 200, -1);
1062   gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (view),
1063                                GTK_WRAP_WORD);
1064   test_case_append_guide (test, view, GUIDE_BASELINE, 2);
1065   gtk_text_buffer_set_text (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)),
1066                             lorem_ipsum, -1);
1067
1068   child = gtk_scrolled_window_new (NULL, NULL);
1069   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (child),
1070                                   GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1071   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (child),
1072                                        GTK_SHADOW_IN);
1073   gtk_container_add (GTK_CONTAINER (child), view);
1074
1075   gtk_table_attach (GTK_TABLE (test->widget), child, 1, 3, 2, 3, 
1076                     GTK_FILL | GTK_EXPAND,
1077                     GTK_FILL | GTK_EXPAND, 
1078                     0, 0);
1079
1080   label = gtk_label_new_with_mnemonic ("_Comment:");
1081   test_case_append_guide (test, label, GUIDE_BASELINE, 2);
1082   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
1083   gtk_label_set_mnemonic_widget  (GTK_LABEL (label), child);
1084   gtk_table_attach (GTK_TABLE (test->widget), label, 0, 1, 2, 3,
1085                     GTK_FILL, GTK_FILL, 0, 0);
1086
1087   return test;
1088 }
1089
1090 static TestCase*
1091 baseline_test_bin_new (TestSuite *suite)
1092 {
1093   GtkWidget *bin;
1094   GtkWidget *label;
1095   GtkWidget *table;
1096
1097   int i, j;
1098
1099   const GType types[] = 
1100     { 
1101       GTK_TYPE_ALIGNMENT, GTK_TYPE_BUTTON, 
1102       GTK_TYPE_EVENT_BOX, GTK_TYPE_FRAME, 
1103       G_TYPE_INVALID
1104     };
1105
1106   TestCase *test = test_case_new (suite, 
1107                                   "Baseline Alignment", "Various GtkBins",
1108                                   gtk_alignment_new (0.5, 0.5, 0.0, 0.0));
1109
1110   table = gtk_table_new (G_N_ELEMENTS (types) - 1, 
1111                          G_N_ELEMENTS (captions),
1112                          FALSE);
1113
1114   gtk_container_set_border_width (GTK_CONTAINER (table), 12);
1115   gtk_table_set_col_spacings (GTK_TABLE (table), 6);
1116   gtk_table_set_row_spacings (GTK_TABLE (table), 6);
1117   gtk_container_add (GTK_CONTAINER (test->widget), table);
1118
1119   for (i = 0; types[i]; ++i)
1120     {
1121       label = gtk_label_new (g_type_name (types[i]));
1122       gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
1123
1124       gtk_table_attach (GTK_TABLE (table), label, 0, 1,
1125                         i, i + 1, GTK_FILL, GTK_FILL, 0, 0);
1126
1127       for (j = 0; captions[j]; ++j)
1128         {
1129           bin = g_object_new (types[i], NULL);
1130           label = gtk_label_new (NULL);
1131
1132           gtk_label_set_markup (GTK_LABEL (label), captions[j]);
1133           gtk_container_add (GTK_CONTAINER (bin), label);
1134
1135           test_case_append_guide (test, bin, GUIDE_BASELINE, i);
1136           gtk_table_attach (GTK_TABLE (table), bin, j + 1, j + 2,
1137                             i, i + 1, GTK_FILL, GTK_FILL, 0, 0);
1138         }
1139     }
1140
1141   return test;
1142 }
1143
1144 #if 0
1145 static TestCase*
1146 baseline_test_hbox_new (TestSuite *suite,
1147                         gboolean   buttons)
1148 {
1149   GtkWidget *bin;
1150   GtkWidget *child;
1151   GtkWidget *table;
1152   GtkWidget *hbox;
1153
1154   int i, j;
1155
1156   const gchar *names[] = 
1157     {
1158       "default", "baseline", "baseline and bottom-padding", 
1159       "baseline and top-padding", "baseline and border-width",
1160       NULL
1161     };
1162
1163   TestCase *test = test_case_new (suite, "Baseline Alignment",
1164                                   buttons ? "GtkHBox and Buttons" 
1165                                           : "GtkHBox and Labels",
1166                                   gtk_alignment_new (0.5, 0.5, 0.0, 0.0));
1167
1168   table = gtk_table_new (G_N_ELEMENTS (names) - 1, 
1169                          G_N_ELEMENTS (captions),
1170                          FALSE);
1171
1172   gtk_container_set_border_width (GTK_CONTAINER (table), 12);
1173   gtk_table_set_col_spacings (GTK_TABLE (table), 6);
1174   gtk_table_set_row_spacings (GTK_TABLE (table), 6);
1175   gtk_container_add (GTK_CONTAINER (test->widget), table);
1176
1177   for (i = 0; names[i]; ++i)
1178     {
1179       child = gtk_label_new (names[i]);
1180       gtk_misc_set_alignment (GTK_MISC (child), 0.0, 0.5);
1181   
1182       hbox = gtk_hbox_new (FALSE, 6);
1183       test_case_append_guide (test, hbox, GUIDE_EXTERIOUR_BOTH, -1);
1184       set_widget_name (hbox, "hbox-%s", names[i]);
1185
1186       if (i > 0)
1187         gtk_hbox_set_baseline_policy (GTK_HBOX (hbox), GTK_BASELINE_FIRST);
1188
1189       gtk_table_attach (GTK_TABLE (table), child,
1190                         0, 1, i, i + 1,
1191                         GTK_FILL, GTK_FILL, 0, 0);
1192       gtk_table_attach (GTK_TABLE (table), hbox,
1193                         1, G_N_ELEMENTS (captions), i, i + 1,
1194                         GTK_FILL, GTK_FILL, 0, 0);
1195
1196       for (j = i ? -3 : 0; captions[MAX (0, j)]; ++j)
1197         {
1198           child = gtk_label_new (NULL);
1199           gtk_label_set_markup (GTK_LABEL (child), captions[MAX (0, j)]);
1200
1201           if (buttons)
1202             {
1203               bin = gtk_button_new ();
1204               gtk_container_add (GTK_CONTAINER (bin), child);
1205               child = bin;
1206             }
1207
1208           test_case_append_guide (test, child, GUIDE_BASELINE, i);
1209
1210           if (j < 0 && i > 1)
1211             {
1212               bin = gtk_alignment_new (0.5, 0.5, 0.0, (j + 3) * 0.5);
1213
1214               set_widget_name (bin, "align-%s-%s-%d",
1215                                buttons ? "button" : "label",
1216                                names[i], (j + 3) * 50);
1217
1218               switch (i) 
1219                 {
1220                   case 2:
1221                     gtk_alignment_set_padding (GTK_ALIGNMENT (bin), 0, 25, 0, 0);
1222                     break;
1223
1224                   case 3:
1225                     gtk_alignment_set_padding (GTK_ALIGNMENT (bin), 25, 0, 0, 0);
1226                     break;
1227
1228                   case 4:
1229                     gtk_container_set_border_width (GTK_CONTAINER (bin), 12);
1230                     break;
1231                 }
1232
1233               gtk_container_add (GTK_CONTAINER (bin), child);
1234               test_case_append_guide (test, bin, GUIDE_BASELINE, i);
1235               child = bin;
1236             }
1237
1238           gtk_box_pack_start (GTK_BOX (hbox), child, FALSE, TRUE, 0);
1239         }
1240     }
1241
1242   return test;
1243 }
1244 #endif
1245
1246 static gboolean
1247 get_extends (GtkWidget    *widget,
1248              GtkWidget    *toplevel,
1249              GdkRectangle *extends)
1250 {
1251   *extends = widget->allocation;
1252
1253   return
1254     gtk_widget_get_visible (widget) &&
1255     gtk_widget_translate_coordinates (widget, toplevel, 0, 0, 
1256                                       &extends->x, &extends->y);
1257 }
1258
1259 static gboolean
1260 get_interiour (GtkWidget    *widget,
1261                 GtkWidget    *toplevel,
1262                GdkRectangle *extends)
1263 {
1264   if (GTK_IS_LABEL (widget))
1265     {
1266       PangoLayout *layout;
1267       PangoRectangle log;
1268       GtkLabel *label;
1269
1270       label = GTK_LABEL (widget);
1271       layout = gtk_label_get_layout (label);
1272       pango_layout_get_pixel_extents (layout, NULL, &log);
1273       gtk_label_get_layout_offsets (label, &log.x, &log.y);
1274
1275       log.x -= toplevel->allocation.x;
1276       log.y -= toplevel->allocation.y;
1277
1278       g_assert (sizeof log == sizeof *extends);
1279       memcpy (extends, &log, sizeof *extends);
1280     }
1281
1282   return FALSE;
1283 }
1284
1285 static gint
1286 get_baseline_of_layout (PangoLayout *layout)
1287 {
1288   PangoLayoutLine *line;
1289   PangoRectangle log;
1290
1291   line = pango_layout_get_line_readonly (layout, 0);
1292   pango_layout_line_get_pixel_extents (line, NULL, &log);
1293   return PANGO_ASCENT (log);
1294 }
1295
1296 static gint
1297 get_baselines_of_text_view (GtkTextView *view, gint **baselines)
1298 {
1299   GtkTextBuffer *buffer = gtk_text_view_get_buffer (view);
1300   GtkTextAttributes *attrs = gtk_text_view_get_default_attributes (view);
1301   PangoContext *context = gtk_widget_get_pango_context (GTK_WIDGET (view));
1302   PangoLayout *layout = pango_layout_new (context);
1303
1304   GtkTextIter start, end;
1305   GdkRectangle bounds;
1306   gchar *text;
1307
1308   gtk_text_buffer_get_start_iter (buffer, &start);
1309   gtk_text_iter_get_attributes (&start, attrs);
1310
1311   end = start;
1312   gtk_text_iter_forward_to_line_end (&end);
1313   text = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
1314   gtk_text_view_get_iter_location (view, &start, &bounds);
1315
1316   pango_layout_set_width (layout, PANGO_SCALE *
1317                           GTK_WIDGET (view)->allocation.width);
1318   pango_layout_set_font_description (layout, attrs->font);
1319   pango_layout_set_wrap (layout, PANGO_WRAP_WORD);
1320   pango_layout_set_text (layout, text, -1);
1321
1322   gtk_text_view_buffer_to_window_coords (view, GTK_TEXT_WINDOW_TEXT,
1323                                          0, bounds.y, NULL, &bounds.y);
1324   bounds.y += get_baseline_of_layout (layout);
1325
1326   gtk_text_attributes_unref (attrs);
1327   g_object_unref (layout);
1328   g_free (text);
1329
1330   *baselines = g_new(gint, 1);
1331   *baselines[0] = bounds.y;
1332
1333   return 1;
1334 }
1335
1336 static gint
1337 get_baselines (GtkWidget *widget, gint **baselines)
1338 {
1339 #if 0
1340   if (GTK_IS_EXTENDED_LAYOUT (widget) &&
1341       GTK_EXTENDED_LAYOUT_HAS_BASELINES (widget))
1342     return gtk_extended_layout_get_baselines (GTK_EXTENDED_LAYOUT (widget), baselines);
1343 #endif
1344   if (GTK_IS_TEXT_VIEW (widget))
1345     return get_baselines_of_text_view (GTK_TEXT_VIEW (widget), baselines);
1346
1347   return -1;
1348 }
1349
1350 static void
1351 draw_baselines (GdkDrawable  *drawable,
1352                 GdkGC        *gc,
1353                 GtkWidget    *toplevel,
1354                 GdkRectangle *extends,
1355                 gint          baseline)
1356 {
1357   const gint x0 = toplevel->allocation.x;
1358   const gint y0 = toplevel->allocation.y;
1359   const gint cx = toplevel->allocation.width;
1360
1361   const gint xa = x0 + extends->x;
1362   const gint xe = xa + extends->width - 1;
1363   const gint ya = y0 + extends->y + baseline;
1364
1365   gdk_draw_line (drawable, gc, xa, ya - 5, xa, ya + 2);
1366   gdk_draw_line (drawable, gc, xa - 5, ya, xe + 5, ya);
1367   gdk_draw_line (drawable, gc, xe, ya - 5, xe, ya + 2);
1368
1369   gdk_gc_set_line_attributes (gc, 1, GDK_LINE_ON_OFF_DASH,
1370                               GDK_CAP_NOT_LAST, GDK_JOIN_MITER);
1371
1372   gdk_gc_set_dashes (gc, x0 % (dashes[0] + dashes[1]), dashes, 2);
1373   gdk_draw_line (drawable, gc, x0, ya, xa - 5, ya);
1374
1375   gdk_gc_set_dashes (gc, (xe + 2) % (dashes[0] + dashes[1]), dashes, 2);
1376   gdk_draw_line (drawable, gc, xe + 5, ya, x0 + cx - 1, ya);
1377
1378   gdk_gc_set_line_attributes (gc, 1, GDK_LINE_SOLID,
1379                               GDK_CAP_NOT_LAST, GDK_JOIN_MITER);
1380 }
1381
1382 static void
1383 draw_extends (GdkDrawable  *drawable,
1384               GdkGC        *gc,
1385               GtkWidget    *toplevel,
1386               GdkRectangle *extends)
1387 {
1388   const gint x0 = toplevel->allocation.x;
1389   const gint y0 = toplevel->allocation.y;
1390   const gint cx = toplevel->allocation.width;
1391   const gint cy = toplevel->allocation.height;
1392
1393   const gint xa = x0 + extends->x;
1394   const gint xe = xa + extends->width - 1;
1395
1396   const gint ya = y0 + extends->y;
1397   const gint ye = ya + extends->height - 1;
1398
1399   gdk_draw_line (drawable, gc, xa, y0, xa, y0 + cy - 1);
1400   gdk_draw_line (drawable, gc, xe, y0, xe, y0 + cy - 1);
1401   gdk_draw_line (drawable, gc, x0, ya, x0 + cx - 1, ya);
1402   gdk_draw_line (drawable, gc, x0, ye, x0 + cx - 1, ye);
1403 }
1404
1405 static gint
1406 test_case_eval_guide (const TestCase  *self,
1407                       const Guide     *guide,
1408                       GdkRectangle    *extends,
1409                       gint           **baselines)
1410 {
1411   gint num_baselines = -1;
1412
1413   if (get_extends (guide->widget, self->widget, extends))
1414     {
1415       *baselines = NULL;
1416
1417       switch (guide->type)
1418         {
1419           case GUIDE_BASELINE:
1420             num_baselines = get_baselines (guide->widget, baselines);
1421             break;
1422
1423           case GUIDE_INTERIOUR_BOTH:
1424           case GUIDE_INTERIOUR_VERTICAL:
1425           case GUIDE_INTERIOUR_HORIZONTAL:
1426             get_interiour (guide->widget, self->widget, extends);
1427             num_baselines = 0;
1428             break;
1429
1430           case GUIDE_EXTERIOUR_BOTH:
1431           case GUIDE_EXTERIOUR_VERTICAL:
1432           case GUIDE_EXTERIOUR_HORIZONTAL:
1433             num_baselines = 0;
1434             break;
1435         }
1436     }
1437
1438   return num_baselines;
1439 }
1440
1441 static gboolean
1442 guide_is_compatible (const Guide    *self,
1443                      const Guide    *other)
1444 {
1445   switch (self->type)
1446     {
1447       case GUIDE_BASELINE:
1448         return
1449           GUIDE_BASELINE == other->type;
1450
1451       case GUIDE_INTERIOUR_BOTH:
1452       case GUIDE_EXTERIOUR_BOTH:
1453         return 
1454           GUIDE_INTERIOUR_BOTH == other->type ||
1455           GUIDE_EXTERIOUR_BOTH == other->type;
1456
1457       case GUIDE_INTERIOUR_VERTICAL:
1458       case GUIDE_EXTERIOUR_VERTICAL:
1459         return 
1460           GUIDE_INTERIOUR_VERTICAL == other->type ||
1461           GUIDE_EXTERIOUR_VERTICAL == other->type;
1462
1463       case GUIDE_INTERIOUR_HORIZONTAL:
1464       case GUIDE_EXTERIOUR_HORIZONTAL:
1465         return 
1466           GUIDE_INTERIOUR_HORIZONTAL == other->type ||
1467           GUIDE_EXTERIOUR_HORIZONTAL == other->type;
1468     }
1469
1470   g_return_val_if_reached (FALSE);
1471 }
1472
1473 static gboolean
1474 test_case_compare_guides (const TestCase *self,
1475                           const Guide    *guide1,
1476                           const Guide    *guide2)
1477 {
1478   gint *baselines1 = NULL, *baselines2 = NULL;
1479   GdkRectangle extends1, extends2;
1480   gboolean equal = FALSE;
1481
1482   if (guide_is_compatible (guide1, guide2) &&
1483       test_case_eval_guide (self, guide1, &extends1, &baselines1) >= 0 &&
1484       test_case_eval_guide (self, guide2, &extends2, &baselines2) >= 0)
1485     {
1486       switch (guide1->type)
1487         {
1488           case GUIDE_BASELINE:
1489             equal =
1490               IS_VALID_BASELINE (*baselines1) &&
1491               IS_VALID_BASELINE (*baselines2) &&
1492               extends1.y + *baselines1 == extends2.y + *baselines2;
1493             break;
1494
1495           case GUIDE_INTERIOUR_HORIZONTAL:
1496           case GUIDE_EXTERIOUR_HORIZONTAL:
1497             equal =
1498               extends1.height == extends2.height &&
1499               extends1.y == extends2.y;
1500             break;
1501
1502           case GUIDE_INTERIOUR_VERTICAL:
1503           case GUIDE_EXTERIOUR_VERTICAL:
1504             equal =
1505               extends1.width == extends2.width &&
1506               extends1.x == extends2.x;
1507             break;
1508
1509           case GUIDE_INTERIOUR_BOTH:
1510           case GUIDE_EXTERIOUR_BOTH:
1511             equal = !memcpy (&extends1, &extends2, sizeof extends1);
1512             break;
1513
1514         }
1515     }
1516
1517   g_free (baselines1);
1518   g_free (baselines2);
1519
1520   return equal;
1521 }
1522
1523 static const gchar*
1524 guide_type_get_color (GuideType type,
1525                       gboolean  is_current)
1526 {
1527   switch (type) 
1528     {
1529       case GUIDE_BASELINE: return is_current ? "#f00" : "#00f";
1530       default: return is_current ? "#000" : "#fff";
1531     }
1532 }
1533
1534 static gboolean
1535 draw_guides (gpointer data)
1536 {
1537   TestCase *test = data;
1538   GdkDrawable *drawable;
1539   const GList *iter;
1540   gint iteration;
1541
1542   GdkGCValues values;
1543   GdkGC *gc;
1544
1545   gboolean show_baselines;
1546   gboolean show_interiour;
1547   gboolean show_exteriour;
1548
1549   values.subwindow_mode = GDK_INCLUDE_INFERIORS;
1550   drawable = test->widget->window;
1551
1552   gc = gdk_gc_new_with_values (drawable, &values, 
1553                                GDK_GC_SUBWINDOW);
1554
1555   gdk_gc_set_tile (gc, test->suite->tile);
1556
1557   show_baselines =
1558     test->suite->baselines && gtk_toggle_button_get_active (
1559     GTK_TOGGLE_BUTTON (test->suite->baselines));
1560   show_interiour =
1561     test->suite->interiour && gtk_toggle_button_get_active (
1562     GTK_TOGGLE_BUTTON (test->suite->interiour));;
1563   show_exteriour = 
1564     test->suite->exteriour && gtk_toggle_button_get_active (
1565     GTK_TOGGLE_BUTTON (test->suite->exteriour));;
1566
1567   for (iteration = 0; iteration < 3; ++iteration)
1568     {
1569       for (iter = test->guides; iter; iter = iter->next)
1570         {
1571           const Guide *guide = iter->data;
1572           gboolean is_current = (guide->widget == test->suite->current);
1573           GdkRectangle extends;
1574           gint num_baselines;
1575           gint *baselines;
1576           gint i;
1577
1578           if (!is_current != !iteration)
1579             continue;
1580
1581           if (is_current)
1582             {
1583               if (test->suite->timestamp >= 3)
1584                 continue;
1585
1586               if (1 == iteration)
1587                 {
1588                   gdk_gc_set_fill (gc, GDK_TILED);
1589                   gdk_gc_set_function (gc, GDK_OR);
1590
1591                   gdk_draw_rectangle (drawable, gc, TRUE, 
1592                                       guide->widget->allocation.x,
1593                                       guide->widget->allocation.y,
1594                                       guide->widget->allocation.width,
1595                                       guide->widget->allocation.height);
1596
1597                   gdk_gc_set_function (gc, GDK_COPY);
1598                   gdk_gc_set_fill (gc, GDK_SOLID);
1599
1600                   continue;
1601                 }
1602             }
1603
1604           gdk_color_parse (guide_type_get_color (guide->type, is_current),
1605                            &values.foreground);
1606
1607           gdk_gc_set_rgb_fg_color (gc, &values.foreground);
1608
1609           num_baselines = test_case_eval_guide (test, guide, &extends, &baselines);
1610
1611           if (num_baselines > 0)
1612             {
1613               g_assert (NULL != baselines);
1614
1615               if (show_baselines)
1616                 for (i = 0; i < num_baselines; ++i)
1617                   draw_baselines (drawable, gc, test->widget, &extends, baselines[i]);
1618             }
1619           else if (num_baselines > -1)
1620             {
1621               if ((show_interiour && (
1622                    guide->type == GUIDE_INTERIOUR_VERTICAL ||
1623                    guide->type == GUIDE_INTERIOUR_HORIZONTAL ||
1624                    guide->type == GUIDE_INTERIOUR_BOTH)) ||
1625                   (show_exteriour && (
1626                    guide->type == GUIDE_EXTERIOUR_VERTICAL ||
1627                    guide->type == GUIDE_EXTERIOUR_HORIZONTAL ||
1628                    guide->type == GUIDE_EXTERIOUR_BOTH)))
1629                 draw_extends (drawable, gc, test->widget, &extends);
1630             }
1631
1632           g_free (baselines);
1633         }
1634     }
1635
1636   g_object_unref (gc);
1637   test->idle = 0;
1638
1639   return FALSE;
1640 }
1641
1642 static gboolean           
1643 expose_cb (GtkWidget      *widget,
1644            GdkEventExpose *event,
1645            gpointer        data)
1646 {
1647   TestCase *test = data;
1648
1649   if (0 == test->idle)
1650     {
1651       if (widget != test->widget)
1652         gtk_widget_queue_draw (test->widget);
1653
1654       test->idle = g_idle_add (draw_guides, test);
1655     }
1656
1657   return FALSE;
1658 }
1659
1660 static void
1661 realize_cb (GtkWidget *widget,
1662             gpointer   data)
1663 {
1664   TestCase *test = data;
1665
1666   if (widget->window != test->widget->window)
1667     g_signal_connect_after (widget, "expose-event",
1668                             G_CALLBACK (expose_cb), test);
1669 }
1670
1671 static void
1672 attach_sub_windows (GtkWidget *widget,
1673                     gpointer   data)
1674 {
1675   g_signal_connect_after (widget, "realize", G_CALLBACK (realize_cb), data);
1676
1677   if (GTK_IS_CONTAINER (widget))
1678     gtk_container_forall (GTK_CONTAINER (widget), attach_sub_windows, data);
1679 }
1680
1681 static void
1682 test_suite_insert_page (TestSuite   *self, 
1683                         TestCase    *test,
1684                         GtkWidget   *widget,
1685                         const gchar *label)
1686 {
1687   GtkTreeModel *model = GTK_TREE_MODEL (self->tests);
1688   TestCase *prev = NULL;
1689   GtkTreeIter iter;
1690   gint i, n_rows;
1691
1692   if (!widget && test)
1693     widget = test->widget;
1694
1695   g_return_if_fail (GTK_IS_WIDGET (widget));
1696
1697   n_rows = gtk_tree_model_iter_n_children (model, NULL);
1698
1699   gtk_notebook_insert_page (GTK_NOTEBOOK (self->notebook), 
1700                             widget, NULL, self->n_test_cases);
1701
1702   gtk_list_store_insert (self->tests, &iter, n_rows);
1703
1704   gtk_list_store_set (self->tests, &iter,
1705                       TEST_COLUMN_LABEL, label, 
1706                       TEST_COLUMN_SELECTED, NULL != test, 
1707                       TEST_COLUMN_HAS_TEST_CASE, NULL != test, 
1708                       TEST_COLUMN_PAGE_INDEX, self->n_test_cases,
1709                       TEST_COLUMN_TEST_CASE, test,
1710                       -1);
1711
1712   for (i = n_rows - 1; i >= 0 && NULL == prev &&
1713        gtk_tree_model_iter_nth_child (model, &iter, NULL, i);
1714        --i)
1715     gtk_tree_model_get (GTK_TREE_MODEL (self->tests), &iter,
1716                         TEST_COLUMN_TEST_CASE, &prev, 
1717                         -1);
1718
1719   if (NULL == test || (prev && strcmp (test->name, prev->name)))
1720     {
1721       gtk_list_store_insert (self->tests, &iter, n_rows);
1722       gtk_list_store_set (self->tests, &iter,
1723                           TEST_COLUMN_HAS_TEST_CASE, FALSE,
1724                           TEST_COLUMN_PAGE_INDEX, -1,
1725                           -1);
1726     }
1727
1728   if (test)
1729     ++self->n_test_cases;
1730 }
1731
1732 static void
1733 test_suite_append (TestSuite *self,
1734                    TestCase  *test)
1735 {
1736   GString *markup = g_string_new (test->name);
1737
1738   g_string_printf (markup, "<b>%s</b>", test->name);
1739
1740   if (test->detail)
1741     g_string_append_printf (markup, "\n<small>%s</small>", test->detail);
1742
1743   test_suite_insert_page (self, test, NULL, markup->str);
1744   g_string_free (markup, TRUE);
1745
1746   g_signal_connect_after (test->widget, "expose-event",
1747                           G_CALLBACK (expose_cb), test);
1748   g_signal_connect_after (test->widget, "realize",
1749                           G_CALLBACK (realize_cb), test);
1750   g_object_set_data_full (G_OBJECT(test->widget), 
1751                           "test-case", test, g_free);
1752
1753   gtk_container_forall (GTK_CONTAINER (test->widget),
1754                         attach_sub_windows, test);
1755 }
1756
1757 static void 
1758 realize_notebook_cb (GtkWidget *widget,
1759                      gpointer   data)
1760 {
1761   TestSuite *suite = data;
1762
1763   suite->tile =
1764     gdk_pixmap_colormap_create_from_xpm_d (
1765     suite->notebook->window, NULL, NULL, NULL,
1766     mask_xpm);
1767 }
1768
1769 static void
1770 test_suite_free (TestSuite* self)
1771 {       
1772   g_object_unref (self->tile);
1773   g_free (self);
1774 }
1775
1776 static void
1777 test_suite_start (TestSuite *self)
1778 {
1779   if (0 == self->level++)
1780     {
1781       g_print ("\033[1mStarting test suite.\033[0m\n");
1782       gtk_tree_store_clear (self->results);
1783     }
1784 }
1785
1786 static void
1787 test_suite_stop (TestSuite *self)
1788 {
1789   if (0 == --self->level)
1790     {
1791       gtk_notebook_set_current_page (GTK_NOTEBOOK (self->notebook), 
1792                                      self->n_test_cases);
1793       g_print ("\033[1mTest suite stopped.\033[0m\n");
1794     }
1795 }
1796
1797 static const gchar*
1798 test_result_to_string (TestResult result)
1799 {
1800   switch (result)
1801   {
1802     case TEST_RESULT_NONE: return NULL;
1803     case TEST_RESULT_SUCCESS: return "SUCCESS";
1804     case TEST_RESULT_FAILURE: return "FAILURE";
1805   }
1806
1807   g_return_val_if_reached (NULL);
1808 }
1809
1810 static const gchar*
1811 test_result_to_icon (TestResult result)
1812 {
1813   switch (result)
1814   {
1815     case TEST_RESULT_NONE: return GTK_STOCK_EXECUTE;
1816     case TEST_RESULT_SUCCESS: return GTK_STOCK_OK;
1817     case TEST_RESULT_FAILURE: return GTK_STOCK_DIALOG_ERROR;
1818   }
1819
1820   g_return_val_if_reached (NULL);
1821 }
1822
1823 static void
1824 test_suite_report (TestSuite   *self,
1825                    const gchar *message,
1826                    gint         group,
1827                    TestResult   result)
1828 {
1829   const gchar *text = test_result_to_string (result);
1830   const gchar *icon = test_result_to_icon (result);
1831
1832   GtkTreeIter iter;
1833
1834   if (message)
1835     {
1836       PangoWeight weight = PANGO_WEIGHT_NORMAL;
1837       GtkTreePath *path;
1838
1839       if (TEST_RESULT_NONE != result)
1840         {
1841           g_print ("   - %s: %s\n", message, text);
1842           gtk_tree_store_append (self->results, &iter, &self->parent);
1843         }
1844       else if (group < 0)
1845         {
1846           g_print ("\033[1mTesting: %s\033[0m\n", message);
1847           gtk_tree_store_append (self->results, &self->parent, NULL);
1848           weight = PANGO_WEIGHT_BOLD;
1849           iter = self->parent;
1850
1851         }
1852       else
1853         {
1854           if (gtk_tree_store_iter_depth (self->results, &self->parent) < 1 ||
1855               !gtk_tree_model_iter_parent (GTK_TREE_MODEL (self->results), &iter, &self->parent))
1856               iter = self->parent;
1857
1858           g_print (" * %s\n", message);
1859           gtk_tree_store_append (self->results, &self->parent, &iter);
1860           iter = self->parent;
1861         }
1862
1863       gtk_tree_store_set (self->results, &iter, 
1864                           RESULT_COLUMN_MESSAGE, message, 
1865                           RESULT_COLUMN_WEIGHT, weight, 
1866                           RESULT_COLUMN_RESULT, text, 
1867                           RESULT_COLUMN_ICON, icon,
1868                           -1);
1869
1870       if (TEST_RESULT_FAILURE == result)
1871         {
1872           path = gtk_tree_model_get_path (GTK_TREE_MODEL (self->results), &iter);
1873           gtk_tree_view_expand_to_path (GTK_TREE_VIEW (self->results_view), path);
1874           gtk_tree_path_free (path);
1875         }
1876     }
1877   else
1878     {
1879       if (-1 == group && gtk_tree_model_iter_parent (
1880           GTK_TREE_MODEL (self->results), &iter, &self->parent))
1881         self->parent = iter;
1882
1883       gtk_tree_store_set (self->results, &self->parent,
1884                           RESULT_COLUMN_RESULT, text, 
1885                           RESULT_COLUMN_ICON, icon,
1886                           -1);
1887     }
1888 }
1889
1890 static void
1891 test_suite_run (TestSuite *self,
1892                 gint       index)
1893 {
1894   GtkNotebook *notebook;
1895   GtkWidget *page;
1896   TestCase *test;
1897
1898   notebook = GTK_NOTEBOOK (self->notebook);
1899
1900   if (-1 == index)
1901     index = gtk_notebook_get_current_page (notebook);
1902
1903   page = gtk_notebook_get_nth_page (notebook, index);
1904   test = g_object_get_data (G_OBJECT (page), "test-case");
1905
1906   if (NULL != test)
1907     {
1908       TestResult test_result = TEST_RESULT_SUCCESS;
1909       gint last_group = -1;
1910       gchar *message;
1911       GList *oiter;
1912       gint o, group;
1913
1914       message = test->detail ?
1915         g_strdup_printf ("%s (%s)", test->name, test->detail) :
1916         g_strdup (test->name);
1917
1918       test_suite_start (self);
1919       test_suite_report (self, message, -1, TEST_RESULT_NONE);
1920
1921       g_free (message);
1922
1923       for (oiter = test->guides; oiter; oiter = oiter->next)
1924         last_group = MAX (last_group, ((const Guide*)oiter->data)->group);
1925
1926       for (group = 0; group <= last_group; ++group)
1927         {
1928           const Guide *oguide;
1929
1930           for (o = 0, oiter = test->guides; oiter; ++o, oiter = oiter->next)
1931             {
1932               oguide = oiter->data;
1933
1934               if (oguide->group == group)
1935                 break;
1936             }
1937
1938           if (oiter)
1939             {
1940               TestResult group_result = TEST_RESULT_SUCCESS;
1941               const gchar *widget_name;
1942               const gchar *type_name;
1943
1944               GList *iiter;
1945               gint i;
1946
1947               widget_name = gtk_widget_get_name (oguide->widget);
1948               type_name = G_OBJECT_TYPE_NAME (oguide->widget);
1949
1950               message = g_strdup_printf (
1951                 "Group %d, Guide %d (%s%s%s)",
1952                 oguide->group, o, type_name,
1953                 strcmp (type_name, widget_name) ? ": " : "",
1954                 strcmp (type_name, widget_name) ? widget_name : "");
1955
1956               test_suite_report (self, message, oguide->group, TEST_RESULT_NONE);
1957               g_free (message);
1958
1959               for(i = 0, iiter = test->guides; iiter; ++i, iiter = iiter->next)
1960                 {
1961                   const Guide *iguide = iiter->data;
1962
1963                   if (iguide->group == oguide->group)
1964                     {
1965                       widget_name = gtk_widget_get_name (iguide->widget);
1966                       type_name = G_OBJECT_TYPE_NAME (iguide->widget);
1967
1968                       message = g_strdup_printf (
1969                         "Guide %d (%s%s%s)", i, type_name,
1970                         strcmp (type_name, widget_name) ? ": " : "",
1971                         strcmp (type_name, widget_name) ? widget_name : "");
1972
1973                       if (test_case_compare_guides (test, oguide, iguide))
1974                         {
1975                           test_suite_report (self, message, oguide->group, TEST_RESULT_SUCCESS);
1976                         }
1977                       else
1978                         {
1979                           test_suite_report (self, message, oguide->group, TEST_RESULT_FAILURE);
1980                           group_result = TEST_RESULT_FAILURE;
1981                           test_result = TEST_RESULT_FAILURE;
1982                         }
1983
1984                       g_free (message);
1985                     }
1986                 } 
1987
1988               test_suite_report (self, NULL, oguide->group, group_result);
1989             }
1990         }
1991
1992       test_suite_report (self, NULL, -1, test_result);
1993       test_suite_stop (self);
1994     }
1995 }
1996
1997 static void
1998 test_current_cb (GtkWidget *widget,
1999                  gpointer  data)
2000 {
2001   TestSuite *suite = data;
2002   test_suite_run (suite, -1);
2003 }
2004
2005 static void
2006 test_suite_show_and_run_test (TestSuite *self,
2007                               gint       page)
2008 {
2009   GTimer *timer = g_timer_new ();
2010
2011   gtk_notebook_set_current_page (GTK_NOTEBOOK (self->notebook), page);
2012   g_timer_start (timer);
2013
2014   while (g_timer_elapsed (timer, NULL) < 0.3 &&
2015          !gtk_main_iteration_do (FALSE))
2016     {
2017       if (!gtk_events_pending ())
2018         g_usleep (500);
2019     }
2020
2021   test_suite_run (self, -1);
2022   g_timer_destroy (timer);
2023 }
2024
2025 static void
2026 test_selected_cb (GtkWidget *widget,
2027                   gpointer   data)
2028 {
2029   TestSuite *suite = data;
2030   GtkTreeModel *model;
2031   GtkTreeIter iter;
2032
2033   model = GTK_TREE_MODEL (suite->tests);
2034   test_suite_start (suite);
2035
2036   if (gtk_tree_model_get_iter_first (model, &iter))
2037     {
2038       do
2039         {
2040           gboolean selected = FALSE;
2041           gint page_index = -1;
2042
2043           gtk_tree_model_get (model, &iter, 
2044                               TEST_COLUMN_SELECTED, &selected,
2045                               TEST_COLUMN_PAGE_INDEX, &page_index,
2046                               -1);
2047
2048           if (page_index >= 0 && selected)
2049             test_suite_show_and_run_test (suite, page_index);
2050         }
2051       while (gtk_tree_model_iter_next (model, &iter));
2052     }
2053
2054   test_suite_stop (suite);
2055 }
2056
2057 static void
2058 test_all_cb (GtkWidget *widget,
2059              gpointer  data)
2060 {
2061   TestSuite *suite = data;
2062   gint i;
2063
2064   test_suite_start (suite);
2065
2066   for (i = 0; i < suite->n_test_cases; ++i)
2067     test_suite_show_and_run_test (suite, i);
2068
2069   test_suite_stop (suite);
2070 }
2071
2072 static void
2073 switch_page_cb (GtkNotebook     *notebook,
2074                 GtkNotebookPage *page,
2075                 gint             index,
2076                 gpointer         data)
2077 {
2078   TestSuite *suite = data;
2079   GtkTreeModel *model;
2080   GtkTreeIter iter;
2081   gint page_index;
2082
2083   gtk_widget_set_sensitive (suite->test_current_button,
2084                             index < suite->n_test_cases);
2085
2086   model = GTK_TREE_MODEL (suite->tests);
2087
2088   if (gtk_tree_model_get_iter_first (model, &iter))
2089     {
2090       do
2091         {
2092           gtk_tree_model_get (model, &iter, 
2093                               TEST_COLUMN_PAGE_INDEX, &page_index,
2094                               -1);
2095
2096           if (page_index == index)
2097             {
2098               gtk_tree_selection_select_iter (suite->selection, &iter);
2099               break;
2100             }
2101         }
2102       while (gtk_tree_model_iter_next (model, &iter));
2103     }
2104 }
2105
2106 static GtkWidget*
2107 find_widget_at_position (GtkWidget *widget,
2108                          gint       x,
2109                          gint       y)
2110 {
2111   if (x < 0 || x >= widget->allocation.width ||
2112       y < 0 || y >= widget->allocation.height)
2113     return NULL;
2114
2115   if (GTK_IS_CONTAINER (widget))
2116     {
2117       GtkWidget *child;
2118       GList *children;
2119       GList *iter;
2120
2121       gint rx, ry;
2122
2123       children = gtk_container_get_children (GTK_CONTAINER (widget));
2124
2125       for (iter = children; iter; iter = iter->next)
2126         {
2127           gtk_widget_translate_coordinates (widget, iter->data, x, y, &rx, &ry);
2128           child = find_widget_at_position (iter->data, rx, ry);
2129
2130           if (child)
2131             {
2132               widget = child;
2133               break;
2134             }
2135         }
2136
2137       g_list_free (children);
2138     }
2139
2140   return widget;
2141 }
2142
2143 static void
2144 queue_redraw (GtkWidget *page, 
2145               GtkWidget *child)
2146 {
2147   gint x, y;
2148
2149   gtk_widget_translate_coordinates (child, page, 0, 0, &x, &y);
2150
2151   gtk_widget_queue_draw_area (page,
2152                               page->allocation.x,
2153                               page->allocation.y + y,
2154                               page->allocation.width,
2155                               child->allocation.height);
2156   gtk_widget_queue_draw_area (page,
2157                               page->allocation.x + x,
2158                               page->allocation.y,
2159                               child->allocation.width,
2160                               page->allocation.height);
2161 }
2162
2163 static gboolean           
2164 watch_pointer_cb (gpointer data)
2165 {
2166   TestSuite *suite = data;
2167   GtkWidget *prev = suite->current;
2168   TestCase *test = NULL;
2169
2170   gboolean dirty;
2171   GtkWidget *page;
2172   GtkWidget *child;
2173   gint i, x, y;
2174
2175   i = gtk_notebook_get_current_page (GTK_NOTEBOOK (suite->notebook));
2176   page = gtk_notebook_get_nth_page (GTK_NOTEBOOK (suite->notebook), i);
2177
2178   gtk_widget_get_pointer (page, &x, &y);
2179   child = find_widget_at_position (page, x, y);
2180
2181   while (child)
2182     {
2183       test = g_object_get_data (G_OBJECT(child), "test-case");
2184
2185       if (test)
2186         break;
2187
2188       child = gtk_widget_get_parent (child);
2189     }
2190
2191   dirty = suite->current && !(suite->timestamp % 3);
2192   suite->timestamp = (suite->timestamp + 1) % 6;
2193
2194   if (!test)
2195     {
2196       dirty = (NULL != suite->current);
2197
2198       if (suite->current)
2199         gtk_label_set_text (GTK_LABEL (suite->statusbar),
2200                             "No widget selected.\n");
2201
2202       suite->current = NULL;
2203     }
2204   else if (child != suite->hover)
2205     {
2206       if (child != suite->current)
2207         update_status (suite, child);
2208
2209       suite->current = child;
2210       suite->hover = child;
2211       dirty = TRUE;
2212     }
2213
2214   if (dirty)
2215     {
2216       if (suite->current)
2217         {
2218           if (prev && prev != suite->current)
2219             queue_redraw (page, prev);
2220
2221           queue_redraw (page, suite->current);
2222         }
2223       else
2224         {
2225           gtk_widget_queue_draw (page);
2226         }
2227     }
2228
2229   return TRUE;
2230 }
2231
2232 static gboolean
2233 button_press_event_cb (GtkWidget      *widget,
2234                        GdkEventButton *event,
2235                        gpointer        data)
2236 {
2237   TestSuite *suite = data;
2238   GtkWidget *popup = NULL;
2239   GtkWidget *page;
2240   gint i;
2241
2242   if (3 != event->button)
2243     return FALSE;
2244
2245   i = gtk_notebook_get_current_page (GTK_NOTEBOOK (suite->notebook));
2246   page = gtk_notebook_get_nth_page (GTK_NOTEBOOK (suite->notebook), i);
2247   popup = g_object_get_data (G_OBJECT (page), "popup");
2248
2249   gtk_menu_popup (GTK_MENU (popup),
2250                   NULL, NULL, NULL, NULL,
2251                   event->button, event->time);
2252
2253   return TRUE;
2254 }
2255
2256 static void
2257 test_suite_setup_results_page (TestSuite *self)
2258 {
2259   GtkTreeViewColumn *column;
2260   GtkCellRenderer *cell;
2261   GtkWidget *scroller;
2262
2263   self->results = gtk_tree_store_new (RESULT_COLUNN_COUNT,
2264                                       G_TYPE_STRING, PANGO_TYPE_WEIGHT,
2265                                       G_TYPE_STRING, G_TYPE_STRING);
2266
2267   self->results_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (self->results));
2268   gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (self->results_view), FALSE);
2269
2270   column = gtk_tree_view_column_new ();
2271   gtk_tree_view_column_set_expand (column, TRUE);
2272   gtk_tree_view_append_column (GTK_TREE_VIEW (self->results_view), column);
2273
2274   cell = gtk_cell_renderer_pixbuf_new ();
2275   gtk_tree_view_column_pack_start (column, cell, FALSE);
2276   gtk_tree_view_column_set_attributes (column, cell, 
2277                                        "icon-name", RESULT_COLUMN_ICON, NULL);
2278
2279   cell = gtk_cell_renderer_text_new ();
2280   gtk_tree_view_column_pack_start (column, cell, TRUE);
2281   gtk_tree_view_column_set_attributes (column, cell, 
2282                                        "text", RESULT_COLUMN_MESSAGE,
2283                                        "weight", RESULT_COLUMN_WEIGHT, NULL);
2284
2285   column = gtk_tree_view_column_new ();
2286   gtk_tree_view_column_set_expand (column, FALSE);
2287   gtk_tree_view_append_column (GTK_TREE_VIEW (self->results_view), column);
2288
2289   cell = gtk_cell_renderer_text_new ();
2290   gtk_tree_view_column_pack_start (column, cell, TRUE);
2291   gtk_tree_view_column_set_attributes (column, cell, 
2292                                        "text", RESULT_COLUMN_RESULT, NULL);
2293
2294   scroller = gtk_scrolled_window_new (NULL, NULL);
2295   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroller),
2296                                   GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
2297   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroller),
2298                                        GTK_SHADOW_IN);
2299   gtk_container_set_border_width (GTK_CONTAINER (scroller), 12);
2300   gtk_container_add (GTK_CONTAINER (scroller), self->results_view);
2301
2302   test_suite_insert_page (self, NULL, scroller, "<b>Test Results</b>");
2303
2304   g_signal_connect (self->notebook, "realize",
2305                     G_CALLBACK (realize_notebook_cb), self);
2306 }
2307
2308 static gboolean
2309 tests_is_separator (GtkTreeModel *model,
2310                     GtkTreeIter  *iter,
2311                     gpointer      data)
2312 {
2313   gchar *label;
2314
2315   gtk_tree_model_get (model, iter, TEST_COLUMN_LABEL, &label, -1);
2316   g_free (label);
2317
2318   return (NULL == label);
2319 }
2320
2321 static void
2322 test_case_toggled (GtkCellRendererToggle *cell,
2323                    gchar                 *path,
2324                    gpointer               data)
2325 {
2326   GtkTreeIter iter;
2327
2328   if (gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (data), &iter, path))
2329     gtk_list_store_set (GTK_LIST_STORE (data), &iter, TEST_COLUMN_SELECTED,
2330                         !gtk_cell_renderer_toggle_get_active (cell),
2331                         -1);
2332 }
2333
2334 static void
2335 selection_changed (GtkTreeSelection *selection,
2336                    gpointer          data)
2337 {
2338   TestSuite *suite = data;
2339   GtkTreeModel *model;
2340   GtkTreeIter iter;
2341   gint page_index;
2342
2343   if (gtk_tree_selection_get_selected (selection, &model, &iter))
2344     {
2345       gtk_tree_model_get (model, &iter, 
2346                           TEST_COLUMN_PAGE_INDEX, &page_index, 
2347                           -1);
2348
2349       if (page_index >= 0)
2350         gtk_notebook_set_current_page (GTK_NOTEBOOK (suite->notebook),
2351                                        page_index);
2352     }
2353 }
2354
2355 static void
2356 test_suite_setup_ui (TestSuite *self)
2357 {
2358   GtkWidget *table, *actions, *button, *align;
2359   GtkWidget *view, *scrolled;
2360
2361   GtkTreeViewColumn *column;
2362   GtkCellRenderer *cell;
2363
2364   self->tests = gtk_list_store_new (TEST_COLUMN_COUNT, 
2365                                     G_TYPE_STRING, G_TYPE_BOOLEAN,
2366                                     G_TYPE_POINTER, G_TYPE_BOOLEAN, 
2367                                     G_TYPE_INT);
2368
2369   view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (self->tests));
2370   gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (view), FALSE);
2371   gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (view), tests_is_separator, NULL, NULL);
2372
2373   self->selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
2374   gtk_tree_selection_set_mode (self->selection, GTK_SELECTION_BROWSE);
2375
2376   g_signal_connect (self->selection, "changed", G_CALLBACK (selection_changed), self);
2377
2378   column = gtk_tree_view_column_new ();
2379   cell = gtk_cell_renderer_toggle_new ();
2380   gtk_tree_view_append_column (GTK_TREE_VIEW (view), column);
2381   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (column), cell, FALSE);
2382   gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (column), cell,
2383                                   "active", TEST_COLUMN_SELECTED, 
2384                                   "activatable", TEST_COLUMN_HAS_TEST_CASE,
2385                                   "visible", TEST_COLUMN_HAS_TEST_CASE,
2386                                   NULL);
2387
2388   g_signal_connect (cell, "toggled", G_CALLBACK (test_case_toggled), self->tests);
2389
2390   column = gtk_tree_view_column_new ();
2391   cell = gtk_cell_renderer_text_new ();
2392   gtk_tree_view_append_column (GTK_TREE_VIEW (view), column);
2393   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (column), cell, TRUE);
2394   gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (column), cell,
2395                                   "markup", TEST_COLUMN_LABEL, NULL);
2396
2397   scrolled = gtk_scrolled_window_new (NULL, NULL);
2398   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled),
2399                                        GTK_SHADOW_IN);
2400   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
2401                                   GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
2402   gtk_container_add (GTK_CONTAINER (scrolled), view);
2403
2404   self->notebook = gtk_notebook_new ();
2405   gtk_notebook_set_show_tabs (GTK_NOTEBOOK (self->notebook), FALSE);
2406
2407   actions = gtk_hbox_new (TRUE, 12);
2408
2409   align = gtk_alignment_new (1.0, 0.5, 0.0, 0.0);
2410   gtk_container_add (GTK_CONTAINER (align), actions);
2411
2412   button = gtk_button_new_with_mnemonic ("Test _Current Page");
2413   g_signal_connect (button, "clicked", G_CALLBACK (test_current_cb), self);
2414   gtk_box_pack_start (GTK_BOX (actions), button, FALSE, TRUE, 0);
2415
2416   self->test_current_button = button;
2417   g_signal_connect (self->notebook, "switch-page", G_CALLBACK (switch_page_cb), self);
2418
2419   button = gtk_button_new_with_mnemonic ("Test _Selected Pages");
2420   g_signal_connect (button, "clicked", G_CALLBACK (test_selected_cb), self);
2421   gtk_box_pack_start (GTK_BOX (actions), button, FALSE, TRUE, 0);
2422
2423   button = gtk_button_new_with_mnemonic ("Test _All Pages");
2424   g_signal_connect (button, "clicked", G_CALLBACK (test_all_cb), self);
2425   gtk_box_pack_start (GTK_BOX (actions), button, FALSE, TRUE, 0);
2426
2427   actions = gtk_hbox_new (FALSE, 12);
2428   gtk_box_pack_end (GTK_BOX (actions), align, TRUE, TRUE, 0);
2429
2430   gtk_box_pack_start (GTK_BOX (actions),
2431                       gtk_label_new ("Guides:"),
2432                       FALSE, TRUE, 0);
2433
2434   self->baselines = gtk_check_button_new_with_mnemonic ("_Baselines");
2435   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->baselines), TRUE);
2436   gtk_box_pack_start (GTK_BOX (actions), self->baselines, FALSE, TRUE, 0);
2437
2438   g_signal_connect_swapped (self->baselines, "toggled", 
2439                             G_CALLBACK (gtk_widget_queue_draw),
2440                             self->notebook);
2441
2442   self->interiour = gtk_check_button_new_with_mnemonic ("_Interiours");
2443   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->interiour), TRUE);
2444   gtk_box_pack_start (GTK_BOX (actions), self->interiour, FALSE, TRUE, 0);
2445
2446   g_signal_connect_swapped (self->interiour, "toggled", 
2447                             G_CALLBACK (gtk_widget_queue_draw),
2448                             self->notebook);
2449   
2450   self->exteriour = gtk_check_button_new_with_mnemonic ("_Exteriours");
2451   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->exteriour), TRUE);
2452   gtk_box_pack_start (GTK_BOX (actions), self->exteriour, FALSE, TRUE, 0);
2453   
2454   g_signal_connect_swapped (self->exteriour, "toggled", 
2455                             G_CALLBACK (gtk_widget_queue_draw),
2456                             self->notebook);
2457
2458   self->statusbar = gtk_label_new ("No widget selected.\n");
2459   gtk_misc_set_alignment (GTK_MISC (self->statusbar), 0.0, 0.5);
2460   gtk_label_set_ellipsize (GTK_LABEL (self->statusbar),
2461                            PANGO_ELLIPSIZE_END);
2462
2463   table = gtk_table_new (3, 2, FALSE);
2464
2465   gtk_table_set_col_spacings (GTK_TABLE (table), 6);
2466   gtk_table_set_row_spacings (GTK_TABLE (table), 6);
2467   gtk_container_set_border_width (GTK_CONTAINER (table), 6);
2468
2469   gtk_table_attach (GTK_TABLE (table), actions, 0, 2, 0, 1, GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0);
2470   gtk_table_attach (GTK_TABLE (table), scrolled, 0, 1, 1, 2, GTK_FILL, GTK_FILL | GTK_EXPAND, 0, 0);
2471   gtk_table_attach (GTK_TABLE (table), self->notebook, 1, 2, 1, 2, GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 0, 0);
2472   gtk_table_attach (GTK_TABLE (table), self->statusbar, 0, 2, 2, 3, GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0);
2473
2474   self->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2475
2476   g_object_connect (self->window, 
2477     "signal::button-press-event", button_press_event_cb, self,
2478     "signal::destroy", gtk_main_quit, NULL,
2479     NULL);
2480
2481   g_timeout_add (200, watch_pointer_cb, self);
2482
2483   gtk_window_set_title (GTK_WINDOW (self->window), "Testing GtkExtendedLayout");
2484   gtk_widget_add_events (self->window, GDK_BUTTON_PRESS_MASK);
2485   gtk_container_add (GTK_CONTAINER (self->window), table);
2486   gtk_widget_grab_focus (view);
2487 }
2488
2489 static TestSuite*
2490 test_suite_new (gchar *arg0)
2491 {       
2492   TestSuite* self = g_new0 (TestSuite, 1);
2493
2494   test_suite_setup_ui (self);
2495
2496   test_suite_append (self, natural_size_test_new (self, FALSE, FALSE));
2497   test_suite_append (self, natural_size_test_new (self, TRUE, FALSE));
2498   test_suite_append (self, natural_size_test_new (self, FALSE, TRUE));
2499   test_suite_append (self, natural_size_test_new (self, TRUE, TRUE));
2500   test_suite_append (self, natural_size_test_misc_new (self, arg0));
2501   test_suite_append (self, size_for_allocation_test_new (self, TRUE, FALSE));
2502   test_suite_append (self, size_for_allocation_test_new (self, FALSE, FALSE));
2503   test_suite_append (self, size_for_allocation_test_new (self, TRUE, TRUE));
2504   test_suite_append (self, size_for_allocation_test_new (self, FALSE, TRUE));
2505   test_suite_append (self, baseline_test_new (self));
2506   test_suite_append (self, baseline_test_bin_new (self));
2507 #if 0
2508   test_suite_append (self, baseline_test_hbox_new (self, FALSE));
2509   test_suite_append (self, baseline_test_hbox_new (self, TRUE));
2510 #endif
2511
2512   test_suite_setup_results_page (self);
2513
2514   return self;
2515 }
2516
2517 static gboolean
2518 on_embedding_timeout (gpointer data)
2519 {
2520   GdkNativeWindow plug_id = GPOINTER_TO_INT (data);
2521   g_printerr ("Embedding timeout expired for plug %d. Aborting.\n", plug_id);
2522   gtk_main_quit ();
2523   return FALSE;
2524 }
2525
2526 static void
2527 on_embedded (GtkWidget *widget, 
2528              gpointer   data)
2529 {
2530   g_source_remove (GPOINTER_TO_INT (data));
2531 }
2532
2533 static void
2534 create_plug (gboolean ellipsize, 
2535              gboolean vertical)
2536 {
2537   GtkWidget *plug, *label;
2538   GdkNativeWindow plug_id;
2539   guint timeout;
2540
2541
2542   label = gtk_label_new ("Hello World");
2543   
2544   if (ellipsize)
2545     gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
2546   if (vertical)
2547     gtk_label_set_angle (GTK_LABEL (label), 90);
2548
2549   plug = gtk_plug_new (0);
2550   gtk_container_add (GTK_CONTAINER (plug), label);
2551   gtk_widget_show_all (plug);
2552
2553   plug_id = gtk_plug_get_id (GTK_PLUG (plug));
2554   timeout = g_timeout_add (5 * 1000, on_embedding_timeout,
2555                            GINT_TO_POINTER (plug_id));
2556
2557   g_signal_connect (plug, "embedded", 
2558                     G_CALLBACK (on_embedded),
2559                     GINT_TO_POINTER (timeout));
2560   g_signal_connect (plug, "delete-event",
2561                     G_CALLBACK (gtk_main_quit),
2562                     NULL);
2563
2564   g_print ("%d\n", plug_id);
2565 }
2566
2567 int
2568 main (int argc, char *argv[])
2569 {
2570   GOptionContext *options;
2571   TestSuite *suite = NULL;
2572   GError *error = NULL;
2573
2574   gboolean ellipsize = FALSE;
2575   gboolean vertical = FALSE;
2576   gint initial_page = 0;
2577   gchar *action = NULL;
2578   
2579   GOptionEntry entries[] = 
2580   {
2581     { "action", 'a', 0, G_OPTION_ARG_STRING, &action, "Action to perform", NULL },
2582     { "initial-page", 'p', 0, G_OPTION_ARG_INT, &initial_page, "Initial page of the test suite", NULL },
2583     { "ellipsize", 0, 0, G_OPTION_ARG_NONE, &ellipsize, "Add ellipses to labels", NULL },
2584     { "vertical", 0, 0, G_OPTION_ARG_NONE, &vertical, "Render vertical layout", NULL },
2585     { NULL }
2586   };
2587
2588   gtk_init (&argc, &argv);
2589
2590   options = g_option_context_new (NULL);
2591   g_option_context_add_main_entries (options, entries, NULL);
2592   g_option_context_add_group (options, gtk_get_option_group (TRUE));
2593   g_option_context_parse (options, &argc, &argv, &error);
2594
2595   if (error)
2596     {
2597       g_print ("Usage Error: %s\n", error->message);
2598       return 2;
2599     }
2600
2601   if (action && g_str_equal (action, "create-plug"))
2602     {
2603       create_plug (ellipsize, vertical);
2604     }
2605   else
2606     {
2607       suite = test_suite_new (argv[0]);
2608       gtk_widget_show_all (suite->window);
2609
2610       gtk_notebook_set_current_page (GTK_NOTEBOOK (suite->notebook),
2611                                      initial_page);
2612     }
2613
2614   gtk_main ();
2615
2616   if (suite)
2617     test_suite_free (suite);
2618
2619   return 0;
2620 }
2621
2622 /* vim: set sw=2 sta et: */