]> Pileus Git - ~andy/gtk/blob - demos/gtk-demo/main.c
74aed4edcd554e15e557b3cbe43a960bcaa5b8d6
[~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 static GtkWidget *notebook;
18
19 enum {
20   NAME_COLUMN,
21   TITLE_COLUMN,
22   FILENAME_COLUMN,
23   FUNC_COLUMN,
24   STYLE_COLUMN,
25   NUM_COLUMNS
26 };
27
28 typedef struct _CallbackData CallbackData;
29 struct _CallbackData
30 {
31   GtkTreeModel *model;
32   GtkTreePath *path;
33 };
34
35 #ifdef G_OS_WIN32
36
37 #undef DEMOCODEDIR
38
39 static char *
40 get_democodedir (void)
41 {
42   static char *result = NULL;
43
44   if (result == NULL)
45     {
46       result = g_win32_get_package_installation_directory_of_module (NULL);
47       if (result == NULL)
48         result = "unknown-location";
49
50       result = g_strconcat (result, "\\share\\gtk-3.0\\demo", NULL);
51     }
52
53   return result;
54 }
55
56 #define DEMOCODEDIR get_democodedir ()
57
58 #endif
59
60 /**
61  * demo_find_file:
62  * @base: base filename
63  * @err:  location to store error, or %NULL.
64  *
65  * Looks for @base first in the current directory, then in the
66  * location GTK+ where it will be installed on make install,
67  * returns the first file found.
68  *
69  * Return value: the filename, if found or %NULL
70  */
71 gchar *
72 demo_find_file (const char *base,
73                 GError    **err)
74 {
75   g_return_val_if_fail (err == NULL || *err == NULL, NULL);
76
77   if (g_file_test ("gtk-logo-rgb.gif", G_FILE_TEST_EXISTS) &&
78       g_file_test (base, G_FILE_TEST_EXISTS))
79     return g_strdup (base);
80   else
81     {
82       char *filename = g_build_filename (DEMOCODEDIR, base, NULL);
83       if (!g_file_test (filename, G_FILE_TEST_EXISTS))
84         {
85           g_set_error (err, G_FILE_ERROR, G_FILE_ERROR_NOENT,
86                        "Cannot find demo data file \"%s\"", base);
87           g_free (filename);
88           return NULL;
89         }
90       return filename;
91     }
92 }
93
94 static void
95 window_closed_cb (GtkWidget *window, gpointer data)
96 {
97   CallbackData *cbdata = data;
98   GtkTreeIter iter;
99   PangoStyle style;
100
101   gtk_tree_model_get_iter (cbdata->model, &iter, cbdata->path);
102   gtk_tree_model_get (GTK_TREE_MODEL (cbdata->model), &iter,
103                       STYLE_COLUMN, &style,
104                       -1);
105   if (style == PANGO_STYLE_ITALIC)
106     gtk_tree_store_set (GTK_TREE_STORE (cbdata->model), &iter,
107                         STYLE_COLUMN, PANGO_STYLE_NORMAL,
108                         -1);
109
110   gtk_tree_path_free (cbdata->path);
111   g_free (cbdata);
112 }
113
114 /* Stupid syntax highlighting.
115  *
116  * No regex was used in the making of this highlighting.
117  * It should only work for simple cases.  This is good, as
118  * that's all we should have in the demos.
119  */
120 /* This code should not be used elsewhere, except perhaps as an example of how
121  * to iterate through a text buffer.
122  */
123 enum {
124   STATE_NORMAL,
125   STATE_IN_COMMENT
126 };
127
128 static gchar *tokens[] =
129 {
130   "/*",
131   "\"",
132   NULL
133 };
134
135 static gchar *types[] =
136 {
137   "static",
138   "const ",
139   "void",
140   "gint",
141   " int ",
142   " char ",
143   "gchar ",
144   "gfloat",
145   "float",
146   "double",
147   "gint8",
148   "gint16",
149   "gint32",
150   "guint",
151   "guint8",
152   "guint16",
153   "guint32",
154   "guchar",
155   "glong",
156   "gboolean" ,
157   "gshort",
158   "gushort",
159   "gulong",
160   "gdouble",
161   "gldouble",
162   "gpointer",
163   "NULL",
164   "GList",
165   "GSList",
166   "FALSE",
167   "TRUE",
168   "FILE ",
169   "GtkColorSelection ",
170   "GtkWidget ",
171   "GtkButton ",
172   "GdkColor ",
173   "GdkRectangle ",
174   "GdkEventExpose ",
175   "GdkGC ",
176   "GdkPixbufLoader ",
177   "GdkPixbuf ",
178   "GError",
179   "size_t",
180   "GtkAboutDialog ",
181   "GtkAction ",
182   "GtkActionEntry ",
183   "GtkRadioActionEntry ",
184   "GtkIconFactory ",
185   "GtkStockItem ",
186   "GtkIconSet ",
187   "GtkTextBuffer ",
188   "GtkStatusbar ",
189   "GtkTextIter ",
190   "GtkTextMark ",
191   "GdkEventWindowState ",
192   "GtkActionGroup ",
193   "GtkUIManager ",
194   "GtkRadioAction ",
195   "GtkActionClass ",
196   "GtkToggleActionEntry ",
197   "GtkAssistant ",
198   "GtkBuilder ",
199   "GtkSizeGroup ",
200   "GtkTreeModel ",
201   "GtkTreeSelection ",
202   "GdkDisplay ",
203   "GdkScreen ",
204   "GdkWindow ",
205   "GdkEventButton ",
206   "GdkCursor ",
207   "GtkTreeIter ",
208   "GtkTreeViewColumn ",
209   "GdkDisplayManager ",
210   "GtkClipboard ",
211   "GtkIconSize ",
212   "GtkImage ",
213   "GdkDragContext ",
214   "GtkSelectionData ",
215   "GtkDialog ",
216   "GtkMenuItem ",
217   "GtkListStore ",
218   "GtkCellLayout ",
219   "GtkCellRenderer ",
220   "GtkTreePath ",
221   "GtkTreeStore ",
222   "GtkEntry ",
223   "GtkEditable ",
224   "GtkEditableInterface ",
225   "GdkPixmap ",
226   "GdkEventConfigure ",
227   "GdkEventMotion ",
228   "GdkModifierType ",
229   "GtkEntryCompletion ",
230   "GtkToolItem ",
231   "GDir ",
232   "GtkIconView ",
233   "GtkCellRendererText ",
234   "GtkContainer ",
235   "GtkAccelGroup ",
236   "GtkPaned ",
237   "GtkPrintOperation ",
238   "GtkPrintContext ",
239   "cairo_t ",
240   "PangoLayout "
241   "PangoFontDescription ",
242   "PangoRenderer ",
243   "PangoMatrix ",
244   "PangoContext ",
245   "PangoLayout ",
246   "GtkTable ",
247   "GtkToggleButton ",
248   "GString ",
249   "GtkIconSize ",
250   "GtkTreeView ",
251   "GtkTextTag ",
252   "GdkEvent ",
253   "GdkEventKey ",
254   "GtkTextView ",
255   "GdkEventVisibility ",
256   "GdkBitmap ",
257   "GtkTextChildAnchor ",
258   "GArray ",
259   "GtkCellEditable ",
260   "GtkCellRendererToggle ",
261   NULL
262 };
263
264 static gchar *control[] =
265 {
266   " if ",
267   " while ",
268   " else",
269   " do ",
270   " for ",
271   "?",
272   ":",
273   "return ",
274   "goto ",
275   NULL
276 };
277 void
278 parse_chars (gchar     *text,
279              gchar    **end_ptr,
280              gint      *state,
281              gchar    **tag,
282              gboolean   start)
283 {
284   gint i;
285   gchar *next_token;
286
287   /* Handle comments first */
288   if (*state == STATE_IN_COMMENT)
289     {
290       *end_ptr = strstr (text, "*/");
291       if (*end_ptr)
292         {
293           *end_ptr += 2;
294           *state = STATE_NORMAL;
295           *tag = "comment";
296         }
297       return;
298     }
299
300   *tag = NULL;
301   *end_ptr = NULL;
302
303   /* check for comment */
304   if (!strncmp (text, "/*", 2))
305     {
306       *end_ptr = strstr (text, "*/");
307       if (*end_ptr)
308         *end_ptr += 2;
309       else
310         *state = STATE_IN_COMMENT;
311       *tag = "comment";
312       return;
313     }
314
315   /* check for preprocessor defines */
316   if (*text == '#' && start)
317     {
318       *end_ptr = NULL;
319       *tag = "preprocessor";
320       return;
321     }
322
323   /* functions */
324   if (start && * text != '\t' && *text != ' ' && *text != '{' && *text != '}')
325     {
326       if (strstr (text, "("))
327         {
328           *end_ptr = strstr (text, "(");
329           *tag = "function";
330           return;
331         }
332     }
333   /* check for types */
334   for (i = 0; types[i] != NULL; i++)
335     if (!strncmp (text, types[i], strlen (types[i])) ||
336         (start && types[i][0] == ' ' && !strncmp (text, types[i] + 1, strlen (types[i]) - 1)))
337       {
338         *end_ptr = text + strlen (types[i]);
339         *tag = "type";
340         return;
341       }
342
343   /* check for control */
344   for (i = 0; control[i] != NULL; i++)
345     if (!strncmp (text, control[i], strlen (control[i])))
346       {
347         *end_ptr = text + strlen (control[i]);
348         *tag = "control";
349         return;
350       }
351
352   /* check for string */
353   if (text[0] == '"')
354     {
355       gint maybe_escape = FALSE;
356
357       *end_ptr = text + 1;
358       *tag = "string";
359       while (**end_ptr != '\000')
360         {
361           if (**end_ptr == '\"' && !maybe_escape)
362             {
363               *end_ptr += 1;
364               return;
365             }
366           if (**end_ptr == '\\')
367             maybe_escape = TRUE;
368           else
369             maybe_escape = FALSE;
370           *end_ptr += 1;
371         }
372       return;
373     }
374
375   /* not at the start of a tag.  Find the next one. */
376   for (i = 0; tokens[i] != NULL; i++)
377     {
378       next_token = strstr (text, tokens[i]);
379       if (next_token)
380         {
381           if (*end_ptr)
382             *end_ptr = (*end_ptr<next_token)?*end_ptr:next_token;
383           else
384             *end_ptr = next_token;
385         }
386     }
387
388   for (i = 0; types[i] != NULL; i++)
389     {
390       next_token = strstr (text, types[i]);
391       if (next_token)
392         {
393           if (*end_ptr)
394             *end_ptr = (*end_ptr<next_token)?*end_ptr:next_token;
395           else
396             *end_ptr = next_token;
397         }
398     }
399
400   for (i = 0; control[i] != NULL; i++)
401     {
402       next_token = strstr (text, control[i]);
403       if (next_token)
404         {
405           if (*end_ptr)
406             *end_ptr = (*end_ptr<next_token)?*end_ptr:next_token;
407           else
408             *end_ptr = next_token;
409         }
410     }
411 }
412
413 /* While not as cool as c-mode, this will do as a quick attempt at highlighting */
414 static void
415 fontify (void)
416 {
417   GtkTextIter start_iter, next_iter, tmp_iter;
418   gint state;
419   gchar *text;
420   gchar *start_ptr, *end_ptr;
421   gchar *tag;
422
423   state = STATE_NORMAL;
424
425   gtk_text_buffer_get_iter_at_offset (source_buffer, &start_iter, 0);
426
427   next_iter = start_iter;
428   while (gtk_text_iter_forward_line (&next_iter))
429     {
430       gboolean start = TRUE;
431       start_ptr = text = gtk_text_iter_get_text (&start_iter, &next_iter);
432
433       do
434         {
435           parse_chars (start_ptr, &end_ptr, &state, &tag, start);
436
437           start = FALSE;
438           if (end_ptr)
439             {
440               tmp_iter = start_iter;
441               gtk_text_iter_forward_chars (&tmp_iter, end_ptr - start_ptr);
442             }
443           else
444             {
445               tmp_iter = next_iter;
446             }
447           if (tag)
448             gtk_text_buffer_apply_tag_by_name (source_buffer, tag, &start_iter, &tmp_iter);
449
450           start_iter = tmp_iter;
451           start_ptr = end_ptr;
452         }
453       while (end_ptr);
454
455       g_free (text);
456       start_iter = next_iter;
457     }
458 }
459
460 static GtkWidget *create_text (GtkTextBuffer **buffer, gboolean is_source);
461
462 static void
463 add_data_tab (const gchar *demoname,
464               const gchar *filename)
465 {
466   GtkTextBuffer *buffer = NULL;
467   gchar *resource_name;
468   GBytes *bytes;
469   GtkWidget *widget, *label;
470
471   resource_name = g_strconcat ("/", demoname, "/", filename, NULL);
472   bytes = g_resources_lookup_data (resource_name, 0, NULL);
473   g_assert (bytes);
474   g_free (resource_name);
475
476   widget = create_text (&buffer, FALSE);
477   gtk_widget_show_all (widget);
478   label = gtk_label_new (filename);
479   gtk_widget_show (label);
480   gtk_notebook_append_page (GTK_NOTEBOOK (notebook), widget, label);
481
482   gtk_text_buffer_set_text (buffer, g_bytes_get_data (bytes, NULL), g_bytes_get_size (bytes));
483 }
484
485 static void
486 remove_data_tabs (void)
487 {
488   gint i;
489
490   for (i = gtk_notebook_get_n_pages (GTK_NOTEBOOK (notebook)) - 1; i > 1; i--)
491     gtk_notebook_remove_page (GTK_NOTEBOOK (notebook), i);
492 }
493
494 void
495 load_file (const gchar *demoname,
496            const gchar *filename)
497 {
498   GtkTextIter start, end;
499   char *resource_filename;
500   GError *err = NULL;
501   int state = 0;
502   gboolean in_para = 0;
503   gchar **names, **lines;
504   GBytes *bytes;
505   gint i;
506
507   remove_data_tabs ();
508
509   names = g_strsplit (filename, " ", -1);
510
511   for (i = 1; names[i]; i++) {
512     if (strlen (names[i]) > 0)
513       add_data_tab (demoname, names[i]);
514   }
515
516   if (current_file && !strcmp (current_file, names[0]))
517     goto out;
518
519   g_free (current_file);
520   current_file = g_strdup (names[0]);
521
522   gtk_text_buffer_get_bounds (info_buffer, &start, &end);
523   gtk_text_buffer_delete (info_buffer, &start, &end);
524
525   gtk_text_buffer_get_bounds (source_buffer, &start, &end);
526   gtk_text_buffer_delete (source_buffer, &start, &end);
527
528   resource_filename = g_strconcat ("/sources/", names[0], NULL);
529   bytes = g_resources_lookup_data (resource_filename, 0, &err);
530   g_free (resource_filename);
531
532   if (bytes == NULL)
533     {
534       g_warning ("Cannot open source for %s: %s\n", names[0], err->message);
535       g_error_free (err);
536       goto out;
537     }
538
539   lines = g_strsplit (g_bytes_get_data (bytes, NULL), "\n", -1);
540   g_bytes_unref (bytes);
541
542   gtk_text_buffer_get_iter_at_offset (info_buffer, &start, 0);
543   for (i = 0; lines[i] != NULL; i++)
544     {
545       gchar *p;
546       gchar *q;
547       gchar *r;
548
549       /* Make sure \r is stripped at the end for the poor windows people */
550       lines[i] = g_strchomp (lines[i]);
551
552       p = lines[i];
553       switch (state)
554         {
555         case 0:
556           /* Reading title */
557           while (*p == '/' || *p == '*' || g_ascii_isspace (*p))
558             p++;
559           r = p;
560           while (*r != '\0')
561             {
562               while (*r != '/' && *r != ':' && *r != '\0')
563                 r++;
564               if (*r == '/')
565                 {
566                   r++;
567                   p = r;
568                 }
569               if (r[0] == ':' && r[1] == ':')
570                 *r = '\0';
571             }
572           q = p + strlen (p);
573           while (q > p && g_ascii_isspace (*(q - 1)))
574             q--;
575
576
577           if (q > p)
578             {
579               int len_chars = g_utf8_pointer_to_offset (p, q);
580
581               end = start;
582
583               g_assert (strlen (p) >= q - p);
584               gtk_text_buffer_insert (info_buffer, &end, p, q - p);
585               start = end;
586
587               gtk_text_iter_backward_chars (&start, len_chars);
588               gtk_text_buffer_apply_tag_by_name (info_buffer, "title", &start, &end);
589
590               start = end;
591
592               while (*p && *p != '\n') p++;
593
594               state++;
595             }
596           break;
597
598         case 1:
599           /* Reading body of info section */
600           while (g_ascii_isspace (*p))
601             p++;
602           if (*p == '*' && *(p + 1) == '/')
603             {
604               gtk_text_buffer_get_iter_at_offset (source_buffer, &start, 0);
605               state++;
606             }
607           else
608             {
609               int len;
610
611               while (*p == '*' || g_ascii_isspace (*p))
612                 p++;
613
614               len = strlen (p);
615               while (g_ascii_isspace (*(p + len - 1)))
616                 len--;
617
618               if (len > 0)
619                 {
620                   if (in_para)
621                     gtk_text_buffer_insert (info_buffer, &start, " ", 1);
622
623                   g_assert (strlen (p) >= len);
624                   gtk_text_buffer_insert (info_buffer, &start, p, len);
625                   in_para = 1;
626                 }
627               else
628                 {
629                   gtk_text_buffer_insert (info_buffer, &start, "\n", 1);
630                   in_para = 0;
631                 }
632             }
633           break;
634
635         case 2:
636           /* Skipping blank lines */
637           while (g_ascii_isspace (*p))
638             p++;
639           if (*p)
640             {
641               p = lines[i];
642               state++;
643               /* Fall through */
644             }
645           else
646             break;
647
648         case 3:
649           /* Reading program body */
650           gtk_text_buffer_insert (source_buffer, &start, p, -1);
651           gtk_text_buffer_insert (source_buffer, &start, "\n", 1);
652           break;
653         }
654     }
655
656   fontify ();
657
658   g_strfreev (lines);
659
660 out:
661   g_strfreev (names);
662 }
663
664 void
665 row_activated_cb (GtkTreeView       *tree_view,
666                   GtkTreePath       *path,
667                   GtkTreeViewColumn *column)
668 {
669   GtkTreeIter iter;
670   PangoStyle style;
671   GDoDemoFunc func;
672   GtkWidget *window;
673   GtkTreeModel *model;
674
675   model = gtk_tree_view_get_model (tree_view);
676
677   gtk_tree_model_get_iter (model, &iter, path);
678   gtk_tree_model_get (GTK_TREE_MODEL (model),
679                       &iter,
680                       FUNC_COLUMN, &func,
681                       STYLE_COLUMN, &style,
682                       -1);
683
684   if (func)
685     {
686       gtk_tree_store_set (GTK_TREE_STORE (model),
687                           &iter,
688                           STYLE_COLUMN, (style == PANGO_STYLE_ITALIC ? PANGO_STYLE_NORMAL : PANGO_STYLE_ITALIC),
689                           -1);
690       window = (func) (gtk_widget_get_toplevel (GTK_WIDGET (tree_view)));
691
692       if (window != NULL)
693         {
694           CallbackData *cbdata;
695
696           cbdata = g_new (CallbackData, 1);
697           cbdata->model = model;
698           cbdata->path = gtk_tree_path_copy (path);
699
700           g_signal_connect (window, "destroy",
701                             G_CALLBACK (window_closed_cb), cbdata);
702         }
703     }
704 }
705
706 static void
707 selection_cb (GtkTreeSelection *selection,
708               GtkTreeModel     *model)
709 {
710   GtkTreeIter iter;
711   char *name, *filename;
712
713   if (! gtk_tree_selection_get_selected (selection, NULL, &iter))
714     return;
715
716   gtk_tree_model_get (model, &iter,
717                       NAME_COLUMN, &name,
718                       FILENAME_COLUMN, &filename,
719                       -1);
720
721   if (filename)
722     load_file (name, filename);
723
724   g_free (name);
725   g_free (filename);
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_override_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 = gtk_demos;
787
788   model = gtk_tree_store_new (NUM_COLUMNS, G_TYPE_STRING, 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                           NAME_COLUMN, d->name,
809                           TITLE_COLUMN, d->title,
810                           FILENAME_COLUMN, d->filename,
811                           FUNC_COLUMN, d->func,
812                           STYLE_COLUMN, PANGO_STYLE_NORMAL,
813                           -1);
814
815       d++;
816
817       if (!children)
818         continue;
819
820       while (children->title)
821         {
822           GtkTreeIter child_iter;
823
824           gtk_tree_store_append (GTK_TREE_STORE (model), &child_iter, &iter);
825
826           gtk_tree_store_set (GTK_TREE_STORE (model),
827                               &child_iter,
828                               NAME_COLUMN, children->name,
829                               TITLE_COLUMN, children->title,
830                               FILENAME_COLUMN, children->filename,
831                               FUNC_COLUMN, children->func,
832                               STYLE_COLUMN, PANGO_STYLE_NORMAL,
833                               -1);
834
835           children++;
836         }
837     }
838
839   cell = gtk_cell_renderer_text_new ();
840
841   column = gtk_tree_view_column_new_with_attributes ("Widget (double click for demo)",
842                                                      cell,
843                                                      "text", TITLE_COLUMN,
844                                                      "style", STYLE_COLUMN,
845                                                      NULL);
846
847   gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view),
848                                GTK_TREE_VIEW_COLUMN (column));
849
850   gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &iter);
851   gtk_tree_selection_select_iter (GTK_TREE_SELECTION (selection), &iter);
852
853   g_signal_connect (selection, "changed", G_CALLBACK (selection_cb), model);
854   g_signal_connect (tree_view, "row_activated", G_CALLBACK (row_activated_cb), model);
855
856   gtk_tree_view_collapse_all (GTK_TREE_VIEW (tree_view));
857   gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tree_view), FALSE);
858
859   scrolled_window = gtk_scrolled_window_new (NULL, NULL);
860   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
861                                   GTK_POLICY_NEVER,
862                                   GTK_POLICY_AUTOMATIC);
863   gtk_container_add (GTK_CONTAINER (scrolled_window), tree_view);
864
865   label = gtk_label_new ("Widget (double click for demo)");
866
867   box = gtk_notebook_new ();
868   gtk_notebook_append_page (GTK_NOTEBOOK (box), scrolled_window, label);
869
870   gtk_widget_grab_focus (tree_view);
871
872    g_object_unref (model);
873
874   return box;
875 }
876
877 static void
878 setup_default_icon (void)
879 {
880   GdkPixbuf *pixbuf;
881   char *filename;
882   GError *err;
883
884   err = NULL;
885
886   pixbuf = NULL;
887   filename = demo_find_file ("gtk-logo-rgb.gif", &err);
888   if (filename)
889     {
890       pixbuf = gdk_pixbuf_new_from_file (filename, &err);
891       g_free (filename);
892     }
893
894   /* Ignoring this error (passing NULL instead of &err above)
895    * would probably be reasonable for most apps.  We're just
896    * showing off.
897    */
898   if (err)
899     {
900       GtkWidget *dialog;
901
902       dialog = gtk_message_dialog_new (NULL, 0,
903                                        GTK_MESSAGE_ERROR,
904                                        GTK_BUTTONS_CLOSE,
905                                        "Failed to read icon file: %s",
906                                        err->message);
907       g_error_free (err);
908
909       g_signal_connect (dialog, "response",
910                         G_CALLBACK (gtk_widget_destroy), NULL);
911     }
912
913   if (pixbuf)
914     {
915       GList *list;
916       GdkPixbuf *transparent;
917
918       /* The gtk-logo-rgb icon has a white background, make it transparent */
919       transparent = gdk_pixbuf_add_alpha (pixbuf, TRUE, 0xff, 0xff, 0xff);
920
921       list = NULL;
922       list = g_list_append (list, transparent);
923       gtk_window_set_default_icon_list (list);
924       g_list_free (list);
925       g_object_unref (pixbuf);
926       g_object_unref (transparent);
927     }
928 }
929
930 int
931 main (int argc, char **argv)
932 {
933   GtkWidget *window;
934   GtkWidget *hbox;
935   GtkWidget *tree;
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 ("../../modules/input/immodules.cache", G_FILE_TEST_EXISTS))
942     {
943       g_setenv ("GTK_IM_MODULE_FILE", "../../modules/input/immodules.cache", TRUE);
944     }
945   /* -- End of hack -- */
946
947   gtk_init (&argc, &argv);
948
949   setup_default_icon ();
950
951   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
952   gtk_window_set_title (GTK_WINDOW (window), "GTK+ Code Demos");
953   g_signal_connect_after (window, "destroy",
954                     G_CALLBACK (gtk_main_quit), NULL);
955
956   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
957   gtk_container_add (GTK_CONTAINER (window), hbox);
958
959   tree = create_tree ();
960   gtk_box_pack_start (GTK_BOX (hbox), tree, FALSE, FALSE, 0);
961
962   notebook = gtk_notebook_new ();
963   gtk_box_pack_start (GTK_BOX (hbox), notebook, TRUE, TRUE, 0);
964
965   gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
966                             create_text (&info_buffer, FALSE),
967                             gtk_label_new_with_mnemonic ("_Info"));
968
969   gtk_text_buffer_create_tag (info_buffer, "title",
970                               "font", "Sans 18",
971                               NULL);
972   g_object_unref (info_buffer);
973
974   gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
975                             create_text (&source_buffer, TRUE),
976                             gtk_label_new_with_mnemonic ("_Source"));
977
978
979   gtk_text_buffer_create_tag (source_buffer, "comment",
980                               "foreground", "DodgerBlue",
981                               NULL);
982   gtk_text_buffer_create_tag (source_buffer, "type",
983                               "foreground", "ForestGreen",
984                               NULL);
985   gtk_text_buffer_create_tag (source_buffer, "string",
986                               "foreground", "RosyBrown",
987                               "weight", PANGO_WEIGHT_BOLD,
988                               NULL);
989   gtk_text_buffer_create_tag (source_buffer, "control",
990                               "foreground", "purple",
991                               NULL);
992   gtk_text_buffer_create_tag (source_buffer, "preprocessor",
993                               "style", PANGO_STYLE_OBLIQUE,
994                               "foreground", "burlywood4",
995                               NULL);
996   gtk_text_buffer_create_tag (source_buffer, "function",
997                               "weight", PANGO_WEIGHT_BOLD,
998                               "foreground", "DarkGoldenrod4",
999                               NULL);
1000   g_object_unref (source_buffer);
1001
1002   gtk_window_set_default_size (GTK_WINDOW (window), 600, 400);
1003   gtk_widget_show_all (window);
1004
1005   load_file (gtk_demos[0].name, gtk_demos[0].filename);
1006
1007   gtk_main ();
1008
1009   return 0;
1010 }