From: Matthias Clasen Date: Thu, 7 Feb 2019 09:12:09 +0000 (-0500) Subject: tools: Split gtk-builder-tool X-Git-Tag: archive/raspbian/4.4.1+ds1-2+rpi1^2~18^2~21^2~761^2~8 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=addcf2e52626635aafaea591e5bba82b526de92b;p=gtk4.git tools: Split gtk-builder-tool Put each command into its own file. This is in preparation for redoing the simplify command. --- diff --git a/gtk/tools/gtk-builder-tool-enumerate.c b/gtk/tools/gtk-builder-tool-enumerate.c new file mode 100644 index 0000000000..c2428d918c --- /dev/null +++ b/gtk/tools/gtk-builder-tool-enumerate.c @@ -0,0 +1,74 @@ +/* Copyright 2015 Red Hat, Inc. + * + * GTK+ is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * GLib is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with GTK+; see the file COPYING. If not, + * see . + * + * Author: Matthias Clasen + */ + +#include +#include +#include + +#include +#include +#include +#include +#include "gtkbuilderprivate.h" + +static const gchar * +object_get_name (GObject *object) +{ + if (GTK_IS_BUILDABLE (object)) + return gtk_buildable_get_name (GTK_BUILDABLE (object)); + else + return g_object_get_data (object, "gtk-builder-name"); +} + +void +do_enumerate (int *argc, char ***argv) +{ + GtkBuilder *builder; + GError *error = NULL; + gint ret; + GSList *list, *l; + GObject *object; + const gchar *name; + const gchar *filename; + + filename = (*argv)[1]; + + builder = gtk_builder_new (); + ret = gtk_builder_add_from_file (builder, filename, &error); + + if (ret == 0) + { + g_printerr ("%s\n", error->message); + exit (1); + } + + list = gtk_builder_get_objects (builder); + for (l = list; l; l = l->next) + { + object = l->data; + name = object_get_name (object); + if (g_str_has_prefix (name, "___") && g_str_has_suffix (name, "___")) + continue; + + g_printf ("%s (%s)\n", name, g_type_name_from_instance ((GTypeInstance*)object)); + } + g_slist_free (list); + + g_object_unref (builder); +} diff --git a/gtk/tools/gtk-builder-tool-preview.c b/gtk/tools/gtk-builder-tool-preview.c new file mode 100644 index 0000000000..cc19251235 --- /dev/null +++ b/gtk/tools/gtk-builder-tool-preview.c @@ -0,0 +1,197 @@ +/* Copyright 2015 Red Hat, Inc. + * + * GTK+ is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * GLib is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with GTK+; see the file COPYING. If not, + * see . + * + * Author: Matthias Clasen + */ + +#include +#include +#include + +#include +#include +#include +#include +#include "gtkbuilderprivate.h" + +static void +set_window_title (GtkWindow *window, + const char *filename, + const char *id) +{ + gchar *name; + gchar *title; + + name = g_path_get_basename (filename); + + if (id) + title = g_strdup_printf ("%s in %s", id, name); + else + title = g_strdup (name); + + gtk_window_set_title (window, title); + + g_free (title); + g_free (name); +} + +static void +preview_file (const char *filename, + const char *id, + const char *cssfile) +{ + GtkBuilder *builder; + GError *error = NULL; + GObject *object; + GtkWidget *window; + + if (cssfile) + { + GtkCssProvider *provider; + + provider = gtk_css_provider_new (); + gtk_css_provider_load_from_path (provider, cssfile); + + gtk_style_context_add_provider_for_display (gdk_display_get_default (), + GTK_STYLE_PROVIDER (provider), + GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + } + + builder = gtk_builder_new (); + if (!gtk_builder_add_from_file (builder, filename, &error)) + { + g_printerr ("%s\n", error->message); + exit (1); + } + + object = NULL; + + if (id) + { + object = gtk_builder_get_object (builder, id); + } + else + { + GSList *objects, *l; + + objects = gtk_builder_get_objects (builder); + for (l = objects; l; l = l->next) + { + GObject *obj = l->data; + + if (GTK_IS_WINDOW (obj)) + { + object = obj; + break; + } + else if (GTK_IS_WIDGET (obj)) + { + if (object == NULL) + object = obj; + } + } + g_slist_free (objects); + } + + if (object == NULL) + { + if (id) + g_printerr ("No object with ID '%s' found\n", id); + else + g_printerr ("No previewable object found\n"); + exit (1); + } + + if (!GTK_IS_WIDGET (object)) + { + g_printerr ("Objects of type %s can't be previewed\n", G_OBJECT_TYPE_NAME (object)); + exit (1); + } + + if (GTK_IS_WINDOW (object)) + window = GTK_WIDGET (object); + else + { + GtkWidget *widget = GTK_WIDGET (object); + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + + if (GTK_IS_BUILDABLE (object)) + id = gtk_buildable_get_name (GTK_BUILDABLE (object)); + + set_window_title (GTK_WINDOW (window), filename, id); + + g_object_ref (widget); + if (gtk_widget_get_parent (widget) != NULL) + gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (widget)), widget); + gtk_container_add (GTK_CONTAINER (window), widget); + g_object_unref (widget); + } + + gtk_window_present (GTK_WINDOW (window)); + + gtk_main (); + + g_object_unref (builder); +} + +void +do_preview (int *argc, + const char ***argv) +{ + GOptionContext *context; + char *id = NULL; + char *css = NULL; + char **filenames = NULL; + const GOptionEntry entries[] = { + { "id", 0, 0, G_OPTION_ARG_STRING, &id, NULL, NULL }, + { "css", 0, 0, G_OPTION_ARG_FILENAME, &css, NULL, NULL }, + { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, NULL, NULL }, + { NULL, } + }; + GError *error = NULL; + + context = g_option_context_new (NULL); + g_option_context_set_help_enabled (context, FALSE); + g_option_context_add_main_entries (context, entries, NULL); + + if (!g_option_context_parse (context, argc, (char ***)argv, &error)) + { + g_printerr ("%s\n", error->message); + g_error_free (error); + exit (1); + } + + g_option_context_free (context); + + if (filenames == NULL) + { + g_printerr ("No .ui file specified\n"); + exit (1); + } + + if (g_strv_length (filenames) > 1) + { + g_printerr ("Can only preview a single .ui file\n"); + exit (1); + } + + preview_file (filenames[0], id, css); + + g_strfreev (filenames); + g_free (id); + g_free (css); +} diff --git a/gtk/tools/gtk-builder-tool-simplify.c b/gtk/tools/gtk-builder-tool-simplify.c new file mode 100644 index 0000000000..ec3cd271ec --- /dev/null +++ b/gtk/tools/gtk-builder-tool-simplify.c @@ -0,0 +1,717 @@ +/* Copyright 2015 Red Hat, Inc. + * + * GTK+ is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * GLib is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with GTK+; see the file COPYING. If not, + * see . + * + * Author: Matthias Clasen + */ + +#include +#include +#include + +#include +#include +#include +#include +#include "gtkbuilderprivate.h" + + +typedef struct { + GtkBuilder *builder; + GList *classes; + gboolean packing; + gboolean packing_started; + gboolean cell_packing; + gboolean cell_packing_started; + gint in_child; + gint child_started; + gchar **attribute_names; + gchar **attribute_values; + GString *value; + gboolean unclosed_starttag; + gint indent; + char *input_filename; + char *output_filename; + FILE *output; +} MyParserData; + +static void +canonicalize_key (gchar *key) +{ + gchar *p; + + for (p = key; *p != 0; p++) + { + gchar c = *p; + + /* We may meet something like AtkObject::accessible-name */ + if (c == ':' && ((p > key && p[-1] == ':') || p[1] == ':')) + continue; + + if (c != '-' && + (c < '0' || c > '9') && + (c < 'A' || c > 'Z') && + (c < 'a' || c > 'z')) + *p = '-'; + } +} + +static GParamSpec * +get_property_pspec (MyParserData *data, + const gchar *class_name, + const gchar *property_name) +{ + GType type; + GObjectClass *class; + GParamSpec *pspec; + gchar *canonical_name; + + type = g_type_from_name (class_name); + if (type == G_TYPE_INVALID) + { + GtkBuilder *builder = gtk_builder_new (); + type = gtk_builder_get_type_from_name (builder, class_name); + g_object_unref (builder); + if (type == G_TYPE_INVALID) + return NULL; + } + + class = g_type_class_ref (type); + canonical_name = g_strdup (property_name); + canonicalize_key (canonical_name); + if (data->packing) + pspec = gtk_container_class_find_child_property (class, canonical_name); + else if (data->cell_packing) + { + GObjectClass *cell_class; + + /* We're just assuming that the cell layout is using a GtkCellAreaBox. */ + cell_class = g_type_class_ref (GTK_TYPE_CELL_AREA_BOX); + pspec = gtk_cell_area_class_find_cell_property (GTK_CELL_AREA_CLASS (cell_class), canonical_name); + g_type_class_unref (cell_class); + } + else + pspec = g_object_class_find_property (class, canonical_name); + g_free (canonical_name); + g_type_class_unref (class); + + return pspec; +} + + +static gboolean +value_is_default (MyParserData *data, + const gchar *class_name, + const gchar *property_name, + const gchar *value_string) +{ + GValue value = { 0, }; + gboolean ret; + GError *error = NULL; + GParamSpec *pspec; + + pspec = get_property_pspec (data, class_name, property_name); + + if (pspec == NULL) + { + if (data->packing) + g_printerr (_("Packing property %s::%s not found\n"), class_name, property_name); + else if (data->cell_packing) + g_printerr (_("Cell property %s::%s not found\n"), class_name, property_name); + else + g_printerr (_("Property %s::%s not found\n"), class_name, property_name); + return FALSE; + } + else if (g_type_is_a (G_PARAM_SPEC_VALUE_TYPE (pspec), G_TYPE_OBJECT)) + return FALSE; + + if (!gtk_builder_value_from_string (data->builder, pspec, value_string, &value, &error)) + { + g_printerr (_("Couldn’t parse value for %s::%s: %s\n"), class_name, property_name, error->message); + g_error_free (error); + ret = FALSE; + } + else + ret = g_param_value_defaults (pspec, &value); + + g_value_reset (&value); + + return ret; +} + +static gboolean +property_is_boolean (MyParserData *data, + const gchar *class_name, + const gchar *property_name) +{ + GParamSpec *pspec; + + pspec = get_property_pspec (data, class_name, property_name); + if (pspec) + return G_PARAM_SPEC_VALUE_TYPE (pspec) == G_TYPE_BOOLEAN; + + return FALSE; +} + +static const gchar * +canonical_boolean_value (MyParserData *data, + const gchar *string) +{ + GValue value = G_VALUE_INIT; + gboolean b = FALSE; + + if (gtk_builder_value_from_string_type (data->builder, G_TYPE_BOOLEAN, string, &value, NULL)) + b = g_value_get_boolean (&value); + + return b ? "1" : "0"; +} + +/* A number of properties unfortunately can't be omitted even + * if they are nominally set to their default value. In many + * cases, this is due to subclasses not overriding the default + * value from the superclass. + */ +static gboolean +needs_explicit_setting (MyParserData *data, + const gchar *class_name, + const gchar *property_name) +{ + struct _Prop { + const char *class; + const char *property; + gboolean packing; + } props[] = { + { "GtkAboutDialog", "program-name", 0 }, + { "GtkCalendar", "year", 0 }, + { "GtkCalendar", "month", 0 }, + { "GtkCalendar", "day", 0 }, + { "GtkPlacesSidebar", "show-desktop", 0 }, + { "GtkRadioButton", "draw-indicator", 0 }, + { "GtkGrid", "left-attach", 1 }, + { "GtkGrid", "top-attach", 1 }, + { "GtkWidget", "hexpand", 0 }, + { "GtkWidget", "vexpand", 0 }, + { NULL, NULL, 0 } + }; + gchar *canonical_name; + gboolean found; + gint k; + + canonical_name = g_strdup (property_name); + g_strdelimit (canonical_name, "_", '-'); + + found = FALSE; + for (k = 0; props[k].class; k++) + { + if (strcmp (class_name, props[k].class) == 0 && + strcmp (canonical_name, props[k].property) == 0 && + data->packing == props[k].packing) + { + found = TRUE; + break; + } + } + + g_free (canonical_name); + + return found; +} + +static void +maybe_start_packing (MyParserData *data) +{ + if (data->packing) + { + if (!data->packing_started) + { + g_fprintf (data->output, "%*s\n", data->indent, ""); + data->indent += 2; + data->packing_started = TRUE; + } + } +} + +static void +maybe_start_cell_packing (MyParserData *data) +{ + if (data->cell_packing) + { + if (!data->cell_packing_started) + { + g_fprintf (data->output, "%*s\n", data->indent, ""); + data->indent += 2; + data->cell_packing_started = TRUE; + } + } +} + +static void +maybe_start_child (MyParserData *data) +{ + if (data->in_child > 0) + { + if (data->child_started < data->in_child) + { + g_fprintf (data->output, "%*s\n", data->indent, ""); + data->indent += 2; + data->child_started += 1; + } + } +} + +static void +maybe_emit_property (MyParserData *data) +{ + gint i; + gboolean bound; + gboolean translatable; + gchar *escaped; + const gchar *class_name; + const gchar *property_name; + const gchar *value_string; + + class_name = (const gchar *)data->classes->data; + property_name = ""; + value_string = (const gchar *)data->value->str; + + bound = FALSE; + translatable = FALSE; + for (i = 0; data->attribute_names[i]; i++) + { + if (strcmp (data->attribute_names[i], "bind-source") == 0 || + strcmp (data->attribute_names[i], "bind_source") == 0) + bound = TRUE; + else if (strcmp (data->attribute_names[i], "translatable") == 0) + translatable = TRUE; + else if (strcmp (data->attribute_names[i], "name") == 0) + property_name = (const gchar *)data->attribute_values[i]; + } + + if (!translatable && + !bound && + !needs_explicit_setting (data, class_name, property_name)) + { + for (i = 0; data->attribute_names[i]; i++) + { + if (strcmp (data->attribute_names[i], "name") == 0) + { + if (data->classes == NULL) + break; + + if (value_is_default (data, class_name, property_name, value_string)) + return; + } + } + } + + maybe_start_packing (data); + maybe_start_cell_packing (data); + + g_fprintf (data->output, "%*sindent, ""); + for (i = 0; data->attribute_names[i]; i++) + { + if (!translatable && + (strcmp (data->attribute_names[i], "comments") == 0 || + strcmp (data->attribute_names[i], "context") == 0)) + continue; + + escaped = g_markup_escape_text (data->attribute_values[i], -1); + + if (strcmp (data->attribute_names[i], "name") == 0) + canonicalize_key (escaped); + + g_fprintf (data->output, " %s=\"%s\"", data->attribute_names[i], escaped); + g_free (escaped); + } + + if (bound) + { + g_fprintf (data->output, "/>\n"); + } + else + { + g_fprintf (data->output, ">"); + if (property_is_boolean (data, class_name, property_name)) + { + g_fprintf (data->output, "%s", canonical_boolean_value (data, value_string)); + } + else + { + escaped = g_markup_escape_text (value_string, -1); + g_fprintf (data->output, "%s", escaped); + g_free (escaped); + } + g_fprintf (data->output, "\n"); + } +} + +static void +maybe_close_starttag (MyParserData *data) +{ + if (data->unclosed_starttag) + { + g_fprintf (data->output, ">\n"); + data->unclosed_starttag = FALSE; + } +} + +static gboolean +stack_is (GMarkupParseContext *context, + ...) +{ + va_list args; + gchar *s, *p; + const GSList *stack; + + stack = g_markup_parse_context_get_element_stack (context); + + va_start (args, context); + s = va_arg (args, gchar *); + while (s) + { + if (stack == NULL) + { + va_end (args); + return FALSE; + } + + p = (gchar *)stack->data; + if (strcmp (s, p) != 0) + { + va_end (args); + return FALSE; + } + + s = va_arg (args, gchar *); + stack = stack->next; + } + + va_end (args); + return TRUE; +} + +static void +start_element (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + gpointer user_data, + GError **error) +{ + gint i; + gchar *escaped; + MyParserData *data = user_data; + + maybe_close_starttag (data); + + if (strcmp (element_name, "property") == 0) + { + g_assert (data->attribute_names == NULL); + g_assert (data->attribute_values == NULL); + g_assert (data->value == NULL); + + data->attribute_names = g_strdupv ((gchar **)attribute_names); + data->attribute_values = g_strdupv ((gchar **)attribute_values); + data->value = g_string_new (""); + + return; + } + else if (strcmp (element_name, "packing") == 0) + { + data->packing = TRUE; + data->packing_started = FALSE; + + return; + } + else if (strcmp (element_name, "cell-packing") == 0) + { + data->cell_packing = TRUE; + data->cell_packing_started = FALSE; + + return; + } + else if (strcmp (element_name, "child") == 0) + { + data->in_child += 1; + + if (attribute_names[0] == NULL) + return; + + data->child_started += 1; + } + else if (strcmp (element_name, "attribute") == 0) + { + /* attribute in label has no content */ + if (data->classes == NULL || + strcmp ((gchar *)data->classes->data, "GtkLabel") != 0) + data->value = g_string_new (""); + } + else if (stack_is (context, "item", "items", NULL) || + stack_is (context, "action-widget", "action-widgets", NULL) || + stack_is (context, "mime-type", "mime-types", NULL) || + stack_is (context, "pattern", "patterns", NULL) || + stack_is (context, "application", "applications", NULL) || + stack_is (context, "col", "row", "data", NULL) || + stack_is (context, "mark", "marks", NULL) || + stack_is (context, "action", "accessibility", NULL)) + { + data->value = g_string_new (""); + } + else if (strcmp (element_name, "placeholder") == 0) + { + return; + } + else if (strcmp (element_name, "object") == 0 || + strcmp (element_name, "template") == 0) + { + maybe_start_child (data); + + for (i = 0; attribute_names[i]; i++) + { + if (strcmp (attribute_names[i], "class") == 0) + { + data->classes = g_list_prepend (data->classes, + g_strdup (attribute_values[i])); + break; + } + } + } + + g_fprintf (data->output, "%*s<%s", data->indent, "", element_name); + for (i = 0; attribute_names[i]; i++) + { + escaped = g_markup_escape_text (attribute_values[i], -1); + g_fprintf (data->output, " %s=\"%s\"", attribute_names[i], escaped); + g_free (escaped); + } + data->unclosed_starttag = TRUE; + data->indent += 2; +} + +static void +end_element (GMarkupParseContext *context, + const gchar *element_name, + gpointer user_data, + GError **error) +{ + MyParserData *data = user_data; + + if (strcmp (element_name, "property") == 0) + { + maybe_emit_property (data); + + g_clear_pointer (&data->attribute_names, g_strfreev); + g_clear_pointer (&data->attribute_values, g_strfreev); + g_string_free (data->value, TRUE); + data->value = NULL; + return; + } + else if (strcmp (element_name, "packing") == 0) + { + data->packing = FALSE; + if (!data->packing_started) + return; + } + else if (strcmp (element_name, "cell-packing") == 0) + { + data->cell_packing = FALSE; + if (!data->cell_packing_started) + return; + } + else if (strcmp (element_name, "child") == 0) + { + data->in_child -= 1; + if (data->child_started == data->in_child) + return; + data->child_started -= 1; + } + else if (strcmp (element_name, "placeholder") == 0) + { + return; + } + else if (strcmp (element_name, "object") == 0 || + strcmp (element_name, "template") == 0) + { + g_free (data->classes->data); + data->classes = g_list_delete_link (data->classes, data->classes); + } + + if (data->value != NULL) + { + gchar *escaped; + + if (data->unclosed_starttag) + g_fprintf (data->output, ">"); + + escaped = g_markup_escape_text (data->value->str, -1); + g_fprintf (data->output, "%s\n", escaped, element_name); + g_free (escaped); + + g_string_free (data->value, TRUE); + data->value = NULL; + } + else + { + if (data->unclosed_starttag) + g_fprintf (data->output, "/>\n"); + else + g_fprintf (data->output, "%*s\n", data->indent - 2, "", element_name); + } + + data->indent -= 2; + data->unclosed_starttag = FALSE; +} + +static void +text (GMarkupParseContext *context, + const gchar *text, + gsize text_len, + gpointer user_data, + GError **error) +{ + MyParserData *data = user_data; + + if (data->value) + { + g_string_append_len (data->value, text, text_len); + return; + } +} + +static void +passthrough (GMarkupParseContext *context, + const gchar *text, + gsize text_len, + gpointer user_data, + GError **error) +{ + MyParserData *data = user_data; + + maybe_close_starttag (data); + + g_fprintf (data->output, "%*s%s\n", data->indent, "", text); +} + +GMarkupParser parser = { + start_element, + end_element, + text, + passthrough, + NULL +}; + +void +do_simplify (int *argc, + const char ***argv) +{ + GMarkupParseContext *context; + gchar *buffer; + MyParserData data; + gboolean replace = FALSE; + char **filenames = NULL; + GOptionContext *ctx; + const GOptionEntry entries[] = { + { "replace", 0, 0, G_OPTION_ARG_NONE, &replace, NULL, NULL }, + { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, NULL, NULL }, + { NULL, } + }; + GError *error = NULL; + + ctx = g_option_context_new (NULL); + g_option_context_set_help_enabled (ctx, FALSE); + g_option_context_add_main_entries (ctx, entries, NULL); + + if (!g_option_context_parse (ctx, argc, (char ***)argv, &error)) + { + g_printerr ("%s\n", error->message); + g_error_free (error); + exit (1); + } + + g_option_context_free (ctx); + + if (filenames == NULL) + { + g_printerr ("No .ui file specified\n"); + exit (1); + } + + if (g_strv_length (filenames) > 1) + { + g_printerr ("Can only simplify a single .ui file\n"); + exit (1); + } + + data.input_filename = filenames[0]; + data.output_filename = NULL; + + if (replace) + { + int fd; + fd = g_file_open_tmp ("gtk-builder-tool-XXXXXX", &data.output_filename, NULL); + data.output = fdopen (fd, "w"); + } + else + { + data.output = stdout; + } + + if (!g_file_get_contents (filenames[0], &buffer, NULL, &error)) + { + g_printerr (_("Can’t load file: %s\n"), error->message); + exit (1); + } + + data.builder = gtk_builder_new (); + data.classes = NULL; + data.attribute_names = NULL; + data.attribute_values = NULL; + data.value = NULL; + data.packing = FALSE; + data.packing_started = FALSE; + data.cell_packing = FALSE; + data.cell_packing_started = FALSE; + data.in_child = 0; + data.child_started = 0; + data.unclosed_starttag = FALSE; + data.indent = 0; + + context = g_markup_parse_context_new (&parser, G_MARKUP_TREAT_CDATA_AS_TEXT, &data, NULL); + if (!g_markup_parse_context_parse (context, buffer, -1, &error)) + { + g_printerr (_("Can’t parse file: %s\n"), error->message); + exit (1); + } + + fclose (data.output); + + if (data.output_filename) + { + char *content; + gsize length; + + if (!g_file_get_contents (data.output_filename, &content, &length, &error)) + { + g_printerr ("Failed to read %s: %s\n", data.output_filename, error->message); + exit (1); + } + + if (!g_file_set_contents (data.input_filename, content, length, &error)) + { + g_printerr ("Failed to write %s: %s\n", data.input_filename, error->message); + exit (1); + } + } +} diff --git a/gtk/tools/gtk-builder-tool-validate.c b/gtk/tools/gtk-builder-tool-validate.c new file mode 100644 index 0000000000..33ee6ff22b --- /dev/null +++ b/gtk/tools/gtk-builder-tool-validate.c @@ -0,0 +1,152 @@ +/* Copyright 2015 Red Hat, Inc. + * + * GTK+ is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * GLib is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with GTK+; see the file COPYING. If not, + * see . + * + * Author: Matthias Clasen + */ + +#include +#include +#include + +#include +#include +#include +#include +#include "gtkbuilderprivate.h" + +static GType +make_fake_type (const gchar *type_name, + const gchar *parent_name) +{ + GType parent_type; + GTypeQuery query; + + parent_type = g_type_from_name (parent_name); + if (parent_type == G_TYPE_INVALID) + { + g_printerr ("Failed to lookup template parent type %s\n", parent_name); + exit (1); + } + + g_type_query (parent_type, &query); + return g_type_register_static_simple (parent_type, + type_name, + query.class_size, + NULL, + query.instance_size, + NULL, + 0); +} + +static void +do_validate_template (const gchar *filename, + const gchar *type_name, + const gchar *parent_name) +{ + GType template_type; + GtkWidget *widget; + GtkBuilder *builder; + GError *error = NULL; + gint ret; + + /* Only make a fake type if it doesn't exist yet. + * This lets us e.g. validate the GtkFileChooserWidget template. + */ + template_type = g_type_from_name (type_name); + if (template_type == G_TYPE_INVALID) + template_type = make_fake_type (type_name, parent_name); + + widget = g_object_new (template_type, NULL); + if (!widget) + { + g_printerr ("Failed to create an instance of the template type %s\n", type_name); + exit (1); + } + + builder = gtk_builder_new (); + ret = gtk_builder_extend_with_template (builder, widget, template_type, " ", 1, &error); + if (ret) + ret = gtk_builder_add_from_file (builder, filename, &error); + g_object_unref (builder); + + if (ret == 0) + { + g_printerr ("%s\n", error->message); + exit (1); + } +} + +static gboolean +parse_template_error (const gchar *message, + gchar **class_name, + gchar **parent_name) +{ + gchar *p; + + if (!strstr (message, "Not expecting to handle a template")) + return FALSE; + + p = strstr (message, "(class '"); + if (p) + { + *class_name = g_strdup (p + strlen ("(class '")); + p = strstr (*class_name, "'"); + if (p) + *p = '\0'; + } + p = strstr (message, ", parent '"); + if (p) + { + *parent_name = g_strdup (p + strlen (", parent '")); + p = strstr (*parent_name, "'"); + if (p) + *p = '\0'; + } + + return TRUE; +} + +void +do_validate (int *argc, char ***argv) +{ + GtkBuilder *builder; + GError *error = NULL; + gint ret; + gchar *class_name = NULL; + gchar *parent_name = NULL; + const gchar *filename; + + filename = (*argv)[1]; + + builder = gtk_builder_new (); + ret = gtk_builder_add_from_file (builder, filename, &error); + g_object_unref (builder); + + if (ret == 0) + { + if (g_error_matches (error, GTK_BUILDER_ERROR, GTK_BUILDER_ERROR_UNHANDLED_TAG) && + parse_template_error (error->message, &class_name, &parent_name)) + { + do_validate_template (filename, class_name, parent_name); + } + else + { + g_printerr ("%s\n", error->message); + exit (1); + } + } +} + diff --git a/gtk/tools/gtk-builder-tool.c b/gtk/tools/gtk-builder-tool.c index ebd965ec12..48b78fb08f 100644 --- a/gtk/tools/gtk-builder-tool.c +++ b/gtk/tools/gtk-builder-tool.c @@ -27,1026 +27,10 @@ #include #include "gtkbuilderprivate.h" - -typedef struct { - GtkBuilder *builder; - GList *classes; - gboolean packing; - gboolean packing_started; - gboolean cell_packing; - gboolean cell_packing_started; - gint in_child; - gint child_started; - gchar **attribute_names; - gchar **attribute_values; - GString *value; - gboolean unclosed_starttag; - gint indent; - char *input_filename; - char *output_filename; - FILE *output; -} MyParserData; - -static void -canonicalize_key (gchar *key) -{ - gchar *p; - - for (p = key; *p != 0; p++) - { - gchar c = *p; - - /* We may meet something like AtkObject::accessible-name */ - if (c == ':' && ((p > key && p[-1] == ':') || p[1] == ':')) - continue; - - if (c != '-' && - (c < '0' || c > '9') && - (c < 'A' || c > 'Z') && - (c < 'a' || c > 'z')) - *p = '-'; - } -} - -static GParamSpec * -get_property_pspec (MyParserData *data, - const gchar *class_name, - const gchar *property_name) -{ - GType type; - GObjectClass *class; - GParamSpec *pspec; - gchar *canonical_name; - - type = g_type_from_name (class_name); - if (type == G_TYPE_INVALID) - { - GtkBuilder *builder = gtk_builder_new (); - type = gtk_builder_get_type_from_name (builder, class_name); - g_object_unref (builder); - if (type == G_TYPE_INVALID) - return NULL; - } - - class = g_type_class_ref (type); - canonical_name = g_strdup (property_name); - canonicalize_key (canonical_name); - if (data->packing) - pspec = gtk_container_class_find_child_property (class, canonical_name); - else if (data->cell_packing) - { - GObjectClass *cell_class; - - /* We're just assuming that the cell layout is using a GtkCellAreaBox. */ - cell_class = g_type_class_ref (GTK_TYPE_CELL_AREA_BOX); - pspec = gtk_cell_area_class_find_cell_property (GTK_CELL_AREA_CLASS (cell_class), canonical_name); - g_type_class_unref (cell_class); - } - else - pspec = g_object_class_find_property (class, canonical_name); - g_free (canonical_name); - g_type_class_unref (class); - - return pspec; -} - - -static gboolean -value_is_default (MyParserData *data, - const gchar *class_name, - const gchar *property_name, - const gchar *value_string) -{ - GValue value = { 0, }; - gboolean ret; - GError *error = NULL; - GParamSpec *pspec; - - pspec = get_property_pspec (data, class_name, property_name); - - if (pspec == NULL) - { - if (data->packing) - g_printerr (_("Packing property %s::%s not found\n"), class_name, property_name); - else if (data->cell_packing) - g_printerr (_("Cell property %s::%s not found\n"), class_name, property_name); - else - g_printerr (_("Property %s::%s not found\n"), class_name, property_name); - return FALSE; - } - else if (g_type_is_a (G_PARAM_SPEC_VALUE_TYPE (pspec), G_TYPE_OBJECT)) - return FALSE; - - if (!gtk_builder_value_from_string (data->builder, pspec, value_string, &value, &error)) - { - g_printerr (_("Couldn’t parse value for %s::%s: %s\n"), class_name, property_name, error->message); - g_error_free (error); - ret = FALSE; - } - else - ret = g_param_value_defaults (pspec, &value); - - g_value_reset (&value); - - return ret; -} - -static gboolean -property_is_boolean (MyParserData *data, - const gchar *class_name, - const gchar *property_name) -{ - GParamSpec *pspec; - - pspec = get_property_pspec (data, class_name, property_name); - if (pspec) - return G_PARAM_SPEC_VALUE_TYPE (pspec) == G_TYPE_BOOLEAN; - - return FALSE; -} - -static const gchar * -canonical_boolean_value (MyParserData *data, - const gchar *string) -{ - GValue value = G_VALUE_INIT; - gboolean b = FALSE; - - if (gtk_builder_value_from_string_type (data->builder, G_TYPE_BOOLEAN, string, &value, NULL)) - b = g_value_get_boolean (&value); - - return b ? "1" : "0"; -} - -/* A number of properties unfortunately can't be omitted even - * if they are nominally set to their default value. In many - * cases, this is due to subclasses not overriding the default - * value from the superclass. - */ -static gboolean -needs_explicit_setting (MyParserData *data, - const gchar *class_name, - const gchar *property_name) -{ - struct _Prop { - const char *class; - const char *property; - gboolean packing; - } props[] = { - { "GtkAboutDialog", "program-name", 0 }, - { "GtkCalendar", "year", 0 }, - { "GtkCalendar", "month", 0 }, - { "GtkCalendar", "day", 0 }, - { "GtkPlacesSidebar", "show-desktop", 0 }, - { "GtkRadioButton", "draw-indicator", 0 }, - { "GtkGrid", "left-attach", 1 }, - { "GtkGrid", "top-attach", 1 }, - { "GtkWidget", "hexpand", 0 }, - { "GtkWidget", "vexpand", 0 }, - { NULL, NULL, 0 } - }; - gchar *canonical_name; - gboolean found; - gint k; - - canonical_name = g_strdup (property_name); - g_strdelimit (canonical_name, "_", '-'); - - found = FALSE; - for (k = 0; props[k].class; k++) - { - if (strcmp (class_name, props[k].class) == 0 && - strcmp (canonical_name, props[k].property) == 0 && - data->packing == props[k].packing) - { - found = TRUE; - break; - } - } - - g_free (canonical_name); - - return found; -} - -static void -maybe_start_packing (MyParserData *data) -{ - if (data->packing) - { - if (!data->packing_started) - { - g_fprintf (data->output, "%*s\n", data->indent, ""); - data->indent += 2; - data->packing_started = TRUE; - } - } -} - -static void -maybe_start_cell_packing (MyParserData *data) -{ - if (data->cell_packing) - { - if (!data->cell_packing_started) - { - g_fprintf (data->output, "%*s\n", data->indent, ""); - data->indent += 2; - data->cell_packing_started = TRUE; - } - } -} - -static void -maybe_start_child (MyParserData *data) -{ - if (data->in_child > 0) - { - if (data->child_started < data->in_child) - { - g_fprintf (data->output, "%*s\n", data->indent, ""); - data->indent += 2; - data->child_started += 1; - } - } -} - -static void -maybe_emit_property (MyParserData *data) -{ - gint i; - gboolean bound; - gboolean translatable; - gchar *escaped; - const gchar *class_name; - const gchar *property_name; - const gchar *value_string; - - class_name = (const gchar *)data->classes->data; - property_name = ""; - value_string = (const gchar *)data->value->str; - - bound = FALSE; - translatable = FALSE; - for (i = 0; data->attribute_names[i]; i++) - { - if (strcmp (data->attribute_names[i], "bind-source") == 0 || - strcmp (data->attribute_names[i], "bind_source") == 0) - bound = TRUE; - else if (strcmp (data->attribute_names[i], "translatable") == 0) - translatable = TRUE; - else if (strcmp (data->attribute_names[i], "name") == 0) - property_name = (const gchar *)data->attribute_values[i]; - } - - if (!translatable && - !bound && - !needs_explicit_setting (data, class_name, property_name)) - { - for (i = 0; data->attribute_names[i]; i++) - { - if (strcmp (data->attribute_names[i], "name") == 0) - { - if (data->classes == NULL) - break; - - if (value_is_default (data, class_name, property_name, value_string)) - return; - } - } - } - - maybe_start_packing (data); - maybe_start_cell_packing (data); - - g_fprintf (data->output, "%*sindent, ""); - for (i = 0; data->attribute_names[i]; i++) - { - if (!translatable && - (strcmp (data->attribute_names[i], "comments") == 0 || - strcmp (data->attribute_names[i], "context") == 0)) - continue; - - escaped = g_markup_escape_text (data->attribute_values[i], -1); - - if (strcmp (data->attribute_names[i], "name") == 0) - canonicalize_key (escaped); - - g_fprintf (data->output, " %s=\"%s\"", data->attribute_names[i], escaped); - g_free (escaped); - } - - if (bound) - { - g_fprintf (data->output, "/>\n"); - } - else - { - g_fprintf (data->output, ">"); - if (property_is_boolean (data, class_name, property_name)) - { - g_fprintf (data->output, "%s", canonical_boolean_value (data, value_string)); - } - else - { - escaped = g_markup_escape_text (value_string, -1); - g_fprintf (data->output, "%s", escaped); - g_free (escaped); - } - g_fprintf (data->output, "\n"); - } -} - -static void -maybe_close_starttag (MyParserData *data) -{ - if (data->unclosed_starttag) - { - g_fprintf (data->output, ">\n"); - data->unclosed_starttag = FALSE; - } -} - -static gboolean -stack_is (GMarkupParseContext *context, - ...) -{ - va_list args; - gchar *s, *p; - const GSList *stack; - - stack = g_markup_parse_context_get_element_stack (context); - - va_start (args, context); - s = va_arg (args, gchar *); - while (s) - { - if (stack == NULL) - { - va_end (args); - return FALSE; - } - - p = (gchar *)stack->data; - if (strcmp (s, p) != 0) - { - va_end (args); - return FALSE; - } - - s = va_arg (args, gchar *); - stack = stack->next; - } - - va_end (args); - return TRUE; -} - -static void -start_element (GMarkupParseContext *context, - const gchar *element_name, - const gchar **attribute_names, - const gchar **attribute_values, - gpointer user_data, - GError **error) -{ - gint i; - gchar *escaped; - MyParserData *data = user_data; - - maybe_close_starttag (data); - - if (strcmp (element_name, "property") == 0) - { - g_assert (data->attribute_names == NULL); - g_assert (data->attribute_values == NULL); - g_assert (data->value == NULL); - - data->attribute_names = g_strdupv ((gchar **)attribute_names); - data->attribute_values = g_strdupv ((gchar **)attribute_values); - data->value = g_string_new (""); - - return; - } - else if (strcmp (element_name, "packing") == 0) - { - data->packing = TRUE; - data->packing_started = FALSE; - - return; - } - else if (strcmp (element_name, "cell-packing") == 0) - { - data->cell_packing = TRUE; - data->cell_packing_started = FALSE; - - return; - } - else if (strcmp (element_name, "child") == 0) - { - data->in_child += 1; - - if (attribute_names[0] == NULL) - return; - - data->child_started += 1; - } - else if (strcmp (element_name, "attribute") == 0) - { - /* attribute in label has no content */ - if (data->classes == NULL || - strcmp ((gchar *)data->classes->data, "GtkLabel") != 0) - data->value = g_string_new (""); - } - else if (stack_is (context, "item", "items", NULL) || - stack_is (context, "action-widget", "action-widgets", NULL) || - stack_is (context, "mime-type", "mime-types", NULL) || - stack_is (context, "pattern", "patterns", NULL) || - stack_is (context, "application", "applications", NULL) || - stack_is (context, "col", "row", "data", NULL) || - stack_is (context, "mark", "marks", NULL) || - stack_is (context, "action", "accessibility", NULL)) - { - data->value = g_string_new (""); - } - else if (strcmp (element_name, "placeholder") == 0) - { - return; - } - else if (strcmp (element_name, "object") == 0 || - strcmp (element_name, "template") == 0) - { - maybe_start_child (data); - - for (i = 0; attribute_names[i]; i++) - { - if (strcmp (attribute_names[i], "class") == 0) - { - data->classes = g_list_prepend (data->classes, - g_strdup (attribute_values[i])); - break; - } - } - } - - g_fprintf (data->output, "%*s<%s", data->indent, "", element_name); - for (i = 0; attribute_names[i]; i++) - { - escaped = g_markup_escape_text (attribute_values[i], -1); - g_fprintf (data->output, " %s=\"%s\"", attribute_names[i], escaped); - g_free (escaped); - } - data->unclosed_starttag = TRUE; - data->indent += 2; -} - -static void -end_element (GMarkupParseContext *context, - const gchar *element_name, - gpointer user_data, - GError **error) -{ - MyParserData *data = user_data; - - if (strcmp (element_name, "property") == 0) - { - maybe_emit_property (data); - - g_clear_pointer (&data->attribute_names, g_strfreev); - g_clear_pointer (&data->attribute_values, g_strfreev); - g_string_free (data->value, TRUE); - data->value = NULL; - return; - } - else if (strcmp (element_name, "packing") == 0) - { - data->packing = FALSE; - if (!data->packing_started) - return; - } - else if (strcmp (element_name, "cell-packing") == 0) - { - data->cell_packing = FALSE; - if (!data->cell_packing_started) - return; - } - else if (strcmp (element_name, "child") == 0) - { - data->in_child -= 1; - if (data->child_started == data->in_child) - return; - data->child_started -= 1; - } - else if (strcmp (element_name, "placeholder") == 0) - { - return; - } - else if (strcmp (element_name, "object") == 0 || - strcmp (element_name, "template") == 0) - { - g_free (data->classes->data); - data->classes = g_list_delete_link (data->classes, data->classes); - } - - if (data->value != NULL) - { - gchar *escaped; - - if (data->unclosed_starttag) - g_fprintf (data->output, ">"); - - escaped = g_markup_escape_text (data->value->str, -1); - g_fprintf (data->output, "%s\n", escaped, element_name); - g_free (escaped); - - g_string_free (data->value, TRUE); - data->value = NULL; - } - else - { - if (data->unclosed_starttag) - g_fprintf (data->output, "/>\n"); - else - g_fprintf (data->output, "%*s\n", data->indent - 2, "", element_name); - } - - data->indent -= 2; - data->unclosed_starttag = FALSE; -} - -static void -text (GMarkupParseContext *context, - const gchar *text, - gsize text_len, - gpointer user_data, - GError **error) -{ - MyParserData *data = user_data; - - if (data->value) - { - g_string_append_len (data->value, text, text_len); - return; - } -} - -static void -passthrough (GMarkupParseContext *context, - const gchar *text, - gsize text_len, - gpointer user_data, - GError **error) -{ - MyParserData *data = user_data; - - maybe_close_starttag (data); - - g_fprintf (data->output, "%*s%s\n", data->indent, "", text); -} - -GMarkupParser parser = { - start_element, - end_element, - text, - passthrough, - NULL -}; - -static void -do_simplify (int *argc, - const char ***argv) -{ - GMarkupParseContext *context; - gchar *buffer; - MyParserData data; - gboolean replace = FALSE; - char **filenames = NULL; - GOptionContext *ctx; - const GOptionEntry entries[] = { - { "replace", 0, 0, G_OPTION_ARG_NONE, &replace, NULL, NULL }, - { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, NULL, NULL }, - { NULL, } - }; - GError *error = NULL; - - ctx = g_option_context_new (NULL); - g_option_context_set_help_enabled (ctx, FALSE); - g_option_context_add_main_entries (ctx, entries, NULL); - - if (!g_option_context_parse (ctx, argc, (char ***)argv, &error)) - { - g_printerr ("%s\n", error->message); - g_error_free (error); - exit (1); - } - - g_option_context_free (ctx); - - if (filenames == NULL) - { - g_printerr ("No .ui file specified\n"); - exit (1); - } - - if (g_strv_length (filenames) > 1) - { - g_printerr ("Can only simplify a single .ui file\n"); - exit (1); - } - - data.input_filename = filenames[0]; - data.output_filename = NULL; - - if (replace) - { - int fd; - fd = g_file_open_tmp ("gtk-builder-tool-XXXXXX", &data.output_filename, NULL); - data.output = fdopen (fd, "w"); - } - else - { - data.output = stdout; - } - - if (!g_file_get_contents (filenames[0], &buffer, NULL, &error)) - { - g_printerr (_("Can’t load file: %s\n"), error->message); - exit (1); - } - - data.builder = gtk_builder_new (); - data.classes = NULL; - data.attribute_names = NULL; - data.attribute_values = NULL; - data.value = NULL; - data.packing = FALSE; - data.packing_started = FALSE; - data.cell_packing = FALSE; - data.cell_packing_started = FALSE; - data.in_child = 0; - data.child_started = 0; - data.unclosed_starttag = FALSE; - data.indent = 0; - - context = g_markup_parse_context_new (&parser, G_MARKUP_TREAT_CDATA_AS_TEXT, &data, NULL); - if (!g_markup_parse_context_parse (context, buffer, -1, &error)) - { - g_printerr (_("Can’t parse file: %s\n"), error->message); - exit (1); - } - - fclose (data.output); - - if (data.output_filename) - { - char *content; - gsize length; - - if (!g_file_get_contents (data.output_filename, &content, &length, &error)) - { - g_printerr ("Failed to read %s: %s\n", data.output_filename, error->message); - exit (1); - } - - if (!g_file_set_contents (data.input_filename, content, length, &error)) - { - g_printerr ("Failed to write %s: %s\n", data.input_filename, error->message); - exit (1); - } - } -} - -static GType -make_fake_type (const gchar *type_name, - const gchar *parent_name) -{ - GType parent_type; - GTypeQuery query; - - parent_type = g_type_from_name (parent_name); - if (parent_type == G_TYPE_INVALID) - { - g_printerr ("Failed to lookup template parent type %s\n", parent_name); - exit (1); - } - - g_type_query (parent_type, &query); - return g_type_register_static_simple (parent_type, - type_name, - query.class_size, - NULL, - query.instance_size, - NULL, - 0); -} - -static void -do_validate_template (const gchar *filename, - const gchar *type_name, - const gchar *parent_name) -{ - GType template_type; - GtkWidget *widget; - GtkBuilder *builder; - GError *error = NULL; - gint ret; - - /* Only make a fake type if it doesn't exist yet. - * This lets us e.g. validate the GtkFileChooserWidget template. - */ - template_type = g_type_from_name (type_name); - if (template_type == G_TYPE_INVALID) - template_type = make_fake_type (type_name, parent_name); - - widget = g_object_new (template_type, NULL); - if (!widget) - { - g_printerr ("Failed to create an instance of the template type %s\n", type_name); - exit (1); - } - - builder = gtk_builder_new (); - ret = gtk_builder_extend_with_template (builder, widget, template_type, " ", 1, &error); - if (ret) - ret = gtk_builder_add_from_file (builder, filename, &error); - g_object_unref (builder); - - if (ret == 0) - { - g_printerr ("%s\n", error->message); - exit (1); - } -} - -static gboolean -parse_template_error (const gchar *message, - gchar **class_name, - gchar **parent_name) -{ - gchar *p; - - if (!strstr (message, "Not expecting to handle a template")) - return FALSE; - - p = strstr (message, "(class '"); - if (p) - { - *class_name = g_strdup (p + strlen ("(class '")); - p = strstr (*class_name, "'"); - if (p) - *p = '\0'; - } - p = strstr (message, ", parent '"); - if (p) - { - *parent_name = g_strdup (p + strlen (", parent '")); - p = strstr (*parent_name, "'"); - if (p) - *p = '\0'; - } - - return TRUE; -} - -static void -do_validate (const gchar *filename) -{ - GtkBuilder *builder; - GError *error = NULL; - gint ret; - gchar *class_name = NULL; - gchar *parent_name = NULL; - - builder = gtk_builder_new (); - ret = gtk_builder_add_from_file (builder, filename, &error); - g_object_unref (builder); - - if (ret == 0) - { - if (g_error_matches (error, GTK_BUILDER_ERROR, GTK_BUILDER_ERROR_UNHANDLED_TAG) && - parse_template_error (error->message, &class_name, &parent_name)) - { - do_validate_template (filename, class_name, parent_name); - } - else - { - g_printerr ("%s\n", error->message); - exit (1); - } - } -} - -static const gchar * -object_get_name (GObject *object) -{ - if (GTK_IS_BUILDABLE (object)) - return gtk_buildable_get_name (GTK_BUILDABLE (object)); - else - return g_object_get_data (object, "gtk-builder-name"); -} - -static void -do_enumerate (const gchar *filename) -{ - GtkBuilder *builder; - GError *error = NULL; - gint ret; - GSList *list, *l; - GObject *object; - const gchar *name; - - builder = gtk_builder_new (); - ret = gtk_builder_add_from_file (builder, filename, &error); - - if (ret == 0) - { - g_printerr ("%s\n", error->message); - exit (1); - } - - list = gtk_builder_get_objects (builder); - for (l = list; l; l = l->next) - { - object = l->data; - name = object_get_name (object); - if (g_str_has_prefix (name, "___") && g_str_has_suffix (name, "___")) - continue; - - g_printf ("%s (%s)\n", name, g_type_name_from_instance ((GTypeInstance*)object)); - } - g_slist_free (list); - - g_object_unref (builder); -} - -static void -set_window_title (GtkWindow *window, - const char *filename, - const char *id) -{ - gchar *name; - gchar *title; - - name = g_path_get_basename (filename); - - if (id) - title = g_strdup_printf ("%s in %s", id, name); - else - title = g_strdup (name); - - gtk_window_set_title (window, title); - - g_free (title); - g_free (name); -} - -static void -preview_file (const char *filename, - const char *id, - const char *cssfile) -{ - GtkBuilder *builder; - GError *error = NULL; - GObject *object; - GtkWidget *window; - - if (cssfile) - { - GtkCssProvider *provider; - - provider = gtk_css_provider_new (); - gtk_css_provider_load_from_path (provider, cssfile); - - gtk_style_context_add_provider_for_display (gdk_display_get_default (), - GTK_STYLE_PROVIDER (provider), - GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); - } - - builder = gtk_builder_new (); - if (!gtk_builder_add_from_file (builder, filename, &error)) - { - g_printerr ("%s\n", error->message); - exit (1); - } - - object = NULL; - - if (id) - { - object = gtk_builder_get_object (builder, id); - } - else - { - GSList *objects, *l; - - objects = gtk_builder_get_objects (builder); - for (l = objects; l; l = l->next) - { - GObject *obj = l->data; - - if (GTK_IS_WINDOW (obj)) - { - object = obj; - break; - } - else if (GTK_IS_WIDGET (obj)) - { - if (object == NULL) - object = obj; - } - } - g_slist_free (objects); - } - - if (object == NULL) - { - if (id) - g_printerr ("No object with ID '%s' found\n", id); - else - g_printerr ("No previewable object found\n"); - exit (1); - } - - if (!GTK_IS_WIDGET (object)) - { - g_printerr ("Objects of type %s can't be previewed\n", G_OBJECT_TYPE_NAME (object)); - exit (1); - } - - if (GTK_IS_WINDOW (object)) - window = GTK_WIDGET (object); - else - { - GtkWidget *widget = GTK_WIDGET (object); - - window = gtk_window_new (GTK_WINDOW_TOPLEVEL); - - if (GTK_IS_BUILDABLE (object)) - id = gtk_buildable_get_name (GTK_BUILDABLE (object)); - - set_window_title (GTK_WINDOW (window), filename, id); - - g_object_ref (widget); - if (gtk_widget_get_parent (widget) != NULL) - gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (widget)), widget); - gtk_container_add (GTK_CONTAINER (window), widget); - g_object_unref (widget); - } - - gtk_window_present (GTK_WINDOW (window)); - - gtk_main (); - - g_object_unref (builder); -} - -static void -do_preview (int *argc, - const char ***argv) -{ - GOptionContext *context; - char *id = NULL; - char *css = NULL; - char **filenames = NULL; - const GOptionEntry entries[] = { - { "id", 0, 0, G_OPTION_ARG_STRING, &id, NULL, NULL }, - { "css", 0, 0, G_OPTION_ARG_FILENAME, &css, NULL, NULL }, - { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, NULL, NULL }, - { NULL, } - }; - GError *error = NULL; - - context = g_option_context_new (NULL); - g_option_context_set_help_enabled (context, FALSE); - g_option_context_add_main_entries (context, entries, NULL); - - if (!g_option_context_parse (context, argc, (char ***)argv, &error)) - { - g_printerr ("%s\n", error->message); - g_error_free (error); - exit (1); - } - - g_option_context_free (context); - - if (filenames == NULL) - { - g_printerr ("No .ui file specified\n"); - exit (1); - } - - if (g_strv_length (filenames) > 1) - { - g_printerr ("Can only preview a single .ui file\n"); - exit (1); - } - - preview_file (filenames[0], id, css); - - g_strfreev (filenames); - g_free (id); - g_free (css); -} +extern void do_simplify (int *argc, const char ***argv); +extern void do_validate (int *argc, const char ***argv); +extern void do_enumerate (int *argc, const char ***argv); +extern void do_preview (int *argc, const char ***argv); static void usage (void) @@ -1090,11 +74,11 @@ main (int argc, const char *argv[]) argc--; if (strcmp (argv[0], "validate") == 0) - do_validate (argv[1]); + do_validate (&argc, &argv); else if (strcmp (argv[0], "simplify") == 0) do_simplify (&argc, &argv); else if (strcmp (argv[0], "enumerate") == 0) - do_enumerate (argv[1]); + do_enumerate (&argc, &argv); else if (strcmp (argv[0], "preview") == 0) do_preview (&argc, &argv); else diff --git a/gtk/tools/meson.build b/gtk/tools/meson.build index 39dcde097f..e082bc833f 100644 --- a/gtk/tools/meson.build +++ b/gtk/tools/meson.build @@ -1,7 +1,11 @@ # Installed tools gtk_tools = [ ['gtk4-query-settings', ['gtk-query-settings.c']], - ['gtk4-builder-tool', ['gtk-builder-tool.c']], + ['gtk4-builder-tool', ['gtk-builder-tool.c', + 'gtk-builder-tool-simplify.c', + 'gtk-builder-tool-validate.c', + 'gtk-builder-tool-enumerate.c', + 'gtk-builder-tool-preview.c']], ['gtk4-update-icon-cache', ['updateiconcache.c', 'gtkiconcachevalidator.c']], ['gtk4-encode-symbolic-svg', ['encodesymbolic.c', 'gdkpixbufutils.c']], ]