]> Pileus Git - ~andy/gtk/blob - demos/gtk-demo/main.c
Use g_fopen().
[~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 GtkTextBuffer *info_buffer;
13 static GtkTextBuffer *source_buffer;
14
15 static gchar *current_file = NULL;
16
17
18 enum {
19   TITLE_COLUMN,
20   FILENAME_COLUMN,
21   FUNC_COLUMN,
22   ITALIC_COLUMN,
23   NUM_COLUMNS
24 };
25
26 typedef struct _CallbackData CallbackData;
27 struct _CallbackData
28 {
29   GtkTreeModel *model;
30   GtkTreePath *path;
31 };
32
33 #ifdef G_OS_WIN32
34
35 #undef DEMOCODEDIR
36
37 static char *
38 get_democodedir (void)
39 {
40   static char *result = NULL;
41
42   if (result == NULL)
43     {
44       result = g_win32_get_package_installation_directory (NULL, NULL);
45       if (result == NULL)
46         result = "unknown-location";
47
48       result = g_strconcat (result, "\\share\\gtk-2.0\\demo", NULL);
49     }
50
51   return result;
52 }
53
54 #define DEMOCODEDIR get_democodedir ()
55
56 #endif
57
58 /**
59  * demo_find_file:
60  * @base: base filename
61  * @err:  location to store error, or %NULL.
62  * 
63  * Looks for @base first in the current directory, then in the
64  * location GTK+ where it will be installed on make install,
65  * returns the first file found.
66  * 
67  * Return value: the filename, if found or %NULL
68  **/
69 gchar *
70 demo_find_file (const char *base,
71                 GError    **err)
72 {
73   g_return_val_if_fail (err == NULL || *err == NULL, NULL);
74   
75   if (g_file_test ("gtk-logo-rgb.gif", G_FILE_TEST_EXISTS) &&
76       g_file_test (base, G_FILE_TEST_EXISTS))
77     return g_strdup (base);
78   else
79     {
80       char *filename = g_build_filename (DEMOCODEDIR, base, NULL);
81       if (!g_file_test (filename, G_FILE_TEST_EXISTS))
82         {
83           g_set_error (err, G_FILE_ERROR, G_FILE_ERROR_NOENT,
84                        "Cannot find demo data file \"%s\"", base);
85           g_free (filename);
86           return NULL;
87         }
88       return filename;
89     }
90 }
91
92 static void
93 window_closed_cb (GtkWidget *window, gpointer data)
94 {
95   CallbackData *cbdata = data;
96   GtkTreeIter iter;
97   gboolean italic;
98
99   gtk_tree_model_get_iter (cbdata->model, &iter, cbdata->path);
100   gtk_tree_model_get (GTK_TREE_MODEL (cbdata->model), &iter,
101                       ITALIC_COLUMN, &italic,
102                       -1);
103   if (italic)
104     gtk_tree_store_set (GTK_TREE_STORE (cbdata->model), &iter,
105                         ITALIC_COLUMN, !italic,
106                         -1);
107
108   gtk_tree_path_free (cbdata->path);
109   g_free (cbdata);
110 }
111
112 gboolean
113 read_line (FILE *stream, GString *str)
114 {
115   int n_read = 0;
116   
117 #ifdef HAVE_FLOCKFILE
118   flockfile (stream);
119 #endif
120
121   g_string_truncate (str, 0);
122   
123   while (1)
124     {
125       int c;
126       
127 #ifdef HAVE_FLOCKFILE
128       c = getc_unlocked (stream);
129 #else
130       c = getc (stream);
131 #endif
132
133       if (c == EOF)
134         goto done;
135       else
136         n_read++;
137
138       switch (c)
139         {
140         case '\r':
141         case '\n':
142           {
143 #ifdef HAVE_FLOCKFILE
144             int next_c = getc_unlocked (stream);
145 #else
146             int next_c = getc (stream);
147 #endif
148             
149             if (!(next_c == EOF ||
150                   (c == '\r' && next_c == '\n') ||
151                   (c == '\n' && next_c == '\r')))
152               ungetc (next_c, stream);
153             
154             goto done;
155           }
156         default:
157           g_string_append_c (str, c);
158         }
159     }
160
161  done:
162
163 #ifdef HAVE_FLOCKFILE
164   funlockfile (stream);
165 #endif
166
167   return n_read > 0;
168 }
169
170
171 /* Stupid syntax highlighting.
172  *
173  * No regex was used in the making of this highlighting.
174  * It should only work for simple cases.  This is good, as
175  * that's all we should have in the demos.
176  */
177 /* This code should not be used elsewhere, except perhaps as an example of how
178  * to iterate through a text buffer.
179  */
180 enum {
181   STATE_NORMAL,
182   STATE_IN_COMMENT
183 };
184
185 static gchar *tokens[] =
186 {
187   "/*",
188   "\"",
189   NULL
190 };
191
192 static gchar *types[] =
193 {
194   "static",
195   "const ",
196   "void",
197   "gint",
198   "int ",
199   "char ",
200   "gchar ",
201   "gfloat",
202   "float",
203   "gint8",
204   "gint16",
205   "gint32",
206   "guint",
207   "guint8",
208   "guint16",
209   "guint32",
210   "guchar",
211   "glong",
212   "gboolean" ,
213   "gshort",
214   "gushort",
215   "gulong",
216   "gdouble",
217   "gldouble",
218   "gpointer",
219   "NULL",
220   "GList",
221   "GSList",
222   "FALSE",
223   "TRUE",
224   "FILE ",
225   "GtkObject ",
226   "GtkColorSelection ",
227   "GtkWidget ",
228   "GtkButton ",
229   "GdkColor ",
230   "GdkRectangle ",
231   "GdkEventExpose ",
232   "GdkGC ",
233   "GdkPixbufLoader ",
234   "GdkPixbuf ",
235   "GError",
236   "size_t",
237   NULL
238 };
239
240 static gchar *control[] =
241 {
242   " if ",
243   " while ",
244   " else",
245   " do ",
246   " for ",
247   "?",
248   ":",
249   "return ",
250   "goto ",
251   NULL
252 };
253 void
254 parse_chars (gchar     *text,
255              gchar    **end_ptr,
256              gint      *state,
257              gchar    **tag,
258              gboolean   start)
259 {
260   gint i;
261   gchar *next_token;
262
263   /* Handle comments first */
264   if (*state == STATE_IN_COMMENT)
265     {
266       *end_ptr = strstr (text, "*/");
267       if (*end_ptr)
268         {
269           *end_ptr += 2;
270           *state = STATE_NORMAL;
271           *tag = "comment";
272         }
273       return;
274     }
275
276   *tag = NULL;
277   *end_ptr = NULL;
278
279   /* check for comment */
280   if (!strncmp (text, "/*", 2))
281     {
282       *end_ptr = strstr (text, "*/");
283       if (*end_ptr)
284         *end_ptr += 2;
285       else
286         *state = STATE_IN_COMMENT;
287       *tag = "comment";
288       return;
289     }
290
291   /* check for preprocessor defines */
292   if (*text == '#' && start)
293     {
294       *end_ptr = NULL;
295       *tag = "preprocessor";
296       return;
297     }
298
299   /* functions */
300   if (start && * text != '\t' && *text != ' ' && *text != '{' && *text != '}')
301     {
302       if (strstr (text, "("))
303         {
304           *end_ptr = strstr (text, "(");
305           *tag = "function";
306           return;
307         }
308     }
309   /* check for types */
310   for (i = 0; types[i] != NULL; i++)
311     if (!strncmp (text, types[i], strlen (types[i])))
312       {
313         *end_ptr = text + strlen (types[i]);
314         *tag = "type";
315         return;
316       }
317
318   /* check for control */
319   for (i = 0; control[i] != NULL; i++)
320     if (!strncmp (text, control[i], strlen (control[i])))
321       {
322         *end_ptr = text + strlen (control[i]);
323         *tag = "control";
324         return;
325       }
326
327   /* check for string */
328   if (text[0] == '"')
329     {
330       gint maybe_escape = FALSE;
331
332       *end_ptr = text + 1;
333       *tag = "string";
334       while (**end_ptr != '\000')
335         {
336           if (**end_ptr == '\"' && !maybe_escape)
337             {
338               *end_ptr += 1;
339               return;
340             }
341           if (**end_ptr == '\\')
342             maybe_escape = TRUE;
343           else
344             maybe_escape = FALSE;
345           *end_ptr += 1;
346         }
347       return;
348     }
349
350   /* not at the start of a tag.  Find the next one. */
351   for (i = 0; tokens[i] != NULL; i++)
352     {
353       next_token = strstr (text, tokens[i]);
354       if (next_token)
355         {
356           if (*end_ptr)
357             *end_ptr = (*end_ptr<next_token)?*end_ptr:next_token;
358           else
359             *end_ptr = next_token;
360         }
361     }
362
363   for (i = 0; types[i] != NULL; i++)
364     {
365       next_token = strstr (text, types[i]);
366       if (next_token)
367         {
368           if (*end_ptr)
369             *end_ptr = (*end_ptr<next_token)?*end_ptr:next_token;
370           else
371             *end_ptr = next_token;
372         }
373     }
374
375   for (i = 0; control[i] != NULL; i++)
376     {
377       next_token = strstr (text, control[i]);
378       if (next_token)
379         {
380           if (*end_ptr)
381             *end_ptr = (*end_ptr<next_token)?*end_ptr:next_token;
382           else
383             *end_ptr = next_token;
384         }
385     }
386 }
387
388 /* While not as cool as c-mode, this will do as a quick attempt at highlighting */
389 static void
390 fontify (void)
391 {
392   GtkTextIter start_iter, next_iter, tmp_iter;
393   gint state;
394   gchar *text;
395   gchar *start_ptr, *end_ptr;
396   gchar *tag;
397
398   state = STATE_NORMAL;
399
400   gtk_text_buffer_get_iter_at_offset (source_buffer, &start_iter, 0);
401
402   next_iter = start_iter;
403   while (gtk_text_iter_forward_line (&next_iter))
404     {
405       gboolean start = TRUE;
406       start_ptr = text = gtk_text_iter_get_text (&start_iter, &next_iter);
407
408       do
409         {
410           parse_chars (start_ptr, &end_ptr, &state, &tag, start);
411
412           start = FALSE;
413           if (end_ptr)
414             {
415               tmp_iter = start_iter;
416               gtk_text_iter_forward_chars (&tmp_iter, end_ptr - start_ptr);
417             }
418           else
419             {
420               tmp_iter = next_iter;
421             }
422           if (tag)
423             gtk_text_buffer_apply_tag_by_name (source_buffer, tag, &start_iter, &tmp_iter);
424
425           start_iter = tmp_iter;
426           start_ptr = end_ptr;
427         }
428       while (end_ptr);
429
430       g_free (text);
431       start_iter = next_iter;
432     }
433 }
434
435 void
436 load_file (const gchar *filename)
437 {
438   FILE *file;
439   GtkTextIter start, end;
440   char *full_filename;
441   GError *err = NULL;
442   GString *buffer = g_string_new (NULL);
443   int state = 0;
444   gboolean in_para = 0;
445
446   if (current_file && !strcmp (current_file, filename))
447     {
448       g_string_free (buffer, TRUE);
449       return;
450     }
451
452   g_free (current_file);
453   current_file = g_strdup (filename);
454   
455   gtk_text_buffer_get_bounds (info_buffer, &start, &end);
456   gtk_text_buffer_delete (info_buffer, &start, &end);
457
458   gtk_text_buffer_get_bounds (source_buffer, &start, &end);
459   gtk_text_buffer_delete (source_buffer, &start, &end);
460
461   full_filename = demo_find_file (filename, &err);
462   if (!full_filename)
463     {
464       g_warning ("%s", err->message);
465       g_error_free (err);
466       return;
467     }
468
469   file = g_fopen (full_filename, "r");
470
471   if (!file)
472     g_warning ("Cannot open %s: %s\n", full_filename, g_strerror (errno));
473
474   g_free (full_filename);
475
476   if (!file)
477     return;
478
479   gtk_text_buffer_get_iter_at_offset (info_buffer, &start, 0);
480   while (read_line (file, buffer))
481     {
482       gchar *p = buffer->str;
483       gchar *q;
484       gchar *r;
485       
486       switch (state)
487         {
488         case 0:
489           /* Reading title */
490           while (*p == '/' || *p == '*' || g_ascii_isspace (*p))
491             p++;
492           r = p;
493           while (*r != '/' && strlen (r))
494             r++;
495           if (strlen (r) > 0)
496             p = r + 1;
497           q = p + strlen (p);
498           while (q > p && g_ascii_isspace (*(q - 1)))
499             q--;
500
501           if (q > p)
502             {
503               int len_chars = g_utf8_pointer_to_offset (p, q);
504
505               end = start;
506
507               g_assert (strlen (p) >= q - p);
508               gtk_text_buffer_insert (info_buffer, &end, p, q - p);
509               start = end;
510
511               gtk_text_iter_backward_chars (&start, len_chars);
512               gtk_text_buffer_apply_tag_by_name (info_buffer, "title", &start, &end);
513
514               start = end;
515               
516               state++;
517             }
518           break;
519             
520         case 1:
521           /* Reading body of info section */
522           while (g_ascii_isspace (*p))
523             p++;
524           if (*p == '*' && *(p + 1) == '/')
525             {
526               gtk_text_buffer_get_iter_at_offset (source_buffer, &start, 0);
527               state++;
528             }
529           else
530             {
531               int len;
532               
533               while (*p == '*' || g_ascii_isspace (*p))
534                 p++;
535
536               len = strlen (p);
537               while (g_ascii_isspace (*(p + len - 1)))
538                 len--;
539               
540               if (len > 0)
541                 {
542                   if (in_para)
543                     gtk_text_buffer_insert (info_buffer, &start, " ", 1);
544
545                   g_assert (strlen (p) >= len);
546                   gtk_text_buffer_insert (info_buffer, &start, p, len);
547                   in_para = 1;
548                 }
549               else
550                 {
551                   gtk_text_buffer_insert (info_buffer, &start, "\n", 1);
552                   in_para = 0;
553                 }
554             }
555           break;
556
557         case 2:
558           /* Skipping blank lines */
559           while (g_ascii_isspace (*p))
560             p++;
561           if (*p)
562             {
563               p = buffer->str;
564               state++;
565               /* Fall through */
566             }
567           else
568             break;
569           
570         case 3:
571           /* Reading program body */
572           gtk_text_buffer_insert (source_buffer, &start, p, -1);
573           gtk_text_buffer_insert (source_buffer, &start, "\n", 1);
574           break;
575         }
576     }
577
578   fontify ();
579
580   g_string_free (buffer, TRUE);
581 }
582
583 void
584 row_activated_cb (GtkTreeView       *tree_view,
585                   GtkTreePath       *path,
586                   GtkTreeViewColumn *column)
587 {
588   GtkTreeIter iter;
589   gboolean italic;
590   GDoDemoFunc func;
591   GtkWidget *window;
592   GtkTreeModel *model;
593
594   model = gtk_tree_view_get_model (tree_view);
595   
596   gtk_tree_model_get_iter (model, &iter, path);
597   gtk_tree_model_get (GTK_TREE_MODEL (model),
598                       &iter,
599                       FUNC_COLUMN, &func,
600                       ITALIC_COLUMN, &italic,
601                       -1);
602
603   if (func)
604     {
605       gtk_tree_store_set (GTK_TREE_STORE (model),
606                           &iter,
607                           ITALIC_COLUMN, !italic,
608                           -1);
609       window = (func) (gtk_widget_get_toplevel (GTK_WIDGET (tree_view)));
610       
611       if (window != NULL)
612         {
613           CallbackData *cbdata;
614           
615           cbdata = g_new (CallbackData, 1);
616           cbdata->model = model;
617           cbdata->path = gtk_tree_path_copy (path);
618           
619           g_signal_connect (window, "destroy",
620                             G_CALLBACK (window_closed_cb), cbdata);
621         }
622     }
623 }
624
625 static void
626 selection_cb (GtkTreeSelection *selection,
627               GtkTreeModel     *model)
628 {
629   GtkTreeIter iter;
630   GValue value = {0, };
631
632   if (! gtk_tree_selection_get_selected (selection, NULL, &iter))
633     return;
634
635   gtk_tree_model_get_value (model, &iter,
636                             FILENAME_COLUMN,
637                             &value);
638   if (g_value_get_string (&value))
639     load_file (g_value_get_string (&value));
640   g_value_unset (&value);
641 }
642
643 static GtkWidget *
644 create_text (GtkTextBuffer **buffer,
645              gboolean        is_source)
646 {
647   GtkWidget *scrolled_window;
648   GtkWidget *text_view;
649   PangoFontDescription *font_desc;
650
651   scrolled_window = gtk_scrolled_window_new (NULL, NULL);
652   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
653                                   GTK_POLICY_AUTOMATIC,
654                                   GTK_POLICY_AUTOMATIC);
655   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window),
656                                        GTK_SHADOW_IN);
657   
658   text_view = gtk_text_view_new ();
659   
660   *buffer = gtk_text_buffer_new (NULL);
661   gtk_text_view_set_buffer (GTK_TEXT_VIEW (text_view), *buffer);
662   gtk_text_view_set_editable (GTK_TEXT_VIEW (text_view), FALSE);
663   gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (text_view), FALSE);
664
665   gtk_container_add (GTK_CONTAINER (scrolled_window), text_view);
666   
667   if (is_source)
668     {
669       font_desc = pango_font_description_from_string ("Courier 12");
670       gtk_widget_modify_font (text_view, font_desc);
671       pango_font_description_free (font_desc);
672
673       gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (text_view),
674                                    GTK_WRAP_NONE);
675     }
676   else
677     {
678       /* Make it a bit nicer for text. */
679       gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (text_view),
680                                    GTK_WRAP_WORD);
681       gtk_text_view_set_pixels_above_lines (GTK_TEXT_VIEW (text_view),
682                                             2);
683       gtk_text_view_set_pixels_below_lines (GTK_TEXT_VIEW (text_view),
684                                             2);
685     }
686   
687   return scrolled_window;
688 }
689
690 static GtkWidget *
691 create_tree (void)
692 {
693   GtkTreeSelection *selection;
694   GtkCellRenderer *cell;
695   GtkWidget *tree_view;
696   GtkTreeViewColumn *column;
697   GtkTreeStore *model;
698   GtkTreeIter iter;
699
700   Demo *d = testgtk_demos;
701
702   model = gtk_tree_store_new (NUM_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_BOOLEAN);
703   tree_view = gtk_tree_view_new ();
704   gtk_tree_view_set_model (GTK_TREE_VIEW (tree_view), GTK_TREE_MODEL (model));
705   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
706
707   gtk_tree_selection_set_mode (GTK_TREE_SELECTION (selection),
708                                GTK_SELECTION_BROWSE);
709   gtk_widget_set_size_request (tree_view, 200, -1);
710
711   /* this code only supports 1 level of children. If we
712    * want more we probably have to use a recursing function.
713    */
714   while (d->title)
715     {
716       Demo *children = d->children;
717
718       gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL);
719
720       gtk_tree_store_set (GTK_TREE_STORE (model),
721                           &iter,
722                           TITLE_COLUMN, d->title,
723                           FILENAME_COLUMN, d->filename,
724                           FUNC_COLUMN, d->func,
725                           ITALIC_COLUMN, FALSE,
726                           -1);
727
728       d++;
729
730       if (!children)
731         continue;
732       
733       while (children->title)
734         {
735           GtkTreeIter child_iter;
736
737           gtk_tree_store_append (GTK_TREE_STORE (model), &child_iter, &iter);
738           
739           gtk_tree_store_set (GTK_TREE_STORE (model),
740                               &child_iter,
741                               TITLE_COLUMN, children->title,
742                               FILENAME_COLUMN, children->filename,
743                               FUNC_COLUMN, children->func,
744                               ITALIC_COLUMN, FALSE,
745                               -1);
746           
747           children++;
748         }
749     }
750
751   cell = gtk_cell_renderer_text_new ();
752
753   g_object_set (cell,
754                 "style", PANGO_STYLE_ITALIC,
755                 NULL);
756   
757   column = gtk_tree_view_column_new_with_attributes ("Widget (double click for demo)",
758                                                      cell,
759                                                      "text", TITLE_COLUMN,
760                                                      "style_set", ITALIC_COLUMN,
761                                                      NULL);
762   
763   gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view),
764                                GTK_TREE_VIEW_COLUMN (column));
765
766   g_signal_connect (selection, "changed", G_CALLBACK (selection_cb), model);
767   g_signal_connect (tree_view, "row_activated", G_CALLBACK (row_activated_cb), model);
768
769   gtk_tree_view_expand_all (GTK_TREE_VIEW (tree_view));
770   return tree_view;
771 }
772
773 static void
774 setup_default_icon (void)
775 {
776   GdkPixbuf *pixbuf;
777   char *filename;
778   GError *err;
779
780   err = NULL;
781
782   pixbuf = NULL;
783   filename = demo_find_file ("gtk-logo-rgb.gif", &err);
784   if (filename)
785     {
786       pixbuf = gdk_pixbuf_new_from_file (filename, &err);
787       g_free (filename);
788     }
789
790   /* Ignoring this error (passing NULL instead of &err above)
791    * would probably be reasonable for most apps.  We're just
792    * showing off.
793    */
794   if (err)
795     {
796       GtkWidget *dialog;
797       
798       dialog = gtk_message_dialog_new (NULL, 0,
799                                        GTK_MESSAGE_ERROR,
800                                        GTK_BUTTONS_CLOSE,
801                                        "Failed to read icon file: %s",
802                                        err->message);
803       g_error_free (err);
804
805       g_signal_connect (dialog, "response",
806                         G_CALLBACK (gtk_widget_destroy), NULL);
807     }
808
809   if (pixbuf)
810     {
811       GList *list;      
812       GdkPixbuf *transparent;
813
814       /* The gtk-logo-rgb icon has a white background, make it transparent */
815       transparent = gdk_pixbuf_add_alpha (pixbuf, TRUE, 0xff, 0xff, 0xff);
816
817       list = NULL;
818       list = g_list_append (list, transparent);
819       gtk_window_set_default_icon_list (list);
820       g_list_free (list);
821       g_object_unref (pixbuf);
822       g_object_unref (transparent);
823     }
824 }
825
826 int
827 main (int argc, char **argv)
828 {
829   GtkWidget *window;
830   GtkWidget *notebook;
831   GtkWidget *hbox;
832   GtkWidget *tree;
833   GtkTextTag *tag;
834
835   /* Most code in gtk-demo is intended to be exemplary, but not
836    * these few lines, which are just a hack so gtk-demo will work
837    * in the GTK tree without installing it.
838    */
839   if (g_file_test ("../../gdk-pixbuf/libpixbufloader-pnm.la",
840                    G_FILE_TEST_EXISTS))
841     {
842       g_setenv ("GDK_PIXBUF_MODULE_FILE", "../../gdk-pixbuf/gdk-pixbuf.loaders", TRUE);
843       g_setenv ("GTK_IM_MODULE_FILE", "../../modules/input/gtk.immodules", TRUE);
844     }
845   /* -- End of hack -- */
846   
847   gtk_init (&argc, &argv);
848
849   setup_default_icon ();
850   
851   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
852   gtk_window_set_title (GTK_WINDOW (window), "GTK+ Code Demos");
853   g_signal_connect (window, "destroy",
854                     G_CALLBACK (gtk_main_quit), NULL);
855
856   hbox = gtk_hbox_new (FALSE, 0);
857   gtk_container_add (GTK_CONTAINER (window), hbox);
858
859   tree = create_tree ();
860   gtk_box_pack_start (GTK_BOX (hbox), tree, FALSE, FALSE, 0);
861
862   notebook = gtk_notebook_new ();
863   gtk_box_pack_start (GTK_BOX (hbox), notebook, TRUE, TRUE, 0);
864
865   gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
866                             create_text (&info_buffer, FALSE),
867                             gtk_label_new_with_mnemonic ("_Info"));
868
869   gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
870                             create_text (&source_buffer, TRUE),
871                             gtk_label_new_with_mnemonic ("_Source"));
872
873   tag = gtk_text_buffer_create_tag (info_buffer, "title",
874                                     "font", "Sans 18",
875                                     NULL);
876
877   tag = gtk_text_buffer_create_tag (source_buffer, "comment",
878                                     "foreground", "red",
879                                     NULL);
880   tag = gtk_text_buffer_create_tag (source_buffer, "type",
881                                     "foreground", "ForestGreen",
882                                     NULL);
883   tag = gtk_text_buffer_create_tag (source_buffer, "string",
884                                     "foreground", "RosyBrown",
885                                     "weight", PANGO_WEIGHT_BOLD,
886                                     NULL);
887   tag = gtk_text_buffer_create_tag (source_buffer, "control",
888                                     "foreground", "purple",
889                                     NULL);
890   tag = gtk_text_buffer_create_tag (source_buffer, "preprocessor",
891                                     "style", PANGO_STYLE_OBLIQUE,
892                                     "foreground", "burlywood4",
893                                     NULL);
894   tag = gtk_text_buffer_create_tag (source_buffer, "function",
895                                     "weight", PANGO_WEIGHT_BOLD,
896                                     "foreground", "DarkGoldenrod4",
897                                     NULL);
898   
899   gtk_window_set_default_size (GTK_WINDOW (window), 600, 400);
900   gtk_widget_show_all (window);
901   
902
903   load_file (testgtk_demos[0].filename);
904   
905   gtk_main ();
906
907   return 0;
908 }