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