]> Pileus Git - ~andy/gtk/blob - demos/gtk-demo/main.c
Fix some leaks. (#348108, Chris Wilson)
[~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   STYLE_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   PangoStyle style;
98
99   gtk_tree_model_get_iter (cbdata->model, &iter, cbdata->path);
100   gtk_tree_model_get (GTK_TREE_MODEL (cbdata->model), &iter,
101                       STYLE_COLUMN, &style,
102                       -1);
103   if (style == PANGO_STYLE_ITALIC)
104     gtk_tree_store_set (GTK_TREE_STORE (cbdata->model), &iter,
105                         STYLE_COLUMN, PANGO_STYLE_NORMAL,
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   PangoStyle style;
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                       STYLE_COLUMN, &style,
601                       -1);
602
603   if (func)
604     {
605       gtk_tree_store_set (GTK_TREE_STORE (model),
606                           &iter,
607                           STYLE_COLUMN, (style == PANGO_STYLE_ITALIC ? PANGO_STYLE_NORMAL : PANGO_STYLE_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 ("monospace");
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   GtkWidget *box, *label, *scrolled_window;
700
701   Demo *d = testgtk_demos;
702
703   model = gtk_tree_store_new (NUM_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_INT);
704   tree_view = gtk_tree_view_new ();
705   gtk_tree_view_set_model (GTK_TREE_VIEW (tree_view), GTK_TREE_MODEL (model));
706   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
707
708   gtk_tree_selection_set_mode (GTK_TREE_SELECTION (selection),
709                                GTK_SELECTION_BROWSE);
710   gtk_widget_set_size_request (tree_view, 200, -1);
711
712   /* this code only supports 1 level of children. If we
713    * want more we probably have to use a recursing function.
714    */
715   while (d->title)
716     {
717       Demo *children = d->children;
718
719       gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL);
720
721       gtk_tree_store_set (GTK_TREE_STORE (model),
722                           &iter,
723                           TITLE_COLUMN, d->title,
724                           FILENAME_COLUMN, d->filename,
725                           FUNC_COLUMN, d->func,
726                           STYLE_COLUMN, PANGO_STYLE_NORMAL,
727                           -1);
728
729       d++;
730
731       if (!children)
732         continue;
733       
734       while (children->title)
735         {
736           GtkTreeIter child_iter;
737
738           gtk_tree_store_append (GTK_TREE_STORE (model), &child_iter, &iter);
739           
740           gtk_tree_store_set (GTK_TREE_STORE (model),
741                               &child_iter,
742                               TITLE_COLUMN, children->title,
743                               FILENAME_COLUMN, children->filename,
744                               FUNC_COLUMN, children->func,
745                               STYLE_COLUMN, PANGO_STYLE_NORMAL,
746                               -1);
747           
748           children++;
749         }
750     }
751
752   cell = gtk_cell_renderer_text_new ();
753
754   column = gtk_tree_view_column_new_with_attributes ("Widget (double click for demo)",
755                                                      cell,
756                                                      "text", TITLE_COLUMN,
757                                                      "style", STYLE_COLUMN,
758                                                      NULL);
759   
760   gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view),
761                                GTK_TREE_VIEW_COLUMN (column));
762
763   gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &iter);
764   gtk_tree_selection_select_iter (GTK_TREE_SELECTION (selection), &iter);
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_collapse_all (GTK_TREE_VIEW (tree_view));
770   gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tree_view), FALSE);
771                                     
772   scrolled_window = gtk_scrolled_window_new (NULL, NULL);
773   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
774                                   GTK_POLICY_NEVER,
775                                   GTK_POLICY_AUTOMATIC);
776   gtk_container_add (GTK_CONTAINER (scrolled_window), tree_view);
777
778   label = gtk_label_new ("Widget (double click for demo)");
779
780   box = gtk_notebook_new ();
781   gtk_notebook_append_page (GTK_NOTEBOOK (box), scrolled_window, label);
782
783   gtk_widget_grab_focus (tree_view);
784
785    g_object_unref (model);
786
787   return box;
788 }
789
790 static void
791 setup_default_icon (void)
792 {
793   GdkPixbuf *pixbuf;
794   char *filename;
795   GError *err;
796
797   err = NULL;
798
799   pixbuf = NULL;
800   filename = demo_find_file ("gtk-logo-rgb.gif", &err);
801   if (filename)
802     {
803       pixbuf = gdk_pixbuf_new_from_file (filename, &err);
804       g_free (filename);
805     }
806
807   /* Ignoring this error (passing NULL instead of &err above)
808    * would probably be reasonable for most apps.  We're just
809    * showing off.
810    */
811   if (err)
812     {
813       GtkWidget *dialog;
814       
815       dialog = gtk_message_dialog_new (NULL, 0,
816                                        GTK_MESSAGE_ERROR,
817                                        GTK_BUTTONS_CLOSE,
818                                        "Failed to read icon file: %s",
819                                        err->message);
820       g_error_free (err);
821
822       g_signal_connect (dialog, "response",
823                         G_CALLBACK (gtk_widget_destroy), NULL);
824     }
825
826   if (pixbuf)
827     {
828       GList *list;      
829       GdkPixbuf *transparent;
830
831       /* The gtk-logo-rgb icon has a white background, make it transparent */
832       transparent = gdk_pixbuf_add_alpha (pixbuf, TRUE, 0xff, 0xff, 0xff);
833
834       list = NULL;
835       list = g_list_append (list, transparent);
836       gtk_window_set_default_icon_list (list);
837       g_list_free (list);
838       g_object_unref (pixbuf);
839       g_object_unref (transparent);
840     }
841 }
842
843 int
844 main (int argc, char **argv)
845 {
846   GtkWidget *window;
847   GtkWidget *notebook;
848   GtkWidget *hbox;
849   GtkWidget *tree;
850   GtkTextTag *tag;
851
852   /* Most code in gtk-demo is intended to be exemplary, but not
853    * these few lines, which are just a hack so gtk-demo will work
854    * in the GTK tree without installing it.
855    */
856   if (g_file_test ("../../gdk-pixbuf/libpixbufloader-pnm.la",
857                    G_FILE_TEST_EXISTS))
858     {
859       g_setenv ("GDK_PIXBUF_MODULE_FILE", "../../gdk-pixbuf/gdk-pixbuf.loaders", TRUE);
860       g_setenv ("GTK_IM_MODULE_FILE", "../../modules/input/gtk.immodules", TRUE);
861     }
862   /* -- End of hack -- */
863   
864   gtk_init (&argc, &argv);
865
866   setup_default_icon ();
867   
868   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
869   gtk_window_set_title (GTK_WINDOW (window), "GTK+ Code Demos");
870   g_signal_connect_after (window, "destroy",
871                     G_CALLBACK (gtk_main_quit), NULL);
872
873   hbox = gtk_hbox_new (FALSE, 0);
874   gtk_container_add (GTK_CONTAINER (window), hbox);
875
876   tree = create_tree ();
877   gtk_box_pack_start (GTK_BOX (hbox), tree, FALSE, FALSE, 0);
878
879   notebook = gtk_notebook_new ();
880   gtk_box_pack_start (GTK_BOX (hbox), notebook, TRUE, TRUE, 0);
881
882   gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
883                             create_text (&info_buffer, FALSE),
884                             gtk_label_new_with_mnemonic ("_Info"));
885
886   tag = gtk_text_buffer_create_tag (info_buffer, "title",
887                                     "font", "Sans 18",
888                                     NULL);
889    g_object_unref (info_buffer);
890
891   gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
892                             create_text (&source_buffer, TRUE),
893                             gtk_label_new_with_mnemonic ("_Source"));
894
895
896   tag = gtk_text_buffer_create_tag (source_buffer, "comment",
897                                     "foreground", "DodgerBlue",
898                                     NULL);
899   tag = gtk_text_buffer_create_tag (source_buffer, "type",
900                                     "foreground", "ForestGreen",
901                                     NULL);
902   tag = gtk_text_buffer_create_tag (source_buffer, "string",
903                                     "foreground", "RosyBrown",
904                                     "weight", PANGO_WEIGHT_BOLD,
905                                     NULL);
906   tag = gtk_text_buffer_create_tag (source_buffer, "control",
907                                     "foreground", "purple",
908                                     NULL);
909   tag = gtk_text_buffer_create_tag (source_buffer, "preprocessor",
910                                     "style", PANGO_STYLE_OBLIQUE,
911                                     "foreground", "burlywood4",
912                                     NULL);
913   tag = gtk_text_buffer_create_tag (source_buffer, "function",
914                                     "weight", PANGO_WEIGHT_BOLD,
915                                     "foreground", "DarkGoldenrod4",
916                                     NULL);
917    g_object_unref (source_buffer);
918   
919   gtk_window_set_default_size (GTK_WINDOW (window), 600, 400);
920   gtk_widget_show_all (window);
921   
922
923   load_file (testgtk_demos[0].filename);
924   
925   gtk_main ();
926
927   return 0;
928 }