8 #include <glib/gstdio.h>
12 static GtkTextBuffer *info_buffer;
13 static GtkTextBuffer *source_buffer;
15 static gchar *current_file = NULL;
17 static GtkWidget *notebook;
27 typedef struct _CallbackData CallbackData;
39 get_democodedir (void)
41 static char *result = NULL;
45 result = g_win32_get_package_installation_directory_of_module (NULL);
47 result = "unknown-location";
49 result = g_strconcat (result, "\\share\\gtk-3.0\\demo", NULL);
55 #define DEMOCODEDIR get_democodedir ()
61 * @base: base filename
62 * @err: location to store error, or %NULL.
64 * Looks for @base first in the current directory, then in the
65 * location GTK+ where it will be installed on make install,
66 * returns the first file found.
68 * Return value: the filename, if found or %NULL
71 demo_find_file (const char *base,
74 g_return_val_if_fail (err == NULL || *err == NULL, NULL);
76 if (g_file_test ("gtk-logo-rgb.gif", G_FILE_TEST_EXISTS) &&
77 g_file_test (base, G_FILE_TEST_EXISTS))
78 return g_strdup (base);
81 char *filename = g_build_filename (DEMOCODEDIR, base, NULL);
82 if (!g_file_test (filename, G_FILE_TEST_EXISTS))
84 g_set_error (err, G_FILE_ERROR, G_FILE_ERROR_NOENT,
85 "Cannot find demo data file \"%s\"", base);
94 window_closed_cb (GtkWidget *window, gpointer data)
96 CallbackData *cbdata = data;
100 gtk_tree_model_get_iter (cbdata->model, &iter, cbdata->path);
101 gtk_tree_model_get (GTK_TREE_MODEL (cbdata->model), &iter,
102 STYLE_COLUMN, &style,
104 if (style == PANGO_STYLE_ITALIC)
105 gtk_tree_store_set (GTK_TREE_STORE (cbdata->model), &iter,
106 STYLE_COLUMN, PANGO_STYLE_NORMAL,
109 gtk_tree_path_free (cbdata->path);
114 read_line (FILE *stream, GString *str)
118 #ifdef HAVE_FLOCKFILE
122 g_string_truncate (str, 0);
128 #ifdef HAVE_FLOCKFILE
129 c = getc_unlocked (stream);
144 #ifdef HAVE_FLOCKFILE
145 int next_c = getc_unlocked (stream);
147 int next_c = getc (stream);
150 if (!(next_c == EOF ||
151 (c == '\r' && next_c == '\n') ||
152 (c == '\n' && next_c == '\r')))
153 ungetc (next_c, stream);
158 g_string_append_c (str, c);
164 #ifdef HAVE_FLOCKFILE
165 funlockfile (stream);
172 /* Stupid syntax highlighting.
174 * No regex was used in the making of this highlighting.
175 * It should only work for simple cases. This is good, as
176 * that's all we should have in the demos.
178 /* This code should not be used elsewhere, except perhaps as an example of how
179 * to iterate through a text buffer.
186 static gchar *tokens[] =
193 static gchar *types[] =
227 "GtkColorSelection ",
241 "GtkRadioActionEntry ",
249 "GdkEventWindowState ",
254 "GtkToggleActionEntry ",
266 "GtkTreeViewColumn ",
267 "GdkDisplayManager ",
282 "GtkEditableInterface ",
284 "GdkEventConfigure ",
287 "GtkEntryCompletion ",
291 "GtkCellRendererText ",
295 "GtkPrintOperation ",
299 "PangoFontDescription ",
313 "GdkEventVisibility ",
315 "GtkTextChildAnchor ",
318 "GtkCellRendererToggle ",
322 static gchar *control[] =
336 parse_chars (gchar *text,
345 /* Handle comments first */
346 if (*state == STATE_IN_COMMENT)
348 *end_ptr = strstr (text, "*/");
352 *state = STATE_NORMAL;
361 /* check for comment */
362 if (!strncmp (text, "/*", 2))
364 *end_ptr = strstr (text, "*/");
368 *state = STATE_IN_COMMENT;
373 /* check for preprocessor defines */
374 if (*text == '#' && start)
377 *tag = "preprocessor";
382 if (start && * text != '\t' && *text != ' ' && *text != '{' && *text != '}')
384 if (strstr (text, "("))
386 *end_ptr = strstr (text, "(");
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)))
396 *end_ptr = text + strlen (types[i]);
401 /* check for control */
402 for (i = 0; control[i] != NULL; i++)
403 if (!strncmp (text, control[i], strlen (control[i])))
405 *end_ptr = text + strlen (control[i]);
410 /* check for string */
413 gint maybe_escape = FALSE;
417 while (**end_ptr != '\000')
419 if (**end_ptr == '\"' && !maybe_escape)
424 if (**end_ptr == '\\')
427 maybe_escape = FALSE;
433 /* not at the start of a tag. Find the next one. */
434 for (i = 0; tokens[i] != NULL; i++)
436 next_token = strstr (text, tokens[i]);
440 *end_ptr = (*end_ptr<next_token)?*end_ptr:next_token;
442 *end_ptr = next_token;
446 for (i = 0; types[i] != NULL; i++)
448 next_token = strstr (text, types[i]);
452 *end_ptr = (*end_ptr<next_token)?*end_ptr:next_token;
454 *end_ptr = next_token;
458 for (i = 0; control[i] != NULL; i++)
460 next_token = strstr (text, control[i]);
464 *end_ptr = (*end_ptr<next_token)?*end_ptr:next_token;
466 *end_ptr = next_token;
471 /* While not as cool as c-mode, this will do as a quick attempt at highlighting */
475 GtkTextIter start_iter, next_iter, tmp_iter;
478 gchar *start_ptr, *end_ptr;
481 state = STATE_NORMAL;
483 gtk_text_buffer_get_iter_at_offset (source_buffer, &start_iter, 0);
485 next_iter = start_iter;
486 while (gtk_text_iter_forward_line (&next_iter))
488 gboolean start = TRUE;
489 start_ptr = text = gtk_text_iter_get_text (&start_iter, &next_iter);
493 parse_chars (start_ptr, &end_ptr, &state, &tag, start);
498 tmp_iter = start_iter;
499 gtk_text_iter_forward_chars (&tmp_iter, end_ptr - start_ptr);
503 tmp_iter = next_iter;
506 gtk_text_buffer_apply_tag_by_name (source_buffer, tag, &start_iter, &tmp_iter);
508 start_iter = tmp_iter;
514 start_iter = next_iter;
518 static GtkWidget *create_text (GtkTextBuffer **buffer, gboolean is_source);
521 add_data_tab (const gchar *filename)
523 GtkTextBuffer *buffer = NULL;
524 gchar *full_filename;
527 GtkWidget *widget, *label;
529 full_filename = demo_find_file (filename, &err);
530 if (!full_filename ||
531 !g_file_get_contents (full_filename, &text, NULL, &err))
533 g_warning ("%s", err->message);
538 widget = create_text (&buffer, FALSE);
539 gtk_widget_show_all (widget);
540 label = gtk_label_new (filename);
541 gtk_widget_show (label);
542 gtk_notebook_append_page (GTK_NOTEBOOK (notebook), widget, label);
544 gtk_text_buffer_set_text (buffer, text, -1);
546 g_free (full_filename);
551 remove_data_tabs (void)
555 for (i = gtk_notebook_get_n_pages (GTK_NOTEBOOK (notebook)) - 1; i > 1; i--)
556 gtk_notebook_remove_page (GTK_NOTEBOOK (notebook), i);
560 load_file (const gchar *filename)
563 GtkTextIter start, end;
566 GString *buffer = g_string_new (NULL);
568 gboolean in_para = 0;
574 names = g_strsplit (filename, " ", -1);
576 for (i = 1; names[i]; i++) {
577 if (strlen (names[i]) > 0)
578 add_data_tab (names[i]);
581 if (current_file && !strcmp (current_file, names[0]))
583 g_string_free (buffer, TRUE);
587 g_free (current_file);
588 current_file = g_strdup (names[0]);
590 gtk_text_buffer_get_bounds (info_buffer, &start, &end);
591 gtk_text_buffer_delete (info_buffer, &start, &end);
593 gtk_text_buffer_get_bounds (source_buffer, &start, &end);
594 gtk_text_buffer_delete (source_buffer, &start, &end);
596 full_filename = demo_find_file (names[0], &err);
599 g_warning ("%s", err->message);
604 file = g_fopen (full_filename, "r");
607 g_warning ("Cannot open %s: %s\n", full_filename, g_strerror (errno));
609 g_free (full_filename);
614 gtk_text_buffer_get_iter_at_offset (info_buffer, &start, 0);
615 while (read_line (file, buffer))
617 gchar *p = buffer->str;
625 while (*p == '/' || *p == '*' || g_ascii_isspace (*p))
630 while (*r != '/' && *r != ':' && *r != '\0')
637 if (r[0] == ':' && r[1] == ':')
641 while (q > p && g_ascii_isspace (*(q - 1)))
647 int len_chars = g_utf8_pointer_to_offset (p, q);
651 g_assert (strlen (p) >= q - p);
652 gtk_text_buffer_insert (info_buffer, &end, p, q - p);
655 gtk_text_iter_backward_chars (&start, len_chars);
656 gtk_text_buffer_apply_tag_by_name (info_buffer, "title", &start, &end);
660 while (*p && *p != '\n') p++;
667 /* Reading body of info section */
668 while (g_ascii_isspace (*p))
670 if (*p == '*' && *(p + 1) == '/')
672 gtk_text_buffer_get_iter_at_offset (source_buffer, &start, 0);
679 while (*p == '*' || g_ascii_isspace (*p))
683 while (g_ascii_isspace (*(p + len - 1)))
689 gtk_text_buffer_insert (info_buffer, &start, " ", 1);
691 g_assert (strlen (p) >= len);
692 gtk_text_buffer_insert (info_buffer, &start, p, len);
697 gtk_text_buffer_insert (info_buffer, &start, "\n", 1);
704 /* Skipping blank lines */
705 while (g_ascii_isspace (*p))
717 /* Reading program body */
718 gtk_text_buffer_insert (source_buffer, &start, p, -1);
719 gtk_text_buffer_insert (source_buffer, &start, "\n", 1);
728 g_string_free (buffer, TRUE);
734 row_activated_cb (GtkTreeView *tree_view,
736 GtkTreeViewColumn *column)
744 model = gtk_tree_view_get_model (tree_view);
746 gtk_tree_model_get_iter (model, &iter, path);
747 gtk_tree_model_get (GTK_TREE_MODEL (model),
750 STYLE_COLUMN, &style,
755 gtk_tree_store_set (GTK_TREE_STORE (model),
757 STYLE_COLUMN, (style == PANGO_STYLE_ITALIC ? PANGO_STYLE_NORMAL : PANGO_STYLE_ITALIC),
759 window = (func) (gtk_widget_get_toplevel (GTK_WIDGET (tree_view)));
763 CallbackData *cbdata;
765 cbdata = g_new (CallbackData, 1);
766 cbdata->model = model;
767 cbdata->path = gtk_tree_path_copy (path);
769 g_signal_connect (window, "destroy",
770 G_CALLBACK (window_closed_cb), cbdata);
776 selection_cb (GtkTreeSelection *selection,
780 GValue value = G_VALUE_INIT;
782 if (! gtk_tree_selection_get_selected (selection, NULL, &iter))
785 gtk_tree_model_get_value (model, &iter,
788 if (g_value_get_string (&value))
789 load_file (g_value_get_string (&value));
790 g_value_unset (&value);
794 create_text (GtkTextBuffer **buffer,
797 GtkWidget *scrolled_window;
798 GtkWidget *text_view;
799 PangoFontDescription *font_desc;
801 scrolled_window = gtk_scrolled_window_new (NULL, NULL);
802 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
803 GTK_POLICY_AUTOMATIC,
804 GTK_POLICY_AUTOMATIC);
805 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window),
808 text_view = gtk_text_view_new ();
810 *buffer = gtk_text_buffer_new (NULL);
811 gtk_text_view_set_buffer (GTK_TEXT_VIEW (text_view), *buffer);
812 gtk_text_view_set_editable (GTK_TEXT_VIEW (text_view), FALSE);
813 gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (text_view), FALSE);
815 gtk_container_add (GTK_CONTAINER (scrolled_window), text_view);
819 font_desc = pango_font_description_from_string ("monospace");
820 gtk_widget_override_font (text_view, font_desc);
821 pango_font_description_free (font_desc);
823 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (text_view),
828 /* Make it a bit nicer for text. */
829 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (text_view),
831 gtk_text_view_set_pixels_above_lines (GTK_TEXT_VIEW (text_view),
833 gtk_text_view_set_pixels_below_lines (GTK_TEXT_VIEW (text_view),
837 return scrolled_window;
843 GtkTreeSelection *selection;
844 GtkCellRenderer *cell;
845 GtkWidget *tree_view;
846 GtkTreeViewColumn *column;
849 GtkWidget *box, *label, *scrolled_window;
853 model = gtk_tree_store_new (NUM_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_INT);
854 tree_view = gtk_tree_view_new ();
855 gtk_tree_view_set_model (GTK_TREE_VIEW (tree_view), GTK_TREE_MODEL (model));
856 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
858 gtk_tree_selection_set_mode (GTK_TREE_SELECTION (selection),
859 GTK_SELECTION_BROWSE);
860 gtk_widget_set_size_request (tree_view, 200, -1);
862 /* this code only supports 1 level of children. If we
863 * want more we probably have to use a recursing function.
867 Demo *children = d->children;
869 gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL);
871 gtk_tree_store_set (GTK_TREE_STORE (model),
873 TITLE_COLUMN, d->title,
874 FILENAME_COLUMN, d->filename,
875 FUNC_COLUMN, d->func,
876 STYLE_COLUMN, PANGO_STYLE_NORMAL,
884 while (children->title)
886 GtkTreeIter child_iter;
888 gtk_tree_store_append (GTK_TREE_STORE (model), &child_iter, &iter);
890 gtk_tree_store_set (GTK_TREE_STORE (model),
892 TITLE_COLUMN, children->title,
893 FILENAME_COLUMN, children->filename,
894 FUNC_COLUMN, children->func,
895 STYLE_COLUMN, PANGO_STYLE_NORMAL,
902 cell = gtk_cell_renderer_text_new ();
904 column = gtk_tree_view_column_new_with_attributes ("Widget (double click for demo)",
906 "text", TITLE_COLUMN,
907 "style", STYLE_COLUMN,
910 gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view),
911 GTK_TREE_VIEW_COLUMN (column));
913 gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &iter);
914 gtk_tree_selection_select_iter (GTK_TREE_SELECTION (selection), &iter);
916 g_signal_connect (selection, "changed", G_CALLBACK (selection_cb), model);
917 g_signal_connect (tree_view, "row_activated", G_CALLBACK (row_activated_cb), model);
919 gtk_tree_view_collapse_all (GTK_TREE_VIEW (tree_view));
920 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tree_view), FALSE);
922 scrolled_window = gtk_scrolled_window_new (NULL, NULL);
923 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
925 GTK_POLICY_AUTOMATIC);
926 gtk_container_add (GTK_CONTAINER (scrolled_window), tree_view);
928 label = gtk_label_new ("Widget (double click for demo)");
930 box = gtk_notebook_new ();
931 gtk_notebook_append_page (GTK_NOTEBOOK (box), scrolled_window, label);
933 gtk_widget_grab_focus (tree_view);
935 g_object_unref (model);
941 setup_default_icon (void)
950 filename = demo_find_file ("gtk-logo-rgb.gif", &err);
953 pixbuf = gdk_pixbuf_new_from_file (filename, &err);
957 /* Ignoring this error (passing NULL instead of &err above)
958 * would probably be reasonable for most apps. We're just
965 dialog = gtk_message_dialog_new (NULL, 0,
968 "Failed to read icon file: %s",
972 g_signal_connect (dialog, "response",
973 G_CALLBACK (gtk_widget_destroy), NULL);
979 GdkPixbuf *transparent;
981 /* The gtk-logo-rgb icon has a white background, make it transparent */
982 transparent = gdk_pixbuf_add_alpha (pixbuf, TRUE, 0xff, 0xff, 0xff);
985 list = g_list_append (list, transparent);
986 gtk_window_set_default_icon_list (list);
988 g_object_unref (pixbuf);
989 g_object_unref (transparent);
994 main (int argc, char **argv)
1000 /* Most code in gtk-demo is intended to be exemplary, but not
1001 * these few lines, which are just a hack so gtk-demo will work
1002 * in the GTK tree without installing it.
1004 if (g_file_test ("../../modules/input/immodules.cache", G_FILE_TEST_EXISTS))
1006 g_setenv ("GTK_IM_MODULE_FILE", "../../modules/input/immodules.cache", TRUE);
1008 /* -- End of hack -- */
1010 gtk_init (&argc, &argv);
1012 setup_default_icon ();
1014 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1015 gtk_window_set_title (GTK_WINDOW (window), "GTK+ Code Demos");
1016 g_signal_connect_after (window, "destroy",
1017 G_CALLBACK (gtk_main_quit), NULL);
1019 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
1020 gtk_container_add (GTK_CONTAINER (window), hbox);
1022 tree = create_tree ();
1023 gtk_box_pack_start (GTK_BOX (hbox), tree, FALSE, FALSE, 0);
1025 notebook = gtk_notebook_new ();
1026 gtk_box_pack_start (GTK_BOX (hbox), notebook, TRUE, TRUE, 0);
1028 gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
1029 create_text (&info_buffer, FALSE),
1030 gtk_label_new_with_mnemonic ("_Info"));
1032 gtk_text_buffer_create_tag (info_buffer, "title",
1035 g_object_unref (info_buffer);
1037 gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
1038 create_text (&source_buffer, TRUE),
1039 gtk_label_new_with_mnemonic ("_Source"));
1042 gtk_text_buffer_create_tag (source_buffer, "comment",
1043 "foreground", "DodgerBlue",
1045 gtk_text_buffer_create_tag (source_buffer, "type",
1046 "foreground", "ForestGreen",
1048 gtk_text_buffer_create_tag (source_buffer, "string",
1049 "foreground", "RosyBrown",
1050 "weight", PANGO_WEIGHT_BOLD,
1052 gtk_text_buffer_create_tag (source_buffer, "control",
1053 "foreground", "purple",
1055 gtk_text_buffer_create_tag (source_buffer, "preprocessor",
1056 "style", PANGO_STYLE_OBLIQUE,
1057 "foreground", "burlywood4",
1059 gtk_text_buffer_create_tag (source_buffer, "function",
1060 "weight", PANGO_WEIGHT_BOLD,
1061 "foreground", "DarkGoldenrod4",
1063 g_object_unref (source_buffer);
1065 gtk_window_set_default_size (GTK_WINDOW (window), 600, 400);
1066 gtk_widget_show_all (window);
1068 load_file (gtk_demos[0].filename);