]> Pileus Git - ~andy/gtk/blob - demos/gtk-demo/main.c
c7492ef196b077cb99385ac111df155cb9303b19
[~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_of_module (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   "double",
204   "gint8",
205   "gint16",
206   "gint32",
207   "guint",
208   "guint8",
209   "guint16",
210   "guint32",
211   "guchar",
212   "glong",
213   "gboolean" ,
214   "gshort",
215   "gushort",
216   "gulong",
217   "gdouble",
218   "gldouble",
219   "gpointer",
220   "NULL",
221   "GList",
222   "GSList",
223   "FALSE",
224   "TRUE",
225   "FILE ",
226   "GtkObject ",
227   "GtkColorSelection ",
228   "GtkWidget ",
229   "GtkButton ",
230   "GdkColor ",
231   "GdkRectangle ",
232   "GdkEventExpose ",
233   "GdkGC ",
234   "GdkPixbufLoader ",
235   "GdkPixbuf ",
236   "GError",
237   "size_t",
238   "GtkAboutDialog ",
239   "GtkAction ",
240   "GtkActionEntry ",
241   "GtkRadioActionEntry ",
242   "GtkIconFactory ",
243   "GtkStockItem ",
244   "GtkIconSet ",
245   "GtkTextBuffer ",
246   "GtkStatusbar ",
247   "GtkTextIter ",
248   "GtkTextMark ",
249   "GdkEventWindowState ",
250   "GtkActionGroup ",
251   "GtkUIManager ",
252   "GtkRadioAction ",
253   "GtkActionClass ",
254   "GtkToggleActionEntry ",
255   "GtkAssistant ",
256   "GtkBuilder ",
257   "GtkSizeGroup ",
258   "GtkTreeModel ",
259   "GtkTreeSelection ",
260   "GdkDisplay ",
261   "GdkScreen ",
262   "GdkWindow ",
263   "GdkEventButton ",
264   "GdkCursor ",
265   "GtkTreeIter ",
266   "GtkTreeViewColumn ",
267   "GdkDisplayManager ",
268   "GtkClipboard ",
269   "GtkIconSize ",
270   "GtkImage ",
271   "GdkDragContext ",
272   "GtkSelectionData ",
273   "GtkDialog ",
274   "GtkMenuItem ",
275   "GtkListStore ",
276   "GtkCellLayout ",
277   "GtkCellRenderer ",
278   "GtkTreePath ",
279   "GtkTreeStore ",
280   "GtkEntry ",
281   "GtkEditable ",
282   "GtkEditableClass ",
283   "GdkPixmap ",
284   "GdkEventConfigure ",
285   "GdkEventMotion ",
286   "GdkModifierType ",
287   "GtkEntryCompletion ",
288   "GtkToolItem ",
289   "GDir ",
290   "GtkIconView ",
291   "GtkCellRendererText ",
292   "GtkContainer ",
293   "GtkAccelGroup ",
294   "GtkPaned ",
295   "GtkPrintOperation ",
296   "GtkPrintContext ",
297   "cairo_t ",
298   "PangoLayout "
299   "PangoFontDescription ",
300   "PangoRenderer ",
301   "PangoMatrix ",
302   "PangoContext ",
303   "PangoLayout ",
304   "GtkTable ",
305   "GtkToggleButton ",
306   "GString ",
307   "GtkIconSize ",
308   "GtkTreeView ",
309   "GtkTextTag ",
310   "GdkEvent ",
311   "GdkEventKey ",
312   "GtkTextView ",
313   "GdkEventVisibility ",
314   "GdkBitmap ",
315   "GtkTextChildAnchor ",
316   "GArray ",
317   "GtkCellEditable ",
318   "GtkCellRendererToggle ",
319   NULL
320 };
321
322 static gchar *control[] =
323 {
324   " if ",
325   " while ",
326   " else",
327   " do ",
328   " for ",
329   "?",
330   ":",
331   "return ",
332   "goto ",
333   NULL
334 };
335 void
336 parse_chars (gchar     *text,
337              gchar    **end_ptr,
338              gint      *state,
339              gchar    **tag,
340              gboolean   start)
341 {
342   gint i;
343   gchar *next_token;
344
345   /* Handle comments first */
346   if (*state == STATE_IN_COMMENT)
347     {
348       *end_ptr = strstr (text, "*/");
349       if (*end_ptr)
350         {
351           *end_ptr += 2;
352           *state = STATE_NORMAL;
353           *tag = "comment";
354         }
355       return;
356     }
357
358   *tag = NULL;
359   *end_ptr = NULL;
360
361   /* check for comment */
362   if (!strncmp (text, "/*", 2))
363     {
364       *end_ptr = strstr (text, "*/");
365       if (*end_ptr)
366         *end_ptr += 2;
367       else
368         *state = STATE_IN_COMMENT;
369       *tag = "comment";
370       return;
371     }
372
373   /* check for preprocessor defines */
374   if (*text == '#' && start)
375     {
376       *end_ptr = NULL;
377       *tag = "preprocessor";
378       return;
379     }
380
381   /* functions */
382   if (start && * text != '\t' && *text != ' ' && *text != '{' && *text != '}')
383     {
384       if (strstr (text, "("))
385         {
386           *end_ptr = strstr (text, "(");
387           *tag = "function";
388           return;
389         }
390     }
391   /* check for types */
392   for (i = 0; types[i] != NULL; i++)
393     if (!strncmp (text, types[i], strlen (types[i])) ||
394         (start && types[i][0] == ' ' && !strncmp (text, types[i] + 1, strlen (types[i]) - 1)))
395       {
396         *end_ptr = text + strlen (types[i]);
397         *tag = "type";
398         return;
399       }
400
401   /* check for control */
402   for (i = 0; control[i] != NULL; i++)
403     if (!strncmp (text, control[i], strlen (control[i])))
404       {
405         *end_ptr = text + strlen (control[i]);
406         *tag = "control";
407         return;
408       }
409
410   /* check for string */
411   if (text[0] == '"')
412     {
413       gint maybe_escape = FALSE;
414
415       *end_ptr = text + 1;
416       *tag = "string";
417       while (**end_ptr != '\000')
418         {
419           if (**end_ptr == '\"' && !maybe_escape)
420             {
421               *end_ptr += 1;
422               return;
423             }
424           if (**end_ptr == '\\')
425             maybe_escape = TRUE;
426           else
427             maybe_escape = FALSE;
428           *end_ptr += 1;
429         }
430       return;
431     }
432
433   /* not at the start of a tag.  Find the next one. */
434   for (i = 0; tokens[i] != NULL; i++)
435     {
436       next_token = strstr (text, tokens[i]);
437       if (next_token)
438         {
439           if (*end_ptr)
440             *end_ptr = (*end_ptr<next_token)?*end_ptr:next_token;
441           else
442             *end_ptr = next_token;
443         }
444     }
445
446   for (i = 0; types[i] != NULL; i++)
447     {
448       next_token = strstr (text, types[i]);
449       if (next_token)
450         {
451           if (*end_ptr)
452             *end_ptr = (*end_ptr<next_token)?*end_ptr:next_token;
453           else
454             *end_ptr = next_token;
455         }
456     }
457
458   for (i = 0; control[i] != NULL; i++)
459     {
460       next_token = strstr (text, control[i]);
461       if (next_token)
462         {
463           if (*end_ptr)
464             *end_ptr = (*end_ptr<next_token)?*end_ptr:next_token;
465           else
466             *end_ptr = next_token;
467         }
468     }
469 }
470
471 /* While not as cool as c-mode, this will do as a quick attempt at highlighting */
472 static void
473 fontify (void)
474 {
475   GtkTextIter start_iter, next_iter, tmp_iter;
476   gint state;
477   gchar *text;
478   gchar *start_ptr, *end_ptr;
479   gchar *tag;
480
481   state = STATE_NORMAL;
482
483   gtk_text_buffer_get_iter_at_offset (source_buffer, &start_iter, 0);
484
485   next_iter = start_iter;
486   while (gtk_text_iter_forward_line (&next_iter))
487     {
488       gboolean start = TRUE;
489       start_ptr = text = gtk_text_iter_get_text (&start_iter, &next_iter);
490
491       do
492         {
493           parse_chars (start_ptr, &end_ptr, &state, &tag, start);
494
495           start = FALSE;
496           if (end_ptr)
497             {
498               tmp_iter = start_iter;
499               gtk_text_iter_forward_chars (&tmp_iter, end_ptr - start_ptr);
500             }
501           else
502             {
503               tmp_iter = next_iter;
504             }
505           if (tag)
506             gtk_text_buffer_apply_tag_by_name (source_buffer, tag, &start_iter, &tmp_iter);
507
508           start_iter = tmp_iter;
509           start_ptr = end_ptr;
510         }
511       while (end_ptr);
512
513       g_free (text);
514       start_iter = next_iter;
515     }
516 }
517
518 void
519 load_file (const gchar *filename)
520 {
521   FILE *file;
522   GtkTextIter start, end;
523   char *full_filename;
524   GError *err = NULL;
525   GString *buffer = g_string_new (NULL);
526   int state = 0;
527   gboolean in_para = 0;
528
529   if (current_file && !strcmp (current_file, filename))
530     {
531       g_string_free (buffer, TRUE);
532       return;
533     }
534
535   g_free (current_file);
536   current_file = g_strdup (filename);
537   
538   gtk_text_buffer_get_bounds (info_buffer, &start, &end);
539   gtk_text_buffer_delete (info_buffer, &start, &end);
540
541   gtk_text_buffer_get_bounds (source_buffer, &start, &end);
542   gtk_text_buffer_delete (source_buffer, &start, &end);
543
544   full_filename = demo_find_file (filename, &err);
545   if (!full_filename)
546     {
547       g_warning ("%s", err->message);
548       g_error_free (err);
549       return;
550     }
551
552   file = g_fopen (full_filename, "r");
553
554   if (!file)
555     g_warning ("Cannot open %s: %s\n", full_filename, g_strerror (errno));
556
557   g_free (full_filename);
558
559   if (!file)
560     return;
561
562   gtk_text_buffer_get_iter_at_offset (info_buffer, &start, 0);
563   while (read_line (file, buffer))
564     {
565       gchar *p = buffer->str;
566       gchar *q;
567       gchar *r;
568       
569       switch (state)
570         {
571         case 0:
572           /* Reading title */
573           while (*p == '/' || *p == '*' || g_ascii_isspace (*p))
574             p++;
575           r = p;
576           while (*r != '/' && strlen (r))
577             r++;
578           if (strlen (r) > 0)
579             p = r + 1;
580           q = p + strlen (p);
581           while (q > p && g_ascii_isspace (*(q - 1)))
582             q--;
583
584           if (q > p)
585             {
586               int len_chars = g_utf8_pointer_to_offset (p, q);
587
588               end = start;
589
590               g_assert (strlen (p) >= q - p);
591               gtk_text_buffer_insert (info_buffer, &end, p, q - p);
592               start = end;
593
594               gtk_text_iter_backward_chars (&start, len_chars);
595               gtk_text_buffer_apply_tag_by_name (info_buffer, "title", &start, &end);
596
597               start = end;
598               
599               state++;
600             }
601           break;
602             
603         case 1:
604           /* Reading body of info section */
605           while (g_ascii_isspace (*p))
606             p++;
607           if (*p == '*' && *(p + 1) == '/')
608             {
609               gtk_text_buffer_get_iter_at_offset (source_buffer, &start, 0);
610               state++;
611             }
612           else
613             {
614               int len;
615               
616               while (*p == '*' || g_ascii_isspace (*p))
617                 p++;
618
619               len = strlen (p);
620               while (g_ascii_isspace (*(p + len - 1)))
621                 len--;
622               
623               if (len > 0)
624                 {
625                   if (in_para)
626                     gtk_text_buffer_insert (info_buffer, &start, " ", 1);
627
628                   g_assert (strlen (p) >= len);
629                   gtk_text_buffer_insert (info_buffer, &start, p, len);
630                   in_para = 1;
631                 }
632               else
633                 {
634                   gtk_text_buffer_insert (info_buffer, &start, "\n", 1);
635                   in_para = 0;
636                 }
637             }
638           break;
639
640         case 2:
641           /* Skipping blank lines */
642           while (g_ascii_isspace (*p))
643             p++;
644           if (*p)
645             {
646               p = buffer->str;
647               state++;
648               /* Fall through */
649             }
650           else
651             break;
652           
653         case 3:
654           /* Reading program body */
655           gtk_text_buffer_insert (source_buffer, &start, p, -1);
656           gtk_text_buffer_insert (source_buffer, &start, "\n", 1);
657           break;
658         }
659     }
660
661   fclose (file);
662   
663   fontify ();
664
665   g_string_free (buffer, TRUE);
666 }
667
668 void
669 row_activated_cb (GtkTreeView       *tree_view,
670                   GtkTreePath       *path,
671                   GtkTreeViewColumn *column)
672 {
673   GtkTreeIter iter;
674   PangoStyle style;
675   GDoDemoFunc func;
676   GtkWidget *window;
677   GtkTreeModel *model;
678
679   model = gtk_tree_view_get_model (tree_view);
680   
681   gtk_tree_model_get_iter (model, &iter, path);
682   gtk_tree_model_get (GTK_TREE_MODEL (model),
683                       &iter,
684                       FUNC_COLUMN, &func,
685                       STYLE_COLUMN, &style,
686                       -1);
687
688   if (func)
689     {
690       gtk_tree_store_set (GTK_TREE_STORE (model),
691                           &iter,
692                           STYLE_COLUMN, (style == PANGO_STYLE_ITALIC ? PANGO_STYLE_NORMAL : PANGO_STYLE_ITALIC),
693                           -1);
694       window = (func) (gtk_widget_get_toplevel (GTK_WIDGET (tree_view)));
695       
696       if (window != NULL)
697         {
698           CallbackData *cbdata;
699           
700           cbdata = g_new (CallbackData, 1);
701           cbdata->model = model;
702           cbdata->path = gtk_tree_path_copy (path);
703           
704           g_signal_connect (window, "destroy",
705                             G_CALLBACK (window_closed_cb), cbdata);
706         }
707     }
708 }
709
710 static void
711 selection_cb (GtkTreeSelection *selection,
712               GtkTreeModel     *model)
713 {
714   GtkTreeIter iter;
715   GValue value = {0, };
716
717   if (! gtk_tree_selection_get_selected (selection, NULL, &iter))
718     return;
719
720   gtk_tree_model_get_value (model, &iter,
721                             FILENAME_COLUMN,
722                             &value);
723   if (g_value_get_string (&value))
724     load_file (g_value_get_string (&value));
725   g_value_unset (&value);
726 }
727
728 static GtkWidget *
729 create_text (GtkTextBuffer **buffer,
730              gboolean        is_source)
731 {
732   GtkWidget *scrolled_window;
733   GtkWidget *text_view;
734   PangoFontDescription *font_desc;
735
736   scrolled_window = gtk_scrolled_window_new (NULL, NULL);
737   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
738                                   GTK_POLICY_AUTOMATIC,
739                                   GTK_POLICY_AUTOMATIC);
740   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window),
741                                        GTK_SHADOW_IN);
742   
743   text_view = gtk_text_view_new ();
744   
745   *buffer = gtk_text_buffer_new (NULL);
746   gtk_text_view_set_buffer (GTK_TEXT_VIEW (text_view), *buffer);
747   gtk_text_view_set_editable (GTK_TEXT_VIEW (text_view), FALSE);
748   gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (text_view), FALSE);
749
750   gtk_container_add (GTK_CONTAINER (scrolled_window), text_view);
751   
752   if (is_source)
753     {
754       font_desc = pango_font_description_from_string ("monospace");
755       gtk_widget_modify_font (text_view, font_desc);
756       pango_font_description_free (font_desc);
757
758       gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (text_view),
759                                    GTK_WRAP_NONE);
760     }
761   else
762     {
763       /* Make it a bit nicer for text. */
764       gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (text_view),
765                                    GTK_WRAP_WORD);
766       gtk_text_view_set_pixels_above_lines (GTK_TEXT_VIEW (text_view),
767                                             2);
768       gtk_text_view_set_pixels_below_lines (GTK_TEXT_VIEW (text_view),
769                                             2);
770     }
771   
772   return scrolled_window;
773 }
774
775 static GtkWidget *
776 create_tree (void)
777 {
778   GtkTreeSelection *selection;
779   GtkCellRenderer *cell;
780   GtkWidget *tree_view;
781   GtkTreeViewColumn *column;
782   GtkTreeStore *model;
783   GtkTreeIter iter;
784   GtkWidget *box, *label, *scrolled_window;
785
786   Demo *d = testgtk_demos;
787
788   model = gtk_tree_store_new (NUM_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_INT);
789   tree_view = gtk_tree_view_new ();
790   gtk_tree_view_set_model (GTK_TREE_VIEW (tree_view), GTK_TREE_MODEL (model));
791   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
792
793   gtk_tree_selection_set_mode (GTK_TREE_SELECTION (selection),
794                                GTK_SELECTION_BROWSE);
795   gtk_widget_set_size_request (tree_view, 200, -1);
796
797   /* this code only supports 1 level of children. If we
798    * want more we probably have to use a recursing function.
799    */
800   while (d->title)
801     {
802       Demo *children = d->children;
803
804       gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL);
805
806       gtk_tree_store_set (GTK_TREE_STORE (model),
807                           &iter,
808                           TITLE_COLUMN, d->title,
809                           FILENAME_COLUMN, d->filename,
810                           FUNC_COLUMN, d->func,
811                           STYLE_COLUMN, PANGO_STYLE_NORMAL,
812                           -1);
813
814       d++;
815
816       if (!children)
817         continue;
818       
819       while (children->title)
820         {
821           GtkTreeIter child_iter;
822
823           gtk_tree_store_append (GTK_TREE_STORE (model), &child_iter, &iter);
824           
825           gtk_tree_store_set (GTK_TREE_STORE (model),
826                               &child_iter,
827                               TITLE_COLUMN, children->title,
828                               FILENAME_COLUMN, children->filename,
829                               FUNC_COLUMN, children->func,
830                               STYLE_COLUMN, PANGO_STYLE_NORMAL,
831                               -1);
832           
833           children++;
834         }
835     }
836
837   cell = gtk_cell_renderer_text_new ();
838
839   column = gtk_tree_view_column_new_with_attributes ("Widget (double click for demo)",
840                                                      cell,
841                                                      "text", TITLE_COLUMN,
842                                                      "style", STYLE_COLUMN,
843                                                      NULL);
844   
845   gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view),
846                                GTK_TREE_VIEW_COLUMN (column));
847
848   gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &iter);
849   gtk_tree_selection_select_iter (GTK_TREE_SELECTION (selection), &iter);
850
851   g_signal_connect (selection, "changed", G_CALLBACK (selection_cb), model);
852   g_signal_connect (tree_view, "row_activated", G_CALLBACK (row_activated_cb), model);
853
854   gtk_tree_view_collapse_all (GTK_TREE_VIEW (tree_view));
855   gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tree_view), FALSE);
856                                     
857   scrolled_window = gtk_scrolled_window_new (NULL, NULL);
858   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
859                                   GTK_POLICY_NEVER,
860                                   GTK_POLICY_AUTOMATIC);
861   gtk_container_add (GTK_CONTAINER (scrolled_window), tree_view);
862
863   label = gtk_label_new ("Widget (double click for demo)");
864
865   box = gtk_notebook_new ();
866   gtk_notebook_append_page (GTK_NOTEBOOK (box), scrolled_window, label);
867
868   gtk_widget_grab_focus (tree_view);
869
870    g_object_unref (model);
871
872   return box;
873 }
874
875 static void
876 setup_default_icon (void)
877 {
878   GdkPixbuf *pixbuf;
879   char *filename;
880   GError *err;
881
882   err = NULL;
883
884   pixbuf = NULL;
885   filename = demo_find_file ("gtk-logo-rgb.gif", &err);
886   if (filename)
887     {
888       pixbuf = gdk_pixbuf_new_from_file (filename, &err);
889       g_free (filename);
890     }
891
892   /* Ignoring this error (passing NULL instead of &err above)
893    * would probably be reasonable for most apps.  We're just
894    * showing off.
895    */
896   if (err)
897     {
898       GtkWidget *dialog;
899       
900       dialog = gtk_message_dialog_new (NULL, 0,
901                                        GTK_MESSAGE_ERROR,
902                                        GTK_BUTTONS_CLOSE,
903                                        "Failed to read icon file: %s",
904                                        err->message);
905       g_error_free (err);
906
907       g_signal_connect (dialog, "response",
908                         G_CALLBACK (gtk_widget_destroy), NULL);
909     }
910
911   if (pixbuf)
912     {
913       GList *list;      
914       GdkPixbuf *transparent;
915
916       /* The gtk-logo-rgb icon has a white background, make it transparent */
917       transparent = gdk_pixbuf_add_alpha (pixbuf, TRUE, 0xff, 0xff, 0xff);
918
919       list = NULL;
920       list = g_list_append (list, transparent);
921       gtk_window_set_default_icon_list (list);
922       g_list_free (list);
923       g_object_unref (pixbuf);
924       g_object_unref (transparent);
925     }
926 }
927
928 int
929 main (int argc, char **argv)
930 {
931   GtkWidget *window;
932   GtkWidget *notebook;
933   GtkWidget *hbox;
934   GtkWidget *tree;
935   GtkTextTag *tag;
936
937   /* Most code in gtk-demo is intended to be exemplary, but not
938    * these few lines, which are just a hack so gtk-demo will work
939    * in the GTK tree without installing it.
940    */
941   if (g_file_test ("../../gdk-pixbuf/libpixbufloader-pnm.la",
942                    G_FILE_TEST_EXISTS))
943     {
944       g_setenv ("GDK_PIXBUF_MODULE_FILE", "../../gdk-pixbuf/gdk-pixbuf.loaders", TRUE);
945       g_setenv ("GTK_IM_MODULE_FILE", "../../modules/input/gtk.immodules", TRUE);
946     }
947   /* -- End of hack -- */
948   
949   gtk_init (&argc, &argv);
950
951   setup_default_icon ();
952   
953   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
954   gtk_window_set_title (GTK_WINDOW (window), "GTK+ Code Demos");
955   g_signal_connect_after (window, "destroy",
956                     G_CALLBACK (gtk_main_quit), NULL);
957
958   hbox = gtk_hbox_new (FALSE, 0);
959   gtk_container_add (GTK_CONTAINER (window), hbox);
960
961   tree = create_tree ();
962   gtk_box_pack_start (GTK_BOX (hbox), tree, FALSE, FALSE, 0);
963
964   notebook = gtk_notebook_new ();
965   gtk_box_pack_start (GTK_BOX (hbox), notebook, TRUE, TRUE, 0);
966
967   gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
968                             create_text (&info_buffer, FALSE),
969                             gtk_label_new_with_mnemonic ("_Info"));
970
971   tag = gtk_text_buffer_create_tag (info_buffer, "title",
972                                     "font", "Sans 18",
973                                     NULL);
974    g_object_unref (info_buffer);
975
976   gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
977                             create_text (&source_buffer, TRUE),
978                             gtk_label_new_with_mnemonic ("_Source"));
979
980
981   tag = gtk_text_buffer_create_tag (source_buffer, "comment",
982                                     "foreground", "DodgerBlue",
983                                     NULL);
984   tag = gtk_text_buffer_create_tag (source_buffer, "type",
985                                     "foreground", "ForestGreen",
986                                     NULL);
987   tag = gtk_text_buffer_create_tag (source_buffer, "string",
988                                     "foreground", "RosyBrown",
989                                     "weight", PANGO_WEIGHT_BOLD,
990                                     NULL);
991   tag = gtk_text_buffer_create_tag (source_buffer, "control",
992                                     "foreground", "purple",
993                                     NULL);
994   tag = gtk_text_buffer_create_tag (source_buffer, "preprocessor",
995                                     "style", PANGO_STYLE_OBLIQUE,
996                                     "foreground", "burlywood4",
997                                     NULL);
998   tag = gtk_text_buffer_create_tag (source_buffer, "function",
999                                     "weight", PANGO_WEIGHT_BOLD,
1000                                     "foreground", "DarkGoldenrod4",
1001                                     NULL);
1002    g_object_unref (source_buffer);
1003   
1004   gtk_window_set_default_size (GTK_WINDOW (window), 600, 400);
1005   gtk_widget_show_all (window);
1006   
1007
1008   load_file (testgtk_demos[0].filename);
1009   
1010   gtk_main ();
1011
1012   return 0;
1013 }