]> Pileus Git - ~andy/gtk/blob - demos/gtk-demo/main.c
stylecontext: Do invalidation on first resize container
[~andy/gtk] / demos / gtk-demo / main.c
1 #include "config.h"
2 #include <errno.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6
7 #include <gtk/gtk.h>
8 #include <glib/gstdio.h>
9
10 #include "demos.h"
11
12 static GtkWidget *info_view;
13 static GtkWidget *source_view;
14
15 static gchar *current_file = NULL;
16
17 static GtkWidget *notebook;
18
19 enum {
20   NAME_COLUMN,
21   TITLE_COLUMN,
22   FILENAME_COLUMN,
23   FUNC_COLUMN,
24   STYLE_COLUMN,
25   NUM_COLUMNS
26 };
27
28 typedef struct _CallbackData CallbackData;
29 struct _CallbackData
30 {
31   GtkTreeModel *model;
32   GtkTreePath *path;
33 };
34
35 static void
36 window_closed_cb (GtkWidget *window, gpointer data)
37 {
38   CallbackData *cbdata = data;
39   GtkTreeIter iter;
40   PangoStyle style;
41
42   gtk_tree_model_get_iter (cbdata->model, &iter, cbdata->path);
43   gtk_tree_model_get (GTK_TREE_MODEL (cbdata->model), &iter,
44                       STYLE_COLUMN, &style,
45                       -1);
46   if (style == PANGO_STYLE_ITALIC)
47     gtk_tree_store_set (GTK_TREE_STORE (cbdata->model), &iter,
48                         STYLE_COLUMN, PANGO_STYLE_NORMAL,
49                         -1);
50
51   gtk_tree_path_free (cbdata->path);
52   g_free (cbdata);
53 }
54
55 /* Stupid syntax highlighting.
56  *
57  * No regex was used in the making of this highlighting.
58  * It should only work for simple cases.  This is good, as
59  * that's all we should have in the demos.
60  */
61 /* This code should not be used elsewhere, except perhaps as an example of how
62  * to iterate through a text buffer.
63  */
64 enum {
65   STATE_NORMAL,
66   STATE_IN_COMMENT
67 };
68
69 static gchar *tokens[] =
70 {
71   "/*",
72   "\"",
73   NULL
74 };
75
76 static gchar *types[] =
77 {
78   "static",
79   "const ",
80   "void",
81   "gint",
82   " int ",
83   " char ",
84   "gchar ",
85   "gfloat",
86   "float",
87   "double",
88   "gint8",
89   "gint16",
90   "gint32",
91   "guint",
92   "guint8",
93   "guint16",
94   "guint32",
95   "guchar",
96   "glong",
97   "gboolean" ,
98   "gshort",
99   "gushort",
100   "gulong",
101   "gdouble",
102   "gldouble",
103   "gpointer",
104   "NULL",
105   "GList",
106   "GSList",
107   "FALSE",
108   "TRUE",
109   "FILE ",
110   "GtkColorSelection ",
111   "GtkWidget ",
112   "GtkButton ",
113   "GdkColor ",
114   "GdkRectangle ",
115   "GdkEventExpose ",
116   "GdkGC ",
117   "GdkPixbufLoader ",
118   "GdkPixbuf ",
119   "GError",
120   "size_t",
121   "GtkAboutDialog ",
122   "GtkAction ",
123   "GtkActionEntry ",
124   "GtkRadioActionEntry ",
125   "GtkIconFactory ",
126   "GtkStockItem ",
127   "GtkIconSet ",
128   "GtkTextBuffer ",
129   "GtkStatusbar ",
130   "GtkTextIter ",
131   "GtkTextMark ",
132   "GdkEventWindowState ",
133   "GtkActionGroup ",
134   "GtkUIManager ",
135   "GtkRadioAction ",
136   "GtkActionClass ",
137   "GtkToggleActionEntry ",
138   "GtkAssistant ",
139   "GtkBuilder ",
140   "GtkSizeGroup ",
141   "GtkTreeModel ",
142   "GtkTreeSelection ",
143   "GdkDisplay ",
144   "GdkScreen ",
145   "GdkWindow ",
146   "GdkEventButton ",
147   "GdkCursor ",
148   "GtkTreeIter ",
149   "GtkTreeViewColumn ",
150   "GdkDisplayManager ",
151   "GtkClipboard ",
152   "GtkIconSize ",
153   "GtkImage ",
154   "GdkDragContext ",
155   "GtkSelectionData ",
156   "GtkDialog ",
157   "GtkMenuItem ",
158   "GtkListStore ",
159   "GtkCellLayout ",
160   "GtkCellRenderer ",
161   "GtkTreePath ",
162   "GtkTreeStore ",
163   "GtkEntry ",
164   "GtkEditable ",
165   "GtkEditableInterface ",
166   "GdkPixmap ",
167   "GdkEventConfigure ",
168   "GdkEventMotion ",
169   "GdkModifierType ",
170   "GtkEntryCompletion ",
171   "GtkToolItem ",
172   "GDir ",
173   "GtkIconView ",
174   "GtkCellRendererText ",
175   "GtkContainer ",
176   "GtkAccelGroup ",
177   "GtkPaned ",
178   "GtkPrintOperation ",
179   "GtkPrintContext ",
180   "cairo_t ",
181   "PangoLayout "
182   "PangoFontDescription ",
183   "PangoRenderer ",
184   "PangoMatrix ",
185   "PangoContext ",
186   "PangoLayout ",
187   "GtkTable ",
188   "GtkToggleButton ",
189   "GString ",
190   "GtkIconSize ",
191   "GtkTreeView ",
192   "GtkTextTag ",
193   "GdkEvent ",
194   "GdkEventKey ",
195   "GtkTextView ",
196   "GdkEventVisibility ",
197   "GdkBitmap ",
198   "GtkTextChildAnchor ",
199   "GArray ",
200   "GtkCellEditable ",
201   "GtkCellRendererToggle ",
202   NULL
203 };
204
205 static gchar *control[] =
206 {
207   " if ",
208   " while ",
209   " else",
210   " do ",
211   " for ",
212   "?",
213   ":",
214   "return ",
215   "goto ",
216   NULL
217 };
218 void
219 parse_chars (gchar     *text,
220              gchar    **end_ptr,
221              gint      *state,
222              gchar    **tag,
223              gboolean   start)
224 {
225   gint i;
226   gchar *next_token;
227
228   /* Handle comments first */
229   if (*state == STATE_IN_COMMENT)
230     {
231       *end_ptr = strstr (text, "*/");
232       if (*end_ptr)
233         {
234           *end_ptr += 2;
235           *state = STATE_NORMAL;
236           *tag = "comment";
237         }
238       return;
239     }
240
241   *tag = NULL;
242   *end_ptr = NULL;
243
244   /* check for comment */
245   if (!strncmp (text, "/*", 2))
246     {
247       *end_ptr = strstr (text, "*/");
248       if (*end_ptr)
249         *end_ptr += 2;
250       else
251         *state = STATE_IN_COMMENT;
252       *tag = "comment";
253       return;
254     }
255
256   /* check for preprocessor defines */
257   if (*text == '#' && start)
258     {
259       *end_ptr = NULL;
260       *tag = "preprocessor";
261       return;
262     }
263
264   /* functions */
265   if (start && * text != '\t' && *text != ' ' && *text != '{' && *text != '}')
266     {
267       if (strstr (text, "("))
268         {
269           *end_ptr = strstr (text, "(");
270           *tag = "function";
271           return;
272         }
273     }
274   /* check for types */
275   for (i = 0; types[i] != NULL; i++)
276     if (!strncmp (text, types[i], strlen (types[i])) ||
277         (start && types[i][0] == ' ' && !strncmp (text, types[i] + 1, strlen (types[i]) - 1)))
278       {
279         *end_ptr = text + strlen (types[i]);
280         *tag = "type";
281         return;
282       }
283
284   /* check for control */
285   for (i = 0; control[i] != NULL; i++)
286     if (!strncmp (text, control[i], strlen (control[i])))
287       {
288         *end_ptr = text + strlen (control[i]);
289         *tag = "control";
290         return;
291       }
292
293   /* check for string */
294   if (text[0] == '"')
295     {
296       gint maybe_escape = FALSE;
297
298       *end_ptr = text + 1;
299       *tag = "string";
300       while (**end_ptr != '\000')
301         {
302           if (**end_ptr == '\"' && !maybe_escape)
303             {
304               *end_ptr += 1;
305               return;
306             }
307           if (**end_ptr == '\\')
308             maybe_escape = TRUE;
309           else
310             maybe_escape = FALSE;
311           *end_ptr += 1;
312         }
313       return;
314     }
315
316   /* not at the start of a tag.  Find the next one. */
317   for (i = 0; tokens[i] != NULL; i++)
318     {
319       next_token = strstr (text, tokens[i]);
320       if (next_token)
321         {
322           if (*end_ptr)
323             *end_ptr = (*end_ptr<next_token)?*end_ptr:next_token;
324           else
325             *end_ptr = next_token;
326         }
327     }
328
329   for (i = 0; types[i] != NULL; i++)
330     {
331       next_token = strstr (text, types[i]);
332       if (next_token)
333         {
334           if (*end_ptr)
335             *end_ptr = (*end_ptr<next_token)?*end_ptr:next_token;
336           else
337             *end_ptr = next_token;
338         }
339     }
340
341   for (i = 0; control[i] != NULL; i++)
342     {
343       next_token = strstr (text, control[i]);
344       if (next_token)
345         {
346           if (*end_ptr)
347             *end_ptr = (*end_ptr<next_token)?*end_ptr:next_token;
348           else
349             *end_ptr = next_token;
350         }
351     }
352 }
353
354 /* While not as cool as c-mode, this will do as a quick attempt at highlighting */
355 static void
356 fontify (GtkTextBuffer *source_buffer)
357 {
358   GtkTextIter start_iter, next_iter, tmp_iter;
359   gint state;
360   gchar *text;
361   gchar *start_ptr, *end_ptr;
362   gchar *tag;
363
364   state = STATE_NORMAL;
365
366   gtk_text_buffer_get_iter_at_offset (source_buffer, &start_iter, 0);
367
368   next_iter = start_iter;
369   while (gtk_text_iter_forward_line (&next_iter))
370     {
371       gboolean start = TRUE;
372       start_ptr = text = gtk_text_iter_get_text (&start_iter, &next_iter);
373
374       do
375         {
376           parse_chars (start_ptr, &end_ptr, &state, &tag, start);
377
378           start = FALSE;
379           if (end_ptr)
380             {
381               tmp_iter = start_iter;
382               gtk_text_iter_forward_chars (&tmp_iter, end_ptr - start_ptr);
383             }
384           else
385             {
386               tmp_iter = next_iter;
387             }
388           if (tag)
389             gtk_text_buffer_apply_tag_by_name (source_buffer, tag, &start_iter, &tmp_iter);
390
391           start_iter = tmp_iter;
392           start_ptr = end_ptr;
393         }
394       while (end_ptr);
395
396       g_free (text);
397       start_iter = next_iter;
398     }
399 }
400
401 static GtkWidget *create_text (GtkWidget **text_view, gboolean is_source);
402
403 static void
404 add_data_tab (const gchar *demoname)
405 {
406   gchar *resource_dir, *resource_name, *content_type;
407   gchar **resources;
408   GBytes *bytes;
409   GtkWidget *widget, *label;
410   guint i;
411
412   resource_dir = g_strconcat ("/", demoname, NULL);
413   resources = g_resources_enumerate_children (resource_dir, 0, NULL);
414   if (resources == NULL)
415     {
416       g_free (resource_dir);
417       return;
418     }
419
420   for (i = 0; resources[i]; i++)
421     {
422       resource_name = g_strconcat (resource_dir, "/", resources[i], NULL);
423       bytes = g_resources_lookup_data (resource_name, 0, NULL);
424       g_assert (bytes);
425
426       content_type = g_content_type_guess (resource_name,
427                                            g_bytes_get_data (bytes, NULL),
428                                            g_bytes_get_size (bytes),
429                                            NULL);
430
431       /* In theory we should look at all the mime types gdk-pixbuf supports
432        * and go from there, but we know what file types we've added.
433        */
434       if (g_content_type_is_a (content_type, "image/png") ||
435           g_content_type_is_a (content_type, "image/gif") ||
436           g_content_type_is_a (content_type, "image/jpeg"))
437         {
438           widget = gtk_image_new_from_resource (resource_name);
439         }
440       else if (g_content_type_is_a (content_type, "text/plain"))
441         {
442           GtkTextBuffer *buffer;
443           GtkWidget *textview;
444
445           widget = create_text (&textview, FALSE);
446           buffer = gtk_text_buffer_new (NULL);
447           gtk_text_buffer_set_text (buffer, g_bytes_get_data (bytes, NULL), g_bytes_get_size (bytes));
448           gtk_text_view_set_buffer (GTK_TEXT_VIEW (textview), buffer);
449         }
450       else
451         {
452           g_warning ("Don't know how to display resource '%s' of type '%s'\n", resource_name, content_type);
453           widget = NULL;
454         }
455
456       gtk_widget_show_all (widget);
457       label = gtk_label_new (resources[i]);
458       gtk_widget_show (label);
459       gtk_notebook_append_page (GTK_NOTEBOOK (notebook), widget, label);
460
461       g_free (content_type);
462       g_free (resource_name);
463       g_bytes_unref (bytes);
464     }
465
466   g_strfreev (resources);
467   g_free (resource_dir);
468 }
469
470 static void
471 remove_data_tabs (void)
472 {
473   gint i;
474
475   for (i = gtk_notebook_get_n_pages (GTK_NOTEBOOK (notebook)) - 1; i > 1; i--)
476     gtk_notebook_remove_page (GTK_NOTEBOOK (notebook), i);
477 }
478
479 void
480 load_file (const gchar *demoname,
481            const gchar *filename)
482 {
483   GtkTextBuffer *info_buffer, *source_buffer;
484   GtkTextIter start, end;
485   char *resource_filename;
486   GError *err = NULL;
487   int state = 0;
488   gboolean in_para = 0;
489   gchar **lines;
490   GBytes *bytes;
491   gint i;
492
493   if (!g_strcmp0 (current_file, filename))
494     return;
495
496   remove_data_tabs ();
497
498   add_data_tab (demoname);
499
500   g_free (current_file);
501   current_file = g_strdup (filename);
502
503   info_buffer = gtk_text_buffer_new (NULL);
504   gtk_text_buffer_create_tag (info_buffer, "title",
505                               "font", "Sans 18",
506                               "pixels-below-lines", 10,
507                               NULL);
508
509   source_buffer = gtk_text_buffer_new (NULL);
510   gtk_text_buffer_create_tag (source_buffer, "comment",
511                               "foreground", "DodgerBlue",
512                               NULL);
513   gtk_text_buffer_create_tag (source_buffer, "type",
514                               "foreground", "ForestGreen",
515                               NULL);
516   gtk_text_buffer_create_tag (source_buffer, "string",
517                               "foreground", "RosyBrown",
518                               "weight", PANGO_WEIGHT_BOLD,
519                               NULL);
520   gtk_text_buffer_create_tag (source_buffer, "control",
521                               "foreground", "purple",
522                               NULL);
523   gtk_text_buffer_create_tag (source_buffer, "preprocessor",
524                               "style", PANGO_STYLE_OBLIQUE,
525                               "foreground", "burlywood4",
526                               NULL);
527   gtk_text_buffer_create_tag (source_buffer, "function",
528                               "weight", PANGO_WEIGHT_BOLD,
529                               "foreground", "DarkGoldenrod4",
530                               NULL);
531
532   resource_filename = g_strconcat ("/sources/", filename, NULL);
533   bytes = g_resources_lookup_data (resource_filename, 0, &err);
534   g_free (resource_filename);
535
536   if (bytes == NULL)
537     {
538       g_warning ("Cannot open source for %s: %s\n", filename, err->message);
539       g_error_free (err);
540       return;
541     }
542
543   lines = g_strsplit (g_bytes_get_data (bytes, NULL), "\n", -1);
544   g_bytes_unref (bytes);
545
546   gtk_text_buffer_get_iter_at_offset (info_buffer, &start, 0);
547   for (i = 0; lines[i] != NULL; i++)
548     {
549       gchar *p;
550       gchar *q;
551       gchar *r;
552
553       /* Make sure \r is stripped at the end for the poor windows people */
554       lines[i] = g_strchomp (lines[i]);
555
556       p = lines[i];
557       switch (state)
558         {
559         case 0:
560           /* Reading title */
561           while (*p == '/' || *p == '*' || g_ascii_isspace (*p))
562             p++;
563           r = p;
564           while (*r != '\0')
565             {
566               while (*r != '/' && *r != ':' && *r != '\0')
567                 r++;
568               if (*r == '/')
569                 {
570                   r++;
571                   p = r;
572                 }
573               if (r[0] == ':' && r[1] == ':')
574                 *r = '\0';
575             }
576           q = p + strlen (p);
577           while (q > p && g_ascii_isspace (*(q - 1)))
578             q--;
579
580
581           if (q > p)
582             {
583               int len_chars = g_utf8_pointer_to_offset (p, q);
584
585               end = start;
586
587               g_assert (strlen (p) >= q - p);
588               gtk_text_buffer_insert (info_buffer, &end, p, q - p);
589               start = end;
590
591               gtk_text_iter_backward_chars (&start, len_chars);
592               gtk_text_buffer_apply_tag_by_name (info_buffer, "title", &start, &end);
593
594               start = end;
595
596               while (*p && *p != '\n') p++;
597
598               state++;
599             }
600           break;
601
602         case 1:
603           /* Reading body of info section */
604           while (g_ascii_isspace (*p))
605             p++;
606           if (*p == '*' && *(p + 1) == '/')
607             {
608               gtk_text_buffer_get_iter_at_offset (source_buffer, &start, 0);
609               state++;
610             }
611           else
612             {
613               int len;
614
615               while (*p == '*' || g_ascii_isspace (*p))
616                 p++;
617
618               len = strlen (p);
619               while (g_ascii_isspace (*(p + len - 1)))
620                 len--;
621
622               if (len > 0)
623                 {
624                   if (in_para)
625                     gtk_text_buffer_insert (info_buffer, &start, " ", 1);
626
627                   g_assert (strlen (p) >= len);
628                   gtk_text_buffer_insert (info_buffer, &start, p, len);
629                   in_para = 1;
630                 }
631               else
632                 {
633                   gtk_text_buffer_insert (info_buffer, &start, "\n", 1);
634                   in_para = 0;
635                 }
636             }
637           break;
638
639         case 2:
640           /* Skipping blank lines */
641           while (g_ascii_isspace (*p))
642             p++;
643           if (*p)
644             {
645               p = lines[i];
646               state++;
647               /* Fall through */
648             }
649           else
650             break;
651
652         case 3:
653           /* Reading program body */
654           gtk_text_buffer_insert (source_buffer, &start, p, -1);
655           gtk_text_buffer_insert (source_buffer, &start, "\n", 1);
656           break;
657         }
658     }
659
660   fontify (source_buffer);
661
662   g_strfreev (lines);
663
664   gtk_text_view_set_buffer (GTK_TEXT_VIEW (info_view), info_buffer);
665   g_object_unref (info_buffer);
666   gtk_text_view_set_buffer (GTK_TEXT_VIEW (source_view), source_buffer);
667   g_object_unref (source_buffer);
668 }
669
670 void
671 row_activated_cb (GtkTreeView       *tree_view,
672                   GtkTreePath       *path,
673                   GtkTreeViewColumn *column)
674 {
675   GtkTreeIter iter;
676   PangoStyle style;
677   GDoDemoFunc func;
678   GtkWidget *window;
679   GtkTreeModel *model;
680
681   model = gtk_tree_view_get_model (tree_view);
682
683   gtk_tree_model_get_iter (model, &iter, path);
684   gtk_tree_model_get (GTK_TREE_MODEL (model),
685                       &iter,
686                       FUNC_COLUMN, &func,
687                       STYLE_COLUMN, &style,
688                       -1);
689
690   if (func)
691     {
692       gtk_tree_store_set (GTK_TREE_STORE (model),
693                           &iter,
694                           STYLE_COLUMN, (style == PANGO_STYLE_ITALIC ? PANGO_STYLE_NORMAL : PANGO_STYLE_ITALIC),
695                           -1);
696       window = (func) (gtk_widget_get_toplevel (GTK_WIDGET (tree_view)));
697
698       if (window != NULL)
699         {
700           CallbackData *cbdata;
701
702           cbdata = g_new (CallbackData, 1);
703           cbdata->model = model;
704           cbdata->path = gtk_tree_path_copy (path);
705
706           g_signal_connect (window, "destroy",
707                             G_CALLBACK (window_closed_cb), cbdata);
708         }
709     }
710 }
711
712 static void
713 selection_cb (GtkTreeSelection *selection,
714               GtkTreeModel     *model)
715 {
716   GtkTreeIter iter;
717   char *name, *filename;
718
719   if (! gtk_tree_selection_get_selected (selection, NULL, &iter))
720     return;
721
722   gtk_tree_model_get (model, &iter,
723                       NAME_COLUMN, &name,
724                       FILENAME_COLUMN, &filename,
725                       -1);
726
727   if (filename)
728     load_file (name, filename);
729
730   g_free (name);
731   g_free (filename);
732 }
733
734 static GtkWidget *
735 create_text (GtkWidget **view,
736              gboolean    is_source)
737 {
738   GtkWidget *scrolled_window;
739   GtkWidget *text_view;
740   PangoFontDescription *font_desc;
741
742   scrolled_window = gtk_scrolled_window_new (NULL, NULL);
743   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
744                                   GTK_POLICY_AUTOMATIC,
745                                   GTK_POLICY_AUTOMATIC);
746   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window),
747                                        GTK_SHADOW_IN);
748
749   *view = text_view = gtk_text_view_new ();
750   g_object_set (text_view, "margin", 20, NULL);
751
752   gtk_text_view_set_editable (GTK_TEXT_VIEW (text_view), FALSE);
753   gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (text_view), FALSE);
754
755   gtk_container_add (GTK_CONTAINER (scrolled_window), text_view);
756
757   if (is_source)
758     {
759       font_desc = pango_font_description_from_string ("monospace");
760       gtk_widget_override_font (text_view, font_desc);
761       pango_font_description_free (font_desc);
762
763       gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (text_view),
764                                    GTK_WRAP_NONE);
765     }
766   else
767     {
768       /* Make it a bit nicer for text. */
769       gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (text_view),
770                                    GTK_WRAP_WORD);
771       gtk_text_view_set_pixels_above_lines (GTK_TEXT_VIEW (text_view),
772                                             2);
773       gtk_text_view_set_pixels_below_lines (GTK_TEXT_VIEW (text_view),
774                                             2);
775     }
776
777   return scrolled_window;
778 }
779
780 static GtkWidget *
781 create_tree (void)
782 {
783   GtkTreeSelection *selection;
784   GtkCellRenderer *cell;
785   GtkWidget *tree_view;
786   GtkTreeViewColumn *column;
787   GtkTreeStore *model;
788   GtkTreeIter iter;
789   GtkWidget *box, *label, *scrolled_window;
790
791   Demo *d = gtk_demos;
792
793   model = gtk_tree_store_new (NUM_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_INT);
794   tree_view = gtk_tree_view_new ();
795   gtk_tree_view_set_model (GTK_TREE_VIEW (tree_view), GTK_TREE_MODEL (model));
796   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
797
798   gtk_tree_selection_set_mode (GTK_TREE_SELECTION (selection),
799                                GTK_SELECTION_BROWSE);
800   gtk_widget_set_size_request (tree_view, 200, -1);
801
802   /* this code only supports 1 level of children. If we
803    * want more we probably have to use a recursing function.
804    */
805   while (d->title)
806     {
807       Demo *children = d->children;
808
809       gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL);
810
811       gtk_tree_store_set (GTK_TREE_STORE (model),
812                           &iter,
813                           NAME_COLUMN, d->name,
814                           TITLE_COLUMN, d->title,
815                           FILENAME_COLUMN, d->filename,
816                           FUNC_COLUMN, d->func,
817                           STYLE_COLUMN, PANGO_STYLE_NORMAL,
818                           -1);
819
820       d++;
821
822       if (!children)
823         continue;
824
825       while (children->title)
826         {
827           GtkTreeIter child_iter;
828
829           gtk_tree_store_append (GTK_TREE_STORE (model), &child_iter, &iter);
830
831           gtk_tree_store_set (GTK_TREE_STORE (model),
832                               &child_iter,
833                               NAME_COLUMN, children->name,
834                               TITLE_COLUMN, children->title,
835                               FILENAME_COLUMN, children->filename,
836                               FUNC_COLUMN, children->func,
837                               STYLE_COLUMN, PANGO_STYLE_NORMAL,
838                               -1);
839
840           children++;
841         }
842     }
843
844   cell = gtk_cell_renderer_text_new ();
845
846   column = gtk_tree_view_column_new_with_attributes ("Widget (double click for demo)",
847                                                      cell,
848                                                      "text", TITLE_COLUMN,
849                                                      "style", STYLE_COLUMN,
850                                                      NULL);
851
852   gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view),
853                                GTK_TREE_VIEW_COLUMN (column));
854
855   gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &iter);
856   gtk_tree_selection_select_iter (GTK_TREE_SELECTION (selection), &iter);
857
858   g_signal_connect (selection, "changed", G_CALLBACK (selection_cb), model);
859   g_signal_connect (tree_view, "row_activated", G_CALLBACK (row_activated_cb), model);
860
861   gtk_tree_view_collapse_all (GTK_TREE_VIEW (tree_view));
862   gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tree_view), FALSE);
863
864   scrolled_window = gtk_scrolled_window_new (NULL, NULL);
865   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
866                                   GTK_POLICY_NEVER,
867                                   GTK_POLICY_AUTOMATIC);
868   gtk_container_add (GTK_CONTAINER (scrolled_window), tree_view);
869
870   label = gtk_label_new ("Widget (double click for demo)");
871
872   box = gtk_notebook_new ();
873   gtk_notebook_append_page (GTK_NOTEBOOK (box), scrolled_window, label);
874
875   gtk_widget_grab_focus (tree_view);
876
877    g_object_unref (model);
878
879   return box;
880 }
881
882 static void
883 setup_default_icon (void)
884 {
885   GdkPixbuf *pixbuf;
886
887   pixbuf = gdk_pixbuf_new_from_resource ("/gtk-logo-old.png", NULL);
888   /* We load a resource, so we can guarantee that loading it is successful */
889   g_assert (pixbuf);
890
891   gtk_window_set_default_icon (pixbuf);
892   
893   g_object_unref (pixbuf);
894 }
895
896 int
897 main (int argc, char **argv)
898 {
899   GtkWidget *window;
900   GtkWidget *hbox;
901   GtkWidget *tree;
902
903   /* Most code in gtk-demo is intended to be exemplary, but not
904    * these few lines, which are just a hack so gtk-demo will work
905    * in the GTK tree without installing it.
906    */
907   if (g_file_test ("../../modules/input/immodules.cache", G_FILE_TEST_EXISTS))
908     {
909       g_setenv ("GTK_IM_MODULE_FILE", "../../modules/input/immodules.cache", TRUE);
910     }
911   /* -- End of hack -- */
912
913   gtk_init (&argc, &argv);
914
915   setup_default_icon ();
916
917   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
918   gtk_window_set_title (GTK_WINDOW (window), "GTK+ Code Demos");
919   g_signal_connect_after (window, "destroy",
920                     G_CALLBACK (gtk_main_quit), NULL);
921
922   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
923   gtk_container_add (GTK_CONTAINER (window), hbox);
924
925   tree = create_tree ();
926   gtk_box_pack_start (GTK_BOX (hbox), tree, FALSE, FALSE, 0);
927
928   notebook = gtk_notebook_new ();
929   gtk_notebook_set_scrollable (GTK_NOTEBOOK (notebook), TRUE);
930   gtk_box_pack_start (GTK_BOX (hbox), notebook, TRUE, TRUE, 0);
931
932   gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
933                             create_text (&info_view, FALSE),
934                             gtk_label_new_with_mnemonic ("_Info"));
935
936   gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
937                             create_text (&source_view, TRUE),
938                             gtk_label_new_with_mnemonic ("_Source"));
939
940   gtk_window_set_default_size (GTK_WINDOW (window), 600, 400);
941   gtk_widget_show_all (window);
942
943   load_file (gtk_demos[0].name, gtk_demos[0].filename);
944
945   gtk_main ();
946
947   return 0;
948 }