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;
28 typedef struct _CallbackData CallbackData;
40 get_democodedir (void)
42 static char *result = NULL;
46 result = g_win32_get_package_installation_directory_of_module (NULL);
48 result = "unknown-location";
50 result = g_strconcat (result, "\\share\\gtk-3.0\\demo", NULL);
56 #define DEMOCODEDIR get_democodedir ()
62 * @base: base filename
63 * @err: location to store error, or %NULL.
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.
69 * Return value: the filename, if found or %NULL
72 demo_find_file (const char *base,
75 g_return_val_if_fail (err == NULL || *err == NULL, NULL);
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);
82 char *filename = g_build_filename (DEMOCODEDIR, base, NULL);
83 if (!g_file_test (filename, G_FILE_TEST_EXISTS))
85 g_set_error (err, G_FILE_ERROR, G_FILE_ERROR_NOENT,
86 "Cannot find demo data file \"%s\"", base);
95 window_closed_cb (GtkWidget *window, gpointer data)
97 CallbackData *cbdata = data;
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,
105 if (style == PANGO_STYLE_ITALIC)
106 gtk_tree_store_set (GTK_TREE_STORE (cbdata->model), &iter,
107 STYLE_COLUMN, PANGO_STYLE_NORMAL,
110 gtk_tree_path_free (cbdata->path);
114 /* Stupid syntax highlighting.
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.
120 /* This code should not be used elsewhere, except perhaps as an example of how
121 * to iterate through a text buffer.
128 static gchar *tokens[] =
135 static gchar *types[] =
169 "GtkColorSelection ",
183 "GtkRadioActionEntry ",
191 "GdkEventWindowState ",
196 "GtkToggleActionEntry ",
208 "GtkTreeViewColumn ",
209 "GdkDisplayManager ",
224 "GtkEditableInterface ",
226 "GdkEventConfigure ",
229 "GtkEntryCompletion ",
233 "GtkCellRendererText ",
237 "GtkPrintOperation ",
241 "PangoFontDescription ",
255 "GdkEventVisibility ",
257 "GtkTextChildAnchor ",
260 "GtkCellRendererToggle ",
264 static gchar *control[] =
278 parse_chars (gchar *text,
287 /* Handle comments first */
288 if (*state == STATE_IN_COMMENT)
290 *end_ptr = strstr (text, "*/");
294 *state = STATE_NORMAL;
303 /* check for comment */
304 if (!strncmp (text, "/*", 2))
306 *end_ptr = strstr (text, "*/");
310 *state = STATE_IN_COMMENT;
315 /* check for preprocessor defines */
316 if (*text == '#' && start)
319 *tag = "preprocessor";
324 if (start && * text != '\t' && *text != ' ' && *text != '{' && *text != '}')
326 if (strstr (text, "("))
328 *end_ptr = strstr (text, "(");
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)))
338 *end_ptr = text + strlen (types[i]);
343 /* check for control */
344 for (i = 0; control[i] != NULL; i++)
345 if (!strncmp (text, control[i], strlen (control[i])))
347 *end_ptr = text + strlen (control[i]);
352 /* check for string */
355 gint maybe_escape = FALSE;
359 while (**end_ptr != '\000')
361 if (**end_ptr == '\"' && !maybe_escape)
366 if (**end_ptr == '\\')
369 maybe_escape = FALSE;
375 /* not at the start of a tag. Find the next one. */
376 for (i = 0; tokens[i] != NULL; i++)
378 next_token = strstr (text, tokens[i]);
382 *end_ptr = (*end_ptr<next_token)?*end_ptr:next_token;
384 *end_ptr = next_token;
388 for (i = 0; types[i] != NULL; i++)
390 next_token = strstr (text, types[i]);
394 *end_ptr = (*end_ptr<next_token)?*end_ptr:next_token;
396 *end_ptr = next_token;
400 for (i = 0; control[i] != NULL; i++)
402 next_token = strstr (text, control[i]);
406 *end_ptr = (*end_ptr<next_token)?*end_ptr:next_token;
408 *end_ptr = next_token;
413 /* While not as cool as c-mode, this will do as a quick attempt at highlighting */
417 GtkTextIter start_iter, next_iter, tmp_iter;
420 gchar *start_ptr, *end_ptr;
423 state = STATE_NORMAL;
425 gtk_text_buffer_get_iter_at_offset (source_buffer, &start_iter, 0);
427 next_iter = start_iter;
428 while (gtk_text_iter_forward_line (&next_iter))
430 gboolean start = TRUE;
431 start_ptr = text = gtk_text_iter_get_text (&start_iter, &next_iter);
435 parse_chars (start_ptr, &end_ptr, &state, &tag, start);
440 tmp_iter = start_iter;
441 gtk_text_iter_forward_chars (&tmp_iter, end_ptr - start_ptr);
445 tmp_iter = next_iter;
448 gtk_text_buffer_apply_tag_by_name (source_buffer, tag, &start_iter, &tmp_iter);
450 start_iter = tmp_iter;
456 start_iter = next_iter;
460 static GtkWidget *create_text (GtkTextBuffer **buffer, gboolean is_source);
463 add_data_tab (const gchar *demoname,
464 const gchar *filename)
466 GtkTextBuffer *buffer = NULL;
467 gchar *resource_name;
469 GtkWidget *widget, *label;
471 resource_name = g_strconcat ("/", demoname, "/", filename, NULL);
472 bytes = g_resources_lookup_data (resource_name, 0, NULL);
474 g_free (resource_name);
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);
482 gtk_text_buffer_set_text (buffer, g_bytes_get_data (bytes, NULL), g_bytes_get_size (bytes));
486 remove_data_tabs (void)
490 for (i = gtk_notebook_get_n_pages (GTK_NOTEBOOK (notebook)) - 1; i > 1; i--)
491 gtk_notebook_remove_page (GTK_NOTEBOOK (notebook), i);
495 load_file (const gchar *demoname,
496 const gchar *filename)
498 GtkTextIter start, end;
499 char *resource_filename;
502 gboolean in_para = 0;
503 gchar **names, **lines;
509 names = g_strsplit (filename, " ", -1);
511 for (i = 1; names[i]; i++) {
512 if (strlen (names[i]) > 0)
513 add_data_tab (demoname, names[i]);
516 if (current_file && !strcmp (current_file, names[0]))
519 g_free (current_file);
520 current_file = g_strdup (names[0]);
522 gtk_text_buffer_get_bounds (info_buffer, &start, &end);
523 gtk_text_buffer_delete (info_buffer, &start, &end);
525 gtk_text_buffer_get_bounds (source_buffer, &start, &end);
526 gtk_text_buffer_delete (source_buffer, &start, &end);
528 resource_filename = g_strconcat ("/sources/", names[0], NULL);
529 bytes = g_resources_lookup_data (resource_filename, 0, &err);
530 g_free (resource_filename);
534 g_warning ("Cannot open source for %s: %s\n", names[0], err->message);
539 lines = g_strsplit (g_bytes_get_data (bytes, NULL), "\n", -1);
540 g_bytes_unref (bytes);
542 gtk_text_buffer_get_iter_at_offset (info_buffer, &start, 0);
543 for (i = 0; lines[i] != NULL; i++)
549 /* Make sure \r is stripped at the end for the poor windows people */
550 lines[i] = g_strchomp (lines[i]);
557 while (*p == '/' || *p == '*' || g_ascii_isspace (*p))
562 while (*r != '/' && *r != ':' && *r != '\0')
569 if (r[0] == ':' && r[1] == ':')
573 while (q > p && g_ascii_isspace (*(q - 1)))
579 int len_chars = g_utf8_pointer_to_offset (p, q);
583 g_assert (strlen (p) >= q - p);
584 gtk_text_buffer_insert (info_buffer, &end, p, q - p);
587 gtk_text_iter_backward_chars (&start, len_chars);
588 gtk_text_buffer_apply_tag_by_name (info_buffer, "title", &start, &end);
592 while (*p && *p != '\n') p++;
599 /* Reading body of info section */
600 while (g_ascii_isspace (*p))
602 if (*p == '*' && *(p + 1) == '/')
604 gtk_text_buffer_get_iter_at_offset (source_buffer, &start, 0);
611 while (*p == '*' || g_ascii_isspace (*p))
615 while (g_ascii_isspace (*(p + len - 1)))
621 gtk_text_buffer_insert (info_buffer, &start, " ", 1);
623 g_assert (strlen (p) >= len);
624 gtk_text_buffer_insert (info_buffer, &start, p, len);
629 gtk_text_buffer_insert (info_buffer, &start, "\n", 1);
636 /* Skipping blank lines */
637 while (g_ascii_isspace (*p))
649 /* Reading program body */
650 gtk_text_buffer_insert (source_buffer, &start, p, -1);
651 gtk_text_buffer_insert (source_buffer, &start, "\n", 1);
665 row_activated_cb (GtkTreeView *tree_view,
667 GtkTreeViewColumn *column)
675 model = gtk_tree_view_get_model (tree_view);
677 gtk_tree_model_get_iter (model, &iter, path);
678 gtk_tree_model_get (GTK_TREE_MODEL (model),
681 STYLE_COLUMN, &style,
686 gtk_tree_store_set (GTK_TREE_STORE (model),
688 STYLE_COLUMN, (style == PANGO_STYLE_ITALIC ? PANGO_STYLE_NORMAL : PANGO_STYLE_ITALIC),
690 window = (func) (gtk_widget_get_toplevel (GTK_WIDGET (tree_view)));
694 CallbackData *cbdata;
696 cbdata = g_new (CallbackData, 1);
697 cbdata->model = model;
698 cbdata->path = gtk_tree_path_copy (path);
700 g_signal_connect (window, "destroy",
701 G_CALLBACK (window_closed_cb), cbdata);
707 selection_cb (GtkTreeSelection *selection,
711 char *name, *filename;
713 if (! gtk_tree_selection_get_selected (selection, NULL, &iter))
716 gtk_tree_model_get (model, &iter,
718 FILENAME_COLUMN, &filename,
722 load_file (name, filename);
729 create_text (GtkTextBuffer **buffer,
732 GtkWidget *scrolled_window;
733 GtkWidget *text_view;
734 PangoFontDescription *font_desc;
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),
743 text_view = gtk_text_view_new ();
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);
750 gtk_container_add (GTK_CONTAINER (scrolled_window), text_view);
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);
758 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (text_view),
763 /* Make it a bit nicer for text. */
764 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (text_view),
766 gtk_text_view_set_pixels_above_lines (GTK_TEXT_VIEW (text_view),
768 gtk_text_view_set_pixels_below_lines (GTK_TEXT_VIEW (text_view),
772 return scrolled_window;
778 GtkTreeSelection *selection;
779 GtkCellRenderer *cell;
780 GtkWidget *tree_view;
781 GtkTreeViewColumn *column;
784 GtkWidget *box, *label, *scrolled_window;
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));
793 gtk_tree_selection_set_mode (GTK_TREE_SELECTION (selection),
794 GTK_SELECTION_BROWSE);
795 gtk_widget_set_size_request (tree_view, 200, -1);
797 /* this code only supports 1 level of children. If we
798 * want more we probably have to use a recursing function.
802 Demo *children = d->children;
804 gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL);
806 gtk_tree_store_set (GTK_TREE_STORE (model),
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,
820 while (children->title)
822 GtkTreeIter child_iter;
824 gtk_tree_store_append (GTK_TREE_STORE (model), &child_iter, &iter);
826 gtk_tree_store_set (GTK_TREE_STORE (model),
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,
839 cell = gtk_cell_renderer_text_new ();
841 column = gtk_tree_view_column_new_with_attributes ("Widget (double click for demo)",
843 "text", TITLE_COLUMN,
844 "style", STYLE_COLUMN,
847 gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view),
848 GTK_TREE_VIEW_COLUMN (column));
850 gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &iter);
851 gtk_tree_selection_select_iter (GTK_TREE_SELECTION (selection), &iter);
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);
856 gtk_tree_view_collapse_all (GTK_TREE_VIEW (tree_view));
857 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tree_view), FALSE);
859 scrolled_window = gtk_scrolled_window_new (NULL, NULL);
860 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
862 GTK_POLICY_AUTOMATIC);
863 gtk_container_add (GTK_CONTAINER (scrolled_window), tree_view);
865 label = gtk_label_new ("Widget (double click for demo)");
867 box = gtk_notebook_new ();
868 gtk_notebook_append_page (GTK_NOTEBOOK (box), scrolled_window, label);
870 gtk_widget_grab_focus (tree_view);
872 g_object_unref (model);
878 setup_default_icon (void)
887 filename = demo_find_file ("gtk-logo-rgb.gif", &err);
890 pixbuf = gdk_pixbuf_new_from_file (filename, &err);
894 /* Ignoring this error (passing NULL instead of &err above)
895 * would probably be reasonable for most apps. We're just
902 dialog = gtk_message_dialog_new (NULL, 0,
905 "Failed to read icon file: %s",
909 g_signal_connect (dialog, "response",
910 G_CALLBACK (gtk_widget_destroy), NULL);
916 GdkPixbuf *transparent;
918 /* The gtk-logo-rgb icon has a white background, make it transparent */
919 transparent = gdk_pixbuf_add_alpha (pixbuf, TRUE, 0xff, 0xff, 0xff);
922 list = g_list_append (list, transparent);
923 gtk_window_set_default_icon_list (list);
925 g_object_unref (pixbuf);
926 g_object_unref (transparent);
931 main (int argc, char **argv)
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.
941 if (g_file_test ("../../modules/input/immodules.cache", G_FILE_TEST_EXISTS))
943 g_setenv ("GTK_IM_MODULE_FILE", "../../modules/input/immodules.cache", TRUE);
945 /* -- End of hack -- */
947 gtk_init (&argc, &argv);
949 setup_default_icon ();
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);
956 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
957 gtk_container_add (GTK_CONTAINER (window), hbox);
959 tree = create_tree ();
960 gtk_box_pack_start (GTK_BOX (hbox), tree, FALSE, FALSE, 0);
962 notebook = gtk_notebook_new ();
963 gtk_box_pack_start (GTK_BOX (hbox), notebook, TRUE, TRUE, 0);
965 gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
966 create_text (&info_buffer, FALSE),
967 gtk_label_new_with_mnemonic ("_Info"));
969 gtk_text_buffer_create_tag (info_buffer, "title",
972 g_object_unref (info_buffer);
974 gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
975 create_text (&source_buffer, TRUE),
976 gtk_label_new_with_mnemonic ("_Source"));
979 gtk_text_buffer_create_tag (source_buffer, "comment",
980 "foreground", "DodgerBlue",
982 gtk_text_buffer_create_tag (source_buffer, "type",
983 "foreground", "ForestGreen",
985 gtk_text_buffer_create_tag (source_buffer, "string",
986 "foreground", "RosyBrown",
987 "weight", PANGO_WEIGHT_BOLD,
989 gtk_text_buffer_create_tag (source_buffer, "control",
990 "foreground", "purple",
992 gtk_text_buffer_create_tag (source_buffer, "preprocessor",
993 "style", PANGO_STYLE_OBLIQUE,
994 "foreground", "burlywood4",
996 gtk_text_buffer_create_tag (source_buffer, "function",
997 "weight", PANGO_WEIGHT_BOLD,
998 "foreground", "DarkGoldenrod4",
1000 g_object_unref (source_buffer);
1002 gtk_window_set_default_size (GTK_WINDOW (window), 600, 400);
1003 gtk_widget_show_all (window);
1005 load_file (gtk_demos[0].name, gtk_demos[0].filename);